diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fdaa0c8628f766e241ba9043698b611a0bd78811..4fc5b97caae0735058337c8b23e4ca7471761d24 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -13,7 +13,7 @@ # - Multiple owners are supported. # - Either handle (e.g, @github_user or @github/team) or email can be used. Keep in mind, # that handles might work better because they are more recognizable on GitHub, -# eyou can use them for mentioning unlike an email. +# you can use them for mentioning unlike an email. # - The latest matching rule, if multiple, takes precedence. # CI diff --git a/.github/scripts/check-prdoc.py b/.github/scripts/check-prdoc.py new file mode 100644 index 0000000000000000000000000000000000000000..42b063f2885da148033986dfa49740f2b0416460 --- /dev/null +++ b/.github/scripts/check-prdoc.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 + +''' +Ensure that the prdoc files are valid. + +# Example + +```sh +python3 -m pip install cargo-workspace +python3 .github/scripts/check-prdoc.py Cargo.toml prdoc/*.prdoc +``` + +Produces example output: +```pre +🔎 Reading workspace polkadot-sdk/Cargo.toml +📦 Checking 32 prdocs against 493 crates. +✅ All prdocs are valid +``` +''' + +import os +import yaml +import argparse +import cargo_workspace + +def check_prdoc_crate_names(root, paths): + ''' + Check that all crates of the `crates` section of each prdoc is present in the workspace. + ''' + + print(f'🔎 Reading workspace {root}.') + workspace = cargo_workspace.Workspace.from_path(root) + crate_names = [crate.name for crate in workspace.crates] + + print(f'📦 Checking {len(paths)} prdocs against {len(crate_names)} crates.') + faulty = {} + + for path in paths: + with open(path, 'r') as f: + prdoc = yaml.safe_load(f) + + for crate in prdoc.get('crates', []): + crate = crate['name'] + if crate in crate_names: + continue + + faulty.setdefault(path, []).append(crate) + + if len(faulty) == 0: + print('✅ All prdocs are valid.') + else: + print('❌ Some prdocs are invalid.') + for path, crates in faulty.items(): + print(f'💥 {path} lists invalid crate: {", ".join(crates)}') + exit(1) + +def parse_args(): + parser = argparse.ArgumentParser(description='Check prdoc files') + parser.add_argument('root', help='The cargo workspace manifest', metavar='root', type=str, nargs=1) + parser.add_argument('prdoc', help='The prdoc files', metavar='prdoc', type=str, nargs='*') + args = parser.parse_args() + + if len(args.prdoc) == 0: + print('❌ Need at least one prdoc file as argument.') + exit(1) + + return { 'root': os.path.abspath(args.root[0]), 'prdocs': args.prdoc } + +if __name__ == '__main__': + args = parse_args() + check_prdoc_crate_names(args['root'], args['prdocs']) diff --git a/.github/scripts/check-workspace.py b/.github/scripts/check-workspace.py index d200122fee9f7035dce8c811e7c24c003d9545a4..1f8f103e4e157a8c1c804a618652741193ca5a00 100644 --- a/.github/scripts/check-workspace.py +++ b/.github/scripts/check-workspace.py @@ -18,7 +18,7 @@ def parse_args(): parser.add_argument('workspace_dir', help='The directory to check', metavar='workspace_dir', type=str, nargs=1) parser.add_argument('--exclude', help='Exclude crate paths from the check', metavar='exclude', type=str, nargs='*', default=[]) - + args = parser.parse_args() return (args.workspace_dir[0], args.exclude) @@ -26,7 +26,7 @@ def main(root, exclude): workspace_crates = get_members(root, exclude) all_crates = get_crates(root, exclude) print(f'📦 Found {len(all_crates)} crates in total') - + check_duplicates(workspace_crates) check_missing(workspace_crates, all_crates) check_links(all_crates) @@ -48,14 +48,14 @@ def get_members(workspace_dir, exclude): if not 'members' in root_manifest['workspace']: return [] - + members = [] for member in root_manifest['workspace']['members']: if member in exclude: print(f'❌ Excluded member should not appear in the workspace {member}') sys.exit(1) members.append(member) - + return members # List all members of the workspace. @@ -74,12 +74,12 @@ def get_crates(workspace_dir, exclude_crates) -> dict: with open(path, "r") as f: content = f.read() manifest = toml.loads(content) - + if 'workspace' in manifest: if root != workspace_dir: print("⏩ Excluded recursive workspace at %s" % path) continue - + # Cut off the root path and the trailing /Cargo.toml. path = path[len(workspace_dir)+1:-11] name = manifest['package']['name'] @@ -87,7 +87,7 @@ def get_crates(workspace_dir, exclude_crates) -> dict: print("⏩ Excluded crate %s at %s" % (name, path)) continue crates[name] = (path, manifest) - + return crates # Check that there are no duplicate entries in the workspace. @@ -138,23 +138,23 @@ def check_links(all_crates): if not 'path' in deps[dep]: broken.append((name, dep_name, "crate must be linked via `path`")) return - + def check_crate(deps): to_checks = ['dependencies', 'dev-dependencies', 'build-dependencies'] for to_check in to_checks: if to_check in deps: check_deps(deps[to_check]) - + # There could possibly target dependant deps: if 'target' in manifest: # Target dependant deps can only have one level of nesting: for _, target in manifest['target'].items(): check_crate(target) - + check_crate(manifest) - + links.sort() broken.sort() diff --git a/.github/scripts/common/lib.sh b/.github/scripts/common/lib.sh index bd12d9c6e6ff773f8513189a381d725243e53eb5..932a6d546c3706f50c74873c32cb8e61e3d33461 100755 --- a/.github/scripts/common/lib.sh +++ b/.github/scripts/common/lib.sh @@ -237,6 +237,61 @@ fetch_release_artifacts() { popd > /dev/null } +# Fetch the release artifacts like binary and signatures from S3. Assumes the ENV are set: +# - RELEASE_ID +# - GITHUB_TOKEN +# - REPO in the form paritytech/polkadot +fetch_release_artifacts_from_s3() { + echo "Version : $VERSION" + echo "Repo : $REPO" + echo "Binary : $BINARY" + OUTPUT_DIR=${OUTPUT_DIR:-"./release-artifacts/${BINARY}"} + echo "OUTPUT_DIR : $OUTPUT_DIR" + + URL_BASE=$(get_s3_url_base $BINARY) + echo "URL_BASE=$URL_BASE" + + URL_BINARY=$URL_BASE/$VERSION/$BINARY + URL_SHA=$URL_BASE/$VERSION/$BINARY.sha256 + URL_ASC=$URL_BASE/$VERSION/$BINARY.asc + + # Fetch artifacts + mkdir -p "$OUTPUT_DIR" + pushd "$OUTPUT_DIR" > /dev/null + + echo "Fetching artifacts..." + for URL in $URL_BINARY $URL_SHA $URL_ASC; do + echo "Fetching %s" "$URL" + curl --progress-bar -LO "$URL" || echo "Missing $URL" + done + + pwd + ls -al --color + popd > /dev/null + +} + +# Pass the name of the binary as input, it will +# return the s3 base url +function get_s3_url_base() { + name=$1 + case $name in + polkadot | polkadot-execute-worker | polkadot-prepare-worker | staking-miner) + printf "https://releases.parity.io/polkadot" + ;; + + polkadot-parachain) + printf "https://releases.parity.io/cumulus" + ;; + + *) + printf "UNSUPPORTED BINARY $name" + exit 1 + ;; + esac +} + + # Check the checksum for a given binary function check_sha256() { echo "Checking SHA256 for $1" @@ -248,13 +303,11 @@ function check_sha256() { function import_gpg_keys() { GPG_KEYSERVER=${GPG_KEYSERVER:-"keyserver.ubuntu.com"} SEC="9D4B2B6EB8F97156D19669A9FF0812D491B96798" - WILL="2835EAF92072BC01D188AF2C4A092B93E97CE1E2" EGOR="E6FC4D4782EB0FA64A4903CCDB7D3555DD3932D3" - MARA="533C920F40E73A21EEB7E9EBF27AEA7E7594C9CF" MORGAN="2E92A9D8B15D7891363D1AE8AF9E6C43F7F8C4CF" echo "Importing GPG keys from $GPG_KEYSERVER in parallel" - for key in $SEC $WILL $EGOR $MARA $MORGAN; do + for key in $SEC $EGOR $MORGAN; do ( echo "Importing GPG key $key" gpg --no-tty --quiet --keyserver $GPG_KEYSERVER --recv-keys $key @@ -344,3 +397,40 @@ function find_runtimes() { done echo $JSON } + +# Filter the version matches the particular pattern and return it. +# input: version (v1.8.0 or v1.8.0-rc1) +# output: none +filter_version_from_input() { + version=$1 + regex="(^v[0-9]+\.[0-9]+\.[0-9]+)$|(^v[0-9]+\.[0-9]+\.[0-9]+-rc[0-9]+)$" + + if [[ $version =~ $regex ]]; then + if [ -n "${BASH_REMATCH[1]}" ]; then + echo "${BASH_REMATCH[1]}" + elif [ -n "${BASH_REMATCH[2]}" ]; then + echo "${BASH_REMATCH[2]}" + fi + else + echo "Invalid version: $version" + exit 1 + fi + +} + +# Check if the release_id is valid number +# input: release_id +# output: release_id or exit 1 +check_release_id() { + input=$1 + + release_id=$(echo "$input" | sed 's/[^0-9]//g') + + if [[ $release_id =~ ^[0-9]+$ ]]; then + echo "$release_id" + else + echo "Invalid release_id from input: $input" + exit 1 + fi + +} diff --git a/.github/workflows/check-licenses.yml b/.github/workflows/check-licenses.yml index e1e92d288ceae235d23fa36c31d592092fe8b0ba..c32b6fcf89e06bb56cefc0517e1dcab1d1ef0f37 100644 --- a/.github/workflows/check-licenses.yml +++ b/.github/workflows/check-licenses.yml @@ -42,5 +42,4 @@ jobs: shopt -s globstar npx @paritytech/license-scanner scan \ --ensure-licenses ${{ env.LICENSES }} \ - --exclude ./substrate/bin/node-template \ -- ./substrate/**/*.rs diff --git a/.github/workflows/check-prdoc.yml b/.github/workflows/check-prdoc.yml index 5503b61d681728b0f1b8adb5af60f9e37b8afee2..c31dee06ec54a0154efc3ad46ff24c79de4d0d7b 100644 --- a/.github/workflows/check-prdoc.yml +++ b/.github/workflows/check-prdoc.yml @@ -56,4 +56,12 @@ jobs: run: | echo "Checking for PR#${GITHUB_PR}" echo "You can find more information about PRDoc at $PRDOC_DOC" - $ENGINE run --rm -v $PWD:/repo $IMAGE check -n ${GITHUB_PR} + $ENGINE run --rm -v $PWD:/repo -e RUST_LOG=info $IMAGE check -n ${GITHUB_PR} + + - name: Validate prdoc for PR#${{ github.event.pull_request.number }} + if: ${{ !contains(steps.get-labels.outputs.labels, 'R0') }} + run: | + echo "Validating PR#${GITHUB_PR}" + python3 --version + python3 -m pip install cargo-workspace==1.2.1 + python3 .github/scripts/check-prdoc.py Cargo.toml prdoc/pr_${GITHUB_PR}.prdoc diff --git a/.github/workflows/release-50_publish-docker.yml b/.github/workflows/release-50_publish-docker.yml index ecbac01cd3a5b2aaed679cfaf2ade0b04900531a..67e93ee96574de1f1e3e29f1bf6d90085865100d 100644 --- a/.github/workflows/release-50_publish-docker.yml +++ b/.github/workflows/release-50_publish-docker.yml @@ -36,7 +36,7 @@ on: -H "Authorization: Bearer ${GITHUB_TOKEN}" https://api.github.com/repos/$OWNER/$REPO/releases | \ jq '.[] | { name: .name, id: .id }' required: true - type: string + type: number registry: description: Container registry @@ -61,7 +61,6 @@ permissions: contents: write env: - RELEASE_ID: ${{ inputs.release_id }} ENGINE: docker REGISTRY: ${{ inputs.registry }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -71,6 +70,7 @@ env: # EVENT_ACTION: ${{ github.event.action }} EVENT_NAME: ${{ github.event_name }} IMAGE_TYPE: ${{ inputs.image_type }} + VERSION: ${{ inputs.version }} jobs: fetch-artifacts: # this job will be triggered for the polkadot-parachain rc and release or polkadot rc image build @@ -95,13 +95,16 @@ jobs: # chmod a+x $BINARY # ls -al - - name: Fetch rc artifacts or release artifacts based on release id + - name: Fetch rc artifacts or release artifacts from s3 based on version #this step runs only if the workflow is triggered manually if: ${{ env.EVENT_NAME == 'workflow_dispatch' }} run: | . ./.github/scripts/common/lib.sh - fetch_release_artifacts + VERSION=$(filter_version_from_input "${{ inputs.version }}") + echo "VERSION=${VERSION}" >> $GITHUB_ENV + + fetch_release_artifacts_from_s3 - name: Cache the artifacts uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c # v3.3.3 @@ -147,7 +150,10 @@ jobs: if: ${{ env.IMAGE_TYPE == 'rc' }} id: fetch_rc_refs run: | - release=release-${{ inputs.release_id }} && \ + . ./.github/scripts/common/lib.sh + + RELEASE_ID=$(check_release_id "${{ inputs.release_id }}") + release=release-$RELEASE_ID && \ echo "release=${release}" >> $GITHUB_OUTPUT commit=$(git rev-parse --short HEAD) && \ diff --git a/.github/workflows/release-99_notif-published.yml b/.github/workflows/release-99_notif-published.yml index b35120ca4e128beaa37047b0ac3f21b02f4da663..05c9d6a47f551860c51e318b01b495ca662e902e 100644 --- a/.github/workflows/release-99_notif-published.yml +++ b/.github/workflows/release-99_notif-published.yml @@ -8,22 +8,14 @@ on: jobs: ping_matrix: runs-on: ubuntu-latest + environment: release strategy: matrix: channel: # Internal - - name: 'RelEng: Cumulus Release Coordination' - room: '!NAEMyPAHWOiOQHsvus:parity.io' - pre-releases: true - name: "RelEng: Polkadot Release Coordination" room: '!cqAmzdIcbOFwrdrubV:parity.io' pre-release: true - - name: 'General: Rust, Polkadot, Substrate' - room: '!aJymqQYtCjjqImFLSb:parity.io' - pre-release: false - - name: 'Team: DevOps' - room: '!lUslSijLMgNcEKcAiE:parity.io' - pre-release: true # External - name: 'Ledger <> Polkadot Coordination' @@ -31,18 +23,15 @@ jobs: pre-release: true # Public - # - name: '#KusamaValidatorLounge:polkadot.builders' - # room: '!LhjZccBOqFNYKLdmbb:polkadot.builders' - # pre-releases: false - # - name: '#kusama-announcements:matrix.parity.io' - # room: '!FMwxpQnYhRCNDRsYGI:matrix.parity.io' - # pre-release: false - # - name: '#polkadotvalidatorlounge:web3.foundation' - # room: '!NZrbtteFeqYKCUGQtr:matrix.parity.io' - # pre-release: false - # - name: '#polkadot-announcements:matrix.parity.io' - # room: '!UqHPWiCBGZWxrmYBkF:matrix.parity.io' - # pre-release: false + - name: '#polkadotvalidatorlounge:web3.foundation' + room: '!NZrbtteFeqYKCUGQtr:matrix.parity.io' + pre-releases: false + - name: '#polkadot-announcements:parity.io' + room: '!UqHPWiCBGZWxrmYBkF:matrix.parity.io' + pre-releases: false + - name: '#kusama-announce:parity.io' + room: '!FMwxpQnYhRCNDRsYGI:matrix.parity.io' + pre-releases: false steps: - name: Matrix notification to ${{ matrix.channel.name }} @@ -53,7 +42,9 @@ jobs: access_token: ${{ secrets.RELEASENOTES_MATRIX_V2_ACCESS_TOKEN }} server: m.parity.io message: | - A (pre)release has been ${{github.event.action}} in **${{github.event.repository.full_name}}:**
+ @room + + A new node release has been ${{github.event.action}} in **${{github.event.repository.full_name}}:**
Release version: [${{github.event.release.tag_name}}](${{github.event.release.html_url}}) ----- diff --git a/.github/workflows/subsystem-benchmarks.yml b/.github/workflows/subsystem-benchmarks.yml new file mode 100644 index 0000000000000000000000000000000000000000..37a9e0f4680c3ef9c8fcdda94c767227e2bfb051 --- /dev/null +++ b/.github/workflows/subsystem-benchmarks.yml @@ -0,0 +1,42 @@ +# The actions takes json file as input and runs github-action-benchmark for it. + +on: + workflow_dispatch: + inputs: + benchmark-data-dir-path: + description: "Path to the benchmark data directory" + required: true + type: string + output-file-path: + description: "Path to the benchmark data file" + required: true + type: string + +jobs: + subsystem-benchmarks: + runs-on: ubuntu-latest + steps: + - name: Checkout Sources + uses: actions/checkout@v4.1.2 + with: + fetch-depth: 0 + ref: "gh-pages" + + - name: Copy bench results + id: step_one + run: | + cp bench/gitlab/${{ github.event.inputs.output-file-path }} ${{ github.event.inputs.output-file-path }} + + - name: Switch branch + id: step_two + run: | + git checkout master + + - name: Store benchmark result + uses: benchmark-action/github-action-benchmark@v1 + with: + tool: "customSmallerIsBetter" + output-file-path: ${{ github.event.inputs.output-file-path }} + benchmark-data-dir-path: "bench/${{ github.event.inputs.benchmark-data-dir-path }}" + github-token: ${{ secrets.GITHUB_TOKEN }} + auto-push: true diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7f8796ca51248acb8be92fcb9f76e280b18e605e..93a6ccb9f8fbabc48a31d403ae4ed17bc2c2966a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -147,6 +147,13 @@ default: - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs - if: $CI_COMMIT_REF_NAME =~ /^gh-readonly-queue.*$/ # merge queues +.publish-gh-pages-refs: + rules: + - if: $CI_PIPELINE_SOURCE == "pipeline" + when: never + - if: $CI_PIPELINE_SOURCE == "web" && $CI_COMMIT_REF_NAME == "master" + - if: $CI_COMMIT_REF_NAME == "master" + # handle the specific case where benches could store incorrect bench data because of the downstream staging runs # exclude cargo-check-benches from such runs .test-refs-check-benches: diff --git a/.gitlab/pipeline/build.yml b/.gitlab/pipeline/build.yml index 15b4869997be186c71cecbc89060b7591d71ffa3..44d66eb2f5eb73dcdab8dad2b4b35165cf9dc4c7 100644 --- a/.gitlab/pipeline/build.yml +++ b/.gitlab/pipeline/build.yml @@ -91,7 +91,7 @@ build-rustdoc: - .run-immediately variables: SKIP_WASM_BUILD: 1 - RUSTDOCFLAGS: "" + RUSTDOCFLAGS: "--default-theme=ayu --html-in-header ./docs/sdk/headers/header.html --extend-css ./docs/sdk/headers/theme.css" artifacts: name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}-doc" when: on_success @@ -337,7 +337,7 @@ build-runtimes-polkavm: - .common-refs - .run-immediately script: - - SUBSTRATE_RUNTIME_TARGET=riscv cargo check -p minimal-runtime + - SUBSTRATE_RUNTIME_TARGET=riscv cargo check -p minimal-template-runtime - SUBSTRATE_RUNTIME_TARGET=riscv cargo check -p westend-runtime - SUBSTRATE_RUNTIME_TARGET=riscv cargo check -p rococo-runtime - SUBSTRATE_RUNTIME_TARGET=riscv cargo check -p polkadot-test-runtime @@ -350,7 +350,7 @@ build-runtimes-polkavm: - .run-immediately # - .collect-artifact variables: - # this variable gets overriden by "rusty-cachier environment inject", use the value as default + # this variable gets overridden by "rusty-cachier environment inject", use the value as default CARGO_TARGET_DIR: "$CI_PROJECT_DIR/target" before_script: - mkdir -p ./artifacts/subkey diff --git a/.gitlab/pipeline/check.yml b/.gitlab/pipeline/check.yml index cdb5d1b05d09d53574316488681bfba667d43682..52da33550508ede16c9577346e6985d869b5e8ae 100644 --- a/.gitlab/pipeline/check.yml +++ b/.gitlab/pipeline/check.yml @@ -108,8 +108,10 @@ check-toml-format: export RUST_LOG=remote-ext=debug,runtime=debug echo "---------- Downloading try-runtime CLI ----------" - curl -sL https://github.com/paritytech/try-runtime-cli/releases/download/v0.5.0/try-runtime-x86_64-unknown-linux-musl -o try-runtime + curl -sL https://github.com/paritytech/try-runtime-cli/releases/download/v0.5.4/try-runtime-x86_64-unknown-linux-musl -o try-runtime chmod +x ./try-runtime + echo "Using try-runtime-cli version:" + ./try-runtime --version echo "---------- Building ${PACKAGE} runtime ----------" time cargo build --release --locked -p "$PACKAGE" --features try-runtime @@ -257,3 +259,19 @@ find-fail-ci-phrase: echo "No $ASSERT_REGEX was found, exiting with 0"; exit 0; fi + +check-core-crypto-features: + stage: check + extends: + - .docker-env + - .common-refs + script: + - pushd substrate/primitives/core + - ./check-features-variants.sh + - popd + - pushd substrate/primitives/application-crypto + - ./check-features-variants.sh + - popd + - pushd substrate/primitives/keyring + - ./check-features-variants.sh + - popd diff --git a/.gitlab/pipeline/publish.yml b/.gitlab/pipeline/publish.yml index b73acb560f67f93e540826b95fcf075374189846..bd9387f3c07fc839d2d037776b4b1e4f56150210 100644 --- a/.gitlab/pipeline/publish.yml +++ b/.gitlab/pipeline/publish.yml @@ -3,16 +3,13 @@ publish-rustdoc: stage: publish - extends: .kubernetes-env + extends: + - .kubernetes-env + - .publish-gh-pages-refs variables: CI_IMAGE: node:18 GIT_DEPTH: 100 RUSTDOCS_DEPLOY_REFS: "master" - rules: - - if: $CI_PIPELINE_SOURCE == "pipeline" - when: never - - if: $CI_PIPELINE_SOURCE == "web" && $CI_COMMIT_REF_NAME == "master" - - if: $CI_COMMIT_REF_NAME == "master" needs: - job: build-rustdoc artifacts: true @@ -60,9 +57,76 @@ publish-rustdoc: - git commit -m "___Updated docs for ${CI_COMMIT_REF_NAME}___" || echo "___Nothing to commit___" - git push origin gh-pages --force + # artificial sleep to publish gh-pages + - sleep 300 after_script: - rm -rf .git/ ./* +publish-subsystem-benchmarks: + stage: publish + variables: + CI_IMAGE: "paritytech/tools:latest" + extends: + - .kubernetes-env + - .publish-gh-pages-refs + needs: + - job: subsystem-regression-tests + artifacts: true + - job: publish-rustdoc + artifacts: false + script: + # setup ssh + - eval $(ssh-agent) + - ssh-add - <<< ${GITHUB_SSH_PRIV_KEY} + - mkdir ~/.ssh && touch ~/.ssh/known_hosts + - ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts + # Set git config + - rm -rf .git/config + - git config user.email "devops-team@parity.io" + - git config user.name "${GITHUB_USER}" + - git config remote.origin.url "git@github.com:/paritytech/${CI_PROJECT_NAME}.git" + - git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" + - git fetch origin gh-pages + # Push result to github + - git checkout gh-pages + - mkdir -p bench/gitlab/ || echo "Directory exists" + - rm -rf bench/gitlab/*.json || echo "No json files" + - cp -r charts/*.json bench/gitlab/ + - git add bench/gitlab/ + - git commit -m "Add json files with benchmark results for ${CI_COMMIT_REF_NAME}" + - git push origin gh-pages + # artificial sleep to publish gh-pages + - sleep 300 + allow_failure: true + after_script: + - rm -rf .git/ ./* + +trigger_workflow: + stage: deploy + extends: + - .kubernetes-env + - .publish-gh-pages-refs + needs: + - job: publish-subsystem-benchmarks + artifacts: false + - job: subsystem-regression-tests + artifacts: true + script: + - echo "Triggering workflow" + - | + for benchmark in $(ls charts/*.json); do + export bencmark_name=$(basename $benchmark) + echo "Benchmark: $bencmark_name" + export benchmark_dir=$(echo $bencmark_name | sed 's/\.json//') + curl -q -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token $GITHUB_TOKEN" \ + https://api.github.com/repos/paritytech-stg/${CI_PROJECT_NAME}/actions/workflows/subsystem-benchmarks.yml/dispatches \ + -d '{"ref":"refs/heads/master","inputs":{"benchmark-data-dir-path":"'$benchmark_dir'","output-file-path":"'$bencmark_name'"}}' + sleep 300 + done + allow_failure: true + # note: images are used not only in zombienet but also in rococo, wococo and versi .build-push-image: image: $BUILDAH_IMAGE diff --git a/.gitlab/pipeline/test.yml b/.gitlab/pipeline/test.yml index b5e26d194896aad7839dce34460409fe9ceaa045..d97f9da986cd6db55ab4f1de2a9fa17002ffd564 100644 --- a/.gitlab/pipeline/test.yml +++ b/.gitlab/pipeline/test.yml @@ -25,6 +25,7 @@ test-linux-stable: # "upgrade_version_checks_should_work" is currently failing - | time cargo nextest run \ + --filter-expr 'not deps(/polkadot-subsystem-bench/)' \ --workspace \ --locked \ --release \ @@ -48,6 +49,7 @@ test-linux-stable: - target/nextest/default/junit.xml reports: junit: target/nextest/default/junit.xml + timeout: 90m test-linux-oldkernel-stable: extends: test-linux-stable @@ -68,7 +70,7 @@ test-linux-stable-runtime-benchmarks: # but still want to have debug assertions. RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" script: - - time cargo nextest run --workspace --features runtime-benchmarks benchmark --locked --cargo-profile testnet + - time cargo nextest run --filter-expr 'not deps(/polkadot-subsystem-bench/)' --workspace --features runtime-benchmarks benchmark --locked --cargo-profile testnet # can be used to run all tests # test-linux-stable-all: @@ -492,3 +494,22 @@ test-syscalls: printf "The x86_64 syscalls used by the worker binaries have changed. Please review if this is expected and update polkadot/scripts/list-syscalls/*-worker-syscalls as needed.\n"; fi allow_failure: false # this rarely triggers in practice + +subsystem-regression-tests: + stage: test + artifacts: + name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}" + when: on_success + expire_in: 1 days + paths: + - charts/ + extends: + - .docker-env + - .common-refs + - .run-immediately + script: + - cargo bench --profile=testnet -p polkadot-availability-recovery --bench availability-recovery-regression-bench --features subsystem-benchmarks + - cargo bench --profile=testnet -p polkadot-availability-distribution --bench availability-distribution-regression-bench --features subsystem-benchmarks + tags: + - benchmark + allow_failure: true diff --git a/.gitlab/pipeline/zombienet.yml b/.gitlab/pipeline/zombienet.yml index 55120e66d0e53c740b16a7ee6276230f42c172ef..82341eb709fee91f9d55f1c4df2e7ddb86940e0c 100644 --- a/.gitlab/pipeline/zombienet.yml +++ b/.gitlab/pipeline/zombienet.yml @@ -1,7 +1,7 @@ .zombienet-refs: extends: .build-refs variables: - ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.91" + ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.98" include: # substrate tests diff --git a/.gitlab/pipeline/zombienet/polkadot.yml b/.gitlab/pipeline/zombienet/polkadot.yml index 9bd62f662d79ccb24cc6ec2e76b7f5a0d69abeb2..26fd951df541aafb2aa12a072562a242aca0d646 100644 --- a/.gitlab/pipeline/zombienet/polkadot.yml +++ b/.gitlab/pipeline/zombienet/polkadot.yml @@ -158,6 +158,14 @@ zombienet-polkadot-functional-0011-async-backing-6-seconds-rate: --local-dir="${LOCAL_DIR}/functional" --test="0011-async-backing-6-seconds-rate.zndsl" +zombienet-polkadot-functional-0012-elastic-scaling-mvp: + extends: + - .zombienet-polkadot-common + script: + - /home/nonroot/zombie-net/scripts/ci/run-test-local-env-manager.sh + --local-dir="${LOCAL_DIR}/functional" + --test="0012-elastic-scaling-mvp.zndsl" + zombienet-polkadot-smoke-0001-parachains-smoke-test: extends: - .zombienet-polkadot-common diff --git a/.gitlab/rust-features.sh b/.gitlab/rust-features.sh index c0ac192a6ec69ba16abb3bad2ec49de7e9cebb61..c3ec61ab8714768c9a49f2eb2e1e544706a1d875 100755 --- a/.gitlab/rust-features.sh +++ b/.gitlab/rust-features.sh @@ -15,7 +15,7 @@ # # The steps of this script: # 1. Check that all required dependencies are installed. -# 2. Check that all rules are fullfilled for the whole workspace. If not: +# 2. Check that all rules are fulfilled for the whole workspace. If not: # 4. Check all crates to find the offending ones. # 5. Print all offending crates and exit with code 1. # diff --git a/Cargo.lock b/Cargo.lock index 9a1abf936d4b7b1c3d072b1d5bf4b643e4674931..d4b598ab22342b20c77977139ea82b1235b97cdb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,7 +27,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ - "gimli 0.28.0", + "gimli 0.28.1", ] [[package]] @@ -42,15 +42,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" -[[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", -] - [[package]] name = "aead" version = "0.5.2" @@ -63,52 +54,26 @@ dependencies = [ [[package]] name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if", - "cipher 0.3.0", - "cpufeatures", - "opaque-debug 0.3.0", -] - -[[package]] -name = "aes" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher 0.4.4", "cpufeatures", ] -[[package]] -name = "aes-gcm" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc3be92e19a7ef47457b8e6f90707e12b6ac5d20c6f3866584fa3be0787d839f" -dependencies = [ - "aead 0.4.3", - "aes 0.7.5", - "cipher 0.3.0", - "ctr 0.7.0", - "ghash 0.4.4", - "subtle 2.5.0", -] - [[package]] name = "aes-gcm" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ - "aead 0.5.2", - "aes 0.8.3", + "aead", + "aes", "cipher 0.4.4", - "ctr 0.9.2", - "ghash 0.5.0", + "ctr", + "ghash", "subtle 2.5.0", ] @@ -118,19 +83,19 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.12", "once_cell", "version_check", ] [[package]] name = "ahash" -version = "0.8.8" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.2.10", + "getrandom 0.2.12", "once_cell", "version_check", "zerocopy", @@ -138,9 +103,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.4" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -173,25 +138,24 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc0fac0fc16baf1f63f78b47c3d24718f3619b0714076f6a02957d808d52cbef" +checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac" dependencies = [ "alloy-rlp-derive", "arrayvec 0.7.4", "bytes", - "smol_str", ] [[package]] name = "alloy-rlp-derive" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0391754c09fab4eae3404d19d0d297aa1c670c1775ab51d8a5312afeca23157" +checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -202,11 +166,11 @@ checksum = "8a98ad1696a2e17f010ae8e43e9f2a1e930ed176a8e3ff77acfeff6dfb07b42c" dependencies = [ "const-hex", "dunce", - "heck", + "heck 0.4.1", "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", "syn-solidity", "tiny-keccak", ] @@ -261,9 +225,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.11" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", @@ -275,43 +239,43 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.2" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" [[package]] name = "approx" @@ -322,6 +286,20 @@ dependencies = [ "num-traits", ] +[[package]] +name = "aquamarine" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da02abba9f9063d786eab1509833ebb2fac0f966862ca59439c76b9c566760" +dependencies = [ + "include_dir", + "itertools 0.10.5", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "aquamarine" version = "0.5.0" @@ -333,7 +311,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -593,20 +571,6 @@ dependencies = [ "hashbrown 0.13.2", ] -[[package]] -name = "ark-scale" -version = "0.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bd73bb6ddb72630987d37fa963e99196896c0d0ea81b7c894567e74a2f83af" -dependencies = [ - "ark-ec", - "ark-ff 0.4.2", - "ark-serialize 0.4.2", - "ark-std 0.4.0", - "parity-scale-codec", - "scale-info", -] - [[package]] name = "ark-scale" version = "0.0.12" @@ -711,9 +675,9 @@ checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" [[package]] name = "array-bytes" -version = "6.1.0" +version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b1c5a481ec30a5abd8dfbd94ab5cf1bb4e9a66be7f1b3b322f2f1170c200fd" +checksum = "6f840fb7195bcfc5e17ea40c26e5ce6d5b9ce5d584466e17703209657e459ae0" [[package]] name = "arrayref" @@ -730,12 +694,6 @@ 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" @@ -783,14 +741,14 @@ dependencies = [ [[package]] name = "assert_cmd" -version = "2.0.12" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" +checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8" dependencies = [ "anstyle", "bstr", "doc-comment", - "predicates 3.0.3", + "predicates 3.1.0", "predicates-core", "predicates-tree", "wait-timeout", @@ -833,6 +791,7 @@ dependencies = [ "pallet-xcm", "parachains-common", "parity-scale-codec", + "penpal-runtime", "rococo-runtime", "rococo-system-emulated-network", "sp-runtime", @@ -954,6 +913,7 @@ dependencies = [ "pallet-xcm", "parachains-common", "parity-scale-codec", + "penpal-runtime", "polkadot-runtime-common", "sp-runtime", "staging-xcm", @@ -1054,6 +1014,7 @@ dependencies = [ "pallet-balances", "pallet-collator-selection", "pallet-session", + "pallet-timestamp", "pallet-xcm", "pallet-xcm-bridge-hub-router", "parachains-common", @@ -1102,17 +1063,30 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-channel" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" +dependencies = [ + "concurrent-queue", + "event-listener 5.2.0", + "event-listener-strategy 0.5.0", + "futures-core", + "pin-project-lite 0.2.13", +] + [[package]] name = "async-executor" -version = "1.5.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" +checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" dependencies = [ - "async-lock 2.8.0", + "async-lock 3.3.0", "async-task", "concurrent-queue", - "fastrand 1.9.0", - "futures-lite", + "fastrand 2.0.2", + "futures-lite 2.3.0", "slab", ] @@ -1125,7 +1099,7 @@ dependencies = [ "async-lock 2.8.0", "autocfg", "blocking", - "futures-lite", + "futures-lite 1.13.0", ] [[package]] @@ -1138,16 +1112,35 @@ dependencies = [ "autocfg", "cfg-if", "concurrent-queue", - "futures-lite", + "futures-lite 1.13.0", "log", "parking", - "polling", - "rustix 0.37.23", + "polling 2.8.0", + "rustix 0.37.27", "slab", - "socket2 0.4.9", + "socket2 0.4.10", "waker-fn", ] +[[package]] +name = "async-io" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" +dependencies = [ + "async-lock 3.3.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.3.0", + "parking", + "polling 3.6.0", + "rustix 0.38.32", + "slab", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "async-lock" version = "2.8.0" @@ -1164,37 +1157,53 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ "event-listener 4.0.3", - "event-listener-strategy", - "pin-project-lite 0.2.12", + "event-listener-strategy 0.4.0", + "pin-project-lite 0.2.13", ] [[package]] name = "async-net" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4051e67316bc7eff608fe723df5d32ed639946adcd69e07df41fd42a7b411f1f" +checksum = "0434b1ed18ce1cf5769b8ac540e33f01fa9471058b5e89da9e06f3c882a8c12f" dependencies = [ - "async-io", - "autocfg", + "async-io 1.13.0", "blocking", - "futures-lite", + "futures-lite 1.13.0", ] [[package]] name = "async-process" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" dependencies = [ - "async-io", + "async-io 1.13.0", "async-lock 2.8.0", - "autocfg", + "async-signal", "blocking", "cfg-if", - "event-listener 2.5.3", - "futures-lite", - "rustix 0.37.23", - "signal-hook", + "event-listener 3.1.0", + "futures-lite 1.13.0", + "rustix 0.38.32", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-signal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +dependencies = [ + "async-io 2.3.2", + "async-lock 2.8.0", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 0.38.32", + "signal-hook-registry", + "slab", "windows-sys 0.48.0", ] @@ -1206,7 +1215,7 @@ checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", - "pin-project-lite 0.2.12", + "pin-project-lite 0.2.13", ] [[package]] @@ -1217,24 +1226,24 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] name = "async-task" -version = "4.4.0" +version = "4.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" +checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "async-trait" -version = "0.1.74" +version = "0.1.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -1247,7 +1256,7 @@ dependencies = [ "futures-sink", "futures-util", "memchr", - "pin-project-lite 0.2.12", + "pin-project-lite 0.2.13", ] [[package]] @@ -1258,9 +1267,9 @@ checksum = "a8ab6b55fe97976e46f91ddbed8d147d966475dc29b2032757ba47e02376fbc3" [[package]] name = "atomic-waker" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "atty" @@ -1275,27 +1284,26 @@ dependencies = [ [[package]] name = "auto_impl" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ - "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.55", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line 0.21.0", "cc", @@ -1319,7 +1327,7 @@ dependencies = [ "ark-std 0.4.0", "dleq_vrf", "fflonk", - "merlin 3.0.0", + "merlin", "rand_chacha 0.3.1", "rand_core 0.6.4", "ring 0.1.0", @@ -1359,15 +1367,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "basic-toml" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2db21524cad41c5591204d22d75e1970a2d1f71060214ca931dc7d5afe2c14e5" -dependencies = [ - "serde", -] - [[package]] name = "beef" version = "0.5.2" @@ -1381,7 +1380,7 @@ dependencies = [ name = "binary-merkle-tree" version = "13.0.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "env_logger 0.9.3", "hash-db", "log", @@ -1410,13 +1409,13 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "prettyplease 0.2.12", + "prettyplease 0.2.17", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -1425,9 +1424,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" dependencies = [ - "bitcoin_hashes", - "rand", - "rand_core 0.6.4", + "bitcoin_hashes 0.11.0", "serde", "unicode-normalization", ] @@ -1447,12 +1444,28 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[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.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" +[[package]] +name = "bitcoin_hashes" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +dependencies = [ + "bitcoin-internals", + "hex-conservative", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -1461,9 +1474,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bitvec" @@ -1511,31 +1524,31 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" +checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" dependencies = [ "arrayref", "arrayvec 0.7.4", - "constant_time_eq 0.2.6", + "constant_time_eq 0.3.0", ] [[package]] name = "blake2s_simd" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f" +checksum = "94230421e395b9920d23df13ea5d77a20e1725331f90fbbf6df6040b33f756ae" dependencies = [ "arrayref", "arrayvec 0.7.4", - "constant_time_eq 0.2.6", + "constant_time_eq 0.3.0", ] [[package]] name = "blake3" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" dependencies = [ "arrayref", "arrayvec 0.7.4", @@ -1544,18 +1557,6 @@ dependencies = [ "constant_time_eq 0.3.0", ] -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding", - "byte-tools", - "byteorder", - "generic-array 0.12.4", -] - [[package]] name = "block-buffer" version = "0.9.0" @@ -1574,28 +1575,20 @@ 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 = "blocking" -version = "1.3.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ - "async-channel", - "async-lock 2.8.0", + "async-channel 2.2.0", + "async-lock 3.3.0", "async-task", - "atomic-waker", - "fastrand 1.9.0", - "futures-lite", - "log", + "fastrand 2.0.2", + "futures-io", + "futures-lite 2.3.0", + "piper", + "tracing", ] [[package]] @@ -2099,6 +2092,7 @@ dependencies = [ "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", + "pallet-timestamp", "pallet-utility", "parachains-common", "parachains-runtimes-test-utils", @@ -2143,6 +2137,7 @@ dependencies = [ "pallet-message-queue", "pallet-xcm", "parachains-common", + "parity-scale-codec", "rococo-westend-system-emulated-network", "sp-runtime", "staging-xcm", @@ -2279,21 +2274,21 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bs58" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ "tinyvec", ] [[package]] name = "bstr" -version = "1.6.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", - "regex-automata 0.3.6", + "regex-automata 0.4.6", "serde", ] @@ -2308,9 +2303,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.2" +version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3b1be7772ee4501dba05acbe66bb1e8760f6a6c474a36035631638e4415f130" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" [[package]] name = "byte-slice-cast" @@ -2326,9 +2321,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytemuck" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" [[package]] name = "byteorder" @@ -2338,9 +2333,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.4.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "bzip2-sys" @@ -2374,9 +2369,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.3" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ "serde", ] @@ -2389,7 +2384,7 @@ checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", - "semver 1.0.18", + "semver 1.0.22", "serde", "serde_json", "thiserror", @@ -2403,9 +2398,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" dependencies = [ "jobserver", "libc", @@ -2422,9 +2417,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.5" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03915af431787e6ffdcc74c645077518c6b6e01f80b761e0fbbfa288536311b3" +checksum = "fa50868b64a9a6fda9d593ce778849ea8715cd2a3d2cc17ffdb4a2f2f2f1961d" dependencies = [ "smallvec", ] @@ -2451,18 +2446,6 @@ dependencies = [ "keystream", ] -[[package]] -name = "chacha20" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" -dependencies = [ - "cfg-if", - "cipher 0.3.0", - "cpufeatures", - "zeroize", -] - [[package]] name = "chacha20" version = "0.9.1" @@ -2476,36 +2459,36 @@ dependencies = [ [[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", - "chacha20 0.8.2", - "cipher 0.3.0", - "poly1305 0.7.2", + "aead", + "chacha20", + "cipher 0.4.4", + "poly1305", "zeroize", ] [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.48.5", + "windows-targets 0.52.4", ] [[package]] name = "ciborium" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", @@ -2514,15 +2497,15 @@ dependencies = [ [[package]] name = "ciborium-io" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", @@ -2550,15 +2533,6 @@ 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" @@ -2567,6 +2541,7 @@ checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", + "zeroize", ] [[package]] @@ -2580,9 +2555,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" dependencies = [ "glob", "libc", @@ -2608,28 +2583,28 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.1" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", - "clap_derive 4.5.0", + "clap_derive 4.5.4", ] [[package]] name = "clap-num" -version = "1.0.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488557e97528174edaa2ee268b23a809e0c598213a4bbcb4f34575a46fda147e" +checksum = "0e063d263364859dc54fb064cedb7c122740cd4733644b14b176c097f51e8ab7" dependencies = [ "num-traits", ] [[package]] name = "clap_builder" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", @@ -2640,11 +2615,11 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.4.0" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "586a385f7ef2f8b4d86bddaa0c094794e7ccbfe5ffef1f434fe928143fc783a5" +checksum = "885e4d7d5af40bfb99ae6f9433e292feac98d452dcb3ec3d25dfe7552b77da8c" dependencies = [ - "clap 4.5.1", + "clap 4.5.4", ] [[package]] @@ -2653,7 +2628,7 @@ version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro-error", "proc-macro2", "quote", @@ -2662,14 +2637,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.0" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -2689,13 +2664,12 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "coarsetime" -version = "0.1.23" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90d114103adbc625300f346d4d09dfb4ab1c4a8df6868435dd903392ecf4354" +checksum = "13b3839cf01bb7960114be3ccf2340f541b6d0c81f8690b007b2b39f750f7e5d" dependencies = [ "libc", - "once_cell", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasix", "wasm-bindgen", ] @@ -2798,9 +2772,9 @@ dependencies = [ [[package]] name = "color-eyre" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" dependencies = [ "backtrace", "eyre", @@ -2811,18 +2785,18 @@ dependencies = [ [[package]] name = "color-print" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2a5e6504ed8648554968650feecea00557a3476bc040d0ffc33080e66b646d0" +checksum = "7a858372ff14bab9b1b30ea504f2a4bc534582aee3e42ba2d41d2a7baba63d5d" dependencies = [ "color-print-proc-macro", ] [[package]] name = "color-print-proc-macro" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51beaa537d73d2d1ff34ee70bc095f170420ab2ec5d687ecd3ec2b0d092514b" +checksum = "57e37866456a721d0a404439a1adae37a31be4e0055590d053dfe6981e05003f" dependencies = [ "nom", "proc-macro2", @@ -2838,11 +2812,10 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "colored" -version = "2.0.4" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" dependencies = [ - "is-terminal", "lazy_static", "windows-sys 0.48.0", ] @@ -2870,7 +2843,7 @@ dependencies = [ "ark-std 0.4.0", "fflonk", "getrandom_or_panic", - "merlin 3.0.0", + "merlin", "rand_chacha 0.3.1", ] @@ -2882,9 +2855,9 @@ checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101" [[package]] name = "concurrent-queue" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" dependencies = [ "crossbeam-utils", ] @@ -2914,9 +2887,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.10.0" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5104de16b218eddf8e34ffe2f86f74bfa4e61e95a1b89732fccf6325efd0557" +checksum = "5ba00838774b4ab0233e355d26710fbfc8327a05c017f6dc4873f876d1f79f78" dependencies = [ "cfg-if", "cpufeatures", @@ -2927,29 +2900,27 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const-random" -version = "0.1.15" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" dependencies = [ "const-random-macro", - "proc-macro-hack", ] [[package]] name = "const-random-macro" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.12", "once_cell", - "proc-macro-hack", "tiny-keccak", ] @@ -2959,12 +2930,6 @@ 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 = "constant_time_eq" version = "0.3.0" @@ -2973,9 +2938,9 @@ checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "constcat" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f272d0c4cf831b4fa80ee529c7707f76585986e910e1fbce1d7921970bc1a241" +checksum = "cd7e35aee659887cbfb97aaf227ac12cad1a9d7c71e55ff3376839ed4e282d08" [[package]] name = "contracts-rococo-runtime" @@ -3048,9 +3013,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -3058,9 +3023,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "core2" @@ -3228,9 +3193,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -3335,9 +3300,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] @@ -3379,7 +3344,7 @@ dependencies = [ "anes", "cast", "ciborium", - "clap 4.5.1", + "clap 4.5.4", "criterion-plot", "futures", "is-terminal", @@ -3408,58 +3373,39 @@ dependencies = [ "itertools 0.10.5", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - [[package]] name = "crossbeam-deque" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.15" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", - "memoffset 0.9.0", - "scopeguard", ] [[package]] name = "crossbeam-queue" -version = "0.3.8" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crunchy" @@ -3469,9 +3415,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.5.2" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array 0.14.7", "rand_core 0.6.4", @@ -3510,25 +3456,6 @@ dependencies = [ "subtle 2.5.0", ] -[[package]] -name = "crypto-mac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e" -dependencies = [ - "generic-array 0.14.7", - "subtle 2.5.0", -] - -[[package]] -name = "ctr" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a232f92a03f37dd7d7dd2adc67166c77e9cd88de5b019b9a9eecfaeaf7bfd481" -dependencies = [ - "cipher 0.3.0", -] - [[package]] name = "ctr" version = "0.9.2" @@ -3542,7 +3469,7 @@ dependencies = [ name = "cumulus-client-cli" version = "0.7.0" dependencies = [ - "clap 4.5.1", + "clap 4.5.4", "parity-scale-codec", "sc-chain-spec", "sc-cli", @@ -3872,6 +3799,7 @@ dependencies = [ "pallet-message-queue", "parity-scale-codec", "polkadot-parachain-primitives", + "polkadot-runtime-common", "polkadot-runtime-parachains", "rand", "sc-client-api", @@ -3897,10 +3825,10 @@ dependencies = [ name = "cumulus-pallet-parachain-system-proc-macro" version = "0.6.0" dependencies = [ - "proc-macro-crate 3.0.0", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -4045,6 +3973,25 @@ dependencies = [ "sp-trie", ] +[[package]] +name = "cumulus-primitives-storage-weight-reclaim" +version = "1.0.0" +dependencies = [ + "cumulus-primitives-core", + "cumulus-primitives-proof-size-hostfunction", + "cumulus-test-runtime", + "docify 0.2.7", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std 14.0.0", + "sp-trie", +] + [[package]] name = "cumulus-primitives-timestamp" version = "0.7.0" @@ -4065,7 +4012,6 @@ dependencies = [ "frame-support", "log", "pallet-asset-conversion", - "pallet-xcm-benchmarks", "parity-scale-codec", "polkadot-runtime-common", "polkadot-runtime-parachains", @@ -4126,7 +4072,7 @@ dependencies = [ name = "cumulus-relay-chain-minimal-node" version = "0.7.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "async-trait", "cumulus-primitives-core", "cumulus-relay-chain-interface", @@ -4145,6 +4091,7 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-primitives", + "polkadot-service", "sc-authority-discovery", "sc-client-api", "sc-network", @@ -4207,6 +4154,7 @@ dependencies = [ "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", + "cumulus-primitives-storage-weight-reclaim", "cumulus-test-relay-sproof-builder", "cumulus-test-runtime", "cumulus-test-service", @@ -4251,6 +4199,7 @@ version = "0.1.0" dependencies = [ "cumulus-pallet-parachain-system", "cumulus-primitives-core", + "cumulus-primitives-storage-weight-reclaim", "frame-executive", "frame-support", "frame-system", @@ -4283,7 +4232,7 @@ name = "cumulus-test-service" version = "0.1.0" dependencies = [ "async-trait", - "clap 4.5.1", + "clap 4.5.4", "criterion 0.5.1", "cumulus-client-cli", "cumulus-client-consensus-common", @@ -4293,6 +4242,7 @@ dependencies = [ "cumulus-client-service", "cumulus-pallet-parachain-system", "cumulus-primitives-core", + "cumulus-primitives-storage-weight-reclaim", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", @@ -4355,19 +4305,6 @@ dependencies = [ "url", ] -[[package]] -name = "curve25519-dalek" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" -dependencies = [ - "byteorder", - "digest 0.8.1", - "rand_core 0.5.1", - "subtle 2.5.0", - "zeroize", -] - [[package]] name = "curve25519-dalek" version = "3.2.0" @@ -4400,13 +4337,13 @@ dependencies = [ [[package]] name = "curve25519-dalek-derive" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -4424,9 +4361,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.106" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28403c86fc49e3401fdf45499ba37fad6493d9329449d6449d7f0e10f4654d28" +checksum = "ff4dc7287237dd438b926a81a1a5605dad33d286870e5eee2db17bf2bcd9e92a" dependencies = [ "cc", "cxxbridge-flags", @@ -4436,9 +4373,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.106" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78da94fef01786dc3e0c76eafcd187abcaa9972c78e05ff4041e24fdf059c285" +checksum = "f47c6c8ad7c1a10d3ef0fe3ff6733f4db0d78f08ef0b13121543163ef327058b" dependencies = [ "cc", "codespan-reporting", @@ -4446,50 +4383,50 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] name = "cxxbridge-flags" -version = "1.0.106" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2a6f5e1dfb4b34292ad4ea1facbfdaa1824705b231610087b00b17008641809" +checksum = "701a1ac7a697e249cdd8dc026d7a7dafbfd0dbcd8bd24ec55889f2bc13dd6287" [[package]] name = "cxxbridge-macro" -version = "1.0.106" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c49547d73ba8dcfd4ad7325d64c6d5391ff4224d498fc39a6f3f49825a530d" +checksum = "b404f596046b0bb2d903a9c786b875a126261b52b7c3a64bbb66382c41c771df" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] name = "dashmap" -version = "5.5.1" +version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd72493923899c6f10c641bdbdeddc7183d6396641d99c1a0d1597f37f92e28" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", "hashbrown 0.14.3", "lock_api", "once_cell", - "parking_lot_core 0.9.8", + "parking_lot_core 0.9.9", ] [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "data-encoding-macro" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" +checksum = "20c01c06f5f429efdf2bae21eb67c28b3df3cf85b7dd2d8ef09c0838dac5d33e" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -4497,9 +4434,9 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" +checksum = "0047d07f2c89b17dd631c80450d69841a6b5d7fb17278cbc43d7e4cfcf2576f3" dependencies = [ "data-encoding", "syn 1.0.109", @@ -4540,9 +4477,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.8" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] [[package]] name = "derivative" @@ -4671,7 +4611,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -4687,7 +4627,7 @@ source = "git+https://github.com/w3f/ring-vrf?rev=e9782f9#e9782f938629c90f3adb3f dependencies = [ "ark-ec", "ark-ff 0.4.2", - "ark-scale 0.0.12", + "ark-scale", "ark-secret-scalar", "ark-serialize 0.4.2", "ark-std 0.4.0", @@ -4698,11 +4638,13 @@ dependencies = [ [[package]] name = "dlmalloc" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "203540e710bfadb90e5e29930baf5d10270cec1f43ab34f46f78b147b2de715a" +checksum = "3264b043b8e977326c1ee9e723da2c1f8d09a99df52cacf00b4dbce5ac54414d" dependencies = [ + "cfg-if", "libc", + "windows-sys 0.52.0", ] [[package]] @@ -4711,13 +4653,39 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "docify" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1b04e6ef3d21119d3eb7b032bca17f99fe041e9c072f30f32cc0e1a2b1f3c4" +dependencies = [ + "docify_macros 0.1.16", +] + [[package]] name = "docify" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cc4fd38aaa9fb98ac70794c82a00360d1e165a87fbf96a8a91f9dfc602aaee2" dependencies = [ - "docify_macros", + "docify_macros 0.2.7", +] + +[[package]] +name = "docify_macros" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b5610df7f2acf89a1bb5d1a66ae56b1c7fcdcfe3948856fb3ace3f644d70eb7" +dependencies = [ + "common-path", + "derive-syn-parse", + "lazy_static", + "proc-macro2", + "quote", + "regex", + "syn 2.0.55", + "termcolor", + "walkdir", ] [[package]] @@ -4732,9 +4700,9 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.50", + "syn 2.0.55", "termcolor", - "toml 0.8.8", + "toml 0.8.12", "walkdir", ] @@ -4785,29 +4753,30 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "ecdsa" -version = "0.16.8" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", "digest 0.10.7", "elliptic-curve", "rfc6979", + "serdect", "signature", "spki", ] [[package]] name = "ed25519" -version = "2.2.2" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ "pkcs8", "signature", @@ -4815,9 +4784,9 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek 4.1.2", "ed25519", @@ -4859,15 +4828,15 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "elliptic-curve" -version = "0.13.5" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", @@ -4878,6 +4847,7 @@ dependencies = [ "pkcs8", "rand_core 0.6.4", "sec1", + "serdect", "subtle 2.5.0", "zeroize", ] @@ -4901,6 +4871,7 @@ dependencies = [ "parachains-common", "parity-scale-codec", "paste", + "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-parachains", "sc-consensus-grandpa", @@ -4934,7 +4905,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "syn 1.0.109", @@ -4942,33 +4913,33 @@ dependencies = [ [[package]] name = "enumflags2" -version = "0.7.7" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c041f5090df68b32bcd905365fd51769c8b9d553fe87fde0b683534f10c01bd2" +checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" dependencies = [ "enumflags2_derive", ] [[package]] name = "enumflags2_derive" -version = "0.7.7" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" +checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] name = "enumn" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ad8cef1d801a4686bfd8919f0b30eac4c8e48968c437a6405ded4fb5272d2b" +checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -4996,9 +4967,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ "humantime", "is-terminal", @@ -5021,9 +4992,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "erased-serde" -version = "0.3.30" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "837c0466252947ada828b975e12daf82e18bb5444e4df87be6038d4469e2a3d2" +checksum = "2b73807008a3c7f171cc40312f37d95ef0396e048b5848d775f54b1a4dd4a0d3" dependencies = [ "serde", ] @@ -5040,23 +5011,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ - "cc", "libc", + "windows-sys 0.52.0", ] [[package]] @@ -5106,6 +5066,17 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "event-listener" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite 0.2.13", +] + [[package]] name = "event-listener" version = "4.0.3" @@ -5114,17 +5085,38 @@ checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" dependencies = [ "concurrent-queue", "parking", - "pin-project-lite 0.2.12", + "pin-project-lite 0.2.13", +] + +[[package]] +name = "event-listener" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite 0.2.13", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.3", + "pin-project-lite 0.2.13", ] [[package]] name = "event-listener-strategy" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" dependencies = [ - "event-listener 4.0.3", - "pin-project-lite 0.2.12", + "event-listener 5.2.0", + "pin-project-lite 0.2.13", ] [[package]] @@ -5150,33 +5142,28 @@ dependencies = [ [[package]] name = "expander" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f86a749cf851891866c10515ef6c299b5c69661465e9c3bbe7e07a2b77fb0f7" +checksum = "00e83c02035136f1592a47964ea60c05a50e4ed8b5892cfac197063850898d4d" dependencies = [ "blake2 0.10.6", "fs-err", + "prettier-please", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] name = "eyre" -version = "0.6.8" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ "indenter", "once_cell", ] -[[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" @@ -5200,9 +5187,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "fastrlp" @@ -5286,14 +5273,14 @@ dependencies = [ "ark-poly", "ark-serialize 0.4.2", "ark-std 0.4.0", - "merlin 3.0.0", + "merlin", ] [[package]] name = "fiat-crypto" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" +checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" [[package]] name = "file-per-thread-logger" @@ -5301,20 +5288,20 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84f2e425d9790201ba4af4630191feac6dcc98765b118d4d18e91d23c2353866" dependencies = [ - "env_logger 0.10.1", + "env_logger 0.10.2", "log", ] [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", - "windows-sys 0.48.0", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", ] [[package]] @@ -5366,9 +5353,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "libz-sys", @@ -5399,9 +5386,9 @@ dependencies = [ [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -5426,7 +5413,7 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" name = "frame" version = "0.0.1-dev" dependencies = [ - "docify", + "docify 0.2.7", "frame-executive", "frame-support", "frame-system", @@ -5455,7 +5442,7 @@ dependencies = [ name = "frame-benchmarking" version = "28.0.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "frame-support", "frame-support-procedural", "frame-system", @@ -5483,9 +5470,9 @@ name = "frame-benchmarking-cli" version = "32.0.0" dependencies = [ "Inflector", - "array-bytes 6.1.0", + "array-bytes 6.2.2", "chrono", - "clap 4.5.1", + "clap 4.5.4", "comfy-table", "frame-benchmarking", "frame-support", @@ -5546,12 +5533,12 @@ dependencies = [ "frame-election-provider-support", "frame-support", "parity-scale-codec", - "proc-macro-crate 3.0.0", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", "scale-info", "sp-arithmetic", - "syn 2.0.50", + "syn 2.0.55", "trybuild", ] @@ -5577,7 +5564,7 @@ dependencies = [ name = "frame-election-solution-type-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.5.1", + "clap 4.5.4", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-support", @@ -5594,7 +5581,8 @@ dependencies = [ name = "frame-executive" version = "28.0.0" dependencies = [ - "array-bytes 6.1.0", + "aquamarine 0.3.3", + "array-bytes 6.2.2", "frame-support", "frame-system", "frame-try-runtime", @@ -5650,11 +5638,11 @@ dependencies = [ name = "frame-support" version = "28.0.0" dependencies = [ - "aquamarine", - "array-bytes 6.1.0", + "aquamarine 0.5.0", + "array-bytes 6.2.2", "assert_matches", "bitflags 1.3.2", - "docify", + "docify 0.2.7", "environmental", "frame-metadata", "frame-support-procedural", @@ -5684,6 +5672,7 @@ dependencies = [ "sp-staking", "sp-state-machine", "sp-std 14.0.0", + "sp-timestamp", "sp-tracing 16.0.0", "sp-weights", "static_assertions", @@ -5697,7 +5686,7 @@ dependencies = [ "Inflector", "cfg-expr", "derive-syn-parse", - "expander 2.0.0", + "expander 2.1.0", "frame-support-procedural-tools", "itertools 0.10.5", "macro_magic", @@ -5706,7 +5695,7 @@ dependencies = [ "quote", "regex", "sp-crypto-hashing", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -5714,10 +5703,10 @@ name = "frame-support-procedural-tools" version = "10.0.0" dependencies = [ "frame-support-procedural-tools-derive", - "proc-macro-crate 3.0.0", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -5726,7 +5715,7 @@ version = "11.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -5797,7 +5786,7 @@ version = "28.0.0" dependencies = [ "cfg-if", "criterion 0.4.0", - "docify", + "docify 0.2.7", "frame-support", "log", "parity-scale-codec", @@ -5851,9 +5840,12 @@ dependencies = [ [[package]] name = "fs-err" -version = "2.9.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" +checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41" +dependencies = [ + "autocfg", +] [[package]] name = "fs2" @@ -5871,7 +5863,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29f9df8a11882c4e3335eb2d18a0137c505d9ca927470b0cac9c6f0ae07d28f7" dependencies = [ - "rustix 0.38.21", + "rustix 0.38.32", "windows-sys 0.48.0", ] @@ -5947,10 +5939,23 @@ dependencies = [ "futures-io", "memchr", "parking", - "pin-project-lite 0.2.12", + "pin-project-lite 0.2.13", "waker-fn", ] +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand 2.0.2", + "futures-core", + "futures-io", + "parking", + "pin-project-lite 0.2.13", +] + [[package]] name = "futures-macro" version = "0.3.30" @@ -5959,7 +5964,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -5969,7 +5974,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd" dependencies = [ "futures-io", - "rustls 0.20.8", + "rustls 0.20.9", "webpki", ] @@ -5987,9 +5992,9 @@ checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" @@ -6004,7 +6009,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.12", + "pin-project-lite 0.2.13", "pin-utils", "slab", ] @@ -6074,9 +6079,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", @@ -6095,22 +6100,12 @@ dependencies = [ [[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" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" dependencies = [ - "opaque-debug 0.3.0", - "polyval 0.6.1", + "opaque-debug 0.3.1", + "polyval", ] [[package]] @@ -6126,9 +6121,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" dependencies = [ "fallible-iterator 0.3.0", "stable_deref_trait", @@ -6188,9 +6183,9 @@ dependencies = [ [[package]] name = "governor" -version = "0.6.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "821239e5672ff23e2a7060901fa622950bbd80b649cdaadd78d1c1767ed14eb4" +checksum = "68a7f542ee6b35af73b06abc0dad1c1bae89964e4e253bc4b587b91c9637867b" dependencies = [ "cfg-if", "dashmap", @@ -6199,9 +6194,11 @@ dependencies = [ "no-std-compat", "nonzero_ext", "parking_lot 0.12.1", + "portable-atomic", "quanta", "rand", "smallvec", + "spinning_top", ] [[package]] @@ -6217,9 +6214,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb" dependencies = [ "bytes", "fnv", @@ -6227,7 +6224,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.2.3", + "indexmap 2.2.6", "slab", "tokio", "tokio-util", @@ -6236,15 +6233,19 @@ dependencies = [ [[package]] name = "half" -version = "1.8.2" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +dependencies = [ + "cfg-if", + "crunchy", +] [[package]] name = "handlebars" -version = "4.3.7" +version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c3372087601b532857d332f5957cbae686da52bb7810bf038c3e3c3cc2fa0d" +checksum = "d08485b96a0e6393e9e4d1b8d48cf74ad6c063cd905eb33f42c1ce3f0377539b" dependencies = [ "log", "pest", @@ -6284,7 +6285,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.8", + "ahash 0.8.11", ] [[package]] @@ -6293,7 +6294,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ - "ahash 0.8.8", + "ahash 0.8.11", "allocator-api2", "serde", ] @@ -6313,6 +6314,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -6324,9 +6331,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -6334,6 +6341,12 @@ 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" + [[package]] name = "hex-literal" version = "0.4.1" @@ -6342,9 +6355,9 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "hkdf" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ "hmac 0.12.1", ] @@ -6359,16 +6372,6 @@ dependencies = [ "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.0", - "digest 0.9.0", -] - [[package]] name = "hmac" version = "0.12.1" @@ -6389,6 +6392,15 @@ dependencies = [ "hmac 0.8.1", ] +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "honggfuzz" version = "0.5.55" @@ -6414,9 +6426,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -6425,13 +6437,13 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", - "pin-project-lite 0.2.12", + "pin-project-lite 0.2.13", ] [[package]] @@ -6460,9 +6472,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -6474,8 +6486,8 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project-lite 0.2.12", - "socket2 0.4.9", + "pin-project-lite 0.2.13", + "socket2 0.5.6", "tokio", "tower-service", "tracing", @@ -6484,9 +6496,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http", @@ -6500,16 +6512,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows 0.48.0", + "windows-core 0.52.0", ] [[package]] @@ -6534,9 +6546,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -6544,21 +6556,21 @@ dependencies = [ [[package]] name = "if-addrs" -version = "0.7.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9" +checksum = "cabb0019d51a643781ff15c9c8a3e5dedc365c47211270f4e8f82812fedd8f0a" dependencies = [ "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "if-watch" -version = "3.0.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9465340214b296cd17a0009acdb890d6160010b8adf8f78a00d0d7ab270f79f" +checksum = "d6b0422c86d7ce0e97169cc42e04ae643caf278874a7a3c87b8150a220dc7e1e" dependencies = [ - "async-io", + "async-io 2.3.2", "core-foundation", "fnv", "futures", @@ -6568,7 +6580,7 @@ dependencies = [ "rtnetlink", "system-configuration", "tokio", - "windows 0.34.0", + "windows", ] [[package]] @@ -6658,9 +6670,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.3" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -6674,9 +6686,9 @@ checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" [[package]] name = "indicatif" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" dependencies = [ "console", "instant", @@ -6724,7 +6736,7 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi 0.3.9", "libc", "windows-sys 0.48.0", ] @@ -6741,7 +6753,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2 0.5.5", + "socket2 0.5.6", "widestring", "windows-sys 0.48.0", "winreg", @@ -6749,19 +6761,19 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.9" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ - "hermit-abi 0.3.2", - "rustix 0.38.21", - "windows-sys 0.48.0", + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.52.0", ] [[package]] @@ -6791,26 +6803,35 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -6823,9 +6844,9 @@ checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" [[package]] name = "jsonrpsee" -version = "0.22.0" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a95f7cc23d5fab0cdeeaf6bad8c8f5e7a3aa7f0d211957ea78232b327ab27b0" +checksum = "3cdbb7cb6f3ba28f5b212dd250ab4483105efc3e381f5c8bb90340f14f0a2cc3" dependencies = [ "jsonrpsee-core", "jsonrpsee-http-client", @@ -6839,9 +6860,9 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" -version = "0.22.0" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1736cfa3845fd9f8f43751f2b8e0e83f7b6081e754502f7d63b6587692cc83" +checksum = "9ab2e14e727d2faf388c99d9ca5210566ed3b044f07d92c29c3611718d178380" dependencies = [ "futures-util", "http", @@ -6860,9 +6881,9 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.22.0" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82030d038658974732103e623ba2e0abec03bbbe175b39c0a2fafbada60c5868" +checksum = "71962a1c49af43adf81d337e4ebc93f3c915faf6eccaa14d74e255107dfd7723" dependencies = [ "anyhow", "async-lock 3.3.0", @@ -6886,9 +6907,9 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.22.0" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a06ef0de060005fddf772d54597bb6a8b0413da47dcffd304b0306147b9678" +checksum = "8c13987da51270bda2c1c9b40c19be0fe9b225c7a0553963d8f17e683a50ce84" dependencies = [ "async-trait", "hyper", @@ -6906,22 +6927,22 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.22.0" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fc56131589f82e57805f7338b87023db4aafef813555708b159787e34ad6bc" +checksum = "1d7c2416c400c94b2e864603c51a5bbd5b103386da1f5e58cbf01e7bb3ef0833" dependencies = [ - "heck", - "proc-macro-crate 3.0.0", + "heck 0.4.1", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.55", ] [[package]] name = "jsonrpsee-server" -version = "0.22.0" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d85be77fe5b2a94589e3164fb780017f7aff7d646b49278c0d0346af16975c8e" +checksum = "4882e640e70c2553e3d9487e6f4dddd5fd11918f25e40fa45218f9fe29ed2152" dependencies = [ "futures-util", "http", @@ -6943,9 +6964,9 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.22.0" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a48fdc1202eafc51c63e00406575e59493284ace8b8b61aa16f3a6db5d64f1a" +checksum = "1e53c72de6cd2ad6ac1aa6e848206ef8b736f92ed02354959130373dfa5b3cbd" dependencies = [ "anyhow", "beef", @@ -6956,9 +6977,9 @@ dependencies = [ [[package]] name = "jsonrpsee-ws-client" -version = "0.22.0" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5ce25d70a8e4d3cc574bbc3cad0137c326ad64b194793d5e7bbdd3fa4504181" +checksum = "c8a07ab8da9a283b906f6735ddd17d3680158bb72259e853441d1dd0167079ec" dependencies = [ "http", "jsonrpsee-client-transport", @@ -6969,22 +6990,23 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", "once_cell", + "serdect", "sha2 0.10.8", ] [[package]] name = "keccak" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ "cpufeatures", ] @@ -7056,6 +7078,7 @@ dependencies = [ "pallet-lottery", "pallet-membership", "pallet-message-queue", + "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", @@ -7172,9 +7195,9 @@ dependencies = [ [[package]] name = "landlock" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1530c5b973eeed4ac216af7e24baf5737645a6272e361f1fb95710678b67d9cc" +checksum = "9baa9eeb6e315942429397e617a190f4fdc696ef1ee0342939d641029cbb4ea7" dependencies = [ "enumflags2", "libc", @@ -7201,9 +7224,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libflate" @@ -7238,12 +7261,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.7.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "winapi", + "windows-targets 0.52.4", ] [[package]] @@ -7261,7 +7284,7 @@ dependencies = [ "bytes", "futures", "futures-timer", - "getrandom 0.2.10", + "getrandom 0.2.12", "instant", "libp2p-allow-block-list", "libp2p-connection-limits", @@ -7434,7 +7457,7 @@ dependencies = [ "log", "rand", "smallvec", - "socket2 0.4.9", + "socket2 0.4.10", "tokio", "trust-dns-proto", "void", @@ -7511,7 +7534,7 @@ dependencies = [ "parking_lot 0.12.1", "quinn-proto", "rand", - "rustls 0.20.8", + "rustls 0.20.9", "thiserror", "tokio", ] @@ -7559,7 +7582,7 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fba456131824ab6acd4c7bf61e9c0f0a3014b5fc9868ccb8e10d344594cdc4f" dependencies = [ - "heck", + "heck 0.4.1", "quote", "syn 1.0.109", ] @@ -7576,7 +7599,7 @@ dependencies = [ "libc", "libp2p-core", "log", - "socket2 0.4.9", + "socket2 0.4.10", "tokio", ] @@ -7592,7 +7615,7 @@ dependencies = [ "libp2p-identity", "rcgen", "ring 0.16.20", - "rustls 0.20.8", + "rustls 0.20.9", "thiserror", "webpki", "x509-parser", @@ -7645,6 +7668,17 @@ dependencies = [ "yamux", ] +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.5.0", + "libc", + "redox_syscall 0.4.1", +] + [[package]] name = "librocksdb-sys" version = "0.11.0+8.1.1" @@ -7710,9 +7744,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.12" +version = "1.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" +checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" dependencies = [ "cc", "pkg-config", @@ -7745,9 +7779,9 @@ dependencies = [ [[package]] name = "linregress" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de0b5f52a9f84544d268f5fabb71b38962d6aa3c6600b8bcd27d44ccf9c9c45" +checksum = "4de04dcecc58d366391f9920245b85ffa684558a5ef6e7736e754347c3aea9c2" dependencies = [ "nalgebra", ] @@ -7766,9 +7800,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lioness" @@ -7802,9 +7836,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -7812,9 +7846,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" dependencies = [ "serde", "value-bag", @@ -7840,9 +7874,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eedb2bdbad7e0634f83989bf596f497b070130daaa398ab22d84c39e266deec5" +checksum = "a4a83fb7698b3643a0e34f9ae6f2e8f0178c0fd42f8b59d493aa271ff3a5bf21" [[package]] name = "lru-cache" @@ -7882,15 +7916,6 @@ dependencies = [ "libc", ] -[[package]] -name = "mach2" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" -dependencies = [ - "libc", -] - [[package]] name = "macro_magic" version = "0.5.0" @@ -7900,7 +7925,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -7914,7 +7939,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -7925,7 +7950,7 @@ checksum = "9ea73aa640dc01d62a590d48c0c3521ed739d53b27f919b25c3551e233481654" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -7936,7 +7961,7 @@ checksum = "ef9d79ae96aaba821963320eb2b6e34d17df1e5a83d8a1985c29cc5be59577b3" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -7977,9 +8002,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "matrixmultiply" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77" +checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" dependencies = [ "autocfg", "rawpointer", @@ -7987,17 +8012,17 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memfd" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" +checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" dependencies = [ - "rustix 0.37.23", + "rustix 0.38.32", ] [[package]] @@ -8011,9 +8036,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45fd3a57831bf88bc63f8cebc0cf956116276e97fef3966103e96416209f7c92" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" dependencies = [ "libc", ] @@ -8036,15 +8061,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - [[package]] name = "memory-db" version = "0.32.0" @@ -8054,18 +8070,6 @@ dependencies = [ "hash-db", ] -[[package]] -name = "merlin" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" -dependencies = [ - "byteorder", - "keccak", - "rand_core 0.5.1", - "zeroize", -] - [[package]] name = "merlin" version = "3.0.0" @@ -8101,20 +8105,72 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "minimal-template-node" +version = "0.0.0" +dependencies = [ + "clap 4.5.4", + "frame", + "futures", + "futures-timer", + "jsonrpsee", + "minimal-template-runtime", + "sc-basic-authorship", + "sc-cli", + "sc-client-api", + "sc-consensus", + "sc-consensus-manual-seal", + "sc-executor", + "sc-network", + "sc-offchain", + "sc-rpc-api", + "sc-service", + "sc-telemetry", + "sc-transaction-pool", + "sc-transaction-pool-api", + "serde_json", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-io", + "sp-keyring", + "sp-runtime", + "sp-timestamp", + "substrate-build-script-utils", + "substrate-frame-rpc-system", +] + +[[package]] +name = "minimal-template-runtime" +version = "0.0.0" +dependencies = [ + "frame", + "pallet-balances", + "pallet-minimal-template", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "parity-scale-codec", + "scale-info", + "sp-genesis-builder", + "substrate-wasm-builder", +] + [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -8362,9 +8418,9 @@ dependencies = [ [[package]] name = "nalgebra" -version = "0.32.3" +version = "0.32.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa" +checksum = "4541eb06dce09c0241ebbaab7102f0a01a0c8994afed2e5d0d66775016e25ac2" dependencies = [ "approx", "matrixmultiply", @@ -8482,16 +8538,15 @@ dependencies = [ [[package]] name = "nix" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", "memoffset 0.7.1", "pin-utils", - "static_assertions", ] [[package]] @@ -8500,7 +8555,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.5.0", "cfg-if", "libc", ] @@ -8521,8 +8576,8 @@ checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" name = "node-bench" version = "0.9.0-dev" dependencies = [ - "array-bytes 6.1.0", - "clap 4.5.1", + "array-bytes 6.2.2", + "clap 4.5.4", "derive_more", "fs_extra", "futures", @@ -8599,7 +8654,7 @@ dependencies = [ name = "node-runtime-generate-bags" version = "3.0.0" dependencies = [ - "clap 4.5.1", + "clap 4.5.4", "generate-bags", "kitchensink-runtime", ] @@ -8608,7 +8663,7 @@ dependencies = [ name = "node-template-release" version = "3.0.0" dependencies = [ - "clap 4.5.1", + "clap 4.5.4", "flate2", "fs_extra", "glob", @@ -8727,13 +8782,19 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-format" version = "0.4.4" @@ -8746,19 +8807,18 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" dependencies = [ "autocfg", "num-integer", @@ -8779,9 +8839,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", "libm", @@ -8793,7 +8853,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi 0.3.9", "libc", ] @@ -8853,9 +8913,9 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl-probe" @@ -8892,8 +8952,8 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eedb646674596266dc9bb2b5c7eea7c36b32ecc7777eba0d510196972d72c4fd" dependencies = [ - "expander 2.0.0", - "indexmap 2.2.3", + "expander 2.1.0", + "indexmap 2.2.6", "itertools 0.11.0", "petgraph", "proc-macro-crate 1.3.1", @@ -8913,9 +8973,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.5.1" +version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" [[package]] name = "overload" @@ -8933,7 +8993,7 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" name = "pallet-alliance" version = "27.0.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "frame-benchmarking", "frame-support", "frame-system", @@ -9141,8 +9201,8 @@ dependencies = [ name = "pallet-bags-list" version = "27.0.0" dependencies = [ - "aquamarine", - "docify", + "aquamarine 0.5.0", + "docify 0.2.7", "frame-benchmarking", "frame-election-provider-support", "frame-support", @@ -9190,7 +9250,7 @@ dependencies = [ name = "pallet-balances" version = "28.0.0" dependencies = [ - "docify", + "docify 0.2.7", "frame-benchmarking", "frame-support", "frame-system", @@ -9237,7 +9297,7 @@ dependencies = [ name = "pallet-beefy-mmr" version = "28.0.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "binary-merkle-tree", "frame-support", "frame-system", @@ -9458,7 +9518,7 @@ dependencies = [ name = "pallet-contracts" version = "27.0.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "assert_matches", "bitflags 1.3.2", "env_logger 0.9.3", @@ -9506,12 +9566,11 @@ dependencies = [ "anyhow", "frame-system", "parity-wasm", - "polkavm-linker 0.5.0", + "polkavm-linker", "sp-runtime", "tempfile", - "toml 0.8.8", + "toml 0.8.12", "twox-hash", - "wat", ] [[package]] @@ -9558,7 +9617,7 @@ version = "18.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -9568,7 +9627,7 @@ dependencies = [ "bitflags 1.3.2", "parity-scale-codec", "paste", - "polkavm-derive 0.5.0", + "polkavm-derive", "scale-info", ] @@ -9794,15 +9853,35 @@ version = "28.0.0" dependencies = [ "frame-support", "frame-system", - "lite-json", + "lite-json", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-keystore", + "sp-runtime", + "sp-std 14.0.0", +] + +[[package]] +name = "pallet-example-single-block-migrations" +version = "0.0.1" +dependencies = [ + "docify 0.2.7", + "frame-executive", + "frame-support", + "frame-system", + "frame-try-runtime", "log", + "pallet-balances", "parity-scale-codec", "scale-info", "sp-core", "sp-io", - "sp-keystore", "sp-runtime", "sp-std 14.0.0", + "sp-version", ] [[package]] @@ -9846,6 +9925,7 @@ dependencies = [ "pallet-example-frame-crate", "pallet-example-kitchensink", "pallet-example-offchain-worker", + "pallet-example-single-block-migrations", "pallet-example-split", "pallet-example-tasks", ] @@ -9854,7 +9934,7 @@ dependencies = [ name = "pallet-fast-unstake" version = "27.0.0" dependencies = [ - "docify", + "docify 0.2.7", "frame-benchmarking", "frame-election-provider-support", "frame-support", @@ -10051,6 +10131,39 @@ dependencies = [ "sp-weights", ] +[[package]] +name = "pallet-migrations" +version = "1.0.0" +dependencies = [ + "docify 0.1.16", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "pretty_assertions", + "scale-info", + "sp-api", + "sp-block-builder", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 14.0.0", + "sp-tracing 16.0.0", + "sp-version", +] + +[[package]] +name = "pallet-minimal-template" +version = "0.0.0" +dependencies = [ + "frame", + "parity-scale-codec", + "scale-info", +] + [[package]] name = "pallet-mixnet" version = "0.4.0" @@ -10074,7 +10187,7 @@ dependencies = [ name = "pallet-mmr" version = "27.0.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "env_logger 0.9.3", "frame-benchmarking", "frame-support", @@ -10326,7 +10439,7 @@ dependencies = [ name = "pallet-paged-list" version = "0.6.0" dependencies = [ - "docify", + "docify 0.2.7", "frame-benchmarking", "frame-support", "frame-system", @@ -10352,14 +10465,13 @@ dependencies = [ [[package]] name = "pallet-parachain-template" -version = "0.7.0" +version = "0.0.0" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "serde", "sp-core", "sp-io", "sp-runtime", @@ -10369,7 +10481,7 @@ dependencies = [ name = "pallet-parameters" version = "0.0.1" dependencies = [ - "docify", + "docify 0.2.7", "frame-benchmarking", "frame-support", "frame-system", @@ -10530,7 +10642,7 @@ dependencies = [ name = "pallet-safe-mode" version = "9.0.0" dependencies = [ - "docify", + "docify 0.2.7", "frame-benchmarking", "frame-support", "frame-system", @@ -10568,7 +10680,7 @@ dependencies = [ name = "pallet-sassafras" version = "0.3.5-dev" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "frame-benchmarking", "frame-support", "frame-system", @@ -10587,7 +10699,7 @@ dependencies = [ name = "pallet-scheduler" version = "29.0.0" dependencies = [ - "docify", + "docify 0.2.7", "frame-benchmarking", "frame-support", "frame-system", @@ -10729,11 +10841,11 @@ dependencies = [ name = "pallet-staking-reward-curve" version = "11.0.0" dependencies = [ - "proc-macro-crate 3.0.0", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", "sp-runtime", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -10800,7 +10912,7 @@ dependencies = [ name = "pallet-sudo" version = "28.0.0" dependencies = [ - "docify", + "docify 0.2.7", "frame-benchmarking", "frame-support", "frame-system", @@ -10812,11 +10924,25 @@ dependencies = [ "sp-std 14.0.0", ] +[[package]] +name = "pallet-template" +version = "0.0.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", +] + [[package]] name = "pallet-timestamp" version = "27.0.0" dependencies = [ - "docify", + "docify 0.2.7", "frame-benchmarking", "frame-support", "frame-system", @@ -10899,7 +11025,7 @@ dependencies = [ name = "pallet-transaction-storage" version = "27.0.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "frame-benchmarking", "frame-support", "frame-system", @@ -10920,7 +11046,7 @@ dependencies = [ name = "pallet-treasury" version = "27.0.0" dependencies = [ - "docify", + "docify 0.2.7", "frame-benchmarking", "frame-support", "frame-system", @@ -10940,7 +11066,7 @@ dependencies = [ name = "pallet-tx-pause" version = "9.0.0" dependencies = [ - "docify", + "docify 0.2.7", "frame-benchmarking", "frame-support", "frame-system", @@ -11049,6 +11175,7 @@ dependencies = [ "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -11122,9 +11249,9 @@ dependencies = [ [[package]] name = "parachain-template-node" -version = "0.1.0" +version = "0.0.0" dependencies = [ - "clap 4.5.1", + "clap 4.5.4", "color-print", "cumulus-client-cli", "cumulus-client-collator", @@ -11180,7 +11307,7 @@ dependencies = [ [[package]] name = "parachain-template-runtime" -version = "0.7.0" +version = "0.0.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", @@ -11188,6 +11315,7 @@ dependencies = [ "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-primitives-core", + "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-utility", "frame-benchmarking", "frame-executive", @@ -11280,6 +11408,7 @@ dependencies = [ "pallet-balances", "pallet-collator-selection", "pallet-session", + "pallet-timestamp", "pallet-xcm", "parity-scale-codec", "polkadot-parachain-primitives", @@ -11295,6 +11424,19 @@ dependencies = [ "substrate-wasm-builder", ] +[[package]] +name = "parity-bip39" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e69bf016dc406eff7d53a7d3f7cf1c2e72c82b9088aac1118591e36dd2cd3e9" +dependencies = [ + "bitcoin_hashes 0.13.0", + "rand", + "rand_core 0.6.4", + "serde", + "unicode-normalization", +] + [[package]] name = "parity-bytes" version = "0.1.2" @@ -11303,9 +11445,9 @@ checksum = "16b56e3a2420138bdb970f84dfb9c774aea80fa0e7371549eedec0d80c209c67" [[package]] name = "parity-db" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e9ab494af9e6e813c72170f0d3c1de1500990d62c97cc05cc7576f91aa402f" +checksum = "592a28a24b09c9dc20ac8afaa6839abc417c720afe42c12e1e4a9d6aa2508d2e" dependencies = [ "blake2 0.10.6", "crc32fast", @@ -11319,13 +11461,14 @@ dependencies = [ "rand", "siphasher", "snap", + "winapi", ] [[package]] name = "parity-scale-codec" -version = "3.6.5" +version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" +checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" dependencies = [ "arrayvec 0.7.4", "bitvec", @@ -11338,11 +11481,11 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.5" +version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" +checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 2.0.0", "proc-macro2", "quote", "syn 1.0.109", @@ -11391,9 +11534,9 @@ checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "parking" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" @@ -11413,7 +11556,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.8", + "parking_lot_core 0.9.9", ] [[package]] @@ -11432,13 +11575,13 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "smallvec", "windows-targets 0.48.5", ] @@ -11450,19 +11593,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7924d1d0ad836f665c9065e26d016c673ece3993f30d340068b16f282afc1156" [[package]] -name = "paste" -version = "1.0.14" +name = "password-hash" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle 2.5.0", +] [[package]] -name = "pbkdf2" -version = "0.8.0" +name = "paste" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" -dependencies = [ - "crypto-mac 0.11.0", -] +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pbkdf2" @@ -11471,6 +11616,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ "digest 0.10.7", + "password-hash", ] [[package]] @@ -11497,9 +11643,8 @@ dependencies = [ "frame-support", "parachains-common", "penpal-runtime", - "rococo-emulated-chain", "sp-core", - "westend-emulated-chain", + "staging-xcm", ] [[package]] @@ -11561,7 +11706,6 @@ dependencies = [ "staging-xcm-builder", "staging-xcm-executor", "substrate-wasm-builder", - "testnet-parachains-constants", ] [[package]] @@ -11764,25 +11908,26 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.2" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" +checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" dependencies = [ + "memchr", "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.7.2" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853" +checksum = "b0d24f72393fd16ab6ac5738bc33cdb6a9aa73f8b902e8fe29cf4e67d7dd1026" dependencies = [ "pest", "pest_generator", @@ -11790,22 +11935,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.2" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" +checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] name = "pest_meta" -version = "2.7.2" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48" +checksum = "934cd7631c050f4674352a6e835d5f6711ffbfb9345c2fc0107155ac495ae293" dependencies = [ "once_cell", "pest", @@ -11819,27 +11964,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.2.3", + "indexmap 2.2.6", ] [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -11850,9 +11995,9 @@ checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" [[package]] name = "pin-project-lite" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -11860,6 +12005,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand 2.0.2", + "futures-io", +] + [[package]] name = "pkcs8" version = "0.10.2" @@ -11872,15 +12028,15 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "platforms" -version = "3.0.2" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" [[package]] name = "plotters" @@ -11916,7 +12072,7 @@ version = "6.0.0" dependencies = [ "assert_cmd", "color-eyre", - "nix 0.26.2", + "nix 0.26.4", "polkadot-cli", "polkadot-core-primitives", "polkadot-node-core-pvf", @@ -12005,6 +12161,7 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "polkadot-primitives-test-helpers", + "polkadot-subsystem-bench", "rand", "sc-network", "schnellru", @@ -12036,6 +12193,7 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "polkadot-primitives-test-helpers", + "polkadot-subsystem-bench", "rand", "sc-network", "schnellru", @@ -12052,7 +12210,7 @@ name = "polkadot-cli" version = "7.0.0" dependencies = [ "cfg-if", - "clap 4.5.1", + "clap 4.5.4", "frame-benchmarking-cli", "futures", "log", @@ -12123,13 +12281,13 @@ name = "polkadot-dispute-distribution" version = "7.0.0" dependencies = [ "assert_matches", - "async-channel", + "async-channel 1.9.0", "async-trait", "derive_more", "fatality", "futures", "futures-timer", - "indexmap 2.2.3", + "indexmap 2.2.6", "lazy_static", "parity-scale-codec", "polkadot-erasure-coding", @@ -12239,6 +12397,7 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "polkadot-primitives-test-helpers", + "rstest", "sp-core", "sp-keyring", "sp-maybe-compressed-blob", @@ -12261,7 +12420,7 @@ dependencies = [ "kvdb", "kvdb-memorydb", "log", - "merlin 3.0.0", + "merlin", "parity-scale-codec", "parking_lot 0.12.1", "polkadot-node-jaeger", @@ -12336,7 +12495,9 @@ dependencies = [ "polkadot-primitives", "polkadot-primitives-test-helpers", "polkadot-statement-table", + "rstest", "sc-keystore", + "schnellru", "sp-application-crypto", "sp-core", "sp-keyring", @@ -12488,6 +12649,7 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "polkadot-primitives-test-helpers", + "rstest", "sc-keystore", "sp-application-crypto", "sp-core", @@ -12511,6 +12673,8 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "polkadot-primitives-test-helpers", + "rstest", + "schnellru", "sp-application-crypto", "sp-keystore", "thiserror", @@ -12522,7 +12686,7 @@ name = "polkadot-node-core-pvf" version = "7.0.0" dependencies = [ "always-assert", - "array-bytes 6.1.0", + "array-bytes 6.2.2", "assert_matches", "blake3", "cfg-if", @@ -12693,7 +12857,7 @@ name = "polkadot-node-metrics" version = "7.0.0" dependencies = [ "assert_cmd", - "bs58 0.5.0", + "bs58 0.5.1", "futures", "futures-timer", "hyper", @@ -12718,7 +12882,7 @@ dependencies = [ name = "polkadot-node-network-protocol" version = "7.0.0" dependencies = [ - "async-channel", + "async-channel 1.9.0", "async-trait", "bitvec", "derive_more", @@ -12897,7 +13061,7 @@ dependencies = [ "async-trait", "bridge-hub-rococo-runtime", "bridge-hub-westend-runtime", - "clap 4.5.1", + "clap 4.5.4", "collectives-westend-runtime", "color-print", "contracts-rococo-runtime", @@ -12924,7 +13088,7 @@ dependencies = [ "hex-literal", "jsonrpsee", "log", - "nix 0.26.2", + "nix 0.26.4", "pallet-transaction-payment", "pallet-transaction-payment-rpc", "pallet-transaction-payment-rpc-runtime-api", @@ -13006,6 +13170,7 @@ version = "7.0.0" dependencies = [ "bitvec", "hex-literal", + "log", "parity-scale-codec", "polkadot-core-primitives", "polkadot-parachain-primitives", @@ -13098,7 +13263,6 @@ dependencies = [ "pallet-transaction-payment", "pallet-treasury", "pallet-vesting", - "pallet-xcm-benchmarks", "parity-scale-codec", "polkadot-primitives", "polkadot-primitives-test-helpers", @@ -13130,7 +13294,7 @@ dependencies = [ name = "polkadot-runtime-metrics" version = "7.0.0" dependencies = [ - "bs58 0.5.0", + "bs58 0.5.1", "frame-benchmarking", "parity-scale-codec", "polkadot-primitives", @@ -13172,6 +13336,7 @@ dependencies = [ "polkadot-runtime-metrics", "rand", "rand_chacha 0.3.1", + "rstest", "rustc-hex", "sc-keystore", "scale-info", @@ -13203,13 +13368,31 @@ version = "0.0.1" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", - "docify", + "docify 0.2.7", "frame", + "frame-executive", + "frame-support", + "frame-system", "kitchensink-runtime", + "pallet-assets", "pallet-aura", + "pallet-authorship", + "pallet-babe", + "pallet-balances", + "pallet-broker", + "pallet-collective", "pallet-default-config-example", + "pallet-democracy", + "pallet-example-offchain-worker", + "pallet-example-single-block-migrations", "pallet-examples", + "pallet-multisig", + "pallet-proxy", + "pallet-referenda", + "pallet-scheduler", "pallet-timestamp", + "pallet-transaction-payment", + "pallet-utility", "parity-scale-codec", "sc-cli", "sc-client-db", @@ -13225,10 +13408,13 @@ dependencies = [ "scale-info", "simple-mermaid", "sp-api", + "sp-arithmetic", "sp-core", "sp-io", "sp-keyring", + "sp-offchain", "sp-runtime", + "sp-version", "staging-chain-spec-builder", "staging-node-cli", "staging-parachain-info", @@ -13243,6 +13429,7 @@ version = "7.0.0" dependencies = [ "assert_matches", "async-trait", + "bitvec", "env_logger 0.9.3", "frame-benchmarking", "frame-benchmarking-cli", @@ -13354,12 +13541,14 @@ dependencies = [ "sp-transaction-pool", "sp-version", "sp-weights", + "staging-xcm", "substrate-prometheus-endpoint", "tempfile", "thiserror", "tracing-gum", "westend-runtime", "westend-runtime-constants", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -13368,12 +13557,12 @@ version = "7.0.0" dependencies = [ "arrayvec 0.7.4", "assert_matches", - "async-channel", + "async-channel 1.9.0", "bitvec", "fatality", "futures", "futures-timer", - "indexmap 2.2.3", + "indexmap 2.2.6", "parity-scale-codec", "polkadot-node-network-protocol", "polkadot-node-primitives", @@ -13403,6 +13592,7 @@ dependencies = [ "parity-scale-codec", "polkadot-primitives", "sp-core", + "tracing-gum", ] [[package]] @@ -13413,7 +13603,7 @@ dependencies = [ "async-trait", "bincode", "bitvec", - "clap 4.5.1", + "clap 4.5.4", "clap-num", "color-eyre", "colored", @@ -13455,8 +13645,9 @@ dependencies = [ "sc-keystore", "sc-network", "sc-service", - "schnorrkel 0.9.1", + "schnorrkel 0.11.4", "serde", + "serde_json", "serde_yaml", "sha1", "sp-application-crypto", @@ -13507,7 +13698,7 @@ version = "1.0.0" dependencies = [ "assert_matches", "async-trait", - "clap 4.5.1", + "clap 4.5.4", "color-eyre", "futures", "futures-timer", @@ -13654,106 +13845,94 @@ dependencies = [ name = "polkadot-voter-bags" version = "7.0.0" dependencies = [ - "clap 4.5.1", + "clap 4.5.4", "generate-bags", "sp-io", "westend-runtime", ] [[package]] -name = "polkavm-common" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b4e215c80fe876147f3d58158d5dfeae7dabdd6047e175af77095b78d0035c" - -[[package]] -name = "polkavm-common" -version = "0.8.0" +name = "polkavm" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92c99f7eee94e7be43ba37eef65ad0ee8cbaf89b7c00001c3f6d2be985cb1817" +checksum = "8a3693e5efdb2bf74e449cd25fd777a28bd7ed87e41f5d5da75eb31b4de48b94" +dependencies = [ + "libc", + "log", + "polkavm-assembler", + "polkavm-common", + "polkavm-linux-raw", +] [[package]] -name = "polkavm-derive" -version = "0.5.0" +name = "polkavm-assembler" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6380dbe1fb03ecc74ad55d841cfc75480222d153ba69ddcb00977866cbdabdb8" +checksum = "1fa96d6d868243acc12de813dd48e756cbadcc8e13964c70d272753266deadc1" dependencies = [ - "polkavm-derive-impl 0.5.0", - "syn 2.0.50", + "log", ] [[package]] -name = "polkavm-derive" -version = "0.8.0" +name = "polkavm-common" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79fa916f7962348bd1bb1a65a83401675e6fc86c51a0fdbcf92a3108e58e6125" +checksum = "1d9428a5cfcc85c5d7b9fc4b6a18c4b802d0173d768182a51cc7751640f08b92" dependencies = [ - "polkavm-derive-impl-macro", + "log", ] [[package]] -name = "polkavm-derive-impl" -version = "0.5.0" +name = "polkavm-derive" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc8211b3365bbafb2fb32057d68b0e1ca55d079f5cf6f9da9b98079b94b3987d" +checksum = "ae8c4bea6f3e11cd89bb18bcdddac10bd9a24015399bd1c485ad68a985a19606" dependencies = [ - "polkavm-common 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.50", + "polkavm-derive-impl-macro", ] [[package]] name = "polkavm-derive-impl" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c10b2654a8a10a83c260bfb93e97b262cf0017494ab94a65d389e0eda6de6c9c" +checksum = "5c4fdfc49717fb9a196e74a5d28e0bc764eb394a2c803eb11133a31ac996c60c" dependencies = [ - "polkavm-common 0.8.0", + "polkavm-common", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] name = "polkavm-derive-impl-macro" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e85319a0d5129dc9f021c62607e0804f5fb777a05cdda44d750ac0732def66" +checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ - "polkavm-derive-impl 0.8.0", - "syn 2.0.50", + "polkavm-derive-impl", + "syn 2.0.55", ] [[package]] name = "polkavm-linker" -version = "0.5.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5a668bb33c7f0b5f4ca91adb1e1e71cf4930fef5e6909f46c2180d65cce37d0" +checksum = "9c7be503e60cf56c0eb785f90aaba4b583b36bff00e93997d93fef97f9553c39" dependencies = [ - "gimli 0.28.0", + "gimli 0.28.1", "hashbrown 0.14.3", "log", "object 0.32.2", - "polkavm-common 0.5.0", + "polkavm-common", "regalloc2 0.9.3", "rustc-demangle", ] [[package]] -name = "polkavm-linker" -version = "0.8.2" +name = "polkavm-linux-raw" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdec1451cb18261d5d01de82acc15305e417fb59588cdcb3127d3dcc9672b925" -dependencies = [ - "gimli 0.28.0", - "hashbrown 0.14.3", - "log", - "object 0.32.2", - "polkavm-common 0.8.0", - "regalloc2 0.9.3", - "rustc-demangle", -] +checksum = "26e85d3456948e650dff0cfc85603915847faf893ed1e66b020bb82ef4557120" [[package]] name = "polling" @@ -13767,19 +13946,23 @@ dependencies = [ "concurrent-queue", "libc", "log", - "pin-project-lite 0.2.12", + "pin-project-lite 0.2.13", "windows-sys 0.48.0", ] [[package]] -name = "poly1305" -version = "0.7.2" +name = "polling" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +checksum = "e0c976a60b2d7e99d6f229e414670a9b85d13ac305cc6d1e9c134de58c5aaaf6" dependencies = [ - "cpufeatures", - "opaque-debug 0.3.0", - "universal-hash 0.4.0", + "cfg-if", + "concurrent-queue", + "hermit-abi 0.3.9", + "pin-project-lite 0.2.13", + "rustix 0.38.32", + "tracing", + "windows-sys 0.52.0", ] [[package]] @@ -13789,39 +13972,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ "cpufeatures", - "opaque-debug 0.3.0", - "universal-hash 0.5.1", -] - -[[package]] -name = "polyval" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug 0.3.0", - "universal-hash 0.4.0", + "opaque-debug 0.3.1", + "universal-hash", ] [[package]] name = "polyval" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", "cpufeatures", - "opaque-debug 0.3.0", - "universal-hash 0.5.1", + "opaque-debug 0.3.1", + "universal-hash", ] [[package]] name = "portable-atomic" -version = "1.4.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f32154ba0af3a075eefa1eda8bb414ee928f62303a54ea85b8d6638ff1a6ee9e" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" [[package]] name = "portpicker" @@ -13832,6 +14003,12 @@ dependencies = [ "rand", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "pprof" version = "0.12.1" @@ -13843,7 +14020,7 @@ dependencies = [ "findshlibs", "libc", "log", - "nix 0.26.2", + "nix 0.26.4", "once_cell", "parking_lot 0.12.1", "smallvec", @@ -13874,13 +14051,12 @@ dependencies = [ [[package]] name = "predicates" -version = "3.0.3" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9" +checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" dependencies = [ "anstyle", "difflib", - "itertools 0.10.5", "predicates-core", ] @@ -13900,6 +14076,16 @@ dependencies = [ "termtree", ] +[[package]] +name = "prettier-please" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22020dfcf177fcc7bf5deaf7440af371400c67c0de14c399938d8ed4fb4645d3" +dependencies = [ + "proc-macro2", + "syn 2.0.55", +] + [[package]] name = "pretty_assertions" version = "1.4.0" @@ -13912,9 +14098,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.1.25" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +checksum = "f28f53e8b192565862cf99343194579a022eb9c7dd3a8d03134734803c7b3125" dependencies = [ "proc-macro2", "syn 1.0.109", @@ -13922,12 +14108,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.12" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" +checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" dependencies = [ "proc-macro2", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -13973,11 +14159,20 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.0.0" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b2685dd208a3771337d8d386a89840f0f43cd68be8dae90a5f8c2384effc9cd" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "toml_edit 0.21.0", + "toml_edit 0.21.1", ] [[package]] @@ -14004,28 +14199,22 @@ dependencies = [ "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-warning" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b698b0b09d40e9b7c1a47b132d66a8b54bcd20583d9b6d06e4535e383b4405c" +checksum = "834da187cfe638ae8abb0203f0b33e5ccdb02a28e7199f2f47b3e2754f50edca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] name = "proc-macro2" -version = "1.0.75" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -14036,13 +14225,13 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "731e0d9356b0c25f16f33b5be79b1c57b562f141ebfcdb0ad8ac2c13a24293b4" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.5.0", "chrono", "flate2", "hex", "lazy_static", "procfs-core", - "rustix 0.38.21", + "rustix 0.38.32", ] [[package]] @@ -14051,7 +14240,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.5.0", "chrono", "hex", ] @@ -14090,17 +14279,17 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] name = "prometheus-parse" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2aa5feb83bf4b2c8919eaf563f51dbab41183de73ba2353c0e03cd7b6bd892" +checksum = "811031bea65e5a401fb2e1f37d802cca6601e204ac463809a3189352d13b78a5" dependencies = [ "chrono", - "itertools 0.10.5", + "itertools 0.12.1", "once_cell", "regex", ] @@ -14113,13 +14302,13 @@ checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.4.0", + "bitflags 2.5.0", "lazy_static", "num-traits", "rand", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", "rusty-fork", "tempfile", "unarray", @@ -14152,13 +14341,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ "bytes", - "heck", + "heck 0.4.1", "itertools 0.10.5", "lazy_static", "log", "multimap", "petgraph", - "prettyplease 0.1.25", + "prettyplease 0.1.11", "prost 0.11.9", "prost-types", "regex", @@ -14190,7 +14379,7 @@ dependencies = [ "itertools 0.11.0", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -14243,13 +14432,12 @@ dependencies = [ [[package]] name = "quanta" -version = "0.11.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17e662a7a8291a865152364c20c7abc5e60486ab2001e8ec10b24862de0b9ab" +checksum = "9ca0b7bac0b97248c40bb77288fc52029cf1459c0461ea1b05ee32ccf011de2c" dependencies = [ "crossbeam-utils", "libc", - "mach2", "once_cell", "raw-cpuid", "wasi 0.11.0+wasi-snapshot-preview1", @@ -14309,15 +14497,15 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c956be1b23f4261676aed05a0046e204e8a6836e50203902683a718af0797989" +checksum = "94b0b33c13a79f669c85defaf4c275dc86a0c0372807d0ca3d78e0bb87274863" dependencies = [ "bytes", "rand", "ring 0.16.20", "rustc-hash", - "rustls 0.20.8", + "rustls 0.20.9", "slab", "thiserror", "tinyvec", @@ -14386,7 +14574,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.10", + "getrandom 0.2.12", ] [[package]] @@ -14419,11 +14607,11 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "10.7.0" +version = "11.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" +checksum = "9d86a7c4638d42c44551f4791a20e687dbb4c3de1f33c43dd71e355cd429def1" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", ] [[package]] @@ -14434,9 +14622,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.7.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -14444,14 +14632,12 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.11.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] @@ -14475,15 +14661,6 @@ 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_syscall" version = "0.4.1" @@ -14495,12 +14672,12 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ - "getrandom 0.2.10", - "redox_syscall 0.2.16", + "getrandom 0.2.12", + "libredox", "thiserror", ] @@ -14518,22 +14695,22 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acde58d073e9c79da00f2b5b84eed919c8326832648a5b109b3fce1bb1175280" +checksum = "c4846d4c50d1721b1a3bef8af76924eef20d5e723647333798c1b519b3a9473f" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925" +checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -14563,14 +14740,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.3", - "regex-syntax 0.8.2", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", ] [[package]] @@ -14584,19 +14761,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" - -[[package]] -name = "regex-automata" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -14607,15 +14778,21 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "relative-path" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc" [[package]] name = "remote-ext-tests-bags-list" version = "1.0.0" dependencies = [ - "clap 4.5.1", + "clap 4.5.4", "frame-system", "log", "pallet-bags-list-remote-tests", @@ -14628,9 +14805,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.20" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ "base64 0.21.7", "bytes", @@ -14648,12 +14825,14 @@ dependencies = [ "mime", "once_cell", "percent-encoding", - "pin-project-lite 0.2.12", + "pin-project-lite 0.2.13", "rustls 0.21.10", - "rustls-pemfile 1.0.3", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", + "system-configuration", "tokio", "tokio-rustls 0.24.1", "tower-service", @@ -14661,7 +14840,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.25.2", + "webpki-roots 0.25.4", "winreg", ] @@ -14698,7 +14877,7 @@ dependencies = [ "blake2 0.10.6", "common", "fflonk", - "merlin 3.0.0", + "merlin", ] [[package]] @@ -14718,16 +14897,17 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", - "getrandom 0.2.10", + "cfg-if", + "getrandom 0.2.12", "libc", "spin 0.9.8", "untrusted 0.9.0", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -14792,6 +14972,7 @@ dependencies = [ "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", + "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-utility", "frame-benchmarking", "frame-executive", @@ -14933,6 +15114,7 @@ dependencies = [ "substrate-wasm-builder", "tiny-keccak", "tokio", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -14984,13 +15166,42 @@ checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" [[package]] name = "rpassword" -version = "7.2.0" +version = "7.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" +checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" dependencies = [ "libc", "rtoolbox", - "winapi", + "windows-sys 0.48.0", +] + +[[package]] +name = "rstest" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version 0.4.0", +] + +[[package]] +name = "rstest_macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" +dependencies = [ + "cfg-if", + "glob", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version 0.4.0", + "syn 2.0.55", + "unicode-ident", ] [[package]] @@ -15010,19 +15221,19 @@ dependencies = [ [[package]] name = "rtoolbox" -version = "0.0.1" +version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" +checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" dependencies = [ "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "ruint" -version = "1.11.1" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608a5726529f2f0ef81b8fde9873c4bb829d6b5b5ca6be4d97345ddf0749c825" +checksum = "8f308135fef9fc398342da5472ce7c484529df23743fb7c734e0f3d472971e62" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", @@ -15044,9 +15255,9 @@ dependencies = [ [[package]] name = "ruint-macro" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" +checksum = "f86854cf50259291520509879a5c294c3c9a4c334e9ff65071c51e42ef1e2343" [[package]] name = "rustc-demangle" @@ -15090,7 +15301,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.18", + "semver 1.0.22", ] [[package]] @@ -15104,9 +15315,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.15" +version = "0.36.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c37f1bd5ef1b5422177b7646cba67430579cfe2ace80f284fee876bca52ad941" +checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed" dependencies = [ "bitflags 1.3.2", "errno", @@ -15118,9 +15329,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.23" +version = "0.37.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" dependencies = [ "bitflags 1.3.2", "errno", @@ -15132,22 +15343,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.21" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.5.0", "errno", "libc", - "linux-raw-sys 0.4.10", - "windows-sys 0.48.0", + "linux-raw-sys 0.4.13", + "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ "log", "ring 0.16.20", @@ -15162,19 +15373,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", - "ring 0.17.7", + "ring 0.17.8", "rustls-webpki 0.101.7", "sct", ] [[package]] name = "rustls" -version = "0.22.2" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" +checksum = "99008d7ad0bbbea527ec27bddbc0e432c5b87d8175178cee68d2eec9c4a1813c" dependencies = [ "log", - "ring 0.17.7", + "ring 0.17.8", "rustls-pki-types", "rustls-webpki 0.102.2", "subtle 2.5.0", @@ -15188,7 +15399,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls-pemfile 1.0.3", + "rustls-pemfile 1.0.4", "schannel", "security-framework", ] @@ -15200,7 +15411,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" dependencies = [ "openssl-probe", - "rustls-pemfile 2.0.0", + "rustls-pemfile 2.1.1", "rustls-pki-types", "schannel", "security-framework", @@ -15208,18 +15419,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ "base64 0.21.7", ] [[package]] name = "rustls-pemfile" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e4980fa29e4c4b212ffb3db068a564cbf560e51d3944b7c88bd8bf5bec64f4" +checksum = "f48172685e6ff52a556baa527774f61fcaa884f59daf3375c62a3f1cd2549dab" dependencies = [ "base64 0.21.7", "rustls-pki-types", @@ -15227,9 +15438,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.2.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a716eb65e3158e90e17cd93d855216e27bde02745ab842f2cab4a39dba1bacf" +checksum = "868e20fada228fefaf6b652e00cc73623d54f8171e7352c18bb281571f2d92da" [[package]] name = "rustls-webpki" @@ -15237,7 +15448,7 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "untrusted 0.9.0", ] @@ -15247,7 +15458,7 @@ version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "rustls-pki-types", "untrusted 0.9.0", ] @@ -15294,9 +15505,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "safe-mix" @@ -15344,6 +15555,7 @@ dependencies = [ "futures-timer", "ip_network", "libp2p", + "linked_hash_set", "log", "multihash 0.18.1", "multihash-codetable", @@ -15411,10 +15623,10 @@ dependencies = [ name = "sc-chain-spec" version = "27.0.0" dependencies = [ - "array-bytes 6.1.0", - "docify", + "array-bytes 6.2.2", + "docify 0.2.7", "log", - "memmap2 0.9.3", + "memmap2 0.9.4", "parity-scale-codec", "sc-chain-spec-derive", "sc-client-api", @@ -15440,20 +15652,19 @@ dependencies = [ name = "sc-chain-spec-derive" version = "11.0.0" dependencies = [ - "proc-macro-crate 3.0.0", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] name = "sc-cli" version = "0.36.0" dependencies = [ - "array-bytes 6.1.0", - "bip39", + "array-bytes 6.2.2", "chrono", - "clap 4.5.1", + "clap 4.5.4", "fdlimit", "futures", "futures-timer", @@ -15461,6 +15672,7 @@ dependencies = [ "libp2p-identity", "log", "names", + "parity-bip39", "parity-scale-codec", "rand", "regex", @@ -15522,7 +15734,7 @@ dependencies = [ name = "sc-client-db" version = "0.35.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "criterion 0.4.0", "hash-db", "kitchensink-runtime", @@ -15688,8 +15900,8 @@ dependencies = [ name = "sc-consensus-beefy" version = "13.0.0" dependencies = [ - "array-bytes 6.1.0", - "async-channel", + "array-bytes 6.2.2", + "async-channel 1.9.0", "async-trait", "fnv", "futures", @@ -15764,8 +15976,8 @@ dependencies = [ name = "sc-consensus-grandpa" version = "0.19.0" dependencies = [ - "ahash 0.8.8", - "array-bytes 6.1.0", + "ahash 0.8.11", + "array-bytes 6.2.2", "assert_matches", "async-trait", "dyn-clone", @@ -15922,7 +16134,7 @@ dependencies = [ name = "sc-executor" version = "0.32.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "assert_matches", "criterion 0.4.0", "env_logger 0.9.3", @@ -15932,6 +16144,7 @@ dependencies = [ "paste", "regex", "sc-executor-common", + "sc-executor-polkavm", "sc-executor-wasmtime", "sc-runtime-test", "sc-tracing", @@ -15961,6 +16174,7 @@ dependencies = [ name = "sc-executor-common" version = "0.29.0" dependencies = [ + "polkavm", "sc-allocator", "sp-maybe-compressed-blob", "sp-wasm-interface 20.0.0", @@ -15968,6 +16182,16 @@ dependencies = [ "wasm-instrument", ] +[[package]] +name = "sc-executor-polkavm" +version = "0.29.0" +dependencies = [ + "log", + "polkavm", + "sc-executor-common", + "sp-wasm-interface 20.0.0", +] + [[package]] name = "sc-executor-wasmtime" version = "0.29.0" @@ -15980,7 +16204,7 @@ dependencies = [ "parity-scale-codec", "parking_lot 0.12.1", "paste", - "rustix 0.36.15", + "rustix 0.36.17", "sc-allocator", "sc-executor-common", "sc-runtime-test", @@ -16012,7 +16236,7 @@ dependencies = [ name = "sc-keystore" version = "25.0.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "parking_lot 0.12.1", "serde_json", "sp-application-crypto", @@ -16054,9 +16278,9 @@ dependencies = [ name = "sc-network" version = "0.34.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "assert_matches", - "async-channel", + "async-channel 1.9.0", "async-trait", "asynchronous-codec", "bytes", @@ -16107,7 +16331,7 @@ dependencies = [ name = "sc-network-bitswap" version = "0.33.0" dependencies = [ - "async-channel", + "async-channel 1.9.0", "cid", "futures", "libp2p-identity", @@ -16150,7 +16374,7 @@ dependencies = [ name = "sc-network-gossip" version = "0.34.0" dependencies = [ - "ahash 0.8.8", + "ahash 0.8.11", "async-trait", "futures", "futures-timer", @@ -16173,8 +16397,8 @@ dependencies = [ name = "sc-network-light" version = "0.33.0" dependencies = [ - "array-bytes 6.1.0", - "async-channel", + "array-bytes 6.2.2", + "async-channel 1.9.0", "futures", "libp2p-identity", "log", @@ -16193,8 +16417,8 @@ dependencies = [ name = "sc-network-statement" version = "0.16.0" dependencies = [ - "array-bytes 6.1.0", - "async-channel", + "array-bytes 6.2.2", + "async-channel 1.9.0", "futures", "libp2p", "log", @@ -16211,8 +16435,8 @@ dependencies = [ name = "sc-network-sync" version = "0.33.0" dependencies = [ - "array-bytes 6.1.0", - "async-channel", + "array-bytes 6.2.2", + "async-channel 1.9.0", "async-trait", "fork-tree", "futures", @@ -16281,7 +16505,7 @@ dependencies = [ name = "sc-network-transactions" version = "0.33.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "futures", "libp2p", "log", @@ -16299,7 +16523,7 @@ dependencies = [ name = "sc-offchain" version = "29.0.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "bytes", "fnv", "futures", @@ -16415,7 +16639,6 @@ dependencies = [ "hyper", "jsonrpsee", "log", - "pin-project", "serde_json", "substrate-prometheus-endpoint", "tokio", @@ -16427,7 +16650,7 @@ dependencies = [ name = "sc-rpc-spec-v2" version = "0.34.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "assert_matches", "futures", "futures-util", @@ -16514,6 +16737,7 @@ dependencies = [ "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", + "schnellru", "serde", "serde_json", "sp-api", @@ -16545,8 +16769,8 @@ dependencies = [ name = "sc-service-test" version = "2.0.0" dependencies = [ - "array-bytes 6.1.0", - "async-channel", + "array-bytes 6.2.2", + "async-channel 1.9.0", "fdlimit", "futures", "log", @@ -16611,7 +16835,7 @@ dependencies = [ name = "sc-storage-monitor" version = "0.16.0" dependencies = [ - "clap 4.5.1", + "clap 4.5.4", "fs4", "log", "sp-core", @@ -16702,7 +16926,7 @@ dependencies = [ "sp-tracing 16.0.0", "thiserror", "tracing", - "tracing-log 0.1.3", + "tracing-log 0.1.4", "tracing-subscriber 0.2.25", ] @@ -16710,17 +16934,17 @@ dependencies = [ name = "sc-tracing-proc-macro" version = "11.0.0" dependencies = [ - "proc-macro-crate 3.0.0", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] name = "sc-transaction-pool" version = "28.0.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "assert_matches", "async-trait", "criterion 0.4.0", @@ -16770,7 +16994,7 @@ dependencies = [ name = "sc-utils" version = "14.0.0" dependencies = [ - "async-channel", + "async-channel 1.9.0", "futures", "futures-timer", "lazy_static", @@ -16783,9 +17007,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" +checksum = "788745a868b0e751750388f4e6546eb921ef714a4317fa6954f7cde114eb2eb7" dependencies = [ "bitvec", "cfg-if", @@ -16797,9 +17021,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" +checksum = "7dc2f4e8bc344b9fc3d5f74f72c2e55bfc38d28dc2ebc69c194a3df424e4d9ac" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", @@ -16809,18 +17033,18 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "schemars" -version = "0.8.13" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763f8cd0d4c71ed8389c90cb8100cba87e763bd01a8e614d4f0af97bcd50a161" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" dependencies = [ "dyn-clone", "schemars_derive", @@ -16830,9 +17054,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.13" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0f696e21e10fa546b7ffb1c9672c6de8fbc7a81acf59524386d8639bf12737" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" dependencies = [ "proc-macro2", "quote", @@ -16846,27 +17070,11 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" dependencies = [ - "ahash 0.8.8", + "ahash 0.8.11", "cfg-if", "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", - "merlin 2.0.1", - "rand_core 0.5.1", - "sha2 0.8.2", - "subtle 2.5.0", - "zeroize", -] - [[package]] name = "schnorrkel" version = "0.10.2" @@ -16876,7 +17084,7 @@ dependencies = [ "arrayref", "arrayvec 0.7.4", "curve25519-dalek-ng", - "merlin 3.0.0", + "merlin", "rand_core 0.6.4", "sha2 0.9.9", "subtle-ng", @@ -16889,12 +17097,12 @@ version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de18f6d8ba0aad7045f5feae07ec29899c1112584a38509a84ad7b04451eaa0" dependencies = [ - "aead 0.5.2", + "aead", "arrayref", "arrayvec 0.7.4", "curve25519-dalek 4.1.2", "getrandom_or_panic", - "merlin 3.0.0", + "merlin", "rand_core 0.6.4", "serde_bytes", "sha2 0.10.8", @@ -16922,12 +17130,12 @@ checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] @@ -16940,6 +17148,7 @@ dependencies = [ "der", "generic-array 0.14.7", "pkcs8", + "serdect", "subtle 2.5.0", "zeroize", ] @@ -16955,18 +17164,18 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.28.0" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5" +checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" dependencies = [ "secp256k1-sys", ] [[package]] name = "secp256k1-sys" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e67c467c38fd24bd5499dc9a18183b31575c12ee549197e3e20d57aa4fe3b7" +checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" dependencies = [ "cc", ] @@ -17067,9 +17276,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.18" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" dependencies = [ "serde", ] @@ -17115,9 +17324,9 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.12" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" dependencies = [ "serde", ] @@ -17130,7 +17339,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -17155,9 +17364,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" dependencies = [ "itoa", "ryu", @@ -17166,9 +17375,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] @@ -17187,17 +17396,27 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.32" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.2.3", + "indexmap 2.2.6", "itoa", "ryu", "serde", "unsafe-libyaml", ] +[[package]] +name = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "serial_test" version = "2.0.0" @@ -17220,7 +17439,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -17233,14 +17452,14 @@ dependencies = [ "cfg-if", "cpufeatures", "digest 0.9.0", - "opaque-debug 0.3.0", + "opaque-debug 0.3.1", ] [[package]] name = "sha-1" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ "cfg-if", "cpufeatures", @@ -17258,18 +17477,6 @@ dependencies = [ "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" @@ -17280,7 +17487,7 @@ dependencies = [ "cfg-if", "cpufeatures", "digest 0.9.0", - "opaque-debug 0.3.0", + "opaque-debug 0.3.1", ] [[package]] @@ -17306,9 +17513,9 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.4" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] @@ -17356,16 +17563,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -17377,9 +17574,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", "rand_core 0.6.4", @@ -17438,18 +17635,18 @@ dependencies = [ [[package]] name = "slotmap" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" dependencies = [ "version_check", ] [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smol" @@ -17457,24 +17654,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13f2b548cd8447f8de0fdf1c592929f70f4fc7039a05e47404b0d096ec6987a1" dependencies = [ - "async-channel", + "async-channel 1.9.0", "async-executor", "async-fs", - "async-io", + "async-io 1.13.0", "async-lock 2.8.0", "async-net", "async-process", "blocking", - "futures-lite", -] - -[[package]] -name = "smol_str" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" -dependencies = [ - "serde", + "futures-lite 1.13.0", ] [[package]] @@ -17489,30 +17677,30 @@ dependencies = [ "base64 0.21.7", "bip39", "blake2-rfc", - "bs58 0.5.0", - "chacha20 0.9.1", + "bs58 0.5.1", + "chacha20", "crossbeam-queue", "derive_more", "ed25519-zebra 4.0.3", "either", "event-listener 2.5.3", "fnv", - "futures-lite", + "futures-lite 1.13.0", "futures-util", "hashbrown 0.14.3", "hex", "hmac 0.12.1", "itertools 0.11.0", "libsecp256k1", - "merlin 3.0.0", + "merlin", "no-std-net", "nom", "num-bigint", "num-rational", "num-traits", - "pbkdf2 0.12.2", + "pbkdf2", "pin-project", - "poly1305 0.8.0", + "poly1305", "rand", "rand_chacha 0.3.1", "ruzstd", @@ -17527,7 +17715,7 @@ dependencies = [ "soketto", "twox-hash", "wasmi", - "x25519-dalek 2.0.0", + "x25519-dalek 2.0.1", "zeroize", ] @@ -17537,7 +17725,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "256b5bad1d6b49045e95fe87492ce73d5af81545d8b4d8318a872d2007024c33" dependencies = [ - "async-channel", + "async-channel 1.9.0", "async-lock 2.8.0", "base64 0.21.7", "blake2-rfc", @@ -17546,13 +17734,13 @@ dependencies = [ "event-listener 2.5.3", "fnv", "futures-channel", - "futures-lite", + "futures-lite 1.13.0", "futures-util", "hashbrown 0.14.3", "hex", "itertools 0.11.0", "log", - "lru 0.11.0", + "lru 0.11.1", "no-std-net", "parking_lot 0.12.1", "pin-project", @@ -17569,22 +17757,22 @@ dependencies = [ [[package]] name = "snap" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" [[package]] name = "snow" -version = "0.9.3" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9d1425eb528a21de2755c75af4c9b5d57f50a0d4c3b7f1828a4cd03f8ba155" +checksum = "850948bee068e713b8ab860fe1adc4d109676ab4c3b621fd8147f06b261f2f85" dependencies = [ - "aes-gcm 0.9.2", + "aes-gcm", "blake2 0.10.6", "chacha20poly1305", "curve25519-dalek 4.1.2", "rand_core 0.6.4", - "ring 0.16.20", + "ring 0.17.8", "rustc_version 0.4.0", "sha2 0.10.8", "subtle 2.5.0", @@ -17602,7 +17790,7 @@ dependencies = [ [[package]] name = "snowbridge-beacon-primitives" -version = "0.0.0" +version = "0.2.0" dependencies = [ "byte-slice-cast", "frame-support", @@ -17626,7 +17814,7 @@ dependencies = [ [[package]] name = "snowbridge-core" -version = "0.0.0" +version = "0.2.0" dependencies = [ "ethabi-decode", "frame-support", @@ -17649,7 +17837,7 @@ dependencies = [ [[package]] name = "snowbridge-ethereum" -version = "0.1.0" +version = "0.3.0" dependencies = [ "ethabi-decode", "ethbloom", @@ -17688,7 +17876,7 @@ dependencies = [ [[package]] name = "snowbridge-outbound-queue-merkle-tree" -version = "0.1.1" +version = "0.3.0" dependencies = [ "array-bytes 4.2.0", "env_logger 0.9.3", @@ -17703,7 +17891,7 @@ dependencies = [ [[package]] name = "snowbridge-outbound-queue-runtime-api" -version = "0.0.0" +version = "0.2.0" dependencies = [ "frame-support", "parity-scale-codec", @@ -17717,7 +17905,7 @@ dependencies = [ [[package]] name = "snowbridge-pallet-ethereum-client" -version = "0.0.0" +version = "0.2.0" dependencies = [ "bp-runtime", "byte-slice-cast", @@ -17763,7 +17951,7 @@ dependencies = [ [[package]] name = "snowbridge-pallet-inbound-queue" -version = "0.0.0" +version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -17796,7 +17984,7 @@ dependencies = [ [[package]] name = "snowbridge-pallet-inbound-queue-fixtures" -version = "0.9.0" +version = "0.10.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -17810,7 +17998,7 @@ dependencies = [ [[package]] name = "snowbridge-pallet-outbound-queue" -version = "0.0.0" +version = "0.2.0" dependencies = [ "bridge-hub-common", "ethabi-decode", @@ -17835,7 +18023,7 @@ dependencies = [ [[package]] name = "snowbridge-pallet-system" -version = "0.0.0" +version = "0.2.0" dependencies = [ "ethabi-decode", "frame-benchmarking", @@ -17863,7 +18051,7 @@ dependencies = [ [[package]] name = "snowbridge-router-primitives" -version = "0.0.0" +version = "0.9.0" dependencies = [ "ethabi-decode", "frame-support", @@ -17886,7 +18074,7 @@ dependencies = [ [[package]] name = "snowbridge-runtime-common" -version = "0.0.0" +version = "0.2.0" dependencies = [ "frame-support", "frame-system", @@ -17902,7 +18090,7 @@ dependencies = [ [[package]] name = "snowbridge-runtime-test-common" -version = "0.0.0" +version = "0.2.0" dependencies = [ "assets-common", "bridge-hub-test-utils", @@ -17979,7 +18167,7 @@ dependencies = [ [[package]] name = "snowbridge-system-runtime-api" -version = "0.0.0" +version = "0.2.0" dependencies = [ "parity-scale-codec", "snowbridge-core", @@ -17991,9 +18179,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", @@ -18001,12 +18189,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -18026,6 +18214,87 @@ dependencies = [ "sha-1 0.9.8", ] +[[package]] +name = "solochain-template-node" +version = "0.0.0" +dependencies = [ + "clap 4.5.4", + "frame-benchmarking-cli", + "frame-system", + "futures", + "jsonrpsee", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc", + "sc-basic-authorship", + "sc-cli", + "sc-client-api", + "sc-consensus", + "sc-consensus-aura", + "sc-consensus-grandpa", + "sc-executor", + "sc-network", + "sc-offchain", + "sc-rpc-api", + "sc-service", + "sc-telemetry", + "sc-transaction-pool", + "sc-transaction-pool-api", + "serde_json", + "solochain-template-runtime", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus-aura", + "sp-consensus-grandpa", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keyring", + "sp-runtime", + "sp-timestamp", + "substrate-build-script-utils", + "substrate-frame-rpc-system", + "try-runtime-cli", +] + +[[package]] +name = "solochain-template-runtime" +version = "0.0.0" +dependencies = [ + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "pallet-aura", + "pallet-balances", + "pallet-grandpa", + "pallet-sudo", + "pallet-template", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-consensus-grandpa", + "sp-core", + "sp-genesis-builder", + "sp-inherents", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std 14.0.0", + "sp-storage 19.0.0", + "sp-transaction-pool", + "sp-version", + "substrate-wasm-builder", +] + [[package]] name = "sp-api" version = "26.0.0" @@ -18055,11 +18324,11 @@ dependencies = [ "Inflector", "assert_matches", "blake2 0.10.6", - "expander 2.0.0", - "proc-macro-crate 3.0.0", + "expander 2.1.0", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -18113,6 +18382,7 @@ name = "sp-arithmetic" version = "23.0.0" dependencies = [ "criterion 0.4.0", + "docify 0.2.7", "integer-sqrt", "num-traits", "parity-scale-codec", @@ -18142,7 +18412,7 @@ version = "0.4.2" source = "git+https://github.com/paritytech/arkworks-substrate#caa2eed74beb885dd07c7db5f916f2281dad818f" dependencies = [ "ark-bls12-381-ext", - "sp-crypto-ec-utils 0.4.1", + "sp-crypto-ec-utils 0.10.0 (git+https://github.com/paritytech/polkadot-sdk)", ] [[package]] @@ -18151,7 +18421,7 @@ version = "0.4.2" source = "git+https://github.com/paritytech/arkworks-substrate#caa2eed74beb885dd07c7db5f916f2281dad818f" dependencies = [ "ark-ed-on-bls12-381-bandersnatch-ext", - "sp-crypto-ec-utils 0.4.1", + "sp-crypto-ec-utils 0.10.0 (git+https://github.com/paritytech/polkadot-sdk)", ] [[package]] @@ -18163,7 +18433,6 @@ dependencies = [ "sp-api", "sp-application-crypto", "sp-runtime", - "sp-std 14.0.0", ] [[package]] @@ -18173,7 +18442,6 @@ dependencies = [ "sp-api", "sp-inherents", "sp-runtime", - "sp-std 14.0.0", ] [[package]] @@ -18220,7 +18488,6 @@ dependencies = [ "sp-consensus-slots", "sp-inherents", "sp-runtime", - "sp-std 14.0.0", "sp-timestamp", ] @@ -18238,7 +18505,6 @@ dependencies = [ "sp-core", "sp-inherents", "sp-runtime", - "sp-std 14.0.0", "sp-timestamp", ] @@ -18246,7 +18512,7 @@ dependencies = [ name = "sp-consensus-beefy" version = "13.0.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "lazy_static", "parity-scale-codec", "scale-info", @@ -18259,7 +18525,6 @@ dependencies = [ "sp-keystore", "sp-mmr-primitives", "sp-runtime", - "sp-std 14.0.0", "strum 0.24.1", "w3f-bls", ] @@ -18278,7 +18543,6 @@ dependencies = [ "sp-core", "sp-keystore", "sp-runtime", - "sp-std 14.0.0", ] [[package]] @@ -18289,7 +18553,6 @@ dependencies = [ "sp-api", "sp-core", "sp-runtime", - "sp-std 14.0.0", ] [[package]] @@ -18304,7 +18567,6 @@ dependencies = [ "sp-consensus-slots", "sp-core", "sp-runtime", - "sp-std 14.0.0", ] [[package]] @@ -18314,7 +18576,6 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-std 14.0.0", "sp-timestamp", ] @@ -18322,13 +18583,12 @@ dependencies = [ name = "sp-core" version = "28.0.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "bandersnatch_vrfs", - "bip39", "bitflags 1.3.2", "blake2 0.10.6", "bounded-collections", - "bs58 0.5.0", + "bs58 0.5.1", "criterion 0.4.0", "dyn-clonable", "ed25519-zebra 3.1.0", @@ -18337,10 +18597,12 @@ dependencies = [ "hash256-std-hasher", "impl-serde", "itertools 0.10.5", + "k256", "lazy_static", "libsecp256k1", "log", - "merlin 3.0.0", + "merlin", + "parity-bip39", "parity-scale-codec", "parking_lot 0.12.1", "paste", @@ -18393,8 +18655,7 @@ dependencies = [ [[package]] name = "sp-crypto-ec-utils" -version = "0.4.1" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +version = "0.10.0" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -18407,14 +18668,14 @@ dependencies = [ "ark-ed-on-bls12-377-ext", "ark-ed-on-bls12-381-bandersnatch", "ark-ed-on-bls12-381-bandersnatch-ext", - "ark-scale 0.0.11", - "sp-runtime-interface 17.0.0", - "sp-std 8.0.0", + "ark-scale", + "sp-runtime-interface 24.0.0", ] [[package]] name = "sp-crypto-ec-utils" version = "0.10.0" +source = "git+https://github.com/paritytech/polkadot-sdk#25af0adf7836c67e28083276ec6f06d974e4f685" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -18427,9 +18688,8 @@ dependencies = [ "ark-ed-on-bls12-377-ext", "ark-ed-on-bls12-381-bandersnatch", "ark-ed-on-bls12-381-bandersnatch-ext", - "ark-scale 0.0.12", - "sp-runtime-interface 24.0.0", - "sp-std 14.0.0", + "ark-scale", + "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk)", ] [[package]] @@ -18452,7 +18712,7 @@ version = "0.0.0" dependencies = [ "quote", "sp-crypto-hashing", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -18465,42 +18725,40 @@ dependencies = [ [[package]] name = "sp-debug-derive" -version = "8.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +version = "14.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] name = "sp-debug-derive" version = "14.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#25af0adf7836c67e28083276ec6f06d974e4f685" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] name = "sp-externalities" -version = "0.19.0" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +version = "0.25.0" dependencies = [ "environmental", "parity-scale-codec", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-storage 19.0.0", ] [[package]] name = "sp-externalities" version = "0.25.0" +source = "git+https://github.com/paritytech/polkadot-sdk#25af0adf7836c67e28083276ec6f06d974e4f685" dependencies = [ "environmental", "parity-scale-codec", - "sp-std 14.0.0", - "sp-storage 19.0.0", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk)", ] [[package]] @@ -18510,7 +18768,6 @@ dependencies = [ "serde_json", "sp-api", "sp-runtime", - "sp-std 14.0.0", ] [[package]] @@ -18523,7 +18780,6 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime", - "sp-std 14.0.0", "thiserror", ] @@ -18536,6 +18792,7 @@ dependencies = [ "libsecp256k1", "log", "parity-scale-codec", + "polkavm-derive", "rustversion", "secp256k1", "sp-core", @@ -18587,7 +18844,6 @@ dependencies = [ "frame-metadata", "parity-scale-codec", "scale-info", - "sp-std 14.0.0", ] [[package]] @@ -18598,14 +18854,13 @@ dependencies = [ "scale-info", "sp-api", "sp-application-crypto", - "sp-std 14.0.0", ] [[package]] name = "sp-mmr-primitives" version = "26.0.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "ckb-merkle-mountain-range", "log", "parity-scale-codec", @@ -18615,7 +18870,6 @@ dependencies = [ "sp-core", "sp-debug-derive 14.0.0", "sp-runtime", - "sp-std 14.0.0", "thiserror", ] @@ -18630,7 +18884,6 @@ dependencies = [ "sp-arithmetic", "sp-core", "sp-runtime", - "sp-std 14.0.0", "substrate-test-utils", ] @@ -18638,7 +18891,7 @@ dependencies = [ name = "sp-npos-elections-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.5.1", + "clap 4.5.4", "honggfuzz", "rand", "sp-npos-elections", @@ -18677,7 +18930,7 @@ dependencies = [ name = "sp-runtime" version = "31.0.1" dependencies = [ - "docify", + "docify 0.2.7", "either", "hash256-std-hasher", "impl-trait-for-tuples", @@ -18702,24 +18955,6 @@ dependencies = [ "zstd 0.12.4", ] -[[package]] -name = "sp-runtime-interface" -version = "17.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" -dependencies = [ - "bytes", - "impl-trait-for-tuples", - "parity-scale-codec", - "primitive-types", - "sp-externalities 0.19.0", - "sp-runtime-interface-proc-macro 11.0.0", - "sp-std 8.0.0", - "sp-storage 13.0.0", - "sp-tracing 10.0.0", - "sp-wasm-interface 14.0.0", - "static_assertions", -] - [[package]] name = "sp-runtime-interface" version = "24.0.0" @@ -18727,7 +18962,7 @@ dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", - "polkavm-derive 0.8.0", + "polkavm-derive", "primitive-types", "rustversion", "sp-core", @@ -18744,28 +18979,48 @@ dependencies = [ "trybuild", ] +[[package]] +name = "sp-runtime-interface" +version = "24.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#25af0adf7836c67e28083276ec6f06d974e4f685" +dependencies = [ + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec", + "polkavm-derive", + "primitive-types", + "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk)", + "sp-runtime-interface-proc-macro 17.0.0 (git+https://github.com/paritytech/polkadot-sdk)", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk)", + "sp-storage 19.0.0 (git+https://github.com/paritytech/polkadot-sdk)", + "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk)", + "sp-wasm-interface 20.0.0 (git+https://github.com/paritytech/polkadot-sdk)", + "static_assertions", +] + [[package]] name = "sp-runtime-interface-proc-macro" -version = "11.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +version = "17.0.0" dependencies = [ "Inflector", - "proc-macro-crate 1.3.1", + "expander 2.1.0", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] name = "sp-runtime-interface-proc-macro" version = "17.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#25af0adf7836c67e28083276ec6f06d974e4f685" dependencies = [ "Inflector", - "expander 2.0.0", - "proc-macro-crate 3.0.0", + "expander 2.1.0", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -18817,7 +19072,6 @@ dependencies = [ "sp-keystore", "sp-runtime", "sp-staking", - "sp-std 14.0.0", ] [[package]] @@ -18830,14 +19084,13 @@ dependencies = [ "serde", "sp-core", "sp-runtime", - "sp-std 14.0.0", ] [[package]] name = "sp-state-machine" version = "0.35.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "assert_matches", "hash-db", "log", @@ -18850,7 +19103,6 @@ dependencies = [ "sp-externalities 0.25.0", "sp-panic-handler", "sp-runtime", - "sp-std 14.0.0", "sp-trie", "thiserror", "tracing", @@ -18861,7 +19113,7 @@ dependencies = [ name = "sp-statement-store" version = "10.0.0" dependencies = [ - "aes-gcm 0.10.3", + "aes-gcm", "curve25519-dalek 4.1.2", "ed25519-dalek", "hkdf", @@ -18876,43 +19128,40 @@ dependencies = [ "sp-externalities 0.25.0", "sp-runtime", "sp-runtime-interface 24.0.0", - "sp-std 14.0.0", "thiserror", - "x25519-dalek 2.0.0", + "x25519-dalek 2.0.1", ] [[package]] name = "sp-std" -version = "8.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +version = "14.0.0" [[package]] name = "sp-std" version = "14.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#25af0adf7836c67e28083276ec6f06d974e4f685" [[package]] name = "sp-storage" -version = "13.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +version = "19.0.0" dependencies = [ "impl-serde", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive 8.0.0", - "sp-std 8.0.0", + "sp-debug-derive 14.0.0", ] [[package]] name = "sp-storage" version = "19.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#25af0adf7836c67e28083276ec6f06d974e4f685" dependencies = [ "impl-serde", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive 14.0.0", - "sp-std 14.0.0", + "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk)", ] [[package]] @@ -18925,7 +19174,6 @@ dependencies = [ "sp-application-crypto", "sp-core", "sp-runtime", - "sp-std 14.0.0", ] [[package]] @@ -18936,17 +19184,14 @@ dependencies = [ "parity-scale-codec", "sp-inherents", "sp-runtime", - "sp-std 14.0.0", "thiserror", ] [[package]] name = "sp-tracing" -version = "10.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +version = "16.0.0" dependencies = [ "parity-scale-codec", - "sp-std 8.0.0", "tracing", "tracing-core", "tracing-subscriber 0.2.25", @@ -18955,9 +19200,9 @@ dependencies = [ [[package]] name = "sp-tracing" version = "16.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#25af0adf7836c67e28083276ec6f06d974e4f685" dependencies = [ "parity-scale-codec", - "sp-std 14.0.0", "tracing", "tracing-core", "tracing-subscriber 0.2.25", @@ -18981,7 +19226,6 @@ dependencies = [ "sp-core", "sp-inherents", "sp-runtime", - "sp-std 14.0.0", "sp-trie", ] @@ -18989,8 +19233,8 @@ dependencies = [ name = "sp-trie" version = "29.0.0" dependencies = [ - "ahash 0.8.8", - "array-bytes 6.1.0", + "ahash 0.8.11", + "array-bytes 6.2.2", "criterion 0.4.0", "hash-db", "lazy_static", @@ -19004,7 +19248,6 @@ dependencies = [ "sp-core", "sp-externalities 0.25.0", "sp-runtime", - "sp-std 14.0.0", "thiserror", "tracing", "trie-bench", @@ -19037,31 +19280,29 @@ dependencies = [ "proc-macro2", "quote", "sp-version", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] name = "sp-wasm-interface" -version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +version = "20.0.0" dependencies = [ "anyhow", "impl-trait-for-tuples", "log", "parity-scale-codec", - "sp-std 8.0.0", "wasmtime", ] [[package]] name = "sp-wasm-interface" version = "20.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#25af0adf7836c67e28083276ec6f06d974e4f685" dependencies = [ "anyhow", "impl-trait-for-tuples", "log", "parity-scale-codec", - "sp-std 14.0.0", "wasmtime", ] @@ -19077,7 +19318,6 @@ dependencies = [ "smallvec", "sp-arithmetic", "sp-debug-derive 14.0.0", - "sp-std 14.0.0", ] [[package]] @@ -19094,20 +19334,29 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "spinners" -version = "4.1.0" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08615eea740067d9899969bc2891c68a19c315cb1f66640af9a9ecb91b13bcab" +checksum = "a0ef947f358b9c238923f764c72a4a9d42f2d637c46e059dbd319d6e7cfb4f82" dependencies = [ "lazy_static", "maplit", "strum 0.24.1", ] +[[package]] +name = "spinning_top" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", @@ -19115,9 +19364,9 @@ dependencies = [ [[package]] name = "ss58-registry" -version = "1.43.0" +version = "1.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6915280e2d0db8911e5032a5c275571af6bdded2916abd691a659be25d3439" +checksum = "4743ce898933fbff7bbf414f497c459a782d496269644b3d650a398ae6a487ba" dependencies = [ "Inflector", "num-format", @@ -19161,7 +19410,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" name = "staging-chain-spec-builder" version = "2.0.0" dependencies = [ - "clap 4.5.1", + "clap 4.5.4", "log", "sc-chain-spec", "serde_json", @@ -19172,9 +19421,9 @@ dependencies = [ name = "staging-node-cli" version = "3.0.0-dev" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "assert_cmd", - "clap 4.5.1", + "clap 4.5.4", "clap_complete", "criterion 0.4.0", "frame-benchmarking", @@ -19187,7 +19436,7 @@ dependencies = [ "kitchensink-runtime", "log", "mmr-gadget", - "nix 0.26.2", + "nix 0.26.4", "node-primitives", "node-rpc", "node-testing", @@ -19284,7 +19533,7 @@ dependencies = [ name = "staging-node-inspect" version = "0.12.0" dependencies = [ - "clap 4.5.1", + "clap 4.5.4", "parity-scale-codec", "sc-cli", "sc-client-api", @@ -19318,7 +19567,7 @@ version = "2.0.0" name = "staging-xcm" version = "7.0.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "bounded-collections", "derivative", "environmental", @@ -19465,7 +19714,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", @@ -19478,31 +19727,31 @@ version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] name = "subkey" version = "9.0.0" dependencies = [ - "clap 4.5.1", + "clap 4.5.4", "sc-cli", ] [[package]] name = "substrate-bip39" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e620c7098893ba667438b47169c00aacdd9e7c10e042250ce2b60b087ec97328" +version = "0.4.7" dependencies = [ - "hmac 0.11.0", - "pbkdf2 0.8.0", - "schnorrkel 0.9.1", - "sha2 0.9.9", + "bip39", + "hmac 0.12.1", + "pbkdf2", + "rustc-hex", + "schnorrkel 0.11.4", + "sha2 0.10.8", "zeroize", ] @@ -19516,7 +19765,7 @@ version = "0.1.0" dependencies = [ "assert_cmd", "futures", - "nix 0.26.2", + "nix 0.26.4", "node-primitives", "regex", "sc-cli", @@ -19531,7 +19780,7 @@ dependencies = [ name = "substrate-frame-cli" version = "32.0.0" dependencies = [ - "clap 4.5.1", + "clap 4.5.4", "frame-support", "frame-system", "sc-cli", @@ -19625,7 +19874,7 @@ dependencies = [ name = "substrate-test-client" version = "2.0.1" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "async-trait", "futures", "parity-scale-codec", @@ -19651,7 +19900,7 @@ dependencies = [ name = "substrate-test-runtime" version = "2.0.0" dependencies = [ - "array-bytes 6.1.0", + "array-bytes 6.2.2", "frame-executive", "frame-support", "frame-system", @@ -19688,7 +19937,6 @@ dependencies = [ "sp-runtime", "sp-session", "sp-state-machine", - "sp-std 14.0.0", "sp-tracing 16.0.0", "sp-transaction-pool", "sp-trie", @@ -19749,11 +19997,11 @@ dependencies = [ "console", "filetime", "parity-wasm", - "polkavm-linker 0.8.2", + "polkavm-linker", "sp-maybe-compressed-blob", "strum 0.24.1", "tempfile", - "toml 0.8.8", + "toml 0.8.12", "walkdir", "wasm-opt", ] @@ -19778,15 +20026,15 @@ checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" [[package]] name = "sval" -version = "2.6.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b031320a434d3e9477ccf9b5756d57d4272937b8d22cb88af80b7633a1b78b1" +checksum = "38a2b3c2dc4e86741e7631ad50ff798c0df4a37b81366f2072f8948805e706e9" [[package]] name = "sval_buffer" -version = "2.6.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bf7e9412af26b342f3f2cc5cc4122b0105e9d16eb76046cd14ed10106cf6028" +checksum = "787c386d99b431292aab95054c555899b259cf069a358dee1b252a76ca8785e3" dependencies = [ "sval", "sval_ref", @@ -19794,18 +20042,18 @@ dependencies = [ [[package]] name = "sval_dynamic" -version = "2.6.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0ef628e8a77a46ed3338db8d1b08af77495123cc229453084e47cd716d403cf" +checksum = "584324174c3c6191a15e0f732b1b34352a87da1a82645644854494f018279ca1" dependencies = [ "sval", ] [[package]] name = "sval_fmt" -version = "2.6.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dc09e9364c2045ab5fa38f7b04d077b3359d30c4c2b3ec4bae67a358bd64326" +checksum = "d0ab125b30b0d250c8821d39e697f073a0e52bca1159c8a14333fdc1e82ab6ef" dependencies = [ "itoa", "ryu", @@ -19814,53 +20062,63 @@ dependencies = [ [[package]] name = "sval_json" -version = "2.6.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ada6f627e38cbb8860283649509d87bc4a5771141daa41c78fd31f2b9485888d" +checksum = "3937398726f8368bcd42d5a18a9b885668e3f183e07f2b845d5a6a52613c0de2" dependencies = [ "itoa", "ryu", "sval", ] +[[package]] +name = "sval_nested" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71c1d35fcbcf0927417de2d558e2b17287eebfd0f1d1e4cd58728b76402428f8" +dependencies = [ + "sval", + "sval_buffer", + "sval_ref", +] + [[package]] name = "sval_ref" -version = "2.6.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703ca1942a984bd0d9b5a4c0a65ab8b4b794038d080af4eb303c71bc6bf22d7c" +checksum = "82694f1a7b53e7765dfe1e9ff1719a05148b647bbac334f8de648b2cd4cd74c5" dependencies = [ "sval", ] [[package]] name = "sval_serde" -version = "2.6.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830926cd0581f7c3e5d51efae4d35c6b6fc4db583842652891ba2f1bed8db046" +checksum = "bf83cd41641c035f9f341bac7c465f5a553dec3b5b06d7691bfd633ab9b05106" dependencies = [ "serde", "sval", - "sval_buffer", - "sval_fmt", + "sval_nested", ] [[package]] name = "symbolic-common" -version = "12.3.0" +version = "12.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167a4ffd7c35c143fd1030aa3c2caf76ba42220bd5a6b5f4781896434723b8c3" +checksum = "1cccfffbc6bb3bb2d3a26cd2077f4d055f6808d266f9d4d158797a4c60510dfe" dependencies = [ "debugid", - "memmap2 0.5.10", + "memmap2 0.9.4", "stable_deref_trait", "uuid", ] [[package]] name = "symbolic-demangle" -version = "12.3.0" +version = "12.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e378c50e80686c1c5c205674e1f86a2858bec3d2a7dfdd690331a8a19330f293" +checksum = "76a99812da4020a67e76c4eb41f08c87364c14170495ff780f30dd519c221a68" dependencies = [ "cpp_demangle 0.4.3", "rustc-demangle", @@ -19880,9 +20138,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.50" +version = "2.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" +checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" dependencies = [ "proc-macro2", "quote", @@ -19898,9 +20156,15 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "synstructure" version = "0.12.6" @@ -19953,28 +20217,27 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.11" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "tempfile" -version = "3.8.1" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.0.0", - "redox_syscall 0.4.1", - "rustix 0.38.21", - "windows-sys 0.48.0", + "fastrand 2.0.2", + "rustix 0.38.32", + "windows-sys 0.52.0", ] [[package]] name = "termcolor" -version = "1.2.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] @@ -19985,7 +20248,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ - "rustix 0.38.21", + "rustix 0.38.32", "windows-sys 0.48.0", ] @@ -20012,7 +20275,7 @@ dependencies = [ name = "test-parachain-adder-collator" version = "1.0.0" dependencies = [ - "clap 4.5.1", + "clap 4.5.4", "futures", "futures-timer", "log", @@ -20060,7 +20323,7 @@ dependencies = [ name = "test-parachain-undying-collator" version = "1.0.0" dependencies = [ - "clap 4.5.1", + "clap 4.5.4", "futures", "futures-timer", "log", @@ -20122,48 +20385,48 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-core" -version = "1.0.38" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d97345f6437bb2004cd58819d8a9ef8e36cdd7661c2abc4bbde0a7c40d9f497" +checksum = "c001ee18b7e5e3f62cbf58c7fe220119e68d902bb7443179c0c8aef30090e999" dependencies = [ "thiserror-core-impl", ] [[package]] name = "thiserror-core-impl" -version = "1.0.38" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10ac1c5050e43014d16b2f94d0d2ce79e65ffdd8b38d8048f9c8f6a8a6da62ac" +checksum = "e4c60d69f36615a077cc7663b9cb8e42275722d23e58a7fa3d2c7f2915d09d04" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.55", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -20174,9 +20437,9 @@ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -20237,12 +20500,14 @@ dependencies = [ [[package]] name = "time" -version = "0.3.27" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb39ee79a6d8de55f48f2293a830e040392f1c5f16e336bdd1788cd0aadce07" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ "deranged", "itoa", + "num-conv", + "powerfmt", "serde", "time-core", "time-macros", @@ -20250,16 +20515,17 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.13" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733d258752e9303d392b94b75230d07b0b9c489350c69b851fc6c065fde3e8f9" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ + "num-conv", "time-core", ] @@ -20309,9 +20575,9 @@ dependencies = [ "mio", "num_cpus", "parking_lot 0.12.1", - "pin-project-lite 0.2.12", + "pin-project-lite 0.2.13", "signal-hook-registry", - "socket2 0.5.5", + "socket2 0.5.6", "tokio-macros", "windows-sys 0.48.0", ] @@ -20324,7 +20590,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -20354,28 +20620,28 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ - "rustls 0.22.2", + "rustls 0.22.3", "rustls-pki-types", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", - "pin-project-lite 0.2.12", + "pin-project-lite 0.2.13", "tokio", "tokio-util", ] [[package]] name = "tokio-test" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719" +checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" dependencies = [ "async-stream", "bytes", @@ -20398,15 +20664,15 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", - "pin-project-lite 0.2.12", + "pin-project-lite 0.2.13", "tokio", "tracing", ] @@ -20422,14 +20688,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.8" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.21.0", + "toml_edit 0.22.9", ] [[package]] @@ -20447,22 +20713,44 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.3", + "indexmap 2.2.6", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" dependencies = [ - "indexmap 2.2.3", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.5", ] [[package]] @@ -20474,7 +20762,7 @@ dependencies = [ "futures-core", "futures-util", "pin-project", - "pin-project-lite 0.2.12", + "pin-project-lite 0.2.13", "tower-layer", "tower-service", "tracing", @@ -20482,18 +20770,18 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ae70283aba8d2a8b411c695c437fe25b8b5e44e23e780662002fc72fb47a82" +checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.5.0", "bytes", "futures-core", "futures-util", "http", "http-body", "http-range-header", - "pin-project-lite 0.2.12", + "pin-project-lite 0.2.13", "tower-layer", "tower-service", ] @@ -20517,7 +20805,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "log", - "pin-project-lite 0.2.12", + "pin-project-lite 0.2.13", "tracing-attributes", "tracing-core", ] @@ -20530,7 +20818,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -20568,21 +20856,21 @@ name = "tracing-gum-proc-macro" version = "5.0.0" dependencies = [ "assert_matches", - "expander 2.0.0", - "proc-macro-crate 3.0.0", + "expander 2.1.0", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" dependencies = [ - "lazy_static", "log", + "once_cell", "tracing-core", ] @@ -20626,7 +20914,7 @@ dependencies = [ "thread_local", "tracing", "tracing-core", - "tracing-log 0.1.3", + "tracing-log 0.1.4", "tracing-serde", ] @@ -20714,7 +21002,7 @@ dependencies = [ "lazy_static", "rand", "smallvec", - "socket2 0.4.9", + "socket2 0.4.10", "thiserror", "tinyvec", "tokio", @@ -20744,9 +21032,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "try-runtime-cli" @@ -20754,7 +21042,7 @@ version = "0.38.0" dependencies = [ "assert_cmd", "async-trait", - "clap 4.5.1", + "clap 4.5.4", "frame-remote-externalities", "frame-try-runtime", "hex", @@ -20791,11 +21079,10 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.89" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a9d3ba662913483d6722303f619e75ea10b7855b0f8e0d72799cf8621bb488f" +checksum = "2aa6f84ec205ebf87fb7a0abdbcd1467fa5af0e86878eb6d888b78ecbb10b6d5" dependencies = [ - "basic-toml", "dissimilar", "glob", "once_cell", @@ -20803,6 +21090,7 @@ dependencies = [ "serde_derive", "serde_json", "termcolor", + "toml 0.8.12", ] [[package]] @@ -20824,7 +21112,7 @@ dependencies = [ "httparse", "log", "rand", - "sha-1 0.10.0", + "sha-1 0.10.1", "thiserror", "url", "utf-8", @@ -20844,9 +21132,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" @@ -20874,15 +21162,15 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -20895,9 +21183,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "unicode-xid" @@ -20905,16 +21193,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" -[[package]] -name = "universal-hash" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" -dependencies = [ - "generic-array 0.14.7", - "subtle 2.5.0", -] - [[package]] name = "universal-hash" version = "0.5.1" @@ -20927,15 +21205,15 @@ dependencies = [ [[package]] name = "unsafe-libyaml" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "unsigned-varint" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" +checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" dependencies = [ "asynchronous-codec", "bytes", @@ -20957,12 +21235,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna 0.4.0", + "idna 0.5.0", "percent-encoding", ] @@ -20980,9 +21258,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.4.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" [[package]] name = "valuable" @@ -20992,9 +21270,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.4.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3" +checksum = "74797339c3b98616c009c7c3eb53a0ce41e85c8ec66bd3db96ed132d20cfdee8" dependencies = [ "value-bag-serde1", "value-bag-sval2", @@ -21002,9 +21280,9 @@ dependencies = [ [[package]] name = "value-bag-serde1" -version = "1.4.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0b9f3feef403a50d4d67e9741a6d8fc688bcbb4e4f31bd4aab72cc690284394" +checksum = "cc35703541cbccb5278ef7b589d79439fc808ff0b5867195a3230f9a47421d39" dependencies = [ "erased-serde", "serde", @@ -21013,9 +21291,9 @@ dependencies = [ [[package]] name = "value-bag-sval2" -version = "1.4.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b24f4146b6f3361e91cbf527d1fb35e9376c3c0cef72ca5ec5af6d640fad7d" +checksum = "285b43c29d0b4c0e65aad24561baee67a1b69dc9be9375d4a85138cbf556f7f8" dependencies = [ "sval", "sval_buffer", @@ -21079,15 +21357,15 @@ dependencies = [ [[package]] name = "waker-fn" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -21114,11 +21392,20 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasix" +version = "0.12.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1fbb4ef9bbca0c1170e0b00dd28abc9e3b68669821600cad1caaed606583c6d" +dependencies = [ + "wasi 0.11.0+wasi-snapshot-preview1", +] + [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "serde", @@ -21128,24 +21415,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -21155,9 +21442,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -21165,28 +21452,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-bindgen-test" -version = "0.3.37" +version = "0.3.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e6e302a7ea94f83a6d09e78e7dc7d9ca7b186bc2829c24a22d0753efd680671" +checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" dependencies = [ "console_error_panic_hook", "js-sys", @@ -21198,19 +21485,20 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.37" +version = "0.3.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" +checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" dependencies = [ "proc-macro2", "quote", + "syn 2.0.55", ] [[package]] name = "wasm-encoder" -version = "0.31.1" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41763f20eafed1399fff1afb466496d3a959f58241436cfdc17e3f5ca954de16" +checksum = "bfd106365a7f5f7aa3c1916a98cbb3ad477f5ff96ddb130285a91c6e7429e67a" dependencies = [ "leb128", ] @@ -21378,7 +21666,7 @@ dependencies = [ "directories-next", "file-per-thread-logger", "log", - "rustix 0.36.15", + "rustix 0.36.17", "serde", "sha2 0.10.8", "toml 0.5.11", @@ -21474,7 +21762,7 @@ checksum = "6e0554b84c15a27d76281d06838aed94e13a77d7bf604bbbaf548aa20eb93846" dependencies = [ "object 0.30.4", "once_cell", - "rustix 0.36.15", + "rustix 0.36.17", ] [[package]] @@ -21505,7 +21793,7 @@ dependencies = [ "memoffset 0.8.0", "paste", "rand", - "rustix 0.36.15", + "rustix 0.36.17", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-jit-debug", @@ -21526,10 +21814,11 @@ dependencies = [ [[package]] name = "wast" -version = "63.0.0" +version = "202.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2560471f60a48b77fccefaf40796fda61c97ce1e790b59dfcec9dc3995c9f63a" +checksum = "1fbcb11204515c953c9b42ede0a46a1c5e17f82af05c4fae201a8efff1b0f4fe" dependencies = [ + "bumpalo", "leb128", "memchr", "unicode-width", @@ -21538,18 +21827,18 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.70" +version = "1.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdc306c2c4c2f2bf2ba69e083731d0d2a77437fc6a350a19db139636e7e416c" +checksum = "4de4b15a47135c56a3573406e9977b9518787a6154459b4842a9b9d3d1684848" dependencies = [ "wast", ] [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -21561,7 +21850,7 @@ version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "untrusted 0.9.0", ] @@ -21576,9 +21865,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.2" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "westend-emulated-chain" @@ -21707,6 +21996,7 @@ dependencies = [ "tiny-keccak", "tokio", "westend-runtime-constants", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -21739,20 +22029,21 @@ dependencies = [ [[package]] name = "which" -version = "4.4.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", + "rustix 0.38.32", ] [[package]] name = "wide" -version = "0.7.11" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa469ffa65ef7e0ba0f164183697b89b854253fd31aeb92358b7b6155177d62f" +checksum = "89beec544f246e679fc25490e3f8e08003bc4bf612068f325120dad4cea02c1c" dependencies = [ "bytemuck", "safe_arch", @@ -21782,9 +22073,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -21797,26 +22088,32 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.34.0" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45296b64204227616fdbf2614cefa4c236b98ee64dfaaaa435207ed99fe7829f" +checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" 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", + "windows-core 0.51.1", + "windows-targets 0.48.5", ] [[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.4", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -21841,7 +22138,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.4", ] [[package]] @@ -21876,17 +22173,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" 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", + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] [[package]] @@ -21903,15 +22200,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[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" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" @@ -21927,15 +22218,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[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" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" @@ -21951,15 +22236,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[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" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" @@ -21975,15 +22254,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[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" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" @@ -21999,9 +22272,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" @@ -22017,15 +22290,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[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" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" @@ -22041,15 +22308,24 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winnow" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] [[package]] name = "winnow" -version = "0.5.15" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" dependencies = [ "memchr", ] @@ -22086,9 +22362,9 @@ dependencies = [ [[package]] name = "x25519-dalek" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ "curve25519-dalek 4.1.2", "rand_core 0.6.4", @@ -22116,11 +22392,13 @@ dependencies = [ [[package]] name = "xattr" -version = "1.0.1" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" dependencies = [ "libc", + "linux-raw-sys 0.4.13", + "rustix 0.38.32", ] [[package]] @@ -22166,10 +22444,12 @@ dependencies = [ "pallet-transaction-payment", "pallet-xcm", "parity-scale-codec", + "polkadot-service", "polkadot-test-client", "polkadot-test-runtime", "polkadot-test-service", "sp-consensus", + "sp-core", "sp-keyring", "sp-runtime", "sp-state-machine", @@ -22178,6 +22458,20 @@ dependencies = [ "staging-xcm-executor", ] +[[package]] +name = "xcm-fee-payment-runtime-api" +version = "0.1.0" +dependencies = [ + "frame-support", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-runtime", + "sp-std 14.0.0", + "sp-weights", + "staging-xcm", +] + [[package]] name = "xcm-procedural" version = "7.0.0" @@ -22186,7 +22480,7 @@ dependencies = [ "proc-macro2", "quote", "staging-xcm", - "syn 2.0.50", + "syn 2.0.55", "trybuild", ] @@ -22308,14 +22602,14 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" dependencies = [ "zeroize_derive", ] @@ -22328,7 +22622,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.55", ] [[package]] @@ -22388,11 +22682,10 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" dependencies = [ "cc", - "libc", "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index b671e8f1dfae44ba99aaacc668a9045facc7f49a..ebea7786f3ed0d5443a5b56439d5c8cff2502ca4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,30 +3,31 @@ authors = ["Parity Technologies "] edition = "2021" repository = "https://github.com/paritytech/polkadot-sdk.git" license = "GPL-3.0-only" +homepage = "https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/index.html" [workspace] resolver = "2" members = [ "bridges/bin/runtime-common", + "bridges/chains/chain-asset-hub-rococo", + "bridges/chains/chain-asset-hub-westend", + "bridges/chains/chain-bridge-hub-cumulus", + "bridges/chains/chain-bridge-hub-kusama", + "bridges/chains/chain-bridge-hub-polkadot", + "bridges/chains/chain-bridge-hub-rococo", + "bridges/chains/chain-bridge-hub-westend", + "bridges/chains/chain-kusama", + "bridges/chains/chain-polkadot", + "bridges/chains/chain-polkadot-bulletin", + "bridges/chains/chain-rococo", + "bridges/chains/chain-westend", "bridges/modules/grandpa", "bridges/modules/messages", "bridges/modules/parachains", "bridges/modules/relayers", "bridges/modules/xcm-bridge-hub", "bridges/modules/xcm-bridge-hub-router", - "bridges/primitives/chain-asset-hub-rococo", - "bridges/primitives/chain-asset-hub-westend", - "bridges/primitives/chain-bridge-hub-cumulus", - "bridges/primitives/chain-bridge-hub-kusama", - "bridges/primitives/chain-bridge-hub-polkadot", - "bridges/primitives/chain-bridge-hub-rococo", - "bridges/primitives/chain-bridge-hub-westend", - "bridges/primitives/chain-kusama", - "bridges/primitives/chain-polkadot", - "bridges/primitives/chain-polkadot-bulletin", - "bridges/primitives/chain-rococo", - "bridges/primitives/chain-westend", "bridges/primitives/header-chain", "bridges/primitives/messages", "bridges/primitives/parachains", @@ -74,9 +75,6 @@ members = [ "cumulus/pallets/solo-to-para", "cumulus/pallets/xcm", "cumulus/pallets/xcmp-queue", - "cumulus/parachain-template/node", - "cumulus/parachain-template/pallets/template", - "cumulus/parachain-template/runtime", "cumulus/parachains/common", "cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo", "cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend", @@ -127,6 +125,7 @@ members = [ "cumulus/primitives/core", "cumulus/primitives/parachain-inherent", "cumulus/primitives/proof-size-hostfunction", + "cumulus/primitives/storage-weight-reclaim", "cumulus/primitives/timestamp", "cumulus/primitives/utility", "cumulus/test/client", @@ -215,6 +214,7 @@ members = [ "polkadot/xcm/xcm-builder", "polkadot/xcm/xcm-executor", "polkadot/xcm/xcm-executor/integration-tests", + "polkadot/xcm/xcm-fee-payment-runtime-api", "polkadot/xcm/xcm-simulator", "polkadot/xcm/xcm-simulator/example", "polkadot/xcm/xcm-simulator/fuzzer", @@ -250,6 +250,7 @@ members = [ "substrate/client/db", "substrate/client/executor", "substrate/client/executor/common", + "substrate/client/executor/polkavm", "substrate/client/executor/runtime-test", "substrate/client/executor/wasmtime", "substrate/client/informant", @@ -331,6 +332,7 @@ members = [ "substrate/frame/examples/frame-crate", "substrate/frame/examples/kitchensink", "substrate/frame/examples/offchain-worker", + "substrate/frame/examples/single-block-migrations", "substrate/frame/examples/split", "substrate/frame/examples/tasks", "substrate/frame/executive", @@ -345,6 +347,7 @@ members = [ "substrate/frame/membership", "substrate/frame/merkle-mountain-range", "substrate/frame/message-queue", + "substrate/frame/migrations", "substrate/frame/mixnet", "substrate/frame/multisig", "substrate/frame/nft-fractionalization", @@ -495,7 +498,20 @@ members = [ "substrate/utils/frame/rpc/system", "substrate/utils/frame/try-runtime/cli", "substrate/utils/prometheus", + "substrate/utils/substrate-bip39", "substrate/utils/wasm-builder", + + "templates/minimal/node", + "templates/minimal/pallets/template", + "templates/minimal/runtime", + + "templates/solochain/node", + "templates/solochain/pallets/template", + "templates/solochain/runtime", + + "templates/parachain/node", + "templates/parachain/pallets/template", + "templates/parachain/runtime", ] default-members = ["polkadot", "substrate/bin/node/cli"] exclude = ["polkadot/zombienet-sdk-tests"] @@ -530,16 +546,17 @@ extra-unused-type-parameters = { level = "allow", priority = 2 } # stylistic default_constructed_unit_structs = { level = "allow", priority = 2 } # stylistic [workspace.dependencies] -polkavm-linker = "0.8.2" -polkavm-derive = "0.8.0" -log = { version = "0.4.20", default-features = false } +polkavm = "0.9.3" +polkavm-linker = "0.9.2" +polkavm-derive = "0.9.1" +log = { version = "0.4.21", default-features = false } quote = { version = "1.0.33" } serde = { version = "1.0.197", default-features = false } serde-big-array = { version = "0.3.2" } serde_derive = { version = "1.0.117" } serde_json = { version = "1.0.114", default-features = false } serde_yaml = { version = "0.9" } -syn = { version = "2.0.50" } +syn = { version = "2.0.53" } thiserror = { version = "1.0.48" } [profile.release] diff --git a/bridges/bin/runtime-common/Cargo.toml b/bridges/bin/runtime-common/Cargo.toml index fac88b20ca57901d4116e147bd9363a41ff35e36..f00ba1c97345567c67a1576d5463a47e94629b2c 100644 --- a/bridges/bin/runtime-common/Cargo.toml +++ b/bridges/bin/runtime-common/Cargo.toml @@ -14,7 +14,7 @@ workspace = true codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } hash-db = { version = "0.16.0", default-features = false } log = { workspace = true } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.11.0", default-features = false, features = ["derive"] } static_assertions = { version = "1.1", optional = true } # Bridge dependencies diff --git a/bridges/bin/runtime-common/src/messages_api.rs b/bridges/bin/runtime-common/src/messages_api.rs index ccf1c754041ed84dc302f0660fdd5bde8dc8d533..7fbdeb366124778b36c77725be8ca8778020be1b 100644 --- a/bridges/bin/runtime-common/src/messages_api.rs +++ b/bridges/bin/runtime-common/src/messages_api.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Helpers for implementing various message-related runtime API mthods. +//! Helpers for implementing various message-related runtime API methods. use bp_messages::{ InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, diff --git a/bridges/bin/runtime-common/src/messages_xcm_extension.rs b/bridges/bin/runtime-common/src/messages_xcm_extension.rs index e3da6155f08a198d5469adbfc64e40213eddf8eb..46ed4da0d85481fcc7223740084945924f9c710f 100644 --- a/bridges/bin/runtime-common/src/messages_xcm_extension.rs +++ b/bridges/bin/runtime-common/src/messages_xcm_extension.rs @@ -248,7 +248,7 @@ impl LocalXcmQueueManager { sender_and_lane: &SenderAndLane, enqueued_messages: MessageNonce, ) { - // skip if we dont want to handle congestion + // skip if we don't want to handle congestion if !H::supports_congestion_detection() { return } diff --git a/bridges/bin/runtime-common/src/mock.rs b/bridges/bin/runtime-common/src/mock.rs index 8877a4fd95ce33150824b78674f38860616cf820..8c4cb2233e17c7ff0f6aa05f45483db07ef2b707 100644 --- a/bridges/bin/runtime-common/src/mock.rs +++ b/bridges/bin/runtime-common/src/mock.rs @@ -141,7 +141,7 @@ parameter_types! { pub const ReserveId: [u8; 8] = *b"brdgrlrs"; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for TestRuntime { type Hash = ThisChainHash; type Hashing = ThisChainHasher; @@ -158,12 +158,13 @@ impl pallet_utility::Config for TestRuntime { type WeightInfo = (); } -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for TestRuntime { type ReserveIdentifier = [u8; 8]; type AccountStore = System; } +#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)] impl pallet_transaction_payment::Config for TestRuntime { type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; type OperationalFeeMultiplier = ConstU8<5>; @@ -378,7 +379,7 @@ impl Chain for BridgedUnderlyingChain { impl ChainWithGrandpa for BridgedUnderlyingChain { const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; const MAX_AUTHORITIES_COUNT: u32 = 16; - const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; + const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8; const MAX_MANDATORY_HEADER_SIZE: u32 = 256; const AVERAGE_HEADER_SIZE: u32 = 64; } diff --git a/bridges/bin/runtime-common/src/priority_calculator.rs b/bridges/bin/runtime-common/src/priority_calculator.rs index a597fb9e2f49289360acfd7ee305b44eb7874a3e..5035553f508dfea94a0cb5ddf9b916dd7d9b4ea5 100644 --- a/bridges/bin/runtime-common/src/priority_calculator.rs +++ b/bridges/bin/runtime-common/src/priority_calculator.rs @@ -128,7 +128,7 @@ mod integrity_tests { Runtime::RuntimeCall: Dispatchable, BalanceOf: Send + Sync + FixedPointOperand, { - // esimate priority of transaction that delivers one message and has large tip + // estimate priority of transaction that delivers one message and has large tip let maximal_messages_in_delivery_transaction = Runtime::MaxUnconfirmedMessagesAtInboundLane::get(); let small_with_tip_priority = @@ -163,7 +163,7 @@ mod integrity_tests { { // just an estimation of extra transaction bytes that are added to every transaction // (including signature, signed extensions extra and etc + in our case it includes - // all call arguments extept the proof itself) + // all call arguments except the proof itself) let base_tx_size = 512; // let's say we are relaying similar small messages and for every message we add more trie // nodes to the proof (x0.5 because we expect some nodes to be reused) diff --git a/bridges/bin/runtime-common/src/refund_relayer_extension.rs b/bridges/bin/runtime-common/src/refund_relayer_extension.rs index bfcb82ad166c3a2a60891c1e18f2ad22e085cb1b..455392a0a277f3520cd7f58150f12e7420d36014 100644 --- a/bridges/bin/runtime-common/src/refund_relayer_extension.rs +++ b/bridges/bin/runtime-common/src/refund_relayer_extension.rs @@ -16,7 +16,7 @@ //! Signed extension that refunds relayer if he has delivered some new messages. //! It also refunds transaction cost if the transaction is an `utility.batchAll()` -//! with calls that are: delivering new messsage and all necessary underlying headers +//! with calls that are: delivering new message and all necessary underlying headers //! (parachain or relay chain). use crate::messages_call_ext::{ @@ -1538,7 +1538,7 @@ mod tests { } #[test] - fn validate_boosts_priority_of_message_delivery_transactons() { + fn validate_boosts_priority_of_message_delivery_transactions() { run_test(|| { initialize_environment(100, 100, 100); @@ -1568,7 +1568,7 @@ mod tests { } #[test] - fn validate_does_not_boost_priority_of_message_delivery_transactons_with_too_many_messages() { + fn validate_does_not_boost_priority_of_message_delivery_transactions_with_too_many_messages() { run_test(|| { initialize_environment(100, 100, 100); diff --git a/bridges/primitives/chain-asset-hub-rococo/Cargo.toml b/bridges/chains/chain-asset-hub-rococo/Cargo.toml similarity index 79% rename from bridges/primitives/chain-asset-hub-rococo/Cargo.toml rename to bridges/chains/chain-asset-hub-rococo/Cargo.toml index 4dfa149e0ea9ab4e0ac1804844a0c128f15bd5bb..55dc384badd56fbd389eb7010706120e6071c8f6 100644 --- a/bridges/primitives/chain-asset-hub-rococo/Cargo.toml +++ b/bridges/chains/chain-asset-hub-rococo/Cargo.toml @@ -11,13 +11,13 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.11.0", default-features = false, features = ["derive"] } # Substrate Dependencies frame-support = { path = "../../../substrate/frame/support", default-features = false } # Bridge Dependencies -bp-xcm-bridge-hub-router = { path = "../xcm-bridge-hub-router", default-features = false } +bp-xcm-bridge-hub-router = { path = "../../primitives/xcm-bridge-hub-router", default-features = false } [features] default = ["std"] diff --git a/bridges/primitives/chain-asset-hub-rococo/src/lib.rs b/bridges/chains/chain-asset-hub-rococo/src/lib.rs similarity index 100% rename from bridges/primitives/chain-asset-hub-rococo/src/lib.rs rename to bridges/chains/chain-asset-hub-rococo/src/lib.rs diff --git a/bridges/primitives/chain-asset-hub-westend/Cargo.toml b/bridges/chains/chain-asset-hub-westend/Cargo.toml similarity index 79% rename from bridges/primitives/chain-asset-hub-westend/Cargo.toml rename to bridges/chains/chain-asset-hub-westend/Cargo.toml index c9bd437562b86a97cdf2807c18b4905e695d1a5e..1379b099a2a85a1ede9012c3f34073c92a3f9a7f 100644 --- a/bridges/primitives/chain-asset-hub-westend/Cargo.toml +++ b/bridges/chains/chain-asset-hub-westend/Cargo.toml @@ -11,13 +11,13 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.11.0", default-features = false, features = ["derive"] } # Substrate Dependencies frame-support = { path = "../../../substrate/frame/support", default-features = false } # Bridge Dependencies -bp-xcm-bridge-hub-router = { path = "../xcm-bridge-hub-router", default-features = false } +bp-xcm-bridge-hub-router = { path = "../../primitives/xcm-bridge-hub-router", default-features = false } [features] default = ["std"] diff --git a/bridges/primitives/chain-asset-hub-westend/src/lib.rs b/bridges/chains/chain-asset-hub-westend/src/lib.rs similarity index 100% rename from bridges/primitives/chain-asset-hub-westend/src/lib.rs rename to bridges/chains/chain-asset-hub-westend/src/lib.rs diff --git a/bridges/primitives/chain-bridge-hub-cumulus/Cargo.toml b/bridges/chains/chain-bridge-hub-cumulus/Cargo.toml similarity index 80% rename from bridges/primitives/chain-bridge-hub-cumulus/Cargo.toml rename to bridges/chains/chain-bridge-hub-cumulus/Cargo.toml index d35eefa1c45c3f7cf479dcea2ee4da87dbc31627..5e14cb052b724ca26b30951492fbb2f97b472a4d 100644 --- a/bridges/primitives/chain-bridge-hub-cumulus/Cargo.toml +++ b/bridges/chains/chain-bridge-hub-cumulus/Cargo.toml @@ -12,9 +12,9 @@ workspace = true [dependencies] # Bridge Dependencies -bp-polkadot-core = { path = "../polkadot-core", default-features = false } -bp-messages = { path = "../messages", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } +bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } +bp-messages = { path = "../../primitives/messages", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } # Substrate Based Dependencies diff --git a/bridges/primitives/chain-bridge-hub-cumulus/src/lib.rs b/bridges/chains/chain-bridge-hub-cumulus/src/lib.rs similarity index 100% rename from bridges/primitives/chain-bridge-hub-cumulus/src/lib.rs rename to bridges/chains/chain-bridge-hub-cumulus/src/lib.rs diff --git a/bridges/primitives/chain-bridge-hub-kusama/Cargo.toml b/bridges/chains/chain-bridge-hub-kusama/Cargo.toml similarity index 85% rename from bridges/primitives/chain-bridge-hub-kusama/Cargo.toml rename to bridges/chains/chain-bridge-hub-kusama/Cargo.toml index 8d71b3f5eb7646a81af55c0030814c604ead82ef..77bc8e54a9d1b1b8e14cc2915ec6419f0128b1dc 100644 --- a/bridges/primitives/chain-bridge-hub-kusama/Cargo.toml +++ b/bridges/chains/chain-bridge-hub-kusama/Cargo.toml @@ -13,8 +13,8 @@ workspace = true # Bridge Dependencies bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } -bp-messages = { path = "../messages", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } +bp-messages = { path = "../../primitives/messages", default-features = false } # Substrate Based Dependencies diff --git a/bridges/primitives/chain-bridge-hub-kusama/src/lib.rs b/bridges/chains/chain-bridge-hub-kusama/src/lib.rs similarity index 100% rename from bridges/primitives/chain-bridge-hub-kusama/src/lib.rs rename to bridges/chains/chain-bridge-hub-kusama/src/lib.rs diff --git a/bridges/primitives/chain-bridge-hub-polkadot/Cargo.toml b/bridges/chains/chain-bridge-hub-polkadot/Cargo.toml similarity index 85% rename from bridges/primitives/chain-bridge-hub-polkadot/Cargo.toml rename to bridges/chains/chain-bridge-hub-polkadot/Cargo.toml index 4e89e8a5c9a173bc9a084af9e7c609a6bae287a8..5d7a3bbcc1da6d0785a6955f67e2fe77bea64957 100644 --- a/bridges/primitives/chain-bridge-hub-polkadot/Cargo.toml +++ b/bridges/chains/chain-bridge-hub-polkadot/Cargo.toml @@ -14,8 +14,8 @@ workspace = true # Bridge Dependencies bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } -bp-messages = { path = "../messages", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } +bp-messages = { path = "../../primitives/messages", default-features = false } # Substrate Based Dependencies diff --git a/bridges/primitives/chain-bridge-hub-polkadot/src/lib.rs b/bridges/chains/chain-bridge-hub-polkadot/src/lib.rs similarity index 100% rename from bridges/primitives/chain-bridge-hub-polkadot/src/lib.rs rename to bridges/chains/chain-bridge-hub-polkadot/src/lib.rs diff --git a/bridges/primitives/chain-bridge-hub-rococo/Cargo.toml b/bridges/chains/chain-bridge-hub-rococo/Cargo.toml similarity index 85% rename from bridges/primitives/chain-bridge-hub-rococo/Cargo.toml rename to bridges/chains/chain-bridge-hub-rococo/Cargo.toml index 1643d934a982ec0795fc370b221dc35d36d3b492..3966ef72dcb0abda2a9bf6eb568e9469cef7c4a7 100644 --- a/bridges/primitives/chain-bridge-hub-rococo/Cargo.toml +++ b/bridges/chains/chain-bridge-hub-rococo/Cargo.toml @@ -13,8 +13,8 @@ workspace = true # Bridge Dependencies bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } -bp-messages = { path = "../messages", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } +bp-messages = { path = "../../primitives/messages", default-features = false } # Substrate Based Dependencies diff --git a/bridges/primitives/chain-bridge-hub-rococo/src/lib.rs b/bridges/chains/chain-bridge-hub-rococo/src/lib.rs similarity index 100% rename from bridges/primitives/chain-bridge-hub-rococo/src/lib.rs rename to bridges/chains/chain-bridge-hub-rococo/src/lib.rs diff --git a/bridges/primitives/chain-bridge-hub-westend/Cargo.toml b/bridges/chains/chain-bridge-hub-westend/Cargo.toml similarity index 85% rename from bridges/primitives/chain-bridge-hub-westend/Cargo.toml rename to bridges/chains/chain-bridge-hub-westend/Cargo.toml index 32a7850c5392fd892ec40e9968fa365ec59cbba3..d35eac8b3fef428f0adedd0ae7b070362a31abe1 100644 --- a/bridges/primitives/chain-bridge-hub-westend/Cargo.toml +++ b/bridges/chains/chain-bridge-hub-westend/Cargo.toml @@ -14,8 +14,8 @@ workspace = true # Bridge Dependencies bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } -bp-messages = { path = "../messages", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } +bp-messages = { path = "../../primitives/messages", default-features = false } # Substrate Based Dependencies diff --git a/bridges/primitives/chain-bridge-hub-westend/src/lib.rs b/bridges/chains/chain-bridge-hub-westend/src/lib.rs similarity index 100% rename from bridges/primitives/chain-bridge-hub-westend/src/lib.rs rename to bridges/chains/chain-bridge-hub-westend/src/lib.rs diff --git a/bridges/primitives/chain-kusama/Cargo.toml b/bridges/chains/chain-kusama/Cargo.toml similarity index 73% rename from bridges/primitives/chain-kusama/Cargo.toml rename to bridges/chains/chain-kusama/Cargo.toml index 0660f34602389d1cf9e1ec88b57d1a9aeb9a3830..4ff4cb46976b6e85a4c2aa47827173b27c4097c7 100644 --- a/bridges/primitives/chain-kusama/Cargo.toml +++ b/bridges/chains/chain-kusama/Cargo.toml @@ -13,9 +13,9 @@ workspace = true # Bridge Dependencies -bp-header-chain = { path = "../header-chain", default-features = false } -bp-polkadot-core = { path = "../polkadot-core", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } +bp-header-chain = { path = "../../primitives/header-chain", default-features = false } +bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } # Substrate Based Dependencies diff --git a/bridges/primitives/chain-kusama/src/lib.rs b/bridges/chains/chain-kusama/src/lib.rs similarity index 95% rename from bridges/primitives/chain-kusama/src/lib.rs rename to bridges/chains/chain-kusama/src/lib.rs index e3b4d0520f61c858b54d78dfa4a45f57bac411fb..a81004afe8127b556211d0207d2bc1f9ecc02955 100644 --- a/bridges/primitives/chain-kusama/src/lib.rs +++ b/bridges/chains/chain-kusama/src/lib.rs @@ -53,8 +53,8 @@ impl Chain for Kusama { impl ChainWithGrandpa for Kusama { const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_KUSAMA_GRANDPA_PALLET_NAME; const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; - const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = - REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY; const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; } diff --git a/bridges/primitives/chain-polkadot-bulletin/Cargo.toml b/bridges/chains/chain-polkadot-bulletin/Cargo.toml similarity index 74% rename from bridges/primitives/chain-polkadot-bulletin/Cargo.toml rename to bridges/chains/chain-polkadot-bulletin/Cargo.toml index 15c824fcbdb31c2c84f63bd56d4d0b3f90efc5b1..37e060d897cdf2a474da830f9eb346ddfe35d5c0 100644 --- a/bridges/primitives/chain-polkadot-bulletin/Cargo.toml +++ b/bridges/chains/chain-polkadot-bulletin/Cargo.toml @@ -11,14 +11,14 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.11.0", default-features = false, features = ["derive"] } # Bridge Dependencies -bp-header-chain = { path = "../header-chain", default-features = false } -bp-messages = { path = "../messages", default-features = false } -bp-polkadot-core = { path = "../polkadot-core", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } +bp-header-chain = { path = "../../primitives/header-chain", default-features = false } +bp-messages = { path = "../../primitives/messages", default-features = false } +bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } # Substrate Based Dependencies diff --git a/bridges/primitives/chain-polkadot-bulletin/src/lib.rs b/bridges/chains/chain-polkadot-bulletin/src/lib.rs similarity index 96% rename from bridges/primitives/chain-polkadot-bulletin/src/lib.rs rename to bridges/chains/chain-polkadot-bulletin/src/lib.rs index f2eebf9312470a42e1d3a1c7d67ab8b7a38af189..f3d300567f2b4f92cec272e0929a3c53d718c823 100644 --- a/bridges/primitives/chain-polkadot-bulletin/src/lib.rs +++ b/bridges/chains/chain-polkadot-bulletin/src/lib.rs @@ -43,7 +43,7 @@ use sp_runtime::{traits::DispatchInfoOf, transaction_validity::TransactionValidi pub use bp_polkadot_core::{ AccountAddress, AccountId, Balance, Block, BlockNumber, Hash, Hasher, Header, Nonce, Signature, SignedBlock, UncheckedExtrinsic, AVERAGE_HEADER_SIZE, EXTRA_STORAGE_PROOF_SIZE, - MAX_MANDATORY_HEADER_SIZE, REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY, + MAX_MANDATORY_HEADER_SIZE, REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY, }; /// Maximal number of GRANDPA authorities at Polkadot Bulletin chain. @@ -62,7 +62,7 @@ const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(90); // Re following constants - we are using the same values at Cumulus parachains. They are limited // by the maximal transaction weight/size. Since block limits at Bulletin Chain are larger than -// at the Cumulus Bridgeg Hubs, we could reuse the same values. +// at the Cumulus Bridge Hubs, we could reuse the same values. /// Maximal number of unrewarded relayer entries at inbound lane for Cumulus-based parachains. pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; @@ -207,8 +207,8 @@ impl Chain for PolkadotBulletin { impl ChainWithGrandpa for PolkadotBulletin { const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_POLKADOT_BULLETIN_GRANDPA_PALLET_NAME; const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; - const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = - REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY; const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; } diff --git a/bridges/primitives/chain-polkadot/Cargo.toml b/bridges/chains/chain-polkadot/Cargo.toml similarity index 73% rename from bridges/primitives/chain-polkadot/Cargo.toml rename to bridges/chains/chain-polkadot/Cargo.toml index 6421b7f40106e404eeba04be72f5448fd5f65159..0db6791f66e0069bd7f5cf7dcd2585478fa26d0c 100644 --- a/bridges/primitives/chain-polkadot/Cargo.toml +++ b/bridges/chains/chain-polkadot/Cargo.toml @@ -13,9 +13,9 @@ workspace = true # Bridge Dependencies -bp-header-chain = { path = "../header-chain", default-features = false } -bp-polkadot-core = { path = "../polkadot-core", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } +bp-header-chain = { path = "../../primitives/header-chain", default-features = false } +bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } # Substrate Based Dependencies diff --git a/bridges/primitives/chain-polkadot/src/lib.rs b/bridges/chains/chain-polkadot/src/lib.rs similarity index 95% rename from bridges/primitives/chain-polkadot/src/lib.rs rename to bridges/chains/chain-polkadot/src/lib.rs index fc5e10308a8e33463a74c041f157daaef09cc9c8..00d35783a9b61844bab7701fdb60711125447ca3 100644 --- a/bridges/primitives/chain-polkadot/src/lib.rs +++ b/bridges/chains/chain-polkadot/src/lib.rs @@ -55,8 +55,8 @@ impl Chain for Polkadot { impl ChainWithGrandpa for Polkadot { const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_POLKADOT_GRANDPA_PALLET_NAME; const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; - const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = - REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY; const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; } diff --git a/bridges/primitives/chain-rococo/Cargo.toml b/bridges/chains/chain-rococo/Cargo.toml similarity index 73% rename from bridges/primitives/chain-rococo/Cargo.toml rename to bridges/chains/chain-rococo/Cargo.toml index de373f0ae64b8d9a8124dbcdfcca3c2bf05e2787..9c63f960ae496127f7c97fca03caed49c2500497 100644 --- a/bridges/primitives/chain-rococo/Cargo.toml +++ b/bridges/chains/chain-rococo/Cargo.toml @@ -13,9 +13,9 @@ workspace = true # Bridge Dependencies -bp-header-chain = { path = "../header-chain", default-features = false } -bp-polkadot-core = { path = "../polkadot-core", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } +bp-header-chain = { path = "../../primitives/header-chain", default-features = false } +bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } # Substrate Based Dependencies diff --git a/bridges/primitives/chain-rococo/src/lib.rs b/bridges/chains/chain-rococo/src/lib.rs similarity index 95% rename from bridges/primitives/chain-rococo/src/lib.rs rename to bridges/chains/chain-rococo/src/lib.rs index f1b256f0f090f048cc8db3a16c112ed8b938f6ce..2385dd2cbb250181ce5f46aef9f1e76f8fd010d2 100644 --- a/bridges/primitives/chain-rococo/src/lib.rs +++ b/bridges/chains/chain-rococo/src/lib.rs @@ -53,8 +53,8 @@ impl Chain for Rococo { impl ChainWithGrandpa for Rococo { const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_ROCOCO_GRANDPA_PALLET_NAME; const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; - const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = - REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY; const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; } diff --git a/bridges/primitives/chain-westend/Cargo.toml b/bridges/chains/chain-westend/Cargo.toml similarity index 73% rename from bridges/primitives/chain-westend/Cargo.toml rename to bridges/chains/chain-westend/Cargo.toml index e55a8d649a88b88d84f4f3547c23709cef67d872..f5de9b95c82c85abfe9b675d2d0aedd1fe89612b 100644 --- a/bridges/primitives/chain-westend/Cargo.toml +++ b/bridges/chains/chain-westend/Cargo.toml @@ -13,9 +13,9 @@ workspace = true # Bridge Dependencies -bp-header-chain = { path = "../header-chain", default-features = false } -bp-polkadot-core = { path = "../polkadot-core", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } +bp-header-chain = { path = "../../primitives/header-chain", default-features = false } +bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } # Substrate Based Dependencies diff --git a/bridges/primitives/chain-westend/src/lib.rs b/bridges/chains/chain-westend/src/lib.rs similarity index 95% rename from bridges/primitives/chain-westend/src/lib.rs rename to bridges/chains/chain-westend/src/lib.rs index f03fd2160a700eb3817a6feb629e9d366cc366aa..b344b7f4bf93392c08502446513a9ae39296b512 100644 --- a/bridges/primitives/chain-westend/src/lib.rs +++ b/bridges/chains/chain-westend/src/lib.rs @@ -53,8 +53,8 @@ impl Chain for Westend { impl ChainWithGrandpa for Westend { const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_WESTEND_GRANDPA_PALLET_NAME; const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; - const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = - REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY; const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; } diff --git a/bridges/docs/bridge-relayers-claim-rewards.png b/bridges/docs/bridge-relayers-claim-rewards.png new file mode 100644 index 0000000000000000000000000000000000000000..d56b8dd871e8445e7cab49517123b0092ce09137 Binary files /dev/null and b/bridges/docs/bridge-relayers-claim-rewards.png differ diff --git a/bridges/docs/bridge-relayers-deregister.png b/bridges/docs/bridge-relayers-deregister.png new file mode 100644 index 0000000000000000000000000000000000000000..e7706cee78916d7e2bbcfd7ee4a1a046a0450f87 Binary files /dev/null and b/bridges/docs/bridge-relayers-deregister.png differ diff --git a/bridges/docs/bridge-relayers-register.png b/bridges/docs/bridge-relayers-register.png new file mode 100644 index 0000000000000000000000000000000000000000..e9e3e1b5ac87c5c9d31477c696912fcbc93b0c78 Binary files /dev/null and b/bridges/docs/bridge-relayers-register.png differ diff --git a/bridges/docs/running-relayer.md b/bridges/docs/running-relayer.md new file mode 100644 index 0000000000000000000000000000000000000000..710810a476e4df5e4b80fde31f9576be5ad26391 --- /dev/null +++ b/bridges/docs/running-relayer.md @@ -0,0 +1,343 @@ +# Running your own bridge relayer + +:warning: :construction: Please read the [Disclaimer](#disclaimer) section first :construction: :warning: + +## Disclaimer + +There are several things you should know before running your own relayer: + +- initial bridge version (we call it bridges v1) supports any number of relayers, but **there's no guaranteed +compensation** for running a relayer and/or submitting valid bridge transactions. Most probably you'll end up +spending more funds than getting from rewards - please accept this fact; + +- even if your relayer has managed to submit a valid bridge transaction that has been included into the bridge +hub block, there's no guarantee that you will be able to claim your compensation for that transaction. That's +because compensations are paid from the account, controlled by relay chain governance and it could have no funds +to compensate your useful actions. We'll be working on a proper process to resupply it on-time, but we can't +provide any guarantee until that process is well established. + +## A Brief Introduction into Relayers and our Compensations Scheme + +Omitting details, relayer is an offchain process that is connected to both bridged chains. It looks at the +outbound bridge messages queue and submits message delivery transactions to the target chain. There's a lot +of details behind that simple phrase - you could find more info in the +[High-Level Bridge Overview](./high-level-overview.md) document. + +Reward that is paid to relayer has two parts. The first part static and is controlled by the governance. +It is rather small initially - e.g. you need to deliver `10_000` Kusama -> Polkadot messages to gain single +KSM token. + +The other reward part is dynamic. So to deliver an XCM message from one BridgeHub to another, we'll need to +submit two transactions on different chains. Every transaction has its cost, which is: + +- dynamic, because e.g. message size can change and/or fee factor of the target chain may change; + +- quite large, because those transactions are quite heavy (mostly in terms of size, not weight). + +We are compensating the cost of **valid**, **minimal** and **useful** bridge-related transactions to +relayer, that has submitted such transaction. Valid here means that the transaction doesn't fail. Minimal +means that all data within transaction call is actually required for the transaction to succeed. Useful +means that all supplied data in transaction is new and yet unknown to the target chain. + +We have implemented a relayer that is able to craft such transactions. The rest of document contains a detailed +information on how to deploy this software on your own node. + +## Relayers Concurrency + +As it has been said above, we are not compensating cost of transactions that are not **useful**. For +example, if message `100` has already been delivered from Kusama Bridge Hub to Polkadot Bridge Hub, then another +transaction that delivers the same message `100` won't be **useful**. Hence, no compensation to relayer that +has submitted that second transaction. + +But what if there are several relayers running? They are noticing the same queued message `100` and +simultaneously submit identical message delivery transactions. You may expect that there'll be one lucky +relayer, whose transaction would win the "race" and which will receive the compensation and reward. And +there'll be several other relayers, losing some funds on their unuseful transactions. + +But actually, we have a solution that invalidates transactions of "unlucky" relayers before they are +included into the block. So at least you may be sure that you won't waste your funds on duplicate transactions. + +
+Some details? + +All **unuseful** transactions are rejected by our +[transaction extension](https://github.com/paritytech/polkadot-sdk/blob/master/bridges/bin/runtime-common/src/refund_relayer_extension.rs), +which also handles transaction fee compensations. You may find more info on unuseful (aka obsolete) transactions +by lurking in the code. + +We also have the WiP prototype of relayers coordination protocol, where relayers will get some guarantee +that their transactions will be prioritized over other relayers transactions at their assigned slots. +That is planned for the future version of bridge and the progress is +[tracked here](https://github.com/paritytech/parity-bridges-common/issues/2486). + +
+ +## Prerequisites + +Let's focus on the bridge between Polkadot and Kusama Bridge Hubs. Let's also assume that we want to start +a relayer that "serves" an initial lane [`0x00000001`](https://github.com/polkadot-fellows/runtimes/blob/9ce1bbbbcd7843b3c76ba4d43c036bc311959e9f/system-parachains/bridge-hubs/bridge-hub-kusama/src/bridge_to_polkadot_config.rs#L54). + +
+Lane? + +Think of lane as a queue of messages that need to be delivered to the other/bridged chain. The lane is +bidirectional, meaning that there are four "endpoints". Two "outbound" endpoints (one at every chain), contain +messages that need to be delivered to the bridged chain. Two "inbound" are accepting messages from the bridged +chain and also remember the relayer, who has delivered message(s) to reward it later. + +
+ +The same steps may be performed for other lanes and bridges as well - you'll just need to change several parameters. + +So to start your relayer instance, you'll need to prepare: + +- an address of ws/wss RPC endpoint of the Kusama relay chain; + +- an address of ws/wss RPC endpoint of the Polkadot relay chain; + +- an address of ws/wss RPC endpoint of the Kusama Bridge Hub chain; + +- an address of ws/wss RPC endpoint of the Polkadot Bridge Hub chain; + +- an account on Kusama Bridge Hub; + +- an account on Polkadot Bridge Hub. + +For RPC endpoints, you could start your own nodes, or use some public community nodes. Nodes are not meant to be +archive or provide access to insecure RPC calls. + +To create an account on Bridge Hubs, you could use XCM teleport functionality. E.g. if you have an account on +the relay chain, you could use the `teleportAssets` call of `xcmPallet` and send asset +`V3 { id: Concrete(0, Here), Fungible: }` to beneficiary `V3(0, X1(AccountId32()))` +on destination `V3(0, X1(Parachain(1002)))`. To estimate amounts you need, please refer to the [Costs](#costs) +section of the document. + +## Registering your Relayer Account (Optional, But Please Read) + +Bridge transactions are quite heavy and expensive. We want to minimize block space that can be occupied by +invalid bridge transactions and prioritize valid transactions over invalid. That is achieved by **optional** +relayer registration. Transactions, signed by relayers with active registration, gain huge priority boost. +In exchange, such relayers may be slashed if they submit **invalid** or **non-minimal** transaction. + +Transactions, signed by relayers **without** active registration, on the other hand, receive no priority +boost. It means that if there is active registered relayer, most likely all transactions from unregistered +will be counted as **unuseful**, not included into the block and unregistered relayer won't get any reward +for his operations. + +Before registering, you should know several things about your funds: + +- to register, you need to hold significant amount of funds on your relayer account. As of now, it is + [100 KSM](https://github.com/polkadot-fellows/runtimes/blob/9ce1bbbbcd7843b3c76ba4d43c036bc311959e9f/system-parachains/bridge-hubs/bridge-hub-kusama/src/bridge_to_polkadot_config.rs#L71C14-L71C43) + for registration on Kusama Bridge Hub and + [500 DOT](https://github.com/polkadot-fellows/runtimes/blob/9ce1bbbbcd7843b3c76ba4d43c036bc311959e9f/system-parachains/bridge-hubs/bridge-hub-polkadot/src/bridge_to_kusama_config.rs#L71C14-L71C43) + for registration on Polkadot Bridge Hub; + +- when you are registered, those funds are reserved on relayer account and you can't transfer them. + +The registration itself, has three states: active, inactive or expired. Initially, it is active, meaning that all +your transactions that are **validated** on top of block, where it is active get priority boost. Registration +becomes expired when the block with the number you have specified during registration is "mined". It is the +`validTill` parameter of the `register` call (see below). After that `validTill` block, you may unregister and get +your reserved funds back. There's also an intermediate point between those blocks - it is the `validTill - LEASE`, +where `LEASE` is the the chain constant, controlled by the governance. Initially it is set to `300` blocks. +All your transactions, **validated** between the `validTill - LEASE` and `validTill` blocks do not get the +priority boost. Also, it is forbidden to specify `validTill` such that the `validTill - currentBlock` is less +than the `LEASE`. + +
+Example? + +| Bridge Hub Block | Registration State | Comment | +| ----------------- | ------------------ | ------------------------------------------------------ | +| 100 | Active | You have submitted a tx with the `register(1000)` call | +| 101 | Active | Your message delivery transactions are boosted | +| 102 | Active | Your message delivery transactions are boosted | +| ... | Active | Your message delivery transactions are boosted | +| 700 | Inactive | Your message delivery transactions are not boosted | +| 701 | Inactive | Your message delivery transactions are not boosted | +| ... | Inactive | Your message delivery transactions are not boosted | +| 1000 | Expired | Your may submit a tx with the `deregister` call | + +
+ +So once you have enough funds on your account and have selected the `validTill` parameter value, you +could use the Polkadot JS apps to submit an extrinsic. If you want priority boost for your transactions +on the Kusama Bridge Hub, open the +[Polkadot JS Apps](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fkusama-bridge-hub-rpc.polkadot.io#/extrinsics) +and submit the `register` extrinsic from the `bridgeRelayers` pallet: + +![Register Extrinsic](./bridge-relayers-register.png) + +To deregister, submit the simple `deregister` extrinsic when registration is expired: + +![Deregister Extrinsic](./bridge-relayers-deregister.png) + +At any time, you can prolong your registration by calling the `register` with the larger `validTill`. + +## Costs + +Your relayer account (on both Bridge Hubs) must hold enough funds to be able to pay costs of bridge +transactions. If your relayer behaves correctly, those costs will be compensated and you will be +able to claim it later. + +**IMPORTANT**: you may add tip to your bridge transactions to boost their priority. But our +compensation mechanism never refunds transaction tip, so all tip tokens will be lost. + +
+Types of bridge transactions + +There are two types of bridge transactions: + +- message delivery transaction brings queued message(s) from one Bridge Hub to another. We record + the fact that this specific (your) relayer has delivered those messages; + +- message confirmation transaction confirms that some message have been delivered and also brings + back information on how many messages (your) relayer has delivered. We use this information later + to register delivery rewards on the source chain. + +Several messages/confirmations may be included in a single bridge transaction. Apart from this +data, bridge transaction may include finality and storage proofs, required to prove authenticity of +this data. + +
+ +To deliver and get reward for a single message, the relayer needs to submit two transactions. One +at the source Bridge Hub and one at the target Bridge Hub. Below are costs for Polkadot <> Kusama +messages (as of today): + +- to deliver a single Polkadot -> Kusama message, you would need to pay around `0.06 KSM` at Kusama + Bridge Hub and around `1.62 DOT` at Polkadot Bridge Hub; + +- to deliver a single Kusama -> Polkadot message, you would need to pay around `1.70 DOT` at Polkadot + Bridge Hub and around `0.05 KSM` at Kusama Bridge Hub. + +Those values are not constants - they depend on call weights (that may change from release to release), +on transaction sizes (that depends on message size and chain state) and congestion factor. In any +case - it is your duty to make sure that the relayer has enough funds to pay transaction fees. + +## Claiming your Compensations and Rewards + +Hopefully you have successfully delivered some messages and now can claim your compensation and reward. +This requires submitting several transactions. But first, let's check that you actually have something to +claim. For that, let's check the state of the pallet that tracks all rewards. + +To check your rewards at the Kusama Bridge Hub, go to the +[Polkadot JS Apps](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fkusama-bridge-hub-rpc.polkadot.io#/chainstate) +targeting Kusama Bridge Hub, select the `bridgeRelayers` pallet, choose `relayerRewards` map and +your relayer account. Then: + +- set the `laneId` to `0x00000001` + +- set the `bridgedChainId` to `bhpd`; + +- check the both variants of the `owner` field: `ThisChain` is used to pay for message delivery transactions + and `BridgedChain` is used to pay for message confirmation transactions. + +If check shows that you have some rewards, you can craft the claim transaction, with similar parameters. +For that, go to `Extrinsics` tab of the +[Polkadot JS Apps](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fkusama-bridge-hub-rpc.polkadot.io#/extrinsics) +and submit the following transaction (make sure to change `owner` before): + +![Claim Rewards Extrinsic](./bridge-relayers-claim-rewards.png) + +To claim rewards on Polkadot Bridge Hub you can follow the same process. The only difference is that you +need to set value of the `bridgedChainId` to `bhks`. + +## Starting your Relayer + +### Starting your Rococo <> Westend Relayer + +You may find the relayer image reference in the +[Releases](https://github.com/paritytech/parity-bridges-common/releases) +of this repository. Make sure to check supported (bundled) versions +of release there. For Rococo <> Westend bridge, normally you may use the +latest published release. The release notes always contain the docker +image reference and source files, required to build relayer manually. + +Once you have the docker image, update variables and run the following script: +```sh +export DOCKER_IMAGE= + +export ROCOCO_HOST= +export ROCOCO_PORT= +# or set it to '--rococo-secure' if wss is used above +export ROCOCO_IS_SECURE= +export BRIDGE_HUB_ROCOCO_HOST= +export BRIDGE_HUB_ROCOCO_PORT= +# or set it to '--bridge-hub-rococo-secure' if wss is used above +export BRIDGE_HUB_ROCOCO_IS_SECURE= +export BRIDGE_HUB_ROCOCO_KEY_FILE= + +export WESTEND_HOST= +export WESTEND_PORT= +# or set it to '--westend-secure' if wss is used above +export WESTEND_IS_SECURE= +export BRIDGE_HUB_WESTEND_HOST= +export BRIDGE_HUB_WESTEND_PORT= +# or set it to '--bridge-hub-westend-secure ' if wss is used above +export BRIDGE_HUB_WESTEND_IS_SECURE= +export BRIDGE_HUB_WESTEND_KEY_FILE= + +# you can get extended relay logs (e.g. for debugging issues) by passing `-e RUST_LOG=bridge=trace` +# argument to the `docker` binary +docker run \ + -v $BRIDGE_HUB_ROCOCO_KEY_FILE:/bhr.key \ + -v $BRIDGE_HUB_WESTEND_KEY_FILE:/bhw.key \ + $DOCKER_IMAGE \ + relay-headers-and-messages bridge-hub-rococo-bridge-hub-westend \ + --rococo-host $ROCOCO_HOST \ + --rococo-port $ROCOCO_PORT \ + $ROCOCO_IS_SECURE \ + --rococo-version-mode Auto \ + --bridge-hub-rococo-host $BRIDGE_HUB_ROCOCO_HOST \ + --bridge-hub-rococo-port $BRIDGE_HUB_ROCOCO_PORT \ + $BRIDGE_HUB_ROCOCO_IS_SECURE \ + --bridge-hub-rococo-version-mode Auto \ + --bridge-hub-rococo-signer-file /bhr.key \ + --bridge-hub-rococo-transactions-mortality 16 \ + --westend-host $WESTEND_HOST \ + --westend-port $WESTEND_PORT \ + $WESTEND_IS_SECURE \ + --westend-version-mode Auto \ + --bridge-hub-westend-host $BRIDGE_HUB_WESTEND_HOST \ + --bridge-hub-westend-port $BRIDGE_HUB_WESTEND_PORT \ + $BRIDGE_HUB_WESTEND_IS_SECURE \ + --bridge-hub-westend-version-mode Auto \ + --bridge-hub-westend-signer-file /bhw.key \ + --bridge-hub-westend-transactions-mortality 16 \ + --lane 00000002 +``` + +### Starting your Polkadot <> Kusama Relayer + +*Work in progress, coming soon* + +### Watching your relayer state + +Our relayer provides some Prometheus metrics that you may convert into some fancy Grafana dashboards +and alerts. By default, metrics are exposed at port `9616`. To expose endpoint to the localhost, change +the docker command by adding following two lines: + +```sh +docker run \ + .. + -p 127.0.0.1:9616:9616 \ # tell Docker to bind container port 9616 to host port 9616 + # and listen for connections on the host' localhost interface + .. + $DOCKER_IMAGE \ + relay-headers-and-messages bridge-hub-rococo-bridge-hub-westend \ + --prometheus-host 0.0.0.0 \ # tell `substrate-relay` binary to accept Prometheus endpoint + # connections from everywhere + .. +``` + +You can find more info on configuring Prometheus and Grafana in the +[Monitor your node](https://wiki.polkadot.network/docs/maintain-guides-how-to-monitor-your-node) +guide from Polkadot wiki. + +We have our own set of Grafana dashboards and alerts. You may use them for inspiration. +Please find them in this folder: + +- for Rococo <> Westend bridge: [rococo-westend](https://github.com/paritytech/parity-bridges-common/tree/master/deployments/bridges/rococo-westend). + +- for Polkadot <> Kusama bridge: *work in progress, coming soon* diff --git a/bridges/modules/grandpa/Cargo.toml b/bridges/modules/grandpa/Cargo.toml index dccd7b3bdca3533cda4fec82ed0266d0b221b7a7..25c6c4e03d5580fd14f7e900f1efc44bc68102dc 100644 --- a/bridges/modules/grandpa/Cargo.toml +++ b/bridges/modules/grandpa/Cargo.toml @@ -15,7 +15,7 @@ workspace = true codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } finality-grandpa = { version = "0.16.2", default-features = false } log = { workspace = true } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.11.0", default-features = false, features = ["derive"] } # Bridge Dependencies diff --git a/bridges/modules/grandpa/README.md b/bridges/modules/grandpa/README.md index 43ee5c316d1b76ec8fc94b0c3819b1340a6ce75c..4a3099b8afc654bfced296aaa0ead4a5d113eb7f 100644 --- a/bridges/modules/grandpa/README.md +++ b/bridges/modules/grandpa/README.md @@ -10,7 +10,7 @@ It is used by the parachains light client (bridge parachains pallet) and by mess ## A Brief Introduction into GRANDPA Finality You can find detailed information on GRANDPA, by exploring its [repository](https://github.com/paritytech/finality-grandpa). -Here is the minimal reqiuired GRANDPA information to understand how pallet works. +Here is the minimal required GRANDPA information to understand how pallet works. Any Substrate chain may use different block authorship algorithms (like BABE or Aura) to determine block producers and generate blocks. This has nothing common with finality, though - the task of block authorship is to coordinate @@ -27,7 +27,7 @@ for provided header. There are two main things in GRANDPA that help building light clients: - there's no need to import all headers of the bridged chain. Light client may import finalized headers or just - some of finalized headders that it consider useful. While the validators set stays the same, the client may + some of finalized headers that it consider useful. While the validators set stays the same, the client may import any header that is finalized by this set; - when validators set changes, the GRANDPA gadget adds next set to the header. So light client doesn't need to diff --git a/bridges/modules/grandpa/src/call_ext.rs b/bridges/modules/grandpa/src/call_ext.rs index e3c778b480baa51a8b9e5d04564ac54bc7a68a21..4a7ebb3cc8d42d7cb9d97d5c6990bb33658416bd 100644 --- a/bridges/modules/grandpa/src/call_ext.rs +++ b/bridges/modules/grandpa/src/call_ext.rs @@ -205,7 +205,7 @@ pub(crate) fn submit_finality_proof_info_from_args, I: 'static>( // as an extra weight. let votes_ancestries_len = justification.votes_ancestries.len().saturated_into(); let extra_weight = - if votes_ancestries_len > T::BridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY { + if votes_ancestries_len > T::BridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY { T::WeightInfo::submit_finality_proof(precommits_len, votes_ancestries_len) } else { Weight::zero() @@ -396,11 +396,11 @@ mod tests { let finality_target = test_header(1); let mut justification_params = JustificationGeneratorParams { header: finality_target.clone(), - ancestors: TestBridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY, + ancestors: TestBridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY, ..Default::default() }; - // when there are `REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY` headers => no refund + // when there are `REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY` headers => no refund let justification = make_justification_for_header(justification_params.clone()); let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex { finality_target: Box::new(finality_target.clone()), @@ -409,7 +409,7 @@ mod tests { }); assert_eq!(call.submit_finality_proof_info().unwrap().extra_weight, Weight::zero()); - // when there are `REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY + 1` headers => full refund + // when there are `REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY + 1` headers => full refund justification_params.ancestors += 1; let justification = make_justification_for_header(justification_params); let call_weight = ::WeightInfo::submit_finality_proof( diff --git a/bridges/modules/grandpa/src/lib.rs b/bridges/modules/grandpa/src/lib.rs index ce2c47da954fa46efc4c70e9608864735fa16277..9e095651ef81da1e5418d7532ae56ae0fb8ef564 100644 --- a/bridges/modules/grandpa/src/lib.rs +++ b/bridges/modules/grandpa/src/lib.rs @@ -935,7 +935,7 @@ mod tests { } #[test] - fn succesfully_imports_header_with_valid_finality() { + fn successfully_imports_header_with_valid_finality() { run_test(|| { initialize_substrate_bridge(); @@ -1192,7 +1192,7 @@ mod tests { header.digest = change_log(0); let justification = make_justification_for_header(JustificationGeneratorParams { header: header.clone(), - ancestors: TestBridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY + 1, + ancestors: TestBridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY + 1, ..Default::default() }); diff --git a/bridges/modules/grandpa/src/mock.rs b/bridges/modules/grandpa/src/mock.rs index e41e89341b312eb252bddce6e918e8367a5ce27f..e689e520c92ffcb230a83f7a728722a688729417 100644 --- a/bridges/modules/grandpa/src/mock.rs +++ b/bridges/modules/grandpa/src/mock.rs @@ -42,7 +42,7 @@ construct_runtime! { } } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for TestRuntime { type Block = Block; } @@ -87,7 +87,7 @@ impl Chain for TestBridgedChain { impl ChainWithGrandpa for TestBridgedChain { const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; const MAX_AUTHORITIES_COUNT: u32 = MAX_BRIDGED_AUTHORITIES; - const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; + const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8; const MAX_MANDATORY_HEADER_SIZE: u32 = 256; const AVERAGE_HEADER_SIZE: u32 = 64; } diff --git a/bridges/modules/messages/Cargo.toml b/bridges/modules/messages/Cargo.toml index 173d6f1c16448517b7051cfba2f96625ff3d525a..7d0e1b94959ee96951b237f0e829a7ae52ace916 100644 --- a/bridges/modules/messages/Cargo.toml +++ b/bridges/modules/messages/Cargo.toml @@ -13,7 +13,7 @@ workspace = true codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } log = { workspace = true } num-traits = { version = "0.2", default-features = false } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.11.0", default-features = false, features = ["derive"] } # Bridge dependencies diff --git a/bridges/modules/messages/src/inbound_lane.rs b/bridges/modules/messages/src/inbound_lane.rs index 966ec939e70e22e830ee30157d2d7da74d59733c..da1698e6e0370f9f84ca8dd53bc1ebc99f696017 100644 --- a/bridges/modules/messages/src/inbound_lane.rs +++ b/bridges/modules/messages/src/inbound_lane.rs @@ -21,7 +21,7 @@ use crate::Config; use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, DeliveredMessages, InboundLaneData, LaneId, MessageKey, MessageNonce, OutboundLaneData, - ReceivalResult, UnrewardedRelayer, + ReceptionResult, UnrewardedRelayer, }; use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; use frame_support::traits::Get; @@ -170,21 +170,21 @@ impl InboundLane { relayer_at_bridged_chain: &S::Relayer, nonce: MessageNonce, message_data: DispatchMessageData, - ) -> ReceivalResult { + ) -> ReceptionResult { let mut data = self.storage.get_or_init_data(); if Some(nonce) != data.last_delivered_nonce().checked_add(1) { - return ReceivalResult::InvalidNonce + return ReceptionResult::InvalidNonce } // if there are more unrewarded relayer entries than we may accept, reject this message if data.relayers.len() as MessageNonce >= self.storage.max_unrewarded_relayer_entries() { - return ReceivalResult::TooManyUnrewardedRelayers + return ReceptionResult::TooManyUnrewardedRelayers } // if there are more unconfirmed messages than we may accept, reject this message let unconfirmed_messages_count = nonce.saturating_sub(data.last_confirmed_nonce); if unconfirmed_messages_count > self.storage.max_unconfirmed_messages() { - return ReceivalResult::TooManyUnconfirmedMessages + return ReceptionResult::TooManyUnconfirmedMessages } // then, dispatch message @@ -207,7 +207,7 @@ impl InboundLane { }; self.storage.set_data(data); - ReceivalResult::Dispatched(dispatch_result) + ReceptionResult::Dispatched(dispatch_result) } } @@ -235,7 +235,7 @@ mod tests { nonce, inbound_message_data(REGULAR_PAYLOAD) ), - ReceivalResult::Dispatched(dispatch_result(0)) + ReceptionResult::Dispatched(dispatch_result(0)) ); } @@ -362,7 +362,7 @@ mod tests { 10, inbound_message_data(REGULAR_PAYLOAD) ), - ReceivalResult::InvalidNonce + ReceptionResult::InvalidNonce ); assert_eq!(lane.storage.get_or_init_data().last_delivered_nonce(), 0); }); @@ -381,7 +381,7 @@ mod tests { current_nonce, inbound_message_data(REGULAR_PAYLOAD) ), - ReceivalResult::Dispatched(dispatch_result(0)) + ReceptionResult::Dispatched(dispatch_result(0)) ); } // Fails to dispatch new message from different than latest relayer. @@ -391,7 +391,7 @@ mod tests { max_nonce + 1, inbound_message_data(REGULAR_PAYLOAD) ), - ReceivalResult::TooManyUnrewardedRelayers, + ReceptionResult::TooManyUnrewardedRelayers, ); // Fails to dispatch new messages from latest relayer. Prevents griefing attacks. assert_eq!( @@ -400,7 +400,7 @@ mod tests { max_nonce + 1, inbound_message_data(REGULAR_PAYLOAD) ), - ReceivalResult::TooManyUnrewardedRelayers, + ReceptionResult::TooManyUnrewardedRelayers, ); }); } @@ -417,7 +417,7 @@ mod tests { current_nonce, inbound_message_data(REGULAR_PAYLOAD) ), - ReceivalResult::Dispatched(dispatch_result(0)) + ReceptionResult::Dispatched(dispatch_result(0)) ); } // Fails to dispatch new message from different than latest relayer. @@ -427,7 +427,7 @@ mod tests { max_nonce + 1, inbound_message_data(REGULAR_PAYLOAD) ), - ReceivalResult::TooManyUnconfirmedMessages, + ReceptionResult::TooManyUnconfirmedMessages, ); // Fails to dispatch new messages from latest relayer. assert_eq!( @@ -436,7 +436,7 @@ mod tests { max_nonce + 1, inbound_message_data(REGULAR_PAYLOAD) ), - ReceivalResult::TooManyUnconfirmedMessages, + ReceptionResult::TooManyUnconfirmedMessages, ); }); } @@ -451,7 +451,7 @@ mod tests { 1, inbound_message_data(REGULAR_PAYLOAD) ), - ReceivalResult::Dispatched(dispatch_result(0)) + ReceptionResult::Dispatched(dispatch_result(0)) ); assert_eq!( lane.receive_message::( @@ -459,7 +459,7 @@ mod tests { 2, inbound_message_data(REGULAR_PAYLOAD) ), - ReceivalResult::Dispatched(dispatch_result(0)) + ReceptionResult::Dispatched(dispatch_result(0)) ); assert_eq!( lane.receive_message::( @@ -467,7 +467,7 @@ mod tests { 3, inbound_message_data(REGULAR_PAYLOAD) ), - ReceivalResult::Dispatched(dispatch_result(0)) + ReceptionResult::Dispatched(dispatch_result(0)) ); assert_eq!( lane.storage.get_or_init_data().relayers, @@ -490,7 +490,7 @@ mod tests { 1, inbound_message_data(REGULAR_PAYLOAD) ), - ReceivalResult::Dispatched(dispatch_result(0)) + ReceptionResult::Dispatched(dispatch_result(0)) ); assert_eq!( lane.receive_message::( @@ -498,7 +498,7 @@ mod tests { 1, inbound_message_data(REGULAR_PAYLOAD) ), - ReceivalResult::InvalidNonce, + ReceptionResult::InvalidNonce, ); }); } @@ -524,7 +524,7 @@ mod tests { 1, inbound_message_data(payload) ), - ReceivalResult::Dispatched(dispatch_result(1)) + ReceptionResult::Dispatched(dispatch_result(1)) ); }); } diff --git a/bridges/modules/messages/src/lib.rs b/bridges/modules/messages/src/lib.rs index a86cb326cf0404512b7fe6ad0aa2a696ff7d0a47..bc00db9eba5ba12dbdaa0de7008f293a727a7ef5 100644 --- a/bridges/modules/messages/src/lib.rs +++ b/bridges/modules/messages/src/lib.rs @@ -47,7 +47,7 @@ pub use weights_ext::{ use crate::{ inbound_lane::{InboundLane, InboundLaneStorage}, - outbound_lane::{OutboundLane, OutboundLaneStorage, ReceivalConfirmationError}, + outbound_lane::{OutboundLane, OutboundLaneStorage, ReceptionConfirmationError}, }; use bp_messages::{ @@ -90,7 +90,7 @@ pub const LOG_TARGET: &str = "runtime::bridge-messages"; #[frame_support::pallet] pub mod pallet { use super::*; - use bp_messages::{ReceivalResult, ReceivedMessages}; + use bp_messages::{ReceivedMessages, ReceptionResult}; use bp_runtime::RangeInclusiveExt; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; @@ -376,13 +376,13 @@ pub mod pallet { // delivery transaction cost anyway. And base cost covers everything except // dispatch, so we have a balance here. let unspent_weight = match &receival_result { - ReceivalResult::Dispatched(dispatch_result) => { + ReceptionResult::Dispatched(dispatch_result) => { valid_messages += 1; dispatch_result.unspent_weight }, - ReceivalResult::InvalidNonce | - ReceivalResult::TooManyUnrewardedRelayers | - ReceivalResult::TooManyUnconfirmedMessages => message_dispatch_weight, + ReceptionResult::InvalidNonce | + ReceptionResult::TooManyUnrewardedRelayers | + ReceptionResult::TooManyUnconfirmedMessages => message_dispatch_weight, }; lane_messages_received_status.push(message.key.nonce, receival_result); @@ -455,7 +455,7 @@ pub mod pallet { last_delivered_nonce, &lane_data.relayers, ) - .map_err(Error::::ReceivalConfirmation)?; + .map_err(Error::::ReceptionConfirmation)?; if let Some(confirmed_messages) = confirmed_messages { // emit 'delivered' event @@ -563,7 +563,7 @@ pub mod pallet { /// The message someone is trying to work with (i.e. increase fee) is not yet sent. MessageIsNotYetSent, /// Error confirming messages receival. - ReceivalConfirmation(ReceivalConfirmationError), + ReceptionConfirmation(ReceptionConfirmationError), /// Error generated by the `OwnedBridgeModule` trait. BridgeModule(bp_runtime::OwnedBridgeModuleError), } @@ -923,7 +923,7 @@ mod tests { PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_LANE_ID_2, TEST_LANE_ID_3, TEST_RELAYER_A, TEST_RELAYER_B, }, - outbound_lane::ReceivalConfirmationError, + outbound_lane::ReceptionConfirmationError, }; use bp_messages::{ source_chain::MessagesBridge, BridgeMessagesCall, UnrewardedRelayer, @@ -950,11 +950,11 @@ mod tests { let outbound_lane = outbound_lane::(lane_id); let message_nonce = outbound_lane.data().latest_generated_nonce + 1; - let prev_enqueud_messages = outbound_lane.data().queued_messages().saturating_len(); + let prev_enqueued_messages = outbound_lane.data().queued_messages().saturating_len(); let valid_message = Pallet::::validate_message(lane_id, ®ULAR_PAYLOAD) .expect("validate_message has failed"); let artifacts = Pallet::::send_message(valid_message); - assert_eq!(artifacts.enqueued_messages, prev_enqueud_messages + 1); + assert_eq!(artifacts.enqueued_messages, prev_enqueued_messages + 1); // check event with assigned nonce assert_eq!( @@ -1541,7 +1541,7 @@ mod tests { } #[test] - fn actual_dispatch_weight_does_not_overlow() { + fn actual_dispatch_weight_does_not_overflow() { run_test(|| { let message1 = message(1, message_payload(0, u64::MAX / 2)); let message2 = message(2, message_payload(0, u64::MAX / 2)); @@ -1775,7 +1775,7 @@ mod tests { // returns `last_confirmed_nonce`; // 3) it means that we're going to confirm delivery of messages 1..=1; // 4) so the number of declared messages (see `UnrewardedRelayersState`) is `0` and - // numer of actually confirmed messages is `1`. + // number of actually confirmed messages is `1`. assert_noop!( Pallet::::receive_messages_delivery_proof( RuntimeOrigin::signed(1), @@ -1785,8 +1785,8 @@ mod tests { ))), UnrewardedRelayersState { last_delivered_nonce: 1, ..Default::default() }, ), - Error::::ReceivalConfirmation( - ReceivalConfirmationError::TryingToConfirmMoreMessagesThanExpected + Error::::ReceptionConfirmation( + ReceptionConfirmationError::TryingToConfirmMoreMessagesThanExpected ), ); }); diff --git a/bridges/modules/messages/src/mock.rs b/bridges/modules/messages/src/mock.rs index af92120539854347111d0562e284dc59e6e251d9..ec63f15b94b5205d744b1379bd6697a4ae43534a 100644 --- a/bridges/modules/messages/src/mock.rs +++ b/bridges/modules/messages/src/mock.rs @@ -77,14 +77,14 @@ frame_support::construct_runtime! { pub type DbWeight = RocksDbWeight; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for TestRuntime { type Block = Block; type AccountData = pallet_balances::AccountData; type DbWeight = DbWeight; } -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for TestRuntime { type ReserveIdentifier = [u8; 8]; type AccountStore = System; diff --git a/bridges/modules/messages/src/outbound_lane.rs b/bridges/modules/messages/src/outbound_lane.rs index 431c2cfb7eef3e8dd48e49c6ac37153ae64d57b6..acef5546d2a64fa8a3fb38c6b41ae30819cdeaa2 100644 --- a/bridges/modules/messages/src/outbound_lane.rs +++ b/bridges/modules/messages/src/outbound_lane.rs @@ -53,7 +53,7 @@ pub type StoredMessagePayload = BoundedVec>::MaximalOu /// Result of messages receival confirmation. #[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo)] -pub enum ReceivalConfirmationError { +pub enum ReceptionConfirmationError { /// Bridged chain is trying to confirm more messages than we have generated. May be a result /// of invalid bridged chain storage. FailedToConfirmFutureMessages, @@ -103,7 +103,7 @@ impl OutboundLane { max_allowed_messages: MessageNonce, latest_delivered_nonce: MessageNonce, relayers: &VecDeque>, - ) -> Result, ReceivalConfirmationError> { + ) -> Result, ReceptionConfirmationError> { let mut data = self.storage.data(); let confirmed_messages = DeliveredMessages { begin: data.latest_received_nonce.saturating_add(1), @@ -113,7 +113,7 @@ impl OutboundLane { return Ok(None) } if confirmed_messages.end > data.latest_generated_nonce { - return Err(ReceivalConfirmationError::FailedToConfirmFutureMessages) + return Err(ReceptionConfirmationError::FailedToConfirmFutureMessages) } if confirmed_messages.total_messages() > max_allowed_messages { // that the relayer has declared correct number of messages that the proof contains (it @@ -127,7 +127,7 @@ impl OutboundLane { confirmed_messages.total_messages(), max_allowed_messages, ); - return Err(ReceivalConfirmationError::TryingToConfirmMoreMessagesThanExpected) + return Err(ReceptionConfirmationError::TryingToConfirmMoreMessagesThanExpected) } ensure_unrewarded_relayers_are_correct(confirmed_messages.end, relayers)?; @@ -176,24 +176,24 @@ impl OutboundLane { fn ensure_unrewarded_relayers_are_correct( latest_received_nonce: MessageNonce, relayers: &VecDeque>, -) -> Result<(), ReceivalConfirmationError> { +) -> Result<(), ReceptionConfirmationError> { let mut expected_entry_begin = relayers.front().map(|entry| entry.messages.begin); for entry in relayers { // unrewarded relayer entry must have at least 1 unconfirmed message // (guaranteed by the `InboundLane::receive_message()`) if entry.messages.end < entry.messages.begin { - return Err(ReceivalConfirmationError::EmptyUnrewardedRelayerEntry) + return Err(ReceptionConfirmationError::EmptyUnrewardedRelayerEntry) } // every entry must confirm range of messages that follows previous entry range // (guaranteed by the `InboundLane::receive_message()`) if expected_entry_begin != Some(entry.messages.begin) { - return Err(ReceivalConfirmationError::NonConsecutiveUnrewardedRelayerEntries) + return Err(ReceptionConfirmationError::NonConsecutiveUnrewardedRelayerEntries) } expected_entry_begin = entry.messages.end.checked_add(1); // entry can't confirm messages larger than `inbound_lane_data.latest_received_nonce()` // (guaranteed by the `InboundLane::receive_message()`) if entry.messages.end > latest_received_nonce { - return Err(ReceivalConfirmationError::FailedToConfirmFutureMessages) + return Err(ReceptionConfirmationError::FailedToConfirmFutureMessages) } } @@ -228,7 +228,7 @@ mod tests { fn assert_3_messages_confirmation_fails( latest_received_nonce: MessageNonce, relayers: &VecDeque>, - ) -> Result, ReceivalConfirmationError> { + ) -> Result, ReceptionConfirmationError> { run_test(|| { let mut lane = outbound_lane::(TEST_LANE_ID); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); @@ -299,7 +299,7 @@ mod tests { fn confirm_delivery_rejects_nonce_larger_than_last_generated() { assert_eq!( assert_3_messages_confirmation_fails(10, &unrewarded_relayers(1..=10),), - Err(ReceivalConfirmationError::FailedToConfirmFutureMessages), + Err(ReceptionConfirmationError::FailedToConfirmFutureMessages), ); } @@ -314,7 +314,7 @@ mod tests { .chain(unrewarded_relayers(3..=3).into_iter()) .collect(), ), - Err(ReceivalConfirmationError::FailedToConfirmFutureMessages), + Err(ReceptionConfirmationError::FailedToConfirmFutureMessages), ); } @@ -330,7 +330,7 @@ mod tests { .chain(unrewarded_relayers(2..=3).into_iter()) .collect(), ), - Err(ReceivalConfirmationError::EmptyUnrewardedRelayerEntry), + Err(ReceptionConfirmationError::EmptyUnrewardedRelayerEntry), ); } @@ -345,7 +345,7 @@ mod tests { .chain(unrewarded_relayers(2..=2).into_iter()) .collect(), ), - Err(ReceivalConfirmationError::NonConsecutiveUnrewardedRelayerEntries), + Err(ReceptionConfirmationError::NonConsecutiveUnrewardedRelayerEntries), ); } @@ -409,11 +409,11 @@ mod tests { lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); assert_eq!( lane.confirm_delivery(0, 3, &unrewarded_relayers(1..=3)), - Err(ReceivalConfirmationError::TryingToConfirmMoreMessagesThanExpected), + Err(ReceptionConfirmationError::TryingToConfirmMoreMessagesThanExpected), ); assert_eq!( lane.confirm_delivery(2, 3, &unrewarded_relayers(1..=3)), - Err(ReceivalConfirmationError::TryingToConfirmMoreMessagesThanExpected), + Err(ReceptionConfirmationError::TryingToConfirmMoreMessagesThanExpected), ); assert_eq!( lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), diff --git a/bridges/modules/parachains/Cargo.toml b/bridges/modules/parachains/Cargo.toml index e454a6f2888fa169a0b0795101172b2f260b4020..a9dd9beeb1f1d89dc3d6829df934b1c72266af9e 100644 --- a/bridges/modules/parachains/Cargo.toml +++ b/bridges/modules/parachains/Cargo.toml @@ -12,7 +12,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } log = { workspace = true } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.11.0", default-features = false, features = ["derive"] } # Bridge Dependencies diff --git a/bridges/modules/parachains/src/mock.rs b/bridges/modules/parachains/src/mock.rs index 143f11d986371c4907f79fc4faf55143d3679034..d9cbabf850ec99ee13baa0f8bfc013b1192bd000 100644 --- a/bridges/modules/parachains/src/mock.rs +++ b/bridges/modules/parachains/src/mock.rs @@ -161,7 +161,7 @@ construct_runtime! { } } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for TestRuntime { type Block = Block; } @@ -261,7 +261,7 @@ impl Chain for TestBridgedChain { impl ChainWithGrandpa for TestBridgedChain { const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; const MAX_AUTHORITIES_COUNT: u32 = 16; - const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; + const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8; const MAX_MANDATORY_HEADER_SIZE: u32 = 256; const AVERAGE_HEADER_SIZE: u32 = 64; } @@ -294,7 +294,7 @@ impl Chain for OtherBridgedChain { impl ChainWithGrandpa for OtherBridgedChain { const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; const MAX_AUTHORITIES_COUNT: u32 = 16; - const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; + const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8; const MAX_MANDATORY_HEADER_SIZE: u32 = 256; const AVERAGE_HEADER_SIZE: u32 = 64; } diff --git a/bridges/modules/relayers/Cargo.toml b/bridges/modules/relayers/Cargo.toml index b78da5cbeeca65a4f448cbc38928894d51e8f7b4..f3de72da771606a7be6c9af35f95afa767ddc538 100644 --- a/bridges/modules/relayers/Cargo.toml +++ b/bridges/modules/relayers/Cargo.toml @@ -12,7 +12,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } log = { workspace = true } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.11.0", default-features = false, features = ["derive"] } # Bridge dependencies diff --git a/bridges/modules/relayers/src/mock.rs b/bridges/modules/relayers/src/mock.rs index 667b10e5c125ed74bad2aa7796756f372578c2ce..3124787896c3e1ee20014fc21b87ccbc19e6a2c2 100644 --- a/bridges/modules/relayers/src/mock.rs +++ b/bridges/modules/relayers/src/mock.rs @@ -59,14 +59,14 @@ parameter_types! { pub const Lease: BlockNumber = 8; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for TestRuntime { type Block = Block; type AccountData = pallet_balances::AccountData; type DbWeight = DbWeight; } -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for TestRuntime { type ReserveIdentifier = [u8; 8]; type AccountStore = System; diff --git a/bridges/modules/xcm-bridge-hub-router/Cargo.toml b/bridges/modules/xcm-bridge-hub-router/Cargo.toml index 20f8ff4407b2ad9882c64b334fa557a6c7dc4ef2..98477f2df1855fd18918b05b884f90d726661fb4 100644 --- a/bridges/modules/xcm-bridge-hub-router/Cargo.toml +++ b/bridges/modules/xcm-bridge-hub-router/Cargo.toml @@ -12,7 +12,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } log = { workspace = true } -scale-info = { version = "2.10.0", default-features = false, features = ["bit-vec", "derive", "serde"] } +scale-info = { version = "2.11.0", default-features = false, features = ["bit-vec", "derive", "serde"] } # Bridge dependencies diff --git a/bridges/modules/xcm-bridge-hub-router/src/lib.rs b/bridges/modules/xcm-bridge-hub-router/src/lib.rs index f219be78f9e1b5469fb752eed3f662c954d0ec42..5d0be41b1b5588e3ddc8c6306c9bf83ec29d6056 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/lib.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/lib.rs @@ -427,7 +427,7 @@ mod tests { run_test(|| { Bridge::::put(uncongested_bridge(FixedU128::from_rational(125, 100))); - // it shold eventually decreased to one + // it should eventually decreased to one while XcmBridgeHubRouter::bridge().delivery_fee_factor > MINIMAL_DELIVERY_FEE_FACTOR { XcmBridgeHubRouter::on_initialize(One::one()); } diff --git a/bridges/modules/xcm-bridge-hub-router/src/mock.rs b/bridges/modules/xcm-bridge-hub-router/src/mock.rs index 6dbfba5f6fdc1f521fb2fdf000ffb778740435e6..54e10966d51b23e7be5010b39cb9cb7d6a3b0118 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/mock.rs @@ -64,7 +64,7 @@ parameter_types! { pub UnknownXcmVersionLocation: Location = Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(9999)]); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for TestRuntime { type Block = Block; } diff --git a/bridges/modules/xcm-bridge-hub/Cargo.toml b/bridges/modules/xcm-bridge-hub/Cargo.toml index e10119e864953f1777c43151092ae43a5e594b8c..68ac32281f3db4c2abbee440d9d2406c4e113909 100644 --- a/bridges/modules/xcm-bridge-hub/Cargo.toml +++ b/bridges/modules/xcm-bridge-hub/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-xcm-bridge-hub" -description = "Module that adds dynamic bridges/lanes support to XCM infrastucture at the bridge hub." +description = "Module that adds dynamic bridges/lanes support to XCM infrastructure at the bridge hub." version = "0.2.0" authors.workspace = true edition.workspace = true @@ -12,7 +12,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } log = { workspace = true } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.11.0", default-features = false, features = ["derive"] } # Bridge Dependencies bp-messages = { path = "../../primitives/messages", default-features = false } diff --git a/bridges/modules/xcm-bridge-hub/src/mock.rs b/bridges/modules/xcm-bridge-hub/src/mock.rs index e40e1f9fb65157feffebeaa53e16c7def2ad22e0..4c09bce56d73eea717ad5149084e2ae337e48e87 100644 --- a/bridges/modules/xcm-bridge-hub/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub/src/mock.rs @@ -64,7 +64,7 @@ parameter_types! { pub const ExistentialDeposit: Balance = 1; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for TestRuntime { type AccountId = AccountId; type AccountData = pallet_balances::AccountData; @@ -72,7 +72,7 @@ impl frame_system::Config for TestRuntime { type Lookup = IdentityLookup; } -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for TestRuntime { type AccountStore = System; } diff --git a/bridges/primitives/header-chain/Cargo.toml b/bridges/primitives/header-chain/Cargo.toml index 205b593365ef8216a2e501e5751303185d4f7537..d96a02efba8a080cc6fdac4ab7e8f77474ea6ad2 100644 --- a/bridges/primitives/header-chain/Cargo.toml +++ b/bridges/primitives/header-chain/Cargo.toml @@ -12,7 +12,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } finality-grandpa = { version = "0.16.2", default-features = false } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.11.0", default-features = false, features = ["derive"] } serde = { features = ["alloc", "derive"], workspace = true } # Bridge dependencies diff --git a/bridges/primitives/header-chain/src/justification/mod.rs b/bridges/primitives/header-chain/src/justification/mod.rs index b32d8bdb5f1d8ce05722c938a083d7f582139835..d7c2cbf429e2b01efe4a9ea2481e66e2857d0044 100644 --- a/bridges/primitives/header-chain/src/justification/mod.rs +++ b/bridges/primitives/header-chain/src/justification/mod.rs @@ -83,7 +83,7 @@ impl GrandpaJustification { .saturating_add(HashOf::::max_encoded_len().saturated_into()); let max_expected_votes_ancestries_size = - C::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY.saturating_mul(C::AVERAGE_HEADER_SIZE); + C::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY.saturating_mul(C::AVERAGE_HEADER_SIZE); // justification is round number (u64=8b), a signed GRANDPA commit and the // `votes_ancestries` vector diff --git a/bridges/primitives/header-chain/src/justification/verification/mod.rs b/bridges/primitives/header-chain/src/justification/verification/mod.rs index c71149bf9c28e350fb43429623ca47cd367b9091..9df3511e1035ef769e5ef6c373253161be041efb 100644 --- a/bridges/primitives/header-chain/src/justification/verification/mod.rs +++ b/bridges/primitives/header-chain/src/justification/verification/mod.rs @@ -318,7 +318,7 @@ trait JustificationVerifier { } // check that the cumulative weight of validators that voted for the justification target - // (or one of its descendents) is larger than the required threshold. + // (or one of its descendants) is larger than the required threshold. if cumulative_weight < threshold { return Err(Error::TooLowCumulativeWeight) } diff --git a/bridges/primitives/header-chain/src/lib.rs b/bridges/primitives/header-chain/src/lib.rs index 84a6a881a835b8afc3b5cde8992df1733859d29a..98fb9ff83d8335fc04fbce7f9e566c73d15752a8 100644 --- a/bridges/primitives/header-chain/src/lib.rs +++ b/bridges/primitives/header-chain/src/lib.rs @@ -283,7 +283,7 @@ pub trait ChainWithGrandpa: Chain { /// ancestry and the pallet will accept such justification. The limit is only used to compute /// maximal refund amount and submitting justifications which exceed the limit, may be costly /// to submitter. - const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32; + const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32; /// Maximal size of the mandatory chain header. Mandatory header is the header that enacts new /// GRANDPA authorities set (so it has large digest inside). @@ -317,8 +317,8 @@ where const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ::WITH_CHAIN_GRANDPA_PALLET_NAME; const MAX_AUTHORITIES_COUNT: u32 = ::MAX_AUTHORITIES_COUNT; - const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = - ::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = + ::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY; const MAX_MANDATORY_HEADER_SIZE: u32 = ::MAX_MANDATORY_HEADER_SIZE; const AVERAGE_HEADER_SIZE: u32 = ::AVERAGE_HEADER_SIZE; @@ -373,7 +373,7 @@ mod tests { impl ChainWithGrandpa for TestChain { const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "Test"; const MAX_AUTHORITIES_COUNT: u32 = 128; - const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 2; + const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 2; const MAX_MANDATORY_HEADER_SIZE: u32 = 100_000; const AVERAGE_HEADER_SIZE: u32 = 1_024; } diff --git a/bridges/primitives/messages/Cargo.toml b/bridges/primitives/messages/Cargo.toml index 8aa6b4b05e5efb2427a8548e91ec5f47ab494968..9d742e3eded323053f4f59317f84a564e337019f 100644 --- a/bridges/primitives/messages/Cargo.toml +++ b/bridges/primitives/messages/Cargo.toml @@ -11,7 +11,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["bit-vec", "derive"] } -scale-info = { version = "2.10.0", default-features = false, features = ["bit-vec", "derive"] } +scale-info = { version = "2.11.0", default-features = false, features = ["bit-vec", "derive"] } serde = { features = ["alloc", "derive"], workspace = true } # Bridge dependencies diff --git a/bridges/primitives/messages/src/lib.rs b/bridges/primitives/messages/src/lib.rs index 51b3f25f7151867b52e8e5f49bc70b0a3632c05e..c3f79b3ee388c4584def56056f6cdf6328032e18 100644 --- a/bridges/primitives/messages/src/lib.rs +++ b/bridges/primitives/messages/src/lib.rs @@ -289,27 +289,27 @@ pub struct ReceivedMessages { /// Id of the lane which is receiving messages. pub lane: LaneId, /// Result of messages which we tried to dispatch - pub receive_results: Vec<(MessageNonce, ReceivalResult)>, + pub receive_results: Vec<(MessageNonce, ReceptionResult)>, } impl ReceivedMessages { /// Creates new `ReceivedMessages` structure from given results. pub fn new( lane: LaneId, - receive_results: Vec<(MessageNonce, ReceivalResult)>, + receive_results: Vec<(MessageNonce, ReceptionResult)>, ) -> Self { ReceivedMessages { lane, receive_results } } /// Push `result` of the `message` delivery onto `receive_results` vector. - pub fn push(&mut self, message: MessageNonce, result: ReceivalResult) { + pub fn push(&mut self, message: MessageNonce, result: ReceptionResult) { self.receive_results.push((message, result)); } } /// Result of single message receival. #[derive(RuntimeDebug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)] -pub enum ReceivalResult { +pub enum ReceptionResult { /// Message has been received and dispatched. Note that we don't care whether dispatch has /// been successful or not - in both case message falls into this category. /// diff --git a/bridges/primitives/parachains/Cargo.toml b/bridges/primitives/parachains/Cargo.toml index 575f26193eb68643c5c0a5fe6376d8b735ee2840..3846c5635756ad929f67d7b058cbe5b15e8af97b 100644 --- a/bridges/primitives/parachains/Cargo.toml +++ b/bridges/primitives/parachains/Cargo.toml @@ -12,7 +12,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } impl-trait-for-tuples = "0.2" -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.11.0", default-features = false, features = ["derive"] } # Bridge dependencies diff --git a/bridges/primitives/polkadot-core/Cargo.toml b/bridges/primitives/polkadot-core/Cargo.toml index c0dae684b5f2f3b7b9be096a808fc67d15dadfcf..5ab502569e438456e0a2b7f23f1ebeb270c60706 100644 --- a/bridges/primitives/polkadot-core/Cargo.toml +++ b/bridges/primitives/polkadot-core/Cargo.toml @@ -12,7 +12,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } parity-util-mem = { version = "0.12.0", optional = true } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.11.0", default-features = false, features = ["derive"] } serde = { optional = true, features = ["derive"], workspace = true, default-features = true } # Bridge Dependencies diff --git a/bridges/primitives/polkadot-core/src/lib.rs b/bridges/primitives/polkadot-core/src/lib.rs index df2836495bbe131e9cf810c43eb4af5eefaf43b7..e83be59b23890036905ae1abb441c454c2ce29a5 100644 --- a/bridges/primitives/polkadot-core/src/lib.rs +++ b/bridges/primitives/polkadot-core/src/lib.rs @@ -71,7 +71,7 @@ pub const MAX_AUTHORITIES_COUNT: u32 = 1_256; /// justifications with any additional headers in votes ancestry, so reasonable headers may /// be set to zero. But we assume that there may be small GRANDPA lags, so we're leaving some /// reserve here. -pub const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 2; +pub const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 2; /// Average header size in `votes_ancestries` field of justification on Polkadot-like /// chains. diff --git a/bridges/primitives/relayers/Cargo.toml b/bridges/primitives/relayers/Cargo.toml index 3bd6809d2789e0b3aced5b8d96448b63e1074ee4..71d0fbf2ec3a0accb97a42e0c326e7e24e81268c 100644 --- a/bridges/primitives/relayers/Cargo.toml +++ b/bridges/primitives/relayers/Cargo.toml @@ -11,7 +11,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["bit-vec", "derive"] } -scale-info = { version = "2.10.0", default-features = false, features = ["bit-vec", "derive"] } +scale-info = { version = "2.11.0", default-features = false, features = ["bit-vec", "derive"] } # Bridge Dependencies diff --git a/bridges/primitives/runtime/Cargo.toml b/bridges/primitives/runtime/Cargo.toml index 22206fb2c376ce53fee9dc8ff806baaef3ce7c28..2d454d264a134849f29eb6a5e1462f4640f861aa 100644 --- a/bridges/primitives/runtime/Cargo.toml +++ b/bridges/primitives/runtime/Cargo.toml @@ -15,7 +15,7 @@ hash-db = { version = "0.16.0", default-features = false } impl-trait-for-tuples = "0.2.2" log = { workspace = true } num-traits = { version = "0.2", default-features = false } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.11.0", default-features = false, features = ["derive"] } serde = { features = ["alloc", "derive"], workspace = true } # Substrate Dependencies diff --git a/bridges/primitives/runtime/src/chain.rs b/bridges/primitives/runtime/src/chain.rs index 9ba21a1cddf13896b21494045cea7fdd92259ce8..4ec5a001a99ecad21617ed0afc57d3edac383d0d 100644 --- a/bridges/primitives/runtime/src/chain.rs +++ b/bridges/primitives/runtime/src/chain.rs @@ -104,7 +104,7 @@ pub trait Chain: Send + Sync + 'static { const ID: ChainId; /// A type that fulfills the abstract idea of what a Substrate block number is. - // Constraits come from the associated Number type of `sp_runtime::traits::Header` + // Constraints come from the associated Number type of `sp_runtime::traits::Header` // See here for more info: // https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Number // @@ -125,7 +125,7 @@ pub trait Chain: Send + Sync + 'static { + MaxEncodedLen; /// A type that fulfills the abstract idea of what a Substrate hash is. - // Constraits come from the associated Hash type of `sp_runtime::traits::Header` + // Constraints come from the associated Hash type of `sp_runtime::traits::Header` // See here for more info: // https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Hash type Hash: Parameter @@ -143,7 +143,7 @@ pub trait Chain: Send + Sync + 'static { /// A type that fulfills the abstract idea of what a Substrate hasher (a type /// that produces hashes) is. - // Constraits come from the associated Hashing type of `sp_runtime::traits::Header` + // Constraints come from the associated Hashing type of `sp_runtime::traits::Header` // See here for more info: // https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Hashing type Hasher: HashT; diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs index 850318923dc7671c26cc3edcf2f9d59bd7b987b9..c9c5c9412913b0470024e9e1473e5d69ff184f25 100644 --- a/bridges/primitives/runtime/src/lib.rs +++ b/bridges/primitives/runtime/src/lib.rs @@ -56,7 +56,7 @@ mod chain; mod storage_proof; mod storage_types; -// Re-export macro to aviod include paste dependency everywhere +// Re-export macro to avoid include paste dependency everywhere pub use sp_runtime::paste; /// Use this when something must be shared among all instances. @@ -461,7 +461,7 @@ macro_rules! generate_static_str_provider { }; } -/// Error message that is only dispayable in `std` environment. +/// Error message that is only displayable in `std` environment. #[derive(Encode, Decode, Clone, Eq, PartialEq, PalletError, TypeInfo)] #[scale_info(skip_type_params(T))] pub struct StrippableError { diff --git a/bridges/primitives/test-utils/src/lib.rs b/bridges/primitives/test-utils/src/lib.rs index f23ddd1a10d3681900b024999aef279ea6fcb91d..f4fe4a242e79c0e1c8a499c4dd18ed4a2164c656 100644 --- a/bridges/primitives/test-utils/src/lib.rs +++ b/bridges/primitives/test-utils/src/lib.rs @@ -88,7 +88,7 @@ pub fn make_default_justification(header: &H) -> GrandpaJustificatio /// Generate justifications in a way where we are able to tune the number of pre-commits /// and vote ancestries which are included in the justification. /// -/// This is useful for benchmarkings where we want to generate valid justifications with +/// This is useful for benchmarks where we want to generate valid justifications with /// a specific number of pre-commits (tuned with the number of "authorities") and/or a specific /// number of vote ancestries (tuned with the "votes" parameter). /// @@ -129,7 +129,7 @@ pub fn make_justification_for_header( votes_ancestries.push(child.clone()); } - // The header we need to use when pre-commiting is the one at the highest height + // The header we need to use when pre-committing is the one at the highest height // on our chain. let precommit_candidate = chain.last().map(|h| (h.hash(), *h.number())).unwrap(); unsigned_precommits.push(precommit_candidate); diff --git a/bridges/primitives/xcm-bridge-hub-router/Cargo.toml b/bridges/primitives/xcm-bridge-hub-router/Cargo.toml index 9297a8603c0aa407e3dc5b860e21a0c227cf1bcc..734930f18c473ce39b9dd5dacc9a5d4b809bcb25 100644 --- a/bridges/primitives/xcm-bridge-hub-router/Cargo.toml +++ b/bridges/primitives/xcm-bridge-hub-router/Cargo.toml @@ -11,7 +11,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["bit-vec", "derive"] } -scale-info = { version = "2.10.0", default-features = false, features = ["bit-vec", "derive"] } +scale-info = { version = "2.11.0", default-features = false, features = ["bit-vec", "derive"] } # Substrate Dependencies sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } diff --git a/bridges/scripts/verify-pallets-build.sh b/bridges/scripts/verify-pallets-build.sh index b96bbf1833b6b3ce2bb34d2dc34aa5b8f54eb528..9c57a2a3c476006a636b4944648dfdf196de3591 100755 --- a/bridges/scripts/verify-pallets-build.sh +++ b/bridges/scripts/verify-pallets-build.sh @@ -68,6 +68,7 @@ rm -rf $BRIDGES_FOLDER/modules/beefy rm -rf $BRIDGES_FOLDER/modules/shift-session-manager rm -rf $BRIDGES_FOLDER/primitives/beefy rm -rf $BRIDGES_FOLDER/relays +rm -rf $BRIDGES_FOLDER/relay-clients rm -rf $BRIDGES_FOLDER/scripts/add_license.sh rm -rf $BRIDGES_FOLDER/scripts/build-containers.sh rm -rf $BRIDGES_FOLDER/scripts/ci-cache.sh @@ -77,6 +78,7 @@ rm -rf $BRIDGES_FOLDER/scripts/regenerate_runtimes.sh rm -rf $BRIDGES_FOLDER/scripts/update-weights.sh rm -rf $BRIDGES_FOLDER/scripts/update-weights-setup.sh rm -rf $BRIDGES_FOLDER/scripts/update_substrate.sh +rm -rf $BRIDGES_FOLDER/substrate-relay rm -rf $BRIDGES_FOLDER/tools rm -f $BRIDGES_FOLDER/.dockerignore rm -f $BRIDGES_FOLDER/local.Dockerfile.dockerignore @@ -89,6 +91,7 @@ rm -f $BRIDGES_FOLDER/local.Dockerfile rm -f $BRIDGES_FOLDER/CODEOWNERS rm -f $BRIDGES_FOLDER/Dockerfile rm -f $BRIDGES_FOLDER/rustfmt.toml +rm -f $BRIDGES_FOLDER/RELEASE.md # let's fix Cargo.toml a bit (it'll be helpful if we are in the bridges repo) if [[ ! -f "Cargo.toml" ]]; then @@ -131,7 +134,7 @@ cargo check -p bridge-runtime-common cargo check -p bridge-runtime-common --features runtime-benchmarks cargo check -p bridge-runtime-common --features integrity-test -# we're removing lock file after all chechs are done. Otherwise we may use different +# we're removing lock file after all checks are done. Otherwise we may use different # Substrate/Polkadot/Cumulus commits and our checks will fail rm -f $BRIDGES_FOLDER/Cargo.lock diff --git a/bridges/snowbridge/README.md b/bridges/snowbridge/README.md index 49b9c2eaf553780176897a770bad9579d53bfaa9..6561df401120e9c5c5d6ee2762eb1423b5d6daaf 100644 --- a/bridges/snowbridge/README.md +++ b/bridges/snowbridge/README.md @@ -1,32 +1,40 @@ -# Snowbridge -[![codecov](https://codecov.io/gh/Snowfork/snowbridge/branch/main/graph/badge.svg?token=9hvgSws4rN)](https://codecov.io/gh/Snowfork/snowbridge) +# Snowbridge · +[![codecov](https://codecov.io/gh/Snowfork/polkadot-sdk/branch/snowbridge/graph/badge.svg?token=9hvgSws4rN)](https://codecov.io/gh/Snowfork/polkadot-sdk) ![GitHub](https://img.shields.io/github/license/Snowfork/snowbridge) Snowbridge is a trustless bridge between Polkadot and Ethereum. For documentation, visit https://docs.snowbridge.network. ## Components +The Snowbridge project lives in two repositories: + +- [Snowfork/Polkadot-sdk](https://github.com/Snowfork/polkadot-sdk): The Snowbridge parachain and pallets live in +a fork of the Polkadot SDK. Changes are eventually contributed back to +[paritytech/Polkadot-sdk](https://github.com/paritytech/polkadot-sdk) +- [Snowfork/snowbridge](https://github.com/Snowfork/snowbridge): The rest of the Snowbridge components, like contracts, +off-chain relayer, end-to-end tests and test-net setup code. + ### Parachain -Polkadot parachain and our pallets. See [parachain/README.md](https://github.com/Snowfork/snowbridge/blob/main/parachain/README.md). +Polkadot parachain and our pallets. See [README.md](https://github.com/Snowfork/polkadot-sdk/blob/snowbridge/bridges/snowbridge/README.md). ### Contracts -Ethereum contracts and unit tests. See [contracts/README.md](https://github.com/Snowfork/snowbridge/blob/main/contracts/README.md) +Ethereum contracts and unit tests. See [Snowfork/snowbridge/contracts/README.md](https://github.com/Snowfork/snowbridge/blob/main/contracts/README.md) ### Relayer Off-chain relayer services for relaying messages between Polkadot and Ethereum. See -[relayer/README.md](https://github.com/Snowfork/snowbridge/blob/main/relayer/README.md) +[Snowfork/snowbridge/relayer/README.md](https://github.com/Snowfork/snowbridge/blob/main/relayer/README.md) ### Local Testnet Scripts to provision a local testnet, running the above services to bridge between local deployments of Polkadot and -Ethereum. See [web/packages/test/README.md](https://github.com/Snowfork/snowbridge/blob/main/web/packages/test/README.md). +Ethereum. See [Snowfork/snowbridge/web/packages/test/README.md](https://github.com/Snowfork/snowbridge/blob/main/web/packages/test/README.md). ### Smoke Tests -Integration tests for our local testnet. See [smoketest/README.md](https://github.com/Snowfork/snowbridge/blob/main/smoketest/README.md). +Integration tests for our local testnet. See [Snowfork/snowbridge/smoketest/README.md](https://github.com/Snowfork/snowbridge/blob/main/smoketest/README.md). ## Development @@ -83,7 +91,7 @@ direnv allow ### Upgrading the Rust toolchain -Sometimes we would like to upgrade rust toolchain. First update `parachain/rust-toolchain.toml` as required and then +Sometimes we would like to upgrade rust toolchain. First update `rust-toolchain.toml` as required and then update `flake.lock` running ```sh nix flake lock --update-input rust-overlay diff --git a/bridges/snowbridge/pallets/ethereum-client/Cargo.toml b/bridges/snowbridge/pallets/ethereum-client/Cargo.toml index 99b4290531145dba85521574a6d5004e7696cb08..cadd542432e775e273f5024928d2162f645a77e2 100644 --- a/bridges/snowbridge/pallets/ethereum-client/Cargo.toml +++ b/bridges/snowbridge/pallets/ethereum-client/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-pallet-ethereum-client" description = "Snowbridge Ethereum Client Pallet" -version = "0.0.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true @@ -36,7 +36,7 @@ sp-io = { path = "../../../../substrate/primitives/io", default-features = false snowbridge-core = { path = "../../primitives/core", default-features = false } snowbridge-ethereum = { path = "../../primitives/ethereum", default-features = false } -snowbridge-pallet-ethereum-client-fixtures = { path = "./fixtures", default-features = false, optional = true } +snowbridge-pallet-ethereum-client-fixtures = { path = "fixtures", default-features = false, optional = true } primitives = { package = "snowbridge-beacon-primitives", path = "../../primitives/beacon", default-features = false } static_assertions = { version = "1.1.0", default-features = false } bp-runtime = { path = "../../../primitives/runtime", default-features = false } @@ -48,7 +48,7 @@ sp-keyring = { path = "../../../../substrate/primitives/keyring" } serde_json = { workspace = true, default-features = true } hex-literal = "0.4.1" pallet-timestamp = { path = "../../../../substrate/frame/timestamp" } -snowbridge-pallet-ethereum-client-fixtures = { path = "./fixtures" } +snowbridge-pallet-ethereum-client-fixtures = { path = "fixtures" } sp-io = { path = "../../../../substrate/primitives/io" } serde = { workspace = true, default-features = true } diff --git a/bridges/snowbridge/pallets/ethereum-client/src/lib.rs b/bridges/snowbridge/pallets/ethereum-client/src/lib.rs index a54d4a05ac5840df4aa7b00389527a0ec94ee22d..f57f5199020935ce190414ec420f35db313e5040 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/lib.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/lib.rs @@ -10,7 +10,7 @@ //! //! * [`Call::force_checkpoint`]: Set the initial trusted consensus checkpoint. //! * [`Call::set_operating_mode`]: Set the operating mode of the pallet. Can be used to disable -//! processing of conensus updates. +//! processing of consensus updates. //! //! ## Consensus Updates //! @@ -130,6 +130,10 @@ pub mod pallet { InvalidExecutionHeaderProof, InvalidAncestryMerkleProof, InvalidBlockRootsRootMerkleProof, + /// The gap between the finalized headers is larger than the sync committee period, + /// rendering execution headers unprovable using ancestry proofs (blocks root size is + /// the same as the sync committee period slots). + InvalidFinalizedHeaderGap, HeaderNotFinalized, BlockBodyHashTreeRootFailed, HeaderHashTreeRootFailed, @@ -398,6 +402,17 @@ pub mod pallet { Error::::IrrelevantUpdate ); + // Verify the finalized header gap between the current finalized header and new imported + // header is not larger than the sync committee period, otherwise we cannot do + // ancestry proofs for execution headers in the gap. + ensure!( + latest_finalized_state + .slot + .saturating_add(config::SLOTS_PER_HISTORICAL_ROOT as u64) >= + update.finalized_header.slot, + Error::::InvalidFinalizedHeaderGap + ); + // Verify that the `finality_branch`, if present, confirms `finalized_header` to match // the finalized checkpoint root saved in the state of `attested_header`. let finalized_block_root: H256 = update diff --git a/bridges/snowbridge/pallets/ethereum-client/src/mock.rs b/bridges/snowbridge/pallets/ethereum-client/src/mock.rs index 3ce34eee191ac48fa95789e03d88e54ff017a2d7..799b14f4773e421a37b75d226d7829ce8bb7dcf6 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/mock.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/mock.rs @@ -95,7 +95,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } diff --git a/bridges/snowbridge/pallets/ethereum-client/src/tests.rs b/bridges/snowbridge/pallets/ethereum-client/src/tests.rs index 50b6a25c3428dba843303031018a4ef5e93cf696..20a184490fd702cf26eff0efcb91baddcf09cfd1 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/tests.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/tests.rs @@ -15,7 +15,7 @@ use crate::mock::{ pub use crate::mock::*; -use crate::config::{EPOCHS_PER_SYNC_COMMITTEE_PERIOD, SLOTS_PER_EPOCH}; +use crate::config::{EPOCHS_PER_SYNC_COMMITTEE_PERIOD, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT}; use frame_support::{assert_err, assert_noop, assert_ok}; use hex_literal::hex; use primitives::{ @@ -249,7 +249,7 @@ pub fn execution_header_pruning() { stored_hashes.push(hash); } - // We should have only stored upto `execution_header_prune_threshold` + // We should have only stored up to `execution_header_prune_threshold` assert_eq!( ExecutionHeaders::::iter().count() as u32, execution_header_prune_threshold @@ -884,6 +884,61 @@ fn submit_execution_header_not_finalized() { }); } +/// Check that a gap of more than 8192 slots between finalized headers is not allowed. +#[test] +fn submit_finalized_header_update_with_too_large_gap() { + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let update = Box::new(load_sync_committee_update_fixture()); + let mut next_update = Box::new(load_next_sync_committee_update_fixture()); + + // Adds 8193 slots, so that the next update is still in the next sync committee, but the + // gap between the finalized headers is more than 8192 slots. + let slot_with_large_gap = checkpoint.header.slot + SLOTS_PER_HISTORICAL_ROOT as u64 + 1; + + next_update.finalized_header.slot = slot_with_large_gap; + // Adding some slots to the attested header and signature slot since they need to be ahead + // of the finalized header. + next_update.attested_header.slot = slot_with_large_gap + 33; + next_update.signature_slot = slot_with_large_gap + 43; + + new_tester().execute_with(|| { + assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); + assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); + assert!(>::exists()); + assert_err!( + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone()), + Error::::InvalidFinalizedHeaderGap + ); + }); +} + +/// Check that a gap of 8192 slots between finalized headers is allowed. +#[test] +fn submit_finalized_header_update_with_gap_at_limit() { + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let update = Box::new(load_sync_committee_update_fixture()); + let mut next_update = Box::new(load_next_sync_committee_update_fixture()); + + next_update.finalized_header.slot = checkpoint.header.slot + SLOTS_PER_HISTORICAL_ROOT as u64; + // Adding some slots to the attested header and signature slot since they need to be ahead + // of the finalized header. + next_update.attested_header.slot = + checkpoint.header.slot + SLOTS_PER_HISTORICAL_ROOT as u64 + 33; + next_update.signature_slot = checkpoint.header.slot + SLOTS_PER_HISTORICAL_ROOT as u64 + 43; + + new_tester().execute_with(|| { + assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); + assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); + assert!(>::exists()); + assert_err!( + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone()), + // The test should pass the InvalidFinalizedHeaderGap check, and will fail at the + // next check, the merkle proof, because we changed the next_update slots. + Error::::InvalidHeaderMerkleProof + ); + }); +} + /* IMPLS */ #[test] diff --git a/bridges/snowbridge/pallets/inbound-queue/Cargo.toml b/bridges/snowbridge/pallets/inbound-queue/Cargo.toml index c26ef90a22dbeca766cb7064c1947b52c2e99794..9fc1f31fbf7c0d0e76c67ae126fddce66ca82b83 100644 --- a/bridges/snowbridge/pallets/inbound-queue/Cargo.toml +++ b/bridges/snowbridge/pallets/inbound-queue/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-pallet-inbound-queue" description = "Snowbridge Inbound Queue Pallet" -version = "0.0.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true @@ -42,7 +42,7 @@ snowbridge-core = { path = "../../primitives/core", default-features = false } snowbridge-ethereum = { path = "../../primitives/ethereum", default-features = false } snowbridge-router-primitives = { path = "../../primitives/router", default-features = false } snowbridge-beacon-primitives = { path = "../../primitives/beacon", default-features = false } -snowbridge-pallet-inbound-queue-fixtures = { path = "./fixtures", default-features = false, optional = true } +snowbridge-pallet-inbound-queue-fixtures = { path = "fixtures", default-features = false, optional = true } [dev-dependencies] frame-benchmarking = { path = "../../../../substrate/frame/benchmarking" } diff --git a/bridges/snowbridge/pallets/inbound-queue/fixtures/Cargo.toml b/bridges/snowbridge/pallets/inbound-queue/fixtures/Cargo.toml index 61f1421e056773c4f078390f9c48f7b8fa0420d3..64605a42f0d383d838429eb9b82b5f6cf238ab09 100644 --- a/bridges/snowbridge/pallets/inbound-queue/fixtures/Cargo.toml +++ b/bridges/snowbridge/pallets/inbound-queue/fixtures/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-pallet-inbound-queue-fixtures" description = "Snowbridge Inbound Queue Test Fixtures" -version = "0.9.0" +version = "0.10.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true diff --git a/bridges/snowbridge/pallets/inbound-queue/fixtures/src/register_token_with_insufficient_fee.rs b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/register_token_with_insufficient_fee.rs index 82ff2283101e331395b06dd14f3876076e71722e..dfda0b2b42780a98b55d4b8f882969078a22e57b 100644 --- a/bridges/snowbridge/pallets/inbound-queue/fixtures/src/register_token_with_insufficient_fee.rs +++ b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/register_token_with_insufficient_fee.rs @@ -9,7 +9,7 @@ use snowbridge_beacon_primitives::CompactExecutionHeader; use snowbridge_core::inbound::{Log, Message, Proof}; use sp_std::vec; -pub fn make_register_token_with_infufficient_fee_message() -> InboundQueueFixture { +pub fn make_register_token_with_insufficient_fee_message() -> InboundQueueFixture { InboundQueueFixture { execution_header: CompactExecutionHeader{ parent_hash: hex!("998e81dc6df788a920b67e058fbde0dc3f4ec6f11f3f7cd8c3148e6d99584885").into(), diff --git a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs index 110f611c6766020039bd1f73def900914da8cae2..39e9532ed321c1c6f83fe5e00886b3d7116f8aec 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs @@ -3,7 +3,7 @@ use super::*; use frame_support::{ - parameter_types, + derive_impl, parameter_types, traits::{ConstU128, ConstU32, Everything}, weights::IdentityFee, }; @@ -47,10 +47,9 @@ parameter_types! { type Balance = u128; +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = Everything; - type BlockWeights = (); - type BlockLength = (); type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type RuntimeTask = RuntimeTask; @@ -60,16 +59,8 @@ impl frame_system::Config for Test { type Lookup = IdentityLookup; type RuntimeEvent = RuntimeEvent; type BlockHashCount = BlockHashCount; - type DbWeight = (); - type Version = (); type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; type Nonce = u64; type Block = Block; } @@ -182,7 +173,8 @@ parameter_types! { pub Parameters: PricingParameters = PricingParameters { exchange_rate: FixedU128::from_rational(1, 400), fee_per_gas: gwei(20), - rewards: Rewards { local: DOT, remote: meth(1) } + rewards: Rewards { local: DOT, remote: meth(1) }, + multiplier: FixedU128::from_rational(1, 1), }; } diff --git a/bridges/snowbridge/pallets/outbound-queue/Cargo.toml b/bridges/snowbridge/pallets/outbound-queue/Cargo.toml index 40e57db4bbd9e5d6e32cee84e586a8858b2222cf..f16a28cb1e457d9ebfb7804fa013e5b57858f79e 100644 --- a/bridges/snowbridge/pallets/outbound-queue/Cargo.toml +++ b/bridges/snowbridge/pallets/outbound-queue/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-pallet-outbound-queue" description = "Snowbridge Outbound Queue Pallet" -version = "0.0.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true diff --git a/bridges/snowbridge/pallets/outbound-queue/merkle-tree/Cargo.toml b/bridges/snowbridge/pallets/outbound-queue/merkle-tree/Cargo.toml index c185d5af7062045f40946fcbd3c45cb62b932216..0606e9de33056c9dffae50befcc1da5e865dca44 100644 --- a/bridges/snowbridge/pallets/outbound-queue/merkle-tree/Cargo.toml +++ b/bridges/snowbridge/pallets/outbound-queue/merkle-tree/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-outbound-queue-merkle-tree" description = "Snowbridge Outbound Queue Merkle Tree" -version = "0.1.1" +version = "0.3.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true diff --git a/bridges/snowbridge/pallets/outbound-queue/runtime-api/Cargo.toml b/bridges/snowbridge/pallets/outbound-queue/runtime-api/Cargo.toml index 347b3bae493b7491790854be7a28f82386d2ee4b..cb68fd0a250a92e7f6a6693f3aebf1c8553308aa 100644 --- a/bridges/snowbridge/pallets/outbound-queue/runtime-api/Cargo.toml +++ b/bridges/snowbridge/pallets/outbound-queue/runtime-api/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-outbound-queue-runtime-api" description = "Snowbridge Outbound Queue Runtime API" -version = "0.0.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true diff --git a/bridges/snowbridge/pallets/outbound-queue/runtime-api/src/lib.rs b/bridges/snowbridge/pallets/outbound-queue/runtime-api/src/lib.rs index 51f46a7b49c8838eddf44d9d3ba18f07b57c5dcd..e6ddaa43935249603d0bee9f852c40e8091471d6 100644 --- a/bridges/snowbridge/pallets/outbound-queue/runtime-api/src/lib.rs +++ b/bridges/snowbridge/pallets/outbound-queue/runtime-api/src/lib.rs @@ -3,7 +3,10 @@ #![cfg_attr(not(feature = "std"), no_std)] use frame_support::traits::tokens::Balance as BalanceT; -use snowbridge_core::outbound::Message; +use snowbridge_core::{ + outbound::{Command, Fee}, + PricingParameters, +}; use snowbridge_outbound_queue_merkle_tree::MerkleProof; sp_api::decl_runtime_apis! { @@ -11,10 +14,10 @@ sp_api::decl_runtime_apis! { { /// Generate a merkle proof for a committed message identified by `leaf_index`. /// The merkle root is stored in the block header as a - /// `\[`sp_runtime::generic::DigestItem::Other`\]` + /// `sp_runtime::generic::DigestItem::Other` fn prove_message(leaf_index: u64) -> Option; - /// Calculate the delivery fee for `message` - fn calculate_fee(message: Message) -> Option; + /// Calculate the delivery fee for `command` + fn calculate_fee(command: Command, parameters: Option>) -> Fee; } } diff --git a/bridges/snowbridge/pallets/outbound-queue/src/api.rs b/bridges/snowbridge/pallets/outbound-queue/src/api.rs index 44d63f1e2d23f48f3d13d7834de5cde8d2c78dfc..b904819b1b186e176976b82a4fadd1ced0f05ed1 100644 --- a/bridges/snowbridge/pallets/outbound-queue/src/api.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/api.rs @@ -4,8 +4,12 @@ use crate::{Config, MessageLeaves}; use frame_support::storage::StorageStreamIter; -use snowbridge_core::outbound::{Message, SendMessage}; +use snowbridge_core::{ + outbound::{Command, Fee, GasMeter}, + PricingParameters, +}; use snowbridge_outbound_queue_merkle_tree::{merkle_proof, MerkleProof}; +use sp_core::Get; pub fn prove_message(leaf_index: u64) -> Option where @@ -19,12 +23,14 @@ where Some(proof) } -pub fn calculate_fee(message: Message) -> Option +pub fn calculate_fee( + command: Command, + parameters: Option>, +) -> Fee where T: Config, { - match crate::Pallet::::validate(&message) { - Ok((_, fees)) => Some(fees.total()), - _ => None, - } + let gas_used_at_most = T::GasMeter::maximum_gas_used_at_most(&command); + let parameters = parameters.unwrap_or(T::PricingParameters::get()); + crate::Pallet::::calculate_fee(gas_used_at_most, parameters) } diff --git a/bridges/snowbridge/pallets/outbound-queue/src/lib.rs b/bridges/snowbridge/pallets/outbound-queue/src/lib.rs index 9e949a4791a8a64d4c36f3f78628279c367939f8..9b9dbe854a5ee51e4182b39a617824dd02d0b3b5 100644 --- a/bridges/snowbridge/pallets/outbound-queue/src/lib.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/lib.rs @@ -47,24 +47,37 @@ //! consume on Ethereum. Using this upper bound, a final fee can be calculated. //! //! The fee calculation also requires the following parameters: -//! * ETH/DOT exchange rate -//! * Ether fee per unit of gas +//! * Average ETH/DOT exchange rate over some period +//! * Max fee per unit of gas that bridge is willing to refund relayers for //! //! By design, it is expected that governance should manually update these //! parameters every few weeks using the `set_pricing_parameters` extrinsic in the //! system pallet. //! +//! This is an interim measure. Once ETH/DOT liquidity pools are available in the Polkadot network, +//! we'll use them as a source of pricing info, subject to certain safeguards. +//! //! ## Fee Computation Function //! //! ```text //! LocalFee(Message) = WeightToFee(ProcessMessageWeight(Message)) -//! RemoteFee(Message) = MaxGasRequired(Message) * FeePerGas + Reward -//! Fee(Message) = LocalFee(Message) + (RemoteFee(Message) / Ratio("ETH/DOT")) +//! RemoteFee(Message) = MaxGasRequired(Message) * Params.MaxFeePerGas + Params.Reward +//! RemoteFeeAdjusted(Message) = Params.Multiplier * (RemoteFee(Message) / Params.Ratio("ETH/DOT")) +//! Fee(Message) = LocalFee(Message) + RemoteFeeAdjusted(Message) //! ``` //! -//! By design, the computed fee is always going to conservative, to cover worst-case -//! costs of dispatch on Ethereum. In future iterations of the design, we will optimize -//! this, or provide a mechanism to asynchronously refund a portion of collected fees. +//! By design, the computed fee includes a safety factor (the `Multiplier`) to cover +//! unfavourable fluctuations in the ETH/DOT exchange rate. +//! +//! ## Fee Settlement +//! +//! On the remote side, in the gateway contract, the relayer accrues +//! +//! ```text +//! Min(GasPrice, Message.MaxFeePerGas) * GasUsed() + Message.Reward +//! ``` +//! Or in plain english, relayers are refunded for gas consumption, using a +//! price that is a minimum of the actual gas price, or `Message.MaxFeePerGas`. //! //! # Extrinsics //! @@ -106,7 +119,7 @@ pub use snowbridge_outbound_queue_merkle_tree::MerkleProof; use sp_core::{H256, U256}; use sp_runtime::{ traits::{CheckedDiv, Hash}, - DigestItem, + DigestItem, Saturating, }; use sp_std::prelude::*; pub use types::{CommittedMessage, ProcessMessageOriginOf}; @@ -366,8 +379,9 @@ pub mod pallet { // downcast to u128 let fee: u128 = fee.try_into().defensive_unwrap_or(u128::MAX); - // convert to local currency + // multiply by multiplier and convert to local currency let fee = FixedU128::from_inner(fee) + .saturating_mul(params.multiplier) .checked_div(¶ms.exchange_rate) .expect("exchange rate is not zero; qed") .into_inner(); diff --git a/bridges/snowbridge/pallets/outbound-queue/src/mock.rs b/bridges/snowbridge/pallets/outbound-queue/src/mock.rs index dd8fee4e2ed08ec0f3090b765fa882b063a98300..67877a05c79a19ff64a121705f5b8f5a6356170c 100644 --- a/bridges/snowbridge/pallets/outbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/mock.rs @@ -3,7 +3,7 @@ use super::*; use frame_support::{ - parameter_types, + derive_impl, parameter_types, traits::{Everything, Hooks}, weights::IdentityFee, }; @@ -37,10 +37,9 @@ parameter_types! { pub const BlockHashCount: u64 = 250; } +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = Everything; - type BlockWeights = (); - type BlockLength = (); type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type RuntimeTask = RuntimeTask; @@ -50,16 +49,7 @@ impl frame_system::Config for Test { type Lookup = IdentityLookup; type RuntimeEvent = RuntimeEvent; type BlockHashCount = BlockHashCount; - type DbWeight = (); - type Version = (); type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; type Nonce = u64; type Block = Block; } @@ -87,7 +77,8 @@ parameter_types! { pub Parameters: PricingParameters = PricingParameters { exchange_rate: FixedU128::from_rational(1, 400), fee_per_gas: gwei(20), - rewards: Rewards { local: DOT, remote: meth(1) } + rewards: Rewards { local: DOT, remote: meth(1) }, + multiplier: FixedU128::from_rational(4, 3), }; } diff --git a/bridges/snowbridge/pallets/outbound-queue/src/test.rs b/bridges/snowbridge/pallets/outbound-queue/src/test.rs index 8ed4a318d68e99181e7f1e0793cc66e23829132a..4e9ea36e24bc1a4e8bb22677d55e7b8a55e6ccf9 100644 --- a/bridges/snowbridge/pallets/outbound-queue/src/test.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/test.rs @@ -268,28 +268,34 @@ fn encode_digest_item() { } #[test] -fn validate_messages_with_fees() { +fn test_calculate_fees_with_unit_multiplier() { new_tester().execute_with(|| { - let message = mock_message(1000); - let (_, fee) = OutboundQueue::validate(&message).unwrap(); + let gas_used: u64 = 250000; + let price_params: PricingParameters<::Balance> = PricingParameters { + exchange_rate: FixedU128::from_rational(1, 400), + fee_per_gas: 10000_u32.into(), + rewards: Rewards { local: 1_u32.into(), remote: 1_u32.into() }, + multiplier: FixedU128::from_rational(1, 1), + }; + let fee = OutboundQueue::calculate_fee(gas_used, price_params); assert_eq!(fee.local, 698000000); - assert_eq!(fee.remote, 2680000000000); + assert_eq!(fee.remote, 1000000); }); } #[test] -fn test_calculate_fees() { +fn test_calculate_fees_with_multiplier() { new_tester().execute_with(|| { let gas_used: u64 = 250000; - let illegal_price_params: PricingParameters<::Balance> = - PricingParameters { - exchange_rate: FixedU128::from_rational(1, 400), - fee_per_gas: 10000_u32.into(), - rewards: Rewards { local: 1_u32.into(), remote: 1_u32.into() }, - }; - let fee = OutboundQueue::calculate_fee(gas_used, illegal_price_params); + let price_params: PricingParameters<::Balance> = PricingParameters { + exchange_rate: FixedU128::from_rational(1, 400), + fee_per_gas: 10000_u32.into(), + rewards: Rewards { local: 1_u32.into(), remote: 1_u32.into() }, + multiplier: FixedU128::from_rational(4, 3), + }; + let fee = OutboundQueue::calculate_fee(gas_used, price_params); assert_eq!(fee.local, 698000000); - assert_eq!(fee.remote, 1000000); + assert_eq!(fee.remote, 1333333); }); } @@ -297,13 +303,13 @@ fn test_calculate_fees() { fn test_calculate_fees_with_valid_exchange_rate_but_remote_fee_calculated_as_zero() { new_tester().execute_with(|| { let gas_used: u64 = 250000; - let illegal_price_params: PricingParameters<::Balance> = - PricingParameters { - exchange_rate: FixedU128::from_rational(1, 1), - fee_per_gas: 1_u32.into(), - rewards: Rewards { local: 1_u32.into(), remote: 1_u32.into() }, - }; - let fee = OutboundQueue::calculate_fee(gas_used, illegal_price_params.clone()); + let price_params: PricingParameters<::Balance> = PricingParameters { + exchange_rate: FixedU128::from_rational(1, 1), + fee_per_gas: 1_u32.into(), + rewards: Rewards { local: 1_u32.into(), remote: 1_u32.into() }, + multiplier: FixedU128::from_rational(1, 1), + }; + let fee = OutboundQueue::calculate_fee(gas_used, price_params.clone()); assert_eq!(fee.local, 698000000); // Though none zero pricing params the remote fee calculated here is invalid // which should be avoided diff --git a/bridges/snowbridge/pallets/system/Cargo.toml b/bridges/snowbridge/pallets/system/Cargo.toml index f6c642e7376f7f068af1fe3c2cf9b11a2c50f2cc..5ad04290de044a2c8ed13aa092f5ea033aaafb97 100644 --- a/bridges/snowbridge/pallets/system/Cargo.toml +++ b/bridges/snowbridge/pallets/system/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-pallet-system" description = "Snowbridge System Pallet" -version = "0.0.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true diff --git a/bridges/snowbridge/pallets/system/runtime-api/Cargo.toml b/bridges/snowbridge/pallets/system/runtime-api/Cargo.toml index 355d2d29147f3cd84ae013363db874c9b9739b8e..eb02ae1db529730f51743e79a322e54db44fee51 100644 --- a/bridges/snowbridge/pallets/system/runtime-api/Cargo.toml +++ b/bridges/snowbridge/pallets/system/runtime-api/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-system-runtime-api" description = "Snowbridge System Runtime API" -version = "0.0.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index b7f38fb753d31bd67acb78174e175f90fc711175..39c73e3630e7b425e46252943a44d10bfe1cebc7 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -37,8 +37,6 @@ //! `force_update_channel` and extrinsics to manage agents and channels for system parachains. #![cfg_attr(not(feature = "std"), no_std)] -pub use pallet::*; - #[cfg(test)] mod mock; @@ -79,6 +77,8 @@ use xcm_executor::traits::ConvertLocation; #[cfg(feature = "runtime-benchmarks")] use frame_support::traits::OriginTrait; +pub use pallet::*; + pub type BalanceOf = <::Token as Inspect<::AccountId>>::Balance; pub type AccountIdOf = ::AccountId; @@ -159,6 +159,7 @@ pub mod pallet { type DefaultPricingParameters: Get>; /// Cost of delivering a message from Ethereum + #[pallet::constant] type InboundDeliveryCost: Get>; type WeightInfo: WeightInfo; @@ -334,6 +335,7 @@ pub mod pallet { let command = Command::SetPricingParameters { exchange_rate: params.exchange_rate.into(), delivery_cost: T::InboundDeliveryCost::get().saturated_into::(), + multiplier: params.multiplier.into(), }; Self::send(PRIMARY_GOVERNANCE_CHANNEL, command, PaysFee::::No)?; diff --git a/bridges/snowbridge/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs index edc3f141b0735d7439b120c51da836fb8a77bd04..0312456c98233a1e8538a741a2b19e0611aa670b 100644 --- a/bridges/snowbridge/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -2,8 +2,8 @@ // SPDX-FileCopyrightText: 2023 Snowfork use crate as snowbridge_system; use frame_support::{ - parameter_types, - traits::{tokens::fungible::Mutate, ConstU128, ConstU16, ConstU64, ConstU8}, + derive_impl, parameter_types, + traits::{tokens::fungible::Mutate, ConstU128, ConstU64, ConstU8}, weights::IdentityFee, PalletId, }; @@ -95,11 +95,9 @@ frame_support::construct_runtime!( } ); +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type RuntimeTask = RuntimeTask; @@ -109,15 +107,8 @@ impl frame_system::Config for Test { type Lookup = IdentityLookup; type RuntimeEvent = RuntimeEvent; type BlockHashCount = ConstU64<250>; - type Version = (); type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = ConstU16<42>; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; type Nonce = u64; type Block = Block; } @@ -202,7 +193,8 @@ parameter_types! { pub Parameters: PricingParameters = PricingParameters { exchange_rate: FixedU128::from_rational(1, 400), fee_per_gas: gwei(20), - rewards: Rewards { local: DOT, remote: meth(1) } + rewards: Rewards { local: DOT, remote: meth(1) }, + multiplier: FixedU128::from_rational(4, 3) }; pub const InboundDeliveryCost: u128 = 1_000_000_000; diff --git a/bridges/snowbridge/primitives/beacon/Cargo.toml b/bridges/snowbridge/primitives/beacon/Cargo.toml index e021bb01071471b56d44f4a430c9ae5b7bdca57d..d181fa1d3945a704a3d1e1e28fea67b7dea0ee15 100644 --- a/bridges/snowbridge/primitives/beacon/Cargo.toml +++ b/bridges/snowbridge/primitives/beacon/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-beacon-primitives" description = "Snowbridge Beacon Primitives" -version = "0.0.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true diff --git a/bridges/snowbridge/primitives/core/Cargo.toml b/bridges/snowbridge/primitives/core/Cargo.toml index 090c48c403fecf76b598abcc390ffa054a8281a3..9a299ad0ae92326a6d0bb0391baf81e6e5bad663 100644 --- a/bridges/snowbridge/primitives/core/Cargo.toml +++ b/bridges/snowbridge/primitives/core/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-core" description = "Snowbridge Core" -version = "0.0.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true diff --git a/bridges/snowbridge/primitives/core/src/outbound.rs b/bridges/snowbridge/primitives/core/src/outbound.rs index bce123878d3a456fc8b50f841cd64516a5d58dee..0ba0fdb61089e9bd77f162a87bcf7f78842bae5c 100644 --- a/bridges/snowbridge/primitives/core/src/outbound.rs +++ b/bridges/snowbridge/primitives/core/src/outbound.rs @@ -136,6 +136,8 @@ mod v1 { exchange_rate: UD60x18, // Cost of delivering a message from Ethereum to BridgeHub, in ROC/KSM/DOT delivery_cost: u128, + // Fee multiplier + multiplier: UD60x18, }, } @@ -203,10 +205,11 @@ mod v1 { Token::Uint(U256::from(*transfer_asset_xcm)), Token::Uint(*register_token), ])]), - Command::SetPricingParameters { exchange_rate, delivery_cost } => + Command::SetPricingParameters { exchange_rate, delivery_cost, multiplier } => ethabi::encode(&[Token::Tuple(vec![ Token::Uint(exchange_rate.clone().into_inner()), Token::Uint(U256::from(*delivery_cost)), + Token::Uint(multiplier.clone().into_inner()), ])]), } } @@ -273,7 +276,8 @@ mod v1 { } } -#[cfg_attr(feature = "std", derive(PartialEq, Debug))] +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +#[cfg_attr(feature = "std", derive(PartialEq))] /// Fee for delivering message pub struct Fee where @@ -346,12 +350,13 @@ pub trait GasMeter { /// the command within the message const MAXIMUM_BASE_GAS: u64; + /// Total gas consumed at most, including verification & dispatch fn maximum_gas_used_at_most(command: &Command) -> u64 { Self::MAXIMUM_BASE_GAS + Self::maximum_dispatch_gas_used_at_most(command) } - /// Measures the maximum amount of gas a command payload will require to dispatch, AFTER - /// validation & verification. + /// Measures the maximum amount of gas a command payload will require to *dispatch*, NOT + /// including validation & verification. fn maximum_dispatch_gas_used_at_most(command: &Command) -> u64; } diff --git a/bridges/snowbridge/primitives/core/src/pricing.rs b/bridges/snowbridge/primitives/core/src/pricing.rs index 33aeda6d15c4701ce4594b1e783d7aa69f84cc8e..0f392c7ad4bdde9c4bed812b995b039816c8977c 100644 --- a/bridges/snowbridge/primitives/core/src/pricing.rs +++ b/bridges/snowbridge/primitives/core/src/pricing.rs @@ -13,6 +13,8 @@ pub struct PricingParameters { pub rewards: Rewards, /// Ether (wei) fee per gas unit pub fee_per_gas: U256, + /// Fee multiplier + pub multiplier: FixedU128, } #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] @@ -43,6 +45,9 @@ where if self.rewards.remote.is_zero() { return Err(InvalidPricingParameters) } + if self.multiplier == FixedU128::zero() { + return Err(InvalidPricingParameters) + } Ok(()) } } diff --git a/bridges/snowbridge/primitives/ethereum/Cargo.toml b/bridges/snowbridge/primitives/ethereum/Cargo.toml index 399139cef3816a2f840f4937a7449034aea9638d..9fa725a6c0565a5f42847d89149878f8997d07a0 100644 --- a/bridges/snowbridge/primitives/ethereum/Cargo.toml +++ b/bridges/snowbridge/primitives/ethereum/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-ethereum" description = "Snowbridge Ethereum" -version = "0.1.0" +version = "0.3.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true diff --git a/bridges/snowbridge/primitives/router/Cargo.toml b/bridges/snowbridge/primitives/router/Cargo.toml index 750a2a73e6372c138cb4420d5f06f26d35b90ab4..ded773e0d38917b7834679b3e521dfbe9539e51b 100644 --- a/bridges/snowbridge/primitives/router/Cargo.toml +++ b/bridges/snowbridge/primitives/router/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-router-primitives" description = "Snowbridge Router Primitives" -version = "0.0.0" +version = "0.9.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true diff --git a/bridges/snowbridge/runtime/runtime-common/Cargo.toml b/bridges/snowbridge/runtime/runtime-common/Cargo.toml index d4c86f8aa750134df8dfbe22a277a872e77f9175..bf5e9a8832dcf48113d5f74a92a060687da2fe4e 100644 --- a/bridges/snowbridge/runtime/runtime-common/Cargo.toml +++ b/bridges/snowbridge/runtime/runtime-common/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-runtime-common" description = "Snowbridge Runtime Common" -version = "0.0.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true diff --git a/bridges/snowbridge/runtime/test-common/Cargo.toml b/bridges/snowbridge/runtime/test-common/Cargo.toml index ec4466b34265fd344e870f92f64eea014ad76b98..4e8b311cb97812bb94140aa02405b3a174064a8f 100644 --- a/bridges/snowbridge/runtime/test-common/Cargo.toml +++ b/bridges/snowbridge/runtime/test-common/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-runtime-test-common" description = "Snowbridge Runtime Tests" -version = "0.0.0" +version = "0.2.0" authors = ["Snowfork "] edition = "2021" license = "Apache-2.0" diff --git a/bridges/snowbridge/runtime/test-common/src/lib.rs b/bridges/snowbridge/runtime/test-common/src/lib.rs index c9bbce98e575d5e55015aa7814d8cd57a5c3a966..7455adf76170acefd50f06e8a40ef1c79028f49f 100644 --- a/bridges/snowbridge/runtime/test-common/src/lib.rs +++ b/bridges/snowbridge/runtime/test-common/src/lib.rs @@ -13,9 +13,9 @@ use parachains_runtimes_test_utils::{ }; use snowbridge_core::{ChannelId, ParaId}; use snowbridge_pallet_ethereum_client_fixtures::*; -use sp_core::H160; +use sp_core::{H160, U256}; use sp_keyring::AccountKeyring::*; -use sp_runtime::{traits::Header, AccountId32, SaturatedConversion, Saturating}; +use sp_runtime::{traits::Header, AccountId32, DigestItem, SaturatedConversion, Saturating}; use xcm::{ latest::prelude::*, v3::Error::{self, Barrier}, @@ -40,6 +40,7 @@ where } pub fn send_transfer_token_message( + ethereum_chain_id: u64, assethub_parachain_id: u32, weth_contract_address: H160, destination_address: H160, @@ -53,7 +54,8 @@ where + parachain_info::Config + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config - + snowbridge_pallet_outbound_queue::Config, + + snowbridge_pallet_outbound_queue::Config + + pallet_timestamp::Config, XcmConfig: xcm_executor::Config, { let assethub_parachain_location = Location::new(1, Parachain(assethub_parachain_id)); @@ -88,7 +90,7 @@ where WithdrawAsset(Assets::from(vec![fee.clone()])), BuyExecution { fees: fee, weight_limit: Unlimited }, ExportMessage { - network: Ethereum { chain_id: 11155111 }, + network: Ethereum { chain_id: ethereum_chain_id }, destination: Here, xcm: inner_xcm, }, @@ -106,6 +108,7 @@ where } pub fn send_transfer_token_message_success( + ethereum_chain_id: u64, collator_session_key: CollatorSessionKeys, runtime_para_id: u32, assethub_parachain_id: u32, @@ -125,7 +128,8 @@ pub fn send_transfer_token_message_success( + pallet_message_queue::Config + cumulus_pallet_parachain_system::Config + snowbridge_pallet_outbound_queue::Config - + snowbridge_pallet_system::Config, + + snowbridge_pallet_system::Config + + pallet_timestamp::Config, XcmConfig: xcm_executor::Config, ValidatorIdOf: From>, ::AccountId: From + AsRef<[u8]>, @@ -147,6 +151,7 @@ pub fn send_transfer_token_message_success( initial_fund::(assethub_parachain_id, 5_000_000_000_000); let outcome = send_transfer_token_message::( + ethereum_chain_id, assethub_parachain_id, weth_contract_address, destination_address, @@ -193,13 +198,104 @@ pub fn send_transfer_token_message_success( let digest = included_head.digest(); - //let digest = frame_system::Pallet::::digest(); let digest_items = digest.logs(); assert!(digest_items.len() == 1 && digest_items[0].as_other().is_some()); }); } +pub fn ethereum_outbound_queue_processes_messages_before_message_queue_works< + Runtime, + XcmConfig, + AllPalletsWithoutSystem, +>( + ethereum_chain_id: u64, + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + assethub_parachain_id: u32, + weth_contract_address: H160, + destination_address: H160, + fee_amount: u128, + snowbridge_pallet_outbound_queue: Box< + dyn Fn(Vec) -> Option>, + >, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + pallet_message_queue::Config + + cumulus_pallet_parachain_system::Config + + snowbridge_pallet_outbound_queue::Config + + snowbridge_pallet_system::Config + + pallet_timestamp::Config, + XcmConfig: xcm_executor::Config, + AllPalletsWithoutSystem: + OnInitialize> + OnFinalize>, + ValidatorIdOf: From>, + ::AccountId: From + AsRef<[u8]>, +{ + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build() + .execute_with(|| { + >::initialize( + runtime_para_id.into(), + assethub_parachain_id.into(), + ) + .unwrap(); + + // fund asset hub sovereign account enough so it can pay fees + initial_fund::(assethub_parachain_id, 5_000_000_000_000); + + let outcome = send_transfer_token_message::( + ethereum_chain_id, + assethub_parachain_id, + weth_contract_address, + destination_address, + fee_amount, + ); + + assert_ok!(outcome.ensure_complete()); + + // check events + let mut events = >::events() + .into_iter() + .filter_map(|e| snowbridge_pallet_outbound_queue(e.event.encode())); + assert!(events.any(|e| matches!( + e, + snowbridge_pallet_outbound_queue::Event::MessageQueued { .. } + ))); + + let next_block_number: U256 = >::block_number() + .saturating_add(BlockNumberFor::::from(1u32)) + .into(); + + let included_head = + RuntimeHelper::::run_to_block_with_finalize( + next_block_number.as_u32(), + ); + let digest = included_head.digest(); + let digest_items = digest.logs(); + + let mut found_outbound_digest = false; + for digest_item in digest_items { + match digest_item { + DigestItem::Other(_) => found_outbound_digest = true, + _ => {}, + } + } + + assert_eq!(found_outbound_digest, true); + }); +} + pub fn send_unpaid_transfer_token_message( + ethereum_chain_id: u64, collator_session_key: CollatorSessionKeys, runtime_para_id: u32, assethub_parachain_id: u32, @@ -213,7 +309,8 @@ pub fn send_unpaid_transfer_token_message( + parachain_info::Config + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config - + snowbridge_pallet_outbound_queue::Config, + + snowbridge_pallet_outbound_queue::Config + + pallet_timestamp::Config, XcmConfig: xcm_executor::Config, ValidatorIdOf: From>, { @@ -262,7 +359,7 @@ pub fn send_unpaid_transfer_token_message( let xcm = Xcm(vec![ UnpaidExecution { weight_limit: Unlimited, check_origin: None }, ExportMessage { - network: Ethereum { chain_id: 11155111 }, + network: Ethereum { chain_id: ethereum_chain_id }, destination: Here, xcm: inner_xcm, }, @@ -284,6 +381,7 @@ pub fn send_unpaid_transfer_token_message( #[allow(clippy::too_many_arguments)] pub fn send_transfer_token_message_failure( + ethereum_chain_id: u64, collator_session_key: CollatorSessionKeys, runtime_para_id: u32, assethub_parachain_id: u32, @@ -301,7 +399,8 @@ pub fn send_transfer_token_message_failure( + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config + snowbridge_pallet_outbound_queue::Config - + snowbridge_pallet_system::Config, + + snowbridge_pallet_system::Config + + pallet_timestamp::Config, XcmConfig: xcm_executor::Config, ValidatorIdOf: From>, { @@ -322,6 +421,7 @@ pub fn send_transfer_token_message_failure( initial_fund::(assethub_parachain_id, initial_amount); let outcome = send_transfer_token_message::( + ethereum_chain_id, assethub_parachain_id, weth_contract_address, destination_address, @@ -349,7 +449,8 @@ pub fn ethereum_extrinsic( + cumulus_pallet_parachain_system::Config + snowbridge_pallet_outbound_queue::Config + snowbridge_pallet_system::Config - + snowbridge_pallet_ethereum_client::Config, + + snowbridge_pallet_ethereum_client::Config + + pallet_timestamp::Config, ValidatorIdOf: From>, ::RuntimeCall: From>, @@ -430,7 +531,8 @@ pub fn ethereum_to_polkadot_message_extrinsics_work( + cumulus_pallet_parachain_system::Config + snowbridge_pallet_outbound_queue::Config + snowbridge_pallet_system::Config - + snowbridge_pallet_ethereum_client::Config, + + snowbridge_pallet_ethereum_client::Config + + pallet_timestamp::Config, ValidatorIdOf: From>, ::RuntimeCall: From>, diff --git a/bridges/snowbridge/scripts/contribute-upstream.sh b/bridges/snowbridge/scripts/contribute-upstream.sh new file mode 100755 index 0000000000000000000000000000000000000000..32005b770ecf44cb9af18c61f830243ed5287e68 --- /dev/null +++ b/bridges/snowbridge/scripts/contribute-upstream.sh @@ -0,0 +1,82 @@ +#!/bin/bash + +# A script to cleanup the Snowfork fork of the polkadot-sdk to contribute it upstream back to parity/polkadot-sdk +# ./bridges/snowbridge/scripts/contribute-upstream.sh + +# show CLI help +function show_help() { + set +x + echo " " + echo Error: $1 + echo "Usage:" + echo " ./bridges/snowbridge/scripts/contribute-upstream.sh Exit with code 0 if pallets code is well decoupled from the other code in the repo" + exit 1 +} + +if [[ -z "$1" ]]; then + echo "Please provide a branch name you would like your upstream branch to be named" + exit 1 +fi + +branch_name=$1 + +set -eux + +# let's avoid any restrictions on where this script can be called for - snowbridge repo may be +# plugged into any other repo folder. So the script (and other stuff that needs to be removed) +# may be located either in call dir, or one of it subdirs. +SNOWBRIDGE_FOLDER="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )/../" + +# Get the current Git branch name +current_branch=$(git rev-parse --abbrev-ref HEAD) + +if [ "$current_branch" = "$branch_name" ] || git branch | grep -q "$branch_name"; then + echo "Already on requested branch or branch exists, not creating." +else + git branch "$branch_name" +fi + +git checkout "$branch_name" + +# remove everything we think is not required for our needs +rm -rf rust-toolchain.toml +rm -rf codecov.yml +rm -rf $SNOWBRIDGE_FOLDER/.cargo +rm -rf $SNOWBRIDGE_FOLDER/.github +rm -rf $SNOWBRIDGE_FOLDER/SECURITY.md +rm -rf $SNOWBRIDGE_FOLDER/.gitignore +rm -rf $SNOWBRIDGE_FOLDER/rustfmt.toml +rm -rf $SNOWBRIDGE_FOLDER/templates +rm -rf $SNOWBRIDGE_FOLDER/pallets/ethereum-client/fuzz + +pushd $SNOWBRIDGE_FOLDER + +# let's test if everything we need compiles +cargo check -p snowbridge-pallet-ethereum-client +cargo check -p snowbridge-pallet-ethereum-client --features runtime-benchmarks +cargo check -p snowbridge-pallet-ethereum-client --features try-runtime +cargo check -p snowbridge-pallet-inbound-queue +cargo check -p snowbridge-pallet-inbound-queue --features runtime-benchmarks +cargo check -p snowbridge-pallet-inbound-queue --features try-runtime +cargo check -p snowbridge-pallet-outbound-queue +cargo check -p snowbridge-pallet-outbound-queue --features runtime-benchmarks +cargo check -p snowbridge-pallet-outbound-queue --features try-runtime +cargo check -p snowbridge-pallet-system +cargo check -p snowbridge-pallet-system --features runtime-benchmarks +cargo check -p snowbridge-pallet-system --features try-runtime + +# we're removing lock file after all checks are done. Otherwise we may use different +# Substrate/Polkadot/Cumulus commits and our checks will fail +rm -f $SNOWBRIDGE_FOLDER/Cargo.toml +rm -f $SNOWBRIDGE_FOLDER/Cargo.lock + +popd + +# Replace Parity's CI files, that we have overwritten in our fork, to run our own CI +rm -rf .github +git remote -v | grep -w parity || git remote add parity https://github.com/paritytech/polkadot-sdk +git fetch parity master +git checkout parity/master -- .github +git add -- .github + +echo "OK" diff --git a/bridges/snowbridge/scripts/verify-pallets-build.sh b/bridges/snowbridge/scripts/verify-pallets-build.sh deleted file mode 100755 index a62f48c84d4fd34731c20365a20097e086aa2c99..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/scripts/verify-pallets-build.sh +++ /dev/null @@ -1,116 +0,0 @@ -#!/bin/bash - -# A script to remove everything from snowbridge repository/subtree, except: -# -# - parachain -# - readme -# - license - -set -eu - -# show CLI help -function show_help() { - set +x - echo " " - echo Error: $1 - echo "Usage:" - echo " ./scripts/verify-pallets-build.sh Exit with code 0 if pallets code is well decoupled from the other code in the repo" - echo "Options:" - echo " --no-revert Leaves only runtime code on exit" - echo " --ignore-git-state Ignores git actual state" - exit 1 -} - -# parse CLI args -NO_REVERT= -IGNORE_GIT_STATE= -for i in "$@" -do - case $i in - --no-revert) - NO_REVERT=true - shift - ;; - --ignore-git-state) - IGNORE_GIT_STATE=true - shift - ;; - *) - show_help "Unknown option: $i" - ;; - esac -done - -# the script is able to work only on clean git copy, unless we want to ignore this check -[[ ! -z "${IGNORE_GIT_STATE}" ]] || [[ -z "$(git status --porcelain)" ]] || { echo >&2 "The git copy must be clean"; exit 1; } - -# let's avoid any restrictions on where this script can be called for - snowbridge repo may be -# plugged into any other repo folder. So the script (and other stuff that needs to be removed) -# may be located either in call dir, or one of it subdirs. -SNOWBRIDGE_FOLDER="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )/../.." - -# remove everything we think is not required for our needs -rm -rf $SNOWBRIDGE_FOLDER/.cargo -rm -rf $SNOWBRIDGE_FOLDER/.github -rm -rf $SNOWBRIDGE_FOLDER/contracts -rm -rf $SNOWBRIDGE_FOLDER/codecov.yml -rm -rf $SNOWBRIDGE_FOLDER/docs -rm -rf $SNOWBRIDGE_FOLDER/hooks -rm -rf $SNOWBRIDGE_FOLDER/relayer -rm -rf $SNOWBRIDGE_FOLDER/scripts -rm -rf $SNOWBRIDGE_FOLDER/SECURITY.md -rm -rf $SNOWBRIDGE_FOLDER/smoketest -rm -rf $SNOWBRIDGE_FOLDER/web -rm -rf $SNOWBRIDGE_FOLDER/.envrc-example -rm -rf $SNOWBRIDGE_FOLDER/.gitbook.yaml -rm -rf $SNOWBRIDGE_FOLDER/.gitignore -rm -rf $SNOWBRIDGE_FOLDER/.gitmodules -rm -rf $SNOWBRIDGE_FOLDER/_typos.toml -rm -rf $SNOWBRIDGE_FOLDER/_codecov.yml -rm -rf $SNOWBRIDGE_FOLDER/flake.lock -rm -rf $SNOWBRIDGE_FOLDER/flake.nix -rm -rf $SNOWBRIDGE_FOLDER/go.work -rm -rf $SNOWBRIDGE_FOLDER/go.work.sum -rm -rf $SNOWBRIDGE_FOLDER/polkadot-sdk -rm -rf $SNOWBRIDGE_FOLDER/rust-toolchain.toml -rm -rf $SNOWBRIDGE_FOLDER/parachain/rustfmt.toml -rm -rf $SNOWBRIDGE_FOLDER/parachain/.gitignore -rm -rf $SNOWBRIDGE_FOLDER/parachain/templates -rm -rf $SNOWBRIDGE_FOLDER/parachain/.cargo -rm -rf $SNOWBRIDGE_FOLDER/parachain/.config -rm -rf $SNOWBRIDGE_FOLDER/parachain/pallets/ethereum-client/fuzz - -cd bridges/snowbridge/parachain - -# fix polkadot-sdk paths in Cargo.toml files -find "." -name 'Cargo.toml' | while read -r file; do - replace=$(printf '../../' ) - if [[ "$(uname)" = "Darwin" ]] || [[ "$(uname)" = *BSD ]]; then - sed -i '' "s|polkadot-sdk/|$replace|g" "$file" - else - sed -i "s|polkadot-sdk/|$replace|g" "$file" - fi -done - -# let's test if everything we need compiles -cargo check -p snowbridge-pallet-ethereum-client -cargo check -p snowbridge-pallet-ethereum-client --features runtime-benchmarks -cargo check -p snowbridge-pallet-ethereum-client --features try-runtime -cargo check -p snowbridge-pallet-inbound-queue -cargo check -p snowbridge-pallet-inbound-queue --features runtime-benchmarks -cargo check -p snowbridge-pallet-inbound-queue --features try-runtime -cargo check -p snowbridge-pallet-outbound-queue -cargo check -p snowbridge-pallet-outbound-queue --features runtime-benchmarks -cargo check -p snowbridge-pallet-outbound-queue --features try-runtime -cargo check -p snowbridge-pallet-system -cargo check -p snowbridge-pallet-system --features runtime-benchmarks -cargo check -p snowbridge-pallet-system --features try-runtime - -cd - - -# we're removing lock file after all checks are done. Otherwise we may use different -# Substrate/Polkadot/Cumulus commits and our checks will fail -rm -f $SNOWBRIDGE_FOLDER/parachain/Cargo.toml -rm -f $SNOWBRIDGE_FOLDER/parachain/Cargo.lock - -echo "OK" diff --git a/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh b/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh index 84764cdaca38c6bd19eb964c6e8d4fd54c4379c4..66c9ddc037b8efb005d2239b174eb5710dddaf53 100755 --- a/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh +++ b/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh @@ -1,7 +1,7 @@ #!/bin/bash # import common functions -source "${BASH_SOURCE%/*}/../../utils/bridges.sh" +source "$FRAMEWORK_PATH/utils/bridges.sh" # Expected sovereign accounts. # @@ -24,12 +24,6 @@ source "${BASH_SOURCE%/*}/../../utils/bridges.sh" # &MultiLocation { parents: 2, interior: X1(GlobalConsensus(Rococo)) }).unwrap() # ).to_ss58check_with_version(42_u16.into()) # ); -# println!("GLOBAL_CONSENSUS_ROCOCO_ASSET_HUB_ROCOCO_1000_SOVEREIGN_ACCOUNT=\"{}\"", -# frame_support::sp_runtime::AccountId32::new( -# GlobalConsensusParachainConvertsFor::::convert_location( -# &MultiLocation { parents: 2, interior: X2(GlobalConsensus(Rococo), Parachain(1000)) }).unwrap() -# ).to_ss58check_with_version(42_u16.into()) -# ); # println!("ASSET_HUB_WESTEND_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_WESTEND=\"{}\"", # frame_support::sp_runtime::AccountId32::new( # SiblingParachainConvertsVia::::convert_location( @@ -44,12 +38,6 @@ source "${BASH_SOURCE%/*}/../../utils/bridges.sh" # &MultiLocation { parents: 2, interior: X1(GlobalConsensus(Westend)) }).unwrap() # ).to_ss58check_with_version(42_u16.into()) # ); -# println!("GLOBAL_CONSENSUS_WESTEND_ASSET_HUB_WESTEND_1000_SOVEREIGN_ACCOUNT=\"{}\"", -# frame_support::sp_runtime::AccountId32::new( -# GlobalConsensusParachainConvertsFor::::convert_location( -# &MultiLocation { parents: 2, interior: X2(GlobalConsensus(Westend), Parachain(1000)) }).unwrap() -# ).to_ss58check_with_version(42_u16.into()) -# ); # println!("ASSET_HUB_ROCOCO_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_ROCOCO=\"{}\"", # frame_support::sp_runtime::AccountId32::new( # SiblingParachainConvertsVia::::convert_location( @@ -58,10 +46,8 @@ source "${BASH_SOURCE%/*}/../../utils/bridges.sh" # ); # } GLOBAL_CONSENSUS_ROCOCO_SOVEREIGN_ACCOUNT="5GxRGwT8bU1JeBPTUXc7LEjZMxNrK8MyL2NJnkWFQJTQ4sii" -GLOBAL_CONSENSUS_ROCOCO_ASSET_HUB_ROCOCO_1000_SOVEREIGN_ACCOUNT="5CfNu7eH3SJvqqPt3aJh38T8dcFvhGzEohp9tsd41ANhXDnQ" ASSET_HUB_WESTEND_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_WESTEND="5Eg2fntNprdN3FgH4sfEaaZhYtddZQSQUqvYJ1f2mLtinVhV" GLOBAL_CONSENSUS_WESTEND_SOVEREIGN_ACCOUNT="5He2Qdztyxxa4GoagY6q1jaiLMmKy1gXS7PdZkhfj8ZG9hk5" -GLOBAL_CONSENSUS_WESTEND_ASSET_HUB_WESTEND_1000_SOVEREIGN_ACCOUNT="5GUD9X494SnhfBTNReHwhV1599McpyVrAqFY6WnTfVQVYNUM" ASSET_HUB_ROCOCO_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_ROCOCO="5Eg2fntNprdN3FgH4sfEaaZhYtddZQSQUqvYJ1f2mLtinVhV" # Expected sovereign accounts for rewards on BridgeHubs. @@ -201,12 +187,6 @@ case "$1" in "$GLOBAL_CONSENSUS_WESTEND_SOVEREIGN_ACCOUNT" \ 10000000000 \ true - # drip SA which holds reserves - transfer_balance \ - "ws://127.0.0.1:9910" \ - "//Alice" \ - "$GLOBAL_CONSENSUS_WESTEND_ASSET_HUB_WESTEND_1000_SOVEREIGN_ACCOUNT" \ - $((1000000000000 + 50000000000 * 20)) # HRMP open_hrmp_channels \ "ws://127.0.0.1:9942" \ @@ -266,12 +246,6 @@ case "$1" in "$GLOBAL_CONSENSUS_ROCOCO_SOVEREIGN_ACCOUNT" \ 10000000000 \ true - # drip SA which holds reserves - transfer_balance \ - "ws://127.0.0.1:9010" \ - "//Alice" \ - "$GLOBAL_CONSENSUS_ROCOCO_ASSET_HUB_ROCOCO_1000_SOVEREIGN_ACCOUNT" \ - $((1000000000000000 + 50000000000 * 20)) # HRMP open_hrmp_channels \ "ws://127.0.0.1:9945" \ @@ -319,6 +293,7 @@ case "$1" in $XCM_VERSION ;; reserve-transfer-assets-from-asset-hub-rococo-local) + amount=$2 ensure_polkadot_js_api # send ROCs to Alice account on AHW limited_reserve_transfer_assets \ @@ -326,11 +301,12 @@ case "$1" in "//Alice" \ "$(jq --null-input '{ "V3": { "parents": 2, "interior": { "X2": [ { "GlobalConsensus": "Westend" }, { "Parachain": 1000 } ] } } }')" \ "$(jq --null-input '{ "V3": { "parents": 0, "interior": { "X1": { "AccountId32": { "id": [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] } } } } }')" \ - "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 1, "interior": "Here" } }, "fun": { "Fungible": 5000000000000 } } ] }')" \ + "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 1, "interior": "Here" } }, "fun": { "Fungible": '$amount' } } ] }')" \ 0 \ "Unlimited" ;; withdraw-reserve-assets-from-asset-hub-rococo-local) + amount=$2 ensure_polkadot_js_api # send back only 100000000000 wrappedWNDs to Alice account on AHW limited_reserve_transfer_assets \ @@ -338,11 +314,12 @@ case "$1" in "//Alice" \ "$(jq --null-input '{ "V3": { "parents": 2, "interior": { "X2": [ { "GlobalConsensus": "Westend" }, { "Parachain": 1000 } ] } } }')" \ "$(jq --null-input '{ "V3": { "parents": 0, "interior": { "X1": { "AccountId32": { "id": [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] } } } } }')" \ - "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 2, "interior": { "X1": { "GlobalConsensus": "Westend" } } } }, "fun": { "Fungible": 3000000000000 } } ] }')" \ + "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 2, "interior": { "X1": { "GlobalConsensus": "Westend" } } } }, "fun": { "Fungible": '$amount' } } ] }')" \ 0 \ "Unlimited" ;; reserve-transfer-assets-from-asset-hub-westend-local) + amount=$2 ensure_polkadot_js_api # send WNDs to Alice account on AHR limited_reserve_transfer_assets \ @@ -350,11 +327,12 @@ case "$1" in "//Alice" \ "$(jq --null-input '{ "V3": { "parents": 2, "interior": { "X2": [ { "GlobalConsensus": "Rococo" }, { "Parachain": 1000 } ] } } }')" \ "$(jq --null-input '{ "V3": { "parents": 0, "interior": { "X1": { "AccountId32": { "id": [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] } } } } }')" \ - "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 1, "interior": "Here" } }, "fun": { "Fungible": 5000000000000 } } ] }')" \ + "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 1, "interior": "Here" } }, "fun": { "Fungible": '$amount' } } ] }')" \ 0 \ "Unlimited" ;; withdraw-reserve-assets-from-asset-hub-westend-local) + amount=$2 ensure_polkadot_js_api # send back only 100000000000 wrappedROCs to Alice account on AHR limited_reserve_transfer_assets \ @@ -362,7 +340,7 @@ case "$1" in "//Alice" \ "$(jq --null-input '{ "V3": { "parents": 2, "interior": { "X2": [ { "GlobalConsensus": "Rococo" }, { "Parachain": 1000 } ] } } }')" \ "$(jq --null-input '{ "V3": { "parents": 0, "interior": { "X1": { "AccountId32": { "id": [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] } } } } }')" \ - "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 2, "interior": { "X1": { "GlobalConsensus": "Rococo" } } } }, "fun": { "Fungible": 3000000000000 } } ] }')" \ + "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 2, "interior": { "X1": { "GlobalConsensus": "Rococo" } } } }, "fun": { "Fungible": '$amount' } } ] }')" \ 0 \ "Unlimited" ;; diff --git a/bridges/testing/environments/rococo-westend/helper.sh b/bridges/testing/environments/rococo-westend/helper.sh index 211a5b53b3d99ebef0e8c6a34cda48321badca9d..0a13ded213f5d3a0920cb466fc974c129e9ad79a 100755 --- a/bridges/testing/environments/rococo-westend/helper.sh +++ b/bridges/testing/environments/rococo-westend/helper.sh @@ -1,3 +1,3 @@ #!/bin/bash -$POLKADOT_SDK_PATH/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh "$@" +$ENV_PATH/bridges_rococo_westend.sh "$@" diff --git a/bridges/testing/environments/rococo-westend/rococo-init.zndsl b/bridges/testing/environments/rococo-westend/rococo-init.zndsl index 145f2df73a6ec4b4179b12f0edbad5a6b95a7cba..c913e4db31f49184eb8214fda4d525c3594b358b 100644 --- a/bridges/testing/environments/rococo-westend/rococo-init.zndsl +++ b/bridges/testing/environments/rococo-westend/rococo-init.zndsl @@ -1,8 +1,8 @@ -Description: User is able to transfer WND from Westend Asset Hub to Rococo Asset Hub and back +Description: Check if the HRMP channel between Rococo BH and Rococo AH was opened successfully Network: ./bridge_hub_rococo_local_network.toml Creds: config # ensure that initialization has completed -asset-hub-rococo-collator1: js-script ../../js-helpers/wait-hrmp-channel-opened.js with "1013" within 300 seconds +asset-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/wait-hrmp-channel-opened.js with "1013" within 300 seconds diff --git a/bridges/testing/environments/rococo-westend/rococo.zndsl b/bridges/testing/environments/rococo-westend/rococo.zndsl index bd8681af2196814405a7d96e55882f00cbc0dc02..a75286445a240eb302ac8fd23ae7fe707e6c0af3 100644 --- a/bridges/testing/environments/rococo-westend/rococo.zndsl +++ b/bridges/testing/environments/rococo-westend/rococo.zndsl @@ -1,7 +1,7 @@ -Description: User is able to transfer WND from Westend Asset Hub to Rococo Asset Hub and back +Description: Check if the with-Westend GRANDPA pallet was initialized at Rococo BH Network: ./bridge_hub_rococo_local_network.toml Creds: config -# relay is already started - let's wait until with-Westend GRANPDA pallet is initialized at Rococo -bridge-hub-rococo-collator1: js-script ../../js-helpers/best-finalized-header-at-bridged-chain.js with "Westend,0" within 400 seconds +# relay is already started - let's wait until with-Westend GRANDPA pallet is initialized at Rococo +bridge-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/best-finalized-header-at-bridged-chain.js with "Westend,0" within 400 seconds diff --git a/bridges/testing/environments/rococo-westend/spawn.sh b/bridges/testing/environments/rococo-westend/spawn.sh index 5a0d65ce65dba1e5649920a419b7e9fa3da00367..cbd0b1bc623ab77876ed5ce3beefd7ab72db2d37 100755 --- a/bridges/testing/environments/rococo-westend/spawn.sh +++ b/bridges/testing/environments/rococo-westend/spawn.sh @@ -4,7 +4,7 @@ set -e trap "trap - SIGTERM && kill -9 -$$" SIGINT SIGTERM EXIT -source "${BASH_SOURCE%/*}/../../utils/zombienet.sh" +source "$FRAMEWORK_PATH/utils/zombienet.sh" # whether to init the chains (open HRMP channels, set XCM version, create reserve assets, etc) init=0 diff --git a/bridges/testing/environments/rococo-westend/start_relayer.sh b/bridges/testing/environments/rococo-westend/start_relayer.sh index c57d4f1a437493afd627efb989cba50222a3653c..7ddd312d395aa8733d2afea59277b48721c8a36b 100755 --- a/bridges/testing/environments/rococo-westend/start_relayer.sh +++ b/bridges/testing/environments/rococo-westend/start_relayer.sh @@ -2,8 +2,8 @@ set -e -source "${BASH_SOURCE%/*}/../../utils/common.sh" -source "${BASH_SOURCE%/*}/../../utils/zombienet.sh" +source "$FRAMEWORK_PATH/utils/common.sh" +source "$FRAMEWORK_PATH/utils/zombienet.sh" rococo_dir=$1 westend_dir=$2 diff --git a/bridges/testing/environments/rococo-westend/westend-init.zndsl b/bridges/testing/environments/rococo-westend/westend-init.zndsl index 2f8e665d592d587f3e88b45b8e5dcfa1e05780cc..0f5428eed3b01c042f8aad3b3df51c3a800a9b72 100644 --- a/bridges/testing/environments/rococo-westend/westend-init.zndsl +++ b/bridges/testing/environments/rococo-westend/westend-init.zndsl @@ -1,7 +1,7 @@ -Description: User is able to transfer ROC from Rococo Asset Hub to Westend Asset Hub and back +Description: Check if the HRMP channel between Westend BH and Westend AH was opened successfully Network: ./bridge_hub_westend_local_network.toml Creds: config # ensure that initialization has completed -asset-hub-westend-collator1: js-script ../../js-helpers/wait-hrmp-channel-opened.js with "1002" within 600 seconds +asset-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/wait-hrmp-channel-opened.js with "1002" within 600 seconds diff --git a/bridges/testing/environments/rococo-westend/westend.zndsl b/bridges/testing/environments/rococo-westend/westend.zndsl index c75ae579d27ab6eb979d6a0b88ab481940eb0e76..21d4ebf3b05b003083f7d35bf6bb7a1ca4c1ad05 100644 --- a/bridges/testing/environments/rococo-westend/westend.zndsl +++ b/bridges/testing/environments/rococo-westend/westend.zndsl @@ -1,6 +1,6 @@ -Description: User is able to transfer ROC from Rococo Asset Hub to Westend Asset Hub and back +Description: Check if the with-Rococo GRANDPA pallet was initialized at Westend BH Network: ./bridge_hub_westend_local_network.toml Creds: config -# relay is already started - let's wait until with-Rococo GRANPDA pallet is initialized at Westend -bridge-hub-westend-collator1: js-script ../../js-helpers/best-finalized-header-at-bridged-chain.js with "Rococo,0" within 400 seconds +# relay is already started - let's wait until with-Rococo GRANDPA pallet is initialized at Westend +bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/best-finalized-header-at-bridged-chain.js with "Rococo,0" within 400 seconds diff --git a/bridges/testing/js-helpers/best-finalized-header-at-bridged-chain.js b/bridges/testing/framework/js-helpers/best-finalized-header-at-bridged-chain.js similarity index 100% rename from bridges/testing/js-helpers/best-finalized-header-at-bridged-chain.js rename to bridges/testing/framework/js-helpers/best-finalized-header-at-bridged-chain.js diff --git a/bridges/testing/js-helpers/chains/rococo-at-westend.js b/bridges/testing/framework/js-helpers/chains/rococo-at-westend.js similarity index 100% rename from bridges/testing/js-helpers/chains/rococo-at-westend.js rename to bridges/testing/framework/js-helpers/chains/rococo-at-westend.js diff --git a/bridges/testing/js-helpers/chains/westend-at-rococo.js b/bridges/testing/framework/js-helpers/chains/westend-at-rococo.js similarity index 100% rename from bridges/testing/js-helpers/chains/westend-at-rococo.js rename to bridges/testing/framework/js-helpers/chains/westend-at-rococo.js diff --git a/bridges/testing/js-helpers/native-assets-balance-increased.js b/bridges/testing/framework/js-helpers/native-assets-balance-increased.js similarity index 82% rename from bridges/testing/js-helpers/native-assets-balance-increased.js rename to bridges/testing/framework/js-helpers/native-assets-balance-increased.js index a35c753d97326d7b200c33844e9c5b8be22dfebc..749c3e2fec32ac0af4d244c53cb4ac1c6237817a 100644 --- a/bridges/testing/js-helpers/native-assets-balance-increased.js +++ b/bridges/testing/framework/js-helpers/native-assets-balance-increased.js @@ -3,12 +3,13 @@ async function run(nodeName, networkInfo, args) { const api = await zombie.connect(wsUri, userDefinedTypes); const accountAddress = args[0]; + const expectedIncrease = BigInt(args[1]); const initialAccountData = await api.query.system.account(accountAddress); const initialAccountBalance = initialAccountData.data['free']; while (true) { const accountData = await api.query.system.account(accountAddress); const accountBalance = accountData.data['free']; - if (accountBalance > initialAccountBalance) { + if (accountBalance > initialAccountBalance + expectedIncrease) { return accountBalance; } @@ -17,4 +18,4 @@ async function run(nodeName, networkInfo, args) { } } -module.exports = { run } +module.exports = {run} diff --git a/bridges/testing/js-helpers/only-mandatory-headers-synced-when-idle.js b/bridges/testing/framework/js-helpers/only-mandatory-headers-synced-when-idle.js similarity index 100% rename from bridges/testing/js-helpers/only-mandatory-headers-synced-when-idle.js rename to bridges/testing/framework/js-helpers/only-mandatory-headers-synced-when-idle.js diff --git a/bridges/testing/js-helpers/only-required-headers-synced-when-idle.js b/bridges/testing/framework/js-helpers/only-required-headers-synced-when-idle.js similarity index 100% rename from bridges/testing/js-helpers/only-required-headers-synced-when-idle.js rename to bridges/testing/framework/js-helpers/only-required-headers-synced-when-idle.js diff --git a/bridges/testing/js-helpers/relayer-rewards.js b/bridges/testing/framework/js-helpers/relayer-rewards.js similarity index 100% rename from bridges/testing/js-helpers/relayer-rewards.js rename to bridges/testing/framework/js-helpers/relayer-rewards.js diff --git a/bridges/testing/js-helpers/utils.js b/bridges/testing/framework/js-helpers/utils.js similarity index 100% rename from bridges/testing/js-helpers/utils.js rename to bridges/testing/framework/js-helpers/utils.js diff --git a/bridges/testing/js-helpers/wait-hrmp-channel-opened.js b/bridges/testing/framework/js-helpers/wait-hrmp-channel-opened.js similarity index 100% rename from bridges/testing/js-helpers/wait-hrmp-channel-opened.js rename to bridges/testing/framework/js-helpers/wait-hrmp-channel-opened.js diff --git a/bridges/testing/js-helpers/wrapped-assets-balance.js b/bridges/testing/framework/js-helpers/wrapped-assets-balance.js similarity index 100% rename from bridges/testing/js-helpers/wrapped-assets-balance.js rename to bridges/testing/framework/js-helpers/wrapped-assets-balance.js diff --git a/bridges/testing/utils/bridges.sh b/bridges/testing/framework/utils/bridges.sh similarity index 98% rename from bridges/testing/utils/bridges.sh rename to bridges/testing/framework/utils/bridges.sh index cfde5dfd26b720e5eba20b0b0b3685e665163e3f..7c8399461584a85e4e8eedf5f347d9d74725f1c9 100755 --- a/bridges/testing/utils/bridges.sh +++ b/bridges/testing/framework/utils/bridges.sh @@ -41,8 +41,8 @@ function ensure_polkadot_js_api() { echo "" echo "" echo "-------------------" - echo "Installing (nodejs) sub module: $(dirname "$0")/generate_hex_encoded_call" - pushd $(dirname "$0")/generate_hex_encoded_call + echo "Installing (nodejs) sub module: ${BASH_SOURCE%/*}/generate_hex_encoded_call" + pushd ${BASH_SOURCE%/*}/generate_hex_encoded_call npm install popd fi diff --git a/bridges/testing/utils/common.sh b/bridges/testing/framework/utils/common.sh similarity index 100% rename from bridges/testing/utils/common.sh rename to bridges/testing/framework/utils/common.sh diff --git a/bridges/testing/utils/generate_hex_encoded_call/index.js b/bridges/testing/framework/utils/generate_hex_encoded_call/index.js similarity index 90% rename from bridges/testing/utils/generate_hex_encoded_call/index.js rename to bridges/testing/framework/utils/generate_hex_encoded_call/index.js index 30f89d754ceb7de1b24bc31413e09c862a461256..c8e361b25a9ce3e56f3c839d53e2fc67106a98c8 100644 --- a/bridges/testing/utils/generate_hex_encoded_call/index.js +++ b/bridges/testing/framework/utils/generate_hex_encoded_call/index.js @@ -126,36 +126,36 @@ if (!process.argv[2] || !process.argv[3]) { } const type = process.argv[2]; -const rpcEnpoint = process.argv[3]; +const rpcEndpoint = process.argv[3]; const output = process.argv[4]; const inputArgs = process.argv.slice(5, process.argv.length); console.log(`Generating hex-encoded call data for:`); console.log(` type: ${type}`); -console.log(` rpcEnpoint: ${rpcEnpoint}`); +console.log(` rpcEndpoint: ${rpcEndpoint}`); console.log(` output: ${output}`); console.log(` inputArgs: ${inputArgs}`); switch (type) { case 'remark-with-event': - remarkWithEvent(rpcEnpoint, output); + remarkWithEvent(rpcEndpoint, output); break; case 'add-exporter-config': - addExporterConfig(rpcEnpoint, output, inputArgs[0], inputArgs[1]); + addExporterConfig(rpcEndpoint, output, inputArgs[0], inputArgs[1]); break; case 'remove-exporter-config': - removeExporterConfig(rpcEnpoint, output, inputArgs[0], inputArgs[1]); + removeExporterConfig(rpcEndpoint, output, inputArgs[0], inputArgs[1]); break; case 'add-universal-alias': - addUniversalAlias(rpcEnpoint, output, inputArgs[0], inputArgs[1]); + addUniversalAlias(rpcEndpoint, output, inputArgs[0], inputArgs[1]); break; case 'add-reserve-location': - addReserveLocation(rpcEnpoint, output, inputArgs[0]); + addReserveLocation(rpcEndpoint, output, inputArgs[0]); break; case 'force-create-asset': - forceCreateAsset(rpcEnpoint, output, inputArgs[0], inputArgs[1], inputArgs[2], inputArgs[3]); + forceCreateAsset(rpcEndpoint, output, inputArgs[0], inputArgs[1], inputArgs[2], inputArgs[3]); break; case 'force-xcm-version': - forceXcmVersion(rpcEnpoint, output, inputArgs[0], inputArgs[1]); + forceXcmVersion(rpcEndpoint, output, inputArgs[0], inputArgs[1]); break; case 'check': console.log(`Checking nodejs installation, if you see this everything is ready!`); diff --git a/bridges/testing/utils/generate_hex_encoded_call/package-lock.json b/bridges/testing/framework/utils/generate_hex_encoded_call/package-lock.json similarity index 100% rename from bridges/testing/utils/generate_hex_encoded_call/package-lock.json rename to bridges/testing/framework/utils/generate_hex_encoded_call/package-lock.json diff --git a/bridges/testing/utils/generate_hex_encoded_call/package.json b/bridges/testing/framework/utils/generate_hex_encoded_call/package.json similarity index 100% rename from bridges/testing/utils/generate_hex_encoded_call/package.json rename to bridges/testing/framework/utils/generate_hex_encoded_call/package.json diff --git a/bridges/testing/utils/zombienet.sh b/bridges/testing/framework/utils/zombienet.sh similarity index 100% rename from bridges/testing/utils/zombienet.sh rename to bridges/testing/framework/utils/zombienet.sh diff --git a/bridges/testing/run-new-test.sh b/bridges/testing/run-new-test.sh index 2ed2a412b8a7467d84624a47805a4d31e4d4d1a1..7c84a69aa47de84439091cb7b908233d02238175 100755 --- a/bridges/testing/run-new-test.sh +++ b/bridges/testing/run-new-test.sh @@ -21,6 +21,7 @@ do done export POLKADOT_SDK_PATH=`realpath ${BASH_SOURCE%/*}/../..` +export FRAMEWORK_PATH=`realpath ${BASH_SOURCE%/*}/framework` # set path to binaries if [ "$ZOMBIENET_DOCKER_PATHS" -eq 1 ]; then diff --git a/bridges/testing/run-tests.sh b/bridges/testing/run-tests.sh index 6149d9912653c79968a0229759c8f1bf46f68a9f..fd12b57f53349a0a449af7103c05341d3c94ceb9 100755 --- a/bridges/testing/run-tests.sh +++ b/bridges/testing/run-tests.sh @@ -30,7 +30,7 @@ done export POLKADOT_SDK_PATH=`realpath $(dirname "$0")/../..` export BRIDGE_TESTS_FOLDER=$POLKADOT_SDK_PATH/bridges/testing/tests -# set pathc to binaries +# set path to binaries if [ "$ZOMBIENET_DOCKER_PATHS" -eq 1 ]; then export POLKADOT_BINARY=/usr/local/bin/polkadot export POLKADOT_PARACHAIN_BINARY=/usr/local/bin/polkadot-parachain diff --git a/bridges/testing/tests/0001-asset-transfer/roc-reaches-westend.zndsl b/bridges/testing/tests/0001-asset-transfer/roc-reaches-westend.zndsl index 203c95b73eb2d3743610b84828fd12402f2399d4..cdb7d28e940cf1ac90562e761cdbad00e95e1748 100644 --- a/bridges/testing/tests/0001-asset-transfer/roc-reaches-westend.zndsl +++ b/bridges/testing/tests/0001-asset-transfer/roc-reaches-westend.zndsl @@ -1,12 +1,12 @@ Description: User is able to transfer ROC from Rococo Asset Hub to Westend Asset Hub and back -Network: ../../environments/rococo-westend/bridge_hub_westend_local_network.toml +Network: {{ENV_PATH}}/bridge_hub_westend_local_network.toml Creds: config -# send ROC to //Alice from Rococo AH to Westend AH -asset-hub-westend-collator1: run ../../environments/rococo-westend/helper.sh with "reserve-transfer-assets-from-asset-hub-rococo-local" within 120 seconds +# send 5 ROC to //Alice from Rococo AH to Westend AH +asset-hub-westend-collator1: run {{ENV_PATH}}/helper.sh with "reserve-transfer-assets-from-asset-hub-rococo-local 5000000000000" within 120 seconds -# check that //Alice received the ROC on Westend AH -asset-hub-westend-collator1: js-script ../../js-helpers/wrapped-assets-balance.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,0,Rococo" within 300 seconds +# check that //Alice received at least 4.8 ROC on Westend AH +asset-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/wrapped-assets-balance.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,4800000000000,Rococo" within 600 seconds # check that the relayer //Charlie is rewarded by Westend AH -bridge-hub-westend-collator1: js-script ../../js-helpers/relayer-rewards.js with "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y,0x00000002,0x6268726F,ThisChain,0" within 30 seconds +bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/relayer-rewards.js with "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y,0x00000002,0x6268726F,ThisChain,0" within 30 seconds diff --git a/bridges/testing/tests/0001-asset-transfer/run.sh b/bridges/testing/tests/0001-asset-transfer/run.sh index 8a053ee72097417eabb8f5a4f14aba90eb172a14..a7bb122919b40187c49e89c489d2271d646bff40 100755 --- a/bridges/testing/tests/0001-asset-transfer/run.sh +++ b/bridges/testing/tests/0001-asset-transfer/run.sh @@ -2,17 +2,19 @@ set -e -source "${BASH_SOURCE%/*}/../../utils/common.sh" -source "${BASH_SOURCE%/*}/../../utils/zombienet.sh" +source "${BASH_SOURCE%/*}/../../framework/utils/common.sh" +source "${BASH_SOURCE%/*}/../../framework/utils/zombienet.sh" -${BASH_SOURCE%/*}/../../environments/rococo-westend/spawn.sh --init --start-relayer & +export ENV_PATH=`realpath ${BASH_SOURCE%/*}/../../environments/rococo-westend` + +$ENV_PATH/spawn.sh --init --start-relayer & env_pid=$! -ensure_process_file $env_pid $TEST_DIR/rococo.env 400 +ensure_process_file $env_pid $TEST_DIR/rococo.env 600 rococo_dir=`cat $TEST_DIR/rococo.env` echo -ensure_process_file $env_pid $TEST_DIR/westend.env 180 +ensure_process_file $env_pid $TEST_DIR/westend.env 300 westend_dir=`cat $TEST_DIR/westend.env` echo diff --git a/bridges/testing/tests/0001-asset-transfer/wnd-reaches-rococo.zndsl b/bridges/testing/tests/0001-asset-transfer/wnd-reaches-rococo.zndsl index bbd95db9cfda88396657d0744ef84a4dd4d92180..dbc03864e2b6e5e10636532ad965860b381fa8f2 100644 --- a/bridges/testing/tests/0001-asset-transfer/wnd-reaches-rococo.zndsl +++ b/bridges/testing/tests/0001-asset-transfer/wnd-reaches-rococo.zndsl @@ -1,12 +1,12 @@ Description: User is able to transfer WND from Westend Asset Hub to Rococo Asset Hub and back -Network: ../../environments/rococo-westend/bridge_hub_rococo_local_network.toml +Network: {{ENV_PATH}}/bridge_hub_rococo_local_network.toml Creds: config -# send WND to //Alice from Westend AH to Rococo AH -asset-hub-rococo-collator1: run ../../environments/rococo-westend/helper.sh with "reserve-transfer-assets-from-asset-hub-westend-local" within 120 seconds +# send 5 WND to //Alice from Westend AH to Rococo AH +asset-hub-rococo-collator1: run {{ENV_PATH}}/helper.sh with "reserve-transfer-assets-from-asset-hub-westend-local 5000000000000" within 120 seconds -# check that //Alice received the WND on Rococo AH -asset-hub-rococo-collator1: js-script ../../js-helpers/wrapped-assets-balance.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,0,Westend" within 300 seconds +# check that //Alice received at least 4.8 WND on Rococo AH +asset-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/wrapped-assets-balance.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,4800000000000,Westend" within 600 seconds # check that the relayer //Charlie is rewarded by Rococo AH -bridge-hub-rococo-collator1: js-script ../../js-helpers/relayer-rewards.js with "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y,0x00000002,0x62687764,ThisChain,0" within 30 seconds +bridge-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/relayer-rewards.js with "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y,0x00000002,0x62687764,ThisChain,0" within 30 seconds diff --git a/bridges/testing/tests/0001-asset-transfer/wroc-reaches-rococo.zndsl b/bridges/testing/tests/0001-asset-transfer/wroc-reaches-rococo.zndsl index 4c0a4675234e614f6569653aa14263dac3ac3e03..9967732cabe15e020060eb0a53fd8ce1440180e5 100644 --- a/bridges/testing/tests/0001-asset-transfer/wroc-reaches-rococo.zndsl +++ b/bridges/testing/tests/0001-asset-transfer/wroc-reaches-rococo.zndsl @@ -1,10 +1,10 @@ Description: User is able to transfer ROC from Rococo Asset Hub to Westend Asset Hub and back -Network: ../../environments/rococo-westend/bridge_hub_westend_local_network.toml +Network: {{ENV_PATH}}/bridge_hub_westend_local_network.toml Creds: config -# send wROC back to Alice from Westend AH to Rococo AH -asset-hub-rococo-collator1: run ../../environments/rococo-westend/helper.sh with "withdraw-reserve-assets-from-asset-hub-westend-local" within 120 seconds +# send 3 wROC back to Alice from Westend AH to Rococo AH +asset-hub-rococo-collator1: run {{ENV_PATH}}/helper.sh with "withdraw-reserve-assets-from-asset-hub-westend-local 3000000000000" within 120 seconds -# check that //Alice received the wROC on Rococo AH +# check that //Alice received at least 2.8 wROC on Rococo AH # (we wait until //Alice account increases here - there are no other transactions that may increase it) -asset-hub-rococo-collator1: js-script ../../js-helpers/native-assets-balance-increased.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" within 300 seconds +asset-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/native-assets-balance-increased.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,2800000000000" within 600 seconds diff --git a/bridges/testing/tests/0001-asset-transfer/wwnd-reaches-westend.zndsl b/bridges/testing/tests/0001-asset-transfer/wwnd-reaches-westend.zndsl index 3acded97d5cc9cd0c2177f47fae7d362058eb85a..2037b0baf3c0aac2a0e1a8f297c6baa155ac680f 100644 --- a/bridges/testing/tests/0001-asset-transfer/wwnd-reaches-westend.zndsl +++ b/bridges/testing/tests/0001-asset-transfer/wwnd-reaches-westend.zndsl @@ -1,10 +1,10 @@ Description: User is able to transfer ROC from Rococo Asset Hub to Westend Asset Hub and back -Network: ../../environments/rococo-westend/bridge_hub_westend_local_network.toml +Network: {{ENV_PATH}}/bridge_hub_westend_local_network.toml Creds: config -# send wWND back to Alice from Rococo AH to Westend AH -asset-hub-westend-collator1: run ../../environments/rococo-westend/helper.sh with "withdraw-reserve-assets-from-asset-hub-rococo-local" within 120 seconds +# send 3 wWND back to Alice from Rococo AH to Westend AH +asset-hub-westend-collator1: run {{ENV_PATH}}/helper.sh with "withdraw-reserve-assets-from-asset-hub-rococo-local 3000000000000" within 120 seconds -# check that //Alice received the wWND on Westend AH +# check that //Alice received at least 2.8 wWND on Westend AH # (we wait until //Alice account increases here - there are no other transactions that may increase it) -asset-hub-westend-collator1: js-script ../../js-helpers/native-assets-balance-increased.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" within 300 seconds +asset-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/native-assets-balance-increased.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,2800000000000" within 600 seconds diff --git a/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/rococo-to-westend.zndsl b/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/rococo-to-westend.zndsl index 82a1a103b14a7a175dff9f352517efc9269d7e36..6e381f5377329430c0d7a8723f9ea9081556bfeb 100644 --- a/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/rococo-to-westend.zndsl +++ b/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/rococo-to-westend.zndsl @@ -1,8 +1,8 @@ Description: While relayer is idle, we only sync mandatory Rococo (and a single Rococo BH) headers to Westend BH. -Network: ../../environments/rococo-westend/bridge_hub_westend_local_network.toml +Network: {{ENV_PATH}}/bridge_hub_westend_local_network.toml Creds: config # ensure that relayer is only syncing mandatory headers while idle. This includes both headers that were # generated while relay was offline and those in the next 100 seconds while script is active. -bridge-hub-westend-collator1: js-script ../../js-helpers/only-mandatory-headers-synced-when-idle.js with "300,rococo-at-westend" within 600 seconds +bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/only-mandatory-headers-synced-when-idle.js with "300,rococo-at-westend" within 600 seconds diff --git a/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/run.sh b/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/run.sh index 423f4a1bcc0f2a2cceb06c4d1855b168063ec990..3a604b3876d96241903c1c5a110cc6392f26cb7e 100755 --- a/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/run.sh +++ b/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/run.sh @@ -2,19 +2,19 @@ set -e -source "${BASH_SOURCE%/*}/../../utils/common.sh" -source "${BASH_SOURCE%/*}/../../utils/zombienet.sh" +source "${BASH_SOURCE%/*}/../../framework/utils/common.sh" +source "${BASH_SOURCE%/*}/../../framework/utils/zombienet.sh" -# We use `--relayer-delay` in order to sleep some time before starting relayer. -# We want to sleep for at least 1 session, which is expected to be 60 seconds for test environment. -${BASH_SOURCE%/*}/../../environments/rococo-westend/spawn.sh & +export ENV_PATH=`realpath ${BASH_SOURCE%/*}/../../environments/rococo-westend` + +$ENV_PATH/spawn.sh & env_pid=$! -ensure_process_file $env_pid $TEST_DIR/rococo.env 400 +ensure_process_file $env_pid $TEST_DIR/rococo.env 600 rococo_dir=`cat $TEST_DIR/rococo.env` echo -ensure_process_file $env_pid $TEST_DIR/westend.env 180 +ensure_process_file $env_pid $TEST_DIR/westend.env 300 westend_dir=`cat $TEST_DIR/westend.env` echo @@ -24,7 +24,7 @@ echo -e "Sleeping 90s before starting relayer ...\n" sleep 90 ${BASH_SOURCE%/*}/../../environments/rococo-westend/start_relayer.sh $rococo_dir $westend_dir relayer_pid -# Sometimes the relayer syncs multiple parachain heads in the begining leading to test failures. +# Sometimes the relayer syncs multiple parachain heads in the beginning leading to test failures. # See issue: https://github.com/paritytech/parity-bridges-common/issues/2838. # TODO: Remove this sleep after the issue is fixed. echo -e "Sleeping 180s before runing the tests ...\n" diff --git a/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/westend-to-rococo.zndsl b/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/westend-to-rococo.zndsl index 865813246252ae7911addd603903b6b2a43bab73..b4b3e43679162feb8c3c5253f3f963d950f31d55 100644 --- a/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/westend-to-rococo.zndsl +++ b/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/westend-to-rococo.zndsl @@ -1,7 +1,7 @@ Description: While relayer is idle, we only sync mandatory Westend (and a single Westend BH) headers to Rococo BH. -Network: ../../environments/rococo-westend/bridge_hub_rococo_local_network.toml +Network: {{ENV_PATH}}/bridge_hub_rococo_local_network.toml Creds: config # ensure that relayer is only syncing mandatory headers while idle. This includes both headers that were # generated while relay was offline and those in the next 100 seconds while script is active. -bridge-hub-rococo-collator1: js-script ../../js-helpers/only-mandatory-headers-synced-when-idle.js with "300,westend-at-rococo" within 600 seconds +bridge-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/only-mandatory-headers-synced-when-idle.js with "300,westend-at-rococo" within 600 seconds diff --git a/cumulus/client/cli/Cargo.toml b/cumulus/client/cli/Cargo.toml index eaf0d5d5d7f78e578644bf35f83d9543ad9af4bd..0b2edb593c405bef266fa7e389a996fe3a2fcec1 100644 --- a/cumulus/client/cli/Cargo.toml +++ b/cumulus/client/cli/Cargo.toml @@ -10,7 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" workspace = true [dependencies] -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.0.0" } url = "2.4.0" diff --git a/cumulus/client/consensus/aura/Cargo.toml b/cumulus/client/consensus/aura/Cargo.toml index e815e89d8ce3bebcdcf52b363917ba17c0382708..58bb1dd5914b01f3489939ba34e1a6234fb8b1ff 100644 --- a/cumulus/client/consensus/aura/Cargo.toml +++ b/cumulus/client/consensus/aura/Cargo.toml @@ -41,7 +41,7 @@ substrate-prometheus-endpoint = { path = "../../../../substrate/utils/prometheus cumulus-client-consensus-common = { path = "../common" } cumulus-relay-chain-interface = { path = "../../relay-chain-interface" } cumulus-client-consensus-proposer = { path = "../proposer" } -cumulus-client-parachain-inherent = { path = "../../../client/parachain-inherent" } +cumulus-client-parachain-inherent = { path = "../../parachain-inherent" } cumulus-primitives-aura = { path = "../../../primitives/aura" } cumulus-primitives-core = { path = "../../../primitives/core" } cumulus-client-collator = { path = "../../collator" } diff --git a/cumulus/client/consensus/aura/src/collators/basic.rs b/cumulus/client/consensus/aura/src/collators/basic.rs index 8740b06005d65d53747dacdb6499370962ffe0a4..52b83254951f0e0ba0fd9ad5420d7faca2402066 100644 --- a/cumulus/client/consensus/aura/src/collators/basic.rs +++ b/cumulus/client/consensus/aura/src/collators/basic.rs @@ -33,12 +33,12 @@ use cumulus_relay_chain_interface::RelayChainInterface; use polkadot_node_primitives::CollationResult; use polkadot_overseer::Handle as OverseerHandle; -use polkadot_primitives::{CollatorPair, Id as ParaId}; +use polkadot_primitives::{CollatorPair, Id as ParaId, ValidationCode}; use futures::{channel::mpsc::Receiver, prelude::*}; use sc_client_api::{backend::AuxStore, BlockBackend, BlockOf}; use sc_consensus::BlockImport; -use sp_api::ProvideRuntimeApi; +use sp_api::{CallApiAt, ProvideRuntimeApi}; use sp_application_crypto::AppPublic; use sp_blockchain::HeaderBackend; use sp_consensus::SyncOracle; @@ -47,6 +47,7 @@ use sp_core::crypto::Pair; use sp_inherents::CreateInherentDataProviders; use sp_keystore::KeystorePtr; use sp_runtime::traits::{Block as BlockT, Header as HeaderT, Member}; +use sp_state_machine::Backend as _; use std::{convert::TryFrom, sync::Arc, time::Duration}; use crate::collator as collator_util; @@ -100,6 +101,7 @@ where + AuxStore + HeaderBackend + BlockBackend + + CallApiAt + Send + Sync + 'static, @@ -172,6 +174,22 @@ where continue } + let Ok(Some(code)) = + params.para_client.state_at(parent_hash).map_err(drop).and_then(|s| { + s.storage(&sp_core::storage::well_known_keys::CODE).map_err(drop) + }) + else { + continue; + }; + + super::check_validation_code_or_log( + &ValidationCode::from(code).hash(), + params.para_id, + ¶ms.relay_client, + *request.relay_parent(), + ) + .await; + let relay_parent_header = match params.relay_client.header(RBlockId::hash(*request.relay_parent())).await { Err(e) => reject_with_error!(e), diff --git a/cumulus/client/consensus/aura/src/collators/lookahead.rs b/cumulus/client/consensus/aura/src/collators/lookahead.rs index a9f33173d832ab96edd0ada3f123647fb9a967d6..161f10d55a193de35a2585e1a1f5725f30e19bf7 100644 --- a/cumulus/client/consensus/aura/src/collators/lookahead.rs +++ b/cumulus/client/consensus/aura/src/collators/lookahead.rs @@ -290,10 +290,7 @@ where // If the longest chain has space, build upon that. Otherwise, don't // build at all. potential_parents.sort_by_key(|a| a.depth); - let initial_parent = match potential_parents.pop() { - None => continue, - Some(p) => p, - }; + let Some(initial_parent) = potential_parents.pop() else { continue }; // Build in a loop until not allowed. Note that the authorities can change // at any block, so we need to re-claim our slot every time. @@ -301,6 +298,10 @@ where let mut parent_header = initial_parent.header; let overseer_handle = &mut params.overseer_handle; + // We mainly call this to inform users at genesis if there is a mismatch with the + // on-chain data. + collator.collator_service().check_block_status(parent_hash, &parent_header); + // This needs to change to support elastic scaling, but for continuously // scheduled chains this ensures that the backlog will grow steadily. for n_built in 0..2 { @@ -353,6 +354,14 @@ where Some(v) => v, }; + super::check_validation_code_or_log( + &validation_code_hash, + params.para_id, + ¶ms.relay_client, + relay_parent, + ) + .await; + match collator .collate( &parent_header, diff --git a/cumulus/client/consensus/aura/src/collators/mod.rs b/cumulus/client/consensus/aura/src/collators/mod.rs index 4c7b759daf736f69de48b586b082a7d01534d7e3..6e0067d0cedb602face8943737f99f3cb1a201a3 100644 --- a/cumulus/client/consensus/aura/src/collators/mod.rs +++ b/cumulus/client/consensus/aura/src/collators/mod.rs @@ -20,5 +20,60 @@ //! included parachain block, as well as the [`lookahead`] collator, which prospectively //! builds on parachain blocks which have not yet been included in the relay chain. +use cumulus_relay_chain_interface::RelayChainInterface; +use polkadot_primitives::{ + Hash as RHash, Id as ParaId, OccupiedCoreAssumption, ValidationCodeHash, +}; + pub mod basic; pub mod lookahead; + +/// Check the `local_validation_code_hash` against the validation code hash in the relay chain +/// state. +/// +/// If the code hashes do not match, it prints a warning. +async fn check_validation_code_or_log( + local_validation_code_hash: &ValidationCodeHash, + para_id: ParaId, + relay_client: &impl RelayChainInterface, + relay_parent: RHash, +) { + let state_validation_code_hash = match relay_client + .validation_code_hash(relay_parent, para_id, OccupiedCoreAssumption::Included) + .await + { + Ok(hash) => hash, + Err(error) => { + tracing::debug!( + target: super::LOG_TARGET, + %error, + ?relay_parent, + %para_id, + "Failed to fetch validation code hash", + ); + return + }, + }; + + match state_validation_code_hash { + Some(state) => + if state != *local_validation_code_hash { + tracing::warn!( + target: super::LOG_TARGET, + %para_id, + ?relay_parent, + ?local_validation_code_hash, + relay_validation_code_hash = ?state, + "Parachain code doesn't match validation code stored in the relay chain state", + ); + }, + None => { + tracing::warn!( + target: super::LOG_TARGET, + %para_id, + ?relay_parent, + "Could not find validation code for parachain in the relay chain state.", + ); + }, + } +} diff --git a/cumulus/client/consensus/aura/src/lib.rs b/cumulus/client/consensus/aura/src/lib.rs index 8e4bc658e44bfdc173f3fdb66518da883edcb05b..ed6f5bdd4d6984350c5f59a3753618c3a038f323 100644 --- a/cumulus/client/consensus/aura/src/lib.rs +++ b/cumulus/client/consensus/aura/src/lib.rs @@ -54,7 +54,10 @@ use std::{ mod import_queue; pub use import_queue::{build_verifier, import_queue, BuildVerifierParams, ImportQueueParams}; -pub use sc_consensus_aura::{slot_duration, AuraVerifier, BuildAuraWorkerParams, SlotProportion}; +pub use sc_consensus_aura::{ + slot_duration, standalone::slot_duration_at, AuraVerifier, BuildAuraWorkerParams, + SlotProportion, +}; pub use sc_consensus_slots::InherentDataProviderExt; pub mod collator; diff --git a/cumulus/client/consensus/common/src/level_monitor.rs b/cumulus/client/consensus/common/src/level_monitor.rs index 270e3f57ae5a374aba2f22966c7376e0791020ca..fb4b0498f6887f8bea1d3e7ba4e7f4082e2f9747 100644 --- a/cumulus/client/consensus/common/src/level_monitor.rs +++ b/cumulus/client/consensus/common/src/level_monitor.rs @@ -158,7 +158,7 @@ where /// the limit passed to the constructor. /// /// If the given level is found to have a number of blocks greater than or equal the limit - /// then the limit is enforced by chosing one (or more) blocks to remove. + /// then the limit is enforced by choosing one (or more) blocks to remove. /// /// The removal strategy is driven by the block freshness. /// diff --git a/cumulus/client/consensus/common/src/tests.rs b/cumulus/client/consensus/common/src/tests.rs index 597d1ab2acc2cff42d3230898c1129a7ba63b6f3..7816d3a4c40a7ab02aceb86c701f127e45158470 100644 --- a/cumulus/client/consensus/common/src/tests.rs +++ b/cumulus/client/consensus/common/src/tests.rs @@ -136,6 +136,15 @@ impl RelayChainInterface for Relaychain { Ok(Some(PersistedValidationData { parent_head, ..Default::default() })) } + async fn validation_code_hash( + &self, + _: PHash, + _: ParaId, + _: OccupiedCoreAssumption, + ) -> RelayChainResult> { + unimplemented!("Not needed for test") + } + async fn candidate_pending_availability( &self, _: PHash, @@ -823,7 +832,7 @@ fn restore_limit_monitor() { .collect::>(); // Scenario before limit application (with B11 imported as best) - // Import order (freshess): B00, B10, B11, B12, B20, B21 + // Import order (freshness): B00, B10, B11, B12, B20, B21 // // B00 --+-- B10 --+-- B20 // | +-- B21 diff --git a/cumulus/client/network/src/tests.rs b/cumulus/client/network/src/tests.rs index e03f470753bb6c32c4410a7c694dea8312c74c31..3f5757d5eac13cd751a89e77545a937b3223c9a0 100644 --- a/cumulus/client/network/src/tests.rs +++ b/cumulus/client/network/src/tests.rs @@ -117,6 +117,15 @@ impl RelayChainInterface for DummyRelayChainInterface { })) } + async fn validation_code_hash( + &self, + _: PHash, + _: ParaId, + _: OccupiedCoreAssumption, + ) -> RelayChainResult> { + unimplemented!("Not needed for test") + } + async fn candidate_pending_availability( &self, _: PHash, @@ -135,7 +144,7 @@ impl RelayChainInterface for DummyRelayChainInterface { persisted_validation_data_hash: PHash::random(), pov_hash: PHash::random(), erasure_root: PHash::random(), - signature: sp_core::sr25519::Signature([0u8; 64]).into(), + signature: sp_core::sr25519::Signature::default().into(), validation_code_hash: ValidationCodeHash::from(PHash::random()), }, commitments: CandidateCommitments { @@ -316,7 +325,7 @@ async fn make_gossip_message_and_header( persisted_validation_data_hash: PHash::random(), pov_hash: PHash::random(), erasure_root: PHash::random(), - signature: sp_core::sr25519::Signature([0u8; 64]).into(), + signature: sp_core::sr25519::Signature::default().into(), para_head: polkadot_parachain_primitives::primitives::HeadData(header.encode()).hash(), validation_code_hash: ValidationCodeHash::from(PHash::random()), }, @@ -507,7 +516,7 @@ async fn check_statement_seconded() { persisted_validation_data_hash: PHash::random(), pov_hash: PHash::random(), erasure_root: PHash::random(), - signature: sp_core::sr25519::Signature([0u8; 64]).into(), + signature: sp_core::sr25519::Signature::default().into(), validation_code_hash: ValidationCodeHash::from(PHash::random()), }, }, diff --git a/cumulus/client/parachain-inherent/src/lib.rs b/cumulus/client/parachain-inherent/src/lib.rs index 57353638e197acf255b73080c6802e94647c2090..051eb6764c8ce97a8305a07bdddb8f06f61fe4bc 100644 --- a/cumulus/client/parachain-inherent/src/lib.rs +++ b/cumulus/client/parachain-inherent/src/lib.rs @@ -159,7 +159,7 @@ impl ParachainInherentDataProvider { target: LOG_TARGET, relay_parent = ?relay_parent, error = ?e, - "An error occured during requesting the downward messages.", + "An error occurred during requesting the downward messages.", ); }) .ok()?; @@ -171,7 +171,7 @@ impl ParachainInherentDataProvider { target: LOG_TARGET, relay_parent = ?relay_parent, error = ?e, - "An error occured during requesting the inbound HRMP messages.", + "An error occurred during requesting the inbound HRMP messages.", ); }) .ok()?; diff --git a/cumulus/client/pov-recovery/src/lib.rs b/cumulus/client/pov-recovery/src/lib.rs index 32aba6c8993a6da67cd3adeb394e87e6c067b46c..0ca21749c3eb557a78f0996c703e86b35cab6f17 100644 --- a/cumulus/client/pov-recovery/src/lib.rs +++ b/cumulus/client/pov-recovery/src/lib.rs @@ -18,7 +18,7 @@ //! //! A parachain needs to build PoVs that are send to the relay chain to progress. These PoVs are //! erasure encoded and one piece of it is stored by each relay chain validator. As the relay chain -//! decides on which PoV per parachain to include and thus, to progess the parachain it can happen +//! decides on which PoV per parachain to include and thus, to progress the parachain it can happen //! that the block corresponding to this PoV isn't propagated in the parachain network. This can //! have several reasons, either a malicious collator that managed to include its own PoV and //! doesn't want to share it with the rest of the network or maybe a collator went down before it @@ -338,8 +338,8 @@ where let mut blocks_to_delete = vec![hash]; while let Some(delete) = blocks_to_delete.pop() { - if let Some(childs) = self.waiting_for_parent.remove(&delete) { - blocks_to_delete.extend(childs.iter().map(BlockT::hash)); + if let Some(children) = self.waiting_for_parent.remove(&delete) { + blocks_to_delete.extend(children.iter().map(BlockT::hash)); } } self.clear_waiting_recovery(&hash); @@ -448,7 +448,7 @@ where /// Import the given `block`. /// - /// This will also recursivley drain `waiting_for_parent` and import them as well. + /// This will also recursively drain `waiting_for_parent` and import them as well. fn import_block(&mut self, block: Block) { let mut blocks = VecDeque::new(); @@ -495,7 +495,7 @@ where tracing::debug!( target: LOG_TARGET, block_hash = ?hash, - "Cound not recover. Block was never announced as candidate" + "Could not recover. Block was never announced as candidate" ); return }, diff --git a/cumulus/client/relay-chain-inprocess-interface/src/lib.rs b/cumulus/client/relay-chain-inprocess-interface/src/lib.rs index 866214fe2c526d77153afbea74b130aa1fda8636..6ea02b2e7c1f6d9b5313459890dd2147015359e5 100644 --- a/cumulus/client/relay-chain-inprocess-interface/src/lib.rs +++ b/cumulus/client/relay-chain-inprocess-interface/src/lib.rs @@ -21,7 +21,7 @@ use cumulus_primitives_core::{ relay_chain::{ runtime_api::ParachainHost, Block as PBlock, BlockId, CommittedCandidateReceipt, Hash as PHash, Header as PHeader, InboundHrmpMessage, OccupiedCoreAssumption, SessionIndex, - ValidatorId, + ValidationCodeHash, ValidatorId, }, InboundDownwardMessage, ParaId, PersistedValidationData, }; @@ -115,6 +115,19 @@ impl RelayChainInterface for RelayChainInProcessInterface { )?) } + async fn validation_code_hash( + &self, + hash: PHash, + para_id: ParaId, + occupied_core_assumption: OccupiedCoreAssumption, + ) -> RelayChainResult> { + Ok(self.full_client.runtime_api().validation_code_hash( + hash, + para_id, + occupied_core_assumption, + )?) + } + async fn candidate_pending_availability( &self, hash: PHash, diff --git a/cumulus/client/relay-chain-interface/src/lib.rs b/cumulus/client/relay-chain-interface/src/lib.rs index de5e7891b30da0d5eaf67c5acc80be48944c1399..bb93e6a168c849fa9d41586ad8b9ec0013e6c01f 100644 --- a/cumulus/client/relay-chain-interface/src/lib.rs +++ b/cumulus/client/relay-chain-interface/src/lib.rs @@ -30,7 +30,7 @@ use cumulus_primitives_core::relay_chain::BlockId; pub use cumulus_primitives_core::{ relay_chain::{ CommittedCandidateReceipt, Hash as PHash, Header as PHeader, InboundHrmpMessage, - OccupiedCoreAssumption, SessionIndex, ValidatorId, + OccupiedCoreAssumption, SessionIndex, ValidationCodeHash, ValidatorId, }, InboundDownwardMessage, ParaId, PersistedValidationData, }; @@ -194,6 +194,15 @@ pub trait RelayChainInterface: Send + Sync { relay_parent: PHash, relevant_keys: &Vec>, ) -> RelayChainResult; + + /// Returns the validation code hash for the given `para_id` using the given + /// `occupied_core_assumption`. + async fn validation_code_hash( + &self, + relay_parent: PHash, + para_id: ParaId, + occupied_core_assumption: OccupiedCoreAssumption, + ) -> RelayChainResult>; } #[async_trait] @@ -301,4 +310,15 @@ where async fn header(&self, block_id: BlockId) -> RelayChainResult> { (**self).header(block_id).await } + + async fn validation_code_hash( + &self, + relay_parent: PHash, + para_id: ParaId, + occupied_core_assumption: OccupiedCoreAssumption, + ) -> RelayChainResult> { + (**self) + .validation_code_hash(relay_parent, para_id, occupied_core_assumption) + .await + } } diff --git a/cumulus/client/relay-chain-minimal-node/Cargo.toml b/cumulus/client/relay-chain-minimal-node/Cargo.toml index 0b1e001e007a493f521f10c2659653699da04d0c..98240c92adab38ea103f14699e1e49f7449b3d24 100644 --- a/cumulus/client/relay-chain-minimal-node/Cargo.toml +++ b/cumulus/client/relay-chain-minimal-node/Cargo.toml @@ -24,6 +24,7 @@ polkadot-node-collation-generation = { path = "../../../polkadot/node/collation- polkadot-node-core-runtime-api = { path = "../../../polkadot/node/core/runtime-api" } polkadot-node-core-chain-api = { path = "../../../polkadot/node/core/chain-api" } polkadot-node-core-prospective-parachains = { path = "../../../polkadot/node/core/prospective-parachains" } +polkadot-service = { path = "../../../polkadot/node/service" } # substrate deps sc-authority-discovery = { path = "../../../substrate/client/authority-discovery" } diff --git a/cumulus/client/relay-chain-minimal-node/src/blockchain_rpc_client.rs b/cumulus/client/relay-chain-minimal-node/src/blockchain_rpc_client.rs index ab56b62c4ca59b3058e9724032e2e09264a45640..8d8a2920b4efaa70a9b59ba42ccd26a2e4cb64d5 100644 --- a/cumulus/client/relay-chain-minimal-node/src/blockchain_rpc_client.rs +++ b/cumulus/client/relay-chain-minimal-node/src/blockchain_rpc_client.rs @@ -14,7 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use std::pin::Pin; +use std::{ + collections::{BTreeMap, VecDeque}, + pin::Pin, +}; use cumulus_relay_chain_interface::{RelayChainError, RelayChainResult}; use cumulus_relay_chain_rpc_interface::RelayChainRpcClient; @@ -25,6 +28,7 @@ use polkadot_primitives::{ async_backing::{AsyncBackingParams, BackingState}, slashing, vstaging::{ApprovalVotingParams, NodeFeatures}, + CoreIndex, }; use sc_authority_discovery::{AuthorityDiscovery, Error as AuthorityDiscoveryError}; use sc_client_api::AuxStore; @@ -442,6 +446,13 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { async fn node_features(&self, at: Hash) -> Result { Ok(self.rpc_client.parachain_host_node_features(at).await?) } + + async fn claim_queue( + &self, + at: Hash, + ) -> Result>, ApiError> { + Ok(self.rpc_client.parachain_host_claim_queue(at).await?) + } } #[async_trait::async_trait] diff --git a/cumulus/client/relay-chain-minimal-node/src/collator_overseer.rs b/cumulus/client/relay-chain-minimal-node/src/collator_overseer.rs index 5f5bf338ef9907756adb1eab3f0541e870677fe5..f01ef8b05ecba77cf6458fdfe80e966b3a67e6a3 100644 --- a/cumulus/client/relay-chain-minimal-node/src/collator_overseer.rs +++ b/cumulus/client/relay-chain-minimal-node/src/collator_overseer.rs @@ -15,159 +15,29 @@ // along with Polkadot. If not, see . use futures::{select, StreamExt}; -use parking_lot::Mutex; -use std::{collections::HashMap, sync::Arc}; +use std::sync::Arc; -use polkadot_availability_recovery::AvailabilityRecoverySubsystem; -use polkadot_collator_protocol::{CollatorProtocolSubsystem, ProtocolSide}; -use polkadot_network_bridge::{ - Metrics as NetworkBridgeMetrics, NetworkBridgeRx as NetworkBridgeRxSubsystem, - NetworkBridgeTx as NetworkBridgeTxSubsystem, -}; -use polkadot_node_collation_generation::CollationGenerationSubsystem; -use polkadot_node_core_chain_api::ChainApiSubsystem; -use polkadot_node_core_prospective_parachains::ProspectiveParachainsSubsystem; -use polkadot_node_core_runtime_api::RuntimeApiSubsystem; -use polkadot_node_network_protocol::{ - peer_set::{PeerSet, PeerSetProtocolNames}, - request_response::{ - v1::{self, AvailableDataFetchingRequest}, - v2, IncomingRequestReceiver, ReqProtocolNames, - }, -}; -use polkadot_node_subsystem_util::metrics::{prometheus::Registry, Metrics}; use polkadot_overseer::{ - BlockInfo, DummySubsystem, Handle, Overseer, OverseerConnector, OverseerHandle, SpawnGlue, - UnpinHandle, + BlockInfo, Handle, Overseer, OverseerConnector, OverseerHandle, SpawnGlue, UnpinHandle, }; -use polkadot_primitives::CollatorPair; +use polkadot_service::overseer::{collator_overseer_builder, OverseerGenArgs}; -use sc_authority_discovery::Service as AuthorityDiscoveryService; -use sc_network::{NetworkStateInfo, NotificationService}; use sc_service::TaskManager; use sc_utils::mpsc::tracing_unbounded; -use cumulus_primitives_core::relay_chain::{Block, Hash as PHash}; use cumulus_relay_chain_interface::RelayChainError; use crate::BlockChainRpcClient; -/// Arguments passed for overseer construction. -pub(crate) struct CollatorOverseerGenArgs<'a> { - /// Runtime client generic, providing the `ProvieRuntimeApi` trait besides others. - pub runtime_client: Arc, - /// Underlying network service implementation. - pub network_service: Arc>, - /// Syncing oracle. - pub sync_oracle: Box, - /// Underlying authority discovery service. - pub authority_discovery_service: AuthorityDiscoveryService, - /// Receiver for collation request protocol v1. - pub collation_req_receiver_v1: IncomingRequestReceiver, - /// Receiver for collation request protocol v2. - pub collation_req_receiver_v2: IncomingRequestReceiver, - /// Receiver for availability request protocol - pub available_data_req_receiver: IncomingRequestReceiver, - /// Prometheus registry, commonly used for production systems, less so for test. - pub registry: Option<&'a Registry>, - /// Task spawner to be used throughout the overseer and the APIs it provides. - pub spawner: sc_service::SpawnTaskHandle, - /// Determines the behavior of the collator. - pub collator_pair: CollatorPair, - /// Request response protocols - pub req_protocol_names: ReqProtocolNames, - /// Peerset protocols name mapping - pub peer_set_protocol_names: PeerSetProtocolNames, - /// Notification services for validation/collation protocols. - pub notification_services: HashMap>, -} - fn build_overseer( connector: OverseerConnector, - CollatorOverseerGenArgs { - runtime_client, - network_service, - sync_oracle, - authority_discovery_service, - collation_req_receiver_v1, - collation_req_receiver_v2, - available_data_req_receiver, - registry, - spawner, - collator_pair, - req_protocol_names, - peer_set_protocol_names, - notification_services, - }: CollatorOverseerGenArgs<'_>, + args: OverseerGenArgs, ) -> Result< (Overseer, Arc>, OverseerHandle), RelayChainError, > { - let spawner = SpawnGlue(spawner); - let network_bridge_metrics: NetworkBridgeMetrics = Metrics::register(registry)?; - let notification_sinks = Arc::new(Mutex::new(HashMap::new())); - - let builder = Overseer::builder() - .availability_distribution(DummySubsystem) - .availability_recovery(AvailabilityRecoverySubsystem::for_collator( - available_data_req_receiver, - Metrics::register(registry)?, - )) - .availability_store(DummySubsystem) - .bitfield_distribution(DummySubsystem) - .bitfield_signing(DummySubsystem) - .candidate_backing(DummySubsystem) - .candidate_validation(DummySubsystem) - .pvf_checker(DummySubsystem) - .chain_api(ChainApiSubsystem::new(runtime_client.clone(), Metrics::register(registry)?)) - .collation_generation(CollationGenerationSubsystem::new(Metrics::register(registry)?)) - .collator_protocol({ - let side = ProtocolSide::Collator { - peer_id: network_service.local_peer_id(), - collator_pair, - request_receiver_v1: collation_req_receiver_v1, - request_receiver_v2: collation_req_receiver_v2, - metrics: Metrics::register(registry)?, - }; - CollatorProtocolSubsystem::new(side) - }) - .network_bridge_rx(NetworkBridgeRxSubsystem::new( - network_service.clone(), - authority_discovery_service.clone(), - sync_oracle, - network_bridge_metrics.clone(), - peer_set_protocol_names.clone(), - notification_services, - notification_sinks.clone(), - )) - .network_bridge_tx(NetworkBridgeTxSubsystem::new( - network_service, - authority_discovery_service, - network_bridge_metrics, - req_protocol_names, - peer_set_protocol_names, - notification_sinks, - )) - .provisioner(DummySubsystem) - .runtime_api(RuntimeApiSubsystem::new( - runtime_client.clone(), - Metrics::register(registry)?, - spawner.clone(), - )) - .statement_distribution(DummySubsystem) - .prospective_parachains(ProspectiveParachainsSubsystem::new(Metrics::register(registry)?)) - .approval_distribution(DummySubsystem) - .approval_voting(DummySubsystem) - .gossip_support(DummySubsystem) - .dispute_coordinator(DummySubsystem) - .dispute_distribution(DummySubsystem) - .chain_selection(DummySubsystem) - .activation_external_listeners(Default::default()) - .span_per_active_leaf(Default::default()) - .active_leaves(Default::default()) - .supports_parachains(runtime_client) - .metrics(Metrics::register(registry)?) - .spawner(spawner); + let builder = + collator_overseer_builder(args).map_err(|e| RelayChainError::Application(e.into()))?; builder .build_with_connector(connector) @@ -175,7 +45,7 @@ fn build_overseer( } pub(crate) fn spawn_overseer( - overseer_args: CollatorOverseerGenArgs, + overseer_args: OverseerGenArgs, task_manager: &TaskManager, relay_chain_rpc_client: Arc, ) -> Result { diff --git a/cumulus/client/relay-chain-minimal-node/src/lib.rs b/cumulus/client/relay-chain-minimal-node/src/lib.rs index d121d2d3356765d9327fdaa0a8c0563c3917266f..6aea043713d873b77ecadc8b7efc7c7eb8c0039b 100644 --- a/cumulus/client/relay-chain-minimal-node/src/lib.rs +++ b/cumulus/client/relay-chain-minimal-node/src/lib.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use collator_overseer::{CollatorOverseerGenArgs, NewMinimalNode}; +use collator_overseer::NewMinimalNode; use cumulus_relay_chain_interface::{RelayChainError, RelayChainInterface, RelayChainResult}; use cumulus_relay_chain_rpc_interface::{RelayChainRpcClient, RelayChainRpcInterface, Url}; @@ -29,6 +29,7 @@ use polkadot_node_network_protocol::{ use polkadot_node_subsystem_util::metrics::prometheus::Registry; use polkadot_primitives::CollatorPair; +use polkadot_service::{overseer::OverseerGenArgs, IsParachainNode}; use sc_authority_discovery::Service as AuthorityDiscoveryService; use sc_network::{config::FullNetworkConfiguration, Event, NetworkEventStream, NetworkService}; @@ -54,6 +55,7 @@ fn build_authority_discovery_service( prometheus_registry: Option, ) -> AuthorityDiscoveryService { let auth_disc_publish_non_global_ips = config.network.allow_non_globals_in_dht; + let auth_disc_public_addresses = config.network.public_addresses.clone(); let authority_discovery_role = sc_authority_discovery::Role::Discover; let dht_event_stream = network.event_stream("authority-discovery").filter_map(|e| async move { match e { @@ -64,6 +66,7 @@ fn build_authority_discovery_service( let (worker, service) = sc_authority_discovery::new_worker_and_service_with_config( sc_authority_discovery::WorkerConfig { publish_non_global_ips: auth_disc_publish_non_global_ips, + public_addresses: auth_disc_public_addresses, // Require that authority discovery records are signed. strict_record_validation: true, ..Default::default() @@ -172,10 +175,10 @@ async fn new_minimal_relay_chain( } let genesis_hash = relay_chain_rpc_client.block_get_hash(Some(0)).await?.unwrap_or_default(); - let peer_set_protocol_names = + let peerset_protocol_names = PeerSetProtocolNames::new(genesis_hash, config.chain_spec.fork_id()); let is_authority = if role.is_authority() { IsAuthority::Yes } else { IsAuthority::No }; - let notification_services = peer_sets_info(is_authority, &peer_set_protocol_names) + let notification_services = peer_sets_info(is_authority, &peerset_protocol_names) .into_iter() .map(|(config, (peerset, service))| { net_config.add_notification_protocol(config); @@ -184,14 +187,14 @@ async fn new_minimal_relay_chain( .collect::>>(); let request_protocol_names = ReqProtocolNames::new(genesis_hash, config.chain_spec.fork_id()); - let (collation_req_receiver_v1, collation_req_receiver_v2, available_data_req_receiver) = + let (collation_req_v1_receiver, collation_req_v2_receiver, available_data_req_receiver) = build_request_response_protocol_receivers(&request_protocol_names, &mut net_config); let best_header = relay_chain_rpc_client .chain_get_header(None) .await? .ok_or_else(|| RelayChainError::RpcCallError("Unable to fetch best header".to_string()))?; - let (network, network_starter, sync_oracle) = build_collator_network( + let (network, network_starter, sync_service) = build_collator_network( &config, net_config, task_manager.spawn_handle(), @@ -208,19 +211,20 @@ async fn new_minimal_relay_chain( prometheus_registry.cloned(), ); - let overseer_args = CollatorOverseerGenArgs { + let overseer_args = OverseerGenArgs { runtime_client: relay_chain_rpc_client.clone(), network_service: network, - sync_oracle, + sync_service, authority_discovery_service, - collation_req_receiver_v1, - collation_req_receiver_v2, + collation_req_v1_receiver, + collation_req_v2_receiver, available_data_req_receiver, registry: prometheus_registry, spawner: task_manager.spawn_handle(), - collator_pair, + is_parachain_node: IsParachainNode::Collator(collator_pair), + overseer_message_channel_capacity_override: None, req_protocol_names: request_protocol_names, - peer_set_protocol_names, + peerset_protocol_names, notification_services, }; @@ -240,10 +244,10 @@ fn build_request_response_protocol_receivers( IncomingRequestReceiver, IncomingRequestReceiver, ) { - let (collation_req_receiver_v1, cfg) = + let (collation_req_v1_receiver, cfg) = IncomingRequest::get_config_receiver(request_protocol_names); config.add_request_response_protocol(cfg); - let (collation_req_receiver_v2, cfg) = + let (collation_req_v2_receiver, cfg) = IncomingRequest::get_config_receiver(request_protocol_names); config.add_request_response_protocol(cfg); let (available_data_req_receiver, cfg) = @@ -251,5 +255,5 @@ fn build_request_response_protocol_receivers( config.add_request_response_protocol(cfg); let cfg = Protocol::ChunkFetchingV1.get_outbound_only_config(request_protocol_names); config.add_request_response_protocol(cfg); - (collation_req_receiver_v1, collation_req_receiver_v2, available_data_req_receiver) + (collation_req_v1_receiver, collation_req_v2_receiver, available_data_req_receiver) } diff --git a/cumulus/client/relay-chain-minimal-node/src/network.rs b/cumulus/client/relay-chain-minimal-node/src/network.rs index 95785063c1aeb6649d7154fa39e4e111e226def3..7286fab7907cb6d475b81a4dfd97c5d001180873 100644 --- a/cumulus/client/relay-chain-minimal-node/src/network.rs +++ b/cumulus/client/relay-chain-minimal-node/src/network.rs @@ -40,7 +40,11 @@ pub(crate) fn build_collator_network( genesis_hash: Hash, best_header: Header, ) -> Result< - (Arc>, NetworkStarter, Box), + ( + Arc>, + NetworkStarter, + Arc, + ), Error, > { let protocol_id = config.protocol_id(); @@ -112,7 +116,7 @@ pub(crate) fn build_collator_network( let network_starter = NetworkStarter::new(network_start_tx); - Ok((network_service, network_starter, Box::new(SyncOracle {}))) + Ok((network_service, network_starter, Arc::new(SyncOracle {}))) } fn adjust_network_config_light_in_peers(config: &mut NetworkConfiguration) { diff --git a/cumulus/client/relay-chain-rpc-interface/src/lib.rs b/cumulus/client/relay-chain-rpc-interface/src/lib.rs index 96f8fc8b5563335eb7796bc8fd105fced15bc5e1..3a4c186e301eab295aa3befbd3c5549636fdb2c0 100644 --- a/cumulus/client/relay-chain-rpc-interface/src/lib.rs +++ b/cumulus/client/relay-chain-rpc-interface/src/lib.rs @@ -19,7 +19,7 @@ use core::time::Duration; use cumulus_primitives_core::{ relay_chain::{ CommittedCandidateReceipt, Hash as RelayHash, Header as RelayHeader, InboundHrmpMessage, - OccupiedCoreAssumption, SessionIndex, ValidatorId, + OccupiedCoreAssumption, SessionIndex, ValidationCodeHash, ValidatorId, }, InboundDownwardMessage, ParaId, PersistedValidationData, }; @@ -110,6 +110,17 @@ impl RelayChainInterface for RelayChainRpcInterface { .await } + async fn validation_code_hash( + &self, + hash: RelayHash, + para_id: ParaId, + occupied_core_assumption: OccupiedCoreAssumption, + ) -> RelayChainResult> { + self.rpc_client + .validation_code_hash(hash, para_id, occupied_core_assumption) + .await + } + async fn candidate_pending_availability( &self, hash: RelayHash, diff --git a/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs b/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs index b716feef1c998d66eba3c5ea28ee1dd98c4959e3..48d35dd3a55eee935fb27f2fbc60302e9ed3e76a 100644 --- a/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs +++ b/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs @@ -293,7 +293,8 @@ impl ReconnectingWebsocketWorker { /// listeners. If an error occurs during sending, the receiver has been closed and we remove /// the sender from the list. /// - Find a new valid RPC server to connect to in case the websocket connection is terminated. - /// If the worker is not able to connec to an RPC server from the list, the worker shuts down. + /// If the worker is not able to connect to an RPC server from the list, the worker shuts + /// down. pub async fn run(mut self) { let mut pending_requests = FuturesUnordered::new(); diff --git a/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs b/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs index a912997e947d19a4c1063c36b0f03dcf32bf12f1..8cf5ccf0c707e5f8e3529462041f75a17ea6c117 100644 --- a/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs +++ b/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs @@ -24,6 +24,7 @@ use jsonrpsee::{ }; use serde::de::DeserializeOwned; use serde_json::Value as JsonValue; +use std::collections::VecDeque; use tokio::sync::mpsc::Sender as TokioSender; use parity_scale_codec::{Decode, Encode}; @@ -34,10 +35,10 @@ use cumulus_primitives_core::{ slashing, vstaging::{ApprovalVotingParams, NodeFeatures}, BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, GroupRotationInfo, - Hash as RelayHash, Header as RelayHeader, InboundHrmpMessage, OccupiedCoreAssumption, - PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, - ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, + CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, + GroupRotationInfo, Hash as RelayHash, Header as RelayHeader, InboundHrmpMessage, + OccupiedCoreAssumption, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, + ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }, InboundDownwardMessage, ParaId, PersistedValidationData, }; @@ -647,6 +648,28 @@ impl RelayChainRpcClient { .await } + pub async fn parachain_host_claim_queue( + &self, + at: RelayHash, + ) -> Result>, RelayChainError> { + self.call_remote_runtime_function("ParachainHost_claim_queue", at, None::<()>) + .await + } + + pub async fn validation_code_hash( + &self, + at: RelayHash, + para_id: ParaId, + occupied_core_assumption: OccupiedCoreAssumption, + ) -> Result, RelayChainError> { + self.call_remote_runtime_function( + "ParachainHost_validation_code_hash", + at, + Some((para_id, occupied_core_assumption)), + ) + .await + } + fn send_register_message_to_worker( &self, message: RpcDispatcherMessage, diff --git a/cumulus/pallets/aura-ext/src/consensus_hook.rs b/cumulus/pallets/aura-ext/src/consensus_hook.rs index 089ab5c3198b98129e53e155dc4fcc009bf0e6d8..592029803391179785bb3b5606079d1bee6b553f 100644 --- a/cumulus/pallets/aura-ext/src/consensus_hook.rs +++ b/cumulus/pallets/aura-ext/src/consensus_hook.rs @@ -54,8 +54,8 @@ where let velocity = V.max(1); let relay_chain_slot = state_proof.read_slot().expect("failed to read relay chain slot"); - let (slot, authored) = pallet::Pallet::::slot_info() - .expect("slot info is inserted on block initialization"); + let (slot, authored) = + pallet::SlotInfo::::get().expect("slot info is inserted on block initialization"); // Convert relay chain timestamp. let relay_chain_timestamp = @@ -100,7 +100,7 @@ impl< /// is more recent than the included block itself. pub fn can_build_upon(included_hash: T::Hash, new_slot: Slot) -> bool { let velocity = V.max(1); - let (last_slot, authored_so_far) = match pallet::Pallet::::slot_info() { + let (last_slot, authored_so_far) = match pallet::SlotInfo::::get() { None => return true, Some(x) => x, }; diff --git a/cumulus/pallets/aura-ext/src/lib.rs b/cumulus/pallets/aura-ext/src/lib.rs index 34a41557152d8df38e0abc2d612151b2f74dc779..7ca84dff7c513c2406d3c0de7b9c0ac26048f508 100644 --- a/cumulus/pallets/aura-ext/src/lib.rs +++ b/cumulus/pallets/aura-ext/src/lib.rs @@ -63,14 +63,14 @@ pub mod pallet { impl Hooks> for Pallet { fn on_finalize(_: BlockNumberFor) { // Update to the latest AuRa authorities. - Authorities::::put(Aura::::authorities()); + Authorities::::put(pallet_aura::Authorities::::get()); } fn on_initialize(_: BlockNumberFor) -> Weight { // Fetch the authorities once to get them into the storage proof of the PoV. Authorities::::get(); - let new_slot = Aura::::current_slot(); + let new_slot = pallet_aura::CurrentSlot::::get(); let (new_slot, authored) = match SlotInfo::::get() { Some((slot, authored)) if slot == new_slot => (slot, authored + 1), @@ -103,7 +103,6 @@ pub mod pallet { /// /// Updated on each block initialization. #[pallet::storage] - #[pallet::getter(fn slot_info)] pub(crate) type SlotInfo = StorageValue<_, (Slot, u32), OptionQuery>; #[pallet::genesis_config] @@ -116,13 +115,7 @@ pub mod pallet { #[pallet::genesis_build] impl BuildGenesisConfig for GenesisConfig { fn build(&self) { - let authorities = Aura::::authorities(); - - assert!( - !authorities.is_empty(), - "AuRa authorities empty, maybe wrong order in `construct_runtime!`?", - ); - + let authorities = pallet_aura::Authorities::::get(); Authorities::::put(authorities); } } diff --git a/cumulus/pallets/collator-selection/Cargo.toml b/cumulus/pallets/collator-selection/Cargo.toml index 20f048b97d558962ea270ef51399f6d2905ab1a0..241a78466d61be3969930e031e1a52f4f3478482 100644 --- a/cumulus/pallets/collator-selection/Cargo.toml +++ b/cumulus/pallets/collator-selection/Cargo.toml @@ -77,5 +77,3 @@ try-runtime = [ "pallet-timestamp/try-runtime", "sp-runtime/try-runtime", ] - -experimental = ["pallet-aura/experimental"] diff --git a/cumulus/pallets/collator-selection/src/benchmarking.rs b/cumulus/pallets/collator-selection/src/benchmarking.rs index 2c40f4dd0eac4a8a2ad3aed7f162a4379ccdbc93..c6b600445282534951b2c63a6c2a87181688acd1 100644 --- a/cumulus/pallets/collator-selection/src/benchmarking.rs +++ b/cumulus/pallets/collator-selection/src/benchmarking.rs @@ -22,9 +22,7 @@ use super::*; #[allow(unused)] use crate::Pallet as CollatorSelection; use codec::Decode; -use frame_benchmarking::{ - account, impl_benchmark_test_suite, v2::*, whitelisted_caller, BenchmarkError, -}; +use frame_benchmarking::{account, v2::*, whitelisted_caller, BenchmarkError}; use frame_support::traits::{Currency, EnsureOrigin, Get, ReservableCurrency}; use frame_system::{pallet_prelude::BlockNumberFor, EventRecord, RawOrigin}; use pallet_authorship::EventHandler; @@ -88,23 +86,23 @@ fn register_validators(count: u32) -> Vec(count: u32) { let candidates = (0..count).map(|c| account("candidate", c, SEED)).collect::>(); - assert!(>::get() > 0u32.into(), "Bond cannot be zero!"); + assert!(CandidacyBond::::get() > 0u32.into(), "Bond cannot be zero!"); for who in candidates { - T::Currency::make_free_balance_be(&who, >::get() * 3u32.into()); + T::Currency::make_free_balance_be(&who, CandidacyBond::::get() * 3u32.into()); >::register_as_candidate(RawOrigin::Signed(who).into()).unwrap(); } } fn min_candidates() -> u32 { let min_collators = T::MinEligibleCollators::get(); - let invulnerable_length = >::get().len(); + let invulnerable_length = Invulnerables::::get().len(); min_collators.saturating_sub(invulnerable_length.try_into().unwrap()) } fn min_invulnerables() -> u32 { let min_collators = T::MinEligibleCollators::get(); - let candidates_length = >::decode_len() + let candidates_length = CandidateList::::decode_len() .unwrap_or_default() .try_into() .unwrap_or_default(); @@ -145,8 +143,8 @@ mod benchmarks { T::UpdateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; // need to fill up candidates - >::put(T::Currency::minimum_balance()); - >::put(c); + CandidacyBond::::put(T::Currency::minimum_balance()); + DesiredCandidates::::put(c); // get accounts and keys for the `c` candidates let mut candidates = (0..c).map(|cc| validator::(cc)).collect::>(); // add one more to the list. should not be in `b` (invulnerables) because it's the account @@ -161,15 +159,15 @@ mod benchmarks { } // ... and register them. for (who, _) in candidates.iter() { - let deposit = >::get(); + let deposit = CandidacyBond::::get(); T::Currency::make_free_balance_be(who, deposit * 1000_u32.into()); - >::try_mutate(|list| { + CandidateList::::try_mutate(|list| { list.try_push(CandidateInfo { who: who.clone(), deposit }).unwrap(); Ok::<(), BenchmarkError>(()) }) .unwrap(); T::Currency::reserve(who, deposit)?; - >::insert( + LastAuthoredBlock::::insert( who.clone(), frame_system::Pallet::::block_number() + T::KickThreshold::get(), ); @@ -180,7 +178,7 @@ mod benchmarks { invulnerables.sort(); let invulnerables: frame_support::BoundedVec<_, T::MaxInvulnerables> = frame_support::BoundedVec::try_from(invulnerables).unwrap(); - >::put(invulnerables); + Invulnerables::::put(invulnerables); #[extrinsic_call] _(origin as T::RuntimeOrigin, new_invulnerable.clone()); @@ -199,8 +197,8 @@ mod benchmarks { invulnerables.sort(); let invulnerables: frame_support::BoundedVec<_, T::MaxInvulnerables> = frame_support::BoundedVec::try_from(invulnerables).unwrap(); - >::put(invulnerables); - let to_remove = >::get().first().unwrap().clone(); + Invulnerables::::put(invulnerables); + let to_remove = Invulnerables::::get().first().unwrap().clone(); #[extrinsic_call] _(origin as T::RuntimeOrigin, to_remove.clone()); @@ -228,14 +226,14 @@ mod benchmarks { k: Linear<0, { T::MaxCandidates::get() }>, ) -> Result<(), BenchmarkError> { let initial_bond_amount: BalanceOf = T::Currency::minimum_balance() * 2u32.into(); - >::put(initial_bond_amount); + CandidacyBond::::put(initial_bond_amount); register_validators::(c); register_candidates::(c); let kicked = cmp::min(k, c); let origin = T::UpdateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let bond_amount = if k > 0 { - >::mutate(|candidates| { + CandidateList::::mutate(|candidates| { for info in candidates.iter_mut().skip(kicked as usize) { info.deposit = T::Currency::minimum_balance() * 3u32.into(); } @@ -256,13 +254,13 @@ mod benchmarks { fn update_bond( c: Linear<{ min_candidates::() + 1 }, { T::MaxCandidates::get() }>, ) -> Result<(), BenchmarkError> { - >::put(T::Currency::minimum_balance()); - >::put(c); + CandidacyBond::::put(T::Currency::minimum_balance()); + DesiredCandidates::::put(c); register_validators::(c); register_candidates::(c); - let caller = >::get()[0].who.clone(); + let caller = CandidateList::::get()[0].who.clone(); v2::whitelist!(caller); let bond_amount: BalanceOf = @@ -275,7 +273,7 @@ mod benchmarks { Event::CandidateBondUpdated { account_id: caller, deposit: bond_amount }.into(), ); assert!( - >::get().iter().last().unwrap().deposit == + CandidateList::::get().iter().last().unwrap().deposit == T::Currency::minimum_balance() * 2u32.into() ); Ok(()) @@ -285,8 +283,8 @@ mod benchmarks { // one. #[benchmark] fn register_as_candidate(c: Linear<1, { T::MaxCandidates::get() - 1 }>) { - >::put(T::Currency::minimum_balance()); - >::put(c + 1); + CandidacyBond::::put(T::Currency::minimum_balance()); + DesiredCandidates::::put(c + 1); register_validators::(c); register_candidates::(c); @@ -312,8 +310,8 @@ mod benchmarks { #[benchmark] fn take_candidate_slot(c: Linear<{ min_candidates::() + 1 }, { T::MaxCandidates::get() }>) { - >::put(T::Currency::minimum_balance()); - >::put(1); + CandidacyBond::::put(T::Currency::minimum_balance()); + DesiredCandidates::::put(1); register_validators::(c); register_candidates::(c); @@ -329,7 +327,7 @@ mod benchmarks { ) .unwrap(); - let target = >::get().iter().last().unwrap().who.clone(); + let target = CandidateList::::get().iter().last().unwrap().who.clone(); #[extrinsic_call] _(RawOrigin::Signed(caller.clone()), bond / 2u32.into(), target.clone()); @@ -343,13 +341,13 @@ mod benchmarks { // worse case is the last candidate leaving. #[benchmark] fn leave_intent(c: Linear<{ min_candidates::() + 1 }, { T::MaxCandidates::get() }>) { - >::put(T::Currency::minimum_balance()); - >::put(c); + CandidacyBond::::put(T::Currency::minimum_balance()); + DesiredCandidates::::put(c); register_validators::(c); register_candidates::(c); - let leaving = >::get().iter().last().unwrap().who.clone(); + let leaving = CandidateList::::get().iter().last().unwrap().who.clone(); v2::whitelist!(leaving); #[extrinsic_call] @@ -361,7 +359,7 @@ mod benchmarks { // worse case is paying a non-existing candidate account. #[benchmark] fn note_author() { - >::put(T::Currency::minimum_balance()); + CandidacyBond::::put(T::Currency::minimum_balance()); T::Currency::make_free_balance_be( &>::account_id(), T::Currency::minimum_balance() * 4u32.into(), @@ -387,8 +385,8 @@ mod benchmarks { r: Linear<1, { T::MaxCandidates::get() }>, c: Linear<1, { T::MaxCandidates::get() }>, ) { - >::put(T::Currency::minimum_balance()); - >::put(c); + CandidacyBond::::put(T::Currency::minimum_balance()); + DesiredCandidates::::put(c); frame_system::Pallet::::set_block_number(0u32.into()); register_validators::(c); @@ -396,7 +394,7 @@ mod benchmarks { let new_block: BlockNumberFor = T::KickThreshold::get(); let zero_block: BlockNumberFor = 0u32.into(); - let candidates: Vec = >::get() + let candidates: Vec = CandidateList::::get() .iter() .map(|candidate_info| candidate_info.who.clone()) .collect(); @@ -404,25 +402,25 @@ mod benchmarks { let non_removals = c.saturating_sub(r); for i in 0..c { - >::insert(candidates[i as usize].clone(), zero_block); + LastAuthoredBlock::::insert(candidates[i as usize].clone(), zero_block); } if non_removals > 0 { for i in 0..non_removals { - >::insert(candidates[i as usize].clone(), new_block); + LastAuthoredBlock::::insert(candidates[i as usize].clone(), new_block); } } else { for i in 0..c { - >::insert(candidates[i as usize].clone(), new_block); + LastAuthoredBlock::::insert(candidates[i as usize].clone(), new_block); } } let min_candidates = min_candidates::(); - let pre_length = >::decode_len().unwrap_or_default(); + let pre_length = CandidateList::::decode_len().unwrap_or_default(); frame_system::Pallet::::set_block_number(new_block); - let current_length: u32 = >::decode_len() + let current_length: u32 = CandidateList::::decode_len() .unwrap_or_default() .try_into() .unwrap_or_default(); @@ -436,12 +434,12 @@ mod benchmarks { // candidates > removals and remaining candidates > min candidates // => remaining candidates should be shorter than before removal, i.e. some were // actually removed. - assert!(>::decode_len().unwrap_or_default() < pre_length); + assert!(CandidateList::::decode_len().unwrap_or_default() < pre_length); } else if c > r && non_removals < min_candidates { // candidates > removals and remaining candidates would be less than min candidates // => remaining candidates should equal min candidates, i.e. some were removed up to // the minimum, but then any more were "forced" to stay in candidates. - let current_length: u32 = >::decode_len() + let current_length: u32 = CandidateList::::decode_len() .unwrap_or_default() .try_into() .unwrap_or_default(); @@ -449,7 +447,7 @@ mod benchmarks { } else { // removals >= candidates, non removals must == 0 // can't remove more than exist - assert!(>::decode_len().unwrap_or_default() == pre_length); + assert!(CandidateList::::decode_len().unwrap_or_default() == pre_length); } } diff --git a/cumulus/pallets/collator-selection/src/lib.rs b/cumulus/pallets/collator-selection/src/lib.rs index 7449f4d68c7eacc8b07fa45f2f991c13f3d5713b..84bde5c9fac9f640692458f02b37f0ee108a734f 100644 --- a/cumulus/pallets/collator-selection/src/lib.rs +++ b/cumulus/pallets/collator-selection/src/lib.rs @@ -118,7 +118,7 @@ pub mod pallet { use sp_staking::SessionIndex; use sp_std::vec::Vec; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); type BalanceOf = @@ -196,7 +196,6 @@ pub mod pallet { /// The invulnerable, permissioned collators. This list must be sorted. #[pallet::storage] - #[pallet::getter(fn invulnerables)] pub type Invulnerables = StorageValue<_, BoundedVec, ValueQuery>; @@ -206,7 +205,6 @@ pub mod pallet { /// This list is sorted in ascending order by deposit and when the deposits are equal, the least /// recently updated is considered greater. #[pallet::storage] - #[pallet::getter(fn candidate_list)] pub type CandidateList = StorageValue< _, BoundedVec>, T::MaxCandidates>, @@ -215,7 +213,6 @@ pub mod pallet { /// Last block authored by collator. #[pallet::storage] - #[pallet::getter(fn last_authored_block)] pub type LastAuthoredBlock = StorageMap<_, Twox64Concat, T::AccountId, BlockNumberFor, ValueQuery>; @@ -223,14 +220,12 @@ pub mod pallet { /// /// This should ideally always be less than [`Config::MaxCandidates`] for weights to be correct. #[pallet::storage] - #[pallet::getter(fn desired_candidates)] pub type DesiredCandidates = StorageValue<_, u32, ValueQuery>; /// Fixed amount to deposit to become a collator. /// /// When a collator calls `leave_intent` they immediately receive the deposit back. #[pallet::storage] - #[pallet::getter(fn candidacy_bond)] pub type CandidacyBond = StorageValue<_, BalanceOf, ValueQuery>; #[pallet::genesis_config] @@ -263,9 +258,9 @@ pub mod pallet { bounded_invulnerables.sort(); - >::put(self.desired_candidates); - >::put(self.candidacy_bond); - >::put(bounded_invulnerables); + DesiredCandidates::::put(self.desired_candidates); + CandidacyBond::::put(self.candidacy_bond); + Invulnerables::::put(bounded_invulnerables); } } @@ -424,7 +419,7 @@ pub mod pallet { // Invulnerables must be sorted for removal. bounded_invulnerables.sort(); - >::put(&bounded_invulnerables); + Invulnerables::::put(&bounded_invulnerables); Self::deposit_event(Event::NewInvulnerables { invulnerables: bounded_invulnerables.to_vec(), }); @@ -448,7 +443,7 @@ pub mod pallet { if max > T::MaxCandidates::get() { log::warn!("max > T::MaxCandidates; you might need to run benchmarks again"); } - >::put(max); + DesiredCandidates::::put(max); Self::deposit_event(Event::NewDesiredCandidates { desired_candidates: max }); Ok(().into()) } @@ -470,17 +465,17 @@ pub mod pallet { bond: BalanceOf, ) -> DispatchResultWithPostInfo { T::UpdateOrigin::ensure_origin(origin)?; - let bond_increased = >::mutate(|old_bond| -> bool { + let bond_increased = CandidacyBond::::mutate(|old_bond| -> bool { let bond_increased = *old_bond < bond; *old_bond = bond; bond_increased }); - let initial_len = >::decode_len().unwrap_or_default(); + let initial_len = CandidateList::::decode_len().unwrap_or_default(); let kicked = (bond_increased && initial_len > 0) .then(|| { // Closure below returns the number of candidates which were kicked because // their deposits were lower than the new candidacy bond. - >::mutate(|candidates| -> usize { + CandidateList::::mutate(|candidates| -> usize { let first_safe_candidate = candidates .iter() .position(|candidate| candidate.deposit >= bond) @@ -488,14 +483,18 @@ pub mod pallet { let kicked_candidates = candidates.drain(..first_safe_candidate); for candidate in kicked_candidates { T::Currency::unreserve(&candidate.who, candidate.deposit); - >::remove(candidate.who); + LastAuthoredBlock::::remove(candidate.who); } first_safe_candidate }) }) .unwrap_or_default(); Self::deposit_event(Event::NewCandidacyBond { bond_amount: bond }); - Ok(Some(T::WeightInfo::set_candidacy_bond(initial_len as u32, kicked as u32)).into()) + Ok(Some(T::WeightInfo::set_candidacy_bond( + bond_increased.then(|| initial_len as u32).unwrap_or_default(), + kicked as u32, + )) + .into()) } /// Register this account as a collator candidate. The account must (a) already have @@ -508,12 +507,12 @@ pub mod pallet { let who = ensure_signed(origin)?; // ensure we are below limit. - let length: u32 = >::decode_len() + let length: u32 = CandidateList::::decode_len() .unwrap_or_default() .try_into() .unwrap_or_default(); ensure!(length < T::MaxCandidates::get(), Error::::TooManyCandidates); - ensure!(!Self::invulnerables().contains(&who), Error::::AlreadyInvulnerable); + ensure!(!Invulnerables::::get().contains(&who), Error::::AlreadyInvulnerable); let validator_key = T::ValidatorIdOf::convert(who.clone()) .ok_or(Error::::NoAssociatedValidatorId)?; @@ -522,15 +521,15 @@ pub mod pallet { Error::::ValidatorNotRegistered ); - let deposit = Self::candidacy_bond(); + let deposit = CandidacyBond::::get(); // First authored block is current block plus kick threshold to handle session delay - >::try_mutate(|candidates| -> Result<(), DispatchError> { + CandidateList::::try_mutate(|candidates| -> Result<(), DispatchError> { ensure!( !candidates.iter().any(|candidate_info| candidate_info.who == who), Error::::AlreadyCandidate ); T::Currency::reserve(&who, deposit)?; - >::insert( + LastAuthoredBlock::::insert( who.clone(), frame_system::Pallet::::block_number() + T::KickThreshold::get(), ); @@ -560,7 +559,7 @@ pub mod pallet { Self::eligible_collators() > T::MinEligibleCollators::get(), Error::::TooFewEligibleCollators ); - let length = >::decode_len().unwrap_or_default(); + let length = CandidateList::::decode_len().unwrap_or_default(); // Do remove their last authored block. Self::try_remove_candidate(&who, true)?; @@ -590,7 +589,7 @@ pub mod pallet { Error::::ValidatorNotRegistered ); - >::try_mutate(|invulnerables| -> DispatchResult { + Invulnerables::::try_mutate(|invulnerables| -> DispatchResult { match invulnerables.binary_search(&who) { Ok(_) => return Err(Error::::AlreadyInvulnerable)?, Err(pos) => invulnerables @@ -611,7 +610,7 @@ pub mod pallet { .unwrap_or_default() .try_into() .unwrap_or(T::MaxInvulnerables::get().saturating_sub(1)), - >::decode_len() + CandidateList::::decode_len() .unwrap_or_default() .try_into() .unwrap_or(T::MaxCandidates::get()), @@ -634,7 +633,7 @@ pub mod pallet { Error::::TooFewEligibleCollators ); - >::try_mutate(|invulnerables| -> DispatchResult { + Invulnerables::::try_mutate(|invulnerables| -> DispatchResult { let pos = invulnerables.binary_search(&who).map_err(|_| Error::::NotInvulnerable)?; invulnerables.remove(pos); @@ -659,12 +658,12 @@ pub mod pallet { new_deposit: BalanceOf, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - ensure!(new_deposit >= >::get(), Error::::DepositTooLow); + ensure!(new_deposit >= CandidacyBond::::get(), Error::::DepositTooLow); // The function below will try to mutate the `CandidateList` entry for the caller to // update their deposit to the new value of `new_deposit`. The return value is the // position of the entry in the list, used for weight calculation. let length = - >::try_mutate(|candidates| -> Result { + CandidateList::::try_mutate(|candidates| -> Result { let idx = candidates .iter() .position(|candidate_info| candidate_info.who == who) @@ -678,7 +677,7 @@ pub mod pallet { } else if new_deposit < old_deposit { // Casting `u32` to `usize` should be safe on all machines running this. ensure!( - idx.saturating_add(>::get() as usize) < + idx.saturating_add(DesiredCandidates::::get() as usize) < candidate_count, Error::::InvalidUnreserve ); @@ -723,8 +722,8 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - ensure!(!Self::invulnerables().contains(&who), Error::::AlreadyInvulnerable); - ensure!(deposit >= Self::candidacy_bond(), Error::::InsufficientBond); + ensure!(!Invulnerables::::get().contains(&who), Error::::AlreadyInvulnerable); + ensure!(deposit >= CandidacyBond::::get(), Error::::InsufficientBond); let validator_key = T::ValidatorIdOf::convert(who.clone()) .ok_or(Error::::NoAssociatedValidatorId)?; @@ -733,12 +732,12 @@ pub mod pallet { Error::::ValidatorNotRegistered ); - let length = >::decode_len().unwrap_or_default(); + let length = CandidateList::::decode_len().unwrap_or_default(); // The closure below iterates through all elements of the candidate list to ensure that // the caller isn't already a candidate and to find the target it's trying to replace in // the list. The return value is a tuple of the position of the candidate to be replaced // in the list along with its candidate information. - let target_info = >::try_mutate( + let target_info = CandidateList::::try_mutate( |candidates| -> Result>, DispatchError> { // Find the position in the list of the candidate that is being replaced. let mut target_info_idx = None; @@ -783,8 +782,8 @@ pub mod pallet { )?; T::Currency::reserve(&who, deposit)?; T::Currency::unreserve(&target_info.who, target_info.deposit); - >::remove(target_info.who.clone()); - >::insert( + LastAuthoredBlock::::remove(target_info.who.clone()); + LastAuthoredBlock::::insert( who.clone(), frame_system::Pallet::::block_number() + T::KickThreshold::get(), ); @@ -803,7 +802,7 @@ pub mod pallet { /// Return the total number of accounts that are eligible collators (candidates and /// invulnerables). fn eligible_collators() -> u32 { - >::decode_len() + CandidateList::::decode_len() .unwrap_or_default() .saturating_add(Invulnerables::::decode_len().unwrap_or_default()) .try_into() @@ -815,7 +814,7 @@ pub mod pallet { who: &T::AccountId, remove_last_authored: bool, ) -> Result<(), DispatchError> { - >::try_mutate(|candidates| -> Result<(), DispatchError> { + CandidateList::::try_mutate(|candidates| -> Result<(), DispatchError> { let idx = candidates .iter() .position(|candidate_info| candidate_info.who == *who) @@ -824,7 +823,7 @@ pub mod pallet { T::Currency::unreserve(who, deposit); candidates.remove(idx); if remove_last_authored { - >::remove(who.clone()) + LastAuthoredBlock::::remove(who.clone()) }; Ok(()) })?; @@ -837,10 +836,10 @@ pub mod pallet { /// This is done on the fly, as frequent as we are told to do so, as the session manager. pub fn assemble_collators() -> Vec { // Casting `u32` to `usize` should be safe on all machines running this. - let desired_candidates = >::get() as usize; - let mut collators = Self::invulnerables().to_vec(); + let desired_candidates = DesiredCandidates::::get() as usize; + let mut collators = Invulnerables::::get().to_vec(); collators.extend( - >::get() + CandidateList::::get() .iter() .rev() .cloned() @@ -861,10 +860,10 @@ pub mod pallet { candidates .into_iter() .filter_map(|c| { - let last_block = >::get(c.clone()); + let last_block = LastAuthoredBlock::::get(c.clone()); let since_last = now.saturating_sub(last_block); - let is_invulnerable = Self::invulnerables().contains(&c); + let is_invulnerable = Invulnerables::::get().contains(&c); let is_lazy = since_last >= kick_threshold; if is_invulnerable { @@ -903,7 +902,7 @@ pub mod pallet { /// or equal to the minimum number of eligible collators. #[cfg(any(test, feature = "try-runtime"))] pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { - let desired_candidates = >::get(); + let desired_candidates = DesiredCandidates::::get(); frame_support::ensure!( desired_candidates <= T::MaxCandidates::get(), @@ -935,7 +934,7 @@ pub mod pallet { // `reward` is half of pot account minus ED, this should never fail. let _success = T::Currency::transfer(&pot, &author, reward, KeepAlive); debug_assert!(_success.is_ok()); - >::insert(author, frame_system::Pallet::::block_number()); + LastAuthoredBlock::::insert(author, frame_system::Pallet::::block_number()); frame_system::Pallet::::register_extra_weight_unchecked( T::WeightInfo::note_author(), @@ -956,12 +955,12 @@ pub mod pallet { // The `expect` below is safe because the list is a `BoundedVec` with a max size of // `T::MaxCandidates`, which is a `u32`. When `decode_len` returns `Some(len)`, `len` // must be valid and at most `u32::MAX`, which must always be able to convert to `u32`. - let candidates_len_before: u32 = >::decode_len() + let candidates_len_before: u32 = CandidateList::::decode_len() .unwrap_or_default() .try_into() .expect("length is at most `T::MaxCandidates`, so it must fit in `u32`; qed"); let active_candidates_count = Self::kick_stale_candidates( - >::get() + CandidateList::::get() .iter() .map(|candidate_info| candidate_info.who.clone()), ); diff --git a/cumulus/pallets/collator-selection/src/migration.rs b/cumulus/pallets/collator-selection/src/migration.rs index 58b4cc5b06a1ab7da17b31ad3c457fd992c59136..5dc2fba4279a9b04dc9f1866fb2c36ea1abde80c 100644 --- a/cumulus/pallets/collator-selection/src/migration.rs +++ b/cumulus/pallets/collator-selection/src/migration.rs @@ -31,10 +31,10 @@ pub mod v1 { pub struct MigrateToV1(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { fn on_runtime_upgrade() -> Weight { - let onchain_version = Pallet::::on_chain_storage_version(); - if onchain_version == 0 { + let on_chain_version = Pallet::::on_chain_storage_version(); + if on_chain_version == 0 { let invulnerables_len = Invulnerables::::get().to_vec().len(); - >::mutate(|invulnerables| { + Invulnerables::::mutate(|invulnerables| { invulnerables.sort(); }); @@ -45,7 +45,7 @@ pub mod v1 { invulnerables_len, ); // Similar complexity to `set_invulnerables` (put storage value) - // Plus 1 read for length, 1 read for `onchain_version`, 1 write to put version + // Plus 1 read for length, 1 read for `on_chain_version`, 1 write to put version T::WeightInfo::set_invulnerables(invulnerables_len as u32) .saturating_add(T::DbWeight::get().reads_writes(2, 1)) } else { @@ -83,8 +83,8 @@ pub mod v1 { "after migration, there should be the same number of invulnerables" ); - let onchain_version = Pallet::::on_chain_storage_version(); - frame_support::ensure!(onchain_version >= 1, "must_upgrade"); + let on_chain_version = Pallet::::on_chain_storage_version(); + frame_support::ensure!(on_chain_version >= 1, "must_upgrade"); Ok(()) } diff --git a/cumulus/pallets/collator-selection/src/mock.rs b/cumulus/pallets/collator-selection/src/mock.rs index fe41e7318bcdafa025ec33a74c9651d664b03be2..4a440dfe1e92f26685415d260e0af9b1eb56a9a4 100644 --- a/cumulus/pallets/collator-selection/src/mock.rs +++ b/cumulus/pallets/collator-selection/src/mock.rs @@ -50,7 +50,7 @@ parameter_types! { pub const SS58Prefix: u8 = 42; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); @@ -125,7 +125,6 @@ impl pallet_aura::Config for Test { type MaxAuthorities = ConstU32<100_000>; type DisabledValidators = (); type AllowMultipleBlocksPerSlot = ConstBool; - #[cfg(feature = "experimental")] type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; } diff --git a/cumulus/pallets/collator-selection/src/tests.rs b/cumulus/pallets/collator-selection/src/tests.rs index ed2044ccdfad7def46ab92180e9b177643ac8679..e8b2a4e146a2f95a1996e81f185f46c853f869a7 100644 --- a/cumulus/pallets/collator-selection/src/tests.rs +++ b/cumulus/pallets/collator-selection/src/tests.rs @@ -14,7 +14,10 @@ // limitations under the License. use crate as collator_selection; -use crate::{mock::*, CandidateInfo, Error}; +use crate::{ + mock::*, CandidacyBond, CandidateInfo, CandidateList, DesiredCandidates, Error, Invulnerables, + LastAuthoredBlock, +}; use frame_support::{ assert_noop, assert_ok, traits::{Currency, OnInitialize}, @@ -25,12 +28,12 @@ use sp_runtime::{testing::UintAuthorityId, traits::BadOrigin, BuildStorage}; #[test] fn basic_setup_works() { new_test_ext().execute_with(|| { - assert_eq!(CollatorSelection::desired_candidates(), 2); - assert_eq!(CollatorSelection::candidacy_bond(), 10); + assert_eq!(DesiredCandidates::::get(), 2); + assert_eq!(CandidacyBond::::get(), 10); - assert_eq!(>::get().iter().count(), 0); + assert_eq!(CandidateList::::get().iter().count(), 0); // genesis should sort input - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(Invulnerables::::get(), vec![1, 2]); }); } @@ -43,7 +46,7 @@ fn it_should_set_invulnerables() { new_set.clone() )); new_set.sort(); - assert_eq!(CollatorSelection::invulnerables(), new_set); + assert_eq!(Invulnerables::::get(), new_set); // cannot set with non-root. assert_noop!( @@ -56,7 +59,7 @@ fn it_should_set_invulnerables() { #[test] fn it_should_set_invulnerables_even_with_some_invalid() { new_test_ext().execute_with(|| { - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(Invulnerables::::get(), vec![1, 2]); let new_with_invalid = vec![1, 4, 3, 42, 2]; assert_ok!(CollatorSelection::set_invulnerables( @@ -65,7 +68,7 @@ fn it_should_set_invulnerables_even_with_some_invalid() { )); // should succeed and order them, but not include 42 - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2, 3, 4]); + assert_eq!(Invulnerables::::get(), vec![1, 2, 3, 4]); }); } @@ -73,7 +76,7 @@ fn it_should_set_invulnerables_even_with_some_invalid() { fn add_invulnerable_works() { new_test_ext().execute_with(|| { initialize_to_block(1); - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(Invulnerables::::get(), vec![1, 2]); let new = 3; // function runs @@ -93,7 +96,7 @@ fn add_invulnerable_works() { ); // new element is now part of the invulnerables list - assert!(CollatorSelection::invulnerables().to_vec().contains(&new)); + assert!(Invulnerables::::get().to_vec().contains(&new)); // cannot add with non-root assert_noop!(CollatorSelection::add_invulnerable(RuntimeOrigin::signed(1), new), BadOrigin); @@ -113,7 +116,7 @@ fn add_invulnerable_works() { #[test] fn invulnerable_limit_works() { new_test_ext().execute_with(|| { - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(Invulnerables::::get(), vec![1, 2]); // MaxInvulnerables: u32 = 20 for ii in 3..=21 { @@ -140,7 +143,7 @@ fn invulnerable_limit_works() { } } let expected: Vec = (1..=20).collect(); - assert_eq!(CollatorSelection::invulnerables(), expected); + assert_eq!(Invulnerables::::get(), expected); // Cannot set too many Invulnerables let too_many_invulnerables: Vec = (1..=21).collect(); @@ -151,7 +154,7 @@ fn invulnerable_limit_works() { ), Error::::TooManyInvulnerables ); - assert_eq!(CollatorSelection::invulnerables(), expected); + assert_eq!(Invulnerables::::get(), expected); }); } @@ -159,7 +162,7 @@ fn invulnerable_limit_works() { fn remove_invulnerable_works() { new_test_ext().execute_with(|| { initialize_to_block(1); - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(Invulnerables::::get(), vec![1, 2]); assert_ok!(CollatorSelection::add_invulnerable( RuntimeOrigin::signed(RootAccount::get()), @@ -170,7 +173,7 @@ fn remove_invulnerable_works() { 3 )); - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2, 3, 4]); + assert_eq!(Invulnerables::::get(), vec![1, 2, 3, 4]); assert_ok!(CollatorSelection::remove_invulnerable( RuntimeOrigin::signed(RootAccount::get()), @@ -180,7 +183,7 @@ fn remove_invulnerable_works() { System::assert_last_event(RuntimeEvent::CollatorSelection( crate::Event::InvulnerableRemoved { account_id: 2 }, )); - assert_eq!(CollatorSelection::invulnerables(), vec![1, 3, 4]); + assert_eq!(Invulnerables::::get(), vec![1, 3, 4]); // cannot remove invulnerable not in the list assert_noop!( @@ -200,11 +203,11 @@ fn remove_invulnerable_works() { fn candidate_to_invulnerable_works() { new_test_ext().execute_with(|| { initialize_to_block(1); - assert_eq!(CollatorSelection::desired_candidates(), 2); - assert_eq!(CollatorSelection::candidacy_bond(), 10); + assert_eq!(DesiredCandidates::::get(), 2); + assert_eq!(CandidacyBond::::get(), 10); - assert_eq!(>::get().iter().count(), 0); - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(CandidateList::::get().iter().count(), 0); + assert_eq!(Invulnerables::::get(), vec![1, 2]); assert_eq!(Balances::free_balance(3), 100); assert_eq!(Balances::free_balance(4), 100); @@ -225,9 +228,9 @@ fn candidate_to_invulnerable_works() { System::assert_has_event(RuntimeEvent::CollatorSelection( crate::Event::InvulnerableAdded { account_id: 3 }, )); - assert!(CollatorSelection::invulnerables().to_vec().contains(&3)); + assert!(Invulnerables::::get().to_vec().contains(&3)); assert_eq!(Balances::free_balance(3), 100); - assert_eq!(>::get().iter().count(), 1); + assert_eq!(CandidateList::::get().iter().count(), 1); assert_ok!(CollatorSelection::add_invulnerable( RuntimeOrigin::signed(RootAccount::get()), @@ -239,10 +242,10 @@ fn candidate_to_invulnerable_works() { System::assert_has_event(RuntimeEvent::CollatorSelection( crate::Event::InvulnerableAdded { account_id: 4 }, )); - assert!(CollatorSelection::invulnerables().to_vec().contains(&4)); + assert!(Invulnerables::::get().to_vec().contains(&4)); assert_eq!(Balances::free_balance(4), 100); - assert_eq!(>::get().iter().count(), 0); + assert_eq!(CandidateList::::get().iter().count(), 0); }); } @@ -250,14 +253,14 @@ fn candidate_to_invulnerable_works() { fn set_desired_candidates_works() { new_test_ext().execute_with(|| { // given - assert_eq!(CollatorSelection::desired_candidates(), 2); + assert_eq!(DesiredCandidates::::get(), 2); // can set assert_ok!(CollatorSelection::set_desired_candidates( RuntimeOrigin::signed(RootAccount::get()), 7 )); - assert_eq!(CollatorSelection::desired_candidates(), 7); + assert_eq!(DesiredCandidates::::get(), 7); // rejects bad origin assert_noop!( @@ -271,16 +274,16 @@ fn set_desired_candidates_works() { fn set_candidacy_bond_empty_candidate_list() { new_test_ext().execute_with(|| { // given - assert_eq!(CollatorSelection::candidacy_bond(), 10); - assert!(>::get().is_empty()); + assert_eq!(CandidacyBond::::get(), 10); + assert!(CandidateList::::get().is_empty()); // can decrease without candidates assert_ok!(CollatorSelection::set_candidacy_bond( RuntimeOrigin::signed(RootAccount::get()), 7 )); - assert_eq!(CollatorSelection::candidacy_bond(), 7); - assert!(>::get().is_empty()); + assert_eq!(CandidacyBond::::get(), 7); + assert!(CandidateList::::get().is_empty()); // rejects bad origin. assert_noop!(CollatorSelection::set_candidacy_bond(RuntimeOrigin::signed(1), 8), BadOrigin); @@ -290,8 +293,8 @@ fn set_candidacy_bond_empty_candidate_list() { RuntimeOrigin::signed(RootAccount::get()), 20 )); - assert!(>::get().is_empty()); - assert_eq!(CollatorSelection::candidacy_bond(), 20); + assert!(CandidateList::::get().is_empty()); + assert_eq!(CandidacyBond::::get(), 20); }); } @@ -299,36 +302,36 @@ fn set_candidacy_bond_empty_candidate_list() { fn set_candidacy_bond_with_one_candidate() { new_test_ext().execute_with(|| { // given - assert_eq!(CollatorSelection::candidacy_bond(), 10); - assert!(>::get().is_empty()); + assert_eq!(CandidacyBond::::get(), 10); + assert!(CandidateList::::get().is_empty()); let candidate_3 = CandidateInfo { who: 3, deposit: 10 }; assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3))); - assert_eq!(>::get(), vec![candidate_3.clone()]); + assert_eq!(CandidateList::::get(), vec![candidate_3.clone()]); // can decrease with one candidate assert_ok!(CollatorSelection::set_candidacy_bond( RuntimeOrigin::signed(RootAccount::get()), 7 )); - assert_eq!(CollatorSelection::candidacy_bond(), 7); - assert_eq!(>::get(), vec![candidate_3.clone()]); + assert_eq!(CandidacyBond::::get(), 7); + assert_eq!(CandidateList::::get(), vec![candidate_3.clone()]); // can increase up to initial deposit assert_ok!(CollatorSelection::set_candidacy_bond( RuntimeOrigin::signed(RootAccount::get()), 10 )); - assert_eq!(CollatorSelection::candidacy_bond(), 10); - assert_eq!(>::get(), vec![candidate_3.clone()]); + assert_eq!(CandidacyBond::::get(), 10); + assert_eq!(CandidateList::::get(), vec![candidate_3.clone()]); // can increase past initial deposit, should kick existing candidate assert_ok!(CollatorSelection::set_candidacy_bond( RuntimeOrigin::signed(RootAccount::get()), 20 )); - assert!(>::get().is_empty()); + assert!(CandidateList::::get().is_empty()); }); } @@ -336,8 +339,8 @@ fn set_candidacy_bond_with_one_candidate() { fn set_candidacy_bond_with_many_candidates_same_deposit() { new_test_ext().execute_with(|| { // given - assert_eq!(CollatorSelection::candidacy_bond(), 10); - assert!(>::get().is_empty()); + assert_eq!(CandidacyBond::::get(), 10); + assert!(CandidateList::::get().is_empty()); let candidate_3 = CandidateInfo { who: 3, deposit: 10 }; let candidate_4 = CandidateInfo { who: 4, deposit: 10 }; @@ -347,7 +350,7 @@ fn set_candidacy_bond_with_many_candidates_same_deposit() { assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4))); assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5))); assert_eq!( - >::get(), + CandidateList::::get(), vec![candidate_5.clone(), candidate_4.clone(), candidate_3.clone()] ); @@ -356,9 +359,9 @@ fn set_candidacy_bond_with_many_candidates_same_deposit() { RuntimeOrigin::signed(RootAccount::get()), 7 )); - assert_eq!(CollatorSelection::candidacy_bond(), 7); + assert_eq!(CandidacyBond::::get(), 7); assert_eq!( - >::get(), + CandidateList::::get(), vec![candidate_5.clone(), candidate_4.clone(), candidate_3.clone()] ); @@ -367,9 +370,9 @@ fn set_candidacy_bond_with_many_candidates_same_deposit() { RuntimeOrigin::signed(RootAccount::get()), 10 )); - assert_eq!(CollatorSelection::candidacy_bond(), 10); + assert_eq!(CandidacyBond::::get(), 10); assert_eq!( - >::get(), + CandidateList::::get(), vec![candidate_5.clone(), candidate_4.clone(), candidate_3.clone()] ); @@ -378,7 +381,7 @@ fn set_candidacy_bond_with_many_candidates_same_deposit() { RuntimeOrigin::signed(RootAccount::get()), 20 )); - assert!(>::get().is_empty()); + assert!(CandidateList::::get().is_empty()); }); } @@ -386,8 +389,8 @@ fn set_candidacy_bond_with_many_candidates_same_deposit() { fn set_candidacy_bond_with_many_candidates_different_deposits() { new_test_ext().execute_with(|| { // given - assert_eq!(CollatorSelection::candidacy_bond(), 10); - assert!(>::get().is_empty()); + assert_eq!(CandidacyBond::::get(), 10); + assert!(CandidateList::::get().is_empty()); let candidate_3 = CandidateInfo { who: 3, deposit: 10 }; let candidate_4 = CandidateInfo { who: 4, deposit: 20 }; @@ -399,7 +402,7 @@ fn set_candidacy_bond_with_many_candidates_different_deposits() { assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 30)); assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 20)); assert_eq!( - >::get(), + CandidateList::::get(), vec![candidate_3.clone(), candidate_4.clone(), candidate_5.clone()] ); @@ -408,9 +411,9 @@ fn set_candidacy_bond_with_many_candidates_different_deposits() { RuntimeOrigin::signed(RootAccount::get()), 7 )); - assert_eq!(CollatorSelection::candidacy_bond(), 7); + assert_eq!(CandidacyBond::::get(), 7); assert_eq!( - >::get(), + CandidateList::::get(), vec![candidate_3.clone(), candidate_4.clone(), candidate_5.clone()] ); // can increase up to initial deposit @@ -418,9 +421,9 @@ fn set_candidacy_bond_with_many_candidates_different_deposits() { RuntimeOrigin::signed(RootAccount::get()), 10 )); - assert_eq!(CollatorSelection::candidacy_bond(), 10); + assert_eq!(CandidacyBond::::get(), 10); assert_eq!( - >::get(), + CandidateList::::get(), vec![candidate_3.clone(), candidate_4.clone(), candidate_5.clone()] ); @@ -429,27 +432,24 @@ fn set_candidacy_bond_with_many_candidates_different_deposits() { RuntimeOrigin::signed(RootAccount::get()), 20 )); - assert_eq!(CollatorSelection::candidacy_bond(), 20); - assert_eq!( - >::get(), - vec![candidate_4.clone(), candidate_5.clone()] - ); + assert_eq!(CandidacyBond::::get(), 20); + assert_eq!(CandidateList::::get(), vec![candidate_4.clone(), candidate_5.clone()]); // can increase past 4's deposit, should kick 4 assert_ok!(CollatorSelection::set_candidacy_bond( RuntimeOrigin::signed(RootAccount::get()), 25 )); - assert_eq!(CollatorSelection::candidacy_bond(), 25); - assert_eq!(>::get(), vec![candidate_5.clone()]); + assert_eq!(CandidacyBond::::get(), 25); + assert_eq!(CandidateList::::get(), vec![candidate_5.clone()]); // lowering the minimum deposit should have no effect assert_ok!(CollatorSelection::set_candidacy_bond( RuntimeOrigin::signed(RootAccount::get()), 5 )); - assert_eq!(CollatorSelection::candidacy_bond(), 5); - assert_eq!(>::get(), vec![candidate_5.clone()]); + assert_eq!(CandidacyBond::::get(), 5); + assert_eq!(CandidateList::::get(), vec![candidate_5.clone()]); // add 3 and 4 back but with higher deposits than minimum assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3))); @@ -457,7 +457,7 @@ fn set_candidacy_bond_with_many_candidates_different_deposits() { assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 10)); assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 20)); assert_eq!( - >::get(), + CandidateList::::get(), vec![candidate_3.clone(), candidate_4.clone(), candidate_5.clone()] ); @@ -467,8 +467,8 @@ fn set_candidacy_bond_with_many_candidates_different_deposits() { RuntimeOrigin::signed(RootAccount::get()), 40 )); - assert_eq!(CollatorSelection::candidacy_bond(), 40); - assert!(>::get().is_empty()); + assert_eq!(CandidacyBond::::get(), 40); + assert!(CandidateList::::get().is_empty()); }); } @@ -500,8 +500,8 @@ fn cannot_register_candidate_if_too_many() { #[test] fn cannot_unregister_candidate_if_too_few() { new_test_ext().execute_with(|| { - assert_eq!(>::get().iter().count(), 0); - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(CandidateList::::get().iter().count(), 0); + assert_eq!(Invulnerables::::get(), vec![1, 2]); assert_ok!(CollatorSelection::remove_invulnerable( RuntimeOrigin::signed(RootAccount::get()), 1 @@ -532,7 +532,7 @@ fn cannot_unregister_candidate_if_too_few() { #[test] fn cannot_register_as_candidate_if_invulnerable() { new_test_ext().execute_with(|| { - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(Invulnerables::::get(), vec![1, 2]); // can't 1 because it is invulnerable. assert_noop!( @@ -561,10 +561,10 @@ fn cannot_register_dupe_candidate() { // tuple of (id, deposit). let addition = CandidateInfo { who: 3, deposit: 10 }; assert_eq!( - >::get().iter().cloned().collect::>(), + CandidateList::::get().iter().cloned().collect::>(), vec![addition] ); - assert_eq!(CollatorSelection::last_authored_block(3), 10); + assert_eq!(LastAuthoredBlock::::get(3), 10); assert_eq!(Balances::free_balance(3), 90); // but no more @@ -596,11 +596,11 @@ fn cannot_register_as_candidate_if_poor() { fn register_as_candidate_works() { new_test_ext().execute_with(|| { // given - assert_eq!(CollatorSelection::desired_candidates(), 2); - assert_eq!(CollatorSelection::candidacy_bond(), 10); + assert_eq!(DesiredCandidates::::get(), 2); + assert_eq!(CandidacyBond::::get(), 10); - assert_eq!(>::get().iter().count(), 0); - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(CandidateList::::get().iter().count(), 0); + assert_eq!(Invulnerables::::get(), vec![1, 2]); // take two endowed, non-invulnerables accounts. assert_eq!(Balances::free_balance(3), 100); @@ -612,14 +612,14 @@ fn register_as_candidate_works() { assert_eq!(Balances::free_balance(3), 90); assert_eq!(Balances::free_balance(4), 90); - assert_eq!(>::get().iter().count(), 2); + assert_eq!(CandidateList::::get().iter().count(), 2); }); } #[test] fn cannot_take_candidate_slot_if_invulnerable() { new_test_ext().execute_with(|| { - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(Invulnerables::::get(), vec![1, 2]); // can't 1 because it is invulnerable. assert_noop!( @@ -649,11 +649,10 @@ fn cannot_take_candidate_slot_if_duplicate() { // tuple of (id, deposit). let candidate_3 = CandidateInfo { who: 3, deposit: 10 }; let candidate_4 = CandidateInfo { who: 4, deposit: 10 }; - let actual_candidates = - >::get().iter().cloned().collect::>(); + let actual_candidates = CandidateList::::get().iter().cloned().collect::>(); assert_eq!(actual_candidates, vec![candidate_4, candidate_3]); - assert_eq!(CollatorSelection::last_authored_block(3), 10); - assert_eq!(CollatorSelection::last_authored_block(4), 10); + assert_eq!(LastAuthoredBlock::::get(3), 10); + assert_eq!(LastAuthoredBlock::::get(4), 10); assert_eq!(Balances::free_balance(3), 90); // but no more @@ -672,10 +671,10 @@ fn cannot_take_candidate_slot_if_target_invalid() { // tuple of (id, deposit). let candidate_3 = CandidateInfo { who: 3, deposit: 10 }; assert_eq!( - >::get().iter().cloned().collect::>(), + CandidateList::::get().iter().cloned().collect::>(), vec![candidate_3] ); - assert_eq!(CollatorSelection::last_authored_block(3), 10); + assert_eq!(LastAuthoredBlock::::get(3), 10); assert_eq!(Balances::free_balance(3), 90); assert_eq!(Balances::free_balance(4), 100); @@ -744,11 +743,11 @@ fn cannot_take_candidate_slot_if_deposit_less_than_target() { fn take_candidate_slot_works() { new_test_ext().execute_with(|| { // given - assert_eq!(CollatorSelection::desired_candidates(), 2); - assert_eq!(CollatorSelection::candidacy_bond(), 10); + assert_eq!(DesiredCandidates::::get(), 2); + assert_eq!(CandidacyBond::::get(), 10); - assert_eq!(>::get().iter().count(), 0); - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(CandidateList::::get().iter().count(), 0); + assert_eq!(Invulnerables::::get(), vec![1, 2]); // take two endowed, non-invulnerables accounts. assert_eq!(Balances::free_balance(3), 100); @@ -763,7 +762,7 @@ fn take_candidate_slot_works() { assert_eq!(Balances::free_balance(4), 90); assert_eq!(Balances::free_balance(5), 90); - assert_eq!(>::get().iter().count(), 3); + assert_eq!(CandidateList::::get().iter().count(), 3); Balances::make_free_balance_be(&6, 100); let key = MockSessionKeys { aura: UintAuthorityId(6) }; @@ -785,10 +784,10 @@ fn take_candidate_slot_works() { let candidate_6 = CandidateInfo { who: 6, deposit: 50 }; let candidate_5 = CandidateInfo { who: 5, deposit: 10 }; let mut actual_candidates = - >::get().iter().cloned().collect::>(); + CandidateList::::get().iter().cloned().collect::>(); actual_candidates.sort_by(|info_1, info_2| info_1.deposit.cmp(&info_2.deposit)); assert_eq!( - >::get().iter().cloned().collect::>(), + CandidateList::::get().iter().cloned().collect::>(), vec![candidate_5, candidate_3, candidate_6] ); }); @@ -798,11 +797,11 @@ fn take_candidate_slot_works() { fn increase_candidacy_bond_non_candidate_account() { new_test_ext().execute_with(|| { // given - assert_eq!(CollatorSelection::desired_candidates(), 2); - assert_eq!(CollatorSelection::candidacy_bond(), 10); + assert_eq!(DesiredCandidates::::get(), 2); + assert_eq!(CandidacyBond::::get(), 10); - assert_eq!(>::get().iter().count(), 0); - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(CandidateList::::get().iter().count(), 0); + assert_eq!(Invulnerables::::get(), vec![1, 2]); assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3))); assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4))); @@ -818,11 +817,11 @@ fn increase_candidacy_bond_non_candidate_account() { fn increase_candidacy_bond_insufficient_balance() { new_test_ext().execute_with(|| { // given - assert_eq!(CollatorSelection::desired_candidates(), 2); - assert_eq!(CollatorSelection::candidacy_bond(), 10); + assert_eq!(DesiredCandidates::::get(), 2); + assert_eq!(CandidacyBond::::get(), 10); - assert_eq!(>::get().iter().count(), 0); - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(CandidateList::::get().iter().count(), 0); + assert_eq!(Invulnerables::::get(), vec![1, 2]); // take two endowed, non-invulnerables accounts. assert_eq!(Balances::free_balance(3), 100); @@ -850,11 +849,11 @@ fn increase_candidacy_bond_insufficient_balance() { fn increase_candidacy_bond_works() { new_test_ext().execute_with(|| { // given - assert_eq!(CollatorSelection::desired_candidates(), 2); - assert_eq!(CollatorSelection::candidacy_bond(), 10); + assert_eq!(DesiredCandidates::::get(), 2); + assert_eq!(CandidacyBond::::get(), 10); - assert_eq!(>::get().iter().count(), 0); - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(CandidateList::::get().iter().count(), 0); + assert_eq!(Invulnerables::::get(), vec![1, 2]); // take three endowed, non-invulnerables accounts. assert_eq!(Balances::free_balance(3), 100); @@ -873,7 +872,7 @@ fn increase_candidacy_bond_works() { assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 30)); assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 40)); - assert_eq!(>::get().iter().count(), 3); + assert_eq!(CandidateList::::get().iter().count(), 3); assert_eq!(Balances::free_balance(3), 80); assert_eq!(Balances::free_balance(4), 70); assert_eq!(Balances::free_balance(5), 60); @@ -881,7 +880,7 @@ fn increase_candidacy_bond_works() { assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 40)); assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 60)); - assert_eq!(>::get().iter().count(), 3); + assert_eq!(CandidateList::::get().iter().count(), 3); assert_eq!(Balances::free_balance(3), 60); assert_eq!(Balances::free_balance(4), 40); assert_eq!(Balances::free_balance(5), 60); @@ -892,11 +891,11 @@ fn increase_candidacy_bond_works() { fn decrease_candidacy_bond_non_candidate_account() { new_test_ext().execute_with(|| { // given - assert_eq!(CollatorSelection::desired_candidates(), 2); - assert_eq!(CollatorSelection::candidacy_bond(), 10); + assert_eq!(DesiredCandidates::::get(), 2); + assert_eq!(CandidacyBond::::get(), 10); - assert_eq!(>::get().iter().count(), 0); - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(CandidateList::::get().iter().count(), 0); + assert_eq!(Invulnerables::::get(), vec![1, 2]); assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3))); assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4))); @@ -914,11 +913,11 @@ fn decrease_candidacy_bond_non_candidate_account() { fn decrease_candidacy_bond_insufficient_funds() { new_test_ext().execute_with(|| { // given - assert_eq!(CollatorSelection::desired_candidates(), 2); - assert_eq!(CollatorSelection::candidacy_bond(), 10); + assert_eq!(DesiredCandidates::::get(), 2); + assert_eq!(CandidacyBond::::get(), 10); - assert_eq!(>::get().iter().count(), 0); - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(CandidateList::::get().iter().count(), 0); + assert_eq!(Invulnerables::::get(), vec![1, 2]); // take two endowed, non-invulnerables accounts. assert_eq!(Balances::free_balance(3), 100); @@ -961,7 +960,7 @@ fn decrease_candidacy_bond_insufficient_funds() { #[test] fn decrease_candidacy_bond_occupying_top_slot() { new_test_ext().execute_with(|| { - assert_eq!(CollatorSelection::desired_candidates(), 2); + assert_eq!(DesiredCandidates::::get(), 2); // Register 3 candidates. assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3))); assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4))); @@ -976,7 +975,7 @@ fn decrease_candidacy_bond_occupying_top_slot() { let candidate_4 = CandidateInfo { who: 4, deposit: 30 }; let candidate_5 = CandidateInfo { who: 5, deposit: 60 }; assert_eq!( - >::get().iter().cloned().collect::>(), + CandidateList::::get().iter().cloned().collect::>(), vec![candidate_4, candidate_3, candidate_5] ); @@ -1000,7 +999,7 @@ fn decrease_candidacy_bond_occupying_top_slot() { let candidate_4 = CandidateInfo { who: 4, deposit: 35 }; let candidate_5 = CandidateInfo { who: 5, deposit: 60 }; assert_eq!( - >::get().iter().cloned().collect::>(), + CandidateList::::get().iter().cloned().collect::>(), vec![candidate_3, candidate_4, candidate_5] ); @@ -1023,11 +1022,11 @@ fn decrease_candidacy_bond_occupying_top_slot() { fn decrease_candidacy_bond_works() { new_test_ext().execute_with(|| { // given - assert_eq!(CollatorSelection::desired_candidates(), 2); - assert_eq!(CollatorSelection::candidacy_bond(), 10); + assert_eq!(DesiredCandidates::::get(), 2); + assert_eq!(CandidacyBond::::get(), 10); - assert_eq!(>::get().iter().count(), 0); - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(CandidateList::::get().iter().count(), 0); + assert_eq!(Invulnerables::::get(), vec![1, 2]); // take three endowed, non-invulnerables accounts. assert_eq!(Balances::free_balance(3), 100); @@ -1046,14 +1045,14 @@ fn decrease_candidacy_bond_works() { assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 30)); assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 40)); - assert_eq!(>::get().iter().count(), 3); + assert_eq!(CandidateList::::get().iter().count(), 3); assert_eq!(Balances::free_balance(3), 80); assert_eq!(Balances::free_balance(4), 70); assert_eq!(Balances::free_balance(5), 60); assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 10)); - assert_eq!(>::get().iter().count(), 3); + assert_eq!(CandidateList::::get().iter().count(), 3); assert_eq!(Balances::free_balance(3), 90); assert_eq!(Balances::free_balance(4), 70); assert_eq!(Balances::free_balance(5), 60); @@ -1064,11 +1063,11 @@ fn decrease_candidacy_bond_works() { fn update_candidacy_bond_with_identical_amount() { new_test_ext().execute_with(|| { // given - assert_eq!(CollatorSelection::desired_candidates(), 2); - assert_eq!(CollatorSelection::candidacy_bond(), 10); + assert_eq!(DesiredCandidates::::get(), 2); + assert_eq!(CandidacyBond::::get(), 10); - assert_eq!(>::get().iter().count(), 0); - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(CandidateList::::get().iter().count(), 0); + assert_eq!(Invulnerables::::get(), vec![1, 2]); // take three endowed, non-invulnerables accounts. assert_eq!(Balances::free_balance(3), 100); @@ -1087,7 +1086,7 @@ fn update_candidacy_bond_with_identical_amount() { assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 30)); assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 40)); - assert_eq!(>::get().iter().count(), 3); + assert_eq!(CandidateList::::get().iter().count(), 3); assert_eq!(Balances::free_balance(3), 80); assert_eq!(Balances::free_balance(4), 70); assert_eq!(Balances::free_balance(5), 60); @@ -1104,11 +1103,11 @@ fn update_candidacy_bond_with_identical_amount() { fn candidate_list_works() { new_test_ext().execute_with(|| { // given - assert_eq!(CollatorSelection::desired_candidates(), 2); - assert_eq!(CollatorSelection::candidacy_bond(), 10); + assert_eq!(DesiredCandidates::::get(), 2); + assert_eq!(CandidacyBond::::get(), 10); - assert_eq!(>::get().iter().count(), 0); - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(CandidateList::::get().iter().count(), 0); + assert_eq!(Invulnerables::::get(), vec![1, 2]); // take three endowed, non-invulnerables accounts. assert_eq!(Balances::free_balance(3), 100); @@ -1131,7 +1130,7 @@ fn candidate_list_works() { let candidate_4 = CandidateInfo { who: 4, deposit: 25 }; let candidate_5 = CandidateInfo { who: 5, deposit: 10 }; assert_eq!( - >::get().iter().cloned().collect::>(), + CandidateList::::get().iter().cloned().collect::>(), vec![candidate_5, candidate_4, candidate_3] ); }); @@ -1157,7 +1156,7 @@ fn leave_intent() { // bond is returned assert_ok!(CollatorSelection::leave_intent(RuntimeOrigin::signed(3))); assert_eq!(Balances::free_balance(3), 100); - assert_eq!(CollatorSelection::last_authored_block(3), 0); + assert_eq!(LastAuthoredBlock::::get(3), 0); }); } @@ -1177,10 +1176,10 @@ fn authorship_event_handler() { let collator = CandidateInfo { who: 4, deposit: 10 }; assert_eq!( - >::get().iter().cloned().collect::>(), + CandidateList::::get().iter().cloned().collect::>(), vec![collator] ); - assert_eq!(CollatorSelection::last_authored_block(4), 0); + assert_eq!(LastAuthoredBlock::::get(4), 0); // half of the pot goes to the collator who's the author (4 in tests). assert_eq!(Balances::free_balance(4), 140); @@ -1206,10 +1205,10 @@ fn fees_edgecases() { let collator = CandidateInfo { who: 4, deposit: 10 }; assert_eq!( - >::get().iter().cloned().collect::>(), + CandidateList::::get().iter().cloned().collect::>(), vec![collator] ); - assert_eq!(CollatorSelection::last_authored_block(4), 0); + assert_eq!(LastAuthoredBlock::::get(4), 0); // Nothing received assert_eq!(Balances::free_balance(4), 90); // all fee stays @@ -1236,7 +1235,7 @@ fn session_management_single_candidate() { // session won't see this. assert_eq!(SessionHandlerCollators::get(), vec![1, 2]); // but we have a new candidate. - assert_eq!(>::get().iter().count(), 1); + assert_eq!(CandidateList::::get().iter().count(), 1); initialize_to_block(10); assert_eq!(SessionChangeBlock::get(), 10); @@ -1274,7 +1273,7 @@ fn session_management_max_candidates() { // session won't see this. assert_eq!(SessionHandlerCollators::get(), vec![1, 2]); // but we have a new candidate. - assert_eq!(>::get().iter().count(), 3); + assert_eq!(CandidateList::::get().iter().count(), 3); initialize_to_block(10); assert_eq!(SessionChangeBlock::get(), 10); @@ -1313,7 +1312,7 @@ fn session_management_increase_bid_with_list_update() { // session won't see this. assert_eq!(SessionHandlerCollators::get(), vec![1, 2]); // but we have a new candidate. - assert_eq!(>::get().iter().count(), 3); + assert_eq!(CandidateList::::get().iter().count(), 3); initialize_to_block(10); assert_eq!(SessionChangeBlock::get(), 10); @@ -1352,7 +1351,7 @@ fn session_management_candidate_list_eager_sort() { // session won't see this. assert_eq!(SessionHandlerCollators::get(), vec![1, 2]); // but we have a new candidate. - assert_eq!(>::get().iter().count(), 3); + assert_eq!(CandidateList::::get().iter().count(), 3); initialize_to_block(10); assert_eq!(SessionChangeBlock::get(), 10); @@ -1399,7 +1398,7 @@ fn session_management_reciprocal_outbidding() { // session won't see this. assert_eq!(SessionHandlerCollators::get(), vec![1, 2]); // but we have a new candidate. - assert_eq!(>::get().iter().count(), 3); + assert_eq!(CandidateList::::get().iter().count(), 3); initialize_to_block(10); assert_eq!(SessionChangeBlock::get(), 10); @@ -1451,7 +1450,7 @@ fn session_management_decrease_bid_after_auction() { // session won't see this. assert_eq!(SessionHandlerCollators::get(), vec![1, 2]); // but we have a new candidate. - assert_eq!(>::get().iter().count(), 3); + assert_eq!(CandidateList::::get().iter().count(), 3); initialize_to_block(10); assert_eq!(SessionChangeBlock::get(), 10); @@ -1476,20 +1475,20 @@ fn kick_mechanism() { assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3))); assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4))); initialize_to_block(10); - assert_eq!(>::get().iter().count(), 2); + assert_eq!(CandidateList::::get().iter().count(), 2); initialize_to_block(20); assert_eq!(SessionChangeBlock::get(), 20); // 4 authored this block, gets to stay 3 was kicked - assert_eq!(>::get().iter().count(), 1); + assert_eq!(CandidateList::::get().iter().count(), 1); // 3 will be kicked after 1 session delay assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]); // tuple of (id, deposit). let collator = CandidateInfo { who: 4, deposit: 10 }; assert_eq!( - >::get().iter().cloned().collect::>(), + CandidateList::::get().iter().cloned().collect::>(), vec![collator] ); - assert_eq!(CollatorSelection::last_authored_block(4), 20); + assert_eq!(LastAuthoredBlock::::get(4), 20); initialize_to_block(30); // 3 gets kicked after 1 session delay assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 4]); @@ -1503,8 +1502,8 @@ fn should_not_kick_mechanism_too_few() { new_test_ext().execute_with(|| { // remove the invulnerables and add new collators 3 and 5 - assert_eq!(>::get().iter().count(), 0); - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]); + assert_eq!(CandidateList::::get().iter().count(), 0); + assert_eq!(Invulnerables::::get(), vec![1, 2]); assert_ok!(CollatorSelection::remove_invulnerable( RuntimeOrigin::signed(RootAccount::get()), 1 @@ -1517,21 +1516,21 @@ fn should_not_kick_mechanism_too_few() { )); initialize_to_block(10); - assert_eq!(>::get().iter().count(), 2); + assert_eq!(CandidateList::::get().iter().count(), 2); initialize_to_block(20); assert_eq!(SessionChangeBlock::get(), 20); // 4 authored this block, 3 is kicked, 5 stays because of too few collators - assert_eq!(>::get().iter().count(), 1); + assert_eq!(CandidateList::::get().iter().count(), 1); // 3 will be kicked after 1 session delay assert_eq!(SessionHandlerCollators::get(), vec![3, 5]); // tuple of (id, deposit). let collator = CandidateInfo { who: 3, deposit: 10 }; assert_eq!( - >::get().iter().cloned().collect::>(), + CandidateList::::get().iter().cloned().collect::>(), vec![collator] ); - assert_eq!(CollatorSelection::last_authored_block(4), 20); + assert_eq!(LastAuthoredBlock::::get(4), 20); initialize_to_block(30); // 3 gets kicked after 1 session delay @@ -1544,7 +1543,7 @@ fn should_not_kick_mechanism_too_few() { #[test] fn should_kick_invulnerables_from_candidates_on_session_change() { new_test_ext().execute_with(|| { - assert_eq!(>::get().iter().count(), 0); + assert_eq!(CandidateList::::get().iter().count(), 0); assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3))); assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4))); assert_eq!(Balances::free_balance(3), 90); @@ -1558,20 +1557,19 @@ fn should_kick_invulnerables_from_candidates_on_session_change() { let collator_3 = CandidateInfo { who: 3, deposit: 10 }; let collator_4 = CandidateInfo { who: 4, deposit: 10 }; - let actual_candidates = - >::get().iter().cloned().collect::>(); + let actual_candidates = CandidateList::::get().iter().cloned().collect::>(); assert_eq!(actual_candidates, vec![collator_4.clone(), collator_3]); - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2, 3]); + assert_eq!(Invulnerables::::get(), vec![1, 2, 3]); // session change initialize_to_block(10); // 3 is removed from candidates assert_eq!( - >::get().iter().cloned().collect::>(), + CandidateList::::get().iter().cloned().collect::>(), vec![collator_4] ); // but not from invulnerables - assert_eq!(CollatorSelection::invulnerables(), vec![1, 2, 3]); + assert_eq!(Invulnerables::::get(), vec![1, 2, 3]); // and it got its deposit back assert_eq!(Balances::free_balance(3), 100); }); diff --git a/cumulus/pallets/dmp-queue/src/mock.rs b/cumulus/pallets/dmp-queue/src/mock.rs index 6bd74a047eb7459b309705d5f4d5a7d2e1e775f4..ed72ce678e3efa416df78b95f18c0115c91e2e1f 100644 --- a/cumulus/pallets/dmp-queue/src/mock.rs +++ b/cumulus/pallets/dmp-queue/src/mock.rs @@ -35,7 +35,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type Block = Block; type RuntimeOrigin = RuntimeOrigin; diff --git a/cumulus/pallets/parachain-system/Cargo.toml b/cumulus/pallets/parachain-system/Cargo.toml index 86647290563cb0379f1acce5090e3fc79da34f1e..7e0442f0b5856fa5153e29ff497cfee876c067ec 100644 --- a/cumulus/pallets/parachain-system/Cargo.toml +++ b/cumulus/pallets/parachain-system/Cargo.toml @@ -36,6 +36,7 @@ sp-version = { path = "../../../substrate/primitives/version", default-features # Polkadot polkadot-parachain-primitives = { path = "../../../polkadot/parachain", default-features = false, features = ["wasm-api"] } polkadot-runtime-parachains = { path = "../../../polkadot/runtime/parachains", default-features = false } +polkadot-runtime-common = { path = "../../../polkadot/runtime/common", default-features = false, optional = true } xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false } # Cumulus @@ -79,6 +80,7 @@ std = [ "log/std", "pallet-message-queue/std", "polkadot-parachain-primitives/std", + "polkadot-runtime-common/std", "polkadot-runtime-parachains/std", "scale-info/std", "sp-core/std", @@ -102,6 +104,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-runtime-common/runtime-benchmarks", "polkadot-runtime-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] @@ -110,6 +113,7 @@ try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "pallet-message-queue/try-runtime", + "polkadot-runtime-common?/try-runtime", "polkadot-runtime-parachains/try-runtime", "sp-runtime/try-runtime", ] diff --git a/cumulus/pallets/parachain-system/src/lib.rs b/cumulus/pallets/parachain-system/src/lib.rs index 5a0fa57fb171c60f13b87d70305a6d58dad475a1..54a1def59600dff5dd8b1484691dcb18fff5ce63 100644 --- a/cumulus/pallets/parachain-system/src/lib.rs +++ b/cumulus/pallets/parachain-system/src/lib.rs @@ -205,6 +205,7 @@ pub mod pallet { type OnSystemEvent: OnSystemEvent; /// Returns the parachain ID we are running with. + #[pallet::constant] type SelfParaId: Get; /// The place where outbound XCMP messages come from. This is queried in `finalize_block`. @@ -266,7 +267,7 @@ pub mod pallet { LastRelayChainBlockNumber::::put(vfp.relay_parent_number); - let host_config = match Self::host_configuration() { + let host_config = match HostConfiguration::::get() { Some(ok) => ok, None => { debug_assert!( @@ -280,7 +281,7 @@ pub mod pallet { // Before updating the relevant messaging state, we need to extract // the total bandwidth limits for the purpose of updating the unincluded // segment. - let total_bandwidth_out = match Self::relevant_messaging_state() { + let total_bandwidth_out = match RelevantMessagingState::::get() { Some(s) => OutboundBandwidthLimits::from_relay_chain_state(&s), None => { debug_assert!( @@ -297,7 +298,8 @@ pub mod pallet { Self::adjust_egress_bandwidth_limits(); let (ump_msg_count, ump_total_bytes) = >::mutate(|up| { - let (available_capacity, available_size) = match Self::relevant_messaging_state() { + let (available_capacity, available_size) = match RelevantMessagingState::::get() + { Some(limits) => ( limits.relay_dispatch_queue_remaining_capacity.remaining_count, limits.relay_dispatch_queue_remaining_capacity.remaining_size, @@ -462,7 +464,7 @@ pub mod pallet { // One complication here, is that the `host_configuration` is updated by an inherent // and those are processed after the block initialization phase. Therefore, we have to // be content only with the configuration as per the previous block. That means that - // the configuration can be either stale (or be abscent altogether in case of the + // the configuration can be either stale (or be absent altogether in case of the // beginning of the chain). // // In order to mitigate this, we do the following. At the time, we are only concerned @@ -475,7 +477,7 @@ pub mod pallet { // than the announced, we would waste some of weight. In the case the actual value is // greater than the announced, we will miss opportunity to send a couple of messages. weight += T::DbWeight::get().reads_writes(1, 1); - let hrmp_max_message_num_per_candidate = Self::host_configuration() + let hrmp_max_message_num_per_candidate = HostConfiguration::::get() .map(|cfg| cfg.hrmp_max_message_num_per_candidate) .unwrap_or(0); >::put(hrmp_max_message_num_per_candidate); @@ -763,7 +765,6 @@ pub mod pallet { /// [`:code`][sp_core::storage::well_known_keys::CODE] which will result the next block process /// with the new validation code. This concludes the upgrade process. #[pallet::storage] - #[pallet::getter(fn new_validation_function)] pub(super) type PendingValidationCode = StorageValue<_, Vec, ValueQuery>; /// Validation code that is set by the parachain and is to be communicated to collator and @@ -778,7 +779,6 @@ pub mod pallet { /// This value is expected to be set only once per block and it's never stored /// in the trie. #[pallet::storage] - #[pallet::getter(fn validation_data)] pub(super) type ValidationData = StorageValue<_, PersistedValidationData>; /// Were the validation data set to notify the relay chain? @@ -819,7 +819,6 @@ pub mod pallet { /// /// This data is also absent from the genesis. #[pallet::storage] - #[pallet::getter(fn relay_state_proof)] pub(super) type RelayStateProof = StorageValue<_, sp_trie::StorageProof>; /// The snapshot of some state related to messaging relevant to the current parachain as per @@ -830,7 +829,6 @@ pub mod pallet { /// /// This data is also absent from the genesis. #[pallet::storage] - #[pallet::getter(fn relevant_messaging_state)] pub(super) type RelevantMessagingState = StorageValue<_, MessagingStateSnapshot>; /// The parachain host configuration that was obtained from the relay parent. @@ -840,7 +838,7 @@ pub mod pallet { /// /// This data is also absent from the genesis. #[pallet::storage] - #[pallet::getter(fn host_configuration)] + #[pallet::disable_try_decode_storage] pub(super) type HostConfiguration = StorageValue<_, AbridgedHostConfiguration>; /// The last downward message queue chain head we have observed. @@ -1039,7 +1037,7 @@ impl GetChannelInfo for Pallet { // // Here it a similar case, with the difference that the realization that the channel is // closed came the same block. - let channels = match Self::relevant_messaging_state() { + let channels = match RelevantMessagingState::::get() { None => { log::warn!("calling `get_channel_status` with no RelevantMessagingState?!"); return ChannelStatus::Closed @@ -1067,7 +1065,7 @@ impl GetChannelInfo for Pallet { } fn get_channel_info(id: ParaId) -> Option { - let channels = Self::relevant_messaging_state()?.egress_channels; + let channels = RelevantMessagingState::::get()?.egress_channels; let index = channels.binary_search_by_key(&id, |item| item.0).ok()?; let info = ChannelInfo { max_capacity: channels[index].1.max_capacity, @@ -1404,7 +1402,7 @@ impl Pallet { ensure!(>::get().is_none(), Error::::ProhibitedByPolkadot); ensure!(!>::exists(), Error::::OverlappingUpgrades); - let cfg = Self::host_configuration().ok_or(Error::::HostConfigurationNotAvailable)?; + let cfg = HostConfiguration::::get().ok_or(Error::::HostConfigurationNotAvailable)?; ensure!(validation_function.len() <= cfg.max_code_size as usize, Error::::TooBig); // When a code upgrade is scheduled, it has to be applied in two @@ -1560,7 +1558,7 @@ impl Pallet { // may change so that the message is no longer valid. // // However, changing this setting is expected to be rare. - if let Some(cfg) = Self::host_configuration() { + if let Some(cfg) = HostConfiguration::::get() { if message_len > cfg.max_upward_message_size as usize { return Err(MessageSendError::TooBig) } @@ -1610,6 +1608,15 @@ impl UpwardMessageSender for Pallet { } } +#[cfg(feature = "runtime-benchmarks")] +impl polkadot_runtime_common::xcm_sender::EnsureForParachain for Pallet { + fn ensure(para_id: ParaId) { + if let ChannelStatus::Closed = Self::get_channel_status(para_id) { + Self::open_outbound_hrmp_channel_for_benchmarks_or_tests(para_id) + } + } +} + /// Something that can check the inherents of a block. #[cfg_attr( feature = "parameterized-consensus-hook", @@ -1707,14 +1714,14 @@ impl BlockNumberProvider for RelaychainDataProvider { type BlockNumber = relay_chain::BlockNumber; fn current_block_number() -> relay_chain::BlockNumber { - Pallet::::validation_data() + ValidationData::::get() .map(|d| d.relay_parent_number) .unwrap_or_else(|| Pallet::::last_relay_block_number()) } #[cfg(feature = "runtime-benchmarks")] fn set_block_number(block: Self::BlockNumber) { - let mut validation_data = Pallet::::validation_data().unwrap_or_else(|| + let mut validation_data = ValidationData::::get().unwrap_or_else(|| // PersistedValidationData does not impl default in non-std PersistedValidationData { parent_head: vec![].into(), @@ -1729,7 +1736,7 @@ impl BlockNumberProvider for RelaychainDataProvider { impl RelaychainStateProvider for RelaychainDataProvider { fn current_relay_chain_state() -> RelayChainState { - Pallet::::validation_data() + ValidationData::::get() .map(|d| RelayChainState { number: d.relay_parent_number, state_root: d.relay_parent_storage_root, @@ -1739,7 +1746,7 @@ impl RelaychainStateProvider for RelaychainDataProvider { #[cfg(feature = "runtime-benchmarks")] fn set_current_relay_chain_state(state: RelayChainState) { - let mut validation_data = Pallet::::validation_data().unwrap_or_else(|| + let mut validation_data = ValidationData::::get().unwrap_or_else(|| // PersistedValidationData does not impl default in non-std PersistedValidationData { parent_head: vec![].into(), diff --git a/cumulus/pallets/parachain-system/src/migration.rs b/cumulus/pallets/parachain-system/src/migration.rs index a92f85b9cd420e9e1027d1f82c643d90fea8b97c..30106aceab5a442c34360563fdfb096fdc6ac9b1 100644 --- a/cumulus/pallets/parachain-system/src/migration.rs +++ b/cumulus/pallets/parachain-system/src/migration.rs @@ -21,7 +21,7 @@ use frame_support::{ weights::Weight, }; -/// The current storage version. +/// The in-code storage version. pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); /// Migrates the pallet storage to the most recent version. diff --git a/cumulus/pallets/parachain-system/src/mock.rs b/cumulus/pallets/parachain-system/src/mock.rs index b76553a53841dcb131e7f1a7f4017987250c739c..0b1d536ba7cd9153d1409a65a87bc8234a09bd95 100644 --- a/cumulus/pallets/parachain-system/src/mock.rs +++ b/cumulus/pallets/parachain-system/src/mock.rs @@ -71,7 +71,7 @@ parameter_types! { pub const ReservedDmpWeight: Weight = Weight::zero(); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type BlockHashCount = BlockHashCount; diff --git a/cumulus/pallets/parachain-system/src/relay_state_snapshot.rs b/cumulus/pallets/parachain-system/src/relay_state_snapshot.rs index 5519d1521ea6da85573f2ecdd9cca001616b5806..60eccfb072f41e87223d54f34b5c7eb59c00b2ad 100644 --- a/cumulus/pallets/parachain-system/src/relay_state_snapshot.rs +++ b/cumulus/pallets/parachain-system/src/relay_state_snapshot.rs @@ -90,7 +90,7 @@ pub enum Error { DmqMqcHead(ReadEntryErr), /// Relay dispatch queue cannot be extracted. RelayDispatchQueueRemainingCapacity(ReadEntryErr), - /// The hrmp inress channel index cannot be extracted. + /// The hrmp ingress channel index cannot be extracted. HrmpIngressChannelIndex(ReadEntryErr), /// The hrmp egress channel index cannot be extracted. HrmpEgressChannelIndex(ReadEntryErr), diff --git a/cumulus/pallets/parachain-system/src/validate_block/implementation.rs b/cumulus/pallets/parachain-system/src/validate_block/implementation.rs index ce3b724420f1c9e50b65a81074e023c6b735227b..ecab7a9a09311ae0f952a7842e9f1826b00adc69 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/implementation.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/implementation.rs @@ -16,7 +16,7 @@ //! The actual implementation of the validate block functionality. -use super::{trie_cache, MemoryOptimizedValidationParams}; +use super::{trie_cache, trie_recorder, MemoryOptimizedValidationParams}; use cumulus_primitives_core::{ relay_chain::Hash as RHash, ParachainBlockData, PersistedValidationData, }; @@ -34,12 +34,14 @@ use sp_externalities::{set_and_run_with_externalities, Externalities}; use sp_io::KillStorageResult; use sp_runtime::traits::{Block as BlockT, Extrinsic, HashingFor, Header as HeaderT}; use sp_std::prelude::*; -use sp_trie::MemoryDB; +use sp_trie::{MemoryDB, ProofSizeProvider}; +use trie_recorder::SizeOnlyRecorderProvider; type TrieBackend = sp_state_machine::TrieBackend< MemoryDB>, HashingFor, trie_cache::CacheProvider>, + SizeOnlyRecorderProvider>, >; type Ext<'a, B> = sp_state_machine::Ext<'a, HashingFor, TrieBackend>; @@ -48,6 +50,9 @@ fn with_externalities R, R>(f: F) -> R { sp_externalities::with_externalities(f).expect("Environmental externalities not set.") } +// Recorder instance to be used during this validate_block call. +environmental::environmental!(recorder: trait ProofSizeProvider); + /// Validate the given parachain block. /// /// This function is doing roughly the following: @@ -120,6 +125,7 @@ where sp_std::mem::drop(storage_proof); + let mut recorder = SizeOnlyRecorderProvider::new(); let cache_provider = trie_cache::CacheProvider::new(); // We use the storage root of the `parent_head` to ensure that it is the correct root. // This is already being done above while creating the in-memory db, but let's be paranoid!! @@ -128,6 +134,7 @@ where *parent_header.state_root(), cache_provider, ) + .with_recorder(recorder.clone()) .build(); let _guard = ( @@ -167,9 +174,11 @@ where .replace_implementation(host_default_child_storage_next_key), sp_io::offchain_index::host_set.replace_implementation(host_offchain_index_set), sp_io::offchain_index::host_clear.replace_implementation(host_offchain_index_clear), + cumulus_primitives_proof_size_hostfunction::storage_proof_size::host_storage_proof_size + .replace_implementation(host_storage_proof_size), ); - run_with_externalities::(&backend, || { + run_with_externalities_and_recorder::(&backend, &mut recorder, || { let relay_chain_proof = crate::RelayChainStateProof::new( PSC::SelfParaId::get(), inherent_data.validation_data.relay_parent_storage_root, @@ -190,7 +199,7 @@ where } }); - run_with_externalities::(&backend, || { + run_with_externalities_and_recorder::(&backend, &mut recorder, || { let head_data = HeadData(block.header().encode()); E::execute_block(block); @@ -265,15 +274,17 @@ fn validate_validation_data( ); } -/// Run the given closure with the externalities set. -fn run_with_externalities R>( +/// Run the given closure with the externalities and recorder set. +fn run_with_externalities_and_recorder R>( backend: &TrieBackend, + recorder: &mut SizeOnlyRecorderProvider>, execute: F, ) -> R { let mut overlay = sp_state_machine::OverlayedChanges::default(); let mut ext = Ext::::new(&mut overlay, backend); + recorder.reset(); - set_and_run_with_externalities(&mut ext, || execute()) + recorder::using(recorder, || set_and_run_with_externalities(&mut ext, || execute())) } fn host_storage_read(key: &[u8], value_out: &mut [u8], value_offset: u32) -> Option { @@ -305,6 +316,10 @@ fn host_storage_clear(key: &[u8]) { with_externalities(|ext| ext.place_storage(key.to_vec(), None)) } +fn host_storage_proof_size() -> u64 { + recorder::with(|rec| rec.estimate_encoded_size()).expect("Recorder is always set; qed") as _ +} + fn host_storage_root(version: StateVersion) -> Vec { with_externalities(|ext| ext.storage_root(version)) } diff --git a/cumulus/pallets/parachain-system/src/validate_block/tests.rs b/cumulus/pallets/parachain-system/src/validate_block/tests.rs index f17ac6007a09bf02011473f95a537855eb43f3b0..a9fb65e11089fab87c543fbb6d7b54f6231a1dd7 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/tests.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/tests.rs @@ -59,7 +59,7 @@ fn call_validate_block( } fn create_test_client() -> (Client, Header) { - let client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().enable_import_proof_recording().build(); let genesis_header = client .header(client.chain_info().genesis_hash) diff --git a/cumulus/pallets/parachain-system/src/validate_block/trie_recorder.rs b/cumulus/pallets/parachain-system/src/validate_block/trie_recorder.rs index e73aef70aa491fc68aad4f9479222d9a076e7edc..48310670c074d1be2ec86f582c0c84622abc086c 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/trie_recorder.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/trie_recorder.rs @@ -97,6 +97,7 @@ pub(crate) struct SizeOnlyRecorderProvider { } impl SizeOnlyRecorderProvider { + /// Create a new instance of [`SizeOnlyRecorderProvider`] pub fn new() -> Self { Self { seen_nodes: Default::default(), @@ -104,6 +105,13 @@ impl SizeOnlyRecorderProvider { recorded_keys: Default::default(), } } + + /// Reset the internal state. + pub fn reset(&self) { + self.seen_nodes.borrow_mut().clear(); + *self.encoded_size.borrow_mut() = 0; + self.recorded_keys.borrow_mut().clear(); + } } impl sp_trie::TrieRecorderProvider for SizeOnlyRecorderProvider { @@ -281,6 +289,9 @@ mod tests { reference_recorder.estimate_encoded_size(), recorder_for_test.estimate_encoded_size() ); + + recorder_for_test.reset(); + assert_eq!(recorder_for_test.estimate_encoded_size(), 0) } } } diff --git a/cumulus/pallets/xcmp-queue/src/lib.rs b/cumulus/pallets/xcmp-queue/src/lib.rs index 5b900769622afa3cba2c47f493ebff1a279b182b..b4cd925d540ead4ef17af337f59192c4cfec0042 100644 --- a/cumulus/pallets/xcmp-queue/src/lib.rs +++ b/cumulus/pallets/xcmp-queue/src/lib.rs @@ -462,7 +462,7 @@ impl Pallet { // Max message size refers to aggregates, or pages. Not to individual fragments. let max_message_size = channel_info.max_message_size as usize; let format_size = format.encoded_size(); - // We check the encoded fragment length plus the format size agains the max message size + // We check the encoded fragment length plus the format size against the max message size // because the format is concatenated if a new page is needed. let size_to_check = encoded_fragment .len() @@ -600,7 +600,7 @@ impl Pallet { let QueueConfigData { drop_threshold, .. } = >::get(); let fp = T::XcmpQueue::footprint(sender); // Assume that it will not fit into the current page: - let new_pages = fp.pages.saturating_add(1); + let new_pages = fp.ready_pages.saturating_add(1); if new_pages > drop_threshold { // This should not happen since the channel should have been suspended in // [`on_queue_changed`]. @@ -663,12 +663,12 @@ impl OnQueueChanged for Pallet { let mut suspended_channels = >::get(); let suspended = suspended_channels.contains(¶); - if suspended && fp.pages <= resume_threshold { + if suspended && fp.ready_pages <= resume_threshold { Self::send_signal(para, ChannelSignal::Resume); suspended_channels.remove(¶); >::put(suspended_channels); - } else if !suspended && fp.pages >= suspend_threshold { + } else if !suspended && fp.ready_pages >= suspend_threshold { log::warn!("XCMP queue for sibling {:?} is full; suspending channel.", para); Self::send_signal(para, ChannelSignal::Suspend); diff --git a/cumulus/pallets/xcmp-queue/src/migration.rs b/cumulus/pallets/xcmp-queue/src/migration.rs index 6c86c3011d23807adfbde801ec6865b6731822df..c7fa61a3e3f0513e53d67fcee4efd6c3cecf29f6 100644 --- a/cumulus/pallets/xcmp-queue/src/migration.rs +++ b/cumulus/pallets/xcmp-queue/src/migration.rs @@ -24,7 +24,7 @@ use frame_support::{ weights::{constants::WEIGHT_REF_TIME_PER_MILLIS, Weight}, }; -/// The current storage version. +/// The in-code storage version. pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); pub const LOG: &str = "runtime::xcmp-queue-migration"; diff --git a/cumulus/pallets/xcmp-queue/src/mock.rs b/cumulus/pallets/xcmp-queue/src/mock.rs index 08ab58ce816046336fcba0a2318bd2ff77227e5f..9d9a723cf8b538b604c45ff5b2bd366cd6f92c2d 100644 --- a/cumulus/pallets/xcmp-queue/src/mock.rs +++ b/cumulus/pallets/xcmp-queue/src/mock.rs @@ -58,7 +58,7 @@ parameter_types! { type AccountId = u64; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = Everything; type BlockWeights = (); @@ -175,6 +175,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } pub type XcmRouter = ( @@ -247,6 +250,7 @@ impl> EnqueueMessage for EnqueueToLocalStorage } } footprint.pages = footprint.storage.size as u32 / 16; // Number does not matter + footprint.ready_pages = footprint.pages; footprint } } diff --git a/cumulus/parachain-template/pallets/template/README.md b/cumulus/parachain-template/pallets/template/README.md deleted file mode 100644 index 5a6461233465c327d233fc5d97bdb3a6a85f8fd9..0000000000000000000000000000000000000000 --- a/cumulus/parachain-template/pallets/template/README.md +++ /dev/null @@ -1 +0,0 @@ -License: Unlicense diff --git a/cumulus/parachain-template/pallets/template/src/benchmarking.rs b/cumulus/parachain-template/pallets/template/src/benchmarking.rs deleted file mode 100644 index 8bba2a09867dea1d55e487661c01fd72acdf4dc9..0000000000000000000000000000000000000000 --- a/cumulus/parachain-template/pallets/template/src/benchmarking.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! Benchmarking setup for pallet-parachain-template - -use super::*; - -#[allow(unused)] -use crate::Pallet as Template; -use frame_benchmarking::{benchmarks, impl_benchmark_test_suite, whitelisted_caller}; -use frame_system::RawOrigin; - -benchmarks! { - do_something { - let s in 0 .. 100; - let caller: T::AccountId = whitelisted_caller(); - }: _(RawOrigin::Signed(caller), s) - verify { - assert_eq!(Something::::get(), Some(s)); - } -} - -impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test,); diff --git a/cumulus/parachains/chain-specs/asset-hub-kusama.json b/cumulus/parachains/chain-specs/asset-hub-kusama.json index fba74b17f9607f58bdb17d7a0b05c8b764a9c4e5..66a705a40869ce6eb3fe2ac7a028ce68c784c18c 100644 --- a/cumulus/parachains/chain-specs/asset-hub-kusama.json +++ b/cumulus/parachains/chain-specs/asset-hub-kusama.json @@ -25,7 +25,10 @@ "/dns/statemine-bootnode.radiumblock.com/tcp/30336/wss/p2p/12D3KooWCKUrE5uaXQ288ko3Ex3zCyozyJLG47KEYTopinnXNtYL", "/dns/mine14.rotko.net/tcp/33524/p2p/12D3KooWJUFnjR2PNbsJhudwPVaWCoZy1acPGKjM2cSuGj345BBu", "/dns/mine14.rotko.net/tcp/34524/ws/p2p/12D3KooWJUFnjR2PNbsJhudwPVaWCoZy1acPGKjM2cSuGj345BBu", - "/dns/mine14.rotko.net/tcp/35524/wss/p2p/12D3KooWJUFnjR2PNbsJhudwPVaWCoZy1acPGKjM2cSuGj345BBu" + "/dns/mine14.rotko.net/tcp/35524/wss/p2p/12D3KooWJUFnjR2PNbsJhudwPVaWCoZy1acPGKjM2cSuGj345BBu", + "/dns/asset-hub-kusama.bootnodes.polkadotters.com/tcp/30511/p2p/12D3KooWDpk7wVH7RgjErEvbvAZ2kY5VeaAwRJP5ojmn1e8b8UbU", + "/dns/asset-hub-kusama.bootnodes.polkadotters.com/tcp/30513/wss/p2p/12D3KooWDpk7wVH7RgjErEvbvAZ2kY5VeaAwRJP5ojmn1e8b8UbU", + "/dns/boot-kusama-assethub.luckyfriday.io/tcp/443/wss/p2p/12D3KooWSwaeFs6FNgpgh54fdoxSDAA4nJNaPE3PAcse2GRrG7b3" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/asset-hub-polkadot.json b/cumulus/parachains/chain-specs/asset-hub-polkadot.json index 685a00ddc7145ed650f7cb5496fe81cfb23f0ffb..16caa52ba91376ed30187c89d249ff38ed1ea42a 100644 --- a/cumulus/parachains/chain-specs/asset-hub-polkadot.json +++ b/cumulus/parachains/chain-specs/asset-hub-polkadot.json @@ -25,7 +25,10 @@ "/dns/statemint-bootnode.radiumblock.com/tcp/30333/p2p/12D3KooWLKxHom7f3XawRJqrF8RwiKK5Sj3qZqz5c7hF6eJeXhTx", "/dns/mint14.rotko.net/tcp/33514/p2p/12D3KooWKkzLjYF6M5eEs7nYiqEtRqY8SGVouoCwo3nCWsRnThDW", "/dns/mint14.rotko.net/tcp/34514/ws/p2p/12D3KooWKkzLjYF6M5eEs7nYiqEtRqY8SGVouoCwo3nCWsRnThDW", - "/dns/mint14.rotko.net/tcp/35514/wss/p2p/12D3KooWKkzLjYF6M5eEs7nYiqEtRqY8SGVouoCwo3nCWsRnThDW" + "/dns/mint14.rotko.net/tcp/35514/wss/p2p/12D3KooWKkzLjYF6M5eEs7nYiqEtRqY8SGVouoCwo3nCWsRnThDW", + "/dns/asset-hub-polkadot.bootnodes.polkadotters.com/tcp/30508/p2p/12D3KooWKbfY9a9oywxMJKiALmt7yhrdQkjXMtvxhhDDN23vG93R", + "/dns/asset-hub-polkadot.bootnodes.polkadotters.com/tcp/30510/wss/p2p/12D3KooWKbfY9a9oywxMJKiALmt7yhrdQkjXMtvxhhDDN23vG93R", + "/dns/boot-polkadot-assethub.luckyfriday.io/tcp/443/wss/p2p/12D3KooWDR9M7CjV1xdjCRbRwkFn1E7sjMaL4oYxGyDWxuLrFc2J" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/asset-hub-westend.json b/cumulus/parachains/chain-specs/asset-hub-westend.json index 6f42b5f7d8bb4a76c7390b538a68b1bb0acc1613..670935c9d2474242307d691d8707030c70f49982 100644 --- a/cumulus/parachains/chain-specs/asset-hub-westend.json +++ b/cumulus/parachains/chain-specs/asset-hub-westend.json @@ -23,7 +23,9 @@ "/dns/westmint-bootnode.radiumblock.com/tcp/30333/p2p/12D3KooWDoq4PVdWm5nzRSvEz3DSSKjVgRhWVUaKyi5JMKwJKYbk", "/dns/wmint14.rotko.net/tcp/33534/p2p/12D3KooWE4UDXqgtTcMCyUQ8S4uvaT8VMzzTBA6NWmKuYwTacWuN", "/dns/wmint14.rotko.net/tcp/34534/ws/p2p/12D3KooWE4UDXqgtTcMCyUQ8S4uvaT8VMzzTBA6NWmKuYwTacWuN", - "/dns/wmint14.rotko.net/tcp/35534/wss/p2p/12D3KooWE4UDXqgtTcMCyUQ8S4uvaT8VMzzTBA6NWmKuYwTacWuN" + "/dns/wmint14.rotko.net/tcp/35534/wss/p2p/12D3KooWE4UDXqgtTcMCyUQ8S4uvaT8VMzzTBA6NWmKuYwTacWuN", + "/dns/asset-hub-westend.bootnodes.polkadotters.com/tcp/30514/p2p/12D3KooWNFYysCqmojxqjjaTfD2VkWBNngfyUKWjcR4WFixfHNTk", + "/dns/asset-hub-westend.bootnodes.polkadotters.com/tcp/30516/wss/p2p/12D3KooWNFYysCqmojxqjjaTfD2VkWBNngfyUKWjcR4WFixfHNTk" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/bridge-hub-kusama.json b/cumulus/parachains/chain-specs/bridge-hub-kusama.json index 0ef81806cc5c08621f3c0a308425c72f836c3ef4..6644ea41ab748548d3c6dcbd06951522672dbb53 100644 --- a/cumulus/parachains/chain-specs/bridge-hub-kusama.json +++ b/cumulus/parachains/chain-specs/bridge-hub-kusama.json @@ -25,7 +25,10 @@ "/dns/bridgehub-kusama-bootnode.radiumblock.com/tcp/30336/wss/p2p/12D3KooWQMWofXj8v3RroDNnrhv1iURqm8vnaG98AdGnCn2YoDcW", "/dns/kbr13.rotko.net/tcp/33553/p2p/12D3KooWAmBp54mUEYtvsk2kxNEsDbAvdUMcaghxKXgUQxmPEQ66", "/dns/kbr13.rotko.net/tcp/34553/ws/p2p/12D3KooWAmBp54mUEYtvsk2kxNEsDbAvdUMcaghxKXgUQxmPEQ66", - "/dns/kbr13.rotko.net/tcp/35553/wss/p2p/12D3KooWAmBp54mUEYtvsk2kxNEsDbAvdUMcaghxKXgUQxmPEQ66" + "/dns/kbr13.rotko.net/tcp/35553/wss/p2p/12D3KooWAmBp54mUEYtvsk2kxNEsDbAvdUMcaghxKXgUQxmPEQ66", + "/dns/bridge-hub-kusama.bootnodes.polkadotters.com/tcp/30520/p2p/12D3KooWH3pucezRRS5esoYyzZsUkKWcPSByQxEvmM819QL1HPLV", + "/dns/bridge-hub-kusama.bootnodes.polkadotters.com/tcp/30522/wss/p2p/12D3KooWH3pucezRRS5esoYyzZsUkKWcPSByQxEvmM819QL1HPLV", + "/dns/boot-kusama-bridgehub.luckyfriday.io/tcp/443/wss/p2p/12D3KooWQybw6AFmAvrFfwUQnNxUpS12RovapD6oorh2mAJr4xyd" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/bridge-hub-polkadot.json b/cumulus/parachains/chain-specs/bridge-hub-polkadot.json index 130bdf31ef211ba6d353fa97944b2bf80320997d..c51c5eff89b86a0ce553bbcfc472f6fb5cf0d535 100644 --- a/cumulus/parachains/chain-specs/bridge-hub-polkadot.json +++ b/cumulus/parachains/chain-specs/bridge-hub-polkadot.json @@ -21,7 +21,10 @@ "/dns/bridgehub-polkadot-bootnode.radiumblock.com/tcp/30333/p2p/12D3KooWPNZm78tWUmKbta3SXdkqTPsquRc8ekEbJjZsGGi7YiRi", "/dns/pbr13.rotko.net/tcp/33543/p2p/12D3KooWMxZY7tDc2Rh454VaJJ7RexKAXVS6xSBEvTnXSGCnuGDw", "/dns/pbr13.rotko.net/tcp/34543/ws/p2p/12D3KooWMxZY7tDc2Rh454VaJJ7RexKAXVS6xSBEvTnXSGCnuGDw", - "/dns/pbr13.rotko.net/tcp/35543/wss/p2p/12D3KooWMxZY7tDc2Rh454VaJJ7RexKAXVS6xSBEvTnXSGCnuGDw" + "/dns/pbr13.rotko.net/tcp/35543/wss/p2p/12D3KooWMxZY7tDc2Rh454VaJJ7RexKAXVS6xSBEvTnXSGCnuGDw", + "/dns/bridge-hub-polkadot.bootnodes.polkadotters.com/tcp/30517/p2p/12D3KooWLUNE3LHPDa1WrrZaYT7ArK66CLM1bPv7kKz74UcLnQRB", + "/dns/bridge-hub-polkadot.bootnodes.polkadotters.com/tcp/30519/wss/p2p/12D3KooWLUNE3LHPDa1WrrZaYT7ArK66CLM1bPv7kKz74UcLnQRB", + "/dns/boot-polkadot-bridgehub.luckyfriday.io/tcp/443/wss/p2p/12D3KooWKf3mBXHjLbwtPqv1BdbQuwbFNcQQYxASS7iQ25264AXH" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/bridge-hub-westend.json b/cumulus/parachains/chain-specs/bridge-hub-westend.json index 018ab0ee6fd9810595c841237dd253b9463aed79..447207a58107a95bcd5fca173d9d5476a0ce9cab 100644 --- a/cumulus/parachains/chain-specs/bridge-hub-westend.json +++ b/cumulus/parachains/chain-specs/bridge-hub-westend.json @@ -19,7 +19,9 @@ "/dns/bridgehub-westend-bootnode.radiumblock.com/tcp/30336/wss/p2p/12D3KooWBsBArCMxmQyo3feCEqMWuwyhb2LTRK8hmCCJxgrNeMke", "/dns/wbr13.rotko.net/tcp/33563/p2p/12D3KooWJyeRHpxZZbfBCNEgeUFzmRC5AMSAs2tJhjJS1k5hULkD", "/dns/wbr13.rotko.net/tcp/34563/ws/p2p/12D3KooWJyeRHpxZZbfBCNEgeUFzmRC5AMSAs2tJhjJS1k5hULkD", - "/dns/wbr13.rotko.net/tcp/35563/wss/p2p/12D3KooWJyeRHpxZZbfBCNEgeUFzmRC5AMSAs2tJhjJS1k5hULkD" + "/dns/wbr13.rotko.net/tcp/35563/wss/p2p/12D3KooWJyeRHpxZZbfBCNEgeUFzmRC5AMSAs2tJhjJS1k5hULkD", + "/dns/bridge-hub-westend.bootnodes.polkadotters.com/tcp/30523/p2p/12D3KooWPkwgJofp4GeeRwNgXqkp2aFwdLkCWv3qodpBJLwK43Jj", + "/dns/bridge-hub-westend.bootnodes.polkadotters.com/tcp/30525/wss/p2p/12D3KooWPkwgJofp4GeeRwNgXqkp2aFwdLkCWv3qodpBJLwK43Jj" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/collectives-polkadot.json b/cumulus/parachains/chain-specs/collectives-polkadot.json index e9f690234e4381f54377c7fe7174f25834a973a5..ce80e21ae625e813be0f71245bc454cb284455f3 100644 --- a/cumulus/parachains/chain-specs/collectives-polkadot.json +++ b/cumulus/parachains/chain-specs/collectives-polkadot.json @@ -25,7 +25,10 @@ "/dns/collectives-polkadot-bootnode.radiumblock.com/tcp/30336/wss/p2p/12D3KooWDumvnNwPbBg5inBEapgjKU7ECdMHHgwfYeGWUkzYUE1c", "/dns/pch13.rotko.net/tcp/33573/p2p/12D3KooWRXudHoazPZ9osMfdY38e8CBxQLD4RhrVeHpRSNNpcDtH", "/dns/pch13.rotko.net/tcp/34573/ws/p2p/12D3KooWRXudHoazPZ9osMfdY38e8CBxQLD4RhrVeHpRSNNpcDtH", - "/dns/pch13.rotko.net/tcp/35573/wss/p2p/12D3KooWRXudHoazPZ9osMfdY38e8CBxQLD4RhrVeHpRSNNpcDtH" + "/dns/pch13.rotko.net/tcp/35573/wss/p2p/12D3KooWRXudHoazPZ9osMfdY38e8CBxQLD4RhrVeHpRSNNpcDtH", + "/dns/collectives-polkadot.bootnodes.polkadotters.com/tcp/30526/p2p/12D3KooWNohUjvJtGKUa8Vhy8C1ZBB5N8JATB6e7rdLVCioeb3ff", + "/dns/collectives-polkadot.bootnodes.polkadotters.com/tcp/30528/wss/p2p/12D3KooWNohUjvJtGKUa8Vhy8C1ZBB5N8JATB6e7rdLVCioeb3ff", + "/dns/boot-polkadot-collectives.luckyfriday.io/tcp/443/wss/p2p/12D3KooWCzifnPooTt4kvTnXT7FTKTymVL7xn7DURQLsS2AKpf6w" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/collectives-westend.json b/cumulus/parachains/chain-specs/collectives-westend.json index ffe73b5a05a391cc1c5396c235460830c023297a..e459c631f8be9df7c5c52993c116f11ef619fefe 100644 --- a/cumulus/parachains/chain-specs/collectives-westend.json +++ b/cumulus/parachains/chain-specs/collectives-westend.json @@ -25,7 +25,9 @@ "/dns/westend-collectives-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWPFM93jgm4pgxx8PM8WJKAJF49qia8jRB95uciUQwYh7m", "/dns/wch13.rotko.net/tcp/33593/p2p/12D3KooWPG85zhuSRoyptjLkFD4iJFistjiBmc15JgQ96B4fdXYr", "/dns/wch13.rotko.net/tcp/34593/ws/p2p/12D3KooWPG85zhuSRoyptjLkFD4iJFistjiBmc15JgQ96B4fdXYr", - "/dns/wch13.rotko.net/tcp/35593/wss/p2p/12D3KooWPG85zhuSRoyptjLkFD4iJFistjiBmc15JgQ96B4fdXYr" + "/dns/wch13.rotko.net/tcp/35593/wss/p2p/12D3KooWPG85zhuSRoyptjLkFD4iJFistjiBmc15JgQ96B4fdXYr", + "/dns/collectives-westend.bootnodes.polkadotters.com/tcp/30529/p2p/12D3KooWAFkXNSBfyPduZVgfS7pj5NuVpbU8Ee5gHeF8wvos7Yqn", + "/dns/collectives-westend.bootnodes.polkadotters.com/tcp/30531/wss/p2p/12D3KooWAFkXNSBfyPduZVgfS7pj5NuVpbU8Ee5gHeF8wvos7Yqn" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/coretime-westend.json b/cumulus/parachains/chain-specs/coretime-westend.json index c79fd582348b0223cd4c1de71b94075751cfdeb2..adb35b8a349f64b87d9aa96feb84e5068f261ad0 100644 --- a/cumulus/parachains/chain-specs/coretime-westend.json +++ b/cumulus/parachains/chain-specs/coretime-westend.json @@ -4,7 +4,8 @@ "chainType": "Live", "bootNodes": [ "/dns/westend-coretime-collator-node-0.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWP93Dzk8T7GWxyWw9jhLcz8Pksokk3R9vL2eEH337bNkT", - "/dns/westend-coretime-collator-node-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWMh2imeAzsZKGQgm2cv6Uoep3GBYtwGfujt1bs5YfVzkH" + "/dns/westend-coretime-collator-node-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWMh2imeAzsZKGQgm2cv6Uoep3GBYtwGfujt1bs5YfVzkH", + "/dns/boot.metaspan.io/tcp/33019/p2p/12D3KooWCa1uNnEZqiqJY9jkKNQxwSLGPeZ5MjWHhjQMGwga9JMM" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/people-westend.json b/cumulus/parachains/chain-specs/people-westend.json index fa29853c70b05e47df50445935be42a4637f240d..29fa0c9cde79c0daecbce029c8fa377ba1ff6918 100644 --- a/cumulus/parachains/chain-specs/people-westend.json +++ b/cumulus/parachains/chain-specs/people-westend.json @@ -10,7 +10,9 @@ "/dns/westend-people-collator-node-2.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWGVYTVKW7tYe51JvetvGvVLDPXzqQX1mueJgz14FgkmHG", "/dns/westend-people-collator-node-2.parity-testnet.parity.io/tcp/443/wss/p2p/12D3KooWGVYTVKW7tYe51JvetvGvVLDPXzqQX1mueJgz14FgkmHG", "/dns/westend-people-collator-node-3.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWCF1eA2Gap69zgXD7Df3e9DqDUsGoByocggTGejoHjK23", - "/dns/westend-people-collator-node-3.parity-testnet.parity.io/tcp/443/wss/p2p/12D3KooWCF1eA2Gap69zgXD7Df3e9DqDUsGoByocggTGejoHjK23" + "/dns/westend-people-collator-node-3.parity-testnet.parity.io/tcp/443/wss/p2p/12D3KooWCF1eA2Gap69zgXD7Df3e9DqDUsGoByocggTGejoHjK23", + "/dns/identity-westend.bootnodes.polkadotters.com/tcp/30532/p2p/12D3KooWKr9San6KTM7REJ95cBaDoiciGcWnW8TTftEJgxGF5Ehb", + "/dns/identity-westend.bootnodes.polkadotters.com/tcp/30534/wss/p2p/12D3KooWKr9San6KTM7REJ95cBaDoiciGcWnW8TTftEJgxGF5Ehb" ], "telemetryEndpoints": null, "protocolId": null, @@ -79,4 +81,4 @@ "childrenDefault": {} } } -} \ No newline at end of file +} diff --git a/cumulus/parachains/common/src/impls.rs b/cumulus/parachains/common/src/impls.rs index 957538b7cdadbd68ed298a5a867cd1a7754e083d..6a990740f0f1930fc259726fd85e966ae48887a2 100644 --- a/cumulus/parachains/common/src/impls.rs +++ b/cumulus/parachains/common/src/impls.rs @@ -217,7 +217,7 @@ mod tests { pub const MaxReserves: u32 = 50; } - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type RuntimeOrigin = RuntimeOrigin; diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml index f4f8b3603ba61e44195d6be8151b296f002ac720..98762beb0cb23132c3880515287328bb09bde032 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml @@ -17,7 +17,7 @@ sp-core = { path = "../../../../../../../../substrate/primitives/core", default- frame-support = { path = "../../../../../../../../substrate/frame/support", default-features = false } # Cumulus -parachains-common = { path = "../../../../../../../parachains/common" } +parachains-common = { path = "../../../../../../common" } cumulus-primitives-core = { path = "../../../../../../../primitives/core", default-features = false } emulated-integration-tests-common = { path = "../../../../common", default-features = false } asset-hub-rococo-runtime = { path = "../../../../../../runtimes/assets/asset-hub-rococo" } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs index 80db56444696a557d5a11fae47fe2bd72d3f00a0..e5378b35f5e484e10db94c66bc5244099b682604 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs @@ -14,17 +14,24 @@ // limitations under the License. // Substrate -use sp_core::storage::Storage; +use frame_support::parameter_types; +use sp_core::{sr25519, storage::Storage}; // Cumulus use emulated_integration_tests_common::{ - accounts, build_genesis_storage, collators, SAFE_XCM_VERSION, + accounts, build_genesis_storage, collators, get_account_id_from_seed, + PenpalSiblingSovereignAccount, PenpalTeleportableAssetLocation, RESERVABLE_ASSET_ID, + SAFE_XCM_VERSION, }; -use parachains_common::Balance; +use parachains_common::{AccountId, Balance}; pub const PARA_ID: u32 = 1000; pub const ED: Balance = testnet_parachains_constants::rococo::currency::EXISTENTIAL_DEPOSIT; +parameter_types! { + pub AssetHubRococoAssetOwner: AccountId = get_account_id_from_seed::("Alice"); +} + pub fn genesis() -> Storage { let genesis_config = asset_hub_rococo_runtime::RuntimeGenesisConfig { system: asset_hub_rococo_runtime::SystemConfig::default(), @@ -60,6 +67,22 @@ pub fn genesis() -> Storage { safe_xcm_version: Some(SAFE_XCM_VERSION), ..Default::default() }, + assets: asset_hub_rococo_runtime::AssetsConfig { + assets: vec![(RESERVABLE_ASSET_ID, AssetHubRococoAssetOwner::get(), true, ED)], + ..Default::default() + }, + foreign_assets: asset_hub_rococo_runtime::ForeignAssetsConfig { + assets: vec![ + // Penpal's teleportable asset representation + ( + PenpalTeleportableAssetLocation::get(), + PenpalSiblingSovereignAccount::get(), + true, + ED, + ), + ], + ..Default::default() + }, ..Default::default() }; diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs index 00f412564205507f2deb6b516ace435302fcf4c4..f1e972e869dc94465aa28356a7eaa1c4cd4503ef 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs @@ -21,7 +21,7 @@ use frame_support::traits::OnInitialize; // Cumulus use emulated_integration_tests_common::{ impl_accounts_helpers_for_parachain, impl_assert_events_helpers_for_parachain, - impl_assets_helpers_for_parachain, impl_foreign_assets_helpers_for_parachain, + impl_assets_helpers_for_parachain, impl_assets_helpers_for_system_parachain, impl_xcm_helpers_for_parachain, impls::Parachain, xcm_emulator::decl_test_parachains, }; use rococo_emulated_chain::Rococo; @@ -54,6 +54,6 @@ decl_test_parachains! { // AssetHubRococo implementation impl_accounts_helpers_for_parachain!(AssetHubRococo); impl_assert_events_helpers_for_parachain!(AssetHubRococo); -impl_assets_helpers_for_parachain!(AssetHubRococo, Rococo); -impl_foreign_assets_helpers_for_parachain!(AssetHubRococo, Rococo); +impl_assets_helpers_for_system_parachain!(AssetHubRococo, Rococo); +impl_assets_helpers_for_parachain!(AssetHubRococo); impl_xcm_helpers_for_parachain!(AssetHubRococo); diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/Cargo.toml index d4764f63bf64d3cc650d53ee8c637129448121a2..a42a9abf618d403852561d5d4b20e7fb6ad576e7 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/Cargo.toml @@ -17,7 +17,7 @@ sp-core = { path = "../../../../../../../../substrate/primitives/core", default- frame-support = { path = "../../../../../../../../substrate/frame/support", default-features = false } # Cumulus -parachains-common = { path = "../../../../../../../parachains/common" } +parachains-common = { path = "../../../../../../common" } cumulus-primitives-core = { path = "../../../../../../../primitives/core", default-features = false } emulated-integration-tests-common = { path = "../../../../common", default-features = false } asset-hub-westend-runtime = { path = "../../../../../../runtimes/assets/asset-hub-westend" } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs index b2e4645ee076803accc14c3fc32653228c61ab98..219d1306906cbc6c20609870bec496c5bd16eaeb 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs @@ -14,17 +14,24 @@ // limitations under the License. // Substrate -use sp_core::storage::Storage; +use frame_support::parameter_types; +use sp_core::{sr25519, storage::Storage}; // Cumulus use emulated_integration_tests_common::{ - accounts, build_genesis_storage, collators, SAFE_XCM_VERSION, + accounts, build_genesis_storage, collators, get_account_id_from_seed, + PenpalSiblingSovereignAccount, PenpalTeleportableAssetLocation, RESERVABLE_ASSET_ID, + SAFE_XCM_VERSION, }; -use parachains_common::Balance; +use parachains_common::{AccountId, Balance}; pub const PARA_ID: u32 = 1000; pub const ED: Balance = testnet_parachains_constants::westend::currency::EXISTENTIAL_DEPOSIT; +parameter_types! { + pub AssetHubWestendAssetOwner: AccountId = get_account_id_from_seed::("Alice"); +} + pub fn genesis() -> Storage { let genesis_config = asset_hub_westend_runtime::RuntimeGenesisConfig { system: asset_hub_westend_runtime::SystemConfig::default(), @@ -56,6 +63,22 @@ pub fn genesis() -> Storage { safe_xcm_version: Some(SAFE_XCM_VERSION), ..Default::default() }, + assets: asset_hub_westend_runtime::AssetsConfig { + assets: vec![(RESERVABLE_ASSET_ID, AssetHubWestendAssetOwner::get(), true, ED)], + ..Default::default() + }, + foreign_assets: asset_hub_westend_runtime::ForeignAssetsConfig { + assets: vec![ + // Penpal's teleportable asset representation + ( + PenpalTeleportableAssetLocation::get(), + PenpalSiblingSovereignAccount::get(), + true, + ED, + ), + ], + ..Default::default() + }, ..Default::default() }; diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs index 25d7c1079b4dd3cfa08a27ac3e10cbc498279e34..7f05eefb4c208dab2192bc111347ffa4f2760fc0 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs @@ -21,7 +21,7 @@ use frame_support::traits::OnInitialize; // Cumulus use emulated_integration_tests_common::{ impl_accounts_helpers_for_parachain, impl_assert_events_helpers_for_parachain, - impl_assets_helpers_for_parachain, impl_foreign_assets_helpers_for_parachain, + impl_assets_helpers_for_parachain, impl_assets_helpers_for_system_parachain, impl_xcm_helpers_for_parachain, impls::Parachain, xcm_emulator::decl_test_parachains, }; use westend_emulated_chain::Westend; @@ -54,6 +54,6 @@ decl_test_parachains! { // AssetHubWestend implementation impl_accounts_helpers_for_parachain!(AssetHubWestend); impl_assert_events_helpers_for_parachain!(AssetHubWestend); -impl_assets_helpers_for_parachain!(AssetHubWestend, Westend); -impl_foreign_assets_helpers_for_parachain!(AssetHubWestend, Westend); +impl_assets_helpers_for_system_parachain!(AssetHubWestend, Westend); +impl_assets_helpers_for_parachain!(AssetHubWestend); impl_xcm_helpers_for_parachain!(AssetHubWestend); diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/Cargo.toml index 322d8b44e6ea6b293fb1c2f52e0de80fa9cd29f1..789f10a35f268c62e0cc9fa153c99e7e5282ee8d 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/Cargo.toml @@ -17,7 +17,7 @@ sp-core = { path = "../../../../../../../../substrate/primitives/core", default- frame-support = { path = "../../../../../../../../substrate/frame/support", default-features = false } # Cumulus -parachains-common = { path = "../../../../../../../parachains/common" } +parachains-common = { path = "../../../../../../common" } emulated-integration-tests-common = { path = "../../../../common", default-features = false } bridge-hub-rococo-runtime = { path = "../../../../../../runtimes/bridge-hubs/bridge-hub-rococo" } bridge-hub-common = { path = "../../../../../../runtimes/bridge-hubs/common", default-features = false } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/Cargo.toml index ec1386b7f6e2b231e7f9ffdc925b3d0c802bf4f2..d82971cf55aeddf20032be952b8a980014434f6b 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/Cargo.toml @@ -17,7 +17,7 @@ sp-core = { path = "../../../../../../../../substrate/primitives/core", default- frame-support = { path = "../../../../../../../../substrate/frame/support", default-features = false } # Cumulus -parachains-common = { path = "../../../../../../../parachains/common" } +parachains-common = { path = "../../../../../../common" } emulated-integration-tests-common = { path = "../../../../common", default-features = false } bridge-hub-westend-runtime = { path = "../../../../../../runtimes/bridge-hubs/bridge-hub-westend" } bridge-hub-common = { path = "../../../../../../runtimes/bridge-hubs/common", default-features = false } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/Cargo.toml index 03f755b666afbf87e876a7f7d5111aa98a1bc727..4c2a7d3c274dce6eade9c9d42be00301bd6dc462 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/Cargo.toml @@ -17,7 +17,7 @@ sp-core = { path = "../../../../../../../../substrate/primitives/core", default- frame-support = { path = "../../../../../../../../substrate/frame/support", default-features = false } # Cumulus -parachains-common = { path = "../../../../../../../parachains/common" } +parachains-common = { path = "../../../../../../common" } cumulus-primitives-core = { path = "../../../../../../../primitives/core", default-features = false } emulated-integration-tests-common = { path = "../../../../common", default-features = false } collectives-westend-runtime = { path = "../../../../../../runtimes/collectives/collectives-westend" } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/Cargo.toml index 65a358d0ef2f6b697453f2c1cbef20d3b2cd93a8..f7fe93d27775a28cb560d8791a3b0d8ed49c9d68 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/Cargo.toml @@ -14,7 +14,7 @@ sp-core = { path = "../../../../../../../../substrate/primitives/core", default- frame-support = { path = "../../../../../../../../substrate/frame/support", default-features = false } # Cumulus -parachains-common = { path = "../../../../../../../parachains/common" } +parachains-common = { path = "../../../../../../common" } cumulus-primitives-core = { path = "../../../../../../../primitives/core", default-features = false } emulated-integration-tests-common = { path = "../../../../common", default-features = false } people-rococo-runtime = { path = "../../../../../../runtimes/people/people-rococo" } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/Cargo.toml index 075698848bcf0705c00ea46e3443802ca2fbe6b0..57a767e0c2a3eb7d23df7f8d95fd78128c996f35 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/Cargo.toml @@ -14,7 +14,7 @@ sp-core = { path = "../../../../../../../../substrate/primitives/core", default- frame-support = { path = "../../../../../../../../substrate/frame/support", default-features = false } # Cumulus -parachains-common = { path = "../../../../../../../parachains/common" } +parachains-common = { path = "../../../../../../common" } cumulus-primitives-core = { path = "../../../../../../../primitives/core", default-features = false } emulated-integration-tests-common = { path = "../../../../common", default-features = false } people-westend-runtime = { path = "../../../../../../runtimes/people/people-westend" } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/Cargo.toml index a853825d8ef626aef9f7a7d1c013667aea9aeb89..2ac508273c6158ddae08615d8574102f98e3e788 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/Cargo.toml @@ -16,10 +16,11 @@ workspace = true sp-core = { path = "../../../../../../../../substrate/primitives/core", default-features = false } frame-support = { path = "../../../../../../../../substrate/frame/support", default-features = false } +# Polkadot +xcm = { package = "staging-xcm", path = "../../../../../../../../polkadot/xcm", default-features = false } + # Cumulus -parachains-common = { path = "../../../../../../../parachains/common" } +parachains-common = { path = "../../../../../../common" } cumulus-primitives-core = { path = "../../../../../../../primitives/core", default-features = false } emulated-integration-tests-common = { path = "../../../../common", default-features = false } penpal-runtime = { path = "../../../../../../runtimes/testing/penpal" } -rococo-emulated-chain = { path = "../../../relays/rococo" } -westend-emulated-chain = { path = "../../../relays/westend" } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs index 9ab32a977d7113a1f01fc287d591f2cc2c550266..d81ab8143ddba678617aaa67db122298ce29606c 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs @@ -14,19 +14,27 @@ // limitations under the License. // Substrate +use frame_support::parameter_types; use sp_core::{sr25519, storage::Storage}; +// Polkadot +use xcm::v3::Location; // Cumulus use emulated_integration_tests_common::{ accounts, build_genesis_storage, collators, get_account_id_from_seed, SAFE_XCM_VERSION, }; -use parachains_common::Balance; - +use parachains_common::{AccountId, Balance}; +use penpal_runtime::xcm_config::{LocalReservableFromAssetHub, RelayLocation}; // Penpal pub const PARA_ID_A: u32 = 2000; pub const PARA_ID_B: u32 = 2001; pub const ED: Balance = penpal_runtime::EXISTENTIAL_DEPOSIT; +parameter_types! { + pub PenpalSudoAccount: AccountId = get_account_id_from_seed::("Alice"); + pub PenpalAssetOwner: AccountId = PenpalSudoAccount::get(); +} + pub fn genesis(para_id: u32) -> Storage { let genesis_config = penpal_runtime::RuntimeGenesisConfig { system: penpal_runtime::SystemConfig::default(), @@ -58,8 +66,35 @@ pub fn genesis(para_id: u32) -> Storage { safe_xcm_version: Some(SAFE_XCM_VERSION), ..Default::default() }, - sudo: penpal_runtime::SudoConfig { - key: Some(get_account_id_from_seed::("Alice")), + sudo: penpal_runtime::SudoConfig { key: Some(PenpalSudoAccount::get()) }, + assets: penpal_runtime::AssetsConfig { + assets: vec![( + penpal_runtime::xcm_config::TELEPORTABLE_ASSET_ID, + PenpalAssetOwner::get(), + false, + ED, + )], + ..Default::default() + }, + foreign_assets: penpal_runtime::ForeignAssetsConfig { + assets: vec![ + // Relay Native asset representation + ( + Location::try_from(RelayLocation::get()).expect("conversion works"), + PenpalAssetOwner::get(), + true, + ED, + ), + // Sufficient AssetHub asset representation + ( + Location::try_from(LocalReservableFromAssetHub::get()) + .expect("conversion works"), + PenpalAssetOwner::get(), + true, + ED, + ), + ], + ..Default::default() }, ..Default::default() }; diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs index 8f586a46a75cb68a7ccdf62d2f23a1865dcf5d9c..0b49c7a3e091a615632728b6ba74ca4dffefae66 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs @@ -14,9 +14,9 @@ // limitations under the License. mod genesis; -pub use genesis::{genesis, ED, PARA_ID_A, PARA_ID_B}; +pub use genesis::{genesis, PenpalAssetOwner, PenpalSudoAccount, ED, PARA_ID_A, PARA_ID_B}; pub use penpal_runtime::xcm_config::{ - LocalTeleportableToAssetHub, LocalTeleportableToAssetHubV3, XcmConfig, + CustomizableAssetFromSystemAssetHub, LocalTeleportableToAssetHub, XcmConfig, }; // Substrate @@ -27,8 +27,6 @@ use emulated_integration_tests_common::{ impl_accounts_helpers_for_parachain, impl_assert_events_helpers_for_parachain, impl_assets_helpers_for_parachain, impls::Parachain, xcm_emulator::decl_test_parachains, }; -use rococo_emulated_chain::Rococo; -use westend_emulated_chain::Westend; // Penpal Parachain declaration decl_test_parachains! { @@ -75,7 +73,7 @@ decl_test_parachains! { // Penpal implementation impl_accounts_helpers_for_parachain!(PenpalA); impl_accounts_helpers_for_parachain!(PenpalB); -impl_assets_helpers_for_parachain!(PenpalA, Rococo); -impl_assets_helpers_for_parachain!(PenpalB, Westend); impl_assert_events_helpers_for_parachain!(PenpalA); impl_assert_events_helpers_for_parachain!(PenpalB); +impl_assets_helpers_for_parachain!(PenpalA); +impl_assets_helpers_for_parachain!(PenpalB); diff --git a/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/Cargo.toml index 2d27426cca7599e3284caa6c8ca69a2d546a8da2..7ac65b0ee1ded60939072ff61d6886fa53a10b94 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/Cargo.toml @@ -25,5 +25,5 @@ rococo-runtime-constants = { path = "../../../../../../../polkadot/runtime/rococ rococo-runtime = { path = "../../../../../../../polkadot/runtime/rococo" } # Cumulus -parachains-common = { path = "../../../../../../parachains/common" } +parachains-common = { path = "../../../../../common" } emulated-integration-tests-common = { path = "../../../common", default-features = false } diff --git a/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/src/genesis.rs index 7db9679f1c3e1554ce97ae2f057fbcd8c27183ca..55437645b0523b577c2e9d455952f5526ba9df0b 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/src/genesis.rs @@ -78,7 +78,7 @@ pub fn genesis() -> Storage { }, babe: rococo_runtime::BabeConfig { authorities: Default::default(), - epoch_config: Some(rococo_runtime::BABE_GENESIS_EPOCH_CONFIG), + epoch_config: rococo_runtime::BABE_GENESIS_EPOCH_CONFIG, ..Default::default() }, sudo: rococo_runtime::SudoConfig { diff --git a/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml index abc40c2040681cc4cb41246a9872d557134b6dde..12a3ad60e0e046f1881d322118d522da3f9b52b1 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml @@ -11,7 +11,6 @@ publish = false workspace = true [dependencies] - # Substrate sp-core = { path = "../../../../../../../substrate/primitives/core", default-features = false } sp-runtime = { path = "../../../../../../../substrate/primitives/runtime", default-features = false } @@ -27,5 +26,5 @@ westend-runtime-constants = { path = "../../../../../../../polkadot/runtime/west westend-runtime = { path = "../../../../../../../polkadot/runtime/westend" } # Cumulus -parachains-common = { path = "../../../../../../parachains/common" } +parachains-common = { path = "../../../../../common" } emulated-integration-tests-common = { path = "../../../common", default-features = false } diff --git a/cumulus/parachains/integration-tests/emulated/chains/relays/westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/relays/westend/src/genesis.rs index 578b307dd9339211de9cb793e4b4f63ffe9d3b15..700b80e63f6cf68e6095b7e02d84bb285ce720f9 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/relays/westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/relays/westend/src/genesis.rs @@ -94,7 +94,7 @@ pub fn genesis() -> Storage { }, babe: westend_runtime::BabeConfig { authorities: Default::default(), - epoch_config: Some(westend_runtime::BABE_GENESIS_EPOCH_CONFIG), + epoch_config: westend_runtime::BABE_GENESIS_EPOCH_CONFIG, ..Default::default() }, configuration: westend_runtime::ConfigurationConfig { config: get_host_config() }, diff --git a/cumulus/parachains/integration-tests/emulated/common/Cargo.toml b/cumulus/parachains/integration-tests/emulated/common/Cargo.toml index 721c58fd86481ca7269db8567c1cc0fd3507a92b..8c44cce7d922dff0a69a4273af7adb69819d2fce 100644 --- a/cumulus/parachains/integration-tests/emulated/common/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/common/Cargo.toml @@ -27,6 +27,7 @@ pallet-message-queue = { path = "../../../../../substrate/frame/message-queue" } # Polkadot polkadot-primitives = { path = "../../../../../polkadot/primitives" } +polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain" } polkadot-runtime-parachains = { path = "../../../../../polkadot/runtime/parachains" } xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm" } pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm" } diff --git a/cumulus/parachains/integration-tests/emulated/common/src/impls.rs b/cumulus/parachains/integration-tests/emulated/common/src/impls.rs index 4bbb4701e43918f5f34b250a23c4c483f2b2d4d1..618c3addc5d0c67c3954610425345d3ec8b2f36b 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/impls.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/impls.rs @@ -114,7 +114,7 @@ where .expect("Bridge message does not exist") .into(); let payload = Vec::::decode(&mut &encoded_payload[..]) - .expect("Decodign XCM message failed"); + .expect("Decoding XCM message failed"); let id: u32 = LaneIdWrapper(*lane).into(); let message = BridgeMessage { id, nonce, payload }; @@ -265,7 +265,7 @@ macro_rules! impl_assert_events_helpers_for_relay_chain { $crate::impls::assert_expected_events!( Self, vec![ - // XCM is succesfully received and proccessed + // XCM is successfully received and processed [<$chain RuntimeEvent>]::::MessageQueue($crate::impls::pallet_message_queue::Event::Processed { origin: $crate::impls::AggregateMessageOrigin::Ump($crate::impls::UmpQueueId::Para(id)), weight_used, @@ -343,7 +343,7 @@ macro_rules! impl_hrmp_channels_helpers_for_relay_chain { ::Runtime, >::contains_key(&channel_id); - // Check the HRMP channel has been successfully registrered + // Check the HRMP channel has been successfully registered assert!(hrmp_channel_exist) }); } @@ -362,7 +362,7 @@ macro_rules! impl_send_transact_helpers_for_relay_chain { recipient: $crate::impls::ParaId, call: $crate::impls::DoubleEncoded<()> ) { - use $crate::impls::{bx, Chain, RelayChain}; + use $crate::impls::{bx, Chain, RelayChain, Encode}; ::execute_with(|| { let root_origin = ::RuntimeOrigin::root(); @@ -370,10 +370,10 @@ macro_rules! impl_send_transact_helpers_for_relay_chain { let xcm = $crate::impls::xcm_transact_unpaid_execution(call, $crate::impls::OriginKind::Superuser); // Send XCM `Transact` - $crate::impls::assert_ok!(]>::XcmPallet::send( + $crate::impls::assert_ok!(]>::XcmPallet::send_blob( root_origin, bx!(destination.into()), - bx!(xcm), + xcm.encode().try_into().unwrap(), )); Self::assert_xcm_pallet_sent(); }); @@ -592,7 +592,7 @@ macro_rules! impl_assert_events_helpers_for_parachain { } #[macro_export] -macro_rules! impl_assets_helpers_for_parachain { +macro_rules! impl_assets_helpers_for_system_parachain { ( $chain:ident, $relay_chain:ident ) => { $crate::impls::paste::paste! { impl $chain { @@ -630,38 +630,6 @@ macro_rules! impl_assets_helpers_for_parachain { $crate::impls::xcm_transact_unpaid_execution(call, origin_kind) } - /// Mint assets making use of the assets pallet - pub fn mint_asset( - signed_origin: ::RuntimeOrigin, - id: u32, - beneficiary: $crate::impls::AccountId, - amount_to_mint: u128, - ) { - ::execute_with(|| { - $crate::impls::assert_ok!(]>::Assets::mint( - signed_origin, - id.clone().into(), - beneficiary.clone().into(), - amount_to_mint - )); - - type RuntimeEvent = <$chain as $crate::impls::Chain>::RuntimeEvent; - - $crate::impls::assert_expected_events!( - Self, - vec![ - RuntimeEvent::::Assets( - $crate::impls::pallet_assets::Event::Issued { asset_id, owner, amount } - ) => { - asset_id: *asset_id == id, - owner: *owner == beneficiary.clone().into(), - amount: *amount == amount_to_mint, - }, - ] - ); - }); - } - /// Force create and mint assets making use of the assets pallet pub fn force_create_and_mint_asset( id: u32, @@ -727,8 +695,8 @@ macro_rules! impl_assets_helpers_for_parachain { } #[macro_export] -macro_rules! impl_foreign_assets_helpers_for_parachain { - ( $chain:ident, $relay_chain:ident ) => { +macro_rules! impl_assets_helpers_for_parachain { + ( $chain:ident) => { $crate::impls::paste::paste! { impl $chain { /// Create foreign assets using sudo `ForeignAssets::force_create()` @@ -803,6 +771,118 @@ macro_rules! impl_foreign_assets_helpers_for_parachain { ); }); } + /// Create assets using sudo `Assets::force_create()` + pub fn force_create_asset( + id: u32, + owner: $crate::impls::AccountId, + is_sufficient: bool, + min_balance: u128, + prefund_accounts: Vec<($crate::impls::AccountId, u128)>, + ) { + use $crate::impls::Inspect; + let sudo_origin = <$chain as $crate::impls::Chain>::RuntimeOrigin::root(); + ::execute_with(|| { + $crate::impls::assert_ok!( + ]>::Assets::force_create( + sudo_origin, + id.clone().into(), + owner.clone().into(), + is_sufficient, + min_balance, + ) + ); + assert!(]>::Assets::asset_exists(id.clone())); + type RuntimeEvent = <$chain as $crate::impls::Chain>::RuntimeEvent; + $crate::impls::assert_expected_events!( + Self, + vec![ + RuntimeEvent::::Assets( + $crate::impls::pallet_assets::Event::ForceCreated { + asset_id, + .. + } + ) => { asset_id: *asset_id == id, }, + ] + ); + }); + for (beneficiary, amount) in prefund_accounts.into_iter() { + let signed_origin = + <$chain as $crate::impls::Chain>::RuntimeOrigin::signed(owner.clone()); + Self::mint_asset(signed_origin, id.clone(), beneficiary, amount); + } + } + + /// Mint assets making use of the assets pallet + pub fn mint_asset( + signed_origin: ::RuntimeOrigin, + id: u32, + beneficiary: $crate::impls::AccountId, + amount_to_mint: u128, + ) { + ::execute_with(|| { + $crate::impls::assert_ok!(]>::Assets::mint( + signed_origin, + id.clone().into(), + beneficiary.clone().into(), + amount_to_mint + )); + + type RuntimeEvent = <$chain as $crate::impls::Chain>::RuntimeEvent; + + $crate::impls::assert_expected_events!( + Self, + vec![ + RuntimeEvent::::Assets( + $crate::impls::pallet_assets::Event::Issued { asset_id, owner, amount } + ) => { + asset_id: *asset_id == id, + owner: *owner == beneficiary.clone().into(), + amount: *amount == amount_to_mint, + }, + ] + ); + }); + } + + /// Returns the encoded call for `create` from the assets pallet + pub fn create_asset_call( + asset_id: u32, + min_balance: $crate::impls::Balance, + admin: $crate::impls::AccountId, + ) -> $crate::impls::DoubleEncoded<()> { + use $crate::impls::{Chain, Encode}; + + ::RuntimeCall::Assets($crate::impls::pallet_assets::Call::< + ::Runtime, + $crate::impls::pallet_assets::Instance1, + >::create { + id: asset_id.into(), + min_balance, + admin: admin.into(), + }) + .encode() + .into() + } + + /// Returns the encoded call for `create` from the foreign assets pallet + pub fn create_foreign_asset_call( + asset_id: $crate::impls::v3::Location, + min_balance: $crate::impls::Balance, + admin: $crate::impls::AccountId, + ) -> $crate::impls::DoubleEncoded<()> { + use $crate::impls::{Chain, Encode}; + + ::RuntimeCall::ForeignAssets($crate::impls::pallet_assets::Call::< + ::Runtime, + $crate::impls::pallet_assets::Instance2, + >::create { + id: asset_id.into(), + min_balance, + admin: admin.into(), + }) + .encode() + .into() + } } } }; diff --git a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs index 1a5cc1f6fea6dde1882180d8389e9bcc7773d023..cbde0642f1a2965579196cda7e7ada4f291e9d98 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs @@ -21,17 +21,19 @@ pub use xcm_emulator; // Substrate use beefy_primitives::ecdsa_crypto::AuthorityId as BeefyId; +use frame_support::parameter_types; use grandpa::AuthorityId as GrandpaId; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_consensus_babe::AuthorityId as BabeId; use sp_core::{sr25519, storage::Storage, Pair, Public}; use sp_runtime::{ - traits::{IdentifyAccount, Verify}, + traits::{AccountIdConversion, IdentifyAccount, Verify}, BuildStorage, MultiSignature, }; // Polakdot use parachains_common::BlockNumber; +use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_parachains::configuration::HostConfiguration; // Cumulus @@ -49,6 +51,25 @@ pub const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION; type AccountPublic = ::Signer; +// This asset is added to AH as Asset and reserved transfer between Parachain and AH +pub const RESERVABLE_ASSET_ID: u32 = 1; +// This asset is added to AH as ForeignAsset and teleported between Penpal and AH +pub const TELEPORTABLE_ASSET_ID: u32 = 2; + +pub const PENPAL_ID: u32 = 2000; +pub const ASSETS_PALLET_ID: u8 = 50; + +parameter_types! { + pub PenpalTeleportableAssetLocation: xcm::v3::Location + = xcm::v3::Location::new(1, [ + xcm::v3::Junction::Parachain(PENPAL_ID), + xcm::v3::Junction::PalletInstance(ASSETS_PALLET_ID), + xcm::v3::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into()), + ] + ); + pub PenpalSiblingSovereignAccount: AccountId = Sibling::from(PENPAL_ID).into_account_truncating(); +} + /// Helper function to generate a crypto pair from seed pub fn get_from_seed(seed: &str) -> ::Public { TPublic::Pair::from_string(&format!("//{}", seed), None) diff --git a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs index d3bb3238a3b4308ca010cf1fa5fe8cd65ab2b74c..6f6bbe41e01bd208ee6d40a9f3b4ba8f98f7975b 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs @@ -16,16 +16,26 @@ pub use paste; // Substrate +pub use frame_support::{pallet_prelude::Weight, weights::WeightToFee}; +pub use pallet_assets; pub use pallet_balances; pub use pallet_message_queue; pub use pallet_xcm; // Polkadot -pub use xcm::prelude::{AccountId32, WeightLimit}; +pub use xcm::{ + prelude::{ + AccountId32, All, Asset, AssetId, BuyExecution, DepositAsset, ExpectTransactStatus, + Fungible, Here, Location, MaybeErrorCode, OriginKind, RefundSurplus, Transact, Unlimited, + VersionedXcm, WeightLimit, WithdrawAsset, Xcm, + }, + v3::Location as V3Location, +}; // Cumulus pub use asset_test_utils; pub use cumulus_pallet_xcmp_queue; +pub use parachains_common::AccountId; pub use xcm_emulator::Chain; #[macro_export] @@ -105,7 +115,7 @@ macro_rules! test_parachain_is_trusted_teleporter { let para_receiver_balance_after = <$receiver_para as $crate::macros::Chain>::account_data_of(receiver.clone()).free; let delivery_fees = <$sender_para>::execute_with(|| { - $crate::macros::asset_test_utils::xcm_helpers::transfer_assets_delivery_fees::< + $crate::macros::asset_test_utils::xcm_helpers::teleport_assets_delivery_fees::< <$sender_xcm_config as xcm_executor::Config>::XcmSender, >($assets.clone(), fee_asset_item, weight_limit.clone(), beneficiary, para_destination) }); @@ -120,102 +130,3 @@ macro_rules! test_parachain_is_trusted_teleporter { } }; } - -#[macro_export] -macro_rules! include_penpal_create_foreign_asset_on_asset_hub { - ( $penpal:ident, $asset_hub:ident, $relay_ed:expr, $weight_to_fee:expr) => { - $crate::impls::paste::paste! { - pub fn penpal_create_foreign_asset_on_asset_hub( - asset_id_on_penpal: u32, - foreign_asset_at_asset_hub: v3::Location, - ah_as_seen_by_penpal: Location, - is_sufficient: bool, - asset_owner: AccountId, - prefund_amount: u128, - ) { - use frame_support::weights::WeightToFee; - let ah_check_account = $asset_hub::execute_with(|| { - <$asset_hub as [<$asset_hub Pallet>]>::PolkadotXcm::check_account() - }); - let penpal_check_account = - $penpal::execute_with(|| <$penpal as [<$penpal Pallet>]>::PolkadotXcm::check_account()); - let penpal_as_seen_by_ah = $asset_hub::sibling_location_of($penpal::para_id()); - - // prefund SA of Penpal on AssetHub with enough native tokens to pay for creating - // new foreign asset, also prefund CheckingAccount with ED, because teleported asset - // itself might not be sufficient and CheckingAccount cannot be created otherwise - let sov_penpal_on_ah = $asset_hub::sovereign_account_id_of(penpal_as_seen_by_ah.clone()); - $asset_hub::fund_accounts(vec![ - (sov_penpal_on_ah.clone().into(), $relay_ed * 100_000_000_000), - (ah_check_account.clone().into(), $relay_ed * 1000), - ]); - - // prefund SA of AssetHub on Penpal with native asset - let sov_ah_on_penpal = $penpal::sovereign_account_id_of(ah_as_seen_by_penpal.clone()); - $penpal::fund_accounts(vec![ - (sov_ah_on_penpal.into(), $relay_ed * 1_000_000_000), - (penpal_check_account.clone().into(), $relay_ed * 1000), - ]); - - // Force create asset on $penpal and prefund [<$penpal Sender>] - $penpal::force_create_and_mint_asset( - asset_id_on_penpal, - ASSET_MIN_BALANCE, - is_sufficient, - asset_owner, - None, - prefund_amount, - ); - - let require_weight_at_most = Weight::from_parts(1_100_000_000_000, 30_000); - // `OriginKind::Xcm` required by ForeignCreators pallet-assets origin filter - let origin_kind = OriginKind::Xcm; - let call_create_foreign_assets = - <$asset_hub as Chain>::RuntimeCall::ForeignAssets(pallet_assets::Call::< - <$asset_hub as Chain>::Runtime, - pallet_assets::Instance2, - >::create { - id: foreign_asset_at_asset_hub, - min_balance: ASSET_MIN_BALANCE, - admin: sov_penpal_on_ah.into(), - }) - .encode(); - let buy_execution_fee_amount = $weight_to_fee::weight_to_fee( - &Weight::from_parts(10_100_000_000_000, 300_000), - ); - let buy_execution_fee = Asset { - id: AssetId(Location { parents: 1, interior: Here }), - fun: Fungible(buy_execution_fee_amount), - }; - let xcm = VersionedXcm::from(Xcm(vec![ - WithdrawAsset { 0: vec![buy_execution_fee.clone()].into() }, - BuyExecution { fees: buy_execution_fee.clone(), weight_limit: Unlimited }, - Transact { require_weight_at_most, origin_kind, call: call_create_foreign_assets.into() }, - ExpectTransactStatus(MaybeErrorCode::Success), - RefundSurplus, - DepositAsset { assets: All.into(), beneficiary: penpal_as_seen_by_ah }, - ])); - // Send XCM message from penpal => asset_hub - let sudo_penpal_origin = <$penpal as Chain>::RuntimeOrigin::root(); - $penpal::execute_with(|| { - assert_ok!(<$penpal as [<$penpal Pallet>]>::PolkadotXcm::send( - sudo_penpal_origin.clone(), - bx!(ah_as_seen_by_penpal.into()), - bx!(xcm), - )); - type RuntimeEvent = <$penpal as Chain>::RuntimeEvent; - assert_expected_events!( - $penpal, - vec![ - RuntimeEvent::PolkadotXcm(pallet_xcm::Event::Sent { .. }) => {}, - ] - ); - }); - $asset_hub::execute_with(|| { - type ForeignAssets = <$asset_hub as [<$asset_hub Pallet>]>::ForeignAssets; - assert!(ForeignAssets::asset_exists(foreign_asset_at_asset_hub)); - }); - } - } - }; -} diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml index 0a397c2617b4bbb4c9a509b8ef32dbcadbc33228..9b519da4b1d82d5536e21f001cced0cec37197a8 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml @@ -30,9 +30,10 @@ rococo-runtime = { path = "../../../../../../../polkadot/runtime/rococo" } # Cumulus asset-test-utils = { path = "../../../../../runtimes/assets/test-utils" } -parachains-common = { path = "../../../../../../parachains/common" } +parachains-common = { path = "../../../../../common" } cumulus-pallet-parachain-system = { path = "../../../../../../pallets/parachain-system", default-features = false } testnet-parachains-constants = { path = "../../../../../runtimes/constants", features = ["rococo"] } asset-hub-rococo-runtime = { path = "../../../../../runtimes/assets/asset-hub-rococo" } +penpal-runtime = { path = "../../../../../runtimes/testing/penpal" } emulated-integration-tests-common = { path = "../../../common", default-features = false } rococo-system-emulated-network = { path = "../../../networks/rococo-system" } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs index 1cc25cb54a14bb757ae844899f2c64e55b8c80bb..a5a4914e21d826ea6c70af4ae31a0d4dee43ef64 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs @@ -13,59 +13,75 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub use codec::Encode; +#[cfg(test)] +mod imports { + pub use codec::Encode; + + // Substrate + pub use frame_support::{ + assert_err, assert_ok, + pallet_prelude::Weight, + sp_runtime::{DispatchError, DispatchResult, ModuleError}, + traits::fungibles::Inspect, + }; -// Substrate -pub use frame_support::{ - assert_err, assert_ok, - pallet_prelude::Weight, - sp_runtime::{AccountId32, DispatchError, DispatchResult}, - traits::fungibles::Inspect, -}; + // Polkadot + pub use xcm::{ + prelude::{AccountId32 as AccountId32Junction, *}, + v3, + }; -// Polkadot -pub use xcm::{ - prelude::{AccountId32 as AccountId32Junction, *}, - v3::{self, Error, NetworkId::Rococo as RococoId}, -}; + // Cumulus + pub use asset_test_utils::xcm_helpers; + pub use emulated_integration_tests_common::{ + test_parachain_is_trusted_teleporter, + xcm_emulator::{ + assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test, + TestArgs, TestContext, TestExt, + }, + xcm_helpers::{non_fee_asset, xcm_transact_paid_execution}, + ASSETS_PALLET_ID, RESERVABLE_ASSET_ID, XCM_V3, + }; + pub use parachains_common::Balance; + pub use rococo_system_emulated_network::{ + asset_hub_rococo_emulated_chain::{ + genesis::{AssetHubRococoAssetOwner, ED as ASSET_HUB_ROCOCO_ED}, + AssetHubRococoParaPallet as AssetHubRococoPallet, + }, + penpal_emulated_chain::{ + PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner, + PenpalBParaPallet as PenpalBPallet, ED as PENPAL_ED, + }, + rococo_emulated_chain::{genesis::ED as ROCOCO_ED, RococoRelayPallet as RococoPallet}, + AssetHubRococoPara as AssetHubRococo, AssetHubRococoParaReceiver as AssetHubRococoReceiver, + AssetHubRococoParaSender as AssetHubRococoSender, BridgeHubRococoPara as BridgeHubRococo, + BridgeHubRococoParaReceiver as BridgeHubRococoReceiver, PenpalAPara as PenpalA, + PenpalAParaReceiver as PenpalAReceiver, PenpalAParaSender as PenpalASender, + PenpalBPara as PenpalB, PenpalBParaReceiver as PenpalBReceiver, RococoRelay as Rococo, + RococoRelayReceiver as RococoReceiver, RococoRelaySender as RococoSender, + }; -// Cumulus -pub use asset_test_utils::xcm_helpers; -pub use emulated_integration_tests_common::{ - test_parachain_is_trusted_teleporter, - xcm_emulator::{ - assert_expected_events, bx, helpers::weight_within_threshold, Chain, Parachain as Para, - RelayChain as Relay, Test, TestArgs, TestContext, TestExt, - }, - xcm_helpers::{xcm_transact_paid_execution, xcm_transact_unpaid_execution}, - PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, XCM_V3, -}; -pub use parachains_common::{AccountId, Balance}; -pub use rococo_system_emulated_network::{ - asset_hub_rococo_emulated_chain::{ - genesis::ED as ASSET_HUB_ROCOCO_ED, AssetHubRococoParaPallet as AssetHubRococoPallet, - }, - penpal_emulated_chain::PenpalAParaPallet as PenpalAPallet, - rococo_emulated_chain::{genesis::ED as ROCOCO_ED, RococoRelayPallet as RococoPallet}, - AssetHubRococoPara as AssetHubRococo, AssetHubRococoParaReceiver as AssetHubRococoReceiver, - AssetHubRococoParaSender as AssetHubRococoSender, BridgeHubRococoPara as BridgeHubRococo, - BridgeHubRococoParaReceiver as BridgeHubRococoReceiver, PenpalAPara as PenpalA, - PenpalAParaReceiver as PenpalAReceiver, PenpalAParaSender as PenpalASender, - PenpalBPara as PenpalB, PenpalBParaReceiver as PenpalBReceiver, RococoRelay as Rococo, - RococoRelayReceiver as RococoReceiver, RococoRelaySender as RococoSender, -}; + // Runtimes + pub use asset_hub_rococo_runtime::xcm_config::{ + TokenLocation as RelayLocation, XcmConfig as AssetHubRococoXcmConfig, + }; + pub use penpal_runtime::xcm_config::{ + LocalReservableFromAssetHub as PenpalLocalReservableFromAssetHub, + LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub, + }; + pub use rococo_runtime::xcm_config::XcmConfig as RococoXcmConfig; -pub const ASSET_ID: u32 = 1; -pub const ASSET_MIN_BALANCE: u128 = 1000; -// `Assets` pallet index -pub const ASSETS_PALLET_ID: u8 = 50; + pub const ASSET_ID: u32 = 3; + pub const ASSET_MIN_BALANCE: u128 = 1000; -pub type RelayToSystemParaTest = Test; -pub type RelayToParaTest = Test; -pub type SystemParaToRelayTest = Test; -pub type SystemParaToParaTest = Test; -pub type ParaToSystemParaTest = Test; -pub type ParaToParaTest = Test; + pub type RelayToSystemParaTest = Test; + pub type RelayToParaTest = Test; + pub type ParaToRelayTest = Test; + pub type SystemParaToRelayTest = Test; + pub type SystemParaToParaTest = Test; + pub type ParaToSystemParaTest = Test; + pub type ParaToParaThroughRelayTest = Test; +} #[cfg(test)] mod tests; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs index 21bed234304e2fae3eb285253e528d02e5441854..b3841af0e6c38372b8fb621fac468b25bdec63a1 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs @@ -18,11 +18,3 @@ mod send; mod set_xcm_versions; mod swap; mod teleport; - -use crate::*; -emulated_integration_tests_common::include_penpal_create_foreign_asset_on_asset_hub!( - PenpalA, - AssetHubRococo, - ROCOCO_ED, - testnet_parachains_constants::rococo::fee::WeightToFee -); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs index d2c3a323256c193a3af6230c0a0b33ae3f54198f..a0738839087a51e87df3187ac6f06d2889cce64e 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs @@ -13,14 +13,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; -use asset_hub_rococo_runtime::xcm_config::XcmConfig as AssetHubRococoXcmConfig; -use rococo_runtime::xcm_config::XcmConfig as RococoXcmConfig; -use rococo_system_emulated_network::penpal_emulated_chain::XcmConfig as PenpalRococoXcmConfig; +use crate::imports::*; fn relay_to_para_sender_assertions(t: RelayToParaTest) { type RuntimeEvent = ::RuntimeEvent; + Rococo::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(864_610_000, 8_799))); + assert_expected_events!( Rococo, vec![ @@ -38,12 +37,32 @@ fn relay_to_para_sender_assertions(t: RelayToParaTest) { ); } +fn para_to_relay_sender_assertions(t: ParaToRelayTest) { + type RuntimeEvent = ::RuntimeEvent; + PenpalA::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(864_610_000, 8_799))); + assert_expected_events!( + PenpalA, + vec![ + // Amount to reserve transfer is transferred to Parachain's Sovereign account + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, balance, .. } + ) => { + asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"), + owner: *owner == t.sender.account_id, + balance: *balance == t.args.amount, + }, + ] + ); +} + fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) { type RuntimeEvent = ::RuntimeEvent; + AssetHubRococo::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts( 864_610_000, 8_799, ))); + assert_expected_events!( AssetHubRococo, vec![ @@ -57,19 +76,29 @@ fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) { ), amount: *amount == t.args.amount, }, + // Transport fees are paid + RuntimeEvent::PolkadotXcm( + pallet_xcm::Event::FeesPaid { .. } + ) => {}, ] ); + AssetHubRococo::assert_xcm_pallet_sent(); } -fn para_receiver_assertions(_: Test) { +fn system_para_to_para_receiver_assertions(t: SystemParaToParaTest) { type RuntimeEvent = ::RuntimeEvent; + let system_para_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + + PenpalA::assert_xcmp_queue_success(None); + assert_expected_events!( PenpalA, vec![ - RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, - RuntimeEvent::MessageQueue( - pallet_message_queue::Event::Processed { success: true, .. } - ) => {}, + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { + asset_id: *asset_id == system_para_native_asset_location, + owner: *owner == t.receiver.account_id, + }, ] ); } @@ -81,12 +110,42 @@ fn para_to_system_para_sender_assertions(t: ParaToSystemParaTest) { PenpalA, vec![ // Amount to reserve transfer is transferred to Parachain's Sovereign account + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, balance, .. } + ) => { + asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"), + owner: *owner == t.sender.account_id, + balance: *balance == t.args.amount, + }, + ] + ); +} + +fn para_to_relay_receiver_assertions(t: ParaToRelayTest) { + type RuntimeEvent = ::RuntimeEvent; + let sov_penpal_on_relay = + Rococo::sovereign_account_id_of(Rococo::child_location_of(PenpalA::para_id())); + + Rococo::assert_ump_queue_processed( + true, + Some(PenpalA::para_id()), + Some(Weight::from_parts(306305000, 7_186)), + ); + + assert_expected_events!( + Rococo, + vec![ + // Amount to reserve transfer is withdrawn from Parachain's Sovereign account RuntimeEvent::Balances( pallet_balances::Event::Burned { who, amount } ) => { - who: *who == t.sender.account_id, + who: *who == sov_penpal_on_relay.clone().into(), amount: *amount == t.args.amount, }, + RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, + RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, ] ); } @@ -96,6 +155,9 @@ fn para_to_system_para_receiver_assertions(t: ParaToSystemParaTest) { let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of( AssetHubRococo::sibling_location_of(PenpalA::para_id()), ); + + AssetHubRococo::assert_xcmp_queue_success(None); + assert_expected_events!( AssetHubRococo, vec![ @@ -127,24 +189,124 @@ fn system_para_to_para_assets_sender_assertions(t: SystemParaToParaTest) { RuntimeEvent::Assets( pallet_assets::Event::Transferred { asset_id, from, to, amount } ) => { - asset_id: *asset_id == ASSET_ID, + asset_id: *asset_id == RESERVABLE_ASSET_ID, from: *from == t.sender.account_id, to: *to == AssetHubRococo::sovereign_account_id_of( t.args.dest.clone() ), amount: *amount == t.args.amount, }, + // Native asset to pay for fees is transferred to Parachain's Sovereign account + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { + who: *who == AssetHubRococo::sovereign_account_id_of( + t.args.dest.clone() + ), + }, + // Transport fees are paid + RuntimeEvent::PolkadotXcm( + pallet_xcm::Event::FeesPaid { .. } + ) => {}, ] ); } -fn system_para_to_para_assets_receiver_assertions(_: Test) { +fn para_to_system_para_assets_sender_assertions(t: ParaToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; + let system_para_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let reservable_asset_location = + v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); + PenpalA::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(864_610_000, 8799))); assert_expected_events!( PenpalA, vec![ - RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, - RuntimeEvent::Assets(pallet_assets::Event::Issued { .. }) => {}, + // Fees amount to reserve transfer is burned from Parachains's sender account + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, .. } + ) => { + asset_id: *asset_id == system_para_native_asset_location, + owner: *owner == t.sender.account_id, + }, + // Amount to reserve transfer is burned from Parachains's sender account + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, balance } + ) => { + asset_id: *asset_id == reservable_asset_location, + owner: *owner == t.sender.account_id, + balance: *balance == t.args.amount, + }, + // Transport fees are paid + RuntimeEvent::PolkadotXcm( + pallet_xcm::Event::FeesPaid { .. } + ) => {}, + ] + ); +} + +fn system_para_to_para_assets_receiver_assertions(t: SystemParaToParaTest) { + type RuntimeEvent = ::RuntimeEvent; + let system_para_asset_location = + v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); + PenpalA::assert_xcmp_queue_success(None); + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { + asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"), + owner: *owner == t.receiver.account_id, + }, + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, amount }) => { + asset_id: *asset_id == system_para_asset_location, + owner: *owner == t.receiver.account_id, + amount: *amount == t.args.amount, + }, + ] + ); +} + +fn para_to_system_para_assets_receiver_assertions(t: ParaToSystemParaTest) { + type RuntimeEvent = ::RuntimeEvent; + let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of( + AssetHubRococo::sibling_location_of(PenpalA::para_id()), + ); + AssetHubRococo::assert_xcmp_queue_success(None); + assert_expected_events!( + AssetHubRococo, + vec![ + // Amount to reserve transfer is burned from Parachain's Sovereign account + RuntimeEvent::Assets(pallet_assets::Event::Burned { asset_id, owner, balance }) => { + asset_id: *asset_id == RESERVABLE_ASSET_ID, + owner: *owner == sov_penpal_on_ahr, + balance: *balance == t.args.amount, + }, + // Fee amount is burned from Parachain's Sovereign account + RuntimeEvent::Balances(pallet_balances::Event::Burned { who, .. }) => { + who: *who == sov_penpal_on_ahr, + }, + // Amount to reserve transfer is issued for beneficiary + RuntimeEvent::Assets(pallet_assets::Event::Issued { asset_id, owner, amount }) => { + asset_id: *asset_id == RESERVABLE_ASSET_ID, + owner: *owner == t.receiver.account_id, + amount: *amount == t.args.amount, + }, + // Remaining fee amount is minted for for beneficiary + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { + who: *who == t.receiver.account_id, + }, + ] + ); +} + +fn relay_to_para_assets_receiver_assertions(t: RelayToParaTest) { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { + asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"), + owner: *owner == t.receiver.account_id, + }, RuntimeEvent::MessageQueue( pallet_message_queue::Event::Processed { success: true, .. } ) => {}, @@ -152,33 +314,38 @@ fn system_para_to_para_assets_receiver_assertions(_: Test) { ); } -fn para_to_para_sender_assertions(t: ParaToParaTest) { +fn para_to_para_through_relay_sender_assertions(t: ParaToParaThroughRelayTest) { type RuntimeEvent = ::RuntimeEvent; + + let relay_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + PenpalA::assert_xcm_pallet_attempted_complete(None); + // XCM sent to relay reserve + PenpalA::assert_parachain_system_ump_sent(); + assert_expected_events!( PenpalA, vec![ // Amount to reserve transfer is transferred to Parachain's Sovereign account - RuntimeEvent::Balances( - pallet_balances::Event::Burned { who, amount } + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, balance }, ) => { - who: *who == t.sender.account_id, - amount: *amount == t.args.amount, + asset_id: *asset_id == relay_asset_location, + owner: *owner == t.sender.account_id, + balance: *balance == t.args.amount, }, - // XCM sent to relay reserve - RuntimeEvent::ParachainSystem( - cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. } - ) => {}, ] ); } -fn para_to_para_relay_hop_assertions(t: ParaToParaTest) { +fn para_to_para_relay_hop_assertions(t: ParaToParaThroughRelayTest) { type RuntimeEvent = ::RuntimeEvent; let sov_penpal_a_on_rococo = Rococo::sovereign_account_id_of(Rococo::child_location_of(PenpalA::para_id())); let sov_penpal_b_on_rococo = Rococo::sovereign_account_id_of(Rococo::child_location_of(PenpalB::para_id())); + assert_expected_events!( Rococo, vec![ @@ -202,15 +369,20 @@ fn para_to_para_relay_hop_assertions(t: ParaToParaTest) { ); } -fn para_to_para_receiver_assertions(_: ParaToParaTest) { +fn para_to_para_through_relay_receiver_assertions(t: ParaToParaThroughRelayTest) { type RuntimeEvent = ::RuntimeEvent; + let relay_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + + PenpalB::assert_xcmp_queue_success(None); + assert_expected_events!( PenpalB, vec![ - RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, - RuntimeEvent::MessageQueue( - pallet_message_queue::Event::Processed { success: true, .. } - ) => {}, + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { + asset_id: *asset_id == relay_asset_location, + owner: *owner == t.receiver.account_id, + }, ] ); } @@ -226,6 +398,17 @@ fn relay_to_para_reserve_transfer_assets(t: RelayToParaTest) -> DispatchResult { ) } +fn para_to_relay_reserve_transfer_assets(t: ParaToRelayTest) -> DispatchResult { + ::PolkadotXcm::limited_reserve_transfer_assets( + t.signed_origin, + bx!(t.args.dest.into()), + bx!(t.args.beneficiary.into()), + bx!(t.args.assets.into()), + t.args.fee_asset_item, + t.args.weight_limit, + ) +} + fn system_para_to_para_reserve_transfer_assets(t: SystemParaToParaTest) -> DispatchResult { ::PolkadotXcm::limited_reserve_transfer_assets( t.signed_origin, @@ -248,7 +431,9 @@ fn para_to_system_para_reserve_transfer_assets(t: ParaToSystemParaTest) -> Dispa ) } -fn para_to_para_limited_reserve_transfer_assets(t: ParaToParaTest) -> DispatchResult { +fn para_to_para_through_relay_limited_reserve_transfer_assets( + t: ParaToParaThroughRelayTest, +) -> DispatchResult { ::PolkadotXcm::limited_reserve_transfer_assets( t.signed_origin, bx!(t.args.dest.into()), @@ -262,6 +447,7 @@ fn para_to_para_limited_reserve_transfer_assets(t: ParaToParaTest) -> DispatchRe /// Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't work #[test] fn reserve_transfer_native_asset_from_relay_to_system_para_fails() { + // Init values for Relay Chain let signed_origin = ::RuntimeOrigin::signed(RococoSender::get().into()); let destination = Rococo::child_location_of(AssetHubRococo::para_id()); let beneficiary: Location = @@ -328,135 +514,260 @@ fn reserve_transfer_native_asset_from_system_para_to_relay_fails() { }); } +// ========================================================================= +// ========= Reserve Transfers - Native Asset - Relay<>Parachain =========== +// ========================================================================= /// Reserve Transfers of native asset from Relay to Parachain should work #[test] fn reserve_transfer_native_asset_from_relay_to_para() { // Init values for Relay let destination = Rococo::child_location_of(PenpalA::para_id()); - let beneficiary_id = PenpalAReceiver::get(); + let sender = RococoSender::get(); let amount_to_send: Balance = ROCOCO_ED * 1000; + // Init values fot Parachain + let relay_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let receiver = PenpalAReceiver::get(); + + // Init Test let test_args = TestContext { - sender: RococoSender::get(), - receiver: PenpalAReceiver::get(), - args: TestArgs::new_relay(destination, beneficiary_id, amount_to_send), + sender, + receiver: receiver.clone(), + args: TestArgs::new_relay(destination.clone(), receiver.clone(), amount_to_send), }; - let mut test = RelayToParaTest::new(test_args); + // Query initial balances let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; + let receiver_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.into(), &receiver) + }); + // Set assertions and dispatchables test.set_assertion::(relay_to_para_sender_assertions); - test.set_assertion::(para_receiver_assertions); + test.set_assertion::(relay_to_para_assets_receiver_assertions); test.set_dispatchable::(relay_to_para_reserve_transfer_assets); test.assert(); - let delivery_fees = Rococo::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + // Query final balances + let sender_balance_after = test.sender.balance; + let receiver_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.into(), &receiver) }); - let sender_balance_after = test.sender.balance; + // Sender's balance is reduced by amount sent plus delivery fees + assert!(sender_balance_after < sender_balance_before - amount_to_send); + // Receiver's asset balance is increased + assert!(receiver_assets_after > receiver_assets_before); + // Receiver's asset balance increased by `amount_to_send - delivery_fees - bought_execution`; + // `delivery_fees` might be paid from transfer or JIT, also `bought_execution` is unknown but + // should be non-zero + assert!(receiver_assets_after < receiver_assets_before + amount_to_send); +} + +/// Reserve Transfers of native asset from Parachain to Relay should work +#[test] +fn reserve_transfer_native_asset_from_para_to_relay() { + // Init values for Parachain + let destination = PenpalA::parent_location(); + let sender = PenpalASender::get(); + let amount_to_send: Balance = ROCOCO_ED * 1000; + let assets: Assets = (Parent, amount_to_send).into(); + let asset_owner = PenpalAssetOwner::get(); + let relay_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + + // fund Parachain's sender account + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(asset_owner), + relay_native_asset_location, + sender.clone(), + amount_to_send * 2, + ); + + // Init values for Relay + let receiver = RococoReceiver::get(); + let penpal_location_as_seen_by_relay = Rococo::child_location_of(PenpalA::para_id()); + let sov_penpal_on_relay = Rococo::sovereign_account_id_of(penpal_location_as_seen_by_relay); + + // fund Parachain's SA on Relay with the native tokens held in reserve + Rococo::fund_accounts(vec![(sov_penpal_on_relay.into(), amount_to_send * 2)]); + + // Init Test + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + destination.clone(), + receiver, + amount_to_send, + assets.clone(), + None, + 0, + ), + }; + let mut test = ParaToRelayTest::new(test_args); + + // Query initial balances + let sender_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.into(), &sender) + }); + let receiver_balance_before = test.receiver.balance; + + // Set assertions and dispatchables + test.set_assertion::(para_to_relay_sender_assertions); + test.set_assertion::(para_to_relay_receiver_assertions); + test.set_dispatchable::(para_to_relay_reserve_transfer_assets); + test.assert(); + + // Query final balances + let sender_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.into(), &sender) + }); let receiver_balance_after = test.receiver.balance; - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased + // Sender's balance is reduced by amount sent plus delivery fees + assert!(sender_assets_after < sender_assets_before - amount_to_send); + // Receiver's asset balance is increased assert!(receiver_balance_after > receiver_balance_before); - // Receiver's balance increased by `amount_to_send - delivery_fees - bought_execution`; + // Receiver's asset balance increased by `amount_to_send - delivery_fees - bought_execution`; // `delivery_fees` might be paid from transfer or JIT, also `bought_execution` is unknown but // should be non-zero assert!(receiver_balance_after < receiver_balance_before + amount_to_send); } +// ========================================================================= +// ======= Reserve Transfers - Native Asset - AssetHub<>Parachain ========== +// ========================================================================= /// Reserve Transfers of native asset from System Parachain to Parachain should work #[test] fn reserve_transfer_native_asset_from_system_para_to_para() { // Init values for System Parachain let destination = AssetHubRococo::sibling_location_of(PenpalA::para_id()); - let beneficiary_id = PenpalAReceiver::get(); - let amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 1000; - let assets = (Parent, amount_to_send).into(); + let sender = AssetHubRococoSender::get(); + let amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 10000; + let assets: Assets = (Parent, amount_to_send).into(); + + // Init values for Parachain + let system_para_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let receiver = PenpalAReceiver::get(); + // Init Test let test_args = TestContext { - sender: AssetHubRococoSender::get(), - receiver: PenpalAReceiver::get(), - args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0), + sender, + receiver: receiver.clone(), + args: TestArgs::new_para( + destination.clone(), + receiver.clone(), + amount_to_send, + assets.clone(), + None, + 0, + ), }; - let mut test = SystemParaToParaTest::new(test_args); + // Query initial balances let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; + let receiver_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_native_asset_location.into(), &receiver) + }); + // Set assertions and dispatchables test.set_assertion::(system_para_to_para_sender_assertions); - test.set_assertion::(para_receiver_assertions); + test.set_assertion::(system_para_to_para_receiver_assertions); test.set_dispatchable::(system_para_to_para_reserve_transfer_assets); test.assert(); + // Query final balances let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - let delivery_fees = AssetHubRococo::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + let receiver_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_native_asset_location, &receiver) }); - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); - // Receiver's balance increased by `amount_to_send - delivery_fees - bought_execution`; + // Sender's balance is reduced by amount sent plus delivery fees + assert!(sender_balance_after < sender_balance_before - amount_to_send); + // Receiver's assets is increased + assert!(receiver_assets_after > receiver_assets_before); + // Receiver's assets increased by `amount_to_send - delivery_fees - bought_execution`; // `delivery_fees` might be paid from transfer or JIT, also `bought_execution` is unknown but // should be non-zero - assert!(receiver_balance_after < receiver_balance_before + amount_to_send); + assert!(receiver_assets_after < receiver_assets_before + amount_to_send); } /// Reserve Transfers of native asset from Parachain to System Parachain should work #[test] fn reserve_transfer_native_asset_from_para_to_system_para() { - // Init values for Penpal Parachain + // Init values for Parachain let destination = PenpalA::sibling_location_of(AssetHubRococo::para_id()); - let beneficiary_id = AssetHubRococoReceiver::get(); - let amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 1000; - let assets = (Parent, amount_to_send).into(); + let sender = PenpalASender::get(); + let amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 10000; + let assets: Assets = (Parent, amount_to_send).into(); + let system_para_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let asset_owner = PenpalAssetOwner::get(); + + // fund Parachain's sender account + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(asset_owner), + system_para_native_asset_location, + sender.clone(), + amount_to_send * 2, + ); + // Init values for System Parachain + let receiver = AssetHubRococoReceiver::get(); + let penpal_location_as_seen_by_ahr = AssetHubRococo::sibling_location_of(PenpalA::para_id()); + let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location_as_seen_by_ahr); + + // fund Parachain's SA on System Parachain with the native tokens held in reserve + AssetHubRococo::fund_accounts(vec![(sov_penpal_on_ahr.into(), amount_to_send * 2)]); + + // Init Test let test_args = TestContext { - sender: PenpalASender::get(), - receiver: AssetHubRococoReceiver::get(), - args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0), + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + destination.clone(), + receiver.clone(), + amount_to_send, + assets.clone(), + None, + 0, + ), }; - let mut test = ParaToSystemParaTest::new(test_args); - let sender_balance_before = test.sender.balance; + // Query initial balances + let sender_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_native_asset_location, &sender) + }); let receiver_balance_before = test.receiver.balance; - let penpal_location_as_seen_by_ahr = AssetHubRococo::sibling_location_of(PenpalA::para_id()); - let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location_as_seen_by_ahr); - - // fund the Penpal's SA on AHR with the native tokens held in reserve - AssetHubRococo::fund_accounts(vec![(sov_penpal_on_ahr.into(), amount_to_send * 2)]); - + // Set assertions and dispatchables test.set_assertion::(para_to_system_para_sender_assertions); test.set_assertion::(para_to_system_para_receiver_assertions); test.set_dispatchable::(para_to_system_para_reserve_transfer_assets); test.assert(); - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - let delivery_fees = PenpalA::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + // Query final balances + let sender_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_native_asset_location, &sender) }); + let receiver_balance_after = test.receiver.balance; - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); + // Sender's balance is reduced by amount sent plus delivery fees + assert!(sender_assets_after < sender_assets_before - amount_to_send); // Receiver's balance is increased assert!(receiver_balance_after > receiver_balance_before); // Receiver's balance increased by `amount_to_send - delivery_fees - bought_execution`; @@ -465,36 +776,27 @@ fn reserve_transfer_native_asset_from_para_to_system_para() { assert!(receiver_balance_after < receiver_balance_before + amount_to_send); } +// ========================================================================= +// ======= Reserve Transfers - Non-system Asset - AssetHub<>Parachain ====== +// ========================================================================= /// Reserve Transfers of a local asset and native asset from System Parachain to Parachain should /// work #[test] fn reserve_transfer_assets_from_system_para_to_para() { - // Force create asset on AssetHubRococo and PenpalA from Relay Chain - AssetHubRococo::force_create_and_mint_asset( - ASSET_ID, - ASSET_MIN_BALANCE, - false, - AssetHubRococoSender::get(), - Some(Weight::from_parts(1_019_445_000, 200_000)), - ASSET_MIN_BALANCE * 1_000_000, - ); - PenpalA::force_create_and_mint_asset( - ASSET_ID, - ASSET_MIN_BALANCE, - false, - PenpalASender::get(), - None, - 0, - ); - // Init values for System Parachain let destination = AssetHubRococo::sibling_location_of(PenpalA::para_id()); - let beneficiary_id = PenpalAReceiver::get(); - let fee_amount_to_send = ASSET_HUB_ROCOCO_ED * 1000; - let asset_amount_to_send = ASSET_MIN_BALANCE * 1000; + let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(destination.clone()); + let sender = AssetHubRococoSender::get(); + let fee_amount_to_send = ASSET_HUB_ROCOCO_ED * 10000; + let asset_amount_to_send = PENPAL_ED * 10000; + let asset_owner = AssetHubRococoAssetOwner::get(); + let asset_owner_signer = ::RuntimeOrigin::signed(asset_owner.clone()); let assets: Assets = vec![ (Parent, fee_amount_to_send).into(), - ([PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())], asset_amount_to_send) + ( + [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(RESERVABLE_ASSET_ID.into())], + asset_amount_to_send, + ) .into(), ] .into(); @@ -503,49 +805,211 @@ fn reserve_transfer_assets_from_system_para_to_para() { .iter() .position(|r| r == &(Parent, fee_amount_to_send).into()) .unwrap() as u32; + AssetHubRococo::mint_asset( + asset_owner_signer, + RESERVABLE_ASSET_ID, + asset_owner, + asset_amount_to_send * 2, + ); + + // Create SA-of-Penpal-on-AHR with ED. + AssetHubRococo::fund_accounts(vec![(sov_penpal_on_ahr.into(), ASSET_HUB_ROCOCO_ED)]); + + // Init values for Parachain + let receiver = PenpalAReceiver::get(); + let system_para_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let system_para_foreign_asset_location = + v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); + // Init Test let para_test_args = TestContext { - sender: AssetHubRococoSender::get(), - receiver: PenpalAReceiver::get(), + sender: sender.clone(), + receiver: receiver.clone(), args: TestArgs::new_para( destination, - beneficiary_id, + receiver.clone(), asset_amount_to_send, assets, None, fee_asset_index, ), }; - let mut test = SystemParaToParaTest::new(para_test_args); - // Create SA-of-Penpal-on-AHR with ED. - let penpal_location = AssetHubRococo::sibling_location_of(PenpalA::para_id()); - let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location); - AssetHubRococo::fund_accounts(vec![(sov_penpal_on_ahr.into(), ROCOCO_ED)]); - + // Query initial balances let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - let sender_assets_before = AssetHubRococo::execute_with(|| { type Assets = ::Assets; - >::balance(ASSET_ID, &AssetHubRococoSender::get()) + >::balance(RESERVABLE_ASSET_ID, &sender) }); - let receiver_assets_before = PenpalA::execute_with(|| { - type Assets = ::Assets; - >::balance(ASSET_ID, &PenpalAReceiver::get()) + let receiver_system_native_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_native_asset_location, &receiver) + }); + let receiver_foreign_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_foreign_asset_location, &receiver) }); + // Set assertions and dispatchables test.set_assertion::(system_para_to_para_assets_sender_assertions); test.set_assertion::(system_para_to_para_assets_receiver_assertions); test.set_dispatchable::(system_para_to_para_reserve_transfer_assets); test.assert(); + // Query final balances let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - + let sender_assets_after = AssetHubRococo::execute_with(|| { + type Assets = ::Assets; + >::balance(RESERVABLE_ASSET_ID, &sender) + }); + let receiver_system_native_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_native_asset_location, &receiver) + }); + let receiver_foreign_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_foreign_asset_location, &receiver) + }); // Sender's balance is reduced assert!(sender_balance_after < sender_balance_before); + // Receiver's foreign asset balance is increased + assert!(receiver_foreign_assets_after > receiver_foreign_assets_before); + // Receiver's system asset balance increased by `amount_to_send - delivery_fees - + // bought_execution`; `delivery_fees` might be paid from transfer or JIT, also + // `bought_execution` is unknown but should be non-zero + assert!( + receiver_system_native_assets_after < + receiver_system_native_assets_before + fee_amount_to_send + ); + + // Sender's asset balance is reduced by exact amount + assert_eq!(sender_assets_before - asset_amount_to_send, sender_assets_after); + // Receiver's foreign asset balance is increased by exact amount + assert_eq!( + receiver_foreign_assets_after, + receiver_foreign_assets_before + asset_amount_to_send + ); +} + +/// Reserve Transfers of a foreign asset and native asset from Parachain to System Para should +/// work +#[test] +fn reserve_transfer_assets_from_para_to_system_para() { + // Init values for Parachain + let destination = PenpalA::sibling_location_of(AssetHubRococo::para_id()); + let sender = PenpalASender::get(); + let fee_amount_to_send = ASSET_HUB_ROCOCO_ED * 10000; + let asset_amount_to_send = ASSET_HUB_ROCOCO_ED * 10000; + let penpal_asset_owner = PenpalAssetOwner::get(); + let penpal_asset_owner_signer = ::RuntimeOrigin::signed(penpal_asset_owner); + let asset_location_on_penpal = + v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); + let asset_location_on_penpal_latest: Location = asset_location_on_penpal.try_into().unwrap(); + let system_asset_location_on_penpal = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let assets: Assets = vec![ + (Parent, fee_amount_to_send).into(), + (asset_location_on_penpal_latest, asset_amount_to_send).into(), + ] + .into(); + let fee_asset_index = assets + .inner() + .iter() + .position(|r| r == &(Parent, fee_amount_to_send).into()) + .unwrap() as u32; + // Fund Parachain's sender account with some foreign assets + PenpalA::mint_foreign_asset( + penpal_asset_owner_signer.clone(), + asset_location_on_penpal, + sender.clone(), + asset_amount_to_send * 2, + ); + // Fund Parachain's sender account with some system assets + PenpalA::mint_foreign_asset( + penpal_asset_owner_signer, + system_asset_location_on_penpal, + sender.clone(), + fee_amount_to_send * 2, + ); + + // Init values for System Parachain + let receiver = AssetHubRococoReceiver::get(); + let penpal_location_as_seen_by_ahr = AssetHubRococo::sibling_location_of(PenpalA::para_id()); + let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location_as_seen_by_ahr); + let system_para_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let system_para_foreign_asset_location = + v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); + let ah_asset_owner = AssetHubRococoAssetOwner::get(); + let ah_asset_owner_signer = ::RuntimeOrigin::signed(ah_asset_owner); + + // Fund SA-of-Penpal-on-AHR to be able to pay for the fees. + AssetHubRococo::fund_accounts(vec![( + sov_penpal_on_ahr.clone().into(), + ASSET_HUB_ROCOCO_ED * 10000000, + )]); + // Fund SA-of-Penpal-on-AHR to be able to pay for the sent amount. + AssetHubRococo::mint_asset( + ah_asset_owner_signer, + RESERVABLE_ASSET_ID, + sov_penpal_on_ahr, + asset_amount_to_send * 2, + ); + + // Init Test + let para_test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + destination, + receiver.clone(), + asset_amount_to_send, + assets, + None, + fee_asset_index, + ), + }; + let mut test = ParaToSystemParaTest::new(para_test_args); + + // Query initial balances + let sender_system_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_native_asset_location, &sender) + }); + let sender_foreign_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_foreign_asset_location, &sender) + }); + let receiver_balance_before = test.receiver.balance; + let receiver_assets_before = AssetHubRococo::execute_with(|| { + type Assets = ::Assets; + >::balance(RESERVABLE_ASSET_ID, &receiver) + }); + + // Set assertions and dispatchables + test.set_assertion::(para_to_system_para_assets_sender_assertions); + test.set_assertion::(para_to_system_para_assets_receiver_assertions); + test.set_dispatchable::(para_to_system_para_reserve_transfer_assets); + test.assert(); + + // Query final balances + let sender_system_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_native_asset_location, &sender) + }); + let sender_foreign_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_foreign_asset_location, &sender) + }); + let receiver_balance_after = test.receiver.balance; + let receiver_assets_after = AssetHubRococo::execute_with(|| { + type Assets = ::Assets; + >::balance(RESERVABLE_ASSET_ID, &receiver) + }); + // Sender's system asset balance is reduced + assert!(sender_system_assets_after < sender_system_assets_before); // Receiver's balance is increased assert!(receiver_balance_after > receiver_balance_before); // Receiver's balance increased by `amount_to_send - delivery_fees - bought_execution`; @@ -553,65 +1017,81 @@ fn reserve_transfer_assets_from_system_para_to_para() { // should be non-zero assert!(receiver_balance_after < receiver_balance_before + fee_amount_to_send); - let sender_assets_after = AssetHubRococo::execute_with(|| { - type Assets = ::Assets; - >::balance(ASSET_ID, &AssetHubRococoSender::get()) - }); - let receiver_assets_after = PenpalA::execute_with(|| { - type Assets = ::Assets; - >::balance(ASSET_ID, &PenpalAReceiver::get()) - }); - - // Sender's balance is reduced by exact amount - assert_eq!(sender_assets_before - asset_amount_to_send, sender_assets_after); - // Receiver's balance is increased by exact amount + // Sender's asset balance is reduced by exact amount + assert_eq!(sender_foreign_assets_before - asset_amount_to_send, sender_foreign_assets_after); + // Receiver's foreign asset balance is increased by exact amount assert_eq!(receiver_assets_after, receiver_assets_before + asset_amount_to_send); } +// ========================================================================= +// ===== Reserve Transfers - Native Asset - Parachain<>Relay<>Parachain ==== +// ========================================================================= /// Reserve Transfers of native asset from Parachain to Parachain (through Relay reserve) should /// work #[test] -fn reserve_transfer_native_asset_from_para_to_para() { - // Init values for Penpal Parachain +fn reserve_transfer_native_asset_from_para_to_para_trough_relay() { + // Init values for Parachain Origin let destination = PenpalA::sibling_location_of(PenpalB::para_id()); - let beneficiary_id = PenpalBReceiver::get(); - let amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 10000; + let sender = PenpalASender::get(); + let amount_to_send: Balance = ROCOCO_ED * 10000; + let asset_owner = PenpalAssetOwner::get(); let assets = (Parent, amount_to_send).into(); + let relay_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let sender_as_seen_by_relay = Rococo::child_location_of(PenpalA::para_id()); + let sov_of_sender_on_relay = Rococo::sovereign_account_id_of(sender_as_seen_by_relay); - let test_args = TestContext { - sender: PenpalASender::get(), - receiver: PenpalBReceiver::get(), - args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0), - }; + // fund Parachain's sender account + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(asset_owner), + relay_native_asset_location, + sender.clone(), + amount_to_send * 2, + ); - let mut test = ParaToParaTest::new(test_args); + // fund the Parachain Origin's SA on Relay Chain with the native tokens held in reserve + Rococo::fund_accounts(vec![(sov_of_sender_on_relay.into(), amount_to_send * 2)]); - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; + // Init values for Parachain Destination + let receiver = PenpalBReceiver::get(); - let sender_as_seen_by_relay = Rococo::child_location_of(PenpalA::para_id()); - let sov_of_sender_on_relay = Rococo::sovereign_account_id_of(sender_as_seen_by_relay); + // Init Test + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para(destination, receiver.clone(), amount_to_send, assets, None, 0), + }; + let mut test = ParaToParaThroughRelayTest::new(test_args); - // fund the PenpalA's SA on Rococo with the native tokens held in reserve - Rococo::fund_accounts(vec![(sov_of_sender_on_relay.into(), amount_to_send * 2)]); + // Query initial balances + let sender_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location, &sender) + }); + let receiver_assets_before = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location, &receiver) + }); - test.set_assertion::(para_to_para_sender_assertions); + // Set assertions and dispatchables + test.set_assertion::(para_to_para_through_relay_sender_assertions); test.set_assertion::(para_to_para_relay_hop_assertions); - test.set_assertion::(para_to_para_receiver_assertions); - test.set_dispatchable::(para_to_para_limited_reserve_transfer_assets); + test.set_assertion::(para_to_para_through_relay_receiver_assertions); + test.set_dispatchable::(para_to_para_through_relay_limited_reserve_transfer_assets); test.assert(); - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - let delivery_fees = PenpalA::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + // Query final balances + let sender_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location, &sender) + }); + let receiver_assets_after = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location, &receiver) }); - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); + // Sender's balance is reduced by amount sent plus delivery fees + assert!(sender_assets_after < sender_assets_before - amount_to_send); // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); + assert!(receiver_assets_after > receiver_assets_before); } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs index 3c9e76a34e36a9e136e1df1421e0e152af71107b..1d120f1dc4c7ed4f923514e0692f3a4beb48103a 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; +use crate::imports::*; /// Relay Chain should be able to execute `Transact` instructions in System Parachain /// when `OriginKind::Superuser`. @@ -28,8 +28,95 @@ fn send_transact_as_superuser_from_relay_to_system_para_works() { ) } -/// Parachain should be able to send XCM paying its fee with sufficient asset -/// in the System Parachain +/// We tests two things here: +/// - Parachain should be able to send XCM paying its fee with system asset in the System Parachain +/// - Parachain should be able to create a new Foreign Asset in the System Parachain +#[test] +fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { + let para_sovereign_account = AssetHubRococo::sovereign_account_id_of( + AssetHubRococo::sibling_location_of(PenpalA::para_id()), + ); + let asset_location_on_penpal = v3::Location::new( + 0, + [ + v3::Junction::PalletInstance(ASSETS_PALLET_ID), + v3::Junction::GeneralIndex(ASSET_ID.into()), + ], + ); + let foreign_asset_at_asset_hub = + v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())]) + .appended_with(asset_location_on_penpal) + .unwrap(); + + // Encoded `create_asset` call to be executed in AssetHub + let call = AssetHubRococo::create_foreign_asset_call( + foreign_asset_at_asset_hub, + ASSET_MIN_BALANCE, + para_sovereign_account.clone(), + ); + + let origin_kind = OriginKind::Xcm; + let fee_amount = ASSET_HUB_ROCOCO_ED * 1000000; + let system_asset = (Parent, fee_amount).into(); + + let root_origin = ::RuntimeOrigin::root(); + let system_para_destination = PenpalA::sibling_location_of(AssetHubRococo::para_id()).into(); + let xcm = xcm_transact_paid_execution( + call, + origin_kind, + system_asset, + para_sovereign_account.clone(), + ); + + // SA-of-Penpal-on-AHR needs to have balance to pay for fees and asset creation deposit + AssetHubRococo::fund_accounts(vec![( + para_sovereign_account.clone().into(), + ASSET_HUB_ROCOCO_ED * 10000000000, + )]); + + PenpalA::execute_with(|| { + assert_ok!(::PolkadotXcm::send_blob( + root_origin, + bx!(system_para_destination), + xcm.encode().try_into().unwrap(), + )); + + PenpalA::assert_xcm_pallet_sent(); + }); + + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + AssetHubRococo::assert_xcmp_queue_success(Some(Weight::from_parts( + 15_594_564_000, + 562_893, + ))); + + assert_expected_events!( + AssetHubRococo, + vec![ + // Burned the fee + RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { + who: *who == para_sovereign_account, + amount: *amount == fee_amount, + }, + // Foreign Asset created + RuntimeEvent::ForeignAssets(pallet_assets::Event::Created { asset_id, creator, owner }) => { + asset_id: *asset_id == foreign_asset_at_asset_hub, + creator: *creator == para_sovereign_account.clone(), + owner: *owner == para_sovereign_account, + }, + ] + ); + + type ForeignAssets = ::ForeignAssets; + assert!(ForeignAssets::asset_exists(foreign_asset_at_asset_hub)); + }); +} + +/// We tests two things here: +/// - Parachain should be able to send XCM paying its fee with system assets in the System Parachain +/// - Parachain should be able to create a new Asset in the System Parachain #[test] fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { let para_sovereign_account = AssetHubRococo::sovereign_account_id_of( @@ -46,34 +133,36 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { ASSET_MIN_BALANCE * 1000000000, ); - // We just need a call that can pass the `SafeCallFilter` - // Call values are not relevant - let call = AssetHubRococo::force_create_asset_call( - ASSET_ID, - para_sovereign_account.clone(), - true, + // Just a different `asset_id`` that does not exist yet + let new_asset_id = ASSET_ID + 1; + + // Encoded `create_asset` call to be executed in AssetHub + let call = AssetHubRococo::create_asset_call( + new_asset_id, ASSET_MIN_BALANCE, + para_sovereign_account.clone(), ); let origin_kind = OriginKind::SovereignAccount; let fee_amount = ASSET_MIN_BALANCE * 1000000; - let native_asset = + let asset = ([PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())], fee_amount).into(); let root_origin = ::RuntimeOrigin::root(); let system_para_destination = PenpalA::sibling_location_of(AssetHubRococo::para_id()).into(); - let xcm = xcm_transact_paid_execution( - call, - origin_kind, - native_asset, - para_sovereign_account.clone(), - ); + let xcm = xcm_transact_paid_execution(call, origin_kind, asset, para_sovereign_account.clone()); + + // SA-of-Penpal-on-AHR needs to have balance to pay for asset creation deposit + AssetHubRococo::fund_accounts(vec![( + para_sovereign_account.clone().into(), + ASSET_HUB_ROCOCO_ED * 10000000000, + )]); PenpalA::execute_with(|| { - assert_ok!(::PolkadotXcm::send( + assert_ok!(::PolkadotXcm::send_blob( root_origin, bx!(system_para_destination), - bx!(xcm), + xcm.encode().try_into().unwrap(), )); PenpalA::assert_xcm_pallet_sent(); @@ -90,13 +179,17 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { assert_expected_events!( AssetHubRococo, vec![ + // Burned the fee RuntimeEvent::Assets(pallet_assets::Event::Burned { asset_id, owner, balance }) => { asset_id: *asset_id == ASSET_ID, owner: *owner == para_sovereign_account, balance: *balance == fee_amount, }, - RuntimeEvent::Assets(pallet_assets::Event::Issued { asset_id, .. }) => { - asset_id: *asset_id == ASSET_ID, + // Asset created + RuntimeEvent::Assets(pallet_assets::Event::Created { asset_id, creator, owner }) => { + asset_id: *asset_id == new_asset_id, + creator: *creator == para_sovereign_account.clone(), + owner: *owner == para_sovereign_account, }, ] ); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/set_xcm_versions.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/set_xcm_versions.rs index 7d630d368051d76bc0915277c44b733d894970ba..5662a78ab67f555c4a0a331fda79cbf984526cc1 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/set_xcm_versions.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/set_xcm_versions.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; +use crate::imports::*; #[test] fn relay_sets_system_para_xcm_supported_version() { diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs index c6a10b252901c03636caf2a6c8291781c28a8b09..e13300b7c11426416f543c2ee026702277b695b1 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs @@ -13,9 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; -use rococo_system_emulated_network::penpal_emulated_chain::LocalTeleportableToAssetHubV3 as PenpalLocalTeleportableToAssetHubV3; -use sp_runtime::ModuleError; +use crate::imports::*; #[test] fn swap_locally_on_chain_using_local_assets() { @@ -114,49 +112,39 @@ fn swap_locally_on_chain_using_local_assets() { #[test] fn swap_locally_on_chain_using_foreign_assets() { - let asset_native = Box::new(asset_hub_rococo_runtime::xcm_config::TokenLocationV3::get()); - let ah_as_seen_by_penpal = PenpalA::sibling_location_of(AssetHubRococo::para_id()); - let asset_location_on_penpal = PenpalLocalTeleportableToAssetHubV3::get(); - let asset_id_on_penpal = match asset_location_on_penpal.last() { - Some(v3::Junction::GeneralIndex(id)) => *id as u32, - _ => unreachable!(), - }; - let asset_owner_on_penpal = PenpalASender::get(); + let asset_native = + Box::new(v3::Location::try_from(RelayLocation::get()).expect("conversion works")); + let asset_location_on_penpal = + v3::Location::try_from(PenpalLocalTeleportableToAssetHub::get()).expect("conversion works"); let foreign_asset_at_asset_hub_rococo = v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())]) .appended_with(asset_location_on_penpal) .unwrap(); - // 1. Create asset on penpal and, 2. Create foreign asset on asset_hub_rococo - super::penpal_create_foreign_asset_on_asset_hub( - asset_id_on_penpal, - foreign_asset_at_asset_hub_rococo, - ah_as_seen_by_penpal, - true, - asset_owner_on_penpal, - ASSET_MIN_BALANCE * 1_000_000, - ); - let penpal_as_seen_by_ah = AssetHubRococo::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_as_seen_by_ah); AssetHubRococo::fund_accounts(vec![ - (AssetHubRococoSender::get().into(), 5_000_000 * ROCOCO_ED), /* An account to swap dot - * for something else. */ + // An account to swap dot for something else. + (AssetHubRococoSender::get().into(), 5_000_000 * ASSET_HUB_ROCOCO_ED), + // Penpal's sovereign account in AH should have some balance + (sov_penpal_on_ahr.clone().into(), 100_000_000 * ASSET_HUB_ROCOCO_ED), ]); AssetHubRococo::execute_with(|| { - // 3: Mint foreign asset on asset_hub_rococo: + // 0: No need to create foreign asset as it exists in genesis. + // + // 1: Mint foreign asset on asset_hub_rococo: // // (While it might be nice to use batch, // currently that's disabled due to safe call filters.) type RuntimeEvent = ::RuntimeEvent; - // 3. Mint foreign asset (in reality this should be a teleport or some such) + // 1. Mint foreign asset (in reality this should be a teleport or some such) assert_ok!(::ForeignAssets::mint( ::RuntimeOrigin::signed(sov_penpal_on_ahr.clone().into()), foreign_asset_at_asset_hub_rococo, sov_penpal_on_ahr.clone().into(), - 3_000_000_000_000, + ASSET_HUB_ROCOCO_ED * 3_000_000_000_000, )); assert_expected_events!( @@ -166,7 +154,7 @@ fn swap_locally_on_chain_using_foreign_assets() { ] ); - // 4. Create pool: + // 2. Create pool: assert_ok!(::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubRococoSender::get()), asset_native.clone(), @@ -180,7 +168,7 @@ fn swap_locally_on_chain_using_foreign_assets() { ] ); - // 5. Add liquidity: + // 3. Add liquidity: assert_ok!(::AssetConversion::add_liquidity( ::RuntimeOrigin::signed(sov_penpal_on_ahr.clone()), asset_native.clone(), @@ -201,15 +189,15 @@ fn swap_locally_on_chain_using_foreign_assets() { ] ); - // 6. Swap! + // 4. Swap! let path = vec![asset_native.clone(), Box::new(foreign_asset_at_asset_hub_rococo)]; assert_ok!( ::AssetConversion::swap_exact_tokens_for_tokens( ::RuntimeOrigin::signed(AssetHubRococoSender::get()), path, - 100000, - 1000, + 100000 * ASSET_HUB_ROCOCO_ED, + 1000 * ASSET_HUB_ROCOCO_ED, AssetHubRococoSender::get().into(), true ) @@ -219,18 +207,18 @@ fn swap_locally_on_chain_using_foreign_assets() { AssetHubRococo, vec![ RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::SwapExecuted { amount_in, amount_out, .. },) => { - amount_in: *amount_in == 100000, - amount_out: *amount_out == 199399, + amount_in: *amount_in == 333333300000, + amount_out: *amount_out == 498874118173, }, ] ); - // 7. Remove liquidity + // 5. Remove liquidity assert_ok!(::AssetConversion::remove_liquidity( ::RuntimeOrigin::signed(sov_penpal_on_ahr.clone()), asset_native.clone(), Box::new(foreign_asset_at_asset_hub_rococo), - 1414213562273 - 2_000_000_000, // all but the 2 EDs can't be retrieved. + 1414213562273 - ASSET_HUB_ROCOCO_ED * 2, // all but the 2 EDs can't be retrieved. 0, 0, sov_penpal_on_ahr.clone().into(), @@ -382,10 +370,10 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() { penpal.clone(), ); - assert_ok!(::PolkadotXcm::send( + assert_ok!(::PolkadotXcm::send_blob( penpal_root, bx!(asset_hub_location), - bx!(xcm), + xcm.encode().try_into().unwrap(), )); PenpalA::assert_xcm_pallet_sent(); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs index dfb5061b55f053b51e0ceedee3f8df0edbb7b8ba..4432999aa95556abba4ab780af11638dd0e6678f 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs @@ -13,11 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; -use asset_hub_rococo_runtime::xcm_config::XcmConfig as AssetHubRococoXcmConfig; -use emulated_integration_tests_common::xcm_helpers::non_fee_asset; -use rococo_runtime::xcm_config::XcmConfig as RococoXcmConfig; -use rococo_system_emulated_network::penpal_emulated_chain::LocalTeleportableToAssetHubV3 as PenpalLocalTeleportableToAssetHubV3; +use crate::imports::*; fn relay_origin_assertions(t: RelayToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; @@ -114,18 +110,21 @@ fn para_dest_assertions(t: RelayToSystemParaTest) { fn penpal_to_ah_foreign_assets_sender_assertions(t: ParaToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; - PenpalA::assert_xcm_pallet_attempted_complete(None); + let system_para_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); let expected_asset_id = t.args.asset_id.unwrap(); let (_, expected_asset_amount) = non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap(); + + PenpalA::assert_xcm_pallet_attempted_complete(None); assert_expected_events!( PenpalA, vec![ - RuntimeEvent::Balances( - pallet_balances::Event::Burned { who, amount } + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, .. } ) => { - who: *who == t.sender.account_id, - amount: *amount == t.args.amount, + asset_id: *asset_id == system_para_native_asset_location, + owner: *owner == t.sender.account_id, }, RuntimeEvent::Assets(pallet_assets::Event::Burned { asset_id, owner, balance }) => { asset_id: *asset_id == expected_asset_id, @@ -144,6 +143,9 @@ fn penpal_to_ah_foreign_assets_receiver_assertions(t: ParaToSystemParaTest) { let (expected_foreign_asset_id, expected_foreign_asset_amount) = non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap(); let expected_foreign_asset_id_v3: v3::Location = expected_foreign_asset_id.try_into().unwrap(); + + AssetHubRococo::assert_xcmp_queue_success(None); + assert_expected_events!( AssetHubRococo, vec![ @@ -163,9 +165,6 @@ fn penpal_to_ah_foreign_assets_receiver_assertions(t: ParaToSystemParaTest) { amount: *amount == expected_foreign_asset_amount, }, RuntimeEvent::Balances(pallet_balances::Event::Deposit { .. }) => {}, - RuntimeEvent::MessageQueue( - pallet_message_queue::Event::Processed { success: true, .. } - ) => {}, ] ); } @@ -205,6 +204,11 @@ fn ah_to_penpal_foreign_assets_receiver_assertions(t: SystemParaToParaTest) { let (_, expected_asset_amount) = non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap(); let checking_account = ::PolkadotXcm::check_account(); + let system_para_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + + PenpalA::assert_xcmp_queue_success(None); + assert_expected_events!( PenpalA, vec![ @@ -221,12 +225,11 @@ fn ah_to_penpal_foreign_assets_receiver_assertions(t: SystemParaToParaTest) { amount: *amount == expected_asset_amount, }, // native asset for fee is deposited to receiver - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, amount }) => { + asset_id: *asset_id == system_para_native_asset_location, + owner: *owner == t.receiver.account_id, + amount: *amount == expected_asset_amount, }, - RuntimeEvent::MessageQueue( - pallet_message_queue::Event::Processed { success: true, .. } - ) => {}, ] ); } @@ -319,7 +322,7 @@ fn limited_teleport_native_assets_from_relay_to_system_para_works() { test.assert(); let delivery_fees = Rococo::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< + xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) }); @@ -366,7 +369,7 @@ fn limited_teleport_native_assets_back_from_system_para_to_relay_works() { let receiver_balance_after = test.receiver.balance; let delivery_fees = AssetHubRococo::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< + xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) }); @@ -407,7 +410,7 @@ fn limited_teleport_native_assets_from_system_para_to_relay_fails() { let receiver_balance_after = test.receiver.balance; let delivery_fees = AssetHubRococo::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< + xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) }); @@ -442,7 +445,7 @@ fn teleport_native_assets_from_relay_to_system_para_works() { test.assert(); let delivery_fees = Rococo::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< + xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) }); @@ -489,7 +492,7 @@ fn teleport_native_assets_back_from_system_para_to_relay_works() { let receiver_balance_after = test.receiver.balance; let delivery_fees = AssetHubRococo::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< + xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) }); @@ -527,7 +530,7 @@ fn teleport_native_assets_from_system_para_to_relay_fails() { test.assert(); let delivery_fees = AssetHubRococo::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< + xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) }); @@ -558,30 +561,21 @@ fn teleport_to_other_system_parachains_works() { /// (using native reserve-based transfer for fees) #[test] fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { - let ah_as_seen_by_penpal = PenpalA::sibling_location_of(AssetHubRococo::para_id()); - let asset_location_on_penpal = PenpalLocalTeleportableToAssetHubV3::get(); + // Init values for Parachain + let fee_amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 10000; + let asset_location_on_penpal = + v3::Location::try_from(PenpalLocalTeleportableToAssetHub::get()).expect("conversion works"); let asset_id_on_penpal = match asset_location_on_penpal.last() { Some(v3::Junction::GeneralIndex(id)) => *id as u32, _ => unreachable!(), }; - let asset_owner_on_penpal = PenpalASender::get(); - let foreign_asset_at_asset_hub_rococo = - v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())]) - .appended_with(asset_location_on_penpal) - .unwrap(); - super::penpal_create_foreign_asset_on_asset_hub( - asset_id_on_penpal, - foreign_asset_at_asset_hub_rococo, - ah_as_seen_by_penpal.clone(), - false, - asset_owner_on_penpal, - ASSET_MIN_BALANCE * 1_000_000, - ); - let penpal_to_ah_beneficiary_id = AssetHubRococoReceiver::get(); - - let fee_amount_to_send = ASSET_HUB_ROCOCO_ED * 10_000; - let asset_amount_to_send = ASSET_MIN_BALANCE * 1000; - + let asset_amount_to_send = ASSET_HUB_ROCOCO_ED * 1000; + let asset_owner = PenpalAssetOwner::get(); + let system_para_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let sender = PenpalASender::get(); + let penpal_check_account = ::PolkadotXcm::check_account(); + let ah_as_seen_by_penpal = PenpalA::sibling_location_of(AssetHubRococo::para_id()); let asset_location_on_penpal_latest: Location = asset_location_on_penpal.try_into().unwrap(); let penpal_assets: Assets = vec![ (Parent, fee_amount_to_send).into(), @@ -594,6 +588,38 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { .position(|r| r == &(Parent, fee_amount_to_send).into()) .unwrap() as u32; + // fund Parachain's sender account + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(asset_owner.clone()), + system_para_native_asset_location, + sender.clone(), + fee_amount_to_send * 2, + ); + // No need to create the asset (only mint) as it exists in genesis. + PenpalA::mint_asset( + ::RuntimeOrigin::signed(asset_owner.clone()), + asset_id_on_penpal, + sender.clone(), + asset_amount_to_send, + ); + // fund Parachain's check account to be able to teleport + PenpalA::fund_accounts(vec![(penpal_check_account.clone().into(), ASSET_HUB_ROCOCO_ED * 1000)]); + + // prefund SA of Penpal on AssetHub with enough native tokens to pay for fees + let penpal_as_seen_by_ah = AssetHubRococo::sibling_location_of(PenpalA::para_id()); + let sov_penpal_on_ah = AssetHubRococo::sovereign_account_id_of(penpal_as_seen_by_ah); + AssetHubRococo::fund_accounts(vec![( + sov_penpal_on_ah.clone().into(), + ASSET_HUB_ROCOCO_ED * 100_000_000_000, + )]); + + // Init values for System Parachain + let foreign_asset_at_asset_hub_rococo = + v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())]) + .appended_with(asset_location_on_penpal) + .unwrap(); + let penpal_to_ah_beneficiary_id = AssetHubRococoReceiver::get(); + // Penpal to AH test args let penpal_to_ah_test_args = TestContext { sender: PenpalASender::get(), @@ -608,8 +634,14 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { ), }; let mut penpal_to_ah = ParaToSystemParaTest::new(penpal_to_ah_test_args); + let penpal_sender_balance_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance( + system_para_native_asset_location, + &PenpalASender::get(), + ) + }); - let penpal_sender_balance_before = penpal_to_ah.sender.balance; let ah_receiver_balance_before = penpal_to_ah.receiver.balance; let penpal_sender_assets_before = PenpalA::execute_with(|| { @@ -629,7 +661,14 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { penpal_to_ah.set_dispatchable::(para_to_system_para_transfer_assets); penpal_to_ah.assert(); - let penpal_sender_balance_after = penpal_to_ah.sender.balance; + let penpal_sender_balance_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance( + system_para_native_asset_location, + &PenpalASender::get(), + ) + }); + let ah_receiver_balance_after = penpal_to_ah.receiver.balance; let penpal_sender_assets_after = PenpalA::execute_with(|| { @@ -704,7 +743,13 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { let mut ah_to_penpal = SystemParaToParaTest::new(ah_to_penpal_test_args); let ah_sender_balance_before = ah_to_penpal.sender.balance; - let penpal_receiver_balance_before = ah_to_penpal.receiver.balance; + let penpal_receiver_balance_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance( + system_para_native_asset_location, + &PenpalAReceiver::get(), + ) + }); let ah_sender_assets_before = AssetHubRococo::execute_with(|| { type ForeignAssets = ::ForeignAssets; @@ -724,7 +769,13 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { ah_to_penpal.assert(); let ah_sender_balance_after = ah_to_penpal.sender.balance; - let penpal_receiver_balance_after = ah_to_penpal.receiver.balance; + let penpal_receiver_balance_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance( + system_para_native_asset_location, + &PenpalAReceiver::get(), + ) + }); let ah_sender_assets_after = AssetHubRococo::execute_with(|| { type ForeignAssets = ::ForeignAssets; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml index 0c920730d0fe695e84c77560ae9755ec65d19d46..3121ed028eb9184cd4e2359e78f17ce6a650c22d 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml @@ -31,11 +31,12 @@ pallet-xcm = { path = "../../../../../../../polkadot/xcm/pallet-xcm", default-fe westend-runtime = { path = "../../../../../../../polkadot/runtime/westend" } # Cumulus -parachains-common = { path = "../../../../../../parachains/common" } +parachains-common = { path = "../../../../../common" } testnet-parachains-constants = { path = "../../../../../runtimes/constants", features = ["westend"] } +penpal-runtime = { path = "../../../../../runtimes/testing/penpal" } asset-hub-westend-runtime = { path = "../../../../../runtimes/assets/asset-hub-westend" } asset-test-utils = { path = "../../../../../runtimes/assets/test-utils" } -cumulus-pallet-xcmp-queue = { default-features = false, path = "../../../../../../pallets/xcmp-queue" } -cumulus-pallet-parachain-system = { default-features = false, path = "../../../../../../pallets/parachain-system" } +cumulus-pallet-xcmp-queue = { path = "../../../../../../pallets/xcmp-queue", default-features = false } +cumulus-pallet-parachain-system = { path = "../../../../../../pallets/parachain-system", default-features = false } emulated-integration-tests-common = { path = "../../../common", default-features = false } westend-system-emulated-network = { path = "../../../networks/westend-system" } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs index 409369df7bb0633a7137c025cb6d28f6f41f8f49..c9f5fe0647e12ba0121261505e27ff56c3f82f96 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs @@ -13,67 +13,79 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub use codec::Encode; +#[cfg(test)] +mod imports { + pub use codec::Encode; + + // Substrate + pub use frame_support::{ + assert_err, assert_ok, + pallet_prelude::Weight, + sp_runtime::{DispatchError, DispatchResult, ModuleError}, + traits::fungibles::Inspect, + }; -// Substrate -pub use frame_support::{ - assert_err, assert_ok, - instances::Instance2, - pallet_prelude::Weight, - sp_runtime::{AccountId32, DispatchError, DispatchResult, ModuleError}, - traits::fungibles::Inspect, - BoundedVec, -}; + // Polkadot + pub use xcm::{ + prelude::{AccountId32 as AccountId32Junction, *}, + v3, + }; -// Polkadot -pub use xcm::{ - prelude::{AccountId32 as AccountId32Junction, *}, - v3::{self, Error, NetworkId::Westend as WestendId}, -}; + // Cumulus + pub use asset_test_utils::xcm_helpers; + pub use emulated_integration_tests_common::{ + test_parachain_is_trusted_teleporter, + xcm_emulator::{ + assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test, + TestArgs, TestContext, TestExt, + }, + xcm_helpers::{non_fee_asset, xcm_transact_paid_execution}, + ASSETS_PALLET_ID, RESERVABLE_ASSET_ID, XCM_V3, + }; + pub use parachains_common::{AccountId, Balance}; + pub use westend_system_emulated_network::{ + asset_hub_westend_emulated_chain::{ + genesis::{AssetHubWestendAssetOwner, ED as ASSET_HUB_WESTEND_ED}, + AssetHubWestendParaPallet as AssetHubWestendPallet, + }, + collectives_westend_emulated_chain::CollectivesWestendParaPallet as CollectivesWestendPallet, + penpal_emulated_chain::{ + PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner, + PenpalBParaPallet as PenpalBPallet, + }, + westend_emulated_chain::{genesis::ED as WESTEND_ED, WestendRelayPallet as WestendPallet}, + AssetHubWestendPara as AssetHubWestend, + AssetHubWestendParaReceiver as AssetHubWestendReceiver, + AssetHubWestendParaSender as AssetHubWestendSender, + BridgeHubWestendPara as BridgeHubWestend, + BridgeHubWestendParaReceiver as BridgeHubWestendReceiver, + CollectivesWestendPara as CollectivesWestend, PenpalAPara as PenpalA, + PenpalAParaReceiver as PenpalAReceiver, PenpalAParaSender as PenpalASender, + PenpalBPara as PenpalB, PenpalBParaReceiver as PenpalBReceiver, WestendRelay as Westend, + WestendRelayReceiver as WestendReceiver, WestendRelaySender as WestendSender, + }; -// Cumulus -pub use asset_test_utils::xcm_helpers; -pub use emulated_integration_tests_common::{ - test_parachain_is_trusted_teleporter, - xcm_emulator::{ - assert_expected_events, bx, helpers::weight_within_threshold, Chain, Parachain as Para, - RelayChain as Relay, Test, TestArgs, TestContext, TestExt, - }, - xcm_helpers::{xcm_transact_paid_execution, xcm_transact_unpaid_execution}, - PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, XCM_V3, -}; -pub use parachains_common::{AccountId, Balance}; -pub use westend_system_emulated_network::{ - asset_hub_westend_emulated_chain::{ - genesis::ED as ASSET_HUB_WESTEND_ED, AssetHubWestendParaPallet as AssetHubWestendPallet, - }, - collectives_westend_emulated_chain::{ - genesis::ED as COLLECTIVES_WESTEND_ED, - CollectivesWestendParaPallet as CollectivesWestendPallet, - }, - penpal_emulated_chain::PenpalBParaPallet as PenpalBPallet, - westend_emulated_chain::{genesis::ED as WESTEND_ED, WestendRelayPallet as WestendPallet}, - AssetHubWestendPara as AssetHubWestend, AssetHubWestendParaReceiver as AssetHubWestendReceiver, - AssetHubWestendParaSender as AssetHubWestendSender, BridgeHubWestendPara as BridgeHubWestend, - BridgeHubWestendParaReceiver as BridgeHubWestendReceiver, - CollectivesWestendPara as CollectivesWestend, PenpalAPara as PenpalA, - PenpalAParaReceiver as PenpalAReceiver, PenpalBPara as PenpalB, - PenpalBParaReceiver as PenpalBReceiver, PenpalBParaSender as PenpalBSender, - WestendRelay as Westend, WestendRelayReceiver as WestendReceiver, - WestendRelaySender as WestendSender, -}; + // Runtimes + pub use asset_hub_westend_runtime::xcm_config::{ + WestendLocation as RelayLocation, XcmConfig as AssetHubWestendXcmConfig, + }; + pub use penpal_runtime::xcm_config::{ + LocalReservableFromAssetHub as PenpalLocalReservableFromAssetHub, + LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub, + }; + pub use westend_runtime::xcm_config::XcmConfig as WestendXcmConfig; -pub const ASSET_ID: u32 = 1; -pub const ASSET_MIN_BALANCE: u128 = 1000; -// `Assets` pallet index -pub const ASSETS_PALLET_ID: u8 = 50; + pub const ASSET_ID: u32 = 3; + pub const ASSET_MIN_BALANCE: u128 = 1000; -pub type RelayToSystemParaTest = Test; -pub type RelayToParaTest = Test; -pub type SystemParaToRelayTest = Test; -pub type SystemParaToParaTest = Test; -pub type ParaToSystemParaTest = Test; -pub type ParaToParaTest = Test; + pub type RelayToSystemParaTest = Test; + pub type RelayToParaTest = Test; + pub type ParaToRelayTest = Test; + pub type SystemParaToRelayTest = Test; + pub type SystemParaToParaTest = Test; + pub type ParaToSystemParaTest = Test; + pub type ParaToParaThroughRelayTest = Test; +} #[cfg(test)] mod tests; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs index 11e1e1762dbb4a62eccc5c92a60a89ee917716e5..2d02e90f47fb834326a7268394671f5ef03f84e7 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; +use crate::imports::*; use emulated_integration_tests_common::accounts::{ALICE, BOB}; use frame_support::traits::fungibles::{Create, Inspect, Mutate}; use polkadot_runtime_common::impls::VersionedLocatableAsset; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs index a56cde8f2a2cb2263cd20c5fe4255459c94a084a..3cd7c9c46d69edd738f067724485824ef51d3259 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs @@ -20,11 +20,3 @@ mod set_xcm_versions; mod swap; mod teleport; mod treasury; - -use crate::*; -emulated_integration_tests_common::include_penpal_create_foreign_asset_on_asset_hub!( - PenpalB, - AssetHubWestend, - WESTEND_ED, - testnet_parachains_constants::westend::fee::WeightToFee -); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs index a29cd10ba8338c9ca560bad29b18fba3a94b4c4d..a26dfef8e8e702ee3f22870116adc03ee8ed1ca2 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs @@ -13,10 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; -use asset_hub_westend_runtime::xcm_config::XcmConfig as AssetHubWestendXcmConfig; -use westend_runtime::xcm_config::XcmConfig as WestendXcmConfig; -use westend_system_emulated_network::penpal_emulated_chain::XcmConfig as PenpalWestendXcmConfig; +use crate::imports::*; fn relay_to_para_sender_assertions(t: RelayToParaTest) { type RuntimeEvent = ::RuntimeEvent; @@ -40,12 +37,30 @@ fn relay_to_para_sender_assertions(t: RelayToParaTest) { ); } +fn para_to_relay_sender_assertions(t: ParaToRelayTest) { + type RuntimeEvent = ::RuntimeEvent; + PenpalA::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(864_610_000, 8_799))); + assert_expected_events!( + PenpalA, + vec![ + // Amount to reserve transfer is transferred to Parachain's Sovereign account + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, balance, .. } + ) => { + asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"), + owner: *owner == t.sender.account_id, + balance: *balance == t.args.amount, + }, + ] + ); +} + fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) { type RuntimeEvent = ::RuntimeEvent; AssetHubWestend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts( - 676_119_000, - 6196, + 864_610_000, + 8_799, ))); assert_expected_events!( @@ -61,57 +76,96 @@ fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) { ), amount: *amount == t.args.amount, }, + // Transport fees are paid + RuntimeEvent::PolkadotXcm( + pallet_xcm::Event::FeesPaid { .. } + ) => {}, ] ); + AssetHubWestend::assert_xcm_pallet_sent(); } -fn para_receiver_assertions(_: Test) { - type RuntimeEvent = ::RuntimeEvent; +fn system_para_to_para_receiver_assertions(t: SystemParaToParaTest) { + type RuntimeEvent = ::RuntimeEvent; + let system_para_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + + PenpalA::assert_xcmp_queue_success(None); + assert_expected_events!( - PenpalB, + PenpalA, vec![ - RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, - RuntimeEvent::MessageQueue( - pallet_message_queue::Event::Processed { success: true, .. } - ) => {}, + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { + asset_id: *asset_id == system_para_native_asset_location, + owner: *owner == t.receiver.account_id, + }, ] ); } fn para_to_system_para_sender_assertions(t: ParaToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; + type RuntimeEvent = ::RuntimeEvent; + PenpalA::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(864_610_000, 8_799))); + assert_expected_events!( + PenpalA, + vec![ + // Amount to reserve transfer is transferred to Parachain's Sovereign account + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, balance, .. } + ) => { + asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"), + owner: *owner == t.sender.account_id, + balance: *balance == t.args.amount, + }, + ] + ); +} - PenpalB::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(864_610_000, 8_799))); +fn para_to_relay_receiver_assertions(t: ParaToRelayTest) { + type RuntimeEvent = ::RuntimeEvent; + let sov_penpal_on_relay = + Westend::sovereign_account_id_of(Westend::child_location_of(PenpalA::para_id())); + + Westend::assert_ump_queue_processed( + true, + Some(PenpalA::para_id()), + Some(Weight::from_parts(306305000, 7_186)), + ); assert_expected_events!( - PenpalB, + Westend, vec![ - // Amount to reserve transfer is transferred to Parachain's Sovereign account + // Amount to reserve transfer is withdrawn from Parachain's Sovereign account RuntimeEvent::Balances( pallet_balances::Event::Burned { who, amount } ) => { - who: *who == t.sender.account_id, + who: *who == sov_penpal_on_relay.clone().into(), amount: *amount == t.args.amount, }, + RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, + RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, ] ); } fn para_to_system_para_receiver_assertions(t: ParaToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; - - let sov_penpal_on_ahw = AssetHubWestend::sovereign_account_id_of( - AssetHubWestend::sibling_location_of(PenpalB::para_id()), + let sov_penpal_on_ahr = AssetHubWestend::sovereign_account_id_of( + AssetHubWestend::sibling_location_of(PenpalA::para_id()), ); + AssetHubWestend::assert_xcmp_queue_success(None); + assert_expected_events!( AssetHubWestend, vec![ - // Amount to reserve transfer is transferred to Parachain's Sovereign account + // Amount to reserve transfer is withdrawn from Parachain's Sovereign account RuntimeEvent::Balances( pallet_balances::Event::Burned { who, amount } ) => { - who: *who == sov_penpal_on_ahw.clone().into(), + who: *who == sov_penpal_on_ahr.clone().into(), amount: *amount == t.args.amount, }, RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, @@ -124,12 +178,10 @@ fn para_to_system_para_receiver_assertions(t: ParaToSystemParaTest) { fn system_para_to_para_assets_sender_assertions(t: SystemParaToParaTest) { type RuntimeEvent = ::RuntimeEvent; - AssetHubWestend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts( - 676_119_000, - 6196, + 864_610_000, + 8799, ))); - assert_expected_events!( AssetHubWestend, vec![ @@ -137,24 +189,124 @@ fn system_para_to_para_assets_sender_assertions(t: SystemParaToParaTest) { RuntimeEvent::Assets( pallet_assets::Event::Transferred { asset_id, from, to, amount } ) => { - asset_id: *asset_id == ASSET_ID, + asset_id: *asset_id == RESERVABLE_ASSET_ID, from: *from == t.sender.account_id, to: *to == AssetHubWestend::sovereign_account_id_of( t.args.dest.clone() ), amount: *amount == t.args.amount, }, + // Native asset to pay for fees is transferred to Parachain's Sovereign account + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { + who: *who == AssetHubWestend::sovereign_account_id_of( + t.args.dest.clone() + ), + }, + // Transport fees are paid + RuntimeEvent::PolkadotXcm( + pallet_xcm::Event::FeesPaid { .. } + ) => {}, ] ); } -fn system_para_to_para_assets_receiver_assertions(_: Test) { - type RuntimeEvent = ::RuntimeEvent; +fn para_to_system_para_assets_sender_assertions(t: ParaToSystemParaTest) { + type RuntimeEvent = ::RuntimeEvent; + let system_para_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let reservable_asset_location = + v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); + PenpalA::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(864_610_000, 8799))); assert_expected_events!( - PenpalB, + PenpalA, vec![ - RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, - RuntimeEvent::Assets(pallet_assets::Event::Issued { .. }) => {}, + // Fees amount to reserve transfer is burned from Parachains's sender account + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, .. } + ) => { + asset_id: *asset_id == system_para_native_asset_location, + owner: *owner == t.sender.account_id, + }, + // Amount to reserve transfer is burned from Parachains's sender account + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, balance } + ) => { + asset_id: *asset_id == reservable_asset_location, + owner: *owner == t.sender.account_id, + balance: *balance == t.args.amount, + }, + // Transport fees are paid + RuntimeEvent::PolkadotXcm( + pallet_xcm::Event::FeesPaid { .. } + ) => {}, + ] + ); +} + +fn system_para_to_para_assets_receiver_assertions(t: SystemParaToParaTest) { + type RuntimeEvent = ::RuntimeEvent; + let system_para_asset_location = + v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); + PenpalA::assert_xcmp_queue_success(None); + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { + asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"), + owner: *owner == t.receiver.account_id, + }, + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, amount }) => { + asset_id: *asset_id == system_para_asset_location, + owner: *owner == t.receiver.account_id, + amount: *amount == t.args.amount, + }, + ] + ); +} + +fn para_to_system_para_assets_receiver_assertions(t: ParaToSystemParaTest) { + type RuntimeEvent = ::RuntimeEvent; + let sov_penpal_on_ahr = AssetHubWestend::sovereign_account_id_of( + AssetHubWestend::sibling_location_of(PenpalA::para_id()), + ); + AssetHubWestend::assert_xcmp_queue_success(None); + assert_expected_events!( + AssetHubWestend, + vec![ + // Amount to reserve transfer is burned from Parachain's Sovereign account + RuntimeEvent::Assets(pallet_assets::Event::Burned { asset_id, owner, balance }) => { + asset_id: *asset_id == RESERVABLE_ASSET_ID, + owner: *owner == sov_penpal_on_ahr, + balance: *balance == t.args.amount, + }, + // Fee amount is burned from Parachain's Sovereign account + RuntimeEvent::Balances(pallet_balances::Event::Burned { who, .. }) => { + who: *who == sov_penpal_on_ahr, + }, + // Amount to reserve transfer is issued for beneficiary + RuntimeEvent::Assets(pallet_assets::Event::Issued { asset_id, owner, amount }) => { + asset_id: *asset_id == RESERVABLE_ASSET_ID, + owner: *owner == t.receiver.account_id, + amount: *amount == t.args.amount, + }, + // Remaining fee amount is minted for for beneficiary + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { + who: *who == t.receiver.account_id, + }, + ] + ); +} + +fn relay_to_para_assets_receiver_assertions(t: RelayToParaTest) { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { + asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"), + owner: *owner == t.receiver.account_id, + }, RuntimeEvent::MessageQueue( pallet_message_queue::Event::Processed { success: true, .. } ) => {}, @@ -162,33 +314,38 @@ fn system_para_to_para_assets_receiver_assertions(_: Test) { ); } -fn para_to_para_sender_assertions(t: ParaToParaTest) { - type RuntimeEvent = ::RuntimeEvent; - PenpalB::assert_xcm_pallet_attempted_complete(None); +fn para_to_para_through_relay_sender_assertions(t: ParaToParaThroughRelayTest) { + type RuntimeEvent = ::RuntimeEvent; + + let relay_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + + PenpalA::assert_xcm_pallet_attempted_complete(None); + // XCM sent to relay reserve + PenpalA::assert_parachain_system_ump_sent(); + assert_expected_events!( - PenpalB, + PenpalA, vec![ // Amount to reserve transfer is transferred to Parachain's Sovereign account - RuntimeEvent::Balances( - pallet_balances::Event::Burned { who, amount } + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, balance }, ) => { - who: *who == t.sender.account_id, - amount: *amount == t.args.amount, + asset_id: *asset_id == relay_asset_location, + owner: *owner == t.sender.account_id, + balance: *balance == t.args.amount, }, - // XCM sent to relay reserve - RuntimeEvent::ParachainSystem( - cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. } - ) => {}, ] ); } -fn para_to_para_relay_hop_assertions(t: ParaToParaTest) { +fn para_to_para_relay_hop_assertions(t: ParaToParaThroughRelayTest) { type RuntimeEvent = ::RuntimeEvent; - let sov_penpal_b_on_westend = - Westend::sovereign_account_id_of(Westend::child_location_of(PenpalB::para_id())); let sov_penpal_a_on_westend = Westend::sovereign_account_id_of(Westend::child_location_of(PenpalA::para_id())); + let sov_penpal_b_on_westend = + Westend::sovereign_account_id_of(Westend::child_location_of(PenpalB::para_id())); + assert_expected_events!( Westend, vec![ @@ -196,14 +353,14 @@ fn para_to_para_relay_hop_assertions(t: ParaToParaTest) { RuntimeEvent::Balances( pallet_balances::Event::Burned { who, amount } ) => { - who: *who == sov_penpal_b_on_westend, + who: *who == sov_penpal_a_on_westend, amount: *amount == t.args.amount, }, // Deposited to receiver parachain SA RuntimeEvent::Balances( pallet_balances::Event::Minted { who, .. } ) => { - who: *who == sov_penpal_a_on_westend, + who: *who == sov_penpal_b_on_westend, }, RuntimeEvent::MessageQueue( pallet_message_queue::Event::Processed { success: true, .. } @@ -212,15 +369,20 @@ fn para_to_para_relay_hop_assertions(t: ParaToParaTest) { ); } -fn para_to_para_receiver_assertions(_: ParaToParaTest) { - type RuntimeEvent = ::RuntimeEvent; +fn para_to_para_through_relay_receiver_assertions(t: ParaToParaThroughRelayTest) { + type RuntimeEvent = ::RuntimeEvent; + let relay_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + + PenpalB::assert_xcmp_queue_success(None); + assert_expected_events!( - PenpalA, + PenpalB, vec![ - RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, - RuntimeEvent::MessageQueue( - pallet_message_queue::Event::Processed { success: true, .. } - ) => {}, + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { + asset_id: *asset_id == relay_asset_location, + owner: *owner == t.receiver.account_id, + }, ] ); } @@ -236,6 +398,17 @@ fn relay_to_para_reserve_transfer_assets(t: RelayToParaTest) -> DispatchResult { ) } +fn para_to_relay_reserve_transfer_assets(t: ParaToRelayTest) -> DispatchResult { + ::PolkadotXcm::limited_reserve_transfer_assets( + t.signed_origin, + bx!(t.args.dest.into()), + bx!(t.args.beneficiary.into()), + bx!(t.args.assets.into()), + t.args.fee_asset_item, + t.args.weight_limit, + ) +} + fn system_para_to_para_reserve_transfer_assets(t: SystemParaToParaTest) -> DispatchResult { ::PolkadotXcm::limited_reserve_transfer_assets( t.signed_origin, @@ -248,7 +421,7 @@ fn system_para_to_para_reserve_transfer_assets(t: SystemParaToParaTest) -> Dispa } fn para_to_system_para_reserve_transfer_assets(t: ParaToSystemParaTest) -> DispatchResult { - ::PolkadotXcm::limited_reserve_transfer_assets( + ::PolkadotXcm::limited_reserve_transfer_assets( t.signed_origin, bx!(t.args.dest.into()), bx!(t.args.beneficiary.into()), @@ -258,8 +431,10 @@ fn para_to_system_para_reserve_transfer_assets(t: ParaToSystemParaTest) -> Dispa ) } -fn para_to_para_limited_reserve_transfer_assets(t: ParaToParaTest) -> DispatchResult { - ::PolkadotXcm::limited_reserve_transfer_assets( +fn para_to_para_through_relay_limited_reserve_transfer_assets( + t: ParaToParaThroughRelayTest, +) -> DispatchResult { + ::PolkadotXcm::limited_reserve_transfer_assets( t.signed_origin, bx!(t.args.dest.into()), bx!(t.args.beneficiary.into()), @@ -272,6 +447,7 @@ fn para_to_para_limited_reserve_transfer_assets(t: ParaToParaTest) -> DispatchRe /// Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't work #[test] fn reserve_transfer_native_asset_from_relay_to_system_para_fails() { + // Init values for Relay Chain let signed_origin = ::RuntimeOrigin::signed(WestendSender::get().into()); let destination = Westend::child_location_of(AssetHubWestend::para_id()); let beneficiary: Location = @@ -312,6 +488,7 @@ fn reserve_transfer_native_asset_from_system_para_to_relay_fails() { let beneficiary: Location = AccountId32Junction { network: None, id: beneficiary_id.into() }.into(); let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000; + let assets: Assets = (Parent, amount_to_send).into(); let fee_asset_item = 0; @@ -337,136 +514,261 @@ fn reserve_transfer_native_asset_from_system_para_to_relay_fails() { }); } +// ========================================================================= +// ========= Reserve Transfers - Native Asset - Relay<>Parachain =========== +// ========================================================================= /// Reserve Transfers of native asset from Relay to Parachain should work #[test] fn reserve_transfer_native_asset_from_relay_to_para() { // Init values for Relay - let destination = Westend::child_location_of(PenpalB::para_id()); - let beneficiary_id = PenpalBReceiver::get(); + let destination = Westend::child_location_of(PenpalA::para_id()); + let sender = WestendSender::get(); let amount_to_send: Balance = WESTEND_ED * 1000; + // Init values fot Parachain + let relay_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let receiver = PenpalAReceiver::get(); + + // Init Test let test_args = TestContext { - sender: WestendSender::get(), - receiver: PenpalBReceiver::get(), - args: TestArgs::new_relay(destination, beneficiary_id, amount_to_send), + sender, + receiver: receiver.clone(), + args: TestArgs::new_relay(destination.clone(), receiver.clone(), amount_to_send), }; - let mut test = RelayToParaTest::new(test_args); + // Query initial balances let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; + let receiver_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.into(), &receiver) + }); + // Set assertions and dispatchables test.set_assertion::(relay_to_para_sender_assertions); - test.set_assertion::(para_receiver_assertions); + test.set_assertion::(relay_to_para_assets_receiver_assertions); test.set_dispatchable::(relay_to_para_reserve_transfer_assets); test.assert(); - let delivery_fees = Westend::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + // Query final balances + let sender_balance_after = test.sender.balance; + let receiver_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.into(), &receiver) }); - let sender_balance_after = test.sender.balance; + // Sender's balance is reduced by amount sent plus delivery fees + assert!(sender_balance_after < sender_balance_before - amount_to_send); + // Receiver's asset balance is increased + assert!(receiver_assets_after > receiver_assets_before); + // Receiver's asset balance increased by `amount_to_send - delivery_fees - bought_execution`; + // `delivery_fees` might be paid from transfer or JIT, also `bought_execution` is unknown but + // should be non-zero + assert!(receiver_assets_after < receiver_assets_before + amount_to_send); +} + +/// Reserve Transfers of native asset from Parachain to Relay should work +#[test] +fn reserve_transfer_native_asset_from_para_to_relay() { + // Init values for Parachain + let destination = PenpalA::parent_location(); + let sender = PenpalASender::get(); + let amount_to_send: Balance = WESTEND_ED * 1000; + let assets: Assets = (Parent, amount_to_send).into(); + let asset_owner = PenpalAssetOwner::get(); + let relay_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + + // fund Parachain's sender account + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(asset_owner), + relay_native_asset_location, + sender.clone(), + amount_to_send * 2, + ); + + // Init values for Relay + let receiver = WestendReceiver::get(); + let penpal_location_as_seen_by_relay = Westend::child_location_of(PenpalA::para_id()); + let sov_penpal_on_relay = Westend::sovereign_account_id_of(penpal_location_as_seen_by_relay); + + // fund Parachain's SA on Relay with the native tokens held in reserve + Westend::fund_accounts(vec![(sov_penpal_on_relay.into(), amount_to_send * 2)]); + + // Init Test + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + destination.clone(), + receiver, + amount_to_send, + assets.clone(), + None, + 0, + ), + }; + let mut test = ParaToRelayTest::new(test_args); + + // Query initial balances + let sender_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.into(), &sender) + }); + let receiver_balance_before = test.receiver.balance; + + // Set assertions and dispatchables + test.set_assertion::(para_to_relay_sender_assertions); + test.set_assertion::(para_to_relay_receiver_assertions); + test.set_dispatchable::(para_to_relay_reserve_transfer_assets); + test.assert(); + + // Query final balances + let sender_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.into(), &sender) + }); let receiver_balance_after = test.receiver.balance; - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased + // Sender's balance is reduced by amount sent plus delivery fees + assert!(sender_assets_after < sender_assets_before - amount_to_send); + // Receiver's asset balance is increased assert!(receiver_balance_after > receiver_balance_before); - // Receiver's balance increased by `amount_to_send - delivery_fees - bought_execution`; + // Receiver's asset balance increased by `amount_to_send - delivery_fees - bought_execution`; // `delivery_fees` might be paid from transfer or JIT, also `bought_execution` is unknown but // should be non-zero assert!(receiver_balance_after < receiver_balance_before + amount_to_send); } +// ========================================================================= +// ======= Reserve Transfers - Native Asset - AssetHub<>Parachain ========== +// ========================================================================= /// Reserve Transfers of native asset from System Parachain to Parachain should work #[test] fn reserve_transfer_native_asset_from_system_para_to_para() { // Init values for System Parachain - let destination = AssetHubWestend::sibling_location_of(PenpalB::para_id()); - let beneficiary_id = PenpalBReceiver::get(); - let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000; - let assets = (Parent, amount_to_send).into(); + let destination = AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let sender = AssetHubWestendSender::get(); + let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 2000; + let assets: Assets = (Parent, amount_to_send).into(); + + // Init values for Parachain + let system_para_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let receiver = PenpalAReceiver::get(); + // Init Test let test_args = TestContext { - sender: AssetHubWestendSender::get(), - receiver: PenpalBReceiver::get(), - args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0), + sender, + receiver: receiver.clone(), + args: TestArgs::new_para( + destination.clone(), + receiver.clone(), + amount_to_send, + assets.clone(), + None, + 0, + ), }; - let mut test = SystemParaToParaTest::new(test_args); + // Query initial balances let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; + let receiver_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_native_asset_location.into(), &receiver) + }); + // Set assertions and dispatchables test.set_assertion::(system_para_to_para_sender_assertions); - test.set_assertion::(para_receiver_assertions); + test.set_assertion::(system_para_to_para_receiver_assertions); test.set_dispatchable::(system_para_to_para_reserve_transfer_assets); test.assert(); + // Query final balances let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - let delivery_fees = AssetHubWestend::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + let receiver_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_native_asset_location, &receiver) }); - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); - // Receiver's balance increased by `amount_to_send - delivery_fees - bought_execution`; + // Sender's balance is reduced by amount sent plus delivery fees + assert!(sender_balance_after < sender_balance_before - amount_to_send); + // Receiver's assets is increased + assert!(receiver_assets_after > receiver_assets_before); + // Receiver's assets increased by `amount_to_send - delivery_fees - bought_execution`; // `delivery_fees` might be paid from transfer or JIT, also `bought_execution` is unknown but // should be non-zero - assert!(receiver_balance_after < receiver_balance_before + amount_to_send); + assert!(receiver_assets_after < receiver_assets_before + amount_to_send); } /// Reserve Transfers of native asset from Parachain to System Parachain should work #[test] fn reserve_transfer_native_asset_from_para_to_system_para() { - // Init values for Penpal Parachain - let destination = PenpalB::sibling_location_of(AssetHubWestend::para_id()); - let beneficiary_id = AssetHubWestendReceiver::get(); + // Init values for Parachain + let destination = PenpalA::sibling_location_of(AssetHubWestend::para_id()); + let sender = PenpalASender::get(); let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000; - let assets = (Parent, amount_to_send).into(); + let assets: Assets = (Parent, amount_to_send).into(); + let system_para_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let asset_owner = PenpalAssetOwner::get(); + + // fund Parachain's sender account + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(asset_owner), + system_para_native_asset_location, + sender.clone(), + amount_to_send * 2, + ); + + // Init values for System Parachain + let receiver = AssetHubWestendReceiver::get(); + let penpal_location_as_seen_by_ahr = AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let sov_penpal_on_ahr = + AssetHubWestend::sovereign_account_id_of(penpal_location_as_seen_by_ahr); + + // fund Parachain's SA on System Parachain with the native tokens held in reserve + AssetHubWestend::fund_accounts(vec![(sov_penpal_on_ahr.into(), amount_to_send * 2)]); + // Init Test let test_args = TestContext { - sender: PenpalBSender::get(), - receiver: AssetHubWestendReceiver::get(), - args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0), + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + destination.clone(), + receiver.clone(), + amount_to_send, + assets.clone(), + None, + 0, + ), }; - let mut test = ParaToSystemParaTest::new(test_args); - let sender_balance_before = test.sender.balance; + // Query initial balances + let sender_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_native_asset_location, &sender) + }); let receiver_balance_before = test.receiver.balance; - let penpal_location_as_seen_by_ahw = AssetHubWestend::sibling_location_of(PenpalB::para_id()); - let sov_penpal_on_ahw = - AssetHubWestend::sovereign_account_id_of(penpal_location_as_seen_by_ahw); - - // fund the Penpal's SA on AHW with the native tokens held in reserve - AssetHubWestend::fund_accounts(vec![(sov_penpal_on_ahw.into(), amount_to_send * 2)]); - - test.set_assertion::(para_to_system_para_sender_assertions); + // Set assertions and dispatchables + test.set_assertion::(para_to_system_para_sender_assertions); test.set_assertion::(para_to_system_para_receiver_assertions); - test.set_dispatchable::(para_to_system_para_reserve_transfer_assets); + test.set_dispatchable::(para_to_system_para_reserve_transfer_assets); test.assert(); - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - let delivery_fees = PenpalB::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + // Query final balances + let sender_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_native_asset_location, &sender) }); + let receiver_balance_after = test.receiver.balance; - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); + // Sender's balance is reduced by amount sent plus delivery fees + assert!(sender_assets_after < sender_assets_before - amount_to_send); // Receiver's balance is increased assert!(receiver_balance_after > receiver_balance_before); // Receiver's balance increased by `amount_to_send - delivery_fees - bought_execution`; @@ -475,36 +777,27 @@ fn reserve_transfer_native_asset_from_para_to_system_para() { assert!(receiver_balance_after < receiver_balance_before + amount_to_send); } +// ========================================================================= +// ======= Reserve Transfers - Non-system Asset - AssetHub<>Parachain ====== +// ========================================================================= /// Reserve Transfers of a local asset and native asset from System Parachain to Parachain should /// work #[test] fn reserve_transfer_assets_from_system_para_to_para() { - // Force create asset on AssetHubWestend and PenpalB from Relay Chain - AssetHubWestend::force_create_and_mint_asset( - ASSET_ID, - ASSET_MIN_BALANCE, - true, - AssetHubWestendSender::get(), - Some(Weight::from_parts(1_019_445_000, 200_000)), - ASSET_MIN_BALANCE * 1_000_000, - ); - PenpalB::force_create_and_mint_asset( - ASSET_ID, - ASSET_MIN_BALANCE, - false, - PenpalBSender::get(), - None, - 0, - ); - // Init values for System Parachain - let destination = AssetHubWestend::sibling_location_of(PenpalB::para_id()); - let beneficiary_id = PenpalBReceiver::get(); - let fee_amount_to_send = ASSET_HUB_WESTEND_ED * 1000; - let asset_amount_to_send = ASSET_MIN_BALANCE * 1000; + let destination = AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let sov_penpal_on_ahr = AssetHubWestend::sovereign_account_id_of(destination.clone()); + let sender = AssetHubWestendSender::get(); + let fee_amount_to_send = ASSET_HUB_WESTEND_ED * 100; + let asset_amount_to_send = ASSET_HUB_WESTEND_ED * 100; + let asset_owner = AssetHubWestendAssetOwner::get(); + let asset_owner_signer = ::RuntimeOrigin::signed(asset_owner.clone()); let assets: Assets = vec![ (Parent, fee_amount_to_send).into(), - ([PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())], asset_amount_to_send) + ( + [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(RESERVABLE_ASSET_ID.into())], + asset_amount_to_send, + ) .into(), ] .into(); @@ -513,49 +806,212 @@ fn reserve_transfer_assets_from_system_para_to_para() { .iter() .position(|r| r == &(Parent, fee_amount_to_send).into()) .unwrap() as u32; + AssetHubWestend::mint_asset( + asset_owner_signer, + RESERVABLE_ASSET_ID, + asset_owner, + asset_amount_to_send * 2, + ); + // Create SA-of-Penpal-on-AHR with ED. + AssetHubWestend::fund_accounts(vec![(sov_penpal_on_ahr.into(), ASSET_HUB_WESTEND_ED)]); + + // Init values for Parachain + let receiver = PenpalAReceiver::get(); + let system_para_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let system_para_foreign_asset_location = + v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); + + // Init Test let para_test_args = TestContext { - sender: AssetHubWestendSender::get(), - receiver: PenpalBReceiver::get(), + sender: sender.clone(), + receiver: receiver.clone(), args: TestArgs::new_para( destination, - beneficiary_id, + receiver.clone(), asset_amount_to_send, assets, None, fee_asset_index, ), }; - let mut test = SystemParaToParaTest::new(para_test_args); - // Create SA-of-Penpal-on-AHW with ED. - let penpal_location = AssetHubWestend::sibling_location_of(PenpalB::para_id()); - let sov_penpal_on_ahw = AssetHubWestend::sovereign_account_id_of(penpal_location); - AssetHubWestend::fund_accounts(vec![(sov_penpal_on_ahw.into(), WESTEND_ED)]); - + // Query initial balances let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - let sender_assets_before = AssetHubWestend::execute_with(|| { type Assets = ::Assets; - >::balance(ASSET_ID, &AssetHubWestendSender::get()) + >::balance(RESERVABLE_ASSET_ID, &sender) }); - let receiver_assets_before = PenpalB::execute_with(|| { - type Assets = ::Assets; - >::balance(ASSET_ID, &PenpalBReceiver::get()) + let receiver_system_native_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_native_asset_location, &receiver) + }); + let receiver_foreign_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_foreign_asset_location, &receiver) }); + // Set assertions and dispatchables test.set_assertion::(system_para_to_para_assets_sender_assertions); - test.set_assertion::(system_para_to_para_assets_receiver_assertions); + test.set_assertion::(system_para_to_para_assets_receiver_assertions); test.set_dispatchable::(system_para_to_para_reserve_transfer_assets); test.assert(); + // Query final balances let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - + let sender_assets_after = AssetHubWestend::execute_with(|| { + type Assets = ::Assets; + >::balance(RESERVABLE_ASSET_ID, &sender) + }); + let receiver_system_native_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_native_asset_location, &receiver) + }); + let receiver_foreign_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_foreign_asset_location, &receiver) + }); // Sender's balance is reduced assert!(sender_balance_after < sender_balance_before); + // Receiver's foreign asset balance is increased + assert!(receiver_foreign_assets_after > receiver_foreign_assets_before); + // Receiver's system asset balance increased by `amount_to_send - delivery_fees - + // bought_execution`; `delivery_fees` might be paid from transfer or JIT, also + // `bought_execution` is unknown but should be non-zero + assert!( + receiver_system_native_assets_after < + receiver_system_native_assets_before + fee_amount_to_send + ); + + // Sender's asset balance is reduced by exact amount + assert_eq!(sender_assets_before - asset_amount_to_send, sender_assets_after); + // Receiver's foreign asset balance is increased by exact amount + assert_eq!( + receiver_foreign_assets_after, + receiver_foreign_assets_before + asset_amount_to_send + ); +} + +/// Reserve Transfers of a foreign asset and native asset from Parachain to System Para should +/// work +#[test] +fn reserve_transfer_assets_from_para_to_system_para() { + // Init values for Parachain + let destination = PenpalA::sibling_location_of(AssetHubWestend::para_id()); + let sender = PenpalASender::get(); + let fee_amount_to_send = ASSET_HUB_WESTEND_ED * 100; + let asset_amount_to_send = ASSET_HUB_WESTEND_ED * 100; + let penpal_asset_owner = PenpalAssetOwner::get(); + let penpal_asset_owner_signer = ::RuntimeOrigin::signed(penpal_asset_owner); + let asset_location_on_penpal = + v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); + let asset_location_on_penpal_latest: Location = asset_location_on_penpal.try_into().unwrap(); + let system_asset_location_on_penpal = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let assets: Assets = vec![ + (Parent, fee_amount_to_send).into(), + (asset_location_on_penpal_latest, asset_amount_to_send).into(), + ] + .into(); + let fee_asset_index = assets + .inner() + .iter() + .position(|r| r == &(Parent, fee_amount_to_send).into()) + .unwrap() as u32; + // Fund Parachain's sender account with some foreign assets + PenpalA::mint_foreign_asset( + penpal_asset_owner_signer.clone(), + asset_location_on_penpal, + sender.clone(), + asset_amount_to_send * 2, + ); + // Fund Parachain's sender account with some system assets + PenpalA::mint_foreign_asset( + penpal_asset_owner_signer, + system_asset_location_on_penpal, + sender.clone(), + fee_amount_to_send * 2, + ); + + // Init values for System Parachain + let receiver = AssetHubWestendReceiver::get(); + let penpal_location_as_seen_by_ahr = AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let sov_penpal_on_ahr = + AssetHubWestend::sovereign_account_id_of(penpal_location_as_seen_by_ahr); + let system_para_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let system_para_foreign_asset_location = + v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); + let ah_asset_owner = AssetHubWestendAssetOwner::get(); + let ah_asset_owner_signer = ::RuntimeOrigin::signed(ah_asset_owner); + + // Fund SA-of-Penpal-on-AHR to be able to pay for the fees. + AssetHubWestend::fund_accounts(vec![( + sov_penpal_on_ahr.clone().into(), + ASSET_HUB_WESTEND_ED * 1000, + )]); + // Fund SA-of-Penpal-on-AHR to be able to pay for the sent amount. + AssetHubWestend::mint_asset( + ah_asset_owner_signer, + RESERVABLE_ASSET_ID, + sov_penpal_on_ahr, + asset_amount_to_send * 2, + ); + + // Init Test + let para_test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + destination, + receiver.clone(), + asset_amount_to_send, + assets, + None, + fee_asset_index, + ), + }; + let mut test = ParaToSystemParaTest::new(para_test_args); + + // Query initial balances + let sender_system_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_native_asset_location, &sender) + }); + let sender_foreign_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_foreign_asset_location, &sender) + }); + let receiver_balance_before = test.receiver.balance; + let receiver_assets_before = AssetHubWestend::execute_with(|| { + type Assets = ::Assets; + >::balance(RESERVABLE_ASSET_ID, &receiver) + }); + + // Set assertions and dispatchables + test.set_assertion::(para_to_system_para_assets_sender_assertions); + test.set_assertion::(para_to_system_para_assets_receiver_assertions); + test.set_dispatchable::(para_to_system_para_reserve_transfer_assets); + test.assert(); + + // Query final balances + let sender_system_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_native_asset_location, &sender) + }); + let sender_foreign_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(system_para_foreign_asset_location, &sender) + }); + let receiver_balance_after = test.receiver.balance; + let receiver_assets_after = AssetHubWestend::execute_with(|| { + type Assets = ::Assets; + >::balance(RESERVABLE_ASSET_ID, &receiver) + }); + // Sender's system asset balance is reduced + assert!(sender_system_assets_after < sender_system_assets_before); // Receiver's balance is increased assert!(receiver_balance_after > receiver_balance_before); // Receiver's balance increased by `amount_to_send - delivery_fees - bought_execution`; @@ -563,65 +1019,81 @@ fn reserve_transfer_assets_from_system_para_to_para() { // should be non-zero assert!(receiver_balance_after < receiver_balance_before + fee_amount_to_send); - let sender_assets_after = AssetHubWestend::execute_with(|| { - type Assets = ::Assets; - >::balance(ASSET_ID, &AssetHubWestendSender::get()) - }); - let receiver_assets_after = PenpalB::execute_with(|| { - type Assets = ::Assets; - >::balance(ASSET_ID, &PenpalBReceiver::get()) - }); - - // Sender's balance is reduced by exact amount - assert_eq!(sender_assets_before - asset_amount_to_send, sender_assets_after); - // Receiver's balance is increased by exact amount + // Sender's asset balance is reduced by exact amount + assert_eq!(sender_foreign_assets_before - asset_amount_to_send, sender_foreign_assets_after); + // Receiver's foreign asset balance is increased by exact amount assert_eq!(receiver_assets_after, receiver_assets_before + asset_amount_to_send); } +// ========================================================================= +// ===== Reserve Transfers - Native Asset - Parachain<>Relay<>Parachain ==== +// ========================================================================= /// Reserve Transfers of native asset from Parachain to Parachain (through Relay reserve) should /// work #[test] -fn reserve_transfer_native_asset_from_para_to_para() { - // Init values for Penpal Parachain - let destination = PenpalB::sibling_location_of(PenpalA::para_id()); - let beneficiary_id = PenpalAReceiver::get(); - let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000; +fn reserve_transfer_native_asset_from_para_to_para_trough_relay() { + // Init values for Parachain Origin + let destination = PenpalA::sibling_location_of(PenpalB::para_id()); + let sender = PenpalASender::get(); + let amount_to_send: Balance = WESTEND_ED * 10000; + let asset_owner = PenpalAssetOwner::get(); let assets = (Parent, amount_to_send).into(); + let relay_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let sender_as_seen_by_relay = Westend::child_location_of(PenpalA::para_id()); + let sov_of_sender_on_relay = Westend::sovereign_account_id_of(sender_as_seen_by_relay); - let test_args = TestContext { - sender: PenpalBSender::get(), - receiver: PenpalAReceiver::get(), - args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0), - }; + // fund Parachain's sender account + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(asset_owner), + relay_native_asset_location, + sender.clone(), + amount_to_send * 2, + ); - let mut test = ParaToParaTest::new(test_args); + // fund the Parachain Origin's SA on Relay Chain with the native tokens held in reserve + Westend::fund_accounts(vec![(sov_of_sender_on_relay.into(), amount_to_send * 2)]); - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; + // Init values for Parachain Destination + let receiver = PenpalBReceiver::get(); - let sender_as_seen_by_relay = Westend::child_location_of(PenpalB::para_id()); - let sov_of_sender_on_relay = Westend::sovereign_account_id_of(sender_as_seen_by_relay); + // Init Test + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para(destination, receiver.clone(), amount_to_send, assets, None, 0), + }; + let mut test = ParaToParaThroughRelayTest::new(test_args); - // fund the PenpalB's SA on Westend with the native tokens held in reserve - Westend::fund_accounts(vec![(sov_of_sender_on_relay.into(), amount_to_send * 2)]); + // Query initial balances + let sender_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location, &sender) + }); + let receiver_assets_before = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location, &receiver) + }); - test.set_assertion::(para_to_para_sender_assertions); + // Set assertions and dispatchables + test.set_assertion::(para_to_para_through_relay_sender_assertions); test.set_assertion::(para_to_para_relay_hop_assertions); - test.set_assertion::(para_to_para_receiver_assertions); - test.set_dispatchable::(para_to_para_limited_reserve_transfer_assets); + test.set_assertion::(para_to_para_through_relay_receiver_assertions); + test.set_dispatchable::(para_to_para_through_relay_limited_reserve_transfer_assets); test.assert(); - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - let delivery_fees = PenpalB::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + // Query final balances + let sender_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location, &sender) + }); + let receiver_assets_after = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location, &receiver) }); - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); + // Sender's balance is reduced by amount sent plus delivery fees + assert!(sender_assets_after < sender_assets_before - amount_to_send); // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); + assert!(receiver_assets_after > receiver_assets_before); } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs index a3cd5c5803eef3937e2a8e5c33894b369cb5cc31..f218b539c387988f70314eff41c2e1ce4e97092b 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; +use crate::imports::*; /// Relay Chain should be able to execute `Transact` instructions in System Parachain /// when `OriginKind::Superuser`. @@ -28,12 +28,99 @@ fn send_transact_as_superuser_from_relay_to_system_para_works() { ) } -/// Parachain should be able to send XCM paying its fee with sufficient asset -/// in the System Parachain +/// We tests two things here: +/// - Parachain should be able to send XCM paying its fee with system asset in the System Parachain +/// - Parachain should be able to create a new Foreign Asset in the System Parachain +#[test] +fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { + let para_sovereign_account = AssetHubWestend::sovereign_account_id_of( + AssetHubWestend::sibling_location_of(PenpalA::para_id()), + ); + let asset_location_on_penpal = v3::Location::new( + 0, + [ + v3::Junction::PalletInstance(ASSETS_PALLET_ID), + v3::Junction::GeneralIndex(ASSET_ID.into()), + ], + ); + let foreign_asset_at_asset_hub = + v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())]) + .appended_with(asset_location_on_penpal) + .unwrap(); + + // Encoded `create_asset` call to be executed in AssetHub + let call = AssetHubWestend::create_foreign_asset_call( + foreign_asset_at_asset_hub, + ASSET_MIN_BALANCE, + para_sovereign_account.clone(), + ); + + let origin_kind = OriginKind::Xcm; + let fee_amount = ASSET_HUB_WESTEND_ED * 1000000; + let system_asset = (Parent, fee_amount).into(); + + let root_origin = ::RuntimeOrigin::root(); + let system_para_destination = PenpalA::sibling_location_of(AssetHubWestend::para_id()).into(); + let xcm = xcm_transact_paid_execution( + call, + origin_kind, + system_asset, + para_sovereign_account.clone(), + ); + + // SA-of-Penpal-on-AHR needs to have balance to pay for fees and asset creation deposit + AssetHubWestend::fund_accounts(vec![( + para_sovereign_account.clone().into(), + ASSET_HUB_WESTEND_ED * 10000000000, + )]); + + PenpalA::execute_with(|| { + assert_ok!(::PolkadotXcm::send_blob( + root_origin, + bx!(system_para_destination), + xcm.encode().try_into().unwrap(), + )); + + PenpalA::assert_xcm_pallet_sent(); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + AssetHubWestend::assert_xcmp_queue_success(Some(Weight::from_parts( + 15_594_564_000, + 562_893, + ))); + + assert_expected_events!( + AssetHubWestend, + vec![ + // Burned the fee + RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { + who: *who == para_sovereign_account, + amount: *amount == fee_amount, + }, + // Foreign Asset created + RuntimeEvent::ForeignAssets(pallet_assets::Event::Created { asset_id, creator, owner }) => { + asset_id: *asset_id == foreign_asset_at_asset_hub, + creator: *creator == para_sovereign_account.clone(), + owner: *owner == para_sovereign_account, + }, + ] + ); + + type ForeignAssets = ::ForeignAssets; + assert!(ForeignAssets::asset_exists(foreign_asset_at_asset_hub)); + }); +} + +/// We tests two things here: +/// - Parachain should be able to send XCM paying its fee with system assets in the System Parachain +/// - Parachain should be able to create a new Asset in the System Parachain #[test] fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { let para_sovereign_account = AssetHubWestend::sovereign_account_id_of( - AssetHubWestend::sibling_location_of(PenpalB::para_id()), + AssetHubWestend::sibling_location_of(PenpalA::para_id()), ); // Force create and mint assets for Parachain's sovereign account @@ -46,57 +133,63 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { ASSET_MIN_BALANCE * 1000000000, ); - // We just need a call that can pass the `SafeCallFilter` - // Call values are not relevant - let call = AssetHubWestend::force_create_asset_call( - ASSET_ID, - para_sovereign_account.clone(), - true, + // Just a different `asset_id`` that does not exist yet + let new_asset_id = ASSET_ID + 1; + + // Encoded `create_asset` call to be executed in AssetHub + let call = AssetHubWestend::create_asset_call( + new_asset_id, ASSET_MIN_BALANCE, + para_sovereign_account.clone(), ); let origin_kind = OriginKind::SovereignAccount; let fee_amount = ASSET_MIN_BALANCE * 1000000; - let native_asset = + let asset = ([PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())], fee_amount).into(); - let root_origin = ::RuntimeOrigin::root(); - let system_para_destination = PenpalB::sibling_location_of(AssetHubWestend::para_id()).into(); - let xcm = xcm_transact_paid_execution( - call, - origin_kind, - native_asset, - para_sovereign_account.clone(), - ); + let root_origin = ::RuntimeOrigin::root(); + let system_para_destination = PenpalA::sibling_location_of(AssetHubWestend::para_id()).into(); + let xcm = xcm_transact_paid_execution(call, origin_kind, asset, para_sovereign_account.clone()); - PenpalB::execute_with(|| { - assert_ok!(::PolkadotXcm::send( + // SA-of-Penpal-on-AHR needs to have balance to pay for asset creation deposit + AssetHubWestend::fund_accounts(vec![( + para_sovereign_account.clone().into(), + ASSET_HUB_WESTEND_ED * 10000000000, + )]); + + PenpalA::execute_with(|| { + assert_ok!(::PolkadotXcm::send_blob( root_origin, bx!(system_para_destination), - bx!(xcm), + xcm.encode().try_into().unwrap(), )); - PenpalB::assert_xcm_pallet_sent(); + PenpalA::assert_xcm_pallet_sent(); }); AssetHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; AssetHubWestend::assert_xcmp_queue_success(Some(Weight::from_parts( - 16_290_336_000, + 15_594_564_000, 562_893, ))); assert_expected_events!( AssetHubWestend, vec![ + // Burned the fee RuntimeEvent::Assets(pallet_assets::Event::Burned { asset_id, owner, balance }) => { asset_id: *asset_id == ASSET_ID, owner: *owner == para_sovereign_account, balance: *balance == fee_amount, }, - RuntimeEvent::Assets(pallet_assets::Event::Issued { asset_id, .. }) => { - asset_id: *asset_id == ASSET_ID, + // Asset created + RuntimeEvent::Assets(pallet_assets::Event::Created { asset_id, creator, owner }) => { + asset_id: *asset_id == new_asset_id, + creator: *creator == para_sovereign_account.clone(), + owner: *owner == para_sovereign_account, }, ] ); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/set_xcm_versions.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/set_xcm_versions.rs index 130454551d2cadc8866128f81cf3f5e6e33cc356..474e9a86ccc22777bc4f2ac574ef70ec39843a45 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/set_xcm_versions.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/set_xcm_versions.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; +use crate::imports::*; #[test] fn relay_sets_system_para_xcm_supported_version() { diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs index b39cc2159de8d407f8ef9b91c32549b2d43411a4..aa673c03483af13cae2ac146049399644b265b6b 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs @@ -13,8 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; -use westend_system_emulated_network::penpal_emulated_chain::LocalTeleportableToAssetHubV3 as PenpalLocalTeleportableToAssetHubV3; +use crate::imports::*; #[test] fn swap_locally_on_chain_using_local_assets() { @@ -112,49 +111,39 @@ fn swap_locally_on_chain_using_local_assets() { #[test] fn swap_locally_on_chain_using_foreign_assets() { - let asset_native = Box::new(asset_hub_westend_runtime::xcm_config::WestendLocationV3::get()); - let ah_as_seen_by_penpal = PenpalB::sibling_location_of(AssetHubWestend::para_id()); - let asset_location_on_penpal = PenpalLocalTeleportableToAssetHubV3::get(); - let asset_id_on_penpal = match asset_location_on_penpal.last() { - Some(v3::Junction::GeneralIndex(id)) => *id as u32, - _ => unreachable!(), - }; - let asset_owner_on_penpal = PenpalBSender::get(); + let asset_native = + Box::new(v3::Location::try_from(RelayLocation::get()).expect("conversion works")); + let asset_location_on_penpal = + v3::Location::try_from(PenpalLocalTeleportableToAssetHub::get()).expect("conversion_works"); let foreign_asset_at_asset_hub_westend = - v3::Location::new(1, [v3::Junction::Parachain(PenpalB::para_id().into())]) + v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())]) .appended_with(asset_location_on_penpal) .unwrap(); - // 1. Create asset on penpal and, 2. Create foreign asset on asset_hub_westend - super::penpal_create_foreign_asset_on_asset_hub( - asset_id_on_penpal, - foreign_asset_at_asset_hub_westend, - ah_as_seen_by_penpal, - true, - asset_owner_on_penpal, - ASSET_MIN_BALANCE * 1_000_000, - ); - - let penpal_as_seen_by_ah = AssetHubWestend::sibling_location_of(PenpalB::para_id()); - let sov_penpal_on_ahw = AssetHubWestend::sovereign_account_id_of(penpal_as_seen_by_ah); + let penpal_as_seen_by_ah = AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let sov_penpal_on_ahr = AssetHubWestend::sovereign_account_id_of(penpal_as_seen_by_ah); AssetHubWestend::fund_accounts(vec![ - (AssetHubWestendSender::get().into(), 5_000_000 * WESTEND_ED), /* An account to swap dot - * for something else. */ + // An account to swap dot for something else. + (AssetHubWestendSender::get().into(), 5_000_000 * ASSET_HUB_WESTEND_ED), + // Penpal's sovereign account in AH should have some balance + (sov_penpal_on_ahr.clone().into(), 100_000_000 * ASSET_HUB_WESTEND_ED), ]); AssetHubWestend::execute_with(|| { - // 3: Mint foreign asset on asset_hub_westend: + // 0: No need to create foreign asset as it exists in genesis. + // + // 1: Mint foreign asset on asset_hub_westend: // // (While it might be nice to use batch, // currently that's disabled due to safe call filters.) type RuntimeEvent = ::RuntimeEvent; - // 3. Mint foreign asset (in reality this should be a teleport or some such) + // 1. Mint foreign asset (in reality this should be a teleport or some such) assert_ok!(::ForeignAssets::mint( - ::RuntimeOrigin::signed(sov_penpal_on_ahw.clone().into()), + ::RuntimeOrigin::signed(sov_penpal_on_ahr.clone().into()), foreign_asset_at_asset_hub_westend, - sov_penpal_on_ahw.clone().into(), - 3_000_000_000_000, + sov_penpal_on_ahr.clone().into(), + ASSET_HUB_WESTEND_ED * 3_000_000_000_000, )); assert_expected_events!( @@ -164,7 +153,7 @@ fn swap_locally_on_chain_using_foreign_assets() { ] ); - // 4. Create pool: + // 2. Create pool: assert_ok!(::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), asset_native.clone(), @@ -178,58 +167,60 @@ fn swap_locally_on_chain_using_foreign_assets() { ] ); - // 5. Add liquidity: + // 3. Add liquidity: assert_ok!(::AssetConversion::add_liquidity( - ::RuntimeOrigin::signed(sov_penpal_on_ahw.clone()), + ::RuntimeOrigin::signed(sov_penpal_on_ahr.clone()), asset_native.clone(), Box::new(foreign_asset_at_asset_hub_westend), - 1_000_000_000_000, - 2_000_000_000_000, + 1_000_000_000_000_000, + 2_000_000_000_000_000, 0, 0, - sov_penpal_on_ahw.clone().into() + sov_penpal_on_ahr.clone().into() )); assert_expected_events!( AssetHubWestend, vec![ RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded {lp_token_minted, .. }) => { - lp_token_minted: *lp_token_minted == 1414213562273, + lp_token_minted: *lp_token_minted == 1414213562372995, }, ] ); - // 6. Swap! + // 4. Swap! let path = vec![asset_native.clone(), Box::new(foreign_asset_at_asset_hub_westend)]; - assert_ok!(::AssetConversion::swap_exact_tokens_for_tokens( - ::RuntimeOrigin::signed(AssetHubWestendSender::get()), - path, - 100000, - 1000, - AssetHubWestendSender::get().into(), - true - )); + assert_ok!( + ::AssetConversion::swap_exact_tokens_for_tokens( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + path, + 100000 * ASSET_HUB_WESTEND_ED, + 1000 * ASSET_HUB_WESTEND_ED, + AssetHubWestendSender::get().into(), + true + ) + ); assert_expected_events!( AssetHubWestend, vec![ RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::SwapExecuted { amount_in, amount_out, .. },) => { - amount_in: *amount_in == 100000, - amount_out: *amount_out == 199399, + amount_in: *amount_in == 100000000000000, + amount_out: *amount_out == 181322178776029, }, ] ); - // 7. Remove liquidity + // 5. Remove liquidity assert_ok!(::AssetConversion::remove_liquidity( - ::RuntimeOrigin::signed(sov_penpal_on_ahw.clone()), + ::RuntimeOrigin::signed(sov_penpal_on_ahr.clone()), asset_native.clone(), Box::new(foreign_asset_at_asset_hub_westend), - 1414213562273 - 2_000_000_000, // all but the 2 EDs can't be retrieved. + 1414213562372995 - ASSET_HUB_WESTEND_ED * 2, // all but the 2 EDs can't be retrieved. 0, 0, - sov_penpal_on_ahw.into(), + sov_penpal_on_ahr.clone().into(), )); }); } @@ -283,7 +274,7 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() { .into(), }; let penpal = AssetHubWestend::sovereign_account_id_of(AssetHubWestend::sibling_location_of( - PenpalB::para_id(), + PenpalA::para_id(), )); AssetHubWestend::execute_with(|| { @@ -356,7 +347,7 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() { )); }); - PenpalB::execute_with(|| { + PenpalA::execute_with(|| { // send xcm transact from `penpal` account which as only `ASSET_ID` tokens on // `AssetHubWestend` let call = AssetHubWestend::force_create_asset_call( @@ -366,11 +357,11 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() { ASSET_MIN_BALANCE, ); - let penpal_root = ::RuntimeOrigin::root(); + let penpal_root = ::RuntimeOrigin::root(); let fee_amount = 4_000_000_000_000u128; let asset_one = ([PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())], fee_amount).into(); - let asset_hub_location = PenpalB::sibling_location_of(AssetHubWestend::para_id()).into(); + let asset_hub_location = PenpalA::sibling_location_of(AssetHubWestend::para_id()).into(); let xcm = xcm_transact_paid_execution( call, OriginKind::SovereignAccount, @@ -378,13 +369,13 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() { penpal.clone(), ); - assert_ok!(::PolkadotXcm::send( + assert_ok!(::PolkadotXcm::send_blob( penpal_root, bx!(asset_hub_location), - bx!(xcm), + xcm.encode().try_into().unwrap(), )); - PenpalB::assert_xcm_pallet_sent(); + PenpalA::assert_xcm_pallet_sent(); }); AssetHubWestend::execute_with(|| { diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs index 0dd1a1533b55904e4fb99dd0d9e3c6face43b19d..aba05ea4322cb1bc6609cbce6f2962236bab588c 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs @@ -13,16 +13,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; -use asset_hub_westend_runtime::xcm_config::XcmConfig as AssetHubWestendXcmConfig; -use emulated_integration_tests_common::xcm_helpers::non_fee_asset; -use westend_runtime::xcm_config::XcmConfig as WestendXcmConfig; -use westend_system_emulated_network::penpal_emulated_chain::LocalTeleportableToAssetHubV3 as PenpalLocalTeleportableToAssetHubV3; +use crate::imports::*; fn relay_origin_assertions(t: RelayToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; - Westend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(632_207_000, 7_186))); + Westend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(631_531_000, 7_186))); assert_expected_events!( Westend, @@ -47,7 +43,7 @@ fn relay_dest_assertions(t: SystemParaToRelayTest) { Westend::assert_ump_queue_processed( true, Some(AssetHubWestend::para_id()), - Some(Weight::from_parts(308_222_000, 7_186)), + Some(Weight::from_parts(307_225_000, 7_186)), ); assert_expected_events!( @@ -70,7 +66,7 @@ fn relay_dest_assertions_fail(_t: SystemParaToRelayTest) { Westend::assert_ump_queue_processed( false, Some(AssetHubWestend::para_id()), - Some(Weight::from_parts(148_705_000, 3_593)), + Some(Weight::from_parts(157_718_000, 3_593)), ); } @@ -78,8 +74,8 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { type RuntimeEvent = ::RuntimeEvent; AssetHubWestend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts( - 533_910_000, - 7167, + 720_053_000, + 7_203, ))); AssetHubWestend::assert_parachain_system_ump_sent(); @@ -99,7 +95,7 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { fn para_dest_assertions(t: RelayToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; - AssetHubWestend::assert_dmp_queue_complete(Some(Weight::from_parts(164_793_000, 3593))); + AssetHubWestend::assert_dmp_queue_complete(Some(Weight::from_parts(157_718_000, 3593))); assert_expected_events!( AssetHubWestend, @@ -113,19 +109,22 @@ fn para_dest_assertions(t: RelayToSystemParaTest) { } fn penpal_to_ah_foreign_assets_sender_assertions(t: ParaToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - PenpalB::assert_xcm_pallet_attempted_complete(None); + type RuntimeEvent = ::RuntimeEvent; + let system_para_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); let expected_asset_id = t.args.asset_id.unwrap(); let (_, expected_asset_amount) = non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap(); + + PenpalA::assert_xcm_pallet_attempted_complete(None); assert_expected_events!( - PenpalB, + PenpalA, vec![ - RuntimeEvent::Balances( - pallet_balances::Event::Burned { who, amount } + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, .. } ) => { - who: *who == t.sender.account_id, - amount: *amount == t.args.amount, + asset_id: *asset_id == system_para_native_asset_location, + owner: *owner == t.sender.account_id, }, RuntimeEvent::Assets(pallet_assets::Event::Burned { asset_id, owner, balance }) => { asset_id: *asset_id == expected_asset_id, @@ -139,11 +138,14 @@ fn penpal_to_ah_foreign_assets_sender_assertions(t: ParaToSystemParaTest) { fn penpal_to_ah_foreign_assets_receiver_assertions(t: ParaToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; let sov_penpal_on_ahr = AssetHubWestend::sovereign_account_id_of( - AssetHubWestend::sibling_location_of(PenpalB::para_id()), + AssetHubWestend::sibling_location_of(PenpalA::para_id()), ); let (expected_foreign_asset_id, expected_foreign_asset_amount) = non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap(); let expected_foreign_asset_id_v3: v3::Location = expected_foreign_asset_id.try_into().unwrap(); + + AssetHubWestend::assert_xcmp_queue_success(None); + assert_expected_events!( AssetHubWestend, vec![ @@ -163,9 +165,6 @@ fn penpal_to_ah_foreign_assets_receiver_assertions(t: ParaToSystemParaTest) { amount: *amount == expected_foreign_asset_amount, }, RuntimeEvent::Balances(pallet_balances::Event::Deposit { .. }) => {}, - RuntimeEvent::MessageQueue( - pallet_message_queue::Event::Processed { success: true, .. } - ) => {}, ] ); } @@ -200,13 +199,18 @@ fn ah_to_penpal_foreign_assets_sender_assertions(t: SystemParaToParaTest) { } fn ah_to_penpal_foreign_assets_receiver_assertions(t: SystemParaToParaTest) { - type RuntimeEvent = ::RuntimeEvent; + type RuntimeEvent = ::RuntimeEvent; let expected_asset_id = t.args.asset_id.unwrap(); let (_, expected_asset_amount) = non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap(); - let checking_account = ::PolkadotXcm::check_account(); + let checking_account = ::PolkadotXcm::check_account(); + let system_para_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + + PenpalA::assert_xcmp_queue_success(None); + assert_expected_events!( - PenpalB, + PenpalA, vec![ // checking account burns local asset as part of incoming teleport RuntimeEvent::Assets(pallet_assets::Event::Burned { asset_id, owner, balance }) => { @@ -221,12 +225,11 @@ fn ah_to_penpal_foreign_assets_receiver_assertions(t: SystemParaToParaTest) { amount: *amount == expected_asset_amount, }, // native asset for fee is deposited to receiver - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, amount }) => { + asset_id: *asset_id == system_para_native_asset_location, + owner: *owner == t.receiver.account_id, + amount: *amount == expected_asset_amount, }, - RuntimeEvent::MessageQueue( - pallet_message_queue::Event::Processed { success: true, .. } - ) => {}, ] ); } @@ -273,8 +276,8 @@ fn system_para_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult { ) } -fn system_para_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResult { - ::PolkadotXcm::transfer_assets( +fn para_to_system_para_transfer_assets(t: ParaToSystemParaTest) -> DispatchResult { + ::PolkadotXcm::transfer_assets( t.signed_origin, bx!(t.args.dest.into()), bx!(t.args.beneficiary.into()), @@ -284,8 +287,8 @@ fn system_para_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResul ) } -fn para_to_system_para_transfer_assets(t: ParaToSystemParaTest) -> DispatchResult { - ::PolkadotXcm::transfer_assets( +fn system_para_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResult { + ::PolkadotXcm::transfer_assets( t.signed_origin, bx!(t.args.dest.into()), bx!(t.args.beneficiary.into()), @@ -301,11 +304,11 @@ fn limited_teleport_native_assets_from_relay_to_system_para_works() { // Init values for Relay Chain let amount_to_send: Balance = WESTEND_ED * 1000; let dest = Westend::child_location_of(AssetHubWestend::para_id()); - let beneficiary = AssetHubWestendReceiver::get(); + let beneficiary_id = AssetHubWestendReceiver::get(); let test_args = TestContext { sender: WestendSender::get(), - receiver: beneficiary.clone(), - args: TestArgs::new_relay(dest, beneficiary, amount_to_send), + receiver: AssetHubWestendReceiver::get(), + args: TestArgs::new_relay(dest, beneficiary_id, amount_to_send), }; let mut test = RelayToSystemParaTest::new(test_args); @@ -319,7 +322,7 @@ fn limited_teleport_native_assets_from_relay_to_system_para_works() { test.assert(); let delivery_fees = Westend::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< + xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) }); @@ -366,7 +369,7 @@ fn limited_teleport_native_assets_back_from_system_para_to_relay_works() { let receiver_balance_after = test.receiver.balance; let delivery_fees = AssetHubWestend::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< + xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) }); @@ -407,7 +410,7 @@ fn limited_teleport_native_assets_from_system_para_to_relay_fails() { let receiver_balance_after = test.receiver.balance; let delivery_fees = AssetHubWestend::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< + xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) }); @@ -424,11 +427,11 @@ fn teleport_native_assets_from_relay_to_system_para_works() { // Init values for Relay Chain let amount_to_send: Balance = WESTEND_ED * 1000; let dest = Westend::child_location_of(AssetHubWestend::para_id()); - let beneficiary = AssetHubWestendReceiver::get(); + let beneficiary_id = AssetHubWestendReceiver::get(); let test_args = TestContext { sender: WestendSender::get(), - receiver: beneficiary.clone(), - args: TestArgs::new_relay(dest, beneficiary, amount_to_send), + receiver: AssetHubWestendReceiver::get(), + args: TestArgs::new_relay(dest, beneficiary_id, amount_to_send), }; let mut test = RelayToSystemParaTest::new(test_args); @@ -442,7 +445,7 @@ fn teleport_native_assets_from_relay_to_system_para_works() { test.assert(); let delivery_fees = Westend::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< + xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) }); @@ -485,15 +488,15 @@ fn teleport_native_assets_back_from_system_para_to_relay_works() { test.set_dispatchable::(system_para_teleport_assets); test.assert(); + let sender_balance_after = test.sender.balance; + let receiver_balance_after = test.receiver.balance; + let delivery_fees = AssetHubWestend::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< + xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) }); - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - // Sender's balance is reduced assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); // Receiver's balance is increased @@ -527,7 +530,7 @@ fn teleport_native_assets_from_system_para_to_relay_fails() { test.assert(); let delivery_fees = AssetHubWestend::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< + xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) }); @@ -558,30 +561,21 @@ fn teleport_to_other_system_parachains_works() { /// (using native reserve-based transfer for fees) #[test] fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { - let ah_as_seen_by_penpal = PenpalB::sibling_location_of(AssetHubWestend::para_id()); - let asset_location_on_penpal = PenpalLocalTeleportableToAssetHubV3::get(); + // Init values for Parachain + let fee_amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 100; + let asset_location_on_penpal = + v3::Location::try_from(PenpalLocalTeleportableToAssetHub::get()).expect("conversion works"); let asset_id_on_penpal = match asset_location_on_penpal.last() { Some(v3::Junction::GeneralIndex(id)) => *id as u32, _ => unreachable!(), }; - let asset_owner_on_penpal = PenpalBSender::get(); - let foreign_asset_at_asset_hub_westend = - v3::Location::new(1, [v3::Junction::Parachain(PenpalB::para_id().into())]) - .appended_with(asset_location_on_penpal) - .unwrap(); - super::penpal_create_foreign_asset_on_asset_hub( - asset_id_on_penpal, - foreign_asset_at_asset_hub_westend, - ah_as_seen_by_penpal.clone(), - false, - asset_owner_on_penpal, - ASSET_MIN_BALANCE * 1_000_000, - ); - let penpal_to_ah_beneficiary_id = AssetHubWestendReceiver::get(); - - let fee_amount_to_send = ASSET_HUB_WESTEND_ED * 1000; - let asset_amount_to_send = ASSET_MIN_BALANCE * 1000; - + let asset_amount_to_send = ASSET_HUB_WESTEND_ED * 100; + let asset_owner = PenpalAssetOwner::get(); + let system_para_native_asset_location = + v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let sender = PenpalASender::get(); + let penpal_check_account = ::PolkadotXcm::check_account(); + let ah_as_seen_by_penpal = PenpalA::sibling_location_of(AssetHubWestend::para_id()); let asset_location_on_penpal_latest: Location = asset_location_on_penpal.try_into().unwrap(); let penpal_assets: Assets = vec![ (Parent, fee_amount_to_send).into(), @@ -594,9 +588,44 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { .position(|r| r == &(Parent, fee_amount_to_send).into()) .unwrap() as u32; + // fund Parachain's sender account + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(asset_owner.clone()), + system_para_native_asset_location, + sender.clone(), + fee_amount_to_send * 2, + ); + // No need to create the asset (only mint) as it exists in genesis. + PenpalA::mint_asset( + ::RuntimeOrigin::signed(asset_owner.clone()), + asset_id_on_penpal, + sender.clone(), + asset_amount_to_send, + ); + // fund Parachain's check account to be able to teleport + PenpalA::fund_accounts(vec![( + penpal_check_account.clone().into(), + ASSET_HUB_WESTEND_ED * 1000, + )]); + + // prefund SA of Penpal on AssetHub with enough native tokens to pay for fees + let penpal_as_seen_by_ah = AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let sov_penpal_on_ah = AssetHubWestend::sovereign_account_id_of(penpal_as_seen_by_ah); + AssetHubWestend::fund_accounts(vec![( + sov_penpal_on_ah.clone().into(), + ASSET_HUB_WESTEND_ED * 100_000_000_000, + )]); + + // Init values for System Parachain + let foreign_asset_at_asset_hub_westend = + v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())]) + .appended_with(asset_location_on_penpal) + .unwrap(); + let penpal_to_ah_beneficiary_id = AssetHubWestendReceiver::get(); + // Penpal to AH test args let penpal_to_ah_test_args = TestContext { - sender: PenpalBSender::get(), + sender: PenpalASender::get(), receiver: AssetHubWestendReceiver::get(), args: TestArgs::new_para( ah_as_seen_by_penpal, @@ -608,13 +637,19 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { ), }; let mut penpal_to_ah = ParaToSystemParaTest::new(penpal_to_ah_test_args); + let penpal_sender_balance_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance( + system_para_native_asset_location, + &PenpalASender::get(), + ) + }); - let penpal_sender_balance_before = penpal_to_ah.sender.balance; let ah_receiver_balance_before = penpal_to_ah.receiver.balance; - let penpal_sender_assets_before = PenpalB::execute_with(|| { - type Assets = ::Assets; - >::balance(asset_id_on_penpal, &PenpalBSender::get()) + let penpal_sender_assets_before = PenpalA::execute_with(|| { + type Assets = ::Assets; + >::balance(asset_id_on_penpal, &PenpalASender::get()) }); let ah_receiver_assets_before = AssetHubWestend::execute_with(|| { type Assets = ::ForeignAssets; @@ -624,17 +659,24 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { ) }); - penpal_to_ah.set_assertion::(penpal_to_ah_foreign_assets_sender_assertions); + penpal_to_ah.set_assertion::(penpal_to_ah_foreign_assets_sender_assertions); penpal_to_ah.set_assertion::(penpal_to_ah_foreign_assets_receiver_assertions); - penpal_to_ah.set_dispatchable::(para_to_system_para_transfer_assets); + penpal_to_ah.set_dispatchable::(para_to_system_para_transfer_assets); penpal_to_ah.assert(); - let penpal_sender_balance_after = penpal_to_ah.sender.balance; + let penpal_sender_balance_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance( + system_para_native_asset_location, + &PenpalASender::get(), + ) + }); + let ah_receiver_balance_after = penpal_to_ah.receiver.balance; - let penpal_sender_assets_after = PenpalB::execute_with(|| { - type Assets = ::Assets; - >::balance(asset_id_on_penpal, &PenpalBSender::get()) + let penpal_sender_assets_after = PenpalA::execute_with(|| { + type Assets = ::Assets; + >::balance(asset_id_on_penpal, &PenpalASender::get()) }); let ah_receiver_assets_after = AssetHubWestend::execute_with(|| { type Assets = ::ForeignAssets; @@ -675,8 +717,8 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { let foreign_asset_at_asset_hub_westend_latest: Location = foreign_asset_at_asset_hub_westend.try_into().unwrap(); - let ah_to_penpal_beneficiary_id = PenpalBReceiver::get(); - let penpal_as_seen_by_ah = AssetHubWestend::sibling_location_of(PenpalB::para_id()); + let ah_to_penpal_beneficiary_id = PenpalAReceiver::get(); + let penpal_as_seen_by_ah = AssetHubWestend::sibling_location_of(PenpalA::para_id()); let ah_assets: Assets = vec![ (Parent, fee_amount_to_send).into(), (foreign_asset_at_asset_hub_westend_latest, asset_amount_to_send).into(), @@ -691,7 +733,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { // AH to Penpal test args let ah_to_penpal_test_args = TestContext { sender: AssetHubWestendSender::get(), - receiver: PenpalBReceiver::get(), + receiver: PenpalAReceiver::get(), args: TestArgs::new_para( penpal_as_seen_by_ah, ah_to_penpal_beneficiary_id, @@ -704,7 +746,13 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { let mut ah_to_penpal = SystemParaToParaTest::new(ah_to_penpal_test_args); let ah_sender_balance_before = ah_to_penpal.sender.balance; - let penpal_receiver_balance_before = ah_to_penpal.receiver.balance; + let penpal_receiver_balance_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance( + system_para_native_asset_location, + &PenpalAReceiver::get(), + ) + }); let ah_sender_assets_before = AssetHubWestend::execute_with(|| { type ForeignAssets = ::ForeignAssets; @@ -713,18 +761,24 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { &AssetHubWestendSender::get(), ) }); - let penpal_receiver_assets_before = PenpalB::execute_with(|| { - type Assets = ::Assets; - >::balance(asset_id_on_penpal, &PenpalBReceiver::get()) + let penpal_receiver_assets_before = PenpalA::execute_with(|| { + type Assets = ::Assets; + >::balance(asset_id_on_penpal, &PenpalAReceiver::get()) }); ah_to_penpal.set_assertion::(ah_to_penpal_foreign_assets_sender_assertions); - ah_to_penpal.set_assertion::(ah_to_penpal_foreign_assets_receiver_assertions); + ah_to_penpal.set_assertion::(ah_to_penpal_foreign_assets_receiver_assertions); ah_to_penpal.set_dispatchable::(system_para_to_para_transfer_assets); ah_to_penpal.assert(); let ah_sender_balance_after = ah_to_penpal.sender.balance; - let penpal_receiver_balance_after = ah_to_penpal.receiver.balance; + let penpal_receiver_balance_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance( + system_para_native_asset_location, + &PenpalAReceiver::get(), + ) + }); let ah_sender_assets_after = AssetHubWestend::execute_with(|| { type ForeignAssets = ::ForeignAssets; @@ -733,9 +787,9 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { &AssetHubWestendSender::get(), ) }); - let penpal_receiver_assets_after = PenpalB::execute_with(|| { - type Assets = ::Assets; - >::balance(asset_id_on_penpal, &PenpalBReceiver::get()) + let penpal_receiver_assets_after = PenpalA::execute_with(|| { + type Assets = ::Assets; + >::balance(asset_id_on_penpal, &PenpalAReceiver::get()) }); // Sender's balance is reduced diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs index 8e82059a32d17303e0d3470e70e017d6e9aa03d5..6d8c0f5e5de6ae70559f5d60545959db5e74f735 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; +use crate::imports::*; use emulated_integration_tests_common::accounts::{ALICE, BOB}; use frame_support::traits::fungibles::{Create, Inspect, Mutate}; use polkadot_runtime_common::impls::VersionedLocatableAsset; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml index 89f0d2a9ca6dacae72e73d5b6e8c310347389070..18c39f895faccd41eac017c746ba2af241a44777 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml @@ -34,10 +34,10 @@ pallet-bridge-messages = { path = "../../../../../../../bridges/modules/messages bp-messages = { path = "../../../../../../../bridges/primitives/messages", default-features = false } # Cumulus -parachains-common = { path = "../../../../../../parachains/common" } +parachains-common = { path = "../../../../../common" } testnet-parachains-constants = { path = "../../../../../runtimes/constants", features = ["rococo"] } cumulus-pallet-xcmp-queue = { path = "../../../../../../pallets/xcmp-queue", default-features = false } -bridge-hub-rococo-runtime = { path = "../../../../../../parachains/runtimes/bridge-hubs/bridge-hub-rococo", default-features = false } +bridge-hub-rococo-runtime = { path = "../../../../../runtimes/bridge-hubs/bridge-hub-rococo", default-features = false } emulated-integration-tests-common = { path = "../../../common", default-features = false } rococo-westend-system-emulated-network = { path = "../../../networks/rococo-westend-system" } rococo-system-emulated-network = { path = "../../../networks/rococo-system" } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs index 9ce981b074c5b671f7a7a4005b1b1e4ae9b1d98f..b5e19cf3fa3a252abe28b31229c876d104b6a8e9 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs @@ -13,62 +13,53 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Substrate -pub use frame_support::{assert_err, assert_ok, pallet_prelude::DispatchResult}; -pub use sp_runtime::DispatchError; - -// Polkadot -pub use xcm::{ - latest::ParentThen, - prelude::{AccountId32 as AccountId32Junction, *}, - v3::{ - self, Error, - NetworkId::{Rococo as RococoId, Westend as WestendId}, - }, -}; +#[cfg(test)] +mod imports { + // Substrate + pub use frame_support::{assert_err, assert_ok, pallet_prelude::DispatchResult}; + pub use sp_runtime::DispatchError; -// Bridges -pub use bp_messages::LaneId; + // Polkadot + pub use xcm::{ + latest::ParentThen, + prelude::{AccountId32 as AccountId32Junction, *}, + v3::{self, NetworkId::Westend as WestendId}, + }; -// Cumulus -pub use emulated_integration_tests_common::{ - accounts::ALICE, - impls::Inspect, - test_parachain_is_trusted_teleporter, - xcm_emulator::{ - assert_expected_events, bx, helpers::weight_within_threshold, Chain, Parachain as Para, - RelayChain as Relay, Test, TestArgs, TestContext, TestExt, - }, - xcm_helpers::{xcm_transact_paid_execution, xcm_transact_unpaid_execution}, - PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, XCM_V3, -}; -pub use parachains_common::{AccountId, Balance}; -pub use rococo_westend_system_emulated_network::{ - asset_hub_rococo_emulated_chain::{ - genesis::ED as ASSET_HUB_ROCOCO_ED, AssetHubRococoParaPallet as AssetHubRococoPallet, - }, - asset_hub_westend_emulated_chain::{ - genesis::ED as ASSET_HUB_WESTEND_ED, AssetHubWestendParaPallet as AssetHubWestendPallet, - }, - bridge_hub_rococo_emulated_chain::{ - genesis::ED as BRIDGE_HUB_ROCOCO_ED, BridgeHubRococoParaPallet as BridgeHubRococoPallet, - }, - penpal_emulated_chain::PenpalAParaPallet as PenpalAPallet, - rococo_emulated_chain::{genesis::ED as ROCOCO_ED, RococoRelayPallet as RococoPallet}, - AssetHubRococoPara as AssetHubRococo, AssetHubRococoParaReceiver as AssetHubRococoReceiver, - AssetHubRococoParaSender as AssetHubRococoSender, AssetHubWestendPara as AssetHubWestend, - AssetHubWestendParaReceiver as AssetHubWestendReceiver, - AssetHubWestendParaSender as AssetHubWestendSender, BridgeHubRococoPara as BridgeHubRococo, - BridgeHubRococoParaReceiver as BridgeHubRococoReceiver, - BridgeHubRococoParaSender as BridgeHubRococoSender, BridgeHubWestendPara as BridgeHubWestend, - PenpalAPara as PenpalA, PenpalAParaReceiver as PenpalAReceiver, - PenpalAParaSender as PenpalASender, RococoRelay as Rococo, - RococoRelayReceiver as RococoReceiver, RococoRelaySender as RococoSender, -}; + // Cumulus + pub use emulated_integration_tests_common::{ + accounts::ALICE, + impls::Inspect, + test_parachain_is_trusted_teleporter, + xcm_emulator::{ + assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, TestExt, + }, + }; + pub use parachains_common::AccountId; + pub use rococo_westend_system_emulated_network::{ + asset_hub_rococo_emulated_chain::{ + genesis::ED as ASSET_HUB_ROCOCO_ED, AssetHubRococoParaPallet as AssetHubRococoPallet, + }, + asset_hub_westend_emulated_chain::{ + genesis::ED as ASSET_HUB_WESTEND_ED, AssetHubWestendParaPallet as AssetHubWestendPallet, + }, + bridge_hub_rococo_emulated_chain::{ + genesis::ED as BRIDGE_HUB_ROCOCO_ED, BridgeHubRococoParaPallet as BridgeHubRococoPallet, + }, + penpal_emulated_chain::PenpalAParaPallet as PenpalAPallet, + rococo_emulated_chain::{genesis::ED as ROCOCO_ED, RococoRelayPallet as RococoPallet}, + AssetHubRococoPara as AssetHubRococo, AssetHubRococoParaReceiver as AssetHubRococoReceiver, + AssetHubRococoParaSender as AssetHubRococoSender, AssetHubWestendPara as AssetHubWestend, + AssetHubWestendParaReceiver as AssetHubWestendReceiver, + AssetHubWestendParaSender as AssetHubWestendSender, BridgeHubRococoPara as BridgeHubRococo, + BridgeHubRococoParaSender as BridgeHubRococoSender, + BridgeHubWestendPara as BridgeHubWestend, PenpalAPara as PenpalA, + PenpalAParaReceiver as PenpalAReceiver, PenpalAParaSender as PenpalASender, + RococoRelay as Rococo, + }; -pub const ASSET_ID: u32 = 1; -pub const ASSET_MIN_BALANCE: u128 = 1000; -pub const ASSETS_PALLET_ID: u8 = 50; + pub const ASSET_MIN_BALANCE: u128 = 1000; +} #[cfg(test)] mod tests; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs index 6d7b53c8fdfdb3660a4888df5dfd380bccc9d0dd..88dad06434b0d4a28295708c303907e02e70927b 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; +use crate::imports::*; mod asset_transfers; mod send_xcm; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs index a1d871cdb618fdddfbbbc3e7812d0ec7f7ae7866..4bd041dc03f4216c9eddf811d325e8262873e473 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs @@ -14,6 +14,7 @@ // limitations under the License. use crate::tests::*; +use codec::Encode; #[test] fn send_xcm_from_rococo_relay_to_westend_asset_hub_should_fail_on_not_applicable() { @@ -26,7 +27,7 @@ fn send_xcm_from_rococo_relay_to_westend_asset_hub_should_fail_on_not_applicable let remote_xcm = Xcm(vec![ClearOrigin]); - let xcm = VersionedXcm::from(Xcm(vec![ + let xcm = VersionedXcm::from(Xcm::<()>(vec![ UnpaidExecution { weight_limit, check_origin }, ExportMessage { network: WestendId.into(), @@ -38,10 +39,10 @@ fn send_xcm_from_rococo_relay_to_westend_asset_hub_should_fail_on_not_applicable // Rococo Global Consensus // Send XCM message from Relay Chain to Bridge Hub source Parachain Rococo::execute_with(|| { - assert_ok!(::XcmPallet::send( + assert_ok!(::XcmPallet::send_blob( sudo_origin, bx!(destination), - bx!(xcm), + xcm.encode().try_into().unwrap(), )); type RuntimeEvent = ::RuntimeEvent; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs index a5957ee339306f580b7906b25d3adfff4a78deb1..caaf24e00a8aea32467f62126f3fbe01d271d3ab 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs @@ -12,17 +12,18 @@ // WITHOUT 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::*; +use crate::imports::*; use bridge_hub_rococo_runtime::{EthereumBeaconClient, EthereumInboundQueue, RuntimeOrigin}; use codec::{Decode, Encode}; use emulated_integration_tests_common::xcm_emulator::ConvertLocation; use frame_support::pallet_prelude::TypeInfo; use hex_literal::hex; +use rococo_system_emulated_network::penpal_emulated_chain::CustomizableAssetFromSystemAssetHub; use rococo_westend_system_emulated_network::BridgeHubRococoParaSender as BridgeHubRococoSender; use snowbridge_core::outbound::OperatingMode; use snowbridge_pallet_inbound_queue_fixtures::{ register_token::make_register_token_message, - register_token_with_insufficient_fee::make_register_token_with_infufficient_fee_message, + register_token_with_insufficient_fee::make_register_token_with_insufficient_fee_message, send_token::make_send_token_message, send_token_to_penpal::make_send_token_to_penpal_message, InboundQueueFixture, }; @@ -82,7 +83,7 @@ fn create_agent() { let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {}); // Construct XCM to create an agent for para 1001 - let remote_xcm = VersionedXcm::from(Xcm(vec![ + let remote_xcm = VersionedXcm::from(Xcm::<()>(vec![ UnpaidExecution { weight_limit: Unlimited, check_origin: None }, DescendOrigin(Parachain(origin_para).into()), Transact { @@ -95,10 +96,10 @@ fn create_agent() { // Rococo Global Consensus // Send XCM message from Relay Chain to Bridge Hub source Parachain Rococo::execute_with(|| { - assert_ok!(::XcmPallet::send( + assert_ok!(::XcmPallet::send_blob( sudo_origin, bx!(destination), - bx!(remote_xcm), + remote_xcm.encode().try_into().unwrap(), )); type RuntimeEvent = ::RuntimeEvent; @@ -140,7 +141,7 @@ fn create_channel() { let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {}); // Construct XCM to create an agent for para 1001 - let create_agent_xcm = VersionedXcm::from(Xcm(vec![ + let create_agent_xcm = VersionedXcm::from(Xcm::<()>(vec![ UnpaidExecution { weight_limit: Unlimited, check_origin: None }, DescendOrigin(Parachain(origin_para).into()), Transact { @@ -153,7 +154,7 @@ fn create_channel() { let create_channel_call = SnowbridgeControl::Control(ControlCall::CreateChannel { mode: OperatingMode::Normal }); // Construct XCM to create a channel for para 1001 - let create_channel_xcm = VersionedXcm::from(Xcm(vec![ + let create_channel_xcm = VersionedXcm::from(Xcm::<()>(vec![ UnpaidExecution { weight_limit: Unlimited, check_origin: None }, DescendOrigin(Parachain(origin_para).into()), Transact { @@ -166,16 +167,16 @@ fn create_channel() { // Rococo Global Consensus // Send XCM message from Relay Chain to Bridge Hub source Parachain Rococo::execute_with(|| { - assert_ok!(::XcmPallet::send( + assert_ok!(::XcmPallet::send_blob( sudo_origin.clone(), bx!(destination.clone()), - bx!(create_agent_xcm), + create_agent_xcm.encode().try_into().unwrap(), )); - assert_ok!(::XcmPallet::send( + assert_ok!(::XcmPallet::send_blob( sudo_origin, bx!(destination), - bx!(create_channel_xcm), + create_channel_xcm.encode().try_into().unwrap(), )); type RuntimeEvent = ::RuntimeEvent; @@ -253,6 +254,16 @@ fn send_token_from_ethereum_to_penpal() { (PenpalASender::get(), INITIAL_FUND), ]); + PenpalA::execute_with(|| { + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![( + CustomizableAssetFromSystemAssetHub::key().to_vec(), + Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]).encode(), + )], + )); + }); + // The Weth asset location, identified by the contract address on Ethereum let weth_asset_location: Location = (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH }).into(); @@ -503,7 +514,7 @@ fn register_weth_token_in_asset_hub_fail_for_insufficient_fee() { type RuntimeEvent = ::RuntimeEvent; // Construct RegisterToken message and sent to inbound queue - let message = make_register_token_with_infufficient_fee_message(); + let message = make_register_token_with_insufficient_fee_message(); send_inbound_message(message).unwrap(); assert_expected_events!( diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/teleport.rs index 43f8af9244f5656e61d72c0352bb6e191dafb30b..8f51f5b180004d3f694cc68ba6d3c11ab46df95a 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/teleport.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; +use crate::tests::*; use bridge_hub_rococo_runtime::xcm_config::XcmConfig; #[test] diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml index 9d55903c858308b9382fda0fc03132d95b9f1028..9c45a7adeb4e5a3c112f0bb650e29e9a76a7e404 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml @@ -11,6 +11,7 @@ publish = false workspace = true [dependencies] +codec = { package = "parity-scale-codec", version = "3.6.0" } # Substrate frame-support = { path = "../../../../../../../substrate/frame/support", default-features = false } @@ -30,8 +31,8 @@ pallet-bridge-messages = { path = "../../../../../../../bridges/modules/messages bp-messages = { path = "../../../../../../../bridges/primitives/messages", default-features = false } # Cumulus -parachains-common = { path = "../../../../../../parachains/common" } +parachains-common = { path = "../../../../../common" } cumulus-pallet-xcmp-queue = { path = "../../../../../../pallets/xcmp-queue", default-features = false } -bridge-hub-westend-runtime = { path = "../../../../../../parachains/runtimes/bridge-hubs/bridge-hub-westend", default-features = false } +bridge-hub-westend-runtime = { path = "../../../../../runtimes/bridge-hubs/bridge-hub-westend", default-features = false } emulated-integration-tests-common = { path = "../../../common", default-features = false } rococo-westend-system-emulated-network = { path = "../../../networks/rococo-westend-system" } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs index 223979cc9c9d3df097014f250e2e648a8d3f0bca..60c31ce5a4aefeb5852f87ddd6804ca267857801 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs @@ -13,59 +13,52 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Substrate -pub use frame_support::{assert_err, assert_ok, pallet_prelude::DispatchResult}; -pub use sp_runtime::DispatchError; - -// Polkadot -pub use xcm::{ - latest::ParentThen, - prelude::{AccountId32 as AccountId32Junction, *}, - v3, - v4::{ - Error, - NetworkId::{Rococo as RococoId, Westend as WestendId}, - }, -}; +#[cfg(test)] +mod imports { + // Substrate + pub use frame_support::{assert_err, assert_ok, pallet_prelude::DispatchResult}; + pub use sp_runtime::DispatchError; -// Bridges -pub use bp_messages::LaneId; + // Polkadot + pub use xcm::{ + latest::ParentThen, + prelude::{AccountId32 as AccountId32Junction, *}, + v3, + v4::NetworkId::Rococo as RococoId, + }; -// Cumulus -pub use emulated_integration_tests_common::{ - accounts::ALICE, - impls::Inspect, - test_parachain_is_trusted_teleporter, - xcm_emulator::{ - assert_expected_events, bx, helpers::weight_within_threshold, Chain, Parachain as Para, - RelayChain as Relay, Test, TestArgs, TestContext, TestExt, - }, - xcm_helpers::{xcm_transact_paid_execution, xcm_transact_unpaid_execution}, - PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, XCM_V3, -}; -pub use parachains_common::{AccountId, Balance}; -pub use rococo_westend_system_emulated_network::{ - asset_hub_rococo_emulated_chain::{ - genesis::ED as ASSET_HUB_ROCOCO_ED, AssetHubRococoParaPallet as AssetHubRococoPallet, - }, - asset_hub_westend_emulated_chain::{ - genesis::ED as ASSET_HUB_WESTEND_ED, AssetHubWestendParaPallet as AssetHubWestendPallet, - }, - bridge_hub_westend_emulated_chain::{ - genesis::ED as BRIDGE_HUB_WESTEND_ED, BridgeHubWestendParaPallet as BridgeHubWestendPallet, - }, - westend_emulated_chain::WestendRelayPallet as WestendPallet, - AssetHubRococoPara as AssetHubRococo, AssetHubRococoParaReceiver as AssetHubRococoReceiver, - AssetHubRococoParaSender as AssetHubRococoSender, AssetHubWestendPara as AssetHubWestend, - AssetHubWestendParaReceiver as AssetHubWestendReceiver, - AssetHubWestendParaSender as AssetHubWestendSender, BridgeHubRococoPara as BridgeHubRococo, - BridgeHubWestendPara as BridgeHubWestend, BridgeHubWestendParaSender as BridgeHubWestendSender, - WestendRelay as Westend, -}; + // Cumulus + pub use emulated_integration_tests_common::{ + accounts::ALICE, + impls::Inspect, + test_parachain_is_trusted_teleporter, + xcm_emulator::{ + assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, TestExt, + }, + }; + pub use parachains_common::AccountId; + pub use rococo_westend_system_emulated_network::{ + asset_hub_rococo_emulated_chain::{ + genesis::ED as ASSET_HUB_ROCOCO_ED, AssetHubRococoParaPallet as AssetHubRococoPallet, + }, + asset_hub_westend_emulated_chain::{ + genesis::ED as ASSET_HUB_WESTEND_ED, AssetHubWestendParaPallet as AssetHubWestendPallet, + }, + bridge_hub_westend_emulated_chain::{ + genesis::ED as BRIDGE_HUB_WESTEND_ED, + BridgeHubWestendParaPallet as BridgeHubWestendPallet, + }, + westend_emulated_chain::WestendRelayPallet as WestendPallet, + AssetHubRococoPara as AssetHubRococo, AssetHubRococoParaReceiver as AssetHubRococoReceiver, + AssetHubRococoParaSender as AssetHubRococoSender, AssetHubWestendPara as AssetHubWestend, + AssetHubWestendParaReceiver as AssetHubWestendReceiver, + AssetHubWestendParaSender as AssetHubWestendSender, BridgeHubRococoPara as BridgeHubRococo, + BridgeHubWestendPara as BridgeHubWestend, + BridgeHubWestendParaSender as BridgeHubWestendSender, WestendRelay as Westend, + }; -pub const ASSET_ID: u32 = 1; -pub const ASSET_MIN_BALANCE: u128 = 1000; -pub const ASSETS_PALLET_ID: u8 = 50; + pub const ASSET_MIN_BALANCE: u128 = 1000; +} #[cfg(test)] mod tests; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs index 3074435e8e4e03e561388e5fb0bdf00d5b3f511f..b781d6e987ca1fc7f2a64710263f432c1cc8b3c6 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; +use crate::imports::*; mod asset_transfers; mod send_xcm; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs index b01be5e8dc84b4edf35651d0388baa1462b54c9b..f69747c17704cb47e11ec00e2d8a08a413fab0a4 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs @@ -14,6 +14,7 @@ // limitations under the License. use crate::tests::*; +use codec::Encode; #[test] fn send_xcm_from_westend_relay_to_rococo_asset_hub_should_fail_on_not_applicable() { @@ -26,7 +27,7 @@ fn send_xcm_from_westend_relay_to_rococo_asset_hub_should_fail_on_not_applicable let remote_xcm = Xcm(vec![ClearOrigin]); - let xcm = VersionedXcm::from(Xcm(vec![ + let xcm = VersionedXcm::from(Xcm::<()>(vec![ UnpaidExecution { weight_limit, check_origin }, ExportMessage { network: RococoId, @@ -38,10 +39,10 @@ fn send_xcm_from_westend_relay_to_rococo_asset_hub_should_fail_on_not_applicable // Westend Global Consensus // Send XCM message from Relay Chain to Bridge Hub source Parachain Westend::execute_with(|| { - assert_ok!(::XcmPallet::send( + assert_ok!(::XcmPallet::send_blob( sudo_origin, bx!(destination), - bx!(xcm), + xcm.encode().try_into().unwrap(), )); type RuntimeEvent = ::RuntimeEvent; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/teleport.rs index edffaf165960cc17f1703cd567019879b1de22e4..c960233c08b73df30b2f873f2ef5333ea15bf428 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/teleport.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; +use crate::tests::*; use bridge_hub_westend_runtime::xcm_config::XcmConfig; #[test] diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/Cargo.toml index 609376c1fee606c6d3d284c0be8f5c30997acfb6..1570aa7662fcbbfe1b018251d2700ce2d653d165 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/Cargo.toml @@ -26,7 +26,7 @@ polkadot-runtime-common = { path = "../../../../../../../polkadot/runtime/common # Cumulus asset-test-utils = { path = "../../../../../runtimes/assets/test-utils" } -parachains-common = { path = "../../../../../../parachains/common" } +parachains-common = { path = "../../../../../common" } people-rococo-runtime = { path = "../../../../../runtimes/people/people-rococo" } emulated-integration-tests-common = { path = "../../../common", default-features = false } rococo-system-emulated-network = { path = "../../../networks/rococo-system" } diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs index 6f2f1409135df0136b991ede4caa0ffdc53ffd63..38ff08b486d4703513ab74d681ae5f07107931ec 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs @@ -13,52 +13,41 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub use codec::Encode; +#[cfg(test)] +mod imports { + pub use codec::Encode; -// Substrate -pub use frame_support::{ - assert_err, assert_ok, - pallet_prelude::Weight, - sp_runtime::{AccountId32, DispatchError, DispatchResult}, - traits::fungibles::Inspect, -}; + // Substrate + pub use frame_support::{ + assert_ok, + pallet_prelude::Weight, + sp_runtime::{AccountId32, DispatchResult}, + traits::fungibles::Inspect, + }; -// Polkadot -pub use xcm::{ - prelude::{AccountId32 as AccountId32Junction, *}, - v3::{Error, NetworkId::Rococo as RococoId}, -}; + // Polkadot + pub use xcm::prelude::*; -// Cumulus -pub use asset_test_utils::xcm_helpers; -pub use emulated_integration_tests_common::{ - test_parachain_is_trusted_teleporter, - xcm_emulator::{ - assert_expected_events, bx, helpers::weight_within_threshold, Chain, Parachain as Para, - RelayChain as Relay, Test, TestArgs, TestContext, TestExt, - }, - xcm_helpers::{xcm_transact_paid_execution, xcm_transact_unpaid_execution}, - PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, XCM_V3, -}; -pub use parachains_common::{AccountId, Balance}; -pub use rococo_system_emulated_network::{ - people_rococo_emulated_chain::{ - genesis::ED as PEOPLE_ROCOCO_ED, PeopleRococoParaPallet as PeopleRococoPallet, - }, - rococo_emulated_chain::{genesis::ED as ROCOCO_ED, RococoRelayPallet as RococoPallet}, - PenpalAPara as PenpalA, PeopleRococoPara as PeopleRococo, - PeopleRococoParaReceiver as PeopleRococoReceiver, PeopleRococoParaSender as PeopleRococoSender, - RococoRelay as Rococo, RococoRelayReceiver as RococoReceiver, - RococoRelaySender as RococoSender, -}; + // Cumulus + pub use asset_test_utils::xcm_helpers; + pub use emulated_integration_tests_common::xcm_emulator::{ + assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test, TestArgs, + TestContext, TestExt, + }; + pub use parachains_common::Balance; + pub use rococo_system_emulated_network::{ + people_rococo_emulated_chain::{ + genesis::ED as PEOPLE_ROCOCO_ED, PeopleRococoParaPallet as PeopleRococoPallet, + }, + rococo_emulated_chain::{genesis::ED as ROCOCO_ED, RococoRelayPallet as RococoPallet}, + PeopleRococoPara as PeopleRococo, PeopleRococoParaReceiver as PeopleRococoReceiver, + PeopleRococoParaSender as PeopleRococoSender, RococoRelay as Rococo, + RococoRelayReceiver as RococoReceiver, RococoRelaySender as RococoSender, + }; -// pub const ASSET_ID: u32 = 1; -// pub const ASSET_MIN_BALANCE: u128 = 1000; -pub type RelayToSystemParaTest = Test; -pub type RelayToParaTest = Test; -pub type SystemParaToRelayTest = Test; -pub type SystemParaToParaTest = Test; -pub type ParaToSystemParaTest = Test; + pub type RelayToSystemParaTest = Test; + pub type SystemParaToRelayTest = Test; +} #[cfg(test)] mod tests; diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/reap_identity.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/reap_identity.rs index 87adb363e022b3c21fdfca052bb7a3019658760c..3f1f8638d6fa1491288cd6bbd60f08c13af566dd 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/reap_identity.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/reap_identity.rs @@ -38,7 +38,7 @@ //! - The freed deposit from the Relay Chain is sufficient for the parachain deposit; and //! - The account will exist on the parachain. -use crate::*; +use crate::imports::*; use frame_support::BoundedVec; use pallet_balances::Event as BalancesEvent; use pallet_identity::{legacy::IdentityInfo, Data, Event as IdentityEvent}; diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs index 0a12277395d739b032d8de032dcdf9f405eaf158..350d87d638ab25bd66a411a67fa3f109bdaffff9 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; +use crate::imports::*; use people_rococo_runtime::xcm_config::XcmConfig as PeopleRococoXcmConfig; use rococo_runtime::xcm_config::XcmConfig as RococoXcmConfig; @@ -155,7 +155,7 @@ fn limited_teleport_native_assets_from_relay_to_system_para_works() { test.assert(); let delivery_fees = Rococo::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< + xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) }); @@ -204,7 +204,7 @@ fn limited_teleport_native_assets_back_from_system_para_to_relay_works() { let receiver_balance_after = test.receiver.balance; let delivery_fees = PeopleRococo::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< + xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) }); @@ -248,7 +248,7 @@ fn limited_teleport_native_assets_from_system_para_to_relay_fails() { let receiver_balance_after = test.receiver.balance; let delivery_fees = PeopleRococo::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< + xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) }); diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/Cargo.toml index f2f3366798a0aacc77fe279efd1b47032c62c7dc..bc093dc0de6356d7cd98d20d12d63748ed248ff5 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/Cargo.toml @@ -26,7 +26,7 @@ polkadot-runtime-common = { path = "../../../../../../../polkadot/runtime/common # Cumulus asset-test-utils = { path = "../../../../../runtimes/assets/test-utils" } -parachains-common = { path = "../../../../../../parachains/common" } +parachains-common = { path = "../../../../../common" } people-westend-runtime = { path = "../../../../../runtimes/people/people-westend" } emulated-integration-tests-common = { path = "../../../common", default-features = false } westend-system-emulated-network = { path = "../../../networks/westend-system" } diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs index 59cec36030b236135af7898ed87f6548cc12cbd4..77ac7cfc78c78c6f51ea174b2436d05ee612e0b7 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs @@ -13,52 +13,40 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub use codec::Encode; - -// Substrate -pub use frame_support::{ - assert_err, assert_ok, - pallet_prelude::Weight, - sp_runtime::{AccountId32, DispatchError, DispatchResult}, - traits::fungibles::Inspect, -}; +#[cfg(test)] +mod imports { + pub use codec::Encode; + // Substrate + pub use frame_support::{ + assert_ok, + pallet_prelude::Weight, + sp_runtime::{AccountId32, DispatchResult}, + traits::fungibles::Inspect, + }; -// Polkadot -pub use xcm::{ - prelude::{AccountId32 as AccountId32Junction, *}, - v3::{Error, NetworkId::Westend as WestendId}, -}; + // Polkadot + pub use xcm::prelude::*; -// Cumulus -pub use asset_test_utils::xcm_helpers; -pub use emulated_integration_tests_common::{ - test_parachain_is_trusted_teleporter, - xcm_emulator::{ - assert_expected_events, bx, helpers::weight_within_threshold, Chain, Parachain as Para, - RelayChain as Relay, Test, TestArgs, TestContext, TestExt, - }, - xcm_helpers::{xcm_transact_paid_execution, xcm_transact_unpaid_execution}, - PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, XCM_V3, -}; -pub use parachains_common::{AccountId, Balance}; -pub use westend_system_emulated_network::{ - people_westend_emulated_chain::{ - genesis::ED as PEOPLE_WESTEND_ED, PeopleWestendParaPallet as PeopleWestendPallet, - }, - westend_emulated_chain::{genesis::ED as WESTEND_ED, WestendRelayPallet as WestendPallet}, - PenpalAPara as PenpalA, PeopleWestendPara as PeopleWestend, - PeopleWestendParaReceiver as PeopleWestendReceiver, - PeopleWestendParaSender as PeopleWestendSender, WestendRelay as Westend, - WestendRelayReceiver as WestendReceiver, WestendRelaySender as WestendSender, -}; + // Cumulus + pub use asset_test_utils::xcm_helpers; + pub use emulated_integration_tests_common::xcm_emulator::{ + assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test, TestArgs, + TestContext, TestExt, + }; + pub use parachains_common::Balance; + pub use westend_system_emulated_network::{ + people_westend_emulated_chain::{ + genesis::ED as PEOPLE_WESTEND_ED, PeopleWestendParaPallet as PeopleWestendPallet, + }, + westend_emulated_chain::{genesis::ED as WESTEND_ED, WestendRelayPallet as WestendPallet}, + PeopleWestendPara as PeopleWestend, PeopleWestendParaReceiver as PeopleWestendReceiver, + PeopleWestendParaSender as PeopleWestendSender, WestendRelay as Westend, + WestendRelayReceiver as WestendReceiver, WestendRelaySender as WestendSender, + }; -// pub const ASSET_ID: u32 = 1; -// pub const ASSET_MIN_BALANCE: u128 = 1000; -pub type RelayToSystemParaTest = Test; -pub type RelayToParaTest = Test; -pub type SystemParaToRelayTest = Test; -pub type SystemParaToParaTest = Test; -pub type ParaToSystemParaTest = Test; + pub type RelayToSystemParaTest = Test; + pub type SystemParaToRelayTest = Test; +} #[cfg(test)] mod tests; diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/reap_identity.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/reap_identity.rs index 8d63c8ceff6efea9825cb0d009da74d2974ba40b..3ed8592918d65b81737a4e8206a3bc23b3684b5f 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/reap_identity.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/reap_identity.rs @@ -38,7 +38,7 @@ //! - The freed deposit from the Relay Chain is sufficient for the parachain deposit; and //! - The account will exist on the parachain. -use crate::*; +use crate::imports::*; use frame_support::BoundedVec; use pallet_balances::Event as BalancesEvent; use pallet_identity::{legacy::IdentityInfo, Data, Event as IdentityEvent}; diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs index 345663be99baa66fc3e871abc43695f8c681308f..8697477ba769329755b40a87132c62b213861cc4 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; +use crate::imports::*; use people_westend_runtime::xcm_config::XcmConfig as PeopleWestendXcmConfig; use westend_runtime::xcm_config::XcmConfig as WestendXcmConfig; @@ -155,7 +155,7 @@ fn limited_teleport_native_assets_from_relay_to_system_para_works() { test.assert(); let delivery_fees = Westend::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< + xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) }); @@ -204,7 +204,7 @@ fn limited_teleport_native_assets_back_from_system_para_to_relay_works() { let receiver_balance_after = test.receiver.balance; let delivery_fees = PeopleWestend::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< + xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) }); @@ -248,7 +248,7 @@ fn limited_teleport_native_assets_from_system_para_to_relay_fails() { let receiver_balance_after = test.receiver.balance; let delivery_fees = PeopleWestend::execute_with(|| { - xcm_helpers::transfer_assets_delivery_fees::< + xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) }); diff --git a/cumulus/parachains/pallets/collective-content/Cargo.toml b/cumulus/parachains/pallets/collective-content/Cargo.toml index 691be02f5b8e390bf150713bcf127af1e2ce44b0..d4290dd2de2460d82d7944a926a0d603c198280e 100644 --- a/cumulus/parachains/pallets/collective-content/Cargo.toml +++ b/cumulus/parachains/pallets/collective-content/Cargo.toml @@ -13,7 +13,7 @@ workspace = true 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"] } -frame-benchmarking = { path = "../../../../substrate/frame/benchmarking", optional = true, default-features = false } +frame-benchmarking = { path = "../../../../substrate/frame/benchmarking", default-features = false, optional = true } frame-support = { path = "../../../../substrate/frame/support", default-features = false } frame-system = { path = "../../../../substrate/frame/system", default-features = false } diff --git a/cumulus/parachains/pallets/collective-content/src/benchmarking.rs b/cumulus/parachains/pallets/collective-content/src/benchmarking.rs index 943386a842766129c2b5d429dc3a9648249ea73f..3d6bf073778a294e39287285ddb8379d57d36c8f 100644 --- a/cumulus/parachains/pallets/collective-content/src/benchmarking.rs +++ b/cumulus/parachains/pallets/collective-content/src/benchmarking.rs @@ -16,7 +16,7 @@ //! The pallet benchmarks. use super::{Pallet as CollectiveContent, *}; -use frame_benchmarking::{impl_benchmark_test_suite, v2::*}; +use frame_benchmarking::v2::*; use frame_support::traits::EnsureOrigin; fn assert_last_event, I: 'static>(generic_event: >::RuntimeEvent) { diff --git a/cumulus/parachains/pallets/collective-content/src/lib.rs b/cumulus/parachains/pallets/collective-content/src/lib.rs index 7a685858accb67868837e734ba9808741a4f7ea1..b1c960ad6a0d337d6b84aaaba59f7fa0625a8124 100644 --- a/cumulus/parachains/pallets/collective-content/src/lib.rs +++ b/cumulus/parachains/pallets/collective-content/src/lib.rs @@ -59,7 +59,7 @@ pub mod pallet { use frame_system::pallet_prelude::*; use sp_runtime::{traits::BadOrigin, Saturating}; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); #[pallet::pallet] diff --git a/cumulus/parachains/pallets/collective-content/src/mock.rs b/cumulus/parachains/pallets/collective-content/src/mock.rs index 7a752da71fceff211679e4b87428198c52f87e5d..5cb0126425e59a67332d880cc8b925f50b1753c2 100644 --- a/cumulus/parachains/pallets/collective-content/src/mock.rs +++ b/cumulus/parachains/pallets/collective-content/src/mock.rs @@ -55,7 +55,7 @@ impl pallet_collective_content::Config for Test { type WeightInfo = CCWeightInfo; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = (); type BlockWeights = (); diff --git a/cumulus/parachains/pallets/parachain-info/src/lib.rs b/cumulus/parachains/pallets/parachain-info/src/lib.rs index c17a6d5e1460a2ef319109c0d867b6c73a6493e0..a4ef448a6b6b9b934e9e49a4a4797a590ad55d6b 100644 --- a/cumulus/parachains/pallets/parachain-info/src/lib.rs +++ b/cumulus/parachains/pallets/parachain-info/src/lib.rs @@ -54,7 +54,7 @@ pub mod pallet { #[pallet::genesis_build] impl BuildGenesisConfig for GenesisConfig { fn build(&self) { - >::put(self.parachain_id); + ParachainId::::put(self.parachain_id); } } @@ -64,13 +64,18 @@ pub mod pallet { } #[pallet::storage] - #[pallet::getter(fn parachain_id)] pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery, DefaultForParachainId>; impl Get for Pallet { fn get() -> ParaId { - Self::parachain_id() + ParachainId::::get() + } + } + + impl Pallet { + pub fn parachain_id() -> ParaId { + ParachainId::::get() } } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index 05936e93993231429d738edf15acc48150bcc542..53abb620022fca617b79aa6608c798780dab89c2 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -86,10 +86,10 @@ assets-common = { path = "../common", default-features = false } # Bridges pallet-xcm-bridge-hub-router = { path = "../../../../../bridges/modules/xcm-bridge-hub-router", default-features = false } -bp-asset-hub-rococo = { path = "../../../../../bridges/primitives/chain-asset-hub-rococo", default-features = false } -bp-asset-hub-westend = { path = "../../../../../bridges/primitives/chain-asset-hub-westend", default-features = false } -bp-bridge-hub-rococo = { path = "../../../../../bridges/primitives/chain-bridge-hub-rococo", default-features = false } -bp-bridge-hub-westend = { path = "../../../../../bridges/primitives/chain-bridge-hub-westend", default-features = false } +bp-asset-hub-rococo = { path = "../../../../../bridges/chains/chain-asset-hub-rococo", default-features = false } +bp-asset-hub-westend = { path = "../../../../../bridges/chains/chain-asset-hub-westend", default-features = false } +bp-bridge-hub-rococo = { path = "../../../../../bridges/chains/chain-bridge-hub-rococo", default-features = false } +bp-bridge-hub-westend = { path = "../../../../../bridges/chains/chain-bridge-hub-westend", default-features = false } snowbridge-router-primitives = { path = "../../../../../bridges/snowbridge/primitives/router", default-features = false } [dev-dependencies] @@ -251,8 +251,6 @@ std = [ "xcm/std", ] -experimental = ["pallet-aura/experimental"] - # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 7e550c329177cea5ceecfad3280f0430ab6245e3..689d8d56c48ba9fd4bbb129a7c1d0f8eec5ec426 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -113,7 +113,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("statemine"), impl_name: create_runtime_str!("statemine"), authoring_version: 1, - spec_version: 1_007_000, + spec_version: 1_009_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 14, @@ -126,7 +126,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("statemine"), impl_name: create_runtime_str!("statemine"), authoring_version: 1, - spec_version: 1_007_000, + spec_version: 1_009_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 14, @@ -165,7 +165,7 @@ parameter_types! { } // Configure FRAME pallets to include in runtime. -#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig)] impl frame_system::Config for Runtime { type BlockWeights = RuntimeBlockWeights; type BlockLength = RuntimeBlockLength; @@ -187,10 +187,7 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; - #[cfg(feature = "experimental")] type MinimumPeriod = ConstU64<0>; - #[cfg(not(feature = "experimental"))] - type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = weights::pallet_timestamp::WeightInfo; } @@ -723,7 +720,6 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; type AllowMultipleBlocksPerSlot = ConstBool; - #[cfg(feature = "experimental")] type SlotDuration = ConstU64; } @@ -986,37 +982,37 @@ impl frame_support::traits::OnRuntimeUpgrade for InitStorageVersions { let mut writes = 0; if PolkadotXcm::on_chain_storage_version() == StorageVersion::new(0) { - PolkadotXcm::current_storage_version().put::(); + PolkadotXcm::in_code_storage_version().put::(); writes.saturating_inc(); } if Multisig::on_chain_storage_version() == StorageVersion::new(0) { - Multisig::current_storage_version().put::(); + Multisig::in_code_storage_version().put::(); writes.saturating_inc(); } if Assets::on_chain_storage_version() == StorageVersion::new(0) { - Assets::current_storage_version().put::(); + Assets::in_code_storage_version().put::(); writes.saturating_inc(); } if Uniques::on_chain_storage_version() == StorageVersion::new(0) { - Uniques::current_storage_version().put::(); + Uniques::in_code_storage_version().put::(); writes.saturating_inc(); } if Nfts::on_chain_storage_version() == StorageVersion::new(0) { - Nfts::current_storage_version().put::(); + Nfts::in_code_storage_version().put::(); writes.saturating_inc(); } if ForeignAssets::on_chain_storage_version() == StorageVersion::new(0) { - ForeignAssets::current_storage_version().put::(); + ForeignAssets::in_code_storage_version().put::(); writes.saturating_inc(); } if PoolAssets::on_chain_storage_version() == StorageVersion::new(0) { - PoolAssets::current_storage_version().put::(); + PoolAssets::in_code_storage_version().put::(); writes.saturating_inc(); } @@ -1071,7 +1067,7 @@ impl_runtime_apis! { } fn authorities() -> Vec { - Aura::authorities().into_inner() + pallet_aura::Authorities::::get().into_inner() } } @@ -1093,7 +1089,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -1355,8 +1351,31 @@ impl_runtime_apis! { Config as XcmBridgeHubRouterConfig, }; + parameter_types! { + pub ExistentialDepositAsset: Option = Some(( + TokenLocation::get(), + ExistentialDeposit::get() + ).into()); + pub const RandomParaId: ParaId = ParaId::new(43211234); + } + use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = ( + cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >, + polkadot_runtime_common::xcm_sender::ToParachainDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + PriceForSiblingParachainDelivery, + RandomParaId, + ParachainSystem, + > + ); + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -1365,7 +1384,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between AH and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, Parent.into(), @@ -1373,17 +1392,13 @@ impl_runtime_apis! { } fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> { - // AH can reserve transfer native token to some random parachain. - let random_para_id = 43211234; - ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests( - random_para_id.into() - ); Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, - ParentThen(Parachain(random_para_id).into()).into(), + // AH can reserve transfer native token to some random parachain. + ParentThen(Parachain(RandomParaId::get().into()).into()).into(), )) } @@ -1435,6 +1450,13 @@ impl_runtime_apis! { }); Some((assets, fee_index as u32, dest, verify)) } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(ExistentialDeposit::get()), + } + } } impl XcmBridgeHubRouterConfig for Runtime { @@ -1469,13 +1491,6 @@ impl_runtime_apis! { use xcm_config::{TokenLocation, MaxAssetsIntoHolding}; use pallet_xcm_benchmarks::asset_instance_from; - parameter_types! { - pub ExistentialDepositAsset: Option = Some(( - TokenLocation::get(), - ExistentialDeposit::get() - ).into()); - } - impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; type AccountIdConverter = xcm_config::LocationToAccountId; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm.rs index f8820bbb58cb24afe1afe034e131414368089444..e0e231d7da279022293d42aa5e3b3fc1b8ad12d5 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-12-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-r43aesjn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -64,8 +64,30 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 25_003_000 picoseconds. - Weight::from_parts(25_800_000, 0) + // Minimum execution time: 21_224_000 picoseconds. + Weight::from_parts(21_821_000, 0) + .saturating_add(Weight::from_parts(0, 3610)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `3610` + // Minimum execution time: 21_474_000 picoseconds. + Weight::from_parts(22_072_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -90,8 +112,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 88_832_000 picoseconds. - Weight::from_parts(90_491_000, 0) + // Minimum execution time: 90_677_000 picoseconds. + Weight::from_parts(93_658_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -118,8 +140,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `400` // Estimated: `6196` - // Minimum execution time: 138_911_000 picoseconds. - Weight::from_parts(142_483_000, 0) + // Minimum execution time: 116_767_000 picoseconds. + Weight::from_parts(118_843_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(5)) @@ -148,8 +170,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `496` // Estimated: `6208` - // Minimum execution time: 146_932_000 picoseconds. - Weight::from_parts(153_200_000, 0) + // Minimum execution time: 137_983_000 picoseconds. + Weight::from_parts(141_396_000, 0) .saturating_add(Weight::from_parts(0, 6208)) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(7)) @@ -164,14 +186,24 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_081_000 picoseconds. - Weight::from_parts(7_397_000, 0) + // Minimum execution time: 6_232_000 picoseconds. + Weight::from_parts(6_507_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -181,8 +213,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_007_000 picoseconds. - Weight::from_parts(2_183_000, 0) + // Minimum execution time: 1_884_000 picoseconds. + Weight::from_parts(2_016_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -208,8 +240,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 28_790_000 picoseconds. - Weight::from_parts(29_767_000, 0) + // Minimum execution time: 26_637_000 picoseconds. + Weight::from_parts(27_616_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) @@ -234,8 +266,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `363` // Estimated: `3828` - // Minimum execution time: 30_951_000 picoseconds. - Weight::from_parts(31_804_000, 0) + // Minimum execution time: 28_668_000 picoseconds. + Weight::from_parts(29_413_000, 0) .saturating_add(Weight::from_parts(0, 3828)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -246,45 +278,45 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_164_000 picoseconds. - Weight::from_parts(2_311_000, 0) + // Minimum execution time: 1_990_000 picoseconds. + Weight::from_parts(2_114_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `PolkadotXcm::SupportedVersion` (r:4 w:2) + /// Storage: `PolkadotXcm::SupportedVersion` (r:5 w:2) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `162` - // Estimated: `11052` - // Minimum execution time: 16_906_000 picoseconds. - Weight::from_parts(17_612_000, 0) - .saturating_add(Weight::from_parts(0, 11052)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `159` + // Estimated: `13524` + // Minimum execution time: 18_856_000 picoseconds. + Weight::from_parts(19_430_000, 0) + .saturating_add(Weight::from_parts(0, 13524)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifiers` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `166` - // Estimated: `11056` - // Minimum execution time: 17_443_000 picoseconds. - Weight::from_parts(18_032_000, 0) - .saturating_add(Weight::from_parts(0, 11056)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `163` + // Estimated: `13528` + // Minimum execution time: 19_068_000 picoseconds. + Weight::from_parts(19_434_000, 0) + .saturating_add(Weight::from_parts(0, 13528)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:6 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `173` - // Estimated: `13538` - // Minimum execution time: 18_992_000 picoseconds. - Weight::from_parts(19_464_000, 0) - .saturating_add(Weight::from_parts(0, 13538)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `16013` + // Minimum execution time: 21_055_000 picoseconds. + Weight::from_parts(21_379_000, 0) + .saturating_add(Weight::from_parts(0, 16013)) + .saturating_add(T::DbWeight::get().reads(6)) } /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:2 w:1) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -304,36 +336,36 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `212` // Estimated: `6152` - // Minimum execution time: 28_011_000 picoseconds. - Weight::from_parts(28_716_000, 0) + // Minimum execution time: 25_736_000 picoseconds. + Weight::from_parts(26_423_000, 0) .saturating_add(Weight::from_parts(0, 6152)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:3 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `206` - // Estimated: `8621` - // Minimum execution time: 9_533_000 picoseconds. - Weight::from_parts(9_856_000, 0) - .saturating_add(Weight::from_parts(0, 8621)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `11096` + // Minimum execution time: 11_853_000 picoseconds. + Weight::from_parts(12_215_000, 0) + .saturating_add(Weight::from_parts(0, 11096)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `173` - // Estimated: `11063` - // Minimum execution time: 17_628_000 picoseconds. - Weight::from_parts(18_146_000, 0) - .saturating_add(Weight::from_parts(0, 11063)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `170` + // Estimated: `13535` + // Minimum execution time: 19_418_000 picoseconds. + Weight::from_parts(19_794_000, 0) + .saturating_add(Weight::from_parts(0, 13535)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -349,12 +381,12 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `215` - // Estimated: `11105` - // Minimum execution time: 34_877_000 picoseconds. - Weight::from_parts(35_607_000, 0) - .saturating_add(Weight::from_parts(0, 11105)) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `212` + // Estimated: `13577` + // Minimum execution time: 34_719_000 picoseconds. + Weight::from_parts(35_260_000, 0) + .saturating_add(Weight::from_parts(0, 13577)) + .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) @@ -365,8 +397,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `1588` - // Minimum execution time: 5_370_000 picoseconds. - Weight::from_parts(5_616_000, 0) + // Minimum execution time: 4_937_000 picoseconds. + Weight::from_parts(5_203_000, 0) .saturating_add(Weight::from_parts(0, 1588)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -377,10 +409,22 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7740` // Estimated: `11205` - // Minimum execution time: 26_820_000 picoseconds. - Weight::from_parts(27_143_000, 0) + // Minimum execution time: 26_064_000 picoseconds. + Weight::from_parts(26_497_000, 0) .saturating_add(Weight::from_parts(0, 11205)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `160` + // Estimated: `3625` + // Minimum execution time: 37_132_000 picoseconds. + Weight::from_parts(37_868_000, 0) + .saturating_add(Weight::from_parts(0, 3625)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index 2584dbdf31062f4c75f03714ce803d234ced0be9..47c3ed3688887f2e240f91cdf0003230f4209482 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -192,7 +192,7 @@ pub type ForeignFungiblesTransactor = FungiblesAdapter< LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): AccountId, - // We dont need to check teleports here. + // We don't need to check teleports here. NoChecking, // The account to use for tracking teleports. CheckingAccount, @@ -628,6 +628,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } /// Converts a local signed origin into an XCM location. diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs index 38c118dbb4b25fe2e345e9247a2a253d0becd613..5fa7455ad2a0b5620a6f6934b87acfe3b85d4f57 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs @@ -527,12 +527,12 @@ fn test_foreign_asset_xcm_take_first_trader() { let bought = Weight::from_parts(4_000_000_000u64, 0); // Lets calculate amount needed - let asset_amount_needed = - ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles( + let asset_amount_needed + = ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles( foreign_location, - bought, + bought ) - .expect("failed to compute"); + .expect("failed to compute"); // Lets pay with: asset_amount_needed + asset_amount_extra let asset_amount_extra = 100_u128; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index 78c48507a7a44efadd39ab7729f9cadfe681cf8a..0f8a1182cd719ab7b9a47bced95ee3919a3b3d45 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -84,10 +84,10 @@ assets-common = { path = "../common", default-features = false } # Bridges pallet-xcm-bridge-hub-router = { path = "../../../../../bridges/modules/xcm-bridge-hub-router", default-features = false } -bp-asset-hub-rococo = { path = "../../../../../bridges/primitives/chain-asset-hub-rococo", default-features = false } -bp-asset-hub-westend = { path = "../../../../../bridges/primitives/chain-asset-hub-westend", default-features = false } -bp-bridge-hub-rococo = { path = "../../../../../bridges/primitives/chain-bridge-hub-rococo", default-features = false } -bp-bridge-hub-westend = { path = "../../../../../bridges/primitives/chain-bridge-hub-westend", default-features = false } +bp-asset-hub-rococo = { path = "../../../../../bridges/chains/chain-asset-hub-rococo", default-features = false } +bp-asset-hub-westend = { path = "../../../../../bridges/chains/chain-asset-hub-westend", default-features = false } +bp-bridge-hub-rococo = { path = "../../../../../bridges/chains/chain-bridge-hub-rococo", default-features = false } +bp-bridge-hub-westend = { path = "../../../../../bridges/chains/chain-bridge-hub-westend", default-features = false } [dev-dependencies] hex-literal = "0.4.1" @@ -236,8 +236,6 @@ std = [ "xcm/std", ] -experimental = ["pallet-aura/experimental"] - # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index a3166106073e50cb22843c4300920559b02df82a..48106b5f302d45bc7d4af104f9bb47e51854ab76 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -110,7 +110,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("westmint"), impl_name: create_runtime_str!("westmint"), authoring_version: 1, - spec_version: 1_007_000, + spec_version: 1_009_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 14, @@ -149,7 +149,7 @@ parameter_types! { } // Configure FRAME pallets to include in runtime. -#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig)] impl frame_system::Config for Runtime { type BlockWeights = RuntimeBlockWeights; type BlockLength = RuntimeBlockLength; @@ -171,10 +171,7 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; - #[cfg(feature = "experimental")] type MinimumPeriod = ConstU64<0>; - #[cfg(not(feature = "experimental"))] - type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = weights::pallet_timestamp::WeightInfo; } @@ -703,7 +700,6 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; type AllowMultipleBlocksPerSlot = ConstBool; - #[cfg(feature = "experimental")] type SlotDuration = ConstU64; } @@ -1037,17 +1033,17 @@ impl frame_support::traits::OnRuntimeUpgrade for InitStorageVersions { let mut writes = 0; if PolkadotXcm::on_chain_storage_version() == StorageVersion::new(0) { - PolkadotXcm::current_storage_version().put::(); + PolkadotXcm::in_code_storage_version().put::(); writes.saturating_inc(); } if ForeignAssets::on_chain_storage_version() == StorageVersion::new(0) { - ForeignAssets::current_storage_version().put::(); + ForeignAssets::in_code_storage_version().put::(); writes.saturating_inc(); } if PoolAssets::on_chain_storage_version() == StorageVersion::new(0) { - PoolAssets::current_storage_version().put::(); + PoolAssets::in_code_storage_version().put::(); writes.saturating_inc(); } @@ -1102,7 +1098,7 @@ impl_runtime_apis! { } fn authorities() -> Vec { - Aura::authorities().into_inner() + pallet_aura::Authorities::::get().into_inner() } } @@ -1124,7 +1120,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -1427,8 +1423,31 @@ impl_runtime_apis! { use cumulus_pallet_session_benchmarking::Pallet as SessionBench; impl cumulus_pallet_session_benchmarking::Config for Runtime {} + parameter_types! { + pub ExistentialDepositAsset: Option = Some(( + WestendLocation::get(), + ExistentialDeposit::get() + ).into()); + pub const RandomParaId: ParaId = ParaId::new(43211234); + } + use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = ( + cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >, + polkadot_runtime_common::xcm_sender::ToParachainDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + PriceForSiblingParachainDelivery, + RandomParaId, + ParachainSystem, + > + ); + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -1437,7 +1456,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between AH and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, Parent.into(), @@ -1445,17 +1464,13 @@ impl_runtime_apis! { } fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> { - // AH can reserve transfer native token to some random parachain. - let random_para_id = 43211234; - ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests( - random_para_id.into() - ); Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, - ParentThen(Parachain(random_para_id).into()).into(), + // AH can reserve transfer native token to some random parachain. + ParentThen(Parachain(RandomParaId::get().into()).into()).into(), )) } @@ -1507,6 +1522,13 @@ impl_runtime_apis! { }); Some((assets, fee_index as u32, dest, verify)) } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(ExistentialDeposit::get()), + } + } } use pallet_xcm_bridge_hub_router::benchmarking::{ @@ -1546,13 +1568,6 @@ impl_runtime_apis! { use xcm_config::{MaxAssetsIntoHolding, WestendLocation}; use pallet_xcm_benchmarks::asset_instance_from; - parameter_types! { - pub ExistentialDepositAsset: Option = Some(( - WestendLocation::get(), - ExistentialDeposit::get() - ).into()); - } - impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; type AccountIdConverter = xcm_config::LocationToAccountId; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm.rs index 504731f4a9ef743e62090582901a63f7aee78829..299e4b8b3cd17516e49883112dc322fd9dff4c1e 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-12-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-r43aesjn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -64,8 +64,30 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 25_482_000 picoseconds. - Weight::from_parts(26_622_000, 0) + // Minimum execution time: 21_722_000 picoseconds. + Weight::from_parts(22_253_000, 0) + .saturating_add(Weight::from_parts(0, 3610)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `3610` + // Minimum execution time: 21_694_000 picoseconds. + Weight::from_parts(22_326_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -90,8 +112,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 87_319_000 picoseconds. - Weight::from_parts(89_764_000, 0) + // Minimum execution time: 94_422_000 picoseconds. + Weight::from_parts(96_997_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -118,8 +140,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `367` // Estimated: `6196` - // Minimum execution time: 139_133_000 picoseconds. - Weight::from_parts(141_507_000, 0) + // Minimum execution time: 123_368_000 picoseconds. + Weight::from_parts(125_798_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(5)) @@ -148,8 +170,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `496` // Estimated: `6208` - // Minimum execution time: 144_241_000 picoseconds. - Weight::from_parts(149_709_000, 0) + // Minimum execution time: 142_033_000 picoseconds. + Weight::from_parts(145_702_000, 0) .saturating_add(Weight::from_parts(0, 6208)) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(7)) @@ -158,8 +180,16 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_392_000 picoseconds. - Weight::from_parts(10_779_000, 0) + // Minimum execution time: 7_558_000 picoseconds. + Weight::from_parts(7_916_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_978_000 picoseconds. + Weight::from_parts(8_210_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) @@ -168,8 +198,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_088_000 picoseconds. - Weight::from_parts(7_257_000, 0) + // Minimum execution time: 6_439_000 picoseconds. + Weight::from_parts(6_711_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -179,8 +209,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_095_000 picoseconds. - Weight::from_parts(2_136_000, 0) + // Minimum execution time: 1_982_000 picoseconds. + Weight::from_parts(2_260_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -206,8 +236,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 28_728_000 picoseconds. - Weight::from_parts(29_349_000, 0) + // Minimum execution time: 27_120_000 picoseconds. + Weight::from_parts(28_048_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) @@ -232,8 +262,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `363` // Estimated: `3828` - // Minimum execution time: 30_605_000 picoseconds. - Weight::from_parts(31_477_000, 0) + // Minimum execution time: 29_354_000 picoseconds. + Weight::from_parts(30_205_000, 0) .saturating_add(Weight::from_parts(0, 3828)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -244,45 +274,45 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_137_000 picoseconds. - Weight::from_parts(2_303_000, 0) + // Minimum execution time: 1_926_000 picoseconds. + Weight::from_parts(2_013_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `PolkadotXcm::SupportedVersion` (r:4 w:2) + /// Storage: `PolkadotXcm::SupportedVersion` (r:5 w:2) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `162` - // Estimated: `11052` - // Minimum execution time: 16_719_000 picoseconds. - Weight::from_parts(17_329_000, 0) - .saturating_add(Weight::from_parts(0, 11052)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `159` + // Estimated: `13524` + // Minimum execution time: 18_611_000 picoseconds. + Weight::from_parts(19_120_000, 0) + .saturating_add(Weight::from_parts(0, 13524)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifiers` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `166` - // Estimated: `11056` - // Minimum execution time: 16_687_000 picoseconds. - Weight::from_parts(17_405_000, 0) - .saturating_add(Weight::from_parts(0, 11056)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `163` + // Estimated: `13528` + // Minimum execution time: 18_373_000 picoseconds. + Weight::from_parts(18_945_000, 0) + .saturating_add(Weight::from_parts(0, 13528)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:6 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `173` - // Estimated: `13538` - // Minimum execution time: 18_751_000 picoseconds. - Weight::from_parts(19_130_000, 0) - .saturating_add(Weight::from_parts(0, 13538)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `16013` + // Minimum execution time: 20_459_000 picoseconds. + Weight::from_parts(20_951_000, 0) + .saturating_add(Weight::from_parts(0, 16013)) + .saturating_add(T::DbWeight::get().reads(6)) } /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:2 w:1) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -302,36 +332,36 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `212` // Estimated: `6152` - // Minimum execution time: 27_189_000 picoseconds. - Weight::from_parts(27_760_000, 0) + // Minimum execution time: 26_003_000 picoseconds. + Weight::from_parts(26_678_000, 0) .saturating_add(Weight::from_parts(0, 6152)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:3 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `206` - // Estimated: `8621` - // Minimum execution time: 9_307_000 picoseconds. - Weight::from_parts(9_691_000, 0) - .saturating_add(Weight::from_parts(0, 8621)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `11096` + // Minimum execution time: 11_557_000 picoseconds. + Weight::from_parts(11_868_000, 0) + .saturating_add(Weight::from_parts(0, 11096)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `173` - // Estimated: `11063` - // Minimum execution time: 17_607_000 picoseconds. - Weight::from_parts(18_090_000, 0) - .saturating_add(Weight::from_parts(0, 11063)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `170` + // Estimated: `13535` + // Minimum execution time: 18_710_000 picoseconds. + Weight::from_parts(19_240_000, 0) + .saturating_add(Weight::from_parts(0, 13535)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -347,12 +377,12 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `215` - // Estimated: `11105` - // Minimum execution time: 34_322_000 picoseconds. - Weight::from_parts(35_754_000, 0) - .saturating_add(Weight::from_parts(0, 11105)) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `212` + // Estimated: `13577` + // Minimum execution time: 34_393_000 picoseconds. + Weight::from_parts(35_138_000, 0) + .saturating_add(Weight::from_parts(0, 13577)) + .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) @@ -363,8 +393,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `1588` - // Minimum execution time: 4_513_000 picoseconds. - Weight::from_parts(4_754_000, 0) + // Minimum execution time: 4_043_000 picoseconds. + Weight::from_parts(4_216_000, 0) .saturating_add(Weight::from_parts(0, 1588)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -375,10 +405,22 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7740` // Estimated: `11205` - // Minimum execution time: 27_860_000 picoseconds. - Weight::from_parts(28_279_000, 0) + // Minimum execution time: 25_410_000 picoseconds. + Weight::from_parts(26_019_000, 0) .saturating_add(Weight::from_parts(0, 11205)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `160` + // Estimated: `3625` + // Minimum execution time: 38_850_000 picoseconds. + Weight::from_parts(39_593_000, 0) + .saturating_add(Weight::from_parts(0, 3625)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 50865c0006117ac619a757fc48ce5cf9db4516b9..7d3ed650e6bfcd10e91bfe19fb9463b6c8a4714f 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -185,7 +185,7 @@ pub type ForeignFungiblesTransactor = FungiblesAdapter< LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): AccountId, - // We dont need to check teleports here. + // We don't need to check teleports here. NoChecking, // The account to use for tracking teleports. CheckingAccount, @@ -649,6 +649,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } /// Local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs index aa8c3cf2f14db33e119a355bff2e61ccc9a2c23d..6696cb2322391c2dbad5f6c9a0afc4d5537de68c 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -527,12 +527,8 @@ fn test_foreign_asset_xcm_take_first_trader() { let bought = Weight::from_parts(4_000_000_000u64, 0); // Lets calculate amount needed - let asset_amount_needed = - ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles( - foreign_location, - bought, - ) - .expect("failed to compute"); + let asset_amount_needed = ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles(foreign_location, bought) + .expect("failed to compute"); // Lets pay with: asset_amount_needed + asset_amount_extra let asset_amount_extra = 100_u128; @@ -622,7 +618,7 @@ fn test_asset_xcm_take_first_trader_with_refund() { // We actually use half of the weight let weight_used = bought / 2; - // Make sure refurnd works. + // Make sure refund works. let amount_refunded = WeightToFee::weight_to_fee(&(bought - weight_used)); assert_eq!( @@ -749,7 +745,7 @@ fn test_that_buying_ed_refund_does_not_refund_for_take_first_trader() { // Buy weight should work assert_ok!(trader.buy_weight(bought, asset.into(), &ctx)); - // Should return None. We have a specific check making sure we dont go below ED for + // Should return None. We have a specific check making sure we don't go below ED for // drop payment assert_eq!(trader.refund_weight(bought, &ctx), None); diff --git a/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml b/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml index 89c4925cb1ac232cfdb95e20fb3a117cc186bef8..883c93c97b4de6774e86ee83b84d246dc1427f7f 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml @@ -17,6 +17,7 @@ frame-support = { path = "../../../../../substrate/frame/support", default-featu frame-system = { path = "../../../../../substrate/frame/system", default-features = false } pallet-assets = { path = "../../../../../substrate/frame/assets", default-features = false } pallet-balances = { path = "../../../../../substrate/frame/balances", default-features = false } +pallet-timestamp = { path = "../../../../../substrate/frame/timestamp", default-features = false } pallet-session = { path = "../../../../../substrate/frame/session", default-features = false } sp-io = { path = "../../../../../substrate/primitives/io", default-features = false } sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false } @@ -59,6 +60,7 @@ std = [ "pallet-balances/std", "pallet-collator-selection/std", "pallet-session/std", + "pallet-timestamp/std", "pallet-xcm-bridge-hub-router/std", "pallet-xcm/std", "parachain-info/std", diff --git a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs index 4007d926983ed97a8cae705428c1d61184173cb7..884b71369e79ad9713ea3cc8e243820862c9c20d 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -70,7 +70,8 @@ pub fn teleports_for_native_asset_works< + parachain_info::Config + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config - + cumulus_pallet_xcmp_queue::Config, + + cumulus_pallet_xcmp_queue::Config + + pallet_timestamp::Config, AllPalletsWithoutSystem: OnInitialize> + OnFinalize>, AccountIdOf: Into<[u8; 32]>, @@ -185,7 +186,7 @@ pub fn teleports_for_native_asset_works< // Mint funds into account to ensure it has enough balance to pay delivery fees let delivery_fees = - xcm_helpers::transfer_assets_delivery_fees::( + xcm_helpers::teleport_assets_delivery_fees::( (native_asset_id.clone(), native_asset_to_teleport_away.into()).into(), 0, Unlimited, @@ -350,7 +351,8 @@ pub fn teleports_for_foreign_assets_works< + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config + cumulus_pallet_xcmp_queue::Config - + pallet_assets::Config, + + pallet_assets::Config + + pallet_timestamp::Config, AllPalletsWithoutSystem: OnInitialize> + OnFinalize>, AccountIdOf: Into<[u8; 32]>, @@ -577,7 +579,7 @@ pub fn teleports_for_foreign_assets_works< // Make sure the target account has enough native asset to pay for delivery fees let delivery_fees = - xcm_helpers::transfer_assets_delivery_fees::( + xcm_helpers::teleport_assets_delivery_fees::( (foreign_asset_id_location_latest.clone(), asset_to_teleport_away).into(), 0, Unlimited, @@ -701,7 +703,8 @@ pub fn asset_transactor_transfer_with_local_consensus_currency_works: Into<[u8; 32]>, ValidatorIdOf: From>, BalanceOf: From, @@ -826,7 +829,8 @@ pub fn asset_transactor_transfer_with_pallet_assets_instance_works< + parachain_info::Config + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config - + pallet_assets::Config, + + pallet_assets::Config + + pallet_timestamp::Config, AccountIdOf: Into<[u8; 32]>, ValidatorIdOf: From>, BalanceOf: From, @@ -1093,7 +1097,8 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor + parachain_info::Config + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config - + pallet_assets::Config, + + pallet_assets::Config + + pallet_timestamp::Config, AccountIdOf: Into<[u8; 32]>, ValidatorIdOf: From>, BalanceOf: From, @@ -1115,7 +1120,7 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor AssetId: Clone, AssetIdConverter: MaybeEquivalence, { - // foreign parachain with the same consenus currency as asset + // foreign parachain with the same consensus currency as asset let foreign_asset_id_location = Location::new(1, [Parachain(2222), GeneralIndex(1234567)]); let asset_id = AssetIdConverter::convert(&foreign_asset_id_location).unwrap(); @@ -1422,7 +1427,8 @@ pub fn reserve_transfer_native_asset_to_non_teleport_para_works< + parachain_info::Config + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config - + cumulus_pallet_xcmp_queue::Config, + + cumulus_pallet_xcmp_queue::Config + + pallet_timestamp::Config, AllPalletsWithoutSystem: OnInitialize> + OnFinalize>, AccountIdOf: Into<[u8; 32]>, diff --git a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs index 66ed3417951a35b922108f97bd480942487ccffc..0b2364dbb8bd858a95f6a109c800032cb286412b 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs @@ -70,7 +70,8 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works< + parachain_info::Config + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config - + cumulus_pallet_xcmp_queue::Config, + + cumulus_pallet_xcmp_queue::Config + + pallet_timestamp::Config, AllPalletsWithoutSystem: OnInitialize> + OnFinalize>, AccountIdOf: Into<[u8; 32]>, @@ -347,7 +348,8 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config + cumulus_pallet_xcmp_queue::Config - + pallet_assets::Config, + + pallet_assets::Config + + pallet_timestamp::Config, AllPalletsWithoutSystem: OnInitialize> + OnFinalize>, AccountIdOf: Into<[u8; 32]> + From<[u8; 32]>, @@ -384,7 +386,7 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< existential_deposit, ); - // create foreign asset for wrapped/derivated representation + // create foreign asset for wrapped/derived representation assert_ok!( >::force_create( RuntimeHelper::::root_origin(), @@ -510,7 +512,8 @@ pub fn report_bridge_status_from_xcm_bridge_router_works< + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config + cumulus_pallet_xcmp_queue::Config - + pallet_xcm_bridge_hub_router::Config, + + pallet_xcm_bridge_hub_router::Config + + pallet_timestamp::Config, AllPalletsWithoutSystem: OnInitialize> + OnFinalize>, AccountIdOf: Into<[u8; 32]>, diff --git a/cumulus/parachains/runtimes/assets/test-utils/src/xcm_helpers.rs b/cumulus/parachains/runtimes/assets/test-utils/src/xcm_helpers.rs index f509a3a8acaad9ea2f499c2fa48ca72e0b0882d9..ca0e81fae42eda216f93ed6265dd6dd87f6de538 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/xcm_helpers.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/xcm_helpers.rs @@ -18,11 +18,10 @@ use xcm::latest::prelude::*; -/// Returns the delivery fees amount for pallet xcm's `teleport_assets` and -/// `reserve_transfer_assets` extrinsics. +/// Returns the delivery fees amount for pallet xcm's `teleport_assets` extrinsics. /// Because it returns only a `u128`, it assumes delivery fees are only paid /// in one asset and that asset is known. -pub fn transfer_assets_delivery_fees( +pub fn teleport_assets_delivery_fees( assets: Assets, fee_asset_item: u32, weight_limit: WeightLimit, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 7a1951fd24bd2d74ae722eef31b81dfdb28c8d9b..13b4b624eef2a85cc38e8134402afc790986f0bf 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -85,20 +85,20 @@ parachains-common = { path = "../../../common", default-features = false } testnet-parachains-constants = { path = "../../constants", default-features = false, features = ["rococo"] } # Bridges -bp-asset-hub-rococo = { path = "../../../../../bridges/primitives/chain-asset-hub-rococo", default-features = false } -bp-asset-hub-westend = { path = "../../../../../bridges/primitives/chain-asset-hub-westend", default-features = false } -bp-bridge-hub-polkadot = { path = "../../../../../bridges/primitives/chain-bridge-hub-polkadot", default-features = false } -bp-bridge-hub-rococo = { path = "../../../../../bridges/primitives/chain-bridge-hub-rococo", default-features = false } -bp-bridge-hub-westend = { path = "../../../../../bridges/primitives/chain-bridge-hub-westend", default-features = false } +bp-asset-hub-rococo = { path = "../../../../../bridges/chains/chain-asset-hub-rococo", default-features = false } +bp-asset-hub-westend = { path = "../../../../../bridges/chains/chain-asset-hub-westend", default-features = false } +bp-bridge-hub-polkadot = { path = "../../../../../bridges/chains/chain-bridge-hub-polkadot", default-features = false } +bp-bridge-hub-rococo = { path = "../../../../../bridges/chains/chain-bridge-hub-rococo", default-features = false } +bp-bridge-hub-westend = { path = "../../../../../bridges/chains/chain-bridge-hub-westend", default-features = false } bp-header-chain = { path = "../../../../../bridges/primitives/header-chain", default-features = false } bp-messages = { path = "../../../../../bridges/primitives/messages", default-features = false } bp-parachains = { path = "../../../../../bridges/primitives/parachains", default-features = false } -bp-polkadot-bulletin = { path = "../../../../../bridges/primitives/chain-polkadot-bulletin", default-features = false } +bp-polkadot-bulletin = { path = "../../../../../bridges/chains/chain-polkadot-bulletin", default-features = false } bp-polkadot-core = { path = "../../../../../bridges/primitives/polkadot-core", default-features = false } bp-relayers = { path = "../../../../../bridges/primitives/relayers", default-features = false } bp-runtime = { path = "../../../../../bridges/primitives/runtime", default-features = false } -bp-rococo = { path = "../../../../../bridges/primitives/chain-rococo", default-features = false } -bp-westend = { path = "../../../../../bridges/primitives/chain-westend", default-features = false } +bp-rococo = { path = "../../../../../bridges/chains/chain-rococo", default-features = false } +bp-westend = { path = "../../../../../bridges/chains/chain-westend", default-features = false } pallet-bridge-grandpa = { path = "../../../../../bridges/modules/grandpa", default-features = false } pallet-bridge-messages = { path = "../../../../../bridges/modules/messages", default-features = false } pallet-bridge-parachains = { path = "../../../../../bridges/modules/parachains", default-features = false } @@ -296,7 +296,6 @@ try-runtime = [ "sp-runtime/try-runtime", ] -experimental = ["pallet-aura/experimental"] fast-runtime = [] # A feature that should be enabled when the runtime should be built for on-chain diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index d1eac089f670ac9d72b33991bfd87dd9c02c4f7a..3980fa0d501a1db10f6cb48bdde87946031be8f4 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -38,7 +38,9 @@ pub mod xcm_config; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use snowbridge_beacon_primitives::{Fork, ForkVersions}; use snowbridge_core::{ - gwei, meth, outbound::Message, AgentId, AllowSiblingsOnly, PricingParameters, Rewards, + gwei, meth, + outbound::{Command, Fee}, + AgentId, AllowSiblingsOnly, PricingParameters, Rewards, }; use snowbridge_router_primitives::inbound::MessageToXcm; use sp_api::impl_runtime_apis; @@ -102,8 +104,6 @@ use polkadot_runtime_common::prod_or_fast; #[cfg(feature = "runtime-benchmarks")] use benchmark_helpers::DoNothingRouter; -#[cfg(not(feature = "runtime-benchmarks"))] -use bridge_hub_common::BridgeHubMessageRouter; /// The address format for describing accounts. pub type Address = MultiAddress; @@ -170,12 +170,12 @@ impl frame_support::traits::OnRuntimeUpgrade for InitStorageVersions { let mut writes = 0; if PolkadotXcm::on_chain_storage_version() == StorageVersion::new(0) { - PolkadotXcm::current_storage_version().put::(); + PolkadotXcm::in_code_storage_version().put::(); writes.saturating_inc(); } if Balances::on_chain_storage_version() == StorageVersion::new(0) { - Balances::current_storage_version().put::(); + Balances::in_code_storage_version().put::(); writes.saturating_inc(); } @@ -204,7 +204,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("bridge-hub-rococo"), impl_name: create_runtime_str!("bridge-hub-rococo"), authoring_version: 1, - spec_version: 1_007_000, + spec_version: 1_009_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 4, @@ -244,7 +244,7 @@ parameter_types! { // Configure FRAME pallets to include in runtime. -#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig)] impl frame_system::Config for Runtime { /// The identifier used to distinguish between accounts. type AccountId = AccountId; @@ -279,10 +279,7 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; - #[cfg(feature = "experimental")] type MinimumPeriod = ConstU64<0>; - #[cfg(not(feature = "experimental"))] - type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = weights::pallet_timestamp::WeightInfo; } @@ -367,11 +364,15 @@ parameter_types! { impl pallet_message_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::pallet_message_queue::WeightInfo; - #[cfg(feature = "runtime-benchmarks")] + // Use the NoopMessageProcessor exclusively for benchmarks, not for tests with the + // runtime-benchmarks feature as tests require the BridgeHubMessageRouter to process messages. + // The "test" feature flag doesn't work, hence the reliance on the "std" feature, which is + // enabled during tests. + #[cfg(all(not(feature = "std"), feature = "runtime-benchmarks"))] type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor; - #[cfg(not(feature = "runtime-benchmarks"))] - type MessageProcessor = BridgeHubMessageRouter< + #[cfg(not(all(not(feature = "std"), feature = "runtime-benchmarks")))] + type MessageProcessor = bridge_hub_common::BridgeHubMessageRouter< xcm_builder::ProcessXcmMessage< AggregateMessageOrigin, xcm_executor::XcmExecutor, @@ -443,7 +444,6 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; type AllowMultipleBlocksPerSlot = ConstBool; - #[cfg(feature = "experimental")] type SlotDuration = ConstU64; } @@ -505,7 +505,8 @@ parameter_types! { pub Parameters: PricingParameters = PricingParameters { exchange_rate: FixedU128::from_rational(1, 400), fee_per_gas: gwei(20), - rewards: Rewards { local: 1 * UNITS, remote: meth(1) } + rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, + multiplier: FixedU128::from_rational(1, 1), }; } @@ -730,7 +731,7 @@ construct_runtime!( // Message Queue. Importantly, is registered last so that messages are processed after // the `on_initialize` hooks of bridging pallets. - MessageQueue: pallet_message_queue = 250, + MessageQueue: pallet_message_queue = 175, } ); @@ -792,7 +793,7 @@ impl_runtime_apis! { } fn authorities() -> Vec { - Aura::authorities().into_inner() + pallet_aura::Authorities::::get().into_inner() } } @@ -814,7 +815,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -1024,8 +1025,8 @@ impl_runtime_apis! { snowbridge_pallet_outbound_queue::api::prove_message::(leaf_index) } - fn calculate_fee(message: Message) -> Option { - snowbridge_pallet_outbound_queue::api::calculate_fee::(message) + fn calculate_fee(command: Command, parameters: Option>) -> Fee { + snowbridge_pallet_outbound_queue::api::calculate_fee::(command, parameters) } } @@ -1109,6 +1110,12 @@ impl_runtime_apis! { use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -1117,7 +1124,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between BH and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, Parent.into(), @@ -1140,6 +1147,13 @@ impl_runtime_apis! { dest ) } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(ExistentialDeposit::get()), + } + } } use xcm::latest::prelude::*; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs index 5faded42aa82df52f403b68de2a470ad4a5a17b7..adfaa9ea2028e4b8880e17a1ccc471beb64c9a3c 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-12-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-r43aesjn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -64,8 +64,30 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 23_683_000 picoseconds. - Weight::from_parts(24_199_000, 0) + // Minimum execution time: 18_732_000 picoseconds. + Weight::from_parts(19_386_000, 0) + .saturating_add(Weight::from_parts(0, 3503)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `38` + // Estimated: `3503` + // Minimum execution time: 18_943_000 picoseconds. + Weight::from_parts(19_455_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -90,8 +112,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3593` - // Minimum execution time: 89_524_000 picoseconds. - Weight::from_parts(91_401_000, 0) + // Minimum execution time: 88_917_000 picoseconds. + Weight::from_parts(91_611_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -126,8 +148,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3593` - // Minimum execution time: 91_890_000 picoseconds. - Weight::from_parts(93_460_000, 0) + // Minimum execution time: 88_587_000 picoseconds. + Weight::from_parts(90_303_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -142,14 +164,24 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_152_000 picoseconds. - Weight::from_parts(7_355_000, 0) + // Minimum execution time: 5_856_000 picoseconds. + Weight::from_parts(6_202_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +191,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_081_000 picoseconds. - Weight::from_parts(2_258_000, 0) + // Minimum execution time: 1_797_000 picoseconds. + Weight::from_parts(1_970_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -186,8 +218,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 28_067_000 picoseconds. - Weight::from_parts(28_693_000, 0) + // Minimum execution time: 24_479_000 picoseconds. + Weight::from_parts(25_058_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) @@ -212,8 +244,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `255` // Estimated: `3720` - // Minimum execution time: 30_420_000 picoseconds. - Weight::from_parts(31_373_000, 0) + // Minimum execution time: 27_282_000 picoseconds. + Weight::from_parts(27_924_000, 0) .saturating_add(Weight::from_parts(0, 3720)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -224,45 +256,45 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_087_000 picoseconds. - Weight::from_parts(2_243_000, 0) + // Minimum execution time: 1_801_000 picoseconds. + Weight::from_parts(1_988_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `PolkadotXcm::SupportedVersion` (r:4 w:2) + /// Storage: `PolkadotXcm::SupportedVersion` (r:5 w:2) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `95` - // Estimated: `10985` - // Minimum execution time: 15_142_000 picoseconds. - Weight::from_parts(15_598_000, 0) - .saturating_add(Weight::from_parts(0, 10985)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `89` + // Estimated: `13454` + // Minimum execution time: 16_509_000 picoseconds. + Weight::from_parts(16_939_000, 0) + .saturating_add(Weight::from_parts(0, 13454)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifiers` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `99` - // Estimated: `10989` - // Minimum execution time: 15_041_000 picoseconds. - Weight::from_parts(15_493_000, 0) - .saturating_add(Weight::from_parts(0, 10989)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `93` + // Estimated: `13458` + // Minimum execution time: 16_140_000 picoseconds. + Weight::from_parts(16_843_000, 0) + .saturating_add(Weight::from_parts(0, 13458)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:6 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `13471` - // Minimum execution time: 16_624_000 picoseconds. - Weight::from_parts(17_031_000, 0) - .saturating_add(Weight::from_parts(0, 13471)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `15946` + // Minimum execution time: 18_160_000 picoseconds. + Weight::from_parts(18_948_000, 0) + .saturating_add(Weight::from_parts(0, 15946)) + .saturating_add(T::DbWeight::get().reads(6)) } /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:2 w:1) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -282,36 +314,36 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `6046` - // Minimum execution time: 26_398_000 picoseconds. - Weight::from_parts(26_847_000, 0) + // Minimum execution time: 24_409_000 picoseconds. + Weight::from_parts(25_261_000, 0) .saturating_add(Weight::from_parts(0, 6046)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:3 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `136` - // Estimated: `8551` - // Minimum execution time: 8_741_000 picoseconds. - Weight::from_parts(8_954_000, 0) - .saturating_add(Weight::from_parts(0, 8551)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `11026` + // Minimum execution time: 10_848_000 picoseconds. + Weight::from_parts(11_241_000, 0) + .saturating_add(Weight::from_parts(0, 11026)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `106` - // Estimated: `10996` - // Minimum execution time: 15_306_000 picoseconds. - Weight::from_parts(15_760_000, 0) - .saturating_add(Weight::from_parts(0, 10996)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `100` + // Estimated: `13465` + // Minimum execution time: 16_609_000 picoseconds. + Weight::from_parts(17_044_000, 0) + .saturating_add(Weight::from_parts(0, 13465)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -327,12 +359,12 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `112` - // Estimated: `11002` - // Minimum execution time: 33_127_000 picoseconds. - Weight::from_parts(33_938_000, 0) - .saturating_add(Weight::from_parts(0, 11002)) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `106` + // Estimated: `13471` + // Minimum execution time: 32_500_000 picoseconds. + Weight::from_parts(33_475_000, 0) + .saturating_add(Weight::from_parts(0, 13471)) + .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) @@ -343,8 +375,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `1517` - // Minimum execution time: 4_290_000 picoseconds. - Weight::from_parts(4_450_000, 0) + // Minimum execution time: 3_484_000 picoseconds. + Weight::from_parts(3_673_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -355,10 +387,22 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7669` // Estimated: `11134` - // Minimum execution time: 26_408_000 picoseconds. - Weight::from_parts(26_900_000, 0) + // Minimum execution time: 25_225_000 picoseconds. + Weight::from_parts(25_731_000, 0) .saturating_add(Weight::from_parts(0, 11134)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `90` + // Estimated: `3555` + // Minimum execution time: 33_961_000 picoseconds. + Weight::from_parts(34_818_000, 0) + .saturating_add(Weight::from_parts(0, 3555)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 55c78477b5684987fe07eaaa87d45d8900e74661..8934ff9b22729da0ea322792837d2a08d5829763 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -336,6 +336,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } pub type PriceForParentDelivery = diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs index b2e5cc9ef06fe827749416ce357ce28c8453f9b9..101b8d86d55790241d364fa50900a22ed0e418ef 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs @@ -20,9 +20,9 @@ use bp_polkadot_core::Signature; use bridge_hub_rococo_runtime::{ bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages, bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages, - xcm_config::XcmConfig, BridgeRejectObsoleteHeadersAndMessages, Executive, - MessageQueueServiceWeight, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, SignedExtra, - UncheckedExtrinsic, + xcm_config::XcmConfig, AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, + Executive, MessageQueueServiceWeight, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, + SignedExtra, UncheckedExtrinsic, }; use codec::{Decode, Encode}; use cumulus_primitives_core::XcmError::{FailedToTransactAsset, NotHoldingFees}; @@ -51,6 +51,7 @@ fn collator_session_keys() -> bridge_hub_test_utils::CollatorSessionKeys( + 11155111, collator_session_keys(), 1013, 1000, @@ -69,6 +70,7 @@ pub fn transfer_token_to_ethereum_works() { #[test] pub fn unpaid_transfer_token_to_ethereum_fails_with_barrier() { snowbridge_runtime_test_common::send_unpaid_transfer_token_message::( + 11155111, collator_session_keys(), 1013, 1000, @@ -80,6 +82,7 @@ pub fn unpaid_transfer_token_to_ethereum_fails_with_barrier() { #[test] pub fn transfer_token_to_ethereum_fee_not_enough() { snowbridge_runtime_test_common::send_transfer_token_message_failure::( + 11155111, collator_session_keys(), 1013, 1000, @@ -95,6 +98,7 @@ pub fn transfer_token_to_ethereum_fee_not_enough() { #[test] pub fn transfer_token_to_ethereum_insufficient_fund() { snowbridge_runtime_test_common::send_transfer_token_message_failure::( + 11155111, collator_session_keys(), 1013, 1000, @@ -135,6 +139,33 @@ fn ethereum_to_polkadot_message_extrinsics_work() { ); } +/// Tests that the digest items are as expected when a Ethereum Outbound message is received. +/// If the MessageQueue pallet is configured before (i.e. the MessageQueue pallet is listed before +/// the EthereumOutboundQueue in the construct_runtime macro) the EthereumOutboundQueue, this test +/// will fail. +#[test] +pub fn ethereum_outbound_queue_processes_messages_before_message_queue_works() { + snowbridge_runtime_test_common::ethereum_outbound_queue_processes_messages_before_message_queue_works::< + Runtime, + XcmConfig, + AllPalletsWithoutSystem, + >( + 11155111, + collator_session_keys(), + 1013, + 1000, + H160::random(), + H160::random(), + DefaultBridgeHubEthereumBaseFee::get(), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::EthereumOutboundQueue(event)) => Some(event), + _ => None, + } + }), + ) +} + fn construct_extrinsic( sender: sp_keyring::AccountKeyring, call: RuntimeCall, @@ -159,12 +190,7 @@ fn construct_extrinsic( ); let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap(); let signature = payload.using_encoded(|e| sender.sign(e)); - UncheckedExtrinsic::new_signed( - call, - account_id.into(), - Signature::Sr25519(signature.clone()), - extra, - ) + UncheckedExtrinsic::new_signed(call, account_id.into(), Signature::Sr25519(signature), extra) } fn construct_and_apply_extrinsic( diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index f11954cf165fdae2ee36377456b2bc4b2d6755a6..fad357b095148fd07db14762b764d29b4a4366fa 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -67,12 +67,7 @@ fn construct_extrinsic( ); let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap(); let signature = payload.using_encoded(|e| sender.sign(e)); - UncheckedExtrinsic::new_signed( - call, - account_id.into(), - Signature::Sr25519(signature.clone()), - extra, - ) + UncheckedExtrinsic::new_signed(call, account_id.into(), Signature::Sr25519(signature), extra) } fn construct_and_apply_extrinsic( diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml index 8623f7cb366ecf3930b25563d6a912e53240d999..0c46e6c2e14c7184e4dfb300b219ad4ce8a9300f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml @@ -77,25 +77,25 @@ parachains-common = { path = "../../../common", default-features = false } testnet-parachains-constants = { path = "../../constants", default-features = false, features = ["westend"] } # Bridges -bp-asset-hub-rococo = { path = "../../../../../bridges/primitives/chain-asset-hub-rococo", default-features = false } -bp-asset-hub-westend = { path = "../../../../../bridges/primitives/chain-asset-hub-westend", default-features = false } -bp-bridge-hub-rococo = { path = "../../../../../bridges/primitives/chain-bridge-hub-rococo", default-features = false } -bp-bridge-hub-westend = { path = "../../../../../bridges/primitives/chain-bridge-hub-westend", default-features = false } +bp-asset-hub-rococo = { path = "../../../../../bridges/chains/chain-asset-hub-rococo", default-features = false } +bp-asset-hub-westend = { path = "../../../../../bridges/chains/chain-asset-hub-westend", default-features = false } +bp-bridge-hub-rococo = { path = "../../../../../bridges/chains/chain-bridge-hub-rococo", default-features = false } +bp-bridge-hub-westend = { path = "../../../../../bridges/chains/chain-bridge-hub-westend", default-features = false } bp-header-chain = { path = "../../../../../bridges/primitives/header-chain", default-features = false } bp-messages = { path = "../../../../../bridges/primitives/messages", default-features = false } bp-parachains = { path = "../../../../../bridges/primitives/parachains", default-features = false } bp-polkadot-core = { path = "../../../../../bridges/primitives/polkadot-core", default-features = false } bp-relayers = { path = "../../../../../bridges/primitives/relayers", default-features = false } bp-runtime = { path = "../../../../../bridges/primitives/runtime", default-features = false } -bp-rococo = { path = "../../../../../bridges/primitives/chain-rococo", default-features = false } -bp-westend = { path = "../../../../../bridges/primitives/chain-westend", default-features = false } +bp-rococo = { path = "../../../../../bridges/chains/chain-rococo", default-features = false } +bp-westend = { path = "../../../../../bridges/chains/chain-westend", default-features = false } pallet-bridge-grandpa = { path = "../../../../../bridges/modules/grandpa", default-features = false } pallet-bridge-messages = { path = "../../../../../bridges/modules/messages", default-features = false } pallet-bridge-parachains = { path = "../../../../../bridges/modules/parachains", default-features = false } pallet-bridge-relayers = { path = "../../../../../bridges/modules/relayers", default-features = false } pallet-xcm-bridge-hub = { path = "../../../../../bridges/modules/xcm-bridge-hub", default-features = false } bridge-runtime-common = { path = "../../../../../bridges/bin/runtime-common", default-features = false } -bridge-hub-common = { path = "../../bridge-hubs/common", default-features = false } +bridge-hub-common = { path = "../common", default-features = false } [dev-dependencies] static_assertions = "1.1" @@ -246,8 +246,6 @@ try-runtime = [ "sp-runtime/try-runtime", ] -experimental = ["pallet-aura/experimental"] - # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index 997320ffcf262ef2c327c6b2a404d43a66a6df5e..9bdea6b9a7dd72f7f280b456f6624f12153dbbba 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -142,12 +142,12 @@ impl frame_support::traits::OnRuntimeUpgrade for InitStorageVersions { let mut writes = 0; if PolkadotXcm::on_chain_storage_version() == StorageVersion::new(0) { - PolkadotXcm::current_storage_version().put::(); + PolkadotXcm::in_code_storage_version().put::(); writes.saturating_inc(); } if Balances::on_chain_storage_version() == StorageVersion::new(0) { - Balances::current_storage_version().put::(); + Balances::in_code_storage_version().put::(); writes.saturating_inc(); } @@ -176,7 +176,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("bridge-hub-westend"), impl_name: create_runtime_str!("bridge-hub-westend"), authoring_version: 1, - spec_version: 1_007_000, + spec_version: 1_009_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 4, @@ -216,7 +216,7 @@ parameter_types! { // Configure FRAME pallets to include in runtime. -#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig)] impl frame_system::Config for Runtime { /// The identifier used to distinguish between accounts. type AccountId = AccountId; @@ -251,10 +251,7 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; - #[cfg(feature = "experimental")] type MinimumPeriod = ConstU64<0>; - #[cfg(not(feature = "experimental"))] - type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = weights::pallet_timestamp::WeightInfo; } @@ -407,7 +404,6 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; type AllowMultipleBlocksPerSlot = ConstBool; - #[cfg(feature = "experimental")] type SlotDuration = ConstU64; } @@ -544,7 +540,7 @@ impl_runtime_apis! { } fn authorities() -> Vec { - Aura::authorities().into_inner() + pallet_aura::Authorities::::get().into_inner() } } @@ -566,7 +562,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -806,6 +802,12 @@ impl_runtime_apis! { use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -814,7 +816,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between BH and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, Parent.into(), @@ -837,6 +839,13 @@ impl_runtime_apis! { dest ) } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(ExistentialDeposit::get()), + } + } } use xcm::latest::prelude::*; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_xcm.rs index 83e4260e77198355d23ea0c38481d7b8e68267c7..9cf4c61466a1bd37ffea56681648b2c2e2ce4555 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_xcm.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-12-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-r43aesjn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -64,8 +64,30 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 23_219_000 picoseconds. - Weight::from_parts(23_818_000, 0) + // Minimum execution time: 19_702_000 picoseconds. + Weight::from_parts(20_410_000, 0) + .saturating_add(Weight::from_parts(0, 3503)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `38` + // Estimated: `3503` + // Minimum execution time: 19_525_000 picoseconds. + Weight::from_parts(20_071_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -88,10 +110,10 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn teleport_assets() -> Weight { // Proof Size summary in bytes: - // Measured: `70` + // Measured: `107` // Estimated: `3593` - // Minimum execution time: 90_120_000 picoseconds. - Weight::from_parts(92_545_000, 0) + // Minimum execution time: 91_793_000 picoseconds. + Weight::from_parts(93_761_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -124,10 +146,10 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn transfer_assets() -> Weight { // Proof Size summary in bytes: - // Measured: `70` + // Measured: `107` // Estimated: `3593` - // Minimum execution time: 91_339_000 picoseconds. - Weight::from_parts(93_204_000, 0) + // Minimum execution time: 91_819_000 picoseconds. + Weight::from_parts(93_198_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -142,14 +164,24 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_976_000 picoseconds. - Weight::from_parts(7_284_000, 0) + // Minimum execution time: 6_183_000 picoseconds. + Weight::from_parts(6_598_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +191,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_044_000 picoseconds. - Weight::from_parts(2_223_000, 0) + // Minimum execution time: 1_987_000 picoseconds. + Weight::from_parts(2_076_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -186,8 +218,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 27_778_000 picoseconds. - Weight::from_parts(28_318_000, 0) + // Minimum execution time: 25_375_000 picoseconds. + Weight::from_parts(26_165_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) @@ -212,8 +244,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `255` // Estimated: `3720` - // Minimum execution time: 30_446_000 picoseconds. - Weight::from_parts(31_925_000, 0) + // Minimum execution time: 28_167_000 picoseconds. + Weight::from_parts(28_792_000, 0) .saturating_add(Weight::from_parts(0, 3720)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -224,45 +256,45 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_037_000 picoseconds. + // Minimum execution time: 2_039_000 picoseconds. Weight::from_parts(2_211_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `PolkadotXcm::SupportedVersion` (r:4 w:2) + /// Storage: `PolkadotXcm::SupportedVersion` (r:5 w:2) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `95` - // Estimated: `10985` - // Minimum execution time: 15_620_000 picoseconds. - Weight::from_parts(15_984_000, 0) - .saturating_add(Weight::from_parts(0, 10985)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `89` + // Estimated: `13454` + // Minimum execution time: 17_127_000 picoseconds. + Weight::from_parts(17_519_000, 0) + .saturating_add(Weight::from_parts(0, 13454)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifiers` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `99` - // Estimated: `10989` - // Minimum execution time: 15_689_000 picoseconds. - Weight::from_parts(16_093_000, 0) - .saturating_add(Weight::from_parts(0, 10989)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `93` + // Estimated: `13458` + // Minimum execution time: 16_701_000 picoseconds. + Weight::from_parts(17_250_000, 0) + .saturating_add(Weight::from_parts(0, 13458)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:6 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `13471` - // Minimum execution time: 16_946_000 picoseconds. - Weight::from_parts(17_192_000, 0) - .saturating_add(Weight::from_parts(0, 13471)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `15946` + // Minimum execution time: 18_795_000 picoseconds. + Weight::from_parts(19_302_000, 0) + .saturating_add(Weight::from_parts(0, 15946)) + .saturating_add(T::DbWeight::get().reads(6)) } /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:2 w:1) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -282,36 +314,36 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `6046` - // Minimum execution time: 27_164_000 picoseconds. - Weight::from_parts(27_760_000, 0) + // Minimum execution time: 25_007_000 picoseconds. + Weight::from_parts(25_786_000, 0) .saturating_add(Weight::from_parts(0, 6046)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:3 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `136` - // Estimated: `8551` - // Minimum execution time: 8_689_000 picoseconds. - Weight::from_parts(8_874_000, 0) - .saturating_add(Weight::from_parts(0, 8551)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `11026` + // Minimum execution time: 11_534_000 picoseconds. + Weight::from_parts(11_798_000, 0) + .saturating_add(Weight::from_parts(0, 11026)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `106` - // Estimated: `10996` - // Minimum execution time: 15_944_000 picoseconds. - Weight::from_parts(16_381_000, 0) - .saturating_add(Weight::from_parts(0, 10996)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `100` + // Estimated: `13465` + // Minimum execution time: 17_357_000 picoseconds. + Weight::from_parts(17_629_000, 0) + .saturating_add(Weight::from_parts(0, 13465)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -327,12 +359,12 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `112` - // Estimated: `11002` - // Minimum execution time: 33_826_000 picoseconds. - Weight::from_parts(34_784_000, 0) - .saturating_add(Weight::from_parts(0, 11002)) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `106` + // Estimated: `13471` + // Minimum execution time: 33_487_000 picoseconds. + Weight::from_parts(34_033_000, 0) + .saturating_add(Weight::from_parts(0, 13471)) + .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) @@ -343,8 +375,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `1517` - // Minimum execution time: 4_257_000 picoseconds. - Weight::from_parts(4_383_000, 0) + // Minimum execution time: 3_688_000 picoseconds. + Weight::from_parts(3_854_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -355,10 +387,22 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7669` // Estimated: `11134` - // Minimum execution time: 26_924_000 picoseconds. - Weight::from_parts(27_455_000, 0) + // Minimum execution time: 26_336_000 picoseconds. + Weight::from_parts(26_873_000, 0) .saturating_add(Weight::from_parts(0, 11134)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `90` + // Estimated: `3555` + // Minimum execution time: 34_633_000 picoseconds. + Weight::from_parts(35_171_000, 0) + .saturating_add(Weight::from_parts(0, 3555)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs index e18df6feda82754d83db711bb2c6ea813c387fca..840d0c9af0e5886eef81ea9bbc542279ff7a106f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs @@ -264,6 +264,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } pub type PriceForParentDelivery = diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs index 149a3bbeb75d82ba7c35641428c7677d18297f8c..235b7f146c8e0f141999ef5a27fe1a21cde7b76b 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs @@ -81,12 +81,7 @@ fn construct_extrinsic( ); let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap(); let signature = payload.using_encoded(|e| sender.sign(e)); - UncheckedExtrinsic::new_signed( - call, - account_id.into(), - Signature::Sr25519(signature.clone()), - extra, - ) + UncheckedExtrinsic::new_signed(call, account_id.into(), Signature::Sr25519(signature), extra) } fn construct_and_apply_extrinsic( diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml index d34b5cd0eed1ef3f9c8192f0eb0bd3fdfdfa2abf..5f2a6e050d83c3db662f8ff4896d32dc8a28fde3 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml @@ -25,6 +25,7 @@ sp-std = { path = "../../../../../substrate/primitives/std", default-features = sp-tracing = { path = "../../../../../substrate/primitives/tracing" } pallet-balances = { path = "../../../../../substrate/frame/balances", default-features = false } pallet-utility = { path = "../../../../../substrate/frame/utility", default-features = false } +pallet-timestamp = { path = "../../../../../substrate/frame/timestamp", default-features = false } # Cumulus asset-test-utils = { path = "../../assets/test-utils" } @@ -73,6 +74,7 @@ std = [ "pallet-bridge-messages/std", "pallet-bridge-parachains/std", "pallet-bridge-relayers/std", + "pallet-timestamp/std", "pallet-utility/std", "parachains-common/std", "parachains-runtimes-test-utils/std", diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs index 4f634c184aa84faa6466102ef5fee30f254bad43..2b48f2e3d515f625532d9c5f50fabadb9a89517a 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs @@ -197,7 +197,9 @@ where pub(crate) fn initialize_bridge_grandpa_pallet( init_data: bp_header_chain::InitializationData>, ) where - Runtime: BridgeGrandpaConfig, + Runtime: BridgeGrandpaConfig + + cumulus_pallet_parachain_system::Config + + pallet_timestamp::Config, { pallet_bridge_grandpa::Pallet::::initialize( RuntimeHelper::::root_origin(), diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml b/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml index ed264f28c26e4d48bca416f1e6bea0f0049aa334..4224b3971398ed87c56aa90fe8f82d9f07e0e4bd 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml @@ -232,8 +232,6 @@ std = [ "xcm/std", ] -experimental = ["pallet-aura/experimental"] - # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/impls.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/impls.rs index caf0cddec664a55ce080ed6052a9cdf42a5e2f28..e5b176fc77873805fb0e4ed6dba74d720ea3479a 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/impls.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/impls.rs @@ -81,7 +81,7 @@ where } fn proposal_of(proposal_hash: HashOf) -> Option> { - pallet_collective::Pallet::::proposal_of(proposal_hash) + pallet_collective::ProposalOf::::get(proposal_hash) } } diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index 4299e8bb3ec02da64820fd8ea2fcfd3343dd8ffa..d3f588bf25ff21768f700a8b1c97007678bee17d 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -117,7 +117,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("collectives-westend"), impl_name: create_runtime_str!("collectives-westend"), authoring_version: 1, - spec_version: 1_007_000, + spec_version: 1_009_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 5, @@ -161,7 +161,7 @@ parameter_types! { } // Configure FRAME pallets to include in runtime. -#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig)] impl frame_system::Config for Runtime { type BlockWeights = RuntimeBlockWeights; type BlockLength = RuntimeBlockLength; @@ -184,10 +184,7 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; - #[cfg(feature = "experimental")] type MinimumPeriod = ConstU64<0>; - #[cfg(not(feature = "experimental"))] - type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = weights::pallet_timestamp::WeightInfo; } @@ -483,7 +480,6 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; type AllowMultipleBlocksPerSlot = ConstBool; - #[cfg(feature = "experimental")] type SlotDuration = ConstU64; } @@ -781,7 +777,7 @@ impl_runtime_apis! { } fn authorities() -> Vec { - Aura::authorities().into_inner() + pallet_aura::Authorities::::get().into_inner() } } @@ -803,7 +799,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -986,8 +982,21 @@ impl_runtime_apis! { use cumulus_pallet_session_benchmarking::Pallet as SessionBench; impl cumulus_pallet_session_benchmarking::Config for Runtime {} + parameter_types! { + pub ExistentialDepositAsset: Option = Some(( + xcm_config::WndLocation::get(), + ExistentialDeposit::get() + ).into()); + } + use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -996,7 +1005,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between Collectives and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }.into(), Parent.into(), @@ -1019,6 +1028,13 @@ impl_runtime_apis! { dest ) } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(ExistentialDeposit::get()), + } + } } let whitelist: Vec = vec![ diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_xcm.rs index 50dfbffde01f21d1138f0fdaa27649f962e245e4..0edd5dfff2b8b714c6de0a34dcd095787673d39b 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_xcm.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-12-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-r43aesjn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("collectives-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -64,8 +64,30 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 24_540_000 picoseconds. - Weight::from_parts(25_439_000, 0) + // Minimum execution time: 21_911_000 picoseconds. + Weight::from_parts(22_431_000, 0) + .saturating_add(Weight::from_parts(0, 3610)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `3610` + // Minimum execution time: 22_143_000 picoseconds. + Weight::from_parts(22_843_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -90,8 +112,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `214` // Estimated: `3679` - // Minimum execution time: 86_614_000 picoseconds. - Weight::from_parts(88_884_000, 0) + // Minimum execution time: 96_273_000 picoseconds. + Weight::from_parts(98_351_000, 0) .saturating_add(Weight::from_parts(0, 3679)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -126,8 +148,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `214` // Estimated: `3679` - // Minimum execution time: 87_915_000 picoseconds. - Weight::from_parts(90_219_000, 0) + // Minimum execution time: 95_571_000 picoseconds. + Weight::from_parts(96_251_000, 0) .saturating_add(Weight::from_parts(0, 3679)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -142,14 +164,24 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_872_000 picoseconds. - Weight::from_parts(7_110_000, 0) + // Minimum execution time: 6_227_000 picoseconds. + Weight::from_parts(6_419_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +191,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_009_000 picoseconds. - Weight::from_parts(2_163_000, 0) + // Minimum execution time: 1_851_000 picoseconds. + Weight::from_parts(1_940_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -186,8 +218,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 28_858_000 picoseconds. - Weight::from_parts(29_355_000, 0) + // Minimum execution time: 27_449_000 picoseconds. + Weight::from_parts(28_513_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) @@ -212,8 +244,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `363` // Estimated: `3828` - // Minimum execution time: 30_598_000 picoseconds. - Weight::from_parts(31_168_000, 0) + // Minimum execution time: 29_477_000 picoseconds. + Weight::from_parts(30_251_000, 0) .saturating_add(Weight::from_parts(0, 3828)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -224,45 +256,45 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_090_000 picoseconds. - Weight::from_parts(2_253_000, 0) + // Minimum execution time: 1_894_000 picoseconds. + Weight::from_parts(2_009_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `PolkadotXcm::SupportedVersion` (r:4 w:2) + /// Storage: `PolkadotXcm::SupportedVersion` (r:5 w:2) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `162` - // Estimated: `11052` - // Minimum execution time: 16_133_000 picoseconds. - Weight::from_parts(16_433_000, 0) - .saturating_add(Weight::from_parts(0, 11052)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `159` + // Estimated: `13524` + // Minimum execution time: 17_991_000 picoseconds. + Weight::from_parts(18_651_000, 0) + .saturating_add(Weight::from_parts(0, 13524)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifiers` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `166` - // Estimated: `11056` - // Minimum execution time: 16_012_000 picoseconds. - Weight::from_parts(16_449_000, 0) - .saturating_add(Weight::from_parts(0, 11056)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `163` + // Estimated: `13528` + // Minimum execution time: 18_321_000 picoseconds. + Weight::from_parts(18_701_000, 0) + .saturating_add(Weight::from_parts(0, 13528)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:6 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `173` - // Estimated: `13538` - // Minimum execution time: 17_922_000 picoseconds. - Weight::from_parts(18_426_000, 0) - .saturating_add(Weight::from_parts(0, 13538)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `16013` + // Minimum execution time: 19_762_000 picoseconds. + Weight::from_parts(20_529_000, 0) + .saturating_add(Weight::from_parts(0, 16013)) + .saturating_add(T::DbWeight::get().reads(6)) } /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:2 w:1) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -282,36 +314,36 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `212` // Estimated: `6152` - // Minimum execution time: 27_280_000 picoseconds. - Weight::from_parts(28_026_000, 0) + // Minimum execution time: 26_927_000 picoseconds. + Weight::from_parts(27_629_000, 0) .saturating_add(Weight::from_parts(0, 6152)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:3 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `206` - // Estimated: `8621` - // Minimum execution time: 9_387_000 picoseconds. - Weight::from_parts(9_644_000, 0) - .saturating_add(Weight::from_parts(0, 8621)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `11096` + // Minimum execution time: 11_957_000 picoseconds. + Weight::from_parts(12_119_000, 0) + .saturating_add(Weight::from_parts(0, 11096)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `173` - // Estimated: `11063` - // Minimum execution time: 16_649_000 picoseconds. - Weight::from_parts(17_025_000, 0) - .saturating_add(Weight::from_parts(0, 11063)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `170` + // Estimated: `13535` + // Minimum execution time: 17_942_000 picoseconds. + Weight::from_parts(18_878_000, 0) + .saturating_add(Weight::from_parts(0, 13535)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -327,12 +359,12 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `215` - // Estimated: `11105` - // Minimum execution time: 34_355_000 picoseconds. - Weight::from_parts(35_295_000, 0) - .saturating_add(Weight::from_parts(0, 11105)) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `212` + // Estimated: `13577` + // Minimum execution time: 35_640_000 picoseconds. + Weight::from_parts(36_340_000, 0) + .saturating_add(Weight::from_parts(0, 13577)) + .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) @@ -343,8 +375,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `1588` - // Minimum execution time: 4_527_000 picoseconds. - Weight::from_parts(4_699_000, 0) + // Minimum execution time: 4_044_000 picoseconds. + Weight::from_parts(4_229_000, 0) .saturating_add(Weight::from_parts(0, 1588)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -355,10 +387,22 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7740` // Estimated: `11205` - // Minimum execution time: 27_011_000 picoseconds. - Weight::from_parts(27_398_000, 0) + // Minimum execution time: 26_262_000 picoseconds. + Weight::from_parts(26_842_000, 0) .saturating_add(Weight::from_parts(0, 11205)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `160` + // Estimated: `3625` + // Minimum execution time: 36_775_000 picoseconds. + Weight::from_parts(37_265_000, 0) + .saturating_add(Weight::from_parts(0, 3625)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs index cc25cbda0a4276c0c8956cf94b590998f4d3fac9..b83106a5828402390d1bd237ceb510d78b020b6d 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs @@ -59,7 +59,7 @@ parameter_types! { pub const GovernanceLocation: Location = Location::parent(); pub const FellowshipAdminBodyId: BodyId = BodyId::Index(xcm_constants::body::FELLOWSHIP_ADMIN_INDEX); pub AssetHub: Location = (Parent, Parachain(1000)).into(); - pub const TreasurerBodyId: BodyId = BodyId::Index(xcm_constants::body::TREASURER_INDEX); + pub const TreasurerBodyId: BodyId = BodyId::Treasury; pub AssetHubUsdtId: AssetId = (PalletInstance(50), GeneralIndex(1984)).into(); pub UsdtAssetHub: LocatableAssetId = LocatableAssetId { location: AssetHub::get(), @@ -288,6 +288,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } /// Converts a local signed origin into an XCM location. diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml b/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml index dcc6c4e853a39d6acb15e205256141de1db4ed2e..e4ac2016a726c4ba9ad63af2ddd332cc6dab415f 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml @@ -196,8 +196,6 @@ try-runtime = [ "sp-runtime/try-runtime", ] -experimental = ["pallet-aura/experimental"] - # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/contracts.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/contracts.rs index 7b89f2df807734086031797915bde30b2eff77e2..171ac6a9528f134d9c22548500805ef36e9504f9 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/contracts.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/contracts.rs @@ -21,6 +21,7 @@ use frame_support::{ parameter_types, traits::{ConstBool, ConstU32, Nothing}, }; +use frame_system::EnsureSigned; use pallet_contracts::{ weights::SubstrateWeight, Config, DebugInfo, DefaultAddressGenerator, Frame, Schedule, }; @@ -65,6 +66,8 @@ impl Config for Runtime { type MaxCodeLen = ConstU32<{ 123 * 1024 }>; type MaxStorageKeyLen = ConstU32<128>; type UnsafeUnstableInterface = ConstBool; + type UploadOrigin = EnsureSigned; + type InstantiateOrigin = EnsureSigned; type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; type MaxDelegateDependencies = ConstU32<32>; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; @@ -72,5 +75,6 @@ impl Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type Debug = (); type Environment = (); + type ApiVersion = (); type Xcm = pallet_xcm::Pallet; } diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs index 94cb0e502e4c831940394ed4f1d22ec8f92a63f1..e1586c7d9b29e1b72eab189f68db034d8414e264 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs @@ -50,7 +50,7 @@ use frame_support::{ dispatch::DispatchClass, genesis_builder_helper::{build_config, create_default_config}, parameter_types, - traits::{ConstBool, ConstU128, ConstU16, ConstU32, ConstU64, ConstU8}, + traits::{ConstBool, ConstU16, ConstU32, ConstU64, ConstU8}, weights::{ConstantMultiplier, Weight}, PalletId, }; @@ -133,7 +133,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("contracts-rococo"), impl_name: create_runtime_str!("contracts-rococo"), authoring_version: 1, - spec_version: 1_007_000, + spec_version: 1_009_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 6, @@ -171,7 +171,7 @@ parameter_types! { } // Configure FRAME pallets to include in runtime. -#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig)] impl frame_system::Config for Runtime { type BlockWeights = RuntimeBlockWeights; type BlockLength = RuntimeBlockLength; @@ -193,10 +193,7 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; - #[cfg(feature = "experimental")] type MinimumPeriod = ConstU64<0>; - #[cfg(not(feature = "experimental"))] - type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = pallet_timestamp::weights::SubstrateWeight; } @@ -205,6 +202,10 @@ impl pallet_authorship::Config for Runtime { type EventHandler = (CollatorSelection,); } +parameter_types! { + pub const ExistentialDeposit: Balance = EXISTENTIAL_DEPOSIT; +} + impl pallet_balances::Config for Runtime { type MaxLocks = ConstU32<50>; /// The type for recording an account's balance. @@ -212,7 +213,7 @@ impl pallet_balances::Config for Runtime { /// The ubiquitous event type. type RuntimeEvent = RuntimeEvent; type DustRemoval = (); - type ExistentialDeposit = ConstU128; + type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = pallet_balances::weights::SubstrateWeight; type MaxReserves = ConstU32<50>; @@ -345,7 +346,6 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; type AllowMultipleBlocksPerSlot = ConstBool; - #[cfg(feature = "experimental")] type SlotDuration = ConstU64; } @@ -440,7 +440,7 @@ impl_runtime_apis! { } fn authorities() -> Vec { - Aura::authorities().into_inner() + pallet_aura::Authorities::::get().into_inner() } } @@ -462,7 +462,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -713,9 +713,22 @@ impl_runtime_apis! { use cumulus_pallet_session_benchmarking::Pallet as SessionBench; impl cumulus_pallet_session_benchmarking::Config for Runtime {} + parameter_types! { + pub ExistentialDepositAsset: Option = Some(( + xcm_config::RelayLocation::get(), + ExistentialDeposit::get() + ).into()); + } + use xcm::latest::prelude::*; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -724,7 +737,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between Contracts-System-Para and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, Parent.into(), @@ -747,6 +760,13 @@ impl_runtime_apis! { dest ) } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(EXISTENTIAL_DEPOSIT), + } + } } let whitelist: Vec = vec![ diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs index e8f3209eb67f627efccc9e8a798da03b6a2280c0..ac6fe634662f9789e6e6d8e4750779517036ee82 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -197,6 +197,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } /// Converts a local signed origin into an XCM location. diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml b/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml index 0bc3b510ed50e9ee590fd82961a7d08fb3f581b0..eb92afc431147ef4fa7b64164c0bc5309a465bb7 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml @@ -194,6 +194,4 @@ try-runtime = [ "sp-runtime/try-runtime", ] -experimental = ["pallet-aura/experimental"] - fast-runtime = [] diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index f07bac8b2ef075c31ad09dfd4e59fb1025f04c5a..86eb5cdfcaf5ebf0a2cbbed87c2642eed6452405 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -133,7 +133,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("coretime-rococo"), impl_name: create_runtime_str!("coretime-rococo"), authoring_version: 1, - spec_version: 1_007_001, + spec_version: 1_009_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 0, @@ -172,7 +172,7 @@ parameter_types! { } // Configure FRAME pallets to include in runtime. -#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig)] impl frame_system::Config for Runtime { /// The identifier used to distinguish between accounts. type AccountId = AccountId; @@ -206,10 +206,7 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; - #[cfg(feature = "experimental")] type MinimumPeriod = ConstU64<0>; - #[cfg(not(feature = "experimental"))] - type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = weights::pallet_timestamp::WeightInfo; } @@ -369,7 +366,6 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; type AllowMultipleBlocksPerSlot = ConstBool; - #[cfg(feature = "experimental")] type SlotDuration = ConstU64; } @@ -499,7 +495,7 @@ impl_runtime_apis! { } fn authorities() -> Vec { - Aura::authorities().into_inner() + pallet_aura::Authorities::::get().into_inner() } } @@ -521,7 +517,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -715,6 +711,12 @@ impl_runtime_apis! { use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -723,7 +725,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between AH and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, Parent.into(), @@ -734,6 +736,13 @@ impl_runtime_apis! { // Reserve transfers are disabled None } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(ExistentialDeposit::get()), + } + } } parameter_types! { diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_broker.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_broker.rs index 2d30ddc612cb9544291b90ea9456e392ab3451d4..89b1c4c86632ff0d19ee4fe1428978303ef92082 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_broker.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_broker.rs @@ -16,26 +16,24 @@ //! Autogenerated weights for `pallet_broker` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-01-12, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-j8vvqcjr-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-rococo-dev")`, DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=coretime-rococo-dev -// --wasm-execution=compiled -// --pallet=pallet_broker -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --extrinsic=* // --steps=50 // --repeat=20 -// --json +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_broker +// --chain=coretime-rococo-dev // --header=./cumulus/file_header.txt // --output=./cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/ @@ -56,8 +54,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_462_000 picoseconds. - Weight::from_parts(2_552_000, 0) + // Minimum execution time: 1_918_000 picoseconds. + Weight::from_parts(2_092_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -67,8 +65,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `10888` // Estimated: `13506` - // Minimum execution time: 25_494_000 picoseconds. - Weight::from_parts(26_063_000, 0) + // Minimum execution time: 21_943_000 picoseconds. + Weight::from_parts(22_570_000, 0) .saturating_add(Weight::from_parts(0, 13506)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -79,8 +77,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `12090` // Estimated: `13506` - // Minimum execution time: 22_299_000 picoseconds. - Weight::from_parts(22_911_000, 0) + // Minimum execution time: 20_923_000 picoseconds. + Weight::from_parts(21_354_000, 0) .saturating_add(Weight::from_parts(0, 13506)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -95,8 +93,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `466` // Estimated: `1951` - // Minimum execution time: 11_590_000 picoseconds. - Weight::from_parts(12_007_000, 0) + // Minimum execution time: 10_687_000 picoseconds. + Weight::from_parts(11_409_000, 0) .saturating_add(Weight::from_parts(0, 1951)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -124,11 +122,11 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `12567` // Estimated: `14052` - // Minimum execution time: 120_928_000 picoseconds. - Weight::from_parts(124_947_252, 0) + // Minimum execution time: 111_288_000 picoseconds. + Weight::from_parts(117_804_282, 0) .saturating_add(Weight::from_parts(0, 14052)) - // Standard Error: 435 - .saturating_add(Weight::from_parts(1_246, 0).saturating_mul(n.into())) + // Standard Error: 391 + .saturating_add(Weight::from_parts(1_243, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(66)) } @@ -144,8 +142,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `316` // Estimated: `3593` - // Minimum execution time: 32_826_000 picoseconds. - Weight::from_parts(33_889_000, 0) + // Minimum execution time: 33_006_000 picoseconds. + Weight::from_parts(34_256_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -166,8 +164,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `434` // Estimated: `4698` - // Minimum execution time: 57_362_000 picoseconds. - Weight::from_parts(58_994_000, 0) + // Minimum execution time: 61_473_000 picoseconds. + Weight::from_parts(66_476_000, 0) .saturating_add(Weight::from_parts(0, 4698)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) @@ -178,8 +176,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `357` // Estimated: `3550` - // Minimum execution time: 13_982_000 picoseconds. - Weight::from_parts(14_447_000, 0) + // Minimum execution time: 13_771_000 picoseconds. + Weight::from_parts(14_374_000, 0) .saturating_add(Weight::from_parts(0, 3550)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -190,8 +188,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `357` // Estimated: `3550` - // Minimum execution time: 15_070_000 picoseconds. - Weight::from_parts(15_735_000, 0) + // Minimum execution time: 15_162_000 picoseconds. + Weight::from_parts(15_742_000, 0) .saturating_add(Weight::from_parts(0, 3550)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -202,8 +200,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `357` // Estimated: `3550` - // Minimum execution time: 16_527_000 picoseconds. - Weight::from_parts(16_894_000, 0) + // Minimum execution time: 16_196_000 picoseconds. + Weight::from_parts(16_796_000, 0) .saturating_add(Weight::from_parts(0, 3550)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(3)) @@ -220,8 +218,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `936` // Estimated: `4681` - // Minimum execution time: 25_493_000 picoseconds. - Weight::from_parts(26_091_000, 0) + // Minimum execution time: 25_653_000 picoseconds. + Weight::from_parts(27_006_000, 0) .saturating_add(Weight::from_parts(0, 4681)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(2)) @@ -240,8 +238,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1002` // Estimated: `5996` - // Minimum execution time: 31_498_000 picoseconds. - Weight::from_parts(32_560_000, 0) + // Minimum execution time: 31_114_000 picoseconds. + Weight::from_parts(32_235_000, 0) .saturating_add(Weight::from_parts(0, 5996)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(5)) @@ -257,11 +255,11 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `652` // Estimated: `6196 + m * (2520 ±0)` - // Minimum execution time: 57_183_000 picoseconds. - Weight::from_parts(58_024_898, 0) + // Minimum execution time: 57_280_000 picoseconds. + Weight::from_parts(58_127_480, 0) .saturating_add(Weight::from_parts(0, 6196)) - // Standard Error: 35_831 - .saturating_add(Weight::from_parts(1_384_446, 0).saturating_mul(m.into())) + // Standard Error: 41_670 + .saturating_add(Weight::from_parts(1_203_066, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().writes(5)) @@ -283,8 +281,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `215` // Estimated: `3680` - // Minimum execution time: 59_762_000 picoseconds. - Weight::from_parts(61_114_000, 0) + // Minimum execution time: 59_968_000 picoseconds. + Weight::from_parts(62_315_000, 0) .saturating_add(Weight::from_parts(0, 3680)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) @@ -297,8 +295,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `465` // Estimated: `3550` - // Minimum execution time: 41_473_000 picoseconds. - Weight::from_parts(44_155_000, 0) + // Minimum execution time: 50_887_000 picoseconds. + Weight::from_parts(57_366_000, 0) .saturating_add(Weight::from_parts(0, 3550)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -313,8 +311,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `463` // Estimated: `3533` - // Minimum execution time: 56_672_000 picoseconds. - Weight::from_parts(58_086_000, 0) + // Minimum execution time: 84_472_000 picoseconds. + Weight::from_parts(96_536_000, 0) .saturating_add(Weight::from_parts(0, 3533)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -331,8 +329,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `857` // Estimated: `3593` - // Minimum execution time: 64_460_000 picoseconds. - Weight::from_parts(65_894_000, 0) + // Minimum execution time: 96_371_000 picoseconds. + Weight::from_parts(104_659_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) @@ -345,8 +343,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `957` // Estimated: `4698` - // Minimum execution time: 37_447_000 picoseconds. - Weight::from_parts(42_318_000, 0) + // Minimum execution time: 51_741_000 picoseconds. + Weight::from_parts(54_461_000, 0) .saturating_add(Weight::from_parts(0, 4698)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -366,8 +364,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 21_219_000 picoseconds. - Weight::from_parts(22_084_648, 0) + // Minimum execution time: 19_901_000 picoseconds. + Weight::from_parts(21_028_116, 0) .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -379,11 +377,11 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `266` // Estimated: `1487` - // Minimum execution time: 5_792_000 picoseconds. - Weight::from_parts(6_358_588, 0) + // Minimum execution time: 5_987_000 picoseconds. + Weight::from_parts(6_412_478, 0) .saturating_add(Weight::from_parts(0, 1487)) - // Standard Error: 20 - .saturating_add(Weight::from_parts(26, 0).saturating_mul(n.into())) + // Standard Error: 16 + .saturating_add(Weight::from_parts(47, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -397,8 +395,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `447` // Estimated: `6196` - // Minimum execution time: 38_690_000 picoseconds. - Weight::from_parts(39_706_000, 0) + // Minimum execution time: 38_623_000 picoseconds. + Weight::from_parts(39_773_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) @@ -414,15 +412,13 @@ impl pallet_broker::WeightInfo for WeightInfo { /// Storage: `Broker::Workplan` (r:0 w:60) /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. - fn rotate_sale(n: u32, ) -> Weight { + fn rotate_sale(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `12514` // Estimated: `13506` - // Minimum execution time: 93_531_000 picoseconds. - Weight::from_parts(95_836_318, 0) + // Minimum execution time: 97_074_000 picoseconds. + Weight::from_parts(101_247_740, 0) .saturating_add(Weight::from_parts(0, 13506)) - // Standard Error: 113 - .saturating_add(Weight::from_parts(329, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(65)) } @@ -434,8 +430,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3493` - // Minimum execution time: 6_506_000 picoseconds. - Weight::from_parts(6_783_000, 0) + // Minimum execution time: 6_317_000 picoseconds. + Weight::from_parts(6_521_000, 0) .saturating_add(Weight::from_parts(0, 3493)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -458,8 +454,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1321` // Estimated: `4786` - // Minimum execution time: 31_927_000 picoseconds. - Weight::from_parts(32_748_000, 0) + // Minimum execution time: 32_575_000 picoseconds. + Weight::from_parts(33_299_000, 0) .saturating_add(Weight::from_parts(0, 4786)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -478,8 +474,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 15_682_000 picoseconds. - Weight::from_parts(16_012_000, 0) + // Minimum execution time: 15_256_000 picoseconds. + Weight::from_parts(15_927_000, 0) .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -490,8 +486,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_147_000 picoseconds. - Weight::from_parts(2_281_000, 0) + // Minimum execution time: 1_783_000 picoseconds. + Weight::from_parts(1_904_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -509,10 +505,22 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `398` // Estimated: `3863` - // Minimum execution time: 12_015_000 picoseconds. - Weight::from_parts(12_619_000, 0) + // Minimum execution time: 12_307_000 picoseconds. + Weight::from_parts(12_967_000, 0) .saturating_add(Weight::from_parts(0, 3863)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `Broker::Leases` (r:1 w:1) + /// Proof: `Broker::Leases` (`max_values`: Some(1), `max_size`: Some(401), added: 896, mode: `MaxEncodedLen`) + fn swap_leases() -> Weight { + // Proof Size summary in bytes: + // Measured: `470` + // Estimated: `1886` + // Minimum execution time: 6_597_000 picoseconds. + Weight::from_parts(6_969_000, 0) + .saturating_add(Weight::from_parts(0, 1886)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_xcm.rs index 0e34cba4aaf5d9879dfecc97ffd736b985c98f23..df0044089c8f6fb558d368e2f006a6a4fbd9fb97 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_xcm.rs @@ -16,26 +16,24 @@ //! Autogenerated weights for `pallet_xcm` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-01-12, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-j8vvqcjr-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-rococo-dev")`, DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=coretime-rococo-dev -// --wasm-execution=compiled -// --pallet=pallet_xcm -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --extrinsic=* // --steps=50 // --repeat=20 -// --json +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm +// --chain=coretime-rococo-dev // --header=./cumulus/file_header.txt // --output=./cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/ @@ -64,8 +62,28 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 22_669_000 picoseconds. - Weight::from_parts(23_227_000, 0) + // Minimum execution time: 18_767_000 picoseconds. + Weight::from_parts(19_420_000, 0) + .saturating_add(Weight::from_parts(0, 3539)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `74` + // Estimated: `3539` + // Minimum execution time: 19_184_000 picoseconds. + Weight::from_parts(19_695_000, 0) .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -86,8 +104,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 64_486_000 picoseconds. - Weight::from_parts(65_247_000, 0) + // Minimum execution time: 58_120_000 picoseconds. + Weight::from_parts(59_533_000, 0) .saturating_add(Weight::from_parts(0, 3571)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -122,14 +140,24 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_020_000 picoseconds. - Weight::from_parts(7_300_000, 0) + // Minimum execution time: 6_074_000 picoseconds. + Weight::from_parts(6_398_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -139,8 +167,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_022_000 picoseconds. - Weight::from_parts(2_141_000, 0) + // Minimum execution time: 2_036_000 picoseconds. + Weight::from_parts(2_180_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -164,8 +192,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 26_893_000 picoseconds. - Weight::from_parts(27_497_000, 0) + // Minimum execution time: 25_014_000 picoseconds. + Weight::from_parts(25_374_000, 0) .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) @@ -188,8 +216,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `292` // Estimated: `3757` - // Minimum execution time: 29_673_000 picoseconds. - Weight::from_parts(30_693_000, 0) + // Minimum execution time: 27_616_000 picoseconds. + Weight::from_parts(28_499_000, 0) .saturating_add(Weight::from_parts(0, 3757)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) @@ -200,45 +228,45 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_990_000 picoseconds. - Weight::from_parts(2_105_000, 0) + // Minimum execution time: 2_061_000 picoseconds. + Weight::from_parts(2_153_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `PolkadotXcm::SupportedVersion` (r:4 w:2) + /// Storage: `PolkadotXcm::SupportedVersion` (r:5 w:2) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `95` - // Estimated: `10985` - // Minimum execution time: 14_819_000 picoseconds. - Weight::from_parts(15_180_000, 0) - .saturating_add(Weight::from_parts(0, 10985)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `89` + // Estimated: `13454` + // Minimum execution time: 16_592_000 picoseconds. + Weight::from_parts(16_900_000, 0) + .saturating_add(Weight::from_parts(0, 13454)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifiers` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `99` - // Estimated: `10989` - // Minimum execution time: 14_935_000 picoseconds. - Weight::from_parts(15_335_000, 0) - .saturating_add(Weight::from_parts(0, 10989)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `93` + // Estimated: `13458` + // Minimum execution time: 16_694_000 picoseconds. + Weight::from_parts(16_905_000, 0) + .saturating_add(Weight::from_parts(0, 13458)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:6 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `13471` - // Minimum execution time: 16_278_000 picoseconds. - Weight::from_parts(16_553_000, 0) - .saturating_add(Weight::from_parts(0, 13471)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `15946` + // Minimum execution time: 17_779_000 picoseconds. + Weight::from_parts(18_490_000, 0) + .saturating_add(Weight::from_parts(0, 15946)) + .saturating_add(T::DbWeight::get().reads(6)) } /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:2 w:1) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -256,36 +284,36 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `142` // Estimated: `6082` - // Minimum execution time: 26_360_000 picoseconds. - Weight::from_parts(26_868_000, 0) + // Minimum execution time: 24_526_000 picoseconds. + Weight::from_parts(25_182_000, 0) .saturating_add(Weight::from_parts(0, 6082)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:3 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `136` - // Estimated: `8551` - // Minimum execution time: 8_615_000 picoseconds. - Weight::from_parts(8_903_000, 0) - .saturating_add(Weight::from_parts(0, 8551)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `11026` + // Minimum execution time: 10_467_000 picoseconds. + Weight::from_parts(10_934_000, 0) + .saturating_add(Weight::from_parts(0, 11026)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `106` - // Estimated: `10996` - // Minimum execution time: 15_284_000 picoseconds. - Weight::from_parts(15_504_000, 0) - .saturating_add(Weight::from_parts(0, 10996)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `100` + // Estimated: `13465` + // Minimum execution time: 16_377_000 picoseconds. + Weight::from_parts(17_114_000, 0) + .saturating_add(Weight::from_parts(0, 13465)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -299,12 +327,12 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `148` - // Estimated: `11038` - // Minimum execution time: 32_675_000 picoseconds. - Weight::from_parts(33_816_000, 0) - .saturating_add(Weight::from_parts(0, 11038)) - .saturating_add(T::DbWeight::get().reads(9)) + // Measured: `142` + // Estimated: `13507` + // Minimum execution time: 32_575_000 picoseconds. + Weight::from_parts(33_483_000, 0) + .saturating_add(Weight::from_parts(0, 13507)) + .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) @@ -315,8 +343,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `1517` - // Minimum execution time: 4_058_000 picoseconds. - Weight::from_parts(4_170_000, 0) + // Minimum execution time: 3_604_000 picoseconds. + Weight::from_parts(3_744_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -327,10 +355,22 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7669` // Estimated: `11134` - // Minimum execution time: 25_375_000 picoseconds. - Weight::from_parts(26_026_000, 0) + // Minimum execution time: 23_983_000 picoseconds. + Weight::from_parts(24_404_000, 0) .saturating_add(Weight::from_parts(0, 11134)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `90` + // Estimated: `3555` + // Minimum execution time: 34_446_000 picoseconds. + Weight::from_parts(35_465_000, 0) + .saturating_add(Weight::from_parts(0, 3555)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs index 37bb8809dabac0ce52192dc64d0aa54ee136dc25..955f2eeba339c0e99a8364377dfc27ccc7557e80 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs @@ -192,7 +192,7 @@ pub type Barrier = TrailingSetTopicAsId< AllowKnownQueryResponses, WithComputedOrigin< ( - // If the message is one that immediately attemps to pay for execution, then + // If the message is one that immediately attempts to pay for execution, then // allow it. AllowTopLevelPaidExecutionFrom, // Parent and its pluralities (i.e. governance bodies) get free execution. @@ -257,6 +257,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } /// Converts a local signed origin into an XCM location. Forms the basis for local origins diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml b/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml index a7d52dfd7849ec1a20ef400bd45f995374697409..b8efecffc5075be04fb3a52410ae86410a01e48d 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml @@ -190,6 +190,4 @@ try-runtime = [ "sp-runtime/try-runtime", ] -experimental = ["pallet-aura/experimental"] - fast-runtime = [] diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index 85d70d2f17b6416c5243d3195808786ee7a19878..c31e474cc2f1718b5de4a8ebb88a19072deba1ba 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -133,7 +133,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("coretime-westend"), impl_name: create_runtime_str!("coretime-westend"), authoring_version: 1, - spec_version: 1_007_001, + spec_version: 1_009_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 0, @@ -172,7 +172,7 @@ parameter_types! { } // Configure FRAME pallets to include in runtime. -#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig)] impl frame_system::Config for Runtime { /// The identifier used to distinguish between accounts. type AccountId = AccountId; @@ -206,10 +206,7 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; - #[cfg(feature = "experimental")] type MinimumPeriod = ConstU64<0>; - #[cfg(not(feature = "experimental"))] - type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = weights::pallet_timestamp::WeightInfo; } @@ -369,7 +366,6 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; type AllowMultipleBlocksPerSlot = ConstBool; - #[cfg(feature = "experimental")] type SlotDuration = ConstU64; } @@ -490,7 +486,7 @@ impl_runtime_apis! { } fn authorities() -> Vec { - Aura::authorities().into_inner() + pallet_aura::Authorities::::get().into_inner() } } @@ -512,7 +508,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -706,6 +702,12 @@ impl_runtime_apis! { use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -714,7 +716,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between AH and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, Parent.into(), @@ -725,6 +727,13 @@ impl_runtime_apis! { // Reserve transfers are disabled None } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(ExistentialDeposit::get()), + } + } } parameter_types! { diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs index 8727b9633b1f0eaca2bcaa5f7a43d832f6abbe9b..13d5fcf3898bcc07fce13c7ee2deeb8c2b9fa76f 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs @@ -17,25 +17,23 @@ //! Autogenerated weights for `pallet_broker` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-02-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-03-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-westend-dev")`, DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=coretime-westend-dev -// --wasm-execution=compiled -// --pallet=pallet_broker -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --extrinsic=* // --steps=50 // --repeat=20 -// --json +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_broker +// --chain=coretime-westend-dev // --header=./cumulus/file_header.txt // --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ @@ -56,8 +54,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_944_000 picoseconds. - Weight::from_parts(2_045_000, 0) + // Minimum execution time: 1_897_000 picoseconds. + Weight::from_parts(2_053_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -67,8 +65,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `10888` // Estimated: `13506` - // Minimum execution time: 21_158_000 picoseconds. - Weight::from_parts(21_572_000, 0) + // Minimum execution time: 22_550_000 picoseconds. + Weight::from_parts(22_871_000, 0) .saturating_add(Weight::from_parts(0, 13506)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -79,8 +77,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `12090` // Estimated: `13506` - // Minimum execution time: 20_497_000 picoseconds. - Weight::from_parts(20_995_000, 0) + // Minimum execution time: 21_170_000 picoseconds. + Weight::from_parts(21_645_000, 0) .saturating_add(Weight::from_parts(0, 13506)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -95,8 +93,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `146` // Estimated: `1631` - // Minimum execution time: 10_280_000 picoseconds. - Weight::from_parts(10_686_000, 0) + // Minimum execution time: 10_494_000 picoseconds. + Weight::from_parts(10_942_000, 0) .saturating_add(Weight::from_parts(0, 1631)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -120,15 +118,13 @@ impl pallet_broker::WeightInfo for WeightInfo { /// Storage: `Broker::Workplan` (r:0 w:20) /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. - fn start_sales(n: u32, ) -> Weight { + fn start_sales(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `12247` // Estimated: `13732` - // Minimum execution time: 61_020_000 picoseconds. - Weight::from_parts(63_240_622, 0) + // Minimum execution time: 61_014_000 picoseconds. + Weight::from_parts(63_267_651, 0) .saturating_add(Weight::from_parts(0, 13732)) - // Standard Error: 102 - .saturating_add(Weight::from_parts(255, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(26)) } @@ -144,8 +140,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `316` // Estimated: `3593` - // Minimum execution time: 30_627_000 picoseconds. - Weight::from_parts(31_648_000, 0) + // Minimum execution time: 30_931_000 picoseconds. + Weight::from_parts(31_941_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -166,8 +162,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `434` // Estimated: `4698` - // Minimum execution time: 57_701_000 picoseconds. - Weight::from_parts(59_825_000, 0) + // Minimum execution time: 57_466_000 picoseconds. + Weight::from_parts(65_042_000, 0) .saturating_add(Weight::from_parts(0, 4698)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) @@ -178,8 +174,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `357` // Estimated: `3550` - // Minimum execution time: 12_898_000 picoseconds. - Weight::from_parts(13_506_000, 0) + // Minimum execution time: 12_799_000 picoseconds. + Weight::from_parts(13_401_000, 0) .saturating_add(Weight::from_parts(0, 3550)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -190,8 +186,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `357` // Estimated: `3550` - // Minimum execution time: 14_284_000 picoseconds. - Weight::from_parts(14_791_000, 0) + // Minimum execution time: 14_107_000 picoseconds. + Weight::from_parts(14_630_000, 0) .saturating_add(Weight::from_parts(0, 3550)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -202,8 +198,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `357` // Estimated: `3550` - // Minimum execution time: 15_570_000 picoseconds. - Weight::from_parts(16_158_000, 0) + // Minimum execution time: 15_254_000 picoseconds. + Weight::from_parts(16_062_000, 0) .saturating_add(Weight::from_parts(0, 3550)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(3)) @@ -220,8 +216,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `735` // Estimated: `4681` - // Minimum execution time: 23_329_000 picoseconds. - Weight::from_parts(24_196_000, 0) + // Minimum execution time: 23_557_000 picoseconds. + Weight::from_parts(24_382_000, 0) .saturating_add(Weight::from_parts(0, 4681)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(2)) @@ -240,8 +236,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `801` // Estimated: `5996` - // Minimum execution time: 29_288_000 picoseconds. - Weight::from_parts(30_066_000, 0) + // Minimum execution time: 29_371_000 picoseconds. + Weight::from_parts(30_200_000, 0) .saturating_add(Weight::from_parts(0, 5996)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(5)) @@ -257,11 +253,11 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `652` // Estimated: `6196 + m * (2520 ±0)` - // Minimum execution time: 54_833_000 picoseconds. - Weight::from_parts(55_577_423, 0) + // Minimum execution time: 54_331_000 picoseconds. + Weight::from_parts(55_322_165, 0) .saturating_add(Weight::from_parts(0, 6196)) - // Standard Error: 35_105 - .saturating_add(Weight::from_parts(1_267_911, 0).saturating_mul(m.into())) + // Standard Error: 35_225 + .saturating_add(Weight::from_parts(1_099_614, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().writes(5)) @@ -283,8 +279,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `215` // Estimated: `3680` - // Minimum execution time: 55_289_000 picoseconds. - Weight::from_parts(56_552_000, 0) + // Minimum execution time: 53_789_000 picoseconds. + Weight::from_parts(55_439_000, 0) .saturating_add(Weight::from_parts(0, 3680)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) @@ -297,8 +293,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `465` // Estimated: `3550` - // Minimum execution time: 39_736_000 picoseconds. - Weight::from_parts(41_346_000, 0) + // Minimum execution time: 43_941_000 picoseconds. + Weight::from_parts(49_776_000, 0) .saturating_add(Weight::from_parts(0, 3550)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -313,8 +309,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `463` // Estimated: `3533` - // Minimum execution time: 57_319_000 picoseconds. - Weight::from_parts(60_204_000, 0) + // Minimum execution time: 64_917_000 picoseconds. + Weight::from_parts(70_403_000, 0) .saturating_add(Weight::from_parts(0, 3533)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -331,8 +327,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `857` // Estimated: `3593` - // Minimum execution time: 85_216_000 picoseconds. - Weight::from_parts(91_144_000, 0) + // Minimum execution time: 72_633_000 picoseconds. + Weight::from_parts(79_305_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) @@ -345,8 +341,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `556` // Estimated: `4698` - // Minimum execution time: 32_331_000 picoseconds. - Weight::from_parts(39_877_000, 0) + // Minimum execution time: 36_643_000 picoseconds. + Weight::from_parts(48_218_000, 0) .saturating_add(Weight::from_parts(0, 4698)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -362,28 +358,28 @@ impl pallet_broker::WeightInfo for WeightInfo { /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `n` is `[0, 1000]`. - fn request_core_count(n: u32, ) -> Weight { + fn request_core_count(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 18_128_000 picoseconds. - Weight::from_parts(19_061_234, 0) + // Minimum execution time: 17_617_000 picoseconds. + Weight::from_parts(18_904_788, 0) .saturating_add(Weight::from_parts(0, 3539)) - // Standard Error: 48 - .saturating_add(Weight::from_parts(141, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Broker::CoreCountInbox` (r:1 w:1) /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. - fn process_core_count(_n: u32, ) -> Weight { + fn process_core_count(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `266` // Estimated: `1487` - // Minimum execution time: 5_368_000 picoseconds. - Weight::from_parts(5_837_005, 0) + // Minimum execution time: 5_575_000 picoseconds. + Weight::from_parts(5_887_598, 0) .saturating_add(Weight::from_parts(0, 1487)) + // Standard Error: 16 + .saturating_add(Weight::from_parts(41, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -397,8 +393,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `447` // Estimated: `6196` - // Minimum execution time: 36_047_000 picoseconds. - Weight::from_parts(37_101_000, 0) + // Minimum execution time: 36_415_000 picoseconds. + Weight::from_parts(37_588_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) @@ -414,13 +410,15 @@ impl pallet_broker::WeightInfo for WeightInfo { /// Storage: `Broker::Workplan` (r:0 w:20) /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. - fn rotate_sale(_n: u32, ) -> Weight { + fn rotate_sale(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `12194` // Estimated: `13506` - // Minimum execution time: 48_158_000 picoseconds. - Weight::from_parts(49_891_920, 0) + // Minimum execution time: 48_362_000 picoseconds. + Weight::from_parts(49_616_106, 0) .saturating_add(Weight::from_parts(0, 13506)) + // Standard Error: 61 + .saturating_add(Weight::from_parts(59, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(25)) } @@ -432,8 +430,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3493` - // Minimum execution time: 5_911_000 picoseconds. - Weight::from_parts(6_173_000, 0) + // Minimum execution time: 6_148_000 picoseconds. + Weight::from_parts(6_374_000, 0) .saturating_add(Weight::from_parts(0, 3493)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -456,8 +454,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1321` // Estimated: `4786` - // Minimum execution time: 30_140_000 picoseconds. - Weight::from_parts(30_912_000, 0) + // Minimum execution time: 30_267_000 picoseconds. + Weight::from_parts(30_825_000, 0) .saturating_add(Weight::from_parts(0, 4786)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -476,8 +474,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 13_684_000 picoseconds. - Weight::from_parts(14_252_000, 0) + // Minimum execution time: 13_491_000 picoseconds. + Weight::from_parts(13_949_000, 0) .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -488,8 +486,8 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_718_000 picoseconds. - Weight::from_parts(1_843_000, 0) + // Minimum execution time: 1_711_000 picoseconds. + Weight::from_parts(1_913_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -507,10 +505,22 @@ impl pallet_broker::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `398` // Estimated: `3863` - // Minimum execution time: 11_771_000 picoseconds. - Weight::from_parts(12_120_000, 0) + // Minimum execution time: 12_035_000 picoseconds. + Weight::from_parts(12_383_000, 0) .saturating_add(Weight::from_parts(0, 3863)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `Broker::Leases` (r:1 w:1) + /// Proof: `Broker::Leases` (`max_values`: Some(1), `max_size`: Some(81), added: 576, mode: `MaxEncodedLen`) + fn swap_leases() -> Weight { + // Proof Size summary in bytes: + // Measured: `150` + // Estimated: `1566` + // Minimum execution time: 6_142_000 picoseconds. + Weight::from_parts(6_538_000, 0) + .saturating_add(Weight::from_parts(0, 1566)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_xcm.rs index d821a581b0dd7bae506f98a52bd28086b9d3c450..a1701c5f1c2ced1bbdcac49863cc05ceb28a019f 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_xcm.rs @@ -17,25 +17,23 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-02-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-westend-dev")`, DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=coretime-westend-dev -// --wasm-execution=compiled -// --pallet=pallet_xcm -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --extrinsic=* // --steps=50 // --repeat=20 -// --json +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm +// --chain=coretime-westend-dev // --header=./cumulus/file_header.txt // --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ @@ -64,8 +62,28 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 17_946_000 picoseconds. - Weight::from_parts(18_398_000, 0) + // Minimum execution time: 17_681_000 picoseconds. + Weight::from_parts(18_350_000, 0) + .saturating_add(Weight::from_parts(0, 3539)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `74` + // Estimated: `3539` + // Minimum execution time: 18_091_000 picoseconds. + Weight::from_parts(18_327_000, 0) .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -86,8 +104,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 47_982_000 picoseconds. - Weight::from_parts(49_215_000, 0) + // Minimum execution time: 54_943_000 picoseconds. + Weight::from_parts(56_519_000, 0) .saturating_add(Weight::from_parts(0, 3571)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -122,14 +140,24 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_042_000 picoseconds. - Weight::from_parts(6_257_000, 0) + // Minimum execution time: 5_887_000 picoseconds. + Weight::from_parts(6_101_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -139,8 +167,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_845_000 picoseconds. - Weight::from_parts(1_993_000, 0) + // Minimum execution time: 1_940_000 picoseconds. + Weight::from_parts(2_022_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -164,8 +192,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 24_062_000 picoseconds. - Weight::from_parts(24_666_000, 0) + // Minimum execution time: 23_165_000 picoseconds. + Weight::from_parts(23_800_000, 0) .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) @@ -188,8 +216,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `292` // Estimated: `3757` - // Minimum execution time: 26_486_000 picoseconds. - Weight::from_parts(27_528_000, 0) + // Minimum execution time: 26_506_000 picoseconds. + Weight::from_parts(27_180_000, 0) .saturating_add(Weight::from_parts(0, 3757)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) @@ -200,8 +228,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_881_000 picoseconds. - Weight::from_parts(2_008_000, 0) + // Minimum execution time: 1_868_000 picoseconds. + Weight::from_parts(2_002_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -211,8 +239,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `89` // Estimated: `13454` - // Minimum execution time: 15_971_000 picoseconds. - Weight::from_parts(16_455_000, 0) + // Minimum execution time: 16_138_000 picoseconds. + Weight::from_parts(16_447_000, 0) .saturating_add(Weight::from_parts(0, 13454)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -223,8 +251,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `93` // Estimated: `13458` - // Minimum execution time: 16_603_000 picoseconds. - Weight::from_parts(17_037_000, 0) + // Minimum execution time: 16_099_000 picoseconds. + Weight::from_parts(16_592_000, 0) .saturating_add(Weight::from_parts(0, 13458)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -235,8 +263,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `15946` - // Minimum execution time: 17_821_000 picoseconds. - Weight::from_parts(18_200_000, 0) + // Minimum execution time: 17_972_000 picoseconds. + Weight::from_parts(18_379_000, 0) .saturating_add(Weight::from_parts(0, 15946)) .saturating_add(T::DbWeight::get().reads(6)) } @@ -256,8 +284,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `142` // Estimated: `6082` - // Minimum execution time: 23_878_000 picoseconds. - Weight::from_parts(24_721_000, 0) + // Minimum execution time: 23_554_000 picoseconds. + Weight::from_parts(24_446_000, 0) .saturating_add(Weight::from_parts(0, 6082)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) @@ -268,8 +296,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `136` // Estimated: `11026` - // Minimum execution time: 10_566_000 picoseconds. - Weight::from_parts(11_053_000, 0) + // Minimum execution time: 10_541_000 picoseconds. + Weight::from_parts(10_894_000, 0) .saturating_add(Weight::from_parts(0, 11026)) .saturating_add(T::DbWeight::get().reads(4)) } @@ -279,8 +307,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `100` // Estimated: `13465` - // Minimum execution time: 16_020_000 picoseconds. - Weight::from_parts(16_619_000, 0) + // Minimum execution time: 16_404_000 picoseconds. + Weight::from_parts(16_818_000, 0) .saturating_add(Weight::from_parts(0, 13465)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -301,8 +329,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `142` // Estimated: `13507` - // Minimum execution time: 32_136_000 picoseconds. - Weight::from_parts(32_610_000, 0) + // Minimum execution time: 31_617_000 picoseconds. + Weight::from_parts(32_336_000, 0) .saturating_add(Weight::from_parts(0, 13507)) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(4)) @@ -315,8 +343,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `1517` - // Minimum execution time: 3_336_000 picoseconds. - Weight::from_parts(3_434_000, 0) + // Minimum execution time: 3_328_000 picoseconds. + Weight::from_parts(3_501_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -327,10 +355,22 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7669` // Estimated: `11134` - // Minimum execution time: 23_977_000 picoseconds. - Weight::from_parts(24_413_000, 0) + // Minimum execution time: 23_571_000 picoseconds. + Weight::from_parts(24_312_000, 0) .saturating_add(Weight::from_parts(0, 11134)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `90` + // Estimated: `3555` + // Minimum execution time: 32_879_000 picoseconds. + Weight::from_parts(33_385_000, 0) + .saturating_add(Weight::from_parts(0, 3555)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs index 44049adf02711bd3b828f98bc3b90ab330e19317..fc7d36a8ba1854f1a52b7630d98b545a28868c74 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs @@ -198,7 +198,7 @@ pub type Barrier = TrailingSetTopicAsId< AllowKnownQueryResponses, WithComputedOrigin< ( - // If the message is one that immediately attemps to pay for execution, then + // If the message is one that immediately attempts to pay for execution, then // allow it. AllowTopLevelPaidExecutionFrom, // Parent, its pluralities (i.e. governance bodies), and the Fellows plurality @@ -269,6 +269,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } /// Converts a local signed origin into an XCM location. Forms the basis for local origins diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/Cargo.toml b/cumulus/parachains/runtimes/glutton/glutton-westend/Cargo.toml index 23c5ce1c7f80d46ed193b37963b96d6fd55243c6..a357bf519e40fcb7b14ae0b2c4e92b0eb6fbb94f 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/Cargo.toml @@ -135,8 +135,6 @@ try-runtime = [ "sp-runtime/try-runtime", ] -experimental = ["pallet-aura/experimental"] - # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs index 10408aaf39a7611f93178802e17403ed4fd90838..cee17cdc7b05de2b9f0736248583cb984547f5f9 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs @@ -100,7 +100,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("glutton-westend"), impl_name: create_runtime_str!("glutton-westend"), authoring_version: 1, - spec_version: 1_007_000, + spec_version: 1_009_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -146,7 +146,7 @@ parameter_types! { pub const SS58Prefix: u8 = 42; } -#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig)] impl frame_system::Config for Runtime { type AccountId = AccountId; type Nonce = Nonce; @@ -221,10 +221,7 @@ impl cumulus_pallet_aura_ext::Config for Runtime {} impl pallet_timestamp::Config for Runtime { type Moment = u64; type OnTimestampSet = Aura; - #[cfg(feature = "experimental")] type MinimumPeriod = ConstU64<0>; - #[cfg(not(feature = "experimental"))] - type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = weights::pallet_timestamp::WeightInfo; } @@ -233,7 +230,6 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; type AllowMultipleBlocksPerSlot = ConstBool; - #[cfg(feature = "experimental")] type SlotDuration = ConstU64; } @@ -332,7 +328,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -357,7 +353,7 @@ impl_runtime_apis! { } fn authorities() -> Vec { - Aura::authorities().into_inner() + pallet_aura::Authorities::::get().into_inner() } } diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs index ad61987c0e7048f2d94b29ff51906e197f5ad2fd..15bb519e115c5c2eaca25053a3d3e47c30fb21e0 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs @@ -88,6 +88,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml b/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml index c0b8fb7636b5898861c0b12912c58fbf0d43f924..eebd662c3fd5728f18b4dfc620d2b8f4c707f130 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml @@ -188,5 +188,3 @@ try-runtime = [ "polkadot-runtime-common/try-runtime", "sp-runtime/try-runtime", ] - -experimental = ["pallet-aura/experimental"] diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs index 570dc0fa12c32f0f232fc6108c5e3373a2695a0d..cd5f1ad3272805d59c4fc3f16043ee875cc6dab1 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs @@ -126,7 +126,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("people-rococo"), impl_name: create_runtime_str!("people-rococo"), authoring_version: 1, - spec_version: 1_007_000, + spec_version: 1_009_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 0, @@ -164,7 +164,7 @@ parameter_types! { pub const SS58Prefix: u8 = 42; } -#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = Everything; type BlockWeights = RuntimeBlockWeights; @@ -187,10 +187,7 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; - #[cfg(feature = "experimental")] type MinimumPeriod = ConstU64<0>; - #[cfg(not(feature = "experimental"))] - type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = weights::pallet_timestamp::WeightInfo; } @@ -337,7 +334,6 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; type AllowMultipleBlocksPerSlot = ConstBool; - #[cfg(feature = "experimental")] type SlotDuration = ConstU64; } @@ -460,7 +456,7 @@ mod benches { [cumulus_pallet_xcmp_queue, XcmpQueue] [pallet_collator_selection, CollatorSelection] // XCM - [pallet_xcm, PalletXcmExtrinsiscsBenchmark::] + [pallet_xcm, PalletXcmExtrinsicsBenchmark::] [pallet_xcm_benchmarks::fungible, XcmBalances] [pallet_xcm_benchmarks::generic, XcmGeneric] ); @@ -473,7 +469,7 @@ impl_runtime_apis! { } fn authorities() -> Vec { - Aura::authorities().into_inner() + pallet_aura::Authorities::::get().into_inner() } } @@ -495,7 +491,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -648,7 +644,7 @@ impl_runtime_apis! { use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; - use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark; + use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; // This is defined once again in dispatch_benchmark, because list_benchmarks! // and add_benchmarks! are macros exported by define_benchmarks! macros and those types @@ -684,8 +680,14 @@ impl_runtime_apis! { use cumulus_pallet_session_benchmarking::Pallet as SessionBench; impl cumulus_pallet_session_benchmarking::Config for Runtime {} - use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark; + use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -694,7 +696,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between People and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, Parent.into(), @@ -704,6 +706,13 @@ impl_runtime_apis! { fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> { None } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(ExistentialDeposit::get()), + } + } } use xcm::latest::prelude::*; diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_xcm.rs index 0f793524de9f5ef7da835090f9d81008ba756d59..ac494fdc719f4139a79b9f92525bde8322267af2 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_xcm.rs @@ -1,40 +1,41 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . //! Autogenerated weights for `pallet_xcm` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("people-kusama-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("people-rococo-dev")`, DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=people-kusama-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_xcm -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/people/people-kusama/src/weights/pallet_xcm.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm +// --chain=people-rococo-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/people/people-rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,57 +48,48 @@ use core::marker::PhantomData; /// Weight functions for `pallet_xcm`. pub struct WeightInfo(PhantomData); impl pallet_xcm::WeightInfo for WeightInfo { - /// Storage: PolkadotXcm SupportedVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem HostConfiguration (r:1 w:0) - /// Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - /// Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn send() -> Weight { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 25_931_000 picoseconds. - Weight::from_parts(26_340_000, 0) + // Minimum execution time: 17_935_000 picoseconds. + Weight::from_parts(18_482_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: ParachainInfo ParachainId (r:1 w:0) - /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn teleport_assets() -> Weight { - // Proof Size summary in bytes: - // Measured: `32` - // Estimated: `1489` - // Minimum execution time: 25_691_000 picoseconds. - Weight::from_parts(25_971_000, 0) - .saturating_add(Weight::from_parts(0, 1489)) - .saturating_add(T::DbWeight::get().reads(1)) - } - /// Storage: Benchmark Override (r:0 w:0) - /// Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) - fn reserve_transfer_assets() -> Weight { + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. - Weight::from_parts(18_446_744_073_709_551_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `38` + // Estimated: `3503` + // Minimum execution time: 18_311_000 picoseconds. + Weight::from_parts(18_850_000, 0) + .saturating_add(Weight::from_parts(0, 3503)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:2 w:2) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) - /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) @@ -108,18 +100,38 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn teleport_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `70` + // Estimated: `3535` + // Minimum execution time: 56_182_000 picoseconds. + Weight::from_parts(58_136_000, 0) + .saturating_add(Weight::from_parts(0, 3535)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn reserve_transfer_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) fn transfer_assets() -> Weight { // Proof Size summary in bytes: - // Measured: `496` - // Estimated: `6208` - // Minimum execution time: 146_932_000 picoseconds. - Weight::from_parts(153_200_000, 0) - .saturating_add(Weight::from_parts(0, 6208)) - .saturating_add(T::DbWeight::get().reads(12)) - .saturating_add(T::DbWeight::get().writes(7)) + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) } - /// Storage: Benchmark Override (r:0 w:0) - /// Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) fn execute() -> Weight { // Proof Size summary in bytes: // Measured: `0` @@ -128,189 +140,199 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } - /// Storage: PolkadotXcm SupportedVersion (r:0 w:1) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_572_000 picoseconds. - Weight::from_parts(9_924_000, 0) + // Minimum execution time: 5_979_000 picoseconds. + Weight::from_parts(6_289_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) - /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:0 w:1) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn force_default_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_997_000 picoseconds. - Weight::from_parts(3_136_000, 0) + // Minimum execution time: 1_853_000 picoseconds. + Weight::from_parts(2_045_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: PolkadotXcm VersionNotifiers (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionNotifiers (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm QueryCounter (r:1 w:1) - /// Proof Skipped: PolkadotXcm QueryCounter (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: PolkadotXcm SupportedVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem HostConfiguration (r:1 w:0) - /// Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - /// Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: PolkadotXcm Queries (r:0 w:1) - /// Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) + /// Proof: `PolkadotXcm::QueryCounter` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_subscribe_version_notify() -> Weight { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 30_271_000 picoseconds. - Weight::from_parts(30_819_000, 0) + // Minimum execution time: 23_827_000 picoseconds. + Weight::from_parts(24_493_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) } - /// Storage: PolkadotXcm VersionNotifiers (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionNotifiers (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm SupportedVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem HostConfiguration (r:1 w:0) - /// Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - /// Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: PolkadotXcm Queries (r:0 w:1) - /// Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_unsubscribe_version_notify() -> Weight { // Proof Size summary in bytes: - // Measured: `220` - // Estimated: `3685` - // Minimum execution time: 32_302_000 picoseconds. - Weight::from_parts(32_807_000, 0) - .saturating_add(Weight::from_parts(0, 3685)) + // Measured: `255` + // Estimated: `3720` + // Minimum execution time: 26_755_000 picoseconds. + Weight::from_parts(27_125_000, 0) + .saturating_add(Weight::from_parts(0, 3720)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: PolkadotXcm XcmExecutionSuspended (r:0 w:1) - /// Proof Skipped: PolkadotXcm XcmExecutionSuspended (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::XcmExecutionSuspended` (r:0 w:1) + /// Proof: `PolkadotXcm::XcmExecutionSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn force_suspension() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_960_000 picoseconds. - Weight::from_parts(3_094_000, 0) + // Minimum execution time: 1_898_000 picoseconds. + Weight::from_parts(2_028_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: PolkadotXcm SupportedVersion (r:4 w:2) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::SupportedVersion` (r:5 w:2) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `95` - // Estimated: `10985` - // Minimum execution time: 14_877_000 picoseconds. - Weight::from_parts(15_296_000, 0) - .saturating_add(Weight::from_parts(0, 10985)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `89` + // Estimated: `13454` + // Minimum execution time: 16_300_000 picoseconds. + Weight::from_parts(16_995_000, 0) + .saturating_add(Weight::from_parts(0, 13454)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: PolkadotXcm VersionNotifiers (r:4 w:2) - /// Proof Skipped: PolkadotXcm VersionNotifiers (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:5 w:2) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `99` - // Estimated: `10989` - // Minimum execution time: 14_835_000 picoseconds. - Weight::from_parts(15_115_000, 0) - .saturating_add(Weight::from_parts(0, 10989)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `93` + // Estimated: `13458` + // Minimum execution time: 16_495_000 picoseconds. + Weight::from_parts(16_950_000, 0) + .saturating_add(Weight::from_parts(0, 13458)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:5 w:0) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:6 w:0) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `13471` - // Minimum execution time: 15_368_000 picoseconds. - Weight::from_parts(15_596_000, 0) - .saturating_add(Weight::from_parts(0, 13471)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `15946` + // Minimum execution time: 18_153_000 picoseconds. + Weight::from_parts(18_595_000, 0) + .saturating_add(Weight::from_parts(0, 15946)) + .saturating_add(T::DbWeight::get().reads(6)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:2 w:1) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm SupportedVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem HostConfiguration (r:1 w:0) - /// Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - /// Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:2 w:1) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn notify_current_targets() -> Weight { // Proof Size summary in bytes: // Measured: `106` // Estimated: `6046` - // Minimum execution time: 28_025_000 picoseconds. - Weight::from_parts(28_524_000, 0) + // Minimum execution time: 23_387_000 picoseconds. + Weight::from_parts(24_677_000, 0) .saturating_add(Weight::from_parts(0, 6046)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:3 w:0) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:0) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `136` - // Estimated: `8551` - // Minimum execution time: 8_166_000 picoseconds. - Weight::from_parts(8_314_000, 0) - .saturating_add(Weight::from_parts(0, 8551)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `11026` + // Minimum execution time: 10_939_000 picoseconds. + Weight::from_parts(11_210_000, 0) + .saturating_add(Weight::from_parts(0, 11026)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `106` - // Estimated: `10996` - // Minimum execution time: 14_871_000 picoseconds. - Weight::from_parts(15_374_000, 0) - .saturating_add(Weight::from_parts(0, 10996)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `100` + // Estimated: `13465` + // Minimum execution time: 16_850_000 picoseconds. + Weight::from_parts(17_195_000, 0) + .saturating_add(Weight::from_parts(0, 13465)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm SupportedVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem HostConfiguration (r:1 w:0) - /// Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - /// Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `112` - // Estimated: `11002` - // Minimum execution time: 33_611_000 picoseconds. - Weight::from_parts(34_008_000, 0) - .saturating_add(Weight::from_parts(0, 11002)) - .saturating_add(T::DbWeight::get().reads(9)) + // Measured: `106` + // Estimated: `13471` + // Minimum execution time: 31_931_000 picoseconds. + Weight::from_parts(32_494_000, 0) + .saturating_add(Weight::from_parts(0, 13471)) + .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) @@ -319,11 +341,11 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn new_query() -> Weight { // Proof Size summary in bytes: - // Measured: `103` - // Estimated: `1588` - // Minimum execution time: 5_496_000 picoseconds. - Weight::from_parts(5_652_000, 0) - .saturating_add(Weight::from_parts(0, 1588)) + // Measured: `32` + // Estimated: `1517` + // Minimum execution time: 3_514_000 picoseconds. + Weight::from_parts(3_709_000, 0) + .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -331,11 +353,23 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn take_response() -> Weight { // Proof Size summary in bytes: - // Measured: `7740` - // Estimated: `11205` - // Minimum execution time: 26_140_000 picoseconds. - Weight::from_parts(26_824_000, 0) - .saturating_add(Weight::from_parts(0, 11205)) + // Measured: `7669` + // Estimated: `11134` + // Minimum execution time: 24_863_000 picoseconds. + Weight::from_parts(25_293_000, 0) + .saturating_add(Weight::from_parts(0, 11134)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `90` + // Estimated: `3555` + // Minimum execution time: 33_799_000 picoseconds. + Weight::from_parts(34_665_000, 0) + .saturating_add(Weight::from_parts(0, 3555)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs index 311128a17ca9c16f18adf60e5dbf383161dc3bab..a10333fdb62651df1d8500f28379710da318d279 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs @@ -207,7 +207,7 @@ pub type Barrier = TrailingSetTopicAsId< AllowKnownQueryResponses, WithComputedOrigin< ( - // If the message is one that immediately attemps to pay for execution, then + // If the message is one that immediately attempts to pay for execution, then // allow it. AllowTopLevelPaidExecutionFrom, // Parent and its pluralities (i.e. governance bodies) get free execution. @@ -269,6 +269,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } /// Converts a local signed origin into an XCM location. Forms the basis for local origins diff --git a/cumulus/parachains/runtimes/people/people-westend/Cargo.toml b/cumulus/parachains/runtimes/people/people-westend/Cargo.toml index e87e825a34e8d9ad8c7f41883f3cfbb826064b06..39cb69e679cc26f2d074626757864cb078d0ded8 100644 --- a/cumulus/parachains/runtimes/people/people-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/people/people-westend/Cargo.toml @@ -188,5 +188,3 @@ try-runtime = [ "polkadot-runtime-common/try-runtime", "sp-runtime/try-runtime", ] - -experimental = ["pallet-aura/experimental"] diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs index a47df66f50399e24da2a1e465f0a039ed6ba7c0c..e840a40f5acdf0684d3e50a079e9eed56a4896ab 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -126,7 +126,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("people-westend"), impl_name: create_runtime_str!("people-westend"), authoring_version: 1, - spec_version: 1_007_000, + spec_version: 1_009_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 0, @@ -164,7 +164,7 @@ parameter_types! { pub const SS58Prefix: u8 = 42; } -#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = Everything; type BlockWeights = RuntimeBlockWeights; @@ -187,10 +187,7 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; - #[cfg(feature = "experimental")] type MinimumPeriod = ConstU64<0>; - #[cfg(not(feature = "experimental"))] - type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = weights::pallet_timestamp::WeightInfo; } @@ -337,7 +334,6 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; type AllowMultipleBlocksPerSlot = ConstBool; - #[cfg(feature = "experimental")] type SlotDuration = ConstU64; } @@ -348,7 +344,7 @@ parameter_types! { pub const StakingAdminBodyId: BodyId = BodyId::Defense; } -/// We allow Root and the `StakingAdmi` to execute privileged collator selection operations. +/// We allow Root and the `StakingAdmin` to execute privileged collator selection operations. pub type CollatorSelectionUpdateOrigin = EitherOfDiverse< EnsureRoot, EnsureXcm>, @@ -460,7 +456,7 @@ mod benches { [cumulus_pallet_xcmp_queue, XcmpQueue] [pallet_collator_selection, CollatorSelection] // XCM - [pallet_xcm, PalletXcmExtrinsiscsBenchmark::] + [pallet_xcm, PalletXcmExtrinsicsBenchmark::] [pallet_xcm_benchmarks::fungible, XcmBalances] [pallet_xcm_benchmarks::generic, XcmGeneric] ); @@ -473,7 +469,7 @@ impl_runtime_apis! { } fn authorities() -> Vec { - Aura::authorities().into_inner() + pallet_aura::Authorities::::get().into_inner() } } @@ -495,7 +491,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -648,7 +644,7 @@ impl_runtime_apis! { use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; - use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark; + use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; // This is defined once again in dispatch_benchmark, because list_benchmarks! // and add_benchmarks! are macros exported by define_benchmarks! macros and those types @@ -684,8 +680,14 @@ impl_runtime_apis! { use cumulus_pallet_session_benchmarking::Pallet as SessionBench; impl cumulus_pallet_session_benchmarking::Config for Runtime {} - use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark; + use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -694,7 +696,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between People and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, Parent.into(), @@ -704,6 +706,13 @@ impl_runtime_apis! { fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> { None } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(ExistentialDeposit::get()), + } + } } use xcm::latest::prelude::*; diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_xcm.rs index d3b60471b850e0fffbad01915aaf461be3d48ae0..62a9c802808c0fdc2305f566f7b7f67b4d0a5748 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_xcm.rs @@ -1,40 +1,41 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . //! Autogenerated weights for `pallet_xcm` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("people-polkadot-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("people-westend-dev")`, DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=people-polkadot-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_xcm -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/people/people-polkadot/src/weights/pallet_xcm.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm +// --chain=people-westend-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/people/people-westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,57 +48,48 @@ use core::marker::PhantomData; /// Weight functions for `pallet_xcm`. pub struct WeightInfo(PhantomData); impl pallet_xcm::WeightInfo for WeightInfo { - /// Storage: PolkadotXcm SupportedVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem HostConfiguration (r:1 w:0) - /// Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - /// Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn send() -> Weight { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 25_783_000 picoseconds. - Weight::from_parts(26_398_000, 0) + // Minimum execution time: 17_450_000 picoseconds. + Weight::from_parts(17_913_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: ParachainInfo ParachainId (r:1 w:0) - /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn teleport_assets() -> Weight { - // Proof Size summary in bytes: - // Measured: `32` - // Estimated: `1489` - // Minimum execution time: 25_511_000 picoseconds. - Weight::from_parts(26_120_000, 0) - .saturating_add(Weight::from_parts(0, 1489)) - .saturating_add(T::DbWeight::get().reads(1)) - } - /// Storage: Benchmark Override (r:0 w:0) - /// Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) - fn reserve_transfer_assets() -> Weight { + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. - Weight::from_parts(18_446_744_073_709_551_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `38` + // Estimated: `3503` + // Minimum execution time: 18_082_000 picoseconds. + Weight::from_parts(18_293_000, 0) + .saturating_add(Weight::from_parts(0, 3503)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:2 w:2) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) - /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) @@ -108,18 +100,38 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn teleport_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `70` + // Estimated: `3535` + // Minimum execution time: 54_939_000 picoseconds. + Weight::from_parts(55_721_000, 0) + .saturating_add(Weight::from_parts(0, 3535)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn reserve_transfer_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) fn transfer_assets() -> Weight { // Proof Size summary in bytes: - // Measured: `496` - // Estimated: `6208` - // Minimum execution time: 146_932_000 picoseconds. - Weight::from_parts(153_200_000, 0) - .saturating_add(Weight::from_parts(0, 6208)) - .saturating_add(T::DbWeight::get().reads(12)) - .saturating_add(T::DbWeight::get().writes(7)) + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) } - /// Storage: Benchmark Override (r:0 w:0) - /// Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) fn execute() -> Weight { // Proof Size summary in bytes: // Measured: `0` @@ -128,189 +140,199 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } - /// Storage: PolkadotXcm SupportedVersion (r:0 w:1) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_707_000 picoseconds. - Weight::from_parts(9_874_000, 0) + // Minimum execution time: 5_789_000 picoseconds. + Weight::from_parts(5_995_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) - /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:0 w:1) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn force_default_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_073_000 picoseconds. - Weight::from_parts(3_183_000, 0) + // Minimum execution time: 1_795_000 picoseconds. + Weight::from_parts(1_924_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: PolkadotXcm VersionNotifiers (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionNotifiers (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm QueryCounter (r:1 w:1) - /// Proof Skipped: PolkadotXcm QueryCounter (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: PolkadotXcm SupportedVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem HostConfiguration (r:1 w:0) - /// Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - /// Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: PolkadotXcm Queries (r:0 w:1) - /// Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) + /// Proof: `PolkadotXcm::QueryCounter` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_subscribe_version_notify() -> Weight { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 30_999_000 picoseconds. - Weight::from_parts(31_641_000, 0) + // Minimum execution time: 23_445_000 picoseconds. + Weight::from_parts(23_906_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) } - /// Storage: PolkadotXcm VersionNotifiers (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionNotifiers (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm SupportedVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem HostConfiguration (r:1 w:0) - /// Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - /// Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: PolkadotXcm Queries (r:0 w:1) - /// Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_unsubscribe_version_notify() -> Weight { // Proof Size summary in bytes: - // Measured: `220` - // Estimated: `3685` - // Minimum execution time: 33_036_000 picoseconds. - Weight::from_parts(33_596_000, 0) - .saturating_add(Weight::from_parts(0, 3685)) + // Measured: `255` + // Estimated: `3720` + // Minimum execution time: 26_590_000 picoseconds. + Weight::from_parts(27_056_000, 0) + .saturating_add(Weight::from_parts(0, 3720)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: PolkadotXcm XcmExecutionSuspended (r:0 w:1) - /// Proof Skipped: PolkadotXcm XcmExecutionSuspended (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::XcmExecutionSuspended` (r:0 w:1) + /// Proof: `PolkadotXcm::XcmExecutionSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn force_suspension() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_035_000 picoseconds. - Weight::from_parts(3_154_000, 0) + // Minimum execution time: 1_889_000 picoseconds. + Weight::from_parts(1_962_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: PolkadotXcm SupportedVersion (r:4 w:2) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::SupportedVersion` (r:5 w:2) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `95` - // Estimated: `10985` - // Minimum execution time: 14_805_000 picoseconds. - Weight::from_parts(15_120_000, 0) - .saturating_add(Weight::from_parts(0, 10985)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `89` + // Estimated: `13454` + // Minimum execution time: 16_408_000 picoseconds. + Weight::from_parts(16_877_000, 0) + .saturating_add(Weight::from_parts(0, 13454)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: PolkadotXcm VersionNotifiers (r:4 w:2) - /// Proof Skipped: PolkadotXcm VersionNotifiers (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:5 w:2) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `99` - // Estimated: `10989` - // Minimum execution time: 14_572_000 picoseconds. - Weight::from_parts(14_909_000, 0) - .saturating_add(Weight::from_parts(0, 10989)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `93` + // Estimated: `13458` + // Minimum execution time: 16_791_000 picoseconds. + Weight::from_parts(17_111_000, 0) + .saturating_add(Weight::from_parts(0, 13458)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:5 w:0) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:6 w:0) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `13471` - // Minimum execution time: 15_341_000 picoseconds. - Weight::from_parts(15_708_000, 0) - .saturating_add(Weight::from_parts(0, 13471)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `15946` + // Minimum execution time: 18_355_000 picoseconds. + Weight::from_parts(19_110_000, 0) + .saturating_add(Weight::from_parts(0, 15946)) + .saturating_add(T::DbWeight::get().reads(6)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:2 w:1) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm SupportedVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem HostConfiguration (r:1 w:0) - /// Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - /// Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:2 w:1) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn notify_current_targets() -> Weight { // Proof Size summary in bytes: // Measured: `106` // Estimated: `6046` - // Minimum execution time: 27_840_000 picoseconds. - Weight::from_parts(28_248_000, 0) + // Minimum execution time: 23_354_000 picoseconds. + Weight::from_parts(23_999_000, 0) .saturating_add(Weight::from_parts(0, 6046)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:3 w:0) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:0) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `136` - // Estimated: `8551` - // Minimum execution time: 8_245_000 picoseconds. - Weight::from_parts(8_523_000, 0) - .saturating_add(Weight::from_parts(0, 8551)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `11026` + // Minimum execution time: 11_065_000 picoseconds. + Weight::from_parts(11_302_000, 0) + .saturating_add(Weight::from_parts(0, 11026)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `106` - // Estimated: `10996` - // Minimum execution time: 14_780_000 picoseconds. - Weight::from_parts(15_173_000, 0) - .saturating_add(Weight::from_parts(0, 10996)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `100` + // Estimated: `13465` + // Minimum execution time: 16_998_000 picoseconds. + Weight::from_parts(17_509_000, 0) + .saturating_add(Weight::from_parts(0, 13465)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm SupportedVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem HostConfiguration (r:1 w:0) - /// Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - /// Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + /// Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `112` - // Estimated: `11002` - // Minimum execution time: 33_422_000 picoseconds. - Weight::from_parts(34_076_000, 0) - .saturating_add(Weight::from_parts(0, 11002)) - .saturating_add(T::DbWeight::get().reads(9)) + // Measured: `106` + // Estimated: `13471` + // Minimum execution time: 31_068_000 picoseconds. + Weight::from_parts(31_978_000, 0) + .saturating_add(Weight::from_parts(0, 13471)) + .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) @@ -319,11 +341,11 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn new_query() -> Weight { // Proof Size summary in bytes: - // Measured: `103` - // Estimated: `1588` - // Minimum execution time: 5_496_000 picoseconds. - Weight::from_parts(5_652_000, 0) - .saturating_add(Weight::from_parts(0, 1588)) + // Measured: `32` + // Estimated: `1517` + // Minimum execution time: 3_478_000 picoseconds. + Weight::from_parts(3_595_000, 0) + .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -331,11 +353,23 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn take_response() -> Weight { // Proof Size summary in bytes: - // Measured: `7740` - // Estimated: `11205` - // Minimum execution time: 26_140_000 picoseconds. - Weight::from_parts(26_824_000, 0) - .saturating_add(Weight::from_parts(0, 11205)) + // Measured: `7669` + // Estimated: `11134` + // Minimum execution time: 24_962_000 picoseconds. + Weight::from_parts(25_404_000, 0) + .saturating_add(Weight::from_parts(0, 11134)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `90` + // Estimated: `3555` + // Minimum execution time: 32_685_000 picoseconds. + Weight::from_parts(33_592_000, 0) + .saturating_add(Weight::from_parts(0, 3555)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs index 4b7da91c17e5568bc4ca34f3f4a255f8276893e8..fee2f5684ac309c623a3914835e0d755953ca52f 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs @@ -214,7 +214,7 @@ pub type Barrier = TrailingSetTopicAsId< AllowKnownQueryResponses, WithComputedOrigin< ( - // If the message is one that immediately attemps to pay for execution, then + // If the message is one that immediately attempts to pay for execution, then // allow it. AllowTopLevelPaidExecutionFrom, // Parent, its pluralities (i.e. governance bodies), and the Fellows plurality @@ -277,6 +277,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } /// Converts a local signed origin into an XCM location. Forms the basis for local origins diff --git a/cumulus/parachains/runtimes/starters/seedling/Cargo.toml b/cumulus/parachains/runtimes/starters/seedling/Cargo.toml index 1f5bee7784e989ab0e6ed76f3692f33d54b6304e..9f08fdf59437cc03336eee449e042eca90ec549a 100644 --- a/cumulus/parachains/runtimes/starters/seedling/Cargo.toml +++ b/cumulus/parachains/runtimes/starters/seedling/Cargo.toml @@ -79,5 +79,3 @@ std = [ "sp-version/std", "substrate-wasm-builder", ] - -experimental = ["pallet-aura/experimental"] diff --git a/cumulus/parachains/runtimes/starters/seedling/src/lib.rs b/cumulus/parachains/runtimes/starters/seedling/src/lib.rs index ba077ef887947f6f0f2b639f85c10fcb13a06fcb..2f4f762408febe0e1176cc1d68dd3d314102a082 100644 --- a/cumulus/parachains/runtimes/starters/seedling/src/lib.rs +++ b/cumulus/parachains/runtimes/starters/seedling/src/lib.rs @@ -135,7 +135,7 @@ parameter_types! { pub const SS58Prefix: u8 = 42; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { /// The identifier used to distinguish between accounts. type AccountId = AccountId; @@ -213,17 +213,13 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; type AllowMultipleBlocksPerSlot = ConstBool; - #[cfg(feature = "experimental")] type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; } impl pallet_timestamp::Config for Runtime { type Moment = u64; type OnTimestampSet = Aura; - #[cfg(feature = "experimental")] type MinimumPeriod = ConstU64<0>; - #[cfg(not(feature = "experimental"))] - type MinimumPeriod = ConstU64<{ parachains_common::SLOT_DURATION / 2 }>; type WeightInfo = (); } @@ -287,7 +283,7 @@ impl_runtime_apis! { } fn authorities() -> Vec { - Aura::authorities().into_inner() + pallet_aura::Authorities::::get().into_inner() } } @@ -300,7 +296,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } diff --git a/cumulus/parachains/runtimes/starters/shell/Cargo.toml b/cumulus/parachains/runtimes/starters/shell/Cargo.toml index 5a8f2a9d125369c91b9859186866cc62edc1795e..2f82547afe9a02d4f1b01699869e35aecb526433 100644 --- a/cumulus/parachains/runtimes/starters/shell/Cargo.toml +++ b/cumulus/parachains/runtimes/starters/shell/Cargo.toml @@ -99,5 +99,3 @@ try-runtime = [ "parachain-info/try-runtime", "sp-runtime/try-runtime", ] - -experimental = ["pallet-aura/experimental"] diff --git a/cumulus/parachains/runtimes/starters/shell/src/lib.rs b/cumulus/parachains/runtimes/starters/shell/src/lib.rs index 457394760d989db4dcd5ba335e1ff2dad46eaaba..0f4957fd802b441e17fe05f791a7e1dbd047a29e 100644 --- a/cumulus/parachains/runtimes/starters/shell/src/lib.rs +++ b/cumulus/parachains/runtimes/starters/shell/src/lib.rs @@ -143,7 +143,7 @@ parameter_types! { pub const SS58Prefix: u8 = 42; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { /// The identifier used to distinguish between accounts. type AccountId = AccountId; @@ -241,17 +241,13 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; type AllowMultipleBlocksPerSlot = ConstBool; - #[cfg(feature = "experimental")] type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; } impl pallet_timestamp::Config for Runtime { type Moment = u64; type OnTimestampSet = Aura; - #[cfg(feature = "experimental")] type MinimumPeriod = ConstU64<0>; - #[cfg(not(feature = "experimental"))] - type MinimumPeriod = ConstU64<{ parachains_common::SLOT_DURATION / 2 }>; type WeightInfo = (); } @@ -344,7 +340,7 @@ impl_runtime_apis! { } fn authorities() -> Vec { - Aura::authorities().into_inner() + pallet_aura::Authorities::::get().into_inner() } } @@ -357,7 +353,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } diff --git a/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs b/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs index f6af50f76d85bd06ca77fa6e6f8b06e41349dc3a..df89158729cd9935911df3a1bff76557c6d5c900 100644 --- a/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs @@ -88,6 +88,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = (); + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/cumulus/parachains/runtimes/test-utils/Cargo.toml b/cumulus/parachains/runtimes/test-utils/Cargo.toml index a61e05de13fa6dff6e54f583a00683d4a270e429..eda88beb7dabb41bd4075ec5ab6bf8ec2f42d3c8 100644 --- a/cumulus/parachains/runtimes/test-utils/Cargo.toml +++ b/cumulus/parachains/runtimes/test-utils/Cargo.toml @@ -17,6 +17,7 @@ frame-support = { path = "../../../../substrate/frame/support", default-features frame-system = { path = "../../../../substrate/frame/system", default-features = false } pallet-balances = { path = "../../../../substrate/frame/balances", default-features = false } pallet-session = { path = "../../../../substrate/frame/session", default-features = false } +pallet-timestamp = { path = "../../../../substrate/frame/timestamp", default-features = false } sp-consensus-aura = { path = "../../../../substrate/primitives/consensus/aura", default-features = false } sp-io = { path = "../../../../substrate/primitives/io", default-features = false } sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } @@ -59,6 +60,7 @@ std = [ "pallet-balances/std", "pallet-collator-selection/std", "pallet-session/std", + "pallet-timestamp/std", "pallet-xcm/std", "parachain-info/std", "polkadot-parachain-primitives/std", diff --git a/cumulus/parachains/runtimes/test-utils/src/lib.rs b/cumulus/parachains/runtimes/test-utils/src/lib.rs index b4eb57fcb66f412cd697cfd4d6efd1f551ef7eb6..e62daa16a1256fa993a97d82d4f6df96f261018b 100644 --- a/cumulus/parachains/runtimes/test-utils/src/lib.rs +++ b/cumulus/parachains/runtimes/test-utils/src/lib.rs @@ -34,7 +34,7 @@ use polkadot_parachain_primitives::primitives::{ }; use sp_consensus_aura::{SlotDuration, AURA_ENGINE_ID}; use sp_core::{Encode, U256}; -use sp_runtime::{traits::Header, BuildStorage, Digest, DigestItem}; +use sp_runtime::{traits::Header, BuildStorage, Digest, DigestItem, SaturatedConversion}; use xcm::{ latest::{Asset, Location, XcmContext, XcmHash}, prelude::*, @@ -129,6 +129,7 @@ pub trait BasicParachainRuntime: + parachain_info::Config + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config + + pallet_timestamp::Config { } @@ -140,7 +141,8 @@ where + pallet_xcm::Config + parachain_info::Config + pallet_collator_selection::Config - + cumulus_pallet_parachain_system::Config, + + cumulus_pallet_parachain_system::Config + + pallet_timestamp::Config, ValidatorIdOf: From>, { } @@ -259,8 +261,10 @@ pub struct RuntimeHelper( ); /// Utility function that advances the chain to the desired block number. /// If an author is provided, that author information is injected to all the blocks in the meantime. -impl - RuntimeHelper +impl< + Runtime: frame_system::Config + cumulus_pallet_parachain_system::Config + pallet_timestamp::Config, + AllPalletsWithoutSystem, + > RuntimeHelper where AccountIdOf: Into<<::RuntimeOrigin as OriginTrait>::AccountId>, @@ -296,6 +300,65 @@ where last_header.expect("run_to_block empty block range") } + pub fn run_to_block_with_finalize(n: u32) -> HeaderFor { + let mut last_header = None; + loop { + let block_number = frame_system::Pallet::::block_number(); + if block_number >= n.into() { + break + } + // Set the new block number and author + let header = frame_system::Pallet::::finalize(); + + let pre_digest = Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, block_number.encode())], + }; + frame_system::Pallet::::reset_events(); + + let next_block_number = block_number + 1u32.into(); + frame_system::Pallet::::initialize( + &next_block_number, + &header.hash(), + &pre_digest, + ); + AllPalletsWithoutSystem::on_initialize(next_block_number); + + let parent_head = HeadData(header.encode()); + let sproof_builder = RelayStateSproofBuilder { + para_id: ::SelfParaId::get(), + included_para_head: parent_head.clone().into(), + ..Default::default() + }; + + let (relay_parent_storage_root, relay_chain_state) = + sproof_builder.into_state_root_and_proof(); + let inherent_data = ParachainInherentData { + validation_data: PersistedValidationData { + parent_head, + relay_parent_number: (block_number.saturated_into::() * 2 + 1).into(), + relay_parent_storage_root, + max_pov_size: 100_000_000, + }, + relay_chain_state, + downward_messages: Default::default(), + horizontal_messages: Default::default(), + }; + + let _ = cumulus_pallet_parachain_system::Pallet::::set_validation_data( + Runtime::RuntimeOrigin::none(), + inherent_data, + ); + let _ = pallet_timestamp::Pallet::::set( + Runtime::RuntimeOrigin::none(), + 300_u32.into(), + ); + AllPalletsWithoutSystem::on_finalize(next_block_number); + let header = frame_system::Pallet::::finalize(); + last_header = Some(header); + } + last_header.expect("run_to_block empty block range") + } + pub fn root_origin() -> ::RuntimeOrigin { ::RuntimeOrigin::root() } diff --git a/cumulus/parachains/runtimes/test-utils/src/test_cases.rs b/cumulus/parachains/runtimes/test-utils/src/test_cases.rs index f78bf9877ec2443a9ce3d754a533f3ee6a3cf14a..1c58df189b673af60bc3e4a9ae7391294287e2aa 100644 --- a/cumulus/parachains/runtimes/test-utils/src/test_cases.rs +++ b/cumulus/parachains/runtimes/test-utils/src/test_cases.rs @@ -37,7 +37,8 @@ pub fn change_storage_constant_by_governance_works: From>, StorageConstant: Get, StorageConstantType: Encode + PartialEq + std::fmt::Debug, @@ -107,7 +108,8 @@ pub fn set_storage_keys_by_governance_works( + pallet_xcm::Config + parachain_info::Config + pallet_collator_selection::Config - + cumulus_pallet_parachain_system::Config, + + cumulus_pallet_parachain_system::Config + + pallet_timestamp::Config, ValidatorIdOf: From>, { let mut runtime = ExtBuilder::::default() diff --git a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml index 08e5987d43afd5cf2676e56dee79afa04362ada0..c18f6571f416beae3b65ffe9b8263696b2ec227c 100644 --- a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml +++ b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml @@ -77,7 +77,6 @@ cumulus-primitives-utility = { path = "../../../../primitives/utility", default- pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachain-info = { package = "staging-parachain-info", path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } -testnet-parachains-constants = { path = "../../constants", default-features = false, features = ["rococo"] } assets-common = { path = "../../assets/common", default-features = false } [features] @@ -133,7 +132,6 @@ std = [ "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", - "testnet-parachains-constants/std", "xcm-builder/std", "xcm-executor/std", "xcm/std", @@ -193,5 +191,3 @@ try-runtime = [ "polkadot-runtime-common/try-runtime", "sp-runtime/try-runtime", ] - -experimental = ["pallet-aura/experimental"] diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index bf8dcbc24c8d51d61c70ea6a56fa8865ea32c893..1d404feac3db1b7d6a59af2b88eaa6adf59fbf2b 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -53,8 +53,10 @@ use frame_system::{ limits::{BlockLength, BlockWeights}, EnsureRoot, EnsureSigned, }; -use parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling}; -use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery; +use parachains_common::{ + impls::{AssetsToBlockAuthor, NonZeroIssuance}, + message_queue::{NarrowOriginToSibling, ParaIdToSibling}, +}; use smallvec::smallvec; use sp_api::impl_runtime_apis; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; @@ -70,7 +72,7 @@ use sp_std::prelude::*; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; -use xcm_config::{AssetsToBlockAuthor, XcmOriginToTransactDispatchOrigin}; +use xcm_config::XcmOriginToTransactDispatchOrigin; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -82,7 +84,7 @@ use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; // XCM Imports use parachains_common::{AccountId, Signature}; -use xcm::latest::prelude::BodyId; +use xcm::latest::prelude::{AssetId as AssetLocationId, BodyId}; /// Balance of an account. pub type Balance = u128; @@ -323,7 +325,7 @@ parameter_types! { // Configure FRAME pallets to include in runtime. -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { /// The identifier used to distinguish between accounts. type AccountId = AccountId; @@ -542,6 +544,20 @@ impl pallet_message_queue::Config for Runtime { impl cumulus_pallet_aura_ext::Config for Runtime {} +parameter_types! { + /// The asset ID for the asset that we use to pay for message delivery fees. + pub FeeAssetId: AssetLocationId = AssetLocationId(xcm_config::RelayLocation::get()); + /// The base fee for the message delivery fees (3 CENTS). + pub const BaseDeliveryFee: u128 = (1_000_000_000_000u128 / 100).saturating_mul(3); +} + +pub type PriceForSiblingParachainDelivery = polkadot_runtime_common::xcm_sender::ExponentialPrice< + FeeAssetId, + BaseDeliveryFee, + TransactionByteFee, + XcmpQueue, +>; + impl cumulus_pallet_xcmp_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; type ChannelInfo = ParachainSystem; @@ -552,7 +568,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = EnsureRoot; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = (); - type PriceForSiblingDelivery = NoPriceForMessageDelivery; + type PriceForSiblingDelivery = PriceForSiblingParachainDelivery; } parameter_types! { @@ -579,7 +595,6 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; type AllowMultipleBlocksPerSlot = ConstBool; - #[cfg(feature = "experimental")] type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; } @@ -618,7 +633,7 @@ impl pallet_asset_tx_payment::Config for Runtime { ConvertInto, pallet_assets::Instance1, >, - AssetsToBlockAuthor, + AssetsToBlockAuthor, >; } @@ -686,7 +701,7 @@ impl_runtime_apis! { } fn authorities() -> Vec { - Aura::authorities().into_inner() + pallet_aura::Authorities::::get().into_inner() } } @@ -699,7 +714,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } diff --git a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs index ef4f466d48425a615ec0816f0c90d13f4ba82a41..7b8e40e04288d213499a35ba085655030abe50d2 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -23,45 +23,45 @@ //! `ReserveAssetTransferDeposited` message but that will but the intension will be to support this //! soon. use super::{ - AccountId, AllPalletsWithSystem, AssetId as AssetIdPalletAssets, Assets, Balance, Balances, - ForeignAssets, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, - RuntimeOrigin, WeightToFee, XcmpQueue, + AccountId, AllPalletsWithSystem, AssetId as AssetIdPalletAssets, Assets, Authorship, Balance, + Balances, ForeignAssets, ForeignAssetsInstance, NonZeroIssuance, ParachainInfo, + ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, + XcmpQueue, }; +use crate::{BaseDeliveryFee, FeeAssetId, TransactionByteFee}; use core::marker::PhantomData; use frame_support::{ parameter_types, - traits::{ - fungibles::{self, Balanced, Credit}, - ConstU32, Contains, ContainsPair, Everything, Get, Nothing, - }, + traits::{ConstU32, Contains, ContainsPair, Everything, EverythingBut, Get, Nothing}, weights::Weight, }; use frame_system::EnsureRoot; -use pallet_asset_tx_payment::HandleCredit; -use pallet_assets::Instance1; use pallet_xcm::XcmPassthrough; +use parachains_common::{xcm_config::AssetFeeAsExistentialDepositMultiplier, TREASURY_PALLET_ID}; use polkadot_parachain_primitives::primitives::Sibling; -use polkadot_runtime_common::impls::ToAuthor; -use sp_runtime::traits::Zero; -use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; +use polkadot_runtime_common::{impls::ToAuthor, xcm_sender::ExponentialPrice}; +use sp_runtime::traits::{AccountIdConversion, ConvertInto}; use xcm::latest::prelude::*; use xcm_builder::{ - AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, - AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, - FungibleAdapter, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, NoChecking, - ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, StartsWith, TakeWeightCredit, TrailingSetTopicAsId, - UsingComponents, WithComputedOrigin, WithUniqueTopic, + AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, EnsureXcmOrigin, + FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, IsConcrete, + LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, StartsWith, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, + XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::{traits::JustTry, XcmExecutor}; parameter_types! { pub const RelayLocation: Location = Location::parent(); + // Local native currency which is stored in `pallet_balances`` + pub const PenpalNativeCurrency: Location = Location::here(); pub const RelayNetwork: Option = None; pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = [Parachain(ParachainInfo::parachain_id().into())].into(); + pub TreasuryAccount: AccountId = TREASURY_PALLET_ID.into_account_truncating(); } /// Type for specifying how a `Location` can be converted into an `AccountId`. This is used @@ -81,7 +81,7 @@ pub type CurrencyTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: - IsConcrete, + IsConcrete, // Do a simple punn to convert an AccountId32 Location into a native chain account ID: LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): @@ -124,9 +124,16 @@ pub type FungiblesTransactor = FungiblesAdapter< CheckingAccount, >; -/// `AssetId/Balance` converter for `TrustBackedAssets` -pub type ForeignAssetsConvertedConcreteId = - assets_common::ForeignAssetsConvertedConcreteId, Balance>; +pub type ForeignAssetsConvertedConcreteId = assets_common::LocationConvertedConcreteId< + EverythingBut<( + // Here we rely on fact that something like this works: + // assert!(Location::new(1, + // [Parachain(100)]).starts_with(&Location::parent())); + // assert!([Parachain(100)].into().starts_with(&Here)); + StartsWith, + )>, + Balance, +>; /// Means for transacting foreign assets from different global consensus. pub type ForeignFungiblesTransactor = FungiblesAdapter< @@ -138,7 +145,7 @@ pub type ForeignFungiblesTransactor = FungiblesAdapter< LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): AccountId, - // We dont need to check teleports here. + // We don't need to check teleports here. NoChecking, // The account to use for tracking teleports. CheckingAccount, @@ -176,6 +183,7 @@ parameter_types! { pub UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; + pub XcmAssetFeesReceiver: Option = Authorship::author(); } pub struct ParentOrParentsExecutivePlurality; @@ -185,13 +193,6 @@ impl Contains for ParentOrParentsExecutivePlurality { } } -pub struct CommonGoodAssetsParachain; -impl Contains for CommonGoodAssetsParachain { - fn contains(location: &Location) -> bool { - matches!(location.unpack(), (1, [Parachain(1000)])) - } -} - pub type Barrier = TrailingSetTopicAsId<( TakeWeightCredit, // Expected responses are OK. @@ -202,12 +203,6 @@ pub type Barrier = TrailingSetTopicAsId<( // If the message is one that immediately attempts to pay for execution, then // allow it. AllowTopLevelPaidExecutionFrom, - // System Assets parachain, parent and its exec plurality get free - // execution - AllowExplicitUnpaidExecutionFrom<( - CommonGoodAssetsParachain, - ParentOrParentsExecutivePlurality, - )>, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, ), @@ -247,55 +242,38 @@ impl> ContainsPair for NativeAssetFrom { } } -/// Allow checking in assets that have issuance > 0. -pub struct NonZeroIssuance(PhantomData<(AccountId, Assets)>); -impl Contains<>::AssetId> - for NonZeroIssuance -where - Assets: fungibles::Inspect, -{ - fn contains(id: &>::AssetId) -> bool { - !Assets::total_issuance(id.clone()).is_zero() - } -} - -/// A `HandleCredit` implementation that naively transfers the fees to the block author. -/// Will drop and burn the assets in case the transfer fails. -pub struct AssetsToBlockAuthor(PhantomData); -impl HandleCredit, pallet_assets::Pallet> for AssetsToBlockAuthor -where - R: pallet_authorship::Config + pallet_assets::Config, - AccountIdOf: From + Into, -{ - fn handle_credit(credit: Credit, pallet_assets::Pallet>) { - if let Some(author) = pallet_authorship::Pallet::::author() { - // In case of error: Will drop the result triggering the `OnDrop` of the imbalance. - let _ = pallet_assets::Pallet::::resolve(&author, credit); - } - } -} - +// This asset can be added to AH as Asset and reserved transfer between Penpal and AH +pub const RESERVABLE_ASSET_ID: u32 = 1; // This asset can be added to AH as ForeignAsset and teleported between Penpal and AH pub const TELEPORTABLE_ASSET_ID: u32 = 2; + +pub const ASSETS_PALLET_ID: u8 = 50; +pub const ASSET_HUB_ID: u32 = 1000; + parameter_types! { /// The location that this chain recognizes as the Relay network's Asset Hub. - pub SystemAssetHubLocation: Location = Location::new(1, [Parachain(1000)]); - // ALWAYS ensure that the index in PalletInstance stays up-to-date with + pub SystemAssetHubLocation: Location = Location::new(1, [Parachain(ASSET_HUB_ID)]); // the Relay Chain's Asset Hub's Assets pallet index pub SystemAssetHubAssetsPalletLocation: Location = - Location::new(1, [Parachain(1000), PalletInstance(50)]); + Location::new(1, [Parachain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID)]); pub AssetsPalletLocation: Location = - Location::new(0, [PalletInstance(50)]); + Location::new(0, [PalletInstance(ASSETS_PALLET_ID)]); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); pub LocalTeleportableToAssetHub: Location = Location::new( 0, - [PalletInstance(50), GeneralIndex(TELEPORTABLE_ASSET_ID.into())] + [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(TELEPORTABLE_ASSET_ID.into())] ); - pub LocalTeleportableToAssetHubV3: xcm::v3::Location = xcm::v3::Location::new( - 0, - [xcm::v3::Junction::PalletInstance(50), xcm::v3::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into())] + pub LocalReservableFromAssetHub: Location = Location::new( + 1, + [Parachain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(RESERVABLE_ASSET_ID.into())] ); - pub EthereumLocation: Location = Location::new(2, [GlobalConsensus(EthereumNetwork::get())]); + + /// The Penpal runtime is utilized for testing with various environment setups. + /// This storage item provides the opportunity to customize testing scenarios + /// by configuring the trusted asset from the `SystemAssetHub`. + /// + /// By default, it is configured as a `SystemAssetHubLocation` and can be modified using `System::set_storage`. + pub storage CustomizableAssetFromSystemAssetHub: Location = SystemAssetHubLocation::get(); } /// Accepts asset with ID `AssetLocation` and is coming from `Origin` chain. @@ -310,11 +288,11 @@ impl, Origin: Get> ContainsPair, NativeAssetFrom, - AssetPrefixFrom, + AssetPrefixFrom, ); pub type TrustedTeleporters = (AssetFromChain,); @@ -326,14 +304,28 @@ impl xcm_executor::Config for XcmConfig { // How to withdraw and deposit an asset. type AssetTransactor = AssetTransactors; type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = Reserves; + type IsReserve = TrustedReserves; // no teleport trust established with other chains type IsTeleporter = TrustedTeleporters; type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = FixedWeightBounds; - type Trader = - UsingComponents>; + type Trader = ( + UsingComponents>, + // This trader allows to pay with `is_sufficient=true` "Foreign" assets from dedicated + // `pallet_assets` instance - `ForeignAssets`. + cumulus_primitives_utility::TakeFirstAssetTrader< + AccountId, + ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, + ForeignAssetsConvertedConcreteId, + ForeignAssets, + cumulus_primitives_utility::XcmFeesTo32ByteAccount< + ForeignFungiblesTransactor, + AccountId, + XcmAssetFeesReceiver, + >, + >, + ); type ResponseHandler = PolkadotXcm; type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; @@ -342,23 +334,41 @@ impl xcm_executor::Config for XcmConfig { type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); - type FeeManager = (); + type FeeManager = XcmFeeManagerFromComponents< + (), + XcmFeeToAccount, + >; type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } +/// Multiplier used for dedicated `TakeFirstAssetTrader` with `ForeignAssets` instance. +pub type ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger = + AssetFeeAsExistentialDepositMultiplier< + Runtime, + WeightToFee, + pallet_assets::BalanceToAssetBalance, + ForeignAssetsInstance, + >; + /// No local origins on this chain are allowed to dispatch XCM sends/executions. pub type LocalOriginToLocation = SignedToAccountId32; +pub type PriceForParentDelivery = + ExponentialPrice; + /// The means for routing XCM messages which are not for local execution into the right message /// queues. pub type XcmRouter = WithUniqueTopic<( // Two routers - use UMP to communicate with the relay chain: - cumulus_primitives_utility::ParentAsUmp, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, )>; diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml b/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml index 42169e8949f553e1920508e92a059e5b76818099..790f38d94f502aba2b008927df602c25aa51d151 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml @@ -56,6 +56,7 @@ cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-f cumulus-ping = { path = "../../../pallets/ping", default-features = false } cumulus-primitives-aura = { path = "../../../../primitives/aura", default-features = false } cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } +cumulus-primitives-storage-weight-reclaim = { path = "../../../../primitives/storage-weight-reclaim", default-features = false } cumulus-primitives-utility = { path = "../../../../primitives/utility", default-features = false } parachains-common = { path = "../../../common", default-features = false } testnet-parachains-constants = { path = "../../constants", default-features = false, features = ["rococo"] } @@ -75,6 +76,7 @@ std = [ "cumulus-ping/std", "cumulus-primitives-aura/std", "cumulus-primitives-core/std", + "cumulus-primitives-storage-weight-reclaim/std", "cumulus-primitives-utility/std", "frame-benchmarking?/std", "frame-executive/std", @@ -134,5 +136,3 @@ runtime-benchmarks = [ "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", ] - -experimental = ["pallet-aura/experimental"] diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs index 57969d9a4f1882d218fdc97374f52f467f109398..c6006141981766320713ec4f7b59af3ee049bc74 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -107,7 +107,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("test-parachain"), impl_name: create_runtime_str!("test-parachain"), authoring_version: 1, - spec_version: 1_007_000, + spec_version: 1_009_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 6, @@ -185,7 +185,7 @@ parameter_types! { pub const SS58Prefix: u8 = 42; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { /// The identifier used to distinguish between accounts. type AccountId = AccountId; @@ -228,10 +228,7 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; - #[cfg(feature = "experimental")] type MinimumPeriod = ConstU64<0>; - #[cfg(not(feature = "experimental"))] - type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = (); } @@ -487,6 +484,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } /// Local origins on this chain are allowed to dispatch XCM sends/executions. @@ -594,7 +594,6 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; type AllowMultipleBlocksPerSlot = ConstBool; - #[cfg(feature = "experimental")] type SlotDuration = ConstU64; } @@ -654,6 +653,7 @@ pub type SignedExtra = ( frame_system::CheckNonce, frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = @@ -689,7 +689,7 @@ impl_runtime_apis! { Executive::execute_block(block); } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -762,7 +762,7 @@ impl_runtime_apis! { } fn authorities() -> Vec { - Aura::authorities().into_inner() + pallet_aura::Authorities::::get().into_inner() } } diff --git a/cumulus/polkadot-parachain/Cargo.toml b/cumulus/polkadot-parachain/Cargo.toml index 84a232e954fc74a3dd4aef36750ef311794e72e5..37b7be75ef91de38dc9ed47e27e7ebd25ac21e54 100644 --- a/cumulus/polkadot-parachain/Cargo.toml +++ b/cumulus/polkadot-parachain/Cargo.toml @@ -16,7 +16,7 @@ path = "src/main.rs" [dependencies] async-trait = "0.1.74" -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.0.0" } futures = "0.3.28" hex-literal = "0.4.1" @@ -42,7 +42,10 @@ jsonrpsee = { version = "0.22", features = ["server"] } people-rococo-runtime = { path = "../parachains/runtimes/people/people-rococo" } people-westend-runtime = { path = "../parachains/runtimes/people/people-westend" } parachains-common = { path = "../parachains/common" } -testnet-parachains-constants = { path = "../parachains/runtimes/constants", default-features = false, features = ["rococo", "westend"] } +testnet-parachains-constants = { path = "../parachains/runtimes/constants", default-features = false, features = [ + "rococo", + "westend", +] } # Substrate frame-benchmarking = { path = "../../substrate/frame/benchmarking" } @@ -168,6 +171,5 @@ try-runtime = [ "shell-runtime/try-runtime", "sp-runtime/try-runtime", ] -fast-runtime = [ - "bridge-hub-rococo-runtime/fast-runtime", -] +fast-runtime = ["bridge-hub-rococo-runtime/fast-runtime"] +elastic-scaling-experimental = ["polkadot-service/elastic-scaling-experimental"] diff --git a/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs index 1db826ea7daf82e93c0099807bab7f44642084b3..15e8a1bf11a055e6c5b4950ad07d78cad72f81a8 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -25,7 +25,10 @@ use std::str::FromStr; #[derive(Debug, PartialEq)] pub enum BridgeHubRuntimeType { Kusama, + KusamaLocal, + Polkadot, + PolkadotLocal, Rococo, RococoLocal, @@ -44,7 +47,9 @@ impl FromStr for BridgeHubRuntimeType { fn from_str(value: &str) -> Result { match value { polkadot::BRIDGE_HUB_POLKADOT => Ok(BridgeHubRuntimeType::Polkadot), + polkadot::BRIDGE_HUB_POLKADOT_LOCAL => Ok(BridgeHubRuntimeType::PolkadotLocal), kusama::BRIDGE_HUB_KUSAMA => Ok(BridgeHubRuntimeType::Kusama), + kusama::BRIDGE_HUB_KUSAMA_LOCAL => Ok(BridgeHubRuntimeType::KusamaLocal), westend::BRIDGE_HUB_WESTEND => Ok(BridgeHubRuntimeType::Westend), westend::BRIDGE_HUB_WESTEND_LOCAL => Ok(BridgeHubRuntimeType::WestendLocal), westend::BRIDGE_HUB_WESTEND_DEVELOPMENT => Ok(BridgeHubRuntimeType::WestendDevelopment), @@ -103,6 +108,7 @@ impl BridgeHubRuntimeType { Some("Bob".to_string()), |_| (), ))), + other => Err(std::format!("No default config present for {:?}", other)), } } } @@ -242,6 +248,7 @@ pub mod rococo { /// Sub-module for Kusama setup pub mod kusama { pub(crate) const BRIDGE_HUB_KUSAMA: &str = "bridge-hub-kusama"; + pub(crate) const BRIDGE_HUB_KUSAMA_LOCAL: &str = "bridge-hub-kusama-local"; } /// Sub-module for Westend setup. @@ -358,4 +365,5 @@ pub mod westend { /// Sub-module for Polkadot setup pub mod polkadot { pub(crate) const BRIDGE_HUB_POLKADOT: &str = "bridge-hub-polkadot"; + pub(crate) const BRIDGE_HUB_POLKADOT_LOCAL: &str = "bridge-hub-polkadot-local"; } diff --git a/cumulus/polkadot-parachain/src/command.rs b/cumulus/polkadot-parachain/src/command.rs index acba32f048bec53f36d21c1437549f77834bb51a..ac9c6b6f978aca7e021944b35870700cbb88a88e 100644 --- a/cumulus/polkadot-parachain/src/command.rs +++ b/cumulus/polkadot-parachain/src/command.rs @@ -23,6 +23,7 @@ use crate::{ }, service::{new_partial, Block}, }; +use cumulus_client_service::storage_proof_size::HostFunctions as ReclaimHostFunctions; use cumulus_primitives_core::ParaId; use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}; use log::info; @@ -134,7 +135,7 @@ fn runtime(id: &str) -> Runtime { fn load_spec(id: &str) -> std::result::Result, String> { let (id, _, para_id) = extract_parachain_id(id); Ok(match id { - // - Defaul-like + // - Default-like "staging" => Box::new(chain_spec::rococo_parachain::staging_rococo_parachain_local_config()), "tick" => Box::new(GenericChainSpec::from_json_bytes( @@ -398,7 +399,7 @@ macro_rules! construct_partials { Runtime::AssetHubPolkadot => { let $partials = new_partial::( &$config, - crate::service::aura_build_import_queue::<_, AssetHubPolkadotAuraId>, + crate::service::build_relay_to_aura_import_queue::<_, AssetHubPolkadotAuraId>, )?; $code }, @@ -412,28 +413,21 @@ macro_rules! construct_partials { Runtime::People(_) => { let $partials = new_partial::( &$config, - crate::service::aura_build_import_queue::<_, AuraId>, + crate::service::build_relay_to_aura_import_queue::<_, AuraId>, )?; $code }, Runtime::GluttonWestend | Runtime::Glutton | Runtime::Shell | Runtime::Seedling => { let $partials = new_partial::( &$config, - crate::service::shell_build_import_queue, + crate::service::build_shell_import_queue, )?; $code }, - Runtime::ContractsRococo => { + Runtime::ContractsRococo | Runtime::Penpal(_) | Runtime::Default => { let $partials = new_partial::( &$config, - crate::service::contracts_rococo_build_import_queue, - )?; - $code - }, - Runtime::Penpal(_) | Runtime::Default => { - let $partials = new_partial::( - &$config, - crate::service::rococo_parachain_build_import_queue, + crate::service::build_aura_import_queue, )?; $code }, @@ -449,7 +443,7 @@ macro_rules! construct_async_run { runner.async_run(|$config| { let $components = new_partial::( &$config, - crate::service::aura_build_import_queue::<_, AssetHubPolkadotAuraId>, + crate::service::build_relay_to_aura_import_queue::<_, AssetHubPolkadotAuraId>, )?; let task_manager = $components.task_manager; { $( $code )* }.map(|v| (v, task_manager)) @@ -466,7 +460,7 @@ macro_rules! construct_async_run { runner.async_run(|$config| { let $components = new_partial::( &$config, - crate::service::aura_build_import_queue::<_, AuraId>, + crate::service::build_relay_to_aura_import_queue::<_, AuraId>, )?; let task_manager = $components.task_manager; { $( $code )* }.map(|v| (v, task_manager)) @@ -479,30 +473,20 @@ macro_rules! construct_async_run { runner.async_run(|$config| { let $components = new_partial::( &$config, - crate::service::shell_build_import_queue, + crate::service::build_shell_import_queue, )?; let task_manager = $components.task_manager; { $( $code )* }.map(|v| (v, task_manager)) }) } - Runtime::ContractsRococo => { - runner.async_run(|$config| { - let $components = new_partial::( - &$config, - crate::service::contracts_rococo_build_import_queue, - )?; - let task_manager = $components.task_manager; - { $( $code )* }.map(|v| (v, task_manager)) - }) - }, - Runtime::Penpal(_) | Runtime::Default => { + Runtime::ContractsRococo | Runtime::Penpal(_) | Runtime::Default => { runner.async_run(|$config| { let $components = new_partial::< RuntimeApi, _, >( &$config, - crate::service::rococo_parachain_build_import_queue, + crate::service::build_aura_import_queue, )?; let task_manager = $components.task_manager; { $( $code )* }.map(|v| (v, task_manager)) @@ -584,7 +568,7 @@ pub fn run() -> Result<()> { match cmd { BenchmarkCmd::Pallet(cmd) => if cfg!(feature = "runtime-benchmarks") { - runner.sync_run(|config| cmd.run::, ()>(config)) + runner.sync_run(|config| cmd.run::, ReclaimHostFunctions>(config)) } else { Err("Benchmarking wasn't enabled when building the node. \ You can enable it with `--features runtime-benchmarks`." @@ -714,25 +698,19 @@ pub fn run() -> Result<()> { .map_err(Into::into), CollectivesPolkadot => - crate::service::start_generic_aura_node::< - RuntimeApi, - AuraId, - >(config, polkadot_config, collator_options, id, hwbench) + crate::service::start_generic_aura_node(config, polkadot_config, collator_options, id, hwbench) .await .map(|r| r.0) .map_err(Into::into), CollectivesWestend => - crate::service::start_generic_aura_lookahead_node::< - RuntimeApi, - AuraId, - >(config, polkadot_config, collator_options, id, hwbench) + crate::service::start_generic_aura_lookahead_node(config, polkadot_config, collator_options, id, hwbench) .await .map(|r| r.0) .map_err(Into::into), Seedling | Shell => - crate::service::start_shell_node::( + crate::service::start_shell_node( config, polkadot_config, collator_options, @@ -755,36 +733,26 @@ pub fn run() -> Result<()> { .map_err(Into::into), BridgeHub(bridge_hub_runtime_type) => match bridge_hub_runtime_type { - chain_spec::bridge_hubs::BridgeHubRuntimeType::Polkadot => - crate::service::start_generic_aura_node::< - RuntimeApi, - AuraId, - >(config, polkadot_config, collator_options, id, hwbench) + chain_spec::bridge_hubs::BridgeHubRuntimeType::Polkadot | + chain_spec::bridge_hubs::BridgeHubRuntimeType::PolkadotLocal => + crate::service::start_generic_aura_node(config, polkadot_config, collator_options, id, hwbench) .await .map(|r| r.0), - chain_spec::bridge_hubs::BridgeHubRuntimeType::Kusama => - crate::service::start_generic_aura_node::< - RuntimeApi, - AuraId, - >(config, polkadot_config, collator_options, id, hwbench) + chain_spec::bridge_hubs::BridgeHubRuntimeType::Kusama | + chain_spec::bridge_hubs::BridgeHubRuntimeType::KusamaLocal => + crate::service::start_generic_aura_node(config, polkadot_config, collator_options, id, hwbench) .await .map(|r| r.0), chain_spec::bridge_hubs::BridgeHubRuntimeType::Westend | chain_spec::bridge_hubs::BridgeHubRuntimeType::WestendLocal | chain_spec::bridge_hubs::BridgeHubRuntimeType::WestendDevelopment => - crate::service::start_generic_aura_lookahead_node::< - RuntimeApi, - AuraId, - >(config, polkadot_config, collator_options, id, hwbench) + crate::service::start_generic_aura_lookahead_node(config, polkadot_config, collator_options, id, hwbench) .await .map(|r| r.0), chain_spec::bridge_hubs::BridgeHubRuntimeType::Rococo | chain_spec::bridge_hubs::BridgeHubRuntimeType::RococoLocal | chain_spec::bridge_hubs::BridgeHubRuntimeType::RococoDevelopment => - crate::service::start_generic_aura_lookahead_node::< - RuntimeApi, - AuraId, - >(config, polkadot_config, collator_options, id, hwbench) + crate::service::start_generic_aura_lookahead_node(config, polkadot_config, collator_options, id, hwbench) .await .map(|r| r.0), } @@ -797,10 +765,7 @@ pub fn run() -> Result<()> { chain_spec::coretime::CoretimeRuntimeType::Westend | chain_spec::coretime::CoretimeRuntimeType::WestendLocal | chain_spec::coretime::CoretimeRuntimeType::WestendDevelopment => - crate::service::start_generic_aura_lookahead_node::< - RuntimeApi, - AuraId, - >(config, polkadot_config, collator_options, id, hwbench) + crate::service::start_generic_aura_lookahead_node(config, polkadot_config, collator_options, id, hwbench) .await .map(|r| r.0), } @@ -819,10 +784,7 @@ pub fn run() -> Result<()> { .map_err(Into::into), Glutton | GluttonWestend => - crate::service::start_basic_lookahead_node::< - RuntimeApi, - AuraId, - >(config, polkadot_config, collator_options, id, hwbench) + crate::service::start_basic_lookahead_node(config, polkadot_config, collator_options, id, hwbench) .await .map(|r| r.0) .map_err(Into::into), @@ -834,10 +796,7 @@ pub fn run() -> Result<()> { chain_spec::people::PeopleRuntimeType::Westend | chain_spec::people::PeopleRuntimeType::WestendLocal | chain_spec::people::PeopleRuntimeType::WestendDevelopment => - crate::service::start_generic_aura_lookahead_node::< - RuntimeApi, - AuraId, - >(config, polkadot_config, collator_options, id, hwbench) + crate::service::start_generic_aura_lookahead_node(config, polkadot_config, collator_options, id, hwbench) .await .map(|r| r.0), } diff --git a/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs b/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs index 76dd7347ccbc35127747e144af7c8705a15022e4..7778d1bf7d2dc0187fe6a0684023a9a4648be596 100644 --- a/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs +++ b/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs @@ -39,7 +39,7 @@ sp_api::impl_runtime_apis! { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs b/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs index 0f01b85ebcf6fa63aabd9115c2ef553c18badea8..880f5d760c74559db87597bce158f8f5e62dbd82 100644 --- a/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs +++ b/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs @@ -39,7 +39,7 @@ sp_api::impl_runtime_apis! { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/cumulus/polkadot-parachain/src/service.rs b/cumulus/polkadot-parachain/src/service.rs index 553975b01a80dd379f673b3ee15f22b7eb9e07ea..ddf595ca70c1d675aa66a94daf21036cc8a3295e 100644 --- a/cumulus/polkadot-parachain/src/service.rs +++ b/cumulus/polkadot-parachain/src/service.rs @@ -36,12 +36,13 @@ use cumulus_primitives_core::{ ParaId, }; use cumulus_relay_chain_interface::{OverseerHandle, RelayChainInterface}; +use sc_rpc::DenyUnsafe; use sp_core::Pair; use jsonrpsee::RpcModule; -use crate::{fake_runtime_api::aura::RuntimeApi, rpc}; -pub use parachains_common::{AccountId, Balance, Block, Hash, Header, Nonce}; +use crate::{fake_runtime_api::aura::RuntimeApi as FakeRuntimeApi, rpc}; +pub use parachains_common::{AccountId, AuraId, Balance, Block, Hash, Header, Nonce}; use cumulus_client_consensus_relay_chain::Verifier as RelayChainVerifier; use futures::{lock::Mutex, prelude::*}; @@ -68,11 +69,15 @@ use substrate_prometheus_endpoint::Registry; use polkadot_primitives::CollatorPair; #[cfg(not(feature = "runtime-benchmarks"))] -type HostFunctions = sp_io::SubstrateHostFunctions; +type HostFunctions = + (sp_io::SubstrateHostFunctions, cumulus_client_service::storage_proof_size::HostFunctions); #[cfg(feature = "runtime-benchmarks")] -type HostFunctions = - (sp_io::SubstrateHostFunctions, frame_benchmarking::benchmarking::HostFunctions); +type HostFunctions = ( + sp_io::SubstrateHostFunctions, + cumulus_client_service::storage_proof_size::HostFunctions, + frame_benchmarking::benchmarking::HostFunctions, +); type ParachainClient = TFullClient>; @@ -81,141 +86,6 @@ type ParachainBackend = TFullBackend; type ParachainBlockImport = TParachainBlockImport>, ParachainBackend>; -/// Native executor instance. -pub struct ShellRuntimeExecutor; - -impl sc_executor::NativeExecutionDispatch for ShellRuntimeExecutor { - type ExtendHostFunctions = (); - - fn dispatch(method: &str, data: &[u8]) -> Option> { - shell_runtime::api::dispatch(method, data) - } - - fn native_version() -> sc_executor::NativeVersion { - shell_runtime::native_version() - } -} - -/// Native Asset Hub Westend (Westmint) executor instance. -pub struct AssetHubWestendExecutor; -impl sc_executor::NativeExecutionDispatch for AssetHubWestendExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - - fn dispatch(method: &str, data: &[u8]) -> Option> { - asset_hub_westend_runtime::api::dispatch(method, data) - } - - fn native_version() -> sc_executor::NativeVersion { - asset_hub_westend_runtime::native_version() - } -} - -/// Native Westend Collectives executor instance. -pub struct CollectivesWestendRuntimeExecutor; - -impl sc_executor::NativeExecutionDispatch for CollectivesWestendRuntimeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - - fn dispatch(method: &str, data: &[u8]) -> Option> { - collectives_westend_runtime::api::dispatch(method, data) - } - - fn native_version() -> sc_executor::NativeVersion { - collectives_westend_runtime::native_version() - } -} - -/// Native BridgeHubRococo executor instance. -pub struct BridgeHubRococoRuntimeExecutor; -impl sc_executor::NativeExecutionDispatch for BridgeHubRococoRuntimeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - - fn dispatch(method: &str, data: &[u8]) -> Option> { - bridge_hub_rococo_runtime::api::dispatch(method, data) - } - - fn native_version() -> sc_executor::NativeVersion { - bridge_hub_rococo_runtime::native_version() - } -} - -/// Native `CoretimeRococo` executor instance. -pub struct CoretimeRococoRuntimeExecutor; -impl sc_executor::NativeExecutionDispatch for CoretimeRococoRuntimeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - fn dispatch(method: &str, data: &[u8]) -> Option> { - coretime_rococo_runtime::api::dispatch(method, data) - } - fn native_version() -> sc_executor::NativeVersion { - coretime_rococo_runtime::native_version() - } -} - -/// Native `CoretimeWestend` executor instance. -pub struct CoretimeWestendRuntimeExecutor; -impl sc_executor::NativeExecutionDispatch for CoretimeWestendRuntimeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - fn dispatch(method: &str, data: &[u8]) -> Option> { - coretime_westend_runtime::api::dispatch(method, data) - } - fn native_version() -> sc_executor::NativeVersion { - coretime_westend_runtime::native_version() - } -} - -/// Native contracts executor instance. -pub struct ContractsRococoRuntimeExecutor; -impl sc_executor::NativeExecutionDispatch for ContractsRococoRuntimeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - - fn dispatch(method: &str, data: &[u8]) -> Option> { - contracts_rococo_runtime::api::dispatch(method, data) - } - - fn native_version() -> sc_executor::NativeVersion { - contracts_rococo_runtime::native_version() - } -} - -/// Native Westend Glutton executor instance. -pub struct GluttonWestendRuntimeExecutor; - -impl sc_executor::NativeExecutionDispatch for GluttonWestendRuntimeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - - fn dispatch(method: &str, data: &[u8]) -> Option> { - glutton_westend_runtime::api::dispatch(method, data) - } - - fn native_version() -> sc_executor::NativeVersion { - glutton_westend_runtime::native_version() - } -} - -/// Native `PeopleWestend` executor instance. -pub struct PeopleWestendRuntimeExecutor; -impl sc_executor::NativeExecutionDispatch for PeopleWestendRuntimeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - fn dispatch(method: &str, data: &[u8]) -> Option> { - people_westend_runtime::api::dispatch(method, data) - } - fn native_version() -> sc_executor::NativeVersion { - people_westend_runtime::native_version() - } -} - -/// Native `PeopleRococo` executor instance. -pub struct PeopleRococoRuntimeExecutor; -impl sc_executor::NativeExecutionDispatch for PeopleRococoRuntimeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - fn dispatch(method: &str, data: &[u8]) -> Option> { - people_rococo_runtime::api::dispatch(method, data) - } - fn native_version() -> sc_executor::NativeVersion { - people_rococo_runtime::native_version() - } -} - /// Assembly of PartialComponents (enough to run chain ops subcommands) pub type Service = PartialComponents< ParachainClient, @@ -274,10 +144,11 @@ where .build(); let (client, backend, keystore_container, task_manager) = - sc_service::new_full_parts::( + sc_service::new_full_parts_record_import::( config, telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), executor, + true, )?; let client = Arc::new(client); @@ -318,12 +189,11 @@ where }) } -/// Start a shell node with the given parachain `Configuration` and relay chain `Configuration`. +/// Start a node with the given parachain `Configuration` and relay chain `Configuration`. /// -/// This is the actual implementation that is abstract over the executor and the runtime api for -/// shell nodes. +/// This is the actual implementation that is abstract over the executor and the runtime api. #[sc_tracing::logging::prefix_logs_with("Parachain")] -async fn start_shell_node_impl( +async fn start_node_impl( parachain_config: Configuration, polkadot_config: Configuration, collator_options: CollatorOptions, @@ -342,8 +212,15 @@ where + sp_api::ApiExt + sp_offchain::OffchainWorkerApi + sp_block_builder::BlockBuilder - + cumulus_primitives_core::CollectCollationInfo, - RB: Fn(Arc>) -> Result, sc_service::Error> + + cumulus_primitives_core::CollectCollationInfo + + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + + frame_rpc_system::AccountNonceApi, + RB: Fn( + DenyUnsafe, + Arc>, + Arc, + Arc>>, + ) -> Result, sc_service::Error> + 'static, BIQ: FnOnce( Arc>, @@ -367,6 +244,7 @@ where CollatorPair, OverseerHandle, Arc>) + Send + Sync>, + Arc, ) -> Result<(), sc_service::Error>, { let parachain_config = prepare_node_config(parachain_config); @@ -378,7 +256,6 @@ where let backend = params.backend.clone(); let mut task_manager = params.task_manager; - let (relay_chain_interface, collator_key) = build_relay_chain_interface( polkadot_config, ¶chain_config, @@ -410,8 +287,20 @@ where }) .await?; - let rpc_client = client.clone(); - let rpc_builder = Box::new(move |_, _| rpc_ext_builder(rpc_client.clone())); + let rpc_builder = { + let client = client.clone(); + let transaction_pool = transaction_pool.clone(); + let backend_for_rpc = backend.clone(); + + Box::new(move |deny_unsafe, _| { + rpc_ext_builder( + deny_unsafe, + client.clone(), + backend_for_rpc.clone(), + transaction_pool.clone(), + ) + }) + }; sc_service::spawn_tasks(sc_service::SpawnTasksParams { rpc_builder, @@ -488,6 +377,7 @@ where collator_key.expect("Command line arguments do not allow this. qed"), overseer_handle, announce_block, + backend.clone(), )?; } @@ -496,1148 +386,248 @@ where Ok((task_manager, client)) } -/// Start a node with the given parachain `Configuration` and relay chain `Configuration`. -/// -/// This is the actual implementation that is abstract over the executor and the runtime api. -#[sc_tracing::logging::prefix_logs_with("Parachain")] -async fn start_node_impl( +/// Build the import queue for Aura-based runtimes. +pub fn build_aura_import_queue( + client: Arc>, + block_import: ParachainBlockImport, + config: &Configuration, + telemetry: Option, + task_manager: &TaskManager, +) -> Result, sc_service::Error> { + let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; + + cumulus_client_consensus_aura::import_queue::< + sp_consensus_aura::sr25519::AuthorityPair, + _, + _, + _, + _, + _, + >(cumulus_client_consensus_aura::ImportQueueParams { + block_import, + client, + create_inherent_data_providers: move |_, _| async move { + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + + Ok((slot, timestamp)) + }, + registry: config.prometheus_registry(), + spawner: &task_manager.spawn_essential_handle(), + telemetry, + }) + .map_err(Into::into) +} + +/// Start a rococo parachain node. +pub async fn start_rococo_parachain_node( parachain_config: Configuration, polkadot_config: Configuration, collator_options: CollatorOptions, - sybil_resistance_level: CollatorSybilResistance, para_id: ParaId, - _rpc_ext_builder: RB, - build_import_queue: BIQ, - start_consensus: SC, hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> +) -> sc_service::error::Result<(TaskManager, Arc>)> { + start_node_impl::( + parachain_config, + polkadot_config, + collator_options, + CollatorSybilResistance::Resistant, // Aura + para_id, + build_parachain_rpc_extensions::, + build_aura_import_queue, + start_lookahead_aura_consensus, + hwbench, + ) + .await +} + +/// Build the import queue for the shell runtime. +pub fn build_shell_import_queue( + client: Arc>, + block_import: ParachainBlockImport, + config: &Configuration, + _: Option, + task_manager: &TaskManager, +) -> Result, sc_service::Error> { + cumulus_client_consensus_relay_chain::import_queue( + client, + block_import, + |_, _| async { Ok(()) }, + &task_manager.spawn_essential_handle(), + config.prometheus_registry(), + ) + .map_err(Into::into) +} + +fn build_parachain_rpc_extensions( + deny_unsafe: sc_rpc::DenyUnsafe, + client: Arc>, + backend: Arc, + pool: Arc>>, +) -> Result, sc_service::Error> where RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::Metadata - + sp_session::SessionKeys - + sp_api::ApiExt - + sp_offchain::OffchainWorkerApi + sp_block_builder::BlockBuilder - + cumulus_primitives_core::CollectCollationInfo + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + frame_rpc_system::AccountNonceApi, - RB: Fn(Arc>) -> Result, sc_service::Error>, - BIQ: FnOnce( - Arc>, - ParachainBlockImport, - &Configuration, - Option, - &TaskManager, - ) -> Result, sc_service::Error>, - SC: FnOnce( - Arc>, - ParachainBlockImport, - Option<&Registry>, - Option, - &TaskManager, - Arc, - Arc>>, - Arc>, - KeystorePtr, - Duration, - ParaId, - CollatorPair, - OverseerHandle, - Arc>) + Send + Sync>, - Arc, - ) -> Result<(), sc_service::Error>, { - let parachain_config = prepare_node_config(parachain_config); + let deps = rpc::FullDeps { client, pool, deny_unsafe }; - let params = new_partial::(¶chain_config, build_import_queue)?; - let (block_import, mut telemetry, telemetry_worker_handle) = params.other; + rpc::create_full(deps, backend).map_err(Into::into) +} - let client = params.client.clone(); - let backend = params.backend.clone(); +fn build_contracts_rpc_extensions( + deny_unsafe: sc_rpc::DenyUnsafe, + client: Arc>, + _backend: Arc, + pool: Arc>>, +) -> Result, sc_service::Error> { + let deps = crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), deny_unsafe }; - let mut task_manager = params.task_manager; - let (relay_chain_interface, collator_key) = build_relay_chain_interface( + crate::rpc::create_contracts_rococo(deps).map_err(Into::into) +} + +/// Start a polkadot-shell parachain node. +pub async fn start_shell_node( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, +) -> sc_service::error::Result<(TaskManager, Arc>)> { + start_node_impl::( + parachain_config, polkadot_config, - ¶chain_config, - telemetry_worker_handle, - &mut task_manager, - collator_options.clone(), - hwbench.clone(), + collator_options, + CollatorSybilResistance::Unresistant, // free-for-all consensus + para_id, + |_, _, _, _| Ok(RpcModule::new(())), + build_shell_import_queue, + start_relay_chain_consensus, + hwbench, ) .await - .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; - - let validator = parachain_config.role.is_authority(); - let prometheus_registry = parachain_config.prometheus_registry().cloned(); - let transaction_pool = params.transaction_pool.clone(); - let import_queue_service = params.import_queue.service(); - let net_config = FullNetworkConfiguration::new(¶chain_config.network); +} - let (network, system_rpc_tx, tx_handler_controller, start_network, sync_service) = - build_network(BuildNetworkParams { - parachain_config: ¶chain_config, - net_config, - client: client.clone(), - transaction_pool: transaction_pool.clone(), - para_id, - spawn_handle: task_manager.spawn_handle(), - relay_chain_interface: relay_chain_interface.clone(), - import_queue: params.import_queue, - sybil_resistance_level, - }) - .await?; +enum BuildOnAccess { + Uninitialized(Option R + Send + Sync>>), + Initialized(R), +} - let rpc_builder = { - let client = client.clone(); - let transaction_pool = transaction_pool.clone(); +impl BuildOnAccess { + fn get_mut(&mut self) -> &mut R { + loop { + match self { + Self::Uninitialized(f) => { + *self = Self::Initialized((f.take().unwrap())()); + }, + Self::Initialized(ref mut r) => return r, + } + } + } +} - let backend_for_rpc = backend.clone(); - Box::new(move |deny_unsafe, _| { - let deps = rpc::FullDeps { - client: client.clone(), - pool: transaction_pool.clone(), - deny_unsafe, - }; +/// Special [`ParachainConsensus`] implementation that waits for the upgrade from +/// shell to a parachain runtime that implements Aura. +struct WaitForAuraConsensus { + client: Arc, + aura_consensus: Arc>>>>, + relay_chain_consensus: Arc>>>, + _phantom: PhantomData, +} - rpc::create_full(deps, backend_for_rpc.clone()).map_err(Into::into) - }) - }; - - sc_service::spawn_tasks(sc_service::SpawnTasksParams { - rpc_builder, - client: client.clone(), - transaction_pool: transaction_pool.clone(), - task_manager: &mut task_manager, - config: parachain_config, - keystore: params.keystore_container.keystore(), - backend: backend.clone(), - network: network.clone(), - sync_service: sync_service.clone(), - system_rpc_tx, - tx_handler_controller, - telemetry: telemetry.as_mut(), - })?; - - if let Some(hwbench) = hwbench { - sc_sysinfo::print_hwbench(&hwbench); - if validator { - warn_if_slow_hardware(&hwbench); - } - - if let Some(ref mut telemetry) = telemetry { - let telemetry_handle = telemetry.handle(); - task_manager.spawn_handle().spawn( - "telemetry_hwbench", - None, - sc_sysinfo::initialize_hwbench_telemetry(telemetry_handle, hwbench), - ); +impl Clone for WaitForAuraConsensus { + fn clone(&self) -> Self { + Self { + client: self.client.clone(), + aura_consensus: self.aura_consensus.clone(), + relay_chain_consensus: self.relay_chain_consensus.clone(), + _phantom: PhantomData, } } - - let announce_block = { - let sync_service = sync_service.clone(); - Arc::new(move |hash, data| sync_service.announce_block(hash, data)) - }; - - let relay_chain_slot_duration = Duration::from_secs(6); - - let overseer_handle = relay_chain_interface - .overseer_handle() - .map_err(|e| sc_service::Error::Application(Box::new(e)))?; - - start_relay_chain_tasks(StartRelayChainTasksParams { - client: client.clone(), - announce_block: announce_block.clone(), - para_id, - relay_chain_interface: relay_chain_interface.clone(), - task_manager: &mut task_manager, - da_recovery_profile: if validator { - DARecoveryProfile::Collator - } else { - DARecoveryProfile::FullNode - }, - import_queue: import_queue_service, - relay_chain_slot_duration, - recovery_handle: Box::new(overseer_handle.clone()), - sync_service: sync_service.clone(), - })?; - - if validator { - start_consensus( - client.clone(), - block_import, - prometheus_registry.as_ref(), - telemetry.as_ref().map(|t| t.handle()), - &task_manager, - relay_chain_interface.clone(), - transaction_pool, - sync_service.clone(), - params.keystore_container.keystore(), - relay_chain_slot_duration, - para_id, - collator_key.expect("Command line arguments do not allow this. qed"), - overseer_handle, - announce_block, - backend.clone(), - )?; - } - - start_network.start_network(); - - Ok((task_manager, client)) } -/// Start a node with the given parachain `Configuration` and relay chain `Configuration`. -/// -/// This is the actual implementation that is abstract over the executor and the runtime api. -/// -/// This node is basic in the sense that it doesn't support functionality like transaction -/// payment. Intended to replace start_shell_node in use for glutton, shell, and seedling. -#[sc_tracing::logging::prefix_logs_with("Parachain")] -async fn start_basic_lookahead_node_impl( - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - sybil_resistance_level: CollatorSybilResistance, - para_id: ParaId, - rpc_ext_builder: RB, - build_import_queue: BIQ, - start_consensus: SC, - hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> +#[async_trait::async_trait] +impl ParachainConsensus for WaitForAuraConsensus where - RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, - RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::Metadata - + sp_session::SessionKeys - + sp_api::ApiExt - + sp_offchain::OffchainWorkerApi - + sp_block_builder::BlockBuilder - + cumulus_primitives_core::CollectCollationInfo - + frame_rpc_system::AccountNonceApi, - RB: Fn(Arc>) -> Result, sc_service::Error> - + 'static, - BIQ: FnOnce( - Arc>, - ParachainBlockImport, - &Configuration, - Option, - &TaskManager, - ) -> Result, sc_service::Error>, - SC: FnOnce( - Arc>, - ParachainBlockImport, - Option<&Registry>, - Option, - &TaskManager, - Arc, - Arc>>, - Arc>, - KeystorePtr, - Duration, - ParaId, - CollatorPair, - OverseerHandle, - Arc>) + Send + Sync>, - Arc, - ) -> Result<(), sc_service::Error>, -{ - let parachain_config = prepare_node_config(parachain_config); - - let params = new_partial::(¶chain_config, build_import_queue)?; - let (block_import, mut telemetry, telemetry_worker_handle) = params.other; - - let client = params.client.clone(); - let backend = params.backend.clone(); - - let mut task_manager = params.task_manager; - let (relay_chain_interface, collator_key) = build_relay_chain_interface( - polkadot_config, - ¶chain_config, - telemetry_worker_handle, - &mut task_manager, - collator_options.clone(), - hwbench.clone(), - ) - .await - .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; - - let validator = parachain_config.role.is_authority(); - let prometheus_registry = parachain_config.prometheus_registry().cloned(); - let transaction_pool = params.transaction_pool.clone(); - let import_queue_service = params.import_queue.service(); - let net_config = FullNetworkConfiguration::new(¶chain_config.network); - - let (network, system_rpc_tx, tx_handler_controller, start_network, sync_service) = - build_network(BuildNetworkParams { - parachain_config: ¶chain_config, - net_config, - client: client.clone(), - transaction_pool: transaction_pool.clone(), - para_id, - spawn_handle: task_manager.spawn_handle(), - relay_chain_interface: relay_chain_interface.clone(), - import_queue: params.import_queue, - sybil_resistance_level, - }) - .await?; - - let rpc_client = client.clone(); - let rpc_builder = Box::new(move |_, _| rpc_ext_builder(rpc_client.clone())); - - sc_service::spawn_tasks(sc_service::SpawnTasksParams { - rpc_builder, - client: client.clone(), - transaction_pool: transaction_pool.clone(), - task_manager: &mut task_manager, - config: parachain_config, - keystore: params.keystore_container.keystore(), - backend: backend.clone(), - network: network.clone(), - sync_service: sync_service.clone(), - system_rpc_tx, - tx_handler_controller, - telemetry: telemetry.as_mut(), - })?; - - if let Some(hwbench) = hwbench { - sc_sysinfo::print_hwbench(&hwbench); - if validator { - warn_if_slow_hardware(&hwbench); - } - - if let Some(ref mut telemetry) = telemetry { - let telemetry_handle = telemetry.handle(); - task_manager.spawn_handle().spawn( - "telemetry_hwbench", - None, - sc_sysinfo::initialize_hwbench_telemetry(telemetry_handle, hwbench), - ); - } - } - - let announce_block = { - let sync_service = sync_service.clone(); - Arc::new(move |hash, data| sync_service.announce_block(hash, data)) - }; - - let relay_chain_slot_duration = Duration::from_secs(6); - - let overseer_handle = relay_chain_interface - .overseer_handle() - .map_err(|e| sc_service::Error::Application(Box::new(e)))?; - - start_relay_chain_tasks(StartRelayChainTasksParams { - client: client.clone(), - announce_block: announce_block.clone(), - para_id, - relay_chain_interface: relay_chain_interface.clone(), - task_manager: &mut task_manager, - da_recovery_profile: if validator { - DARecoveryProfile::Collator - } else { - DARecoveryProfile::FullNode - }, - import_queue: import_queue_service, - relay_chain_slot_duration, - recovery_handle: Box::new(overseer_handle.clone()), - sync_service: sync_service.clone(), - })?; - - if validator { - start_consensus( - client.clone(), - block_import, - prometheus_registry.as_ref(), - telemetry.as_ref().map(|t| t.handle()), - &task_manager, - relay_chain_interface.clone(), - transaction_pool, - sync_service.clone(), - params.keystore_container.keystore(), - relay_chain_slot_duration, - para_id, - collator_key.expect("Command line arguments do not allow this. qed"), - overseer_handle, - announce_block, - backend.clone(), - )?; - } - - start_network.start_network(); - - Ok((task_manager, client)) -} - -/// Build the import queue for the rococo parachain runtime. -pub fn rococo_parachain_build_import_queue( - client: Arc>, - block_import: ParachainBlockImport, - config: &Configuration, - telemetry: Option, - task_manager: &TaskManager, -) -> Result, sc_service::Error> { - let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; - - cumulus_client_consensus_aura::import_queue::< - sp_consensus_aura::sr25519::AuthorityPair, - _, - _, - _, - _, - _, - >(cumulus_client_consensus_aura::ImportQueueParams { - block_import, - client, - create_inherent_data_providers: move |_, _| async move { - let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); - - let slot = - sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( - *timestamp, - slot_duration, - ); - - Ok((slot, timestamp)) - }, - registry: config.prometheus_registry(), - spawner: &task_manager.spawn_essential_handle(), - telemetry, - }) - .map_err(Into::into) -} - -/// Start a rococo parachain node. -pub async fn start_rococo_parachain_node( - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - para_id: ParaId, - hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> { - start_node_impl::( - parachain_config, - polkadot_config, - collator_options, - CollatorSybilResistance::Resistant, // Aura - para_id, - |_| Ok(RpcModule::new(())), - rococo_parachain_build_import_queue, - |client, - block_import, - prometheus_registry, - telemetry, - task_manager, - relay_chain_interface, - transaction_pool, - sync_oracle, - keystore, - relay_chain_slot_duration, - para_id, - collator_key, - overseer_handle, - announce_block, - backend| { - let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( - task_manager.spawn_handle(), - client.clone(), - transaction_pool, - prometheus_registry, - telemetry.clone(), - ); - let proposer = Proposer::new(proposer_factory); - - let collator_service = CollatorService::new( - client.clone(), - Arc::new(task_manager.spawn_handle()), - announce_block, - client.clone(), - ); - - let params = AuraParams { - create_inherent_data_providers: move |_, ()| async move { Ok(()) }, - block_import, - para_client: client.clone(), - para_backend: backend.clone(), - relay_client: relay_chain_interface, - code_hash_provider: move |block_hash| { - client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) - }, - sync_oracle, - keystore, - collator_key, - para_id, - overseer_handle, - relay_chain_slot_duration, - proposer, - collator_service, - authoring_duration: Duration::from_millis(1500), - reinitialize: false, - }; - - let fut = aura::run::< - Block, - sp_consensus_aura::sr25519::AuthorityPair, - _, - _, - _, - _, - _, - _, - _, - _, - _, - >(params); - task_manager.spawn_essential_handle().spawn("aura", None, fut); - - Ok(()) - }, - hwbench, - ) - .await -} - -/// Build the import queue for the shell runtime. -pub fn shell_build_import_queue( - client: Arc>, - block_import: ParachainBlockImport, - config: &Configuration, - _: Option, - task_manager: &TaskManager, -) -> Result, sc_service::Error> -where - RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, - RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::Metadata - + sp_session::SessionKeys - + sp_api::ApiExt - + sp_offchain::OffchainWorkerApi - + sp_block_builder::BlockBuilder, -{ - cumulus_client_consensus_relay_chain::import_queue( - client, - block_import, - |_, _| async { Ok(()) }, - &task_manager.spawn_essential_handle(), - config.prometheus_registry(), - ) - .map_err(Into::into) -} - -/// Start a polkadot-shell parachain node. -pub async fn start_shell_node( - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - para_id: ParaId, - hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> -where - RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, - RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::Metadata - + sp_session::SessionKeys - + sp_api::ApiExt - + sp_offchain::OffchainWorkerApi - + sp_block_builder::BlockBuilder - + cumulus_primitives_core::CollectCollationInfo, -{ - start_shell_node_impl::( - parachain_config, - polkadot_config, - collator_options, - CollatorSybilResistance::Unresistant, // free-for-all consensus - para_id, - |_| Ok(RpcModule::new(())), - shell_build_import_queue, - |client, - block_import, - prometheus_registry, - telemetry, - task_manager, - relay_chain_interface, - transaction_pool, - _sync_oracle, - _keystore, - _relay_chain_slot_duration, - para_id, - collator_key, - overseer_handle, - announce_block| { - let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( - task_manager.spawn_handle(), - client.clone(), - transaction_pool, - prometheus_registry, - telemetry, - ); - - let free_for_all = cumulus_client_consensus_relay_chain::build_relay_chain_consensus( - cumulus_client_consensus_relay_chain::BuildRelayChainConsensusParams { - para_id, - proposer_factory, - block_import, - relay_chain_interface: relay_chain_interface.clone(), - create_inherent_data_providers: move |_, (relay_parent, validation_data)| { - let relay_chain_interface = relay_chain_interface.clone(); - async move { - let parachain_inherent = - cumulus_client_parachain_inherent::ParachainInherentDataProvider::create_at( - relay_parent, - &relay_chain_interface, - &validation_data, - para_id, - ).await; - let parachain_inherent = parachain_inherent.ok_or_else(|| { - Box::::from( - "Failed to create parachain inherent", - ) - })?; - Ok(parachain_inherent) - } - }, - }, - ); - - let spawner = task_manager.spawn_handle(); - - // Required for free-for-all consensus - #[allow(deprecated)] - old_consensus::start_collator_sync(old_consensus::StartCollatorParams { - para_id, - block_status: client.clone(), - announce_block, - overseer_handle, - spawner, - key: collator_key, - parachain_consensus: free_for_all, - runtime_api: client.clone(), - }); - - Ok(()) - }, - hwbench, - ) - .await -} - -enum BuildOnAccess { - Uninitialized(Option R + Send + Sync>>), - Initialized(R), -} - -impl BuildOnAccess { - fn get_mut(&mut self) -> &mut R { - loop { - match self { - Self::Uninitialized(f) => { - *self = Self::Initialized((f.take().unwrap())()); - }, - Self::Initialized(ref mut r) => return r, - } - } - } -} - -/// Special [`ParachainConsensus`] implementation that waits for the upgrade from -/// shell to a parachain runtime that implements Aura. -struct WaitForAuraConsensus { - client: Arc, - aura_consensus: Arc>>>>, - relay_chain_consensus: Arc>>>, - _phantom: PhantomData, -} - -impl Clone for WaitForAuraConsensus { - fn clone(&self) -> Self { - Self { - client: self.client.clone(), - aura_consensus: self.aura_consensus.clone(), - relay_chain_consensus: self.relay_chain_consensus.clone(), - _phantom: PhantomData, - } - } -} - -#[async_trait::async_trait] -impl ParachainConsensus for WaitForAuraConsensus -where - Client: sp_api::ProvideRuntimeApi + Send + Sync, - Client::Api: AuraApi, - AuraId: Send + Codec + Sync, + Client: sp_api::ProvideRuntimeApi + Send + Sync, + Client::Api: AuraApi, + AuraId: Send + Codec + Sync, { async fn produce_candidate( &mut self, parent: &Header, - relay_parent: PHash, - validation_data: &PersistedValidationData, - ) -> Option> { - if self - .client - .runtime_api() - .has_api::>(parent.hash()) - .unwrap_or(false) - { - self.aura_consensus - .lock() - .await - .get_mut() - .produce_candidate(parent, relay_parent, validation_data) - .await - } else { - self.relay_chain_consensus - .lock() - .await - .produce_candidate(parent, relay_parent, validation_data) - .await - } - } -} - -struct Verifier { - client: Arc, - aura_verifier: BuildOnAccess>>, - relay_chain_verifier: Box>, - _phantom: PhantomData, -} - -#[async_trait::async_trait] -impl VerifierT for Verifier -where - Client: sp_api::ProvideRuntimeApi + Send + Sync, - Client::Api: AuraApi, - AuraId: Send + Sync + Codec, -{ - async fn verify( - &mut self, - block_import: BlockImportParams, - ) -> Result, String> { - if self - .client - .runtime_api() - .has_api::>(*block_import.header.parent_hash()) - .unwrap_or(false) - { - self.aura_verifier.get_mut().verify(block_import).await - } else { - self.relay_chain_verifier.verify(block_import).await - } - } -} - -/// Build the import queue for Aura-based runtimes. -pub fn aura_build_import_queue( - client: Arc>, - block_import: ParachainBlockImport, - config: &Configuration, - telemetry_handle: Option, - task_manager: &TaskManager, -) -> Result, sc_service::Error> -where - RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, - RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::Metadata - + sp_session::SessionKeys - + sp_api::ApiExt - + sp_offchain::OffchainWorkerApi - + sp_block_builder::BlockBuilder - + sp_consensus_aura::AuraApi::Pair as Pair>::Public>, - <::Pair as Pair>::Signature: - TryFrom> + std::hash::Hash + sp_runtime::traits::Member + Codec, -{ - let client2 = client.clone(); - - let aura_verifier = move || { - let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client2).unwrap(); - - Box::new(cumulus_client_consensus_aura::build_verifier::< - ::Pair, - _, - _, - _, - >(cumulus_client_consensus_aura::BuildVerifierParams { - client: client2.clone(), - create_inherent_data_providers: move |_, _| async move { - let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); - - let slot = - sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( - *timestamp, - slot_duration, - ); - - Ok((slot, timestamp)) - }, - telemetry: telemetry_handle, - })) as Box<_> - }; - - let relay_chain_verifier = - Box::new(RelayChainVerifier::new(client.clone(), |_, _| async { Ok(()) })) as Box<_>; - - let verifier = Verifier { - client, - relay_chain_verifier, - aura_verifier: BuildOnAccess::Uninitialized(Some(Box::new(aura_verifier))), - _phantom: PhantomData, - }; - - let registry = config.prometheus_registry(); - let spawner = task_manager.spawn_essential_handle(); - - Ok(BasicQueue::new(verifier, Box::new(block_import), None, &spawner, registry)) -} - -/// Start an aura powered parachain node. Some system chains use this. -pub async fn start_generic_aura_node( - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - para_id: ParaId, - hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> -where - RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, - RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::Metadata - + sp_session::SessionKeys - + sp_api::ApiExt - + sp_offchain::OffchainWorkerApi - + sp_block_builder::BlockBuilder - + cumulus_primitives_core::CollectCollationInfo - + sp_consensus_aura::AuraApi::Pair as Pair>::Public> - + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi - + frame_rpc_system::AccountNonceApi, - <::Pair as Pair>::Signature: - TryFrom> + std::hash::Hash + sp_runtime::traits::Member + Codec, -{ - start_node_impl::( - parachain_config, - polkadot_config, - collator_options, - CollatorSybilResistance::Resistant, // Aura - para_id, - |_| Ok(RpcModule::new(())), - aura_build_import_queue::<_, AuraId>, - |client, - block_import, - prometheus_registry, - telemetry, - task_manager, - relay_chain_interface, - transaction_pool, - sync_oracle, - keystore, - relay_chain_slot_duration, - para_id, - collator_key, - overseer_handle, - announce_block, - _backend| { - let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; - - let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( - task_manager.spawn_handle(), - client.clone(), - transaction_pool, - prometheus_registry, - telemetry.clone(), - ); - let proposer = Proposer::new(proposer_factory); - - let collator_service = CollatorService::new( - client.clone(), - Arc::new(task_manager.spawn_handle()), - announce_block, - client.clone(), - ); - - let params = BasicAuraParams { - create_inherent_data_providers: move |_, ()| async move { Ok(()) }, - block_import, - para_client: client, - relay_client: relay_chain_interface, - sync_oracle, - keystore, - collator_key, - para_id, - overseer_handle, - slot_duration, - relay_chain_slot_duration, - proposer, - collator_service, - // Very limited proposal time. - authoring_duration: Duration::from_millis(500), - collation_request_receiver: None, - }; - - let fut = - basic_aura::run::::Pair, _, _, _, _, _, _, _>(params); - task_manager.spawn_essential_handle().spawn("aura", None, fut); - - Ok(()) - }, - hwbench, - ) - .await -} - -/// Uses the lookahead collator to support async backing. -/// -/// Start an aura powered parachain node. Some system chains use this. -pub async fn start_generic_aura_lookahead_node( - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - para_id: ParaId, - hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> -where - RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, - RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::Metadata - + sp_session::SessionKeys - + sp_api::ApiExt - + sp_offchain::OffchainWorkerApi - + sp_block_builder::BlockBuilder - + cumulus_primitives_core::CollectCollationInfo - + sp_consensus_aura::AuraApi::Pair as Pair>::Public> - + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi - + frame_rpc_system::AccountNonceApi - + cumulus_primitives_aura::AuraUnincludedSegmentApi, - <::Pair as Pair>::Signature: - TryFrom> + std::hash::Hash + sp_runtime::traits::Member + Codec, -{ - start_node_impl::( - parachain_config, - polkadot_config, - collator_options, - CollatorSybilResistance::Resistant, // Aura - para_id, - |_| Ok(RpcModule::new(())), - aura_build_import_queue::<_, AuraId>, - |client, - block_import, - prometheus_registry, - telemetry, - task_manager, - relay_chain_interface, - transaction_pool, - sync_oracle, - keystore, - relay_chain_slot_duration, - para_id, - collator_key, - overseer_handle, - announce_block, - backend| { - let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( - task_manager.spawn_handle(), - client.clone(), - transaction_pool, - prometheus_registry, - telemetry.clone(), - ); - let proposer = Proposer::new(proposer_factory); - - let collator_service = CollatorService::new( - client.clone(), - Arc::new(task_manager.spawn_handle()), - announce_block, - client.clone(), - ); - - let params = AuraParams { - create_inherent_data_providers: move |_, ()| async move { Ok(()) }, - block_import, - para_client: client.clone(), - para_backend: backend, - relay_client: relay_chain_interface, - code_hash_provider: move |block_hash| { - client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) - }, - sync_oracle, - keystore, - collator_key, - para_id, - overseer_handle, - relay_chain_slot_duration, - proposer, - collator_service, - authoring_duration: Duration::from_millis(1500), - reinitialize: false, - }; - - let fut = - aura::run::::Pair, _, _, _, _, _, _, _, _, _>(params); - task_manager.spawn_essential_handle().spawn("aura", None, fut); - - Ok(()) - }, - hwbench, - ) - .await -} - -/// Start a shell node which should later transition into an Aura powered parachain node. Asset Hub -/// uses this because at genesis, Asset Hub was on the `shell` runtime which didn't have Aura and -/// needs to sync and upgrade before it can run `AuraApi` functions. -pub async fn start_asset_hub_node( - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - para_id: ParaId, - hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> -where - RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, - RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::Metadata - + sp_session::SessionKeys - + sp_api::ApiExt - + sp_offchain::OffchainWorkerApi - + sp_block_builder::BlockBuilder - + cumulus_primitives_core::CollectCollationInfo - + sp_consensus_aura::AuraApi::Pair as Pair>::Public> - + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi - + frame_rpc_system::AccountNonceApi, - <::Pair as Pair>::Signature: - TryFrom> + std::hash::Hash + sp_runtime::traits::Member + Codec, -{ - start_node_impl::( - parachain_config, - polkadot_config, - collator_options, - CollatorSybilResistance::Resistant, // Aura - para_id, - |_| Ok(RpcModule::new(())), - aura_build_import_queue::<_, AuraId>, - |client, - block_import, - prometheus_registry, - telemetry, - task_manager, - relay_chain_interface, - transaction_pool, - sync_oracle, - keystore, - relay_chain_slot_duration, - para_id, - collator_key, - overseer_handle, - announce_block, - _backend| { - let relay_chain_interface2 = relay_chain_interface.clone(); - - let collator_service = CollatorService::new( - client.clone(), - Arc::new(task_manager.spawn_handle()), - announce_block, - client.clone(), - ); - - let spawner = task_manager.spawn_handle(); - - let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( - spawner, - client.clone(), - transaction_pool, - prometheus_registry, - telemetry.clone(), - ); - - let collation_future = Box::pin(async move { - // Start collating with the `shell` runtime while waiting for an upgrade to an Aura - // compatible runtime. - let mut request_stream = cumulus_client_collator::relay_chain_driven::init( - collator_key.clone(), - para_id, - overseer_handle.clone(), - ) - .await; - while let Some(request) = request_stream.next().await { - let pvd = request.persisted_validation_data().clone(); - let last_head_hash = - match ::Header::decode(&mut &pvd.parent_head.0[..]) { - Ok(header) => header.hash(), - Err(e) => { - log::error!("Could not decode the head data: {e}"); - request.complete(None); - continue - }, - }; - - // Check if we have upgraded to an Aura compatible runtime and transition if - // necessary. - if client - .runtime_api() - .has_api::>(last_head_hash) - .unwrap_or(false) - { - // Respond to this request before transitioning to Aura. - request.complete(None); - break - } - } - - // Move to Aura consensus. - let slot_duration = match cumulus_client_consensus_aura::slot_duration(&*client) { - Ok(d) => d, - Err(e) => { - log::error!("Could not get Aura slot duration: {e}"); - return - }, - }; - - let proposer = Proposer::new(proposer_factory); - - let params = BasicAuraParams { - create_inherent_data_providers: move |_, ()| async move { Ok(()) }, - block_import, - para_client: client, - relay_client: relay_chain_interface2, - sync_oracle, - keystore, - collator_key, - para_id, - overseer_handle, - slot_duration, - relay_chain_slot_duration, - proposer, - collator_service, - // Very limited proposal time. - authoring_duration: Duration::from_millis(500), - collation_request_receiver: Some(request_stream), - }; - - basic_aura::run::::Pair, _, _, _, _, _, _, _>(params) - .await - }); + relay_parent: PHash, + validation_data: &PersistedValidationData, + ) -> Option> { + if self + .client + .runtime_api() + .has_api::>(parent.hash()) + .unwrap_or(false) + { + self.aura_consensus + .lock() + .await + .get_mut() + .produce_candidate(parent, relay_parent, validation_data) + .await + } else { + self.relay_chain_consensus + .lock() + .await + .produce_candidate(parent, relay_parent, validation_data) + .await + } + } +} - let spawner = task_manager.spawn_essential_handle(); - spawner.spawn_essential("cumulus-asset-hub-collator", None, collation_future); +struct Verifier { + client: Arc, + aura_verifier: BuildOnAccess>>, + relay_chain_verifier: Box>, + _phantom: PhantomData, +} - Ok(()) - }, - hwbench, - ) - .await +#[async_trait::async_trait] +impl VerifierT for Verifier +where + Client: sp_api::ProvideRuntimeApi + Send + Sync, + Client::Api: AuraApi, + AuraId: Send + Sync + Codec, +{ + async fn verify( + &mut self, + block_import: BlockImportParams, + ) -> Result, String> { + if self + .client + .runtime_api() + .has_api::>(*block_import.header.parent_hash()) + .unwrap_or(false) + { + self.aura_verifier.get_mut().verify(block_import).await + } else { + self.relay_chain_verifier.verify(block_import).await + } + } } -/// Start a shell node which should later transition into an Aura powered parachain node. Asset Hub -/// uses this because at genesis, Asset Hub was on the `shell` runtime which didn't have Aura and -/// needs to sync and upgrade before it can run `AuraApi` functions. -/// -/// Uses the lookahead collator to support async backing. -#[sc_tracing::logging::prefix_logs_with("Parachain")] -pub async fn start_asset_hub_lookahead_node( - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - para_id: ParaId, - hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> +/// Build the import queue for parachain runtimes that started with relay chain consensus and +/// switched to aura. +pub fn build_relay_to_aura_import_queue( + client: Arc>, + block_import: ParachainBlockImport, + config: &Configuration, + telemetry_handle: Option, + task_manager: &TaskManager, +) -> Result, sc_service::Error> where RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue @@ -1646,162 +636,74 @@ where + sp_api::ApiExt + sp_offchain::OffchainWorkerApi + sp_block_builder::BlockBuilder - + cumulus_primitives_core::CollectCollationInfo - + sp_consensus_aura::AuraApi::Pair as Pair>::Public> - + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi - + frame_rpc_system::AccountNonceApi - + cumulus_primitives_aura::AuraUnincludedSegmentApi, + + sp_consensus_aura::AuraApi::Pair as Pair>::Public>, <::Pair as Pair>::Signature: TryFrom> + std::hash::Hash + sp_runtime::traits::Member + Codec, { - start_node_impl::( - parachain_config, - polkadot_config, - collator_options, - CollatorSybilResistance::Resistant, // Aura - para_id, - |_| Ok(RpcModule::new(())), - aura_build_import_queue::<_, AuraId>, - |client, - block_import, - prometheus_registry, - telemetry, - task_manager, - relay_chain_interface, - transaction_pool, - sync_oracle, - keystore, - relay_chain_slot_duration, - para_id, - collator_key, - overseer_handle, - announce_block, - backend| { - let relay_chain_interface2 = relay_chain_interface.clone(); - - let collator_service = CollatorService::new( - client.clone(), - Arc::new(task_manager.spawn_handle()), - announce_block, - client.clone(), - ); - - let spawner = task_manager.spawn_handle(); - - let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( - spawner, - client.clone(), - transaction_pool, - prometheus_registry, - telemetry.clone(), - ); - - let collation_future = Box::pin(async move { - // Start collating with the `shell` runtime while waiting for an upgrade to an Aura - // compatible runtime. - let mut request_stream = cumulus_client_collator::relay_chain_driven::init( - collator_key.clone(), - para_id, - overseer_handle.clone(), - ) - .await; - while let Some(request) = request_stream.next().await { - let pvd = request.persisted_validation_data().clone(); - let last_head_hash = - match ::Header::decode(&mut &pvd.parent_head.0[..]) { - Ok(header) => header.hash(), - Err(e) => { - log::error!("Could not decode the head data: {e}"); - request.complete(None); - continue - }, - }; + let verifier_client = client.clone(); - // Check if we have upgraded to an Aura compatible runtime and transition if - // necessary. - if client - .runtime_api() - .has_api::>(last_head_hash) - .unwrap_or(false) - { - // Respond to this request before transitioning to Aura. - request.complete(None); - break - } + let aura_verifier = move || { + Box::new(cumulus_client_consensus_aura::build_verifier::< + ::Pair, + _, + _, + _, + >(cumulus_client_consensus_aura::BuildVerifierParams { + client: verifier_client.clone(), + create_inherent_data_providers: move |parent_hash, _| { + let cidp_client = verifier_client.clone(); + async move { + let slot_duration = cumulus_client_consensus_aura::slot_duration_at( + &*cidp_client, + parent_hash, + )?; + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + + Ok((slot, timestamp)) } + }, + telemetry: telemetry_handle, + })) as Box<_> + }; - // Move to Aura consensus. - let proposer = Proposer::new(proposer_factory); - - let params = AuraParams { - create_inherent_data_providers: move |_, ()| async move { Ok(()) }, - block_import, - para_client: client.clone(), - para_backend: backend, - relay_client: relay_chain_interface2, - code_hash_provider: move |block_hash| { - client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) - }, - sync_oracle, - keystore, - collator_key, - para_id, - overseer_handle, - relay_chain_slot_duration, - proposer, - collator_service, - authoring_duration: Duration::from_millis(1500), - reinitialize: true, /* we need to always re-initialize for asset-hub moving - * to aura */ - }; + let relay_chain_verifier = + Box::new(RelayChainVerifier::new(client.clone(), |_, _| async { Ok(()) })) as Box<_>; - aura::run::::Pair, _, _, _, _, _, _, _, _, _>(params) - .await - }); + let verifier = Verifier { + client, + relay_chain_verifier, + aura_verifier: BuildOnAccess::Uninitialized(Some(Box::new(aura_verifier))), + _phantom: PhantomData, + }; - let spawner = task_manager.spawn_essential_handle(); - spawner.spawn_essential("cumulus-asset-hub-collator", None, collation_future); + let registry = config.prometheus_registry(); + let spawner = task_manager.spawn_essential_handle(); - Ok(()) - }, - hwbench, - ) - .await + Ok(BasicQueue::new(verifier, Box::new(block_import), None, &spawner, registry)) } -/// Start an aura powered parachain node which uses the lookahead collator to support async backing. -/// This node is basic in the sense that its runtime api doesn't include common contents such as -/// transaction payment. Used for aura glutton. -pub async fn start_basic_lookahead_node( +/// Start an aura powered parachain node. Some system chains use this. +pub async fn start_generic_aura_node( parachain_config: Configuration, polkadot_config: Configuration, collator_options: CollatorOptions, para_id: ParaId, hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> -where - RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, - RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::Metadata - + sp_session::SessionKeys - + sp_api::ApiExt - + sp_offchain::OffchainWorkerApi - + sp_block_builder::BlockBuilder - + cumulus_primitives_core::CollectCollationInfo - + sp_consensus_aura::AuraApi::Pair as Pair>::Public> - + frame_rpc_system::AccountNonceApi - + cumulus_primitives_aura::AuraUnincludedSegmentApi, - <::Pair as Pair>::Signature: - TryFrom> + std::hash::Hash + sp_runtime::traits::Member + Codec, -{ - start_basic_lookahead_node_impl::( +) -> sc_service::error::Result<(TaskManager, Arc>)> { + start_node_impl::( parachain_config, polkadot_config, collator_options, CollatorSybilResistance::Resistant, // Aura para_id, - |_| Ok(RpcModule::new(())), - aura_build_import_queue::<_, AuraId>, + build_parachain_rpc_extensions::, + build_relay_to_aura_import_queue::<_, AuraId>, |client, block_import, prometheus_registry, @@ -1816,7 +718,9 @@ where collator_key, overseer_handle, announce_block, - backend| { + _backend| { + let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; + let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( task_manager.spawn_handle(), client.clone(), @@ -1833,29 +737,27 @@ where client.clone(), ); - let params = AuraParams { + let params = BasicAuraParams { create_inherent_data_providers: move |_, ()| async move { Ok(()) }, block_import, - para_client: client.clone(), - para_backend: backend.clone(), + para_client: client, relay_client: relay_chain_interface, - code_hash_provider: move |block_hash| { - client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) - }, sync_oracle, keystore, collator_key, para_id, overseer_handle, + slot_duration, relay_chain_slot_duration, proposer, collator_service, - authoring_duration: Duration::from_millis(1500), - reinitialize: false, + // Very limited proposal time. + authoring_duration: Duration::from_millis(500), + collation_request_receiver: None, }; let fut = - aura::run::::Pair, _, _, _, _, _, _, _, _, _>(params); + basic_aura::run::::Pair, _, _, _, _, _, _, _>(params); task_manager.spawn_essential_handle().spawn("aura", None, fut); Ok(()) @@ -1865,16 +767,38 @@ where .await } -#[sc_tracing::logging::prefix_logs_with("Parachain")] -async fn start_contracts_rococo_node_impl( +/// Uses the lookahead collator to support async backing. +/// +/// Start an aura powered parachain node. Some system chains use this. +pub async fn start_generic_aura_lookahead_node( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, +) -> sc_service::error::Result<(TaskManager, Arc>)> { + start_node_impl::( + parachain_config, + polkadot_config, + collator_options, + CollatorSybilResistance::Resistant, // Aura + para_id, + build_parachain_rpc_extensions::, + build_relay_to_aura_import_queue::<_, AuraId>, + start_lookahead_aura_consensus, + hwbench, + ) + .await +} + +/// Start a shell node which should later transition into an Aura powered parachain node. Asset Hub +/// uses this because at genesis, Asset Hub was on the `shell` runtime which didn't have Aura and +/// needs to sync and upgrade before it can run `AuraApi` functions. +pub async fn start_asset_hub_node( parachain_config: Configuration, polkadot_config: Configuration, collator_options: CollatorOptions, - sybil_resistance_level: CollatorSybilResistance, para_id: ParaId, - _rpc_ext_builder: RB, - build_import_queue: BIQ, - start_consensus: SC, hwbench: Option, ) -> sc_service::error::Result<(TaskManager, Arc>)> where @@ -1886,228 +810,169 @@ where + sp_offchain::OffchainWorkerApi + sp_block_builder::BlockBuilder + cumulus_primitives_core::CollectCollationInfo + + sp_consensus_aura::AuraApi::Pair as Pair>::Public> + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi - + frame_rpc_system::AccountNonceApi - + cumulus_primitives_aura::AuraUnincludedSegmentApi, - RB: Fn(Arc>) -> Result, sc_service::Error>, - BIQ: FnOnce( - Arc>, - ParachainBlockImport, - &Configuration, - Option, - &TaskManager, - ) -> Result, sc_service::Error>, - SC: FnOnce( - Arc>, - ParachainBlockImport, - Option<&Registry>, - Option, - &TaskManager, - Arc, - Arc>>, - Arc>, - KeystorePtr, - Duration, - ParaId, - CollatorPair, - OverseerHandle, - Arc>) + Send + Sync>, - Arc, - ) -> Result<(), sc_service::Error>, + + frame_rpc_system::AccountNonceApi, + <::Pair as Pair>::Signature: + TryFrom> + std::hash::Hash + sp_runtime::traits::Member + Codec, { - let parachain_config = prepare_node_config(parachain_config); - - let params = new_partial::(¶chain_config, build_import_queue)?; - let (block_import, mut telemetry, telemetry_worker_handle) = params.other; - - let client = params.client.clone(); - let backend = params.backend.clone(); - let mut task_manager = params.task_manager; - - let (relay_chain_interface, collator_key) = build_relay_chain_interface( + start_node_impl::( + parachain_config, polkadot_config, - ¶chain_config, - telemetry_worker_handle, - &mut task_manager, - collator_options.clone(), - hwbench.clone(), - ) - .await - .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; - - let validator = parachain_config.role.is_authority(); - let prometheus_registry = parachain_config.prometheus_registry().cloned(); - let transaction_pool = params.transaction_pool.clone(); - let import_queue_service = params.import_queue.service(); - let net_config = FullNetworkConfiguration::new(¶chain_config.network); - - let (network, system_rpc_tx, tx_handler_controller, start_network, sync_service) = - build_network(BuildNetworkParams { - parachain_config: ¶chain_config, - net_config, - client: client.clone(), - transaction_pool: transaction_pool.clone(), - para_id, - spawn_handle: task_manager.spawn_handle(), - relay_chain_interface: relay_chain_interface.clone(), - import_queue: params.import_queue, - sybil_resistance_level, - }) - .await?; - - let rpc_builder = { - let client = client.clone(); - let transaction_pool = transaction_pool.clone(); - - Box::new(move |deny_unsafe, _| { - let deps = crate::rpc::FullDeps { - client: client.clone(), - pool: transaction_pool.clone(), - deny_unsafe, - }; - - crate::rpc::create_contracts_rococo(deps).map_err(Into::into) - }) - }; - - sc_service::spawn_tasks(sc_service::SpawnTasksParams { - rpc_builder, - client: client.clone(), - transaction_pool: transaction_pool.clone(), - task_manager: &mut task_manager, - config: parachain_config, - keystore: params.keystore_container.keystore(), - backend: backend.clone(), - network: network.clone(), - sync_service: sync_service.clone(), - system_rpc_tx, - tx_handler_controller, - telemetry: telemetry.as_mut(), - })?; - - if let Some(hwbench) = hwbench { - sc_sysinfo::print_hwbench(&hwbench); - if validator { - warn_if_slow_hardware(&hwbench); - } - - if let Some(ref mut telemetry) = telemetry { - let telemetry_handle = telemetry.handle(); - task_manager.spawn_handle().spawn( - "telemetry_hwbench", - None, - sc_sysinfo::initialize_hwbench_telemetry(telemetry_handle, hwbench), - ); - } - } - - let announce_block = { - let sync_service = sync_service.clone(); - Arc::new(move |hash, data| sync_service.announce_block(hash, data)) - }; - - let relay_chain_slot_duration = Duration::from_secs(6); - - let overseer_handle = relay_chain_interface - .overseer_handle() - .map_err(|e| sc_service::Error::Application(Box::new(e)))?; - - start_relay_chain_tasks(StartRelayChainTasksParams { - client: client.clone(), - announce_block: announce_block.clone(), + collator_options, + CollatorSybilResistance::Resistant, // Aura para_id, - relay_chain_interface: relay_chain_interface.clone(), - task_manager: &mut task_manager, - da_recovery_profile: if validator { - DARecoveryProfile::Collator - } else { - DARecoveryProfile::FullNode - }, - import_queue: import_queue_service, - relay_chain_slot_duration, - recovery_handle: Box::new(overseer_handle.clone()), - sync_service: sync_service.clone(), - })?; + build_parachain_rpc_extensions::, + build_relay_to_aura_import_queue::<_, AuraId>, + |client, + block_import, + prometheus_registry, + telemetry, + task_manager, + relay_chain_interface, + transaction_pool, + sync_oracle, + keystore, + relay_chain_slot_duration, + para_id, + collator_key, + overseer_handle, + announce_block, + _backend| { + let relay_chain_interface2 = relay_chain_interface.clone(); - if validator { - start_consensus( - client.clone(), - block_import, - prometheus_registry.as_ref(), - telemetry.as_ref().map(|t| t.handle()), - &task_manager, - relay_chain_interface.clone(), - transaction_pool, - sync_service.clone(), - params.keystore_container.keystore(), - relay_chain_slot_duration, - para_id, - collator_key.expect("Command line arguments do not allow this. qed"), - overseer_handle, - announce_block, - backend.clone(), - )?; - } + let collator_service = CollatorService::new( + client.clone(), + Arc::new(task_manager.spawn_handle()), + announce_block, + client.clone(), + ); - start_network.start_network(); + let spawner = task_manager.spawn_handle(); - Ok((task_manager, client)) -} + let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( + spawner, + client.clone(), + transaction_pool, + prometheus_registry, + telemetry.clone(), + ); -#[allow(clippy::type_complexity)] -pub fn contracts_rococo_build_import_queue( - client: Arc>, - block_import: ParachainBlockImport, - config: &Configuration, - telemetry: Option, - task_manager: &TaskManager, -) -> Result, sc_service::Error> { - let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; + let collation_future = Box::pin(async move { + // Start collating with the `shell` runtime while waiting for an upgrade to an Aura + // compatible runtime. + let mut request_stream = cumulus_client_collator::relay_chain_driven::init( + collator_key.clone(), + para_id, + overseer_handle.clone(), + ) + .await; + while let Some(request) = request_stream.next().await { + let pvd = request.persisted_validation_data().clone(); + let last_head_hash = + match ::Header::decode(&mut &pvd.parent_head.0[..]) { + Ok(header) => header.hash(), + Err(e) => { + log::error!("Could not decode the head data: {e}"); + request.complete(None); + continue + }, + }; - cumulus_client_consensus_aura::import_queue::< - sp_consensus_aura::sr25519::AuthorityPair, - _, - _, - _, - _, - _, - >(cumulus_client_consensus_aura::ImportQueueParams { - block_import, - client, - create_inherent_data_providers: move |_, _| async move { - let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + // Check if we have upgraded to an Aura compatible runtime and transition if + // necessary. + if client + .runtime_api() + .has_api::>(last_head_hash) + .unwrap_or(false) + { + // Respond to this request before transitioning to Aura. + request.complete(None); + break + } + } - let slot = - sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( - *timestamp, + // Move to Aura consensus. + let slot_duration = match cumulus_client_consensus_aura::slot_duration(&*client) { + Ok(d) => d, + Err(e) => { + log::error!("Could not get Aura slot duration: {e}"); + return + }, + }; + + let proposer = Proposer::new(proposer_factory); + + let params = BasicAuraParams { + create_inherent_data_providers: move |_, ()| async move { Ok(()) }, + block_import, + para_client: client, + relay_client: relay_chain_interface2, + sync_oracle, + keystore, + collator_key, + para_id, + overseer_handle, slot_duration, - ); + relay_chain_slot_duration, + proposer, + collator_service, + // Very limited proposal time. + authoring_duration: Duration::from_millis(500), + collation_request_receiver: Some(request_stream), + }; - Ok((slot, timestamp)) + basic_aura::run::::Pair, _, _, _, _, _, _, _>(params) + .await + }); + + let spawner = task_manager.spawn_essential_handle(); + spawner.spawn_essential("cumulus-asset-hub-collator", None, collation_future); + + Ok(()) }, - registry: config.prometheus_registry(), - spawner: &task_manager.spawn_essential_handle(), - telemetry, - }) - .map_err(Into::into) + hwbench, + ) + .await } -/// Start a parachain node. -pub async fn start_contracts_rococo_node( +/// Start a shell node which should later transition into an Aura powered parachain node. Asset Hub +/// uses this because at genesis, Asset Hub was on the `shell` runtime which didn't have Aura and +/// needs to sync and upgrade before it can run `AuraApi` functions. +/// +/// Uses the lookahead collator to support async backing. +#[sc_tracing::logging::prefix_logs_with("Parachain")] +pub async fn start_asset_hub_lookahead_node( parachain_config: Configuration, polkadot_config: Configuration, collator_options: CollatorOptions, para_id: ParaId, hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> { - start_contracts_rococo_node_impl::( +) -> sc_service::error::Result<(TaskManager, Arc>)> +where + RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, + RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue + + sp_api::Metadata + + sp_session::SessionKeys + + sp_api::ApiExt + + sp_offchain::OffchainWorkerApi + + sp_block_builder::BlockBuilder + + cumulus_primitives_core::CollectCollationInfo + + sp_consensus_aura::AuraApi::Pair as Pair>::Public> + + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + + frame_rpc_system::AccountNonceApi + + cumulus_primitives_aura::AuraUnincludedSegmentApi, + <::Pair as Pair>::Signature: + TryFrom> + std::hash::Hash + sp_runtime::traits::Member + Codec, +{ + start_node_impl::( parachain_config, polkadot_config, collator_options, CollatorSybilResistance::Resistant, // Aura para_id, - |_| Ok(RpcModule::new(())), - contracts_rococo_build_import_queue, + build_parachain_rpc_extensions::, + build_relay_to_aura_import_queue::<_, AuraId>, |client, block_import, prometheus_registry, @@ -2123,14 +988,7 @@ pub async fn start_contracts_rococo_node( overseer_handle, announce_block, backend| { - let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( - task_manager.spawn_handle(), - client.clone(), - transaction_pool, - prometheus_registry, - telemetry.clone(), - ); - let proposer = Proposer::new(proposer_factory); + let relay_chain_interface2 = relay_chain_interface.clone(); let collator_service = CollatorService::new( client.clone(), @@ -2139,42 +997,81 @@ pub async fn start_contracts_rococo_node( client.clone(), ); - let params = AuraParams { - create_inherent_data_providers: move |_, ()| async move { Ok(()) }, - block_import, - para_client: client.clone(), - para_backend: backend.clone(), - relay_client: relay_chain_interface, - code_hash_provider: move |block_hash| { - client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) - }, - sync_oracle, - keystore, - collator_key, - para_id, - overseer_handle, - relay_chain_slot_duration, - proposer, - collator_service, - // Very limited proposal time. - authoring_duration: Duration::from_millis(1500), - reinitialize: false, - }; + let spawner = task_manager.spawn_handle(); - let fut = aura::run::< - Block, - sp_consensus_aura::sr25519::AuthorityPair, - _, - _, - _, - _, - _, - _, - _, - _, - _, - >(params); - task_manager.spawn_essential_handle().spawn("aura", None, fut); + let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( + spawner, + client.clone(), + transaction_pool, + prometheus_registry, + telemetry.clone(), + ); + + let collation_future = Box::pin(async move { + // Start collating with the `shell` runtime while waiting for an upgrade to an Aura + // compatible runtime. + let mut request_stream = cumulus_client_collator::relay_chain_driven::init( + collator_key.clone(), + para_id, + overseer_handle.clone(), + ) + .await; + while let Some(request) = request_stream.next().await { + let pvd = request.persisted_validation_data().clone(); + let last_head_hash = + match ::Header::decode(&mut &pvd.parent_head.0[..]) { + Ok(header) => header.hash(), + Err(e) => { + log::error!("Could not decode the head data: {e}"); + request.complete(None); + continue + }, + }; + + // Check if we have upgraded to an Aura compatible runtime and transition if + // necessary. + if client + .runtime_api() + .has_api::>(last_head_hash) + .unwrap_or(false) + { + // Respond to this request before transitioning to Aura. + request.complete(None); + break + } + } + + // Move to Aura consensus. + let proposer = Proposer::new(proposer_factory); + + let params = AuraParams { + create_inherent_data_providers: move |_, ()| async move { Ok(()) }, + block_import, + para_client: client.clone(), + para_backend: backend, + relay_client: relay_chain_interface2, + code_hash_provider: move |block_hash| { + client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) + }, + sync_oracle, + keystore, + collator_key, + para_id, + overseer_handle, + relay_chain_slot_duration, + proposer, + collator_service, + authoring_duration: Duration::from_millis(1500), + reinitialize: true, /* we need to always re-initialize for asset-hub moving + * to aura */ + }; + + aura::run::::Pair, _, _, _, _, _, _, _, _, _>(params) + .await + }); + + let spawner = task_manager.spawn_essential_handle(); + spawner.spawn_essential("cumulus-asset-hub-collator", None, collation_future); Ok(()) }, @@ -2183,6 +1080,184 @@ pub async fn start_contracts_rococo_node( .await } +/// Start relay-chain consensus that is free for all. Everyone can submit a block, the relay-chain +/// decides what is backed and included. +fn start_relay_chain_consensus( + client: Arc>, + block_import: ParachainBlockImport, + prometheus_registry: Option<&Registry>, + telemetry: Option, + task_manager: &TaskManager, + relay_chain_interface: Arc, + transaction_pool: Arc>>, + _sync_oracle: Arc>, + _keystore: KeystorePtr, + _relay_chain_slot_duration: Duration, + para_id: ParaId, + collator_key: CollatorPair, + overseer_handle: OverseerHandle, + announce_block: Arc>) + Send + Sync>, + _backend: Arc, +) -> Result<(), sc_service::Error> { + let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( + task_manager.spawn_handle(), + client.clone(), + transaction_pool, + prometheus_registry, + telemetry, + ); + + let free_for_all = cumulus_client_consensus_relay_chain::build_relay_chain_consensus( + cumulus_client_consensus_relay_chain::BuildRelayChainConsensusParams { + para_id, + proposer_factory, + block_import, + relay_chain_interface: relay_chain_interface.clone(), + create_inherent_data_providers: move |_, (relay_parent, validation_data)| { + let relay_chain_interface = relay_chain_interface.clone(); + async move { + let parachain_inherent = + cumulus_client_parachain_inherent::ParachainInherentDataProvider::create_at( + relay_parent, + &relay_chain_interface, + &validation_data, + para_id, + ).await; + let parachain_inherent = parachain_inherent.ok_or_else(|| { + Box::::from( + "Failed to create parachain inherent", + ) + })?; + Ok(parachain_inherent) + } + }, + }, + ); + + let spawner = task_manager.spawn_handle(); + + // Required for free-for-all consensus + #[allow(deprecated)] + old_consensus::start_collator_sync(old_consensus::StartCollatorParams { + para_id, + block_status: client.clone(), + announce_block, + overseer_handle, + spawner, + key: collator_key, + parachain_consensus: free_for_all, + runtime_api: client.clone(), + }); + + Ok(()) +} + +/// Start consensus using the lookahead aura collator. +fn start_lookahead_aura_consensus( + client: Arc>, + block_import: ParachainBlockImport, + prometheus_registry: Option<&Registry>, + telemetry: Option, + task_manager: &TaskManager, + relay_chain_interface: Arc, + transaction_pool: Arc>>, + sync_oracle: Arc>, + keystore: KeystorePtr, + relay_chain_slot_duration: Duration, + para_id: ParaId, + collator_key: CollatorPair, + overseer_handle: OverseerHandle, + announce_block: Arc>) + Send + Sync>, + backend: Arc, +) -> Result<(), sc_service::Error> { + let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( + task_manager.spawn_handle(), + client.clone(), + transaction_pool, + prometheus_registry, + telemetry.clone(), + ); + + let collator_service = CollatorService::new( + client.clone(), + Arc::new(task_manager.spawn_handle()), + announce_block, + client.clone(), + ); + + let params = AuraParams { + create_inherent_data_providers: move |_, ()| async move { Ok(()) }, + block_import, + para_client: client.clone(), + para_backend: backend, + relay_client: relay_chain_interface, + code_hash_provider: move |block_hash| { + client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) + }, + sync_oracle, + keystore, + collator_key, + para_id, + overseer_handle, + relay_chain_slot_duration, + proposer: Proposer::new(proposer_factory), + collator_service, + authoring_duration: Duration::from_millis(1500), + reinitialize: false, + }; + + let fut = aura::run::::Pair, _, _, _, _, _, _, _, _, _>(params); + task_manager.spawn_essential_handle().spawn("aura", None, fut); + + Ok(()) +} + +/// Start an aura powered parachain node which uses the lookahead collator to support async backing. +/// This node is basic in the sense that its runtime api doesn't include common contents such as +/// transaction payment. Used for aura glutton. +pub async fn start_basic_lookahead_node( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, +) -> sc_service::error::Result<(TaskManager, Arc>)> { + start_node_impl::( + parachain_config, + polkadot_config, + collator_options, + CollatorSybilResistance::Resistant, // Aura + para_id, + |_, _, _, _| Ok(RpcModule::new(())), + build_relay_to_aura_import_queue::<_, AuraId>, + start_lookahead_aura_consensus, + hwbench, + ) + .await +} + +/// Start a parachain node for Rococo Contracts. +pub async fn start_contracts_rococo_node( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, +) -> sc_service::error::Result<(TaskManager, Arc>)> { + start_node_impl::( + parachain_config, + polkadot_config, + collator_options, + CollatorSybilResistance::Resistant, // Aura + para_id, + build_contracts_rpc_extensions, + build_aura_import_queue, + start_lookahead_aura_consensus, + hwbench, + ) + .await +} + /// Checks that the hardware meets the requirements and print a warning otherwise. fn warn_if_slow_hardware(hwbench: &sc_sysinfo::HwBench) { // Polkadot para-chains should generally use these requirements to ensure that the relay-chain diff --git a/cumulus/primitives/proof-size-hostfunction/src/lib.rs b/cumulus/primitives/proof-size-hostfunction/src/lib.rs index 6da6235e585a343887f87931e375b21bec48c20d..8ebc58ea450d4aea023f2a3af218bbd9eb3546e9 100644 --- a/cumulus/primitives/proof-size-hostfunction/src/lib.rs +++ b/cumulus/primitives/proof-size-hostfunction/src/lib.rs @@ -18,6 +18,7 @@ #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "std")] use sp_externalities::ExternalitiesExt; use sp_runtime_interface::runtime_interface; @@ -35,7 +36,8 @@ pub const PROOF_RECORDING_DISABLED: u64 = u64::MAX; pub trait StorageProofSize { /// Returns the current storage proof size. fn storage_proof_size(&mut self) -> u64 { - self.extension::().map_or(u64::MAX, |e| e.storage_proof_size()) + self.extension::() + .map_or(PROOF_RECORDING_DISABLED, |e| e.storage_proof_size()) } } diff --git a/cumulus/primitives/storage-weight-reclaim/Cargo.toml b/cumulus/primitives/storage-weight-reclaim/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..73e0f03cd375672e1b5b7f5cfda31531a51b68a1 --- /dev/null +++ b/cumulus/primitives/storage-weight-reclaim/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "cumulus-primitives-storage-weight-reclaim" +version = "1.0.0" +authors.workspace = true +edition.workspace = true +description = "Utilities to reclaim storage weight." +license = "Apache-2.0" + +[lints] +workspace = true + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { workspace = true } +scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } + +frame-support = { path = "../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../substrate/frame/system", default-features = false } + +sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } +sp-std = { path = "../../../substrate/primitives/std", default-features = false } + +cumulus-primitives-core = { path = "../core", default-features = false } +cumulus-primitives-proof-size-hostfunction = { path = "../proof-size-hostfunction", default-features = false } +docify = "0.2.7" + +[dev-dependencies] +sp-trie = { path = "../../../substrate/primitives/trie", default-features = false } +sp-io = { path = "../../../substrate/primitives/io", default-features = false } +cumulus-test-runtime = { path = "../../test/runtime" } + +[features] +default = ["std"] +std = [ + "codec/std", + "cumulus-primitives-core/std", + "cumulus-primitives-proof-size-hostfunction/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "sp-trie/std", +] diff --git a/cumulus/primitives/storage-weight-reclaim/src/lib.rs b/cumulus/primitives/storage-weight-reclaim/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..c09c12d7a0abf8ac3c64974390d47880af6ec6e9 --- /dev/null +++ b/cumulus/primitives/storage-weight-reclaim/src/lib.rs @@ -0,0 +1,663 @@ +// 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. + +//! Mechanism to reclaim PoV proof size weight after an extrinsic has been applied. + +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Decode, Encode}; +use cumulus_primitives_core::Weight; +use cumulus_primitives_proof_size_hostfunction::{ + storage_proof_size::storage_proof_size, PROOF_RECORDING_DISABLED, +}; +use frame_support::{ + dispatch::{DispatchInfo, PostDispatchInfo}, + weights::WeightMeter, +}; +use frame_system::Config; +use scale_info::TypeInfo; +use sp_runtime::{ + traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension}, + transaction_validity::TransactionValidityError, + DispatchResult, +}; +use sp_std::marker::PhantomData; + +const LOG_TARGET: &'static str = "runtime::storage_reclaim"; + +/// `StorageWeightReclaimer` is a mechanism for manually reclaiming storage weight. +/// +/// It internally keeps track of the proof size and storage weight at initialization time. At +/// reclaim it computes the real consumed storage weight and refunds excess weight. +/// +/// # Example +#[doc = docify::embed!("src/lib.rs", simple_reclaimer_example)] +pub struct StorageWeightReclaimer { + previous_remaining_proof_size: u64, + previous_reported_proof_size: Option, +} + +impl StorageWeightReclaimer { + /// Creates a new `StorageWeightReclaimer` instance and initializes it with the storage + /// size provided by `weight_meter` and reported proof size from the node. + #[must_use = "Must call `reclaim_with_meter` to reclaim the weight"] + pub fn new(weight_meter: &WeightMeter) -> StorageWeightReclaimer { + let previous_remaining_proof_size = weight_meter.remaining().proof_size(); + let previous_reported_proof_size = get_proof_size(); + Self { previous_remaining_proof_size, previous_reported_proof_size } + } + + /// Check the consumed storage weight and calculate the consumed excess weight. + fn reclaim(&mut self, remaining_weight: Weight) -> Option { + let current_remaining_weight = remaining_weight.proof_size(); + let current_storage_proof_size = get_proof_size()?; + let previous_storage_proof_size = self.previous_reported_proof_size?; + let used_weight = + self.previous_remaining_proof_size.saturating_sub(current_remaining_weight); + let reported_used_size = + current_storage_proof_size.saturating_sub(previous_storage_proof_size); + let reclaimable = used_weight.saturating_sub(reported_used_size); + log::trace!( + target: LOG_TARGET, + "Found reclaimable storage weight. benchmarked: {used_weight}, consumed: {reported_used_size}" + ); + + self.previous_remaining_proof_size = current_remaining_weight.saturating_add(reclaimable); + self.previous_reported_proof_size = Some(current_storage_proof_size); + Some(Weight::from_parts(0, reclaimable)) + } + + /// Check the consumed storage weight and add the reclaimed + /// weight budget back to `weight_meter`. + pub fn reclaim_with_meter(&mut self, weight_meter: &mut WeightMeter) -> Option { + let reclaimed = self.reclaim(weight_meter.remaining())?; + weight_meter.reclaim_proof_size(reclaimed.proof_size()); + Some(reclaimed) + } +} + +/// Returns the current storage proof size from the host side. +/// +/// Returns `None` if proof recording is disabled on the host. +pub fn get_proof_size() -> Option { + let proof_size = storage_proof_size(); + (proof_size != PROOF_RECORDING_DISABLED).then_some(proof_size) +} + +/// Storage weight reclaim mechanism. +/// +/// This extension checks the size of the node-side storage proof +/// before and after executing a given extrinsic. The difference between +/// benchmarked and spent weight can be reclaimed. +#[derive(Encode, Decode, Clone, Eq, PartialEq, Default, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct StorageWeightReclaim(PhantomData); + +impl StorageWeightReclaim { + /// Create a new `StorageWeightReclaim` instance. + pub fn new() -> Self { + Self(Default::default()) + } +} + +impl core::fmt::Debug for StorageWeightReclaim { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + let _ = write!(f, "StorageWeightReclaim"); + Ok(()) + } +} + +impl SignedExtension for StorageWeightReclaim +where + T::RuntimeCall: Dispatchable, +{ + const IDENTIFIER: &'static str = "StorageWeightReclaim"; + + type AccountId = T::AccountId; + type Call = T::RuntimeCall; + type AdditionalSigned = (); + type Pre = Option; + + fn additional_signed( + &self, + ) -> Result + { + Ok(()) + } + + fn pre_dispatch( + self, + _who: &Self::AccountId, + _call: &Self::Call, + _info: &sp_runtime::traits::DispatchInfoOf, + _len: usize, + ) -> Result { + Ok(get_proof_size()) + } + + fn post_dispatch( + pre: Option, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, + _len: usize, + _result: &DispatchResult, + ) -> Result<(), TransactionValidityError> { + let Some(Some(pre_dispatch_proof_size)) = pre else { + return Ok(()); + }; + + let Some(post_dispatch_proof_size) = get_proof_size() else { + log::debug!( + target: LOG_TARGET, + "Proof recording enabled during pre-dispatch, now disabled. This should not happen." + ); + return Ok(()) + }; + let benchmarked_weight = info.weight.proof_size(); + let consumed_weight = post_dispatch_proof_size.saturating_sub(pre_dispatch_proof_size); + + // Unspent weight according to the `actual_weight` from `PostDispatchInfo` + // This unspent weight will be refunded by the `CheckWeight` extension, so we need to + // account for that. + let unspent = post_info.calc_unspent(info).proof_size(); + let storage_size_diff = + benchmarked_weight.saturating_sub(unspent).abs_diff(consumed_weight as u64); + + // This value will be reclaimed by [`frame_system::CheckWeight`], so we need to calculate + // that in. + frame_system::BlockWeight::::mutate(|current| { + if consumed_weight > benchmarked_weight { + log::error!( + target: LOG_TARGET, + "Benchmarked storage weight smaller than consumed storage weight. benchmarked: {benchmarked_weight} consumed: {consumed_weight} unspent: {unspent}" + ); + current.accrue(Weight::from_parts(0, storage_size_diff), info.class) + } else { + log::trace!( + target: LOG_TARGET, + "Reclaiming storage weight. benchmarked: {benchmarked_weight}, consumed: {consumed_weight} unspent: {unspent}" + ); + current.reduce(Weight::from_parts(0, storage_size_diff), info.class) + } + }); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use frame_support::{ + assert_ok, + dispatch::DispatchClass, + weights::{Weight, WeightMeter}, + }; + use frame_system::{BlockWeight, CheckWeight}; + use sp_runtime::{AccountId32, BuildStorage}; + use sp_std::marker::PhantomData; + use sp_trie::proof_size_extension::ProofSizeExt; + + type Test = cumulus_test_runtime::Runtime; + const CALL: &::RuntimeCall = + &cumulus_test_runtime::RuntimeCall::System(frame_system::Call::set_heap_pages { + pages: 0u64, + }); + const ALICE: AccountId32 = AccountId32::new([1u8; 32]); + const LEN: usize = 0; + + pub fn new_test_ext() -> sp_io::TestExternalities { + let ext: sp_io::TestExternalities = cumulus_test_runtime::RuntimeGenesisConfig::default() + .build_storage() + .unwrap() + .into(); + ext + } + + struct TestRecorder { + return_values: Box<[usize]>, + counter: std::sync::atomic::AtomicUsize, + } + + impl TestRecorder { + fn new(values: &[usize]) -> Self { + TestRecorder { return_values: values.into(), counter: Default::default() } + } + } + + impl sp_trie::ProofSizeProvider for TestRecorder { + fn estimate_encoded_size(&self) -> usize { + let counter = self.counter.fetch_add(1, core::sync::atomic::Ordering::Relaxed); + self.return_values[counter] + } + } + + fn setup_test_externalities(proof_values: &[usize]) -> sp_io::TestExternalities { + let mut test_ext = new_test_ext(); + let test_recorder = TestRecorder::new(proof_values); + test_ext.register_extension(ProofSizeExt::new(test_recorder)); + test_ext + } + + fn set_current_storage_weight(new_weight: u64) { + BlockWeight::::mutate(|current_weight| { + current_weight.set(Weight::from_parts(0, new_weight), DispatchClass::Normal); + }); + } + + #[test] + fn basic_refund() { + // The real cost will be 100 bytes of storage size + let mut test_ext = setup_test_externalities(&[0, 100]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + + // Benchmarked storage weight: 500 + let info = DispatchInfo { weight: Weight::from_parts(0, 500), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + let pre = StorageWeightReclaim::(PhantomData) + .pre_dispatch(&ALICE, CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(0)); + + assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); + // We expect a refund of 400 + assert_ok!(StorageWeightReclaim::::post_dispatch( + Some(pre), + &info, + &post_info, + LEN, + &Ok(()) + )); + + assert_eq!(BlockWeight::::get().total().proof_size(), 600); + }) + } + + #[test] + fn does_nothing_without_extension() { + let mut test_ext = new_test_ext(); + + // Proof size extension not registered + test_ext.execute_with(|| { + set_current_storage_weight(1000); + + // Benchmarked storage weight: 500 + let info = DispatchInfo { weight: Weight::from_parts(0, 500), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + let pre = StorageWeightReclaim::(PhantomData) + .pre_dispatch(&ALICE, CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, None); + + assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); + assert_ok!(StorageWeightReclaim::::post_dispatch( + Some(pre), + &info, + &post_info, + LEN, + &Ok(()) + )); + + assert_eq!(BlockWeight::::get().total().proof_size(), 1000); + }) + } + + #[test] + fn negative_refund_is_added_to_weight() { + let mut test_ext = setup_test_externalities(&[100, 300]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + // Benchmarked storage weight: 100 + let info = DispatchInfo { weight: Weight::from_parts(0, 100), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + let pre = StorageWeightReclaim::(PhantomData) + .pre_dispatch(&ALICE, CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(100)); + + // We expect no refund + assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); + assert_ok!(StorageWeightReclaim::::post_dispatch( + Some(pre), + &info, + &post_info, + LEN, + &Ok(()) + )); + + assert_eq!(BlockWeight::::get().total().proof_size(), 1100); + }) + } + + #[test] + fn test_zero_proof_size() { + let mut test_ext = setup_test_externalities(&[0, 0]); + + test_ext.execute_with(|| { + let info = DispatchInfo { weight: Weight::from_parts(0, 500), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + let pre = StorageWeightReclaim::(PhantomData) + .pre_dispatch(&ALICE, CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(0)); + + assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); + assert_ok!(StorageWeightReclaim::::post_dispatch( + Some(pre), + &info, + &post_info, + LEN, + &Ok(()) + )); + + assert_eq!(BlockWeight::::get().total().proof_size(), 0); + }); + } + + #[test] + fn test_larger_pre_dispatch_proof_size() { + let mut test_ext = setup_test_externalities(&[300, 100]); + + test_ext.execute_with(|| { + set_current_storage_weight(1300); + + let info = DispatchInfo { weight: Weight::from_parts(0, 500), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + let pre = StorageWeightReclaim::(PhantomData) + .pre_dispatch(&ALICE, CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(300)); + + assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); + assert_ok!(StorageWeightReclaim::::post_dispatch( + Some(pre), + &info, + &post_info, + LEN, + &Ok(()) + )); + + assert_eq!(BlockWeight::::get().total().proof_size(), 800); + }); + } + + #[test] + fn test_incorporates_check_weight_unspent_weight() { + let mut test_ext = setup_test_externalities(&[100, 300]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + + // Benchmarked storage weight: 300 + let info = DispatchInfo { weight: Weight::from_parts(100, 300), ..Default::default() }; + + // Actual weight is 50 + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(50, 250)), + pays_fee: Default::default(), + }; + + let pre = StorageWeightReclaim::(PhantomData) + .pre_dispatch(&ALICE, CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(100)); + + // The `CheckWeight` extension will refund `actual_weight` from `PostDispatchInfo` + // we always need to call `post_dispatch` to verify that they interoperate correctly. + assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); + assert_ok!(StorageWeightReclaim::::post_dispatch( + Some(pre), + &info, + &post_info, + LEN, + &Ok(()) + )); + + assert_eq!(BlockWeight::::get().total().proof_size(), 900); + }) + } + + #[test] + fn test_incorporates_check_weight_unspent_weight_on_negative() { + let mut test_ext = setup_test_externalities(&[100, 300]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + // Benchmarked storage weight: 50 + let info = DispatchInfo { weight: Weight::from_parts(100, 50), ..Default::default() }; + + // Actual weight is 25 + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(50, 25)), + pays_fee: Default::default(), + }; + + let pre = StorageWeightReclaim::(PhantomData) + .pre_dispatch(&ALICE, CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(100)); + + // The `CheckWeight` extension will refund `actual_weight` from `PostDispatchInfo` + // we always need to call `post_dispatch` to verify that they interoperate correctly. + assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); + assert_ok!(StorageWeightReclaim::::post_dispatch( + Some(pre), + &info, + &post_info, + LEN, + &Ok(()) + )); + + assert_eq!(BlockWeight::::get().total().proof_size(), 1150); + }) + } + + #[test] + fn test_incorporates_check_weight_unspent_weight_reverse_order() { + let mut test_ext = setup_test_externalities(&[100, 300]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + + // Benchmarked storage weight: 300 + let info = DispatchInfo { weight: Weight::from_parts(100, 300), ..Default::default() }; + + // Actual weight is 50 + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(50, 250)), + pays_fee: Default::default(), + }; + + let pre = StorageWeightReclaim::(PhantomData) + .pre_dispatch(&ALICE, CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(100)); + + assert_ok!(StorageWeightReclaim::::post_dispatch( + Some(pre), + &info, + &post_info, + LEN, + &Ok(()) + )); + // `CheckWeight` gets called after `StorageWeightReclaim` this time. + // The `CheckWeight` extension will refund `actual_weight` from `PostDispatchInfo` + // we always need to call `post_dispatch` to verify that they interoperate correctly. + assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); + + assert_eq!(BlockWeight::::get().total().proof_size(), 900); + }) + } + + #[test] + fn test_incorporates_check_weight_unspent_weight_on_negative_reverse_order() { + let mut test_ext = setup_test_externalities(&[100, 300]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + // Benchmarked storage weight: 50 + let info = DispatchInfo { weight: Weight::from_parts(100, 50), ..Default::default() }; + + // Actual weight is 25 + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(50, 25)), + pays_fee: Default::default(), + }; + + let pre = StorageWeightReclaim::(PhantomData) + .pre_dispatch(&ALICE, CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(100)); + + assert_ok!(StorageWeightReclaim::::post_dispatch( + Some(pre), + &info, + &post_info, + LEN, + &Ok(()) + )); + // `CheckWeight` gets called after `StorageWeightReclaim` this time. + // The `CheckWeight` extension will refund `actual_weight` from `PostDispatchInfo` + // we always need to call `post_dispatch` to verify that they interoperate correctly. + assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); + + assert_eq!(BlockWeight::::get().total().proof_size(), 1150); + }) + } + + #[test] + fn storage_size_reported_correctly() { + let mut test_ext = setup_test_externalities(&[1000]); + test_ext.execute_with(|| { + assert_eq!(get_proof_size(), Some(1000)); + }); + + let mut test_ext = new_test_ext(); + + let test_recorder = TestRecorder::new(&[0]); + + test_ext.register_extension(ProofSizeExt::new(test_recorder)); + + test_ext.execute_with(|| { + assert_eq!(get_proof_size(), Some(0)); + }); + } + + #[test] + fn storage_size_disabled_reported_correctly() { + let mut test_ext = setup_test_externalities(&[PROOF_RECORDING_DISABLED as usize]); + + test_ext.execute_with(|| { + assert_eq!(get_proof_size(), None); + }); + } + + #[test] + fn test_reclaim_helper() { + let mut test_ext = setup_test_externalities(&[1000, 1300, 1800]); + + test_ext.execute_with(|| { + let mut remaining_weight_meter = WeightMeter::with_limit(Weight::from_parts(0, 2000)); + let mut reclaim_helper = StorageWeightReclaimer::new(&remaining_weight_meter); + remaining_weight_meter.consume(Weight::from_parts(0, 500)); + let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); + + assert_eq!(reclaimed, Some(Weight::from_parts(0, 200))); + + remaining_weight_meter.consume(Weight::from_parts(0, 800)); + let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); + assert_eq!(reclaimed, Some(Weight::from_parts(0, 300))); + assert_eq!(remaining_weight_meter.remaining(), Weight::from_parts(0, 1200)); + }); + } + + #[test] + fn test_reclaim_helper_does_not_reclaim_negative() { + // Benchmarked weight does not change at all + let mut test_ext = setup_test_externalities(&[1000, 1300]); + + test_ext.execute_with(|| { + let mut remaining_weight_meter = WeightMeter::with_limit(Weight::from_parts(0, 1000)); + let mut reclaim_helper = StorageWeightReclaimer::new(&remaining_weight_meter); + let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); + + assert_eq!(reclaimed, Some(Weight::from_parts(0, 0))); + assert_eq!(remaining_weight_meter.remaining(), Weight::from_parts(0, 1000)); + }); + + // Benchmarked weight increases less than storage proof consumes + let mut test_ext = setup_test_externalities(&[1000, 1300]); + + test_ext.execute_with(|| { + let mut remaining_weight_meter = WeightMeter::with_limit(Weight::from_parts(0, 1000)); + let mut reclaim_helper = StorageWeightReclaimer::new(&remaining_weight_meter); + remaining_weight_meter.consume(Weight::from_parts(0, 0)); + let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); + + assert_eq!(reclaimed, Some(Weight::from_parts(0, 0))); + }); + } + + /// Just here for doc purposes + fn get_benched_weight() -> Weight { + Weight::from_parts(0, 5) + } + + /// Just here for doc purposes + fn do_work() {} + + #[docify::export_content(simple_reclaimer_example)] + fn reclaim_with_weight_meter() { + let mut remaining_weight_meter = WeightMeter::with_limit(Weight::from_parts(10, 10)); + + let benched_weight = get_benched_weight(); + + // It is important to instantiate the `StorageWeightReclaimer` before we consume the weight + // for a piece of work from the weight meter. + let mut reclaim_helper = StorageWeightReclaimer::new(&remaining_weight_meter); + + if remaining_weight_meter.try_consume(benched_weight).is_ok() { + // Perform some work that takes has `benched_weight` storage weight. + do_work(); + + // Reclaimer will detect that we only consumed 2 bytes, so 3 bytes are reclaimed. + let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); + + // We reclaimed 3 bytes of storage size! + assert_eq!(reclaimed, Some(Weight::from_parts(0, 3))); + assert_eq!(BlockWeight::::get().total().proof_size(), 10); + assert_eq!(remaining_weight_meter.remaining(), Weight::from_parts(10, 8)); + } + } + + #[test] + fn test_reclaim_helper_works_with_meter() { + // The node will report 12 - 10 = 2 consumed storage size between the calls. + let mut test_ext = setup_test_externalities(&[10, 12]); + + test_ext.execute_with(|| { + // Initial storage size is 10. + set_current_storage_weight(10); + reclaim_with_weight_meter(); + }); + } +} diff --git a/cumulus/primitives/timestamp/src/lib.rs b/cumulus/primitives/timestamp/src/lib.rs index 535c4a2a7268b9df053021d824fd0fbb0599845b..e6aba6d0bb74043c134400387d84429cf0a712d1 100644 --- a/cumulus/primitives/timestamp/src/lib.rs +++ b/cumulus/primitives/timestamp/src/lib.rs @@ -22,7 +22,7 @@ //! access to any clock from the runtime the timestamp is always passed as an inherent into the //! runtime. To check this inherent when validating the block, we will use the relay chain slot. As //! the relay chain slot is derived from a timestamp, we can easily convert it back to a timestamp -//! by muliplying it with the slot duration. By comparing the relay chain slot derived timestamp +//! by multiplying it with the slot duration. By comparing the relay chain slot derived timestamp //! with the timestamp we can ensure that the parachain timestamp is reasonable. #![cfg_attr(not(feature = "std"), no_std)] diff --git a/cumulus/primitives/utility/Cargo.toml b/cumulus/primitives/utility/Cargo.toml index 45c0e6670942e6b9b1ba0e6b1e80a1f0b357ff50..1e2c300b9ba257d2c8fb998689ae45847099dd63 100644 --- a/cumulus/primitives/utility/Cargo.toml +++ b/cumulus/primitives/utility/Cargo.toml @@ -26,7 +26,6 @@ polkadot-runtime-parachains = { path = "../../../polkadot/runtime/parachains", d xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../../polkadot/xcm/xcm-executor", default-features = false } xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot/xcm/xcm-builder", default-features = false } -pallet-xcm-benchmarks = { path = "../../../polkadot/xcm/pallet-xcm-benchmarks", default-features = false } # Cumulus cumulus-primitives-core = { path = "../core", default-features = false } @@ -39,7 +38,6 @@ std = [ "frame-support/std", "log/std", "pallet-asset-conversion/std", - "pallet-xcm-benchmarks/std", "polkadot-runtime-common/std", "polkadot-runtime-parachains/std", "sp-io/std", @@ -54,7 +52,6 @@ runtime-benchmarks = [ "cumulus-primitives-core/runtime-benchmarks", "frame-support/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", - "pallet-xcm-benchmarks/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", "polkadot-runtime-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index 0d8921227429c5c1e0f62a4f3c3f93db4ad73bcb..d5d411356dc385948f31730ba65dcd26074d0336 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -141,7 +141,7 @@ impl< ) -> Result { log::trace!(target: "xcm::weight", "TakeFirstAssetTrader::buy_weight weight: {:?}, payment: {:?}, context: {:?}", weight, payment, context); - // Make sure we dont enter twice + // Make sure we don't enter twice if self.0.is_some() { return Err(XcmError::NotWithdrawable) } @@ -176,7 +176,7 @@ impl< // Convert to the same kind of asset, with the required fungible balance let required = first.id.clone().into_asset(asset_balance.into()); - // Substract payment + // Subtract payment let unused = payment.checked_sub(required.clone()).map_err(|_| XcmError::TooExpensive)?; // record weight and asset @@ -203,7 +203,7 @@ impl< // Calculate asset_balance // This read should have already be cached in buy_weight - let (asset_balance, outstanding_minus_substracted) = + let (asset_balance, outstanding_minus_subtracted) = FeeCharger::charge_weight_in_fungibles(local_asset_id, weight).ok().map( |asset_balance| { // Require at least a drop of minimum_balance @@ -221,16 +221,15 @@ impl< )?; // Convert balances into u128 - let outstanding_minus_substracted: u128 = - outstanding_minus_substracted.saturated_into(); + let outstanding_minus_subtracted: u128 = outstanding_minus_subtracted.saturated_into(); let asset_balance: u128 = asset_balance.saturated_into(); - // Construct outstanding_concrete_asset with the same location id and substracted + // Construct outstanding_concrete_asset with the same location id and subtracted // balance let outstanding_concrete_asset: Asset = - (id.clone(), outstanding_minus_substracted).into(); + (id.clone(), outstanding_minus_subtracted).into(); - // Substract from existing weight and balance + // Subtract from existing weight and balance weight_outstanding = weight_outstanding.saturating_sub(weight); // Override AssetTraderRefunder @@ -263,9 +262,10 @@ impl< } } -/// XCM fee depositor to which we implement the TakeRevenue trait -/// It receives a Transact implemented argument, a 32 byte convertible acocuntId, and the fee -/// receiver account FungiblesMutateAdapter should be identical to that implemented by WithdrawAsset +/// XCM fee depositor to which we implement the `TakeRevenue` trait. +/// It receives a `Transact` implemented argument and a 32 byte convertible `AccountId`, and the fee +/// receiver account's `FungiblesMutateAdapter` should be identical to that implemented by +/// `WithdrawAsset`. pub struct XcmFeesTo32ByteAccount( PhantomData<(FungiblesMutateAdapter, AccountId, ReceiverAccount)>, ); @@ -760,10 +760,11 @@ mod test_trader { } } -/// Implementation of `pallet_xcm_benchmarks::EnsureDelivery` which helps to ensure delivery to the +/// Implementation of `xcm_builder::EnsureDelivery` which helps to ensure delivery to the /// parent relay chain. Deposits existential deposit for origin (if needed). /// Deposits estimated fee to the origin account (if needed). -/// Allows to trigger additional logic for specific `ParaId` (e.g. open HRMP channel) (if neeeded). +/// Allows triggering of additional logic for a specific `ParaId` (e.g. to open an HRMP channel) if +/// needed. #[cfg(feature = "runtime-benchmarks")] pub struct ToParentDeliveryHelper( sp_std::marker::PhantomData<(XcmConfig, ExistentialDeposit, PriceForDelivery)>, @@ -774,17 +775,22 @@ impl< XcmConfig: xcm_executor::Config, ExistentialDeposit: Get>, PriceForDelivery: PriceForMessageDelivery, - > pallet_xcm_benchmarks::EnsureDelivery + > xcm_builder::EnsureDelivery for ToParentDeliveryHelper { fn ensure_successful_delivery( origin_ref: &Location, - _dest: &Location, + dest: &Location, fee_reason: xcm_executor::traits::FeeReason, ) -> (Option, Option) { use xcm::latest::{MAX_INSTRUCTIONS_TO_DECODE, MAX_ITEMS_IN_ASSETS}; use xcm_executor::{traits::FeeManager, FeesMode}; + // check if the destination is relay/parent + if dest.ne(&Location::parent()) { + return (None, None); + } + let mut fees_mode = None; if !XcmConfig::FeeManager::is_waived(Some(origin_ref), fee_reason) { // if not waived, we need to set up accounts for paying and receiving fees diff --git a/cumulus/scripts/scale_encode_genesis/index.js b/cumulus/scripts/scale_encode_genesis/index.js index f612e6da79dd51452f46bcd0e626b5648b77f2e4..c6600e406361178524998b1a212e25fb97619957 100644 --- a/cumulus/scripts/scale_encode_genesis/index.js +++ b/cumulus/scripts/scale_encode_genesis/index.js @@ -19,14 +19,14 @@ async function connect(endpoint, types = {}) { } if (!process.argv[2] || !process.argv[3]) { - console.log("usage: node generate_keys [rpc enpoint]"); + console.log("usage: node generate_keys [rpc endpoint]"); exit(); } const input = process.argv[2]; const output = process.argv[3]; // default to localhost and the default Substrate port -const rpcEnpoint = process.argv[4] || "ws://localhost:9944"; +const rpcEndpoint = process.argv[4] || "ws://localhost:9944"; console.log("Processing", input, output); fs.readFile(input, "utf8", (err, data) => { @@ -38,8 +38,8 @@ fs.readFile(input, "utf8", (err, data) => { const genesis = JSON.parse(data); console.log("loaded genesis, length = ", genesis.length); - console.log(`Connecting to RPC endpoint: ${rpcEnpoint}`); - connect(rpcEnpoint) + console.log(`Connecting to RPC endpoint: ${rpcEndpoint}`); + connect(rpcEndpoint) .then((api) => { console.log('Connected'); const setStorage = api.tx.system.setStorage(genesis); diff --git a/cumulus/scripts/temp_parachain_types.json b/cumulus/scripts/temp_parachain_types.json index f550a6774450deb8abc9651d15a08b1b34b28c48..2509d32be9fd2cab453c5fd440b9879fca317c17 100644 --- a/cumulus/scripts/temp_parachain_types.json +++ b/cumulus/scripts/temp_parachain_types.json @@ -54,7 +54,7 @@ "validity_votes": "Vec", "validator_indices": "BitVec" }, - "CandidatePendingAvailablility": { + "CandidatePendingAvailability": { "core": "u32", "descriptor": "CandidateDescriptor", "availability_votes": "BitVec", diff --git a/cumulus/test/client/Cargo.toml b/cumulus/test/client/Cargo.toml index 7190172101cb509f7dd7c19ad25dc6d4d54036e7..028733ce23554967ee46e5ae8f2adebf6da70eb6 100644 --- a/cumulus/test/client/Cargo.toml +++ b/cumulus/test/client/Cargo.toml @@ -41,6 +41,7 @@ cumulus-test-relay-sproof-builder = { path = "../relay-sproof-builder" } cumulus-primitives-core = { path = "../../primitives/core" } cumulus-primitives-proof-size-hostfunction = { path = "../../primitives/proof-size-hostfunction" } cumulus-primitives-parachain-inherent = { path = "../../primitives/parachain-inherent" } +cumulus-primitives-storage-weight-reclaim = { path = "../../primitives/storage-weight-reclaim" } [features] runtime-benchmarks = [ diff --git a/cumulus/test/client/src/lib.rs b/cumulus/test/client/src/lib.rs index df63f683de6b4312a953bbbf9f03862eac7ce451..c46f4da7f6788cf53dfdd6d7be5c221d81562e70 100644 --- a/cumulus/test/client/src/lib.rs +++ b/cumulus/test/client/src/lib.rs @@ -151,6 +151,7 @@ pub fn generate_extrinsic_with_pair( frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::::new(), ); let function = function.into(); @@ -158,7 +159,7 @@ pub fn generate_extrinsic_with_pair( let raw_payload = SignedPayload::from_raw( function.clone(), extra.clone(), - ((), VERSION.spec_version, genesis_block, current_block_hash, (), (), ()), + ((), VERSION.spec_version, genesis_block, current_block_hash, (), (), (), ()), ); let signature = raw_payload.using_encoded(|e| origin.sign(e)); @@ -203,13 +204,16 @@ pub fn validate_block( let mut ext_ext = ext.ext(); let heap_pages = HeapAllocStrategy::Static { extra_pages: 1024 }; - let executor = WasmExecutor::::builder() - .with_execution_method(WasmExecutionMethod::default()) - .with_max_runtime_instances(1) - .with_runtime_cache_size(2) - .with_onchain_heap_alloc_strategy(heap_pages) - .with_offchain_heap_alloc_strategy(heap_pages) - .build(); + let executor = WasmExecutor::<( + sp_io::SubstrateHostFunctions, + cumulus_primitives_proof_size_hostfunction::storage_proof_size::HostFunctions, + )>::builder() + .with_execution_method(WasmExecutionMethod::default()) + .with_max_runtime_instances(1) + .with_runtime_cache_size(2) + .with_onchain_heap_alloc_strategy(heap_pages) + .with_offchain_heap_alloc_strategy(heap_pages) + .build(); executor .uncached_call( diff --git a/cumulus/test/runtime/Cargo.toml b/cumulus/test/runtime/Cargo.toml index 5902a62512bed772318145ccdb954ff2dfef4c92..449a8b819bc074e0c891d99d6fa2f42480f56ce6 100644 --- a/cumulus/test/runtime/Cargo.toml +++ b/cumulus/test/runtime/Cargo.toml @@ -39,6 +39,7 @@ sp-version = { path = "../../../substrate/primitives/version", default-features # Cumulus cumulus-pallet-parachain-system = { path = "../../pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook"] } cumulus-primitives-core = { path = "../../primitives/core", default-features = false } +cumulus-primitives-storage-weight-reclaim = { path = "../../primitives/storage-weight-reclaim", default-features = false } [build-dependencies] substrate-wasm-builder = { path = "../../../substrate/utils/wasm-builder", optional = true } @@ -49,6 +50,7 @@ std = [ "codec/std", "cumulus-pallet-parachain-system/std", "cumulus-primitives-core/std", + "cumulus-primitives-storage-weight-reclaim/std", "frame-executive/std", "frame-support/std", "frame-system-rpc-runtime-api/std", diff --git a/cumulus/test/runtime/src/lib.rs b/cumulus/test/runtime/src/lib.rs index 6068f895c83bf6adba5e2d7fa68b3aa2b8821204..5127b63f27155835f9f8bcd4ec50a4b47f9cc332 100644 --- a/cumulus/test/runtime/src/lib.rs +++ b/cumulus/test/runtime/src/lib.rs @@ -139,7 +139,7 @@ pub fn native_version() -> NativeVersion { NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } } -/// We assume that ~10% of the block weight is consumed by `on_initalize` handlers. +/// We assume that ~10% of the block weight is consumed by `on_initialize` handlers. /// This is used to limit the maximal weight of a single extrinsic. const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10); /// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used @@ -177,7 +177,7 @@ parameter_types! { pub const SS58Prefix: u8 = 42; } -#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig)] impl frame_system::Config for Runtime { /// The identifier used to distinguish between accounts. type AccountId = AccountId; @@ -331,6 +331,7 @@ pub type SignedExtra = ( frame_system::CheckNonce, frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = @@ -376,7 +377,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } diff --git a/cumulus/test/service/Cargo.toml b/cumulus/test/service/Cargo.toml index b26f0b9967cf75bdd45d1cae3dea7140ada6dd6c..45e21432f5b82e58d208088842c813986c94dc88 100644 --- a/cumulus/test/service/Cargo.toml +++ b/cumulus/test/service/Cargo.toml @@ -14,7 +14,7 @@ path = "src/main.rs" [dependencies] async-trait = "0.1.74" -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.0.0" } criterion = { version = "0.5.1", features = ["async_tokio"] } jsonrpsee = { version = "0.22", features = ["server"] } @@ -81,6 +81,7 @@ cumulus-relay-chain-minimal-node = { path = "../../client/relay-chain-minimal-no cumulus-client-pov-recovery = { path = "../../client/pov-recovery" } cumulus-test-relay-sproof-builder = { path = "../relay-sproof-builder" } cumulus-pallet-parachain-system = { path = "../../pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook"] } +cumulus-primitives-storage-weight-reclaim = { path = "../../primitives/storage-weight-reclaim" } pallet-timestamp = { path = "../../../substrate/frame/timestamp" } [dev-dependencies] diff --git a/cumulus/test/service/benches/validate_block.rs b/cumulus/test/service/benches/validate_block.rs index a614863803e09e89ff9671dd6916ccbfeb657278..d95f733969bce01b16229994ad4f88f6fea312e1 100644 --- a/cumulus/test/service/benches/validate_block.rs +++ b/cumulus/test/service/benches/validate_block.rs @@ -47,7 +47,7 @@ fn create_extrinsics( src_accounts: &[sr25519::Pair], dst_accounts: &[sr25519::Pair], ) -> (usize, Vec) { - // Add as many tranfer extrinsics as possible into a single block. + // Add as many transfer extrinsics as possible into a single block. let mut block_builder = BlockBuilderBuilder::new(client) .on_parent_block(client.chain_info().best_hash) .with_parent_block_number(client.chain_info().best_number) diff --git a/cumulus/test/service/src/lib.rs b/cumulus/test/service/src/lib.rs index 1c2e1db974149f2cae9ec3cad4ca16aa75036d8c..3af3901d175e5a113f137741a5de2ca11544c238 100644 --- a/cumulus/test/service/src/lib.rs +++ b/cumulus/test/service/src/lib.rs @@ -112,7 +112,7 @@ pub type AnnounceBlockFn = Arc>) + Send + Sync>; pub struct RuntimeExecutor; impl sc_executor::NativeExecutionDispatch for RuntimeExecutor { - type ExtendHostFunctions = (); + type ExtendHostFunctions = cumulus_client_service::storage_proof_size::HostFunctions; fn dispatch(method: &str, data: &[u8]) -> Option> { cumulus_test_runtime::api::dispatch(method, data) @@ -735,7 +735,7 @@ pub fn node_config( tokio_handle: tokio::runtime::Handle, key: Sr25519Keyring, nodes: Vec, - nodes_exlusive: bool, + nodes_exclusive: bool, para_id: ParaId, is_collator: bool, endowed_accounts: Vec, @@ -759,7 +759,7 @@ pub fn node_config( None, ); - if nodes_exlusive { + if nodes_exclusive { network_config.default_peers_set.reserved_nodes = nodes; network_config.default_peers_set.non_reserved_mode = sc_network::config::NonReservedPeerMode::Deny; @@ -894,11 +894,12 @@ pub fn construct_extrinsic( frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::::new(), ); let raw_payload = runtime::SignedPayload::from_raw( function.clone(), extra.clone(), - ((), runtime::VERSION.spec_version, genesis_block, current_block_hash, (), (), ()), + ((), runtime::VERSION.spec_version, genesis_block, current_block_hash, (), (), (), ()), ); let signature = raw_payload.using_encoded(|e| caller.sign(e)); runtime::UncheckedExtrinsic::new_signed( diff --git a/cumulus/zombienet/tests/0002-pov_recovery.toml b/cumulus/zombienet/tests/0002-pov_recovery.toml index fe42fd4b2f6681154e89d5e7274618a8f307dfab..15a61eba2a0342bce55f805473d2c3ba9b51c7f8 100644 --- a/cumulus/zombienet/tests/0002-pov_recovery.toml +++ b/cumulus/zombienet/tests/0002-pov_recovery.toml @@ -4,7 +4,7 @@ default_command = "polkadot" chain = "rococo-local" -[relaychain.genesis.runtimeGenesis.patch.configuration.config] +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] # set parameters such that collators only connect to 1 validator as a backing group max_validators_per_core = 1 group_rotation_frequency = 100 # 10 mins diff --git a/docker/dockerfiles/binary_injected.Dockerfile b/docker/dockerfiles/binary_injected.Dockerfile index ac1fd5317c67cb4cbef1044a6e2e3379f8ff4662..c8930bd83f0274990aff281e84d75b7add9d1289 100644 --- a/docker/dockerfiles/binary_injected.Dockerfile +++ b/docker/dockerfiles/binary_injected.Dockerfile @@ -2,7 +2,7 @@ FROM docker.io/parity/base-bin # This file allows building a Generic container image # based on one or multiple pre-built Linux binaries. -# Some defaults are set to polkadot but all can be overriden. +# Some defaults are set to polkadot but all can be overridden. SHELL ["/bin/bash", "-c"] diff --git a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile index fde9cc6e7cf3cbf3ca21f7d6b489ad2a637d1f42..4bfb73acda05880ef49570594d0769d1e5e4b147 100644 --- a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile +++ b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile @@ -45,7 +45,7 @@ RUN mkdir -p /home/nonroot/bridges-polkadot-sdk COPY ./artifacts/bridges-polkadot-sdk /home/nonroot/bridges-polkadot-sdk # also prepare `generate_hex_encoded_call` for running RUN set -eux; \ - cd /home/nonroot/bridges-polkadot-sdk/bridges/testing/utils/generate_hex_encoded_call; \ + cd /home/nonroot/bridges-polkadot-sdk/bridges/testing/framework/utils/generate_hex_encoded_call; \ npm install # check if executable works in this container diff --git a/docker/scripts/build-injected.sh b/docker/scripts/build-injected.sh index f415cf43c0eeefecbe0fe2a0649bbe9df0e2f960..749d0fa335cc1248fcde1770046fc7c1e31e9fbe 100755 --- a/docker/scripts/build-injected.sh +++ b/docker/scripts/build-injected.sh @@ -20,7 +20,7 @@ PROJECT_ROOT=${PROJECT_ROOT:-$(git rev-parse --show-toplevel)} DOCKERFILE=${DOCKERFILE:-docker/dockerfiles/binary_injected.Dockerfile} VERSION_TOML=$(grep "^version " $PROJECT_ROOT/Cargo.toml | grep -oE "([0-9\.]+-?[0-9]+)") -#n The following VAR have default that can be overriden +#n The following VAR have default that can be overridden DOCKER_OWNER=${DOCKER_OWNER:-parity} # We may get 1..n binaries, comma separated diff --git a/docs/RELEASE.md b/docs/RELEASE.md index 6d681d78f367abff313c63adb1559ea6aa06dbc7..e73be2779a99426203e209da846f938c0f73cceb 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -18,10 +18,16 @@ Rococo. To easily refer to a release, it shall be named by its date in the form ## Crate -We try to follow [SemVer 2.0.0](https://semver.org/) as best as possible for versioning our crates. SemVer requires a -piece of software to first declare a public API. The public API of the Polkadot SDK is hereby declared as the sum of all -crates' public APIs. +We try to follow [SemVer 2.0.0](https://semver.org/) as best as possible for versioning our crates. The definitions of +`major`, `minor` and `patch` version for Rust crates are slightly altered from their standard for pre `1.0.0` versions. +Quoting [rust-lang.org](https://doc.rust-lang.org/cargo/reference/semver.html): +>Initial development releases starting with “0.y.z” can treat changes in “y” as a major release, and “z” as a minor +release. “0.0.z” releases are always major changes. This is because Cargo uses the convention that only changes in the +left-most non-zero component are considered incompatible. + +SemVer requires a piece of software to first declare a public API. The public API of the Polkadot SDK +is hereby declared as the sum of all crates' public APIs. Inductively, the public API of our library crates is declared as all public items that are neither: - Inside a `__private` module diff --git a/docs/contributor/container.md b/docs/contributor/container.md index dd44b31bfe96994ed54e3eca8610e7d8778f5261..9c542f411c81f4237e69ffaf63d4686eeac204e4 100644 --- a/docs/contributor/container.md +++ b/docs/contributor/container.md @@ -16,7 +16,7 @@ Parity builds and publishes a container image that can be found as `docker.io/pa ## Parity CI image Parity maintains and uses internally a generic "CI" image that can be used as a base to build binaries: [Parity CI -container image](https://github.com/paritytech/scripts/tree/master/dockerfiles/ci-linux): +container image](https://github.com/paritytech/scripts/tree/master/dockerfiles/ci-unified): The command below allows building a Linux binary without having to even install Rust or any dependency locally: @@ -24,14 +24,11 @@ The command below allows building a Linux binary without having to even install docker run --rm -it \ -w /polkadot-sdk \ -v $(pwd):/polkadot-sdk \ - paritytech/ci-linux:production \ + paritytech/ci-unified:bullseye-1.75.0-2024-01-22-v20240222 \ cargo build --release --locked -p polkadot-parachain-bin --bin polkadot-parachain sudo chown -R $(id -u):$(id -g) target/ ``` -If you want to reproduce other steps of CI process you can use the following -[guide](https://github.com/paritytech/scripts#gitlab-ci-for-building-docker-images). - ## Injected image Injecting a binary inside a base image is the quickest option to get a working container image. This only works if you diff --git a/docs/mermaid/IA.mmd b/docs/mermaid/IA.mmd index 93d3e92814cf1307fa7c0ea9f290ed21edae58fb..4eb50bcf96a8932de3fa90748fcfeb3ca7f02a5f 100644 --- a/docs/mermaid/IA.mmd +++ b/docs/mermaid/IA.mmd @@ -3,7 +3,7 @@ flowchart devhub --> polkadot_sdk devhub --> reference_docs - devhub --> tutorial + devhub --> guides polkadot_sdk --> substrate polkadot_sdk --> frame diff --git a/docs/mermaid/outer_runtime_types.mmd b/docs/mermaid/outer_runtime_types.mmd new file mode 100644 index 0000000000000000000000000000000000000000..c909df16af1ffbb697e3d363634fa218005a9c32 --- /dev/null +++ b/docs/mermaid/outer_runtime_types.mmd @@ -0,0 +1,3 @@ +flowchart LR + RuntimeCall --"TryInto"--> PalletCall + PalletCall --"Into"--> RuntimeCall diff --git a/docs/sdk/Cargo.toml b/docs/sdk/Cargo.toml index 05aced8751aee8286c78ca4f5baeb398a0ac83f6..434202ed69365549b4e6ddf0c1b7a4d9c1e303c5 100644 --- a/docs/sdk/Cargo.toml +++ b/docs/sdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-sdk-docs" -description = "The one stop shop for developers of the polakdot-sdk" +description = "The one stop shop for developers of the polkadot-sdk" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "paritytech.github.io" repository.workspace = true @@ -17,9 +17,13 @@ workspace = true # Needed for all FRAME-based code parity-scale-codec = { version = "3.0.0", default-features = false } scale-info = { version = "2.6.0", default-features = false } -frame = { path = "../../substrate/frame", features = ["experimental", "runtime"] } +frame = { path = "../../substrate/frame", features = [ + "experimental", + "runtime", +] } pallet-examples = { path = "../../substrate/frame/examples" } pallet-default-config-example = { path = "../../substrate/frame/examples/default-config" } +pallet-example-offchain-worker = { path = "../../substrate/frame/examples/offchain-worker" } # How we build docs in rust-docs simple-mermaid = "0.1.1" @@ -30,8 +34,12 @@ node-cli = { package = "staging-node-cli", path = "../../substrate/bin/node/cli" kitchensink-runtime = { path = "../../substrate/bin/node/runtime" } chain-spec-builder = { package = "staging-chain-spec-builder", path = "../../substrate/bin/utils/chain-spec-builder" } subkey = { path = "../../substrate/bin/utils/subkey" } +frame-system = { path = "../../substrate/frame/system", default-features = false } +frame-support = { path = "../../substrate/frame/support", default-features = false } +frame-executive = { path = "../../substrate/frame/executive", default-features = false } +pallet-example-single-block-migrations = { path = "../../substrate/frame/examples/single-block-migrations" } -# Substrate +# Substrate Client sc-network = { path = "../../substrate/client/network" } sc-rpc-api = { path = "../../substrate/client/rpc-api" } sc-rpc = { path = "../../substrate/client/rpc" } @@ -43,6 +51,7 @@ sc-consensus-grandpa = { path = "../../substrate/client/consensus/grandpa" } sc-consensus-beefy = { path = "../../substrate/client/consensus/beefy" } sc-consensus-manual-seal = { path = "../../substrate/client/consensus/manual-seal" } sc-consensus-pow = { path = "../../substrate/client/consensus/pow" } + substrate-wasm-builder = { path = "../../substrate/utils/wasm-builder" } # Cumulus @@ -52,7 +61,19 @@ cumulus-pallet-parachain-system = { path = "../../cumulus/pallets/parachain-syst ] } parachain-info = { package = "staging-parachain-info", path = "../../cumulus/parachains/pallets/parachain-info" } pallet-aura = { path = "../../substrate/frame/aura", default-features = false } + +# Pallets and FRAME internals pallet-timestamp = { path = "../../substrate/frame/timestamp" } +pallet-balances = { path = "../../substrate/frame/balances" } +pallet-assets = { path = "../../substrate/frame/assets" } +pallet-transaction-payment = { path = "../../substrate/frame/transaction-payment" } +pallet-utility = { path = "../../substrate/frame/utility" } +pallet-multisig = { path = "../../substrate/frame/multisig" } +pallet-proxy = { path = "../../substrate/frame/proxy" } +pallet-authorship = { path = "../../substrate/frame/authorship" } +pallet-collective = { path = "../../substrate/frame/collective" } +pallet-democracy = { path = "../../substrate/frame/democracy" } +pallet-scheduler = { path = "../../substrate/frame/scheduler" } # Primitives sp-io = { path = "../../substrate/primitives/io" } @@ -60,13 +81,15 @@ sp-api = { path = "../../substrate/primitives/api" } sp-core = { path = "../../substrate/primitives/core" } sp-keyring = { path = "../../substrate/primitives/keyring" } sp-runtime = { path = "../../substrate/primitives/runtime" } +sp-arithmetic = { path = "../../substrate/primitives/arithmetic" } -# XCM -xcm = { package = "staging-xcm", path = "../../polkadot/xcm" } +# Misc pallet dependencies +pallet-referenda = { path = "../../substrate/frame/referenda" } +pallet-broker = { path = "../../substrate/frame/broker" } +pallet-babe = { path = "../../substrate/frame/babe" } -[dev-dependencies] -parity-scale-codec = "3.6.5" -scale-info = "2.9.0" +sp-offchain = { path = "../../substrate/primitives/offchain" } +sp-version = { path = "../../substrate/primitives/version" } -[features] -experimental = ["pallet-aura/experimental"] +# XCM +xcm = { package = "staging-xcm", path = "../../polkadot/xcm" } diff --git a/docs/sdk/headers/header.html b/docs/sdk/headers/header.html new file mode 100644 index 0000000000000000000000000000000000000000..e28458c4ccc791d9a72613ffb530b685828ea828 --- /dev/null +++ b/docs/sdk/headers/header.html @@ -0,0 +1,144 @@ + + + diff --git a/docs/sdk/headers/theme.css b/docs/sdk/headers/theme.css new file mode 100644 index 0000000000000000000000000000000000000000..a488e15c36b70ee76b14f429434daf0717bc0320 --- /dev/null +++ b/docs/sdk/headers/theme.css @@ -0,0 +1,17 @@ +:root { + --polkadot-pink: #E6007A; + --polkadot-green: #56F39A; + --polkadot-lime: #D3FF33; + --polkadot-cyan: #00B2FF; + --polkadot-purple: #552BBF; +} + +body.sdk-docs { + nav.sidebar>div.sidebar-crate>a>img { + width: 190px; + } + + nav.sidebar { + flex: 0 0 250px; + } +} diff --git a/docs/sdk/headers/toc.html b/docs/sdk/headers/toc.html deleted file mode 100644 index a4a074cb4f3153cb135da8608462d4ecf59144cb..0000000000000000000000000000000000000000 --- a/docs/sdk/headers/toc.html +++ /dev/null @@ -1,54 +0,0 @@ - - diff --git a/docs/sdk/src/guides/your_first_pallet/mod.rs b/docs/sdk/src/guides/your_first_pallet/mod.rs index 29cdda36ed15ba3fcf964494732fc8364defff45..c6e0dd0edf8925105661591d0831d65ff8ed02be 100644 --- a/docs/sdk/src/guides/your_first_pallet/mod.rs +++ b/docs/sdk/src/guides/your_first_pallet/mod.rs @@ -105,8 +105,8 @@ //! This macro will call `.into()` under the hood. #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", transfer_better)] //! -//! Moreover, you will learn in the [Safe Defensive Programming -//! section](crate::reference_docs::safe_defensive_programming) that it is always recommended to use +//! Moreover, you will learn in the [Defensive Programming +//! section](crate::reference_docs::defensive_programming) that it is always recommended to use //! safe arithmetic operations in your runtime. By using [`frame::traits::CheckedSub`], we can not //! only take a step in that direction, but also improve the error handing and make it slightly more //! ergonomic. @@ -128,8 +128,8 @@ //! //! Recall that within our pallet, (almost) all blocks of code are generic over ``. And, //! because `trait Config: frame_system::Config`, we can get access to all items in `Config` (or -//! `frame_system::Config`) using `T::NameOfItem`. This is all within the boundaries of how Rust -//! traits and generics work. If unfamiliar with this pattern, read +//! `frame_system::Config`) using `T::NameOfItem`. This is all within the boundaries of how +//! Rust traits and generics work. If unfamiliar with this pattern, read //! [`crate::reference_docs::trait_based_programming`] before going further. //! //! Crucially, a typical FRAME runtime contains a `struct Runtime`. The main role of this `struct` @@ -250,14 +250,16 @@ // of event is probably not the best. //! //! With the explanation out of the way, let's see how these components can be added. Both follow a -//! fairly familiar syntax: normal Rust enums, with an extra `#[frame::event/error]` attribute -//! attached. +//! fairly familiar syntax: normal Rust enums, with extra +//! [`#[frame::event]`](frame::pallet_macros::event) and +//! [`#[frame::error]`](frame::pallet_macros::error) attributes attached. #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Event)] #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Error)] //! -//! One slightly custom part of this is the `#[pallet::generate_deposit(pub(super) fn -//! deposit_event)]` part. Without going into too much detail, in order for a pallet to emit events -//! to the rest of the system, it needs to do two things: +//! One slightly custom part of this is the [`#[pallet::generate_deposit(pub(super) fn +//! deposit_event)]`](frame::pallet_macros::generate_deposit) part. Without going into too +//! much detail, in order for a pallet to emit events to the rest of the system, it needs to do two +//! things: //! //! 1. Declare a type in its `Config` that refers to the overarching event type of the runtime. In //! short, by doing this, the pallet is expressing an important bound: `type RuntimeEvent: @@ -266,11 +268,12 @@ //! store it where needed. //! //! 2. But, doing this conversion and storing is too much to expect each pallet to define. FRAME -//! provides a default way of storing events, and this is what `pallet::generate_deposit` is doing. +//! provides a default way of storing events, and this is what +//! [`pallet::generate_deposit`](frame::pallet_macros::generate_deposit) is doing. #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", config_v2)] //! //! > These `Runtime*` types are better explained in -//! > [`crate::reference_docs::frame_composite_enums`]. +//! > [`crate::reference_docs::frame_runtime_types`]. //! //! Then, we can rewrite the `transfer` dispatchable as such: #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", transfer_v2)] @@ -280,20 +283,20 @@ #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", runtime_v2)] //! //! In this snippet, the actual `RuntimeEvent` type (right hand side of `type RuntimeEvent = -//! RuntimeEvent`) is generated by `construct_runtime`. An interesting way to inspect this type is -//! to see its definition in rust-docs: +//! RuntimeEvent`) is generated by +//! [`construct_runtime`](frame::runtime::prelude::construct_runtime). An interesting way to inspect +//! this type is to see its definition in rust-docs: //! [`crate::guides::your_first_pallet::pallet_v2::tests::runtime_v2::RuntimeEvent`]. //! //! -//! //! ## What Next? //! //! The following topics where used in this guide, but not covered in depth. It is suggested to //! study them subsequently: //! -//! - [`crate::reference_docs::safe_defensive_programming`]. +//! - [`crate::reference_docs::defensive_programming`]. //! - [`crate::reference_docs::frame_origin`]. -//! - [`crate::reference_docs::frame_composite_enums`]. +//! - [`crate::reference_docs::frame_runtime_types`]. //! - The pallet we wrote in this guide was using `dev_mode`, learn more in //! [`frame::pallet_macros::config`]. //! - Learn more about the individual pallet items/macros, such as event and errors and call, in @@ -435,7 +438,7 @@ pub mod pallet { } ); - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type Block = MockBlock; // within pallet we just said `::AccountId`, now we @@ -714,7 +717,7 @@ pub mod pallet_v2 { } ); - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type Block = MockBlock; type AccountId = u64; diff --git a/docs/sdk/src/lib.rs b/docs/sdk/src/lib.rs index 075d9ddaffe5bd95bafa4ca1d06667c253d4a21b..e211476d2514419a3d0889ef426817342155c178 100644 --- a/docs/sdk/src/lib.rs +++ b/docs/sdk/src/lib.rs @@ -15,7 +15,7 @@ //! - Start by learning about the the [`polkadot_sdk`], its structure and context. //! - Then, head over the [`guides`]. This modules contains in-depth guides about the most important //! user-journeys of the Polkadot SDK. -//! - Whilst reading the guides, you might find back-links to [`crate::reference_docs`]. +//! - Whilst reading the guides, you might find back-links to [`reference_docs`]. //! - Finally, is the parent website of this crate that contains the //! list of further tools related to the Polkadot SDK. //! @@ -25,6 +25,11 @@ #![doc = simple_mermaid::mermaid!("../../mermaid/IA.mmd")] #![warn(rustdoc::broken_intra_doc_links)] #![warn(rustdoc::private_intra_doc_links)] +#![doc(html_favicon_url = "https://polkadot.network/favicon-32x32.png")] +#![doc( + html_logo_url = "https://europe1.discourse-cdn.com/standard21/uploads/polkadot2/original/1X/eb57081e2bb7c39e5fcb1a98b443e423fa4448ae.svg" +)] +#![doc(issue_tracker_base_url = "https://github.com/paritytech/polkadot-sdk/issues")] /// Meta information about this crate, how it is built, what principles dictates its evolution and /// how one can contribute to it. diff --git a/docs/sdk/src/meta_contributing.rs b/docs/sdk/src/meta_contributing.rs index 7ecf8b0adfd3a2038bd32b6d6b830c71782cd1b2..fcdcea9934bb6b8cf7ee6ec090ebd0939888238c 100644 --- a/docs/sdk/src/meta_contributing.rs +++ b/docs/sdk/src/meta_contributing.rs @@ -101,7 +101,7 @@ //! * Before even getting started, what is with all of this ``? We link to //! [`crate::reference_docs::trait_based_programming`]. //! * First, the name. Why is this called `pallet::call`? This goes back to `enum Call`, which is -//! explained in [`crate::reference_docs::frame_composite_enums`]. Build on top of this! +//! explained in [`crate::reference_docs::frame_runtime_types`]. Build on top of this! //! * Then, what is `origin`? Just an account id? [`crate::reference_docs::frame_origin`]. //! * Then, what is `DispatchResult`? Why is this called *dispatch*? Probably something that can be //! explained in the documentation of [`frame::prelude::DispatchResult`]. @@ -138,7 +138,9 @@ //! injected, run: //! //! ```sh -//! SKIP_WASM_BUILD=1 RUSTDOCFLAGS="--html-in-header $(pwd)/docs/sdk/headers/toc.html" cargo doc -p polkadot-sdk-docs --no-deps --open +//! SKIP_WASM_BUILD=1 \ +//! RUSTDOCFLAGS="--html-in-header $(pwd)/docs/sdk/headers/header.html --extend-css $(pwd)/docs/sdk/headers/theme.css --default-theme=ayu" \ +//! cargo doc -p polkadot-sdk-docs --no-deps --open //! ``` //! //! If even faster build time for docs is needed, you can temporarily remove most of the diff --git a/docs/sdk/src/polkadot_sdk/cumulus.rs b/docs/sdk/src/polkadot_sdk/cumulus.rs index 60c4839f9e2d680d6604cf12c0ba28f8d81765a2..9bd957c7c1c07fec93b330ce8a8e7ec8118d31c1 100644 --- a/docs/sdk/src/polkadot_sdk/cumulus.rs +++ b/docs/sdk/src/polkadot_sdk/cumulus.rs @@ -72,7 +72,7 @@ mod tests { mod system_pallets { use super::*; - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type Block = MockBlock; type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; @@ -110,12 +110,11 @@ mod tests { type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; type AllowMultipleBlocksPerSlot = ConstBool; - #[cfg(feature = "experimental")] type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; } #[docify::export(timestamp)] - #[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig as pallet_timestamp::DefaultConfig)] + #[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)] impl pallet_timestamp::Config for Runtime {} impl cumulus_pallet_aura_ext::Config for Runtime {} diff --git a/docs/sdk/src/polkadot_sdk/frame_runtime.rs b/docs/sdk/src/polkadot_sdk/frame_runtime.rs index c9eba7d64bd46b51230c1b06a96c6b89ffddd73d..f9b8a381365c4b3675485f3f32cb0cf751138275 100644 --- a/docs/sdk/src/polkadot_sdk/frame_runtime.rs +++ b/docs/sdk/src/polkadot_sdk/frame_runtime.rs @@ -87,93 +87,89 @@ //! * writing a runtime in pure Rust, as done in [this template](https://github.com/JoshOrndorff/frameless-node-template). //! * writing a runtime in AssemblyScript,as explored in [this project](https://github.com/LimeChain/subsembly). -#[cfg(test)] -mod tests { - use frame::prelude::*; +use frame::prelude::*; - /// A FRAME based pallet. This `mod` is the entry point for everything else. All - /// `#[pallet::xxx]` macros must be defined in this `mod`. Although, frame also provides an - /// experimental feature to break these parts into different `mod`s. See [`pallet_examples`] for - /// more. - #[docify::export] - #[frame::pallet(dev_mode)] - pub mod pallet { - use super::*; +/// A FRAME based pallet. This `mod` is the entry point for everything else. All +/// `#[pallet::xxx]` macros must be defined in this `mod`. Although, frame also provides an +/// experimental feature to break these parts into different `mod`s. See [`pallet_examples`] for +/// more. +#[docify::export] +#[frame::pallet(dev_mode)] +pub mod pallet { + use super::*; - /// The configuration trait of a pallet. Mandatory. Allows a pallet to receive types at a - /// later point from the runtime that wishes to contain it. It allows the pallet to be - /// parameterized over both types and values. - #[pallet::config] - pub trait Config: frame_system::Config { - /// A type that is not known now, but the runtime that will contain this pallet will - /// know it later, therefore we define it here as an associated type. - type RuntimeEvent: IsType<::RuntimeEvent> - + From>; + /// The configuration trait of a pallet. Mandatory. Allows a pallet to receive types at a + /// later point from the runtime that wishes to contain it. It allows the pallet to be + /// parameterized over both types and values. + #[pallet::config] + pub trait Config: frame_system::Config { + /// A type that is not known now, but the runtime that will contain this pallet will + /// know it later, therefore we define it here as an associated type. + type RuntimeEvent: IsType<::RuntimeEvent> + From>; - /// A parameterize-able value that we receive later via the `Get<_>` trait. - type ValueParameter: Get; + /// A parameterize-able value that we receive later via the `Get<_>` trait. + type ValueParameter: Get; - /// Similar to [`Config::ValueParameter`], but using `const`. Both are functionally - /// equal, but offer different tradeoffs. - const ANOTHER_VALUE_PARAMETER: u32; - } + /// Similar to [`Config::ValueParameter`], but using `const`. Both are functionally + /// equal, but offer different tradeoffs. + const ANOTHER_VALUE_PARAMETER: u32; + } - /// A mandatory struct in each pallet. All functions callable by external users (aka. - /// transactions) must be attached to this type (see [`frame::pallet_macros::call`]). For - /// convenience, internal (private) functions can also be attached to this type. - #[pallet::pallet] - pub struct Pallet(PhantomData); + /// A mandatory struct in each pallet. All functions callable by external users (aka. + /// transactions) must be attached to this type (see [`frame::pallet_macros::call`]). For + /// convenience, internal (private) functions can also be attached to this type. + #[pallet::pallet] + pub struct Pallet(PhantomData); - /// The events tha this pallet can emit. - #[pallet::event] - pub enum Event {} + /// The events tha this pallet can emit. + #[pallet::event] + pub enum Event {} - /// A storage item that this pallet contains. This will be part of the state root trie/root - /// of the blockchain. - #[pallet::storage] - pub type Value = StorageValue; + /// A storage item that this pallet contains. This will be part of the state root trie/root + /// of the blockchain. + #[pallet::storage] + pub type Value = StorageValue; - /// All *dispatchable* call functions (aka. transactions) are attached to `Pallet` in a - /// `impl` block. - #[pallet::call] - impl Pallet { - /// This will be callable by external users, and has two u32s as a parameter. - pub fn some_dispatchable( - _origin: OriginFor, - _param: u32, - _other_para: u32, - ) -> DispatchResult { - Ok(()) - } + /// All *dispatchable* call functions (aka. transactions) are attached to `Pallet` in a + /// `impl` block. + #[pallet::call] + impl Pallet { + /// This will be callable by external users, and has two u32s as a parameter. + pub fn some_dispatchable( + _origin: OriginFor, + _param: u32, + _other_para: u32, + ) -> DispatchResult { + Ok(()) } } +} - /// A simple runtime that contains the above pallet and `frame_system`, the mandatory pallet of - /// all runtimes. This runtime is for testing, but it shares a lot of similarities with a *real* - /// runtime. - #[docify::export] - pub mod runtime { - use super::pallet as pallet_example; - use frame::{prelude::*, testing_prelude::*}; - - // The major macro that amalgamates pallets into `enum Runtime` - construct_runtime!( - pub enum Runtime { - System: frame_system, - Example: pallet_example, - } - ); +/// A simple runtime that contains the above pallet and `frame_system`, the mandatory pallet of +/// all runtimes. This runtime is for testing, but it shares a lot of similarities with a *real* +/// runtime. +#[docify::export] +pub mod runtime { + use super::pallet as pallet_example; + use frame::{prelude::*, testing_prelude::*}; - // These `impl` blocks specify the parameters of each pallet's `trait Config`. - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] - impl frame_system::Config for Runtime { - type Block = MockBlock; + // The major macro that amalgamates pallets into `enum Runtime` + construct_runtime!( + pub enum Runtime { + System: frame_system, + Example: pallet_example, } + ); - impl pallet_example::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type ValueParameter = ConstU32<42>; - const ANOTHER_VALUE_PARAMETER: u32 = 42; - } + // These `impl` blocks specify the parameters of each pallet's `trait Config`. + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] + impl frame_system::Config for Runtime { + type Block = MockBlock; + } + + impl pallet_example::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ValueParameter = ConstU32<42>; + const ANOTHER_VALUE_PARAMETER: u32 = 42; } } diff --git a/docs/sdk/src/reference_docs/defensive_programming.rs b/docs/sdk/src/reference_docs/defensive_programming.rs new file mode 100644 index 0000000000000000000000000000000000000000..9828e1b50918f3e832523c686797091db034a985 --- /dev/null +++ b/docs/sdk/src/reference_docs/defensive_programming.rs @@ -0,0 +1,395 @@ +// 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. + +//! [Defensive programming](https://en.wikipedia.org/wiki/Defensive_programming) is a design paradigm that enables a program to continue +//! running despite unexpected behavior, input, or events that may arise in runtime. +//! Usually, unforeseen circumstances may cause the program to stop or, in the Rust context, +//! panic!. Defensive practices allow for these circumstances to be accounted for ahead of time +//! and for them to be handled gracefully, which is in line with the intended fault-tolerant and +//! deterministic nature of blockchains. +//! +//! The Polkadot SDK is built to reflect these principles and to facilitate their usage accordingly. +//! +//! ## General Overview +//! +//! When developing within the context of the Substrate runtime, there is one golden rule: +//! +//! ***DO NOT PANIC***. There are some exceptions, but generally, this is the default precedent. +//! +//! > It’s important to differentiate between the runtime and node. The runtime refers to the core +//! > business logic of a Substrate-based chain, whereas the node refers to the outer client, which +//! > deals with telemetry and gossip from other nodes. For more information, read about +//! > [Substrate's node +//! > architecture](crate::reference_docs::wasm_meta_protocol#node-vs-runtime). It’s also important +//! > to note that the criticality of the node is slightly lesser +//! > than that of the runtime, which is why you may see `unwrap()` or other “non-defensive” +//! > approaches +//! in a few places of the node's code repository. +//! +//! Most of these practices fall within Rust's +//! colloquial usage of proper error propagation, handling, and arithmetic-based edge cases. +//! +//! General guidelines: +//! +//! - **Avoid writing functions that could explicitly panic,** such as directly using `unwrap()` on +//! a [`Result`], or accessing an out-of-bounds index on a collection. Safer methods to access +//! collection types, i.e., `get()` which allow defensive handling of the resulting [`Option`] are +//! recommended to be used. +//! - **It may be acceptable to use `except()`,** but only if one is completely certain (and has +//! performed a check beforehand) that a value won't panic upon unwrapping. *Even this is +//! discouraged*, however, as future changes to that function could then cause that statement to +//! panic. It is important to ensure all possible errors are propagated and handled effectively. +//! - **If a function *can* panic,** it usually is prefaced with `unchecked_` to indicate its +//! unsafety. +//! - **If you are writing a function that could panic,** [document it!](https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html#documenting-components) +//! - **Carefully handle mathematical operations.** Many seemingly, simplistic operations, such as +//! **arithmetic** in the runtime, could present a number of issues [(see more later in this +//! document)](#integer-overflow). Use checked arithmetic wherever possible. +//! +//! These guidelines could be summarized in the following example, where `bad_pop` is prone to +//! panicking, and `good_pop` allows for proper error handling to take place: +//! +//!```ignore +//! // Bad pop always requires that we return something, even if vector/array is empty. +//! fn bad_pop(v: Vec) -> T {} +//! // Good pop allows us to return None from the Option if need be. +//! fn good_pop(v: Vec) -> Option {} +//! ``` +//! +//! ### Defensive Traits +//! +//! The [`Defensive`](frame::traits::Defensive) trait provides a number of functions, all of which +//! provide an alternative to 'vanilla' Rust functions, e.g.,: +//! +//! - [`defensive_unwrap_or()`](frame::traits::Defensive::defensive_unwrap_or) instead of +//! `unwrap_or()` +//! - [`defensive_ok_or()`](frame::traits::DefensiveOption::defensive_ok_or) instead of `ok_or()` +//! +//! Defensive methods use [`debug_assertions`](https://doc.rust-lang.org/reference/conditional-compilation.html#debug_assertions), which panic in development, but in +//! production/release, they will merely log an error (i.e., `log::error`). +//! +//! The [`Defensive`](frame::traits::Defensive) trait and its various implementations can be found +//! [here](frame::traits::Defensive). +//! +//! ## Integer Overflow +//! +//! The Rust compiler prevents static overflow from happening at compile time. +//! The compiler panics in **debug** mode in the event of an integer overflow. In +//! **release** mode, it resorts to silently _wrapping_ the overflowed amount in a modular fashion +//! (from the `MAX` back to zero). +//! +//! In runtime development, we don't always have control over what is being supplied +//! as a parameter. For example, even this simple add function could present one of two outcomes +//! depending on whether it is in **release** or **debug** mode: +//! +//! ```ignore +//! fn naive_add(x: u8, y: u8) -> u8 { +//! x + y +//! } +//! ``` +//! If we passed overflow-able values at runtime, this could panic (or wrap if in release). +//! +//! ```ignore +//! naive_add(250u8, 10u8); // In debug mode, this would panic. In release, this would return 4. +//! ``` +//! +//! It is the silent portion of this behavior that presents a real issue. Such behavior should be +//! made obvious, especially in blockchain development, where unsafe arithmetic could produce +//! unexpected consequences like a user balance over or underflowing. +//! +//! Fortunately, there are ways to both represent and handle these scenarios depending on our +//! specific use case natively built into Rust and libraries like [`sp_arithmetic`]. +//! +//! ## Infallible Arithmetic +//! +//! Both Rust and Substrate provide safe ways to deal with numbers and alternatives to floating +//! point arithmetic. +//! +//! Known scenarios that could be fallible should be avoided: i.e., avoiding the possibility of +//! dividing/modulo by zero at any point should be mitigated. One should be opting for a +//! `checked_*` method to introduce safe arithmetic in their code in most cases. +//! +//! A developer should use fixed-point instead of floating-point arithmetic to mitigate the +//! potential for inaccuracy, rounding errors, or other unexpected behavior. +//! +//! - [Fixed point types](sp_arithmetic::fixed_point) and their associated usage can be found here. +//! - [PerThing](sp_arithmetic::per_things) and its associated types can be found here. +//! +//! Using floating point number types (i.e., f32. f64) in the runtime should be avoided, as a single non-deterministic result could cause chaos for blockchain consensus along with the issues above. For more on the specifics of the peculiarities of floating point calculations, [watch this video by the Computerphile](https://www.youtube.com/watch?v=PZRI1IfStY0). +//! +//! The following methods demonstrate different ways to handle numbers natively in Rust safely, +//! without fear of panic or unexpected behavior from wrapping. +//! +//! ### Checked Arithmetic +//! +//! **Checked operations** utilize an `Option` as a return type. This allows for +//! catching any unexpected behavior in the event of an overflow through simple pattern matching. +//! +//! This is an example of a valid operation: +#![doc = docify::embed!("./src/reference_docs/defensive_programming.rs", checked_add_example)] +//! +//! This is an example of an invalid operation. In this case, a simulated integer overflow, which +//! would simply result in `None`: +#![doc = docify::embed!( + "./src/reference_docs/defensive_programming.rs", + checked_add_handle_error_example +)] +//! +//! Suppose you aren’t sure which operation to use for runtime math. In that case, checked +//! operations are the safest bet, presenting two predictable (and erroring) outcomes that can be +//! handled accordingly (Some and None). +//! +//! The following conventions can be seen within the Polkadot SDK, where it is +//! handled in two ways: +//! +//! - As an [`Option`], using the `if let` / `if` or `match` +//! - As a [`Result`], via `ok_or` (or similar conversion to [`Result`] from [`Option`]) +//! +//! #### Handling via Option - More Verbose +//! +//! Because wrapped operations return `Option`, you can use a more verbose/explicit form of error +//! handling via `if` or `if let`: +#![doc = docify::embed!("./src/reference_docs/defensive_programming.rs", increase_balance)] +//! +//! Optionally, match may also be directly used in a more concise manner: +#![doc = docify::embed!("./src/reference_docs/defensive_programming.rs", increase_balance_match)] +//! +//! This is generally a useful convention for handling checked types and most types that return +//! `Option`. +//! +//! #### Handling via Result - Less Verbose +//! +//! In the Polkadot SDK codebase, checked operations are handled as a `Result` via `ok_or`. This is +//! a less verbose way of expressing the above. This usage often boils down to the developer’s +//! preference: +#![doc = docify::embed!("./src/reference_docs/defensive_programming.rs", increase_balance_result)] +//! +//! ### Saturating Operations +//! +//! Saturating a number limits it to the type’s upper or lower bound, even if the integer type +//! overflowed in runtime. For example, adding to `u32::MAX` would simply limit itself to +//! `u32::MAX`: +#![doc = docify::embed!("./src/reference_docs/defensive_programming.rs", saturated_add_example)] +//! +//! Saturating calculations can be used if one is very sure that something won't overflow, but wants +//! to avoid introducing the notion of any potential-panic or wrapping behavior. +//! +//! There is also a series of defensive alternatives via +//! [`DefensiveSaturating`](frame::traits::DefensiveSaturating), which introduces the same behavior +//! of the [`Defensive`](frame::traits::Defensive) trait, only with saturating, mathematical +//! operations: +#![doc = docify::embed!( + "./src/reference_docs/defensive_programming.rs", + saturated_defensive_example +)] +//! +//! ### Mathematical Operations in Substrate Development - Further Context +//! +//! As a recap, we covered the following concepts: +//! +//! 1. **Checked** operations - using [`Option`] or [`Result`] +//! 2. **Saturating** operations - limited to the lower and upper bounds of a number type +//! 3. **Wrapped** operations (the default) - wrap around to above or below the bounds of a type +//! +//! #### The problem with 'default' wrapped operations +//! +//! **Wrapped operations** cause the overflow to wrap around to either the maximum or minimum of +//! that type. Imagine this in the context of a blockchain, where there are account balances, voting +//! counters, nonces for transactions, and other aspects of a blockchain. +//! +//! While it may seem trivial, choosing how to handle numbers is quite important. As a thought +//! exercise, here are some scenarios of which will shed more light on when to use which. +//! +//! #### Bob's Overflowed Balance +//! +//! **Bob's** balance exceeds the `Balance` type on the `EduChain`. Because the pallet developer did +//! not handle the calculation to add to Bob's balance with any regard to this overflow, **Bob's** +//! balance is now essentially `0`, the operation **wrapped**. +//! +//!
+//! Solution: Saturating or Checked +//! For Bob's balance problems, using a `saturating_add` or `checked_add` could've mitigated +//! this issue. They simply would've reached the upper, or lower bounds, of the particular type for +//! an on-chain balance. In other words: Bob's balance would've stayed at the maximum of the +//! Balance type.
+//! +//! #### Alice's 'Underflowed' Balance +//! +//! Alice’s balance has reached `0` after a transfer to Bob. Suddenly, she has been slashed on +//! EduChain, causing her balance to reach near the limit of `u32::MAX` - a very large amount - as +//! wrapped operations can go both ways. Alice can now successfully vote using her new, overpowered +//! token balance, destroying the chain's integrity. +//! +//!
+//! Solution: Saturating +//! For Alice's balance problem, using `saturated_sub` could've mitigated this issue. A saturating +//! calculation would've simply limited her balance to the lower bound of u32, as having a negative +//! balance is not a concept within blockchains. In other words: Alice's balance would've stayed +//! at "0", even after being slashed. +//! +//! This is also an example that while one system may work in isolation, shared interfaces, such +//! as the notion of balances, are often shared across multiple pallets - meaning these small +//! changes can make a big difference depending on the scenario.
+//! +//! #### Proposal ID Overwrite +//! +//! A `u8` parameter, called `proposals_count`, represents the type for counting the number of +//! proposals on-chain. Every time a new proposal is added to the system, this number increases. +//! With the proposal pallet's high usage, it has reached `u8::MAX`’s limit of 255, causing +//! `proposals_count` to go to 0. Unfortunately, this results in new proposals overwriting old ones, +//! effectively erasing any notion of past proposals! +//! +//!
+//! Solution: Checked +//! For the proposal IDs, proper handling via `checked` math would've been suitable, +//! Saturating could've been used - but it also would've 'failed' silently. Using `checked_add` to +//! ensure that the next proposal ID would've been valid would've been a viable way to let the user +//! know the state of their proposal: +//! +//! ```ignore +//! let next_proposal_id = current_count.checked_add(1).ok_or_else(|| Error::TooManyProposals)?; +//! ``` +//! +//!
+//! +//! From the above, we can clearly see the problematic nature of seemingly simple operations in the +//! runtime, and care should be given to ensure a defensive approach is taken. +//! +//! ### Edge cases of `panic!`-able instances in Substrate +//! +//! As you traverse through the codebase (particularly in `substrate/frame`, where the majority of +//! runtime code lives), you may notice that there (only a few!) occurrences where `panic!` is used +//! explicitly. This is used when the runtime should stall, rather than keep running, as that is +//! considered safer. Particularly when it comes to mission-critical components, such as block +//! authoring, consensus, or other protocol-level dependencies, going through with an action may +//! actually cause harm to the network, and thus stalling would be the better option. +//! +//! Take the example of the BABE pallet ([`pallet_babe`]), which doesn't allow for a validator to +//! participate if it is disabled (see: [`frame::traits::DisabledValidators`]): +//! +//! ```ignore +//! if T::DisabledValidators::is_disabled(authority_index) { +//! panic!( +//! "Validator with index {:?} is disabled and should not be attempting to author blocks.", +//! authority_index, +//! ); +//! } +//! ``` +//! +//! There are other examples in various pallets, mostly those crucial to the blockchain’s +//! functionality. Most of the time, you will not be writing pallets which operate at this level, +//! but these exceptions should be noted regardless. +//! +//! ## Other Resources +//! +//! - [PBA Book - FRAME Tips & Tricks](https://polkadot-blockchain-academy.github.io/pba-book/substrate/tips-tricks/page.html?highlight=perthing#substrate-and-frame-tips-and-tricks) +#![allow(dead_code)] +#[allow(unused_variables)] +mod fake_runtime_types { + // Note: The following types are purely for the purpose of example, and do not contain any + // *real* use case other than demonstrating various concepts. + pub enum RuntimeError { + Overflow, + UserDoesntExist, + } + + pub type Address = (); + + pub struct Runtime; + + impl Runtime { + fn get_balance(account: Address) -> Result { + Ok(0u64) + } + + fn set_balance(account: Address, new_balance: u64) {} + } + + #[docify::export] + fn increase_balance(account: Address, amount: u64) -> Result<(), RuntimeError> { + // Get a user's current balance + let balance = Runtime::get_balance(account)?; + // SAFELY increase the balance by some amount + if let Some(new_balance) = balance.checked_add(amount) { + Runtime::set_balance(account, new_balance); + Ok(()) + } else { + Err(RuntimeError::Overflow) + } + } + + #[docify::export] + fn increase_balance_match(account: Address, amount: u64) -> Result<(), RuntimeError> { + // Get a user's current balance + let balance = Runtime::get_balance(account)?; + // SAFELY increase the balance by some amount + let new_balance = match balance.checked_add(amount) { + Some(balance) => balance, + None => { + return Err(RuntimeError::Overflow); + }, + }; + Runtime::set_balance(account, new_balance); + Ok(()) + } + + #[docify::export] + fn increase_balance_result(account: Address, amount: u64) -> Result<(), RuntimeError> { + // Get a user's current balance + let balance = Runtime::get_balance(account)?; + // SAFELY increase the balance by some amount - this time, by using `ok_or` + let new_balance = balance.checked_add(amount).ok_or(RuntimeError::Overflow)?; + Runtime::set_balance(account, new_balance); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use frame::traits::DefensiveSaturating; + #[docify::export] + #[test] + fn checked_add_example() { + // This is valid, as 20 is perfectly within the bounds of u32. + let add = (10u32).checked_add(10); + assert_eq!(add, Some(20)) + } + + #[docify::export] + #[test] + fn checked_add_handle_error_example() { + // This is invalid - we are adding something to the max of u32::MAX, which would overflow. + // Luckily, checked_add just marks this as None! + let add = u32::MAX.checked_add(10); + assert_eq!(add, None) + } + + #[docify::export] + #[test] + fn saturated_add_example() { + // Saturating add simply saturates + // to the numeric bound of that type if it overflows. + let add = u32::MAX.saturating_add(10); + assert_eq!(add, u32::MAX) + } + + #[docify::export] + #[test] + #[cfg_attr(debug_assertions, should_panic(expected = "Defensive failure has been triggered!"))] + fn saturated_defensive_example() { + let saturated_defensive = u32::MAX.defensive_saturating_add(10); + assert_eq!(saturated_defensive, u32::MAX); + } +} diff --git a/docs/sdk/src/reference_docs/development_environment_advice.rs b/docs/sdk/src/reference_docs/development_environment_advice.rs index 4317695979367b32bfb673d2d368399c5ca35faa..21bbe78836c44b8afd70cab68e4b9b2f929fb4a0 100644 --- a/docs/sdk/src/reference_docs/development_environment_advice.rs +++ b/docs/sdk/src/reference_docs/development_environment_advice.rs @@ -111,3 +111,74 @@ //! If you have a powerful remote server available, you may consider using //! [cargo-remote](https://github.com/sgeisler/cargo-remote) to execute cargo commands on it, //! freeing up local resources for other tasks like `rust-analyzer`. +//! +//! When using `cargo-remote`, you can configure your editor to perform the the typical +//! "check-on-save" remotely as well. The configuration for VSCode is as follows: +//! +//! ```json +//! { +//! "rust-analyzer.cargo.buildScripts.overrideCommand": [ +//! "cargo", +//! "remote", +//! "--build-env", +//! "SKIP_WASM_BUILD=1", +//! "--", +//! "check", +//! "--message-format=json", +//! "--all-targets", +//! "--all-features", +//! "--target-dir=target/rust-analyzer" +//! ], +//! "rust-analyzer.check.overrideCommand": [ +//! "cargo", +//! "remote", +//! "--build-env", +//! "SKIP_WASM_BUILD=1", +//! "--", +//! "check", +//! "--workspace", +//! "--message-format=json", +//! "--all-targets", +//! "--all-features", +//! "--target-dir=target/rust-analyzer" +//! ], +//! } +//! ``` +//! +//! //! and the same in Lua for `neovim/nvim-lspconfig`: +//! +//! ```lua +//! ["rust-analyzer"] = { +//! cargo = { +//! buildScripts = { +//! overrideCommand = { +//! "cargo", +//! "remote", +//! "--build-env", +//! "SKIP_WASM_BUILD=1", +//! "--", +//! "check", +//! "--message-format=json", +//! "--all-targets", +//! "--all-features", +//! "--target-dir=target/rust-analyzer" +//! }, +//! }, +//! check = { +//! overrideCommand = { +//! "cargo", +//! "remote", +//! "--build-env", +//! "SKIP_WASM_BUILD=1", +//! "--", +//! "check", +//! "--workspace", +//! "--message-format=json", +//! "--all-targets", +//! "--all-features", +//! "--target-dir=target/rust-analyzer" +//! }, +//! }, +//! }, +//! }, +//! ``` diff --git a/docs/sdk/src/reference_docs/extrinsic_encoding.rs b/docs/sdk/src/reference_docs/extrinsic_encoding.rs index 9008f8f835f5d6cd1705c9dd4e4e4e78fd3aa2d9..8c8568a228fad140115e68feb3eec717ef30170c 100644 --- a/docs/sdk/src/reference_docs/extrinsic_encoding.rs +++ b/docs/sdk/src/reference_docs/extrinsic_encoding.rs @@ -127,7 +127,7 @@ //! runtimes, a call is represented as an enum of enums, where the outer enum represents the FRAME //! pallet being called, and the inner enum represents the call being made within that pallet, and //! any arguments to it. Read more about the call enum -//! [here][crate::reference_docs::frame_composite_enums]. +//! [here][crate::reference_docs::frame_runtime_types]. //! //! FRAME `Call` enums are automatically generated, and end up looking something like this: #![doc = docify::embed!("./src/reference_docs/extrinsic_encoding.rs", call_data)] diff --git a/docs/sdk/src/reference_docs/frame_composite_enums.rs b/docs/sdk/src/reference_docs/frame_composite_enums.rs deleted file mode 100644 index 6051cd534467672b9831187ef5c8b814712f7d18..0000000000000000000000000000000000000000 --- a/docs/sdk/src/reference_docs/frame_composite_enums.rs +++ /dev/null @@ -1 +0,0 @@ -//! # FRAME Composite Enums diff --git a/docs/sdk/src/reference_docs/frame_offchain_workers.rs b/docs/sdk/src/reference_docs/frame_offchain_workers.rs new file mode 100644 index 0000000000000000000000000000000000000000..7999707e5ee018c4bb7634e7a506ff8fee8fa8ac --- /dev/null +++ b/docs/sdk/src/reference_docs/frame_offchain_workers.rs @@ -0,0 +1,115 @@ +//! # Offchain Workers +//! +//! This reference document explains how offchain workers work in Substrate and FRAME. The main +//! focus is upon FRAME's implementation of this functionality. Nonetheless, offchain workers are a +//! Substrate-provided feature and can be used with possible alternatives to [`frame`] as well. +//! +//! Offchain workers are a commonly misunderstood topic, therefore we explain them bottom-up, +//! starting at the fundamentals and then describing the developer interface. +//! +//! ## Context +//! +//! Recall from [`crate::reference_docs::wasm_meta_protocol`] that the node and the runtime +//! communicate with one another via host functions and runtime APIs. Many of these interactions +//! contribute to the actual state transition of the blockchain. For example [`sp_api::Core`] is the +//! main runtime API that is called to execute new blocks. +//! +//! Offchain workers are in principle not different in any way: It is a runtime API exposed by the +//! wasm blob ([`sp_offchain::OffchainWorkerApi`]), and the node software calls into it when it +//! deems fit. But, crucially, this API call is different in that: +//! +//! 1. It can have no impact on the state ie. it is _OFF (the) CHAIN_. If any state is altered +//! during the execution of this API call, it is discarded. +//! 2. It has access to an extended set of host functions that allow the wasm blob to do more. For +//! example, call into HTTP requests. +//! +//! > The main way through which an offchain worker can interact with the state is by submitting an +//! > extrinsic to the chain. This is the ONLY way to alter the state from an offchain worker. +//! > [`pallet_example_offchain_worker`] provides an example of this. +//! +//! +//! Given the "Off Chain" nature of this API, it is important to remember that calling this API is +//! entirely optional. Some nodes might call into it, some might not, and it would have no impact on +//! the execution of your blockchain because no state is altered no matter the execution of the +//! offchain worker API. +//! +//! Substrate's CLI allows some degree of configuration about this, allowing node operators to +//! specify when they want to run the offchain worker API. See +//! [`sc_cli::RunCmd::offchain_worker_params`]. +//! +//! ## Nondeterministic Execution +//! +//! Needless to say, given the above description, the code in your offchain worker API can be +//! nondeterministic, as it is not part of the blockchain's STF, so it can be executed at unknown +//! times, by unknown nodes, and has no impact on the state. This is why an HTTP +//! ([`sp_runtime::offchain::http`]) API is readily provided to the offchain worker APIs. Because +//! there is no need for determinism in this context. +//! +//! > A common mistake here is for novice developers to see this HTTP API, and imagine that +//! > `polkadot-sdk` somehow magically solved the determinism in blockchains, and now a blockchain +//! > can make HTTP calls and it will all work. This is absolutely NOT the case. An HTTP call made +//! > by the offchain worker is non-deterministic by design. Blockchains can't and always won't be +//! > able to perform non-deterministic operations such as making HTTP calls to a foreign server. +//! +//! ## FRAME's API +//! +//! [`frame`] provides a simple API through which pallets can define offchain worker functions. This +//! is part of [`frame::traits::Hooks`], which is implemented as a part of +//! [`frame::pallet_macros::hooks`]. +//! +//! ``` +//! +//! #[frame::pallet] +//! pub mod pallet { +//! use frame::prelude::*; +//! +//! #[pallet::config] +//! pub trait Config: frame_system::Config {} +//! +//! #[pallet::pallet] +//! pub struct Pallet(_); +//! +//! #[pallet::hooks] +//! impl Hooks> for Pallet { +//! fn offchain_worker(block_number: BlockNumberFor) { +//! // ... +//! } +//! } +//! } +//! ``` +//! +//! Additionally, [`sp_runtime::offchain`] provides a set of utilities that can be used to moderate +//! the execution of offchain workers. +//! +//! ## Think Twice: Why Use Substrate's Offchain Workers? +//! +//! Consider the fact that in principle, an offchain worker code written using the above API is no +//! different than an equivalent written with an _actual offchain interaction library_, such as +//! [Polkadot-JS](https://polkadot.js.org/docs/), or any of the other ones listed [here](https://github.com/substrate-developer-hub/awesome-substrate?tab=readme-ov-file#client-libraries). +//! +//! They can both read from the state, and have no means of updating the state, other than the route +//! of submitting an extrinsic to the chain. Therefore, it is worth thinking twice before embedding +//! a logic as a part of Substrate's offchain worker API. Does it have to be there? can it not be a +//! simple, actual offchain application that lives outside of the chain's WASM blob? +//! +//! Some of the reasons why you might want to do the opposite, and actually embed an offchain worker +//! API into the WASM blob are: +//! +//! * Accessing the state is easier within the `offchain_worker` function, as it is already a part +//! of the runtime, and [`frame::pallet_macros::storage`] provides all the tools needed to read +//! the state. Other client libraries might provide varying degrees of capability here. +//! * It will be updated in synchrony with the runtime. A Substrate's offchain application is part +//! of the same WASM blob, and is therefore guaranteed to be up to date. +//! +//! For example, imagine you have modified a storage item to have a new type. This will possibly +//! require a [`crate::reference_docs::frame_runtime_upgrades_and_migrations`], and any offchain +//! code, such as a Polkadot-JS application, will have to be updated to reflect this change. Whereas +//! the WASM offchain worker code is guaranteed to already be updated, or else the runtime code will +//! not even compile. +//! +//! +//! ## Further References +//! +//! - +//! - +//! - [Offchain worker example](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame/examples/offchain-worker) diff --git a/docs/sdk/src/reference_docs/frame_origin.rs b/docs/sdk/src/reference_docs/frame_origin.rs index a4078377cd77dad6b3aac78ba6acdddda14251a8..a2aac7dd3554fb614658f71a2bf88573379bbf9d 100644 --- a/docs/sdk/src/reference_docs/frame_origin.rs +++ b/docs/sdk/src/reference_docs/frame_origin.rs @@ -1,14 +1,260 @@ //! # FRAME Origin //! -//! Notes: -//! -//! - Def talk about account abstraction and how it is a solved issue in frame. See Gav's talk in -//! Protocol Berg 2023 -//! - system's raw origin, how it is amalgamated with other origins into one type -//! [`frame_composite_enums`] -//! - signed origin -//! - unsigned origin, link to [`fee_less_runtime`] -//! - Root origin, how no one can obtain it. -//! - Abstract origin: how FRAME allows you to express "origin is 2/3 of the this body or 1/2 of -//! that body or half of the token holders". -//! - `type CustomOrigin: EnsureOrigin<_>` in pallets. +//! Let's start by clarifying a common wrong assumption about Origin: +//! +//! **ORIGIN IS NOT AN ACCOUNT ID**. +//! +//! FRAME's origin abstractions allow you to convey meanings far beyond just an account-id being the +//! caller of an extrinsic. Nonetheless, an account-id having signed an extrinsic is one of the +//! meanings that an origin can convey. This is the commonly used [`frame_system::ensure_signed`], +//! where the return value happens to be an account-id. +//! +//! Instead, let's establish the following as the correct definition of an origin: +//! +//! > The origin type represents the privilege level of the caller of an extrinsic. +//! +//! That is, an extrinsic, through checking the origin, can *express what privilege level it wishes +//! to impose on the caller of the extrinsic*. One of those checks can be as simple as "*any account +//! that has signed a statement can pass*". +//! +//! But the origin system can also express more abstract and complicated privilege levels. For +//! example: +//! +//! * If the majority of token holders agreed upon this. This is more or less what the +//! [`pallet_democracy`] does under the hood ([reference](https://github.com/paritytech/polkadot-sdk/blob/edd95b3749754d2ed0c5738588e872c87be91624/substrate/frame/democracy/src/lib.rs#L1603-L1633)). +//! * If a specific ratio of an instance of [`pallet_collective`]/DAO agrees upon this. +//! * If another consensus system, for example a bridged network or a parachain, agrees upon this. +//! * If the majority of validator/authority set agrees upon this[^1]. +//! * If caller holds a particular NFT. +//! +//! and many more. +//! +//! ## Context +//! +//! First, let's look at where the `origin` type is encountered in a typical pallet. The `origin: +//! OriginFor` has to be the first argument of any given callable extrinsic in FRAME: +#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", call_simple)] +//! +//! Typically, the code of an extrinsic starts with an origin check, such as +//! [`frame_system::ensure_signed`]. +//! +//! Note that [`OriginFor`](frame_system::pallet_prelude::OriginFor) is merely a shorthand for +//! [`frame_system::Config::RuntimeOrigin`]. Given the name prefix `Runtime`, we can learn that +//! `RuntimeOrigin` is similar to `RuntimeCall` and others, a runtime composite enum that is +//! amalgamated at the runtime level. Read [`crate::reference_docs::frame_runtime_types`] to +//! familiarize yourself with these types. +//! +//! To understand this better, we will next create a pallet with a custom origin, which will add a +//! new variant to `RuntimeOrigin`. +//! +//! ## Adding Custom Pallet Origin to the Runtime +//! +//! For example, given a pallet that defines the following custom origin: +#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin)] +//! +//! And a runtime with the following pallets: +#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", runtime_exp)] +//! +//! The type [`crate::reference_docs::frame_origin::runtime_for_origin::RuntimeOrigin`] is expanded. +//! This `RuntimeOrigin` contains a variant for the [`frame_system::RawOrigin`] and the custom +//! origin of the pallet. +//! +//! > Notice how the [`frame_system::ensure_signed`] is nothing more than a `match` statement. If +//! > you want to know where the actual origin of an extrinsic is set (and the signature +//! > verification happens, if any), see +//! > [`sp_runtime::generic::CheckedExtrinsic#trait-implementations`], specifically +//! > [`sp_runtime::traits::Applyable`]'s implementation. +//! +//! ## Asserting on a Custom Internal Origin +//! +//! In order to assert on a custom origin that is defined within your pallet, we need a way to first +//! convert the `::RuntimeOrigin` into the local `enum Origin` of the +//! current pallet. This is a common process that is explained in +//! [`crate::reference_docs::frame_runtime_types# +//! adding-further-constraints-to-runtime-composite-enums`]. +//! +//! We use the same process here to express that `RuntimeOrigin` has a number of additional bounds, +//! as follows. +//! +//! 1. Defining a custom `RuntimeOrigin` with further bounds in the pallet. +#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin_bound)] +//! +//! 2. Using it in the pallet. +#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin_usage)] +//! +//! ## Asserting on a Custom External Origin +//! +//! Very often, a pallet wants to have a parameterized origin that is **NOT** defined within the +//! pallet. In other words, a pallet wants to delegate an origin check to something that is +//! specified later at the runtime level. Like many other parameterizations in FRAME, this implies +//! adding a new associated type to `trait Config`. +#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_def)] +//! +//! Then, within the pallet, we can simply use this "unknown" origin check type: +#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_usage)] +//! +//! Finally, at the runtime, any implementation of [`frame::traits::EnsureOrigin`] can be passed. +#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_provide)] +//! +//! Indeed, some of these implementations of [`frame::traits::EnsureOrigin`] are similar to the ones +//! that we know about: [`frame::runtime::prelude::EnsureSigned`], +//! [`frame::runtime::prelude::EnsureSignedBy`], [`frame::runtime::prelude::EnsureRoot`], +//! [`frame::runtime::prelude::EnsureNone`], etc. But, there are also many more that are not known +//! to us, and are defined in other pallets. +//! +//! For example, [`pallet_collective`] defines [`pallet_collective::EnsureMember`] and +//! [`pallet_collective::EnsureProportionMoreThan`] and many more, which is exactly what we alluded +//! to earlier in this document. +//! +//! Make sure to check the full list of [implementors of +//! `EnsureOrigin`](frame::traits::EnsureOrigin#implementors) for more inspiration. +//! +//! ## Obtaining Abstract Origins +//! +//! So far we have learned that FRAME pallets can assert on custom and abstract origin types, +//! whether they are defined within the pallet or not. But how can we obtain these abstract origins? +//! +//! > All extrinsics that come from the outer world can generally only be obtained as either +//! > `signed` or `none` origin. +//! +//! Generally, these abstract origins are only obtained within the runtime, when a call is +//! dispatched within the runtime. +//! +//! ## Further References +//! +//! - [Gavin Wood's speech about FRAME features at Protocol Berg 2023.](https://youtu.be/j7b8Upipmeg?si=83_XUgYuJxMwWX4g&t=195) +//! - [A related StackExchange question.](https://substrate.stackexchange.com/questions/10992/how-do-you-find-the-public-key-for-the-medium-spender-track-origin) +//! +//! [^1]: Inherents are essentially unsigned extrinsics that need an [`frame_system::ensure_none`] +//! origin check, and through the virtue of being an inherent, are agreed upon by all validators. + +use frame::prelude::*; + +#[frame::pallet(dev_mode)] +pub mod pallet_for_origin { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[docify::export(call_simple)] + #[pallet::call] + impl Pallet { + pub fn do_something(_origin: OriginFor) -> DispatchResult { + // ^^^^^^^^^^^^^^^^^^^^^ + todo!(); + } + } +} + +#[frame::pallet(dev_mode)] +pub mod pallet_with_custom_origin { + use super::*; + + #[docify::export(custom_origin_bound)] + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeOrigin: From<::RuntimeOrigin> + + Into::RuntimeOrigin>>; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[docify::export(custom_origin)] + /// A dummy custom origin. + #[pallet::origin] + #[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)] + pub enum Origin { + /// If all holders of a particular NFT have agreed upon this. + AllNftHolders, + /// If all validators have agreed upon this. + ValidatorSet, + } + + #[docify::export(custom_origin_usage)] + #[pallet::call] + impl Pallet { + pub fn only_validators(origin: OriginFor) -> DispatchResult { + // first, we convert from `::RuntimeOrigin` to `::RuntimeOrigin` + let local_runtime_origin = <::RuntimeOrigin as From< + ::RuntimeOrigin, + >>::from(origin); + // then we convert to `origin`, if possible + let local_origin = + local_runtime_origin.into().map_err(|_| "invalid origin type provided")?; + ensure!(matches!(local_origin, Origin::ValidatorSet), "Not authorized"); + todo!(); + } + } +} + +pub mod runtime_for_origin { + use super::pallet_with_custom_origin; + use frame::{runtime::prelude::*, testing_prelude::*}; + + #[docify::export(runtime_exp)] + construct_runtime!( + pub struct Runtime { + System: frame_system, + PalletWithCustomOrigin: pallet_with_custom_origin, + } + ); + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] + impl frame_system::Config for Runtime { + type Block = MockBlock; + } + + impl pallet_with_custom_origin::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + } +} + +#[frame::pallet(dev_mode)] +pub mod pallet_with_external_origin { + use super::*; + #[docify::export(external_origin_def)] + #[pallet::config] + pub trait Config: frame_system::Config { + type ExternalOrigin: EnsureOrigin; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[docify::export(external_origin_usage)] + #[pallet::call] + impl Pallet { + pub fn externally_checked_ext(origin: OriginFor) -> DispatchResult { + let _ = T::ExternalOrigin::ensure_origin(origin)?; + todo!(); + } + } +} + +pub mod runtime_for_external_origin { + use super::*; + use frame::{runtime::prelude::*, testing_prelude::*}; + + construct_runtime!( + pub struct Runtime { + System: frame_system, + PalletWithExternalOrigin: pallet_with_external_origin, + } + ); + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] + impl frame_system::Config for Runtime { + type Block = MockBlock; + } + + #[docify::export(external_origin_provide)] + impl pallet_with_external_origin::Config for Runtime { + type ExternalOrigin = EnsureSigned<::AccountId>; + } +} diff --git a/docs/sdk/src/reference_docs/frame_pallet_coupling.rs b/docs/sdk/src/reference_docs/frame_pallet_coupling.rs new file mode 100644 index 0000000000000000000000000000000000000000..cca7f9feb3f40ca9f933e9e89ed8739fa2a79b45 --- /dev/null +++ b/docs/sdk/src/reference_docs/frame_pallet_coupling.rs @@ -0,0 +1,296 @@ +//! # FRAME Pallet Coupling +//! +//! This reference document explains how FRAME pallets can be combined to interact together. +//! +//! It is suggested to re-read [`crate::polkadot_sdk::frame_runtime`], notably the information +//! around [`frame::pallet_macros::config`]. Recall that: +//! +//! > Configuration trait of a pallet: It allows a pallet to receive types at a later +//! > point from the runtime that wishes to contain it. It allows the pallet to be parameterized +//! > over both types and values. +//! +//! ## Context, Background +//! +//! FRAME pallets, as per described in [`crate::polkadot_sdk::frame_runtime`] are: +//! +//! > A pallet is a unit of encapsulated logic. It has a clearly defined responsibility and can be +//! linked to other pallets. +//! +//! That is to say: +//! +//! * *encapsulated*: Ideally, a FRAME pallet contains encapsulated logic which has clear +//! boundaries. It is generally a bad idea to build a single monolithic pallet that does multiple +//! things, such as handling currencies, identities and staking all at the same time. +//! * *linked to other pallets*: But, adhering extensively to the above also hinders the ability to +//! write useful applications. Pallets often need to work with each other, communicate and use +//! each other's functionalities. +//! +//! The broad principle that allows pallets to be linked together is the same way through which a +//! pallet uses its `Config` trait to receive types and values from the runtime that contains it. +//! +//! There are generally two ways to achieve this: +//! +//! 1. Tight coupling pallets +//! 2. Loose coupling pallets +//! +//! To explain the difference between the two, consider two pallets, `A` and `B`. In both cases, `A` +//! wants to use some functionality exposed by `B`. +//! +//! When tightly coupling pallets, `A` can only exist in a runtime if `B` is also present in the +//! same runtime. That is, `A` is expressing that can only work if `B` is present. +//! +//! This translates to the following Rust code: +//! +//! ``` +//! trait Pallet_B_Config {} +//! trait Pallet_A_Config: Pallet_B_Config {} +//! ``` +//! +//! Contrary, when pallets are loosely coupled, `A` expresses that some functionality, expressed via +//! a trait `F`, needs to be fulfilled. This trait is then implemented by `B`, and the two pallets +//! are linked together at the runtime level. This means that `A` only relies on the implementation +//! of `F`, which may be `B`, or another implementation of `F`. +//! +//! This translates to the following Rust code: +//! +//! ``` +//! trait F {} +//! trait Pallet_A_Config { +//! type F: F; +//! } +//! // Pallet_B will implement and fulfill `F`. +//! ``` +//! +//! ## Example +//! +//! Consider the following example, in which `pallet-foo` needs another pallet to provide the block +//! author to it, and `pallet-author` which has access to this information. +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", pallet_foo)] +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", pallet_author)] +//! +//! ### Tight Coupling Pallets +//! +//! To tightly couple `pallet-foo` and `pallet-author`, we use Rust's supertrait system. When a +//! pallet makes its own `trait Config` be bounded by another pallet's `trait Config`, it is +//! expressing two things: +//! +//! 1. that it can only exist in a runtime if the other pallet is also present. +//! 2. that it can use the other pallet's functionality. +//! +//! `pallet-foo`'s `Config` would then look like: +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", tight_config)] +//! +//! And `pallet-foo` can use the method exposed by `pallet_author::Pallet` directly: +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", tight_usage)] +//! +//! +//! ### Loosely Coupling Pallets +//! +//! If `pallet-foo` wants to *not* rely on `pallet-author` directly, it can leverage its +//! `Config`'s associated types. First, we need a trait to express the functionality that +//! `pallet-foo` wants to obtain: +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", AuthorProvider)] +//! +//! > We sometimes refer to such traits that help two pallets interact as "glue traits". +//! +//! Next, `pallet-foo` states that it needs this trait to be provided to it, at the runtime level, +//! via an associated type: +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", loose_config)] +//! +//! Then, `pallet-foo` can use this trait to obtain the block author, without knowing where it comes +//! from: +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", loose_usage)] +//! +//! Then, if `pallet-author` implements this glue-trait: +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", pallet_author_provider)] +//! +//! And upon the creation of the runtime, the two pallets are linked together as such: +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", runtime_author_provider)] +//! +//! Crucially, when using loose coupling, we gain the flexibility of providing different +//! implementations of `AuthorProvider`, such that different users of a `pallet-foo` can use +//! different ones, without any code change being needed. For example, in the code snippets of this +//! module, you can fund [`OtherAuthorProvider`] which is an alternative implementation of +//! [`AuthorProvider`]. +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", other_author_provider)] +//! +//! A common pattern in polkadot-sdk is to provide an implementation of such glu traits for the unit +//! type as a "default/test behavior". +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", unit_author_provider)] +//! +//! ## Frame System +//! +//! With the above information in context, we can conclude that **`frame_system` is a special pallet +//! that is tightly coupled with every other pallet**. This is because it provides the fundamental +//! system functionality that every pallet needs, such as some types like +//! [`frame::prelude::frame_system::Config::AccountId`], +//! [`frame::prelude::frame_system::Config::Hash`], and some functionality such as block number, +//! etc. +//! +//! ## Recap +//! +//! To recap, consider the following rules of thumb: +//! +//! * In all cases, try and break down big pallets apart with clear boundaries of responsibility. In +//! general, it is easier to argue about multiple pallet if they only communicate together via a +//! known trait, rather than having access to all of each others public items, such as storage and +//! dispatchables. +//! * If a group of pallets are meant to work together, and but are not foreseen to be generalized, +//! or used by others, consider tightly coupling pallets, *if it simplifies the development*. +//! * If a pallet needs a functionality provided by another pallet, but multiple implementations can +//! be foreseen, consider loosely coupling pallets. +//! +//! For example, all pallets in `polkadot-sdk` that needed to work with currencies could have been +//! tightly coupled with [`pallet_balances`]. But, `polkadot-sdk` also provides [`pallet_assets`] +//! (and more implementations by the community), therefore all pallets use traits to loosely couple +//! with balances or assets pallet. More on this in [`crate::reference_docs::frame_currency`]. +//! +//! ## Further References +//! +//! - +//! - +//! +//! [`AuthorProvider`]: crate::reference_docs::frame_pallet_coupling::AuthorProvider +//! [`OtherAuthorProvider`]: crate::reference_docs::frame_pallet_coupling::OtherAuthorProvider + +#![allow(unused)] + +use frame::prelude::*; + +#[docify::export] +#[frame::pallet] +pub mod pallet_foo { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + impl Pallet { + fn do_stuff_with_author() { + // needs block author here + } + } +} + +#[docify::export] +#[frame::pallet] +pub mod pallet_author { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + impl Pallet { + pub fn author() -> T::AccountId { + todo!("somehow has access to the block author and can return it here") + } + } +} + +#[frame::pallet] +pub mod pallet_foo_tight { + use super::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[docify::export(tight_config)] + /// This pallet can only live in a runtime that has both `frame_system` and `pallet_author`. + #[pallet::config] + pub trait Config: frame_system::Config + pallet_author::Config {} + + #[docify::export(tight_usage)] + impl Pallet { + // anywhere in `pallet-foo`, we can call into `pallet-author` directly, namely because + // `T: pallet_author::Config` + fn do_stuff_with_author() { + let _ = pallet_author::Pallet::::author(); + } + } +} + +#[docify::export] +/// Abstraction over "something that can provide the block author". +pub trait AuthorProvider { + fn author() -> AccountId; +} + +#[frame::pallet] +pub mod pallet_foo_loose { + use super::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[docify::export(loose_config)] + #[pallet::config] + pub trait Config: frame_system::Config { + /// This pallet relies on the existence of something that implements [`AuthorProvider`], + /// which may or may not be `pallet-author`. + type AuthorProvider: AuthorProvider; + } + + #[docify::export(loose_usage)] + impl Pallet { + fn do_stuff_with_author() { + let _ = T::AuthorProvider::author(); + } + } +} + +#[docify::export(pallet_author_provider)] +impl AuthorProvider for pallet_author::Pallet { + fn author() -> T::AccountId { + pallet_author::Pallet::::author() + } +} + +pub struct OtherAuthorProvider; + +#[docify::export(other_author_provider)] +impl AuthorProvider for OtherAuthorProvider { + fn author() -> AccountId { + todo!("somehow get the block author here") + } +} + +#[docify::export(unit_author_provider)] +impl AuthorProvider for () { + fn author() -> AccountId { + todo!("somehow get the block author here") + } +} + +pub mod runtime { + use super::*; + use cumulus_pallet_aura_ext::pallet; + use frame::{runtime::prelude::*, testing_prelude::*}; + + construct_runtime!( + pub struct Runtime { + System: frame_system, + PalletFoo: pallet_foo_loose, + PalletAuthor: pallet_author, + } + ); + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] + impl frame_system::Config for Runtime { + type Block = MockBlock; + } + + impl pallet_author::Config for Runtime {} + + #[docify::export(runtime_author_provider)] + impl pallet_foo_loose::Config for Runtime { + type AuthorProvider = pallet_author::Pallet; + // which is also equivalent to + // type AuthorProvider = PalletAuthor; + } +} diff --git a/docs/sdk/src/reference_docs/frame_runtime_migration.rs b/docs/sdk/src/reference_docs/frame_runtime_migration.rs deleted file mode 100644 index 0616ccbb6f57971823c7347a60574ef0c0bef2ab..0000000000000000000000000000000000000000 --- a/docs/sdk/src/reference_docs/frame_runtime_migration.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! # Runtime Runtime Upgrade and Testing -//! -//! -//! Notes: -//! -//! - Flow of things, when does `on_runtime_upgrade` get called. Link to to `Hooks` and its diagram -//! as source of truth. -//! - Data migration and when it is needed. -//! - Look into the pba-lecture. diff --git a/docs/sdk/src/reference_docs/frame_runtime_types.rs b/docs/sdk/src/reference_docs/frame_runtime_types.rs new file mode 100644 index 0000000000000000000000000000000000000000..32cda5bc5345d7eee43efa9e2b99ac831c8fb3a8 --- /dev/null +++ b/docs/sdk/src/reference_docs/frame_runtime_types.rs @@ -0,0 +1,306 @@ +//! # FRAME Runtime Types +//! +//! This reference document briefly explores the idea around types generated at the runtime level by +//! the FRAME macros. +//! +//! > As of now, many of these important types are generated within the internals of +//! > [`construct_runtime`], and there is no easy way for you to visually know they exist. +//! > [#polkadot-sdk#1378](https://github.com/paritytech/polkadot-sdk/pull/1378) is meant to +//! > significantly improve this. Exploring the rust-docs of a runtime, such as [`runtime`] which is +//! > defined in this module is as of now the best way to learn about these types. +//! +//! ## Composite Enums +//! +//! Many types within a FRAME runtime follow the following structure: +//! +//! * Each individual pallet defines a type, for example `Foo`. +//! * At the runtime level, these types are amalgamated into a single type, for example +//! `RuntimeFoo`. +//! +//! As the names suggest, all composite enums in a FRAME runtime start their name with `Runtime`. +//! For example, `RuntimeCall` is a representation of the most high level `Call`-able type in the +//! runtime. +//! +//! Composite enums are generally convertible to their individual parts as such: +#![doc = simple_mermaid::mermaid!("../../../mermaid/outer_runtime_types.mmd")] +//! +//! In that one can always convert from the inner type into the outer type, but not vice versa. This +//! is usually expressed by implementing `From`, `TryFrom`, `From>` and similar traits. +//! +//! ### Example +//! +//! We provide the following two pallets: [`pallet_foo`] and [`pallet_bar`]. Each define a +//! dispatchable, and `Foo` also defines a custom origin. Lastly, `Bar` defines an additional +//! `GenesisConfig`. +#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_foo)] +#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_bar)] +//! +//! Let's explore how each of these affect the [`RuntimeCall`], [`RuntimeOrigin`] and +//! [`RuntimeGenesisConfig`] generated in [`runtime`] by respectively. +//! +//! As observed, [`RuntimeCall`] has 3 variants, one for each pallet and one for `frame_system`. If +//! you explore further, you will soon realize that each variant is merely a pointer to the `Call` +//! type in each pallet, for example [`pallet_foo::Call`]. +//! +//! [`RuntimeOrigin`]'s [`OriginCaller`] has two variants, one for system, and one for `pallet_foo` +//! which utilized [`frame::pallet_macros::origin`]. +//! +//! Finally, [`RuntimeGenesisConfig`] is composed of `frame_system` and a variant for `pallet_bar`'s +//! [`pallet_bar::GenesisConfig`]. +//! +//! You can find other composite enums by scanning [`runtime`] for other types who's name starts +//! with `Runtime`. Some of the more noteworthy ones are: +//! +//! - [`RuntimeEvent`] +//! - [`RuntimeError`] +//! - [`RuntimeHoldReason`] +//! +//! ### Adding Further Constraints to Runtime Composite Enums +//! +//! This section explores a common scenario where a pallet has access to one of these runtime +//! composite enums, but it wishes to further specify it by adding more trait bounds to it. +//! +//! Let's take the example of `RuntimeCall`. This is an associated type in +//! [`frame_system::Config::RuntimeCall`], and all pallets have access to this type, because they +//! have access to [`frame_system::Config`]. Finally, this type is meant to be set to outer call of +//! the entire runtime. +//! +//! But, let's not forget that this is information that *we know*, and the Rust compiler does not. +//! All that the rust compiler knows about this type is *ONLY* what the trait bounds of +//! [`frame_system::Config::RuntimeCall`] are specifying: +#![doc = docify::embed!("../../substrate/frame/system/src/lib.rs", system_runtime_call)] +//! +//! So, when at a given pallet, one accesses `::RuntimeCall`, the type is +//! extremely opaque from the perspective of the Rust compiler. +//! +//! How can a pallet access the `RuntimeCall` type with further constraints? For example, each +//! pallet has its own `enum Call`, and knows that its local `Call` is a part of `RuntimeCall`, +//! therefore there should be a `impl From> for RuntimeCall`. +//! +//! The only way to express this using Rust's associated types is for the pallet to **define its own +//! associated type `RuntimeCall`, and further specify what it thinks `RuntimeCall` should be**. +//! +//! In this case, we will want to assert the existence of [`frame::traits::IsSubType`], which is +//! very similar to [`TryFrom`]. +#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", custom_runtime_call)] +//! +//! And indeed, at the runtime level, this associated type would be the same `RuntimeCall` that is +//! passed to `frame_system`. +#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_with_specific_runtime_call_impl)] +//! +//! > In other words, the degree of specificity that [`frame_system::Config::RuntimeCall`] has is +//! > not enough for the pallet to work with. Therefore, the pallet has to define its own associated +//! > type representing `RuntimeCall`. +//! +//! Another way to look at this is: +//! +//! `pallet_with_specific_runtime_call::Config::RuntimeCall` and `frame_system::Config::RuntimeCall` +//! are two different representations of the same concrete type that is only known when the runtime +//! is being constructed. +//! +//! Now, within this pallet, this new `RuntimeCall` can be used, and it can use its new trait +//! bounds, such as being [`frame::traits::IsSubType`]: +#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", custom_runtime_call_usages)] +//! +//! ### Asserting Equality of Multiple Runtime Composite Enums +//! +//! Recall that in the above example, `::RuntimeCall` and `::RuntimeCall` are expected to be equal types, but at the compile-time we +//! have to represent them with two different associated types with different bounds. Would it not +//! be cool if we had a test to make sure they actually resolve to the same concrete type once the +//! runtime is constructed? The following snippet exactly does that: +#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", assert_equality)] +//! +//! We leave it to the reader to further explore what [`frame::traits::Hooks::integrity_test`] is, +//! and what [`core::any::TypeId`] is. Another way to assert this is using +//! [`frame::traits::IsType`]. +//! +//! ## Type Aliases +//! +//! A number of type aliases are generated by the `construct_runtime` which are also noteworthy: +//! +//! * [`runtime::PalletFoo`] is an alias to [`pallet_foo::Pallet`]. Same for `PalletBar`, and +//! `System` +//! * [`runtime::AllPalletsWithSystem`] is an alias for a tuple of all of the above. This type is +//! important to FRAME internals such as `executive`, as it implements traits such as +//! [`frame::traits::Hooks`]. +//! +//! ## Further Details +//! +//! * [`crate::reference_docs::frame_origin`] explores further details about the usage of +//! `RuntimeOrigin`. +//! * [`RuntimeCall`] is a particularly interesting composite enum as it dictates the encoding of an +//! extrinsic. See [`crate::reference_docs::signed_extensions`] for more information. +//! * See the documentation of [`construct_runtime`]. +//! * See the corresponding lecture in the [pba-book](https://polkadot-blockchain-academy.github.io/pba-book/frame/outer-enum/page.html). +//! +//! +//! [`construct_runtime`]: frame::runtime::prelude::construct_runtime +//! [`runtime::PalletFoo`]: crate::reference_docs::frame_runtime_types::runtime::PalletFoo +//! [`runtime::AllPalletsWithSystem`]: crate::reference_docs::frame_runtime_types::runtime::AllPalletsWithSystem +//! [`runtime`]: crate::reference_docs::frame_runtime_types::runtime +//! [`pallet_foo`]: crate::reference_docs::frame_runtime_types::pallet_foo +//! [`pallet_foo::Call`]: crate::reference_docs::frame_runtime_types::pallet_foo::Call +//! [`pallet_foo::Pallet`]: crate::reference_docs::frame_runtime_types::pallet_foo::Pallet +//! [`pallet_bar`]: crate::reference_docs::frame_runtime_types::pallet_bar +//! [`pallet_bar::GenesisConfig`]: crate::reference_docs::frame_runtime_types::pallet_bar::GenesisConfig +//! [`RuntimeEvent`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeEvent +//! [`RuntimeGenesisConfig`]: +//! crate::reference_docs::frame_runtime_types::runtime::RuntimeGenesisConfig +//! [`RuntimeOrigin`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeOrigin +//! [`OriginCaller`]: crate::reference_docs::frame_runtime_types::runtime::OriginCaller +//! [`RuntimeError`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeError +//! [`RuntimeCall`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeCall +//! [`RuntimeHoldReason`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeHoldReason + +use frame::prelude::*; + +#[docify::export] +#[frame::pallet(dev_mode)] +pub mod pallet_foo { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::origin] + #[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)] + pub enum Origin { + A, + B, + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::call] + impl Pallet { + pub fn foo(_origin: OriginFor) -> DispatchResult { + todo!(); + } + + pub fn other(_origin: OriginFor) -> DispatchResult { + todo!(); + } + } +} + +#[docify::export] +#[frame::pallet(dev_mode)] +pub mod pallet_bar { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::genesis_config] + #[derive(DefaultNoBound)] + pub struct GenesisConfig { + pub initial_account: Option, + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) {} + } + + #[pallet::call] + impl Pallet { + pub fn bar(_origin: OriginFor) -> DispatchResult { + todo!(); + } + } +} + +pub mod runtime { + use super::{pallet_bar, pallet_foo}; + use frame::{runtime::prelude::*, testing_prelude::*}; + + #[docify::export(runtime_exp)] + construct_runtime!( + pub struct Runtime { + System: frame_system, + PalletFoo: pallet_foo, + PalletBar: pallet_bar, + } + ); + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] + impl frame_system::Config for Runtime { + type Block = MockBlock; + } + + impl pallet_foo::Config for Runtime {} + impl pallet_bar::Config for Runtime {} +} + +#[frame::pallet(dev_mode)] +pub mod pallet_with_specific_runtime_call { + use super::*; + use frame::traits::IsSubType; + + #[docify::export(custom_runtime_call)] + /// A pallet that wants to further narrow down what `RuntimeCall` is. + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeCall: IsSubType>; + } + + #[pallet::pallet] + pub struct Pallet(_); + + // note that this pallet needs some `call` to have a `enum Call`. + #[pallet::call] + impl Pallet { + pub fn foo(_origin: OriginFor) -> DispatchResult { + todo!(); + } + } + + #[docify::export(custom_runtime_call_usages)] + impl Pallet { + fn _do_something_useful_with_runtime_call(call: ::RuntimeCall) { + // check if the runtime call given is of this pallet's variant. + let _maybe_my_call: Option<&Call> = call.is_sub_type(); + todo!(); + } + } + + #[docify::export(assert_equality)] + #[pallet::hooks] + impl Hooks> for Pallet { + fn integrity_test() { + use core::any::TypeId; + assert_eq!( + TypeId::of::<::RuntimeCall>(), + TypeId::of::<::RuntimeCall>() + ); + } + } +} + +pub mod runtime_with_specific_runtime_call { + use super::pallet_with_specific_runtime_call; + use frame::{runtime::prelude::*, testing_prelude::*}; + + construct_runtime!( + pub struct Runtime { + System: frame_system, + PalletWithSpecificRuntimeCall: pallet_with_specific_runtime_call, + } + ); + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] + impl frame_system::Config for Runtime { + type Block = MockBlock; + } + + #[docify::export(pallet_with_specific_runtime_call_impl)] + impl pallet_with_specific_runtime_call::Config for Runtime { + // an implementation of `IsSubType` is provided by `construct_runtime`. + type RuntimeCall = RuntimeCall; + } +} diff --git a/docs/sdk/src/reference_docs/frame_runtime_upgrades_and_migrations.rs b/docs/sdk/src/reference_docs/frame_runtime_upgrades_and_migrations.rs new file mode 100644 index 0000000000000000000000000000000000000000..cbbf611f9dc4afd9866eb4786599294fdfda69a3 --- /dev/null +++ b/docs/sdk/src/reference_docs/frame_runtime_upgrades_and_migrations.rs @@ -0,0 +1,138 @@ +//! # Runtime Upgrades +//! +//! At their core, blockchain logic consists of +//! +//! 1. on-chain state and +//! 2. a state transition function +//! +//! In Substrate-based blockchains, state transition functions are referred to as +//! [runtimes](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/blockchain_state_machines/index.html). +//! +//! Traditionally, before Substrate, upgrading state transition functions required node +//! operators to download new software and restart their nodes in a process called +//! [forking](https://en.wikipedia.org/wiki/Fork_(blockchain)). +//! +//! Substrate-based blockchains do not require forking, and instead upgrade runtimes +//! in a process called "Runtime Upgrades". +//! +//! Forkless runtime upgrades are a defining feature of the Substrate framework. Updating the +//! runtime logic without forking the code base enables your blockchain to seamlessly evolve +//! over time in a deterministic, rules-based manner. It also removes ambiguity for node operators +//! and other participants in the network about what is the canonical runtime. +//! +//! This capability is possible due to the runtime of a blockchain existing in on-chain storage. +//! +//! ## Performing a Runtime Upgrade +//! +//! To upgrade a runtime, an [`Origin`](frame_system::RawOrigin) with the necessary permissions +//! (usually via governance) changes the `:code` storage. Usually, this is performed via a call to +//! [`set_code`] (or [`set_code_without_checks`]) with the desired new runtime blob, scheduled +//! using [`pallet_scheduler`]. +//! +//! Prior to building the new runtime, don't forget to update the +//! [`RuntimeVersion`](sp_version::RuntimeVersion). +//! +//! # Migrations +//! +//! It is often desirable to define logic to execute immediately after runtime upgrades (see +//! [this diagram](frame::traits::Hooks)). +//! +//! Self-contained pieces of logic that execute after a runtime upgrade are called "Migrations". +//! +//! The typical use case of a migration is to 'migrate' pallet storage from one layout to another, +//! for example when the encoding of a storage item is changed. However, they can also execute +//! arbitrary logic such as: +//! +//! - Calling arbitrary pallet methods +//! - Mutating arbitrary on-chain state +//! - Cleaning up some old storage items that are no longer needed +//! +//! ## Single Block Migrations +//! +//! - Execute immediately and entirely at the beginning of the block following +//! a runtime upgrade. +//! - Are suitable for migrations which are guaranteed to not exceed the block weight. +//! - Are simply implementations of [`OnRuntimeUpgrade`]. +//! +//! To learn best practices for writing single block pallet storage migrations, see the +//! [Single Block Migration Example Pallet](pallet_example_single_block_migrations). +//! +//! ### Scheduling the Single Block Migrations to Run Next Runtime Upgrade +//! +//! Schedule migrations to run next runtime upgrade passing them as a generic parameter to your +//! [`Executive`](frame_executive) pallet: +//! +//! ```ignore +//! /// Tuple of migrations (structs that implement `OnRuntimeUpgrade`) +//! type Migrations = ( +//! pallet_example_storage_migration::migrations::v1::versioned::MigrateV0ToV1, +//! MyCustomMigration, +//! // ...more migrations here +//! ); +//! pub type Executive = frame_executive::Executive< +//! Runtime, +//! Block, +//! frame_system::ChainContext, +//! Runtime, +//! AllPalletsWithSystem, +//! Migrations, // <-- pass your migrations to Executive here +//! >; +//! ``` +//! +//! ### Ensuring Single Block Migration Safety +//! +//! "My migration unit tests pass, so it should be safe to deploy right?" +//! +//! No! Unit tests execute the migration in a very simple test environment, and cannot account +//! for the complexities of a real runtime or real on-chain state. +//! +//! Prior to deploying migrations, it is critical to perform additional checks to ensure that when +//! run in our real runtime they will not brick the chain due to: +//! - Panicking +//! - Touching too many storage keys and resulting in an excessively large PoV +//! - Taking too long to execute +//! +//! [`try-runtime-cli`](https://github.com/paritytech/try-runtime-cli) has a sub-command +//! [`on-runtime-upgrade`](https://paritytech.github.io/try-runtime-cli/try_runtime_core/commands/enum.Action.html#variant.OnRuntimeUpgrade) +//! which is designed to help with exactly this. +//! +//! Developers MUST run this command before deploying migrations to ensure they will not +//! inadvertently result in a bricked chain. +//! +//! It is recommended to run as part of your CI pipeline. See the +//! [polkadot-sdk check-runtime-migration job](https://github.com/paritytech/polkadot-sdk/blob/4a293bc5a25be637c06ce950a34490706597615b/.gitlab/pipeline/check.yml#L103-L124) +//! for an example of how to configure this. +//! +//! ### Note on the Manipulability of PoV Size and Execution Time +//! +//! While [`try-runtime-cli`](https://github.com/paritytech/try-runtime-cli) can help ensure with +//! very high certainty that a migration will succeed given **existing** on-chain state, it cannot +//! prevent a malicious actor from manipulating state in a way that will cause the migration to take +//! longer or produce a PoV much larger than previously measured. +//! +//! Therefore, it is important to write migrations in such a way that the execution time or PoV size +//! it adds to the block cannot be easily manipulated. e.g., do not iterate over storage that can +//! quickly or cheaply be bloated. +//! +//! If writing your migration in such a way is not possible, a multi block migration should be used +//! instead. +//! +//! ### Other useful tools +//! +//! [`Chopsticks`](https://github.com/AcalaNetwork/chopsticks) is another tool in the Substrate +//! ecosystem which developers may find useful to use in addition to `try-runtime-cli` when testing +//! their single block migrations. +//! +//! ## Multi Block Migrations +//! +//! Safely and easily execute long-running migrations across multiple blocks. +//! +//! Suitable for migrations which could use arbitrary amounts of block weight. +//! +//! TODO: Link to multi block migration example/s once PR is merged (). +//! +//! [`GetStorageVersion`]: frame_support::traits::GetStorageVersion +//! [`OnRuntimeUpgrade`]: frame_support::traits::OnRuntimeUpgrade +//! [`StorageVersion`]: frame_support::traits::StorageVersion +//! [`set_code`]: frame_system::Call::set_code +//! [`set_code_without_checks`]: frame_system::Call::set_code_without_checks diff --git a/docs/sdk/src/reference_docs/mod.rs b/docs/sdk/src/reference_docs/mod.rs index c16122ee4287b17614f5d61acc9f26df0429c2cd..a0d8d05b44926c4b2ca9259d0709a8a7dee2fb76 100644 --- a/docs/sdk/src/reference_docs/mod.rs +++ b/docs/sdk/src/reference_docs/mod.rs @@ -43,16 +43,15 @@ pub mod extrinsic_encoding; // TODO: @jsdw https://github.com/paritytech/polkadot-sdk-docs/issues/42 pub mod signed_extensions; -/// Learn about *"Origin"* A topic in FRAME that enables complex account abstractions to be built. -// TODO: @shawntabrizi https://github.com/paritytech/polkadot-sdk-docs/issues/43 +/// Learn about *Origins*, a topic in FRAME that enables complex account abstractions to be built. pub mod frame_origin; /// Learn about how to write safe and defensive code in your FRAME runtime. -// TODO: @CrackTheCode016 https://github.com/paritytech/polkadot-sdk-docs/issues/44 -pub mod safe_defensive_programming; +pub mod defensive_programming; -/// Learn about composite enums in FRAME-based runtimes, such as "RuntimeEvent" and "RuntimeCall". -pub mod frame_composite_enums; +/// Learn about composite enums and other runtime level types, such as "RuntimeEvent" and +/// "RuntimeCall". +pub mod frame_runtime_types; /// Learn about how to make a pallet/runtime that is fee-less and instead uses another mechanism to /// control usage and sybil attacks. @@ -92,11 +91,18 @@ pub mod cli; // TODO: @JoshOrndorff @kianenigma https://github.com/paritytech/polkadot-sdk-docs/issues/54 pub mod consensus_swapping; -/// Learn about all the advance ways to test your coordinate a rutnime upgrade and data migration. -// TODO: @liamaharon https://github.com/paritytech/polkadot-sdk-docs/issues/55 -pub mod frame_runtime_migration; +/// Learn about Runtime Upgrades and best practices for writing Migrations. +pub mod frame_runtime_upgrades_and_migrations; /// Learn about light nodes, how they function, and how Substrate-based chains come /// light-node-first out of the box. // TODO: @jsdw @josepot https://github.com/paritytech/polkadot-sdk-docs/issues/68 pub mod light_nodes; + +/// Learn about the offchain workers, how they function, and how to use them, as provided by the +/// [`frame`] APIs. +pub mod frame_offchain_workers; + +/// Learn about the different ways through which multiple [`frame`] pallets can be combined to work +/// together. +pub mod frame_pallet_coupling; diff --git a/docs/sdk/src/reference_docs/safe_defensive_programming.rs b/docs/sdk/src/reference_docs/safe_defensive_programming.rs deleted file mode 100644 index 9d0f028e570d42d223e656cb7f46798d956fd173..0000000000000000000000000000000000000000 --- a/docs/sdk/src/reference_docs/safe_defensive_programming.rs +++ /dev/null @@ -1 +0,0 @@ -//! diff --git a/polkadot/Cargo.toml b/polkadot/Cargo.toml index b0d71a18eaa13f8e7d188ca6420effa9dde4bb6b..883568b23f74dc3417fec5e29093a7e9358dd915 100644 --- a/polkadot/Cargo.toml +++ b/polkadot/Cargo.toml @@ -46,7 +46,7 @@ assert_cmd = "2.0.4" nix = { version = "0.26.1", features = ["signal"] } tempfile = "3.2.0" tokio = "1.24.2" -substrate-rpc-client = { path = "../substrate/utils/frame/rpc/client/" } +substrate-rpc-client = { path = "../substrate/utils/frame/rpc/client" } polkadot-core-primitives = { path = "core-primitives" } [build-dependencies] diff --git a/polkadot/cli/Cargo.toml b/polkadot/cli/Cargo.toml index b9232f95981bf66427ffc67cc98282efb8cf330b..f57efa7ba4361ecdcfa40eb1d63dabb4a0343767 100644 --- a/polkadot/cli/Cargo.toml +++ b/polkadot/cli/Cargo.toml @@ -19,7 +19,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] cfg-if = "1.0" -clap = { version = "4.5.1", features = ["derive"], optional = true } +clap = { version = "4.5.3", features = ["derive"], optional = true } log = { workspace = true, default-features = true } thiserror = { workspace = true } futures = "0.3.21" diff --git a/polkadot/cli/src/cli.rs b/polkadot/cli/src/cli.rs index 30f35ebcb6ffa95f0f2384821e168353d95df94b..74e190444693183de13c1961e859cf61ab04156b 100644 --- a/polkadot/cli/src/cli.rs +++ b/polkadot/cli/src/cli.rs @@ -52,7 +52,7 @@ pub enum Subcommand { /// Try-runtime has migrated to a standalone CLI /// (). The subcommand exists as a stub and - /// deprecation notice. It will be removed entirely some time after Janurary 2024. + /// deprecation notice. It will be removed entirely some time after January 2024. TryRuntime, /// Key management CLI utilities @@ -122,7 +122,7 @@ pub struct RunCmd { /// Overseer message capacity override. /// - /// **Dangerous!** Do not touch unless explicitly adviced to. + /// **Dangerous!** Do not touch unless explicitly advised to. #[arg(long)] pub overseer_channel_capacity_override: Option, diff --git a/polkadot/doc/testing.md b/polkadot/doc/testing.md deleted file mode 100644 index 76703b1b4398a0cac8423183a5d2febfabab0e6c..0000000000000000000000000000000000000000 --- a/polkadot/doc/testing.md +++ /dev/null @@ -1,302 +0,0 @@ -# Testing - -Testing is an essential tool to assure correctness. This document describes how we test the Polkadot code, whether -locally, at scale, and/or automatically in CI. - -## Scopes - -The testing strategy for Polkadot is 4-fold: - -### Unit testing (1) - -Boring, small scale correctness tests of individual functions. It is usually -enough to run `cargo test` in the crate you are testing. - -For full coverage you may have to pass some additional features. For example: - -```sh -cargo test --features ci-only-tests -``` - -### Integration tests - -There are the following variants of integration tests: - -#### Subsystem tests (2) - -One particular subsystem (subsystem under test) interacts with a mocked overseer that is made to assert incoming and -outgoing messages of the subsystem under test. See e.g. the `statement-distribution` tests. - -#### Behavior tests (3) - -Launching small scale networks, with multiple adversarial nodes. This should include tests around the thresholds in -order to evaluate the error handling once certain assumed invariants fail. - -Currently, we commonly use **zombienet** to run mini test-networks, whether locally or in CI. To run on your machine: - -- First, make sure you have [zombienet][zombienet] installed. - -- Now, all the required binaries must be installed in your $PATH. You must run the following from the `polkadot/` -directory in order to test your changes. (Not `zombienet setup`, or you will get the released binaries without your -local changes!) - -```sh -cargo install --path . --locked -``` - -- You will also need to install whatever binaries are required for your specific tests. For example, to install -`undying-collator`, from `polkadot/`, run: - -```sh -cargo install --path ./parachain/test-parachains/undying/collator --locked -``` - -- Finally, run the zombienet test from the `polkadot` directory: - -```sh -RUST_LOG=parachain::pvf=trace zombienet --provider=native spawn zombienet_tests/functional/0001-parachains-pvf.toml -``` - -- You can pick a validator node like `alice` from the output and view its logs -(`tail -f `) or metrics. Make sure there is nothing funny in the logs -(try `grep WARN `). - -#### Testing at scale (4) - -Launching many nodes with configurable network speed and node features in a cluster of nodes. At this scale the -[Simnet][simnet] comes into play which launches a full cluster of nodes. The scale is handled by spawning a kubernetes -cluster and the meta description is covered by [Gurke][Gurke]. Asserts are made using Grafana rules, based on the -existing prometheus metrics. This can be extended by adding an additional service translating `jaeger` spans into -addition prometheus avoiding additional Polkadot source changes. - -_Behavior tests_ and _testing at scale_ have naturally soft boundary. The most significant difference is the presence of -a real network and the number of nodes, since a single host often not capable to run multiple nodes at once. - -## Observing Logs - -To verify expected behavior it's often useful to observe logs. To avoid too many -logs at once, you can run one test at a time: - -1. Add `sp_tracing::try_init_simple();` to the beginning of a test -2. Specify `RUST_LOG=::=trace` before the cargo command. - -For example: - -```sh -RUST_LOG=parachain::pvf=trace cargo test execute_can_run_serially -``` - -For more info on how our logs work, check [the docs][logs]. - -## Coverage - -Coverage gives a _hint_ of the actually covered source lines by tests and test applications. - -The state of the art is currently tarpaulin which unfortunately yields a lot of false negatives. Lines that -are in fact covered, marked as uncovered due to a mere linebreak in a statement can cause these artifacts. This leads to -lower coverage percentages than there actually is. - -Since late 2020 rust has gained [MIR based coverage tooling]( -https://blog.rust-lang.org/inside-rust/2020/11/12/source-based-code-coverage.html). - -```sh -# setup -rustup component add llvm-tools-preview -cargo install grcov miniserve - -export CARGO_INCREMENTAL=0 -# wasm is not happy with the instrumentation -export SKIP_BUILD_WASM=true -export BUILD_DUMMY_WASM_BINARY=true -# the actully collected coverage data -export LLVM_PROFILE_FILE="llvmcoveragedata-%p-%m.profraw" -# build wasm without instrumentation -export WASM_TARGET_DIRECTORY=/tmp/wasm -cargo +nightly build -# required rust flags -export RUSTFLAGS="-Zinstrument-coverage" -# assure target dir is clean -rm -r target/{debug,tests} -# run tests to get coverage data -cargo +nightly test --all - -# create the *html* report out of all the test binaries -# mostly useful for local inspection -grcov . --binary-path ./target/debug -s . -t html --branch --ignore-not-existing -o ./coverage/ -miniserve -r ./coverage - -# create a *codecov* compatible report -grcov . --binary-path ./target/debug/ -s . -t lcov --branch --ignore-not-existing --ignore "/*" -o lcov.info -``` - -The test coverage in `lcov` can the be published to . - -```sh -bash <(curl -s https://codecov.io/bash) -f lcov.info -``` - -or just printed as part of the PR using a github action i.e. -[`jest-lcov-reporter`](https://github.com/marketplace/actions/jest-lcov-reporter). - -For full examples on how to use [`grcov` /w Polkadot specifics see the github -repo](https://github.com/mozilla/grcov#coverallscodecov-output). - -## Fuzzing - -Fuzzing is an approach to verify correctness against arbitrary or partially structured inputs. - -Currently implemented fuzzing targets: - -- `erasure-coding` - -The tooling of choice here is `honggfuzz-rs` as it allows _fastest_ coverage according to "some paper" which is a -positive feature when run as part of PRs. - -Fuzzing is generally not applicable for data secured by cryptographic hashes or signatures. Either the input has to be -specifically crafted, such that the discarded input percentage stays in an acceptable range. System level fuzzing is -hence simply not feasible due to the amount of state that is required. - -Other candidates to implement fuzzing are: - -- `rpc` -- ... - -## Performance metrics - -There are various ways of performance metrics. - -- timing with `criterion` -- cache hits/misses w/ `iai` harness or `criterion-perf` -- `coz` a performance based compiler - -Most of them are standard tools to aid in the creation of statistical tests regarding change in time of certain unit -tests. - -`coz` is meant for runtime. In our case, the system is far too large to yield a sufficient number of measurements in -finite time. An alternative approach could be to record incoming package streams per subsystem and store dumps of them, -which in return could be replayed repeatedly at an accelerated speed, with which enough metrics could be obtained to -yield information on which areas would improve the metrics. This unfortunately will not yield much information, since -most if not all of the subsystem code is linear based on the input to generate one or multiple output messages, it is -unlikely to get any useful metrics without mocking a sufficiently large part of the other subsystem which overlaps with -[#Integration tests] which is unfortunately not repeatable as of now. As such the effort gain seems low and this is not -pursued at the current time. - -## Writing small scope integration tests with preconfigured workers - -Requirements: - -- spawn nodes with preconfigured behaviors -- allow multiple types of configuration to be specified -- allow extendability via external crates -- ... - ---- - -## Implementation of different behavior strain nodes - -### Goals - -The main goals are is to allow creating a test node which exhibits a certain behavior by utilizing a subset of _wrapped_ -or _replaced_ subsystems easily. The runtime must not matter at all for these tests and should be simplistic. The -execution must be fast, this mostly means to assure a close to zero network latency as well as shorting the block time -and epoch times down to a few `100ms` and a few dozend blocks per epoch. - -### Approach - -#### MVP - -A simple small scale builder pattern would suffice for stage one implementation of allowing to replace individual -subsystems. An alternative would be to harness the existing `AllSubsystems` type and replace the subsystems as needed. - -#### Full `proc-macro` implementation - -`Overseer` is a common pattern. It could be extracted as `proc` macro and generative `proc-macro`. This would replace -the `AllSubsystems` type as well as implicitly create the `AllMessages` enum as `AllSubsystemsGen` does today. - -The implementation is yet to be completed, see the [implementation PR](https://github.com/paritytech/polkadot/pull/2962) -for details. - -##### Declare an overseer implementation - -```rust -struct BehaveMaleficient; - -impl OverseerGen for BehaveMaleficient { - fn generate<'a, Spawner, RuntimeClient>( - &self, - args: OverseerGenArgs<'a, Spawner, RuntimeClient>, - ) -> Result<(Overseer>, OverseerHandler), Error> - where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, - Spawner: 'static + overseer::gen::Spawner + Clone + Unpin, - { - let spawner = args.spawner.clone(); - let leaves = args.leaves.clone(); - let runtime_client = args.runtime_client.clone(); - let registry = args.registry.clone(); - let candidate_validation_config = args.candidate_validation_config.clone(); - // modify the subsystem(s) as needed: - let all_subsystems = create_default_subsystems(args)?. - // or spawn an entirely new set - - replace_candidate_validation( - // create the filtered subsystem - FilteredSubsystem::new( - CandidateValidationSubsystem::with_config( - candidate_validation_config, - Metrics::register(registry)?, - ), - // an implementation of - Skippy::default(), - ), - ); - - Overseer::new(leaves, all_subsystems, registry, runtime_client, spawner) - .map_err(|e| e.into()) - - // A builder pattern will simplify this further - // WIP https://github.com/paritytech/polkadot/pull/2962 - } -} - -fn main() -> eyre::Result<()> { - color_eyre::install()?; - let cli = Cli::from_args(); - assert_matches::assert_matches!(cli.subcommand, None); - polkadot_cli::run_node(cli, BehaveMaleficient)?; - Ok(()) -} -``` - -[`variant-a`](../node/malus/src/variant-a.rs) is a fully working example. - -#### Simnet - -Spawn a kubernetes cluster based on a meta description using [Gurke] with the [Simnet] scripts. - -Coordinated attacks of multiple nodes or subsystems must be made possible via a side-channel, that is out of scope for -this document. - -The individual node configurations are done as targets with a particular builder configuration. - -#### Behavior tests w/o Simnet - -Commonly this will require multiple nodes, and most machines are limited to running two or three nodes concurrently. -Hence, this is not the common case and is just an implementation _idea_. - -```rust -behavior_testcase!{ -"TestRuntime" => -"Alice": , -"Bob": , -"Charles": Default, -"David": "Charles", -"Eve": "Bob", -} -``` - -[zombienet]: https://github.com/paritytech/zombienet -[Gurke]: https://github.com/paritytech/gurke -[simnet]: https://github.com/paritytech/simnet_scripts -[logs]: https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/node/gum/src/lib.rs diff --git a/polkadot/grafana/README.md b/polkadot/grafana/README.md index 7350001bfa1fcebe367799cc36426522c4cf4f97..e909fdd29a757afba3ba6c76ade6466acffc7bda 100644 --- a/polkadot/grafana/README.md +++ b/polkadot/grafana/README.md @@ -8,7 +8,7 @@ monitor the liveliness and performance of a network and its validators. # How does it work ? Just import the dashboard JSON files from this folder in your Grafana installation. All dashboards are grouped in -folder percategory (like for example `parachains`). The files have been created by Grafana export functionality and +folder per category (like for example `parachains`). The files have been created by Grafana export functionality and follow the data model specified [here](https://grafana.com/docs/grafana/latest/dashboards/json-model/). We aim to keep the dashboards here in sync with the implementation, except dashboards for development and diff --git a/polkadot/node/collation-generation/Cargo.toml b/polkadot/node/collation-generation/Cargo.toml index 8df0c2b1edae47cad98881e7243eb1dc68449e9e..f72af87c15edf1f1147018de75b79079c8755b41 100644 --- a/polkadot/node/collation-generation/Cargo.toml +++ b/polkadot/node/collation-generation/Cargo.toml @@ -26,4 +26,5 @@ parity-scale-codec = { version = "3.6.1", default-features = false, features = [ polkadot-node-subsystem-test-helpers = { path = "../subsystem-test-helpers" } test-helpers = { package = "polkadot-primitives-test-helpers", path = "../../primitives/test-helpers" } assert_matches = "1.4.0" +rstest = "0.18.2" sp-keyring = { path = "../../../substrate/primitives/keyring" } diff --git a/polkadot/node/collation-generation/src/lib.rs b/polkadot/node/collation-generation/src/lib.rs index cfa75d7b44119d8ec388e80b7bb537b0adeb42d0..3b1a8f5ff2305c022f56aac6bddec1c00599cbd3 100644 --- a/polkadot/node/collation-generation/src/lib.rs +++ b/polkadot/node/collation-generation/src/lib.rs @@ -38,21 +38,25 @@ use polkadot_node_primitives::{ SubmitCollationParams, }; use polkadot_node_subsystem::{ - messages::{CollationGenerationMessage, CollatorProtocolMessage}, + messages::{CollationGenerationMessage, CollatorProtocolMessage, RuntimeApiRequest}, overseer, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, RuntimeApiError, SpawnedSubsystem, SubsystemContext, SubsystemError, SubsystemResult, }; use polkadot_node_subsystem_util::{ - request_async_backing_params, request_availability_cores, request_persisted_validation_data, - request_validation_code, request_validation_code_hash, request_validators, + has_required_runtime, request_async_backing_params, request_availability_cores, + request_claim_queue, request_persisted_validation_data, request_validation_code, + request_validation_code_hash, request_validators, }; use polkadot_primitives::{ collator_signature_payload, CandidateCommitments, CandidateDescriptor, CandidateReceipt, - CollatorPair, CoreState, Hash, Id as ParaId, OccupiedCoreAssumption, PersistedValidationData, - ValidationCodeHash, + CollatorPair, CoreIndex, CoreState, Hash, Id as ParaId, OccupiedCoreAssumption, + PersistedValidationData, ScheduledCore, ValidationCodeHash, }; use sp_core::crypto::Pair; -use std::sync::Arc; +use std::{ + collections::{BTreeMap, VecDeque}, + sync::Arc, +}; mod error; @@ -223,6 +227,7 @@ async fn handle_new_activations( let availability_cores = availability_cores??; let n_validators = validators??.len(); let async_backing_params = async_backing_params?.ok(); + let maybe_claim_queue = fetch_claim_queue(ctx.sender(), relay_parent).await?; for (core_idx, core) in availability_cores.into_iter().enumerate() { let _availability_core_timer = metrics.time_new_activations_availability_core(); @@ -239,10 +244,25 @@ async fn handle_new_activations( // TODO [now]: this assumes that next up == current. // in practice we should only set `OccupiedCoreAssumption::Included` // when the candidate occupying the core is also of the same para. - if let Some(scheduled) = occupied_core.next_up_on_available { - (scheduled, OccupiedCoreAssumption::Included) - } else { - continue + let res = match maybe_claim_queue { + Some(ref claim_queue) => { + // read what's in the claim queue for this core + fetch_next_scheduled_on_core( + claim_queue, + CoreIndex(core_idx as u32), + ) + }, + None => { + // Runtime doesn't support claim queue runtime api. Fallback to + // `next_up_on_available` + occupied_core.next_up_on_available + }, + } + .map(|scheduled| (scheduled, OccupiedCoreAssumption::Included)); + + match res { + Some(res) => res, + None => continue, } }, _ => { @@ -466,6 +486,7 @@ async fn construct_and_distribute_receipt( } = collation; let persisted_validation_data_hash = validation_data.hash(); + let parent_head_data = validation_data.parent_head.clone(); let parent_head_data_hash = validation_data.parent_head.hash(); // Apply compression to the block data. @@ -551,12 +572,13 @@ async fn construct_and_distribute_receipt( metrics.on_collation_generated(); sender - .send_message(CollatorProtocolMessage::DistributeCollation( - ccr, + .send_message(CollatorProtocolMessage::DistributeCollation { + candidate_receipt: ccr, parent_head_data_hash, pov, + parent_head_data, result_sender, - )) + }) .await; } @@ -598,3 +620,37 @@ fn erasure_root( let chunks = polkadot_erasure_coding::obtain_chunks_v1(n_validators, &available_data)?; Ok(polkadot_erasure_coding::branches(&chunks).root()) } + +// Checks if the runtime supports `request_claim_queue` and executes it. Returns `Ok(None)` +// otherwise. Any [`RuntimeApiError`]s are bubbled up to the caller. +async fn fetch_claim_queue( + sender: &mut impl overseer::CollationGenerationSenderTrait, + relay_parent: Hash, +) -> crate::error::Result>>> { + if has_required_runtime( + sender, + relay_parent, + RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT, + ) + .await + { + let res = request_claim_queue(relay_parent, sender).await.await??; + Ok(Some(res)) + } else { + gum::trace!(target: LOG_TARGET, "Runtime doesn't support `request_claim_queue`"); + Ok(None) + } +} + +// Returns the next scheduled `ParaId` for a core in the claim queue, wrapped in `ScheduledCore`. +// This function is supposed to be used in `handle_new_activations` hence the return type. +fn fetch_next_scheduled_on_core( + claim_queue: &BTreeMap>, + core_idx: CoreIndex, +) -> Option { + claim_queue + .get(&core_idx)? + .front() + .cloned() + .map(|para_id| ScheduledCore { para_id, collator: None }) +} diff --git a/polkadot/node/collation-generation/src/tests.rs b/polkadot/node/collation-generation/src/tests.rs index 9094f40cca8419b608d451c1dc6f09854f53f07c..9b16980e6af43e458a644697282aa74d52bc5ca2 100644 --- a/polkadot/node/collation-generation/src/tests.rs +++ b/polkadot/node/collation-generation/src/tests.rs @@ -25,15 +25,18 @@ use polkadot_node_primitives::{BlockData, Collation, CollationResult, MaybeCompr use polkadot_node_subsystem::{ errors::RuntimeApiError, messages::{AllMessages, RuntimeApiMessage, RuntimeApiRequest}, + ActivatedLeaf, }; use polkadot_node_subsystem_test_helpers::{subsystem_test_harness, TestSubsystemContextHandle}; use polkadot_node_subsystem_util::TimeoutExt; use polkadot_primitives::{ - CollatorPair, HeadData, Id as ParaId, PersistedValidationData, ScheduledCore, ValidationCode, + AsyncBackingParams, CollatorPair, HeadData, Id as ParaId, Id, PersistedValidationData, + ScheduledCore, ValidationCode, }; +use rstest::rstest; use sp_keyring::sr25519::Keyring as Sr25519Keyring; use std::pin::Pin; -use test_helpers::{dummy_hash, dummy_head_data, dummy_validator}; +use test_helpers::{dummy_candidate_descriptor, dummy_hash, dummy_head_data, dummy_validator}; type VirtualOverseer = TestSubsystemContextHandle; @@ -132,8 +135,10 @@ fn scheduled_core_for>(para_id: Id) -> ScheduledCore { ScheduledCore { para_id: para_id.into(), collator: None } } -#[test] -fn requests_availability_per_relay_parent() { +#[rstest] +#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT - 1)] +#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT)] +fn requests_availability_per_relay_parent(#[case] runtime_version: u32) { let activated_hashes: Vec = vec![[1; 32].into(), [4; 32].into(), [9; 32].into(), [16; 32].into()]; @@ -159,6 +164,18 @@ fn requests_availability_per_relay_parent() { ))) => { tx.send(Err(RuntimeApiError::NotSupported { runtime_api_name: "doesnt_matter" })).unwrap(); }, + Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _hash, + RuntimeApiRequest::Version(tx), + ))) => { + tx.send(Ok(runtime_version)).unwrap(); + }, + Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _hash, + RuntimeApiRequest::ClaimQueue(tx), + ))) if runtime_version >= RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT => { + tx.send(Ok(BTreeMap::new())).unwrap(); + }, Some(msg) => panic!("didn't expect any other overseer requests given no availability cores; got {:?}", msg), } } @@ -184,8 +201,10 @@ fn requests_availability_per_relay_parent() { assert_eq!(requested_availability_cores, activated_hashes); } -#[test] -fn requests_validation_data_for_scheduled_matches() { +#[rstest] +#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT - 1)] +#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT)] +fn requests_validation_data_for_scheduled_matches(#[case] runtime_version: u32) { let activated_hashes: Vec = vec![ Hash::repeat_byte(1), Hash::repeat_byte(4), @@ -242,6 +261,18 @@ fn requests_validation_data_for_scheduled_matches() { })) .unwrap(); }, + Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _hash, + RuntimeApiRequest::Version(tx), + ))) => { + tx.send(Ok(runtime_version)).unwrap(); + }, + Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _hash, + RuntimeApiRequest::ClaimQueue(tx), + ))) if runtime_version >= RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT => { + tx.send(Ok(BTreeMap::new())).unwrap(); + }, Some(msg) => { panic!("didn't expect any other overseer requests; got {:?}", msg) }, @@ -271,8 +302,10 @@ fn requests_validation_data_for_scheduled_matches() { assert_eq!(requested_validation_data, vec![[4; 32].into()]); } -#[test] -fn sends_distribute_collation_message() { +#[rstest] +#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT - 1)] +#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT)] +fn sends_distribute_collation_message(#[case] runtime_version: u32) { let activated_hashes: Vec = vec![ Hash::repeat_byte(1), Hash::repeat_byte(4), @@ -339,6 +372,18 @@ fn sends_distribute_collation_message() { })) .unwrap(); }, + Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _hash, + RuntimeApiRequest::Version(tx), + ))) => { + tx.send(Ok(runtime_version)).unwrap(); + }, + Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _hash, + RuntimeApiRequest::ClaimQueue(tx), + ))) if runtime_version >= RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT => { + tx.send(Ok(BTreeMap::new())).unwrap(); + }, Some(msg @ AllMessages::CollatorProtocol(_)) => { inner_to_collator_protocol.lock().await.push(msg); }, @@ -390,11 +435,11 @@ fn sends_distribute_collation_message() { assert_eq!(to_collator_protocol.len(), 1); match AllMessages::from(to_collator_protocol.pop().unwrap()) { - AllMessages::CollatorProtocol(CollatorProtocolMessage::DistributeCollation( - CandidateReceipt { descriptor, .. }, - _pov, - .., - )) => { + AllMessages::CollatorProtocol(CollatorProtocolMessage::DistributeCollation { + candidate_receipt, + .. + }) => { + let CandidateReceipt { descriptor, .. } = candidate_receipt; // signature generation is non-deterministic, so we can't just assert that the // expected descriptor is correct. What we can do is validate that the produced // descriptor has a valid signature, then just copy in the generated signature @@ -423,8 +468,10 @@ fn sends_distribute_collation_message() { } } -#[test] -fn fallback_when_no_validation_code_hash_api() { +#[rstest] +#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT - 1)] +#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT)] +fn fallback_when_no_validation_code_hash_api(#[case] runtime_version: u32) { // This is a variant of the above test, but with the validation code hash API disabled. let activated_hashes: Vec = vec![ @@ -501,9 +548,22 @@ fn fallback_when_no_validation_code_hash_api() { })) .unwrap(); }, + Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _hash, + RuntimeApiRequest::Version(tx), + ))) => { + tx.send(Ok(runtime_version)).unwrap(); + }, Some(msg @ AllMessages::CollatorProtocol(_)) => { inner_to_collator_protocol.lock().await.push(msg); }, + Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _hash, + RuntimeApiRequest::ClaimQueue(tx), + ))) if runtime_version >= RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT => { + let res = BTreeMap::>::new(); + tx.send(Ok(res)).unwrap(); + }, Some(msg) => { panic!("didn't expect any other overseer requests; got {:?}", msg) }, @@ -529,11 +589,11 @@ fn fallback_when_no_validation_code_hash_api() { assert_eq!(to_collator_protocol.len(), 1); match &to_collator_protocol[0] { - AllMessages::CollatorProtocol(CollatorProtocolMessage::DistributeCollation( - CandidateReceipt { descriptor, .. }, - _pov, - .., - )) => { + AllMessages::CollatorProtocol(CollatorProtocolMessage::DistributeCollation { + candidate_receipt, + .. + }) => { + let CandidateReceipt { descriptor, .. } = candidate_receipt; assert_eq!(expect_validation_code_hash, descriptor.validation_code_hash); }, _ => panic!("received wrong message type"), @@ -619,18 +679,268 @@ fn submit_collation_leads_to_distribution() { assert_matches!( overseer_recv(&mut virtual_overseer).await, - AllMessages::CollatorProtocol(CollatorProtocolMessage::DistributeCollation( - ccr, + AllMessages::CollatorProtocol(CollatorProtocolMessage::DistributeCollation { + candidate_receipt, parent_head_data_hash, .. - )) => { + }) => { + let CandidateReceipt { descriptor, .. } = candidate_receipt; assert_eq!(parent_head_data_hash, parent_head.hash()); - assert_eq!(ccr.descriptor().persisted_validation_data_hash, expected_pvd.hash()); - assert_eq!(ccr.descriptor().para_head, dummy_head_data().hash()); - assert_eq!(ccr.descriptor().validation_code_hash, validation_code_hash); + assert_eq!(descriptor.persisted_validation_data_hash, expected_pvd.hash()); + assert_eq!(descriptor.para_head, dummy_head_data().hash()); + assert_eq!(descriptor.validation_code_hash, validation_code_hash); } ); virtual_overseer }); } + +// There is one core in `Occupied` state and async backing is enabled. On new head activation +// `CollationGeneration` should produce and distribute a new collation. +#[rstest] +#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT - 1)] +#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT)] +fn distribute_collation_for_occupied_core_with_async_backing_enabled(#[case] runtime_version: u32) { + let activated_hash: Hash = [1; 32].into(); + let para_id = ParaId::from(5); + + // One core, in occupied state. The data in `CoreState` and `ClaimQueue` should match. + let cores: Vec = vec![CoreState::Occupied(polkadot_primitives::OccupiedCore { + next_up_on_available: Some(ScheduledCore { para_id, collator: None }), + occupied_since: 1, + time_out_at: 10, + next_up_on_time_out: Some(ScheduledCore { para_id, collator: None }), + availability: Default::default(), // doesn't matter + group_responsible: polkadot_primitives::GroupIndex(0), + candidate_hash: Default::default(), + candidate_descriptor: dummy_candidate_descriptor(dummy_hash()), + })]; + let claim_queue = BTreeMap::from([(CoreIndex::from(0), VecDeque::from([para_id]))]); + + test_harness(|mut virtual_overseer| async move { + helpers::initialize_collator(&mut virtual_overseer, para_id).await; + helpers::activate_new_head(&mut virtual_overseer, activated_hash).await; + helpers::handle_runtime_calls_on_new_head_activation( + &mut virtual_overseer, + activated_hash, + AsyncBackingParams { max_candidate_depth: 1, allowed_ancestry_len: 1 }, + cores, + runtime_version, + claim_queue, + ) + .await; + helpers::handle_core_processing_for_a_leaf( + &mut virtual_overseer, + activated_hash, + para_id, + // `CoreState` is `Occupied` => `OccupiedCoreAssumption` is `Included` + OccupiedCoreAssumption::Included, + ) + .await; + + virtual_overseer + }); +} + +// There is one core in `Occupied` state and async backing is disabled. On new head activation +// no new collation should be generated. +#[rstest] +#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT - 1)] +#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT)] +fn no_collation_is_distributed_for_occupied_core_with_async_backing_disabled( + #[case] runtime_version: u32, +) { + let activated_hash: Hash = [1; 32].into(); + let para_id = ParaId::from(5); + + // One core, in occupied state. The data in `CoreState` and `ClaimQueue` should match. + let cores: Vec = vec![CoreState::Occupied(polkadot_primitives::OccupiedCore { + next_up_on_available: Some(ScheduledCore { para_id, collator: None }), + occupied_since: 1, + time_out_at: 10, + next_up_on_time_out: Some(ScheduledCore { para_id, collator: None }), + availability: Default::default(), // doesn't matter + group_responsible: polkadot_primitives::GroupIndex(0), + candidate_hash: Default::default(), + candidate_descriptor: dummy_candidate_descriptor(dummy_hash()), + })]; + let claim_queue = BTreeMap::from([(CoreIndex::from(0), VecDeque::from([para_id]))]); + + test_harness(|mut virtual_overseer| async move { + helpers::initialize_collator(&mut virtual_overseer, para_id).await; + helpers::activate_new_head(&mut virtual_overseer, activated_hash).await; + helpers::handle_runtime_calls_on_new_head_activation( + &mut virtual_overseer, + activated_hash, + AsyncBackingParams { max_candidate_depth: 0, allowed_ancestry_len: 0 }, + cores, + runtime_version, + claim_queue, + ) + .await; + + virtual_overseer + }); +} + +mod helpers { + use super::*; + + // Sends `Initialize` with a collator config + pub async fn initialize_collator(virtual_overseer: &mut VirtualOverseer, para_id: ParaId) { + virtual_overseer + .send(FromOrchestra::Communication { + msg: CollationGenerationMessage::Initialize(test_config(para_id)), + }) + .await; + } + + // Sends `ActiveLeaves` for a single leaf with the specified hash. Block number is hardcoded. + pub async fn activate_new_head(virtual_overseer: &mut VirtualOverseer, activated_hash: Hash) { + virtual_overseer + .send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate { + activated: Some(ActivatedLeaf { + hash: activated_hash, + number: 10, + unpin_handle: polkadot_node_subsystem_test_helpers::mock::dummy_unpin_handle( + activated_hash, + ), + span: Arc::new(overseer::jaeger::Span::Disabled), + }), + ..Default::default() + }))) + .await; + } + + // Handle all runtime calls performed in `handle_new_activations`. Conditionally expects a + // `CLAIM_QUEUE_RUNTIME_REQUIREMENT` call if the passed `runtime_version` is greater or equal to + // `CLAIM_QUEUE_RUNTIME_REQUIREMENT` + pub async fn handle_runtime_calls_on_new_head_activation( + virtual_overseer: &mut VirtualOverseer, + activated_hash: Hash, + async_backing_params: AsyncBackingParams, + cores: Vec, + runtime_version: u32, + claim_queue: BTreeMap>, + ) { + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(hash, RuntimeApiRequest::AvailabilityCores(tx))) => { + assert_eq!(hash, activated_hash); + let _ = tx.send(Ok(cores)); + } + ); + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(hash, RuntimeApiRequest::Validators(tx))) => { + assert_eq!(hash, activated_hash); + let _ = tx.send(Ok(vec![ + Sr25519Keyring::Alice.public().into(), + Sr25519Keyring::Bob.public().into(), + Sr25519Keyring::Charlie.public().into(), + ])); + } + ); + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + hash, + RuntimeApiRequest::AsyncBackingParams( + tx, + ), + )) => { + assert_eq!(hash, activated_hash); + let _ = tx.send(Ok(async_backing_params)); + } + ); + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + hash, + RuntimeApiRequest::Version(tx), + )) => { + assert_eq!(hash, activated_hash); + let _ = tx.send(Ok(runtime_version)); + } + ); + + if runtime_version == RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT { + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + hash, + RuntimeApiRequest::ClaimQueue(tx), + )) => { + assert_eq!(hash, activated_hash); + let _ = tx.send(Ok(claim_queue)); + } + ); + } + } + + // Handles all runtime requests performed in `handle_new_activations` for the case when a + // collation should be prepared for the new leaf + pub async fn handle_core_processing_for_a_leaf( + virtual_overseer: &mut VirtualOverseer, + activated_hash: Hash, + para_id: ParaId, + expected_occupied_core_assumption: OccupiedCoreAssumption, + ) { + // Some hardcoded data - if needed, extract to parameters + let validation_code_hash = ValidationCodeHash::from(Hash::repeat_byte(42)); + let parent_head = HeadData::from(vec![1, 2, 3]); + let pvd = PersistedValidationData { + parent_head: parent_head.clone(), + relay_parent_number: 10, + relay_parent_storage_root: Hash::repeat_byte(1), + max_pov_size: 1024, + }; + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(hash, RuntimeApiRequest::PersistedValidationData(id, a, tx))) => { + assert_eq!(hash, activated_hash); + assert_eq!(id, para_id); + assert_eq!(a, expected_occupied_core_assumption); + + let _ = tx.send(Ok(Some(pvd.clone()))); + } + ); + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + hash, + RuntimeApiRequest::ValidationCodeHash( + id, + assumption, + tx, + ), + )) => { + assert_eq!(hash, activated_hash); + assert_eq!(id, para_id); + assert_eq!(assumption, expected_occupied_core_assumption); + + let _ = tx.send(Ok(Some(validation_code_hash))); + } + ); + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::CollatorProtocol(CollatorProtocolMessage::DistributeCollation{ + candidate_receipt, + parent_head_data_hash, + .. + }) => { + assert_eq!(parent_head_data_hash, parent_head.hash()); + assert_eq!(candidate_receipt.descriptor().persisted_validation_data_hash, pvd.hash()); + assert_eq!(candidate_receipt.descriptor().para_head, dummy_head_data().hash()); + assert_eq!(candidate_receipt.descriptor().validation_code_hash, validation_code_hash); + } + ); + } +} diff --git a/polkadot/node/core/approval-voting/Cargo.toml b/polkadot/node/core/approval-voting/Cargo.toml index f6d89dbc15280efc51e685595cb62969d3d206e7..2a5b6198b9a827788696bdf5e1eeeb5829a9da6d 100644 --- a/polkadot/node/core/approval-voting/Cargo.toml +++ b/polkadot/node/core/approval-voting/Cargo.toml @@ -35,7 +35,7 @@ sp-consensus = { path = "../../../../substrate/primitives/consensus/common", def sp-consensus-slots = { path = "../../../../substrate/primitives/consensus/slots", default-features = false } sp-application-crypto = { path = "../../../../substrate/primitives/application-crypto", default-features = false, features = ["full_crypto"] } sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } -# should match schnorrkel +# rand_core should match schnorrkel rand_core = "0.6.2" rand_chacha = { version = "0.3.1" } rand = "0.8.5" diff --git a/polkadot/node/core/approval-voting/src/approval_db/v1/tests.rs b/polkadot/node/core/approval-voting/src/approval_db/v1/tests.rs index b979cb7ef45f6bf6af02acd8fde1d04e6bee8206..b0966ad01f7bff8242e3255968b3e85b4d0239ca 100644 --- a/polkadot/node/core/approval-voting/src/approval_db/v1/tests.rs +++ b/polkadot/node/core/approval-voting/src/approval_db/v1/tests.rs @@ -254,7 +254,7 @@ fn canonicalize_works() { // -> B1 -> C1 -> D1 // A -> B2 -> C2 -> D2 // - // We'll canonicalize C1. Everytning except D1 should disappear. + // We'll canonicalize C1. Everything except D1 should disappear. // // Candidates: // Cand1 in B2 diff --git a/polkadot/node/core/approval-voting/src/approval_db/v2/migration_helpers.rs b/polkadot/node/core/approval-voting/src/approval_db/v2/migration_helpers.rs index df6e4754dbd63ba81e736495297bfaf017a4e3dc..1081d79884f7594be12ebe6cee3047cb2b9feebe 100644 --- a/polkadot/node/core/approval-voting/src/approval_db/v2/migration_helpers.rs +++ b/polkadot/node/core/approval-voting/src/approval_db/v2/migration_helpers.rs @@ -79,7 +79,7 @@ pub fn v1_to_latest(db: Arc, config: Config) -> Result<()> { block.candidates().iter().enumerate() { // Loading the candidate will also perform the conversion to the updated format and - // return that represantation. + // return that representation. if let Some(candidate_entry) = backend .load_candidate_entry_v1(&candidate_hash, candidate_index as CandidateIndex) .map_err(|e| Error::InternalError(e))? diff --git a/polkadot/node/core/approval-voting/src/approval_db/v2/tests.rs b/polkadot/node/core/approval-voting/src/approval_db/v2/tests.rs index 6021b44c2765ff12a03e1ad85bfb8117c9fcfb03..5fa915add4166fa1f042dadaae063aaa83941b92 100644 --- a/polkadot/node/core/approval-voting/src/approval_db/v2/tests.rs +++ b/polkadot/node/core/approval-voting/src/approval_db/v2/tests.rs @@ -269,7 +269,7 @@ fn canonicalize_works() { // -> B1 -> C1 -> D1 // A -> B2 -> C2 -> D2 // - // We'll canonicalize C1. Everytning except D1 should disappear. + // We'll canonicalize C1. Everything except D1 should disappear. // // Candidates: // Cand1 in B2 diff --git a/polkadot/node/core/approval-voting/src/approval_db/v3/migration_helpers.rs b/polkadot/node/core/approval-voting/src/approval_db/v3/migration_helpers.rs index ad5e89ef3de84035ff3e9c79533edfc07bf8d4c5..d1e7ee08225bf96ceffab8842972cf53ed5725da 100644 --- a/polkadot/node/core/approval-voting/src/approval_db/v3/migration_helpers.rs +++ b/polkadot/node/core/approval-voting/src/approval_db/v3/migration_helpers.rs @@ -60,7 +60,7 @@ pub fn v2_to_latest(db: Arc, config: Config) -> Result<()> { block.candidates().iter().enumerate() { // Loading the candidate will also perform the conversion to the updated format and - // return that represantation. + // return that representation. if let Some(candidate_entry) = backend .load_candidate_entry_v2(&candidate_hash, candidate_index as CandidateIndex) .map_err(|e| Error::InternalError(e))? @@ -104,7 +104,7 @@ pub fn v1_to_latest_sanity_check( for block in all_blocks { for (_core_index, candidate_hash) in block.candidates() { // Loading the candidate will also perform the conversion to the updated format and - // return that represantation. + // return that representation. if let Some(candidate_entry) = backend.load_candidate_entry(&candidate_hash).unwrap() { candidates.insert(candidate_entry.candidate.hash()); } diff --git a/polkadot/node/core/approval-voting/src/approval_db/v3/tests.rs b/polkadot/node/core/approval-voting/src/approval_db/v3/tests.rs index 08c65461bca80aafb758e42c80b509bc37c47ece..7c0cf9d4f7daaccd1887a03a2a06e9bb4e6b587e 100644 --- a/polkadot/node/core/approval-voting/src/approval_db/v3/tests.rs +++ b/polkadot/node/core/approval-voting/src/approval_db/v3/tests.rs @@ -264,7 +264,7 @@ fn canonicalize_works() { // -> B1 -> C1 -> D1 // A -> B2 -> C2 -> D2 // - // We'll canonicalize C1. Everytning except D1 should disappear. + // We'll canonicalize C1. Everything except D1 should disappear. // // Candidates: // Cand1 in B2 diff --git a/polkadot/node/core/approval-voting/src/criteria.rs b/polkadot/node/core/approval-voting/src/criteria.rs index 1ebea2641b62198c291951448c8c552d9cfb34cf..57c0ac272dc5a5cad8b9c69831faa5ecfc778033 100644 --- a/polkadot/node/core/approval-voting/src/criteria.rs +++ b/polkadot/node/core/approval-voting/src/criteria.rs @@ -148,7 +148,7 @@ fn relay_vrf_modulo_cores( generate_samples(rand_chacha, num_samples as usize, max_cores as usize) } -/// Generates `num_sumples` randomly from (0..max_cores) range +/// Generates `num_samples` randomly from (0..max_cores) range /// /// Note! The algorithm can't change because validators on the other /// side won't be able to check the assignments until they update. diff --git a/polkadot/node/core/approval-voting/src/import.rs b/polkadot/node/core/approval-voting/src/import.rs index 7a56e9fd11293d1a96debd5e98b719a19c48045f..d34191fba31d2bf41f83bee3bc08565a260640f1 100644 --- a/polkadot/node/core/approval-voting/src/import.rs +++ b/polkadot/node/core/approval-voting/src/import.rs @@ -91,7 +91,7 @@ enum ImportedBlockInfoError { #[error(transparent)] RuntimeError(RuntimeApiError), - #[error("future cancalled while requesting {0}")] + #[error("future cancelled while requesting {0}")] FutureCancelled(&'static str, futures::channel::oneshot::Canceled), #[error(transparent)] diff --git a/polkadot/node/core/approval-voting/src/lib.rs b/polkadot/node/core/approval-voting/src/lib.rs index 456ae319787b07740bb0817dcfbe260500eb9dd8..76b3d476e28f51cef6e1a3b7ab4859d0e92613d8 100644 --- a/polkadot/node/core/approval-voting/src/lib.rs +++ b/polkadot/node/core/approval-voting/src/lib.rs @@ -55,9 +55,9 @@ use polkadot_node_subsystem_util::{ }; use polkadot_primitives::{ vstaging::{ApprovalVoteMultipleCandidates, ApprovalVotingParams}, - BlockNumber, CandidateHash, CandidateIndex, CandidateReceipt, DisputeStatement, ExecutorParams, - GroupIndex, Hash, PvfExecKind, SessionIndex, SessionInfo, ValidDisputeStatementKind, - ValidatorId, ValidatorIndex, ValidatorPair, ValidatorSignature, + BlockNumber, CandidateHash, CandidateIndex, CandidateReceipt, CoreIndex, DisputeStatement, + ExecutorParams, GroupIndex, Hash, PvfExecKind, SessionIndex, SessionInfo, + ValidDisputeStatementKind, ValidatorId, ValidatorIndex, ValidatorPair, ValidatorSignature, }; use sc_keystore::LocalKeystore; use sp_application_crypto::Pair; @@ -1253,13 +1253,20 @@ async fn handle_actions( Action::BecomeActive => { *mode = Mode::Active; - let messages = distribution_messages_for_activation( + let (messages, next_actions) = distribution_messages_for_activation( + ctx, overlayed_db, state, delayed_approvals_timers, - )?; + session_info_provider, + ) + .await?; ctx.send_messages(messages.into_iter()).await; + let next_actions: Vec = + next_actions.into_iter().map(|v| v.clone()).chain(actions_iter).collect(); + + actions_iter = next_actions.into_iter(); }, Action::Conclude => { conclude = true; @@ -1290,6 +1297,21 @@ fn cores_to_candidate_indices( CandidateBitfield::try_from(candidate_indices) } +// Returns the claimed core bitfield from the assignment cert and the core index +// from the block entry. +fn get_core_indices_on_startup( + assignment: &AssignmentCertKindV2, + block_entry_core_index: CoreIndex, +) -> CoreBitfield { + match &assignment { + AssignmentCertKindV2::RelayVRFModuloCompact { core_bitfield } => core_bitfield.clone(), + AssignmentCertKindV2::RelayVRFModulo { sample: _ } => + CoreBitfield::try_from(vec![block_entry_core_index]).expect("Not an empty vec; qed"), + AssignmentCertKindV2::RelayVRFDelay { core_index } => + CoreBitfield::try_from(vec![*core_index]).expect("Not an empty vec; qed"), + } +} + // Returns the claimed core bitfield from the assignment cert, the candidate hash and a // `BlockEntry`. Can fail only for VRF Delay assignments for which we cannot find the candidate hash // in the block entry which indicates a bug or corrupted storage. @@ -1313,15 +1335,19 @@ fn get_assignment_core_indices( } } -fn distribution_messages_for_activation( +#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] +async fn distribution_messages_for_activation( + ctx: &mut Context, db: &OverlayedBackend<'_, impl Backend>, state: &State, delayed_approvals_timers: &mut DelayedApprovalTimer, -) -> SubsystemResult> { + session_info_provider: &mut RuntimeInfo, +) -> SubsystemResult<(Vec, Vec)> { let all_blocks: Vec = db.load_all_blocks()?; let mut approval_meta = Vec::with_capacity(all_blocks.len()); let mut messages = Vec::new(); + let mut actions = Vec::new(); messages.push(ApprovalDistributionMessage::NewBlocks(Vec::new())); // dummy value. @@ -1356,7 +1382,7 @@ fn distribution_messages_for_activation( session: block_entry.session(), }); let mut signatures_queued = HashSet::new(); - for (_, candidate_hash) in block_entry.candidates() { + for (core_index, candidate_hash) in block_entry.candidates() { let _candidate_span = distribution_message_span.child("candidate").with_candidate(*candidate_hash); let candidate_entry = match db.load_candidate_entry(&candidate_hash)? { @@ -1378,108 +1404,121 @@ fn distribution_messages_for_activation( match approval_entry.local_statements() { (None, None) | (None, Some(_)) => {}, // second is impossible case. (Some(assignment), None) => { - if let Some(claimed_core_indices) = get_assignment_core_indices( - &assignment.cert().kind, - &candidate_hash, - &block_entry, - ) { - if block_entry.has_candidates_pending_signature() { - delayed_approvals_timers.maybe_arm_timer( - state.clock.tick_now(), - state.clock.as_ref(), - block_entry.block_hash(), - assignment.validator_index(), - ) - } + let claimed_core_indices = + get_core_indices_on_startup(&assignment.cert().kind, *core_index); + + if block_entry.has_candidates_pending_signature() { + delayed_approvals_timers.maybe_arm_timer( + state.clock.tick_now(), + state.clock.as_ref(), + block_entry.block_hash(), + assignment.validator_index(), + ) + } + + match cores_to_candidate_indices(&claimed_core_indices, &block_entry) { + Ok(bitfield) => { + gum::debug!( + target: LOG_TARGET, + candidate_hash = ?candidate_entry.candidate_receipt().hash(), + ?block_hash, + "Discovered, triggered assignment, not approved yet", + ); - match cores_to_candidate_indices( - &claimed_core_indices, - &block_entry, - ) { - Ok(bitfield) => messages.push( + let indirect_cert = IndirectAssignmentCertV2 { + block_hash, + validator: assignment.validator_index(), + cert: assignment.cert().clone(), + }; + messages.push( ApprovalDistributionMessage::DistributeAssignment( - IndirectAssignmentCertV2 { - block_hash, - validator: assignment.validator_index(), - cert: assignment.cert().clone(), - }, - bitfield, + indirect_cert.clone(), + bitfield.clone(), ), - ), - Err(err) => { - // Should never happen. If we fail here it means the - // assignment is null (no cores claimed). - gum::warn!( - target: LOG_TARGET, - ?block_hash, - ?candidate_hash, - ?err, - "Failed to create assignment bitfield", - ); - }, - } - } else { - gum::warn!( - target: LOG_TARGET, - ?block_hash, - ?candidate_hash, - "Cannot get assignment claimed core indices", - ); + ); + + if !block_entry.candidate_is_pending_signature(*candidate_hash) + { + let ExtendedSessionInfo { ref executor_params, .. } = + match get_extended_session_info( + session_info_provider, + ctx.sender(), + block_entry.block_hash(), + block_entry.session(), + ) + .await + { + Some(i) => i, + None => continue, + }; + + actions.push(Action::LaunchApproval { + claimed_candidate_indices: bitfield, + candidate_hash: candidate_entry + .candidate_receipt() + .hash(), + indirect_cert, + assignment_tranche: assignment.tranche(), + relay_block_hash: block_hash, + session: block_entry.session(), + executor_params: executor_params.clone(), + candidate: candidate_entry.candidate_receipt().clone(), + backing_group: approval_entry.backing_group(), + distribute_assignment: false, + }); + } + }, + Err(err) => { + // Should never happen. If we fail here it means the + // assignment is null (no cores claimed). + gum::warn!( + target: LOG_TARGET, + ?block_hash, + ?candidate_hash, + ?err, + "Failed to create assignment bitfield", + ); + }, } }, (Some(assignment), Some(approval_sig)) => { - if let Some(claimed_core_indices) = get_assignment_core_indices( - &assignment.cert().kind, - &candidate_hash, - &block_entry, - ) { - match cores_to_candidate_indices( - &claimed_core_indices, - &block_entry, - ) { - Ok(bitfield) => messages.push( - ApprovalDistributionMessage::DistributeAssignment( - IndirectAssignmentCertV2 { - block_hash, - validator: assignment.validator_index(), - cert: assignment.cert().clone(), - }, - bitfield, - ), - ), - Err(err) => { - gum::warn!( - target: LOG_TARGET, - ?block_hash, - ?candidate_hash, - ?err, - "Failed to create assignment bitfield", - ); - // If we didn't send assignment, we don't send approval. - continue - }, - } - if signatures_queued - .insert(approval_sig.signed_candidates_indices.clone()) - { - messages.push(ApprovalDistributionMessage::DistributeApproval( - IndirectSignedApprovalVoteV2 { + let claimed_core_indices = + get_core_indices_on_startup(&assignment.cert().kind, *core_index); + match cores_to_candidate_indices(&claimed_core_indices, &block_entry) { + Ok(bitfield) => messages.push( + ApprovalDistributionMessage::DistributeAssignment( + IndirectAssignmentCertV2 { block_hash, - candidate_indices: approval_sig - .signed_candidates_indices, validator: assignment.validator_index(), - signature: approval_sig.signature, + cert: assignment.cert().clone(), }, - )) - }; - } else { - gum::warn!( - target: LOG_TARGET, - ?block_hash, - ?candidate_hash, - "Cannot get assignment claimed core indices", - ); + bitfield, + ), + ), + Err(err) => { + gum::warn!( + target: LOG_TARGET, + ?block_hash, + ?candidate_hash, + ?err, + "Failed to create assignment bitfield", + ); + // If we didn't send assignment, we don't send approval. + continue + }, } + if signatures_queued + .insert(approval_sig.signed_candidates_indices.clone()) + { + messages.push(ApprovalDistributionMessage::DistributeApproval( + IndirectSignedApprovalVoteV2 { + block_hash, + candidate_indices: approval_sig.signed_candidates_indices, + validator: assignment.validator_index(), + signature: approval_sig.signature, + }, + )) + }; }, } }, @@ -1496,7 +1535,7 @@ fn distribution_messages_for_activation( } messages[0] = ApprovalDistributionMessage::NewBlocks(approval_meta); - Ok(messages) + Ok((messages, actions)) } // Handle an incoming signal from the overseer. Returns true if execution should conclude. @@ -1811,7 +1850,7 @@ async fn get_approval_signatures_for_candidate( gum::trace!( target: LOG_TARGET, ?candidate_hash, - "Spawning task for fetching sinatures from approval-distribution" + "Spawning task for fetching signatures from approval-distribution" ); ctx.spawn("get-approval-signatures", Box::pin(get_approvals)) } diff --git a/polkadot/node/core/approval-voting/src/persisted_entries.rs b/polkadot/node/core/approval-voting/src/persisted_entries.rs index ef47bdb2213a153dc7223c1018ba8ec9b341a5aa..6eeb99cb99ffa017492dba93e72556eab9104c8e 100644 --- a/polkadot/node/core/approval-voting/src/persisted_entries.rs +++ b/polkadot/node/core/approval-voting/src/persisted_entries.rs @@ -454,7 +454,7 @@ pub struct BlockEntry { slot: Slot, relay_vrf_story: RelayVRFStory, // The candidates included as-of this block and the index of the core they are - // leaving. Sorted ascending by core index. + // leaving. candidates: Vec<(CoreIndex, CandidateHash)>, // A bitfield where the i'th bit corresponds to the i'th candidate in `candidates`. // The i'th bit is `true` iff the candidate has been approved in the context of this @@ -588,6 +588,13 @@ impl BlockEntry { !self.candidates_pending_signature.is_empty() } + /// Returns true if candidate hash is in the queue for a signature. + pub fn candidate_is_pending_signature(&self, candidate_hash: CandidateHash) -> bool { + self.candidates_pending_signature + .values() + .any(|context| context.candidate_hash == candidate_hash) + } + /// Candidate hashes for candidates pending signatures fn candidate_hashes_pending_signature(&self) -> Vec { self.candidates_pending_signature diff --git a/polkadot/node/core/approval-voting/src/tests.rs b/polkadot/node/core/approval-voting/src/tests.rs index 9220e84a2554a48c14468567e87e74319ce94151..1483af5658537782cbe93deaa51149fecb5bd856 100644 --- a/polkadot/node/core/approval-voting/src/tests.rs +++ b/polkadot/node/core/approval-voting/src/tests.rs @@ -78,6 +78,7 @@ struct TestSyncOracle { struct TestSyncOracleHandle { done_syncing_receiver: oneshot::Receiver<()>, + is_major_syncing: Arc, } impl TestSyncOracleHandle { @@ -108,8 +109,9 @@ impl SyncOracle for TestSyncOracle { fn make_sync_oracle(val: bool) -> (Box, TestSyncOracleHandle) { let (tx, rx) = oneshot::channel(); let flag = Arc::new(AtomicBool::new(val)); - let oracle = TestSyncOracle { flag, done_syncing_sender: Arc::new(Mutex::new(Some(tx))) }; - let handle = TestSyncOracleHandle { done_syncing_receiver: rx }; + let oracle = + TestSyncOracle { flag: flag.clone(), done_syncing_sender: Arc::new(Mutex::new(Some(tx))) }; + let handle = TestSyncOracleHandle { done_syncing_receiver: rx, is_major_syncing: flag }; (Box::new(oracle), handle) } @@ -465,6 +467,7 @@ struct HarnessConfigBuilder { clock: Option, backend: Option, assignment_criteria: Option>, + major_syncing: bool, } impl HarnessConfigBuilder { @@ -476,9 +479,19 @@ impl HarnessConfigBuilder { self } + pub fn major_syncing(&mut self, value: bool) -> &mut Self { + self.major_syncing = value; + self + } + + pub fn backend(&mut self, store: TestStore) -> &mut Self { + self.backend = Some(store); + self + } + pub fn build(&mut self) -> HarnessConfig { let (sync_oracle, sync_oracle_handle) = - self.sync_oracle.take().unwrap_or_else(|| make_sync_oracle(false)); + self.sync_oracle.take().unwrap_or_else(|| make_sync_oracle(self.major_syncing)); let assignment_criteria = self .assignment_criteria @@ -736,11 +749,13 @@ struct BlockConfig { slot: Slot, candidates: Option>, session_info: Option, + end_syncing: bool, } struct ChainBuilder { blocks_by_hash: HashMap, blocks_at_height: BTreeMap>, + is_major_syncing: Arc, } impl ChainBuilder { @@ -748,16 +763,28 @@ impl ChainBuilder { const GENESIS_PARENT_HASH: Hash = Hash::repeat_byte(0x00); pub fn new() -> Self { - let mut builder = - Self { blocks_by_hash: HashMap::new(), blocks_at_height: BTreeMap::new() }; + let mut builder = Self { + blocks_by_hash: HashMap::new(), + blocks_at_height: BTreeMap::new(), + is_major_syncing: Arc::new(AtomicBool::new(false)), + }; builder.add_block_inner( Self::GENESIS_HASH, Self::GENESIS_PARENT_HASH, 0, - BlockConfig { slot: Slot::from(0), candidates: None, session_info: None }, + BlockConfig { + slot: Slot::from(0), + candidates: None, + session_info: None, + end_syncing: false, + }, ); builder } + pub fn major_syncing(&mut self, major_syncing: Arc) -> &mut Self { + self.is_major_syncing = major_syncing; + self + } pub fn add_block( &mut self, @@ -808,8 +835,16 @@ impl ChainBuilder { } ancestry.reverse(); - import_block(overseer, ancestry.as_ref(), *number, block_config, false, i > 0) - .await; + import_block( + overseer, + ancestry.as_ref(), + *number, + block_config, + false, + i > 0, + self.is_major_syncing.clone(), + ) + .await; let _: Option<()> = future::pending().timeout(Duration::from_millis(100)).await; } } @@ -863,6 +898,7 @@ async fn import_block( config: &BlockConfig, gap: bool, fork: bool, + major_syncing: Arc, ) { let (new_head, new_header) = &hashes[hashes.len() - 1]; let candidates = config.candidates.clone().unwrap_or(vec![( @@ -891,6 +927,12 @@ async fn import_block( h_tx.send(Ok(Some(new_header.clone()))).unwrap(); } ); + + let is_major_syncing = major_syncing.load(Ordering::SeqCst); + if config.end_syncing { + major_syncing.store(false, Ordering::SeqCst); + } + if !fork { let mut _ancestry_step = 0; if gap { @@ -931,7 +973,7 @@ async fn import_block( } } - if number > 0 { + if number > 0 && !is_major_syncing { assert_matches!( overseer_recv(overseer).await, AllMessages::RuntimeApi( @@ -944,7 +986,6 @@ async fn import_block( c_tx.send(Ok(inclusion_events)).unwrap(); } ); - assert_matches!( overseer_recv(overseer).await, AllMessages::RuntimeApi( @@ -984,14 +1025,14 @@ async fn import_block( ); } - if number == 0 { + if number == 0 && !is_major_syncing { assert_matches!( overseer_recv(overseer).await, AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NewBlocks(v)) => { assert_eq!(v.len(), 0usize); } ); - } else { + } else if number > 0 && !is_major_syncing { if !fork { // SessionInfo won't be called for forks - it's already cached assert_matches!( @@ -1031,20 +1072,23 @@ async fn import_block( ); } - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalDistribution( - ApprovalDistributionMessage::NewBlocks(mut approval_vec) - ) => { - assert_eq!(approval_vec.len(), 1); - let metadata = approval_vec.pop().unwrap(); - let hash = &hashes[number as usize]; - let parent_hash = &hashes[(number - 1) as usize]; - assert_eq!(metadata.hash, hash.0.clone()); - assert_eq!(metadata.parent_hash, parent_hash.0.clone()); - assert_eq!(metadata.slot, config.slot); - } - ); + if !is_major_syncing { + assert_matches!( + overseer_recv(overseer).await, + + AllMessages::ApprovalDistribution( + ApprovalDistributionMessage::NewBlocks(mut approval_vec) + ) => { + assert_eq!(approval_vec.len(), 1); + let metadata = approval_vec.pop().unwrap(); + let hash = &hashes[number as usize]; + let parent_hash = &hashes[(number - 1) as usize]; + assert_eq!(metadata.hash, hash.0.clone()); + assert_eq!(metadata.parent_hash, parent_hash.0.clone()); + assert_eq!(metadata.slot, config.slot); + } + ); + } } } @@ -1072,7 +1116,7 @@ fn subsystem_rejects_bad_assignment_ok_criteria() { block_hash, head, 1, - BlockConfig { slot, candidates: None, session_info: None }, + BlockConfig { slot, candidates: None, session_info: None, end_syncing: false }, ); builder.build(&mut virtual_overseer).await; @@ -1135,7 +1179,7 @@ fn subsystem_rejects_bad_assignment_err_criteria() { block_hash, head, 1, - BlockConfig { slot, candidates: None, session_info: None }, + BlockConfig { slot, candidates: None, session_info: None, end_syncing: false }, ); builder.build(&mut virtual_overseer).await; @@ -1240,6 +1284,7 @@ fn subsystem_rejects_approval_if_no_candidate_entry() { slot, candidates: Some(vec![(candidate_descriptor, CoreIndex(1), GroupIndex(1))]), session_info: None, + end_syncing: false, }, ); builder.build(&mut virtual_overseer).await; @@ -1345,7 +1390,12 @@ fn subsystem_rejects_approval_before_assignment() { block_hash, ChainBuilder::GENESIS_HASH, 1, - BlockConfig { slot: Slot::from(1), candidates: None, session_info: None }, + BlockConfig { + slot: Slot::from(1), + candidates: None, + session_info: None, + end_syncing: false, + }, ) .build(&mut virtual_overseer) .await; @@ -1398,7 +1448,12 @@ fn subsystem_rejects_assignment_in_future() { block_hash, ChainBuilder::GENESIS_HASH, 1, - BlockConfig { slot: Slot::from(0), candidates: None, session_info: None }, + BlockConfig { + slot: Slot::from(0), + candidates: None, + session_info: None, + end_syncing: false, + }, ) .build(&mut virtual_overseer) .await; @@ -1472,6 +1527,7 @@ fn subsystem_accepts_duplicate_assignment() { (candidate_receipt2, CoreIndex(1), GroupIndex(1)), ]), session_info: None, + end_syncing: false, }, ) .build(&mut virtual_overseer) @@ -1537,7 +1593,12 @@ fn subsystem_rejects_assignment_with_unknown_candidate() { block_hash, ChainBuilder::GENESIS_HASH, 1, - BlockConfig { slot: Slot::from(1), candidates: None, session_info: None }, + BlockConfig { + slot: Slot::from(1), + candidates: None, + session_info: None, + end_syncing: false, + }, ) .build(&mut virtual_overseer) .await; @@ -1582,7 +1643,12 @@ fn subsystem_rejects_oversized_bitfields() { block_hash, ChainBuilder::GENESIS_HASH, 1, - BlockConfig { slot: Slot::from(1), candidates: None, session_info: None }, + BlockConfig { + slot: Slot::from(1), + candidates: None, + session_info: None, + end_syncing: false, + }, ) .build(&mut virtual_overseer) .await; @@ -1650,7 +1716,12 @@ fn subsystem_accepts_and_imports_approval_after_assignment() { block_hash, ChainBuilder::GENESIS_HASH, 1, - BlockConfig { slot: Slot::from(1), candidates: None, session_info: None }, + BlockConfig { + slot: Slot::from(1), + candidates: None, + session_info: None, + end_syncing: false, + }, ) .build(&mut virtual_overseer) .await; @@ -1741,6 +1812,7 @@ fn subsystem_second_approval_import_only_schedules_wakeups() { slot: Slot::from(0), candidates: None, session_info: Some(session_info), + end_syncing: false, }, ) .build(&mut virtual_overseer) @@ -1824,7 +1896,12 @@ fn subsystem_assignment_import_updates_candidate_entry_and_schedules_wakeup() { block_hash, ChainBuilder::GENESIS_HASH, 1, - BlockConfig { slot: Slot::from(1), candidates: None, session_info: None }, + BlockConfig { + slot: Slot::from(1), + candidates: None, + session_info: None, + end_syncing: false, + }, ) .build(&mut virtual_overseer) .await; @@ -1873,7 +1950,12 @@ fn subsystem_process_wakeup_schedules_wakeup() { block_hash, ChainBuilder::GENESIS_HASH, 1, - BlockConfig { slot: Slot::from(1), candidates: None, session_info: None }, + BlockConfig { + slot: Slot::from(1), + candidates: None, + session_info: None, + end_syncing: false, + }, ) .build(&mut virtual_overseer) .await; @@ -1925,7 +2007,7 @@ fn linear_import_act_on_leaf() { hash, head, i, - BlockConfig { slot, candidates: None, session_info: None }, + BlockConfig { slot, candidates: None, session_info: None, end_syncing: false }, ); head = hash; } @@ -1983,7 +2065,7 @@ fn forkful_import_at_same_height_act_on_leaf() { hash, head, i, - BlockConfig { slot, candidates: None, session_info: None }, + BlockConfig { slot, candidates: None, session_info: None, end_syncing: false }, ); head = hash; } @@ -1997,7 +2079,7 @@ fn forkful_import_at_same_height_act_on_leaf() { hash, head, session, - BlockConfig { slot, candidates: None, session_info: None }, + BlockConfig { slot, candidates: None, session_info: None, end_syncing: false }, ); } builder.build(&mut virtual_overseer).await; @@ -2168,6 +2250,7 @@ fn import_checked_approval_updates_entries_and_schedules() { slot, candidates: Some(vec![(candidate_descriptor, CoreIndex(0), GroupIndex(0))]), session_info: Some(session_info), + end_syncing: false, }, ); builder.build(&mut virtual_overseer).await; @@ -2323,6 +2406,7 @@ fn subsystem_import_checked_approval_sets_one_block_bit_at_a_time() { (candidate_receipt2, CoreIndex(1), GroupIndex(1)), ]), session_info: Some(session_info), + end_syncing: false, }, ) .build(&mut virtual_overseer) @@ -2395,6 +2479,173 @@ fn subsystem_import_checked_approval_sets_one_block_bit_at_a_time() { }); } +// See https://github.com/paritytech/polkadot-sdk/issues/3826 +#[test] +fn inclusion_events_can_be_unordered_by_core_index() { + let assignment_criteria = Box::new(MockAssignmentCriteria( + || { + let mut assignments = HashMap::new(); + for core in 0..3 { + let _ = assignments.insert( + CoreIndex(core), + approval_db::v2::OurAssignment { + cert: garbage_assignment_cert_v2( + AssignmentCertKindV2::RelayVRFModuloCompact { + core_bitfield: vec![CoreIndex(0), CoreIndex(1), CoreIndex(2)] + .try_into() + .unwrap(), + }, + ), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + } + assignments + }, + |_| Ok(0), + )); + let config = HarnessConfigBuilder::default().assignment_criteria(assignment_criteria).build(); + let store = config.backend(); + + test_harness(config, |test_harness| async move { + let TestHarness { + mut virtual_overseer, + clock, + sync_oracle_handle: _sync_oracle_handle, + .. + } = test_harness; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ChainApi(ChainApiMessage::FinalizedBlockNumber(rx)) => { + rx.send(Ok(0)).unwrap(); + } + ); + + let block_hash = Hash::repeat_byte(0x01); + + let candidate_receipt0 = { + let mut receipt = dummy_candidate_receipt(block_hash); + receipt.descriptor.para_id = ParaId::from(0_u32); + receipt + }; + let candidate_receipt1 = { + let mut receipt = dummy_candidate_receipt(block_hash); + receipt.descriptor.para_id = ParaId::from(1_u32); + receipt + }; + let candidate_receipt2 = { + let mut receipt = dummy_candidate_receipt(block_hash); + receipt.descriptor.para_id = ParaId::from(2_u32); + receipt + }; + let candidate_index0 = 0; + let candidate_index1 = 1; + let candidate_index2 = 2; + + let validator0 = ValidatorIndex(0); + let validator1 = ValidatorIndex(1); + let validator2 = ValidatorIndex(2); + let validator3 = ValidatorIndex(3); + + let validators = vec![ + Sr25519Keyring::Alice, + Sr25519Keyring::Bob, + Sr25519Keyring::Charlie, + Sr25519Keyring::Dave, + Sr25519Keyring::Eve, + ]; + let session_info = SessionInfo { + validator_groups: IndexedVec::>::from(vec![ + vec![validator0, validator1], + vec![validator2], + vec![validator3], + ]), + needed_approvals: 1, + zeroth_delay_tranche_width: 1, + relay_vrf_modulo_samples: 1, + n_delay_tranches: 1, + no_show_slots: 1, + ..session_info(&validators) + }; + + ChainBuilder::new() + .add_block( + block_hash, + ChainBuilder::GENESIS_HASH, + 1, + BlockConfig { + slot: Slot::from(0), + candidates: Some(vec![ + (candidate_receipt0.clone(), CoreIndex(2), GroupIndex(2)), + (candidate_receipt1.clone(), CoreIndex(1), GroupIndex(0)), + (candidate_receipt2.clone(), CoreIndex(0), GroupIndex(1)), + ]), + session_info: Some(session_info), + end_syncing: true, + }, + ) + .build(&mut virtual_overseer) + .await; + + assert_eq!(clock.inner.lock().next_wakeup().unwrap(), 2); + clock.inner.lock().wakeup_all(100); + + assert_eq!(clock.inner.lock().wakeups.len(), 0); + + futures_timer::Delay::new(Duration::from_millis(100)).await; + + // Assignment is distributed only once from `approval-voting` + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + c_indices, + )) => { + assert_eq!(c_indices, vec![candidate_index0, candidate_index1, candidate_index2].try_into().unwrap()); + } + ); + + // Candidate 0 + recover_available_data(&mut virtual_overseer).await; + fetch_validation_code(&mut virtual_overseer).await; + + // Candidate 1 + recover_available_data(&mut virtual_overseer).await; + fetch_validation_code(&mut virtual_overseer).await; + + // Candidate 2 + recover_available_data(&mut virtual_overseer).await; + fetch_validation_code(&mut virtual_overseer).await; + + // Check if assignment was triggered for candidate 0. + let candidate_entry = + store.load_candidate_entry(&candidate_receipt0.hash()).unwrap().unwrap(); + let our_assignment = + candidate_entry.approval_entry(&block_hash).unwrap().our_assignment().unwrap(); + assert!(our_assignment.triggered()); + + // Check if assignment was triggered for candidate 1. + let candidate_entry = + store.load_candidate_entry(&candidate_receipt1.hash()).unwrap().unwrap(); + let our_assignment = + candidate_entry.approval_entry(&block_hash).unwrap().our_assignment().unwrap(); + assert!(our_assignment.triggered()); + + // Check if assignment was triggered for candidate 2. + let candidate_entry = + store.load_candidate_entry(&candidate_receipt2.hash()).unwrap().unwrap(); + let our_assignment = + candidate_entry.approval_entry(&block_hash).unwrap().our_assignment().unwrap(); + assert!(our_assignment.triggered()); + + virtual_overseer + }); +} + fn approved_ancestor_test( skip_approval: impl Fn(BlockNumber) -> bool, approved_height: BlockNumber, @@ -2445,6 +2696,7 @@ fn approved_ancestor_test( slot: Slot::from(i as u64), candidates: Some(vec![(candidate_receipt, CoreIndex(0), GroupIndex(0))]), session_info: None, + end_syncing: false, }, ); } @@ -2623,13 +2875,19 @@ fn subsystem_validate_approvals_cache() { slot, candidates: candidates.clone(), session_info: Some(session_info.clone()), + end_syncing: false, }, ) .add_block( fork_block_hash, ChainBuilder::GENESIS_HASH, 1, - BlockConfig { slot, candidates, session_info: Some(session_info) }, + BlockConfig { + slot, + candidates, + session_info: Some(session_info), + end_syncing: false, + }, ) .build(&mut virtual_overseer) .await; @@ -2740,6 +2998,7 @@ fn subsystem_doesnt_distribute_duplicate_compact_assignments() { (candidate_receipt2.clone(), CoreIndex(1), GroupIndex(1)), ]), session_info: None, + end_syncing: false, }, ) .build(&mut virtual_overseer) @@ -2997,6 +3256,7 @@ where slot, candidates: Some(vec![(candidate_receipt, CoreIndex(0), GroupIndex(2))]), session_info: Some(session_info), + end_syncing: false, }, ) .build(&mut virtual_overseer) @@ -3061,7 +3321,7 @@ where // starting configuration. The relevant ticks (all scheduled wakeups) are printed after no further // ticks are scheduled. To create a valid test, a prefix of the relevant ticks should be included // in the final test configuration, ending at the tick with the desired inputs to -// should_trigger_assignemnt. +// should_trigger_assignment. async fn step_until_done(clock: &MockClock) { let mut relevant_ticks = Vec::new(); loop { @@ -3314,6 +3574,7 @@ fn pre_covers_dont_stall_approval() { slot, candidates: Some(vec![(candidate_descriptor, CoreIndex(0), GroupIndex(0))]), session_info: Some(session_info), + end_syncing: false, }, ); builder.build(&mut virtual_overseer).await; @@ -3491,6 +3752,7 @@ fn waits_until_approving_assignments_are_old_enough() { slot, candidates: Some(vec![(candidate_descriptor, CoreIndex(0), GroupIndex(0))]), session_info: Some(session_info), + end_syncing: false, }, ); builder.build(&mut virtual_overseer).await; @@ -3705,6 +3967,7 @@ fn test_approval_is_sent_on_max_approval_coalesce_count() { slot, candidates: candidates.clone(), session_info: Some(session_info.clone()), + end_syncing: false, }, ) .build(&mut virtual_overseer) @@ -3741,7 +4004,7 @@ fn test_approval_is_sent_on_max_approval_coalesce_count() { async fn handle_approval_on_max_coalesce_count( virtual_overseer: &mut VirtualOverseer, - candidate_indicies: Vec, + candidate_indices: Vec, ) { assert_matches!( overseer_recv(virtual_overseer).await, @@ -3749,16 +4012,16 @@ async fn handle_approval_on_max_coalesce_count( _, c_indices, )) => { - assert_eq!(TryInto::::try_into(candidate_indicies.clone()).unwrap(), c_indices); + assert_eq!(TryInto::::try_into(candidate_indices.clone()).unwrap(), c_indices); } ); - for _ in &candidate_indicies { + for _ in &candidate_indices { recover_available_data(virtual_overseer).await; fetch_validation_code(virtual_overseer).await; } - for _ in &candidate_indicies { + for _ in &candidate_indices { assert_matches!( overseer_recv(virtual_overseer).await, AllMessages::CandidateValidation(CandidateValidationMessage::ValidateFromExhaustive{exec_kind, response_sender, ..}) if exec_kind == PvfExecKind::Approval => { @@ -3789,7 +4052,7 @@ async fn handle_approval_on_max_coalesce_count( assert_matches!( overseer_recv(virtual_overseer).await, AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeApproval(vote)) => { - assert_eq!(TryInto::::try_into(candidate_indicies).unwrap(), vote.candidate_indices); + assert_eq!(TryInto::::try_into(candidate_indices).unwrap(), vote.candidate_indices); } ); @@ -3799,7 +4062,7 @@ async fn handle_approval_on_max_coalesce_count( async fn handle_approval_on_max_wait_time( virtual_overseer: &mut VirtualOverseer, - candidate_indicies: Vec, + candidate_indices: Vec, clock: Box, ) { const TICK_NOW_BEGIN: u64 = 1; @@ -3813,16 +4076,16 @@ async fn handle_approval_on_max_wait_time( _, c_indices, )) => { - assert_eq!(TryInto::::try_into(candidate_indicies.clone()).unwrap(), c_indices); + assert_eq!(TryInto::::try_into(candidate_indices.clone()).unwrap(), c_indices); } ); - for _ in &candidate_indicies { + for _ in &candidate_indices { recover_available_data(virtual_overseer).await; fetch_validation_code(virtual_overseer).await; } - for _ in &candidate_indicies { + for _ in &candidate_indices { assert_matches!( overseer_recv(virtual_overseer).await, AllMessages::CandidateValidation(CandidateValidationMessage::ValidateFromExhaustive{exec_kind, response_sender, ..}) if exec_kind == PvfExecKind::Approval => { @@ -3882,7 +4145,7 @@ async fn handle_approval_on_max_wait_time( assert_matches!( overseer_recv(virtual_overseer).await, AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeApproval(vote)) => { - assert_eq!(TryInto::::try_into(candidate_indicies).unwrap(), vote.candidate_indices); + assert_eq!(TryInto::::try_into(candidate_indices).unwrap(), vote.candidate_indices); } ); @@ -3943,8 +4206,7 @@ fn test_approval_is_sent_on_max_approval_coalesce_wait() { let store = config.backend(); test_harness(config, |test_harness| async move { - let TestHarness { mut virtual_overseer, clock, sync_oracle_handle: _sync_oracle_handle } = - test_harness; + let TestHarness { mut virtual_overseer, clock, sync_oracle_handle: _ } = test_harness; assert_matches!( overseer_recv(&mut virtual_overseer).await, @@ -4006,6 +4268,7 @@ fn test_approval_is_sent_on_max_approval_coalesce_wait() { slot, candidates: candidates.clone(), session_info: Some(session_info.clone()), + end_syncing: false, }, ) .build(&mut virtual_overseer) @@ -4040,3 +4303,569 @@ fn test_approval_is_sent_on_max_approval_coalesce_wait() { virtual_overseer }); } + +// Builds a chain with a fork where both relay blocks include the same candidate. +async fn build_chain_with_two_blocks_with_one_candidate_each( + block_hash1: Hash, + block_hash2: Hash, + slot: Slot, + sync_oracle_handle: TestSyncOracleHandle, + candidate_receipt: CandidateReceipt, +) -> (ChainBuilder, SessionInfo) { + let validators = vec![ + Sr25519Keyring::Alice, + Sr25519Keyring::Bob, + Sr25519Keyring::Charlie, + Sr25519Keyring::Dave, + Sr25519Keyring::Eve, + ]; + let session_info = SessionInfo { + validator_groups: IndexedVec::>::from(vec![ + vec![ValidatorIndex(0), ValidatorIndex(1)], + vec![ValidatorIndex(2)], + vec![ValidatorIndex(3), ValidatorIndex(4)], + ]), + ..session_info(&validators) + }; + + let candidates = Some(vec![(candidate_receipt.clone(), CoreIndex(0), GroupIndex(0))]); + let mut chain_builder = ChainBuilder::new(); + + chain_builder + .major_syncing(sync_oracle_handle.is_major_syncing.clone()) + .add_block( + block_hash1, + ChainBuilder::GENESIS_HASH, + 1, + BlockConfig { + slot, + candidates: candidates.clone(), + session_info: Some(session_info.clone()), + end_syncing: false, + }, + ) + .add_block( + block_hash2, + ChainBuilder::GENESIS_HASH, + 1, + BlockConfig { + slot, + candidates, + session_info: Some(session_info.clone()), + end_syncing: true, + }, + ); + (chain_builder, session_info) +} + +async fn setup_overseer_with_two_blocks_each_with_one_assignment_triggered( + virtual_overseer: &mut VirtualOverseer, + store: TestStore, + clock: &Box, + sync_oracle_handle: TestSyncOracleHandle, +) { + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::ChainApi(ChainApiMessage::FinalizedBlockNumber(rx)) => { + rx.send(Ok(0)).unwrap(); + } + ); + + let block_hash = Hash::repeat_byte(0x01); + let fork_block_hash = Hash::repeat_byte(0x02); + let candidate_commitments = CandidateCommitments::default(); + let mut candidate_receipt = dummy_candidate_receipt(block_hash); + candidate_receipt.commitments_hash = candidate_commitments.hash(); + let candidate_hash = candidate_receipt.hash(); + let slot = Slot::from(1); + let (chain_builder, _session_info) = build_chain_with_two_blocks_with_one_candidate_each( + block_hash, + fork_block_hash, + slot, + sync_oracle_handle, + candidate_receipt, + ) + .await; + chain_builder.build(virtual_overseer).await; + + assert!(!clock.inner.lock().current_wakeup_is(1)); + clock.inner.lock().wakeup_all(1); + + assert!(clock.inner.lock().current_wakeup_is(slot_to_tick(slot))); + clock.inner.lock().wakeup_all(slot_to_tick(slot)); + + futures_timer::Delay::new(Duration::from_millis(200)).await; + + clock.inner.lock().wakeup_all(slot_to_tick(slot + 2)); + + assert_eq!(clock.inner.lock().wakeups.len(), 0); + + futures_timer::Delay::new(Duration::from_millis(200)).await; + + let candidate_entry = store.load_candidate_entry(&candidate_hash).unwrap().unwrap(); + let our_assignment = + candidate_entry.approval_entry(&block_hash).unwrap().our_assignment().unwrap(); + assert!(our_assignment.triggered()); +} + +// Tests that for candidates that we did not approve yet, for which we triggered the assignment and +// the approval work we restart the work to approve it. +#[test] +fn subsystem_relaunches_approval_work_on_restart() { + let assignment_criteria = Box::new(MockAssignmentCriteria( + || { + let mut assignments = HashMap::new(); + let _ = assignments.insert( + CoreIndex(0), + approval_db::v2::OurAssignment { + cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0 }) + .into(), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + + let _ = assignments.insert( + CoreIndex(0), + approval_db::v2::OurAssignment { + cert: garbage_assignment_cert_v2(AssignmentCertKindV2::RelayVRFModuloCompact { + core_bitfield: vec![CoreIndex(0), CoreIndex(1), CoreIndex(2)] + .try_into() + .unwrap(), + }), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + assignments + }, + |_| Ok(0), + )); + let config = HarnessConfigBuilder::default().assignment_criteria(assignment_criteria).build(); + let store = config.backend(); + let store_clone = config.backend(); + + test_harness(config, |test_harness| async move { + let TestHarness { mut virtual_overseer, clock, sync_oracle_handle } = test_harness; + + setup_overseer_with_two_blocks_each_with_one_assignment_triggered( + &mut virtual_overseer, + store, + &clock, + sync_oracle_handle, + ) + .await; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + _, + )) => { + } + ); + + recover_available_data(&mut virtual_overseer).await; + fetch_validation_code(&mut virtual_overseer).await; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + _ + )) => { + } + ); + + // Bail early after the assignment has been distributed but before we answer with the mocked + // approval from CandidateValidation. + virtual_overseer + }); + + // Restart a new approval voting subsystem with the same database and major syncing true until + // the last leaf. + let config = HarnessConfigBuilder::default().backend(store_clone).major_syncing(true).build(); + + test_harness(config, |test_harness| async move { + let TestHarness { mut virtual_overseer, clock, sync_oracle_handle } = test_harness; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ChainApi(ChainApiMessage::FinalizedBlockNumber(rx)) => { + rx.send(Ok(0)).unwrap(); + } + ); + + let block_hash = Hash::repeat_byte(0x01); + let fork_block_hash = Hash::repeat_byte(0x02); + let candidate_commitments = CandidateCommitments::default(); + let mut candidate_receipt = dummy_candidate_receipt(block_hash); + candidate_receipt.commitments_hash = candidate_commitments.hash(); + let slot = Slot::from(1); + clock.inner.lock().set_tick(slot_to_tick(slot + 2)); + let (chain_builder, session_info) = build_chain_with_two_blocks_with_one_candidate_each( + block_hash, + fork_block_hash, + slot, + sync_oracle_handle, + candidate_receipt, + ) + .await; + + chain_builder.build(&mut virtual_overseer).await; + + futures_timer::Delay::new(Duration::from_millis(2000)).await; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request( + _, + RuntimeApiRequest::SessionInfo(_, si_tx), + ) + ) => { + si_tx.send(Ok(Some(session_info.clone()))).unwrap(); + } + ); + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request( + _, + RuntimeApiRequest::SessionExecutorParams(_, si_tx), + ) + ) => { + // Make sure all SessionExecutorParams calls are not made for the leaf (but for its relay parent) + si_tx.send(Ok(Some(ExecutorParams::default()))).unwrap(); + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request(_, RuntimeApiRequest::NodeFeatures(_, si_tx), ) + ) => { + si_tx.send(Ok(NodeFeatures::EMPTY)).unwrap(); + } + ); + + // On major syncing ending Approval voting should send all the necessary messages for a + // candidate to be approved. + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NewBlocks( + _, + )) => { + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + _, + )) => { + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + _, + )) => { + } + ); + + // Guarantees the approval work has been relaunched. + recover_available_data(&mut virtual_overseer).await; + fetch_validation_code(&mut virtual_overseer).await; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::CandidateValidation(CandidateValidationMessage::ValidateFromExhaustive { + exec_kind, + response_sender, + .. + }) if exec_kind == PvfExecKind::Approval => { + response_sender.send(Ok(ValidationResult::Valid(Default::default(), Default::default()))) + .unwrap(); + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(_, sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: 1, + })); + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeApproval(_)) + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(_, sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: 1, + })); + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeApproval(_)) + ); + + // Assert that there are no more messages being sent by the subsystem + assert!(overseer_recv(&mut virtual_overseer).timeout(TIMEOUT / 2).await.is_none()); + + virtual_overseer + }); +} + +// Test that cached approvals, which are candidates that we approved but we didn't issue +// the signature yet because we want to coalesce it with more candidate are sent after restart. +#[test] +fn subsystem_sends_pending_approvals_on_approval_restart() { + let assignment_criteria = Box::new(MockAssignmentCriteria( + || { + let mut assignments = HashMap::new(); + let _ = assignments.insert( + CoreIndex(0), + approval_db::v2::OurAssignment { + cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0 }) + .into(), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + + let _ = assignments.insert( + CoreIndex(0), + approval_db::v2::OurAssignment { + cert: garbage_assignment_cert_v2(AssignmentCertKindV2::RelayVRFModuloCompact { + core_bitfield: vec![CoreIndex(0), CoreIndex(1), CoreIndex(2)] + .try_into() + .unwrap(), + }), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + assignments + }, + |_| Ok(0), + )); + let config = HarnessConfigBuilder::default().assignment_criteria(assignment_criteria).build(); + let store = config.backend(); + let store_clone = config.backend(); + + test_harness(config, |test_harness| async move { + let TestHarness { mut virtual_overseer, clock, sync_oracle_handle } = test_harness; + + setup_overseer_with_two_blocks_each_with_one_assignment_triggered( + &mut virtual_overseer, + store, + &clock, + sync_oracle_handle, + ) + .await; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + _, + )) => { + } + ); + + recover_available_data(&mut virtual_overseer).await; + fetch_validation_code(&mut virtual_overseer).await; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + _ + )) => { + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::CandidateValidation(CandidateValidationMessage::ValidateFromExhaustive { + exec_kind, + response_sender, + .. + }) if exec_kind == PvfExecKind::Approval => { + response_sender.send(Ok(ValidationResult::Valid(Default::default(), Default::default()))) + .unwrap(); + } + ); + + // Configure a big coalesce number, so that the signature is cached instead of being sent to + // approval-distribution. + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(_, sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: 6, + })); + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(_, sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: 6, + })); + } + ); + + // Assert that there are no more messages being sent by the subsystem + assert!(overseer_recv(&mut virtual_overseer).timeout(TIMEOUT / 2).await.is_none()); + + virtual_overseer + }); + + let config = HarnessConfigBuilder::default().backend(store_clone).major_syncing(true).build(); + // On restart signatures should be sent to approval-distribution without relaunching the + // approval work. + test_harness(config, |test_harness| async move { + let TestHarness { mut virtual_overseer, clock, sync_oracle_handle } = test_harness; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ChainApi(ChainApiMessage::FinalizedBlockNumber(rx)) => { + rx.send(Ok(0)).unwrap(); + } + ); + + let block_hash = Hash::repeat_byte(0x01); + let fork_block_hash = Hash::repeat_byte(0x02); + let candidate_commitments = CandidateCommitments::default(); + let mut candidate_receipt = dummy_candidate_receipt(block_hash); + candidate_receipt.commitments_hash = candidate_commitments.hash(); + let slot = Slot::from(1); + + clock.inner.lock().set_tick(slot_to_tick(slot + 2)); + let (chain_builder, session_info) = build_chain_with_two_blocks_with_one_candidate_each( + block_hash, + fork_block_hash, + slot, + sync_oracle_handle, + candidate_receipt, + ) + .await; + chain_builder.build(&mut virtual_overseer).await; + + futures_timer::Delay::new(Duration::from_millis(2000)).await; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NewBlocks( + _, + )) => { + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + _, + )) => { + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + _, + )) => { + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(_, sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: 1, + })); + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request( + _, + RuntimeApiRequest::SessionInfo(_, si_tx), + ) + ) => { + si_tx.send(Ok(Some(session_info.clone()))).unwrap(); + } + ); + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request( + _, + RuntimeApiRequest::SessionExecutorParams(_, si_tx), + ) + ) => { + // Make sure all SessionExecutorParams calls are not made for the leaf (but for its relay parent) + si_tx.send(Ok(Some(ExecutorParams::default()))).unwrap(); + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request(_, RuntimeApiRequest::NodeFeatures(_, si_tx), ) + ) => { + si_tx.send(Ok(NodeFeatures::EMPTY)).unwrap(); + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeApproval(_)) + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(_, sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: 1, + })); + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeApproval(_)) + ); + + // Assert that there are no more messages being sent by the subsystem + assert!(overseer_recv(&mut virtual_overseer).timeout(TIMEOUT / 2).await.is_none()); + + virtual_overseer + }); +} diff --git a/polkadot/node/core/approval-voting/src/time.rs b/polkadot/node/core/approval-voting/src/time.rs index 99dfbe07678f2f0956565e92ffaf196b6f482af9..5c3e7e85a17a212eeae2e8b6b9b7cf6dbecde036 100644 --- a/polkadot/node/core/approval-voting/src/time.rs +++ b/polkadot/node/core/approval-voting/src/time.rs @@ -126,13 +126,13 @@ impl DelayedApprovalTimer { /// no additional timer is started. pub(crate) fn maybe_arm_timer( &mut self, - wait_untill: Tick, + wait_until: Tick, clock: &dyn Clock, block_hash: Hash, validator_index: ValidatorIndex, ) { if self.blocks.insert(block_hash) { - let clock_wait = clock.wait(wait_untill); + let clock_wait = clock.wait(wait_until); self.timers.push(Box::pin(async move { clock_wait.await; (block_hash, validator_index) diff --git a/polkadot/node/core/av-store/src/lib.rs b/polkadot/node/core/av-store/src/lib.rs index ef7dcecac0755084efbba839d12b532ff0f336cb..68db4686a9740bb052f7d05031d148cd5b05e0a2 100644 --- a/polkadot/node/core/av-store/src/lib.rs +++ b/polkadot/node/core/av-store/src/lib.rs @@ -1218,7 +1218,7 @@ fn process_message( // tx channel is dropped and that error is caught by the caller subsystem. // // We bubble up the specific error here so `av-store` logs still tell what - // happend. + // happened. return Err(e.into()) }, } diff --git a/polkadot/node/core/backing/Cargo.toml b/polkadot/node/core/backing/Cargo.toml index b0cf041e38da9a8a73fd140652da14a1a79cedea..d0c1f9aa4832dfe629e1fb20d3082f27c260a7d5 100644 --- a/polkadot/node/core/backing/Cargo.toml +++ b/polkadot/node/core/backing/Cargo.toml @@ -22,6 +22,7 @@ bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] } gum = { package = "tracing-gum", path = "../../gum" } thiserror = { workspace = true } fatality = "0.0.6" +schnellru = "0.2.1" [dev-dependencies] sp-core = { path = "../../../../substrate/primitives/core" } @@ -31,5 +32,6 @@ sc-keystore = { path = "../../../../substrate/client/keystore" } sp-tracing = { path = "../../../../substrate/primitives/tracing" } futures = { version = "0.3.21", features = ["thread-pool"] } assert_matches = "1.4.0" +rstest = "0.18.2" polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers" } test-helpers = { package = "polkadot-primitives-test-helpers", path = "../../../primitives/test-helpers" } diff --git a/polkadot/node/core/backing/src/error.rs b/polkadot/node/core/backing/src/error.rs index 1b00a62510b7c634b3135a56ca6412c0948ef6a0..52684f3fe3063e95dcab48947cf86ca62c321372 100644 --- a/polkadot/node/core/backing/src/error.rs +++ b/polkadot/node/core/backing/src/error.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . +use std::collections::HashMap; + use fatality::Nested; use futures::channel::{mpsc, oneshot}; @@ -24,7 +26,7 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_util::{runtime, Error as UtilError}; use polkadot_primitives::{BackedCandidate, ValidationCodeHash}; -use crate::LOG_TARGET; +use crate::{ParaId, LOG_TARGET}; pub type Result = std::result::Result; pub type FatalResult = std::result::Result; @@ -48,11 +50,14 @@ pub enum Error { #[error("Candidate is not found")] CandidateNotFound, + #[error("CoreIndex cannot be determined for a candidate")] + CoreIndexUnavailable, + #[error("Signature is invalid")] InvalidSignature, #[error("Failed to send candidates {0:?}")] - Send(Vec), + Send(HashMap>), #[error("FetchPoV failed")] FetchPoV, diff --git a/polkadot/node/core/backing/src/lib.rs b/polkadot/node/core/backing/src/lib.rs index 98bbd6232add4d893293fc948c0a99bba8d46bf3..4b6beb5592e10fe8eef56cbb5958540ff334bd9c 100644 --- a/polkadot/node/core/backing/src/lib.rs +++ b/polkadot/node/core/backing/src/lib.rs @@ -77,6 +77,7 @@ use futures::{ stream::FuturesOrdered, FutureExt, SinkExt, StreamExt, TryFutureExt, }; +use schnellru::{ByLength, LruMap}; use error::{Error, FatalResult}; use polkadot_node_primitives::{ @@ -104,10 +105,12 @@ use polkadot_node_subsystem_util::{ Validator, }; use polkadot_primitives::{ + vstaging::{node_features::FeatureIndex, NodeFeatures}, BackedCandidate, CandidateCommitments, CandidateHash, CandidateReceipt, - CommittedCandidateReceipt, CoreIndex, CoreState, ExecutorParams, Hash, Id as ParaId, - PersistedValidationData, PvfExecKind, SigningContext, ValidationCode, ValidatorId, - ValidatorIndex, ValidatorSignature, ValidityAttestation, + CommittedCandidateReceipt, CoreIndex, CoreState, ExecutorParams, GroupIndex, GroupRotationInfo, + Hash, Id as ParaId, IndexedVec, PersistedValidationData, PvfExecKind, SessionIndex, + SigningContext, ValidationCode, ValidatorId, ValidatorIndex, ValidatorSignature, + ValidityAttestation, }; use sp_keystore::KeystorePtr; use statement_table::{ @@ -118,7 +121,7 @@ use statement_table::{ }, Config as TableConfig, Context as TableContextTrait, Table, }; -use util::vstaging::get_disabled_validators_with_fallback; +use util::{runtime::request_node_features, vstaging::get_disabled_validators_with_fallback}; mod error; @@ -209,7 +212,9 @@ struct PerRelayParentState { /// The hash of the relay parent on top of which this job is doing it's work. parent: Hash, /// The `ParaId` assigned to the local validator at this relay parent. - assignment: Option, + assigned_para: Option, + /// The `CoreIndex` assigned to the local validator at this relay parent. + assigned_core: Option, /// The candidates that are backed by enough validators in their group, by hash. backed: HashSet, /// The table of candidates and statements under this relay-parent. @@ -224,6 +229,15 @@ struct PerRelayParentState { fallbacks: HashMap, /// The minimum backing votes threshold. minimum_backing_votes: u32, + /// If true, we're appending extra bits in the BackedCandidate validator indices bitfield, + /// which represent the assigned core index. True if ElasticScalingMVP is enabled. + inject_core_index: bool, + /// The core states for all cores. + cores: Vec, + /// The validator index -> group mapping at this relay parent. + validator_to_group: Arc>>, + /// The associated group rotation information. + group_rotation_info: GroupRotationInfo, } struct PerCandidateState { @@ -275,7 +289,10 @@ struct State { /// This is guaranteed to have an entry for each candidate with a relay parent in the implicit /// or explicit view for which a `Seconded` statement has been successfully imported. per_candidate: HashMap, - /// A cloneable sender which is dispatched to background candidate validation tasks to inform + /// Cache the per-session Validator->Group mapping. + validator_to_group_cache: + LruMap>>>, + /// A clonable sender which is dispatched to background candidate validation tasks to inform /// the main task of the result. background_validation_tx: mpsc::Sender<(Hash, ValidatedCandidateCommand)>, /// The handle to the keystore used for signing. @@ -292,6 +309,7 @@ impl State { per_leaf: HashMap::default(), per_relay_parent: HashMap::default(), per_candidate: HashMap::new(), + validator_to_group_cache: LruMap::new(ByLength::new(2)), background_validation_tx, keystore, } @@ -379,10 +397,10 @@ struct AttestingData { backing: Vec, } -#[derive(Default)] +#[derive(Default, Debug)] struct TableContext { validator: Option, - groups: HashMap>, + groups: HashMap>, validators: Vec, disabled_validators: Vec, } @@ -404,7 +422,7 @@ impl TableContext { impl TableContextTrait for TableContext { type AuthorityId = ValidatorIndex; type Digest = CandidateHash; - type GroupId = ParaId; + type GroupId = CoreIndex; type Signature = ValidatorSignature; type Candidate = CommittedCandidateReceipt; @@ -412,15 +430,11 @@ impl TableContextTrait for TableContext { candidate.hash() } - fn candidate_group(candidate: &CommittedCandidateReceipt) -> ParaId { - candidate.descriptor().para_id + fn is_member_of(&self, authority: &ValidatorIndex, core: &CoreIndex) -> bool { + self.groups.get(core).map_or(false, |g| g.iter().any(|a| a == authority)) } - fn is_member_of(&self, authority: &ValidatorIndex, group: &ParaId) -> bool { - self.groups.get(group).map_or(false, |g| g.iter().any(|a| a == authority)) - } - - fn get_group_size(&self, group: &ParaId) -> Option { + fn get_group_size(&self, group: &CoreIndex) -> Option { self.groups.get(group).map(|g| g.len()) } } @@ -442,19 +456,20 @@ fn primitive_statement_to_table(s: &SignedFullStatementWithPVD) -> TableSignedSt fn table_attested_to_backed( attested: TableAttestedCandidate< - ParaId, + CoreIndex, CommittedCandidateReceipt, ValidatorIndex, ValidatorSignature, >, table_context: &TableContext, + inject_core_index: bool, ) -> Option { - let TableAttestedCandidate { candidate, validity_votes, group_id: para_id } = attested; + let TableAttestedCandidate { candidate, validity_votes, group_id: core_index } = attested; let (ids, validity_votes): (Vec<_>, Vec) = validity_votes.into_iter().map(|(id, vote)| (id, vote.into())).unzip(); - let group = table_context.groups.get(¶_id)?; + let group = table_context.groups.get(&core_index)?; let mut validator_indices = BitVec::with_capacity(group.len()); @@ -479,14 +494,15 @@ fn table_attested_to_backed( } vote_positions.sort_by_key(|(_orig, pos_in_group)| *pos_in_group); - Some(BackedCandidate { + Some(BackedCandidate::new( candidate, - validity_votes: vote_positions + vote_positions .into_iter() .map(|(pos_in_votes, _pos_in_group)| validity_votes[pos_in_votes].clone()) .collect(), validator_indices, - }) + inject_core_index.then_some(core_index), + )) } async fn store_available_data( @@ -971,7 +987,14 @@ async fn handle_active_leaves_update( // construct a `PerRelayParent` from the runtime API // and insert it. - let per = construct_per_relay_parent_state(ctx, maybe_new, &state.keystore, mode).await?; + let per = construct_per_relay_parent_state( + ctx, + maybe_new, + &state.keystore, + &mut state.validator_to_group_cache, + mode, + ) + .await?; if let Some(per) = per { state.per_relay_parent.insert(maybe_new, per); @@ -981,31 +1004,112 @@ async fn handle_active_leaves_update( Ok(()) } +macro_rules! try_runtime_api { + ($x: expr) => { + match $x { + Ok(x) => x, + Err(err) => { + // Only bubble up fatal errors. + error::log_error(Err(Into::::into(err).into()))?; + + // We can't do candidate validation work if we don't have the + // requisite runtime API data. But these errors should not take + // down the node. + return Ok(None) + }, + } + }; +} + +fn core_index_from_statement( + validator_to_group: &IndexedVec>, + group_rotation_info: &GroupRotationInfo, + cores: &[CoreState], + statement: &SignedFullStatementWithPVD, +) -> Option { + let compact_statement = statement.as_unchecked(); + let candidate_hash = CandidateHash(*compact_statement.unchecked_payload().candidate_hash()); + + let n_cores = cores.len(); + + gum::trace!( + target:LOG_TARGET, + ?group_rotation_info, + ?statement, + ?validator_to_group, + n_cores = ?cores.len(), + ?candidate_hash, + "Extracting core index from statement" + ); + + let statement_validator_index = statement.validator_index(); + let Some(Some(group_index)) = validator_to_group.get(statement_validator_index) else { + gum::debug!( + target: LOG_TARGET, + ?group_rotation_info, + ?statement, + ?validator_to_group, + n_cores = ?cores.len() , + ?candidate_hash, + "Invalid validator index: {:?}", + statement_validator_index + ); + return None + }; + + // First check if the statement para id matches the core assignment. + let core_index = group_rotation_info.core_for_group(*group_index, n_cores); + + if core_index.0 as usize > n_cores { + gum::warn!(target: LOG_TARGET, ?candidate_hash, ?core_index, n_cores, "Invalid CoreIndex"); + return None + } + + if let StatementWithPVD::Seconded(candidate, _pvd) = statement.payload() { + let candidate_para_id = candidate.descriptor.para_id; + let assigned_para_id = match &cores[core_index.0 as usize] { + CoreState::Free => { + gum::debug!(target: LOG_TARGET, ?candidate_hash, "Invalid CoreIndex, core is not assigned to any para_id"); + return None + }, + CoreState::Occupied(occupied) => + if let Some(next) = &occupied.next_up_on_available { + next.para_id + } else { + return None + }, + CoreState::Scheduled(scheduled) => scheduled.para_id, + }; + + if assigned_para_id != candidate_para_id { + gum::debug!( + target: LOG_TARGET, + ?candidate_hash, + ?core_index, + ?assigned_para_id, + ?candidate_para_id, + "Invalid CoreIndex, core is assigned to a different para_id" + ); + return None + } + return Some(core_index) + } else { + return Some(core_index) + } +} + /// Load the data necessary to do backing work on top of a relay-parent. #[overseer::contextbounds(CandidateBacking, prefix = self::overseer)] async fn construct_per_relay_parent_state( ctx: &mut Context, relay_parent: Hash, keystore: &KeystorePtr, + validator_to_group_cache: &mut LruMap< + SessionIndex, + Arc>>, + >, mode: ProspectiveParachainsMode, ) -> Result, Error> { - macro_rules! try_runtime_api { - ($x: expr) => { - match $x { - Ok(x) => x, - Err(err) => { - // Only bubble up fatal errors. - error::log_error(Err(Into::::into(err).into()))?; - - // We can't do candidate validation work if we don't have the - // requisite runtime API data. But these errors should not take - // down the node. - return Ok(None) - }, - } - }; - } - let parent = relay_parent; let (session_index, validators, groups, cores) = futures::try_join!( @@ -1020,6 +1124,16 @@ async fn construct_per_relay_parent_state( .map_err(Error::JoinMultiple)?; let session_index = try_runtime_api!(session_index); + + let inject_core_index = request_node_features(parent, session_index, ctx.sender()) + .await? + .unwrap_or(NodeFeatures::EMPTY) + .get(FeatureIndex::ElasticScalingMVP as usize) + .map(|b| *b) + .unwrap_or(false); + + gum::debug!(target: LOG_TARGET, inject_core_index, ?parent, "New state"); + let validators: Vec<_> = try_runtime_api!(validators); let (validator_groups, group_rotation_info) = try_runtime_api!(groups); let cores = try_runtime_api!(cores); @@ -1055,18 +1169,24 @@ async fn construct_per_relay_parent_state( }, }; - let mut groups = HashMap::new(); let n_cores = cores.len(); - let mut assignment = None; - for (idx, core) in cores.into_iter().enumerate() { + let mut groups = HashMap::>::new(); + let mut assigned_core = None; + let mut assigned_para = None; + + for (idx, core) in cores.iter().enumerate() { let core_para_id = match core { CoreState::Scheduled(scheduled) => scheduled.para_id, CoreState::Occupied(occupied) => if mode.is_enabled() { // Async backing makes it legal to build on top of // occupied core. - occupied.candidate_descriptor.para_id + if let Some(next) = &occupied.next_up_on_available { + next.para_id + } else { + continue + } } else { continue }, @@ -1077,11 +1197,27 @@ async fn construct_per_relay_parent_state( let group_index = group_rotation_info.group_for_core(core_index, n_cores); if let Some(g) = validator_groups.get(group_index.0 as usize) { if validator.as_ref().map_or(false, |v| g.contains(&v.index())) { - assignment = Some(core_para_id); + assigned_para = Some(core_para_id); + assigned_core = Some(core_index); } - groups.insert(core_para_id, g.clone()); + groups.insert(core_index, g.clone()); } } + gum::debug!(target: LOG_TARGET, ?groups, "TableContext"); + + let validator_to_group = validator_to_group_cache + .get_or_insert(session_index, || { + let mut vector = vec![None; validators.len()]; + + for (group_idx, validator_group) in validator_groups.iter().enumerate() { + for validator in validator_group { + vector[validator.0 as usize] = Some(GroupIndex(group_idx as u32)); + } + } + + Arc::new(IndexedVec::<_, _>::from(vector)) + }) + .expect("Just inserted"); let table_context = TableContext { validator, groups, validators, disabled_validators }; let table_config = TableConfig { @@ -1094,7 +1230,8 @@ async fn construct_per_relay_parent_state( Ok(Some(PerRelayParentState { prospective_parachains_mode: mode, parent, - assignment, + assigned_core, + assigned_para, backed: HashSet::new(), table: Table::new(table_config), table_context, @@ -1102,6 +1239,10 @@ async fn construct_per_relay_parent_state( awaiting_validation: HashSet::new(), fallbacks: HashMap::new(), minimum_backing_votes, + inject_core_index, + cores, + validator_to_group: validator_to_group.clone(), + group_rotation_info, })) } @@ -1519,15 +1660,16 @@ async fn import_statement( per_candidate: &mut HashMap, statement: &SignedFullStatementWithPVD, ) -> Result, Error> { + let candidate_hash = statement.payload().candidate_hash(); + gum::debug!( target: LOG_TARGET, statement = ?statement.payload().to_compact(), validator_index = statement.validator_index().0, + ?candidate_hash, "Importing statement", ); - let candidate_hash = statement.payload().candidate_hash(); - // If this is a new candidate (statement is 'seconded' and candidate is unknown), // we need to create an entry in the `PerCandidateState` map. // @@ -1593,7 +1735,15 @@ async fn import_statement( let stmt = primitive_statement_to_table(statement); - Ok(rp_state.table.import_statement(&rp_state.table_context, stmt)) + let core = core_index_from_statement( + &rp_state.validator_to_group, + &rp_state.group_rotation_info, + &rp_state.cores, + statement, + ) + .ok_or(Error::CoreIndexUnavailable)?; + + Ok(rp_state.table.import_statement(&rp_state.table_context, core, stmt)) } /// Handles a summary received from [`import_statement`] and dispatches `Backed` notifications and @@ -1615,8 +1765,12 @@ async fn post_import_statement_actions( // `HashSet::insert` returns true if the thing wasn't in there already. if rp_state.backed.insert(candidate_hash) { - if let Some(backed) = table_attested_to_backed(attested, &rp_state.table_context) { - let para_id = backed.candidate.descriptor.para_id; + if let Some(backed) = table_attested_to_backed( + attested, + &rp_state.table_context, + rp_state.inject_core_index, + ) { + let para_id = backed.candidate().descriptor.para_id; gum::debug!( target: LOG_TARGET, candidate_hash = ?candidate_hash, @@ -1637,7 +1791,7 @@ async fn post_import_statement_actions( // notify collator protocol. ctx.send_message(CollatorProtocolMessage::Backed { para_id, - para_head: backed.candidate.descriptor.para_head, + para_head: backed.candidate().descriptor.para_head, }) .await; // Notify statement distribution of backed candidate. @@ -1654,8 +1808,14 @@ async fn post_import_statement_actions( ); ctx.send_unbounded_message(message); } + } else { + gum::debug!(target: LOG_TARGET, ?candidate_hash, "Cannot get BackedCandidate"); } + } else { + gum::debug!(target: LOG_TARGET, ?candidate_hash, "Candidate already known"); } + } else { + gum::debug!(target: LOG_TARGET, "No attested candidate"); } issue_new_misbehaviors(ctx, rp_state.parent, &mut rp_state.table); @@ -1722,18 +1882,20 @@ async fn background_validate_and_make_available( if rp_state.awaiting_validation.insert(candidate_hash) { // spawn background task. let bg = async move { - if let Err(e) = validate_and_make_available(params).await { - if let Error::BackgroundValidationMpsc(error) = e { + if let Err(error) = validate_and_make_available(params).await { + if let Error::BackgroundValidationMpsc(error) = error { gum::debug!( target: LOG_TARGET, + ?candidate_hash, ?error, "Mpsc background validation mpsc died during validation- leaf no longer active?" ); } else { gum::error!( target: LOG_TARGET, - "Failed to validate and make available: {:?}", - e + ?candidate_hash, + ?error, + "Failed to validate and make available", ); } } @@ -1859,9 +2021,10 @@ async fn maybe_validate_and_import( let candidate_hash = summary.candidate; - if Some(summary.group_id) != rp_state.assignment { + if Some(summary.group_id) != rp_state.assigned_core { return Ok(()) } + let attesting = match statement.payload() { StatementWithPVD::Seconded(receipt, _) => { let attesting = AttestingData { @@ -2004,10 +2167,11 @@ async fn handle_second_message( } // Sanity check that candidate is from our assignment. - if Some(candidate.descriptor().para_id) != rp_state.assignment { + if Some(candidate.descriptor().para_id) != rp_state.assigned_para { gum::debug!( target: LOG_TARGET, - our_assignment = ?rp_state.assignment, + our_assignment_core = ?rp_state.assigned_core, + our_assignment_para = ?rp_state.assigned_para, collation = ?candidate.descriptor().para_id, "Subsystem asked to second for para outside of our assignment", ); @@ -2015,6 +2179,14 @@ async fn handle_second_message( return Ok(()) } + gum::debug!( + target: LOG_TARGET, + our_assignment_core = ?rp_state.assigned_core, + our_assignment_para = ?rp_state.assigned_para, + collation = ?candidate.descriptor().para_id, + "Current assignments vs collation", + ); + // If the message is a `CandidateBackingMessage::Second`, sign and dispatch a // Seconded statement only if we have not signed a Valid statement for the requested candidate. // @@ -2059,15 +2231,16 @@ async fn handle_statement_message( fn handle_get_backed_candidates_message( state: &State, - requested_candidates: Vec<(CandidateHash, Hash)>, - tx: oneshot::Sender>, + requested_candidates: HashMap>, + tx: oneshot::Sender>>, metrics: &Metrics, ) -> Result<(), Error> { let _timer = metrics.time_get_backed_candidates(); - let backed = requested_candidates - .into_iter() - .filter_map(|(candidate_hash, relay_parent)| { + let mut backed = HashMap::with_capacity(requested_candidates.len()); + + for (para_id, para_candidates) in requested_candidates { + for (candidate_hash, relay_parent) in para_candidates.iter() { let rp_state = match state.per_relay_parent.get(&relay_parent) { Some(rp_state) => rp_state, None => { @@ -2077,19 +2250,34 @@ fn handle_get_backed_candidates_message( ?candidate_hash, "Requested candidate's relay parent is out of view", ); - return None + break }, }; - rp_state + let maybe_backed_candidate = rp_state .table .attested_candidate( - &candidate_hash, + candidate_hash, &rp_state.table_context, rp_state.minimum_backing_votes, ) - .and_then(|attested| table_attested_to_backed(attested, &rp_state.table_context)) - }) - .collect(); + .and_then(|attested| { + table_attested_to_backed( + attested, + &rp_state.table_context, + rp_state.inject_core_index, + ) + }); + + if let Some(backed_candidate) = maybe_backed_candidate { + backed + .entry(para_id) + .or_insert_with(|| Vec::with_capacity(para_candidates.len())) + .push(backed_candidate); + } else { + break + } + } + } tx.send(backed).map_err(|data| Error::Send(data))?; Ok(()) diff --git a/polkadot/node/core/backing/src/tests/mod.rs b/polkadot/node/core/backing/src/tests/mod.rs index 1957f4e19c54bd9845e5bb5bd03383985d9c9636..fdb47581ea3a94fc8c80624ca4359f09f40dd9a6 100644 --- a/polkadot/node/core/backing/src/tests/mod.rs +++ b/polkadot/node/core/backing/src/tests/mod.rs @@ -33,9 +33,10 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_primitives::{ - CandidateDescriptor, GroupRotationInfo, HeadData, PersistedValidationData, PvfExecKind, - ScheduledCore, SessionIndex, LEGACY_MIN_BACKING_VOTES, + vstaging::node_features, CandidateDescriptor, GroupRotationInfo, HeadData, + PersistedValidationData, PvfExecKind, ScheduledCore, SessionIndex, LEGACY_MIN_BACKING_VOTES, }; +use rstest::rstest; use sp_application_crypto::AppCrypto; use sp_keyring::Sr25519Keyring; use sp_keystore::Keystore; @@ -65,19 +66,21 @@ fn dummy_pvd() -> PersistedValidationData { } } -struct TestState { +pub(crate) struct TestState { chain_ids: Vec, keystore: KeystorePtr, validators: Vec, validator_public: Vec, validation_data: PersistedValidationData, validator_groups: (Vec>, GroupRotationInfo), + validator_to_group: IndexedVec>, availability_cores: Vec, head_data: HashMap, signing_context: SigningContext, relay_parent: Hash, minimum_backing_votes: u32, disabled_validators: Vec, + node_features: NodeFeatures, } impl TestState { @@ -114,6 +117,11 @@ impl Default for TestState { .into_iter() .map(|g| g.into_iter().map(ValidatorIndex).collect()) .collect(); + let validator_to_group: IndexedVec<_, _> = + vec![Some(0), Some(1), Some(0), Some(0), None, Some(0)] + .into_iter() + .map(|x| x.map(|x| GroupIndex(x))) + .collect(); let group_rotation_info = GroupRotationInfo { session_start_block: 0, group_rotation_frequency: 100, now: 1 }; @@ -143,6 +151,7 @@ impl Default for TestState { validators, validator_public, validator_groups: (validator_groups, group_rotation_info), + validator_to_group, availability_cores, head_data, validation_data, @@ -150,6 +159,7 @@ impl Default for TestState { relay_parent, minimum_backing_votes: LEGACY_MIN_BACKING_VOTES, disabled_validators: Vec::new(), + node_features: Default::default(), } } } @@ -285,6 +295,16 @@ async fn test_startup(virtual_overseer: &mut VirtualOverseer, test_state: &TestS } ); + // Node features request from runtime: all features are disabled. + assert_matches!( + virtual_overseer.recv().await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request(_parent, RuntimeApiRequest::NodeFeatures(_session_index, tx)) + ) => { + tx.send(Ok(test_state.node_features.clone())).unwrap(); + } + ); + // Check if subsystem job issues a request for the minimum backing votes. assert_matches!( virtual_overseer.recv().await, @@ -477,9 +497,20 @@ fn backing_second_works() { } // Test that the candidate reaches quorum successfully. -#[test] -fn backing_works() { - let test_state = TestState::default(); +#[rstest] +#[case(true)] +#[case(false)] +fn backing_works(#[case] elastic_scaling_mvp: bool) { + let mut test_state = TestState::default(); + if elastic_scaling_mvp { + test_state + .node_features + .resize((node_features::FeatureIndex::ElasticScalingMVP as u8 + 1) as usize, false); + test_state + .node_features + .set(node_features::FeatureIndex::ElasticScalingMVP as u8 as usize, true); + } + test_harness(test_state.keystore.clone(), |mut virtual_overseer| async move { test_startup(&mut virtual_overseer, &test_state).await; @@ -630,6 +661,354 @@ fn backing_works() { virtual_overseer.send(FromOrchestra::Communication { msg: statement }).await; + let (tx, rx) = oneshot::channel(); + let msg = CandidateBackingMessage::GetBackedCandidates( + std::iter::once(( + test_state.chain_ids[0], + vec![(candidate_a_hash, test_state.relay_parent)], + )) + .collect(), + tx, + ); + + virtual_overseer.send(FromOrchestra::Communication { msg }).await; + + let mut candidates = rx.await.unwrap(); + assert_eq!(1, candidates.len()); + let candidates = candidates.remove(&test_state.chain_ids[0]).unwrap(); + assert_eq!(1, candidates.len()); + assert_eq!(candidates[0].validity_votes().len(), 3); + + let (validator_indices, maybe_core_index) = + candidates[0].validator_indices_and_core_index(elastic_scaling_mvp); + if elastic_scaling_mvp { + assert_eq!(maybe_core_index.unwrap(), CoreIndex(0)); + } else { + assert!(maybe_core_index.is_none()); + } + + assert_eq!( + validator_indices, + bitvec::bitvec![u8, bitvec::order::Lsb0; 1, 1, 0, 1].as_bitslice() + ); + + virtual_overseer + .send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves( + ActiveLeavesUpdate::stop_work(test_state.relay_parent), + ))) + .await; + virtual_overseer + }); +} + +#[test] +fn get_backed_candidate_preserves_order() { + let mut test_state = TestState::default(); + test_state + .node_features + .resize((node_features::FeatureIndex::ElasticScalingMVP as u8 + 1) as usize, false); + test_state + .node_features + .set(node_features::FeatureIndex::ElasticScalingMVP as u8 as usize, true); + + // Set a single validator as the first validator group. It simplifies the test. + test_state.validator_groups.0[0] = vec![ValidatorIndex(2)]; + // Add another validator group for the third core. + test_state.validator_groups.0.push(vec![ValidatorIndex(3)]); + // Assign the second core to the same para as the first one. + test_state.availability_cores[1] = + CoreState::Scheduled(ScheduledCore { para_id: test_state.chain_ids[0], collator: None }); + // Add another availability core for paraid 2. + test_state.availability_cores.push(CoreState::Scheduled(ScheduledCore { + para_id: test_state.chain_ids[1], + collator: None, + })); + + test_harness(test_state.keystore.clone(), |mut virtual_overseer| async move { + test_startup(&mut virtual_overseer, &test_state).await; + + let pov_a = PoV { block_data: BlockData(vec![1, 2, 3]) }; + let pov_b = PoV { block_data: BlockData(vec![3, 4, 5]) }; + let pov_c = PoV { block_data: BlockData(vec![5, 6, 7]) }; + let validation_code_ab = ValidationCode(vec![1, 2, 3]); + let validation_code_c = ValidationCode(vec![4, 5, 6]); + + let parent_head_data_a = test_state.head_data.get(&test_state.chain_ids[0]).unwrap(); + let parent_head_data_b = { + let mut head = parent_head_data_a.clone(); + head.0[0] = 98; + head + }; + let output_head_data_b = { + let mut head = parent_head_data_a.clone(); + head.0[0] = 99; + head + }; + let parent_head_data_c = test_state.head_data.get(&test_state.chain_ids[1]).unwrap(); + let output_head_data_c = { + let mut head = parent_head_data_c.clone(); + head.0[0] = 97; + head + }; + + let pvd_a = PersistedValidationData { + parent_head: parent_head_data_a.clone(), + relay_parent_number: 0_u32.into(), + max_pov_size: 1024, + relay_parent_storage_root: dummy_hash(), + }; + let pvd_b = PersistedValidationData { + parent_head: parent_head_data_b.clone(), + relay_parent_number: 0_u32.into(), + max_pov_size: 1024, + relay_parent_storage_root: dummy_hash(), + }; + let pvd_c = PersistedValidationData { + parent_head: parent_head_data_c.clone(), + relay_parent_number: 0_u32.into(), + max_pov_size: 1024, + relay_parent_storage_root: dummy_hash(), + }; + + let candidate_a = TestCandidateBuilder { + para_id: test_state.chain_ids[0], + relay_parent: test_state.relay_parent, + pov_hash: pov_a.hash(), + head_data: parent_head_data_b.clone(), + erasure_root: make_erasure_root(&test_state, pov_a.clone(), pvd_a.clone()), + validation_code: validation_code_ab.0.clone(), + persisted_validation_data_hash: pvd_a.hash(), + } + .build(); + let candidate_b = TestCandidateBuilder { + para_id: test_state.chain_ids[0], + relay_parent: test_state.relay_parent, + pov_hash: pov_b.hash(), + head_data: output_head_data_b.clone(), + erasure_root: make_erasure_root(&test_state, pov_b.clone(), pvd_b.clone()), + validation_code: validation_code_ab.0.clone(), + persisted_validation_data_hash: pvd_b.hash(), + } + .build(); + let candidate_c = TestCandidateBuilder { + para_id: test_state.chain_ids[1], + relay_parent: test_state.relay_parent, + pov_hash: pov_c.hash(), + head_data: output_head_data_c.clone(), + erasure_root: make_erasure_root(&test_state, pov_b.clone(), pvd_c.clone()), + validation_code: validation_code_c.0.clone(), + persisted_validation_data_hash: pvd_c.hash(), + } + .build(); + let candidate_a_hash = candidate_a.hash(); + let candidate_b_hash = candidate_b.hash(); + let candidate_c_hash = candidate_c.hash(); + + // Back a chain of two candidates for the first paraid. Back one candidate for the second + // paraid. + for (candidate, pvd, validator_index) in [ + (candidate_a, pvd_a, ValidatorIndex(2)), + (candidate_b, pvd_b, ValidatorIndex(1)), + (candidate_c, pvd_c, ValidatorIndex(3)), + ] { + let public = Keystore::sr25519_generate_new( + &*test_state.keystore, + ValidatorId::ID, + Some(&test_state.validators[validator_index.0 as usize].to_seed()), + ) + .expect("Insert key into keystore"); + + let signed = SignedFullStatementWithPVD::sign( + &test_state.keystore, + StatementWithPVD::Seconded(candidate.clone(), pvd.clone()), + &test_state.signing_context, + validator_index, + &public.into(), + ) + .ok() + .flatten() + .expect("should be signed"); + + let statement = + CandidateBackingMessage::Statement(test_state.relay_parent, signed.clone()); + + virtual_overseer.send(FromOrchestra::Communication { msg: statement }).await; + + assert_matches!( + virtual_overseer.recv().await, + AllMessages::Provisioner( + ProvisionerMessage::ProvisionableData( + _, + ProvisionableData::BackedCandidate(candidate_receipt) + ) + ) => { + assert_eq!(candidate_receipt, candidate.to_plain()); + } + ); + } + + // Happy case, all candidates should be present. + let (tx, rx) = oneshot::channel(); + let msg = CandidateBackingMessage::GetBackedCandidates( + [ + ( + test_state.chain_ids[0], + vec![ + (candidate_a_hash, test_state.relay_parent), + (candidate_b_hash, test_state.relay_parent), + ], + ), + (test_state.chain_ids[1], vec![(candidate_c_hash, test_state.relay_parent)]), + ] + .into_iter() + .collect(), + tx, + ); + virtual_overseer.send(FromOrchestra::Communication { msg }).await; + let mut candidates = rx.await.unwrap(); + assert_eq!(2, candidates.len()); + assert_eq!( + candidates + .remove(&test_state.chain_ids[0]) + .unwrap() + .iter() + .map(|c| c.hash()) + .collect::>(), + vec![candidate_a_hash, candidate_b_hash] + ); + assert_eq!( + candidates + .remove(&test_state.chain_ids[1]) + .unwrap() + .iter() + .map(|c| c.hash()) + .collect::>(), + vec![candidate_c_hash] + ); + + // The first candidate of the first para is invalid (we supply the wrong relay parent or a + // wrong candidate hash). No candidates should be returned for paraid 1. ParaId 2 should be + // fine. + for candidates in [ + vec![ + (candidate_a_hash, Hash::repeat_byte(9)), + (candidate_b_hash, test_state.relay_parent), + ], + vec![ + (CandidateHash(Hash::repeat_byte(9)), test_state.relay_parent), + (candidate_b_hash, test_state.relay_parent), + ], + ] { + let (tx, rx) = oneshot::channel(); + let msg = CandidateBackingMessage::GetBackedCandidates( + [ + (test_state.chain_ids[0], candidates), + (test_state.chain_ids[1], vec![(candidate_c_hash, test_state.relay_parent)]), + ] + .into_iter() + .collect(), + tx, + ); + virtual_overseer.send(FromOrchestra::Communication { msg }).await; + let mut candidates = rx.await.unwrap(); + assert_eq!(candidates.len(), 1); + + assert!(candidates.remove(&test_state.chain_ids[0]).is_none()); + assert_eq!( + candidates + .remove(&test_state.chain_ids[1]) + .unwrap() + .iter() + .map(|c| c.hash()) + .collect::>(), + vec![candidate_c_hash] + ); + } + + // The second candidate of the first para is invalid (we supply the wrong relay parent or a + // wrong candidate hash). The first candidate of the first para should still be present. + // ParaId 2 is fine. + for candidates in [ + vec![ + (candidate_a_hash, test_state.relay_parent), + (candidate_b_hash, Hash::repeat_byte(9)), + ], + vec![ + (candidate_a_hash, test_state.relay_parent), + (CandidateHash(Hash::repeat_byte(9)), test_state.relay_parent), + ], + ] { + let (tx, rx) = oneshot::channel(); + let msg = CandidateBackingMessage::GetBackedCandidates( + [ + (test_state.chain_ids[0], candidates), + (test_state.chain_ids[1], vec![(candidate_c_hash, test_state.relay_parent)]), + ] + .into_iter() + .collect(), + tx, + ); + virtual_overseer.send(FromOrchestra::Communication { msg }).await; + let mut candidates = rx.await.unwrap(); + assert_eq!(2, candidates.len()); + assert_eq!( + candidates + .remove(&test_state.chain_ids[0]) + .unwrap() + .iter() + .map(|c| c.hash()) + .collect::>(), + vec![candidate_a_hash] + ); + assert_eq!( + candidates + .remove(&test_state.chain_ids[1]) + .unwrap() + .iter() + .map(|c| c.hash()) + .collect::>(), + vec![candidate_c_hash] + ); + } + + // Both candidates of para id 1 are invalid (we supply the wrong relay parent or a wrong + // candidate hash). No candidates should be returned for para id 1. Para Id 2 is fine. + for candidates in [ + vec![ + (CandidateHash(Hash::repeat_byte(9)), test_state.relay_parent), + (CandidateHash(Hash::repeat_byte(10)), test_state.relay_parent), + ], + vec![ + (candidate_a_hash, Hash::repeat_byte(9)), + (candidate_b_hash, Hash::repeat_byte(10)), + ], + ] { + let (tx, rx) = oneshot::channel(); + let msg = CandidateBackingMessage::GetBackedCandidates( + [ + (test_state.chain_ids[0], candidates), + (test_state.chain_ids[1], vec![(candidate_c_hash, test_state.relay_parent)]), + ] + .into_iter() + .collect(), + tx, + ); + virtual_overseer.send(FromOrchestra::Communication { msg }).await; + let mut candidates = rx.await.unwrap(); + assert_eq!(candidates.len(), 1); + + assert!(candidates.remove(&test_state.chain_ids[0]).is_none()); + assert_eq!( + candidates + .remove(&test_state.chain_ids[1]) + .unwrap() + .iter() + .map(|c| c.hash()) + .collect::>(), + vec![candidate_c_hash] + ); + } + virtual_overseer .send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves( ActiveLeavesUpdate::stop_work(test_state.relay_parent), @@ -639,6 +1018,107 @@ fn backing_works() { }); } +#[test] +fn extract_core_index_from_statement_works() { + let test_state = TestState::default(); + + let pov_a = PoV { block_data: BlockData(vec![42, 43, 44]) }; + let pvd_a = dummy_pvd(); + let validation_code_a = ValidationCode(vec![1, 2, 3]); + + let pov_hash = pov_a.hash(); + + let mut candidate = TestCandidateBuilder { + para_id: test_state.chain_ids[0], + relay_parent: test_state.relay_parent, + pov_hash, + erasure_root: make_erasure_root(&test_state, pov_a.clone(), pvd_a.clone()), + persisted_validation_data_hash: pvd_a.hash(), + validation_code: validation_code_a.0.clone(), + ..Default::default() + } + .build(); + + let public2 = Keystore::sr25519_generate_new( + &*test_state.keystore, + ValidatorId::ID, + Some(&test_state.validators[2].to_seed()), + ) + .expect("Insert key into keystore"); + + let signed_statement_1 = SignedFullStatementWithPVD::sign( + &test_state.keystore, + StatementWithPVD::Seconded(candidate.clone(), pvd_a.clone()), + &test_state.signing_context, + ValidatorIndex(2), + &public2.into(), + ) + .ok() + .flatten() + .expect("should be signed"); + + let public1 = Keystore::sr25519_generate_new( + &*test_state.keystore, + ValidatorId::ID, + Some(&test_state.validators[1].to_seed()), + ) + .expect("Insert key into keystore"); + + let signed_statement_2 = SignedFullStatementWithPVD::sign( + &test_state.keystore, + StatementWithPVD::Seconded(candidate.clone(), pvd_a.clone()), + &test_state.signing_context, + ValidatorIndex(1), + &public1.into(), + ) + .ok() + .flatten() + .expect("should be signed"); + + candidate.descriptor.para_id = test_state.chain_ids[1]; + + let signed_statement_3 = SignedFullStatementWithPVD::sign( + &test_state.keystore, + StatementWithPVD::Seconded(candidate, pvd_a.clone()), + &test_state.signing_context, + ValidatorIndex(1), + &public1.into(), + ) + .ok() + .flatten() + .expect("should be signed"); + + let core_index_1 = core_index_from_statement( + &test_state.validator_to_group, + &test_state.validator_groups.1, + &test_state.availability_cores, + &signed_statement_1, + ) + .unwrap(); + + assert_eq!(core_index_1, CoreIndex(0)); + + let core_index_2 = core_index_from_statement( + &test_state.validator_to_group, + &test_state.validator_groups.1, + &test_state.availability_cores, + &signed_statement_2, + ); + + // Must be none, para_id in descriptor is different than para assigned to core + assert_eq!(core_index_2, None); + + let core_index_3 = core_index_from_statement( + &test_state.validator_to_group, + &test_state.validator_groups.1, + &test_state.availability_cores, + &signed_statement_3, + ) + .unwrap(); + + assert_eq!(core_index_3, CoreIndex(1)); +} + #[test] fn backing_works_while_validation_ongoing() { let test_state = TestState::default(); @@ -793,28 +1273,34 @@ fn backing_works_while_validation_ongoing() { let (tx, rx) = oneshot::channel(); let msg = CandidateBackingMessage::GetBackedCandidates( - vec![(candidate_a.hash(), test_state.relay_parent)], + std::iter::once(( + test_state.chain_ids[0], + vec![(candidate_a.hash(), test_state.relay_parent)], + )) + .collect(), tx, ); virtual_overseer.send(FromOrchestra::Communication { msg }).await; - let candidates = rx.await.unwrap(); + let mut candidates = rx.await.unwrap(); + assert_eq!(candidates.len(), 1); + let candidates = candidates.remove(&test_state.chain_ids[0]).unwrap(); assert_eq!(1, candidates.len()); - assert_eq!(candidates[0].validity_votes.len(), 3); + assert_eq!(candidates[0].validity_votes().len(), 3); assert!(candidates[0] - .validity_votes + .validity_votes() .contains(&ValidityAttestation::Implicit(signed_a.signature().clone()))); assert!(candidates[0] - .validity_votes + .validity_votes() .contains(&ValidityAttestation::Explicit(signed_b.signature().clone()))); assert!(candidates[0] - .validity_votes + .validity_votes() .contains(&ValidityAttestation::Explicit(signed_c.signature().clone()))); assert_eq!( - candidates[0].validator_indices, - bitvec::bitvec![u8, bitvec::order::Lsb0; 1, 0, 1, 1], + candidates[0].validator_indices_and_core_index(false), + (bitvec::bitvec![u8, bitvec::order::Lsb0; 1, 0, 1, 1].as_bitslice(), None) ); virtual_overseer @@ -1408,7 +1894,11 @@ fn backing_works_after_failed_validation() { // and check that it is still alive. let (tx, rx) = oneshot::channel(); let msg = CandidateBackingMessage::GetBackedCandidates( - vec![(candidate.hash(), test_state.relay_parent)], + std::iter::once(( + test_state.chain_ids[0], + vec![(candidate.hash(), test_state.relay_parent)], + )) + .collect(), tx, ); @@ -1422,7 +1912,7 @@ fn backing_works_after_failed_validation() { fn candidate_backing_reorders_votes() { use sp_core::Encode; - let para_id = ParaId::from(10); + let core_idx = CoreIndex(10); let validators = vec![ Sr25519Keyring::Alice, Sr25519Keyring::Bob, @@ -1436,7 +1926,7 @@ fn candidate_backing_reorders_votes() { let validator_groups = { let mut validator_groups = HashMap::new(); validator_groups - .insert(para_id, vec![0, 1, 2, 3, 4, 5].into_iter().map(ValidatorIndex).collect()); + .insert(core_idx, vec![0, 1, 2, 3, 4, 5].into_iter().map(ValidatorIndex).collect()); validator_groups }; @@ -1466,10 +1956,10 @@ fn candidate_backing_reorders_votes() { (ValidatorIndex(3), fake_attestation(3)), (ValidatorIndex(1), fake_attestation(1)), ], - group_id: para_id, + group_id: core_idx, }; - let backed = table_attested_to_backed(attested, &table_context).unwrap(); + let backed = table_attested_to_backed(attested, &table_context, false).unwrap(); let expected_bitvec = { let mut validator_indices = BitVec::::with_capacity(6); @@ -1486,8 +1976,11 @@ fn candidate_backing_reorders_votes() { let expected_attestations = vec![fake_attestation(1).into(), fake_attestation(3).into(), fake_attestation(5).into()]; - assert_eq!(backed.validator_indices, expected_bitvec); - assert_eq!(backed.validity_votes, expected_attestations); + assert_eq!( + backed.validator_indices_and_core_index(false), + (expected_bitvec.as_bitslice(), None) + ); + assert_eq!(backed.validity_votes(), expected_attestations); } // Test whether we retry on failed PoV fetching. diff --git a/polkadot/node/core/backing/src/tests/prospective_parachains.rs b/polkadot/node/core/backing/src/tests/prospective_parachains.rs index 578f21bef66515e49042d7a11692de67b9642d41..94310d2aa164650db84b78ddf361a9f465ac207d 100644 --- a/polkadot/node/core/backing/src/tests/prospective_parachains.rs +++ b/polkadot/node/core/backing/src/tests/prospective_parachains.rs @@ -185,6 +185,16 @@ async fn activate_leaf( } ); + // Node features request from runtime: all features are disabled. + assert_matches!( + virtual_overseer.recv().await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request(parent, RuntimeApiRequest::NodeFeatures(_session_index, tx)) + ) if parent == hash => { + tx.send(Ok(Default::default())).unwrap(); + } + ); + // Check if subsystem job issues a request for the minimum backing votes. assert_matches!( virtual_overseer.recv().await, @@ -305,10 +315,11 @@ async fn assert_hypothetical_frontier_requests( ) => { let idx = match expected_requests.iter().position(|r| r.0 == request) { Some(idx) => idx, - None => panic!( + None => + panic!( "unexpected hypothetical frontier request, no match found for {:?}", request - ), + ), }; let resp = std::mem::take(&mut expected_requests[idx].1); tx.send(resp).unwrap(); @@ -1268,6 +1279,7 @@ fn concurrent_dependent_candidates() { let statement_b = CandidateBackingMessage::Statement(leaf_parent, signed_b.clone()); virtual_overseer.send(FromOrchestra::Communication { msg: statement_a }).await; + // At this point the subsystem waits for response, the previous message is received, // send a second one without blocking. let _ = virtual_overseer @@ -1388,7 +1400,19 @@ fn concurrent_dependent_candidates() { assert_eq!(sess_idx, 1); tx.send(Ok(Some(ExecutorParams::default()))).unwrap(); }, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _parent, + RuntimeApiRequest::ValidatorGroups(tx), + )) => { + tx.send(Ok(test_state.validator_groups.clone())).unwrap(); + }, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _parent, + RuntimeApiRequest::AvailabilityCores(tx), + )) => { + tx.send(Ok(test_state.availability_cores.clone())).unwrap(); + }, _ => panic!("unexpected message received from overseer: {:?}", msg), } } @@ -1419,7 +1443,6 @@ fn seconding_sanity_check_occupy_same_depth() { let leaf_parent = get_parent_hash(leaf_hash); let activated = new_leaf(leaf_hash, LEAF_BLOCK_NUMBER); - let min_block_number = LEAF_BLOCK_NUMBER - LEAF_ANCESTRY_LEN; let min_relay_parents = vec![(para_id_a, min_block_number), (para_id_b, min_block_number)]; let test_leaf_a = TestLeaf { activated, min_relay_parents }; @@ -1555,13 +1578,14 @@ fn occupied_core_assignment() { const LEAF_A_BLOCK_NUMBER: BlockNumber = 100; const LEAF_A_ANCESTRY_LEN: BlockNumber = 3; let para_id = test_state.chain_ids[0]; + let previous_para_id = test_state.chain_ids[1]; // Set the core state to occupied. let mut candidate_descriptor = ::test_helpers::dummy_candidate_descriptor(Hash::zero()); - candidate_descriptor.para_id = para_id; + candidate_descriptor.para_id = previous_para_id; test_state.availability_cores[0] = CoreState::Occupied(OccupiedCore { group_responsible: Default::default(), - next_up_on_available: None, + next_up_on_available: Some(ScheduledCore { para_id, collator: None }), occupied_since: 100_u32, time_out_at: 200_u32, next_up_on_time_out: None, diff --git a/polkadot/node/core/candidate-validation/src/lib.rs b/polkadot/node/core/candidate-validation/src/lib.rs index bf6e09fd1b69b702206eb52090cb4a12812c00c0..ec24434db24c30713e106b99b3238f997ba4765f 100644 --- a/polkadot/node/core/candidate-validation/src/lib.rs +++ b/polkadot/node/core/candidate-validation/src/lib.rs @@ -617,7 +617,7 @@ async fn validate_candidate_exhaustive( Err(e) => { gum::info!(target: LOG_TARGET, ?para_id, err=?e, "Invalid candidate (validation code)"); - // Code already passed pre-checking, if decompression fails now this most likley means + // Code already passed pre-checking, if decompression fails now this most likely means // some local corruption happened. return Err(ValidationFailed("Code decompression failed".to_string())) }, @@ -695,6 +695,8 @@ async fn validate_candidate_exhaustive( ))), Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::JobError(err))) => Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError(err))), + Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::RuntimeConstruction(err))) => + Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError(err))), Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::AmbiguousJobDeath(err))) => Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError(format!( @@ -780,40 +782,50 @@ trait ValidationBackend { return validation_result } + macro_rules! break_if_no_retries_left { + ($counter:ident) => { + if $counter > 0 { + $counter -= 1; + } else { + break + } + }; + } + // Allow limited retries for each kind of error. let mut num_death_retries_left = 1; let mut num_job_error_retries_left = 1; let mut num_internal_retries_left = 1; + let mut num_runtime_construction_retries_left = 1; loop { // Stop retrying if we exceeded the timeout. if total_time_start.elapsed() + retry_delay > exec_timeout { break } - + let mut retry_immediately = false; match validation_result { Err(ValidationError::PossiblyInvalid( PossiblyInvalidError::AmbiguousWorkerDeath | PossiblyInvalidError::AmbiguousJobDeath(_), - )) => - if num_death_retries_left > 0 { - num_death_retries_left -= 1; - } else { - break - }, + )) => break_if_no_retries_left!(num_death_retries_left), Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::JobError(_))) => - if num_job_error_retries_left > 0 { - num_job_error_retries_left -= 1; - } else { - break - }, + break_if_no_retries_left!(num_job_error_retries_left), Err(ValidationError::Internal(_)) => - if num_internal_retries_left > 0 { - num_internal_retries_left -= 1; - } else { - break - }, + break_if_no_retries_left!(num_internal_retries_left), + + Err(ValidationError::PossiblyInvalid( + PossiblyInvalidError::RuntimeConstruction(_), + )) => { + break_if_no_retries_left!(num_runtime_construction_retries_left); + self.precheck_pvf(pvf.clone()).await?; + // In this case the error is deterministic + // And a retry forces the ValidationBackend + // to re-prepare the artifact so + // there is no need to wait before the retry + retry_immediately = true; + }, Ok(_) | Err(ValidationError::Invalid(_) | ValidationError::Preparation(_)) => break, } @@ -821,8 +833,11 @@ trait ValidationBackend { // If we got a possibly transient error, retry once after a brief delay, on the // assumption that the conditions that caused this error may have resolved on their own. { - // Wait a brief delay before retrying. - futures_timer::Delay::new(retry_delay).await; + // In case of many transient errors it is necessary to wait a little bit + // for the error to be probably resolved + if !retry_immediately { + futures_timer::Delay::new(retry_delay).await; + } let new_timeout = exec_timeout.saturating_sub(total_time_start.elapsed()); diff --git a/polkadot/node/core/chain-selection/src/lib.rs b/polkadot/node/core/chain-selection/src/lib.rs index aa5bb9548ad2484b3e0bdc0499b250335ea57f35..6f864fefb6110184233d52620dea923f8f0b1e4a 100644 --- a/polkadot/node/core/chain-selection/src/lib.rs +++ b/polkadot/node/core/chain-selection/src/lib.rs @@ -51,7 +51,7 @@ type Timestamp = u64; // If a block isn't approved in 120 seconds, nodes will abandon it // and begin building on another chain. const STAGNANT_TIMEOUT: Timestamp = 120; -// Delay prunning of the stagnant keys in prune only mode by 25 hours to avoid interception with the +// Delay pruning of the stagnant keys in prune only mode by 25 hours to avoid interception with the // finality const STAGNANT_PRUNE_DELAY: Timestamp = 25 * 60 * 60; // Maximum number of stagnant entries cleaned during one `STAGNANT_TIMEOUT` iteration @@ -237,7 +237,7 @@ impl Clock for SystemClock { // // The exact time that a block becomes stagnant in the local node is always expected // to differ from other nodes due to network asynchrony and delays in block propagation. - // Non-monotonicity exarcerbates that somewhat, but not meaningfully. + // Non-monotonicity exacerbates that somewhat, but not meaningfully. match SystemTime::now().duration_since(UNIX_EPOCH) { Ok(d) => d.as_secs(), diff --git a/polkadot/node/core/chain-selection/src/tests.rs b/polkadot/node/core/chain-selection/src/tests.rs index cf021c0efeb0638b5fe1db4ef5dbe9dd4c7766f6..bc998f268a0da6a4ca3703f929be3e1c19677b35 100644 --- a/polkadot/node/core/chain-selection/src/tests.rs +++ b/polkadot/node/core/chain-selection/src/tests.rs @@ -406,7 +406,7 @@ async fn import_chains_into_empty( // some pre-blocks may need to be supplied to answer ancestry requests // that gather batches beyond the beginning of the new chain. // pre-blocks are those already known by the subsystem, however, -// the subsystem has no way of knowin that until requesting ancestry. +// the subsystem has no way of knowing that until requesting ancestry. async fn import_all_blocks_into( virtual_overseer: &mut VirtualOverseer, backend: &TestBackend, @@ -1300,7 +1300,7 @@ fn finalize_erases_unviable_from_one_but_not_all_reverts() { // F <- A1 <- A2 <- A3 // // A3 reverts A2 and A1. - // Finalize A1. A2 is stil unviable. + // Finalize A1. A2 is still unviable. let (a3_hash, chain_a) = construct_chain_on_base(vec![1, 2, 3], finalized_number, finalized_hash, |h| { diff --git a/polkadot/node/core/dispute-coordinator/src/db/v1.rs b/polkadot/node/core/dispute-coordinator/src/db/v1.rs index f0f17d2325d68bb1d70f40a2a761bdf5f35ebcfb..4950765cf510c047b811f50d0459c0add4c75d78 100644 --- a/polkadot/node/core/dispute-coordinator/src/db/v1.rs +++ b/polkadot/node/core/dispute-coordinator/src/db/v1.rs @@ -341,7 +341,7 @@ pub(crate) fn note_earliest_session( let lower_bound = (new_earliest_session, CandidateHash(Hash::repeat_byte(0x00))); let new_recent_disputes = recent_disputes.split_off(&lower_bound); - // Any remanining disputes are considered ancient and must be pruned. + // Any remaining disputes are considered ancient and must be pruned. let pruned_disputes = recent_disputes; if pruned_disputes.len() != 0 { diff --git a/polkadot/node/core/dispute-coordinator/src/initialized.rs b/polkadot/node/core/dispute-coordinator/src/initialized.rs index 54e0410268f1b30f1e6627d4b2c89cac2b741ccb..5f86da87f21ca060cfe14de536b39e652d12b7b1 100644 --- a/polkadot/node/core/dispute-coordinator/src/initialized.rs +++ b/polkadot/node/core/dispute-coordinator/src/initialized.rs @@ -99,7 +99,7 @@ pub(crate) struct Initialized { /// This is the highest `SessionIndex` seen via `ActiveLeavesUpdate`. It doesn't matter if it /// was cached successfully or not. It is used to detect ancient disputes. highest_session_seen: SessionIndex, - /// Will be set to `true` if an error occured during the last caching attempt + /// Will be set to `true` if an error occurred during the last caching attempt gaps_in_cache: bool, spam_slots: SpamSlots, participation: Participation, diff --git a/polkadot/node/core/dispute-coordinator/src/lib.rs b/polkadot/node/core/dispute-coordinator/src/lib.rs index 4b511e7430af655303a7bb6e5ca0e86d0f8e8c7b..daa384b36ffbaf2c8d3c004e35eb537a3c33e4c0 100644 --- a/polkadot/node/core/dispute-coordinator/src/lib.rs +++ b/polkadot/node/core/dispute-coordinator/src/lib.rs @@ -462,7 +462,7 @@ async fn wait_for_first_leaf(ctx: &mut Context) -> Result { // Rare case where same candidate was present on multiple heights, but all are // pruned at the same time. This candidate was already pruned in the previous - // occurence so it is skipped now. + // occurrence so it is skipped now. }, Entry::Occupied(mut e) => { let mut blocks_including = std::mem::take(e.get_mut()); diff --git a/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs b/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs index 748f9a16f493e2bce191091638771aa5be57fa9f..726dda596d7b8be26da55358c95c9080ec89d962 100644 --- a/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs +++ b/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs @@ -542,8 +542,8 @@ fn scraper_handles_backed_but_not_included_candidate() { } #[test] -fn scraper_handles_the_same_candidate_incuded_in_two_different_block_heights() { - // Same candidate will be inclued in these two leaves +fn scraper_handles_the_same_candidate_included_in_two_different_block_heights() { + // Same candidate will be included in these two leaves let test_targets = vec![2, 3]; // How many blocks should we skip before sending a leaf update. diff --git a/polkadot/node/core/dispute-coordinator/src/tests.rs b/polkadot/node/core/dispute-coordinator/src/tests.rs index 0360e357bee4ca70fe9a6ab3a9454d611ddc93fd..7c1f4ff241d3862889b964a12b38669f0f358b96 100644 --- a/polkadot/node/core/dispute-coordinator/src/tests.rs +++ b/polkadot/node/core/dispute-coordinator/src/tests.rs @@ -2226,7 +2226,7 @@ fn resume_dispute_without_local_statement() { test_state }) }) - // Alice should send a DisputeParticiationMessage::Participate on restart since she has no + // Alice should send a DisputeParticipationMessage::Participate on restart since she has no // local statement for the active dispute. .resume(|mut test_state, mut virtual_overseer| { Box::pin(async move { @@ -2390,7 +2390,7 @@ fn resume_dispute_with_local_statement() { test_state }) }) - // Alice should not send a DisputeParticiationMessage::Participate on restart since she has a + // Alice should not send a DisputeParticipationMessage::Participate on restart since she has a // local statement for the active dispute, instead she should try to (re-)send her vote. .resume(|mut test_state, mut virtual_overseer| { let candidate_receipt = make_valid_candidate_receipt(); @@ -2495,7 +2495,7 @@ fn resume_dispute_without_local_statement_or_local_key() { test_state }) }) - // Two should not send a DisputeParticiationMessage::Participate on restart since she is no + // Two should not send a DisputeParticipationMessage::Participate on restart since she is no // validator in that dispute. .resume(|mut test_state, mut virtual_overseer| { Box::pin(async move { diff --git a/polkadot/node/core/prospective-parachains/Cargo.toml b/polkadot/node/core/prospective-parachains/Cargo.toml index 5b62d90c1d4f82c885a650cfb63801358e57277d..f66a66e859ec0a139e31068f78225597d577d1a9 100644 --- a/polkadot/node/core/prospective-parachains/Cargo.toml +++ b/polkadot/node/core/prospective-parachains/Cargo.toml @@ -23,6 +23,7 @@ polkadot-node-subsystem = { path = "../../subsystem" } polkadot-node-subsystem-util = { path = "../../subsystem-util" } [dev-dependencies] +rstest = "0.18.2" assert_matches = "1" polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers" } polkadot-node-subsystem-types = { path = "../../subsystem-types" } diff --git a/polkadot/node/core/prospective-parachains/src/fragment_tree.rs b/polkadot/node/core/prospective-parachains/src/fragment_tree.rs index 04ee42a9de062bbd183f92c360703eb12c25f1aa..8061dc82d8358ae9e7c741e3f785634601eb5d03 100644 --- a/polkadot/node/core/prospective-parachains/src/fragment_tree.rs +++ b/polkadot/node/core/prospective-parachains/src/fragment_tree.rs @@ -96,6 +96,7 @@ use std::{ use super::LOG_TARGET; use bitvec::prelude::*; +use polkadot_node_subsystem::messages::Ancestors; use polkadot_node_subsystem_util::inclusion_emulator::{ ConstraintModifications, Constraints, Fragment, ProspectiveCandidate, RelayChainBlockInfo, }; @@ -756,45 +757,46 @@ impl FragmentTree { depths.iter_ones().collect() } - /// Select `count` candidates after the given `required_path` which pass + /// Select `count` candidates after the given `ancestors` which pass /// the predicate and have not already been backed on chain. /// - /// Does an exhaustive search into the tree starting after `required_path`. - /// If there are multiple possibilities of size `count`, this will select the first one. - /// If there is no chain of size `count` that matches the criteria, this will return the largest - /// chain it could find with the criteria. - /// If there are no candidates meeting those criteria, returns an empty `Vec`. - /// Cycles are accepted, see module docs for the `Cycles` section. + /// Does an exhaustive search into the tree after traversing the ancestors path. + /// If the ancestors draw out a path that can be traversed in multiple ways, no + /// candidates will be returned. + /// If the ancestors do not draw out a full path (the path contains holes), candidates will be + /// suggested that may fill these holes. + /// If the ancestors don't draw out a valid path, no candidates will be returned. If there are + /// multiple possibilities of the same size, this will select the first one. If there is no + /// chain of size `count` that matches the criteria, this will return the largest chain it could + /// find with the criteria. If there are no candidates meeting those criteria, returns an empty + /// `Vec`. + /// Cycles are accepted, but this code expects that the runtime will deduplicate + /// identical candidates when occupying the cores (when proposing to back A->B->A, only A will + /// be backed on chain). /// - /// The intention of the `required_path` is to allow queries on the basis of + /// The intention of the `ancestors` is to allow queries on the basis of /// one or more candidates which were previously pending availability becoming - /// available and opening up more room on the core. - pub(crate) fn select_children( + /// available or candidates timing out. + pub(crate) fn find_backable_chain( &self, - required_path: &[CandidateHash], + ancestors: Ancestors, count: u32, pred: impl Fn(&CandidateHash) -> bool, ) -> Vec { - let base_node = { - // traverse the required path. - let mut node = NodePointer::Root; - for required_step in required_path { - if let Some(next_node) = self.node_candidate_child(node, &required_step) { - node = next_node; - } else { - return vec![] - }; - } - - node - }; - - // TODO: taking the first best selection might introduce bias - // or become gameable. - // - // For plausibly unique parachains, this shouldn't matter much. - // figure out alternative selection criteria? - self.select_children_inner(base_node, count, count, &pred, &mut vec![]) + if count == 0 { + return vec![] + } + // First, we need to order the ancestors. + // The node returned is the one from which we can start finding new backable candidates. + let Some(base_node) = self.find_ancestor_path(ancestors) else { return vec![] }; + + self.find_backable_chain_inner( + base_node, + count, + count, + &pred, + &mut Vec::with_capacity(count as usize), + ) } // Try finding a candidate chain starting from `base_node` of length `expected_count`. @@ -805,7 +807,7 @@ impl FragmentTree { // Cycles are accepted, but this doesn't allow for infinite execution time, because the maximum // depth we'll reach is `expected_count`. // - // Worst case performance is `O(num_forks ^ expected_count)`. + // Worst case performance is `O(num_forks ^ expected_count)`, the same as populating the tree. // Although an exponential function, this is actually a constant that can only be altered via // sudo/governance, because: // 1. `num_forks` at a given level is at most `max_candidate_depth * max_validators_per_core` @@ -817,7 +819,7 @@ impl FragmentTree { // scaling scenario). For non-elastic-scaling, this is just 1. In practice, this should be a // small number (1-3), capped by the total number of available cores (a constant alterable // only via governance/sudo). - fn select_children_inner( + fn find_backable_chain_inner( &self, base_node: NodePointer, expected_count: u32, @@ -857,7 +859,7 @@ impl FragmentTree { for (child_ptr, child_hash) in children { accumulator.push(child_hash); - let result = self.select_children_inner( + let result = self.find_backable_chain_inner( child_ptr, expected_count, remaining_count - 1, @@ -869,6 +871,9 @@ impl FragmentTree { // Short-circuit the search if we've found the right length. Otherwise, we'll // search for a max. + // Taking the first best selection doesn't introduce bias or become gameable, + // because `find_ancestor_path` uses a `HashSet` to track the ancestors, which + // makes the order in which ancestors are visited non-deterministic. if result.len() == expected_count as usize { return result } else if best_result.len() < result.len() { @@ -879,6 +884,93 @@ impl FragmentTree { best_result } + // Orders the ancestors into a viable path from root to the last one. + // Returns a pointer to the last node in the path. + // We assume that the ancestors form a chain (that the + // av-cores do not back parachain forks), None is returned otherwise. + // If we cannot use all ancestors, stop at the first found hole in the chain. This usually + // translates to a timed out candidate. + fn find_ancestor_path(&self, mut ancestors: Ancestors) -> Option { + // The number of elements in the path we've processed so far. + let mut depth = 0; + let mut last_node = NodePointer::Root; + let mut next_node: Option = Some(NodePointer::Root); + + while let Some(node) = next_node { + if depth > self.scope.max_depth { + return None; + } + + last_node = node; + + next_node = match node { + NodePointer::Root => { + let children = self + .nodes + .iter() + .enumerate() + .take_while(|n| n.1.parent == NodePointer::Root) + .map(|(index, node)| (NodePointer::Storage(index), node.candidate_hash)) + .collect::>(); + + self.find_valid_child(&mut ancestors, children.iter()).ok()? + }, + NodePointer::Storage(ptr) => { + let children = self.nodes.get(ptr).and_then(|n| Some(n.children.iter())); + if let Some(children) = children { + self.find_valid_child(&mut ancestors, children).ok()? + } else { + None + } + }, + }; + + depth += 1; + } + + Some(last_node) + } + + // Find a node from the given iterator which is present in the ancestors + // collection. If there are multiple such nodes, return an error and log a warning. We don't + // accept forks in a parachain to be backed. The supplied ancestors should all form a chain. + // If there is no such node, return None. + fn find_valid_child<'a>( + &self, + ancestors: &'a mut Ancestors, + nodes: impl Iterator + 'a, + ) -> Result, ()> { + let mut possible_children = + nodes.filter_map(|(node_ptr, hash)| match ancestors.remove(&hash) { + true => Some(node_ptr), + false => None, + }); + + // We don't accept forks in a parachain to be backed. The supplied ancestors + // should all form a chain. + let next = possible_children.next(); + if let Some(second_child) = possible_children.next() { + if let (Some(NodePointer::Storage(first_child)), NodePointer::Storage(second_child)) = + (next, second_child) + { + gum::error!( + target: LOG_TARGET, + para_id = ?self.scope.para, + relay_parent = ?self.scope.relay_parent, + "Trying to find new backable candidates for a parachain for which we've backed a fork.\ + This is a bug and the runtime should not have allowed it.\n\ + Backed candidates with the same parent: {}, {}", + self.nodes[*first_child].candidate_hash, + self.nodes[*second_child].candidate_hash, + ); + } + + Err(()) + } else { + Ok(next.copied()) + } + } + fn populate_from_bases(&mut self, storage: &CandidateStorage, initial_bases: Vec) { // Populate the tree breadth-first. let mut last_sweep_start = None; @@ -1061,8 +1153,18 @@ mod tests { use polkadot_node_subsystem_util::inclusion_emulator::InboundHrmpLimitations; use polkadot_primitives::{BlockNumber, CandidateCommitments, CandidateDescriptor, HeadData}; use polkadot_primitives_test_helpers as test_helpers; + use rstest::rstest; use std::iter; + impl NodePointer { + fn unwrap_idx(self) -> usize { + match self { + NodePointer::Root => panic!("Unexpected root"), + NodePointer::Storage(index) => index, + } + } + } + fn make_constraints( min_relay_parent_number: BlockNumber, valid_watermarks: Vec, @@ -1546,6 +1648,373 @@ mod tests { assert_eq!(tree.nodes[1].parent, NodePointer::Storage(0)); } + #[test] + fn test_find_ancestor_path_and_find_backable_chain_empty_tree() { + let para_id = ParaId::from(5u32); + let relay_parent = Hash::repeat_byte(1); + let required_parent: HeadData = vec![0xff].into(); + let max_depth = 10; + + // Empty tree + let storage = CandidateStorage::new(); + let base_constraints = make_constraints(0, vec![0], required_parent.clone()); + + let relay_parent_info = + RelayChainBlockInfo { number: 0, hash: relay_parent, storage_root: Hash::zero() }; + + let scope = Scope::with_ancestors( + para_id, + relay_parent_info, + base_constraints, + vec![], + max_depth, + vec![], + ) + .unwrap(); + let tree = FragmentTree::populate(scope, &storage); + assert_eq!(tree.candidates().collect::>().len(), 0); + assert_eq!(tree.nodes.len(), 0); + + assert_eq!(tree.find_ancestor_path(Ancestors::new()).unwrap(), NodePointer::Root); + assert_eq!(tree.find_backable_chain(Ancestors::new(), 2, |_| true), vec![]); + // Invalid candidate. + let ancestors: Ancestors = [CandidateHash::default()].into_iter().collect(); + assert_eq!(tree.find_ancestor_path(ancestors.clone()), Some(NodePointer::Root)); + assert_eq!(tree.find_backable_chain(ancestors, 2, |_| true), vec![]); + } + + #[rstest] + #[case(true, 13)] + #[case(false, 8)] + // The tree with no cycles looks like: + // Make a tree that looks like this (note that there's no cycle): + // +-(root)-+ + // | | + // +----0---+ 7 + // | | + // 1----+ 5 + // | | + // | | + // 2 6 + // | + // 3 + // | + // 4 + // + // The tree with cycles is the same as the first but has a cycle from 4 back to the state + // produced by 0 (It's bounded by the max_depth + 1). + // +-(root)-+ + // | | + // +----0---+ 7 + // | | + // 1----+ 5 + // | | + // | | + // 2 6 + // | + // 3 + // | + // 4---+ + // | | + // 1 5 + // | + // 2 + // | + // 3 + fn test_find_ancestor_path_and_find_backable_chain( + #[case] has_cycle: bool, + #[case] expected_node_count: usize, + ) { + let para_id = ParaId::from(5u32); + let relay_parent = Hash::repeat_byte(1); + let required_parent: HeadData = vec![0xff].into(); + let max_depth = 7; + let relay_parent_number = 0; + let relay_parent_storage_root = Hash::repeat_byte(69); + + let mut candidates = vec![]; + + // Candidate 0 + candidates.push(make_committed_candidate( + para_id, + relay_parent, + 0, + required_parent.clone(), + vec![0].into(), + 0, + )); + // Candidate 1 + candidates.push(make_committed_candidate( + para_id, + relay_parent, + 0, + vec![0].into(), + vec![1].into(), + 0, + )); + // Candidate 2 + candidates.push(make_committed_candidate( + para_id, + relay_parent, + 0, + vec![1].into(), + vec![2].into(), + 0, + )); + // Candidate 3 + candidates.push(make_committed_candidate( + para_id, + relay_parent, + 0, + vec![2].into(), + vec![3].into(), + 0, + )); + // Candidate 4 + candidates.push(make_committed_candidate( + para_id, + relay_parent, + 0, + vec![3].into(), + vec![4].into(), + 0, + )); + // Candidate 5 + candidates.push(make_committed_candidate( + para_id, + relay_parent, + 0, + vec![0].into(), + vec![5].into(), + 0, + )); + // Candidate 6 + candidates.push(make_committed_candidate( + para_id, + relay_parent, + 0, + vec![1].into(), + vec![6].into(), + 0, + )); + // Candidate 7 + candidates.push(make_committed_candidate( + para_id, + relay_parent, + 0, + required_parent.clone(), + vec![7].into(), + 0, + )); + + if has_cycle { + candidates[4] = make_committed_candidate( + para_id, + relay_parent, + 0, + vec![3].into(), + vec![0].into(), // put the cycle here back to the output state of 0. + 0, + ); + } + + let base_constraints = make_constraints(0, vec![0], required_parent.clone()); + let mut storage = CandidateStorage::new(); + + let relay_parent_info = RelayChainBlockInfo { + number: relay_parent_number, + hash: relay_parent, + storage_root: relay_parent_storage_root, + }; + + for (pvd, candidate) in candidates.iter() { + storage.add_candidate(candidate.clone(), pvd.clone()).unwrap(); + } + let candidates = + candidates.into_iter().map(|(_pvd, candidate)| candidate).collect::>(); + let scope = Scope::with_ancestors( + para_id, + relay_parent_info, + base_constraints, + vec![], + max_depth, + vec![], + ) + .unwrap(); + let tree = FragmentTree::populate(scope, &storage); + + assert_eq!(tree.candidates().collect::>().len(), candidates.len()); + assert_eq!(tree.nodes.len(), expected_node_count); + + // Do some common tests on both trees. + { + // No ancestors supplied. + assert_eq!(tree.find_ancestor_path(Ancestors::new()).unwrap(), NodePointer::Root); + assert_eq!( + tree.find_backable_chain(Ancestors::new(), 4, |_| true), + [0, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + // Ancestor which is not part of the tree. Will be ignored. + let ancestors: Ancestors = [CandidateHash::default()].into_iter().collect(); + assert_eq!(tree.find_ancestor_path(ancestors.clone()).unwrap(), NodePointer::Root); + assert_eq!( + tree.find_backable_chain(ancestors, 4, |_| true), + [0, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + // A chain fork. + let ancestors: Ancestors = + [(candidates[0].hash()), (candidates[7].hash())].into_iter().collect(); + assert_eq!(tree.find_ancestor_path(ancestors.clone()), None); + assert_eq!(tree.find_backable_chain(ancestors, 1, |_| true), vec![]); + + // Ancestors which are part of the tree but don't form a path. Will be ignored. + let ancestors: Ancestors = + [candidates[1].hash(), candidates[2].hash()].into_iter().collect(); + assert_eq!(tree.find_ancestor_path(ancestors.clone()).unwrap(), NodePointer::Root); + assert_eq!( + tree.find_backable_chain(ancestors, 4, |_| true), + [0, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + + // Valid ancestors. + let ancestors: Ancestors = [candidates[7].hash()].into_iter().collect(); + let res = tree.find_ancestor_path(ancestors.clone()).unwrap(); + let candidate = &tree.nodes[res.unwrap_idx()]; + assert_eq!(candidate.candidate_hash, candidates[7].hash()); + assert_eq!(tree.find_backable_chain(ancestors, 1, |_| true), vec![]); + + let ancestors: Ancestors = + [candidates[2].hash(), candidates[0].hash(), candidates[1].hash()] + .into_iter() + .collect(); + let res = tree.find_ancestor_path(ancestors.clone()).unwrap(); + let candidate = &tree.nodes[res.unwrap_idx()]; + assert_eq!(candidate.candidate_hash, candidates[2].hash()); + assert_eq!( + tree.find_backable_chain(ancestors.clone(), 2, |_| true), + [3, 4].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + + // Valid ancestors with candidates which have been omitted due to timeouts + let ancestors: Ancestors = + [candidates[0].hash(), candidates[2].hash()].into_iter().collect(); + let res = tree.find_ancestor_path(ancestors.clone()).unwrap(); + let candidate = &tree.nodes[res.unwrap_idx()]; + assert_eq!(candidate.candidate_hash, candidates[0].hash()); + assert_eq!( + tree.find_backable_chain(ancestors, 3, |_| true), + [1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + + let ancestors: Ancestors = + [candidates[0].hash(), candidates[1].hash(), candidates[3].hash()] + .into_iter() + .collect(); + let res = tree.find_ancestor_path(ancestors.clone()).unwrap(); + let candidate = &tree.nodes[res.unwrap_idx()]; + assert_eq!(candidate.candidate_hash, candidates[1].hash()); + if has_cycle { + assert_eq!( + tree.find_backable_chain(ancestors, 2, |_| true), + [2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + } else { + assert_eq!( + tree.find_backable_chain(ancestors, 4, |_| true), + [2, 3, 4].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + } + + let ancestors: Ancestors = + [candidates[1].hash(), candidates[2].hash()].into_iter().collect(); + let res = tree.find_ancestor_path(ancestors.clone()).unwrap(); + assert_eq!(res, NodePointer::Root); + assert_eq!( + tree.find_backable_chain(ancestors, 4, |_| true), + [0, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + + // Requested count is 0. + assert_eq!(tree.find_backable_chain(Ancestors::new(), 0, |_| true), vec![]); + + let ancestors: Ancestors = + [candidates[2].hash(), candidates[0].hash(), candidates[1].hash()] + .into_iter() + .collect(); + assert_eq!(tree.find_backable_chain(ancestors, 0, |_| true), vec![]); + + let ancestors: Ancestors = + [candidates[2].hash(), candidates[0].hash()].into_iter().collect(); + assert_eq!(tree.find_backable_chain(ancestors, 0, |_| true), vec![]); + } + + // Now do some tests only on the tree with cycles + if has_cycle { + // Exceeds the maximum tree depth. 0-1-2-3-4-1-2-3-4, when the tree stops at + // 0-1-2-3-4-1-2-3. + let ancestors: Ancestors = [ + candidates[0].hash(), + candidates[1].hash(), + candidates[2].hash(), + candidates[3].hash(), + candidates[4].hash(), + ] + .into_iter() + .collect(); + let res = tree.find_ancestor_path(ancestors.clone()).unwrap(); + let candidate = &tree.nodes[res.unwrap_idx()]; + assert_eq!(candidate.candidate_hash, candidates[4].hash()); + assert_eq!( + tree.find_backable_chain(ancestors, 4, |_| true), + [1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + + // 0-1-2. + let ancestors: Ancestors = + [candidates[0].hash(), candidates[1].hash(), candidates[2].hash()] + .into_iter() + .collect(); + let res = tree.find_ancestor_path(ancestors.clone()).unwrap(); + let candidate = &tree.nodes[res.unwrap_idx()]; + assert_eq!(candidate.candidate_hash, candidates[2].hash()); + assert_eq!( + tree.find_backable_chain(ancestors.clone(), 1, |_| true), + [3].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + assert_eq!( + tree.find_backable_chain(ancestors, 5, |_| true), + [3, 4, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + + // 0-1 + let ancestors: Ancestors = + [candidates[0].hash(), candidates[1].hash()].into_iter().collect(); + let res = tree.find_ancestor_path(ancestors.clone()).unwrap(); + let candidate = &tree.nodes[res.unwrap_idx()]; + assert_eq!(candidate.candidate_hash, candidates[1].hash()); + assert_eq!( + tree.find_backable_chain(ancestors, 6, |_| true), + [2, 3, 4, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>(), + ); + + // For 0-1-2-3-4-5, there's more than 1 way of finding this path in + // the tree. `None` should be returned. The runtime should not have accepted this. + let ancestors: Ancestors = [ + candidates[0].hash(), + candidates[1].hash(), + candidates[2].hash(), + candidates[3].hash(), + candidates[4].hash(), + candidates[5].hash(), + ] + .into_iter() + .collect(); + let res = tree.find_ancestor_path(ancestors.clone()); + assert_eq!(res, None); + assert_eq!(tree.find_backable_chain(ancestors, 1, |_| true), vec![]); + } + } + #[test] fn graceful_cycle_of_0() { let mut storage = CandidateStorage::new(); @@ -1602,13 +2071,17 @@ mod tests { for count in 1..10 { assert_eq!( - tree.select_children(&[], count, |_| true), + tree.find_backable_chain(Ancestors::new(), count, |_| true), iter::repeat(candidate_a_hash) .take(std::cmp::min(count as usize, max_depth + 1)) .collect::>() ); assert_eq!( - tree.select_children(&[candidate_a_hash], count - 1, |_| true), + tree.find_backable_chain( + [candidate_a_hash].into_iter().collect(), + count - 1, + |_| true + ), iter::repeat(candidate_a_hash) .take(std::cmp::min(count as usize - 1, max_depth)) .collect::>() @@ -1682,22 +2155,22 @@ mod tests { assert_eq!(tree.nodes[3].candidate_hash, candidate_b_hash); assert_eq!(tree.nodes[4].candidate_hash, candidate_a_hash); - assert_eq!(tree.select_children(&[], 1, |_| true), vec![candidate_a_hash],); + assert_eq!(tree.find_backable_chain(Ancestors::new(), 1, |_| true), vec![candidate_a_hash],); assert_eq!( - tree.select_children(&[], 2, |_| true), + tree.find_backable_chain(Ancestors::new(), 2, |_| true), vec![candidate_a_hash, candidate_b_hash], ); assert_eq!( - tree.select_children(&[], 3, |_| true), + tree.find_backable_chain(Ancestors::new(), 3, |_| true), vec![candidate_a_hash, candidate_b_hash, candidate_a_hash], ); assert_eq!( - tree.select_children(&[candidate_a_hash], 2, |_| true), + tree.find_backable_chain([candidate_a_hash].into_iter().collect(), 2, |_| true), vec![candidate_b_hash, candidate_a_hash], ); assert_eq!( - tree.select_children(&[], 6, |_| true), + tree.find_backable_chain(Ancestors::new(), 6, |_| true), vec![ candidate_a_hash, candidate_b_hash, @@ -1706,10 +2179,17 @@ mod tests { candidate_a_hash ], ); - assert_eq!( - tree.select_children(&[candidate_a_hash, candidate_b_hash], 6, |_| true), - vec![candidate_a_hash, candidate_b_hash, candidate_a_hash,], - ); + + for count in 3..7 { + assert_eq!( + tree.find_backable_chain( + [candidate_a_hash, candidate_b_hash].into_iter().collect(), + count, + |_| true + ), + vec![candidate_a_hash, candidate_b_hash, candidate_a_hash], + ); + } } #[test] diff --git a/polkadot/node/core/prospective-parachains/src/lib.rs b/polkadot/node/core/prospective-parachains/src/lib.rs index 5937a1c1fb9fdc04eb2fa70e65f92f19d7b99bff..f5d50fb74faca5fec78c47aa6a653a1a9fe48699 100644 --- a/polkadot/node/core/prospective-parachains/src/lib.rs +++ b/polkadot/node/core/prospective-parachains/src/lib.rs @@ -35,9 +35,10 @@ use futures::{channel::oneshot, prelude::*}; use polkadot_node_subsystem::{ messages::{ - ChainApiMessage, FragmentTreeMembership, HypotheticalCandidate, - HypotheticalFrontierRequest, IntroduceCandidateRequest, ProspectiveParachainsMessage, - ProspectiveValidationDataRequest, RuntimeApiMessage, RuntimeApiRequest, + Ancestors, ChainApiMessage, FragmentTreeMembership, HypotheticalCandidate, + HypotheticalFrontierRequest, IntroduceCandidateRequest, ParentHeadData, + ProspectiveParachainsMessage, ProspectiveValidationDataRequest, RuntimeApiMessage, + RuntimeApiRequest, }, overseer, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, }; @@ -150,16 +151,9 @@ async fn run_iteration( relay_parent, para, count, - required_path, + ancestors, tx, - ) => answer_get_backable_candidates( - &view, - relay_parent, - para, - count, - required_path, - tx, - ), + ) => answer_get_backable_candidates(&view, relay_parent, para, count, ancestors, tx), ProspectiveParachainsMessage::GetHypotheticalFrontier(request, tx) => answer_hypothetical_frontier_request(&view, request, tx), ProspectiveParachainsMessage::GetTreeMembership(para, candidate, tx) => @@ -565,7 +559,7 @@ fn answer_get_backable_candidates( relay_parent: Hash, para: ParaId, count: u32, - required_path: Vec, + ancestors: Ancestors, tx: oneshot::Sender>, ) { let data = match view.active_leaves.get(&relay_parent) { @@ -614,7 +608,7 @@ fn answer_get_backable_candidates( }; let backable_candidates: Vec<_> = tree - .select_children(&required_path, count, |candidate| storage.is_backed(candidate)) + .find_backable_chain(ancestors.clone(), count, |candidate| storage.is_backed(candidate)) .into_iter() .filter_map(|child_hash| { storage.relay_parent_by_candidate_hash(&child_hash).map_or_else( @@ -635,7 +629,7 @@ fn answer_get_backable_candidates( if backable_candidates.is_empty() { gum::trace!( target: LOG_TARGET, - ?required_path, + ?ancestors, para_id = ?para, %relay_parent, "Could not find any backable candidate", @@ -771,8 +765,14 @@ fn answer_prospective_validation_data_request( Some(s) => s, }; - let mut head_data = - storage.head_data_by_hash(&request.parent_head_data_hash).map(|x| x.clone()); + let (mut head_data, parent_head_data_hash) = match request.parent_head_data { + ParentHeadData::OnlyHash(parent_head_data_hash) => ( + storage.head_data_by_hash(&parent_head_data_hash).map(|x| x.clone()), + parent_head_data_hash, + ), + ParentHeadData::WithData { head_data, hash } => (Some(head_data), hash), + }; + let mut relay_parent_info = None; let mut max_pov_size = None; @@ -790,7 +790,7 @@ fn answer_prospective_validation_data_request( } if head_data.is_none() { let required_parent = &fragment_tree.scope().base_constraints().required_parent; - if required_parent.hash() == request.parent_head_data_hash { + if required_parent.hash() == parent_head_data_hash { head_data = Some(required_parent.clone()); } } diff --git a/polkadot/node/core/prospective-parachains/src/tests.rs b/polkadot/node/core/prospective-parachains/src/tests.rs index 732736b101de0ce525c7753af4b60cca7534522b..0e0079c02bbea27be88fc2dc9e030cf1a3311e89 100644 --- a/polkadot/node/core/prospective-parachains/src/tests.rs +++ b/polkadot/node/core/prospective-parachains/src/tests.rs @@ -19,7 +19,7 @@ use assert_matches::assert_matches; use polkadot_node_subsystem::{ errors::RuntimeApiError, messages::{ - AllMessages, HypotheticalFrontierRequest, ProspectiveParachainsMessage, + AllMessages, HypotheticalFrontierRequest, ParentHeadData, ProspectiveParachainsMessage, ProspectiveValidationDataRequest, }, }; @@ -407,7 +407,7 @@ async fn get_backable_candidates( virtual_overseer: &mut VirtualOverseer, leaf: &TestLeaf, para_id: ParaId, - required_path: Vec, + ancestors: Ancestors, count: u32, expected_result: Vec<(CandidateHash, Hash)>, ) { @@ -415,11 +415,7 @@ async fn get_backable_candidates( virtual_overseer .send(overseer::FromOrchestra::Communication { msg: ProspectiveParachainsMessage::GetBackableCandidates( - leaf.hash, - para_id, - count, - required_path, - tx, + leaf.hash, para_id, count, ancestors, tx, ), }) .await; @@ -472,7 +468,7 @@ async fn get_pvd( let request = ProspectiveValidationDataRequest { para_id, candidate_relay_parent, - parent_head_data_hash: parent_head_data.hash(), + parent_head_data: ParentHeadData::OnlyHash(parent_head_data.hash()), }; let (tx, rx) = oneshot::channel(); virtual_overseer @@ -903,7 +899,7 @@ fn check_backable_query_single_candidate() { &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_a], + vec![candidate_hash_a].into_iter().collect(), 1, vec![], ) @@ -912,12 +908,20 @@ fn check_backable_query_single_candidate() { &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_a], + vec![candidate_hash_a].into_iter().collect(), + 0, + vec![], + ) + .await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + Ancestors::new(), 0, vec![], ) .await; - get_backable_candidates(&mut virtual_overseer, &leaf_a, 1.into(), vec![], 0, vec![]).await; // Second candidates. second_candidate(&mut virtual_overseer, candidate_a.clone()).await; @@ -928,7 +932,7 @@ fn check_backable_query_single_candidate() { &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_a], + vec![candidate_hash_a].into_iter().collect(), 1, vec![], ) @@ -939,12 +943,20 @@ fn check_backable_query_single_candidate() { back_candidate(&mut virtual_overseer, &candidate_b, candidate_hash_b).await; // Should not get any backable candidates for the other para. - get_backable_candidates(&mut virtual_overseer, &leaf_a, 2.into(), vec![], 1, vec![]).await; get_backable_candidates( &mut virtual_overseer, &leaf_a, 2.into(), - vec![candidate_hash_a], + Ancestors::new(), + 1, + vec![], + ) + .await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 2.into(), + vec![candidate_hash_a].into_iter().collect(), 1, vec![], ) @@ -955,7 +967,7 @@ fn check_backable_query_single_candidate() { &mut virtual_overseer, &leaf_a, 1.into(), - vec![], + Ancestors::new(), 1, vec![(candidate_hash_a, leaf_a.hash)], ) @@ -964,20 +976,20 @@ fn check_backable_query_single_candidate() { &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_a], + vec![candidate_hash_a].into_iter().collect(), 1, vec![(candidate_hash_b, leaf_a.hash)], ) .await; - // Should not get anything at the wrong path. + // Wrong path get_backable_candidates( &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_b], + vec![candidate_hash_b].into_iter().collect(), 1, - vec![], + vec![(candidate_hash_a, leaf_a.hash)], ) .await; @@ -1075,15 +1087,29 @@ fn check_backable_query_multiple_candidates() { make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_i, 10); // Should not get any backable candidates for the other para. - get_backable_candidates(&mut virtual_overseer, &leaf_a, 2.into(), vec![], 1, vec![]) - .await; - get_backable_candidates(&mut virtual_overseer, &leaf_a, 2.into(), vec![], 5, vec![]) - .await; get_backable_candidates( &mut virtual_overseer, &leaf_a, 2.into(), - vec![candidate_hash_a], + Ancestors::new(), + 1, + vec![], + ) + .await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 2.into(), + Ancestors::new(), + 5, + vec![], + ) + .await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 2.into(), + vec![candidate_hash_a].into_iter().collect(), 1, vec![], ) @@ -1097,7 +1123,7 @@ fn check_backable_query_multiple_candidates() { &mut virtual_overseer, &leaf_a, 1.into(), - vec![], + Ancestors::new(), 1, vec![(candidate_hash_a, leaf_a.hash)], ) @@ -1106,7 +1132,7 @@ fn check_backable_query_multiple_candidates() { &mut virtual_overseer, &leaf_a, 1.into(), - vec![], + Ancestors::new(), 4, vec![ (candidate_hash_a, leaf_a.hash), @@ -1124,7 +1150,7 @@ fn check_backable_query_multiple_candidates() { &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_a], + vec![candidate_hash_a].into_iter().collect(), 1, vec![(candidate_hash_b, leaf_a.hash)], ) @@ -1133,16 +1159,7 @@ fn check_backable_query_multiple_candidates() { &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_a], - 2, - vec![(candidate_hash_b, leaf_a.hash), (candidate_hash_d, leaf_a.hash)], - ) - .await; - get_backable_candidates( - &mut virtual_overseer, - &leaf_a, - 1.into(), - vec![candidate_hash_a], + vec![candidate_hash_a].into_iter().collect(), 3, vec![ (candidate_hash_b, leaf_a.hash), @@ -1159,7 +1176,7 @@ fn check_backable_query_multiple_candidates() { &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_a], + vec![candidate_hash_a].into_iter().collect(), count, vec![ (candidate_hash_c, leaf_a.hash), @@ -1172,26 +1189,30 @@ fn check_backable_query_multiple_candidates() { } } - // required path of 2 + // required path of 2 and higher { get_backable_candidates( &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_a, candidate_hash_b], + vec![candidate_hash_a, candidate_hash_i, candidate_hash_h, candidate_hash_c] + .into_iter() + .collect(), 1, - vec![(candidate_hash_d, leaf_a.hash)], + vec![(candidate_hash_j, leaf_a.hash)], ) .await; + get_backable_candidates( &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_a, candidate_hash_c], + vec![candidate_hash_a, candidate_hash_b].into_iter().collect(), 1, - vec![(candidate_hash_h, leaf_a.hash)], + vec![(candidate_hash_d, leaf_a.hash)], ) .await; + // If the requested count exceeds the largest chain, return the longest // chain we can get. for count in 4..10 { @@ -1199,7 +1220,7 @@ fn check_backable_query_multiple_candidates() { &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_a, candidate_hash_c], + vec![candidate_hash_a, candidate_hash_c].into_iter().collect(), count, vec![ (candidate_hash_h, leaf_a.hash), @@ -1213,317 +1234,127 @@ fn check_backable_query_multiple_candidates() { // No more candidates in any chain. { - let required_paths = vec![ - vec![candidate_hash_a, candidate_hash_b, candidate_hash_e], - vec![ - candidate_hash_a, - candidate_hash_c, - candidate_hash_h, - candidate_hash_i, - candidate_hash_j, - ], - ]; - for path in required_paths { - for count in 1..4 { - get_backable_candidates( - &mut virtual_overseer, - &leaf_a, - 1.into(), - path.clone(), - count, - vec![], - ) - .await; - } + for count in 1..4 { + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_a, candidate_hash_b, candidate_hash_e] + .into_iter() + .collect(), + count, + vec![], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![ + candidate_hash_a, + candidate_hash_c, + candidate_hash_h, + candidate_hash_i, + candidate_hash_j, + ] + .into_iter() + .collect(), + count, + vec![], + ) + .await; } } - // Should not get anything at the wrong path. + // Wrong paths. get_backable_candidates( &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_b], + vec![candidate_hash_b].into_iter().collect(), 1, - vec![], + vec![(candidate_hash_a, leaf_a.hash)], ) .await; get_backable_candidates( &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_b, candidate_hash_a], + vec![candidate_hash_b, candidate_hash_f].into_iter().collect(), 3, - vec![], + vec![ + (candidate_hash_a, leaf_a.hash), + (candidate_hash_b, leaf_a.hash), + (candidate_hash_d, leaf_a.hash), + ], ) .await; get_backable_candidates( &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_a, candidate_hash_b, candidate_hash_c], - 3, - vec![], + vec![candidate_hash_a, candidate_hash_h].into_iter().collect(), + 4, + vec![ + (candidate_hash_c, leaf_a.hash), + (candidate_hash_h, leaf_a.hash), + (candidate_hash_i, leaf_a.hash), + (candidate_hash_j, leaf_a.hash), + ], ) .await; - - virtual_overseer - }); - - assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - // 10 candidates and 7 parents on para 1. - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (7, 10)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); - } - - // A tree with multiple roots. - // Parachain 1 looks like this: - // (imaginary root) - // | | - // +----B---+ A - // | | | | - // | | | C - // D E F | - // | H - // G | - // I - // | - // J - { - let test_state = TestState::default(); - let view = test_harness(|mut virtual_overseer| async move { - // Leaf A - let leaf_a = TestLeaf { - number: 100, - hash: Hash::from_low_u64_be(130), - para_data: vec![ - (1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))), - (2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))), - ], - }; - - // Activate leaves. - activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await; - - // Candidate B - let (candidate_b, pvd_b) = make_candidate( - leaf_a.hash, - leaf_a.number, + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, 1.into(), - HeadData(vec![1, 2, 3]), - HeadData(vec![2]), - test_state.validation_code_hash, - ); - let candidate_hash_b = candidate_b.hash(); - introduce_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b).await; - second_candidate(&mut virtual_overseer, candidate_b.clone()).await; - back_candidate(&mut virtual_overseer, &candidate_b, candidate_hash_b).await; + vec![candidate_hash_e, candidate_hash_h].into_iter().collect(), + 2, + vec![(candidate_hash_a, leaf_a.hash), (candidate_hash_b, leaf_a.hash)], + ) + .await; - // Candidate A - let (candidate_a, pvd_a) = make_candidate( - leaf_a.hash, - leaf_a.number, + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, 1.into(), - HeadData(vec![1, 2, 3]), - HeadData(vec![1]), - test_state.validation_code_hash, - ); - let candidate_hash_a = candidate_a.hash(); - introduce_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await; - second_candidate(&mut virtual_overseer, candidate_a.clone()).await; - back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await; - - let (candidate_c, candidate_hash_c) = - make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_a, 3); - let (_candidate_d, candidate_hash_d) = - make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_b, 4); - let (_candidate_e, candidate_hash_e) = - make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_b, 5); - let (candidate_f, candidate_hash_f) = - make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_b, 6); - let (_candidate_g, candidate_hash_g) = - make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_f, 7); - let (candidate_h, candidate_hash_h) = - make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_c, 8); - let (candidate_i, candidate_hash_i) = - make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_h, 9); - let (_candidate_j, candidate_hash_j) = - make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_i, 10); + vec![candidate_hash_a, candidate_hash_c, candidate_hash_d].into_iter().collect(), + 2, + vec![(candidate_hash_h, leaf_a.hash), (candidate_hash_i, leaf_a.hash)], + ) + .await; - // Should not get any backable candidates for the other para. - get_backable_candidates(&mut virtual_overseer, &leaf_a, 2.into(), vec![], 1, vec![]) - .await; - get_backable_candidates(&mut virtual_overseer, &leaf_a, 2.into(), vec![], 5, vec![]) - .await; + // Parachain fork. get_backable_candidates( &mut virtual_overseer, &leaf_a, - 2.into(), - vec![candidate_hash_a], + 1.into(), + vec![candidate_hash_a, candidate_hash_b, candidate_hash_c].into_iter().collect(), 1, vec![], ) .await; - // Test various scenarios with various counts. - - // empty required_path - { - get_backable_candidates( - &mut virtual_overseer, - &leaf_a, - 1.into(), - vec![], - 1, - vec![(candidate_hash_b, leaf_a.hash)], - ) - .await; - get_backable_candidates( - &mut virtual_overseer, - &leaf_a, - 1.into(), - vec![], - 2, - vec![(candidate_hash_b, leaf_a.hash), (candidate_hash_d, leaf_a.hash)], - ) - .await; - get_backable_candidates( - &mut virtual_overseer, - &leaf_a, - 1.into(), - vec![], - 4, - vec![ - (candidate_hash_a, leaf_a.hash), - (candidate_hash_c, leaf_a.hash), - (candidate_hash_h, leaf_a.hash), - (candidate_hash_i, leaf_a.hash), - ], - ) - .await; - } - - // required path of 1 - { - get_backable_candidates( - &mut virtual_overseer, - &leaf_a, - 1.into(), - vec![candidate_hash_a], - 1, - vec![(candidate_hash_c, leaf_a.hash)], - ) - .await; - get_backable_candidates( - &mut virtual_overseer, - &leaf_a, - 1.into(), - vec![candidate_hash_b], - 1, - vec![(candidate_hash_d, leaf_a.hash)], - ) - .await; - get_backable_candidates( - &mut virtual_overseer, - &leaf_a, - 1.into(), - vec![candidate_hash_a], - 2, - vec![(candidate_hash_c, leaf_a.hash), (candidate_hash_h, leaf_a.hash)], - ) - .await; - - // If the requested count exceeds the largest chain, return the longest - // chain we can get. - for count in 2..10 { - get_backable_candidates( - &mut virtual_overseer, - &leaf_a, - 1.into(), - vec![candidate_hash_b], - count, - vec![(candidate_hash_f, leaf_a.hash), (candidate_hash_g, leaf_a.hash)], - ) - .await; - } - } - - // required path of 2 - { - get_backable_candidates( - &mut virtual_overseer, - &leaf_a, - 1.into(), - vec![candidate_hash_b, candidate_hash_f], - 1, - vec![(candidate_hash_g, leaf_a.hash)], - ) - .await; - get_backable_candidates( - &mut virtual_overseer, - &leaf_a, - 1.into(), - vec![candidate_hash_a, candidate_hash_c], - 1, - vec![(candidate_hash_h, leaf_a.hash)], - ) - .await; - // If the requested count exceeds the largest chain, return the longest - // chain we can get. - for count in 4..10 { - get_backable_candidates( - &mut virtual_overseer, - &leaf_a, - 1.into(), - vec![candidate_hash_a, candidate_hash_c], - count, - vec![ - (candidate_hash_h, leaf_a.hash), - (candidate_hash_i, leaf_a.hash), - (candidate_hash_j, leaf_a.hash), - ], - ) - .await; - } - } - - // No more candidates in any chain. - { - let required_paths = vec![ - vec![candidate_hash_b, candidate_hash_f, candidate_hash_g], - vec![candidate_hash_b, candidate_hash_e], - vec![candidate_hash_b, candidate_hash_d], - vec![ - candidate_hash_a, - candidate_hash_c, - candidate_hash_h, - candidate_hash_i, - candidate_hash_j, - ], - ]; - for path in required_paths { - for count in 1..4 { - get_backable_candidates( - &mut virtual_overseer, - &leaf_a, - 1.into(), - path.clone(), - count, - vec![], - ) - .await; - } - } - } + // Non-existent candidate. + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_a, CandidateHash(Hash::from_low_u64_be(100))] + .into_iter() + .collect(), + 2, + vec![(candidate_hash_b, leaf_a.hash), (candidate_hash_d, leaf_a.hash)], + ) + .await; - // Should not get anything at the wrong path. + // Requested count is zero. get_backable_candidates( &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_d], - 1, + Ancestors::new(), + 0, vec![], ) .await; @@ -1531,8 +1362,8 @@ fn check_backable_query_multiple_candidates() { &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_b, candidate_hash_a], - 3, + vec![candidate_hash_a].into_iter().collect(), + 0, vec![], ) .await; @@ -1540,8 +1371,8 @@ fn check_backable_query_multiple_candidates() { &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_a, candidate_hash_c, candidate_hash_d], - 3, + vec![candidate_hash_a, candidate_hash_b].into_iter().collect(), + 0, vec![], ) .await; @@ -1853,8 +1684,8 @@ fn check_pvd_query() { assert_eq!(view.candidate_storage.len(), 2); } -// Test simultaneously activating and deactivating leaves, and simultaneously deactivating multiple -// leaves. +// Test simultaneously activating and deactivating leaves, and simultaneously deactivating +// multiple leaves. #[test] fn correctly_updates_leaves() { let test_state = TestState::default(); @@ -2048,7 +1879,7 @@ fn persists_pending_availability_candidate() { &mut virtual_overseer, &leaf_b, para_id, - vec![candidate_hash_a], + vec![candidate_hash_a].into_iter().collect(), 1, vec![(candidate_hash_b, leaf_b_hash)], ) @@ -2113,7 +1944,7 @@ fn backwards_compatible() { &mut virtual_overseer, &leaf_a, para_id, - vec![], + Ancestors::new(), 1, vec![(candidate_hash_a, candidate_relay_parent)], ) @@ -2135,7 +1966,15 @@ fn backwards_compatible() { ) .await; - get_backable_candidates(&mut virtual_overseer, &leaf_b, para_id, vec![], 1, vec![]).await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + Ancestors::new(), + 1, + vec![], + ) + .await; virtual_overseer }); @@ -2162,13 +2001,13 @@ fn uses_ancestry_only_within_session() { .await; assert_matches!( - virtual_overseer.recv().await, - AllMessages::RuntimeApi( - RuntimeApiMessage::Request(parent, RuntimeApiRequest::AsyncBackingParams(tx)) - ) if parent == hash => { - tx.send(Ok(AsyncBackingParams { max_candidate_depth: 0, allowed_ancestry_len: ancestry_len })).unwrap(); - } - ); + virtual_overseer.recv().await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request(parent, RuntimeApiRequest::AsyncBackingParams(tx)) + ) if parent == hash => { + tx.send(Ok(AsyncBackingParams { max_candidate_depth: 0, allowed_ancestry_len: ancestry_len + })).unwrap(); } + ); assert_matches!( virtual_overseer.recv().await, diff --git a/polkadot/node/core/provisioner/Cargo.toml b/polkadot/node/core/provisioner/Cargo.toml index 24cdfd6b57b373712bfdd05e897e2a6b0f1417b3..2a09e2b5b2cc83a0596c6f0ae153e66f8f6a1e66 100644 --- a/polkadot/node/core/provisioner/Cargo.toml +++ b/polkadot/node/core/provisioner/Cargo.toml @@ -20,9 +20,11 @@ polkadot-node-subsystem = { path = "../../subsystem" } polkadot-node-subsystem-util = { path = "../../subsystem-util" } futures-timer = "3.0.2" fatality = "0.0.6" +schnellru = "0.2.1" [dev-dependencies] sp-application-crypto = { path = "../../../../substrate/primitives/application-crypto" } sp-keystore = { path = "../../../../substrate/primitives/keystore" } polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers" } test-helpers = { package = "polkadot-primitives-test-helpers", path = "../../../primitives/test-helpers" } +rstest = "0.18.2" diff --git a/polkadot/node/core/provisioner/src/disputes/prioritized_selection/mod.rs b/polkadot/node/core/provisioner/src/disputes/prioritized_selection/mod.rs index cb55ce39bc89f3eeee3d1cb319351f659eace478..d7a5a8113369fbafb693098003a3f7d43413634d 100644 --- a/polkadot/node/core/provisioner/src/disputes/prioritized_selection/mod.rs +++ b/polkadot/node/core/provisioner/src/disputes/prioritized_selection/mod.rs @@ -52,7 +52,7 @@ pub const MAX_DISPUTE_VOTES_FORWARDED_TO_RUNTIME: usize = 200; /// `dispute-coordinator`. /// /// This value should be less than `MAX_DISPUTE_VOTES_FORWARDED_TO_RUNTIME`. Increase it in case -/// `provisioner` sends too many `QueryCandidateVotes` messages to `dispite-coordinator`. +/// `provisioner` sends too many `QueryCandidateVotes` messages to `dispute-coordinator`. #[cfg(not(test))] const VOTES_SELECTION_BATCH_SIZE: usize = 1_100; #[cfg(test)] diff --git a/polkadot/node/core/provisioner/src/error.rs b/polkadot/node/core/provisioner/src/error.rs index 376d69f276fc892e92828bf994b29f847fae31d0..aae3234c3cc49d19bdaf20b87fa71cf3f3276bfe 100644 --- a/polkadot/node/core/provisioner/src/error.rs +++ b/polkadot/node/core/provisioner/src/error.rs @@ -44,14 +44,17 @@ pub enum Error { #[error("failed to get block number")] CanceledBlockNumber(#[source] oneshot::Canceled), + #[error("failed to get session index")] + CanceledSessionIndex(#[source] oneshot::Canceled), + #[error("failed to get backed candidates")] CanceledBackedCandidates(#[source] oneshot::Canceled), #[error("failed to get votes on dispute")] CanceledCandidateVotes(#[source] oneshot::Canceled), - #[error("failed to get backable candidate from prospective parachains")] - CanceledBackableCandidate(#[source] oneshot::Canceled), + #[error("failed to get backable candidates from prospective parachains")] + CanceledBackableCandidates(#[source] oneshot::Canceled), #[error(transparent)] ChainApi(#[from] ChainApiError), @@ -71,11 +74,6 @@ pub enum Error { #[error("failed to send return message with Inherents")] InherentDataReturnChannel, - #[error( - "backed candidate does not correspond to selected candidate; check logic in provisioner" - )] - BackedCandidateOrderingProblem, - #[fatal] #[error("Failed to spawn background task")] FailedToSpawnBackgroundTask, diff --git a/polkadot/node/core/provisioner/src/lib.rs b/polkadot/node/core/provisioner/src/lib.rs index 51f768d782e0bff3d54efd70523120658b73bba2..3ccf499f325b61d6841e26a9a9259772ce5c67fb 100644 --- a/polkadot/node/core/provisioner/src/lib.rs +++ b/polkadot/node/core/provisioner/src/lib.rs @@ -24,24 +24,27 @@ use futures::{ channel::oneshot, future::BoxFuture, prelude::*, stream::FuturesUnordered, FutureExt, }; use futures_timer::Delay; +use schnellru::{ByLength, LruMap}; use polkadot_node_subsystem::{ jaeger, messages::{ - CandidateBackingMessage, ChainApiMessage, ProspectiveParachainsMessage, ProvisionableData, - ProvisionerInherentData, ProvisionerMessage, RuntimeApiRequest, + Ancestors, CandidateBackingMessage, ChainApiMessage, ProspectiveParachainsMessage, + ProvisionableData, ProvisionerInherentData, ProvisionerMessage, RuntimeApiRequest, }, overseer, ActivatedLeaf, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, PerLeafSpan, SpawnedSubsystem, SubsystemError, }; use polkadot_node_subsystem_util::{ has_required_runtime, request_availability_cores, request_persisted_validation_data, - runtime::{prospective_parachains_mode, ProspectiveParachainsMode}, + request_session_index_for_child, + runtime::{prospective_parachains_mode, request_node_features, ProspectiveParachainsMode}, TimeoutExt, }; use polkadot_primitives::{ - BackedCandidate, BlockNumber, CandidateHash, CandidateReceipt, CoreState, Hash, Id as ParaId, - OccupiedCoreAssumption, SignedAvailabilityBitfield, ValidatorIndex, + vstaging::{node_features::FeatureIndex, NodeFeatures}, + BackedCandidate, BlockNumber, CandidateHash, CandidateReceipt, CoreIndex, CoreState, Hash, + Id as ParaId, OccupiedCoreAssumption, SessionIndex, SignedAvailabilityBitfield, ValidatorIndex, }; use std::collections::{BTreeMap, HashMap}; @@ -77,11 +80,18 @@ impl ProvisionerSubsystem { } } +/// Per-session info we need for the provisioner subsystem. +pub struct PerSession { + prospective_parachains_mode: ProspectiveParachainsMode, + elastic_scaling_mvp: bool, +} + /// A per-relay-parent state for the provisioning subsystem. pub struct PerRelayParent { leaf: ActivatedLeaf, backed_candidates: Vec, prospective_parachains_mode: ProspectiveParachainsMode, + elastic_scaling_mvp: bool, signed_bitfields: Vec, is_inherent_ready: bool, awaiting_inherent: Vec>, @@ -89,13 +99,14 @@ pub struct PerRelayParent { } impl PerRelayParent { - fn new(leaf: ActivatedLeaf, prospective_parachains_mode: ProspectiveParachainsMode) -> Self { + fn new(leaf: ActivatedLeaf, per_session: &PerSession) -> Self { let span = PerLeafSpan::new(leaf.span.clone(), "provisioner"); Self { leaf, backed_candidates: Vec::new(), - prospective_parachains_mode, + prospective_parachains_mode: per_session.prospective_parachains_mode, + elastic_scaling_mvp: per_session.elastic_scaling_mvp, signed_bitfields: Vec::new(), is_inherent_ready: false, awaiting_inherent: Vec::new(), @@ -124,10 +135,17 @@ impl ProvisionerSubsystem { async fn run(mut ctx: Context, metrics: Metrics) -> FatalResult<()> { let mut inherent_delays = InherentDelays::new(); let mut per_relay_parent = HashMap::new(); + let mut per_session = LruMap::new(ByLength::new(2)); loop { - let result = - run_iteration(&mut ctx, &mut per_relay_parent, &mut inherent_delays, &metrics).await; + let result = run_iteration( + &mut ctx, + &mut per_relay_parent, + &mut per_session, + &mut inherent_delays, + &metrics, + ) + .await; match result { Ok(()) => break, @@ -142,6 +160,7 @@ async fn run(mut ctx: Context, metrics: Metrics) -> FatalResult<()> { async fn run_iteration( ctx: &mut Context, per_relay_parent: &mut HashMap, + per_session: &mut LruMap, inherent_delays: &mut InherentDelays, metrics: &Metrics, ) -> Result<(), Error> { @@ -151,7 +170,7 @@ async fn run_iteration( // Map the error to ensure that the subsystem exits when the overseer is gone. match from_overseer.map_err(Error::OverseerExited)? { FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)) => - handle_active_leaves_update(ctx.sender(), update, per_relay_parent, inherent_delays).await?, + handle_active_leaves_update(ctx.sender(), update, per_relay_parent, per_session, inherent_delays).await?, FromOrchestra::Signal(OverseerSignal::BlockFinalized(..)) => {}, FromOrchestra::Signal(OverseerSignal::Conclude) => return Ok(()), FromOrchestra::Communication { msg } => { @@ -183,6 +202,7 @@ async fn handle_active_leaves_update( sender: &mut impl overseer::ProvisionerSenderTrait, update: ActiveLeavesUpdate, per_relay_parent: &mut HashMap, + per_session: &mut LruMap, inherent_delays: &mut InherentDelays, ) -> Result<(), Error> { gum::trace!(target: LOG_TARGET, "Handle ActiveLeavesUpdate"); @@ -191,10 +211,31 @@ async fn handle_active_leaves_update( } if let Some(leaf) = update.activated { + let session_index = request_session_index_for_child(leaf.hash, sender) + .await + .await + .map_err(Error::CanceledSessionIndex)??; + if per_session.get(&session_index).is_none() { + let prospective_parachains_mode = + prospective_parachains_mode(sender, leaf.hash).await?; + let elastic_scaling_mvp = request_node_features(leaf.hash, session_index, sender) + .await? + .unwrap_or(NodeFeatures::EMPTY) + .get(FeatureIndex::ElasticScalingMVP as usize) + .map(|b| *b) + .unwrap_or(false); + + per_session.insert( + session_index, + PerSession { prospective_parachains_mode, elastic_scaling_mvp }, + ); + } + + let session_info = per_session.get(&session_index).expect("Just inserted"); + gum::trace!(target: LOG_TARGET, leaf_hash=?leaf.hash, "Adding delay"); - let prospective_parachains_mode = prospective_parachains_mode(sender, leaf.hash).await?; let delay_fut = Delay::new(PRE_PROPOSE_TIMEOUT).map(move |_| leaf.hash).boxed(); - per_relay_parent.insert(leaf.hash, PerRelayParent::new(leaf, prospective_parachains_mode)); + per_relay_parent.insert(leaf.hash, PerRelayParent::new(leaf, session_info)); inherent_delays.push(delay_fut); } @@ -253,6 +294,7 @@ async fn send_inherent_data_bg( let signed_bitfields = per_relay_parent.signed_bitfields.clone(); let backed_candidates = per_relay_parent.backed_candidates.clone(); let mode = per_relay_parent.prospective_parachains_mode; + let elastic_scaling_mvp = per_relay_parent.elastic_scaling_mvp; let span = per_relay_parent.span.child("req-inherent-data"); let mut sender = ctx.sender().clone(); @@ -272,6 +314,7 @@ async fn send_inherent_data_bg( &signed_bitfields, &backed_candidates, mode, + elastic_scaling_mvp, return_senders, &mut sender, &metrics, @@ -383,6 +426,7 @@ async fn send_inherent_data( bitfields: &[SignedAvailabilityBitfield], candidates: &[CandidateReceipt], prospective_parachains_mode: ProspectiveParachainsMode, + elastic_scaling_mvp: bool, return_senders: Vec>, from_job: &mut impl overseer::ProvisionerSenderTrait, metrics: &Metrics, @@ -434,6 +478,7 @@ async fn send_inherent_data( &bitfields, candidates, prospective_parachains_mode, + elastic_scaling_mvp, leaf.hash, from_job, ) @@ -553,11 +598,11 @@ async fn select_candidate_hashes_from_tracked( candidates: &[CandidateReceipt], relay_parent: Hash, sender: &mut impl overseer::ProvisionerSenderTrait, -) -> Result, Error> { +) -> Result>, Error> { let block_number = get_block_number_under_construction(relay_parent, sender).await?; let mut selected_candidates = - Vec::with_capacity(candidates.len().min(availability_cores.len())); + HashMap::with_capacity(candidates.len().min(availability_cores.len())); gum::debug!( target: LOG_TARGET, @@ -591,6 +636,12 @@ async fn select_candidate_hashes_from_tracked( CoreState::Free => continue, }; + if selected_candidates.contains_key(&scheduled_core.para_id) { + // We already picked a candidate for this parachain. Elastic scaling only works with + // prospective parachains mode. + continue + } + let validation_data = match request_persisted_validation_data( relay_parent, scheduled_core.para_id, @@ -624,7 +675,10 @@ async fn select_candidate_hashes_from_tracked( "Selected candidate receipt", ); - selected_candidates.push((candidate_hash, candidate.descriptor.relay_parent)); + selected_candidates.insert( + candidate.descriptor.para_id, + vec![(candidate_hash, candidate.descriptor.relay_parent)], + ); } } @@ -637,63 +691,93 @@ async fn select_candidate_hashes_from_tracked( /// Should be called when prospective parachains are enabled. async fn request_backable_candidates( availability_cores: &[CoreState], + elastic_scaling_mvp: bool, bitfields: &[SignedAvailabilityBitfield], relay_parent: Hash, sender: &mut impl overseer::ProvisionerSenderTrait, -) -> Result, Error> { +) -> Result>, Error> { let block_number = get_block_number_under_construction(relay_parent, sender).await?; - let mut selected_candidates = Vec::with_capacity(availability_cores.len()); + // Record how many cores are scheduled for each paraid. Use a BTreeMap because + // we'll need to iterate through them. + let mut scheduled_cores_per_para: BTreeMap = BTreeMap::new(); + // The on-chain ancestors of a para present in availability-cores. + let mut ancestors: HashMap = + HashMap::with_capacity(availability_cores.len()); for (core_idx, core) in availability_cores.iter().enumerate() { - let (para_id, required_path) = match core { + let core_idx = CoreIndex(core_idx as u32); + match core { CoreState::Scheduled(scheduled_core) => { - // The core is free, pick the first eligible candidate from - // the fragment tree. - (scheduled_core.para_id, Vec::new()) + *scheduled_cores_per_para.entry(scheduled_core.para_id).or_insert(0) += 1; }, CoreState::Occupied(occupied_core) => { - if bitfields_indicate_availability(core_idx, bitfields, &occupied_core.availability) - { + let is_available = bitfields_indicate_availability( + core_idx.0 as usize, + bitfields, + &occupied_core.availability, + ); + + if is_available { + ancestors + .entry(occupied_core.para_id()) + .or_default() + .insert(occupied_core.candidate_hash); + if let Some(ref scheduled_core) = occupied_core.next_up_on_available { - // The candidate occupying the core is available, choose its - // child in the fragment tree. - // - // TODO: doesn't work for on-demand parachains. We lean hard on the - // assumption that cores are fixed to specific parachains within a session. - // https://github.com/paritytech/polkadot/issues/5492 - (scheduled_core.para_id, vec![occupied_core.candidate_hash]) - } else { - continue - } - } else { - if occupied_core.time_out_at != block_number { - continue + // Request a new backable candidate for the newly scheduled para id. + *scheduled_cores_per_para.entry(scheduled_core.para_id).or_insert(0) += 1; } + } else if occupied_core.time_out_at <= block_number { + // Timed out before being available. + if let Some(ref scheduled_core) = occupied_core.next_up_on_time_out { // Candidate's availability timed out, practically same as scheduled. - (scheduled_core.para_id, Vec::new()) - } else { - continue + *scheduled_cores_per_para.entry(scheduled_core.para_id).or_insert(0) += 1; } + } else { + // Not timed out and not available. + ancestors + .entry(occupied_core.para_id()) + .or_default() + .insert(occupied_core.candidate_hash); } }, CoreState::Free => continue, }; + } - let response = get_backable_candidate(relay_parent, para_id, required_path, sender).await?; + let mut selected_candidates: HashMap> = + HashMap::with_capacity(scheduled_cores_per_para.len()); - match response { - Some((hash, relay_parent)) => selected_candidates.push((hash, relay_parent)), - None => { - gum::debug!( - target: LOG_TARGET, - leaf_hash = ?relay_parent, - core = core_idx, - "No backable candidate returned by prospective parachains", - ); - }, + for (para_id, core_count) in scheduled_cores_per_para { + let para_ancestors = ancestors.remove(¶_id).unwrap_or_default(); + + // If elastic scaling MVP is disabled, only allow one candidate per parachain. + if !elastic_scaling_mvp && core_count > 1 { + continue } + + let response = get_backable_candidates( + relay_parent, + para_id, + para_ancestors, + core_count as u32, + sender, + ) + .await?; + + if response.is_empty() { + gum::debug!( + target: LOG_TARGET, + leaf_hash = ?relay_parent, + ?para_id, + "No backable candidate returned by prospective parachains", + ); + continue + } + + selected_candidates.insert(para_id, response); } Ok(selected_candidates) @@ -706,6 +790,7 @@ async fn select_candidates( bitfields: &[SignedAvailabilityBitfield], candidates: &[CandidateReceipt], prospective_parachains_mode: ProspectiveParachainsMode, + elastic_scaling_mvp: bool, relay_parent: Hash, sender: &mut impl overseer::ProvisionerSenderTrait, ) -> Result, Error> { @@ -715,7 +800,14 @@ async fn select_candidates( let selected_candidates = match prospective_parachains_mode { ProspectiveParachainsMode::Enabled { .. } => - request_backable_candidates(availability_cores, bitfields, relay_parent, sender).await?, + request_backable_candidates( + availability_cores, + elastic_scaling_mvp, + bitfields, + relay_parent, + sender, + ) + .await?, ProspectiveParachainsMode::Disabled => select_candidate_hashes_from_tracked( availability_cores, @@ -726,6 +818,7 @@ async fn select_candidates( ) .await?, }; + gum::debug!(target: LOG_TARGET, ?selected_candidates, "Got backable candidates"); // now get the backed candidates corresponding to these candidate receipts let (tx, rx) = oneshot::channel(); @@ -733,51 +826,38 @@ async fn select_candidates( selected_candidates.clone(), tx, )); - let mut candidates = rx.await.map_err(|err| Error::CanceledBackedCandidates(err))?; + let candidates = rx.await.map_err(|err| Error::CanceledBackedCandidates(err))?; gum::trace!(target: LOG_TARGET, leaf_hash=?relay_parent, "Got {} backed candidates", candidates.len()); - // `selected_candidates` is generated in ascending order by core index, and - // `GetBackedCandidates` _should_ preserve that property, but let's just make sure. - // - // We can't easily map from `BackedCandidate` to `core_idx`, but we know that every selected - // candidate maps to either 0 or 1 backed candidate, and the hashes correspond. Therefore, by - // checking them in order, we can ensure that the backed candidates are also in order. - let mut backed_idx = 0; - for selected in selected_candidates { - if selected.0 == - candidates.get(backed_idx).ok_or(Error::BackedCandidateOrderingProblem)?.hash() - { - backed_idx += 1; - } - } - if candidates.len() != backed_idx { - Err(Error::BackedCandidateOrderingProblem)?; - } - // keep only one candidate with validation code. let mut with_validation_code = false; - candidates.retain(|c| { - if c.candidate.commitments.new_validation_code.is_some() { - if with_validation_code { - return false + // merge the candidates into a common collection, preserving the order + let mut merged_candidates = Vec::with_capacity(availability_cores.len()); + + for para_candidates in candidates.into_values() { + for candidate in para_candidates { + if candidate.candidate().commitments.new_validation_code.is_some() { + if with_validation_code { + break + } else { + with_validation_code = true; + } } - with_validation_code = true; + merged_candidates.push(candidate); } - - true - }); + } gum::debug!( target: LOG_TARGET, - n_candidates = candidates.len(), + n_candidates = merged_candidates.len(), n_cores = availability_cores.len(), ?relay_parent, "Selected backed candidates", ); - Ok(candidates) + Ok(merged_candidates) } /// Produces a block number 1 higher than that of the relay parent @@ -796,28 +876,27 @@ async fn get_block_number_under_construction( } } -/// Requests backable candidate from Prospective Parachains based on -/// the given path in the fragment tree. -async fn get_backable_candidate( +/// Requests backable candidates from Prospective Parachains based on +/// the given ancestors in the fragment tree. The ancestors may not be ordered. +async fn get_backable_candidates( relay_parent: Hash, para_id: ParaId, - required_path: Vec, + ancestors: Ancestors, + count: u32, sender: &mut impl overseer::ProvisionerSenderTrait, -) -> Result, Error> { +) -> Result, Error> { let (tx, rx) = oneshot::channel(); sender .send_message(ProspectiveParachainsMessage::GetBackableCandidates( relay_parent, para_id, - 1, // core count hardcoded to 1, until elastic scaling is implemented and enabled. - required_path, + count, + ancestors, tx, )) .await; - rx.await - .map_err(Error::CanceledBackableCandidate) - .map(|res| res.get(0).copied()) + rx.await.map_err(Error::CanceledBackableCandidates) } /// The availability bitfield for a given core is the transpose diff --git a/polkadot/node/core/provisioner/src/tests.rs b/polkadot/node/core/provisioner/src/tests.rs index b26df8ddb910465fc9920d462fd9266935112175..823b1d86e4617691ecfafc132fd02e9a3721a460 100644 --- a/polkadot/node/core/provisioner/src/tests.rs +++ b/polkadot/node/core/provisioner/src/tests.rs @@ -22,6 +22,9 @@ use polkadot_primitives::{OccupiedCore, ScheduledCore}; const MOCK_GROUP_SIZE: usize = 5; pub fn occupied_core(para_id: u32) -> CoreState { + let mut candidate_descriptor = dummy_candidate_descriptor(dummy_hash()); + candidate_descriptor.para_id = para_id.into(); + CoreState::Occupied(OccupiedCore { group_responsible: para_id.into(), next_up_on_available: None, @@ -29,7 +32,7 @@ pub fn occupied_core(para_id: u32) -> CoreState { time_out_at: 200_u32, next_up_on_time_out: None, availability: bitvec![u8, bitvec::order::Lsb0; 0; 32], - candidate_descriptor: dummy_candidate_descriptor(dummy_hash()), + candidate_descriptor, candidate_hash: Default::default(), }) } @@ -254,10 +257,58 @@ mod select_candidates { use polkadot_primitives::{ BlockNumber, CandidateCommitments, CommittedCandidateReceipt, PersistedValidationData, }; + use rstest::rstest; + use std::ops::Not; + use CoreState::{Free, Scheduled}; const BLOCK_UNDER_PRODUCTION: BlockNumber = 128; - // For test purposes, we always return this set of availability cores: + fn dummy_candidate_template() -> CandidateReceipt { + let empty_hash = PersistedValidationData::::default().hash(); + + let mut descriptor_template = dummy_candidate_descriptor(dummy_hash()); + descriptor_template.persisted_validation_data_hash = empty_hash; + CandidateReceipt { + descriptor: descriptor_template, + commitments_hash: CandidateCommitments::default().hash(), + } + } + + fn make_candidates( + core_count: usize, + expected_backed_indices: Vec, + ) -> (Vec, Vec) { + let candidate_template = dummy_candidate_template(); + let candidates: Vec<_> = std::iter::repeat(candidate_template) + .take(core_count) + .enumerate() + .map(|(idx, mut candidate)| { + candidate.descriptor.para_id = idx.into(); + candidate + }) + .collect(); + + let expected_backed = expected_backed_indices + .iter() + .map(|&idx| candidates[idx].clone()) + .map(|c| { + BackedCandidate::new( + CommittedCandidateReceipt { + descriptor: c.descriptor.clone(), + commitments: Default::default(), + }, + Vec::new(), + default_bitvec(MOCK_GROUP_SIZE), + None, + ) + }) + .collect(); + let candidate_hashes = candidates.into_iter().map(|c| c.hash()).collect(); + + (candidate_hashes, expected_backed) + } + + // For testing only one core assigned to a parachain, we return this set of availability cores: // // [ // 0: Free, @@ -273,10 +324,70 @@ mod select_candidates { // 10: Occupied(both next_up set, not available, timeout), // 11: Occupied(next_up_on_available and available, but different successor para_id) // ] - fn mock_availability_cores() -> Vec { - use std::ops::Not; - use CoreState::{Free, Scheduled}; + fn mock_availability_cores_one_per_para() -> Vec { + vec![ + // 0: Free, + Free, + // 1: Scheduled(default), + Scheduled(scheduled_core(1)), + // 2: Occupied(no next_up set), + occupied_core(2), + // 3: Occupied(next_up_on_available set but not available), + build_occupied_core(3, |core| { + core.next_up_on_available = Some(scheduled_core(3)); + }), + // 4: Occupied(next_up_on_available set and available), + build_occupied_core(4, |core| { + core.next_up_on_available = Some(scheduled_core(4)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(41)); + }), + // 5: Occupied(next_up_on_time_out set but not timeout), + build_occupied_core(5, |core| { + core.next_up_on_time_out = Some(scheduled_core(5)); + }), + // 6: Occupied(next_up_on_time_out set and timeout but available), + build_occupied_core(6, |core| { + core.next_up_on_time_out = Some(scheduled_core(6)); + core.time_out_at = BLOCK_UNDER_PRODUCTION; + core.availability = core.availability.clone().not(); + }), + // 7: Occupied(next_up_on_time_out set and timeout and not available), + build_occupied_core(7, |core| { + core.next_up_on_time_out = Some(scheduled_core(7)); + core.time_out_at = BLOCK_UNDER_PRODUCTION; + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(71)); + }), + // 8: Occupied(both next_up set, available), + build_occupied_core(8, |core| { + core.next_up_on_available = Some(scheduled_core(8)); + core.next_up_on_time_out = Some(scheduled_core(8)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(81)); + }), + // 9: Occupied(both next_up set, not available, no timeout), + build_occupied_core(9, |core| { + core.next_up_on_available = Some(scheduled_core(9)); + core.next_up_on_time_out = Some(scheduled_core(9)); + }), + // 10: Occupied(both next_up set, not available, timeout), + build_occupied_core(10, |core| { + core.next_up_on_available = Some(scheduled_core(10)); + core.next_up_on_time_out = Some(scheduled_core(10)); + core.time_out_at = BLOCK_UNDER_PRODUCTION; + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(101)); + }), + // 11: Occupied(next_up_on_available and available, but different successor para_id) + build_occupied_core(11, |core| { + core.next_up_on_available = Some(scheduled_core(12)); + core.availability = core.availability.clone().not(); + }), + ] + } + // For test purposes with multiple possible cores assigned to a para, we always return this set + // of availability cores: + fn mock_availability_cores_multiple_per_para() -> Vec { vec![ // 0: Free, Free, @@ -292,6 +403,7 @@ mod select_candidates { build_occupied_core(4, |core| { core.next_up_on_available = Some(scheduled_core(4)); core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(41)); }), // 5: Occupied(next_up_on_time_out set but not timeout), build_occupied_core(5, |core| { @@ -307,12 +419,14 @@ mod select_candidates { build_occupied_core(7, |core| { core.next_up_on_time_out = Some(scheduled_core(7)); core.time_out_at = BLOCK_UNDER_PRODUCTION; + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(71)); }), // 8: Occupied(both next_up set, available), build_occupied_core(8, |core| { core.next_up_on_available = Some(scheduled_core(8)); core.next_up_on_time_out = Some(scheduled_core(8)); core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(81)); }), // 9: Occupied(both next_up set, not available, no timeout), build_occupied_core(9, |core| { @@ -324,29 +438,136 @@ mod select_candidates { core.next_up_on_available = Some(scheduled_core(10)); core.next_up_on_time_out = Some(scheduled_core(10)); core.time_out_at = BLOCK_UNDER_PRODUCTION; + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(101)); }), // 11: Occupied(next_up_on_available and available, but different successor para_id) build_occupied_core(11, |core| { core.next_up_on_available = Some(scheduled_core(12)); core.availability = core.availability.clone().not(); }), + // 12-14: Occupied(next_up_on_available and available, same para_id). + build_occupied_core(12, |core| { + core.next_up_on_available = Some(scheduled_core(12)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(121)); + }), + build_occupied_core(12, |core| { + core.next_up_on_available = Some(scheduled_core(12)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(122)); + }), + build_occupied_core(12, |core| { + core.next_up_on_available = Some(scheduled_core(12)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(123)); + }), + // 15: Scheduled on same para_id as 12-14. + Scheduled(scheduled_core(12)), + // 16: Occupied(13, no next_up set, not available) + build_occupied_core(13, |core| { + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(131)); + }), + // 17: Occupied(13, no next_up set, available) + build_occupied_core(13, |core| { + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(132)); + }), + // 18: Occupied(13, next_up_on_available set to 13 but not available) + build_occupied_core(13, |core| { + core.next_up_on_available = Some(scheduled_core(13)); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(133)); + }), + // 19: Occupied(13, next_up_on_available set to 13 and available) + build_occupied_core(13, |core| { + core.next_up_on_available = Some(scheduled_core(13)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(134)); + }), + // 20: Occupied(13, next_up_on_time_out set to 13 but not timeout) + build_occupied_core(13, |core| { + core.next_up_on_time_out = Some(scheduled_core(13)); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(135)); + }), + // 21: Occupied(13, next_up_on_available set to 14 and available) + build_occupied_core(13, |core| { + core.next_up_on_available = Some(scheduled_core(14)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(136)); + }), + // 22: Occupied(13, next_up_on_available set to 14 but not available) + build_occupied_core(13, |core| { + core.next_up_on_available = Some(scheduled_core(14)); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(137)); + }), + // 23: Occupied(13, both next_up set to 14, available) + build_occupied_core(13, |core| { + core.next_up_on_available = Some(scheduled_core(14)); + core.next_up_on_time_out = Some(scheduled_core(14)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(138)); + }), + // 24: Occupied(13, both next_up set to 14, not available, timeout) + build_occupied_core(13, |core| { + core.next_up_on_available = Some(scheduled_core(14)); + core.next_up_on_time_out = Some(scheduled_core(14)); + core.time_out_at = BLOCK_UNDER_PRODUCTION; + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(1399)); + }), + // 25: Occupied(13, next_up_on_available and available, but successor para_id 15) + build_occupied_core(13, |core| { + core.next_up_on_available = Some(scheduled_core(15)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(139)); + }), + // 26: Occupied(15, next_up_on_available and available, but successor para_id 13) + build_occupied_core(15, |core| { + core.next_up_on_available = Some(scheduled_core(13)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(151)); + }), + // 27: Occupied(15, both next_up, both available and timed out) + build_occupied_core(15, |core| { + core.next_up_on_available = Some(scheduled_core(15)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(152)); + core.time_out_at = BLOCK_UNDER_PRODUCTION; + }), + // 28: Occupied(13, both next_up set to 13, not available) + build_occupied_core(13, |core| { + core.next_up_on_available = Some(scheduled_core(13)); + core.next_up_on_time_out = Some(scheduled_core(13)); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(1398)); + }), + // 29: Occupied(13, both next_up set to 13, not available, timeout) + build_occupied_core(13, |core| { + core.next_up_on_available = Some(scheduled_core(13)); + core.next_up_on_time_out = Some(scheduled_core(13)); + core.time_out_at = BLOCK_UNDER_PRODUCTION; + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(1397)); + }), ] } async fn mock_overseer( mut receiver: mpsc::UnboundedReceiver, - expected: Vec, + mock_availability_cores: Vec, + mut expected: Vec, + mut expected_ancestors: HashMap, Ancestors>, prospective_parachains_mode: ProspectiveParachainsMode, ) { use ChainApiMessage::BlockNumber; use RuntimeApiMessage::Request; + let mut backed = expected.clone().into_iter().fold(HashMap::new(), |mut acc, candidate| { + acc.entry(candidate.descriptor().para_id).or_insert(vec![]).push(candidate); + acc + }); + + expected.sort_by_key(|c| c.candidate().descriptor.para_id); let mut candidates_iter = expected .iter() .map(|candidate| (candidate.hash(), candidate.descriptor().relay_parent)); - let mut backed_iter = expected.clone().into_iter(); - while let Some(from_job) = receiver.next().await { match from_job { AllMessages::ChainApi(BlockNumber(_relay_parent, tx)) => @@ -356,16 +577,35 @@ mod select_candidates { PersistedValidationDataReq(_para_id, _assumption, tx), )) => tx.send(Ok(Some(Default::default()))).unwrap(), AllMessages::RuntimeApi(Request(_parent_hash, AvailabilityCores(tx))) => - tx.send(Ok(mock_availability_cores())).unwrap(), + tx.send(Ok(mock_availability_cores.clone())).unwrap(), AllMessages::CandidateBacking(CandidateBackingMessage::GetBackedCandidates( hashes, sender, )) => { - let response: Vec = - backed_iter.by_ref().take(hashes.len()).collect(); - let expected_hashes: Vec<(CandidateHash, Hash)> = response + let mut response: HashMap> = HashMap::new(); + for (para_id, requested_candidates) in hashes.clone() { + response.insert( + para_id, + backed + .get_mut(¶_id) + .unwrap() + .drain(0..requested_candidates.len()) + .collect(), + ); + } + let expected_hashes: HashMap> = response .iter() - .map(|candidate| (candidate.hash(), candidate.descriptor().relay_parent)) + .map(|(para_id, candidates)| { + ( + *para_id, + candidates + .iter() + .map(|candidate| { + (candidate.hash(), candidate.descriptor().relay_parent) + }) + .collect(), + ) + }) .collect(); assert_eq!(expected_hashes, hashes); @@ -373,35 +613,71 @@ mod select_candidates { let _ = sender.send(response); }, AllMessages::ProspectiveParachains( - ProspectiveParachainsMessage::GetBackableCandidates(_, _, count, _, tx), - ) => { - assert_eq!(count, 1); - - match prospective_parachains_mode { - ProspectiveParachainsMode::Enabled { .. } => { - let _ = - tx.send(candidates_iter.next().map_or_else(Vec::new, |c| vec![c])); - }, - ProspectiveParachainsMode::Disabled => - panic!("unexpected prospective parachains request"), - } + ProspectiveParachainsMessage::GetBackableCandidates( + _, + _para_id, + count, + actual_ancestors, + tx, + ), + ) => match prospective_parachains_mode { + ProspectiveParachainsMode::Enabled { .. } => { + assert!(count > 0); + let candidates = + (&mut candidates_iter).take(count as usize).collect::>(); + assert_eq!(candidates.len(), count as usize); + + if !expected_ancestors.is_empty() { + if let Some(expected_required_ancestors) = expected_ancestors.remove( + &(candidates + .clone() + .into_iter() + .take(actual_ancestors.len()) + .map(|(c_hash, _)| c_hash) + .collect::>()), + ) { + assert_eq!(expected_required_ancestors, actual_ancestors); + } else { + assert_eq!(actual_ancestors.len(), 0); + } + } + + let _ = tx.send(candidates); + }, + ProspectiveParachainsMode::Disabled => + panic!("unexpected prospective parachains request"), }, _ => panic!("Unexpected message: {:?}", from_job), } } + + if let ProspectiveParachainsMode::Enabled { .. } = prospective_parachains_mode { + assert_eq!(candidates_iter.next(), None); + } + assert_eq!(expected_ancestors.len(), 0); } - #[test] - fn can_succeed() { + #[rstest] + #[case(ProspectiveParachainsMode::Disabled)] + #[case(ProspectiveParachainsMode::Enabled {max_candidate_depth: 0, allowed_ancestry_len: 0})] + fn can_succeed(#[case] prospective_parachains_mode: ProspectiveParachainsMode) { test_harness( - |r| mock_overseer(r, Vec::new(), ProspectiveParachainsMode::Disabled), + |r| { + mock_overseer( + r, + Vec::new(), + Vec::new(), + HashMap::new(), + prospective_parachains_mode, + ) + }, |mut tx: TestSubsystemSender| async move { - let prospective_parachains_mode = ProspectiveParachainsMode::Disabled; select_candidates( &[], &[], &[], prospective_parachains_mode, + false, Default::default(), &mut tx, ) @@ -411,22 +687,22 @@ mod select_candidates { ) } - // this tests that only the appropriate candidates get selected. - // To accomplish this, we supply a candidate list containing one candidate per possible core; - // the candidate selection algorithm must filter them to the appropriate set - #[test] - fn selects_correct_candidates() { - let mock_cores = mock_availability_cores(); - - let empty_hash = PersistedValidationData::::default().hash(); - - let mut descriptor_template = dummy_candidate_descriptor(dummy_hash()); - descriptor_template.persisted_validation_data_hash = empty_hash; - let candidate_template = CandidateReceipt { - descriptor: descriptor_template, - commitments_hash: CandidateCommitments::default().hash(), - }; - + // Test candidate selection when prospective parachains mode is disabled. + // This tests that only the appropriate candidates get selected when prospective parachains mode + // is disabled. To accomplish this, we supply a candidate list containing one candidate per + // possible core; the candidate selection algorithm must filter them to the appropriate set + #[rstest] + // why those particular indices? see the comments on mock_availability_cores_*() functions. + #[case(mock_availability_cores_one_per_para(), vec![1, 4, 7, 8, 10], true)] + #[case(mock_availability_cores_one_per_para(), vec![1, 4, 7, 8, 10], false)] + #[case(mock_availability_cores_multiple_per_para(), vec![1, 4, 7, 8, 10, 12, 13, 14, 15], true)] + #[case(mock_availability_cores_multiple_per_para(), vec![1, 4, 7, 8, 10, 12, 13, 14, 15], false)] + fn test_in_subsystem_selection( + #[case] mock_cores: Vec, + #[case] expected_candidates: Vec, + #[case] elastic_scaling_mvp: bool, + ) { + let candidate_template = dummy_candidate_template(); let candidates: Vec<_> = std::iter::repeat(candidate_template) .take(mock_cores.len()) .enumerate() @@ -453,31 +729,43 @@ mod select_candidates { }) .collect(); - // why those particular indices? see the comments on mock_availability_cores() let expected_candidates: Vec<_> = - [1, 4, 7, 8, 10].iter().map(|&idx| candidates[idx].clone()).collect(); + expected_candidates.into_iter().map(|idx| candidates[idx].clone()).collect(); let prospective_parachains_mode = ProspectiveParachainsMode::Disabled; let expected_backed = expected_candidates .iter() - .map(|c| BackedCandidate { - candidate: CommittedCandidateReceipt { - descriptor: c.descriptor.clone(), - commitments: Default::default(), - }, - validity_votes: Vec::new(), - validator_indices: default_bitvec(MOCK_GROUP_SIZE), + .map(|c| { + BackedCandidate::new( + CommittedCandidateReceipt { + descriptor: c.descriptor().clone(), + commitments: Default::default(), + }, + Vec::new(), + default_bitvec(MOCK_GROUP_SIZE), + None, + ) }) .collect(); + let mock_cores_clone = mock_cores.clone(); test_harness( - |r| mock_overseer(r, expected_backed, prospective_parachains_mode), + |r| { + mock_overseer( + r, + mock_cores_clone, + expected_backed, + HashMap::new(), + prospective_parachains_mode, + ) + }, |mut tx: TestSubsystemSender| async move { - let result = select_candidates( + let result: Vec = select_candidates( &mock_cores, &[], &candidates, prospective_parachains_mode, + elastic_scaling_mvp, Default::default(), &mut tx, ) @@ -486,7 +774,7 @@ mod select_candidates { result.into_iter().for_each(|c| { assert!( - expected_candidates.iter().any(|c2| c.candidate.corresponds_to(c2)), + expected_candidates.iter().any(|c2| c.candidate().corresponds_to(c2)), "Failed to find candidate: {:?}", c, ) @@ -495,20 +783,29 @@ mod select_candidates { ) } - #[test] - fn selects_max_one_code_upgrade() { - let mock_cores = mock_availability_cores(); + #[rstest] + #[case(ProspectiveParachainsMode::Disabled)] + #[case(ProspectiveParachainsMode::Enabled {max_candidate_depth: 0, allowed_ancestry_len: 0})] + fn selects_max_one_code_upgrade_one_core_per_para( + #[case] prospective_parachains_mode: ProspectiveParachainsMode, + ) { + let mock_cores = mock_availability_cores_one_per_para(); let empty_hash = PersistedValidationData::::default().hash(); // why those particular indices? see the comments on mock_availability_cores() - // the first candidate with code is included out of [1, 4, 7, 8, 10]. - let cores = [1, 4, 7, 8, 10]; + // the first candidate with code is included out of [1, 4, 7, 8, 10, 12]. + let cores = [1, 4, 7, 8, 10, 12]; let cores_with_code = [1, 4, 8]; - let expected_cores = [1, 7, 10]; + // We can't be sure which one code upgrade the provisioner will pick. We can only assert + // that it only picks one. These are the possible cores for which the provisioner will + // supply candidates. + // There are multiple possibilities depending on which code upgrade it + // chooses. + let possible_expected_cores = [[1, 7, 10, 12], [4, 7, 10, 12], [7, 8, 10, 12]]; - let committed_receipts: Vec<_> = (0..mock_cores.len()) + let committed_receipts: Vec<_> = (0..=mock_cores.len()) .map(|i| { let mut descriptor = dummy_candidate_descriptor(dummy_hash()); descriptor.para_id = i.into(); @@ -532,10 +829,13 @@ mod select_candidates { // Build possible outputs from select_candidates let backed_candidates: Vec<_> = committed_receipts .iter() - .map(|committed_receipt| BackedCandidate { - candidate: committed_receipt.clone(), - validity_votes: Vec::new(), - validator_indices: default_bitvec(MOCK_GROUP_SIZE), + .map(|committed_receipt| { + BackedCandidate::new( + committed_receipt.clone(), + Vec::new(), + default_bitvec(MOCK_GROUP_SIZE), + None, + ) }) .collect(); @@ -543,30 +843,212 @@ mod select_candidates { // Then, some of them get filtered due to new validation code rule. let expected_backed: Vec<_> = cores.iter().map(|&idx| backed_candidates[idx].clone()).collect(); - let expected_backed_filtered: Vec<_> = - expected_cores.iter().map(|&idx| candidates[idx].clone()).collect(); + let expected_backed_filtered: Vec> = possible_expected_cores + .iter() + .map(|indices| indices.iter().map(|&idx| candidates[idx].clone()).collect()) + .collect(); - let prospective_parachains_mode = ProspectiveParachainsMode::Disabled; + let mock_cores_clone = mock_cores.clone(); test_harness( - |r| mock_overseer(r, expected_backed, prospective_parachains_mode), + |r| { + mock_overseer( + r, + mock_cores_clone, + expected_backed, + HashMap::new(), + prospective_parachains_mode, + ) + }, |mut tx: TestSubsystemSender| async move { let result = select_candidates( &mock_cores, &[], &candidates, prospective_parachains_mode, + false, Default::default(), &mut tx, ) .await .unwrap(); - assert_eq!(result.len(), 3); + assert_eq!(result.len(), 4); + assert!(expected_backed_filtered.iter().any(|expected_backed_filtered| { + result.clone().into_iter().all(|c| { + expected_backed_filtered.iter().any(|c2| c.candidate().corresponds_to(c2)) + }) + })); + }, + ) + } + + #[test] + fn selects_max_one_code_upgrade_multiple_cores_per_para() { + let prospective_parachains_mode = + ProspectiveParachainsMode::Enabled { max_candidate_depth: 0, allowed_ancestry_len: 0 }; + let mock_cores = vec![ + // 0: Scheduled(default), + Scheduled(scheduled_core(1)), + // 1: Scheduled(default), + Scheduled(scheduled_core(2)), + // 2: Scheduled(default), + Scheduled(scheduled_core(2)), + // 3: Scheduled(default), + Scheduled(scheduled_core(2)), + // 4: Scheduled(default), + Scheduled(scheduled_core(3)), + // 5: Scheduled(default), + Scheduled(scheduled_core(3)), + // 6: Scheduled(default), + Scheduled(scheduled_core(3)), + ]; + + let empty_hash = PersistedValidationData::::default().hash(); + let cores_with_code = [0, 2, 4, 5]; + + // We can't be sure which one code upgrade the provisioner will pick. We can only assert + // that it only picks one. + // These are the possible cores for which the provisioner will + // supply candidates. There are multiple possibilities depending on which code upgrade it + // chooses. + let possible_expected_cores = [vec![0, 1], vec![1, 2, 3], vec![4, 1]]; + + let committed_receipts: Vec<_> = (0..mock_cores.len()) + .map(|i| { + let mut descriptor = dummy_candidate_descriptor(dummy_hash()); + descriptor.para_id = mock_cores[i].para_id().unwrap(); + descriptor.persisted_validation_data_hash = empty_hash; + descriptor.pov_hash = Hash::from_low_u64_be(i as u64); + CommittedCandidateReceipt { + descriptor, + commitments: CandidateCommitments { + new_validation_code: if cores_with_code.contains(&i) { + Some(vec![].into()) + } else { + None + }, + ..Default::default() + }, + } + }) + .collect(); + + // Input to select_candidates + let candidates: Vec<_> = committed_receipts.iter().map(|r| r.to_plain()).collect(); + // Build possible outputs from select_candidates + let backed_candidates: Vec<_> = committed_receipts + .iter() + .map(|committed_receipt| { + BackedCandidate::new( + committed_receipt.clone(), + Vec::new(), + default_bitvec(MOCK_GROUP_SIZE), + None, + ) + }) + .collect(); + + // First, provisioner will request backable candidates for each scheduled core. + // Then, some of them get filtered due to new validation code rule. + let expected_backed: Vec<_> = + (0..mock_cores.len()).map(|idx| backed_candidates[idx].clone()).collect(); + let expected_backed_filtered: Vec> = possible_expected_cores + .iter() + .map(|indices| indices.iter().map(|&idx| candidates[idx].clone()).collect()) + .collect(); + + let mock_cores_clone = mock_cores.clone(); + + test_harness( + |r| { + mock_overseer( + r, + mock_cores_clone, + expected_backed, + HashMap::new(), + prospective_parachains_mode, + ) + }, + |mut tx: TestSubsystemSender| async move { + let result = select_candidates( + &mock_cores, + &[], + &candidates, + prospective_parachains_mode, + true, + Default::default(), + &mut tx, + ) + .await + .unwrap(); + + assert!(expected_backed_filtered.iter().any(|expected_backed_filtered| { + result.clone().into_iter().all(|c| { + expected_backed_filtered.iter().any(|c2| c.candidate().corresponds_to(c2)) + }) && (expected_backed_filtered.len() == result.len()) + })); + }, + ) + } + + #[rstest] + #[case(true)] + #[case(false)] + fn request_from_prospective_parachains_one_core_per_para(#[case] elastic_scaling_mvp: bool) { + let mock_cores = mock_availability_cores_one_per_para(); + + // why those particular indices? see the comments on mock_availability_cores() + let expected_candidates: Vec<_> = vec![1, 4, 7, 8, 10, 12]; + let (candidates, expected_candidates) = + make_candidates(mock_cores.len() + 1, expected_candidates); + + // Expect prospective parachains subsystem requests. + let prospective_parachains_mode = + ProspectiveParachainsMode::Enabled { max_candidate_depth: 0, allowed_ancestry_len: 0 }; + + let mut required_ancestors: HashMap, Ancestors> = HashMap::new(); + required_ancestors.insert( + vec![candidates[4]], + vec![CandidateHash(Hash::from_low_u64_be(41))].into_iter().collect(), + ); + required_ancestors.insert( + vec![candidates[8]], + vec![CandidateHash(Hash::from_low_u64_be(81))].into_iter().collect(), + ); + + let mock_cores_clone = mock_cores.clone(); + let expected_candidates_clone = expected_candidates.clone(); + test_harness( + |r| { + mock_overseer( + r, + mock_cores_clone, + expected_candidates_clone, + required_ancestors, + prospective_parachains_mode, + ) + }, + |mut tx: TestSubsystemSender| async move { + let result = select_candidates( + &mock_cores, + &[], + &[], + prospective_parachains_mode, + elastic_scaling_mvp, + Default::default(), + &mut tx, + ) + .await + .unwrap(); + + assert_eq!(result.len(), expected_candidates.len()); result.into_iter().for_each(|c| { assert!( - expected_backed_filtered.iter().any(|c2| c.candidate.corresponds_to(c2)), + expected_candidates + .iter() + .any(|c2| c.candidate().corresponds_to(&c2.receipt())), "Failed to find candidate: {:?}", c, ) @@ -576,62 +1058,149 @@ mod select_candidates { } #[test] - fn request_from_prospective_parachains() { - let mock_cores = mock_availability_cores(); - let empty_hash = PersistedValidationData::::default().hash(); - - let mut descriptor_template = dummy_candidate_descriptor(dummy_hash()); - descriptor_template.persisted_validation_data_hash = empty_hash; - let candidate_template = CandidateReceipt { - descriptor: descriptor_template, - commitments_hash: CandidateCommitments::default().hash(), - }; - - let candidates: Vec<_> = std::iter::repeat(candidate_template) - .take(mock_cores.len()) - .enumerate() - .map(|(idx, mut candidate)| { - candidate.descriptor.para_id = idx.into(); - candidate - }) - .collect(); + fn request_from_prospective_parachains_multiple_cores_per_para_elastic_scaling_mvp() { + let mock_cores = mock_availability_cores_multiple_per_para(); // why those particular indices? see the comments on mock_availability_cores() let expected_candidates: Vec<_> = - [1, 4, 7, 8, 10].iter().map(|&idx| candidates[idx].clone()).collect(); + vec![1, 4, 7, 8, 10, 12, 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15]; // Expect prospective parachains subsystem requests. let prospective_parachains_mode = ProspectiveParachainsMode::Enabled { max_candidate_depth: 0, allowed_ancestry_len: 0 }; - let expected_backed = expected_candidates - .iter() - .map(|c| BackedCandidate { - candidate: CommittedCandidateReceipt { - descriptor: c.descriptor.clone(), - commitments: Default::default(), - }, - validity_votes: Vec::new(), - validator_indices: default_bitvec(MOCK_GROUP_SIZE), - }) - .collect(); + let (candidates, expected_candidates) = + make_candidates(mock_cores.len(), expected_candidates); + + let mut required_ancestors: HashMap, Ancestors> = HashMap::new(); + required_ancestors.insert( + vec![candidates[4]], + vec![CandidateHash(Hash::from_low_u64_be(41))].into_iter().collect(), + ); + required_ancestors.insert( + vec![candidates[8]], + vec![CandidateHash(Hash::from_low_u64_be(81))].into_iter().collect(), + ); + required_ancestors.insert( + [12, 12, 12].iter().map(|&idx| candidates[idx]).collect::>(), + vec![ + CandidateHash(Hash::from_low_u64_be(121)), + CandidateHash(Hash::from_low_u64_be(122)), + CandidateHash(Hash::from_low_u64_be(123)), + ] + .into_iter() + .collect(), + ); + required_ancestors.insert( + [13, 13, 13].iter().map(|&idx| candidates[idx]).collect::>(), + (131..=139) + .map(|num| CandidateHash(Hash::from_low_u64_be(num))) + .chain(std::iter::once(CandidateHash(Hash::from_low_u64_be(1398)))) + .collect(), + ); + + required_ancestors.insert( + [15, 15].iter().map(|&idx| candidates[idx]).collect::>(), + vec![ + CandidateHash(Hash::from_low_u64_be(151)), + CandidateHash(Hash::from_low_u64_be(152)), + ] + .into_iter() + .collect(), + ); + + let mock_cores_clone = mock_cores.clone(); + let expected_candidates_clone = expected_candidates.clone(); + test_harness( + |r| { + mock_overseer( + r, + mock_cores_clone, + expected_candidates, + required_ancestors, + prospective_parachains_mode, + ) + }, + |mut tx: TestSubsystemSender| async move { + let result = select_candidates( + &mock_cores, + &[], + &[], + prospective_parachains_mode, + true, + Default::default(), + &mut tx, + ) + .await + .unwrap(); + + assert_eq!(result.len(), expected_candidates_clone.len()); + result.into_iter().for_each(|c| { + assert!( + expected_candidates_clone + .iter() + .any(|c2| c.candidate().corresponds_to(&c2.receipt())), + "Failed to find candidate: {:?}", + c, + ) + }); + }, + ) + } + + #[test] + fn request_from_prospective_parachains_multiple_cores_per_para_elastic_scaling_mvp_disabled() { + let mock_cores = mock_availability_cores_multiple_per_para(); + // why those particular indices? see the comments on mock_availability_cores() + let expected_candidates: Vec<_> = vec![1, 4, 7, 8, 10]; + // Expect prospective parachains subsystem requests. + let prospective_parachains_mode = + ProspectiveParachainsMode::Enabled { max_candidate_depth: 0, allowed_ancestry_len: 0 }; + + let (candidates, expected_candidates) = + make_candidates(mock_cores.len(), expected_candidates); + + let mut required_ancestors: HashMap, Ancestors> = HashMap::new(); + required_ancestors.insert( + vec![candidates[4]], + vec![CandidateHash(Hash::from_low_u64_be(41))].into_iter().collect(), + ); + required_ancestors.insert( + vec![candidates[8]], + vec![CandidateHash(Hash::from_low_u64_be(81))].into_iter().collect(), + ); + + let mock_cores_clone = mock_cores.clone(); + let expected_candidates_clone = expected_candidates.clone(); test_harness( - |r| mock_overseer(r, expected_backed, prospective_parachains_mode), + |r| { + mock_overseer( + r, + mock_cores_clone, + expected_candidates, + required_ancestors, + prospective_parachains_mode, + ) + }, |mut tx: TestSubsystemSender| async move { let result = select_candidates( &mock_cores, &[], &[], prospective_parachains_mode, + false, Default::default(), &mut tx, ) .await .unwrap(); + assert_eq!(result.len(), expected_candidates_clone.len()); result.into_iter().for_each(|c| { assert!( - expected_candidates.iter().any(|c2| c.candidate.corresponds_to(c2)), + expected_candidates_clone + .iter() + .any(|c2| c.candidate().corresponds_to(&c2.receipt())), "Failed to find candidate: {:?}", c, ) @@ -642,18 +1211,11 @@ mod select_candidates { #[test] fn request_receipts_based_on_relay_parent() { - let mock_cores = mock_availability_cores(); - let empty_hash = PersistedValidationData::::default().hash(); - - let mut descriptor_template = dummy_candidate_descriptor(dummy_hash()); - descriptor_template.persisted_validation_data_hash = empty_hash; - let candidate_template = CandidateReceipt { - descriptor: descriptor_template, - commitments_hash: CandidateCommitments::default().hash(), - }; + let mock_cores = mock_availability_cores_one_per_para(); + let candidate_template = dummy_candidate_template(); let candidates: Vec<_> = std::iter::repeat(candidate_template) - .take(mock_cores.len()) + .take(mock_cores.len() + 1) .enumerate() .map(|(idx, mut candidate)| { candidate.descriptor.para_id = idx.into(); @@ -664,31 +1226,44 @@ mod select_candidates { // why those particular indices? see the comments on mock_availability_cores() let expected_candidates: Vec<_> = - [1, 4, 7, 8, 10].iter().map(|&idx| candidates[idx].clone()).collect(); + [1, 4, 7, 8, 10, 12].iter().map(|&idx| candidates[idx].clone()).collect(); // Expect prospective parachains subsystem requests. let prospective_parachains_mode = ProspectiveParachainsMode::Enabled { max_candidate_depth: 0, allowed_ancestry_len: 0 }; let expected_backed = expected_candidates .iter() - .map(|c| BackedCandidate { - candidate: CommittedCandidateReceipt { - descriptor: c.descriptor.clone(), - commitments: Default::default(), - }, - validity_votes: Vec::new(), - validator_indices: default_bitvec(MOCK_GROUP_SIZE), + .map(|c| { + BackedCandidate::new( + CommittedCandidateReceipt { + descriptor: c.descriptor().clone(), + commitments: Default::default(), + }, + Vec::new(), + default_bitvec(MOCK_GROUP_SIZE), + None, + ) }) .collect(); + let mock_cores_clone = mock_cores.clone(); test_harness( - |r| mock_overseer(r, expected_backed, prospective_parachains_mode), + |r| { + mock_overseer( + r, + mock_cores_clone, + expected_backed, + HashMap::new(), + prospective_parachains_mode, + ) + }, |mut tx: TestSubsystemSender| async move { let result = select_candidates( &mock_cores, &[], &[], prospective_parachains_mode, + false, Default::default(), &mut tx, ) @@ -697,7 +1272,7 @@ mod select_candidates { result.into_iter().for_each(|c| { assert!( - expected_candidates.iter().any(|c2| c.candidate.corresponds_to(c2)), + expected_candidates.iter().any(|c2| c.candidate().corresponds_to(c2)), "Failed to find candidate: {:?}", c, ) diff --git a/polkadot/node/core/pvf-checker/src/lib.rs b/polkadot/node/core/pvf-checker/src/lib.rs index ae0fae6b4f9f7e2a4924effd4c3395d60b456f2f..c00ec0d952f158f8df21de87ee9d7460d68218bc 100644 --- a/polkadot/node/core/pvf-checker/src/lib.rs +++ b/polkadot/node/core/pvf-checker/src/lib.rs @@ -415,7 +415,7 @@ async fn check_signing_credentials( gum::warn!( target: LOG_TARGET, relay_parent = ?leaf, - "error occured during requesting validators: {:?}", + "error occurred during requesting validators: {:?}", e ); return None @@ -508,7 +508,7 @@ async fn sign_and_submit_pvf_check_statement( target: LOG_TARGET, ?relay_parent, ?validation_code_hash, - "error occured during submitting a vote: {:?}", + "error occurred during submitting a vote: {:?}", e, ); }, diff --git a/polkadot/node/core/pvf-checker/src/tests.rs b/polkadot/node/core/pvf-checker/src/tests.rs index b0401ecdc3bd2368e34a263842ea566b712884a0..b2365fe53e52c0f2e1c7cb18768c33cdd6801ca9 100644 --- a/polkadot/node/core/pvf-checker/src/tests.rs +++ b/polkadot/node/core/pvf-checker/src/tests.rs @@ -39,8 +39,8 @@ use std::{collections::HashMap, sync::Arc, time::Duration}; type VirtualOverseer = TestSubsystemContextHandle; -fn dummy_validation_code_hash(descriminator: u8) -> ValidationCodeHash { - ValidationCode(vec![descriminator]).hash() +fn dummy_validation_code_hash(discriminator: u8) -> ValidationCodeHash { + ValidationCode(vec![discriminator]).hash() } struct StartsNewSession { @@ -511,7 +511,7 @@ fn reactivating_pvf_leads_to_second_check() { .reply(PreCheckOutcome::Valid); test_state.expect_submit_vote(&mut handle).await.reply_ok(); - // Now activate a descdedant leaf, where the PVF is not present. + // Now activate a descendant leaf, where the PVF is not present. test_state .active_leaves_update( &mut handle, diff --git a/polkadot/node/core/pvf/Cargo.toml b/polkadot/node/core/pvf/Cargo.toml index 9ed64b88ffde857c6abe171a7d1cad7dd66dd509..6ad36a39be60262861fb41e4cfb013f008eec1a9 100644 --- a/polkadot/node/core/pvf/Cargo.toml +++ b/polkadot/node/core/pvf/Cargo.toml @@ -50,7 +50,7 @@ hex-literal = "0.4.1" polkadot-node-core-pvf-common = { path = "common", features = ["test-utils"] } # For benches and integration tests, depend on ourselves with the test-utils # feature. -polkadot-node-core-pvf = { path = ".", features = ["test-utils"] } +polkadot-node-core-pvf = { path = "", features = ["test-utils"] } rococo-runtime = { path = "../../../runtime/rococo" } adder = { package = "test-parachain-adder", path = "../../../parachain/test-parachains/adder" } diff --git a/polkadot/node/core/pvf/common/src/error.rs b/polkadot/node/core/pvf/common/src/error.rs index f8faefc24e65aadb2c50d2375e30fc7d579d6d37..cf274044456f3ea2db6770fbd61b3319f6420996 100644 --- a/polkadot/node/core/pvf/common/src/error.rs +++ b/polkadot/node/core/pvf/common/src/error.rs @@ -16,6 +16,7 @@ use crate::prepare::{PrepareSuccess, PrepareWorkerSuccess}; use parity_scale_codec::{Decode, Encode}; +pub use sc_executor_common::error::Error as ExecuteError; /// Result of PVF preparation from a worker, with checksum of the compiled PVF and stats of the /// preparation if successful. diff --git a/polkadot/node/core/pvf/common/src/execute.rs b/polkadot/node/core/pvf/common/src/execute.rs index 6b3becf524d71fa2d4f4bf574e0a86d09a7a60d9..18c97b03cbcd6fccc03cb27cb9f9e3aba9c7cf2a 100644 --- a/polkadot/node/core/pvf/common/src/execute.rs +++ b/polkadot/node/core/pvf/common/src/execute.rs @@ -40,6 +40,9 @@ pub enum WorkerResponse { }, /// The candidate is invalid. InvalidCandidate(String), + /// Instantiation of the WASM module instance failed during an execution. + /// Possibly related to local issues or dirty node update. May be retried with re-preparation. + RuntimeConstruction(String), /// The job timed out. JobTimedOut, /// The job process has died. We must kill the worker just in case. @@ -68,6 +71,9 @@ pub enum JobResponse { /// The result of parachain validation. result_descriptor: ValidationResult, }, + /// A possibly transient runtime instantiation error happened during the execution; may be + /// retried with re-preparation + RuntimeConstruction(String), /// The candidate is invalid. InvalidCandidate(String), } @@ -81,6 +87,15 @@ impl JobResponse { Self::InvalidCandidate(format!("{}: {}", ctx, msg)) } } + + /// Creates a may retry response from a context `ctx` and a message `msg` (which can be empty). + pub fn runtime_construction(ctx: &'static str, msg: &str) -> Self { + if msg.is_empty() { + Self::RuntimeConstruction(ctx.to_string()) + } else { + Self::RuntimeConstruction(format!("{}: {}", ctx, msg)) + } + } } /// An unexpected error occurred in the execution job process. Because this comes from the job, diff --git a/polkadot/node/core/pvf/common/src/executor_interface.rs b/polkadot/node/core/pvf/common/src/executor_interface.rs index 4cd2f5c85eec53661fde7625bb50a1d195381beb..252e611db8a488cff776b5d7863c987318e7e013 100644 --- a/polkadot/node/core/pvf/common/src/executor_interface.rs +++ b/polkadot/node/core/pvf/common/src/executor_interface.rs @@ -16,6 +16,7 @@ //! Interface to the Substrate Executor +use crate::error::ExecuteError; use polkadot_primitives::{ executor_params::{DEFAULT_LOGICAL_STACK_MAX, DEFAULT_NATIVE_STACK_MAX}, ExecutorParam, ExecutorParams, @@ -23,7 +24,7 @@ use polkadot_primitives::{ use sc_executor_common::{ error::WasmError, runtime_blob::RuntimeBlob, - wasm_runtime::{HeapAllocStrategy, InvokeMethod, WasmModule as _}, + wasm_runtime::{HeapAllocStrategy, WasmModule as _}, }; use sc_executor_wasmtime::{Config, DeterministicStackLimit, Semantics, WasmtimeRuntime}; use sp_core::storage::{ChildInfo, TrackedStorageKey}; @@ -109,7 +110,7 @@ pub unsafe fn execute_artifact( compiled_artifact_blob: &[u8], executor_params: &ExecutorParams, params: &[u8], -) -> Result, String> { +) -> Result, ExecuteError> { let mut extensions = sp_externalities::Extensions::new(); extensions.register(sp_core::traits::ReadRuntimeVersionExt::new(ReadRuntimeVersion)); @@ -118,12 +119,11 @@ pub unsafe fn execute_artifact( match sc_executor::with_externalities_safe(&mut ext, || { let runtime = create_runtime_from_artifact_bytes(compiled_artifact_blob, executor_params)?; - runtime.new_instance()?.call(InvokeMethod::Export("validate_block"), params) + runtime.new_instance()?.call("validate_block", params) }) { Ok(Ok(ok)) => Ok(ok), Ok(Err(err)) | Err(err) => Err(err), } - .map_err(|err| format!("execute error: {:?}", err)) } /// Constructs the runtime for the given PVF, given the artifact bytes. diff --git a/polkadot/node/core/pvf/common/src/pvf.rs b/polkadot/node/core/pvf/common/src/pvf.rs index 3f5b4d7ca70c14d53770af2dc470e4a2e5e572a4..340dffe07c3fd3e8b049f1a126a348a7ff4e326f 100644 --- a/polkadot/node/core/pvf/common/src/pvf.rs +++ b/polkadot/node/core/pvf/common/src/pvf.rs @@ -85,9 +85,9 @@ impl PvfPrepData { /// Creates a structure for tests. #[cfg(feature = "test-utils")] pub fn from_discriminator_and_timeout(num: u32, timeout: Duration) -> Self { - let descriminator_buf = num.to_le_bytes().to_vec(); + let discriminator_buf = num.to_le_bytes().to_vec(); Self::from_code( - descriminator_buf, + discriminator_buf, ExecutorParams::default(), timeout, PrepareJobKind::Compilation, diff --git a/polkadot/node/core/pvf/execute-worker/src/lib.rs b/polkadot/node/core/pvf/execute-worker/src/lib.rs index 0cfa5a786946c7428ea8954515fe03807a90da32..bd7e76010a6dfedb339fd2fc1c17aa7f4007babe 100644 --- a/polkadot/node/core/pvf/execute-worker/src/lib.rs +++ b/polkadot/node/core/pvf/execute-worker/src/lib.rs @@ -16,7 +16,9 @@ //! Contains the logic for executing PVFs. Used by the polkadot-execute-worker binary. -pub use polkadot_node_core_pvf_common::executor_interface::execute_artifact; +pub use polkadot_node_core_pvf_common::{ + error::ExecuteError, executor_interface::execute_artifact, +}; // NOTE: Initializing logging in e.g. tests will not have an effect in the workers, as they are // separate spawned processes. Run with e.g. `RUST_LOG=parachain::pvf-execute-worker=trace`. @@ -237,7 +239,9 @@ fn validate_using_artifact( // [`executor_interface::prepare`]. execute_artifact(compiled_artifact_blob, executor_params, params) } { - Err(err) => return JobResponse::format_invalid("execute", &err), + Err(ExecuteError::RuntimeConstruction(wasmerr)) => + return JobResponse::runtime_construction("execute", &wasmerr.to_string()), + Err(err) => return JobResponse::format_invalid("execute", &err.to_string()), Ok(d) => d, }; @@ -550,6 +554,8 @@ fn handle_parent_process( Ok(WorkerResponse::Ok { result_descriptor, duration: cpu_tv }) }, Ok(JobResponse::InvalidCandidate(err)) => Ok(WorkerResponse::InvalidCandidate(err)), + Ok(JobResponse::RuntimeConstruction(err)) => + Ok(WorkerResponse::RuntimeConstruction(err)), Err(job_error) => { gum::warn!( target: LOG_TARGET, diff --git a/polkadot/node/core/pvf/src/artifacts.rs b/polkadot/node/core/pvf/src/artifacts.rs index 78dfe71adaddcd8d917a639591c102e08ebb7e4b..6288755526d497812027f7bf04e11e5a67ab799e 100644 --- a/polkadot/node/core/pvf/src/artifacts.rs +++ b/polkadot/node/core/pvf/src/artifacts.rs @@ -238,6 +238,14 @@ impl Artifacts { .is_none()); } + /// Remove artifact by its id. + pub fn remove(&mut self, artifact_id: ArtifactId) -> Option<(ArtifactId, PathBuf)> { + self.inner.remove(&artifact_id).and_then(|state| match state { + ArtifactState::Prepared { path, .. } => Some((artifact_id, path)), + _ => None, + }) + } + /// Remove artifacts older than the given TTL and return id and path of the removed ones. pub fn prune(&mut self, artifact_ttl: Duration) -> Vec<(ArtifactId, PathBuf)> { let now = SystemTime::now(); diff --git a/polkadot/node/core/pvf/src/error.rs b/polkadot/node/core/pvf/src/error.rs index 80d41d5c64be1a5cec31aee084a0f536ad0380bf..8dc96305eadb8f3ced1231889f5fa6c5ffaeac57 100644 --- a/polkadot/node/core/pvf/src/error.rs +++ b/polkadot/node/core/pvf/src/error.rs @@ -86,6 +86,10 @@ pub enum PossiblyInvalidError { /// vote invalid. #[error("possibly invalid: job error: {0}")] JobError(String), + /// Instantiation of the WASM module instance failed during an execution. + /// Possibly related to local issues or dirty node update. May be retried with re-preparation. + #[error("possibly invalid: runtime construction: {0}")] + RuntimeConstruction(String), } impl From for ValidationError { diff --git a/polkadot/node/core/pvf/src/execute/mod.rs b/polkadot/node/core/pvf/src/execute/mod.rs index c6d9cf90fa289601f89b0aad307483a002f89427..365e98196cae29dcb9c5085708cfaf7ee8b26238 100644 --- a/polkadot/node/core/pvf/src/execute/mod.rs +++ b/polkadot/node/core/pvf/src/execute/mod.rs @@ -23,4 +23,4 @@ mod queue; mod worker_interface; -pub use queue::{start, PendingExecutionRequest, ToQueue}; +pub use queue::{start, FromQueue, PendingExecutionRequest, ToQueue}; diff --git a/polkadot/node/core/pvf/src/execute/queue.rs b/polkadot/node/core/pvf/src/execute/queue.rs index aa91d11781fc625993484fa64ff8ebca4d65aeb2..bdc3c7327b06079eaaeebf737ffa7ca2c8c8270c 100644 --- a/polkadot/node/core/pvf/src/execute/queue.rs +++ b/polkadot/node/core/pvf/src/execute/queue.rs @@ -25,7 +25,7 @@ use crate::{ InvalidCandidate, PossiblyInvalidError, ValidationError, LOG_TARGET, }; use futures::{ - channel::mpsc, + channel::{mpsc, oneshot}, future::BoxFuture, stream::{FuturesUnordered, StreamExt as _}, Future, FutureExt, @@ -54,6 +54,12 @@ pub enum ToQueue { Enqueue { artifact: ArtifactPathId, pending_execution_request: PendingExecutionRequest }, } +/// A response from queue. +#[derive(Debug)] +pub enum FromQueue { + RemoveArtifact { artifact: ArtifactId, reply_to: oneshot::Sender<()> }, +} + /// An execution request that should execute the PVF (known in the context) and send the results /// to the given result sender. #[derive(Debug)] @@ -137,6 +143,8 @@ struct Queue { /// The receiver that receives messages to the pool. to_queue_rx: mpsc::Receiver, + /// The sender to send messages back to validation host. + from_queue_tx: mpsc::UnboundedSender, // Some variables related to the current session. program_path: PathBuf, @@ -161,6 +169,7 @@ impl Queue { node_version: Option, security_status: SecurityStatus, to_queue_rx: mpsc::Receiver, + from_queue_tx: mpsc::UnboundedSender, ) -> Self { Self { metrics, @@ -170,6 +179,7 @@ impl Queue { node_version, security_status, to_queue_rx, + from_queue_tx, queue: VecDeque::new(), mux: Mux::new(), workers: Workers { @@ -301,7 +311,7 @@ async fn handle_mux(queue: &mut Queue, event: QueueEvent) { handle_worker_spawned(queue, idle, handle, job); }, QueueEvent::StartWork(worker, outcome, artifact_id, result_tx) => { - handle_job_finish(queue, worker, outcome, artifact_id, result_tx); + handle_job_finish(queue, worker, outcome, artifact_id, result_tx).await; }, } } @@ -327,42 +337,69 @@ fn handle_worker_spawned( /// If there are pending jobs in the queue, schedules the next of them onto the just freed up /// worker. Otherwise, puts back into the available workers list. -fn handle_job_finish( +async fn handle_job_finish( queue: &mut Queue, worker: Worker, outcome: Outcome, artifact_id: ArtifactId, result_tx: ResultSender, ) { - let (idle_worker, result, duration) = match outcome { + let (idle_worker, result, duration, sync_channel) = match outcome { Outcome::Ok { result_descriptor, duration, idle_worker } => { // TODO: propagate the soft timeout - (Some(idle_worker), Ok(result_descriptor), Some(duration)) + (Some(idle_worker), Ok(result_descriptor), Some(duration), None) }, Outcome::InvalidCandidate { err, idle_worker } => ( Some(idle_worker), Err(ValidationError::Invalid(InvalidCandidate::WorkerReportedInvalid(err))), None, + None, ), - Outcome::InternalError { err } => (None, Err(ValidationError::Internal(err)), None), + Outcome::RuntimeConstruction { err, idle_worker } => { + // The task for artifact removal is executed concurrently with + // the message to the host on the execution result. + let (result_tx, result_rx) = oneshot::channel(); + queue + .from_queue_tx + .unbounded_send(FromQueue::RemoveArtifact { + artifact: artifact_id.clone(), + reply_to: result_tx, + }) + .expect("from execute queue receiver is listened by the host; qed"); + ( + Some(idle_worker), + Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::RuntimeConstruction( + err, + ))), + None, + Some(result_rx), + ) + }, + Outcome::InternalError { err } => (None, Err(ValidationError::Internal(err)), None, None), // Either the worker or the job timed out. Kill the worker in either case. Treated as // definitely-invalid, because if we timed out, there's no time left for a retry. Outcome::HardTimeout => - (None, Err(ValidationError::Invalid(InvalidCandidate::HardTimeout)), None), + (None, Err(ValidationError::Invalid(InvalidCandidate::HardTimeout)), None, None), // "Maybe invalid" errors (will retry). Outcome::WorkerIntfErr => ( None, Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::AmbiguousWorkerDeath)), None, + None, ), Outcome::JobDied { err } => ( None, Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::AmbiguousJobDeath(err))), None, + None, + ), + Outcome::JobError { err } => ( + None, + Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::JobError(err))), + None, + None, ), - Outcome::JobError { err } => - (None, Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::JobError(err))), None), }; queue.metrics.execute_finished(); @@ -386,6 +423,12 @@ fn handle_job_finish( ); } + if let Some(sync_channel) = sync_channel { + // err means the sender is dropped (the artifact is already removed from the cache) + // so that's legitimate to ignore the result + let _ = sync_channel.await; + } + // First we send the result. It may fail due to the other end of the channel being dropped, // that's legitimate and we don't treat that as an error. let _ = result_tx.send(result); @@ -521,8 +564,10 @@ pub fn start( spawn_timeout: Duration, node_version: Option, security_status: SecurityStatus, -) -> (mpsc::Sender, impl Future) { +) -> (mpsc::Sender, mpsc::UnboundedReceiver, impl Future) { let (to_queue_tx, to_queue_rx) = mpsc::channel(20); + let (from_queue_tx, from_queue_rx) = mpsc::unbounded(); + let run = Queue::new( metrics, program_path, @@ -532,7 +577,8 @@ pub fn start( node_version, security_status, to_queue_rx, + from_queue_tx, ) .run(); - (to_queue_tx, run) + (to_queue_tx, from_queue_rx, run) } diff --git a/polkadot/node/core/pvf/src/execute/worker_interface.rs b/polkadot/node/core/pvf/src/execute/worker_interface.rs index 9f7738f00e699ab981d7fa4396fcd09d5e1a4abe..db81da118d7b5563ea7b4bec1b6c76bd3bf842e2 100644 --- a/polkadot/node/core/pvf/src/execute/worker_interface.rs +++ b/polkadot/node/core/pvf/src/execute/worker_interface.rs @@ -87,6 +87,10 @@ pub enum Outcome { /// a trap. Errors related to the preparation process are not expected to be encountered by the /// execution workers. InvalidCandidate { err: String, idle_worker: IdleWorker }, + /// The error is probably transient. It may be for example + /// because the artifact was prepared with a Wasmtime version different from the version + /// in the current execution environment. + RuntimeConstruction { err: String, idle_worker: IdleWorker }, /// The execution time exceeded the hard limit. The worker is terminated. HardTimeout, /// An I/O error happened during communication with the worker. This may mean that the worker @@ -193,6 +197,10 @@ pub async fn start_work( err, idle_worker: IdleWorker { stream, pid, worker_dir }, }, + WorkerResponse::RuntimeConstruction(err) => Outcome::RuntimeConstruction { + err, + idle_worker: IdleWorker { stream, pid, worker_dir }, + }, WorkerResponse::JobTimedOut => Outcome::HardTimeout, WorkerResponse::JobDied { err, job_pid: _ } => Outcome::JobDied { err }, WorkerResponse::JobError(err) => Outcome::JobError { err }, diff --git a/polkadot/node/core/pvf/src/host.rs b/polkadot/node/core/pvf/src/host.rs index ae9fdc7d2dea81ac85c8c4ad752fca6fbf9a6f55..59d5a7e20a887ef35282f0a8ae8c0998625d825a 100644 --- a/polkadot/node/core/pvf/src/host.rs +++ b/polkadot/node/core/pvf/src/host.rs @@ -274,7 +274,7 @@ pub async fn start( from_prepare_pool, ); - let (to_execute_queue_tx, run_execute_queue) = execute::start( + let (to_execute_queue_tx, from_execute_queue_rx, run_execute_queue) = execute::start( metrics, config.execute_worker_program_path.to_owned(), config.cache_path.clone(), @@ -296,6 +296,7 @@ pub async fn start( to_prepare_queue_tx, from_prepare_queue_rx, to_execute_queue_tx, + from_execute_queue_rx, to_sweeper_tx, awaiting_prepare: AwaitingPrepare::default(), }) @@ -342,6 +343,8 @@ struct Inner { from_prepare_queue_rx: mpsc::UnboundedReceiver, to_execute_queue_tx: mpsc::Sender, + from_execute_queue_rx: mpsc::UnboundedReceiver, + to_sweeper_tx: mpsc::Sender, awaiting_prepare: AwaitingPrepare, @@ -358,6 +361,7 @@ async fn run( to_host_rx, from_prepare_queue_rx, mut to_prepare_queue_tx, + from_execute_queue_rx, mut to_execute_queue_tx, mut to_sweeper_tx, mut awaiting_prepare, @@ -384,10 +388,21 @@ async fn run( let mut to_host_rx = to_host_rx.fuse(); let mut from_prepare_queue_rx = from_prepare_queue_rx.fuse(); + let mut from_execute_queue_rx = from_execute_queue_rx.fuse(); loop { // biased to make it behave deterministically for tests. futures::select_biased! { + from_execute_queue_rx = from_execute_queue_rx.next() => { + let from_queue = break_if_fatal!(from_execute_queue_rx.ok_or(Fatal)); + let execute::FromQueue::RemoveArtifact { artifact, reply_to } = from_queue; + break_if_fatal!(handle_artifact_removal( + &mut to_sweeper_tx, + &mut artifacts, + artifact, + reply_to, + ).await); + }, () = cleanup_pulse.select_next_some() => { // `select_next_some` because we don't expect this to fail, but if it does, we // still don't fail. The trade-off is that the compiled cache will start growing @@ -861,6 +876,37 @@ async fn handle_cleanup_pulse( Ok(()) } +async fn handle_artifact_removal( + sweeper_tx: &mut mpsc::Sender, + artifacts: &mut Artifacts, + artifact_id: ArtifactId, + reply_to: oneshot::Sender<()>, +) -> Result<(), Fatal> { + let (artifact_id, path) = if let Some(artifact) = artifacts.remove(artifact_id) { + artifact + } else { + // if we haven't found the artifact by its id, + // it has been probably removed + // anyway with the randomness of the artifact name + // it is safe to ignore + return Ok(()); + }; + reply_to + .send(()) + .expect("the execute queue waits for the artifact remove confirmation; qed"); + // Thanks to the randomness of the artifact name (see + // `artifacts::generate_artifact_path`) there is no issue with any name conflict on + // future repreparation. + // So we can confirm the artifact removal already + gum::debug!( + target: LOG_TARGET, + validation_code_hash = ?artifact_id.code_hash, + "PVF pruning: pruning artifact by request from the execute queue", + ); + sweeper_tx.send(path).await.map_err(|_| Fatal)?; + Ok(()) +} + /// A simple task which sole purpose is to delete files thrown at it. async fn sweeper_task(mut sweeper_rx: mpsc::Receiver) { loop { @@ -871,7 +917,7 @@ async fn sweeper_task(mut sweeper_rx: mpsc::Receiver) { gum::trace!( target: LOG_TARGET, ?result, - "Sweeped the artifact file {}", + "Swept the artifact file {}", condemned.display(), ); }, @@ -968,6 +1014,8 @@ pub(crate) mod tests { to_prepare_queue_rx: mpsc::Receiver, from_prepare_queue_tx: mpsc::UnboundedSender, to_execute_queue_rx: mpsc::Receiver, + #[allow(unused)] + from_execute_queue_tx: mpsc::UnboundedSender, to_sweeper_rx: mpsc::Receiver, run: BoxFuture<'static, ()>, @@ -979,6 +1027,7 @@ pub(crate) mod tests { let (to_prepare_queue_tx, to_prepare_queue_rx) = mpsc::channel(10); let (from_prepare_queue_tx, from_prepare_queue_rx) = mpsc::unbounded(); let (to_execute_queue_tx, to_execute_queue_rx) = mpsc::channel(10); + let (from_execute_queue_tx, from_execute_queue_rx) = mpsc::unbounded(); let (to_sweeper_tx, to_sweeper_rx) = mpsc::channel(10); let run = run(Inner { @@ -989,6 +1038,7 @@ pub(crate) mod tests { to_prepare_queue_tx, from_prepare_queue_rx, to_execute_queue_tx, + from_execute_queue_rx, to_sweeper_tx, awaiting_prepare: AwaitingPrepare::default(), }) @@ -999,6 +1049,7 @@ pub(crate) mod tests { to_prepare_queue_rx, from_prepare_queue_tx, to_execute_queue_rx, + from_execute_queue_tx, to_sweeper_rx, run, } diff --git a/polkadot/node/core/pvf/src/priority.rs b/polkadot/node/core/pvf/src/priority.rs index d4bd49eaee84037043a46f6645612627b081c0f4..d1ef9c604b117d10aca5b99cc8ccd1953fa522f5 100644 --- a/polkadot/node/core/pvf/src/priority.rs +++ b/polkadot/node/core/pvf/src/priority.rs @@ -29,7 +29,7 @@ pub enum Priority { } impl Priority { - /// Returns `true` if `self` is `Crticial` + /// Returns `true` if `self` is `Critical` pub fn is_critical(self) -> bool { self == Priority::Critical } diff --git a/polkadot/node/core/pvf/src/worker_interface.rs b/polkadot/node/core/pvf/src/worker_interface.rs index ad9f0294c09400a47f249bdf16855b3c110f155a..93fffc80662266a6a1112f6d5120cb454bca6276 100644 --- a/polkadot/node/core/pvf/src/worker_interface.rs +++ b/polkadot/node/core/pvf/src/worker_interface.rs @@ -130,11 +130,11 @@ where fn make_tmppath(prefix: &str, dir: &Path) -> PathBuf { use rand::distributions::Alphanumeric; - const DESCRIMINATOR_LEN: usize = 10; + const DISCRIMINATOR_LEN: usize = 10; - let mut buf = Vec::with_capacity(prefix.len() + DESCRIMINATOR_LEN); + let mut buf = Vec::with_capacity(prefix.len() + DISCRIMINATOR_LEN); buf.extend(prefix.as_bytes()); - buf.extend(rand::thread_rng().sample_iter(&Alphanumeric).take(DESCRIMINATOR_LEN)); + buf.extend(rand::thread_rng().sample_iter(&Alphanumeric).take(DISCRIMINATOR_LEN)); let s = std::str::from_utf8(&buf) .expect("the string is collected from a valid utf-8 sequence; qed"); diff --git a/polkadot/node/core/pvf/tests/it/main.rs b/polkadot/node/core/pvf/tests/it/main.rs index bcc10749e746d05479997e161329bbc6b4c468ff..16ef23c69cad9d40d6cd79d2a754d8a347bfac3c 100644 --- a/polkadot/node/core/pvf/tests/it/main.rs +++ b/polkadot/node/core/pvf/tests/it/main.rs @@ -21,13 +21,14 @@ use parity_scale_codec::Encode as _; #[cfg(all(feature = "ci-only-tests", target_os = "linux"))] use polkadot_node_core_pvf::SecurityStatus; use polkadot_node_core_pvf::{ - start, testing::build_workers_and_get_paths, Config, InvalidCandidate, Metrics, PrepareError, - PrepareJobKind, PvfPrepData, ValidationError, ValidationHost, JOB_TIMEOUT_WALL_CLOCK_FACTOR, + start, testing::build_workers_and_get_paths, Config, InvalidCandidate, Metrics, + PossiblyInvalidError, PrepareError, PrepareJobKind, PvfPrepData, ValidationError, + ValidationHost, JOB_TIMEOUT_WALL_CLOCK_FACTOR, }; use polkadot_parachain_primitives::primitives::{BlockData, ValidationParams, ValidationResult}; use polkadot_primitives::{ExecutorParam, ExecutorParams}; -use std::time::Duration; +use std::{io::Write, time::Duration}; use tokio::sync::Mutex; mod adder; @@ -352,10 +353,80 @@ async fn deleting_prepared_artifact_does_not_dispute() { ) .await; - match result { - Err(ValidationError::Invalid(InvalidCandidate::HardTimeout)) => {}, - r => panic!("{:?}", r), + assert_matches!(result, Err(ValidationError::Invalid(InvalidCandidate::HardTimeout))); +} + +// Test that corruption of a prepared artifact does not lead to a dispute when we try to execute it. +#[tokio::test] +async fn corrupted_prepared_artifact_does_not_dispute() { + let host = TestHost::new().await; + let cache_dir = host.cache_dir.path(); + + let _stats = host.precheck_pvf(halt::wasm_binary_unwrap(), Default::default()).await.unwrap(); + + // Manually corrupting the prepared artifact from disk. The in-memory artifacts table won't + // change. + let artifact_path = { + // Get the artifact path (asserting it exists). + let mut cache_dir: Vec<_> = std::fs::read_dir(cache_dir).unwrap().collect(); + // Should contain the artifact and the worker dir. + assert_eq!(cache_dir.len(), 2); + let mut artifact_path = cache_dir.pop().unwrap().unwrap(); + if artifact_path.path().is_dir() { + artifact_path = cache_dir.pop().unwrap().unwrap(); + } + + // Corrupt the artifact. + let mut f = std::fs::OpenOptions::new() + .write(true) + .truncate(true) + .open(artifact_path.path()) + .unwrap(); + f.write_all(b"corrupted wasm").unwrap(); + f.flush().unwrap(); + artifact_path + }; + + assert!(artifact_path.path().exists()); + + // Try to validate, artifact should get removed because of the corruption. + let result = host + .validate_candidate( + halt::wasm_binary_unwrap(), + ValidationParams { + block_data: BlockData(Vec::new()), + parent_head: Default::default(), + relay_parent_number: 1, + relay_parent_storage_root: Default::default(), + }, + Default::default(), + ) + .await; + + assert_matches!( + result, + Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::RuntimeConstruction(_))) + ); + + // because of RuntimeConstruction we may retry + host.precheck_pvf(halt::wasm_binary_unwrap(), Default::default()).await.unwrap(); + + // The actual artifact removal is done concurrently + // with sending of the result of the execution + // it is not a problem for further re-preparation as + // artifact filenames are random + for _ in 1..5 { + if !artifact_path.path().exists() { + break; + } + tokio::time::sleep(Duration::from_secs(1)).await; } + + assert!( + !artifact_path.path().exists(), + "the corrupted artifact ({}) should be deleted by the host", + artifact_path.path().display() + ); } #[tokio::test] @@ -472,9 +543,9 @@ async fn all_security_features_work() { // artifacts cache existing. #[cfg(all(feature = "ci-only-tests", target_os = "linux"))] #[tokio::test] -async fn nonexistant_cache_dir() { +async fn nonexistent_cache_dir() { let host = TestHost::new_with_config(|cfg| { - cfg.cache_path = cfg.cache_path.join("nonexistant_cache_dir"); + cfg.cache_path = cfg.cache_path.join("nonexistent_cache_dir"); }) .await; diff --git a/polkadot/node/core/runtime-api/src/cache.rs b/polkadot/node/core/runtime-api/src/cache.rs index 5eca551db0a69f0edcab3cbba87c2029274b50ef..9674cda983852be245da33e953bce4c8a395b363 100644 --- a/polkadot/node/core/runtime-api/src/cache.rs +++ b/polkadot/node/core/runtime-api/src/cache.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use std::collections::btree_map::BTreeMap; +use std::collections::{btree_map::BTreeMap, VecDeque}; use schnellru::{ByLength, LruMap}; use sp_consensus_babe::Epoch; @@ -23,10 +23,11 @@ use polkadot_primitives::{ async_backing, slashing, vstaging::{self, ApprovalVotingParams}, AuthorityDiscoveryId, BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, - Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, OccupiedCoreAssumption, - PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, - ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, + CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, + GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, + OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, + SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, + ValidatorSignature, }; /// For consistency we have the same capacity for all caches. We use 128 as we'll only need that @@ -70,6 +71,7 @@ pub(crate) struct RequestResultCache { async_backing_params: LruMap, node_features: LruMap, approval_voting_params: LruMap, + claim_queue: LruMap>>, } impl Default for RequestResultCache { @@ -105,6 +107,7 @@ impl Default for RequestResultCache { para_backing_state: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)), async_backing_params: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)), node_features: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)), + claim_queue: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)), } } } @@ -525,6 +528,21 @@ impl RequestResultCache { ) { self.approval_voting_params.insert(session_index, value); } + + pub(crate) fn claim_queue( + &mut self, + relay_parent: &Hash, + ) -> Option<&BTreeMap>> { + self.claim_queue.get(relay_parent).map(|v| &*v) + } + + pub(crate) fn cache_claim_queue( + &mut self, + relay_parent: Hash, + value: BTreeMap>, + ) { + self.claim_queue.insert(relay_parent, value); + } } pub(crate) enum RequestResult { @@ -577,4 +595,5 @@ pub(crate) enum RequestResult { ParaBackingState(Hash, ParaId, Option), AsyncBackingParams(Hash, async_backing::AsyncBackingParams), NodeFeatures(SessionIndex, vstaging::NodeFeatures), + ClaimQueue(Hash, BTreeMap>), } diff --git a/polkadot/node/core/runtime-api/src/lib.rs b/polkadot/node/core/runtime-api/src/lib.rs index 4bedfd827340bc60b0101f1c854f207705bc0b31..2b7f6fc2d609f4dfcdf4e94fb63bf499376f6d07 100644 --- a/polkadot/node/core/runtime-api/src/lib.rs +++ b/polkadot/node/core/runtime-api/src/lib.rs @@ -177,6 +177,9 @@ where self.requests_cache.cache_async_backing_params(relay_parent, params), NodeFeatures(session_index, params) => self.requests_cache.cache_node_features(session_index, params), + ClaimQueue(relay_parent, sender) => { + self.requests_cache.cache_claim_queue(relay_parent, sender); + }, } } @@ -329,6 +332,8 @@ where Some(Request::NodeFeatures(index, sender)) } }, + Request::ClaimQueue(sender) => + query!(claim_queue(), sender).map(|sender| Request::ClaimQueue(sender)), } } @@ -433,6 +438,7 @@ where .unwrap_or_else(|e| { gum::warn!( target: LOG_TARGET, + api = ?stringify!($api_name), "cannot query the runtime API version: {}", e, ); @@ -625,5 +631,11 @@ where sender, result = (index) ), + Request::ClaimQueue(sender) => query!( + ClaimQueue, + claim_queue(), + ver = Request::CLAIM_QUEUE_RUNTIME_REQUIREMENT, + sender + ), } } diff --git a/polkadot/node/core/runtime-api/src/tests.rs b/polkadot/node/core/runtime-api/src/tests.rs index f91723b3d39e9a6548d94ffde1da534d2e7592ab..b51682aa0f4c2db816668bf489f4b983630f883a 100644 --- a/polkadot/node/core/runtime-api/src/tests.rs +++ b/polkadot/node/core/runtime-api/src/tests.rs @@ -23,15 +23,16 @@ use polkadot_primitives::{ async_backing, slashing, vstaging::{ApprovalVotingParams, NodeFeatures}, AuthorityDiscoveryId, BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, GroupRotationInfo, - Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, OccupiedCoreAssumption, - PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, - Slot, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, + CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, + GroupRotationInfo, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, + OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, + SessionIndex, SessionInfo, Slot, ValidationCode, ValidationCodeHash, ValidatorId, + ValidatorIndex, ValidatorSignature, }; use sp_api::ApiError; use sp_core::testing::TaskExecutor; use std::{ - collections::{BTreeMap, HashMap}, + collections::{BTreeMap, HashMap, VecDeque}, sync::{Arc, Mutex}, }; use test_helpers::{dummy_committed_candidate_receipt, dummy_validation_code}; @@ -286,17 +287,24 @@ impl RuntimeApiSubsystemClient for MockSubsystemClient { async fn disabled_validators(&self, _: Hash) -> Result, ApiError> { todo!("Not required for tests") } + + async fn claim_queue( + &self, + _: Hash, + ) -> Result>, ApiError> { + todo!("Not required for tests") + } } #[test] fn requests_authorities() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); - let substem_client = Arc::new(MockSubsystemClient::default()); + let subsystem_client = Arc::new(MockSubsystemClient::default()); let relay_parent = [1; 32].into(); let spawner = sp_core::testing::TaskExecutor::new(); let subsystem = - RuntimeApiSubsystem::new(substem_client.clone(), Metrics(None), SpawnGlue(spawner)); + RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let test_task = async move { let (tx, rx) = oneshot::channel(); @@ -307,7 +315,7 @@ fn requests_authorities() { }) .await; - assert_eq!(rx.await.unwrap().unwrap(), substem_client.authorities); + assert_eq!(rx.await.unwrap().unwrap(), subsystem_client.authorities); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; @@ -1034,7 +1042,7 @@ fn requests_submit_pvf_check_statement() { let _ = rx.await.unwrap().unwrap(); assert_eq!( - &*subsystem_client.submitted_pvf_check_statement.lock().expect("poisened mutex"), + &*subsystem_client.submitted_pvf_check_statement.lock().expect("poisoned mutex"), &[(stmt.clone(), sig.clone()), (stmt.clone(), sig.clone())] ); diff --git a/polkadot/node/jaeger/src/spans.rs b/polkadot/node/jaeger/src/spans.rs index 4038d41344f2db09908fc50c82ddd787d374f505..4816fccf3b96e741027d0b0897627486e420d80e 100644 --- a/polkadot/node/jaeger/src/spans.rs +++ b/polkadot/node/jaeger/src/spans.rs @@ -70,7 +70,7 @@ //! let root_span = //! jaeger::Span::new(relay_parent, "root_of_aaall_spans"); //! -//! // the prefered way of adding additional delayed information: +//! // the preferred way of adding additional delayed information: //! let span = root_span.child("inner"); //! //! // ... more operations ... diff --git a/polkadot/node/malus/Cargo.toml b/polkadot/node/malus/Cargo.toml index ea25b9077f3a7000cef89a328016fd95b80b9035..b3eb856f08efec851d9f19a07008e0d6853d81ae 100644 --- a/polkadot/node/malus/Cargo.toml +++ b/polkadot/node/malus/Cargo.toml @@ -43,7 +43,7 @@ assert_matches = "1.5" async-trait = "0.1.74" sp-keystore = { path = "../../../substrate/primitives/keystore" } sp-core = { path = "../../../substrate/primitives/core" } -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } futures = "0.3.21" futures-timer = "3.0.2" gum = { package = "tracing-gum", path = "../gum" } diff --git a/polkadot/node/malus/README.md b/polkadot/node/malus/README.md index e0c7893a75319ed40b6bde633309f209dac91225..25453a1980e4b8f66ac50d3be89eb798f4b2b0e6 100644 --- a/polkadot/node/malus/README.md +++ b/polkadot/node/malus/README.md @@ -18,7 +18,7 @@ defined in the [(DSL[(**D**omain **S**pecific **L**anguage)]) doc](https://parit ## Usage -> Assumes you already gained permissiones, ping in element `@javier:matrix.parity.io` to get access. +> Assumes you already gained permissions, ping in element `@javier:matrix.parity.io` to get access. > and you have cloned the [zombienet][zombienet] repo. To launch a test case in the development cluster use (e.g. for the ./node/malus/integrationtests/0001-dispute-valid-block.toml): diff --git a/polkadot/node/malus/src/variants/back_garbage_candidate.rs b/polkadot/node/malus/src/variants/back_garbage_candidate.rs index 82475d291422582f5197613d4f347baaf203b929..b939a2151e2359828da74d317c9e2bc0af2c6e0c 100644 --- a/polkadot/node/malus/src/variants/back_garbage_candidate.rs +++ b/polkadot/node/malus/src/variants/back_garbage_candidate.rs @@ -20,14 +20,13 @@ use polkadot_cli::{ service::{ - AuthorityDiscoveryApi, AuxStore, BabeApi, Block, Error, ExtendedOverseerGenArgs, - HeaderBackend, Overseer, OverseerConnector, OverseerGen, OverseerGenArgs, OverseerHandle, - ParachainHost, ProvideRuntimeApi, + AuxStore, Error, ExtendedOverseerGenArgs, Overseer, OverseerConnector, OverseerGen, + OverseerGenArgs, OverseerHandle, }, validator_overseer_builder, Cli, }; use polkadot_node_subsystem::SpawnGlue; -use polkadot_node_subsystem_types::DefaultSubsystemClient; +use polkadot_node_subsystem_types::{ChainApiBackend, RuntimeApiSubsystemClient}; use sp_core::traits::SpawnNamed; use crate::{ @@ -63,13 +62,9 @@ impl OverseerGen for BackGarbageCandidates { connector: OverseerConnector, args: OverseerGenArgs<'_, Spawner, RuntimeClient>, ext_args: Option, - ) -> Result< - (Overseer, Arc>>, OverseerHandle), - Error, - > + ) -> Result<(Overseer, Arc>, OverseerHandle), Error> where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, Spawner: 'static + SpawnNamed + Clone + Unpin, { let spawner = args.spawner.clone(); diff --git a/polkadot/node/malus/src/variants/common.rs b/polkadot/node/malus/src/variants/common.rs index 011fcc80e37331909434ccd2fa1bfa1e96f48f35..eb6988f81811687442a36ae3f8ec225de48dd9df 100644 --- a/polkadot/node/malus/src/variants/common.rs +++ b/polkadot/node/malus/src/variants/common.rs @@ -23,10 +23,6 @@ use crate::{ use polkadot_node_core_candidate_validation::find_validation_data; use polkadot_node_primitives::{InvalidCandidate, ValidationResult}; -use polkadot_node_subsystem::{ - messages::{CandidateValidationMessage, ValidationFailed}, - overseer, -}; use polkadot_primitives::{ CandidateCommitments, CandidateDescriptor, CandidateReceipt, PersistedValidationData, diff --git a/polkadot/node/malus/src/variants/dispute_finalized_candidates.rs b/polkadot/node/malus/src/variants/dispute_finalized_candidates.rs index b3555ba2f5beb3e45285bc443dee9a4d029b070b..7a95bdaead26e204bd4cd8b386ae02b5e12297f9 100644 --- a/polkadot/node/malus/src/variants/dispute_finalized_candidates.rs +++ b/polkadot/node/malus/src/variants/dispute_finalized_candidates.rs @@ -34,14 +34,13 @@ use futures::channel::oneshot; use polkadot_cli::{ service::{ - AuthorityDiscoveryApi, AuxStore, BabeApi, Block, Error, ExtendedOverseerGenArgs, - HeaderBackend, Overseer, OverseerConnector, OverseerGen, OverseerGenArgs, OverseerHandle, - ParachainHost, ProvideRuntimeApi, + AuxStore, Error, ExtendedOverseerGenArgs, Overseer, OverseerConnector, OverseerGen, + OverseerGenArgs, OverseerHandle, }, validator_overseer_builder, Cli, }; -use polkadot_node_subsystem::{messages::ApprovalVotingMessage, SpawnGlue}; -use polkadot_node_subsystem_types::{DefaultSubsystemClient, OverseerSignal}; +use polkadot_node_subsystem::SpawnGlue; +use polkadot_node_subsystem_types::{ChainApiBackend, OverseerSignal, RuntimeApiSubsystemClient}; use polkadot_node_subsystem_util::request_candidate_events; use polkadot_primitives::CandidateEvent; use sp_core::traits::SpawnNamed; @@ -237,13 +236,9 @@ impl OverseerGen for DisputeFinalizedCandidates { connector: OverseerConnector, args: OverseerGenArgs<'_, Spawner, RuntimeClient>, ext_args: Option, - ) -> Result< - (Overseer, Arc>>, OverseerHandle), - Error, - > + ) -> Result<(Overseer, Arc>, OverseerHandle), Error> where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, Spawner: 'static + SpawnNamed + Clone + Unpin, { gum::info!( diff --git a/polkadot/node/malus/src/variants/dispute_valid_candidates.rs b/polkadot/node/malus/src/variants/dispute_valid_candidates.rs index b9812cbb5012a4ff97e3f1ed146538147efda03b..a50fdce16e4ec41d6b9bea4f0190616f756d9193 100644 --- a/polkadot/node/malus/src/variants/dispute_valid_candidates.rs +++ b/polkadot/node/malus/src/variants/dispute_valid_candidates.rs @@ -24,14 +24,13 @@ use polkadot_cli::{ service::{ - AuthorityDiscoveryApi, AuxStore, BabeApi, Block, Error, ExtendedOverseerGenArgs, - HeaderBackend, Overseer, OverseerConnector, OverseerGen, OverseerGenArgs, OverseerHandle, - ParachainHost, ProvideRuntimeApi, + AuxStore, Error, ExtendedOverseerGenArgs, Overseer, OverseerConnector, OverseerGen, + OverseerGenArgs, OverseerHandle, }, validator_overseer_builder, Cli, }; use polkadot_node_subsystem::SpawnGlue; -use polkadot_node_subsystem_types::DefaultSubsystemClient; +use polkadot_node_subsystem_types::{ChainApiBackend, RuntimeApiSubsystemClient}; use sp_core::traits::SpawnNamed; // Filter wrapping related types. @@ -80,13 +79,9 @@ impl OverseerGen for DisputeValidCandidates { connector: OverseerConnector, args: OverseerGenArgs<'_, Spawner, RuntimeClient>, ext_args: Option, - ) -> Result< - (Overseer, Arc>>, OverseerHandle), - Error, - > + ) -> Result<(Overseer, Arc>, OverseerHandle), Error> where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, Spawner: 'static + SpawnNamed + Clone + Unpin, { let spawner = args.spawner.clone(); diff --git a/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs b/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs index 22b44ddd1dc359620b199118bebf0bb83b69dada..739ed40db362a2ae330f14b7a24cc77c1ab76159 100644 --- a/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs +++ b/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs @@ -25,14 +25,13 @@ use futures::channel::oneshot; use polkadot_cli::{ service::{ - AuthorityDiscoveryApi, AuxStore, BabeApi, Block, Error, ExtendedOverseerGenArgs, - HeaderBackend, Overseer, OverseerConnector, OverseerGen, OverseerGenArgs, OverseerHandle, - ParachainHost, ProvideRuntimeApi, + AuxStore, Error, ExtendedOverseerGenArgs, Overseer, OverseerConnector, OverseerGen, + OverseerGenArgs, OverseerHandle, }, validator_overseer_builder, Cli, }; use polkadot_node_primitives::{AvailableData, BlockData, PoV}; -use polkadot_node_subsystem_types::DefaultSubsystemClient; +use polkadot_node_subsystem_types::{ChainApiBackend, RuntimeApiSubsystemClient}; use polkadot_primitives::{CandidateDescriptor, CandidateReceipt}; use polkadot_node_subsystem_util::request_validators; @@ -52,7 +51,7 @@ use crate::{ // Import extra types relevant to the particular // subsystem. -use polkadot_node_subsystem::{messages::CandidateBackingMessage, SpawnGlue}; +use polkadot_node_subsystem::SpawnGlue; use std::sync::Arc; @@ -296,13 +295,9 @@ impl OverseerGen for SuggestGarbageCandidates { connector: OverseerConnector, args: OverseerGenArgs<'_, Spawner, RuntimeClient>, ext_args: Option, - ) -> Result< - (Overseer, Arc>>, OverseerHandle), - Error, - > + ) -> Result<(Overseer, Arc>, OverseerHandle), Error> where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, Spawner: 'static + SpawnNamed + Clone + Unpin, { gum::info!( diff --git a/polkadot/node/malus/src/variants/support_disabled.rs b/polkadot/node/malus/src/variants/support_disabled.rs index e25df56fd643cdde44b861973751b1cd23e25480..169c442db25b7426f44cdfbe2c4181233802a914 100644 --- a/polkadot/node/malus/src/variants/support_disabled.rs +++ b/polkadot/node/malus/src/variants/support_disabled.rs @@ -19,14 +19,13 @@ use polkadot_cli::{ service::{ - AuthorityDiscoveryApi, AuxStore, BabeApi, Block, Error, ExtendedOverseerGenArgs, - HeaderBackend, Overseer, OverseerConnector, OverseerGen, OverseerGenArgs, OverseerHandle, - ParachainHost, ProvideRuntimeApi, + AuxStore, Error, ExtendedOverseerGenArgs, Overseer, OverseerConnector, OverseerGen, + OverseerGenArgs, OverseerHandle, }, validator_overseer_builder, Cli, }; use polkadot_node_subsystem::SpawnGlue; -use polkadot_node_subsystem_types::DefaultSubsystemClient; +use polkadot_node_subsystem_types::{ChainApiBackend, RuntimeApiSubsystemClient}; use sp_core::traits::SpawnNamed; use crate::interceptor::*; @@ -50,13 +49,9 @@ impl OverseerGen for SupportDisabled { connector: OverseerConnector, args: OverseerGenArgs<'_, Spawner, RuntimeClient>, ext_args: Option, - ) -> Result< - (Overseer, Arc>>, OverseerHandle), - Error, - > + ) -> Result<(Overseer, Arc>, OverseerHandle), Error> where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, Spawner: 'static + SpawnNamed + Clone + Unpin, { validator_overseer_builder( diff --git a/polkadot/node/network/approval-distribution/src/lib.rs b/polkadot/node/network/approval-distribution/src/lib.rs index f4e40270160cbf54cfeb516c5d7deeed59e7373a..d360a18423e6bdea62ee0609cf5ea67f16e2e68e 100644 --- a/polkadot/node/network/approval-distribution/src/lib.rs +++ b/polkadot/node/network/approval-distribution/src/lib.rs @@ -230,7 +230,7 @@ impl ApprovalEntry { Ok(()) } - // Get the assignment certiticate and claimed candidates. + // Get the assignment certificate and claimed candidates. pub fn assignment(&self) -> (IndirectAssignmentCertV2, CandidateBitfield) { (self.assignment.clone(), self.assignment_claimed_candidates.clone()) } @@ -404,7 +404,7 @@ impl Knowledge { }, }; - // In case of succesful insertion of multiple candidate assignments create additional + // In case of successful insertion of multiple candidate assignments create additional // entries for each assigned candidate. This fakes knowledge of individual assignments, but // we need to share the same `MessageSubject` with the followup approval candidate index. if kind == MessageKind::Assignment && success && message.1.count_ones() > 1 { @@ -1897,10 +1897,10 @@ impl State { _ => break, }; - // Any peer which is in the `known_by` see and we know its peer_id authorithy id + // Any peer which is in the `known_by` see and we know its peer_id authority id // mapping has already been sent all messages it's meant to get for that block and // all in-scope prior blocks. In case, we just learnt about its peer_id - // authorithy-id mapping we have to retry sending the messages that should be sent + // authority-id mapping we have to retry sending the messages that should be sent // to it for all un-finalized blocks. if entry.known_by.contains_key(&peer_id) && !retry_known_blocks { break @@ -2199,7 +2199,7 @@ impl State { sanitized_assignments } - // Filter out obviously invalid candidate indicies. + // Filter out obviously invalid candidate indices. async fn sanitize_v1_approvals( &mut self, peer_id: PeerId, @@ -2226,7 +2226,7 @@ impl State { sanitized_approvals } - // Filter out obviously invalid candidate indicies. + // Filter out obviously invalid candidate indices. async fn sanitize_v2_approvals( &mut self, peer_id: PeerId, @@ -2260,7 +2260,7 @@ impl State { // The modifier accepts as inputs the current required-routing state, whether // the message is locally originating, and the validator index of the message issuer. // -// Then, if the topology is known, this progates messages to all peers in the required +// Then, if the topology is known, this propagates messages to all peers in the required // routing set which are aware of the block. Peers which are unaware of the block // will have the message sent when it enters their view in `unify_with_peer`. // @@ -2440,7 +2440,7 @@ impl ApprovalDistribution { gum::trace!(target: LOG_TARGET, "active leaves signal (ignored)"); // the relay chain blocks relevant to the approval subsystems // are those that are available, but not finalized yet - // actived and deactivated heads hence are irrelevant to this subsystem, other than + // activated and deactivated heads hence are irrelevant to this subsystem, other than // for tracing purposes. if let Some(activated) = update.activated { let head = activated.hash; diff --git a/polkadot/node/network/approval-distribution/src/metrics.rs b/polkadot/node/network/approval-distribution/src/metrics.rs index 0642b1b2e0cdcea51d4a34263bad87ad6612f035..60c7f2f6d3b895c1f406ed8f3dfbbbe5c499eab1 100644 --- a/polkadot/node/network/approval-distribution/src/metrics.rs +++ b/polkadot/node/network/approval-distribution/src/metrics.rs @@ -299,7 +299,7 @@ impl MetricsTrait for Metrics { prometheus::CounterVec::new( prometheus::Opts::new( "polkadot_parachain_assignments_received_result", - "Result of a processed assignement", + "Result of a processed assignment", ), &["status"] )?, diff --git a/polkadot/node/network/approval-distribution/src/tests.rs b/polkadot/node/network/approval-distribution/src/tests.rs index 6c88dd53ad364ab0eabed065c9098c1dfcdd81d9..3159fe2ae5e8d4e4df0ba7bf2f4c42b44d658686 100644 --- a/polkadot/node/network/approval-distribution/src/tests.rs +++ b/polkadot/node/network/approval-distribution/src/tests.rs @@ -394,7 +394,7 @@ fn try_import_the_same_assignment() { setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V1).await; - // Set up a gossip topology, where a, b, c and d are topology neighboors to the node under + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node under // testing. let peers_with_optional_peer_id = peers .iter() @@ -491,7 +491,7 @@ fn try_import_the_same_assignment_v2() { setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; - // Set up a gossip topology, where a, b, c and d are topology neighboors to the node under + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node under // testing. let peers_with_optional_peer_id = peers .iter() @@ -744,7 +744,7 @@ fn peer_sending_us_the_same_we_just_sent_them_is_ok() { .iter() .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) .collect_vec(); - // Setup a topology where peer_a is neigboor to current node. + // Setup a topology where peer_a is neighbor to current node. setup_gossip_topology( overseer, make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), @@ -850,7 +850,7 @@ fn import_approval_happy_path_v1_v2_peers() { .iter() .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) .collect_vec(); - // Set up a gossip topology, where a, b, and c are topology neighboors to the node. + // Set up a gossip topology, where a, b, and c are topology neighbors to the node. setup_gossip_topology( overseer, make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), @@ -972,7 +972,7 @@ fn import_approval_happy_path_v2() { .iter() .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) .collect_vec(); - // Set up a gossip topology, where a, b, and c are topology neighboors to the node. + // Set up a gossip topology, where a, b, and c are topology neighbors to the node. setup_gossip_topology( overseer, make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), @@ -1083,7 +1083,7 @@ fn multiple_assignments_covered_with_one_approval_vote() { .iter() .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) .collect_vec(); - // Set up a gossip topology, where a, b, and c, d are topology neighboors to the node. + // Set up a gossip topology, where a, b, and c, d are topology neighbors to the node. setup_gossip_topology( overseer, make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), @@ -1273,7 +1273,7 @@ fn unify_with_peer_multiple_assignments_covered_with_one_approval_vote() { .iter() .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) .collect_vec(); - // Set up a gossip topology, where a, b, and c, d are topology neighboors to the node. + // Set up a gossip topology, where a, b, and c, d are topology neighbors to the node. setup_gossip_topology( overseer, make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), @@ -1631,7 +1631,7 @@ fn update_peer_view() { .iter() .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) .collect_vec(); - // Setup a topology where peer_a is neigboor to current node. + // Setup a topology where peer_a is neighbor to current node. setup_gossip_topology( overseer, make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), @@ -1758,7 +1758,7 @@ fn update_peer_view() { assert!(state.blocks.get(&hash_c).unwrap().known_by.get(peer).is_none()); } -// Tests that updating the known peer_id for a given authorithy updates the topology +// Tests that updating the known peer_id for a given authority updates the topology // and sends the required messages #[test] fn update_peer_authority_id() { @@ -1770,9 +1770,9 @@ fn update_peer_authority_id() { let neighbour_x_index = 0; let neighbour_y_index = 2; let local_index = 1; - // X neighbour, we simulate that PeerId is not known in the beginining. + // X neighbour, we simulate that PeerId is not known in the beginning. let neighbour_x = peers.get(neighbour_x_index).unwrap().0; - // Y neighbour, we simulate that PeerId is not known in the beginining. + // Y neighbour, we simulate that PeerId is not known in the beginning. let neighbour_y = peers.get(neighbour_y_index).unwrap().0; let _state = test_harness(State::default(), |mut virtual_overseer| async move { @@ -1814,7 +1814,7 @@ fn update_peer_authority_id() { }) .collect_vec(); - // Setup a topology where peer_a is neigboor to current node. + // Setup a topology where peer_a is neighbor to current node. setup_gossip_topology( overseer, make_gossip_topology( @@ -2053,7 +2053,7 @@ fn sends_assignments_even_when_state_is_approved() { .iter() .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) .collect_vec(); - // Setup a topology where peer_a is neigboor to current node. + // Setup a topology where peer_a is neighbor to current node. setup_gossip_topology( overseer, make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), @@ -2125,7 +2125,7 @@ fn sends_assignments_even_when_state_is_approved() { } /// Same as `sends_assignments_even_when_state_is_approved_v2` but with `VRFModuloCompact` -/// assignemnts. +/// assignments. #[test] fn sends_assignments_even_when_state_is_approved_v2() { let peers = make_peers_and_authority_ids(8); @@ -2153,7 +2153,7 @@ fn sends_assignments_even_when_state_is_approved_v2() { .iter() .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) .collect_vec(); - // Setup a topology where peer_a is neigboor to current node. + // Setup a topology where peer_a is neighbor to current node. setup_gossip_topology( overseer, make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), @@ -3509,7 +3509,7 @@ fn import_versioned_approval() { setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V2).await; - // Set up a gossip topology, where a, b, c and d are topology neighboors to the node under + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node under // testing. let peers_with_optional_peer_id = peers .iter() diff --git a/polkadot/node/network/availability-distribution/Cargo.toml b/polkadot/node/network/availability-distribution/Cargo.toml index 432501ed23fbbca5e0ea49c82b8ac19efa5bd473..ac606bd377f7181a1f3b27f979ae7229ac8f45d8 100644 --- a/polkadot/node/network/availability-distribution/Cargo.toml +++ b/polkadot/node/network/availability-distribution/Cargo.toml @@ -36,3 +36,14 @@ sc-network = { path = "../../../../substrate/client/network" } futures-timer = "3.0.2" assert_matches = "1.4.0" polkadot-primitives-test-helpers = { path = "../../../primitives/test-helpers" } +polkadot-subsystem-bench = { path = "../../subsystem-bench" } + + +[[bench]] +name = "availability-distribution-regression-bench" +path = "benches/availability-distribution-regression-bench.rs" +harness = false +required-features = ["subsystem-benchmarks"] + +[features] +subsystem-benchmarks = [] diff --git a/polkadot/node/network/availability-distribution/benches/availability-distribution-regression-bench.rs b/polkadot/node/network/availability-distribution/benches/availability-distribution-regression-bench.rs new file mode 100644 index 0000000000000000000000000000000000000000..c33674a8f2f926ad0a186bc867aabe843f091e32 --- /dev/null +++ b/polkadot/node/network/availability-distribution/benches/availability-distribution-regression-bench.rs @@ -0,0 +1,91 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! availability-read regression tests +//! +//! Availability read benchmark based on Kusama parameters and scale. +//! +//! Subsystems involved: +//! - availability-distribution +//! - bitfield-distribution +//! - availability-store + +use polkadot_subsystem_bench::{ + availability::{benchmark_availability_write, prepare_test, TestState}, + configuration::TestConfiguration, + usage::BenchmarkUsage, + utils::save_to_file, +}; +use std::io::Write; + +const BENCH_COUNT: usize = 50; + +fn main() -> Result<(), String> { + let mut messages = vec![]; + let mut config = TestConfiguration::default(); + // A single node effort roughly + config.n_cores = 10; + config.n_validators = 500; + config.num_blocks = 3; + config.generate_pov_sizes(); + let state = TestState::new(&config); + + println!("Benchmarking..."); + let usages: Vec = (0..BENCH_COUNT) + .map(|n| { + print!("\r[{}{}]", "#".repeat(n), "_".repeat(BENCH_COUNT - n)); + std::io::stdout().flush().unwrap(); + let (mut env, _cfgs) = prepare_test( + &state, + polkadot_subsystem_bench::availability::TestDataAvailability::Write, + false, + ); + env.runtime().block_on(benchmark_availability_write( + "data_availability_write", + &mut env, + &state, + )) + }) + .collect(); + println!("\rDone!{}", " ".repeat(BENCH_COUNT)); + + let average_usage = BenchmarkUsage::average(&usages); + save_to_file( + "charts/availability-distribution-regression-bench.json", + average_usage.to_chart_json().map_err(|e| e.to_string())?, + ) + .map_err(|e| e.to_string())?; + println!("{}", average_usage); + + // We expect no variance for received and sent + // but use 0.001 because we operate with floats + messages.extend(average_usage.check_network_usage(&[ + ("Received from peers", 433.3, 0.001), + ("Sent to peers", 18480.0, 0.001), + ])); + messages.extend(average_usage.check_cpu_usage(&[ + ("availability-distribution", 0.012, 0.05), + ("availability-store", 0.153, 0.05), + ("bitfield-distribution", 0.026, 0.05), + ])); + + if messages.is_empty() { + Ok(()) + } else { + eprintln!("{}", messages.join("\n")); + Err("Regressions found".to_string()) + } +} diff --git a/polkadot/node/network/availability-distribution/src/requester/fetch_task/mod.rs b/polkadot/node/network/availability-distribution/src/requester/fetch_task/mod.rs index 191ee2acd973b2391773a8ca8e5fdcf7032009e5..f478defcaa96530d695eace0acad11057609d8e9 100644 --- a/polkadot/node/network/availability-distribution/src/requester/fetch_task/mod.rs +++ b/polkadot/node/network/availability-distribution/src/requester/fetch_task/mod.rs @@ -348,7 +348,7 @@ impl RunningTask { async fn do_request( &mut self, validator: &AuthorityDiscoveryId, - nerwork_error_freq: &mut gum::Freq, + network_error_freq: &mut gum::Freq, canceled_freq: &mut gum::Freq, ) -> std::result::Result { gum::trace!( @@ -395,7 +395,7 @@ impl RunningTask { }, Err(RequestError::NetworkError(err)) => { gum::warn_if_frequent!( - freq: nerwork_error_freq, + freq: network_error_freq, max_rate: gum::Times::PerHour(100), target: LOG_TARGET, origin = ?validator, diff --git a/polkadot/node/network/availability-recovery/Cargo.toml b/polkadot/node/network/availability-recovery/Cargo.toml index 9eddf5c86d2ed905888cf5e5fad4dd200fdbee08..23c4148fa858f7530405d9bd08f79625a40eb968 100644 --- a/polkadot/node/network/availability-recovery/Cargo.toml +++ b/polkadot/node/network/availability-recovery/Cargo.toml @@ -41,6 +41,13 @@ sc-network = { path = "../../../../substrate/client/network" } polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers" } polkadot-primitives-test-helpers = { path = "../../../primitives/test-helpers" } +polkadot-subsystem-bench = { path = "../../subsystem-bench" } + +[[bench]] +name = "availability-recovery-regression-bench" +path = "benches/availability-recovery-regression-bench.rs" +harness = false +required-features = ["subsystem-benchmarks"] [features] subsystem-benchmarks = [] diff --git a/polkadot/node/network/availability-recovery/benches/availability-recovery-regression-bench.rs b/polkadot/node/network/availability-recovery/benches/availability-recovery-regression-bench.rs new file mode 100644 index 0000000000000000000000000000000000000000..46a38516898f2eea48602d951e928f94838c592f --- /dev/null +++ b/polkadot/node/network/availability-recovery/benches/availability-recovery-regression-bench.rs @@ -0,0 +1,85 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! availability-read regression tests +//! +//! Availability read benchmark based on Kusama parameters and scale. +//! +//! Subsystems involved: +//! - availability-recovery + +use polkadot_subsystem_bench::{ + availability::{ + benchmark_availability_read, prepare_test, DataAvailabilityReadOptions, + TestDataAvailability, TestState, + }, + configuration::TestConfiguration, + usage::BenchmarkUsage, + utils::save_to_file, +}; +use std::io::Write; + +const BENCH_COUNT: usize = 50; + +fn main() -> Result<(), String> { + let mut messages = vec![]; + + let options = DataAvailabilityReadOptions { fetch_from_backers: true }; + let mut config = TestConfiguration::default(); + config.num_blocks = 3; + config.generate_pov_sizes(); + + let state = TestState::new(&config); + + println!("Benchmarking..."); + let usages: Vec = (0..BENCH_COUNT) + .map(|n| { + print!("\r[{}{}]", "#".repeat(n), "_".repeat(BENCH_COUNT - n)); + std::io::stdout().flush().unwrap(); + let (mut env, _cfgs) = + prepare_test(&state, TestDataAvailability::Read(options.clone()), false); + env.runtime().block_on(benchmark_availability_read( + "data_availability_read", + &mut env, + &state, + )) + }) + .collect(); + println!("\rDone!{}", " ".repeat(BENCH_COUNT)); + + let average_usage = BenchmarkUsage::average(&usages); + save_to_file( + "charts/availability-recovery-regression-bench.json", + average_usage.to_chart_json().map_err(|e| e.to_string())?, + ) + .map_err(|e| e.to_string())?; + println!("{}", average_usage); + + // We expect no variance for received and sent + // but use 0.001 because we operate with floats + messages.extend(average_usage.check_network_usage(&[ + ("Received from peers", 307200.000, 0.001), + ("Sent to peers", 1.667, 0.001), + ])); + messages.extend(average_usage.check_cpu_usage(&[("availability-recovery", 11.500, 0.05)])); + + if messages.is_empty() { + Ok(()) + } else { + eprintln!("{}", messages.join("\n")); + Err("Regressions found".to_string()) + } +} diff --git a/polkadot/node/network/availability-recovery/src/lib.rs b/polkadot/node/network/availability-recovery/src/lib.rs index fb8064878f4f6c02236afbede9f8e91b8f2dd594..94b9d9546cdecd4a244bc3de9e10c1db4c3c066d 100644 --- a/polkadot/node/network/availability-recovery/src/lib.rs +++ b/polkadot/node/network/availability-recovery/src/lib.rs @@ -408,7 +408,7 @@ async fn handle_recover( ) -> error::Result<()> { let candidate_hash = receipt.hash(); - let span = jaeger::Span::new(candidate_hash, "availbility-recovery") + let span = jaeger::Span::new(candidate_hash, "availability-recovery") .with_stage(jaeger::Stage::AvailabilityRecovery); if let Some(result) = diff --git a/polkadot/node/network/availability-recovery/src/metrics.rs b/polkadot/node/network/availability-recovery/src/metrics.rs index d82a8f9ae5faf662e05c7a8dcaf731e17756a636..9f4cddc57e43a93089fbb08ec4a18dfd77ffea8d 100644 --- a/polkadot/node/network/availability-recovery/src/metrics.rs +++ b/polkadot/node/network/availability-recovery/src/metrics.rs @@ -31,7 +31,7 @@ struct MetricsInner { chunk_requests_issued: Counter, /// Total number of bytes recovered /// - /// Gets incremented on each succesful recovery + /// Gets incremented on each successful recovery recovered_bytes_total: Counter, /// A counter for finished chunk requests. /// @@ -232,7 +232,7 @@ impl metrics::Metrics for Metrics { )?, full_recoveries_started: prometheus::register( Counter::new( - "polkadot_parachain_availability_recovery_recovieries_started", + "polkadot_parachain_availability_recovery_recoveries_started", "Total number of started recoveries.", )?, registry, diff --git a/polkadot/node/network/bitfield-distribution/src/metrics.rs b/polkadot/node/network/bitfield-distribution/src/metrics.rs index 71d8a01300f25aea09099b882a00ff5df9cba491..bd956bcbe4a3ebf9038073d8bed78ac50da756d4 100644 --- a/polkadot/node/network/bitfield-distribution/src/metrics.rs +++ b/polkadot/node/network/bitfield-distribution/src/metrics.rs @@ -69,14 +69,14 @@ impl MetricsTrait for Metrics { let metrics = MetricsInner { sent_own_availability_bitfields: prometheus::register( prometheus::Counter::new( - "polkadot_parachain_sent_own_availabilty_bitfields_total", + "polkadot_parachain_sent_own_availability_bitfields_total", "Number of own availability bitfields sent to other peers.", )?, registry, )?, received_availability_bitfields: prometheus::register( prometheus::Counter::new( - "polkadot_parachain_received_availabilty_bitfields_total", + "polkadot_parachain_received_availability_bitfields_total", "Number of valid availability bitfields received from other peers.", )?, registry, diff --git a/polkadot/node/network/bitfield-distribution/src/tests.rs b/polkadot/node/network/bitfield-distribution/src/tests.rs index ba2434ea47d69469e812d6274b17b43c3b6b5ee3..188b51ebcccae89fd6f69410fd054d25eeabbb01 100644 --- a/polkadot/node/network/bitfield-distribution/src/tests.rs +++ b/polkadot/node/network/bitfield-distribution/src/tests.rs @@ -150,7 +150,7 @@ fn receive_invalid_signature() { let signing_context = SigningContext { session_index: 1, parent_hash: hash_a }; - // another validator not part of the validatorset + // another validator not part of the validator set let keystore: KeystorePtr = Arc::new(MemoryKeystore::new()); let malicious = Keystore::sr25519_generate_new(&*keystore, ValidatorId::ID, None) .expect("Malicious key created"); diff --git a/polkadot/node/network/collator-protocol/Cargo.toml b/polkadot/node/network/collator-protocol/Cargo.toml index f0f8be0f7bab240a928be9f30a18ca10b14eb16d..cfd88df958ce94f65da14eeab91040672cc4bf7a 100644 --- a/polkadot/node/network/collator-protocol/Cargo.toml +++ b/polkadot/node/network/collator-protocol/Cargo.toml @@ -41,3 +41,7 @@ parity-scale-codec = { version = "3.6.1", features = ["std"] } polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers" } polkadot-primitives-test-helpers = { path = "../../../primitives/test-helpers" } + +[features] +default = [] +elastic-scaling-experimental = [] diff --git a/polkadot/node/network/collator-protocol/src/collator_side/collation.rs b/polkadot/node/network/collator-protocol/src/collator_side/collation.rs index 53f947142d10d615496b184b22ce7d69d815c977..57e1479a449b75e29a11292d720543b71c382f9c 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/collation.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/collation.rs @@ -27,6 +27,7 @@ use polkadot_node_network_protocol::{ PeerId, }; use polkadot_node_primitives::PoV; +use polkadot_node_subsystem::messages::ParentHeadData; use polkadot_primitives::{CandidateHash, CandidateReceipt, Hash, Id as ParaId}; /// The status of a collation as seen from the collator. @@ -59,10 +60,10 @@ impl CollationStatus { pub struct Collation { /// Candidate receipt. pub receipt: CandidateReceipt, - /// Parent head-data hash. - pub parent_head_data_hash: Hash, /// Proof to verify the state transition of the parachain. pub pov: PoV, + /// Parent head-data (or just hash). + pub parent_head_data: ParentHeadData, /// Collation status. pub status: CollationStatus, } diff --git a/polkadot/node/network/collator-protocol/src/collator_side/mod.rs b/polkadot/node/network/collator-protocol/src/collator_side/mod.rs index 8fb0bb2154445f99000f329ff9aeb30e29c02092..9f306f288a16207d8ee9a3be8ab3771625f46d7e 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/mod.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/mod.rs @@ -40,7 +40,8 @@ use polkadot_node_primitives::{CollationSecondedSignal, PoV, Statement}; use polkadot_node_subsystem::{ jaeger, messages::{ - CollatorProtocolMessage, NetworkBridgeEvent, NetworkBridgeTxMessage, RuntimeApiMessage, + CollatorProtocolMessage, NetworkBridgeEvent, NetworkBridgeTxMessage, ParentHeadData, + RuntimeApiMessage, }, overseer, CollatorProtocolSenderTrait, FromOrchestra, OverseerSignal, PerLeafSpan, }; @@ -55,7 +56,7 @@ use polkadot_node_subsystem_util::{ }; use polkadot_primitives::{ AuthorityDiscoveryId, CandidateHash, CandidateReceipt, CollatorPair, CoreIndex, CoreState, - GroupIndex, Hash, Id as ParaId, SessionIndex, + GroupIndex, Hash, HeadData, Id as ParaId, SessionIndex, }; use super::LOG_TARGET; @@ -347,6 +348,7 @@ async fn distribute_collation( receipt: CandidateReceipt, parent_head_data_hash: Hash, pov: PoV, + parent_head_data: HeadData, result_sender: Option>, ) -> Result<()> { let candidate_relay_parent = receipt.descriptor.relay_parent; @@ -394,12 +396,11 @@ async fn distribute_collation( return Ok(()) } - // Determine which core the para collated-on is assigned to. + // Determine which core(s) the para collated-on is assigned to. // If it is not scheduled then ignore the message. - let (our_core, num_cores) = - match determine_core(ctx.sender(), id, candidate_relay_parent, relay_parent_mode).await? { - Some(core) => core, - None => { + let (our_cores, num_cores) = + match determine_cores(ctx.sender(), id, candidate_relay_parent, relay_parent_mode).await? { + (cores, _num_cores) if cores.is_empty() => { gum::warn!( target: LOG_TARGET, para_id = %id, @@ -408,8 +409,20 @@ async fn distribute_collation( return Ok(()) }, + (cores, num_cores) => (cores, num_cores), }; + let elastic_scaling = our_cores.len() > 1; + if elastic_scaling { + gum::debug!( + target: LOG_TARGET, + para_id = %id, + cores = ?our_cores, + "{} is assigned to {} cores at {}", id, our_cores.len(), candidate_relay_parent, + ); + } + + let our_core = our_cores[0]; // Determine the group on that core. // // When prospective parachains are disabled, candidate relay parent here is @@ -463,9 +476,15 @@ async fn distribute_collation( state.collation_result_senders.insert(candidate_hash, result_sender); } + let parent_head_data = if elastic_scaling { + ParentHeadData::WithData { hash: parent_head_data_hash, head_data: parent_head_data } + } else { + ParentHeadData::OnlyHash(parent_head_data_hash) + }; + per_relay_parent.collations.insert( candidate_hash, - Collation { receipt, parent_head_data_hash, pov, status: CollationStatus::Created }, + Collation { receipt, pov, parent_head_data, status: CollationStatus::Created }, ); // If prospective parachains are disabled, a leaf should be known to peer. @@ -506,15 +525,17 @@ async fn distribute_collation( Ok(()) } -/// Get the Id of the Core that is assigned to the para being collated on if any +/// Get the core indices that are assigned to the para being collated on if any /// and the total number of cores. -async fn determine_core( +async fn determine_cores( sender: &mut impl overseer::SubsystemSender, para_id: ParaId, relay_parent: Hash, relay_parent_mode: ProspectiveParachainsMode, -) -> Result> { +) -> Result<(Vec, usize)> { let cores = get_availability_cores(sender, relay_parent).await?; + let n_cores = cores.len(); + let mut assigned_cores = Vec::new(); for (idx, core) in cores.iter().enumerate() { let core_para_id = match core { @@ -531,11 +552,11 @@ async fn determine_core( }; if core_para_id == Some(para_id) { - return Ok(Some(((idx as u32).into(), cores.len()))) + assigned_cores.push(CoreIndex::from(idx as u32)); } } - Ok(None) + Ok((assigned_cores, n_cores)) } /// Validators of a particular group index. @@ -718,7 +739,7 @@ async fn advertise_collation( let wire_message = protocol_v2::CollatorProtocolMessage::AdvertiseCollation { relay_parent, candidate_hash: *candidate_hash, - parent_head_data_hash: collation.parent_head_data_hash, + parent_head_data_hash: collation.parent_head_data.hash(), }; Versioned::V2(protocol_v2::CollationProtocol::CollatorProtocol(wire_message)) }, @@ -763,20 +784,26 @@ async fn process_msg( CollateOn(id) => { state.collating_on = Some(id); }, - DistributeCollation(receipt, parent_head_data_hash, pov, result_sender) => { + DistributeCollation { + candidate_receipt, + parent_head_data_hash, + pov, + parent_head_data, + result_sender, + } => { let _span1 = state .span_per_relay_parent - .get(&receipt.descriptor.relay_parent) + .get(&candidate_receipt.descriptor.relay_parent) .map(|s| s.child("distributing-collation")); let _span2 = jaeger::Span::new(&pov, "distributing-collation"); match state.collating_on { - Some(id) if receipt.descriptor.para_id != id => { + Some(id) if candidate_receipt.descriptor.para_id != id => { // If the ParaId of a collation requested to be distributed does not match // the one we expect, we ignore the message. gum::warn!( target: LOG_TARGET, - para_id = %receipt.descriptor.para_id, + para_id = %candidate_receipt.descriptor.para_id, collating_on = %id, "DistributeCollation for unexpected para_id", ); @@ -788,9 +815,10 @@ async fn process_msg( runtime, state, id, - receipt, + candidate_receipt, parent_head_data_hash, pov, + parent_head_data, result_sender, ) .await?; @@ -798,7 +826,7 @@ async fn process_msg( None => { gum::warn!( target: LOG_TARGET, - para_id = %receipt.descriptor.para_id, + para_id = %candidate_receipt.descriptor.para_id, "DistributeCollation message while not collating on any", ); }, @@ -835,6 +863,7 @@ async fn send_collation( request: VersionedCollationRequest, receipt: CandidateReceipt, pov: PoV, + parent_head_data: ParentHeadData, ) { let (tx, rx) = oneshot::channel(); @@ -842,14 +871,28 @@ async fn send_collation( let peer_id = request.peer_id(); let candidate_hash = receipt.hash(); - // The response payload is the same for both versions of protocol - // and doesn't have v2 alias for simplicity. - let response = OutgoingResponse { - result: Ok(request_v1::CollationFetchingResponse::Collation(receipt, pov)), - reputation_changes: Vec::new(), - sent_feedback: Some(tx), + #[cfg(feature = "elastic-scaling-experimental")] + let result = match parent_head_data { + ParentHeadData::WithData { head_data, .. } => + Ok(request_v2::CollationFetchingResponse::CollationWithParentHeadData { + receipt, + pov, + parent_head_data: head_data, + }), + ParentHeadData::OnlyHash(_) => + Ok(request_v1::CollationFetchingResponse::Collation(receipt, pov)), + }; + #[cfg(not(feature = "elastic-scaling-experimental"))] + let result = { + // suppress unused warning + let _parent_head_data = parent_head_data; + + Ok(request_v1::CollationFetchingResponse::Collation(receipt, pov)) }; + let response = + OutgoingResponse { result, reputation_changes: Vec::new(), sent_feedback: Some(tx) }; + if let Err(_) = request.send_outgoing_response(response) { gum::warn!(target: LOG_TARGET, "Sending collation response failed"); } @@ -1027,9 +1070,13 @@ async fn handle_incoming_request( return Ok(()) }, }; - let (receipt, pov) = if let Some(collation) = collation { + let (receipt, pov, parent_head_data) = if let Some(collation) = collation { collation.status.advance_to_requested(); - (collation.receipt.clone(), collation.pov.clone()) + ( + collation.receipt.clone(), + collation.pov.clone(), + collation.parent_head_data.clone(), + ) } else { gum::warn!( target: LOG_TARGET, @@ -1068,7 +1115,7 @@ async fn handle_incoming_request( waiting.collation_fetch_active = true; // Obtain a timer for sending collation let _ = state.metrics.time_collation_distribution("send"); - send_collation(state, req, receipt, pov).await; + send_collation(state, req, receipt, pov, parent_head_data).await; } }, Some(our_para_id) => { @@ -1453,8 +1500,9 @@ async fn run_inner( if let Some(collation) = next_collation { let receipt = collation.receipt.clone(); let pov = collation.pov.clone(); + let parent_head_data = collation.parent_head_data.clone(); - send_collation(&mut state, next, receipt, pov).await; + send_collation(&mut state, next, receipt, pov, parent_head_data).await; } }, (candidate_hash, peer_id) = state.advertisement_timeouts.select_next_some() => { diff --git a/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs b/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs index 1b1194c72706703283d0002a149a59d950d33975..38e6780eb7d201ed4840cedf2d3bf0c5c310cf07 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs @@ -142,6 +142,21 @@ impl Default for TestState { } impl TestState { + /// Adds a few more scheduled cores to the state for the same para id + /// compared to the default. + #[cfg(feature = "elastic-scaling-experimental")] + pub fn with_elastic_scaling() -> Self { + let mut state = Self::default(); + let para_id = state.para_id; + state + .availability_cores + .push(CoreState::Scheduled(ScheduledCore { para_id, collator: None })); + state + .availability_cores + .push(CoreState::Scheduled(ScheduledCore { para_id, collator: None })); + state + } + fn current_group_validator_indices(&self) -> &[ValidatorIndex] { let core_num = self.availability_cores.len(); let GroupIndex(group_idx) = self.group_rotation_info.group_for_core(CoreIndex(0), core_num); @@ -356,12 +371,13 @@ async fn distribute_collation_with_receipt( ) -> DistributeCollation { overseer_send( virtual_overseer, - CollatorProtocolMessage::DistributeCollation( - candidate.clone(), + CollatorProtocolMessage::DistributeCollation { + candidate_receipt: candidate.clone(), parent_head_data_hash, - pov.clone(), - None, - ), + pov: pov.clone(), + parent_head_data: HeadData(vec![1, 2, 3]), + result_sender: None, + }, ) .await; @@ -627,6 +643,18 @@ async fn send_peer_view_change( .await; } +fn decode_collation_response(bytes: &[u8]) -> (CandidateReceipt, PoV) { + let response: request_v1::CollationFetchingResponse = + request_v1::CollationFetchingResponse::decode(&mut &bytes[..]) + .expect("Decoding should work"); + match response { + request_v1::CollationFetchingResponse::Collation(receipt, pov) => (receipt, pov), + request_v1::CollationFetchingResponse::CollationWithParentHeadData { + receipt, pov, .. + } => (receipt, pov), + } +} + #[test] fn advertise_and_send_collation() { let mut test_state = TestState::default(); @@ -736,12 +764,10 @@ fn advertise_and_send_collation() { assert_matches!( rx.await, Ok(full_response) => { - let request_v1::CollationFetchingResponse::Collation(receipt, pov): request_v1::CollationFetchingResponse - = request_v1::CollationFetchingResponse::decode( - &mut full_response.result - .expect("We should have a proper answer").as_ref() - ) - .expect("Decoding should work"); + let (receipt, pov) = decode_collation_response( + full_response.result + .expect("We should have a proper answer").as_ref() + ); assert_eq!(receipt, candidate); assert_eq!(pov, pov_block); } @@ -1338,12 +1364,10 @@ where let feedback_tx = assert_matches!( rx.await, Ok(full_response) => { - let request_v1::CollationFetchingResponse::Collation(receipt, pov): request_v1::CollationFetchingResponse - = request_v1::CollationFetchingResponse::decode( - &mut full_response.result - .expect("We should have a proper answer").as_ref() - ) - .expect("Decoding should work"); + let (receipt, pov) = decode_collation_response( + full_response.result + .expect("We should have a proper answer").as_ref() + ); assert_eq!(receipt, candidate); assert_eq!(pov, pov_block); @@ -1375,12 +1399,10 @@ where assert_matches!( rx.await, Ok(full_response) => { - let request_v1::CollationFetchingResponse::Collation(receipt, pov): request_v1::CollationFetchingResponse - = request_v1::CollationFetchingResponse::decode( - &mut full_response.result - .expect("We should have a proper answer").as_ref() - ) - .expect("Decoding should work"); + let (receipt, pov) = decode_collation_response( + full_response.result + .expect("We should have a proper answer").as_ref() + ); assert_eq!(receipt, candidate); assert_eq!(pov, pov_block); @@ -1469,11 +1491,10 @@ fn connect_to_buffered_groups() { assert_matches!( rx.await, Ok(full_response) => { - let request_v1::CollationFetchingResponse::Collation(..) = - request_v1::CollationFetchingResponse::decode( - &mut full_response.result.expect("We should have a proper answer").as_ref(), - ) - .expect("Decoding should work"); + let _ = decode_collation_response( + full_response.result + .expect("We should have a proper answer").as_ref() + ); } ); diff --git a/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs b/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs index fd9d7a746ebe49623ff0feebd48793e8e603807b..e419cd5444f5aabd24e1abe76d82caa6358fe179 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs @@ -271,12 +271,13 @@ fn distribute_collation_from_implicit_view() { .build(); overseer_send( virtual_overseer, - CollatorProtocolMessage::DistributeCollation( - candidate.clone(), + CollatorProtocolMessage::DistributeCollation { + candidate_receipt: candidate.clone(), parent_head_data_hash, - pov.clone(), - None, - ), + pov: pov.clone(), + parent_head_data: HeadData(vec![1, 2, 3]), + result_sender: None, + }, ) .await; @@ -351,12 +352,13 @@ fn distribute_collation_up_to_limit() { .build(); overseer_send( virtual_overseer, - CollatorProtocolMessage::DistributeCollation( - candidate.clone(), + CollatorProtocolMessage::DistributeCollation { + candidate_receipt: candidate.clone(), parent_head_data_hash, - pov.clone(), - None, - ), + pov: pov.clone(), + parent_head_data: HeadData(vec![1, 2, 3]), + result_sender: None, + }, ) .await; @@ -370,6 +372,119 @@ fn distribute_collation_up_to_limit() { ) } +/// Tests that collator send the parent head data in +/// case the para is assigned to multiple cores (elastic scaling). +#[test] +#[cfg(feature = "elastic-scaling-experimental")] +fn send_parent_head_data_for_elastic_scaling() { + let test_state = TestState::with_elastic_scaling(); + + let local_peer_id = test_state.local_peer_id; + let collator_pair = test_state.collator_pair.clone(); + + test_harness( + local_peer_id, + collator_pair, + ReputationAggregator::new(|_| true), + |test_harness| async move { + let mut virtual_overseer = test_harness.virtual_overseer; + let req_v1_cfg = test_harness.req_v1_cfg; + let mut req_v2_cfg = test_harness.req_v2_cfg; + + let head_b = Hash::from_low_u64_be(129); + let head_b_num: u32 = 63; + + // Set collating para id. + overseer_send( + &mut virtual_overseer, + CollatorProtocolMessage::CollateOn(test_state.para_id), + ) + .await; + update_view(&mut virtual_overseer, &test_state, vec![(head_b, head_b_num)], 1).await; + + let pov_data = PoV { block_data: BlockData(vec![1 as u8]) }; + let candidate = TestCandidateBuilder { + para_id: test_state.para_id, + relay_parent: head_b, + pov_hash: pov_data.hash(), + ..Default::default() + } + .build(); + + let phd = HeadData(vec![1, 2, 3]); + let phdh = phd.hash(); + + distribute_collation_with_receipt( + &mut virtual_overseer, + &test_state, + head_b, + true, + candidate.clone(), + pov_data.clone(), + phdh, + ) + .await; + + let peer = test_state.validator_peer_id[0]; + let validator_id = test_state.current_group_validator_authority_ids()[0].clone(); + connect_peer( + &mut virtual_overseer, + peer, + CollationVersion::V2, + Some(validator_id.clone()), + ) + .await; + expect_declare_msg_v2(&mut virtual_overseer, &test_state, &peer).await; + + send_peer_view_change(&mut virtual_overseer, &peer, vec![head_b]).await; + let hashes: Vec<_> = vec![candidate.hash()]; + expect_advertise_collation_msg(&mut virtual_overseer, &peer, head_b, Some(hashes)) + .await; + + let (pending_response, rx) = oneshot::channel(); + req_v2_cfg + .inbound_queue + .as_mut() + .unwrap() + .send(RawIncomingRequest { + peer, + payload: request_v2::CollationFetchingRequest { + relay_parent: head_b, + para_id: test_state.para_id, + candidate_hash: candidate.hash(), + } + .encode(), + pending_response, + }) + .await + .unwrap(); + + assert_matches!( + rx.await, + Ok(full_response) => { + let response: request_v2::CollationFetchingResponse = + request_v2::CollationFetchingResponse::decode(&mut + full_response.result + .expect("We should have a proper answer").as_ref() + ).expect("Decoding should work"); + assert_matches!( + response, + request_v1::CollationFetchingResponse::CollationWithParentHeadData { + receipt, pov, parent_head_data + } => { + assert_eq!(receipt, candidate); + assert_eq!(pov, pov_data); + assert_eq!(parent_head_data, phd); + } + ); + } + ); + + TestHarness { virtual_overseer, req_v1_cfg, req_v2_cfg } + }, + ) +} + /// Tests that collator correctly handles peer V2 requests. #[test] fn advertise_and_send_collation_by_hash() { @@ -469,12 +584,10 @@ fn advertise_and_send_collation_by_hash() { rx.await, Ok(full_response) => { // Response is the same for v2. - let request_v1::CollationFetchingResponse::Collation(receipt, pov): request_v1::CollationFetchingResponse - = request_v1::CollationFetchingResponse::decode( - &mut full_response.result - .expect("We should have a proper answer").as_ref() - ) - .expect("Decoding should work"); + let (receipt, pov) = decode_collation_response( + full_response.result + .expect("We should have a proper answer").as_ref() + ); assert_eq!(receipt, candidate); assert_eq!(pov, pov_block); } diff --git a/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs b/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs index 5b88efc99d8307594c5ac726d1af5ab141e4718d..1533f2eda5a57d332ea899e1dd81764f31c86dad 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs @@ -90,8 +90,7 @@ impl ValidatorGroupsBuffer { } } - /// Returns discovery ids of validators we have at least one advertised-but-not-fetched - /// collation for. + /// Returns discovery ids of validators we are assigned to in this backing group window. pub fn validators_to_connect(&self) -> Vec { let validators_num = self.validators.len(); let bits = self @@ -99,11 +98,22 @@ impl ValidatorGroupsBuffer { .values() .fold(bitvec![0; validators_num], |acc, next| acc | next); - self.validators + let mut should_be_connected: Vec = self + .validators .iter() .enumerate() .filter_map(|(idx, authority_id)| bits[idx].then_some(authority_id.clone())) - .collect() + .collect(); + + if let Some(last_group) = self.group_infos.iter().last() { + for validator in self.validators.iter().rev().take(last_group.len) { + if !should_be_connected.contains(validator) { + should_be_connected.push(validator.clone()); + } + } + } + + should_be_connected } /// Note a new advertisement, marking that we want to be connected to validators @@ -279,7 +289,7 @@ mod tests { assert_eq!(buf.validators_to_connect(), validators[..2].to_vec()); buf.reset_validator_interest(hash_a, &validators[1]); - assert_eq!(buf.validators_to_connect(), vec![validators[0].clone()]); + assert_eq!(buf.validators_to_connect(), validators[0..2].to_vec()); buf.note_collation_advertised(hash_b, 0, GroupIndex(1), &validators[2..]); assert_eq!(buf.validators_to_connect(), validators[2..].to_vec()); @@ -287,7 +297,11 @@ mod tests { for validator in &validators[2..] { buf.reset_validator_interest(hash_b, validator); } - assert!(buf.validators_to_connect().is_empty()); + let mut expected = validators[2..].to_vec(); + expected.sort(); + let mut result = buf.validators_to_connect(); + result.sort(); + assert_eq!(result, expected); } #[test] @@ -320,10 +334,18 @@ mod tests { } buf.reset_validator_interest(hashes[1], &validators[0]); - assert_eq!(buf.validators_to_connect(), validators[..2].to_vec()); + let mut expected: Vec<_> = validators[..4].iter().cloned().collect(); + let mut result = buf.validators_to_connect(); + expected.sort(); + result.sort(); + assert_eq!(result, expected); buf.reset_validator_interest(hashes[0], &validators[0]); - assert_eq!(buf.validators_to_connect(), vec![validators[1].clone()]); + let mut expected: Vec<_> = validators[1..4].iter().cloned().collect(); + expected.sort(); + let mut result = buf.validators_to_connect(); + result.sort(); + assert_eq!(result, expected); buf.note_collation_advertised(hashes[3], 0, GroupIndex(1), &validators[2..4]); buf.note_collation_advertised( diff --git a/polkadot/node/network/collator-protocol/src/error.rs b/polkadot/node/network/collator-protocol/src/error.rs index 9348198e70853de3ab954dd51d9c17836f8d8dd6..0f5e0699d85c026cb3ad489de919cbb9c82ed50c 100644 --- a/polkadot/node/network/collator-protocol/src/error.rs +++ b/polkadot/node/network/collator-protocol/src/error.rs @@ -89,13 +89,21 @@ pub enum SecondingError { #[error("Received duplicate collation from the peer")] Duplicate, + + #[error("The provided parent head data does not match the hash")] + ParentHeadDataMismatch, } impl SecondingError { /// Returns true if an error indicates that a peer is malicious. pub fn is_malicious(&self) -> bool { use SecondingError::*; - matches!(self, PersistedValidationDataMismatch | CandidateHashMismatch | Duplicate) + matches!( + self, + PersistedValidationDataMismatch | + CandidateHashMismatch | + Duplicate | ParentHeadDataMismatch + ) } } diff --git a/polkadot/node/network/collator-protocol/src/validator_side/collation.rs b/polkadot/node/network/collator-protocol/src/validator_side/collation.rs index d6f34fc81b82568f77835a31706abeab5ac51d13..8c3889a3554865c919f2eb33a8f86cce15317ff3 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/collation.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/collation.rs @@ -41,7 +41,8 @@ use polkadot_node_subsystem_util::{ metrics::prometheus::prometheus::HistogramTimer, runtime::ProspectiveParachainsMode, }; use polkadot_primitives::{ - CandidateHash, CandidateReceipt, CollatorId, Hash, Id as ParaId, PersistedValidationData, + CandidateHash, CandidateReceipt, CollatorId, Hash, HeadData, Id as ParaId, + PersistedValidationData, }; use tokio_util::sync::CancellationToken; @@ -120,7 +121,7 @@ impl PendingCollation { } } -/// v2 advertisement that was rejected by the backing +/// v2 or v3 advertisement that was rejected by the backing /// subsystem. Validator may fetch it later if its fragment /// membership gets recognized before relay parent goes out of view. #[derive(Debug, Clone)] @@ -143,6 +144,7 @@ pub fn fetched_collation_sanity_check( advertised: &PendingCollation, fetched: &CandidateReceipt, persisted_validation_data: &PersistedValidationData, + maybe_parent_head_and_hash: Option<(HeadData, Hash)>, ) -> Result<(), SecondingError> { if persisted_validation_data.hash() != fetched.descriptor().persisted_validation_data_hash { Err(SecondingError::PersistedValidationDataMismatch) @@ -151,6 +153,8 @@ pub fn fetched_collation_sanity_check( .map_or(false, |pc| pc.candidate_hash() != fetched.hash()) { Err(SecondingError::CandidateHashMismatch) + } else if maybe_parent_head_and_hash.map_or(false, |(head, hash)| head.hash() != hash) { + Err(SecondingError::ParentHeadDataMismatch) } else { Ok(()) } @@ -176,6 +180,9 @@ pub struct PendingCollationFetch { pub candidate_receipt: CandidateReceipt, /// Proof of validity. pub pov: PoV, + /// Optional parachain parent head data. + /// Only needed for elastic scaling. + pub maybe_parent_head_data: Option, } /// The status of the collations in [`CollationsPerRelayParent`]. @@ -359,7 +366,7 @@ impl Future for CollationFetchRequest { }); match &res { - Poll::Ready((_, Ok(request_v1::CollationFetchingResponse::Collation(..)))) => { + Poll::Ready((_, Ok(_))) => { self.span.as_mut().map(|s| s.add_string_tag("success", "true")); }, Poll::Ready((_, Err(_))) => { diff --git a/polkadot/node/network/collator-protocol/src/validator_side/mod.rs b/polkadot/node/network/collator-protocol/src/validator_side/mod.rs index 48ad3c711a6db9c5e9e09c26d1a6290ffbed60f4..d23279e875419b19052380d8d25a1cda6f5d4dfa 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/mod.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/mod.rs @@ -44,7 +44,7 @@ use polkadot_node_subsystem::{ jaeger, messages::{ CanSecondRequest, CandidateBackingMessage, CollatorProtocolMessage, IfDisconnected, - NetworkBridgeEvent, NetworkBridgeTxMessage, ProspectiveParachainsMessage, + NetworkBridgeEvent, NetworkBridgeTxMessage, ParentHeadData, ProspectiveParachainsMessage, ProspectiveValidationDataRequest, }, overseer, CollatorProtocolSenderTrait, FromOrchestra, OverseerSignal, PerLeafSpan, @@ -55,7 +55,7 @@ use polkadot_node_subsystem_util::{ runtime::{prospective_parachains_mode, ProspectiveParachainsMode}, }; use polkadot_primitives::{ - CandidateHash, CollatorId, CoreState, Hash, Id as ParaId, OccupiedCoreAssumption, + CandidateHash, CollatorId, CoreState, Hash, HeadData, Id as ParaId, OccupiedCoreAssumption, PersistedValidationData, }; @@ -723,7 +723,7 @@ async fn request_collation( pending_collation, collator_id: collator_id.clone(), collator_protocol_version: peer_protocol_version, - from_collator: response_recv.boxed(), + from_collator: response_recv, cancellation_token: cancellation_token.clone(), span: state .span_per_relay_parent @@ -889,16 +889,16 @@ async fn process_incoming_peer_message( modify_reputation(&mut state.reputation, ctx.sender(), origin, rep).await; } }, - Versioned::V2(V2::AdvertiseCollation { + Versioned::V3(V2::AdvertiseCollation { relay_parent, candidate_hash, parent_head_data_hash, }) | - Versioned::V3(V2::AdvertiseCollation { + Versioned::V2(V2::AdvertiseCollation { relay_parent, candidate_hash, parent_head_data_hash, - }) => + }) => { if let Err(err) = handle_advertisement( ctx.sender(), state, @@ -920,7 +920,8 @@ async fn process_incoming_peer_message( if let Some(rep) = err.reputation_changes() { modify_reputation(&mut state.reputation, ctx.sender(), origin, rep).await; } - }, + } + }, Versioned::V1(V1::CollationSeconded(..)) | Versioned::V2(V2::CollationSeconded(..)) | Versioned::V3(V2::CollationSeconded(..)) => { @@ -1477,7 +1478,7 @@ async fn process_msg( "CollateOn message is not expected on the validator side of the protocol", ); }, - DistributeCollation(..) => { + DistributeCollation { .. } => { gum::warn!( target: LOG_TARGET, "DistributeCollation message is not expected on the validator side of the protocol", @@ -1776,14 +1777,21 @@ async fn request_prospective_validation_data( candidate_relay_parent: Hash, parent_head_data_hash: Hash, para_id: ParaId, + maybe_parent_head_data: Option, ) -> std::result::Result, SecondingError> where Sender: CollatorProtocolSenderTrait, { let (tx, rx) = oneshot::channel(); + let parent_head_data = if let Some(head_data) = maybe_parent_head_data { + ParentHeadData::WithData { head_data, hash: parent_head_data_hash } + } else { + ParentHeadData::OnlyHash(parent_head_data_hash) + }; + let request = - ProspectiveValidationDataRequest { para_id, candidate_relay_parent, parent_head_data_hash }; + ProspectiveValidationDataRequest { para_id, candidate_relay_parent, parent_head_data }; sender .send_message(ProspectiveParachainsMessage::GetProspectiveValidationData(request, tx)) @@ -1797,7 +1805,7 @@ where async fn kick_off_seconding( ctx: &mut Context, state: &mut State, - PendingCollationFetch { mut collation_event, candidate_receipt, pov }: PendingCollationFetch, + PendingCollationFetch { mut collation_event, candidate_receipt, pov, maybe_parent_head_data }: PendingCollationFetch, ) -> std::result::Result<(), SecondingError> { let pending_collation = collation_event.pending_collation; let relay_parent = pending_collation.relay_parent; @@ -1821,38 +1829,46 @@ async fn kick_off_seconding( collation_event.pending_collation.commitments_hash = Some(candidate_receipt.commitments_hash); - let pvd = match ( + let (maybe_pvd, maybe_parent_head_and_hash) = match ( collation_event.collator_protocol_version, collation_event.pending_collation.prospective_candidate, ) { (CollationVersion::V2, Some(ProspectiveCandidate { parent_head_data_hash, .. })) if per_relay_parent.prospective_parachains_mode.is_enabled() => - request_prospective_validation_data( + { + let pvd = request_prospective_validation_data( ctx.sender(), relay_parent, parent_head_data_hash, pending_collation.para_id, + maybe_parent_head_data.clone(), ) - .await?, + .await?; + + (pvd, maybe_parent_head_data.map(|head_data| (head_data, parent_head_data_hash))) + }, // Support V2 collators without async backing enabled. - (CollationVersion::V2, Some(_)) | (CollationVersion::V1, _) => - request_persisted_validation_data( + (CollationVersion::V2, Some(_)) | (CollationVersion::V1, _) => { + let pvd = request_persisted_validation_data( ctx.sender(), candidate_receipt.descriptor().relay_parent, candidate_receipt.descriptor().para_id, ) - .await?, + .await?; + (pvd, None) + }, _ => { // `handle_advertisement` checks for protocol mismatch. return Ok(()) }, - } - .ok_or(SecondingError::PersistedValidationDataNotFound)?; + }; + let pvd = maybe_pvd.ok_or(SecondingError::PersistedValidationDataNotFound)?; fetched_collation_sanity_check( &collation_event.pending_collation, &candidate_receipt, &pvd, + maybe_parent_head_and_hash, )?; ctx.send_message(CandidateBackingMessage::Second( @@ -1883,7 +1899,7 @@ async fn disconnect_inactive_peers( ) { for (peer, peer_data) in peers { if peer_data.is_inactive(&eviction_policy) { - gum::trace!(target: LOG_TARGET, "Disconnecting inactive peer"); + gum::trace!(target: LOG_TARGET, ?peer, "Disconnecting inactive peer"); disconnect_peer(sender, *peer).await; } } @@ -1978,9 +1994,10 @@ async fn handle_collation_fetch_response( ); Err(None) }, - Ok(request_v1::CollationFetchingResponse::Collation(receipt, _)) - if receipt.descriptor().para_id != pending_collation.para_id => - { + Ok( + request_v1::CollationFetchingResponse::Collation(receipt, _) | + request_v1::CollationFetchingResponse::CollationWithParentHeadData { receipt, .. }, + ) if receipt.descriptor().para_id != pending_collation.para_id => { gum::debug!( target: LOG_TARGET, expected_para_id = ?pending_collation.para_id, @@ -2010,6 +2027,33 @@ async fn handle_collation_fetch_response( }, candidate_receipt, pov, + maybe_parent_head_data: None, + }) + }, + Ok(request_v2::CollationFetchingResponse::CollationWithParentHeadData { + receipt, + pov, + parent_head_data, + }) => { + gum::debug!( + target: LOG_TARGET, + para_id = %pending_collation.para_id, + hash = ?pending_collation.relay_parent, + candidate_hash = ?receipt.hash(), + "Received collation (v3)", + ); + let _span = jaeger::Span::new(&pov, "received-collation"); + + metrics_result = Ok(()); + Ok(PendingCollationFetch { + collation_event: CollationEvent { + collator_id, + pending_collation, + collator_protocol_version, + }, + candidate_receipt: receipt, + pov, + maybe_parent_head_data: Some(parent_head_data), }) }, }; diff --git a/polkadot/node/network/collator-protocol/src/validator_side/tests/prospective_parachains.rs b/polkadot/node/network/collator-protocol/src/validator_side/tests/prospective_parachains.rs index 23963e65554eb379693f4dc25e6ef2a4b9ebea89..eaa725f2642ed38e3a6f222f9624dd6e2bc4fcce 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/tests/prospective_parachains.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/tests/prospective_parachains.rs @@ -754,6 +754,126 @@ fn fetched_collation_sanity_check() { }); } +#[test] +fn sanity_check_invalid_parent_head_data() { + let test_state = TestState::default(); + + test_harness(ReputationAggregator::new(|_| true), |test_harness| async move { + let TestHarness { mut virtual_overseer, .. } = test_harness; + + let pair = CollatorPair::generate().0; + + let head_c = Hash::from_low_u64_be(130); + let head_c_num = 3; + + update_view(&mut virtual_overseer, &test_state, vec![(head_c, head_c_num)], 1).await; + + let peer_a = PeerId::random(); + + connect_and_declare_collator( + &mut virtual_overseer, + peer_a, + pair.clone(), + test_state.chain_ids[0], + CollationVersion::V2, + ) + .await; + + let mut candidate = dummy_candidate_receipt_bad_sig(head_c, Some(Default::default())); + candidate.descriptor.para_id = test_state.chain_ids[0]; + + let commitments = CandidateCommitments { + head_data: HeadData(vec![1, 2, 3]), + horizontal_messages: Default::default(), + upward_messages: Default::default(), + new_validation_code: None, + processed_downward_messages: 0, + hrmp_watermark: 0, + }; + candidate.commitments_hash = commitments.hash(); + + let parent_head_data = HeadData(vec![4, 2, 0]); + let parent_head_data_hash = parent_head_data.hash(); + let wrong_parent_head_data = HeadData(vec![4, 2]); + + let mut pvd = dummy_pvd(); + pvd.parent_head = parent_head_data; + + candidate.descriptor.persisted_validation_data_hash = pvd.hash(); + + let candidate_hash = candidate.hash(); + + advertise_collation( + &mut virtual_overseer, + peer_a, + head_c, + Some((candidate_hash, parent_head_data_hash)), + ) + .await; + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::CandidateBacking( + CandidateBackingMessage::CanSecond(request, tx), + ) => { + assert_eq!(request.candidate_hash, candidate_hash); + assert_eq!(request.candidate_para_id, test_state.chain_ids[0]); + assert_eq!(request.parent_head_data_hash, parent_head_data_hash); + tx.send(true).expect("receiving side should be alive"); + } + ); + + let response_channel = assert_fetch_collation_request( + &mut virtual_overseer, + head_c, + test_state.chain_ids[0], + Some(candidate_hash), + ) + .await; + + let pov = PoV { block_data: BlockData(vec![1]) }; + + response_channel + .send(Ok(( + request_v2::CollationFetchingResponse::CollationWithParentHeadData { + receipt: candidate.clone(), + pov: pov.clone(), + parent_head_data: wrong_parent_head_data, + } + .encode(), + ProtocolName::from(""), + ))) + .expect("Sending response should succeed"); + + // PVD request. + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ProspectiveParachains( + ProspectiveParachainsMessage::GetProspectiveValidationData(request, tx), + ) => { + assert_eq!(head_c, request.candidate_relay_parent); + assert_eq!(test_state.chain_ids[0], request.para_id); + tx.send(Some(pvd)).unwrap(); + } + ); + + // Reported malicious. + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::NetworkBridgeTx( + NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Single(peer_id, rep)), + ) => { + assert_eq!(peer_a, peer_id); + assert_eq!(rep.value, COST_REPORT_BAD.cost_or_benefit()); + } + ); + + test_helpers::Yield::new().await; + assert_matches!(virtual_overseer.recv().now_or_never(), None); + + virtual_overseer + }); +} + #[test] fn advertisement_spam_protection() { let test_state = TestState::default(); diff --git a/polkadot/node/network/dispute-distribution/src/sender/mod.rs b/polkadot/node/network/dispute-distribution/src/sender/mod.rs index f4acc72318ad448b99a106b460b84eefabd5c96d..8187f20146c7ae43fe36601c180ebe9af2c2faeb 100644 --- a/polkadot/node/network/dispute-distribution/src/sender/mod.rs +++ b/polkadot/node/network/dispute-distribution/src/sender/mod.rs @@ -76,7 +76,7 @@ pub struct DisputeSender { /// Value is the hash that was used for the query. active_sessions: HashMap, - /// All ongoing dispute sendings this subsystem is aware of. + /// All ongoing dispute sending this subsystem is aware of. /// /// Using an `IndexMap` so items can be iterated in the order of insertion. disputes: IndexMap>, @@ -105,7 +105,7 @@ struct WaitForActiveDisputesState { #[overseer::contextbounds(DisputeDistribution, prefix = self::overseer)] impl DisputeSender { - /// Create a new `DisputeSender` which can be used to start dispute sendings. + /// Create a new `DisputeSender` which can be used to start dispute sending. pub fn new(tx: NestingSender, metrics: Metrics) -> Self { Self { active_heads: Vec::new(), @@ -362,7 +362,7 @@ async fn get_active_session_indices( runtime: &mut RuntimeInfo, active_heads: &Vec, ) -> Result> { - let mut indeces = HashMap::new(); + let mut indices = HashMap::new(); // Iterate all heads we track as active and fetch the child' session indices. for head in active_heads { let session_index = runtime.get_session_index_for_child(ctx.sender(), *head).await?; @@ -372,9 +372,9 @@ async fn get_active_session_indices( { gum::debug!(target: LOG_TARGET, ?err, ?session_index, "Can't cache SessionInfo"); } - indeces.insert(session_index, *head); + indices.insert(session_index, *head); } - Ok(indeces) + Ok(indices) } /// Retrieve Set of active disputes from the dispute coordinator. diff --git a/polkadot/node/network/dispute-distribution/src/tests/mock.rs b/polkadot/node/network/dispute-distribution/src/tests/mock.rs index e6a49f14c094c9ce98030dda76f1ea2baf88ec87..ccc050233e8408d09e8cbe6261a44e682939d4b9 100644 --- a/polkadot/node/network/dispute-distribution/src/tests/mock.rs +++ b/polkadot/node/network/dispute-distribution/src/tests/mock.rs @@ -163,7 +163,7 @@ pub fn make_dispute_message( let invalid_vote = make_explicit_signed(MOCK_VALIDATORS[invalid_validator.0 as usize], candidate_hash, false); gum::trace!( - "Passed time for invald vote: {:#?}", + "Passed time for invalid vote: {:#?}", Instant::now().saturating_duration_since(before_request) ); DisputeMessage::from_signed_statements( diff --git a/polkadot/node/network/gossip-support/src/lib.rs b/polkadot/node/network/gossip-support/src/lib.rs index 4dfdd1f7208f66ec4a787ffe8bc7f72c41575c9d..9f33cd5d8a31697f5d42030fc043643ef1ee247f 100644 --- a/polkadot/node/network/gossip-support/src/lib.rs +++ b/polkadot/node/network/gossip-support/src/lib.rs @@ -508,7 +508,7 @@ where ); } let pretty = PrettyAuthorities(unconnected_authorities); - gum::debug!( + gum::info!( target: LOG_TARGET, ?connected_ratio, ?absolute_connected, diff --git a/polkadot/node/network/gossip-support/src/tests.rs b/polkadot/node/network/gossip-support/src/tests.rs index 6817c85f98d87c03b3c252783c464efed88dfdb6..cce78df38f308e34e1c663ab2c27850c818e26f8 100644 --- a/polkadot/node/network/gossip-support/src/tests.rs +++ b/polkadot/node/network/gossip-support/src/tests.rs @@ -122,7 +122,7 @@ impl MockAuthorityDiscovery { self.authorities.lock().clone() } - fn add_more_authorties( + fn add_more_authorities( &self, new_known: Vec, ) -> HashMap> { @@ -720,7 +720,7 @@ fn issues_update_authorities_after_session() { assert!(overseer.recv().timeout(TIMEOUT).await.is_none()); // 4. Connect more authorities except one - let newly_added = authority_discovery_mock.add_more_authorties(unknown_at_session); + let newly_added = authority_discovery_mock.add_more_authorities(unknown_at_session); let mut newly_added_iter = newly_added.iter(); let unconnected_at_last_retry = newly_added_iter .next() diff --git a/polkadot/node/network/protocol/src/grid_topology.rs b/polkadot/node/network/protocol/src/grid_topology.rs index 3c4372a27a2c42e20227adbe15d2cc7fcade3d12..a14d24610722bfad2d26df9526d8e6d7411e6f68 100644 --- a/polkadot/node/network/protocol/src/grid_topology.rs +++ b/polkadot/node/network/protocol/src/grid_topology.rs @@ -89,7 +89,7 @@ impl SessionGridTopology { SessionGridTopology { shuffled_indices, canonical_shuffling, peer_ids } } - /// Updates the known peer ids for the passed authorithies ids. + /// Updates the known peer ids for the passed authorities ids. pub fn update_authority_ids( &mut self, peer_id: PeerId, @@ -313,7 +313,7 @@ impl SessionGridTopologyEntry { self.topology.is_validator(peer) } - /// Updates the known peer ids for the passed authorithies ids. + /// Updates the known peer ids for the passed authorities ids. pub fn update_authority_ids( &mut self, peer_id: PeerId, @@ -345,7 +345,7 @@ impl SessionGridTopologies { self.inner.get(&session).and_then(|val| val.0.as_ref()) } - /// Updates the known peer ids for the passed authorithies ids. + /// Updates the known peer ids for the passed authorities ids. pub fn update_authority_ids( &mut self, peer_id: PeerId, diff --git a/polkadot/node/network/protocol/src/lib.rs b/polkadot/node/network/protocol/src/lib.rs index 7a0ff9f4fa9a26c2ec1f7d97b03d61424e83b98b..4dd94b5eac4fc430d1b28d5d906d922f0e970870 100644 --- a/polkadot/node/network/protocol/src/lib.rs +++ b/polkadot/node/network/protocol/src/lib.rs @@ -871,7 +871,7 @@ pub mod v2 { } /// v3 network protocol types. -/// Purpose is for chaning ApprovalDistributionMessage to +/// Purpose is for changing ApprovalDistributionMessage to /// include more than one assignment and approval in a message. pub mod v3 { use parity_scale_codec::{Decode, Encode}; diff --git a/polkadot/node/network/protocol/src/peer_set.rs b/polkadot/node/network/protocol/src/peer_set.rs index cb329607ad6127024af9e6d6bc3c75c7e813c0e3..d0ae5b4a1bf3c5fa6b70fed76551dffbe75fc7f6 100644 --- a/polkadot/node/network/protocol/src/peer_set.rs +++ b/polkadot/node/network/protocol/src/peer_set.rs @@ -234,7 +234,7 @@ pub enum ValidationVersion { /// The second version. V2 = 2, /// The third version where changes to ApprovalDistributionMessage had been made. - /// The changes are translatable to V2 format untill assignments v2 and approvals + /// The changes are translatable to V2 format until assignments v2 and approvals /// coalescing is enabled through a runtime upgrade. V3 = 3, } diff --git a/polkadot/node/network/protocol/src/request_response/incoming/mod.rs b/polkadot/node/network/protocol/src/request_response/incoming/mod.rs index 4455448386728e2fdc7188f35841947ca8ddd985..1d7c4a63e0c31e9440c1386909fe198a6909f740 100644 --- a/polkadot/node/network/protocol/src/request_response/incoming/mod.rs +++ b/polkadot/node/network/protocol/src/request_response/incoming/mod.rs @@ -47,7 +47,7 @@ where Req: IsRequest + Decode + Encode, Req::Response: Encode, { - /// Create configuration for `NetworkConfiguration::request_response_porotocols` and a + /// Create configuration for `NetworkConfiguration::request_response_protocols` and a /// corresponding typed receiver. /// /// This Register that config with substrate networking and receive incoming requests via the diff --git a/polkadot/node/network/protocol/src/request_response/mod.rs b/polkadot/node/network/protocol/src/request_response/mod.rs index a67d83aff0c9210e9a1233640540635514e4cf71..87217bf084fb9277a3e2930cb67f6a5a5ac1dcc8 100644 --- a/polkadot/node/network/protocol/src/request_response/mod.rs +++ b/polkadot/node/network/protocol/src/request_response/mod.rs @@ -31,7 +31,7 @@ //! data, like what is the corresponding response type. //! //! ## Versioning -//! +//! //! Versioning for request-response protocols can be done in multiple ways. //! //! If you're just changing the protocol name but the binary payloads are the same, just add a new @@ -287,7 +287,7 @@ impl Protocol { match self { // Hundreds of validators will start requesting their chunks once they see a candidate // awaiting availability on chain. Given that they will see that block at different - // times (due to network delays), 100 seems big enough to accomodate for "bursts", + // times (due to network delays), 100 seems big enough to accommodate for "bursts", // assuming we can service requests relatively quickly, which would need to be measured // as well. Protocol::ChunkFetchingV1 => 100, diff --git a/polkadot/node/network/protocol/src/request_response/v1.rs b/polkadot/node/network/protocol/src/request_response/v1.rs index 0832593a6a3d938cc4349c57972b850023ae02f6..60eecb69f738912ddb0240c890e2283db7f91a72 100644 --- a/polkadot/node/network/protocol/src/request_response/v1.rs +++ b/polkadot/node/network/protocol/src/request_response/v1.rs @@ -22,7 +22,8 @@ use polkadot_node_primitives::{ AvailableData, DisputeMessage, ErasureChunk, PoV, Proof, UncheckedDisputeMessage, }; use polkadot_primitives::{ - CandidateHash, CandidateReceipt, CommittedCandidateReceipt, Hash, Id as ParaId, ValidatorIndex, + CandidateHash, CandidateReceipt, CommittedCandidateReceipt, Hash, HeadData, Id as ParaId, + ValidatorIndex, }; use super::{IsRequest, Protocol}; @@ -103,6 +104,18 @@ pub enum CollationFetchingResponse { /// Deliver requested collation. #[codec(index = 0)] Collation(CandidateReceipt, PoV), + + /// Deliver requested collation along with parent head data. + #[codec(index = 1)] + CollationWithParentHeadData { + /// The receipt of the candidate. + receipt: CandidateReceipt, + /// Candidate's proof of validity. + pov: PoV, + /// The head data of the candidate's parent. + /// This is needed for elastic scaling to work. + parent_head_data: HeadData, + }, } impl IsRequest for CollationFetchingRequest { @@ -170,7 +183,7 @@ impl IsRequest for AvailableDataFetchingRequest { pub struct StatementFetchingRequest { /// Data needed to locate and identify the needed statement. pub relay_parent: Hash, - /// Hash of candidate that was used create the `CommitedCandidateRecept`. + /// Hash of candidate that was used create the `CommittedCandidateReceipt`. pub candidate_hash: CandidateHash, } diff --git a/polkadot/node/network/statement-distribution/src/legacy_v1/responder.rs b/polkadot/node/network/statement-distribution/src/legacy_v1/responder.rs index 81e226c4ff8950d8edc9b18d8a3ddef0e0b76d93..8d1683759a0360871b149926b73b8a77ad5bec6a 100644 --- a/polkadot/node/network/statement-distribution/src/legacy_v1/responder.rs +++ b/polkadot/node/network/statement-distribution/src/legacy_v1/responder.rs @@ -64,7 +64,7 @@ pub async fn respond( // late, as each requester having the data will help distributing it. // 2. If we take too long, the requests timing out will not yet have had any data sent, thus // we wasted no bandwidth. - // 3. If the queue is full, requestes will get an immediate error instead of running in a + // 3. If the queue is full, requests will get an immediate error instead of running in a // timeout, thus requesters can immediately try another peer and be faster. // // From this perspective we would not want parallel response sending at all, but we don't diff --git a/polkadot/node/network/statement-distribution/src/legacy_v1/tests.rs b/polkadot/node/network/statement-distribution/src/legacy_v1/tests.rs index 2766ec9815af1e87051603c1b45304c4758cf9f9..08e9d69d8ee662cc4947ec9bc06c0fddbf88b6c8 100644 --- a/polkadot/node/network/statement-distribution/src/legacy_v1/tests.rs +++ b/polkadot/node/network/statement-distribution/src/legacy_v1/tests.rs @@ -1494,7 +1494,7 @@ fn receiving_large_statement_from_one_sends_to_another_and_to_candidate_backing( Err(()) => {} ); - // And now the succeding request from peer_b: + // And now the succeeding request from peer_b: let (pending_response, response_rx) = oneshot::channel(); let inner_req = StatementFetchingRequest { relay_parent: metadata.relay_parent, diff --git a/polkadot/node/network/statement-distribution/src/v2/cluster.rs b/polkadot/node/network/statement-distribution/src/v2/cluster.rs index 619114de9670c8aef50ce129b6c26bf6ff206f70..87cdc389cb35973403bdf33ab4522d32174d7ec1 100644 --- a/polkadot/node/network/statement-distribution/src/v2/cluster.rs +++ b/polkadot/node/network/statement-distribution/src/v2/cluster.rs @@ -55,8 +55,9 @@ //! and to keep track of what we have sent to other validators in the group and what we may //! continue to send them. -use polkadot_primitives::{CandidateHash, CompactStatement, ValidatorIndex}; +use polkadot_primitives::{CandidateHash, CompactStatement, Hash, ValidatorIndex}; +use crate::LOG_TARGET; use std::collections::{HashMap, HashSet}; #[derive(Hash, PartialEq, Eq)] @@ -424,6 +425,28 @@ impl ClusterTracker { fn is_in_group(&self, validator: ValidatorIndex) -> bool { self.validators.contains(&validator) } + + /// Dumps pending statement for this cluster. + /// + /// Normally we should not have pending statements to validators in our cluster, + /// but if we do for all validators in our cluster, then we don't participate + /// in backing. Occasional pending statements are expected if two authorities + /// can't detect each other or after restart, where it takes a while to discover + /// the whole network. + + pub fn warn_if_too_many_pending_statements(&self, parent_hash: Hash) { + if self.pending.iter().filter(|pending| !pending.1.is_empty()).count() >= + self.validators.len() + { + gum::warn!( + target: LOG_TARGET, + pending_statements = ?self.pending, + ?parent_hash, + "Cluster has too many pending statements, something wrong with our connection to our group peers \n + Restart might be needed if validator gets 0 backing rewards for more than 3-4 consecutive sessions" + ); + } + } } /// Incoming statement was accepted. diff --git a/polkadot/node/network/statement-distribution/src/v2/mod.rs b/polkadot/node/network/statement-distribution/src/v2/mod.rs index dc29c19a48e333324c1aaa59f543d18e1ea33413..d782e37f10b4bed17af7bc33912304441214fe32 100644 --- a/polkadot/node/network/statement-distribution/src/v2/mod.rs +++ b/polkadot/node/network/statement-distribution/src/v2/mod.rs @@ -112,7 +112,7 @@ const COST_EXCESSIVE_SECONDED: Rep = Rep::CostMinor("Sent Excessive `Seconded` S const COST_DISABLED_VALIDATOR: Rep = Rep::CostMinor("Sent a statement from a disabled validator"); const COST_UNEXPECTED_MANIFEST_MISSING_KNOWLEDGE: Rep = - Rep::CostMinor("Unexpected Manifest, missing knowlege for relay parent"); + Rep::CostMinor("Unexpected Manifest, missing knowledge for relay parent"); const COST_UNEXPECTED_MANIFEST_DISALLOWED: Rep = Rep::CostMinor("Unexpected Manifest, Peer Disallowed"); const COST_UNEXPECTED_MANIFEST_PEER_UNKNOWN: Rep = @@ -251,6 +251,13 @@ impl PerSessionState { if local_index.is_some() { self.local_validator.get_or_insert(LocalValidatorIndex::Inactive); } + + gum::info!( + target: LOG_TARGET, + index_in_gossip_topology = ?local_index, + index_in_parachain_authorities = ?self.local_validator, + "Node uses the following topology indices" + ); } /// Returns `true` if local is neither active or inactive validator node. @@ -621,8 +628,8 @@ pub(crate) async fn handle_active_leaves_update( request_min_backing_votes(new_relay_parent, session_index, ctx.sender()).await?; let mut per_session_state = PerSessionState::new(session_info, &state.keystore, minimum_backing_votes); - if let Some(toplogy) = state.unused_topologies.remove(&session_index) { - per_session_state.supply_topology(&toplogy.topology, toplogy.local_index); + if let Some(topology) = state.unused_topologies.remove(&session_index) { + per_session_state.supply_topology(&topology.topology, topology.local_index); } state.per_session.insert(session_index, per_session_state); } @@ -768,7 +775,15 @@ pub(crate) fn handle_deactivate_leaves(state: &mut State, leaves: &[Hash]) { let pruned = state.implicit_view.deactivate_leaf(*leaf); for pruned_rp in pruned { // clean up per-relay-parent data based on everything removed. - state.per_relay_parent.remove(&pruned_rp); + state + .per_relay_parent + .remove(&pruned_rp) + .as_ref() + .and_then(|pruned| pruned.active_validator_state()) + .map(|active_state| { + active_state.cluster_tracker.warn_if_too_many_pending_statements(pruned_rp) + }); + // clean up requests related to this relay parent. state.request_manager.remove_by_relay_parent(*leaf); } diff --git a/polkadot/node/network/statement-distribution/src/v2/requests.rs b/polkadot/node/network/statement-distribution/src/v2/requests.rs index bed3d5c18ae2b1007a3ee585f00a54d1b98f7c27..fe270c8a58e81fc5ebee9a42c38a1cedd1a0b516 100644 --- a/polkadot/node/network/statement-distribution/src/v2/requests.rs +++ b/polkadot/node/network/statement-distribution/src/v2/requests.rs @@ -315,7 +315,16 @@ impl RequestManager { request_props: impl Fn(&CandidateIdentifier) -> Option, peer_advertised: impl Fn(&CandidateIdentifier, &PeerId) -> Option, ) -> Option> { - if response_manager.len() >= MAX_PARALLEL_ATTESTED_CANDIDATE_REQUESTS as usize { + // The number of parallel requests a node can answer is limited by + // `MAX_PARALLEL_ATTESTED_CANDIDATE_REQUESTS`, however there is no + // need for the current node to limit itself to the same amount the + // requests, because the requests are going to different nodes anyways. + // While looking at https://github.com/paritytech/polkadot-sdk/issues/3314, + // found out that this requests take around 100ms to fulfill, so it + // would make sense to try to request things as early as we can, given + // we would need to request it for each candidate, around 25 right now + // on kusama. + if response_manager.len() >= 2 * MAX_PARALLEL_ATTESTED_CANDIDATE_REQUESTS as usize { return None } @@ -1027,6 +1036,7 @@ mod tests { let peer_advertised = |_identifier: &CandidateIdentifier, _peer: &_| { Some(StatementFilter::full(group_size)) }; + let outgoing = request_manager .next_request(&mut response_manager, request_props, peer_advertised) .unwrap(); @@ -1148,6 +1158,7 @@ mod tests { { let request_props = |_identifier: &CandidateIdentifier| Some((&request_properties).clone()); + let outgoing = request_manager .next_request(&mut response_manager, request_props, peer_advertised) .unwrap(); @@ -1230,6 +1241,7 @@ mod tests { { let request_props = |_identifier: &CandidateIdentifier| Some((&request_properties).clone()); + let outgoing = request_manager .next_request(&mut response_manager, request_props, peer_advertised) .unwrap(); diff --git a/polkadot/node/overseer/src/lib.rs b/polkadot/node/overseer/src/lib.rs index e16a3fd27ab3d920da5c83b3c4ee02ab4ccba2cb..167b32a15bc4d1e6cdb150a3e1d0133e035fb617 100644 --- a/polkadot/node/overseer/src/lib.rs +++ b/polkadot/node/overseer/src/lib.rs @@ -871,7 +871,7 @@ where gum::trace!( target: LOG_TARGET, relay_parent = ?hash, - "Leaf got activated, notifying exterinal listeners" + "Leaf got activated, notifying external listeners" ); for listener in listeners { // it's fine if the listener is no longer interested diff --git a/polkadot/node/overseer/src/tests.rs b/polkadot/node/overseer/src/tests.rs index 0494274367d953146ad06e93c3ee00cfc9c3b983..55a6bdb74ba73c04bc66847a1b04ff6175608fa7 100644 --- a/polkadot/node/overseer/src/tests.rs +++ b/polkadot/node/overseer/src/tests.rs @@ -811,7 +811,7 @@ fn test_candidate_validation_msg() -> CandidateValidationMessage { fn test_candidate_backing_msg() -> CandidateBackingMessage { let (sender, _) = oneshot::channel(); - CandidateBackingMessage::GetBackedCandidates(Vec::new(), sender) + CandidateBackingMessage::GetBackedCandidates(Default::default(), sender) } fn test_chain_api_msg() -> ChainApiMessage { diff --git a/polkadot/node/primitives/src/approval.rs b/polkadot/node/primitives/src/approval.rs index f2a79e025affe3de108f0ce8abd985e53c862a5c..b73cb4c717db7ff856669b528d5e065e413d6101 100644 --- a/polkadot/node/primitives/src/approval.rs +++ b/polkadot/node/primitives/src/approval.rs @@ -382,7 +382,7 @@ pub mod v2 { /// The core index chosen in this cert. core_index: CoreIndex, }, - /// Deprectated assignment. Soon to be removed. + /// Deprecated assignment. Soon to be removed. /// An assignment story based on the VRF that authorized the relay-chain block where the /// candidate was included combined with a sample number. /// diff --git a/polkadot/node/primitives/src/disputes/mod.rs b/polkadot/node/primitives/src/disputes/mod.rs index 768b95f65537b7ebfe5e4a8baadfd9eec685af4f..5814ecee44f4e9ee333f1898c92341799978d646 100644 --- a/polkadot/node/primitives/src/disputes/mod.rs +++ b/polkadot/node/primitives/src/disputes/mod.rs @@ -84,7 +84,7 @@ impl CandidateVotes { #[derive(Debug, Clone)] /// Valid candidate votes. /// -/// Prefere backing votes over other votes. +/// Prefer backing votes over other votes. pub struct ValidCandidateVotes { votes: BTreeMap, } @@ -133,7 +133,7 @@ impl ValidCandidateVotes { self.votes.retain(f) } - /// Get all the validator indeces we have votes for. + /// Get all the validator indices we have votes for. pub fn keys( &self, ) -> Bkeys<'_, ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature)> { diff --git a/polkadot/node/primitives/src/lib.rs b/polkadot/node/primitives/src/lib.rs index 6e3eefbcbe8c7bd6029c44e2cc43a2c30ea834f3..b102cf06c38f61bc2971364cfda3e4a64eaa8abc 100644 --- a/polkadot/node/primitives/src/lib.rs +++ b/polkadot/node/primitives/src/lib.rs @@ -58,7 +58,7 @@ pub use disputes::{ /// relatively rare. /// /// The associated worker binaries should use the same version as the node that spawns them. -pub const NODE_VERSION: &'static str = "1.7.0"; +pub const NODE_VERSION: &'static str = "1.9.0"; // For a 16-ary Merkle Prefix Trie, we can expect at most 16 32-byte hashes per node // plus some overhead: @@ -74,7 +74,7 @@ pub const VALIDATION_CODE_BOMB_LIMIT: usize = (MAX_CODE_SIZE * 4u32) as usize; pub const POV_BOMB_LIMIT: usize = (MAX_POV_SIZE * 4u32) as usize; /// How many blocks after finalization an information about backed/included candidate should be -/// pre-loaded (when scraoing onchain votes) and kept locally (when pruning). +/// pre-loaded (when scraping onchain votes) and kept locally (when pruning). /// /// We don't want to remove scraped candidates on finalization because we want to /// be sure that disputes will conclude on abandoned forks. diff --git a/polkadot/node/service/Cargo.toml b/polkadot/node/service/Cargo.toml index 8fd9f20b7bcfbde6a811fb7a4334242d9a1eb54f..5a42443c84c8903020587a71fd701b10964306fc 100644 --- a/polkadot/node/service/Cargo.toml +++ b/polkadot/node/service/Cargo.toml @@ -93,6 +93,7 @@ kvdb-rocksdb = { version = "0.19.0", optional = true } parity-db = { version = "0.4.12", optional = true } codec = { package = "parity-scale-codec", version = "3.6.1" } parking_lot = "0.12.1" +bitvec = { version = "1.0.1", optional = true } # Polkadot polkadot-core-primitives = { path = "../../core-primitives" } @@ -141,6 +142,9 @@ polkadot-node-core-pvf-checker = { path = "../core/pvf-checker", optional = true polkadot-node-core-runtime-api = { path = "../core/runtime-api", optional = true } polkadot-statement-distribution = { path = "../network/statement-distribution", optional = true } +xcm = { package = "staging-xcm", path = "../../xcm" } +xcm-fee-payment-runtime-api = { path = "../../xcm/xcm-fee-payment-runtime-api" } + [dev-dependencies] polkadot-test-client = { path = "../test/client" } polkadot-node-subsystem-test-helpers = { path = "../subsystem-test-helpers" } @@ -184,8 +188,8 @@ full-node = [ ] # Configure the native runtimes to use. -westend-native = ["westend-runtime", "westend-runtime-constants"] -rococo-native = ["rococo-runtime", "rococo-runtime-constants"] +westend-native = ["bitvec", "westend-runtime", "westend-runtime-constants"] +rococo-native = ["bitvec", "rococo-runtime", "rococo-runtime-constants"] runtime-benchmarks = [ "frame-benchmarking-cli/runtime-benchmarks", @@ -217,10 +221,7 @@ try-runtime = [ "sp-runtime/try-runtime", "westend-runtime?/try-runtime", ] -fast-runtime = [ - "rococo-runtime?/fast-runtime", - "westend-runtime?/fast-runtime", -] +fast-runtime = ["rococo-runtime?/fast-runtime", "westend-runtime?/fast-runtime"] malus = ["full-node"] runtime-metrics = [ @@ -228,3 +229,7 @@ runtime-metrics = [ "rococo-runtime?/runtime-metrics", "westend-runtime?/runtime-metrics", ] + +elastic-scaling-experimental = [ + "polkadot-collator-protocol?/elastic-scaling-experimental", +] diff --git a/polkadot/node/service/chain-specs/kusama.json b/polkadot/node/service/chain-specs/kusama.json index 979550c7570643380c5a2e0f0f5de7c049c01f85..490b39ee696930bafc9af4c139339a9974dffff9 100644 --- a/polkadot/node/service/chain-specs/kusama.json +++ b/polkadot/node/service/chain-specs/kusama.json @@ -16,8 +16,8 @@ "/dns/boot-node.helikon.io/tcp/7062/wss/p2p/12D3KooWL4KPqfAsPE2aY1g5Zo1CxsDwcdJ7mmAghK7cg6M2fdbD", "/dns/kusama.bootnode.amforc.com/tcp/30333/p2p/12D3KooWLx6nsj6Fpd8biP1VDyuCUjazvRiGWyBam8PsqRJkbUb9", "/dns/kusama.bootnode.amforc.com/tcp/30334/wss/p2p/12D3KooWLx6nsj6Fpd8biP1VDyuCUjazvRiGWyBam8PsqRJkbUb9", - "/dns/kusama-bootnode.polkadotters.com/tcp/30333/p2p/12D3KooWHB5rTeNkQdXNJ9ynvGz8Lpnmsctt7Tvp7mrYv6bcwbPG", - "/dns/kusama-bootnode.polkadotters.com/tcp/30334/wss/p2p/12D3KooWHB5rTeNkQdXNJ9ynvGz8Lpnmsctt7Tvp7mrYv6bcwbPG", + "/dns/kusama.bootnodes.polkadotters.com/tcp/30311/p2p/12D3KooWHB5rTeNkQdXNJ9ynvGz8Lpnmsctt7Tvp7mrYv6bcwbPG", + "/dns/kusama.bootnodes.polkadotters.com/tcp/30313/wss/p2p/12D3KooWHB5rTeNkQdXNJ9ynvGz8Lpnmsctt7Tvp7mrYv6bcwbPG", "/dns/boot-cr.gatotech.network/tcp/33200/p2p/12D3KooWRNZXf99BfzQDE1C8YhuBbuy7Sj18UEf7FNpD8egbURYD", "/dns/boot-cr.gatotech.network/tcp/35200/wss/p2p/12D3KooWRNZXf99BfzQDE1C8YhuBbuy7Sj18UEf7FNpD8egbURYD", "/dns/boot-kusama.metaspan.io/tcp/23012/p2p/12D3KooWE1tq9ZL9AAxMiUBBqy1ENmh5pwfWabnoBPMo8gFPXhn6", @@ -35,6 +35,7 @@ "/dns/ksm14.rotko.net/tcp/35224/wss/p2p/12D3KooWAa5THTw8HPfnhEei23HdL8P9McBXdozG2oTtMMksjZkK", "/dns/ksm14.rotko.net/tcp/33224/p2p/12D3KooWAa5THTw8HPfnhEei23HdL8P9McBXdozG2oTtMMksjZkK", "/dns/ibp-boot-kusama.luckyfriday.io/tcp/30333/p2p/12D3KooW9vu1GWHBuxyhm7rZgD3fhGZpNajPXFexadvhujWMgwfT", + "/dns/boot-kusama.luckyfriday.io/tcp/443/wss/p2p/12D3KooWS1Lu6DmK8YHSvkErpxpcXmk14vG6y4KVEFEkd9g62PP8", "/dns/ibp-boot-kusama.luckyfriday.io/tcp/30334/wss/p2p/12D3KooW9vu1GWHBuxyhm7rZgD3fhGZpNajPXFexadvhujWMgwfT" ], "telemetryEndpoints": [ diff --git a/polkadot/node/service/chain-specs/paseo.json b/polkadot/node/service/chain-specs/paseo.json index c8f2b58533c62af5ee3b71ffa5d782c852edd162..2e659716766ede8cf4ec2b183013b5e925dff998 100644 --- a/polkadot/node/service/chain-specs/paseo.json +++ b/polkadot/node/service/chain-specs/paseo.json @@ -5,21 +5,20 @@ "bootNodes": [ "/dns/paseo.bootnode.amforc.com/tcp/30333/wss/p2p/12D3KooWFD81HC9memUwuGMLvhDDEfmXjn6jC4n7zyNs3vToXapS", "/dns/paseo.bootnode.amforc.com/tcp/30344/p2p/12D3KooWFD81HC9memUwuGMLvhDDEfmXjn6jC4n7zyNs3vToXapS", - "/dns/paseo-bootnode.radiumblock.com/tcp/30333/p2p/12D3KooWADeayZC8zag4Qrb4GosSn65MmfVZztRPMaBdgZnQqXRo", - "/dns/paseo-bootnode.radiumblock.com/tcp/30335/wss/p2p/12D3KooWADeayZC8zag4Qrb4GosSn65MmfVZztRPMaBdgZnQqXRo", - "/dns/boot.gatotech.network/tcp/33400/p2p/12D3KooWEvz5Ygv3MhCUNTVQbUTVhzhvf4KKcNoe5M5YbVLPBeeW", - "/dns/boot.gatotech.network/tcp/35400/wss/p2p/12D3KooWEvz5Ygv3MhCUNTVQbUTVhzhvf4KKcNoe5M5YbVLPBeeW", + "/dns/boot.stake.plus/tcp/43334/wss/p2p/12D3KooWNhgAC3hjZHxaT52EpPFZohkCL1AHFAijqcN8xB9Rwud2", + "/dns/boot.stake.plus/tcp/43333/p2p/12D3KooWNhgAC3hjZHxaT52EpPFZohkCL1AHFAijqcN8xB9Rwud2", + "/dns/boot.metaspan.io/tcp/36017/wss/p2p/12D3KooWSW6nDfM3SS8rUtjMyjdszivK31bu4a1sRngGa2hFETz7", + "/dns/boot.metaspan.io/tcp/36018/p2p/12D3KooWSW6nDfM3SS8rUtjMyjdszivK31bu4a1sRngGa2hFETz7", + "/dns/paseo.bootnodes.polkadotters.com/tcp/30538/p2p/12D3KooWPbbFy4TefEGTRF5eTYhq8LEzc4VAHdNUVCbY4nAnhqPP", + "/dns/paseo.bootnodes.polkadotters.com/tcp/30540/wss/p2p/12D3KooWPbbFy4TefEGTRF5eTYhq8LEzc4VAHdNUVCbY4nAnhqPP", "/dns/boot-node.helikon.io/tcp/10020/p2p/12D3KooWBetfzZpf6tGihKrqCo5z854Ub4ZNAUUTRT6eYHNh7FYi", "/dns/boot-node.helikon.io/tcp/10022/wss/p2p/12D3KooWBetfzZpf6tGihKrqCo5z854Ub4ZNAUUTRT6eYHNh7FYi", - "/dns/pso16.rotko.net/tcp/33246/p2p/12D3KooWRH8eBMhw8c7bucy6pJfy94q4dKpLkF3pmeGohHmemdRu", - "/dns/pso16.rotko.net/tcp/35246/wss/p2p/12D3KooWRH8eBMhw8c7bucy6pJfy94q4dKpLkF3pmeGohHmemdRu" - ], - "telemetryEndpoints": [ - [ - "wss://telemetry.polkadot.io/submit/", - 0 - ] + "/dns/boot.gatotech.network/tcp/33400/p2p/12D3KooWEvz5Ygv3MhCUNTVQbUTVhzhvf4KKcNoe5M5YbVLPBeeW", + "/dns/boot.gatotech.network/tcp/35400/wss/p2p/12D3KooWEvz5Ygv3MhCUNTVQbUTVhzhvf4KKcNoe5M5YbVLPBeeW", + "/dns/paseo-bootnode.turboflakes.io/tcp/30630/p2p/12D3KooWMjCN2CrnN71hAdehn6M2iYKeGdGbZ1A3SKhf4hxrgG9e", + "/dns/paseo-bootnode.turboflakes.io/tcp/30730/wss/p2p/12D3KooWMjCN2CrnN71hAdehn6M2iYKeGdGbZ1A3SKhf4hxrgG9e" ], + "telemetryEndpoints": null, "protocolId": "pas", "properties": { "ss58Format": 42, @@ -34,6 +33,12 @@ "0x06de3d8a54d27e44a9d5ce189618f22d4e7b9012096b41c4eb3aaf947f6ea429": "0x0900", "0x06de3d8a54d27e44a9d5ce189618f22db4b49d95320d9021994c850f25b8e385": "0x0000300000800000080000000000100000c8000005000000050000000200000002000000000000000000000000005000000010000400000000000000000000000000000000000000000000000000000000000000000000000800000000200000040000000000100000b004000000000000000000001027000080b2e60e80c3c9018096980000000000000000000000000005000000140000000400000001000000000006000000640000000200000019000000000000000200000002000000020000000500000002000000", "0x074b65e262fcd5bd9c785caf7f42e00a4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x08c41974a97dbf15cfbec28365bea2da4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x08c41974a97dbf15cfbec28365bea2da5e0621c4869aa60c02be9adcc98a0d1d": "0x380325fc2095902f5fe394f244bce38b0dc3d631cbc05f0b64d5620a71bbf2514f0f03f045328f504c13dac9ddd9b1186098aee7c46cb8d55289dbbf2433bab7a2623903a0af06322d100056125fac1df39d161089b07ae279505aae8731c4d110a54ad703aec8e80ea0375f8669d6e55d7abb6a3117678d7bb851a1bd100a01e52a4fed9003d46c454f9b620603feef8c3a2a5d7098205b8566500fda0fa0b456d6ded54538030e901c390fa37d101ff25d70594acd2df67b4493ee77a73684f25d39313536d702f4f4d0eccb899bf2d611b56e0afec7c740efba404f8d0e82a545f988c45316c402182879ec92e811e2a8cc117f3cde1f61d3cba0093134cfb1ed17a4ef74915d4a03e843f200e30bc5b951c73a96d968db1c0cd05e357d910fce159fc59c40e9d6e20203d51bba2124f480e3507eb1764fc3019ac7abae8ee215683a285078bda7f51d029338ece1c6bc6439dc4d16bfe33f28c9f0af31626bb8142849742b7a624f68070333022898140662dfea847e3cbfe5e989845ac6766e83472f8b0c650d85e77bae03586dafcdab3d4647d4dc68732a9cab8aa34c00c5edd04e65d9dd44c2a1fd21e2029a1eb2e31dcaf468dbb516f9b620fdd7c3f090d58a88e02b51b25255b2182dd1", + "0x08c41974a97dbf15cfbec28365bea2da8f05bccc2f70ec66a32999c5761156be": "0x0000000000000000", + "0x08c41974a97dbf15cfbec28365bea2daaacf00b9b41fda7a9268821c2a2b3e4c": "0x380325fc2095902f5fe394f244bce38b0dc3d631cbc05f0b64d5620a71bbf2514f0f03f045328f504c13dac9ddd9b1186098aee7c46cb8d55289dbbf2433bab7a2623903a0af06322d100056125fac1df39d161089b07ae279505aae8731c4d110a54ad703aec8e80ea0375f8669d6e55d7abb6a3117678d7bb851a1bd100a01e52a4fed9003d46c454f9b620603feef8c3a2a5d7098205b8566500fda0fa0b456d6ded54538030e901c390fa37d101ff25d70594acd2df67b4493ee77a73684f25d39313536d702f4f4d0eccb899bf2d611b56e0afec7c740efba404f8d0e82a545f988c45316c402182879ec92e811e2a8cc117f3cde1f61d3cba0093134cfb1ed17a4ef74915d4a03e843f200e30bc5b951c73a96d968db1c0cd05e357d910fce159fc59c40e9d6e20203d51bba2124f480e3507eb1764fc3019ac7abae8ee215683a285078bda7f51d029338ece1c6bc6439dc4d16bfe33f28c9f0af31626bb8142849742b7a624f68070333022898140662dfea847e3cbfe5e989845ac6766e83472f8b0c650d85e77bae03586dafcdab3d4647d4dc68732a9cab8aa34c00c5edd04e65d9dd44c2a1fd21e2029a1eb2e31dcaf468dbb516f9b620fdd7c3f090d58a88e02b51b25255b2182dd1", + "0x08c41974a97dbf15cfbec28365bea2dac713b7f8b14e2815d297585d3581e774": "0x0101000000", + "0x08c41974a97dbf15cfbec28365bea2dad47cb8f5328af743ddfb361e7180e7fcbb1bdbcacd6ac9340000000000000000": "0x00000000", "0x0f6738a0ee80c8e74cd2c7417c1e25564e7b9012096b41c4eb3aaf947f6ea429": "0x0100", "0x1405f2411d0af5a7ff397e7c9dc68d194e7b9012096b41c4eb3aaf947f6ea429": "0x0100", "0x1405f2411d0af5a7ff397e7c9dc68d196323ae84c43568be0d1394d5d0d522c4": "0x03000000", @@ -41,45 +46,59 @@ "0x196e027349017067f9eb56e2c4d9ded54e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x1a736d37504c2e3fb73dad160c55b2914e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x1cb6f36e027abb2091cfb5110ab5087f5e0621c4869aa60c02be9adcc98a0d1d": "0x10b07d600e3487e2712dcc3879c7b17c9b29cd2243b45f0d9343c591b89cf82a65010000000000000058108e1651614afc6a535c426fc013945e93533faa33819fe4e69423fe3233020100000000000000facb2f987caac6c1290a9784b1efdba78343d39aed805addb12945efbe4440000100000000000000ae240842b74e5dd778972e451558134f434c7a1d8a52bc70519f38054e2455330100000000000000", + "0x1cb6f36e027abb2091cfb5110ab5087f5e0621c4869aa60c02be9adcc98a0d1d": "0x38b07d600e3487e2712dcc3879c7b17c9b29cd2243b45f0d9343c591b89cf82a6501000000000000005440add43e5388a81aef665c9086d386c0be0ce75e4f8a4a3d8168e976ea821f010000000000000074dacbca0cdb5099afef67e622c147614198e669931cebc605629da332632473010000000000000058108e1651614afc6a535c426fc013945e93533faa33819fe4e69423fe323302010000000000000050412bd7d3f1075e8f8e3b682d05ea20b391c287d8c849a0e49a78f568553e690100000000000000c44b3e8efe854419ccd5801a82ada22d39cfccdbcece382304cdfeac81ebe4020100000000000000b2efe7e70daf44b3466c63ccbf4487f42c6a9f6fbb7050b849691e36ce92e3470100000000000000facb2f987caac6c1290a9784b1efdba78343d39aed805addb12945efbe444000010000000000000068a9ec74fa35b3425eaf503dd36294ba8e758e7b8084c4d6bfd547f8c6b5827401000000000000007c8348ec95a0faad6a638ef74864761028c53221bde07e9ff7c81a3f427abf3f0100000000000000ae240842b74e5dd778972e451558134f434c7a1d8a52bc70519f38054e245533010000000000000018bd0f67d77f04f1a92400421813d8927fad109b40a8689254a5f0c8b346857c01000000000000006248d87bd2a640ffe26d6b831735887c24e2076a3a0f3a74f7ae7568c276040801000000000000001ab03b1b3277edfedd24ef3d3359b449b64bd95ed82a04e7f9fbaab7b71dc0150100000000000000", "0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000", - "0x1cb6f36e027abb2091cfb5110ab5087faacf00b9b41fda7a9268821c2a2b3e4c": "0x10b07d600e3487e2712dcc3879c7b17c9b29cd2243b45f0d9343c591b89cf82a65010000000000000058108e1651614afc6a535c426fc013945e93533faa33819fe4e69423fe3233020100000000000000facb2f987caac6c1290a9784b1efdba78343d39aed805addb12945efbe4440000100000000000000ae240842b74e5dd778972e451558134f434c7a1d8a52bc70519f38054e2455330100000000000000", + "0x1cb6f36e027abb2091cfb5110ab5087faacf00b9b41fda7a9268821c2a2b3e4c": "0x38b07d600e3487e2712dcc3879c7b17c9b29cd2243b45f0d9343c591b89cf82a6501000000000000005440add43e5388a81aef665c9086d386c0be0ce75e4f8a4a3d8168e976ea821f010000000000000074dacbca0cdb5099afef67e622c147614198e669931cebc605629da332632473010000000000000058108e1651614afc6a535c426fc013945e93533faa33819fe4e69423fe323302010000000000000050412bd7d3f1075e8f8e3b682d05ea20b391c287d8c849a0e49a78f568553e690100000000000000c44b3e8efe854419ccd5801a82ada22d39cfccdbcece382304cdfeac81ebe4020100000000000000b2efe7e70daf44b3466c63ccbf4487f42c6a9f6fbb7050b849691e36ce92e3470100000000000000facb2f987caac6c1290a9784b1efdba78343d39aed805addb12945efbe444000010000000000000068a9ec74fa35b3425eaf503dd36294ba8e758e7b8084c4d6bfd547f8c6b5827401000000000000007c8348ec95a0faad6a638ef74864761028c53221bde07e9ff7c81a3f427abf3f0100000000000000ae240842b74e5dd778972e451558134f434c7a1d8a52bc70519f38054e245533010000000000000018bd0f67d77f04f1a92400421813d8927fad109b40a8689254a5f0c8b346857c01000000000000006248d87bd2a640ffe26d6b831735887c24e2076a3a0f3a74f7ae7568c276040801000000000000001ab03b1b3277edfedd24ef3d3359b449b64bd95ed82a04e7f9fbaab7b71dc0150100000000000000", "0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000002", - "0x2099d7f109d6e535fb000bba623fd4404c014e6bf8b8c2c011e7290b85696bb3": "0x10f89c97bf5b2c07c05c84eebce4ffc7b28766946c03741fd1a71fdae0942e876892cb05c48fc643f057626c669604675c5ad5a836266f260ae7030c6fdc17a54326e2fc857945d01520797a75388c58e710c9fefedd28387af70880f1682be41e763d070989ead31f265b40cc7a0cd29d47799b766d6a7f084e44c82baedfc01e", + "0x2099d7f109d6e535fb000bba623fd4404c014e6bf8b8c2c011e7290b85696bb3": "0x38f89c97bf5b2c07c05c84eebce4ffc7b28766946c03741fd1a71fdae0942e8768cce3ec06252cf7cdad47fe1265047a9bbddb9059ee4bdc6dec83b67249b4a9342ada042fb4bbfd9b6d8c48293ffc4a7722632c843a67e608554c41d06aabc41392cb05c48fc643f057626c669604675c5ad5a836266f260ae7030c6fdc17a5438c15606f4c121376097ff0e96c2a33ea7b024d812b42fe2c741c8b8cee17e63d729053f28155071474b4686323db5f7a318cb3f088b76660cc8ff5e3e11ec32eaa3955187f755708cd6a8104314b962ff5043e36efa3ec5d84df40c58b44222126e2fc857945d01520797a75388c58e710c9fefedd28387af70880f1682be41e64ffc83f4f86cc595e607a00b977eeb6641e02a4e6e556c24ab163aecd7d146c7aeb767131602e6612e607a9eb8e26b4ce4fa4765593d032bc923ce8acadda42763d070989ead31f265b40cc7a0cd29d47799b766d6a7f084e44c82baedfc01e629f9fd0dd7279c7af7470472d1208a13e33239b484974d47cffce4ad4785644047277f22b9ef92a8b99618f4c86c2412f0e3b08a4f965f842775672043d1e259a86227e204a2d003399c2a3b50c2c869c4380c195a014a02f6d2e7048941237", "0x2099d7f109d6e535fb000bba623fd4404e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x2099d7f109d6e535fb000bba623fd4409f99a2ce711f3a31b2fc05604c93f179": "0x10f89c97bf5b2c07c05c84eebce4ffc7b28766946c03741fd1a71fdae0942e876892cb05c48fc643f057626c669604675c5ad5a836266f260ae7030c6fdc17a54326e2fc857945d01520797a75388c58e710c9fefedd28387af70880f1682be41e763d070989ead31f265b40cc7a0cd29d47799b766d6a7f084e44c82baedfc01e", - "0x26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96": "0x00000000070d8aa99f1452f92000", + "0x2099d7f109d6e535fb000bba623fd4409f99a2ce711f3a31b2fc05604c93f179": "0x38f89c97bf5b2c07c05c84eebce4ffc7b28766946c03741fd1a71fdae0942e8768cce3ec06252cf7cdad47fe1265047a9bbddb9059ee4bdc6dec83b67249b4a9342ada042fb4bbfd9b6d8c48293ffc4a7722632c843a67e608554c41d06aabc41392cb05c48fc643f057626c669604675c5ad5a836266f260ae7030c6fdc17a5438c15606f4c121376097ff0e96c2a33ea7b024d812b42fe2c741c8b8cee17e63d729053f28155071474b4686323db5f7a318cb3f088b76660cc8ff5e3e11ec32eaa3955187f755708cd6a8104314b962ff5043e36efa3ec5d84df40c58b44222126e2fc857945d01520797a75388c58e710c9fefedd28387af70880f1682be41e64ffc83f4f86cc595e607a00b977eeb6641e02a4e6e556c24ab163aecd7d146c7aeb767131602e6612e607a9eb8e26b4ce4fa4765593d032bc923ce8acadda42763d070989ead31f265b40cc7a0cd29d47799b766d6a7f084e44c82baedfc01e629f9fd0dd7279c7af7470472d1208a13e33239b484974d47cffce4ad4785644047277f22b9ef92a8b99618f4c86c2412f0e3b08a4f965f842775672043d1e259a86227e204a2d003399c2a3b50c2c869c4380c195a014a02f6d2e7048941237", + "0x26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96": "0x0000000007def964eb114a412100", "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746b4def25cfda6ef3a00000000": "0x4545454545454545454545454545454545454545454545454545454545454545", "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da902d496d20c019d22397accfc42b7635d94c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10": "0x000000000400000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000080c6a47e8d0300000000000000000000000000000000000000000000000080", "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da90ffdb9747f7a8bfa200ea2291eea6bdf9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x000000000400000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000080c6a47e8d0300000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da911fcf3e922de10898cb04273301735e65270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c": "0x000000000400000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000080c6a47e8d0300000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da915fbc5d9b44885a2dce0fbaa3834ba211e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413": "0x000000000400000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000080c6a47e8d0300000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9279effc2672354478fb5e3f7e65726b668728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69": "0x000000000400000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000080c6a47e8d0300000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92ffe7c0dd7d3d8d8485c403a33fb7adb6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627": "0x000000000400000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000080c6a47e8d0300000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9350ea875b4c0459b3527d475e9825c99d41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d": "0x000000000400000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000080c6a47e8d0300000000000000000000000000000000000000000000000080", "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94d176c4abc6d2410c43423c2714b909c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x000000000400000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000080c6a47e8d0300000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9585bd5ccc1af6cf60c19d791bb091b750efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108": "0x000000000400000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000080c6a47e8d0300000000000000000000000000000000000000000000000080", "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da95ecffd7b6c0f78751baa9d281e0bfa3a6d6f646c70792f74727372790000000000000000000000000000000000000000": "0x0000000000000000010000000000000000e40b54020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da965bb58c29c9825c1f6727fe8c41650ac82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x000000000400000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000080c6a47e8d0300000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da96deaa967a94fa09152be01a253511479d0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c": "0x000000000400000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000080c6a47e8d0300000000000000000000000000000000000000000000000080", "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9829f3f4d6d7c991993e0c044ae97410b043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x000000000400000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000080c6a47e8d0300000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9a00c6f5358fa04d76ce7cf995bce2eabe21bb02f2a82cb1113ff10693093377672925b23f047624c0cfa7a24a8609841": "0x000000000000000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9ba2db252d1b8c64ec51b04d0f50d7d2b9037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74": "0x000000000400000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000080c6a47e8d0300000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9dfc40f350cc4563ad9ddff5ad92944278eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964": "0x000000000400000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000080c6a47e8d0300000000000000000000000000000000000000000000000080", "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9e6fb488a1496189393ed0a95dcf5577e7e939ef17e229e9a29210d95cb0b607e0030d54899c05f791a62d5c6f4557659": "0x00000000000000000100000000000000000064a7b3b6e00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", - "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x02093d0014706173656f", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0xaa183d0014706173656f", "0x2762c81376aaa894b6f64c67e58cc6504e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x2aeddc77fe58c98d50bd37f1b90840f94e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x2b06af9719ac64d755623cda8ddd9b944e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0x2b06af9719ac64d755623cda8ddd9b949f99a2ce711f3a31b2fc05604c93f179": "0x100edf2a41cb81178704560b02c35f5e01a5a97a568ebc10c025ade18b6ab2fa1d74bd654c470ed9b94972c1f997593fab7bdd4d6b85e3cf49401265668142584eca3c2703db1633a27eff681d979967988c3a6752c669fd41f1abde10f3b0544606bd8fd81e50cda2bd67bf6893d921d1aae5cb08409ae43e0bff4d54e1830e58", + "0x2b06af9719ac64d755623cda8ddd9b949f99a2ce711f3a31b2fc05604c93f179": "0x380edf2a41cb81178704560b02c35f5e01a5a97a568ebc10c025ade18b6ab2fa1d98aab6f52520022d011a6eba2dca1c6327edbbcd753c170dcf0e9118b5f0f25bb414aa148096a92a1831309f758f944725653363ccbaeb21817b7df5784b8d4674bd654c470ed9b94972c1f997593fab7bdd4d6b85e3cf49401265668142584e94848b8cf2cbc9e6fd72db8d80676591b5be4d1ec68972ada48cf6fd01228712a8a03d86e6c0dbe180cadfc7994121f462b28f7a8cb1be7e0e354147624be734bef47a9e4b47ed57461e1d28cac7da327a52ebcd64d74080d31deb3ac7a7645eca3c2703db1633a27eff681d979967988c3a6752c669fd41f1abde10f3b0544660fcc9d094d21fe17cfb7426501f50cb3d75c4c9395a3140e0f255443f660d3b30c40adee5476157ef3c2a26e10cab95ec7d54b62dd220738f5a474d5f86874e06bd8fd81e50cda2bd67bf6893d921d1aae5cb08409ae43e0bff4d54e1830e58d28145a7cde195a4c834276730d30f074b212a150e770931ee9470e853e7d2247c03ca47a3201455f8f89defda4aa909cb1d25dd9ddb7fd62a940606f79b56632cd12c731d91441f0114b08d314cd3f9a9f7fd0240d467fe54adefbee4d90762", + "0x2ecf93be7260df120a495bd3855c0e600c98535b82c72faf3c64974094af4643": "0x01000000000000000e00000063e7f4c4028d97f9e69689cea528e2d0155ea2b202be0a42381c642ace2e4cf4", + "0x2ecf93be7260df120a495bd3855c0e604e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x2ecf93be7260df120a495bd3855c0e60c52aa943bf0908860a3eea0fad707cdc": "0x00000000000000000e00000063e7f4c4028d97f9e69689cea528e2d0155ea2b202be0a42381c642ace2e4cf4", "0x2f85f1e1378cb2d7b83adbaf0b5869c24e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0x2f85f1e1378cb2d7b83adbaf0b5869c298ef7dc060436e4ed803af07632b89b65153cb1f00942ff401000000": "0xcee7559d1a7431ad9a589c49d42b6e5da57d8208d290516347b21415449fbf4c04000000", - "0x2f85f1e1378cb2d7b83adbaf0b5869c298ef7dc060436e4ed803af07632b89b6b4def25cfda6ef3a00000000": "0xcee7559d1a7431ad9a589c49d42b6e5da57d8208d290516347b21415449fbf4c04000000", + "0x2f85f1e1378cb2d7b83adbaf0b5869c298ef7dc060436e4ed803af07632b89b65153cb1f00942ff401000000": "0xe68919a7ceff5bcca24e5051b9ab34b9c9f9e74a925cde4c49570cfed7bf14180e000000", + "0x2f85f1e1378cb2d7b83adbaf0b5869c298ef7dc060436e4ed803af07632b89b6b4def25cfda6ef3a00000000": "0xe68919a7ceff5bcca24e5051b9ab34b9c9f9e74a925cde4c49570cfed7bf14180e000000", "0x2f85f1e1378cb2d7b83adbaf0b5869c2ff3ae12770bea2e48d9bde7385e7a25f": "0x0000000002000000", "0x31a3a2ce3603138b8b352e8f192ca55a4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x3a636f6465": "0x52bc537646db8e0528b52ffd00580c6105dee2866016521030cf2a1d73eebb153057df9bb472cc0a860361c473d10e8f9f75d9071693fda90415453adee41bf641bf24fc4306beefa61e5e18c0cbb36d9bec46d011d098dc9c6d2bd360511bf97b6f42f6de5ba6245368194d145f153c9d801bc50970a58c7bc312c497e3b3cbd8a80c636c54ca9e9d7d081bcd672c609e1dc646fceccb479356fa3cfb8a0bd032c606ed2b7128347d00b7c8b83ea38c7bb361fdf8e5d73f36da9badccaf876c348f9656c27e07006ed2387d7d5be0b2d1dcf9ebe06bd968d9cbefd7291b4d5f0727cf02b488b141fbe05c911de470f76601596460d97e536f9fdea2925977de2c200b1896edb337097638c9f92cb2af48ab12afa8844ef100b567259e7380fb1f1b95ecedeb3c497618d9277355e2da9c4bc0e9fb53fab34ff201e3cd66fb923e3b65a375f66ffaf7f5d1f2ac1c7f7a397ef93dbbff0aa9cd3942b7cf3979b62bf1ec7676eaddddd37b32fbf75c7b98b053e7599765e292e95c4bf6e94aacc4fbd395c97f85d478ce2ec0fd29fdd9c1cd600417695c9695dfb3b7afa8a47d8a1a9860b3d9b07c3bc8e1b2d1f492fefab251fbfa8acb3276256e92b28cfd682cc7679f4e9d1d84b1d13e3b38c3918d9665ebec30369acf1e63a376f61595b42bb11253f24a9087aafdac3d4cbe9ffefdac3d4ca6539f4e6bd4fcaecbb292fefa8a4a662de74f576225de1fae4cfe2ba4d6736e01eeef4f393e3bf8b15139bd7d9d27c90ee3f4327cf6b27d59b60e36f9b16c9d689cdebebe6207d399ba7e2c5ba72c5b57627092fce1263dcddb5fe97e3abbb8494fd3f64e03f8e9ec2087bb49d3cbf665d9fac746e5fcf675b0c98f65eb4363fbfa8a1b5096ad77fd58b6aec4208793a41f6ed2d3cc7a27efafe0fc747671939ea6ed9d06f057c29fce6eb3494fd3f64e03f82bdd4f677f6dd2d3b4bdd3007e3a3bd8e124fbc54d7a9ab6771ac05fe97e3a7bb8494f13cd5f097f3abbcd263d4ddb3b0de0a7b3afd801b5d97eba124f71c968fbe9211bedb3afb864da7eba122bb1122bf192cbb2f5fde1ca34fe0aa9f1e435c06570195c24e3f4f56d21a9833fd0c20aae2431246101490a90e881040f2470202101923790c481e40e2423409207123248be404205121790d480e405243224dd92c4400203921890cc80848624169074414285644b520b48b824a5901443920992ea484241d214198c49a222e904495924e1911446d218495e248191b405922c495720d182448524aca42e92ca40b242920792be482a419215495524199184449298a41990b0904443929924aa0cd264c044064a64f04506502449914112495d3258228330327022898e0c8e48d222894c1216495c2495c9e089242d196065104712144944240991810892de487223294b0623c8c08da4393200230331322823292be90348c020b1010996a42592a2487222498e0cbc4842930482a438924490f444120990a48074061218485f20758184855406e90a242a9064404a026909241a9036808404120f483d206900290c120e483e20fd8054051219242b907440ba42ca01e906242590c0208d412a02e909a40e20058104051211483320d940d4a5888ea23a8a4a506482a2111499513444911045411475a00888220e148529faa1c887a20d14f550c44351154556149129a2a2c889a2268aa62852a3288da2334568149d51d4451117455b1469519445111645658aae28f240511145628a88a041143151b444d19822258a922842a2e88822238acc1495514446d118453814dd50344351068a5e28a2a1a8aaa84b119722ab22188a64209283680ea23a884c400407d11b4423208a830804446a88ec204201d10988c220fa82080ca232888e2082820809a214105d41f4049114445310dd88b420c261480d1116a21486483024c7501b43730c610d5d31c4c51013434a0c093164c5d00d43381c61e3481e432c0cb97024cd912b8eec71248b23681c01c191368e9c397282232138f2c611111c71636885237a1cd1e24899211586aa86b80c6519a21ad232b4e5481947d63862e6089a2363fc8b913b8ce0c16e7811302af80c2328e037a606aa1e312b8c9461640c223378822022c3c80e4379f8e0e1a30696858d169b148aa4a0481945ee2862a6089a221ff02b86ae8672e830806cd0690156850c861f2e300b48e420d246921a4e44d211b132404b00c901c80d449c00028648134498008283634164892176780d18b041240c110e10010288ed87123213f8d8414409227a08b11141027606d01940b4ec906007053c48ecdcc1c3028f179e1676460084ca070f46acd81141bfc10326280515580d3511c487236414c962e78c991b3c43f86061288a9c2c76e2086a8327892010f06c095a63c7044167621ce0e11234c69013b33678c6047921a3baa08bec043e2008a2da39418e16413610a4875816205d7cd6e460c9628871d9b12308882258fca8198262f6c60f167cf6f08165678d217a046de191418786217bc4b0c0000418a0c1400d0cceec7c609666e7cc4c04332966b71919b3286669ccceccd698a9319b022803332b6664664c245161162676c5ec87990fb30de874a958b013c84a20cb92c531244bf682cc4aa605680fa03c80f00052038402a03a804a0094054402a03880e0004a032402a03580d820aa01080d901a41ee08a207ac07d80678b400c221e601201d6262807278cde12900ed102b02e80bd00d312378b6e0e102e80a0808202284c4003484101980c408a101a80821330009212403401e000a42480d42be008501a2828f1bf58c111788b04044059e1c76e0a868f01441c4cb8f16580344b220a245055a783ac033034f161e2d414a50dd702d5504158e223b764240c4852174d430aa173b20e0a1e25921c81e3a566e457d23c8098a80112405414c00c44310140451e359bc0aa88620790081098287db7ccb8b04be826c0659065c059812b024dc05242db80c0e83bf50a4825bf938011ba248999d3d865ad89183c8871f2ffc8041e78b4e0c415408d2a582121cf1020916500f3fac2af85241063823d80b5808182b698f24362ac0c2360021118b0289890c52907303c2e293029f3b6cb010e1425b00d20050123c63141183a70b1e2f7474a8800b101a3f9a187a02e88d1c2980eef8a103e80c902c312d4026c899c28605a035b0334072bc6e365b7eec006407902d406efc946036021b1580da8099817551d7f8e4f183099819156c81810113434808848000e804b00f00d15141085e3bc0d0009aa302157456001ac18e183b5fc0d6f8b104d00772b6105205a407500a8042b003c68b8a236600e1a25326b66516c76b0a205874b0289a63c78c970f455b606e106591404c892261d4361c4b4d03a4444f0e31276250c49e804d019322c604511eb1266249502086dc51cd50332ea60787980fb10d101902c9cc10131c7161878cd80b31ab1d343c61743cc0a343ab21f24264851f46042902c606cc0e58160f0f3f74f82103112b2330f8e861c46a670e2010f0882172044f0a15500d29814e0674b87029c0c8806d3184044350e06476bcd84183c8061d1ca89620297484f0598347033fae606d0cbdf092029606ac8c5e0366056c0c2234302e605a201d11ebc1081a447e9825012443912e5e560461e20799fa058c0ae6015811303130236044c090801d01fb813f000b0303a247075810b00e04b10396c34b8e23627894b0e9b21306ec0a1d1988ec9113460e183d6062591879814815912d33387862e0d940cfd52b8ea23b869c00830c14b9721bd8082433780c03c11dd019036803f388514d9035ce05690c6f82a80c4c8e1e0d38d578c4ab8e318908bc4af0caeab9e145c75016af375e6986d431448df3e04038073c8cffe03ef80686ccb183c74e0a8e8041e405225d76d8e0b94107cc113c8edc6124061e23a612445a401222481d3c553c30341c1d87cd15938b5a066b51e48b222d3644f834e1c3c410962367bc7ae079c1cbd850f1a86093e5081642b4bc7878fda053354405c3a14386102ebc42067a50178e8c11a44383f10389203c10ed4124890a7ca86420f9c253032c052dc705b62369c04af02309202b34968f1d8e8461b3820f1d6061e86800481e485208f940520e5c065115dd848f1e92e0481a41066fbca2c8a0043c030c0d9b119265a70c215886c8bcc8c0b082b0c0d498d1f0da001d434817d81c47d604d921880e41ae74b6e8500d65402705242d8450250d61e4899d12e86881e9b193467f61e345074b911a581a1e2ba22f3a2c2435e155ecd0314443901b908080e161840c9baa991c49587e4620c44b521a19c8c15b6460068897231e602f3f70c0f2c879024855066b9a8b1c366cb6b0e9c2860b1b2f86be04a9222243640d521442561092c2d014478480ad11a202ec8e0bb2f01c21048d8e0b3c38086101884c8fa0282b033a3280e38214605ed0333a579c86c7079d1d605f004981870347be388265c3250332807020a98d234820c1218331846c81c501a48566020647121a1760493a936446100f00d162a80e98088e14c12a00a53154050c043013d02cca048d82de681afa049582be419da070d026e81440476010020cdaa078d03ba81a226390380044066ac7eccb6c06581d3b583a30f06021b282280b1d1b88b020520791087868d0f142f401d90880c000c40a880b4062a8573b667466808d0046079119a2338c98b1b366678c1d35746ed05141e7859d36783c60a4059d1a602480a9e1c9804e0e3b593a5976f480bd0143818e6dc83604c390d5900c47d43862c42bcc11318e1c01e485a12d906e455440588061c1d3a5c84bd10a394d10e901db83a707242f606580ec8081160cb660409583821c353976f8bce133021f38646a6478c8eef8f1c30f0efc0853ada865ea1545c61461a2c812332266625e59bcb6786911c48b205f04c19add007361680898158c0bacea05c64b8c571841e0f871c58f323f6ef891c30f1c8a1051a488226288a041240d22678cc061240e2323a8515429ea4d670f1f369d354134047d09cac08d0c3734dccc60b3e685e565cb39d37be4a4a12387ce1c3a24c8d123674dce1e152451c1980a94a82c54176a0bb3aa990ab32e168cc002392c88a382ab0a76a840070bd4e01258b00699d52120e968326ccab031c3c6cc0f296ceab039818d0980d001c404404a504110231863183e6af0a1011f365430f586aa811baa1b2e375b76f2e0b1edd4c1e302cfd58e1b3b6a786ce0f9c243c44e9a9d37787ee0118267071e20883a7024054331f416445bd01b280e3487a435b48a72e93c908618ba63a8049da6dd681310ad4004a6c7683188f020baa39de824faaa8968311da677e80eb412cd43ebd041b407fa4b83691c7a8846a26fe81fda87d64017d1363410cd81eea18fe81a7a4ce7d01b68235a88211410adc174701ccc06b7c122603518048c86d7e010b01943763019ae6286e129580abe71147c048be130ec0373006907fe8193183a016f807be01a9806fe326402de81abb80bf3403b8054c6cc626a31b7984ccc25a6969965a630b14c9b950640e083f9030833070750214184a50a78b00005342140070940c0010cc0c1061a28a14d27e6182345478c844600d500e7effca1438b7b090a4a059c5881b224c5891370760854969c38710505ed7421b4c345531394284d4d5008b08594a811a82c3569b14b4b2238a912e5244aca0853962c30dbc962a19aa640895a0ac1c9d2920817ec60b15544a032c5c9094f4b2538399962e5a3a82c9170029426be536697a250a942c509942854aa2420ca03d9b922842a51964208b263c5429db0c40475c2121306903b55404d29c0094fee9029daa1e267678a3561890ace8e147b5b2951aa5859ead989629f4a18618a0854aa447132c5ca2f55000a4d67078a0b769e40da71624398e2246a09eaa984283b3b4d2cd4d294a8294f55a4449500c5c9084ea65879285642a002e569830a76985828a8a526203b4be4ec8c5162a3a89450258a1311a02c35e9ec24b14f24904002025e3b4858b073c486b04465842950544a7012a5ca952925382d19ed18b1514b504e3cb05053a04e106169a9024ea658f91344585aa2f9d82962a3a0aa343979aa6265e949cc8a50a5ca93152756a648817a5aa262812a4e4e586222778858a8252950509aa09c84b034a5c98a1528544a08b233c4422d459560056a4a083e768458295050a4d49d2076e9c98a93a5a72925382d3d398112e18a15213b1dd8282a4b242c4541898212e18a15d90e100bb51455e58a132754aaf8ec70604fa8809330504d4b54a8409112c58915129ea43809814a09552a4065ca0f5154aa4471e24442083e2c1409509ea05871f25465035b45049e9d1e162a8acad295251e76290a4a085342a00225ca909d1d7629842a5180ece8b04f5544805a82ba5aa8a812ac44955005682707124a80828262c10e0e0b250a4a084ea8549912c25214d414a8a7252b17ecdcb0504b4f55a23839216a4ad3940a40699a02e50a14df01b350564aa8f23482930a7634b0504f55a2a0aa7000e5094a93d88e0d6b812956fe6989e9a98a033a20776ad8a5a512964a508007212c8d3002140f00b0f365a1445529c1c90914152753acfc0854a63041896280cf4e06b64a94131a76290a15284d502ae0a44a9413a8282a53aafcecccb050213c41b1c0d2939311a84c0981ca92152753ac3c5453952a4da85499120585ca1412aa24a0eec8b0504c5196a4448961a1a2a82c3d2d49a132c57760a03245849e9d1736ca14282a539ea24c5872724293152b509c3c2d85d034a5041a53942a4d349f1dab85e2d97161a138f1d96961497002250a4a0850a2961070b3c3c205a6344199123585ca149d1d2fcb14a54a53059c2c45951065c984259c9d15f6a90a01c29d4b4d15801245a504119c4445597a9a120295294e9cec2c81c9e081a105cd6f0ddc9bcdf6b3d63b1db806b8c6b4323dfaf578cfe75f4fcff77ddfd7bbcc54ecee6e6ef7833bca4cc7edb6b9ebe6eeecdeaeebe672efecbaddae97776eb7bbdd757f1dedba6e7767d7f4ebbaedeeba053d8f76f7d72d7acdcc226fd7f2e8f2ccf5a80f6e71e734479b03993bce6bdeed3aee9a365dba1d04887437a9ef8f8e865e2fb38db74bbb026f9b7638bc61d7d1deae9b9b2eb7d7f570d731d38e763f28bda0a3dd32dd6daec3deee7e703fa0bbfb477773cefd3ceffbe852dae0521b1bda607b1ef53aca0c720882d7dc0ec0a09b3fe80e3b6e224cbd6ec0fa6ec774b9bbee6370bfa58d036eb75e8c7a33c85c0f3aee2006f402767977bbf0dbddde95b1b79333bbeeb8fbb883bac9dbcddd75d3dd21ddfd7a756f0f69cabdb3e3791d8436a5ed75b7473dcff33c4a3da6de647675dd1ed80a601d1d9dd993276dd2ed76c8b3bbfb075b30f67a1e0540f7a4cd1dcc2e01ddbb949bbfba4166d760d39b9b1b6646c0d7cd9383508f7976b72165a6947aaffd983deea9d3cb71dddcea5e6e77b6ba5bdbda566bb7d56a6df74d77377bbbbddddcd2b6e9eeedb6bbebf0e38e59c0b46da82732337f7c00cacc5dd73177dc0c8add819dc71de8713779765de7791ef5769b72bd74297b9477bd8fbb9be93237ef2ea5bdbb5e874cbaebe69cbbe16800ee70c9641a7608807e31dd6eca1caba0b7db6e829cd7bddb2d07dd9eb7dd8b7bceee39a74777b71bc4d9607976cf9d5f476d6c30605148a7016d9edd3173b7bdaed9ec66dd4a9a520ff428a5cccddc71b7bbdd36dd5eda5d1736c7ade59876d3d975ddecb83b2cc0d72debbd809b3673d3a64d9979f2f6f77dccccfb639b0094f9a7797bb7bbebfd68f7c7d4a31defecc066baa1b7bcdccdbccdfdb3db4dd7029ad7b4bbb7db66973d5e5e66ba5cd7cb2d33c37876bb777a3c419edbdb8cd371b7ccb3bdee9a32e5ee3aef6bf6b6692fb8ddafed9e61d38f6977b737bb67f786e118766c0cc39f7d333b3a2773f7722c0c976d7ab9576776d7e20de9584105aff0d51c762f2b51a69e3752cf1b80e779d4f37e3c01785e00c6eeee26a594b97bf2ecbaaeebddee769777777227e99682fbf5ecd9755df7c74cb9bb9b524abb6753eee56e4ab79b6ed751daccb4b997f2d2edb6dbaeeb3adad16ebadb743bcfe3a6db81dbddf6ce9ddd766f37a5db75dfb7dd323377d36ebba674b767ef5266ee8984c7eee6e6eeddedee66cadd761d737777ddcdccdd2575403c8f99bbeef3beae1b27032024203814c81110103ae7ecbe0fa7ebbaafeb7c187545f3fb70e6c7f39b389fd77536ccdc5dd775dd31cfc95dd75fd7ed761c534a41ca94f276dccddddd5cd3afe3ee761b491d4850d03acb273c55216189848ec8088809552c1055011394285196988c8a8e38ec889111a84c910205458a1385b0f4b414c25315274645479c1c596282d25401274b539ea23018ba801c425e701342d31429519c443595004584a51066478e8c30058ad4396189c94994294d1528c18aecc90228a8a5272b24548972e4da234b57aa44597a7202052a84284b4e8c3019151db11265096a881011a63c2d5d1152a3a03c2d51591ac14906468a8a8e84b0e4a44a9495139ea258d06a5abab2d4e4c404282404798d50a5c9c90854a68c304295a69d1b139642a032c52709bb46969e448852a5a9098a0f8f1512a044412d913025ca7d2a5002d4acb5b404e584842a215081f2545f50322332a110a84071b2f404c50a095588769aa044a9d28441ab4a149410a63441b9e2046a8a132a53a0488922820855842c9130258a4a094f35074984a81280905002159f1c1396a43889a232e5c98a93285053487882120575c10dd48ece1ea952e58987290a940a34355579c2a00894a5279e1396989c4499b044a5029fa8a5117660505150534c58f2e1d608952a53a2a24081a202e529ca49d4141248a82223f788095344587ab252739ea02c3545e9b94265a96fd688132a55a6403d2d5d71120295252b56a038591a01ca539528282751262cf95ca1b2d4422f23786b53535fd8a235afc64d4d4df554ed7b529bae27b5393535353555eb5a2d3ea9cd5aeb496dd65e5353cdfd646a4ecda9a9a9599b9aea273b35e5d5a6f8c94e4d6d6d6a0aac7d53535353357e32356bb57eb2b5ee496d4e4d4d4dd5b82753739fec546d3ea9cda9f964a7e693dadca9297eb2b5a9a95aadd675cd7b529b5353dd93a959abf193dadc27b559abd5bac63da9cdadd1275babf193add56afda4366bf3c9d6e69308742cccdd9d244041d9ffb573f77b03c2853a8462335675e6d97d7ef888612c833d692465dd9f6b902fc31840104690fbbdf110c434c2889ead06a8fd3a0a41ac2f4d88ad1b049c43eb6bb34628bde0cbdf5b5598dda452a8c695b496534fbd6c216043e04b7ece9194e4ba882cda576ddb8515f5b2f57d81af08810b5e68827174e4853aa5e4c4f2eb3d9daf96aa8ff679cb3db2eaa379be6439e501db689fb7c82d4834ef0a7dce7b96ccabe7b8aee5544f579b78beefc148fbbaae3abd9eab3f7cb56f57ea69fbcf23bbc631b77004c60c25ab288d7a931e8c34eabbddd5e9f53f504a6ecf9679de50830d1a9845c01cd96066a4db1ad83da37b3be02bf369fb92eb34bf7d7783d6a715b8208cd3cb9065000041a8f9dcaa4cd35b3498f9929fe606711ef5bdd3ae79d057d4207497cb57e4601fac25c43e2af6b3ba949a5be4ea403df4341f7acbc10e995c97b79804b99039f4292e6979286ee039e89304bd45864e45269f4f9f753768965c5b0e9225fff4d695ef9d7a0f5b19359f7d7aad9b3c02ead3a903e069b8facebb090833c4d1b1eee9dc7f644fa4c89086ab00481c1c37e79c9d37072108b12d7300369acfb9101bed6d86239e3eb765ce3ce7cb46b12f978de7ba0eac3cfb4e8fea989cffc3464e73cdd370924feb511d1327cfdeb1ef74aebb43a612a6962b9ca206dd9e831e5962e03b974281bf29d680bd5bb56467fa6ad9be5e765d272ee9dac3a444e1d9f7d9d99d8d986c07273b93d95526d339c6469e73eee3917bab0a33c26e32f4f01dc975cee9b1c71ebfe29a1db2de9173cdaf68db21cbb65f710963e1662b09b04cf2fb72d1d7616ce4f2f5181b81beeee33f6cf4f9ba8c8d3aafbd8b14fd236b20e8025d8943b2e7f3968b6c3c755086a7ee9241cb536f8901eedc61dc5b0c62fc4e1dc6bdc540e6a98b0eb6c28ea40e82b5d99125e761ab4572e00c39703f045fe4d256049da98789e79c7b9ec8843ae89e8dc7e4f33c1b0ff4cfc196cbf75baeaac1e72d6f3171deaaa17fe4fcd614357079cb257640dd5599382fa943d5be734eec00aaf6a133751d50f76ad979585bdc82ae70c5b096521f7a4796f43b7f91cd5b768d4c9a96976fd935d2f9a4297548baa87764fb8b5c1ae80a5fe4d25cfd658d746a26adca243a75d1575492a48651f476256691095d027a574bd1c1da03bae79cafc884ba587bc0cae4f3ae235dcef4799fc7e4f3be562d3b071d6472959d8731d4f1d441073904c932f49e96ef83de913dadaa01e7a083b59cdff93ed8a18b0cbd9c1f82de72b0ee0695a0b7b7ea06952d77b0962eef48d0959a23e777f35de47e6b7e5712fd9649be7022612cbf6e238434356b2e0e7788c14b3bf10443d59cd3bbae73cfeb3acfeb9cdd878d9ee8773ee795cd66c3429bbe22f58e7cfa9ea3cfb9127724577dba39e3b8e029ef06754e7d929e7764399dbd450da67bb59c5f0f93e99e4ff7ea74d74f70c10e39707b929465dcb1776207ae5f776d123b153b00ab6b83d8f3c869fb8e2cbf67c6f2ec5c1d2f18590a707219a783e0ce0dc8f0d329599f9be064f100a133393d24997803f6c9f386716f2ff4f02ecfe35d1f3a087638c50d3e6fb98b2c5bce04bacbe5ec20933d4ba8cdf6a2d3ef5fd199e417bd90ee06bd9c73266dfc45964c450dd86d6a3959d4807d74a51ec91e26ec36ce6e53c91e26ae67773d579e0d6a6feca3a5b533f9721b4fecc0f5a1f36c523b2776d0b341ed61e5b1711b72da7ec50da6ed871b57e2b18f6eeaf6acfdd53913bbcb4b57e203d4a270b99418a422074a3e450da6b333bb12b7b884ddc5e4daa0767083daf6bb413d8191dab0bce8ed600b14ebcb77835a2fb76991653fe836a04d0b74917cb27985aec4afd06d4031f41618d6dd21606db9cb79fa32d9dd7875645957eb731566c3de0ce0e432d2dfdb0b2c4c2e23f77b7ba16a37c80246b2bf5e6c48c24c7691a2bb77d6dd5a9af0cb24543bc72b88a228860eba288aa2e8fb23e60182e0b72414088220e8fb03a2a0d56ab53ef7bcd56ab55abe3fad2ccff3be25a13ccff3e000976476708acc5196713ebf0de27c4928ce3976cee9b7496c80dbda2425ed4b042ed3483d075fedddddddbddd9d83b30357a4b5ac951fe934eecd2acd73ae84761db941e73977e0f57ad9b8e8a183fe7abd5e2f108020e8390882206806dcdd395fdc202fdc20cf27e93476ce95df7bee79b849ec9e8b9bb4ef39935e17e0f6b20b8157b87772fa4ee3157e27fa3e0b0b01dc1d7089e71e75ef5b9d7faec4b4e59f77947e5d0bf4c82bf3297965fef7b9c7be41fb55a6cf3ba708f8bc6bd5b2f62da7dfe74a4dc992fa6c39f8cd39a97f73cee91f65ffe69cff718b3ffed827fb6b9deca14cf650a724074abf3b8486d3f3dc69ae798a0aad2c7962801db2817b64f9391375cf41ea5d9f9c36cb66fb289cffbc3b2afcaf032bdf722576914f4e0378de20ea51ddbbf8a9b7c827974775fff98a4c9c7ccb837ccb5da4d3007e6e10a5adba3b848a1a704e7d8a4aba5ad6be734a7e1f39bd9cbfce91253b7da2f5abb56f7e599a739023f7a7d80e4e7172e4244303fbab65edbf59b2b7e779ee797375925a6f72bfa7934e3d3d77f2f11cfcb8f428cb3cd2f3eab2ecab5ed764d97e65fc266ac3f2e5e74e5e1e58d6fc95ef3f9f64d962a2ce0eba573f32f4bcfc3ecfcbcf9d3e2d2960f9a72bdd4fbfe23d54ed3d9fa212cf95da233bffca287e9777aeea34f378da22a3f8a9d79e25f3f6ec2c2a61efc8597ade399357f8f38ecbce9dbca747bab73e4926f69dd5239bcbf99f7fb59cbeeef992fe12aaf61a2c7d5a99984cefcab4cedb9a1ed5087a80564616adb0bea44f9f8c564626e98759efe4fd151cf135c34d9acefde2952e44f357429be9ec2c76406db69f3ec525a3eda7ed270ac0d792ce3befa6ebf3f5cf57d4a0e5dfe822a7831d79657e39bfe593bc32bfd50279e732b599d537a8235b95e9737064a3ce435f9fa26b3eb8a292ed6e4696b95c48c9ac65ed27d3e7a0cb376986a46fd0fac8464223cbf6fb8ec9e760a52c7b39e7d45b3ec5b9656cafadea13fcc8f2f39683e08aae5ad6dee5932ce7f4962bf524cbe9721074b5beefa7b748afdb21ec1cdd21ec2d5f5103fab34536ef10f6d200b5287eee107603d4a2f8dd21ec2da7fef66d91936a6c1fbff73d32e8d7bf197e64c9f9748fec99d5697e577dc8d86c0f80f76687066caba7de646d64e2fc822fb93abdb06cbf1d9c5ed8689ffad2263d09fd0febb515814fdfad5dd5b22588d679550e6d3af5e9858d604f9d270998b16cfae98565d485be847db96d2860695a3a9faec4526339bd3cf572db78ea52eb63cf4e0046891c941083c666a3754e6993b4d10eb4c0430a3b3c41d3b2258836bd2a87b6ae650bd33caf12006d7a4756098026258407d3a59af48cfac47aea9d4b09e181e75d9552ea1975af4a09e141e7b34a79cfa87755687b567e5feeed36bd5cac4501172c7e90e30c203e00863ff524398c4babcdb0c51338e02085190f9880b6b4f552c803b7d968d30b1b5df0d4f78756a669f52b648a6bccf40a9cea59cfd6570714aabdd6459dd2a96f9f21785ab9b2a3ee552a5d2d3b6fea22b29eab9d97d4a777de24f515996c172e7c577ff840cecba92f39b2e4bca906e63d2adb85d5735e5ee03baf5259369eeba80440cbedb98a15068a22654c514b13ac6eeff37bb3eae1410e39f207a9f5a9d79a356bc0d05a4ebd66cdff6003c2c20664b87ab2e08a8832b6282bcbc6ff602b52c614b62b01d07233c1b65d58d1d6cb0b3c97c2b2416b55dbb2415b2fa59eabe5f4de22e5de5a38f351f37fb0f950a3882c6fd86cb4ad52eb535470d0c10c2c79d86cb4afdab4d86cb4f5921ad15a5a59a7643b8b1f1429438a351fdc7c400d1ff6c8b2660d0df63cc5358e3b215a00f3e36c41869f9361d8da519e544d37697f9d525189182c6cd1751cc7712255d2cc5280ebdf6dd29c62c7227b3e2cf37ebd4d9abf2dce1a58f0f21ef3c2c6b7fbfc7c835d4829d7715d8b012f70c208ae061e569bb3085c2f61640fc1a694e47c4e8fe33a6ff7388e033bd287659cb3386be09057e09c2a719a6b9e7a577d58c6d5a5949c4f99c300a77f9b346bd5190f6359fbb749ed4abe4c926ed0725d967d3de711ebdf2671be0e723dfbf3eebc3b6f27fabaf6eebcbb9df36159e7208794dce794300dd49598a7d8790ff5f9fc3d7486ce39affab0ace3c8f91fc701b2d34d6272c2403769d618de7d36c869aee10df69de61a6e83b6c6737601d739a79bd4f93ab74993146219e7c5ea87b08cf3157b452b43627de4da24dfa0cf3997b1d1c8b2afe59cbbc8e5d9e7de47df8faf8f603cfb9cf38de37f6cd27cce3b1f967dde2c74a47fdebe82998fb1ec9be2e73d9ef7f3f778d569ae79cff7e6a5eaf7e6658587b1ecf3ce897eabfab0ecf3486e83d657a41bb4aec41d397f6ad6386e21c0f9203845252e0f9dba478abe9b043ae720486a50fb1535f0983c5f0fc9d2f3e9e06ca7bf372f5a9e6e90a801c92e929eb7c8f9ecdfe722f9798b9ceb4afc91e5fcade5e7eb20871fc9a0cfcf9518ecf02339ace5fcd0c1d057dc20741748c38f645f7183d04104881ed6f2fbb0e5537432414b0a586cef545a20057aac79a8c50a23830abf0e55fbad1b8221b92f92a5090f2240aca5090f7ab8b59cbf1e3a276ae07a77b952af92b032b93470f95627a92ffb5d0f56a6cfd94596df8a4cd83fa7b9e65baed44adcda27c91a382915e04619a997b1e7bed1d9887322ce63679e731f36e2e7849e8b3de7231bb1730e63a3768ee37c7fb8cab4372de003c61c70bb9b906514d6848d4a1fe3f3e58a419d73efe3fcbdce91d8a88c29b151f971c046e5f8848d3c4fc246b1f71c84156478cf25c046f4bd5e33eff520bde7cb463d5fee16ef71aec43e20939e18138cf79c3f86f59e83cb73dbc669aef91dd9a8dd73181b51f7fc63a3e99e7bbe3f5e65daac0e036ed3b8372b355f7e7be301ccb7efb33777393e7526191ba9702b63319f76dae106bb36e0e432d27dc0e8036cdf9f72b1c874d338f62cc444cfced3440b150a6a506159515da1061bb4ed19d57fe18118b64873871d52d0bebee16af94da4679f4e5d09a94b1ccf9ea4007c3469a5cfceb3fbb011e75cc315670d7f6f5dce3cad3096cdc5ea17c02dd2612cbf9fce1edba4308f67874d2e2365d9baefcd17fbf671ccb77f1178210d4f1f4626e2f7964315cf39fcde6ef0e165bf373064bc83935c129525ebcb9a13dd9725bc60c5afbfa60f634ff17bab41869f647e6f19c0fa21bf371ad44c7103171768dee9c9af6fe0cac2e19da66cbe8c02811ab11cbe8c0a930308822fa3560001f725941948008009ac2fa1c660fd2ef96c367109b5ed9a5f27e7832fffe5198278ae8c9f0ebe9ce6530729c939b8e206b496738a4cdab9963d8c79eaec2c6a407dba5233b9cf9193ec61c295f1d4b932be84aa3d75ce9974da356bd670edd9b931e5b8b71dc03cf9e4831c3219e439922be3a7b8813f3b25afe8cc1ac54cfcd9959809c9c44e1d64b29ccf398b1bcc35ef34d7705d7b964c6fdf607ad728706f3c1cf151fc5c2dd9afcc6faf7d2bb1125392fc26c9494681bf5ee326ab017748f3ae9f0eb26b93925c48761629bf3deae66c939476c06383f6cb45a2b4839a2db2cfb59c3ec50ec09fb59cfa2bfc233ccd9fde82b946300c3ff10ab5d9db0e4ebcd3ebb549abc7b77713c9ee97fc65f7ed200d7793a6cdc9381d9c220546066717cc64a6a8c489da6c8b868ee1ce746cec4432d39cf9759eae8984da7e1df4c21635d833def77d5fc7dfd7f3fb3e4e6633bd5b63f303f87beb02e6613ee0f6d19679a18938de00428a321aa0ad7f7d54ab41032eac81822c2cde9081b6be5528a4dc01dda0aba57fe7df76379465b58cea9feef2918d40f740afd5aa65cb5ddef23c6f0fd9a8e5eda047ce075b64e853dce0c7539685b50cdde51eb91f9251fd2d0727ef6c1b8fdc07c97d578bfc61a3dda0d2e7a7b7c7c69fb5ec9cf376b0e7ecc8fd014cffc828ee61cf5d2d4df8ce972cbf16b7bb01a9b8df4dc8326759bbe73e2c6b5f5189a7c44acd4ec972bed35cb366cd9a2f6bbfde9151fdeb93fc58c63e45255bb93ab28c7df4f961a3fd181bf9b7cb36a9bfdddf35391ec0f5fd29a7edd941b23632b5ef97d3c6bfb716ccfc7c32eead85323f6d4b2696df5a4edb1f2dc8e18a4aa82f7985fe3a25978cbfde6459fbf6dda4927acf12d7d3eaf4faf52de385bedc355fb6f3fe92cbb225a711e3f4f9cb45eb537472a7f1bbfa4c9b1760834d82308a0f18a9735d64789e5f15e66869ec1c0963d92c633f7d45109ece2870195c06c10d6bcdb322b853c6bd557d79eef7c665ccef0d98495d8969fdaa05402663cb5ee064c0b8a2016a4f69adcd59802540260470d25e96979dc375160f507ba6ad4e53bf5eb24f28b880f9bd6dc9836ec17a0a82533c40ed976525fdda77156c5109c7aec4e00c3bce49bac1fcce7b3498df7957bf9fce91f3f787f6e41e4026d399a697ed3cc92438ccb91a2cd2183e7df0c5f563597b3be5ba0dd8d9a84703f6f6ee7c72ced44ebdebfc6323eec2576225aecd39cb80cb80117c7109821ea0f6b151397fd68f65d4a983af25633bbbd3d453a7aec4693ead150097fbd54c26d961a4dd0ece90f612f88524b83fe5debe1dac73faf2fa96302dc4466592246c44bd5bdf5bbe7cf7f8dd927d3bc821a51f1b31b17f2c9b3e7df968ec593bade5f74b6f9665d429cba697e166fd3aa592e16869eb2bb22bf1143560a7f50bd988f7c66480cbe014620e31be4618a79771ce15c621e04619bbbddbbb5b4b1edfdddd4e7ffac98c969b162def6cc44e47362afd270b563f7b197ee94c2bc32f218bd5ef2d05361e00bfb714c63cf8da1fca46b52fd9a733619ab58709fde9f4a79b60b3d96c4ba8732dd9a9afb8a434e1a9d3eac4347dbff4f6a72bd3f82ba4c6736a01f7a714fa6559f9f58c7d1d5ca4b1f3f955c6bda510e6f79682ed877e6f58b67816953c602cc7710cd9889d7dd9e8f3ced93f16957c5de7e00ce78353ec1cecc8a8e9a484b28cfa92f13b9f2207fb9dafa8645956863fb95ad69efb3e57ea8f2ce997d4a3fa3d5f5109e74a0c822b762da5befd234bffcf3bb25c9699f09f97cbb2d2ca7f5e76be2c2ba5fce74adda4579da4bebd23f73fb2f479eae00c3d72bf23f757ec807e73a4537f3b259765d35754d295b2cc9b5d062cbf76e69aec9ec9f9932c4ff8e9e04e669b1203dfe4f41597703f5d899ba4cfe47e2d2d17ea231dd1d9657de4c375433ed920cf4752022cf3fc4582c032cf6d480f58e6b948c658e679482ab1cc73902c00cb3cf7417e3cf35c875c9e6d1f71c0334f037e9747f46513d2770e736b3cfca53613e603ce902c630fd627da14ba389906907ab5449ae3e8257579eb861cca1a416ff9487edef4a6e736def45cf43cf47c2ab50d2992e1fcbed2cb28faed2d7264d9573dee364e9e83e3386e0d8ee3d26db9bbdb1dc7110d390012d2779502317eb1989ff808a2382496ed929111fd8ac182038b2601f62c89a25f7e5e56ee419fd5e7f459cb9e2551f45d3e6bc979c95efb56ed096b493ff4567da2df84f42daf3df8899f8845bac8254312ecc2d0bb708b5f17d922cbaf847d9f7b5fe5603e48690158c6759d73ddce675eee2953ee9e7b5bff8a4bf6f65c27c95f13278705e4e077a6314ea74d48bfbbe34e2ca50d5a18c771dc2881e79c92538d713a6dfa798e0b3f3add18a7734362dd1b43ba18a77f4dbba98c6b4e2c19112c1958932107d304e3f46e427afaa1e17c143bc026496dd02c89f636848f24c0b389a4268eac3aa43c01ee0f8c8f864ca3a1acb1fcbe29c7580ab9f65b3ece461cc7fe79bbaacbe9afd872d5a6f0bf4fe6f39bfc03b848e38dd36f957be6d73de016076cd4f9ee1637056023aed5721bbee5e51192f9658bb4bc2cfafdf952f6365e0e11226ab5bc56abe543672cf31890c9f9ae16bb7ea0ad56cb6bb9b0f4b839e17279cbe718d0f5b1ec6b81b99a414c31df8d57c686b246cf27186314f7d3f90b18ae2dbf2129cbbaceb956eb76d36ab56ebcd56ab55aa3b75a2d9f64d888bde5f3c646d35b37b575f38465ec23fbab8c8d9018fb10249b4ed06ab55a2da46fb9abd59aadd98a6133185d5eca5e6993ac6cd074d8bfbc0c7fdbe785fec6d79d8f62a2dbf8aec7868037e47cfe905c3536c77843ee831dba9a90cc3421c5f8f99279cf4472be8decdbe538de2ed48e437e3d9bfbf591d22441b77b0082ccf3febc91d888bdbd006ce4f27625d0bb09e95dde22932939d05042896fda34ef727086a06b7e6cfc742f8d045886c603e3f46fdff990127664c2cf6f8c8f947ad6f90ea519436fc2320f58d6b9120fbd31967be63b1f3a632ce7972f6ce4cd316ce4657de7b38723d8288eeff68398627ea0aec44cee37e9a358cf3affbeb11c6becbb490e658d9df7d1acea59d7f98ca3ebbc091b85deb90720b05198ee0b18367279e7f38a8d7ae0e9858daad8687a58abbe9b358ad25f91ebcf06b173648c652c6b91435923cf3ac6e965ecbb8ed6b9f7103e5a5ae7714dbdf3fde92ad3bc71536c77bb7b669cbe7c46b7ceadb7971cb7cead3371dbddee2e377b085acbe6dcf3b29bf376ce999a8ceae79c7275418e2c857eddf30de27ac8afefa4be2ce3b8eaf36912e7259d4f7d7224d1066d4929a54d765cc2dfa6900b703f96351e61a5edd44bce979bed9ccf76266ecef2eb4a9dd63dc0fde948ea5afe82df1b11677e8b97fdde88e8e1c588f99ddf9b18191eec90eb815e3518fef29ef75d95e12b5bb1cce3aea41fc525d7287ef6ce7b17ccbb5adae078f9b9d875be7c94e39dc36a1945bf3ecaf15e1f3d875c9ebd1c875c9e8d65543fe8ddabe59c4726d961b4a951f4bd502493ec307e35aadf0b7d39177d8a1b304da7ef55a6b07ab48ca23c497018a3faa78b5e46f514c37dab32b17bee7160e59c2bd374ee5bec80fb59f93de76ac956db2ceb1f96b52c8cbde779e794df7329dff4748fd23ff6a5142ab1a795693aadedb4321132bcffde86a0e1b99bde4d51ece066f5f4c1451af9f6f4411a2e1f75b58ca2ef390dbd9c47fcac252de77be0865c477a5e3ccfd98a8a4abc9b525351c9ecbaceea978daad8cb44b2556cd4c244b24f9dadd8c873ea2c2aa1b7f7c825e96f0720c755e07b955b984592b0acbd89cb5dde5517d9482cdb632cbf6f9f73fa574bd94bbde794ac4bd8eacb287e6e6116a14edd23cbda7b9c533f272ae196d66c7844022c6372534e5492c29844c90da41ca55de7799c472793f1f9e93032c639c718b796b4e368f3f46c4a137e4dad9b9c14e066307e0ebef607e42c63393ec79efac7465de5402f3fa432eab9b2e9b9d20ad628c42c63a4767e1711cbd84de803b06c3e2587fc24cbaf1cc72ef6732c7de9b75afef1d1d872ca46adeef315bb8f73ae4d3126188c01c418cf714e9fab3d9cd7de59367deca3a1acb15c35b2e9268cdf9629b7ab65ecb9557300967196716e570e58c6338f2d33afba3792100d21834548897f7e77a362ef2ca3b2a7ce710e8235a4df2813362c61bf2ce3caeced9158466bd9b458de6c1e401094b18c0e795ace6fd0e59e1253d146b411c96559b8ddf75d2d9b7ede73d095da25d2ddcf3b07c15d727f3eeffc739afafd9ce6af8cab2147cee56c7683a8fbb0cc4796d15a36217df357b3995c1968ee98b669c3c26203cd933cd0604989c0881ad65cf1d090012e1a57cbd73fa794524a5d89859e52417ad02959223552f9f313fcbe394330865eca64e1071b7d623f4fbdf4e7ca214fddc697e8a98b3ca3cebd26e9721bb2943de722590a3d579fe88364c9c173dea212171882b5e9e73b679189d4183a42b0c6739dbb48fa2d729ff448ca322434bf426a36937a015ee19ea973cfbb978b0e7b97afa8242692e57cd1c6575462e34a1c9225d287bea2122506c3a80f43b214fa8f65ae6a4396dfbb6a19c5bd285ee19e8973ea477bb3d96c5a68dff40fbdf4cf731f36b2f97ac622e7375ec6de73d70cc3e532366abd9c5d888d5ace4ec4466513d28fde029d7d24479685de223f9685fe224396857e43c678163a475296856e43c294887559e6a08b2c3f6f7d3fbbca91b4be983ac1399d58ef7d53cdc80e96abe6db29e5c872c97c921dc625f3e5f81f183f07818d5cce75bcab2ecba82bb5e72df289bec77de4c8b2c9d5a62533e502172ae6788efe24f7b78f40a7ce1eb693cb5456cd7395a96bc8b239b15acc654c2fa8734db19fdedfce9151fc4b30180308329a6273fa1910f47670722d2ae9269b9cce93b7805bcbf137ece95c476e609cde39535729cbf018d9c3dace9551f4a7efb402dc2567d568f37b1ba24acd97e1b31071fcf4908d989c318c7b13e28cf87b13a28b9ffef52ce12f4ff80b7c8fcce0ac1a5fbf587a94f909669cceb21d13fa88ae2f159d96cb715db7be9e67e9267d2445eb9649a31ce7619d61cc749d83758a41b365d23c77d53926cd96499b69e62d6b92d30c39b1c85986e346d05d31aee51fe7794739a7be2e6b6242cfa6cd5541109a555f7d82f4eb3bada4f4ac777eb1d2b332b34bd9aa91f5919410a5ce21852e2ab91ce4a08f3e6f75eeb918d66fec2329d025d58a791f49795cbb6a933f26c0fd29f9f6ddec94d6726b29fbe6762cc6725ebd10b62f578d1a3366d0a04993a69c655acac65fde52a18bde522e07bda53e97e2bcf3f61d5f75653db3f152c846acdff6ac440ac13a6ecfcabdb56a8c3d4b77d5cb79c5618d992a018e5aaa64342d5b9084689e6f91d13adf12a36979b9962d1f8dba962da3962cdecde634976bd922a3b55c4b15114dcb16245ae860dd2244036d7ccb109aa8a5ca87a6658bd36efc55b7c468a36ff9a1bdb46c91d182ac91e95a78b668fa16a755b07e43c2b668fa48e6ac91e92f32b643d36dc86f8ba68b64b846a687a4b746a68364b746a6bb48ba46a6b7486e8d4cffc8dda2e91e69b346a677a4b846a673e46b8d4ca7e4cd1a99de24ce1a99cee4b846a64f8d937b003bea5b288d139adc94f3be968bab1ad2dd2dfbd9e11aeb990f900a82d0a693d180fb5372568c2517e6cb485511d1a84bb567a15539b4be55118da1a267d9309eb8434c15118d73a92c5e16eaec5c983ecae265a195cb00685da5a4a4b80cc0e332009a58a56a414cc1a555a54ea062a2aa882c8e584393e232001a57a5a6dc0073c697306662a065f13c8b97c5d3b225a47540fcab82d15a1dd755e02e7a8586fbe11406e35c072b88e360512b5abd459712c203d22b933d0e52bfc59cc73d766b5188fff8f0711f395c05a35131cf9db5e336ecfc53a5441752bd56a991ac524a20e83d558ade78aa148d55a9f6dab71f556a6130d7a95214abb182546a7513735795a215544a81d42d43689fb77c6c7d3e554a080f78bcc7a5c41fce2ebfb972b9d4a7e3ad9e2a253a4f7dd233761f552aa74a0955a92d43689e87558a8ae92c313b95b31c4ba88b893f6a9733ba14bf5c8adda6ad9eadb06c3faa54e73e2a4e95b2d233f69b2a35afaad455cff88ad12c9aac2b0ed6e55429213cb8711c976ab9541511cdc5dd70aa54cb6fea1cabd47c55a96955a5daaa671c56c6b24d311bc613775851b055a5b46c0969ae4aab8868b44ab5ea44d355a98955a5d856f9363b3caab0a30b24b4c032b1e697106cc08d327ec8e24df115471401c31779688046ebcccabab2c2a2376abbf51515d35962b25a4c57b92f68fa0acd155739abb6a25e3296b766cd1e6694918506ac68e5aaf8364940c61921c8a1cc19b4a53518192063092e5cc8415b5a57ce36b39e7d5e314e2f391b87f52b64aad626fb8061a279f6d9be6a16eb77d69274da39e74fd9ede795d3a5b594752d658fc68b9896abf1cfcbc5fabce5a5ecd755f22d0d2c0b87d5e4947d330656f9cbcdc19838bed0f055cf98394ccf580cd6f3989eb1992f67d6623dadb2b2f9125eb9aeb26e46353726a0b9b9b1a5b9a9baa9cabab1bab152337ee9190751f525ad2aa38717349d1bac9b9b1c3339398ee33e7c549d2a631d9dba1cc0603855090727a722e5789671e6dc54a1e9f36a5e95e199e69b685c4e7533cd78be34aa9b89e59148f61867f57c9619613858b38c192caa2ca3df54cff11caa1bcf32c22a0a39f5e6a6ce3450b5e75b99e9e5bc2af3fcaae528d6323682b574572d63ad5acafeaba5b37b24df560d1a35612ca1830d6dd0c183d4c24195c5cc18693c4193e23beaf0c1c6c5c6019a547731460437d8ee688226c565b6c461870a72aca1717093a64a0f27b2acbe501bbda261a89883260322cd97b40acdb3f34d56a68f84b0fa08c94c1f29319a3ee2a067ecb76797ddc4f46cc774b5fc6e5c2dc7323ddba7b58c290581d5b37d31368c03c5edd7a77268377e33060736460cccc5f48c711c965351b8a9f40b0ebd9a586a5e63d5100e9a1dff9126c76f6e2a0a3caee3b057a5caf272aa988fdff88df3d8e3e3f1f0f47cbe7d9453739ca7e5db473b75fad26215859b9c8ac2e82e9f5633745a158a4e6da28db79a9ea9613559dc37eff31ef3b5bcc5b45cde615c2e24d433f6efe334dd68a873dedce8595ea02f8daaeb1c24e99789f5c5cb8935e64bb6650975c0eacbefead9594d1f71989eb17f36b5fc46b196632cac658cab1a066bb96a7e7ac961a62dccaf99b73d87995833ccc49a61a69a325f26d697ab2f5fb2ca3c673d632d561f8d2f33398e134383e33f7ea4b9711f3e1c85d17574bca2f072589582393bfdd24752b1daa467d373aefa48ea87999f8ee3523ed0fcf41b97d249f3d347978265fdf497b30bf5514e95f56c3a8ed04f77d5f2bb41fae9ad5a8ee3eda77f42afba3d2bb7cc4ff784ba5a7e5c2d475acbd897df772dfdcbef6d6a22dfb401ee4f49b39e1ddc3e5293f5eb6926ad5c33bffe9193b6687e17ebcb3643c3f4ccf6558d56b12fbfebd4d6472da667ec7e15fb325a557ddfd8666849b19edb8c18ac3e5a9a193434cd330d2386d16c5657f5115516cea93cf7c2b4c0305760ebea8b5349550dd15a0e7eb1ea23aaceb3705eedaa8a42e75d6dabaee2315ff5ec6d66d17c69c6ac58abe9a32d93e63d636f33568c26cd17ab31cfce567dd4cc5f9ec73613a3b667b682622c5bcc5c9ac7be1cbfa46196366f334d9a8ecb4261dec44c3565c6d8b06e3d63aeea2333657ac652284c343d636fac9e7149c33c7bd9629e69983ee220ac50a30d1b5ac882c64ec5f451cf80471e3f00b1c31434763aa68fe69615cc58a3660d1934767aeb232357d40082355bc8d0d869993e9a4adc1143d50960b0d1f8cbb3975ffa486a5ef551cb993d67ced9adf491d4540e8ddd843e72550ef4f8c2d647addaa467cc3eabfaa882e0ecd3aa8fa4aa86685f45c1ab4f50e0aa949e316f9967256d6683d8dbccb377f76c2f65cfcdfe35ad1b3b5a189bb41caf0a69b19dcdddd9a45ead79a4a3ada446eaa2deb6ef1bdb38dbba530f590c78f3af4ab5edc6b65795fadc43f3cd7997f5edd4a5c22ac5592cc68ad0d8b2b9fafe1c1c5d58df225b7d87236349b195d4c492da69d5448aafc01b46d371556a66f914f3da5bd8b6d68d6db65b28d4ca129325a6e51ea301475f1ae8255f89ae79c57939da78b9e3abba4dfdb0c4b0c6c03ab255ab8e5f8d614de5d0bc4aebaaf76cd6a5d1ba3deb576db28b0b707fca99f5b9e7eb631f75be1eeb23ced7bd8fa8c8b5ac71a45392ba474ae9f8474afdf0a91c9a962d228dba94101e84eeb954077ae75256943a67e3f4f689feb9d4f6ec8a6bb9cba584f060f4d6a2e17ccb7c3987f5edca711d9712c2031ccf41e3ba71974b6d964e95d2b245f4e13fb4540d80a6e35b264d4a080f60aee3523faa948fba65d274da6175da7ad69e53a5269a9eb5e3d42d93e6aa524237d54acfda5b55ea49cfdac7ba65d2b82a35b17ad6feaa2e6553b74cda57a5c43aafa4c2ba65d2ba2ad5a467ed609d563de3b0a8d5772377d5f272de2856e78b46c8db2c9797138d8bf3a5755eeead55a77268d497e6f9e74bfbbc9c62be3db223c79e4de7c858cfa653d27b3627ac36d9c302f7a7f4b2befdeba372a45ec64a2f6548f6189f38ef7cdd65e3dd6ab92a0a22e8ec7281158556e8d4b78fc28a82ebe5d3b78fa842247b8c4ef3bd321bd4be34aad0a5aa8668af8a0268535108bd4e4da7a68fc44a258ab553d3b3f6b05285a1e7b38c574b072b1508cedbac65cc55a95c9ee5732a57dd313d6ba7b51cbfecd4b42a55cbb37c15852c730d4d08ea4ba36ad529a6679e0dc91ee353e7ecf336729832ddde80b8bdb7771dd60c13a68fbe2af589e92371f5c01ad347e177eb23706c79fb2cd347ae564521f655140690456be748247b8cd4c1ade574a28e648f5183dda032a8642fbd32f389713e3196de55a766cdcc30fca557c62bd347334ccfdabf6f1c63b12fbd32df3d2bfdfd6dde9557c6bb5a36beecb03a351dd6912f3b5b17a6b3716238339c18d897140d5745d1b8bea456744c49adb82ffbd669fa4bdf38abbf94c9e22b5b99307ca5264c39d5084d5bf9956b46a8fcbee76d8c629ccf5ece305ed6af90a9afc6b18d1770a7082d4ba3a1413649d5105a96ce83679354511a27a90a7d3649150cc826a9f2a9609354fd7839476859d8c6be65c2364995a7b349aa5e3e3649d58f1f9ba42a486c93545d40eb2ad579d5106d9368a90aa2656935365afb964edc24556cb349aa5caf4d5285336e922a9e9b4d5255713649150634ae529c570d759b444b168f56254463dfd2de26a9e2689da44a6c6d922a1dd726a90242eb245516d0ba4ab5570dcd4d52f5f126a91a7b9354c5689ca4ca6949aa64344ec2558afa540ecdf3b8ebbc1ae3b8ae8e5da5aa8668ed5cfd7a36bd6a88269543a3552a4bafa175dd9e4d9fcaa1550dd1b87a4f7593de8010337b4e71c97083d81b882b206a78a7716f3c78f992fe0231435878b1e546939a9a50a85165a309c13ee52ae30b4d881e266cfc3c5233020d31676c208f2b20e8e05f324e58638f2d779c200d37dea0adef08e0228ddfd3efcaf03bdf8eb2118c8d5adeb96fd27ed775738c5da78406ab67fbadd6f74afc9194655446fba87b638c1ba8c8b0210533df3945d3c6775d11697ce1820d304570f101da18df755fd6b84fddeb588c57009a460d6994f9e2bbbb9ef93c9ac585716f698c4943ccef2d8dabdf346a48230b02a8fc7cf9f3dbcda67ce60b1a3b9ca9a2cb4f1a4fb41207b5fcce205195c61922b6a471260c9734ba336bfcebf77606ab1b438d3cd6b01a1ad56043e7f7a68617b0df9b1a4d10c143f87b53230321f87b53c38b97461e6990e0671a68a4a185cbc64ff8652a903f80244488c60f1a488ef37b43c36ac67e6f67a8217f6f6794d1678cf1f9bd9d01e6a783dc195ec63c3a33e2f8f17b33430ccf0c32427e6f66e4007efe7b33b3e6a783ad30c8efcd0c9a9f0ebac225a346a8716f66b06018777e6f668ef8b9aed607f62c01bfbf6bf9f4dc3380cb9d379be33252e8bac658e6830757020a47930182aaaef3ae23e38c2aa5f91c4706558c655ce531bc9fdf1b1949901104d65704b83fb309d712f6d399722411cba6195b192afc8ab43aa545b422552132c615429ad387e0182bf8bd91c102195d3e5e420c31be40fa82fba8d79b821460f130061466b8bc415b32b07cd769f15de7423e2cc300982e6000a20b174400025ac7550fe373fddec6b0e3c65978cc678c9b185b8c51471630c6c8d202c618726401638c32defbbd8d8145b1e0829b948302848105061a6bfaa70b1378f9e9e20d15681794d230d03ca53f5d503166f8d6ef4d0c2f5f80b36a0ce2d7c570e2b746f1ef0659d060a6f76e50194f8eace7ba9bf26f6cbcf23d5d30c87cf93de7eb312124a53e721ae71c1c6093fa396f02c611cf79f89c6f5fd12817c7739e67f1eb5c992df3e590e9b376bd01cecf2c709e73b0ebbadda0d9d5ae6ed697a6afbe9d070beff7068618df35c90ee3c47a21224650e02478238e12cc0dda00a91d6cc706b52741750d1bd4ce28d8a0e6b84abf6c12fdb25cfc6e906fed2f1b74c588f3fd6522d96f6f1b3669bf198a3176a484856667d348092bcdcaf64b9bef99c4ef9738af8475c6ca2612252c35b6c8fe55d8461e5871fc2cf3ede0ab7dfe404edb7f8b1dcc322f32995774759a6b9c7e7e6af9e9c3cf2fbcfc2cf3e0b215e3debea8e3cbef8b20bef49ebf88c38b3cca4c28c61f36096a6af9f9c306ad2741f9407d785a6693ba96f3a1a8f715df3eb1d8a4727ed7596683d6b1d8a0f599c5264ddfa4ae4edf4fa75dc06d1adbcb6f6f5ec4f194522a055cfe61ea3d5c35e0ffdc73a2f5cec3ce471f19cfd6cb2423cfb6837de7a59743be731739f28cbd457e1ef5e2ccef18bfa2929fae798a005cfea5e0321771fc4af17be3a2030fbec0ed231e20bece81953ea2be3ebe0c1eb97c5d4a1f5de01bc4b7a791fae8e5eb4ffaa8fa8abea4afaf0bf511ccd741e8a31c5f20df1dfff17513fae887af10af31f2a702c089f810f7410aa9543f42dc02c721812ad50cc8656e43d64a4524c87d78489295ca82eaa4b7c80b2a55900f1fe21f19a452c92e700cdc237b2a5525fdc73b1248a5eae9bc499f4a758167a1aaf1905440bcf320d37582b88f77b5cb105a053e9254416a1700d0a86c3c888ff7384852f9d42e3a34aacf7d6a171b9a3b904ac5e3a3ef9054608feb388ca4ead17120fe22a9746a97a651c57c04e22f77bd1c7420b54b108d0ac781384752bd6a9700d0a8420f02d62e3e342ad07dbca77681d1a838eff11c920a00446a971d1a55cb7d3ca876f96854ed3d4e492a1fb50b0e8dca878f436a9749a3d271200e80daa5a351bd3c88ff20a930a85d7a68549df7cc6a971f1ad58e8f0ee4a2ac7679d1a8440fe217d42e48342acf7d1ca87671d1a8721c88df9054423c0b558d6641ed72018d8a7a8fd7da6546a3fae163a5aade65d25cce245516aa1aeda77611008d6af42041dc55a9749c0ae654394e75e3382ea47639a251b9dca756aacea938a7a24ec5deeee364edc234aa1b07e24e52b1f754aa20950a742a9753b59ccaf3cfbb4c5a8f574052c11c48a5ea32693e956a74aa9753d93855e8a2f7542a1ea7da71aa9853f9f01faed43ce40e29ebd97a8c6cd2b3f51fa4093d5bf7b1ae437e3d5b87913924083d5bc759bf591fc9b167eb2f12a967eb36e4939ead8be4acead97ab80e92b19eadbb48a59eadb748293d5bffc869b5de91deb3758ea4a4959ead3739bff46c9df7b605d6977bd3c2eaa3fa99a6c73ef641f35363892596405ae2c727e66b460b4378c1021b19f0e10320647121051fce88414b0e5984414a6185142d2b9470856a7041f42c99627a96302331726524c6f5dad77ed6da83dd7d4529fd32e7729c3357b90a8bae0ba0c401cb26119f0f2d4a29a59436a556acf0945af1e529e5aab0c3c60aaaae4837f175e745470658ae972573de39d8b56847b28b6b247e7086aba48990ccc535d91deb19e720d7a21cc91c757194ecafc9f79cdb33a62bfd3f9ce079eb57f480da6c1e4c9b8de6397bb0379a5739e8d9ba12f37c8a85c55548635d4322072dc69ce9020572fca08019a608e209335cd0a8a38a35ba0a10e80ce0f37a84fddeb068e2c1df1b16626e9c06b83f5cc458ae99ac3e12e23f5eb2159005cece6358d633eef101227376fed24717383b5ff511e9ec1ca68faab3b3983e729f693a2fa715e7e5fc323adf38e338e738c39c7f784c07843e7a39fb933eb27176297d24eef0f870dedb22811790415c4656e0e00c81909326238138908a0250ad1790163800dc410e8190aec4401cbc8084c11c5651f0d1d1c9e989c570c8495b51c90f3f1c875c5a0e2973187981afa8c4e005a4587b3eb8c045172b0a377e41edf9c00700dc878cb4a93d1fc8fc02bfa0a230baacf67cc0e33297551466cee34b5bda8b9c3470863fec7893972bf14c880b71009052427c3049804416361a063e73a929995fe018ccea94ac2e17951cc44f4761ea826aeb332390c146fbf19f3040e4546301c93f644fe83d1fb8fb4fed01bde703d7f19fdae3f29e0fdc87ff5421644f589b90def3815717527bc0dae487f77ce03117527b5cb5c98ef77ce03c2ea456557262f56c3a49ce323d9beee434d3b3e941c879ebd9f40ac839a667d38190534ccfa6fb90334ccfa6f790f3aa67134dcfe6cc1a3bd9f3903b648c34a167d37f90567a36dd07a9434e5bcfa6c34829e4131c128491e4c04654ea23a146ea2035905d5080df3af80af313674510a43c1fbb1b11051c177d695de879e84bbbc1f1a6989b39e6a777f3a6f3c395fce0e3a6c371d1f350f4500c6d26d6741cb6fd74b135cbfcf4cf4c169a344fed62e8398e7bbe34d141e8f900c7733ca7a2d0398e2f2df40df629cc736e1cd6790e082c9b0e237ba8c39ed8a1e63b3d3981f0f2cf4170b98d2bf9a1e54e1f48850efacb3f90127d74106edcf32982d0398e37d1e2390ee89ee3d41ece7fd4eeeaa7df8479ae36f1a1536d30f2cb4fcf217b686d52f5d3ad9abf907cd5b3e920934f4e50f39d9c16b034cf17b0341c7702a1e70397bffc5551a0790e42cf072fefbcab2888fef2a5e1f8067be39d8337be2208a2773c1f87fc6ec81e76cf6b34b017b0342d2e7f798753fc207497b30882e82f6f10407755aeead9f457ed69b77191e4a067d343727bc6563d5d9bb4be2af3c81eae4d847a36bd469bc2a951fe8800979f7082892698b0a28ef79034516669535a953868b2007b850f46b881841c586031070da38c1968e680420d140441a357d0f0945e91c3d39da702784a29a594d2fe01230524a0018b14742083b6a18716476859638d0f1fa0512bec781a3ebd22cbd39ca701784a6937b13e7e6f64c27c1ae0646e8ba38fc644d09f1f4afab0cc8616f88789afc9b4f0ed85d60ee31f262a074190b17d197bca3115753cf77ba3628e2f85e80d091224489ee8f0984b3dea3c4473dd1c19ad987364401907dd751558df557891e4bb5da8964d485c131fe7d15993f840470b7150c1250d99309ea842081f4c2046961c768ca1c20c15653af65200a7cd89214270c3aad2920609a509008d0c7821850b5a6051066d7d97aa38820c1444083105144c3ca1c4ec4010484c0e007104910546e6b922c3ac61ac80fff7e00b4d9329e4c7023461c7a29456252a2a8cf872b19a78158b524a05114f29a53e692d325231c4b3bf122bf10f0fc53176bf372ac2a0a150b4a670828a2f2cc5996729c47866de329642426ce4394f073972fcc8ef85d1e5fbe5f72e6f39272ee1dc554ba666a23e99da3b063951c9d7a2e2016a2bb6bcc5259fb76af931b5833cc5f68f64a23edf2349da91e1c87db7313f3f3250fa4305a90909062a48cb463d419e24388c5d7b187afe393b0ff596835f0ca3e7fbcc7dbf7df4d1be0eaecc9f955ee1beab4cd4b93a99579042ccbfbe8fdc77cfeb9cd6929c7fcbfa0e3f92c178e47eccd56950526f39e730cf5db56749e72defbeae3279cee212cfbf24388c5e65f2c2259e77de2202baea7dd5ab4fac7d003b8fadbb14574f9d93c22a0a331eec18e7b0986b50561f96852cebff58d60bde2c204b1c3556993a67ff1c966487b1f392738e097b5799b8eab5bd2f63dc71de4dbbfbbe6d030a2c3280c5195daea0c20122f8d2431a458461871734ef04de910bad8ea3cd350a32fffddea2f8825e457144143dfcf490c18dd19b0568d1f2dfcd02b238c1b27d9043cade43e9b76ca30f51d64fd84f2c286ef8d29f50f3e590b76022fd34f36592a7cd60f3b36fc0cf20cf651baecc65ccf9bd39b1e69578f21a45fffd7a13a43ea2b45d2e86c0e18529c678a10c3468ab85102f6c91020b72888086c3b81f0b4d605535b185964fc8efad090e340186a338b7d96c511c12b7e0c58fcf183ad8f8e1eedd169899f97beefe25383d6011c34f9f67dc96c06a42062aeae32cd332ba7e6f4da8f06b56f8b162420d971f2b26e6e8f2634583fcde98c0fa9c273c0f7c4a1c14a08b8f0c3a84e44290102748a14c16aa1768d82e7088018b3437ace1001267886ed81021b9f0c5c6df1b13429cb1392021d5a000d342078c480209aa10d48270238b092ca2c0a14c07baf8c8a043482e2c194d9031441d6770d8c20a09b8e1461d37b8616606369ce0a4108f20b920e63d2417bef0f7c6840e2768c5dc87892f5d38e6319f581f2931c1c20a5f6582cb969688c7c75d0c47bbb332c8dca3d81e292de75e261142e697c6d888dba18c7b53e2ea6f7e6f4904f12b72aec47c35c67c3c9f3f5f7a5589838e2c00cbe69a14684a1c14e028363da79c85e6fdc436d65b8978369d2385dc878cf5d10fcfa6330ea312077d346d369bad8a36e76dfad008c6fdbd2991c7fff8bd2d1182a7bfb725d6f8c965801d5cd9bcf958e65e0ee91b18cb7aaabebd87ab1af073beb72e7a78ea4a7dd437760e3c78d2f4e559a88ed7426f5f866f23c4dec6a0f9db1831ffe5a679665ab96a16a0c50cb9002c60c80560b962d9bed0ac7a3622f46558fbae3d4ce6b72f404bed7feb0f9d2bf1f43e1abbba3d1b59c6370bc87266d90a0d08524a414a3b57e7ea5c9dcb455d61975476eeb95273d9de39d8ea662ee753572d6bd92ed579b743d59e3f26d6805d9376994f50cfabe557fabc0763dfa47ecf975a8d9ecfb167d43fa3aec6820b2770a30b1075c861076deca32d811966505185135c8c6983e6f92a91c47befb9b35191f75cd64794e6b950574796516717c6fdb18f3e1aad7e05d8dddd5eb7d7852c5b4e9c5cb75fed76ef5b5d73f2fe9b7acf5954f2d5b03d9023dbc1f6ce39b2ddeb38e7ce3b36a26cc4b909df1cc7b183dd91ec9ea884b24fb1ebcfd988c9ee06b06a4edf65dff649eecfc94d7189f7de926cd5753ef5fbebd35774ce27c95e7e3acfe91c37274f8e9d921f5d4e0360b94e29a594b26c7da71d60397fd38c25fdf9d41e459ff3ad3d4b76d99b96cc539f3b97235ddff2ca30c64db92ee426cff1c6ab8dd18664b1430a72aeaee585637bd731f4f93236e9f7e5c8327efa21cb98d2df350a611ea0d64e9dd6927e57b68dd36bffc3ae36c06523fec2918f465f5a8b64f748eaee52c2ded280f965d37e43fec494fa45da902d4eac2e722437490d63e7ad0d3a675149abcef6bacf458e20255dceadf0878d5cb58ccdb01c9b9ce29a0dd33d7ee81963f9f3ecb202707080a618fb4fcc872343148ca5ec59c646cc5d47969919658c07b87dd47235bbb1dfaf964db2a74e2ecfb8e99ef7757b5e4f35d32f9d73ce7907668b1697501a66118ef326679192caf0ec5354c255a6611661a7fb733a8b4a3866925a99f09c33d9f40bcb6acf75fb8afd5196cde9ceb2e971f78122ce88b93df86a34a698ff3e67f7deb53ef594fb3e6f55ef2339075b9845d6bdbab388e71cc95e9e732a2ad9ca55eef93e25f74df8de632b96714aede43de9d124bc2061fb2972aec474837dea1cc96138521dc05d234a9b91b097cbbfcbbf3e9de9697e127bb9938ce235622fd77777d769dde5b9deb3b9706ab2a444794faf78cfc41a9801822092d037611967869b73ce39e79c6b66d99000cbb8aa013713d759f3662f91f5447e6f47dc1e7ccdce39b28cb1f8b96ca4bcf7b53c977fde22cb980d39bf8f5e0ebec8e59957cbef3db1e55e4f1882ee72d025e53dffb8c997eb4db1b23682dc3e88f19a768c1301f327190e29d8aae35b6e041b0f80df9b115f9ec99cffc36ddca6f4e13b1ee361c271188843e6f80dc9d3718ca87a1c17d578204d7ba0eadb03b6ef06bb70c52539ce5782fc0be638a1e3dcdcbc70701cc773c872f47503f8f02ec421717cc504ccf7e14a8ce3379d88801c87390e594a791cef4404dc387fdfe8e5f838be7cd4133a9710e4c7b227ac06a8fd587b5ece14fae8afd0470feb38fab22caca5941feb4b7998e394a37738ce891cccf7e130b284794e4db2c308ab1df8709ccae4c3c3ca243a8e2bf1483259c26a53ec7dd41bd1d779c8927ecc5b64222e112b65994d75ce716ab953976531a72c8b3102b896f37f20e0c70fb29c1297b0df3878432ee977894c4a1cbf79f2b07621a064c7f19bdaffa396287ca18b649c0aab25e751fd2e2f29cb6e5ca7ea38c7e2127e1d77913ab59cefba71ca46a1dfd4f2c66fc8243b8c51fd2ee7fcc641326abecba7a8e4c665c7f8790f93f9383e1fc7b9daf8ac3dae0ffd08314768610ac525aec7d9d05baec42d12c4215fd35b1e30f32d0f1d5c900b577cd5b2f62f1fc932f6a3c3c852cac360ffc3633737ceb1ea43e6a396e3df78ce8d2f1ff5b80f1fb5fcfea6963a0ef31cef21976739b5fc9e6b29aec3c8ce75c8b229f6382efa8bbc321fc7a9b8841fc7a7c864e278ab96a1e3380ef99aa2016aabc438243f8b1ae0d4723e8ebf7c24739c4998df9065ec6fbc47676287f9c80e7376581d3da7deb852e390fc376429baf72fc721f97b709ce9c6d9716a921ac69e2537ce7e53b9daf8586d96f0bf2a53e8626d8a7d585bbeaed420397a6449544af9f5169570d3f64a6853ecaf4c9d18bf861023ca9300b83f5f944dabc63f72493a0121d09bdddc208e8652f4bb290d3fa3fe7ea66e2ea4fc257d4f023f2568fa79eadd04bf900e4f544243dabd9f8d95318b3efde1dcebbcf43ccfa35dd90ca2f10d7ae27444715774775dd72d772fef8705c65866d69225dd92d2f0e316b0908490907ea472c6b19652c61b980fb2003cd3a95c9765383e2c1be76b7c91b158cc7fb07298de3c2f2a0831efd5c2bc4765d33c93e79f979b66d3203d09e1a5e078d9644500554ef3e16514cc4b1372bc94fa1b5f1d076be9c101402f9bfcbaac8f746a2d3f91fcc187e7c0c8a1acb1e58afdd0113dc79d659eb3e3903487f47191483152f6430867286b0ca4f8a8442a144ba4899e04a153088dd0cc06010dc31460404028168d07240251d5d40714800e9cb64e54a14ae424c8711032c818630c310600008008c0c088b4011baf7c02a02fa1fbe1b4d32f99d7b62afa7fc898fe216df96c1755fda153a462695579f9549042bf752c4cdf1a464bcf8197f6a81e2f02f64c79f312f8f6b33778daf93e9f1525e570f7b648736655a8c5500a32b7c94479e040faf4b24159a64483487f20872d03693850197c8bd432289043476c5404ef57d1af89a2615e4b70bd7ab5a20f3341842816368b8b1adba3fe817f8e13f592d5b9e0c1fb251eadaa7cf96ead5a1ed3fe2061230dba61a80703c0513be8426d6778c535a744b8e2dae994a3195a018ee2da7edc753a170744dd57c4487d7378aec6701581d14c6f1b4af5481745494383a65bb700b770b8de3008c1fe13f3bd73b40a4228de0ac1fb0b029ec83a31b1a022562a5af60580c774a0ba5e183d165e8c8b6eeebf47b58352a943542e8f4ce7567aa111e6a56a5fa17313ab8184f43bd68ecb2e2a2b3851eb84a3084cffe90c135656238de56f5b4da7e5dde85ceffd2357ed623497f49f634218c25bce0ddc478a3f32c5992c25d8ef90350f10757008ba510da1bc3656d52d9cdc20708a953a006810f8a2084f8674832cd98b1aa35d87a3a8c15e4b4c6170b062d3e7425f3263eef65e9a0dbdbc6e61b71e2d87ad16019d1d1db6edcc590bc39eccdceca528aa2a69a8b6d0deaad868162b44c7432db57a93ed6bf94d3c628d032f9d5723c56fbec400a8cadf00dd6b12457c8a6c5fba3195026be1c57c76282c03e0986a83473d40c827baeb3422619c14b5009fd7ef3c2d02443b5cb97e12ff8fd7706ef009b8994fc01bd4c7bffb7322d19f3f3fa8e19d71b09be2770a68e1c10ea417b05f3f8aefb701440aac78792752d7e46f4e72f7db68f6f4040e8065681c6d2c16adf482ac4254a78c59759f2c6ca89de73a5a991252d5e87f86e3448c60aa80b39934c04a24f5debebbc791d45ba714e9233573b9d69749417c471996c178a9ddf1b74ab541d4bf5547d832c24a5d9458d26dfbea58886bd8e0a2a40c1bd4f016577e7fe1dd5645e87e801edc50cec969d834ebc5d1c513ce2db67b15396c7b59dca586d80fc4de8f94b2a13fd22fb17fde9cf5ab55a63c1583fefacca68abf01389f6a236a6aa385fa5f9adc4c3ae28b4eaaab6295d66bd4d71833a564dfd76fff1f9cd46951654d3be5ff9fcca22bc384d8d30858775c75b1accc89d9a9402279dcd0a6ea12ae4c3744eafaf6cb3f4aeeee2f03440eb30217919845b215177db5e8c30f642f484ed0090688382a27e7f7c48a85478904644f79a06833465c8f10baa982f8a4068bfab79a5fe28c38f18de4b041dd5d1d5ae934abef2fe697b54fa2512c874c30e494f9eb3550f64880d9acbff4eb88b4ae27c1064c5cb90dafbe6f9660b2863e8f0463393cabe89d4cc59395a89dbae72c9f7b854b039d0013b5b0c2e643047e26968c3f4ee89803293e6b5f4a24f2b30fbe708bbc2678d55a642c2a5d47a50a1f76fe9e4385a403550622038524edeee8cecf77b1e3bbe80f9541f95b2ec95138819f61a77394a3a727417e468261ba66e6e887b76a9b4343ab9117b9d8a21bf01c898441234329d92eb68eeecdf7fa955e342051e9f72bfe5508fe19f4be0e6eb0196494f65077fc3997232a80f429f2c3f2145fe8a780fddf20dcccb5b4648f4676917ceb2afb60e7947d0d2b0c5792c484a2e8144452259d46753fdcf00d7cc3d56863e399adfa866f04dc0ba07700bd01a2f781c98379d376a2c89b212f1f4bf9e19e84f00c143b2775095549d5599a986eb080a62f7c16b772006fd63d18e2d8a26957648530f3ceb30b785eec719d854b868bb803757a8286d8ef1ed0ffb66b24b7ba60767141007c3788b0504b16c9a28dd9f7adbea23ee54fe8bd15c9fc899681b6ba72d34b9e84012e20e30e32183df021da4fb48e714b6b8d0a9546d226fb2c5878b9fc22cd77e97be8a5d60e5750d72f07262f3e831d8bf19d8594367203c5144f3d07015cd1aa15abafdfc1e694ecaf5516108f59f23f8069cbf5d64b206ddce2322622894838454f8511c4460dd919a62b877987766a50a8cdfa7e976d932de014665d46c53796e6652d1aea77a325473b20ff1c80836a13d56fa16bb47fd7ea0cebf8119207ac18d5500f2f7b01f080f5cb76a79a1f36b0992fabe957f1f500ee113939e61d06c501a8fcf371adef04422891b6f9c7a80bc0f30af3638af7302217e1ee585dc6f352b0c81c449bc49df60a8ac754c1202fb6c1701b21173e9ae400473fd8980a2d3c6b23b1c4d203bd2b3502a2eb9923ac97d346364844dac3c8fd78720d13f70eebb4993d266f99c002c9b0584c960f518e4c18071b67d2bd0eb12af8e0c78736def06c39e71b0adf80da0b68d1ebddd0a706e07f207ad183e167e734b64706c752b9eea98a012732bd87507a414864a71f7e0fcbc3e329e08da9f5c263424b6d06012b46646fa1787413370ef74e7a1f7a7f09835e241858b26ee1914cc4d08b34285aaceeb75737a1a2e2cf7e9960998fe03f9fa2875ce96b2f85e7a6840d2d53ef4760e3e680be703145b0db25bbd8bd3934de5cfbb328e3ae04d5c6e0653b061ff4f7580107c1b5f98289c3d7af68f822aa201f61c03b99aebbc55b730553a5f63d92443b6a41ebf1b935f6c08f23bf3bb0680f711e29f306444ffca262fb740a5059a784426b92f2599f2ed2548204ede3ededdd980f62405f01f4ec935fdcd644c56cfda65d02bf9e87000d4c28138e4ac4540e181b4c19c0b6e5dce1eb9a26731f47b49f957fc3ff79ae72c8be7b66e9c396f8eaa4f49d344afaa00e90a242d730c734649cabb26ab2159580f3728a20c45343361ed152f6892a372771d12c40b6022809a50b032c2447b51ea89cf2094be1b8a4845aea263e9732c8add004aa09113d26a6755c161371cbff143bae25a63af1ef331a0423ac5e17c180d431e457c6dde6f5eb202ead50c4b6a914005f22ddb099e4c23270f2a9a6219453430c2ad048b16d4d87f388e2fec1391351709b62c2c34e0fd2eeb064a6cc37b7829705ab313da5fe80037595e77caf14f6f9d5de8d84b67270634b91528552b4f740331b194a8cbce623fb50394f232cb5ec456dcbfeeb0965fac77d3d823dd7827de0067b6b69202c0695e4db2d6b8d1d100198ca08b073668b98aeb65902b759c66eb30cdf668de06671ee092e6bb67b87a0fda076cf4515663715766714bb5f27d9dd824440acf1c3d37f24d611e1812eda5c2fe77114e149dad5b6a8a19280e4375d7ad4e7a89ac2ea45703150483a85ca69381a511a61330c1bcf1893d55c33ec222e81f0c30ef962d715c746e86eebbcb42473c8e1426381a6d1a6e4582a0de68a49a76a91c39e187ac08bfaf2acea53df28c67ae5b43f61cb05c97f0fde26e6fe19a7c3ed2738b91a542a6eb5185bb35a0deb915f9727a7d81cf9d6593dc2c3f92b72d540588d7691df35895fec4007a3c4d7e37f9a3c955d4eb149adee33d865671a6e3a373a132095d1b79dc04617e88f2330b5b3200c949aa80a7857edaf5678cc5269a2857755159a2ec8bbe02c1d897957a5eed5745669500173de88e7620a20668d5bae6295e763e1d53d62b23112c85dfb116e4386d9b52c203a018e4c6c9dc99c43bb2bd82bf65c4926e0442626e8a0fd60cad45fc94d57cc4a67fdfdb773dfd6cf1928d2e6e5f3b14d2fd795db2c50a35d3192b8b79212454eaa472a6982a88b76b53cd9671bdfe5f3102a31af56c96a292c078ca0c0e42ea7ab32f8d5081e9e8c030ecf3edd201977885ea48ea3ab135a62458b5df9a1a660f49a80f63b1a949edf0d467a7dd7fc63c42c6e532464332f5a45ebe3f7919a17f98cd1e1c4e8afe682250248a81f8dbbcce37b971874807b834f483d4cc5c2ab3b0bfcd627acd5a4d0323268600bd28bd34fa50649ab71904e71a68252cb800795384628c5e11ba9f690a8c042e6588aa0319c6dedd1ee8b9828892ab5d6c8dca33825caec0cea2dd5db37b7a9364da678a704cb7ccfad9cf1cd596a64d44dfb5d9e2dda4836ed27051b2c16814fc7c7f90c6d4bc465c2d52bed066068c6064eb5907380938e0543c781e86c16e9bf55af1dfaaff97c6072d3e9fe57fe73b0c40fc913ae712af8a8f9486154064679f00f46801e259a4a00a0d0873956fb26f4b5164bae6cceede07bd895fb666bbce376e387b3d36e9dc50c7ea4a36268c3e5d087d39e3644310575b0a727042574a57c62ac7cc65b874810f8577b63a513fc4da0a9258881c114fe2842db24749256910d4c99610558a48188f271e1866f700cc6ca0cceff3666dea683b21c6090c29db9203299381956bc68b5198414dbff80985903c3bc0b2838076d8a93bdfe9adaa0d2e9f0da6133cfc62404a9560c75b2c736be45c206bd56a98beb06564f6f77af14e2bae6f009c7371eca13a5bf4114d5450fce119ab3572d6040dc28761a9ab8ca399f67a380a0cc4f78862fd6f81efd5689732a49ce54df24731c462ed9a1855966220d380b32bebb2d53f55d85c98e4b6d46261820f6a4ddf634e9f46e02d66d01a145b6b918746324175159f3eebe5e128c8699ff96c92205cfea63b74476705c13d934aba3bc7cc6bc1cc6262d6ff17f94718c08e2b2767ec6e962450833d4c882d3cd8b8f286c7b1c45bdc74d92a3ce9303a47c4679ec1c8530079eaa354db0909015ada4229ba8a98866da128e95e490d9ff97fcc0c4bca26fda53e5c5d109a6a1103b4640b04f5c7526c98bbb15339de2092f4c6c3db0f29338c562764100491993ea7c6b1f1fd4dbfa304cd1af1be83de83a684e714290f5ca2f7787b003933e8b0e922284e34eee299c651f1d451a359369a7c85578123c198d22e35070abddd6c1156081fd6159519adc90c45f2cbaaae89b56a1188750c9b766a1fdeaf8f1619b53e0af943e93bb8f87ce8892805fd203ffbc2a93aaaef12818436bd5cace61eb43da626fece284452e059dab60ba9c10099d3d324a144c41f5a74e0cad520acb355f7551ee6171276e1ba76ae882f8ca50c23caaf4c2eb312da84c367b0540f4322ebc1f93e8d455dbfaa17e9829346e051066e135cb7d4bf14e697707dd18881a12b370975be93be3142da9219906bbed24a6f18a860560260abf0b22a6ceb71b50239196921643bdee33a3f308c55d282557dcc2983aa44882d1da6597e22aa6e4e865954459dd8873020c6dbc4a65d5da57251bb177f340796bd8da63947c6ee219b58daff93bd53103c57144ecba24fa10d72c7f8df442a84d97fbf44043319f091581e010529b2178920148ddf522c90c99a48aa1625544068c9134bfed707d9152538568a5b2ac296d8a2fd3c90f6e876a02585a2a9d265258cd83c631db8638981ffe150fbaf5b1aeeeb346877d925179dacfea07e7cd3a05cccb311465b66f131fe2ad251f0b4dbed1b87105b0fb46aa16f90a53c294b06acc2cf6d4a34dfc928665261ab77c00be0222362d8baac6dc6a40d7bca78e697390844f779d2c7c81e995ae7eaed4e7bc8cf365458d2a6d60f01c9563e9d48822952ef92223c1b7959e204e95b93e85699aba5083cb8cbf287549c65573280a1abedd45df52f9ac45862b8e1775645d4ab1795f51f7562dac283b58b140743b63783d19a629db97708e889d6f4dd0ffefe56baf939860ff9ed02f33fc2c317da209c0ac21b9ca57c4b7ef67269747dc46a5d9bdf49a291f61f59e02655bd61d2d758277ae74890b4f71e899e450b8a17eca5d642d48496981b5661f94379aac5f51e7420b7a0b621ea4c6687e63cdb8fb89d8002f352125851717137eb5d31e994d3fe3e8d044feefc96787e7d2155548532f92269afcf27efda70545134c0c4529646d45762c697c05a1b06250b68bc8d8ddd5caa031fd4d1fdb1aca5b6043b3d65aa835a3a82cc40d1031c08007c189d837ce74e4c711b0d100049f7ea907c35a396e93d8891a5b18f15ba30ea1ce1336898bc12248ca454d645e3193f1f17080c71782405215ac04ba485daa0ae3a284af7525835e32914aaf22b3673f60629409f1e891032e88c33aa31f4cba1a2d0e8ec67f0a5d3d97ffe6b060e807cf6cb5e516a7b1616431515657b4124fbdf9308b3da1766324fc63c9b62c86d1e80dda3206e78fc8df2431f8ef49f4ae0c3f4aac455ac22527f87ffcf6bd6b7e55a5b53f206c4120f808b37c23470e8299c571bbdb95cb96864bdabebea2930e42120bd8c64b927192b226748e408c181f308bb7667f9384c95ce6e0f0457ff3e1503b05ba4c6653619c012f2419791c0eb5534cd87f7d417fb32fd87703d70bdccff054db10c3df2d5f307f2a11bd5e0ad20e408144b60ed1aee124984c525f40bcb8016430faf36ef2c1fe9dd2392f3d06e9d8cde7dd0bb739a4ad44153e52c9c7033853fdceb77d106151175e2268b26a7b3dc86025c9722cb877538e2a104d54ac215bb1b9f823aeca42a796d1ab9739d09ad0cc1591cf0aed54a5e848e0ffeb6e654b3e08a18b81ee621f28cc05ebf700f525d53beb2bfed0aff83c3cc2de08dc58198cb3c2bf58ce6db1ee5d5ef010a806390455afaad3c9e144eadfcdf7073c04333f147a19a1e90aa913fce2be349183b242ee523cf19713a43f961a7f28bd58b1ad1e688db48c1c16856e3e6377b8a727f71f0c038ad05c201284b19fd012b54274e2c91141a0922154dcc4cc76406b2cb40580600905032db142d7bef036970a3de9ef3c43b2af511b2a69362a15cb4cbe6850d8a7a82c02b8e1b6c4a9d19a6bf22c907fc3e65be6f384ee9cc3cc8d12b338d0522c91de558ae2869d04995846dc69d68beda0b179be7f7b0efa887740c307ce38c2d5beb9990849af8529f2cb99a05c441f322b43f495682372cfee9ff25706e292e4b666e2c0313585d7cfb2c97019cea0949a1355c484ec5708b27539f0d2870be4edfcfd76149ff8bda09b6d555458cce62b7089d48b9fac02384f909658c9937f6694a6b637bf7fd8b66cc1b46d04eb1f610c5069cf747cd04c76139584adfe83a688aa8bc1eee59184b274002ac16c88a31dfd5059827516e1d446107001c5a4efd043565aeba5ecec433ecf7517c63ac965e94d2c684ba45185722f838cbbe7ac9b45491c900ebb3e41097467d1aa3e6388e324ca98b41a61747775c18c6ef35d36a44fa823f25bde957387213d0192a2da7c2c4987a36210baa224d423117432037e6ec3ebdf6427d05f1c1dc8390d40987b2dd60f6fca02544d2ef847d2ec17a1c8ba2fe91f1dacc4411300746ef95f720fe730e26dd37b851a8bc1a6f26bb9e121ca4f980d28922323f5ae7926e6a9b7d2285d43307e843b6d28bda5a1717f663dc9da4a8c675bae683ef53d5b205c17f2267def5f7c53143f9ca230dbc63b07a107fa59a635c2961364486352041e09536b418eb89653e513d0940952c1f508e8a940849d767dfe1d7366b176737977267285bb00e67c519f127eb19a67239dc64d481cf7988f681c43ea2493b27fc412f2037327c30f119cf6119842fb28a3ba6d95415ac83074d41aeadb419a33d945fefd902f6bda3734d1ad524fe3e24513891d2202641ea4f6ca62d2f51ae349597907f44ee910218f0349cea14bc7ef424d73c0dcd77a1dab61e426404bdc28703071c68512b4753c32573c593ee3bc0a0b1af338de17d9a11eefee8a511a4a4c4c47040a44b3743b1ec3b9dac711943b8c272791089566bea429f56322e8604227d66e10fdd96d7f643053c68512b3d00f26a4d95382bf2e919623dcf9cfb6bad386036fd3a5d9c3953e1d03a0d0667447631eeb6e3e6eda4dcb8deec671afddbcdc75bbe961c8d71e4e7957c1f34dc0bea877b7370fef9761d9809d32cdd620abede66e456f32b8f75c15ee88f45472119d1bf7404468d1d920894640d6cdae5217e746d0b67f02b89e76480df1e22e6ab7f0f989e75b3f311c6405a42d0ba45a68d4083f144cec124fa26b22d3152fcd152d3b108a72faea2375748973e21c445edaf9c1fea33e5105ea9a488e353cfdeafe7ff2c0884983858c0af31c34848a6b013ca901fb186903ff36008f90de91c6456f0685022c39ec1400f35904808e934498a6b9eb8898c2a834cc0031332752102a2da97c3779f2eb6453a48008cbccce73b396c941cccaea538df7448cb1125a51fa343156972674d863d182b2fcd24932f26641cd8fac7be568b7967b72b4e8e09b60912ad23a77e30f86fe8c3ddc446f8ea34fa5fc8f3de4f935533f522b694a262368087efb3ead00b41bbc31707738b0a9ffacbd048af586d8360abe11d33df5ad8c767663364a0d6f19eb5144b525d532da304539d7d08f5485ed17cdd0524657addf6f4a89c90d2a8fa24ca7c33ec03f993710f4cb8f4cab643da394a26e966bafea06497c3630c7359aa323b1ab7e359dc74c51494a021cb73433f6f7cf20a35cacdb549a8f9d9a2b92425bc83cf952c6f39c54fee6e133441bec06ab36c161fdb49c0d873c11610c95cd8895761f3ba8e02a7c23ff59888d0050754252421cb43be5641d742477ef4db2e8193dfa1e6c370df547747ee4d718be5a5cd189ce747bb8ad1ade8fab820c46002940e94168b6b986718e2e358da9330f86db5327aaed9803424f6b887f415451f45b7b7101c7c29d3bb4e3e4e5013a1fbc8349bc86cbba571c2dffb583ad39cd469187bfb73dba622612700ef5db9247c02eae2fb0c2bc9b5b9e86a876bcea9e4418a55a3f23b6147aa1bbd06a9b7e71eda11f0c3df93254c6490b5b757d0e58b1518b1ac6e7b6375cc1fbe0cb1b6efe94a087e0acf1d344b3102f06291ba12e072a12275de15adc6b91c101ff71f2903356e87f14113f6a84db524aad5eb6a5dbd1e837f024c3c62ec701b6333ae17e57620f6852bde9d9a94570273254350229afeccd8f5fa13c7cace8665423e825e9b64c6b72201e03e26d631291838bda51bcd58441f79142116b536bc58a046275df4cd03952e6f301e163c03510acb7ac3c41d4a613841b7b5237924bee5d72383bc8b766dfe847dc83cc702013c36552d0913b766f98ef168a37d08f08c56edcd9005f6dfd07c920c4c63be3dd8d764fc1646f0c38c8352dc64dafd77894dd711b836d94bcc6af9aa30111a22e48b210a0ea7db0597ae49ef70d37f90dc76034170686c994c383c3d122e612d6b0c94d97a7e7c8c84df06519c081d89ab3f13e52c81d47e13d9e62c6889a597fba2792e6614851b06d500bb09bf87d24c9c6fb0602b1d074886357a5bd390409878b93169a1a909fe76e96fc2c27bad6395b647d26a0d2f458dda041a1df9d3d35ed519f12c1a421e9483b1a5dffd98a0057cab66c7e4bd8ef53df5aceaca96022d7a29ff31abdbb9945516bf228e21ca59ac18ce8a55ebe6fe722313b1d1465f542f8465f07210518c48190837e4b07ecdace3cf3b58531b2d9ca8bc0face6180cb762a56a1141e5198e264ab4815f1f51ebf66c59199869d2a85f7ee14bef8569145936259e77f47b3df611691d39a2ae844af00ea91bd1c5c3fd8f7147c3e46f6148fb0d7951410030560baafb1d5404ab219e8ec0c44f2815a3ca0cf2862b05918d2b777fa517b07e0e1548dc3b5d7f082321ce9fe86eb199496efa0a5dc3c9553bac8d9937975466862290e7f044856993ae88596df22d1caf2cdd98724e0fe637dd57d43433bd6b010a4cc599930d40e53b0feb6793a28b0ea33bf06809f292f10808f4866b58da8b455865b63edb06ff45f32838a9428a4ae6a79dce62acd946d49f43a52dc583fdf389c5958addacecc847cb13a608678439a85d97c39a6a87a07207ea13b50d128a7629d8b69866e2c9521130194ce6f5c84fa5f60b9441f15cbd4e22029394510f78d842a75010bd87faf40609f16a5f07142272f5c0e197a4a5e9729f93b184b31f12687c2c403143c1aae6daadbca1a202ba251de382a4e02d7d90f2154f40d1692dbdd9a012c9044a67cf1cce746500b98cd2b42b61bea8a40049958aaf58e5d0ab3ae17db0d725677f9283d1c8a2cab632edf3a5deb14ae78b86edf0108a2b7f64dc00faecd93367301957597ba94a24ca0bedcf5d6c36e5af656e078fbc9302d830b764720db1abfb76606710afd2875be909148801ce08dfc7f643799b3aca5b81692168338815d8f6125253f2981f8b9dd693a3e1577060e52c6408b531198835a000bc70b2b502b81b5d274828b23c66eaad03e161679ac5c83cdb77b36b17bbcef540d0a0d851a4e244a05bf8c45de0ad7d9f7afeb9556645d4374efae9f913a839d0f5b1dc8a0e1a0b65a3d8831354be6e336e9ecbd6989644b763ac632d84e739f9682dbde4c6458fc9b430156624e6bf838f2326fa15cc5160b75203d77168461d33dbb20ca0577c1bbd17ea447f9d7e12e2f3645311ca0b70b2a7744394af663df86293463144dca158de1a996f33c018e105b945ac325479e8608ba91b301105204ac08235ed0b1aa7598b69d5edee918791e8ea7d77537a356e4083acaf9dc7aa4128ec8072cdf72a33b0210bfc514f0549f22d335cbe8d0cfd07c06253738bb74c60835c58f8294fe22caae51bec904a7a744b2ae1c50823ee776b918e7294c351ccf110cb47eb765f4f4e0b6f0bd3714bb270f8176bcd88c3b3559b79113c10e36e24e39882081860b5556bccffea4468ad586ad43e32138962ed520393973118011ea72da5cdc10ee56701aacf59b95db19a84b7e24f20094371abbda7acffb6a9cbfbdd8fef07fedc5d7c34892ac2b475ec39053a5df21c8a59007695b25a9f741ea59085c16fb5fee68dde659ee4118df4913a9a47120c581387ac5502661d2a9d5ce8343bb825a7fecd96411da2f63c9765e740f33505f44eb435c984e61b242ac62b822e017e65808cb2828ba65baadce5a2d966fd4b84f212ebaa12267bbb95d197fde2ca7f36d39a1c8eab896e32c2fe381db3b39e9d73fdf85927344a7ff0dc0b9687f2fddf34516cbf4fb2c1bd07f3a300beff2f297652089bf83f232de3d256fa9951a5954d7ea2f653a2fc6e87762d3ea8578f1df64d5866d08d527916c821aa3397e6ee10bf9a5c09ea923c8b2e06dabf1a5ff0f37f80b6a558130d9af1af5c5d259df1b6a13f6d19a17a4fae6c4b79484c26e2e0e8fd6b9353ea84917e060c62b5827c556948d2d9d0bf5b38e10a5b8ff94a63b37ee7c0c3f5900867059ff83c993011164020ae9d40d1080a199fae10275c8860bd8a18a60a29aadcb0576c792cf9d3b42adb02d1702f1e806cda89b77a1a45c6c478812475a3750d84e14f26c4a109ad61ae0ea1dd4eebced3bfa2e54d998cac44eb87e56f8426582aafa8535fcc3415419ff4db51cd4d9019bd92030d49def76e801473763f33b23899d7885a1f0d0d15b0d8ce8d004bb4b5b8a1a5b76bbeadf6b66f01c4623b3e8d20a89b1f13036db2d5c414ff3ab66290511990c1b59e9b8fe4da158bf0eea57098322c0ad0c08cd0991d84c38b1e249257b23c54a2e5ca25876602977b9eb21a5a443403c59c49102cb7492272c6fb678dbc285130d6d9322c6bf57ee5b39899570c1124f5d034705d78046f116944f6d955221792cc43867778022675e92ae111871f503cc6c598c0ba3714a61ae8e046c2a64212addc1f971f308ae2e91f12fb70f8cc14846c19f1d28268ddd324094819620af220875d53b89421fd2025761d7655285a596f32b856031b839a76e3a491f5f2281c3fbaec1b351114e056c50dd0566ed6ba9988061930fe3ec222fc6a8bf4d5c63aec87c959f472e38276c6a54ebc94a60899507951181da3f49d4ad688b6aa1ec2b42bc398664cdfbcb7a44b83e726a3b4e3d4ca1c5fa46cf40ea3b8556561e563a16c2450fd6068a34f712f8943ed60d9666c25e4d296093cfec2922e21228d936a0d1760adf16163bef1f3318906d00147c33cbdb525a274d7883d3394308916445eca5a141cad84cf9d5ef6e6a01fe0b9649e00f517fe94b250e3273b8f7766c2c7f8eeca1c687647d816d9efc640dcf2ed240b6228330e080c1dc28bf85f3399a410efe99c8d1c36ae2f37ca335d3cce4f8b66542971f82bc327e7d3183f38596adef1c6058160073f4ad071034c3dce1bda361608751b76dcb9d5c06816225a7eaf941dd8b9e1de35da3afbe604e13a56a162f6e9fe0afcd6a2adb6121230b4f805abe394d8c122ab03ac24121b1071d33e5bde4e00d14d2d5f133bfbfb5dd2f16bd6ada7821af6948d55206f242bf6b2c0b29832f7965162e537e6e90393785c2232accfae34cc72223ccaca13e8f03b3bed01bdc01a24cddd19220557ca2f35f5bb9fe3faa387450cbd768919bb700d6355613fd0849c7350a02e25bf6821b02a08a0abd53a5f0f55e5c62716e6d0272b63679e334939a2a996d3a8f9f0e5a7efe7401eb40029926d510c66f8ea9214dfa93d9b94532b827fe722f8a242c7ca00d396bfb698a19209ddf157c09a058f11267ff3b63679ec77ff33272612b85f3a22c4891080bd5acaea6612da89eed4961d3d729263f2b1c2a223ca36f2a2abaa25b1b2554f29eba5232617ebb5b667535ec41d6c2ac566417ed9eacc5021126a2ab68e4b42f0e3e03732f3309aa426ee3b735a51c1044f6abd0ea578846a4008acb51f0c9740acb36f193dae9203dccec3845835f3a23f83ff13995b2a1d8b5288642d86c438f7b55290448e027155da101eac9cb3930c921722f757257ac98423a2e416f0f6188fa7cf60de063374a230186ebdf14c8d9748ed0844953d617a51d18afa89f145a5cbee8bf1c3d3971967cdbf1c8fed9565b27c13ef16965f308102f3bd17d6c22dc17e0042523aa25d4156c129a5c08ef8c2e1d7ac3b9f7180ebd1ab507ea46b779a1444067b5366ab8049465866588aa51e86e9509f875e52b0bc14caa2fddf014da58b812621e824664aa22453e71cafd1f8300a51129ab5477c2335c5371c9c0d512bb675ffdf53941b0ea691976dc0655c0f6f22dae7c99459e5b8b4fc2c477a508b942df87e2525f18b0a0bedb1d99192ae452164666f1a6f32e5a1ac5c06db2feaf87b6911da179dbd6a7f1ad28425eb668933c7cc4d8a4581fa5647e3e59fc4e9e3e4ee3374183ed760f24155fd1eadfe4a6333e77338ffdada383a01ef30dd33b9c2eb05b0780732f51c518c652528874b11101ab8055a4d09ce65d951b28f0a4f008aae09998f2cfdc52ec15ca9e53cf745b4a7ae0d43c43e7f5520600addcdfe7359f5ba2e87606c2a5165ac07124b8c1338edc6828c9243acbab14344e46ae8f6c14aa573cfcb76ed122de1023ff1b47b68c5fb1e76bd0d35193342953f22b2f263fab63f185cb32ae9d6501c5b019d03682ecc4ec6408a7ae7eff0064ed68c7241fe8cce7ca2a7f62ec84e8a0683a70f4623682b1cde07c0884a67871981de5c4d1c2d04275eb2154b8113e2bae438bf80aaab5078cae4fc4a883e5c59274c153923b2612b00a4b98fbf91370deee72a8f5cfcca4e2908e56cb2a22f3343cabf28a96aac9d66e783654861fd885ca4504eaea752b8999d40cb7718fb8248abe00300f7064f8c99c316ce225c1835030dbc04cc84e14b51d465e2530241ed554f3d1e7d2c4a6cd26afedc0183ed2ae9a6897063d16b84acd0660604567e9422ae446b72fb5a1a36c3faaca089a1835fa46a9ef8afc79e7be3952a6ca85af2480db54de196ec2d298d0e3c9327ef8413e1fbcb61119b08560f9abb76d2991a5e9f67b65ae674c3d6c7b018620910f2378f44a0ac124f501fcac6e2ebd05ce848ff4c859f6304987669a457c49bc33fd832a7d9e051edcefaddcb69fe6626792a080f4cf48887dcea4920571a5c20d5dd4bb0b8730b29a3e863128e8c1c2c9285af15086b136b7fcf1ae3e92b81a383eaa790482ed94b9a4b206bb6e144fefa62532e613261c4b0b4b90bf963f1931e61fd51e2b160e914a3577c817e8d3b8d43b9d12395b37841d0b0c1abba432917fe56f28cebc2e1cec800e84c30b55a2cbeac96e82e62a9ff5ed034ff3e6d9f24f9accf0c33193c1960d8a3b3c091b73f22cded9b25a0cb6fe2c0c74e8a534b2c1c1ab2f8ebb59600bbf20c6a6a1998ef111ef1e54c3c0c057af7693632ec85e84fc57dc0a6da7510836a032d8329eea382bdfec8aba8e860a8299b91f77cdd9634a3f6ba4e4f2e0d02ee2e9771487a46bc774e004b9bfa5a8f395633d8e0225ac295bebf62da1ff6d1985df0371b7c92c9b2b6d9262ba60c1c6cf442f16777e262a598dfa9936021a21bbc0c00790be5c0106d237bbb9b018786745d498a1826114b0575d75e54a2b0754afd83282f53a3ac8a369e0ccb9486a74c21494c40696e08d2af555409de89d4b8ea5e0ca95fe0428e79e3906f9d61e7443c48e6bcc73c93945df5c161062c2ac426a7e816c333393850d5a6a4a5907cbbbb01f6ac86f1e37ac3175f0f41fbabfae80cad8c05a415ffc48dcc79e64ba5edb3e388261c4ce1bd9337e498769576057da935aa59d180b529882711a4d406d990875023650d1a50ad95ac42b184ac0ce3c641e29ae4d807f07b090d4a6cbee5d6261e922a92c395483f0b5a02c853f1e961e3f4be44fd6289193ab506332c51d69e117eb2ec4519634585bc4d1b8fd79118440a7efae3debfec402eb06e1f45089d03b1c1c339e1f47569b7b494027e96b3e5cd17ada2471c8d59442b213c613ab0dd7a78fdb9f9171d50cac28957d7e0e033477182b0cafde449f6a848a9c9cc0a654211f7a854f5dbde31e18feeb23a3a7c99d89e1a57185875a93ca7783917719dbea5ffc7a910bfe4b795980a54dd4880040278105a54d0ec2e681a5a0a2cea7d536f5c2e3969344c4e8483072809fabfed7b10b7ca023e6c4276aba07f52efdd7f5b6f2d158004922269903b7f761159f42fe8ff0840b66361b1a587ddabb5521123a4866025f02efb1fddf630eb3c20b2cc09c790cc8d2616cfb7329ce27f5272bd24c4c0bcb5e5d1183848bb9b5f116870f818ff4ef63052a8aa17366bb4bcc86ccae389caf37a0a3e351fae2c4e5d87a9641ef791f226692f1b2ae618e57f8f1a7c5105bd173400da6819338fb21c5ed49678c68bfa54a5ffbf90acf970834aaeb34b4fd70be5ce21671f1db6d8b3b232916e44c8efe69290c118729946aa3ad45a076e1e2139c97e4c4b101ae1a40a91cd2062fa323d9eac3468f07589bb5d35eb349b261abbfa0d4dfd3ad2f8b2150f1ce85bb916e05e1ef7970041c6656e730ee282cffb0cc2290361324deb631e8b09986842e28ea120c6e22be2fa9a00c5959a9e43d198f1666d40f26c867c2b9833d3ee8262533571590f8b65c3e1abe20ac665d3de079aa2858088d8adebeef9fd67e7e70474267dbcbb392985d54723562a5d7ae14bbffe730c642a96d752f71e39913ca24e015bf902c0caea90bd16e96ba3a5dcce41c1ac48dfbceb36bbf51b66da90ea66b85e35c4e7694864e8925ce3cabaa580c8f1f0135b50a8b7d50b10e55302c53fb4bfdab70f004d435b4652bac91fb6eb4745fad2b28b458824dd6ab834535acc72f0f0682b5b8e3f1120a1656cdb7f8839e595b620b9aff99704888f746288414abdc93ad24034c4eca3e354cb129713321dc360d1cef032bbf5f5342edc2fbaca6555327c8652123ae728481c08a3d08b4564211a1fcbbab90aa027b88d698ba426ec31bd38d471b41e4414e12708d353f3c216014f991d44288d642adda9ffb7dfbe23810b92c7333c8c3b9536c9dc15fc7599843b3a95bf40242b303234b4bcf5a262a989c719b56aec27ac26f781634796d037a4fcb2695794171b5866cc95820e6433119a5d6d80d1dd9831e6c84936a6491ddd59cc8b5b5ac5be6f3322292754fe8a3184c4e5d9c1d06afbbeae2186e5c2bba68d85d3777a9fb21858d4ba6e3d3d38ce2f8f6286be45b86ac6482606e9aa1ea11e281bf211721f5ee520853aad06ba75105e58cbc87ebc83869a98b53df83cf5efb1c2506d38081ed5f58585b0542587808e57d960f88b00868ef52582c8d0c3ddc9bb00460b808bf0ea502a200d43951875e4f5e30cd374c1e9583275a4246ef945ecc80251407c6db439ead0b4ab15ccca6855a52c95f1956c633a0b4e3d81867363df81c9da8124d84f6605cafae7cc882e5a82316559d7e7eb30aa424a8f2d9d2ace04924cc904b91f09ac7130e4c35593ee207b03274777f0874959977c48be8693dda01a23243d8ecb218500399c2273284e48b8fa48c0da67748d1418d35b92ae87abd8ba873a589b3a3cf4bfcf8a1e2b595b01cef5a87517938950851f7883dc4bfa5459e3be73d4cbcd54bb271103161d2198649922923927c7743928129493ebc139b7948195b9081b95767030e71ce5decb93dd8f374e08bc54db8682a7e05b54bdce51a3130f9d3f3fa2d64d510aa083584d7f689b1ca48d62bf220796d287d0895ec9010f3c435aafc7f08fdae7589e55b2e5fd1bcc0b172263efba217923525856525d7d70acb422192daccbba6628b6966f947f86c9022d7f55e506c67b25f200be291a0b769df303edd8eb26362057a70b20ae76874a513dd9a40b32490b6dd74621b80948c847423fd061cff206746bcf3954415af77b9061c5d8abe6452179e098d24231e9e25067997a5d4ff5705942282f91232b93f19fa27c6ad0370a2cd07d2e5159eaa9bdecfe2067d66675f90d2267efab7e9f9363cacd2d248db60559224025096bbd435de84a48c139e2349af0cea30283608d458a2a385dc43e74fb03c3040fe687a5fd573e9d0192f309a233b0054283e959c41e0c60f538dd0589cc15e5afbd4da26bb88388b9b2a954ff008905871943b115a28a8871478e616125c38e46afc5b8654dab5b5fdcfedd7f0b753b0d5b67b0b940b81edda0867efc61bd15b8c0f929b8a17bcfba94c087302247ad6c617917f26ceff6a74e6ffda3e113ae60e97cc0bc49d9b8ac8503c57985da5cdb5b917acaea8250ec7f3f900f97b7fca882da48c713306c1c4512532fccf326b593a46a05aa9e01b9d30c9c245d71ac4201a064a1e4e7575cf9c900b296389e6698178c4c6a678f4394e50a000ff4f0165b54c21875383cde7435334e0b88d3b763381a213ae2101d04c53ff62f828f91d7337902c52511e0ac0ed1c83694f6330014cfc8d95184c294aba1345806c3667b41ac47ef2b29fdf8d5bce78fa84af518ea7e2cf329dba195f51a89a3612a75930806807bbe0cb80ba6897728ac94eda834124b9720e3443e1d37c991be621c3afffcbd4a9b58688a75f6067be93f0af0d9152c90fe2753da8b8b43d5767ac75da282d6c3fd1b71a1a9489c1bd71711cab4f090e459ecbdcc45cf8007e174ba4c5453fad1a12bc8b58a724c9eecb4e9098691e49c274df2d4b92474bdcafe447728d397aafeb627eba455ec080bf830f95279fc38cd9431755f7050d4cf23491a8122929e31b7bc228d2a2ca273598c48dc13cd941985e1231e2f0ef927e8200934d83fd0ff316409bf94d6c60943ce244d2ce9b8fe652737fd67c53b0099dd10ddb46e0cf2b6c756a75d3baafadbe4a8155fac1ff97c8dd2a312563d25095f254693ce182fadbf93bdbc1d5b54399b0d43b4bce59199683770e5ace855b39a517c35115d6f10863777aa30032b5929ed50955a75cabdcdb081fba7942e929a3af10ae5afbf77c7eb4233c0e08a7369fd2a42023748801dd99d6d9b84414894c8dd42cc2ecd225ec8fe52b13fc092170c3c39be1774137cf614e7d3e117e05e95d648765763b604c143b572c560a741aa96c93cfce9549c875d05e999c34658d54a130cdf863126f24639e08e8d96ba0822b364401fdbac559ee330d81b14a427f3820baa50c145f2efd091cb560d626632730380a63ccf70ef9437092458508394712885d61421d2214da206f9d5f672d206d6d6e3413056d34412eb5a52b0b7c37ffdcd75f3e22fd36825d7897b0b93eb3b554924ac42970eb684e8c1347e98f442a5d044dbb3f56e31e4b1bde268e28f2414882c46c1a0b65a2adea8a5f21d27aa07823bc1aa8c7026b03d56dbb0be649bf2320efcfa21d88d112021f9e262e52c811b1cc61b2d78ffd89382adc9a668cc2510ee53267c95b210a2b6610c6759b9d3c40e90900a4e04987233baf39eb18fbc1513b84d54df85fe28bbda20ae555058f7a6e13ce1519a91f78d24d49f67663e01be2f6b6eba86f93cb2c9f8142fcc1e2aaaad677636af5190574ae9ae7cf4817afe9f2f0b75139b035c70780fa4aa015ce21623559395ab7388e8ecc1763018dc783ce308d1eda0ad44437b4e58fef345608784117675615b8a2db95fb8fcea4ab979d4fbe08a621118af024a1a915246da6c0529c06f92028c553f001c2a03a9f5d0f0bc6eb5cbf712404763a02d5c894742b3601c0c1e93d08adb178de32df03cd95be63343881703955fe50f398c7a72c5548d23a7261927562ad0c212619ea106e8c9613fba38ffebf2684ae42a6d040851b31473bc48efc335cd000a6a15c62f6cdc667b755d0430f768e8b0247f36b2df8610ff969156057b43dc4f6c0bdc910121b659c79265a883b55663155aff06c7a7e5b33e81c157210222a23bae30dd157ae530bd74cc3523b38a582039fe058b53277814cc7ad9bc76e1c65c1932ea76c8d34dcc7584a5cf2ba9e576c22c4e9245a44ba71df510c503ff1005fc9a02d5b4490a5ff94d78226f03ad545a68767cfd4f191e7b1e1a33be873f5a206415200c761da02c066b99298e1714c5178282888bf2d8098fd27ca4bda6a5d3f221e547b75e2bd96652207e9f0740e327e847419a1b241afb3764d6da8b870dd292c4f30d3d454ac0c84e1689941a78c39252cee78cf574a5318eb8706f7cafa373ec469a998b366bfafc91d4174ecb63320689b7f932db380492525070e62612712658908b5c508b9b763a6e61127c8df619e3817eb1073c67da233cd4e870ad97ff2ad43ec007b53f90a0e20d5651e6bd5781169eeb0f6b24263ec4261cdde0c6b6408b458d6139cfd1dd85d4e861844e9dd36f4339bcbb08ccf931abaa74291c715278a53169248d914d6108b91fc8e6196e6ec7f5c2496df3d6955110e420a2a60dd042b80355da97d27978e5902bb5c52e82a049c8f691644b5b78c3d74a85c10a21afcddfd31af244e6d30e14e0cc41f734fcacaf9a091188184693294928adde3b2817c2796db1e05248b1e2ba858eec795817368db7a7c01e18d8208e6197fabbb1443dd6804256dd3ce000fb413aedae5cdcd7c857c78fde2adf47f361bb8bed915aa2d75dfe039ce71c75b559c35c413495c834509930c3a7d1c4567fabee8700de6b9e037c331dd331db5ed94a8b849d0aa7cdea4a167257647ddfee53b1e3e799b164df6b6cf8940e8535f7915fc4941ed328a15049a6bf3b20c6921a6d758ad8a9ab246a184bc9b6b458336656d87739e16b3f7c4395abd36da726028b1800067cc6803e516b8ee0118a4a094b70a9c67ff7178db297af12a60a3cb45c1ca3fe722562b0be942803cc68be0569e4e6690c45f112db041913dd012a50accab971475591b328705e21398d98447eaa288f05c8014128ff10d0cc94252e31a5c0652012457d288220eff51c4b4f37e6cfe624d37c7ef6e9e54c1c21a79c4fe41f4aa1b6b21cbd738e8d29502abb89e5e8f6017b27579a9bf12466c22c41440f700a54dae58d963439f7578ccc4bb2d252ddf22aef1ea7597fb7304eccd43fa02aa91e2bce67b06e7225866fc31cc0dbf6ce18d5ac49eedf32372efd350b368bed7c00668e9102eefdc77efa353ca6c2346931f360315145c5a1814a953dde44b836876d0e115b028f598d0ca5db361615be32ddd2b435d4c7966018872b4b59ce655b07f4b724c7b7e9efc1c36c4c7ce347ce05f22152079ef7800584b9888fe76ca6059aaae34da70216053e2c4e05c31ec1394fc0a5639544bc985f60ef25968136f70035c1060df929fc396bf17a01c36f976c538f5206fbc6e8a4150fc6df0f0bcf3605c7965a96d5478c749f4d0ef8184a429fb6e7e7eda0b2fa7b5b4a610ae09193ad14b7eafd355612116a1a860af494e57f95d4adba5daea4b8600d9955ce2932eab751d155f547f43688d047cb2f52297085c427f63b89a385a61a12442c2acb3c2cf39d39dd5db7492af67ac467c54d945c4b5ede0ed64f6b8bf60769c2210bc2de89f071c4c92584b8dc87229ebd2525c55aecddfea46d4149297410e962527593b096df6539b5022f66fa0dea1d06a2b328efb54f5b77afa866b8a3a08eca964739cbb1993d883b511353cf4c4082484efc2af4473eb49371d4d3e62e4df5440a20104891ec1bf2d59d5d4ef7a28306fa3de97156da72c9560487b3390b2bcb9b0bccbc48059c5fb6606b8e8936fd28c731b2cf4f793b85e44b0035749fc56230db45bf41b1611b891ddb8e74117ca60d078b449b3ecd88e613d8856c6954cf462de8fd809154d0e0e3b58d634a818e91731a6f75749846465110d8ab63ad4b258096a9c9d25a75f42d61a0fef31730fa3001a2c431396413e6c84e94a0af485fb896216e968172c73525fe3effafe00632c6388b94afce032f35813e5790450833d03423f758828bd8ec496c8c4d06c51cec1de474504498927454b3584f02522a8a087a371a8a9167341a849e392febde547ae8f15a186207ba137d7992753789e47960fbd1c644aa5fc1343967830646f4c043faedf2662c0f257198907cee40336ffbe10befec215f367528f082e4f92b9e9435d7313e922a18d581afe5168ceac051a7bf54f0819e5fb3d9ab99103c9c35f4f27f9c08f1b333febe71050d0622340c5e7cdb992b332274245f107267594cbf29c62cc5a51bba591cd1e462d0ce13b51861e710cc9b3d46c3755176f752096852ab8c5da6a4928250f2f0e58a39e31dd0f5ad7a2c15970fa1672f7431c89e6162f81d969d463ed655ed2c91375fc166c1f9560f35485b7972ff2be4855e57dae89a4b5c0f83aa139a6fd2bc4fb41dae61d8b512ed6f84ad6f553836f710a4bd7072fa76de3499ebd8158d559880602ea2959ce154109c60450c861158635a9b713c40ffc259350593ea4d0690c540d37db29bb166b96099104f8b42625f732fe64b4875fe559d2a97cb112aee966edb3c17db99cc8ed3d1cd62b0db7b857fdbc185c9510b7c6e74689abc599d84b7ec3dd3d4543b9288d1a1c92721be961f0355648272c40f04ff2152131a0ca11485933239d5d61d94159c5f8c3426ca579f48e1d7b001c8827fdad92243567900de6c44b9ecb299f3c6d90a8a1468859dca673ae3d4e622df8bdb1a98115e4136ee241ecc9fe5ffa1eb04e13703648d626e6984232be47084d8b8129774aef4be852c475df158b366808a2fe0d809f0a7670bbb2430db6a1333ec375d842c3d04e7a1e9f1fb828fea0edb58e14394eb3735b0ee096b3801371072638558d5e97ea2998720dd3813f1aa2c0cf4fe6267e67405277579c507b6c3d0be72ed44c95a1e0322a7c8e70f97d97e2648cbddb0c11f12edb97c37a52fc7d54a71457b49ebde0cad0e375826ea5e0d563c9b521bf7f1a63e75197724cd5bfc4654983a0aa1b602b3242e6f4887a338896130431a28cbff1a1d6524b5d66dfa219ae426209fa2f2eb24ac563494f148d1c3ee6a5c888543226b399b4f04592222717dcc66083709cf74810b1f75d2acaad3b2ed2f39c2e6a271eff473bb28c710ad80a0c3feec8dcfbc18d5e062a2d89162d15d342aae24ba42e412b1ca5c1e7201a7bb6c9c28c7f4e662f6a3e506e60735732b8ef27def67dbc095336402b7437d97fabbd2aea13907ad5e440340870d272bf00ffa550e65720391695f18048c1ca078765c756b297db6aa02891ec54f923460306639f34464a49cd852669d3934c2e58893972a2714dd96b6ed9cb4ab280c3ed09733ae2f1d10550940577341909caea9fd1017f43953e6af3ba1731822657b9b7e61f7b9cdd6a26e3804e630e68896484a5e63714009cdbdb5201846093048ca86e3aa8c26d0c2fc9c51ec467cc7503508f1f33d1779af8f745bb9d6b21c4825ad7e7c6651587867ac990c2f1d18bacb48d8e971d96c784eeb4e2068d33031e93b40dc5c10e419a2341b988582aad415527199105107436f4e7c36ae33d29cb312cbc2cde6309c0055353d183395898085f6a53756a6d69e012f933df3aebadfff73dfd20c4134ea60cffb8a75dbb3e4f626007020296f63cc545ffbc6b192ef244b38264a0ce542f66f0f923e093843db689ea8819bec1bd0047587f75971b887ac6bf1d72234d7477017b82800a14cb42e599b7c8c00d9179e003b296ab1c42caf089faaa7023a037b0435d3e981e39399a01348b29450f128dc4bf738e01f2fe315d20e9f4f059595f1dccd47e4907093cb017408eeb0dc58e8c880dd0edb56a87c4dc6c2a401041b1920cedcd7046b3470da422c9a0ce7c8e4031255365eb7d4c216c5af1f27414149b64f46e90c1c34de39fcff676dab104356b0245e809ef0991e326e37ce47124141870968c90c9f2c20eb01d313059c927e81f22f0f1e2efde2831944a02136f230424426ae4b89cdddbc3b161f09234fe0b8beb075564a32267cde42d061bcde8a97102a4b4aaca41ad7d56bbe26ca501081577d792dd5b0e73e620cbae45df56d41bd3617ca38a3c4037523e920a2cd434b1f104881fc8cadbb89cf9b7a73f9fc072de521d925df5d352a8e4554c5818d314a9d5dd88893c2a01b7c24fac6760dc9ee024c6eb592c0961611857689184c10c85034c56e82565ec06c84a99d0696fe5156b30b4138aeee8d79163ebd3c569cd3a846fcece037da1a61922347d7d0a96d6e61dfa6c9640142ab6d7cda2a1841af9a2b87eb41187c7d032fe0f0d9aa998d094835063308838fbc5cfd98fa1454695bdd2e7348bb3e789b15f78d9de9c384608a6f2c275d9c2628ea1d973e020bdce13782cfd506197674345a0a820990a6061cd98447580997a1c86aefc511bd34d9369f5a8e1a5d62afaa008ec7a832bb1b416820f11bb16a598bcb44a682696837832159ac0a7b887b56eb2e7d1e4950475e1b9613380509a99a650f80cffd21e6eddd84750288521f8e79dacf4ed14b8e364fec7b9ada27689cb28e9c730d020b1b37c0e4647383cd4e136c202b3c966142606c4903d5d384fba4c4e29951be84a501bea4d941cb1713152b00adf6d216654a2d7cadf3501d631f9aa73c5744eab1b6f9ae6064c9b387c9ce8a90341f5c424a129d6a7515e76eda5a08b71ba25f2ac25e4969be471f8844df1ac48eb29f8390332661357bcd4408063a08ef3544187ba03cfd85d8a13692aa50da61924721f9336a2c2d015ae940e85fed8f4174ac8eda8ac08434bca2881fa47118ddd67930fb014de2894f60f643d30aaef5f0e0087aa44e50113e5af7263e17183193c644a72ba147847cca26f4b944288bcb52c5c0b66cc95f0ac96b768193902108131e838f457fce6d430e3b4bbf3beca269eb0fac9b52719b2f29ea3a2394ea4ce892f937194c1dc846651cecd4acbc3cd5f3604065490896f6142c05e1abbcece1344b5d18b317d996ad4e6e38594db2153b85b28327f5ccf33995dc7808e530dfd9b1e33c68c556bf8f80315e0a102494341c807e67fd8414d335ee2625d82e9dac5a69a22ee289b1479ce74160de59ba31357ab564c270e05753b9394c8958133c7f56de960d64175bacd6bb9928d4c9ebce9db44b51ee4d4613964686eed7ce23c39e351b5d2fd43bfa9c27af7f7ce699859e8819b4c6a847ac791b57cae334efaa152bb62fd67743f57a2bc5b65b0c68358827b206aa5f80aae7176fa170a357afc9ea445cc1b5ff700ec8d7f73eefc97557c54775a518925eea0fb5417c458bc7add07e518dab7bb1660c592eb52461ef555e7de6e5badf06f761433bdb3431f546866cf950e4842944bfb0dfb2b403cf92e282c8d084cae5b3abbaff972fd7f1f3988e6ae66c05daf59d482a3cb776fedac534afe3f5c2cc9eec4792346e69f1ea56fff1de92bddfd481977719619db26ecaaedcec95d58ddd609ddb58865a44b4e687736a1520d2d5337513747b67b7f6ff1cb922d48e77479e536e1b6dd10dac40f62366ca2125be581166205d63aa9dd463f59cef1742e56d58221dfc6ea59d29ce54170d9b3086e40f70fd4836063ea081356a38868694c103031d928449c5f41eca47804534e0fe1dd5b36a5f33ecaff65ffdaa03e6bb4feb5ab77a04d0edae1602e69a3aea3e88039c04aceaad82bff4f79c2782c8bd7917d50eb8d268a7f2069a64552bf2d3c71402081941d12e195ff3ec4b488f34ce1956c0bd4f9b9638ca16bdf10c8f10c6e0a1e96556573b863d4347c798e0617f737bf6db1b1f0552bef43602a81b56511eaf5beb8efff647222f916a74602e6812c2ef4cad15305dd21b0503c4aa3678c5e4232c37a13963ac3a0167942d102e754c6897d97fe910628478f958812f58ada66f147a216f08cb3d4d9d605a0b2d5f0aafa0b5bc9e2c79584d229576994fc4b8cb98ca3cf233b8083ffcef2ec16382c93c5419f8a10f9ca67a15fa517de8a1a87af420aa0d99f3c3851788e9bd76bda0a40429b740b360cc419831c33589b99932a39dacfcee9c1316f6ce4ba7163ca986111efddd2d05c345709b58cadddb963196f5e347cd52c44d00f0a5adeae0504549d543b4c17d4802b1d6155b302a7bd0868da1d7ab8907ed08cef127e991d9205aeeb2f5fad1f13946efd9c0cc2d716da49bb4064f902b7fd25be6d5b5b6b44bc7f7b49b6245ff5101a6ac80f45aed038d2e40ca71783d5e15b7fdc7895122458505f39707a4b48116080202c4ffa659df1d2639a0611d01421462b8d41427d918d39048600a0ea0ca69fd8fc3994316ddb44da49870e5f7ae7b7a674f9615ae2291ba70d05bb59ab01683fab97d5514057acbf8cc2c4d95c7781ace5d7d701f1913f7d13b894055b525ba43e967b74634c302273000fffe80d986ca5758e59e07fba5c5ab6e9e0597b6158eaf059f099f5dac64d2ca2dc6eca1da5056a8f8d7cf1026f1521ce65ade53fed44fdf234880d7364ca0f77aa8426f9334733eb7287d4bbc4c648595946dbbebac8f63be8e928e7a022a346b7fa1cbe472b61a598ca8866f9f9eab70a03f65dd73db75aa4ec3ea5f9dc598b8f28a5e161d94513886722f5a358dd65d20f091d3d84c41cb48dcacdd239802e48453da052154535138f9f63ea8b212c13d5c21e4dcdfb87062162e6665fcdec9ca5c5ccca8a90f57aa03aa32e547570a2ac08f850ba0d48d19eb8129eb9f0c303f60a0f074015890be1fff7ef2e31963a6ca97b75c67a061d22fdf0af0a96af4d9813d1788c9002474ad24cf62c2defbeb305279d632ec9b645e643b20ff3f18617c2e481eac308372de9461c559447bad51babe3206f4831b863b68ccffa767663075236985dfcc74b7c2578891614890caac95d96ea8cec97511a8b422c5da9aaef3a04f2082773f9d312b856e035982e96fe4fd86fe217eb825b13dde4863cebe08505eaa3bc86f84c92702b8ce58308e54ecbdb817fc0561ae6094a50e698da0769f657f68fafaeebe65d3238934d98cd9e3097597a3e8a5295b3a2c39cc92ad5c4b25ad4331f254a7b8f376171cbf4592ba3f6740947284201a11d403fa42ff0fa16377c1059386fb548640c48428cc13591e5f3bc9b3836505ec3eeb09372b227a339c98ae9e3da10d87f0454fc6e0d84386a4648f41dd6f6bb61776376933f40b32568252a870d236d36fd7beb7aa09689317dbf945f6aaaaad5d0965259dc8b3ae16e7011d957d1a09e4c9744b6a49088637c848d4904ff820e6655dbfdca5675e444101d1d84d5b62ae60aae5de4e59853564caf9c5cc5102927bd68f02f86e86036952754b557094881aed786013fd8949e0f533c4221aaeef790b89c06b9856047d287de2f7edfe35fb821003c0941a0b0805f7d7e76eb437ec8b9b5c429815bb7e043b94716b73632ec93c9939986b35aeceef1e8844f27e9bd874dd5ef30a520a4ad32c47d50da8e12217d7cfac93388fff4d8d6e810935ee82c723b258fa32839b5dca28a0916222471285a75a55d23327c58ad2e812918b5828dbea38cb9e437d346f3685e322ec9915011aad59ec14a7d8052e9a8b328faccf983e533b46b40fe37907f2790fce50d27b83bbc0475899f967b07c98145821e714c7c14750c071b3b84299bb66a1127643ae30b8cd096b3b2ff78cb43a951b370b49b39d83a4c94b3157349af264b313090bb0cea754737f1c02f3b6ca0b94581c60805cc21a727f79431d5edca3e0930cd2c293cd2957c38925e11927f0510223dd9f68d1af45c1160f3c964c762408d92436ef4b0ad54a8238165afb1bc55f955c97558d39ba4b1ff78e3f33b436be0bbffbd87359dd1954f65b2ce1091ca2092312f6923fcac1cbcdfacb3f194dabb95f0ee102c1789a58bc0959e9c480aafb76331525b7643e821ff84a8088e610f66c7c3da318b7f79eb70dfd59b18275b42d9de230ff9cd7c2b5db2340e6c97b8c95c7629d943f01d6b6db3a30d575ed40de52139ab1746dfb016fe338ad6be7c3bf9662c276c818a804e65ca4262a2c7dcf20f845c07e5a5e041c1abad7a5d89a1f02ac1b522f88fcb42fc394f3fe799e92a3c90de2a3c9483e23a1d28f48f435df1c0a06141ffe7d1ef8d811226e18816c6e1dc171475f4cea1ac240309ecee067de71f09cc1c4fe8aafe20eb8b38b97fb73af8d54856a052548dc2b3f441b01e9a491474c334440aed0336e33e551a8cddc041cde8fc177b21bbb1eeb998c30fb5bc788103201f2be3ed5b1c5946cfbea1034a925d7918d08b940886a76298a43fe71ac0dad45bd1863160bb3623e31750b14e27a1175cb1241d3eaf2854c353a9394ad861990a8c2484ce3fb3bbd0991d847a615d8de180d0d61122dc1442200796f4035be26547f74f2793b4d139e2162f417dfba9c5acc4fecd4c8a29276784a08abb1c934bebcedf60b1d9bd956232f3f43ffb1e7c92c46fc0e3e2faf928c862dc5e3e9e85a29ca44cf8e1c90b465826245988671f1fa5c1152031632098c3b9fb5276378ab349760fbd1c94684b9bb776bccf5464d380ce0615dfcb6bb7866eeece129168a28ff2a6f42fc617fd3c4d959d2b6d6a5a8c269560a0b26a1c860a17e2493d27e7a955baf48bcabda85474864aa417dbe7878be236f31604a138e0bae5005714e9550810cc00ae8bf2c1e42132d2606ced79b220be67abc2b2ab651d62e0659583d3257a7109f54f2107f72724739182c7a25e2b4e793098f3a8ecd79019a9a4b25827854bd869a1a9d4921edfe85326fa6d21bd120b884ae0547420031ab50679c8fe0d204e36c24328db1c93cb23d4a90a00eeac121cf49ee2d3c72bd6170917b5580cf80030142ac3d208c298d7bd96b9b686a7406d29e74060f5e9cde94ae35f071017665abfde276af7d849148caf77bad0f02e8b0af2914cc2c20c07c6dfa608cc28b7e5e14719f6aebb649a8b8a67c2448f73cd6592ab038970ff7e95d1f0ad52d4244507dfa9afc580e3bd5240ce6f0007b742614824c631b6ab27f16b2774d65dd94cd586729d15b8d3d23d137dcd10f7dc58b0cb26c0c177e5f6f46bff4e041e4830d0b61ccb991094a3c37830e77b0ec9ffc1ba45938f010367a164697b133ddf6b11f0035598760e31d807f2cf257915e3ed334a56ec837c75ee26c3a27876b41ac39f6471d0d38f3ae5de1c5aee64411a62413102d080f267d0a1f48891b89c0dbf15438248dd01f3f20c4b2d071c0cbcbc530562488017c68c587e41354b717828c36fe748611fff3a609333d9034af05706481f8674baf95606cf7cdcf2543f599622df73e85a3e6cedcbce10798e082833b10cbcd26d5ce459b4ecd1abc68744cdfbbc0c0f27b0c307d5b0f132c91f75f08b1eb3d44c256f2563d74f086d4120d189e9a24f4f8d9a94f689f53819e319e987e72ea56f040b23a6cac63ce0ac8f86e6031ccde6b9a8208835812daa944425b58cea11e759172398a153c321d6db503c362d3947032804f9dd44aec42d7f059e190aa922e6520b158d47ed134cdc491fff2482518d688c51c28af6b184b4c2c2ebb456841d02c13a4a2e86d693fd0775c7d5890ab6def00830951908c5a2d84829c5c55f5d5c418a3050ac9ff02551057a38e87a8034068796f76a1cf7a1aded96761b8ee0a81f61df8074a47a1b98587cf9cbe2792be5d903f2fbf94378a20ef84226b146f61d27e9f2476d7971e9ca40e4243871bab6cc465e702b83728c43b0695db5260ebd0e7881ee1064694d3fdbea1ff546c947a100d351abf0a6bbb75131fdd80a1db10d1289665a90375a824e6606d0d3b3822f76be196d087eee472042e46cd323f354d0819cc5a86e0d6002e64bee03c60f8860420466e566b804dabf9a4fe9b14c6b2ed50640abeedb64797c7114a7f4cd3b536539617cf32afa52e3f1a66cd6895fa38226db6abf3f320f089d4941a82003e78f703aadd8a9dabc320fedb729cf5e67e870704b6249a3156863e6b663c7b8567703beb900d81e40d18dd88d0dd7e8dff3d15d28c2912c3a222b6544f1c8218d8a123dd997a0b9785440ae8c2d78653d5513f10dba02d4f8b7c428fc16a196a846003e44c18883b9b3dd03a4426cc4c5062bc6929971146aa1fd27a0292e45085588c7e240d64e51c854a082c9bbcd1e42f701c2a3ac23b309c8700c74b13ac39000077d86b4b1499d09f4c80c7aedc3d29cc140801f4479524955347a24e441897df77cb2d1d7366c07791dc24f78aed86a0c8df944217af9f23aa4fc2c7fea3650794515bf3d1833101e67a30b27de32a451b04f5e4d5f554465a16f6cec0486eadba05c58629af47fdf5c7f3b057bd624dda6cc2a4bd251adecaffe21af3fb5c71703462f10bda32d39c4d9b39788b7be8a16f913d87390a1cc5fc741c17ea0e87f1be1191db8c3e573703b73606c7d1209b5c34773e12c4c397822c1b332805271084d2c0ffe2538a2bfff08ff0284dedd1cd00d11bbdc914ab3210ce003f57204b820e1cf29da35a110a41f4dfb6e161034b53f15b5016ce078cfd74a9242fc71356177c3c73f6a790ff1cfc28a46d113783f39f1a6197b095bb2ba83c35d429cb7155df07665ea65e39c1fc3c39ce3e9f9772354f2f93df8b25a27025abb10f6ab16d9a98da67a44d689e2732cdc2b153c8305124bce193775da12e57f69810fb58acc97f4258adbf7372a37d1e2890a762d0fb9c2a399329d040dc9195a72eb9ae3769f8203f2ff1f445ef84916e460f108cb11e8b4b4fad48a3ea172704225f85b7795f02641168ff9e8c8ae47a2a5486e4195cdc17b0566636ed9dec297c6a2ce9d6c76f55e49151bbfb73cf642ddc5ba81c799e391c2b6c53920aeb1dea6801f206da5eb0e6c5486c4696ca544b006ccb2e5c1b9942c7ed958a0d9f634c58567f219ee4ea7676e57aa9f2dcd187d2e333f2ef2c188190cd2d4e8249667b3e7bc02d7f7d657d965d641f0a07814adf21af807991254f2cf5aa6e5de9ab0cb04b7486474c8e4c537bbb34b96f1f510f8c1ab2392dc69d1f272c0e20aa959d3f321791f6d586fd9f604545a2ad7e5e7a1559104b460beebe964e08fd34cc2c1f0562812234a8156cc7171d929767e96910e555392b02d276321e454d4220b0b29f4536c1a76c54dbd024c1003eac5769459204cd29a0e893213309d6a0407e4ef54cc887b90edfc5ddcb544b64ea506f04f8dd72c20cd06fbfb85d54fcf1a183028f03b83e0f78d15f3dd442157c5b63e1c3b8770897bfc7eaf9734273034d442dade6d4dee2d65923205430ae408c9081946b364fcf58da070cf53a559937bde7eace69ac1ff3a103b9292cf8330330ccb28e9f34655aa4ce67d7d1b2ed5ff6a6765df6ac9fef3405cdfa80aa68e846906b1eff9af431cb11e2bf688a5fd9f8f89a5fdceb1ffb1fff3f767a8e38e1ddc977d6cecc07ecfdb5087fd9e9f2150eceb38abe8fd950105fdf723dfaf6153b2fe7ce720eff91ea09e71f623e2d0839941b80ffbaf962fd0157a3033ac2bbbd107cdb2f7c62bb3d1d6f3623f25896c8ee4d3331a51c17e077b0bf87e69cb4d231670f999da8c92ec178bb96627d6fc3d11a531b113932849fff5f46533aa825bafe4727d1fdaef5dae9af12bb912c5e2ccb00f6a0e45d77fa20f9a5d8f6533dbbdf79f272e75ff75b6b3d7f34173ad5d494481e88b0a4ac7dddd07cdf3dd27c5e170381f340d9d243a44f3ea4c1d3b6b8e0ba8a0a284e88a8a2748aea820e1c815154e49ee60e1a38a12a925aa94e014a5c906ac2b701021ea0b09b2c043c5c3f34275b2738838fd40278c151c1a8e1049d4809b020725a6f090e08ae20d79d121a182a8393be820468e172c7cd20c0922cf111870b0c3039e219f1029ebc37d3d5146c9054a3966a4b9c3252b4a0a239a10010c72a6b400b5244b0bb75a459d5cebdf2aa8c8f5411192ea38d1935407091f6e529db1561184ab8a1b5ad946529d7b072acf72e7eed070270c115d561554c420c6eb3c712634a3b8332577278a5f9db903e50e132abfb366cd144ea4a08273f5da7aaded29acb2b5d6da3676ac68aded5c5d9c3d11bc94b476f0647b870898ed636bad65833b97f74e5135050c5358b9b91caaaa92baaa54077795243b13529c905b02c772e7a470ca65e7d0d4b153bd3a69ea9cc9a58dca85cfbba7e91883446da0dfa3aa8dd6dfd9bd4eb05202f477ddf8236a83be0109e2fdfdeeef88f3c6a8f9dde8efb383faf6dd5fc77c2b7e14f4aebbd2752310d23b03995daf75d35969e704d51262728bdc544be0f2079b2fd236e81b8db6c3fe067db32307b02b164b5b0e2c04fa66c71f33aff595178d92e0997fd07638796d477badb3b2683c3a2babb3b2948cb0d4a8ed5b140d83f18e738e340cbc03da95237fb34fd331c72b760c12b581faf3ed13a031255b6c4a5ec105e9de12a0f441330674eca0df2f25bf19f2c076c445d50f1245d351ff6b5587eb58a82ad827bba4a343c83a30778a28b288c8b9592344ca03f058c1258815b260e5b8743cdc89b34826763535747deb0c15cf64d891dacae0b563ea5e79823852a74c104bb2883a220acf0b35e48172449d17b257e749963fd1b960c6cd24cd392367cd8ceab66cbd6212258d2839bd8a64c3e170436a145172adf53b8992f36d4ae02e436b8d86d62e9d35b4d6b9c4f9fe0c97ee2c7be2054a949c1620224e5494253830e1722b67998e9d4b078bc89439533081927273cb22e98ca523a7d2b9caf5412c72adf59ec1b7da92a268cd9933c7eada3a04700c6038b9385fa6d4385b9ee2b0d024ce932570b0527803274d7ee5cec121e206383824a170683002470627382c50e960bdd9c92913a7eacd1b26eea31225e79b239eb498375e9e38116fb42c7122bc377094bcc1b2370c4124be19014a0f3ac44922829b1e2c0ba8c218918b838788245ab9736fb07816df745dd064acc4b829e341782deffefc9696232d48a902cf9123f75a9dc917966f9e7cefdbd6a011410a0957a8c8dd70bbef605c0ec404b9736f84e42e77ce0aabe25c8c8ff1301e86c8739eebda5811b9f9d2e6481b9c9d65ebed8f2e774acb99bb3a766339ffba3a3be783d094dd03cf1d0efba57defbf9e3dfd552f761f70abd59aad2bb6bac8ebbcaef33afbdd5876e06fbf03ef446b45a6ce55ec3c43cf75bc5d64b3392425f9977b938888fc875a89664744c4c68d111c1b35443a5786842782080ec2e9b21133a44eb1f1810865d3c5c8ac6343432e6d2e183ae7c1bfa1e092ddffdddd7bb8dd151240c38412244da47470c2668606d44055420f6cd4ac3086cd0b2f54b5991d0c682425af5f35ca935228b0a8b12177e8a38d925e308f0748801e953d8832be94880650ee1c9b273638362290805b78dc097752b809f87343b1d4e4b27344d2b89033221c41f10ce148c640f942a728819f40022537d02aa49c890cbee4853b55ae9842ad8042b368d264047f5a73c4c89a2176b23882ad15940a6ced3cb91a22a704ab11d678912a4f8286669dc1a28c3ba1ca6553d13cb8e6ce414126ef707728c46477dfd17947cb879967fe60369b51d2b4d140cc7f2272da683122645a025ce28c65b369a3c18890690930fdf9a598e7d3bfd3467f1121d33fb23fb31d6f4cc1fefef6bb70c9cecffe9ffdc1f9fd5db834f3d7dd2b0f90e7622806563c67b645d0eb1b06e3854f4ff7236bb9c2fb3915a2650dd09a8c0841a20e940d73aeb0304a5125e74411615a786385cbce0b45a4d182854a490a405b6840a10bee0a0d60f0786941b740b9e224a584942c12c80186276d6418038554155d28087ec0e1c81635509e7e682aa1aa08122728389899438597518d179cd58212639c46103215150128a2a8304215284f577780605b4a68d3830d7060582197470d156c84786ae2881d1898e6082d4370c161c509d96091d2040b3a6b9a98a2cbed291daa729042c91238364eb480040c4e767ac06223050f446abc2229264cccc0e608291886c889e2a44505304da2a8e3e5c7822758c860c3130c56cc6460250a33499a844921cf55979997ac1e6e7841891fccf00081293cb91fa8701802083a968514980823b5c3973959a820c40a3924c166072756a2e0410804236200e382972b989062a38a13524c218182133ecce902f699302ea051e34688245a34887a21053934cc412245ce0b099420a284c821024c1a02e04820030a4d9c50e285385d94551b2d261c59f226eb872c459e903283912e60e0f0f01218a2c4942b3dac61738491932655d8a0a509161edec450e3a565353473010b56191c114be0b020c490275688b83387891b183cdc50c31029ea3c5161a504ae0c1b304ad2a8d0c68b8c6ac49815f294b162ca98322db4907067843448b090b0893571830b6a88a062450a1e5a40cd906b824e0f3a5059e3c55d3881fad2831d2d72a40690c4142a52a428c2ce1c28ba886cb2a24832449e144a3083810d3db46113c52a8a1435d3cc972c69aa18d26488150d0891a33a722797e7c9133e63c26831620a8e0857301871c495094caa5068e3a68a871e9f1db494b0029ca9115c683a4ab08c8952020a4820e1c48b11082104578218e0ac51a2853064ae2011a7081fa69af012d46b6498010e9c30504478b2c08c096bdac81993c298345ebe2052d01182053449c440a6863a4c4439e3c50a2c54a0f1227b40922167a0a060bdc9228323ee9c806b0288222630114e12881099c2a9cb1d2b0ab040220a1636dc90c4cb125d5cf814054cc4f044d41b16b2e234a550248d192b155c70424a09efa12a8a1356a6572657bb831ef95f9092348b117af105b1187a1dc976ad55f342b6f62d119964bfb465233b263d11c56b348e9a28f94e25354f7e9f88d6e600e2e07e028927a872f9b91ac5995c9279ae20070c16484a5872c4845bf9486f660053345144144640d981865b9313192482b0e1aaea09226e34d0a4d0640519a078e3c26ddaf2cd1d4e5b0e1345521444335099e6f63ee89f778eb3c96a731d41eeb75d54eb52fbcdc305bc728fddf4e6e207ddffe4f22797f7031f37cfae61dfc72cdbb74f8e54bb665bb8505a28305bfb4e815c7588b3a7dbea41105c76eb944b3bc546a1247d1b6a406fde689fbc465f480eeeb7fe34c42180ce765d37835e3e42f9f03551e9c265fadf3c3275c8fd954b53cd792322972d2d994abdc8736c497511fd1714da45d681e4aede2e3da24ddaef4e04d9bff3c076244ff4db6d0be687798eb38b6edf8d223eb5681b43b238afd18aeb7e362b2daeb3b84c7f9c40f61fc7e948f6d6ff79b6d2d70d991a8adcbd87380490bbffda5bae56ab45835edda8445d383c356891a26186a82c35ccf108775fc1842473ca30a7a31003123060b9f2421417acb4c04215225054545862ce0a2ac471f718a6387122a5c69ca4302d0a78da28787e43c1f3ed133cdf9e80e75b27b715a3a6a5e51abbc8beeb039b1e893630493664c4f7b9d223224a3e256b54f0fcf2c321395f9e4f5de4247c814d8f604c3c4ef2c493a90cc90bd6f66790ec8f416b029e60d32317b9df45a19ea02878be6de2239b89c90a004019cbe407b44f40a02d2108b424cc5a804d8f7cc81c83d202b423b800ed121a6895088136c90bd022813114e270e5b1c810688f3c688d8823d8f4c80563ca18a325cb09515206133188c02693494a8a511081b6880cd0129980022b2486888ac60b39145c47c257aed7666418325c12b44eb50aa8182220132a9801363d9a3630499613124344831c4fc0f3678076080db0e9d10c1a332aa8cd20299840c60ca21833c61933c49f3104e385d00c9a8ba016b319403366ccf891f9cce88961d88c5708cef85cde9a7167789dad7452dcabbbc8fe8cb0738fed4fd8aa7a59ff521253d2bf7680050bb9add4ca3085c452b0ebbe297f16b42289e991f5b7ef210623c836d7b7a18e9a3be8fef3402b8e2025afe00ad2236fbe176630b3f21cc1b05ec1344f1b6ddb82fb34d451f3fd5aa0fdc074daf5e5811775dfbdcfec5155eafb94925cf6b75fb5981eb9fd4ac91083aac3be7d1aeaa0df811df1ad6d41570ae52bdd854f80950a9504454483540e5a6a00d2725d5b57cda7dc1585a29a9d7bb7e5b5aee7148a76eeddee56cda7dc95bb67bdaaf994abb5b475d57cb2ddbb2d775567bf224182e9d34b8fecdf6ca928399f524a2b755c17cd597fd62498beb5df75e37811e27891bdfbceb59c9956bf29d7b1142d580117e00acff7b14a75d17cd71ca7eeeed589d4e94fd467ed6e8b9b977ade556b6b77e7dd5ea557b4a0bb3b2840cc23938e950b3fec30459612663c284fc8a0f0830d4ec0981026a10390ba43022c39986087cd12515646e0ac1015e6e67953489cb2e721a92c18c9d9b16344ca46f53321222733a127a2edb8df9907c5ddbec78ed47a574b3c726ff75da25391a4dbfd23f7f67da3ebafd87a2577892db10303c81d62d06e728f57681bf4adf53f3a770bc1fd10b42a0ec763d27adcd11a6d1db555a68a90c9e59d3ab2e6dea7bc81cf06a5731ef9d70f68f641f395fb5346a6e4f24e197192296d82cf4c11a9aa210f8abbb5be1da9c36e2ce7917b6b795f7fade295d678cb10721dbbebba0e7cd05cc70f78eef7c91d624041eef14a08ee2bd13b7e47eb109dcf84be3ec137f7149123350af6dc53448854afcb3de59465029b14bcd613b02cf79453114a4e248052c5a90528b527f7941393fc937b6a08969af9d342654a5b98239d0925c1d6ce39d2e6cf6cc10ab88e235367fa72aee4d8b9928385a74baecb975c173262b84c4add6bb55eabb55de7dd7aafd3792b68bbcef3ee6d75a0776fabe5727d600541a7136cb95cdf0782a1d5abbe5e4ee7eb8c0bfc40300c5f2f580ef0d5e5f525d387e5605d327d8cc964fa31313db5a7c7e9ece14249faf4892ea14ee8135d42a7d02368aad0aca94223c7eacc949a59e6962935b374c9f42d973cb1604d1cd6749a38acb9641e09810c2d43a6d1f4372975afd576b5eb9cce6e52f75aadedbcea794ea7576dd779debd2d5775b99c4e97775b2d97ebfbc030743a43d7078261f87ac170c5d8e9c4e10b06c33816ebf1a93e3e4ea74f8f8f4cf6f303041434abb399d3398b7d3da08b3e423ea08bb297812e7e507fbe1fd045a0906966b2836a5090d31974ebf8735f341032b5a2247da617b98ae9a229b89c55316dc14fa66fa3b4050d15803b5feb56265eb777d5dada79b747bbaf23d52ffd7ab9037777f7cc2c6955ea4f4ff4c9da576b5eea7957adadd9337da2b9be806b2e632db0be80672e41b02de84cf1adb5bbd65afdc55a6badb5da5a6badb3d65a6bed64329f24afb556af4cbc9d26d57abbbbdddd7d09936b5adbaad5e5f2008d55f9aaedbcdbba2da82b9af3a7aa2a653bef7a17ea8ae6fcc9abaaedbcaea926d415cdf953671bea8ae6aaad16eaaa7aa56239a168d95455497790ab94c5dd79bd7c4d944245e1e2ea757be9eefed4b573af26d8fba5bbbbd72eaadd47ddddee2ecbee933bf0ea4caa143cbf96dddd2d96b319beff43b98396826bbd607deaeef6d68ba6a395fd7fe45b67ea489929235515cad4903b756a9579e46a6a089c4a84686a486e4a0819aba9216d84d422afdc5343ce4c0da1c26fadbd9d6d75df7ab3ceb4b2aab0b5d65a6badbdc22a5b9b35275b6bbfaa509fb2ae6cd69d22726c1622852405892b5430792187874beeba29213ce4ae6301082b4fa0aa48299946f2cc2257d83c93c815b90b82987e150183b9735837e4d2568560c980b5c2082e42ce6d8e742f5691285779f090e079328e8bd786e4b1ca53e5e571210f542e3f27c537ff9b6288e3957dac5d94a9d8392cb6e46f8258d9c58fce7ed174d85c73fd20fbd839ac2fb9ce10645185859cced95a71d873e7f248c9c234772e0f0a958828d322b94cca14872b686083e526874310b71a56704a204788196060e156925d9ae49ea8b02297e5875b69cb4324d3ff280008c2ca095094206705a71bed5c1e26b9b40d61059115495758d1449e1284105591a748152e72e7ae9073c51a21a8f0dad5a805dc8d0095bfd9814dc199905f232a240d2316704966276978192256e070d78892ee3468cd494afa1b59c1ed33b7a0510b787e4fd22d150e8713e16fe4e04195dd7146d9c32a3c57f98cf7193c59dcd639eb788da8e02bcf8a4a243beafb15ac141b51c1017992a23879ea11fb656485cbf9f414658a13da93361a0db269182d994e3d3637b89cb8899b4e343c7b1a24259de62e1ce3d9ec1f8f0d2b048fc61a5cf6550742f8e163367bb2ebf842331978897239a95ef489869dab1efd44c9ea8492f5bfc9d4bd8f2599fdab184aa5057ff25afdbed7777fa1eb63ff8a7d8719b83e0ca64d4ce8236f124a23795d0ba9d4b4014253c6b81b09d6b59e077b7ac29e3a4326cfcaca78549a35ea0efcde482becf158e30d2ebb2ad7af40067a5c801eb5bebe928b6ff1e59d11cd1afe2ef129e92ec6a635ffdab416e42db19c571f542ab97e4c7c89259d92eb7f624965c8f56fd983b45a5381eb6118866118866118866118866118866118866118866118866118866118e6300b9a85e2cf1afe9f0bcddc95dc7e499f7a10a139c3837e55555555559590115cd2aaab923e59c959d3e283bebc42334a862d8228161f968a4c0e1fb15b4828063ceb3a865f625096cb49d542376462f713cd5ca00376cc60c0b2d9e748ef0249d2057ab1d68e279f886cc544109a726cc710e88120181b61cc1af5df9176f4db946af5a79c3fd1b0da9f2655d0d797cd1ae17f22a664183476f87752359a599291ed476c990c06140c1878d608df916663a5b5f06b796b0f190d92e8476c3cc305cb3bd1f8a82f34039a21d054a264adf4ebc01932f97cf8b3d98c18d34cd7ea975ef6a7c1daa79166eeee5e33fe1ef6f89b1ef57c874bb0ef30832f470eb00c6a794f9fbc5663b3f7ef696352cd1af57d2c7b8cd95fc9c51c3160af638ed8cfea4faa690374ffd8b7c692e6725281d0f4b1110c71785656768d3f6b54aa9ef135cebe67e30efffa938a92b5cc0e47a25595560945c1148a1e95b628f4a8fc29f4a83e7542abde468f2a25c3afffb978678dd03e28d6bff561625312ec7cc4b207e840a24dc263a6b9fb1f71669938b38f38735f71e69638b337f36be66fe60fe971dc07c7d98c92b59c9143fc658c1c36e9a2fae52b874f6b245ae5b5fae1c3fae8f5e1fb3452d0870f4e1bfe346bd40f3f0cc3d006030ec371466995234d2aafd5c7e3f7305a95eb7b93d8d7f7a76963c7ffb4e15959d9cb21a4d9c31aa9c5fb7b1ff5bcbfd7482ec2f777217a5d0bbf47f426c36f21c2ba16fe4c0c1f870f0b614fc325d8d894843d0d33081f368294f431ef9836605ff1c7c6927eae8f0d657f4cb3ff4c8475cd1f2676d7f00f12bdaef9bf446fd2fb50ccc041d1fe2796d82596b77e4b2ced52a524ced5a17ce844e28546b6fd143d08423f537a01253ba4ee59d3adc4ef483d2399bbd9f3dd235e406bdd7f0edaba5cbfc564dfd2ac94ed05d94ed18320d975955bdf1ecc2517f8250e5f5f92f84b5beebe9f1228d2e6831ffb50a4f94ba4f948d3e0f53dcfe3f53defdf33fe88f29147f83ee38f9e0fff47ec5fdf74fb449a06e0c79e36471ee0c7683cbeeff9ef7b1e26d2e6fb7cd30d8bb439d234c00f7b1ef8613f1f36fe889a230fd8fb8c3f68f3b1d7ba6fbac1fe076d8e1ff0f079d96fd037a7ebb5ee632f1b7ffc90bdcca7e986bfe9f67debe919fb161bed773798d8b797d8b750ec1b2836dd3eb1e966c5f27d74d9f5345c728d3928d97d4b949105b67fc70b28093403f6a76f73a40b28a844c9ee75c0ae264bef16afb47e49df3bcf9fd51cf0fc9b7fbad075df445ffba4be740305fc4fa27f061c2977cd7fcc5511ad8498adb2ff0c95ba28b68190ff289a30b681902c94eca77f2fc61497ed988392ae245e4049ffb14a3f00dadc80eb2b5d906396dd77aed687c0f5b1fa9d5bdfd1eeb8c3d5b95b6b9c91750bae6369cb2dd592a1022533cb478a3542575090588c09ca22660c224c0d1e3098ee9a0d78be23e01e2097dddd43656ef45157bbd6afb6001f287b92544150a6e428e8880104a54056185803060a300880246404808aa8071c7798e0e78ab1a8098c580a2f5e79847c9470f1f3a6854cc80c8696a0f1c84f6d04990c373e49493904f840d1151c0265a4f9443aadd711de2b0afb624e9da088f6ea2e03c44950a027ae3a3ec1f39d046349ba678d1c452132df9129d907ad60498b8af567b97d72c772cff097bf3a9b0a49416605cd80cb3babe04cea88bcce56ef1200f760042bfba0b95232df1e5373bde077bdeeea68e5f93748cbc9fdf37bc8b20eef61cbf47dfca0e61e3da89decd6da310809cf95923564c0e505c0194cb3f9839a7d7499b6c3fb00e459523a2b404e3a2240feda2b9d4de3616fddd35cce7ca51b7f04b979a3511ebbfce4c1e52d3221260597f585e5c3042eef0f1c5a77511980dc5fcb3d2697b5d9e42ec76419998683e67e9a67ee27f267eb160c8e4949e012d3fa2a9bf2880497b556029661d545e429fb21f7d8d44ec9f925771e0a97eacc6570795f747079bf69e94f16ed4ccc5f753a6bdb57763a6953fb2a9b7277efca316c8e11f298a2e718dd78bd86cfe0f2d270cc9c4b9bc728b8ac65597594b5c7a6fc51b00738c50f6aee31934dd9944d59df57d994f3d7dfa46002194431c851acfd108c17dd4234172d6641403f329f9e1886bd42f073b566bc82d0264b7dc2047533578b0c42530ebf7aef41cd1f7091abb7ee675f301cebf191fd0005cd5ab8a009bd8031f4e277b834be1205bb7c0902ec40286046b6f4972e792a65574e08b11b4535af89a593bae0466ee8c60fad096d9872c34a8ea42f2f9260bc7002bcf29434a328c0080ccd488896e693329d8ce6921d361f5ad85cb460e32a21045bf6461e34c204d198053dd132a1070d3b0f207bf821817ed05c2a1520ad244024c68748e6f3c62b8107119a0afc98d8bb441fb627e6a67bd7bb40b00b97c00ef461a92e2004044c28899230d1d606bb1e7c30e9821c36a370a9f3946457e77d0fa1ec656046e635235fe2d3f2e0b2c72cc946c91eb2ec3d3985e9bbc41eb2ec799ed009b8ec21cbde7b6307f010dfbb441f3587e105c12ffc1a2e7d3e6a0e67e128d6ec03fca1f52eb10798ef75fd9d36eeace175df0a3db19c654f262bc1ec2a6f768163e9fdf72d7bc3fb1e2eb5400c42c0e17043b2174276bd27922abc4bf4e1d927a0c2470fbef7d503bbb73477dff26e06b3eb4b3083ff892ef07bdac8ae2f6b06c7722cbdb79e67f76066f7461f9ebdebbc35895374fafae7b501f71fc015f3cb5995e74faa2bafcd9f3fb1484d2e197024e792e7977d75638427626ef31f5001cac591fae64f7dbbcd4f72a4796b2e74e472a7185b55d550d13cfaf339a8ef23901148140ffffa405e4fc75a7f83bed507d24e6d38dabc762b67afb1c71f3fa2683bfc37e89bc7c2e7a0c3f3248e0a2a88c1e16e4062a392d7bad7d881fe1b54e6886f48a1631d1fd78f7df923ce757bbd8dca8edc5801afd51148140ffbfe403e3602b1e3c479d9f4e748c7f9e4b5aebe77ff1961c1f4e7849a50330735a5625fce1c7dfb11a06f47da2d02f36323cd1b691ad4b7cf417d3b0289e231df1f08fdfa1c38909b12a5ea7e5651bdbe6f55341e218d2d323cb14392a65be8c3330748e138af1c8b23e1e6d524a2c2102b5a5618d201f7192b333d50e10487bb856fbfa438970a979c4beebedb1dca6bdd1b61893d7e3bfe88a269603ff63cecc7c61f515ef7d61b79d8c7e30ffae4b5eeed48a5bcd63d6cecbe74a827fa25859a5f52299a06f5e973e03b9c421108cd79ad7bd71616a0e2c8c982c3dd68963354391a8f1bb7f06fdcc2eebf9748abbcd6fd0d29b86f7872f7f4ca6bdd97b4aaa4571cf49d30718ae06ef695e84b0cafa29220c10e4f38dc2d14a35cf57389e02d318011e18197959585955df75dde57ceec8d9e373a9696e85628d9dd3abca34351b2fb27fc8e732487ea5af7f77a5f629c2bb710608e646f2cb1cd3d75472a970ee5b6c9a330aa8fb6a33ecdb4db45fdcdb0976a1078fe91dc4fdffe15fa1fbd5dd41574ef3b266fbcdd5fe9c62b75a4f1f09bbfdf7cfc11e446c72095522a05c0d55297e7f25caeb1a44fbffb699f7a74f461f350565656b69d1d49224acef9949c2f32c1dfb5f9ddfd2dc4172f998e138b929d7b724f556195cb774d27c174ea94c9abd790a9da5a6d67aded42a6aeebbaceab40e0b24ae5f96595cafdb72acff6277fa2b9abab0935a180c061065e6ef57cd5b1ec20fbd811f9e87fd51632b932657265ef0dbc9bd3dd5f83a4a4eab6b35e95e55a432657c7e4651ae62edc8086b5d65a67df57c8649fd2f176d1eda2d945f3e70e272244a6d845f3c52eba2d4a521c60d954c77a4326b28cb542a6f91fed495f6553f642268c611a78b9fe06ae90a96ce5f965abfe075210044170f67d55a6db45f3eb785d3d5dd4abedbceb7ad9ef463b36d9d90aa2fb59bfacee5f7d94596fe6796fbdd1d301e6fa5fd97dd6ba7f6f00cc752c7d646fec61cb2e22dbaf20c904464c60c4cc6753a13ad703c92cee0c8a6ad5f0725de1523f95eb886cfffbc04e2cbb4ceb5e8cc3257f110469080e21315f200c949d503108d31254a6072c7dc01a31c8400098c173fe80011002081c497083c01f2bee0c2c62d2028ca1505d80af2b2e0df4594208fcb1ba2f2ac8a508c3cabea17693bb4daf81227f3f412e65e4995d8422bb8501c2a8e10e81a391076b2488a08c36776c23628032d0d0c0e4f9830f0df690ab28037c193101f89a7329005f4441821318416b2036732b00814e98018e502a0d30a604631a68c0f0e4bbf7624c3ff744ea599ba32d064cbf8e7716f9be3e53064deef733b4cc1a5af5d42994bbbbbbbbdbaaedbccebbaddb727dedfac0b0c1b06160f7bfacbcb2e47e18166a25f763ba25b7546e0a45c97e3b78ec60e1b1439d705762be9011f3a50c556e467132a744994ea615a8f944d7b8d51a3b571530ba91dbc8f64d4add6bb593bad76a6d576dd779debd2defb65a2ed7f78114a4217dc1422a9697698214d3580ca43db77f808226157d2849451915cbcaf403524cc99e998ee58fec2508798e948a922dd545fd00b0f37ddd1d02568feeced1362322722604b6abf97cf860b229a35e6de7c16ec8d4e59a296d81d8152e4d593b0c0683c1e8173279f97611a51804837b34c24511b8e704bfba440302d39e4d57ce09da49e57e1755b84330988e3fd7ebeccf87e5a573767dcdafffb6aeb219513b4b4ceb76141d5c7a854c14e4db4530188ef524dd303ab3e4534626fbf901ea8080802c50753a811a7c953f2e16632113edb960a593d24cdf710f9df3026bedacfe807b94d129ebdad3a28f2cfca13f3fb3ad0f7ec95a2c7150900705d1a0290b976e5072565f341d359741327dcf6553fe01a2b114349be1162d5cb8304ab2bd78e174b68080828292966636ea6ed1e2024af6bb70d1399a50184cc712a3a93ac3a5cb968e329faeecf4a0ca418388521c2d084cc77262e51e61844b43d7eb6c1df2a1a1a1213afb05bb75ef0a6a410b975c98c1d4826d678d7e1fb0c599acc58f0b1a9d97923f608b25d8e2479b4d5f25907c5d7451b824639ab41083996b0ba1165fb408030fd1a139645f80b410078ca13934d415068611e2f0a17069285c7a1c430ca6a31311115122a249d4b1572902511c63c42022922163820928a080246bb5bee0186312b5bfca20b97e31c018b2d91c693f603ac6087178be4b4421130dafd1da9c205c825130963dc03cdfc57296299d94fadb4ad2c0603a9660ad8219e1128d7009174d0f8697280899687ef00b2e697830b3b27b394790e748d28448f2050c9224875c90640b926c5dafb364753a2b7819c007da44e97f301cad0f70b4f2157f0e5e371f7f84e076610bf07c29d945546c20b20d17e0e51e938e0cc1e52e5c00cd83993fd0ef48ae70d90b1710947b842d7900825c7220e320ca1edcc8edba1e7d951cc8f6bbf77ed7bb5f77ef772ff9ea2fac0ed2eeec0c5baf20b36952aa837badee62e501cf07a9530a7a584326b1b38738ec8fb38bfa3d5c0a32d270f8e4fece3eb9c5ce176c0bea58bb68c8074c9eefac319f29969b92583efa1d1bbf0478be8c92734ef966eefad59f3506da42c9c7920536050a3f5152893abd42c0eae971e555604d8bc7999ba36d463e44a46cf643c90e3f99edc207dfdd43fb60e8c1c42eefe74b47bf7b51592c168bbde2e09252512a7a064d197ae4f3f5956eec4b5cce7a9294daff8e65526b2c6dde58923de36b8c8d78f411cb1bb493592e5bb0d8b0ceb1c1e5cce5c490f9f242887623e985d0c57836fb27499b2d2997f4c9472795054be31b6d7039a5a4b874d9429f485b92918d469faae854ddb00235a563495574a6a8aaaa5203859b2ac755c1a93fa1b064b1224473f1a4914d88e6a2c5a4222715cd55d191139c0aa8642b594b8a9a444aa21100000000c316003028180e8ac4a22489e24cf50314000d7090425e5030164963811c48311407310cc43080106008300818659ca22439f006d235f4e42018eb63110d50aee9340d7f8161cfa29a8831cf1f0fd1bcaba05e1849d4a0a3861c6cdfcb708a891e6d65a05aec2e84ca34c93d9dde9c42ea39d237cb018b75a14debed289c44b19780e090b50e7ca46164d5fe29642fc302c8d86498673fa13c08761fc57d40548f93de19828a3617b2477a7349a10242798c5e20a795394aeb85eea2eb1e074e0b012b8eeb7387bee2dd8eca378670ff03b3817c7008854871f932ec3f11492d0dbe12988f5d782fbb644239cfebeb9bf9d2fc85f3a83ab306f5d31e2f7c9d8818ee152dadbd87e476600397cf40aa5a6be7903c946cceb5a71e3a457833d06909fd0025cab195e8e48f7c1376d82a8f54f010787c73c53abb434984780d3c901b819a9db633229bb178e52b05dafbe815c4ee8a350d6517e9189a6de57768b396f5ffcf09b07d667005a213b986cae91341f3342748272f90237fcf9950cbafd67bc0f3e63224bea686a78051b28f0316675bc16c4c66d864314a809d9f53604fa81befa79a189c6c4be07e78b8cf5969fcfc0ca37550743d5a1bdd4494da23214f9f82d0aba024bce2afa37223fbf16cb6e90c6607e78f4d604eb6f6f55cd7f06e9a615420dd9cf3517afd7a9b90e564427174e4b84b122d140bb1f8628d2a9ba0a6d354ca2e7ef3cd95894938623cb081255be68b4b93dc9511a940e4eac17dc92087d8fc9b75f31dc05bb87b7702f6e74c505dd8d2b58a186f662a90f34f54081cc82a643425059d567ee09c35dc3cc8d712c2cb0bcd12323cdeb48f68e3c576242a0df9bf1721fc29da2ff46c1f362af06a4371e7bc116606fb72e640f63d4d5038bd722e58faa11d95e4607e614c26ab90e0f0a90bf362d6100f1258bcfb041f45653a28f6b8e7cb5ff71c211f90208b22ffcf5853e7ea945ecfca1778c29f0e702d58f0735a1f885d21097d0327da6ccde95548d51a557b8a1cbfba81f6397b4a34b4ae8b71a01c0cfec48f8c10116b24eea14751334fc3e26761e0d900cc817888205a689b65be607a5cbed142dddfae21a39aee34288f82f534887b8a046d55f8bd44dac5620d72ffe76719f468385d8bc69e51077f1e1a6db09036fe1883a483c6f1413db043a9907e1f76bc8f45d171a28dc8d8a5a7261c3cecb707f753495719ed5dd5a714847f71e5deef91012b12adf214fdeba8cbf09a4cccbaf47a918247cb47b3aee35ad5ede689294c582b8e7c9f57bc496fc8d0c277e87bb512c1727771785f73d5e7e25419656641ea970500c7e3e1566485892f86f3ba7b039231c98324676891eec97b4d6f45b416ef8fdf3581b5a47d91951a027e8ea0db6e8df091c242d1eacb0c49e9e4865c9bcc10b3a44196f0bee22f7b9df62245190af2d1c6bdb02690f849622d69407bbf51ca95160d03d6e91660bcc2a9a95693decf7de15d4c3e70d9b8facd39f49a5636f9f5d3e6a2a5383c403412b1548c5b476151bd2221a77670d48aa1c52b42ce2bf0f744b0df0bfafbc24a46d4ac6bc48583d46f3715facb207a1e41c829e9b7a29bffcd1f6eb32b8a4a594fe08dde2779282662df42e8e42424d1dad670803b9f73286b8a2b7edfe20188763f8f0e63d74ee8e8e3d585d5201c4d1abe6cca52de773fcba76413f8b6f07af4aa42f42fb450ea6b3105988b1ec23376b2f52b8780ffb74384b20a0493bfb81edde2040f35e5feab3c648f30a1f3122d62dfd7a425a5924c83f89c40be1c4c8166ba486979809153f11a662d4498821f82b9682fa9d5c285ba4f1013bf81a4d7d0e00f9a60e457af2ca44f9909cd28e7eb8900d84c10777ba55702605bff7cd60234675ff846fdcfabeebf6370788209cc7a581a1b17659857f482cf2665645f4273244ebe81be38209e86fc05e3ff9d831235e0810e7a0319acef41f71f12a0432147e00eaf04ef3799aef4d6ee3387a2b6713067b15a914070f425b3df5c54b0b7c2c4a8e47d84adfaca66f60c89def8c0df2b87d7337ccf5648e87084cdd955edb1b4a5b8e2a302c6ccbd5206872af1affc491aae85f5906ff6f0a5845cb1998f4a9a2283b05dfa7ce2ec5dc6a29f672a58e4e6c2ae4aa2c0d41fc6d1c9f3a5ec5c3a0ec991d82e37ba3dfd12f312534e2039c527f5b082ab52c3f7f8ca8c9fdef8080bbfcdcd1cdf9bd377c8b91284ee3364142bed14a0b4e0cc23d5b75dd268665d74b51f8a74e034bce827f3794342b89370e56ce1127bf3b99822d76e31904f764bdc4dbf0c3bc81be669f6115a6406b74ca88f8ad4d748bc777de62f9fa5fa82cc309914da11537e374087be55cc35ea3ac7148ffda82de4b2e6bc200a322d0d6be9315b424e52126737f22c58a6df52efdb25a4933e9fbf46a57b718e3a97cc2b2a211b41aa20601eaa47eb943e77bb9c0d89db8ee47cd2027e8304454a2d7fd90670cf338dd93836d2d035099956e07a5bd985e3f1ff08b6b0d6026cc5901977c5348e06d9310ebc47f6018e4be32e4c425a1b545b657dd6827353fa39db29c1aa7eeda3ab2d789a5d5aa17be8082990b32435185ca3e0fc9a7aaaa112833ef9607c97e7326ec83c42b3efafbc58e25d56fee2dd98cf884364b473bd956baae15eaf55a4f7e3dbae18ba0719a3c1245eae0943f1351d604b63b612dc36ec436671eba8782af3868e71debb98f6a7fd3d3b51a130a0b0208c643fde81a54f7ebfe9a4505ec4a2a8a68b59558127bc550668ef30ef02041be9b5b6ecf665226e460b48a8df7e135232e237b91243f4f3eeaf8ab6c3468646d54c28f13fc769bf9da78d32e7465b88b4fa8f3f700fbf8003274674cbde63d5ff3fcfc069365adeda0a208a69a4e6c1adbd801163d46b067f41d897d5978ab7107b16a9ae9a049a20ddbf2cf829f211bdd876a07ea2893b6450aa18b75d801d4e6a2100a171706ca6e660c51ccad8a438e366148cedb06f8fb89cd66940153d0e6df161605c280d5ba55aae1e567498b0775a8b5185d1a9474b4cd0feefda6bcfc3a076098a31d2f825a16b635663795a01a6498bcd1d752026fb38fa8b454ba57995cd8f30f736ba1495030c56fc129863727d0c3713521c1501222687c47e6342cee7e8c26b227e7593e6130ef39eeaf5b5743563e7c19d959daab8c28c76365f10015f547974012de4de32909b4777c39353a0d6ce3b7aa41571b2616009090ccfa9588a0ecc4c6330259fc05c33c8cb230cfa1e729447a79c2c8ca5e528cc76aee11c29a74a8c4a96a898488fb3b0ed36b37ea431b04a057e82361e5b0558768d716c8da76cc580c1f5d4e01c4f0f21a78895182d58ad270144d1c824335993fcaae33bbda38e07d1da15484a8cafaa779abd94bde040824afb141338882a28e51c190eabd36c7988d9f72efcaa77b82ed368bc499dedbac06902d3af1d365859724bd9ff6660e54adcbfa8db7148c498697e6c62581cf830cf1a6326c5bd3ce32b73759321f3113b3933ae796cfb3b3da644bbe64871f16e68438ba7e635ce8c65c381705be7760a16da6ddb85d34d6e529632bc6a0aed9cfce162fb77afb18fd5271f061199858ca10336eea4ef52a108ac84863b497564040be246b304958a4cb5e68a54cf3a7cb7942a824d0d571e610d7b8562774c1bb90f0ac5cff493077a6242a498ccdf2182ae9dd64746076a07364806b4c3c6650b6294e4563e0d7541f5708d6416af21319d920aa8906a96ea895a8f4e1368123331037e71eca2e94ba8158d32de5c6d3fa3f6b52f390e04429094182047986c1977fb55b45435894c0e6156c13d94ce3cee803ca838b71fcd71a83e93093c3ac0f207440977b62ab62179041b470f91b4108431f1116bd403f8d967e363874438d9a219e96d84a139d146344855cbf1032153c29caf8ecb994ff4e53a347d16dba473e88d59936a69d69dd13eaf3067c4ec0c0eaceb3ff9908e7baa2794522921ee06280034cb5ccbe34d249c7c3612a5beb1df8a8624b89dcc6b07f394cf9097fe32bc7df3607abdd8c9c3c4bf80b7881bf430bc3b561cf07fe26014ee96d98053bcddb1df79c4d75c7a353f1c94c5c5537906cfaec1bfc2efb147ac407747813873f8e1382e9acf3587f1411a4ba00f02f466cbdf9d6e4d53c2cadabadfc5cd19e7f7540ebf4b3c36488b4ba07624ca774ed7edc3b7d186efc7063a44dcaf5ef846c30c87e9cfd49be0a38f10c6e9cb59f8401b5d132bad5f61978e65a55fece5c8346318c67dd364132e4966fccc9bdf5456ccf5b4f5e3b91e0161e0f282634660ee293c76e8c1a561cb4905a52c4e314531fc772eb056eacf3fd064315ef4d73e1581e0b78b7c2e518003b05de86af9587b746c614e2b836acbd4c17b52f00d7faf55a428a299f937007e4f50dd15de571cc601f842eb9e5b0a9908111314183d8b5ad069ac2f9d28465620d1e098361360f0ff282adb886c4a003fb110edbab70ae0c517687381aa1606d5acbfc88b1fa6c709ae7b740f33c0873bc7b7f962cd6979075286f2e4dd023c92886b4a0456c461ddaadbe545dc2707dfec37f965ae8c396ade30ebbf227e6f87b58bbf5c59ba9e2375552a5a58fbb480a927cf532f04d44e63d273afcc1b3f5adc65d5316bb3dfbeaaa57b8e60dadc23239549c8f25b0fc191cdad6141e72cbb9eada00a91349d7f9932b78e34db3da00f8593fb2e0c7626ba71a80b2497ae139028ab0e2fb97c0a0216e5f6c6c9a2d4bc36392cbb5389d7c6fa37cf66710524f0946d71cfe03ca328632ea35f47a6ff9914959d1b2d0d99d2f0b55cd7e6f4c82ee7b87f14c0bea9388726af93ad52bfabcd13a5b117dfa8011c5062f41ddb3b28c57263b4588b8d1e10ad7ecc98ea25e62aecbd4732952f8e7cb668fdea9fc50251fa5b6793e8cde9d9b84ecf523944493e068b09498e2cc86ff73307df9d5a16dd88db88ffed21d071d9cd7af0d6198ed4ce320bc0582d1e452682405e6368a6429af5064827dac63f649d221b88e2d3e8aba867cd5c4dc6cc1a221251cea2ff846f3601538bd4bd24dff3f3e9b6434e93459bdf4e2e3a516066f221cb6c12998f911c7cb79b4fbfcb45f04990aa545b22a1b40517735d023c20bb0f3a745dbfb2905332ed12e38fe10fed68aa83beffa98343378028c9324c90f4977c10342c23f46759b8996cfe9441aa7741236972951c1a90cf7cbab1fcbf56f02195d0de4f1b451e36de5031f1dc665db007bed532fff4bca1ed199a56d174d67e07824200f42f1c9a96225cee0fe4dd2786d64fb70407ca82bc49fa0c5cd3530ab0705f0ea18972681ebc8998984b900c272647fce1ab2f043e159aaf47b0e9bdbe0d0f246f5ee0e104032af451faae3879dc572346d54ea053a808a919518279aae0f873ccd4231128cba21be6449165bc6e355d310944b3e763a3e2740e7c467d10a60c4a5f86351274171aaee934efec61b6d1f4d39bc97a363b230df64c031fe357eb7e7aa1d4455912945fca038de943f8ce29775b8b48adb30bb60ab5830d770d08d9e375260513f63fb5b7a7626d87afe9be2496e939954e32ecb098d10c926bb02fdb3c8098af60bc883b8ffcb751783a557b7763b4b4b2694e5dd67072fef52102c50724de77991f6c92c87e90ceba5c30bb38ba652c24f3eb7a8debd193b10f4960bc0c3503d8414ff77bc2b518c3224a55b03698da260710e8f9a84fcadfd681bb6019d7ff02097fd23e3b7842ba0334f0e045c8b62d6900088392632267336bd203a2253f4e16b8b1e7ff79f775982c910da9eb0a272eb34149e8a89ba9177328ec6a3656834746222e4b2e668348a8dea5c0daf96e9499217a469a422e90ab1d37caa2975a1b04a11df5c8fb801368fada3b70d2a768d31914a1489be799ba3b11243d06eea310fc43354fb37540f9dbbabacdf2fbe78fdcba568486f9130b3cd8fd5eb6eb59ddca3d49cfce3ed38c13e2f5eb1a91812d40015967c2d7ade9edde1a00f1f40e7e07000a827d860b38e211fe735be6bf018c37c027c00702ea0217e8f7eba78f87222640d0f90a30a14fd7d546b0e6c6c781f08cafb6b85defb75fac0f77cadd5c6e6e008820e1f7c2d6f9e86f1d0b5408cd31d477f77ec936a1fd809950d9eff28361531d6756f2a7844e4c208078fe90204037c1b747182d7699a5afe879e0aea255255d76519d0ad0a70782c802614cbe66e978696bb7bbf7eeefc379a8ba51eef7a1b2f4aedbf074956eef11cab7fc610c11a1eeeb4edff13b443d988e64a041e8a14bc3d9ecad9ec6205074520678417e8581501efb0cb73a8a2ceb1c33e137c4b1027caae9d8ca1f93a381a0e3fefee9b25b51150446dda7db771cbeb514efb8c516583f231cd75b0ff3a6a7e51c2fa53125b0b15a3bc268de007c761855cfb70a92c084557d3fed60c3b43bdf861998bf6d56012b46b7d69e7da8e083e0995ae99f5efc49a08628cceece5bd0be064ca667ef9fbc910993fc76b68335a7695881b1db41b65e765914144869aed2870a874591745cd8497248abfe4c575f18e5a3d0a3a67bc105d316e3b65a06f6bb311544803e5d3e98093e022dc30206364af4cd16b005b99fc89679dcac5bcc8b9bb0b900118b87ffc7620319653b8658960ef38c64bcc5b6d20cbe27a138349435cb2cc5eb40dfb53ad1d0dbe56ac0d0b81f1f6b542a784305731760864ddca1afaad34344d2c4286ad5c4365f869a72ba0351b81035d4bb17738654241a9f6d664d08f2db7d33da1ba2ac9b1a2eb6474bddf62755bbc8b3bd76e28120512ec1c67c26d6b057591382ceace1eaac1393e07c0f9235940935c953110ba1829a88831bbf573e427eedc209d1517a36000004003b87914a12954c47e93cf415ea8ad135eb738ae8069565832e3a7332eec5c41989e26c56e6fc999338c23cd49266a4496cb2721d470232610571806253d8e89c5123014932e3d8c40d49b9f48523834493506634f3c80d5f660ac60fb938c702c8d9ec39b133928a3fb65abdb4595d948e8efed35c4524e3e10a72455e4481704854d1e071c648551392525620c67ddb0bf587fc7d4195021aee2196dc77f4618b96368814653e0e9aeb9c5633ea0722c9814a36dacb854b38c1b871b17ea6b9f780b4e172afd257a4f85e534656a6b64d5a0770d25aa0f4fd180163bb34f0c63528651b0b62e4e603b5c45b4a3f4e8b552a6ceb1b6ba58f24e9634afa2687ef64fa48ad47f0678e1acfd33c1d61de8a89ee3ad2981826a9395149f56f67d20a465a70d5994aa5ae7f4f8a27a5a2bd7f6b0f23a714aff649fe993710a0fb999bd10fb75b3caf368ad6dc7dc186be5c148aeb2248d9b1a7b8f23928f0a2e763e456b3f80936eca387fb646fe0729ceab8901b00628d8569b05339484dd2ab287959a3c95b59448f57341292fac3342e02bca444f72b99d5a6a03fd58958d647d4ee9f2c77a8e70b85fc5182b4d6fcf1d037b1da81e2326315ea3a25c0872e5b490d81422d03f849aa29686f4218afcd1f5cbb5920bb645f60d34e9df122708602e530a8da428ca00f9b535d8a89026359c7a38304fc2570c0e7b121423974553e02391c8b54241505155802bd326f939acf821faa1ca8e98929909c704d715aad6d97f0f1800b9936a51413ff1fecc50864a58f5ecc0c2c22f8c372f42707c3bd3c22e476f7affac852b8398cdb38104da26caa18137ab47389925931a04b353822862f595172ad2d6e3dc3a3f03364140a506d9bb4ab9aeb21f734e1e6cf87442b770ec1d6613894892fc07421409e8e012e3ee23bccfc83d94c5fc0bb5f4ca12af5bf5a7518a23a2ede4041abaf1cc0da1c5a98b3db68ee368a86a96cd45d1181c3fb0dc833f127cc91fe3402f60ac02a16828d0faa937cc9ef1d758088cb16e99eb3932346d53d230c92889fb4b8810862717ff7169ebaf08d46c4dd6943ce5571a866f1f3e61c6e7f21a61a3e170acf439c992ade2db92ed7e6620c5fbd11064551e13a855771f4f98494d8f9e880a02fbdc0a8ff51447b32d454c42ccb26ca90ba7f557a02b8609cf00f673572bde1e7912333835f33544597e89b159c9cab85f0bc022a10c97d30edfae61bcdbb3855e374f786532e4803d5b2e64c2b521f3240cf9a20778d3265efd7f9ec6726cea3170b78c53602ccad0c08a55f77f15111bc625ea2c221f447c7bdbfd303721fc5865d5958026b20cba392d3aebfbb3a0ab416bb579c85c064b624a049a889842f8081b9ebef572751d0a9b2286bdc84c42dc3a48d2d1a32de7d7007f8e953ab534d97028c0aa5780d98449109209409520d0f1bddf860727532dc22d1d5193cb5cc4b28b84804c62588350a9315737adb855c09e7b8938f962cf6764be0ca36a64e8a2a03a0522003d550a8c7d6579fbdce58d90e2a702f47eed847a4e051a9ce5f152536906006a13ac5b692a7b0ee260456a79446f80748874feba1900fed3f4ea2d1cf1a1ea074466a0dc8533c90bdbf2af482b73a840a726cca7437f10a10fcc1bb29f2f7b88a4fa047f739dce1e1064a8f9a1a14c52c62bf5502897986ccf2b7916519c0b45bc27a315ff9e6ed040d33502109d65104b491ca0e397447ab3091ebf684507000ba2f5a00077d5461c7b00e9a21b6a8420099fda630afb240d34079f2f6ae7d0854c36e2d00b6b6aa84bf381dc31d3ff230a8e6a20061b11cd8e9dd757d21560ba5f4ac56d095b132589b83ba19c200d36f7480dc4ca1f68a4249302c06bda4dc68502552038e9e0461fd86aa8a24de716363e1961e8aeddbd8e62d3ce14df49128485e1d493255821681011837dc762a44d2e47f55ee19f22f569a7525d102f19ec3dee9f15e328e7c2ced0f6c753162227e0034933a7fa5051c8362ca196b1fb9c0a16ca2f2be5d17114174cfde8d97065c86478883db8c803d44190b8a11b505f432071a60b4d35403ec245f255a0201c74165fa077bcd535b14a45a9cddd4b610e12801698a611e29d92c921c979520d9fa26d352a24a0d4c2f319f390b5336a2f234cc570e2475c4fdc570566613ab9297c848573fbaa9012148c2c5583aeab27204de3967365a103580b546d8d4c86a5421cfb180bed898da6321741712b0e5ff4c4d82bb68bf95339ab6230a78780f6b50c8508be9a4b69fd09d1c8469bb569d1c71f19a5d54fe9649f585e3b4d492ec3533a58ac7e2ff46155aedfcebb002a310775322ffd5a2a0202a9300f55c243f17ac25d814ff251d6359c5de31d82a0ddfdf0ae6f0a3d3154bc11d7f1351470a81905ff477a084f9780e6d8e9360bbbd89c26182c0ed0973d14698d4b381188312fa005327b1933fbfc2b943af0a058bda70065452b67ed56cb819046114988758dbc437a6c77c4386fef6e5f016760ce1bf7cf97f066edf444d8620e0feb056222130eedc381d0543639cb01054da438790f41684aa14eff21816ded6be5edc6f3b8e3b162fd1949f90d3edc357333f48baf7923418df83f78b0d8398da28f1352ce10f7ef2c1602080c2e7248aa293bcb87d679917e72ee0957e2e09feb786f6aadd6c043451ea5c0d0bce3becbf569c5714e89e8f1e6b841da7454e71af3dbf6088b43cdc765015b6ac5372391b0e0538e4aabcb054cc2510a98eddb9d840f57aca960470198fe31cdcf7d2a39647b58820f3e06ac526a1e2aae91933f79b4f7df9a0731ba71bc9d09aaca51070bc644e77cd97fdc5bad04d4d87a4d482064bde52ca4b3edc60f9dfd53cb6517a975b69da868bac43e46f8c8682da70be1e43bb1d160e52f97c279dc2cbfb430dcc6dd73585b74f692148e3c95da483271db92998bdc01a9cf0cea6531b3de089ff729adcd268030d22bfd9a697cc6338853ad3ae442a2b4ecacc6333f05dcc50ba521962d87069f7628a04fd8d90f3bf618d47d4079d410f60dbbab1f51784c58906d635090805b30ac1b491430ac91c7815c649d51f0bf840d34a4910cef10168732d644f8c8825fc9809cae1cdc4c61ece428424bfbd2ddb36a108ce5bde10cdaea9b495bf3a0f70d54497c96cab80234920287fc822acb426a917237451dd6d74794f00698393b0f82132bd3e7a46d25065c27f56caeef2eed667a51b83c1f1b33f5072067c25bd75abc274eb60e7665f21184d91ee076c6ed067fc020c09e08b79d6f3da97ef13441004508d431a0c246c414419030aa2fc780cacc9f687cc48cca51d86b6eaa02077763ef286c5f9dd036f5096f50c28b5f4b560e5469d9fcb13c20f0e113048dbb727b4968316f62de1159b87eee7fb580e29771891872f7cffc30277ac266463c5b215ace0c5925887782b7b6bbe03c212e10a0cde9ee1d420f1c9559cfda6489cb66d9e9a482dc5b68216a945a145d0566821b515b1b63915e4cd8dd5e8b54d1a732b25137e9d127e50c989d5f9deaa539272608a0359fa5f5afb82a1bdf4de1532c749d23012cc3336df63f63baa579fba82f852a3e5092cae94752a8892620fe8f91a06c394c6fa28f0a6a47622a2d59609c9782e5d216356fc5ec410c041038cc0c2415903b78a99b06c848b4db32ea64318a8501681ab2282a860783b1623d2d65becf7db56f77d3a09a011581020abc05531139d4145cb6f437608801b171ee7b159f338dcda7d796481c505a78a88130768cf76f7f1951d9f8a2a619c9d715d7a953ddad9927c933f0b855a3bbf6096072f24cd643e4d6c9e84ea38880ea513b8705cd0423008e9c831aa4dea9bff9d1b0ce4fba863a147bd32ba48c975916fc31f75219f0a78c9822d8f72c52f44838b10c74cb276ce16ee365bacbfc9e3d84c26d99f312c445ddc2ff6ae51d044e6ddc69580ac247f550bde4172cbc0b0addc2910360c832475b8179b8aafda44e99fef22a47050c36e7566f0fb91c7532e7372a628802eb3b52d3a0f8445dfc48062c2bedee05825fed81e61c9227c648b00461a3705cf833510af2a8be1064465d6c4b91db9d66dde4988acc53cf5144e14afd5193bf1fcba6ce7a298480773967b6fb78157a1c64600fef08524698162e3ee7bd472631c71f08a36ac04ae8a3cc1737443b35637f7850a5a63dc42754834a1612b42a00fa5436b159268452d09e17ecf50364d6c3f2c0aacf5bafc02d5a71b44fe822e9abc0f41a40e7268a05d215ae31fac9e6788c4a12513de736272d3c97c296a883749c6ecdde3664fa0ab722024e1a61eab9dbbb09f6d1311a10ac64ff446c5c7b9aaaafdd6b885ca5b5279b6cf3597fbe09856582368a96d0894a185745522d2c5c172e583c0a3eecb4bab49c5542a940783eefd51e89d6a15950644bd23a0c9aa18b438650e582bda18eb3dfb9173dd13f34206ba59b0fb55905152815f94cab5b7cd9834667883ad4922bcc091f5c09b87f7a565f9f2ab7840504a3886de817e9d38ea9bd67f7681cf1db429e6e094d80056f2fb39200e3bf1638ef29a54ca672ed73f2af5356df709d8e914b5c7c7300743cf76e4b47330279dbb4fcaf11ca1fa89769347814e51555a48950b215bbe86f046a18bd6b1c93a41840199b01b3484ec882f5fab54ea0b370527922f56c0d879b71145e5d7f138ff434512be1e4f3c7d3b5713430ab186d432157378b7d63ae48e513497331efb10e9fa1a75901a3ce08777d631d8239278b27bd215108c225ac80aaddcb75f46cd48f43e606f1a4089c06d69a050d4cf13d97d3edd0b38a85fc002dc045c6a1b4738f1d1a037f5db1acbc410f5a6c2c0650510507f560c9174ce0b72848d22408dadbdd68731bf4ed7bb916e0c88e1e0364cb65b41b5400a606cec5d617dd664631f41ebb152da2fe3323d6e9ee3e42f4cab6bb342e21223856d38a41370f76d20564335544b4bda21a2204ea40d52b1145959311302eccfa4b576eabb4e98690a48a8ce1bf38586e6d431dd2a6994a05e2c8c7dbbe3b4104ce0fd92f916265d3dc733fe85e0f0230bb85fb3252eec7e9f13b622fad64638856dd9e733384ed1297a44d91a355230ece99ef70befc208e43a091d37752d65e16578b61117f5d622b8cef2ea12707a828026078bcac7e4e230223839a3dba09a198711bc98569c4b869f5e736692fcc6f2eb77decb892542816ebc37a5b368ec0af38a7186053266a7075033e4942d95960d50926361149cee5ea33e341ec37d763bc075930d64664fa13602ad289da1697453d278a43b788c61b1a9da66437724486ccc1b4dfe3471cd4e6524a4044e57bce486ed46feeb86cb54bdcc91beb12c214f93924659f81bece4de2e77b1d245deecdbb8f7a88caf01284919866794e05fea807fcbd82303c3836906dd81c09eeda26c7f4dc685b637f3d4c38d35cd32f623080c13172e690bcac49094ae457a51c4df7fb3f3cf07a202868c79f2b01cce4f378c692d25a97eeef7b1ae1bb9df9ad01a7dc76431ed1fc69301e2ca2de0435ab9027553c351f2593768c88ee9998e1752e0f2bb97720a3a711523469bb43d6cf41ec9c4c4e0baf20900f1251c27bfb1f90b565ccf0029497d0796727eb81c25c971b840f6a703d2688ab3854d11ace3f075c510092e7bdb1433b5226725636bdc04cc710a220babacbc1aafba6d03728f078a373487987642d7483719f1809a71bb5a1bba42ef6d5a068e03bcc9c4ca002c02e0206c346ae0e6c384d175cec94c39142c5cbacba931c34fdb90d420e8695d48409701b28b69e1257a023d3c89804272aae3e09d439d908dd1f0f4cf5e8fb439212e6f2d3ece071a445b2218449fb310b752dfb227f2615ecb7446abf0fffb6e7a795bfa032a602da0cf6ec45891ec149346d0c01aaded28d9c087a15cea77a0d9294ab5e48c3f534540ce6714837be81c39701f583f1df2fa369f7111fb67d8f4c5c8d54dd94cfcad498f8e83e2fe2ab1ec857c4f5de33ae4da91a78aa24f305aea033b983b60723ae9bf7b55b0cdd7bd4fe6dd53525a3e353442648c580ba0ba6d46cd20138d0235b2732c0ea59bafafcd4cb28d822b3fadc96b6660c4f5c608608be89d7a5635e13ca3c1c6d91ebb7c2a1f0167a6da16397ec0cebffa0390bf70e48bad90b3fff6f2b42f713ad0bc4591134749986721ce686c8d5fb9e71318b5967e5dd4bce83b94d15ea048dd244cd3e0827be122b09692c0d8072a94f007b933b3c1265a7e61a039ba9fab3ef6481415b57640f76894078cdbeef21a8c068c28705554ea05eda9a0f8be71bacda64ec61c9c2c810f41a7a9696dd65f7accb0e22125cc6e6f168831731ae2110c2d4c5a823ae41e7e518cc84e4802ac93f149490c69f41545fe86afde41b6760450060d80d429a06c812b8285f02011f8378cd4a94d8e02479748e0718d2430182cce2aa7add2456594833453022df6f2736049e4a3cd8100e51a0707588f69da2fe0b109603b0b5b6e6dffe8949564ad1daefc6db6ee5c2978b6e4fd97bdd6868c8cc3d871b922036d478959e58289a11d7be2e0fd4e30fbe030302bc1f0791623a6c82c77ff0f4d16a91f61ff6068632bbbaf5520f0d5f533504cabf152bb9c222e19d5a72b62a688cfaa6e2d54057c2a00a5fe737960dc33726e5494c0cfb1f6553dfe59f29bb33a26aa5b7166701f2ccd0f15cff910fae4b1c755624f887213a2057bdb4ff8c0d6939865a4b7fbabaae3152ee58ede9cf6657dacc74bfc7e690fb5c26e912e5826d53011b3f1a35e7de8e8823c3f1a7546e6048ba49e20fe66e1cc5fc9f30a724841e491e7ebb45250b0529e11010ccbbe2b1537ae289f7555d911b09d23a34577c8fd12dfbce2be68c68a9b750d3d1571bc880907f5ca24602c655c746bc2a5d25bf733e63d47981bc9f5c438ad1e58af68239c67cd79bfb7a72d7cc64c9976146a780581130805aaf099b1d7b0f2e8e45ad58b5a835947e6dbc6c0f46cb3c30f6f3e954fa837ddd0d42a90e09893aa435d40e6c3c4701234f5f6c1436becb6826f2c0d760431744f49357a9091ec68649668fdf4c1b4556894e1f8a97981033a15c84cc5234c810eb0a5efe6f7253cab66dbd24849111069b2d88d5ebb906ae12dcb939a2457f2b2f3aaccfa2a0edd55366887cea29fc58e8c5fb34a5c9ff8f82e6b1ad079f30323e12ac30fdc4992add79c2566a250dc032ec3236427fb90d81b4365403b39e4a71ba26bb9c77aca7546e88199bf07ff0e24cb10efff8a286122b2bdae84016da93a0cf988105e3f57c13d9f90c0684984d58b16df506fb4ddffe1c5921056fe76cee46ba12962652f660504b6940422e578c3d389700c44e90020e1c9a990d90883ad7338023eddb53220fb8cb6fe1784cf3988e439897e1f065bad668f2f33a2cdeb1a3d54455e6d35ce2fe56338f1f01210cb06b41e5c9e3806237d30d2201c19b04d801ab291431a4cf51d6914fa77f8b2712da68ee6f0bb7bb898b720c6786b1fa6dfd9106d8834bf67c6a70d28132a7d707faf862905c79800a27d66793559efb612a6a4a13678d5667a9dfa0cd06ae213e3228037dbec4d9b1a2daeb7aca71478c07efb19fe0607588188f8c4db6688294c5184a96106a8ffca6f944e029b28219aad65b2b431245623baa2c1f83e9cf55cdd3d01eb483f1c16e4c21ad68a9b76f12ae904bfb23d14691a9457e9a6459b25a3e8cd5a12676b5619b7e8b6ab24d59805963a0328387d653765239b861115225fb21d12d156c70285fddb53916eb64912b8e80c6039f195dd348b6c1a0d54887ca96d4844371d0b2c0cf451d0fbc89893f80f0dd7b9eac1a8443818bd72ad9d6fec40644c0733d41791177dea2c1a15dff0e67159c8f3870304191b3a5b90ecff9fa071222b7121d29cf9c853fc0dd01fe14e4a89005d945d3dfb81a3dad0980addb0cb18a49ed538da782457c7fb7f52b03963a1cf6ed3a596041b449acd84107dde660a56c014b738fc54456a7e6487e2d5f955605bbbbb259f13d5a14d4fdc61c04276b714710ca8ddb0644de72f71dfc4c3f8f5413826f5a860cb7bde0b73f866bae86c39d35870ec67c51c43b7fddb165cda23cb93bcf182800a067463cb6cc6406bda7877eb5604ab0656aad275f3906c83fdfa4303b817a02f06506aa9df72a49a3dfca13ba3ac8a0a779bf95b8d0268f21ac262f88f93c8501d5b4b7348aa30e41d9c169b370ec1929117ca3044af96d1ca2a2ee3c8f9810143bf7eb8a2540b44811ef3027d72e263546e2ee2180a5d88b021258b31acf86af95101ed60dff1b0941dd1a991c855a11d6d7a77489831217ae4cade2bac440e4d2fdfec0c5f9634f99d86f684fdb276b2d9bc8a0f55ab7ae8fbb91d63db9fe2782ff669a1a28ac5a18c60c875e3ddcfb46bde2c31aa7f46f264e151270766000f2d0710d6e2203c5a69b624557d6797300f7da3f0d372667e201206f2627c96e99cbd55db0fcaa3f89a2d30d375a70fa00822674351a6b34574038e10f956c5c2c8d2ac6cfff6a459a5f08867a2d0a20bd9efbb042a77e9d0ba248cc21ef690a4a0601325c9f00c1d89ca52c9d95c0c3c057d35992c91d69ad0d66addf42a60a5a8d44be719e3db5e4b7f22ef72154262687dc7fd1bc28bb9111329a396a7227320e7a50af6169416f059dc2608dd2a19d34e17ff0b55a821069b6856392a4529aa4a91fbf1aa60e8f62e4ed6883a254a0e1907004fbff2d3cdf61c14ba666ae3d3fc0458d9d0bd06d1953ccc799dbce98b3095f74ce320e66eb5e8a789296a57af7d458193b1ff821849bb7c0f4135ff4c4e111853211de68285dbc29c881d846bd9bfa182ef19658fccd30107da42b2ac0e82fe0895e976e04089eadbc86e7f7ecda79684f9c5ca51d1e4ab08ef9fe8091ec9eebf9b9f6b45f75f2304ecd595d4ecb636d9fdfad62197b189135e4370ffec550a3db1923f5aaf081c4bc89293ba6ade67ef59c713e0b667c6949dedc0076e8860f5af92e822ce974bf0a5269b9ed8022aa996c5aa0b846901d26a7c7246bed4da1b456759d9d384a96c5c457e8a0ff4970adea44cf2675740dec1d138166b36d7790f6b123c8865d199a6231d8cb7e8fe785a0ec340ea06ca873662863d1d8827e3d04c3f086931c15be6747c89c26664faa7018e7ee9694e40570c85661742b19d2e3c6827b4c827dfa518e80854cca6bc4e31e0b23d12694548ac71d6ee81ec6039b438e57fb3faad22ae4c38dbb58b872a35c0aec4255636f3a96c975f4bc18b07b83edda5dbaeb71d1be6e7c85f50e130b29c474367e0fb95ccf01e45cc9f4c81ffc5cd939a9f6befff658ecc8bb8c45988f935ed5b8bbb60567de857d9419f622c9a3eea06a028b19f75c6d7e367c6e36d448949f9f35eb619b30a015670e5927bf99303a6057c3de22cec8dabb52184c49c3f6159a3f6a067f4e0203aab2f6633b4494390cd8076695986398479cd0cc172a6d3f8ec3a97f8be2d01e940c47d238e21af5da53a752e448f994515a03996798d7a77f8e39c2ed1ffe9c12414fb88438a797dd6bc8df8b6e112ec792f0a3b2fceb2fdc59ac42db9588d10f51b2f0f510a65fe8c1c198593c86947393c7ce68340ef8120e2fe696b5f871a844af54004737fc420f84f5f6041f248cc8000f36ed1afb811e9a75ea823d84712d13e40a0abda53cec532ed77699d660ad90309e41e326cac44814e89accb88e7332552b2bed322dbadf7b651163ade2431d0f07ebf750c62551c60a2aca4ac94897326c30fd42fa3c9a362a46cfabf06237398fb14b9f7af9cd0037bfe8c1f51c818feec37dd3c0f5c1353d1891580a362dbd9bb489833329b68047ba0a6d8c0b6cb7cceeb7b494ce20992329f9272b3e5f665702e74b306592198282cff24d8a8e6aa8cf57c507484f9bb1d390c1dca12d2855c071272309288ad138c8f3c82e618a1b32e6565db5528c5e1ca594695dc596cf0397badde8684ed988de094880ff53931cfba8309d8292cdb5be841f3de82e9eff737c14c2cf3ac5f1fec80fdf9bd231586d99d755aee2c6292b865c6e27f9b648c4136f2e673061720db82dcccb1cb2451d2817ce7395b677ccc5632fd183b433f880cab5ca5a49edf0e0881c58132a1d1ae4232d23386ca5c3830c5da01a3c2609ea41f19e9933cc95becd56b7cecc4308608c4e2989d6b1a52cf9248962be64c6ed8b7e7ffe982fc733b54f92416b81a1ffb5020d01b085a391ed61ca36583fa85cd546dc8d1fc4b1ab3e107583a854dac281b990b046df140b35b23263cb1a6271232f84ed6c16cf5e1cc7d58d978eeb0c2a2fc8a6e0cb1c01f9e447eb5cca109830542ca53fff10c32e8c24d10c7af28830bbd4fecd9e04fb5a5279b3169445e2c7ce095780cae16257171215fa64fb153e9fe24e8efd95050247cfc265a705bf337fb152a50d1ce7ac4c988ac36393b89298cd59aa703c936a881a29862c2e1bddc251b9bb02a3a2744ac1b9e2532cd596856dc8f6a45521f50047e3cabbda5bcc8ef304a82c7c78742e56a218c7a85c0a310ad0e56164a0c91790058cd53c33551614164e7f09e6083242801943b28d271dd7e6b96b08631f553b6e3858c31380d5d9ebc32bb4a0f3e66de48fd861d65284083d5213219a0a47b8203ab2881f2f508293e18da4589e1d2763ef1701f831198ce99761233bcf5b498f22fdbe4d2e4632fb2885c42bc06116b98b64084b42e5543a40f4ac7c88047bac01094201b7ca003d4005ae9d4f44730750269bd7ed36210268bb5b7d958e56a1fa057b9304a2ea6c531485929d6e0561b8d20bc5d08f096429bd6febaf525ab467e964539e8e30083baa8725fd631a13546e333709545739e09ccb845ac3917ab2e5015626d13712a6134c45a9978e369e960252026d4b9a784d40fb09d691fd8e5eb267592054fffacd957e61644fb2ee78beb36362c408dd1298636546d39760dc1b07b48b048b5f6bf0fecd0a5cb12b43fc31ca48bf5cddee43ab8cf845181db0a5cf64f62900239a64ef09137dfb6d69c3c26164ce67b5da10b78744fc684836e491851881014a02c67dcdc9686fe3cd50f8097080897ac274b4c1d0470991f44d19790edf3c33709216ff5dc041e06552b6104719a8f13154068fda512a86a53ae1ffe99eae19190c41468ad3a40d053fcddf96b05efc4cbd01c58e31e0f2a9173c4be95f3427e370f8c1684845147b643687a70d3434acf7fb0b6d32ebb1fb3c3a8941180b090508d677f7b8d642a9b852415f3ec8d0eedde27ff6dc192c961a3d52b1fcb98bf4b937ce41acde6e0b3abc1ae39caec8272f3098af83a77e7d27ad37edc0fd951d52c630181f188e3fc0764990168d181cce8684380dd0492b4bcb1e806a23b9c9eebbc00af3cd89d5f2f00c3981a00eed45a1715da5b506a885da4e047eb0a076ad72c935c6e858dbe9723c35126555a73c5574e0caf19f007177cdf244cc18e8a6a29ed7186cd93568fe1e5950e1e5bacbce62895d3ee762a74a7129a474312062d8a1a1174f7c8cf0a0b4fdddfaef4c1a1cb6cd0349306206cd834b4dde162b54fe552813cc6ec104b68a451b878300221d4741575f216bcb3f1fd2aa0746355dc59a66635c4a5b0a804100c21dc2c34294660f0517c66e21f1e32b64ebccb7ca2b3eb807608ed75ace8ad0be196d6eaaad759b968b431be73afd5e2ef698144c40d894d00944f4558ac62e64a422957d77a40a4b0161b3ad71761028573c7cdc84e1fc6913c5354079d36b06deb03733437d7991c078972e001abb735d3852ef6af57e52c35ca74b0d05a85e6d2ed96d38c511b85a5d9c55b5e7e1f4ffc131520c60fbb4238958001508c59cbf038d34c399e806dbf5a27a244ae5c45b245f1531486f9325bf9142170d79c580c9cd005fb25947dce12a1f92ab8d66e80230a54c773afd1ff22116113950bcf0a884c952282d70a7abb59e5870546acc0256e2229c9fe3da6753f19ab8448de781293b8353c4548c78431858ddd7797be16ce0c3ff0bb6b67b61740032fdcd3e52699b47d663aa46594bf99edd0bfe8a86c8ff12ec4e9aa12fd7efab5db754ca76547548a92af8583f694156182fa06333b3fe34226e100146c4724ed6737723abe574c5367c85cf224b838979b78c0ffd78b058e7fa50e96853f30e4233602902d0a04af42ae5fbef101426af41260b09a244018f2e95d753e392252143c574b9b51227603f5684791ab2b85d932879a050a68e99a05061a162a471a2b477330048363060a5e06c2300294fa23fa9d566a1cc41f8108a7c3a7528c632498746c4a096c2d70528df611fcd02aef9a32ff350b35665329dfc2320533d551057019f3791e6e42b571f04b1211e18f095f0d05514371cc03b71c752e0fa892ec08a11002b76c7280ebc26953e5b01e5fb30b9299f1425c5b72b40acf5c07f2424232d4e4e4663c3b39dc03e32e73a920076aa3c328db0ed1311a71f0326614d0d1c1fedb8365f315e04b3c316c31f7a74517a6849345e7dde0bf0ee4df1b85f33085a6748d1c51fd4dcd7a2ae6a173ec5a3dc69bd9346bb399c19878af728e9891c19786093e86cf3ec4167527aa4345f675ff16ed23da3c648463fe015741c699ecee8fba85902c365580886cd3375800ec80cc04dd6b2c1dacbacde69d5a9cadaa28ab1108150089d7d3c209dbc2201eae15f68ed483bb9b9506ddde585948ad864377acabc830c82ad14296dbef58efb9520c265328d1c3470cf8f0b56b4a88dd2b1340e57942d7b4269388794afb3a452db1ef25b71eb5f643dd05f4288b5dc262d474a67a2e78726e8d0933c99bc8e797d73c1896613e14cef89ab36c771379275e7a00d775a4a7aa085a2a73c4643020273e55d61400adb0a113476537fd8805e4ee2ee46bfee6d7f87434b668904081fa5e0f18701834fa3cb099e5d1f0c90ce5077cb097e9415bdc132f281711a9ed188da63ef84431735a5a7e74750ce9b146dd05e318bd0a3b602c81918892c02856ce6e6a01906a07f4e7b7396aafe005ff9fbdd0b9e2477065820617617abe2c607d75a1a44ab4b4e3b0444dabaee7b583b3695bde2599402533a3b20ef5d5d89264e799be7a89e65eb57dc2214b47036bd99c6c204771a1b453e49333a13de3c5cd21789fd94a0ceac3adac0e8f37e3aaa0d1b81b0b408ba25fc0a21e976f024d6bade56226125831a11ee174653fb68f8eaabfbf02f17a37235329dd73b972b493789197c8ff9a3172e40948c9e057d3ef35f0bec051c269eec734ad0353d532bb81fa0e0710fa54ad85ab317326e86a5543c7cf183587f48fed52ff3f394036d8413bd110b2891e45f681b1b4aa75cec9db3c64ba37259501a31b99efe8192435402a91ef44b0867ee30919fa6dbc4c21b84589d8dc781fa95134fa8eb333e1861cd9e50178d8dbf1d1df7c2688f21530c0d328730130bac02b2a8771b6c0f85e6764595f14407e5b5bf73e9822a0fc71ebdba3e3dbef7c6959ca01ccdb76516c9ecc4462a93c2d01f31f065f7a302ab783cf1c4ae16b35f79a4a98e5195b1d5457521ee49a533f6e93400f772565a59dfda43dcdb09adf099bd207c2cf37148cf1a8e8b1aca1f56745a9882493915cbca373875ef6335b18d271535e3ffb4a0d45818679a5e215b939b243812e8019d32e555c6746c6c5696bac350051d7ec33b3b02c62ac93f3735bc4537c42a262a29c6c876dbd5caa0a28db465575a4b0c1f2cbb0cc65b3b55437983b95ba8383b8caef0fbdad1e9790f644aa5e598d6b17bf55037d641adeada684f36a483821826cb707bb3fbeaf8c72c116fadbd694e6711205b3422abd30dad5bb061bb3e9b6dd2fe1b255395a22b423ea7b4f6783424eab7377ad5e67ebd021797ecf2ec031e91580837c94624c43e404f82b38504625748209c914f41b87b056be5e5fd9dbe4ec29231c923053eb5645b36ce3d5d13194a654a50e0be21a113b2bcb8679f6177b5f15af16c6fccc9d4e0b94d74cf7458e0cc66684aacea0abdf7bb75672825f59b4d4aaab0657f3ec6c96399846b4bfa1b2706a25971aa711b62755d57f0747159037ec30d392a058fbc7f85c193f48980a216a1337928e4988f7f4e31f00c09660dda47f92b5ab673907aa11fb6d762e809c6ce7d3bf837e7217cb9f678ceb6155ca558eb256c415e8ea577553cb4a8eb2a2881574e5a7e256ca0aa772a58815f4ca57e9564a15aeb2528a2a68ca97e25296142ea556883d27b0962b02884a8e95a0c28c1091f70e4af6b7d268d1d4eaf4cc5893fc714b1c6a8378a29428085a74522bc4bb52a240a3950eda82fc5192e0e35bfde6fb87cae9f7f917643c5acfa24f24249718e86f5d4f8ded08b897e2a52de85768a8a058678ab8d6c9ffc6043c8eb1ce63690b0985f501ffa75ffc9fa12b0af24f7c7a857eca9ae257575a0fd267fae72e513ffcd66fff19b446805e14177a439f5287a0ab0e1a0f1a486c51211cddf5f46d261fafaf3580faf4867ea547e34a775a4fa327e867cc57fbfc81487d505b987bd7f34d22e25c1eaba111fe723f09d45c9ce6ccb08a5cfdad5fbc0fcd4efd4cf1a30bade7e933f9f38a38dd17f1af55a3204b545bbffc175a2717c545eb91de13de5208d75d9e71872938ffaa476fe8afaca8e147884eb43c6911b16505faabdb9ee7d032faedf4b8a81bfa951535c4f95ee0ba4c7fb322cc2919d783630d0aa25bbbf8b84523a1f9e1fe004f57195319685d055506d994097cc07b137651605cd8814acab51176a7c68aabb90a91ef5b24fbd5782d0e79e940820f3e26f43906f00a907cc1e9c0d7414c6d28607c7c21352ae5b5c2dd8e6cc8ef7bfd3c4c49ec516c702adb205d1b5a0ccce18c8fda56e418b66972ec0f560f0725f960f70628ea69ef092dff873b66f6579df76d0b119a71f05b6f4dd9f2e1150c372edb745734d6f9f2ee7a973c71b35b5a4dfb4e48d1bd0a6eda3f49ee3444acf1c1261b558713d2526f2b37884f4567ad6caf9b7db7de803c7eb7413154b2a135635b850c4fb2585145cbd935647ad8092f5b7a178436047ece441f11501040c2923a468ffb199af514a9cbf5e765d571a71abd21bc9316e70b0ef541cbd584a547aa549fc7421583fe249432dde7304cf7391898b7dc4043fdf31f3aa14e4ac0dcfeadb5ba8dca6df7de52ee1df510c211aa105dafd9677f91af92df6f92dfafd7eff7f92de87a44c9b225c32adeef159690345eb0c4b0a6e2fd9228c3c46a4957588c78bfcb0c5862a4b13ad3020e47bc5f2c35665c41986a5b56f17e9b52561c61209912622adeaf46912e691475df9bfbece9eeb97deee9644feeb3f7e492bdaedf3d24bf7b477ef77abd1e5990b31460da1881d215ef5ed5979345142346b61c89772ff9d9ca45db0f156f60bec4bbb79481102d36d1aaa91e21e2dd635a3181d166e564c75bbc7bcdcf4de70e28560d1b56e2dd736636bccc8627fccd9b9bcb47aa6fdedbcff1e23e3bef052f4ac9e3f178519e5b9674294bdde74473a239d19c1c1a39515cbf7372727272a25ae6426885f151a726de39cb33272bdeb8b99938c63b8749245d2252f7384d9c264e13070707a7acccecb3e31c71a29abf7194467ee314f98df3fce20345520773ccd59678e3fce4c8a164e1e6e6c957bc71dacef8206bcbf1e20b8d78e32c4b9c388bebd292252cde3841d0844c8d607194638b370e8b2e56584a444c9d88378e33cb4897b28cba77f3d275f352eeb3bb3a3efb76a3945a9f5d379a664797a6eefdf76e4e6e27b79393dba9b57df65d597eca53f948f5bd337bd23e69e9a02db485ca7df6b6ad355b91c695b5250d9a0a34f1de318d634ebe52849541a123debb2591df3badddeff76e37e4dbb956cdabce8ba57b166eb3723e65545e5260deb45011ef367d7344a9c6a2468c2942e2dd7ad98811f684c71ba928f16e971f80d9ba32958666cb8d78b74c0c9c41316122c79b1bcf78b7cd058c3031e3ecf9542325dead73eb77fb1cf2bb15f2bb55b6519fbd3d3e9f9dfbecec312ac86f56a9f59b05f29b7d7e7616ca9ab16a6c9b1cf437ebb6fbcdbe6d386ec900532404da9278b348b3105c6e6c41ba3189379b7c4184068c1b2198c859c59b5d7e964135e479a64cdc5cbcd9b21923c44c88f1a0938a37dbac00444b103837636f5de2cd3a3f57a54b6a55ddcf295755b9aaca755dd71e3dd635bafef8bd2ab5d6a72a624594352da4e06011ef15c9620b9b35415484897989f79a3c41ba34a6ee35081a4ec3c969b5b2fca446e523d5d5286df6d9f5dbfa5b47f9f8ad959f758fdffab9b31cb2e304720e4d8c786ba4037cd09032f38508902ef1d6491488a9522356c28bdc8e78eba605945021c360a12166156fed9440ba44979ed57dce739fddd2729a26f7497549e992ea5ba52bbac115d32f39b8df398ac7efacdcf13bebf89d9f6e9c3da7e5a804a2a6d8980873c2868978676408596a598e5934acb8c43b675131e25942078b3430e29d975bb9a349c694a224de99a9041343c5aacbca5c9378e7265286133146b8acb525e39d9dafce7d8eb76af624807cb4d957b3d720edc0a64baae7a595335ae2c4ccccc450309080a003a3a4cc491c77846a4d9b1c3d8e6cf156995f82275c3fd284d01931136f75c9c30a0b6d69b60549166fd5f931b08478b91961a2b68cb7da64fe569f59bf55ad1cd9d5e3f1b3d3c07513909f829e81fcb4c6d941909dd22f36c916c78d576d7cb6a2b2046d0b6d8c9129713e62e5cacc57979b3566e20dff09ba963ae2aa760c898214238c9b17346b405489a1f453015a34b9546809f3c42dce52ea5e8095613ae98a2f888079c8bc51af3acf1838c806ef5edaab4e5cd30b7bd5896ae0ddd37ad58172f4ae5e75201cf0ee21c57861f0e6455ff52523bcb2577da908bc79cd57fde807bc79c8577d870cde3970af3a052f78e7a4bdea14a0c03bc7f9aa536802ef1ce6ab7e7405ef9ce4ab64448a3cec449975a4fcb4c6ac37b9ecd308f1ce735ee3cec5798d2c54cf14bc71e45e75a012de38d157bd8704bc719eaffa8e1478e368bdea14c0c01b47f9aa038dc11be7f86aef77f0db263bb6be5dfbd65d16df928d697ddb84c78c6fdd7d8dad03b9487bd581aae0ed3a5ff5a51c3d5ef5251cf07695afba121537ea55579a02ef1ddcabcef445ed5567f202efddf3551f1ad97ad5874554f751b39b7ad57de2544f4ad1f6aa27a180770b7dd57dc9b6eb55f7a98077cb7cd575d6b454afba8e1af5a9c6c5af4e1d7e75275a572faeeeaecf346f179ef59d537ab6e35b6fff35ae503e5dd25e751f2ef0669daf3a8f1bb6c7abced306deacf2556791faa9c722c1de650f6e1815f7aa0f45c07b8dbeea4725ca5ef52312f05e9bafba052eeb8d57ddc21678afc9577d2886dbab3e64056f0d7dd52d58d15dafba8534786be6ab2e7c837f4ca8aee3a4ec55d76902efdc7cd5878ec0404e78af0fa404d6716b7bd575bea84efe29196b472aea2758bbab1d7e9d64c4b3189ef5f535b2e9538e5b82057a8c5777d3a7345e5dbfc615ce504efdd483ebc17d521078f75ee5abfe2adf2bd58917971fbed63db89edc908e1eaffa101386a5c051af3a0c857f580c5bc07bfd210bd87784f7faf01b78bbbfe1f8afd6a71546e040cd603fb41ed64004bd86589d063e50c07ed63a5c5d84540b90a863cce6cc8a519510114555c44423a894d83da55a23bf231aa1f53ba211cbdf118da8fa1dd188af332c8846ac389f7e9d31c1cbe2a50e2622ccfd3cdc4991544f85388ff3e96bcc3260f5f7ceab9eeea41c5209f052a2ad56186e10be82abc20dbfca0b40fc193e3001a0d8c112074a1e923a2071c0b0c105a8a327a293d106cfce41ed50d4e4d93bf8a076d090866d759980b3f79a5d01885fb8ba3db8071874222252b109ca4306269ddf4fd0d0b0e8825206270eeb7f60ea80030f1d386c00f5e4b4a1c909ca83da218333b586c0d9df1e5aff0100c2d5edc13dc0a01311918a4d504c459ddf4fd0d0b0e8825206271d0074c041d1c35207250e491b20416178bae074b481d864a4c1b367503b1465f0ec4e6a070b4c1eae6e0081b3ef2c6f00a0030e84abdb837b8041272222159b8a4b3abf9fa0a161d105a50c2c071e96386c00f5e4b4a1494306a56293daa1089554aa80b3c3544a0e3c7410ae6e0fee01069d8888545c52d2f9fd040d0d8b2e28f93a70d840092ae909c909c3860b4d471a88198c983cfb05b54351d1b32b15d50e4b4d9f5d8d9ab282b3ff90531d386c205cdd1edc030c3a1111492949e7f71334342cbad06e0095e4b4a1494306a66211116908834cd4c71470761e1aa8c70da09e84abdb837b804127222621e9fc7e8286864559d8f3e4b401a90983860b198e988845a325cf3e543b142979f622b58385a40f42d00167f701218427a70dc2d5edc13dc0a0132161d0f9fd040d0db370b7a1094306a6e292d2901365f780044c38fb0f0848b0a1498370757b700f30e8182ee8fc7e828684401a32305d281e2d11958c923c7b90daa108c9b30fb9da01c307449f3d0219180167075620031a32300957b707f70083178e747e3f4159e832158f9492907e80c1ec0568c012ce2e21010d602a2e0957b707f7008f883abf9fa025a52422921106cffe533b1479f69e1fb58385a3e067f72000197076a10f0258524a12ae6e0fee211ae9fc7092908c2e1cf514a1a8e0ecc126a824240cc2d5edc146453a3f01c305cfeee6a81d8a889e5dc761b583d1073d9f9d080909671f222261b870245cdd5e514e8ea76a8723cf9ed50e440b4445403f0167a710f423a2917075db7723cfdeaa1d8a2cf454c1d985708f91305597b21709b3ba94dd82705597f270e8697622f2ecabdac182e7e10e210b8b25e09c87ae0561aa2ea5c2550cd6c79f4f27cdc40ac2a1d02908878413841be9b5078542c220e19620dce9f3b503b50369ff11fa68efd1ce23fc09b71ab5d5a91de156afd41baffd15f6b4f3845bed4a9dc062013c80415265eaa735867370861bca99b61bc26df588dcaa721d6e554b6daace57919e4126cccb6bfac35e266acda64e0f50455515aa5bbacb43318496a9e5e1576fa19a3cbcd6379a8030949a54b645a4a6d77a0a89c88b30508f48bf7a4b2472a40f741c11f003a2076e1e248a809abf0ec221b0056833fc41f035f0758fbbaeae57a80f7a5ed300061f0251cf6bf8754f5d3d84dea6c1f732b1f7ba675d8398dec0a9baa47dbb21e887250c61f2210d40fa54d4c3942d3af9500056983a3f08a00009284235115913a2450c163088704df5d002a3498e36b8b9b45d0d109cdd80a291e27a7205490ea499184a3265a4d020d6800162ca5c8b12333a10240cd616313b489435d92d556431733e4db8f0e1230d43420e8e39468683cd12ac74c90c37665654903abca08abc6963458559d447d70c0e9078a21c2283ce0c8c14501271635a48aa2d44763246a06acb02724339630a97a11c1cd8b22e8999f3c7ee4a4ad898b132ab31e6981172a4c92a71d5050c1d0b668c1ad09d0d6162f522461a138cd87ae226e608983735acb28499bd3519a22441868993a926ea15c743c4158c76075ab30c0815254f00c831cbb26ac0a07bba508e10a4ca0a855b11164bc856266457369634718e255fc02348c88511c10389a388973354dc9a785102559e703164a256230574c8893637d2254bd6943bd89c8d5929a3aa8a3b2367a0209330b1e165ea39972684d4982457de50bd2e662254b1a9b80ed32c57565c60390b83c21801479b3151da8030a912126b4f5fc8bdf6353fb5b16ee1ba30b04bd2ed11731645b37265ed4b89279a67cfedf9e2c973f3ecb579f6ac9ebcb9679667ef6d400440082149a5b5958939e7b575737a3af0afe707089789d9dfd7a8c665629a526562f6a19e76d34f39a9d5f2930aa78fb2ab6dffa442c3f293aad5d44f6ab2477e02019aa655a6269d705b50e667cfce1c969dd65c969dd8ecb4c3f1ec192ecf69a4a6ca4e3ecf501fdfd9ccf59dd57234c7777603f29ddf3ee738a0ef2c27c1771af67a2a286b6516d26321dffaeab30b55c13b6bc57df6dc15a67af6bc953b086a6527353ba9aa191dbcf3f27b64a71d427b83474d6303ef7cfc3ce52147e50e8219b9834508096f350a7d836a8bbae954099a83b7ead42a6b0a733edf2d2ae0ad2e7b146f2c993d362978ab47750a292a77103c66271e9e901458a8050cdd1f103f0be971d0793c3b08720741cfee0109b2138f670fe6bc461e21702fe7fd09b384bc884c08e735fa7a54e01e1816660505b9af718757060e027584591224ec5ee3cb0513ed5e176601bd880cd8be465e2f0c466a5f621690179101b1af11470ddc54ccfaf9e1bd813d4065f9f8e8d7c89a81059089d93f64f5788f3087e5a39c9d6f7002de026462f622b2e71167845ff645013bd2038cab1994e443010490828d9896e4338a8cb712e04832828514a713292f72f0bce002000b0d35b27a4891b8d1f5266563828b962b38aab495b9c0b2e3228c87155e43cacbc59a2c176046d8d2f1e6268a6c88153017553a2e6c402553316c44d0805b21638d8811ba4094a09b92c56c0d4b159ce8061531985b43e40a4d6acc589a2125e452a448b37cabaab156e560525340b9c39da75f7e374645a6e97755a08a395c9c20afafa9b77f3ffddad2e2799f7ea5380384e688e1a544e951bb480f5ccffe21bb03e55cd777ea064c83a1e99308c5b2694e814da6d399b314217a74d911963bf68307fe7ab8aa6378699abade39859c739b87bbddb16debecbaaebb765dd9b555d7acb566873e74476855d6cb8eaaaaaaaaaa4cade69245aa495855aac7d50d4ece03fd74d33ee4752516a15cd54feb71754fc0e9675555b55eb3389f073c1e8f209c71da5499acab48f5e881a76e70c39d46d7357d02a1d24d74fd43ea749ce1eafca37ecacef428e7657a941da884934e15e9bac1cb0ffa291fd3a3ec6a34559de95176605a4e9f4028b8b889407837dc70f6d6c98a306ccb686ecb4e6e6e148d0c2f6cbfb37327450606f73b2f5b345e57c0fcce47368c8709b02dbfd5e87aa34533a2edcbaf2b4fe79ce1de1d29abb56b6f9d751aacdeb24311325d57f218afabaf43364d1fb1507da4e6ccb2e1a5449b8dbeea5b3b59f2532793e6a7d14f9d4f1faf932907ed4c93d407e458377316e244fd8481ca9a35ad63ac7a513f8da044751870b06ac2741e637584587535a92af593ced89426c963acbaba54d3321cd0bcd44fb9998f480d4672b8d18a55cfca0c85cab9a5e9a71c0dfa99730d76c79fe9f1a683eec943457c348b8195654bcc1b3171aa9fe02f307e9860e162f926de2954c7b3ee489ef50f4cbc9e8d2165cd1c8e64122c2562960991675de935b2d1a4d708e4fb9f1fed3f3f3f5efcf911f33f6759cf863deb48af913d5bbe760caf51fb85d7f8d3c1fb58997a1f3f7a8d3e55c8bea7c7813d3d4e84949d7adee3c51e874aabf13d1e7a7a3c03704f4f9a9befe9e9e97195aaa7a7a7a7a7a7c7d3ae07c0fb9cf81a7d382ccbb22ccba60d799665dde835b21c3cec699787bde835c2ed267eb143152dafaba4bd760b4fbe8736f73d277a8dbd295832a0359a4a52b869a690239a5b58149692241a9b6fbdc2f035f630795a80781e17be469e249aacdfb913edbcb85b71f53b9d5dceb1f30ae82737deb907f06ec702c4ef787619c7aee7bb9d6fb77395c9e2ccef76377eab4e57cb76bbb4df95e50893453906bd428b772bd2c69c55dc8480897735b68e2ab69c993961b32b71c5668adc0c2127e2ad1e1b00460acd985609a913f156b57e374304da1b2cacb934f1569b2c64fccfd79ffb6bfca51beab5a7458741e135ea28ef13f23e1f7a8d3e2a5c646c754b46f47a36be97467f5c9161e6c708b9b0b8b762c7f77cc26bec053dece98a100f7bf035c26e0a8587157a964db27836197d56c59a675de835b2331041664819189116ceb6c6b7289ec059b9b9aa2a638b5b1564bef5a0d7d8ba84d7b88beeb039f91d07bec69db5bad7d1a1f13aee441d9d2894d7e1d1c94b9dd67574b48e0edb91d7f1e9642c1d5793fac98d755c5566a7f475581d1d77f5130755564bc450a188b2893f9c914aceb59965d1999be286450b246d55749aa8a1359f69b07ce978aae33ace46d99e6559b6132cb0f75a1fbd7658ebb503cdbc9612affda76ddbd65bf7798d6d85dfada9fdce7b5ee38ee735eabc9e26fff5df6b7c753c8f37e379eefac98d794e44ca4eadf3bc089585ec791e783ccf40dac67310b253fab5767c5edcf346fc5691cf7335999d58e7b94ac5bb7a1ecf5dfdc400e6162b492026c68cc43c4ff55304583411336506848813f1869d3c4f772af73cf5f83cb4eff5dcf71a7b566bd844682193e258176ff8595606084c7489ca5813e24ccca6a07bd6e1d7c84af89dd6ef7ce735eec6ac7dbd09f14241e2eab572ee4c0b122e2f5448c49a05d56bd7798ddab38cf8d6dfd7d8be46de6b5c9739c2f70a47b87a015e7fd0384277f8c1bbc411bac3579989aa2bb981f7abecf056a54b6f55dde7a657d9f42a9b5e6553139ab9a618bf9bac7e376518bf9b9a9a9ac6a4d0f0a2448b8d2b2bde4dc81038bc8089127b0316efa6e4afc61ba60e31687472f16e5aee3871d616458e9588771313c5dc0e1d5d6a101111ef261b5658615248668978378dd1304643db6f0d701ae034f4e03468d0a0a12c3fbd516f9406b3cfae21eea841f95bc38bdf1a5cfcd6e05c6362466c2b0795ab786b40028049862bcb91182068c45b43520ab94679a6c6dc58bc352c73e45052e5a51c5226de1a985b9a88c0b116840734de1a2e34a966648995b129e3ade12cc359062ebf33a46548cb909641eeb3674866a8fa9d81ea778616bf3364c890211ae6055b8c2c232b56c43b03528673478e1b544892c43b03b4860c23716c72dc2811ef0c2ad20a44101441a6dec43bc308512db2ce5a18c172a42cde193e60264a9c16882033b4e29dc147bae4a3ee33534f8ba9a7c5d4d3626262622acb4fbdb05e1813d39289c56fa615bf9992bf999e474ed4889c51d13d298b371392a9dc0143a943ae2cde4c492a2b9e6042e8904122de4ccb1a43c8c67278599b8b37530aedf1c479054b0d1434f1664a40f48845049116489ec49ba937a2d81b517cfe2ef690c51eb2d843168bc562597eea5df5ae8ac538ada28adfc514bf8b287e179f3bd06c31c3a54bed4b19efe2545b883219459a5125f12efa940c1b4c80b8a04222dec5dd91a89c192eac2b63f12efebc69a3824794333514f12e761933d6e50ada5bd44bbc8b59d2a52c759f97a24bd1a5e8d2d2d2525cdc92d9675f8a6b2e4dfd5e92fabd74e2f7d2335d2325e42b8a8e124810f15e4a2205240c182a2268e2bd94b43056a48eca11ba286bf15e5a0241d9a4c9128488f249bc97986c8e2b6b64d4e0d060c47ba9698204151738ce9031e2bd44245d2252f75989d754e23595784d252525a5b2fcc42be3952929c53995b289df4a19f95b2997f8adb41c51e3870d363e7254c55b093982b6a418161d0d3722e2ad942cbab1b6848eecc9d58cb7d2726a19834c4d19922ade4a4c658a195c6054b9e226de4a3f5371584c9c69dae6e2adc4a391c4a391b493783792de9e772329eeb32791fdfc4e8a526a2525254593dc7823d225de88bacf483c24120f89c4432221459148fc46527e1ef11b09090929cab5e244931b724faf782321bfea4d324c0a3134af7823253f5f95498226ab8891b3782335bf0ca932a34637264bbc91c6a44b63ea3e63c881c390038721070e038638380c51bf3188f88d412bc46f0c183060887e2983a5cb8b36b078634022717103cb12af3264bc31c420c814158da6d80e2ef1c6e044e5c9c5e549143174f1c6b0e6a3c8549a2a6ff1c640a3485d082155985b6ce28dc17976e1ec0207bf2fa45d48bb907641eeb35f88a3bb70e1c2850b172e44bd8ebc8e3cfc3e721e398f9c47729ffd28cdd45106f1fb281f7f1fd5fd3e3a3a3a7a81922f7225ce9a707589f7919a193e7e640119239622de47c9216b6ef8101345c62dde474b1bd1252e740441ab118df711b34c8db5b0a91d5f67e27dd42c03e39a1195e34a955abc8f9c3938883938881c7e137398c41c2631874924268951442291488ce6a048977250d47d36ca491ae5248d729246464ba337bf8d8c8c8c8c8ca21e9c6e65a6d0b0a26449bc8d9022bc7a64e9814cb122de46c934e9529abacf45724572457245729fbd288d5611ddefa2b9df456e7e173d3f7b11b4a848ada82d65d3723b729961317fc4bb0839033568acbac8526049c6bb2839648b0d29244c191cf12e5a7a9935bb7c509d1d81c4bb8839024c13ad263f966f92f12e62f65881c6ca9a921a51f12eca62218b85a7df16a216a216a2162c10b11065c182050b16a262e99258dd67a227d193e8494404fd4cd4e63711119bdf444444445ec68e3458901d4b9489375154c9151b7260d48644c59b28c9854b8c166acc66803de34dd4a480c646268697b62868bc899c383e2ae0f8a8b0e17705ad0a5a15b42a544813ad20f7bbc29adf15d4fcae50a14285b02029e2d0c4a80a8115ef0ac8205abc346173a5e54a34de159235d80a8bf206e5869a57bc2b2cc73899f1c556b58249bc2b30cb30a7509cb1ea32c54dbc2b342740a7c6c59912501a68f1aee0c4a91ae2540d9b7e0f9543e5503994fbecc33470c334bf874aade170388ca24922069d2a63a2ad19ef211209879613356132c0c2c47b9874e9842e9d50c36f21ce51887314e21c85729f5d7814c6fd162a85687e0b85426154889aaa1a7358626c52c45b88dc9202c609178bb8b5780b9327cc74b1952953232ddec2a609c2d40561f2189366196fa1138c83f10cbf7deef35b7e7bf7cde33ebbab41e1514a2df7b4ec94beab7df674bbbb79a54b5e759f29b84e0aae9382eba440810285b2fce4a6b96914282c79bf299cf94dc1cc6f0a70bf29c46004734df387da1793785318001a29ada794999832de145228556eb6d07dd568e2883705635cdcf32b4a881e63f1a67081341f5f6b5b275a7cc59bc29b12335f669e45c268c49b825b952eb955759f875ce590ab1c72954343434365f9c9edb187cc3efbd071e7f75099df43647e0f8df93df414246e850832cb296ae23dd486eb468e206a90439cc47b28a900265ecb3231559a0489f79007e5982d3367991997780fa9799932e2c344d40733de43cdcf46a2a460296205778cf7d09874694cdde7097013e026c04d98306142597e72a3dca80913b426bcfd9e20e6f78430bf273c776c54ca34bd80e19115ef094804b8e83a61428c8b122be23d21996a512eb18859ab2a0322de13963c14695748d8a0d2844dbc2730b9dc504c1529836623de13a8509993922379046312ef0962e99258dde7e0ee19dc3d83bb6730180c96e5a79dda4e2d18747ef61d04f33be8f63bf8e577f059811a6639c4b06895618b77106900325d6044ab68a90b8b773029c6e6e5edad8a7923dec12509ba9a566d55d01366f10e2ec0ad0b8a1864cd3828f10eae8d01f1d17cc3a2e319efe04e86d04e8650d16fa1dd5268b714da2d85848484caf2d36e6bb72524147d7f0b79f92dd4e5b750db6fa123ca1c595e26927161e22d8464b2044995922c2b76c45b2884922e45ba9c804b33126f21362147404c793177166f21260f279922614f960889b750b3056a39acce54e1911c126fa13441698286bf83e482e482e482828282caf2d36e6a3715140417c4e577d096df416cbf839e1c588226244814971428f10e421ec0cc102e5d6fc4c0a0f10e4a8e00240ecd5493c68eaf13f10e5a4ea152c26a8c978cb7780731353ca7b860d0d8aa3b13efa01b6662c8c94033f18615efa0f68984f68984a1df12a012a012a012244890d0d62641821a3909d1df12b4fc96b0f65bc27304233a78b0117931e58b32de1290259c54a9e0f2450b48bc2524ab9a106163c1e64c46bc252c7f581179ae5d45a9c55b0202e8967ca112236dcd4abc2584797116c6865bb3c996784b70a61d5a1ce9528ba3ee33b065025b26b06502814060597e6abbda2e20f0f8d93730cb6fa0da6f2096dfc0332236dc285d24d15126dec0392597e657162533bce20dfc99a3d14288538b5558bc812100190364aec9991861f106b2a12215034c8fe40e30f10636a17429d2e42b4b49166f204b07c4d201057f03b547a0f608d41e81808080caf2534bd55201012181aefc06b2f21b28ed37d0b3032b3a1a3bb4e4c49c88379013145b536440c8c15d8937d00bbc58980852a7638923de402048aee9e1a5c30dc65bbc81cc90995a9679b1226c45bc819a64c68e37beb8b0498b37100be68705f313f4fb877dfb79fbb79fb8cffe93fca9f2fb07edf7cfd9ef9fe767ff81fefca8fdb4810089d2470b1f43a698c4fb0709050bf40817163a5ac4fb07090b29145ad6403953c6fb676974a644cdc79536376ef1fe612660ee8b9bba1f55d81089f74f33ad42457786d603c7159378ff38592f1fd6cb47c26f1fd6e9c33a7d58a78f8f8f4f597e62d3d8341f9f354a1fb3df3e547efb4cf9edf3a4fa2197a4cad8941f26e2ed83f460cb152f6c516aa492c4db27b905ec899aa4162755bc7daeb6c4b8116469022bde3e5e6bde906186884a1b126f9f17f878cb2a0356e6a64dbc7daad2a5aabacf3dacb28755f6b0ca9e9e9e9eb2fcc4f6607bf4f42cdbdf3d527ef7407ff744f9dd6336a5c89b1c4faeb667bc7b90616d8cb49569f18811ef1e1b6588b0593d81b2d012ef9e0f5ecc48c32204859733f1eeb9e2834d92a935439af1ee693a712cadd848b13113ef1e273b8287ee47d47de69963913c3c729f9d478b07ca6f9e27bf79ca7ef3f0f0f044d768fde671e379938391bc4262854d4c57bc79909fc5cc705ea16b033326de3c3f545964715f6c8841c69b8758a516c7a50d9932ae78f3f4a08bf332048baac99878f33437c0345247e388122360f1e6c9922e65a9fbfc5ba3bfdf1afdfd7ebfb2fcb4c6ad71bfdf1ae7cfc9ef5f93df3fb2dfbf2758d48c1a38a874c4fb87bc3211c7e522c7d59278ff929fa7d2c6d48d7842c42ddebf011cc76449128408579a78ff9e6296cc7066a5507ec5fb976e41f3e58994284a2fc878ff88a44b44ea3efb9abea6afe9f3f97c65653e1fd4f7fced63f2db37f6dbf75443e143080db2113ce2ed438aaded784b9323cc33debee4e7126b492320a6582889b7ef499a176b5cd8ac09b3116f1fd3054d9a44a4acb5e1d2156f1f0a1e4e92b4c09292ac126fdf8a225d5a51d47d86d7240caf491886e1b2fcb4de586fc0f09a282cf61b0efb0d2ff90dd79968e1a6840c1d5d6df1863340f6c3069a32143bc8c41bee809227559c9a123ba0c41b9ea12c31428690326546e20d33e33222736d659623b0e20d373f7b9888a65b911c6586c41b66932eb1a9fb1dbdf3f6733b719f7d678ddbcececececece4e74c7ed49baf4a4eeb30e5407aa03d5d1d1d17173d331fbec3a471db0df3a5fbf759cbf759e1ddcd4b4a93bb1068d9178eb2057d0e5c559a7038c8b10f1d6497ab8422b42e6c856978e78eb2cc7ac884bf214732123de3a4c9c202f629c8d798e51126f9de6e71756702071e3854a2ade3a38d2251c759f5fcd7c35f3d5ccf7557bdb3efb5b969f74573e525d07ce645b52a5b6e62a4a1c8606092b502e6e537171ea6a140aa0de04aee378ea34800108d929bb7dea15c84ef9cba7fe21f5f2a9c3690b3d1e8f445257ddb2535c72ab729f7eada5f17cf5deea3dd9e9f5d581be45ea57a25f8948be9b3e64f9b5b73ed3b25f53330fac34f9b5edd7f56d953b9b53a37e7cab48aa5faf9cacfd7a03c8b7caecf12bd0b7baf5ebeaaa5345965fd53009bed567d9af41ebea6a9409f357b5edd775b9d5b75fd5b895079a5f57e4d4af994ac9acfa35dff895f96b1efaceab672715e5af39ecd7672efb75f574f51c5da6f935b7fdba7aba5a58e57e3d93fad567d5c8a95f3595f257d7cbecb4ea1bbfae3d8c7cebad152dccaf3bab7e96fdbaeab45f5d47af00f955b7fdaadd7e7dfb356ef5f5a8f6f6eb1af5ebba4efd4af5ebd5af9e5afdcf7fbf9f1321652724ff79110bdaff7e7e5c7e3fcfc04fc3eff7fbfd94a89c7ebfdfefb7e4bf5f87dfeff7fbfd7ebfdf4fadc777f00eaece788d1d96969696965c95f11a979cdedec9d5abd7e8e42a292929c5487a0d5874bc0657ad5ea3866030180c0683c160d05518af3138e591ae3c1fc955e56b44d21dd22652d680592be3c5e2cadae504244a10394237824d542cbc12f642575fbc46a18bd7f8b3b2e57dbeb6f7b9cf897cefb3f2f63e9f8f8b4fe8beb4ed7d3e0741842f487575e37d57aedec7e373e273f5999d94dce76a99cf4c557b9f98162259633c94308e3473742989b0f18a7d209aa82092850a13343eb83066c6803979689df97c3e9fa73e57bae2e2959494945cad7a8d4aedd207dd0d5aa1fba0ab54af3128140a8542a1502814badae2350a7507fd3f2b5afef7b3a2f63f5759bcc6df8ad7e83b6af247ae265fe391ebeeee44ee4577d7ddeb1d0d45f17faeaa788d3f9f8f440a47f11add2845da1bb93af51a8d50943d94cf4341b96727a14339119417a152883d140fd41628af40760a3a946700ea070505e5500d10432307168b1949a4a0dc266bc860a559395902953ea170fbdfcf5d38c5ff5c957a8d3f6030c8f641778328d83ee8ea89d71804db414a9b569fbb30dee73096f739d0893e14caf799bdcf5513afd107258cbed05da114f485ae225fa3d04bbc462838b3e0844f4a324b7e92591b15349fe42a89d798e4bc1fd2f24343438e949d7c3ee4c5a1322aa2b4c80203cd8c190f399db860127921862cdeb0db0ff917951aeff3d5e7ea88d7e873a35ee390054f99bc055745bc460b5453cfb26bcfb2ec90b32c559367594f2be0010b42558a67d338645299c4b255679ecd6173169675151a645957a36c905563a1a27c6c4e3eeb59c926b1acd059f6c7b223c454ede87aa202c8593ca58d96175b2aa046d2672ad74c8185220c8796367116185bc8889182438b95a302a30c0f2e6610160b1417744a1a6a2b5896f5dfeff7fbfd7ebfdfeff7fbfd7e3f5743bcc69f10cb6fa8176a35194266449525c878c32f8ca2824d0a33625b421667706c4ae8d4a8110aafbc5068a479c3c48e66142c2ab150e82a88d728ec2d09035be203f335f97c3e1750dee7f3f99456dee7f3592ddf67a5f63e6fc08cf7b97a7c8dbe9e9a820466669c50d68101c266cd5682644aa8a13942c7e62686aa9a7b281fe13542f59c55583e5815e3832ec26b0cc249593ec96bf01a9398dea960bc3b0d5ea30f81f9219fc16b1c92418616c596a41be98bbf9bbe580440b1e82ef4ea8b0e03a1655f7497b2e28b4e9462f6452f2ebfe8501fa658f9a2a751d4bee831788d4528680fc3690fc330ecc5fc94468fc7238918762838092e027b05b253d161cf800f861d0438085781a11c8661380a948775e0340d7663d85568764a1f16c2b0bbb0c3f0541234d132618aa088772a3734c60799256a567e48c19ec26a330af385be66a7a2173a0c5ea3100a9817a455a0a0a0a0a0a0dc05af11cae7f3f97c3e9fcfe7f3f97c3e9fcf5bf01a7d3b6858709cc0214702ca5a0ab320576158c87529b2a94127a58d14311065c5e2d9178b3b44d028b3be82881913178b1ec26b2c32f9ddfbdfff3cddc12848fff35fd992fffd1c78e27f4faafcef0b4a0e2771ff73161457f01a61e10fb3e27ee82a788d431a58dfb666dfb6add0db96c6d9b73a2d95b66d836d6b43c8b73aed94d64705d5b63996dff65a29edcfdbb6d8ae7dfbd34af9ad1e73540bb7703b2777683142a64ec81a15713b25c4878e37487a7445d5681bd2648bcb494c47228d972d3366c0c53b7d6e90e34b045b1b243eb8e29dca8d8023cc0b3a316f8654a1c55b3d6ab5cd2894e41965d3479be24a16091a4eea66c45b85fb168683480f373deca9d6c3309c03eee12c200fbbc34e84bd083b08af116ea3b0d075478e1ecf7a0a8a9eee9e2f7aba836cc5a2c3c82f3af046db17dd7128f9a20d275f7414bcc62291851ea0c798b412636c881511c12a0a139a850b048a0c67baa4a0492365c49adf2f47f99f8db1ff71c045d693311d3bae90887f369cfff313bcc65fefa1a06ab03d548d120fe51fbc462820286fc6a201f73e777d34b8bccf4df01a7d411a591ff4fd1a83ae7039f7422fc16b1492e035b659b9010f2abf818fe0356ec054fb5e0fcaf77abd9ff77a3a5e7cefed3de9f5dc8362afb7b4fa5e52af0753b53d4f7b31a67c2fd82bebf9bcd7637baad6f17894eac17121e391852e7adc8a7b49cac47c5c997863126ff87b2a5c40b37a24e9e1a517c5aa8b738853889c8d5eafb7e6e60bd5962764607a1dc49082d222851b332c7a6c70233ddbe4416ca765a9990bd65d1d3b9e7578c5b30e7c31e5597765d5b34ed401e6592fc678d6a1606479d63ff848bf769c79d61bc083c8b32e82d7c8a6f59cc09041620518166b255ab850aae94a53f339d0e7e2f83e9f8e1beff310bc465f0700f8dd13dc4460bf919abef5f06d7b45e55b192bbe9501fd76c68a6f67a07d8ba5fc16cbcab7572abe7510bcc676a7c96fa814a85d499150c1a2465b08a858585561751d298226434cca8bdb94b61430d9c357c787ddd54f1ba8c275c4871b191a91188e41e661ffc06b848b4c305f740fbcc6a2fb63c6f89f77e035fe3c788dbd28d3989967720ebc46a61e2f5ebb906b2ffe20f11ad64cb40efa4edb400022e4753af65b55d509bed52b550da226e1b72af65b7dea56438fc7e308ed6a744b861af6ba1735e43b4f65e5315fbd8ed2b06bcf5d147ce7b00c2d82cc66d9edb567b82c979dd277df794e238bc4bdd6539a999d7cae5df7106a4fb56b67d3860e5bf25ba7bd761dcd4e43d7aedb2af8d66f1aeeb56b39aea9d71a4cfbaa5c977bbda1c515b76e7d695fa1d9c9826b5fcd8a7caf6a6bf4b5af6e74af7daf6fcedfec11999d8ca688be592a56f9dad9259231f68692df6cf3b5b3ceec74e4dad9b00bbed93216fadad9b42e1daf59b524bf59b8d7cecab5470cbedba816f9dadba417d7eb96caeb5badec84e4dadbad24df6d97f6f69984455bf6ba8dbef6d62db76f4abedbb8dd5189885d54d7ef9df2b5ef963b66764a7fc9f7ae87d66bdf3595b0e9f9bdeb42f27b077dedbbb4ec5474ed3b3526dfbbb69ddb6bdfc13979bcdec51df9ed225fbb9b7495d929fd0cbeddab25d34906e8b7db834bbbfbcc4e1a5cbb5bd6e4db3573d35ebb1b05a3f1da6d6bfe76e55e3bce1107999dd2dfe01b672af9da7194da71ae8c68c769662727d78ed3f5e41b270ce7f9da71a04bdae09815f98de3f6da71e0b213946bc789dbc037cedceb9c2392cae777ce5487df39cbd79ec3ccd1ca4ee973f09db3d57ced394e312eaf73c28868cf8966a70eae3da72dfbce797bede9ce817bed3972d929fd1a3b0bfece997bbdf59b977ced3ca57e7263edbc2507be79375ef398afd78e30b97a16119324d6587e3c71f1c4c613c50835484e6c51231317efd4998a318165c64710ace556bcd3a7049c309e9465c1eaaa11ef542e4b09dd191869d694d4c55b3d86e03924440d2051392bb19499724e45522b196f754d172a296098751d118bb7ea86810857dd0e125959667166e6b41cd549ad7c7d029af5850b982773f1d6cf1e713e44b8c10246ea2cde1a6a850556d890126ea09c88f77a8c3241fce8d21126cb4bbc57a40b204848346db0e1a122de6b338cd408233f54b06589f7ead614f1d6e6879a1821f15ee186b05822055d96692315ef55ee751af2e99c61f675424dbc59e6eb305ba4f8c86ac1c22bdeacd6eb1140c02812044513cb8c59bcd9e86baab52c3ed89a4c25f166dd5e60c6ca121a68d6643c8977ab7c9d62a173322584181a3121ba85be6ed35e47795302a66685ee48bc77c8b43164b6906f66bee2bd4bbef6401632ae8ec4173211f1de395f1f95c815311263c559bc774fa38d19586459456c8488f74e4e0139e0dcd810d1e68954bcdd23d00c4b6bc6992e5da0c4dbd5cac0d75b5813372ed88cc4db6d0a5db46d6933a7264b8a78bb6e43196e4e4ce9a145ea4abc5db8d746545421d364449b2cba4413354da28c399222de38ccd7eea52b2d880da6179278e3a4bdaea18c2239ee28a323de38d1d79eee9ce46bcf51befeb22c52c406599127f1ce79bed6f2724be243069c8a78e74055f022c4da1521483729f1e61d5f57f991b64cb3e2622ede3ca418dd73e02b07be81d7c881ebc1d7ecb4c17bf020b6e33db8065ea3870e1d3a7410d3f11d3c03afb18373f0bbf71c963c0777ab3c8730aae7e018788d1c600a1b6ce0eb06ee62f90d1cde60c98adfc02ff01a3700123d541ef21beaa1945e3190544dbd60c61b7e2819236a44db447cf9ea8a37f1a1be485c9d8181842bc59778171f0a4ac843b99b79a83740ea72405f64d9d126865a12f5506e81d708f5e46b76d2f04f4f49fec92bf01a9f602727073ab9eb0426e49d9c02afd1c97bbfc1d70dee62f90d5f237e834fe0356e8095bec9e9fc2697c06b6c723568d0a0c139e43578045ea386a1cfe044f1191c02af31031393af4c4c4c4a94cfe40f788d4cc062b1582c168bc562d11df01a8b4b4b4b4b4b497afc9237e0352e2929b9929292d794577206bc46259c5f90df50ff9312c5f1a20c9d9325f186ff17570309056ec696977813ff97f565849c15315b28e25dfcdf4feb7f5e64ff7b912cadbcb698412b13ffbcc4fee70b788dbfb1a4a74f4a72b80bcd2775757d922be035260191909090ba8c3c9227e03522953d0624591e8323e03562708dfe0212acbfe007788d175460c5ea4d08972336d8e20dffd111903f72573f3d8099462dcc2bec8d89f808498c3f7203bcc6232722ba569e78e4f8442fc06b24c246464646464e80d76854c485e38b7c00afb1c8b560c18205aeabb7e0c6d768c1899a5f4fe402788d4464bf7b5fc1d70a46ae7c052322be8207e03556180e87c3a1c7af71e8c29e17065f287457e8b0d081c222582ff47f8d42f7f97c3e9fcfe7f3f9fcc36bf4b97b787787cdde8928df1d00afd181624f610bc953f00e5e23057707ff80611172b0b9c84ad2a66d071c00234e1e62a8ae64f101c3f0d6d6c330ec1cbc46b8d7e477cf0f0db92b44cd0f393c84ca0f09b9fa21f7f01a878046bd1d3de2f77abd5e9034dfebf5bcc36bec394929fa4df4ed152d20df3adc3a50abcbb7ee418a7c0be4ec5be7f01a5b628e0f11486efca012c7867601e517a913cb325422a6b0b479a1e4058e303d9e7507e2e40260c49a64c160922644cc02097bd637788d6cba7b3fc127b8cbe527fc70f1131cea354e8077ef834dbf7b3ee86bd0dd361f74d8c7940f7ada83c607fde93506832fc4c3cd0bb9d36b14daf01af5502b4845d80779d36b0c722538508204091224b82b81c84b5021e2d90ccfba86d7c86aadb5f60caf513bd36b9400057422f5402fbe46a0dbf3403c1e08c889801c293b6907f2627e5293c7e3d1440ce4ae7eba3016a38d0dcad88a223190d85514555bda3144bce107f274cd03b995df3d5428fc467a9d6e0f7ff43b0d63c2248809db6b073e8fbc7627937aedc4b5d75e24a37bed1f9a4079ed4baf31732e89a8dd6e37429226488e146903b6034b541b1d65e2c2ea6890858de8d21983414fed4ab8b15560849588eeb400af72fca8d035c85b90940cdab64c388f1972c9edb49412358608312bb9dfed58966505e09616f644eac86979d67b30cbb2ac12e64e0990145d526f5582c0c2ccf0b44b637ace36112dc0e8e2b404b1f294b21e01b26ea802d5e2c3098d2e6efa44fa1c5bb201e6147ab792d9ea12c7e5a50412006ba245dc4819031df134b15bfa68134315617262291b5b56247182c3acac6a4bd79b2899b30672cde59a723552a88d1810146d685d44a07989f2454a0c8dd4d2081117cc88972e718382114c4b8385f1b1abb0eb00892252278c94110135031345c78c317328c6c4f83489319e226ac6a8626e34028999630e2c30423459630548143659dc803d9b3a7ac4ce09521fa52e92ac5d5999b5c7048bfa9daf3d1e753cea847e07e379c25dd500bc51ae3c56603deba0d11a6dacc072dcf8e203069c93301151376ac452bb3db8b0e182ae69f258c058f349133323744e44b869a0f088e80146b965583fa546122ca4a4c19955e530737c963043d7836b8919154bd494d021527775d354188c498cdf2d49a364076908ccc4550021e45816e8e2e00d61ea30ac0cb8134b37faacf7d82f375a0212ac3733b54316138bea4dc1bbdd6eecc8b62ccbb22ccbb26c0082a4a5edb06c58f30bef4d06126277a40b670a4fd8138c85d6eb9127bfd320c652a30ea8988e341649250cdcb66dcbb66ddbb66ddbb66dab7506c70d6fdd8dd560330484492f0028906b7ceca86159966559966559f5637675367d966559d6d825b9ae60da8825e9a2adc51a104ad694e18a37f0c53ccb86d5907b76832b6ebcb2bc89ee90d347b0b0b7ae67bde72cbb26e1dd6eb73b21978fd200481df351fadb857be0155ae076b7e2062cc6cd0a341c315c3ab045b44b0509ebee3c5d3bfb9daf606b56bfeb00a60d401b52cc8c6fdb2542d2be6d1d4e9da86c3cc5c9a54d163336c68e3441116fe03fcf9e6553341c4c2c38a6e0a2b66236d73dcbb253d8748eacd830f6e477675439b2b66ddb3626d3c4c17177ad83c979f9b60dfbb6cd6f5896655996655996650380958683e3ee96609d6159966559984db1e6ebcdb3decb4e3dcfbab32cbbaa10c0d613f8c91d988920f02e8cc80b1e988ab876a9bd1292b5e2c953471b1e4b5e90f520b214e2430b18b2471be683c78feeaa2e1f1173e796ec86383b27d6d5d4dd05204dc9b23842570846c5449a15778ead9df0c655656d859316565232373457d07858cd7d4561b1244a8ce0efb29bdfed763b4f83fe6e073645cdef76eebbddae98a30de3a51d805330115bdfc1a76021d2bcd004863448c0d34a1434d69ec6faa99daf03d258eb217ba52a597d6d5125cc9499e3d1040994ac315c722ad8a47d01a135d5458a18339f890bc534e98209b3231e380d63d9ebf57abd1e0c82eead3db6d7f676bd1d5c0bc7c2ad701a8ef8e96bfc1024e35cb223a859afc849a3544d6da6d1a6074437d1a47a2caa4091e8f7faebcaaeeddaa6b1696b9af6b422d00e58f55d3cae23a8394fb1404694894b5dd1630c84d224203209ea5456aac9649ab4c44cea25900376f1077dc454e6c9cfb0e9f399e8b3a64b8f7a958949491afc7e6210f491dc0c7cea124f950c07bfdc2a2797893e5ee0a23b7170eab1e7084765a093260d0181863de906a4cfd40955243a4eef53fd849374a16a720755932d544db249a89a4ca15b4d36e0480a565dcdd94de5bd87b569af7b5dd7757561f0a1e9f78af47b250aee5cd7757730dc0281acfb4a24fe4ee5762af7c1421bacf5690511da11d4ac9d1c7766aa561ad5f2001a3db61fe4a1462f5d838c20a449d975de60d5770a976300a791887abd5e0f6e4031f8bbf76b8fed655d8416e1b3229c9a330c1e86356bd743f7ddf37bddebeabaa93e721f12567dbb9ad5b50a3975f0fa2958883367d2a5538302c61a2e1e12b0105196fc2e46b178c1315a6bad596f59b66559b76533a4493f5a5fc9128a808eb3eb380f6920422c829ab59bd667e7a956f40514704ed35a6badb576801bc3fa88078d5e77964daf93962605d1ac2cdad1916dcf764040da9dd9c39f87653b97cabaa4c4c599a2779ee0e448c970ce440a68393c68ce3967f7e14cec59c1eb45790f35e0acf61bfe9db3de0b85058af1ea3c71fb0dffce520f9d9d32d7856120d09d482c16617da464650776d232c1c1be26ba58d450503a13875ebe1fd9eee14cf499fbf13ce77892a8787a98b4b13e163d3e63393a743e3f62d975330c671e9eb51fa0301f3140c025c0ef110194009606eba3a1211282be1e7ef8617569782248c8f9958f92424100b830d089458df370265a00139cd0010c0381ee7a68c684210e5cd742748882ce33e5e0a1e766a2f08c4bc13b6437c34038137f4eb890c3007c0fa7493a67c2e106dad5b0066ad7445dd440581f01810d2b403dfc3e0fa7493a7015889e72ce3913279421b2e094a6a9a7fae78585a20dae0bc340a03b71c3c50d7f1ec2286afaf6e110b0514fe7997248af181559202aaa30147a1185a2a1a2a2a2094545c1a2a29fa2b6c8a787a7e8e783778a74de5e91bb2b62d7a222384dca5ef4a69fbe70ca6147ab4213de8514753c6dc1382e4720c880b49ec3274c9cf0d80e940e2854f1d0c3026a076d03d01571c98791d2150b4c668886498a7ce886495543be161374de0479b404f9bc49e889020e6d010da57c8e56f45808c33384d5b3c0c67362736673f63565f33afc917275d076404b5cc70c3b016efd51e60cad549f889c73ce41a12009abeac346228e44da88e8f143508b82b66b9a24a4fed0c5c92a4f9a601a3875e2a743b644b2e7b61be09cd5f4c9b785ad8ff366a60e5df643c8cf4462ea47241c3e8f675dfad180a367fa9da6ece77cce0b21407f76a10164111e6ebffd229350fe0a7955d35667ee43c80f6604f343c89fdde71d804e1a060cdbf3803aabe9ba7efe74b8553096863d3f84fca903539e04ade998c47e88c8700a4ebf56c0fe072974481d4ab5d00ad8ff80f83d6ff4453f24fa2151050b4226f688c9c2f330e4e0e0b8bb1603bbae2b069d5535f5e9a45f7f064e5d8d527e0859e7d321529a24a4a649a9e3fcd1bb69ab93665f149c3a2f1d9a1014220a92506108ef08dddd51b0a0a645455fe1f37027e0759e6952ea597dd5784744e02f78af4c34e024f07627d0b08006ef151e83b7fb2931e590eed8345d739aa669aa0a75760d542dde0a42703f6bbdeabca32d78af9fae60f721b82f3400fda9a7df4b935402e87c3a4c9996f0fb803416b1c3e60aac319b612f78bb7ac212deabc780b3eba7ac44de488576c0bbf7e99a8faf87454d78bb6beee95ddd9be3e7212c066f976d75dbb66d4e853abb069f3f6b8df152a29d86b8525515849bbad3b29c6655d5a990d6d981fa49fd01773c1e4120bdea02c84e29d831ee5555856ea25777703ed2af2dcb0711f5dbc3abcf9cc2d969bb3dd9299379d5d3347502ac9fbaa9abcecb6a4eb54ed69fbfd828ad17342f2b69dc4e30e7e0750a07c58e6d06fc2ecdc4a1d1f1244b1bb12f234d286225945a7859b07bdcb941e6688177492c3c20b0fb64bfe01d9427840796239713ed1909c153924273aec0bd23cf2dd95ac1384b160d76b55a1a30dbdc4181719cbc143970f03ac56bc2ac560e91b629a45d71a35dc2f3e1727173b45fad989d1a1b6dd1ecbe7867ed16db65f782cc140d26bc35f837640bec3edb27bc83ee7c20e179e9f1c049837e487db7fb142cc40d5d22aab638bf817ad5adbb735d015ca199c3d98a3bb6492697ee9538ad1c2f714d1cb438674e142c0366cbc0eb148e17cc5bb66330abe506f7a02c579aa8581a381c37f07b746bc0bce56e06bcf3e9d799e82e0ae3686d81833efd52e3a402bf4ce070e4d4c8f1cc60f698e306bb48d60abc4bb24de09d928705b7cb9c30308e561b8ce3c45902c39f7ead814e59137502bb47164e0e996305ef92386c67b4784560f7c96a8177d09c1e304e340abbc9c1b94a609cdf2914678d4d12bae2401b5001b010567e17a3cef78be96caa51964daddc45eb496c169dda99c6c2a565b8828dd0deb4379aba30d0592dd03689bb03536a31ddb634b7349c2a3d3964d2dad2da7857de7a62afb2d55adf324c1d32de0e9717b95491f098271b5d7d67cfdf1b94478a330dda33e63365fd410304556a318169e49272bf5da58433702d12eeb7ab0cda426c169d504f212c4d285113c9d9f40cc639d3a09ebc096a9a5062f139c4844299968779dbbd0aa3503757395412958bfe769515c4683d7944bd0c57b076a3357dec21b56b7fd37816d8964dad22334c2751d3684913bae623d57bcb1e66508ba889e47cfd7ced3c622fc3156c1406a753401c3ff59e323bedb7ed552db757fd6dcb4efb4d7bd5557fd3de68764a5f1d2a59a57e72d9e50b5774c14b89760bf7da79eb1258f5755d57e0042eb8fded9faa81b79af6ac328d4dba6992500474ccba8ed9210d4488537d945bb89cf395871a309b7c557d13a5b5d63ae79c8112de5d35cbb22dcbb62cbbaeebaaaaaaaa9fd4f448e3689f561d8ba77eba02dd6502144af8547dadb5ce39e7246ecedecb5955553547f152a2bdb3fc9ca6411e6ac0adab9e1dc0c6ad1a076521d6d9380e5ad787d324364de2e006de39ca67cae1e7b3e728530ebccf9e73959d76603efb8e32e590a671eb1f726e27a0aa3b4bab9dacd39c8729f0d377554f63367bd9efde6b1556d1b4fdee3d3cc2aaea1dbcccc4cb2907bc3509bc9375d669d2ab5f02a80318aa6992f6d55756b35aab2f0178decd443dd4aaeae6ea35abebaaa67aeb359c72507d5d5d4de28ab7c326b556573d421af5f09a874336b9aa6a9d1210552802acb76fbcc690310b5c85421158d71848e215adcada325e856b0c2471fa0e20bb97ee192d7d827813a5afdd1aa573d3168b005437953e63c422a85c47626b98f1841c4643e0bdf27af0921eebac1078d403867dc6a22ff7ea3004fd3b3da5216022a01f827e2103e4573dbf6ae54ccce3d7f3253d614a5a3d48602ea05e4f1209236067502fc8e560462c267060851baca070d32a8b3a20ca7815ea6889532649b8b08857a1509a98a77bac306d23030a8d5761d044cc6359372c5a6ef12ae449897943bd543fd14025c1a54c9b5804b58e8e274a945884972d5e8769dee0fa2a74c7c0ebf15921538a28ce7825807e9ef752fd24a47a06cb4236634dc24cce4d48bd50225651ac1ddfba0a3d4f42d4e30ac23bcb9feaa715563bac421ab42082b800738c87594db5ba5405ec18f38a7a124818f5dc09f71c8173dcf0544f895a1076cfab5a6badaa7a755615b2ac5015b240fd23329889fcd4b7e7ad9d7b7c76f513513eca9eb31b9180fdf3d0cd4435af3a7280ae0417af6971eab07eea39b5cee46ef0aa6ff8f3d7ab47e991dacbc42c44d1130a65efb91aeb0885b2bfbec65a28948703d0ef389edfd54f44e2d71322f157adf55047e87a00abef729c15babe7e407cd7b31686a0df0012cabc6cbdebea074409655e86bceb2265628ed07d9efb2e71cb77eefac89acf45a23e521d47089526a9ee5c60ffed12d323d55de2b3c37548e47dfa648c057113797967e4c818f8532855a04483b8e0edafbebdea4ee421ebac737677c3da37d06587eb30859245c56fd720786a9a6637b3cf699f55a803729cea231032514de5d4344d7df50f5aa83269c1d933f42873815070ee4c83be5b848a12b332ed5737eac2b00b7461778144d8d57a46a15c66ea7c37faaebbabcc7797eebaaa50efbefb1b8eefbe5aafba3a22edf02ad325d5d3d7ea55df6a894f533458217260c9525596234e63b810e164c5159522b1baec81b65655e6c493144554340bc60d272fd21f41cc34b172e343199bc18212b704e3034abcd53431690e93e62e29984f3d4da3508015318324da166fff1e1d998be7110dd588382d3ec0459421534a365130e202e8a7a2d75a17241b0cbaa9380da19ea874275b6ca379986256290867040008000318003028180c85591625415072fc1480165aae5a5e589749e47918c330630c00061803000000001802223445350177dcf17c5bd743951ad4b1bdff03051a70ccb0a6308c2d9027f822fb8f7cba32d409d5e4767d4a7dcfe0593c62fd4e903f5514c334ca63a67095d62333a5b1dece423db8274569142c81be022c432ad914ab934fb8264c4481e0c357530bd5f7ea03e64bcdfb97f1339089ff5b4cca43d622985e288dd1d2baeec65b03bc4ccd336f488123d7c1f3245f2ac4e01856003580fa16f27f0309ba1bfa127f73f8bf3503f8b81877069a0b2a1607d93f9731eeff24fba0a8e077a69a92cf54f4ca7cec117839b525330b457909973625d21b145585994c51bbaf496e4948a4527ecb52286ff6c50d172735c4890e32dacd468bc91bc6f53b86708120c963efc48fa0d72ececd0392376e9bfa9f1fceb3fc4aea3f53711671caa89d6ef86bca12776ed48cb43f6539e62e8fbe6f8c4ae19ecc408ec50a5217a35ad941c58f4d8c3e34378f5843e51789b414da345e67619526ab3f1a35577232d37e31f9019739aed261ca68e22ae72c079f556e3d8844e90b715d99b825914de5a4012d774d7e85f05efe16d7a5a09528b047a4c12d5b4c2b0d0265687a9029ebbe2b748415f60c2ec09a07eb341dee168175fd14d715a8c5d6263e7772aaad39875a4d58de7f20bb8cff99adfa939885f5c83ea23ef372dc639dd2b6e46f3428cfb2fef79134adab3d6d2c198fd32cbc54bd3870170f85eb5b252dceecd09819f42c159c6d58669db1d15925ee20622f04b204e5939dfe832455ea8c820dfff4a8c4259bd59a239ed8b49dd7dc84acd4a98b12b635342d71e445b7f4d7c19664ce63eb4c25712feace4d8eb5be0c147229badb11a12a7151f0318978607db308d70adc409e8f2adbdb2c6e2f48689fc77b1dee80a58f1be997ad30969161fa588a9890ee84e46ae1b8928ed67da8ad55ca356b2df35671e6581069f2107aa7aa9a49ecf233b1e1291bd1d0a52b91a38bdd3c08aee2ed6ce3dcaa45fc9385b877497e689ac95b4d145217f4888d41a9c617995b1a3ace0952ea9702c6160d25be345742bf48c422e7622e2fc09db595ba88b900979db93666b6f57f98a9c73a36d459ee15409431540630c8c6f908639ac7ac872667918b09aa3fb7c58c8f0ca429c64250a516a93e05dc62101a930d477d9d725a18f03e23390fc61a8c04f78d92ff3952ff9dbea9771d0e89644edb582195a11d397e132a44103556d0b33065edcbe121733994cfc9b18a02b6429d0ac409a167d9c283bd5669ac0f2d051d7c3dfe2c31ebd3d7d616380834bdb95c0266a8ae9fe6946cd7d34f09d2a122ca594402674d3f7522705a4657747dfb8f611bd4ad48397056165ca80337aee88c6f339172c71ebc6ca575e02df707dd8d4f1c4c035a8029a69aee16b1c7ed9351c622ccea69de4c104f8f635fffb6d6dd08111b4340d846ac6a844faaf1d6e48720098079718863752afa9cde96b90e0b2bf122e5b1724b95c1fca32e665339858522bcbc51e0564441412444447b9e29b972c7f7617952188a5e61d519d3aee6d0fc37dc2ccfd063071353faa85b613890a5db57018ecdc47463ab7fff6de20436911d85f8f8f18e1c290d9857bf890193219980c653a23188851e74e516a0a652a2496b256e2a9cb6bc0b32924c6ad18c78a0a1b91993584ca07d2b7a63ec5725e3a52a7c6f48ed3a4ec3d527e8df30811b583efbae85c4e116cff51d36f8c8126407613a06bffb4ab271236ce0ad3440c93c7afba8a65834704bf2c33a9c407d5e624739332e3ead2c1890312f691805a748febd9edcb39e88eced610202484f4e6c2b80f4ff77649f7d0526a5fbc65d8dc4ccdad7c708d9f5d6094b290f97d6451c4ceabe1fa6e8945baab22ea6383fcdd88e9b81c5a40a59da89d2b5ddd3a0e48668a77be93bff31a2c6912b82c5fd24aead749d183ca348bc4bf7b833edc8f9372881eacaadcda5b56b3ebbf199ecb04185b605462f092bb065b5f42c0a7b3f80dfdafec377d66f6d720cc0224c01f4f7873403b408401b41028d749b61fd7d60f06124329f77d521d8046cc6acaa698289386699298afb82547417d6a0aa2f0cbd28900dd73da97438136078c8fa65df8515ec1ca5ef06108eaab8a92c8857f80395ffdd71a1f34db6b163dfbfcfa21d7e9f7ae8618aeefae6d5833f728a6a63a1fe381dfadaf9902f819d8d6a1fbf49e11c9faea8a6597242bf7a920997ecd97470cd19e12d3a11aca7179056e2d1b5005b339653e13774cad65a083548f2da36c02e352c53a9ecb77917946d1ce8a0278596fd576079510de65efc9f37d2e7c2375a9e42527a45d5bf49d3eb79b9b34373e9f693c5aac7f2e72cc2de4e5f4d7bc123f29634d3b505c5594407481c880acfee7cfcd4e5926ada233da797030873f18d6bb02516a34b96a73be8e9267045084a096121b4d6e67e4ab0d1ff0138c92ec33851e36e3a4baee473bfb98ed3c5c34ad474b0dad543546a7fd119a921054fd0818425dd62e216c478479949aabf09d0229f00ced55bfa4fddda5b6cf1943f33010c30cc658a726e1eb15ad1fcdaec59fb932c5b3b2d1c5c350d03e09d7442e36fc88f1a59faceba0fcc0a916f48ca4cff97a317177a71d493d3e3a4d7627a327a5ab7999b90f53aa6d731bd1c5d8c9ed66de676b9c1582f667a4dd693d15bf4a840bd54e1d0ce423ffcfef3bd0e81cc92f10fadff3f98c86af1ba7f9438dc596d9d881f853988963847539011502b4c7298ba2286913e8e40682bda2ab4cf4252f5ed8931843dd71bcfae3c260e934692f343520251d94eb7bca0ddc7133cd72f6e6417209f45d7787e030c98bc5f2852af834a634b43e02e83a21d69e2ce467f8f0becd5a1ac302b87e4dbfb8a3cdefba1629980b5f6e744aab68ae6a6b751ea2bdb91a06584528ff2a4172ec2aa4b34d5d226d8f67ebd6f4bf5aa86c0c016ef435d431130df076622dc3c267482fbb1f76cbdbd8c15ef0bffcd7b327d7e41fee7ba62f4fff1ee4ff9589a1ddb7cf56138fbf422654fc09b7aab7e95f6fad34099f3014c73c1ceef5090e78f990d1cc7e5cde4e652b36105c60356b3b8ff09e804e6599bc1855bced6839b830653448c39d4a88e36defde487a837833f973e6133e9d3f8c709b72050a4e32c91bcff7ed4fbbfbc5dd8e91502d55186a6ef6d04619160e9b2092ed81bdccf91cbe376c030284ad22cf2adbfa671c88a4eb1bcac379edf4b0cf93b6a824268b94f21e82cfe768bae2a629ab00d7fe7ebd13382be0a1908f6148cacf6a0eb85f42a0574769b91308fbd084e15ed33106ed59eaf875d4fb0633ef3f046c09e88b3c2f58479d7d6662e29a64096f92d0d49dc4770b0be83ab0f4781c78d3391fa54a5149fd6639836f302b433117158e0076ef8b3e1246051db7bdb0238d39b29032b62dbd2e45885bc389a11e151f1b4b18d13d8cebbe061e610176b20b6f91051b7bec15072991db9801f2dfb9699b2b0c608f0f7ae8bc7891d5ccc88d7e85688edc9a6b348042054e9b6f6a45ea51ef772ab071f8e420438fdca9afbf16811ac6fc4be1f3216ddd79078fbc52a362808630ab1515c1bf6b3c3f6e69b8d04a88f30981e43613bf0e7062079eef9a17ac9ff2eccc41080a20c448ff15e055127cb47c6e34e6192acdec2221aa04f930142cdbafc91bd7fe2266ad8b005cd92b9fed089de0d44df92fd9f349affccd1f2f3d3b6315c6910928451a3fd45eef68ace41044e4d5b2e58ee144511a2653eb3094acfdad2a4a63692661e918f5e770d0bddb47003c2db445e4574b12ed763164061b75592ff9dc3a4a71eaef149eb7cc81f56823bd5a18c0c207571f0875478b5fa8810a4238ea2958880b40192f4dfdcff7b5458bfcc620c79ab823dd2bcce6e027d6b67f357ef1ed9892aa5591002939696d595c891b2f01dab3115b92ec63d9c3100057c2a497557a29e2971890283406ccb41e52f71e375d53840e5d2904798bfe69a6043be473c94cbfc4021acdcbc49bf652f60c8e53893ddeca9da77082485f7387ffa464373d3682b56451697b1c1f0f6d9152d5973b834a27673ac4599254ed1b67685347e90bff1a1db1bddee69406eaa021306ae8388fc8c6fc5c0d444564ca0b45566c4b961dc690aa23a47403dd4c8e43f5241a624560d87406b27deb44d3697da14d33ba4b46d066ccf1cb53640488d238623c081b481c8ec246ab57d4c580ca17871501d82402890c3c113d885e188b3a87a500d34364fa4635b8a0ec900999e0e844c97a57bf86fad132b55b71082451366fc20711132ac24cbd7465cbbbb4727213a520fdaa75b2adf0258242e2cab1414992892d8ea8251195542ebb1d4fb038c44f9068f6512eddc1df88b6572e86d61c400a0ecabfe65f624127bb8b0b210006373d6b3225a5c80871ebfa4bc93481b04dc42c627cd724ad69a48fd6f744d494a53a9fc8dbe294849e304bf1dce212b9aa5f657f4a62145f3d4fe88ceb4a44d034ffc629c53ac3543c5dfe94d899466a9fd011d9ab68e1e1e57c21ac79cf476b5b802ab2a9410e46645158bfb1d2c75f5100d101e33475e78c7d3cd118eb1da24f77fe63f2dd69be1f2effcf73416c4253e46e299547c60a6c2e4ed579899ca8a7fbeb4836deb4c533c2675db96effb035bb01f7403c2486ae8dabe3d29d8e3f7794c62146434df6b7437ee77380197d2bd80dae3c192b5f39ff2ca6ddd9836ed03dfd20357384012dc9df5f74df9f6305ed7bad6038023210b5b9f6e712b12402596e30ded09fb872321df858e85f5fbd3f64531c1bac018a302cde70dc5a2034677b024e951e8f673215f705b672a2146f8dba0e490657f72617356389ec33a04eb22b3224b2208dab3bef06ea0ac025f15ecc6fde943a59d8ade6eae3d48a5109720abb9558f0b5b7c6400901e5459e0f41c30d996e05dbf7588b5eda277732d2e365cc18b833c866192da5e73eb0bc193548ff3b87dc4ee0f4f0b14f4010dbdbd1ad8a2fb1c4ae27aa70d2fc8868802b42bbe508dd800a5f90428188dc215d93b704f0302761328207900be35d47f4f194a0d0c42c236585d290636f922e002bd74278660ebe34f2d88a2ef836c7232981d839280033ff0ca6101269e29a0823c0a88ae2a41d9a12b9b0b092cd5f7831afb61eeecf83dc2a2c698400f7f8311a6361d64051155fc014e3dfa6606c2a3882a79c9901b77570a88545c0aa31bc9fa72ee3aff963ba8d4c505d077bd7e67e9b2830ed2fa3b45b706a678192d7e1811c4e923b4ef6992f48b2d33936c081cd3fb5a11543681dd22d45e92fa88f187b61653328ff938f3104e6f999709d9979cc2061aded002086ec38776aabab7df09022864222f09e734da21d387893d6e3c5960edc4405007a22a8e03634584d06cf8d66d219b91a595d40557f26fad2aa6ab6a2653d3c535acc3d86921481316025713b9d793602b0437ba0fd1fad7b9631a46535c182afb7f6932b2850385ee17210bf545d55bba7f0b1d0b4ed75a30f47e28efd29d428d8894eb5f3b499db20f2afe8a2a2259599dc4a79d721ac2891ba68cb4b78477e3970fcfcbe7e072c04b55c18e811162a0cbc10b94824e68b81c3c74afb99d194f559890a5273230cc54a1d86d2693951fe741f6376b7e72a1dd4ccb7055701f072ca1749a2d4474bfd316c2be8ee5c9de48d3a9f2e4b7c9d366f0c0a92b297860cc2179015a1a029946032f2a588d12236079248972106b953d9a556e7aee9d2711b445fecf26e41c225b4e0e936b890facc9666170d04d7666f3214c0b8b32280d6599539251e7dff2546126774d12623795411d820265b407c095a5e7670238aec60e2615b1117635655d8532288d32501ee5a20c4aa30c9447b92883d22803655d0ef49b275ae55c83ff495111a930115917a024f38e249c852635eb6594003e485c6bee8a9b482f71ad791e234d66289f3314c3fe20c3c66ca14049912ccb9602c0936b0d41d9b41634381aa78fa94ddc844676139bc4a33f0be0180e52df5d4ad62e345ce2a90a99c68e5823114d0b50fb19686b2bad9966ea247c03059abe176dd56c9e4280c6eea38ea0f5eda92bd50ecc5c285cff723e3376b66d6dd83468dd9055d0dc80a5323c88bbdd4f6cdf03246d140fcaba2e449b7dd4c55eacab7b8048bb38e009bb3800509b3416d1497b91782245764f94444d0926418dce0d01aad036e855cc6efbc928f12a7edd6b728f7411428d9a7de7d3a1434964005a4f7653f0c00d32704b4a109c7c44b8134e55732741970dbcf6b316ab54b209f7590d1304f6db34f7a102095135af174a2f9c2210cb41576748b881d2f41249820149cd2525f21fe99b0149e20449147bc2f83f1a6351789d4d4cf275e838ce353efa9c680922044a617420702ba443b48ae5189b78696d1e0f3edf86a851e81959a7891f63d2b2778c68cde55632b66ff8c6792e6ba96c0c01dd827ae56c40d32ecee6362aceb8226d03352a73d086c11840237b54b0a05ca88d34a32a7b091e19c7f4b6f46204cb1ccca803fb9b8bab26a9bd0b0193a8000c9215c3279204948abd832105810e65d820e3886c4a28c94610bff0a6c27dce41fcf1a7941454a19d545c42e4a8ede1049f5da1bc4f60d0e08af13bb9ca55c1ceaf10981d6195631809450c2ca322b9d1649431dde5c51f50b7113e2a1ac26009f1034ff086b0f0fcf9e3b50767578023381c70ba861fc7262f53c45f301bef2436060969cc8ff577348079cb3896501595cb19f1473eae97f0f67e8f0876640ef7a24944bc37f1b21537f3ef86964ab51ac2bc56aa30540670ca97bc7b13850e95ee431485d04c568ed3687ad938b5cbf74bf0d1361c51fab353d5e006632e94186ee1238a0f2a58f27c6563c0e276029551fee13e7e61d75a42f61dae5ed52474275940d9078049ee634e45333cb0fd9f1f3ba04ef54906f377acbf80f530b92e97804483de6f37da13a881b67440c3118235c0a478bacc1d9a476e063e43073511af04cf1101dd7fd37fd4e55045e2e828bee4a8c99674c90948c611eec87543d70548e8908468b9f2064e7ce8f01109b9b191c10ad365710fca43ede1f4faa5a22e1f55ec219bb81e8b7cfa8a234f8da3f5358debd7bcf77c3cc7c920c47ed6656f47e3a8bf1a27436e00294169fc6ed427c58bd69ca7dc7bf3a781418ca075cd7db33ad51c135f45a9106bf5b2f62e88947c410aaf9a028d49bb8c9b7415342c1799c57f788c3a463e97df0d89f29abd0918b4a092b73c3f5d88372bcf126068c0092144951b9b86469e38c79429b42501ec29b52b9f5ac55c730405ea972e14b25afd0cd6d1035795c426b5f83bed80f6db52fc11b1aa049ba3613991f9a7fa6cef227759c52cda5a38c95a0cb20cee766c3d6287d4f4fecbc5f92c89b625957a45d686cc42d2833694935cfc0b782b913cbed174f9ed9be4d9e532fc0531581fbe74e23db11aedf77642abaec68530192a0033f2b154b647d0786164ddcca4a77dd4c47c005e3613ccb0a398ba725e37f035e889d249e2e8f1f6a0f15d3af66e6a52530c7340210d7e0ecbd0e88742b47464e038c15d1e1c57ca5752afed88d3c218cd85875d5e58821c455071284eb472f801385a489edc2fa811a1844b500204f6f04a519cb6e507e4631a9cee7ad4ec56884bc42798b98a11e52c74ec3bb8b6a3348ebb6143f49613133cdfb6213bdd96102c25e8352c4eb6e06149a99c5df11d52509decbf6e05944ec5cbea5819d97b75dd462167057c3e900b376c347dee637ecc6a003e9c059b727006f8e61ae62083d48ed3adb154c170817e8dbc52ed1e66ad109b95c1cb5d3b653445191ce20b330d44ca05d259a5a79626db6720f1d40d5a71520f274095381819441c59fd359bd9949b90cf60cd7364a77d58dbb0120fd08c86788174e0702b1d6a9737d343a1ec8ffa4380051bc0c55abe90e48564f32e31f09a4d83f8b622b75b8f48b10c7c710aa023a4d5b87583b186c571c26e7abfdb1030a973d0e285e23e656bd3cbfdc08a627bce527940db9e70c7c08c58431c1b91e008bff1a451c974aec77fa2a19c29ddcf7530e8dbc121755eee11079e9b56c54281bec884ebeab6d651431d37056f9c44248626c07b26bb4b910431f3201374a9a03ca33167fbc1c0282b87b1b96954d408437daf18eab9ddd9d294801dfb815b92381d9aaf765fb998cf75447cf858efe7eaee4375a2b321bda91854e2bc35992573b186c0156f6c375787786c20f346c8cbf8998d2f57e810cd15b492ee6f2fc3f04a8e055eeea40287dbb43c220543253b95199b6b3cf061f94d24e8cfc21244a1605e0857d109efe9bd610dcd499c49200c7fbac96915ed9f5c8155dfc51e64d8f9c23e11ff16ee963f7f530ab309b7d4f6a5b9a825d86a3528ccafd21fdc72ff8aa2a90e402cddd41602505b60bdee89d7e4eaf52174c8ac0227ae8e16caca5d0b4818bc48c1e41fdedb7ca539c483d507cea0e5618bf2bd0d88211227cbe5f269c82575d9ab8b5c807c14811bbd3f7f252d1103e95583c2876271adf0735ca8c031edad056056d2e13635d9ff85a5ccdedcff8f67a00448ea95371cdd9702296431d8f0c660c1088e96a09e6e15767730ff184c1505ac6e391754792af0c10818c8a73cd419b66da285bbd265f8f55d945056ce2b32e2dfe726c9ba471a62637e12ce55317c183390c74d57deea7f94c9d14162c5d9ee6357750de9cd2864a8c3180eba461babd3751e9a7e6fec4226bd6197522a25bf4b6b9e45f8c9853abd8dd4d56e449f38ee12a45b48e15fc17da9bf77b832d8f30d8468c249f6e95984a99988c92de139271a202c3c53a2cc38a45bced1e7377489ff0844f647e096b2eaeec41c336b7ab4b50f9601fe11a43f4f6b4e7d7549c7886ab269b3493cd0fc44e1adc458117e02d7896157f69f23bda7442511a204790eb23b0a37cf06871f04f8891aacf0a3327239655a20950458d541ace8f1015ba15efa9d95172eddf3d576417d10b7de75889402193b311d720436b30bf73a243d824b5b0c2fac95d1048eb833b77f97dfca3ad4286a5a9a10bd0f6b867dccb87d2d0172da6aaf4fb8b7b4db97c5889d3fda8bdabefaba1ff765588def33a07952e89969bf8e4744c5c090feaa167be86c3f0528cc6d643d80cd4831ad0d1515e3d72bb8c42ffd05910788f32ebb206aca16c47ec73c27a6683bf65910427c20cca211975f2d9d0f6e45dbda9c74654b4cbc54d37693d44b6ef3f4b4c7a161a17ac0ba8bd33d136b483cc1a17169d337772ac41d9a435bc0bc02fa0544b4e3c1232a7fe7c7f21e56065592820aebb6b6461ec50856346533e3a0328a0721933ec52447ccf9ef85d4fc6ed719d058d9612fe87c67330fec30aac5a37d1dbb0bde4df5d44121db69030ac3ec9c89520964eaf4a746d69b225cbd6a719ca20c8aa84b905949b26a25f521e23b6f0f84aaecfe2a783971b4f858613c458629933a5fe66a2274ee742d4e715123533cadb83881cdc8ad6bc40ca72ac9e52787ebc9bc7909746001f80ad085e0c65e4daf2b76f207fea6a32c88b7d53ec8e0a99e42d1e84a630fbf5d7de5d9db198b4147bb569eef363872d0bb10aa6e82334293d83b6bde51f1c8e5d91382f28623ac5d7eb91dc484b4e9fd4956324f7f3d6bffe166188126b20b43d4f9806b312ef7f6a96555be4d8ff4495033306e3ce9082b9262acc4c131eb505ce6819704f564633a21a4d7d42a3569b6f0aa7ca0aced32e9d02e97630d987f47314611e3be2260749c8a9f8d8fb1b8155ac2993e928bf72b68cd18fac157cc4b31a2ef25b8b1afb2ba9daaa3f8a6b8b01112e2cd822827fde0ea27611b39ae7d9ddd2983a955bca7e88ebbf049bb8296c3188d9474eec33c9b1505265994e0e5bce6af02474f4cb8d2383b23d39fc87c74ea70ceabdb6d4c3d89c089856e093bf7706fc5fa5a1ce049409d1179866c155acfc12b12a1f41c6e99565a0ef8e709c664acceede5b06b214641103cc0044272620e6c5123f67fdf9e5608c26dafed1267a0a9d55023ea2f6d1c2fc35db80e4b1c8275949a2ac9226ef3c6d99bcfe9ae4df0d2cdb3c663e28a2e58fea753e1b4164556ea6f599a5e08b90b000b7f64a96defa1d78cefe28a18d6f23462d99bc413f5a4914c2bcad00329a3308d84b1c281220b608e57ea8e098d775feb60a3e221b15cf2866f40424b914715d9ddba87e205b0061411fcec3cf92d4dab2311b7638d0af2c49840c64a1da0bc332e7368f6cb0fcdec225cf7c7cf9b6c9c84580ac0a37eafd90c7163859eb6432102c527149799ac441681d27053f4c3a6a37bb16376720a5bdf862a91a79db2160d24c089aa751a76d30870a086a22c44a3a0c639184250aac11d0ef1c9bf6d34fc340419f7521f2a00a0416d88166ce1a86d40ac08d0b117e17b037e8f7de39bb47f6d0a527be6c67a4ec55ac7218725b0a3e874fc29d3c9fec0405ba2284469ab325da9cb1c9d8325e9399c0fcd2affd336638e081b580f7f8aba94e5a038391199d9ec9380df3c73b7f85f3e87a270ac5b39b60bd77c8597f5bd4c393e4826c9e0f5fe2fc07bb0c340147729e16ce4b9862de4454d1f6e811ae3010c41f97b866e4f5eb5f56af9e831802da89ecfb14d7e7437e1305ebfa9095f18a593fd9dc0c246b4a176cdfe452b9000734d7766f10ae766c41c3350a260248e8137a42bd85604c18f86c155fbfc0b5e1340b659663e30c040e978ba7c0101174449610f7cf527078005d0d5831ac22df4cc0ebc65357f15d132701476fb8771145d303fed3acc1a9e1f5f69c4add531fc37d5eea9aa7bc1730bb42b3570942abee35e995f603a81f80d3b92d541fca58d81d1d705b9022b2d0b16ce3f9b70f813a9724ce163ce6b2ae3cf217a2bfa78e755ab66feaa90bb0f2da802acf5d97ab78f3bb0d4ef5e2b72328cb4e948c82b483049a501edff4580ae5ec3a620b7c2a1b4ae8ec7086284928397bbd10c506d8535063fd619b0ec3407bd8b97c9ae0e7d41121457ba8bdfe240d050d687182b3d909c59264df630aa6a53d277c2087935cb97db49d23753c3807ae277f75be59d8f743a7f53a7ce56b0886ac656b082747ff038060411cc6cb57922124fde4e348b692db5ec136346b720c048bb309e4694e886336575382f3a02aaa2cfc3ba889f7e61920f98c0a205882a5659a081a1f743ca7a52a72e1a54b951b0e0f585190912506661f7850c7d60a317be921978e2465f63b8ab06d768ad0aa641960639aa873b112b7d439543b97b41acfb7d13f59f6f90d1645f0b3a1a68b2cce23e374403bfe1f33d1db705561d9a478c1800a41f9aa8ba3386670c3c01aa05560573a6d0c81d1c666a88c0f265c6830daacc930685548c48012db7b92816cfc7439a6a9822280998465778195738c29ad55b6c4fe05a8c9870e206d1739790dbc06b1dd4f1032a9224e295ced223710638a72572f78fa4f7e80a2f436a11b5b7d3b54fd0784fd77098e418e217f244a247634e0c1dda61df72be28c607c2a82b2f0fd2d4c414562f1d3475f089f993aefb0accc919bc6426a77b0ed7c3a01da5aafdb8bc3b0489848eef561572af6cddb232204ceae747c8a7853af688f1b8d0f6fef12623cd82697b442e242982e10cc22add3e55963677dd602589849203b817b9ccffe5440aef1b270552c7cfd2722a7896687cf21325dcd5f43b0e54f1f801d3ae24830224227ae94e2fe1885fa79781bf5799cdeecaf05a53db5022fbb5a6649f24049b0f6fe868bd3a75e7aef1bc721b76e8d5e39dc376481574e2e858b901c729269f829ad16834e02dd084eeee493490216b079373d3d426814a84d57382633231b51e29c7100faca480585ff329b7462a2eb96fce3212e19a478edd276094dac166cfc3ef98cf43dc753234e351b1ee243cae74163085c1271c88f3767d192c6e76604b3578541fd13331aed8c838cc51556df3e61e657754f37f508f1ee95b3d9bdbe205032d82a2bd075278f97ec53cbacdf4abab8049ed3c127324144cc2b1c1eb3cbea1513d7094bcfa4f9ece013d1bc4d4146c326d345c42818f5591fc328e8ded33b88be68f5f225165617f454f91ec0a492bba4610f61cbbd3748a21463484e75a352d9270daf706b43ad225ed68b4d4f7aa5cdad083490e095451a224985cd5b9c063350ef89c3ee0d45ef7301e091033e65452842c101b9cfa8c29d9bb33d63b58f6c408d46cc9ee62bbf635bba2c2caa27be976b38ce9afa1e4d8a627db7cfb3847c16d786f212a8420a2144745eeac357d244abb65d32169da1c78205cd24be470f768f84d1b3a73349ccb5352b903757eadd6784a3a2d2513274882fd744a258d24213040aeb280b63bf13f0c07febba78423e3bfc805d70d6b583a9b23a64670422becbf9d3530ad0501e65d06e94ccac80394e64d9cf8ae88fb06fd236a93d7dfe868f20238b8b1cd89ad62240bf8cd69c322b8777414eeb1bd78406c3b8680933ab19dc1f89d23b376e39f85a92d3880b07457b14e595ec02ce7d909e6565b44bca67b1e925c618347402227ddf76812db54fcacc8a22996d44d92eeae9c61bf8a2593e59eaf21d7ac1c00ee10a290d7cf1648ccff59a51c58c9bde5456ddfb40a9f60342006e3905ec833160fc6ecdf89a83e3a7231ee7c1acba0d2d12e97ecaf437f31786431698d54325cf8b2372f81a362e1dc2c2c9a84168104ceb3db07159e893283e5ebb9a9069b6538a1504d935406d7e2479d7be764943fa2409ec205de93e95057704b4372cdb9c2d6ca0dbe64156a7931990b8c6ef30ef55b44d0c3f3ed9efa4ed91ce3b4ba6756a9790908e35f6edd1de4e63adbc717f8bc4ded451dbb979983f80a4e59a2ff1498efcf1fc17d78d481f1b334816cc5ab521fa86d3a06241c6758f9d13f9a2f34c00bba8a399a60bca38f04ebb762e4c61398bf68c38a63e804690c9fa99e29de1a3c9745b1bccdf4b2e0e783c5ccd7f890905ebc639fedc119a13e2afc3d3d51aefd0d0aab93ad2397f7d442172be0306c90e2e313298abf9f9b1a4fa6aa748bd2f9e3c95c2446f3a6ab32b9f593b0d19e7b8b743aecd1b8caa6c5f971ebccdd18832f4d0f81ec6e3b694cc7afe4267c3d6af2d0fbbecabb8f3503cb04b1359eb74792d8d2569cfca63d471d65806e32297fd27e31a63c02ade72b9dbc6388a0ca7625048168398b186e4fb1c779da27d8c1973a531284572df56b9f8c2ece8f9f4e23a6401d5b664a4c61fdc64cdca96e2815dc87ec5a8808005a7560c6560477d6aa61391707da8aa6c6781b3e9afe8ccb9dfc18090eb7a8c41cadbbc4ff7117e8b6e2a7174f0fcf486c223d32c2361181fe1b642b0b2b7a10cad1233ae1019b0375f49677689125a5735c574446e75bfae7047e56b275ed15db9be11a4f8f2952d4f514d12fdd918b719a697f6156496340d7c97078e8f388f9cfd17c36954f2ca529eff5ffbb4d90ee0d9b794f54702c9fad097c9a6bd6f0dba435a4181c177f733ff3debb7614a81ae1c8279daa58c1e3b1d42eed4b2705fa9858e30253bcab0b8d84242ca22e67ae51e445260aacf5738f89a5a35da961d16d7f652961465a793097234531fabbac357485bfb1037fc61fa5ec78209012096eff061b4880223def9b896d160af185f891b683184b78ce77432c84fd778cb711091ef81528f1495a30815e969f07b78c50892afdf2de5321a388ccb9d9f21b271b3c57e64e746be5695fdf41c8a6631732d4096324a120dc87951f430ffe5eda6ce48a151e2b93d0f83bb5d447294ce12439d191413b7d93434a90a102ff070e01119f41664bd6ad748f3d6736c7ea05b9100d5e4babfac36d1b4f847c62f3da47fa9a71581a890233924bc7432862e7e67265be6c18ae3589fe406756702159319f05c30f85e8f3b8ee75761f96c440142bda0c4a2938aab294469fb9ad8e4cef76bccf02fdea3c51f22faf128caf300290e504754445733be80aec875f5d5cc4bfc2043fa2964400cf294c1b148320dfc1e8f7a741274feaaca78ccd481c6a00022ef4430a11a42cf97af62ea05171ff8adffba679a91c7565ef01e92542a0db0d9d496a61743718e33c8d65345ca0d371c4b70bc302945a78805a08fd75e93f59bede1c95f5be0b1b1349ecbe23b14d3f6288d140b16418e71bd39729ebc82fb9ebdb83b494b5c8bfbfb7088a9425c5a070a3378a67865f7a2ef2eb07eebd257c234cb9ec2c5ece3b970f0cf4c806bb0b7da27dd4b9f9e686a86ab5572446f9fd69047657840bfc5eec0af03c58927df568f863fbef7a6eeee3cb788bf3906cfe4486c4d038c6c1e15747a3c0bcbbab7c2a292bdbfbc4b950db8fb867d71fd611763741586beb7b3c1968d20d5b68eab919bdb73ea5cfcc80e03942033ac093179e370c6d58810a4d92d2b9b0afef0e3650814e4dd093dbcc9e0f01d08fa4aaf65ba0791bfc63614bc6a0e2a60c43c84aae1453e30e7873db027404248a3da76b967cc27c4e9416a1b9f31395006a4e41bfd747f0d8eab36538383daa2125e3fdf33cc3bed766e7c67bdfea30e18fa44d8167c7ce395bc964d1bfd3aa114b0780d83a918c4d3ea2ef33abe4bae88be492c92994cce77c98963d4a9cfe7c9848bd62d88d48f5c59ecfb87886c9bbf114d849b316da6bff12d95ab872a76ef4a4fd2049663ec073fc95b09f71efa2d2cad5121edd631add3779813ab72978b14add0f80de03735beed8c50b05778d203319dd1fe730612f553d57290984a16ac1db12cc82e5c90d10bf00dad3f6013b66afa1c5078106bf4bd6430638542f30cb62199493368804e671d3ec95adc9d80aca10424933171c3054565b61e1674455ed1bfcf35ec1422e430502333a75cec4b73c662fb55d01da2e52e0d88600951cd310bc420a4fd844982a040c344b55b656ec7a8c5025928c7f4ddee5f0504f1af3457f4b833af8f22ae93e79af513e0421f1ba467feeeebed82d76ff3f9ba600ba7bf9d86078f6dc11690846f8ae840e7f03b369600ccc6a7f1430e70c6d7007b0d0796c802718f1556a2d605c71b89e2569207d8573a54b0cbfaaec60ef4cb721e0236422e1bbb6497d5dac28eba29acc9f07fa9969b9484316bc027a0136660cd6960a42ea49a533482ca7daa0e7fcb4a8b4cae21c169a33c7787482c4f24c343041e840dd63df9af1019de8233c1d2806a6e8a4198b42d26073e3b95cf8e66031784ab93e8168c24f4acd0be2adb37b152af9744ac9bc8bf32072ddf1ab63a08fad82e6e414901b6527048070ec7dcb80a83d0f230144ccce32a24233a9dad9068394c99f74f1b5896c32808898f3f57b25042c86ecdd214347bd808a2bf9dfc6ff11ff53f13b43a08ff1afa781f1c2f62dc6a0efd9bd01ebe92e910c6dcb6865e6871ef8bf23e34c0c0c8f39667dc54e0b4cac3c82311dd2cf7227c18e9f9bff5998599351dffde9d9188b83fd1a25fc32dba7658465a1532ad40551fc843c7085f08595844c6e0512107ff2cd702c29c7954097b45b7b327151460670a0fe1a838541f716ef762cb27f38156995bb030d6d97676312f1992a69e01c6785d268a2062f8c424013177d494c5ab8ee1c8679a1ce31e519b27b7d1f6956543f9d6abb66497193abb989b56aa775e53466daa1f71835e524aa3183e67599a26b18cf6fbf21a34f0a3c7087e787454d9f2ae3e3a2b29b4521dbc43a3c4982f74fb36d8bdb104e5189de740048f893451365e3d68f8958523e17b4ca105e568a017fa9389aa2bd915882b2ba8094082613c4a7df77108cc0864c74f0d141fdd24d2fafaac6247077ffda4b14c66bb31e0f146f7ba4ca7725f3403db7b5bed88e3bc02140bae5391b4cad8eac5bd2088a938b498eaa98aebac09cc8d722cc6b83015d57511b83ea0609da006ac86e7f10a51960172a4bd9413726c48063fa93ac9969b48430c313308e4454c3d8930dfe320617f44ad6a6ed62f1054d12953410a50c832e0fff3739b5bddcc4a4c366a52decd84b9e84495277a85e86fd71bd1d62d2c7b262f3f0ef031cbc853a2e4711d7ed0691f0cae477f8681b5ad04b739453cb2f8422a3b8b289d0118a91ea1e859368c3e9f0e42c8bc3bcdee2719eeaf29675d10ef5b083d052d7315052cf7415f8e0a0c1a25f25ae02aeaa801ba102785262190c79e3588e4126ee5024ce82d0f71212a653ee64371d5085490cf810e98595c984ccafef465b90bda1e263e201acb1372c9748e941cd5367aaac1c85246a4280f9897892529d82c48b695a9da753137d703033505132239a7da43bc5c80c99fbb1bbe61424f18040224386304a0fa1f9bff1ef3476abc9f7bb52e67301080ad1f7864ae58969aa0c61f5f83265c0fb77f53777c76fc54956d7b78c8838dcb12e1808438448b3c1dea9e5f3246c13fbc5d56efc612c1435c447db7cbac0b4aa63cd488a355db0d421970b79392472972df2ec0a654c7cb60cc499dd926ac701f5096eda78a4b6bb2a33a19a0b81ea7859a79ca71b390e2f72bd68e824470145a2440d180c9c6250f79125ddacc79b9c18ad199449c9bae23baf0938f69528d54a9764c54db6ae31b8c15f4596a6771c876191e47af9b3e99909a5d2370a53da280ac4a3e29014fed6f1aa4c3a384aef995fba773d517dfab329f4ea8e8a211cae2486ef430de79f33281b855083a014dbce81caa49e7fe6b133f90273b653ec59596dbb0184f2798287ef67b3ae4a7c93c0d49071df0e9ab9a6e53958414a4a7492cb294d53834e40d4088e9f64dd77da05fe93a68538270d5fbf4e852e402f69d261d82cce21604e14a03790044b3d1365637ce6164d8fc9959291434da0be8269bd718363ed6d9b6731760888aec9a055dda0323898765f26e58722945c34907f285777808032e2f25274b9c66f927afe9e86ce310b98f2ce2b6731cbda3938c815d38c72929939425950ced27f16e58a496922d78b128220415402eb30d33f764490df3d086cc04c2e357387062bca6b4945471ab0718d69ad76645abb3f0926105e7b26b601389ecd2b6a7550318dccf6b981aece8feabb165c86db1861331ef118c31821c3b5ba6d570aa8f8166f6e6d3b2415967c2d8bf310ece56c422843300a8a40d4f09612ba63d41a133951f3b9fc7bd72aacb3055bc374e2245959a57fedad0f0a19b487cae13974464f354a824922e51bb2619ac71ae55e009ff4ec739d173f4d8c1bea35a43c03f5f4cbd2bbbbd6be658e9d7f967d83f32c4729524f38d69e06524a4a7fa16f62dba99c5795f172b86e44af762920f5a129b79b279f71ae8b025f6a6258d774c0e7f0b2b72205f55dc7503d96559fbb1c122344a00e1e18fc7c2709854d064347c2d2cd543666ed378de78b0be89fce9b585ead6cb5ca1039504ee88d8a2fa510790b6b65b6d2d31cac0307f918cf9b7351390674004a0968ca9d507574ac02094a31b377a64b3411b0cc188e1f59a420639a8d88fc01342a81239d748851eebc3b10b1a2f9ce7aaacbe2c99df2919696b90bf8879a277180143c9d5ef58d2256d6f67c323d9cd4d512c43b5ca0052e7d1e9b681b85f87c143849ac1a2c4654b4788412c1639c0ed287a1ef3057c7b35845a606926e6412cdec5038d4de2a2147a9da2e91f045a0ee482e518b1a4625d28437ad84f8d14aeeed1eb51d6ff086a1efab35843f4843ce8e6cdcb9e5de33ec1648e7d29bc4687ead6a2a820cbdd6322dac17eebdae4f93828179676e8ea466594e1eca689fbaad4d756690b2446afee138f7f7d29f83c5053aebcaf12f95e57264c1c1e2a8ed32090d99a5c63293725fbe9a3f2d059b7f0ca9a625ea22768ee051397c68def06110cc014c4fd64e2bdd52a108fdfffb138150bf7f554bcea616fab79fb01df2714f5ac076139d9a0ff080b000149bb058f6b5c02b214ad380989ec1fc6d2d7b1c01cdd0efc65a24a2255a445113d4bc1387cb578d355b0c6650bb60ed84c3b49debf91380f4a92dd63fd99a000d300248953a355d2892cb497668220bede1affe787b8d3d4ecf9e9b09e421d7d0e5740119ad50f8750680cb85b4ad67dc82116ac5f7a9ed3d1a6e8e9795394bac582bca50939eaff43fb1e4352081b327984157712be71dd4ee51a5a6ed443471ec058ee94236606b6ff28e83f172d1cc29ed74eac52fd8deec7a7ea5cde9a2a84c380240508995c8ccaf95ae142d753d2848b8886da3bf18fbd7da7fc935b8d087b4c9edafd3de4bf48096c4e02863a50615a5db2525fdba1258d97aa43ca26a2cae12a14c1a69451e57867b4d2d47c3bca4c51973958e887ef19a3772c7ea735064d4aa0cf9346da21f1247bb0895c0115de0b911b5f80f553d75057a4cd25bda9424c2b7c71dfdb103d9c6b9e7cca33dd31a6266f008037a6c418517d22e88f48f6fd1c674205f79eeab54737cdcc32f9221e3244aa29fbf50c250de81e668ca4228e09a2f38ff959f143c69197f852ea7ee8f0ce6ba08d79475e4564ee3dcb77226b1777d0aae77d2ac6208baba59ff05a3333e49d95ff54c26ed6d9a96aca0445a1656a7f2c6c03a9bab65df0b97f1d28d26f1559d1847ce4e672c580271da4d695b7bb7771f4fc7140590fd9feeafc6e13485bc64521d76bcb9247af28da6688742161b71bc0373ecb5092959cab8f743e01216f3605b1378f180cb0dc2f6515b310064734382f2141aa24124cdeee28c5ae0c6fc98713a5ba194da466a326a375886dc06c785972cd7c15fb06d4f72a66d258a37e27bde5e2436905ef5ea5e848b83c05ebb4d107c8c4a3bd70b5e2fa21e9c2d83db1708340e6497e819a1bbb7e241170e08483cb8225064e33816f0302e9f728009b0ccbdf26bb741e741809872234bf82913d6c9b09e6220f88dd9d93eee82e6f5f7bd1736a17ec33784f4e2e08daabe56e4ec0c7dd277088f3d56abf4aefeec04526629b47bd5e6481b63fd46438deb01933ca0e62fc2f651254b62c0c9a4ad91d30500c84c4afd630ae47ce46c604e1f6b5294194d1ff7b96d0307e636d64577c80169868459ecf816099f26953d5d804235ae3c9f43e8bf066525a8d812632fc4c0f10e1b70f2cd63b542dd0e597a9056621f6c20b1c0d49fdd2d58957f427a8044c0c2f5397c1dfd5e60f5a8ed6453755a9397cc5d5fafbe4345309e76c7a1cca63b5b410904b0ff1bae83796367232e45bbe343b5e0c39675c62ddbcc4314a6401c23dd7eaa812ab37c28530cb54161ac35d961a5c0079f848ecebd977cafe7377f8e9f087f772260da84aed11f5f0a3bcfcfda243f11fe3a38023e81e34deb5eab53c64fb9f945fbe0ac3749083a0439c2ee24e24c6572651c8340af25060e20f0c6734f7e326c79ce8d474d7cd1529a561e60e39366854d06404facf1f6b775ca648fd622d88e11cefcbf6f08de744765a3cb7c15ef55f81021eca46175c18201c38dbb961011627051e2bb862cc7625b03b227e33e414889dcc1787c1259ed1918e3e6c12b256b21a362e6da1cc03ac3918296205dafdb1ff2d4849902e6d4b42309d0a3c0fffdedaeda3d6262621798b704a9e56f6b87371e3b7cdfb5fffb1a4fb64154ae76ae77bf665a975117f3160328564959b61e2da34968661d5bc146310e6659dceca2dd0334822dbcf3ec67b7cdefcb3692518683051b6dbb0ab0e7578b98677110ef114730cc1ceb8584adb2a0571d614a019863676eed845e498ca63b489458c0727459b2979824b440bfe8e07101e4ba51d1450a50405268dafb785b4b25728670126a61d550b5ce5279bde96d3cbe1e4fa0e8945f7da6e2abc22f7f9e8ec719f7b628121b60c0248516e0bde1498138b8c429f0110df643838d2c70afc375f21c8d9b845c037623a45562029197f4959d7be1c7bbe18ce814be36aa9fb9dc73db9a3b7e22ba4801ea5881c5380fd81eec921b650cc82cb8e44e5b8fc502f7ee52190267c680f00bd3939566d1a8f5d00e4bb5c1dc197e04ac719820ebd9c3ed92d0432a47c3721c8297a032dbac0a4754c673ff5b01851c99bf2c9c109f02da05be9c3713bba005f0f3351d4ef39cbc7657957990dc8b65832754a2ee0e180b86587edec39de02bbe1a4e54742191c2e097aff9b3694a42a7095af6b7210e36a00d1510f9525e94fdd2020a04fd5c8d07e7d98865f6e99524a395ae872145ade10f30903dae140d0840b6c761dd378ed3b5ddaa47429e8b6a602716e8189060246667c992dd04571b90e17cfdbb7bce9420d1ebb67129f91cf0bd7c23520a4e2987dad66185ee2f9eec6e36d0c9387c5fd312e1aa255eebe27aebb13715a58d75c5b3a0514619000dc4a84a08e6d5a6a80c2606bf2cee6fec5bd3762e38bf1275747d002d83a2dd09a1758d4363ce9f077d450a2040f504066e9ec47f40395c2ae6cfa1f42ec70cd5339b40e28241d9bf9668263b7bdcf795237a30199def28180c952a0247e6c33df6d84c7042cc3af33cb95845d6a48d761e92cefaaa14ddf70f0450da535943b54c9ba5e149ca8f4aab6a9933056afed8356a6c0057d21f258ec1f774c015746e6611e5715b676610e731e3d7e353d386e9edbd5cd76e014bd23528c0dbef0247e751d2f2d8c64e254fa0026f6b4b25030279bb67d19888e5a058855d50d1c93ad67eb08d62abfb1b7179fff214453a9d6425b4da704c4dd08ed428d85ce553615586e81599ba2073bdb02d00e958e55b066a785f1215f2326d84f739eb834bbebce831f69487772b749b500ca4f4b1ef955e58ccad4a084cc9234211811a1045f0b732cc8820238079f1c263e1b475466bc4c383c76bdc6dae3ab111e492a11ed24577382a712c67c3bef4ef99f765382b84da8bbb797ba4a506540e4f2a98e9e74a48a07bb89835a8fa5b9b2e93202d6509a96ed650533d57cb5daa8547385dde388659d3514db4af330ac9b2d88c24d69519e2e13397332ae4262fe2e83dd4dd6e4d3d0b178f0a19533a8012887d011239eba1b35db66424138cc7d120924f9a09e1cfa1886c9555dd0917be4ab892e13b474f3c759eb4b73543885466934591718597800b6b4a61d4cbe8a1b24558f135c18e32c2748f80968b3d4a64c36dcd35dff6dac4e7f4c5aaeaa8308043602c49e925091b0e6561618bb4209e5a049b939ce6acb2d0d1dff767f25e18ec6aca11cea37431e24459a3dbb1c85af414ee5998d77f57468025f45e66c11d4c6bee192c8a8b4955142b56740a084a271a22de41ac424084b159ddb50827d0107bce0736521153d818ace2e7c622c22718049fc0271cd8ccf0c080490b96aa855782ecc0ac07bb33960efcf6677c1dcfefaa3cde667a158145cec7d9464a494808062aa4dcb1bb59de23ad72a4f9c664e53291e9c2aa2516952734c39062d38ecd9783b4d26b3eb3846f2ab3ae29c02e953b52e94d3f5382be3ab232f675bc3be532a1bba5c6303af356d4b1d0c95015d04076738d55c231824643dd2e164ef0488c7b88d68ae8baeb558c1a95d9cb0d6126393f6a0f8730c05278d7f8f2a9d83452cbdd68acd6d20d00fc6e9952573e3ee9da0c2c07a4e8a87dbe460c30c9bb89d22f97d2b9cbd9b38861923861b9fe661825cbf14b5c8d8277ee98602e64191dca3b4a27ba593ad19ba513fd672ed3fccaa48c32cbbfccca5c066594f132cab4cc32b9f72df3d4a0751934601168dbc13c5fd1a14f4265c99f88f2311144c2fd7b3e169a886c620f47169063a103f24f23b31d4d86b3280ecc0df9401d8fd687425980b9862718c1dded008c9930d44c35e4ab4cc818e8bbfde839a6aade99d5702ecad589b5fa922d98c4b177db92aafab6af7f0589f2061fab751a9124da83d5ba11fdaea73bfdec9a878cc85920b67eb2a07c4c4be5bc57b87020d10d17e7df5c1add28301059c96039cddc8ac4a71e5bf286625b3cbcaaa44986912dccc27bcbce313202b307a8fb593d69fb75b1ec1922601599dacc83c0f5f82343fb6b732508d80fff819cb69e656bc565226dff164d7cbf156c7bf06404674dd20884133f21bc46be205c0eab98140daf835a184bc8a828ad794653fd46eaf52a4c1ce7d11fb26af4e71f8ebbfffebd3e4334529174ffc79e702874b3da41f2485adcec2f3f1d896722352a8bc037af540b73c4c82a11fb8a6df69ae2d8ae8cfdb607e4622257814203594267a2e4c9d92cab8c929c110aa8cef720fc752c32fff3c55258677959091c8c7d4c87298d423916ae1b5f97be0e786fb539e6a5e8645d39cec28bbf2ef8d9f813bdb95f578a2e11ef39dc9be87f57b5e8e850945f18e7d71d60d68d877837675e6891bcf4351aa79cb08b748eeadf73a31b8a246ee89e54254a90d6049f7ccca4c8c91a9df5d94944966efb9a5277abfb1567e28109ad5325d1836b6211c9bcd3ec1b8514ed1be34557730a12237bdb7b400e0069f47cccc6f15b1ad6342a7add388cc62cde53e11a39e9a7b25b54ae1b2896305370430328d4f8bf8017df13162065d507862a573f0dae063321c663ea7d54cf3b046fe2f08a0a41333f76e61d8d813e6dbf3101de91aa6cbf118253e7cb7ec515256c6a28a5f2e866af5a18d4c2144b6d851504e2b2d9f4d400f322c528799d8b4767bd0e3504107b892b608e458a477d6f738dfa5eb9893e2d3042738c6a73f284e3cfe4f8d4bd5f62f6b4fb5db66953d9148f857ddbb43ba6f403051e0d00d4a1ea140ce67b2eb4b330dcaad8c11a99fe4876d223b6c82139f0e2a29eee2c3a09ed3a17a5bfe9af52eef3ad617ddbd2d7e73ce0e013d74de47d1e42faf5ef23f221f4eb31f68cd0d848b59d47ca9f638fd6eff2fa12aa8fe2a2090c86f782dd0e7270281472db569d54138f00e24f091673e27d4b8957559c92eb11ab873a0766f5c9f323386de3334d954cc9089f685aa1afaf28fc3b6167917068d2d6928d4eae34285ed0b9474cd7b00124e77df1a14c403fc7ccb21b9a112084c0db73d41c9b018679d6968477a7af3a73ba1ddb51a87cc0a30f52fc152a9a08564c61d463e034847f0a0a0c51d3ea20185f28d0eeda6638d71d936dabdd20a42cfb88034cab0b2c146639ab9cfcc47c019904cf19fd120e9da2e941632922b046768a613abbb1e023974d48b27d17db8c58f0bdc40bbea13dcd017a6141c767e08b7a1a8ab045403d10c94167a96edca6892a9cf4b806e95b2a41af42d28fb2675da7b47f57b20829699f2168bea93172f63944659fb3373c74c091c7d1902409f835ccebbf4e320d3059bd5cb351d9dad000b3ac6258259fdc8ab7665d5dc4fe83d398dcf5cf938106871efb5368b7cca143ddd18d88be5d4cfca439ee7f0f9ecdc185e6a381f95fc7baa5d1464d99b3ca90f455049a8f201119f54df879806c1e43855d22275b6e54f448d485dc9414586c184f7124e3553cfcc1c29df1c51cd461676d98670ef7c79a0292aefe0467d4e20ac2a8b23bc5c27c4da0bef846338f6cde58828cbeb9de547bfb12fda37d29d7b9ed32f09f30d79d70c08b289f189e2ec9a5652098ec95c114822e281b0624f39e6989504c4d22bf885c8a311ba4a271f78b23e41e7430983e03026dddb8450d4295fe6ee74a19e2e69f0938ed6502d7c94e2678bf0c7856c1ca1912d229eb432459dac8edcf0ae23e619cc36e9819cee18aee33a56e0b2070009ecb6968367a7722e55ba87e67852db36fbf3c7f05962a2ea29cce2847a5a41b434b060825a28141c9072eadd5d7ff8c03c5b234cc14192169d24aca042e9481d290103a9cd08617b9f2ae0efa9f974f5c19a36898b6328d70630d62f4eb382e6e384bb98da60a2ad23a9aa33e07543b4856d2d745d2696de951c714517e15569d48d14bbec673d579fe8b3f8bcef923982c3842a2298214044c016af3dc374f50d030d301d4ebbfa2188286a5daa6d0bcac3b085bfc4376adba2cebf55a8164dd35394ca100a2bbdbf407a7a0fa9754c54cb58a0198e77d71350344f476035a08ba547b365412290ed952a6a9880470036003680333c6ff1ed243bd97f23fd36ce3dc964cf2213c73fd9ca8451dcfae5915c67ca049931d9fa351b873d676c094633a21b9169ff1490a5b99b6cad853448a1dc78d733e021ad9991495893b2b0a81974f2a585e6d152acb1f0337511bfcd89effffffffffffc7ccf6ffff4d6a1c94e273219f710c8e73ce4de68f9223d1ae8aab34569573f6629e40ded855ca4c8952d77e8afc3c6ab643f1d3ab70ce79b7b9397c74d75ae41b330759a0261140e5009d06b1477aef233eee980c34ed7b8624a39ab99b870ac0a16e137b41e47a8eb6f3587c670946ef506ba2dc5d7c5f2cae5d2b12be8e17adc6ff3f9216c8e1db068d485c8dff8dffffe74c2544401ba993e6b1ce27d258269d7d18e47acd06cbb28c82616c6c86b59203d3d405ad0803baa9a5cabe41299ff16b452b5fbc47385a819115038320fddd370672f856f0e824f144a365a3c681122c3b408ede31c8f80488d5214dd3341df4a16ea1a24f5ee40721475e18202cd8562ec386e7a49b661266864de6549f61325c9f1ca1e4b46793d71a9ee49cf30e96e3ce9eb2c4896c79e7d472bcb6288b1c019836b1d399a7c444033c29375dc74cf8d25a1ef2081a76a291c209f9c516fe928147e2cc08db9648eae3f20d9b90224b455a2a53596136992d076c19e88f85aed6d69e1865a449c4890a160cba04038ee9a866a041c725ce2e28a9ab75ef1a1552e41c256dad591d3f4de918398c780804fedfbf197a4c6cc598e4f1f5e975c2b9557ac6d54eb98a17102d7f072cc10e4b6747a4e964268b7c22313a31b36553f3ec6d17de7bef0e28c764587492405c449494c8ccd4ac8c45ea9e25889639fcffdf9102faff7fc582626ccc27064030222766d171625e45c8c2595a7dcdac5a6db16f96b498253167d768a5248e68e75b53979f8c33f66c80bd826d02a0ee225be5da6a134768472198a9a5d8de79e1dd1809b3d38aad02f4b0894ae7da66ea6463c0676c83f077afdf1de27e72bd75a1920d945683aa92352088004b50b1a8d20c1923967635aa067918329595da3191a4af2617da7809d053e362c7129b2d7f7bbff7906e890cc0a07c9e9543787498642b255557d979429daad23ba5f7deadac9bc3471f69ecd589dd7befbdf7de9f8773be5d86337b1b5121a45cb79f15075d42c33457ecc8a8181fb637a4b4308b146253a826ac95a6a997c71655a0d0d1ee95937f756ff03d44ba19c428ee1c458640b16f10993c23169b69fe8e26e1f3fffffffffff7a89b6f2f632b2c69482d59c617348a2ad997004ad0297d6d8174dd2c15ffbfceaaf18cefc571ae40080c241a765565167427d63b6115576f945cb6fc831ab37f58b13d1240b6de7b27d7400edfd57b90bd34665fceb02e2f877cec56cf950cc77624ea3198f71e74f975d034b7475d4c5da70d66147befbd43df08256610d32adc3a67aa67218b142964ea4b301f3e5fa6ecbf7ae15735db8b72430c1df4284d223bd1706d878aa4a82749345cc796948bbdf7de7befbdf73e84023dd0686505e6b38cd301d1cd135303365760a08d867f7901f0c06c5c5518a3709c771fb781ac0d98819f1a906af97d58c01b023fb1dfffff1f41ffbf41a1de4f43cf2a96c2ca49a1c23b866db5ecccd0483752df4cd21d067e453388c39b27ddd37bef4666cc7f905eea5d488ac8dac376e58aea4893cc1e132e21fc641fdd09add2bc5cc8a51454d704dad8a24dcb2daeeb8c6225601bcd3260079c3c22e27c1f62b80ec47033e59f276b0db64d090048518acc5c9de3347eb4f8ff288084cd9a3a55291f1fa326341e1460644cc6772e7928da77ffff462fee07c1b84f2ba9344e047e75d008069d7dbdab6973bee2e4eaacc6041461f3f654bc5f5e606a44b039a297b9f7def5e4d69f76cf4e7a42868a84f7f644aee128f2222c13c8c99bcf1e0a4685c4071c497f25941638b54f138f4a8fd294981db90ae14194556b48076ac6c4167afeffffffffff57e9e6f0d1dca75557ddd1cb8ac26c7d59cbb82fb014d5a6d5b883527c1e28d1609d7c0e2b3aa0ca534dceb90457ae8591eec896adc1526922c35a95e13c94f33006c01129e31edf489480689fee069a6981fa47ccb10bbd45d4900998e93af38d426d45a13f71656c7337ddfcf027cf429cdf0772c9f930bc9fcf9f870bd4e741231d42a79e6921802653bb438cdb949cbb901d0844cb6d579876fd2c9d8195d1bd54d03dcb40d5aa6aadc4d9d0ffffff9f996f4cb1ac09c933f1babca824d974ba92c858500c19abe8b87d2c3a50a02acff9ff7ffbffffffff466466c89d14aba0ea3fd196b222395d242d3bc196a110bbe7a970540400aa0000d3d525efdc5de590983863b6ece5d5083119b0798c71d61f336acc247bd251a2a37b29ba3b8b1d4c44b4ecccb2a6dba6bdf35b9166859eaad07a6caf2cee84738e0d66eb5493e28fe498180629fe46541534c5fe03ca029943152c0711e59b25280c42fb34085dc0c7821f8aa97d26bd687ae83515bd9f404d22a139b12e6d01dcd445604ebca89516e811f5cae89e5290a857aee043ec48cd87d08d6e66d5adbd73ceedbd7795a48c6f3d859b7befbc0c88f401c78aa31532f93ac0702500a138cb727457cc2bb81c6c414968e8cd076915b5a78840ef0e83ae558cb88e8c7f6f334b10db13e45896a0cf46fb4119df1fb14b61eb3a319bb1a3e540563813ae6635e0c0179c736ebc6aaf6bbcf7dea59bc39741e4d8655db17fccbda8c8937a20214b3c9737d67b3beb692a59f17a6aeb4878ed25664764f1e82faa4051714222e16a8971a07735c9eb572eba5283f520c4a5c547b6e3cc85883448cccf47cff436fa01ab1e6a1bd5158cc25a5839e7fc275a21fe2003d73b4820b85312d5eafc9a7a598b8188eaf22405f1ee88b3b438ab9e7bdf789f9f1c34ecf3402a671049828e544b3865f589f1058da27428292f0c64888bb594c46b325e69bc2d8302e6b3ad86d0bd100d739b9a6c1da528195b0ac3430afc91e51367f415ef91eb29128b9acab6c4ab8e690db1b4ed28ae40cf925d6c3dae6d6e3d21577e8ffa9539bff8ff2f230b96a505cbc41a9c6fcc4316c70f4bdc3d6b39bf08fb985ae696d955e47ca4bf716d483a02ca3b4a59ddc2e4b7286a5756af6569cc0a14e9fcfb07b9e3e6f0d1526dca4d5fa470ce3955f90a8773cea552a18613dc5e0af59a01886c60ed40e46285449113bf15b900c5f25a33d2995ad950c4ffffffffffff2490c3073da189cb230076d5b9b1fcab9b9577efffff072583c6a013620a8f50058a94feffef086f8341bab7c0933443b9d37bc9097383616ff12528be5ccd9ed0f208c6cbf8d4e2ff7b20da116311493b3a022d9da86d7366e411b6269b4c01d31413217e60b3712bda47a6b2c011ed0f494d0b1a5b00f8f22b81e46643b602b4f098de7bef64d1cde1ebbd77efcf109439e73eb32750efbdcf3dff83b40a5c98744eae9ce534bea0512ca358946b1ec0865c456f9a2d63e3d38c9ed78a8374927cd141465c67435ebe422ee86030b6537968256e2e46399df29c40e1ad250cdeb1190223578120973591693ee0bc6758de6c567869c474359df2c478e9b23ed0750190da018d6747a9332702301ec44e14816b46d5ba835b209d4cc6f83490406b3087bd269559954a565bd776c64e480cac075737f7c12e20df5165fdb718427b1f7a6d70ce39cd916649d38cd9983771c7fc70c84c2f9254f30eac8531135c4d7b0b412aa30ad79ad4b42ed4494145db96b8015d2a8c26d0143bdf18ef8374ef3c7b6a8b38fcffaf667c35b222fc4783322b8d73aa85ad5e4e29593a58d837a14d461d1133e2d7668c8a3237040e40a144c0f93c9ea4f5535928fc172ef03105efff7ff0e6f0d1c227613113b33eaae652006c88de7bef3c61c78fb025f1bfc9854c7e1f3e4b28daff7fc748f3ffff7f9811fdb0ffffff4ba4449bdaa99c28b5b33c37e30b1ac52b8a970fc1fb3161e9322133b219da63ff5fb312f440c11d5aa1fc529e4331563afba6dee35ad0d71ab74e8cf65230ad73b9cb79a9d1336e1375901a065f9a087b1f0f89dbcd9fdb88b67d9a964a1be19b26e9769080897cf8bc20fb4354ec750c48c988a7760994eef79806a09da4f9c28e26f8a707fcba5e00e3184edbd400327f820fa828e79c73ce8d555da71ff2940e9218d26aa5375f216910b4cd961331cac856ccd7240cdf8db3a462aa4bf48e68666b19d5f942697f9bebe3ced097edbec2c8ec8db6b272f1ffff243c4abaf3c9afe36d28304b6ce449c296a08c9ed1126557f9c7e6d9f32265ecfd2a765240d813bbd472abf255d762d76bdedee37c1f65daffdad8534e208947896ba7e295958a49bb385a6ecd96a5b3c262e46062ac13c6484d678c6e85e7a88ccae59be7a5fca23fb87db55add693e85f2da3a56ec7dd8794febd00b35e59588f24bd62d9ae99a20f91c81c4c16d2f19ba6d383b4c21aa34b3b3eb4d98cd943aeef2519b5fb65f4f61eb4a2983f5f596e5827350602121b96d6dae1034df8daa0e92ac9c25bb212c17778b215b4101052edb9ace1c846a1ed78b15b9096c84b2a6c68806acfa757d2b12b1b7245044e0a16403d8b177dc17215ce01e864035c40a385a5ee4d0a1a81e71799335e8d4155d81d790c45b6b8a2135677f1840c977e1232ebd280d40944e44da232c79674de78e29d85aeec3ffffb7d01b8087ed80af6007b53bfb7acf08fa3e73529cc0724dc0a34cc3eb82c9c691a9153b56ef1642a373c9a53257bcf7b969f91362881ca42513e20dab51e8b0ac19cd80e92c6969b6d586d3a286707f9c50af5a72d884c0c357f78c49826424b5ab46dce88d70a320a9b939e7bc5cbf1ce98eb425a0a7b84305925c504a90f5562b03328f89b2a4221324cd90a1543345b8038db8a8446eb8358d01307b040319040001005114c8c24c925c3d14000417ba9ca47c34483038281207426120181006838380000000000381807030180612c3a2d6c03d4c6d7dd0c9832619b11eb0ac4cce745e11bd9739ba2086e0a3e154a9915f26b861dd54573a9e70debe2d88342357061b1b7c670363f9a11b6e0885cb4eb763888f96fd2d9790e77a92094f6d8ded5c52891777879af879094377864c0ca958ab1597730867879c4285593be7831501874836c79f537a00c6a9f7297cc7140c8c5a16499d789bdae48023b1c10246b7bd47de2274afd17abdbd1e1cd8c806fb4af009e14d46f69aa1a76ef1702879c7539c1c83a02c8a83a68b2b0f15ffea10db60183ba696cf8a8c6671275248799099eea6f001636d68078ab302a85a4c456ae28f1f7a66b634ce5232df592e497aac3d13f44e9241496ce8c031f6723a18fc68a6f9174fe69ee029deb47cf5932bd4600b61e85792a5386c2635a9c56b417ca4e0c4559d59a05a3d38508f2320e9917afb08cc594008d3aff5221153a2d39d151da7b8eb36abc3640a1326c35dcbc79add52fd70ddf60ffb74539d43c40c02e57b85803391bf4d777c9699b4ff4bec8479b34f8d5b27e8da782ad465e9cc76c26a30cca5e7962fa97d186c8a82570ea20fb78df4d23195e00ce0396036d64e8e2a12da8b804584a73488d01f1e5df04e32371d8092c9dfbee61c6cc8afe37f773f2b33beff6571d7a618207d0ded80b8ef1ebafa9ffe366dc29bc1ebaba635ee3e121a6ae289943b0292c3d9b536606f680881eaf927ba4ed468d2671dfa692572d2f543d318b4b92794b1e6079dd8dd1be8423d79e2b4a7425fd8c48605e5c66469697d0a1209dc114ca9f721e39c8b5ea1c42566725c2611ec90b6419145e8a4baac6eb62cab81744cb44dd3ec38bfdb0e019570e53aaab3d05d50f953ba02d19c73bb00a242f4cd99f7de9ac084c7226844a6a0d5b71f62a515d02476816bacd56711624bbd92d44a2b8424fe67ca3776bc413f7a28ed21c910a77374018b5b4aa27ccf0ad338da94bfb451b09a2b55ab653b4bd1bbfa45977d00296afc8d224f21e691b1f89de51ce589237b93c5c33a5c02fa75cac43e88b32b660717dd7261f5cb413a251205393be5c25c530a150a42f6375704e673f4a553e603167f23b11c480989eb7a6491375df1265feca7689ba91523aaa5f1851ce5f8523802e6255c8c03813d7458665003abfc64697541851ae05afe5fb474e3471f54c4a39530654d2096b1486c13d460f9b062bb1305696db0343393ff7a088f1da64f66e91e758025f3ddab26e4326c45cda5151ea7759da05f8eaab8ccbd8957cbe8b00460b810007481659eb274ea4f9b6bc6d6a0abd7cae9e316803f526b2f4cb9093140f3736dee1b256933d4a8c729818e9bce7d3c9618284ba6dfb4bc6bd14a783472467d804b422e963c4751aba88922b9835156b90e83a03ee25ba3d1b968d1efb4a0187d0f0cddab30ef45b70aa670b48ffd26df16845d6a896d8aa7026d8c6cb8eae9f9cf2d7dfab847e484562245661d269c3b3c4d26b1085df783e0540997e4634906f92fd4735738438452568023da1e63415ba9f22c49a46aac095bcd2f915e2ee893e20921a4138b4e581485c4bdb456a56c203d7e0eb4045df60b53846477461e3814b938936936ccc06e79b32eb68e0d248be3e2a8bdcdebbd2fc39915530bee6b5d6a9889aa03c18caa7b5fcd270d3ffdfdb7d4da1cee9f1e32a7e0d45ce7ede3e951a6e3a3bde2cf32dde0da38a6854158e99b0804cbac5298880ffce8b50acebb1df4062b1c3a7d053259a00489ef1d337993a3bc95244618ac00419cabdcf265b7ea9aa9a4ef596e71d112a56e150d3cbeb05cb93b39ddd0c6492b6cce805edd24598ef6572edafeea87a6182260c12ac13ed5fbb54e75628fe5feb111d7d1455a9a6277eedae36345e5eb46122428667bbcd06c292f838579d63521ae5ca9bbfd4aa8045d61cc8c3579a5f402cb47094ecb8817ad1f94e0207f58267f648135b11f66b9f4c0223b48633a4d224eeb7ea7f16b1e4d678862cf730b4edf1ab328c282f3d3499556f246dcd3d9f3f245613b30faa91ee1f486b71e608f8169278ade0c3b00ae6b81f5f10a57b7a8f9f69f7bd7c13e01905f5dfdeab382d9c3189473ae77ea94052b1d7d5c7261fe396e02e4b5ee73e13147aaba2ac214a7f9365107808726badb0ed1f514b19cc061ee12ce25dd3c77953f82ffa489902df79d910e0e457915b4b0cb17ce6c8886ef8857e3bb7c234b2c8bb42ee018f998e06fb1a03f6a820a75d0fcd0ccd2cdb87f247e820799c944bbbbacf42bc2964201c9085c33abc18cb1a375abe723e8363a95a132fc02359908e3532ec7aed2cc1a49d5501cb5159ba7e25acb14f05b4f746b89d7f2cd77fcb853b7bc2e3fce3b1f7ae2012899d42660c119557355a88a1a115f85dd078e620bb83e94978f90a2b675afaac669adf6d35d2806db7aabbf534faa981849c18e6bd893682bfb258e10232d171379883c1efbbded23c6f7b808883cfec543bff72adbb4a34e622e0f2aace4fbf0459a36be5555687187bef98305e0b6ef81ae10ae1c65c2a22ca6c9264db164b9d5c2023580cbc0fbbbe10ac6d915707e82553b3a7c02939ba5c0ba3ea0f001b447c0498eca6917bf60a2dc2c3405792eec52f9a79c0f63f9fe026beaf14217db6559389362691eb363f5e00579807fcc0b02eea07934db9b725fc5a6b7c23d2dfe48680455caa5af203178126bee22681afd01c5a8dffed5b493a0fe28541ef10543f876c3e238f7f6a693d93849913564e6fb6a50cb1cdb8693fbbd7d772075d86fc5b846e42f4192c7dcf0f42fe87b20f7b6f5e0249ed00b92b270ebe76e24d4aaa66084249680e1238082aa532eda9efa01517185af981a5c69f2ebe8f8af08d6ba44ecf7dccd58bf20378feed8465448f7abd923c0430b6c95406e1da3f429367beb3f010b4e2f10c80b1104dc343d7de2e3c8006c21ffde51158dea7ec92abd64687c1b56f3f31c5a79bd2889cf8f28a8755c31873dfcc3c094b299f138b8b3cd1d2e3f7504f75ee577adcc4a748f752d32a813c52b5d48a738909afbbc01c9f071a4af8e2eb0f3e178590856a83bc17dc1034a593af0f1b4e8798bfe2488ad83d764baf9e70e0f065731aea5316803ff5012396ebdf0153c9b0fe4c921350bc9d822e6f6184d85d72882a2d740a3ca40ea433dc687bdcfd133328b6500ca833e1ddec88a4434bbea14388c16bbe1763ec95e86b4ace27f6fb333234a7683536d3cb106885c07f0c91ac142cd68ae8f248289734017de9361285392fd6ebadab15ffc1f5b5ee5eb66af05472d4894372a09419986183ae29af9c4c46c89ae4cbb37db360c69f53afa13a0a258fc7df008bb115ca4ca682615b76838c642281440affca1218c068be1a67a8080fa0f2f6fab4ba20c5eae3e5cefd831599eab2b0fe9d52c3ffc52387e6b66651d8b47735b0dbc5b557aca4b4d6163f422d8df39a42d39bf162d98c5bca3e7fd4e6abe1e99821c88b0a5dd90897685654bc2c0a95beba5e9036064f95e5a37d75924c9375a61a147b9a061d4c02deb96adbb9f14a5256e805f6a52d1cacb5256ddfc1a7610f72bee27e63cfbb38c30d9caa1c1aeaa7432b5170cf0aa36e2a406ff8624b778e14c2c4addb2552a31abaa5a1bf4f32b3d9ec51ae32a8099d10d56bd536650394acfeb57a9c02ae08d2b8a7d23aba0f9f35c15510a722a15f046e58b604d96b0f81902eb4b6bb4d58a0aabc77ebb74c13acbca308f9c4b36e71ede7e9e920cd92b1524a24f2e85a9a4c74d6e3566fc9bd7ed8a1f5fa60ebacf0341c556ea4f3aa612365c31ce408f191f03b5e9b203cba76b8287c73412bc120441e6c04a28663e5f61b6a8996a69e6699673fa35ae2d1a4f88e1274a583cea1dd34fff360a34106bca0888b20289d21a512320b24d903c44a00cce4cdd40fe2aafefb1e299257ecba8439a967888b9ee4c81058121fc8d186813947568124c62bb198192e4560e39b22bea132108745416713d10f5890ab7a6844138f5085f39ac98621cf973c8b661ac0bbb69742a25562ec2ba7f15e51ad1f51ef944729ec9f545c310d5dcd642391cb4c57a20c8777917b8fb9e4d432158f222ce86df8973934af3cb3a5a408e87954d771686804b6ef47c3737816403a27155ec9b7a55a8ceacfc338fa25fd5a73a1313c1bd577a217c69cb2af0f1a5ac6b780237d57ac20df625c74f90d4c914aa8bdeaa99998447fb46e4fdcab279ff331a56cc8c6accf81031c810364d6eda58e3a75a7b51d443304fcad926646d447a4d92ef94a8d06945f772e8ebd69c362ddf5886586355ac2bc041260ef41eca233815e50a66a54cce9d4720d9e30134945346926d4f174da076349421bd9e96a05845978c0b8a8d00786230faedbce169a58fb089f1d4d9839b26bd852f945b02858e59fe947216ac00332a44d1643bec6fce9bf6ba63f9e73a89a67141f9d6256f43734c59c0786f59bb321d2ef448e3477617cab081f13dc0eb93b7c96777c854ae5da0dea0a38eab91971e2d84db8d385e57b9af1688b3e5cee60d923b74a5a82dd18765d1e07d1ea5f80a82861a1c60b08c2818ea28898485833306213ee3cb4442c15cb4bbe209a8f165a424577b040c41e9c3f362b52eb3b532051038f98a602e41887dfa902afd43d1563450b1e74dbb3b8c7c8a097d5de2f5946cdc1d17bfe0d5c8feef9c308284cf80be37a4af2e27b714741d6fe1d39fdebd24ceb7d1567976f89788cacccfe48b203fc48ea0cc241902b753fd35eb4354b65e8ba12b495f4d0cd3e2bcad2ad6e91a9a99cb965511fd3f3ac3b95a69fdc57fbd97a84be1ac3bc2693737e01eb2c8c4c2f91681b9b98528b0d376737d356851ef864b8a82b9932adcb3e8e2ab6700fa8b1ed7235b422466b727d2a314175a3224d4fb65e8a0ab937e55685816ca40e067e4bea8288afd77123fe1ae6642f566f70f8b1285979989f8599b7fbc886184a8544901e051dce6b87f8f97cf36e791c5a4c35dde05d20b4b19dd988b024e30f2710e77b3543dd9dc3da34b8b6d2d0746009904b627419f9dae8c9ea0d4cc08f3249b4dae222f94b120e0eff159da29d6fe355a5a2ff4559b7c3834ec2078308d7146ace6988ca0fff6b9936c521956dca6ea4d4ff67dca59e137cf711f33fbbe9aae8d81359416d4a1b978b5e341e85dce83090573107a0b41dc42f6d54358ff1e7b9452141786b5f4a0318f7c4e65b301383999125430065a0d0e6f14245264d51c5798d1ad95234ed01d96cca8e862ed8890413b260468e552e5c79f3a4b336bd18381b55c6a4c1240d460c76c66343082550324f429942a766c7e3648d206ee334cac41afaa3e65c1813c857a7184dbf08b0fdcc015e7aa488d4170c63ec025004e74fb08009c58f0697c377002b9fe96561bc68609d763b541c9f9ced82364b7fab611aa7859aa9752a00a3507bd1c0c1a0d1fbd8d2ebe7acddd03036dcda1500a013b5be6138f15691b96ecdf2968fcc3c9fef6a22fe8448faf1aadf15cc489b091e22400b769647980a29a48822b4deffb2d952f6b5eea062534d7309863340500c312ad505815317bb976b465174bd9c0bfd092e48205816ab8aafe34ae192ac6c8b7104009db05ff50f56f11c159721df017c20a86ffc266ab8605b32c6b3a8a494a5906cb47d66ab856c29c3a257b0d2a7f2fb6eb789f00188b8cede55fb44ad189b402a3759415931084eb34b0dcbf6503d0026083b4b28c1af4ab1823799acb1eadc5a04599784d685797d570a7be4d3037dfed542674b07825caa104a994c03003d826387205e616dd6a2f6bb5f89e9cf31c4205d2010d5e4ed56232880c13ffb31e8689d6cc5558d294eb5980ad3655117a95eddc7fbec7c4049f36895faa0be4c43833c76ce852ca387b9a0eb6dbace9c1393f47c680b9a8ba14c5645b471bb70e3c9b6e33f9845acab7f16279e6edecd9e68f072eb1a0370d46956da19761983f72c8481d62e32775fb1e5dadcb5c423893648838176d048cf6ff5ce3a5df113a10ca7ecbdf600516a3acd66969ff160e42faa192f641c3ec29d0950691d77c98ab92a5ca1403bbedf50fd23c78125c413be12ec5f09613e6bc71e6fd1aef2fa48ba750942ef603a85535c32a59373fa93c8e32d0c5acfcfd1490ae647b546bd8ec96bc03664c68a8659b74b43374d046bb19bdd4b0f2dc4e22fa6338b2a94a53158bca91719936ca3290f3f06033ec3b240cab1d3c27263c0127e98fc35ffd7ec0a0284cb2fd5746604b749c5765dafa607b0f9254fc57706513af645778d2c38b69e6c1cd06f521d1d5df8a1994592b56eb298cb3ae4017e14c19ec873aefb46117024038b81e7b7bc6d3887f134d9c7988fc20c803e2cd8770d5a11c93c17d04a70f1eda600b073e52df54b1af48e6bf7942202257b8e8161d203cf97f42f7cb78297b8ce4c58024fe8edf3f2ebeaf51fee529489079efc5866ef8ccae6b727a3e386c5c07e31544753bf35bbecffcb822da72fd9c0ae72d371aeb487260c1a79a702ad07c184fb12f8c1d2b824bf5be0f9b7fa7b503246d317e767ac91b3dc4fce102ccef703401a29f9a8ba287cb0b05a7874cbd4c54ed41ea786ebe9297c2b5c1d18eca23551f25da20030681222e96b2a93a552cee7fe972d2105d97758d0d2bb0e58e0f29516ed97cb4a5c4b75e06c7775ada7d6061223ee134b014653f76408e07f5b69e0458928e56f58f7200c6ef5da25554911740bacc75ed5b8064bfb4c899e29a7a6af4583a4783d04919f314d9891fba406a43dc60989c48ed82bf0998c8e366ef2a5797003435209fa64fb0ea31ce1a3e6501d3570d7e73087441b9655cd915cf7d391fae9ef7f80d2bcb6382be28bb5afd42258a3db9f9992b9540b7e64f820162b3956d7ed0f9744c2b3a3931723decd0c776f6e46ca4b7389fc64d39fc060caf5d4ae5795a6506140c2844e53985151cab439da4d9c90eb586230b147c3b7a031c3ae0c459441f7ba7fb5860a856f90187ab2b9bfcaafcd697870e03c08d23ed92f84d338c77fabc35885365eba1872a707256945f80143c8d476ff24174830d958331fcea75c990917bad425952772b45e546a01f9ecff29ba76478bb74d896892e9041e4c0fb1d44064a4edda296d5c952328a44c8d4d68e7e55632d4c431e8ca6f4e25b0de82dfa5758850036589b6f94c17dcf3f2e1e33c60e3793d43c46a989718227e2b85a9df20fbd06f2a91531930bdea59f8869cc12260a7af99f03cebadac3562900108920838964e201e702e120a27c84b40dadad6dd396a6e288d8ba626a3309e9bdf13d35940e0fd882a077200adb1902299c83c354e47f7f7f942eb46f286333b9b7e35d6522662f0394e7372503481c71fecc71c07e20fc2c8e59d4fd75e68c322297c9cf7f5c1a2d236fb2ed2098f8ea2477141fa0d9dc46b37f59b0ee1a2860afb44d0d410bee6fec2ad62a5365da0439ee1e5306aaed35aa7af006b103e8ba37ca9d31ab92fbfa63ff918c307caefe405cf847823dbbd10a4577ae11ba1426729751e62cf58260df5ab6b866d8145fd721255351307fd7422f4285d04e3a870728f2803a5b924d7c2a11276685563828fa8a0ddc943bf8b951220480964958049d89f04aaf38726e4a0fd2f9a87aacc47a06111179a7420233153bc96ad7256b165c226a6b576094d8f25e49cc0c4c5e9850b83598c9ecaece40d2fd7ee63042e92c9ec99d7daf4b4ad29480ec3c99795d1b4b00fdb9cf05a7e1f313c50eaf066ccbeaeab4fe642c7b14acad53d3f55e713c6d951d5169eda1325357ae5eb03dbccc56bdfe5221651cdc9e91bcb6d4ec5935add36dba4f7b82f885d6a380ca3467fc96bbf22a7639523c42643cdff1e6e4d0d34ae477f2cfbd7cd8159b3c125ed6d7476e8ccb2132c7f8faa1306464d52f84ef9522e8047158d97f74e67ed8b1745c76dce26c2eec8e7184425ee476bc6452d8cf9b12e3d925e25ab2a14f29348b2942763c54d8dfa02ee06a9a898e32557cd4c2eefd2d960d9928ddae6b1bf94dce39cd8aefaf3952be837d0bab80292177dd34e61867a5e85974b98ccf62eeaeda11aa2399e78bebf1338573170300e87f7a09ca1220228bf7cbcfec8c9c44d77f6b0bea5ba1bfd9ada29131562ec4b23ac038550cde17f3d3977efbf3e973d89b2ad944737c52cbc2c68d30fe806a13c26b76103e182b00b3227956e70f46057623883f90bb20df7e6430afd09e8cbeafa05974a24318c5f68e8e2242d914b2d6ee8e6a24fa079e70e9741199839fbaa2f2921c9d06c548773e6bca2cc9538f0168b2dbc24d96886f807a978c30d4984964a99b51e0a5ec2d12d0113010492e71740c8ddc04df194827236273572872efcbf7851c4a5730b33c2667afe1f8f31a1ea43370438bb76e96c5c6fcafa71468ee3479fab21d24dc2cceb1fdf9ea38da014a5815bf5e837f3c60b53e51470e797f8e8fca1a27e5d082780e290ddbb07f195c4f20e47ffd00cb4be4d0cae9b3d891b8708a9131652a5aa5adf6931c2912978af5c605c2683568fd15d9e64e3379f295ad91929d2ff8b0008b088b5b285f38e8ee6102a54c24961c30f21196f729039d29df4ddadb9975c83c132c7e72a20aa3131e4ad84a2ed88621eda3fcb0cf560ac05228224d39cc6ce0d0c5190d2830e0d39d23bb3138b8b33685e840944c4764e9da232bc54cd8ebf3fab7fd89216565c06d428d15dc001419df1f26cb74e98cf8bdc88ad68d9d01e6e12d56f3c48796073dacff78b7b7c346e35afb731fd18194772989590714e110b80fe45e0cced015f9301c4323c89470418c7ba36ef6d13a809ca495d44518c5ff5e58321b7acbc3d9250a4d0786f4a817c047bbf2c3a155dd79efa7c8b734721234d44971b4483b06975ce2c5f5390b2614848ed57b45e50c5947ffbb400700eede7a54dbfa65091eedeb6dd39bc967147b1254718fd2df7453e88060acbe82cddff371a57df99d7a60b76208d922b755561cf23b99048b5ed4392228917747ceca7e20ecade451ecdda85b27c075bb03e4459e13618e68099ea66969d8616145fc5a056f9813172f8fa43e59b12664a2bab2554ea9f5c19696b4a76981257a92c9507df17f8efad2bab5e9e11d77a354e1566695c256d1b25e05566b643f878123621237f859e9efac70790dc56086c288852d030d2b4de97b74f2711369fcf371d98de184a011b879ec9bd1617548df0cd3eab2f15b348a120715e02344274b9167fa43ed914422432b051f12abe0d628013d514d813e31aed71e998c887a04ccdfe41de2a09f937941a102b1735cb7c23bc4d5fafdb447d77edd1cfd349891ab66f35fddf6bbdd56d20b459107644a2b257a6a7aec6cf814ed6b06c314ea8f84ae95ba14d4d36a5288658c2569d2c02b0bb3589efff41471a5885693a00d57db65f3d45cc0fc7efa7902aaacab591e98b228127a84b29ebe85d773d8d9810d5866d23e13896b54675321a1e1b15742f7580addb55db02d1cb48f919a1d69458c907a24d80269562cd8a5db6c935f72f08e4f44d42d94027a63f0507463e27a92bd31d1c511d4b7a1f6726a44d35f2acd94ab8195faf979d38d785768609ad22f6bd6f5dab81dbb3a60db12008222a60042a0243825aca5ebfc372627bdb065e154098075ed352195248bad0f55c29435a58d84b294dbbf46036127cb76d2bd61d292c7651e8a8cfe917587b3cacd04e8652cf0ca25db392b60f261b1a4a9bbc782cbff92a677a5e907da54317a987ee10b73f78876302eb9e1517163d493f60e96681f6d8ad7ea96f3700d5438a3d7be151741374aa700c0873b711d30e6cb893396f9195c5cfbd8a00b2c20010aad942751600eb1ea545b1497655c847ac9d390f4cc649973f38056c549d550ab64697146a22eeb19d7157ff1c63b28e18d17487e92732ce0d35f800f2c2e8b9909136a49a142fa43a4a7d0b1342dd949a6bdf7de016102610261023278454ae35ad48a660c2f88a6ca48c8857bf245176d76b5d819b66856a0c2ff4b98cba2997b6ad3a2c9b009654596525de3cf755ae1bcfdff0f21b574efd5c3ffddfbdf4d3199b24d96e2103336c64847d0d5f6cd74cd956300d0bcdb594a425bf144e19962d75627c3066374b7aab9bb6fbdd0a0b7cadd6b8fe8da7603939c174ee50c8c3db25c1a4ea1c2ba37f6d385373632715b982e383a5a4f27aee752f8441a1e0045332d714271e16a9f5119644810735a3d63654c9570879977efa01c02c9f519b80c2a5dda3454f4a8b2a2bb77992e0aa05121400c8662ecbd37ca95cc0177a05ceb215c90e50b1cca757befcd9d39e07a6fe1da0276dc9845d4de7b8f1d9788265264c8426fb6f5fd10b5c168765b4dd063e2812ebefeff0d51bb83a542462a061bfaf98f86640ed8e9c6232887149f12f0a5f83ffdff27e2ba91c83b74ba31c2abfb111341e644517c2ccd176a1a2dab9b33a26d67ed49700b435ac81a9b235458727833c176c4a8f905c5313bfbffb932e11c974e22e174859921b59244a6da7c0a8e83564b4de268d1b118b7591b102f1bbddafb4423dbebd5efe2494a77f71967af63063aacd69f62a6e54bb04678884066aa80f0b230cc568ac248a62c732547c092a7e15cb68346322e65745a542a642832e54416ba5387ec6a3099b3c5ff7f64754393cede7befa59b9903aee7f9f3bcb2d285a590293249326542a5a68e0c6d7880a56be2c244337a9bc9841d7755da2704cac141f6de7bb9f7de7b0b37b5fa21c5d2432af2b9f7de59ad9170f6605b564fb8bb7bd27f2348bea9079505e166aa5b673d9aaa620a72a6a29e0235725c2a792b4e248d6d04d3e58723cf568ac5f992a5a7d3210f332ce9a888adc475516ce7ee4cb5145eb803578475d7b01a435d144bf6e173fd12013a9282fac462f99ce998e8115e05e8bac467a76112972c7b1b9adcddc3aa3c3a45df000e89c6a0d33129863c140c5e813e301e578ae71d029733d2518ef595062efd4ac0561c93eead4bc7f4020b56d6cb88a888c9dcbcf7de7b5310e504d7c29a418937af252a09b51a2b570aedebe8482937f7feff89dc7bf5fffffc55f8fffffbf694df0e3e06c464d598fbb4b1ce3d952865b87e51cc80e0cc9ae24cd062537ada9069312975ed583849c0359574b2c59f59c843246226f03548b566922efdffff1b8686e4de89d37b2d6f25e4b6b2f7de1d6e50b73520941bddd6104dce1e274e77f70e30f1b20a835495f04f162a880ee8838ddbe456e96b2a6b5a228494567626d04665bd5a60a5c68c4e6f691e0eeeb0e25a798bbe4163f8246714a85349c0908489fc887005b9b4b1d4dc889d29857d32e6c3cc757747bafb8c5474777777d8badd65f1f1d7f3f77abbbb1f650e185927650eb8b877af06feffbfbbbb6fd04ebd272fc60cb75ce5b985368004abeed17b252245fb8a98788640516d19bf9a33af40c069c9edeceeadd9643d9167ddc1abea2e8b52dcde249862b9f1dc1e122a02ba5f7c67a5ecefe7eebfdf36252e470a33d635e265d2b08bb2f25916c559b94db9c4a8975fb0cd6a6524020f4a2c09d885b2639b92b93962f6826d514c211b05652358dcdc8eb06031981c154e8d66454890adae8bb12ed89921b68ae8d5ac99a1b4d6ec2c0b52dad9eebd9adef252061a2a63a51dcfffbf87f7cabd579f056531b1284db91e56c853935330453b5c33c7b09f6888d512d1d405abf2a345cc90c422c98e60242d7223bce48d6082c6e4189fa63feaa187b9c01a137fab2f1bd5a04b77e14a72fb68d8430e090402b74e5587acdac48bc2eed7a5c77e68ef9f8b7fafbcb2d2aa01e846761f5d826c13b5cbd89984f7c294924a61e1ec5bde08154a9731249c207dfc690022230287a86ba9ec6045d75a0787fcbaa3cee4fd989c73034a4e0c776e8ca65cf30e29848651884eb9e126938943e113b5bddfd76b98640e58eac61a2a0e4eaa86249de07e20c587076f1187c420daad55b07afeff7f669b7730e5862d8e543827492eba2ae30a1c8a631941dcad7d82d9c4fcd8cd458ddfedbbca60f4560831cd97a710e40a170cdd33ae3c9c32ad14cf0874fcff8ffefeff933307dc26f62f1d82ebbde59fdfec43affebdf72693ca1c30d596189a0759db7fa0e1edeeeeffffff1b9f526fdf7befcd362373c0394021d88c363086b34afc51ad85b6425c212f07ade2976ceaffbfd7bd57bff09f05b8e45d7605dbd44e0a2a696e2e53d96cd6956f2a0376177495ffffa1b2c797bb81545c48cd0d717807f4d9acfdff13b50b74b8d128f1f12469aeaf4e4a65cc762a748f4bcfcc70caa336f7e4f8feffff1fb8516ef6de71d211a3b7bc32604262fb5476c71c52485fdbae7209302de706a2428016f4786dd8556ba72420c6c8f7ccf8e661f5441c338f1c8aff0f38b304062dd21f310c983417dba96754d91728ec7e031075e931dfd93ee07321895a0dd59903ae7f183dc36a70bbf7a6d990340428dca4ae20936b9b4e1dcd4e165457b70534e3f9e561ae4bca7155106c2750a49833a677e663e6982545f2b11580170e1131d0cf0ed7e8a4e849c513f5fa34f68343c322482763ee8f0eeb453f70e2b2eabaa9c114fdd0a8aa63cd2cac11bd72edc92a47fac2eed7a5e797c66537d0855223a76bdd5c772fdb40c96f9e504f95894f33048e73b6345561619973d2a68c15adc2c1549a3c52b59d39e09a867bf5ddfffb94f466247083902a41a4ed2dbcc84299587c19f474f3aa1148d9e4ac1da83bd2e28fc904b821d91c7d814076226853fba0c95ab7372a7c291d9aace26cb536a99e804279b5d5245d63ea99dc6df820240b67e2ac6e7d689421d5dd1d2eebeeee3e1fab7244d0e0beb4416975093707dc68bcaa943e7c7a48e893bde5abf94975178235f2ba46b1a25926792d576a923679d2783bce6eb25c16081ea2e114fab3748cc1920447dcd9dd3d28d90b520edddddddddddd6d68b2ec2fa8a18293a508cd9b56a305439897cb8f0817526d9a624897f88496f1cff3c643dd9f6c61e74ff8031fbd258a7b0f3d3e444c4ab840a2bc9c78583ce958a47a3468e45cb3dceeeeee5e348d9db551152356e36e97c5bdb554660ab65887eaa3e413cde686aa8cf4d944c8ff19509ebc723c509bb4320f5aa0b0ca29f960e98c4fc7e4d13409974ae5f556426778dd7deeee0e5cb2d04ee13f93532ea46a0515534c6eeb89b79b40886d2c3925825ea6a886e891019e90f33cb3107a7d557e8d1561cfd4ae8c46ec2598356d94841d13ef8e16f18baf195266461933b39cc126fac5b63001e6d695754687502920228118cd8d5a59af644f2b06e3eb5e25ffffffd92d1b2d69d8881b44faeabb8f9ddea6f0634c2039d276f609c006cdb3ab7e90884a8bdbd22e25618d7c6b1a048023e74c443a9babc6312d759d35d470578c5da8f40ff8443310d08e00a318080400c4401c09e34813cc071400040eb2909c8040302c442c281286c20020180c080ac000000010068341e140280c220745ee615d070d0ca45aeed304d6a2ca8b6926b4d36ee5721865d94b577cba5ada1efe02c85ce3288a7b59c7dec983e6d8038458efbd2a81d9bbc70d717d3ee2d17dd7d19a0a3d2a56dcedf4bef690fb77ecff7b944122dc3d3e2fdab19244208ba2f87369da6f50a230a607fd1f44dde52cf0ccdbbf6f3070f6204b189387cbf681b38bcdf629ea80f8c31aeb38e40bb95c2e3ac4600d00811f0e35762e05816673adb44e0a0cdb4591313b9610e22336d0db4f341a9c8f6c374e0d8b3f39193a068b292f115aa3b9054a51905b96981f2e4365c950e04fae7866db6869aede51568ac2322d54a74f25c7cc1ad94aa70ee18dec76dbcf3cfbf450ff8e32b13530fdba6454a1da5deaaa6fbbde72a9874caa80cb13cf45975d2db86c65a36604ff2795e7b558ba18c3a610d02b48c4170968a89baae10a9b063c583e5543eb4e26e5a8a0c04409a1e1184d2a4b65a895f54a8345d8112e1b3761daaf06f8ab44045fa147d6376c84576da35327abcffa2ea9ad9cb916bc4011a3c9cd17f051ebbf0089d207fcadc924f6ea510efb376d7040ebd5955ca63f118fbfc1191ec4f21169531aa0cec08930420350c4497dc933d7f0de254e04946a942049824699b32b36b3d96d9ec30813d9913d45b349b0e1f3d43db3c77e61b5d58176a6f2f831d1461f3fe6b35d42443d1189712153f40fefd37d3175c19cf01a0b2b699e075c0944690db7860ee59f94c69244989da3857ea3568a48c5537c36478ae6ac14e9f038b4a5aa94a0cd060ab97950b50996b5ab613b57217ba48863fca4e32b39318601e98b29ecb4d34cdfdf24cecb0945ec25c2372043627b53bfd0db58f56d5339126b9858d8c08abda9a77722d78b1c10d50d21d849bb20e05ccece3d28129632e8471c0675975f2ef4dfb810fde6152eaae16ea4c8ebfa42a9fb7ef87e58a268dce9e3dd91c180a3763ceed2def19354ba37c42f9e4a681dd132049390846fb3c50b0235ef219e882b39941e78ed16c5aaea5262bb8f091b865963b75ef6967c3990c1a7c58a665790e132ad267e6b24d56eafaa5e9433ed1ee2cd3efb266a2f61630c94561e5c1bd6231acf0d93ce85c44242a00b41181afe48871d265d79e190f2f4619eecfe76d1a2ad5c6c6f134ac271ef45a26d7783c2f5db4085cb7c1ec0b1e9ac9f0883b45deaf0b711e2452ea5368ba911b25a580976071fe4ff72a6f991e5c11c7df7b9b5f9a31ede42d62aaf6c3f3b51bce751c7542b93d8441c38edd89abd370fbb61eb72321c17b60cb9b92c59eba65e5a1871027db0aebad830b5a498de1775138e0b563860812601e36e4a90b82a9c5a35616710755f28e6cfbf15dd6cda87039cf6095fc070a85390e2243894bba147fd6cfb9c6287952ad7c37d8291b782f24201344c52e0ec0c9c682edf2e8af99ba2252b63f2031f3bc56a8256ce7ccc5f482fe00c9ad987c33ac3eadd9c04218dc7377d4272f7fa1e2270de0e75c3e2ef8991e1ec9e649f6513e9cfebb680e99ec7f447b7ad47242d4819e9bde405922d90ad788e66b7b1f2a51cb4c991a4ec8c3483a9121d104cddb5b84d28263e5ce1bbb3b4f3c2efa879a33e1b8c8370d4e2bd381bae7b7028341fb7d2824360e2dfebf1eb9831a5630d7cab4b09be7d0b531d33907ab625dfcb8aa2f0d5f109ce3631b26e8e0d434c3aa3f0cf6868727f70e7d53fcc21383462cc6d2aef2037c6274d20fdb6ed1c5a48f67f5e445d5273f99475ef12afa4fdfde3a10f7a54b39eb63fe77a4e3dced42c979c744f703f7dfd3be7a01aba782623925cf0601e9d7fd0fb9aed9d0abb02cc5ad0073f2f00acb46298306e89dad930ab3ac59be8c67cf7bd0d1a677eaeddbfd2e8e4c806638407bf094d111016dd5ab1cf86a23a755eba03b1b6569893c308bb65081994c2341db9e61bcaaa5715ce14af4f57650f3b8ae8008098cf48185ebbc8aa89861a05ea8932109feed278e3c896e3ad8aa052f03004b52fe38a71ad66286de852acb2d014aad50a4b73bbc2a780bb455bb415f410d2a5297dcc654855b2db42ba1d3af9a0b8ef4c0b27289a118087cb12d1554edcc9003026d124485f1d24e4951b26a22d6b9c0fb3e778ae03c97075c543c9b5b156b68ce45e5ade585e72c7060ee3dbd640b70e676414e9cd07baf1614665895bbbad08828c563e6f68ecf6a0eb214d687a9a608f0327656971ea64c057edabaeff02aa25ed35e99ea0e8ed029cd6c54fe588576d7400975170499bb848c0a5e4053acdebdfe843023f94d8d133ee34cbf34760b5f9b385f47c005a28cab61d56f1c69e1ee84eb7839b7efd0186e5ffb3adb95d60b44a0b1f19997b97f196932ee0e8422569a740ffd98f249195d7d6fb9039b4b05062f7295dfe3b0705c962947f62d0c60aac5e3dcc7f3cdace496da115287014865c810c28e503ae98594f00ae28788211cde50813b834ec7bb12aca5585e6bec2f264c3087effcf0d1b8e6fb8db8c39b5a2abdfc67934fa39133a16ff3b1bdafe5810999b4d68b45ade46b72d905644ee61171aaf7df4fe6909d5e37e0db9944ecdd496afda1bb7cb26bd88a26043c509598064a422dc151f644583c291fa048641c75065b11139747dda2473c3a378de3d85eb28552861195c3daa1d08b763b762d447e12452d119bf532038079df1fb87594a018c57695215a4e2dcb519e7f989d3ba2c6a33c737f5d7276d41d9ac588523995fcf7beaafb4b51cc623e41988319defff66bd5a4a675f24eaf2602e8deabfed541379ca56ee3e6665866d13f8947755c23b935e52e9c6171c25926a453cd0162d87b66c99a5f1f9263c97a94ddc98783172afa933859264a925b593dbee32062491982416e5caec1c7eae1b6f8c0523e3ec433ca901abff2f4802e1a194f4f02007529a671cf2c9a07523e984e09ecad0078eb5d28dc5da378582242cd59bd2477fc1d1c6e2d5405320f7e668f59635c8a29e717eacd591f8d776bd46c36f36993f253d4bf4a1f5d0a921da07e0504e0c10209be1b596dcbc6401c0615cc18a596558d70e9c03d5b3bb76cbdf72960cb807761771eb8e69916b4c34bec8ac2c013240d517d413184282cf48753ca833d18fd8bbd15a11c7b256e8dbe8f06e9e3984e023228d2337ac19cf6557ddaf4fc72ef50dfd59fa544334b030c2479bad5de6ddf36a8d480c5742c5b2b1bc33d7aa2e80adf978041489aec2eb821ab9ebec98343cb254d6c08a256886a2d5073efe9d6939ae2584dae4c08b2980f5be102c2b2c1e4e834b2a103f8041da1073ffe5ea21341b82503bbc9da30d72ca3cbbf7bb7309c9ae3bc685b3ce9b5dadb372c119caf0918f6047bc190996da57b0e6f2bb92121663b994598790eb5572cae3329251f11a8e372c2947623db33fdd238803853e14c1c8bf9bf7ad3f3e852138b03fd43b0836e59ed68f0b76c37bc5726bd8462f20605681ebf260c501447d89aba12e0728631d5492c2c0a57c564ca626c9776208ab0b5a87d54ee2603ff0199092e1c3be0e05aac5bf457c3d710f612667aa4706bbcac54e12b0adf41bf2c17e8425c0cc3f9337e25c772f957fd5a0889ec20e628812d932b47bc20a756ce96d7f5c1a532a6f45d2146d88b9ccacd64c0d741c2b4444290d12e164e1acfea9fd3b4b6317924f705637034e117df47f5d058cceb8671bd88d29154b4edc1a380ad56abacaea33b1d3f493bedafe67a3305c96fa699fda68435c286585ddb1c8a0581362a32a27f397d9f86c92824ba2fdbbff67ef9ae85009ae55f8a6249541828d4d45da32037f2c7281c001e80b935404781e70bd76f5414397374399174194a82fc9c6562564b5934742ea707671dd39c49080d8c1ba82727c5fc8f240340211034f16d14bca7f45138a15caa7735d6889586d3146890c2b0592480ef2dfd49eace22b9520cd942f6a816ea57cf6eab6290ce6ac91d4f90334c7dd08195081520ea77fc39cb7976d3814e6f8e2ad2a2fa8ef2c54a06a3f891e196c810b0ed6f11d9719eb1072d7abc30698a1d11c0ebc22d6d11d6741e2644ad05c224296ae5899c3e07be59780f4ce68c13a50e240b212a17b0f7a5baa48fc44e936f112295a36bd265968f7b64f48e008cb17b12ec7a7a6ef762be0ceb349f620cb8d79e632c14774e4f636a56d87e9d8ca084e87fbcb68807d1864e35c1eb00745291654eb0e214d1d1727d73bf7ca9c0f90826720824e76f0e6a597771d75b0bc8824bf51e49f639fd10155b26a18daf298666c169e29fb4cafa11631d3cd41d1145ef9723ffee0338b4f47cf0f44f716d375200b67e1f567d222e2709c7f3de272178ee22b60ae7fb711a663a40109960dfa47faa50a59b4903b9c19581a9f2141190d8f40495da44394147e5029a937617dadea3dc03d4662115f393e38173d2e8a0dd151842cb098939f1ab9647c54dfb007477c5f56b5d5e0ed616e6773d5b26bba1ff0fd6c1f1df0974939f13c509980355925580cc7609f98f3232b51b3d19c9685c5ab2ab26015673b3e76aacba37c86c98bdc9e1408d82ca1741ffb92747c27b356c46194c63f4a919a322b52422891f094221d7a9b8a72cfe7b3f4558162233133bee7274e34aac4c8faa867cbe30c9204437731ba53679acac02d8441de30e296994cd2ea0b5f1b476527e3bad1377c103ae6d323bde535ad53aff6649634ba5eb43c044ee914a801c28561730af15c3e991e0a3ad1abfcd108e02e46fb08ce4a118ed5ac9e1520d6747cf8fb6429033a166596f77278137cb66a5b3d4062ee9fc206dd5c5686e928475333356ea6aa86a9c4edf987c78282031943db8b997d7b518a98b0ed56e7e058d1eb846d1987f56d703f3ce4ace613b68dc0145724c011006df741018eb26d3a90e4256be98f876ced0235a25600fd8b4aa3c9fa19b483eb83662651adee001d345e69c6199874979ba9b8e3c12a200b355e0d8825fd6502ebf4c29f0ccaa6a9c5f6a27ac8bfb56436e13f14d9e8801ff3b751c19994eb0753499fa12490d27a3dc8b33b0fb8cb6e1905bf0a140b1b0fd4d62cd58ca2cb7b706eec4ad2bee14454502f46db72d403bc7d9012483f40d78eb3f6364686deef3faef8b8502deda960dce505f87fc8576fab64965c6b41a18622a2f4d0a14d85e221fc1c3c50c0fe334406e56545cc3341756a7be4c1e4ae326cccaf0ddccd599893591b88f841ee2bf5daab39a2412b1ea1a97dad2cc38611531063ae78dfc0b2f823a3f185f1e2a3c0f5e6e2c1155090d73b1105873d803badfb59677b554996c2f0bc1f443fc266a200715184fdb375313f077c5ab4c3b6ba4aab038c2691a3069b2eedaca20ff3d23e286d81b0f134a35d79f5c8c48348a3a57719366a0b237ed724842cc393f674f41a7add14d075951c1d17fabd91cb19c6b2e38d278af2f89f6b63d9ca4d23e15a8dec637e57f5347ee12c1512e0f1557e9ddc3d5a8876ee18120d76f1e8e3c7f1ef8d9a605ed24d02a272de2ae89988b8bb55a12b0a860614b2c1dbcc10b052c0f89d638c88e2278c517db0b05e7c4e8cff1ba0c226882f18598bba1dda5656e286f9d4e152deea4030a7ba3df174f20079493cdc636c458aa84f0cb34640aa41cd1cf39f9aa1a1f06356ed6077a009286102b8b48f46a32cf6ac68864c24aba18b2cba7e61aab172d81fe4c8ca5ee8ce26d2fbbc406e8034482a6b7cb1c4911e45cb5e8113d1728cb38c84fa2c5113244b090f7d56e245e2e4cc216b6c59dd1fc2c0ed89b82ca88ed8d14438c60445c003e238b47cd90ea0c1586498cdb787d1d0fab298b2beed5469cdca5198b93bddd36e479e7966a25c03f065387884e7fb699d3f64b167c2413f5f2730f595ae70f5ea33cb3e83ac97efe5c79e3229e29eb3ad9c111f91de4f4619ccb19213dd4fce36efd4f6c66a3c2616cbb90094cfa705fc96c4b458990ed1aba8af4578368386235899a98dc4d8c2954ea2d497c03f0e1b714c6d842c5643a49e39b1cae22133f6c4e89a38f679b313cef8627fb3c2999b3239ba07c7c3611d9c532f63271cd876c48cf0df6685480d570a5f978f5297d3485dbe2cecd3bab699e6d5db777e46a16cb612eac8fcc1046c422caca3a28553e26ca349cb52ba23b89527cc288f4e06e0231657887a1734a8f430ff9a126d4bc670fc6a1d4325d3222bff9cf029ba18a82db76b42e7b558e8460f5c1c437d2a1a9d287831901a3657e372ed681df847ad4840b47e2f98b23b6ca1a08f48cc0cd2634dd6caf5200c7b4bdf67de499289a96f0108b3293a5f97c394a597a1d59c793a5641f6929fd633b42b201cc1f29a877bb2479c483bb642a55d4a7ca6471338e976b32f1e8bf01001e85835146792c97349c93342c23e0d20aad0a038ded2015b1c2e264dbfd3b5300c9ef2915d87e1caa91b44150e3050439c3d2a19d4242f3db722f7af3a246945c93b4250a5c1d2aaf7a7d5805cd43e873e2a7371735c81fdbf481291e92b3e05d0aa7cbfca57690b35322fd17eb54c58afcab81b411c08d5ad057cd8350d3fe5ee1665f50df4e94073d273e369784d1734359f65be9829bf48d8f9dd9ffcaeb93f8e0833966922158ad741e52c13bf6b202e8db773f13511565fdffcbd9d97f016f87d8b623d0b47a1577f7f16291cf5993b20063d9957277411361a8bcd1d86e20930b1069bfe21016be478f0033e8e04a60ee4cb22e800f049ed078b0406d9d2aa1bbb1d075a556275d61f29c18b93cd98da9e6bf74276c96f8249072c55fbfb6e3af297493a8e36d0226a456e40b6acee6761c0d1eda24c73857dcb5d6dd09b39abf76e12c9414cb5f9bbcd9db12bce75128fd04411c462c801349075c5647339eb1ab30b5bfc3ae3595b3a3af97565d3c717cd7134550ac043016eeafdbc38885f7e2456b7df942bfe65c5d1b7aaecfd8ed19a1046c9e9ecf5cbe50fa74543b3508707b8a55f727f0847e6be9e27823f9b7103767e8e0720ebf67840fce2560008b80c6cd696962994ac076330de23052a75511efe1bf339b33474b93d492f3c94b59de27d438617653fc6d41829e70808aad7a32439019febe8f077191e38b31785f6fabc3e999e97028e0105a6b6f91337034c1df47f14f9c932ef0178d72c3e009556cf7c3d901c0a6ff67023972373718418e07330bcb7870fcc3c96d355795f8ee799a7607ce96ea7aaa3bce9321fccd1d0231ab259a8a9fdcb9072e86e29d6fe0c24d09f7b43d6543f87e4b2d9389d60075b0575ec30d9b5a13a3c78ed0666928439669b01f75a4a1bf7c116a75f8a266ba63139cd72e8d03bffb09ba44129f02ffdb38bb3e9a65d446b0ee6acf3e90d1d613f91d8fde09a7137eef7ead14a6c5887327c603f45c6b47d5dc50966efd7cb1825d8b3c96f46bc9bb8d907aaf9078798b44f5bc35c785da84e389c2afa262429e9b142582ec97038964ccf0268472e526e168179971479c110bd975bc64ccf126ad0c801ba9d6c1f79fb42693ae8103900bb048d188d53cb020037b813e4f13002355a98ff3e7b10b38496dd6268ed6121ab138d62e03e7a1707d5f9643b0fdb6066b4e090b01a95cd9365dfe55c9f77a55983be74a42fec61b7d21aced0e13f31cfa60244015ef5a4d371564056965843b0ef721cc06b0937269bfa5dddfd7738cdc7c7ca422716bdc12b08775668d13c52d1e4c193991e5f7b923fdd0f8ae8e04fe0a7a0ab5de7a0a2e5043148c6451c75c045b2bb5d88a628c42b6f2b8397eb81c8e486af0119d36eee1cc836466741b04de22e42e91c1c79b144f540c3aae95614751c99ac89cd66fb8dd224341af4a237eb34733ea8a087898069f832950d14817ce2019b1381dcf23c0cad45844b4f7be3438796deb015c5878eed36536f69542705263bef8dd9e02559b6ad65db1a1cffe154fc62b22792698aa39b7d4f62abe14d220fc19b7fc4cee3699aa1e101d93a49d3b4d8e7e4dc56cc91fab8e032dca73a09a616613b56215edb1b1a909f4f490c6a60467754366f97bd3722f3caa85d8349f2c8fc71730a3e6682a8ea46e3c33bc32532801f2cc9d18eb0ac41192eb22e47178e33ff75d7db3377f4adbc0cdd82f0100470f5daff73b04786e28b813328d5ab132344b4d003ee5df49828c7f6b88fd2f867851e4a3d4689690393a4882e785e6e562c8f5df0a3ccf52dd76c4d4f6257fb60092a4afad1231cc292e8f084abc90f725eb168e5019fa2a3f0fa20ef383a2d8b9741f081493792529fb6c38d63e4224ed4a3ca65e320bc5c18c44d14c1c404db275fbc55d95084bd8b67808c9901702cd2d3bc23540a2b311b0353d97716a10d9d085879270ca8fc359bb043a2c7831160c35dc09f56a656c8a2525f001b4ca9c759bc02d7f2b39956b2bef296cbfdd9ecc5bd94f47785725cfef1dfdb1c29b41a55d931c21f75997d35c297900dbcbe70fd2cd6f0f62f86cb666d76c4b55c4a418b9b6476a8e134841ee171179a9a19e59ab78b6eaae0ae89a9fa7c8c9dd5e87147f0cfbc51e88af4af929983b3c75f09ae282538f2f211b36eb571c98bd1345c7c28cd602bc2636d54226199ad599509bbba6a572c4bf2f57224a858f55a9215d3ef1bcb5fe25eed5d60d3cefd9153cdf7bde0e82e4cb01568bd9e0c00505c3161f9353aba75d97c3b7b2fc0ec251cbae66867664dc830fac61dc0e0734235e51783443842bb825fe1d61a795af92cc96b13dd2e2e9e9c20c7203b3ab3d4b8899b2290c5c561e017054dd88d5bf0157cce862f98bd4643783854d51be06c4cd4347c5da2c45f6d16f347bcfbff4a4fd674ccd5626c0c0b04d0ead4cabb43507077d7192207e7844758eca5f3ceb8b82d4ec07d30fa66ebe47c0d56ec1d1717b2fe6a7e7b95dd7ac36eb9097c0cd8631116da273b688b21f41fefd29f613953b453311ea97fced2c915f68ef182f4868cb16d2b8b0d566e49d41f941db6c5176a752a5635f70b8a608d2399d393954f93df632f4f9f03b68bed75cf972b3721686d04651f3f0116053fe08b880516fb129e9f79c209833251a401c7faab62758809564c0401027839add4f4447c31a934459904db885bec3c2c76d11ebd43a7245f7d979cde08b69577048bb5ca005184f14c1020a2804e2881015a30b08a9f5dd9572789abc9474545cdee87ba7951725bfa957c31f758a62769b7691ba9936020b7892fb112f453f0e4f50bf7f9180d24c317c726f5c32932176a499752fb5771774cd383849f8262c049e551d4324847eb68878be0accd87edd2afaff8743e4389c89db83f6320ca5b21155ed13c4e6d25828a20553199040e75b874de287414ece8c02ee7cbc7c5e54c3b46d66ee501a4b401ca0905395d408c519b0e8a205e38af5c17d68a17f6bb355aaad015f04ccdc19ce03bc4e11e7015705d4a37404795b8826461d300f8678233c84fed5b9a0e70c0588114fbbb4c623b2a31af02c8dd87e26a1b1902df0b14a18e64b73529654a524a19eb039d0328033c600da1a0a307ab8d53d49420ac1b8268d951a9c192a34241acc834c384648dcc18f960adb539f05e1fe4d0cb12d011e5c4eaa90a8f1254979d08542b84d9aa91c5a8698525b2058403135f950d4c587ea040162e476cc00295b59503017c8874c2097f2ea58880bd176234a71e3634b47489d206033acad4e881ca19a62c5158388acb859c9ce7b33bb7d6dadccea241626709b6a89cb1633123c9069c0e94d020eb2011910a56497a54f899c009905ca192eaa282561b210e82cf8e0b07873012cb07452d92d185b2ab376cac97df18decb8509b4ab327c6ace30e94961fd6068f570e64a8b8fcf2e864944c86e16257956e087f7d21a3fff327578f2f58632c1bcf0dc9a426806e511a164eba525c4745273347237ae5e61700ea7cb231401cb01801aa2260e3d0c96789ad24c2e61915251531154a6b904b21d8054374c21436fac7123bb1c22dec78f0670068959e4952500b3c9bdf776fdf3b8f7aa2952c5a2302526186bad4db2f6286beaec1028da63b646490f26c861c90a2b03266bcc70656101071e416e93cc0caffebcb7cb13c9a6f7e3ffbfcd111c367257b5dd2200deae8c18cbf55736fe47a5f48429d325c5c98a09571215ad204e5b9c87e640604bf06462e1377780cab38b002a04b2b0d2fc08e9116255250743d6128f141ecef070a5b05efe23bc4b6c684591aa31f81ee60670a2390f332505390c095ad343ef5e19916543072818a640f9a2e4304819a9408494102c87d7782f24c0a163071532415236f03285ca490f3c62bcb88f0138a9aaf227854b95b560e8eeffffdf92b05162fe3e60b275effdc9eebdf782f44ae85e25b938eebd37cb2cffc5c94ca25597a629a92524a8a1ca951380bc9063d32385cbc783b03460920ba706504d35784d807c59238124453f45607e80f2d1c4cbbede23440f0d275662b0e2ebf1e3c2e40a0e68aeca6479f282ff6437e14ec4eb7d3f52383f101c08e999485381cbdeee6564f260f7de7b350f5c4567800cf14133c34e8c160f21565a4c6451269c07d5d51c69010bf7de9bab3912efa07e9cd47b435a43a946d38dbd9103e84ad4161a32e820b343325a12c60894a22940840ee03baacc5b93d820c93fadae6691ae2808d680a317688fd4ff3f14100e18880a722f460407890756c10f7884e80945a68a7e7234d07019a3e45463cb162740565ca81cbe0b1ca85efdffffff236007dbe4baf75e211dcd91424138530f9a088153e8def0056373af8309b5aa6653ae2c9997d20cb3890a0910887651a60be50bd0ab53904cd9f9a48bc2c3feeb3093161c973d2589f9c1c29a3339a0352b44598145e920db75ed6ea8b2a4644483ebd8e0e8694696225351b0542c3d8039c150960e304b557c9e43e827fbff7f90cd64baf7de7b3ff3b93e4bff7fedb66b2fae4969b3bead0828bdb6eec8366ddaccf05a71440bfd220a753bf50174daf6706729f59882adba766cfb0ba4e47982b3903109606a9bd55fd034d072a4c5041a6f09a4a9a831de96ff5dbf30bd228ddaaab619e57982efb7628cdf2981c26f975555c93728ab39535b9550a01e53847a4cc078623b09991e67db6b8eab085f7a7405603de21b3dd6eddaf5941d90300df042f6f60d487e4028666aab9a84ab6b2e4b9eac5e7d238e748fd20b8e8923ce395767aeda4e8c26a44b8902535bcd0fb07be6248aaa5fb50c6c80daaa154b38e1439e5a45bdb513e61e6595f842f6b66b1fd102b7a9c6ed9b696fa04155306c45c34ae9dedfbdd7ef5da2d65ac942aba7e58b6951e178121959be78f945916187930bdc30e3888da62527132019724477f49aae4ce065079a22412d4c09f3442a089519178e3c09fa32e4d55a6b6d0fa61ebcbc46aee05599786a33a60368764678d63851218a48d5db19698a72438d23fdff67650de5185680c8f4c02a23464d56578c042e63ae5c18e25b410537c2429d3c3d2a73fc9b52bf8f4ea3c15c929249d92591bd2530314e15e4f7de9bab39f2de7b63cfa493533a6bdb1879ba8002f5663f9e484961e0c4c3a49a4d14ca15ce6ba88b88872847a4230a221222da610140a705a635c7308de9faffffa71131117165fecb6a425522f960373fbe93611374b500ca784025be7f3032e9c198ad1f75ab1f38dc1d61b02331bb485e0f1629179058e9f1b510e573830811b8b4acf87e4192fc1cfdbfce7d9a58f840597baf452202a5c4347a187233e96a0485a12775d7c8ce02aaab415f85d8fc50fdf4183965d9a1378306d696aede0e183030a25d3f6c1753acbe0e97e5c3edd9ac57f253d273a2b404232652140d7269c17b9f5a7d911e2594442a25e60e3fd5f57f6d95ba7fcbf5faf11bbfff7f5ecd9118dc41873435e87260ac2e8e1d429c40c1150f128cc112e5e3bbff7048a727afe6acb5266270e10a0be1c17526c606dd0d8faeb5d65aa42aa42ba4ad2e9edcaf38a2fbc7f7de7b1d8bd644694ea1e0bd5ef8d629494c2094b02c6024840b11263d348ce9f2abb26404c74f942f2ec3ccc807abf7dedb7b0f7ad9efbd61133a15c25318326c928e0cb1e9e04a19ae2d696ee07066c5c53931385e4466fa5485e12ec7f3586badb559319e50b53af86662b207435bb0295914fd7fb863337739803b9d05a08e74e1d2b5e48a56d32f8635638238a541d202e144bd3aeb3520033a43831f5050a6c9488fa5200dfc7035b504074f39e7b1eaa8199f30d3ad6df558654a68341c666aa60a6905a766787cb227791477b36dd70f7b491d59268eb8db2d268e36d7a1dbb74d30e9d85f94f5c260f1d220cc7d3548633b0ed83017c17e3d5ff40a8c406dd5c50658f2d4321443830f97bed8b434e09a74c1964cab1e7dfbf42df14080fda223b1d3e768807ed3b76b920ee3d80defb5ed7b6fb494316c8f99b6d96e371c0e880a60e2a85d549d9928d270448ca58e02ee4b8f1a5b99d5ddd02bf10d2b0f90868f5ef19283d7af0f6cafb1166e5bfc764b634a0e5ecff0c08760fb4f496fa44431dffeb5f7be9e6fdf84706bea7f599d5fb3a2045f7bad9602489e5aee4aa7ef6caae719a15b439c82936d7596b247da7e2d5d3200ebbff7e268981e31a8c74c8be951db6eb812aa0e8e987138d9ed7663b3c5b40e693450ac9252c5b63b0ed856632951e0e9af9a9436d71e0321856ddda902c6e9ef6308b6e3305fd9bb01c2940297bded224ee1d227c3b3f7c6c2cd8a4afe74cc8ff7b79215eb9a53350fabf32e67e998cee9d3b530c4385f8cb338871987c3d196d0709869be3a65e2a7e5bff746be57aba0ffaff7fbbdf36b13ae49dbfdb6b09b76c99456a4ed75687b0542eb0e5a6f50ea03d5a9bc4abbc4dab5ab1e5fd4a4a4ca0bc51183e29869af6ab3edb1f26eb7dab5c7dc25625bb1e63288a1ded855bfd7249a444907ae49b069895c8deb8b55579d98be3ad764b59b9aac39af1b886bb8268332c6612d5f5d41517cfdf2c0be795fed25bcb635b9edbde28b5b88232d47b686b78c9970db2ede5ed47f788e6d2f66b9ec943f4bc72f6608f06145cc6c7bb5f1afff7d66f5ab8635c63a2fed235ebb7ed546bc7e692cd26a4ed5339ebffb8e03dcb76f019dea504debdc533bd52ea60092a996bbeaa947ec7060c48e8f1c90d4a6695524a182ce1e98433abd66f5acc190316d2d8bb4fbd22e8e99da68aec7bc67b4574dc6111f5d63cd01bf1f8ae0daba96bbbaeb162e5ad1a9ed451913402fc682ea1adbc13acf689386db358ded482d475a8e9e4a152841290210d0fd1a1c61bb96246cd7b202fb61c8184f6c0b636eaa2d95188fda828db3fda58373c7559cbf44893de240f86dc52c51548d018f698204530ac298c0012108d33731f15e18f529fc8281e108b89f308f10fec01fecf7fae1dffdd19668341c66772ca06bed557bd5b48a31e8f6aa46ce39e7e7fc185f7bedd33ca385be85c2070a1fded37249a1a8efe90bb98f2069cf1128ede9e3ef780a457b54076b1769d4e783563d12fd5d9b2ca9e40935deaddfd5f177bffbddd474e86ec1826041b0a05dc7dff9aacf8d20df769eb5c183439ce9e3170c74710bb38d768446c361f61b8f9aecb65b02a84e2c86058ac168f896801876f2a8f8600b4c93436aab2158bbb9c85666f796ad826dfb61f665b1b5369dccb1b097c5186f6bdfb65df47a02b73effff61e8e897f8fd02a0493a762a50e27e563df55bd1eaaaa96bea5574a901c89caa471cc3870c405e7d03f0e5fd1da3e04478ffb8090e330ee1ae2ff8929d8eb029186f061814d8d4cd440b13c69360a4f8c07821741377b37d187391625b6db56317315fe8037d36dbed769fa4c0c9411149b1c1e94df08bc4b63ac8eba6c44998a0a26388840f0c48679452ea4e5f5f7d4559f32c2793edd136ab61524a84c32de936a94ae2d13eedaa6dd2119b645b3dda2712fb33189a36690af976d826855b56a86c8966eb9c502fa9f69a446b23dd54c64e06d29e14860c1aa482ca1a8c1a065dccdae76f28cc745b7156058097a71e6795e4c0dddf7f25b822faadc1f98a34d75507d4ae6d27a84e882bd2af6c7506684ecd738ae30b1c7d3da7760f61cf08ece9b24a05bdb7468bbd5802b5551de6c8aea71e65d54ede4e25d7b4cc71db08785bad42c232b63fdf5ac7985a5e5dc006dbe66c5bdbd6826d71b67d6dab2d9108ed8b893a1435288e1a268eb65908eda228e2a4cdb42c3eedab1fcaf6508416f269bb7ed1ab9f5eecf4695b4d633bb22a282a98209fb62ea47649951ce50f5632e0af8a64d78e65981f767d712651542dc391e709bfc77cf5ca926186ab595577415e5b6ddf7738066548f1756b4111f6d25fc2b6faf5bedaedc57eafb59a6a2ba2f07b67f9a463fd35ab69f55fe35a047bdd44d7606dae88d56a39fb75ae1b889037ae61a0e6728235b94262f1e2400c51d18171db454c7fc796d5f48543b01adb5ab63604cf3d5a4ff79c380eb3dbb6dc0550e2aaa6fa45a9dd7dce266e797048cd70c1019182a8048f37d596191904800023170000180804038241d190222749e93e1400093daa90b87c5038228f86e280300c0603c280601884410080410008831880a438c693ad01328aaf397620091664207f82c8ffb94c94ca28f2a22186b8faccc921abb45221e07f36c2b4ae35d6075e817f7cd1d7d5ed3462c30e02f432630371434ed80f71e138ad96722dc99a042cde33cfb032b8df92f685ea1dd8e62dc586836f38f0019b9bcd32db88e44f0e274d264f0d21baa0465e136f67efcb5b2a3a585f3c936e80d5fce88afb44f630c9e488b16f56ac8d6a7037952b640bbea520cb2d677917217ab1f4466f6fc1afbb66719be1519965bbf7aa6fdc1ee0660d8c2c632f97ebcd738946b4fc2bdacd00af9d069c1b36467cb8cc7b563768065360989ce863c19b78899c4cce121cbdf9c4c2f201ed318f087f59d4a61b795d1d1d98a5bccfd4f5d45d04d27dc75a8eb9f7e7ac433184f1a9405d29e22671a93d954c47ee3d7487c12561fa74c0c3b183b408f8db10cd64c56ad8d12ac0ffa52a30064f2bee7185992c4d6645f1f96b048d49f25bd79f47093bbc8932f0bd4ff42955c78e25d5e8ecec10187c5fe22f9f93810eb97201aa55732db3595ecfc6f5c530a0707c1a3fdf7c029c7509fabc611e09f721eb827ecf13102b362fd64985c89ffe03253bd4a8c3064d8c68c81748cf6bcbf3de041d29f779c7f39d35caf1fddd0bdcf625d77f1346f3541a4801d6cb098d212fc7af044b0cd7dfe6bf29f14226299654e33380e9da40743f09aeee05a31f884fb260e0c1703a4cb95db670a050997290410927a38b372aac51b29d2df8ea94daaf3429e68c5b99f496ca53baa8760dce0fb643abf0e80855765b850f43ba013fecc220939941594c7221cd500066b7f5811153f634c79e89a4a73d158da084072923c93eaf0dfe1b0508f90a6d65f10d7232dae76b90f6d3dd288c148863470f32a4810c255908d57669b4014efe425e08b03995304449fcacaacd1ebf3ce9f1635d4dd9035841a5450718d61dc0dee679baf7b18eec316b0944aee2c40c72a59b1e276098225f5fd13bfa26676bfd6df65536e7ddfe83f212a8c8e1b3fc1ea2afe5f4f79daa680e6b30a4401d1ce92300f12e2789e0e7869069633beb3aa9381300ad6f5df8faae2eed5e0256f53d138d67f156a4aa29d1d201fac7db55e5c61e8aae11048034e184f8bf174d3f4c72978969b47175bb9a32484a6a68f6f386041636ac748094e56be01077ff23008e62cf1095e22f52c5cf23f5c63811fc2460863128ce05a1bf80a11d9991fd0428b96d878f1cfbcae64e2eeb770b38033b7c5a57c3e61fb8024798589afb5342f8e1fc235e2b7f9608aef26e53eefc49823ef02690348ca1086f9e2fd48a34f2cce1d866d919b698d801c7fc180915d6eba48d1c623b8251c4951de9c198d4a4fe56dbe0526205427f384fdf44e430f6aa09afa84210a75771a59b7fbf398248240ca5af1c22f6f82fe391f1373dcf50d07fb84540480ea8b8dbf0508814fce04244a9726e6f5664d1132edd91c6272c22e5fe06249ac4746a7c1759a0b7afbadcd1fdd7fb2edb439e30ba1f3dfa80e38f6302e0f3fc4a284e37d9e663d1b28725cad04f9130b144f1563c9498a89b9c2ea81c088295d87199954a988b1dc718228b1cf5044c08428e09abe2d9a5fa9d48be79fa871c430052b268b41383f6c4a31edf76ce800aebb03635ab1c553ad6e35e05cba81a783ad25785a194a733182cd70b76aea02349e3a44e6d9a8ca03da5c80ee8b93bfabf45945d8bbf178461f5ed29644cbe16e1fbbd1da45a509e43992cb14c3cb6208db1059f657bc92c64b0762cc081b5912136885d1f7cf0eca27fb19498f6c7cb9eeab7f0a2f0f99c3c9b96afa3875202f7b7186e443592104dc565c52cba5a0a46f5678ade27f7df7e3608a2051b11f84db4fc74ea16d5228860fe7e63e0f78a06722b004e396441957ce8c41d71479347a710715018002e6c3e52cb15851666b40ec8d663e96ebd02995d54160aea95b415c9751c5e9a381ad742f5014e0037702404fc936a723aa8afe2f8901eaa085c147c8604ad090a228ee88f8ca5ba584ee9418766218dc0dc08e1ca08c285810074e0b528a3eda68820b94e4c3fdfae325973ba9d35890e39a734784399e2acf562822a37918cdd15bbbccccd229d53c22df6cb89597901e7ad88f9d89014dc12cddd35c2364675cf36a0376cba1a19f7f3d451a5a7cc1d9b19d059117f930ae6bed8168ac35d4dcc82c990d882a13ecec1ea30813310fda9f896d00c796827c8f6a8abbce87ad27abbef8415b035cd2d4dafdb47fa28661237ea13133512ab5528158143f2c98d192221fa797a1057c56ae27630585cebb9879c45efd051dc2226798619c0fb5183865ea14ab6b14493ae99c047be64380d4aa93b8d937edd347b49f35a52b29d792bd03aba71acde3f72d8aae259e6ad6802b9c652d488470afe027349fba9c8501b7d145ae0c8060c49a27f6f1e8e469813c9114a05dd5ef2b0f5e819a13bdf7b55cbd09df6de38d44d6b0e65deb1a4a0e4896370c3984bb294948e4c759343cd6721b059239ea8db752511495f6afef090cc6808b968af86e52f32592764e1ece95bef78a2901c2f8087f602a935e51deff865400af364b63ddb96c413cea3a1b0aed04199fe019b95770c407d546a19b37a77f9f892990abb6305a248631dc2aca362f2c1ab7da6090d4ad92eeaedc4fa2efdfa78f1d6f0714c3d7c6a3093758ab9eb66ec764f325993e0f9ac1272935f17fb0596f077b8495a9b69ef61ff877c9218321caae66e370ba96ea88c5bb1d37f41adae41172d190c8d43d743d0a06bac476aa64e89181ab9273d8bdcd7a29b963634e92ddfac13d2f53760f3939b8b70508dadd85b91c2fec4649e471afb7cd8b3231c98794985d6c0c56549b05d31be1111757d4fa778c7e65111720a3ef8f8fa1f6b94c0a62881977f86468a170233d7ed64229276ac57943301c9a89400a791b6a7b1a47e62339f52fc5ef4e7392c50e96af9f2ce54e2e08c7f73386b79cdbcdd41580a797a7221ea46bc3dc6f4c88ab94201d5e363141db5c57882ba095abdf86cfced2218300ad66af020034859221532c665c96a3a1e318e1b491af6a6d15ab43ea92311d3df857b614537553f9c38cbc517122f5945cca1b09816b72012e84489c8267f5347f5c9d25f9e93efbc0e1287f41be474497fac89341c26037655dec10bfd020e7b3d992c91c83a9828ab9c4139f8d4d23db3d44a4b669e3cc1e4c2347ed9c9834aca89f30f07dee019e5049a3bb80fb89746475f10076810ab7c914222a1fa1fc7ecd2c51fa224d8a7e922c4e0857f1f75dc553851ea8fd52912df3b5f853646320ec46907143d320f19b0693f0a24878f515c4f6db0064a5307d908154a97165af06dbb33739f060084f8070611628aaefacc11326cd62040c0311e2132c3cfc43f9888ea5321e01f62f36153afa6424558b831f824aef414b2338baca78c25ba12775ca0258092f7d4ac5e8d7046f98c7fcf8ca6e9dd6339ca97329ce13b8f0c57c54f913149392c07a6fca8a68dedf3275c601ff373f58ef970894cd718731b15cf7695380e2dcc6601d53b8d00fc166620e344efa1aee634efb72b85be6577a2d02745f58170ceeb96bc849647fae47f1bc71bbddc9c6e7432c0c40e24515d943d5a49519d13c879db8a9aa03b2760555830dffdefc5c09e859d9f77fb9a94003ab16fe52b10eb0b68b794279fe6178578489445d71b45fbc4143cc0466e9ba64e906d51cb0534a460f599a043b20986e5d6013355c6f96803510a41b7b23909befc076b68f9717a0e62d1fba28fae413fb963ebc7acf0bb208476d5b2a20ab671e7d14055f73b92da8bc0ff8c1dd2403a7bc17eae46381116b91aacdda11b7b457d4a5d5478337d4c42292642b0f3b60e7fff3c63375784b1b01102876831d9ef60e790baa1ed0428f35983aa4013507302d9918cbed8eec48c6de212e0ea621190385e0616529639548508b4d557753f8b8360f84e46b887f3e9cf9dc79126a4da56db92b521647ce8a5ef2ad93c2c56bd850a18bb1a4bed1e37c2ce4a7350facec3fb3f42dfbd9bcba98a3016de14fe91f8adae815d20b2d3335527e33ccb461d2f5fd269ed883a07760e0c55a97737aaecbf9a80ad7011768fc2bf9a94b9f5fe1eff408e0288463c22a71f5ebf7486b927d392fb14ef2b6d5cb3c48e78070199e4b5eb79bab1791090d7139884155624a5a1f51314b38e3813eb4a230c0382f0aa583dc720705b7b670b4f0038241c509cf94e46254041deb893f890947414600209902fa500b02c1fa82f4a37e471f17cc1def5d60897f4ae7f7b72c4a5de99ba90f6febab6bdeabf1389586144a85a515fcc1e8d4994d59457c6fba34beb541515b1f30f129fcab0ba482b2942a864c5fb101432851099b6072bb88370bd1bda82007214b4c04da8a2408ff33bfa49ae8ffeaadffb6261ddde4303ca44015745a819e2abefac1670e771d607bc0443449ba0603846df818e6e8e4e2621139344ae6950f78f674a0dc51e51ce70ebef4511045ebe0e08818b1cf0f2ed0f4af8c4bc468046ccc440e12843f2cce110ac9f39d6210dbe4367f91217a3f5ab4b29af76bdd114c558dc1cf807ddf70552f17b1458ea929f30aa187c21a6c7991bd0f01e316b03841f800980393197e090b6b966ca3bba3d06d6d6a8164f7a0fb739db04ddbaf3aae9060b445b9f1c108da25448872a6d5d31ea5bb424b0ae9f1a4247d5b93892b1d48740e55d87499c52092a5a13bc3ceb0697b319524a371b795763744be76b6694e075c96120a11b6028d0a068b41d2ac5429da64e47df90f5decac5e0c2d73ceb0986d5a30639e7e8fecc50acbaec856bbdf78156c1c728a82eb89a02b59e8617d1d3ddad0aa91e2fd0a2bdf0f69c6e8b66231a0a7e94174d17c90fe586a1b83b09cbb4fc7410a1c4a559dfede27279f37c8cf9b62182997069b3a030b2c6751e28bfa11d205866ccf9431bd3e893f5d13568f8f91d7447e94032e545db9a8df5f8b6c5bac85948eb4e84acd148f59c58b706fc6d9ea0b25368853eefdb242a81456e813ef93cf6ac76aed1a1193aeefb22823f4f67e82a14c7ceee637152cb30614bb9a36f52226f9033ce608b8d21a800c55926a29d8559ce59d3868f53f9827a01160a30a12fb7947ad05900741251366d18dd31bdd7e0370eca3f65e7c281292701a9ea1cbfa2aa593c6ca9c2795e0eb1146e7fed9535c52545280d2f349700efcf9f9480626ff9df067eef44ca44e28a9bb4435255fbb0678fe9fc1e80edd8ce83fd16446139469f60eb30c4966414cd1c954fddb228f724c1ab993ebccf00422f51f3cf383bd35fe5f3c0324d46b3766a94bde369c0c27c6227662fba5a84511528ff210e0886211dfe777f13a572c24414296978f893144e99c709425dbde890023865f78c02d9683e31526b6efb8b6268a0bd5d3ff042fba895cccf2b4297c32fe725dde910dd9ce4c40e8c78d47b957cb01f2097bc8e3432cf77e62c5f90864c1cb20ba7462d71392f751a9b651c21b3a38d33952236baf851ba88f16707d01a65eb50c6331e6f725194f3170b849eb67c70ae0246be27898362f9bd75bb7aa836fe2d2207a811e83d525be6bb04297644e18b383988e57bd06671d4c887a4a417e611a07204f67be7d7e221408001e2fdbbd802481b3509672c8e1735627694257f342424f833f822cab30d5d0ed2d600812921ab460ad107c57c1d5170053d27121f62423e5451171fb06bb14028e939f5cb8238b2fb03fb5a90f33c7dd406c309c95a450f3a8886df73ed49d2c8e4bc9598a2390054a2830fd4fbede8b9c6493b2ea082a81d41d2b41c96b25504d4a381813ebb623ccebd1041fb34b3799a34348f9696e65e26fc7265f0020ff15990c1f4936b5df656b4d1eb8c82dd111f9347c734c284c68a5f1816e1281ae6142ab2a0433cdd477bdff121181c11d4a902aad3624ed435ba805c98b8ce62f244a5f18f4cd8e39302759aae7f5084621f998a4f3b0323203d01b8b83811341b659e7046f40b496d077b61403a639ae8345f5c2b563d691a150fa3b8372a7afe658cc9ef8fe76a1ee1faaa65b350324606fb939e83be898cd22c2dd9f182d5c83a5023f763f94aa429b0185c535af19b06be46dc8ba93a7a0ffbac931768ce7273ca014b95266c643b42fa81ef81936990549d0b1280d243f95b570570e5105067f18f7c5761351b05f1b45288e7c807e10436a680fe665da6cc9d7c9255a327f0ca2fad39b36fbe7843d1f2d80f3a71543ddfe27dfa1291f6edd60746e18112df0e1ac1260c5da93b9ed2cfc67a47b5f17fe18e0adc8a62880906165d082e0639fec5ff69cf6b8a89eb11371852ff3cde3984acc9f0ff0ded7a281df6dc5dc73f0075c24cad4852ce8b7c44b99e4cc537a2f23589bd2b3653d105214ec010903d60e1f5df8e488e150f036157c58dc7510d6c5ddbafcd65a4741db1283e5c718e3144ff44789c8ec06a6ee293d2c88bfd6cff393cd1a105e40e8e495a0d7d6a8e561b3cd99173958833acc1b626797f81bbd83aa02e3aa19fe313e9bdf534d8c256feb02a3c80c3ce7477bcab7d5059d0cf283c703efb50f171d6a2c0f73b2e1ab0e18dcf694bffa52689e1706e8973852a165ed73cb4427f43cd76c5145287be94df3a2c08fdab864889064f42d6aea1f09d44adff70aab232418839415dca01d18eabfd7a3f108877b9e7bb02973b5991ef3a77410fa86a395289afeedc82ea779453747b5316f2d484f1a4e0200b03e2aa925b5fbf36fafb8db8b9375e1a5b863c3fe7f1109aea1022806d55e5455b041c69b1e5004940fdbbd57f6440ddebe8ecc3060a67f07e8e0f1da83979abe9810e6db6c980785e2a2b480abee610eb3a6610577dde4c5aff87f99953a99a6099fe584c2eb05cca058d42734eb4a0ef4f9e47a5c58179f1c3ce1342550f381941ad50ac12b8dd57ed4d3301c94f2fe085cf9ca03c903fe6515142134dea6740a74d788ee0e0322c86ae99780f55c3e430ad637210f0ec3c045dc5f5121b758cc8344620eb70c7e14d718c1c9032b9ab0a746b424ab814422a64b21c3556b5b8f5e98eae6a482b100d9e9b15b00e22c084a1a07bebb71ec2e3aa80ed8827b54f9d84d1fff19d0a38fb7cb8e892db4f78ff7344074755dd282b0c3433fa93014265f6c86c87215e6e574f717f92eaf513de41a1cf34991defa724c813df3c37a92235872cef766d7d19f30ef901d580b04e63175102ccc105d33fe434345436879eb2a67098cca443209875294482444062672d78ba5c8dbc515f400648ddcc7174eb36115b5fac25d91c1d1c530b163deafc5e8e80b6ef5d4bdb9f2f5132897b3a7f3829d339dd8dd7765d3a61a762486840648e530d893117e3e5afb721220b3007d6966b7c8d5966072bd2a2715e00f39052f41c9d48eb176766ea7fa52883b7d25507a65ab652d763624857916afdc6980acfedbe6ea8bb8c3537b8b87bd966f454659bb33ee31c221742e1eea303d281127693380c08ea0cd422e0613d634f881c83fdf025def8b6bc7d584305d0771c81d919ac971518aa59fec9799966f56a0832161988c67094f481f9f954b5f5e345f29c967520bdaeac58c9530280415c7ead61f2546d696d0af67a7e20c4e3371ad15de84743d7749e65010861ec3155b45bb770df901591c56bed4862dbe5d76ff4a08efabd4e1069b63ccf4f54f549586d86313cbbe1baf95bb2f35dec7dac356d5246fe4261e49f48291195b13d124c751b0c637d8a4923d5c9c0aa231c186e8a8c39a66881059a439dc1978698e53495dcf923f7b7b75ffd0efcb32dfa4780bc3e7720fb4cd0a3beedb1a3f120f5c5f8bdd7ca5377b7dc44db7dc3dd9a411917dccb976aa3b92f6dcc288f38a0fb02a20dd9eeef3b483b97ef0bc8d25597fbafc64d744a569a634db9b81dc081bb535862e80201cc4c1e5cb7555e91f71843fb83428311140cb87461531dc6bfeb3609a34a1236a9620c5414c398caf28475aa1d57c78d3684dd8105ebd9ac96f805ee403b3f26c4ba94a6f01f67a275ccd8ae4898ea2cccabe69a0ae5e495a10804dde43d8630e1342f5c38dd54b8d61059f9a7b6eefc77224e050db996eb53dfda6505fa383b09c9e7493861801aac120cabfe91042b053bf149ceaf37d853b6e58f5b38531aef3e7fc045cf78bc8d5d4f14d1243d37d78738bce0db85618b6c8a6a96dd4800752dc8abb6231202307821af9260036071c764e46d05faf59049224ccef2e20d4250a8b48f347bd2067298f8b8202075d80b37c6fb061fd1bf95caa60d7a53ccc4f9cdb445996cecb01f6e871a1bb138a3a2c386dce9e093adcada350562ccde0f31d5ac40fb541eda1f605e63efa97683650cb65a86ec6f32cb347ba5bf957605dedf0adc6882dc8ecbd158ba4e5541f39e9a49e1ca3c445f08ad9d9e35b9039f7c0714786f3fd0df24c8c221b470cb1d08a395aebc45c1de45e5b293fa7404eccc40ea11b193d692e5bb20ecbd6635d37a461056eaf71bc4c1f4c643d4ad5622509d5b39bf389d8d083d868ecfaa70f26c507a4fae027ebc48432fb0550f0b92a1d189bc6ada460033c66b07caa80d54e34cdd5d34d93a7e7e59c81088beb22378deeed8bb3299b3ff6a1dbb4ce8d3b6799e404f1abb791c0075da5c109a306edbe6c712b48c18a95db7fca39404e38ee4b2d7bcae4d400f88c4ac1eba96aa8796fd6cf22df08491d042711a032b65790298050a42881067d453407bcdff4810edcffef83c83268bdc9a72cda4f46f0b110e20d71f07f06ef64f1168ac700c278f9310b405054f7689e58d74a9dd32d274b4cb0565f2bd6bb27164d11fd9a451003865705ed2273034eaf8d85850728275ff92de70654baa0f30a4945180d26442aca004527b2ff7d86feb0c604ac816996364c454da6467debffe2cf417bdc68b8f8372972df4882e131a5a3b3fd115321af3e28e906053a655f8728f3d49d86c43d0fd1ad74e576dddecaa11d214881f5763a1e66a4e390006606b1bf7f983ec529d67e96356ac56a52cd57794638c711b1cbce65d2e9a151a1599a67e6e03ab0400fd2ff24021ae5a015fb89e70bf92229ddba87043784b53e229cf88b897e8c8cf01c5c56f6d71ccc9135f074dd035349a1ccc51b87d4eaa6cdc6a302336b0b7556bd44df6fbac509b371495f4fb671fdfe29925aacb62243619b45e4c58535949642592261dd8c3552f44d6c5f39f4b8ce03c83b72e90614cced29b0a9811dd6a62468c17eb3951d7ef2609f6b7ea590fbb55d553adc6009d2b905a5923fd192990df7afc4d4c2e3f6924fdf2a414e8bb4c54e76cd1cad86b0dfc3d515e38064fe67f132d40c0b6c51df2df819f8471b592412c0fc16a83012c037bb4229143177258528a297ff35bd72f0ed93910d0f92793a5796f25c9a9440b844dc17a1c2e9795868bf36760b6569ffdbfb86424f86e666b26918df0df129bf1573277a9041fd288106f5ceca00f90f414305dfb2bbff484bf788a2f1511e13cd05cbb8e530b093f7b56c581660fd8e8636b61b37ac97952e9ea318fd7712c76bdaf5b9d02dc75c0b690bac70791587d82aade91de5782ea8d03ca6a01b1a495ff62b3ede97becc5e80916bd9c37bd527743bac64ac2e2b5bacccfc8e1086807722efbbbf1e152cc7e0db00348557c1cce740f3bee8e072f3c6b86e69853c38bd008cf7bb4c7f9b2f75ed1bff79779094484a87c5c279062723334312d8a5940879cbe5a90803bc0ba5cd9349e6351f257268d77d0c541fd446583be874c162e7959a657b05ed630f0869c2acda7d0429e7a795c65b0f70492a9d563c7e38181fce6f33a7f811d5355a8e9605e9e9cbd0530c36dfdf90abdf8da5dcdf86d8ba52582d0d84eaabbf5768d7b16669eadab31a7d13ab1279580cf564b32804ab6d3b61c770510693360f3d6329ed282df407b076856dbe7086dd48bfb4f8728e47fcf21b219a0d06b32048df2f97d7ad8df7da0b969962eff51cb578c115e18a69fa4ef186b6c7a36d1f6b33568f5efe59dff06230dc582113f17deba752f2f398ea453026ec8a454a087126dc72bae4bfede56c940f66c8d83e1e030f069c494af47efb6e5e1ffd7503179cc9655707d100ada4c21fa3182f4bf57584ff02862d350a54a593870627924433eaa1b4e78372a454853bddc14863537d8d5b145ecfb4949fc7b944a163197393a963533bd6d510f6962e7e53481fb18b8979ba0fe1ddced0ca05ab2f05acdab0a3d76c53932505144b0d6034612d1c4c0a4642289f6f325f9c84647bbcbbca6aac76eadc28dcb9ba6cd8dab0d680abae56d5fd9761e4c6e546fe2357f9fda784c1fa513ff007319afc0bea97ef447e0e57b2ea2970f51d26015af89d7a1ebc3e6840aff92b54906de85f83f48435b23da256be2e804170d422de0a2d67850b7eed39096891621c667d05a270cff897452b970aee4e9acebd23eed85d32619338b2062664265bfc5320f116b14f68ccda4f2f4cd0bbfc1e2547d878ba741ec83016a5251b2159215e6491ac41beb36c2889aa52bdc57fde109df7e5840d2841a936ee9b3afa861edc9aff6c87e5295ce1cf062031bfa622a9fcc267e327bc88598b5e6a3d4e262a4c964eeb0b4c085b20fd8c89f0f92e4ce64aaeed3693ba11dc84192433ecf3547cc7f34bed5d2f2133cf5a710f9cee67f50ee4fff45af9c6e824b6a0983d9778d9a9878739a8cfff3e70f8b2105c14209c3f2be88548f55c6c9dbeb2697805378130efa511096b552b37470d1ef41fcdf56042529b721c2653f74b1462849769c14eb20fe400c8e39cddc79383ac8960c5302e0817a90248af05ad1bdec443f604725f0610b401dc316dd3a3aa39e8591d609dc25b39d25addbe3831f5b00297dd7d1664558020adb0b456fc1d6ac833956554305a5abf49e346af2185009587cf47dc41dba855194b96ee005c446a09432548252a29b1fab685cc4a0db2f5818f02f5696db3f68ae3d74a747e17790a7cbc97d32e89700079f488edd6e06e8a19997c80793cc7ff05b51f5907b3045fa1767c7df6f4a9dc3b23ee3b73854474c9b16b2965f42ea24b06ed9eb5d9de3efbfdcb6a44105e26bc5e7da9133e20d710940ad363658138c12ca8c5528e994ed2a4caa3cc156e544848aab42ea5681138266fde494ffdf166286541081f8672c32efafa144051127d71ad6261ba7e4234170c718ef42423585b1603eb046fc5b905c0e899eb0a3537542e50f429f6460324743fcad51eb1fa469bb8c160d3642c8324a393a15321e8ca767a12376c2109736d9e4e35ae5201d8b47a729495c0a52fdc4023e6e1d45390c1a6773ad726f7194e91ef18e03634c1af7d6e6c66425e3a32da2bf847060a4574deb30dcc0037eba8a7ac21cbdb2ff83e56810ea5522de44ea0e7f9f247fe424976643119f0c235a1998880d2f146dbdb8ab6ba3ceb8dbbedf12ec093a22ad98cfac21ae2b530d079db60799b2392169b6a0ad2214f1b0797803363f172a1e53209165163ac5efebc95be47009268d7e2074ef2dcdb9148a7d9f0a0623babe77c8cf940315efa7b7b8b6585d62b1b8d9ca26547b263347ad980d6e3172de7b82fbadf471eda74c347991cd7c16262522d2499d29ff9a60cdc7cfa8f8fd3abe6c2aeb9edfad46c245fc7c527b1fc51da4ac219674c61ef1d95b21ecca8e483cb59f7f066629bdedf2b4c2e6802338872b896dc25438281435bb54800b8a9a46512699b59cc1bcf52f25992fea43c387a47f8e7e862d1895bd0c55d8f213ed5ce9a0607df7f5d2fb4b94122b0263a4ac6a763a84568ce550c9c6adb2aae8bc56ec9bf12a30f0dd9c4b386e150463c7011a183a1e8d3cf528484d5218b89ac08641af049e723651330a46ea6f6b923eacba3d4b4d6f25c6eb32a0ca45eb49ecf318e3657fb8ea65336af723c9bb7b107598c10de2de30a6af0a069710a20e96bc7daf45f2c857ff02784ce6fe249c76998e31269cf748c09b57e87346ce96427c0a82c53004368fa61a84352bfe59a5a77aed67f8de841177b533090be0752fc6b7419a86d1216a22b4b882881677549794bb562de056c3d7bea3589f8462421e15fae46c2da7e8dccb19199f8802a7004a161d1e715f94b8d5ef023009c5928ccf9f82b9a3830dd8929eb85126de4d6d0dcb5594ab1f329eb674ac7d5e2af4e6f3ba179fc4d3080644f9461ac2e48b4eaec28ea752c348bbcc704a54bae61f175ab56452bd78d166ded98b506352e71da23dbc15a5ca4a71b6e0c7c691c28e5434be6c77157ace465b403dc019fb628e33fc3c36a45a0f03b640222b34bca82654890cd6a54028b8b0aa8f1abd612a4afaa5d25a40b4beb86422186df25826c5bf0bb10add2982a45caebc886bd3e4fb3679e2436cf20475d60f14c2dc410e8c0d66b9c0f4c5de74d5c376f8e4fbcf50a57c4ea47f95f4ea0825f8ae8d3770d223f89e1c645d3bad92c3cbaa312ac295d87e54dd0192e55bffd417bca0c3161af5d14418970bae597097a7a6ddcf8ec482c2b67cd25c35ed7e2b1cbd8bd5965367c154b74ee3222f77b7d60d04948acf772f1c37e6df968a67edc617067a2ccb2d7d4069a586e1119b78ac7388acf088bd25788748d199963e704dc8f14e179446be97f7b959721981d93c86f663acf9c5301b16ad54a13b8fb548ec39308020ab1171e421094cd6c54378472b16b0c0b007b0d6f5d346d8fbaa3a931231c6ae07e6e810e595425d232420aad3184c3523fa2365d91968104350d823a4e4a2d855fc204f7cd8a9ac5283be583815588e4e2f55484f6b6bf1f4578f5721b64bb572cebeb04fcdf2e2c4872cb535e2bd55c7d1f97aff3e7652d0803b99449371aff5abf55ad75ee6d2be9dbb062c926c148b168befa8d8ad195157e8b54a6082a3800346eac612e7e49b204b45fa45715f5aafd8f2a34f596398185bce5f998ddb74d1889e351e66de7b52d7d65620d8081c727bda68a0014c34880776ebe917fb7e4fb83d0f44fb3ec0a5910ff352df034ace49dccb53e83ce3e13e636b81e07c41adc5b633f4f64a14680804fc996c0248077b2cd0d05719c9758999aaecfbd95e8b095c239ac0b92e9a4c193bc386840d2b9f626c9a53999a802cc47bfca004867b6dcbfec9b599e26d3e99494551caf9c9d75f46c98d7024b7954eba28bdb9850bca53533dc42741c09f20cc1e2156884f72798e2a72d8ca65ffbfeccf909ae45dd42a74ca7709c0cad3752d8c9caf8c535241984db1e2063d92f9cdbfb681d6830051dd48a7344a57efa028823a98183abd846fb77398cda67900e01b944f05cd511fbc4a015abe8038b62f00e3ec057bb24499d5c7af69f845cb0ac997f4e259fdba66981c3d07df33ae9fad4a2099aaa7dc98193a962388ceb925d62247ef7a954fbd26ce5391b1a80f3d0416d76c5023336ab47479f6a1d117104f0de5f073c116c26102eb895b94448048fd625c516e0be75934924be1b04b3f56122ba47cd518840dc20095b55b623f22114dafebe2ceb523e023b638af0ea6789957f0e7d6ab3574eba68cdecf0bcca22060c20a6851c6fe86dfdf782377755f6ef285bba84e517d7fae02fc7189aa1cca4c6626280351e13a4cf49b75ac9f6ca9a38640a8d8706e6b016c9e9cf571ba54908e4bb52bc459caa9b408df125d2b14ae176584f80c08c7e805cdceebaa55cb265894b9a7d8c0d818d44ecc37191e0ea2e658ecf20310b58c659c72e4b09c26cd8726f4da983ffc913360b9417bb387e928f3c3a0c21572bcfcdd11f05e6bdd81753c38b672c93de6788e0cd9eead776730d09ba4c234db2c3ca0997b973b53d8c97baaa06279b611397b6274e2023426cca5fbb513918992543903e570ce2722e15946ee5f424470b3841b7bcde0767736f6e44337523960dc4c5d9fe4e4c01f8a1dbc5603c30156fb7e0ca1be04cbe183030dcf158da9b668ebc1a6157ff17e380e27d8b370d1279a58043dc9da1c20be56add5e4294f399179f98485e76cf3da29c1074a4361eabcbf732deea27d93a229825c8bafd183cde12fe055f5de88a869f2679c4ad4d1fc3d8c4cb6c88852e9d53bc8ff19393585826fb6c399bb99121cca9abffeea7fd54084351c0a301fa1471c16fc6beb3104a3ee61e11d345aeb42822f004a97412b9db0581e57abe5a7fc33ab469448b6b5670e4389cd4010349555659d41ba9044382f8930e390619af26b42c3d3b5b09bd384186ae104802c3f1220529e79d2009a0de158349bd589dfa5ce390fc59d3b338a52778991b6b7ab6eb38e1867ca6d8ee75b55bf7c36067e5478bc0406ddd1eec63f6ad18e2017c1473a414833dd8292ef8d0361555ffc02902c64f12e4c5bc1fab3d60921a2bdc1328ad279e9161e29b28e61850192ae1328d077c28fbcba10ead32f298ecc373d72b01d58dd2e311d199da0deb58a13a4ba030adeea8556505ad5a4399e04e87e448152e7aa7a3848b0089182a854558b806ce508a24e94086935f219ffa6c1808d32e6158aa3a8a6075d40fa6f17086fdc31678cb07f60d6e177433236f4f6ffbfb5b675a326d444444812d902dd0804081308fd9c7bae3131b88831d61e62ede9ee561b0b8ddd880cc4da64b9d618638c09811d7c7313011020213a1fd6dbbe555b3010e37b188831d7b71880651f00c8b28ff99e880a51597e0b7683ff1c00c439b62836aa3296b0a26807ad1d16ec2d6aadb5f6f0931731bbf7de9bacd6b268ac06bf2258c239e75a6bd79a6bac190fde7b7bce3dc7746837d6302b7c546fe41f1a47b546d7b84e11a69d0f33d68db6b56037f893d59e8369cb0c8b18364ad1dfa5477fd1bb2b82156b33f319df45553cd18c198b6028623baa97551bbdb5d680d8e87df5886a2c5bc177ce3dc4982d3ec33bd71b29b134a6582e3c3d7c06c800961160b95e77f9e818fc952ed9a774c528f2901e2d47d10df728c2b3107cac0cc46cc646f761860a57f4ea04167bf41e8c0cb359364638e25fbdd7b52ccb67218e57c4f5bef3e5c3caeffc57431c11c1ea37b643a47e2dfafbea4a0be163f4433cbc14fd20155962da7cbaebddba522a17d63deae102d4e5a87529a1374f5d8f52d65dba3a00adac57c5ba6bdb8380f61d066159e98d0af1ecab49445958595d3443daf1626d06703e9a732e1273504008217c84706a8173ce39e75c6b1d079a163c1c61b25e6bece958c0632dc289525484127415092cd0294e29b9e7a085e29118fc4163075d6cb4d6dc83aefd4d51609632924c9481772d0a71235363d911b1cc4e88e5a865b0d52aac99010d049c02609d2f7060f1ef5c1201714b6c3fd75e6bb587abb18e91381102eb9e73351b191d4497822524f539e7f51430799a97534053c0ec695ead80d9a6793105cc37c17721aaf8f616feb5808881f511a20ab95b36b291bdae7e2c1b2f218c2c47219a757f56f443c40cd0217eb391bddc42af1f4fd462e42096dd0989f049f2c128a52ac0993733d38810430d7308834b8c202821c66b4e5284a33d463f3c164c56644556448386bd56db571b10cde8ab08c1e2d3169f648d0b907ef3ebb931dd55510222e6c6aa15956322a22357a5176314e188f1acbd17df73f041e86aabf8edc0491a6498d88163cc4be0194f4481cbb4a425592e2e2e2e2e518b3c6307e792830514e02c1440550244320e601d8f68238ccc29a10149b2b5d69a73ceb9186bb09c15b5a8041529962089b1d5a03d25e27b7788221cb426821bb4268376c4452d72149b23293d2865da9df41e7cad4a0d3e2b5cd4e20c1c01cbc4295ac21c40448d9c444b7090819641057b3d18e8285932b1600417c425b6c95801fbb53717dbd9bdcff75a9c1828628ed68ef72267a45f9c2c251089d5feae38fd4eef26603b5d5204a2f671c03a1781debb49f6398e40afbde65c14cd78dc5e4b40d7c4f678506bf155217a1e03143dccc973ceb9f7defb419cac080dcde21623c6dc2cf7ea6b38a0f33050ab2f83354566973108a1b5385944cccccccccc7890e0f1c4c96a48580b9418d681d80072e0e242448c920f11c89d88f60480023e58a03d7a1e78526566dd8f1d8f5957df0e5af341b35105b6b750981d33f33b636667799ce4dce3d91dc6d69a73cdb93a45e139e72c140f335603e6ca0e858af62063118db539b4ee3eeb7e9d9deebafa81ba3b8b7ea077f7b814300be1836d7359577b33dea98ba319eff462d18cf7e96ad18cf779cd909e7d8fd08a4ac4e7286b4798569bac16f9c5c134095a10db6a129d45167603f030d1723483fdd8217df206a010338b2cecf51124886db5f9e80e300b3f5d126d210389d704d359b49e81294370d2c87b41e35cc3223a995273d34544cbe8e96424c2b9cc1031333313b5d84065f0487fd139e7a28f69c2f8a0041f84f039e8dc7bf0819a0a11bfc7de73ef39e7dc7b41f48e44f4e4518b4288a1c28d6009518b4ed8c9d030cd144c632f3fe1278da397b48cbe15fb891516bf9748fdba9774ec27fc84260a43a0601a27d39e88628ef7de638d3596238710ba0a34716aadb5e65a634a641a4707a1a271c8b7da51d404c16abfd14e5690c611754b413a066918a45f102aecf54401815680ac6ca665f43b341eb6e4c11a86c50990c41adb18c83923cc442db6ec789722075927617b6f2e2e916b305689b5260f0482226a3ae0c20fda4ccd4c9118507145cd0eae705295c089931a29ea30859a99d783f75af09084379d210b3c348989375a12555102f71b2d79a2553193823640c85c3841c5d482058b5f0fde6bc143118a48244e886d9f2d5fb05c3424d7d50008b6bd21897e706ff5057142a488c25003dbd60beb6a9301c1e0362038210c053388c0099155cc808ae0f1c4c95201bfd0603f18a88509cd4387289d5cc704876405ee078e0930458aa845878495c004254487443a614d7e90c76a8ffaa0228f019aeaac2e56cb681cc496d1f7c10a331e26034b031d3b5e8f018a92b910c46883385971c8021866e2d422470b197864bcc0e33c71b26682709fbb16a199b1c2098d1022324d9cd0a0c91395203c79e2e4891530e184c6bdf79e73cef1600731718a9624e7449180799385040d0d0d0d0d0d8d47071e4f9cac181978fb2aeb81ec1b807d2ef6cd4f368c104155f55a567deec9f31911d1a2a89d2e1911c8ddc5c5c56586046c3b4b191598354633dc658b40ee8cee884abc47cfd9b0ad3a214a72ea786f51878c8c8c8c0c1106b0f2c41b7c3000dbdee0fe18d3b8d870ce31b35aa00c60f67069624e27608f40ce08fbeee07548194986b585e2796788929c53036cbcb7d8002516102c2b6ad14667891598d8704143d25c40d2608c6d4d0adb185c00c38144cbe8b739510dcc3c8184069a26fe205074d24e8878c184c80ad790401144d0b91358ce3dc61d6058b0509a732e3ae3c8591774ce39f7698f87350612ab991b6b33683d5807273861f483048ae2bdf7de3bbf7a9901cd4a67548868a51ab51d84c12546175c844d02b6b046d10c06b2230b7800eb08609d0ceb2eeb58acdb582759f7e81c454cb8c1caab7a2355af35d5fb592f48aa5c5a51711ce36b6ef1396602654f053172f4ee6f1cd5dd5d6ce4381263f35151ed39f79c7ccdbdd6de7339206b46885e8baaf0c14a447487fc016c23806d326cb3ae0887f51c511525c8d1388a8094f52e2d2b5525a219462c7e11510423072318b5584545022788a10adb188863267e7488ae870706f292aea701683d08a173efc1e7891b4e27077578f8cec5b93657d42cb82a0f8e96bd894c7407a326dd21f95a7994d2d94879dd8e8111734151c958644b0ff4d4cb6c13b1ec2eea65d235738fb177f916c262ec7d67576b26bb08d5bfd329aade2662dfe9a9aed49d2a55a95fcf9b6aaa298ab1686dea87ea68f9bc45108cba948cb1fbe28ffaca75d9aebca23ce41925290e8aca8bea82922ea8a43ec9bbf71076aa45bd9e4f6da95d05a3d702ae06c843ca83bff277b1cbeb9de59aeeb9aac8ae5c73855546f3d8a91eeb2c18290b56ab87507f944d4d2216b188518797d34459a72cfabbf710482b104f5877824f5277511451f542a6b587309a1ed244742c80811cac3dc4b5b0470e50ba27d56794c3724a6cb4ee3392262a9e20b14e1ce39896e1b8f6a66594b01b74474fba832b07e9955f50ca3bbf3926bb36ee458b96535f79ca3b9f134a2965c83a63ccaad750055d9277d5f50d6bbd7d5da7c47277bfebaf3c0fbff03cd4aed5adf72f3bb704e2839d15f63e66ff619bf9ba8e0af6bedf29b1ef308c6aaaa9a62eeb9d2edbb56b67551867690df559c594a42425dfae9d9467d38bdfd8e2d76a79bb247d5dc9f573fd5df670182e3e2c208f4a0e94cfd9f506c1de43b0cbf35d15fcd267d8576461df27bfd9c0770df52d76287f3d96fe5a969ec6380ff57a7878adefa1d97b9094f29093bc0e66b20f5df400e351c7a83c54566b9d4faebe38c875f112d5c54cf42f0761edaae0c1bef9bbfa7cbdff9aaea3d93c0db271320cf4aea365b8c3a837bea837baa8d76a51aff5a9d7d352af87a5de7bea05add40b52a929f5ba5894daa9370787fa85158799998a97e7df8ae392dc984e136bd813cbdee76f75f64b9d85a64b5518672996524aea5615c6b1ac2e09d3e0bc8ae7544172b8801ccbb40c77b7a93787cb3d1e190e52e51f3ca3b15505e209ab72d31fe5513dc495aace3249f8a592a7aa4be2f1384663df6ff4782296a917d6f71b9d8c4be294709025820471499826059463103863e79c57a9cef53e4a85909627ca43bee87086dda03b5ecfe9f29cabffaea9ad252d1dc207a52b44230e45bd4bd2a3389d771905a55ec6d78533f69d992bbdef4dff6a4fef90a77708afb0773a0c0bf17fd877dd0ea2add4fdaedbec740a422bec9d7a88cb38d00afc427dba30ac5e3863a71fbef31b5ec9463f74ded13b4f2b67289daa1f944fef20f86f08fe3b547248057e7987efbd0bc32e9cb1280ffa5743efd42b7a5deef7e93d410a560ae1557a4c6f7aef69a23b660cf2502ebe828681dc81b01e4f0f528107bcfc74b10af5952b052a89a460dddb9cab1548c1ba5b9beb235d3eb2d49a6e31ad216a057e69e9b7473473dfa51a8a87a7987a441f00cf57abb171589fae68d58ff4778a97acc03af877ea02e209ab478ff7f996becf899d8f100af64a967a75c5680645a53c27f61d5a017efa3c5402adc02f919d7f74c78433bc8481dca58baf6020078405adc0349eab211663b1142ab4d0c4de4649ac58b20528ac8b8d9268e10dd60a255600364a92852c2c61c24e963d7ec98c35808d922c2962b9065694840b4fecb4fc1a1b1fe7d1e6739a54561eebe5cb282a84b49136798ecce79a89eee04677b03a67bc66acd4d975ad78192fd959c64b761dcd42d67fd818d11839e54c87a09809bfc8f38c9d21a8daa997591a63656b7d32475943f4d6e36fbcae652304e2e30764f13a3a5ef82b14ff83bd4d529e31768ab1b01746363ac465361e0552879fbfd6a5ac976b3e5d56669d397df224a57cbc2ed33a751e60f1e9e345b97eacc3cbcf19443c0f11cf6724ec863c3bd3f00b9f558a6fcae12fd758f9f9aa86241f5e467653aa4c49a9174652ca233ec4aafc14235f4eba63a22eb6e81519e81d08eb4ce3328d65879447fc3cbcf8ec8247b9a677aeea7da9bc5d2b918d180903c16f38d7477e3eb29bdf0dd3c92666c22f90cf750694747819921e2f27cb4b9cab510fd89ce24bdad48f3c7c3c3c37cbbf30cd52879f4ea7af5857b9a296615d96c2c23fca831fa2e81d1d62b2ccf1f4d3190933e197c84e538d9d57935763a007841519888398365666c234e0b318070b5e20c2dc6198658c7d4188ad61a3245eb88251ec8f2df5e059126ddf7b14f528ea51d4a3a8771dfdfa4d14f5f7fabda99a445ba61e3da2a87b810a5e8082a29cbbd4297a77569d7aa1572f4fefbdce9bb1dec889b15ecf931eab5355be7371aea837977545edec542f102ba58832512cc2e00a273600364a72451512638c85ee7bff477b9fb3b5d93ebb26d1f6b27d0fe8c3db2ce5580fb9f4e9d5752d0bc4c78f6a7ec0f267adaea3abeb3614d5a72b241f59be6edbea110da22dfd9cafaa334b9faa4f7c3b06f64a8761b326d176feb0a1e9b2ba0ec6a7bfd619c659c6ccf5b2595d3a3fe58a9adfe17a615ccb65984d856176ca5fabc2305bd5cb76faa41586d9a946e9ecf2ea214c43aab05e3bfd68402c0669d0052696d0d859b5db45ec65e78b0b351664a3245c90b15e58c216c04649ba30632f576627b6911fb1bb0bc4b28ee650123eac7b649b06d1b69d93681bdd76b9c2b0db4576c027e9828c05808d9274a1889d707a74073c53081f1b07e710d63b7d3ec21aa15c51cbe80a9f72592d0352a730ac3a0ae7da5c51c3bbaa5ed684a23562a0a9c238db32ca3ff7f8a63c2e9090ac17c65d1867790696546198edc75f18c69ad821fc026b88dfb4b587300d56511c908abd19cc7e631b44368231b0eef409ebba58466b6085da590f611ad0620c043b56c723b22dc916ec9083ddb85694640b6fb00c440f611aee4c6f0bb111331a068a8468b6d51ed26d09f313fdc38fec87016cb932cb96d92a9ac18f685427c0d10f5534839d1b30a3e8067b6b1d31f3985184a3d9b9c628623f185bc039e75c51cbe044eb9babc1f4734d34c144134d48134c23c20deb54a199a970d940c66a73c4534cd6cd7dd1477346c618838957a8eaeed88f2a2ca2f014fcc23e53ba5a3947a6c24ece3c63b127b31a8a7d5d0b888ff92657d1714a81048693a9e02968c5efc34358d536dfb37e544e75f7a5988c5dcf1f3ca5b3739e62a5b27e68f67ccf7a57289ee231405d790a96c2ce3e9d5047f7f488536fdc30d74d259deb75b19b6a0ace61d964e9d9abb3384f63db3c4a164f71a9eab7aab7457af76af214b4f2144c039ea78090a9788cf3142e288c7205e69eb18d768c6d312e9c4e8baa6059f404cb1060d90db0f9b551a3067479afc150caa75cf3efeaa87180207bcd0141103390e70f442174e101bd42d27fd89772c6284b0aca1994ae32044a5579cdb2d40bc3208535442ffdb1c309c447e4f29a07f6f67c8c397269287a52c63aa686f4f97a3b66bec2fc4aef0c65ce39278317cbc6460f906a8d77c1fa59797f5613e5a917c6abf7bd472efcf2deeb183bd9f36a5e8996ca52a5fa16582516764ba244d460a7d827f6ce52ab9ef15c352e2518c87de5f2c141a227359846bb5cec3a5ae5daa45cf3289775eaba9ee97a44dc68f52f8c0e6773221a87129c0bc3c3cc752aca6166e6aac735d954e3b6475f8f6bf29c73ce4e8f8bd66b83996b300dce698fcbc657f5dab09cfa9194681ca096e1eef15816fc6d76f6b82cfded71591b767e7345d10cbe754df4a18a1ed43858ba688d8e51c2c713079f0ab790e2454d6cb3113da224632eee4d16d9a393f36d6098bb944e69bd36acfb14e22b3de03197766db0f76a8dc66e3c56a377493d81f8f8a144bc4b8c64e6159afec38af1ef452efcf23e3b67717eb26af04b3c7b9c21ac7a999dcea997d969c376e6a7f926f63759fd4c7797efbdf323171623e7eed7869597e8245fe32d9c0ac32ce7ccbd8738ec4974876c12afab5d4058ec91f27897eedc3797bc75d1ebe8e71eac9a75520d7ea13ef1d2232b04f5592f5baa7ee2dde56bf0cbbba371aa312f2f5fe3d11dd2c6d55eadc1349cc5550ba0fcccd8f889b111cab4d4dc162c20c3969a8f914f0cbb015b42d982c86bf3ee969afea7484794559a621f23fcd212e512e51363b9b96b889eba6c081ffcf0c0de27ffc3729b5a6a3e453e50bc9ee0fdc47c8c4cea13d39fde156efa97829d37ec162c241fcaebaad4e6fa51f97bb7d47c8a40c6eeca27c6363bd51cb95225734fd367e66384690461207795cb2de198e07f8cf0a78885920fa7a3c8ce759be55c2d350c848446e3ad78493f461e37a3c252a7933cfc44272a79921f2390b560c148b0d705b1eecc2d61dd3f46f885f560544cecb6606127d8835161e59f7bd582857dff18f91869a9f918f9c438e7dcc70894e970070e4d896dad888d6942a4642e4877b7a4583721cd895684dd68d9074dea2c299d0d7d3373cb3722dca0b0f40a55510bc65a50d6a2e1ab7e41abbf80ad08d70b53310dcb4a4ad5b2fb75834236ec6e42e81b117ee9d3160309bb39c12f14762b629f7c475e763a524a2969b3aca45495d58f8b3f2460fc9eeb2a1c76834853dae4298a56a7e491681c2eecf388e1f85cf51546bd91c190dd957e72692a2b8c1755e5517bba3981848b0b03f0ca004d73a2a9bede5bcc7fae7baa0971a9375a967aa3653d7701b546439b13b4be8a369aaa8628a5cd09ead238ea5dede1f7f5d7c3c3af15e337da1e2a0b798c1a1d0f35ea5a9b13147c9fa890a6776f4e342720753dd7a24d4873a2154102033434cd891817ac051b8aa44845580d8c1d7971a839e78c87612169d239e37cc52225558f1b5663e36118a5a7f5d2a7fc5a4f992a4cb39b1ae26b511425bdbae635d1b326bc2279585a2ed5cbb35eaea810d24a7c8af2982496580dbb31cf45e80ee99a3ea72451674da4cbde505da18d842249f3d559137e915e312cec9d144a7589fa64ae6aeca45252a6f54985d8ccfad93c9e9224e93ccf9accaaa23ea5549f52aecb143f4abf2a4fa12a104f58d255a930cdcec753164ae52b179532290fe9a1592f4b496289eea045208407c262e726763e521ed23faf78948b9e5d2cefabc55d9eeb6829c6f9aa8ad48cf15ce3355d91e57856067ee13866ddb93ef353f54bddfae55b35242f352bcff91582533ff3f1d6b9ea60ac0cfc223d56a77e1989b52a4cb3cd568fa76f61b11eefb9365fb9a296b1a9d7aa30ed32121b1f2bea9095413ac5caf058cd753508a5cacac034628516e390614696fcf4c852fadb445c57b31bc2d9ddf3dd703633cf331a38afa327bb425414fbba78bfad0e36af83f58ca726a362bfce966e46cdd99f6f76eb8bd1ccb38673b2955fea10c676b01d6c07e74a85bf14fde42946082133c74a3d54ed212e2aecfaa1fe0eb9e164ac5dc0f99eb0ff0ea78b0a279c74ced99452d6d4b58006cc49a7b62c2b247d9e5facf33e3a0f4f299c91ce0963fdccbff345e32f6bf2484108e13c4529a5d3945264e19532c856d245e2511e459fd2d5f9e41697979f7a9923257df5c66e48965da49273ca8bca3a01f151f1ab2ab4f90f2b234b4a5e395bd35975b6be4999ea6576f3141d4cf2a594b2569bca85ac6f2eab8a31c64292624caae4552e4d9736af2aab629537555555959caecb5c532e37b587c8ebaed4cda557524a21d374792e55a7743a555d7a7501f184a4aea7ae30c618af54f57260d21d94928b371610963cbd245de5a29e7271de57cbdbd5e2ee62f9bb3ed7d192b2f40ecaf5994e1f59abf3dbb16a684aee5caf74a99a6a685ebe9a2a799e28d5d47955f99ab73af5335d3a5fd6a553d6376f7155ff5cd55b2e96cb6a195595ced948567557558aa2dee80eaa089d6294356e58470c92bb0adbb889d816598c999395a294c7caa93776434656329a28922425c94592a46b439224e9d22459ac5103657a43608e39e69863ef46c816a5344246584429e37b9452d24829e5e3a3ac714af4559846af8881a6178f380265a20c9ba00d196390b1eeee8e24c6de20846f6287bf304ca2d6a4935167b4a2d75425595126e886eed7dd9c3e84902d07368cad01f1c1e1bc8570ba5d21f78853216cf0ad1ba7dd71381c8e2772cee9ce3912a6a034aab2e00a54b1604a05a94aa17438d651ae8d55517492f50291ea8d96a14c1c26a1204618637c6c8af151b37d1147f1374651616077cb7052e6a5202d83fd469577bdd3217b4ca9ec28f546957aad4ebd9e4dbdafea05512fd84aa742a2bba6d41b3d7133b1dea8c6b25fef3c8a70d4a73c8a70f0f09547118e18677914e1e8e19f47110e1fee221ec67db8e27bb8e2635cf13c5cf58abfae5f512423d60bf30ea3bea837878bdaa25e974f6da917c4523df57ea55e8ff5a454947aad4ebdd15ab59b4ace4e09dfeb90744ea69de3917884695eb05983c4c2bf17fbb5185b6a3e453e3132c658b52716be25562663e1a38ac16bf6d48245f73f463e463e463e46a08b436b365e6bed2eedbd67a36dbcc7f95a8d1a356a3807b9c641c82074b0a6b9a69aa873ae8d35bde71ebb4a919d99301366c24c1a48c5dc179c834c888390098602081d6442a46610fe4e8ff0b15e79ea10d6479b77ab9a32e663779417a6b166674db026d8c4dc15efc5dbafc5185b742f3a2231c6afa8a49cbea74b0d6e22199b7aa34abd91f3cd19eb3c05e5624d389b7a3d96b146d5eea986261bd243e073ada10a9c7bccb1293e235e7beebdf7de83147e9ea2ea6d96d2f775a7d3daaf4eb3de2649523ae288238e30f25eecc7a618e3598ad8a23bb78d4f768c31461bebbdf4d2e3a71c9d4f47b9222663aad7c546ebd41431d0a6de66519d2b6237a27401f184456b9342adfef69055fa75eff860eb8ac240d1df124b52e292180593a9624c84a2c812c684302682b40caeec07815c5c72e4f0782c0be6c52394d678c46234261e8951b01b54d2b6dec2b4bb296b1aa5b02c8576288d31900754875e078b473a3485aac094abc4282cebee9af2dbcdbabbbbbb43d453defcd8e963a7518a2bd4945d312642c12ff432f280693cc25c6f8cc2bec8cdb25936cb66d92c9b65b3ec4bea5962cd70584a29a5e486b45da21cb6536a3c02f3505a183b638f31ec863c7d8482b118058b515879cb4a39ad217a9822adcda30807dbe4b82ea07b66fae742bea9694ccc8b5ae95db4a8d7834437bdfcd49ba3a55e17967a419eda2bacc2302fa5725c128f5c2cc3407c948bab60250cc4f1088b50586afdbd777a9e73dd48e646326cc518067a5505a3ac1e9ffe553cc29c12b6c432eaecd989524ca19495524a73d007708d4798467579dd0a32245a867b11c698c891c3c50504fa3d1ecb8a479c7332efb9189d8c7d47221ee1178e4772e47071a1a77f31261e8951c8542123138fc87c38461384a04016d2d9ccee0e41a9bbcfde94e095fee4d96bf5aaca576ffa957de71f83d26cd6086e08eb11121ad1f00824d88d86f09246445858e90a4d1165cc081436d123268b5a2e16a55e94374bf945b9ca2fbb4aa7d4e917456270ea43a9eb67fafb84d0889e94f2409950aecb965360ad2a7d955a53268a94e88e29e488e96a91c5bede39d7bc7551a7f0f11fcfcb976f9a13c6c647f0cb46fae64a952f3de7773ae74eb01ec134369fe2e608fae92d16756b3a825ff8882368a27684c5e18d0ea28b189f44ab17307e59cdebd97356553567c593635535218d481562893c9e599db14c1791e52e28ae582a6abe94ce863acbace69cf0734e22158b87157c55bdea55afa2aa0abeea55afaaae381fcba43e2c87afaea3fa9c31565f5971115d54cc45355f5c305c4c7488f957bdd752190b753580811e9cf0629f17bce7a2efebc5db0503bef7de93a25455b09a73ceeaf1f38754352b58d58f7ce3f0789c885405e3c515b58caafaab5ef5aae6043c7d13d29c58407ccd8988c6c358c83013672e19632dc6df64b3c2f08b05c3b1f10b85e1f7dafb4c6bad0da0c5b08f5fac5f1866e617398dc57879498a133ff6c82cbef7a299c779ed3133c7c72fcccc2f3ee608c3f1bdf7c7349c551d914d2c245276cbee0e12293b8f36acbb19494bfa1fb623aa9ba1a44ebb512836b9a9d3b784407c48c948a494524a5933c9269d5f4ac1ce34498aa56859fbba2b95aa40c9d85de97424934c386726d34a85819225335161248c29253bb2e786b95e9846498a99c05841499d35f9a6a9c99452844c508a40841911d8646516c1348dd2295dbdd96dcee6d6bb32d652042b82dd88b8096cd691e77346203eba089a6627b1a34f3753f8a1223c7fdef1c39eaeb7c7046f3777777777c366c61e73c21a6545b01b3435947113462183104247298410a6e3c57ef8fc42dbe2f7d9fe752f609c453ffc7d8e7eb8de6f66d217d74f0c96f75004c3b7b430c5efe12ced03eca91bfad0431144580cfa5afede79b8a6d78bf3ebdafc97751897ca5f5cd55d5c296f71a1fc73755aae59044b8dd6adf04ae495c85698b018eb9b7e7b4c374e3544993200b09515865c8178c2524941e177f89ccdc52c9ac1afae16cde07a43d141ee3145c84c8a98a626933101c015c570957958b02231192604461999a0d62636697a7b6317b936e90ec784546f73d7b52c101f3fac7426d80d14486fb51d0c0b51ff61a58823494ca4559335c12fed4c49d2e4489724d5b7f62bb124b51aaa4e5dfa6dce4517037be3d955ce31f1c988c45649d2dd5b8b6fedad35d738b1b50bc39254a5ebc2348a09860237e734adcc532b2cb9af34c9b556ad4d5da1ae7daa9703ac09aa822a9ef7c5f294cb3ae7da5c474b5307853baf21793b4df04b735dc9cfcff93a793e6a1c9244614d7438674d702acbc5b9e70aa7cca8f4ac59e88290211a010000000023160000300c0c854363c140523619a80f14001479a0505e549909c42849619842c618630c000020002202323335090218511a9a6190c0d005f976d64fa643f46448c78dc09721cb735bd15d70ba493802e9209f08266185c54fc783ad271caf1decc996d98606681472b73f4268293f5c25f0c944c8676bea75f29e6cc752db63cfad94a423e4b91180c354eb085efe322e0b165b5e55c42782529ad2b9c68146e711d4c046862517bf8f5967c6cf0a2b4c14bc33439079781fc143ab9d730c35bb555b82cd01befa111e7badede713724706f7cc468d2fd548179b3530c2b40ea815ca7b0b40427de0393feab8d5acb434b6062414b58e19fd1ba789f6e8066808649167af774ec710405407790db4fa615ef49a0219e89b9d95be4bf337af03210b1d4f4e65ff57fe748dc27948fc9a715b9860016739570f69f51d270e7c4b68c36ce3fbcfd7598f2a94e2d7c49c91a80ade82cfa5ea3d3ae72b45559dda173fbe9f46c9277bda2cf2ea3d1483118edf35f8ca9fea998f4ef6027e1aa2b01c9f9e31001a0221c99cf683ca8ae5fb7b16208253ae6785be94e092ac7e042e6ad55a0518a06e5b18fc755ccddf058ba393d128418a2ba55019d722e495a09cab94b94982a82334334592d906c0a6889e3d3fd41e79c3acb8594bc4e72ae212277ab28092f18804f8182a1314e70a437a1baa502820b0a4ba53c0a598f3073e6791cd9bdc983d78ec74a18c7bee5ee00ae01bb313ad51524e588e70fbe0c376d95f3dbeeefd979b33a711ac212c8a48793161a85f1667abb2f929594aa01a103260b361bbbcb0ff30e21bf35c018fff11bcda673c26518d2757403a04643c8d94ea83a11f7db1799ec1a2a3c4296c5f908fa83f695eb09083425d5f1c9f2cb92cf394220b914542420a27243d9687ed96a7d8cb131e8f67af26663b05eb890e4c71a2b36ac868bc7a74a9f214e018114455c042d14fe640cd1a48c43382f6d22a47815d83b54718122183ba2f3d11a423811c3abcaef442655117e748cd8f62d3a814dd103bacdf8e7c18f51976ba1fff99c07495a4c7a35fa2e7cc6772b110b7b82d7da964c219d68fa7d0280a72b5f9892602a6f19d1ae35cbc199ec79bc384ba9a1ae5ae48960367b84ddc9ee3fff7d1396d950f89c2855d1220b942f500ad83fdba281e64e2d2410a0f0f93b416a1710b94ac52be64c360f99a050ef1baf31840222853330d88a3ccd7fbc0728bc072375a0061398daf2dbc1704119530cee8eba4340649363ab660ecf279bc9230421f681cd0be7305789de474d40e9ec7585659b51aef947f57b3112297affaf94fd09b426da3827970d55c1e1bae7d32092ad9bd27d9aa7ee154ee4ad2375cf1f9fab378ecf686762b3a6039727a70dd00e1d630e55842b4633d9d3649fba21dfbe2b373786e35e747fe8c2bbb031581adc6a9a5c32dfde58cae84d218b2e5d3d5cc0043c97cf594935af2342f10df3a13f3bb9aef44e4c1373571b82c7ef92d4d9cd9d166793943430f009e918cf9a1e4e99a39f34b714418cb8810b8a26b268728a9622560a13bc4cc3221f4ca875cfbd10f25766cafdcbeb212b883d3433e12ccdab6852756964844caa229af9eb8b5b1d9e2a8001321142a91780372ae3d5802dae14b7573237715190ce3c4be26ec9d1ca25477dee384451d6ec00b56735105690f15f5c014e6378bc5fbcd349e374f403610b95e6689e574e590198ac6060502493421c78655759b9988a92b92ed3364d6db84212b28398ef817615f4d50c4945d3544ca25bdc80c305bde1f0d48451548a56d0a623a8b451e3aaecae5054d7b8c9842b0faee6f094d72d7cb5962f057ea42a451e7c14cf354333ba09262c03b638bcfe6b5b57745d6573e7b3b5315f6923849b4c390778bca71a57ac5a2de07f6866fc80fcee4f77eb8645871ede33087fd635b7eddeba982bcf1cb8f92178ffa894eee88623116cc967661d116a4100b2ebb01c59a21f50f5ed6891636af5fcc228047c01f77e9ac941cbfe8d744dcc74f41cd711440b58f0f97f55ccadee1d87d8472bb7259665873c053421b7203695f469ba9781104de570348bbabddf78667a862afd612731080bccea59807174f940b6d53b55ffdf640603821db1f18e967ab6a1682786201ac3b718513f50aab6bfb75b61d00004fd6c400a5578718e4d954cdb19e173ddb92928fa42c5afba2a1067d382825d0e825683bf4b1405c4ab51071497720f809f9aa2c569fafd61812a57111cf58e4a46e532c7d283be72018c3fbeab9be2f756fb8a138b58152b2403706d16cc99e52ebb2f114b28aabb7602f93dea23a1fc4334b61469cdcd6e8954e309a8fabf4bb3e95cc22439f592af2402f4a5f377666d1b35a6af9785257fc9e6308b6f97b9cd8652cc73d867fc99323ce6c597e7b6bd1178c879c44a5afa1d01fce4eb28ad03c172c0f5b6d309b23fc939bd3e5f628156a1dde8d2ca71e190d26ceef34bbeaae48cc3297cf0636f37839cf3d7b929b5423287c35f9d15e1d22531f154a28b8d4aa12be688e5127b0782cf9774603edb4af575b102a65da07cc26eac3d27cffd4bdb176ca79fc84fc1fc0d4381e1084445e52a4d9a7140dcac7b9a94c6b4db10255ba1226f01cc9a7a7ea15b8674d841e3b4ba242049212635284550356a40cc11302e669709d7a0417ef0e2c499ae4af6b1cbfbbd445b0f8c91668e02ff0f1f89cc0b031321f146ee6f74648b05607802fb83ee10b40c1f2ed997559503821567c7a8e267bba0242e15684281c3d0a37c081fe03c7a99a856de4310d2c62fefdda7c32110ccb30436beabf36f0a0e905115c66da477e3306d94af57a76166c470702958507dbfa971697572c72a3d3dada52c69b1b052a28b1d820a9c0cb9c1eee836d935a672a09cf5b7da367b6a708c5d0c8a38527b573c37940dc6cd05db9548c0b986c5898129e39a64b3e137a66a2b8b0c987145691c4c0e1c0161d3c7cbe290fd1531794fd3fea1c5b8d59db6ae9b4459f4af1ba97cd9c3dbe90d1201b3c0b17a0a778681e208769f3c383a729e280da5ea983e5436d23fba8b54e24bff617dfb93069e8f365a2756e445612a592b3045f6b6e816fc781449a5401440c7ca4ab2d29949952caf684b4c70721de7bd940d68697a79b1da5d7cc2ce8c5fc889ecda4663e17f3d4b62cd73fe39cb14c497a15e6eda303346a18d23f71a46278b843a8b66f842f4b9509c0e311f12553b8400ff26c234a97caacdd2d7781adb7045d98e9bc3616f1cf4545da9ffb8590c2ac9a892004aae73e7906a2fec470be6258c0b8998e37adaeeb4f993902d06f0621f09dcaecd0c4c8a18e4c9a2c701b27426cb779ccaee851547c74bcbf0941c2c0e9b7802771f26905c4073a13857e745e515565958a552ba6f223e062c4d7c21a05abe3394b08cf95e386c535ce86f37de079ee98132976bcddb50364b48618c20c3309f25570372e8f6c57e188af7640ff760c4c614d208ca7506cfcd38290eb9e630da5d5845b2ce471b57be424e42c36e733c56fedb6a2ab4e946bcc1e7dd0e334694f1ce693f8ba353e74bee3396e64ac2cf0f614483c7c5c76b9483e618c0b8a0d6dd8beb55d6e46e68c755ec0a944c8f7d91b4a311c8e0d6a29ec86a5017b887631a9d47bb04c9a3a41fba032782401575355429464affa480c17ca678d596d5666c4f3de1570acbcb7e0e0faa29b4e28ac616c8c564ac0df5e9dd098ae9ad42610980479d6bd82cc81c643020faddddece8a2886893d79997e23f6eac82f6ed8f33b91e5ba6d521ee276eb395da9371b0fe44857ba887103b22dd5a77cc904c2a94b5e0c0b9b7e17ab6a75cb15ae963aa2364d3d158c21f401b3ec4dd30a9dfe80d896590e13462bbee9c8ed947ca215482613129ae3c1749b0858bee3093c81d2172e4b1c2b01e9097a9b51024393a0676e30eed2b0670b735666ae656dfacbfc8b314dd9cf49c232c9b2b69515877ab4a6bf0338143b450d42eea0c3f334d99d72b926517fe45d3b0a9783b6b5187bbe7ef6c08c22cb906ec86acdb69205ff6a61788c0a7bc8cbbdd5f962ff32fa8b2d5dfff85449d4a88c7ddbc901b611127d13092aa0e6e9324bdb5673219883d69d866ec8c3a6f03453559f5e255cfc173a83dfc9296e3c33aa3243592d1ea4940e54237aa30bbc849aacb07d3c09cbc32b84aa8b4155ff4ab5aa96ea2e28a8564c50a2fe842d68306cfd7dfe1b1c64989807ae60377d6301574ab10211e85c8a2854002f27fba9103efe646db73cc8f86ffc6e2b607368c09c0acb2d64e102c0b5b6c3035665447962a4b8503ffa304ac10907e80521604da9611cea38d5b8a779ee0f3a44f42b81ef0fba45fc0d0b012480e95a7d00886dd291d401a80ae7f35b94896d02c7eb9e3d2239fa60a3e1845a518e022a495c0f1b81959b7a10c6360ca47dda742c1931c11dfe1e5d34588b1578a583db506cfdcb4c507a8af40f7ad1259a16a9ce145af9700d536c7cb99ef94507c3010180be540f6ec562b61cb16453fd6a4c6c610e7de3668e448f3839439f48bb565075c428943313de3a08b04797ac6882ceb3b61f72b7882c1f7416a7cd4baa29cfeac2d0382b2cb47c878acb2a374f3e578e7ec67ca828e436d2936a9cb4fdabae07c042c40813bc4ce99d06d3fe496c7f60a24b5281e0c196188a9bfc228e7682661a9239ec5cfccf6366c94c92f994598f49ba9b99ba46da2b8b95f5a1019a385c98e7a89d4957e56ec1e7b6945acab25a33972872409c816dbd7ff1b0764b0ef8f926c07bc03fab1c1756d36e466bba5229c9753b6e90fd8e546895b43dc942200c662788e168114aef7ba8ee9a48426bb82e874c0f862a3496e4040d8079ae7c25e4d24188a921e2d4b447071d8c4a3fd5ace6cde1885b61cc9290d0ab31ab33cb570e858a0e456b8760540692ebd09087af9618e4dbe06c1219418f40dc7727ec602fbae61eca793a119178a8e657ac73b7a4054559683abaf51c2370a3882435557ac54ea5016bf5081b509f9e8d4acd97f08a0cde7d988cfcc13ec4719984a79adbf1ab2c99902439abf5ac601ab85a6506961c2bff0109321da6bb550152614036403f36b4b201ef3a89ccf7e74fb9c8bdb85dc69c718ffd3819f595f47fb8e52a026643be284bec9f7b094f5500bcb44554235ea8a8660f189200bd26aa8dec22f54c140f185daa8bd4fcb746a75485be4538c789a42235472dcb44f27f90a38803b75ea6d2e823c3e1fc6ac12e502c12490d8a3e81178baca1af6a074618f211f9f666e7d3547e4a1f4ef28592f1a3e0438fc8010c0094dc262707702bd32b0850a09ede8c62fb12f3ef3f54a63bdd3ac71032907d8f0bfbac57a671b0ffc1449b28f79adfa2a4ca14f2831c84cb9c431d9e435872b87971300bdbcf3a766225ffc9281e937f236e3740c39ee985a933c572cf9a9db538c0ba187c34cf74b8ee40ad2252cf34f1e0d354254e286606885105d6d06f77ba52a446c5f6cff014cf14b18ab7e1cb2017d52023e87b011149674c51e7802ff2c353d57481ea848d249ee4783bf0bca1dc87442851697a23430e2d31f6c7f60f2dc3f184df8a9e05fedd344a48574cc16630632ec644831e09c03f76a3d2896004877080244dcd3905cf00aede1110f5817bb2879e660b990b4181c561afeb4cc873164300c89c8874967c947b6d4fa3d6c2d13b193704bc0deb52551d0b82f36049419fa2da62aa63cf6fe23fb00fc8324fd83e350aa867110cb6548455fba19a5353a0c7000b0234dd36c1a8682735b33600329e62e121fd8251a139a7ddc2f0a1ab017f5fb57de9155b76cbee405c96b8303672b8e52aeee5590fe6b2aca66b595ff829e7e909cdd19df4b70d47dedfca51519379a317fb5c13c69ee6b3198536d5c1d2b53f50224d38c9c50efef5d9c8c0e85d09e2507caecbd5865fc7bac70d2611a4355586213079cfe38dbcdfe59898db25f5154a4bd5fd42ff2bdaa896c794928e09501636638aadeb46ac7ceae64668e54aa5dc20aa68202e30520a5c58d382ff82aa4b1160370416537c483ae8a30a3c56d03fc34396d25dee22f1c5443515eeaa15f6a55089239d9d68402a0b264852d1525479484a2a2acca4310a83c622f037122b4dc52c10dcc14d5231cdec4905e394c0bee429340f045271938063703c41f70f9333bc920aed919fbfc521283f307f447ba38a6dc5ba32fc29216c48907cfe8fde0ef38b3b99221b2165c0e9b75a204b5d1d52de90db7cfab3ba2deffd10cc35de271aba1fed4b0a153f99bcf132654d5823153c27eb21abe7f52e2f316c4709891bf948a3c5cd29498a4f1d54e458eda18c1e0dc86163c8147628f154b50ed145f4499934d5bac1510f3f09810b8c42f7f433b7a4d453a7988167f554e8180ccaeb94545fa6b4611673d1669e0d7389bfb55436150c3b31d9fa1175aa757038dbf71c100c7ac97239a7fe36ac45e28b23646b16502c15864b56f54f3c8d784a7725058f6795944df2e9d37cb61627aefbe02d81fdd0302a52dc2563595bde74cc123d27483dbdf39920ebd5f6d7c3e7a766e315314071ea9e0d2103882e666feac3a080ba04c603c61d41b0607f539c9042363745bdfbb7b73d894b746a7310ab5446e51a6e4f29e391957f2dcfdea91d83e194fdbbe01293143f04e2a37d98db9adad7a18bb635b9e9897e091075f5740c39a33f74e38ec7f604e4449785a2532754c6419f4cbade9ecbdb29dd514417da6d75aacc8d6df0c9397505bda70f50035a8d9299e9d759efc1c89990b5ae230a7e29aa37111dd39394022fc55e22e4837c953c569902f8861650ae988d4d453ca4088ec35216f72694f2a85706d81e52be692876acdea7ff72966abeb7dcd6a803862cbd3845da2c2957403c1fc6b37ef828619c754ecf14a55477dd534aca89d4940bfca9895abb8870a18a085c34c240226c4891ffc068d6f5f84985a6a87e4c54986952ae5c18b411ada49542285b411c0780e4110b1871262cfe0324647ca51dc52c1c52c7b863aa72df21fab9f42a3089dfa5b0a4a79d35ca689ff93c03607d36226124882a8fd099f691a991fedf4c4dc3c759b7468669f84df734eb1b0384bdb71d485cb0d8928f8e3b96f55c6aca6755e6fd9dd4583f9aee55b8af8c05c6bc5e52686417383fc6cfe1144b08a9dc0a89ac0f9fd9f3e3684200e5328507314df32622568d94abfc3cebde601df82fc1771f802af3a496c9f2a5b7510df4eaf8b41ccb3f7eda2d0871e4f8c80d4dc137f7fc682e7197de0bb77f96a4ff29090bcd2b278b28f4c44a8dbc6adcfe466528a392f3ae6c07e1b51d1f3df6ed10bdb02e29c545def5577ccb221e89610a29d5575e41e53465f1e527b9377a2aeeef4c31a534b31490d347af3ddaf4230599b461d47c7b82959f6a8a6768fb2a76e2ccd75a3ecb4f38d8bfabb4736ffa52824f8bde209abf35a984555982b5ce7ebcc19bc7e34c04388b905a39becd499dc738aff4522b7e075e0e6d531aea9756b4c6d6f2b3d58f332686cdda9da3eea72e1615f35c6757bb7f034146ccdcae644a0937c64ae6bed9eff678cc9de948dfe75b26771cadd9410775ae0168ac7b603c4f5e12c55708e5cb6ceb4e563f1505ca8851007e16332c49a8570715943b4305e7b5b5a895f3983d1de1eaceefdab7facd635544bdac55131f1a62949ece54d59c8b6c4f6d945d80bd3cd9bf9c29c167abbe84754a65030f8b3c02115a70b70ee2c3ae2763c19751eb2b544ec7aa8263e56932c6dd5c502e815c173f26662c5af4a24b36dd04c9b8965dc0581909851e8d3fa0db16f6fc654bc49386565ee7a343af62fbc823b8c73f0ce838fb204928d2267dbe56dc83244385649cc26ffaa84c4d87f19933dfcc23f47f43fdaa7e38d307219c0d59b8daf7c3d79770584a4eb522c483cfd1e11c52e6b5e78dd6fcc3f3af630774083a91cf91ecd3731d696b57042342d759a6890fb95bf91c1c4c6fd7cf770e3a3e60c7596be0fb83199e469ae3f08802c3aa45c360f4bd4cf1ce5cdb28a0161d85b6162f73f669e67db9216f28b112cb2ad2f53584b5a2e0407db92abe8669b736724571a02f3f64e51ed5ba08ec041cf84bb4843cb1e368dfb729ac6fe8275e9b3a304da1444e26845bfa84411584ce2c97ba7328f32da048d451baec0f6066cd022176312751caee95044625c871524027e57c12c226818ad632560f727e93b4119e1c20210f6ac4a7202f477e6f41873e868409f0a70733ac35d92db66226663fb142972d522222b18e21375add85fa044d3f065eb900fa2f86003a8b70a23a2b8f0ebacbc88493351e3a0b755d706982fe98c0fc5b48766f283d44d62990eea8d0528e3d4311e63a2443c6858c079582accdb071269553a8842ef831085fbf3cf5d640e58f4129c335b36ac650e9ae944bf85021af5159d1c921ff184fd7d46547bf66a268e569454f00ce63c6848338d541554e09bf4d068a0f820f09eed024f81533466297a4560b850528b9f59b4ce9e269332011e2567937a500d7246525539819d0177fc1529620549c2f81e0befe095463dc16119be0095f7847f4ab19d199542538e062851054d956e4c938b65f4875cda6fea1bdaa26291912b561aa3f4cdaf21b1693c3bc574c36d3c394c131ce28d75034f69d4d533e5c9d0b2f171bbe94a4857f181f244c2393d8b093a8b4bedfb1f8543e38b370b63f245c354fa55849cc07a83eb406daa00c5b287adeac9feb24f509df5054bb0dec52032ed198a7dfbabe99b3d1dd10c4154aa18a5f89842297162d0b6204bc6ac6312e4083ad65e91d0aa121c64ecc2a84d04e834f7c2a94dd9cf196c9b869bb637bfce0df787f1bf63582002e3be917fc1ba13f4be59d3c0416ce22a553a44b5c89059c2ec272b35245b163278a48dc1fd8d4ac82b1668a449a493a70e85c05041c559295bb3700f4c7a193f4440848a8961c59a924d6d95d3b5825d3b91f98d48d36d712d698543d910008808a4880e5b6ae91622e2b81402759bf4ce28e13ddcdd255b408a9d04c5ec434dba54820d278c21d603d60f7d1f1e252d23346da826dff78ddc528ae093aa8c06b3f741814b1af9cee7ab8d542c4cc5c93cc1902cfe47d3212bfb61ebea639ae5b94496826753c7c63d3feef80bafc1ca606281efaff42f2f9ce2eabff74aba76aa5c82e6e47bf44fce96c564ec2e5b65babd17181aa0421507e01f5828f31e63874c275c5f99ad1dd284c9cff9af27f28289d056e18b7c06f3f5ea6645ebcc0d717d511ddcd23fa842b009082e6a8ee33419e6894717b2ac3e4a5c9b683d603bb02c51fc4029bbff7f37fa3056731466633ffcc5274518f51fda98e0ef72995b1dde6000054e144cdde2a85d4b456bf29e4a25f4e0257e2b084c262cbc45932bf7bd348c98c41fecf05b6bfdda7d8cb66067004a07486c46293a9ffe4d4a0cc4c7e3a7ee164f6892216567f7b10ae89387c8f9f70714394e796d9df7386c6d36cbb4ea7e2cb0cf447f03ad607bab5cb4445e7257f18b3a90c628333abf48dc789960d49f42461765102d9bcf1b90878b60c1bea8a0dd9cee97ebd39d5f53daf3929eb93c90697bdd038f6c2213b09c80f3fe24a911788868301312b928d5324409e8ddce1188893140f9b999ae83a1e78d903976796ff9a43056da72e4824c7bb256ab8e442ad341a89344731e3d6ddd386d55c62462fb439dd78fa27d50f4a25c9ba22fb69114c2dbbd0b24ef5d9108809d2794040a50654ca48123043d3c207dcaa991365ae1f176b3ff8faecd6f8c66be3227f23c7795c5b2faba518b1c915c9b950d7a678f15559c071f09936be71da62c4199677622b45cad87ca2df5a90e2eb4aa7748a15067432ea56182ca5152d6c6c4732c244d1724fdc33610f7e96161e059399a88732dc3f6766a7c3ee4e54abde105d0ce8c489ea31cefcd0e8494321098496a5a6abac5d743b57748fc71d393bc5db063ac79e9b63c5cae3e7059a25ebe487451a463b45c2859686a9857fdc189e224d32460f359e726fd4f178badc45030737632e359a630a61125b4d78b2ee671c78c2c0980e46499b7d6fdd1df2aa8c975f02c91eaf61d582921e441594fcf1a4c4b26f4876a10517202b332d7afcc1305d39af1af50a134e48a8ec05c80a89a19c5c2280c4c6595c088bec0f6b6d8a2b7773ffff5fa140fb12e9b6ba8c2ba3580528d86da0643d43082a58a379cfcae660ad4f485448290d6b2b8437bc51f824fc24ca064ad78db503d467faaf32ea35662f4aec10a0296bc344c9d59236e2e9330e9ee1c0b9d74709cedd0cbf5a743fd1c766fc9acb19cbec868f72aae868752e4b6c1eb0d8fc874b6fffe0e0426ad3c8858ce845395360ea98b4d0b0367afa289d1f55068037520015c59d90eb4763f766eb21fea8f61ffdbd8c31c69ef023c8ed11c33a715682f2edc551645d4ff44299e44cc2c0d90683c945affb330ef0f0e3996f847fe8b76814f41994b010fd028d1bae8175865e595fc9ae5f2654604e8dec12fa0345a566136c1566b331d942ad8ee005efd59317f3429ab5401d7267a042a82cdf6f47b72db8d2e21e0f9a094ab5a187f688771a6193208c0f254996466277669ee917713af2bc17b88475b73c16c5ab12009c2dd45f459436c3d3c924f63d607ec13310c0d02bf5a740ad2107f3c051585ffe231c4c628d79778137a7d43b98e863d9a39583521924f337b1fdad5d15246cfd1f39129070eb5c4a8e8f4be2e133d9d39dce2d898ee05c8a90c0e0cd6829c592dd3c06639e37813f05807bc82e984a8885706e9b2709af0b3512a031bcf1c360c2875f01be69465ad58e1b542aaae848dbf4c115ae910f805405ad90471ba1b1f0d7f51980af7d1a2333ba0b531226a2e746ab1789c7894f7246fcff9bdd10e0e9cc18cf600810cb91e90f7749a8c806d378f619f6419b5e656a51449821262ab75c7118d8a9e819ea9a78e17dd0e8c8a3e4c321da000448155b2637a04d36848af447508404b44af95069c586081d19ee10edd31e0ee4c4e37108e0b75ef34a503061d95f68f569d06d61596c0af833bd130f9c1b9c9640226e17a8c5966645b694b14ba0415c275448bcfd782ea5b6655ce2402564c6a760226e73e358a5d708c5a209dfa13015662890d3f8d4d0e238c395c97881ab813973f711883fa5ac33b81f968c855e90ba56dbb24655f86f0d6ca644d39e58f1897f7b0a7c529dccc056ec3f7a0e34a9d4b329eed791de6772506b77aeb8f86b16a15d6b60512bb285cf9c2d458e407b7251498b2ab89bc2125002c4a293ce810e6553ac697aae362a25a468d18d43338b63f070b174612c7a59c8876aa9f5f70a4cbb1235abdb33591a120aa31559fe83bdb9cebb2106a135c4d097aa4e356ea82d8ed56977c3c770b7388dd192d3f34fdcda673e710affb86309e29f1e47ced76c0dff9cef1d7af47ed07d6d25e81da59d64fc746e40a3930ed5486af262590e895a353f0c667be24d7b6c58832c7cde9aaf2fac2c90cf4b52841dc240873cd523dff8fa7c627c24bbdcaa8440c3235ccf52acca8966e383e2e12e372f740abc53dddd473fe4962e3169517090ca64fc65723f267cee197cfea497793990bf3d6dcf62a778b3e3b40164a7de8c8e8dabae04e2ea55c36f9e5d77da708d0e69ee7c4c64efd47c1a170e341a574d39211d9bd21188dae24b7d2524aac6df7932a0852766e187ca4c0401281316acedb230c87393b1d62d151783f0107af211f1713aeb145f7243b92d55849ea6a0c2d43c4d6b33ab68a8078a38c3d713b949116fe536c89a384095a5c717ed065265b1854aa17f8fa1b49db1e94f3c65b99be4329df60f783e5c9677bd64a2e1c2ef5600900b2b7faa02fac8cb3ee509c56c8f1d2d1d0ca6f6141781690ec1dcc63cf4a8de6655091d142a9b71aeb18426048535f68a1dfc1b4aadcfb19b5a069fabdded364f387eabad2eff0490e582c0e786496fbdd3ae9820eea2f799b05071d9f8b9dfd817d86e905bd6ca9187e081618c07dabe3122f73bdeebad1983c78aee5010bf4dd2214ea86c59e592874bac96c73a1ad3f61b2ff61be0df5b08c3021a1d46e729106cb6f3be8dac5a828a9c9bd20c597dbd6a5e376de33fa8c6aaff8250a297d91f9355519acdb024f0c06130449c8f3de38aaa0e235995968c282c588dce337c064e1cd0bd10747fb28270d2f736c899393962882e2f0e4709a9cca2d007039537f19c3c7518e6f65f798055821269d8fcb385b93f406dcd91a539c05e43514efb80d3bbbb4e3bb23370ea0ddb746c616f209b4f64e5d13dfa1d519806418bb13d39ade152900d0f9e724cac8253797dbb56655a69ce167239904fde385dcf12b9ddc20da969aef627a6299175a29223de4f8f63ab94eb209c3b1a87d0650a87a838c849a40ce71c904577a5ed97535cce8670cc28149656e7fe0d7ada0ae70d2994685d739747c9ae252eca9ca3747e6623317514f0c766176b15142d32689e18534719ea1550ac0315e9d12db3e2f2e1107224b86b83a5c1c66b9fbcb112ea7e21dbee403a421b0ff5020e25989dff4f89036d85568691ce16ccf7d81fea35e62419929427e01e61704dc2567198151aaceba97ffe523d5953322a281c16c422a6a8ae4aa113b19e0640919a4e5c8f840d750be11abcc73b75cde857e9d7b6429f49f06b11ec94152648ed7cba66dc91326bff046b3f717ac2cb3236d293b21d91e95537f3324f6c22c7c41fe19ae0b90c97aee4a224101a3b8fa85b5c4a2f4f857227fcd59484629625d3246bdbaa1724f6844c9d5f8b61d44a7cf7e9b826931938242b5c42669315512c3252459ba0d6331488fa9debe44c413fe6dc339db773e43002dc066c0281d321e16321fc903955b6857c8b2880deb7b008c8825905a28422f8c8a6f1dc8e848351a2dbf53c38e8fdee63f40ef1d6ffcc2c2956c56624c5e681f955fbc7f95b25da9b3e9e87517c274b88114f96a5f43661f598d62c732e8a8a8515912e45b9e672268ce4c84668ba6fea7a2c91a1639bc6a8dc32fa09daabde84671b2e62cd995022e4c9360194fc027f539b1f4f43133d90d95b343937bca095d0a1d8425c73f187575892bfbdaff52f4818ba6c55c875a758fa987fb2e0e2fce594e4311a02334f6ee3f132dff4d4878a8fcf3a153c71736db6e471a2d7b3eb8a8da0c26495ec92c12e3b031a7ff899d67cddd01856d4d294b0831a2155d4f894086b2c2452ce0172b56272b2be74ae8768448c36b75bea90f76388bc88bd8c7b49c889eba3b3647f53f8a7cdb285ae23a7c62c9b521b0a993a23aac58dd512dc8763805ae49af4b788a62ed96fdb7bc3245ef94f05b66afc5220d5007325f26f31bcd954b9d5727fc4b31d0326af32575e904d715843a550c8928af9b5d3fab8167191150abbc1b9946d46e149f76d5ce5504671601012769f851dd2cd0595d2a420bd3561a3234205bbbb29486f6330de5e1a3db3adfdc4e468d3da807e37de912cb4f22f02eb66fed67e4ab38a14318273539c4e2d0aa6915cdd6e0f4e3b04c760b817bd398ca66bb910f2624ad244fe0bf10dd368708f9f4e4ee4a08fcae279c93ba944b4910002ca472c2c13dda77ed6dbe3583c348f4dc192f919e7ff864e3087b0ac1bae6fe603247f1944153bccfb1fe052250017566a732bd5213531182f94b855e5cfd229480de1ee7373ac14fa6869589e604fd66b0895549fdc508a20c5906b188f6661605f205503e2fea483838b28844f778b226ab931f78c03c4e25e41bc06ba582c535fdf01fd6ef65dfc3064281b90706652000311994fe879d8ed6987e80504f2ff3f557ad521ba37f984b7ddcbeeabbcb12bb6cd80fc4df200aac85c41af952e5690af10b55f64befb343e9509e64410e5a6295d23c4cd4472a407381d1e96bbb9cee5d2cdf391003ab1c3ab7f959b2e13041dab2a1c846d7ebe405fafad0394bc459410379ee2076625bcb68b7c4a32facee6ebb5919820b41365944c6eea9be55c71db9ed74fe988141cfedb96095e7e94943e232d72c76f835cd0ad21a03862f9f8a5d5b9d467f5bcd93d39ebc3528a6272aee74d247acbedc52ed72aff1d4ef7ae4042a7753673d5d609f75c24cbf20e6b2a0a4edad9fa7658cc428705066b02c19a119dfce4e226595df7c546edac7161956110d515e157a556bb4d704709be5d0f20ab00409d57f2497fd6e85b5e12f90e0e287b54d78a5ce68af2ab4bcf23bfa1226856bc66755a38f1ef8bbf9e5c39afa9b26535de6ab412d6a4d88f7586931715d7201e8599afe49203d1aa9904926f1ddf068493b00748db58f19f5aa462b3f0da6534bfc06410e2bc45c9d5fcc2ddc018f1b08290ddc035eefc429f8517a8c2985fc2aa32e49d4bbad6b0d9addbd8aafb1241a6200ef21819896d7f3cb99cb247008ff37db1e353cca148f44cd524f700a3cff3be3820345631c6197808522a91455cc0297d092292fc7d510d16d10f9022ade1d77f29f6941a1e0e49275121212da94731cbfaa6ca657c53cb355ae3440cce71d7cade0e1e8e8e06313fe8afc567660ddc19a94b605bc969d2c1204222cceeb333a7b51eb32d49c05a501a81e7ec5ff684ad5fc6deed8c9ac5e0e5d42890653313eae6a16a6fcf635c318194e7cbdcd00fc657cd938f960735feb27994e7adc419103536d39f53734cc0d4b36c075712cd6e8dc52a52c3c66a1e10807da89e343865c115f313347b6497d17fcba2970b292e6375ee7951ea4a073aaf4fd76a7d1911e6b9cc2f09dbb49950229daa30e082c71e31d152fe953ecf03902f502f7fdec6e4e53f3e212ee31a5e2f5678541eefa7087b22086eb11fad9d805f178b2b98f828ca3856741c03289a01212ba0c741df7c8efa9d57bee12a738eff85f148bbebf47acc32a1f8e7dc9e841b4690b213c438374e76047776b1e03c977a711e98ce737f1913757bc3280798bc4d9f9fe7fc2357f3e44d5822fdac3069d04462f7d0b9aed0a19890e6653e44a4f2b8f999d36ed334a7c44621b04389a0fd82df9b51f140c3fb058f8cce355da744a38c4c04226eb02cb228d41e22f403afec12e958596134c35f231aded03b847a30fba96b310d59dc1adceab44256c8852c5a5533d3b9aa18b774443a9345df18352accdd27808fbcfb1ad1fd8c790a17c9bc32e9b59a59ce541916ceb0973e928da2526524bd7d027a8d1ad4cc643664f27927886a99f62126589219d88227b3573eaad358cc1ab1a622aaa528889b7eac81c1cce785a687a7fbecf163c23103ae76489fb4d75b25c9779b6d2e335243b102b6477494e95bcd87d460bb4db967a66b1697e15b379a694a1c1ae28f88af645a3bb53298ee3d39ebf303cd1a769e1088a33871a6d457e0e55c1b327844ac292240d5e4523db147775c665fb1ccaae3e42caceb9db3394740249787efa7ea80e08a47e9aacaf3e1a663f9f6313a8f398a9cb0d198f37c53b90a13d8dafeb0ccd6c414f4c0c6de4c90f2d1a3bcd9ea45a056d268f1807d97a8ec5313269c03284cda28475838e1cac2e44c0744ee46b7f6ce8b4649f53a27d9c32671393a4d695e438608622b3fecea6e31c22d578e4739e564f2755e8a988ad37412a9897cf14fc00c70b960847b4df075706b82f4f8d01f0ea8969a1948cfd8afd3b74318206de42ca1b228642558fbec668d22b68861dc48938efc39a954741feb783fefb48e0e2d14a66fbb2e6d9253179235da1c17b30f7c09bc6d8a4367c84b7d2c15e2daa9bd18151edd9db4b0fcdbb168cd9b1d7a7625bb20cc550bf2dd65b6800670705846d2f470d1b528371ca8a253d7486538277035b7b1668b9e93244e42316e1e4a35c0ed1c6e918a4c4d9236c95e5c647e46b9db47de5f6775d08c79b5f509e093fad57208ab71a18f5176ebf1d06aedd7acc0b746920adc04c9aaf7e8399e03ebe018c65a2a3ce6815986fc764169139e1944ea727bd000be3b5409ef9155aa6ca95878b4a505013192326367a81560d253f1b1847e5d96ff24eb5690ed9459d23b54e9a0f71e454f270b3a3fc7f82b8a4d1632260b85e2dc388d094606a80864df2dc5e331c0161f7c2a2da08765118c8a94562d6b3e519182cfaa5d04277185794059c3efe6eded4f03995c62b416832dbd3a547fd5eca31aa07e7b2e61630bcbd727393efa90d1bab6cbbbdc475fadfe9a44e7d7425ac1bf5893e781b2966b91fa6b5fda224cdc2020eac23455c354613dea54ea9317d7d129fbf3c9ea28cc93a01fdccd500e0363c3d7be7e1de7188e5ce1fe947e369ae05c9af08dce9e897c38412b306db97f9e9a4d06eddaa0eab77d743fed64f7ae8512489f3886d5470ce8ebd12577b1cb08be33740e599f765d3ab0a0dfa2f05a3f7ca69db9f5af3fc6aa288591d5554d5243b377c2277283d349820eb2bea7a5635e03b2ab86a1f0eb7eadc23b0276369eb092409eb9f63609e66992fc462d2e8d2e4e5e969260e672b70d10dcc83e864f666e6b7932ad3b1856b1f71f475a16b5cede7a24740d1fff06552bdc6518a6b44f1c9a0fea4865913250041e0a598b2ef80f39477fc27ec7494305ce0d0ee8cfcbd86b76ddee0cb76fb6cd40b3204e0fa7ae8222aa102448ce00153da7b2a60566dfb1ea930b661e89c0b0ba22de33e7ff30312960ede84fb1157afd55c87a43026b126f73f94ce991678c2f5c2d0e54e810c01ca41c2a72991003fe76b909a7bb85914d83ed7e07ca1f27bc9b5bf38570e02224fdb93f1efb1c11dda1195e0a18badffd7a40abf83ecd573067e4caa492c5afd0d932d1790d91cc70aa52b221726433c386ab67affb2bce09082e825643c1f9e904228318c1d9330bff6420b86630ed85d695758f9c5d4f1c08be8ee362fcd263e80341e67eb297c1496e34a0f4c2106adf0dcdacbd409032dedc0bbdec19fcc86f9f9b04270a280253a288be38382803ca21bcccc6b99bd0577045e2d4939ba9c9ab7c4f2b9c37d1195a2c82224e13f23f8e020951c80d5a751964b12ee4d66d0017b4700176f03f5e31f3857064f4ac51877354efd462d0eb0350615f9d326337a4f0a00f993e0392426a23f92f466d4f07b30531bfe883d5cc8f91fbc16a74427a22270b24ff8830698a02f22c6a2fc825ed720869bec328599f33f93eeee70c00be57e605d6eff003a31e0d6135b58d61c40534c4587e3b9165262e9fb706e196683b2c3cd1f00e53ce7f880eaa8255798e3ba719ca490bf124a009bf3134aedf6c383cc0cda3edc50eba91d649c603b729a9398fcb12fae2f9c938f9961714dd9943df520caaaf8c28b922075467dcdce22804fd587c1197232299b56be175ad9931297e8007a0c458aff5a7ceaa310b0f87ffa12e487985951c4870504464b3c4c1c2c4027de126d7e48d6d3720d46adc03b4aa40e7f8c6a0930b8b0855e70cd96ea56c115290f68bcd62d86f639aed98f0316426d47aee53710a050aa5ed04d2de5e52c7842013d321282e15cf421d4897bccc64283f7d008bf1a8e6365b039ccf301950d812b618eccf145649a68968e842005b5497efb504a969ebfcc428e6a534bc4aea38d9dc0e0917398384b55f31ec255f8b1a720f8728136d20a213c1bac934fd2c7e1f55456005ca370066aaa62aecde72fa843810f64b475b3d8d4340d7d00cec1cb211c53a82e5194ffe7c9884d09e79bcaf2406e821443d24eb3ed2c4994537e5c0ebf00fdd1dd582d00b1d6089995ddfcfc39bed692b21421a2d4398d6fede843baf9541f0e3e9a0cda0f7af98efd4c85b17b72ab56b5700b3694798c2317c30f38bcb63d52b8d9b41a4108290e52580524a242dd6a9588294ca0f02e1e952b3f3b6da8a136182ea294780a52c288ca74da9c7ea6b6bc19906b0efa74ce8be12d1b45dfec5900e4da2f07392ec26177c22ce4a14f17bb66757dc2130b052c03e9c86e442cb1fd8100fcafb1b09c177cae896b5981a552186c508345200cecb0b4a763a1eccfb6b47aaf3663cbff1fbdf7f396f2e9e962789ed2a516701b516c6c782b97ab42611b035dfd56fa664a5c654739e80351714e766296c51ebbb7ac4a3b449c6f51a5c0a559de16f16885ee81bde4f26f43b89841b2b72ae3b30ecc028395c7f3a9d893303ef854a4782241a55b8d16bce5d85cddd002a0485c85eaacb9c728100d001e30f7fac23757420ccac4e4969650ec32780f9bdbf5be43dec11399cbec60895889b2dd68df548dbdc30fa36f14a2d9c04ea1a3680235a3ed1cf0e9437acf941530b59d5e8483b0192876d598cec48e197e86048f23a3be2a36c08ce90aa30d7abeafb9347a49ee26261372457f4ebb999851a00e2e9ee833fdf97b0b52759a8f7b904c5f0f67ac54a85534f03741c8bceb272c941d39af1d95dab0406a406b771f0346fa7ab9dcc4b99262dea5a9981b669915aa140393da78c4b6cc20ca6f7e8f0b1acb55c3a8b3f814ee7853321a2fc2e112d2b30a878e16d741eda66c7344339ff9e04c6e5c023a0a1350f4a538fc27b3fe2308d72dacf59cdeeadb7fab63d38c12fdd11c6e8f0743759d5cc1452ca3f87f69bcf057f10a3cbdc5cdbf924446bdbd4cb7cbf548e4f52ba40bbb515333d74b8dd649a80209d4f618c03eb615101a5e48be1b40941859646dbc984b720026fa6e5b94303a0052f9c25c9c97ae9fae7f1f631b14710a73013bb73541675921b2875b002e5d498d08163e82a8978f6421f2193042179059a6f8cf9f3dc0a6c94ae674950b75fd733b8de98defb32dd307f9dd595bbe53f347c3df9ee350eb49f5676d1ee28fa17bcd73a543729331d6b9b2ca1128a324d2b257b33a0c44d183e00bd4df37883658395c1907a5a80542fdad5ea9f93bd1dc00ad0f38415054a18ee88a98f2b5256103d8af4c899cbc115289fe9bb4f05868896de610ad166828714ed12f0edce8ae84bb71b32df732b2917137936e6eb5c7b40ed0bbdb68400a4193bb5e0741661b429e63a228366070787fbd0c33cdac548a9c335e31c47cadc5a83c867d25ace75ffce4975ff9fa324bd9c7908405a0067eacd11a4e5acb9ecca70fbb544a24a9f368075e984737f6bb896c1c8969751815b254e23897536a8af2f890e156e914ea713a0b42b0f6ca40b82f2dc83461242700a44f6aac6a8701aa1ec783325d728cc062ab5f9d9e2394adae03759cfb6df5e01fdf32ad9ba3c1ba29694d6b1d8cc5f715922a0f4c5eb9b003f40b419293450b7e92eb064f51ee529f88f0ef22166d8afc76c62285d316979048cbdf68a881e8e57e87c7832ea0b0004ff8f1e1f9a568facc64af15ec08084a425695936d98b6edc8fd7f4c99cce997bc46ca5f59aa1f4a1162fee93838010bdd0bfdcc6ab2e25708cea2f0bc2e6d051f12063cd59474ace729c649112c8e626828185e2d8ae43b824935bc04d74f6f629b25ec667392bec135199e21a2183160ccef3d5545ca90fcc417442f8861d73e5cd2ec51ea8bd42b4addf8db68ea433385ee9f7a8255d2436f38501c30b838e42baa5c4bd892ead63ea69f453e17f1da5e3fdb1b4bc9779558773f6e9cc94caa8acc9ebef189227d48df91b2db96ef07b4070ebdfe76bdde7cbfbd8a2ae2808654c0c2f2c160ba6974f0693f6e7e4daf17cfabd8f2640740a4ca760064f28906b67074e7b07bfec361e3af49bb53ed9d15f40f27704bc5fc3805351e848522ec2fa942507ff92261f751318dc481c86b3db2b562b036ac8030990a9ebab2c7990a36ce435afc5d2c3b40506acac62647726d5293a07dbf4eb1d44dfdc6c8286cd333427806095398c1687d416eccfa2c7f9b04c33ff738205e55dd2aa5363e357075851bc158cda6f1cf2ccccea458e021b70c2ae5c2e0a04815e2130f32fb99e05b666e7ab53d2e6dfaaf36c3771ef4cacf3a135347f99fdfdbd555ab509e8f1b53c6cc24e19f00fdbdd8fcf8bdf537b3a940972a00db31ededad5f8082462ebeb02089c79e5b8dfdaa1b50c3f673fbb44ec12033357e6643fb24506402d29005057f3747fd64dee16c9c8dde565a5ccd310ad9a7efadbf86a8e4accd2139d94af6edb03c1081ee43d47a12ca3d3681e3b447c0ad3391ae947232e8c03ba2e4673c5a4b775adf247f03dbd7471d9041ef25dba3fb5987a29aa1dc75af71f4240a4590c69047d9fcbe7652754b92807f2f9f11dd6920170294d5d972aa31fa57659c621e4d1cde53bd8843e1b8d7b2217807c2eadcad9c504cf785247ede4df6720c487d6cb05599499fd154de7da7ef7cea4a956004f021d22723fa638cca959ca8b21ae02912982c8e8563112c8ab0ce25dccb95b4273568389373be0362ae1e00fd307dc3badfde659af377c5a70ae1f4837798ab369aad198af7a1e6a2312e8328ee21e61b8e2c2ecdfc47839fd91e6b71c9a9dd53de3e3d622a9748fc06cfc26ff671600ebbc79e81ffdb244a2e139d8dc26cbb1d59ca0bfcdb73c48717bb43386c3462dadce036646a870bd95a7e3a2759e6e2dbd0ddfa592f01d1fd5284495f3d4bb1c7c646e6ee590a22c5ad62212e9cba0311fd1fb150d4b13ed272fbd6e21e33165604c5a35c781609342fd2f336941e121dde1c0b925e3934de9d1601b8c23a2c728c557c47171e682f9794aa5d399d38ab5ad73b6ce8b5513658ff126040aa68ae8a37e2740a08fbedffc262378b63d246ef5d904c6d6c784c556cb80f6982001550fc7001cfb2d8991b49003ad37384bde7ba1aa1c281fdc5ab99b9c3a7f9649c3354df226f3e65f6c0c8fa167378b45489865fd703f8768ceee20930f98c4fe5a04c0de50c7fa5492d5545e913a5128c35ddaf037406f7888da3c41a4e2790f6704f3d6fb2392365e5936f98294e134fd434074d925c7a39837df65804c11ba437aeade4c9af1c3a24bc1e59f393d50e5f8481da17c38b5c033f63ac2128640323a3b68c49b1ed2b25a14cbf786e85317d4d7368693ce11361e0b69a150d500d125398b7a623c88fa946422cf092c887c9be2a65b398dd1b400672439d600e9b56d10e32073270257b8f63da75e34d9122902819a3d58bba886529acc8825b166158043b69be595baf2afa33adb4c555e5b06a544a9ba8e06e57d5283fb9ad84534374907cbaa8bfba8daa34f1046a7708cbeb120a132dd12a0670e915fcd4f7e8c71573b27f1d0bd8e4b164ceaeb5c0b1654e909d584e30f6112e8b391f710a0e9fc0da4231096d633338f2885312915db0a3bc47898e950431e921b4d248738dbca4291a3c0225155d84c45103d2c45a1435afae134808799c73354eca043524c77120898b73ec8cd9384f2ea61835c716e63e6d7d885062671fa4d28cd405105797a28c2706c09510122ccc149a6fdcf54aa54de92e509c834fd01a7f82f96eb251f3d3b30d6f6c337f7aef2b012471a0a1d38aee51fd9a96ce8bdf389053d1b8d1e929fd9529983c2d7ac86cbef6d2a832eaeb12a0541bb4c12a547657030b51f0ce4b56da06ebf0fe08d96d27fefe85a4a432d242d71c6779ed54fd3cf31047de2c66088820bd66549c634c7236f908f245dc42a066e360cff7b29af45eab3a947093c839c7ca3b64656beb10f879283de0924e826ded008a914e9422a087ef6216a22313d31c7d2d9f0e48d02e64127adafa843f01c07259541d31b755399eacff38912cf71de7c1e9582f0ca8d9cab14ff09b68fe00c970077dbcc318c87ebb2b1563607d19568eb7e2018cdb233f49c776d3da04d92324811818c933444a8195dfeadd75ac2c2026b9cb561527006cc959f8ac83b114fd52db2b8d34400b4b10bb67b7c13fde387aee6e2f90f0ccca20d9504535b0213daa49b4e11403b168c087ec4597ba8a491183b883f153a4d6de9714cdcb85e8c1dcc13c98362ec8d0549ca2efb4c94224163ce0405195db450c5ee088ca08ddde616fc9c0f63ad7742bc7afe9158be135e043b0217199ed72c913cb823884af44de3208377a8713ba1d9f6f44e93d30930d4c84eb4b45405693268cfd35539a3d541a7ac0adf72037c1a8b450e6bcd1ee330f082c8f9770e392ead87d31e493c53c6e4a1bd51a40e7b0b67ed7b045ba63eb2123c976d9c9386a8ef4872d8c84dec18abab58ca5c565d8ffd3f05f5a1efdc6d5c3491b1e72df4d4eca0482f42c331923371f95d7952e34ec13b99b0aef2340f6e0068e56362700a55469bf293678f16d39dda65c2ba8ab1becbeeb362a5fd222d5aa227f964901d17ac68683d295dc86ab2d0298d71529e6b241916d880567491d82467a7e5e9c0b2856b2b3a4468ddb9bff3f2bb69c614cc6cf3936f9c332c63b63222a70cc90c2babb45d4ace8c661fe1d4e6e4bf6d68d45998ef42cd0abeb4bcc02e8e86319008c7df10df8447a319467c6023e17ecedaff420523089b7d8d1553ba2731cc3a51a2391482f4bc3f85873279fe215c58ead04fbc9c35aa44810c9ef7ee246f70c68d4147a171be602a7365f3920215a3a09048c7fb8d5ee98ac635ddc3bc7847ea776df0353dc50d87fc6912b671ca5aae28a58bd719baf636bbe046f2f188387605556aaeae323ce17f300e56682e1941f3e305d7fb4aa670ef7c33d08cadb5c93d1164801f09d468e44ea6eb41883374c7d18bc6854ab9aee8b9df4836c6c42adb7587b0303e72587691eb9ef0f1a52c002845291070a21cbbdcfa1aeb32376c740efca2614ac9ce8c6220551a66b93932f0f50fa420320846338f479575244a0b07cd1e4ecdc3c0a379aa2db5741a9fb97edc6495e9f2fafa492c94d7af00188c8a09015d8912226b9517870892eb7d29c2495724b28991192b8d507222ada5547dbf42527fd5ae7428311f8124b66b04cfc5bae06707b48a560a9ccb7ea257a2d967d791f35f1a4a2057a35084e5eeb47ea4b77542884fd9e1661da11df80e1fac2aa9c833b1f280add502c2413bdf0289c4dd4fe7784286467aa49e8346f98b4bbff63b5793422eb538cf915dc40e04825c773fff66f296d6a31106076099a414f20e7afd81b14ba6eaf20cc6e4ad4dfbff048cffc219200da9873fa24830934ba8bbfbf83b7186fb13808d800089262457055c6985c7ba9fbde55fd3884453995fe0b2e6b82d9bd60a79d0cd64edebdcdbc6fd929921643cee2664e292cae1211124bc4a84a5e0f64a310f26566a2f52043225bd982562d3911fdda5d6d611032667ba02c5634d206a775a255a53a430a67b792ebf45d26703221275f9123af20abd4fc336996ed3480faf880245f8af185da71550249388d25567ca26bcd50137db2d4b30af51a06dd5c660d4e9fb6d9b492825b1460ddef4e8cf9bc017bb697c8cca2a2460ced55192a78e4bc1dc5da88633ba7f27d6d7b05e4f4aca0880ab27c19d6aee5870eaa5199d31254207892f1e7539b2c519f29503f50c4ece48a2b3a52852686dba76d4f7f1f02a3eeaffae62252aa584284efd5852698f195d5716fbcd20b19584ddfbdf6b61493602c36fa9a87c4ba388b692dea00a06279e01c5865d5075e0cbceffdccd0b1d62fc9c5602b32fec1205c27761c89453ad36321413d523c1ed9fd4425b5103e9c99613148b24643b33ca61cc830c454b874e8905c39c0b46edfbe47c62dd53452f36f0f565a8b66ddb5415d0ea06b37f8483c3a0ffcc1a70966a15f3c34bd5ddaf57242ec70b2c768987449c991f063f1292a16043f63e3dbcc4aa01e4bf4b857f9160243327e10ffc44e10e33ed3123a8cc1c334f409c8acbcda8490d83711619164a4d7219aad006aad41351642c325b0e52d5ef9bf9d202b1e4565ac33cf8a1bb351d7c486af8894b2c849f713d4581db3fd5c1186ffdbfc4532e168ae9cd4854fb53803a278e2efae9e2a4e5dbcafc8b73bb52e515bbf0aef4ae7de8365543137ed9cd508722f8834e59cd8ab5acf40dc7a83fad6b115997bfbbd152c64a7601db022eb3ccf179e7c88ae8033ce53ea12856ce255b871a836c73cd7d288e5d5c39108081aebb0092639c8557ae36845d53c2e96cc792b4c0106b94b9f8f8cbd915eb6fb3dbf82228f4a69d0b0880a5df4248077f0b425afe2de40ee8e997306e0a7041d659ad3b814b2fc3635230c93e221215cd711791e9241b5b544ed9fdd9f9f0fa28b61d84829662c310c1256c150ed652166416f12354652128bafbc8d8ff0c64d5d84154ce31f8d11c5ae5ae921ad6e57441a2e44b595c8d4162a6ffd515e2a7961258537a1be938287e5e6a1169d8e042b8ac02921953d1bc4c7368aef1293699295fddbcfb6771b568e574074ccdf5c6096e758d98e4eee175e1e15dd9135e5452e62e648090fdd498cdf342525719b07ee8d6dc6888cdd7c5053c05c79e9d40456b0bd1b7e9d45d8263d2e73cb11c79356522adfb62ce1d3d10aedcdf36cb17316bcf6ee29d4209b7a24ebb534b775edfc4585d37150cf03606b4211334e74413d01028ac5a214d5aa10b5a0ed7f5baa6bc270402848f930d10d004449f0242fae9734054ca14c22d40d57ed7e92a126050bac5f6bbc4a6d776e662af1e8856ce2760ae9e359da6534fd5132dd45d16c133a746d7569269db3162d37511e889d1497f1af7fca4218ebd173f1aea4ba3b7342ae3c95774c8154c03222250c5838c7651c18e27eb4520bad88139bc4051b0056f388ee66d5c206ffb53f86851eeae417754ce9b57b3cc74c18076632b1985b1b11368e0549dfb221a4ebd131d60bc82245cbcb75731b1c1592a46bce7f328d4de71abed21bd9115c11d633e3080296ad1c5a67308b173d89f9505511202eaa8a2147b742cadf1982824f98823800004aa36b6988b4628c667390f07b8fae18608d8c7dbda44b6969bf0a9aee90e809f1dd4c784ac16d5fd29ddb675bd850a82b6ae96933faaa3a8803bdac984bba1c1d140501757b8c12606b809b0ad45e0bfb6838954486a29e553b816a2c6a9136b7a7578dda8b6172961d348791eac5f8f376c63c155cb13e309f35cf281431507239f30d3cde665938ac48f5a1b4e18a35c88874a823a13c61d1c44024a4b60c04c3ff408d67a602f08df38412d7df634334c3fb42104be47e75a1bb941400c72bc2d8ac728c583c4915235a6d9fc9c574d91ac22585b942cf69bd83fee6c5476bac187b94fdf3fad472f23066356190c7b2e68143c6dcd2132c0851d9752efab0828b9081c8e2e173adf66ee96445b068a30e211e9cd01f104471b6a1751125ac4178cb614487d7a43604e2cd5ceb29fc824cec5251f568de8d17302cabc5cf47a5d0d0cab6bacdcf9d14887a9c9f922c3de357373e83c2178fe976a1a365b594c20ab1ab18f5f0c13b07d2c8be69945291f9220bef3754b55d340649959c3d911d4ef49f5c6f2f85a406aebe6565886c0b6b25e51b4745638553dc00d56b37803ee4113da61857ebd1ad1fc012f3f9fb0e1c676c49f7096dfabb8b1f53d03eae90a0592d881c8d3f8f058661fbb80d10d8669e9a10b30e86c9ae9949bb6aaad71e1fec235b810fe469b65a024e0d6b0b1ac84d11301d3026973e1c7bd173a9cbe7a73d8d669434df02d97894415b4302a323bbe7367d4825c1af1028487004de45b5b75ec77af3f5cfd5fb702a547d53814fabb8e57039c807f2d484bd2022edcb85e374d9eef2736f97e0343960b9f7da0a8de9f6db65d646f2143ec71f042905c702591899c8023c0543dc74164732c549ebab0d25b8da99d26a7bbebe146f2b7ec58df5ab0b4230b5383e4c412d802f37b0fd221dcd491ab8e9ff4a5a153be8c7b3d871c9585b6bb44ab2c3768d207b8886f36636afac441ea46a85715cb573f874e5d4fb47a90ec551323ae07b5265c9748d4e4e95f62bdfe34939d71f8d7597cca5752c7ebbca0ca85e5ff8caf0957db188795e56acb23b566c8fe8aad0bc60250cff5d675b1adb4d6b44332755d010a0bb21594b2fc51d497bd33e4539f859b8b60d1782e457b41270b31e78ce6f13e67e559a3952c93566a90493aacec5b12487c7586cdd0d159ee0ea3776ad50fa5f8cd27e59808872c5161b60487b32cf8bd251c0a0364db211b8ca76b8d8558e102eaaacb8a98d6bb4885f34636ef29ea2416920d969699a02aaed2699e3c3465f18ac2cc151d0ecf405189b6d781e87451bb76aff4102a9332ec022314968b0163e2b08a39a967743dd34579de202cc8b4ddeb442a73afa3935a2d20984c8077f3184f27768cd8cf6da2d67ce1ba91b10a6f14c4acfd8a0183e1ab06175bba9dc70a5d5b4942e6b4510f5107a4d7414e1a4a3b4214c2ba888615b30616d0310c10762b08690d588f3ad0ae92d9ba3a67464a6d8711c4698118514e9f41bb01b4cf94d430d3ff66e6956745e40aca11b4fdfb5474bb4ad2b087549b0ae54ae7f426b5a331f18ef00af89dcd6f5b0cc952c0c8d4b3afb60e2422a1593769220eb36549cec7e43367b51d3f7b36f8af1143253dee3695c69a52402df8bfa2e7f062a3c3871c15ae34edc92ca6cd4996e0a93533730232a953cbbf76a49a111af463425b87d9a057c89042d2a12e796e861bce2a3a64d44f8051484b17a1f84773b72124e368b3442f4bf3f83a5d5bc02ed574e3701a2673cb77d0d96c3de9b6c0106c5e26d7bfaa87c3761f59d7f318ab69110fc933b6104fa2cf127b22f0455622b30462a1a4d6137c347efde6e2a90f94481c275d23544745a75dd46ea84ac8a1460f055ab6dae395379ceb97791cdd09057ed676a07e11bb43f14748669c9a9e9df10b299a68c11286c552f9a9e230a19b5a82b4bf577e6df054f42ad9b52e34e64961efc94491e823bc35ab7c5011a3daa8daa9105554d11b396cffabdb0a64fb6c512481c51a03112dc5c262bb64dfdb9fab188ccfffebf7632f07dec99a15e69a679bc34f8b0ad4b43e46007854c9841df5c3777416583f002fd2138aede64484195195f2d87565ce933483df3ad94f5bfa8033da4f3041a591a09cd91bca84623f38a95b47e3d469bfd98ec70bd72169e65c9621c8db39db3e0c80af6a1de7b45c4c7e5f9501873f831998e2c89264b379ba9e1ae045a6282909d2757e5b75c7f087740db7188dce1ed8f6035372ff8067300692a3febcf001b24be9cf09ca4e70b3df0890deed4ee3ee34e9aa495d7f17812d5d12850764a2df6421f9ce741e968c909b417e05f074b07c16ff86a1a8e7993fecbeba9acf5b506e08089a14fa81144d0c8707a97fa310eeef266d112818820c011b9ea5367fb81bfe4acc5f028e211005895e44773399e0318116a66c2bfba9cdc341ac1b44cba5a44c315bf78b34bec292004e375bccbafa1b4d7d80ef78a3f0041070a6a2c5d7d3f9c3240ca3cd22d6a0f6216309f9a5e580d7333f83f3f3a37c825d6fb977b369e712c3e5a0e3986a92cf0f4f0aba7ce9043f641b042c828446c213e7574b618e3a24c0ad73cc751017264c036c1f2de71e8405bfe50283e9821476457584ef78028106f6cc953598f87090f19411f1bda68f373e373878f4edd96b6987101cc815fdfac6c1a5efce6c398fd853fb25d8ca1f8ac20367ab0c77b2452be15621ffe4ecf74e7953f4fc54ff2e6ef2b866a6d1fecfaf2ed69b6811addb8d5dfda298d64dd005da3bbab197951903c8b5500edea2110aa29b2bc5723f9259a9a98a6ba90f469769e4b8318bf50fa22a59ee107c0052115042cd68761336a96ae7202c046f99cb00f0c4ed8eb686a3a3672bc8deedae60d17462b69a4df2a14be64bc5824c2eaba082eff20eaacb818d826189a5f34b306be4cdb6fa6bd5866b846d6fa326a095e2d9b046f139168a5dab6ee017b8da051f8cc6883f79a1a1472eac7d778f8defaf5f53ca29b17275d7a45b7161f58a3542c96b75707333bac8f50699cc390f56d50dfa9d257e44037d3af148606ee20918860d485eb4220d7d331e235080a646e502f1115c99157c47cdd12ebf1403af57c3c27d4f29d2aac533f291cd1b9738de47005b132fbc343c053d1697ff5b225a4cfed01167e863ca834b1ddc9c8a168b089083edcf66c3ee3545fe0ed23f09f035fc8ac6240ac8eaa3c0465259f8ee8dbcadacfecd009af24f60234492615940e5ff5b2cdb576dc34f5c939e76e373977145bccc809a157eb7ff4f5a9cba62499111b8a759a91b86990a480476f6984ee95db5b0d429c1714f5d9111d1dc8bb54524e3c4f8e60be623065867b9b2d929504b91f8dbc889a887dd3d2bf3789ba524612f7ccbb33036cd5e01defd0a802d82d8348319db2292979ae30043f5e8463303a7566f425b120b160a78fe5e77c19aeeebaf2ad1bf1a7e19a9be460fbeaa2f6525a85e3f75e30eaf62c4dea73e96f268e9770c64db7fd17208c9d83e870f6285969a47e14fc5b53a30570793bb964cf10f3d3275a1206018f266e48e6567ec8d2862e538af0d7587b20c78075d78a17027fb6c12d3b4578ebc63f2bce684c5d88d024d6b4bf44841f4cf41fe0d90638139c3a8c61ffc9bd31cb891a37462381692ad665c012d93b733d6ca3544aa2cfd17d6f74a3679f5fb598732904f7657c313ace9f23dba5cfc0c06112b6b1c9d720c81403fccbb16902e8952c3fda93230428cb8cd4b5a60def33d02c34fa1f01420b58a12a58ed0f2c9f32fd719eaee7df5b395182c558c5dac152a9ed0d0554f19d88b2dbb5e2eea8d9e8af2d221305f105442e1ee4b34152d2345734ee8e700fb30f3cdbab2ada6400f907b428a09b7b86418d2d3cb31171fb395f5b848130cca798e8c46acab4848595ddbff2a0c54667be6c49cd8634b7881034d26e3e7995575033f6785dfb44c9f4e9c2898939800b716001a964aa956560a955148e4173def113bb25c04e4009c1f07b49c9b434b10a7b242c6a52245cc2033226bd911da2ae440236ddeed77cb188657e8cb464f66b5fe76eafe37ae894da4498f034d6dbd749056453a450faaccb80064704d7b1c389320a5ba584a6f24ab3e86716969dfd6707b67b6eabfe2a2ca77616e11e2fe05e35ef925c71f3d349d5dcd7aeee5cac0f1d45b77f72a4246c32884f6432d010d9b3d52333629ff49bc8e55f96a631e10e84815ea31a77b0442c5dcdd4ca47844099631735e757f5fb4991e680239217d732886357bc63d0480816d26989d6eb264c3d016c0714abf5160517448795fd56c1a4789f5e699360abe27d37174882faab4a62cce4fca2872d7fdbbcd7a7a29bf4c9ed5532f024bf1c0636e37517b715bc631f784d283c650c778801ba8b9d34a1b3ed89aed517a56b45166926e1412ea2b38d7861465f28a6e4fc32c402069dfc750c30de29b392c2fdf043e649c2b428bc162537d58aec9a4c0f3921890c01edeacf4f1e65b9f54d148bafc7cdd8338042daed3acd387701a9c824fb04a6ac587e30f846f0de1ba1c59aa2233acc7164536ee79913c2925af85b3a730d407214d81581545aba1025f73418b6492a5c2aea0c07755695e776efe1f46f6c6d0dcdbd0e4fc6cecade3395917b5b280551e39e62f6a8058f39aca927e347d7f9af7dd344d0f77e01118e43583dda1efc749c405ac0b4e4016df13f62883f8387962fd720f3afad76e6d43593d2c2bb1f547d2c98d4eba9e76933c9b90de774c5508de9389940a5dcb79a1b3890b39214087ee77ca3a9af2762c2f1de440ae057b4c036f967e6ea562c8cb9294f8dc9c8b1fa549c8fcca5714d2f46cbce23d012a235597d1055befb6528d8d096369611c11c39a16e698ad5c81b0264698bd838ddd299402370c9954b90a2c15c9cb4076a9a1715bbb9078199b9d2272398c8090cbcd9c6f0d574185670ed99402bcba31574b646bdb495c60a071f3497e5e7c98c013a6e371c5697663852c5caad40638679cb24dcd54e923169b5cb4e2b13b66b72b2baf0991b0fda97bacbce3010ca68e27b169933d46f8d9e10201d4b500c93e946c5671efe9699b2af929f85acde488f1e49dc575f4487d45cc2581c1b4a683b8f9d3a544eaefb73318175db0828ae8bdbcfc78fb8defa48533ecab681ad8a768769f43045ff83b942be006d8bbe95199d1d1b9d7ee14e2623a1e50b60960e94712c164e0a9bc85a3af430633a6722819720913ba6627f5a3fa3dbcea2f2bc0e7ffc9ac44561cf5a24acd7673295ee542ecd9f4c7b9fe5c65ae0d6bb5a8dff9f85d73c197597d7a80c94606c2af76ac991ae9d7c7ed363b80569d378d30b082ba881f18ac081a13743a02283b86eea640e3a03a14bc066dfccbf681d1f3b9cbeb16556c6717a08f892fbf1034db622ae1908cf4dec8574f88135fd9db6c971a6da8d256dacea7b0b6bb4568dbad839ac20ef3f7c463ed786eb54efcbde2fabaf8ca33b1594e474efaf0ffc55d3a79132430aa0edfdf8115a321389ee95a042a1d7c6ae9fe1cb36dd6e8a91ee243c0eedb3d03ea9af87164d77ecd450ef15a92478f76dae2bcb63f98848cd6c2c396df273b0c4431c3fc2d18cd3ce1a420d7417ea3ec13a7a017b87b89c8d310cde0682146271e55b402d7a31832d0cc22fb0f4c9fb0ddd7875734a40769d7ac128f1524021249040f890e35b11e71c0701f88cd818717482c1f4817f32169723376b17977f3be58188121939150421e06ec9a7062e0866be5a5d43e1e2c29bcf585941b5c729ab935a11f4d15e645aee9e13857e2241c9f11c067e7cf7cd2703a9de6ca884d3993bd4886249fcf94a1b03abc9a0113053bcc50e91d290dbf67b9462299f190bdf1c6ccf17b4ca0f7e9afbba143ac3e18ef44d5f1df68d9e2828c73b45554b837ff563364f8ac072e603a19f261fc8e6c08e40800d0a01b6ffa947673f7cf0936cb48692f429c9a241d322bcf94a1cd58af54d607a18e9a3547ac0d451d09d1415bdd5e92d63765a0196e7f2beec9883e9e2603cb83edad73a43cc283742e66bc0798d47c04fe9412d25c2a8c5b9b26f19e08a76bd3edf1264966b74dad7010818eec13919406c76d4ec09a625f516c8882cc81b63128b97e772e343d0b247a041aac8bb85c40981935076dfd727ab99e48c6251f1b4d46f92208e168c94019ca28ab84102988d4f1434ab3151c313221bb1c21055492e60a5adb3b0f28a3c7a7c580551202bfb2596bacb8c4302895d58ec5b6df5a6b2db4b4b449681329539201080b070c460b3b4332a53aa71fa9ce09b01554277b17bedb40d10b3f413c5a18b0d3ff953375c22376fa7b6b3e818fafcd27f061b852fee931f817fc07c1ef7fe14a993ab9705ddc13f8a4ef4e4f6fbdb4a7a7157faa2b40ac63c5d3ef5e81755c57a53a4464b3bb290ea57f5dd7a5514a5da8a30bd115e0498612715d1487e6501c4a04a5171e6b28114d290e2582ead02794886e8ae3c27571e54c6d2e1e5c71f2a9e1ab51356d16d6c8c015a7beae5e51776cdd476a310cff512896be764c97de210d613984556c67eecc9db94343c6c5f7ceacd55a14ea3fdbc92e709da91a0af0b0ea50227c5838cb0fb6896118864d6c661ff7181eadf7acef5efe1d6f77c7ef251e559ef5610d7b96eca6f5de8f19f6e6ac5847f7dff7d7de611d9695618d6157be2c1f64d0c4e6e7fd8d292f0cbb1ec3acebba2886cd39b19f13bbe89cd8959db735f7ddebbddbbf75f6d773b7ffcaee85bf8ff1c6baf72ef7dfedbeebcdee86fa598c639746cd2a9e7876dd45c9366cc5f46df494a66283dab0714bef7957e6cd5eec8e256a5da156e69057b181d5a652b7ef473f4a4dfbd2c546587bebc312a8621e337ff4c33a1c67de67f52fdc536a747457dc53b6ae72f537ac560c6b91b75ab2be6b84442ae5faa24b7aec7a8f893ec6ad8e87d1977e76343c5c3dacb57eccdc2ece5eecb7abfd8671dec57e87d1974497b46db77e7718feae91ad6a2f3b1cb418dfb63b96f2f625ef560fb7275dce535b090892ef33f02292ec651744e7fadf3522fa7a6948ef72a816ff4284ada7fa454d78ac2394c8dcf12199523c733aeefbb1b19b39a7d7ea87614f573cf6a251f4980b17c0cfbe4596b578d3bb30652edef42e9832d367efc2f42d4c0f5e29f1c8c362b162c56ba7d38a9187459fe613d691e193d38e4e21a6d3094f21a29a0ceba0d434854c0cebee98fe9ae434c162b09c2639394dda66c57727d3f652575c99473f56fb232a679fb31b4fcdcf7e7bc94ea6ab2fd3a97ef631b33e8eff5e4ed17bf62b6ee752f638441885da5e5e64ec1f857aad6f66add67adf643ffecbb5c1c35aabb528d4ff587372727272784e6ea282dd6a5141af4c2b3ed419bb9087d485b01609919ea5cb9e85e5b33f5d29531966b9a58bb1a7da578a756c9695ab0e91773a508dd35ea46118765117a25cddeacb4c4dcba5702a2e850be9b8144ec5a570211d97c2a578992e3d666afee4f05837d18642d9d93a33355ffbcd4a989e376caf89aeefbc6f51adaf75376c5fbfc773dfbd9cefbdd4be6217fe34ed658f2bdd44e56da250d662376c8f7dcdb2ac62ee5651045cc8a5b03e1af23ccff35efa10446f481f6ee0f06e78f2060eef7a466e5eb4bf5d377beecadc5d23f6ab65850ecd2a2ed4d9078cc343cf0bbaa1735bdc6f2f5f1b69ae78945fadcfae8547955f0debb0f07f864755d3bf71a36be0b08eecbbefaeb126bbd132e88d96512ba67d8cdf50ffbadccc62a611fbd873b773955cbd01bbd757eeaf3c25674ba81b9d77377472d6156a650e79952c947153f1f0fbbef0d857f6d848fee57d7dd215d5bfaa763faf71d0813dcd1bd6918d54870de3b126c31d03accb92a816b496422076a04a7904a2d434c69be79b0118dd7a4af3f494a692d35368f3f494a692d3539a27c6e987d96bf87a22d8f9b57e53e929419f7f425b73d26933a0d6afd8e94f21b5562153a7a351311ad8d4916f55aae3abfad49a42ae2be8aa780ad97eeb526075606fe5f9500cc92bb27402858d95395f552c6dfdca91ae14b59b65d53b1cbaeaa9faa28bf254fdeadb6bfad12dfd774f2fba2ca1e9256bcfd2239f64edab76577ce9827fbaa867b92c368bd36e7d7b57fce882ff5dd48b2e8b97dd0d1de9b608b25881aaae5def966ef594f62c17e529ed4da7bf217b6d7a46481fe3dd9feef72c5754b2e122e9495756d58c171b3cb4fe9eea0d5f42eabd5e76d785bb1ace3b12588ee3aa58cad1e76edcb01ce70367378eb3df5b6edb3c0f8f32e847d593464f227d292ce1917b121e559d8747f8c68d1b374a7d3931e79ca3e95d231642a0cea8b3a08e86fd65fdf439fa917b165cf55854e15a7bb008b008b008b008ac033fe79c9d3549575743a3759eb5df755e37124d37953ecebee6262ff9e724ecb23e3ae2bafecfd37e0a9942a69029040a77f78d5e94d2efb8cbeaeee6baf720dcfbd5a1711ec771bf65ed35f7319e8ddb97f230cfb6d92f7b1fc287f0217c88ce3121e69cb3b330cb12592f2a6157e98e36d7d0884a2251e9636649fbeeead84a2391e84ba5d2775a271a75255357d2b0cb7ace492311374da5e79c34d25e84fd65fdec91e8f338131e4b4ef2918bdcfae71dae1ee2f1a516a9456a11d993d180ce39a78e117bfa3f2cccdab72cce7aced6d235b3d7ec1d6daea1e12cc7d98f99561b5d1d9bed38eead1dd91f69cd8d18b03da7711f3747ff7dd8393c02c182e68b9ba4d1f6a3c8729df6f3b7db4a5a8696553c5a43dcdd45de595554431324e2beec45df8bb8d12644bdf43ce846ba3c926c9d64947d4fe9293d450beeeed4fb5a43b34995ecf9542dea9a2dede826eaeebd6e6969c9b62f20b2743a5522a79aa4d4273cd6e4d3772552aa479cea915ae4542a9dbe964aa78f99a7d3c9542afde9747a5b427d8c9b4e2816564ffe2e0b75faef2b5d174b372a145d6fa14e2ca6121e512429c2a3cae2fa1fae456a915aa416a945648f06e46b68268e3575587e9c3933c7d6d0605644b2a4b756644da21f4bdfd8ea478d488fc9a6d853162daba3cbf4d991c8623f854c2153c814328598ba1ed1416d857695166aa19e52ba4adfe8fad2e84bdef7fd4812623995462716db493c534fe9293da5a7f4949163506c0d4d27e926562b71fab197f492565243233b09a993643f7e23d18f358faeff4c3fda90bf1f8df0a8cadfd34f74c2168ba9446a25ada495b4128b39c1e27edb30ac86067b12e9411dec4947e8f891b12691bedbc2ea63d645d2eaf55b66758f996a6848df833aa48f99248cf4a6abc3235912097b13e94d98e963dc923ec6dffe58fa0f7b169975bd65fafefb302db3584c2512b6bdf51ed6711b1eb5ac5e1858c485c1225a86e53d9811be1a9a91f4a14a5fa3d175f9f0492aa3b63fcaa0a01b3726e94777b435349ff4e1935548a31169f4a4cbf33e19f4a3ea59de449ff42c7f7ad4951e9ef0f83d8b156193e595da7e1ebe71a3655c376edc3035a633bfaeebba2feb3a55d7d05de38c5c7f9cd1d2d232baefa6fdbeafb3578727fa3e6bbbafe4fdf58944789c913beaf42361d18f44dfb778b7a565742d2d2d35a2be80e08d4674348978a311f5e69091a88e467f8d3e668eee38bfaba31bd9d1e81bfd777df61b95ec8f246a62f97e8b3ab1f4554b789440cd48c2de1432854c2153c814426b448e3d21496e1064e9e4083b793ee82f33f6f27fdbfabaaeef3dc847ffea18b3b764f67df4c2fea2d8cb6e01d66b347363cdf59767ad7d0f627d888e4e64edf5def7ded5d67e8c8bfabbcec3238f697fe4314522ec51cfc3230fdb9146dad3efae0fc1591db6ace111977c889651f2b02d0ef3a159ed24590d4dd649ba0986619da4f6926adf62ef89de72189e5d873117618b8d80e05a4989c3b2506f3c48adf5c683d4ac560f526badb53e8f4ef4e366472edf5fa96bbb5ee451cb4d1fc287a0f4473b3fabf3b3ffec2b910c9359764939e59435c926e56759dd21528f90f231ec7fac47268661daff7569ffc25fd9cf6ceb798c914c231bca0e8fa2d247328d6c97b18c300c63c1be34c25e6431d1db7f6c546241b168f1efc207e01f00ff31fcbfb8ff30dc7f7cffef9532f5787cc105ec028f2f2d308847150bbc028f210a9ff0f82cd8844ba41126b29fd7593db9c4bde01d15dad50eeababb83bab6b275dd5f5750d7752dd4759d8575d68fee7d57b78ab579bdcd26a7dd4e522fdc534a5ce7d80fcd542a952aa333cbb22c0b3858cf9096613dd9bf621850fdb8772a0e0c2f4eb906e58c2728fb63dd0da69cf14061fbc78c87c542616330511e09e342cbbc39b169032f3295410816acd7882ae5133718419676d63a5a1845b0a3f49199991788fdceb27c22073ac8b3b12c9990024fbbbbbf9043d40877e13c2561789022100c60c0ce94fccb27d3dfa89458b0ee65f9ddede0e2cf14456dced103a0668a7e8c8f94602f1c5fd197b1315da06065244c67fa3bf0d02e15e8147d4d6b17d9a9713492725c71292063f490315a78489fa22e9f09834dbed8e4e4662a0456ce54aa7a20b0de3e01ecadeb670264aeabbaee2eb060bdb1023cf48721841dbb0132e59f841b26648f828e0d19c3815c0fd8bec16de3a10c55b48d6be1099b7c214a2412684b69f0811de5131759c35a14eac3f089dcc1c3b1eef8aaee9e3936144437d5344cf343d46cc878488390751e3051c335666a3e0d50b0955a52d8e48be5726fd657d7a9fc6abd4e978e45549fcbfdd8f3204414e9e25cee16509ff3d043fad708fb32459f4a2a25fd279428226754f6d8d05eb210f12004151eca5438d2e45a31ca432ac4ccc29271881f2764200a233af0849a22aeaca0063727200213461045d684ec5cf61953c39dc80f56e0408a2a8d7a77ccdd9faba18938d95470a54e9bccdd515f03298664428022fb4bff4a85d64025022a3b70420a2e20048abff4af338630b142124d90b284921a1c0635647fe95ffd5153babe63ee4db23bf516c47858c31f0797010544488282215670425480952024410a251cc10344447f1925bb37398194ec2cd9a992ecee58f5c73d3109a8a1005f8d9ffdbd053a88a20877275480f2294b1d4471816cb3d4411425c8232a8f28ccaf2768b063258a0e3690852a60c0bf5a2218c10a38508acc400949a612ae60831a08e1491349f84bff4a831386f4c002527652d033033984234ea640450533c0c204a87cc008422cb0842d7cc0ab0c3204aa00240a124c5901139888e3fb57d761842296a0b3030b746003a27b538941cd06a01841859f2638018324a2a4c1146ca258810a186840c8cde6990725650648b84205144b041125144f34d0840c8e38e1048b2532909a029020d1af9438b0b24448f40b2a11e709a2b0c00e15a0b0a181018a2b5441fcb17ac1169e80018af8e3540471628b4a01c5055c20fe08a30871c245040543b002c6cd7b3911a209544420fa955718810ac4ad727ee90a6a20ce4b7319e181186441fc31a3080115a274028c765e1936441c11ddfa40901516883f4225a0608214e7a54909c9a006e28fbe4142e49a9872a5e3e615c0d0077c3c6ede141443e011fd4a71fbb8797b054010018fe8170148889be5e685e161018e28094d8e0c897e5542e2369a4264904304f1c796030b20f183f4e35132c0c447f40b0314712b71f3d2502be0407102f14748832b3e2a2131032e4e4c9d003dda4116763084e8575a4104e2c6c2cd3b8b48c1c647f42b738044dc6ac52eebc4a29fb0840d8444bf310c117f7853276c72ba4c67a20851e088349a124cb0117fc020710429349814f147c905e2c4d209c0440c7e8e88dbe4e6955150208423ca1e08d98290d81dd8800a7a44bfaa24e27659179dded2bab27e230cc1e446f4db6203e20fef6ec17c7007d461210416d1236588af4620ac972d7377ad41c743ea3f68645a838e653d66fdc4b2655c4f6f0d39997e0d381e52ab06225a06c513d760ed4adcabc8bf550cd690b93e0e32575c93b15993698dd50d85943f2dfcc5ccc9c971e99edb327f0e8fa81e2dfba14a8c50023c351f0815e59f207b7272727272729e60fd25add712361cd429ae47ea114a2f6a51eb713ca93ed6932718fd51fa5c14ff703f5f2e1516d6cf365aeb86476b51ff148f2fb5482592e97707cd26b44d2d528b34c1fa4b5abb460982a1b261fdac56948b9d296feb9aa1eac6cccfead090452714a4e3608e83391edb7fab8e471e37e14ccda7d7093e84132c3a31b77cb0c917dbf4562de92599f519a697124a954cd50c4fcd2e0ad64b4a5a4914acbfa4d5e7084116b7753675a60e67c96d7bcba94c211e3485c8fc70db8da1af5df9a4ca145225c852b9bc333cc466507a518b5a957acbf0b7ee85c75665da564bcba0f8059ea516dbd2d232030ad65fd2aa6ac2265f58d943e8f2cbddb25eb3f07861cde31ee3217dfada55cd147dbf33d509d94a694fe90174b73be6986342567ae8d86551cb2d1a6cd9330c078eacde50cdf0c2b8dd14e18d296415cb7b7cd5dec302773c6c4b761e5f8c12b35808616bc7c3f916913c2340b3a3b20b4d065e048be801f44f1a2cc1225a46c7cc20e68f7c8d351613ac7fae168f5503ae6435aa83ceb0205895ca47e5a3f259a2ca2a5f6136c424a086d74afe45b8ea42a9031f6a2951e2097bfdf8e112db3233367ce868d74a7ac9a892990152924476ad6402c1f99086826dd32c0d7034fb010eac8eac25c37eb084d591b5645ccfe832dca2699a1318d5341c50ebc5894dd396d834aab5ccd85a549ad6d22233261b17bad86a61497bac4dc7ea21d85106e1080ab29058659afdaddb66566659aaefce7851f98a7e2fb2c7ca4c2dcb07f6fa51f58284c5f1430e2076f8eafbf93c7af8ead3722ccdca81e3872adaf74d2196e5759a14b28e5a3f4d3467fdd880fb8a6dda73cd5dae6f781e0f3caf096a492a3ca89ed7d9c056cffb1b2da3caa01b5502498fc3be8e16beb1550cd30c0aaa0fbba9d3edd8de2a6725c55640b11da06c0a6cafb4c7368b6edbfb6a7c41cdd62c0fc22ccbba964359995f5436f9a2c5f859725bbd84e0a435ec1242125647c5324fbb2e0f7bec6a1fe2eaacce92f472725d9e733db156bdb2aec392a857d6753e44cbc81ce7ca2e09c587a89ddf7417d6b096e1b14aa086fe6771ae99603e0eecc2f0ac340732639a85e1da978b6283b02b737703aba3be1544c557425da587acf8ea8a76ad979d86555c8bccdeeace8eedf2e8b183470f1f8ae58062583d02f3699b1ebeba81ed7eebb120becab69cad166919593d82655807e5d9403a482dd232dadaea0e8bb6d97afa499eeda3d10bfbb699b55a8b42fd872136315c8bb40cb7e17436f962c482f27cef6922bfc5a655ae32d119664b44b0a30b4d17ebc84ccdafd5855c8889d5a1bd9547eb48b68ef80a3bc2571b8fafba24beca96584af8aabb975db6b110d2305804f61f07f230c87f1cc8575c90a4161332530b2402c5857e3c88896d21afe2436ec5af3815d3062c02dcf1d5681dc9425ec5ad8045b010722117da7eb42cb0081655b0a0ed023cf966e984084182f0c3c9efe5b790878d1b53acbbc6927a93deb2df26e75de3da01c1d68a9a5da7c4e7ed5f84beea7e3e0cd58c173cd6642fbbad352bebc770ccb430774b1785f2954b6a754da865f5773db0d7771e650fca57d9cfef94b0edab7e4b7bcafdc4161e6b6ac4cc4f80af3acfee5e761aa60cb07210b38a2ab2b55d3953978f3cdcc29b6b50f8beeffb3ceffbbeeff36c70e3924118a5524a2a3fcb42f308d14ed242ae536bf59aa7795aa856a1eb9a3f956821da533e4f443b1cbaf7be5e0d97aee9b6e8966226aa74555923bde88ede74495fbaa316ebad73d6176b45d5ef9b1f33bf7f11c26821212121ceb72b6c488c6dd341b0bee91c5f555c7fd69f3a351948a5ddc7784e4e4ece2867341a8d724c2552d7c77a241a8d46a3910b0b0bc631c2230a8ff631131eeb09b360d2736fafcc9e9a3f33edb71719c3e17bd1dbdb5966da276d1a07d5f145bebe6dae9fd7f59788fbcbbee87e72acf34318d755cdf0b07aaadf92fcebc12567b49c41a545d532e352b5cca0542a66c9255a66b478dfcfaf7e9deb36392f7bed3fcdfb6e46cb6731e0b1a2509f09c59fcc1ed38fb3a74da7d9235d33610f7e8e35ec82c964d23493f62e5c543699344d33cd2cfad14da22ac2a6d39baa757afad764729a4c66cf6492d16ed2ef8ec9ec99432b98cc254acc254c7a86e612263d7389127309939e212673099315b76d662a14ab45fdcd0b8eadfb444197e984639b530ce7cdcdcdcd8aef9b1558ce94ec39e79cb387490f931e263d4c7a98b0b8a9083d2d3d2d3d2d3d2d5f774fcbf7bdecfafb66f7d6f774b2470ec1783fce68b12ea6b25a627c65759ff52d96d5f359aa9618abe5fb64c682e8dcdd50bfdfaa1f6645a13e138a3fe92c5538c6bef573fee83fb1aeff75af8d33f2e8fd7cfadbb8fdfcefcec896c558ab5887c48f692ddf626d2db3add73c778743f6d877f7fa0d933130e9ee8d3faeb7707dec6a35ab32464f4b4f4b4f4b4f4b4f8bd71c934dbea8d611f4c824e2012577b1039f3c493776b48e583abeca466b489ed6916c84843922613697ef5fe4fa998647ef31000162c4905f842a1765cfb27b77b99bfdd6d55fbb41746e1c0402be5f76de2d964401fb8ae17ab5a05dbc53f3356d34addb32c63cc2c36935b68ec85085d57c5eb039143b86399430ed2243a7fc3dcc3ecafe52862919c369f5d0c7b087073d02999a2f1b0579764aa6e68beacfd946f2943012e8dc3e2f110f1788e2a1cc3300b1f3df4553d84bc757a3fcb1217f6c4898cef31b206b36ca9f2c444b97b640a7e64f1407f2942d24cf1f4579ce4085bd76b2d0351460c3431f5764e92da5bb548bea3cd33fc8c0e69801886d39e424102dc18ed582e2ab118674e9b2e64afdc543577938ef106bf10c1bb07cc0e63c942f654abe548dd666d9c494dc58aaa44f0c28bd3488b5e88b0aca08d0dc4432c53c684dae992d059f2ce3a5fccd5fa66b50dd1f34ec80093ea20c969d2122322e126f9f7f613e3a432c5f62611d430a6c120be365474386971d8d193ec6fd67dbdee774ff20be2aaad842a6ef39f44f1e7f1cc90fd0617d8c18bf5977ec9fbcc3579e9b65080a421bed82e82a4477d5cd9490b344c421c64f6cad5faf925d5ac92ea9001f48f60e72cac95e7222eedfa49f6428d9a3646fa01f8bcb5ecadea24545c38faa2ce3c7972948dcbd5a1758ef96992e319e9a4f5f0057a6c21b800b801bc36d71150003bed7c6f5c1c3f92f5c1c2e5a5c203c9c0fde1d978787f3575cd46d9bdb40e69b6ee37838bf349f347f747b8887f345b7ed6d227d44f374925ee2a1121ece6ed24f1acafc96fb9e9a4fc355796abe4ac60d9e613cbd303ec6c5e00c303038c3d73cc30c327c15a897e5134a64215b3a1e0ef18ad8cfa3f338cf74a93cb9fa95b23efcfe1f7c15806f007c7f0f7197017cc7f0fd7dc4572fbebf8ff0150cdff8fb3b89afeefff7b712be7ae1fbbbc7572e7c7f37f1958befef27be6af1fd0dc557e0378befef28be5af1fdfd03e4abd3f7b37c9bbe4b557c459a2ede148ef0b1b1b1e9018ed8d26708231ab011fbddc657a3ef7720be127ddbeff720befadefb7ecff155f7fdaee32beefb7d88eff88a88af8e60dff2fd3da60b0ddf740a121bb15fc6cffe2dbc3207e0caacba526cb9529471a548c395620c57e617576618aeccf8ca7cafcc7f657ee1caecc295b9c595199479c595192573eacaece27a1e75b69e4f57e64ce64d66128be7ee76f6aea492bf7b6500dc9a07900a2f0e0ffb03707ff0b01f00b707703b88a7fa63b89dd330dc1ee261ef7848c4c3231e1ee161bf0bd7c56d711fbcad048bdb3d2b50b7a178d87fbaede3613fcbed1f0f8148b7a9886e57b96de5bb7d850ab77120d76f3c0ce2389e735de7e698a97e1a6e8f99ea575d1957ce94cff8e0e1e7b6c9b4b6080a9de04093dc6ec35c523b1e279e04218faf052b798491dbf3f8e2e2287b7c89af787c259de80094dcef497c4591c836c823975b763878762ca3832538c9dd7f7926124bb2636f04fb5d4d59f2c00643d9414a4d41367ed8e1ab99fb697e7263385354218f12c80ed9d8d8d88848a49327c0208f52688aa06e275480ba7198d963dc47007ea39006b87971da70a312e78fa46bb40e307fecb29430467cd370f072105d45b7838bfe21a59a91fad7cd716f2e900cbc8813cb99baa962c5363a1e6e762ce94129f52cc3b586d56c142c0f5f8d75071d424d17f0022f0cee30a7bbbf8fece74b118cc015011ad6cb540cc68e9e4c1f83e121787d0f715ef0c2323b664aac57150e553b5da6c5a18ef16cf2c5fc0b9c18dc617bec7bd0fec23eaed71ec33e90cc5cafbd0f5162d87505a143e77e1ea458bf7db6772753bf6b7b2703645e5fb10bcec73efb1eaeafd847fdeb33ecc34726c51f3ca4f643395398a85d2966578af54a11bb52bcea4cd11c188787ee34c9265f8cd60673174a7ff0f07a7ab759757048982312e6657ef3f4917cb90d7d8f881dfde9fd471974c200f7352206467fc220865958300d120c9b316e61a2e539a6cb88fb1196339563a6ae211c1e11eb0389f53e46cfbd0fd19fbe7f87d1733f33c220f62c169e1161700796a70f627886058f7e3e3853e7c95b81ada70b625895305d034b1ebd2c5fb68baa53971c9d5e8a1b9ee19e8645f452cc6e6d4ebb9eb38b65d9d10065462ff30ccbc7b8ecb6a7a1cffd866746cf8269e68fb2263a81f57388dc052b9691e22d81b521e3ef2baca3b139d71d76ad067277e00e9e37d33d58473fc3fd08d320a94f635930cdf7a3af094287cef5799022cb63d735903b19968f718d05ab1a28c33d0f52e4b00f24f57dd0e7be8711f6315ff4313efa19fa230c6a3f7a29cecc3ffde87b38fd08fb4032337a96f741fad3f7c0f2a637611fa36779296257d340de46178c40103a74c69e07299a3e0234b497313d28437a900729921eec21861d183cb111473ffacc949d9e05c4bef452245d10c3e00ea4377d0fa32f611fa51fbd09fb403253fad1fb303de9a5c8722a99462491e54ad174a558ba52245d298ebee299f9a2c72e9667886e8ea12b36f992a2e84a11c72d817dc12a0faf97a109db3faab89fa1cf61b0be1467e683d74bf17f0afbaaeb477bd52b03a13fdfda6409c30faee67211148b93af03cc972d3c757de807f0bffe5152891eb293710ccad46d7be9bf43c2f4d63aa3ed7690a28de2e6fa1ddf1209f392afefb143390eab66eaaa3153d7cffb326566ea7aede2b015b352d8510ea956d75fbfe37a967c3da5225fd7f5270963fdf52c12c6e2581ad8aeef6bbc9fe1de7beb3d4c83c4c233dd7f9886a6e6c3ff1dd305b4de7b295edf63ba80160677e0fefb1ebaf7b00fefbbffb00f2433de7718a41d95a2d8f4e3ae4b5ea8be2c3cdacbea9fe13e7b17b4dec7f60e36de613ef733f3b9b7de07120bcfcce7de07128a67367f1fdc4bbe5e3cbc2d7a29b0d64bc6c144f0f2ed74e1bcef0beee0ef3d48f18cbf076e7f41fafd18a811ed7717a4ef8f811a91a32ffa1a8ac11db8ef7e86fbeee977980609c533f62d06351a7f8efb1aee7e1e55a036dfc67401350ceee03df73d7cdf611fd807920dcff47fdffbf0f730a8bd14bda7dae7f9a87ca80f7765d39001e2df7f9d6028d377a18414e97727836150c67a7026861d18e8a00af13282bd8dc28ed2e79257b0df6c1436c7749199fea723615e727d4a5133458dd4975d0f2e566c672a9c29fad7b4aeb5556ccff8aa9f850764e7fb3734aba0bdf4dfbe877e0dfbe07c2099f121516f41f9dbd788f3734c1750627087c63448249ee9df300df7bd3d4dbf6390be14fd6bc46a5d39533f857d999adf63e1277420852c31289f4767d0fa99edfdafef0ccaf78184be0fffed7d20b9f08cf6fd3e365062d0c23bf86f3fe3bf61395314cff46bef43c3aae99dc08eaa7c0175eebc49f963b6422553fd624b17eba53866594ad94fe5ca9bcecebcf1478743eb90653a813cbfbb1bb69c43a6fd968d8cd14b4c9c9f4112c6f4ed99b29430fefd321286f4fdbd236146dfdf431246f4fdde2361ecb7258990e75f45f27caca990e7d7f95908f27c8dc680744b3fbaa4df4477f49bbda2dfbe6b7f33ddef37cb6fe94dd7b32eac66dac6d1375def3bcbebc7da8208de058b789705152ae7dd15da8fdec4bba810701ce9c4fd38a7789725773fd21898be66d3972ee9cac818fda3db3b433d1347c6e8f7ee0c92313a05b9a3909d89dc20c89d84dc6ff5531e19c326cbc146ad0bab59a53cd685556ce2045dd885ed0c79cfc4a1d665518b0ed1b913e4eeeed306b57b8cd7ae46368b042b5fbe7c8b04dbffc97eea02202020202020202020202020202020202020202020202020202020202020202020202020d9b9096c97a620458a142952a4489122458a142952a4489122458a142952a4489122458a142952a4489122458a1429f3e5a42620ed935a1756336de33aefb3a211a9646239a156b0005bb0b04105d6b209dab1a13c208b15a8138bbfc9850b2fb498383b964f108ba9441a895e78d07b76268ec87ede4ef6effeb2689c6ba8e3b66b28fb6b17af08d345ce94f5ae694ae625030d64a881ed97b9c60557a41072078388fdc633544e63e500be645087cedc77e6b00d2f1acef00ea8c88efb8adb503184ed91c7e5d7fb3fbda78c5de9031f6c21ffc8964d026816834c3fc6fba78fd498a918f1a7e8b1ee0e1e3d96b0a33ff1950e194f9efe3070ff3c7123309ec2781eee02e3e7ef8011c4396ea09e1d2a8e1c39c27363c387237a8efcdc4c97069a1d8dfef170feff98a8e8f11f1985f5271246cbf34538120626cfdfdcdd9f4c17c94490fb0059c982509e3754e4e94fdc7dc8af4c9bf625798ebdc457208e1f3c9c9fc40df2c84d7f32fd499edd8f83e7ae3f47b20bb97f648cf9344401846828e5e2678be4edc0eab8de3ee9fb4716f5c8197e72abd55a8a75481e5c7475c8b7b445a24fc7f5f2492fbb1d7858593ee94d4174d621df4adb9d65c626d2531a161c7dcd234a2f38925752d3ddbab4034b3dfb11d3bc973fb3dcb897ef12a6c31bbda3abf28025fd68f34b94af748c5efe88f4d6f7148f9fa9bce3936468fdd8224b961572655fa2e4cf2c3b1c4acf72e79faebfbdd3deeb49960c2d1e579c6e6529bd09df30b308a3640c3a7a7a47ffddd1972e900cbca865f5bd47128108f0b0b97b1ea88c3d8f9ab9df40f9328bfebae0f5f6651e5d507ecdf2b7efa505ca97a2ebded060109de94dc2a23cb4fec35606296cbf279fbbdbd5e467320626ff73cd09a5f4433f00b52aa5f4ca324a29cd9e524a2f3a31aa6958e52f32f4b1ab09aa1d43b6d2299b7c31ce9e4cb7967dd33a1749ff901d0f298deb1b8804d488f97e91139b638787de12f685bc7d75f9191ce109228e8001a12988f4af1b433f1be2ab1d2434f3b3b77eda4c970e4f9b99cd9b4c318d7f867d68a6e8d35857beafd0aff54a9527339563a6e8a3504256a8626fe2a10ce8cf00c43ea594d26fd13b819d5f2b751f297ad737f522a1e13efe33531e94e97b4acc2e46c6a0bf45f11ff7e9711f4c8a413ca4ef3f9029fa45089390e98faa4c9d0914c3ee236350995f0adb387de3219dc10576f650d7acd093fbca7e4fa85105c84d43ca45145676f35b60e5469fd20f67c428a09bc827d287fafc727fbfed2ff59c737e12667e4ff931ee220956ce107f8f953ffed743832c8f2f5901646a3e0c27b6a36881ed0db4f04e8c026cf0c0024b60c003219cc8a3f43932b139277595afc600e4f9930742ec6424f264d21e0009e33f3d0c62fb5d24c1f6dbe0b4c705b66d8879f1b2e31719e389f56f984ecdff014a3a219b7881a10a3d0c33251bf71587d3d3df5a5c9a7e8207b2f023b6a06927468af488342ebec5c31882cda145ae5f93fd89af5840f1d5e6e3517ce57ddf60a0e549b09287f5dd66ba740c7ab020d87b8e0ef6bee344b0f723d2c914a864ec9dc712b1b7b2eae3abb1a9c83fd8631886bd6827ec2874092a55ac60df54f80aac9c686747b423dac998a8d65a5ddc1f9d03851243a28b073fc65d5c242c3e5b9655bffbeddbb33a1adb6f1bb771d5fbeeca78a8fdf43c0dbccde2f6f0105b81693c8cc28e6e03c3434cc2dcd827120626d3d084752036bed21e7bbff11516622f5cc7874898eb31ebb36b5ded7198be8e40c82018beda30fdf9d8e8417858192ca12ef8bdcc1e98c3bbe982a5ef5cc26e3303893b2264d1d6e2ca1e4ca1c81531f455fd16d785114b1c11351adbb7b8f2670849f08861a783f5d2c33bf6c87566b03ecdf5b70b56991dc88fdb60492cd91ed4a1fe90ebcb2cbb0c886718f6d708eb37d86d3cc4665881adffc25715b78e8718d7a26f660aeb1bae7e17d5c243ec5f0bf64794dbc814f61d1e2b9661cc895cf168b1972f30ac43d4637291132cb2d024e0f999560c2db0a3fc59400c16b08d258f89c3c3f6c186874046301ed2ef03e44fd6217fe2f1080d3d7674409e78c69c2e158acc4c4905f8b5feba406c735e540e6188523134e7bc68f3d4f05563394a28756234a49449b0eb58b5714f601df712896730c28e337860c4c0c4dae87e95af5ae47e18be8a0107b63fb4622577c700f9d2075bc261c3eee0a28dce393c943f40e4cf0bf9d345e000a20710292891bbdba9c83d65949806512d6c681b3c3edf930d85f298d88b3e786135a60b0c1f191f72f0f0957462e5883ce7bcaaf8c42c8a9a6095996b31bf624ae9e34033bd8195d93b1c3a77d34031b1fef3caa00e7d7dbfcc9bb7377d3a453c586095a513290ce58bfebd4658195c638615d89287610d999a5feb689f50dc38d5071cb0e34b0d5f8dff07c8de3f2601be1a7958d9c8cd8bd9f7a7fc1bbfd0c0cadcff82ad97f9325fa64bebaca4cc961329bc4c218baec619fe335ef8cb4bf616beeabf80b0c917a34832b141962fb66035507ea65d0cd488db775fe3fdccf6decbf7b8ef304de634da6f18f41a914e51fb4c5a2bdc6f76c8c32b26b0f47d7c244c14dbdf3fd385fbcebe5f6a28eea266ea09ed81cd5eb4071d1be146d4142f5eb9ede6b8413eb20765b4d7308d8f1f9890b8fd14b57f861b4b1f1312a5b8611b4c48d430d8436c9b99fa296de139b37c22892764d93edd3353bd64a6c6f6ef287dd341faa77d3c1c3b1bc9be77322c67cace9d8ef10b64c7bdbd7da64b17d163636323c490afacdcdf5400e55e0224b75bc97dc56dfc26f7d32997f86aec21b9bfc1c6e91c0fbbedd074699f2a87f088b243b925ce3c00017a114e754b820d94995f23d2972fda861258d05f8af441ff1641194ab19ca916e7fc162796339ca99e4fff0556fe18c6cc97ee219ff66e1c1c6c600e123472f3e2f7463efc83fe8848fc6b72635087fed958e69a9a0ce670e5f932eb98d87b99bbcf56dea4b74bea8137d03c7f5e0cc8fc04132a88498013567e026ecd66588105b551f6d892edf944124de4896ae16163f8afc73dbbc7734a45fdecbdb33c22673a58cfd55ab96d7beeb31b5e18b57e9914dcdd9fc6ca0b10e953af7cd98baf460925fbfbf70df94a6583027c05da92953edddd313421613cfbcb4c171a39842124b040fcc1f9c00234b0315d684025258c047bcfa17bfbdfcdbe554efa445f3886303cf46f68a6fc459684a78dc2ca48982ffbcfc07c3ff3d9c032df500ed94f3087ecb1cfb20be630aba8a28a2a72cb2aaac8fd19023298c3f7d97f5f93bf21111eadc52a1919c345de73ff711e6e5fa50f8fce1a7751b745103ba2328caeb2ab99f655d3c01cea675f3fab18fc1743d34546a6fc6bf56f287b29cb277a88c8ed3823efa870a568595735535ee3ee2018cafe32a0b0926b0edfe0d9b3aaf04c2b1ce12f478e7b397694fe6971f4ac6e1c1fe5508ecee9e12b50e210c97ec47bdd737e1ead8f145152cc2ac679a32c9f48220a396268c28ed647c2d0f4111088600a3a22cd8fd44f91274496ff3143084f7a209edebf974c179a17569001f107f80491e684817002f8b3e01d4e00ffbe522b104094462418997c32c2242cad94f6b18e86e51961516fbf71dce8b98e86a879248c97fd3bc97745c2c0d4bb7de8d6a7093b2d040513ca3ea70df2d5d838d6c757d2895093ec6fa3f8db1f5f492755a092fd2d90bfa5e22b3a6974cfce8aac68869eafa361593707edb9ff3a06789f434e91f560561dccf00d9e33cf2e9530d44b9470172073a84af6ef26403a9f286c203bcd2e9d340189ecdf3fd209157eb27f0b4d9716fdb36af5f0d528adf0f01558391a5fd124b2ff35a262cd635918c481660b75c119d4b33cea597e87af7678e8288be5add15bf6ad4ff6383c5415b1997f86737848bb1cead7b7be266fd6f7bd6581396c39585fdfc2a07d1e56b65f936bf2f6351967a6fcdd5422d18e861ddd1e1efa8b2ecd0dee36090b6c2fc1cd235b60312853235a1b4a60591e946161017b68f1f42d9ede7b811d1be7bd4694338523ad8569b4006260af8f993113756b44eecafcf58d405f1e9dbddf2a87ba35a2ec7a20c450bebe0112e6075798880213511cc93b7c25819022cb778f8da05e8aa7efde3b1a1dea9e6e8d99f267b9fe53f4bc86350e5f71efff83afbcf7cfc1fdc675d6cbaef3bef33c3087eeadefdec2e0f51d066d641fbee66b847deb335d9aa75acce3939d94e51338f0406e3c5a26aac093c7e6c994b34301a996a8f4cc968541eb14a21901000000003315000038140c078482d17840921435f914800e88aa486a4c96c70325c8410a2983080106184280080014ccd0264101466764bb46cd93d687284a2ef37e74a1bc1a8186a50e8474f974d1092432de9a4f38ea8fc8b22972cd1f109d04601e2b4f4543c2aed26856696e9228eecbf618d0a8caede336ca6bdb2a8a5c4ddd449163593a110e2125f8e778973242add9ea6e207ba386da2bf8dbef8b0784bfec462df371ef1a9f3d85a555a91b9b938aae01c06cbf104aafad4065785fe17e5ae3c08ddde4d43a88b83bac528bcfc67787865186d3bc69e7976439881245cbde72115edb32422b359c06d8beefa6338ee61b17d89b83bc9e20d3dc4cf7a2e1c6183c023a16238eefb858ef1d2c22191664d5c4810cfcca8dc99d932e0280d7a883dae6e817fdf7b09fa6b5a4937988ecc2946c4da956a2a19cad076dd8a0e0233d7068d770eb55353ceb6cd518b10879c09b0fd3376c21a6430db62b4d6ab47340ea47e12a6787554189f7241080a4bd8961e6833d427742a33e23d38b9a76783a8ef34cfde2aa902da4d517e131b9f790580d294ebcf75173ca27c305f0883814d1af6ea0370668f108d62f8017be91f1818ddff5c62557147299fbe60b3c5b4bc9dfdf1862c1d920c812350383c202991b05cc2baf119a9da86cab7ee0e1d2c785e85c7232f66b97880752d9a728aa106ae3549bc791b6e54d0d708e6b5167e8d6e28b6057a377f32a9cb545ab91c8ec704a953118d70404506d172d088c325e5b4eacd090cf988e6fe1c91ad2b0e8c6289a1dc9b84a2b5bcee2b4fb6e92f205fcfddc5bb697c8e6ea490fdcb69f929e76d37965b17d41e3568c9fdd1823831a30c0d1b559dee25b5a33b898f20c7ce724ae4c9e20b8aa2ff93044b2e03898a7dfdff9dc30e943542b1636daa68f04e3476e4bc690d7375f756293eb364ff1a1fb783d89ccd5672e188ecec787ea7fe859896e76cb226e79296e1b6a4590b255b663c5fa17c047ad14279dacd52c9589649003fa0e89257a3c39b0d4a01435149f26d2469ae0a56398205a314011247b05c5d1be76ae42bc582e5ba6cc20e5655edc9030e98347bcbfe08ecab323aa19214475c6431855ca887dc8f0e3fb0670b51077770981044cc9ab67bfb205ff5964c9ce43427c9f6498ad9aaa1401a3251b2519202daa6bacd3b05e6ec21f2a5e575c5babedf2138ba6e03e518c5266bc52818d0f55d9324017ebc1f63e7f74a9223a55304d369546c527cc662310a136cc746b539c44cec78425e8c2cff9fcb8a66a01957636bf60f02853af911435d18823bd3edccb108b8f9c905bef76592b75b10b59f753e7dbf1fee26565d1ce9491a537fb3032d1d24e11ee30a512422e605e268dab88d3c891d192d0d99a29cc0e9d41577e330af7a79cf242d78e081ceb395f2027c02c41b7cdb07f18a1228c196074bc84bf856e928bc0130af2ab345d7f7dfa2836c92a070868cd4d836068e84cab7a6a3784555a7234051c6fe9f7320de149381df0f328557fbe2f075846b5c15dd76e3c1072dfda4675c11e1917b605b25fddda2b04a9457385e6d610574b15044b65888466e5a508e53c4ecf1a5622366cdb91a86a4655d25435624ad5ae8f31fd0cf23d306e78f47e3bd708a8b8899a4bcadb2ce41321101c0c3b4fca486d77327cbba0a913ac893f72b06fc2b86ed3c9c6bbe14b9ea4f92d61a69c782c8797da56fdffb9fa01deb513bbc5023b2016463f0d3f43d93c63af601bb7c93ca6dfa059bc7e5a92ad0d2e56105f8c13f18233afe3f1cfe74b1489fb21c10e9f00ba8a89b07625dfc292eae5a704c4948a315456f9c8145e933692d1624e458f6880d60f18762ac0a127aa9e812d9d4b7c4551c19185e451b9b9712254d3313afb21cfbe5662cd908fdaf9fab6f8ad4bf73a867c57d934ac955f22b1888c325f956c7dc9053431f6e56f2e93f294fa5c28c13b9101c74a47533c47660e2876c8c8e88fee0541482f93646ff2a5227ed53d554a1d2ae117f3481cb343a98683c86ba650c06cb926c55633c2fd8d7ebf38428084e4824b6cd5add16ea73dd35db66524a5c7f0876cd895ae999dd1f04e977b7e745b3abba3f142210df8e7f3b370041e2f074b99ddc870ac24e3e7538c9b1fce2e1f34974d14c190635842cc4ba966355f0178626adf6de43601c7b517e69f983aded515786edaaabd7b65fb38597ce569807b2ca8c3bfe2a3dc4d11dc746758441744d548937d0a22c4cd175efd23ead706d3d4d7a06709be658602d305a0a02e5e454d661aa30162edad7ad23075c4f3ef0e0e5f530eb68ba3ad705bcf925cd8668a1d7deefdf8615579c1f09e6af8bdebadd3ad7d5363f2456ffecd0053aa3237e814b704729edb63c49cced932ab07dd53996a0e0bf6bc44ea089593291b95c924c3dfe2172812ff096c61d971cc2f2a747cea1a318830170703de5442494edd1d87bbd3db809840442fd8c695d12eb008144ac71c840ca6090775a400a498b90e4a6343236c5b79397102293f2325b747a2aac7c1e823d994a81742c2e94128039d816880a344d0e09de4765e2ad430ff0cc2f7414ef688c0a86fc466e4622b0365d00251d666e221601ca19319b065d2c4dfe9c8c360986d956f18c0e7fcbac576db690a45730934cb961635053450578b84cc4b09e0dd669b7234ec5da1bc3db6fbf68231502d5cc2cc6d8fa34f67cb1d6e04f9b0a4458a21fd9c328f8d7c39bc4f2dde2ec10c8f57d253aa746a0de1be137981a181d18066f78ef3a882061067d8cc9968d746cf9f5c85e3a0a66972890f1f4de01cc40c3693dfbd2b583eb5f44458707a02818a100a4addc7a4ae10eada19f5203f891421bc70e746950b572ba1c8fd70ff21d2de941b96c792ea40b884a24359ee3d6bd3a85020a3fd44cb21f66d790499e9d32e6323410ddcfc9898b55ab0655e06e3042981ddab814ce3741b2cfae20f6c8d4f53d0dd8ef8d3bef73d8ad0c91c20ce75c9a429a286b04901d48fe77629311032aef3b5216f60275773ed0a76dfeb2ccca3a58c4faa2463d1518f9d444477504d6b5c93af832fa9532ec6060c7b5747b315331bfe6adb2e54ed0706c9911515937b524df66d01f6bd92d412fd6ff3b888c21efd3ad196acc3732a21b1dc24aef702460b9cd25f1d450e8a5d256b9fcee90d84d07f9b222f10f9bc32430144f0f97d826a5ada873cefa8a67b20b31b4236b915c17cf41336e4ecbcd6c8b94f70598e3076f7d7d30410b7def047e332bec210c32561ecc2d17fca6d0c09e02813379823f7eed085669154a5ee3dc7c4490a2570c5ee3304f1fa30e1146177797ff86ef605dd0366181911c09010e38ccc49a9e98a20936900cdc4045d241833581b7201acd6975a4c6565e14d5646cb037cf8e2934fb987d255b17a286f42f78ac7399d15b25ab85519feea823ffe98d36b43ee7c0550fd0db9fbb1cfd09849d349489bcb11acfe89c3880f5a25b7ca681fba6e1003286591b2fa2be9290df0d2989b5a730d85f44cbc3d1afdd5fd40bf4695d6a06fdca727026932986bbb6c19bfb6e97e8ff86eee1434cf4bd4bf711bc1ce532bd5c00c16bad40ea19d000d73524411f2d8e98a0a626ccdba7caa4973e3ce1e4998abe5a5fbe4cd650f13be17bae456c5f395dd28e82656a130b71cf5572e18685201958de21fabd3072f01dcd9c261cc774c09dcc2697e8c1a4dcb9fe46d6ce1585e726043c35d5de701dea48fbf2832ab6314656b16f3f2c4476eea5e606612c271f80fcb1b124b38e8a608a295b96e0f695a39152c98a0826d1b78779f5abb5bd41662a15dbd08b64ab55bdb9966a6db18b607b69d37b7e73951b22ad299986e2749b43ffc8d1ed7954147f7da1adf4af50f533e5fa9721ad92bb3329a7d5e519d7dc1e65db43cb95e80db3feeada3093bec6debfa88c50dffdd48ca8513fc97bd79391107698c2647837dfd8d0f78f19df4fed5f744bfc7aba6a1b31e19ee5b87ad33e70b9f1b1cc09a0e6e06d4815b6550f8d8577b5efa282ce12d7c5aebd7c468ea723bda1036eac8cb71e62284a3aaad93d45d8ca0313ed512bc734d264391047b6f2372eabc397f575594e4ff32efae17984af237eacdf1ea542f96d752ca2d696b34c82ff24b16c855243e7a0c707e4bec4f699b029f52e6433a4fa83d36f5ade7f323d24fa7d65792316f981f7141180f21d99888d15a016b3c56005c07e8a934b2117a22852fab15a30f9a89a4680b768b93d3c446c742ee477b9f11ff09f0b7c94874e3d7efacfd2c8d8fbdecc3413813a3ae1283548d31e88961aa819ef76f37bc08a82f19491288bf8af4dda12fd3720bcc1a46e92a36efa4e28c0dfc29ac56d0f2290b2bf0af1d80a2e6414df6e6cee8ca5572b18739e6764c869f4beabcd4169d6e5fdda8eca109472928959c133dac274924e5444f21f6f211a5fabae93851ebba1015b737e8d412a8aad23a6ea7a1c662cee6ed665e5722f715192bd1b64aa48a8e55911a52950c16e9619f694fab9a0460bdaf607b1c8f2971fbfec7aea97c1a778845c0e1d42b85a876955044a12834b830bfa806648a9ddcf152b9f871c662aba87f786a41eeb35db92ff6c480e904c536a6719f9d8af510f35dff6d690b5cca05edd30bd46fa775818f7e591065605f8343aade11b37845e81d974357ce3655b5e7d1bc99b3029603de393bb08c419a8ef508520166ef87e3961e385be6a0580d61def567aebc3b6c2e16a4278f5b2732e6d9e13ce4e3926046ca98cbf318d08b8d60ca09e17c0449a236b3774bd75d35c4b64ea7d39dd58da8898d51e61e3fbc55d2eaf1e4cc326f0ceffb603147fcfcf343a5fc92baad91bf5166625907af66012e394e8a4b44e5dcbee8b4ece1b1b5b97dab914fce2800345f0d28669651b34b4e7a4222b76e12a5401a407370459831799abbb0d0137ea975f2d32684ccb098cc113df655c2b2246a4e505a0abe8b92f0fcfbc77b55514323478497ae5fabf1ecc1b558efb28ed39056e10c1cc10dcd861792405c4523ec17f5de3951c09c8fe532bc9521ff179ea81ff0db94281188a3f355f4399a78a5beda7933253a580f735e5f756bd28e8a8b2bc79a0a8ad5d13b78943473d5813332a7914038f15edd1c156133e0c50d632cd57274c00dbfec804587f2f942ec69608840aee96582e1bd38eb14fec7a17cdd23201b177c11b2e05ebcaf51b1e553e6319b3602a4e1fa2a6fbf88ae6ba7ba0f696f1f9c843afa8042eff8ee41ae8e7a210d615c8de1146a1b0feab759ee1ff70f3738cca4ed69f86a4f1b9b1c1cae99f2c1f2d6ef4f0ee79be36fd6fa582af6ec6d98c68b6eafcfb0c5d87b8f600ed4d2dbd7f9835387f1f374361212616e0b18b5d2c8c5a5765095d65e7b3820e803bfbda9cb18923fa569ab127f921f74366b18dc4a486fdcde6e997d70e92b7143c8ca52bf4264d5d7582cf76f3981d2d9cb2b30800786235e68f15812c8a04b5c5e88950362160b613036a4fb1f9d559374ee113c9a405a3bb3fb862f4610cc1657d7603f7a626a777b0d3e083de29311ec164eb114bce1609d0298f66cef86127622fa095a2f3edefb2a6de4ae63ada8642cd1ff1c0428f53120af06c4078858bcbb257f78b006f117de364f3d9ecf591ee37bcbcc8e76471a683957e335e32f73c23fc4d7c9dc03c56450e987abc24447273e088aa7827ab02fab551987109045f16257796c01f0ec2ed087c911718bf17b1ec6c9f35b7b1ef68bb77835e5629fa580183c4c3cdbc4ca5875b50bc34664d97550ca5dfe8a61ed1993f54d2a801e8e1bfce78b34907c8cfbd00389999909584a701dd3e35012537a3eff6cb30e323a338159fa2501d0ba4ba7efafb937f50e7245f7235c99501b3d3320863b0e439df79826822c2168e21c84600950462f70f3e8ac25d8f10f2d1cf669529164dd5977dd65291fbbf3913271d485f529c8dc762babf33a476930f0c8e99634f5b6952b9ba74b1c284b6b27d720336e303dcbc94ae65e6b5700b63539b4b67ff74ac4853836ec5ab59c6ce0db0f2cc8d997600b6b7ce9705f00f3e39d95e449612391071fa0180e7fe082c66bde61c8531a7484a25b90c129fefc9bce817d7afaa6c3623877d78798aff8f718670057b5ba0af8e0829e803bd2a763d71260c3f626d0aed0045027e9e74a23d2adf8614865fe3a163b42274ce48d14361df75167db0d25a9946eef40521760a5ca468f26eb20d2cd132b4ab90328f29e6ca8d03d92aea5ace32329daf7bfba0a8e0f1afa61cfa389b47b279c117c86e256356d3d7781ea3fa504973a0630e9cbf76466934477b69e9ebb475e97dda90a8ef5c104f517b5720d10ce459e760026a4c54fd0529b84840c6baad742e51d3b2f50922595b0ab8fe7ace2798a878a8b03708bc18cf0f8e915793c6d0534f572a87b691d037db79aebb634da695166c4c612654dd7c7c2eb563d459700ba542e688387ef0941e1c18085347d0cabcf44856bf8338207f26441176da54363f163bf2c8865a8f2be95cee5132ba1762bd1715df06959014b72124ffcbfba8093302411795b0c19ece0118835908d934188baf9ece3d8f95d17c09d6cdf98b9e060b570282c8713bf22061491d2060a7599ac7948321f22be1a8bd04694d2becb590536f59ab52ed08b05842754ac3ae5991f60141997a2ab0674343e6860a3ca6dff401e5a4a705665004353f5e9e26ce9117458f6716a4196f2e0c09e64ab3bca824423c3fa6641d60ddd65abcf0678d66ca2eb4625336860cb0dd7dc1b3adcdf79e51018f20b2da0f9c8819f492441633646903a04b9a1dbc07e4591912ebe0aeeaddceb580ac78e95cf1d480edc85b3498f7a362e1e29057f7833703fefdd27af6c340e7c357f3a3010abc06652b15fc7a6ea70e12b2afc3a8af9327d863c363156c8dc7f0c90f419c0157d3057b7a14e874e0f4d1a1cc1a0ae639e43182d613ee64695ed6e2558990f579ad4af02b2145ead04595c8b453b9f25aaa314081cec5ff5dbf0dd1b5a232aceacef17374bd874bf3a275590bca4d5abfd22d89327af0ee16c5ea5a5c1aca7ba420328ec9d02a1096168367386c537f695ebf0917740672763a30c1ffe8f6d888e64bf2b6e4236a196c69d6eaef712e14c753d59ee214e9d7a575c9fecb4f7bc551900110c9e9d305241bf16592e0a0cf12a08b73e7311f72430b931cc0648674d4bc3fe714ca7ebbad81a30e53dcce271cec4a05da78666f271baeb7fbaf1449e8e334f12bc55a7040c573538b6cffbbf206e48425141bff8c05538c4e6bc6fb9c42d9189d530fd0ed612123a86738fe2a19c9c38daf00dd2298c164da64728b9924f9ae175c1ea2a507742b1995f8f8c9c743db20f3fc14a70216220442058f7e9acebdb845536f9ef0ac350b13b397b212461366c89f248b9a52ed105cc9c842b51d11d11fb9d92484fac27e026388e3bbf60d8bfc0c8c774b999813d760a652fbb294341820669a1e5cd16966bd47e290297279c57b5432d1637eb4df7dc6bd85c345800673562a6547c00985274d0e35a58cf5714bac07d3c45d7889dc814e0e4e2309c1a5c896f8b94a73c279dd17eabeb92ccae1517b0e4e6a4519242f06772ced47b6f77e49387a75588bb9084757a21f0c60aab872135903987e48cced77cbd5ae9128ff7a24fa55b7b70cf12186c0e5981b25350fd15f55982c79a4c32906e955a11bb19697080618d1cd7d73407b3738bfca5a88ac84fa28adf2ba635e63d81a5c3df46d6347362f603feaa6889e63330a2800fa12d9fde98377a3dde65d311c5a9e0c771a3b010d475871eff91155fe279f18b7cd0efd78e13efbe8abc79bed67a515ea194d799afb68ae425f57944f30c27833ce3d33c03bbfcc9036022e333ee4e88e208c993615fd5d51d5af5c9826be914debb340466cb63fa07b2abde5425e18708f5b9233f1da16b16d439c44c7128c26d38385bd9aa9d3e7da0a17a3d6f1c2d55024a11fa7e7a602717a06f6a334d4dc50d065aae319ebc50d6eb3ced57aafb59d69456ae6aa085436fe72acc1345e04c35b10911c2ea4fd8da7421709278d59e731958b1db003a08744ab8ce3cbddad3213793d095f09cc4ce2175520b422e3fa7c6094ac816ca540d3f2987ab03021bf7bd02595631245208310348ed2287c47479057d4c2812ca385c62450f21860e9b82842408e7c7e259cb6d9e2a3354312aff42852794f8f16011aca4e823c731ea18a28557e10928622eada728a042e70cd994d4288ff67645f297693152a4c4c57c47ad37b74a2e9c9333ec840d44d1cae978f817d04ee3c481efdc61335fde2b2f4ec1ecb21ffc68e50809c37820407268ae5c2562e5eeb0e12f7d51c29463f8778077863a412026c9b7c2c3c74d70444a2c8c14958295ab18db94e5e1517e40fbb39c29b96c204e1d1cf140fe4fd46144ba279150d54a7a4cb3f8a8e91301f367123faf58f0ef9b5a4feedf30c59187fc021dc60001a015695e0374491f2ed2fa76db7a80956a820a522260eda8d6ea889de5bab74fd56f55a6a17b4ce6aa4bc4867f7090741207d217347570cbd6c98e38ffd0d5f2ce5adc7ab403e7bf60312502a3d29c079fd0b9928ac4032c76785bf85b0293823edb200728940218d11fe9811b10176a6cde956525b9dd188304d2dba36a98a2d4d3e66efc561da5efd3ae8f81cceb9be4f60205ce0c6cad2eff745f732a16015aed1b41415d61edd6a176aab91efaea39a0c116b4c1cb4bf3754ebf3d37e32fe7db2ebc6d012487ce619a388d3bf184278ec31f0d9e16d2e7d850b56e6b9d9e52beaa64fb1ca6a0ae2d5e24e98973eae48b7b4c3c0ad97ed19e7f5ba971651c9edfee51604d77eb313a54d975faa7c519ec1835a28b50bb489f5896f53f3cc667ee456b7f8672b373016caf0575058b9205d368334578ed2b737fd29a118affbc89bc43211a1222ec44465704ff1ed1e9833f2c8ebfe2fb1bd0e40c6f620f77283d330c69fc6f77a2a56343090c25aa6a213852049042adcfac8221d98597cadc495f025a9328c453f53cd007d70265964cdea924f31357e9c48875584c659e01df606d6b2e9ea9be0cbea7a7935aaa08ce0bcb70892c81f6698ac61e2c932fd2b4a4909b9f5f480d66a998cac658589b53d9deae72d9cd80955a38a62f95ee6737973e59a88a49551809df7f96b2581b2061991629f4cfc9ae3fbafa11dac1a9eddda390c419ee7ca6a4db2048fc268a456ac6c1d952d697b048abd8c202ddb3c40216a29388bab851699615982964504917daa1cd586187aca3850faf9109a56ba42da499ab5c1274a62aad6145de2a9d2197a9a1bdb0dc6b555c32a6726e7123e43b345b974d18a161f503d4eb9145d0b01662f1490efd8ca9623519253b51be3168edbd085ee780c3da964719d8903ccb7f9a94d17b4242b5df9026b6aebf338b8122f7d61a3a060856f48ea2a9500e1a05c8a831013d20e3b488cc1debd7976c1802fc4f49edcadaedf3e66d71388a453a990bb5f87553d3fe113f4953ea3f09f10e8a46fbdfc58fd35446b72b44d04fd014788d18dd1ce05bd99c1423bafb56617f77074a28e93587e0aca63180e96426a74b054ae2e1fda44ba37b43718b39ae01f1fb3f25bd077f48d4b6321f602128e1dabe03c1a7fb107e9b8dc43558834974bb14856c7bc3f128630404a7d486ae010ba14edbe6143d6a0c8eb5fcd14bd8ba1b01e45ced6f4609636f2602561f77556dffd6956b0e3b3443618ad4bb311f7992f84fdacbe17b623be32fcf9195284741fb985e6e807173ca97db9ce54ee3b21880988830f2a1d9a385653f00dd51c21ce2fecb1c4ec79ac77e14591643edf49a83d20141a405633a3f480930b9105acee064cbb9dbf087699dc38cf6a366e5aa32edb0c794ca2ed9fbbdc82acc672a0f006c4c0f4f9dfb4aed66d7fead2fc1240712510cc441032172d1a90a42f1e8bb6e3b9ae41165128c07473fc4b34094a42dc549233320c9986ea8bef5e13dc98209bcd504502e47a0b1ecf4d710666fbedfb1ca1940a33c500682fe5598958c2f7ba9e2b67a2898a3ff902c995fe1a47e304800f50719ba5b4876421e6d4740f094e7fa19c4c7ef6456f51bfdbcc770521588edcf6378887e36897f5a92088495b6e2ecb77f878c9efb9383fa738b47fb2945031917576192276d9423b553523a238e413e025f6090bd6988d48787acf1c39680c4026a3207015295f9f025a8042c585eb9edb66786edd15d2e8f12d985bfdd6f2b395b619c6a0bb2806016cec909bd6543c58ea830ce057ebd1ed0a5233d7cd6713cc5b5d1a833dad7b97a395e3a93f812cacb28a57544c3d2ef9df0576d32624457aa70e4ffb951f6b3c9aa5ccb00f9f227c2b5467751d625cc189c49a92aabd31c5cfae567ee3ade5358de40a05e4783865b6d53652beb1f045196b2d63d771a9a2777a039fcc86ef54eb9e1367aa32fbc1f87897aeffee71a941b8e2359d52b2aa5ab39fd370be8f771f6ffe65d40a83e74385db1dafacd6671dfbdd9fe8a30fb02a1f995768f9b02ccccce00dae7c868d381888d5adc7ce7e1cab4ecf0497d73d77f024a3d46a40e66d28b7e3e2daa1cf5ec4ae66e3ee57c197c0b58ea40daa1364666b58c86005ed432f4b697b8cc906662217e274290b57bb66d8131e256e259781a25ae3194ca9655148f24e693531851b323c64af209a0fdb14e5315e5249a5684a913acb44b5d428bf5f4ee61dcb1b2a363bff284fc2d4591d97ddd68d5c48cdb4d5eec61f1515fda6b4023767cb724ac7986e19269b91e6580593db51f30f422d6925548ed0c1f5f9944e031ad63e132e0a7b733952e110e2dd2ae6672971e3520d9bb3bdacb74001fb44f2bb8be75bf2db80045df1e0300ef03c4930bccc66f00de73bb09d4663342eaeb9aefeaa2dd4f5ba1e2d0f64b58bccac8343bcb7ed36009a3ebc972cc390d8a63a141b41d0bd750dec51c64771274f965a1728fea1987c83c8535071132ddff52f0c68c41d9b623397e3b1349818809cff0513a3bcbf57e25979b85ff6e1ec62396de4a0114006b301c33be69d8cb91485a17a514911285fb527860df0b95d3c5c9d60615f131466cf30a00789a09c19b74de3c675294680afb18b543c3d1429dabd7324646c8bc4bc3cd29b6ee8090704a8cdc5a1d6da36605b3fb8143caf554747ea4c9d0bcdab91f9707048f795c700bc152731e1cbe91a9b2677839b089c23a1294ed761358616e3fb8be88fd8d50d0d7e05ae3c732e8c3a57e92a0337717292acb758127409e322998b59b61e07204e9191308d20147895a13f66a2bd0805a586d65e25bdd6a9478aab15712aa8f4ec0978fa52e038aed1d88ddde4bed8b53e2a619817f5230cc9cfaff7c0e9a6b92d903c028de80ec29529f88ec6bbf41944572d302bb0c5ab759c1bf70e16920f227552bbe041f975b6f3f70718be77744b2517f5c48f66b3bf439709396aba5c926c6b814457574aa69e6ba3d213ab3c5f3f26f300a33dac395de19dc3a503e7e13dfff66b99f8f392f2108578f066a724826872ab1016ae8c4dfdd6f90637770bad62ad5f2c813e526aed48f3934b92dcabbe929eefb2a64d9c5577dfcfbfebf596e2949abe6c0d89818af23e65dd8691818090545df910b351065708c2b0abdcd10c662f10b3e13da435fdd35e87accbfffcb678e1a2bde7b4cc569a67158280706d17e12d73c6c9c1e6ea085a3d5b883b759d52c9583c4a992634e9e2c4a1cbe1fa69bb1bff9c853bab0df4d00712aa10c3201d12788568e5d79d807bdd6f2238593b85a7bba88079682372a5045ea5771d4a7af0c6b8871511f02864c9788f925e4fae4a008c610ace3199d2b3a87df1930e2f584fc36771e3291783e63b00ae1706f8f5b02941a66c851c3ad627c4a7ff093b99b963732d003c10b90f61d616212cbf76d307d4b7680edb5770f46ef40cc4efc045759d3404158f1d3b8865b646950916d3cf30e583eb30c0bec8cbc85a6cf5b7ad068cccaee6883cf6b50d4ff6296ea1891f00603e8f5d5ee7509dff530dca53e1ba8f84a37c9cd980fbf899f7f887a37f53d496d40447a472f1f3b3ac29ec7c03c72ea068116a3e5cc12f3233f316eab3bd745d0853ca57d5572fe4369706f0917f476ab161c9bfbd0066927a2ff3833aca1a912185c7edc9856203db0c46e8d5db4290097e858f862bd6168e9e6befc3ce809b10002f053296a9932228719da0fdb7ae6bccdc47c7e1f4899b9358da9316c51b0eea76199ac73a3cc9fc28a35c9b277eb973e87c40226f4928ea52f15a8e708e567a492ab56acfcdb211e2f2894b10766a08e965b15cb51fc81cff37097f5b64e1edd463714cba6ba3fc461e4aa0f019ecdfe0d47888003f26afa671ad8f35e32213bb003b497a98d7ed64fdd1d88507ab2a006c99bac0833c3bdcfd74934bef86f45e8372c99c987eec995f25bcb5cb276326962672e5f2da63128aff497685e8224e8d6fe9bcc9a810377ead40314ee5461bc55fd7160c34904debd186e9da87fd36a7e0db55fa43d9a4309e685f89555bdb8ec25b64a8e23308e127c46dfd27c10b44e07cf27b1fddd97e83d8ab336620413fa77dcd35b066cc4df4e75520ce26ac1dfbe3ef90240b8b783b91477d82f9f6145d1252695260414460883c58f56612d8512b5e980994824f5981b34d3cc10a17fdb0b3d20987cb27f8f6790ff6ad6bec3579bab83877c6de83416b22972e7f0ef7c0fa4df9c36d28ccce516b30d347f59f30c733f15de89ae895064232324bfce797fb80ae84ca20888081312d7bd4d3ed8b2bd35bb5f6c19689b3c0033f5e48ec19b3d91de93bc1dbd9a15feb112ca7fc45b1b1916bdeb521eeed810d81d85d77a196df14bf42b034abe2b251a0efdb6a0a8c08d0a4d36b7f3f22b305c069400af9fbe8ec1989a721c9c4976b522efa6f4364b84e22a2c160f21ff0c2375ba276673adde031e083802b2b0947319023c6f0b629aff4f4a853fa30a1191a30f4cd0d1d958673c0f26b0d03ba2d37b2f232059b4b7e40892a3156463a52b494bd3a14c518b1b549de9af4a61085e54d483ba733d05dbd1191808ef6028fa48e570dbe52fa2d1b0736817be2b980a70b8735f37378b37fdc633a2333e0842c4867f92689847a2e31822d171e4c848a52439b66a55f8f11679f25f7fec595d11816effb64c3bfc25b5362039902a4518967dd140afc013a4e8f59d83aa0eeec8664fc92cb34c5a19012c6edabcaa83550fe2465826d7506a32c8b0065cd458b4aa43841d8630be8035ec5ec6863a32bbca24f46f8ef454f62c3af0e64a99a4321978dfb104d817c89dd394c9637ca6f2a906d4a4c74d99bc5cebf7d11193f42165f2fd6e260339e21518293d14ad4f813eac780065305ac2d08eb60dfa2e6eada4dae64e3ca41f6967b05a0a20496f0276ed4b55aec9739005021fe6936f13df8a6ab3f6358b352cb7864a3f4045c425e368786ce37aef3944bd4d84bcbb833af4a0072da8423d743d78061db4421d7ad083fe035fa10e2de8410f3a86f80f15f44105dd6ccf1bd4b8461b3d6df84683be4d8c94ec4f1ad04c296e2fbbc43d3776957902d1350d015e1a0279f7831ea8a0147a68833af47bf0071db4421d5ad083fe075ea11f2ae8830aba0cf10e25e8410fba6ccf37d6688d1a3ddbf04604fd4d14294512f53c141dfe1314ed9fb924e37672111d15a2578ce39583e9191759ff5c1b6af374500d18781eaa028c9e0eaa01038f437524683f73908700dd898a8e135af1b51f65349e7aa03cf3a82633a8853ab406ea38c224ec9cdfabd2a3952ca4749420da3feafeca7bad38416bb1eb72bb718d8f869c357ab4485e00b49687e81f2d77eed161b94199c9a89729a816f460a187ed9f5deee11b62d6523d9ada286bb28d9f92f33f57d09044a3b6f663e8fef07d0c2759434912aa06f38c68a11fdd9fa1d6fffb4a70d986e90c2ae78b620a94220ecae6ab4ceea7a23aaa010ed8a551b04df8b1e914a9331804cb210c2989add54838ecbf735d7ac39bcebd5ee7e28fb48476394734ad1b36b43c3ff1b0915a02cfb2ff16c0cef27a39b4b00695f683e0256f3d8e2b493771fe9bd6df97ca59c1957481a324d072d8dbd61c6e1f4e4248bd67811fbafc3ad98d6394044eec0ca1e8c3a93f30a1ab25227bace7b7e48cdf8e077e4ba9c2593fe3f544873654064b803b0f79a8d97a038334e714508ec99f760f43dff0d36a531ae5a1982c3bdab360c4713df5ef08b36a258e9ec51da1c0a57fec5109f00257700d4a30df0e19afe570a5a0dd41b1cede76b5c07515bd7888aa06181e41bb101bb7df2b5c8f976eac260183f8a1a1e35d70bafd3c2932d8fc524db2ac502407cdf1511dce2672df32ec0594ecbeb97eda70cd55235e5268a487a85e30a563ccb1c18ccdae759ea41410776d1c4e4f21f5e2363e4da2cbf6ceb4ae8f5d4c3439fe8f8a3f2125fafbadb261b85b2cdc192e1b18a21b0fb6de5872cb3d2efe3399df7d1f62a40abba159b9fafdbcc64e359f72f9da67084c1c42b53b43c51fa063d72a97499d3f9c1e0272e0471de2be375dc8c0d91c1013e99b9e05ca5d28f24d25107426b904cfa86343b46003c5281948ec4f26340a431cb44c65bcb6d532ed45870d29e728a06eb62a08327c914b332cd42e534650512969f4e59002b46bd4644149cacafc3d2ae2c10e0e6db7af6a52d754fb772fc01dd4d99d747f1308c2ab917328ae1fee5332a381ba73fdcd5be3e8490d497136f326f415c8b6bf27d8c62f7fd9711011fdc18c57152b8022be869c1a3d3b245a46c340db7bef7d756fd136e5245443f9d79e8025626920591aa6619de30f02271a2218908345864bb64a15b9935e2f423c3c7976920a8530a4c1a97c6c0359f2f19766cecea9db5cb4520aeff9a853184ee4a158a62b99ec12e3244d30160779662c2a2862666868c362b472996115b281e07d8603f390b78619b80d044d1a693aad5ceadb903f6e8c078ffdc473b4474a06a09b41f8f552c194f7f5cb802f06971eddc3520abe9d2f75e3988e3d03a6afcdfcfceea18c51664b37661603d1f615b2ecdafba0f987246158b694b024ff54a8258ce2136499606cbc06dc478fdf8bf6d1096c0aa56e3fe5b906d19a6335c769e9a047d968788e1973bb1185db4d235d581527918eab13ec3bdd530ca43ce6d7434daf9b36d34ef7f6397ae4c39374d63248e64f8b963037ec7f6d341361ab46bc861ac08dad45bc2cc6c38241573a47f745e039947153746f40fae46c0cb7fc2a83464548cf0675d899d7d284d296830046d1e2fa765d45e7409ec3a6ebcf471152198df6a7ddd7015be6e8aae27f0f532b3975cf27d44a8bff68ff710331ed480675d0abd1868dbfa3d3cb7294e63ddbb9f547dcf45f4ecb5ac608a542ab721acc6d87944421da0d57fcfab45293d23a0f63904a79e976602b5f00119204daf2286ff9cba161c6b12beb8bc0b8d18839b909eddb9cd1d29280f270e8cb21b1cbd04e061b573a432c1f89260aa0b1817fbb82e67c7eee8d2834dfaecf150297bef25c7ca3382028b2bf05367755e2882068d5cc98e9ce92be2cef2ac7f0cc5b7ae8cc3b1d8b74fb81a33e0aa01909525975ea76119f1f02e8b1b2b1515de81188cc6f175cf26d62a34afda8b5616f20df52ca1bf6730b6c91e7477b0f377cab4ddf40e20579ad37ebb024a0af17878c89d9f483dd3002977f45f2c4bb2dccc382c9641791e826c5d6b0ebcb5aa33aaa4437bd7fd7b329c9f3a42c5b97b7c1850d5196d289a096a4324862a05fec814324daa89453b984ae5adac3cbf432b0b812a5669fc48580be63edbcea83160b8058545efc959156f463da638daefb653d1a47a2216b4d326cac2f38de4c345ea76346f41c02662787f8713190f27694b66e00493c07a3ce508f8ca1283807c334f31b22bdbf3977097a30be947c98367270f19016881555b780d02fd6abb2e779e8c8bfdf06efc700bffdb0b9daea44319e5e6cb2ab1c882382033fe654bb90b3c5d38f739f4417e2942a6a2759e104de472b3dd9bd1ff0476d6dd2f0299eff6fd96702df7768d1702fbf254791be5d082d2b14d1c5901f927a6cbc3eb07efb68a82de1de7e09807cad3ee39fe9a89f36ccea290ab67dd1c568c35e2ed8a019de8723b8abac12a918781334eba5abe05536e4dfc1f9176b93d109a941c24c1fc6681482b1f7286e7258cfd139c9764e779f9cdfdaeb3daa45364bb4f7a4b6a90f5ee950b31141e01a47f9e0f8ed82144bbdd6de8edb83b41b2b2342baff7cf452c988d607c449a3d7d8ea3c7470d1a743f7f3617c3dfecf36dc82065652811e6b670de006c6a3d901fd73f00069509e4f468335faca9c98193f7108fb979c8dce97541400e8e723e4c4c34330ec6cc9f3f6dde371336eb598292c0deccf8331e25581d5b553e8c08116feba82a7a89206af918f624645da3a9bd07d122bb1f15f6f0a7e3a5e0488d45fee67eb6fa3c96a88a83a6d7e9f200f7b7c1d83a7f86bc126497598d076eddcded51a5b29cecff4571d24f99fb5515e14c68c78e18e14b00e28a4f1c903e1e19e89e454b19337e1dfdc1fa3b150fb240d4634437a7fa4cbe9ec87b60f3d258014ba4182b2a8f1dca351a5d0661fcb9fa31b208bc060816e4cf4f18d7854ad0ad4cddcb5bf404d2e36b1cd50ec3fd48b8f35fa29d6d7b34bd27f8d076195ca069505d16b7eada2cc36d25ed4f33ece9fa2e4f482ce2631e55d85495cb8744c41632efd9463a687f942138dd577683165e2a0c51dbfa3008951ecb127bdb138aa31cb17d7ed816ba0b8afe6e391b29355e5efd632c9ca4ad760b0b3181f3ec95d4dca46a00aac6c44fdfea341cbd120de2c96652310511a82601f771761489092e248511b33295a5beb41e8ee68a7cbf4a7a9d7fb5635a334e716ea1eb128720c4f512dfc7e4e3eac6202bcc675791b9387b229e36fd96ead5ae9d408f9b85e3e1e619265e8221b2f9952a51310994c92ce278057696dc36071e064856f62d2b15b381e97d26215293f0979ccbf4d55cd262d4fae12e5a32ff98be96b9261e66c9dd8e78f1b6eb1ddb2c32edf6c224e2c2c77f50f135cca7f7c750374d187151fce02abbac8544c467b3b4114bf7dd2934dbce70081263005161fe8f53a28db9d8e6e14eda7e5b41e76f984ad38d2803becf56e7451218a9ff5074566b31faabe2893f4b2b2e299ffd2d617b9c1a0efffaef828840e8728b52f0ab4fd33f4aee92d4e0a5ee426a836c8759fe123c918a5eff3ae217b60ec162cfd2ffe55af2d4614c7248a3f4e7e83519a9ba66b910d08ecf9082e0a4f7ddc30db133339887f2044d648d58e99ae53c36fa4ddb36203636f574162a2f78616edc10cf7bb32d4d3d71a8a9dacac4d01b278afa8ead2df387b6b5936d85781dbd73f9f32197c351ae7b6ec61c35aa53392de31c42c155b0e139184a99c42564c00b796c9dd70ae8eb79e376d08e5f67739e6adf3d5c0af3e0fb777d6c9a1b337096ad0b52190897f1ee10be252120edf1d821bcb13282d46473b81f9ec509be82ff243987fd17c93d038b5a17608fcd875eda8fb0fd471896b918fb1a6b8beeaade32d6dfc1cc5e08f95453746e05dc93238b51e73a785d0393ff2e8c78925016db2425424d8c1122755464b90baf976ab6459e0022747d5d134b29915a485786c9d5b929d61ab0cf283027c042f0057d8b75c97c81607e1d24e5212ecc451e62bbf556a2b3a540842a4b87b8011d5d57a7b2e97c8e0680200588bbd8faaf348d0e33dc71698ac463b7f0150c2048b3d28d8f5825fca182989336b78d0c9d89569d307e0a1fe080d0c4e65318563acf5991c665927f9662b75f4df453d8248da84c79f1dc3784862638cafb14dd282750ae23089974d30534f1a8677274648f37346e1d0c27687da331dcdb703f8ceb9c96687fb1b7a4003e54093f5281e560e26c43a6de9b2a62b89ae0a6b5266e04b187e57ada100515822c6729075d3010a9b52052835b964b0ae44c9d21d6c64abe51a52041c8d4487686a4303b317ecf7adb22543c3a3623912ec21455632701009c0fe618c19a917d37d197f31fb93f227aaa2f3e4aee978ba645e297c30b77a0c00c6f4735d2937c83caebc0df23f98251cb120b07d6544d2b477fe061d54dbc5e70bc0746ed900f383e1d5403c84bd712e1bed50d8a7b88f553d2843121598dcbf105f217e23925a6fd62a9f7e195303754812383294834efef7b7b76b8e7e0eb33d8041d73da6672dc163c420a15a11404020dfe0088075dfe3b838dab84d45c414c441bc7ee092d2c37dd265aaf4750512e3150fefa5e8ca4cbcc76f620ae462a199000aabcdf2c53956c34444ab9accb167d3b47e78173a2be9c15cf247c47b3fe843e176ca73be165166d192ffbdb9229ca43dbcb38b82ca987cbc255f746a517c165aa1199e996118a11ef059247000170cdd3d21d79a075b0dcead8f6b95559765ad05eeaef7d92bcb21bfd5a92e02f1ea3b1409728528e325cc9b7e11d2c00983b41a248af4f621c6f8cb866d7c5c68fdff9dca497be26f9d43a0187cf1962d3b7535b0201ac36a5450b6c4444c884db645066320a8712407660e0169df622056fc242b99816b66214214014fcb38535d76269c2e13d9a71d922ca2ecacf7ab1df6c08db5135f3cd5547b4235d43c92c3ac93b4296e9d22589d54006b416095d6f81273e2bb81a7b760b7bacb2111384ab5ad3eba044a87e1e53f6d4623e926f3f12de2702d113677f49fa9434f00c87017844a99758bcde74de899f25209499da903318f0d060a8a040a1f871b142d6a3de6a0d25f225e9cbc2c9ab9226af32ad4761fca23c76bb5304ffb740b21a1e4e5174eb3080f21a598909e4bf25a8cde03865e1afd258151cb61496e91d5ec4818e1107775cf804868c10c6d2a0f264d0636cc49b007c2dcad491caf4e268fabc2df0a87527cd412157e904a3f168887aba8a31cdfa0d40c8056a1cc5a5e8440891e12a7876c42bb6d89d2d62f43f6992dda3789f217f6c012b5ee95932a88d9610f195f5eb594443868cc696a00a768ee801f10ac419e203015600a5d9f9475097724649e2e3904e845a04708f31cb06a8c00d92625966c8f87282b4af788537312f20a13ba4d5c7294c23742bf87c9acce78f17e18f259a1b07779249f8f7c9b5080199771078a9f7c6656ce1d146234a5658c30d5e140e22f40adaf7f4590a20443b6dfdf516d9570df0ae39c903f6883a0b65bdc07c4102ea90fe218d98981861b0b1eaf9c2ef35b2693b0020b130ca9de08ab2498571010e93d992856fde107689aca698cbe3cbd616a7e5194aa73aa055d58e2a4e3b3cdb63341b567818b08cf4886cafdd0f5f8c8f68059ec3d32b7cb03c96b8b87c362af3cda0ba785701eec71ce8fe0752e33eaeb778f49e27e74febf2ef62cb18edd5a0e6c7cdd9ed15868906b4a026450c4c443327c6380fc7add56c083701ca80ed5214a1104a85903cd77c5f2967d89936d93b420de513297504410258629130d05387d7235aee7a38f9fe92a7fa656f2f4c5eb1fd3e9e599b84eff03e55a2f0026f02d64b72769d116912002768b79f87e78c9676730a5fd93426de7357ba8089d50c9741b2a59fb31f2ba8ad35fcfb920e0623c434ccc17212a65978c1ff6afd41f164e8891dda39f09aa03102c66f86bfbbc1cd6ce1fdd6acf20216ff459c04443ead3ecd31070f83c9b9f8294d62d10d5d340a0537c7367b03d0c53ccee9f4e38bace629354abd6b04e45208cd252f72d5cdcd8b0d97894ec3cf80dca7f0c1302b3b8463d4d79813f36cd5e5f2c1f9209b74b309b2a8bf326c76c33223cb98eb2319d6f309142f9ce757fc332ef8d0c616a2dba00839aa32ffa8192f4039335765d3f8a7cf7bf3f2d79265237060ae7ccc4a4fc13f7eb6558101a35985557cbacd2071a987c42ff286accc24c8ce39513da111361fb9c0e02a59ca73b2c489e62ede6a02ab14cb705099d133ee5b1c21827517adcb3a76724c238d66a9588f9c4e4956c517b194075411c76e5d396b1595a137b04695022303a213d5240d2ed8fc80e9d8306bea3f521344be5b71229d794349f0c7711cf8d5e0a53c3e88bfe15f30b71c8ab7d6bebba05a51dae9780ad7fb018b26aeb58939dd08e692d6bc2c818c2949c729f913803dee9697fa897dc71ce008f768efb87b1d98cde60fb03a75845d0d5dc614eb427f26c11b91558694d7c7199608e27dc2c824f3c9428f11e4d02172c22a82de07ef6925e09076a338111e195a6b40d5180db9ca760ef99f62da2c8eb8fdbc1987ff914bf4540dc66a70cb3d3c23b8696a8b0f7c44e19484c2c13e8bbbd8c88c5919404b5626ab288ec84d74c8e5d7f5377dba80a5eafe078d620097243f740f38a4df43a0e0d9ed3106acd4cb3c737af9775ac9daa50ae75b0e20814573e85926b9e250da7259e7aa10cb4430e62d14f442a057ca53c92d4f3011368ea5df078aa834bc414138b044c1acdd37445361b392e633f04ee69663b1bf1c935a6f0b2b8f8c4cb3f3b4e9e9fa802a9c72c2ebae54b60b8591cbf2072161e5a935fe44177e8c1bedb69c12de7ab258830d84e712988bb5f091b53ffa04eb32171d84e289bd007d197a089c5603c936311224d7c98ac21cacc57fc6638b319130cf9d2329f9917a360a46b872a7211ecc4d3cac77d95311ef3a653c8fbdf8ef187878c93684640e285aefabcf74cd61ed9893ec50215d8373fc7f311c3231fe2d78f4e11155e3382035205c1a50d5010d1881d38fd872f2c74cb30459c783fb439e3e56b99e3547ec60286829e4d7e8d5f86f8124d9182975aaa9bdacfb0a691ead298ac71b7958856386d080885aab164726643be69a759f8d496d60d4bfe9c0d40f5975066c9e3b682f241d717e48ad1379b0887f25de68ff310cc6f9ebe7badd92c3d5c6e4e49833be88186f5be8f214e410d661f66107b29f1dfea1d0e2d1a05c9465f1db5ebc996de9bb391558e165b3ab78c1a4008c5c63dee7bda2ef7c1ad3d4f39edcfe016b66a935c57f91aa05ab9920086ebab1c8e4548780dcef78ef797acab33d1603751394117e86fd8a2cdd369d193dea88152680cdef42fb5a2483714e4a112f47a1b8c4cab9e7c08c870527f0d402c7241d2c0d3ed9602350834f1e151d16d402b863e8bc60643e6aae001727610c22c1a6aee0110851ff4cc9875939c01a27be9d6dd95610c29607ce901918b52863c48bbc54cb74bd05da661babab4000fd0dce768ea7c062eb20624206508960620c1b3ecb035e87669a2a8b15cf06ece5d49bf88197ffecc1dadb129c823aca5d3aa92ee2d3857c5ad80ac568e38e0528d0ad361bea100b7d30113e115254b6d34ec1d74f09aba6146f7aeb7ae5cad210604e4eef493af4ca5bc1dd8cd422ecc9436212a61d19a687f912b6a700e2d78fbc0d58b30b78b5e21fb6a7382bf5165089f8dec33354e36db56c9c629439ff95c6db8e8e0fc48832ee6986fdf18305636202bbe4e0b645174020a72699895a15797b2c9b04f68198b0ec67755ce077107ccfa8537d1e132d9e863ab71fd75e7a7a38ed0db49f0284ba0548d535359d8976d166e55420f45c1e4ada586f4120dc1bf19c0aeeee526f4207e9137ffcdcd7763ccf5859da30150da2616f2c524c5b8b5d4b41d6857a504a5ee1b8a695cf302ccfd3ae8ebc0d2388546f1b404c5098f4b5c05383ecd5560d7bc7d428f4da2e8c1ef0882f8c6dc38d43da0fa5c78420fa55616a9ff9934e0f1a84a4a9689224363e1613a7f73acfea53c41cd025c537b50ef77a29f11f63d25c48c91c6ef1ecee571acef1de45e05e9991cbd5ebdc52d639b617c4640a990ebff0d4af225c902c690ac015bc0296800daa25323d2e5f851ba9f8500b7e82d5a195cac6f11ec746e38a04934bd745c76232e71abf37d04f77dc52164702e029fcf07b00c0823d156ed9284af6d9f00475af39901a14fbb823e5b407fa5c5df8fccda18dcce74f8f9e38ed31d9128f765aee0a0359b54cfbfb8e7684c47049b099369aec9b978bee0086a60f1d2a55a7a4a8b44c44c8b431387579b16692e925417a5aeb91d3d364c36caba1a40acd300a88afd4114da38044b9aa75a7e1b5505c2ea7d23ebdcbbbb6c42d25b9d582619637ddb4c4aba0c365dd62b7e5939a1278f9e350d8a2b4409e0a7fd506600e0e53f76bc7dabb9b416cd57b3290afa9cdde37baf4c5699847105085a17de9035216a6d0f5b4db7f9dc9785ad137379390cac6f508b885425b5297523d56bb78e8a79c1a2b7952dc56fc12ff019b42b88140e33c95cce44a1a9da13b8cb51e0a69e8a266502b35331f27a062281ba7c4a618386e14cdc91e3047885bb070463eb1b46e24a6705ce5d45f4a3dbfa2f013a7efafdd7eb1d948c6cbcdf8fe4fe368377e1a14fc35e586387824533ea453435febc5fcfdc4f6a21864e28e674a5f128f809d95efc4176c730105e04cdddb49da1844cbbad31d0a3183fc8ce79c0c8d036efda089411a5a5602076f5ec8018d9b2764e4df4c4bd04bba52adc743827eb40f1f7a840d447a1bff8a4c615b881a88c93b2f97ec6d79d446212dfea468b0b577c645135d9103660723bafc7001b7ab7977d3340adb4a638ab2731452205471f81b57812fed1ec4dd9f7d84af7a2a8073bc28176470e28a3e43dcae49dd758b7805848d618d01fd509ea28332f0d4a37d8bc5c18f5ed801248e994ea9d2950cdba75960a267f6229a41ca0d614fd881faa80b1348b58b465d3d15810b970682c43fbc37f4561c11cf5e8f761b66b463d23823be6daccada57cf62696ea2e2be6f20250c51fb075cea142150256e8c433144a2f72cd8ac9e4c628bffa7c91824260d12fbc91da1d138d961d729af31973e37297ea147803674c0eb90e87ecd231a3d31003004bffaea8a4ea88de418aadab1168c42b0f25b9357fe320a30736e138593fe629bc29ddeb985ff93a124b5fe7bb7e0fecc7afeb278aa685f12682c901267650790c88cc0ef6c677f278cf2c8881c2e4a93394ce38ffce146afa9d8e7f944476a9328a0b8bd780823080d4a421804dc06c19804c808c4b393388b0ca2fce96f9e9fc8206577ae6cd82e5ad64e0d195371d351617416a2aad6e4a49d3c2cb21d5d01718e3bb25072e485dab0cc20bc7aa554f3a30d1556bb5a05ebdf168ac1f705a605c9513a85a7dae028ee4900ac235c746af527222aaee381a90f024bd5a0d6d3e06a257ad6db6827432ba74c0b5cca1a66119e365befad94a6df23aaff5695ad54a53ef4bac55227f2f9a4a4bed9350da751e39895540e7ed16aca2bd452ae4b920948f36786f62f5f89529c1aa2b56e40b12af1971c3162f667380ff5f36591a13c08403d18bb56393c8d97e910ebf82110bd6b59fcae92c2a9256ed8769808f75cf792a75dc1f8b425dd950266ba06a9aeeda8f589edd84f88a2cd6a01c197b62ba5f9c6075093f2e175a51edf56dc3bc96ec23e501bb03a9f0db7d842a75b2a1a607ebd7cc9328ac1e1e1b71c735733388b12b27d277401689c4d641adbf1787428e60158b5ab71698e3cb386a61edf25d6c60af2fec3acaf004b06d2a00565943aa9fe256bb2047406760ab14632aace205c212c2292172f32b76bfd481be1c6cae84187f8b62d576a871fdab0859941b70d07cea4f9ec0331f4240ad91d7f6979c395b387ce8ebd948ca2ccd876d57a12d1976a68a8274749a9d0d6e8c44a0466b51f2c9dc8720fef7ca2dc9a3740ad013ad0fb1fb6bfad7b8caa0c973ebab88683436a70fad7357c9c7405e4532d3ba6805329f529f4cad5ea7c34cd4167168002121ea8afdebd7344817cd66106cc896ffc50de4d7ca125cff8c79adfd51660b14e996902587b31bd9beb90d898498ad7c7bcaa3eeb2d78240c1629d964f8ac2a8376e763964f39ee0baa4af460f52abc57854479329f65696663a49c25f63d49f4928258d042981e262a4f3a586d0e46bc899ca37d771732de2597f8e27b68af492ae5ed284d04acabf572732d08efaae154c03fbfa097a3e076ed865ec7021225968430e626464af86905680f9cd8de335ad24a66d10089c969213e0eccd56c64e3d620f7628fa6e3b066b6f7fc6550c3d25c038059f208a5bd5814699c09a6c3ace24035016d1e427b3fb5b0d09244ea30c4779a948c4ea47c1d988b66ae21f37ed6f37905706d871ee5ef46c14ae6774e15c6fd198bc4a1929866bbc15276a987fe4169cace3406d609be3655acd13ffe95bf256f7ee6b4516e9eb9ea914e7143941e9ef569f704441e4830a3e4154fcdba4c2c10e15a78028d310909f456a6e6492f91f2fd28549115c06843771a7dc9aaa4f1e416536f6e50c084fcdb51bf73eeda6f5107c8fcba3eac0c7416333ef04bc4966541a8c5cad61469e3a38e5938d15bce683b2ed594689b14c2d3e4d3249d79b6f7f6fc41628404b5918491e29ffab657d1ff80100b14997a56f1f24d3842e2c3c2c06e54700c86679682000f2c0ba15ac79be3d645084caae7408953e297793187989bcee6afa764daca3872e820e10bc78751addf376a54853d55236068ba77a4fcb6926dad08645ed3dd6413af1c2c96f4eb1ad5a7176733301bfebb1369be249c02721bd9247d0b2b1105fb1f9e4b966ef34a016a57a179dcc9c74b6ac32163fbd9bac0ea09f4ee03e711efdeb53232076b3496fbc400db90cf26a04c29017fd5c854dea7872a9ce3047722535c7d652892e375e512e25821988fa45222a5f140943d1c3216a44f6fcb9c445d29ec263b7ff37c44b3454756188cc1335e92c82c63835b837d4d6244c22408c30da5f2858af9886c20817daa100f15cbaa98370a1880acc29d9a57b9a1841eb97c0a5473ea9d31dd8dfa2a8d163749fa226db2085d7bd0ee35e366e01254724d51077225c642b48ede1f839b89919c3653da4b2711ba633a407815a451c2ddad8249f41a78755635403a3af4c48237a730f79ed007084f875a16b36e480c22d7594ad94b110c860ed236114915f0a71f5ab52eec78f53886d6954964229aeb93db8bbdb8d77cd6872e7d21890183a48db4b8313d25e1458e354ba8469cf2579357b5e6ed0a82eb0921bcc8425747b3fd89090eee22023b20086ede1ad0e07e665c1645593ff285b3eb70659db448d793fd56cd52cc8a19c90862d94d7b7c21432d74674c5a7c356c4d54b03e2e211be07a26de4cf0773c052d3b02f97a6fb6da4e002b6091204b8636afd7be726ff342c1a4c126ae6be54904e4332f7a3e34a80dfbebf6665701cab558f93067b1dd653a7a11f2935ca6eaca8e4ac0990c98a60c2c8270cde35cf54797a9983f94f830964d0addadda386be03577d9948bd6d95a7d63a0d16c161aee70b74fc0dd6b21ae7d6578b300b6ce7a97f6c17068db0dda88eb92dea01d5bd8ad7314d774f561ac9abc628d40d31b70fcc38c4da756217389035527fafa3e99600ee7c99481adaab28f1e2987815d08646315851625707f9250d8145435031a486694c85abb009cf863cff97e881c7f628b02f051e49b4ea89e5280c2fd87304515e5495e89633590318043664bd412babdb4ec5486eff84ae3971115f20f2b8de6763a85c873ddf3d38c405467722c341fa3a9fa9edd8d06304ce0ea245f318bfd2d4215f3ce18e350ce28cc3db7b70921c4e33ec12a311cae3f8c8baf360edb9d47242b688b66ec667ebba6a750e495ef04e677f5d8c5bc55acbf1ec47f48e0e9dbbf9eb35ff8e4e6397e19a2a1dc57a68c24fdc6a4a40fec5195ecd431f6a31591e82cb8b268b4234798e055b8361639eef1cc766415d6486f5a9213dd42d68092eebeaced0bc0b82154ce78cbf7626b3027c9bb233473e8769ce269e697b7ce85de93434ab0e00309dd835409554b8a2a5a8145b74e0513b01f7d19ec7573ba0fea14858b84387258ca72d4819bc9b57f8302aa7ac1712b9d0cb7918f3c0ecd4dd752e7dffaa20a990f00807a32e3a3a1c09cb5bd17101f753b0026657b9f05ec879c519b766aecdda8bde285770f5d285f4e8ad82cb932361fbcd3937eb862038796e1c4aa9d243070caacd110beb759a83bc6b7b8b0f639713c2132daf18471638a662f94a3e93aac0a4a71ba9c744d750d7be4eb663650c56e80a1a6080271cae81e46927af419e340a088c2820503592397302e80dd61c1fce888c886880c6d28bdadadfe86d228946dd78338d66f48f3f3b9f7fc991b6ac0089d89b30f5ad9517ece26a4f3d8e8385879d41211bd62394035dcb7336bb96ada44bca76176c52b485a17399a1a69dae17366158c5b81d46cc6deac01437b7497b8b2b18a057ea67743e53e2a4fd55974660507ca6f0a1905ad011bf715d0b1e3561b0a7eca41e5710814732f71b48e18c400d21fa93b0ff69963158068619b2edd3a4f67637b885d9d140249441659f0c308a7f150a6720a0f3abc85f6f62751d87603a3e13d5708689ad584fb080299da7da6cd1177eb024d15b92bf2a80851d6fa6faf334c298dee84e927fbb816f33b5ec2cb93753f793a4ac63cd2a4e58d5c276960add7e3a59ec85911c2757f40e642904a192e2ba9fca8581c4daa2e2df8ef5bcc307438fd6484a3c0823f6630c9ab0f609a36f3de8c13908d00695f113c12d9612fd8edd4c26ded72511d60d9958a19b863461264afcab594a54b24eb6de59ae64ad5ebcfc6e0d5531174beabfa292f98d001b4aefe7664c9e03f9f0ab270d888436558592d1d73cda4ea4d47e4cf485619d377bd458f464f405a8e416287534c868b046f64ba831e5d92eba6502a194145ddaa2d051ef3538c9243a05d585a187033c9d94f3fa2d2da1fdb67e99b2cf56e425ebec831dcd49abf7036258c88fda1ebbd432580771e69fe64470c2f0864776128a1e63c9be1ac2490fb2e37b3919284a2fce388c0d6a9b884c7338d036d08fdf8c7cd44689c38a67e664d2631dec3496e792137edd76b8038d173d5b2e91c9faae2719197d60d4f5bafec375fa7fb969a2ebb4e3165313c2cd3f40c1cddecabf44f0178244baa2657c3942fac38a0fa90eb35fbaad755c91e21765b3d02729b8ab4b58f619220b7466311b69602508ea6aa53a2e3f4ae5dcc7a76ccccbac514af40c3d8952665907e61b51c0a9c6acbf76d2b37e7c7c05c4bce2b70f460b317cffbfbe15e3328dc53013a3fe2707fe39636cf9f75daaf0a987cccc0d247c8ee1e870081a6deafb2d2bffb8f2480845672e05dc923d2c780232d8cdd6e2a95c0450a7286c73247a0cfb7fe4c2dee3da32b940f63c99a371f51337a216ce63e0a6a7dfc7f37a15ac608146749e3999640aab0e81e150fabccd14b26a1266cbeea0b7c01bd0e706b6981bc32ca6ae54023c158c5fa51b115a95958d9d643d88b0388f39af1df55e2dad0edaed1b33db24e60364650594b3f3abb249308ab3a6414b25f336d012ed8fe15af8819adc5063b3eac79e63f7aa508b53837fd2664837a4d31e3b746ad9c86e6fbeda1fa3a82ce0e453b91cec06f8eadacabeb9f1e7fffc68911881dbbf7e2996720f6d17eb8437a7e8201a64ffc9f2b150ba310aede19e206a3e4d958bb3d6c09fdfbbd3abab21262d761a6c95d5086f2f8d8c4d51135d28d96df6573ce887f171ffabf4a72b9eadef3ff15af839b60d51d067ba76464816edcbecacf2f136c9d1b3136574756071771355c530ad139d3857830c131329efb151513d8433c32f9ae3698e65d5d897532fa9ab25297169dd54fbcc2534cda7ccdd135433216ce1453607b361fa26aa21b489a0349feaeabef31e08d38afb4a52850a413d5bf0cd3c2ff1399cec28c537fb565bd6b812f35545039b66070cd7e28f36fa7d5a8439bfb403210258de6c8b07045c1b66b95ed7f503a598191108d9dcdba3089bf6b54fc3396a603c2a8494809da46d558cada91876f846c9e61c7df2288acdc6a4d25279ca84a4b3f7f9f8d867942d87fd8f10815114c12c2382389ed507cd87edf04cb9a0faae56e57b42d6d27f55fc1d271bc3357296d900e5a600949f348abd7bed28b886b933a637d5841aa9d8e7c8508432c8c80ee739c14538c011a487b4935c863c89a18b431b4fa2bfd727a6e8d4307e50d888f3b6d5e6d5920431f215e4279bd123210a2c8a445c1f0912428c18a2dd4430ca78413fa425d34e2ec26d353f8e526edb692e8730f380eb1ce74c2c0f3a429228e7ebcbf48df355efcd37e9458cc1657bdd30735db19044676b7f30fea6158fa11c68945275c2b964592668655f6bf0992e25c7ac55d83c1aff6dccc07ff78ee1fdb8dd6b02f318cad96eb2a0dd9a77f5c08bf24be06ed05b57c51f96855ed8f9a1b32937a0dd23fa9cd23f6e0035398414bef5c0c4282ddbd5fcb7aec0d956f2c4205de5afd9e8c1efbae50c72d542a6ed79c493604eeaef667c301d2e76ad514a65684a4d99c43c9c0711d95744a8f5d11a0f11f4cd675551cdca55fbb4a9b68a904c62bb5c66729390306a3243089a097f8ebfd29f2b7c6dcdff3252a5f540c4faa0fba7bccc56121b502506eb3d621224249dbc2642a88bf836c715bf9f601413c95f2e30cbc18415049f7848fde1ca557719f46cc596495f91f71a4483c76c470729ede72f524f70fdc4a7828feb9a572c77d9e02bebf1ddeab16a183c74faf3688734ff05cb6d3753028de2878c728fca642d84e1dd04192a570d6df6c5ae0718cf77ad82f0d071f49eb4fe7206909e5320d8ca0b6c0ec83bd0d473bc5cb555a3f6200db54a5cc280202a5972552e255678b31d3eeb9e13895b06726c10f36b217677015bd2b05333c5f2dbfd5a60677329ea92ae7aeaae2ae307cee3b377247c6722b1dd692b12dcf32dea678ed9cd14a03e90890cbd255db5900f023cccb23096852f97fcac8a089438cfd14bddc95f965e30d5e84da47ee77d8266d633d72e76e21a5a5592a767daf321b720f7e49b26b3205a958ee49900a1ef148cda890c8730353db45d75ba03f647ae183fe1d13934354bb28175c440a247ba6f9c35e526f3b6995b8164db3351da63bcee9c02c08e22066f4af43b62fe6533b400c96c4a3b62dfe901fca624f65479be2be809289c4624a6eabe524c0c820bf9711012fb2f99afe1bda30f7a888758484c750369ddf24754095101091f221d24f0e6429bf7d2332fa3f2fc992b6b901617b1927304a15871e84856122af7f52f4c3e1bdb1541a72351569719511753bd10d9a2942d071833fbee5f50d926752dec7a2dc424ac6fc402460d0df10283bc057bb7faf85713ef3deb45cc2ad31f4e8b333e0aa02ec1cff6d2baf2f1212d41c29f3c05d79d0159005f29913b5d4c89508088e20239a8e9b5413b7f5ba86af98507235c409086dcaa5bc247c9ca201f62d5df640d6641caa4fab06f11fb9b652105209fed575cf6451f16ad4911c582ff390609137af9ee74e87b056ba173c9103ba8700201ce7d993a2249d08df4b3007819144334e52efa398c5b20ec2ae599a1d024f81c429d6b4859c87d82ead095f193aea7eedf476993e1c1638465c9ef97212f2dda4dadb56c95058ea466670c174a69a1d6113d202fff8d31cc3b0226e081c8b8c1653a9dc96d15394bb9b727ddf0f530144c63e94f16e9bfcd82135244ba8836fa4d46426f1ee6a09852bd9013d969d9ab5d802c523e46b46a5198739ecccbb921be9962c0503970ac939d7bd0edef5f9088e2a835d4a6da5a090058a8db3d161fae3590ee57d6b566a349e5013a6b90733e15ba69adabe1336777ca2b3bb3ba321f09dcb5b83bdc60086e40419a6849b500d129e32d5a655ca195893244ccd664a32bb2e97734bc3846f1f8ca82b4c38cf29e37964a827d5c09774a726f5c49d1ab0e6e332e24b1ab593aae331eecb27e70e2fddfbf9451a59332597705e399cb0985aea0906759d93bce6c7c1080ebe11a708311a68661d05dc049145e4238650bd7a380ff02576dbb96b80b06f95cb905a15f1bee624ac1a96fffe8786af33d1613746d2dd366e36ddbaadf184ce75a24e8188b712a33da9635b98bd8dd46ff2fd7a358975372fb32b50768588481cd85202fdb4ddb76de8871e75eb21fa5cdb615e3300c6068226f8e921a276ccfcd3a63ed69cc0fdc1cc87571e1b8cd88b258b1cae322ca92e4b2c73208ab0b23b9b5b6d7c4480d873746daa462190e997e987ed3192d04d644bc376f483597cf5d6d4d547f2418d8546de3c752ce0357008e46f85df7ff16e16d31f25e159b9e29a35e3915cbfeb0132dd5232e129d2e9a5da1f8719575d07af584eb2eaa872618d4759d6694f291f5c5ac1c10d3b7d57389020b921879f7648bdcd424da99a7e8fc15b8f15bf462484cb846325dcf8686eef4ea13e2e6c2be02770ddc016e338254b510602232b0580e966d819e30c3ce2f2df09858713c0b9e16cb0c95e27c2fa21d0bf5b5e3fce0c4156d36f55d0bce5532dbdea37a4013b4d95938a79bb69fed82c7d8d9f17fd76725744779bb5484d8cea9e9e2761a0f57a5cb1c78079311d6f8bb1abdc54bacbffc4eb8471b96d0b8a6e042d0815dff1cf77cdae33ceb8f10512f70c51390a1e04f1362f6d9bea9705972d1a4f414273b2bc20d912f5aa28a1d7139a8acb859c997f6f70009b6e131bcdd5b54feb3ff5bd9026b9928eaab567063dcdb2b4ca603c091e37a59ba83a3ed758d56a08eab023a583118800e03cd579016423847079d6618d82dcb27f40544140ad77d185828b2c93cfdd5d3a10edba6325f07458e1b40ce1aa2fbb5d1655f916f3961be6b4035ae066bdeff8e85e41c30726c39e6d20e3988aa629913eaf62f68e940a89b2a22cc050175ecd4c71e2030d43fcfb4833f28eac6c9d92ccbcbaf4a04b485b1d293899b9c217ef3fc5a847d49fb877c50faaf378aca8fbe692606ea56db9d53eb2f679600d78e9be10663d80da06622a71269bc1cb525407c178f9c5d75b561c87f6d09734576a21b9772ef6ebc1cb5cd60c7648bf1b5d7b1c6faadb508b1eec8c9efdac68c0ada8f044f3135531cd94a6b480c765e324adefacb991ec818a2514457f4a9efbd7f1a48ad0d6bfc05174cdb25d0a4112750fc3fe469207e81f455e0b06d40c38f63982b8301fd85727a6254a02a49e6ef1538326b9921b59f5d4970e0817c857e2509cac3b5044b8a5ca209463461b1a48e6ecb2d159125a9be0739f52d0ac73d33db97174d6b49b12cac644b320b8004a97d0c286e49275b6e58d1f1c05bd22908f932874e66b0b4db79d2cde84987ba08d73307cc737cb8e94930ece33d10db3c0da72739f53e73d793ac29f6242bed49a6bd42b906c8a0a824d13f2468b1ff1240d35f26ef35e421538fc3947707ff65340989a9f2f0fe61740db8f24cba855982930613730edeb2833c6840f8f479fee185121959413f2722fa77808fdd08380bfd4a3bde3f7e90fda45f27a05ebb0d6c01aa67f6f62b397ff97241f4227da7eb9648e3a037e71d822002578523f1192e788225c804c4b8b340859e44fcfee741909fa89c0cce922615a202498743f7c3ea7360104b42875b10220f661c95258aa361e5061c795341c750a3abcc43dccd099a4f8e14279cc8432e04242265ca587fc10f3c9843d10e72608f34d2cd89f2194f3014ec48b4e3907160a73488bc05681f65311885a9dfff77d18c85ff70b9e9ea6ff8bf1ac9a61cf40186f0fe3b7924187669cb146698572b159afe453e63923f682bc5f94fee3f662d028f25ecde62c9fe4bb3d59cfeb99577fb84dfa641bfa5f619624a8da03929bda4670889e7337bc6806cf0182be1d6c3f0d9ae7c5b102842106d79e1a2a2c00dcf628b66d96f11607b19d5da1f6b95fb972983c45022612e0232d108d6697ee0f17f169ec7c17f07e5d1521f4413f967b42b96fe8ca83a45a94b54c8ccfdab77fec0825f348ccf579377c7d50352b5fb052be38ec8328178bf903b272080e4e00ac55349902443fb0c9e38c7be6378a034af6b22a23cabb4370cb20887f675c21dea3c123fdbb4d141426b5b7cb566b8e6b8bdeb9320a62a2e03dc1874db8388d71bdc85277e9dd220f20ac067d6afe137fd8effc7a31bf2ad3d5ad5c2d2b74df84ebfd9585c1fea13e4857671ca1466d0db62220aed73fe1857daa29980ec522b7fa860ae8c61fde742fa66e989adecd89f7a7e819180c55b260089cbac9eb072605ecb77e556e0c850c88b78c21f4284d0cd47d2a774d863888efe19fe1575d3cfbd6774fbf735b96d2d57654af2022925226e81f78fd2a13eca5efb673c5ab658455c6bed3f6d11796263ca19da1b6d01c57f6cb03f6af99b9be83fd0cfd5f3f347204dbbee04580f1d776421e8d04b8705138c6152101a9156de3631f3e05fd4d3e262f5d460b387a5a9e31a8d3ed9f1b8b4fd805a9efe6413a56664c9d3ac82d94bd3e2b996d3e3ce0173f112d22c7b38753155551773c5546030572f6f6cb6026bf0e662338289ddd6bc50e5b43a98e3999fcba522d6eb09b97f9d12ce40242b5d068fcefa792d8a83c2505c081aeaf122390c0306d1d640e28b53a98c88fbe577df43bb66fc6a7fd1ae3ff61c7a6cedfa808e69b5dc37608afec43102788be13289ff6bc35b745e8b06a6b99aeeb9bc6f35365fea6ed4e2a6aace7e6696a96b0d040d3acfd5be7525fd625e80f344db3957cd5c849e7ed770057de4df5b4ff36761ff78c0f7c0da5d774f6a8447058f8e093a2d198aabfd63ba10cc4476076f96723fb3e202d452dc3efd015d702010d21402c54970b5d55a2058b97af2d655bc57867bf8b9431b98652fbb67aeb639a35c133b2e118bb813ef2a24c1a3d90ddba16dd2f08a4fb519cd91d3013b53e8aabdc2ace5e900da2b604d50986de56e88c64eb6824ecd14cb3662b1193f27ef93860790151565e5425bda90089402d6aaac70692966aae80f77e71ba22395c70ce2ab5783eb87ec5b320834d57c71adaa2079176ade8bc27567e6447c3ba558d72b14f806dfc88981aac1c81b41aad9eba71c1e27106038fe4e13041e1133f82586cfe589af2358ad13421008af3e6f856e84d84d6d912059ae3c3c707114b1262ccc9d7afe6b3e088bca4d65647f5d566e62f0a7a08c38a357c2fe4eecd4822226f8daf157e97c9c223dde3e7850b37bf25497791213986669226df6ddb599bba9381645f8cc49ba2f8599de79e112b709829355e680572a7f4ae4e580eacecdc4a1b5eda6523247fb764a65eed1d2c976bdf79ad63953ef5c4cfc8960c18b74fd485f6a166a4d58362feaeabf753e83d33e4d9374cfac54967a15f3a98dacd2481c747500d6c6cb7b9b9a1090589a10aca54bf6376455d3c15cb933c28b0f12f78ee4814620689f80e078f369e444a9a662bc18d41069de2a1687caf133860a47aec10585af4179e7229287d044d4219236e4c2089a2a483f13022bc3c7230c700526f97859dcd60df1c786a8708a18686df824abcef17a827de670a459d4a964a9a5cdf3b09cad0b9072aa42af77fc7685e03edb9e89ff33bbf06d94b0c2336822a38bd910998844102c11cae683f832b48f0cfcd30f55e07095f140251904eb7ca0fc18d0e60771c1ef94f375003109f373a2eee6da84a736e7e01f38bd26d2a3a18c6a355dc2a8b2ec556643753587bf6345ab1c4d0f222161f3b5bc4fe890bf69ffb52ae17a92620ba9ce9e427b6f139717da5f7f1e4b28b114d9c197b6368fc11485d57efddc76a364b54106dd388f6b40d62c5cee289355a1e4d7c4d5427cbfd4ae8fa8ee06c357db5916cab962a3600dd77789c2839358154acb20d219d6dfca3d1428116044cdc29a8d570192472e948db87fe424da1e0631dda758b3339732fdfb335fcb0c613f3c436726ccf99bce9d2bc8bc6f9987411beebe4cc58d29c37ed2e5b18f638d9666fe5a63ff2c6556e11bbffcb322ee4f2152d24eef8f97a3cbf3824011d6f6f70bac718f9bff6e1bf16ae0d4fbf555517dec383beda7f8718b22867a17390e8ea02d8f9e56411d615557c33d1a52b8a4f14c933eb025112608803270921e56bdf5d5f15591113814daf10d65f9e61e739d338009042b6ebd517ce7a0529ed5260deb8db39618d796776eb186e703ac98c1d02258f985c107b1469b53b5b7931b70c361a3783ad5e9aca8f2a050be6c4d12196d7ea94c42038173ef4ad3ca61992f72830aaf18a1e1566910d3224a37340dda944331cab6fd0d529d184d66ebae41ffd8e8adf9cacbde1cdf4252a49e9ba3fed54ea70ec6214de06ef6c29ae51a6272f3057c78889a05c15631c6f547dedeb75785cd97aa011a385f3214177170ff57428702219d399223bb616ae86f21e196b8ce5aef0a153ec06c554eb82e9fac7e14ed5882bcca36c6fa315c63a40eafb44d8ed214a9e98706ba72650f24df8cc5f664cf0ef70c2b657d91fd46357e3c0f501aeba3f9bf18c016ef60b46c6d7e2ebfb13cdacc2db3d730dbc50eea2cd0ceb0a9daf8862f36d764293da5704057dc888bb9ce102a7ba85e7a827ad762f6a627c8153595e2c1472d46936454d87d4d33cd15f37e4268fc855a940da8dea9e2e060546b5cae25646de0952aea67479da85f77aba1c06b1d5aa79846d4dce3a80b9de95c170e309a7f69040bcf8a2590e383610acbd132459779862d0873a9087a327cd847daa60f27c82ed3b03a3c225760badfa630dcf4ece43cb5242ec3aabb8c9ff7a5bf2fe359bdb58b5317272f6c6121a52e4bb7f90b7c00e270a2de9c8c4338833bd58eb62b5a6e8c724385a18cfcd1dbc28a4f86cdba18798fd708f36b79a9e3766b35b030fcde4b84fa0f0b83e4d2a28460289fa2115c2547db3652ae0dfde90822d202112c6c00218959916e60e5bec90508aef11fb8edbf6712ca1f56996bb9ef533b1341ccf399d2e8ed669a10703d1082a9cfd22311a2080754cd4fee44fdf5bb86dba37a3980ce5b0ab75db2876f5eb060cb6d262d33998190d7dd494d38d5457299606d65231bec064c9303cad752925b30903a4e072dba2dd1c8143fdfb2cda47885c2ef475ff23e0bf105bc262687d568365ec12fe42f59be751dc2524fcd0feee3fb73cb5d9f85013bf37c03c5093883cb17afc39ac5ffd9078f81e5ccba0909697befbde5de32a52465e9081e09e4083f374dd3b45a674f05f00af329fdaeb79f41381f761326b86d13e468ab7ebdf55d4aab86b36852911754c2ce241c5dd8241a285d993483305dae4c9a81923b8ba4f00ef73f5d0522328c9c507cd309e9eae7cf04d39da12909cb3f4a32291004283015183497ec702507980b5b9826b02a2ce56826195161b159d3a87f822b165b40a6d3cbaf63358fd6f3a3794ebfa4b258d61a2de12f01d90b12605d0f57d03e133b7ffa2a1ab682a03ef543ecab4224aab79f52bd7d24a947d1501a615dcabad9775ead055bfef6250c05b63c6c05f11ef543beb72112fbdfa3c2ef91a0de7b09939f01b6f7d06a9a36414d02ac9b85a30ad708ab881672cdaaa136eb6fde09fcd1aefe5a348d8aa651510f0bcdc362dd6e5e120196ece1d3886524753847063baeeeca2e6dacf2420ca43347ffea6f14efb6d50a74b58d13a76a037d5a9bd7bb6a92cd639d755dfbd5ea554f64158a777b554844b68b0bc7bf5b38025ded53a16cd72b49d1cb4dec44b4c0a89fc5fa9c9e122a17960fad7a554a2935d943ca9b672db50f2ac9ed88d9cf85a99405078480a53ea881880273a10613b0545825fb9175030756f52b209eaea941d97c10ea6bc2564d02fbae55315338d6f0146ea1d64b35dfcdac55df7e12980a543dea85a81ef5b5864256ab0f42bdea93c0aab366bf26142df812278d47bd0d59ed12516195ca02a7a05e5e21a830f5346fdfe6ab08a979d4a3506f5f08eaad10d4db84413e614d68847a9b7ad40f493d8ae6edd364a0bc13041d34f2ba5df73456ec582fea0b8b492c369779cbacde876e8fbee4f644bd2ad63ca89063ed1a6a9e55c8432625aceaed07d97ccdab542ad5ab4217544f2344b27a1a6fc320fba8b75c5814c842bbfa576252547aa85dad85b17bc4a22aaca2030a9b7ad6cff8519261cd08e792763d4182c4260caa2264c6dba71122b1d686311bba80645a61fd8c70092b64220640ff8c22206684765a79827a541854659c827ad4a3421790cca59ff1369c38b8d8f94433c22afc354a40d484d3c7934906bc352fbf06d89a3056c5f4554cffe3fd8e98fd46d69513d6d5ef67a394fe09f2d24f721d1cbfc8e5a7a08f1e4e9de432a8813e6d0279cc171f58811c213bf2518fe925ee215015530db1e8812ee4bdd4ae7e15d864dad59f1a733d5857af8a0b3df662f4d0518771feedfd47f7b19b09fcdb6ffe2a819d5f6e370f8f611a7ac8e1c8646e8bcde3432cee25298ef54a197228e7c63bab2ee3314f7fc13da6bfc9f0e0919373e58d77c7bf632f792d1406982630094494d31d9fc6ed68f3e0f418de11c363bc6303efc82d2ec0725b15f31daae8a52d636470c72e33ba900ff9122762d51855ac5f25c43b6e823959fb4c68302ccecf00b28abbf109dacbb06580faf44fa817d8be821b1f85e19d2decef32b0bfacd002dfca2b84145c817f05ede5a5e0c89f85d93bbf668124976ba83d83a31bc0339e32f4b01498e649856ea57ba8bff04e26c5adc0bc1598776cb097a44e7b112c36ab84586cd5902ac6620e2155ec76ea53616b40bafa8358eaa19928fdf490d4691c67a6a45a0c8b8d02d95ab93da697f8073bf2d19201f83d4c52c4f597973f20fc1e3216167ba8879aa7977ac97ba98f545d928f6fc850c30a9a79777777777777e6b4bb9b524ae937edeefa2d5aeb8469da4f9816da76bdf6b57bc0577777d3eea6ddddfd51a712a79b7412a7061576be9c430dc69650e19dc9d30a75167867944997df08bbbbff903dfcd971b02d20d9ae24302993b46ff94f58386159f6139685b65d9fbdd6f3e2312a34cf1c922efe5af947f3d4da1ecea14c876e8afca82d1694cf969f82dfaef90ece2ad3def9414e44fcfcf7fab0123843eacc9752aec04a983be79c72ced93c23e7666661d557c833640ff964280aca2c5db9a58cd2d1955bcad8703532b75f7e95a2c7b0a19bfa2c937a277e76abbab038a6d899d43281af943d2890499d7e3006cb4a9cc30895f2e1a9f3ad639601393664c729d469b3c6b9e8aa542a950f53c0833367cc5caa74c72456420948a5cbe10a357f7af99950c35fe99c737a8e143bca2d63207165a625960322ea19a572af1881996078a725bd0825a860b97d74a7d74985775a7665b98d7952b96206c94c120c94dc4e4ec0ca2d6348b972cb0dbeb0e0ca2d65c8a0eca7bacdabf909d6bc4af53a606bfe8e4f02d301b6e6e77c125847c46233941617c1620bd1f139ef60101d9ff394b118f1152fc357988a9520ec388784a8dcbef96e7dd7f8b6c90183aa702844e7778441afd7f9a0105ec77735c016108fdd1a1d74f20352e33ff8aecbf11d38af8d0f0ed816fa8e1648726fe0781c8ff3e24bde713d082bd0cf9e82dccf1aef6688aebfcbcc10521965ee38633058bafd930c21ef997bfeec4b40f6b4b5519fdee2c2d696130e19211421f4f7c071fb9b2f808d0c01f3da781ff6707e071cd0f5cfbe03cc674aa9b7b6cfc2b1d5a24f64c7e73c91115e84ec391c5bdb8ecff9213bc29c70c808210b975f8490859bbdbc4097dfc7edffc7bc369e0b7d44b1344c808df770051bcf852bb8de8dbc2e0d8dbcae8d50b288e367381a795dd7df3c08e0e857fcfe0c5003882b74167184efc17b38e2849245f19d45f100376e7cf737c0d185db31380f30ba3816b9b49befcf37c21149e67520770798ef210d470fbe85aef1a3b3c839c21cefc1f3cd91231ce7edc2b1f300e4e7ee3d78feb07bfafd707742f8a62d76448db9fd2d03f8cf1084cfc216901a3fbfc6cf70e42ece239d917273ae443253e6ae80f31f7cc71f10eed6f80fde8725ce7ff001d839c835fe03a432c6dc0f1e07688a36decb91f38de386cbbb3173841b0e5728de0847d68b0cc24f7034f2ba1ffc3f7503d4f80ffe8370c47977902711227c7340117e0738c24fb0bf56909c17e187e4bc08e1901d3f4228db41be205ff9b58288f0393f44841084cf0941f82120fc8e100420385fe37d181c6768e47571c20ffee67dbab317bcf90747a07bf3f4fbf120fb7e3a78dd0bcc5b0388bf706b8453e9069c4993cbfcc2624f2a34425638c390110b6719950bec38a9cc89e3073bcea157d83f572f5734341c59a16c97874343d5b25834cd49cd49493f1cfae800254c67fcbbea57552b37d5281bce32edeabf39b2232719a95f4df2cee953e89b734e21b8b093b9b0122781c0e62cd9f9322895dcfe2bf86a57bbdcbe9a271c7b5e2cba0a9542995e5287fff56229f15f8d5e055a26ccebe3b7824c584fbb50b76ba794d24cf32618b6c0b5dbbc0956c06f0bf4765de7d1ebd7e3eb7dde044d9e111c6f9efa9eacb516e5a1be96b429af6542df2975bae9fd1870e6c92190bf3a14dbd5a9522815b7a95455cb5454e59dcacc4c77f7e9ee2ea59452b2bc7bce39a594337420b168a62389adf783f7b028df260acb1f143bdf260a3baf4d1456def95d5397d29daeba339596ea764f800c57b0a1d4a8cdba7394521a9ebcdbf3e4475dfe013a93f77dde35cdce9db3a1b439aaa6a43655a3bbbbd378f293b2060f763e6804e747be0d254ae3bd0d2577772977e48feeee3fbb199ebbbb1cdddd5daa9eb0518583a37825d778a2f3b1a97ba06471669a6f3c659b562cca2ff3c56d81afb32897ae77d8d37db92df0adb582b0712074dcddb4cc9b9ff77d3ff21d1c3fcfc1913a3866d73d29fd64bdc92995277967e5752c2f051c9a2b47b0a3d9cdda636a04e7f677acefa7bfbb47fff636a2e1519b7ac383d16d81af8d2ff2b9868d694c72d9132346a3bfcea3d120eb7a6057e4bf53dd9c661ad7993ccb49392ff73ecc6dbf6df57db8f6a66559c66559d69e83f3527713c83070c2eca94d1dc8b009f33ee93dbbaebb8b8da3e9eebaf6e2a95a5edc393f599b49d1838e707fd7759fc0bddbbb3ba9bb938ebaaebb8bee39fb04ddb26777775d7be9eebaf6e275475ee7e566872959b6cdf529454aa5531af2043ac8badfa9f3be6e54fefb344f8a6f7f403a8c173bbc302f95b3a5b78976f34b80e4e27a61f71b37a5e84f33907dc771a1dcbe1fbef36af4d2acbb8633495b9061dd099cb01438a5cb1f05faa3267033d4cdbe73d6a1defd810ceba6f78113660299bbf45312b86da7e4364998e4bad56c1c0db752a148d7792acf93b2478fd4ed3f7284c5569706a71429f701a1b7bffb50a69327650f7be53bebca23ed02412952101c708437b6371b410880b7ed49c94ad9e3f3ee9462545d0a44a4ecc1090162110a3b693c297b6cb54c37c393b28726cf5c3e22332079bee77016b58b77e68f3386cb2f8b5a6368d05b650f0accdbffa34314a48eaa4b9227aff3ce94227d39bb6b75cfeb75e3f195cdf3fd3785d52e1b5da6e915db865073125dfe113c7244a5c57e536c74b11d7852f6b069d160d9988c3aa67185e6831454b42f0de6035b16123c309b608d593bbacccc9799f9c60bdb572229e1c46519312f71997164f0917559092c1d5cb9e505646ebd12490924a8bc7ff559c44a120677cef98f93f3621e368ac5623bc0e6f7e470279114e89ccfc21577be146368eea0bd9c544660651651b73997a2dc39c5cc32770a752f9928cabd2343b993af60995f036ce43adf4826c932312b57e6985974e733953bbfeffb8cbecfc7f77d25c872bff7ef6b0cf7bbf2c4fdbeff2b55dcef4526f7fb571253dcef7b72b89f09bedc4f8926f77beffbf9fe4bd7751d0962b723c197db8d20cbed9efb7ebaf7d8b6badbcddddeb70d092a7743c28bbb8db9dbd7ef67fbd6adaf7d3f551341d1d53efb7eb49b9b397d69834bdfbf1f9a5dcfe2440ef0fab82d8f067047c965c71de5973e6a125a92314ea19d3b4e1b9ee809e5361129b8dd5fc7b85d0412b78bb0c1ed17ab94e0f6bfaa6871fb7b7c62b8fd3f8cc072fb818c18e3f613010111b74100c6ed67e1c810312b600c89e02285e005b7918e30e2f673d197eb4af79ba718b06c7da2ebd5b2a676e5338f6c30464398e4082860fe72196988125c26e286cb2f3fb0c5e517e1fb61560d1ec042cc1435a8220218bae0418c90098c30223a02c62ca408fb139793b8fc253033f38eef877dbca18205a63bcaa30e585181076af0c00e1e90c19428a840a18203a40f5871fd5fdf8f2b91852684550f295fa4f870f9757c3fcc8ae910e2072f427c71fd75ac4e19637c70fd73be1faf3a7a88122547d5e1c34d1023a85166b8fe38ac545222c1087e90d20412103b3884a0870e5441c31208607e7485737d3fee2284c05cf4e57210335c7e59b481cb2f7e3ffc310042053baeff8defc7ab95609094a86c7c3f6d4978e2f2572080e0800a2e3f08566a89423577703c00775c0d800341fcb0c5e5ff8045e444943ac3efa727bf3cc1e507bf1fae642041e172fdbdfe40c3f57fab63c4fc2760aefb5728315c7f0fbe1fb757eeb8f2e104599e2cb9fe1d7c3fee04c7871aae7f0d110a0a78ee782380d1c791e422bf3821e33a0f517828430402221b5cff574f0f22b8fe3e3f806ebe1fd7f164cc696243074d86b8feac7702c3f5179d1471fd5bdf8fbf7492b8d1a188ebee3a94b9feac1d94aebfcdf7e3af449214600cd1a0c5031dd0220a143cb1c20a182c60d2b5784d193b61f3c7d50d931aece440c4e567e9f0c2e5a791450b4ef660cdd5cd8c25645c7e16132bb8f8612d45354b7af8e10ebc602bb1584c871af79beb38d771ee98739dc7751f2f47c94586f94292a1e38e53689c36cc1d86b25c7ff7af1a00e2fa5b96064670fd3f86eb2f9ee07a0d33b8fe3d1b90e2fafbd830e5faffb80186eb0f44e4862cd7bf880fd79194d4e0fab3706409120e5ecc25abefc7894868020429d9433579482e434a68c872f953ac0e5e304318a8efc75960cc70e5bafd7e9cd5c494c105a7efc7e519289e7c7fc7558fcbfc1649861b2effc752228c947704c5e5ef9ae8c209bd70f937678289adb2daa41892b8fc9a0e0b78dc276ef51864b8fe19cbfb47f30ca1440a3eb1796cdb50b2a3f5c13b634f0f8fc3a6cb1c775c857199915e40bafc16061f2ebfb3a02083a6959062415c7eaed3d62e5140b9fe25f08ee9faff10e2465877b9236fdfb87c668ccbd3e74a4ba749a55e1522099120517d8aa268683e152241927ad54bd8fc7e5af487a45ef543562d1a06517dea87a8c2d4109af0434b432c8b1035c1748b489dfe1c494ed89e2d567e045428c2a2fc4903a43ad245ba53a954f86a57d38040768ced5f1af3f25fb4ac1c6258f9fdf3053248466430992d2fb8c2db92c5109213423bc8ec7994b5d62c913128ca10737bfa0cd704451939e8a1b9a466f45b223571e6d2dc3601a5f44fe04bfb0332c2e5f17bc81ea7cf9e650ffb59f62e7ba0defb917eafddec339405f964fabc0e943d585287fe58df5f05d61468427d8d82b366405ed61b43a5408e567e67b9b048507648f6e8715958923d2a922a4250ff3d12fbde8fd628887def8758d61054d8d3ae7e95fd940dabf073924d50d8153ecb0292e7dbc5924533d955f5deb466c69c41c392ffdfe363998d7c686a56aa148ac6e4237b48ca3a37e8a4949de4b6c249333040cc3c58eb69177777e59fd41f03544aa72f0f7dd832386f7367833a35d93567edee927e0d1e9c7653a636aaa093f6d8a8424a71c3a2856ecf2538d26efa9995524a5aa93b0f072f15e55833c7fe91bebb3b0547a6e018d2e6dadd7fccdcddbda98d2f35936d7778dbddfbc86855ab5acda877a95a46dd8956b59a51ef52b58cba9339994061fa7eea9b4e9956bf6a1975275acfd6967e82611d7179de194aa9e3aeb513ad6a35a3dea56a197527d433ad52cfba38a59e39a91a75275ad56a972e5573a255ad66d4bb542da3eee407e776aa711cd76d454444d5f3beaa64650ec5666c93728e9963a41792877be6c2698506973d0dd8a0232c265993c974ca64b76d9bd2e9fba97ffa4ea03deaf4e64efda5c9036636d8baf366b352ceadb52857b2a2c252112b6dbf1ffa564b35100fae3eeee492c38a959c974b9230b9cc10587b2b49403f68a6c445e939cd4a120b5d1a8e938ca4542aa5e29ee6f95e1a3dd8f3527d3ffeaaef04bebdf22a77ea8e7ff72fa95df956f24ca5764912a695190227bdd839c9b0c85fc1f9058b2c06bb7892305f2043e067a1cbaf4d32bc903afc745a3181de0ee718a9c35fa3899ddfaddc7d49651a5f9ceb408f5dfe2c1e40d775610b28ebdc97644bddfb30e7daf7f80065148ce77723b6eab1ab9fc503e0be37e9dacb7e159ad6dadddd739bb32d775dd823823e20485f3063937d49ad561a59a2269adc79a79726d5cb52a714823a05136e2a2571bb77de9160b678f1a24ad7e2e507119cc7794700977ec5e2044188701d0c0a3e903ea46082cb0e5cbeb8fd6296dbffeac2451432b86cbbb9e5c01ae97ce1125effda82105cbffe5afd401443d7fdb31a050a263aa068e2bad529ea582c1623824deecb9933b4cb2738f9d5f03ba54c2426162d5fb21c5d71fd7b7c7e3cd1c5f52327d75f05168aae1f55b9be8588eb4f6471fd092daeff242ada924405ca7589847483eb48494e5c7f5f028cb559b4e470f979069fb571f9e5175e0d3c42333c2cfba187d5973f250917a877fe055877be277970d8e5edf2f7704af65dabc09439b6b4245beaee1ea08ccae045a19048f8b96f900b91f4532295d36255897b1fce946259c6695cdb7a21543f7ba1edb5e74020229a935162a662143173ff3c00db75f63770fb8e3d065bfc9b3ff743fab7900b915411b27d3f12eefdc5281386397fbe207bd0dfde04470c51430e377b397db899d8aeec297843ea64bf655bd8023a795e0d2c7628e3c21650c771df620e6c71d80ac271db7b5bb83d12d9262e2cf7db7ff4bb49279ba6989ebf2113660a8392c0503fbd5ab4bd0f7733164bb2b548cc4ca6b0670381daa5820f917665cf812cb42bfb0f3c755b5816ca5eb4e166bc85ad20dbfbb7ea77ddf777614b7b5316b66a28647bff2df47efb21dd9b4224dc77bf71614bfb8f7bd30fd9fe0b917cbfbd29445245c8f7db233179d96bd99bb2f7b64cc26033e6d3c3222d6ae6b89881f59f53548dba70a5d1735cd7be827fd6ce711ce75f5fca706c8176fe59f69d7716a47b9f962dfaf5d2e6160de5ada0b4ef58486bfddd699ce5dfa1502105fb6660cb04d467cf2dfbfd345f03b682f4d37ccb8643fa695a43bee6190552609e51c0ea6784b25d353493257564eaa53da1b2a22ef31fbd28b6459fafbcf597740f4a58c7203b6cbbeef5b910097ffd8dbf3e927ead6a1ab76ddacf2f08c3b4df381f967569c992f802e2ba06c9d8512e1589802fcd0f4848687bed85b8afdf01759f8112463950857695019f5856689760fd3503e3651115b4fa30eee64f07aa7f648b2d2df976b1dae5ffea1f600f10cbc6c08e722902459662931d07ed3baf830d58be41b2ef1f42bf3258dba5699aa64932b34ac78024307f322e51f630ddf92632b2474fdd6103107a4edcf9d60576e37e7e4fff7822cdd3fd64895d583497c2881a8f593a89413a2d49296552f3582b75e6fb708e242b5b40dcb7866c6f93e38495608bdd62f34831645e3e7e0839c93077ced92c4f49bc337a272ebce35bee7c66e6ef7db8c57675f6f4e50724545f134a12f397df0224ccdf874f5c64d3e912e6609298d8ae79523a25b188238b953f9e926ef3264627715a40f4bb36083eb19379bed0491cfe1fdccb17c2bdfc7e19065569b971616b4b02ab1c3333739725c0dc0e8d96b88c76c7eaa36acfb8328f259848c1cdc0d8d695484c60f9818928da0b2cea4a242698183121745950c61dd384abb67a7603643a8ccdb4d0c26e57645ea6a85d6c2645455a620925d4e707332192626d84e062603f275a1138743dd87a25d21234d025ac7625d212b1323e25fc4041496933aab6b28c5e5ac6a9eaaa7ab655edaa7a2c82b10116189b33592c99418017a8d03841cd1117963db304139a1c2ce560cc0c7ba6ca92d50c546ace48d180ca4c12cd192729180eaeacce281942c55466ca38b1305c4999d9a2c909658512d3186b464b0e9f9593990ff8e01140a88cc90c0f39743080f9aa990dd8c0c5883c33376c630c7533a83158e1b458a2955161656cac29986449558b127a26a695c0063f53940dd1b031c0d0fafae1866a1966664c6d32345f3d4c0019886add5ecc5bca18baccfcbf85b512208658bbe4ad72a2f83d49d3b88c7abf5e3c7a7cd02b91b01455237106a7da017db92c76f5aeb8d9a609fbdad7d108ceed095f3c546053543427d6e6ca2d2e50d229d9eeca2d2e88216b815591d9c2b0fe8536c6725a6844d82c4c56c69a4e5045606992a858ecaa035c0aba201aa20b1a6a18f61b838362bf187431581a30f48a651955315655656bc29a8018c36e4e382a2c65c2010d6444b65eb9858b17b2261645a69ab1d9955bc4c0e08eb54eb1dc955bc42cd5c0bad1095888da256fb68138ecf22a8bfd4a2425a2a8f588eeeeb6ddf3c4d2344a29a56cb1db3f1c7777af9b764f2fdc66adac4de332f76e94524ae9757777378bed99f8d4deddedee3edddd7ab359af7df61e7606fa777b4b546b06ddb4777a384114ea4eee93bb89d24cdc50345ce5c54e9a699ecc64cdc2b187ba56bd4df670dc44b9ee8e005cf9a0e479fdcbe6bada799ad376dade97d1d7c96fbce912063b9f693b6db72846491edb2efe2ab2ad5fc354d2354a32659af9c77e0fbb93d47982f2b365263529dd59d75d3eab524a69d5a4952ef9eebe4329a5549b9488c9ed34d3eac675de673a59544ae5ad56df0f952fe5cbb72bad48045430c2420b4782666cf592e665cdcb192f5b2f7fca1a2f3b78e9c1cb7f09be94345efebce27af9d3887770bcfc79c43b395ee6bcfcc98577745eea7819c2cb1b2f5f2fff070a40bcc37a2982f022bcfcf12f8f117600f0025f8ae2df006fbe480820eb26023ac0154b051dd01ac9013d167280b5051ce20df1884bbc21de00e1e7db001d049c507e007a2cb24d589fe021451b9b1fe04b8a3c5008c19c1710088a394276409674d93c00409188fd7025451b1b15aed3f020b452b431d201e8b15003ac346c5ab8b17949c3e6480b9452b4a1e1d786463806b154df4f871d68612320453ae652a21ab0484a0c141a300529d2477d407c09242245fa368c141ee00fe9a23f02a804c4c1501101ec912efa3b40295da60fc81515c8e3fb80d89e97c703051498d3bd802c287239426ee36c221113f8db07de48917eedd76e22d081ab8ca5c2eda72b231be879d7f65ab8f548364360540d3a89c352ae2c9a4bccb55acb62fd8be2f8eab9de4fa32ea9fb71163517a954ac95715eb9754ce5984b67e92c9d594a98db3f4d2f640a4724647ce138c540b9ed85e39412660ab940861403451e5d8a85a670fba74869775814e55457ab9bc93c5ebb34a09b855c476bed6ac655b0ebe96171fadce64f12b2f0d27f51b067084b7f7c9dc0a3e7fb09a79c5e2a40c24c216b489f39033b852cf0a062bff7fef4f6ab0841bde94d6150955610d39f5e88897e902914627a1b0699425468fad1c224ecf4bd698857fbcc99ebd33c1e8eacad9f33bd7da1d3a366e5efa77e57ebbc403ac08186a3eb1f7680030d5aae7fd76559969d42efebf749603e5d5f1e9804f681a6ff5e88e9bfe7fe3bbd1706a1b8201bca369daa17b6b820d9402d2e6c05f9de7b21df7b4155b85088f7a630c8fba0fadf2781f9b4b3c830fe1e58069b5f029ce34e4bd5dceb806e7d0e1cedf47e03dfc8eb6a5e28790c694fdf5f5cf6465e7433fab81f7b7e645da0cbfdfceafdf438fa9502413ebc69f75ec10fb82eccf00bdb67ce7018cea11adef963367ff407dc2d09163193df7f76ccb3636fa670b3a0065be05bc1cc636099e538dbcf378233bdbbd514ee96e4ca9dbf65147097610a09b295862d054858125812d88d2eac2082491885d1506cd774ef691e6a3467232d95b9537e716766c5873bcaa322a416b460e82e8d6928ba73c6b0a378272cd64f3a493f4884620323180d5bf3fb0b1332b02009c60e3a2c81b9e03581d1974137586034acc22fdf853907f064cb174b482859db65c9498d2155710377c5aeae445a82618c2cc86459ace94aa42cb488624f572265510516b6bb12298b249f127ea0a0a4c5ea062759c690f9410b983143445accb0c50a25455330d942e5e868a904373c61f4aa82081b8eae54f164c9134b55dca0e4a80c156670d052860a316a4032a202063520c5a858aa618b11156086908aa8501aca52440513391c11bd78f4f8e820879a99d1a2868e072d62340596b3c10602ab819115613f2d341258d4149b17cb21716424518fac76845683942b2dd041062d9081e660cb5219472c85a165494c16463e25fc40414c9628ba6e73ffc9ddcdccccccddddccccccccedee1a8b99997a6566e6aedcddcccccc939999697898dcbeb1d3ac669ca639ed6ed6d3eefecc471aedc8bad18d73ae6bcb7a1f16bde33ceea6ded43dee9bddd49bfac799a4b78f121813d771261fa93367f040439d27cd28cf4ccb786a4126cc65d89342960199338745f97ebb93d3c780d39f6e659f4623f2a8c5435247febc347726b1d095610557f6c4d39a4c3ca589d2300803300f2bd2c924650d1e3e666f25a5ec971d2661bbbba39452ca9ae3b4f28c56932ea5a4610e09ec7c6943897677f728c3f4f0d497527677b79653b38d76dba842c9f6b7c44e1b4a9ec7392598ddf2546b18840198890658a4bf117974250db3eed6b8865b53ca227712dd5833bb698fe1a555865addb8ce0b47161198dbe194ec7d9866ba3f75cfc111bcfe5dd6e32948db5d7bedeedf3481a77ea734a7736ae38beda72c164fdf8f6ceb757f2c4a9d18ecfc946ab5a2a9a95247fec93426b92d212798f6dc1de79da28541188065a16da2890f4d7c68d24427d624a9939a2479497a2f491f48aab09a2727e79d6e725a1d31b96b9c8baedf2ea3f1eab6a2dbb671aaac79523079d35df95bf3f0c15772329d1eb249a43da99d8722f2bcef277bef4b8177bc04e6a95cbe68efe4f28f245cf93ed827009284b9438fa615d1b69048ce998cf98f34cf94b2c32461f62043e047a24c114fa40eff0ef563818c85c921f30c6c0be591db4280d4dba6e631c1240a2cf26fdf0f1639c97e2f51144b60d1c78355def11a588782c9e736c661f19b8783c9ef541bacc81281b1343a506cfff7d3451d4d11d15f71124d22a953c349f3d4c04e262975a66a0b5b9d65afb8e2a900071f617c10238c0c4c58dca0ca42878f938c3cb8a38745d1fd2a1662ac7aeee8ddae638115b723c38621428c7b575b5ded8aab7d15e36a2be8a282272c2f8474082306cbac14571c9960053e58005c11039d8115568c613b1f5eb437189af209199c7851831b50312207499a1c49a1c2490e464aacc0d899c59c74d9893397e5182358cd233a4b7418e3e8beb9fdde5fc1e070fb2d0b4c1229c072fb8558bd508074190535b8fcac67518b67c5133056d0e028e775c7d5172192be78516d66830ae5ca268223b62f4370526c26a56b42c5136d888a26433728a142063a86ddc8e898d8cc063dd88c8c0ed9076c1d534760b5166c536ccacb46c3139c98253224cb5d893485084c4004f7812984a85fa68092651bf5669f127ea030e38adfc27bf584a1641bd37161432db2c2a10007ed4abba4141b285a42820702087165ca0d0490429443ac091874086228064da2944162220b25415c79f1e8f131e38a4f755175426c9183a22d68c085b1f6ca2d5b6c41a5b0a72bb76cf1a504d6bb72cb1649516c3182ed882da66c506ccd955bb660a23db1ab2bb7685146ed99e2c9e519573c15d32511094955d7ad54cc9c4231db6e53777f5ec731f3c662e6aa3187dd3fda25954277cf989929337b873deececccccccc5c2b8bc5ccccccbc52a56870e0a09bdcab76aaa2592ad3509aadb47d361a9bb4d17026a73e4a5782e1baaff33ceaed7ddd67e2a8b7e9b49d6cb5280d95ca7e7c8f51843be2b89ccd951ff20e2c76569b5251d5caa9f78aa669a4924cea09a1bca8344c19ca8a54324422000000001316002020100e8844429128c9635dcdde1400116c94386c56301b4ab328495114838c31c60002002106100380310c1551078a10948e0f99444829cd742afad15fe80b2aa10f01ce4454299dbbdbe806469d6f0b0a81752d89c8dfde2dec4c564189e3c72a048dc0a5a89e4982b0608677d9a18178cd136965fafd769a82efbc0f6077e7a5d1420b656b596d5c84a7e490da23569e0a44e87ef47bce6fe1ebe3e8f44399a4cc9c165ef184acafeeeb1593af250d6a9191f580f4b2fd4ff72d8777b091e0cd1bc4f8656884512c3e2e587419b1e6fc2ed81b2a4feec1d83b490f7aa19bacd9d92c871b7727f53c1e9da3a300f7247574b4c5e21ee2bc6c4d13dd64bd914dac7b890e8c1e990a06b72ee42809780b820296f81c4d41b24a1767b58512c11fb1414322829a3c7caecd1ba07fda2acf6ad964c081ee7449b1f6797cdf9dc62adc73c3a17648ce2d47f238c28964ca0c0dca484999a911bab8135ed93de83d640edd51ec21f2fd81eab0116d5e7394500323daa4fabfefd11bf521a50c87ea3ef28343c12e58e026e7b1c9b5d314203aeb30ab47fd238c5f9efdd73e124f2db56a6bc11a96aa755b2a47245daa9828a19f59d3d6b1a18aa9dbb56608fdce5d4fdaea8eb71b0085f43c5f6931473f8d4b16498a43158be508cbb38161cbe7ad58f0f2403f9dd32c100e9e554d4f55da2ae052785e1b940d95f4b8a8db75e7c3721ad39fbffc6a986c07ac8790f726e817535221e7a19c6722b047fc91ea7328b11e32b936234e0f728e4be1634edc4aff75bb93df6ca4801a7e59db88624ff0053460e7ac53eeb7a239e04946d92f17f02787fee04d596ec642bff49193d8b3ecca8e2b586d3f61fef712db3cd5bc6fbc521f7c9d15843fe5a585faee97ac9e5c02d6a55317e2954706e1c7b5b6413de499cc8ca665786e5e48212f315fc824e5e0bd1ca9e7f1cc6caf84473873c7610b4b8d9cec45198ed011e05f5a13392ef3615b699de33ba7dffdbf8e236705b67022ad77be12ac27c608d21e80d09e41bba957d4bb39965038c202495a7124d42ce8bac451a74258a0327b13c9462470bd0bf75f7863260f396350a03d3e800993cc33f8952803df9660d87aabce274a6ac3034da1e05ddd366ab0fdb7111aee013df75ca43777f764678653ea342934f979339615d7bb5b3c4946627aaffbb5e64ea75abbf6f47460474d52fbe3f45af3cda78f4d9e8be85e3a0b299057c6334bd3d83b9cf1b4e09cedaf92feb60db795292cebe293850ad2e3a39328ca1ce1105905c6ddbd7815b3e27a08404befa98f95175499fbdefcaff7a70b26dce835fe55d6bb2352b3f55ff77e2d609462708b56451953afad8e60cbe205368acce28ad13153f2a6a05fff21b969433cfcc3e6202f4625ccdf9b57f275bf38d1ca425eabfb92ce228a87e709197d259d73fe80c4c0f7ca78c486e50c14f9fc1bd8a1ae2e6afbfd8009f2f6e16611f5d8a1998d4de4c7b9933d780575a64e36b51c712896e3da5793038cb96a82c43fef70a1e6d60363d282881bc157f789fbe76903b987dec8773dfcf2f7dfae12f0b6998f1688e20d786f34540d203c9e5e707d05a1f6bd5c40943970e6b10c75ac0476ed23a219a2dbcfa876f1411e505f706571a83751cd5bf2f9d9de4534fdda40cd30533aab1466034073fabe645c296d07c28bffcb10fff1acac391ec0f01e92ebd2f4e350f0d3ff88e504186ae00e19d66a0ae70772cb05cd543e01813a48262a2c10e8d577e718934e25c520b99237c2569d38d626e75e183e57bf0b0e102ae25b8430b6ba0ea7a43fbae827c37257fb5199644b3dcce83b4bd8102ea3b51282ff23152fdd53606a19d1e946ecafca4f608d0fda44d09828b9914badf764ba6786056201c52f2f2ca30317dd0262e09c646d934120e9418b51d07fc0182bb0949c55f40f45b46e2cfe0e5ab7fa355335ad340291d8dd5e35ea4324e421639e1ccc8d808a1c7fe78a77ed954e09b00df0d544bd6fad9171f08b2690ccf482e667e043ea87e97605b41d4246ae987960881bbc9ec38d4ba23d91241b0e1f24c2250fbca0096db6f5455b9161e0e9182dfc9609fb22f10758ea832761054d3174481087e94308d93397798a870c4cbbce58af2d75329398ae2e5968d4c46c1e9288800846ace2f1db199857803730a92b07b607996c4f7300e518b5ea70c3e4f9584af549ba431050fdf1365510814695d2e011748d28e8d785c4393b5c1424568c8200e474202963f395fb2c97d0b425079ad8ea5260e8ff515a468e1630242dd272fdc8f33ea0db2e2ece0632c063e1d16d46b86028d95f0895573483a81a5dd245294049d61d160952e7f120cbdea7d2ef32023b47a15b5297d5ed22eab258379e23748720ab1f4925d7ba7f3bba4b0ba8a535bcbf7b4e45701656491edf92af0df23216bb7b11ba27ca792d7467d8a48545b997677c017076a899a72fe32846a4c57fe463bc34da69423c8734bc9709d21cd6752d61306185ba24ad625a64f91edeae2605b4d31ffdab7dfbb7578c0d2028ee8f4ac3acf7e69d2e4427b612287bda1d25724b5c7d7b6f31573d17500ec3cbc89b3ca0732752bb1b50c96386a1c1b5c92a72e90a802733d19cb700b0ce9c55157adb29d0614eebfd9d100eb048a506553a05823a24c996d5e26f920431f49f4ecc4623f933d673d63b06ca7fd7f2f40bde05e0a13d13261fa0a5cef00b30281f37c6ffde2b7423b71f8e0a35fa6b7d191f7da2a1eb10b7d5949b6d8cd575e08890759cd7d3fd921ae5ee618373078f521e13ef9b3a4e18a4ebd04d0baf3bd25463227969476fcd3b1f569ebb9d6ef28b67721406eea3b9ead0083f7413ce3aac2abb5e1c4307ac7037aeddb1169c90f9406dc79acb17e9ad26da99f7009ec8bf0e39bb0ccf36d9ce7c0e0126d2037a7301d0d01fac6db10a3ab5e41bd89b4c282a199bc69dbb8c777707c85d7c152a68358ea103c83ce639189c10f63f103431fe3a1c9a68fe3b189c78f63b349c10ee3f109818f73e1c9a68fb1d4ccdf7f575384558b2e7cb72f2f57536e996b7dcd0f8701cc753290bf3c568e1d7d55a1505093f96423fbb2adbf7b784ab8227b1fbd5bb43794e894065bf48cf5568ad7e6d68ef7891e370412bb652deee5ab5b8fe082c3b8a5ca11576ddcce4a76cdca75896702ac5399a28ff760fa3bad7d4ef82bf40b943e042885d20d4dbd19e1d759c9c4ea42ba059756b9e2b23889e822b1d28d957d294fd5737ee6a93c149d90465de92c951ae8b214d12b8718b4caaf886b5aec2cf468f93becda1c32bbd10f25df36f2f96eaf68fd95c8185060df0061979d95be170f71e68ae5bc50b036d1455087ec2817c1d8a856e788ce06ce07d034ed2343185c7074265aac0b48212dbfa067b71f3892ce6c21230b734786a64dac93f4e6cf382078dc5efce7ca59eb12346b502b0b5b20334991b6f7a4def7ec1ec4e29a027e50dda8a99c70217d16d6442f1d7486fa003e9c48331a962c76bb8f50f5210a5907ecbfe72d6e8d9a0e1a80f0e30ea3590a23f8197197a3a60142cb88e3cec3c4bb60966f4e7ca5ef75d8f820e7c9b663cc3f35780e790ce50af06a16d210b2beded0c4a042b226488cc1410268ea804d0196282e3464beda208a42d3036630b5f960a952989c360ee8b76cb82b48af1507822873f50738e28ec301e6cca3f0b43606ff7b621ba4debef83158354b77bd767c99848cf079f835f9faa12409f56c0e7f8c1d29feb0de0cfaeb90a8c6b42814fa21b1536833d2e3f1484d4af80af703c09922e427dcf10c6472ef74e7aef7c17a349539b91bf86163fc5104a6f684d5038ff880a6ac3dacd398a8080dbc4d3191a31e5df57252bd4c69581a11e6a72dde3fd9557995be88f9c996801d6dd2a6d068eeb23757c46cf91f42dc62203bf8bd782a30bdad5e7f93a50b7d7b8b7df66887dcd02f81e8f3debffba76ab67f5e78f181bdf24e7c41df662a48c839e7be7c348754806ba4db59e0b122be1a8a5f74d0e4263c1f765d6af84f96d0df4002ba43a84210b973c612c2846460702f895d091b314e35de0240ebec12ca2ab2a8d212112c755e48ab1812af50ff9c9ca6649d229b30cf68249b694bea19483d048b82bb7ac310206c826aaeb09fac194801690b33a10bac8e56c298aa9a36c67f4bc022c2caac4e88f818ac663585bbe46dbd120b7c01f57241bd946aaa53298600fba73264c3682ebab9985fcfb566d3a6bdb260e230ccf7f656b770bc07cfb7f742abf8a86cd768b6c95f4618474f923524038fc7d6890b2f43dfba0a656cdcbfca0f9f05e5d711d8158423f118084e4a56ff47c19f10438e2972aa2b70dd479f336b33d6c8525da9ff283a8481f40020116647d1908bb4d067a0c5f404f9034e73c4dcc54bbaaba4020b4c147752ba45b4bcf206dabb5fa450aa0efd30fc359c0e1640d8de18d22b5e1d2c9d147b09a212e21ba40b5055a495725619b77bce0e6b434ea9c6406c19eb868c1cec9ca318ebd5167bdc174d7c304751a40dac30bd960890e88022b021d9d2b06c92b1005adf00f6b3b2f226e8478afc23780dc58e754c62b5c613ff6311f30cda76a7a90dece5b0ee25e5195b6fed37e3154fbe48efc228dd23a0003ceb542f6f904c782b0a0bc0882138733207af1da8313ba6ea84c95ddb555afd55b05fd5f9aa3cd50df4eff586f8b3bff3acad164c119280bcd5545aa43c4cf7ed2b3e586f14a6647c07d4357a5b3c278ef53782359e3864ec7a344b022930a6c9ed9b64e11ab33d53e02b18af8cd5c4740a27c52e0cd5bdd0eaa28e8f1bf1fc251afaa289f5ec9755b8469201ec9be6104e656f15910e241501e26fc3b2256ba046bc758b518111f0430bd785054104cdc042ade4d4be2e9ae5548846f2f8108075c720cf655d2bb9ac39122f6fb154295ccd71c134d2c400cda5880b1001bfbe3462ab79a45b915f41559e06454926d86379e00da4e58b66e322a69a000c5cf21bd8bbe5578d916e97dfb95576100bf05fa4cdb842326e84ef6d06c56918973e8e677ce56f642406350db6d8ec23170004aad47c9b851af59b13d6d814f0685a957404f210e052cd0d47d1df90a8806bc7a513de0cd773244b767e0887022ed970250dd39dd9dc999db24f790c356771f693a25d0dfac859ed5f3f3760bf3b6d8fad470fa00589b3df48dd151596c33dd85d69c8e97327805612885234da228cd261fc5b1c24597c23db23bc22c70932b52290da20240777d99db7a6cee49791c8434e0e8001d6b30afa191a889982e7a4822ae33ecabbbb7200625cbb95076cd8334bf68417a06a22fb4a58d89da8a7d5c3315ba1e522ff3da3282bb9e3759e8c8ed3191574950d2bf07db12d270783671ed7456bc0f1c06ff6fafb612e191140d7082a8f9089762f682bf450844522f86bad2fdd51b0c17a8a4211554a5e43ac5b3132678ea11c146cf367265e8beb3e2d083a3b1a48b4e04189839ce5b64f1bd9c88ee443a8b3589731396595095f2c452e997462d0b9a466d37c691ad10d76a670108b7d1c07327bef5c622823bbc1d1b73c1ba1bad6b572ec17c855344d22ad3a85a460e22b2b6deb21575f78104cca8803e56c902e5ebdd468ac78d98bc9a9ce81afec0fdd29cea0602ba929b51a71ab0eea0c64b581598a79869b71fe68ec52d755648abb41150429555e1d629a721956b92dc039babe5adbf4a017790187c33866c23993697206a0fa379dc74aec75527c064df90a1a47538edb7b7b30cd71a0f4d56a70031c3c4b18960c437caf37bc215eb4e29d50cc25f94cea819ece3029455e976b025e3a2a232bc0b1992cfa55da0c199bc0158244812f0a056ece5c68897e9d3f8c4ba9bb189d8b75253c391ad743394bd7b72cfbb192a7425e55084e8f1548b423763790320b2d5d97c9e6c49fb9ad6cc08838218e92f212a3551a6c6f82bce6e06c85c9f67419bceb4c9fb0a7d45c9a844d214dd0ced6aa46d79e666ec3e59fa9e101a76b130e6b7601a9012ff328569b4eb507257778afb8be71e047fe84e5b7b30f8d0ed339136721559e3d39e9fc3eea13b5d3885b1b32fdac5c34a4ef82e85dd781567ebe1d4881c42f0bdba0f689edae266a27ed4502ab45052d66228e949a88dd9a7be4f1a8fd37e214069fc3ee9cdcdec3270b09950b83b6322ca902fee9026adbbb438b06231b18706f15fdf381d62fa99adb7d343fc5d05d1e54e8f72dce4dd69dac34bbbba08357973cf8f7174f6ae115cc0f19aa71980f3f37f7a853710b734a604a5b95c46b5b566e23715e8cee4fa2a79aa885fe37cc49e10be1377b11ff14b262caa3f0cd4a00158e8f8dcc4075aa938b504adec8b1edd40c83dd235b3e25dbec0719b1e43de99cde45070f25df227b884c9556516229879b3186716825774f9aba286e512d85c9d71f436fa34c5613c1e234af4dd8981d2946fadb9691ac8d0f7230f84053f6f5c01ab428e53b0c4609ea5cae1f4db9d7f7bf8f2084de31cf0cfcb816c0c88c2a4a4dd7dfee110c6f0f2e9878ede6a72c026f59ea33ab3e097eebede272d12b7e0cdd0ee6e0e406ff75322a66ac028846b0442f4839ba0da68e8dc52d2057d38f254f5d6ff607645db5a2cdd8e65cb886c406ceb5b1b9074d08ab9516d8b355f69ba03b56d2b7e2516df626c6b9cf29dc5cdf800d728434ddb0a160898367e69ac004c9cfa3bb0e80b4e3226d149a94485c7e024217c29c7f1fd66342055388959721286adf5a5eabaf8a9a10d2dc938e9f6d929d54acd491d9b4b7f44a2fba04593ea07bc4dd073177601dac8da4b3a123a3f099c2ab150f8984c7fd3e14def293ec9f745079900cf201943515248612fad0f4e5d17e0c554ba23630ae261220eba6999fe8fdda4e1bfbfcceb86bfa4f700328a009ba51a0156663865627ca887490839fe833c0ecc2b6b2443100cd17efb7ffbefe6ef6e4a0db8af96a3e1988abff5fde69febbfe9a5e7f0194e2631ec8fbbcca5cac85b89ca974c0400c549f8649ce4ee1e18bf41421227f93058aa140ddcefe69d1aa58125877762a0ffbe3df8b10d63906d57312c9bc5049954007e056256769e8cd1df27a50ce44669d007a0fb3adb9ec7d24aace0ae73cd09af13076f8eb10206e79b4d5e8adc01d02019152151a8e775871992b5937175a6b3597a9e9ec38d8aa943f36fdc16149489bcfa8fc1695ad19b7430a0d91b36d0640f0081026f3cfeccd7c7be2858391c1b1aecccff80aedf080aa891c55dae8c409a37dc6ee84031d8b7b83e5eab37fa91b090566cdcf64dc9a637a99404ff2fddd260ad45c3ce1abb654dd872220f83aa2e0d9d5ae796d96ccba53c0dd237860dea9b001864a0944928260b2868703ecd7c3a19fe31bdaf32b2f979b4610a27c220b071c2b1a4a294b3315b851834e1b026e33a49d08bc06fd3a40175135687b08ff5315d7aa573283147f5d306b5c602a81db46fe4b13a8cdb1c8038b740b5554b0c6900bba9e0b73c0e794372e2399e244f7a939685310294f657a2e0bfdec032c13256ec147d644bc1d6d54da178955a5fd13eb3a1db179882765a5ccaa2bed860cc48e69c58e430fb1c38f7f1a56124be60f403c26d64d7b6c60ddf76b65cdf2c58021c1a9727f0243eeb2a5b3788c9bb4c1929214a6bc7055d087577602aa8d4ce05ddc78a0a64cfb372516a1f73dbefaa0889387b02320149729ee3936e13de4e5c54c76d3c571f61a402085537774213450796e4f5335dead38038cfe4227b2ba14b674444077a4027cc7b397d684c0843dff20c96aa268ba34a30749c8ea0c6326cdab3ef5f4ff5a2ef5be4124467ed7fe27dc9d6813ea0637f1f2ab81558554b70aa0fc974a7a60d7656e673894b551bf6964f1ff14beeb87f6a631af9cc84b90c59be1030b4d0fd7a859056ca2bfd818a24b773ed728a36245e997abd42dc0067440680b38e7d88376fbec5b389803f0e85f8ffadc7d19e51604ccac90a3046f4fb36e97ca61a27071d3c2a11591f1a1d20c7ae4f766f8b4f09827adfafdaee869291ddf769db1cc67e518c0f72fab3f0c6822d4436720a10cddb93299fe7948e905216aa08925f31f882ab759c3a9f84b64f07e5fd721d139f86bdf442e02f504e9bc5e2fec1a2aeae14931f1bcf5ccc86db4aff9057c7ebc990310fbcb45b51a94fbc3a6451471f405c214f1316b03d4ee372115704e473ac3a7b74b95e909388ccc2e0938bd32dd1be506e1c9e9fae391755a1632dd8d6ec31827ed10a8379b1257b4600d2d28602bfab3a66d0220ddbd60ff953f1d562641ae5146110b8d8722e442967513bca4ef8f7217910cbc333408f8769a8b79caa87111fd62ce34d2cec9842f4ca6a1eb41c80347344237d555aca73d6775d3dd7fb1b508c404d528f8e335ea6675580c4062451a6435eaab6ab66829e5e9882f76d2e503d02c0745a6dca87bfe203bf291b2efb93ba5eaca7054b56e2196ed91b5985e6b4a19a26d3393338e13915ee6b3b5b2df21d58291fa504aa1554b370dec6d9d2d2de931539852890f4764bc3264569afa9d8676a9c94d7b370e537a3e699807868c50f52c87245e5e4621125714989de6d97f702b342bfbb0c9eb2559d45f5fff04470a81780bd93cb6e5b609c1de50cbd04c3b51eadf8094d357b8b3ee16d4cbe53e19eb6b354a3df8395f251246023627a0e28f36467ec4b3d28cca2b90a1ba064d29c25b32d39edc28cdc7093831ad1654c8e55a1bee6b3725b07f4ae1dd74d484d4311d3f048dc6caa36df9c10a3c6344c9bac7f0e761b2fce90e18a2c04543810fc964936cb96384201dd82688e137a519df9fdaaa413cda4af51ec3f0c182df3881f424e20b2f6906f2e17e549290d464841e0097244e9ec3518b61504f96516c006b31ba3a79ee10aed9a4c18a940863ca3edc3843f778855077daa7dfab9b8d33b81eec41857265f996c07b43b5fc8357e660c55a04083a7aab9f94b98c674918a8ad4b41592f51ccbf0740add052e8ea66ed2b426c230b16c0365c8010bb1ade97edeb6bdcbfb6a58232d69865ee021db1d51804849606fbc58309e7c5aa6d04e19f3cb50085e92f954000623a5227a84bc2d9631fb2c44eefcdc4ea189b92b75350b3544f3c65be691a0738443b81ed17fe40af5ef69609731a4f3f3ed2627e9c10a4eb908b31973dcd5fc01615d37d7a9f610939c32b02ae044e9dc53404db23cbd13371fca616ef9d9f49bb3027cf4427626ad6ca6de586b69902361fc62a2c54727031e5be76b82d9ae86f905c7498a7b8eadd9ad30fbc2413c4c10cc62f79e967d2d246ca9e8cb6b1e1d9c596594e169abe884d1139d5f833c291e23252a804e59268cda3aa4dd998d1e57f1e031080d2473cc8c2cf50ccc917e574396c7f402849043d0dc1a9d4c3ca4d3209d1f33e2254408164c91f189516b71be5edc3cc64d39e39d35c5d989e9c17d8af7c7f9b406a9446cf4c349294e251a4ce55b166b283fa8784a91109dd24ad88beb40ff21312c653d8e2b114ca2d3f58e7b1e056c7fed90f64b5c35d5b935bbf37b4e9dfcc5f00c316631b36d93262a2342783007ebe6063e7d4e0c4c59782b5c1457266d04c35bf695ea675b32431e01343267a4d28002cc82755b9a19b2b0cd7461843c23e968ca0d70b6f18b35e217f1366e7e9991dcf03603f4c87f4199943f3711f5edf3e8e6850bad2825d6c9cd6e84c24432a08dfc0109bfa62c062b1c983c036d60bfe548c79df0227c982c158ced7562532dca672c859c417f2769543bdbac8fb06e8f891217d14424e24d4660c044e932a0268ed8f01e357038445af89e0c073cd48861f756ace4983653a5f48b1a2074572d298ed6e86f2b04a3181c9bf856c5c94fba9fce17cf1050b6b14577f75b400973622a150208a602c0254f052a5ee036ae0d0a07717450beab0f45c3d9715f6f4879beda465ddfdee5866f92f43ef51ad4638e1758dfe22149f99ab8ec3f9924ed4fa75f325d0b49925e2706b32ae224c908a1a55092e2a1b80e6c98529821272597624fa093492b469e4c8cc68e256731a38d269d12e929e423b8329a5279155716563b20eaae460af04ca89da0ffd2a40a8d3ee1952615287f838b5a26df996cb28da38c36ff104e06940b5b9b3811cb7a9f7b5362a7fce6a0f4a1326059778b24759de1c4c09de8751114caa174a7e5cb80297cbcd70985fec6c0a427a13b1953e49e482eea00d91c5e88a3a4649d56a5c8a83ba36fdec8b24d5cd07f5dba71a3f361169c76d16812fdbdc8ca66590da513115699671830f3ddc5618ea6eb7dc14d45e6a69ad24ddddc4dc9c43b5b1d8cecbd33088cb97df27863eeb3b484831c731099a3cd659e0a32586374704144bb84bee6e3643f2e5863dc69da54668b616698e3b11e181d2d4e702d91123bc5ba5aa98bf79f360a8ea95cc70d216719a37cd35741da68320ad40e03a5aa8d61647d4589062666c683d27d62988c6f77049f4e05d1a89b5356da22846b9be0042452052d25b301e48faee46794576faed8acba8a95c11b4873a451d4adba7526e8cdd5a4a5609deaacf83c6f379d53eaaeb8145f203458ff50a4404692ca85cb85eac49b028fa83a568014d15905fc1980f014f9e242d98449974fcf8e02b8572df55903af94fe9c0def31e1924b858ebb49292ced2a2c73f576c8e9a0f1481a3eaab4185e0251dd2254879a30a52bb373dc743ad0852ae93e573c3a264c4f794f073dadb13ed9d31175040e10bfd26997ddeaaf4411647bb49d2527050ddea00a4c1d4244fe36274e1972e289f10ba7c4aed8d7534d30cc2fce7c3532224894d4e3be91add9b89c5fa1b546bd41e066370fede5bf3737172f1e57ed059eea51d2d14d133637e3e9331510c8676d7ae66609c34121c8084ce820ca462032ce8680979bab5c35263c5ed1d9ee59039dd08d7b3c92f3341ac23006399f277ac65947abe6911b0009034742e2246613254cd14d1459f31f57ae80183c963e6ec2e6f522687f28146a0048fe383b52bdc40ff73011c7c8b6e3fc79b30b27a13396681d02bb813568f4362e26c3fe3ab5ae47c0a5f49d404be790f254bc67530ac2b01095f2d100f80ccf86b4b0739131e3c98a1d8c505272469ddd5abf0b85673bb6723dd255b4119e1f74dc5a18eb07cba9205ccee00a2c03d9845fbc081175166f140de594686cad2ab3755d386073f23ab6240db12801c0b22524eec7ba3a53780cdec306a775964377de465b025bc2a696a7ece334ca51d580aee78af9126a2424319330e4a7001e7b738d901725fd05852eb968e2dd34419cb2c762b02849f2a300791eac804544ae2776216ec1ca9466542147f2ce44bc210f0a937e6291968c0dc932b7f141113ae48b5b69c57fdb42ade8e47d8daa10943d21199ed7b272c9ed247a9234000e2a168047c2140b91c6ac6fa0bd7a5ec42b91481e0817e4f54c9a438c4f52a93a34b5491472acbe1ee056b91b940ad2ecbe7707aae3d33c31ebd749507132e47b0d19600b4407365be9a501fbb40989b3d1487407e92c5fd651cbcc8505277afba17d0911c3ad30e10037bbd1630761763245f55ea9f941f638f58be807bcbe075c2cbac2a56023a2beb680ef03306076e66b63539015a687df70b66d4325aa8a3accf944b39cf4508a61b14c715e4c915d4c34296742548449d87c039640ab5e7a6f2faedfe87bb5aa56b6da15f1df02eb76217ef27e88607aacaf5e52bf4b344ca15ff17d5f87d44117afbecf4510eee9938efd5246792ebb9c9d905aeef03006882342b3253b0665b47848b406d68e81be36dc7e84029e78402544a90424e6bc72c24f0f38d52f5f0a8a9762764e58e3fde0801bc5ded1c915b421c6798fd74fd07a83d6340b58fe1f6c82675890cf5fe30971ef98c070b7abfc7e9acef646d26c798b38cd222064a3725b134466f68f033a992640a7de9a4065d83aaf09b1f7b3a15381ebc010bf284919e08ea7c5a4106860815d87c793bbe0f341f8f03f8147f472bd3b60873209d810b7cf3ed816bc4c02d72881a7aede12a39b4b188a94bb3305a1d574ad693bb8d75be0c99cc10bd21d8b0724b6e49135a7c001bffa6eb60827ee885e99c496dcebed00f5e2b73d2ba8c6b643138d4cdf64531d6fba1fa18b58cdc4f3c99b5a461ae3e5aad88799b80a79747650043ceafdde16cfeec7c4797cf7bdd01126bec8915babddad27c2cda7687e1876e3269acd73090848aade257587574ed8e3e922cf598e7dbf84986032fe17759e4b698258636a0f6ce1333b7eb23e6851d88e9dce44347501bd4ce955dc1b7aa62242506eb6e39527f94b808beecab96fe8dc871730b0e029e24518d65b30deef313dfb4e727589aa207eaf2fea1407bcd223d7a5b4c65d2fdfd6c2757574b75985a05feaa9b2e1a678dbc07c535d98c538f71ccfa45b9f3f5b9281f8969d5b0b5c98d997875cddb321d7356bc38cdc39c6cb1d5067c3ade992c3ad362c510ddf62bb148e5bf36c8106141625fa2cfd7a58d46a9d710190f0da3531bbce6224f0ae81048afc660703d00db4d20eda79946014f7ad5d606d146b41b24dba8b82b5e6238158419f5a086249d1c0a54f41b070e4f6fd3e6f6e893c8521beaa350607eab0caabfdf63c0ae9833336a08d273f443e0c98435a88ff50eab51f89352165ef1ba5fd82e2bc31b3e1bc0075036ebbeb6816e9ae7783013b069a1efde5b72315239bc150ef22032db8c976f3fd4724efb88b7dafb2849f334e27fc3d606d96e76c9b507ccce63a0b0206a681b29ad484466433fe29bb066080cc146e4533528ec0ddcfb47121f22f4531b0abd437cc32642ab47c89fcfe4d5a5de5ef1899326f0fa88c550537f68285adcebf8494144153be88eed54b1adbc77bbf7fb5b8d23d5187cf2c8fc541ee3a8b9af539908396b013ba10565077679f1e290ede354005e7bbb53ef7ea581c1dd21ef2d02196af907069f66e9971e1c0b1a3abff73510bd516d4221fc48fdc795ac99f36aa60aa7a162d296c8597035ee52ed4c8d62ffffba89996855fdf5b7bac6957d7d4bd63e5054f1ab7050b22c1dde53e6454d92c5bf12af95dbb1e622598d345e98c5154a682d9470b4022d89f0c32b57ae16f24828f6eb02cfb8be0f24bd6780a6eca0ee3972c053a3e58de4895796a9d6e49db7b502b52bca8a3470b83d6ae33604742f3e5514852359a183a96a360d6c5a3765a0c03636cbdec769ed81728c5c4011f036fa55e3bb24d254112abb3d19cb0cd155e791983496a0980af5b30bd0e6ccf5ffc79eb8441a35c3c0598101c3e06f3bfa84ba408e31f3d8dc07c35dab31ea34b0070c1a68e8a4305c3dc111cc55f88b78d61c4b6b670fe40b69cdb5fce0b6c897b6c5cbcc3aeca41b0e9ce7080df0c5b7aa5fb8d705f558adf339eff21cdd067e90a7680420ca236535e89a2f086e5d383b6a124625f4c1312de1d0dbdb439519c1c7e406e0737bf492f2346f83a3fc0426d38cab7a8b19b78a7d21ff11867fb5bd62e50bc25df87dcc89552c14a296ca70f67e43581a85f799fcc209196c20590372f57f1389a7068c498ec74610780147278aadaabf3ced9f6bb918f0297983a17dfa1b9926db7e2e632c2535ffd3a9bc53e6ac2a0720d38e8e5246f4fc5d38daeb0a6c5a7dbd196915468791fcd0edbe6caa21f416415587f8766cab28a37d7ccc0e12f22547bc3d11853f28ae65b9c34a85044282ee3c86f8b6fa3ae74eca4c93e35d30839e9e31243ef1ecb1275ced33726d470e98c944045f33d35a372adc60d90f73253e48a9da5b0383058081cd5d3da01ff6627563e8faf92b40d8a16ee0cabd182a093203db9d74695925b7beb8f1735a79a4ec48734f6de27a057672287f8805965aea9d210e25bd1875218cef97ac1aa4fde28b6daa3e81fda4e4b8abc0c05605e23359b6ac768dce9850e53d3e03e1d8a6da4fb75dfb049aace427a33000d96516d94fcb1de021d18d6bc5ad2abf88edb7cdbb674ebf0b168e2dae314a60cb7f5cd24767057386ede3cf2338b15fa2041188f1cbecd2c2d87491918a2774b852b95f924669bef604793e036825ef36f025d06a269c038cf6b412a6e18ee395dd07e5bb549ed753de5817c5e0861d77811cf536328378c2a1ea986b9a4d0b7ac4837f67faaab4547a56a61eeb8a06d69db6e37dab9d2720fb37488b5e3873f46ae2a947b007a55b3bae5f4418d173f7134d7ebb77b9f219d5551739fdefd5354df8dd28a682e6dde5a0da4e21f76ea3125859c469da9c4c047b7d196df5685594d414ef1475b867c34f4edc495ace52a5774770d85da7c6bfa33a4750f232fd9e1a8ed880a48ec32c8adda2179835acc093f57fa2035d1f3e8b0742578d5bc39e8e9daf96fff8480835f94cb8e614a7e68b49cce628e0bc66a5be6fa92bf4ef1dbeae19e7091eb107491c9c0181d9cdf8389032e09b7424264b2f963c52d819d09d37b0c60da58fa3c85497707cd4d5daae8b1b9aceccd79081b5ceca401e61eea2d4da9a1dbc94478aee8d8eafaca5008c59d06ff20bab33a15ad8faac1950bb909d31d626007ff5ab59867a457960001cc5cb94ac40b9c338629c6d3530dfdef8a6aa44a6a1f70d5b398d000c4f4f8412b3ee536319d8d9bd82e16ada13e13d80ec5487e6388fc7105607f22f3eb654520dcb5d5200882138dc170cd0de1aaa4883f82bf230adb66f1409f8119b4fae128901ea315a788d0ab9ea876a6c98d230572903b1fba7be16bd5f12ce50cc0e26566885b5a745fedee0c4cda638ac20432a346ef76e410dc2794ba328f0f862e45c2f61e7cd215d0f5c15f1f1649746a64f770993bfbc50515a1ad063d2a142d83f08b5a1864c3308d21c17c121f98508705537ceef84460425c3a2b13a2304dda5518b959ebeaed924c3d9abe9cd8a7d52020f9bccbbd4f51576e2aff2637e94e50e256fb1a1bf2098b9023fbed6b35f624a3acfbb2b55436bc5da6cd3e7c4826c8e26d142a45dc59f19435c212d5d2cb139a96b55327e00995b55068b8ff0610e05bc907cbd354495254eef76db000758d059887ecf5ad854c389a915a295d7473843fcc73676c612f25f65b5f355339532375e2798ac41069dc847785f5041c71ebd2ee80ddc8d0d926959e1b8db9a0a45a7ad76a6bcf28c33c208b7cb4db20802f374894cfd8f217a62a44574e8bb45bf01f137fba5a7564b2363406ec88ae24191ca81df5b7798af14d50bc216a28902f98d37895026758db8a9e748cc99a154a1b980cb912fb45f3bd506cc0919be9c0aa72acf872b910eba82b457443d4d925abd1fcbed8a651123e0fc6a292e385e01bd93145cf2dc15eb2c5733072a3793b9daf99bc5b8bd91a29abdce2e87e3aaeedf18c760221b631746b1746a7be507376ff262e77b673b2f9932fa14d39deda0a112d8cc4f560d41b50024b0f778cfd234cc78110ee03506efb146698ba56881c20574cd05c2deca1f46b9415860115e012209badf183de2833f7b06a528366e0886d8a310876c56cfbcc53d088ec53cf24e89b5ecb7de3923c055fd3897189bfab354d8e16da4d0013288a6ef577f5d03f57f28d9eb0d969f722dc2ddc719569e9b1a61056629eb57163f2600c38b44ab1f5d89a6bdc4b895cae33cddedf1a1ff6d8bd44a6ec9e49b0fd9cf77c09ba509af6b69cfa955978aa91efeffc95e61cba3b1f36ca9edde872f85b22da1c1267a7183cb873ced78f1dccacab5ec6b087fa37c59138825364dc1edd804fc143ff4ffeef8f334c5839c73fff9a08f5f287fe5e02934a43f30ad99e593e4887781754894a757753378845221950702cbacf010f66fb291c1e7d39a9d1e3e9f6c976c4e3dcc473358f2f070ab9e48eb4eed98f70adf2820db9e456c3ca91ad9875f38c59cb9a1ef365a53ed02c35cd64f8da071e5214a193473ad578368710e1456fd3a4d7f82825672ca2d1d5e83779c4140d8c79e86767776de34d93b551f0932cb515fa8c399e522366e6426934c935a9613f6c99a1662cd5efb0c2174b433115258a1e387fdcdd9e60aa87d375b3346f1ec3c12b597efbe0e04b08975c45e06bd47a290c43dd789d9c40a6542d9e8fece63ad2c0fc9b4973e4c69acd2f1028cd9cf660bf44ed54e67d6fdead6b5390f47104e2d2f6b563ff2055722507574c70f34b1c8a460b1e9d116d2d7b300abb163ff8a2231327c8558affbcfa78a913c3cba6a1ba05dec251543ac86521d346d64b97da7cee1227239255eb83763ecdbad52dcdbda2f60f55fdb8573023765a22817b55a145a8a0aa9a1e5c17fd7dff6a5c961b70b3b97320ff363340d59cb317364da09d009c6f5ae74fb50cf787642d9cfd6b2fe08b09b776c73e184d976edcf00a400a0a3aa118db39010ceb9caf636977212ccb9f520a334c509248e9e5cb055ee08cbc6dd985ec949edfdd72892856921d77a288bce75b3a60daba062177794525ae315b193804c41e9f4880816caae4c07eca2d7cf552df4a906b18cd63243b2cd3a62c71540fecf229c285ddaa8074ba11e667241d4ddc3e6e21e93e148ac46a9cba06601a15c0d41a5ccc70bc0c64d3b4257285b6b29fe479bb7fe85a6a7473d08a7d1db8ffc22cdf58daf242d499677e2038d35d94432c5b7284c4acd0f140d972df1c0ea4ecbf1f27b1bb45520704749c9fb09e76d29d44dcc8bd3a2ef14e19ab46c634c764f7754fecc8d1b11e5e4a0629c715d2ab748f8c078056222736918e25809b2a1e573d0dd4069ec6b1c5195404283e42e3444bfded6a890e40ae55f8095bb6d43a24bdf41bebbb9bdac494f2f796ae17471d9904a6b63d522d6784ffe0711fc4a78e6a8620582879c20194d940bdd0b9e0ed33e5ca7a26d7df0425af50ded27409634be43ab80dcb70b392130423474841473de2286b05fbaaecf008dd8f1671f79b75a1a79067617aecae28b0dceb52a513fa21e0225405c660e91ac37c94f1da8b68b682bd76c603b0d99228796d66dd859e490ddafce681bce62a68c6ec7e8873a7e0f4c0ea28fd399122d17294b9bf39dcb9a9d39b2788f9f3d968b735d350848ca0ca7a528f2179874e09ebd4dc0d6933c78ab5dc79ff89739fc0c6c08854dc0db4b99b6be2d286b123d34555cf8bf5a96e3a46963fbd43f92440bb10e7e95aa64b89535a97f38d734210f88353026f8684b3ac2b57838a6bcd09a0198d24bb267f90a89c236d469d1587894a19bde81b87c13a9328f99f57c8933129e26139202aae7961a47ee80e585e44693f532bc9335362801e1015d698d1baed9f28e707ea8c452135ef7f286b59af3343439323ec5bfc1bfd4554549107b7c995a89eac42e318dae81c31946a866d1f0998e19c39394ce84efd0841f86770db4f08530414bc5753a95acb2004623064088ea106df117fbe3f6600635647662b49a7203d71a4fa45103fe3a8a5e21a0653dd09d1c924dd61fdf0421ee2ac6b7114f186a472d59414eb6eae29ed986baabf692a2b9ddf547c0b31b929563d8ede8485c187d8a879a48e8ea9768ef36cc554f174046de215e12b4e3e41b9ee0654e4129749b1d2e5c8d08b9d7b11499d873d813479727acf863fc7d7cf2ccec54a705fc507942d1a851c202fe4d86f1f2939ba70032a6359bd7ae37f2957df4ec8a5d889d2655df1d60cf17a756a5dc28c515cd7d5c0c44e2279b0b0134f9c6cc61d7ba13db98b8c096b9ab6f1ba0111f5f1d2cd35d0a28fcadcaa0a74c28c67521b90f355dd8bb05258876245a9ab24b7595885bcbdabbb60c862a9241ea2c1adf403ba59da6922ec77e133707ca749ecaaf6ff9a48b00b2dd8e6bd3f0094841d07fa7186b6b2dcefea73ca0a18c0a781370978e8d2d8664659a11cdd2cbc7507bd78a14d92694831eb0c62683e75981d34f73ef6c7ec43f226c346c2e9adc81f81246ca24fa91a046df40a0f25c1fa6020d542a7a8b250299d9d8c261d37bafcc214772c4bd55780cdda0f6ab453e3d43a2d98d1653d4b22d8922be02b5b980a271efb4e4c566c3089a04113c4f63a8cdc1327f7ed891c8ded9bc96d9f6d46db5767a8fcee972827ec1ab44305610d155bf6a7f1dc14c86c05dc776f62f1415638cf3392b54df32fb2ec922a646e4845d9259d526ab03d24b13d50853ddebc6c552c74d96a3ceeedf96b2852ee960d3cb52fc6e0aec9cbacb475075885098dbff45c25c1c86aa44dd1be2e9095528230f9e5c9ca1805e6d9260ec9579f1e4ebccb386b2b55fac6cabc78612f580f27245cab793c1952b77f22d2d06a511712ff2e3d6fb98d9752cea444c0f30b16f60e12eb2aad98441471ffa8a6aaeeed36736e334d2e20b292198bc3f89ea9f4e57c92f2915d28ed6b9658789cd53af1af7b90b429ea89d39655a306231afa13c4c4d0c0f479e746623513905c2315b3b38e0ec47c02156d77070f985219b7f99974673fde193d95c9bd9df3784b1d3e59423654968bfe175cbe5051b2aacb2ffdfd9d4aab133479376aa3170460743620b4e4a465b789d8225af452bb7a7a700954cb4b07c37a8125b0c342fc16faaa51bd0f2be941fd1a52637973f6c985d6f2ca044b570e8a48b83ec74e1eed33ad5779862b0fdca4fddd9cf9e7aadb8a4b83a87f2bf80f17e9de8270aebd09c14b2c35bd5549fa6341ee28f8988cb7457562b3aabc269d2654f112f0e3be84b28686116b34b0122f34e9103fcb5afec3b23cdbbb3984b4498911b0d16d896d72cefea232974208b008293341dd55cf24e0d5e1bee1de8e02a32a8645712149351d3209113fa8ee10c23fe50397835e08bbb1350446d82e12451700a26f1440c42ae91937c3b40b9de510015f9faa4912fd1a0778cf214198d3c24b3053645dc104192d3341c98085094bf2b81d20a7b94e7291f4768785723fb890ccd254d281d2d6fa47a6e39e555f08be0e19a404ed49c8953ccb1798ac125312af23760115fea118a77844a0884e0a83769054d248df1b24fe0ec5c5a1fba2cec68e3817d8cda44b04be83472e90e3844daaea9d2b5e2851f6f12cca67ab24070fd964003175a57ff80ea3e7183cef50d0ba338f206437ac828bccce32e26b1873e21fbcc93cc14ea7a1f32f8d74df66e415387ac58c0ce1b02ef389bbf242978e6ebb5e62362e980934fd9825eca7462232365bf292562e62a6ede277dfe5c0c6a911bc4074190aeac40b3122dd2770dd7a0fee30f5ddc8146431b54b24e770f003db4c1e9f5fe5b821d132902f09151d715f710e4fe8026cdfbdffbf3cfa0b31a3f6ea94a6dc9b6c2317e32278a3908ed7b5978c42d1f3606b9cba01b3eb8c99261fdc4aefbbcf380d082feb3411e942870a1316225b3faf3e52bbabc18047ecd4aeda6cf4a6ed684c44c6f58826d46512d89a6559cfc512314ab6a4f99a3335ae0cd103d566773e8d1a466aa363879e276967332e4138a5d8d9643486726fb2a90454794fc76b27ebf3d9e6b33aaef8a90db9af3cf059784e5e821a5604a669ac7756113efaa2f8759109b265277fc56aee2fe4f8e37fa3f6ab6ffbdd426970afe075adea516a288de9f0b954b0f61ed1263231525cb8c51ad492d6e2daa261b83bb1ecdba97996eec55f8a223f16fdce025f660261b300c54c95025e5762e9b7fefa04227fbeb20543b0e873b933d6a41eaa8a2c90086885866671105cb6a270c24ce2c294bf0870e0ad9f90d42b154039512809d1bc7c369a84df8c63d42798fd6218db5a5795ef79392419a209e9791269b1211f9e36b51a0ee7267dbe95d09b3d12b069abb71ff66cc6076cd4ec0fdd512309ab24e78ae50bc7c36583f89ac67990396056d351b11f3f288c81843308d2217e99c7468df3204e6ac7bfc51cbf5edf28d3815b82d7eaa7c7531a35a76865b63568da58571df210f392c7aad8ce073242c210dc61b19c892d1ca039755607526dd4bd217692119722507cd4d1cc06c9dc6c42e0daf3ceb2c1533f9ae15668273b19dfdad8d8ff9c7cc3d10558de76dbc3202ba216f53ced4e4ce77cb68012dd3dd3ce5d6d7f23e1514e141c1cfed18c9942fe80b5c3f89685b88e413e0550e6ee82d9720766dd8dcefb24ca34d836d0bbd83312059ade8de1b2dad440f4d92fa3348da2830eed3de7a0881ede975b3779eadde393e99b9df869eb7b50722db24032e7e5590c026fc67822d8807ecaad2d0c3e9d93c385adf938c7e4c226a40704f07b9f7270eb7aa856e20dde85b659a0de9392e4b0c10d3f05d0ccb81b6c4f0b5508b140a3511bbec32ff77397e560ad841c4b637e7c3e511e2fe0383fc81bc7c420bf8858ec016c15466a3eae32c56d17ff4a11f4be71e721e14523ec70f9ca173c4e15522ae0c6414885365d15c89cbd5396eaa6dc98b54323f5b164c58f2502604430cd51ec4aedea99bc5bd690bcea12c907c027e92838b35d2c158e9f6616751a164642d3098e2ee0e9f18131305351d75b748eafcee645cd16542c09115dec3f7fb34cf9aa95209a387f262257242a0e31ed46f322ba2814687eff9c2a0f5f5d239bbbdf32b5622636a723f01b434aea6ce1d6818634a68c8de5b415001014ae00a55766b8c84c832b6862319c1dacd160c8e41a8f7434a2db5617492e00552eb2bdb07c607fd82c3e05bf22d888a7358184fe26f4292e74938b24928c7e23d16b5dc99fdea5f01c32adf01ee78c077a70f3c1f8717143760367781037c8a8a8248c825434997af03a81291eb37778c93e32b57616d27d9421a1142b6aa60fd6bd15aed2bad3ded4ed5bbdf5aba25d09d5f93bb255a5e5a3423ba76f059c83f75dca12e3e534c182ec2d75cf5974423397a7d937b5ad0ef4f3c5a17a641f2a762a87fa384e70864e1e6cdc571bb038ad62ac31c1720f32766e25367a1aa64d9c080752e4e41936541d09e83dd9df6d3a860607f7d2294d4654a0250731d228ae7df6d84e93a987a40a4357cc351a65430c9478725c019217f53039489d8c1793312a6681486684975f8e717637e9a566c5cfcc6b787cfae0e5c7d10466589bb74d744ac11c19e9f6d0ea423ed68be3c68ea6c825428c9311bac9fe721a2af4224a7a5b12fcbf02d1fb8cf306b20927a576b49e522b675e5232090422d77cc93cbcc3b97f2a86976436ba85c80f2257f50ea8a3f1a6d4dfb8b46423925a36d493f676655192c20237fa686a82a1cadf2f7ed6177a8637c6055f6e5a26e7bd643593295a491b48e49fabce69ef46ed6b4d6ddad64028ab874fc0bf3bd4b4d54a3552d4e1699e03f45c51a5a1cffeb3f273970e34b6ba5d90d7632491fd7ff86ff11b6c04855f8301cc4b0d532c7e1df4ff06d2a3f564dad47e0b54411953e87e1dd1f08cfffa4a9b5dde7eab0cbcd31522650909a6d415d675adbcc0eb2253fb182ca261177220c34c02552cdc025d4884f1aba848ab03f1a9d994c1b1d0c64aef6ff6616e6398700c557b191772a736a45f1251913ab3ce5608035fb0473694e477193553d8e801e854084a4c6a2342ba882a4d0166096e92d3869b8588132d3b73e4749f5954044cfa188c3d3a7537403e597d8d5a8a4c9b1e36b2953641ab6205b206c8b3150da0045f2f1bb8b030ecd510248df0583922625c891ede367646583288ee0d202ceb35091ef31f8d8fa779c17eac0fc68f3f47402c212cac9cff64d241c9724f5704f28c44fb632cf23a446f7a93110645402f79a3d808000679f4b605b0b4ba518dc73234b0fdc7149b80dea91aa7a0fcfe9edc8c749a4c7a68dfdd6f884f2a327a2d8ca24b16613303ccc8e81fc4b6ed0f31cf92b252c8ff5de749d9ce66941a30487d1995b2ac044935e8df915cbff38995f4dfb45b015ed8decd9e854bdd76d54de12f21c1f95e5795c2b6cb69f0da4d32badd5ad4769305d86ec57ac97a6af585dc2f7bf569b39b78e7accd7657c020d83ecdcd91ea0a379493733eb4f635285e4b36694fe3c6bf589b5db4604f0b6bf41211ca49385929d8ed6edb864f1f69269f6422906ed9c1af51d791ae5a5b2bb8ec5b1775074a24d6948f4b9eaa9efe4fe33e2e513dae4e7d6c8b516a5382a27f72167568e4b54485398b331e2b011916ab56e2399376e92022e58927c9466705703cfe6679f567ff02da3a6219f393c7bc065b2d33f90b71318717ccec1aef7ae0d81c7f42604ccd3be9248a2d7f63d611c69cc37363bb79f65ddb60ed5b0ad824595b04957b97a9deefc7d5c9e06a91a2e4b7d7406054118f481b9ce83da1642a42c1a67d97799681ad30c700be84ae3f0bd7aeebc0072e7d918a7b969eee0ee18100bb16b8d628f460a92692b2c784a9315fbc685719476c43e4fa3556f8db74f866a92169101263d516a107cdfa3ea9194d0261d4c9b711765aeab849d2d00766db8b3eea23f737fa8b17c57ba10768d78ac8866c61369fe980fd3f5520b218975cfc4c967b11c2630d876b473b7a38d4aa75b633b592832d5accad046954c877dedb9ba51e561e41b3e46a75018e3184fd514de7a65549c2888831ac0c59e05262fada64185a7d154de3043354c034a3a35c534a9398bcec1d132d569191c6591de21bc43433e4331215439ba95e242433646a8d37c906d0eff35d43aea7462c8f1df622f69b57e0e6a7aab4d87e587825881618dc083c97c76e32f8231cc8e49c138c5fd31973de56067fb6402c5a7491a66fdff03ce02d4c00994a60e364faebd00a96ddf085b9228c38ee07d0fee8661ffa3bb4c00d75580bdbb4e7eea8786573291f3f616aadca3127792244e0f7422b77eb6d072877d4934eefd2eecc4948c814072d2ea05ddd2ef8d859fa04e4317bf1e23850d6999cc9cc77db7a6287f71c60258884620fb8f64e03d007068418566a453405161d372d81f405b9bc58af62793dceb08e02c8f4b34c8634e19db493cb89b6a4db239032cf517d1e9f83815691d70f778b370bd0260f37cfccf6d72fafa6f3239873e63ef9abb8648eb6199bc8fbe59f15fe214a6224b8627993ff8712e8fd21b2b5b2875e1fccd46daf33961a4458cc22a7a6662473c24258193189771d70916553ea343049eab617729bd569d525188b2e593d16a7426b7f97cc34eaeec2a486aa939b4f0b076f67a618e54ef7372603e8f32d5a7520951769b39147445391776f232253b0cec67b248139eb57c1b21902e0e57c2a97143ed8525854d02ddfb505cef905cd2eb3278b56d2d494df489127cbbb560e5415c5d3fd65213cdd99420eec0635a8ac568cf43cfaf8e7a7595fba1a768f1fa7c9d9ad9760c540cfe4048b6d09a745e346b73f997d2e051d92d950a0b7891cfd3545ca3159d7ea11be4df3988461f7f433d16631a4804a8a111eea96a5b2a64219d01bf239040ed4f0074d33bd4d7be7654caee31944d8ba93e9b88a85abf703589f6bcc8eaf4374f4a889e3c3e080f2e5383ac85a084c184fa2294368a0394a4699a3b5e5567c353ffd921a23370c97d8c2b0d3f26e54d6cf2c28b8527987731494061a4d600a714159e7010af3d88035bcf6898f8a9bcc1a0a20d20e29860c7fabe97a4837b59981610ef4001acd6fb30fa3d4550d590989a85efa05f9466b4b50aaab68227d1586420e1a2c1e03e4c90a2f97a25cea4dbaa5aa19cb34ab2fb1c305ae67521bda4fe7dbc07ac30a7e521bced1d0a0aa19bab03f0ac85a4c3a5fc230ae9bac2f5cb0cb50e8ab4f09da3203e5cb25db72945c241bdb843879019cbd402b0d7b376cfdb62633311df70fb676a3dc4db153ec5a0f8b2553cfae9758ea5865f91c0440f42c105f12b8966a993a89bf1904f7f862b3890b14d5c33b78d8805f20fb0216ebfafb301065d79cf7208ec1d0dccbce2df9470d7f889647aa36bb6340db4536e9f7f231a6cbaee1e35bad223dea274525e81fc799a1baf477250406d51651b7e4e738b74e7e71b5716a20a7fbab9f7272fc510eb140d8dab48ce9c38144536c66228231e885d19809b40937bcdc73f5b8ec02fcfbe55a48ee8843a64f18d41f77031189fb92f390156df1f77364a0d31e80d3196c7e8b059d95dc2b20f245892ae8bb5534286cec741f2227b9739846e122429096b8f2b8eeac4693d50d1f4f5ab2e50442d1ddd4d85d58b053ecc6708f7ea0877a346eb28b13c9b856fb50eb1871b793c0d0e937ccb496dccf3e2157a6a7bf092ed8160179e4d562ad20ff595728d400d088c14d3d67c256d6791d0eee16a88b5a53309eec8393f443d735bc5b5417b0482319a1ed1b45f7648473b150aced4a83e51ec6094fcbb8a800ec5789051418cf731243d8f4388f2a7205ed86008e257fd93fc6132fcc3c8b2e75b8a4d9fea74b19b05148ad7b3b44d6dffb5d34ed9bf62ad77db67063b5e066ba3f43e36d38b35c36d0503ec16a53840a5b4c92f9faafb7a36969a75146b0e72109db0ac39bc6d43eacfdcc603c28c16bd3a26b31d237e31a0088a439b7ba45aa1a3698753d10f0899b065f7dadbe9b663baf7ae1d410f3571f0084030ece21dbe951dd9ece2ef88254a431359cb8c46ed977bdeb6beede03e2c6bf998e1bce35fa77ac05caf784887deb518719c982447e7be3067334c449580d128d5239693ef7f0a499bbe194582168ca950b77e4d2cdcd2e88baaf1993a190443a5d2822f7f6927b0e85e03c755531ef881217d09ce575a1dcd6ffc18b67eee13b891b0738eadfb5404a1808f5d253a663f705e811e1cb6ac85bb3568128d86bddb21a857efe57b3485ac3ad37bca29a055605c862b81cdecd08dd3c7f689744c1ff26974d3602c714bdb2a02f2cbc141c511da248101031c8f44eea64e987164ebfddb31043a4eb8cdd8400ee478cf9d2f2ed80ffa9ab48bc5ed52fbcb0967fcdd1fb0db8c2ade05f65b2f72122852cfa07683b581dc715221ffeda77ba86c59b045b3f2f74010d065cefb149abc65bbf8420035892b6f451e3a3dd93a195913251d04d7cbc82080b80094d5c9a4c9234875c306b4f470c46776c1c094a2991af1b3f13ea09fb2f5d81b51be372240953e0c956e48eee7390aa4ef10c2c2dfdf946d0a7a98c6191009df2c1f024d0636caded4e0b72a6669194695759849afe9db4fe609106fe0b61ab39929f052af5ba8667939c33e53c58fcaef16ee4e520cb116ca711db6b76c753d302b62c282e73eb1d73f1260070f5d38e660698d70638eddbb09641b5a719035a69de266e3c79b73914ed06219a87053b185e61f4f4d2e393def0081331a9f9ecd07b3c360b58d7eb65fc52c3a0d7a67665db20c10ba283cf9782fb3498a0f05619f654c2a32fe81a94b538c5cb64aa5dc3111f6bd2fe776bd0b51df859c9b99e004eb4bbf6997fa525fe662579136df8ed437b8f38ab908242c7322df280be7997cd6b2d42af382ba241d21df5c4a7fdea4a49cf8f43a53746cfc0d0883c79f4cbd418813b9793cf07eac2058caf13c6ae002b286d3ea11e1c787b36a2f851fa751cbdeab6792331d73f1952ab3057f90f9a53ac072375100eb1d678ef8a590357d05e31a69dcb053b195923537ed5c29315558e423e42e93635682a4800d03619debe7d14f98719d02bbeab9c36abc08868f8a7304ceead0a8ad309505c00a522fafa54481d63a6e21b5633a76bbcb7e90c0a77d8e371f78aededc7e5ecd3751b2cc35d7321a848e998564db4c3e088bf53545c65c00247c16975c7dfee483b18a08214a7b7e718c643d0c7a544b4a9a4574246cdc690cf18e38252f002dc40cae4df8d143e846889f4d4436aa9734ebfe0f289ac9c3c2f2366ceb8a5e4fd0349acce7cc8495093d26158c1446518630a3817520cf9b967f581809b16471907fa0879532eb7483280a901564f1c300d79bcb7695c7cad0922593269016021b4787b5fe4ffd591ad025101ea3f1ed44c5168464ee48836a3f68cfb0e79c9ed8aa36748a82aba4c59c558b1889562bd2c710cb4adb284e274212076b27459b14a85c8c58266ad5fb104d5447364e9d1a982afa925dec463268261450e036e185526270f0ac11a9f903d8c0133e94f107cd29d9f426aa13ccde7e3f6321587227b9fc0868b245369c781068281e849aca88134602be08ea4fe1ce27f43f398570994ac88c067cf2dec4744aacd17097e909d54d570737d838308941ebb891f28159cbd4c0c82dfd4e1d07e672658c5e851c3c3afb6ad2079b81629fb8cec94385742b6316c2cd72cec9b2f27c067d63a1ad994b116cddf74e33036bf7da44ae3a68a4a0e508447955ef06de3bfb8c12376f35db2290a638422358a78eb796bc072708bbd54bcdf96b27492f1989720201c2f651421f1013e5b8096a498047762db89380c6e753bc7b5c002e957d245103f8617283340d3c881cc4a074a082517e73f45882c7b89a1f61b1f5f8f85af90b13050afedf89297831e4afc3216fcd6ca61c5d2cf7867d186e2291acf4de9cb788767d4d7ae3d62106b747175322cd6e48a5972427a58fa7e7fb8e9c37e268dcb4f2d50a6e94595154a14a4ecff3b65bec137f663b9329ae24b3c9aa6b3b288fe974bda7ceef38b0d228862ec59981c3c2bfdac0fd62ecf43937038d4b4aba51247068fa5dc8f41e80e799d06a3e33bcc11d111e6d329ed7e1c42804534d309f97408aeb0b1c49b41b518b4eb4f8043a02c79e44b6bae287693e982150a214003591d62dd6df5fc00182c0f73adc88c23b2de3bb1b855e349db1736aa1b5618c988bc940f14f96244d463af73fda80fa108d0e4e104524bf6d2f24364ba431666a4d712b63cce4d0e2a40e939dfa3968d5f3b08400bdbe627a3e374a92e6e0bf5af5c03e3d5797f3b62803a34ec8bcd8749862f6e62be2589ca43424f40c0b7b4c4f466f796768fd7bb0d094c1877f2e8efabc84cb45db7b710d8f400e3840d70147ee5e96896245c80e407c39b2e38bdce29ead73e6280e3d14566f4caef6ce25f0490f9303410937b94d6fd74337d4dff898d6eb2c17aff24c32519733e20a5afa1559064a530b498b843634414d256b51d8f56a99fbd9ea442454daf20be8fe7f78c230974011e0527528cd3a8b32c0b2d389d604c28be9b5e884c69a6e9aa121c7def48e9c388f6b905ccd7de84ea543f41f1b69d856593c7f534bf1a806bcd0ee2d276171c04c3838af43f4c45dbaeebeb0bc48df8c88ea407f7a427949bcba1460cf33d0f6f143ee0454dd95207913d83eee76a086fd5d3df345e8865a55e4e2aebe6e6fc5af04ce150b1cc3b0758d9e46814f11f7a001d930f792c20d6aa8a32902bbd5844658da2f39984f1759ed6c7943f90075aa4fb0fd4cd6f12edaf44030ef2f933a15fb89d12c782ec9f64a88851d0b27910e12d46069fac2693e088ef4e175fd32a6341dec3d9480fb0d559e52b58d0fd6f6b612f80f3f6bcd8f55fd11d02273e06827a3e4c06baf314bb18864c387ab42c403228d3c323483326144ebb5546d59a15d64a6c406e99bee6c7da6117d2f72df00e2741c04b0a43f26fcf7a788856f34a38bacd9bc1ee4ae080aa4e920110da9c6ff0b3a754e8eba07924eab280d2bc02bad23318c83264ad061543e81d03c0a667afcb2d420b39a6184e0bfecd0b8e092b6ad23f2e201aba1d9853a8e4d40c8fe8f9b9d48befe2636bad21c3c20c11f2db695aad06bec4f7cbd5be26eb5a95c728d21b0bac218c40136556dc392af6e60dc6bbbc689ff94aee350134d03ad3e161be99cdc466ded12c08223fbd236ca5210cf8e22cb4aaf9943dcb60a3f57b39923bec1d15b8608d8559ad65f81a0d7a49b5f6401aeaad8685f805595e1806f0bd9216aa3330d1ea7c0c5869fc3643271039f9cee5744e2a085b92df2656fc51e91f2a763bf2d335e860289f879852d2b801a01f572d38bea8eac6d8d9c89d39129cf627119527557f49305ea3830d64325c36e12332b9ac08c1f8dd1809d0b2628e211f1f34c24a676cc3e423244f8dadb212c02d30dc2e73397c651347ba0b993fbddb871526f9e031da6a2e72106a0608f10473b6929144a83ecc4dc219986598d8d1bc880994eefb8e68b1edbbc147cff50227b73d984710f6c8adfff16052113c4ef6d23820c9c4fd1b08d06d15bb5e0f37264de68c80cbf3dd89aafae57799361bfa830bf437048a8f7fb45a30d815271d7ee7a45f5a478caa3b769a5db755f94f13d918d9f5cdee5d442354dfb6a7f6ff9f833ffcfdbff3599b02787c280440f128ce52fd5ba999180c2ad90131175d72689e2ca2be21d0b7d5be5d6209914608217b6fb977ad0b520bee0a78040679c107638733eed178bfc6cd14a33e3b1fe6edbbf34103a41dce00cf02c60e51a71da2c02c244bbcf2c3cab515cd5ef44d7435efa29166ef5db4a25d9ca6d97b232d53918a06aabcaaa8884ec12cd25e2f7a48a485eef558940fd8bb05816c1344f998b79fb71b90ed99b73dfbddbc2b9acfb26f286026925f70e9a39b81c0a3166c6a09c354ea58746c59b065c1a294c75e7d58143bb4262bb207ad358940d247371dbcb15526907b7f74af93bc7347f13cef1998b77b9b7745df62c783ec78f03c50747b479fd65e667ab7b1bd993b26ba771bc74f09dbf9981c18e4056f60f6beccb458bb98e9f6f7c416ed049bd5a1086c6e56af96344c6cc243a6274e0d59b0264c6c1d232d73b3586da4717e3a07a8758262fa26080868dafccc9a5610eb4776fbf48d4fdbb0baa6a795f3a303b4233f593b3a466e58467a6e78a48d4e500e10cecf8d8f4d6e566e16c690d08cd0f9cc286270c4073e34c081028b0c1990200738603981851ffc200a168ca298620903f022053b602c410a2e48c07282203088f18530e8200a5140418ba2496a490c1645866957a5339351582b0cc10a3b50816475770d8a02e7cf0b7684e3155638410eac70044b6b1610412c4008118e577411d4a25e140995c06e610a5d58e19986e3155d4401e7971a18d4200a4a70b71630c0a26a8102032a44358c1083459161da55e9e49121400f06488266a405c4c462f852bdfa3bebce341bd88ae3155d64f16920ab0b2c5299076816c2ae4a594004b10021ba0b1fe0fce19a0328c4985e304d2ca610410d80bea0054f1758b0021d1c49228c9b2337302041080646424a08e178451738f88991ce24ece870ce177a3a315ae2b82c0837bfd880f064f082638c5c08be2db4186bd6355a3426e6e5250cffefeb1d7a583e3c5115df3551159f65af117b583e3273364e879d14158bf1a2d735bf5cca4e33f0f4ba61ca892c9bf3650b8d67461eda6adaa22ddae2993059abd5d362b5aed684c932fb87216dd116ce84c96a6a726a747670ffaa993058ab75d9b4ae9bd665a43561301e9e1f1ea07ee59930180e4e0f0ece84c1aed6b76375b09a93fd58a0ef0aca5a592b6b65ad2ccb329ce3e064389147037398693cb49aac26abc96a5aad568b8787870727b3381d2b563361b41aac06abc16ab41aad46abd16ab41aada65751e3f17cb063be6434eb46603f6996659719a533dbe1e9a93835bb8c89797909c3ef9065df97655996cdcca7f503d4945299655946474984b490a531143d8a8ab021cd1375a20eb4541cf17e030b2f440516da25483d2fc8fbbc37b0f038288ab0c554d658b84f43dc6d2974aeebe6e4e6398ee338d9cd5819eddcb890d6a5c266d2ad6ed22b4fa705b3642fb9edd361374b0acb653a5d6b986055603ad81418126ca7438c074bd2610fa604637588618de5e8000a4c04c519cbc9c172705f63f56c31656f4b72d2eebef2b5e56cb25df3e32cfdbc341ac0b68b460317d6625f74e688b783bbac58b6c1785ae8f12fdd23273bae3bd771de46e311d14537a51d61b90e3ba485407a81a956f55b602fb0648f883e45bfd1936a5518765efa00b274c83536852da6b20808898db98160b266d2a457f512fc7007559fa9ea571f1a96f85426b5d561571f2558f90eead57c2f2182e2dc4199ce8eea3361424b5ad567d26af2e304e8c9436384215688d561dfc38195e774268c88dbc1cd855813469465d67edf1f862f2f5974833bc4c27dee66c27440409c11201ca01ca009d3b158412cae86c5d9b0264ca7a3e3a3d3d2d19930ddcd54f5b31d9e1edce758dbcd84e1b81faec5f97c391607e7cd082b13b130114bc412ddcc9898979730fcbf2f8b6e44749e5220202020168bc5d2d1d1d1e96eba9beea6bbc1fd21ce01cdefe3808eab08c83ec124458784bd4e2f5538bc816d60ecd2124baace39a97763e5bbd460920e12593876286764d329b30e2f5995a001851a042d1b5995efe2a9c293061a00ed59e489943e3ba6d10c9c32f3ca4126931d58f9234c291c81f06460e5299d194062ded3e92737587f811362d015c1c2045814c162c698c9af3cc17221ee5afccb0e8ee3b46da39178e366a557866911e8a61c2b8d84ae5c42cb81e5b21b3d4b48231eb025d9538dc89b2d58969b8f93558b493353dfe3ea8e6626fb6be963f9d65d8945b7c670bb6601ac5d5ed48134ba6c9bf670db429a065e7ab3d99d6c30b6382f5a6bac97b44a0905235e09ac8c316659966559966595eb32e69c73a6269def10c4ae2a6f08a3cb8cc19fc082e8092fa09cd882f6440de909139c1a085b2ce1c2085d388207070b430461a8c20a181c41054fc480f444153409321c78820c8690042e244187081240f891c107b2600227b678420bef892d4448286d618302911d64e1440b142811069b1314410bac248e2843075d24f901fa585ee8428c2b64210a3308810ebea8e9809223c22003082870c2e64ee4e0bee2084d4c011b421264702426d81713e7051fce0e48da0e46385ec1c58e86238252e774bf800aa3304213052f608273c113d210b617844114bb60c2929a31961cd1f345fcc217a8700310e0400ca02cbc408b5292177cc044b5888922d33829444ea2f68398182a4a4740a941131ed066f4b800085f9842167aac2032832ad840490f7a10460d6e78b486f36444b32802d74413d9771c26d0a004f8a8108424216041ab0c1e0514e1044d6a82f063b3842662903561839092264ec0c96eac0525c07d8e84e3155c60e1154126663c502cb12581e205251a2c0945e1a4834c05c72bbae8c011a2f6839818222920c2132536458270051222d012b670e4270a4048e2880b96182571010c6a5023acca48c20c98c08229e458e1c38e912435c042055bf0000b5113be22b4600b5a0b825868a079385ed1e40976322704a49396d42706e66318569a188661d8b10de3b01f260642434ed88e9c62c71969e442df36d141d1153fce6659267a3c62731e29a1905e9562387afc3204e8e9f11fcb23277aca032010640239d86b4b41b0c74da034b22321a4911dc570641f47d6f352a20894d9820077435dc112a0876e061382e5b2d32540abea086ce90616599661599665590f9d8f53191dfb955a73a263fcc0e0b26e2cd258639c36731ac1f11447eb9f06eaa05993c99898979730fcbfcf5a6c076a43690fcba785e5e8ecf05cdd4c635de50fa531a358c52e0c03bb060251159b06142c10ddddfd45ecce00be24290535f6c4b26b5ef3026d8753ebacb1ee9e57adb4e71259af62f7638992963e68bcada47b3a9c734eda2c11e08b07f4999c73621798392c3beb26aa0c54e1c6ae6a23ae3e035313a61da6af1146bb9447c5e822a93c06e64feb1ef0000542c02a9cc29a4e51658cf790b039c33493b2524a834c91e433d24d329973ce39237d965936e5af0ccfe59153cbe608e67a66c134ee88305fce01d3a7a2966197d22458a6675f5a682ac15a5f679db5e79cd4524aa9e4f024829b7ab1cd6b5e5536146aacb17a29b097974caa8c466059af959992524a9b06947e578c1d1ec340db210d494d66f232763392528c088a2b48e714d78eb8c2d705e6ebbaaefb7518ff0169ef466073769d070e5f59965d57100e7fd7955d1a88f2713d0bc2e19a51f044f016349858f9f8fee930e63e9009c420e04c0408787ece6f7db386fb1de670087737c1b14f2ba6fd9bfbb94edc59a63a8c1aceaeef1aceae8ae9ad14b3988633ec5eb4629a584637336995b45fafebbafa327d492c33e9bc9e2b9765199df3d7cd1d05ee67ddcc3c0cb29c18e0c6bae6e99e66d52e426593a0298bf8b47e805af2f4b0a4ed82e002001c3f223c8b7009d806a52d6983b3bb1bcc73ce0cbb404a678d8d498d04496c34de50639c51a38de80280d0b30e3812216681e31270dc02c7cf99045f555479bafd071b80e7ec64f7e3b5f3f128291561a08c2ef594f6051279aeae486c9189cf920e594a7a92f05441c5890a2bbf834487ca981c2870983042e7121dc66f5bec26bd349060e5a9108a670f374814e950937382d6fba62aca1a238ac02e0cbc2ebd402b64024120f08021a6100b984174186fe262b646b3d9dd9d4d3a35307f174e8ece4ead5ad7b40d8ebfe26b1ba1b1e79c738259d3a4a669daa9a6b57e8082a6c6d3c3f2e9ef03bb1b476a9aacc1f1f75e9ab570fc4f3237023431bad81fb1398f011c190018624dd58dd8bb3c6ec867aae823401362459790121518ea89d828bcf77a7b5578519409a650c0b1bb115ed40d903e015327e04d763d324c23cba03e387635bcb40938765675fbc7bbaa4b84772113cf00f17ee3b1fbc10381d467f8c6653aacb737f310310d86551f32b10acc15a4c170f81be0908943b09d7498fad67136bceb668800c0856478c7575f5d1296dd0f948fd0611e3acc63bec98e8798ef808233f5ee8d1d6e967887e370730c02c62154a416e9904da8a6431c2ed39b6481cda11e27bda20ff5c41b62ce739e3355f47fec6274c141b2260c8fd3bb3c4e1802bce571e6380106c003149d763fbad7ee0798cfae470ec7e15b1c2273fc7a55f7ee0d9006c3378e6fbc3418f6c25037a3ba016689bb1e3a9c4696c183005ec39b6fb8ccdce14601dc3c04ec9f0e53d7e1d260d88219e61e4883e1fa1c628e03d84c3a4cdd23824d3d877230fd0d371b816df876f3f6f0366e96d8c60370733ff101e631a9cf8e0718303464e29afaea963000ee8e7b84e8421fca22ce6003f8a9c0d60072335f0b6c967886c70e6978ce360f8954c2e6c864f3381a6f503d84044c9f3b1c433e98621155f4b97a5bd81ceaa13e9149ec50f51baf9706d5cda5f06689c36f2fd7fec615e2c5ce8440bc976efdcbb5d93be9374021c28e07115681a19ee8426fefcb6b68c17244f8b0db4960fc8dcbf4d6f9d88edd63623b7a217ad53e1da664b7281399e1a27a342622a424bad0d368a006cff01994d8d46df4a06c2966aae8a947a9bdcb73f8400a3c4375fa0b49afc21fda8375802e60ce66bc05cc56063007384375bff0b284c186dfd12b1ba70f538fc1917aea16cc5fea1e98fabc71251e7dcb31e0cd2ff7e6f0970859e3661a37779219375b969b230feae6d85ab959d6a8dc2c71be9b254fcacdb275bab94d2a4098966e9e00d64837e620fd243ee9d5751ac4631bd5d0820de14cb1778abbab20d852bdead97904122a300011ec8141ec2551f5118758a80a461c52d221fda5c21641e00210a6cd049e3a1dd2504f87f452847a423da9774861280f9e78db1de60306663d51459f65a11e5ac16c433d2b3502e804e86c3195893cfbaa6d1dd6dd963a9acd796d9bfc9c735e122588db83018018dd0584ae100f80c0bca612f292b39b48b84b80db83012e104174d8548273878253a743198ebc45b7d7eb9697243c55ec20d149c253c5ce44024e9d8d76f737fb450476931cf783b76d9352ca21f0bcbac76e9e7bbd44f0bc422aee38ee66dfaee64447666a9776a8043711b48f63fa4f753f87965ecd3975a6aa3fa7ced48913bc46d2b3628ba99c0284e54f97e9eff528a1995047ba2d91acb5329d897a8b7134baa854839edc969834f1ec9cde679f46565687b2fa70613356af8ca4ccb84aceb0caca6bc8c2e6caaa711a19a9d5abae49afec8f13a05eddcbd72741bdda5e44df64e755d6b42140d6c46709a9f563ba99930ce849104b7a60c6eaa9846459e2c3a4c35693ec2763f520c36a69a9a7d7b8f9346cb0fc8c9b3f969b332758febab90575730b5eb94450ac72f3c60496ff4e5f89498bb524746ddc7bc82d0ce9c3a742fb89c096be1fa54f13ca870904fb0429a6271d05354f51f311934229e5930ee5b4893652dcbe36e94e24a6cdbc0181bdcc84993651253fb2de08c72c9294813b07192bf3c998b43a6cf2e364dbe9db4bc0607f7a4599b0a43aa94cb295b3a7cd54c9db4c988c5565a6854f077083ac9cb1b0fc290f9902a44203acd4216d9a1e3b1e38ecdc56fa388b61114b1a0de05229e2524d8757119425d8f8ee229919e91513bdaabfbc2ca7575892cacac1098bbdbb77efb846eb9b3953759de56349efcac2137b28bf5e32729de4c47a67d2ab2c6462945b5b915850a7c30b090a5877588ed81c84c3a573e7aec4a28c9660afeb7afb7478fdc6c0fe7ad5c9aa8e5775aa0ebe3c9c5e79bf5ea1e895fdf59a6302c33a21aef29079bd6e116fa0c17006e621f37af5893764200d8643e0925e5d95d5e13577264c6545d5f51d7c6961871d76d8e13b649939f3aa210b2bb178c55859132606bb8957bce215afc8c232def8d8b06a7a5a39d815af5f31c6eb25a0d17683e54844422171b72592c974996e1a894d328dc51863bc1c8dc473939ce763433f37ef9f76d22ba0eca6574b6446321e2c5f9974d5a948ea0ee912a557a6cbd79eaaa4b2ea92ead3abeeb936c1f2c7b2a399b9de43622cc699f84d765c8624cbc9a0c8a6c874b29dac8a8c274bd2614f873253d22a79b904736f2991da6a7ab3e950665564507498d3e1141dea20c9763aec0cc70a3bc1306738585e86233673db8d56b3c52da3742e51d24d29a594625d54d993d65ecd896599d64a58180bd67ddfa569217995acada16ee648ddba4749b3ea63ee9f7b4ae7f6a45a91e43fdfb192d6d9928a7492a8c29e70bc428c26e01c794236c82e2e963085cf6699b5359e7081052c99cd380b464eaba2ce0c183b34be275cdca0c250240c48ccb04f94a860050c3e2c4f7860029520d493263e3178569e20718294201d95309ce09cc2e0f9c248c20a4c4140296104a124c6290c1c908074630a03c7042863d894c2284112232052f602061949d8cc669cad99a2470d1214fb92797102ef25e665474c194ccc9401455b309e50021d2b96d80143073740603c6102303a8033e3859e2fb6b0c40c188c7cd104234f6e5430d3859b2fa86062860b3b5f48713343c6cd172b4862260c3f5f78e0043d36c002a2c213335f68015511c54c1835402a7862e6898f8e2fae8a4d23d785553afb65474c0e3a70b4c24908a2c08556d7d362a00077bfbff0c205672e39cd851bdccdd38225ce26108ef12d2b940f357b7dc4281fe8b1d3c730aee25951157db296fd89aa88817802e8f86ac1f406a977023fd644046b73a5b0b789aaf92cbbc06c373a42409122178dab0c3c820a628581475041c85fd7ad210b365b3c6d70ee1afbd5643986cd8401e2099da88a3b31b70d8f91277aa22a62201d54cf060c6cb6980a3ec0d900962fc51b68c0f23528b1f96535e30d8de5770449dcff2abebbbef40ff13419d081bdb40de617ccfac12a769d4d6577d3a96937bac4635a379dddf39a42944d7bf5bf5771d22aab3652c1573ebaf4ffffb4e514fd95059a13f6c2f10a2013b4803a70edc05a1cafd8421846c8b62b50a1c48c173757c831c115545082192bfc5ce1034af878c1052578bce0e2a60b2f94a860860b1f2f729082991f235e1869c14c17412f3b6272681c1e2b4605c216b8c03973c1d18a2bfc64ac2624cd2c7e02d4b516427072c9ab9b99320b210880b6168ce0d60217b8ff6de1481696e0be0e234e664086d5f1440c58a8020e2721c0f4d336810527303da5385811348a269c391e2c8ce00a59e02260afb7bb496096989481c05d9659fb7df9392482d35d0188b4e781281fde4fe7c103e989e06dce8e07efa5cbce87377a8740e04d869a1e47db06e6eef129f794c308b6444da37975b7be7473e8909ef49d6e9df7d82370045a30db2f7f1db78121f08b1d0f419aa5d49714763c1e469fcfec6337e3658947398be1d9a69ebf1b22119883bcdcb8366eeac670c19b87c09e2813c1a3d0bb4ce720b0779939ba127b1b58badbe8cecf1c327a28573b1f8d475ba6726de5333ee32c47d5b0df6eef7d13894a17bd4bf9057b236fe48dbc91e779df4657f4d23d7d93a713e863f4ef2390e5327dbab28788b9933e8ab847d75e9eeee85a992ea5fccb5ce6b0fd4ea5d14db7a543fb94fb5454894e25d1472d1d8a2e33edb5ef6e2ebd5f80db5fa62de89dfedebba3796b4f830c9b68c5155a58eec0d2873b7ae9662070e9b5fbc1fb28cbacfdbe7c5cb2cf212e956427026924a600101a892de8c39ef48845df9a1b5dd1b76b2de9ddcd9c6644b77703334a04168008b68f18881ce40557a006b660ca3bd3513e3b1fa6a35c76a79ca58cbed153463fdd340233105874eeda92112cca43fb2f4c99f6a7d0821908bc957ed95f5d0f12ffdbcda35ff0c76f41203f0a0884c6ed4b37c84ba492a844427d9fc874292c9d02a62d7cbacaaf9bce72db860a2b31e5a191e870d2b43c64e9d564a14975586f2c4290125c6f7d49c483f4be2cf7d1653e76a48b6ece300924827ec5d43728138a8585c5f4aa72539b2e8a544dd474691392746822a2d67a520581d06798f4a62450d6d38c53e9d4046622d81401fa4d745a6b251981ebe91e99f1add4348ed028b1743867e4bbb0d4d37f473de5f943f5f77a514deb5751278252fa150a02f99ee195eb3b6ac807ae804326be4014d8fce127b89bd26ff13b81cdb3064f1b8260b30d4c8ffa7514e9116721159b6eff953e4b4f297da5f4195f0a2caa07c5df7d6800e70ce00f8cd105854ddf48d76636dcc09248263023813e443705893ff19b3535a7cfcf22bd221df5c19876e0958f25a58f859e812d9df4ef32c1f9baa39949790f893f94caca7b488cba4cb70df77d9f299f97467a75fabcc49139281f289036dd74599e0a5b4e7707aa91b81fb07d03b64d0e19d8124802730b102ca9f076f7a5c31c42b36dbe960ed80bacf4fbe8f7e195b6f1b38465003973045b4d60730cb673d2d6c2662e268b3de3b444708c0c0c2637a5a0020b98f9c9811364d7ae85b2a99ac084c91ed240d4f753c17e888ead982e88e66104111ee1c523a7123506d798a9aa8ff4461471123281d3097c406631f5130c84de747a4fa79b4e7fbaf4d7a5f4b18b725641228117ebd775786173ec993ba73b7984e8b02e2008206a0f24148974fa05d4abc656dc5c3e4ceaaf26d7cf845179fdf5a4fe0abac6e855bce20b49703d56d32b89ebb122f5980de9745dd7751e1a5f32321d56167c95388c0273b1a6aafe0aab4c8735c7e42083728760cd672430dbd33d1f3b87b8a6aa9ec014d2af3b733aacdfe52750afaee797acded3a6e870a7661605cce14bfd4622819a4e87f52750cbe9b05e0625b41c5c7f2db97c2ed6119b2f16f72b9f7e7d3bcd9553de7e5df77433e9282512e9a35fa7d3e92730cbccd1a531029f4ec717cdc4a7232bdf36120a3c82022f9fa95ab9576baa6a04682e9fe8722de9b05eac0eebe9a5938ef2eb843222fd7451a352e927d209a57447449c4ea76f271008e919de4ea4a38c3e64e2119849200d864fdfc021139f4e0003f4eaf4fa1e80e855e9f541f40a65f47a217ab5bd76df2877baa66f9be4aeae479fc0dc4f4ca7dd95812930fd9683e94750607a141c4c5f6202d39f326024034b607ad2bdc1b4ebd1f802e315bacafd6a0eae4fe96e8eadd88408ea4d2020a49f6ee8a71bbae89a28681fb3ba4e8fdd7592ef4bbe49570213478c6dd7b65ddbe995406461205bb6f18a2f28c1dd49f4f202f390791a0c979ef2d8fdf0813d74583f64e212573abdb3fbd19df4ede226bddb5b96b039c4f5a257efdce8a6d3af6b3abda7974a27ddeba64b8361d36937733281595e6696480f8d4e2f3a290f99985ef4d1a5c13005b37d0fbde22cbd5dc4d84337bb6a965d2c5caf8a02b2f963e955c459dbc1f555e35931b2f5e0b07c4c79486cd7cea9391326620147a75755f42a2ee955bcc20b504cdaea02193837125cc4c371b283733bc1b995e0c96a9f6ee1f94aa3cd44a25792c312ec9c19b2b0b1c31d1df6ed1838a7733a9c6d63a7b5b15c186c7ccee80d09b698ca590b4e61165cea1eb19f09d3813d46abfa1c07b495b0a0104b0d6e1bdc6007c5cc88043ba8c33e473b88be87945792a01e9c6f4b1f4b87158a9a5371329caba9cfff1b7c051b3b94427af6181dd44f72d4d8f929053ccf52a9162c200a86af2429189830670d5087f3c93b6c2909a1b725db21865d1ff64cc5059b1b68d228e0f98944bc41e29965df57636c4a81260d1cb5587204a6375e6b8ba92c9373c2cca067803d3e0ac15c4604d3ce87794c3e60889c1dd12e4487127b83a80b3c127a4d69d74e55fd3d818de9b0d6cba0034b8f8110e85036ce18c0580f0c63af65e007597b81324461f34bbf3ea62a0824f2006d368241faa1c41e4002e72c9ce245c8c47285525a4121934ef92113cb970d6c3195576a30fd579e602b89447b72342c8574905c97e7813dea3d09b0602015943ba004c1607b8e60e11cca31d2abac02847182a88c3d459898c248cf116360161c7b8e68610fb470ecf1000bdb80638f0798c0a1291aa6c80c3c1d39146f59d6cd7014f41e3e76332a1c5c3dafd6476c6ba80a5f809e74483d3013c92cb85a0bc6baf180f2d1611ebb202c8f19de436298cb34e5a155d01d3a05d56175489768ad0e29939edb9292241dd26b55744871a8d78687c3a5911a8f8dabed8494784ad8102bd4a3eda838c12d2a4e30d580a66a49c867c214e9008f1f711e33f04edfe5f4ca86d37753f46ac7e93b9d5ee170da0280875abd92e1f4fbaca43d51d5f9b0f17a95a05e5510e5838e7a1da0901fad0274753eaee778ed6672809946eaf8f5aba391ad9a9a1aac03bcbccb52031bb1918e5faff70ef31caad1e1e5d0715139aeeb3a74783a88b8aeeb362e1b33d47b3a3e64621d609ee136c02113cf001ee1716d27cbacfdbe873b98da67ec06d37b44b05cd8eea657322cd12b1c8cf4ea62a2c351b9a1f4975743d5aba004ea90ca1f9b1b5e431656000f05f09ce15000cf16870278fe70f8d82a55ab540ff5cc9898979730fcbf6fb6e8f0fce1940ecf16a77478ceb00e4fd1f01b376878b6f8060dcf1fbef17abfcf5a19198cb7e3efb3f6e5e51e13209ad3218b4ed1ab9f9eeaf44ae5f41449af767a95e3f4b48a5e7da7a73cbd429dd24839bdf469399de194e5f4d249af5c4e2fe9e5935ee9385d393d8f2bc3c5e1963abc6ec3fd3abcbe6d5c8e6fdd0cea5b4733f3bd87c4392ed31bcb655ac76337b3a2a3e5d4725b6a9961866e6686cbb48d6ac37badb536be5ee765e39e776db2830163871798596e37e9b03e763d76f8f57a95dbad0eeb73dcfee9b0febbeda4c37ad46da00eeb0cb79475589fd272b90e6b3dcb75b92c1dd6c7e8b8a90eeb576ed83336c0dc4f700547a7bf2e0e56f7e4fea1e1ea70f3ec993f985e0037539b1e1600dcbc1569c12f376f36981edfbce5607a999bb71e4c7fe3e6ed07d3a76ee66c329783e9c39bb91e4c1f804bc48eab5dc3814252141d2455f46a87d0719c5ef2c89e2d0761c1f6389e4ba9c6f6a1cd99570a5b308ed4a5913804865417c7d5803aa413c061b69e05bd0960204228cea457d0bb2f2f540c208c711d62a2c3909111ce1407a1837eba6d0f9407e0a91b80dfb8116f371dd2ba4494692d480b32e2bdbed4ab1a3ec3ab91a9a2f51a50d680307da20575484f030d7fa90f5f5460eb4b88060d254858a19c5e6515273abdca2a41a11c95315482a650828356378fcc6d11ca71808d57744107d7c0f18a2edce087efe9c00f9601c72c923082b738848d5774a166db86c0048e57044181e90ed692504fafe2155d6882e9434a7a2531e591434b30e5b8193a1e6e0891d803730570bc1e87639c753fd40be02a4f7a2580d3ab00a93809d9846c629e7570790640cbb34b8e671b3a9e67c8f06caa11a6deb0c32b986d98b0b588cd0d377698fa0e17eb7ec05cf521f0f163c01c83ef02e617fc163087f839c07c7c1d60fef065a0b8eba1c3f1b76de364c783cc431e6edcbedef0deb54fdd4c71eaaa9b25560d91e9f0d26038cc5962ef3233a5ba0d979938dc900a4788890ee975088f7fc3fba6753d645eb10d6f37b932de3dfa1ab2b0a967c94a9da7e7f4d8a7a79e82939293927aa867c2c44455ea34a61e434c1d7c9ca9df54ea32a43e23751d37dff7a5fe3d7595d48f0a90caeb4d7d25f59ce195d45774be1c9b4a1d85e3f9c32c389e2d66c1f19c6196c77896876a7ae59d3eb48a9e0d1f3b1b8a98d80103d77fb6feab146ff5660ff3c032af219889e0f09ef76aaf3a9829b6b373046274f1c06cafba71698cc0de65dae2f0d24cec1de1f12da6d2660313994c4741b18172329d4c26d34d39ca1d813652c098ba9187ccc43a03cc380bd6f5d0279d7ed3387b4fc7ba03483ce3decda89fc01c9cb028b07b8340669c050412834a4681415af00c9004e6202d3886c78e01acf1fa06f3fd6b7cfbf61aa86fa81a2c47adb01c5583650585a24183c6b74b03d51d8542a1cea1380e6052f7d0bdaeeb48dd26b9d9f5a05d105374922a2a2995d148741a753328301781519799a84b53840f0c03e98e65df6487d28181c90766222d3908cc1262d4fba530856a168cfa52be920aeaad02a6dc54baa89414149851cef2d25325d1572e33576e3ccbbd8e7245ffee904a3b1fdd49df48b26340ea36bed9b86cccb8f72d555554522aa3914a0b26ddfb8c9b8934b68db78dc38248dd46dbe0b0f72d753f55b2fbf1a5ac3ce5a49b2797225da6595858584e04a350df572ecb45bdb4725552bea2024e25e40c29ff6eee164e79ca09a41d8e4a37c760cf070d04d25d661118a4057760e8d1620d6400cb205af010791b1c83623fe23c42d78b9099d9a5d7ae872e6d005c8f19988ba8745e2d99606becb6d96fde5db9bddd754fc9b8528bba7b9ef7bee5be72f38813711d67bb7b1e4af40ecca81fa373dddb07d73de222708fb8eee6ae039b0381a85c040249792764aa74ddb66dfdee5bb76dddb675dbb6795b77db45e9baaeeb8e6dddb6d91aab8542a15028140a85ba51a2f7752f141a755dd6dd9ce16ed475592664666bf1e48860532f3067d9f523d79bd04a45a2c3cb06773f32305f00085fc7ae0ab4ea4dbba0de9081423afd94556e7a49b47251ca7c1629379f45caaddc823948bde12ee23e02ed45a3afdcbc8d54462a236ee5de396efb681b157101a06da4f29591ca4720109577209094df20564504a63cfe2ba59c48a652c95a12e9a193481c47e2442828280f5d144bc6766ddb36cde3bc1c9cb0ddbd9b83d41b118db2fb516f303712e56a83bb7ba011596fbc9c0b00d59b7ac32d81de2c84c54d03f3755dd7754ddb408baf2b0aa95546489d528c61b37fd7b42ee52ae748de479b774d17914eb760a6391eb2824d28f9582412894cdeed2f33bd5c7a7cca51bceb3d7607f0461799aeee07d16d96590ba2502aae400d5cba45010b400497c03c04b68f78083cba57f2523eba7974d328e51e0824e5282010d23d21157bb715a780229023759b0ac769dab72ddb344df3469a48f469e0f75dbb9f1cc34a7b29a5fc107854038b6ad0a03163060b0b0ab5b2a2a2f27d2929a793c9542a91482828a391b59e2712751dc76d5b28a469598661d7552ba573769f9e920266195dac28ba4429bb0210f92bdec0ec43b4b010fa49138fa457a6121b5104cef142fa9212095fb0a150e99be9dbf70ff5949f54bec2dd3bf7d137ee923eba568574dbdd721cbde828a291e824d4c5715784e22e0247a7a41b81b95fdd0fa35b548772d1233622a33ad0078ac80823b03d7799c98950248bba0804827a070251b927a4310a8c1d7e1fa93c7ee5a4f299524e2793e9db4d9b356ddbe81eb78dee99b68cdbb611b759afb481a5d2b75b02b3c49be56e377b6dbbec66eca6795fb05dc8bbbce78542a15048147ae8dc48488b4e22bd7b776a45606e2c7a5604663908ce23b6cafba843b9646012091c224537e2504824a25e26a21d769769eef2a1687186dd5c8411387e52215d13f1c26e0ce52270c51a9c00ce7e15813330d72af10f1eb25f3fea2f4ac19ecd822dc8e6ecccad67233d94b2f2d34ddf5546f7b6d145b9bd5c773fbc8f28606fceb0dd4497fbe6237628baa3d17d742250f4f802acbcbb4c6f2ba36f20f5bc7f28f73ebab9083cfad5fd60ff6dbbdc37793d108890892920b1e8deb90b44c8c42290c6478feedbb98b1ebb1fdd36ba59b4ad1c05e47eac7c0381acbc03817cf72eba42e60a68bfc7ab98bc2fa5f47a0fcc279389e34aa51277ef9b57d2bc90570a7123ef9c4722bd921ebaa17ba79e97853ebb192f943d14643910c876eddd69e763e336ee02d936efe62c178151e75037b6aa3b77f3f551ecdcdd0faef326127117e5dbec7c8cbaf3e1719cb49d8fd0bbf5c0acc3eef21ba8598cddcc1581630d96691aac8c32ca08c242faeb2e8c4dd368671d98a57631cdfe10026bcf3afaefd3b04d7469ff10222504cef141520ff5ccc21323a739eddab99e6d72a10e8572ef28ef6f28f7f496259bcd52ca47a78f7e753fbca3fc30757783a813e8c30492da3ee56694a37c275d1cca7799b6defbf49485c0f657f7437f64519e8f2d0a8832dd0333ea07e928de47604699401fa41116028f6e517ebdd6ae0789bf7bf7bb05817c2781404ab7416ef6c02029fc81a5c7a33a5be256ba8ee338ce7a96b39e4ae8aa7862d84b6ced66af97c228f76e16027b23b06feaeed1a84d2c2c2696d2bd9bce628af7eef5d2cd4152d8be3bc9da4b7b0dccdcadbd9eec7ed8be281fddd9f9e8a380b1436b3b14bdc1548729ad67a6a4fc5469af045bec8de523628bbdd5d8626f372cc182261e53f954c05d022470730cf07064956009892915763ececf8328fa8a354cc138bb035b4ce5520dee77a4af998d8d9dde1cf50d9883a4c2b6f1802dd236a9173c44939e37a41e5b0705e7d6e91bc84eddb641c459a2c336d2e1e49955cc1d1d47a233854d919a31829e502374097a230f45877d90e248ca449316139f25f3b14340e4e9c00ead9a1a40da9aaafebc39fe103182a56ab272880b7533df552e2955ae7bef45cda8918244678a28db06f186d4fbad136f101dc15e6f726320abc8f04c323d2cd82983dc4035beb099b278f03525cfcc94a74fa15fa04c539f18ae182e902ee9b06300290be7b813bcd3f4c5696bc2cc29faa2926219314de30b9c49a24bb390508265c0f1084440c245bc6103f1060a48524edfa4953414ad67d28bfed0160c6c151389ad9b9936b3484d8fd141734e39e56396faec89e11dc34d31bc14c3514eab00dfafc10436d3560ba8d6b024a64d7e9cf4ea49d018bda2e0eca14d6828c1d21f90b63aac21092b3fc1c933773aaca2c39ed771354c5be8676c5ca6657879195d5c543870e4c09103878b4a9583ba7c86db387dcb9d4a5ea8c8b38787a9a4c327a7404059d22634d44987f4e79257e60cd7da84b6723c9c3d53c9646d2a49030d2f34bcbcfcfa0cf4c2412f0a87a43e98366116e9904e2c4462da8a2efde622baf4af0ba4970829e5710099cf300e97cb3b7f4d9006c32ec7010e99d8059c361df675dc0beba8a1d3719976f9a43402f3d771dcf8bcb37304e2751cd70d298f27963aee911abeb95c361cb101a43f53d5b8864b81a6aa2340437fa20b6d82437e3ec7556f79a62dd595e3f2a2ae392f7348d5ef9cf3870902c9f10c8732803e326c23c75517cc39401a0cdb78080e99d806d863a0c0023d91df1276be67f6f40a451f9bd09f5ea128485b11ff4839031bc3c1e7f9c3e2805853d57d290bf73d26f66a1c366ebc87c438be852a99ae94d52b1cefd325bdbaf13ef5e9958d778ef7fb49af540fdff8ba725cfaa3ba34bc14c7a54f2e0dba748c147851a9a9a4c39e2970f67498430636061004730bc67e6ffda54da8934b81e8930efb332e65b9748c5b576eb5a9371df6bf5b97e8b08613db38b74e0a485bd7963a9c3d26b004664b0251c0106d499f0e1be35a85bd31ec4604de74dd934b38774f7ec1b9812a6d112183bcac66afae8b5eb4ce473a1f6b7a55a469abc39e3d1386b6a2aa9f65b307f704b3a5ad52cd4baf763622b08d086cb381c7e6effbbeaf3f6e53d283babc99cf957bed66b26ba6e6acf6be341a305d1a895150b650c759e9a4d5e1920ea74f0d26b0f9c3614ccd0e39067e94411d4e15252795cb744aca6d66f6433dcb6347b382571e314551d447228902a56da8e4ab576b93982a5524020000004315002028140e874362b170340ff45cf60e14000e829e466e4e188ae324c76118669031c61802080000406040866ab801c0f5ca7f2e122ca5ad7fdfd32c7c813cd155c205f4177424226a9f2df5c4c303deaea5b21d67f4ab0614f86ac59d4b8f602307e455c59e16fc7a9f7a3a1e3ec1f19ce62dfbd434c900a800e21db66ba1c1869a2e655fc0001a0b4cff8a32c1ea02e562db2baa4d10fc5818e04f007fe75b62250b23a5757a733a119ca405c28b5ee7cb4b4539b771cb2f478343c55bda17ba29e320e8dacbfb1604d05b94cbb2b3237ab26cbed908b2d33922f3a74413144705750ef692a5536b06dc80a3f27159753bbd41751979ccadf7250fe12191914fde4b5195037248124afde92e654392d41f97e577c3f826646e24d216cad09b16175d9d61fe6d970574623fe28a06cac79db2c249696ad12e65d2c083e569fc16a38eb2004f7781bfdfa44236310678f400614a0b9c5e28ab5f0ed976f94edef951a222e07aa13999c1695fd191796204e6d7cddc057e71adf07caab029f10d59ee8f6715f4a67bb811af7d17cdef6cd6a12d3293ae43a3f1ff33962f4f6bbb6a7275bfcbf0349e3d42061a985f34b9264b90e96907ec20513091061618c89b334d2e829669278fb2175657ddaf8f9418201421104fefe25b55b1ea083fa899a7203528a2396c02e53851d8cad390375bda1b190c312a305e2e0d4c209531d6c3e0ba9c5def659ae755561c277a22a6ce711f369bbd50c03d677f5e4a681764146ed6e885ed922fe86512165b3766b548f1d65db01441ecae1d00094ef2702df2c11da873d8d45f45ef5d3d6ab6cb6997e71a1e57e367231cae28424f1dc02e28f29b9a4f46266657021c3fd64da34a98e2998e88974ccde34e5b83e62b4ffb2e3a79691769ab604c0cdb68526b4893adfb82e4eefbadac55c6569ffc5abdbbba5db475183abcdd685f3dfd4ee03706eacb52c170a943e3c1753373d620f7b28ec5707581edeb2e878205d6254e108d4dee2d3540b94bb73bb38a2bc5c6f7b5798447b20006647a7382b61006d0a59983ea17d8e2fe01e76f7be761945b8ea02871f8887467f179e27e18fac718d4bee627c4a2f2548e920c8421d63276ba4e0056175033daffb5d0df65ac3fd75e1797b79b1b643d6e20787a231ed99579e37dad4ef19ff157f5f1ac44918cb2666f0be911adbaeac7fb2ae82eda0e2e68bc133c19901a37f73851edf419f044f81af353f0cab54d4296c6d9f4a042c020103278c7cfd6d2b223aa4742339c2342f2f95f6bffe1cd91d52fd66e4371c897c520940f8c9bd37b9010c48a001d9f1045c0228c990143029dc05902a2532bd59241c28c598d3346c07085a6cc6471f0ecca01ee20d0407a395867c024731a969ca9c5e1c8c9e6d599912c942c09398f967881648a0fc70d91ac0b78a115f569b8ebcad3e021e336ddc7ca639b07403a07a0f4ba4e8d67e32d5ad818d543f878498b403d0d642ae3d8d30c7e04f54a7051418226aebc5ace063a52efae3e90f49e5fe6728c3368c6cf930046618e923a8b6e6b11f1367563e6a764f3bb748b7acf1b1620c229f442a01a65942eb783cde74a85c868980df634d087a2006aa3e7f31962f8d8f6d5aeec2a4184ea89f16327251042f80b63f7dbc690cff61c0bcfe528ab94a51d39420b3ce531a07e1034858e8126bc2563aa76f42b9a7577c26b454efcac195ce260d2013fd089ba349a83d64bcd2abaf398de7c57e5929ecdc9b868a66062e365e8183744de2394dbb9f4174f350f041535fb4b391386d5f382082257694a6025d01fd897a677c25ddbc0d0d2064e59ed8188a71ec34d32c68eb8a58c2c5b4fc5c17df13087382d846d2672c11dfbb1102e267313f2784b94ae00ccb14a859768fb3918e6f02bb775b1bdab47936d7aef604e2d001fcf4ca0b752bd98584374464ab6d74e221cd74e30930dcf9834d9426de174e88161be3b6c9cda57b077aa71a494aa7189986b44d35efb3f5a1e26b2ef1ba3a77db58f196b96ffc5caaaf6c62eb90638c446c898e48c9a15b74f70486035c1b4129407062092ce0f8328251280f38e801d0f5258fc7f87b366679db9a548d8db35a3b727756fceab3f2a46c2db78e4639f95a4487037be87905a48fb20ec8f6417c13de47720df8b603f92fd487720df07712fa2fdc8ec23b9877817c41d917646b213e91ef27b10ee45b61bc96ea4fb90ef81bc13d16e04f6113c61d1059dc20510945adf9d477ab8460a561824ce9e00bd6c24e217ec6a1ad130e0e7f585945bd061e099bb4c3b10037b05fbd39412ea8f1830afefa993891fc783a99c18c0e4e4eeae8bf8bb782cfc6a0436251934efa093d671157740625cca49fa165f126acc3d76300260f1a6037b87ecd0b4775d3f6e0d51ec6966f6aa9b9c91e2f9bb3d51712f6cf4189e8ad53631294985cfe0f0fb86278f3ad97bb75f15ca0843da4200d78b028cf35e7c020a834b8c1148b03c32824ac6769019989200506d307b1ecba1589b56caa3c1fe31831a31a2ccd5d19ecb0037f086b12185f6ca1d4dc46ce1ffcba7188634b36113bc3824a5c6444c7fd5b84230ff2e756a89389ec3d5e5360919528970f7f99433d2a6ad5c965061f4e08e27e2532ba25ec0b8d11f80f743f87a5a42e38d14aab48990980f0225ea9a256c1247ca192af38ccb201838546c096a9ca5c64b4530283fb35659782dfe05f9b8c513540479943c63689dbab4e8dbecf7b99eb74586d7ab79052b95d6f13a34259b110170714505e049beb37934a8d9a30af31c3166818c58a766a8555dbb384f8303b999f4fada60ffdb7e3ebf6bf862a2804cd070b15e64cef282b912020f5bb9fc8711ec627600012ee8006c34b04b0c96d730f0a2d4092d1aff07153004f9f8fbfe6e5fcabedf3c75117838ef51d0bdc70726760306b45c543e5ba909f04608915af24e16ad04a96d32b8894becb28ae4d93c3c36d22b470352a6587aee717409d6b7ad81a9a8f165541c043cc5f9bd0a7d7edc8aa6171964de998189253fed1cbcf358dada9dc77287a3d911f32d9b212657898df775c310882984cda136c391cd8865aeb59a9e0aaeab512811caf5699a7b4464ed569258d40131aa5f27a1dc29a7036848e27806b25e144b5b32acfe3a37e45e7a408e6d6ab9524610d226573543798b8aa0178913a054f1e00247f1d612b062f53c95ea9ff2dd09e44c6f700d75f3795f339f778a30e4028c34d36b852d991bde826c58de5c1e06955082841eaf02f74b58618da7e555ab6f67339681a5066c7b6db22917ffd934aa1d98c368eb8d9229ac52b85771314dccc04cbb4c8f1362336ae7e85eb1b09ceb656cc6acef46c02150ce25f7ac1c6096969168633f1a7865f7d46ca4d71e1752c75604878b82722b309dcf88f1ae59ef68c4cb4ee5ac47609ff34ccb3aef19bb28da30e38bb73d7f94b90ff3fc5d278d806ec8a2e8e947d9726a667ba52e611dd957aef47eb606d643f612778057c7ab5f82b29199ae0add0f301d32244c63128bdf39db7572aaf47cfa5ec1b963c028abd9a0f4d6282cc176cc21bf5adc243de7234ae2c27cc26add9e41b8e4872b6d2b80547bfea4af3bed414acf3bb079092d1dcb3c9cc540eb59d1ce649b3a2b0b8f0f0b76d65826ddbbc78e8ad88036b72910da362b1fa10b4588bcb0c01009270f8ae89c7f8789cebcc9a6d70d3dd90dc8fa5c416afcd469346f38e7f22f9e88bf92dac787b74347b6d16bfa1f4decca869f94cdcb1ee8646e414ec1768eb25ac69881e564b7c35dd8048cc711ecfa887d432a0c561f0bec9acd93aa2d32d48f45a05bb44b08c34507a7469ecd93d5dc1100c27dda389e768d4535ed904ec6349ad669478cddbcb3629bfe2f55735e7c8e7755de67c56aeb74ac76aa38b0971f6d903b76870c29d1aeb0ae4176979c5ccb1b1543bac86e5508f91df47c3ed8d8481d6ed473b32f0db2fed71cdfe2995c534925191676e2c9bd7ca242efd44bc75dbb557f09e69140d7eed60e7573d95de11131367c6a998ff163b2633eb4c2a47f12bf9b830a2833ea15f89a6296335b0a491afe9a508a45e9b12668ef69771ed24ed2e2d54f97ae473a213fc7ccec257bfb690c4c996c0d1f1040ed42bf323e0380666bb9e3fc27705fcddd313f6f87f344e30f5a2a4580e8552ecbb8a0d99c6e384f2e52977ddd1d34d27446906631d6967f85195a9635a54ddbd4e17514b5a77cdad350579eb44a14a72c11239b76beb06478728b627de0be93a35f5e5c99bfdee672dafcce3732a13e83eea043a779d2ae47a08dd269f01092df749ea8eecfa0dd64929c0ec7987a17b21f8d8015de90f6b40f82f43a05f8f752a36387cf92216070721494ee4894151aae2ed009a420793b88a25fc712e13cbbc9e1a7b26e87c09a31d7a5670bbda4b66def5fbc4075feace7c0968837d0ceaf37a522e9ee5b7c52008a1d49722b54d06faad06d630985f20f713816fee03f98662d3c10473419cc94ade3df299757448cc1d59a623bb08436edffe53cfab331d5c366efbcd081bcb05e0ea3d38c06917f497ab26e6a78b422a49afe68f947188b6404b3929223102cc976618a6a299925194155af0f5d19b9b066e3ebaa3d55b9e48cb85c38210f56c5c363f3a4a47b98bdb4203c3e9c30bd79c4cf8048ff67ee2eca53142c5f391e4c8244af47eb1984c856b9c58be34ba2229a8461a469f377d0a44dcd6dbc56afa6a98989ee66a34040dd73bc857871ec82f34f57266c3a23524b7781fe9f245afe4ddcba4ecd5dd9d4fb7aee84e08f12035868d6af743267f2a5f948599dd0e52c604a53cbc59d109789fcc369f35c8b22b53b3beab3a9c31bbbee082183434120f9d8ad44b1e440ba5597a5a8e73400e6e0e238ffe7bb1407aa08cfa4d8492ab4d76ba91c8967b6f73abb91b3fc7dcc0ef354eda792e5893ded667f8b6411ba8ef968757ba215ff01aa2a1e78e7b123a9753b631ed1fc135425ee4ffffc4992a17385f2023b167e8146910c8b6c7ffdc46e99e99316691bc40e041627f87e05a491c1b629a273bad5fe75a4c7a91415735a3035ab2c316eae2e2f03fe4c496936ed25d3aa188511b9e23a41c8c1ac81a3dc8c001e6aae57cbfcb36375f152e5df74e933f31a8ff86194e8d0649e78acc210c5962c6fa3c379f717ec4550901763a19322e6dbc6d58d8b37d8b7cf7a5d13f7fe16672041e8afaa72f376e9ccdce277f11551b9dfaabfb1b8381f36768e6eb2eb7c20dac143edbee44869c7488f1daab0fea9839a5ee5c265190a9c46f24139f7c246864633cafefc963d98873df41326febdc1037756ad83c5c56f57f494d0990c4db2ec535d401dc78106c468ad4232377138296359b7d5490b7a79049c0468acec984aa6c3d440422cc32e79a8068c631cb462d57901c6495e8e5923790541afc225b1774ec230b5dc533ae7667a9fd5eb02916f0ec978af4f9e861c01b4a8e2a147ceeb04f01ff638f9adcd8db789911afa08e0045081e9e15be708f90548c4a992d8ada3489db3c72ba51435ee2059faaa17f56e360451d499a0bd8ea448e90827b097813cd25f511f78936307cd46e85d44dbacb531e9302e6e12eb7b39428c394435501a37821e506a3e6ec3254a2414c8634a6a2df646238df0bd1ef4b2d2fbdc0e0c1dbb99a0294c2648971db68bbff1d0c63a7a3c2a13421cb1a910fd94cac37a0de4c1751678343f0368c026308c70fc953001ce1e39fcc95ddc34b2dba12d41713ace144289d3c73d821b86670766b93d71346ccfa49f1b35b9bb5a11adadac7413a693b69814c38c12b0948ade791056fabe7e3ab92e68ff4762bdffee4def4bde5ca3c7362b5958c18809eac2d90330ac27e02b5d19b62f2e551cae8d1fa06848d22dcead61773ed3b1eaac22c1baad088938dc12f83bb5390f2a88eddcecb63369e71f86a2a76846ab0ee328a6301db96d2bd716070c20fff56ded9f33911fd66c62ab07dbcd9a0654e6152fe621d97069dd05220320488871cff804ba8fc967f3f9d6608f4ffffdc17a7832b7563618206e11391972afadfad35b7d078461ddd454acd3e9f0b1e2b24b4ab53d98894fdb0911619140f5dc5d7f6a2b68b2d03a6e19dbf843e7d1eccacece6273008c3b00a32a3ef4308a1019a3965e6495ffa5622105e27ce519b8bb8c1d8ae244b5de23dd614ab00972e97416124342e8953ca447d0056844f87b36718265c93ecf85382cd835a67a5c876b2a1435b243e02bbe00e56c03cf065df8b7c08cdd518383125da3c1f78a978c55530e03636c6db4cf79cc067d8f5b21727afc2c14049fbedfbe4242ef64e282495377b874201f057ab0f46c96077c13e0c3b0e9b28943d8daa7fa37bb8fdae924edc5d3b30e12ba6ee970ad417a3a42280095fc865f4ae1f320f39e2050205b2a2c3e03fc50992557ebac488ef62912fe0925e2c86c6a6c3be7c189d2965d4d214069714942560e0df376f2b13b843f4a2fc61ca60802b0fe2cd2044a7ed05672d693a7141c1b38e74f5f33be0fc817760023ce2d0bc0dfff4fe8b729cf8688f2804a6c35e2aa89191b6e01151f031141f8e2364ef515b06d5699ad93ba5f288907c10b33fee0594b1e4d4684692d1b267260eeddd2e3dec7270540a3c459f2179368d2966d8a7426bea6bf5110d97b688ad4034ce99c982bd48eec33ddced5a8a1a3d14ba0316e272d60ef324f5c180aab0aef40c0ece295d6eed4e5f7db0843faaba6eabdbe0e2a5335374cc6391faf86ff86ba52194d3d63021f44fa88283492216b54ca8a6741d2cf24e17e1988b3220d13a2364af08d329b79071e50c178255e1b3241d8427956842e098ae47155a44765438ce653d35c3bb5dfa4978245554c2ebba913d6342038807757cceabf437970662bfd8a440ff7c52e4f4dc795014d30ff8e808a6f2bdbe6846b3b8ed6fcadeb8dc3cdf17092866de1a7686bc254a8f20b0f2bea877382dbeffb3ad895cdc3876bd85839f2dc3a4d629d8c6cce0fa3b153417167a9a1061272d6d3dd9e41be90bf05f9602ee89ac1f5c9d9021ce990431e38d0961673d9afa45dbcd2e189026ed3a82494e76ad6e2c6de1a6624b07bfb20138a4e5501e72ef8c501f819709d193250edd13b8bc2b0277b04c037e8d9bfefe8f24e549f6d194131d8931decb1123d9df2885b768fe041148b3ab640da86d855784c091b97eb09b098c36e08481f10b131ffb877bbd9d311dd3324352b47b24142e7d89863e5f2af87ee24e9f0fed019cb295ebed8c5f6abe437c675b22261a7b661131e1ed51d9b46f80ce6cfb42263bff4c3359c56842bc4b6d1806ccf744c0df6e2c56e93642f76841e4d473f8be23381a60a0942077d8b8d69723ce2307c54d170a7f853a60b9ff49d48c52491083cf65c5a8acd3820281bdda73ab895381bb347b0299eab7936b4217e272f059c0b6e67ee0e1d537ebe5057436a40298599f8f1ebd4113b9a44532c3d9c6de6a4bacdeb67cf0ddc825299f5da0ec73e560b401a8e6cc68aa85bfa067ea5978884495f72af03fe5c2d11347361e0bfb67ff3ca455f0de8fee99fa8ec664363b7f09ec17c237e5dcaa736a6fadfee8a5f18bb95cb00e5bfb6849fddbc324427bd5826c34c7aeef3ce6f00789ca123f87904abdbd456ef7e3731e1df7a59eb43be5af621ec25ae21a98414149e7cb2377e3d345965d59ebe9c34f80caa5d0aa8c64c274b51954ae11b3424623cc7e9e280493866bf5cf2aa6680f77b0e474b82b0e862062098335f0fe9dba13a938e909536c0e27c3d2d82cf7b560d6ea51e84fccc2b105906170a1799280ca198d146c480a5f2925c14943217846a520026128037920004b87154e2c9a9360188c1da478c4f68c7e9400a3b7f5892110b80800a22eba8cf5cc99b13f9516a5613004a9d00e07e02a0c869fc022431a995479676cc2e4bba01953e3a704c48b9f23cf92115a1c4239c920e451478de01719373bc30f5f196ca0007daeed3abb989274e16af293c6e5853cc753d44e3d61a001f8962e931a2b5d7c524ca743c5a38a8de622c73205babd838320205363d5f2b374cbd7a9a55899f45d52ff2264f385b45af2d8edbfef79e78b1b4c94aaa5126298118c600093af69c5b7eaff678ce1b8a7bc94a0c7be2073246e5693bfe9c404891114f6eda4ca8216c291a8c150b7de2e212beb9923c3962cf2c27d9513aeb7065934a88e45a4beb94baac1da449eabd11c6a13229c1a9f52f7b077e4a88fd627e0d9301fffdb087afc9346a19d5bc2419f0fc33fefeaf0ef072906c714d18bcbc81eef184227a8be9858b2f81d1f4f27dadf2f722017d08c0154f577312f129600eb9383027962d3e991088b996f92884e6fd2ffa9c058192ef244e7c7da34f42eb23cc769a87ae9e4e6275e5bdc4b259fef3d6c456e79b0faa26a54e4e8dca1c5e794932bf61aa408b1e08e81ed77310eb9a1f5144a4d62a588ce35506d034ea591b99b651cfa59ad34b52950c8f81bbd02e8c34258787f2d19662e2744d1cc38f50c4aca72deeb75f4dce28c2d94aac551a1333e637ac1773cb02cad20ba2202e5c1610874bc0850f5d79e0544c00440be5786b8473ebfa88ffbfb9a8ff4cbdef76989f509069131725649582e31bda0970cf962f4c956cf42b6c8ffbb2b4831e1040a3555ce15dc7cb648dc9d67c63085b50c3f59e9f978b5be0caaab0dd9663d10d5e157aa76a64ef2a2f6a3c7b5f0dccfa260fd9325bd10644c46e2bcc1c401b2cd3f7597ea0ca22f3d7e097de9fa282912e45891e57e946fe53176dbb0a9841eb5805a9a7c018970997bbdd93312b5314a32b1e7a4d459196ca2a98b3ccd88768e6ca92ea000b8c68d2b84761aaa57c0dbf24beed3a5451622849486520487438caa2db9c1cbf8a93487a7926ff9dc6899b993c935392716e6859ae41e98bf82703fde80fb14c9c8c2ddcbc637b88a19dce185b4bf09e3fa0a01139dcc4a88a0b2f9144d245627445a99492317bf05390e3a7cbf69663dec151b224a44fd5d9395ed114b9ef0697d07a31e17c1cd3777f49b5102bc1b2c5a8b7b57174c51dfc7749a4c2ab945e424bd257f5e094920cf8a72ee86497678db2c3bca32b23e1ab46e19848bff8455ff422cbceb03b7ebc8844d816415dd6655adc71aaefa1c2dd3b0ecb9f411e33936cced3c3f3a7fad0df6fa2f1340f882f029d668231a47154fcf5bc70b31385170c46a7e51720fff3909fb25a754a24e59d10e91804d33070b1daa33bdff39516ea6470acc07ceb7f9ea543b4fd33cd6652eb9ad67349a6e826d0c022de12122b926a3a64b61866b21bd2a04187b3a4a990b3bf9e624005ccb9d1872f614d608b8f0608782ff3bbeb82f1c1be5e28d432a5f38a437edeaf00e5eede4a6a967c22e2e6055d28ce8ac364b45000fe8e34a691777346b51d2b9e42b14c4be9e993a9e414b6681c28291d8be17ab110adf73c94310b67d2672ac02a252ed7540efaa470ec3935da539aabf2d8ada35083aa4932fa1a09174ad2363c3ca405acfc0991f9eac616e3bce00a3efb080ae5be0aba6191ccd7a432730f866fa4a6835cdb4527df4d20b58f149a1c58c814b0093d06104c730ee40b5cd73e404ff1e0156e6f6d76c67bea60dd3416686de30ae5424cfccd5648fc32f567030d4678489786f29a9c3d0b893a96459eca386db3a98898662aad51b07fc215ee0a4d775aca5d0304eb54afaf35235b0d2d67619e620abfff0951337c6daaa07abae0fc6248096441a41f03499426993901592e23b77c25d9dc53d6c329dc20c9f16e5ad07998f994145f41d0581d8044040a6d8cecd85b5799e47a81329dc424633f8bb822ee7e4d8d8885ef35d924e04b8dcfe2e6248717fbf15141cae6fffaa8438cf809bf1d7c15780407ea5339ccdcaa403999ff24fdff6fb01a90f194eeb0fc579ff99315be34d22c05aa09a087ea6763faa391821861558d5066eb597f09e4aacc7e50f0c869441c70d657ff5f5c8abb64d6f64303ea03f0366136243e490dff65dba954deb4dda4f08a015e340ce19839f654d8131db635204994bf698b80f122255a9872da2bf42ffc97e5f7c73d4315c8258db1d11913230135071470abc5daf574af83108f9319abbec6e23e9571d95895e3ab9effc694ef93c735d36d2ef265dede958bf9fefa079e7022d451f3635a6b1bab3c64d11c64d13d6f5cdb963187092f1694c785638540afe51f35b35e27246bedd008f838c0013c865ec548e5bdccc3dedf1a7c8945105d727349ee01cbb5accfc3a018047823a0d316705a29e07909785932133b0cf18c55ac9077bc6b0d6938f6bbd88b0e83aba3794b88fab30f529e5c32c619e62487985a34c81a061179bfe46e79794c5bb2ad882b2582ad55ff8cf71a2e9da75263dcc9ec38c7fd2564754101836ab17af96863dbfd355b7222648e393cb4cc321b6c67ceeb601f53b3ae332bd4fa693a8655e5147e0c1bdaded47b42e02de0514231e909769811b1dcc16bf6a421a2bd4b96ca09aa69b69aec69e83505ef2b26067badef57fd0c96998a4fb8dedefedabbc5b5e6ae7ad58247c381e215a576ddbe15f57f942142093da6a45fcd4d2dbca7848d87e23514ff631f4607d1071adda19b7a078b518508e477558b1a41d8d2288257989b45bdff86f20aff9dcf60f4389ee7c50717015237bd5cfa53fb20db4c7e6dc7c15550a6bdb0f375ac5d12fa37c200a6dc5acd8308a362f8ee90d3f49b39032fa565c07866920eeb37a2e305810d4f8ecb8eb5d9f88a79e7c6bbe04eba3ed526e6fad91aad062e5f98c20eaede60b148cc4eb8319cb1d743ec98f1f142eec9d878c21436b71cad05c65a0ea8b0af47a4109af7e37846ae6cfbb8ee1854279f5de3c9a64e7982995c4b2d524cf98cd367c9feca71d7f164778f20849daa1a263c1317e2a40ab9b9f233eeb68ef7624fb275e127454b19fde6d9236842cf1248b7a7df26c21145a05e9a8554f2099510c18f028d29dc3f72d71a6f94b741085fbc2662cad3a8278c179c050d70c7a3ba8b02884e4c8c0a7cdf43be2d62df57aff75bee538a1669bc31fc8fbbdde691e973ffbe0f1c10e53ac77842bc50021678b1b5f6ad8839ef2cfecb16a475388283c30e5accd3634bf4b6be5e4dd42f46ba9b4b20372a810dd193f0e23953a4f988341b74193fd1996d20583bfdfd024f89dca7d83264518437bfc063dafd9eb73b052f9bd7d0de65b82c9eed302be5698bd4137eabe6c425d961364d8195446d09f2093139c0c73cce8bb77639142de05ca66eb8344fca84e3580c4318988312433de913e5c90866d16fade4106b294c51a83210223ad112382f0bc4b6086b94d5df8f91184a74cd374fe2ba7340d46aa2a03de55d8402ba515a8ffaf9a71e04fa27ae5b7c81eb0c9c7d57fcbc6776b599d1d5777e1623774cee2cf5e7c492653396cce178809decbc3f5e65a0ddd295d700ba2ced0e3b31dcde6e9d49b680b1e9b4dc08d6ade8e4b682437527b7323b1e41a74c761cfe735a61ffba5f0df2b96e54fffb72cb37555153eed474bc2a80b351193051206d8d7fe489978b41f26e7478c6af8b72cce64eefdbbb10b68c1c9669d4ba475d65bda332ea04ffcd3b177ad38c5f5f9d14ad920957d977323587aabdc3aaec13391113e5c9068bfda7c44c450e8bc5edf765b1dccedac352fed1eef7864036bdfea94562cc7a4b48e83350989e0b3f152f033cacf3a5c1d6809c32cc252502b71bd980eb22d9e9f0d5c473d8e9fce10548191e007d54b0b3897a8cc806f51cb59c0d753df3d5ca7def704ad1104ec317e28c55a9ad80055ddd85e59ebe483e06e46e81b3ab2ed68ca1793140e8e9124d79d312b5d890d1df777d8e6b1b91899238a36fc964e3b501ce83b92ac4074d46d957f5d03ad24efb1022815078796d1199f10d5f866431fcd5e58de04632e9223ab54b9b4ce74d7f500d3fabe1660d8abb2b15bfb2bb52df5456941b2762f00b92a9ef8b72e4f83ac78d08c23da01f0111102fa050a743305999dbc76cd0f6b6c3c23cafa735139d6010f3616907920c13961eb038eb673e9284ca4a2bdb8b321b2967caaaafe9b06a1e68a2712298f2dfad3485b62ba5ca69ca792deba5e5aafc0344885586def460d9cc37a101af17fd9a7d0f1f70564a6c367bcfbd147217bf07ff4ade2c77c86c20c38a0ac17a5316669e85821eeaf9971355ed9d587ba7744d2edc8a72f46e4db2de00f2c81570c18640fdc55ae781fc2a7cd9322a795bb0cf51a5f0a7ac4e32f8d3983bb999ba1aa2ad851bc479aafea1b1930c61aab91065400e764be10af6e1a319cd389546ebe246afc03223bbfcc801acc0d3ca77eb1bc00ed74c4fd4a26d2f318cf6c312812bea6067b03200266d9e7fce29c1eb18c5f2bc64fb88de1bdee5a80d1d9e60e34bf0631dceb2ee651c55ee76057ab84715c2e780253d92186584d5190208c8a5003e3a07d749823c1a146561d57f27a1abfe2fb856d9be4efcc8b381d71e93220da9bcfce4edd8e4cd4655528e23987aa292070f65d241d8e80515ef582278df5c9c1a8580eb16330a22fac05bef41932172eafa627d8e8274fecad84b8560bf1939c87cd0bf3fd4fab817ca4bfa6e665d2ffab22d7147d826fb4927a492659f99a34e27cd8113c043d6d7719c34031b23ca43cb7681b2a26a8495289a59dd0d3e5063aad840e696ce42cafacd2810f50127f0f6de701bf265c50ae6601e436719e692ea102824c1c08fcb32731b88abce96d91c337bc0950075c7012050edf529e66cbb0d1c3a88eaaecf1b4cdddfd8667086f60041217465959b4a4598992d270755c83064ca40032ed73b52f0661a6d6f4b24affbbc16e08166f6e86e955d0190ee74daff8239785e33daa1d32bafdeaeb039f1a64ed4aa82c9debea0118ee9045b38599f69ac185e684fa97deea143f968d83dd5fd36bd153df507cfbe3b0b58739ff8f5b89bcf381ca18cd0aa86f2818ca3643ccf06dc15d0a84ffc41844b628e661258190a350046eb3ed79d4536c091946a8d87593a41a208a4a1bc9cab35156a2e5ff745960291b831847b005450d1dfe67dea4f3e92a748946f747a2d30270a01224b6a28c42d7afa4d84421092b2e99060077313615fd99f4b50953c07d81ce36af5a91320959c48407477834739fe44ef9d46ecc0b94cca53ef7a7ef9c532b9cf3f79ebd2d2f388bd85a1dc1d18848e91fd20ae443ef546f3666d4c8f5c63d64013e738fa8f44031f83ee11201b02cd27c69431a4b237061a1f69ea454a636810d4f668101deea7ba499623ff4a6fdd820757ff625c5a228eb432f234bda5d3a069171b0cff0774418edcd5eae4771fc7fb0003fe88e239cad8d67bb6e4f4097d2348a0519bd1ca39eaf5ef10e0a6fa98034ef1b5defc8260e3b1d4286f7572ff5464a90759aff408aac47ecee8437168b62ab4d6c17ebdfb534b373196209467f237348fe4075e73c52484372a7bb16f7ac83d9c535f196c5a01c1dc0f74d564e0f28cfcc0c12a665ccd13c4f0af2e1f40ce56d4c921c87f9041a6a0ddf12486fbdeec3f21e280bab3b9f8ae1e1920e7896485194954606456767d9d008a0db0931546b4bb54b9ff4854bebc91b860111eb029e9c93cde37036d359624a07ffd08133f0d4eabf9c6ccf31d04f9e833f945e90d1d198dd94000629af42deaf0117fa9e5efc85f423b3417ed6f30a5934f6ecf6070d35738378fa9d37d69596557f50709c48ab4ef3d4a9436160935ed1aab49fe27a941df138475f647385a1e76461e2c6a3cd40cbd40d7872c0a186c6a68d53b3da6f5cb1d66537f42e3a4a8701e8b10328b5fb1ed790115268778b7d1be1bb29628f8df22215b89ffbdcb5c1da913ea32279ba172c0dad8092a2147da9ab94d8c51036c5c89cc377c77c5da8f5d0a663504ce5327aa667b4b6ede5d997665ce8f203628b88dcd7ce54a3c6b5ad2771db818feff948044bd9d4166ec446b6d3ace318c13bdb4296eb1ee78a120b56f3412237b987dd3c37b8b906079c7088f2f8421808987c8e425a0c40096f1269724c7ad911b75a2b6f21f7e8192ec76764e975b9a90798f8efb7aa24a2d2cf6e8b389161609866c159d0cbf7769ec6e95570bf493522d2146e4f21b158ba70131a00dd2b64c54ba71f1dc939f00ea1ac28bf3bcba9f0617eea732fc1d4e5d7b2e1f18735270d2730a7bbaa4679ab82afb737e02853ff3a1e07315d088b2da40fb10bffde9d175ff39e429ac33811c702f86a2b17369a0e0646e6d027ca6b10b15dd3a38a0529e17c5f7aa503d342bc39ae860963cc53381c9df1b1b5d4245c1c3b2175141259e4c26c9713745c5ddb7e0adfadc197e1560acb848d60ffd1ea693aa1719a7d53ccf31ae811592777fe48e2fa0581d2cab480cc9015d56b7dd6d7202827558c00a3e472b5f25b75f7b9f473a8b3a549a75cbecbaa6a38cc8a10dbbdb841877de78c898fcbaa3b4dbb959e947184c9d4078bada53aa6e7ad283bf584d276c3bf5866c9e16375d0965035e5deb833208f98a086e0f6e57e2ae300eefb12b1c8aba1aa62271eab3b758e1f1e0fb61f8f354da8be277493cccd0e400acc68dbe2e59f09f308bcbf42dc5d1a9f381e96fa5ae67618c2111a0141f3224b25b33d93ee7f6d03abeef9582f70c274742e8870e6b3230a754356965facebb2716f4438096246bf100246e125ea950b823286bd76a5109ad6e70fbf774e7666f2e570318b3bc3ce662baa2dff33eefa833bff4f267e334a794b0ce7e8b032d9945dca8f822105c6cffeabfe472e3a94b44f8569af925bf9ec202b043e9d0a1cae6103d4a364524bcf38d167133abfbf5205514fb79c63efa1a87f36bbf8f51f94fad2d9f53a92c578d02fc9ffbdcf628df0d0361766886b042f5d92e63dc50c697b394bc2e269499ee64e540e2cccf059a2996d8509c73b3f072429eb1292e41320422db6e06e72b780e0dd7953bf5f4bbc3e9e18ca9e17e14fae000dd3fe1cacf030c2465e8101af4418be70daf26ea224f36bdf5a5251774445f1d25a32fd29f3b8caebeda8a50a2e1fad91ba6e908b777acf45a222b27165875753447485f2636c7129ae5090ee6547fbe362ec2f044e6ff2d93e5e92669dd46ba2b3c81cab9e696f4fd4e934b75707cac5d899a69e615c6c4c3485862ab9e1288950fb172513e34a3cb9e8428b0d985ec4b58a5be6e18a6c41d660ffa4385e2a0fccf8a2752fec5addca34e0374fb951c7d2380926b48535888ad21640ac8a33ac3d972a615c80c0f98f82244baf8f93d09ae0289302d5fb0a54c1ba60838083745250bc3efb98b5e4254b0fc417892a56d6b4fb2e99a2ce03d321edd59238cd2fb806bd84f6ad32de9a69925cfa01f483d4bdeb071134c236b8cd58c2bbf3e33653ac1ac78645de9ea29d6757063b578dd65d56cc191d20a82af95465fc100600a7ed3ef02008196d04f2e73abf9f4a0f2abf83cf6b14a3ae100feb683cbbd81e4f803eb3f5e5709a22002f1322cfc786c2e97a97d1b2be82fdf584e9bb2ca4a3c4d30c6dfbf2fbdaf1a06573b44aaf5961068fd22e556bbd933818cc6f7a478da9774450858923d6393bf250f06fa3745de5a8c2700b2b522acf97b8a4af734fe903de9d4032ae25e55a4082b7801506d1b9ba4e73cbaa91ad7b862587e61bc52afbda8e4d91795094fdf1541e45522e7fd2c4d6ff5884f75fbe31c60240a7c7f1aad09bcb32790f0441d266d4c58d8506bfd82b70460d3b4280b787fc6f9d9b5c3f12abfe358aa780d53c61a458a161863a2a9368144b70ba4f638d22729e8e71033e9ef9c10a195b0a0dcb2a5167fbccc1a53806c2bcd5f9965d02db164b51a7b1364c06508ecb3f4762d53505313bef74b506ed7233f97e3ddd70710e035f32a176c99f1312fbdc92d38eb59fa0e203aae94c55ab81ad94efcae87fc658e92b8d4c8a99fc72c30e1f66c0f78be5621285d85aa3ad8d282b2082c6057b334a799c92db44214cd03074c2943c3f0d59adc462c1b35ea34ff19e9840b8fd36ea47cc21da8ce3da075c802fe9aafb1273ca3f87aeebd4d411973584e26b177ff4aab46e7302629cfc60853a98c98e7cdcbfc15e68f481b0da2a4af40ccd81ec782906c5921929d8d1e2946d3da9205bc6be13786ed729d99c21e002b44b1b766fbc11ea92e9a2970a6754d01a1e392c7e899f6aab2d87c869161f29795bd175ef40e33cc838697bf94bf38aeb7f8c479ff5fd9662602d57820801427986e85ce886cea126d039a8102f40ef692842e51ceee04ff9278d3b027791358000391ef27dc92c87c5aab98febfd52a46895ac525065471947cd80306d29a8ea9687ffdcf6ddc6747376fc04f9fdc5562af83ba28c5e37e76d316b940e7dc6f68c0b61eac852f05fbae74b3c4885fb613aa01bf17ebac5d0c3ed832d322fc9cd0643cfc1ae1fbabd73b57180804c18b1d0078ac4eb3af47b88ebb7735b480d3732c03ed25194b8b56f8d60647628a975e2ebbcbea26105ae82b9f619230b9c31574a22b197fb42900112b6763f5c7df0e3e48c13638ba6e8cc209c8431c0b18863ffb0d05020680539780cfc8583f439474cfb0358fbec74403e73a7311139a62d1932eb46c388de46e5347c9db32bdde6a612b630942695b5434847a977e6ced2adffdd63815baa94862417c50998f6f4699f069088dfa8ea9b656352860c5bbc1517e5f2f0cc0759fdd2e65150a0baca73637863884903ad646a3186f28e14603c9d995e310a903ad6df7336cbb24f80d749ce52a310a732e0167d6640a4ca8b887db74510b4ebf5c8d236453985bc2c10dc84fcd87b6179a6381e2d24ee6e5d8d4f9e26e38297572bd210c8e76d0eb898043319f138b5a23076e50ecfa3f4925bef065e14164b68938be41b1b5cc124aae2bac9f3e9b24672ec25d2e24a7a1bb8ec0ea3bd2a0aba3a8c0ae24b364630ed3996dc9283766b49ca3853d811a5a4f682c11d1b02b8c468c1f4dd0268df91d3eab6027582570072473edbf08440784cca91f9c4c2cdab0a613bd41ddbeecc9a89d695f2f72864a7bcf6de7bd4c6d703864e74f228f90fb4da1a64f99026c4319887d9d8637217442c4346c80c01180044afd149ddaa1bbf2d5e8df08c1beb65386d0f6a4edcc095813c3e9eb2e1f00a731facf779290e44422e6d914a7b9cfb25683ee4dda9667f439cb3045c6a4918917f10986dab227c88647f9a7ec43453903fcac66255f77d6dfcd9a6731278a1c0b63181ea4ab42f0407843740820c9d543738b05e716b0c1843262a8b882df7fef750c6740f05ea6eea0222cda8317af14fef6400851d33d8ddc9c65f251745493d1367c84955c365f220ffd9f6420b9aa231a83e0ccf3f8e084fc55cde59e674a90e9a27362553842574660faf88ec8fe2e14b1ee314a6c280612c63030ef2d98017aec587c0e97ff5777bf3f28c476a33078e47565461b979b6c8c41512ae105480551f6759afade88a5d0f5b40d70aac06d1cb73bb0b1c305938f17664026c1c8ef0ab5d89c4715fbed9a067511cc22bd9f06df429be98e1afb3816bdc8f7eed540ad55987933164e3ec32bc088488d1df785217d1fe9bacbc50511103a1a2b6a78539f0e93050b03e2ae965b9a4fd6293f45ec34789cf826a90576e154ce288cf53c389729f190b3e4b94809c22baffe9e1a9f9745923fb72afaaa14e9769239cba587c4547152875d6f8f60e5cc80c9ceaf6cd28357b588209be16248240b83bab2f823cf882e264063e8632ba739d889f6bdfac1825a4283b5725e8a17d44e0e91ac6035ab411e09ca48897ca70b9f803d33bf160de668dc848c92944c4de6e0d85f45d1933b2bf24e8db821d2efc2556330b9675775f64c86afc9fea81366a74eb387245d96ee6b17ed210dd4b98bd9c17261fe4c3c1ea0920828a3bd12607024543ddf36289946d88b904396f9b49800f8d3f04dc7e7aa8a47873057c3df0561121dd954345089d433130226f6d629f1d7198e81f909adb944325f415fb403ed45812bab9760ddc4c157fe507bd1fdbcafc40813b41a6ebd08b1ba8ff2db9ddfbe055148aa7b0ba67cb6b8ae7af70a3da712a545a28aec420efd19a7cd83722e25dfad82c4038c72b411d5102633290f9cb82bdcf526f20ac28815a832deaf76d029b2da982236efd3c672abcb85c234c0e1d571a1200d7048755d525c033854dd9055a03d5bb8af2a021cf3ab33428df0abb5d497515cac46e1820685a3ad1bff2109eb1e2be81b37cbd47c5d4925c7228a376ea186c2aba4451990dc53264cddd6c447d8a1306e3da3d5c12a08baf5ddfb444ab16aed55a82f76c61a7e0dae44f782d0cd5d972ee7963d31cd69e1f771761d768f4b26cea35db3da2504b10979ddea1e3160ee8b391fe7abb07b5cf2713cf762621d16f93fc82c617eb9cee23df788d1ba5d5c6002d030afe447210fbdcb404c0de3845b8b23f5a94183cfedce3b3467284df4b580c597f4838ee6450fffe804407aa7a0f066dd9fb85c39704bf24c058ec445a97a340d88c4b077b5fa9eaa3c3c297b0b5989a4842e1640282aca569c3abc77ee92af3e2ee84ff1ba59cb31fbbd7727df83786dbb9095dda16d2ff18716d65aa352d709571ce2cdec12008488ba658bce60772fd926615c91ef9b86a1e18372a544ee540e3a308a0ad00c8fa89d9b75bde5b528a3e824cce996cb8bb9945fee6008a37db25726401f75360baf10f2fcc1c6eccdd8e571202ba9049790b6d5b216e868db192c5385fd7e3faf92dbdbb90ed390d1084660d1cbb6a4c957a5507219a59a9790d8387ef07330c5a21ddf8f2824320f72c8a8ea2ffb9b5f86aa38121a57cfa7dafc0eabcb27981f31416131dae218fb70382d7c5fbf2c6631abdf11ad0cca4f6bf64269c1b172a4240de389636c4d1f47e7c34b04ac445b174a485c38d9476ea1fbcc835676198a8ee11b0438e3f7731d42e13782a02e2214d061911f48178ac53cbfec55b86c6bc78ed170a2664037b109523f25812fdb0df09bccb8ea4631a77b2fdc37817da0242804fa8725758eafff248e9139b86026e1ba6dd37e9335139bce7c9c2c7daa35f15d6bc7b7844fa84436c3d6c1cd0b6960ab2898350f74924d63bc842e040f9589251b4ea0057419198e44c5428556029294e5f2ce468dedbb0724815cd4525f4baa4b6650e9bf06f3fb6168ad1411575f1d40d0218caee8a52e11794c58f75dc7f55a72193b7981baf5eed56f3120aaecc5dbf1a0d6c408a186b61d7769c0d269d2225d6d9d18a4a0d21566a4180c66d798acaeee93e7ac937cac3cc876e7325ccdfa7119c2c8e93317734992d7f2bf70ac4d4994f3c601572e61e5c6b49fa17c0c08b646d887daf83d19b485eee50a757baa338aedb805f1c0cebbc1e87ae9c1a86cbdc4a693ce36349933923436f556ac32fc03e03c7d6c10689d3e15a7fe2c7fe82556d963e295daf2f95f1f6ab43dab1b15a945c4d01a21fea7a67f22ccd39edab73d3406c1797ac12301e86f5ff59e5871d78586532d0cb7080f7b62e56e179063cb4e08efb4c865aa897916ed91a1a955b2cd61c1eff37c1576c72d3bc643a5acd2f9068d70f285250de8ddc031404599439607d89dcdfda88ef66760620e0a68825305fc180107570927bae9beb11ba2e605f25362103b2f3016ab168c874dd0126408b3bd7eab77ee106cf5afa08a2a8fcbfaadff685996fad2637f33981eb4500697e286926d2ed06b1b4c357ea0e3afc6e9303495848696de2ef67f9d200724498b9485b4609acf2db8e042990f92d4ce13bc988123cabbbe8a07b9a6eff665107bc6c5e586865d7b501feac95f9f53273fcba6e5c4d6c3c2668f2c9da55d38b7a15106374166b5b1e009995943d4242ce618cc40bafb2486fbc812ea4300452586cb8683f7a315b1c6e20afd263c7d0489ce331a15aa0326af6793ce8a1f1c9834a0d3c0a46ea4cf6998efc5e3167b88e97588ae84aac59690556c488406aac1e1560f45f83ea7511107fa483eea01750425fa94d50a6a61dad2aa3f75fc47e14004cda15a81ba82b13da4354a1f400f94f5b890ece720c14bcfb21c8106bcc0d00f7315fbe2b6b12dc54341ac52644d11144b64e0d4163094e5e6c0ab4901688bfadf5e700faecda2eb0dfe7236ab09625ee89b53bc824b71a84a33a59547b84bd4f21cfaa6ba56236f83dc562e54b8104e6f342aa92bbf5ce28a558530c8096ba058799aff7d7d1eff4b2a291224d39f077f1ed7065c9de85b9803a6db04be0896e6848b531c9bba904dd48fedc9a3f39c638be8f9b39053b117242c737f655b522215674bc60ffc9858957e0d3ff1d8e1605ad8cea2a8399e918e68cd1050bea665b403c9b061cf4350dc34ca0c143021ce32743e0688f97fb1ee3ade0f5121e9ad289da307b68459624d60b07a1bec329a7cc1604d263c0dbee0c4c5b9532b12bc82661a3cd0cbf76306329fe9b116b2f676b767ebbdd065dfa959140d30e1cfdafd596f6c31987c2d8a46b743877f1b4039797b28fe4e247b2b5a81522f8493e52b11d6c1849f0121aa779d625be005a00c758a4d43d282b372bad97c9bd9a026a88080443a9ba1af1db80ce60fbd2fc2fb37dac3f320c26c327a55f48e363ae9a9a297ca4d4d01acbfecfcc0a17f885cfa19fb944ac32986d0128d0a4406eb16dd1396cae2690ecc41b453f87685d861f0546846621bda513299f09a6267b3e1197defcd2d708967b9a37326f4144b42b9bf6801c6efcb275ac33d452916a465d63b5a843b0e301828b6e30eaf36a90eba8851086adc0f09c90c7ff85d040590a23f997255503df4f251e667f3f05e44ae1549511fd7d032c152795f7f8aeec95f5e6b45dced4bed22f92dbaedd09ffa06aba7db67ba928ef4a607480dc872291306ee684541db1c1092660f98e5d9f6c5e25c4fe14c05c710c7a9295dfe238a4523091712528a5110e29fc1ec3bc055bf455bcd7f86b5da1fb6d5fb728334e598c74fdc89036af07141829199ecac213c0e4131939d4dd714cb8f75d64e57e9b4b2678c73c4af838f7f0cad030e86273b23ee5afed676302781e84f5ccae40ddcbc4de25443885347fa874710fe96ecac6fc07dcbb548c3b079d9744c295a16c061aace2bb194ec0ca06e5e959ce01ff1c1d52513b2bb2093be3ad999ce6722957a9fa626f91d5b92d3f24315b115645790a58adf2a18a0188678506b1669419f28f64653844e22fcaa30c63da0b915ba982a4d413acea68bdb9b0a63343a7aecd33ebc3b1598f0916ff9da4767a0dea127971b2afae3231b1cca16512b145db4dfa8338909397fbb7119d70794a9ffd6190dee32fa2b4299fd2c3baf054d21ebfbec24e4417f5e6dfc596d406d28dce86420736032c677d2ce7e0c3302423b9b7fa14f17d90297eb4f1ea7bd1ae7c87369b441e83436a797411a8abe51953d599007c5febfbece57f5c865ddf9a551f8e7284f46421b56d3d0d206154da37d168ec4f0545d274de364f1b1e83a385c28e9710fb3221f33d80417f06528d318ac3474a750740c69696d6a8416e37deb2d42b7d8702ce2c41c1f8aa2bb281ab5f87e62cb77da5ee095a195bbe01b555566803b75c494cb0ab31964dfe934ed4085b308d0db9bd01f2ad81acd26e54c160ee9a45655cbb54af0ff2d9fd62266d4249d6611393f3a44e7926b1128192b945e4c9433da235dd50889c6443abd00d669ed55d21df7b8ef1dc308c73590a5af58ed3383d06daa7aaa74225eede32a6c8b55c6e124411077772bfc28b98235b0e170367641ab6e3d1058d69c29844260ca055ae183789709af52ce04a6b52ec81822a3aea2ac8a1d79afbfe1525eb03330f06ce20dff34194bbea70a0add65ef9f7e03dd40dd78fe1cb1de96578787126a29744edb496cc1bb6847f9de5bdc26cb63a9497610ca290da80d1394a70ada2d79ad0f4e405d0050b7f35f2564f1276d5eee94776ff908786d86d058e334b56018a742cb2e1694b9ac032b4448d27b8bb5becc05f6dd2208dc1ebd6dd13f55fb93fe761b31be5c8a1cd7939bb01931dbd36d65ebc851f58d077187aaf7909b07c556dc5e293db7fd6e32bcc4c73239a80517b878cbe9e78ae28a2e7a2bede1159f54893a5f437df560706549aae3697d6dd2b32724573f638dd9594146c422d71d12c80795d30a06d9a4711342bcd62359c94e6fd6e850430d889aa78b84d11f1e00441f286570688f1aad6eba33a6884a3fd15e8d2caf944a5ca8e36c4fa2511cab2f6c01fd867b94f776dc5c8a739ff1263f7991c8ba12ae7003fc20d3338472afe3537ac061965fc7cc19ff5b0bff4b6b138ec62676dc5fc1d9c27202a48addd2fde03f481628ce7d870648c716a821fbb0591fa930186e0caf56e98733190a12022462ffe84468134c6e5220c61b257e7faf78bb90f0a502908ee1595b6021961b2801576676d1215ba3e8cba39913ee531849fde9f7ab4db3883fa8aa5a4a34e863c717e8b7850c4f72025f6d7c8de826f5f0116ee1a89349002e2c3fdd3aa3dd8472415fecfb8999a75135a5a3cbd6973eab6e4559e8c00c09620284c26c765927da24a201089895c0d2b12c6d3396f2db2e4e756403c08855716296411c265c2fc6c65bb3799803c6c1849f0dc6d4e6d07a75ff3c3268c1c4994755919cbf30c14c597998c71abbf84ba1cf59ada58bffb55d22faacff7fdf9753dd3d8d9278ddaef803d5cbdae4e7a58380a3d8831c697a879de9e165a8a365854ad4861f9d75d516aeb865678db45e24045493c756d57254aef1d8f12048f123c6fe9ae0ba7ffe8e2fedd8d11dd738cd5da24d0340db7f84636d82637723cfb80770d840489e397e45d532ae9c7c1a852529bbdacc6d58f645f7a5655cbff5695eefb4829768ae53c1572163d2b9631e33f672785162193b6771ee46e2616380d690c1a59918176c9be9083e1c507aa163cb2ef2cba004d5cf18089ec9e5d449d2c0925b27ac542f9e5c54f1d09274370146ee9d9f5a7c59239246df7c03ad3ef1f866d051dacc5af20b512ac5f2d1ea310b0af8dcad988a16264c598a4bad474b1a0601300bc708ee9cdbda3cfa3d810dec63bda76d3aa8edddc50ef989e9b5c1eab58a69bf8fa897e19e71344e212701b9f996cf85534fc63a19e5fadaecc853ed19380e4f2f73024f73ba6aedf41bfca9aa96b71c2d413107addd7db19483a8f0d761fb3bcf9c21800c4e709009d84791787ae865ed045e414a4cf3d0a49b1b05bff2e60d552457d7c6959990e8bb9b6480193a05d9708119f80a588881b7d95669f49d1139cb33fa54bfc03c46b7f8c137dfab0447b286c751c69a8a0eddba11f8fdde77517a02a53f67c5b6f9d9d91381646a224b94636afc61fbbfaa579f172b7175e504e144356240ac1cc2dbd31993b92dc396def6adf726795e299bed822de95729672cb1849f56244bb84cfbb11dc394f030859c47d2e74c45e9abdd760f85cccb418a139a6452ff2f462ecd3a38756208d053afe6192e356a4be55df756a838293561fa27e4f44c07ddca8621395840f6583d3765ea755b434d40900df1b9f84a64f42f5bf4941ca013a9be6b994be32f989cad62266dd28efb8465709480829e455054f906779095751d1733354df7e1ac4640ec1324f3f69ece62da99bbd0f471d87167e43c832850cab7e54b182ef02663e55a3d24d2491fbfac944a01ea13bf0d7be025618d4102810dc95e7f0e2c112f58fd554f4aef40120facb8d821489bc951bf408e52868e8907137bae067ac81ddad7341c9e27d55a7691026bd4b80082422a42c15c48023b53c42df4b09d081ebdf02d0ccc663c74e8b565429f8d66b70387dad93145292c04e5628eb42271c377ebe9e87f03b9dcea458776e63f468b4ea491ff568f4c2e708c270f03079ad1d4487f2edfb406110222213f213a4bf815dde300f2fe32f504aae6c6b9b92695200cb68ed91664c36d07e12a16662a21213d0ac4ff3119471399719a2f48a278210737a0bf6154360c94de10d25b06d5486e27c732265a86a1f7a96e7201bd7b7faf9428b528a25042d8d090e1021a4c98874b0ab1f0df8a545264dd53616becdebb9ef5e9eedc8bb6de8720e388cac26f7e4cb09f0e5ef74e5bb00bd7fda283a3bd489c5729458086fe952c05065cc53778d3ebc96f25bda4acbd8e3398ff6ca919f8a91ff185d0e4368f7c57a8ee6267387f1eff887488bca8755a5261955325d2bf1c64b9dd8e272ccb2d1c9a23931c3baaacd4f14f530d8beba24ae2e5e47f645049c703bc2e4f735dae349c62fa526b1dbcf4fa7a646c770085527698d0a747cffe8dd6b2d00abcf63a69f4b4c2486cf4076a9c2e07885f0857b50f0515085af29c8035ea69063c6739dd2924b392ddb4b049e30e0a2516ee2e1a066bd5531f3e376b51531bbcc3887acefb331d3452aba6706f774ef1e486580667cddbbb98c702ae2d2c025449e66e1bc3267b0ddc8b49f9dba2384414d2a6bbcad9bbf6ad6238c5169ec6ef9a87048122d04ade13090bbc206d8d2cdb9d57d37a1a720ed0adde1a50fabc842a71e833c6edb6fa00688d9278c9297b9364ba9b6dae180e5104e0d300002345b90d5610a3701a277a30f9fb4fe76c9966ad32834fb16d2416b83efbf1b039e46cd26fe2642831869fe8188e13d4f36b69069ba2fc1bbf58c3c705f58ba60681900e7ad39d1a8a194589d4c47fa890ad39e4dfad9cfa4c97fe3f8d31131f53674a09eb8e81cee429eee002875a2ec7b0e6fcfff78df1c2b14b2e049b448574f90ec0c031a5822dd7e98f873488bfd1b155a8c1067dc27be19b0ee8d33e21fffd0d300319824d9e61ee702d847738349f980c9b51450d2d153e2116b0f9ca0e68b846e313e20cc1cf103c745ff98d3a48bb387282823c1d51ab155cdca83b022a9a90153d2424f625962893c4a1177f26bff46688aa1b7034cbe1680d6926aa126bc0d90455e82b3e388edfab5a6d74a2e0ad3edc41317fd4c3169dae32737eb3e70423180d101cd1a5fb30b4be4d12cf18a60ac82a07f1522919ad02b13d3d1673e88921851d63faf07e9ea60b4dd6b4398e47fdb2c13da24ee314e6ac9cc11c3607eb3d298f178370a3f3787e26ddd13aa01054814b66aca7a5318d3cb454685bf71bd4a19dfca6268a45a3bb8430c8ed0313a2509b26c3c67b376c76e0b540c761850206b7b2f509a4a1e00d998550f292c0a876c0c03d7b6beefe124843aadd26c9884eb6ee8e7fdefff722c7ad9982b23a86c252f1dfcfc1ac2cfaed0f846581bfaf03df0cc831da735d39514ad96a18d2699ae3c782efe484b72c5ceee2ce6d3331d095ff320a579df15390c1e6b707da8d3c3a46ac7870c48b4734ced92c7b070b911c25f3991ddebe32611b8545ec4b06128e6d067dad56c4a57826e2c2c9e0020c1965372efcf1a30e0b53c263288afc9b49470276ef6dd516cd5d790324a2e890a372736da2e94c8c5d58e4e5fdf56a6c76306cfa239fad43910e75811d9892900eeb8520d2c4b7d7013a2c3c3a249aa04d84eed6c2f6c6e4d7367dd060427d7970323bbabac88c846d9674006fd959cabf90b3ca974f4950e8052766240490304db471993a496f50865c1ae15eeb4a5bb1e8334650f6c70eaf391f85d56645710a7ea478035d809c9f190f386c4cc58a07d857deb704c5fd82208550179c3f63a99a879cf9ef3d7c3d564d7a1a548ff11ef1cec8c0b627744ba837b4e367e0dea567c83f87da8bb74219fb8feffcd8d35a1d0d781008af5163d768c82a5afb484f34fcac31571331f30b82dab193dc71560dfe2e35af54d99ddb078b4f15b9a2356a27da6fea4457694d183a1b4eff3eeed375e6235de683f37fda8cb3002d3bdb310ac4640790f08e2f4bb9677520d44a53cf8e2ce903c384d0d6a3bb435d026304b5b1898cfe612138364ebae973d32037f1832388e785798aac5bbfea5c6d2465ef73400a3e39dbc57230e0e029dca06e466018644c74cee198d63189763f6a024ba3031ef08ac2926274d6bb986077d44b1ed1c98f91a76b19792e2501b4aa6cf6d65b392d1f43cd4fc3647acd7381259688b0c8d7f433f038e6a54a1770ca15c6a633060f65eda07482ec1e6a16aa5fe362d7fc27bd25480397cd43280c5bb6a2467c58e83ea7e2683f03424b44f003dfdd08752ea9f57abf7e4ad693490e0fedbb01411d5d2e884f29bfc5fdd872291412180e09817cda3fe7986d6801defa176b871c6e62f5c5f23f34e09b9b2cd787c98099d46a3282769ad44cf2ae81587bd565dbb57e97f6745595de76bf4e6cd58bc01202e595f6e1bd73be494ace8c49d7faca99ece5126932ec5d52cec87a014551843abd9325d1e92363d27f44460a53647107288f7e7103b7cc297c2058402c87405c340557a65c5e3fb7b37d3070162e4e76518d83c7d67068c79a9ae8a2759b6e14ecda5af980bb1a6fa1b488f17593b70b6338ade329f95ec7cea1ceeee07ac833021a60a4d934d463db4e12a9e486d9c936f0f1f54179f9ba56c6d5689829d99cddb77df2ca2857cc304d2d0b6cb94c565973aeac0b468a4806fb71e89665edbd1e0e47466c4c78b7aef002de4c13c5e42fa5121c09313a2cb3e25498e3a8a7a9f6f85443aadd0c5db94b07c7c7b70e4aa2fe87304acdd636e52be44df0c27d346acc7bc54f531efbb97bd46bff22f33b1363c2c0d2dd05096fa08d83380299ff648ed7a2c43b5323240fa22ee12fd5eee41620ed8e80c9fb9c982f566f1e8ca9dec53a144d5b006bdfefe891afc52ccb51b92b4259c7aeffb2bb060dea31257ff2ded01396a1ee19cbf97dbfcb722cc567cafaf0cde34db1b50015a0ebcfd13f298e88bfb2daa10235aa9d31bdbc42b5a61737137348ecd6d6ebdf43135ecef16d9169bb7b916f0f57842c3981c0d31b7dd41d3921bf2f023b937397019cee808f77c1d8e83d97233a9adcfadf045353d352d92ea645b4723ef058f8983f228083d89d8ccfba79540bef3a714b62a9091d964e3a2eeccd3f5f7b96e7994cb8076b4a896c6fa2d0234cd2ecae4f9726bc5b1e3effb7c2f7dde6cd5fb2b41293e5235c20258a28e279568ca6b475939b9c76b4cd119f9c8bb4febf8be1ea9775ae1b59d3aaae69d7ada4a5bf7d24011850d8ebacf68fbc22fca58f4c2bb8f45d0196cee6a07da2953a544e21cb85ec88a6588fa107d5e70d7c73ac2df3f7caae8158d961d4be16eb847b121ad34a0f73e253b828d1cbe17580da765fb74294092e6ae6f610e57d5bbd5907a50196c66eb61ffcd1800f832a0fd35f1a11cb07b465c8acf4f1d7e35d12cd528b1ee77ad274873fe1e45fd259043b805b20fee025a30058899dda75382a4fc077c9f4056884378ba26a477c28a676550237128296d3d5b7cf1cc4a41b9e863bdb8598b4ce5b1807b34f05c478f5978c68fcd8c66c7e69508e7c6ed5ba41548bf65405e08bf154d28981fd0f8060a8f80ceed2b5b56859c2abcbec437b337189a87730882540a921ae6527e1bbe7d6d7e9b1da252951e248049cf41725a3c7af8e41c09c4c4a26d70f40272f665743fd56a4ed3ab2db507f785c441b38ec1db7d14f6664a715cfdb070bec4cd273a100a19bd71c04775291b0cb3d989e1b07a7f1f064e57769f4c1875d5f867b75928dbe425ef641cc5099246958ff155b9489e16e92cbf3bb2090309546fdaa3e8a6b599261235f04e0c712041ad01c0f9bbffa8854b5b86ae1d3b6610cec81c5cb904bf89eb1713c7ed1e2992da31cf32438ecf424625cd9d40945f26e832e58abd8d8e41e8c0b2392ee1d9302de22cd36bbf79a9c9cdac250d8e59b3d8da62088679b646168c7eb2d9a77300765ee173a89279d6719b2f90c605dc6f5d8c8749de043e5102a74120d9d9f87ce19a253c8e19004d7e319447019f096c2c6708a530eac7b7f3c0291567331af67df9f70b33845fdf09e17d09a73ddeed4317b6bae0b596b2c7a5bc852b966c1815971065053d0088b23eca25e95e2d92c28b4d284733f20222bbe8154a4ce288abad2d0dc151cbc53f75fe41342131afb8f34552c6849672232fa5c8b824d40f0a1a21c78b807f594802805dbe34dc40d2dc1f6363569eb4bbb3c7ce1be08100716571614567d8208a5fb2b718b2309e348aac9ff0158657398f2dad56e88ee00c28f339232f73df08365e00e8041fde1d1af8b397f3b51f03a1bc3a6178c7d090225c197ca961f32c4379309e6cba63259ea29523ee5192284d43b45d9d4dd29782f491f1b2ccf53a328661dfd9288f54e5acbd1b61b920abd7f8519a3f6d54273f158019500dba50b8e345857a22620f13b16b4c9e4a7d23544bf25ef4f6512a93ea9706294d1e87586a771774212c347bbc9a0f5c8278c7e29eb489b2db5f9d13b967c52c0ef36e1c8c364b7a85ba2bacbec593e79b8f4662e2929fdd8a6aa94c467c35f482e21bca9449c6682acb77f0d8a1a484a6b984b9c89d7853b36c2395698f831b30ca0b83569266c0e8fca8752c701cfe7eb10ee6c076ad5d82fcaabc7cf0a377b4c4df4681a9906cecb848f13bf7f94ca67b514655a1a0758e90cf254da72117960fc268e5a541069f5233a7da299acefa2b1702855b499d15a47315e6115d3060e4929ff4aa8e8de7df4e8965a84d5dc3ff5917b9a766a882961defc383e16aa092e1e16566ca2a2189e3ec04ee85c2a373905561d6a887e2aa1f077b116bb4d0ebac643f361accbcabc299a75deff2fba837cfbbad323edcacaea48f6aa84d58583be8a0794229ca0ac1c412662a04fb8bb7ed9813241cc9efb366386c337d068a8b8922cf50211f2e03c5e20e6a8b809858c6edd048b5e8591a7344d9490171d668f0cf6cc9517e2248296ee393348fb88ab8e44d6f872dcc7c7396365acf7fe3cf995d8d340441391e37242f06d92bae11f7bfa352d55202813ec399f5261e65bfa4b65b9669858ec8d4986c59f276540b615ba2a19b92c240b3ed212a7da962da85d6c3455509e08bb8b71d0af42a65b9959b90c1e4bf90368aa0fd97e995e329fc4a860ade464248fa7b61e64e59643ae59a8b7f0199961c82b51d890834084b2ae9608da45824076c8a678de19af6c42602fbbd98ff4905f17f62a38e2e3946cd589077739968c92b504b3561b2fdd98c414c13caf65fcc660ec83423e06079054214b6ad181407d669d4317feb703fa7b6e8bcff29c8d4e80ab5d61d8fbc878c650460788215ac86524e5182755c110c5fbd4af89aec013f2fcb86012571a9206a2cc067ed51a1b711ff1cf34b195ea85e045ee557e92c9802ef264c48376db9bb020b68730bf8911ab54b5884e1e413d9fcb3ab5167853eb92626ea0215888ed878e4662bec0d6e7119bbe3702b15d6a94295c731135d4b86a7a3edc04797484210aeefc85b3f8b4ad4dffbf1a087d760372e75afee45a870f9f33d6fc352a14b48be5970540eee5d9b0bcda085435607a470ed5b52c6f86fd7a8cc2596191644cd2b0e32e042969edf901bdadc68f540cf4700b7534e7344d66b25f922db8ee6c07f3f76bbab3fec710106ecc750969c4c03ffc47afa56f82692d7cfa15cbd98d27fb19a77564d897ba42538f6218b2c49426fbc95704940a0a0bd81d010266feda2d34704e2bf226038544804a6ff6139a4ea76c85c75f44cde949599ae924547ee31869ab0d99e162f6f4f4b6b17851268e2644cc330dbfec988a8ccee1352c95154ca852329e5a8eac03b3b943817d6231419fe40a91a5f3e8515466aae56903f440a9d4cb3c1b64c6f41ba026011eaaa267586e1812fdc18152e3a867502aae77a6246793676bff5ab4a0a688e5a651c7d758dbd88f071c488c2856e8918751c4169741fa21c5692f14ccc5706c931c2601fce671cfb899b2ce82d410fecd97394524fb9fc3fa1d7a7b3192bb297c242108294ed544786dc8aa80c193a47fa1b80cb46292ccecd05c0e0f67c22d62c5b5c24af3569a6acb01e39bb89f52c0fc0d95cbb9557dc2793fd5ae2e57b35f6d5060fac69f3ab340493e9f09082651d6d3c3c9556f87c2a1770b301ecba08a3456828aa1900af812c85c5fbf1a530b9df0a02dcbcf652a38ea57137ba052ec8a641e1044064e9b85c6cc798940abfa7f5449af5f009b96a9445a1895f552ca3cb94a54c14f9166ef8702f912fbe7cdb9aacea30a0c14db8a73fe5aca73d9d6cc4d1400b251c66bd1dd8a072097eae583387e1c63b1dff922cbf09c143e29647c86f51a705f9281832dc92f79f6b4590472e4dea193f6a0ae72e87ffaf3f1a28d5afc441cf5366642c85ce2a643d34b43c6f1391f950577c4548aa1f5d08c3391938de844601cd418cc90d6c7e98a9ad4744579776a540ee80096b8deaad5dd0c2b538dc443f4571ca23a6617553344f907472d83d401ee8398bf5e8b7811789d85d585b2a44835e02f2fca1d39265831f65af37068ca67deba448ce78fc03e64271422e25507fa8d280bb27040307d1703bfd91bd51bfbbffe7ece18b35d5eb68f62e9d799703e225a428370487478ee56f1e4b3feac41f6c067e9065b20ec572d490ee58b596e3c1cd23fb03802497bb9a8dd5984f377c2d5dc5bef7f537ce4f06f176933e760b4100f0550f00fe1bf73f1d03a0bb45f6df71327d60c3714daada11a861002ea4f8095bf39a00165369bfe79f22e7b50ba52194be158348ae7551475af0e2a85961250d65a68ce78d1070ec48f4b526306cd193b71e587e66ac17f1c9bc4bf5e6eb0184486193751817c6dfb131e32b992c99a5c35dbe9939a3346521c42d8ffc2f3792935c738cb1a2ca9b3d8c5df1196914c890dbdaf3c0f0464c53e7daba20cd36af94df9ff7dec457618bcf3a14b80ff95a609098d6717e115d4117e5f36f3df2a1dad58311e4b2aea2eaca251241fb01fcc43e2d0a6c1cca5ae7025e2a06935aa9141a290684086caa9d8e1ecadedcb78616f4a1463cb99433de91b3c6eafbea3e44b340b60e8d6de0af7238beb6a2184d90a33c980ff09a868bb14dcb1fe745ee7c077227883bf1df5d426ab086047fa689a0e06b46c4e0bde395c223b9565024da79176c31bc51b72cca9e9db49b0ca4743326f87990437ab3202a997a106a73c02d58249b7534c75a5a95e4b436b480a04e0c3b37af097d3d7beb02c31a74b45f381815e27b616902695a12acaadc6264fb5530299f3272bbd39ee0b3309f5de4bcd506f07c6760a6f084c3eb3045406d925ba5baadaa1b3436c2789594eab38388a82aaa374252500ee00d5d0094277f6cfd6d4b47da876f392b2d2e8fd48e285a85163ea15fb3d300d1a85ca8d13a22a35f88b41533174eb6b46c79259a65bf5ea91f87285b48fe5b3bd1ee1f7bbb977a8ee38a6a7d120799a08b5debd31ecc8dd9d870ebc4a0d8046293986a4fb168fc5d9e2094da6610032cf06ff9124ab21534c73d2da7cabeb785e1a056ddf282bd8d1e1a24e4098bd6f068da1a48419ca2a29378c9e6dd3752657fe120f0d7cc1eebbea8037c8560f3f7f05c78162b178afeac8a457c79662f56712994f7736488295d8d6aa04dd2b6484418a2b21d0985178254050698d8e447f3f28ee01d81e84480f87ae7f05763f4690ccfcdaefff156d6396e1fd9e124af9c6531cc0049c8ca7344aef8b59678db01310ca1e557da614fa0f18de42ca9802b6894b3a2c481cb71d315dacc474f102b1aff72d8b6d5b562e049eaa38667044e60b59c44132674e5ecda8c93c4e0ab5a0ec98c5f00f01bbf89a4eb27a07f0c4f7707abc115bc54a6c5d31c4d1641d7b91f25f121d5785e17c981154dd91eec55b8cc345f0e02545c49a8f53684021c0e7156a0d5649fe515a331677213943f7c9300205fd633b76ea942956898b49f91b73f1d4c5b61dc68bdc709a634ab3ff051d3511d535a2d1bda6c195b9c948826597ed196bfc89574404225b5596709ef05b44c43a27d870a75776d0efe27bd4ebac5d25f1ae85bce947fee41fe4337fd03983fe05aa0dc16e9bfcb998502ef7f50f37605f575569a18b04f7d4f139a7e51e904a0defc3680376e50df5622bde1f502be042edde97bded22d97ff4199d58634aa651938e189f72208d889f97ae8982a1503ec871936c81f89265ce31ba8ac32d14264a593863162dcea41a91966c438f4f175b337668a07c1d8a761e54efb58d499f68f63428f80b45ed128796271dd7bf24c3c607ea7edc2bcc73d0b49f0d8e262f00f5df12d7c2301dc8db4c972c1d1a5f6aa9f30af0285d5ab3d254f0d42ae5a9f9da2d967bf46e4b20a89ee98ca70689ca3ce5bddc0eb0b66c8dd465166dbc9477fdc09ff01a88810ee94514853b24b8deb3552e8708e1161ca77df1f81161c357eb91ed422fe63af988701159245d8c32498eb608ad692315f4ed03fa49d7a65920161e35633d413fa8453dd6a1d7f588bf21086c2ae5eab775f5f61e5b398a84c51eeb67e9f9e2ac5bd91be6a580090001816010286632f558404b3d993c70a1cf7498b76c1c52adc3fe6fa81b3521a1a5a54d429b4829a50ce609460a8c0aa6cbdc88e9c337f27dcbfc74112aca2230142482760c336d8f11c9c898443132a6d21613532a954a254dd38e650d5fbbbb09bf64d2a3b5bbc75c264b3913a3476be3934a9a74986c77377a27c6cc300638dcd5b54cd374b4ed6ed9d2d3a5b3afdf5027a438780226e45e7a2773d347fa27432232bae8def781a20ffc964711380231f8619d0f04313ea50775744420470a37c88d3412199d30dc5cb8653a9998b0d475611886210c0cccb30c839f0875b6371a7d6118ee0ffcbece0b75c2ad240b1ddad825a55ebed0426bb65f9a5ecba3cbcc2c1b759da4dab32fe8c9f2b2332ddb8258d475da6483f7ee42bc777a6783e0910d5ea39b288f76f635023dcdeb3ad135930ddb4517b25db4b3af0ce671ee231bd3fd6904cc17745cdfb26b99c6aa65e9d0e5007642cb321aa3dda6d2b156caf4612e65ad6e251bb687c7a132c754ee2192d8e1393d0e91c4e6c252095fdc90a9cd084744f04b9a88e874fb41024cc3d8baf4f0dbbbae2bc1c0c068da7798dc85971c908bbb81c6472f65fa8a1b42b56b2b65ed156780b9691ce75124d2210d1a1f692433b84c2fd23a348c4d63d33328a559761c2a2333ba687491cc48241269178944a25fd175025f37d9b0c3d38d865a9839987f1a044f22d130a88de8cf929e3c7ad245f7323d789c0ae6ed9f7598300c436e72446ac83dac303030a5124ce9fb604030775e498f3adba6dc431c9a841e6d6e9ff71d0a82da6403e9e085900eea9d3d1a1dd9a3c378301a01f385efa33cead81f3e69ebf26593b6e402094400e596d1c149ba46d2261b4ea2796cb2e9479994c7235b748d6ea31c7e67871a01f305f060f72f636db1ecedda86488286b195cc16a84909edfe561bebfe806f3dfda053faa1444f20028c61592683b7ee74c3a5cd719bce1181dcf72e8fe027fa4cdff6c3f6303efd7ef861d3cf74bc95f677fa6924a652699b4a22f86153510bdf657094d329954a9bfb38ae44459a18ca5dd16eb5198134b486fb7d0f5ad3fdde7fdb876cec625c2afda248b3b75f4d4b9c2cbb9619f97e1d06a604f327e1f552f624d472c7675f170dd9971eb14b7b2fcf3deedc1e54a4e26665b88461320e3454bca7d9b66b1b78d29f7c0fc1932eeb09bb91ef6178d28d940e136a927e52ba87b136d24737125e44bafb66fa49688de2be7a1cb2ad83facbd7b9a1f9e63a42c321c0db6692b98c8c16b86b5e0ecf65fabb855474ea0999636cf01cc7711c168114d3d050916a586b2334f45f4fde719e63944a18d713c55a3fa1a167604fe47ddc908d8f31c69886666321df699039c6f67410738c219b9a72c07b370901bf2db9eb3fa1f1199f1c688406e8e539c6c81be9f1c878647f1e8dcf00ff848696f524d2e3904d3f430fd9df17924831f446e6a60519195896994cd6f42c9b6a08eccd90b7f05b6df4ab8cd2c516f44823c32413532afda24843a3b3ac3a3794c3210867e34f68fcc62767ca61c66ddc088ddfc8356a68fd27cffadf9941e346a6711b79863723db388d7ce33ad7f8733ecde3cd38840a996370363ee34f6e9c868dcfb8911ba7f15fdf0800eec2b5068096f5f4d1bbe0c3a69c369972d0af71237fbe7e0dfde4cfbac8a636f28d2ceb89d323109e0d3454ecde5deee419aff127349e67bcc68dcc780d2debc9088d67fd7aea9e5fe3466edc46ae217443631b7a88e4a8369972a8717d23f97f0dadb5acdd9f647d7d534fdd77360ea59165ed3ea3bb7733e42628db31d1182ab4ba6557ad74937dd9ca11b9ba09a54660026b9acc683709639c791947046ff7cab20c671eeef4055a1d86614f46067b32187bdec8c3dfb187ef61cc84d77151042e5b72014697d2965c808183ad6599c9945dd665655c10cd035fdd665c4ec5c7638c31c618fb52d1d3df1262adb51ed997f85a6b3d224e113fb07132f8df77b750fceec5b854fabfeffb3ef0c3f89ed7799ef789dfbbe3efc8ee3e1ed9187f5804c51247f270d76ddbf62c6f5dd7755df7017ccf75a6aeeb3a93288aa2281e11693080591610bbfa65f120ebe966da075a4407b80e7c97b74d6fb73a501b3169197d0d831803b1f1863fd365bc6f7a347dda886963b2bd4db3fbfb066a24a524767b5cb76d1bde36bc6d1dc0db86819039f85d26771b0f32d9848dc8dca4653d6d9b8c163923f0ad36400061ed1438702f8661adcbdcb792c9e277dda6c1cda4e16fe30ec4ee6e71386cf73eed186b3a0704e2c35c122b533ab61b5b3b2ac184a4d1280cc330dc44222c8b3870efbdf7de7bc17b2fc69c05626827a99b15b01b71d4c56e06b2e40c1d5222cb382d461381ffbe6b79043feea28ffb34d13739037e9ce671ff3412eea28dcbb22c874a801789f437f369afdbfee54ece8c56ce8c9bc42193ad0b767d5656fdc52a0d744c985c97062e151bd4a31cd57ad8d7b561b34b7a6d7a2c651aa33dd8a86263a3b50ac740e999e822ee32e1bf87611e9d668f1bdd0bc187dec3531c668c690f5e88f1781bded8c3fe30e68e3d1b1b46536ee344a5986d2b954a25eff34a9fe7799ee7791ec640f8eff33ccff3e84d9e67f24ce0f5e9d1c6a6b4fb6863dbb0e125716ff579511fd5a5ae80a8e8f4728c449675dfde75deb61dc6bbc511d9de753aa7bba6bd63ed32591bbb6bddf6aef3b86fda76efd39af7eed33619ed69dcd66d9be76dd7b66df38eb77bdbb66ddbb661243c6fbb2949bd760ba2d16c6cecdac6ddae3bd2594e8f776f37f38e8847449a2f34b8e787761be9322f99b42f87a7998bb95b383a179ebb4544944771d350e77ce7be8c3fcae3f7910e7ea4f008f77ddfa74d22f08848e780dcbf234736f761528849c71a09e9a0461273ac236e7c4edca44ddc31a5980da6548281818181e9baee59ee7af0c5300cc3300cc38edc985f52292485208c1e2ff791e8d33a2286899778893aa2143a22fad0dc7bb3ecdefb99dddf7b2feeeb575ea7a1f9a1c146c83c233d8b19dd7498edd3ba92b45bf84e098d2d7a87c1e37b3dcfa35aa47f3081df1d9f7a0ff308e2d1457884f1a9a8c8f63a4f9b40fc0e8f458a80a37f79c417e9d13d8d647411cdc65a87668ff406538a219960602825919e9d4449daf5de913cdcc9c85099675946eb346c04be9cd76158d79dd212290465bc440a41ad61f488495aa43f3d8abb769791653d75dc69deeedd3ac6369af12ba679a41dd53a343445686852d80976629920a19fa2493fe71fb0b3e9b516f3f196b46f4dd3eca6c95ccb5da795c68fe2d6b40ac3306cfc2e6d96ddf3bc0acbb0ec38cbf2bd97391d5be632b7da2fed22ed21882f6b8c2e6991268d30ec15c783e932aec9a0329ee5d124d217f676ea66ca320cc30b913bfcce16e52b8f3eeceb3883e043d133f0f20ee69d7d61efbecb9cb64636f62c8fdbc6f4b8370e07fa7918ba29a151e0a74ae5665ca3714d5fb7f0fcdb7dee5c16e14c22ddbb566dd806514c4c4c4c0ce5c1a3721bd9dc63f46864c75cb381b98abb0efbb0bd219344e96bdc8dcba26bdae47262ce99ce1df370e3363eeab857541289b4c9a48ddcb88de3dbe8be4c3920df47380135d0d33c0efd2a8d73651376b9efff04198465cba693e2d860049bfede9636c022c6a632689b02059e800008a89a7bcc006501296150407661451528a89bcdd3082f88802557c1842f94506d364f1e31788ab0369b6b9690822e50dab5d95c638526a680d2ee66734d952c154069d8128640851e649bcd72c8053a7882b6d97c2f565cc102db6673938791c5044ca08888210b282c01655f739339c5145044842240592da98f116f9ba5129ca8a20baa66c9832ba0b6d89265d321f06c4a25ee6440228b0b50358759a0aa13a52d8f51f256d6487bedad4db49a85c0626c192f7bc45d94d45db5ec510665d9f33855db1c0496290a68b1ed96290a5cb1876000cb2db7e0f2c53c028f785735cf2d56a30c128748b145bbba8eeab86757353245819f5dbd065a43c517a0c66dd32368338abdb7aee350bbeca6b592527b59e520a6a59976e3b4acabc12ebdac2154a422fda1a3c70d4e0d54bcc1083d55cf0a3c96ea272dd11a5a2d1e4dbedad0dc20659845842165aa083164c04c206dd436957a9ed219e489aad261adb87333c5564b6e54ac97d993425a5b53baac10768f96d6a1d99516e26e1d2ab425152b0e344945ca733fca302e4b1e085a3356977388d68caf97f3a38e22bf818a219f5078a121868f618f8f251f1f651ce03c3edac8e13b3e8a928ad8c71ea48f37301f7fbcf05166a1f928b5c0f0517e4144722922bb841fe51729c64f1fe712cff17142f9ccc7d9840de7f1714271c3777c9c3fff3ece2a177d9c56708c33cb9638a48e8ff38b9ea3849cc738b96c2943be031ffc38c5d8f2f487d2004437b1a81ab413fa437dc813e0f298c7e57f001a2abec88ecbfb40c447bc9462c34b97325284bca429944b192e6f67b8fcfd71011c8786017c945ae07c94a9fd5162e1a9b1f9389ddc7c9c3d0190ddb98f538bede34c6d8965cbf3c84bae1e79475e72d164312fb972e499975c38f2922b061c7200801e331c5cd0230c0e3b78c880030d0e3b76f0d0e30c38c4e043071e3f80bea147925e72b303d9d023a6830f01fcd0e3d481c7817a647a24dd870e07f2711f7ac42eea90f1c881870ccff4928be9313b4c0ed80c7a24c5a0471fe791430c07e221831e6f1c268703657ab4712c07ec4019498f317c8603f9d0238fcb40f31d7ac91577f8e8e1e3403d00f01d2ee0007320928f0ba0c77ffc859c22ba861e2b9abfb083063de2c81169f4a89fe32fe454071aedd0e300725e80c1003b4e44671a0e743d5e03881b678701f413274034341c06031c0886c3a0c77bd100e10543a8975c1a0e74f558e92577007a0c0f835eb2bda04718be432fd95e38d08ed3a0c7fc8bc381ee2b1cee81aa173e8003c1a0c71da741eb71141e28871ec51c071261a081e14034a11e73d438508ee7d0a398333a5008c337cd71ae7f0272836e418f227baad1238e4f2093c6ad5e8245cf01c41ea885931e6b2e0239100e3d9e66f2926b4b2980781bb4c9e640317abcf492bba5146026007ac471a3c75900f140396c5e8003cd14e03bd0cd3573a3975c9b03dd9acfe8251bd0498f3317f592ed7420d1468f57ce4d07cab90e646bf478bacd8166f428bee63134f4d8825e726f0e74717c663ec7cc8172dce85144e340a28bf4f8890ef45d8f330fc081a61e73fce62d9ce621baa25e0338e3542fd93a3dd21a70a0f428e3e06b0df54028ee386aa03af2920bc781e685681cc40a32e33882e808320f8443e6414ac741f4137c20eb7ad43ef5926b4b0162e925578f1548e94095d3e30644f538e3532fd9e881a68c1e4b7a09de32072abda4c70a443f710254bfe9919e3bd00c3dce772fe9380ebde49a81d24bae8c035ded33f4920d48871eb7e3d04b361d07c271197a04f512bc671c083ca847ac97e02da506fd043c10be13238e02d2f4a8e3da81363de2f87619c7a91b67003408e0c70c3204e0c6a6c647d6f11e19c763c8f43cf23c0c59c769328ebf90e977e4f999ac63892d9f23e338659ac49617f39407409e7117b28cdfc832b7914baf91675c6719cf59e6cfa5d3c833b4a4620b598696543465192d1f934b50b6041f8254b69c92ed2e5b4860316484cde5ca4411a926ec2d5f35e8f9810ab6962d2f8ae8228c9d6559c3af20b780bcca526ad04d26aa86db2c25886e425135bc66294074938aaa413b91d407d523fdd9f204d0461c25a98fa9474ac59653531e22be1d14ad11042a9e165b8e5476f50ac59eafaacad249a6452fabba7de58854af96cdb3aa722e89544d244752d13ed3cbaa93cc4d77ac59895091a074d235399fc032c5c3656f2f85a169f657c681f4d2ad4c64e6eb356bc755600c3bae02db6ba7bf9eb7fc6553cef5f94b2399bfbf4eba12ba91c41cbbe6494e765b77b9a796a5f7ce93f734c96d44e47c9fdb7779db5d12b1bb6b79641204c976b93fdc6473dd0376f69c720b8374f72e3712d1e51eb9a9cd3c6aa3768f8252ce8cda25adf9f4a809c1846727a19be384d0adc9e420e60376f6a7b9e3c3607953ce77ed9f46d2a4c9f72e574bbb4e8f399de85f1e81d89ce838d413bdd3e3e4d9a24eb3da96b77ae9363791eb5b0586a1aeaed51d6ebabae44221db2792502b995bbb964df8a26b0fb3095f7b28448eced93eaf7ddce1d99a5642b7a673ae93ad6924f25cccc33c79f6e4d95c26bdf418fdee93b3617be994cb21bd44e4faa769dc4bf95e4b42b96ccad94efa76529614cf127bfebaa65ddbe4888cf6d4482e69370776bf32a7376f9bd7b58343af65efbafc523c4ef67cb79da4c7b949154744eb3429cb7d8d3b9ac68efdca92c87d77ede6792c4bdd9dc354e0793d9bdabf0f09f7ebdaf55d5ece4f8f54cbd77569da3f269e5632bf2c3797c78f7373a6ebe035d3452f9df3dde3ae819ef72e5f077377ec7dd7b2e9d3393037e17f076fc23ae73be85dbe74cfd41d1ffc954de14b99c4715a26c190465a1e3b9d43baa91bbb73978739be289bb88bde5d03f376980c9e9447a3c3e4eda41d29c40c750e7813774ee77c97f7c0cc71bf32be285fcff9cec9716e5dde2e391b60d8f349e6beae659dba2fad84ee790d4f81e7a5855110c4c543a22529685a45245b4de5a47a1352d16b0c2a7b7e57757a735e39671e4810fa7b9c7a8d7667572d2906c2f4f29e15b03da5af6e55746e8d3e678708ada817647ede7acd959573ea2b223c30266c16b4ecf9ead24e3b85989bd67aeb49b86d559984561a892967beaa74109756a2807167cfef6c4b3fa07a926a57fa01d5957cbbd20f984fc2ed4964de6aaa95cc3de9127e31d5d9d7bd18df707b76b17be453b81430dcf6d245dde85ea6dd078674f49d7ae14905d83e3a0e0d71e8611def340c290468e051676330dc4661a8916c1f69245db8f169b8bd4ed4712211c7711c2745f8ef1b799e17def08ee1a6f4babbfb18ee500759965997138c9f2e88bb95c8d8dff6751e77cfa3df2904687005d8d99be6ba1f19992ce3b8cb5d18ba8161024debb6980ffcba8bbeeedbb8afdb38dafdd348ba8bb8ce04941ae172e1c225cbaabb55acc9be19c603a7aff3905dd3b29e66a68b5011bb972e7269950205c7850b972cdb2e4edd72976d9b28dbb60c7260967526f03cb470192650cb8037d282960116a1a255848addad631887451de779187b5070e1c2850b172e20088220f84e4606ecc0c9030f9725bae67ddeab2222530e0116ef0a822b578866b805a532aa28b1e9317da98a0f5a8005087d1646f04ce182225c9003a19d9ed40a94b052821308696182951f68600b9eb0800640e80c10522ce0b1c73358b962a39452ea71406a7843ce90851634acf082ca07a8f8a0091f2a62aca0052ba882152fd8f6dd13ac20c1b6f61c07c45e6bc508aa426f6fc45a6bedc5af5620b1eb350e48c5d6082c2e50ca5460c5a6c730ada162d8e0a2342c68a0010df6f8635b394a2c5530018760db97ec6f826d2fa20009db9e268a6d7f334509db1e0705dba652302404832434ab0a2db3877a61e58a663920f5d2500595296c8a6f0c0161f6482d415f6417688b1b51b4eb1a155f6875abd95a29b56907d96408497da02847640749cd961179ec3ac8635ad6931eeb9554b77225da0802fe9e7fbedb06562ab0ebe50ed2b04b1b9f1c0ff79894179ea7919776ea110b0d31418b2c951a227fa85dea90d41099aa94ea64b2f4129769449c7a9abfd9969882ca96a92d535278d9527cb1a513b64c9d800953ee944159b6f4a19136dbe5af05257daa4d917a9a17aa157341ea662267e6bbdcfa20675e0421cdd6abd268aacd02aab0e776b91fe40c06ba74e1385b2b103a549cd80f3e77c3e00446b877dcf679cce5207f307c7ef0e932bbcc295c13b50156cbd6118093a02d65bbe476b05a50db0edb10dc37add52c002741db395cdab4bc388bf5a4d92a545eabd9b3cfef68134a9b8c64d7aee926560b0eb5d588f64c3731499128ad845da25e696eea8952bb65f2d8303c28d48792708077a5d7a61c2a4a7bb7e750b5a13ef2343f87e65077ee4ce86bdd2ca5c1b4831ec2847da29a4c14be77890ada62d37a6ac2c3240a3c87e66718537eb15e8a528a40117b9e1eb1e7ad127b9efa4cca31913318a016e3fb2a6318add829c54eb16f0cd393e76e2bdf9d652a2fa0c265526dbc4f265791523d99729828eddd37ea537db9526d995dea69f654b9a2aa6659966559b5560fb5cae950519a264d81855c1856ab4eb8edbcb909156765f20226571cf145078b62e2c417265fbe58f9c197100b4249e0d9d20a5b0a7579b283f501cab44376ec981e427b86c22e519333925dd34d4c52242ad392d341a26e3d4dab052551a68badb45ec47aba953437f5347f3068acca5689af60ab81ef96cc540acd947724a458553d54ac5ecd2b2584c4fca287d64c2fa458bd9a7a6aa182b4011617a0f458ca206dba7f9f5b6c60cf9e3c7deaa96eab279417a1e7c6000a58a8580951918a51e091fad01abb81274e8ad8d5e912520649835d9da4045c8f3f873e7feaa9bab8add777f9f5641dcb6e55d32a426fd8d953eb883bbbe56c88c172042b76f62354aceed6e6cfcf4fb591a8db55b9c105bee1013c4a214aefada7eaf4876ab49f0e4e9ccc7be78f3c5540e68e2b7092bb4df33b7ba6b4e9badcf313557d66a9466c5959aded8a3bf3562d6659b962f6549b79459eaae7c0c78b5d5544d8d57da0cc282939531d0333556750954d535460d9f2ca96292a2ab02507a448a79b4c940f374080c7999a58a6d0f7487dbadf6aa7d6e51b58e0cab45dd2ad3a955834d9058f4c4d3134a7a4a6f8b2f9a49ce8c0a49452aa43775311ef9e6e3251264cc57aaabe83c2415ff8506d3acd63b5a04c3b74f7eee9214c4789f554bdd326ee12d55da26ee50a4d3e1834160649ccd4901bb68c936ba062e5536d664a9eaa5713d3239e152d526da84f3d55afacb5160b43f6f566ea43c5ea57a64bd0ea266b7ea6bacc30aacbaa9a59765579d955555993ce1e5a53a57e96d8954cc2ae3e93c8b130f0c4b6348522c309a69fd5a760f4860ab4fa49a3501a14a5dac84cf9c162c527077ba45470291328618fb48a09b44851a19166d9f523b573ce392d078250f2e265eb895e622cb6a25ad8283445b1e460dbef8002b6af7c68cd482b28d546f254552e2ed5b9e5f6b40a0daaf63425246598a928b4eca02cdef692ea1317d81e8756f6d65aeb96da9ba4c0348aad5eaf57b6d6caca7c35eb7c9d75d659679d75d659679df5168bc90478be9ab3ce3aebacb3ce3aebacb3fe9ab3ce3aebdc8440e94f5cd028517ca82841b95b863f4880ab57fd830498f2d4caca2887367db5252868be6a828ab1a716a1ca67a62a9fca27a8fedc380993a87ce612160bad3101a61f6d141d93465a2aa8cd264ff45dba6cd942471ba5dedc84e168a3d8d8a38d6279aacdecd2656888f6c85b965a49b897431beff156d4d5a42970bd16c44a710405b2b0334b290aa1e10dac10bad1022c3af00545f31c7ae2e3044a4041d16c8a02752a44e019811628223c2d76a359c606503537994d0c31310414119814d546b30c420f503537a915684207544011314a01ea5a3a9490440a45c41dc2b5d12c81900554cd4de8145350d00545445804d4959755d96bed0f72669e0b1864d10545f38f24a076261194522542bc517156405a21ce02e35f71f67c230dbed7c3b82b95b85f8f347b14051ee797fa2ff532054514bb82bbeea02aa502553fb5c8a02e73280b4c0f4488209264d97550b13e882473bc1ec4b5b59f8a9992ba35fd5a8370a954c719347e72330a98e6b002631f67dde49cc1f2583cf2f2d4123d0f2a602db858106061440f76fdec526bc7b3e74c5131dfab671018f83be85e1bac1c4bf7f4d4fa2b2b21970d775756ff2067e86994cd094dfaec7929af6451654f9de49ffcd1634445014d7da50c4234798365f6b43eb7fcf86df9571b4fd7d0692cdb92521c65168b89714249c1900b3616dbcac1b6b80945ce58542e1646f54bc67e36515dc774fdf5faebd7b3bcaf5b435fa0c0234f72cbb0a1d02367e41ebf9fd0459ee4c6b42ccf26608033141bbe915ccff46c828ad68c426bae5b986db2b1213646c4c62e774c8f577659bfd6b1d3c0007397d93e50d1ca3d589f4d584b3431430e1250c817e4ce41220979ba321b875e5946935c96651bb07def8375fdc8957da0a2751a2af0d50315ad2c0b41b391e8641bd30dc8f6fdbd7acc7bac8e549b89b2eec35d82943228c19b9e5ede7241496a83456d05a6a9124cb176fd0da6dbb6dc0f2630762924458c2d854e30836d3921d84df6938b3db5d856abb699b70cf06cf91be01c4fe09166571a9a4d93019e1e3739a2e0b164bd3e4666567d3fe2d841cc8e67cb6a53aaa79ae18d692f576e233472a6deca30c899fa2af3903331c8174639835d67c032c072cb54133efb888ea103f8dbd62eae0e36b5f2079ba2e489ca3d5239eeec3b862684fcbdb431d264ba6f4e72f7942fcccb5bcb1edb16a556756f61d7fac4e16d887266c6e04bd733877878783480ead9538ff24bf7118bc071fb4efa5802411d1b041a5de6e3cd06411004c14b2118f072ce901dbcb665519662ea22c574195ff6bc8c6934b4a744855cf6bca8cb9edfbc685bf694f385ed5866e5cae66abd2c252505e6a52ceb6994a923b4ec7952ae0010f313226589c25aec792f8545723a7544d03ea2ca9ed778f67ce664cf6350f6fc6d62cf5f51f6bcf5b3e76dcf9ea90bf8ecd9f55c3b236a7dd1e49c61ce908212c64e3237044a3b49695bcf616a73b372cd1465e55ba9edec514264555f5155ab4c45e8775044a0344a5196be79a2345babad9da5af9c9928ab692582521ac59c17b0c24f151411b2081cd84114d4774aa7e7015a437a9ad72687432dc06577afae5d3d1ad9575bbce13cead8f4029fbbb3b0dba81bd3dc2b4b478749bbdc580df464d26eb796577087e0649f5b4d9102f6d8f4376c80471954f31a865ce2b7d4e9b1e9ab4ff71c05ea92d341a29c64c766b67559d6539528c9012952c2415bd620e2dba9a8b784266d8c98cfaed656d5cd0d438a683d5c9ac4a1a148d68a614ee6b6244d0f6badf54ae620a6767d8161d65655d67aa85893b0f6f6f8c88bd4e6bb04b3eca9555b50b5858a35e3f1040f0fcc9ffb9c536e2f54841285dafc509b4fcae0d30029b4915ca79fb7ae9980ad064821213b537655c9797deaebd29796eb8b06d06c24f6d52dbd655996752599bb01724b8f4db3c79d28bb01349b6a24f3486c906b9c1ac8bce6edd563f5c57339a15202185e3069104da1321f5e2cf414ea9b85de940704fc65de54058fb787c7123d780478bc7cacdf1f2a4201c3c0023c26b1fb3251c03fd74fdd92af2f54ac5ff6909aba7cae2f3050f1a93ebd5fa77abc5fecfb73a998e2051eefcf2e45c1b6bbd78377849bbaf355eebcab8b8e8d3df53504061e6fb6023c5e5a3696af2d25c0539b66769b0415ede57369d9f5f76ab9a57da5523762a5a2f404b63ede54690498c706f065e574023cded4beb474d1aecce92b888afb62d15e7d0badd1b858b982f45505062baaecfa4bf79d72a9a06215ba3fde0f700b5370123a34fbeaa9f4c543c51a64ee9b92a7fa54a9d2b78a2ca62bf078f16834448142059439a134416b2c17945e3e97cfcf12502e1f4f09d81e94610bfdc01713a5533e90d2e3504ae935e50351e81592c0aaaa2ad355c910c54b25ba526d46d5e6fba9aaaa32555736423a78ed5ef7d2004cb11e08325d7add1e5a73f55011caeda1353873ceebd2261becbe2ec4eeab497892bed9c8e89f6e22fa486312e0d18aa09876f80e1ed44384ffb4f5418d37a46c7d28a5d70c42b303424a582047adf49a426bc6faa50a187b68bc56e6d4287764b403495b1f6d79eaa97e942d947ac2a161be5a44f75e3cbbfe2405b6507c9eda33554f75fed42aa6f9b9a7e6634a077cae181288c7051e672a456ba490b5a5da885e6f0d551b89b2b4585dac2fbb529e2df76853f6a79eea45d94ea9a77a30db28a727f06853a97b31d6925559767b042d91258badb26b7db040a936403ed5e6fed45305858af543542e1f28d712b4c63ee1de7ba1d01a918787a788211e1e54bdfda13534888706f1f0ec2ccb6e0c4b2c71b7ec9a55168afbf3d1da02658ac9f2d9b5fede2de371efbd433ff766b9d7cb1dba5fb02dab7e68cd686da182d6d8a1aaaa2a8b038ca5c0b0178b15a2624de5236c4ad3a6928c940dd242f4d14ce1808f97cb47288a969f6a33aab751aa8d4fb5099fbab2c7fbc5976ad3e4d34dbe13815d0182115a5040d745afa16e02ea26e0c38fb4984f53e071a63250f5a953528b6d5333be90d2daaaaaaaaab2f60815ab0f582a9a1ed65af94a9626141a4198eef1619ea8c95db7a914adb9ae8b86613202da14f5510f7e07e5f9e0d1a6be6c32f25d74916e22faf71d944d8972930d0a201bf00045c46f30861628497d8c94cb09aad081104041e154304bd437848a15949cd545ea966a6353b56ea95ca88d44d5ea25dfc0565a0861cf1b2ac0e3c592ba42370bad1969123b55ef531f657e4951d17a9ee2a66eeaa66e6aa6687a703f98c09f5ee6480ad6526dec1579aacfc1f562572d9608bb5e4bb591a88bab4dc9999ab2360b1b5436555536556d7258a12f688db4616cc0caaea31563d7cd6ed935b5abb5959d5f684dbd37b553b4661689426b70e697700ab17bce54b529cd21f506f3e9680f5d68d2670b61e9142ad22954884ea936378268152de81525a8159aa226da45a3f6a7dad0b4000449b000892b590ca1e8b58ad91f1d846e9ba2353fd5c6852d768a159b85cba68fb2e947fbc5a6d514252cb0474b653bf186b070608f251f2f7bb458361547fbb32976a78d426bc6da6543416bac13e69cb376a936f45e8c4ba55f14bbe8405135554ff4180c2cf738a14c11481b8b0239a1541b2bc5b452cc9a2aa64d76fad1b260d35b608f1cb7c7096553a127fa9badd0295e1578ac5d36bd17ad1ec3b00b911bfb0e933d2b2cff60025ff1a9b235ba5db5feca3b7be624755bf555b5549498a5b56e57a8c2f6ec02df6bcb7481ad2dd305aeb64c17d86e992e70dd532894e902d33d3580e7ed1e3fcc851db890030c040c84aa7241484a8f09d6da49abaabaf6558ed5da55cdd56eb757da6b5b36d7add129691d87d02e64995688eba54dbae16a2babe6d8f53576fdac2a7de5cc744107dbdacaca63dd36c7b679acb16d3dddf512bb975571544c114bd286b392f2f0509e4979ee0ea4483f6e3593092705887aa277a1063c3876d8f5cf9464d5e70eac4f9e2a5a4329d5b5529975d6aa95bd9775a5ba52b7bd525da1db5ea9aeecea4ad572f228a980aca95ae70a9562654960d7cfaba9b5d65a9753cee85d358695a60d4ab3d6a9ad754d0d843ae1bd2c5bdd541bec3c343c725a40da501e146c3aa56c8ad5e0b26c55653ea7501e5a6b152a7da43c41b43d854a4410ba32e5a2a324d45a6b96556de3ba8edbb4cc8bcad67b59d5e5be5b747e487511eae1480d3835f5e3267553a5ecaeb4e472ecae2ef08eb098cfca999a89f5150797b3a6684e39060b7542ec5e569565b984b1dccbaa2c2762db4eec5e56e50556eb7647db85d4e852181e9fd168544776146ef1f45c8af982ed489bed9b9e4ecc0f6860d38fd3029b5e2cce29f807787e34dab2e66eb2e67a359233d4db7de51e8e24c087baaf7cc3909411ca1511ba99ee2bffb8e94163abeeea20e6187b017286fe071d0566a2e4eefa09c89aaa93509102b1c5d22969d6557a6e9133740b38faeaadeab367fa60787fdf00339132486dbb092fa647bc7d31f974237fb12d6317bab83159d4985cd3e3cde6bdb854e92d5300b0c7b9654ab58900a594522636a5f741b6ab66f2033c69c704d33a3bda2e6c81474ce40cfdc224a324272ee4a0d65a2bb6d55a6badb5d65a25a6f4f201cd9cb30bebf5f89862eb6a59a6498c450dcb19fa9eecbab29ea80b58f0bcacae8dcacb395f5fcdcbe46e69b3ad69b609706bb3d26d6653ce68a5d6d9764ea523bd4eb8251577c4705b8d56db52aa838a53dbba534d8bc4c74a2ba516b52c8bd62b8fd6a9fd45afeba2746b368f56b5276bc4e4e5680a3c4f4f3b8fa22d3349a7c545d3484078524a01d083e7ad911478debe3a0ebda4945655eb2b6df5b8e3c4a4955ad6754111033279021bb02b95a76c574ab2aaaa9f5665ad652b6b6d554929a5652b6b596e0712b0fd3c0ecda6d0269dd5d67da9cdb5c12e5fa27bea0fdc57033009e473c0ce539eeeaeae25bb1edb555ed36e550504152b8b43e794524a39a7ac5765a5cdbd00b080e5640b617755f58e2e3d96da597db41e582a5842063e4d780015832d4ea0a0828252547881829241179b2157b65c576655b6d2f4b8f94153575c10d4850e30b865ea8a943b0696b165ea0a121812b068cb54eacb90d4502a0ad196292728b0ed96a9d413f7da3bb74822705a66cf083e2d39e852a925b83b841a1f96c7ea141728c04014bbed2adc950c71db8a0628d8150da2b0ab2da8ec6a8b293441dc3184b2b7ce6007bb6ac1c40ca0ec5ab25a5073079bd797a089ed15a74d6dfb792f74e7172ff60614b0d8e8f1430659b6bdb54123d836e805581881c50eb278228b256cfb163820360650f064c8407f65008218e060d39b3011412842ca115e88604415720859460004822e8ef0831fd8266cb1014dca4cecc6608b4daf68c2a68fe180501cb4c52d8d326cd4587181952c5c91c5b6562ab0ed613820569c3d9409185020a394524aba5568e3be8067dbe3173cd9f6a517f860db1f06402e38c1b61f714002a3b3a8744cb7a4514e3245230000000000a315002030140c888462b14816e789a4cb0314800e819848764a188a93240862140421648c32881842089019919119022700b1e93befdce7735ac0c7858f9980a7ec852f010b693b71b19c7c8eb5bcdff64b59fc5a65d9850c0fac9c1dbcd142f73eac89db6747b11f920f66f35b0f22820bb40569b86f23a108f1a3b539885a9d90692c3448e70effbfced2695bf80127bd9c2a2cf50df5428977703634668d2635c33cec33a3a394b6cabdf30b8d4234614d1652e297550e85763a52d2726cb929b9b4531db8c729c67c52cd819c6377df6502140463e15a44974d289ee51d1aae6e79b6479baa79d5afcec7e6ebefe7dbb5fa144cb520d2360e2328fdd40fecf112baf7804ba25fca5e65f611ed3fbbf12745c129074a6b4b536d02e5b6e914be41f9f23656455d39c5830d7df1c550b29c6546b32128f255b1f9ca10bb4a7a9ad94f804b3df4428f06ec246bc57af48aed4e7fa3ff74647ab9188d589ddb52c600c715ec7aeb8677dfa8bd181a30ebab7895f4605ce87378a10659446aa0252bb5c6ae008e8b5fd4b1a53a8e28085dde3027da3ae70f76e1004bd40e66c1f9202f7d6a3e6882b79b87cec0325d2d032e36e587d3e953dea1ab54aaf7e9b92aa3d8d9546c949f1a8e427c3d2b0d5c6664ef578880bdab1386613d350202f07065d6f3824cb0b2d2f62d4c3d93838beba01efbfd8b8767f1f63c6f547c5833a04717e4bb5467ea05842d4d4657fa03164c223517e06858cad5da97bf7787ec8a26a8dfe401af9ed5ba28ae517f759c9313b09b7d22b844e4d2ee521c543cfb8c4188eb1289dba9458a63b3f2329f73266545cb6015bf34d7e95f0a4d900e2d4959c22312c1dfd89349fd3c52662d123ea4ec064cb40f129398bf207c252106ac9cfef2ce200dc92655d57da4be2a943c16159eaa803e322188c60a4bf4a4b4617249e22cfaba51f3a243e5996a3e568e77edf41c2111d0f965a1510052b569f2181804b2b9a12871f40aeacfcc0f0bc3be5b4cee1673c7081dd08f791f7f83a290948b27fe63593c716938ab3b972f7ee3b1f562a3d741e50387cfa4ed57f4fd71f3ef8f4266caddaec3e75f56d346dc61a7377bc0ed6cb550e3fcf77e3c319144b165c114d424e5ffa8ac153e4b98da865b45c4dd58f418a27408423108285f2708b1bb4cf9be5a47bec9ccaab25ba8e0f6f16b55099e6d04ec7bce249038606e514f50213abe16c5407625cf72713fb09812773df9aa98cb8d9f84603f734a877ac2b4ce4048ebd27f767fbb8f5ea6a0074c9dc9c65a89cb276b678cae6e1450c789312330dd94c9ddec4fc90f09ca0400d056f7c20f73573500e730f2b215d1e823361095c337a77a60476a310ff0cd2d10fce12d7f48b3e3ed614649bca0f45da765730958969227eb6663449e889b8f385a3ce2104205a8e038df43f9fb4ab8fbf32552e1f3f4881e23f28665e8208a03354f33571d2ec679cad4baa7f40e42dc59fb8c3bc1202043eee9441d303ef75148921776b90a52dab6d0084cc14875062968af24bc7e9c3c7bb3dce6ec327a7a33a81e85b0f3b7f53f8bef7df42bbd04556293f66ee71e30d1c29b1ee5ede96c765e51695531959e034a431b3b7686c9235330232d450f94b24d3789f3965626af1a14a2134a43087d1f4037181515a6ca40e71a5b87a7329c6fe7afec3b9d5eedd10455de12201c54fd2c8f3b3cc9f7edba38076748f2113938d31020d39d90636def74fc650f9e7c509b21710cd25978e7eae09a87967856cd06ba744080dc68ef37dd274b76a17ec216f8da35f1658c1c238ee0afcb06cca337b18f1e3a16a701b6194fc21cac87c72501b7682acb9c1e464b39369ff050e31df60ccee2b9746149dd7c75825fc97c8d1194bff2b69113e57b59d85cfe5500ef398622416f82421f70283098fd0af3d4d7efc032be96dd41d1135f6e88769284e8e5ca1135a03ab74f649286e564c7cfdf0ec3038c391ebb001234647d4a6ded3b3552b11933bee18ac695568d5c0007deaf31e8f221e2f54082b0f1195453b808515ae8065b705c5b91693a7c0b4b3cc94e381a3381ac88ad2aab0f87124a70de576d1addc97e607d24ab3337fc606a0d57de90fb4ec71d4250fb733bf1cd37e3dd041b55adbc59bc939956e6fe03dfc60c4afb4a27b215aacaf6b0c590c69520dc0614125c8cfa3a4d93836eab6d096727c3e01228fe82da65213c8182b78340b4bd88d3614e00b19a3a3d85e21da9a6774fafe308fe9a958a896b9535b2a308fde57b641b2968593c3894b20c4a4444a88253f4aae32f600d4f0e1387666e1056f9ee136d67496a2b609ac79a53c15c3aa4849d8d4746247090240c0b9b90534d819eef4dd1ef071ec2c1d533b52fb9f0db9df80d1f66e6b199125f9b7ac54152b8a7a4a75ce72bc45c91e1af1ac4ac6d52717895846636b6f2e96c3c47914a840d78af2fb0d4feb2f0f9354a72ccaca58da32400a0f34ee8714543a1351524337f6008834f8cf6cc5ba586f9245e4ddf3a505f2bf86d82683325d52ab09ab7fd878ecea98582c9caa5075d582488bb492e8cea5c1ea00d463e18c738aff79a1029f1e77e4b54962b10febe5fba360c16f83afde3c488977d9ff316bd389b199ab5574a8deefb192cbdd4ba80fc08aa30ec856bdb055d4e8f772d24a9bd2ba577128d5024d8a053a642a33f86b124355396e0bf9ded09ae1ab39b0f198fde0a87442af46f0c8045d2ca4cb2b23b033dc468ff5a7597170c48aa9e867bba8edf99995c3848a6ebec8d1c5afe9a3fb55dca4dec49becfbf531ed4cdce5646ffa04c45b434721d1c85eaad273effba7c9e98abb7aa613fc723904f5adb1a0fe51fbb508e13de8338d499bf56a244276b157dc9e20562621245b81b01089cc757bb934f1d0c94aa60148950c3b3db035d3c656ad0d2a5ae2e63b83fe421eb0fe5c39ed70ef34a2c688cf202befbd13353eca9aacb96f1fe1197f94ad16b8734c3a1a09e44a37164df4b1d4112b38728b71c041ef46afe63bfd297b8fe8d120770fa24bb0e2ca90538aefb6b8b744407f959b06cccf98946d04f4b78153a4eadbd7d41081287d9f5d5f6ff8cca14d2feb19f3c5558409a974654f9b195e2d24287ef3118947d3138edd55cc84caec0e15cc9937a49aa385b4b46568cc918f73cca55f1f71e651d370fe1d4e7360bbd9e85b6f8109a38ce18b36c3a6843d92c3f8835549ee61f081ee2e087a1fb568586c5d0773473e074b3d93d2a45bcf6f8116a4017240617ae4c4f3872a56b1ed41d85a3f80d68161b36500c6e5541965e15d3b0d13e44a347c5d6baeb282baf74a5ad144140b393b4209cbdd157c2a9ac30d83af581e842a54a06113cc689fa5a5d5e3d2161c4a055dbd12b37af967055df4d46025b455ea97988ebe524f6a18aa86b8dd6612aeb45fd2c190eb2eced57710c0ba22191de54d9f13127d46566678b60ee7d05f01df2e544e720a51cc0d312c545d8cd09355894feac135a307e16cd9cda754370f702405772eb22644a088461a7ceb0c6fc51597c52e476c8595223fa2cdc4407354aa599f2243fa4da42b506f74e94b401459b74dae8b72979f8a502a12d40b010ba85574ae57afa47b091ce0ad73ca14b854ba0cc087520d34aece2980494ee8e89b2dbc6c4c2361922c44cf244595b6bd0a2004dcac7245390fa470cfe6f825c646622bd66c68f984c1ebb18465471d3de939123109361a19518464f56149868f311f60cf51034130b0653ec15896200f403d929b764211f798b7ae48f2824ca0439ed01c472bff8e00ae0d71280327a8cc20045093d750802aa69f851ee194fe80ddfda6e99a13d13ab7fd3a4e5fb654d1981c49d9281789b4fe4f9c38b8b0db712d1ad191e682bb7e59ac6e304fb34e51bef5273dc8c37cb9209a960396f1f39cd0807f877fd7dbae5c738ad07adcda930eca9df58ef0b654045b08572a9901537e0001a5512280c195900a8f24aba845bba481ab1a71c59b9b7dc86348eb845b306a83fc7d114138dc0bfe3e9f9098cfa6e304fc663a88e8db8646f43ec1a15e38df68282fa4748b5d0bbac71a1bd7766365fded16fa576fd8d1d9c6c950849eeeb64a771010143c9a9b1ce8ae65041198ea6dbecb9ae37bfe380cfee1611c6fb0d38eb44b756809a3bd083a3506da2e449c5e99fff5b872e04fbba22ea20972d73d086f981e23023952405d8f224d5f19dba5ae13955f336286714790c007c8189c29f7496ccd49f0d16cb372c277dc4c7f948173555c89ba000f94d79d7191c1117dce9ebc5085e7ed8d61eea3beea46e81d10bbce7294c8254b8dfa905536ae3e2e33032cbba79a2cdb3fedcd2cd7d48b8cef5e6eb7bfdf81a0ab014e587b832520aa31f5502fd2008b323a0e89a132903323e9f054df7d833f8cc0e1b645af2025048325087a8762aa86603e68fec1027ba8457a07803c3968a7548c107082aeaa3c1c9eab255e404a54bf12423b4e8a38c5502111292bd7f53b2883448cfe8ac80cff9a9fbdda93d60c0877703e9905e7706fff5a353b24f745c243d1a517d6aed78c3197f758aac094a1db3cf63332d9cb477829e7b5f9cf28f9335f02326f212c645a7ab2e84f22c21c566afda5d63bc9d9940a12bb76b72f1bff0a83fdcf102c3d474e7afd79aa6b8ed7d99c26d16c8090a00e541488ca97c166bd31e3c5611447510e5754cfb70548cf0f85e302717e0092d789592b231d4a3a3158fd54100f55a46903dbf198bc204c4649ea195a42c29795f1e9224f5d01828b2e3404b7e2f0738a0e8a51048956aee5206d530c37c143f781ded15ab1c47fd57aa9a5e3e82025e46a889ae7629c217ada4838219d1fcd0999cbf2027253e97d3c0d1214842b32dafb4bc6d85a49f081ce698268036d546f752d6e7ea2fa10523d61429f03f4e22b9444fe465e6a3846d15f796728feafbc4a35dbecb07289077f5db9d57124b9d7145f606c833a55c8504b7b7a3bbf83a8bc21b6067573c1c874f53db1e2bf82689b23e8b2e6cdcecda6b7fdb5676af5a23065c987d0cda95d75ca56b54e15bed17fac93f2884fda25f3a3cea5df5ef1990174ade03f9d0e706ebc89e689eeb008edb9225177014b511635c4f494962afd6ddab2a8220f790154eb4b7cf9ec0e2985db334b543873a7c8789902dc258b23a4df0b8f275c2b8cce9e6157a59d42f14ef2a22dda1b5ea5c367d7799739e0a251f09aa983309dffab04e52a905ebaebd8a5647013957be1d0baa091d08a627632b54c053125267fba92b60cfd4543036767252f895a63707aea83e948b2528cdb83951734a45e5a8c84112461b35a2699c36f2b5d2455fb466e22d7f3a6a1ee4f8a60c96870b6376c461471cfc408e373ca8bf2d9ccf29fec13c17ce099792be28dc0400f6d973c031387ce1bcc6077e3ae0614ba09cc521c1c9a4c2cf028abb99cde300af93cfbb491d08c723ba28c713a132863d3b9b80da929c3756b3ea16d4c63b5adb9a924a7bea649f3cc812306a626c53d74583e048486a89887a19f8f7257d6b914b7bf23497310126621ab2d09ce43cd856838324c8487e758d4b0b670ea7f59afba5b9fde4a5352290b765b89171caac959f307676e6abfb052266614c4c1fbe24100cb4dbff2d1e5c9d4aa7824a8ce0fbda740e1de3981cf8a4dd7c56beacfe97459a730ca62639e2804370088cd04f10c71d383bef060ac56f5667b8253667dcdcb8ec81d2324742fa36264d55726a4124107bcc82146b0a547cf14a21ede0c0f51917658a50a6d4c4dd1119bc1c66519c288ee9359f3de69a0af283abc32340e70cc04d0ccf07fdb257389cb028f9609c9e601d80007f74e5c652ca60da29a4f8ec5a10e82c518a1372fa77994625f8490b650f14993724432231650f5ee52168e065784382843d2cf01d140a6488ef02ab8411f5154c8493da7d0072e0a63ea001040fc841487c700410a2831cac5efac1d51c42f4e1c04df5e2a0f9869654cf13061a579e32004c780c570bbc1b2a996b255a88151d4454f882ac721834ca734c0f12a49bdf2cc715c8a5832ae09ec305ac64ac963d36b4ab6787de1b8a27586a6e313ad84e8ccb8488f3227263cdbeddbe75cc4d229943b5112dc420a2ddcf18e7fdbfda15198567447ef8bbe53a16b3c5ab35a204753e83cb4e0ffe9819a6c9ac9599f35938c2cff76033d8ec230d39042992418327fca6f3dd383ebf0a1b0b399a1c3b4ffa80ea47e93dfd1535fb619c1d7d5f1d034c89f8a96ebd00a73d4bd0e1442d51907af368631a12c627e61b0b186c3f6ef38f8af1e22b96039eb983970c8c30e60f2424210672194ede810c7246fb7d84ef2152e06e1cd1dd1f15246110fabb1bcc3dbe26cd37e7b96efee321d2e14162bf574a4f51f8d389235c27ee7de7390e061ab4b8b580e2afe09cad1809ebc13f22428e7835e670f246eb429f448f053516e0a7c384754ed011f6ccbbe582d7093db88bd89309d98802c4d70fd6c9010e13bd6a8473007f2289d68d816eb8033c1124280903c8462b23c042c5f53d8525b049d89ec205f5d109593b950504dea5ef57314f519097a4f902336bf82431d358f1de5428bac44e9285d4032c2799c97332ba5c0039846466e4e6511dd6ea3ef768bcd3a5103f072e9ba2c974e72ada1a94b13a14e8fe76115ff822a7cac026a184bdb7700027802f099f79e0a879ef49c1f6fd8aaf767bb153c786df33196cf73eb62c2c8ac5eee13f98cf9cfb3c82a2ad28bbca754232204d10a185f5fbd8c0bc75f42ccf48ac69381a5014473a0b74b9fabf2e9f770461defb2c2392f50d89ec3db1665a0336d6fe2a27c8101c58604a61c3b758c00b6d26c992ad299928afc74b5476ed3edfc374d8d2a42a87e9ee14975494df27ff6f0ce853dcc7408b820986f63e0dd431332994c9c446a7afe563c104b142bf778dee89de7cd68dbc514b6ecee274570dfed845203474bede63c7ede08904b9ae37ad655eddf092d4075cd47172ddf654bc6f5dab0545ac07c28e7ddbef50c19f9f7aa2d0b4ad11b6ce75fe59c17867c35312aa1d1bc34ffd52ea4542ad61ec014732de7fa3ffca212dabd14312004f6c899d114aec3eedff640cddb934813597e71ffcbaa3505e3ad2dcb6de37a05e77421d06dcadfae5cd63c8e0974ff23a31acbe4fd25755c83470c8290d3dd0648962d8f47bcd28e6e16cf442f7cfd793b3366dadc93f1fb53378261a5d1bb4902caec9c5dd391904cd57b4030d1c53600d288a22b8461d4448c09395fd3294e1c470ace2a2581e8bb307d8cb9432a601e1db6e51256c75db8938e5e8eaff2f6a33d0240d150dc40541973fee91f8fe5cca444cf05e8f6173bea857c814e0d87306a943981db0ac77640cf2cb49f414e47c04787784fb2902d587fc415a88844ffab87e6d1875d6ae6a0c2cc0abd5af09f67a03d35cc444c59d10fd2e0061d1a8491035dc1c060f2f003f944958660c02fa3b4be3646be4894228cfe88a7811373a6b344ab8e98532ef8e66cd201c11de1b2b49b138fa4a48e12cdc01f3a1beaad1c408f2372a60670166298e1ee5a9029bf1a832cb8fb5344099b5246142bf2997c9e3ab5d024c549a29d8b8c3fe34ed31f098e5a6c3caceebe564d6ad6b9ced3a5b86381dabc06ae6a8086cbe389e43d2746cea956ebe374bf1e1087e2151137be3b6b42c1312e5addfc2563d33d3ec7335ce7c02133c299d7ba03aa7433b061e9c612a3170a5fcbd07cf75839352fbdceb77dce4d04f2faac3c4b58638d24f97ce8bee207dd2db5fabe81b54bf2ff8c0cf96ea867607e0515c193d4372374ea595dca723bd2bacc7ac642d54dd046dbd60cb44c54089a4fb9300b80f9f93ede815922f3dd3f21852db89ff4647c93e4ef8da44e122d346580e4788ac9c9bdd92c31639d7cc7e790b6886f81cdc1f898200bcdf2938f8831ef6bb3505bbba1f682afd60dd078c841ff9c3cb96d16d7d32e74611fcd35a75ba27e36d0f8f31fd7981882820129c7c540709be084406cc81a2aa279458521e68e409b520eeda833c236d4000ef5b32f5042805385b99dc1694cd973d0d40b461e6fe36db81e59c99b92e1303ee303faff15794803b0bafc0f5afa5ad89d6a01aa337f57b14450fa643ae51805e6fef452387a6dc8421d16be51a5c1e4cf3d60062ff60ed4f957f6253725d49bc019d5854fbab2e9ae9f400c5fe26e25e32725dc7dd70820477c3e0cf717cc466ea739e1cf642c2820de11ec469b182a6c118507efaf8dc8c928eec02f7d2141bb2639d2f40881f559e8dacf2f0f44b44b13839de83e59c40436b9969dfa29810091a3c74671ef76a59d12063b6ff14c6904c44e37cc2f5d3a5ca5124ac60a4cdf6f6791363d709ae05ab60bcb006deef336d47714912edf0ac49d8676ae750e46f1764a2aaec77263143d798f635be1d2dc1595cedbf2a3f08f1aa8571d34cfbe92a7284f8eeb76fc1aeb71f5ccd703d223de5da71b11ac0e5f57950580a122fd2fef8445805a49d4027ef790216a972220b6abc7af8ea8bc72248570e83033ce082a680c4906f01ca205c3f4071a117b0d5e84806c81e4f644aebfdd765b70d37ab4c768a78e1029f63780cbe47571d8dfa2d2562872c2162887c4202b3437594843473a76712493de5712349d28d8a3dda8befb00a22a8f0a1508bd80c0ee298b31575b10cdd19bbca815796b7820320ec578d1ddeed24f3d5443110fe1520a06e614e52faa645d2069a6970e65a38e68dacdaa85e1a3e52bfd4736ec085bae2007996e4195f430a6b827c2e6116ef2af7e9d4565da8ab407558bdc2b7524a5b95c5526fcd38c72850a117e80f1c47b5be8cba9aa46a3e19d07d4a8ab196fe1f917ee798fcb70c65247bf9eeac6070e9208c536463102ae64ec8a4adf9f7f63fa2ba0317091e86264cfed88f74a54216a4458d7e08edc1b65f5caccfe78bc23b51d42436daaf7cbe87e798a6bacbdceec827069cc7e15a509d530b2dfcb10667d11b255aa1122166985d179d60a02c43d615072e52590a8c4d60f080c91159d1c854a02709e36a499199b46f086ae194a3c62e516636ba5ab293a0b3342bd0d03fe4c5183fc5af52d94d6962d8a1ea307a5451d45971520dc01d13355281c42a0bba012f984a1616ee98a232022fb0bce98269d1dcf720061ac9005bc6ecd611b05e395c4d0c7a330a2eb9e3420a20c8de31bf0ad9b31652391a686e70b07f457856a1e416d82e9233aa7be8e31481ef07fec9ea219d367b7d222534eab995c450b49249eefa2ea39e8b2b5e5c6b124659cf1012774756bc7b2cd1b3e2272ba451b9adc2a31116e149b8000d2ebf1eaaab756f76256da48196d09c3796c01374db6e69ea8fb007da9f1255e2b1cb55a6a95e057e770ed5faa6408bc7ab45dde79cdab98f6b83a80a11cc7a92ec100f9b0a7ba0756a9eefcd1d9c9b079591a4e29447e943ef719274816ebec26ad05da374115a01792ca79f524273acde09e5c89e01443352f43a59e18300ba81a8311a465d99125fa99101a1d0c532e25f11cccf8a93151c1048b073a21868a53c25f74ba1d58fe9a324e5e415ea65b076398080be06d4ada2c873b5a92dbd33c0fff3ae0556a962e3ce5458152a3abb107c93352a0ccce9bd4038fa50096ebacc903af7136949814a1de449256cc9acea86ae5005fe93acc2a5d18022fcee71f4be00216f79477590451dfb0a224998825dc6294eb066f54e1272bbdcc358597a084fbb8facaf9f28e85ce09afcf624fbc154d6f9d81e61200c266b351e64048d17b3a57b966506abad6ae5b7c26c7699681332a798e68d390bb7b243346ba1422de20491e16950c3b5ea66f632df32c5dce272b28bab9802c4c94ec85a9640a62e0bf7b34165d725c5e7747950e22449aee0b716614046f633aeff930dbb1454d6a584547e5c7dab821e4d5233022ecdb8702d87fe810a68fb0b96a96e8610a737c725e0766b7182ea961e9457ed8c1a61a589039afa37ce4ac539beb34286f85271eb3f6501c0a7e2749f7cf1b6d27292d0b48280f5604edc4cd7ddd5669ab31ca2e1e4650353046491369545f4b5de64164120369db736ea0dd79616682572a2fcb4b3c74660c5888322a1578a777ad838b1c1fbaa4ae20201a0529e88370e5c6bf97d1a117426e7d383fa48024a720c3040563b0516d772942b6e32ded0fe3d6c107a6e929ff377e665c5413c8f004a27999a7fa50fe8342ab300f0f8838efe441b67e4b96ef0fdbc1fd9b4caed2ca6c930ca796460421d0f1321b8482fec8b60a27cd1549fdf0063f887da879da28416d5963ae92485ff25015d2c752fb9005b47a25daae8069a2837da92cd7578598dd5d491778ef0b6d2d269f082bf28252333022190f01cc482b8a2b8102f15268f109057377fbbb8c57257eb841da8ac5e24856da0c5acea340e517e4aca636e32b6b0daa5021f4a665e3f7b7c78cb78709a09271222f0a1f78ca32c7d48c89844ac6e842fee80baf1b1c341c0c45d00c6989ca5023508b7fb402c996bc36ff7d4981ffb335b79342b17ecb990df542f56f67ee6b8f0901317d9108c84cbb53451b29b2b33120a546f2a17ff5a12068e8fe21e9c1028c0a6b35c29317560b04ff97b3e4973d9540923dc41cbe89f81ef77e7ec73d83c0d87818bd2cd30617223e70f62b313b2323174532a4cd2ae2426d480cde46a89230b7e7121302d19082151ff07c64ac622c0a17a0c2a84128050d220776263d7a097c051a7f9780fa6cf3d2e4ae8427f0c92ca483974150df73b2d1af7620e2dd809340da2d221b18fe3b95cfff54dcc911e2e8b31e1ff5ffd5df8f5e771b5da12e1ccf8f466b7184f4f4b3ceeca4c07b4a644bb442de1089395097b5195affd74bf26e66470c74cf418144186fdf7c5a8c5683889f264a018e58c94941138f00dbc0d783011fc65d3e14ead1e6adf5338971167bf316d849773891fe4f7b76baa9212f44cdf3d4adeb31cd021460aa47b6391c232aec6467d03a1881a07226311d21f38a4a5fa98ba32f397c20e721f460721565864b0285d5979f860e7913a9eb3f8ee4757c6cd67af552a4f1540d9e382a0426b9dfffecb9ed4696ea8a30735775b0cfa2c34c94527dd7069e8a31c1dfc96b325b879b6908191740cea711376edd552ad4ca91cb857700def9e7997122180cd1b5bb1dc6be990182bfb9ae22a7a1ff595826059510ba3b01103faaa5c868ab5eef41deac81b9c6072fa6e8f5d1d8bb5a0e666b07960330594a7a50552dc35abf4b8f4ccef841c48721281098df1b9403266b77dafdab679b32fce61b7777149655592edaf34351fb43d8cf6e9d06faf4ccb745a48a982b9ea08c4af3a9fab2e0730331664a8caa170e53a47371b200048ee3db360cda551744ab7d3b89716534c9e6245a6e39ac6d392a99a370f0b874e46a928c1539e2bbf38ce2a44b27632413336f098f57a694526a304083efcbee8e4afd9d2e4af5737ebf368fa8f18029286e81a94407a3492825ca3ec0b6bfdd5b5ff03dd02b1a680ec269e29e35ceea4ac018bf26def385e2376447eeff477ee9021f48db8315892545a4d203ad6086551eeaaf0e01c2a8fb5cfb9e53259bb13250c5bc0f62c0cce58e37307ad581abeadc0a31dd70c106f0e58c2bd53d3cd29ab7e308c5886bc1a4e5a10afeec2de0204aa7dab9fa960e09b9da507436fff121de78622d267b43874be7b8541f79066e1ebd6cde6c223617d7fc03398b891cf80f46f700902ff0f6400b96fe2dd0cceaaed6bf2c95f3bb66010352b3d3e3c6b99fc97631aae9f11d671b2fed069e1a55f5c58117ab926238bccdc54d6cfe2597301c428ca67c4d07ffde35923ffb44a36c8d607a3cb2db3bf582a9b7dc8fd0bd899a6768b28ad801d3fac38d928e7fc604595a9c89afd43652d81b7a8ee23368a9d88dbb375db83f73bd675a220f96c64a980120aacbd23c81e78402c950b3cd5f379b38c5bd9aaf9e1fd43b4fd63788f438605b7af472774fe9d7f349bf9d7be6d363b718f8628f3aa2c78b6ef25b5536e0993243de5f0ceb62dc1ee60c257e3c40cff3a33005a51dc16c79213242248ce8c7e3565838e38f1183fe8ba47fbb8c61f1486ed6678d74f1a7846d2fa6308d8371fb209622e3a291af983f24b2b183e199872fb863e0c0e2686d4566d0620888a5c194f32b59d59b2b0f0ec795020d7ae4e4400f399e8a49f81959681ba44d99cadb6cb6087c1f6f2680ba5a5bd29e8aa867ee73017996dcc6a93b2c4b2a7e10b468232a20d79ef6728d3c76a0850f799f6baba86b2651e6d75e8febdad71f46f9bf6638fd7d9638617aeeb92dc7866bbb587636f8626f198d5d60c0fa1f88abb0e5ac186cd4c51354c980f1e8ac2c90730d30e585a4252d46f87da5be7bc74501f821144de6700c43e52201027993620f9b04f82bdec153ac1dd4ce126d2cf083415af33403025f6ec391a48c96c2e9251246622c88c80ddda6230ffdd4198d130b8fe4568858824a174571262880e6ce1ff2270bf82f443b1950b7f75d73bde42c10e2dd29889b5e2359250640be1285a041ded2f5ea092850c5988caf4d03bf6be85a7818831df5f18b88f5d088db3cc6d8f8ca48d36de28b960f64261d5e9d7e931f657ef88f29d3285ad63dc789a9dd3a387abc8a0045a38c1aede2f94c1b20c270892d33554ea4cf71ab8ed30096583c9985957042c75161b58f158daaa0b3e135d2f0f3e41c335246418508562509ac6f75ed30e9ccfccfcaa541d6a73308442ec4aac45ae38a89538a81f405be040e5218c683c2f2c72583cebabbc675a0fc37cfd921b5d885361e5f4d29c6b791b234e4007f367e4791d4518bd10e4dce1d2ec8589c78d2319a2c84f3a325ee95a18fbd3ccaf71652e83d2133f940da21addef78c1d6919b0d932c7eae20908fc89262bd48df34e685b3ce73bd71c146af6dd416dfabed0caa170a9ea6c6ce49d0846136f64dc4e5d8f71b4e4f6c42a0c2c75a8c586dd8eec1770189b8d16c1f4abd3c30218ffd66c317f4838ebd1d9112aa367722ba69f79ca1f9b644f1ea5e62984fd44ba8beaa84039f8996bfceb84f21aa8f7585209163002822005131e59c9e3b9e3779c8e2a842a294b20a74330b222ef27e853c1f5982f9ba9c97f653833e55371725875699f882b3fa233d062aa0a100907e1f8890e635e626fc384e63dfb2fdb477fad8dd7fc85fa5078723e899e51944ae0a37533c86e8712877f4185e675428b3d926d5afc78638c9224e0e7efea31fc58353a780d48ee0925f73c6b8e0b46e3d7295dcd4a90d81f480cd922ebc2aaa551ae9367504200dbcf540cea9e603464dad240eda57c823c519b9c04aea8a0ecc62699316ac0ebefe60a6615d9688c34b89e12bd073299030dc37bf99829623a90570f4744adeaf026e523f4b448b85b11f37f185c5aaecb643a5156990f90640249423a1e6818b173bcace20a4713c2da217f3ead1e1fbc834d929bd814b5c6031216d97235372e332c4204f4ff855743880c0f195247692daced14b4ecf2d08fb0d399e74843518e24d1daebd8e89c8d20234f3eb1c74bdf94dafd3d1452072425310ba7c705b6cccfb5643eeb816a401f4da310a1e5d3f95abe3280384fc6ec431a4b5087bb2d3275c9ae770ff83a489675ec672495399ea2899772bba29b48d7e3d5e59989087bbd7a4625d6079e36b5e237bc037b81cf64cd00d44dd07cd8b3103f160dd75ac3ce6a08dc8f9104851d3beee2f02fb012dac64ceab245ed88828b532ee558d2a40074b235534af4b01142a884f8bf376320ad21f31b8b4071e26dc5d9f278d612e3c2d74197be443c1191acaebe6a94dc5a0302c4d001fbe079c548337ca3cf85596f43d5543ab3332324e23aa9a1ac8afb5203d37ef1461a4f3e72df909d073cc8da23df3233fd8decf9867079838b53febf683f7935bddc75b90728361fce4e598348f53e073eb0f60df712637008a104ec368695236e9b0a60c5fd3f877381b8337df30ac046c81de265a04456e1f531376245959c74c8903c64dffb25debb407a5d11438c1042ccd0280c80140acbebb22398f5837962e0c52913ea14b2bc20f7f72c6b03ae82fe3e7e1b21310c0902e7eb6aa50478c99264c9144f8c1e62cadba04fb244f44e6014434aa8ca7a525fc9505d0680451a33b7e0cb556481b3f62f74ec06228dbc48b51381affa25b45582d284c0abf5a5795b9e35e701b5ae622c9daa3263751f29623178048f601f4312d399a9d2391100ad40e4a223c23cd8c48f7965950a8c07eab7534f394ee1131f7720f368c42ed9490a1b75fe22742039d7212fafa1a1f9470f92b0d58d1eeca07a8a3889ac798aa02564f901d6ec48b44cd85a241b9c6bf740261bd8e863fe9bb6d65a0dff1eb678f1ea75620942b3981071348c6347b5568765b410d591dd91f66c0b4fe99140f8134ad9c12e4200bf761b408a7edd5ea3e9647f163aa1e0d441709df5cc80c711244464a094c36da9613a59f011171ff7ca45106dbb73a1f5969aea4045e31b3b117874e28b480c9504bba622fc627fa3ee33438b029527636cd0d585f038533893421457110a32754054f2323ad548bbf092a7b5041771d83b11d765822cb0351aed0e4a171d00b5db14449c89b35596b7806038aa6db001e7910516bd3a0f1468de3c60e56bba6026a25de32fbbc4516da67eaa23fbbe4eddb9f6d94eb9fedc5ed9e2a58a7abbb752055129214bc34d842b725b31a10ff80253df240944dc2fe4a733e727991c589a2ef76c23f22dc9f072ea27b8d6ff4966a08345100b8967ca331a5e963ba194399226b548aecc9a471b8a9cd80069b2e1affb1df323db692953ee1b46822f2c152f359085916783800aa85cc7d09767055eb97106870f50c480c8b9816c5568da5bba37d1d3d6378c944262209c35ff965142b6a9bfae7ef22f680b02b94012528112ab2dbe44126cabf80d8098b111ee38f61d83eee1776524b11483a3a904a87daff01e4f74beb073ec5b9d9838dddf1cce8c23fb634300846fa678af7fa467ce11fd69d3d2d312ace3becbdeeed1a60bb3b25b20273e35bd0471720c36747ea96d18e0769de2b190cde1cf9afa49fb09323ea079ab8422ccdd5f48f510fc1553ec97ac42270afc9f94608e517907ff80910e981b2d5a546364b55bb653246e4caf220984bb9e871aac4485bc6896ff08ad76e6a319665882a8f5886f38d529bc42e67af430f039857fc6c146e66b336e31455d33a0742da43732d82689da4f1b7d66fee033b5966d389b2c1e30877fbc0b3d1c62343cfeede4bf31366ec09e39d484742cd8b0c34e64473384a129cadf9e24148514249333b445c080e1972308571250cb4bebe5a8dadabcd2c038de7d61feda0980e7a5b2d66683324a6d1f93a458c2322a0c2518adfa94e1769b2927abc7b2ba9307891aa8e5257b941d6942765403ca8f101212d0b650db3624a4c285ab4f0c81508143cda1ab87f37240204948425811513d707742781dfd07f8c83f272e3b3141055cff5b0bc2b9af419ee15a4d1c3aa506458aa3eb6d6cc355e33b1d93d34a0f72e81575268ef911dc4bd2be2c8298844d25f1ad7b357782d5429a42f2fa44cca4cd7f4f119a50dcfe361e006276f551cff6a2b10293242011cff32e92e9c344533c2994208f4c869549c96d8ca321cc25efdf00f39ed53eb8612c9768fb6b2f19baa148a5b3d0de40847b742ebf9971266dc64d1cf68fb4eb88a082f7998a48c32baf62a22ab2bdf89a185f5a57660e4c44d7906f75ccc8d21806fd271c8a787ef836ff3c47db033d30fcf46cb97ccd7a53b14e7774c13019d73aba6c2dae52d205f55ad19701112356d1971fb51ffd5da4ab7a53e0db279be7d8548a418b4fde69db21c4c8720a4c4405dc5427eae072110c2e4fe061c5c238ad7ed508006e75f3984b16b03d73c7826aa63d23db5df6635aa556d11217c83bdb22fddcc56f4239a9b77d8f62f51638e05ff0fc6be6e75d28a570aecdde77fd44ca22d22315af2df8281fa533e9fb2a142fa9c5cfc8389a772287af8462368904046ed1e2d0b0dea346a7ffd6bc6f56d09ca5f5b35bb8d7c48ecfe8164dc8b4683a30741ca57bc16586e810033d849a51cf6827396c5190b101351326d6e1a8ba546f17a9cd86c823224e56157fdcc075202c90b6d26e62be12b7318ba828052abf770c5355bbe542783cc39a45039c82179eecf5b0e5373415e9f5f6802c6968d632c2827af3caa110e8981bf8841ff2404ee601bcbfb1319596cd3cb28ce668b7e0be5c1d6225110377604f9efce338bd41398fd0d2e04997e2282dd3778122586b3ceac13ab58bd80ecbc9c6d52d0752233830664018f6220afb2b9a48ccffbaa783e41903d991f1619f43fec4a0f08df0ebd503aee696e60e075c0076e219b6f1f2c299931ec7f41803205bacd81ab6a395e3890ce5abc6feac6e7f2eeb4f2bb55de3478f970e902591b9f87a9d9511ec594ca3959131c1d45ffaa07c8cebc71903d5910c6008267f5bba491e18c9dcdcdd3c13923f7769494d445889cc7b0d6a4c48ff7606e7411bf618444c2f82ffc1ceff38b4d6543615787b0c8e53c6a57a6599ae1dd57aadaca5290e4893d5b87bb7614788497b09887c35e4b14a6ec84099d96b6f5e5793f3380dfe7bdba579b297d5c45e5eb5dc5e2712ec28bcad65c1fa941236ece4babbccd706e7765ef1622f8b5dfb938d9646ae3d949a09f46794634fac2d7d2b0c725801228241db2ef8f73e278af27706cad70640a3ad3d43defb9b0f189a323aa3de5a3b51cd4e63995c9a0cefe8216d6e81bc27f5f96dad6b63d0e25501527eab3c4614e6aee3619cf23c77ec62ffe749f36fe3d7dfa17f5d99c941c521f311f89e8e320aa04cd8d1cd0d76308a5d9a536bceef8343f34645abde4aa7097a4256dcfb0d45b1891f25f35fe592d8b6403926ca5fc72ca4da0d37df004260dc2b2c67aae102cbf61f1cb79d4b507c45f0303f5f84cbb449d1272a36ac36fa48d2526a6201df6be8f736ffdc1aa2a8992a1e5b1508cc337550508db4dc9c06d120731fcaed45d44df3d3c230e06a396d1ab492b3ff3ed012e74ab2df4f71199657b998628a484a2fefbabfd31ad4b7f440a258dbc232dea76568233d63fba01ffd7d130efe6a1b3232aa99b6ed27307eb438525faa8cb84c54644dcff6767e0d0f66e65d9b6a579a905e761e682e9510257b9c265670e54b9805ba3e60dd1453a977c83ef926db342f2b5ecc7de4fd399b8d92065991959b0c0d4a18d6405e180003d38a34595f75ebf18ffcd8c58b87b295797aa96462a14c0187ba326f6c96eee8b8688b54aeebe940622d639c127bc41b8cd70ac9e38da232840f34132d9794604ad852688c2036507260262a56646748226b2737eed5b1c17e7d7fc984ea019a0d889fdd0e84f9e384f233aa342632738724c88e0d7b214b43b1e43be38270b3ae19264d8c2dae4c796ae10ec1a71198afb6b2975fd4d1e4bd42a86c486353ece288bb252451ff4f455162c97f8be79c055116fdd816a65acb922f1a03f1507715d4488768fb2da9c580c10e34bb1fa77291d143433575f3487689c5f3c79bcf4601b2aa84397736341c2617a1cae9df3b8e6fa6f1427bbc7e5ad1e184e02a55fdf14239c77c1287bb331307facd5abccb2c0436654985e8cf98d6083de7643f617ad093241480b6595214766bc04a90aba3902b223477f738c7c3627a6ffcf71621635722cbd8bfc1c89ec45bfc8f66a71d8e031f24b2e8c948e0ad780e040290e99e70aab08dc3fd88745c7510fab53f80678db0889b2dcb4b3604b8b59e8251b328ee077017c1adfd610e27b96ef34deb20a5ed65752ad013986fcd0ce2287312b2003be2a014efbdfb637a85603d2dca46d2cf480e0370e5c8f805c6de6592e2e8cfbf986360e93235632dc1d03e8633c87ec1edb1e06249ad98cc016a7be5080cec72ff002ee0b9ef1981cc9c81d5e414761b218567df65ebe6374dff8c441d142ce4da60db2fda48e4178a3e64e808d194bebb30d16bb75600298e5640f062bd8508ed75eb0bf4601d0d99e08592bd0fe36c1e39f1114e5be04725d0dc9b3052aea8fb144863512b608295dc55a9e9e986bb1606c398c6b8ab129c6383e19d3659cc2848ffba90ea540882badb9641a42ae607f20fa81fe50d928f303854f4e5908b666f9d94579000a6e94d5d199ad28825f4760ecd44c5125ade787454b1bf900ee5403a5e70429a4dfdc4832d63b1058f4793a6fd908b275989806d6b3ecb7370600623ac94019dbea72e493fab941a84a354a75c985aa3fd65f7335e04c6b34d9968e5a2828328c30718705da3a671e89e27afc8a5dd6107c3c1c44e041bf8e223a1391431bbb4354cd3a4029b4362a7c9f6798dc8285ade3480875e6f9eea557bf4be954deebca3b5d81b8f4a25a2bd838c61a6c63ce6cd4b8ad204779e268a065eb6dc25d1075f8752301d3c194d9162d8d90c9e7c483efd974e6283250ce97c02ce22a959d8b092175f81a66cc591a4a2a112c8cd533185f4d54a4e2d0ff12d823906155f832a0cc89b2dce65af25530f6496c5a8bba36c0f69a323179a46dd6c1a5cf73414f1acfae0951512e5bed02148257a18f30e188ad45b68b3cc6115bc26b6326b18126a2e5b113b0514aaaf071b682a94af1fc63cf7605fc9993264a6099adb79e88470444ab2a659d0b635013a0e577326d7e55336cece5d8380437071a36a7bdd33c7469888cd3eee5d308084aee362f5d4f9f7f7571d06c8478d9a2297dd5391f04a969abd778187acbe466daba1a7d96614008420c67794ed6502a3a4aa016032dcc99e2270e0957e90d61f7f3d916e6a441bfe6120f405dfe859f0c0de66dac3a72b71487a482040f8552052a0e44403594b8be1b7460d25cba4727fa8055fc58f179c1193a1130c8a5df010ca201a80f756aefc1b46e6260ec64b178aa39f859408455e18796ba7ef187285df86959813c72c6a71cdbc4e64d7b2ae7b371daf156d9a36c4d26710f9ae0b3b1015c0c8bc870c3b800d9c01f204dcd5389256bb46ae71693a3ea53a91515bc5438a9602d0256170b95f102e9aac8b7f3f442457ada4938c50e7a9a88539ddd12d651a440b4839584d480bd63b7316e68e08f159fd7c38da6ac63b4f2e75ffbed674c3c105d8c770f503964ebc7881b478079a55e2027f41bab89923307a8c12cdd065f9027a952243ca66f1418922e214fc42b496e77d041d84e9696769aee8a6c445913f0a7bdc1146f13b566533ea6bc1894a8b3a78cf35042413b82447b263fe58005dc7cae6b8508276135c8baa7017ed24c3e4762f6cd5fd8f4d303d2d58a5f911b3dd63729dc757dea36d7c2454ee0ff8581aaefc8e39129ca79fdef00a9ac1a731a82dd3e09ee983b7bbfe6353c2951b1c84d546e91d6540eeb818824aa20a4e241969a1a0347e3d917fa1eec699080ad0782ea47acb3a6c23b7a932d086675795a0b78f77c82d72f5487f1669918f254ec49b98128c31b05fc1b974e6bae762298983dfef7ad1ee719ae301749b9bb1903bd1c23ce6ddd077a1728c15028c36bcd2e07ac39159f0053af811eeb81aa41ab89838e59f71bc14c099f60fc4e8823d1c13240816377a0ee988db1ed23c400e6b264ba4d57f3d090d2f6c27a25e261049633ace8a82d0bb53a3c00080547eb29746647e68f6fb6a9aa6d556bc6e6b34610204ca0bbd9d1f29a43f6c089a1a5c595361831c35bd2949d54c1ab1b9ee3f2833519244c0489bae24dbb293be62422d2bc6b4e62eed5c8f93b73397a49e631edbc525aad80f62cb9df656ead23394b3307909ccf71786595207f02405b740f9b55484a5f0ca522aa492c4c2b63909943347ff01c61786361388ee00932e30f3b461d915dceefc01db671e22665bbf423e4d7435b7fe17b2bbe59b05fbe7507bdf4457bfe6ac66d42f5485531b0b88a66d790f80454d1649bab70722ec0b1c6b520bef1ff757e52656f6620c01798335f4210981352a1073ad189035565937a2f3c169f26f00bf694d0ca1c93ecbd771b4714e4640f5f20353ee54dc4096526f057f6de1a1186afdbb85fc55837a8fb3cba6370d1a4d78dafc128c0b2600d3b831cc2fa6ab557ce903c8f22fca76ea26cddc850e05c5c7c3954a0cd0ce0c9c92c277b3c7ec0b4e6411c5e0e14e5588bc75360a9c10cb2a7a29937fddee89f086ed003c5b98c48f9e7b71dbe4f9ffeb8a1f05e7a14b1faf6f36897865ec81d6ae2f9f23cc0bd7be72d0a39e3acc1164028856df7540f9ad90d0df8a90e8ae40f715cc38886239ef994488633423e1a806be7e524cc5670872b5f1a51f9e8a35f71f00f967cf6b2404d01d28414b321418282754330988413cc352f4b7f746351137b88be237d6b69f2c12bd53fa794bbcccfd92b2817207fd300614e95e28c5dd71c8dbf98877abe5d202551a6fc4552a6f797361dadc46bac5f9fe4d7f883167f1e10e5f8852fe05eeddc2e864ce3adf2d434eddcf0b0248d1c205c4e0456573830f12b8b8fa764e88748a0a6481001517ab54c7c6306bbbb0e2e100f924abf219a85ceecffb00c441187935cffbec7ab6a1324f72d206578c691b43b60a552db408a4d8059cc48e81c591a438be7d1c1077d27e356b5a4ede95cf9200a8f56860cb279230c6aaa738992e4dc7f464057e62251ce1a6b09e6db8d8a77646e8b044aff828a765b7f7aba8d33ca61b6c4964901a98b179ede97524b9b2356db59dec6e32a5bdc7fd0d48631150c38699a75190515eb0b68434159d50552c2b8240e6b1b40e1f5e4eb74a279f8035f2ec2ad6417fe2973f1bf7675baee96e652c026e8ecf0fc6e533ac90446461343488e6e733a25786fa60efbef330103b6e5446011ea94134c4fc30b93594c37526df30923e68f071db6b85545b3120669dea1f0eee1f8ae8b54b1a526630b4ff962426628a2b76332ce46c715b4d7dd0a58a482012bff978791849d0a8ebd187ee648e6a72538d658db25c3c20b124f852126844070c38798416971ac3c82324881683dd00614fb268b8d6de1e68d8e7c36ac974ba9391c59c7ed87296b5737aabaaae1faa4aa462f9553b511280e1ef277228cf702831e87f83bfaafba647dedb9b8e9f986f6da25942b108fa82c3b72e0ac09c88c7a838ea4e927a37c5f54c2cc89a6a80c30c1e2abd06b2d87691926415975786d25b1466478d97773b4e9a057a529bb7b756eb9c05e7572cca1b18b5655a2dc55ebf840991a3c1a7cb109e03d084bf3bcd4806fc5e2249ace3d87dfb89b9dcc8f8486a1998ec6852a6ba9c0adf2dc707f0fb7922fd31beb57669e49ccb2600d6193d5883e18ee91681901cfa5329bae859a89f8a5cef03ad434c2cff2dd17afd07dea9751290cdda97301d9e08fa5bec4105b8747b97fe87792958117cf849901578441d3a6f2d02d007983a2855b20cf2c5425a5c79f146474b287c044928f02f97b3d715534dfdf04cf4c861542141ab75f006db962f717f8ec97552c7a99260214356458f74b00a5153b3b261f94e02feaeaa160ade1919dac03e000da5dbcd8bf7232beac63ff651f30da2be8cdfa8c1f2cba58c50ffec48f671f5e486811e4ce9c2e5b1c9a7ef47d20e4a9371e2e8a6d39f4171bdecb58f67fd3acbbfed7188ee1900321bde9661a149d7e5c9d0bdee79150c7e05ff405223469de1911585a7fac2f4a7a48d30fe7fd72febfee60177763b62062ae2a71c30941f82866d66a13240bb34055984fe8ea323f66ffeadc2cb38a980834647eb3575efb531820650f85ff6651dcd5970ddcac1c034ee44661e19263b3173a0e396558abfdc7a3e00db359c4b546f60e5cb0599c66b317f5611721ee163b250f45236339d72c38fc72cb5472ba970c972af30deccf020167ba311721355be605a8e3b05eb4ef72bf9a058fcddfa8563a8ec40fc948562470bc4abc5ea61b7506441b9d435a2c3bc188976f9ceae19078611ce1708ee8596138bc65d25d167127a304f834a172e757b256e76e583bc663f24efaebbe83ce63ff273138950d4aa09adc6ee74b782a92ac64e58097a72fa78b72278d95698967a959ffc13be92b4cc603950cdf5ff43d373d4af9ef7ff8d6c61735bdab3ba9dbdf0a5662426571fb5a7295d4f5c4b63b5dc31e767092b05fe2fc8decf3d1348aa623b11ece9536e86a952e9f3e6609b49f9a40073e22595de5d8369a63c5ded29044f703b2bf7fd1a4e90d0ee59f4941827d89a38ebeab8ca4e1a05a1099818d6fb8fbc2a108909563d11145708eef584f14b2802a92bddc213f590cdcaa3170545d461fbd4073a3de186e1cc33686f9b36c36471b3a1655301b9210fd78920fe303d96c37304711404bf2aa3d9371ff7a3fe880d80416842e66f8411d2d9a6a511ab66d08435489ac87232ffcdd3408cf16bafc8308ec34442eda12c53e681174883cb0259cc98e8abdc02f44eebf220027c98542dacaa867267d518e1ac5a851eb89822df7fc7cce8e2d5ccb11c71ae12939eda1dd3f9f95aa42667751d1b88d2ceb9b3f8d6213fddfe52317c8b4818f66789dea06d69eb76c65ccc0af5e2ca040cd054b39e352264ee8f271e04bb7b6baa8618cb003e163f86d04b6a28768180b75dea0dc12c662de833b30b5f206bd9ab932322bb5f788c9d2a8137ff1779530ff06edbb200a4b05504b51da1ddea099e98aa2658bee0fe8b075fb98bec18dc35d08eea7760784906a469e274989cea2b477404da46d2f16adb563bd5749c127cbe2a1f6cade697f439600b1115ccf825775121c7de1595f618b13ef61dbaaed08c8a7c1225423730ff64b1a8e10ee01f39ff2e18cd5a653efbf112840663eba7fa523108d8846080eb0e6cb0b89077916419910b3e06413b999350b15588a460a0f5f8d95d94578013cd98b35e31f509058d31d2bc55775df4518a98345d59cd4c2bdb46f59b35d9d7ea4c34d9876b1e78345c883e235d6ac39f7d89c99a47ec09a22c979ff4e7b34196677633bd489e6da4b4174b205562b75a467faed264cb023f8747e3669d5df308d620c6b00a98c813ee0578a63c3b47adc87a119576857acce77b0d3771ed63531573564e240d0d578e4b430a6a8612d05747d532ff4abe2e69c9304fc8e30137b16da4d6873fac10157584f6c91707832fd433036afd5ffe34e6beea8fae4dcfa2c372c37ceaf9c66505ce4050039dd1d32beb71df01a37761497d7a58f30884e4cea24e0e528245225e7b36dfe938603a89bceea80d4e26cebaba9e5f170b4a0c7b278b8b4d5dc22026c7b0293245a07229166c5249cecaf4df379e056c0845a90a3a9d3d29a4bae835b760323307bced0ea3a67181846806545a0988f255d9664c18cbd573d0d2200ef4acb352db665829176746a06012a528e41ace6f2fea02070e3b01a5e4b31587b5c70c190ef5fd8d58da6c7ad95fd9dbf4b18ff59a44db63ec458396522afb15fd5d362404072291575a6b8bb86ec6b3a723a95f848d05e95c0adc77510712e550783cc32195787b0edfa5fb04eaeebe838845debf1e1015826beb05388c99bea4d78c46f0add1a297a4b660bd07d221401986345d2b209d01a5aee0184327045c1f8460a5d1c1107003087e551c30c594783175b4733f4e74e6451177b3be047a9228366783b04299ec007fc40e5a4b3219d61f9ec06edac692f70ecb4235f313565aacb72b6bcde4869c9f7a9e29a2695ae85704f333590c5eb93e276020c6f518f9b7857d9181d4487cac1f42c2c4b0ea78c2aa27200d90cf1c76383e11c28dc637bb4088e4412413eb5d89af75f994eedf27b8c80bead0ec3d73ad0d6ad769174596cfce2eaac2fbbf111b8a30d4bf45b393e0ed942d81cf21aa88c3c924b03a115a9dae3dfccdec07fdd05f6de5d24c029e06b256ab1a15192bb7681b0a0d25a1ecedd95c87ccb5feb58f9a400edf05ac7fc1e6911d41f1d57c3f91604a809fca04542527db0a805d08a64cc4bc439824494df694e214e3f6081b4a1d20acaa7adfdeb163f106e5871a844303c6e54778ddcf202d019410b013d1369f211e8174b20e17d5c5849dcfc2a372123e2bd1d83bf628ddf5dde622149991785d031599027ff885b4f0878f600e1e072c7d5e0801d1c949a144f6c04fb23afdf6542618203dc2cb8a412bb3d231fcdd74ff31b8eaacc7603c640c361690e659c69ebe7618bf3104c7eaf2b4c79f38932721db2446ce9214a986ee8e28912f3c2989ad72db6cdf4145121a34e6da2a8aa4e7d31e427883f6391c623593d14edb968d48dd0a0367cd44d66f674caceea3106d3dcb4da63c4770b2201672d9370abcefc85f4ce98fc0ebacffabd30ed67d30fd2b219ceb389ca73ef10bdcbce2cc2a6c09bab486c1e2d1094dc88584bbf3ee655c8ade975a91b97457d9e33eb14c66533a9e2401a00a9d30218434fbbf1abbbd3028d127b0d1038e91e2cb4b985b94a7d0289c9911f3a1e3491db68f37796d60985ea390f3deabc4617f616b70029ef269949f725571b794fb5147a29b4649a8101aca74da9da279e5757b16c4dcf9c7308e05f8d3a798e72b08f94039b0bd803678a68cdaa4842f976e457d5d7f669c2d189e51d7804ad2162c908dc50add289b254924aa562dabaf5c725ed5d9eba736b3a7937d1ac2ec63580156bb17af81b18da30d97d960b141322e47799a767383fc0b23823a9b263187c1fb2ca9cdf4bceff64bf527261e37d60d94e0299e4604bb035a409099dead983d800305dc232b04284efef5f12e4b8625785e23d6304ab6c3d343813dc574b72c01010223e723fe7579501dbebd32324ceee7b451e98304fd46850e13685710046323be119fef7b03821af87fb39a423fb57dfbbd0d2097aa9a9af29b3c21714c8623ea0432352f883180d4b6e893818fc3f4ebcd3dceacde0a39c38eaca919d5aec1aa872cfd6fc0ef5d5c2b9cf84d69359a3918ba38ca914b9bb8076ec2959485c8efad0010e5e4a5a050e32b067aed4d592b051e55c43bbb198b17f476c61d41d8bd032244a457db651f731a1abf3ecaa19df554e2f4a744c835c3c29fc5ce93d20ea3e6e1ffd65c293a307f22159ed9333320884371d0001d90490c99dd625cfe500fb65d5aed53cd63d3f20110b7518362110e1138e2d8492abc9db033823b3db4ce142629838824733ee53f5d0214599facc8c738dab83784197a95ccd40e117c8f8a7118de1d95809113e3fb0c65eb606bec5c6f0f2e6c0355f47c86cf3965dd60e2d5acd1287cfc648b0ba879aaffa2718fc113f7ac4c734a97ab4c66c79af0fcf83f7433e2a263ba802b88daf0c863980ca3f57fe31e61d792c4d857fee4515c74ab21df4f7b0fd06482efafcaeca7d1c8c1a016f8d88d119e1c36ee003271b0da304b0382170e05fa7e2194e73ebd484f1afad100a75435bb50055f32c85e41c39911516d55aaf7018462f75b05e9aeb89427c968ae2ec0621a7ba83fa7ebe63d5edcf1d30437d0583bc730a571ac8e61841cfab5c3d86d566213195fafe288b3af51005b592888ee0ffa0a4c600c4fe446298548b4c1e2d837c5cacc1cd537f348ebd972a0909f4d3e771c5e4c86cbd65d1fcdfc9a17fed57beb38c531967942b79d4fa7d90705408b51422dc07507fde1cc529ebadb97544b86151eb1c6d428758974f06b069c0c6bfe674941a888d7b9100a6bb06a5064fe41b690fd97931d874797b4957eb683cb713a3f9691e679a17a617c8921abba63f151bd00d4ac90696865d048aa010020b2e0fdc77b74357aa81b6d040628374c1ccea44df11194a86e2ec50761762cd6bef8815158899a19fcc2ca53ce043e5b5f58d0a94863b9fc768e1ca2c1b62ccc9251fb4d321678839296d947bdaa482c18ebcaab293d7011d62b3611bfdfc27b699ca0ea8865e117a383bf9d7ef53dda861e75221ffa7ac41391cf9c027e1b6a07a527606fcda2b534fc17fef3bfcda25d8ac5c330601b1d9383e66e10b9f73ec0b70e4f939e2c2b2cc926bcf24896ed827f635721eea82aa7e4d9f8d649b1fe3bc5002ee235c3e7e609bccbcf3347298041b28d0340def87ebfac1251b476c9cd22696ffce80910d546c6fa6f73aee1e6411e4625400b89042d4dace2f18de7434839dde79fcbc38c778a4f3c1e100683b95613b73b4372b6962492f3d16a62ce0046b5816864401894903708275177024e24d18b80670af900227e47a6ac4600da54bb5ce180ea10549b06106118513b185d7c7e5cf8913a421460c570597e5f6c67e4a28f4642040c1d3099dc48d1837ca9f5ed0dbbf60c438d8c8d79b3aa5df6f717646379cf7a8ec0d944bab3a8b5673704ac741a788f4e35a895f3988d009f7a5897bd171b13a25678cf7d235f48967a9379b3214c7373c57a2951ade1639940fbe385ee5641f7b549c4b697878a67d045e12190effed225d3978f73083c93f7e17c87d8323cf4a1fb44cb8ee0d70c10483bb832f4f7bc7643ce373a869ece25dbf8f8d013f4120dd6f7afbd96a809940a02db4b63110c0ae2ca67bd470c33a83b86f13f5d9f6fa1e722b6a9233b934ec8211f8275c88be67afd2b59413b3b00b0af525ae77fe7af1dce9d5241fddec343f78e3c356b210b23f748d5828fae2256f8e9b21a335c0cc8d58ee9bcff0e947cdf9e411dd4dab9a34e13c2cb4bed1fb66d908a22abb990019e2173eea563c50abea0e436487fce137b711ab1fa44d00f57d2ec0fb27c6a29b78c50e775bbe8a2453bb897fe2daec2666102826cf9bad2be062270d8fa642e6f3eef4785dbb2076840f5c9e69d3a12b45a606a93691c3ce3a9509489d238d1f8c04d8e2af97b06c02bc4b780a2c9a35329bc45ec6d05274fd729111ecbf561b21769b6ede33a534c0de0948c00d33ff42a32d5529c817a71a84ba61d117597ef42c962c3b939e457727aaf7aeaf6899f7207831d5a0f7a41f96d44dcd34811bad040b4147b1099bec7fed9b3a18d9bd8801b59243136b81bd68dc31f534f44fe06f7b51b960f9f352cadcb4101bd4855b984f0886022fdc3b0b4f04a1974fb9c568fd3e8e7d90d3945bb12c1ec39501bf8e8fe203bbe8544031428fc416cd390ba2d33c25dde0716c0fd859c4203a53bba999da60e76dc1c0fe6209f28451322c7aa7bff167be868e0f582160e8267ba0b0bf2748d0adf2927b7f8e599bd66750089286d7d45ee1bb94c74e7c2a1eb33e4b49d82fee4cc9c48861db09f180f15a91bfb5b9bbf5a0f2e76b6e69865debf72afbda0119f9731b8256a5ccac66943ff4f6527b117ec9c98c721cbb93e0cba5a4909816f17fa1e4c6257f1fdd9f877a1b96649631107fb47405be1d51e814833b092fc76fc744f96fc81132d1815a7fa367c7490588c7bcd56a0c00b5d38ed994df109fa0282878bc244c07938aff822b93407133a3a0c46a02cb30c81398d11847a260963d85d58f36b13da9d5540457e0b2f22ccfb9cebc91edf97b06ad5262c2295888fd3f54d2f08458274095e25dbde888660b6cc1f5143085ae8c21c9a50319a4318c50933904ba5a0d817d2213b84383367d5ee01f33ab926b85c851178647a10cf9950b4a5e142cd5c1148dacd5d17e511e9d02b7e8c23f1e46bc480acbf276ed952ce7da774e008e2f77727e900f3964eea34abea8c2de9279cacf7c0b4c783f3ffec900fec8e013b8252b7f5bdae59bd1b7db006470f3a40275d431ca29bb39aa2f8c8de39666f916d068c80840dffaf81614d6386188f54c6142c52fe4af5e95c684278543f41740242332a482598f0957bcfa2dc1a07e1016e77ec0aa75dd1563c5554563500e3501ac5a28084342c2d0fa2063bdd7c0adffd4fb8078a70b092d8a7a6962cc2453176a17bac5296f1d764275c49be8f53fc52ac308f5494615c407ca317073534d4ad8559e7b1f14bcc1a8b59291ee886d01eb2143351e4ea76d39a78794a5da0cc6ff91476ce7b19e69f0af2b4fb980c0995fad24d476adae40493ad68381aeb66fa5d24e9d36c0ddba15eb27b73db328ad474a59f615d6be1285d71ce93c8eaf34b0c72276ac3732877eac80b5e16e0225ffabc5e42a4485c18bc10c233d24f84ae8bf31d42c33dc63e8d2b1882711af64492335adc7cc60d0944b72f03ac58ec960c35d8929fff13ee14d06763383d160b8ad18878e8b18da98654b546aacdbb7c60700fc92ce4dd32ba93262026468e2200b3613696b4f22afe282d90d9321374024e8262f6b543d2e512621f34a5c6d3cb28be23f60c4d28711bc12196e1854a84c1ff8ab16038f2ba05c9491676050336492cdea0e8225749e64404249e7c14be3e01a7240810d11dcc5337136b41fad0de1b90db92fa68e18d26c90db0d7932db44e49325c17f68204a9e7f2ae53fff31034284b5ab804b4e0bf3e11745da9954a7518603468dc34066200ded8048acae195818f56336239b232b5a8f5eb401b17e004489824ad81fca0c2d4778c711c05e0564869a5b4d75b33cfcd28c5a3bb85fe5b6d07d98558c4f36621cc1bb576c6481cc73e5b6dcd988ae06b9a22d371996da2fe2c305bea95982304bc5058208b9818de2518bed0cd41a4a22644068ffc8f66079287d1fa5c31ece2ee4cc41dcdd88343adc958738a196937388174d81289487f30b70d837eaa92dbc600bef3946531e92475947f64db586dc5a1fa62f0ec0315e20b13d67cac39eb5801fc2f37ac08700e91c1c31b30902cf2f2be41528873db322b5b83a4905f21c6f4a37e4ae1371c576571383e63179755c052afec5bdef64c87c9b1bfb2bc561bac93a9a4ea8aa86b123be1992f7fce6f953cbbfe8246d5a7a8b19fc3c1177f071a719fb546821ae3bf241d71dacabe6f444b5f7881fbb46d609dd2768cfa69f817f9ec750b819cdacc4f37787f4b0e6f7de671b1ffff1fbf942015a7223d7e9295ea01bb60de6b45dc3bf8c53ee1c2db30d6ce2fff9729de2b356e3a30ab2082121878c82803b0a7cc4f2cffb2fd380e39a84404cd543870af4e4afbc1cefba834d23e18fc79adbe6fbbb4696d83f3f05d714ecccd38cbc8452f9410ac6dfb6e916b8e1b0b60dbef7408e522724a231af251c0894106859d6acb7ad32c350f123f870d7d888319d6b48466b1cfa25d26602f10ba6d2975c8ae02599d3698e51e5a30049ee16a784a3ebb48b02678fdf7d201824dd5a4ca7b2fee7aa6c2874d0d27ade3e3749fc94c511319a95bce8ada2d98d08c1384ee0dba5672a2630d8dfc5ec774243436a415bd2a3fa4f1e5c0d78c6886ef4a6c4ce5a75fecfe87dd2a0b43609cf05667bd1f97e811562d6619c5f20183698e1d2633326b0e12c766463c8c58926d85aa7de649a9c080e092b7313d8946d85e6588505a6abaa39ff443e47438bf2b79ade8077fa0da4c72a03b9ca31d1ac0466c6883dbb5870fb11c1598b20f4c84a2c605ec6d214281756858d18beb86c22c6c041dbca5f4dd96a801734effb389da387ab0245eb5e991f7a8e9dcf292b89a77e9e0d3cc6c7502e594d746c0786f803d22ac529aa0d9f822892d27452247f7b93b021811db34e578e69f8c9887ba62d45d450df93cefcf1fba5d7d013d70e7f694ac47aad1293bf55664a064ba78bdfc1c8a3285d8c816022c0f11db33021465241a9fb7ed46dcc57beff82bed924739832245c62e8ce1435982317808c0c9e2118d73366fa01559b20806bed5a843268f72ad4cccbc9e224d1267358388b1de7c34412c86bb183e9a0af30665df3cb81d3847c7888e7a8871555fc817315510369582420643fe7263e3f0a3344e96a8fa6cb8044772e87c4027c2181e06f0a6500e70b4eb431f91a39a6563b02f6cb1feffe5d434c99a6fea1dbbfbec194e1933f9aa4bac648720afb061211428bfcbccd4b1a4de7e1acd03e1bddb8a23834b4f25a821859be4534508c88873e3b5df2012fd15b98e10023842791573e360ea3717c626eb58f7b503171d1560474dfaaf64fa8503d3b41b9ad6b11237ea36e81329f2f36d717a6c5fb3e94917cc6510b6bd2587f153f796c6e68cec27404794316cd2400db36231bb7151d55136653d4d130664ca495aa25277653d18dd0de94e59e387abb9ea864499d33f312d2257aec96fc0ad7f7a5400537bcc36f9fb2398b14aae52dbeef9322c3e577264607817ce1da5339cd862d9109409bd3ab4b7079234644c060c6560c6da9b22fb53ce330512568527579b3042c74eb726756594dc91b46cb7de1ddc520128e66bbfce6a155539bfcc1a64485ab06706348b52963cce047a83a072b9278f10d1c0104ea14f79d5920da554d0739bce3333337440f35e531849d712302340117173d6e413e8ee3e8956344653f9920159ced9aeb0148583f00da018a8adcf2c1014771999729ec8623e5dfb255cb5e3e6db25c6c1228a4b5bdf7de524a29659232b609df09b4093d72c83b07bded03f97ff1845a6d1994ef7bb9e2a58c4eeebd979bdd68fb15508aa8f7c0afebba4775dfafa03ed7b5b3f7de6bf15fb7a005668d1328fd00a314a2b5863518f58592b2eeae20de32052938fd09a87d0aa81fdfaba343d219bd4e7702e6b8ae7b4ca0d5396e9b7a95c282da4ea5134ec5843267ed705771ed7057b183280ea5629993db30c678e2896b2afbe4b68a718f540e6d543aa53f4d90a6001bcc0ca702a6fe04aeb80af5d3b3393d45a9fc277d85faef59409cefb757813845f2e3a0bee5074de75651c1ff03f52a8fc39bd3af843fbe3fa19efb1b5418de7c386f4e05f4fe04766f1a3d0aa85d9b827252c375d2ecc4652a99429b5228c3954aa6d49b7064d639ffe4882593e94da6ffc1f2a5b0f43962fef955e9673acf667546475f4ab570a7a095ef54c639234202e847e8c77586663fb31158a4fe15fab925d58fb2a8f4dcec546091d2b7944ca56f31994a2a9b1c3a666bf2305d5226cbf6c7da1f2023b22532a19f9892a0990d9ac994c866b43579663c7a3679f0cd215bf3073f52367f7e342dacc14fe3a9faaa6f791630553ff52cafbd66e3315ae86686016f116dfab3ce3dfb837a956f01714acfbd0ac4e1bef42c20f738269c2237dc971ea77bd3573095c3f4ddef283df733468b4ab2a010059ce2c97453c0ed57c0136c4ad9741d972204048bc9826643443f43b39fd989eb04c60703f339cc0763f91c7796f0c7ca4ccdeba0a4b08c6116762b2c2653a5709cb0ac9436930aaae707364e19103c3bbeea8c743118ac33d2b17cca8f32e8034929aa67f992ea378ee35ec5954e6f52d5990dc817dae2c00c6606b3c31e9904a574ba06f29851c3f48f46c2dd5dc37211cc877cb9b686198c9cc94f9f0030399c3b3b398735f6693c7f86f1186dc457b435ded81516daa8eaf7a7d75179d3732ad0e559c0964f8129bf02967e07efd2df0994837b97bfe1eeff4879ee6f78b37d4bf8a3f41bf72ce0f65c05e59ea2296553b22850ff07924a29d5573aa458c1148a6884270683cdaea758a16ab3eb5cdcc54575626149ad702a28dd6742a1508f7279965f71790e64e93a96efdea563f915708a8e53e426f5a8c751fdca7b204eea5134deb9587b59be73d1ec6b61cdfdd0c6c585e54790b3698f1ca6f0ef48f995dfc102a6ecb37c8ee802a66cf8d63ef72d9f23729ad5b4fc113d71042685769d5ab6f565983395a37b97bfe9de25fc51247f4bf84315a6eca7fc0af72ce18f5498c2618e945ff9940d6f527e2575537ad4971ef5dc8f1e057aff81fa554e53a4a0dc26f194735d2ba14b2922cac96fdf8df4d3095362fae992f9f4b87aa468fa51ce4a8ff2270e2cfd06a27c8ec8bd8e1c91e3bec4a1fca95442316ddcdcb8e91acd572a07f7dbdf6cdb8fc21f27bf6dff2365fac669ed72b9be194e97ee714d772f55b7e7e6ddc175c3e9922fd365fffe0d8bb8e12dee29e7b3f58fee29bddc37d44bd5d0867b1d52e47e878bc72567f0a64f8ac91999939e949a6d4ca18ecee8bb22a60d7f715ffa1bee4b29dcdbf0667b94f0c789f69af423efbb227c45b9ce1b9174e7f2e971f55c271d51b5898292e252a77075fb5a4f5feb49ad2ab55467ad951249c1144d6f6b7dee04ec7e03b912caa77c0ffc2412e95148298f02a77802a2fc06a6bcf7c02fc55a6ba6294a67adb5d65a6bad74d65a6badf5fb6a8ffb2398f5bdd64fa995877ca9d3b3fe2e450ccaed8152fc407902a5697a36199cbb447a1338450cce6de9055336282829cf4daf04a6d85a6badb5d65a6bd539d7bd196cafa33d102da2502ba4af8e5a462913894545d334d5a6d2364db5a9b4edf42dd3d39123aa5e478ea80a7fe488aa27fd8d05b09001ed20aac21f16c04206e412472d2c9f7ae9a5340db5c27195e3344eabb5d6ca6ddc73da08b5b26ddbf69b16d2785d7996d7417d8ad36460b495f6daa7696d0373a773c6816efc38c81c66e7b62ddcd19235178802898664b19530c57d0b2a4ce568f9954f71e18e9670e5558ffad40ed5a35e857a1aba8138484e0274e340f716e2b0002d7416f0e453a0fe1530e55120caab9474505e9348a3ffc4142845150fe5e4f529d421fdc83422e9139494d20e4f45c9d93c45094a63323034ef2edb99cdd60394525a85f650e606d0a61a0998914f79ca3d4ff5dc7b214e911bd57361aae2b878e6404df58ffea1adb92359f4b3a63b2c32db26a5faa79c8a09e53ba508e92934775a28c7cdae458aeee9e84096e7c0d4cf14ac7cf729403df70f4e919b5e0e29ea7caff252e4e6cc0766666b449143323befe89f6c830c02646bbe40d3f628b3b393bfe6af61cd7d1ab73d5238f5f655a00ba8b5d0a7ff5ec7f42a3f7d9e1424676436cd3b72266fdaf283a6fa675324e48cf6f4b51064eb03595062535964440b6d902fbe47399483e555cf2365e3e202b6a84022f47cb976f28fd365c351af867bc9f23aa4c812e214c18fb3f25d2ac4413d97cab1f2dddfd4cf43014f34123d3e3f40b0986cee68a09f9d1f994a0e761417fd85963ad024a03d7e1029c863f2c77c4542f285963fb7cb8ecb0e76d971d9d9d3e5886c7b6a06baef7c19a4399264376ea0493d18bc20a844874a5812440a725250386a5290cb8f7d42a63a40b488f240b2d8c31c432ce2aff38211511e8816bb979b7692b64c2699bf0611a09efedc19a218a82188884de641593473b373d3a3bcaaeb506cba0ee5bb3775e18f1c11e555e0143de5cfbd87a91c2a2aa7e7421c963fddb0fce97152d34b69cf23f52acf83253b38b56f3ba7720e6bb8c1c7d4a13ea050ece043738d1961704080ca987f5ffee4cb5725b59fca2815902ff42b688305268b7e27a701c6d11ee53cc028a7b071c84306d5c7ff38acb9f8f108e6b69fcaa18dc984d243871475561ef5292dcc917a95df910a557e074b78caa91caa4ffd8dea53e18f22daff60795578a3fa96f047ea55a1a4ac5c7de52c18cc7b05d48f02499f026efffd09f4de04768f0272fa47af437aef2dc87d498a28508a1f28c5295a50ee94ed4fb41465006ba02cfa9d2c0279c8c0f8ca94839d44034eca18356cd31e3d72d0db3f07d9953ad992a12d1a47983db4f5bdf6a7a7de93266ce4d3e3abd9d364d3b7e97ef6802adffda9880e29eaa4fcc97fa014635c28da320a5bc6be10c24e790d3cf90f9ca20a28450f2481349425b3bd0ef9a3c8cdc96f9ff20571a3711b1dd39f5e7a299b94d721c59450d2935053d609fc1c3a6e30e02cfa1c0d2734cb12dada10697f0fa5f46e689ca3e18496beb2817cb9c1ac51844f22ce1aed1ace1ef9423f06245afaca71b8dfe3be7d1a4ce8fab504b9047a1b91f2dbc9bf5e4c59d24a514089a66c524e9e7a291cda6cbfe304d43aa4a8a3a3f336291365c64a3a3aa3ef5ec77bee39ad859e40b347ce0cc9199914193699c2f9f241593b1ad4427a9cf4abcda31e2dd135ac1b0df59efe28b36f2465b5fca0f1ce4f1abdf7dd731f2e97b8a557e9e6a8bbd399e4fb72961fb8e5bb7f5fce4e29adeeeeeeeeaa21a49452924c4a241ee2a48c39b3a64f5cae775b03b5b4d67aadad54bec8d98b3b17e81ee44767644c0ce60fdb698843c670c0ac563432ee2e04df0f35f7dc35c40178e2ee003c912fbea3e972b9e48cdc3397d37dda1d298a3f6d6863ec4add08df53777bca647bce30bcf5396bfae83d9e84fbe6ef403c6c1ecbc2b5dd611c0bd876ceab17dc612e972b88e822e8d9de64be26103e87f8106df72eecd844e24a669090ed4e83cde2adecacf958aca00363bb11866c610925409cc0828f384a2700418c2e1021c80b3f4944395dab9810465880831514ed20322164051178828a7c208e1348023e5378c28e0f7c7082083e8916b00420f05c81052f82f083e8527081053128ea096276856ddb64eced65cfde7edb5e4be189bdfdc7b3b7ff2908eded37af665bd9806177af79359db31481351cec86afe6b6adb6d8423067c998b3e496ad8260663243ce925b88c859728f3236ebc1d65e7becd568ad9c05104062feebd5648d0514d8b56f85010b216aac8c7198949db71c760515326cfa33aba4a0267199d7c35129e39133326c9ab39cb19b3e6b6b529e48941cc17b2fbe175f29b3b99744d4843c6576bd4688a0041d2e84c098428e114590c113848a74823c21162961c9685e81c4a6bf9291f04d945de915378802149bfea7a74e8c09a6a9d284a24dffa4e3892ba0d8c1074ff0200a1e3e54c081155238c1051e44e7c2126c8022674cd3094b70b0e997b2a6bfa294de504a7d079bbe0ca594524a698a5743eba378357586922910209d0a6f8257818520226c7f1db464fb137ea0b0c3f6879131281861fbd358b1b3fd715891c511d9366374ad02b6e993667041064963017b9461bb1362b0dafed90947d8fe1f153c6cffa76208dbabd8c1656678a1a48a1bdb677ca1e4082cdbbf4b62885b6b13c0d02b29cb2fe3312e0529aaf8c2c3415bdff7ed29abe8b57ef66aeadcc1a6f43115b126e86c5fa248cb9880c4a65f7386fc20041766510ce102081d4174a1029d2540611603d1298e2fb69faea03f18993d614c80c10e377d3afd1b80c363eacba11ecc014c2144b30ef4143b98792e6ab5b5d65aedcf5a6bb53464dc6ed70a5e1ce8b637c4815a9bb3753c055aa70cb43d91beaaff4d6aad7532a10724e108b36b1d6ff8f6dc1687694389c39c16d75a6fc5f8a98871a5a0ddd1459ccb4073bee59472ca9f36b42ddf9ee8e80189afe4963ffffef56aae07819c2df3bc3f9fa31ef52c9ed3ba17a66ea8989f8a39fca18398ed890e9d20be92bbe4315fd853beccb7a0dd79bb846cc8963159903d723c7b3e8d2d6349665ccf9ed927d7ede5cbbafd3f199a652ce5203d934fa3932d0fb2912d0ba344b2cf0ca7cf8faf263dd1d1431833ffd0300615e8510363ecfa63f689c0efb0ab0e8b103a653f7747d01f22d9c7085ac6d75230a57d106de523246bfe0f721134137bbe077990077950f671ad37d9ec41cf16e4317223f298bae76f61788c8c4951b4e7cb711bda53c61e37d956b427e7dab31347eeb527f509f2b30304044c486c081911414346af6248c72e8199ecf9d3886d849c2132a711194a64d8e3f41162329b6c618f338c3d9becd175f6f77d2b8d482bca3eb23d9f89c760224c8489301126c2449828f34c9fe9337da68f8f5d4206874cccfb4efa6abc3ffb0259988fd5f118fb9af6655f2f9bf5675ff6a70a39e32f85b611291bb96fdc258e421e4468cab281b2a4d08ed17761caa67bef4761ca897c353f47f4a0fd23c31edaca3f9435df674ffd659f0ff21eda92a2cf0f506ccfec4359f3c1c83eee44c8194ab5948f1f423be36beb1072267f081ce61ea6a64bc72544ce6860086e90427b2372b9f616a6e62b8814da199442fb05c40e103b4b146ddfe119e2417a8ac83e16967d2c6c3a0d753bd713002470944a3a539eb1ad31239b72630e625ddba78cb13f5fda4de3ab496bf0180b1ea1fdb5165a86ed9a6e40b65692e55426f9b90318db7516b657610131c8d64b967c9964bb5cc276036cb741674fd890c374798c5d3261517841ceba9e9e1544c1648f53c7561984012fb66b41af562bf9a39ef1f018f935726bfb02b6fffd13394383dc3d4ee4cbfcf9f406ffc811e74dc6f139e2f41d2d5fe627d11994a91bed734edde4c738cc81d23087d79ceadb8be348d2e98b06a9d6c757d212394942e467cbd7485ecd11ed2149d774d23d6f7a769472c0ddfebe3d1c29b07fbc1a24b6dc60db7f050bbdfdf84a8e1b10d7755ded3aeaddece46934e6a41ca072b23d1c3bb0e7d3f88a060d794bf9e248642ec3d6de90912f32cf9cb3b423396b482c6560cecff61c2470d061e20052ecd1e0cf91bc9aec495f691bbe9bad1bdd369f9b548d4613ec198e62f245be2645ce5a4b4d59f275f460c4575224f182caa9aba44900c29e3032ea85adb3deb0e70fe91b4a7bde50da59d04b44c136e06d750bd2c4a0025db3beb536d1be85ffb44593e9a76c198b62073bf4811e651430d8f41b20b78ce1a067d3eced5fce131d3d0c994f5b34305ac31a546a5d6aaddfab7e07ea55429c9647bd0a857a9cff2f4cbd14f17c14f8d55abfd65b6b5551f98f964a2da80a7efde9daa9b5ea5a6bad456e1d89ad36e58f7a297e60cafffbabaa6febdf5aabcb7fbfc3e53fd5e3fcbbfc0ed5a3429c96573d0575941e54790f2cfdf46a46af320a7fe488a507c129d2946bd48f7ec7f75e88d3f2df8f5afe7b9c7f947d1e2e8f7a1eaaff5450b5525a6badb5d65a2badb5d64a714ab3f569adb5d2cff47358831f4ce547bdcbb780297b431b95979bc629d82365c31ca877f91d2e8ffa1daafffec1540e6f50ef127eaffa163075f3bdea3f8a2fbeb6d65a6badb5faa85596fd6409f3fb3c65653b954e5466909df97a2d99bd2eea378ee3384c009a02700aa85eeb0c7b8aea74d7ccc78fa21a680bc70c47ecc2e0ea03cf0ab8f982c1eb851a41cefe686bbe5e2890fb4fce8c367db92315ae84a9fb2b2fc51d92b270eaaefc0eee57429c2237df6f8fb3637bd4a3429c2b451aa7715bedce0f102cf69ab3949ceba4d8cca55b7a205afcf7e1f2d2a5f2aadfd1f22e218ecbb7bcaaa5e571c2570953538a5b0b98294bd3968f700bb10cfa0f7f94d93276aa20f82e2d1559e483b600e0a57ee55f3cd307e8870f5f15f9bd362d9ff21298f216d00765e1548e962ffd8e962f85384570c26ff91daa5709715c5ef51c373b00a47284aff23b5cbef43523f0df03c19fe1ab3c0f972f3d00c0294a2f75ff51ab95006c1a6e8a6b51ad3e5c4510299a3b73488fcf848181c1dfb72ff8f58226902faf1278822f9a3cf828c2618da76e68f361ea8261eabecb971e270c53f7ff5bc007431c973095e3bfe55337bcf930753fa5a97ccbbbbcea53372eaf7a973075c3dba326bf8dcba7b430b543e53fc429f22abfa3f4ffaf1a41ceae2c8f7a9dd47f8fabaf5a5e85f229f0e44d291ecaebd73979d268e4fdc956506e53277a23923e4149f901f2f1e5688e52d4a37eda5f6ed7c196311a20b1e71ef1cb673064bb3f7e01b1fd7110c7af30f6d8cd399f86b9e7a7fffbf15462a0a578958cf9458030f47895dca2eddfd244cf1f3150d0f6e77628cb1f465740b425c520590cc35e94e5318ca12790af469b861828069afbaec60d34f5f15507ea1631625789c7b0ec198e8cd039b9c6107aa43f9327bf64ece992029d72007efa384cbdaf64e0898130106de51706c240db9d98c1b6e1a899d0c22c08a43e33965f5dd094556397fe28d9637559253e63f4976421cde46b62c3c82fd7f69f47642bfb20b1679e5c43881ee509660f3e509053347f982b1a3933579bc278504dcb396f396fdab6e5bc79997b2a72d957f9955f2a3f5a8ee62c51e0601cd018b6c8123591a34d2d116d4912c9313065986cd26e09a2dd66018ceb721894a0e9cf97c7d0d7ac16e44c09b4259a366658022f64eb7bdd1eac17b6dfe02d3bab806cc948296c7f1ef246f7fe769601c9f22ee45eb3ad858c02baa7e10c2513dbc40b3d778ffaa22d5b4459fe6f89424bc41109a2b91fcec71ed1d257daf7c9dccbc35926d511cdb93c46c763b6f727a22deffd72210e33744fc5ee390e3fe7eea225a22d8ec81259a2971cb9d768da23f7e2421ce618291bef73441dc4951b37d05687a8035de47c4aeb75648417deefec8e058267080e3ab8f60db3afa625da0eca6d8938d8e6a331fcb57dc290482959a0af4d42bed89e3d79a4ac2d2cd07a3b0d21f4386113467518ee315938ec9b4cb60fd99061d690c2d09cc9144d50093d6313a6841e659091198be1087a94b14007240f2b64cff7bf80d4808cb1efb6883d7f9457d87e2d9671e7b66debb6addb9ed26ddbbca7a217fed041949435376bc4c27c351f839647beccbfa01de2ab1e5f25215f668cb2a65542d6981f646563cc76584705e740db278f2679bebf266740f9e2efbfcd56f899076d650ff26818ed2c5ffc5bbed013639ce5cc8886d19ee1ef407f40b6a41824b378c1155cb4a4174318c2822fb2a0841688c4f99d2763eccf7932bd19487b7a33b8dd377ce51e04f9e20f0a691a195f790c35d03694a161ca49e99c944e7aed90201e9e85382963dac9512929f5a474160f44291ada6cff4d6cfa367ee32bd7401f9488070c4de81166cf70d4ec903df1bdf8badfebdecd37b43b3cbe9a22702abb8e9390b3654a793aadd669b597fabc2670b47b82e0e630f589ab7fcea6937e9de52eb67b7eae48e81ace394b336b74e21d5c6dcc34acb4d9b1ad175b6cafb5d65e190190e539736d67ed5bebb386b5d65a6badb5d64ae9d65a6badb52f7d65adb556087afe57a4e7d357c9c05a6badbe8269ca501ab5d64aa9bbbb530ae32e43bec877ea3be78d6bddb5d6fa75594a29a59492548fe8f9f45333687bca1777afbe916c70ab6671d62cbe95894c05ab69160751af6683e83a4ff33c6f942fce9a0d025f7cb3662fce9a0dc256cdc6b41a55ab18638cb10d42be0ef4521494147a7110f7534a5e59f027e5aada505e1c84bd75b3b732b1514ac3295fb4d7a86bfbda1a84942c39e0aa755aedc5596a5e4d7eed729bc3d47891366e660d9fb9ff5519b44f4863991b1919a964fbe75b3f1972f861aeeb3a4ff33c6f94f368e4d5d41fe14d43c6d4d764e82880eeb70ee4365b6572984924afe63e495fbb7ad296ff656f86968ec81a1587d67ab3f669e3ca1ad656972bebfb56eea4932914971c7fb94eb9ba7fedcf19eac6f7afb53597bc1afb25d39cd15ec2e48a8c8d6fd6f00f80127a3ea791e0db39d4a75dece94940db359cf22567cfaf6d59830c6d79c0595e0159c3bfc9963317f878086192292336167491843de61eba288250173f4c7491031774c1d3c5cebd9aadd48b609b177ec05020d2887eb84f906521a127ec0859e103146242b21fa0d023147b554124047b59c12354041504c1484adc2010d1c4107c00848821421001848684881f362410e1c24886d0c21186a0429e81d6b68c0d1105ed086db78c0d1164032aa690f3f785d5636aa50df055cedf175297564a974cd0efcb19acb5ba0df9a3e740fbf69f207071d690ff5fa77c91eed65a1bd65aa753aa1a42cf7749c3484a2761319d565ba9bd9836c063ec9592b5e583531e99423e61cbdfefe197b5294e1bf3670d9f36acd033674d535fdb648dd2734f5bdb6b6cff4903739ad1b6b7529f764433e50ce3d943594da4fce48c6f3c7bf063cc7d7266b4f18fb4d0a41fe12931fe11387f280b3f09bfef1128c51ed006da43fc143f0c5ce81166c77a34f8d67c4e9f73dad03c1af0ab8ca0ef1ebf1b424e58a0c7d933aa6cfc786efcb3081df82f206de60464ccf6f8292063b8c71eee6cf7c999b9f1733054cb5de7759dd755eade8d9e8aa3f0870ee2e83b3c7b3c067760063590827627abc819faf8a50c1c8260c2b06b57bcce75f54332d050f88e158050a0f09d2b00d9ae20856dce5eb0e714a262cf3dfa102d614073487a00e0c2677b9ec74514b68785207cf7b97bceabe9b4e4328764731c13226c8e0b1f3677854d4f11bbc24e7e5c6f58e1898dffd62882125063db212aaa80832a7861c888d59c41912834ec08a55bb460d32da4a0c2135f85224fbc3a2cb4c5829eaf4b312ad460bbffcc2f4cf04e0b2053685185498b2672663efd12107dbb05d9b232488cbd85cea630f942df06d19665c2cab6d8d41ed9d406b9687b288b8e76dd82a696675ba24dbf6ea165ed428f96c866415a220bc6a677874daf0fa69d22707e32dfc6aea00e1e98c82349e0d9f26bd51246c6dc18fc474896c421f403c99236aa8d4de3155f30755fee6aff56fb16c6da2a6545382983932167e8cb9751005bfea35027da0ca9e597a13282e6eca496c7b8da737b5b05b0e773396b6d6386ac311f6f1bcaa013d49ebbe00dab7db7707cd9f5d63d3f77606a86a00d60e042c27ac071d2868d9f8ef7e5bd97864a4ceb534a6db5f6daf77d9fa360ad21e8f60dc71cb60d6fe8f6dc9cd76e941adb7bbb3cdadc5170bc95664ddbd257141ce58ff45de61f7cf3b54fc35ba55b198e36ac28e1ab912f69add47a0be34e4a4925b7034a299058b5f369b5b60289715ec438a21890189018e83bd3ec230389612038d8be2123f3b4d3b6ca00e32b7f986a6f119ac66a65af6542cbf8bfb8b27c38d3929e95d33a19434393b386737dbf5e20518e88900dac101302c266416f5bc6845690c5183fc88ca0495bc6b220e2aad07acb5816b32c8230deaead94a601382050fa228b1fd9edc1a7750622881591ccf01429704115a300ba322dc44036b34e9881679d3b9d048924492431a2bf2c3bc210538a22a420a26332222ec8b21d198fb3e41eb3cec2a7f445f705198d923dad9689a04fb68c09d1e24a01054c85eeb68c0991220ba1b92d63429200d32a5bc68410d9a384dd233a6f191352c41ef396448fb68c09112227d177cb98909dd21716e8e753f0737094479a98b97ff50982a004741b6182fadc96de04ef96decd0cdf1e7ffb3ba5d45271da39bd4e84a0ef73de49195a9e3b246750b6ff652267e8fb9b7efc2f17f26e61fb0d6317c997098664f9fb6b87f9851eef50133dcea09da0ebb8d6fab566f97287eed069e71aa10b337e66f84839fe4aa8292a3ce9f0f482998ea40a483669cfe7eca9d24b5f940b710d6dc895c02972267056a10b5604899309168488899c8b410517ed4ce054c2164d008927d0a14842e42c756ba7acd6ea919d41d8e20b44e209b42e1842d45a6819a79e138cb64c31ca7a4d1b73fb9f829c766e38fe787afdec0bd2b2eda6536cfbfc3a448f58e5837cf107c7d0299f4279b94fded4c48cc1e126d8b4d13d044c4e6c294362fb978e9036e692592252fa29f9c8993cafe001c9b5fd4732dac253f41f11d156e97df4f408440173378a9d5ea3980e47b1d3e9fd4750c8d629c43e94e5ef6902c856e9fd614631da2a85a62c4896bf45c0f670cc76143381b1dd74321199c21b64d19e8f7d3c73dc0340104cfc38e580fbf669b86f431f80f60db268e3709c5f82b9ede790f60571a0638cb1bbd00423eda99520607262fb9b9a982bff207b2c2151fa291129f92c99364a251ecc253e7b24d5aa5732caf2d12868fbeb70b1148e62a7516c8e64b3f4b3542a85a797a989a9c844743211956e89e7749af183e2a61f70b7d1156d75a1cbe639afbdd32567dcdd082e062be871ba968cfec3397b0ce6603735e2f522174e216739bd94524c2995d44a8c25c6aa2ee81cbc1a1fbef229a482140f7a9c2e57c1f6af31e30d32fc5d057246faa0ad295be5b09a487c253487e6cc5763ce13e3c933c4e51abd600c599a25a7d5deac6d1ba7b5d6baeb4e929c24399d462492d627282929a592c9743aa95c151515aba2728292b279d5badc75a85c773286a6a19c25596f97e29546273f9d462592499f7c1ffd3eff4c1fea745251f93e146a6525956a695151f93e146a652595626151a1b840bbb8fcffb7c2524299564e2996930a4be988140b0bcb8a2ad5c2e2f22d1d8baa737917176f06fa5ecd7b351e565f81326233e88c19337cc68c39230c012043c68c190108800004b05ab15830c010760000402763c69c2143860c6f06ba3329d7cc902f3460701804b062c110038d9701ac565f3a6286ac140b8ba56a61b1582e2b2c168ac56255ea1386d10c141c4a6d8df4ac31da483a3d6d74b386bfed3adb754ac0d9b64b6cd09e33b44e98d3f78910b445c2ed8faf7cdab047c81afe9607ddbdd7fac81731f468617bb43e3688b266942586b461dfdf8e21673a3b4459d3860dad13640d30648c9d42aefcddca8abad05661ad4cbef89ba6d0e3edb132d9ce7f85b0fd47026c1adbafcfed61e95e763fa66e4f079620effbdcec346db9c7dc17fdebc87b1ad7dec2e8f62a981ef5eeb93d3dea8fdebb1321e819da227c75ef0874f1f6e8db737bacd6dd8854bdd088d40d15f5535187d7f20ce929828afaa7f351179a74f7151a56e8e9d1909dd0ef56b876d6b9aaf15de528a5b55a2954613b11c9ce62181aed0c02ef0dc92cb63321ff289bb09db44719b3827a35f869ad565aa723aabdbfcc97554ee851c66812b0188e0acead85d6b19cb5febeffd54afa4c9f9cb5febfef2b1fda7271f3597df76738fefe9498fa74b432c63ea535d0d6f4e1344a22f21af2c5321cff7b4c1f9f3b7d2ea59752958f1e57abe7a69c958eee113fd8fed99ffa631ff9e2ffaac13d86032fc2d8343058d6b44c29f6f1919176b83fd7a7c94ad5053de58bffa8adb53130d1a3157ad19685c5b67f157a31ec901da2adfbba2f3b6487ae1dda7e84883b6dadb3d65aeb7d2aded5fdc14dc486866c68a26d18723dafd0748f7ae60328b335a2b45ca1ed1e75cb15daf7a85baed0738fbae50a7df7a83dcffbc0d090a6c286a1d8880a5a8a190698077a94b10f00a1b56c397ac3197a80f015cc0689a07fefd0268e6c4abd18b0d038e4cc071c205be3dc21d685edae85294647e871e503c46ab5e2baaedb9e8adbb60dd930e28293d265a0c73cdaa325b46fe9e55a71adf8de6a6b55811448454d78482be86aadc5b8e6f70a6818f018f93c483d72a6b5fd6ba02d191919f288218996330c90b1b1001928a64b9f7484ffa3f4a31fa5b5d6dc93840b09e7da8a64646464680b86981eedcc579a909cd9420b29862da2ee2e2343c346b43561329bcd68cbc26033d86c666116369bcd5c9a132929b5d65a6badf5556ba5aa257aacafd9d65ae8fa923377fb571f6a1094fb33dc10cb975a5f5bd0566c7f0985ed53c80e9d08d3a69133f7fd6d3ca6c763e8f64712f07a51fb51ca1b2f185123a2041bc1bd06beb2a8fd06ea009111b57f65911bb2a5b8559eea0a69517d515ab4fd9d525a5faf949f0d082793737e152ee8fae367e9517a29c5b022b4fcb4f8905e4d9cf4ca1c09e322122ea22d920f92e5ff029710920d482ff9e2610f1a7352cea7d7c8f61bdbfe14467a4d1ba3ed4f0ae231323943dab194e443a3057a24bd48af70d43a7c799b9b1e09e62bdf314978099d710c43f428613953da8203bded3133d95e222a0d9976ee2bc76f7be5c60df415da238b9c655c44598e5d7b2415e55a6f80033ddddb5149a3306ea049ae5111e9b5fd61b0821e15ed69ffefc518678c339e734e0fbff46c34d28bf46af9d147acccc6b44c83e48b16d21cd20fa29452db8d78f0a2adfc5e05d992a2644d4aef1c624bf9324e212ec3f6247c85648f94c9f6a74342dac22c10bda1754aa995b914283b28afedcf35795b6df8b48679e85f60adf52167e2313f726601dbabfdd18b647c4167a16b385f322b43cec31ee7101b1fa22c7f1a2f3d830fb90ff910ca8e0dc29454aa70b59b437c95556648d3c4d08fc6b7e10611b802bcf7e781811ac09ff3e7cfe71952840f11fb4359f3f10e3839d242e3cf2f836710070d207a9441068fd17a5a7badbdd6da2a23638970d72c4acd30daf4a5b74c5ffa31eb2c5fdc1466afdb9cc6b94ce90c66c45bbed0a31a4568ee4bbfc45ba526dc9842dab8e1757206096fd19478308f20f2337d82d01de4d479bdd3e97295c01a6633ee5146dcf49149967fd6e1283482914dc339e2384e739ce6ae0ec771a5a762c903ad901df29565e2ac69a5206bcc9f9fc59e3ffa06da997ca1c1c01299cc85e22321589805fa9fa6247a4e3a27f5e6d3199731861effdfc2681338528c6b72225b2c2d6746db577be2bf23223923a7d8beba02ffb8d272c66b758942a7ec514740faaa879c75b8f654d4c21f3a881a4f5dad48442a3f2a3eabd58a46ce38921ce87116a047d36ff942632c601e33e2d70e104e22e72cc606c21863fc4232aa4184a63f33b6696bf2bc785e2ffce25129b94194e59fb3fef00dca413ce62567e8f6abe4249333328aed9f81fc738615e558916c3b0e6938aef6dc415f25bef2f77098caa17ff437fa478f7f14fe2882c31bd27be10f9427fd8f93d79f23e6d7b495b29f62df4b097f70f851c013b0c60c3469148e9fa62c8a8b80811ab09383f0d06cd9ca3ec0605d87f1cc3c3a5ccc3b94e54908a550a814ed17453d0c15918c000000017315002020100c074442c1705050532dcc0314800d6e8c487854409687a324476214a59031c6204208310046446668481b00c44d6a21faa695bdd88540057e08bf563a00b69fe8a20c14bdbddc9ceeb3589dd79dfafba5c41e973c51df2dc4d53fc224df1bf07d7926923e54285d6c0547c6dbfd9a4542903c06f5f05d5047e13115a92a6c8455210014e30e80a09afbf7782daf200069ee9cec4fcb5d0181476a8e3ebf461da85e2033609708a2002c1b829bed5a5ba9c9d423f05891d6aa3c98c6f5a7e0eb2d9013c42ac890c5c10845cfc763ad9e817a5a446996faeb74ea1a18ed05072f92d369a43ef9dec73a3c06140b81a766b3758ea96722ecad727cea15fcb3a5c1b5ec7d88dc1f3dd10b4fc2da917cea29438527ebdaf9e451335fb5d5d80c3d79709459a4504c5eb8b03d27db6f3d40224122f435a248477324ef8f6b78da38f24a805529a87b5288ebd4bbef21c319b85349bf2602149bf0e3ae9d604341daf6e7f595c0ba2fe7763a6f4868109c4e1dc0d9de0b4eb814a73ebf89744117778e04d69bfd4d2d43819649c9aa9a3ecc9af748688642f6af1f5ed98d494210fefcf4ec07401c964d8c3970c830ff68af33625089a6d96825f084a32e0c0e0d41a4a3ae4c00b045b6bbdaf8f623f62d2c3c1a588545778d44b8058130064bdb28ec1e005220a2909b247ae8f0f838a6938e9178ff39925c75d231a78c2d4f65a7930619b24e007e5e629ab1e081fef1710ee230d9e7e0d3a171ae5a0b1f169785661454d536076dea39344e25ba51dace31c85116751b4a661d0e530743054858080825b9980494704eb9524ea7006c3ce1fc33741793202bb912b1055a6308ca5c0d67c0f942bd320b29f6b9b92bdbc2cbfc9d7d6219094e8e3ea5fc05aedf9eccc55808c35431710f1a93662803741e63a3ef343160a478dfc8f5a2c0af735868d4027b2cf1386dc56b890dade39c703dddd947d599262afe5e690cf82cf062e6a6ab9315c373011ca5d674194764a38e5759a7e1c7102471b02400440396c2a744fe608cf0a247766392b5a65a42326b6a7083d038e755b98433bd2bb7bf861dcbb0ea3afa0eb0e59f0824b2addb0b69b0c2dcb817478358d5817bd4bdb8483a40bf81233dcce288baaf4d15cd22959daec350055a47d6ecd5b8ef00ce2ddfac779e52af08a2d92e38c3793e7a9536068e956eccbc458e15a78719bb61d7f46215d508ea9e68953b46a8f158c21e660535ce0521151a74ab37f484184f29ca52c649aa3ee963bbebd14dc50b77a4c53a616dd029565090292d7afa6966cd41527ec8d8d3ddf37c7a7b777a83234095da3f2527f5b2258051c8af149a20fa69ef461ece0290440e6f761d517c0dad75e7af894483ba9df88e79b3d02e6e64930a3f3cdcd2a43a60fd4310bd786e08440c33cc732a880c5279f2a3d871b5e95007d4654b9c8ee43ecbe5327417bd727b168aa245e726761cac32a54ac13b17a126c8025edc7cd12a16d7e42851fc88633355052b64c44ee2a679aeffa9fef71d003217d74e29e556b1e848a12fd19dbce972158bb4a940f7cc7761a3bf8462736e66d0c83b55c1e890531c490a199c88837908b5a4f3e8cdd5da35e79b4b8aab57e40bba326dd606b5f36c2019a2e94315917b3fed8c577ac756ca1524460b73cf35e4da328b9edfdda202ab865f7e2f34f23d0845023df374a176fdc70c2b78415f80e4645861bc246764c627620a62608e11fbf281bcf6613dfe8a37c177a8c2ef0fae3549e74df93c19123e400905045a42bf484f7307407d98fee984dc3a27018d9456a0bf007ae765a705722ca879097784c655fea33418d6bb832380f217e0a773190ca56f7175d266f21baf965549d72a74cdb839c567293d7c6f816e3c8debf8d131c16f73478a898649be81f2647fde29bcce20eff1c7c4cadd59bc8bafcfc735397ceef392e389a4e415a0ef01d8974caeb6c9d192c4df38da6dfa344ff139b94766bed4a0e7cf31e729ebab41f43c427e2c551d2fc9d8bea8bb732a554d4d8ab89212316604f9245c7b63417383f51507c064160ce9e2eda7c6902f49bac72472f1c050820c5f1ed0b72961d05d6d6a4332c4cd194addd3777d437ec549dcddac1a8056264e595165a0d4e406a5c5773540e1d1cdb73bb66e98f8de843d51bf19a47c29fcdd650deaa2040d6190edb7f407746c3ead87a0bcaf3a49bd912ef72d332b87b190c9ee681cd24ce4e31da8428e790970c96430a682408f5716addea498045335b7ac26f7bea71f1c74297e007aafa4c8567012bcfdc99d430bc6ebfebfb11993facdc71757345296ced0f844f07d482f81a70c5e99386554ff4db606e39435fa25de2fae3370303e3848d73173da2b129119ae8e2ddd580911051a515409d1360057253e102569bd74c0652293e4127438a57ab9aa45c4fc83833ad5895926495bb92feae35bcd325600a9ca3a4cdf9f263f4e59cafe1a7c1db0f19526c48c4edbfb3e308d3f8557c1456c51aa1b892bd7a124f82548b0ceac39575eacb2fbb1a36579f524e6c8518c8e0b8ab79536e955d9bfcd401cf72968668bb6f793b0c09d062f8aebee44a7f85959ecb4caa78153898cea4ef7f9d6b22e567f49f2986892549322e108233c09c50fa881eee00a415a2cb6e5216f3e514cce8e9d1aea9c9b9d46bd1070ddb2507b6bed66dd67a5871a89afea3846a6ba13cee60f71a3e224b4ee480adcc456a4602d65632be926b8d9348100b7a1cd980116bbfd63878e52d0aa0428fbd38c5e6665e2a8678e11092c05c519f32765f5873cab7c9fcbd6637f64407b2d8675e8b9e8bb66ae2fb1eacc95bbe44da28a5f9d42b398a6624693a7a8bcf361901a66480379493b80da7d510d05950a0c6a3ce3bbe309eacccb77016480e9a16169e0fbd48194931e0c0f51a9d16531983e1e1e94c04dc813e2a80bd991b981117197f80b51c60b4e0910f16fe4bd563796408fd030b1f428f3effd26f44861bdcad33d2be9c8c8592c1a351500f018f46cd58a49292957714ccacc7a9f0bb951cb960ff95c1712da13a54ca7c438c05f62add5cfba78190b5b923a5ff645cad5826165b9ef1151e1187a2c2be1832a99955fcc4fcef179adbd729c288fdba40035d08e53644e017b3badb4691591b22b79ce87c9bfa609eade0c68cfd7ed7cc8a16ff48ea0a694d38a963e604a1f9df54f9ac4710573bc4131a56c72cd0a3e0057deb03731e6968478fd76bbeaf9c0b177d2901a45406e651c4251c4a964d3bf6e4506db3aeb0c581b103209c079bee08b352e800c247701db0708714b13e4d7269a67c94ba82001e3aa2ab3c39f529a69276d6d0c37edaf8841487246a23f875e4e74123d804fb6a37a9804a626abe693168b6bf6c247f0ad1ea614754695fb6ef1de240db16b61da29d5db4e82c76e6f5f88f07aa1d2d6bd4c0f1d2df31696f5b8be35526c58608c442c6b08cf12fcfdfc304a8fb665ac049b07da4ea8e3b231dfb6c0be719acf0539bfd3032f5b45d866a8f978fa356262aa9efa10517b3ad26f1e86d4287fbd14193038a2330f141326d412315520aa9f3bc229f088eb990c22d09d6762c56fd13ee8e033e4e1db7c3b3845ab7c64456dbe04bad27a520da5400124ae04fb99009edf79fe8f236c4f8f64ffa0c67c3fc562276c5e24d4fc8cebc5e4b11292ab8facb50dafc0f9a3dfdde6447c14b8775b1f63ed46836f769a172ef6b6eee066d328f68ffa78d802662c4870928e1d87643c721d29a4fcc9d27e76ea5bf930482c09d577b8caa485b83252f7e22efea150af8a1986849b6f406d2611e092e11663a03bf1502772457b39607865d3c8414b3614e82d4a03184d83051a4db4f6f0b977c5e7343ba79c08da83a86c259b549e2205a67c059dca4d738bc82766ab9c8d04a659c8b2fed25333ee0e3d61d2382379df7536c2e72ee9a79803f86dc559469259cb8833790efae2515ba3cae401f2da91e4e9911ef51929dc80b15d4e3c703b3e9ffb0b50934404bdb31fad37f87f1d0bb2dc7f7946c214b0c499c3fc4773a1f9174a5632e5fc4734ce83a809735f044732e22bfca99123fcc2a8dfbcfd6d6f5f32103ce930a50c100de58e937d217503e81f6b5972702d53861d1ec1416f9fd83abd11a228e461025615e1da622eb2c198065b6ce5688f28c48acc25b65d1999fda983a860b308c00cc42893fa8d3ae9adbbd132d5efbe74c52ea6caedf45a061422900cca04fca24919368469c537839008ba95375f3e2de2cb4617cb6728f9611cd6e13a65dcc4484972ba07642b271dfb047f296c983cccb22a3006a23c0f130461e3974071c60ddbbb75a8db078e10f282b406fd5a629a1fb423a5eb80b65a513ac6cbb60ba59dde161c3e12acf455955a29638ea7435806bf9dcdcb9073014c5ce425065882796d8fea7058cc70b1afd029cec0def041935d6eefa60df08897a16b297f18fc7324894f923b93fafe5a7019aed62d483823311230ecda0aef5ab46a2139fc138449644e788dfc0253b7d1ff313f2df92dc4b82ff4c08ff36d47379fd86044ddc05a82a0e6f11566bed764ac356252d15a5aff943e52fec0b8a98891f2b81a18195d109bd3664196e740497a0a508d9d9c59955eeb879bee712a4954d25c00bd04f98a25cfe8a0aee8bb4e1e849fa84d8d0361d644679c73945258f96cff91e4c44dca82deb1bf45a1d7eae7ca05b7ec1322263d36478a6b411d795fe0bafcd52b4fccdd137cd5809c71514d2bfea641244ab539f9773883a1cb10505bcf4371fb0089fc81f909f8f98f16007ccb17d419d4c26cbb44854b80932d0bdf1fecc25e5460906fe0372f8e2b66ab5e98d55b07ae183ec2d803425b7d8c62f11fd04e586937b1628ad25713d9b6b0cfee4f7eef155a48f7fde6ba8b906a4ef5e53c1c6a5aeb385700bc45a536fc5f8ae5d5eed6bd653e81f56161adffe442d779fc4b16d039d2880a01693afdcd161e6f06f7b2ce9f3e070b1a0bfd0a28f9e56d01a2f309811968c08dd19e609f9c139e10a306b677e85a55f952cb9bdf5031d12f64682887a83ac67585168c41960e506311e214868e7d3e2f198f7740e026a50a55cfb71b54e2904002684c2afeece5ff79f0b03a6fcc759773af082f33252dc7363aaf54bfd34d5a34c911e41c1c8f594f02fbee561f96f67aa23562fb8d1c99890712aa1a033c2b187c51f9342126b75ba0765793a0f07944f031240bfeef0e1e209f46ffbd9d70f70c33bcd70c2d106d57c0f4d2c097b460287bc37e4d27424052d477c4ff93bd06de3c9c36fe75aec3a88441c9f75741de6fc88a6af0a47a7f17a50521924ad9d2a93be853330a55770e53526b0432269607c1fad82af1d2c8468a7a3f231cf745129ccb269611296261b1f123a15296445733c3ca43a20601aa17a42a7fe4ec329e8dcdbdfbc03bb95ae092dd3493e651bd505f56721e90bfdbb96c9b14ebb248283dfd2a14e3b1db8596696134867e4661ac920c608e6ee6c0b8216c322bf00a1097e0a4eed035199804ec130c262e0d3d8853f5e851663856b9f9062eabcfb4f1db55af481a58dee90630aade4cf836e9e4d6a2b4f1b36de494e04688aab4ce079cdbcbe315c74546ce0163dcfe94cef1a655e86f0bcb6ab7c99bd412f42a60ba2975b5faa1094cbcea2badfd0978fcdc61b38e8439ed7a32f4f8e47d554231a2cd3fdea54d0a6cf1f2c1b00a0c03a53aa7117b6626f115def912eefd7ebe8fc91a0936e6f930c460aa403744f3ee6a1796ffc72484518da7943663853f43616721a22b77113a48f3862cd692bdb214ea5e9eb3eb49f99008f59db3ea705dadcc1c1f9113f1878fd9f46f6bd01cebd23b83972ffa43bdb285fa5ee75315d401f6eca1343d023f0fc888d745fc8258be1e7cf7bf053694ba7b6d5e23b5d182ea920c813a9d0a05c2407e545cde1014ac14509c88717af79f6b6ce3275b3a644b5031d721a2a6562f067bf11deaaf35c90330187ed48dc2d8de80833e1638ec706307d9975a9348e857247c1f8151eb36f1f8d65dc46d381619e80f3ff6ad253badccbb0bd335a60a499580b23d3634c1478a21015801cf1f0423a44523da2111ca6ce25d94bc08c1f3a8a829fb5212d54ad9412fac7083a9fe86049fd794bbf2a7ec57f968807a2188d9cdd3b23a003909dceed0c69dc741a8dccbfc246d9378593c82f7afe610a7a87218f5892db1d3ca1f3b459156731c3ea7cac2cf7158cffe378e3b47d1c595bffab89a2730df357049d290a714bd1cbf9c830735152c521c6fdee2ccfa5e80e896c81e235426e41e9ae527113f549e508d32b8efda4dd00b326e278c428ad0d9f2375d3d7700b3334ddd046ea196e6e881267cce702d2a94eb6a7f915328bce2adc81a2fa9085a3e98f06b09df7ae6f8d7495f4854d983740c6168f88978323aef124b4b91383ce7351037a3361b5f5438bd59e13022f3229a76a193f78efb3fe73e63895116df44153802fe186a261f05322e4ad3e9c741addd8f1973cc2304cb147229b41e25a39c438c790230280ce4351fdddde13415368681911afc3323bd75487b47f8b0e576237f5e3f42629d5c896663d402a5ccba8bc848f886b8b6eab58528992aa50ba8dc198c2ac502fcb4cd79590f931932e5dcd3be810ea01057d2212d6b77f2337790837827a450e3737e12c26783e87707414598acfd4a45b02e424c81f8c282082a7536976574664ebca556a1dff0bcc4226624a225fdbe018f49aec36265dd933bcea917f785d38176671ae844fa218a4d12edc158bf0a558c7e11002cbf19814c82786c2dde069ed52649adba15c2efa32d84a69ebb51f672d15c0fe2cb78f90c5e2efe7cf8f3472fbf629def3cda2f81d4fe1ce4c813f67818c028c2d7708d3bced0b04f311bb8ecf419d1b1cb8a611e4af172428358c22e79190c0b7db305c594cb94d8e2b5d731a6a42dfbddc764275641bb91a8cf5719987a0d0aa1ad86868a96b92c2f231bf1c17f2a379664fa43f6218e7ca4096ba74559fecc0f0ab721b728455ec2bc976e751a527f091004f9528769d449248e6e310504917d1e1c991685a4abc0cffc9f91aa53ef5e64f435cebe1194a730f5a0792012fece49225c2c897da9824e18f526a0d34e53c402714e49808bb0ab0febe0d4d665f44527a84f7d5c0c5972b488102a02080a7ed4735cc50d8edd1156320ba2163b8841df0ba79c33c0ad0aae5a3f0921fd136544eda24823e2f6d4f0edc0013f65c6e93234cc6408ddb91942e0cf332cb79bb3a024f1591a302865b82de7d96c070ab1189c5876138991b2595c706217b3ef495c7d5625b4dc88813d5d6d9b97dcdc4d61567299fb0bf5f5acefb8d4c095e5134368b7b32e7d966144d0a8e9ef133bae11f50c3f59fa936a90e4aa788b6944f74f9527f673c08815c586d7eb08424ea3a95ec1e4ab5cc1caf12c54df2856565420568009d3e36b2ea3d38374b6f22cc2fa859a20f1b08aaaf729008412b195d558f08fa6b3ac5e92f115fdac746e7365d08b3da9f9e6abce1f02b5fa10fe70a8243b33cd6653d96586d430e7a171ce1c8a2e6e080ffc1c09c4e4baf5405889ca78e125364ca968154f29228bd162b2ad2b701cd22ca987d362ee142900ff6c06cc53077f1a20ad4b7471cabf37a4fb9e8fbb726fc91a37c2cd063d7213f812d12b817d6055504f5c230127862c5951e91cb303192501c33fc973dc056dbee1581e107c8cee477cae73baf6b3a5ead10b7a6056b81e34580593c8fa6aca611f685942c433781e837210caed7a39eaf58c7984488ae146b163ab45c93531bb3bab3528aad3522b7d909b381c22c2dbd98a0f0e329791b38f0c532dd837829e88b232e825b7778e83702b4a3317f850568ac19ff2ac6e18e644e4b904836675a3054dfcc27f2ad56904832eccedc2288de8af10d8d111d6ff401b99016a34bd2b7c498c45e3777e0c1372c13772921134784899288a52d38b5a14dd89bffc346c5636bc21da37797e63a701cd5858439c4b6f2eba1722ac8becdc609da866494b5003a12527994ccd30526373eed42908dca2f27091ce50d71e57b15da1ce7f7c52784d292487ecc452d04d50ebf071c6f27cf458fb4f00888371be3e09d07361725d9a345ed02c5e62031dd0695b08e11e442882218a83e1e5c09ab13e6fd7750f312264a2c001ccf9f40991bb4c27399a182e0601fe15b6f39f01e8b050661703336bb9199e2288b0551f95009764d3f5481a634cf69200a0a0abf80699b567b98181b1af93927ce9de7729f5c9cc02ca60bec3cfef563985b7b150e07d611a20af24459b00acad5a7f8fecd0da01a0083c9eea06d2322d0fe350238856d1e711bf517f98c90f6f561faea8f155bc7881c7b9aab6ba96e2bac8157ac35135c634cb2ad4b7a4049c90e796a65f15dc2091a46d2d1f404e8a622d6fb92542ca48abc670c840a0584d2aa1ff0e7151f002539755c2c42c531b09b2eac82d1604764c682b5bfa7184a2b02ddec954580c8ff326264bf53cb7293187dabdc545fc4f0683500988d825a212b4a202d8435c2974fcaa47b28d2c4208a5e094e18722e961647d1725dd25b91cb83ea2fe823a2bc042e2b345d9b171e24f6a08cc5bac2674c04a54284c37063a782db543fbae3d8025400672cbd352f75b8df4e38b3ad38b2a7a24196e1699dcf896fd8519daf5abeffd2657808e34f577149891d3008ea8986abbaa6803b04efba7c391912cafd65b3366603c5e1308164784c0f2adc5898eb88cb1c90b04a07d755362101a90724082912212f645514ae574938bbdcc896d78fe829c8293d14fac4d3bca7632b626375842b431b14d7009f176ed4c0ddd305b0d62d6607cf39562db10ea3d4536cc276eb6dd18fb3eb0024a1bc36f619a84b3b18c26d0a0d8ca310196ab2d7369b297923ecad96e65cbb4cfce2d904386343138e959f95e634e8c566ed8d6d9d747bdfc707a2e009d3bb8df0a83e332ae1819fd8db20d9b41ef0ce00c0c4dd201e7d7a553d1ee73fae2841aaa59dc96d8a89772c06459415f4292efc7075fda0b2fff28803d43f40610997412ea2b1171b436cc9abea33c903a57ffc052074064ea6a74aca5399f64a88eb31144be2089ef1a294e978d4986e874929cda0f8006697acc83034e19cb7136d8432a34f19ab64534d3990f183f764d6f9c0aca3f121cde876a6b196e4584f143e7cacd14fddadbd173125a6f39013ad9daa5dbcb205a10371738e4a29099efb30fb514bf39ad3f89eb6d580929419e5f194c9c6839db30da9f1fb5b249b807b75bb5acaf03660cd71e1916158452dad6a0754ed57cd326b061f16179fb5076574ade8b3b470f752e3f06618170b363f3e5a13cf74378a70779a83b066a0f16c59a2e29fc218b88133ec69c2d949c5fc6ee1c5fbc4d6d29fbe8caf755f0a77f3e1e78e1be4990ec0efa3bc7dc71517b37021fa6ac86809dd1ba05e79fcdd1bccb1bd965c7d08a1be948b94e17f305db20bf2fbfe7101592a9652fd928f9a01e0d8c7c32befc2323757d1fa42ee320c1183880461561c55096f69e90b7c03439db621d8aef530193ea7e756c80feceaa7c1ba989097f021ed1e72a062e981f2d4c19e376b0bf9c5bed5b3c7a0be6f4ecb18390f9db0cf02cd1471e1c32d37e56d4554356d6400f33df18e5891f7f743977de3299f8c58473f822df026648146270ab7bd92fe5023e0851b5a32c12f7b68ea76a7876d68dac8ff33239c21b38ab4f1621b2f51ec19c9f38385617655a4ea9bf2bb1f1bb5b2c821f2a5e946715c0752faea09bda0b77c38d040bb16b0586a6851eefac5c5518cb9d2688d21a343cd0f5e557ba5b3ad8d27e68eef7b31d9c75647c53619391a93e2c595b9651d9bd2e5ed4c09afcffdf1086aca4c63c73142ab08c921d650379e479338d93632dba6d9d464a103a20ef8d023e26196ff0644b02b0dd3ea03aa505f87af1a0accf33a9b0cfd4db4814000ac38c971122a016ef5666b373ee75919d48fe5dc2415ea42fceab2b695f1651da0847b40baa0037c4cf715c336123022402ea28db064b0d6e165182de343035df7c844f62ca3a64fe9e5181160937329b0a3bfefc58511ecf70541f2d78053d13c77c8f9b06a9d1ce9dda69204414bf42a504e2d343028a14f3b03f7148c076109b55f78f0b2b811fbf13b8bca02388822b0c2303a73d1434eb8f26905e8da38f0a5922fde395f1506563b2c2d02e4fb3bbad76113a46bdd0d164dbc06a096b6c035026db4702327ba47de9104d6c18706216e6ce898abf75f42c32bddbd12271f091f17c377793cfa89a9056cf61374233e72913a67c2ac4e8e373e01c299ca450f8b4eebc871eb6962428e58280d7339348d4c9e1f6fd09b6cb6a0217f7bb3274a20bb855cc4cd874de91541b72d8828acb6460b9fd6c0c0c975756e88d47ae1bf3d82df99989c011c2a432a2d7cec01f9e248e620f98d2b28b895914d4f23de5da09a035e26209811677e1081f43865085a1e14e429569d83409745085925bee0808c032dc79da7a5b0d95560eae8745d1e9dd0fae732fdb1132cd004af2968154ec7841ce98b5be88cfbd0e3cd700ceb40795b9d0c19baf77cd7a331c9326ed77ada491aa57e8a63069c039603b7c0498aaa2b38e3758fdf6247e551e744903c97d095d4fa3d9545d467326861b63fb0a0729dca9fd6f88fc35f617919657cc12125920812a8a23707a8716613e60fdf59c46768e99afa9a6da58067f8469a3f8a1a6dfcaee2b14d0916bc219712903c75f4c6b965ba519d7168886582ccbf58835f75b1455f301aadcceff582e77ce8a2b82e90b38e4eab4e3d21691c5e032c59b3af0401627f5788b50e654959548109d48643de4f514b7c1e93ab44d1746fa4b5afb3e9c2ee41636f894aca81c94991b6f27445b41643ee6bb4ab9825f76783f21d45aa7314922126967f12ce2c9a829024b3a2c91aa3372e4fe0d8738cdc441ea77c1425fb0af658c0b8648388df481922fbd18df8308f0f2d89d2c1150e012926da783e5f37256e9090da64b71e909b395859f706d6b4a23ea97e28e3a8fb4d847823412c734391a1aaa217b3148226798ef1708f6ec9ca5f96431798f5db1c38cc0be4fe8add17f231fb37ca188bb62627285c8871bf199121a0374a6bbea912b0c3525a38ae72d1452e3bb3fd45efc13442cf220e43406f6e984a97636f567960137d288c1ee56476579842d6df4d00a02360a874bac5501cbb69d1e31cbc8d518c35c0eff8de2005d26afe64a81860d0812795a02a77504c04a2acbb34b65bc913746c8c75806bfc7339c893a3a1a5363bfa567e1ef621469ec044c2bcfd0137e49660c663263d2862cdc6f281dc3419d06c905c5b52dc130d6dcbc95f57029e5266f6dbcdf30db08fca995c504d4853ae43987d68571971841fe389c1fec94906420708d84449fb045f9a8e3d5a58fbf242395b2a319b2c928bacd990780a66f91060ada6e678fad2099dd65cb0a4e11f3bad420ee753ca0de6fc7556ec6acba30d40cc2ffa7c23d0b41d401fdde0515b8a1d1ae01d9bcc3544ae32785800df5f706bccb63a9040640d19660a70e4a3f27f41eebc727fddf1c2fcd5a3b5b957932c7f12f2d635d52b2f5d3f464067072edf6506807d580e42ebe23c276d00fd99147037282a5b4a064e1fa225b790cf5dd5e651a5b9f1a1ca66ebebff8f502da4f4883da1d6b004edfb0d04fc700a16678dec53b0751025280b77264de60f8404a4003c5a6c46abd5e7fdc992ec70014596769bbcc3f0e2017151d169951d506691472e9a00617940a9bfffd255a06f36401b54477cce40429d0cd75b0b2ea9429b4415e6aa6d89ef56626af902da3e9ebbbc9963d1f348543907df82ac307a698aeedab746cc292216a5741df8e283dade90b443a4371978105cbdf9d2292f6ad695083f9de6472bca4b07c300359579bc3adeb80370db3ba63060665258036aeab1996c2f9d1d43a56fd35bed1b97cb26cca48f150c69c9f26109de8654eeb84d7e91dfd58f983fa13a998135e150b19847b35545171a7f68db1351597868ecf4025a7a0aa5280b907e3cf07f07d01bc48123877e50c0fc4cfe847dce0735f0182e60ada9be06f46e9c6add7328734a5c51258914f2960a3c06a211815fed62e757389e76090cabc41fd17fd11709b05a9cc82ed53191bf21e428e02eb97b42ce2f1eee17e34ccc19a3d0175744ea41f6246b6d53bdec4aba52c8776c787b19a97e2304e8d939829c4ccb2b86fa8c9b30b9ca4517ddcfacfa11422adc975657764d1eae9f67c848b312a7da86242583fd1441fc8a48e7a95495a72e3de8f60b2c0108c1cfb40c6636328d21fa372ccdb53c730b93da1c6c56ea0fa586c77c07a5af50d03e192b6c9eb7b56dfad42e66065642a76e56eddfc2b4beecf964a1fc20897bd7d2815b7af682fcad08e5eaad68267d84b2b6efd9cc87c095a54220cb1acee42a920360a7a913ac5a59e8871699ee61cb1d1c815f1f312ac9840ffba2c3756c2b06150e54421c008ca5445fff8378495160644d82c85e00b704bde8f0b6aa11cfbc0ded259f2289684cb8a930818b2bc37ab0878f0574188c6526577161412705b6621dca599bebdf40cad6e3bc4956f3cde1f935ee5acdb5f80ff78666032e5846046200e7b66848617b9fe94c9c3bb7cb99b13d9df3a6dd33a1fc3f18b060f3964e4f64dab5e5b979810d851b15eb448e46c5b6088360316d3415bdc9d9a80687c8c53e0864c5e44c725d1162c52e2187cd0554d3b820b918c3642a548ccd22f1460f55afe791b1c4b2bd0615e56ed7d1eed834a36e1de8dadbd63e7d62110fd4f15d8f454ebc31b2bc1703e3f90bfda3af7f93f93ed71e9906a386838493669fa19271f665ea89883dc96835b9d4af59e67db7631c41b5ab55f55accedad0c97e1be92f2b3119e469d039dc98aa2b077ac1c3b910868c2448d48928b66e3a73078d9bf7cd407c4203627ac599c294eb2cdcdba6f29faa11e4406dde5932e69e323ba1fec3667fca7601336c41669be42ef1d04b32073c17ce4e379feb635d5e416a4f0f01cc934dc26f199506bdf5507d5f919eb6e6c9f1c4f679c9aeae7aff8c44862bdf9eab73637a54d8b474aa3db84692d29b3c596e73746e19752d53f4f90cfbc1458b5edbf0f1f398c22016692bf1263ea9f79f0e15ce7734d03468559ba798c391822a0cb0a77d09f440fa5056a7d0508fa170f4015afe32226cee9105c68e0f925bd27a444069118ce947b5a07aeb577f7ce3f0193fdb4a4390b9fdd098f285406018d9e011e60e065e460a37ed460e408ae8573847d8dce9e97d11c0a9375d1482cc1529fabb003bceb063697c07256c9432df4f638644e3136e80abbfdf42d8c6c96ed33f640a79438c18f17454f3f51fe908830287e9b90dec8137ed25d0b7ec098d4f6b33229455707a30a1191401ceed7d44edf7264f820b7c91e67adf6478c1b5ddba1cd27d88c17de1fd67ddf3ad086e60392927f6b9499413a59a6a4a95d0071e0beb6294fe7e7f7b3faf797e5961e2adc6aac24fa42855cdb4fb6cb6383469a1d9b5b0fb95fe1caf09dd6a3dbdede4c0143337bf5ec2c60ad8958dcf82544eeb5932ec238a01604047dbe649021e6dc7b34ced91e04300299a6ada995ec654cb3a933549eafd0f09cbf28f40db7118928c03e806845f32dcffcfd094032d44cd4e6b810dac905258594399c115d10ba401e8c179366b35e7db316f6625fbe7faec0fb15e7c653291e7c1a0afdb2fd62e6f1b70a35cece9307ab268cf00f46c473ea1cc94fb95f771a3fa75cf03ce2d56a90e1298feaea25bd7f41509887ebb449e6def95327d6db66beb3b74bc3e8606dfefcf6dd34f09dff7d8f3e72e52ee791bdf5e7cdff7cd804d5a70bd5f8b04f048067a9f05ac5688afb47f8890a3a16e3d79dffea05e6b0bb0c0cff1ae1fbffbb9906b839f77c1372848809d0d60ff0eb109cb04031b867ed81344399d72d071f47a585ce6752cf52400403af8713d2e8461029da043abf0779490eb545595c11008d25505e6a22140b31a3628deabd8c78f39fd0297152aa7e0a1609e28ba20f659c73b71e99d15a4a503be3f4e97e7e1681483861d811ce29158d67665299a2544e8b9e96191789afde0e38f6431c7c3f46d0487366cd24a7843c7f2a0a090412e4abe391e3b36a4770b66568a12624f0565adf24f085f26fb9f994822878a388c2cc5490f9d52aaaefd675bf314ff93b7e51fea85e2cdaccf0c531029d036722adb03d798aaf737615b75e1a9be9ca2c6327ffa403ff2992420d979ba79306a91a923dfd52a8c145da12c35204104c6e556451146fb93cd32ee9482b57b8d08b08dda237dfc064052a1844faf375890b60c09cfbb2e508c479c5c5cbede9519aa21156b167301d6836df56c4f8806ee80289b3daf6d8acf7d200bdde239a58b8cf256e9149783b7b8612db6b8b39d32234c2fd7d1fa84418627b6ae4cec8cfa70e183afb55b0f2be06543e3b7b1c5a41b0a560c11980e3ddb7acb7299e164c0b72b6f74a43ecdf7696c1bf816e6c1e1f545c4a1b11c6aa67ec6a0557fafd0e43ee150bffcab0aeb2df17e380735c25c344bff74c8806eb556c75282da7fae0c30b62babddcc51684352b1136ec8ef5ecd69e21260d159186b83da81dccccf42d96bf0f1a42dfe48c6632319449217a832fec973b4b368c42f5e4b6a7bc703203b0372df104f6fba38769ac0d736535f89765affbd3c5a3e6a5eec30f99bd89dbf10a305985e58f59725c00b88f8af108548fe7411eb58339fa2365ffaef703b5f47e8bd2c4b336c4c0601b8e790d4218b1079dcace49a395efd4c557704176a3f86b2cb8ff4029787a8f61c641d35b9d9e42073f3a24362969458f481f69f9fa22b9c884017850d6d49a5cc5cd817df18a2ca7aaf38e7e793901c9e265c4b5813fdfff973ecd3049b89c37535ad1848e74a2368934e7206b778c5688acfb73c35a9f3942044b4e3211eb2bcf2b376b719f69de60ca7868b478b7c56d3daaaa1b24294db5917be0413f6ef07609e2127d780cc095b90bb34bdc7eef6009405d41889f179454554836455a112b0e739fd660c79a9751feb57479f8323405485e6bc8ba34ad147d26cc44baa501af848ccba3ad6dc56ee2ab02aaa93df6a1588e61db6d5c2b628835deba255c1a9842af465f0863f7469cb3138caad750918a4ce96d5b68a9659540dcfc573e02500e8382bd444d09a292eb4754d8debc7912c348b66d652a268b313842e7b1049ea658143f003f08d4ae5ed0335c1afa1a13fae8f583914fcaebb2b97a431022967720c2e740bf003194ada123638c80883f4d7f4394d07e3367ad1b8add6b115351cdb44639c706dfa54d097c96e6200bde14cba3c15ae5c8207b65cf28d096c379f841762275808a8f5c2d42ac6e12e13dce197811d5dc208e34a6fc42cb80ed6f818e315a3caff03eff94dba9cc7b5721d968452df743355790ea95df3c4749c071ec3f1dd39cb0c72e7c29f2e9b7f7714fd605a024c57363796c83f36cd40bf392b4e64b4a084e6023a9b62de160fe9e21a779167762d6d4acd84e095684974a8e23eee655604bd1b8e36b73a304ec36feca210f233710c84e7950c0c97924b6623391e4825a7c786c0d0000fbece2f11e3ad05378b551e172b9771899096a8686fe0e28a423c3870612fd4feecb5e35e1e84faa9d6765c78a219c4ce3264dfb1c486856e627c544957e3a7b76adb8a2650e70ab10e631e102258a92d5b3b0149c3b34d0353b99df2b783f156cc9bfe62b5026c28255ca3a2e4504ea0d5afcbb62d6e6846247b51c61343a9996386df28a149867a2f67078c9c12312485f71e8f73d33ad81128193b41bb7568c045c713d2da3264ba43a436a29e27f359df1baa4cb327b3cdec952ebd97a3448ff482e63c738e9488551336e70300f7b4af198cfb78a20e03a8cff7ca79d0b6bb0f942edd76a63d33ccd9b0c9c99669c76e837073bc34289a45fd2da77d35fcf642630d61e51d4eb7d996f15aa95809108ce22c2da5e2733e45cfb48e1a6f340d6cbcacafcece001c29dad36f546e6deb99994870ba4c33331262a6aff1e53cbf6af263a6ea363edddf140cacdc0a4fb48ed16a8c0a189e51576e9dbf7c1ba39f9c0c2f95f5074ac27f3527439de162dfb7cc476cd78cd1fcca2a3fb86c047c4c1760e927a36a36964f7c495244b175f24c687589438b456a221e1a44d17b82691b640c4897010e8ad17af9499533a4b5027f0d4493a849d78a2c2a23a6498b6e32070313a74128dd7866080d42181edcfdfb2357a508c430e0ff4e8406c0b40db15cb1e9163e02a01597a1385dc157de44a2d0bea3e87e1248233b10320c5c9e50985bc95a9064a171fa526beb84700bf636586028de38bfdc6e7253e216250d12a3f29dc4b1ae9961bb5224597a5d83660ec997fdff5e696f8c954e0c55c430339d092ebd59b00a73eab89813ad12dba834d526069644a4fa9d1dc61bd160a9d23d884b37e702eda2ba5c916ce614d6a3dc5b88fb08cb6cb9dc4979e62e571ab84361376290d8a29cc41c558b294a152344948579e7b71685e35cb72f8d0797329414061fffc04c1f00b0d0d469a593da077107ff175b477ca5fa164d74b935f72aab2082616a1353a961055c7a68acfe3b117fd5c97dd397a1cef31b7c3a293a29939b0ed74eecefa16c59664f2c6b1ca14687161a1ac6b53fd020bad86016c499f391dfc40ffc4475d63be7013f196509ba3b8757f9fd844a3eba245e8de51c77a4b3c04ce4d241d771661b9155fb3882ab135901a712909b22161358c532ed14eef9e411907cced8e099f44e333d053d72323aecec0db39822b1e173ae9f940932dc2131a4092db728d6e1b8cb3b3a25aa3a8299e0e06a6a5d4716277ccf58df23d98d85e9a1baaf4d20a45355a9bfd7d8408127b75130fb096bd8ffe19d58a6a8cb83e5cd411ebec32dbdcd990c6ec53e8c3fbe10e41ce1a6671ec23a1fed705506a33c7f10ee40a43868ae1d20c0f2dc09de2371dc6368afeb05fa2b894e4dfc23b38bffbc7aefb3a34e9d99ff39f7612dd93e80b4da01c8f2e3cd5cb52018ee3b0621f3c319ec37738f4890a95e20fc755e4dd50e8beb60d2f9f86f034d9f5982c4a01587aad68cfaab1128581e2a761ad422ca082540fc27681bd7292ab01cee7da1e8f7053c9fcf039595f4258513522d7642593d90a38ed8bcc7464f0d4a7e16f01322e881c6590d5ad8b76fdda82e64ec283770f0707a75032d7a14c74dacb0dabbdde355c8213fbdce4007966277ec7bbf38c431219c1dcfffc92f09cdd486c0d8125ae4842f77a972287f3bbe4585819f097f8bc92700fc64c23ca615bcffce620ced129c223eb4510d5e43cf5d8f3d09fe968da43403b319b883c2ec8fe71810e47b4122a35353cad42c3c107f972491806c50dc3b405b949622af078dc7e598891764e07f6631e020577c41375c5c23fd01eb67ae7f83487692d0e31a9d411714ef99b839fe078f33e8c5659434c7a1f8d8b59132d63ec93d8a935edac8fc9088f957fdd901ca5ac30e1fc46484563c12f98ad6ea85ba2ce9e242ef4fdebe535e698f9fc3309f23701edb7e2df8868a324484dd9efeb42a27a118c41f79a745884199ff432b0f62f0b4d0eb1920066d663ca6e238c100e6953f3857b90a55acd02b0956de3162e649b240d01bee2280464cf8b7c5dcf4e356f7320e8786628cd73d863a23162dab36131c2c8c6419883d1f04a1a445d3026a183762423a6057459484168c113bbd27bb92814e050ffc55f2fbac00916da6474c647d8d075a02a679eb5a99c3d9e1b4b493bb363fae6183d016107565d6e7135e191b582894ef2f70e19df444d6bc4343c389da27ac72c80ec66f8cef0931b5c81e7119b03fa6ec692d126fc24c97a6ca97e0b95dabd42d41cc3922bc3dd7437fd55a2d6501034a65181319b78af61192c1611704ec1018915a23027bc018bf4b20bbd4c8f445c40aadd926fb5e85f46beb003bd51e9649623b3d54776ff5215a2ae974b66e6f1ab2af3068480bc075d6d0683742e1cc3aa0f5c4df5dd0adc9be78232f25bf5d56574145adb0d942e1c6ae52776f270797c33f65dbb5b7044e1b86585e5361991fbb27a409c5502be941f2d1306de3ea69cead92d6c026fc6cf9c9b194098db4a273c6e4f8973a7065e137e38244ea348da3f67195eb86b570541add84510259dd8fccb4635ff5b450ee64739bbcd057cb1a23c3619086477c38d5ab178c20004acd416a7d93b0f449619a96a3896e79f586716c37221c5012826dc6ce821a26aa096b45bc95aa5ad17942304ed323cef6ea71d254e9bbb378966eef23e1f9403ac99b835fea3e15b23be21c8c19b5cafc0a557db9b2c1efa9974120f34a995e7d0d62e41122b68521868d912dc8186849ae6a0a8173bf716f6fea4e22b567a501328442e078fcc64e99d21d56a6133898e9dc1e28534318c8046b7c6147f31aee2d4da7d45607a705240d4674052e54a4e0c610f2786ab86070605c7004ce4374a57904e3d31c115c165820947eeaacfc735b6dc4040e1a6e6de66311091683ab09078b50a96b0c290f1214cac1ff6d3aff0a4db233032e759b7857b7d99b04298291ee0499b47a4463740ee5767e7090935830d3b9702f1b710090bc0b6b7b017838c74a6aa2582c8aeae6808cbaf041c4e613592f938f1d34df5e212f1031171f1f6793993da248deac0f6f7b7841d01c44d9e2dfab611f78248af6e2872967772b56c7a7cdf1b247f26c9dc998e8d40bfce8f6aace2d45c14ebd8a0ecc887cb433764bea80c73722f45ad3089d688884e6a0f990e2d12149b79bf5c25c2483155a166d7becd884c39b0d3d7ba1dc40305e50d45e913a00153992e5d2c727c6ca95afa884178f732485694582ae3842c253784c102cdadf0d3cd258d80aa4d6bd653be10fac7223df09ed0282d440ff808f9fa4c32464bb610fe887a8d1e4fe662e3a277e1e61a0ad3417b5ac6f4e8d9333152801a06b46d0391ae072baefd8a0ed33f4e5ee4a8ca41426a61ebfaf26072d08c2c60dd8fe83a4725e996519014b8266ee1059383876c4a7ffd3468f509f3e82b1672cb392617f0be3fcd0b158fd443d9e3322b4737c8e48811b6fc37bce299b2154c0f0f0258efa89fbe79b1cbc0465bd5cd4d20baaa07307cae3234d281ac9b09d8c105a8465963861818d280b6a9d1e502e31eca8ae30c427627eb99bf7bde688dadb5e284552c6c96241406161b406022956887b4f6015a60333716fcb4a77d1685c7d32c08cb9b221f5a147a462d096eeaaddbbd08a2f083c357a112d93886e81b38966e769216200ae07add5661edc9ad29e709e1d6fbdd08f5b5d68d2f6e64a5a6f59121b5237c63284792114eeee781356f6d04736be565a8534bc5942dcc8d6a8515883944a950fb18953c74ee4dd0f31cfcf16845ab49ffa715d6960601779c689d2ee24f80060b8b4301eeb766fdba0d5ca22ca6afc811ce2f70fa6e0b337008ad13a1400f47d4aa1698bc0de4a99580dae2098441ac60082940c33a205ec94b6b80390162618aff5b7d553a7ec60b9437c092ba57852a18e45defd962f016726b1226ebca09a3c622f368707eac820438a73981a745f2049ce4c332edce2a44860235016477893def48defc56fa6f245417cc55b168b41d6d1eba62f9a79e889de4a9420c1aa8bbfb628c238bd3749bf26fa52348d70d7daa9f56a9a1bf21af9da5625693ae1e4554e8d72314de0c381b742d750ed03411d81382ea0bb02c073e1b0a4c130838b30e5b3d58efd12e318bccb8df8237c6401f3073b0967c790e9982bfea422966b03e09fe9fb22ec238e947fa8c2c4c5e065e51bf09107f9eaf53034faccae4681df94c5ff00249f40b63ee18fe1fd1d1420d82a2943853a2a6f79df284b49f1381ec534248885ce8b15f8e5e30393aba82f584d9f9250ec36a28b1792fe276c0d0ae483d7dbbcb99e01185d48963c02081a59547caa3c6cdb5d4f40e56288ca46435ece23f37ab9b46193c8d2db37b0276ddc68a0c9b8d2d4d7912f2453c7de775322a4f6a50388c3c650749a4c775b489612d00677261afad697dd1a809ec6f58df1610f1e654d90c99480896a014b720e1b9454da8a5ebf0ccb3c699902aef67094c3f598516cb4a6749969bc857ee6b2890a1a98faafcaa3d6fa5ce3b36fc3450f8b4c6dfe05e0857c34ed043fac807b42cd86ca2622fa44b665245da40350040ce721db6c9832de2d3767a290571a2b5d9138853d1d75056755cf8b35c33e4b91b9f19009d8715e78a183906141c07ea26f9e3aaa0153dbfa5a394d693cf5ccbef4761c6ee12b1c06352fdabc4253c34a2d5ecb2cb9c2182567945f791114af992e4f88ef4f3def31b4736b42e423dc9f99126cb391a00c9a033df3847f87f5d36680afe00aa7dc8ee9bad28987b02b07e89186346b9077b70c3d43d30a0382dd3e13e0df963a75b5dd291175b01dd25f374f5b51c83a071649d2b97485a81aa4bfea678801f2ea5afbd5a551b4f0f6a2e96f826581c85f3e9f418aa2670717b15617247caca81bf4f218488873d62d8188a5791b2485a2dad26902ebebd46e8d76ddb46f40ab5dbb39ba0b42ed116866f9729360a847730e8de31056ff3efaad458619088648dc2d23007b0bf80ec00495f214f1dd6291e1f00124b40ac71f8a42b6a4087ad9b734e0dd53202454b3fc242049e9ec17e162b7290e9cd668d6a0df6799600a15fb0da6367400bf2ddc9b7854246b4bbb11d85fdcfff118bf8021fdb090ad73c3bf4b17e5d3404e209ef4c8de68b22d3eab71ed59826e17ed9c6e080d4ffe9af05ed814ead42055e2835b11b9d2604fd9b5e22d9e9776595e58c2bc8929061c93ac89f25198503822438ba64e1bd676f5412d7e9042d152aa91ba1b16cd26232090234cdfc87f75903af8fe5785297048a0afe1d176c84f8d5a9fafe9f219c5446ceedea90411bf7c67b90508bf0ecc63b635e3ac8bce80463a30aaa9055bcc49149b59be574c385214be44a133924c6467a7b50011270af5e59a68548408e830bb22522c1ffc244e90a9942ea1132baf1dc2f81d69723d7bdcb0ae030d45a1ffae2e49b44523ed1366bc1adf2bfe8c1681e4177f990a4dcb6c1216e420dc6ae98b65f9cd9251f665658e38d732d0ddc6d66e4f79e5c8b0cfe6177805b9893f7d3956c6f736f24bcce66cdb1093bdb81bd059dd5a619a07dd0b1a6f0ee2c4d000b7610991887dc563f1a0c3e6a48936c461db5764c1fe15b5760459ff4bb97b40ec14a12c7f73a51a5643ead55ea80ed594bb420a98c2b3469c8b3b18e6f0e502bd30c819c5e9abfde7856bc549019022b008cee08e8fbbc30b56a08a6379a9c057a3fa39472b5ce793e28eaf0545d0184c0486ea2b9ff7cfb08e54ef7cb35a0dc24165579f8daf71058d867623b6e301e7cfae7792db952029e67749a4e57c570adf7993ef0430b92afa9f607fbedbe491d21378578d3a82c14a5aebc883bbdac0872395234a0c40d0b3017790adfca886180e24b3d8eeef64da18cc75ed4bff81193a51688e1db0040238141eb5425aa3a77e9b26a9b984dd7b4f59ca701843264a01fbd08a7197797c15572418574d7d956d6f27f0f5d4a55bcd8391088fbd888b5423b4cba81515684d00ec98e041f68028df9d8b5e24560f720211dba38c70624808ba8b4155d0d07836962ccb410ac5c36664f542a6409bd0220f681e72aba68fb4228a3ae8656c9b8276dc86040e00b3201d96b89741a6c424e18f919f408a66dc0230cbfcabbbb6da59c7fdb95cfad999413174e112e98d432a7fe8355df0e62c1be6f81c48e49b751c80a820245155073e540905c2eec03b887e8a1462cc33f5e242120ddd1368ef8d2640cfa186f3f4e8deaadac608c274a586088bc2931f681ada8b94772e08e00971858e72d86feb1218c6bb2c72979d0eefb13625b6922cb7dc3f35f227f361ea281fea09cb9a7fdfe160092858058c93defa94c95d8ce79c76a2c2286bdb7a56707c582ce40fb274e4485bc84a8088c091f0143f542e1c900f21edca4977e2bd5b4878d47ccee9cd5533427fd454d60a16877dc962f6281894455db796b2d89a8e9d0dd01021805ce05a16abd27a9ece9f7aa69d69980a8cbc1f95d628b36beb2d6cce5c6dc9cd320dd4e3ae5062c43b0e680e9b748e0067f4b2776173e829ef2e25f76a447e7c4dbd40e78f9b24d4f6d73d2e12cdb966cf94f42be2f15d9fdb23ae8324aa618891d26f0cb84545e7bb7209696f4d6d85a9d5d07b4b75d79b32d9e46f62cc711966ca935207ca1c4d17141faf3791811c2d1e6976656b94ec57e243693f7518cb94da552fd56436562a59457fad35c630541720482728e6bc804723b1fa2fe1222589ea16d3b459694010177fb58c89877f5308b5302f92218d3a1e6d3f1cbb91d8e74cc53360ad6320d2079ad499ee358da566e03c229ed95b8fd03b4ebe45d7938fe91d57da522c991668c6d9573a251524ae163f0eb7efba06ce39f17ea0747e39d164c1ea422a2c299ed145ec5bc3b4d3373df4059315ae9252348285eefaa0e9256f31d0fd2ccba7c5593604f6a1e54a1dbaa427d5b2888254c78de53205b305facf8134c9129dd8b731c7ceb1799d417c7ac2e54c77b2e557e50a1a5fa9ca53bc70c5cf915c9fb232ba6de51f6242ea9163687358dc9450dd1d232b121f4456d09d0044dc830a6d06592ad9985195f02fa6a3b627322647610732f51bade881f9e7bf08ffe7365384df76d7af0db836873d0bf1722e96ff958a5d0672b77991fac685092f33402424d253efc0432bb24db7f2905defeb7f50535d163a5167e2c44bdec69d89f0759d69410b4b4d34ddff78b2fd578fec2ebffbffdd2574737b17ba8a28517f700742a6bc7f29a98b436d315479111edad1ce27a8a6f3f7ab7f4191386af2abd1f0fb9b1a3b174e07657512782875d66f2e74f9702f615123dc2a2ab240ae824f73b1776d0bfa81e7761252a8309f303e8ddf70b3cb0fbf92b9a5998ae1877d4ccf8cf2323fc894479efe4cdcb57706ea57d8529143758685ea05e4f3ee80d81ac60f716f4f05039591aa3d5534cbc9b596095d2500f146586edf4dd8c46325e3ce9c6379caf15780359264e44ff1892a6cd94d1431fc38f3ac5d4d8d2892b8e34affa182620494e39d324ebccf04ca94977c8d1989570c58a1e90d16f287bb0755da980413f328a60092265ce8f703357862813355efae8225311e1e597321f9cb4c05aee6d33c2c1cbba70c1680459910b7f7a26a319da2b1270d87ac1b81061d50ac6ae45b8209b015372946af64e1c35afee469c523d261679e06d15a1b19541a69b9b11b848abac044a8ea3439e939370c525a2b425df1a80ce7e6cb6ea0bf3ee41b97225bea891f220ac4860b96da108a4649bae339b66b064b3cbf0292ee18ed2dde95c12889f2d8625bd6ec45966bd64e20bc5159634ec936d3cc1278841c22cde3c12c81411236e4a0478a61407ec4ca128410b0a4fa46ab90d7222e9c5ae991b625405fc6326e5b6d6bd8118bbe9c203eaa046c33c61220c2da434202f723b5d03422165b08533319b21727f60e80fe8aca322d38280477371e968bcea88983cbb38848968c2fd81a44774043bd852c84b34db90908975e0882a5c2521094e9179aa6e266fca01b7c90a99bc358dcf443744a6d5c54557a8b12545d70e485336cfd885251b5ada773bc3d0abc98397a25c72feb27f5c3b4eafaca7c10862b9c32085685003c6b5486e4cf95df7aa86d5f1343c2c1d4d54dd6b3ebb4e6a444804119278eb1520d093ae5c5515134b5df36ec392ded59db0c0d44547f748278d9160d521f9e3f0896426b1c7efb2920a0a60e23dcd83e84fb6e9efcb18450fd6f1eb4d63943a5e280a6aea954d1fc28fa4357b1ee8b18116867aa7347bbe0036fcd3f20ab78785fbe8052742253f4a7aa73ef1c10af1f6a1ba64fb612f068b244e1ce1bbc12cf0d868a10493aad47c20d3982eaa3c682d1031f29c2cfe2b4675e6efceab113c9fbfa016730585406dce641570a1ee511d04941fc98c833ca303d5eb93ed2b4c0b445125b459638cbb9fa2cf38273e3e24329ceb0767cc133ff22504890e00204bc8ff489096b4e783702f161a30595669b436b660f2c049f28b662189cfc57253f703e06d3901f7b3a4225a221cfca65ef09acc67744b0914025110d6b8a4644cf7a91c1e375927678be3b349df58c5bf8587062849cae471f0dfc5d247d7d58239ee2d17a3967321eaedc0e6d928733384c10f4e2b573d2443a0c2e24ac23f5de96908691efd77a92397fa0e4aca95f96e9f87d408b87c2ceb4262a281b9a430cd06c309c2ba5feb9b2151594498b0de5b28d8b091a58b0336b107c63e7cee058ecddedbde3a6a0bf99751004ef4364428a1f895b97b8fb40a47d23b3f8da6eb7f04093a0067756ae3c74056fc2714e6d2d0413c72dece251a64bf4b3ef103b67bc09c4ea564260f683f468954e41cbf95531cc4e46e5193c0818b821af77eca5b0a9b7c7c1d25b41a07fb8ee76811e3dd80212cebd8ab0a0059e31502108da5f55e2ed049cdc49d74b0dc1219fa85a269a9671750833441b35201203a06f2829e4771dc279b6859947e1298059188ef4057b061f6e2f7cc8fff29812f0f5ec49833286ddc44a9d9c48e86c1c53a0c3538db8d4c16689071d41aaebf2bfd23fb905c61cbbdba25d8377cdb58308d67ec48082be9ca30d47895f20bfb415891b52be294351199a365716323d5ca95000873f2b36fce18dc120d840799d1f4ea71ae20e35841a6d4923ffa305a14f1cacb4889036b7e706478334629cdf0d4eb31fbbf42521c822b9c85fc5201d5196bf8d9c5b06893c948f0f2a38479baf71fd41b928d6a3231928e89d448e5411b1a371b4cc872a90eb8074934253e7247843ad3681d0e91b8dc8d3ea8169c0b25b9575e5aceaf6f80f0a067f3b573aab9d9b696d5bfcdbb2013ec084b9a1c16b98224b5b887be9dd4525c802458a377810ff213df2116f84a51745b97061b8ba741262c45766645f453ae2ea62c323ced9704c65d14a6668468ddd2123737fede108000414cb193442d4285aad5366a46f5735cf14630b25a0d85699747c94f1f5bd8cde5512d6ec410bff5c49c40a8ff541d146d420742ebde8c2368f51b864b40791d3698014431aca6f8eefa59f2b869354ade572bfa3290ec6d2f6624fd27e89be1e57292f647de17f6c636e25bc38bbde23fb4eb183985be1102358fdd5a7303c4f7182b8a60c8883290af8273ef2c84c18339dafb2e3b455e422d70bf824b11f3538a4e929e85e347dd98aaf1741cc98d3a7493cdc8a05666085732c8a58ac84864606d92257ac962a384becc55fdb98792d2c6788b37d99c9abe574806089301f5a25fda8e60471afe61b50be757f532f858c53822da6b14f9ebd5640273bb3b0a541213c16c6c525f0c1cbd8c994fc14e5af2785b2831016f960c89f2f535bad650a5aaa0df7cc5b35380ac07242b003feb85a0b149eb7e1cc616071fde0e948259edc77c26e0afb30e129a52280a4260c3df5d806a03a28686d2ff843044cbd14003d512e53277f75ef29de7569548b37f425143f75d74546aac90080548f101fd7e5416d0a787d0c04dc441f0184485e48a65075f5071b74eb5fb9b0400991c0dd7d7c12f7a305ae54af83550919881211413303e3a84036e2cbaa7c979ed7c5116dc4897c9a090f2cab16afd11cd47ebda21e0f1cac3e7f50ccf5020eef302c96fb7ea4d42e0676838b03bb2bb87090e08393eeaf089dd952237cddabb898fdf2546c3fb5172244c55605ce92615905ceb7bc0c9c4468e6500bdbbf0366d581507096be15e7b72c5ef6b5fcd78b8f7f59fcf9ae7809f2c96e55e2ed4a5b2bd5869c19cabe2d2dad942621dd56707501739471fe86271c62e428ca79e0c98c8563c83e580f5b157d30a1571eeda7cadba089c80bfcb95ce2856d4813f848baec1941dd20290fbc8803936c290aaa2f56438462cff30d0d917dfa25e8a73c1d685f857ed3240b4aa29a975a55c4238b86ce0460c87f98cc88028c024c4626fb01c507f4440603d2e1b8385ad312c7b8fda3b2977e4d79e60fe8158104853737603f5400b8bb0e64fde59bf9a858a01328203f7971645f40358ddcc0152ee1ccd8fb2a7ddb8be5eb59c143dac089522e2a93974c9f0c69a35a1052b943a93cc0d336c2e2a1e86724f91835039c57696f9b4b58f3c88383cad8e9caf349bcd548a2cc574f3b48c787a0bea22547d9ecf5b8f83ce25ffaca94e57877dad729e01b6ded97d635a421f001c7ca7f0f72eb4bcb2b30f778c2bdd9b13203d4943909b0c18c6f0abcc6a1cd986e766d9ea4c8e57c3c7acdec90494ab79e662fa1ac15a73419ac57b3ae48daf8a04649f315db3b200f535d009ff1ad709e2932bcdf04ab925fbb514917e7d93c4d7fde22c62280ce0f21e10d9136087ea49c31877e169eb43c9eba8ce9a5176d2d528eb726fda602c4c30e07b0bb3e54e00f2f7e00fe7eea43042627b85c5a352d520acf3c320fef29159f7facdca47ea75bc82c1263fcde08553e3aeaa00aeb98fe4e81dedf80600ef0a23ef3eda924828f19c4c0c5877d58b4320b541a0b32c5b7cecc6138160c705b061e8d6af8f481aeac518eafc00091900310405f8b8a18fb46034c784b0ccf71dfe9292153336958556890f287d59cc22cacd71073147776815f8de615ea4b07126a47f94ba0399f7900fbc799ec7e709c5f355034861cb29dd29cfd36a6fa03039acb064e23c8445c64eb8070fcb0c5631658b685ded44605e80fadc594a4eadc15b198479733af5b9ff3b896f7fe4a731d9a090630df8f6940889e29f3534680d0ad9c101b90b2ecb6a7038c84c8ed8e26035f82c61aefa51d6b407229ef3b017dd1a72031cb176554f5100cd89e6cb69dec68fae5d651cea4de44ba2c3df673c22a6a4baa3861cbc9e2acee22c31f789a4a1c2492e5f3af498714016f9e6a946060aa02762619725cffeadd1f84cf0ea52be70c09315782df9d30065744cac6294d4e18c5cc7cdb09cac949d4637dba8f51aa90bba9a8a12c7f64a6b4f44a82d0ed60a2446135ba44030b7397e061d8588b6d45cce1be33730e3aa87918b55171c2609c22372868a2c05e460143bc00e45d58233de5aabd554a300577e7aff90b94b919c07fc91a2d937e7ffc539e168926a2fead1c693e34f6cc18d1d4df693f3e2eb2af7362b5c4f2da61c8a4238cd14e9be09da8b6c9b5a726e687017f3c96584abb79dd97fb1e7d3fcd1c32fe6418e4f211ec432608d2e38b3b8ec3f192ad02ec99995f81afc8c5f818730fb2e4c783b9109649de3e9f79bed41bec50895d4e56e1e2fbe717555b668a827c8637113c353bfe1c62ef791904f78dc710be83fd068774c4d5d5bf617c861d2a8759ca443f09820351eb7304598d1321ab3f18327cba7de5d9519dae4ce814a23d91d5b5a856ae70614ecc8d2fd73de127976be8f92be8fdf45acf1719402f5c4eb7811094ae196a5d786de09f00b23c6a72b5dd6c4027b866b246893bb208f6cc26d7be1928c36a199a4cfd3b157ddef32b4ad5bc2f9e26f44ae43f73edb79f556706e51016645b68eab7c800972e3394ce7377db9633258b82fdf336c8bd3a88d22044d8ad623e2ca6891e1a156fe8dfbaebe7158690f7a046411e8cda03cef95c4fed5e08ab5c2b74d9e4161343a8a2d8ebc26f184b70e5b68d532fbdef8cb83cbfd1f1e5d5cbace98c801323e4cd40e7e63c3de1d600ff02a1bc50cb9560e6d79669f1a8a58f6dd61498681ffed2f9bd9d87573a8d3397dc68df5296973674f5715cf7657e0c9bf5fe9b3e9843406d2d01d5d45afb7a9b2e779c862ae929444b4d35331089b608c17d3acf02b3bcecd81e1a3acc43b3f75b27115bbb2b1f4c6ee84c23e72f05870d7bb74bbfa53cf338c2699292bb33d574e3225489852e235489c52ed1d99ebbf8fb0b59c7645629e74f7a942b19b6ecf40ea20b2bd3303fe48a78ace9683a68d54d4807f6c59a2ae64b11a18ce6d7e9d54b5b01044fd2e32bdfd7e292d75ed8c3e70b97927d03ceb0aafe531f2c2b35655be2508ea60976fe9f137cf4592d3faae62f9a7c94257b7a3e786fb8b5704ca96ea34205f664db4cdab859f14647ce14219422a26ed3a287879234492b275fe629b72c895e38b081cad431a06105226b06e36b17e42d485baed431d2412c3616114c0e7d2a71515f6fb0f49b44ed89358557fd16f974cff249510476744bac49014b579eb57451a8aa181b7c25d597612629be1320879756139ef98ecc17ef896060a46f7ce19d322bcfceb78de3666615ed7c9d123c6ed47f188b4402c256f7c311a40417bf665bfc5fe82df2070678b2ac008c970dd2b112c10413d3a67456c76c24d6e18c5c390a53fea5872f137deb12c2eb303f93f0c3fe3de46c4228aa119ec9b82e498c5b2d6f30c3112dab301e2b8157c4f84712406f5eb5d14624ed006f3a9e940899930b7de24a0e9e419b7dadcd79fa677d3f64a0feb7e058521fe36d86164551307c045162727a5f6923df40246e70437a611aea7db6ad67024e3bea4583febb2dd0aac181ce845169144bbc0a775f72530fe5f34db86908acdd3fbd3b297c2fd79df7694e81164c432e3a8e953a80a423814aaf8925d25b6e106c21762c500afadadcf654d157c9c76c229859b6155e7e5892f16e51ee093b6f447515bc62d7f5bdfeb594113905d2fea6583ac361dd8528141e4d00f6dfa85ca9d35feac4baeabbae6b06a677e83f8f631b7426556f386ebd18328b6c3c14b1f903912cd41c16a966cfb30793ddb509e1c989156247814ed0d30095010e36129be243059cb2a7d82a0d2236f05a7fbd70ce15398e1b06a716cb31f1835480ed463fb1e422ba05329c73d7f01e532438776ee4af2b8396b214735a20bd380ade09c3febb515cfbe9565749e00ab66651edaeaa1ef3439ec7768289d0410c587a482b513fb073c60de1fc4a3716951a3d62f14f7b6506eb82d0d9d4e2ab9abfaa90091b77e03a3183d5cae89062ba57dd2d162bbf67908d267718240b87951d582a269e991b595ede9acd9e58b7bcfd2c5c36fb1b51eb1e5bd4824bac297bd794d23ba81bd9ab070c38554963e4aa185c80c762763d340c674be8999421d731a6eeb69d8b9e64eddd6abc1d2856e5f7f4946f9b70df49c1ec5add59ee0c9f115d9061b83a1117975e071f5f3f1e5a37be34ea6eedc434ee6e4572feb6cb2db4cda0fd45e355c81755383aa0c257403f53c1508c1938949989a6605e6293f840049f5c8f2106c57238801117960cf12af04e4c42ee7093b7a538d3d15705748f03ee7aef82f6165fdf414a2d9885c785e3736898165e150cc1ab60fdcec56a19667be2da60cb86a53b1561a5ef3087e6235524ee3863d0a252cf39c1ab1d1cd7d2a352d1bcd723c7211205dcedf228bd65e4c4bcb4537bc8d43827ba96eefeb0d273bd208f10d7883927c283b554a53770702e776f85ad812aadb0d6d40758e02a456cc4bb7fec9f640a39c8360117d6754a3333e01aea28cb8c135d85848310cb523e0c185354f974cc11cee73e614462267f92a33101cee19f5b558152c0a63d2735a7c024485a7af7ab556a34e3043736b45509a662d5a02e8ff9912b20826d299205e41db0541d3c74d1541f8464093bce9b0e7d44840724d44878b1841795835b6c50a865468d622db3140cd392be96656a887676854e94538f9695db79498fc36f9df4fc15a9333277dfee43e84a7e690b3b90639d6dc282464f9d1896a49819b5d30527d075489674dc7e1f64a7145caa6222a45d37eddaf94a7a4c3cee28d85f674e189468b2ab88a8ce33c96c46c9c7c14a562836260bbb8cb1ce8c8f931eea4c408a4add3a6b244c81be0f920d07edb53fd32feca32efbd55fab0d8e69f41b8204d0d09090746d126ab6e3ccc159ff0ac101d9eec3da912f893a89a71a2cf194da4851466fff016c956a72a9609c4d7bd0eb67078c0910d5913fe27001b315abe160d2942fda4cebbbf94929b8b7b033f71f7abdc9286013ae1b9f66a226d48b35c13b6550af609708285fbb45768b8c4dba2528e0e735133b502f59486980182750887229e935bbb49c58422512cc397efb59a747bc3288fe40d4b9436d8735fb3d72eb19af81b1dd908b132eb2ea0329a97b0294ca4a5e361342d6a45b44d41c7ee4d2e3f08bf477eac87a8173de91a4acc17693cc61b94ac5d5c0c1ce1395e4af8ea4677d4ef8b6228a9712c8e4d241c1cd21cfa3c77ad4b3ff4611b06633897d701c6d4010e7a2a3b7cb2ce45256f23ae21c53916c866aa3ebd49a563e684c65640a5abf3ef2e540c46032e2098cd8050dcaaf08a38846df8404ea617ada62b85f5c3bc46a30cf0f9c4aeb680399a9d71f3d764ede53a4565d4cf2ede1a96e4badc4dc71437a5114b272ac73ee17c90e3b8bf6a6b6d407d05dea355ac6db51483b1b8eaeaa84adf2bc0e77a49e2ef462248a04cd0f72e605ee2b54a21ebd8ca0fc5567f241db30def821fd8831f789fa636685fed87a9fa00833e8e60aa29281473348401edb61e92a11fabb25c7310f94a1f519e73cad636b7911324592db4b2967538595f6468943ae2d941278e8f91f00d705258b7d0ff7ef6fcea3860cd032fc842771d653a6e5f8e8150f8644ab36227fe27c3728868acd5f38f7ae2ed3239143313ad338c623402b5d7a9414a5bc9dc4742b0657af50107aa451b3eae9e194628448ced0a0c8d470950f4e1b4bcf73d3422ef90b313596405efda8bba54e43969877387f74f994345746199e4894594a3161e9095ff192c3769a4176cb880b89b677315691223f369171527e7bab444d39ef5b5d164648ccbaa2007cabdb991d98b7c93b350b0fdda6b31c211dfa0533300b80a32fd43e2d8c9b1af0d73d3fa516ab8bdd32ba4dc1edff8a6fc4bdaa9536302a53c3ffe18cf5a2ed0e029e0ed7e8de59899eb6c9b748e27dbbf3d455f59784d9892493b89b3f0aac5ca75f193eac6c2e2f8b3cfb29324b48abade8b3c8efd255aa344b6d2063c69ca29917829d7182ac061bd888c002ef7fe9e0ba9e99ca29584c1c874eb72480f2e33c8b12a12cd2c2c2dc07cd95e2a1289633a5a5262c87a783c4d984b6d2058e87a8355a9ae8ccfc47675466b41d75d037680413650633a1690a57c54c96169788b19a09b18805b2e1085dd74622ad4f7ea8ce38a50ac554bc89a229c2b23b609a53b9f692c33ed5837b6711deb3e89ba8464e1126d7fc1188b0dc9b197ddaa6e946db9a8fdccf4d9ba0343b93e5bf6f14a31783bc543c0efbe4200f4f50c0ba16542ae51c445222891dfe946a172c451af241829748b0fe3c5f9bd7514c45679188ed51b29564b66a5b1abbd657423e4cac52ed9854b896cbb4cbde1848951599bb1cc353f557c5bf0cf79cf54884c77d32165d4000766f5386891cd35135b6950ff0b9632e91152effed03d89a2750f76f04b750dd914a605a7fbcaf5699c8ee21c2abadbe9512b130857ff1918c0331642979bafc87cb6445c49ce509d9e58867f84466bf7337db673cd270807c916e798ad021d7e95352303b842c62dfdbab6f7177724b9ca3e1caee058457de9f25025bc959f52f2b63d9322467b8b623836c8c73cdf7d8c43d8ee8f371bd5539ea74e950e2c860a9c7e184604a8092c0274de42842d63317039d1db06ce082c216a7e0d0f8010e95e49871d052017568a6873137d127968cb13a640417b88baa0d8396d70f91b1687db88870cb451ec26b0475e7cbb25bf27b7e0a39d85669929c71e7511112beda1836eaccb91206cdc79a3a28d73cd251e593d50a3a8304a4e42805c458034a2a726f582018daa8036ecf0bd9fb8cf980f861287d841c329a31e87ede6dc58969a5aec522fecd75babc72382b3f473c084e7dc53cc67d1302721debdbaf0d4879d158f7202632d6e9c07bc9bcd19803df24d9cdddb69452ee94a40cd806de06e806a220f0b471d10aa60cf42d035d71c20827535822537fd66fdad06f09fcdef216ca5b1890257f41e68166d179a059946ee34a3ad5286f6ddb067a378140206f79ab25f66900ac5e5e774f57bc9aa6793c8c80c57962a28205e4de80d0be883c8bc54e41a04e1c0841910f48192a466e599a4f434cb880dc379c0f6aaaa2c230c438c421c63dc09d83683ca860310c5b46a43426d229b95fa4f40ab5923b040e8a4052a1c2298b4162a7ec379054a7ece0b74418ca4207c33ca5655ef4865a699939ef8d28f0ac3e8661aa7601b3f0caab780e97030788a4b2d62aabb5d5da9f58969abadba7b34e0a16e9b00761d561feb8a7d20b3e2c59da501404a5142103f2470eb2a4e5fe9048cbd09b0fe130771472e060ce3f8dab9eda01d1de3f2f27c9f6945d73af965e0963040aa37ca48314f5df8b51a8dc52b28329c822a590e1ca3a7859a47c1cfcc90cae38a389b5e0e989fffc5e3c2b2ff57fc895e659ab1029331f3598442232611356c39c1346a44a315313e59a76ef06a4a81fa7c815b0286731130e33415c954ef7ecdf7bafbbbbca66a6aa90d2cbfd6200b2fce40fcff421270d02c6b8c110a624fe032d4cdb1b21cb07801c754cf2b7ef008d8003cc600009d32f7a31325c1123628a71bb7b0dc65a50d3fc82d552f99c737e9c7beee722c50ac7c4b9d69982314f4a281527ae64aaa984a0847ab117132768132b4ae96465faaecdedc34e338bd8c206c7745d449fc612acfd8c8f2e12bf77a683919689e1880c545cc9f4736819d17ffa1ceb7bd70fc90b7898998749674e2047ef22fa21103142644a9a271049873bc8a034134987f479bc004f9777a70b2015689cb33d4ea4b30b840516f1b59dee39ebb07bce663d671c07d2b146c5643603a92c11f45da81891b0accd28505a45662c5f6354c9f427cde2a5b403b26db2d49ad601e1ec7334473a7bfe7e21b8df71d01270f8ef11fd3b5adebef3cf8884653104dedfa72fce34e001428c789e761098a0e8816fdfbff3337e65de3e9945fb1dd8437bb8ab03a4ad0e485d39c0218f25b0f637ca90dd0df7e7b78162df2802936d265fff39b73d470ff0acb57e4f5f06418874d17c1d3334909ae0f0ada051772ac4de44e1020172b8a9392b4f87994fc09e6779bea4ad0e29abc3a9b96beeabcc8dd77e0c5b60913bf9531977f29174e74ece9daa286f282b4f169416a7aa4a5c2d1d2de37f63b2bbe7fd48ed5e0d009f8772382174ef58a3ca62d66ab572c256d80a5b3e0ac3960c674b96ba4cff244ba5dc3fa3c8a450c93dbb22f77b46c041e4c3e04dc1a184e999c5d0498793a56292cbc0dedd305f0621b07f367267b944cb0f6c5c2d170e65d901b95db8b6f3db5760abc00cb9a81bc330cc47b325450dbb19c36c15b3d5289fadd992308dade0d86c49172cf7cfd45a25cc6ccd16e8e40131d2b44b29ed6ad03cb9678a866d061e5fb0eed6354d13028ba5d4aea61cd9b760a7c44e39a809eca184699114f51f9167e8542a4bef344a29a5945acd5329cb2ae28b916105d8ffff71f0897d603994d21c3ec0fe1b69cbfd347c4e2a80d2a194daf4af46fefadbfb276a354c506ad9be589034939232246dbcc813cc516550962e22cdafca94615aab3bf5ce27d649d1f7fa800ea7006e5c3412f27e012ce1b363ecd6dab746425903390d4435aa7d4d49312e9a3f83a623cbee65fa0766d9bd68a0900d942ed305df45f3cb14088dd3b29cee3e79b5562a733a94cd49517d8275acc350cecc47f657563aec3e79150a76ac614da2fef569c7195ac883a8a009a88009a14c92a506220172d429587223032aed0229f95464c719580c85344ae9e9d438dc195f60fc7abdbccabfbafbe52f4b5e1836cb5829c82f244ec912037ce4fd8c07f7e2ec22f77d6f09eef1048bf235513efaf0c8920b1ed0013acc1fddf75340da307f74200e119032da4b09c381541043555e21b70c430036d882af571465e4db5a886571be88c0f665693a0e22b0f5ec0064b8bb0a78ffa9c0e7416fce4a6d5e2f8cd379e2f399dd01a9d50b2f264bf226c7d0cd007fe8ebf31802f74b0f855c37a882165266b18e739a442aab63ad691955cbd496e1bebf2a6999cee77d54d61f3b55e9d0d5a1a6695acbc635e555e5a6432674181353064ecec499b2d05c328e38c4c09dc590124c433e12037b16434a70cd6268dc3659dabe1f007234a54c5728bbbc0c42c1e2fceea7f5af93a108fcb2fd0f79eb0105bc81d315c3328fae2a5d7062ca0b26030ad72ec8b805f6e1c97c1f3cdfbd0fde7fbeb3e7bd1fc1211ef8019f80458923b2028b73a4428526e4fe59d332aa2d44a9b1421324a0c8028ea97f7e319fefdb1df1d1079c352deaf798e038c1994d5cc93e6ba60adc1105961d526ef3e05082e7eceefc1c885c67cf9e9d6ddbb86de3b66ddbc61a1593d51ce738713afd49824fc7366e18587cedff031a48b58005c785e3c2c2f572699acbe57269d8f3c4621e4fbbba4fb7fab85c2e57f7c1ae8f0736c1dd58a362b2fa70375d7fa648d9b2524a8b80b5278580d03067484a60926b9a46b234a03854e69ac9b933774e1c64dc7bef0b3cbec00d6899dacdb7a22971a2d11885f90c56b5cf4696dd0d0d44a66fada53846e0edf33a4e55cb3411fb5b05ea872eba3fbdbff7493ef26e4c58c1f7dccb5d29a9d7c4f35ac65ee0397ab85b81fa9def803e74be1ef11135dd9f2bfaf73f2d5992b07c1fc9c5f73efd420973bf7e979a3ca110f83ebdafbdbff8f9fefd23b2c4c9eba2d771c110e014163747863e54e40b7c9fd36e4c8ca7c3b3bbf95df7f27c2f43bcf7bc07a4f1bc07ce1f4c9f1b4c33048e421651cce6d8322f38b8833b60d80c28a440993c9f8683274c3baec0abd56a357d7832df034e5407d2fc60f2803e5880e98b7305e47e36b7e6bddce0d9dfb36ba02be01f4c3f98c4f9ca343b4ded62375aaef77ba9200f1198ae0f8203f7179c29294129b95aebf50ec8f77203fdfb97fe0fa094220a709859eb6ad06ef03ce3fe6daf7d9d8d4e5687bdc99247c23c91a34e8afa63c8e2bde2ed4025ec3802737f3a9deea975d000cf1b1e42c0a2ccd2a9d745f46e9bc7ba66402a98a36d74b7fbf75e3074918e7fe17be50a5e3547e92edda711b2c8f35e207246b2a096042bb0e80843310011e279817570517f9652ce48a232b0180ac5d03a423f23f71f4096e6bda110d590688f84ce9270c5142492a6a938276013f613e6a37fd8c3fe4619f86a9e1b6560cfe2df7b9b4987edb9461f5d537f33e925a4cc344286fd7253d548984ea76e76d292c5bf510606c00e26abe72bcf2a138a16258b9e39573e9ae0c4b105fe1f9ce409bed0cf482ca8811e302681cf77ff525ff07b58254ba5dc2407812ff54f8dbadd4792c105b847132c4e18ecf630028b24188f2a5824b906abb109eb904404467292add5d65a6bed6030ae3f4ea47c79796a7ed3630aa63fc117a97d569f958fee67353fab7b35ed5a6b6f624e40a1503299cc7dce59a9e802f7fc285876f8dbb5f65a6bc79a7f89040c2e185adc9883471f61c741067e01071998722ae74659e2723f570380982cc92aeee8cfb9a8cc4ad83575efd59c5bc28ddc78069ee08d27e01baefacf77f0e57a5efefe36db5edbcb47dc78b9717b71636552532eeafba5723f0c0e6eaffb22633b62db8ed8562d236edbb6a936269b6a6be5de8e90252042b88d0913785b6d2a287853e57eaf6054594a96e8ac44c8d1765fac4b8cb82fd623726f2f26553aec6fd2f9fb6df751d7d4e19c6c2f6ebcdcb871ab7b735fdca88154d621c5a1f4bbd6da57ff0883ebf3046b3535adeff672db48a6ad73b4ca45fd2d4b4ad02c5f49e0204bd9fd61c5a0e5049a7999eba654025ac4de47099012be7c4c905473c8b0278ba4069d64090bfc72c3045fecff0fae96d17e8a9d20988fba413010ac41b0ee6edd74ab5bad9b966d9b3703b7057b1c028281602de3a0906eb55a2da70d085cb4600aa624750c25a4c09179e75ace08d814489892482b987004234c1e0d3f6416732c00754d9d0250a1a90369e810373025816109260f3c4023a05fbc91e57394d24ea7e322196017c9970daf9617dc719136a48cc501c8fd55d620fecddc714d993e6c9038b70a372f774dab8f830afc4deedbbf2ec39ee0dcdc37d056173181fb78e8e1e6e91f0f5eeee1d65aad6d071bd0e10cc8e30330803740d634eddf1ee1c60dcff372a687f25128e4853cbb43901ff2f63892c0a190a704357fa862f0523ec2dfa8989cdcef0143fe9853a60f2c1e1c8f6c8a270ccd5e214795e5a2c6f111b6b2dc968bdc568bdcf6656f8890fd9d2c6967b8665172571c91ac37166591f8a417d8a22c4a96e8f75b21e4c8e5454b44b64bde43428e5cc0daf279a0288b1e0a04d6295af75eef24304ddd73d58323894abdfa7392f32ba5945ebdcecff4106a02bdcc4d403fd3fe71b9882cbae47c2e382e585c6453e60f17305cc2904293bdbccf37f36ebc97979230fd95e523ef0a8f0829ead782c5054b3685749d1165c604ce1248e4f696dc74a06795d894457916b5052c7a1e2ab787f25037585c7b70a8d0126b81b5bce52d6f79cbefc5f87fc65b3e6a534c4c1886422ebdd3e974b3721d4f27cb1f25f806f408f451e4fe2a2a17853478d98ca624320b12ae1003534b37c19464da985a5e2479cb6d5aa26051c2544c8a68f968a6a9c5b1749802491a2097afe102fae0f2344059b71a2098b8cac70e3dcfcdd92d1779be69e3a38f5ee6db70958f5ee60b2fd8007d007d8d7f01e4f2794f7f8d8fc6f75598f5cc473e4a517ba08873c83373afd10196d9e541efdd9ae96ef9c85b2eeabf2d6fb9cb37e32de21c458f5959f66f7c924791a27e97d2f249d3ec2ce8b22cb49396d1ccc8fd5d44cbd0f9a34f63d7e4f9de01a1739b74823e4d2df30c61ad57244c955dc127db6a1928f6d532623d4d81d2320358417fbd224bf3bd903fec16d287cc4717b4add000b2585d9d5b65ad095a2b12a61fa404d32c12a6255558e8208b249a5441a6aa4c98f505d2e64499484110b20a646d819499a716892e59ac279f9425611a4a4b96e796f25c36523650322bb332992c870f30e83f39b260d897e5f001dee4277f94e0662f95a0f376d6f99395c912abf3b21e517065c9caf2817b0f585bb95db3582c0e68651dcaec8bffe7713c1fd087ce77ef75beebde078e6a9d07b4b2e9a0c5d261cb8404c17dc7ce6c96ce67652eea57872d05feecf7de64d7ba209e680558a4b38e45595467d2af5ad1eccbcaacccbeeccbcaeccbcab258199d592c6d65b68abdb12f2ba3381d6e93d2d9e974acccca6ca4ea0fb75b72a660066b752fc6a150ff3b1192bbb59f49422c2047930b12c957b955f89226eadacf44715246c1f3a3609f9a67e33c05042125777ed2b004d32ca5741185fca2bd1784e66e2ae9b0bb59c4ec342b4f28a801284121f1af07f048cd7d51e27c3e9f77d367ac51a15098d5b5c708572be7fa8b975201d8cc0e88a6edc8e2da132a4db3d5d62cb048dbc4a4439f242a450a3573a494dcfc9a78feb68c19da735f398e93538e40b0611e3adf03279b40bcd705962599a71cf154cd9eeffe83f0842a6c9044155490610ac2f35d8d1846e2a2938b46465ad40fd68802ee96b13f67e5a107cc699ac6cd776dbecf9b65870109d3bf7d19903efaefa70109d346be23e0233a74b8a306580c591094a1d066adf58c35321d521ed8fcc2c0e2cf39a7bbc7a8c86b6f9e37d74e0f06efdfe7310416b58e36a7a6cda975c02a48b218c129726508cc860e3b943107d0118662f060efc206274b2a3631141528c6dc03ac652415284e262a4fbc322747802515285659527101962c4e30b2a412e5ca111cfc39039f21ab9455fe4cd7d0e1045992f38776c22a94923ca76b73ce39a97dca1db1385a0c2c061613d3b90eda176bc7704d2c2d330b62b65a0975d900210fa10a9b31cbc090c5308b130623e5092577dbd0538331a1ff308c8989994e260c6706935d106965c254f8336cc2260c0786f3fa224f50c447244cff0c2a4c25ca153072ff9c7eafdfeb5d8d0a06a39102f7649a69334506285387c70becb5d7e6a0f3d1cc2b3cf84265eaf0d80119d726041f0b1663c8fd349de76cbeb25a2b37623941d9a17f87ce7fea82db43ee7ccb47e3f22def9fcbd734c8b9c131c9a004163fadfb1f1b599a21df2f4015fc345ae6c3caf7432f869c40c9f763f3b132ff63e5d3fab83abcbfe30b2c7e58f9fe074acb58f00587f9f73fac96b17395841731538f2958fc58c9f7c5edeffdb47c843b11873c1f2b2d6325ce37dfa7e1c1eefd78cbedbe56beeff9c4f9f63b9f11cf0b0eb97367993fb14ed322b8a752a9d44ce5d8215b5fb7358bef21ddd40d854231969b2db0020a382611e7309e6012433348c5621c77e7dc5e24e54ee6348fcc72c4880eb34dd204ea0245e2a7090365c1dd4120910106b85f861496e1094cbf471898bec300cfbfa04802cf1a28dedfc1e6acfdfb0097c0f3066e2fdb77401eb4970d34d2617b423132cc794ff7769867e4500cf0fd9b8bb4cc2cc2037234692045fd3a74dcb851a34637db4f1df2080226222475475df7c916d19013cca426943014d4d121b5af75375cf005d3f86f09ec9ed4378be8027f4ea5b69bf28e5a4badedc09a54ca6b249142c95ad6b29c7bc3f086375658d3646128c7e82c4d54b2ce3a7dce39a7bb7ba2d80002961dbadf98f252985f44a39ca669367860a0c8f6b0ab69b7d3d93e6ae3d889ab358e231ef188471f613ce2711c65a9ab51f9688b89aaa624359b896635cb5aab75357819636f168e2c362b8f2df3d24629ead6c67e5950c4230efe33d5e1fca904f56921122a1b64c9fe0d576091c5cb8a3714ebe4358eab6eb1c6daac19d63a8e2eee0aef3e464c29b5d62bb5efee374a4ecc4cdb74a8697d330ebd2706045e0c4abfb296853126596a59ea2ac8d11cbb26b95b368198a9266311173519b10ee4d032f55229c2026a026bae06030bb97fdb42ee770722f75f998469d9189ba377f7d61e1664afd7ebbeeeebbe7c74efebbe64a9e6d27ded7616ef84210622cc579e04b8027b40c0409a6c7776f3abf88e2c60f1abdcbcbed93d37d3dc378df71214e7d80169ee7b689e0cb9cf8134dcdfcebfd4f7fcfd97219ebfef012fcd7d0f7ca9e090ee3b3e3ca9efc3fdcefbd3ee4502f73b3fe4821355c1211c384f2f0e0a31f96bc28c8037b0c7d74bc21461335db3f5a222617a52c1056c3a41092f09d39fc3ffffbdaf576d0f0b37afb03ff47062be4714b8badcc949192973ffbe74773a9d9fdbedbc87d334ae394de33ca04dfdd42bdcbc3c9f4ed33a4dcbf9d4936786650c2d60f9784020b0584791c65c55555746c87a842c8d158565fee8403a465d2265281670ea1850dc64b7764a957b6f28bfe7b340ba2edadef36d1c27615cb898b3a5bb5b1baac105ee1c2e60018981224464c9e6cc201320a51419ea9e73d6a826eb8acfa74a8e9c9a26352d14037d4d8b6aadfbed30141243e67581adf5a0c02fa1cc71628b2aa6c005186401c7d41eb00711b0286faa844288ecc8026f2f860fd86180d08794a1aea9695a8873031aa3301e60e0f9378874cdd4d4dc3629dbc10416a51644d21113d0b0c4565befb66d5bbbbb6bfab052985978282fa7bbd33e9e4eb5540abb448eaa8cfb1e65a98544c7f31f0dbf25c22eb12a0c56d965a23b83525a2dbd9d2773dd93f6c7650a06819f05dd0e08c83dbc0ca1a6ee656ec2fdccde1744102c7893f72c8831750f7a5ba96abe3c2224025272867eedf346afe5226900d1a3419633889e0d72bfc7e52d619d31e3400fe59d2e66b4d24a2466c3c6bd37e7ded147f7de7be7cf3bde71ac94def185982c5d23a4bc4ae43b84139984b1600c912d16eccb451d6b96f6059ef78eb234f312399a41e2e62ec144ebda68ef1aa5f48ea30d1767869f5e965c4a5196b2bbc3755c4a29a594528274a454b31d6a9f7e3b24d961e6ed1e91b37b7fa6ddddb99f718d036fe815d3df5872d7e93059c188f8e90303d578efd5e1c610300f2ab028655794578cc8d22b071a44ca54d7126cbdeffefe3ebbb76ddb6c362bb5ce395bd3c21b29ea2863fc5e8c43a1ff30f45cd7755d9717c56c16bb321cde9ff373cd54d58bb3ad5057a194d629f5d5eab0ab4d6eafded016cc959bdebc7c64552eea3aa5ded059bdb135d689b5ab0e9bc25630180ce6b2570957cca462c8b0a91825098825092bdd1e5960fa443050ebdf3a89146182e98b1256ab65825d479e79d674785975d5e1dbafce808495931ee930d93a565587b49f888fe8bd188742d5094e689ebeaba010222e3a610b56565d3dc1fe625d7d41448afac590a13852d29c0ebb7abe713a9c75485fa43f4d1d50ba88aba00a3ba775b7d65b6bad9d2a2b22619a888f3ab665ee7f9145519e12468b20e2a2a6589014a7e5a55bc8fdd424bb5a6b056b544848d1fc284c2c916775816490e326c70b3b815978b59233dda3068bdbb8a9b6d147d5d4ff2dd1a7d5c94f787572fcd19e85c2a12d88938ff01252d4cf042a8a94950537d476d2401a2f7c93471d66604c0377032147b36bb490fbbde567dab3cb47fdc27bafc30c1c53e35ff809a5452dce295556f38727f7632738706409b37ca48307863a507c0b8a2198024c9fc3c1046f7d3aadfa94056f2bbcf21135f56cfae89fb2dbfa4c57ee17fe806208f6b1dc2f7cb7c667b1b4a83fe7da2c388890922f22f7d730b9c9b22b9f8d49182b16666f687cbd3a754d8b4e3e6a21a4d827500c654a9f7e4dfff3325ef948487f8408916f9a881eb793d602f60937aaa505a449626a6901f10ad760151ef10a8f37a8c0225ee155eed52ac70d6e17273829659d970fe8b0a5f63620f1d66a60e7f3728f2598fbcda7160a99e91e3bc0d2451774197640117f9e06bff71f90469a40ff79ae83bf970dfdafedf5554cd1224f500baa2cfedd3a1cd7e974544cfea5a7b72f306cce29a66bb6723ead8fa6693978c0a7108e9ae3744945ab63681d13b5c385c5d9d2642c8b21eeb51e7a44da801c695f8414f39107a4a847a0ac426e0d74504716650c0b0d1bcdf5a8c99aab160ca1a9e59032348607c7d9b86a5438d65121679248a49983079872d2b525cccfc7118e50b56ddb444fe882b497b9fbb2ab61e6fb127ca9f7e9cfccd91aeca5b756ed45d24c87f44592f350824552056bb059e3b419d2e7961af8354cdc54a1e1d202c21f0f898f68802ef7b660dc3351ce6084c0af43bd9d2791484d38a114d404a6ede93a0fc7719c13d60c49df78bae3440af7de23482477f72a046725066bd9409163cac88d0d3d7448ae4f5cd8c33f8b4fcfa2468d7b319628890a2b6ace9c7b31fea73315070cb06751bab48a8a8909c3af2d436025d9082e10c10c134d12508c1d986423802689dc626585e949e793844e98b9c0f4e4fe4b2cb8f05f51346e64494a41c445dd8282b97c4b915c95d454457538eb5f9a27433cef695c9ee6c910d07b208df7a0a7f19726bfdbcb0e0828ba7c342d204dcbbbd0b8fc4b4704148b48609aa48b4cd245be4392dcd95a66ee74c00fc944790d125932428ebce54af8eb895b0145742c664eb374d8f78914f537e1ca50e42956e44ea2a2b0404998eb44c260c11a584f331e34b5e6abaa8a9af933bd0ad6110ad8c1ded18519d570669e74430a166b45d5a82aaaa26ab03c0d72b1582c96dbc962dd1d536071b2b656e7bb3e127247d19943984fcc22e4fe6ddb1e7f7e83d2a18fb268d9678be5e06459111b66dddd59ace6dae3c4ebdedb3dbb59c51461cc50590c6b77ad6d9ab6f5bd634d1886d11b2a140a5509c95987302b4c68491fd956cbc63ad4b421600b8a01166d8bce3a9f82a894eeb50545c968060000800083140000200c0a8683e1906048301e47f7061480096a7e46785a389707644194e5380a628c31461a020800801802a6466c8840001ad35242247515dd25aea9bd1a815eb13b684f591a12e5e2936f07005b91e1280395374a407da2a5fa2210d60d5434e3000446e8254883c0e8082f937664ab41307f8e43c53b3df9214aed61572b1ce2ad97a1cc323d1aa8c59eeff5747a3451153be82b1c293b3ebd73aa827cc48be50b89aabc9622163eee20ef807562c16c2a6b314a8d6a60791b1cb3d50c5595fd6248ee8f2f4c5e7c4b6228d43f319abeed966c94ddf12a233bcfa2a6700f5a1246bce30668549ba375d34189d49b3f5623f8448a707c222d683e61e4096b3b9a3e81cdfecfec11b16b0a315b34081a101288b026c199e6756e1658c5c6585db0aa20ca9d206e649b236e3867254e42966fb596c4572163a29603a08a05622c1a8164bf70c57e235f4988c2c5ef111a1ba5ca98f2fb8dae5c2e2c21a904cd86b58d80d468474dd21d0a97dc76e5ed7ab4f29c8a51e17124bc73ed4189df74bc81424c1321c1f65c2e7dc5dd8610ffab3780f4c95e8602c2f9b550b0cea4380b464ce46cea4746dbab8c8a32c7121946c0512c6f945668868d821e52c31efd58e2b80e46eea42ead5a2a6fd3b02f505bc2e9c793b166242b2c2c2e5f3e8c46b642e630e67b7116ec0953b2577cf6080e775ebedb294a2e77a36220fb4c5af98c39c9b80435034a053300aa68666986d2f655117f68e687e033db859379ca671893624670f2d762e9ae0fd4c2f1dfd186fe3d35519fff39fe9bb4563e1cb4fad2d400d550c487eb1cd85a1f2d9d10f503a1a82bf522d75aceb7d711fd2cf3cca0527dee80786ca9ed53d1a0cef6faa349616d70a28525f2b8ec8bd0626316e61f358935b0e7ba97be1833d150a38d050f13fc02b189a98d7d995942aa17eb56b7ef4ce2f80b3c45455998a70b38a1d5ddc1186a73104db8f226875f21b415e98ee9097225b982cdd68e3586e00fee1b0ad841c54f92c9ee9ce8ace47de7d72c2784dceaf1c4d7c58d114319c7e6ceef8342546d0b4a291368f8c9a23258079f081bbc9b023e2891deab175090f42be2f395a13c7523581df68b39ed85855944c6f99c3602e0a83a59e9737b5455154280c1d7c9bbeb0eb9af43da1dee264bfff8e5749c6ae7d60e9fc6921a8dac6d892e3eb026998dab0511ac24bb1dfe4aebec3f1c68867366fe6cf3077758b7b933f57318f22a1a06d621eebcb29bf1a5b44d04f6a629177780b0b54dd21d95fe77dc91dbf37c8d7bed54d0a9ae744d930fd4a1acd2f370026b2bfbd28596dae134a79d8913896baa4bedb089ce1bae748c638aeeb49322bd1bf1b273adf9110235da41b4a47808cc2608dc583528901d7065d4eaca3d90cceb38d21d93ba6f486a6d020db715c1dd91abb4aad177271d976a75d24b2f0892bc943b46285f893fe5f07e6e41a017ef0b39b7543f17babb1f6567d95a93a5b4905b3c5f667376dc5880e2f2b34ea7a59f683f6021d88fe8ebe319828f28b89d74e3623d51e3d702636ef4df3e75eac947a6f796fcb29c16a111c9404a7f2954b9ba689c2247bd99208978cdbe6a7badbdcdf8d31912522e3dccf570ebcbb51ba0c2dd951a71a9d3cef7a34a664be2252f2e12f1f931798f96bbb90f4c6eb4e4c779e88f9c914a19a9e00d5da8aadf2ca002b29f80e4508163a704b12042f4149f0a34cc85691cf5b9fb16cd377472118fed2c882b486a29309d3bd6d51d468d8f5247624536875856f0f09aef960224e1e8ed563fad2a8b925f6e81a77767016f8a8e626fc30ad4de082f33b7e30d041bf9d83c0eec99436f1cefbc4b2a75f222172853d70de7611aa236e2af1d9c43dce85e7ab6481b7ac00a1cc1ce2522143bbe8db98909817722b5e381a4765353a229156077f6a738f398be0095b551b1fba45e3e80d4814f34e13fee56435bce74a1ab63e7d7f1d00621708d81b94890d20ef8ed27c1f899e31a0404828ef6887d8e5b605d8db427d65ade2f9ae584151ef682c401e708816962161a8470f1dbadc2085e92b591fcca6c66e4fb5cb4db43aaec92e9a3196d464d5e93c81834447948a26534fd87cfcef9d527cfa0c22f3f2ae7d1f4ef22e028ab4c4e7f7dc11bd83f50413b39b344e2f945b6f61c819bc5a983ba97b9e8c678d22ffadaad715ee6a160a9b3f8cee850103694aff8eda216e8ed416ca4b602589029255f97d7214d323a943681463e563d075fb0ea4816b696f10c66753c2abbe1266646e74943699faddcfe58501e7cae44cbeaae05853b2bc484ad84e28b471f261d6ca41a090c415fad0920b123ca408b6b0befb34f64d0fb121b77b25ee94f1f61282b2d2dc7ab4e73fac76a0291bcbf1bb212004d431014b13f26c6cdbbf91ab8528838dad23e9235dcdd369ab5c3bfd9c493747dd7eafe0a2e8ea05bee2679a502f96feee3b2930d56690b77f4ad965913ec1fc193163cc1ce896ba0c792ef35dc4307c4227d0a681bd7d8c03fb1317e128c645720b720187401873c6ca93782c65691b5143d90b0c0a22e788cd2a890026e9567ba97ee10a58e8f4be51f3f9f2f82383d2e87c7c331e719bb2d4cd201adb4538ff2719d0ebe9215e01a232c2108044fe00681f0289a15365b39cac829cb297e0037cc15406ab2863c7371e73fd7d20459379fe60d64bd92f3f5260cc6c0b690aa08f46ba1885084a0688832b09265489ccd9d457f98dea5931e462ea40698808c432a3c9d2863f0a09bbda496da4403326e91e5725ca2eb66d1b4e9b8a2cc250ef85c397335d127a85ac5da441cff651fa4ad5981b833c242c783a180512b063cdccf8d2314431606b7144e21f8e4730ed8ed1bb883b6382df6ef74dc7f06f27f08bcb1db10b369561bc884ea1894d4469054720f6c4a2a185444820873cf73a11bde7a776e6d0217920c057ee2bd4b3958991fcb2f50f400b3387e1f84c55198a8e87a4f460d539f28d260d943ddd5ae07a9eca4bf8eefa5145a8c0e3a78336a688f492e996ab8a56283122b68c858cbc5400f7301f785bee22d0dc53fb134ec25cba8187c5730da9a378549c328d5774995bcc165f9c04304afa308ed2e8901740314631ac3b13cac3f85ec79d267cc9442d775cc0d52bb97199192d4497b4b3cdebb927db7bd410d1cee745d3a7cdbd022b887d12b4faaf0690b9cad772a1871d83f2d0396564a5b380f86919887dbe5190baebdc0333f355bf7f494aa9fa10e4cdcd4cfbd2f6c973d61b5b854c0e700fa147846c517c56a37bbc6e4eda39b526d14fe64494ad792e95b6875718ee0d92cc816a5094d8434a58397ed4cbb5862a1574e06ad975f12db225beb7b58fa7d2f4bbbec8207996d74fb13fb2fa291cde27d131af34ad5506dc2ed56d9edca3514da72170971b34f7fae2fc02493e25672539c3e4d25208a7a57a0afa5e299c440ddd3898a787c922ab0fe5ec5ead20e97d02da5aea9c348148e98ab9ea6fc1e318051ec5818857129e3fe52b346fa3ee6346b45775082b7352135fb2892c7cb1731089d0387b19dd4691331333f2dbbe935642d9b53db333d8c5f8640c04d1ddadc5ad4b92b568ce5db9633a3387bfe14b3181576c57c98c0d68f2b45261b7b614748d092430890e1a509dc61f40686b5689eea15fd9e5d704d0607781105a440aa4561986745d9f8d392e6a4ef31e44ffd2ffe6c907e4d3774079029d31eadc2fcf4a64d65a917945e792d5640b4fbbb97bea9db8786ab457e5adb3a1723767df504cc413641f444001f718f6fff11e9f0ad28b1e8fc2db0dc53b6f9c7646ff3e24cf51ea2ec7896ea8fd8b633062068b8e72950eff65fa1a3518b43dba683174ee80ee8869195af357003ec3a23dda5407b5321814c049f600b73eed059fa8ab028b95f03e71581974b8f1f0e8f0ef83bb60cf33a695036740901601792ee57190abd932d05d7243250cac7fe450d03a1739a8c21689851202f1994b8bdf73e2e76cb09106c431f3aa0afafe1fa83691190dd18a7cc9d7cc3822f9cb434c7699c6aad130f78e5ad6c76e657396794a2da5151d74e12e1ee8f1a74f25e60298a9126456ac2e553db5fba0aef2755814cfa94e1f6c0ba8c3c77f4a3a95509e6e794dc8f7ee6fd78abdd2b045bc0a26ef6560410c63bed05a43d9858d79dedb47f9288cdc04843476102c82c6604147dde05de7c8b8c9f45731112eed495ccdb31db83bbb968ac61900946c6f154ae3163575a29f82e8b94566656ea014d72876c34c5da7a23015a85df05178a947b448ea59edae8c111922d1a220e9878b086de5419810cc52ad341d8e781f9bf849f72b1ca734177852e232e35e6f2c5881f2d84954676b4682445a15395a3d0f915d29803fde0c1d7955add5333aa09d3cccbbaf9f21c32135389ea4acd483bc7e7d5bb61e3d9b7ba8308615fcb5fefbab56cde38a817b9d9329b3aade619998470a15bbc4a88b71b2e1613a1df3c5ee0be45428a5034673a2cc740c237a1dcfdd07b56e03e6ff3aca19a4c665afbbc644185201f77e61226d193e394429897df1c1ce11932aa23cbd6a8c4d6200479465c5183d84f412db145aacc1996e3e8555ee3a302d26cbd2947807b14051e8925393c4a1d29a30c89f246cff02da9493f7a06fd998dd5c34e543a7d804eaa99f7bf3a25c664ec01615463d18705aaffd203035520caca3049c05fd2eb7aa33b488c94651523f1046324a8616124a14f1ff259ea200e42b422c5871a8d0e01eb8316c2671f9244285b30d02f3c242fc0188de2470a76eea3414303093f3ff1d1309a7ef508fc32207c17eb5654658fcb667e6aeb34b52b1a696ae500dd6e1b19d50229555f1546dc4ebda087dda36b0e53ab2ac61ef38ffa244f39a78ff6f933f7783184b1ce1269309531b4546f69b514a026377b1ff3c0d4c50b0d53c672045dc67900ad99322939796e6e226182663889720d49553ba44fc1665596e006d8348adc96371c2ec5d60a1ede52d6a8b3b3146e72d06af5ed31ee591e071b3fcd1be3f990c012ace2b3ef3cc694c19e25f7886311999bee4712a57d042d8f5c410bfa2767c5cee7058c5d742349a03ccdc4550f87b33007fbee823637b1c6cc47ce13e62da9ae463388ee6e62e4209f5a9f07de7af81c2a48f72cf418f397c14f0467bf0e47d8d54f364e47450827a8d83ec05e5c80df651c44a8800dd4a66bf0820753eaad2ef8b5d5667dac02a4a9101503541db6e89afd8c4a53626c39cbef710ff57df37040e18008bbd2d8163eb2bc2096308f0ebd3d2b8c59e7473ded2159e1ff7946211001b236dfb3330db0ab56e8ae5a20d9c2fe69f97fc8b6f3a7eeb19d74f987f86ce6beb81a7e8f78beba3028ee1b9784d949544c64ad4c88c687edeb4118a37c277084986ecd1f7006d2544eca2e301a11121208481165b039077c1a2115249bef9c99a790a42bbfde4d649b5d4ec77a690b00cd0737c26436a3d40e93361d7b34683833424c2b386ec486adb99c30c76a5fde823791a3832cce0bb883373343730694f11f98068dd35ec68afc1f8041637ca965ba98512402bee0f049689063c12110632a47ec1ae294430fd497f31017c15df28fc0cde13f192708d57aa2944786ee7b2d30ef3c3a06615903de899e8b9b6de8ea9cc6392f078eb071c02faba22b04b97df8f40216f808d9d6d3a0c90e9a793545df12e77493ac2fda696f315c5fa26d723a4a8bdac2718ce973647e19127642f2dea87b84642ce065c4bfaecf886c29cedbdf2974f7a4f2ac33a407ca21160af3f111c0d1b7e23d9cd2ab5ed8b288663b84de659137e4e6f3c74ab2af4320d25fd3de3a8c6777bdcb29dc4761dd6f7d8b54c83023111cc0e143b68c01abb4fd221d1c0ec2187a29593ccbc99dea46c7b3c84baa814e49e7a2e6a1214ee5624b52143cc0eecb2d676385fceab7946a52a91701afce00ddca67e2a651c26ef9b222c0dbd7a959b97a0da44765c68b2c32a91825c4ac130ae684167e418f6558556140f50a3e7f876b99e4928981b297fa7b140d9355c4cbe4078884e16df95c9cceedc1d67669da8d873cfed61fba9194e4e59e0d0f2ac17f117f70b16be7d294b743f17301230545af7f886f700fc58fefba5e769897897bf141e2266a418ae5fcafef04b5cbe128c563848113a67ae190e094ff2d7bf4adf58bd83bc0315af29bc4e42b6789e9f447e3b090c3b617191410feeac5258fe55e4cfba3db1f027527f5b0a0f545a238578df60b7d7160c7719415d53502822917951c3bc6e309c69be1a067b5b87e200fdfb161193f736826957c480b14205bb019e304d1c08e621a20d77a0133e010e5c1280d5f178217e4a0c4ba11120a80dd081f53099b32d8f8d186d7d20383aaf732bdb8e245012357c07efbba849ad02210debee21bd2995a90b87e120abede046b0f16dfcf5eee4cb9422c64551558089c34c6455fd927435e3b7046a1ec2fe75d161cbc9bd683f49d5a40bacb94c2f40e02c559930a66f84593184ede64c997d63f45c3aa7c917b8d30c76d02a03d27fc67e7891c593b018f54f68a51b1df174d5a966acb929b35a7d4770f1effd3fc36ff55fa2dde207600a7eca42c08c6d78decbf02e713c79df46baba7dcb673136dba46f2871d3f1bc76fff4faab721a390a93dee33583eceae2809b71c97b1f7b417a97b34e0cc89e53f77df8a8d355d63712d3ddbc2b5a48afee9b5a399a1b1a42f7f54042726d9b4c4389e101396c96267fcb253829c42b49a0f0e29df189ec643912a9edf47999177c71547c199a3a063ade3de8264e07b40beee1eacc23187588c4181112c176693e6a5dba1baafd1ece46af8a055cc4d315de8c64262ca93f8ea0bf2e3cfeace9545810328f729de075297880ba8426ffc2f62c3aaa59362fd1aa8c1b75e52a7232ac2c8736a8f301dcb5b9297ad4e62f96ff97a93e419fc0039a4b0e50407bc022a5e076ae8f56c7249b6aaa017ea2e883218faa9720f46b559c29b4566a570bc6d96ebd9d858b7b297a623f331dadaf75e2246d4955d3483105f7f569f2a3fcac29b149994d12df555b986051ea205dfc3b3df8a34f874f0b54b3b7a7304a39d479441705146215fec87802da013d594c1ad9d87ec7fa2326154261f62ba632ad0c84804a0f6dfcdae9a153e155f15933ae606a171b85c4aec5b4abdb19ccbdfc54cb14943be06c5dcde8542266b71b3c2f0d889671dc32a4c3bd9046eb370fb0eae014e9e227fba17c1fb8ec0ba0b0f114cb43c67751efce48d11e73fd1d202089de111638ceb719a48a78105cdd70d7f17a17b76eab72e1a1adcd71458dde8a3df1b0dfc96649647feb8e63f48a2839a51bed4547e4a9f95a826664e522b2018c59f07c4d45a2a8321fbba03b7fb514f4ea4c6c93bb21df88b60ebd75258b94c93b01749e9afd4340482fe2e1a6f04c817feb935aa96911225799e903f768884baf8b9fc1ef96015be73f9a8df535d5774eeb438301abd695b6f048a1b5ca2023886de887e4c97480e272859f0b9b38f0c8a7a0e3495aeb31b1d44e1e099b2532c3ceba74984e18b6e92bcf8d5159eb16ea7d85cea366a522818a077cb9f718228e5b7307069c85554b545ec4d59aeb587390ab86d8e1488532806b07f7cdef024f1e5fd28eac181cfb8a2dd250eb67127a762e112d7ff9cf3c769bcedfb5221d1cd63403b39568cefee9180a9021f0f8bf8606e8a1604e13a49608d635463ea0bcc3ba3fdfe880a5c7defcea98d852176cc3b3373e715ce30aa0ce0a0987df9dbddf022381bf9e7cfbaa159aaba58b07c7a34752eafe5e549a351850b0664801a0fc32d6647b38a0b49f88f80a402f2910baf0df0ccdfd8876b697ec5e9d54630bf04b8638c95339590eeeb01fd4d7de6c13a3fcb0aa2d5f81bba4a561cf997f144a1213870798cce37cca85a6721d84190d9f2610801536b2ed213f7b72c23cc05c62f3ced50149aceeb5a21b5b36703354a42ff4a157ce2006e7bfb9e50e40658c97ab0adbc4304e444117bf15fe3a2ca2a78de18d915dc87cdc3f474f07dc2dfdbdb7da584c32ce01460a1773e3744918e2308b8753b31e4edb1b136a4fd0966b7c842e9641d6b3afef2c182061be14279e196845c715029dc21252a181a5c1883252023e447c88fe1ad94b18b31ef2ccb07010178b61b7611e69250d0614880cff227710d150757c4837ee649e18dbd3843a923a2df34b0dbf82559acabf4590a04bf6812af248e5689e612e11a88fe5c24d3ce4a1754ec1f61674ef6a98468694347c9df6069fd2ee2eddeb2913980c4c62c2f1b97342603c3183b1364cb7dd756eb2ef8a464481a9da05d2b206c9eed196036fc478b58fc035999259c92965ccc26ca694d194661cd0234a2c4db85da661751618a7415c0fe64f14559713cb84853de1db24398ba8caeab81958c2658d1ba78ac23cfcbec96b2c0a24230236760d3da3926a900461a8a1a62d5b1341b08eacf71ab1d08dbfe50143bcfdb069d5c75bf495ff6f98f63c4a84844d7a65409efe07cc9c3f2c67c232556e7005143e5353b53d7a26abdefb0d325621a1dd0c587323349bd57fb1e082340a788073a3bb67c2d0763b50080887e786624022eb8b3c8be4495d20595378c971c079d704b28121332784f6766ea7bcd3f23f3e5286aab3ee94174131287962195cf5bd2138a567d9d47ce4c95168488ac9382cad4a25d1716a0056d43acc83d7b7dba4c5d42487f168a9c34d866209871cbfc3f5af3df77ba506e6dd582e21e867f74a26a31a263a1a126bd161d81570895ae3e48e7a6b210811331aca6b3b1f675681305f656aca37bd6af4dc32d51c2a158cd595d1e0a9dc771a50cc39db78c836ab5e6841ff4200bf5b061219ef4ffdf6d31f9a5bc1159d4407a9f607b31cb1f06f1cac9f9c5056b5055add2fd5999820819365b0b6481c2d944ba9bdc2eb1ddb6fcb2902e87fb5b2c848c3ae2714f1d77348001f5e6e0ebfc18dfc0413e68103275b2b1dd301f900788534b00f5b301906eb38981b338fcb368f1d04b7ba73f56a1c006591080ed1f87b7c0317c1cc6132452ffb1a933c740b0544df8bf45a0deda8b1656f8493970c416c2e50fb7130591168c2fe9ee176df6490555421497d7d1c944bf5e079fe890a89615f2b574b9263d2b8a59974612c5076bb9ae5fa953ad5909c0a73f708332671d94906c5181f04921dff2452f47a12602d5d3a25018771f4621b37ba35bc9e6aa93561f53ea001d670863b4a7556f6fce9834a6063e1e0bcb022904e8e2249c360d911f8e19fd7c7773cf0c70c3848b27c2933b75895c41db11a4ed509b70d90669416c05d1facb869b7406a11239b7cb78f1f6321b70a026748d0b6bf0a17f956afb50f20fd52628242cd4cf4910df09516801e232d35e5a09d60c9c84e96b52167753f6d001b06a6a91cab7437448d854a2bc488b48c1b510ed721e42af74594d81e06ce5f831a8fe17b6fae02a6a3a25b45bb78d042c3c4a72c7b56be30852490ee91d9d72fb509f4770b25fbcecba1eebba786c6cc3edf65289a3a7d521a80707c00f2252f3cb20d570184d163ae7be0f517e90cd2787186b4645eed11c721c4badc09a1f7992ecca001feda1d21f221c296cfd21d244b07db898f67f6396a27c9156d45bc221e26c3a45a1ee78e9db8e0c194343001f898ec3e39453a67fee72804bf9d853939c46092af30107ab074f1d3c0145fc4a67ced4314185a0e28a165c5d226b01baa30aeb2d839e7354f15024b9da181c1d7f7610a0840b99a5e1302ee4ff16dfd53a81185a9d7a508ff0bd3e2071b5dd28ce329d06e4b90d483ec0cb0c9d6dca1a95615a46eab3059ba8653d2accbc04170a2de77b35e7217ad9eedfa1d2dd3f5e37c06a95ed33c80f5d0b155c9d2a4e34e01f94346e37c7b0d06bceeb34fd501be60430a9f2548832740715f2f25ffb2a7387476dc51894fb89b1d9b19d0020c853af062c5d5ec0f08a6394c5fa1652bf45e452602570b90b411741a59041bcc8d120fc965b7ea663f636c06c0a4c6a2b99208db60f3cc22f3906e24d172507f823311675b0dbe39fce4d234de175d7b2734d4723d287673f1f36ba732e7508eedceed03ef889ea5f2d1f84e6e842c825682a987a6b4df7486e7d6e7fa1583bedd291eee7276789af29cc3ed61005c040c72c0285e8fcfc34813658b07a6084612e15ce429ab6851285284fcf101599573a2cf33c939599da526772ca7f1ba167f88b868eccbc15e65eb5cca9e18b855521bb0228156651247eee9b28b2b03c0641d137bfbd33585152680ba7a8a414562a0401704e8594f0306020e9f0685e3d7529901241ec5c0ab85978c7a83ef088ffdf341bca436910f1a0fce055b7cde39b8f6c2ca2091055707f606505fe0536bf79a0c628090a78533febacdaab21760297e567c1faf220000e10e2e99f30079c864dddc11aa7df2258ede8a7c4a6787e79799436f77fbdcfd69629294d574f3cff9034fa9edf3bda38615f570a5840f3598732c584d88e2bb2a465a412803edc11d29cea511fb217f0fb154a8e08dd6996a0c083dd0442550348b869206809fe5df92508a7474851ca81a92a46d559d040dd8680c008ac08aaf8dec735110b05264928c52c24fbfc91b2c665316f7e55fd0bc55053aee5dceda0f0e2b120aa0c8058e3ce02454d917063de20573cf9ba48884cd02e6f1794fd090eeaafec974c7a15e1fe9f412b78333acff701f8ec7c277cd8e244b6d4f100750f53608517fc92174e1a2f25e3a3b46c94643d90e8a687ac919a4fd293dfbcd558452fe720c82856627d187f87a2c5dedf56cc1510cdda7939060fe2c66155b63957fb510aa4f681b626c392441d5d4a1b482fcf4021c7dc68d800c4e1c24e9ad1b3b4de3d11390b54d2d5a187b233d9969ef323b3712e48387890033ace1acd43d72ece6212bc3fcad79a5f76d771b218124a65e863a238b385b3f4b5f925dadfc1b9c1867a31f623eb73c282b38c599af4fc9fb2d06cf4be8a27101b62cb24a46fdf6d7b9828890eda25228c1e6b5e01427655176c03e4bea1605bd05c351d79f270a50b794c6779ac7968f4a253656d08ac8d21d5ca401b336ddc6c5fbbcf1456d279ba069a2775293832cc455018a380f9057c12a93514d533fd23061067feb47d571b6eef982a20723c4fd626337cf4ced5fcbc620725085aa8965fb81bfe7a0589ccf93e31233bf39afa83f099fb4c1e79219d3219e2c696b5df3530924dae7f3068fc2a6b539b449ecfceae04c03d014eca253180e2f7f91dfe6f2c8144427955944d9c3fe94828917372d00dedfbf6173298e29632f2c3d6c6faff40cc81bb1f50c069fc77c91cb585cf59885719c290bf7cb2b75be049eb3efd396a95059ca5686eecbaa0064a7165f3c9f45401d32def76106f86085980fe11572d8dccd7473ba49f034347e6ab34d22f02ba4014382c54a145477b9cc1a9ea54d30a004289ad6665bc7e391659fcdf297b167718cc3a5af31928b0bfef88128362cf28edbb13a67913b5029b82ad349eeaefb176301e765bbd46475231c8b921a1aaa072068396da180b8caabfa9f947d6350fb8d3488c99f61030b8a0881d96fde491bf09a0c9737e7a31b2e20625da2166ff045a61b42cb39687fd2877d316b505df7f0807f11dc8118aacb56d6a1af6c90f44228c19c5e761d527aefad365ce8d50d8fa13c9f40457e6718086d3fa4cae121ceb4ac270214463558bcefbc7808d71056309be84e2659cede327d07aa7b59cfac95c438a801f0c921093011a0b4175812bc8d4d5874c8d3e5296de8f9c4deb4c79665d3ab1c952443af6b451950afa9293ed22b89edff93884bc938e72574f66337624b8c27cb46fc00c4f84202b526135c15169c4962dccddb6bbda55bb0b18a079e0c4bb381682c42ba27ab6f11cd999b62b5060db8b3354e5cf84521f2af8c05614fcc0cf91515e91537e587ebef3bb3a0c976b3436ed385a4026de29a16593744503b0a30fbb5add02d851594e6b3e5fc914983eca6a572f51c025361d3e3c20c4ffa26355af4326be333ffbdb0a5322af292887b90ed060e8b052ee70b60f6d826b4c75c89a94a3093fa46f91637dfa81fd7e07822c77f7e88c6885463bd01528a420857e0071e89b2ec1003e21fa931c9ee3f80367faef174c9d9887e1062c5ddd5c6497e2686b258480dbf1f4deb1acdcc02487a340843889111f82e1fa5a58b3ee47aa202f3a614c0771e88e5b503e6b40b913b6d3174b47932b737b349303ce07b62e7b3577386c99c4d2c4076edbe4c459728bd9fe57c21c6eff2aa4e3f76a0ebebe434e89a9e79ed98a6de80236e8134243f1b6e828b0d7a910c20fd50be70b98bffd94278c59fc6d3c5f40b61f2eb7e41c64bb03bd87f520633204d60346338cb11566b98431898ee08155d205ca1550f140e07492ccc8e6813ed10de83730d6891f2374e48227cae408b9db53c4aafed1fd77a54cef20c2777ad39c6c30e4cedafc49088a4a43ef267f8f30afaaae01386c4b7d754f0ad31dd909d34c3da32ce6c6bdfc2522bd72d43cc7f8606c29dbc161d0d0aa025be078585826009b284ff089c73533d9d9f5bc953664a5fcf9568aac7c3e54aa94addbe1e54ead5b6925956be0a77795afe6a7cda311c80569afe424c24b86f456cd29cdb08347551beacec138f824976537908197a959234cfbc09ce1841258d8324fa48c416f26fba42653e2cebf0f6e854721dcdca8b482a18542eb1fca0170cab1257f982702f79cf8ad849238baf659eedc36bb753560a07d11dd5609f0bf98706b7f0e3b13bdb63f0c59406e6dca73ab201a3c7c799790bac98f3dcef77bd53bebb0f2e715cef82988a2834ca1f75b87a10a902cb89d42a6b255943ab542b5c7b37dbb5b2a8795dd0ed662eed0fe0d2b5c09748ff378a9b7a95d5ec538778831ac416fca3c42641e381cc21a6056acd516c86afcbd332b7cdb0d7d22a4cb73ffc2a86df00c38c3be0c641f66f490e93cedc5a3f648e59fec32cfe2abee6cf12de5f9da13912667dd9e323d1c32f1c935d6f856444551fc621acc75eb70ede3bb38ef740e6f228fbdc01c7147966cc379ec436f59f38734a6bc38d316d0cc232aba8be940ebd0af2062ad5f13365ecf2d9ace2c485d9d550085603ee7a802fcb4227aef875b6931a78616210ed38b1ac60387bf7a62a793656302fe86012703e3b0104e91028dc443d144a8cfa4b3ea55bda0a102ac10dbb51395d5748a0c768280684763d0455fc3a8ad81ab3821aaadd4b0b51e75696b0185a7990d780b0a3952429040442e76bf74b583d733a0462b6e2f2302e9cc0f7f782291136147c5de04ae63d346c5d47c900bb97c4e7326d019ad298eff44d7f4d351e875c33889e4885cafaced34ff562787c3004a79a568825ece55113c8d2e742716ea2406167e0e1690a480dc16830a0bc80335b5a1d28cea029a96b253da941787987895a41d88edbaf4e71bf53b52492a52c9df1482b7715b793d5b54ee9752010f4abd71722aa57cbc793113b5e4b1ea4492e47336a18501385c386d70b2e46b425e7b71870f6f4435dca86ecc81e7283faf857b5fadee2a512717fc031d803f65b6dd7a6d9924fe6e3afde8957b156b6e712ebdfb1f9e505a0deffb832394f9580994142fea3e5716a1afb387dcbc802cb9028a2a5063c148374538082c6c92859e15b6ed846cb4dc8206835b0ed5b1c39fa91c2b8814128868452f20e17b4ad8635f796fe1d56efb27c223617838529ac4b3b153998c40b1a083b440dee87812e76eecaa031f40840a33bec2110413be7620882b16607c64cae0d78b8d10f32a18e62750b744152a5ebc768b0f83a2ac85af02e07456240f836a086b9e341f4b3346164f01f1be4858fb37d1463ee1063e9b02768570f0dca05696f839384f4fcb053300822af0795a71dc0912605b00824183b3be9d9e709e53851d8fcbcbbe541cad3d2f915a0fc62a185af17ae84cf9a80e3f36a6cea459cd45264232372bab7e03e5069d024b353a03b3897b483696a4f9ee52336632bd9cc3aa36ade094ba51767aeeab6ec70d5221f82d1a44d453d147d219e30a018a78cb6d569f6b6499b21401385711736adcfb01c6870587b2beae740f0e5f84dd1b986ab6543b073f5b8c338ec3f86a42f5e2ac17fe4c8ea6d1f6efce635dd24ab6e6778b00c08c21b2d6b792a9fabd979448fc8845a97e1cbcaeec40ed5e381aa5cb0706b4df8d1b930b6865e6160361378737cf94122d34212716211228832972a1372e88ad007bcb78f68cb14fc0eae8b3e8315fc32191037e43a45ea3d861f6d930e2ce2e22c6c4547081db0d86885beeed3d2dfca16af4f0efc75818a673af204bc024607c6f701ea2ac5c8b372a3da66c9153925914dfef8b3574f67bc250ab1d0a378d77ff5d36ee2248ac73c8f8260fd0c860ca48301300866ee73211bc65b97bd9e1252090cb6d82c74a318c7ada7d20993a151a0668f5ad3aad626c6db88c0b89a3355e0cf48dacd17402ce55d5e8e5c7e968b0464366e239a8413d33c632279b75df5b1e67e5460f2d313fec646fdb9f70edc056e7739e081fc30efcee0bf15e7e9731db9d6a01e1061e4c7b523eb2a7f3b2ccf662e83b837c45a4119c78feeaa47f4990def4cf5e329f60615be6df4fb4513b234174b0909688d3803003b5ccb7dc6448ce601631e0d01e171eed832f76b61e53826d61f6b4f62b940b8835aa423f3a077b5bee6a20a809291859d5880bc2b80c16917793720eb694d6696e713d5cc1212ed547ca8f361cb08bed9689533f2ec3c8896b4f354e94fb8220e9e837abbe7ca68b8835793b4c069704b222f95f6563fa9e6cce67b79f8c340bc3f7f008a1d359073c2b4123dc4819522b34d6935d1e1cf0959fd2f3799dd899b7a32eae6667fb3a4ce58538c1ad9b2356db6b9f0c862effa84772a9071055984b865c34e1ca7e83f92c0c0740dae0ac5ca47b1aeefb40e0f0abde6011a61739c71bac32f80c62feffdca9c9445ac0d770a7391ce1650562113dcaf71b2a6a232dc0e6f79be84c34ca2b7ffff7f2f9fc86dc51e60aa15a399a21a726db5189d7264cce2ace2839a9330368f4bd82ff22028fa66b4ca286db01f5c6aa9dac79c7ccbb329a4da304e244161bd3b3325c5be82fc41ccbe042c351cd03bcdc8a0a10e1143fa8cc038dc61975266480403ec905b6c0075518585d71e7aa4bf1df2c02d5a10b5dc2211c1ab10119ba1e7f61f0f0535d9ea7130cd2abf40504078d9a3825a42daca66853b08807fa4e41118195d4d7221c5e221618fd392301ee403a6cad55f9201b49c1a3bb2df64314986e0051ed50109af4f4f3e8b5624f0f0d672d0e8ab320cba18588e75286b7762e779c460c69f880ceb395de5d721a6bff4eb42a8eebc7616baa751164b1a54ccca8604cd0294cf1035250f96f44533e6393cc039afb57c2042515dbb85fe655feb0b3fce2b61b755e9e36d5deff76b93d04567f5e6b422887a09bbeaa72fe9ab96002bc7f6ebd00d552028ea772a3b720f6b1e5363d4d56a314cc29c2eca0ea7069367a5c02d15d0b4d46758c3c0b06cf9c297dbb69dbea6cc2e6c0a92af6ff96fbb64de32a0ee67b54dcf464b1bd56ef00f71d384d38c9d329fe128c9d64262eb73170725ca0effa96418e9ba4d157c7b1400212a415b5394a3e2efee419103f5d86bedf4cfb9f578a6ba712a16022a8c0bfd24ec15b9f77230f707a8f0f0c91f14fb7fc0b57dfad59970984de7a7ca629825a1c84981ef3b9d0d66496fff4e83d18772ededabaeb347802fc745ffc2c43ec1945f5d6b756c26db2c7ea1665560a588029581c094ed2daffdee302b2a01d6b255c3c2673d7fd54ac6dbad93263791cc4ddba43b485b358178877ba805f2b6025006b3539498825a34c57312b9861e573a7ce89ca073fd19e8f09f1a2e6ed6f27d5d86cd92cc7b253cebe2932960e8caaa686ce9cc42740257a247852b62f98075d10a08a4a4b58e47888d9690c043de3f6bde383ab07f7bd11006d3286a36c226acd11e1b1906c5f83efd4de1a28823e2b743eb8ab684d6f00449bfc0832efd217c9130fe8429b7a7f44818c43c050e948e575103d1b9f06026c6f0e2df9a8756606490856c8bde69cf726b7370a3378563175ac84a8c382f859f51ce670e3e1b759bc24795e49261e16212b869d733d98285f5bce2bfb89fe2c3a9e0269cb2b67da9d384b68a03628dd7fb27681fc51895d1799919a5cf8b05a6ec53a0bb510df5244ee8851bb6c57be96a600e9c03f5932dd9545ceae12746f2c2be5e70fe99a6537707e12643e889d0309e5a1fa019a79234c23f0d8158e30ca241b86bc37e074554ae880a328ffa8ac9772cd93ea2056effa72fcb8e6f803e858ac394cc1ac57f800169ae9a7d8ebd80f5a904049ec5bfe531fbfe3f90280f603838b05daec2eaae6af01873a4f7bb89a15a5f99e2f1c3e4a9bd826dc24bfd64aa511a95e230d8d543b7da799de659c4a06ac616461664e4e85af6144a9fdfdf822779030de49ed4a7e464916386827e73283ad77fe8d148a47b9dd8f24978c2d05bb7d560319d8b6e137273842500035e18e58f45500efa1f3a7abe8ad29180e08ea76369d53b3e10128f363146c18891fbec5c1ab30fd3d6a29145446c913c9891dbf8f7f0e635761eb9d7471a89c4e6c484ffe6e2764601c24bd9c15f73d70afa20c70cf28a8ecab03c4f052318176eba996180126770d0152485278a1def8a9ad62c7e4f5fcab62269359fc99c0622bf3161ed794efc92e1cf4022a7ea311f3aeff9b675507139e94c1e35d44f4e3da7754f8c4c4fb7a4d3ac3a875e47ea8d21557a7165c637f2b319b33751e97e16a70add02a300ee345a0b5ec4640d17e9a3c2489e599338170e746a3805053082e81d4398501ce5423b10b6ff885126feadbe44d81aafea4447a5fa774a202c150da3586d2bc10399aa1941ab1f17e8e33296433a1fc1b002eb98e659e2971dfd5ff4edbf9d9e879118a960e9d68aaa6ed65a0220f9d53bc855dc50d1bbef9daa4d1bbb09e81b15c5cedb6749fb396db6d5529f935f883c67fa0f4a1e0dca1dd21689ef8649c7d34bfba48816c719b00ce34788aec6583cca60d6f6ee7d562a38f773f5062f89985ba0b92279b248b1d36a94466d37f1b3d77e466e8bab9c0b62a84819ecfa5529c0f4c5dd3823cbfce02651990ec43b930c779d7b4c36e6786429f8c372533d12e5605ba077c95a47c0d56905344a8162899de18f30196434be8b1068f5f90afffff1d4b4eb31333aff4474b586b80c802acd15a43fbe0f9cac95568eac44dba55279aaee1c5a3a483b58f8dcd9372e3bb7915ae75bfbf92acf7f8aa92baff97e74795148d99dcbfa8e180b8aa2919694ec63e39c3b07ff98a45c5ce696a387a8b4e2284172f3f0a96a5ddb1572bf1406248716df27edc4068f3e6b468f7c7f11c2b63a693a7ed6f28436975a07b485634042f8db50c3d0a0f46a3f719ad7462260c9bf42ff182c38bb06962c4cf7cfe9f697919238d9977d8e01549865627e9ba600c3aff16161390de872e94d02d5ad5e92ad76be30ee9029d7ff177bedf855c959f7b3f51dbc568173b93e461fbb0cefe409c6a9a1a545262c462abc8add129cb92152b7d434064506438d42beb790b7bddd829db28d385853abcce1dde8eb3cb2279fdc910f019b65117735a878e8bd412ddeaa82cf9d42dec20242dc3cbaa64e6e812c6278d540f2b9d16b3b81e7f0277e3cc7a0cf95931821089518f56f4723e8ae8d29eb79855e0eeb484522f41e23efaab13cbe09621011062a7c7d5154b5ff03131a8c91621f478d3265a9082e0d07014267a0f02d9f9fc80555a1abb6752c0ec96865a40e05c0b489ef07697ac0dd9604c05c30e151b05d12bd2a0c1e1ac36aced0b286da1bcf4fbf5e5abc867619ba5c2d7a4293a529670802dff444f6822d6ff3de81dc576530a69354ef58510813e308ee02604ca470c228282ff2b2554af1b5da533c642f2ef76403488089c539602b07726bd45a6166b2a8500c0fd74ee26c1083723eb8bf4df4d5ab483e8f3435e75721b163c8146faf88afad07f2b348e334898fdea807a0919c1a4bd21d8583aa7e48be0e0c5523e931eb8c1d3e5842714c963181fa5658a2de73d7f0710240d031e07975ae4ea1165193cbaffb6210144fde14488f32c7e559412e2969e99651e29c7bbfcf262fc3997c4f479081c3730a0d135401e78c222b3f8bee160a0a0d0a011c2f0164b224ba43ab1908462745d39edfac97d2ec4036f6c0a0d2a8e952019f9d0ac7e9c9be695b2ea2df6bb7b36419afb1de8e5cceb2c98dbe27b85915b2cc5b40ec3d03c181832467a103cf32ad5a4a1db4ef0eaa584153fc276a6a992d1765e011aea880f700fc3948f10a20f320181cb6c103d79c34e93e65fa4920bc9be7032f99344b13a02aa8c1976377ee1b847bcf3a9524e137697dc78793b0aeed6410087b1f24d5e10572ccff7aedd0adb60e9b042baaab1ef8657865962f28958691a8ff422b6ba9a03791e1db10e4cda3d17eec4d62b56e02f56e1411a4614860c9c937e8d5555ac10698408041be2803a8592a7d38c56a1f7643f34e75d899d2a0a9817ccd81dd813a77e31b58e5755fc4332a201b0aa5ff2ef9289476018c331f165f2d4c0a9347e238048bc1e9ad37f21fb42c3eb351f3ab6d8392750b5237c7c0fbe218671d045acc6f23ab4a35e25f0f8bf8da5d0bd22855513a155ac29a8a0302d22e35ae2d64918ed878862c294109d1abf2638e255a1ed3b3deb10464a4eaedad250d3d130a9adde6f83bda0dc2a23b2a9c007ab7aa03f75983c7f6eb9c4d6ecc4162ebd058d442452ad8bc49016991ad4338056623a7ee5198d0e54bc5adc04557eaf5d5c730e162e37722fd385a7f4793bf159600ccdff9a29837b0356c5a542ca5560e37eb6fb4ece57d1b64729b3dc95088ee3068315bde7eb4724c7bca0c0f11c5bb74870ae635185045141728bf708a0b75ed7d33dfd46c1893fd68b613891f687388bdd544516c91f7c1a445664687b6bb3c8a44bccc7f8c33a1b87e4a26299bf3dd17bb5377d8ab27fad394b7d46c5e50050ab25d363a08ac0225bfb017c45cb1a0011c6bd55e43f7ae25dc9b6864cb9514e15d274ee5b592cfa0f890c772a3702ae09221513423cdcfdf1b9de8d1a44d3e87c8440674f8a61b41a6b308e89211a3424c406fa557c2a494874032c17cd88e4db74f6e28cecd722e498446474bbad9d27b7f54776e3761ab10dbc327bde78366baa0d1db5d39e872190f627d65ffb632dc9fad0e50adb99ac7f9998da0aecb59a7b046aff7a38bdd72fbb4bc7bced8666d4e86d4b6b8dbc40c0a8b2cce98b97a5ae9f41f4b3b6c3308674f890ff0ba8a7c99619b1af214ecb373bab204f551807b9c3c5fc3a528a6e3a77add5e5b3bea0611bab37f96a778bc5b24e848a6f367db1ffeb7fdcc36ea23ae000f86928788cccf82706c6dd920b129182562c8dbd46313d255c266bb537af4b1b768e0662cb93f483516a8429843767f73748873bbb86bc144ad30a81edb91eeb9b86beacfa1008cc25c0e1b13b0464164c2ea368561489c2421e51a80feb2506ef2f36fc390bd034c06d57c167ff421dc18d338de898ede2a7ea87590f755ffc5d93665f69d4579152f0c23f82539aec82501bed1e49871daf8f11d99eb22b73497f2633b78d3155e5bc151330520f15bdcea1e7f0944e77cf4281ac8d110cca29273b3dd0b436194e179eac5c60016008ffde897f1f27d91f08187ea6259ec160f0865d306b3e4b0617a35f4f5c787ecf3b9f50d47b2898d270579c99174442800630839503b87cc487ad5368d0642a1242eaedeea504bda4a45b35208174d3185f8d8dae1c2ebce4f8135c73fb37123a30bf9aaecca1b7e86747c1739b7d5ba28b37bb1907f7eb803b24e43fae983647be677ce18cbd5b693a4cd85217695f47f0fa70d0c27dcff03e4d681a250825c71aab1d88ee7e9b1205488d563406bd7c0c172397135cb5627176c0a26f78d091da779f90c88e782845b6526615ad3fbec982e5f18baa2d835a48984c22e3af8730afec8f461f57217c9e3518a21e038508f068e48b4cb7a5df5568fff052e4770e139e9eb60a19b62f5009e1f0c18a54f3b2fe3aaa274e767b0cbaee6e9800767d82de563d1a135649bd0a1fcbdc5ac89a4f72f44951a9bedca38ada187118b31957188948b32d8e0237d8a1a4a656bdb7fcebfb927d75b7bd68dd2bf371e4fa3147a381f2854138e8e4867080b542fb3d3dace18efb929a32c1d0bff1d5269363ba695a8345062bad65114359e45fc49ce5fa67fa2dc4903af573e076d8046dbf004704203cfc434ec99e434203daab8a49af1ce43f19adec01425c741913f5aeba5cc7d369a27b168cbecc329b38dc48b2de5bf8f6140fc1582efd0e19b0f3538dfad580156d458c175f31201380c55288b301dc313023baa585f8ca149e9537fd5b599ea9985807d291802f4eb48f6df92142cb8ed81df9849465fc65ea30a6b3d7593a537320a4152425ae80bafae36811bd793df55ced6ad58a8c43440058920fbd9bfc7297872637a0bf5de31a213d91cc68e41eecc5ffcb95d3a89012546c8f01880d4906d56310ad2b4720980735c2f1bc85d320cf0939177cb60d539f23517669c378a6280cf5dd9c25e983736d0fdc9482f68b9eb72e9c4cba3f5aa36000c7436a2a04bfe882c4a4c836c0223c384fe678c429875022cc05756b9de9e882ed255006ef1d4d24012bc716951fdafa3147cd31d9e9ca72949420e675da20a7da2ae0108e8ef21229b0371a1a4cfb2c4965f0ef574382eeb0c40536696a960a1588daa65daa9c034ad8eed7e1ebb37dec045c744c70549cbf95cb722968f9d164afc140573840f02435b35ed82e62d45ee7e9fec567ad7ad368f3d204ca1d60d8121622b8a35bc7fb5f619a0adda306b5d91e47a91be2957b194ed9fc99c23e3812d30ba99995f53d2044670fcfdd2cc31e614cee1e6ecb10ca59fd7f30489d37a5da80b42b39a4ee2dde91f428091d660d0bd7cf01a0dddb2ddaebb9bca842659c706bd3c9c9b012dc36e270e9f6eedb38402b9149ea129619c8e23343a0f28c814d880013d5396d87a242beacac0d1b6efde9973f431ccf52cd0b2ed3ad6eaceb2146bfcea1e2e601bb3e1aa29cac84660325193df0b0f27861d9d5f545fa899239d0b0f757297adee7b9d8f4e8d98377746481006421ccbbde87b46dd049024107ad613774097c9f6643e90c69b3fb942d24f214ed258a2a1702fda37cb9418b49918b58d80a9cdd18615625d1b5156bc4210da53e0aa511ea9130a0f4b881061c12ce61eb17b38361f82ee1641c58bf3d59b2710fddffc21026611b32528b196c686e4f6db72ef4f80e4306b4c7758246e878fa8aff39bf7f61013a680690e85e997f4eaee12240f48826b39e431933342b900a74d46390ffadc4782718d2ce519b20c817cd56c450e2391bdd2dc02ce972da75d67f79b0d7a318f8479c7175386f041ad5d724ff641fc7867f20bd2be993905074861fd3a98b4b7c2ad4b648cd9eba8aa4d3e853b6830ff5cf8cbd593905771e35adb63f5ab46816f633afdb0ad99e7913b4af4eee974e140c8e819de74ed7752687876680a37455e36ef74065328741a27383c82192cb36a0b657fc01a0bf6104bd737020ac8662edb42b6dc223faa50bfa5a2108f86b063e331ed91cb05ded6ea3805a945e59cdd103749dbe1a161ee0a55168cb631340722957d5b8bf30165a51fb5346ed3770efe354b73a61ef25b4fb7b93dae2c8dfb4b5cd34250f733a746e6bc5b6a70ca04097fa39d5fa02a37a6d6cf5345228ce5aa1b9fe794db1c93837df81b0700e8c235a9820bb1e6c870409cf54e4168ed70faf7f5c113374cffaccc1d645bb2db84726298942439d271fb6febae20db1d2d1828e038a5137b61463ed64468ab433c4cda10d544ed9c399b7c37aac2adad3c07d9004cf986a3842b7d430d3d2f4e3fb3d25b5212a946c96eeaa35780376bd3cf6c589eaf2d59fb65cbfd32f17a9652ca42162e337448ee51b5d6ca7b1929b4fb8df40835bdc5c9c1ab5a0be2b2e5f64a6154cc9d762c49140be2269d544c41427c252b9edf5f716f7dfeb9b484fb1ecd518d0223f0022745269cda60900e4beaed49990dd0e9ce195fa973891e221a0f52c27fa9c91a47766934bc68b8b2a10120c71650bb64d8c00bdc78fed143ccad3ad9457779fc5e102d3548fea9ceb990f520c45398ec3ee5ccd6193719b31a7e163cf5460d37c1826b042b48c80dafb80996bbf44ffeb21e23710a2daeb631cdfcd6a026d8b66bc8358b855c16b02daae2d8dac14ad448dc277a3ae25b5625785b5e6b23f10466f18a35e9bd610b76b1a0bc3db4d3f21d7d2bc18233cbf8a1158a68edeb9f764a85b736c45b491272a20b8353398a2299356f9b656b2cf0832c9f52a22e4f5938fea061e4122d07616031cf1708390685660fba08e718cfa7d53aa13a939108b320b83bea111df0a1b91d8f2682ad8c74bd464438cf781f89a0b9d677811d2edf9664039e3de59b7143a05e250485b6e27314bdeccb0b05f42b4ff87c6c6c5101cadf43c8c6e09d8dd8255e31c9173646a66684da1cc9b0a000400c0da6f6281b9886f9eb4a10cd4aedfece252aafe4e609d5eb16bd8caa102f46378a1655236658e9a20e8e11af8e3549d16ba9f06ac841752cc9524cf822ec316957c670e2afb7e37a23ed2a1baa7372780282be06c5d6aa3e101e46329bdbbe648df822f8564998173e0cd5e02bb8624a4ec340e539e052694928e84473bfa3d336a1f3fd1cfc21b38eecf7574c4f043e0d1f14ba5a6605e3f65d0fb84814d7f249fbf91ec0abbe643b96abb307df4b56774ab8d846fb2e31aa50be1e8d916bc099fa289bbb0d9132df9ffc1da2a98b1e61a2ad2d4fd458f6342661fad75901be21b073c48500a99f6a25662c267e4cf8916bcfd03622be6602a82f5043d8a7c857fe67a28c0d1884d65354307df2907b064c5073c377bb808cc4c3e2e2e615c2c18ec5ec330069c58adb2d56f0704dbbfd2c8ee30aa1f0c61db69589f7f1e3929baf5baea3d775f600ae84e2e07c70dfab87bb20f4bca9364e6bdd3ea3d363a8c15582207c8591c2c9424f1fa07c910030e5b4bf826b1d4027a4156b0245e413376c1c5083cb05384ccfe7d91cca8414fa651cded3aa4bc0dab07e7a6ffa944e1a0b25840679e4f6f85857b9cb1c9844427113d62b7a8a59c9f2cd8601612d6f11eb24af306749ad5394428930044c64e4e817eb7dd48d218532365e707e70af84f99681c00ddc043adea8c0d90193e0d0c921ec5608515e0eaa8d7426fcb53332c4401bfa6ba17d0a47eee3b8f0db9b7630786ac90a444d3b71fa6dbf451888d5503d7571cd1f0b937383b80128d724850b60ee46af4002f72aeddace5850859b20a79394ccb388ef06cbd7746c6edaccd5215743979b5888f2ff97cca11ad7048db5d949e826d8a02728e3c51ef6837c491cb0c8dc6d43006e0d1a6a5c38bceb7a69d60cb741d73f55d5b2ddd5339983575f51092b0d59653111ad48a603b885f6f2c0735ec2a4664bf4786c55617659d8a78059ce600f3d7e3d36e7791e667910ccdee62992fa8ab2d497aa259ab3bc9745da61266c7e82f1d9b24ff0cd172d5b811422eab668f23871f24073ac9a5e73802c39d293b13557c341066a0338d8ce4ccd57c4979b9a39cd12c6e57a2b1c551f30233eb407e8df9255c7d612854a8076d334d84434e2bf255902094999f64539ab8ad0a32eb35172d92c708a8f90b12d61b3a0aab9faaa267d09d9e2e15ed253060d7631050f3e1932ed83fc2b3c0c436ab7e3f6ea6984159d3d4f2017d6163e82a2108eae8870cbf208bb710737bab0097c4c817a33749d87abd758fa2a175ac724d816d68f5df2dd8e8187044e93ae16f36f1a5502aae9450263e01c648ce26b9840184dcaa569107abb4a665d50ffa3cea042ab365134750c04d42a0657f0f9c7e8ab8bb32bd21b6049f849e54f1e0145d02e999c893db3e5572f8acb46daef587422b87763bee2a5b25131b106fc68fb313b76107da86b0d767b9303d293c6de682afc7c3abc2d8838d33ca9254763dc7ead576134c194455db5f3295b716f580b7ff22e2185b24d3f7779c537f996f24b0011db8d8aedf334f19b25280e9fa71197288764777b98c613ed1add4423da43f344f3aa52e71a1545751677186ce3d0434c66d7deea5d668fc34053139f2e6f89a13eaad9c6b3fd0cda650cabea25f20d5a0f26ea6905ae30fd65f1e4a69755a500cca2d80f324cd441011e5a5351d82e057dcf4095dd21c6eb4c279950c6391b35f08fcbdc17ec4554a741460cf3ff932c443b09fbb270fe90c8e558c7ee72ac6fdd06fcea8495fddf78af9a3398618cea73fb9d2a0e4900e9d23d30ae3bb409949cefb25ccddc8416e29e597ed90fba85b45e1ed17d00fb01e188b543b071a08b1eaf232aef8f4f635e21e8a22921773256270bcf8de2d37df4e364ddf09349caeb7b69a41d02485478f45abe8952e463db17dd20721564e6b117bf0a5ba8f91787db1d4bbd499c7a987e243933488ea8116dd57162336511f5b56864fc1a6f82c705147533de16fabed8d8814eea4343b57225c51ce39934e260496428b09e60b0d6f7cf5516650178cfad05b24e811df095ca664711a57044acc38cdac2cd6cc26118c26006cd12e3a4eaf934c2677b18c9f94e72df34faf4990f4ce5c265cee6ed2b5e8056dcbdbd35016fc1484388ee95267d762430f762ce5c82f9ef9e09856f03c8f72a4138fd4616bbffe6ea733536882171ef7f85806c219e35aa14a40b7707c8fa2488b16470a4a07ae99d2b4a3d199e9c8673993e03553dac294759c6b8d6f42dc51e387450ef7ea9c83e0e46a9a000410dfd9e7464fb6236edf1d48c7997d16beb1bc3585aeb59a275b6e5ce51e1e974ffb4bbe241ffbceaf66e18763941c673cc1f42c41557d1ed2232b900af3175dccfbed3675fc51a0459d7703f64a6be74b3c56d092125fc2facf47992ea89fa0976aaf8ee359af3a6ea2b08403aed869b19bd7a053b2180b5ee0a9e8d0940a5601d453e7636a97a6e920d95b8996b69573a583ebe42110a8e0892871313d841faf6ccfc6bf2814c571fc8a57983870699d5b4124e759bec98ea775ae78e2ee856819c6f8b225648886c84903fd1875d8568579548cd064554041bb8345b02b90bab6357cefac92dc1cb9a2bd7a4ccc3e7cb062080247463cbd1a10d8e218cebcea6251fd4760805cbcf87d1aeb507b2d7e8174a55e07dc8fd669406935ba1443a4b55594363e9edad32a5102a5bf7d4b845d42b871d512f1fe051da009447488b31a06438c64538ba1c3aaccf843fd8d3b7921f23736a2a3285d4606da884c9413720fff7e8770eb2bde0e4d8d44095fa003a42242061ff897f0893692b51a26127945429b6f4bbb5688b82218ac48768055fb6059224baf7764fd4ab42926bd00e10fa1a9ae07e30533e0c106ef1cea046bb430c9910f0659ecc65379757bbf6d5fddd40d6bdc3965c450958d33fea3771712aaf9c8b81f8698e349d32b022116b8db661b151c4aa4641f5927cfae5b985d4c34c5b9345534eeeaccac8533edd82ebfb1c7923141507f8333ab8f69221bc0037899ecfe53419c5403f5b323ed9d6d83608b9f8f4ff45bd49133547509bade3a0e7816a32f95d0f243726fc8ad66f7295bad638355a28f64451281e8fe71d7165c5621685fc60998509bca9e53bbd6ea062facb9576374c9ab246b10dc599fc5836190236d649e7aecc23887d450c64fe23deb2181ca88fc7e08ff5b3f2f100d517c801c32b44382d393c03895a4001e9d44e72a1f089656a8f345619d74acf6805843c34222a215060fa12b8ac609493724ead35b01068d3e33d0acb8c69e2d956845960bc1480177d72300d4ea44130ee064594d6f4a9da8eee9cfbc9d73e724c60bba726b76612b80c579918437b56821ad8389d90e5136e4af71d7c413ceb15d8eebb768632a4e1a62e016a205c391aa2250df0c0ae2db96b223839f62c020d99b5f6fbf452f096b11e7dd3a1cb50c8e4b9521a1fb6224350a755ef71e8f1e625affbb52d86091d5af9a09432547b1090e8012d6f258ee222fb60a625c57b7853a4a5427e00fc3fb545e2a5d8ea8e77b171cdd83b0e9ebe1baf89bb06169dee8f683b6e49e3dfaaa3f34188617d987b39bfe57d826a2d06e31c9560811eb03a5fed353fb94198d2680221bf91ffbb9d659ba8fe57d65d37a0ab1face49eaddc8a605ba649d3d0fa784f3953c44f0cdd88d588caee355f29d3ca63d0532ca85d21229d22c3951c72835dac61c752a4bf9738b4de9b965d8a72f964dbd9af8b3ae15e702a7011e98f29c1ef2e30cc294f1866583265d6055e177ffbda00c05106d3c65862a0b8d5a06d242c9c9131df17301ef8a0c5ec52a6cb2c02aa34d530d86cca2c8191932e15093b0df9574ce59cdc63f8d02aa2630f17092cf16d7b675e52c23dc6efe873ea083bf84ac59b58fa0773b1a4dee684928d1309a510f9140975c6045f5b5ddcfc0d1de96d9e9fc391cecba9ebb55467a2128fe390475cb5f0abadf4660404f5d0a2252d7d82499511b3291e7a7836f6411dc894aa98d5836af994bd734cbfc7492e24b02f98ad29177347a3bc8ba7e9e37bc7f974af514193df45b0a8ff2e71c62a039b8798885efd2e3cbd5b065b3bb6465783c849e05ab8377f902a78c52f2fc855171462c4146b0bfe09d4e46f816511e0160f3883ff371c65ffc9648933774919a74beb7b16c107a3b44d24dc6206b16c539a37c269ae95f3c8726aa19d899e40eb6f6c180252786f8f2311cac8efb275405b3de4094d1e6dcaa4a649f08e0bbbe59ef0f6a15096284ba7d3d1b318dd0fd7674c09838ac68e230d0c47c7509a869148612a79f40063631b250a5130a19be762559cdeafd94a9f40a2484793347b6721f240f9db1e4c3a6c0da408f06f67d1e5bf301ac91d6aa16471c4967c86c0b44594a1bd52788a09197e6ebf729fcd37668cdfe60550c371cb59f0da53911cb05ae18cf6385a17e58c78e1bf3f6376072fb7f4ebe61bc65f798e61c40b6de5297fad0a5c3c84524021ce14030340308c1c049c8c39d125ae16ddaa55947370d7e9913c262664fe39d1fd99b2a37066128100c529dcfb7e1edaaad4b09f1b18c2448425209e596b25f72cf3db230ce9a77071c469ab476aa2af6b097ee00d6a2d7809e62376a7e5e229cbb286144595fcf832f6ca92cb2e00b45a00a6bffdf799f2cc65e3ab93472c63817bf9a0368fc2b0d42ba2e0e007f19ab0da2cd083eadc62e51b915699e4deba81c2e89fb52c94ec40ee50adeec7cd000b761cc935b2996b280e0c7a444b2128fa4cf868289263fcb8adb26daea5867c9b93ebfc8194fe2a59afcd06e69ae7c93e29231efd39f7a62b820e6eb8583aff3c7981b39e8fafcc0e74710860f2d28d47dc1327e81a2a71f939edd809a7e7f61e16a420a9c7f5602771d45af3027011f6a0e36501fbe9f5800c78539879e73930dcf391bb81c993f6f5ea31733fbc7a3f2fb8ffeedca7ddf3608fe117dc5037999154482c0e0857ff84e2ba86f89c4f2900e0a7dcadae595c5652a292cf85d4839eb720d65fd7f3433f9c6ccf5f08da8a380d40fb143234cb4ae8df9a47b9481ceb1422d62ac518f34385a2ba6ce9bc7e90cf082b13df6ce73d56833f20fe0a49ceed8b237b71b307861da1b39f8221f46dcc041146477c9dec57609da7592cbb9a8ef3405a35d5cff42eea30bb16bc0d28d79dbbd2f622e49eb54dc53bdb8e6f0777daf41f9b67bd7d7ce45794f98fe31c76983561594a58b5e3cc9681f523d3e4dcaf256eb89da8f27a425af420f3361a5a12cff422374385069e2f1ea3d61145c3446aa6203c248d1b975753ce8c94a950567de9389724730c3ddcb28dde2264afa166c8d05ba2f5414eb57e241045835b8ef1043c2582d6aef970d99037250c858aeb3df2a1cbb2b3293ebd970147e9cba6c10dfe6d6680a03285a35ad0ec2ae6c9e2d2a22293b821c5ae6780c898c9bf3e21b6d263f03b2044ad1411baed524ae539c2ffa46e179816baa912a4e2180398c1752002afe923341e2e5ed807db5f6279ccb7a15fae30441e670bd3f5e6977c2f63204881468fed91ecce2218ba08dac89e2205de38b5e76c4c502f8154e52c30966712a644ebe8a7db81f2aef9de84290576ad18042f48e728b13b3b71060813435746c16446cbd904f9347426902485fc21e93a1e1cb9dd79047ace9dd7904faf1fa1722da16b3c6305c0d6cf14ae7e2d36e102c6d86529fed6307e304b8136e3e926dfe7aba148882c2d3b93681b6f143b8eec6b9512ea674d6f30ac1bd3853ea1459a337fe7571bf1ee51b04427f12480b22b0af86a5151f1cc1695a9fe03362f0d8372770082b38380dd68afc0b8322ac4d3d00028c3303da0106695e4050b2710e2543b2d0bda5696e1c7b7fcdefde39187aafb4acc8dc3877039e51a3f78574bfd23ce76ac561da0e7804d88349ffe781da1b361fd0798146d8ede60a48702be40634e381333c776de2cb72173181f6d24d44844829a59452ca6e0677066006b29c735655b29220185725f6ae6f7d20d5f79a31f5459825500d73dfcdcd40271508a40282ef6fca54fad912b743a457ceca075a8154948708280fd04d0d27201008e402a9fe3bd51d45be14e591bffed7c30bcc96a6a81393cd7ad9ebbfb86ce9e5d5ebbfac6c89db2467f597964bec05654ba09385a932797cd4c5cacbc9a59c32fbb9aaa77a75090304ebf5c31317ad703b24a77e7f888bacd7f7ecd451ed1804a9402217f011478902a5d79781523110aa898a5ff835e212cae45e5326664b9dfe19059df64fdbb5909b3dc16d69c6fe82405c28eab6aeab236e06524d983a8254fbdbdcacd79711305c7d04911c1066c90bb997b51c47721274e25e2fee15f238d5519fbb808fb844d912c755d935740ed5ddc0a1b8d9898fb804a9b899aa571be21909a402a9dec532444b0bc678a792b6b4372637b984e34eb2cc2a7e71fabbcf25a080626b4a526bdf0b3e6292dc9b2469f40b43828f9824c91658be626f1c3be0e363d89783ebb88dd3388dd3b88edb42249da6e2ba11b47c3ee0355beebbbd3b0eb76c690b5b16635675824c825f5c2f1573ee6cce298f8aeaf6a58f3ce75c472c68ac15e86b346370e361acb50962ac62a870894b5ce272c75acd47245c9248a3d1c779a937c7bf09041cac9440b411e222b30265038a0fa212ee858c68708506a2128d734e5d29ebc125cb0b9f7089cb19f0e7f3362cff096d3e1f7a1bfea0e7b6cec3f9e68b3d1eb779b88ef37061088401e7304b96243fd0f2e1081c2564980a694bdb8709535f496b333942895ee21b1ce2992c17c125c7a536759851b4d6eaf1e012972b1c586d4fede7d6e6dc94eeff1b3ece98e92b9553dff45e4aefa564ec85d86ab55aad56ab952d9148a3d1f7516e4610ee8a445a913967fa03856191e88aae44347b73b129745647f5456535650a30636a49863e316340799848d01b4c56f838632f0c11f76b5fb35f0b6d94d8b711d1d79edab086b311d1df4f439a6e883853abe65a65ad77cdb59381172994fe705cc11f50f342a9031ef4bd3f9df79348a46aadd5f9b19db0fc655b6b298e2ef8fd4a6dced4d2640decfd7abd5eafd7ebf57abd4044706a711f473813631a3af0dbc7518912a930259b0a337960200333a6fe88a3477e98edfdfe12ba46a9a8a67baefbadc119b8972d6dfd7abdeaeb6eee4d908a1c17da681e9052035108be222ab13f0409f733d513ce5898eac3c2d0fcf082bbb48ebdeaa846a93022f8585ff555a3c02cbd4c38fe994a030a1feb4bc7ea8ee9c4ab895e2fe56143c38c41f1b1beeacb86d56bc09c7257f75e9cd76ba7b4fab1e223f3c1d138c2ab0ffee2af39f241f3e37cadf0494d99033c41453f827bd89005fb34e10c7d80393ea87df2824e4f9d5b00a6e1421f2b3e435088ede3351a519bb3cdd9c767b6dd5481693db22e7dcae0881abadd5b6bad1a8d70928eeb4018e7ce93c331c6dce6f6deba4efaafad200b92247768687ae094356330c61e4a5a1811cd1091dc5e449f8a229aed450e1039eea9c8fdb430a33d4ed96761663e1389a22892891f5800861826200b8346d3eebd2b6d07e317c0d7a13c4c003de0f7eb9390cc5882cfc4ac1353ecb71104af3fb73e5f4facfa9c75d799f5a5f4aae6f3f022fb4e95de9cf357a38a9cb3567d7ed87baa3ed50e89341afdbf2ab403bf3376ab5eb5555b657380140c72441b2100e0a289c8bd901282b881d87d0eed2ff143a70b9183083e6ed52cbf8e050fa2a4860bc2bdd702f7de911fc8cd7d90cf7b3e88f7dcdf9b03ef442d7c3edccf4f588e38b403a7e1b899c420a46f17b621b6368649e79c938e8fb3dd7beba76f29a5337bc6219b669252aaf5cc1e3bee9a59ddd547dbfad013f71e17dae01f482145104ea2e7eb166b8a1a913263eabea9230f7c2a7caf5475b4a7d8854ae80f4eab2c20d8629b8ed57666cedb96701113a63a7945d9ad1963855340c8a9d3cc654aafc8bd159f8561a3dfe6ac2286d9d23fec31ec31ecff7feb3c0cebb81089b7a96ebaff2dc655c139a5744785984ae5517d6253060177d2cc92e7ad09ddbe66799026fefc09b33477e0a7aa9f25fe36d62f48c3e98615befd38cb0f4855fa555bef9efbd951d516965aa5ca39e319da77c0e78b6a9ef280441158ac4ea2e7f3221b25359ff7bcc8859f914310465627d113da28195da081c44ad4800188b84421d60698e87909b474249e30c8ff27e416c6da0013f97f7ee425e021c9c2d437001197189266a60cb517637cb36ac25d3a96bb16abba7592a4df54b7d74ebb029d163cefacd67a98b882f16b49ced7d6244c19fd14a56b933a7abdb2a516cb32895df8256bbd5e2f9c33c618df9a730c2a2f6e9361c6542e9461b665a855065b5a408de2c6111ca7b417ca607338ed146ae4a5fa66216de26aa55a796bbdb5d65ad75d77ccc7ded6dabd6bb438c7c48f3fd2930f2070eaa9306526a594ea6d6f2aaa430454c70d183a5b8c1298533a10fc3100995380604b9330a153ccdab61f6b689b8744fb0d446d517b90887b41708a2022b8a5b403bde7b6d99c278c5b0f7a7d1b48e29c258c1b17bdfe96c544a1be9ddb4eafa5adb45b17dc89032b01a0d082e438706b6d2e0bc381a22edc5a22aa81222fdc5ca1887e17c67add64221ab650c5edb77083859bcef5826f30d4146e7fdcb6e02387faa8d0af00a500e854d4e98778e0a7cf324b1bccc24c1ef9a91752a8175326ccec2674aa03ff5661eab326877a129c2287b2a50db6c150a80accc001a063047bfd0d56b120c006cba193c10b6c118d4602ea02bbdf7003a9c8e16bb1f520d142d587e2507cd5e5e05331bef77a3e39bf60eba78fefe5b6eef5add74e3d776c1f0bb761cff3eef5ba26ae27b1ff4dc59a4c9b56ca7dcd87e3be0b6d8278de7b2e14599b204a8274cffde7f343c4ad2f95711864af1828e0e394793e08f79e17d9af11d9b085eeb977a10b5df0845e68c218df2b2b81d4110c5998baa7e1be0bb911bead77d5b6d6b66ddb9a705728a551f6de39e79c338d1cb8ae828ff4f4b53e87168ff3c294b154d340b1eff4027c7532bedaea93b5d66a71a7a8a6fb2122b72dccd54104af4f02d7755fc555529a730e830593226301ec46d1efacac14a7ee18294ec7357814a05726a51d6863057ca4373836072b4c4b041f29496feae8e6e488828f14a7dfa73722fb99843abad7c78cb97fefbdd746109cbeeed32853a6847edf842897041a6c69f4d7da6df58d2f46f7f7e6d7de4b737cdcbf7f9fbe66cca52cb28e48e8f7060fa860c1408683042540c90226720cb09fe92743087d83073eea0efaba31a397c2ee8773074e420ab413d3b134240517fdf8edb720a7dfd76a7dad4fbffd3e272b950d20b8fd69ad9d71c46a872a7c00a230d594994974eb5514acc409e2fde78328c11fa47bcfdbe7333e9bdd3e7370b052ea130a0e4c3a85d9129fcdba83cf668cfd1856f0d793cf711c473291485be74de3a00d3dff9a10e72c0f7a9b5028e27e8aa1bb59401eda7cc661efa93f7e8d1c9efb8c5105af3894d474ef11dd50061a2c8cb50f835deb791a85474575280cc78a5350ca734586e5d70e577338ee399c76bcf79e55262e351c052e43b09cea99cc24669138164bd56e73328bc4e1e494c942cc981ccc524e4261ea17915580c923f8ec5807c34ef8007f0c736196b5b85547ad2f6f3dc05e37472d696fea810e3d1085e82f6a40976841e1de7b6fad57036554983a02b1cc98fa9513e13cf0e813c6acbceed414c698086a1ac14793b534654a51157cdff77da9944ee2743aed4f1f778e274ea5df45f8874b607c02284971680d138c180e7dccd4d1bd57ebd3495b792eaff57ddfcf19709ab37d195470910274b7af754f06159ceb758eba0db7d60df8f8b5fa6bca1c993b30933aaaaa16cc9b8e5d0025e37064cadc1f751118d730472d20d2e9d714745a030eba087c9a31144371010afa688fe0605533e6ca27b3e2e3d6bad2efd6eaf5b7d694b13fa3e7f0b2ec69a4245ce1e3f7792eed82af06f78123307b0d1d4eb8168a36ac8b58f8c03afd0facd3701b475df6bad8d345b7ae8ab7ed0bd80eed426ead76813e6ce7f9f9ddcfaef3849c63249eafc13f3d8bc4f31d88daa2e7b90f38bfd0000f34a6830d5aa287ab5cd0a0a436283103d5dd49cd6086bb0f38bd88e2042b93290db874f701a910ad28b0ed44075e30d3a2a48a406d43c8410717d77dc07985092350a207bc8185c875575c29affb8073c6450a60a2c7e3f13a0ff7d097a9c19397e801c314885c8e1b6ee4bd1f268f11f570c8eaad7e1b3d2fedb225ef31a69e0d3d2e8f931953733cd2c3f1569487f78307c40d9dbeb54bbb2c8c28ff74b5a89e98258d3daf551f712cd672534bd948c552fae87931e9a30ccfcbc70a13a6fe579aae510b55ecc219ed02a2ebba2ed4ae281ed8ab8e3c76ceedd1ae7b3d074cb1cb3a679d73ce9d7669570c180fc1e7b33fb3ced84f67ce0238317d4c4ff887c3cf5aab62b23f860e38beb02672be59c5b40c40a05a6b256de9bf7e7d52866631636a896a419fe69c6d8d25dfc929836753664e51492a9bf1b1929ddc17a6020da0a24f461dd5ef614330d4a9ac92a48cca2a496595ac64256bceac24a5426754564701d65eead97d83a3f0973d5f5e70f8aca34de9d3a7af71dcd6b90796158b6ac6c5bd30f3c36ecfd92eb403e77e84b83eced8de5bd3ca59d7a68b6f9b89c4144290816823643231022044ef85dc88411044cf9756cad03d6d5e96b46575e86fda80fcdb7319a4303b85cfbeb7cf332d689197f77451dba6b79f9e8805fddaaba0c3b96d4336303fa5af3afa3cf00a8008e7c2914661a54c992a6629fb50d256495b256d95b6cc3761f091aa349a1932b7bb991f5a1f676c857bb2a5115b4e98aad9b2a5952505a7082805828e609660eed09b0ee1a95e9832b44f716eadd150964d5c4f6c694b96169c92531dce39ced1d081ef3e723d434be51181c9326562d08163be370d1d68e8c0372aa6f774065f1759d49beaa332a935a7e26c22b54835d2eb7b2b989327f1ddb0c1cb5301481dc150479cd5993138b5548d5498fa948a09539fcc2724120c0c08529d2903d5d4fbe00609f848592c160d287ca43aaf2994c7b6b3811a04a263507ca43a3a9c3369e5af2bd43ea90eed676ad65ae390391f1cd458d5b1b5d65a6bada9bd39ffcfb7a66aaaa66a6a87786833a18ca0074c442123184b0b5149f743c491d42be73f441c4d394ac0c79a0a8136a1e7a0cd94228c50a8c47e55d9127f96285004210491e790711bd293f59ef42aae3e5f55585d479faf2aa74e6ece7b1b56cdf57d1aea889e8af03bafe837a4a78fb6c34d43063b954a8113c63ef5a0c24ff83386a2beb146a7e15853d06b7e1ae8e9a9de9e8233e0ded137d53e4e578f27d4ce3be7fde9fa5e7998ab8e6b0af42c1f24f4fc83809ee53d58f00f7d1096077d76817f887fe86bf8879e86354dfa7ca29f094b83f389ccc8cb3e0559f69da2920b569c4ad610b881b473e0eca02dac2b9d7a6935625535a5537f58ab632fc638e6a9220c2135a79dd3ce49a54fe1235e6dbcc2ab5ef16a8557788557339ce89415169973cef9055b6b53a7e584594b985c4722599bf357b7e6b6ef746275554b62f30e67d2feab51bc8edbb4adbd309b4c1a879f0dc707a9b8412e583dc0441b1d0af13821ea1f226aa0688ab8c3198797f02b56af070d0d251965aa28d4bf504c7b5f93e6f5b8de02865c269cce60828f269329e3bac576acad3fbf8bfbf8d876734331029d2c441b210058ed207a2f0454821d44cf77e1c877387e4d6c2b74b2ae6b5b3a325414ea7b60a9433bf071eef40c7e07615e24db1a71cf711fa75fcb5e1f5f8b03f000b7adbac3654b54f43c977f8331d8cfdff7952d62a1fbfb2a747bfff6776f200c7534b230da7be00f2b534608b384ab9830f595282182883e4e9d26b5491fa70e9d3137756457d614d668efd14c0ba3e9b75e03acab8e2ab86d0b34c2ff878e932bdd8660a0fa685b2693c9e4dd848e4c0ff071c248dfbdf72b89d27ba986d1e82252f66ccbf491944e65c1474dce46cccab33ccbb34c2fa537eb7c7b36bbc117d46e2e2938d064491229c4ad69daa6ddd5f984329f48218599029878dc5801dfb183f2e09c6b245c52dce21f71cc85877ff9b8af3977a8ad1409bfa139656ace99fe83a6cc7dfada2c69ead1680f7b3434fb3d2d678bf1cac2cc186a32c5f4113060d6f26b7b6fadb5d65a6b7dafa6cda9699aa6cd39a7c6699aa669dad4eed5eefd32de9acea96d5b0762eddebdfa40d149c9138a0ef8f88da64f6532c5c0d8300a8542a150282479a6da3e62b23ef5ee9d76842738c5240aaf6450c137eb6570cba93075cba9a33a02149cfec6aaa3ba91755491887ae023466daaba63dc90742375c70824b33a12d48bf98bd403a0521b926905bf5f67f4cbd2e115a3306a736d14804094524a29dd296d89441a8dfebf8f732b2566b2a52a8af33561a21a4dfb21e20645357b2aa144dc3f44b44ae4d460266e702aa1449c9e02be2ca78cee32bda652b494524751d04c198ddab29e520380f191a6521c2d3fc0e97b5f6ef937d0041db8f7dc03f9781d8d40be0ea33a22de4719bd858ba8078a2847bd9acfd30ff8d9e8001fbfad7b403d75130df5783caa755a1f81366ce0e308878e9771aeb44cc2479aead4f5991e2e4827a2ef7df75bf7221ad67861b7bd880be9f75896525a2a331911593d951e2ba73aa2e5cd0ddfedcf54da6ae2a2f58da852e5accbd0799eb7736d49e2f5a4dc4fa3078cb1d6a30ee0fbf89a4c26d38f30beba81a0bb7de21887183172ce39e76abb194670dcc79d73ce4264d2434e1921b4a03c5a6636063427a125fce980dbe005c5560137a2d79f548644af9f434573cb963289b5880118529cc411d70f3ee04e94767397dd4275581ecce00b51884eb212390e4ca1a2509fc318a7d042791460ea78f116138c8fdf8fd91636b841bfe2fbbe6fd50446b38d58ad563ba64a4ddb13b5d0fdf65ccd6fa2a5e9f3f9cc08028fd147d3ecb60d1e5abc1efcebfeb371031f81e467c1841bdebf21901880cc29dd8612985372d4d148073e8e7ad8a7e5bd1914d99002b4937a0933a63e88846aa55aa956ab1e291ca9f666a5ca38965c9555668903252dcb159d5514a6c07eb0a7bea2426f56aad5a5a114e6793c1e4d53b9404570e0e29273ce39e79dbd39ffbeffd188442a6524d94826fb3813569cab22cbdbb811477a6201a9482b91f1aaee15e2a6e0b6e8ed55cbdc2e70a0a5fb030a53b106447083d0c3cd55a6ae0195478af2783c5a6b16da7584808fb9a47bd753ce34c098c9bace279bc17ada01cfa1033e525708b4b15c20b1da4214e2f12207715614eecfb0011f7349631b8d693436230a3e665b6e6e3f5ad21021127a22f3676a8d0d803456662b75473de9952db5c470719675d99086a5e7083ede9927c6690f85a16f09452db87cc8e543a29a976ff9976f79172b7cac293baba32efc1f2286c09ad0b73cfd969677096d5ebee56d46f8d00f11638022fedf0f1163fc1091b3a5cb49f42cef828c770983b8bc8c67098328a97179191f4484175d548d16586a84415e7ec6d3088328a979f9191f648467f9297e9e7b0147a397613ae1875c82b4c386d8a2571bfd644b592766616a9ec94e80c2c95cea932d89e07933befe65d9128bc7237a192c3fe367ea74f92b03b42f22136ee0dd734446006b70808bf02e8f5fb3aa0dac54e7d7af427efd43ba05c76bd32968480785b607aa439bf242d7ee4d776b583e860821cbdbb026c67fa18d4b18e367ec071c6f8a2c1f504731baf04709280e8eaf4f7587d6b3eb1d3bbc536e4c7b291c69a28b197367b9d46182f2caea880817e55cce1810055598dd0e6193acb2e8d56e3163ea97f7e472975cf2922ebf448c0f06970595e624788ee9f0909a32fbeb0e1e66892534c3a1b9cca52de198e95311add23875c77cdd18b9339d6387e863e7b17c58a28f3ae5034e1ff58d5e69525b5aceea8e6bc49d79d65a5285aa91b47c08446d31f42c20111b4c62e8515b6cd1457afd1630971c65d128d625b386c09dcd6c29e7f2ce72f9f22e3a1c3f5441404b2fa10efd3800969a720997c4084fd05249c01206a08f77160af500fa7867b9b43037470b7ea9eb22d1c474253177e41d98061405035dad3acff3426fc55059baac3c4378c5d144399371b4a3f6298c863534fb4926f0201da9a424ea0e67a09d737befd5b3fd99292cc14aa176601c0d2b78d65a3ff812025ea3cf9795221d2311810827cd7882a3501fc5b8d3c734b4526251e8c06c0f1c38e0143565a810f48659a2499c18a1021e3c42f005d4918f3aaa2f93c96430c86437639c71ce18670f6fa94e89280ab02d3ae7fa9cb6b93a4ff1ed2c48de4be3d9fd4cedc2146cb5b6a73063927dc718e3cd0724128944229148a424f8ac53e324520e311daf23cd182f2485241269ca54da2273ac9d110426ad3aaa648eeaa68e484ef8485d1e2566d73670e0f5a9124c4c990b7cdd3e055dfaf45600751a56581d21e9de76617dd18e34656cf727e88a4375d4af391586491db570288f2a441d62c6d413f44a8a8cf091bac64a7698caf5e41585e45caeaab2252a4e58102cbd93f29cdcbbf46d915075cb3969b5d59c0b2ebc85a9d4e572912eeaaa29cd84ba8ae458223e5eaf17a65a53ad55372e578d81849698f8020bbe350e4ddb5abbdbb66d4d3e170782e0235ed5c7387593b5da71efbd65a50d0e22b3c8478d984c37abbd81d02ccdd22c9a44b362668c661d8102cdd2ac24569aa5599aa55930e041b37696f04b6bb57b6b11c03ad758c2bb1a4b38ef359670dad558c2731f778d25fc6509c71ffa53d80801dfa68cbee8135694bac3be58698ba43019e84c4663df94699e0ae38f90c3091fa70c6603dcbe6c7b3d361b3b70edf5d0c1049f3115701c34e0b38e56d0defeac1e0bf6453100d99e7b110bb36f5cb86de116b2a0fdf6d55b41b3b08573be5e5626b3d6daec7d36c04dee53d2cee6c084af9852abd46fa76e2ab5d6aa75db6fdc26a2e962b5e38ca862b12d04ce77e6d43026a7cc2747123e5292921b11542d32a3a4ce988335f57dc4392a9a199bf1234952461dd930ca4749ec71dbc66ddcb6711b6cabda14d68a998cbf6cadba3739c70002d75b75b3c2c9ae2aa8adb1033ee99cd43ae134ac473e9f8fa562084d4891045d04718a531c6bf479ed7da2db3d4d26fa32eaa8fe0f12a6a0388ee0e3f7c9c8de0c273cefc0648a59d9379c70fcf7b3d6379df3a864ceb6f5b474664600002000a3140000200c088583c180503c289aaf733e14800b687240805c3a9687439124c9a1140531c810480c008000000c31ca501195019cabc1bb09c79cd789056f9203ad59a3ac8b70c46a30a447ce99d9dbb71d269c6835b89406e65005a0623fc0c2e1e4f4f60c5e14afe651c890900f851013c17077cea3f18700407172c28c512623ec379e550b45757435117cd6f87f85e95ea391047972fa52af434cbdfa663984ea82ac01292e8f8a3147c4cacd2187b539fe2e828c9b0ac697fa952d8153d626909f3c41b424084349813d64a6c13a1ee05cf719e5c0e2418c35c79a4855cf61d76b598191e68c2629e743a4b471a478670d44a3316d382e526ed1df7b1deedaf1520a04b98491c7e09cc33b6de7ae11f8a6e7ff12da86300cd53cdef308413483735b133227f3d478c7df22b1dc0aa6401221f35f5a3c5d3239e0e5f8e402a04c5c2e8acde18357858a737d576b314cf4dba599e86406da5119d61d4b42df78d6e00bfe0d246202e4a93e2f1db2a41a77691ed3605b64f3bad2ae7c405c141c5d2738e3efcc7d873a525daf90152be2205251af73d8d09ad8705d2d921ca8937290ab24716fe036e13c8db3114172adeef976bd0305f873a24eb526a3cc80459900f78abe8d480bbecd1628fc1f02134ebf1172155671d87d032fb6015a4d845cb160f59d995f8f1a33044f9e12a50e000012283bbcbd146d9f7862a0e1137c48a692a9ac3d445ccd45be773b49f766c70ea4f1c0f888d195c443094c2defb387c5487d1c670a64152935c9e75c03d7d11829416e675b13469631cd6feb19cc30096ef52d961940dffc39a1119ca53d9a48e5ab6b52dc6427830999d4aa43404e3585dd88f30b7dfc58248b31050a307893c322de1162e629b04963d6379a1d5cd90ff0c721e869a0b2f271628d8de514960783e68583308a858aeb718e767cd5c142a65be21687818e1f2e848895f870e4869cda7c209861b0361533c4003630a080942d7b6bc60f4a22780971d00c2e117e1400d15d82aa446136a1182a44a1b7ec8930e2807c8ff2d07ba864e553f945c32cd40e87a9260f32b0e3ce4c6b390534d0688669f9ea734bb6d813a04a6dbdfdad2cacdfcfaf01986585c01dc0c5a50c801cc221cb19023c003a8a04e6d1f12cf3655b6330dced1a83eacea00351da705d7f616c441e8554c1056b84ad16dede116cc87d3d20c3a83a869ea705f9471886f1d1fcbf407786ddc50d2f2d47c24c079b21d76c4b54cdbeeeb537bb0684d90736c5f8ce26b10c292b1e6c9f87f7e4844ff7094beeb675f35f75bb87f3ab92aa0e515e19531769b74152e811ef527d29f0411ddda10d427d5f15a16539569674f22312dedb42d5db87ea950144163f9e39a743cb3854fb4402901cc0ead83a42d01e52af228a2e4a8af08c11dd84f1b4fe08ade3052427a15c85cdad7430b6a6a98aef8faf594b76b2b5791eb2d0a1f5316e4b380b552f5d1b3e27e75726774f076052d7a280cba39d06652b43876674d3e9419036741b4eeafd6803108cf0a102951617dcaad6ba6642e75809eb03f9b5b112c93447f5e4d7ebdf2c137b9470978c1f3bb3eca32c4f645634d4381d5fc26f7539cbe60dcbbd4ba85056929b5c57b29ae8854010de77062cf61d984124a77e10bbc3b93f8364fee6e949c22740457d116d5bd9067568b328265c81b6ef01f3b613abd597ede790cdaac896e979d8872da0e662230cb415b917c2b5d64af5e1bc7b1ea450b8f726e2d24fdfbf0f9f5081c013ea43ba569c8da274d33d07461811bd0a0c3271bdb2c61743a765c97e9e2d76815973463683fa8bf7fc29e895ffaba13d7367a67a9977451fae6dc9974b4216a8cbce8c1adb2c248049f7471b9e9691053586aeb3f08babbb96b25a5ccead731c70245104b21aab62942ab445b9ba8d6b5ab050f33bcee9d9189c3ba7cdacb93e89f101713ddb1ac8a319d9954e245b946079fa6471c141d57cd2e3bdbb409b4fe2819ed3dddfc45308480a5733ee128eca0729262cab89bfb540d8e3df0338cecb9a049512d96fbcff258e2c40faa400337da9d95d12959f250df27d019113f3d232e41496372aa008b60385fce178c9d0d3f159a20c5dfaf21b09cd01002def8dd0c3d63d14e7191bd5a219223a0dfe299b2c0d442c4718718d27f18a1fbe8ff346809737d923669f081ad513fbf5e732ed6a9078a57c52921c9d501c8168e00d80ece63f4a428a27b0dc18a18f2ef14d84e6aa754c6b36db5c66680a3318190ea6550fed154f89b0855f246a41ca749aa85cca838fa81d8c051d7c847b2f0fac118a787969c8da08197ca13f60c416db386faa5a40919d0c5f9f5d4ca24d0464452c9a2ef2c6a604da7e6e57e09839831ae3287730cc4355c19b6dc449a26c873d83fa446defcc9c844919adab189598fe376bc49b31175c8a06d5000663c5f9bf903febbdfd56bda4054b388713c5bf2b7127a3c4ed9711c9182e892108857be8a2f0c68daba38976b807828240213fa197b783b366540df1dcff02129ec995eca16e85175144dd18c787c75577222793f0793de92a861d2203158a33bda1352df299533f8b7d6beeb7f1db1c789557c1e28008bad5f12a6a504dda7b63d58b5810bcb75c2546f2de9f370b055f3def3594b5d548a42fa13a9af06253255707001f1b556cf3fc69871d1edeeec342f5b85edd627268a8a8f0a6ac4afbfc1bb18751ec015432adf733df7f8506416191f5fc435f92c30a3e053062bf12f840353e767dfd925aeee1a8fd68223adba0286015d3f190d5965145bd6e746ba2c6b5a6f34648bf1d9f9dc3f63b63074c2ffbfc6e5554b566d247a4b01a3b279fed4ac00e3bfa7706ab4b2804b3581c5217e4e008939c943f9feff68faf1fae33ee5eb49b6598efde63f3dd12baac697115d3269ff6f926cc3cc8c993a9278e42c700a57866272f19a9478d016479af3cb8bd38523e93cea4a64ef0746330267b8e505103925cd0bbf7e8e0a5c5ecd8906061d4edf29932bb71cc38cb82eada8cdb6daca01b10f39e5e3a6fe52b59ceee150e78c6c75880e2b70713ce1b3bc58a2cfb26a7b1c2f34612c35abc87435a6e749bde532ace4f3b11d3f5bf0509aa12aa6e362f9faa49a9e7354e5cbcc3a804dd7487f66369b03526e20da141c9e1dbc11cbe0baa74d924464fc3c55a17dfd44e535695db25f0dbd6091c0a718211b11cdcebf6c8d68d989c834d36707a5188f636cc29b86cce7d55cb2fff1e7c6535dca80fbe1e4a2096c7d1f4925a2281a16eb26c033ea0ecb44d1c472765ef051f1062fc38092f031fe869d01b1e29b610f8a0d188a43b70901e6b40b59b9f67da257efb08457b619e9aeea84200c8d05b534c5b1e91b766f662163130f30ad13c4e2f15b60381338b25fed84f362cc7373e73f4e33d2062da0e14bc43bb48ebdb36bbe1808a53df952b513c41a891ada49faf3b5d6138d3ed3ebd00117e994e732e036a4540120acaf4ed1d81d3e67ed24aa7f7f167ae812354b995de540c5a901632a3b00c268d664b0684be3760c460928f96e1b063920362b4bdf6d71d250f7a1719a9c2988a66949e42a1a59c0ab988fb0d94297435990696f1b4ff0b6be0a079f42531c1dd099c5ed5109a9f9c5b6b5ba86b5c8f94a9d49c38d980735724714055a6a3b188828a8575406c17d51336393425d00926743e36523f7a05c384b406d8a950048aa5a7a121b5c0db35b484d47bbd21603ed392245f45c667fe3a1f464daeae03906547442a78043a8be15c55c681201c58aa7de99fcdb416aa2acdc40beed01f06bdb47b89fe1b23cc0369e4d1f81b66cbfb3b997fda77dda3737dc4ca90116dc4f575bd7725608a1e0b53dadd815b19db8e0ea7370b5ee583245496fd366bd6f30f25799fb8e187c53e3326ca45e2c5025679f4cedd1ec83f29712a320cb0d4b0a0e4adc281b4e7d13d5ed6bff39554656c829d22ede5f4ed4648276409cf49ef2b344a8c41616c379dce419f920522131e55acd0df1f674f00d64ae82410312648406d44666288a0208fd89bac499835416a791ea1d0dbd8f9d56dfafba09503e84143d677173ebea61eb01a974dacbee61d782c9a0b32ee93c036490b45968e7db4eef66acadcbbe452cee90bbe58d5263a046fa6a88a5f0180abcbc7498b14bcd50e22574489fc373aedd7a4d2e5c6f4d0abcbae25589a22f62115dfc51fc1fec654a281ff043f0d09b5d3c238eefa959edaecb168a0a24f257ea0da85f1cd2245ef3e27c670d075fc5138815c0ce0746ddc22c27fc149e204df29494df0c03551eab765cd65afb7a063e52eb83888814fa05f9a560d34af290dc72bb688835f0363b702c7d6924753315cdfca3c8605d00d6dba944fb32e146fa8c7d82fcb81a8614cc4bf108a56a7d0d76c8a5a8f5d7bdb4ee7a9382f1f8c836d2ba8864a43e31d565f119db7a8904963ea6847b5ca62d93791bda15946b0c011bf5eb704803c656d6f4648e1689e191783f246be8e88919759b4b6cce95b0c592ea6c3fe23920309287057635dff34e13f2d354f25119908aab2a29734c0334d724643efc2183b772101112b04e00607018a768fad5e361d68be1ad551280005beb36d6768be410b5f76da03bd55c317e60397819cbb4c03b8fa9cded6cd45f8ada7792695aea7f8ffc0a0436c3e8bee8460472a60cd379dfc34fbd96499246e54b22910d1fae19a7e3a6f6d41cc277d7b0a7e6ecfb78efb1d117095700341353b8f3c7c0d88b942cbb2d4d38dcf630d896e40691c7f4069c04df83f9c2d3f7588a085720e552f20b066530b57afcca8fb50074a488a9490f25af3446af06c665067ff4a0c16f2f0976f527a3ab7b058407ee53c0d7f9ee392400973f295f230677d0f15e32e1256542e21308a78f832d171eaa57a0b9e8a12c857f1b16980ab0f9baddd33679b7a27ac2b349c70ea6bcb9c5a2ca8db597c3c997ccb9ac532e49ca5583880a32f7b21d6a1c712ef78d9d42a0ea73d863e21ac4b2b16c830cef97d6b3477439ede5616638f37ec89752235421c9cb3b7abdecd8400ae33578d46996f8f8e003c306d4ac60fd399d11a2cd2093dc9d9485d64f710139f3846b5d6821159126cfd896b35f853eaadbc8151cce6ca78fc47c96c9a117824766cf56d5bee6e48d1a30aa26451150dfa03ad1130c3fe50d545892cc3628258b3d7c0e51852bf3c82eb0e005e61c823b93f01271993395e3f3a247e262e4b2a3973d16d1dc7f1deab17cfd70513c9e38156653d26835b61d0c8d11b03912de92fad584ea8c3322624a5c203a682b6c9da1c6e2ba095e1cd974c9df1d29eede112891f12221a8f3b413cec060404e8a0c8d444679f4b0110435b42133e42103235965b59e97dd1d3c051dfacc0eb5a9198f23d36bc2c3c5fbe9f0536d538e7ebda66b784a16b3a99a016a4e286a3441b6227e61ebe31c3f2e5243f22b121f4b061c5c486550b10f6cbba637aed3167dc4f3f97330690b7bdedebb4204e168c9631683f17ba6d30e74a01918a8052eeb3584dd5b488d45a424729a7850765f012a49c2d265d9110c0fb95da5f81e74c3242e32d021e401dfbd243c32f1398ef0954d417833bdb68ce33a6a9c006aab4039bcede821eb7649b0abac969dd07f5816e13ce86e55d5cc79ee147b0e00a58516b31855f93650fbf679d3943e27bc386e7c42d59fc3026bf919e7362be2bfe7124eda77d178603d56508c0ebe6b31201368a0ce29e71c5cf2dffc322267896c2631f2c826d586482185484b6d170088a07d7f8c0e150467ed35d5afc97dd8a2818d75f56b0a29c2c040299c1e22101f46ceca62b140e86932a791a9b09c09b85a94f0ce1e6ec4208483b3b8ae70ec6f91c482742a6800184e7624af489219558bb9233440dd35f06fb7c92b47d5895df75d47ef7df5c55f4894f62ad123bd6eb467c76a9bcdf9218942c49f5fedf127cd327ff92fe24d99889b2661ef1a9c2b7d34314c5d9d1fb47264a479fb7ba638a13007d284627d8ed5537a54ff3696aef45a01f81605202144af65f545229aa6acad51027077ee923ce68b1d10ab3c9842a51c050f4f7d6eeb27ce0ecf5c3da9604ea28721af69ec6ca705152b068e952ae6c082f9c2cad2ce5f06b60aa5ab807c343c55e2e1b0e7a62ca60b004fdadb28693e8a65711e9f9188fcde1bc51f28414113f1a206cc0151fd74fdf4824105ce48905204c54c5b9642ba3169c02c84c6ec3986f0a9dc6561d36b0b638e21412814c2627a2a68b2a550c28da1018f3844c0fbe0be0c0e2b0e3d6515b1fa447d4b9e23402dd9900047cc45f569df9614cf1ad5f583f4b15bc9036c531988039b9e52c87d35130a7a79313315dcab3400f6b67e7d2b64e12fdb19281fe763af3e7fe25a552837c30d4ae9bf6f008ad3b2bfab26285d22c666f090b1c4512ef9946db317080242b6a32dbd9c5a7586c1690a175f31bc048e2a785fd61ee3e7aa14f674bdb26b3f583115b46f103aec26d7e308929ad963f3f3676f7a29d99a202ff1b0f0302dadb400eb2bda468e5f153634003e27e04f7f75bd627b44c637558d6d94370f59c670f1b068afa4b76ba28755214d4e74f39e1c39616d4ccf0e792bc00542b5c7bdfc60c605b68f56f9d17ec37811acb953994eb4fd597aa100cb57d7d81884a2e800d9696d302400c0b02e5663bcab806257e873e47477ef5c750c787eeaced4e6fc5897389d0e3d85eefd33403e05a05cd0f1ac75ff98e5bf6ae1bc87c524256d8b95ae430ab90433a7c301a14381d3bf8b0c62ccfe1b7af6b8466d9ae4ea74bdab52d9399c74ced51a17fc72f9760dde54f954761af6ed818eabf025b08d2e632fbe5b6e8e51c03a1012e76434015969a3aab33d7e5514a4a40748965ee78dad9ba168f8fb8442e14968deffb345a4d1c20d523615d2c20d5b9580c3c6b340369fd7c6e46d7bf8edb11fc5a50056018f50539f44a4a9152e78859d45c5516905607bbe8a80f857d8794ca0deceaf6ce1d7ec4072fc3d2093375d2fbfda23a915dc81f51fa93b4dd6bf7a055cd4cf593b4c8370fd5979a3585e9e1cb18d2ce4acc7ea1bbea4cff87399e84f3c44c49a7646f102723663ec9f94cb1c13e240e20b83dcd22d0da7b2f7822da2396adadb180ec4351916f57f905e6f83d49a044a4ce3d1511c8248ab141db53f00f0be0284d84034ab1cdc38a0603e35735becd2f59566f62be5bd5a318899fa38e93022e78e15a030eab8b42bab46c245a883977004cfc55f81431d514248aba5ad10d4d86a1cc2422ec9a64e59976a341d1556145781f68bf8916a0abb7db1269751911e0748a9215ee37af5b9530588a6b53534f3604cbb2d97f250b25821de35fbd118e7d49198d2536dd292d7e4e58b032e3b332bb7524720e668e222df87c49c69da891ddbeba746daf55850b2cefdf9f790a03e1014caa6190c6cf33873bb50f251f31b4960e9ca449b85339130e540a029d58c173b52af15d9f39d92b0b47cb85f3d707cb7bc030ac1ad4375fe7226701e3a99079acc0c3d66adc4aaa2b5e46d5bfcc6cd0321c8e435a7ac2000f1514a657414ff50a07db0a299d8b33a9ed8df27df341c8bff26f163134f874d579f63f053c92c12d3db2ef8ee728004e504cca5bc7c9909fe4376f6a5750ca57291039026e527b365748ed5968f707c1a9565effe0e0d5641cbb50eb0c6c808f5f56d2488b5e9f107a9edfa088092ddd445ba7816d8453392842c893cf9e459f45f22fe17c92130f30fa5f0f28deb44c01896f623e800f4c339a2424caa6af7f03644db8053ee4e056d7a78819a93bbd808a2e602e8584ab363a5c31813706079cd128bae2da7c0841b0048f56142348df9068c41f458d6bd30e4a81eaf1e3669b888dca48442c9998db5f7a1a8a18492ca21b330fc8cf2ebcd40b272c596f62f8dcc3f667e4a954b3f803f931bbea96f3deb015db8b44c2ba3fc5aec780903fb245a4e53b0f6d5f10f6ce11f87ba852f17e24ac8dd94c949010dd83481d4d825ad3ca81c7800fd898a198749221fa1d92a692bbc88f57df2c685dc3941da53cb836e3925f9b1331863f1c31e88f4f6022c92c4df7080a85a01eccdc42b7260b42b86a474ac57aa01df20e4172fa66aa8f00c2cc5d81dded9b5a6100d94db19866ea16d94e846d5081d2e7085374c6808609f83900398738ca8b5cfdc8be7f85640c8caa9479f5eb420920095c3e456c573612d30c7982c285aaea99efcc92dff9a02a55e5de286aae3bb10458af58ac07faaea8571ca3228a48c43e1ed435708931d690185d39306492cf5a92f831024d8b98b95ed6778ad87d1a339d342e4dbf2b453fb68f394f612d365714459996faa1c8501b434bb4c05eba7f219ce3ee5476eca2fafd1f125b0133800d306fa0a5b321685d90aeb65eea0d43e6b578ea6cb6b089856cf9a40e0c140e934c7c16a17a72442a7ade5a26bf24eb575fe7f0b494f13343204dc3231e2636f3f32cf3f3ec016c5e8e67d0370cc90a508c1a12b04b84ef29df7341f2789915f80b14f482070ef32d186e79176690c55d22ebd6b1f38d618018039f0143904d00e58b6194914addaa591834585fad4f415b57c2522c906165ae4d130af07148123d1a2a66cf992de6a8139965f74338cc0717c68a6f95c7d64a39de1bb3ba6fa4459b55de5e681da223ec9bfa001e731c239680e72aaec60f59b23ea2de9a62008748bd691ef71f6b8b0f7dba190d7e4a0754a2db24cb5c44aa899c306bc7cf84abc06d9c8e3a27d636362e0f765077b87d43adc7d8c8f0cd867b6ee33146922f84db8ed187a858bcc7b8d33b87db2a8d7a5917ced41d7a1cdf45523b768b70ad2d65a3d4026ce00d8d0425ab3c45b5e520f20745b8919ae11613b43ed26b42c1d913c26db05291608f8720478414852b0bdbff9519b930d45c8ffeb0fb2aebb7b63ef5ad105618c28b81646bf9f79582a2e90a63142f3bdc90a1d1c4b50424a51d99881aad394a21e7512bb3fdcbcdbd68bc8b3220145e0dd5e6295dbe0c732b91a5280c025c07083eb3da768636f141404b6f7cf5425318c87fbac182809845931949b7bee05425fdd7af4fb99d27ed2b43b1dc4eb8122c19f76a828f1985cb9bd1f0cb025f9030065ac799d062293e8164ec4e621afa53129a0d58369a45a05c1e15a07d8f9cb334d700afbd242a622f903049d410a71a4d09161bdc7b9f778d466668bcdbee39e76263a9f1281023d7530ae726b0fc6b60db524459a62f7577599000bd7a19eee8ffa3516281f0e6ecae2f78475621c31593a3987641bb908ce29279ba63a0bbf48c37abaab2992cf9e75faf6e1d31b1b6a2145efadbe5b2afeddbd0b642136261724a66ec0737fb30002df8d62196344ac1bc47aa191e8f67be6c3aac322abced6c04b40b20a7e664f2eca39a312f40c8a98f57bb989b4addd4cc4d4f9668964e73d3bd3f64544f31a9a2414b03bb73d3027745a3c1aacc2eee0196982ffe91384f12cd3d20934311741a3bd8cbcd3236ee22866f4d1a0feba788410be4a604daa932be5e4de1db09907fb00ba658fd62c6543864477b19109f34b3636d337bcf866de0f558a4645b40e39e03ca0e546466821de711b848b05ef4188fa7a1718937ff8f7e1c66b2baebd59fed23f8f2a8f4ac91d8dd4ffe2472c8eef9cbbadad6239ba3a90f41c9947410d88ccc02ddfd78dba3e2ffd2c4d3261bcc40f219526b94abc75a7d03f03974f03a296b25abf56d7df5583afa4c375b7a7bc817c7e736f997a7d54c7283456291b6dd84ae1dcd129a34a5fab2962aab008ce02dd3441381f59dce84abd357b99b7ac81d5cfb1d7ac640d87ec553cc2f70562868c8c0ea68ab8ecfc6e839e8d5f27c6a4dc869ce08e500d2189e5ec11ab94f34a658449ee7aac3c518e246058235e31ed404aafbf2ec388b0c9692a155f7371f4e9e1f04b105c17a53980110de11f520b840a0ac8e1bb7abca6a09ab71483c00df58943dd8a8d19388aa07af10a89ac8b4194c62b8685174a6e75c7aa1015658437faf040530a7b01a372f84131250ec5d8cc025ebd341d1bb99fbe012e4a112646e5c2c9914093cc0e26aa3c4b834060146bae96887052d1e346fd2b582ad578f326f8fc0a46d5f12c964f3ad11b8229fea1dc50e6c27dd54c8fff4c71827b119c481e92c0ff4010dbeda115695dc12b0ba8f3ac4387e3bf93717b738761ba407f13f2f6faa8b9fed956f575051850935a2b43569ddd2ce56c89cf6d5e6f73b1019202415c87274a887b3f72c8bc0fba3364d5f2517627bc9d1bbbd8b5513033d331ef62510678137d1741ae742ae0ecb0f4d3f9a32fbc553c88bb24c06a94b1d65c1819177f36da497235c3519f78548c8017650c53bbb8c1cb69cd553ada0b130c065e0d4289fd8cb4bcf155a95ef04debbfa54f5b9ecd657722d821274ae3a9abf45854a5a67315eb37429e1463ec5a983bb690c0666a57c61bc3053001ceb48643f758c8272e7b647d7377b9a76881f07b47047a2bafce09f25865a4a953aa797d4eca2d3a485819759c59866c39a3fddbc60e45b936f7562fee6266852b8446e407b6fc13e4fc60bd94fad290bacab790884ffbae472c8443b08aff302d19ef11be196ed740e2bd9a4fcf2cbf9c0191464c2671b2b7cf943de2d07d6c2b2d1aef897e6e4d6ee1dfeef5a0397b7ec779bbf021ca32abc3bef9f09e14212b18abfa32282b167e47d9d0f9b34393d698fc3379435a0ccdd04ea3bc483074fbe754a16e1eb051992efc744ead7c51600cee60c85e3739d995bb5aab4f38b68052ed7de91ca2d8821451df98097330e6a357aaff8dd640646f08de61cb2b0d7eab089b2cb035b91ec7fdc2012b27958d1f281d3e4ede26cef5b03942937792a5f671778673f9c582c7dc6697554e203f181d0aa22896e488b5595623784a65755f13d05571fef35db71f4100b4bc95fd15a30e8a8f82d343d197a02738e0a6ba36d2c5437a112c3f21aadad6811d08641fe6d2609051290fabc58472bbca253ae651a4ccfed673308a8d5343798941e07d8b4985dbbd484fa47cb72ca4d7f938faa12b0a5b025eeccf27632fea2a2864a9c58969069a31247a23a6e7b64d10b3a75dd798e75eb4b2c597117d04b54a9be554614a662a2adea9d988bcd770a684fb8ac9072caa94e99c7e779fb26108bb36bb46c18833e075ff760c80d87e5bffe3fb302164f2cbfca2be87d9a90eec4acaa4441fb36a4819fdd0933ebfc06be6c3e95341b9af549f4ecc2d5e186bd9abeec36fc3ebc3bd49b189da0ba6f3d725cc57f559a4f78ae629abe4099d87195e502247df521f079cfd69305106092f45a4ee8e205257f5b58a3cc8d76c9a6e43a7a510a0b007c857652a2bd545064d41b9857e5e7d59920da89c03e6c8e53c90d23a02ee29a4d84e20f03ea94c275ad16c4bad85fd02e8cfd2bf3aeac466ea80a18975463a79b728ebc87ba0b4690cefb5acc8c5c58f0bd044f7f35eae1503662fe24e0c5644484604e893fc87f0ea2a046a5ded233698a6f5cbb5433558355a4cb4c0599b4461f7a8223d886eee66d65c8fc2be5aeea14c6153d921084f5d4d0b5cc93797266451f19545f0448b4d062d81fba286b1daef345dd079b5833506166c86edcc21804192555947322f1b436b6fce06d7e1036ee3fa72f1318f2636428b4516e3b274e7dbba6eb0c279113f86caca6568b45b71a16ef97ac789edbc7822646b6264b21a37a067608e7f1073af9303dd4a23080c90dd8b70d785618ed5a9dac5504ce23e900f355a6e08a00ef1a1d3df5dbc91e6f9fb8c7ef847cb41bccca50eabf973f69180c636f1a9408ab12baba7c8116d6632314fe33e59e36c5338b7f7633ce8971530c6f7d68a82d925c24c5cf8222a163d70842f1399ef431041fd9ebc8df7328a17574eaab139f3f9856422c8c7403bc736d8ea5c5b6b4ee12905bd15664a99e2d925fa1bf009d4fcfa3f20b1517ee1eaf429fd4c2f100995f37cc324acc89d71756a802a023a06592c98dafef2def1801033f24aeaa78353abe4ec30da0c368791e80359088c0e0e60e126f01513d4666c8fe4b5d17aa3eeeb5d857b7507cf24e16badc92a64d789fa26ca70b51a3408912ba687d396525f78e38b159de81df7b11d9de73698ea95db309355c26a779a29be4c8711cc78344741fb23b4cdd1917911c043560493a59326652106f401166ebdb9c39d7e384887b8bd76d0c3938f082f98e3450d04a4b6197ab351f3bca83f49545962d9215cfcdb2d390875f7253f5ede667fe10f8ae85f30fa185220e05c9d807496c4b1da39197268b537a3a4ab43a8503fd28c04e185587fd9548c98e5018e1aa589ed9e1399a9e0798304edde338318b299f18389ede6ad25952de4d6f404ede41df1d597b8b9ee947c558ed698bbeb424bb76ae715f4959e09d33acb11e9014380f8859b894b682299df6d9f2877cf93518dc7921ea150d2788ad26f640cb46aee51a911b6246905aa3a81dcd23ec01be54a0a036bd019118a24f76b6ce63f5c5c2104ee00cd40ea76855a7e980ba3007f78049e3a458d07b737691b9988ac6403fd2adca49a1a1c8457d14285f011cc6bf3e957aa77faa1c8e6826f9246805238b78d8f732a822c0a746190de1cc83beff9e34f01b20d8c840d944914709189981d07a9ff41d4b66d3f15754b3b7fc6cf3869f513bec81d1b67b254548bb0b2d3e4112544500c251828c6799da2c340771e02576af8b35c12daf72a828d4a94fd8823997453457efe048295bd616239eb285c258917e3d068f3a2a2755c621f7087f25b37954f075bd04df240e07c827569bea50debbc0b80279178d68de753668de547d20e950fb0e5f191e0728e7d748f4e9aadfc4ca32b1d873f98073c858d20492986e6bf83f9c8300f3868c4befd01e9efb7becfdfa09d7d3f768f83095d5d0386173f8e5b6868899d49b9f15668a3607c5464962a10fdedb6672e6af9573afd079fce05745a0302b3ea076356f84aa51314eb051bd8b780d0f895fad54183fd6e5e824e241848c5bc73e325212e8d8e80274919935d8bd97ff11089489f35bb20340083bab3e461c184f7c9ebe88dc92674240422ea85ece51c0030dbf78af41320a7a50f2cfc6c327caf68f75995eacf7a495be225fb7dd6acc6a3275d5ad385247703df595c1cd015e311fc1a9ebef6fb211a55ece646fc5fa8448d1ad7aaaadddfad2ce37bd13c6357c47dfc7a5c3357f74921ecb671ed5af3d6a86efe10f3ae44f07509c41a67c85d0a750941fd0abe1e7de49f4c42ae21e625f980290823f3a8912a3c4b1176a79ee2cdc41316908da32871cb3450667a8fcd99b2e7855d3bb7d1c9645dc68421760f71e9f74997c52530ca7ae3d5187b673b0ab725447837acd0a3195b51de626a27296b606ae8cafcfe0a57ab8a75d8fa96d4f2bf8fba074971e63d96dd359da2ed439dedf960ac02d1ee2a4a5088a73fb502d3617556a1a7ad11a85131480f4ca76cd3ea480225d1ef97625e370ae4df9d00956ea4bf1d5b32cb37a241d53949e22cfd9d1981e5799204f9c104614b561c9faa24cee688365b60b662df7821acc80e4824067f34129560c690e85e4b51a0ef2c4b30bcc8f949ee326ae84c38a2fc13184840317a8a5bd144db2d701b4c85dcd01e4b010b0e0e707bce76b2fbaaad1df162e870ee0bf5bd8ae55d316c996246aa8888462f1d335639210622e00ee163b2f85230409517006f17c71d577a91ade85f3e999ba48783a3fd329bd31d8f79cd572eab63f717ca97ae0d140228a3e732301d44e0f39f20b90cec76c378785d7682a1646f33959306d05e5ef42200d94750dd1d41e80ef1569c95352dd00a12b919bbafd43333d9a9f296a384f43675ad617eaeccdda12c3027e52138402a27dad9e6cf6e277d30de4723e951275f5219d2fde3ad67ff33fb5c0bae49a15c1e04599afa4b819c5d715731e7a2c7bc6f60e3d800f19dcf2e0b428350a0f6126dcbd874b328e58974489f22b8db688c32a334ba785841fbb01ca762120b8b03bc8418c7ed2a1d433a920a2a98b68fd991d72f01b6a4d7b4b2fa234ce798cdc1a1784db392529a01726d37e5c844337571ec09557d704a0aa3bda02a76bec69491056f0b915ace57d9d8959523b9606c3f1e0f3ed7c16e3cfe92e29c450dc528c41cc76fd6264c1743e825a2309c7e2616e8b8cc218cbeef538f7f774afcab01d61a61807a34cf600698737cb85299a8bb59230b221fd45dc3be6058d81252aac3b12a7905aa70ea439e84779cc8b2413746b2718aa4b0a65051bade844bb70b1d041eee6bd46ed7ccc4c528a808444a11884ae6242748585ad5c94a37fb640670cbf6a1419cbf2740f46cd6094cfe8bc3e47a1d86be1def0b70287796f494d6775be2a768711d31f60d75f6d9a5a060d2671c3c1d7bd31e486c3e25a2f12e254a01aa1f9a186379bb95db562f27d5a78e01b0ac8cfb137d8d559733c93276cafb93bd18a44670bf4026bdbdcc29c3951b2b4cea3b7202fd974214b6baa8a47b50fb2b8a1044e8b64fdb72e4a7d287eed020a503f424b5072d709bd8900bb14c5df53e5fff2f09fb6d191465a0f0159e8cf6c2de04a7bb4f91e22f39dfe78569160da1d08a8ee0a91e5a9bac312729e14e6abd410f9161f513020ff45b8062974000d86d5ebe49d9caa04902ef8059f6b7ae509ccd0cd14727a266f52a6b4dc207f40b25d06b561c4e6cddfdd9803ef170ab62685617ef968d4261eb95c760547d4dd08ac88a63d189c3306334ba9a0e052becb1d17c4f86a69035ee67ed8e788caa0696effeed7423ac3f11f51cf28ca4c3afd36ef84bced048df7452e96dc952d25655fbc4ba895d4531434647ab108f94e59d5a242cb81cd96ef84279803a431c10241897e70231a219b8a6cd4731bf72d50523e94d0a92403bba3d7e1917739b01d05aff550b6b089b8b849955c93cceb29b31f2983351f84653fe1b84d795732a08dc8f882250643379785b3c54f732452bd356cfabdc51d7de6f71b9b2ab3bc9e6846413cc48e347bc573025e8814638fad7ba6c76e9d7feb325b4f6adb47d84f3dedc539bfda7e96e822a1ebcac3294a5f29992949ea7f80e389dde233ee6eddb8e0f800594ede7f156ee3ef855bb27b5fd4fd9af05cb24b06e0f8d4cf468c33fa8bc64d46a829dcb195dac426ef3c76f41b4ded3203726587acd0e57e9f54fd2ac383cad20aa20ac3f39cbd21429f7adcc613031e7c843ef68e38f312f3c119919a53f298ce0774c7a8fb29f58a4f0139f310b2fe3c1db53560953a8c49ded0a702eae16eec3ad8806a440fa1a80a5e8a633c144e8c9c3367687f3a19c8088cff8b8bf64c60f41cc47810f1b3c800230219d3eff359b1af5cd67511d1da08949e9a8cd29c3776c9631e417b7e88ca0c01af10bed1e617b274416291d5d2f4fc24d8d0a177c988c5890adb1122b73301475001a74312a30d5e0ccf88da90d4b69f8be88728d0023adeca9065960946b2fe65ec6d911cac16c14434b47c63896d95b0fc78790688c6ff258bfc27786fac0d8b6ba2086bb674fe833ba196e74cac25060bfd8db4c4104ef2193de2a2248ab07f91bf69315c660bfed13e182d113b2a80fc5e487639ee5a19be9036d30b467b43ddeb388852f45893c768788a31f923bce036a7eda08293e054a14b8680575f181712a36123dff54c604c9c5a0ad7e4407358f7678f2d8fbfba0710c0ea45f83f69004caf3d386c28c2a0e39bc05452041f5ef4d698a5ab076ca8121bed01b943c7af1c844e8ed2448d5ed96f650dfdb1c70b1eb38527f0c41ac3e20caea0b97073a03d5b8e28537618d2bc69145c7a941a2b9f787222744f856439c3750f168f77790401dfb3701c7558da523af1bdaa13289075016be3bd8c12e1c3eca5068a3ca6feb7007a0060f8b83b750df32ac6891b05ab89163ea4dd8b0a3c4d5974fe791dbf6a4495808ce28c322ad0e9afdc4d44813c6ad48f18b50cc36363086aad6110d203581520b08bca7435b6e454c5db160760e260640f371af78acbf2ba3a95be60025a92f94dca9b86881b2452ae3777191c33b9c489905d029eb5e613fcdcc71fe1e651c45b5cb83cfb69f767a3f7b629646ebfde5dd46915f303b284dd70503a69bbb00b2eab9ffaa719b1ee30db2bedcc884e775c4b90e8c58f7ef8e3c0b492665d6bd191a95d54c7878e92de721e82bec3de5903d513ad51bea54e6457afad97c2ca829c281c97d37fe60739401b432cd97d285a0245c245c4743ca17b676575d955d61dd729ee3401c512bc4b686de02cd55facb38abcb4f317fe50dfb0705da71755cbdc3ba7c989dee24e1e44a9406322acc5aa97564e5652335f08270c17f68caaf01c0324ac892d224e3cdfba8301e49017e38249bb26cfcf5428b579d33653ab50a44cc5537f9495ca99dda75ac8ca5974dab538e92247b64e2d192957c211fda88bd871b95964b9d282d7d7727fd785bf5cdac6c4473732f3510fc33ac516615c6e1b0f328386f714aee5e5e539e4bd5df05a775260152ef6c595708f2442c6b72c9c6f1647bc60e51b906c17fb8849fb29bbf7878a2aa00a0d23256f03e532933a5e8e8304694f8ff4405b286694f3f835debe5d2fe3b2819d0a52fa924ce3f369181793aca51d15cc2d932c2c2adeb4c416dfb3089b8d244ab9740d4a8d2a5c5bc50a4bf71c1e14e120c5b30ad5d0fb341181b52953b67d9fc50ad7b2f1f199510d88e8a7517a41b6080b360840131d6bc9fffd4739090507cd4edead665ff275eb8ad11f2278c4dcfe4ef1fbe491074b146b681533717458a108058c15dbdcd413684c52bbb3138c7caad85d928965162f428eb59537d9954ecebe1e24611628305af907eba2a29f895138f64253c550aa2fcf41b88dbf556e0a54595f425feb597f31cb870ca46aa4c0b82d7c9cf4d33948b1b292875734e5469f4d4bb4a7d1744010e438ed57f5abc250711b945cc850bb591b63e1a8f1598ab832ca63fcf00acffa3020fc43ab785137feff22d98f31c0aaee495fa28de5d4876ef4d98970fb1870cba132b81753b74054bdb08c9fb85325efd7ff751d61d9b0e2536f01701e1322a4aa9e01c00821517e3e3ad0262c2d90f6bcddd7b9c23110ff93f5fed2ce345faf9b093c36b1e7419c9e241f4a6dd53066df76923326ca5f2a963cb91d885aa8030da7bc8497bfc1c2a0728608417aaceaf8410b5916aede8f6e16179af4729bf2c6a747d1f54ce04d3fb7f6a538acddd73478501a4b371c81b6d1a5f55f6dc477ca9dab56eee308ba9712d74839395761563fea2aeff298f1b34a57a1b0d32e065d7eca75631587a805b0df275b8b263e39a63c5f9a24cac358f813a81ba077b763beb4c577bb137c54b52c5042e26152427dd934389ba92657db6fff2fdf86533ccbc56a0d2ac712b034acf5b62b17043a787fbfe79fa5eadee7c5d23980411914326335aa615b84dbd41442093e104367305ce358d1801f9b8bc72e4eedfbf4693079ce6d99e52004cf00d25e79006a5bb61f0b0a2e0fbf038a134c4c26fe655dd4991da345c6a17f7532305665c11bd685716fa1f2640d607d801a258e7f59d13457617f80fa3e7f44b230b9491295786dc29dee4c4db00606f5585f2b960887408acdff2cb691a34878f4426144629518cc179ea35173405161f9bd556904870d6ca4a1e2a6450f654304b1f0f3239c52379b14af3c91743e0f3d0583513b9175a7d90545115a6f377a3b149fb7d31f4f8f380ee048a4148ad64981cb7e28dffe7cd5ad37e1f3ad69ccc27ef16baf1b5be48a39ad4892b7ae4747d63309bdc0937b591f0b1c95860f8b6e693afc4655bc57966e11888e5f6b3415b8571bdd0fff799ca1aecab9382abee907847a4d2f23951e8add10e5a25f526b959832520d66158cd41cf3dbb1fd8de2108c1a7a6e8b759236b9647beb240e39a803f267cd92ba0487e11ee9b5e94daa6002ce3ecccb8be6e3ecec6b1f96213d4b4e2e849add7fe0337e846ad8e2ce3515039dfe56a4abe6e45b1cda8c49d2f3fde0d1b22e80f934c5d3184baaf040760bb539320cf697b2b875a1e22cec4f14c3b4b367fb7cbe160d0f829dbd898eef4bc825f38c06c347411d03354aefe7d8b22e8c87eb0e5181542d04db09f7db538030839481a26ca10866654fa04a09706e580a24678132cba2172739fa8d4d2b089324b99ac282f786fa660aaa1346fe0972ebd510de75854ff6c446e6c42ba56d4fe3e059cbb0e33dc34a44c11b9d582b9fa7ebed792c035e5f68f88e9fe7af6db4db332555abaafe4453cd568d648f7dba508af82ae8b26c92552f80a07b0be8905a8a400d989e9861a0afab3932ead7997c5aa8e351adca49fb28f14d6f97b55e0bbe943716913dc250342c65f78818934ebf8b54cd60a7f499b2039f90a2b65060e2f1962636d86bca2bc63d8bc2218da40073e3db9e629ab6dd6cde2b93e27c2812a2a91e3c21c34d39987cc28fcc3b614612dbaa962a58664c3fbd156e44b5c8285f736c508e1b3e551e9827afa0d90d1a9b6a396336894be25a17d8df907809c662c75f23db7179b91e930d4f2830bb030842c9074d88bf8e1a5c724f2f732a187c151935040c852e1136958b52277766c20f61d3dd11734a936b0d56c445f315f8b764e2c1d4f9eedfa4005033ada7c7f84d1e7f22d66cb20f66bec89ddde30e34aa0048092e893fe072768b6aec9c3290e9af31e2a9890d4db6f5a680f8ffa35eb1b95058caefbefc3064399daedb6b391a858eeaca10a74f491817fe0a45489a9d96616bd1841eeae2b76020a3f120dec08e7db4a39127f314778804606d6890313f341d1a8c19323b1b40e32cd3d14a22ca229ca1004081737a5e08fa5fd2a3c5142e77dce13399e796b6836998e4ad6af82a86f2405e3c8b81afff9970b6638026f74af89799fcadf06175e398c57d91d6365464cdaaf9ecde8fa3ce4f2feaab3f2b31b1df1860c5a560f21d4df8ddf46d42ba573ce50542ad11eccdb5c280e888777f00e81c9a91f75ccc1fa75aee7badf8d29be00d36b467815222914a4e0d9924c34da880001184fa2b2fde4cc638ea62e81ad96de11942c9ca02adaf35be6aebdd16d8ab386d13a8e7b5cac25abe9407f451f9a0aca8c7c45b7dba74a6bb49dbe831d7c0174599c621c7aa15c68d4aca456baf33df589fd407cc8fca1c655a089dcd5ec0e94b6b3f1942ae2491b68272262eb02bdb6af120e0b1545284e06975fe49dc1c986490bc052e3043cb6dfdfd1daa5bdb1c6e375073e107838aa8b53c79ca75125c3b94ca35de68fb9e075c3243885d708a156fd96f434f952618aad78cbc7f8333f8737b51686987204cb03c1bc7cda3494669f3e50c8a210251d71dd7f8cc4c32b45b8a327173615e6575b13ad4db2e42e6ff42d46e0dff550eadaf9a86a02244e64b47a7471e5d3e672dba24662bdf5c56cd9b7bb0ed8bd04bd80be7c5878e2f48a35641c35658a5205f9a53f543b9748fcdb55d313a2429e6c29bbee2c942ea144985f60b595688a164dd80ffbb46e42928c10b9b42ee27bccc863a9428fd5a554594e69df39a9b18a1dcf8338afef91a1b10eeb3229bb0c6efce1787c76f712891fb2dc05619828b0705bcd8eb2ee2a60ad91bb980da500e4185dc7d241a6750856dc0be4cabf360a7edaa2add54b45b9c538273ba6a72aea7ea2283a968c20b8e0846260c8fb09ed81e76b494264b4f79a40aa1b6adf4f5aebfb5d4ceefa42c82cf7708f53c0a82442c2d1c739de6d258a2aab6b72fb21d5b3399c782b4432b5843c29df2162bd5855461eb679def39718a575cf8da672405216dcf9372d6b0cbf2d879951ac8206d20c7aa60f2e04b093b706ce346ce1ccafa3c2cb8ba01d7c04978bd955589ea0697d9f9c1864ab15eea0f5aea3ca59f1979568e01021fc5b06fc1bcfe260b7ebba5a343cef7976422d00b2b6d658f133fb687fef38e4e477f76dc2f850f921ef443f1a1b2598e3f7741deead531fb9618a45d59018c76a1a0d5d81275543b0b3d2f7e72761246104312be4f1176b4c173c064bac8afb9d2e97d1e72dcad36b61bedc9e356d890a23c5207216632b9c99860eb817e8e5a15b446e8c5ff62549b6126460086590f2ecf826e9de37f8c08d8e2d7a45ccd24f26956af44d3c7cb7f4b196b84aed952b905b25145dd0a65587056b1691e9065dc4cc70609db79f917e3573db2b0e203a0862acb69a6a4f429d2856a3b00f4ebe624d1378f8f031b5138a9fdc4993a273e4c3f0c68fcb52dc90d4869469ec9a028c7e007f146842155608497994d9eb2362813add94ec625015490539c259275a4389d71483ee688160682f14616ce17e51ad67cdfcda8dc2d09a551ff5a2eb8b0b2764689c9c817b0019209ab8b7c074f1f9de15da1b52d3ae441492e763a2eefa6e40da5f54bbe76843c20ed692cf6fcb6832e52547067d187409fbc058e540503b139f8c1ba1f0fbc8905d6ccbc0bfe08b5d71137211755f4b0868c8cd9e96ec777cd5555c5a994b89a4a92d5eb66e4053cd4c34a7b6ecb1f54daeb780f68ce0623e6381352837ef340c6c90204b77ab5ea3403ff6fa4c72eefb8359aa115010f4faefd6fc8207cb459f02d48cf21ead338d8d0e253597abc2650650b633b14d2895fc46ab4461919a4eee67b48bcae074ea10b3ff4eb8748473e5927df6700bdfdedd417df0cfb71aa5ecdc3825060fab3a2f940ed1918bcab7189849080870a33aa010d9d5d88b64700da39c61756abf43f440aee45e1b12d883336a59048c55d102a9d4897ff974f2d5c41ef3f7aaa24fffd872564c96d1e39ec279fa183c1c1fe6a4c29821b2c4debdf01b20c137f152f96c57012101ec5d6ca4980b59e626ff23b703e3e61291dae24befaccd92ba78725c4b6bb959fbaadffb51af5267e47748f502f9addf2896a752a56fa026d6041ebf8ee04c665be1269ad9085dfaa4f3c0a6573a20234f3024ff07e216cef358d86a4e7281863f0f7205e6c50a3efac602bca0ded81b7fdf2ec5f83c8b08236760ee993a29d61d0025bcd0b13f551e853ab651f6039b83e3b5873111f7fc58d773b46952ba011bc0ea08de2a76b8df8afeb7925371f32ad08b28bfd635c84034cb120e2339034c36278d4256c8b9a770be440919af0bbd3fb976a37d66474b75fb7826ff34d88030311aa88ed27bc996aa3d9fb4877f8884468cf58f0765a4f69ccf148ed6d0f0c6749a77c2814773d8460d48122c922a5815453a371d51b13aabab13c10326b8f00d0200aa70ab92f6e9992d6dfa80287fb07989b3550446caf5af40dfa2012c9bf3e6021811aea1dd308049da641472d750b17a0574039b9974cb5712b442530d0614339f22bca784c99bdcb81c822d71a86fc2729a50923a385459c58b8248238969c399ad595eabfe0a9d81919729493f453b57bfee056813cd6f472283a37fb3ea332129170ff86634d9d6d1283bf49ef410664d5b74ee3f90515b053ee095670be9a7977533d921a49635284414c8f54f813acd9f787eb888fc67bef318979ecb0da6173aae06c89663b87d7e1ee0e5658db21a73428b34867b6c32e99f1c8073679dcabd4f0b01fb26a15eba572a3e0cb1bd9f052fb0925822c47ee1f142b33669bbf71946165df504c0023cab0cba3b0179c216f10a32495fb6d47ed2515caf779ee5f368fca1766648f21439ae37e1897beb862fe5d7b396921ea7006d995cca3b3f4e32b3f9e72063afb74ceaf09c10d431a1710b8a457c9c4672a1deac86076dee177d6dfefbc50de6a39cbe63c01bdd4ad160e596c443a046d85875d20889cefbbf22e93693f77c1939af4c3145ad6413f759dbd0e12851d3c243be8cd06fdc531a178f5e7c49f81216a72cb8141457c02438f05cab66e9aaf602039c5d35d598214be544ed1a6b0f9e4409717a018ca89a0db262cca67b7edeb78fe3f61fa8ccd1e4fcbe8b06f60cbf6d24825802f5578299b1c59b7f85c6fc74b7b3285705aca33540d31be0b49a6be336d9a0bea357498afe630d9eed768524507ae8ef8e7f13a8c4e76e9f0322f58bd5bec62e42228166396ef2abb718bcf14ab510451c38dce7010694b6600c01ff3b8297c62c943fe599936dbf8b3dad21b38f5d23d5c32deefeaa4beeabdd96c25d3a0975782b10842708abc704a2e70cbdeabed664f60272e622e750411e405f2e9d9d536374111d6365e42e8c6e14e06260b9658dbfc4aa13f90a8e01d1a2491a2963fc9a57e1adf9baeadc9a7ada8fc87ef8486d6433c51d2dc756d8be1e81c7768d80504cdaa705f523d6b5b57e83a003c5f7ca27fa980d75c566e66286e1c875226556544a7de44e75a4604fe55662c6b52deedccfb95ad6d0393cf047b1b22055e32a817712b2d64176348b55af0ebc6cb57ec003a93822c35b935d8b26d6f9739559ac1c4e8828999fb65c328b8827e50b667ed1d91695ecbcb189d99ab750d597f4b3d130fd9b6a5aa31c1cbf0fd3d139de71b35a660671375f605878a9e1fcb1f67a792a56c5b3a107ce145edd0806d770ccdb60dea7a108c3c95b66d655d0e1dd8efd58d940cb1096bd627b94455cd0b45a4643026a392f0ed9dcf65b853ce11673ebc36ee3357940735c90f239711e6a008d26f5c511e1491afa68df6186ce53045c3a5d0c19287f14822461b897bda7175c72ac639cb7407afd76c9cb80e8c70ee960cca5117d152721495191e466076068bf2479be07c3bf464a3bd3bdf849807c13c5209e7d0aa600374810ea5bc5988d8377abdf72eb2748165aebbede94b49ecd0c1950ded6a6459d19968db5e21571a355935e864233456a785c6b7827f066cc2b87519efa4bc3bcc12f2e9ed4e51fbfb60d1f6b78f8761e0b23f0e2448c370043299f6ea840b2fd5793777a7193947b2d16ab19be4d168daf98227b67ea4a31e9059fe1ca70eb0480e23f9f01dc468522d0ef37109e73f0ae4ba2e2ac747af6cd5c07d76e3c7f3cb8e0b5eb688964ec66d661bff4ab3e90a98a458e6998fc171cf87a6bd0a1172ca0a6b0ae8d77ba9327d7a57021c5cf8c5d4b55d362f6f8d59804cdd88be79525eb9e0318e3e6e4880471025d71d706f2f9c42ee9fa6321479ca5f7fa5db945949c44425a10b6d1d7d095f04d1baa00bcc0573ff1125e625e04d654126561ce5711f70bd48a68b0631a875e201835294a4500a3de0ee79579849e9b2b0b079e631aab380b31b23a3063ebae07a6d4751d7817c972e85337c410df313417df67d95de425b9bd9c702e03a8325682726d1e3006975f5ec46e8d2ae2d1a2c0f973bde4e42974d9249d283d538c9cb0ecceb911177ae3ee92fadb0451754e6fde6f47c0a84e0f3bd0906a4021777b8ea40d38034a754399752790ca0d61d2dc4a1ca967db990c1083df1f230f7c70121b1c3cb826ab5cc728aff81a56a027aa7b6a8c2fbf9aa9629a44a295a3ddd208de0a14e7661de8ad3589e0ced207c04877e9fbe92f8b3a6b7a1422c68e7538fca57ba7d35144d97225ce0d7cd1a0be055e002c7a5e99cf796e468b16de8cd84e36967142071615e8d4f792b39f9bea0c88990235d177322f7661597512b86949a1eb940c3fc0003646546cde88185c9035cbd328c8d3e2a90d18f9f15a9fe1381788592d631cc8a2cbfb0ad26f41b988e0f8a3b88bc9cbd1179d0781eb46e57ad4b1301efe66d3c797ea0eee2ecab5b5728e1081aa3fc06db8e2fe0f22ff24bcd0cbbba872c38f445d8910b634d4856a6ed95f9e36442706fb07e137ada1ac11528ca0d7244cba017e88f82e51a22f86a702a2802ecf5143bd9ff2a2ffc66ed80bfbef47b532ce7592c169ea0d49e55fafd6bd7a53fa2e25dfb0481f38d5014d035cfc0da872707ef3cf1bf991c9f2e8be4851d769d0ff7118c146e2847a508d09ed9fa41ef73c4adb0b6dc44237d7aad5b6e2a8a2aa7e8accdb6e448044dd0b06baea1eab5a78c03bcca522f24ae0f1c4ad2203ce8fb475bfe40bf6cb4415f92b6b6a21b4a62fafdad28618757747950e12c42c76017010c74e5dca3c21dde1a8694942e60ff85a259dcca7fb7ce2cd4540f5537b3002b9ec21952247c804984ea99a00ddf544b835e5f039997e7bff9795b5f9a4aa437321c6a4aefa0b9d061a0274955ba19221dde23c3387304d82dbd407df41d13fcc8353fb410fa156b2e58c3b259b151460a3e24f4a52fcb566c4a3c678704b05d2e2f1b57d05a327b1990ef00f2de65c59ae8ea5ebc1ae5df6e2a3a4d6ea8b90a31e439952cdd0d2f7ad1ed5d292ec56016af588e2426b3109876ef6e4de4b6ade01652856591107df824a5b68828bf39e2ef9ad544910b1ddcc8f33e502a991eb988e4517f21774b7816a763beb52f344be13f0dfe042cf148ead3683e96c476850f6cd4c5fc896a6f17d313e427167038949c968b279141cbb738d4bfce09e506a0a34423dd172bd649fc8552cc8281c915de1152ebd6695f2349384fa6ac94467c8deb96ed96bc3454c1c2871dc236728e90811f5d74b1f552c516c6dcd0e143b486cb6910360ecf99b391c66c62379929c215c4e34818e74a26764526ae3123505ffc86b4aba945dacde021cb41499c9e8a944af71a432f0eaa7086942e3e20486ee940575840ede582e8adaf6d8a26be5a152327922a95476b15ee95d3117721b684dfddabfb8d48e4fd259020e1156737d279bebe303ea0b120c7b15e91deae47c8a433a55e8975368dbe4eca06e4412de107c8ae91ceabf5158262c864b301b5b7ad3a4c7e8d38ad088e64db8c0ef4ee3c43547f3aae4bdd6cc68a43881033dc70208cf41e3c07f514e0d3613620a5b63450d868b92e896bea811d52f5401950fefe6f5a059c1a64e047cc2cf00939684e71de72db555b1944d4118cef35e8e308e945cd3306a63f6f240aaedfab1e45e2ee220ad7aab1ad6b337638d9f7105a6e89ce1e69e43c28b4842747df4c68c644ac42a05e6a0d2935a0ed65141f48a193497714ca7656b4a1df89b0a0a2ba4a27bc22e7e1842640c9b32e00b5c2cbf7f1afd4340e613ba4ac59548319c8a1bfba0beb546d9097d93b885434d8dc492c1d6ac0e6d7be8edfc2e4dc576628bd74db10f35056d5ee4ef62c1d7d9793d1e4bce966fb6fbf4d501ede69281fd0e892388b1fcefb935875e815c0830e1614f7888f95cc3abe3e8fd563720b69953a9e32ee113efd72bfa61155bee210cd4dddecb998eb2687a86c15ec47b6fda154ea88c7e2d5340c9776af147595f77d94a20fed7c9c0101d434c2b9a178bb7f1b2858d874823a15ff3809e32c3a52ec7944fd6a2e276286f4a1bf29e172dbea12c84e82193b264072dba696804dadada59d1e4986b22f05c95bd88110990dc6ec5ea09b1c7cd5db0bdc725869a4efa42d020553683a2bbc415c57669057fd391c74b9f0a92513c01bd4b40ddab8b9564139bb348db3afa59adc89f90c35a960706bfa0b75b407363993de2df042545c42221c2f0683178416ad4f86ed22316cb2c00b835468c0e36329770839ba100bf05f323d631cabbcfb65250e6af067f538960e9f114b0f5b5db41adc1c4ee7a665699f6d7aab5f207a0f840160ad4713dbfc90bada129a8d31985ebba6d6d222d946f34a6de02a8f1a4032106bf1e06142d458efbcb52842b6ad622e230ad1799763e2b9bb2586f00244e8dbb0cc17201430955b482f3f47489917f2389e711d8459befc52bdaf5db4e74014e60a3d89906dfa30e8c153d4ffb145c5f7d4e3c60603839a08e3d303355f0e2f24b032f41ca99c3debafe805271808bdd512a078e4060d3f681641ef04a61bbe92d723ee098292d9be479ade0957a10430087d056276826ba2eaaff6e7695488cbabd07d1108159d05a531523144139c71ba159ab7b0965e2e80efdd83dc6e6a6700ea83526c8e64986baa44f6d16b056e598d0351fd53e584e9a47553f5eb0857612b4c043eaa35774cae5740eedaf338ee688fd0717610b7ed8757c3c351dea861e313c400ba5a589a4378f58b25f01b34b1f140d86a03d1fb2f501ed6d9ab4eead03aba71fcb1f5d0f913bba776edf8e29511a5f1285227786802de4005b29aed83d671f5d786f3690906f03efd283b6544830c703b0fa79427289f8d46a117c14a0885c02cbf5442637d09ccdab447ad07107a0b00b11a1be30f7ba15a43b9d6f5db88611dd71de6f79cf95dd17c13e36ca0e790cebdcaf5b44bf5369a2b04e63cad4d2c5526027aa8c24ecf8629ec616bcd56befde8b31095de91a05b7798f8c2651690697c23e2c9b3733f06d702d5505ddfb1af5f859a208c22e926e6b30ba69dc4dd9831fabdfca1ff37e1ab6ccee6c06ac1f7c1f463c0d22fff9c86ee9a4030210e825c4b829a1bfcc60e8fb43b58ec10fd9158d7546c5e62b19dfc798d2cc54f089cb94f6db80d1ac360a53b41046b0c93cc635068db6fc1b7236e952fd638df64d008c9f7745afb2ee7634e4d614e4d2f494c89726a410a0793fb42fbe3e4b5b694d5720a271859753300aebc17488875ac11e18301777e758b89915043dde24ce2e5682f68731dbdb49db0b90fb49a5972fd75c726a23c8932a47da0f518faa88327e8cd9f602c4967ff4e24a4b3826b82702113d730048c112eb87e4e69bf04402eba68d5d67929aba2ea9788246883ad32df2015b7d351a9ed6be3007ab2fc7e89f58a9088ded987191c6f0de2e81537afe1fc9f415e08d1dfdf065a6083bf44906922e8daf360ed8ea72edcbf55d4e66fcbd9e69774d600946ae7717f8a8a1e22317ea5ef04ce094050a188318056045ca1d655c27a9a782e094f317fc655edf87a6e8ac0261ad194d7a56573b5b4a4ffc513df64ac6243e2ddbe699fd5568d87d6761919f66f02f1f809942e706aa6d27e1d83f791e2ab4401979d6810bcbfd297f635d042375ff1c4fc0cc39bfbce12ca8c5599c3710e9803ca1d0731a4b41c31e3d150323ba023872a2e13d9e0efc7a3cce501291686de39f138cf7be2ed71790d6b2a7db9b0603069f9efed5d815241b2c065b94ea720083424593b3bae9d06fb5b91e9adff23b59700be4cdbffdc2ce6b76455e892943fa7f69a3f0d9069c84c66e6a4a21aa04e7941a754cf6339fffbe4f2d4626d7e09e05c4046f32ff05ec8d963cbe2efde1a0bb740650b3b6d4362533dd22c92d5a7c1eac1127f2805ee9d574bd277e022e1a6708ac64a67cb028ac1d0e2f45bb2d18793e77612eeb07c1f07cf50fc66735ccfc1f31b6e5fc3bfcdd437e416fea917b2990b6edfb5e291b38d14bb58060a7dc4ab3088bd48b688f548e4e049b41f2be10f7fc40449689c5592b1c38cb33ed0f01b60b66a94af276010c48c5634201fe3d5f44f02871de745a07baa320031f869fc7c97cbc62399d446d537094e198efb05e2724281d4229df5d595e1b03f80fb13c478ca3ef4d6175286f3be3d59d17dd370c49d9ab87fc68a0cf503ead086935b20503ae21c0a33bb97958e0ff2a4b27582ae57fcac616b7a60469b78dff062ada3db28aab6a8dae204222cb12015a000044b81004aabdc959bb2d99ce70ae7cbd287f4756335ae8bbd7c5f7e6d408df675e57bb97665f7962949192c096e09be085f6454aa4442b164940c52789151a8b23e41ad0000f53852a80f9e8aa80fb08f8f4f9e419549ba3c632a53a64c9e9929ab2c45da4c9236e3206b2cc7cc939f7a102506493d989ac1f1549cc171fac142443ebd6ae6f4aad3a9ac516826c90aa53ccd9cc8a7e2899c8952a443c6a752250e9989529443c66e738ab4194556a01f4360fca89f79f02406997970269542d5ae904136bd4a846c7a95380455bbe28b6c2a31cd28b22ef1c084512815152d8cc8a557b572e955e21015152da8e492cd33a4cd27928536fd3b207f0c814ba0490c821fc4255c9aa919424c12592c952a7148cd106290c862129a4d486e882551fc241dbe4a1c6293058c12e4b03cd2a6db8c499b4da49136fd6d90a218a4f460292c85187c5508be0a9c00cde2ce0a0c415392ce2a5055c46691143f0c1f0c49a4528ea21ca4e4ef55a4ef55df5702a122f9c3e6237de253f113756841658b7cbf12c998efabc4213ab4a0a245be3687a4cd20d9fa41bae153f18603e8214b18d9bb259254f606d0431630b2e7953c68fe72d478d7f34022954a1c82803265cad84c226dfec8b14d7f1af2c710d8fe77edb524aa05152d8cc8ddabc2dc6941450b2ab94b8560f725e95c42f5f1f1f121d293bb328967ee55e2105aa64c993299bba4cd9507f964006ce011c0715647a0f53acd59c12c520729840aee4af0defbfe031f0c3f145f2c7dc9f426fcf8f4a7b396a84795503ff5a9f24c7dfd9e8cfa9e3cf33df87bb2e97b72e97bb2f83d39fc9e0c7e4f267dcff764ef7bf2775c5229f73fe1e4364d64490cc126d983fcbd27ba2362d56f7ef35717a3ac7e77bfd5b253e176a3e1bcaee3dee36ac29aae86f50e63b158b1988a0642b784689a78f005ae5ffdab14325e5c506532994c269389a132158a1c2972c449534a458edcdcc9fcd56a99e62d82824709620b336027859dd488964255bc30d4bda56b6deb4eb5769d27334eb4d3967659276c0a4eb4931934a77eb74c147f5561a7d357d8a9d64ee2e944f357cb6427da8976a29db6b495969d68a75a9b2528b00727b21e59598bdf61b3ab09bf4e7360949639abc35a79fe196398b2bdfb07047766fef43ba8175f25561a9ae7317dcdcf98489e2a3ba2f83ca63ffda57925a7524915252a2562a924f5a8a7f95a9a30f3a747616687e6c5e7517d0d0aa8174b9ed4d3a8fc3c924fee918c33640d4fb2fb20c8e84ddbdaa88ffa46ba8fb394e81a085abe28f795dcdff509bcb2c9b4d9d5566bb3fefcfcfcfc7441dd600fd863193988720ca9a6a884544194d5d7fb4a3f252ffdaa519ad52520934fbb56dc0523260aeacf926de6cf1297b344e4a4d5c42cfd90f1122dab8228abdfaa825441d50798fb555070d85442fe32bd0ccb737c55d0ab825cec6eaaa0eea60a520585a251ed218a316735aaabb92833c52191c814696117a2d947863432dcd266adcda39bbfba925305f90bab825447dd195eba20dec45b77530575b7ee28954aa54a5590aa890a48f5a30a52fddc58013e5541aaa0dcaa2055500ea2992ed93003da4c2693c964b256cb3431a6d5ece188872a2786d4ba8b6458aae1d7528b0bc5b75f1d67226c8e81bbbb049f55169379f5d2ae15cdb15f87a5cc1bc16e0a4e2b0026ebc9d126bb8ef7f54abd4407153fbde4087b2f26eb51155365f446899bcb82b4f4955b0859f0d9b17685df32d27f5d0d4551dce896f662c2a55a4ba552ada552496f4a9c10e93bc2ca904c8612e30b5c7393d09b0e50567f5d8206c705f11582f857279f032a608157dcd3f70374dcab04fa74f65c2984ded0e0804f7af39b0a1c12e3388ee3381b67e3074f70f5d1514173684e052201e6a139de01235b4d006670dc78c3f6c5ec596b3da802753319102d163b629665c6bd10e3a1b503f5d1e2688e2de15322d76f551f3caab77e88d026bd397de43e798419a441a842d185ccccc1d4c867bdd523ce4601bab7bb76adb58ad0ad560d9f75564ba84978c9b6d6c546da366d4fe6c89e8c2d0966f287eefeb67d25d7d90d15e0b3ce663353173114c3dfc161f862c9139ec2c7f8bba413fe2e297cb1f426d39742532914c5c0a24e2896a1f7ddef923c04ad05ad0581c219c4605c8467a7b7d6e258acd2a016b50bb9eb19b96b98d10b5ab96811ffac4a9cf504f50a9e5981f1ec887e1d59cd9c158c95fa04969a654b77a9a4596c964f259ee1af3cab90a9ec2c9058964660e971a177c309ec660ed2237c39722d4f7724a6f590d4236aacca84c02a1c6993ce14e00021432803a8bb3bea1d86ba9d1acbc2d0f8a88e4a267b54f357a9b447b5aed6ce1ed19c52ae5d1da8f8fd9e8ce6749f43fa7ecf0634c7a85df50911a31199c90da88745f594a0af5b03d08561cc2806fea2cab8b827c057a8572406ccab64fab3f36206a63fbb306a34e772a13939f09ed01c2b04a55f1eebc6d0973deab8a8a3958e32ea806ba0d683e17e81c5a836c8fdf6c85f15e8e8681c6bf698bfaa2c16eb237f551aedc86947652c08e4af7b812ed005fa482499bf4c992933651ecdd5be3da2d41d0798e3ba3fed51d791351fa94caa8c44aaa417f3a307253c194d0649da714d3c668f4adc4fd78eb81cd431943a0cd46ba8e5e99db55d67f1117f79cc63376e74ad878086866ab29aece8e8c81e5531a167037ad32f9253f09b4c5f32fdcf85e1d7c1bb81afc0889108fc24766b18ba300c8d19ca5a484f466f3c2c2acb5382b2fac518bfc8ed71512b1aafe0131c75603a23172a3da8f3a0720512e8fd065098a95da47710247d2417037ad33d682a35ec9220ed6aa92d4dc165a97b13e9b0eb2fae0bcaaa6fc64846185098fee2525059fee27ea0acfa5b8c5738c70226c62ab94ac9f56b351a9f37c1ccda1a34bcef0e70ffbf6a6bb5ffb4d9bd248ee370a0a0356bbf8b73e27c97f460188258daaca140fa815e771b7a9ea5d6526bfdab8305e8af3f12c791ace538ceafd7713623a4d875f9c537dc51da0dc58bb517d35910ee9de4826d693e8786cb3ce6b7dbed76bbddb2dc6eb7dbadde6e5f687a9df68008d7dc61b94fd4eeb6440041b044f31dcab5964a2408964a6009044b206845131aa6ba0f964aa552a9e4485219fcfbb795c127816f6b8900826089e4400bf974aac105f6c7a0bb1884045cc224ce0f20428814d9998091365168d3853e68041aca270eade278c1dfa612486a271dd42eef1f556f94d59f2acf8b2a716e38b3276e57caa0e5b3de54333333332ad53b4cc50d6153490c8f905969f309222cb79bcb505d438a98f66eeaa3973855748ef36c1d17db2e0f7fff0c7317be78ff4924cddf7be73ef3978579ef3412a9ef733da4de3f9249df91e798eb02baffbad346eede59f54f645b6957f7deb7f79e0522f5dcfe11dc10a2c0c11039b8f0420d5a10848b0d9a98d5b47831668c1fd21839fc0f37c612a029d35998322b5bc16bcbfea12fcdf1ca025c6b12d5396ef6841da10eb141a88cc1e4761a9c77475c0428a6a37d373790a921288cad0a0e14b0e9f002181e8480ca8091c10466041fc8c00315361f6663b6b8d560a6050b846e18411a6088c014fde062f85246f7654c080605a118060f5f6c75810f9c227c491826a0210756b49800003784e912c41819a858e20b111e951b8e89031142c404bb7858388ee3b896b3a8b5d6ba7b128ef31a61e0fad75ef29d45df23a95b6b6d795a2644448810f1ae73794f7fa75d1f480a2f0ea59c0e1021edfa4468d9161501a7cd9619387ccae29e4452ca3acdcc3de58ae06c7f1c899c3e45d2a2e6f998f6ffdd39bee842c755ebb5062048698ea5f45e7b693efdb992d66cbbdaea2b6802e47cb439d9da2497c1ca95baa4f4c63a977b6cad4adc2f407057c6f89d41c108680a70708cba285090c617194a548cbc3cf922831518b0dce04f680813c40f656420324157031b6e094e1156bc8a47abc029228a2f3b7c1dfb4a557c28d1fdc10a14a441060e88e04107d839669c2b26e0220a2f8ab818324107c50e0e069c22c6fad58ae314d1312e0a31aa78465c0fa735f67f1137d3591820ae0aecfbdb3249674a9b3a75eaee3bdc881021d22e2241ec1321d2aefaf48bf40bf4befb1e87ea0011428282b9df3285442233d0ac364203ec5f3f7be945e3c5788aa9338e18bf91b76da77c3e5830b097e92c8c0ee338030d9d83898d304b98e460528e69e4ae3efda976d91c4cec770c97510a239fb6a802d21c16d31b0fdba45d378ed45a6e013709c7d9f6527d683ad4ab91829ba3cbfab7bd681581693ec7eb759c1d6d3e73e4c6a158c2c827a54699de6e57728391fb7f2644eed799e59eddb4e4fedb3ee35733d3d9ad873c93e9ec26c3c885add5b9222e32575ab2946da53acdd91950968f048662199ae6ff385631de0d3f929172045b2d92697eff771c89c0def48acf1442301d33809d2d9c1218a3e64a2f4bb792d18e11e05f552b67a42183d52572704557f1440c9f2523237f1521cbc0253063705519a98cfc555219a98c4a462a236d1e71561ad88bf8ab64e4acfe92110a25a336bb488a283949e70ee09c04e7faef053e62c428f7ef8801d72f024385fd54a94fc9b820c5110cb016198023d792d61ef64b3810a586ba4942281bcdd1919b83e2a43ee7a45df6392123703eb9a01a943b2888c88bc8b0d56280bbffe7869e1ba239ca3cf7231444e977b64e4e8846fadcdd3defa315e9ac80d44a0eb0c4964c21e34662037c42b8c13e18fe02054d2c985a8594e20e4fcd108cf45f493180e96c0c992250c688c9fd44fa8bb1f3aa69050b6433cf1342cac10dbe4f4c0748606b726210cfa779b3741c59612472b5f3f15429594c1982f17ca5d742280290bc4102180f49469b00fe9f27eba00a969d5c6d363b41c8093acef2824faee6d19c5b8537cb523b9fa614b87abf8237cb3bb257eab4494ef9de268165edf2f181f57bb1761dd107f879d8577a593ab20b2c44a7cd1d3b64f168de8c12e91011dd98c2753ea1f00a15e0f36b6c80ed90ebe3936b75f788aa16d40c6a0624f379adb5d6e20097f9bc3da2a0401439431ab12d00bfa5b431de52a3b529fe5969268cf1e313c6278c319ec133a66e899197d213f14d2273e8844d367ad36f13e4af5369ba265b68830b5e8dd0652f4f4ef24c3e4db6b6d904511780e045ee07c146733ac0b125f7e3b8623374c388e60c7540441295371c44386638b2b46b8524378bdf8f83d62e0f0796324cbe92cf0eaee4be41039b13e47e9b1ee8eb944365f5fb20c4690812904076c2c1460cf069b2e50e7aa0ae9493ee2088e6809fe2819afd67075072eac96933a339dfdb08a239a4b70922d190fbad8ce670dfd9da55f2622492537e2eec248a24a2d203c0097cfa876ff2de392fb9e712a81752c8903d2f91908886502fa488217b7f4f5db8ad76d1c96ca716602f2d68b2996ce1835e78226db3daf348445dd013900f36b1769d27209b281f42e9039c826c844e40e129e5a45da69f3679a02e530dd4ec6f130ea927a69418f904d443f6def463aa810bfef269b281a477a3c884838d1e6c41f4a68c4d904d50f79537da7f859afb46fe4a2bf39c6b42a2e1e45ec025370cb50c0c979c8fb3b82649e415ea0b32bde14a2bbb31c3a74d50b509ca6d1364136412957a060d1410ea87d29617198ff8e8236da48d3c03c0a14b1a309e1606908213c0c26f811a91710556a58ea3d38cc058239a6365f5e7c7e8c7e8c7c8e8279c895fe2380e3c724da8b0f02f797325e2bc2781e0832f921d9619c27df72470df77a4e748523794c50afaea6c308a19c58c624631a3119ad09b36726b6d1806b9911ba180442c543e47b55aade6af71ac8db5b156ab8d5f3e47319f23b5d15c973673705cb8dae85dd09bfe2edcd6aed3bbc8fdeec56d160d3f7277f72e6e38d29cd2d94337bad4bad4aa8f9450abfbf1ef460d9f5cad5d14e48cba25adf287b3d58e70cd6778e083593d647f8aecc0c168338ef466269ff6a7b333d017c7a5f6c3d56a6d04bde9aa09f40592280c72d32e7ebc546f123e8904492118ae06724041f84fabb632017cd29f0096a4f2841f0373b55a773def7a9ee77924aec6d5543ea81c665dd7654121f6240af4c61f4441003ad4cabd83a9b51a75ee2507a08ca159bbba775b9bb9b3d22e8e2bbbe43e42d63dd12eeab536bbfd766b9343a1fa681f2a5554714b8051d04991fb4724eec5789c9dcae0b38b5d2221bfed781eefb776519b6bfc18029f7e4bc27d4bbb66ef59da455a55afb9cdc1b48b8e1d676d32b5628a1264ae93b5d91d5145f779b626532b562094391edf3a7ec79f2e63fde94440a3100eba9efa0f1ef51f3c8e273f657a54e967c43f858fc13791bef4bd783f7c90f45f3dca3783aaaf06c7d5d431f4e0863d1a914ceeb71dc7d5ca55ebd584a732d8967e6443abcdcd4dc9d302cc6f9ded7b60a00e9245ea2857f56f5825eb75fc0eeb757c7d1d3afea6e4d151763256d90363911446c52045ba580d14d8b9c028cc69331d248505c0087cfa2d6685dfa81f75e69f75f6c19f9586e3cf5a236badb73663fe627dfddad5d8af6ff2b0f9faa35d35beafabaf4fa4daf80a7ebdf1f533700449bb3af8fa2a2461a15d1e7c7d9ed29276fd4f937699be964e309476e5108a02802366acb40b84a1ca72b33c3bd907258e922ccf6e660442ea713cf9208d18847c904c91a90f4c3248112cd267547bf8fb93d020c1307e63302adbb2ff05999080b4b83fd5070907da83069a43d281def83b500866e0f303cafe9fcd5ff7623c8ef5b3657ffffea26691bfd057ed4259b6eaa36af9964187415ddcd360f4d2b67c62027fc7d1ea96366b8d6c9828f4c9a08d46fffa27ce9e422d5338cca920f645223dd96b7daf61bb2c13600860e07efb379ee0ae7f8307dc7fd625670f19c1da28ebb44eb773a9b7131fb4272670a5d19c2dedbab524a29452fa558b4802f7ab213605abd34db3421ee4418f717243a94ec8d20d6a1a51f5d1596a8f7eefa1d4ae4b534ca692fb69217f71b28f09651c24d4259ff688dc312b94a5fab0425822f2d959727f075870fdd30ac1a848a447241b4645126ce6002dd37164d8e60c0d905aa7b43bd5ddddab12b8dc4f57c85d6d0d50792613d129703ecaeaa753dc7b517ea2207dd92fdff5272a5b9cff1c6c2aa5ee8d1e70bdfd4345ddfab7db82f1f90c072468822447c66d26c089ed6b8980f383c8095883f20297c61333a06030d3d91330a0134f649083275b5c2a3097e9ec8996d9adeb3ece7aeb001142a42fa6c974268418d9661180673dfd3fb0de367380c0e7e546a8b4ab535baae514fb0eb3e5aa04faf56b7976a6a5b582ed0a3623a9b4ec3c6081c5ed2e024b3de0fab67a7d166191d11b6b33ed8a28a5f62bf704005ab470eed5ba5b263817707db75e2d13b87ebbdb2ab34c709472d6bbd28e0a4873544b3baff3bc2c5d9656b5defdbaee8ad9c72531c42a81c1aebb62067249743decb78762d75d3113bb9691b806cab7be675abe19f44226b0ff0dafbfb8ae23cd27715ade8923ecc8f67e24cec60886b4fab0365bad091c8c2b8dc882385006438721246147aea511247010afb4ec5e33a560747d19a917a82faed471d53fa918210cd587d7fded2b010d94d9988dfddb180d4dc9534e212383b5e04c24d1c40ed9988dd99825b23299ec88d43d087a1e27725fbff63e7ceb854a6a3ccf6a8766fcd5fb9b346f435f76867bc32a92e09567a5c97878a8a08981d578507634aea8d880a621573b347fe36f943c379ee606e906b9eae046b922916abe060744a7078740d96763fe02819ce57f2f908d81402010080402d918492c039fa0ec03037f436f65a9f79a57a964fefa88401928eb14ea23fa883ea2f323ca5e43da7c8a44aa212d2c45aa211d4622f5cf083b32a9f4686a901118620b169847d23c7392c56c50f6b719a77ce418924868480a1b493a92ab223523d923596160a57987d1943c2dc068ded2d4d0d08cefb0b1864626a37d3da856c588848484848484849c95e5b7c0f6753c7c9d6efbe1cf586997f7357fefc5781cb9b26c599e994f796984fef053686f40c3505fc3a3aa51d5d4d46a7e7c95ea6d54634d1916a437b4b6dbd27d4f484848484848280c49081fcf000bd11c6ca44a91ab9d544a8552a9bea6f4da0a7cd5d73c8a5ced8c8ffa1155aec0af01518f5209c94c0fc6dab542929b4fef0f0af90bf57d27527c14e8e303920dfab8d392d35209f44191347f024ba0859d4014e930106cb0e344d5e333a4499d600f2f4c752792da2a154aa5fa19d4cc0c6a464888c8d4a30d409ae3ac21642db516f471965b1f7f35cc6dbee66b4a9e9ab729794a40348102d682cfd0c40eb02af67b60270894fd5b2d1baf5ee2dee75e8cc7f11ff4b1b6497fb00b4db44cfa12e883f10cb0cd8ce6d015647fd087e69cb2631ca32c7ffc949e9ed2d338fe679a3b35abaff91e980db9dab1f9f1c792677c9bef81812bd26ba4dbc01aaec8d58e8d5fbd8dd26b2bef576fe34772b573e3c7bf51bacd59fe63b9f2de46697d9ce56f7f9ce55e6942c3c61fcb55e947a02dc32fe88381dad52b24b9197c7fd0c75fe3c3b8237ff9bd7eba2aa7fea33deafbcd552a8c6bbc69242d0c243d70b3473acc23c5b7b1fea965ffd13f376c910335ab7e0bdcafe31eb9814acb670bb29c02e94bdfe4596919fcb305592eedc8de9f3fb20d49612a92e6147982b9a2c8156c3ec37c82a41bb142ad61425269d9be484e193f24572081da3ce8e18f7f630cf6421b92c2ba1aab05956c411bb2611fe981249eef129f6cd3a10d69611f796bde6135254f0bb09ad7691bd261d4f3c6afac212dec2b6bc8865d9b77984dc9d3026c2c1d06faf8b4cbbb8650aa69a600ab124b28b1020b408ae9749a799f29ff95f4180116bfe52cf1c517d2ae8b31922319fc9344a6d0fdf7dd831d0982a4f2d371f169eec4b2a1b4496ad3fb9f80bf4b25fdf8bd545245094d0090c0dffbe77f82f73bd8e4eafbfa77fcfbbfef92a7cad77e3b772f4ddd5393e969cad5f718f7b7dae9a7214ddf247e2751ddafe4bb08a505d17a9d56bfbf922e8da8406d5e5267894fbf076d78abc77c4bf61c8c57eb25751ef323b8aeeb3ab11cf2955d596489e971f820899fc24cfea63fa11f974af0f79bca7e2534ef3f6bd3fb12d9596e7dd4ad3ad39f80fa4795279894e037950fab1d0db9eabaaedc493daae4c19f7a1e1a0aabdc9b3e02f653afd34e2a5bfe4a954adc9fa65c75dffd7845b3dac16f22fd31d96f225124751efc4a6a5e49aaac3ecef21efff834e5a73e88b1ac403e4f6a4a5c7aaab29554d9c16f7a254a705989fcac4241d0781ecae4910da54def4d640f39cbfbee2b895ae1e3e3e3e38215cade5332d9b39d6148640330f98aa1e53387e7c14eeb93bdafa4eef4287d24e463e5070ad02c68c8a3b46b556d6d7a69311ed7a46fa72fa9219fde8486be656f867c3a94ec793ea32cefef387a5dd73d0936773df3973784df7bd0c611de770d7cefdbd65eda4456186d17e83d8849b08a45681eace22a02a6c7a8efca1dd3e392a7c24ca8c7a4e96fcef2bc46dab2e73367792f420c9b1e3fcdab1e857a9adf413d8df75d598d9ce53daaec1f67799f2a4f9f19b907f3fc4dbfe36f2a573bfdb85c75a6dfc16f323d2e77508f5165eb6272f53dea7b602672f5e1df313dfeef71c953e52b77f0a3bac7cf63fa1ed8e9f37e158aef64da75f6ed23536d86df6a8bf7dea55d367f2d11b3c81e18effde662bcf723ebe3af0af33e14bfa6a2862cd9fbfebeefbb25f82692c270bbc22c4efb9ed4d33de94cd21d79f6e42e05f069eec852fc8e3c710b99f4a7a5a4f20403fe320095288208220ca3255e78132b248081133508511b0373772e87a226d9dddddd2d17b4052f7801d5229f3c8470011745432841440d447051148509292d2b95473e534232e4c6a248888b104604e951871660ba30a9f2023266e86162cb1442d8ae3831e30b8703172cc27a2a402d081c647f3c060144f6372bc5e92472d384c8a6dcdddd3469609a436df5bf188819003194fdaf18c4c7336ae035c18296f2030fb9ff1679b9c5a00c229c4842043698f1c30db2386a414c09583fe5807e48422ccdb10e258bec5060f0e48cbf6d858ccd8230729f727777e3581c3a13f2c4bf1ad1200cf269c3071e6e7cc0e2430bcc34405312b4562055680d2474b8d806294420b30108292c30b2a1490e7ee39adca0870e3285906d62d3a1d682cc6cb8c9143d0461c10f6d0c192d1840a63332b60851062dfc604cdf10c6bd95c9ec894bafbd5cf4e0e1a3499108c0b861071bb4742bdb1c6a7db3f3148a52da6754dae2a83b7e1d8b4d2233652f11111125f230345de0fa1d2e5328ea56c5cad47ab5d6ed69adb596e338ae6ba71cd7d1b28201a8a26556f7724c65fb1cb6d65a6b2dad443905fab4e46814998a2f689142924a2149dd1bc36c069862fb463ab184ff2e1718604ab10ad6da54b648b2fd94739dbd626de765c98bf3834c5f455f98fb4e94e202506aa3cfa22f1d1d7a9305c65dc782402a7b1e47a7d0a109a02ff0c1b2c6681528a8319a8344ee0bd4578d552af55583685de20aa554508a05ad415f707daf134b20b3579a43a23a35e6e30b7075afe4b06842c3462e5437b27d09948073f735c6597f557618a5020b2eae7c912d103a4d710548fab5288676c9c05ea6453454c9e78d626f160e8d99775b4f00d98b6f88c70475ad2b6be26486c89c170b01011943930108080819a3188e8280a001a425065768c1ddaceb82c34c67529eb84e48598226a50a0899ceaa88a20cef76ebeed6dddd6d8e3581663d380277efd9adab70a44da33a037de17cf988bfc6ebfed4b648c25dd98ee4ec77b633d2007dddf7770fa3017ae38fe3af5b9e95e5fe9d9f71d2298ebc15d8fa9f3b96f8b9d75a6b74a4ab4246968a5044c394dc37e4fe2b3ec164a657c218e5d3e68a83e3c457a10e055db2bff5287586235ad96da9faa9ecb48bdc41f62219d0c8fe4394fde78cbfd7fde949b3d0d10a2c5cb8ec2f64a75f16e6eeee14763305bd360c67b98e8507c5841d0b98e3bae7bed29705288b7bcec671dc8337bcdd57accffd68937b8efb191a3ee951500f412e080a1a476ed5559104ef57ddd72972c5923b285d15e2e8d107e8eb7bee59a039fddd9f98ba90b96ff203f4867b21fefa4a10d4fa42e6decbae3ca90b58a037dc631560afbcd4e4b8f7efba7b31eeba7751acf7d61fefe5388eab32db601324213029d3a2186af0518167322d8aa10bd704fe322d8a210b8f09c69916c5208517e3e3323ed4e02101060931488cf7e92df342bef753edeabfff34a7cbb73c91e430f1fdf386b7c44a7869160573c5348b2a41a659b4ff96039012048911039024645c00015962a6650619ad8a188054b1b5d220aae13a012283d22c9acf8b4938688621c668513184171b99ce8628734d400aa165b676b0ccec62031ce65f4910b2b5254f08605e3617d75adbb296479b66ff0f7a43bfd5f686b0025c9fdaca711ced4afbdcbd18734ebf922704303105b5dd729d77ef77edad1f09ec606ddafbdd7beff540230e46dff416ce8f4b5f46c42070f570dc70bb7bafcdc6dd7bef6d97ff2dbbd288b3b0e0fa46dab446aa8edb8cd86cb60bb30dd2607477e79c72eeeeee9d53af2b4b5aea68e0bc7b45606dc7536d57bb1304d9be735de7e58fa16fe97bcec201045293337120bb474dee1bec40f6daedf2a80976c0a9c93df53ed0aec46b575b826c8bb8186c8bf80b5bdb4118b86953dad4bd833070cd20c725b9997bfb780630fb289868b8e52cfbb50ceb2df2f543f8824d37d3519bab13c0d26cf9ab05abd682ab2129ac86dc5c6ee064218b257cf99629ac86a450ff2b7fdc52a74dfb344f6087ded80749234d00fb1c04e88dfd07d01eb6489bf6bb750481fd17e05f4b1c93e9af222d8ba44d6b3a32dd7634814fd32d5bda7577dd5c37d7d692213866b4668c7e6dd22fb359d5745285bca7c1cdf1a2349c175c1f1c5dd235218a209ff5a64a192fcc606082c30860fd1a6b781e9541be25c89ec7c48aecc5d0c4a341039fe6add86180cf9b6b98bb54ee9e86c9dddbae088659ee9640c9dd783367b770418ea36c8b5cb822dba217a4644b026bda8e656fe85e9a189f308420308f221f191a8d22236badad3dcad4b20f2bf869819414d8400614d8d9cad6bee9af1a60c698228b1dd5600798add91762ad0dd95e914512d986d95a6b6d8943f8824fd3acb332520690bb2f5e01115ed555a594e2115fcae351224584507a34c3639b674fbe658f25d423b5a8e7512f883c126d89803394fb7f504ae90a63dc5406b625b53f0a163f21d6893496e4137cdff77161aa4dea79a510b2452271c2558d26e19b4aef11f9783e8431f089851469f37b30c1dedf9ff1f8b0d0a6589e3d99fe19e610482934e1b459fab075a44d138bd1482344ec3ed448d620213327fca017e630fca73ea82bfc4e0c120ae9193c73c2b585858a4144a8a909e1cbac45e2b4a9fa7e16bcc334249076d17cea5134aa31639a2aab1314256a53062c1c75b3b21b427afaab9d1a5fc5fb1adf035b91ab22366f53f25498cdf7c0c4b72159a83eceea2791f5a736a941154a9b9dc5b23a216926913be02225f511b832a444355d4304d21c5d582bede23336d18c4c1730d4df86cfd0e892bdbb9bbae877a6648a94c1e85d6494bb3cb1181f1e44b89f0714f8c42bd84c7d6aed6e5e3d842ff8c446c823a82ff8cdcaa35d94479b3928ae7f6fa53b6dd623a591366bef68029f61bdf90bb8a06cbf921df7fddd9fcd9529f45bec858e777b18527b91b451ee9e025a8a23c3a976d970ac9d6ad76d93fe6943897bb971bcb7b4e5890400b4f1b3a8adeca5bbfb58dfebfe035ed779b7abf92d0c30674b7707000dbb3b67b91c9e839adfbdb3ebbcf2b4d97bb7ded18ec4d65a29ee7d106bddfdcbdc7f524a3ec4b0c12406940f8730a21438a4e1c3859f2c409c22ae8f3c3b830638d3d9195cf297e9ec0c1a1018f61391dab2964c604a733fa263a5bd1ff9ab6dceaa7f2faea4579abfbc095c7d7033f7f1d77da782deb8fb64e7aaa821fb73b3eae3fed41e1c1534e7d2406f1a8a7c92b82a3e9bcd5fdf171aa435a874edb645350bd5d0000000000003150020200c0c07042291502c1e69a2327e14800d7288487862369508a324c7611c858c2186204200010022023443b4158000ff69ec42ea38e690d2e044c09771405c4011f467ed583ea67afc5fa7aa6a316d5e9a1dd56cd1aaf1b3b609c825222205545447574be311a73f31bf4e05ee1657d85b386e41d3b00a23636a44e6fa2fa039e24c2673125ed7ad7d328de929c17bf295c09db757a2c79890e41428a6cd0e52ad43fa216b9a7394e7fbc2141f24640485ae806dcfb7fce7dcbe09a6e9aa59dbae52cc931de88dccb269f13d01ef387ca403d31fb643977960438bb517b61feb53340b00f295eb3270fa18d33f8f11c64bc4116767d750087301e98db1ac6d4633382414a2e84a7ee348e089908e6df2383bcbb9c33a6bc4070fefaf2948a7a1672139446e54003ddc2a5340b367ea09c2438a2ca20864c9209b7dbc8bb85e27cfa99e25c8d0abc6fab5b55a0743941122444c6a1bdf349a0e938a5765af780914ce2c5db8025232b71ffd1dd0ac775632fade2dd55129549b129d026ab5c6041005acfd090d752c24c4d56da5cadc13810436fd834c8ab93b6f2e8fb14283b98b729023410deeb901c023b03e3cf9a4d846dbf9c55a4af984d842893a492bada78daa4c294c9b67788d3f9dce807d24c5c6fedcdc8bd1e21e39ee404dff10742c2ff1d630ed625d27da535565cc0001a4b0245f44677d574163e562e150e7be59943cb302e07fa2a4efc54b8db53cf6986a850df30e37335b3b657c2835b3967f2060ce1e25106aa521541cc3814b188050b986806363df1fc5ea20c51005a52a31596b730f98c0cc1e4aed5bf2e5b19bdd87016826f32e56186320193535609acce401d97f64ad000b3d25ddb2c82d22d70d43be22e183e2069e2011ffb7b220b94ecffddffd19bfa5f8a595cf2b128dffd313b9ff8f9fda799f172012ce14c7ba6d8a33a9a53dc88c8cd69631ebe23c0e289667bb6070e32307e2a9f6165a6751101d7de9676ad68bfcc572445e5cab4bb37dadd63dec892e3f114406ca6b2a9f4b423fbdd351a0ce42edbfbc3ce3a9adf66800270fed0445f8babe092448b4f7a46b00ceffed2de727ddaf9732bed5c63783366e6a6a5494c8519ffffdc87f4a8449193576bafc52a71d86d5abe30bf100c92117248556d63145b8d5606f17164d1b51f00073c414f4ea151d206cf3deb5fc57af2febb8d4a6caf2a2d224b3413b3bdcb27fc7eb62c8eb14fb547925a9a305b0a70ca1a4c004949a5fb2b93e9114b0f5fe9230420d0da71822cddb48a10c7bd5490fdcf66197f1e71a92d95ca267b8d7cf209351a1bb251b13ef1772192e602e8859f2d594776d2ada51aa784aa86454f6d688c2cdf80858cb1429d03f947dee07d4b5f59c59fe29e56f1467dbee3ab96c25e23ced021b0de1fda24ee8ef1b2de94bfc0fbbd6461903d36538236cdc14000c2f26a98ba5c5af2859717a020ef114d0d276f682053e452d11e68f8890db8ee607b28e8f94c1acab0f75f60373db21c1f36e191403025709c3106d4895a84cad8104af3a155f9d706d422c4ae91ff3fe2c37826878154d8b4fd5d9302f2668b5567bfac5ca3d5c88e0e4109622df3e4d3c9a0a527e995df45fe1725ce2776c77061b08ed11865d1c0680ba9fe36aabbed77db764b8e5fa8d7315e309d8fb99ad48a646cf68f7822087d8bb2919f1f2c707c983fd950a3db7575083fe0485b6ecd21dd4187a2ae394495c4119eb16264c4d86c1b4dfecbfe986b90b895666ff65538547438c6717f49f2a4c0a4c2ac2dec675edd4e592905a79177534d32d35b72dd221944152a7f80558b11c81ea1cdac0a7ae513d3f74d684a1ce39dd8cb698f31e100944dd5064a6eba3a0073bef4c5bd733b535d0728b0767516119182573500615c1a1edb611f9fe0b9f77b7d1b058cd86065adc7d904e2306cd7f29e69298d9c17fb0919cd3ddee4c10d8cc452f35a3c2fdcc93b02806f1fb0e5702b1448d5357b7f18d661be55ec4d9e098d10f10c5abfa02558e33f1dbdd0b8b4243bb285b1b8cd0da321b830056c96d86acda626f4b1b5e6cf417d834ddf84eeb07a7ce8f3bc7d4ca8a521174b7f469853886bc0269ad1e9e6b7a9205501e2a970299570d37c8c19f382dd533e0893315a9bc122b577af75c500518a95cc0e10611ee09ba2ee657e62b91fbcc5bdffea348643f56e2e00585416c3fc14b24e2ec4056e16d594b64fd36477a739570b9af84188cdad176cf5954d99df8ce22655517811f19870af839cac8680086e78e14b42b4d3c0d1347882a8f1fd3cb1dda5638a0c6b7c10a2b7b1db9af30954bb1e703ae0393c866819f6ed5d1f073013e24cd6ef68cea42235bba7cb4d2869d143495deeffda556f56e58a2d8a9f96d377ea575e90d9e582b3ac035e900d63c0e6c5aa538923641f330e476e26eda339b25b7861d3fe9e15d5d550cadc8625c7d60f60fdad6f962a5a1bb139777fe9e17ad9f64a11d9a1295f10f96474363a6b417633594346085b1e56fc4e8c50f84af55cd9b7d78d5848381f8c1861d31c22558d34434c666a505bdf6eba7ba1daaa781b138de498846def6954ed57575da3594d2131bff6ff25407d2f2c75d02467646ba67d0f622bf9d8c13ede1d387aadcc225b8200a7c8b80c77ae784a030721c232a56cab4f3796dfb5e7be40b344cfcf77ff735a493bf9610a4f02b2e72b882a3bccf3cb8fd1deea961fa2783ad4c4033647447136359ab8506b76c31b86b41de7def0ca5bc15a40750a646af86bf82425c51b8de8614bec8d28f4bee9a9b9ed306449808080a3d22209054ab0123a4a344a9da581045c4c84b6130f87d8dba953dc3c7ba09697fb57d1ee37d9072b317be1ef93e7c085363c9f90dad90eed6ad46a7177a90d98d7b83ff02cb90ca00120bb51add79068df553e1f0315a4562ea346ca6b5532f14363d973e0a67819cd0076c825c6b38766a22f300f1245bb937c8fcc3debf446a8e02483b65238f46b081db06d6840dd2e282a1c16d64b83f9840e76a46889c3611b1c02c8548da19626004028ac71bcb6e43e12fc7ef9906eccd893db8e1b8b9d12fc436ecfc510ae210197c999aa0af378fa1108e19c00d6c8fe75110266df619eb5644ac6526a1b82a65d3348108814c2d6c1392779910bd893d9ec69e4d5a5a2ecb782f03f5ce61e26c3304577712087c04c55dc96274f559ae44225a8c29b883cba51ad89d772f1b4112d3cbe9e105b631091bc348b8e8ebb73af926d0ec9594742ddca5ff880c99a233363787fc47edc6a2166c8b2e42cd8794937587b244835ce74f1e0a0be443a993ef5e141162bc940a9df9f26e0d464f06bf7dac2ed6e0dc05867808851a76ef34156e6698b57f7b2fadfc530e3656edbeea5dcb23a6508b22b2c1f2b080462266d97d34b8db4c4888b4685f53349a61ef52a6532dcdaeff5a11fc9f944dd0c9cf6a7d32765268f5db28ec8260c50c83e22aa99a166c1c3a1b7bc328aa743292514b6e59764ad34059ce1ede5e48b96f7c4d5d1923508adf26b0cc644a8f65be79b19fe066eb3c885f0c096aa701045f0ebd61df89f6821f2fb702843a050ca340270559fd68f36eb43fc44baa710e50418138a54d425668d920bccfe21e39536b3714a91c4ef305c4ee4c7926549bd1f428fd0a81617a86b732108b5734f9cc28fa7c9e2032ef9df117440d0974f851d041e6e9a22a16488abbf539f35bbcd8dbfbcfb20da08a1929938aaf7621addbb4a96ea0c538db69f87d0ccf4e12cf82f70666bb0566ed5c07726881ad405526622300131f35844e3e0d71cac580fac08779f7918cbdd6da2a1bca94c9220850b664e0e5f4bb48a124c073e2e91e00a6a3f8e100713950068741aa19651729943cf6adc62880303175a34a913738902f9557370695395294b2b90f628ecfc0002015a58caacf15b9b2ce6f50ede942a31e851d134026e5a883c4aff869d348520345a8904a5b22aea98153bba8a0a2f2c964f38c534d784d17620467fde8e362913104424eb3cc5bc7db85639470812af75edf2201f6290370860a2a80699606147c3baa4842a21e39f9417bc4445836037bffc586f5153f252e677a57843a2004effff05083ee0dfb49550d225d8f029899408ae646760386a603ca19cbbc99872337d001b9c58f4f5708badd91531e90c84506eccf1ee110b8b4642e91d1e9707cc8793c6e310195c02905350827527a785f36bc06c55a9f3e8f6334302b5275aad8825f4499ae0537ab0a8934eea96d4cf8fdd7ea79d0fb4186dbb55d1ef97d9272a346a747b94f8edcacedf3e866a25659b48b9716403b65c2b93659bff9d83677dd02d0ac0615ad994d37b0f42dc752c7b0e20be92e6db1e047f9e10f56537421c10b1e3e98b809b47e440656fc24d18aa6a40d17738521c87fb02a960c6bc0acf591797177d5104e84efb772b8cc5cda0f356ef36d4a02fbe9ebbba7239fbb0916a2f9a783c672f4aacd1fa0058b55d470d5e040d48487f4ec1a156ded5005aaab19b052bd9a46c89a8ddfce283c35ac420339e55befa94eaba9676f1d897f590f0e5fbb67e1cdb602845576591caed6a0147a0aee9c77c199414e73e12b79abd43d568aefbd604d163285a1b41988ba5dc7d36e1e2346c053498df515bc4e425a3a0ffb776c42ca917f7565c44ba1aed8440eeae84bab8a68285144f809572bf58aac2484c141c5484221b32581dad414dac0a9d371f6cb4dc61df8a0400497c1f35e48ad725b8c69a9859834e7866140b93d9c84cdf5c4aca2a2d2d831919947f3ec53dca6380da4857e290eb8b6dcd1c53fdba060995507fd92aaa8a8452a1effcdfc31e0e62ca562de7036882c8c24af3692360a0dee869be8a2f8389cda3db718491b8bc31e94a25f56db738cd0be367dde84a0c6c54fb8e6fb2f9ed81f270855d468660fa97a2990d780f568a3acb3fb1b91338e6db515e082892414c818e9a089cd5efb84c94b837ea78174d23fe59a1b749182a2f4ff2753db2a1a56bb25815b3534918ffb9a0c272db77f7db8e94bca4f4105f55952257daa90414d3fc7547cc35aef2fc9e0f42215e539049627f1a32cb3aa073ccc8638c47617c76123b5d18aabf010bc03081bf546d83b429fe0c9f2abf09e6725d90c02d0706c7842e74900b100bc6b09b7944f64e7160aa01ddbf2b505853c292f7d3ca5a739a6582a68faa69e04726cca9a4a46bf4dccc04036b9415177aa10cb67df8a08da8e083a933bfafeb240bccf61083b0da8c640f78be3dd44beb15d0538d2be6377f633fcf94d255c512324b4cae838c11a033b707fd200ce0e6c8eeef0bcdf115bac30ff4eb8e0b3fa17742eb7ac9f655bef69bb0eb446e5447a522ec4cbd818e2dfefc1e46208e4e9dc41cc0acea27f0a9fe474cd6d86cff5b788b9b77cc58e9c8797567354ddcdbbb3a521d09f82df8c00912ef5209c0cea89a6624dd408aa8bb6b3325c53d4dc61d09d295273ada84382544c4ba26ff95059de70a57083b775b32c7e276b29678b892965c980922b35dcc4551c424e6ccc62e0ff8f4564b86d2600062236125e4dc307db8e846ccccde4a9e01dc8e37385c98004b8807def412949f46cad51b981550234b122b248a144f1a4c541314adcfddd66bdbcd8c6e4855785b7881ea56152c9e235b575e98445a95140780bda19d718b8af9eba8747963520bf645f616033d639ccc851c25360214181670e9779a08c1b98bb7755f855cba63a837aa71714752f57ea6167a02e3e058ce3ef01346ecc331f8b89d8180e14c3e00814198f16ae547e80b18e91cbe33e3aefd929522a32084de036359c22bfd14a05078e353d3dce53eae69dd5ee5e67c12b6d910b6be651ee39680b5bb1705b9ab061e46adf20e7189484804a3fe56021f5edb856e07b7d87233d8d68fbc8123c55072caeca12e92b31b1361381048275c5c425eeda0817271e01fba5a2e4b2756c34b148f211b3040dc73dff14b45c4818315edd23dc4251dba8da7e3ce05a99216f0682675182f808a1b0c244ca01b2c1185189868c2ad22d6d1eae74212bbbd01d95362c5a52b7bb65bd981e553f640eaf54ad114d35be7803bf0c42b7492f3f751356f668a23b2d48bd88edf76273aff7aa0c8490097f3d3be4be9a4dc574c06e3b79d251db544211931aba328910e1328c1a836b357c55407e836543f324d7d9fea249da4b699a48c488a48c2bc5f2e141224b9e56eee64e49a43d1f4e54492c5f02be85ac00a988a23db4a3da7bbace5ac72961ccef75a235be42f62227097663c8b8f72b9eb987801db80ba6be4131429d79fcc2046510e818ed7baa8e8b8df28ee687e5aa258d6ae79f8f1fd2836ebaf26b340a62649b7eaf30ab49166825650402026631916d042cdcf2f39233fb4e66626fc20209d55db13661127eaf4edb9ec7419721db35d764bf02cc86621627de70da998f91ac05f6935ba49e9442858102470c866e62988c1a95e0db30ef687e1faf559a80a5d8303e08c622c4f339638901c102ab5b5ae3b0471c905e4c9665ee9d3de3cfff7a3333e77aa52d7cc91b62270eae2fba513235f74a13a7ce642abab46166713cb84dd6e1bc31a0220766cb542fb795f8596c6d4fad77d0c0213a3ad40bb5a3855dfee4dc27670c6c87346380678905e8314dba51f512887221973f9f50cd08e9f3b5617926fb4dab023c50d669c4ccd94ad5a409a2fa269e9f5cac00b498c5f1eb038d16a447bb547007388f1361d9d6aaf0800d9ef7c09e3758127fd4f0580f40ea6afb532fdd11c6e4fd689985c41b33950d291f8c0c7095d88ce2f6d296309b15608670dd5b950e40331f92775123506da2ffebfab2f5dec45fc6da9cfb87ab3cb02587dd1accf8a95db6683750457e6fa82a0cfe6de59e0aa8f3ef231ab1c1952158d1738e81d6ea96ca04a3c52522b50201de1f1628d7ad79ebf0b316d6b6d0ec7f3a69356ffd03775654069c14b6388a16f10b6a293b95563f871b87424be1bfb88ea564af503762aaf3511fd22ab78f4dc2f56db9aaa646f497b81fc111740c5d3c1c1963a06c48a21fb995df6372b1eff394655f96ac89a58c067f7d93b9c4413ce698fa0ef0d7b240f5c19e925e874c9b88d428f2240291fb088822c3fc479fffaab5732e880905e1d92e509d642f28d370fd34b9b1a9552c8aeb2d2b41363056c0a8bf298502e381960a53a2e99f71b12fb49b5361e9da7c633e6315daa984cab75bdf00e24c32a1fcfa7cb5fa0d4c0ec611bc537e25cb3135f43031e74c35a2909168e09ab88d5453273a5f9f920ae8ce20b38d5c72c19dec16e6733d4077ab4bf52805f3a71aaa69d1141a02eccae1e0241f3c39a93ffac3aa9d44f2a858147c6a478842b13a9ae0f581f3b9678361013449ffb5660ebb10489d8a4ec22516c1972e7497ee300d3716d1d7bb6f6a0425ee4d69c7df94a8de004bdb5cc7b2cd09684ebcb9a7b90b052cd09c24a264b9783821bf91b68490f9dcea914790e8246959e3aec5bc63ee80c09a70bb89c875a8e5d5bcecfb47605bff94af76a33b9cc33d90fa53f9ecd8a0f617263e93597861ce61710dcc1538284a79e2e65705f6872023f4cbf3c605bba166ca28cd4578d20d080c733dc0425e2243052b505eed8c1f084e3b8563e293cfcf30c4e59474290ae3895039a68641968cd346683360c1c3ba646af62eacd5f186174811e547f153845997bb980542bf000b2defad9c2fc56077cf19742deff1128cb95c41f38e091b038087b1ff9583e14f19b7d8a079f7c784a03f592bc56a69c65a06da2df5fcefd540a04d7302c5787bfab7c35401a5d7f2cee1d0a7566ada64372994f2b56ccef4c828a7487c970bf4319d4fa243f0d535ec06359f2a3ec1e07bcc4f04915d4296bf0d34cb663ff16e76ef4148be3be5ebdec95d90e28728b325e937b53f569a09ebeb66be79b48360da7bc72755dae75613d8f550b8a18fcb08bc8d25537482d35cbfacc13a104db5c181229dced1b8ee0d7a2a3fe789484a3b89aca7fe828ac87edce007d466d17ad8b14fa33ee7808a9d15a8fe0dccf75b9fed46e120d3be129d19c073c75f3c259de4002e93b22374663b103052aca21178b8097dfad11015dc1e59e2322e449facf09cd98c6b6663bbe59fbf686f03c90ec88a97945de579a7bd6c2e70a747d1e1b5b307c8bbbc6fe6b093c3d5d4dccda8dedf4ba2eed64f0ba702083957f2c7105719d41d2e96bca2a60e11485441ae72847dccf6de4f3a4acb766c50dd0b61dfc943d5c46730d8f17500451d5649ee33d8245e7552e44793c94d95e6c8e27248c78fb21cc49a9bf589b1adadbac1839077b466756d6eced7805818b8af23890f96719efc7a3d0016720c79ece8a2db3310cbf0945c2ca37aab613a48ffd68c4a894af14c112272f8f13ced2969a68bd5e4fca2d3419a123aa511c384adf1a574f4bab147937500e8f8d48fd27dc7ae850677bb649b0d1abb961b1a45261cdc883b6408ed5b3cca2440733beda0ca7f20f622e8d8fcf4522592e2c705c9d2de47e5cfcacfaed53c1c44fea6bc2f5f0686cbc2eaccef864ef663e35e56225342c854979ba2f86e5e3148a8ce26dfb4636ddd4a4d68a21a01cc47795a72ae45e3b8a6274e6e1932066105bab4f57ae19f2d2ac33c03b024e833666b4a4be24318443863c5183597946d2ac119cc819fa8c457e9c187396785d7f30e49077d50493cd94aaa66d06e80896a680dfcb9fdc190731bcda6ea354ed012fe1780947ceb223de3dbfc684652b2a92605d3a08a6e5e4a52c584f87f64c5733900111389844bb39fb94a81b7c276a7e36f48d6d1d10d45360f313f8f6a622f4bf905876effadeb985297700ca452a791a84087c9d1d6418feb20c93e4b4d35d1facf9efe8a5aea8f1f28435d51e6fc57bdf86e8012b994574ad9af5a8a1da2f1482df6843e2373c9a9379d360730ea31073e405d445cd48ac3f2d17fe4767ab9f940a00a4262ac0b8be536fa54cdea0940dea45e1cf00c0920813382d3d152b2cb3ec9a786eebf891a054b970b5cec86eb216b6e8d68bb0b1a047c8028ea34a40eb77221e057008bd6b26c832e69d3b3e6994ccdf8a1e045c46d58df461518a4de30931d30dd359b5b4043090d9293b611df01e587c1199f3c75f889743f359932152eec416b54fd29af905a08cfd1b80c8e2b4194b9baa435859a9ec167be3bc7c277a78ccc1599f6de7bbe23cd4513aeff5628fb2fb7a053103d9cb8b1836c3a46f578ebddca2509f5ac1a0d0f55f96d364fcaf18964d08f65546f47d93993cf7cae52ec54aa9b0e31e7ee14c1bd3a4af72889753f6cbbe7c2b2570fd09603e440a378ad13582bcad526f6b684a18104570674a3f043553cd7d38602d0d39f5dc59685761bc5ae8d146549276947677b5a3322e1af9ce4e923d8d72c3672685a69a6ed218c713898860290ffb19ba1a6fe04829c64976b1a27148f2473b6152f232683b6c5fae593a9cc091344e05d2dc701601d9e943db93e1118f7dd0cbb5ce31e56e545b58687b0c0b4467f33b85031b86d52250d8018160e10f778350d0cd39e379597fdddbcef7efad49a0a524e8c3248c70b6a8a97e32bc6724f6906c0479fb26abd8d947afc379ca412d4d2f89ec1bc2082a4f0b628137b10143d0a7200bdd58d6259d9412243efef0b5cfe6daf26515f6985c0499db9d5a8d10e8bec2ed6d331f4694a77d39aa51797d84363bbd7967c25b415202e209338ef122354fbceaa3af9039339db194f8502c0729b4dddc4aea9764658b0b3d60cf97e5fb2544ad507fb93f217a5ef4de34e2abf5340c29e4050e0912e57d3401e65b714adc347df949aa9e5db130484d89e569ea644d1387743dec88d72dcf200cd8e089379e9b841d15ee0d9fc0ff208d4b39894a7f144fb2f99c31039a6fc52233cbd7f00bbc8424c97fa4a3ed2bc864e566860bc58d4fa62a7277c539f733d59736e223a9cfa1d29b0c868abc9a647efb473504ee9510936e6446f9311f5c609cfae7bad89480524534bb8e8ba169fddd769fb2132fdbee1332f283938f5fce6f12df8d1367d8601f5ab93713b2dcd77afd5e8d67033d1c3d4af07525c7f894c21abc1ec9f7663c4c290a2b1f38fdff21013f19bda29f07197f2462869f0718c197694e434f938f6aac53baada38bf404b013e1d31ea8449748e923a0558ee9a8c39c67557f4228b876488340f88752f39ac0cb8395b296b1e35c62375f69126be03fb011413e90403deafd71adc691c17402573a2009377f594a6ab976c65f7f3a6d45c53b68603dacf539bccc433c6e6511e5a78823d718466034125ebd1894b8c29f94886c38c930f81588cc9e39c31058088611baa736625407a4ada6e0bef66221b3ddfd429f135000a2f5287970d7da86a7c5eb91f5a8a38bcf20a460f98645985cd85832342f7551ce6487a9b80b558a489f08874b608f95a9c26afbbf8228b45417f7ae962c6234fe9f290a473d58c67cccd1fc23a617a842aa853a1be670d29f4707fb55a9514276f073587e0a880ee6296a53137aeb157a03ba2c6954809a9c5c2a9a4a34f2899131f53190cc951da588db1e117f5156369feb543363e1fcb3dbf2d53f00a6f726489243d878436c575b150051b4c60c9eebde8b4384c4fa92bcb858b6206a862a28e725523984cb424cb450f46eaeee2c4b27205b67f0c060d4d37ef56d5dd7f639a43b5b21649bee95ecc3235403665ae3ff1a3a25bd002eddac98b0b17eb9e0799c227c5632043ecce47b84b4dfa9713f894643f59fb2301ed5a39ad5ead16ee72e8dc70973dc0e6f40c90ece6573de920d7ad36e6420662b017519104d6055708bb5bf2bf301531a14021b690e328e9c03dd754d4e24b6745cb5584ac769ccd35c40b312a3767c03d0fbbca7ffd7bbd72672dd49c71913f63379067dfb90e0f8763c39ddefb83ab2cf63a578ab88e4bfe51f588b7428f4b44101ea183749de8151d5bf45041a44b953f78a4843c51c9ba6546d1a55856b91fdab53f74b19289e14f66280b514c42251120f21892b51a0c873cea82bb00de9dcbf14891a3a13a0f796c44ca133fee5a162c411629d5274f07c9220b79d0d725fe9dedce15913ab243a98b2eade234121833e798d1fad2ecefab5c96fa82de940a8a609c1cfad6d47a5829145cfbd403a76bd5caa1034e0d3f9ef118abdf6d4668bf52a8f665d0ad98a840348377bfb1ad305d906a63df6f8c840f43ee9970f0a254f433d2c860b7995bc2427fe34c649916ed580f2ccc2f64b4ed0a2930ef5288a6552937c0c781dafa4b95f96779dedb904940cdc94309fb08149cd46b5c64c9e80877cb068972ce84600b6294afa88c2b71d045e8a96d9636354b200c7e3b972c69219679346f97e4fab8269aa3de27d319a6f60fb367e64101d21ecfaaf398707efde4e1232d937fd0641f2607bf9f67d9c387c1629eedfbb9363566be39955a2b84f8b3acc2da600c30d06cab5150df0a19e127ba351dde879a8d7d4b0a71aff695d206ad92130fe9fcc0e93e326e52b96e0990e49de25a135d449c81e240712a443af544edc43583147d20774cc784be2859868022ffdae3e4dee15f4f9a2985b292a6f2a4394fb6e151ba995dcd2365f12b2342b4ba09675bc4dbe6baf7b27a22095ad20924fd6d479b26d7579865ac8ab5b6256e4b8bda8f15bf361ced4064035a96915a1f10ff653a7d2545ef36a10a8dedcb6351f2d32c28897bc04fab6a990f31e0436be04b917d949695a322a33671adb3113ee0872fa15cf9584a6293f622ed413c71652738e5a8af0d49747600c81ad61f4ea93c88c704d1269609e5b4d2f61b8d938513e74bb8df4c3e9a3ee3b7e7008054fb374b08542d8ee194a3174b23c4ec4d28d68a77da7621bafaec875b4ab52716d3d885d8f1a3a5aab6e00728aa23a046cc6d22e8015f85c1f7f0bb490cc214c170800d45b0748c62347691b7b3c56c1dae377ff6931668f3a432c62b43db2234556c721a658b41049abcb3b50f324f766b1905f8ea5e1c90c945ca71514ef3caa7f4929e4ba5d93ed9dcf0f29b3d2a9f5354fa953b4eacc05940353bc88a837b06a9fb5c055848ec86f1d4a5992bf93564e85ec568c8d06d9db3670709a5bace3bf3972a5c026e265e559a8b79858b26c3fb5ab85ff73d87bad6915e12471cd65e9ccfd311f7f731decd5a80732cd48fbd358f5ebceb79f042978ea54c94ab755fa94c89ef902974adb95155b11718527cf82d301fc7a8e267bbfe21504385ab1f5d452d39454d7e75761a17898c67020cc0fc46343d9c36daaae61b110aa081450150b407a66fda274c5c14ae0baa2ebb10e959d97ea2424bc4a49dba5f0385f65c34bc0b81d77dcbf877c0b585867d1758f674ce4ec6608c28ba904e9c2f717aedef3849115d9c3ae206760ca5315af688fffc6c6ab112131c6acf04a6a383bc3d9bc17dee50a6c781167e07393424e832b40203c54950cac608eed24f365e10855e36084f62e6bdbca032b6be67e00a017296eb360ea363eedcc6cd4e27986c29836e719c31bbb43dd91890e5f796a599bab6cd4633952168e7ab1d682e36f50256696e5c83817b76ddab433ab1aa597fe7a7f06180d3cdcea85e76b303b179b7b5f03fe5df611438096d2025ecc324b8cdd824166bcfabdab685b837aa3f01c4eb4a39ca87ab43f4fced39e658b9b9f453bc7fd4aa2f0d0f9b6fce634cb82592da2311ef11d54bcd44e563cb3a836e53e941f977d9ec9c655cfbeb637651671bb99914196e11502ac6d3f5defe914d522fae4fbbf9468ba2e99e1219d632c2ee4db8bf20ec4fae2b07a4a214214a174894ea24c6c5c8f80675367770e6d1b16437328b5b5b030674403ec3d0bedb02886e7ce084732cf34f14be91c7ea6d329cf7e4b02efb3ead68aaffda4659444581a00e7be1dd4aa5dd98e78e0e1b4bdc680049e90d48e7cfe66bd03db09f097d724de936364086614d77ae580a5c86725bdeef4ee0b0639d1056ad7a01e0a1198868edbf61b55483aa7dda2b2f77ef4f2ec43590db0f09f63631e0eee509cb8176516f90819b276a44dd54c3bb28f1871d85c2a2709379d4a8748fba4e0ecdf0d3f91f607ad166221420e25cbc8023bcf6c2bcfb54fc40859c5315205a0bd3ba15bb30d8dfcc8c03828296f5521ec55978ce17a3b4db8fe94d808c34a743205c70356d35b391c23b008e037826b569bdff08bb5f4dbc32ec4eaafb59c9237b3fb5fe67ed30882f6980fe158566d665dc4e33e06933757301b5566d9ae0dd025981a283191fed6a2ff0157f8b44d2f6f4af4a1b268ad388ee327bd7270b60a3e73cb54a2bc4d652f271c2f8d8facc3aee4096f0662ca01346fd14d9ef137220690501763ee5fd40fda7254f1cce0ff7297329c5115b46fc68e434fdfbf0d8d4a250000698f85b9cd9485ab9506767448a2d9252dab27a6bde51d986141eb2400610295124e5ca59559757e908e711f14ec5ab806198123944b8445b30e7150b7b0085d6ab6eb88e6888165db7c54b57cfd75e42a26b99387ce61da530303e7b1678b422692c656dc3f5e2f0858ccf7aa981f9c08442556a97939a1fafd6684be578c86bb1da014e377b80e873c4d6439cb7d5cf5be0d84cd46b976ffb21631ab9c73a44c89bb9499d9d1b3465e5129a8e3ed680bfc33e0ac6ce348fea495277e425271f9b92221e875e219b7ec246015de64c04a65150e1ef01df49512dc70c4326a0a43c468b2d397ddcc29440752f5f9f8949db513bdbbc5a11cfa36af383876ef3a16ea16412fa14d0265c33738b3b30540c203667e6818893ef373b058cd8a5aeccf5b48f5cd6b581da9e32228fe6083913e90f39a3fd2a8b9030d75d0ea1121b5f332cb21c196b759a13fbfb83bf95d406e89b7a4050eda8a6200edc365ffb84d423e416b4541517a144074b9391c068ac8acdb41a7a0840e6e967858010985d5991ee89c29a8912b2ad5e3427fcfe08e27cee41fe52d88f625f178ae84b91fa3c875436527e007b6634fd50603992b37a0a53b58dbbc2c17457c38f30c642046804e8a20d013845944fd4e6d2d14efd0f844bc83fbfc4fea2de3ebdaebd719e3870908a1e4dc11f41d37337c4d936b73582f5ac8e620bc4012801a34884cc8980e264201339c3466c6d769d90f1abdc38298e810ead5269411a42467a15cee81ff90858f638c6a6c681fe95a0dbd000a5de2508c322fb9a7aaebf5a6826d031c67f6944bd2382acd17aaa7b72cb18a1d2eaccd88e79609298895c4b91ac1522fe8e7f0cc86e4619c9efc0e046dd758f0a82a4acc3ae11fdb8a70b24347bc0268b556281476c84575d8f141678f4b4df2318d0acc0a53f8f98ad60527bdeecd097ce8df8677e61c584414e287d925804130b0d5a81b285d1421eb4784bc00257634d6db545e72f12d78c8d01544a5c399b003b2c0dc7037247a53d03f7fd6587d650233b6e74df9d1be68fce431659d914711e419e82b1fed9c86fd3973878d54ed5312dd4405c385efad0146585aa89bfff611f597580383af4f521a9e268fa99a49cdaa18985fbdfd2e38eaa812f8f45828d340094e518cc210d44a7b5b5904286c5ad9a5087db41b84cec37936040f35ba893e9183043fc284cc3a6bdb26f5629c887cad708c994b5c13affa02451d75c9b5c21beeab3b058b257651c05d9716face1283320712fb055c6f8f4dd64fc560e9c5259d82210f904e3324f9fb44f6a5238cccc9d08e01a0fd88bdb498e0933fb02e47ec119017901b9d91b9fd097637ff3a4f1ca2c02d36accd72534b32308f4009039b79ff592629347ca4188179b148ec704bc4ac140ad5170c108547ff861bc0a0640990c91620440832b81ca189648ae8880db02c43caf0c1fde9fee80c5c002a8d2a6d54f646ff63278caea3a66a35358009b3bf8e905a9630c661d6570c47806c4bc347178e8b2d000da8cd372f0dfbf25898e7e8d2bd033a407592779649707f82d61cd269c5b42107e30c58982603b10e3464b3e23087031408119693fbce9118eaa0657609718a8cf8a052203f59ea3c3c10c8a0c5ac87faac6bab9bd8d52ec0d36ce6a2a0c85185343e8220e3cf166ca89e2be79d967b46deb21bee8bf21e4c31f662ee480767d0687d267cb7d99d246438641218028ea9a010d312712aecf571603a231c53a71082d5abfc3e10c56cc175fed776cce8dfdef4d3bf0d9d59c48039e4fe7dcdf78a1bbc3dcc5e789c4f4902196323437084d93c4970fc598040ba251a447216ed3e81fac1cbcd9ee0efa147f762bb6f436af8335bae1267bb12aa1d20c2e84ea701f9b671851b98d6b55012b8361719c3f1c32c42e0a5ef486baf50d4ff788256009768b4f70a5cc18a5162568c575ced919ce71285ff99b3102c38855063c4c1dafc70be8888c2952a5e658aebb2608c7c232da4e876fb07f3a1d467687457a89ad845a54c5165f2ca2b12c27ff47280e4edc4c4843df368249840e4145ae6ea88467dd0dbd909b3c0293397138865376bebec4b48a0471a1cfb18e1310195c4d659f8f249c05063fbb08108e4ea157bffd7251763cf3fd597ee19b28a00ae8a7626a673a8d83b519366f05d32d1afa0de0b5c222cb5633c35943219aa55ec21593f29548cd4c41cabdf33fe42b6f758a22c43f7364bc1d01a531d91aafa65ed26021d47ceb47ab4fc5f32aef5aee00b9d12522af3696042d9692cbceb5c7fbb623fc075abf4132185534b482ac5d2020741f86b3ac3b1bbaaaceb887a0a67e06d65d6517afa3ac9c4fb9a0c81e01624b5d7cd559474d41e137fd66c28c9feb893c59297421723052e1c9b74c220a5ba95fb4a2f493851caf35e057cfc3bf5e71e7548f84cc143caa52c1e99596baabd0ff518d2c9f3778373761b8c07e0da23b48418795dbf5bab05c40599dad48e663cff009a3e5e259e17bbad6bc05b94311893f794b9acc1e89dc99638479db97a3afe3c108de8ad75018062bd58fa432a8e4be998256e8406097962a49d977cd03760fe57e34cf7aa0ea4da8a54f5f87205f6bdf9cce259f305544c15b5ec6dbca079e7d32c3458e275c62030959680c26524548801fee18cca43090dfb212cc08c95752807b37874134ad8a6cf17ba865f5d7cc25ef3308c2b6410eb76c3d54cdfc2090218742aba2ba19347b340cfa6b114efda5aa5801ba8e69ee074ee93a1cb3fa79a3640d81091814fa8611a523b7a054ca68ca39acb28732ec20d3095d7ae22e14202661bc969cd86cacdf550481b07103c6e7461840fa11a9dc2c6ddc06c3d0c4930cba4f24b0494a6f5bd9e47abe09e24acce59c23d717d19aa51517bc40619433bee82e8d362ea50b4289067a3bab37f272e1fbbdc7c062a46c11b26b0257b14d9801ed560413d38683cfb0bbf3a7813e6b1ed8dd7796db16ec77eb457f8324a41cace7f84a5f00e07612990dfe698c809cee2bdccc685e3182937ac020ce8230897459da0520dd2e167a0fd31de55f3f69f95ad22d5691f211ccd061bae4370d51845591d4357482cef2d30f1872996beb80c984f48b8b61872828827255efe0762d9da931d700ceee38cc090144ff4b93fbe42e8542ef28ea9ccd7ecfa28f5656223dd843c8d5fb7c5fdff386e81bff51822b812bd1a5ac2e3d5b07b7e676bea6bbdd705967773862b71a7b5e1704d2b4d1ee967d6c07f5b69501409400016f98909666bcac8df7d5d9d199c651d2411a681f3d94d2ad94f316d5c1c00bcecf3a4bac3d938d15e7d33d3536cafef595e1b2868e14fd4372f41752551b7a24b319a912f41b693ae692fdfc98ea5d12ef3d679bdde5f6fd9e846c720014e9d5d3285f11f440eee6a418f123f4e269c945fa3eca6d8ea1105fd2747d8c0d01f34f74d06f9bae7f3bd0eebe3746d928faa70865e0c68b23ae830b4838280a3940c20bbf0efb1391a4ba2cc53426d04a0f3502349a2e1fcc899d2cbc0ba707375449f4e824c2cb1954d843e1bd1969ba23e410777d2775a3a9d8ac951632ed6f1b531a56a0510fb9e243ffbc3d2f625880e8d51c84eef167c054feb2a31253b00473ec3d1afb32c3cbdd8d8e69267c84c6643040413e35ecfe9eaa97f0c1297f5ce91592aeb242f78e93717bcc0c0ce83f3bf4df822a79b69976b8c7f65728d8a41b51a2acd79b02508185f577842c42d24aafb994cd1ece8bd0130bd6cbe03d55b4ada3b89ebcae9ca4012e705a0466d1f392b87b9b9f51aa502c7ec411a6cba71d5b0df24438117cb6c0dc22a2e28670787a0d099e6a3b5917edc2f34227482693791e22f549f7128ed22e10285f1be46703e92877abd0c846abe95617b0165340321c046307592f6fa9907a9ac280dfd05dc232f6dd0e59ffebd7d22f1b959e0652116644cb11ceb258e0e962a2cbd16f551a9926adae5fea650176126292c8c3e1a28b3c032d2d629f84f0e9cc9b7174216dbd3dd6525fe510fcee8b3c12dc463d716db9572285f6b5d0244bc1c08aad5a18167c881068dfadd495f3bdb04d58d6084df2bcf7409eff573adf4bf07bd74182e2d36b4cad69a4f769a5a9da231ac4778a7c6b1b0bc401d34ff165b641e07ab33f3b9f54e27061dbd9101ccba8df5870360863430553c4fba7e897460958c6ec936de50c5b5faad1520a02c7cb48b32ac16e06edb0fb584611985db2f820b039e5167706198ae835192a8d7104a81f6ce9b504b7b8d907ad33029bb5f144614aa98db14b73b7dda41ffc82650ae797962ee8b7d460ba3194aa76c2192be8297314f713b72277c1d8bdb1fda159f409bdab8ad59212415c39bd9fbc0fcff9079fb357dbb03cc97b97498daf45b7176a8da5365f2dee14a749dc1647fc818f9a7022e257610ca17e02c477977b85e41f53ad1fc10fb6a9e9bdf4d302254aa2831d9b47481be54cd83340365b795ce482de59fe818fa2606c4edf8a0aa0b0886a284f615f341b7b4bba4d55936beccccad9b7c6941686f6c5e7662aa8e9c75b436ef9ce3d4d10cf96af03290015583f7bd67c36ff5c4bd92d38dfc9ca2fc79b1b5c75660841f562032b14f6a0263467118f5a03406d674ee4181b67e560835fb43ee8845f34a0852bc32fb1a38d19ad1db71bf6024e35d32a714b59f00bfc731d6f7b06204d8ae0f226986d4f57f3b773cf4c4591ecb97beb09a0349a509f53ee2945108401b5048d310ae64c903627cc18822ee414f7dd7f166110b5b39e0a2fbc89b1728e26593db47231f8a79bbc95eb3c59f6e60c8b2093d14925a38612d7dc3851070525c315a89bf84b347781ba154cc2d522420c9d5727498df0fcf81bbfaa3ed38f254e45ed3aca631b0e88acaf2b287c56db43fdb0f51e926204caa772c9983f770dd620f6415aaa37b3d1ce5d1d313115bc875c5edecd63391b9ac98ade7ae16479e7ae117a387897411be5a119efc50a19f0d94d50bb09e29d3d0ac7433cfbb7c060a92f440f777203586a2162c0f70eb3a59634173dc1d81a9c102f4b1beda21e543977df7384cd4296d6f47e419f2615712f34e9fbeb943f61171401368c71b7293afd5aa80523e8a387652086e3f1b5cc4c02fa08e67ed8701423ebc4dbd370cd1a4bcfbcdd56b98c63bca39e1525039c318f79a4844d73edb1f75f5193690f26a9836a9446a9074652f56043d93ad6b58eb3e30488eb645c68e145059f5e506e4ba91652c8efe310beaddf905def263aebaff73cace39058c5801bad598b58478ecfabd8884d8128115ca2d3408787cf4f450596029940e4b380f6f926e10ad61045fbaf648c4e62a1165653c50e28a2dfd62cfe37f6226cdc088140eec5a1006b31e29e10be9d5093b116fa92e6ef0b16b49a7e2e6b98632a16126e7adcbdf9ac92023b6e226cea45a5e30a3f5dcc92072fd43a1c63976632c321e710dbbcc4a05cef15e40f34e353d7f21ac9ecddd65b0c32da25dfb83c5a573c157e7788a065bf5df82949319b2ba7f07beb5f801c74c8ca0f5aaf7a58dbda5b09b4e24ec2bab8b5ab2acb3256e055f5e688e07e3c926bb0fabe586c9de51a64e5ea1cbd046bedf95e331a8c81a6a937732c6d370642c00e409db0b6b4f490e9538b86b544d545c163489ce527b3e46cd5d3e9d97f922f59846a7b347a4d9ec2f84989cbb48e574d17bb6a6d3f6a5aa6ff715c1ba770e45e86ed38f8db7248e39334c08ef04db67edae9106a38a08287296842141b786a6d45a9afeaf54d46e5bc072af7c22857976675abe292041e54463c038b0d688dc0d6942cb3120db6f0c43eb47847231a6a38c96e07710475341b24a66a3283797c7a572d56b05174e23ee19ead53d68ead2357cd4604e27cf9a56d917a6e881ec7555b533bd3c16cb761c2e3f13c13235609149f6cc23543dbb8dd297ecce481f5e47afd23d2e469c82b57f5f47be913c2c75b487831243c1df6652e062bf5dd1383ac4906cf9a5c8e20d229b9da750a10c87972fa0e2e58cbbdfa4af1fa075b80e9b73581b446dd71d16c6813343e6b8c1af79012b755334083d7a3710c682ec4503f346da158d301cee757d96aa6251c0435a97defef7cf39028390314fc0a3867375853f57cca7d5d105332911d28d0e835b34d167362c8c61e8cec6adda3eee89570670259cf7cfcc3668590c4cf1b2d9efd3b643e97e00aa0ed24303d0624e43b189e88802f1e25fc74cdf327e7101ea365cca17c8f2eb63475bada05686e0728f2905ebad04da11d04ca0ea3ab9e09846b8e5d764a73bf0c950aced89b3a4b75f7170582d6ea4ca8dfe1e3f81d19536fe0a0dff2927558572e61478c40b018b5d2b4732eb99ad484a530ab92ca0bca03dd2abae1c36c8a65023e64a0e0cb1897d19513b09e2ae95872cc8c864550c848c3dab5df8686c0e7026be4b481571f402d569bdc4cbba21208a0d8897552f9e67e0062964560e5f58c71244e75dad07dd14d2db7cc1837317af745908a1e22a551c0b3e07150a491f1ce157dcc205c24d8b1ec88cf1596ec233c8089324e5798d8230019e85fecd9653bbe4832dfcc5b475e0a1d319b23416901ec77c519b9d47cb30efc75800604814e778843c75ec8ca2a87e5c27f57c3499cbbe5c69723ba5938b0a7c099e5a0b419a4d9fdd82f3c060eb30e153ccd945003c77679345d718ed30b6bb90a9a1786090948047fe5a80e5a186a24328c4b034cc8d31c60483a16daeb3ad0dc7f5f651be9c41543e4de9c5dff28fa01714156101fb5a830e41e676e15c5222d90f41c34c3528ce9b04d22250e8ca3d028d4c2a5a2e6c442ef9dcb68a0a214a3d815036d56c8425d7d0bca6f9fa2c4861ebcbbe672ab4d6e0f9f9579adfbe4040b5337728050caf810c9a0d5c7ee7de56471af3a8f465bb2c5985fff6e9750b393c4153fba1768776b04ebc5ea356645723798ade5428d483ac6125e3abf0b13a58337379f6fa4b08287e4539b5ecaef12900294fce193c02d0471ed247c4abf390987fd1aaf27c273880d8de43e90fc8a85606e84b5e72f84205d546f1aae6d372f494d9940d8cf23fbf06e5f4941b0c4470d62bfd448d918530e53aae7b390a9a920fb894b0317fa74c83ed6307f682e52673875b1840c0982318cbd1da2135008593aca61ceb068cac339eb54225299e05a48487bf32be7d051483b82a4c3ef2e419ec123a92906c878eaecb73712ba45a85c4904478171ef790f3de2412a39df6797b1e51ad4865acc8530613bc654f361d24c20e5d1a262778c66b965a40eb60747c2bbea246a220accd8f88404d7474a5695d25313c92c5a1ca0b01d92e49a4086221383a53ef2a0436bf338ac959fd3dde95f3f503eda305d86452945596c256e09306c33133baad4efa9cf23e9e59ec2512c2a11c594fc54260a313cd2d51a5329baea2dbea5626371ad475009469ab64b6965566a603da696e259a033b858f93ee34fc8edb416da1d1b83d996b29db623c43fb3c1c50aa50adab1ded8cb0ccdfe44cc12006604da8328bac2c5761a75f6c8f6c20321110fd5c11d16eabbf1d9f6b64ab9281889f8647ba70a76e8ee49ef56c048645f3c403c9c7b08201996efc4f8ac9527f99f359bbf48765e0befa668b8a101a149f8a1df0d5e9a2215226c8cca44c6ce4f4acdd27aa545f1a0a5804bacc483faca833c5f94c9e4edf4dda44410d88e3b5dbd54906cd104422aa9be870a02613e09b97a052cbb3e2f8496982adccdd7c349eba366fb0d7202f5ae1346e5681860fabdc5b5acf5513a4d35b22c7e1ee217f0900b56093b1a93288ab083067f9553bf2abd0e06653546dfddb6297fbaef1ae81769bd485d3b17fe0348a4afa5f4efbb32ae031cc0fbc38fc52dab288b604e08a7254df2e9650b95c9e76a32eade377d254eb034f1ce6be147d4fe07807dbe42fe8dcf2c4a8df1b665ede89f88f1158d2ff5a0cf69f11f8c0f1ef4ab4eff30b39fc064dfaafab8d54f065dcd561dd6dc0d48950c171b52c318c422854c43327058bac23369a6f382bdb1fda2509f34812cf32b9eaa7b60a7db50341edc699f67bdb226f5504ea4951dba4a23f128a7b642c901b56cb61e067f529458f33c9bbaf692358087243f98e9ec37e4281f651ca5d5188eecf877bb99e4a30413cfb149a5a9bbe92380ffe1036346a5ec22b7401c7e11cdc8d06500588aa06fe423428449d6021032b969a640f236b20768ce5c36a8e5a99dc7ae121fb702112f93054269786be7a24cc3a4c83b1fad40119313e3569c1e128ffc5a6b9dccbf8a818849d2caa8ebea233175f5b22ddebe0d09226454c1cc15e153716ffee221016c2b94ddce91e24502e47f48c0b00da8f8258641283dd742e06b79e27e4d19c4b0ca2850ab6088e1531d7b9bff086c7baf0a120462a794ed88f280694d6644d74524b841059ce667922672602aef63c41e735c7ec0b4030a0160089ccee93cd59d8a8a6c505fded193b9971e0836318da6e4e99da1ee3d56689601d4ac10c32470e803105cd5b0ab150537d16c3adabc4f0641b70b9e3451f02a3584b2304f6ea4d4659bd100f8b26e17606530516d576aeba9a8e0a2a24d6f8a4cdf80e4fb2df58a688b8934a6a25f01363d4f35ba4e758f36a9a81d33aaca48207eea106668dfd960fb8023685795a37c2d42005800a4eda599db79737f0a4ecdb8da8150a7c74e83fda25c1a56bad4aee515900b8fc1a058bf60faa38460148d1c46ae6f513b56933820c1381b0cf1f4a632315aa5eff8786b34a9f6eb237ad162e76379f1a9476c925edbf0c7e264c1ab5909c8d2e8f9c7222f3263bd2af19e244dfa80f7bfba275e8977dc782af638a7047195189e6d335498d28d7ceaed84924b40a1787954ec90afa97819355ccdcbd01363a716a0a3e547a760da63f1e446a6f09993311188a29024f5e740941a6c5175dddc6422f13c3901d60a3d250b16461925e822be80e93a765bb711d33c774fdf1367cb3af6cc1a2d32d3212910c634aa8d640213e0b64114fd8c56d07f97985834e3312fe50d6b134348c8124fcdc712ae8c5694a58d3fc542e43d2cd2ba7da3f8ef19c59b5e7cae2ab274f178a861c2a68117fbde1ca71b3e28301e54995a7761b935b3d3afd56eb35e5902bea62e68bba11accc13e43b3b25eda5d700650648326618c4faa089334d7f9002a873cff85114c6c3b9a9950b1ccf49daa5b9617c177545c18e2c8b8941952c566611d1b02b4c54805393c1e10c6b10104b62a4255433dfab8f06ba5ae0a6515baa2cac466743026178a2a8ccb3cc8208ec9a896eff4d2aa1560d9203a9cbc82a6bd60322ddfb72699f5b0263519945f8ac8e36ae4b3f5dfc81697c8a0bece7ba975012e188daa371312af32e6b3f700d44ad5436abfa601463b548c764c876a1c9887e920e1a89bede3026e8b2d9f002dae5f54db419c083e7aa5b3efe5a80562fca0e27fd0e15a9139f986ab2e9b61ed36e82a6e791ed3ef440b64d71273ec7acf2dcf1bd6d6e3570a1a27e49b5520033536b0e723546eb503ef2f1c682834f5af9150c121f5db72a1321990b9426695f93bacae993b0713c2717e17860c7515085cbafba1d6ccf9b3e1aa6de8518a1f94e637727dd48b6e17f409a543d29dbfeb74c161cd5d26138ffb71cc163ff59da3bd6eaa80ca062d7a95428fb3cbd1c3425824ae4de219738f48613835018bd5d24f9fe41db9a40535c819b0870bc63bf0fe7f80d96f8527192e59e3e42389f03e97db639458ee882382361a414e1267e3889ba99126ef49aed496a3a3ce60a64aa10a60c179c9f7e4a3f121105a12b4ef2123d9fb77c758da1018da9ffa06b37f54394020f4234945cad3beb3a664c0f3c966385e8b7614a65a760d065e93cec692a81e262c8d23f8e544541ede09ad438d5211d396b116d876cc4ac2ce9029a516441744fdf7dcdedb6dc52fedf6520a2661b4a8662b4ce6ab4056e578445fa713ad0db80e66f222584a02d27e0389a3211034b4b52ae0b4d2f91a9dcd7b5817b45d99e2fe3807ee3119aae896dd02c9e4489d81957fcab3f6e2d02ed903102cf64e3645541c90eaa871bb3abbb2068769aa95c7f95cccfa1905094940ead729c9bf9582a20330030d9029cf0bf523cd71daa2050f616cab4d590ecc48f10b745cc0322dacaa6970bea78306a7a12aa1ba7647a661ead8a30dc2bf5182b3336858738934497a78083541dafc67d1cee72ca24eddec4888aeab7cd7231d6f73de9b993cf9757d15496dcc156f78c6fd21903071712de61f63a5ec981e2c1951711222eb7f8bf2ad080794eb311b96f1d3e98c1335c80ca176d2941fb93c18a829ff0678c339d571af874b33f03520c534fffd46a94fbb5bddb12654f9b64c7621eab3224d39986a42d5b33da1d05491c6126a510303243b78a237952ed8cb45943069d26afcd032be856ab46f320294e45a7a2cc5ef9cc0f0b245e54c862311b65175ef96f151a39f8570b792ba13e92c941a7ef2d7d8f4d07d6265be0dccdb3bee7c245b9d962793b23999f6056fbb985a72a99ceee78ca2d6488b5eb7ca061261e814ca253c10690a6bf3f8f5ef34548ee38de748f8fda9e124bf2ce760e24c6a5d2bbf411a1bda3a67c5b45b573741cfe6b417b72fdc55ea2795dac4403fd6bc1408cabf0df9d4083125330c0916fdaf95d5d5bed9370f13dc9a9073f9aa9716aceeb40a5ae64c7db032b4cc92438bf911d5bcb81e97c03a2a16829802de9975cfcf3255a8ab138a0fa513f6bfbb263fcf32a29b111b179e0d3776d04331aaf925608cde44bee2b41880d544fe1c84d769e1e94427f298b50d52516e549ebab5ccbe2aff6097ea2e18681f625f57364bae5446289952dc964e76d173987a83b8846d4f718cea386215b615144cea451d03ada622bdcf5d70cef89db4020044dd6ede6363731557b3bd3ae4b329e545545a9adedc34dda42c38973f4b9b9e3bcf3125589345e469b97cb981144a6c2abf0306917d6a537e22df410de49e6837f996f1cbb122f172114b39ead3779bb1e2d2ae97ae806f5392e01872e5829602038ef53e27ba1e38a097e148871a78568f6d3bc813e4bb32fa7f1dc5d1f53de0d94d3c66c290c314449fb9e185aee324dcf8031d9470798c4bf7b1fe1f8ff785adeb3644cdd0ff5a67445c481b2f31e17609f696d6a2cdd64c0df182792817a2a42d5cb080a28452419b4084a3496c79b7540dc208c082c50bac70c7bd38ebf4a2aca2341e682f17fe3f65b3ef12f7fc23195a8cfa546fee77b7c634c1cc5ae525980950a39d564959e79874a5bc5d675459fe2de8cb92506605726cb49eaf809dd149a6da46160dfedfab83f328a6ce878ebaab980c4f7a69c6563045b61c8ad4d6c001e10c1945e912cf26861708b51d902d7026e0f543317a5ff9def006b272a32820ce0ba4849d280a21f76ff67103a02a45aab4fb82909c0789d459e41ea4245163ab5d9646c1c033c29a3442d85aa5cb6e7f4baaec8a0b09eba7e2686e535e060ce76336e43ccfc38e8e572b4ab4106c14eeae7d089d4691e5c76ffc3b0d406a6fb175c0d12c5ef9ff4ae677bcffc6fe1d3114a9ad523760db203158d5d0f96d94478c6282117035f3307a75e680b434478631d5a16c7ffa94d7d4e691b3d536c51d114413d691c506eb24f5adecf79d31e85451115f598c3eca8e4982a2a4cb99381499a4575872eb8f7a3955ffa0bca07e69849fe78f1bc4703997cc77b1fb185fc55b3f34490bf0e7bd8946861d6525cb3e60ee285f6c31c8b901d78f9af3adf256396234ec3fa0ca9b0a61d2fdcc90da7de72fd18ec7897557cbe97b4b4e8584b9bf81f3ed12958ce26aacca62121a4f4bd3512d52cb3bd6ea0fe44a23802af18c4ae87f69277d7cf5cc94a8b3b7bfb979f501d72d8913caea7787b9975b227f1c7cc98f71377020b2cf3b78fb21c5d9962aed360f040da1cb5781cfa8af6e44597cb0b1e8eac429808def6113829a823d7cacba16d1f5985ffc03b7b490e7e8c69a30b9d0ea0045f1160713f38a28c6a8b926378c17ed654c7539f488d1cfdaf9ffe9ef6ba4b5721a7ed5cd888271c28e03f3235e67deca9023f6e5b7ecaab8d29dcefab20e637bc21f304411b769950ff4a7f5c4c0f060f3f1c08e8183a5d5c9729949509f503d73a18ba012194da4799ea3a0aa88026ece0334215a26792da859de2ff695206daa449317810828fb9a6253dbda08d9f5791b1f1474239fac0387f7cdbe85e28c95b8fe4655f4e35d1960f09f8536549855d10f33a7c79b4e3ee7372073393cf5cee4d1148a7ecbe448538fb494586ba5c7f94fc23903d8b5b2aa60dcac5647f4669be5252bb33115b9fac5377fdeac8ddc9c298b38cfa0b55f5f6a3cdd5d606446bde4272e5e5bf184e851eb9b52346ef678f8d761597245615a96f5fc7478f82e96a144e0ebf1c2ac10e2e6994f07b14c259a234b634555a8bd514de26db8c8e14a35865a89c830518e3cf76700f9752037758402bba5a83552fe368fef0afcb148c45ac4beabb60c40d50e6730daabb6e8702c27c8c0be7124e325174086ae73fc16b20d38dee419c6dcf5395c771733bee79bef093748a2613998ffbf05162ba739f4dc76149ee9c959d839214d48dffe5d76627c982d42c87caf0aac2305f36278f6f0e11614585446b5ecef24c300bf0dfce4ca244e3875b9b0bbd6a8f9338da6d644951fa9639a1145201e78ef7c47ef342ad49962e3c0b04a1ad408b26109f6465bd54556a23b60a340d66ecb04aa517fb2045a3a4bae231bbb9033eab9421548fb98663c39eb776128c441a33995d815d886342bbf2cb96db5f86f5bbd9bdf7591f7664bb884ed46da350aa5a62b40bc607f156f27fdc2af15ffd33b296f9308e708a69e381ee7aca5ec823886762f2838218205e3e3d405c60ebe8a200aae9c3a596698be213827a5136996b2147a4d2ec2578267cda20527f384b6e3982a09854a361e4dc206cd57ea97e07384954476b2f427e03fab78a9cfd3f720d27c9c9f80c8d6ab6a6c9d362a1b3a264bbcfd5d3bd0ce94ab5c4335c50317d0e5050b743bf3c5e005b9443bce0c7f11640e6824f355a6b2b1333401d73875e077432d5a188474222d5a27e98aa1089a407fe753f53fb9a11c2fb5bd68e910d8a1d0eb95ee20566241055dbeb0c1664bbd688d19a96d2f55a458f1a2556f919842b423e65e00386c203315e7a557b8ad979c55d60ec3f15ab41a775bb420451670145cb40eac69f4dedf84e3f416bc689ddc7b53ecea7b58fef6f1fe61d9f87bbe1e8809baaa823bb528c379a8a31cdb664cdfc19029125440d568cd736ccc0ce650e1f9c1290b7d4a8187d0d8206fb19348ad70763cb75aec03c8dcd0c74e33f6ef817b20f667356d0145898607983070c84182bb3f90aff033c18ff1b509f88da496dfffa06bb9999d3c00a2aad25b6ea8ec9d89b08151bd725065a99ab4647d14e16ca19a3d4d61cbd476ab895d485bd01025046a3cca72861d39f3db1bf26c4b691425b07dc0f48403cdaf39dd906421859ee03da27da2978ceb1251fbbb9698ff158d4920cc38bd553d8be8ea058681da60a14690ab5f7490df1c579e07c9e9b025ed9e57e20fce7d1bcea97e30fd9ad8d349f7bf918f3060110cff2aa8421637f8f898f35b9246bdbe2e5845635d669fdd142ec9ad1b78e2b432c07b6e99c510ffdf004f71c66750dc1949fa99a2f6ce0cfae3b61b7b95ea1ced46a92c9b4f226759f46a2c4c5a9ea96f4051e077fc422ef18fb45a115117588a15390376f6307e8799aa152c23a02f08b76363a804fdc7a5da83ca0692be3d1993e5039e33c7901551d112df5afea1cf748bbbae9069bf79ef5c21b0d0e186442cf76aebb52a59d5cfa3d677a1416b0026cd8095063425b156ec4d564bda151aca9626b393db11570b3a4dd9966d7189f0c767b09895e6a33ce49340c4ed8a43f2d077495360adad8b735908f463e0b3c8c82376411bf53f63f31ccd52be2c070d6b0ede24c11bb3456fe6a00b25385a5763ae21ecacd306b285229b1aa324b45279208d6c32b9c5216e842af3de3b75338f141162678e9ea4ecfc876b675080b37a3b094a2f39520a3406c558ecfcd59e996af0b35253cc6370e6589ecd4771278f236cde9c662271cb28c2a10e38c1560ee73cdc11f5afd0d9b85289b0420755746afb1c7e422a2dc1186a63e91b034c5f1886c1f88d18cf7cc389f709fa8d4c2b7c02711c63ae9391a52112baa21d653cc7406b65e7132009479ff1f20c0394733ad5eb33be3e5fa7817f60e68d1c129779842233e8cca7621a5419859b96a544010944984feb8382bcce3caeec60b3df6be811d328bcfe1fe816abff3075f05697611f655394dcfdc6bc58dfe724e9cec805e2dfe513be5ba11663a4427b523d80c1efcae28ae60748576a622c252eef19219a647b91d3855c05a60a1dcc9909c40ef2418178e10b2a3d5ecf98e4478eb6215db237e79f1c8808ce9a38d53cdc0321bae5d089521432306363556e274156da805114c16aac1eebc0509d5773384f804848c38099e32ca9d50ff528b49a21442e1b7c8f5badeb9d75b1dc8c12180c5452ce740d717423babd7f45680bf4fe0f7819f06280f7e74cd3deb0a2f3b33ba9d09001ab93b1866042a2fdc938c9e9508369f8a4113454807250a9e4401030b1122b4c6bc2ae6cae48c5e806c965c251cc13755c13a0546a76767580055432e6823f2178c64b3c01408d4f2ada312344f3913e87d95f37c376dc324de56122774297161c2da2ebee42bbfceba8c92aa1637bfa4e9b0edc1809b0eae9aa37c9376ed9754337001df7d0b27858f21782b9dc81846972665f44f7bb0c26262cb8772e50bc9bc193eec40ab788d4258ee3fb0491b074cb52ea5f89e9a3730a8ff5ba7f064f6337f3cf0103c54d1ab7cb6a6e8c3dba046354225ac63cd2a26d681a7c8c06ddbdc5c5aa8be2eb440cddb940644ad8eac219ce40f40ce6731984044c87c75454d478a518753b4a9a72514903c02b1bc6ed2eea6f9e40a003ca54730ea2ea9c9750bdc72d11894ef20698054539ca2400f9d67d7cb9648a1cf3c7a09ae8cb65089b6973a0f90de3300ee9ad2bbe74569c2b826bab3c664b6b5e698c1c50e768740a95c107bdcd528ea28c91856ff297695dd8f77299021adb8a269791a83777a699d2c2e5183100a6b2eb0fd5e4325a44650faab201aea4d68dbe834443f481bf37a27d97c5d0e2c609794558255f488ce8d973f5a0ea91c3b311cfc1c4baa902c30017b377ff6d325d66cd6252c0236925377f7ac5c10b97c49b76e0927863cfaa58ccec3661c28c9a96a5b5c09b402d4245fb8c873a82311983812af0f07c80a01953570fe8db2fd57ac086500aa2b7f13a8a21d7dc3ea72814471c1472743e61e7d7c7d9418e67301baf221af265051caea32db56eb8abd1bb66c68c3e087e037d3cec1d07b09d9fee66fa8c443c731adf86963048abd2d8f920ca17ac3802f4c2ad0507f0b00fa298cf7262ce4b68c9efa5562888a86c4b72079d7adbd82ad942f8278ce8079999772b12b79820c02dc6a665025d223a05e689c9fb287e5be78ec6c22847da2cc3af1274d9c07cfc2d3ca52c960a0af635bfe738db05989a2eaa13e508fc7e317f086bbb82e5a621b701d113ab26f1f1860e24c1d778ed32284b0391ff2afd9f36aeaa8eceade8b20aa58f1eb8aa4c8bc4d45c2bb576b209f420b16d54affe788f675281366cfb04ae1bd936cffcf2c5545cc77a481fa44a385610fa7ee931c1c9a2e53326ab68ae935ebc43c61de0babdbc1c211a1da1d3f608b9841adf41426e5c129b67f7a007ebcb9f86bf95f70712037c11fe99d2cacbd2a719b6dd096c9a14716118b78b013e37d0620f7c2f8695b7ed0c60ab33ccf4ca011831c32749ad5921361c34479d925e454c91d08e9494d0fa4d100d58efaddf3674f7a81bf74081eeda5d4bc3dc9742d7453e77729acc2b3a5f5b12670b54acd7cd78bf1023d8ebf7251404874a465eb8b0c600b174865052ceb6e715473e36c13c85c29678de0681d596085835403dff2fbc5a2b215f4e105ab289fbb5fa794bb1bed93718b01a746a668809e7577d596d5c5b2e887308c83bd60db01cb9679f8d3abea9ff04e4c15b19469c7f7bb38a48ee8c612ab7daf82b892d33438ba3fa9c41ea876563343b7da15e342987e344add2d07a20125d0c54cae96179262036c37762b4c22f7a48544c7020323c85950a9210a3f9d586031ae0375f39aafec6ef13d44c3ea67b1a42751f8a631359b64215544eddf722d34f43e81844921a59b924069f39b5b3826e97bc1aad33142ef63b4306b2f7b150306b16f103c55bce35aab662c1c18b6f66f344cead86644706deed609acd6f6c700a0099e646b73f4436df108ffa004dfb0cdd8a7431e385d4d01b71a4c5f8deaacaab11ae13f3fb1aed5d89cd47b12ad9cf2cfb6589ce13b73d90e449c4e9b5c1ed757244557ee4e752e9cf9b2b172093358259c38052246825a8a45ac076aa516f59c3fa7a6ba59f01d734b2a27c03edc3717068471994f1ba41cdc3f29761dbe4bdcbf6f16445d91dc7e06e2a1a40457b6cd38a6f366c95c2568f06bd56b1f0bc96f63c5672b97bd1bc9483b30ff2adb2c9e28d8ad258ce55743526cd9148abceacc863eac4bf5fa7754d084570f47c8e7c05c667b83afb0d3f11a577daf1bfd1da2aa1a080b71718f3e89f6a03128bffe84315b3ced16b63c597c70cb2fb1b978bfce5d58ee351100018f68d0d445aca040ec85c10daa4e5aac5ca8cc8028047f5b3339a78f9ff51eb6e61348216b334fa526f25c392c6a6dc566c73342a0187d69d4b17108b85537980aa9c85610875bc2767d6241a183b1ce3e0835c737fab001f18658b4649061f1aac7ebb2e6ee24124e5642a94430f1b30cc1afe3acbe949a93b0e5cf28c8123bf8bd5d839c0829d756079680464c00d3685627f30e7bc114017e06027b10b4900873690c2b5d315ac55307bd7a043d3feda9a358e7c925f9b424b4e65f310dcdfd27c5b71ef3c8558a2ce5e2f9365ceb8a1578d5018e83843494a0a2e7386e1f273a630caba8f319b9be9c8a99258d5ea87bc7e16dbdaf505526138bbdfd4534ecebef9a76782bebbacff88107060560fabff97c77b797c941880764393c29684259bd0b604a15a02801cfbece9378404ece1c4347d63447853bc62b30c364167c1876ddc7f5f9d067ca7930aa13bbe9d2bcf99646e46838e28e5a980cf19a41dfe6a4d51d369b335062a8e82fe0856b931c167d5ef876c3ee321c0fcd07c72e361aa67cbb25bac9318d491eabee4fd0590c6593a6892708bc6e4fc1314a4202820bc381f1802529f70df65805dd23a0ea8115986429127e94c02a367fbecb631d3191392e5177492a020c6eed33419dca7253cb789612e9564c42d6fded62021b9f0625e5240443118779c0be7438645633164a4c2b3467aa9402e5c8aa1317c83068b45134765255d8504b2c7c9e7940c2e9747d0d89ed835ee857bc9a6ab9a10788cc13a22625e94fe6778f9163da8ff85f5bb5462ac96854d57fd5d3847e8f197d2df15e9efe24ecfe11584d4d44c5d8a3b778dcbf425768fd1cb386a769fe88e2a63cc4ba5e7b003fd4a8a769f817dc8d3947a79654ea06578f4ebaa86b552e9783d99937b0b7a5dc5cd80c69d2e3941348736d562cdeb4a30403447dc01059f1a2e7b7c039c06944dfb36f5270ba73e93a550961e8df08572c4bd336f4b0007261273a384c98b3093f09eebd285b0520d0849e23c1896f4c5f2d5b2ceb7447eac36eecad28328d3e8014886954f9e50a60a95df72e29c490296829f6794b9d95949711a08f9395b275025e2bb3d603e9143644e61bf0a2911a930f29e15c9d7be9c071580a9575c4ebeaf9a2ed89f1a4dfd5cf0db56a57d92dfe735f164b08738223acd1f1d465aa46bb64cc3afcf74531d8552ddd15fd0a1286c06aa0026b8500c1dd6701b7090983a3cea995b4fc74489c7b46ec645c483d3658bca7d9b9cd4f2e1791777ef84f8999cf28269b5e940645529b0bb0897b89eb8735c5eca33e4480623e44896f56d6c219656cba0905e2efe8658b4a015e586f361f29b87b4a738b63340dfeb44d67e0015919b4a7c4fce9590cfaa50497eb18716ea31c329d558a37d61d0a32045c0214349d8701b3f6cf4ba639be60e6a85bbda4f5b65dbd8a010fe8ad7ac029a4db5f0413e3139e252f1524090d1325c187a205ccaa42ffea6585c7e0e594985f4652d4569242addd42830ff288c5fa750d48ec981bed3b08a691c4fb517bee4985b8de12638ed7df237b327ad9d497dd11759eb4b2546bcf11810c9a8b4c8b674694b5a69760227d03c727e2fa95bedf7873d9a88772b76974981e647709afec10cef7252bf543feb8d339b3d6aa9c76be997a3e438ed23534e9160441a064cad0df3b8d0d24939f5a864f3bb20f902c41d40f0b3d8d6803b3afd15fa1d3cbc0ea707c33096d208e0723b195637d6fc6364996d85b4c6eb7e702e9df7c7bcaf1250e060fd4d6802a0cedd077f58452530a4abab5264f4b580281fd1246ecd7117ae052495b1931dd700219b901e84db6f6088e542caa18f72d37901fefc79689b2807b401201af26c8313c317cc9e5d23dfe7c4b3843be76c312d2bb93ac17296b967bf319fe0d547a2b83af666b42a3e0d6244936596003c047c1455243cb61a7d172b955a41028b8ecd2deeb7b32870f091c90105d79960923cc0e6c5e9e858351e7cca7ad8a1200038f0c967be4fd4c067ddd0c69cbea00bdb01d25d308154c31a2f3945cbd1035176a72d93bb531baa20133e396df6cbf088c04dd9e07dfa8804ccd235d47cdda2dae4e2f5d974a3fd4f5c5e45bc983d9b10c9d99b8dd17e743dbd481403846d023abda15f5a404a69351d003f8272edab34503972a60a616d76ad5e233dbbc40715295556ebf686b941d6c1fd61466051148d974f7840cb3e02c53b50e97a4ff363d4a53077fa707ea968f90461df3e97fa18651645ecde6d6defbda54c52069205a005d6053154840ba434826ff34f94c8d3d764bfe4335da6b0f4859b6c2d0cd30bb4e99c02b77f08393a3ff9accb899c822fba8dffddf427a6a11cda77f80cff6d92139d2439ec964832fd4b92d8efc88187e559d889ac680e7c86436c39cff516af7845d4798e33878df173c83ab285d21aa9d96ee57993556b5231157e930a106ec4dc6a53f9cb47c94fb15f07b85f81173a02dea4ebfc48e839f4f7ffbf45859d510f57fac2505b84df7ca71a69cbb46801b62623e1d6caf7a65b2b530f565a75cf39124e6f930c5b7218675bd807d701a831f620058a13433ed3a2d5a18dafd346a5f49fc04929bab26e565796bd8f3b0035f67b319a02559d46dc74ef9a62288b1d801a6ffbf745fc77e30fd111c25d84df6e1445383db13dd8e9df5996cb9b2c194eca803df25fd195c283215c9798787eeced93fa2dd6e2c7c41781c6c4094712457fb1569ee89dc3a40b57f36044fb9f2871bb411c2927de1de69641e9f0c731ddeb6b345159897290c1c8513cd1cb630726572badf8e297b60df574ef1a84e84aeb5caa713e13c957679902e60ec167e2d36ce8bc81f8392572fe9297109f84bdc49f893eb3e9c2c009596b7cf45af4fa51141f952a21be288aa3489221ee683fdf67e22ff9288a3f8a4f0113edafdbe8fb248e2f6251fc514c41d0bad18170a2782f056b963985b25086933c86ad002ae0b61bd19ce480accf8cc222fa5a13455503ca69518839843703934a640bcc5947084d512dd627508e517878c2bdf73ed0694a7e490a84c8a3afae46b6e4bd17c7a4f4fd8cb3b539678b2db6388b18638c7328ea10873e628c49ac628cb13531c67875a4383cd2b7e42eee10fbc6e8ab0edbaf718b1bbbdb0fafd8c24bfebde35f58698aa2288aa228c6d9e18d10d2100d48ed2cfce00031dd210436bea98ba228a2f73caf949e19c270b617e71c9e4038dcf80c01279dc136e1065ea2c9c2759c61fb8e8e347e7e9c66f043dbdf062a29d168b399c5d8ea25233bcb6f9391f9c40edfceb2f5608736994c1473cea2b596dcff82c1de6efb3074c88f48d0bc0fcad20c61f9038b41799a5afc601cca69780261a098697e6031f00d4bd9e09c73ccfcc062505e5d8c74247b1e008fa25b0f73ce9e439c91b299b168862e5e10fc339add5dbc6593eb388f9e43511465f6b31d493293f8f57218ec9626f976c425e390e48e365e11566e9504d2418209e58f77066fc0d4f8cc166abcd43c955c6ee4ef099643888f91cdee9563060e318d8dbc75dd6062f3748343030d8f466ac9c034259a054a697cf8c8debecc586f576430adf1dd9ac86042f304a359a09441083819a6a27404dbf2ac846c95a4ddb18241faa873c332538525ac5295647ca18bb6e55545953b3f559a685b5e0f18625b9e151c5fafc4eeee6163111b47608fb089ad3d4fd0f789029ba6d19ce6b4ad60883b5aa53191610598609f4b1cc447bb96cff05fae223de4cb3e990c3bd01ee127497c7a90729e264e788042021f8cd9b73ce8409f51c0448be2bdf802e56059a7b446aadeedec38dcf35ba375b788f172760f8bc2508eed290a77c4094ee39a843c2b385bbd50b720395de8b7c991548d0befbdd70313cdc7dde6239bef7a9fee3a82cfee0839ebd9aa71fb3af97eef8a8ff25f47354ee7709b54492d028d56354ee31c49bd6b902e9d7324d5e6bf4f299a5a3cdd75e6eeeeee79f597de9d7ffe922fcb2f233fe3d18579ae84b6ca81c99aed46dbbdc98bed2f6bc2c2f68fe064f237c324078793c981669692dd4c0970c9952749251b6608c1c9e4e1dd43d6b002d41a3098608cee4a922472367ea29309bfbbbbfb1615b6ebedeeee9ee164f23c81c54ac243c2b5f10f75757023a5ad8ab020032d476a1cd1b2a918dbffa7d26cff199229db9fa6b4848167e7e2d67585515a23e7e07e4504cff6cf9e0bb33de701a9284568ec5e91ad1d4f3d2369b6bfd0c9e466a280824010f9b2f1eb9309cbaaca7011d2a66a4812224fdb3f16bb950cde7e5e703cd97989e2e30507ee691ce113f3c22ecc13db385e6939bfc871f305888543c455b9a8dae1831c75f8175453c38e6ecc3edbd39c60092448c140645c1821406b982e5d4c847c41ae8c686c8ef0c850e73fa99d150b9e6f8cddac5e90a9fa33c4ca05aeeb77fe742c6c3d6c38ba05f379b067b5c2d7eb62a3c7cbea0a92b2550baf576466fcaec0f8aec4b059f7dd70efbd5f001e6cf6558d783cb68832f62837032f4a6b14667ca3d0d8324604963c60b240c196ab066f46be2fee48ada8655404965a19ee96e4f8d44a310b1733346ae5c8c40b9f5a499a71643ca9d92d5954606a6193919a5a98eacc46679885dddfb9d1f1a2965123353577af533a06b6c9eebaac6d6a2a622cdc89d4afa642003dc0bca8e5c76893f5d554582eb59c4ee5bb4bfc4308554dc5cc049d8ea69b829dae9a8a198e5707c53d3d6e74dc6a4d269c1a5c3515b8aa76ff92963cb3e505aa1a464b2f3547ef56e14ed77d85e52093893ea4f588c37bc72c8e1f3e467398ef95d27b53de30b41c6432d187b41ecdbcad4d82fdab6ee4476e36a1d68eb023ec88fc229a4332c9c88f4e266c6d6833766bed0bdbdb3d6d0394315445b1ad7e6b5d2ba3f86e7b5e3218638c02e312f885f10bbffe7c200b74a2260cffebf57a2d71809342c0e6638c4dfc7a8c456c6ccbf2bebc2ccbdfc0ee322d61a8fa1bf6248aaab07fa1e28f4e9b445d6b8fe2120e330ebbdc762eb5119d73892ca6aa053b7f92ea40517c8dc68e7e9eb464efbe8d22e4bd9493803eff9073f024d21eddcfda02a50cb423bd2a62677bc5170e0d607798aae64892e779fe06769f290782ec3344d517d11355c387954edb2cc7878dff4255f7edd1f4990d730ac21d73646b2d699568d5eeee2f1580038c4fa6579af7936418865f5e993dba3f7e0639f406f9d10c7ee4dff8bca718aac1779f06b0afd7eb64826518f9426ffb332e6169110776c37e03bb61efb45fe8455514fbeafb7a1f455475ad9dafe9b38b9f94f6add1ef4e712b3b2e974cd537786eb4bd0a140d93be8defcdf2d9fd25a7c2494092c7f3f9b27e5b4d6f90938ce5339cb37c86b3cf8ea0ba6702f2cea903b78a575c0b5312878ff04fab1edcd891ba489f15eba5f5520036b405515aa3250e50e024f3b1b55663251aae2467304dc34a8e8a90376e21a2f709ca6718e38b23772d440afac48e037c847928fc08bf4972b024897da28b6d4a123348401991a4d632d9ff6c66adfd3f51f8cc43a4a09f445bec7638c541c2ec17a8efdb8c4568c11f861de0cf8fd322ecb4fd64baa1187e51986a30eefbf7efb6e02ad186b8d3687857da2f2d6a81bbadf97eb4a6e0a6d5bcb7bd4a349fe57dfffa28ef0beddbf6a86cf9ea4b616d513b089fa71b756f17676f968d315a78beb323700e1fd91bb480155c18d27887ad256bd5d9c598e4599fc5abdbd809e6e3a0a0192de84b5b5a339f1a04a5eaefa0c7f0504f6b538aa03e43437f8131df5176412e7f8702dab647c5665fc78db19c73ce262d168b415d6006a1954f1757d0674c772be7abcbe5ec6b4cbb66bb8ca1c927fa9cf3cbccb76ebe7533278bbd058ee9fda537cb66157d86542855b5ec8b5208487243aa14f41adeee215495a1f80245201291df0c7a7b76f0eb6a81eeb6a786f402774092c45d386623455717e6b044ab178871b9e548d6c476a74b39030f3aedc1895667b25ff2526ddaa6cd419f3fc5f5fddfcb548a1798b384b25294a208345abd595bbd58dbddff062fd66de233718a0dcabfac953fef9cdeac2c5ad0e7183ad35023aca07fd92c2c0f4460c92e96eccc291ef6af1f316048519cc873d3b95224732ef644a94a664887d25495edd8634873507a814279c88d021e6f4204330d18ec96a08d7c5696d9080201b8d0f981be8c4089fecb551424f4425f14f4426951d0e71d94de1c6c8b4a46e656ce3608bd4021f406af0f07a5463ed3c0770876446ea36ff002b7b0dff3b423507e0dc6ad1c4c2fd0681b85af51f536d9fe01b510e9dd619889d983489b2e20c2d5559ba5970a1f95e975275df4acdffd91e35b2e9ff9d5f2ddb30177f64bf865e2e843a44db64a4ded28bd3b7c04e26769d3f55571215803915e2a2d470a53cfddfb6f4958a4fcf78a20d04b7577f8c8b99c8fe7ec1afdaabeb7e83451b4ab7d787b9a20c01daa5fcd8387f7bf541f3ea01e509907b4113a4369525254095d5241df473ba025644424404fa028c4e8acacfbf31f9f8d530e68111a020afa6e08809696a1f7e9daf091ff871bf79601bdb8304036b47a7f9e66fb772db90450fc754ffb0fa118d0144bcb9c82fc65f4a06041b9b2da73b3562b94d51edbb5e8be1beac99154bf637b31ba1cc9b7b6d11195b6e1c98496c11f6c68fbb135da62c1a4f467204e32ff94c08d2e128bc4748e0c0b951093ed691b9d437294a53b2ef6eeacdb90dbd71fdd1945b73d9946df69f159fe3952bef2d97d114db49a7d57192bbb5f89f8a1d53c4526cb37ffbccb3c28ed5a7c76dffffed99ee6cc6687935cdd659ef3b4e80ca4e08856f3943cc5918a6816d0d2055832bcbc9d5335dc39e3edf955e7ddfd4eb4ea360cdbf6a06e3b7ffebd9e71ffdcd6f766454f935bcddbfadce4e1e9bcadcf8d9dec3395965c1c510e4af8bde1a49cc61edd8743bab0efe70fdc7092ad91fafe2880ca38139081e1483671f7287a4605dcdae59dfe230a827ffed2f198c70df08fa9ea8f81bbc70f476066e3b31bda0cc3ddb35f1c3b3b8a84535b3aea54fb1231d57bcd7b2a20d7cc34dffbe98f67332ffff24fd47578bd7d1d845e120dd1342d2859bbe470dca123c7312453d28e184754d5fb83f0bef03bd54a12cda9ea3b7cb1c804f9452648f2853ad596f02bbf48a2b6166e704f99c597cc6c7498929bc4e9fdd38afd0ec1755d6368c837b6ff05bb755da320584d1fdbdf0253b7ae2b0c9b93ed3f00dccbcaf617c04b47f2b67f0082ebbabaa00a7ddb9ff65bd795c6ee2566fb5740aeebbaf2e8117d20f863b1fd47fcd6753d2178dad8fe227cebbae288e5b63f05bb755d6940652bdb3f046e5dd71936fc65fb1fd9d675bd812bb5b6ff6cd4852fd8d9fe2082ebba463939596cff0fbf755d73f8cc1bdbdf836f5dd7a8df9964fb1b89bb755d4df02cdb7f0251b7aeebcdd7730fd6f64f45dcbaae26b80cbd676bfba321aedcd367fb3f8ea15fccb6fd3be85e44b6bf04bb755d83ec205061fb47f03bcf6c7f0e19471424f904b77f11fe69e0f367fb43807debba42ad619f19db7f03de6978d2b1fd65389bd4f6d7a0e1361261c7895bb67f86e0baae3622ddcb6afb0ffdd675b571a18c67b63f06dfbaaeb6ddab4f70fb0bdd34f8f4bafd836810fdc8dbf6d7d7b7aeab0ea01091ed1fb3ad57f7e407ccf63f67d8463f68b6bf695bd7b5060e989f39dbbf5c897c907efe6c7f58705dd719e38cedff32410ab663fb93795dd7550713a1a9ed3fd2e0915db6bf3843836f1cb3fd431bd1efc566fbe7185b996bfb631a1aba82e4953f7fb6bfdb64eb6bc6f6bfeeb24d9b627a92a961bbb8f2d8b98495d54589628be2e50e1f24bcfbf1673585708e73f1a40cbae0d95e4436dcd619a0383c2edcebd563a1ccd51c2e1d49e27e5a3c80706758705185c13a6362bb2d315cbb17de8dab1a2abdaa0972f5882a08d6e9f29be3e23722e88b0304f2c9b1113cc5a048c52546a587ab4f0436db5410db94173c50a76f3582643532068f0d8b2b9d0ec4e830c1832bb9e08c7421b9fe7eb9df6f0c1e2727d04ed4d5c10a5e9de0333be87e30b6a8f0fc62ba2b2b9d1e3cc192c44ec4f5d25c61b158cf10178ab901e9c46491a1857c3a38a7edaa05aebc527441382c4e1f1b16ecfc6243c12fc0386be6e4d1bd6c9775851e9f15bab410c3adc083b142558b1d04b1a0af0d96569edfa90117b40ae3e3a2cb132475941d5fa420f94285f5254a903c5de66e0c2e471e3d6b68ead86099f344be569e8f383f563db65128d8e3d1d13245cf6dc410cc412c41204f70e268abf3428feee5bed176c685d7ef0c103d50827666bcfc5838b27bc17eab1c3de7afcd961e9f0e5a833728df0d14f4fcb20eceed891738595ee0e0f400853ae0d617567e557e02b21ccc8b9f912a9d602d4e519a50d9f510d3e5061c0a20a162430ad58a3441462c615080b97291c38b983a5ca4baf22c6047c20a5caed8c951c9912cf08ad71c0f2e1158af023b65ca64d932c50b551ba9b185fd43c7d3947032921c4c485eed8fce9f13e0dca25a50a148e9c38b885e9a381141218c6f2a09162a5052211f7c8bc06f05307fb666bc8002e31af3811f7dc0bc0066cb8aab22641c8c070645835483ef6405a9f4e8e1b952c2ad058cce0c0c3e42a06d7b7ba0acca34f9486ed4c282d46b489fdc1b2e920b1df10d0ba90e537aa854056bf0dee499a560664a1f1f58807ce131b4040415a9292c2b27b4d8d1e7015645aa604921cad7a64899524b1136e1141cc1b6555051b6b96d151499a8110fd612c105172a177ab489b3239f308f9205f7b16427868d0f1b2c0f007325125c059d234707e5094c982c52b00f641e3e2c3070e2953df8e5b92fe49edd6d4bce61b5cacca811d58b73cb2235069fb1f1bafcb8d2064d1f3ab07ca3c808057fc9894eae0bd80a99d4b805639158baa010626141875892656cdb1e9e2b3c74c41eac8def1963785529230709d1a78e9d29467038b9e5461e1653bec953454432d8c9ab8a4c6a94825788fdc8f3a6ca9230695ae4c22f17461da0902d52eefc294346ea87332932eec0e4b48c21d18ecedbf6ac80f418c5307b60c58ac79d40ce0b161c2540bad418c096be4985528c9c94c8c1f3c54b8cad1645388891658c1321515990cc2031d2bbcb881280282fe0ac7af4acd8725242c5ca4c941c983025d89d25fd657e0294adda9d06aa29283c3551be4ca9a94a349c84819b1c55baa2e61c6951f327b73dbdc605177841c2c3042c624c207306098b0c78ce97ba2c53ea8f4c48923d1024e1e9a9abf38896a3728b921840a6ae0aa8489eed9ffd0b98218c6643292b757eceb28d10940d4570a45d68feb89119d905e96cc49074652891608921010384e91801124192ac096c45478c6c302283c80906194646f46c0812c7c415c0c1a205334b9e80607441b6e8a8b637492243562a292aa53c9016096334554603121c19f016244788b64c0585b016293de74b36864c1962db2a1f4b76d0b6553e7c604027a4ca00853d2e5401a9b3b5a20697609a24ab9b35566bfc497f194eb2e6b620b1c6fdc936af3b2a7475e123248b19193b7c590e94a75e0b0fbc30c5cd6b4f09c465e785e094d612274e270cb1b1a70e579e3b4fb89861b63fa14366a84e8d3ab8d812a01d093a72741d124cf79c85ce10d30996f935521aadd51e0ddbf6f6e8d8af6d7b7b9468184d88fc54e1e974bce989f9f59122c60b90296b6cd8352cda904893e3277e409bdb56ddec6c55764687db56dde66cd5fa5e6434b96dd5ad8d7bd0c373c64ab06dd5adcb2eb7adba4d91fdeae01e58a70e126ed26471b375c9175cf022a4deb6f7429a3d6edb7bc10c0d8b4326585034b6588152e6e92a5c72813f575a5c5b4c52a053d300151a5175b0c4c8d3c462d991c2102a7fb6fc9c99d1686b42254e40a87a5aaea480230c919ad51ac265851b50c008c920059b9733b916e60041009d1e2538b953029029293d51ca0aa20e4d4c0c4cca1051ca1151ca92520a95224a563c12565f643d2eba5db46dcf6a6a8bdbf6ac888829877adabe1d2fc0f892e06200581028615fc9da1724d61ebbdaf5034461ad394a6b52733977a4046b8e5a2d68baf4c801c4c3c29fac5a199a18bd2fde20c9d54ad1c468d21230bc5a399a186dba59b0fc3152535176b12fa0386278b89aa31ca2d454f8bd554afba8081ab7edbfb6ed7189da15d0b6c7a5c5deb6b7058f0c3db46d6f4b9acd80d2faabb4462a6d83083b621ff519a1b4462ac66d1046e9bf041c209079207a0f69918921b3451c5e6de83dbc87b4c9c3046887088a3668780c6991090cffe13fa44d191ec33bd5ec976288e2bff835c02ff42ff4ee3234ef99ff85da1df3e90bc447472e84867fcfd797e3c994ff9ef788d62febfe3ca7f31906391abfaf5a3e33cb9c69da3d41b3cc1dd1f5d9d1c42f1f0794651a84a11c4975fce4487e579fff9c8d07bdcbf9e015b7c0b68c2f2f8ae5f1c1dafe8ee54819cabdf7965cced495d3dbe5bf2c7c1f861675007b0c7ba77d659a0098c343dde733f7278ff2a80a14e577da3955efc51803e59c56d081bb2f900353fa41a41758d400103ffb59dae4540391de5feda278976e3ace918a3ad03a2dea403bedfc4e4e3b6887fb938310941675901d87af5bbc73d087f7f0eaa5327db6e36315b0d0aa3f81787fdf89cb73805290282053ce7b09fbd3532e776f2e0bc46c06e2ef6922c4cfd2a68c82949651a894edf546b175686c00004000131600002810080884429120092345d27307140009599840745a30164804712486510c04311c840000c22008024000220e31a318d50ed7e74b2d240f98433a9bae8e5b869ce533a1cb90629b003021a9620d97d049c5f27ec0a26f469174ad554442bd56dc7fe55ea1bddde60a581dcbdec3d7503759d108bee1e274c9be15f71f235d4614ab132a9c5bc6be099c5bcbc0559185be9f8078ff524537e67d56630c77a95f6afab4a22880a996dded1620ad3fdf301d5a14cc044306ca4ed531c4c3d283629d2450139172be36139cb8ea06c56c32d133bafaafeaf524d9329f017a7428ac5f4584b61b6d98aaf71e56fa8c2872120f358c0cdfb5e9fc55f7152265b025d9f59fb872f767a6cb9ca2fdff49d6bb9ab10052f4085d716eb48f0710a45ca15c4dbecefca9e858a1c8d36476e2c6254174e2280a5ac58f5ea382e4c943ed0aa83f3bedba0e508a3ee927a3a02702546053bf97296b5c6783520c1161123fb6a3a6159c9a4403ab1664f5f64539e845be9604dfc42b7f2087827a0c1d7428d826a55ed131b6a237e61591fd8cd0a3a9c8999cb241cde80a58846cf0a1d7906e6245233835a2e992ad00368c68595f665d66454ec82f8293b12f4e04e7c157f52bdc61c148a59a7f226b0e5cc1877ea382fa3d44e01789e4d2283e063e4adb1daccbd2ec126c3c5cae2b0e258d7638f98aa3392be35813f34b15efcdb6ec95c6eeab2555e688eaffd71b702228f6c04f78cb604022da6ef2601c1dba299889acc6ffabc5ed6e3e9d8301809bbe0945d9260aee9227cbe8ed126baeb84771ba1e793b7f4f55519977341d9a14d1ae50427448ba308b56dcf9cc7433a2689b50cd4e19ea45cc4e570e94dc32b99b0f80e9e6b18945b24bb99149751534bf0b1238be72c549b77c83232ca7407b1d81b41a44218a941c6cfdb623c2f62e75cd28a8dc26ba023e2fa1eae835d54d4ed1ad36393c02f5cd5cedbb82c92b37891555ddc395e61d455599978e9ee51444adbae4e403c559b1c507423ca9e82da348d4146c7d1c31e256c15046b070a56e0fa858b5f606e71c2021cedc1f3b5d06146b27a81940431d251940cd01a6e09ecc3a805fcca530e9ec9d090c273282b180c2225c1191618ed2ba1cef22660453bd4e4e8932664235872bc9317b2e6490207d06141821aa7c67867a59b0a7c7afea8d821f78c8324c5a2f7f655886abae281881b7eaddae2e01939422bfe9e92c51c42495d5fdc4f66fd3fddcebeaed14b8afa2fcd804ba902b1a054d20dc1f26facc2992bb0a266e6cb133bcd28b892dc623e06a5f7a9978b8f317e04fc7bf28c78e7c9e01bd8929e68dadc2805caafe65fd0c743245d1b924ed0a60c17332ebcbaecb9822fa154a702fab17e28a46806595727fab8ce96554b897d32481f4c3c8ae78f4a3b29ef9f1ee12d6795df8d3b86cbe22e8948b5263e529d8b2dc48315bc189ec69b23883e2c190d7dfff63221d56cd34456ed90caf9de818e260da0b40275deaf0b8e4b254f188f03fa3353dabb28f5352c2f3d98e218d0a9d1f641360b0fd5e392af677609b00bcf78018fad3af858b068cc9a215e43263e0b55520b736ba6f1f42cb62f99296d26dc271c5a855bcec010bf33cfa8bfece32345a6777966d02fd1df6f948253829f3f15f8ce0b2578449db122da9b118454332d9f47c978412651121944debddf272979752cb05eafcba66be6316fbeacda4a6c9c1ee81bcb5c85e7cef37fe4efafa10aaa432ac544e8a78ed25ebc46fc155157b3e8c6bf6ef2d8d498c09334fc0568fbd379a14b72daae7a3e27f298dc243e4bce9a3700fa323a698571c935a9f3d8a4027422abb2f13ccb42d49b0de1c0be9178b267afde156aa3d455d1da018c3226da687649b1179b6bd5ee33ebb9422efaa9877cd7a98f5d4a5adf613122ea397b66199c32e4be593722022da1dea26e511d5a15dbf9be7cedce665a78ac0cd85dfee54d171c4ccb43dd4131a0444a063759c9c347056e97782c82fa5900c256161728376b8faa25e9f10ffd8530ad29ed383159fd4c4b5a7a35811e468d7252d6598e1086b1d62da46914e7a4f292fabe2b068b42871482c9b00d73db9756e4d628fa2e9f562adb87297c6871a87a3820464a9456341c5717a925348bfdc9b90991a0489f2d705afaf1887b9df1d7031225cba5e9b441a2be0a370b805e4487630c721b03aa9073fcf0f71817e7f3ff74404881e08eb06d761f94d73fba73c122ddf1aedffee5cbd50d81678aee7e164d7ac2dbcd843b8a80d8726ed3139e36e7e6350f5e025d9c94f0b3a8320ee8d9f271bda8e6bf08c8770b31c5248169529f6ba804e7a05ea911c5d45a6bca6eeb3dfadfd56e36491dd8c0ffd8a13b008a1c31ce0376d066dee3ae166637fbc2f19af7eb85413c72d924dde4a4871d3312683a2f91172091df9bb4919f996bbe05852e7703ccda08e4b644355192a0c81125f8499527aa1cd081ee29d4a1a90641b5b2630fe6d068af09024f0ecb910674eabf8a4961d4db21424fc55c95c93ec1da34a8c7179b7d134a0c7b2c73cafe4122d861cd2738cadc9a2e2050013b8d92041b6eeffe6c7590f681bde85cabb229167a46c24e8d03f7465538cd473e1d442fc656e54373cc793b9a1049ae73be630a03ce2f3000487bf0e54eaec3aa8a29f9a7fc01fe6f0b320306fdc85e3b2dd65341b2978a07a1c86c9ce7a9cb60459b734ac82bb250644f6249eb57150a77b63d3feb93a9a7e48560fe7420f4e0bb8e40935ad9abdb18d67602794ac8d132b9170d215631ab47a4299ab6e661e23ff0c2f92bb46639e36773c5c4dd4dcf16401ac2a30bd9fdd6c27d082cdd525c5a0a6b9e95716f8468867524c0e88e08ee9bd1d499f7c5915df0b3fe01ed28b67543e0729b50022277644739909a270b74783fd49bfc79acfc4558dafaf461e3d14ad7d3b404432f27bd3716c2eef6e1abd3568195ac573d402888fd4d1b1e7ee4d6e181cebffe7041cbf01b2fae29480798b3b80239294087d0d0ce046cabdde7bc9a17a197f4b975db2b49333e6866993dfdbaec826394669d824be03758235cad558bb623609c6ce9b564546fb11bd763f36efb2433fe85b3cf48247ef5e225a9d46eab213239fd409e41e999bc41287e10715364b066798249840afc49e4668656bacb0ef9ce6a8a8f9d1c1b42fb163db8da573aadf24b15ca8bdd8866f213c5b278a03e8043bae436d4661d8abff4a8abdb98036790be0d57d819b2968507f90faa5635c991331cad7f0b9ee6bac2ae29bb307a0a9037fe1fe3fe4fdfec0cb9251007df6ab14a05f4ac767d605c404b2b82812878d57debea002ab49e5c541a1b79f1a644481aab6300035a83ac5d72bffe2b7beffc6fe883645224b27dac1a9adbc663b01d99225a49253ae7a7fc5c420e6d2be2e8ad5d764476ece5624d89b6bb29f77679150c1c20530fffae3e4ad7fbbd0c354b692d0c35959f9b93433086699debf3e84d5967f81c57897d6c05be7e3171df02b3ac54f37eb7ac593e16b1e3a4502421d278ee242dc2665f2f3a796f16e416287f4ce2380abc7720fa3b460fa1a8ed91cc0735ea80ae095ab05b2b6047e657e3209521b505aa6a388208730a2ca140a251896b5b86a55799e5c875e62de81d677ff459f82b58328140dc19e2c0d65eef53d2b8bb86fe8025301811f1fdb2c86b660a4db4486802263c9ab974e57adc92964eb54bce039efb2c2fb5f4ee1714b5301d6cd6080532328ee499b08c1f4af902238b64be1b37d4a9aebfa18fd143429fd0818a06e2a85df841c1042756a2ed9684daec8a0b1d83892c069a7dbc659e7f271135ee73ffa1ae289107764a20538c60febedb3c33b1b34589dd95bf273d92ab99e584cd3aa124df80c3a90489b3ee6e3a8f51e83c19cc88ae3d6ed2e366f488461ef6e2e8b4d2af02c0fd24af17191f732bc141558c1b0692b926c1298dca271d1601a83e5628a36c59fe4f7f4ed26647b7f7171550465a4269fb0e739d26e3cd0c18d8a5adb3a31ccc7913a9d3525409f9c842a569570d7f9b49ddb958cbdcfb6ef50b45199b6eb33df7d5bfc8f64e0e0e1dabfc809423e94a54f7e2185c8c999d401183f2e6984f236cfc25214ddc7ff2ca690ce6d46b11e84a6e30948f5cc7f20cd6275ba4ea6487fb803a4e227461c784fbaf92a146748653af06eadc67a4131d0c67a137394d55c2e3cb3e6cb4c95093da6bc2d83f85cacb2d1d86b9b17c46896819aa8cae0237b21ad1223be80f6528cd6c83ec80b325667934b35bec094bd92d4a33397c693602091d03cee8e8e357ec2d67f3e949509f4501c7ce1815e60a5127556e9f06500b53c8dfaf13892e7856248ccbd0f891e066cd41448d7792d2e3da3ff7644876f4fc85ee4a642868c425a3c7622ff4b7eefcecc0a3c2028a88ee6ba7e0cf3fd0852de44e82a52846998446e2d67e4038371f6627e3982434730293a85322d1b5f9f16511658da0ce6f6a806979a274d0e6032ce0ae63ad2da943c0d466650a6c8fab5a134180ff77e9daa552ec3d599119797b19936844bef032edf871ce45111dd145116931cd7b09bdc2e5a5b4eb54dddcf9937b0eeb8b8cc411980c316624282807e3bc09990687570320a38c6e793bef633fa8c2c2de63120fd079405af234341e1bea0940e1ed2d31e8eb57a35b1828f2b9792d9d51a1d9394917fb3eccec4e3ecec2ef4c56ca7793804f6080f612eebe2ee39370aea60e9c1b80dcd61684e8600a8ce1d1ab819468558fdd9198aeb1a26b000f9a290baa29c894e43f17713146278b5e6e597f63d4f06a2f54732bdf4bc67b854633fc0a48310036b1ea709fa6f9f04099220f6825fc1f69581ab3bb1513137fb7903b60e4b37bc859fd6e36b42c63b1db93caa796716f33a209a96e65b38f69a3bc300aa87952e8a1fd6499c24dde75ba565d53f1c60be1edb5b9ffea45d2e0619aa1c47436ab9b9022543fd0ac93108d2897da0d34094c71b53b038409855bf36393001e29e0d7534177c729e74620f22c686ddc394ab45fa51c31cce2172b10d07a9e45ea28f284dac38e809f0ffead1ffda2a748485d38c9229a636cbedfa0fbfef08d3924ee8580969b12d994e322403e9c813e6f4586d67e4a3a5e578d76aa451086182299e1a29f0d8f422ebff9cf4b2dd62107fc3ed0a9b0b5768901d92aae91eb03ff53089c29684391751bdf68fc4058ae2ce110a8963769e795ceaff82ec9ca6eac5eed0f0861c9380a0f1f474f05ed500a25c23aaad2baa93e87a297fd2c5a1d30ad01b810539c67fe800a2e85f614f21ff76034e6655eb23be4249fc277a510afbf8781e984e22f5986035e690e6744529c8f6e0bd825713ac660c09451e2c1e294289aaaf2d1c7d355f2d06f52fb2bb74842b82e4ac8458f24c256d7ce4650e233632e06af4f5024333969ae3a49e8cffab1e8f42b9a1d8de784b538044df1d90dbdc5709bf37a2c2843d5b99bf81994bad0f85dbff61b55fe5ef3a466a36d1c8a3ad8ea36eb897efb0b8aff9e63417593c598af1adff2fd3bdc6c3708335a1c0fe6bdfb53c76209d9ee94c7c838e4099af7fdbf89b29cf8cdd4c23a5154f28dfe18c26977f7306ee8acb9db3fac924f11f69fb7035d605c0534e97365733207382e493414b8b59e241bd860841fe3e4329351d3fd44dd4b0698463418464911a83cd6aa298abbdd00860456443cc892d0d30952f90b605bfa607c4932e8885832be2402e84c00d484d922615dd6da84e4cf6337dff208e9f7b1930c417b1c6dde4bd2b213f3bf2252f1b468f415c37c599cec961232584b1126b446d5881bffbacb3bda4f244f73bd51f6d5759251262363952275a124cdd92210b74f072f8b08006622098582cf653f3717fdd18a9c1f0528cd4b76c1d4a0e211882f9082983bfc8b6662b67cb17924886c9d6b84f18f59cf03c23f8787b8fa61a8c4c9e946f9a3a8388677dd6a4b2c8ab3aed1ee18228f239994e90d37a3a8e26f1005757babc26798924848be3c25aebd503f4d2198e74d282ab06620cdb234977852de0c4dcaa071879b1832e5155493a3dcb7e8108c7fd1fabc173e4c3b6dc360b8c1bf0cef1d3600ee279e73d009dfa1211e1382866053c5e3ee643f1ec4b8a66dd797ca82859cd0e21ca1012b6e006597c5d9edaf377f9330dde3e8e5b7f46d7d9fe1ee23d440a5ebe408211f8d50d21723b1f3d11253ef99cc558760cb026820b30400f896d0c11a77f9f1eb4151208063a0f4171211b2e5cb14469f3b19dbf778b13203b3c441b743faa5aa69ab729dfe636ee4759081534a17cad1cb75b89b1fc8e7e45ec0560cceaa4384a7c9c289b7e81e07cd2d949862d9c46aac447d9030de21c10f4be854aa090dd29eba0c48f8aa8a14af04de0e095f405d339e7fb5d76a5f9188e6e55527c00b94f20c32240288878468a5cb31c8dc7be384cd40b5c321a09986d60c540f64e2789eb378bcf200d86544ac2702f10c028174dbd19ee8599b8e27b90d651e2d4a2417390851d1ff7b886b3bdc737c62a382774bb792ee7636650fd93e1b21cafd3a79628a9c4ac6702f6c4a8029049e704ca19d84bf506abfe0b21f50bd6fb478421b1ecde21f062c70a37a2c3f297f6f65c64435cc041e26360d95a7cb285a0b21537dfefb334c4502c7ed3210c590a4bcdd7104e0569d56b14aba1ef57f644361067365839869cb0c72756b89d6f962ce7d40e530c3fbebc126f48aceba1b21b0ee716fc347e1732ac134f874f085a174eb7d7d8c0663385dd1d3468c0850ffedc7eb11cf6a1056210a6dddefb3430a221e31c1c067e1857036b86759321c32b1f872ec65fd848e506a1876ce1dce3404b42af626c0acbccc6a65ba8b636daf822f88662f7674e43f4c29a42c54ea4c36549e106808606547f2d75d570d7bec0a2d570d73ac062d55039b71deca03de86f868173eda0419b3f8a06a5bb511cb103853d7ade62da5d1d3d882dd9033038d26ae4a84aa132ed072f997c404a2bf7aaa116c220c334df5f6639864b0943a3b2056486592cb22ce787341824404eb29753222b146577a01c345765d21efc3cc706b730f3bbf14f1ac83e60b1e47af75391030d6ca7e60c71239aa76670a345699defc2887d8c81ed60bea6d04ba92251d61a89c5e7a599d9ea64ea71affbc089c694f1b40ae11609de81aa1b32ebb7206cffd23b88de016660e3d06e8a211eedd36586d1320b76b3d1653619bab202f0d537141ba810eaac028971a48ee7d883149b8036866c5e0675c908ff7455468f7cdfb46cab82696232701e0d3f870bd5c4475bdf2815d943666f7bc433924f1adca0faff60577eed0267d8c76a9c498630c28e19590f40d5aeaf5592f6fa49a3fd83528f0079f6442655afe6e8eeb6c7f25e824bf5b78d8b4e62b3e2ff6122ce5831d42451f940ff0f03a15663b427ed322400fe39b90204b5a68018c9e4488e3d74ae93829601a891e7dd75ef3ebd1c97a17aab034954cbfd0ac017f564348364d0c34a69e6c8a66dc80c2ba0365c08baff90fc334ce69f736a9b9cc5d417f2213cb50b5c1582233daa072c7bdb7a947e08923b4f4d62b81f0b1127838e1649f83069e1c8bd78524623b3fd91b7eaed9d4fbcea22cffa78c40cc20b18055174aa6595eed61912ed9f33201cad52ea43d9c6d9d92cfd3f90210009ce1762b99c18224afec8012d91e63c8699230b7daa1bafc117c136cf6ac9f0b1fda5b0b44dc6da3a3f64c61daa3bb79735b4a72c847b1943f831406a77306ac23e99eddbcf38f8ea540cf547c086df4f1e6ec7bbdf0e903a81f7cb452d79c756fd20003ff13cc822ad7c1cf281821f5cfbab4b9cf5639440844c39570c0cf61281415aec1d092014591164f6e849c1de05f8c9f3980c74115635700b0e2f26d31f6372ef2dfae039b215f6186cf0fef8d677bf7e8a64ee718cd69df36f640678b78ff567ae2b9f7f1f5639ff03268094c99ec7643133cce771875a6b4022e6e8d63a7f843d8a0e64a67554cf1130d9c8dafed5cf129994567d5983478e9ef8ee779e483672071eb507636636ea2c317eb50780d379540f16398c356254abf5c8860fd2f3b7855c72b684a5aedf5c417fa3ab66f821afda56e4133e5b8df9c1e266d1f19f9cb630d5818b5c694d943034b6ad2fd278e8bfb844b37483e4e41c21defcf60cc8787b343a811f0ebc79b83f8f7def41a96cbfd2b992c8b1861e444c1a405d4aa3706851fc68f4d288596e75fbbd2b07b530d3513d1317b16dfd6c871f52fede032f1480f63c4d5803a42038acc482ba822c604fa3b837cc938f28c0f760d397f0f5322a0004546c2308d09bd27cb07f05fd1843320cdfee822a1be0ea0a12cc0bc76360ebaff92027f06372b5da09a967b80eac2da55e7c43d2a47f9417e0add5fd4651eb48ea9de85f9f595c494ee4cb3f7b4479b4afa5cbd2f08e4cc46112b9e5c1865c2260f21c3a12c1de4ae21c03726e44bfbd483431698d010cda5463a3f0ec1e7af14629a4cfd5670436a8cf75931fbc38b53cdf1b2a9f241f8c0e0fe1ffd2cdd57b71239b45bbf1a519b253c5d58184ed9654c496c40cb6fcf90c7e626d40393b7eaccef77a65a0f2db2e3d192e626fe4fc9772090f8d39cbb8ed41b2ba9249765bdd04d259d60ebab1887e7ca45827a04f34b8965d249c142b2a38889cecf2bb31ec14ff19524ec2782fa050f85ef59beb499837e3c79a53648a663394069c8364b87dab4e755f0b6ee981da91ec1b10f42ded265ad5cd9a1246462a04890a1e213752ec0ad122c29ded61cbbd9797da69651cea9a3f9ce1d2662d31f88c16f7116e81b3df9bd6d73802a2a89ac7036c89d66e9905e8e270585772741698abc592967bf8deff9073774c6a0969e5e5c46a0906df3823e6a8caf643ffa5d1a6bd4062310ca4d55c85914f014f81e9334aaea0b0546158de22d7ccc12a8ca3893910ab30ace5999323d84ca15fe75195d223be901890fc1d8ae69fbaa2c80c1c40e034eafb815b51bf076bc0174cde9e3bdb71fe796b57be1adcb0fc21423cccf09a4223071d73b110990bc78eac132ec6a3050af7ad2ac09499f9b7555461d66bb2c882d879ae98188a5c717018c2f80cf8a011c30ee2d2c563fc1622899a89928bc377fca40e12970f408ec77eb76a86e45f2e329ad9410a915e4ec2054c8fa3d15daf6330d5f319bba6e3be6356ed8ca5aa0a8e8ce3d909e83e1befb826cba82fb06609eb4ca0b95277cc864abe15f5bc4a87217797002e7883ff280c945e17aa9bc7cc3b4fba9cd10186ebdc7c2e5f95cf9da15e26b30348be9a099b91c7f44b498117bef803f51ea01164b233758c0624c6c4ed913b9bfa362f6d2f828bbb950cbc7002168b21af8f4432d681fbf4367bbf982a99459c3e56b45f0dced78ccb2e5bc385060ac8dc28b0ee2f83a73d830184256af963d4a63f79fadc0ea45c02f5deb60f2cf3feb0f0b0d10f832ec6bdf29bcb8b26eeb7e2695793a172d21e3cf8fd1a35ecdff78516a5ed0d24fa4e5792c15df7758495310d132c25d95c0e0b005a97280b6d51013ea004469ddefdc80f55a41fe05d0217ea1404b8a8b206cb1efb049430a6cba0445a246bb857219bb56058146a7ad4e67141b9f861126a6363bdbcbca08e2c024490aa2966daf00208d50faaf07e5888f3bdcabe7cdb1bcf38bd172ba0abb76dcd3acf6301f22e1de960667ec082001a3522b5e187a8eaffb0569a3d0a57b4bbb45358a1f2dbc560ae0d1ee11ca3429d97a0db71be29c079126d1bc6fc259a3c7b183b1adba9bce5b498810f449baefc2a1dd828428af0747e44849b51dec214e95e702fcad4e347bed2f66cf05a66d0432fd74e478bdfe8071f635b3cce5ed3e9610eab301de821ae8227142bb5a00917e80494cb0c07e79829cc372c12afb5de71bcc30219d1fc0e7cf37158df2b413c830ef03d663cf26ec86184d89224d73ccc8259adaa4b7e8d7786fb6f4f5ea02b8071157bffedb16a20c9dfa6582486ed1ecd15909470b7343d655359b8734f114f74d7b3d12b2e4214d8b8f606a84dced369f96cd44988f375b6f88f77d9284a3a37a12fce92bf75416597f5a5e4f97d1d408cea3e7608261bcf06b88d4ae200d65a7f089cd2b98f16f7b3929bcbe0202592629970e51bc9c025431732ab9f83e1689e2134854fe5e394f4016a442696e2f588dc790680f23418f162fd38b118675beb5f833ac822ff4cbe65497e4afa9313ff3c0c4a82847d4e2a9e11096b2389f583a2e24a3ff3eaf294ebfc2ef0c344e3404c60c813ae6ba0d8f6d34948f0a016523dc66f933541a43429c42eaf2564a6f2953f085bcec8334114ae15a3cce7ce3f13d8e69fe43722758023a853c817b147ef60a670d7e3ab88217de7ba61ec8d0221d97e4e19382676b8ddc8a280c95e4ced38e08a05caf048c0524dbac39c86ad7ee3673afd57b15a1e4358f2d03913093878ab2d5b227115899cc2d5d77f4bb6625e5219c4a314c49a7a5d5ee557cb87ca84137412068e8d40cef97e7a004640adb58e5d50a609020163e43499a225bfbff6dda7a5dac7bd6ba6495afa0a588e23056127dbd3695b6ae608972b8c996a7eac641c331eaaefe8bb75d741d0913b87a89b074e3c0ed333dc97fa1b43eb56238e7dbf6e838bd645b51365c2520417636670204861e2bc4740a5ca9d86e9e33911dffc5607ea806a08cbb80c58f6fdec0bff48066adcd9ff0d50294b9241e877df5fa11110d0d7b2ef3df32faff17c080097959e7be401ac7a52929ce260b26c5d900463594200c2329a640da9c86f703fbc3d21797878a8c1b38c0ab4c1cec38633ddc01eed92972d904397afca582a0f6a37f80b2420c3eba91fedea1b4a320c6fe28e144049ef62d5cbbb36a624c5e1cb999fb184277fe2992de5ccc3687e1414aa98eaf49eefa15b3590efbe68da3bd0a16e1e8b3427cf5469c553e80148d02358788979508a08a327f89f5ca0dd84708f5426bcfb5b6f5f6c770356ed745242010f1ec11b2b114e86e8953db7b737b68ee54376c31c510866d83ef62af1a21e76c2cc301e01fc16741dcea9ad5bbb589e1073d1c7f2a53c2f1344f84a9babfce840bdd752f08f60421d558a947c88b3616e5bdd16178e7e376f2a3b034ffda56e7df8061aff107bf90d9773dc2d36c79a641e58cba4a027e892eab5b9c106262b9fe47ee1f651953ad5ffccdb395a89df92fca9aa719e3481f4c3c2ae683843a831330f182fde56a60a111e5b355e0285dd6505ec85db7f77189e098507572fbdd98d8b94b927767c7ba886cdadb63bb7bf4602d4d05f8122a8b116f605d2acbf8f62ceac374db5b59e3c12aa8a3ce6837a7aa871950ce4a7fa48d41a2550061a552f7b1d4558e24456dcc5c784c29af1875b98d45ba81a0613ae3e7aacb541f9d482e29b58507e5adb013ff9c26e8188803f88c5c63d719e4459cb1f449a08d3dc8304b9d33f0ec62a898cda29d301c54aced91839cbd238b36ab99055a86f22ffc766e69ca601939a8e019b7a42f7b2237aa4f90b09b61cdb31c938183af0a1f50521246da779640975584bcc3f2ee3009a920e08315e1f6222314dd42160a3011d40e02aff95e6b7e62e7706b89932ef9a0d2888adb612ea6ff343edf2acac3d9fc0147bf42722bb26eb753f0bc39f7d123052731b52f3fa1fc27f7faf317b581a2c97693a850033dc5c08b084897bbc4d5a09e62af2518a7ce8f978203516812940e0cccb886d9462cf8af132706224636f9bc4bd683cf70c10bf0fb2a58ceba04c178a6853ed6246ba47de0298616346f0d9dc3fdc9fb1eaf5f2881e55940d0d0acc3cd657da01cae79377c87a692011ee365243c40d6eb93af6ac3b7dd4743b0208cde29f5c22368e5f03a8b2f3524b1c597aca583d430edd81d9c60d99344a5f3e9ca2aa2133d63d65da562a155fccb7126106aa06c625d016327bf3111d3a76daae2fcd4e9d22e67429e44afbb9bf1dc537848e79dfcee41f6a339468b31bba049c4c2168d60ff25f4676f4b885b006275d3e22ac8e88639a13590b8c7c4271448a35e06f3bd9a342d9957ae85f3b5120f59ee03b0bc18d69182ac3b0a8097cc3f333816a6f2544a2f842c2c77b4f442615e1e901977f989581998dab39c42239a223e72665e3df3701b83c7daa2282faa6c3623777d075d69805f00819a542940304c8634c45ae1d41e925d105db5376d86110111e6b0af1c2816204c00a8e644b1935cbc4f4dc866b0007e03193e588ec5d2befa63564369b36b92a65a15a60fb03ef5b0dfef4081a4fc2398d206957e698a64a4d86b2ec18da7e6d9b86e214278a85636605348eb16022b2acc3536b49af63dd91c0b7d708ab886cb81ea76f9371bb178d7074704835be5c87feda7d90421b81abcdbbeae0903b832ee056207cea68c808d5a4e457386b8ae90d2e8a6f935bdeee875cd25ebd36154298e66c07ab7fdc46f91fc153f787db75fd0387ece79b534ccc18345eada8aa4db87ba80b355dac32bd90c0dd00aa7732fab03392af8ac8187e050da43baa6095fa013aa4fa6649bc6e7b9efa0ed4136bfbd8a6de97e0c3adf5045b15dd401c796e339a4ed406131b5d5055ec832cb28355e34528387d6336cfe0e9c25072a4146698d7cef9d2fad445f6b0da06bfa045bae870da69ac17b3462727e047ffb428e2b0b4b234ed2d89962ed1032abc4e5309960112366cfffa6cee9461fb5c99361677b886b6ab0a17622cda853a4bffb441211d02b78cfc01b428566d4325e32467978a1006d76179dbdc115295f12b78f6a7d774c9f167ac956487c333537246008374056ac80d5f4fc93cf6d57a2b3b786cd6744c6a7a20d5b1c6604ff667d74b1c75b308af25d69f576c6c91ff0fad881b7437502f1b7df9a12ce529a7ccadba9cbf0ada8de5c501d961c90e4e505d6189725bbafe8425e45bd102a1921b5dd990129c994edda09c73b632cccf6bf3f7b5f4e7632acc615fc943670bcc1bea2bf8c4928518a89864127ddb10458f76d5e64a8ad45923b6c478ff7d83bd59fec00c0493e5e868bbb112fce8832071efa4e446b3f9283ddaf15e65292348f664dc123c965336e02b54dd8a9413a6d21830db502dcdd35021e0d63986fb6cb551b50a9cd7545f29310cac705e546e3537c2f1bec306256a15f683211a0e1c4be52611dcf98955e9e83a1d0418b30b4d678bdbf74520d96cb430e6dcfabcbcd97ce7edfe591b6c176f90d0a642f3f285f983778cbc3a1786242d339414d553da4a62e7e8e403d99d76152c97d4f536a882c237f1eaa474d9b7c236250436bdb191a5d5fb9108b4d208da05fc91f455fb91d49abbc595419ea41e943ab20127d2eb164d810577d825af6b180e7a71b59121aa8ad90575c77048505fb8e9f29266f96863b0d868d666c7cf51110415e45410f894a103bfb6d158c2a02e3afc6d8ffb73723f51eff0d6cda5edc6b7cc1aa5ac40ac99631e732052f44cd0db45cbe41089a00248a69f87441cc9cc0f83877a5ed2f17b80bdda935bccad3b898c2d5f4a11102ec69d19d6693f4c36bca031f93468d4444caae08b21ef26aefa7e197cae4cace00c8ceb5dcbfc374451a661d0de39c18b4f35511c570b6cdcf306345d87a1974137430bb74589ae9cc781a81b5939a73dfb29adc427454b08cea15a2f85b4c493b800acc80a18427de4de42932fef99334e367bb58f3545c7b392cdf4ffe0c342742e68d9ca8dba81955c01fd92b5ca21c0aa4e1bf311791f61bea7ebac2c03642ab48689e9d19218fe779280b7102d41e7f1e287b6cfb8faf88e8e15dbe7d8a4c26ca42e4e59d5130dd512a90e8dbc72a909a8edfe54cae073a12029ffa3e1377ba59cac67a30071b51e9cf1e92948ec8c2a66e69e9c0676ca14240e8be7c577357d135f50d851cd7d0f5c9b7416a9813cba66dd03ca250d97a2288f6516f8068e519f1095aeaa59c5f2aa26041ba21458fd9dadfaa11003575418e09728c585d5eea7061ec8b8bd5be9482ed7ba37f3c5047bd8f15efe32b01664e404d27ab7741861e8b23a5ae48530369a91a8b79bb42c390218dd34d9d7cc43f267fad1f714f8e385f0f042a1c87930d00b803eec341b09cbcf0ff58343f3cc02df7300709b96050621dbc422d5b72511f3517f1518e51f530f5c55ec6a057fcac224bd5a5e88f99c40fe9b0c45e88497402fa173454d878b377421b9bc3ea23aad74c7c07d80dedff8d45b80e151f6b4f4e261af367696c34d2e613c76e9287361a54ed541bec37b457c09e0475e6feaf9fbd3e21101c833bd6bf7a90747db099cb9d12302afca3c7ffe76b90502cc2751b5a3e7c819a91c26e5cbc4812cdde45345fd8c633417a25cea1700a619ca452f2ffa0f047e106a2c9239a3088ada15e220e9bdf2f03e4fa40f3d516abadcc6a67f5e75ee85ffa332c3d55290de64f77ef3a7f4862278be0d0efde7b640ae96bd1a9e1a0d52693bf450f75465a1ab249c53cc6800bce6b01875c285357b67f36dd457c3cc99d797bd1a17126105da419c63d89d52d9a436ba0a70acf068eaf7bc56eef84b57f877963bb19db88ac3a33e097160fae1b59e532d40a555f928ccd7b349c9e1ea7316dcf52d9751c5f6b916d8f6ebef106f7e19ce5798aab13574fa17e23dfdbeb732fe47ebe672c65dd01acbf15fec8b36887242ae2f041a99ead05795b1a34c8a37718473e1352a152052ecb70be1ceade2b24189f69f7c595ac06b445e275ed35d346d89eb0ab120efee9689253c9ed6395c801b391d357441978d21494fc2d7c88efa50fb3ef18a34c77fb142e8e9b7ab2351a2032299cf1bf339fe070625bf3b0a5145b78b40915a4d565d4f198ead9f911e6024fa17290d19ca0d37ccebf363588874d03f962090e5687d111a7d989cfb994d0cd867c2d32d31284cf06383d3d3c45d38f0da5972d0a8564d753350123a571931b3403d40ade54dc39800e9cd868384b5f6c22c9eb4a95ab227c0854b8f5a57a212190c8fa6e48e8ed070c83999ec2574c7b50bb7c80b3a3ae750f5671213354706953b0b36abeb96b60320181b3384e252dbf296b912f3c93b4265d07561497153b67101105cbb1719c9ea1d0981f663e7882b9aa9e5699b274a321ee7d786897c5853528fa6702a2386a36f88fde0174819142ed60bf85df3ef8f04831fe9a8ec88c3512dd765c889541dee62829e99055db3ddb966b69c8cd4dd80e794b0d7a63feecfeebb0bef3c7c1dd47fdb5edd9897fdb8d6c30f242722e1be9f2637b99101589eb5fa86434825888eb277d3b8881ca4dfc710f06b465b443cb1d1c21b15ba2bdd8c46ad9afedc06f837cb07413ca99a05a3ec3849d5a47ea8bd8f0119ef252364ac50f326df040b31570d004be1b70650aaab981244487b44fdfb2c99d638e5ba750a8bfd35d62aca728f59717188e45ab8a7b6da8a5981e573bc10487836b20bdf32ea565ddaea0f4c0d40db941ce97174b9b8f52b46491f45613d4c9f90697465443b29b2843153c241cb6a0d302b84cbda7e02728bfb09641edb71e1627c2cfffa2dbf08e3369a9758acd4f3ab7f5eb0aa37f74d00aa5b2dd27d8338b9a7764cf789dc35ff941197c8cbb13f7d19fd6785ba639c6cc00ee58dbb138b28aac859c79aaf288d7e586c911d802745101550064d29f0352fd6a434f28ae7f5826d3a6057dfef131042292904f41bfbbe43adb59697907b70f7a924b39b92405fc2590d50df217c85cb76df8077b66bc470ef948d5a0d6d9f8aa209e5b1dc3ebe179ecd282294d3f95edb69faafdeac682caade12e546f9c7fd112f0721060c03f07a4334c2226b54846fd71ed42174f168d34fea114c4934d1cf2ab244434e1c72c6c8494b3956b1161c30cf5d3ddf97dc51748c7fc8d73a7bd3377c6f025cf6d27a64bdb23a40f103cf841dfe57204d495eebcc573e006b2dcc546603b0f93d4b48f74182b91cf859d72cdfbd369ca95d40c48d8aa1bbf122a37924261ba14a01b9887ce9dbedbd77191ff381546776d5a63e69243cd74c93a00178ab22e40f2fcc8fe6595d2e034d82b2b72a063f904cd2de1434e41a8720350646df47553520cd64fa49718e6a75e34ce696d2d825473bbbf6a4ca1af38f42c1a17689a5a5687da69e8f34a2ff59a9e7f02c1a6ac2dd44cda08cca362f9d99350e4a0d1521f2be154fbde064cd10d5c074199953c3615fd59e009654704b9987bf4f77785c345f97ccf8ba8a85d751520b1b1f6f3dbe1e8dd25a303602060ef8e93833538d6676329dafdb77a2ff2ae8907be05896cc7cc3b6b4315f7eb5f01537920f817621a56e6f2e160e87100d5a19f798f419a7a21215e811c52d7dc1c70448288e77a941cba19528ab324040b903c135596fb66cfe23f67282dfeef970941838d57ecf241b08ed239e5bea715d2ddbba4c1318471ecd80d8f298f8ad9c01095b736ccf63c736db62cd03d0f3a4228cace74a579e0bac42b3874d33b9b7d7f629527ccf35366c86337eed9261b1ebeb71bcd2609b6ca7dfc8fa289a50beacb8378d059de2e8d5d83dd5fa58600a015fd3a368e373c83f0157f9e5dc15a7367295a1f54960a15aff1c6dbe6f33b82a374c365f66dc5e80a1be42eeb0fe3609bc3e5a435d3f1db9373364581cf79ea3cb75a237551de8f9140617458ddd4ce629aec49fa2fccc1c5fbef0da1d21c1c7529838738ed3df0ac9e5fa1473e6d8b57064c5cf2eb46e4e9f797b87d26113b2eeab2a10f12e3e09ad9c65ab19a6359ed0f9d461ef03c05e92a643eeeec1a765d5ea492a1af780b46f58a72a1673ae25393695962f95b77b8b0356fc911c3a5017e89f6d0f48e565c5e4927bcc395c5ca7920667390362bd0ef9951bdc92bc174c2e06dfdd57630dec7c4e71424df0104845e554144a1b6f94c8d8ed5b0b7c2a0f7710c277829b218bcea729ba916366d7549236dcb354a2f4d142708d6b8d56a9900718eead5d8f2e415b9b0b867181b23871ff3998e4875fed9b9c1b8cb26be4b390da72b67cc1ba6c1a827f6a41339e2ebb9a2ef98258979d4e050c2de2a5b0bb1acda1a833ec50dfd847a392cd5600b1f966105c26a186e62ac01fe232bdb5b8a6b151310b4f4adc6d5976b77111f6201e51e625ce9001fe4a24dfd22ac97be93734a131b7d8c401c3ef7a616b68bed3b65f6ee887a54e71273e1306a224a19d487be910cc44f8eda12d897e76ee168ae72f205b0df20c91d8a19b85bc246ff7e7499b3bc7219165a3e02d0f5baaa118d8d779b50f726f7e7000a4ec58e4f676c2aef4280791678595269e5844428f5e204e952ad00f45c6fec6ee7d7a0fe3fe5505b70a33b6a68cfebf1e003485d16c5e9f5461b3a3c1ab924e48823ea317ca8e1f7c4fcef3e52e132be355ffee330a358900c97812e699bfbc1e1d489f4010ac39a0c9f41b84e0f95159809cf790f436534d38d809d37046a9e3332360149395da11c67fd4334353189888891926d9b0da98e4d715018bd023d416661c5c24ede02dab4098d4e7c01124e9e0b1088559187d2e159599abb4b53dcceded091c55ec0a3c68cccb46326b7a529f6f9f3e230a51595c71c19681a78ad230a87247826c33fe1b4b51042c039d048508583634e98a1281cf5e6646abee92947d1df51d47b011345a7b599ad6a377c25299f8146b674610d10a5b40e61330560e345d9e6b7092e4cbb6344c84b5d718f86ef939f68f282c6425c01118c54e78feb734001e847a2d5c18e58868d32009d09fb93bb9d2585b6022da0dee4194a6bced0bc74295b9415214b0ea9a6b42389cc5f35f687834bd772e7cd809063f64f5be16b487ff7d6cfb8a0614250d7dc9c64c1fbb23923a5d882af013024a580bfcfb2f307ea3aa3dc763ea5bc1e86382dd6ef2393f6347fd3500f458130bf1c627ec875ea1ba1cf3a065bd2b778c3b1e5968087dd021b56d6842ed3743325a662f620b69ef35e3ba4d62cbea685078e8998298e82a878865ecd472b1171d0eac192a83c579edd7df7781f6a9150f3be060ae5ddc78ce47a8f4e1f925eba57103701320491f429d43ffff50fde0d701ea73d302835fb73e103a95e3a0f005bfe4c95d9299f42dc1ee47943d4160ec924842e49316e43888dba506661c27a23b7d532bf34ef00f49fb18042fece45f0b84bcd0d48116627f3bb10d08d6833a8f01b2bcc3803a388a3b3037cdf9d585e1da581518bbabae5cce6319ff38263f736424bd2f31226ed2236b28b477ebdd434c92ad79de6c16abe9fab0d296baf358f8b6f3e5afd2e2ad1e8dcf600ba6588746331369f6693f68c9d6eda034f2f5bd54e340f25a367a565db349d57a549cebac48d2bde268520448fa19774d3378cbc82ba8206ebfc34837c7c234ef5176995ddbece9b7c533aab3da80d63585323ef0bf8701bcc25a4e7a3816d0d03f4f85840a27686fac92a2384e048f169eb8aa9453c9c91ca32740eacb7be2430d4f62190381f01d337a22be2f6e89592a20746fe68ffc80c083837ad82ea331cac5a234753883b1d1841b5b0aa4239f223ff7f9d2f3d4d1a9cfd6fe0da627b4fd42d32589f93933c9ffb809e152f2f5c46ab68dce011b81cfe6f48c3d13d973163ceabe2aa7e784a2a113dd53ef94fb177eca41f49cf0a64bb9da8b10f6c2f0ad757d535518e043127c558932774a3e94dceedc490de5ae64c3265d26b9a764870a89d558436d0578b546ee399b00c9ad4704613cef102b6ea2261a598790756b0a222c6a509c02145d2c9cc98d9bfb7727f7afa822a9b0103db4f333bf53429644da627de08e687f7a6fd7c497bc3f9c7e3fcf75a1e3b23839467f9ed63344948263460cd4bc61810938dc34423ffa0bc4ecf2876015d51dc9cc667b0df3bad497cf82a5d4738cfc90322581154bdde09b44e636d1c6c78b359f3338f7cb5d2edbd32b95d8f26bedb899d20be6bcdf4836bea19a305ef174e77cdb69cb3a447fe6bc5a21784b2e4127401283e871387d0dc4a5fe457937b662b512314898d785e6698313eea588d9c10fc66b36bec5ac4481bd340216aca5859585cfd09127ca8fbe82356ef09510dec4887699c587633bc1620c4a3d1fda69fc65db7286dc8ae492d017e31f6111f62bdec6745ee6ffadabdcd531fd0f4c60dfac0761e3afd40819a11404f0afe6ce7e9ec2e68c7528e8402d914b0dee8eba0729cc25a8a8ed9606970ccd0ec4831fb5a6a00434d2c00d79d8fe343e415cf87290b8c2259efb9a313356375dd1a0cae09c3d47ce0eeef683ee5c0fbef746f3c95ab241ed3fb21b7eb22edc295876ad7608cfe045a39ae2abd72e22577b5f26a23aecfa138be4ac1136f557215cd8d588e5e72cd26f7b7fe3225c832cb79d543055abe410fca33807b4b3030a9f1a885b308e424e073e567aed2248d05d56237a8d08903b1823e00d2581f168f4017791a7d59e3cf01ecd05986bbc643580468efc10a0a30e83989ae8790c34c12bbf061578de17a4bbd7b4511eb93a7e814fb424d484c8bde5de52ca24532108500880083a38d0bd93fbd4972f5518958ed4fa14fb25238c936fed0d27d73f7ef7c5fc9252caedbb872ce58734f7d34da5c545a53e859358304a2bc46230159e62aa6c2abfe1e4468fb22f2e092979debd1bde6484128d74b847e44fadf7ca9410c7aef9147729dc487163f7d4709bce69d78d3ff63ba7fbf026f76d7d503fe4b2cdfdd30950e4c1500fc60f3a0c147778c5b847f7801d0c85d33deaa37ec41d8ec2b54727278ebd022d0698291094748ffd8a3c987dd457f430fb403fe20747e20e7fdc75966cb91408b6934ebbaeebae9b9dca0235d34fd23dfeb5492c2212c61832c5491c8c4eba8a78c3c98d798b3164776185ec39348f4b19b2bf63a0239882ac7b6ea2b8f6a353ec774a488c65691f89e469343e72ca2cb3d8d3c6181bc8142dce50b97d4a4f1940832c719cfd70a9bf94b9cf94529cc7c1c4f8b2a8c8c176d5146d48846938c912589c49914712875116694b55774facc2864c906c1f8293abe990dd20dd63d2a7828b003c5d3237f7aaa87bf8783715cd2425fd1824288a86632c89834142780a00179f82bbe120c923bf242ed0ab95e2af7ab03d01a48680d2aa3bfab70b01b3bfdb006ed1d224f79626d2640d726f49224c7eca9f7b8b9659b612a5bd0f1dee4b2b5134eabb42f0dfde7ffbfaf37b43d12fe6989a4fb7ed2745406707e1eb54083cd9ff73d107f37c0dbb2816028230d8ded9a205494b946c236ad33eeee3f7dd78b9fdaef820b8e283797b2d622160debebea5dff69bf6d1a5fdf6d4a53d9867eec7e1fc5c202089c56229e49b272efdfeb0ba42f04cdf021208b5a738ac5f71091a76694f7fc32e0d0b01337d30530c3ad85776b1736aefa04ba9e1de72e64bdee99ef6bc5ab1c3d03e71ca2007fbfd29585f3fa77b9ebf3b8d120448d0a34e084e864056b9103481182b314e969b52d020db2904ea2ceeafa59e05392d9151b9994c50447d8112507777bfc0f7a69452768e2f130a3b1b63031b9b642f452cfe8352ca12a9f8cfed8f56805a098a51ee8ee506ff8b3161727794491fd5941662b91fc9452188fb20c1c2d20b3858335a5e682f1cb01282262ea5944b34f990b012e7873b0bfe82329ce90566dc9d4ee93e521891fd5b289752ca0ef294a28769b8867b28b829f982b552caf872cbda74ff3e706f8cb8f1dda59c92ca8c7ee1cc7236e79c73ce39e7b49e573f534a298d4579d2a4dc4fe7944f9f52cfc08c22e5f93745dcf8d30845cafd71ce396717c3acdc36e7e49834cf1cd3ac584ef9d346d6e6d4a636351c0ed87e90996a13c758a454c45423f78211e5baafd1d0b7b28617e74b26d373f7b5eed2a54170b869cc3d744168b67e36b2a669da951e8a6215af92aa14b95442b12689b498166be23fb6892d0a53b4a4bd46b929d669ec41bf935a892b9da58e14a8256e6042ccafd70080b46fe7adfc62eebe40c1acdb3d7807cad47dd164f64893c91f7ba4c98e8e8e8e8e5654303551b926c433b24ef6f00ed463cfa99278464d8af3f8009cb8a13da2e23f968a3d428db147ad2c31ca412d57daa3eef13f3aea1e9d2f1287c3fc4e6a60e407790ed97e53b0524a29a594dddd2de5f4da3c758b0db687554a29e9e790e2c6eeeea6b81f8339a7ca861be9519e734e106ccca6dc9c10546a9cb5cfc9b79de53a1b9dd8d88c71c63aa7ad486ab4d65a28248e55cbb6e3bc561b39c662b19846439745db3a14eabbed5116d55914d79f6b91ec6a28702d5073f50ca05028d4131b8e5a46d976ea9fc43568ce311d750fd954e9152aba24c9e33e5dc7a541ff2e49fe74b3cec9412eedd365d1a0bf276dd2ca14102122ce001a910af10c587d162a8cebca38b94f87268bf6e9ca882d348f93dec0bf43e33e31e66019ed135568b0e36207617c1269f0c03f16bdada19d5827d9df2d0ed9df35230b45fed822798be44fc3668db98f8d3938dda733179df44f0bfdf353bc4d5351fc69b9e6f072e3bfaed8c854c6dec66c2c081aa18dc5f00ac9c6dcc662bbcac66cccc66e8c2381edcfc9cd14850c3772337d79f22f39be955f8a9832ac72337d999231c8cdf4a568d29b9b698626d7c8cd340ba379b9996659663c6c4e747233cd62c80f46f61144ad50ec9075723341d184ab7141be11e68b32dc66f2d225a7723379c19269e466f2a2438e1f93170fa05cb9999e4093e3178b2e8ddc4c4fa020c729b3b77b58c084ed8c33d0e410498e2fdbf2d40ec6fec1a4fe50f7f5fb2b4e52a531f749ea3371c1f64ed8fd5c90aeabcf6125dc57eeabc5dd374cdb628c542976ec962f785c3c2e1e178f8bc7058790bd5f5b213ab9fde54a979cfbcb95a5dc5fae2465cffde50a11b9cbfde58a0f393eaa1760e5f74ed8c96e7eb8ddcd45cf9e33ced83975f3c39d3ff1043200262ed8dd23a1740f9bb9f841ec1e37d99f1a758f76027950c91efac8ce546318a3b7c480e505d44a321f99fb84338afbd024b2978042915f27d3b4d85696de5f8184b8f2dd486b3e4d01a159f2ec2672a6f3fd152900f20efe7278e5cc32e7d7ba6dbdd536d2ca8d42d5cfa98c90d756924baf18431db95bfba2561a98b62d284fed5faf577cc1f64e18631a7daf4cff08491ebfb7fdf260715b68f67abd5e454250ab91dccab3465cdbfb7985b8d3482bd39e4d6ff3b943c5d541774e6e5293af1cb3ebdbbb77fd3afbcc55a2720c75e4d8b4ca769c2a48e67a3e7864d7b1d33c8ebd9d1c7adc87ad3c67ff6cbf933bdccd61afd8375cc37d76b2cf3bb8d34ed3670a64833a74b8efe898e18231ca95dff21abee33c6e75af39bcd3676df92d9f726e613d46ef183be764f5fe393f774da3dbac92f32eceeeb8b6aa99aa3980dd53e7ecffba5d2e5a15697ae8a89820c061441966c600f38297192ad10823b06180c8968711b6175c210133319429c11815a830c410d3c8227dad514f3a758c53e761e02bbbf7f65effbceebbfc7f7fa53075273776e7e1e04e8e31de98c325b91d0b6bbb7d7aba4bb0672fa8c1411b355c3aa358dbee137239de441e5f7670ce39512d259538937a761cad46895aca19669832be9c728b2fe39c6106319a9db3c1f9ad4d8d6edb91d47711ff597d7719f7c1d16fd07e770f0d762c4f7ff0a36a7367f5dde6b1b6b35db5b6b3f52307fe0085748fffdca65677c6545c7768d85240b4df5206e8eebec15ae7bf6eada9ef07ea5a8ba4ea565b13c1c121e7f10fa177f88fc92e2448e51ae5ad9d1c24489c9d5b236a3f8d61c54886cd08fd458e5f638c4b35f29cb347991164597ffcc6270982486b5b197148c4c60beefcabd113688ed808e88ec32a6eb7e39203182a4e61c820c64691134838c91205164b5409252faa382b065ba0608b24bc5a95c400c5191e5062054ce860797861cb152284b0228bdbf74809893cc7dcf0f3162ef864ffae148bcedd7e3cbee87283e6712d05e484ede531b51203f05754a1e91e404dd23eeaf5f28f413c9cc7dff10b8bebf3ebd3ef0414aea51cd7494e4a89c19deef1d73e00f40ee701f4fad54c13c1b797fd43f9839abd146f5070ffb9b72688e0e07c950eb773fded6790ee996fc38c5b234f073b83196e5833e8a07f5060fcf91af48ef6f108824d7d760f9bfe41f58e5eb50f7d9d1963d4689b71698e292042441d6e79532ea12a73ef4ce9348dd591c50d513a9adcf0beb461c69531041bd9714561596d5e6ece5b6ef4e40b63764a29ddde45377cc290f73997629702703cce0ff95e072bd1f9ef71b0922a433ad853928393d486e1541ceffbedb7bff2e7bfb3f90d874640eb9f43f19791d17f2e211c44f2788f5ddb7bdf30ecdab0ab013a9ff34370dec34aaae060d73624e7693c0dac440757e9b04b48e71252f3f46b9e7e0e762900e7bd776d7808ce7bae213a9ff33a3898c03e4c83c6fd242ed4bbecbb6860d7ea5ddbd3b878083f8ec78193b850f884a1ff1b4116bb1480e35d3f04c7bbb0922a43aeff5ec90d9c0448f2c87fdc34e4fbffc74a5c98c887b16b854f18723d7ef9183c8d1f82c1d3a0819380436cbceb5d58090d4c021358d84bf84320d7378c46d7f4dfb01b1f82dfb0c7555c42e86fbfe1126a9e7ee7b0cb6416fe6efc7f351ec787c17f9f8d8f299710ce0fd9799d0d0b794f640757a19ff33a007826301c1c9c9d07004e82f33bb84af73a5f922a433a8f833f1caf040723f95ee777de070e3b5807bf2b8c45381e7f03be77619710c61f61ff1f61bff34c60dee7dab04bc8f35cbf839378efc2556ae8e7a29ff325a93294f31ef6de0606afc4c3118a8dcf79d7fbc02107fbb8e1de781a8fc1dbc0e06f7c036c3c0dec12ba71e323ac468d8f3911a6b37d110bfd625212a58e2900e61252bf4abd7c7f825aadeebdf7de55e8e5d56ab55acd94664b1e0ececc4efcde4dbd7f2954eaed27e5af56dc0a07ee6cbedfbebec5e0c6ef34fd8a579202602ea17b0b3209997cadcfb5baf96e1ee7876e7070922a2b3cd45abdf70d6b7d12d70abb1490f3383f44e73d8c83697c0e1358953f396fdf7d566f7fc77d5457fe786f697cce1371e188e63b75c290eb69e024b1cc021a163def23ccfb86d1f830a2a181abb884a04a583d0ae7c35886fe7c07fbbb38dd7b6804447d2eefec4d1c949acbe457e952ca8da374d39e52eda946a30d33eecce14c85a0fd86ab839a4fec4e354c58ae83b4bd95288fdb5b9adb8619d73555e572f36cbf7deea06ddb5e25e692b94482ba47e7ed5745db07913ffed569dba47c0fda2a0eea9eede6ed3f21c9b3bdfc88b4fce787f36cbfbdf2066a1f4735cd4e6d7635e6fa7282b97efd5e46b7fe9612e2ca2fdc0e6e5feb6b0be2e0f65b06340811d19f2ba7ed8b4b7eea55bffa5c0a48bdea5d12bb24eaed6321d5db6f980f22b2c8f7dd709790ab1fb2bffaee57ab4f927afb4c607665edab7e88bd778595dcb7b8e50943aabf38494b1eee1be612b2ef5a40afde3524c2561f612bdc30d5875785abb884d47630fcce0db358d24d87831694238034345115e02419292971d0c1d6010737543850cad2847444054a08b2f468a0430002f01b2d621c70d081009a30003eec2e59caa62ca594f2c9f5619ba1f161a3d1f9b0d364f0618c791f46229e8f4db27c19a1d87c18a364c97a29ffc36805f5614cb21f462cddc72e597e7dfd8e0f2ff801f8d0cb353cf8f03bf810e4f1e12b73f01bbcc4a191d894a59472a50c721effb8c1eb43edf8c0af1580cffb6a7c227c38bea18fc8f75f91efc647428defc8870292245f8c795f248a4546506214998352e291ca4ad217b138a8f4c52c0efa77d13e0fbe771e7f1edfcb79fc3bf8da7938f8da794220da237b5e003a7ab04bc7d7ac4387069f0d544e3985830ff29b30321995c926079f81c753e43e32070070e0ef31c8b9612366711fd7471a1f3f7689b30826da382696895fb4cc4434ee93c67da813ad3ebe3771a39adf3e3e09f2c7838f8f44fe74f03e3ce2db0cbe21222338181f0cc0179f832f36719ef839a044914939a28294f3452b495894b22cc52e71d614c338e814c794f9a2f5c5279b4935d5eab26a3cf848903cf13bf890489ef83e5f4b1e1e31d6d9e76b98075fc3787c0debe06b588eafb30dcf385fcc3731dbf855755fc74cbf1e93e7a765f9d5ccc1d7302b3b924989eeee425ba69449e1ba4cd6aacdb09db3158123ee238974601a6305d3982653ba1265b2817f5978943b927e6defbe5e418288c8294292c7232c07971b3526d1231aaf7be69c73ca22e9ede81eff1d1146e34b1e6fcb8e86edd8d13dfa153d2a51bd6bba5a6e263142283189f961678529022766871eae38838b3b9cd8b803052e68453b343aa552d08f2022b818195260c60606ab7be6ac45dc2b17653dd59e77e57a57aee78397a4e3bd2410f4d1835d80c354df6e69f864f93187feb93d049165bc92e5531d1db8f1b58f1f38ac99d5b2c1a19759ac8f29202c292e731fd607b1bc224feec37a7924070907e906a985648354837491a454bd4c01a961fdfd6ddbb6d0336bdb3ec8afcdfd5c6373258bc5fa22ac977f84c57acf2907e33cdd3cb1bef5c47a9b27d6d73cb1b0eaef53cd62a6b70a16ddf05bd7f379f9739dc75fbe6b888be33a67e6d7d47cb7be2deb6b7d4a6b5d92595f5bb45573536badb5d61b14abcecea8473deb67b579940d6e7dfdc23f8202922433d71a246d4c4092595f83dc842db35ed69aef54ad6155160bc59a125bf22121118355fa141b9139647d0598d44f000b537c1df49a9b9b9b9b5a93ead7bb699b36578ffaeabc2c6a436b28adb53e576d6a58b5dae0e0d4e0fe61b1587f63f3310542fd16ced7af6ddedab46c5a15e76f5a3838363895561a83162d20a20a2e6a1856ac68a2c51058cc9081a9eac449a56a77d3b2a961d1ba62a1501595690d65d5505cc2eaedcf1408abb741ab5a6b7d9ceea66553539f55f1eae9f6aa2f82b15f6864a35de538ae260dc9ba7d5e8d470de1a5309f8594e5f7e7b9d54e79dbb62379db5a6f6f53f7f820872e254ba94aba31cfb735d8e6b72ffc2328204932eb56f375b6f94c40926fcdf783bcc6842ddf9f9f6b88f75374feb3db7ddfeefbbd1f5be2bdf7d6fb6c4c0d793856659b3fb11199ef08f76bd9d6d79243ce23bff509391825c871158fe2a01bb991ac3572b8210a34fb7050f2e895eaedb77d9cf9701ff9ab2ab7e7e1c341f9af26777b201f46f2a75e961bc93133300eca8d08b4e27a918cd5bc7c1fe33e377ff3f2adedd4d6dbb77ee270fb9ab7f920c9fa6ea44d8dcd76f32d9b9bb73735377896dab6165c07258da51bba9153711f494612155378f0c18525c4c0361558b9a2090d3790e00b2e60a90df571168dea0dbd97af6c34371c1ae9d94c01b9f3adb5d6dac9da5898023dcb1397a0faee670a04d577dfa9ad673f5c48deb4b0b4c13ddb9e85c397eae74cfbd417ba94308201e58f1096281efe1361f2cbb892ca7dc28ca51cba51962f3484f3dbe6d5f8b672fd430f29378e2593898de328711bc852450e58be94c9e20b9315c0c052855316573851a14b1a6e051c2b30d1650c8e0924a66c3f1c3f71349978820a8e9701c443e58e5039ec31ee454ccdcd444696dc31b1a0082bc83892e286306e2a3713193284c04c1a6e298c29cc381d516fca8b2e62e88890a1bc30c113ce8a91f5e2896ae5a9f3422986cd082a4658e16e112f684328552f46408790499eea4514198a58f2a289111168bc883529e2a88b26f105f4e20144c44cf274eda2298a114e669a5e3c807c7c0b4a55a2626e2b37139318459069ba282ab4242eea892992c8615d820908ce072699970e38c1445481dacc10994b14b6b5a6eeee7ea5c45eeef92ddde9a4de6d37957bd7011b5d6a4026a5da46615b95dac3bcaece711daa6b77d776b35bdd54fd46d8d0dd638cee2ee79c94d2f8797b7b03bd248fbbcb962db7f879dea2478f9eafe4e997ae9131e79c336eae7577cf996396438a1c33a7af38e7cc21c59c735e22daa7cb9f94d2eea6ddada1b6ca7576ebee4aa5942d4b29a572dacad52dc82c492997a4944b332c492997a4944b50ccb024a55c220305d78292c6551466ea4835424299ba45a088536fa9a68e0e09cdec741f118238c83d2abf6a6424958b4f3aae52e122941f3515e43ebef3aa952c3b1428dff5a6d43aab731bf0e6bd95c5fa59a75c19f5835145184a78666a9e80f07218a930f1813886284d8e4bb8512c16e3c06c053d38187f32b38a32e601d9244b598e2f8f2415568c4c666439dac8a61c5f3ac931f28b1ce553a3f1d1c40bce787186480c0ba207883e8082a62d4e1cb10516c6a45806a80320404a42aa41082b786608c2093324b862690c2cf4236f92465942914b72966d6cbe77c83636367fc77022db7c4d0a888d1763b15cf3ac14909ac94222b3fea680b0769c608918ecde5fa580dc5b23af5610435ebd2a056455555f3b9502a2ba29314690538f4a0149a13ea258513851b9dc7dec9f1e366c39528ac13a96971cd6c89c96cc717fbb64eec9062615b0f0379690e5ba854145de5e4b01d91a8a186259a3df60789129ad1b510c0c199a6c13afd38c31fefcf833fe8d7902d1f24c89c024a4399e51ebec6003428cb1458c1061a43982e6072bba48628b0e4f2d3cb1840bab1e1cee84c54c26fb4f5a99ac004412134f889278c2836bb1610c1345b327623e9822683aa3868cfb4513def3c41753f2cca2847d7971a656e9265ae7faf46f51f768a64c5f470db752ede9d3d701c3ad5f44d2f7ba875144924f299aa34c29a5342ab956df59f5a8a8d43df4555adc8b591f956ecb41bae3044bf8534ffe44a5e6a14fb19745895cb1bcd1e46e4ddcaf133d23c61cae0e2c1715a3c7182b875df563ae381645295d52d618638c31c618638c2fabc718a3bb7b8d317efd2e9aebe27e622dd238a3b7525432010739dcf0d5c4158afffd4528b148f2107d6d1b22b88c3ee84713077d621cfdbf21eee730ca5c69c3b2279018935b49a9956690fca9d1b9b7bc900595cb7451f48f1e65deeed35b8e54c8ce23fb77d1dcb0d656c6c29ee0b1dcdfbd6502ab3fc4d5fa1b4e22bf26a9cf71cf64eb9eebafb3679e2b377c6525dccbeffe1544fe44d8b7f6f55ded1aa2b2c3aec643dad7af0db312f5aa40f2677bf94338ee25275f09ea6129cfc1eee5f792a77b30ee17913fafe6e99e85c37a7158ad4c75d2d58f42ccaed40f6dcfbdea63ae9fab5f491594922a2a3c34bfbe92edb9979fabb12b8587b8dfb0abff66180a0fd59faf646257f74092a7fb86d55761b92efbdb37cc65b71fc2fd565fc97cee875425f26bf8ca5d874b88b1285a145d68a91cb8aadc4c5d247581d44551b76961668609c549d3220c1459801a9ca81652b8e04ef3554b30437c01bd7800d950648391f3740eebd5e2850240310aa18bd88b07900f67e2a2c9ce300bd222b589220aceccbd2141b744133f98e076b9999a90c24213b210b898e1e4665a0a83898b224a2995ef78767b9d53a33495dbb420242f1272fe56efbe07d6d96d924124eb8bb09aef6b9ef939738304648d4a8baa936ada5bb7399a877e4b00f4fa1c60a3328baa391a602bed1e4a27b5aab717d5748976a133ad8cf685ffe47cbf664643133f7041f750fdcdb7d6a18694fbb532dac736e8bf6a9be4f44ddeec2ee57e2dc9bfabd793b54674d32099c9fdf40bffe9b2c8dd75a163ca50a7ebb69f82f11fab2479505fcc5649fe5029ce43c5a6a88c0985a3ed54b8bbc766d4c7e9203d92b2a7d668018d81159bf311b1f0309ccfc9399fd61ea0beb340ee3edab712d3ee417dc35019c8ddc7c7f9627b807a5487fadca8aefbee49fe58db3dd9eec976360c681efbadef8a6cdf1a59236be4df646e885330f121d7e7d13db661ed41b6426abe9de6b1cffa2c9585ddac9be5b622e4fa91ca507fb16c85c3abc261cd3b2380b6fb9ed926e1be59f1dc706f50821bdf1d49fe502aced3257e4073e81d5996b0286511745db25399171995514a3d4f3bd36183ab8a1eac78200726ba50c14b0725b6b0e20b134d4e8841868ad1c5479f98620917829891c8e9d38c1245e0061bbe20a2881f7430ba00194178e940161e7c80f274457d22738376712fae0a80903331926ac4d8e2cafeb404a7a4ddc04969c92cc450a18a01c3942a2c5144457b222245b988e1843221c613b901ae8a90c6071c227712e270682a48b3430e7b960408aa0d977b52af6892ed935b7333a521f252c60baac752948bd62ae4b0d144a208a5c39814635344c34404b22c8ad0c2820e52b840258b165c999d49f20116c6a3ec1129c63096894f536c7203618ee02226538a4e61c49cc8e1cb89c3610b039aa082055ca6884208a3d88216379841c511175b8ce8448046bbe169082964451c2105091e6ea002083090b8dc40061874054c4c2891a632010227de27d0c20c5416296437452ac40d7ba9676514f909ca78a10c18b27f7542e56672419a23a4dc9f54c3ef3238a44fe96b956a5a8d54a47c52a3c42671de9e4040794e1c6a9a9cb9b539254827add5f3a4e3ea4cb425d1390ed8963f6ee54914295424981ae68ef1be783361a7e1b85706aa1ba594524aa9369f0a0a0985c325b32560ac46a7c66974528dce8f60d688e449378dce89238c09b7c45db3217647799227aa67404d5e91084cf366bc2fee983060bc25a50a74a3ece608f4f42a139572777c5a6214a944a42533af23952560b2b41b10d0eb0582ff4a4d2c0e9798e9be20de9575974cad282929292d255122597e8c4e9da39cde5b52c5df99b88652af7a550abb866c15ee6de8e5d85cc07aa31ae540872be35eaac45cff20f2a7b3400ad3a5883288d83206163f486e00c30da35134721fe772ecde360ad40146a389644346b4752077266229b5c08b15309900b999d074c9f1a312535c9a92cc557deaa510910afbf7c3052228fb1d914136118486462812012982d00649378c46d4e80995f056aff588ed8b455f247230d614cb42fd94a3947224a913397eb4d23d3a17b9cbef8e1f8d96bc5801a68b113258c39c5458d8de09a92ccb9f645c32d7a9cc03ed080d0e39ac76039a984b7333a1b1c14b0d6dea992ec2c4ce1ce14298d9193145561c9d9922862bd09c61ba22e90c132c5c114b01b5846c89871cb612154b34dcb0d402134eb8208514b895b56f7bdd318c7e828eee1248d720a3774b29bd728063c519664a29a594b24894944ac9c21299195d4a94e7fe58ddea9f6d7b11119fda7a21020fcc004408b040220cc194278638aaa1052c3868e1090ace00a20b3136d0c4951d9ce4a0458b0b460073bf7ea596ecb315b2ff0f77f7e8c50ed31916bc3a59378acb534a2aa594b27bff20a746504e700d532387d52add3fbcc9347e3735a4e41e3561f4b340cd73e2896bf7c40ec3343051768bc4289efdfb696b27575208fceaa8886c41966264c9c50cb7be9350c441a92273519ffa224e4e4072ffbc0cea1ef34749e986933ba34ba99a62df53431c66bffba28c84ee2932ca1388fd64d982170bb4b8c88f2041db0fd68783fddb536ddb288e76a3db06e460fbd02007a5b669da576d52ba6d35d28ab5df34cd37ac69af69400ef68fbf7657feb5a6d1ed67d74d73dc92a797b85717ec266595a0a50d6a5aa5786a409b5993f1b92fc2ec8733c8f43940c00366ec1eca692bcdbb1b3448db0321597e7c7dcd43bf9ffbe8dc2001593a963f12164b68250dc9c91567de8e7b8ddf0912f4038acbe5663a7304d3b5b999ce0031353a7d263444648433b29ccacd74c603397c398160039f1c63c78e03c85187ecfe1112dc473efd58eff5bc078db8fc99648f0f829e279fc62ca38c3246ea30b90009a3f8e55f12b73aa84194bb9a7de9ee6e29a594ddaedc34bc9c8c93db06f5614dea43964dcdb33e4cdd0f55ab0f5759f5e1b5c18f627d1fee1effd587a37b00d03dfea82f47f7711fcf9741f7f86f9f06ddd33d9f00768040fee517d59d496c290a12d460c54bec03b01838808505506c61461064d823a6f8c18a253198a88075961aaa90a14b129414b4a0d16b227b162f6160c0a1a18f3c6171762be4e92c60ca527eac5cce64ffe95f4c0da5a1e8779cc532566f7e90e49195a41817749f9b6f3b0f2b20febd83b443237e293febf81ded63fc904796467a96a3747f726760bc6ddbb66d6bd9bbdd223a3f85060d1dbc44e775deeae844c0f39aeebd37f7400ad344cb182b8cd902968220ac08430b2e538821034bdd269f7b737efb7a732a12368e83c59db50d8bb5dddf6cbe5320449b8bab8335373aadd68daaf5418e48f79b75713eac99157144aadf5db36d3f5340b66865db3614620602d43dfeaf577deb6fbea686a5aab997c562b1582c16ebb654ad0ff2db7ab96d97f5f170d0b7564b67de0cdddc74531011743e148e605142b2622509294b061da94c10a411869e3820dcb6dd3c4b4a330e8ec0aab9deb5b94897f543ac6f049601888c10ab9e0d520d928c49a227332c2947eed322ca79c9fac2cfac9a5b736b588ff31787ac6f7d4c81c0fa9b9c677d9de70deb3b75c3ca799c9b9c9c560e1212eb5a61255dec481e4342b282d405098bfff418425c21c3095478228a3130af4286226008428b23601e46caf271be865583657d13a200a874ad3765b174886a060000001315002020100c07040261402c188d144afc14800d759c3c745e341709a320c6619441c6204308008010200044666868c6012a9973402ef8003624399e6e793a3a96eb8f018f6db63e43ba85ad79abb41aee0d27a280265c31b8695aa0ff4ef4572ec0b07f6c2698a16d812663327f6ff805202b0425804d189912f08b2759cc9c17b19b1ff19c96e413b94d8961bddadf550c0bc50099d4120859454964c3296f470f43733b372f69a11d1b4729f3189c925ba87a88e50e0793d9afa07a5943c89622b309ed2f6fcaa26bd8bbb5972b9661e49f07b6a1143422c1a65ab6dbf5c3800b3636512e67bbfa6f3775ad8f313765e442abbdb61837f024f27759c4af6ce318e63be0ee7342e2420fa06649e332246ec4ad961bdb953353d2c067341d7ba9972af0bae62c0b5a3a7b0588c42c0c8acfd2a3249c3a1dfbe09b40ea6703bc75c8b6fe0182a32fc62c7bffbcc721e43f2a00d985ae804befafacd6d3e0ecc33cae3999f0d36cd757636cb43ada3d392bf1dac50f4a61999802e13a78e08b858911719c809edfcc4b12eddc08c3252e9f24c707397c2f048861ca6a13e5e498658d5bf2172482c45c308da16b103dcaaa4631ed95a2c5eb3cb68bdeb4b7abf7e0b1c8c0ae95d216560afd9926dbeab2eabcee22ad27faf54e25d692cc6bfb252df44ba171d5abb9207b01269ab72a045d26339a8275a8722a7ef346fcc72d408341c0d60864acf886007ec0472add5c98f0df8737328cb6ea42051867d69651339b6c3a54cd02402a6ce31af55c108cc339bc2c9ffe7d1740749467d4912839a8ac3b3089828a9b8e627fc91538e92c8ede801cb8f472c75a7f93f7d3a88160c31f7e015c567b44615e37b29b01a966aa4363d7abd8e4bb52f46c0b9975070f98e75d80f67542e59723d5db3083748ae01b4974d2d9dd36204dae6bb861b6562a3c4d72f90ae2d20be459fe81e605c01f10a5c5f2f63e2a2fd0189a5ac59e54e3c87965e609a8d25693523a8274e6c749c02513de88ace66d6db6712ae4dc8ceaaa5d6e4022dd0f19bb005616db2f235f378f5c564e7b5eed114025f501b9a8edb37a6fd67b9dd539652a68ff8f775dffe9db4d4f3e3e90abe247ca56128b02424333105d797a9625c1312ae943d1563326a566b6a9809d6435258115ed7afd7e0560e178266c94edcb9eb250e8c580bff8fb133e2573e0b41452896aa016d67cdb989908578f18415f14eca787ae2e19070db89a415cbc87b16c98f48aad38cd743890889f8f8f819bf4a05fbaf850c9cdf736602a312e72bffc935c7e339e6b01beabe13d08b85f5cf0a45bfa1f27ed5110e4c4a6ea608e8fbe44f647a88b1efafc5f7f34c879ea8804a0feecbf05578c612ea7ae612ebfb97f21586b482d304a2045abf65d6260b03cd0e6abe810107232db811b1b9dbf218d57be48724af21746074881365efee65d972ad6e0fd1e11e21fb2151b3e5d8de87da899b2962a3021fd52e10d52b11c0aafba7d299c867262da47132b07ef611c7bdd056492e3b68c9c71181a215496fbaa38520a7aa80ca244084d490c26f4f20fcbed0348c6b474d7d88f511b2de24c28e3294223802342ecafff7673957a60f2dd552054fa80097cbb3c9b137411d6cb71d4d46a1b0111edbaab9760c1b5ab3704ee6995109878dd5abf188a0e04d977cc3c120466ac1347d4a9a11c685e584c9f3a5c17b493d3967415869d6f79d02267075194dc9b5cc582a4823bd06128f1d451032859bd5a9119e063082b09c7b2c4d8f82840e6a34b0c08de7446efbba9e4e3b504dd072aa4195120aa5f03854ad68530dc03492aef0293344b016b4c3cc4403cc1cfb034e2b87382d4beaa9c0fac21e9f151ff036573a7d93d050293277436e795a925cacb76a90998cb852451f40e9397904620ff43c5032db1b5cccb86e9ac09342df68397631368b9aa09e07bb464fb9cc4ecaaa4077018d35591b6142061232d21a899aaaacc915c2244538152a0078cf539d5f345c6962aac5205066d900b81403b113dffbc08860f529841ddd779fde756002cac5853c22799c66f6a221efc61bb7351f4a2939320a8932c1de87b24f470681231613bc656526685ffe9c065be26db5193181deeb3944d77321d5efe17a5a3a6cb902eb79c044fcba0255e1de14bb3004e440975902353d66ee96923390785e75b05c80d4d7960dbc96012aa3005f048b0431d8b856610b9e044c089a485345a66372a4391e050aa5cb12eb8631618cdcd66a1c69d0d4a9ba1b1ed47868a102e81ba6790056ad8edb9aab5d4629de44f4e60b3d94d203577982dc18fa311d9c914d6b6fd5ae69e7d1e14ab7ef4d1e4dd9eb4f5b67a4c5c61771d77002f32e3ac450da2cd6f6eb428bbf84b668d99bdcce4183843b8e611bae67c15ffc32ab25016a03eadcbf4385b1ad071a55f17678528ca64aa9a2aa4422d9be672e7e5b0151b648dfa74a0f83f07fb69fe63d31c8e77d3f8736735858949f3fd34ce36817b476ccc7cf57456ec8735975a78413d489414a6464ddaffb2dbbb0c1133621de936fad22637c42e1f64e381a2e9c1c8443c29f1c2b213b95a722b72b38cf46c087d59701fdad23c3e6b2087c625f62cffe62f3f7354a50d347d919cf506c5329e2e19559840baacb654d8fa831765dae33879819f6cb4aaa8df5742e3c6925034d2fdb5047282a9676accde57146fe4d0c4ca6e780129e05577638ff32934a35237b2f75e1081c1d6e10af63ae31fb3d63841b7c632002337e282bf9d73e62aca7d253692447eca13b8a0c0c634475ce6fa0be150fe5e414113bb6fd9d930f99241db724dae9380bc14535af67b250a003058c507b9ae6548691e98d79cb86a3014e1ad9069b335739458511e6337a7e671c514754ef5ece8bbc22d2b82047aa1578ec08657a0ad1ecfd64870e2cb77d09deaaa45e729a91497b9b71f41c834315c9e88acd6123f3447331825c2c3929d0d497caa74f72605be6d68f933ac6037e520591bc945cd1733bc23cb229be0ab997156c32c4d165b1b72513207d9bb1969050d5050291b52ee83a54bacb32595746dec0c389ba14f32ff2531f8a93bb892a1e9f9837c689b753040212420d1bd379969a0d9a2b2d0efc84d1ae80d9648d88b726014ebff35eb8cf6c4636ac13f39cb1dc0c5619fb3441da032a00d969939005d00f0fa0fca779999f4c32ea5a612dcc40586b0ffd383e7a685becad59198ab60f6817e0c9fb5f7d08265cd2071bac7bba70ec3cf13c1b0c47e7a72514f017154835824aaaec0c21ba19ca1f215f4083624835af7ea0e1266bbadfbd807d9a307f1d288a3566c3370db0144e2aaefec090c81ebbe0ee973f29134db58f18846e5b8da919ea629e9fb1ad8a417cec460c28321bbee00b08120a8f4b67de7f5ee4f0b482e06ed45727a3342330351f9fd58758d2a47ff8f323f2f1e68c1a218768e109d15dc9ac354735a4cb73b205453c097059d945c00742ad1e4fa556614bb852a486c5919b404137302033af8a593bace23aecb8d71531583cd8b06aefa0270b441d32506de30b587bf0ce1ba315bed903941fe0b13fa36d7a24d07022f902e3122eb25f2d82a667989d495133bc546997b983c27bc2ae51334b1eeb53e86b3ff974ff2c7c1cde502066a1acf39792084e3eb0fd73a9b6b8e17a3b0833331fb5a17255cd435a1dde6abcb8a40207c2e72134b27b38c660a58d565573a4fc46d30defd3cb3d5a6262d0311f672c266403487f867d116db863ed72c24ec2308d19e5c02abd7468a599611c240c4a68ca5e5baedf2da3e4c047ffe323afcf472519a886cf453f1ef83e873c89ba3dbb1547b98e43fbc3a33d6bcfa811c06f22c29d75020bdaa53f05d9ec2baaae97795cfdda6d9f88b6a9da0d6cff2e360251cd943a60309c18ab57e0a34c71e705005621f69c91169df5fe4ab79f3644fff85720e9f086c9df9b06490fd50bedc5ef8834c69dbd087b1c26d831fce4ac28bcd463e57035a8ac0dca0c8a6c558f940d1384982f51aaf206a2358c1e1c6bab3620b0ae5b8310f118fd58d60b83561be7d858fd784e7b070b4106e543a7885d75da4871466f84bdfcdeaace0c939fc2ee45a8a903bc3b04892293b61fba6f3ad2960b16c8e819c5aab957903046cf335da09d5c998e79b4a9967fe0e00940452f587fd127041afa38eb92e86cecc1127e1bd9d81c8075a5c1edc349b7d1e7d0d0abefc6876ec93902ee822cae30e85998404d1d4e1c53d5f47f7ea08055be279d1e2d02275597b97a96cf8440d243ae8fd6028533b18ec70b9be2d5cd631e266d0824982b900b26e2a62ed8f8583033dd1ad2b35fe86730fd33c1c05acc3299d401b13c01be7ef042608ddca4fdd8828777488af673e21262cdbb12a7d2a0d14de108ca1fa738919bbec42622cb7033e73cb95bfbaf5dec2abd35088761675f04b7844d50aad7339bb1dd8de126b3123ab7365dc4467d80563a1c3092452091da5ee55a45338d5e095198b01b8b7ace6f9857fe9f3d2a437d680497b15e2c31af087f061b4a5d08846c452ed6f5cefb1d31ecb9f3e74b81f6a701c37e1245074ec6108009bdab63417ad1e857d979a6bd164b52d9942d16b104eb6d34566380bc34245b661742f22a07a09de6373b2a19988b1e870f57f37e15750c0bc34ec14114782236e4c5eeb4fac0f4a0553af72e00a839bd14e2d0b7f294071ef5f493261014484791571a443ce14a815e91f006e0cacee1b6e1968b03510479baf120b73c5a75312023d672628af14cb09bcf1bc70d04ea09e3b7ab22482c7f479915c434d616898ca04e62fdefa3051bee426155d4626ae680aed24c401f0930c88028075239aa2e9b058b9955a2ed6f8cfcb0dbeaeeb01ffb164db3dc4532887e6a74d2984535718594f305c916c65d9acc33b95953d1ded52c4fc485b96711e575559ef43d124de4b03b7ce9ffb637ebb5fbcfbeee715a2f14e8e887728ec4db95225b0d29dc59881eccb78b8eb82861114eedd478d8d9d5f2a989420aa48ea2ca46f73b6476928fdcec2a63c350dfd3e31750754f0ddaae5b164b1ff29b5e1d15f09cd58197959724d85c176967d98fa8a14ba37de052f7f095b8342c7eef18cc85bd5c42a6258f226811ce6177b5a7d6705a8f2fe18088e441f4cae4f810b8960d1abd0dacc9e36d75e2e2c3c26564f79137f38911cd388c9e4010e5064c683fa374bbc79d2b24b213155a32b33ba047fb4f395d89c5d79073db28cb9b5a424c15552facc98d493cc070093a3993e784db5633e19354deeac3ed5f68e3eca23caa221915cddceb0880e12f4a08521e95422e9f3e2fd760e4868618bedfcd94e6853c595ed0d8875299282a7427c714f1c6668654b16548380fa6aa7c52335dd7cf3b9ab58d70afddf1ad0f48ea8c801bfe268e6b2c4131704b90543f283adb59134f40ee3e07d87929045473422b51389b8c63a81e2a074d967ce4315b35bd8c495c85180837e9c6ad0148cc51c95683b1f44af1912998dfb4f7fde5c7e2cda9c86e2462bc029933404ca83fb885be5e3b1c37ed46197d945120835b2e78b6ac3256e4a43fd67d901c3e6173e1f142479c0cca74ace2abdff4ca791b96e73c14fd25c63b3bcad7672b43405fc308d1e94caacd3712d9272015c478f5fa576043131ee9a6f9c5b0a65e7c8e1cf625ee32da2ab623a5d49591900958e952762beab54dd0c19464b48b4a90581c8a158c7dd65460389fac268a47617ee6a1f974daab7d73ff51d5aee8ac8767206923bed934f410724d643dc80e3635cf7aaa3832c4be66b9f3c5a5d77411d0b98466e2dd71ec099552a0e7ddb85dfbd99e40273e272fac63d793803439d0284067cecfaa324c991a45063a1c96521abced68934ac360155a7b8740848a6ec2b7158d3d7fc031e133d226c38844dfff42f615718ddf329d38e51b23659ec5a97d19177cbcafd1a681a96633064aa5c350a581c41ecbf55317e4c825552c6142157b2e2c2912569cf5735975d656b4c149ab39d1e580e2b8a3eb478d46fc6be527084726fba2c5edf2575b57ad00aba7eeacde5fda77a81184e907c6796755b6c9243efe90cf9e5db71c7505d97aec829840867d6625db1b314c260e7a19da807c81efc640c3b417512935c47a962915aebfa31342331b064eb706c5d58c3a1a1a98e2bdaea456d19c869e013fc68a209572bac42465c138b327a94cd221baa886a0455d90df121875ddb444b4caffc5e651bca6be2f983d7beda53fc46d719bf4883393b73751150174782e9d4cbaf00eacfe61052e5b38c7426ca4c5c513b59e5e2a6d0e66d1e9bebef42df92696b3ef8fc79e8771987a2c1d496708b3c5bdd01bd83d9886f274e10e8434d0ee796868d676d5cdea715c64594043fcd4b50a49a1c8c3e860bcb887d325857200b53ffc2d5a80bbe9205a6b83890e62a0ac540e4a5a8b35f7c74b8cdbaec851407861d3711cad4427f5328ac356aa5f8b806ffad440b92df5e3c505985c256dc2034039bc840cba83b78f5076afc7478593195a471920c0d238e6d5c3d3412ba1846e7cea1b0eb3506ceba468944ca866966550691e2fb4869bda3d7df7ea4546a6a0f028da13ebddd1615392c685d21088d6a752dab31f2b99e63d8123dfa01bd57978246d229a971f703289543bd4551a8cc326a6fd9d46f409641e76e658c1b063aeb0522038d9a3fb64ce89cc9cef1dfc3a81fd8b04e200ebf4924dc0357a6fa6cf66ff0d1860a7bd7987c1edc775d8aba9c9af02830a7a03f3908d7faa85c4eca3ec434f4dc661b02f04941b1133885aea2d70828fc64e7069a96cc4ba5d85ade11aa5aab94044171c9ad06904cc5f0a5ca1fb7347164d6e93cda91c0bfd0216b5627bc4a6854ce402476e59d25a64062e17f296dc7bf6833a0572d2c10b77adaf54d467f56c943f9f3b8b7b59d5aa6f4aecaffd1d005e2401b66d34fa6127b5d99dd9510cb08fdb9287db0cdfd460736f884276030a20028961168319cd2073bf89216409981ec38f205050b0e9d03414bb801e5d9b927f95d8f9628b7696410a731742d49f65b762e650b103fb279e298631e80b2dce1d6ce1240915104e6932f9ea5f3224ae503807984b8e87ae05ef90062227a83a687b69b14bacc9a9445d7744c2c41b803e0b9d259f78eba1017b217bdf422810b949a0b977988893445d6039a5b3d7a2f38cf5fbdc792f10db545356d54c84a6d4db816a2c8fee631e1769745e385202c25230285227ed69dcdedf0ad8e12a59880cf933d80c0c5f710bf8297424fe582198b946145dcabc81daab943de811103e168309cd1d1dd459e910b74d1530623f5e5fb441e34ea21ac519b5c97353f93ebe6ce685983bc7a0bc345b5d02e7d84ec72e82d883e1f75086928f8b54a521b502288fd7e8ddb3fc4969a5c6a8f300524a52d34973ac06668e08f7ad1c4f1d196f63d3a24e4b83b1cd8c4d01783fe7b3022b43c9a68a5e4c850868511437e2823c7f78642a991b70b8abd97d709547a63efef8d153538c4dc24d0318c9dde9f332d42698df8540bfbd12e151b2f4424df74723dd62ce2e04590e72842d264517a03645efae523114f294e05b81a50fac76ba093e8e8b961db7bec03ded2937d3a4747ee36e4bb8e50efc6b4d7794c33503a969e05eb0845aa17f541f747cf3a9887fc5cb43f38213ba85bfbd2b189bc45736de0ec9fef66fc3b0bd8b21da02f919807c223c8955ea80e90796d2515020789661cc3f760e46da50b014fb6ef1532939c85d1de75e722587ffebb6d869ce57b52dffd16ce338677179f41ec80053744dcc91872bf39481b0d7b7ad9d9f61c61ac645423a51b9106ec2b48a7edf1cda8c5b81df8d4e5ff16914a3f8c45f2b0bab9960469a8b3bb5e0f566adf7e6c14c629d03c12540abdee7a9a2d1fe4dae95af84e34ae56ed57d7e08dc1cc1713e677a5b5ff38d6d3c9e49908768b85169ed72e9911447bfd7691e3fac129337c067dced96aba03660dba16a35caaee87958d41599052d6031520746bbb7022491465037282ac51d361ddeed55f6450e49af19e557bfaaecede3ded1505a68564e365c5279ee705eac930a6ceb275d9bb99f416b052bc809733913b6ba6e750cbbd4d71715f7e910d78e2ac7a3d0bfe78d0b7e184a0ec3a9dd43adc77fb6546cc6a4f7bc958c298add5de1c374253023ec5387cacc60a2ab2316d0e12e305d1f68710662d59ca03ec82a5df08308b1e73f49795b60ea433da425749218a55ad8a5e8836000dfb533e5e4bdad5cc86d5f01687fcc656a8ade3cd2e92e2f3112877716479a2d8bde22e5e2dd9676ddd8039f850958072147a3787272ca405d052bf7b8943fded7f6b2309dcc36f0e2494184384e49103e536c8e76c9b96c7cedf6f81d08eb589f6a71d2ca14823f9bb51044aebe507019932037cde204f63df5c50229b0dcef72b36e3f54d279e0c5044b7b830ba275d246fb788f51e321ac2f6910f7f9c2486482b2ee1d009ade9b78b7ef7057d991b7baca47c423d6c0bca70ddc8a26cd18e25d961c6def51a073d11c060c0989321136bffe9ae1b5b1bebf09b9c771d1f6145f872d088287cf47ee78b77c86b59a69ca1984dd7ccc7df855a1fff33d4660525419929d3a59fef52bfc9c173f574e07219afd4603cbc29d7958658dbb4d282823b2aa76e960053da1a8eb2d678c3883e8b60ec18ef7c51b9f630c6b8d3795a98b0d623a1d0acaa4803b3d80cd2c586741c6dfb035b10381aec17bb79597d5fa3895d542c2e5a47099a944e5535d8305a317e8fceb0b8b32df8326d2d9495c365ae480e54388ff5bd5af52a6531b8b52d71062f081afafcf3b2c7d5e40bd497d4a4f05634df232beba30616e8deb843054f88676afb7c62b71e8c27b7c71ef649bb34eaa3cb5a21ffa96811146f25e1ea5b0a3107457ba883aaf6eea07e65dd09bd8cb81d5fb2c743dc01c066c6af22921dcddce93bd4e51045c382e961044b2efbaaf6b4680e3efd758d41740c874520a998199ba0744cfcdda2651eb8315f672ce092437bc2767e2f1596ffb12c9a4788865f14c6290ea068db9195da8ebc8b16ab3ad4dcbc0d9a38b31b1fa268b1cd622302c9ef35ac62e703197efee49f4836ce12664e80bea28517a654d2310c4aed59a44a3fb13989cb834448fcaf6400f04dc367455b76b82e7d1734896e57ff5668e2afb0affdd633443457c24f171fe1b37d9768c52bda8e17919f25180206b159f5bfedd0f7b026bdd15e86a880d3328d3fe0b53c29ee1aee38bf2f4292a180267cef8fb26ffebe2b378423d216e659af003c150d2646f352f3bfa40880aa4f912d4d7774349d4b34a1c4f6b9ff4879c2b77ffe8ea814b045d71ad78392bb22921dec14a20a2445e2db0097e0c31284ca8ad9a4a53296ba0a175c96f86118f6ea2b75efcb1021324422990d6f60d13324be97c5226ba61ff839c2a14f1180107d54afdc0534394c79f9298c012e140840dccf87d5a09b9c58fcab8eebb1c8812338333878c3cbe4cb3c7fe324d67309c8fde32c6d4dec33e1a1a7e90da24e131bf1383d05a5ac2d4c5cf8551a039e487180efbfe55cc2439f703863da81f5f6d172b4631dc4c568ae1f4d542a2f6cf5cfd305764d64d03918c488e31c3c4499d695be73541369138d5a7f8b1740a47a8a38b9f7bc3d43e01013db043524223b0deb75174d92d41e506944e0b4cd5d079383d9542c0cdf865fec0b37efa75bb30acf8186316e4beeef78adada2dc5c984f226f21f3efb7a5e4fecbde44be6793ffb9ffd85d6fef142cf4ddef4d1229ae4b22107ddbe52167bb06b1cff4076e3fb8b34180c74fda4f08f428c430a57c8522d3eb99e2ef8d20f1e30d05bc23cee31645e5c12a38ad5fa65f80dd2420003b68d97195cdb8761678716499213ddc73cdecf66e7ff48cf492c4f6c622a4ce16c31e2cef827acf8342b0732cecf55855fccb448661984e82f2a46ef5b8f0c3a9ece98ddfe87f32f788651c20ba3c7ebbdaa5d0d3af278ff9b6fda789e0f5bce721abcfa04a9c98c20ec30d4d1f690d4dfda99724ea3ee2a5d17e1a757b02fbc4e597fa509ef6dede3bfe6120a72b608f91d37a40b46733605d907e16fc2b8b918012c5a98cd68cab8fb25121f016caaae69544dfcbc0aecf175d9b6e5dc85d9302d5d67e4e513608c3606c0abf481daea76a80156060f84354d56da00b1a0430199f25b9bbb1c8090aaf00e5ba0441503c0a772b735cf4d83b5b06c7df38dc3b835b85b8f44fc2e386191bfe9cd71e52790af90c50048b9dba9ce9986a1de110a6540d8215abc0c1c3b9e152d43b8b6ae6216ef66090da7d21fecee386ff728bec2828cb26ad6cac053403f53a218fa3c057c5162416458484062f27ab1814d753d377c3ab39018957fc5768c6cb160145186f696dc1d94eb5637be75d860820ac6da40d2b3073ffb800489bf4dd009f13e04e47b2f9bb618e6ea5b351efc4cc3ea0dc58624a4444c5caa5d33f79cb4c88a83a0836a8e8a17bef1698af49f39b88f324ecc8405160df729738964c708f055a8342cd7bab8365e281db18f9668c16c10f9d0c5e8eba22bddc537246a9384d297753aa60edb58488240497faa23295957cbd210212f710ef159ec819c1394c0b325fcebca13b01252bb52c803db7d670426c2197c5498ec377ccd732c95b6363788b25ac93ec7a3e47d09ac893f95cec1e5d949dd57190680f52b0f5eb0c2fb678e5cc848fdbd2e87187d81aa90d74394ebae76f24a9fe446d437f96ba6cba187a6ce3f3f0e28849ac872ba34832b8038d341d7a573e4989075f73d04171ea7e8add0fed1cc1644594b2d83c188d8e448d8eb4954a834b75cb6005fa4773c4a8d9b700702deaae3175296462b5cc0f58ede96cd670c687930c2bc43d56ba5df0c359f576e03fb84a78a489f0894afe62397f39dfd995825ccd0688055ca14bbe817409dabb83aff1ffb63925332e4847f248e2a67851c565fdbe6aad82fa3421bb6b92386a8cb018a06fd1043542d50105376e715f32fb2196c221180366ca1073e8313cacf243b46ed8e5e74527a1e50f332463ba1d82dc67c223a684e1033b218d790082dfd97e24252b8b106dcc3cfdb3bbdf3271e50eaf7f2bc5bd3bd40831607f729eafd37cace9e70deeebc2e4d812f42403f3084ad874619de09c814fffaff6e5047d2c15432252b8acdfc6d1c5236839c864d0643230d3085d23821d069096ba22ca82ee5dac05bb7065b2049a0f8f05075669f3d50877b0450f56506836874e9f7703ba889a1071706307288ad923f2c7130b0c57b53270447ef98517d8062fac89f2629651b21ebbb4f087f04c76e8aa59cb1610e94984e96b2766af1b4b4eee94239ada163dbcd146b550cc57907900967150b86da9ea8b7d04808a49e70a788c3ab94c5c60ad8883ac2e5607d40f28a4470da07b63951440b0625ee3a9ce156001855670bcbc5caccdbc70dc53df1ac10c629de708a61705b0505a41381a80dac458ac6166ab35fec612ca4ed5a90c024058fe0f9b76f9849ff1b6a6d1de21dde03900f6a196d9771b9ffe11a1f49370576d60863ab97b1bdd8e3cc9771fd0f550726ddfc5704df19d503b616710b72e2c8b8be68f5302e563a506b6543c1b27043be44f196557feb6be5e40553052399b45ff17b54d5bf80c30e4b5378af700538300a4f471f8a1ff1a61a4e3341fc848bb4f7e35ff835a30643ec10e8a10f9608563a07f054a4c7d0a0b00bf69e18d6f8bf55619449747fe71934ee834f33cf86467ba6ec8f1fa114325f358077185809e959d13334981cf61e64fa70225ebe6f8fc0c6e4b9530be2ac388abccda910db5fcef959399f17b4d14dc6f98fa36567b9024be4f5c002c7784102d1abd50d43e38a6f415497a4cefc63502ecbc2158c11298b39b1caf7caa98cbc100ac691a44810289da75e65a477879dbb4e3cb7f172ddbe808cb5aea8df91d7deb8b68f6adf345808a2ee6f5c62f8a043344ced800de4d4c9dbfd70f303fa181494c356a1b60a8665abee4ec6696fd31c3ff41fe76f0fa30c028cb0d10d5ebbcbbfdbf9ca67b8347e6a050dcf1790f65a5ce05f060fc7ece38adcbd1ebe05bd8cf91875ae49b1cfe20ff809afbf1b5b8321effa1c20dbbabba1a5b7b7b262215da735c733a801f9720d3c79405c584049ace2dd77d4d8939228c1e5fe54b42cc4bb6a428f29100077b3168be083fbe31de23548152920b738a5de776d40d00eaf63fd8bfb8694bf6a3c9ba226deeeef88c9ea590bf8e31398903378d03012091cc3546932475dfc871371e08008924047afc3d4666421f1f9d3df4752d7dee90f7dec604c82686fafa8b532df9175f1ee96c95362121eb99691bb804ef498311ec70e7ab7de2a6b62139412ef8877cb110e6f1b1212e4f382f8ab7c896a9c38b51e5370d9bcd9e57f2a7367cc3d40012827cc237cdf62402060489d39ce7cd210c8d76b7f5b4b077bace44c332b481ffa35ec6675f98961fb82abfc34fedf614ec35f202cc635e30e54181a6bf1cb40924f6cd042e038accfb738867b365fc3498af489a85be7e452a678c18b54e92b9c17c83af72fd87b5c2835dccef3769d84c4b40cd2f62584fce49bb60de389e823f6174eba65fdf1579e2d9b81aee9606d5d65e2e16e4397448a2f4315534a1d51fc3ad003841b5525c6e92bf541152949ac41a2e532f170f5f9e1baa361eefe93c129a55106c504c40e184c38336c8c3851add3c316bdd1ba52c4ddcb8e35de75bb05bf796c54d4acb5400974f28d4da425f4d8cc70462ee8b5d1d81f8c362442fcc53cfda82c205a8321f339578694e2c4621217c6bf44639fe4cc5942862becff9449b630e92e7f223b52111bbc2d04a3e53e11a04c8ddef947d3c80d5c09b00be685bec0629f14969bf588e4648e4ccf3e483e110f5ed7d32cf494656b9de2af6a9d3ff4d494b2acbe17a536ba2e2872e089dc50c29e44e5e63130ef92007a3a20a0299e286b712b893e2302079b8311861bd41e41661020ea3f0180c3d971d79be98cb33d2f39de89fb7fdb277d0820c94178232bb57caebbe828653166e920c9689a14ab519f81344a4730182992e128778d15a86edd4d4bf506a7a911057c366839eddede9f02fb6a84a9f16aecb7ccb632b6a5b3eb95e389f2ea05a7b64f022bc5eb81e740566c583f01649b58508378a1185869a6f3aa1fc5ba7c7ec3d0edb6c9e65e03eddc3b89ae29e32e02f773f9c2c044d859c30559ff78670dc180ab3da10d8404f9295087c08ffb506b5646b50292e508f03cc9df06a31d8593eb283879d9307306c4be0ba216b0f56b686374aa8ee534fd693fb147a0d41adce51240f7322341ce019e4f2ed791aa7779fbed640f8d01da7b59f36364e5980d41eb8333e76dc6c0f81281554bc6d388562da116524ad5c6d9d6dd8e48b9845472da76ad5ba799716086fa054d5a3883c91a1bfc2cf8c88cd4d1de121cd625bc83dce1439e512339b33bc9c06464e37fb705c268796538a5e2e4ca0cce95ef8c32cbd647b58b9f7c5c85c2283d1e8962d21e9db37a92d9c3a8503f91c89f2d183d8c085af3926863b46d6608a8e1ca181a3c60c42ef5eb726a6ef89e4e3b5c8b57c6d3237753e9f794f16c2e0234a943be85c3a57509ac83202ea4d21ac82d2f4c95a4fc83f86564809d4dbdc45471dc199c7e5bfe286830c966cb7a700c5e08bfaf4fe9ad391b08633e8e1082eda31c89f54d09b3c207c2445eb12699ce97f58c65a6d9e8ecdbbfccdfd3758992eb51514f575e39043d652d61eeca8080e762b13290d42fa285339b9212715ebdd2ced70e0801ea0433aee66cbde2f405b1f0ddb7148118f84be453b165b7446029a1364796c7b868389afa7a6f1efa6c42a878d6bbe39559c73ef0508ff27662d3f42a5afd05a0f09303d206808a404b71a18767452f0265cf6501e27d467cb3736651d9a9c76d4f65ea62503c55b8c501528500ce145389de09865ec5c0e437cb164339ec31e5e89c275076c4f483a254275c15568784ab981ef066fa9225832c760119a62239f709c74d349456abb0f869b9814ca3b13f332654c5be8d5918497efebfef656e1401969d8705a5e382f86e0921190ff4d6fba4e84fa81c0b7ec570602f0635237012bd901c4de7b0de4655a2c58929fecd17c284d89700ad43685264d5107f36dc96d7cf1596321018325a1d6d79f6de0baaee48a0c1bb5462d4e2499834ca5984400f2399c7e999ab792276298a46391dbb26063ef069c2da021f024bdcf12051e1c6bfd02104293da79f2b212c6e0759ec8df330bd7ce08245ce3bb7e557e3a6526c894c994d9da903ece76e433685bcf89758f35af860f14e0b4592185b73e98026240677c49d7767924fd745fd7d1cfa416eda5b9b81a309ada71df18da18023fa676fc36fcd96d4838e61039e9d62faa85028b207c781872705c73e505cdf3494f9f2cecf7898d809683c3ded66b8ef8987ad533829a66105ab49603505f144a6a05e168049fd2fba2baa93c672c69cfda4ef3d84c9309b78630cb71cc2bce636a34f012844336eb2e96759fe1f60d81d57c035c09bab77d8c86835efeb2ac3379d3e5a4c65f22bae4549dfe4b05e9268788ebeda9a6536d4f9d8d29c88d64c874534125780ee12f29b891bf39e62cb3cccfcb901f465726c11c02d9cb2ddb285efd3ecf1e84eae0cc4461e542ec297b14f1bae4c8582bf1bc6fe78c4f47ce36b6515507372bb5ac91a558d140cc42f94fb0da52a72780df9ed7b3a2d40017ab62498a3618478a2454d5639cbd0b3dc7ac490c70df4e1bc3085c901c56a0b31f59d7c738e9a084f52663e8fa0ea35ea114fb7cff55ef7bb7e490f9a58d84212a605f46d54b06752d3323e3647b2b6c6582f4ffb187f8c84c8552ad295de7c938dd3b333d3a359f3acfd901c8bd64a745722c2b2c4f4345c7188fc7ef79df7a635cf09bd5d7f5fc9f176f17b054ec4b3bf88d05b86a4443b4bb0a3f2f331002f7b02dc031b84df8b24a29114801d32fde5a3d38d54a0f03bcfbf5085c59796f355b66ebc1ee2ccd142f08c6752cdcaddc80171bb14d32b41f4ef05a9c2a35d26942c431626bf596e011642d7d2d6debfc654da62fbed2db53f8ed3f130d08b694406e69811c529aa143de40c2ec0ac702a386107904acb7bb2342815f62404f93a82a343b40a64923d4d5048a4b623c421de2fee0070df46b117663bf0f343b826ab2bd9b676e3ab5538b02dbf67cc080b257b0ff490b6a85015d8b79fc8cb6870edef3d76fb8b8161b31f5451c6581f290b2b4e992f740ff3495eccb222032195ec8a4d7c60c747c5a302638494774d15222c93eea0089d470c81f1ad0a6e300a18870c817d211bd1a444511d23e97d7d4f4413e4836d73d72bf1c802a7f4ddb42d540e6d134a6bff6110f67cb80bb13b6793e1a4f1a864a1157ac7d73c0f437b5292220c55368ac5cddcd781fc307b0078e5c8efa4cdea8a71c22f08aae5264ebd76354805bb1f273f3e2d8e57dd0084606ff0ba168a24c8d84afd0d138c2a66266258199aabc03b07d010c48007567493e29c0f2c042f63329ccb197b690dec1c6361ecc2779b0b96e166ac1dadbccbe51ea4aad298df093aaea3df2ef84756368bd179873db908fbc56e4efff1ab47564e0ca6b5b2d91542f97547f57e2e3c169232f091926bdcedacc011ac3a941479bb73289e40749c5eb17e8098df2c73e4bfd8074166eead3dc7ec011aebf2845224b7232d534681f20c2411a32c36d722edc463a6385791d880d056d176ad470338270b7e9235d7414d22e9d36062403a467e08c0274f44bcfc120820f7b48ddcd44e1f27dc29bd9300f48c3c40d0c5481520bd52b7ad4d848736111f8f7b97fde6bc03df283606deae1bc9d39de4c5402c7ef939c3abc52538c7ed32a686c156117a27512335f9252641a1c9b33932f71ce6cf40c0f5e66709eb332109e318585408fce1bd66560e2e6b11aa4699969c0dabefe1d1749a1e47917ab06595c7cf1e0922962a8fc92ba30661e7845c172d1b3d5c37d58115909dc2172ca04ab9c7ee56159f7c6bd100b0b1f17836dc572942e58d759fd4a82cbc056c3e2fef1b6a8d97bd957a868c36f5faec7d2568550540b5c7a0b5933fe270e40039744f62d835406ca8cd8256b31922e48748011b36e280332706e3be3e78d195d74a346f2ed127e1e3e59662d4bd0e8574917ce846d51cb1d22765553327b52362bd75e46a2329b40e779f973dc43e5e73e717101cfe1d16136fa2a84ab145509060ff54d0f6834e209048c54493381dbd4575f33ab2a8925bda59d953ca3ba6115f8f9c90de1c12fc58f702a4b88bcd936980a8bc9a003ea2cddc9f25bd1ac1f7ebdc71b9752f996d5f2989fcfc5b13523419447d1ca0412f80246df048cf112dd5b56dbff13efae8e62209e8fe2934cdb93d3457cf395fc56380ff0dfc99241cd917db6de2b9d63289679912d330185582e1203b31c1c73d8e5eed909c207d0a74c914805fab0d2d41ac39fd00b6f2323ab026322cfa8441956d9644723a7425f00864a3062ea74aa6e39e48240b046a7a38c8eb546188b00ff8a1e32ac154f07185aed167c6054d0a4553eac5febc33aae5a3890dc64d4efbcec31ec68e8bace0401cf5f3d58fd0ce244ba99991cc999d5652390c1034c41cd02bb3ecc631fd8aa7f5923deaf884a8e695982963d58636b2dc198032e937fc99bf53da8f103b9e8df0c46d2f3d7a2e6362fdbed64a2f3a83b5a9612de91f41b658d6d8b7d3ccd23dd098cb3a4c8d84bc4e03a5461e9d79633fc5b2d3548f3be8d5d7ca04cfc5e386e6c11dbd067292f23b4210647104a866fa53ef328f9efb1f8ea302c75130019595b9879f6cb51c34095ada5328b8183d41c8bad20cbabf107f3f6e1783a8269f519606c695d2b46dbf2fcf3227ce13a09b7dd97d281ec831e33e631660c1550ef62caa8f9eec64a758d311c942746df061520a3cd395d6b2b83c32252013e6314a5600b8a3f7642c4d54767630ae69d4db61b90beec2505d8a64fd821930facdbf16fa877bc73ffa38ad284db075647d8fcb88182fecd32df66febdabe28116f43e72464d3ec5e1247d80ac601fa4f9dd47d8df4aa86894af8a3ed18884200b14db22bce8e1da00718c75f1dd23fa730701e285bcda46a972960600a68ed16621d5016703f32a71f65939ae1b08aa5f527d4397a8db7e97e91c6b7a250e8016ef45a08e3eda048480bac1933be1f85ddb7566d9e0cae478ed5d07f1da20ffda65f8f4198ebf4ee39662b150bc03fdd0817411762e802faf8f46e2c8995e142acf93abd2ca067e0d7b4d41bf65c2c1b99a2df4e420ecef4983e021e2e304aa4fca0b4bd04ccee587ecf1a1a2278ccfaee6f6f0e6a418a64905d4927506df3c5719d6d1f498a612a70cf89bc73d19fddbf072b65a69ce78c6ead7a93997436e7768dc204948b3469840bc20e24e72d08e647e1ab9af92522a6e417e15f16ee2a5e9138a7a5ae2064b0b64329cda446414d2d731363b0e840a1df0a39553d19df8e0358b7530851add4c447bf01026b9c603973e8d1e0eae594a69cb090b3146e0ad436b104d55aae3ba9a2dfb389f2d2349e640d296effbc6199100fdee2fd2405ae0f78f28aacf772570de600f7e4d473a604f24dd4e4cab5bf7b54b76904c8bde70130a578b479801fab7d80fdf14d6e24e99cf07fd7da4a23ba869f6d80d3c14cac92c2b602c753da87abe9d4bf3f121161fdeaa87b6cdc747e6c826861c14076a5fd159dfffb5b36e45d9c11dcc39a27a24317d0b64e9c342b956a2cb5753cc3d18d9cecefd8d19b5a73c96c3c37656dcf5c85cad59bb3076226fe1f01c6d45e205d4c415a637f94f22f3aaadf4eccbc14427ade84da446781b66fc8b4c48755cd0e565b502f2e6233daf541580fe46d8135ed540e3dbc4f26d9effb4e03e4dfc3d08abc1de42c1cafb0c68880b94b51614426a04131d671300170fa04e905b60befbd66dfce0a8611b697f665b80ac204a20c29c01d6cfacd3f11ac73c7104eddc2a03d2ab0d5959b7b54d91999b01f1f0187dd56901d95b65b1090050e32d00a328dd889f50d8afda0ad2ea05f952e73a3b14ede232ff35a1c7fe9c1ec57d6135ae9888b713428452fb44a32ce931b95268388a753654860d54e75fb3158d6499a0175da09be6e549018ff92019c7c4d5a7bfa30271148c0544a218f396f27b4fb652b92b4eeac07cdce1ee43d771f1accae37ccecb4ce094e7ae1b8ddc146f88f7ef06f6607638705c1fbd9877e48ec04eb1615dcc0f54e7c2b201daf9fe1d69c79d004698a2526d03e0cff03a01de8f7f4573b428735a4ab9f43e97b6c222beadad9ab56fb1b03766fe6a5e0872f223c4cbbdc9978e4d5dcfd3b82c508104fccccf9b137053c7c424524da279e80a460c0d3d262da458d2bbd443cdaff8d9b43d15f157de6c212200427e09daf0da00fa6b84d05babb9c5b361c894321f25aa5ff9bb6c7001849be6857460f36bd05ff1a543255585ed69d41287dd90d0cc72f0eacd702d2a1f239f09e184135ccb2fc1c1e53acff2b5150e441abca48f8f9c8fce2ecfe9933ca7a2436228a25dd85ac6c4492fa81b52c9328a76cffae907234639b948e812a0e6199cd83559ecbc5420e73ee5f72cbde6fd2a016a6c78266b2be62063d21a13be99d8fad7cdf501a0d2a752a8e6532a99d807ccc1ce3209a964d3fa1a5c556d7aaba806e4269b50e1e5af97a0d46d9934e83e64a59958136d9dc7ef84cc8dad8a21a8fb178b18b4dc786d540c27bde468ad646790658b922b08854ea2c0b793c180a2e4212af68b44e0fba1bf3de6ce06f1b8a30f2d75f2e8ae124bee9f6c984a61cfe176a3b419adf89d39e338831faef5bf31e14e89041295a5a61b4ac98880ab0dd21ae0da42ccedb891fbdce9f1beea2d1c7c5d68e43f03a581276155f298eba893c6e076882aa597b8acb8a02c555c76c9b3befda966620d1a2a99cc7caaf0b62b4a9a77cddebc17f70e1732ab947b7478548f12cb94bbf7e0dfa761e6c0a010f1bf092eb08ee9dbbd12adf7318ef5f3c37822152f7083bcd3bc5ade28d45e32245fe395886544bee46e57f380d1630766aca58c737484d5b25750ef77cb4e574a5726a97d9af14853aaaac596de58a52964f4c9bf0d084979b3f36430b560148d2458a3e8f74e80a307e02b704e32fe1d3dbba71528218033744f0f48b92fcbdf19a05bd112c0d3a25f576b91031859191f261710a859debb5d916ba70e2936815b57425b8f3135cacadb7a145044751f535a3609a6e12c44b292416a2a392d6759889982ca1624ee7c699e24cb37b6d0efb3428fbeac018b218db6fcea0b201b5c197077c3c655c8a540331c5640a7fc4bbb97524edb23dc504640b61f36bd1ce51b912991c2fb93593921e106c4a96bc30becf2298b2eef1f119b17043c3184810aec263fea7de7fb340f1b02fb22772a8a7844a60e287ad837f1a6c6be658a1ef2908f0ff1dbcba018a2474aeb40dab9b89faf2ae3be474f38f43774f5ffd05660f9817a9a257b1d606e7f28dff979f49aa65d4ebf15aaa9f67d2e3824c61f8bd8a5551cb130b2327a4af0a7c971971ff08cfb81846eac3dd6c52f7340a8e49faaf4acf9d59551827bce014ce3ff8540883d29e5c564eddd363b95989c9852df111470e90ebd24a20a643758253a89f180f2f1774ffe5bc7add46c4070f4912db64a3f3da2e14858dc8fafdc9486d67bd72adf31533108acbe3d3d94af29fa9ef288b1598541d415e2baf87bb60d0fba0fe29e2bd94547402f1a84835fe73e2f4445ccbcbb5fa79ffc0c26369c422ea70ba3a72eed23ea116dc24927c5aa25200ff03e6502c0e9564d309924ce31ca8d13ce61330e3bd2344767becc18d182d06453e9b389e8ab9675eb5323ea531d11649517e38b35b7a08de9f09915fb3e729aaef4768ab93384f07c9987822c75af1170e2ebf362d9d6b48b8a763b9725f70979c08c119459efd8f49133330fd5997480730e25f9e3bc63d60159c09b84d6af45c6ee880717c5e3e337b2e054d33fc99d3f4dcf99f0ebd8e9a8fb2f1d8467cee5af5aa21dc42ccc662baa65979df8dc421a6ccf22dde2710e8c9954e27ac5809704f974c19e750ff5b9e998346da8f9483bd2cf52892c6fcd5cc9438171e89121185cfc2bc82165fd4334e5aec0bce590d2660e697b4a3106ab351ca4521aa31cd9119b132cedb7513fe7345f58a96a8c5a021f17b35116a0cfeff9470fa97bed9484f57b9cd91b0055899e24e5ece9201d071424693d567dce4e737433be979ed74f89646444c0a22325519d960fcac6c295e76306cfce8ace62b489de22a5dfb7d860c5a6dd8e5fdb203f69133463e507af339d72e392834ae897662c9977d1a24d4071a2354dfdb5cf321311d0e090bf5411e36876c39cb1db43d20f37c8843c14d8b84112f211710f552480d00de17fba214a4c1be27b3684ce88e2ad23f10d74a43f1fefb239243704f42437e8f30b4a4a896e9871dda007154a4811b1e5b41fb3c38225010738379b86e2eef09c9739046dbea40b4cd2ed0cf4d678923bc64b0bf9f818bde626622a7802de9da6e5263c00f5aca4e327bb9c518985abf75cdc66906db003f8560747dbf6600d37915f420fd1779fcd2014e929b98996ef7a420cc58f8661c540e697aeac5f3535116d29a6266edd969af0689848d58d76f0449bf05c05077a1d72a00e9f03478786ae1c732fd2098dd780027adef62b4e9ba00afda42e117df7d2d0048939034589902d6e074d7f4f9dd326daa95c44aee4fe8132580350e15ab628334ea2537c8bcc36dfb9f82d92fadd19fc003c8373a119da84f7b140b45c095dd44e42bb4e7f00ba6122e22f723ac76c4495d949806f26ba30cba9d2813abebd14168c1dc61b1fb46b8fe7cdbfa75e7c0a59b56e142b5e45ba54c2551f3665e2f85a53103bc4f49efc7f5a2d2b819458df1adee453b8efe747ba4c60b60f0d9ca827a19d5b6cc9c633549ba3f1a71c5e63c15444ab87542a23f74ecf69c7e9c04329c583c4da11ec4f7e7292b94f90a51bd9fa8bc9cc0f03b8cda126b6fd5bffb1d4721d6951efc85af4d140dd2c13a26891d8ec024bccab8e3d32da6b428926c9f0509d2f75fd21fc77ef481722647dad24ba85c043f22bd9a304c4992955c9f683d2527ddae3d1f341492c98fdc83442e1999e9f4f4d93472899a6eb9f67382a116ef3d9e9a6b744852f9aa85c339307943ec78fc4fd33aa6d84aca6999cd272fab382298156172151a7cf905f1d89351ae4efbe3f9ccfdaa6bbfa94857a07f04f1180eb7e468136daf2834628819312d59356b1bfd437ed989c6e4a4bd0073280e357ca3ea80834255dcd811f21cfccb594458caf0084232848719030d80c011b8818f980d42c387665a23cdf0f50ea2b985f578462088c3136c954be243e1632b80e4c7e705d797a9304c50f127b731c1dad22b8b9e222385a712924c933ffa9a11403d00ecc99a01850f70f6feba747f39a113a6bf29d43b1624921170bb2b4cb501243139d57a67afa35a4154864db2778b8aa2cc1360075689339648e4c3f4f546adeae5129d413b02ec32eb849a3b38bee894a6922f8f32534fe26fe22a1ad22dcd562d0586b4174aff8262fb0d77cf2f601664f3ac5f4f10e5f07cac64e4170743e18c97be025650c1b20a35fc188ca98cd69e0e8ac5bbb47cc0c5069326686d4ce2f4de07ee38ff9a34b046f4e5fef42269a6baab657d0c03367509304dccf6c992accbb916a3710559a8ac4e3b25e4eaa5888116a654fe7e4a547593658536a1e153e6546cc431e690f73338a972d114845bf7ac264465976d37a0f08e78e388ac4d2b97448fb63eb7872d975876e613bc74bec42c055143dcb6480accf982650c5e729073034418ee91aaa1a7f68741bc68777d718708bf0489362733f4da8b8d6893f3cf7a949f37b419621fe13587c74ebd9b9ca3c5dc3a8186658ddc2a0a2d2bf442aaa0e108bea52a3921ebdd80244eba3cab89ef5c70b55aa158b271a4b5322e8af3c25ee3113ccaa0c62d381289a5a656fddf2abb6463162e2ce21952dd9d88ff327f8439bebbef16de26567efef12c0351b126590b7d2038440c9cfb7a9a9a0671cdace3597a8ceccb0ec70a7521d04d02e6204ab5525f56a59aaddf603157bda1661b0d40751c800c9568c81992310e00c465151889b4e80008c14255dd6ecc4927858444e73dc4aa7773c8e9808f5109f92dcd95fa7a10af18e2239fe81ba3fc4365d9be2d647d55053494d9b12491c4a24481b7dac617c4ad2e762798429133bf66ce1b460a6111bb447221ceb7ca7e6cf9123919a6b1e426672e65a07bc26488fd55497701c4c4effe41bf67927ae8c882aa51eab0cf5ef5b8d59949c149110ddb5cccfbe0cb27ffa09f6e1f95a39868c0a65516ca16a1d0229c9487216fb7548c0043e4936f4fb7f4692295c9e5a16ceba9dc092a6aa2b1abbc0c0a10cfacf0f19f494cfc721e35e2270205ad65f806387a5569d42b23424e2283a67c85d7ec08fb3d1a49d4bc5dc0e5839d6216d50554c7d44b3da5122787a033253b32b1c829e56d245646fc1631dbeffbe99a26d83e15cf9570f4f3a1f25ec8ecde29921bf254af06792a7238cd18e076f2bb26a750e7e725dd6fb161e3d88e5462e69fd6453a22ebb020dbaabe5401ae087cac66ca529ed03446dcb0be2a9e1520106920900a515642efd4db643b7f8a445ed0643d0b8fb1f86a42de7ba47207b11a87e56e86ea6e5ccb42749c38559120265532bd80d17f9b2207b1c13c9ed2dda1fdad5d28f6fe738c48b3ce407ee53a0ae3dc3c73d6f58520254975c4381b600016b44f727e07bab87ed93d0ebd312e650a590c01de22ed38cdb4e5c46bf471153fd6ea6452869251ae7c2049c5674c7f57d1a46f6cca4944c7a62d8488d5d1809a91ed4639e755c4d68879ac36fef29862c1cfbbfc370177ce3c49518ad23413d5992ba907d8e62b089c959cd79ff48371142f117f091d41ab4596686d05914c4f556885de716e3f240834e0003f0847af41be9cfe568df455d04c775cf3a13e39848915e515b8d81348b6ff2b6ea3262d3c7b3873f167cde0665ed6cd5b7a0b6ab2fbe048004b387ca5d8b1b74644a40d0c73705127dc4d846d5ad813c71e55bba9ed0f144c501f2b6fd307102e84d212a1068fc1e36e3574fc2174f4119c6368fa7d9e79589a35da1360b49876ecef094bf49a4e5152830c9e55fc1d614b9f9491ad2b52d56d2560d965904b03276af9547bf3097fcbb99f0018248a814ae7864dbb2b3fd715fe06477e49130165e8277493b7257d92bc3560b3ff2b0b8b4b08bbca3968c51f6ac464bc63f70f82cc57d96413583b749d361a87e0d57dde09ad7990324866c64fc614c08b6a47695576a996b37e493673994f8bd5b395557b5cf379149b0cc905f0a423259c4d9baa9c75cbf6be1565f2006c63dc72892b865b4a52a0c39330ec202723698357cbd5d09c8d9e429df63b5e7cd4012dd43923858c71856529a8b9d203f56b6faf7992e569bcf22a5eca3f874ada4860ca7596919ec32ef76c2b1a1e08c66941844f29478fde5c6d60ea702e83bd1f8db54b40bc4521cbf573d917596ee8533384a189f7cc75fd6393f6a1755d2bdeeda7dfc155543d3a2e49901d3a7e4564c4b83466b71b1b6742b0cd29d2be69277996564014780a96a1aeaf8e566d8500c8ce893497aa379f16904525a74916b1c7c88a9f56cc57fbb17726822b0c6af902313fc9e149f9bfcf2598ebe1816e230d74704c15d3a71c3bec10b183aa2a874452f770ad5069f8df22067245c9c3e11ef900603124234bd1b4fba3a34b01edd691c2d39f4e3dff13f25690134e3f1ac1a131672185f918981da4ac25fa180a1516626a7788ff9161e68a719f00a2bc49528e956c8b296a2fe3271b20d4bfa7f6ad852f4c0bebdcea87d486735b476739ab5317719575633b46913081dc6007b7e02c8faaf9f73bd8c002d5a1db1a44623485a7dc2b3fe600b050b0dc4d002a57cb2763ec8d3d63342d9763eb863f3590d8fc0aef9069f833ea0958b29fe1b69cfe0fc45e2c8ecb34d51b2b1c5e3175790e8918e4a41979c2803bac587ff513c902a9772717238a4dfc0c3f99d9fbe23f7ec1968722933882787bcf352316f8e20c28dff2c22c38be9d95ed4f2661ff84539df351fe3129020f634c7e85403c613ea7c48f9df0e416679c478930656ae330ebf1297e443ed33de93a51c640e6999e4171bae597d44141496a235a2d0299a6782c7e1e4466a9803db320e89219c37f9196e41b81d778542f596fce605eaaece8a7ea3781be64e6b9282af6ae688e514bf3b68c914581c8dd114374d8060ad313552a039ca493867c7404c78e08b79fe4a2350e076edfb0c95cf0e531ae4fb7d35d3d627e9d789a9a97c49a62963dca03c5324454633564ff651e4c1d12d97594ab3e04fc79361161e3cdc4866ed9392c5a4b06b166b233bfa98f2405252d9c8e0b6998201b9333ec832b6e64e041ba146a4121395b4aab2778e965f01f6480e30e4be370c85dce2951d22afd1f8d69ca3fda1dfae26b833b5fef488c096766de23685c81b4efbe29c68e5b74c1619617b9dd485eca7633d49435a1c6af1de6f39de7b404d3307e0636f6f222e52ef98641f74e8589f72f6abaaa3cd50a7893ce0736fc8648f669b8ea692f0fdff2b7e0674c9cf009c56ba68ca34db85c1ef4a8bec49ce23d97398f71c4bcac23b309146fb02c60f9fe7c47c92ae533bf63aed317c46b56a4c1d4dca53d30c89a1c4155a11b6a25beeb9699cea90cfd5b8101a347c070089ee208b0096125757c19ebb9f9081c08f80664bb878a8a8715e62c1e4b06fac48be327f202a412a318b5abbc82755c0ad3757cc1a5accb7e6f8285fa8388891e13e9e382fc21e9356898ea19c57527b68bed66b24047135d0c4265bf01c899867b3cf33ec603b254ee8826830c1ba24fdc4dc214256feac5e15f8ef17149728d756eccf67989de43087e80d537caa99aedc2cfe7b0b5cce309bc2f769b46870de19bc4f2001a92da4fbdf31ee6b1812d435f854564422f6f972c260104c0f63366b654cf46a2c756236ec0d03966fba1af4896a0c1cd6fea753a1fa8a38c9acb07375281d8b64c7fbec0858db3fa79a43ea4e9345143df9b8c7e1c771ff01a7203de9f50a363d992cea950472444efe94c538bc0bcd9ed847f260f8404c51551d42a84d59905f2092eea93951a99bafba989bfc5c2ba2531448c063dd236913f8b7527e19a247a6628fcc3580ad87da2e4fe1b03e214d699aad02451650c46be6bce3c23e3197c2758a00b6085e1c6c546fbeb8e703068bb63efdec9473ecd644dd44ad4c6587d06d871867719887b7e172648b23e5b4ac40d6811869b964e76d5ac959027550f18943d5e6dbbfa52b9fcaeab49cc348c634c1d43533b26130bce84c36ac788e547ef8852c17e7e9b2b3c09a0fbf629ac7c8ff3071b65d54f515dd85edc9bf6e387dc4627829f8938037403eedef619f1cd0185f081d8dab065227ba1823d3eb3a1fe115704be610c9692ebb0c5ebb51b3d147f9ea65bb151fbd0ca7d252e4668bd1ae6be59b470f98ba916d3f1212576ab60563c67b21d9f13997430364825181cb1fca2600184a21fa0425572b0f1615b4102ed2c6fa387a1bc97ec40317d4f1ec9f1fac1a408face3aff2a0cd60f2c48fbf4b71ddb1e2d294a770e28268feb18822b5152386062f450b10d556c18cfe859cac572ede82629710a20815e3800e30fe29b1a04ace1d9471f8df994b400fe97f86e033b76683d920b4ebaf5ddb4e21a03a02a5d06742c31c97b2d2541b889384368ca5826b450d26abb983495acad2fe07ab9bb1d813071801ae0007bbd3397ab560ea7904e049464292a24abdae552fa3c68c882e4e4a0366b7cda73cc2922798834e1683c0e6d9074deae807044f31eb2be018d65624bac4836f4b9e0ad4d0e823526c5845d73218c83829aca70c383fc2349e92b697d41b250084d712c88f598b2adce4a784407a0f9d16ab8affdf71d886b1f1286ac0eb46bdd2b3de52c788973f26717dd8d6dc1d403b501b58606e9b628b96382e47d92e6033ed199c92bb9b9e0443fe0b18d2490fcfcdf93f3de15b24a73dfd1152e79d19272220a4581c35770aec84394db06bf0fd3a91e6bfbd79850c2618840aac0a374c01a788c8e407323635404def5d02751fac01807af36aeeeaa3e181eb4023d01a5c2213b6aedc414374aa295e57f6903d3c4f11269f93650e8b756785c85aa23491a0a10e33a76d32875a6fc7f7e0d1e891b5ba32d4002443abac6471cf0c212f8e9742b6096227ce9d5d22da97c0bdd1e88da09d14c8ebaee574a41649882b72ebbca693b465f45dcd7c52620f1d1508cdc24873cbe0c2c9e200d224089e828eb65cc14aa27a3f21d15c6124ca28cc6e5222a58b987f0d9ba4a3bc2e3c710717d1e9caa49469b4900944ec43c8879a73143dc812fd6ffd247b6f27fb1e0b3626d6ef6365540c081c3d0f133bda9f04254b6d8f3a02fe2282675416c37d0bb923b6edc842694d35711e4c41032981c44381f51f216048466e1d08ca30d35533b061b109f88945001ff468f499fb5b77f261960a688d97c8a0caf054b0af3ce90dfb477c06ba96ba901223db0fe8001ef316134fb21499e6db92e45863d0f3f4fd64ee4c966c2058ce531fd4228c3242766574cbd78130d8dd66a010ef7bfca02c3b5ca3296f2883e30b833e71515be97e82c74021e3a78bd7ed330f80726a284fd737fbc0161ca11b82aeb8027a0b016bad637eed8164a5155578978140554d4ccc650d6c632522b6258683b304a607c13b4bfc8094dc2c12f84a9418ba48ca37016c7eb17da6ba88bf69fd892bab99c223810954970de12261f616f1cd085598e5d9a65ea0dfb2da3064fd13585c233288bacdeb23f507645860360700a4b1d86a12089006fc7a51817f260cd1e4f6a502c3f0692e1941ed8cfb4004f879dd2ed54d2c68ff706da4fc91b6c6f3a906b657f55639902cf1027eeeebf046e2a735978a235084382a1482544b184c3b216f3aa326ea82f94f5a0373c07ee3933b122f735abb5a5d6103f91f95e5ce4ae43e5e1a316dd1fe6a9f6b3305b52a081abb97a37566de8856c9f1828bd7460e4e609f5051d221abc33a320332d75ee821d0b4b2899314aab4e8dbb8c6d3b6d439d0e47ee92423b9f72011f065c026bf47a90ffd9368182ae1f778bfa3ee26975c4f457256f19f4bc69b3b68f6d12d268ed9e6f12f94762d5e0668117851b922fc55ee8641ccec424fe360d59fb84ff1e191e2c2575d1045ac24641a5e5bd6746b5829c814a3c36d6bd33bab42b894a3e7690943368154da716d372c46afe72affd3edae35eb6595545a444cf309b383806900a49236011410c14ae259f2ce7f3a89b97c4f629c83236b3d6ecdd7bd7d70089580209949d24f313dc91978b438d8a89d1370b711d9270d331fa3f34d8e50f1d8d99bc6c429b2b48ae5f688be0e61345b643259cc413cca12e74771c6efb1bc8858efd46e1d560942691e47d1d5334882a3311da409d666db4c422c3582c131b17d2e0f853cf33e516a3c14bd6f2d14ddb52fdbea60c12db23cd021f741bdcc6f4d2a7886eddc0d1a1a6d6b4500f5a78ac256a32b689844c39eea588dba1bdb81bbcf3eb5f8008e0880c103b0f24526bb59e4117bdf22271e511684075656ebad45bd4208eb16c6c9e0e2031286b91086d5c257bf1fd2e81341637d02552b2353fdb7ccd61af16948ba32b1489fed94557bea9bc0031329d844f1ee5b337f80c1da5357df1ad13b496e73cda09fe3a4cfb90afa39de2aaa7dcee5831242f5895400ce39a4efa6395bb9d1f1f582f216833caa426f0bdb8c145eab46b78097a9fd7acc7ec004066307be7ca31ccb0eccc4b1dfdb5c71bf7ddf103e58035f9962dffac9fcc4a53869acd44814e7bb7006f123b810be32492a884f70620f3da0f360c98a078736e3db32eca37994b3edf8b594ce546c3228c52d7de85a9f2ef4a595b896bd8a687bda944f1a4b93f469e305046059d99454d3e8bc9ad51c01c85a71c34c82835e87fa3db3e8a4897ecab39a0d4c84f618ba0aca5b8894073a30cdcf49bc6e427233ac324b5bac4758addce6a08540a6413c6ad9b3668839122407a75b8f12715b07303ab22833bea1abb10dee94682c2173c5cfaf41129ea2008d9977f7868470e0e54c94368b19c5caa08b26c55f6952dc264172ae3e07a294a620021bf44e7f51cb6667b130f53a0d1b877c3946bd6a3d6d6b2789937973364536491275dc530573d6af0606088b1e998a20cd595a9b4c558b2ab1d82ec94f32ab0667070ae803f128899c6cb835584dcef17bd65414a5a7fd1c83bb0abfbf3ca0894571ec8a806d5a612a14b1503576e362a70ae02400c7ebdc3ca900530622bf4242dcbeee49eec01d6c72c1df9076b84e9c8b67e5f8465ad3792b0be4d025e6f8314054595b4201f9b59c01bc0b6972821fda612133f2571547c6da56a2c854406e0bef9c7418be83eb7bc697ab56600c63adb1f27618056876b4da9a950693c76a0cd6b131a361f138bd35bbde6d3826b9a43d8760ec2add22cc7666d20cfbccf43e857291be0a4345fce193a63fd0b8e9c7cd12e323a6adea734e21d310f08618286d30d6925322dba370f0e3706544973a310ced7076d60fdb8d4d1106b53c32568ba0bd55759ec86d369860ca67aa54a86bb7fb3a399c3cbdbd6bb14f5e728ec5ae4a9e29f88b672c181d0e499eb4f806686dbca10b0814ef8f72a25dd1114868e4385b6a8e3e50f3a307650181d355c090839a7cffafbf899b7c28e3f7e777263db68633f620029016cf863dfa7fe87c00f6b04ec4057517324f5e1604fd4ad8c927311c64529459c72692cae0f3ff1144fe8ea5cdcce0157a1a89f53c064b42708ff8753b04f511a15f9fb50b5adf339b51eb1215b4ab23ba8985e75e20218646809522c4fa5c88b9927d8fd8caa94a1c7f7d249068008688ea874583094f495270b5b18aeda2c662c56d9fe1afc612f6f39a44f04574a3073c28f5807bedad0ff73aed6c281aa1ac4e9566bc4d91d85a5e4fb0dff3506689d140197624544af9a840125df3f2306b5e7f5677dd02a200547f9be70833c186f1310cad2480acb21ab7104b5682bbf521abe7da4543294cf50070927370ebb63a2f94a9cc12b0c6b24ded69cbb5b6e75bf517119f879868e801068ebf7beb21db0f59e84219aa7fc68eca6f55f63bec86251ada339f74071bd9f65d6839057bb506e691b05a3d5efac7a2919ec3b8a404d800688eba66de6865a3aab61d4d702f5ea11405206e549d9fe574b102d5fea4a0acfd926966d7fac903869f34c6b641aa2a36c35c7beb91d1103bac45574f424b33c63a5ef0f4817887fca0b010d145508fbde5f6d4221a3a7ac76d494c09b06a8b0f516fdbb219dc3ba1d0c7722232cabd0747922b2b288055f5b8b3806331b0415579122a53792ac827f8f4308969e950c902faa4d689e4e7c48f9b908f84f247fd272e034200d3f4fd65af374cc98fd7b4fad4332036767a501eae3e322a05f967e18f75ac81e4409278e0685a67e03fe29b9681de0d32291459f7ac4184c30509875c58817ee6328d26f776e0cf69543be5c69a3e0e279a5ba2cca7e19bcb2055cf15d823c54ac976554f73ebb321fdf6bae8663ea934fc477c92c0506eda12fd22aefe6a0c6aeec8a8c4c0f8fe966f28e5448d941aa7caf2c50a3e5a2cac91d48e4c73199e5194c909ac6b835ebf50fecbbe1eb58432b870d85d0c55478e5eccd66f66736385140f80527213a0a31775b0c76b20581ef92c9174ab328525f7ac8457c8fe0c4f6c152cce82b0258707c656c401db8f4305dd47964f4df762996bd3c8ff16a954e336c2be388e44dd9b51ec3f362e3a304f08625341ee2da051e748da8a616182bc35076164826c9dca4e522e45724d2c031f4f724b33e0d5988559f7f54cad5184edcbb73a99497b532172104638886452cfad9f0dfc3d794c87a066baf3c7723a9f32eb3c06290fa76ab41a1afce34247e2845bb1fc332b65ac800308034bc44bb62e43769bfbcaba2cffe473266f8cbfb7b1eef1d56eb83d7321467394deec257c752e08ca7e14656caa0613b0154bc966184f51a4aca7c19f247c5e5afa1b159481d553b58f62f511e4532dd72b8d7eec5120313d432cc174b3e255ed159095adc0a82acfbf74047e5df5756c941fbfe84d276fb402abeb65398025fd98416d73d5f4040ce0ba9ed87eef0dac29616ee81ada76a55e1558bbca71b9da101536b5c20a56bd465145506ac0dc8eab330a1ab09175a557835ee94d02040303ea08c66a59304ada4a07ca94563ba85f41b4d278c99dc0f1d9ac5767b6ed689d69f5af0b387a812ae2dd18684005c6748bfc1880eb014401812000e24c29a6b439d2dabb08523cfdde2d3889fbd08110479a89652d219b10b9a59429251930067106e906734e2927571fc6e3e8d6d7a8e4fad6174eceb9cfe5f90d725f977af2eb6bb6810f79a70593ca7c61c486f53a6b807bcea317fe8d4e698dd297a051be1a74999cf4c339279d4fa953a79f7d71d639ea74e9e71ea07352ca5646a5e4e8d00ed672df1cc77134d2196f73b6a6a8945c12a5d4b68db67fc7794feeeb567deeab747eaf06e3acaf0de873cf7da1d0a59ffc7a4424fe68e6faa0dd1ff4fa171b74ba2e18bab864634ff5e9caef0511dcb1bb4ff7f9385a29f6c4e432f674bdaf44a41a43babb3bd26ee4b89d1833d483e5176c2c7f3cf2a63edda37bbf74269dff94d66bfdb3fbe527bb186d92d660774f76f7492b95eeadca7dd2fdf3ae67a8c04c7df1d2650b162b531a948212a504eac80f79bda9871521041078ecd0d141831a3428fd820689a00c5919b262d9b362bcdd79c3bf99694c225de45b1b2c345762a8a6a494524a29a594524a294379a5f49f1ce52847ed0f48932447df3fca4f4180d81f2434f51021041078ec685076a7b331725e07baf7f46e7ba5fc29b33fd1c70fee6610ef8d530bbf5c4ecad596ab7bc17c7e2c502c4868c5d01051d117c3e882b6d1e76acbf5f179801082083dda06f4f19bda16fb1f6d135af1f183b46de8e3f353db883e3e0f699bec238b8f2e3ebef8f8303efe47efe3b354db661f9fa78058b674f1f245828f323e3e53b58d468b9f41abf14934bc8fe08c97e1157d081278b22211903c21598f23cf0a3545e0bd66907069f5f5a3e6c91940c0191f65cc90417b7e235a8c2f7e455f0d238b6051d12d1261e609c97a789e2d9a7dd1ac6806a36856347b51342b9ab9289a15cd5aac508285d714c12c2899d743c88e12224f84a09b540713a0b9353c0b626d7e9207e6cefff1962238df07b681bd165c2025682017ba7e6e01b558c49aec67997ce940573ea05d561001eed28fb2ca9f6dc05d7fae84b8e7d28f2392bcfd3fecd4ee345b56496a5303e401800928b0a164abe1d19e06c2357afe24251b0536003041b30c22933a26a5abeff55bb556ffd6ac25f09d5fe5fcc2a3cbf5ab2ad890dfb9e9e8ce11fcf9a8f561d3d19def5cc4baf5bba35929d6a55f9dacdcfa0e38baf41ba1d5fa70c7a57f745b4ad66bdc44ecacf56b55fa351d4d49d90405590b0404148bf9f8c82f080ffd40e7734a8eb6f4d8c3de61dfb57cdc7aa1530edbddb2a0c90fe701f93c07f4f90f4cc307ebfa3c0eb7301b83c9a72fb9fedadddcd7da957bfe9ee68465a1b6855688363ff96d935ffc69aaa68b34fa7a3aebecbce38664f15fe199b2f0147b2ff93f2bd8c824f43f1fd62321fe0843807525f5edc762b1eefe0c621842f5dab3a0099017f411c6412d2fecdbb5b2419f9fd8a0cfe368210c5efff3f1e3f0f7f164450d361dddee73c0d1757d23cce77eb27f6d7552fdf64a20c09e3d7aeed10d7778097cfb73c0d1e56f848a41fd92923c60f9e7d3da16d26c341ddddae0837dd436fac9a49715d1a46d2e858e56ce6b4ab572dc50d5026b8414dab8f106e84110e32635d8822dcdfb4144d2c134fa250fafc5cf8a6a50decac90f7ee01bfc8569801078b5fe0d648a6888886463361164305de32f7c83a718ce19d6224444ea0ff987db1f6fe8b1fc1dff0d3d16838814ce0f798a2f87b1214f319c5801d30f89601ac4cb405c090755c734777e48e411201ecd776f01f168f270992c623d2dd171cf588341c0ce1e3e7808cb8c350983c0f58fa09c9dcca2d3d17766667667eea77b910a96a347082baad8f8b1bbdb6f04db63b3f491dcee7102d1c58f3420310f908e317afeb53774c5c60f1baa6df1a31fcc3c60a6ee3f687d330f98a9c10e8815af6df1225e836ab02914bad199ead7525ee3b73184cde2d82454954441b5947fe467fe15556c7fd874428fba9c85c782196ccc6a97aa4374312a712ff35a9d958bb5725eebe9dd1fe5299dc6e792c870a683823bef4c478d90efbcbab01ab703c0fccd6f7e373dd7ed6247761f7aa537d341bfc6e5cb5ec8f3761ce78d9738ee0b2284897eb14e37a5b33f7bfca4586bad1f950e846b5859fe38e79c13cdbca22222c587b183e50fa9971b7fda6970c6396beb43fa533acdba91f29cb37e94ce47298d0dcef9352acdb7d16bb54e4ae9332d72a97429669d62289d540dc67751c6f20d4af1308d39db3199dcbf2042bc8d52e076392957bb208f32d519e4714c5dcbd54d97ab6bd5ee255b2d57e55e30af1dd6e7fb94404b2c22925154aad512b9128e9f5cac2072609a5b49926e46d9b3dd65db9805793b92b9fd49f1935c8982ac0b7480d0af48c54296f3d82aa6b8a41bbe8c5ce0aab9fe15ca2e3939779494eac288054649d0181aedfad1751b4237b420061114aebfacccf5ff6845248aeb5645c4442b2334ac00e74513474ec6558873dd7d8517ae3fcbac8dd93044132c35486498e34dce4c5dcdb0850a23c430b9cbdcd55cafba2eafbbbb0fb5111a9281242b159854cb19a97298b861cd13366160d842811eb89871220a3b58a8a250a11189648c3c81228825392c01b5a409244a4045d14413719454714aae6c58744abf40830ec0163756ede9b2a7861b2638214e7f3107fdb0cfa01ff64e37e4d860ebd5bd3ca7db7173aa271be4d8e01e1d2c206181273c54b920c5898897354b70d0b2660930696612467420061d862c11e58a0c0a187385062c70947499ba891045ca9233718218a24224b304c914726c28e365081825503e4860eda172e326e3c62a3d7bf46475004e126cc87cc1f3821411091782d0f3860916ab21261a4e72fb7f072e6ea2c6cc81c2654c9cc6d281cd963554f2485de1c01909c0d1b38207a67093813a39a48042981330d0c3130f767298624d1a20a29a70e3e508be9244892b55ec61c2c40c0d189e9871c24b9d1cd4e88083935372b516800a2368f0418c932b380065090155878815ec541d995795a353ca9c6cc0c1863d5e907ca0ea095368799669a1061922375ce9d2e58601a3eb8367e6d0c5a5cfd3e6c67632d7bf285610a59452c6b6648e3073fb9dca500d43553ce098a2d79e4c800e2865465df9cd5044c765e5860c74fd9bf93b5df719752b8862e50720cbcaa917d7bf16554402e2733397aa0f35b09a3c5bb9f0961545d87e968a32d3452e7f7b5fc0f2739e0b443b5fc407bee13c924dafebf444487861e687fc3afdbbf76ebf0c03367c294cfdeeeceedde979f723127f122ad8fe10ec7efbc3b46b5d0393997f0c8d61eeeeee39274d6e99547a4ddf75230ce637c2e40c61fbfdfee80be78bedf7f1c3a474ce3929e539bb7e8d4aadafd3c53d694be7a5a6633a29f9081ff2c0bc6db3ad63becfa51fab69777f2170e64faf2beac0535cbe5d99bfda56be35b6df5d7eff7494cb649243a72998faa53785f5281c4654935017596388ed9f534e496f13edd6065f4b5e1cd1aae14cbe776310379c71f4f9b66e389bcf97e8deb82dcfc9e5afd10397e9128fe641ca6849afe70f01cd77ef67ea20a24138b6dfea8e95fbe343e22fb333f7d5dbcfec1c3b4f8d22ac8d51ba0c14ac946ddbfd41afb4a00a8d2759ad2f18121bd63be7ec1fb257e85ccc59331116d69fb2d60ade56abf55e63be754e4a69e7deb1ecba97dd179b8dbbbb2c224977ebeeee37acaf6be7d706ed0d9deeab41779fb35629b95d7a741ff938babedc0b629801da69d7bf26f0d545f9cd5b91cc68c19ddc9740d62143d112454cd81891fa89a4b4838844e97394520e4783944e999c9fad310c950f6e63ed67e9609d4d1fdf0fc96a6477db8854bf93927b2993d51a265139679d2d59e78bfba8bf9c3a1aac9c94f5d65b0290fb6ab0e5bdd7eaed39651c83950789c80673fa10ba3fe695f5948884252333b3bcaf677e82abbbcff75783b4db05cacc9619f64d6eca555aeb944524fe99848529e5942848191b8eed0f6b977f080eb7fbe01bce7c269faf578b481af862fb59ba0fd7fd31ed9bc9fcccb345062ce52a37e9945f8e22393ca77713d681d39df90db9cb52c8ddf283c4c1877a68d1849535c89f909cd7c7ebcaaf09b418b49d974f60be1dbf243fbf6063fb65ec6f06923fef87c5f6f30c03bff2671ba47019851be3f4a5b747448a4f0401f1a32fa51ca594a3b6894bb1fd73fa74293bf2972ff9fd47dfa6d7651bb876b7773bbf7f330f9a897eec84cbb78d981f02777757db1d043fc728fbe5927e6d0447b918634cba3ffad2f05397afd7ca752d6504c1a4a4a5258e23c0d4e3f088632a5c7af9492b9353b6eae75d2725add231101293612dd8b0caac943f64105d47fb29ed29e5b4c558677e1c1af3021b9cf3b32f1ad86028e7d7f472d7d2c5251ba5b87ced94dfd57facc4ffd658ffa4db515e8b8c274ad13b586696ccfcf99857367777b770c2caea3daffc7cc82bdfe946588e1f7edbb76459202bcb79cee7c4f6cfef6adb22fb38b12c9d5bb2d71cfe91df935f41143b65d490172c2c30a92347e6a884d4608888362f6317ab757f19665c1f86b3d56a81d7e57245be94528ee3a4ac5e8bafd7cf1d718ae935e59d2925c7399d2f67b73ed3b95596b130cfa9a8a76348f45aad46a62c6c5f03afd5ff205976f4ff7c902cafaf0fdbf1f3b02f08e8b5da5f90d71744e6b5fa3e5f0ddd8b2c045fe1d2addfacf5f568b03e095eabb5ba97da6228eaaad54b83554635589fbc56ffe5d59fe1829ded787dffeba9da36fb20320d6128afd59fcd74c0fe83c804fb1200fb2ab08fa5bc56bfbe8be11b7cebcba8b679f1291b566c64926a528873c51499988b192d2ad8b1f2f4c41499e695d7aa7ba150d589396b121137380e3f437684e25499c2c56eb46ab2a652999161aa4009c5095343ab1b4be50e1a8b6dd98d564d84b841375a35d152c6460795166bb495b88ff12348bfe3105cada8e30d5602624d82cec4954ee2f43e88e0017e92b87299a2953c028709624d7eab822bad1057fe70fd19887e3522929cc05a2be74f777709e3e0e74e1a6bf45ddea493ced70738904f3d6205554a6e8aa5ba22365ee160274b75ad1b358a4e569414bdd1aa890d564e2ab4f9a1092758c26c00050c6e77a65c5551530394397594669cee464d1365d5440a77a35593175a4a645f545b707201393a4c3dedf0860c07dad0f1a2840a940b13865b0c20da54015506cd1ea80a9464d141cb9e2e4f545cbe4c384fae5c619eac71791e283ac5a6e54ceb2c98c4cd75a35513224160e12a9dd27b8aeb5a375a45e9a9024616ebfaf83a76f000e127ca5fda5cdf6c03d7cf777d4e57d6d74bb69eafcb13b2b67274ba38e897eff2fa4acf0619ecbd31f6ff26c978f5e362ce4c17ef4f6c70466ff4dac4407efe31a0eba253dbdd3ddf5dcaee6ee6cf76d3f9d99fe9b97b83eeb1bbbbbbbbbb6bb77bffecc05436c74de90c26fdcb38a54fe9dc735e392ec92ec95cde7bf6abf7df73fc956b45ffffffd6ec97194ae7337dd9b77db9bb33cb33b65f1ecaf739677b0f1d63a41c7333466f15ca505151515151f9f7cb6436262cc982255905936de3aa1e9630e181a362c544cb103676a3159318506042019716262d5059a2a73bc1fadc68b524abce195cad22a0e8095735a9f610c58505a90bf664254972d3f3428ea7292b244ebc90e30428174c0fcf28678c70c560fdc6aa33542e246c7763d5191f6a37569d512393b748c410af3297a9122853dc3016e667314c217395b42974a70577ce2548eeacc011535684923dd7bde63075a513c7f40699e51a93ba7c4218218c507367ea46c4e98518a42c0165a4033edc6192f34d193bdc101a0acbe1fc2ae286421440c28412d99697150c5dac28f074e54b580e69afd2c8ee6e67336868570ee04af916cacb952f3372a3d50b6eaea4e2caa9223e324692343726a57475eb774ff3d6a7cda1d1699bb4d3b6f959ae448b402d1d55c086f1d6f5c7162d4eac5518592c77238c2c56c2c862dd95ac78ad7eeceb932d121a417d32aff1ad62bd56ffc8880d7a8add62b7d88d888b0d95c4dcfa61a4cd695b5512d3b69fafaf64c595948ad450c98a95d62b1571a5d8d78ff1e5b6caac52915bc1d852922bd925ac51115a7d0058891544246e7d1a1d1a1e5712fa0a6514652475eb7f68f474eb1fe160632ff4a12d039fdb7db45b44aaefc3840dfa4cc8f2f326047dec577c26ac78a1e70ffad8ef08fa66afd847a69f6fc78a6ff6fa40a624afd517fa66dd9700ee639f095976c49ebe0941cf7d02e80bbdd06742ece947261c1d811e6bbb293db5adbf566b43dbedd68f7938bc563f21661e701ffb1ddcc7bee83be8fb0d8a8d3ddd786337afd5d853f78291c5521859ecbc31ca86b19b7d877dd04cb49b074194b51fd26efe2d6fe61f6d4e44aa2f83023f1fd26edf1076bef51c20848096ebdba0e5df0ce8e3f5f9101aac1fdd813c1d3ede8e2bd8ab73b568731aacb706ebd36eae5606fe331e0dd68f1784d8cd9568b758abef43bb85d5765f28737db1dbad456eeb0b2ded76691461fb95ae8072e87884a68f9f0384ae8f9992394c5df738c63d08299a1b6aea05668a0e4151d0b8a2bc199216f8acf88091c102302d45375a1df1001457a0b043375a1d9123bb82d29a49b47005c78932354819c207248cec8025cf1c36420c61c17043411b304d58add04399233c3552b2a0dcba0c21654305826cb0d262b609c09a2a7c70a3e776054f950bfccc80a7fad390162ca431020d0b56724ec09345050b49707083849b27920049214d36b41ca0a0323587035d88683263860631575d6480a24a13af2a7758cc3511b899c2cb9e2c501cf1668c153932e46101c64a9d203730d80b4c152288980a288e6891adb8d3f26a5f5647667041565b5f16ca93d72aa7c7c26eb43a52811b264971c4c8912328dc3089ce602b155b5ab33a53f38eccea8905244cb0f6b4c0a4c81d234da6e8206952848d0b4bbcb48c8c0b51535154515636ab73e3b658b51005a5c1ba6eb46aa10e77c6aeb8d1aa0534b33a31c618a574eff923449fed5ead5799bb7062f9a5f46777311d8e399b3efd6aa562659c93f22fe1086cf936870edb612c12bcd62ec6a7f80b7be9b2054b18d1c436f116e7443b312b4a11f1c43cfcc443b8081fe9d006ed7a91bfac92d18771aaf6fe85313c9995c1c69035e824780824fc0012c49dac80c063878e0e34b8e0e270fae2e445881d2159333e142285903c4e4f4e439c8a381d71825af1a19392a10f9da294647ce804c569ca75f09f59c8ed7a4f11024588d475a3af3d7fa1902b9085dc84cc111275fdeb226c83cfb34d9c75637c322a5955a0223bbbbb7bdfee7d84e5f8beeb22b33b4fc9ffbc12f84ae955011f47c300c2c6efaa3c51e5613ccdb69e4b48a6623a6debdb56a6ac6a18327443ce0a394f3fb177fe3ed7997d8b77691bb76d263dd87598cfbf880e80a3815e7aaecbecc55e7aadcbb4678ff6d2ab97d973f1d2e32eb32763f6845e7af3327b967f5eca59118cb7f816f62d5bf83bc9c199cfc3ae0fd1bbf7ba43ef5ee79eeb461807eeb5ae74f17e80d9cbde3d7a859c039e7a7aba3f0fe3a2c7d1ef4181ade4d242a69a9a63a724689bcff7fb94b6c99ec5f7fb111ff2424cdb6048d136d717fdec5b7c1b3d8df7be90afbcf72fb6986e346d23fafebebfdbc478ce4e56dbb05c49c6f7f754db84bebfc1b46dc5f7d08f559a2181babfbfb4cdc52d826fda1772d411d58749525c8c9ae7d19815b9bad4175e6c908a85ccc7a373fd259851b1a190dbf53e8c5448de9117810781e7c28b79b141a0195eec9afd191292e17d5e6c90c88b0d829e7fa110a8eb7da1fc26cebaeecfc273299fe2e3b915cfb1349db6d3592d4591d778665ee769e1f953e7f99097e745fc48830ee5b5ee4e6a342da66b4dd560bc9eea2feda5bb0061e9295d93f21a8ad74aba06c5623cbeea270f4d1e29de8e6ccee536d48bf7456472e1f52df2f8ce3cbe2d3cbe2cbcbe32af2fd18a182f50649ae145a6be308fefcb8b736ee7d1ebf2e66d79d58b1d70b2c4d3e5bc4806ea1a799f7441b78c0cfad2cfbb417e558c3ec272187d4c15d6cad42fe32cf6a9988aa9fc2a462361d9cb5ebd9d7c51d960ec2ad7cd71e498f90ab7cb595fc4b1fc5d5fceb56ab7a6ab7bc17c7e2c10500c284868c510918c458b59918b1730def3fabbef15fbfb62d08c64cc006b1044f08a5e38efd12b7a9d04485c0c3618003d81d790499306f78ab018257077772477773f6a45e082a09bf13282d17cbe1fcf3ed0a413468c4ef717412e848a56cc865810c964442c865aac98091505b988bd80f11ecc7bc162a8dfab85958d97e3ebeb62b468d5889341674c50d61c828e808f5eeeee1e913eef7f90908a7842e5dd4260ce8e741401b743c041647fdc0e4612ec1166ba9bbb23337fb4189ff7305eb8e020b228926067b746126c0b1632a2a11542413120fbe37355a673b52a47a7f4ee6fe796cf42cf8ccbc636a0ab6d9315880c97de4db0178e76a70ee92802086e4d823e468cd165498bf1790fe3850b59646757da162c6444432b84826240f6c787c808a2ced5ba95a3f34aef29e50f9204471140500367c830a2c54042fa90bc4782f1c245d1ac050b1912d1d00aa1a0189045424272b52878210d5851b921499822766e4f70f43cc1c1e202e70b97068e0cf40638465c4bdedc89f2e68d9c3742b470b0b41babde44717d7973048c957163951b265a49dcfcc03d61bd1babdcc430ddd8193756b5b963449b3434ab0d0f68da486d6963c455874d16f7c5823756b1a16a99619365036ca0e8149645075a606c0b37ae276c511015c99a2c6fd6249961acd08d556aaaf8809a263a3716e9c62a353e6cb9a1caa0a609173bbbb10a4b8f182bbbb10a8b0931d6c58d555855f78695e586550b561445c206dd5885a5428b083bf4840a69e0d02f16e8c6aa3464ba232c8b1babd2eca0c416dd5895a64947c4c2b8b12a092a26b8b12a8e98ea03dba5fa04ab4380386108879765a41bbeb0a6e8995283b5d6c907275e80210a17272a1011a9eedc70268a17a69ea881c93f3acff95285bbbbf44ea25f4d0018b77b862cb76948e176d390e4360d19980189d113475652da4cf08274973c018e26bd7b06a6a65adecf7f5f2225c97763d51154a4c8bbd481cbdc57cc9d0be08c8dfbe3514c44736bb8f3119ee20485249c60399173fdff8905ae3ff8248aeb9f048588ebbf8463c9f5d780caf5d7b1c3f5e71102d6f5ef118313d79f042ed71f8812d79f9f8a4461024a061ea25eb8fe2c255587270db10bb0bc30d1e6c8153b57325870c2849b254eccd061f229678a8c4fac32e6e838c51e21ba2461a108180d3085f6c69b0e4dacd1f2440b95104c530921bc00c56bad9c2ed2569cf9a099da4bf57478cdbf7e90d9df9a7590597f330fe6d7df313fc87bcd3f881393135374f9014524973151ea1ff0df3c4c65aaaed42f0babfd75de80fe83c804f4e101fa2a40dfcc9ab75bbf35f078b413d3eb3bf6c29e0a9c32a814eeb724ad84684c101008200073150020180c0a0604029148301e07ab2a1f14800d637c42766234134b23418ea3208882188661180631c418628c210c292891556adb5debc62be24c8b22c941c640f71d4f5ff11289222290843657cbdbb4db4273aa100b5eedf0b70ba4f582f4990b1ccf90b1360a48ac37cd72a036030d738a0530abcad1a5be8e8a8dba8797d3ed12a17062ffd99496f3981cde2a8f782107a7e52217d76fe16432743eaf44ae77749a1bf135225b6d8e9c7defad6aff08ae3a52df0d9f7a806c93f062014a369c7d16d00d1f9ac195f2cba09c0fab9abac3459b635cae705443937e446a5fb862d8343da7cf2f1dd1444a580599fff029a29f69178066f2379a038933ad67bd4868e3d5af00f8dc7942104d5c219a6187688af9cdb49f36330bd5cc854c689a9bae5202c22834bf2c9e29f3d799d213f327a732973238d37fda664a77355351c832cd6f3333c5bac30e51d26536f3bc7cc37e7b45c11d4fa7f5dbfccbe4956b0a3015fab738ec8be84e9353f01a226e858f490e12ef57e6a3d94c381805b6a3f97b6b1aab52e7f36ebbcd9947749516157222684bab84a151364d587d8d98de0ad21a7166f29bbbbb8ac45a79c8d1a4e45eb3c6e9c034627d74c0c64d3c571693e9ceba98c6da01fbd4f2ddf0ab02bd71c856c3641760eea08702bb3508a0da6c6faa3a2cb005cb5988b86aeacd7b8da7989a99af484cb17ec96c6114b10641ccf1e9982776e6e0aa7a478a5efedd35833c48201565fa80fac66635595b8de80596fd0de48a536e27dec880ada6fdfce344df6e682055ee9b467e820eb038c99194b19eee6dac131d39ae5dd1c25e984ab03563c3e5541321905c28853392d9639de21f6ec9e7c3214a925543c1b8d10acd93c43ddeb71a511ece8cd8cb2244f44867a1acc52c0c26229b4204e4ec097b425cad3795262199b5ef2ccccff89ad3c44614dd0809fdfb80a53a4ee1a823f29e0d552a232795e0cd05f1806370a6bb8a41a40cdac02c15895d1c560d12474f35bc66cfc4a10119995517ce103e47826c7f0828006d8f728b2b7eb9a99026e24a909c0b9d5b6e6e3c46d3edb388583aca4a2080164f9faadd5ed4222b4c5509f6ddb75101a6cf33b88b441cc6b03e378935503db6a50f92bfbc6865d09f0bbd4d8a69733a088e0bad8cb47005a00c095410e799a5eaceb97c71075bb5f64f5ed0571652c55a84fe0470f808b3651845b7cdb61d133c606450d37101548e6b05cf353754715c12a6d193358d7a29e0122f339a55b3a4829288b041b2cbad5684f103ad1429290c136d824897d9f859ae3e29c489475e93231c2acb508bb877ce67e2cc433045432311f0e5ed45d2420924fd41862d2c49da2635555aad4130af413d5ad17c3b54ced0703f1a9a93887dc9fc20f117254c670898ee38f6d7421e4f7df026b686fa41417d5729925e7a23ab1f6d8ca2a72cfab97d3ab4832e6f23173b9843f86dd2bcd41c7cbfb1e53c4e76698cc51a858577092aa9783fe140fdc45385dbc30c3348bd5d9eb99d2ac9d0f65c3a5ee1ed309b41658ef1306f084bd253ee35110adb764bcba5ed919f3fb4a3ab467995849f2950b9e1768281455c37a32b2b621085cd6e207be0bd40ed00153616b2a84831610aada568e7986dd632591c7651d063743e66bfd66d7be8be392ccdfaa7847aa486b00fc92060b6fd0b224418f44dc8cca9f78a707e7cba29c9db5038777674f8ac339acd1cd8dbd054966de7f1acd01f684314a16a23001412e2247eab7ee19f27817fc9b628ebfb54a4c951f4e13ecf995c74b2af2f7031fa5d053f4ffee3f514f5731fee30a915e126bf58c5da76292fc3d149502225a03e0f4d4a00f8809362e073a30ff844eb324e4c526eb06304a1324a4f677a02c7a4c573c1033b7fc536aa3eebc5cb440cc9e498c99ffd3a9894985a0d65006e2cc8c9464107b8ab3a30315a23ce44ff0519bf08d77a0ccb87dfbf41e8947f738104c4161404fd1205a37f6a8dd1055a994c72f75ab21024f40e4c69b4ecb7bdd7a2626f9649ac3f0716600ef870774883cd1decb81e5fd3a6b5d798686fbaf9b34a46b065c1f50cc12fb0521e802f05789c2356b688d97e4e4d4a39c64d800fac5acea72acf441e287a64b64ec7ad4955940c797bb50d8a5a3d097cea6bd8c9dd0933c4ef4132cdeae6748f65725a84a22b37e1c9bb770129e926c8b9d2c5ae9ce409dd7bbb7d094eb5beaa995e64f0282fd5c551598efa7be3adea2891185ba1a06cf96ba635d5ca1b2c5d2dafe01d3d1f69687de9e5bc8af8ca0a3e8b6ce1f314c2d8e2969c34afb6132733bc179d748f817686b1b782b5cd122bf9eecf810f3a1966dfa81c266a01cfaf250b1155b336cd12fc1c2e54d004347d912a25027d23d97cae3cd1aae8d463ffa58e997c18d31193858407266e849bbd07f2daca711a05b551fab0798fbd1a08c7cf5d28451128d196445a01bd58c4cab18efa11ddc8fb7205641b3b50e47cc1494330d4be17134e5ac181291ad7202d8e73fd392bbe34a3726a3f8423f928e7550bce91b52fa0c589a1607ccf9562cb3928eda83c5cddc09024288ad38d319dde1736b9f78c0f50129c0c3c26ffa4da0de5bc1e1b599484ba378c633b4874572ec5d69077aace6bbf3651c20a28e847cae7c94987f11db164f983ce86d3140c0de027b547fe6615c41ea0cb8d289df731a2977253aec672acc38e6e6545591bd7b11cc3d17af89bb220c6eb8a7943181fc53fc48c638cf1939d7027e3593999f3496aefe0e94709642cb5c95b2643bd37d6b91d64642b0fea6adc8e75a8c8a5dc54bba11cd663238b2bbf1fb22a670e371827c52e53a7ffdba92c4445b3f570b64aced6929d45ac6e6a0e599aee13a3d1ae5e04c235364e0c9af822db2935e6708f08d4d080d5d8602240b24cbafca46254e9525d0e2360fa8ddda071318ad685b6852a8aef5470199cee0cddeec59e32cc5730ecde88bc713040b6348d18a5026d4872c753eb2d7aed6f8081944dab5782042c01eab807e2963d0282cd7fa49277b4640c25bb3bddfab9cf4e5dceb5b5907942d365606e7e6225f2c57ae023e5f556fc14161f4467c902930dd2443749836a260dfacdc2f1e68440b1905b3df3d3092d57a2ab0efd4123074a1b91f044f43cdf61d484fc14d3c6b7504442441309114520c43491122823d27216ee7eebc93e99a227e41499509364424fc82495935d74546e9dd566cbe74e0f8b8b1fcffcf889b46be343fc86c911350a72fc1c907a72403ba564c172f46dd3890e81b0db4f10d5039f950f48cc1c361cb33bbfef6f8b2f596516f0489695696334b967c2949127007aa4d48aa237904d458f3342663f310475a33fad51c7281b0e4609cedfa02dfcaaf60d0c0c7255cc81d991672ba352dc624d5b4b623fc18926a4cf3731a6dc5c50252d693eb68f17f4699dbb08fccf3ba1a2fe7a99bb1ae221decc37e45c6a7248597e8e0ae443816b2cbae2c468d84a245deef4b2a4b4f1bb1376c01e0d048216493f6a5ec169e945b4b3db581d5f31febf4b1eccd70cf4f3a076feef09446426d07c7ec47b716c95e294f2fede3fbd33a29e02e9a01ab41663b549b12929a23353c66a5cd3b18d48c15384e6946b695b7aba5ca519e3803335d67fac67f06e38643dc00fe48a111e719955c77a1c23f77361412cab58622d3f3170f6d774437b8c80851141266dfb6756f8ae338ba7e76679cf37d844cfaf7e6299ee0c85fc1fa7130214441f8f900f84c37353afea3eb10cc60984d355a42ce1a4d4f849d200ac4053584d56366360ffb537cc66ed18376aac082d0bbc8eb9b4321e1a51270eb0a3b1230f32fdaa8f93777ae74332294d3d00fa3f3264b498ae53160ee822075457f02d1c21ff8919e82b058cf290cad19ec248e5e1e64e29e56dcd25d773cdfd6f68fc6972585a5f34c194478dc1a6660860279ca207e59950c7685e0b16836fc5895f9c7265e5b53a869aa102798aeedfaaaf1c5601d1f4749b047a46ede23f20803c6b2cb7a8d26ca74de60c616b733e341c579772c3ba9317a8b7bb806f3f2a242873a24f355dbb10fca2f2436f205b06df865aa0837f7fbe8c86038e1273ab14de9778a1649e8411e558dbd60bac50a6f744ad8c3937b1627a3cba9a3c34d546058bb743f1fab4dd90169a4d527d860c1e9f240e8322055ad8627a16506d541ce33081f0e4aeca87592aedf46f717a5f6a914ed1212672a56501a98080ed19077fdc96335c3f197860045a7ec429b1fe34e8ca3794ba2cd322c827f8fe83551213a2c346bd81cd53e58c21d5a5f1507df49a9235064144476a6ca92919c832f5eb70549485271c64962166359117eb4f71db54acde7a07cd9adf612e0e4643829dd7bb267ed5d8992c50a77e03012a4b8aeaea6b948eeba400134089f93f97836ea5242e97e89356c303c15995416913db2e7ee07138b6172d9de4565ba656c926bbfa89998364be49fefbc44f127a507dbbb99b08feef92cc9d952704678975e020cc70294cf747deec21436629532c7ff21891f12b9ec90525fcb448a4724a2045d965219101701c472421ea4ff8cd222923de06229ff9fc9abd8df232fe9a031c64da654268d6a8d463f1cf51d761249d100926d8b2d2ad390cb68874b9fcfbc35fea689d180268082184b51be58441cb297b54f3ab83135a3864604c4785f959263ab9e7ee584ae8356aa351564fb2289b904caa2a54e81bd51af6c6afcaa69f34838effc6686d61acf8ea304b483814e30a76b3ffc05d7f4f7b94e3b718c00b175d7098d91540c65fa56b45160d9bc95df16acae02476c65983d2d477c48f503c803f5ccc9f0d7d573867ff45bd6a38c93a4b8da4ee03897f750c3fbe7d6cdec6f0f6c90eae915fb21b85dfadd1d0e74fc2ff09034b6fd2e39b91928ec51407cd1bff318a86988d01caca55d7c2a3b07c837f934ec22fdb1e7da5fe435a654956d928a47089bc59f610b5d254fcd645689c11e9b5f62c7e6a48b8d4447ec9b2ce016c2eeba572fb770e9f904330fd2e2f0211fb826ecc4a4be3493f4cc6740e653a2753fdb3370d63b1eaea8e81d6217799ac4e2362674a7c2cb4534eeec01af6f955ebf736acdd7b92395a7bd475137490fd21d110350e7858afab4de5b0bcc3684e09e7b6c1571890f842020955b3307086b0c82a55af8dd4a168a6e8655c70ab89898d0b9c04dbf7b4839f5070f6104fb9bfeeee2ea86fc1d22164b9b8c18bc2bb04dab64628e2d96fd7de26dde08aaddf80ce61213256275e3549a046f31062fb42a6149d09bddf77a7ccaecd89b84c21d55c32990a421d58fb8d53ac2432220af11543afac65e970a6bb8116288fe262ea514165366c73d72d6b44934e903133a20b5044052a2392d7d32c597aae5061a8bd3553ef38aef39107e104ed8bc23e1a2964c5ae0a78b92f04d4762714355a393535dc24be13e313dcf4fa2cdd85c2cae754b55a62b76b089bbb80c21e6988b627690db76cd6d2911d118701e9ec9121c2e46b32e84328d6e496a02793a0986f5469a584d954af851765924c66ac4bb6009b46239aeb415cbdec0ec0e991b085d0a31804a48403d09592e50f6160e494c37ade417da4aaf9102fa75371ed6f050beac56ac466d7c92454ff415f4e75885511f7c450d85ffa1f8183d8f1502cceb6d3e8087ea75566e79382a07e6f60d0f38b8cf03bf164a00cf8a842398fabb183588ec42299d10f4d4c2014f7959157643708cb6e0ac95f91314b9d97290a3e132bae06a434177996080a02be18e0ee9cb176ba317452463edaa40642c534d312ae77b7810aacfe427364fc7148d2085f5bbbda20bd56de65efb60d8219ac24298df7bcc8703a66dd7c54f40efa425b93d07df9b68e2979ba908a9676517f690b802ff2586d33d1b2d9b149a284953da8ca3f486375abe99ffaaa85998efa3603b2d7d325a5f5328536626d09051b2ee1e0100b213bbf20a6ac7638d3f8c597cf8e3eaebc7d650b540e8d2a9ccbcdbe03dced458294ef35a84982ae15cd9b5b83ce80ece0afe0d5aec3b9b7dbe9ad271a820fbafac43405a8d755e3573b9492042d35a247525d80fea5c54618293789f110dee15faee79834191584df5695e27a2548bc44ce998e214496296ca0b66f203a6a1b5189f38ae9f3242157ab616376dd67b7fb510115e8669fc20049f1b09cca53056fbfb61fa8b101ace99442fc4063ed7fa0593a1902604af9192120eaa62e0e45000b257d289cb3043882a11a9b725ce7f5096be25cbd1f9e685a2634d43449fd784bd281d550e9aa0d4e8a964414c0c7508b980a45146cf016512821542d505424dc0d858997705e584277cf6095c68c2810588b11c5cfd01997cc643f16dd7559e7ac433f3afef2ce9df7f41a7f89bf2f1f26a8599d821572016b0b91443c23f970a722a5c5d2b357aa97880541aa67d2c13cf970a017adea5b80b81f4206237c1071c0da2b9080093ec458d4097c48ff2a107d9ffc3e6221dd62a793c8d30b19cb0222c195755eb6d4e0f29655cc5bda192b9af8c37d50d6c1ffca742a9958cd68aebdcf7e8d6e9075ba9815e8ce2b3e9217ff4280ede3057f8b6996ed77afb52756de080c820b68cac103febb93a01b2875bfb80c04a023d39ca930b473c7ae5a1e19a35b32204679906442c60c3ccde9fbcacbf87c96a9d0616fd11adf7f453618956b52ed3d387fc040505d6255a6c7794cf8042ed77140a2741e97202e5774a84463b69f71d45ef3d9766c719d7f1f8bec4b40174623ecea164d9cabbc6a24a7807ff37086a9640ccd590a72516fa7db04faadda72b755f26ae8d34f8b1afbccfb4f074899d86b4945b257cdd36faf11d1f0a07d348c2105274d45a907ea3334fbf827a831f0af71d51efa4aed1a0789b9eb49dc8529bf99da5c4843640f0ae3ca16e716fc567dde93b8671e4197bb1a99dd3f01629ed8da81010ec21d018a8e97699b20a249d7fce70c24875a965ef2c6453953422d74e948f0b7616e2d33ba24c8eb374bdf4c67e7f16923baa276ce19bbaa9f640bab42e1ee20fb75ce897b944800b3db14bc232f628289a97bdb2799cbb484f7496b87503e70610b56870fdf4cdd65bdbf83bb358cd5139c9f4a71b89a6e022a23a8b8c29ccfd07ac9515a22f244b11b114a5aa24915ce57904d25950dee3d0d5e9425ab9242b9a2534a02646376dd11e6635d5efa1608b00df4b2885b5bd46004797527e151c128399e93b0886f0d5a6725f1ebca0fbc6b15e5264f6b77cd9dde4157be80dabc67636c9250f091a9da67d687bb9f89d2e97a6558f6aa25f25a2452ef26fde69839853f0aa42c281cfd4b48cb962a871ca50bf307b911322097ed1c721d79af1dde4be8b90c46805c467e776c63390cad732fb78a3c054704ba71e82d3a29b66ff04d21cd09c960f29c74d5f8ad0eea129708332548b2be3f41b8e106b47085cc383068fa5002d20814d706f6880f78810e7ae4243023830249303bceb18f4cf31ac2182b45d2ec6a592a982dd32f70b8723c23953589db1b97a08d0159e96d576722c118e517830ff91bbd311e7a747e828942116abd33a6668139700a639064ff06d16d97527217fabccbf0997a205bfcb22550f02162068bf8c5d6d057f642bd83a2898d5089fe89a288495dfe8423c2dc4b449fe4931f01691f340a69e30b5ce67e3be422be959db137f6dc09140f7dd92425274f8ce02dcd57027f7f8d1c11414842dc16bb8f25db12fa57aa2e78cdb14ce3c87ec2badeb084670dfa0b76de64833892973e5949de787a74acc1fa17b75da3c5483422a1f21e455fa460b4ae18a89a16f1d428700e4b0c09076b6968eeedad5d4ba26511e029314ca8c510878f76d3dfd62003d652a128e99196534dc80f6c176d1134e1e5e5b34cdb0111823b15dfff1729659513d650f9b4b4ed9937f23b9039f98451a8a7252a360d4ac9c9a6bc0a8d1530c0ca664f51448e6ed85b274122d50fb1a05792109e226120340cfe82b05b5696d63156010134981d21115f00378522a4c14817628608d6bf6d6e993ad24fd334a589ce1b1f0f2ebeae8d5c9922813100eb2d66715899caa2e6599e4122333c5256aaa0c519e9c227bb62d915fa04873afce45b8799e7791ed93a682ef132782c9c5dbc2d4c32ec7cfc2dab044a4ca0a1bcb69238a1dee661afb64e7e5d5aa5017d6d217176f2e3f6fd0ac50d0947b2091732dc1cb9762d7ab28f189bc37ae49ad6bc778cb0a9ccc16b174d5c18eaed6e16f1e8917d38070ccfc0af711a8a55852cdbdd4a6379912ca0df795009d81decacb4d01e9358b5dd412d57c329df0bc77418f89f7b257d52ad247099c89fd09d64bd2eb76802750b1ad710059cf19dfbde54db30131543a3a25ab57062863e85b33339dfbca1d47cd6eff1e3321f945c310ed412917ff7aa72a4dfff3a831552e9a4a99c56dd0d1054ed74c214759043be8192f3b7cac0cab410017264020db2b468e08092a99fa87cb1f843b1bc5bd736a1cf0bee8e55d8f92a68cb93b947f02dc62523d63a28a116476de9c22703dac51dccfe79258dc8c983ee112af373a9b5c83c1ead6f1aa727fd0c0005fa79f606a71bc9a1bd2ba15f35a33496471dbb2b31c5f8d9edab9288a5e61aa4c94c048b1cd6b217d1e44466ba58753b1e6279cfa2006c0467229b2d8a755bd4c55603c9afa208460e0f381637dbc0dad188f36b0f4e08fa82a3ffd36dbf014cf89dfe1964319660fd96e4e277b0ae3174694ee4b983350c992eb47aee363fcf1d80797e6e6f8a025795a2228d19fd2b7b56fc3e878cd888de5f9c2a834297f0459e1545e8601ae5be4a2cd942bb97fa41d8553eaac3e21e4b5903bc162123567c38ba0405d1b5631cfbb30b9d829f923c2c4bb1681eb25a3caeca1664ede3149256007a73f14352b8d20863b25b8b45bbc9777200095a533ef2ace720922e056a6f2204bbddba45162c4f3a2fc7e54283aeb43f7781d9ea93cb6b8e27dd9ca3865428c2fff1b02950665bdb78c85b0065322cbf4c5f9ad1326c0acaf6f842e48891cb29f1c86aba54db9fc8e56cccecc819d74fbddf428d0614dd02612aacc2c631c880efcced88040f857ef515e0150da0f6d86113f4c67965477b0ef8aa3d7451a41c931cba40a737e2ef2cd82d407ced10fe82ee3c8494da0af352b9e63432195294f0240bec14b90a754c4504345ed34ed50942e6fb0d8a8b8401956a5f72910ca223384ad87ba011f485c7ca7d6d92e43f1c5eba3e196a692a3b5ca0796ae4f2f2a16dd94a5049008b75e7e22d2424d9d937de22b285dfeabd4da8f13741331e2c0b763e5926ac480888756bb3e0170e24fc3a56bd7ae933d2fddb0353897d17574c47113ccb036cb39d7f9e022aaeb383f27e2e39a346e07b246ee20c69abfcefe9c4d79e1132c7331e0a5fca2e29dacc898436588ef684e3af6259cc7082940c5f3cb999a7d19ac92b44db7f68a8ff394bc4f1d1f0f58890ce6f0c1032da8f915b570d3e61e9264ae57a4a26f517b15206940d8a8f69ce832d3c7078590de8e2bd00a0482b1fa6bf317985f4d31cd1ead4ed74c19c93db21ae36ffa6b3b44b14b02ff1da582472f5b508ca2c7c6f584e117aad2bb4ede136842a48a5e8bb7873d0b7db413ec43a9b5b5545c6e7fe987bf2c4d438d3aa02d5bb8ebad141f34e6e6f33bff7c307088c9de4ca2431d70efe3635b3f51d32018c9cb0c1b32daf8ab465a3e3a225d1acd5989836d5a8feb75c7efbc157ab44d5c38a75e1e10cf4a40b12af1a07830d4438a593aca557013c0edb07cce8978b09cadd0aaa70490d59db3475f558e7c42a2e496d6c2ad0f109fdd4d27c30dc56153965fac17414ca7d41961acc2b09dde8aca95e9b682bf87783ad36cda2353729692364a2f7db996e908a1baecbb1025c96b37182ab14d4d2f7ca61eaa39611b82fc57c60bfc55b7d97d413257a4767c77119e93c8636c578558b177cb9eb0d49bdfc7e675f4475fa593ca0f7cab334870a5b89c0ee87c088dff1b8b9e271d0d6ef5dd74af3381c8af635fc4fa550dbbebb45d9e5a7216a86f3a33652c3ae295129acfd691a3492e7df8cf3051baa1639ee8448b52d14dee56209a0d6a4be9e9d673def76702b27f06486a7c4efabdd77007d876d94f91a21a9f4893c9c835c9f22b65356191e5dd461853f52383c1608e56f490af2677a0d339f06dd88227199e80ac84fdf51e82b1aa09d483c134a3316bc7b9b4d10e64b473679a6344e41acec55a4e94e1baffea5663de9f1d0bf8031735294d6d1f72b517720da7248ad11f0d8cfc1ae4cd2043953efec2768bebc60f129c83a73c5f6af60302b97f47e413f8e9ed0eb950f087a72077cc6cf27a7d6b0682f99a9ba8f51bf68f3353e849b74f922428520770140f58d9879b0fdecc5aacc36f0a140f223807e5f323f1f91b75a913cf2c23258fca230af1bcbeface82c88fc29d3629504c5e639dbde4ac3883d9cd52a847ffdb5f59e15806825872f2a998c51367f8033af2733b6f53e022ad37f57a3781468f2dfbfa6a90480e6ef734d5741002dfef752d04f5003b0e5ea31a15ba19a358f52f715d38b2d57dd379f2bdcd68d4b31f651ed571f0cd764925982bd7417435c7aec98f6834c9d2cd486b8baebcf90ad0a6a96a253ee76eb8442cf0c4d02f834576d177f2dfa1c962fb26be29ae5e0f4878b4db490182a50d6e00aed8b2589634ff4a832040d439a0874cab47c8e078fa4781033f565c7513024c6485c98843378ff194fb1d4d07f47525887479a234241ef2975f20bd1b6007d720b3b9aa858d620a642046035800cf7461faae720e1904526e9c9e8a20bb54ed0d66a53f2859e3d2797fdc7fc2e672fb75358e54a5819b3315681d98c9c93a0de7a7821846dde0df524cd088b3714e6e5f9182573601df9ce79c2fd5cd0549df8478e4f58819636fbba03d98264f2425faeb78ec4fcee8d17d4af8ad5b08bf818d1e2818b01f911798f1a5cfe7d486c4b5cd3bd8758ef3bb46e4e30fd275ef093c289ce7d044b49bf154fe0a015d43447a75c9844777dcbc7a33233da6093ebc2ac2d06ad5573b5d904a29a1912c2b2526e5f0cb0cfd81a70d4383f63e278b0a0de78c79958f082ec58ea41a3e1782cd90683bbb960c2b067bf70e879dc760bf904188a206b1aeb1b885e4aed0aac3b8f4d51f0e3d2163bbed2fa285d546c2a2afc1ac1c7988ed81eb20c25cc6be5ac1a4f4bba10d4bbe86fbca268de52cf968dfba9b6aa5dace23ed3eed60cade533251de12fb33348e8c82b50697b4540baf6b6521d9f3c4f7e1bd68b3e9700e2be49589f597a2486800c8ba86d51fc72429f652f221fb68fbcd49975e85172b1d4558014b60fe32e32247c9f5a1da5824aa2bc7b00a54270dbff28b6925ca497e5aae4aef173d4585eeee7ba8c9204ef4394e9c3fda724eebfbff02df1bb5f090fb98246f8191659732ea9e00c103152b8ed7d568937e2e80489481e7884b6efbc085a80620b4d0e5d47a8e50730c2fd9ab6a30eba4b5fa54b9828e1c82c2d7b947b022c79f87706fea9f351af58a19ccfcb51318723cf5fa0c4a209e25e8ba81c26365dab0e5e2c31002d2566007ed18f5f43b5232f23a7cf28345843defee56509ac4348e92aee8040d9d5160bf3b5d84ce6884147cb25b86c6b3f9643cb3d986a7f6872edf912258f1f72535f90cb5e876cbfe963f095424fde4622515b3a1b4bda62d0ade043ee76a97edb44c802f284ad86b98f286010d030cdd6c2cf37024e7a1821dec037e2cf33a9b423f8ae1e73700fe9f024e267a25d468600e5159b90a08c4df9847899ba208d9347ee29f4f5869733c24acdc0da25155df253a86cd39e8781e4e914f0cebb4d71a72b768a84f23870df3595a3a24bb650d1bdf0fb2e0e502ec9b1e80645886f3e8e3d16478bca866fcf1d2ed25a472f3a3f351e211014042450adb6f70269762b3f8812c5d5bd88180cd1a7d2c0584afaa0e666ee17ba1120aa4876262b78bda2d9c7c473f1809129506f406da1b3d1fbffddb1df9353844a4f67f35be96d74899d0f130dbb74cae761f9f794efa388b31afab185d12343c5b3a8b2c298700af25aa449aae5bc0cecdd0a5b889072e81fd59bbd4de37569b43dc62573ece4d97d756fc8b155fe5b477bf6f0e8975c9682798141b68af5412cc509b73dae4ee3d07541dfb8864e5b50eac3f437505e51547a58eb0beec11029ee565cf0000a624ada5f6122d3c3347c07da9ca1e708e881aa2b1f7a9be77a45181d6d5141f03e6e780ac3ea8b0bf1fc32bc5f5d7b3be6c1a66c402bc690082168067b422bdaf9b8040ca205dd65a5a387d351aa88675a05912245cb51a0e204ab280b853bbace330d2fc967099be0fce8e15162feb3cb61057c0703244431f9e4ffbce0ab63cb4919fcf614b227f81017999cf90ee660b2b85ca6c05099c2d35ceac26a3067e0e2d7fa601c801fde6653979902a53b01f7618206f9b72d035176755d575b811ae6f461af08a2cc13bed3aa1ae280a253b707da9decbac24041c95503f3681fd1bcb8a808053a0ad483b6f9ecf50a39e59ecf6120afdee58cdb880aa3468acac968227051a8ae6a867b0a3f05b41f1f343d57d01dd00f17b973cab4104e1257b3dce857a7a61352558b1d723554904607f6f111fc84b7c1215a1d59f42c56914d3576443908d7db98f4fac25ae3c32160f4de4dcd87c17908fe1b22c692b3bab8128d4f6f32b63ff630646151f402f4f5e837a77de939bae6f55b511e1e2e919cd308eb96c0bae738ea9ff9dec5f3cf759367287ea2e03f79befe62a9c8fde9c8a8ae0c81176a35f5ff036a46beff6201deabe345d5031b162af52efc4f0ff91c309a336e0b3c302a9808233e68212f9acb140a48b2c2f73a7571788595f32a94f3fb570c6f7eddf9381647fdbb79da362c831e55dbaac3b1b1fb111847067a2686a282ac66dad1ee5159ca2697b01df76618b8f25fccf758e0e3aa03503dac3ceafaa20451942b3b81b86e72bc1092ab856628ba6aa85e8cb453329a4eea2360c4bdfcd4f9f4c72ecfaefabfe3f6b598f8694f876316fa5da704c604566f80d8f9d89997c65c4319df205a9ac5567f1f4e92a6b8c44c7d4f41c53f4bfb7214148b0a2ae449f630b9872da677880e8bbde9591f67c2b1cec404f2c4a7926e90bcad181409fd16d539da1e5fa4c2d94299f5aae18c559cc5872c557a5470d54f56f556e397a4570c9f6af2edad9563eab27cf19583b6fd1333f3e76b011514bfa87c456b52dadda28e185fe0d792a4c7ee871bafd23f9d24db3c998b27f6e10b83aa8dbb8a3bf945edfbf49f9d0f49d6f75a131f7ef891194c3abb56d9bb0c6c3d8ce72340b341a69a0f76cb111453f7b0115aece97073a785aee3f93127b4e8a404ba6c8dc4e393c72ec908556e046e2c8629a9cb3a0f95cf23abebdd1cc37043c5b873b7b7703ba22164a1641a94ce75d754f5a0f16f6191a85397244468f795674f8b152dbed2b5ebe857de571e2969d194b527f9208c545319025ae348c0096e4fa92e3679dbd2f98dc3c288c4fbbff49cc9aa5c9d5647c49b245ef3158784341c0c479425128ca359684d64d10b86c01c8cc10d1b852271f5dd190996f9d1215c437aad1f28b1b65f244b969a50b8f1e23c088d64eac2bf0b05db80e7cd2d442a560fdd1d0297a267d0e5e6bd71f4b5470f41abfc3716fbaa5a7adb410efdee85d01f3c4e673bd40b8930f0f3032cf40d32e07613e9114c669bbe5b6d726a92c9b219e4d68f65cd727ce8966f2bcb486dd0eef43e4e6ba617ddfcad1c4d759858c97005733fb9a087392ef557fbe07e6e210000402c24a3fd933f26db61d6ce54b7491c848dc0988c98839607b60326e6fc6a4ced0502224aeffe478c1f58ebd4c08b4b731c8b7eeab1011bdaf5004f893d715bfc008c9bac029eb2831f2f73a4f85cdacf32286d552331a07990444b6424819a017a9de6b50fc10841289fd935020d621781da124a45649256d48a3655e26d4af2c9192b638320e1453a8480d176902897318947483eb539d236a07e14fe3c567068c03071c0cc341ffbc718f40a760625ff5a1df628aec7f87694a0cb679e5def3e6d28869cb7e1eec9c32f60e3f3809ce02e74d3c838085b961aaceca8606ee556ea5a84e999fe29db213dbf4db4d6f91b94dfd155ea15400d7954b5ffe402bfbc997428c0f4c31231b55ff2adeab30060a99ea87bcfac4fbc9cdc3b0c96d2f0376606eb4baebde5bd35e49d64aecfd990a78646546f4e19d72a87b2c2187a4ccfd76526cd55a1a354bc2d4a3cc5ba8598078ff2e0354efbce1d2085c135df55b054f57ed067171bad0adfe166bced2de9e08b87c79e027c612063a4a781cf1cd7d7925c4fdc6f77ef71fadf110389078466e9ddeec9022044cfc84eb54647a2f0295ae3e5707136177d01a8177e6d22629cf59e23ad67155347c4ee6aecdbd8fb3dbb2e5b5e0ada986b4e4fc490263d42c43f219200c5900555c90525d7f21a401d9aff366c47fb2a55d51d3307f218506b00a3eb7db46e92396132be948498b4e09fea66fc129bd92433ee099666c04140eb3d3a40db0b9752da62f357cc12be51b93cb1bb4c093fa2337dea1f08908d8592c13459c35cee005e0115ee369505ab5cf15b5327b73a0aabe275e798fdef7f106be94e9178c317c1837470668e364f4cb1f860b88c9e6405b122b6d1363da6c2010fbf2ff4b0245729d1691e8b87e1b2a6150017b99c837fceebbd4f861faf9c537629232da5e354ec0a35cd975ee35f198d3b164c9b2de83a3d881665b90348a81bde33cdcdd26d911abde330822ac53e368a11c3773dfcb94c207b592b9f7c4e3d586b1373f62736c694d3fd2099910e3b8a940890600c1a48733dd9d4ed80cc1c1d9426e8eb91cf0cf20807cc3adc4dd8df264b544afeb1c6553858042659d405401a556b6738c34e5b79b01706d01f1be98c660370dbfeed5cd0fb5cfcd8666c74a9b60f40cbdf98d2de98b0d6de9b80966e45042b7e42a4de250ffb0d1800585a4cbbff76aec782779569977863ddc3f49e2627255ab20484f239b6df61f1db472a63bec75dd1c900c48950106c2d0bac55476e924a09125af4f6c7d90b67dbcd973c2400e0d457b337296e187d07befc3bbe9a29a1c92612a9365e3a47ad14966920615cdded8b9ad903fd74c0c763a58170ac0ce003287c70ddb6e7d28a819c29ed8d1e13ec45821a4d66e1d4fac2829bc3d7459f2979c2562736f32e17c8370554b172f44e3c42f1814d34443b47febcd8140e4f637c3c9fd504f899009732f8c60603122ae2116061ea1a55418c0414bfb4f0cd5f0207d1ba509136faec3985eb442235221984c46f97f2326a762ac78710701112555c8473d56a5992f4ccd36c5605f6f12ebcc72d9957acf14b4ce726e1c13382d5e9f44b6d49f6f43c5113697d2c2b867aee311bae67e6878fdca2c9f0da3a6af54fd81b570e2c3574ff17f3a48fae26831cff9570f00db95563ef3a9300d702dc651f94b2a5911f52335ca44d3e1ba5752865be2411c961b12791419c2709e7c1abf87f6a2bc70ec1836aebc8f8900584e8ac4746db68eafe5726a2c5775edadeb6ec3c7898866d2612720f45e03114183c253391d9781ed5b86dee41dbdadbf227229f1a5928b37ab9eacda66a8a0046a42238e158edd614ef00687b586eec44b87deecd0eb67d96bed51688668b88ad2bfe4bd7f44a939b2a004d79c1abc6c9609bcf20cbf740979d1fe179bdf0e791eb5ce40aff1f711832c9b4ebc55da4f4446d1e12d20eb1ab555952106c80714ff97973919128778c5e23452b1155ec34f76c1d23567ff5062e4f43eb9eb8f92076ca0e48d46885f82ab04cc385f3fd1e8034359a2f452117b011217bc3f5358b1d9afd60d70cc5bbd5e92d7b8282a1067e433c8d34d6a36664c9c4cfa199a5c9b07c2157323ea09e44b88b8231b9d7167dd0e9109f3b4b6e96a3070f0807c25e455e3c6fb89fad64cbf05267e2bb05fa67492a3a2bb811eeb18373bfcf663c8d26239458a0c438a4733facdf9fbb950440338797471dff353dcc6a1fbc4a01e8cf23f08e79c948f3562551f8d01e595a26c369b7d0c8837100eb0177ffe36b1c250e4934774d8d9d05d6210b0421c2df8619e7b6dd03b516aa43d0f0f2be0af3f54fc95a5792065778e5b3060b975150c4644d181d44640efa9d80cfbd106629f1b65a2322623084c60955691a2901ad531f9f3e221401569b518763841f0c2548cd2e225a5b00222da9611d8d2d88b7314a172b07a02d95ac0d8a4e14a35524a8ab79578a6f03d19c86e13cdba7be2ae2000f212dd0ef10f19898d124363fad30fccc5fedc03b16a17f8f9946b513ee30a1707e9abceca8bbcad511819829e79a30e7a30c4264d93aa0de914fdd2e5a0f337d7b44b238aae01d20cd8369774972b9f2d6c13ce266c93a9ba91f2e519c025f18bc47fde670e5a86936ad90d79a5d416568c093a8b89b9db1d8e15df185b46edac6c4430efe9ccf2a499bcd6ddac1c3b3475710a501795534aea9fbf81db8c81a6cbfb990f1d6492e9b9aa8415b47a3cb9693281a38acbbb65f0e3162f5e3a0687512d433bc4cbdb5c90068df7215c1cba215c84d01230669b85716d08e9444328175659512406b25d2084998f322e5f7ea02576ec90381fb74064c9a8a7d1286fc31376596d90fa46c4cc89dab555a69380b06f8168c46d923bf95f7d220fc093d683fa2688b151013aaf187a4eba4f3d4604b9b6febc7e593071a7924a721ad181caabc4e97429069a942e54809aa69b47e028828281b4f897b7992890e2955c2133a8cb4e4d2482c5d8cb58bdd058133924061716374713bdab3ad663ff3e0ef2628452fae5b0b426c4e807a019ad121e94a3e7b1d6b819a9a4326175bc0d35700d1b4fe3aca6ccdf2d509d5a69b08f1d5c8d40cdfbf5067b7b3d886e67b5bc9de6813b560a4a163b35af5257a5c1f9141378f4d64238adc1b3c7fe02df50aae9abc604aa561128096b011f7774bb231674662b6ccf04617027f2f6f75446e09095853f97260ce07665846200093f5cc6dea1b36bfd04525391e25a96308637ba28c8c4f29ecc44d8306a80dea2f3350ca85cd852b2d75149841b7ac491426c79d211791d86142e74e093f37958705db04fa2e1f47add0b1813805b2a215c4919eada6f2e828b3d9f03a88c3e0703a98de5f1dc69ac6094ef4af9f1ba67cda71922e8a8e583535acc3221a66cb1b418d36ad8764066af244f9a9a8b0add4c05ffeaa2cb884f241c9ffdedb278ed09c23eb93d629a16cdfdf83459480ac9203839ce9583361e028b0b40f78a7cb98cf1e8056af46fc3c68163ef8a77dda07905b298abe793100d0587ba02812db83c7019d1eb2460e4f7e532f7a260e923c8e5325046972229e3f291058ee965264e4ad2021c1b8bbf4596509f5c5b73bce23b58b5284be77b23735095031b4955c7f6720967ce885b857fbefdb1c1bca5b9088340bb663f9c4406ffbe3c0c34d0c406789d85a5e5c9bdc375fbc7c02cd1075b4a4c17b0b4ec85a3e737b2483b7e25dbfce8b15053928a14e9447fda796cd42ca4c2a1a38d5b99c3281a1900de02dc1d0b42c01b974f101d8fd7283f6e1338e4a0b04a3083c67278089528fa6bec91d2141f1ba5e000c40a9f1e97defaaa10091c0b998ba01ef8294243631d064d00e24b5fa921997414679ae1f87bfcbc6536275bf4995992fa4de542dbd459f2aa946d9f28c34ab6491de03388989caeac6ad0aa19453e72dfbd2caec5dcd90d77d19809684cba2038ae29cc236afd97596d79b13c8eec5477b71f922486c9b9911936abb9b2d6a831c4ca2eec168865fa1b37780054458d70c74a6dc008dfacbc013a677388204a7a1456c0e6d233cd6459a927def86db6ee24b1b3add78903a82a68d0695bb713c81a81faa34de2e462e23be3650076d990553b987cc7d99887a8e00a5d0db7dfeb397342dc5cbf56d105c6b84290efc6334dce00a1e5400dd236e40c9ae488d3a0865c0962e55931a54abf6759cbb436748e2b5c766137d0dac43b6e69144e236f001f47f1731e581b34ce9f849eefdfb79b0d0e9850673eb3bf4fd0eb080333d8559128798484e03b4df55d666c51da65b21161003c90a2ad52102c93b8efba14966a9a43a74f51787bd9f43bb16aa1396637584db90392a946ae42c2fe866ec5a0a918f09bd4122e36c0e3ac4d2af866ba9c022bd7b9d0292d92d459c53e9ad0b191a6cc8b0ea5d40238c0a2a6b1ff6ea067052e3011c1f3f2be098ae43d141248a209aa168b366ce5a58f41af04c86c45007ae4d0ba732c6b45ddd02d0896f276d72fb171e58393382b292af86585ab1f1155c32c32d74e7f1cfad631d8e8ec82553a5d615334ab97b64f35e2f9b5deab3b6cb1554571339adc8ad110059b43911247fc1023e6449620cff73d3a5c0c8c0bcac2616f872c54d981db99d70a25c66e0763cb9dd264d7da70897675d1b56c8948ce8ed420ef9a223371c01758d6ad2a74ce035d8a5e2bac3254973bf5ef12c5cdd5b0f30b39183eaf56b45fdea02a50ccffff585464b43083dde855473eb17bfc5380261ff4dd126d6618f132c4326a3a72353246c7ba89145bb2f2c8088031c32440df1136873eda8a5f563356573399d2a463042da4f48145e3b79c60dc8e09d1119dcc66dc6a361f602d61d61eeadc5aa3d9f4748610c5801f12c2c3924532efaa1de4f9043ccb89ef2003dddb062d14710fb89cf8f85062752b995feecb8f4fa55848d5b58bb7b96f28c0ff63927bfde78c7b6f7a19701fca479572fb3a81e63b2ced063f63ecb2dcfe013cbed45a09201d3a39dc1adb4f4597a169a2b251600a78fddf28c894b52517c09beb3a09231ce2b2b17630a829b36f5d2bda8e34f5bea21a23b4896a1a1b1235efd3be7f648a51b574b7e268d6cd3aee10d559688f6f6cb61d579597ba57f23816cf2aa52abc64c1dc425a8315e452c70438bc7a4534d98b5fa41a050231b5d87f549c543c8df28b00ff47bc3587c173044c41f5a010f3c43677919d72988f9745814ee5343177050c3f7a55f159f2000c41a7e26c9604f895fcd84643342255eee71855766c2a46317614c94c25a42dfa8051515b35a4cfd37010d5aa168ef02b5a3ac8c6cf8c4e5b5743e8bb9951243c100e74aa819c213acf9eeeebc3317408bf1cecdd5ff12af4bd2bcc51efe149fb531671f4cc3f415ca15f591554703cb5797ff0140424fc2b7b318192122966f2c606aba06f88f050c4a87af0d49e2d55e97206b89aa0dcbbd610971316b9979b35dcde14a0d73d0904eb02707f654804cf9e9d689709d40046b7795e8e90fac78c1757165749706a94c36088901173ed7f5adc8570d8edd89135f8d19229e7b281961ea61538e41fc981f3843b4f27ecb04a1fa1cb0408001c6605a5809974e913da1573900421b90505375de709d0c943a2e37cf7b7638a0b7f3d6de602e8e405bed211f9947a4dbad6d22ca5053e27a1e91931aabc8f84809a80b15408a5fe1515806fcc403334535869d97f37b9739deed4c399464dfb3151162bb1de8e15ce2098d1f348240361c031cfc46564606358eea5fcba21c5887eb22cfc422930b875f86b7433c4b125ceed79cf476d9cdbb16b2c362f595573bde2e70bc9b05ec447dc536fb1dc8e9e9d5b28122efd12c72c1bd397020f155dae9570cc92fa01d3ea0c6b9843740c4fee2ab4cafbbf18fd5df0636fac1944d48d75a03bd400841ec82b990512d1a636895b01a02665710aa16bfdb8fd4f5891c8806491894fda286d9c3c7a5d7956cb1d37761650fdb9762079a54f69d03497f5a76f4b57d374cc5b068f89f56b387880428cca3039998867342df589d50957c7bf0498a168ab06cd8d407b8fea5001fb648dc72da9c19117bbd37f54df0036e6e473ff262aac2ddc687fe309f1ae1f3d0b83314440f491f47fc40db7339db31b2b14e3509a5a00753866813118d05210dc14d7c19f65b1711621e83ab1f447525f686f37a15db77d9c2e93eb55eecb653b24edca375cdf7edbec90a67f7779d6456ccbbc0bdaa099c080fe538694a99dbd4a3ce5658d63b7e6f08c094f254c1d09586dee9f721e8c62ca6bd101c622563dcdc4775ce4e7b96b2e1a0e1b2ecc6870f030591a2a332fa34e324b5532b78ff3e3684c533bec8e22dc23015c0e7dc9eb1092f3c4da403925536acd08a8124dfc44cce70cf8517a3cadd4e43cdd5aa5237b361fe9ea9d796ac9264926f6b2d93fb41ac52582849ba009b7006e2baf8e08b55edee3178c44ab01a64c62f99ce07b237f6de75b98a5bfecd6b851449b22f2b130a8ddaec456df12f3ce21ce42770a3dd2218d81455dad7175c31529d8408e09290483800edd80ba99fd10485b232ab25b8abd3b82f56e5db5b6faa15e4d3e8fb665259d0b90b3e02eb08a566b2c541f4839957974e359239045a4922983fbbe5674e70795b5505002c55aefacc09ba5564307744ac3b4dbbd643089ebc0eee208d60a2c8eab6a98408674bd21279a622eea31a539632a4e6a204790860e2476bccce6cdf00269da13d97a5c03d5d003dce7672b287a5a190198ce3b197d40d5ac940c4d486aac9176ba8634e6658450b7b67eb43d27541cf94d8fd009c05d8be528e3dc4d20a23ae573255b196c30374ccac8ec096f7a0cc671daf11284b76afe148f0561656c7ac5fde4f699bd4d34059b064ea560f0987a1d575ea9cba7e95ead1a2ecb04adf137d81ef7b8225c1c303b53818a638606b25d5102f2c48988312361a6c97a74f466f1d22bc12b51070f5ce5a8c58f2838bcaf26ecc32527d1c3178b317605d289ef3aba08b5667112df9eb945f47041f6fa6e2d385abfcbe0a906355dcd59745416caa7e1c0625a72366fd2d52cbcc38b2146769196e401f98be439cb2fde793adbf0afcb5a7a999dbd72b3e54945e801d84b9fe73577e526f0a50557a036c4410ed84d935ed4290ffbb72cd069ee5da5347a2f4cd8e15f0dbbd81f18e9b4b937c83f9223baefd81836bfcc80cc0baea80e25d3399e9e403be01a354ae1fae41d0948c56068cf683b49bc77008f04ad56107bc6b0fcba0ab857317bc808fdfb8f53f6c05ea40c303cfdfa733f63b7cfa3688fb24c433f687017f30f147bcba766705a5d738431d50d3a16e5e6ab4e86e057648ebe4f083e5a01821874604dd452e669fdab6dfcde74338267b5a3f83fe4c33552d70f985c87d6d6e172aad01d8f61328a1456f3844b4623883fd5dfb7289173ecc3b7db2caff0dad5521092785e560080e005f6fd06e06bec103bda095508db417ac6ddb02b6d5d861a12a1129cda50feef7c62ccd2c68f817d5207daa9e1ee7ae33a0baccbe36aacd19902f454aaf3230691639e23b94aa430f047b14aa1796a1a0905d4fdc285107fef97eac5455dd700ef86a345361a30a8dd37ea8a9ff07e8371f542dc1ff613625cd1e66036f977d5dd74ba4d577de4d362d41ddae2fb611addf16557e999db2045bc99e45be1dab745796524ac9d58c0280e961014ca1b61af51ca97e7a5fa23774a4b5542e1b378be4389995e0640a5bae5e0c98bb4be0ea0421e34f486e7d335150e9c88d69fb65333b10c88d30f357cf66ec577077bed9731627df0e16e18731d70cdf28cf048a188ceac964ac21d101086ed720e6c770d73134a45bb56aa136e42d9e33a228f63837031f8b415431753c25c31353380e6212b04d285ffe72d3d9857794c8282bd90ffb448eac55be1f2934d0df015631890a8d80d6472f4e8ed78bd2fbdad09fd7c36f038be115b94298ed1128f9d8a005caf20e8dfca36ab62a4ca09ff178d3415a003cd753abf8a3f553894dae2cc4f418c520f33f4992c836170022b04fe21d12f9df02db8ab81dc1184411068dc6210d99311c1ba9fd75c9878b8cb4a653cb81f1f987806d6b57b423cbed19fa8c015e898571c5e12e78f1fb1a4b3e817f02b92240f8c4487247aacd3efb4a24de6014fa13f264bf461475ac3e47d2fdb5f9891dafd532803f350db67c395dabba2af1e8f36789b1ee1ecc315ff06c9e1b7f624b079715e96961b48d1d57f85fa9c6b17ee6b5dc5cbd245f14d72383ec7b5a03cc8a980243871599dcb8213802ef2a8d1be5a95a2d06586bedc3000eb59cc7d853d3b7d4304691903b520251b65a6101ad83ce51deec4e000c85277ffb4a5cb02de2a2179c3f0d061c24919f9e0964f946cdb7cb73281feeaf80e5617835491dccd24d1c51b40406c6e6ec14681f09b87853675d2c5aa9222da3b54d6ffac1317a84d9b8a0e89bb667ac77bb3cd1fde0e3826aff3a7599e63914d3623ecd044d87435a0a4ddf77be06a51a369bdd06766da9418060a8d44c0e5fd8e5a25ef1cbeddf19b79656e87151fd8f234af3b027c4e3145394b01c92bd0fa20cd2218ba0733a431119dd99d7bd3b70286eb6bc8735c8e1d371cae62a711f1e20e6c1501a2e25aeb2bbb258df5514e0e297ef4acffa5e1ff727d344448360c4440947b5196d9da9edb05490d848937aa98d8e37509be9b005727c29a55e3ebd992c32427da50808a3494268629c8e72097c065274ffadfd08b5f76e9123b7839b8e70b17d6f98f6e1771e112a552345147555435628fe7a16718015bc11cc7dd4ac8afe2c1d37f622d9ace927157ab781203559db4b7ce3cf89c94a9d11da74ae235592462a010459a17ec4c49f8aa84dffcd83517768849e133589f7d753a4d945124d3566321b856848bab3d566c5cc4ecc1d5728d8d8480da0510bf61a617c465d541f4624d1659ea44006e3da9c5e4c0d0abca140db44037e4df4ca0588c0712536376be23eacb6494dfa8b1e6ef1446290d964fa6464fc0c7636cf406145aefd18c9cff5e56839c88752bf34f92e8f26a78ae37d0e8e8e5e7aa3d5295a22e2d18b02e03226cb8fbc6f841f32becd80a2515e00202c5ddae57a215abdee549f269f186900c71382972a22d67d773f6b0af91b2e0ee098ce7dc4e07fe5311e60d8e57c28cdf50b79f2d51463a9548272bf8958bb03ad94820f8915eba02b5a49f731c3c328cfa3a0a29e29b1e75c9300cf11c4354a40d87572c3940c6c4fd1fedc47a1bd5850f10f0820e01a53035a12dc3c0cdb7e82edca8cf9f525fb87f4b3271a0c7d244ad2efbf375ae175a91401eb0b8f0d5e9125dd14d41fb4a99f4bfefee397c634fd2e664c92e9404be41a2d953c8c1520b651242579ce5703480d282831a64c03ac8a108e0f117761abb5957fb9f0d453d510924a2fc898acefa1065c74d62dd220acc4da693e81590b03f84f53d87483da5c95a28d335700358af3d1b4b2d2bbe670c6f36a3fdb03b530a3ab4124dc62705e5822bfbac53d0ff512623327193f1d584aff5625fc1293a27a3c2f7c9661aee55681ef62af0b8f1e115073d610d4cefddb9b412e631b12c6c51638238782a4aa6b415bf230b6fabe84472eadefa4ad3e5f5f37dfbdb5360357c3031d359a3d17a614cc292bba7339c1c2333622b42a2ac2eb0434233fa97ff58e434051c54eeb599c94c2ca933846e0a883e78f11e00aa52e8dc72b317f698c6efc8303c32a7230dcf6eda8cdf46a0bf82e7757bf0b9980d20796ea593e9252defef2f3ee8bb1196a67b87de9b04b3fc0ff114e699b78af64e644a169187694a476914b970a5be49bf01764962df8ede7ab60f5ae390140d157c9a844c13f170e841f362995cf8e35afe5595f9d890561d99266bffcd3c29e1656834b1675ad45a3703621fd24e5d4ab0c167df577ea98128d1d66b36bdb742918218fa93cb855ef7e8ef5e244e0cb9c6d8f7da37e44dcaf1d37def80fa5576eab971633782e42f364bbb9f9c0cc5f6c3710ab37763eeae98a78107a2df1505ae6f16cea294c508502304cb54dfddab2c94c22d251351f9915eee4af9e24f712fbf45010a59dc2c69154ad0fcf962f48f17a01971f81221b832d12fb61a3fd72435c3bdeac5a64694dccc7906be85058d6d9e6a0ed746aced691325c8c4bfa1b5dd4b757dc04613dd38121538ada05c4b4a064d0b06e3c67f00e14adefb556256a926e21c2f0de4b99670101ef226b25540c9d3cc41f17b8c435ff63e821c11e533a372375ec3e53f9a3898802f2ebde11449c568d16a5cadfb00951359bc711f37343374fdb6ae89abcb6f6f11a2ca1cf8dcb3f090c88b5813d63af18feddada2b27b7c9ec8fa5fa25e3488c5b6b148e39cbb576eecf4913bdc562892af487f42fe2c4df9a875b3e251007d29d8f41ee57be5117a0d3a2f1da1c1413b013cd6ea1eed81b3a960ef886b64be8f190663e77d0c32e7c6844f63a0dac750f76bde94d050b0f963940afaa915b173687035a32d3761e18acd31401ad39403ff7f20fe1f3c857f5f74b74928534ec00f70447d9f393f2811dd89e9d2d2838739146849289592f5a0259add664b0e8d23aee7c53ca8b202fcb2fe29d90da28812c44cccb73ff16af6599267079bd604a169facf3338c768508323fa68a11e6ceffdd843ec90afc6462ce8dcb86d8d18c8141a24964a4cf8b33a695d68dbfce1855de3385ff3b4c6376c58b692f65b7e038904d684bd44eba10e429f02aa24c5c50e5ed34a374dc20ba984dbc23307059a15f8da91c6d7016df9ea803527e9dc832116968e746ff5e0dad14b88c1f4748bb86213703f46da44bbd160b216550ed64527767da4e290513dd30aadacac36404de88f6313fcd228911fdddd04ace86937535708606be26ff7015b63a5ad77ddfd76b7a261ca5b353f0a19ed5b6934ff95a24dd15e51d493ff72346808c2d24eebf62449629e13182c148344482684764ed0256223ffaec2f3f79d6f0e06779f122a711bc5fc3aa59f11ac9f98d499f180b23e4ce2c076a0125f91e029d15329969e94f8fd8f7d83ca1715497a62c25cced053d07348edceb2fb5cdc21cd4e80a1c7423ba9ec08fd2f57ad39638a823c1e2145ed2ee3845f8836abb1b34f3a58ad720347382758918a7d1ef4b20aca11ab7b97b600ba853ea4e699012a78906870ad2042fe9af796aa8bf9c2776642ff1b6b2dd3d569e7f10c42ff8f0f784602ac8a00ea701016402051aa033013208e1899529d5051e9e044a4842352ca0982d21879a7685f809456a694371caa965584d0fffff6fe965246cdb26ddbb66d5bcbb6b5ffcb68f2074c015d0130019ad47a8b611e2f994a62c8c8b48d2b3bef43c1c7a7659ba66956317c50c10182040a2cd8b07023d336aeecbc0f051f9f96746b6d6eadb7de3abd53fdb27e78c90af8695b565e1c7697cc9e668f50c44fdef1fcc0ee9614d426da79dfc658a66d5c79c1e44c8769f18f73bce46331f4f08e0ce652729b9661e8e775a8065317c34cded16921e238284c38244688080162e3a0e9e3d3825fa2b1bcc440f98e86fcaab95f1d4df84e7aa2e72981984e0687d59575edbdb7d595756d5bbfd2da5e56e5d6f357d695ddfa6555efd62fab924834f61d8deffd799986811c063d7fcf751e96691bce0efaa66598d771fff9e775dca6655815b93704c5f16299b66daef3be1014c78b65dab6b9cefbc602ce545fab0fd6e7c6fb799986f988416ebc607847f1e33a0fcbb4cdc5f186a06f5a86791df785e01dc5cfeb38dfb40c7334f7ab23710a38e3f8126a97e2311aff4c285065e3baca3a2f2cd3aee582c94094c9f31f234478481365fdb22aecb69c50a0489aa8cbaaa8af9a49fa8926fa470351265489acf3cd37671d82d677f850594a1cef2445d0a46aa81b2a070a2e2b7aefbd43d0fa1120240a1c409040c102060d1c100f09d2e183891f806c26b8ac58528ace79eb9c75d679925af5c9a7322d927baaf13e7642ce6d816165a91253a63b8a15b7a4df8b09144927d7790f8a65da465e301d321d52044d74451cef44c165055ae4e375dc993f003804ad872269baa348d9ca89f19d29f9e0b25292ef54077534d5578d7d490c0df568a408aa9c9449a5b29d4c0d490cc90cc988c48624c7050c19aa861a62c186851c17306400d110b281034807100f5ea661dc874f83db4cb9ce43b14cdbca0b26673a4c8b7f9ce3251f8ba1877764309772d3320cf53a2edda00e11e9b84dcb30546b8deb6bffecff9edf7d739df76daef3becf73cf9fdddfddf7767777bd35196eadbb751759e79cb37e93c4658aeabdf77e32454d73bce9a35f59f6392b26a6b2d4349132d8ddc3fcb27eb8ceb32e2cd336124db7e5f2680fefc888e3c5256543b0c25c2c294a6e5a865d96d771f93343b07a0a1449771427ca7e5ec7919b96619765e2d2919827a00d6d0d417a24038a3d49d19ea4e87d923e3f3a8af493a5c931a8d8fb72bf2ebfdc5d8c63add5abbed3ae7b57777dddddddddab57afeeb56e77afb57a75afeeb57aadb556afee5e6bf5eaeeee5ebdd6ea9fe1eaafe1ea8fe1ea7f71f50771f5e770bd70f5b770adb56a7777ed9e5f3f8fceeeefee7b53f6b22acab1a5262972fffce8cabab0ce1e491365fdb2aa8edb2aefa1913451b6424bd24c53e0c718e32fa9498a20ca64ebcf39e7929aa408a24cf47e8cf363fcf831652faba24ad44587fd40ca236ae1e10ebefcc7e6c4f5518596c2d879ec4c24f28ef80c807de31b890902974fc00928c25ce759f822906a08d9b09519dcb1f988208ac60438c74b4a93c71365c5b0faf0e8c858cad442c18b1856d91999e1a0329c18305eb868b14204850900e908071501e283870e1c36846870305cb87cb5e1a42315440d516100c0bc88172d340e09cce525062c2f31909e034e88a09bcd66b389303232b2d984301d0c8bbd29eb7558767d9b5551f603a9c9436fe3bacabab04c0b7da2c0cf6a19765955c76d9ec8de94f53a2cbbbecdaa28fb81d42479e86d5c5759179669a14b13057e56cbb0cbaa3a6ef3a489023feb75dca665d865551f26e9836299b6915ce79d1f0597158bde7b6f110f4a9e30b2ce37df9c358010b4beb29438de498aa049faa058a66d24d779e747c165c5a2f7dedb01840813285400ad872269baa348d92a822a34f6b69e53f41f7d2d178cc9e827cd1ea68b634976643017cb7d14ac9c6db5d67a5bbded761b8810a864877c3b1082a201931c042000c010c00ce5d0681400061530bc448c5431180a46a330200c060b828140200c0a0280814000445110c5a12009f24c510db312aa6e3f8902d5808b9f95de29f4901079301cf2b1bb9057b766b866edd9dd5c2867a0f8c70074d3dadfa04cd38915d3a0ccd3a78c43d495bb3438cce59a76dbb3fac782dbe066bc46b7ab1a94c9eaf20a33d85f7b71a9d0d50d16b89601a7fc13b228a0aa012a6ef739d1a3c52ad7d6e3ca690c647f40388d86e46fdad6bb1aa09bfc1d6e67d60c7ee3ba01ad5222f6d3ac01c37f0f280c723428d7e67c186f2278de811f458f9862c9589d1a28936d9d9c0e518a7ae4cf3fb3a7b8013a5c56b7c35d72aec73ed944071691d1a98eafd99035e05ebe4119064a40d480034d8302240d8a503428fb8c6495c56c6421ea236bed28dbd700d02ebbc93e3a820d565c6419dc54fdb1e08042139d628382b9d3d6b553654b3b8c65d1ad9c6948b3c038de22d56079f8a7f29972a4458c4c0d7a3d908eb30236402126bf05f2f78b9ae1f777b4c370e6e0e628a407d1827f238957bb21479d716b50ae3962239ad628b2e6efd2888cb1f67c0d24aff9f9733edf5fbc5ece35381b2cbe7c98982320b241515ee494406df309246a711577bb67230b5759906c806e5708e282dbe134a206b22e4c345717a63b9f8abb1cff2eea607e01f057310301c32866084ae6c0b4b0d98effb48e365daf545de351ae6862130f06408790684f1ce7e8566c970927ca911f1fecb82dc36960bf0d0939db1eea29d9609860b44d8b9c8fd05170e5f0f3b1df42ecfa73a04e23bf74a50c0511af1df865afce80c53c3059a172f294ca2f249b5d55259a8ff623fb68a901d172f80f68f2bbae65ba15a024f78588ba640f681160090431386fefbe0544e52864f02e8195f9dba813fada4514cf7e092bae72acb085681ebaca543eff6461422592a9a7793982657fbf8d6df76f351e1e31b5f4a48f7c136e0961a6fc47cb956a143fb5f6d6e493fd4cdda3c2409253047b4f6f91b5b02705f745d271b13a203304386a3949d621a88a74d98f9660cba4646e753eba45645bc44676081e623c88bf9e5943964f5ac63a43a8547b861f031d181d1068130c570dc9c84b50647079c8f52864a514648002906bd8c7eab1c7403c3caecbd6b18e192d645d0447b83f0a52a04015180ac88e0c3c85a7c0e92b21724ee71a429e17601cf2aec9f6230bb6b120851728c4903dc73e1c72270d8789227215e1906d0e8d436eba6dc8149bd2416a508998d33f255c30dd77387e9b374ef4bb5f5a05ea04da6ae390601c770909d927478f5383022a58f91ab3135bb6b309b4d05b438a3e057dbb0a56365f3040c8e16ed6de2f89bdd5363b5cafcd937ab34d48e0a6bc9f11adb268b16ceb3a6ba96b3149399a442d2ff80ce760ace267057aa0119e182b5f1c24c752f79fcd08f78841d16c8282b67e38f2898eb5576ccdf6cce0cb6b8d5ec157ae8cf3c69af740ceb6fd00405eab73d2d456f08110ce2cdb5a9bb3b1f9a1ec3c45371ef646bb1a0e582d78372177a591cb62e98d6dc154df36eb27d0e6ad25fb353acc1b46f1ba592f8df68a296c5e415c496233fb6d317537f672166b9cb48472b9afbff9c06b29c77d60332a68951163483a4d4a68947eb6e518dc983823d9aa897445470a30488db6e1f9dd11159304b9a5d61cb5fdfc06eab41163e8359b6a3d0ab22aa02c4033388aa1fac94784d39ba13d9c47d3bd276af1614d091825d394ca0408bead4748886302da86185cfffc46cc0e917faf9a98d66fc9599d19b48dafaf7ba1c0e16250a626f7bf352705829637b133dca16a669d1cc8865f0408995a6f71c62cc08bbf4a998c29a0244867f41a478e8e3a20fc47b1b837a46f006ae557ddaa0c2742deab4d5e4d37203fa4c811b0b3077b9b3683f4f19403586c704ff3c1e6e682947a427fc835eea2c9e4734b7472edf8f07a85fbe3eacfae1daaf725617ac76d6d145803bdb88b004ec62874080632efad5f10fdf111a4106e9e48daa3d3d41af79fc94f18f2cb85c9c97cd32f89bcb27f06aa8ddc95bd6fd984a86e453097c94494e0f5e8733677aa0c1842900f0c91b8c794550cdb8d5cd2015a1e655f4bfb08370b61435f12671c14eea9d8ed858e19524095ac746f57755f6bddfbc7bf671fa631cb760bb245e98f01c99bf06ba34ea04b2ebd920bcd062c68b5071e46c65254711b952383bcdb399f5494c54e8b7a57078b2cec7d271d1982ef47813e1c50592f6a5d7221564cd6f5513218685d729548ab80ceef5478954dbc4ed6152c9962dae4c4826bcab16e8d93b3857f12b5b99712faf30d5003db14d1f6790e668420828edca1d73b88aef228ca02e39094c9f381c889430eb10bc2b63b42263f95c9063befe769f22e597cbb65f5dcbf5bdc8e2b591f7a49cbcda60ac8fa8b62c94abb5db288c421d39f999cea0747255d32e621fc0a4e9a9dff845f15ce1e9662324520cde48ca8e209d4b8e1a8a960345916120022c3c4885e580820f8db85a2fc26681b068a890844551a2fd189315df306ed13dd674f1e4b014f43eed9261601cb447c3276ccf924da2f19c197ab0e9c407260b448e0e85e1ab677cb8a5dadbc5a07708b3a11799b32cd6ade0606365c0edd46f2392136c3bf2c0d16b1b8fb4e74b008de2ffed53cc4c86a1c1f2f786118b73f8db7d3541503b4362509372d4e971b66318f3701ec9456f13e13eb2add627204257869268a10a4758ab97ce48cb3c5e9934d2713cced831876434178bda9d1e0adfef6addf02b5ed4fff6b9107e7433cde54b86d2285d81e84264447edf5fece9e45306f1044eb78f778289f609d5c8ebe1ff8befee402b28abe7200121f9f9b24e5faf44ae26f703887fd06a6008984a82eb943f96ef4c9a24ca54e567a91d897d0413affc7d4282238dc9c6da59d37190265e22694349a03e09b65ee8e18b760c26fd6d66ecfef747fa1f109af1437c38f9b6c993b4ffc019a718b86b8667f449070a3296e89dea56c96093726c56dfbdbe5ca75a4b1071df63c4464ff0b3704dc513478848c243e8c9b1036b9705ede3019cd563df38559f5654cc40d3e0aedd11012424dc2e48787674c7e7d8e356f0388116b4b37e572a3dcc645a07db596c8e80b0f88552a825f9c4c7188430711cae6f08b903a373c9f20a5c2091647e61290a46875bbd96227ebee79713b89c20a67dc22c6de0ed07afd20ea3e5b4baf501fcdb2db66344d42fb7db748053abfcb044de269b2095c340c347d97280f07f9b13fe192cc4789ed89115ac634893fee455752bd1e129a977ecaab475962649057910be11afb475a20de5c0382891719b2301de98ab996fc93117d460294bf48c0f8f1023cb4607624e086c0f789677173e1b3c97f467fcce2f267d613eb36062a1ec24d644cb02dd1b451acd1cfeab74bffde1054dea2d96e42856a2b8161793d098b986dc4525bf13edb85ee6143b0eab678adeec7658f96d01567dce8fea1f3f61d41a054537d8f800062c621091873054802314bb700d377fe23a81f4a546863752303a71bc162b6ea037ce0731db89716f01099d928e1f6770cb4bb0753b814463e80d5b6f2721d8102dbc9a37b72d3cf82d8216521c3f03a72db8a4100e41d2d4cffd0b854e66fd9e29f0277748488a0a660f8c3c533506d2b2f1de45de26f14e061d06b7c46967a48a5d47d6d4893affb90f3bb7d81f8c3e55f99be629eda0669feb4ddabaf1b498eb6893e6d6517c4dfc12a83561becffb57df5afb6984c69b800af36ac26f455c1255e4d9d6cc600b4190439c69a90bb4ffc3840e80be7c606ef98b858962f89120c202ec05131b385707cd8016248305c1958a2a2737535cd0d4cebeef99692c2de74f21612925df8ada72ea856056c379df33526394b069edb332b657286cbc65dfbbdd181d424d10651db20f2b2839f251a5e33ad2a3312ddcfbc8e0f21e6e6337f8ec7d6358e64586d458413800b8710d2d9d681b2ac8e4ecf27bb40dfca89a497631a645fd7f745d9eabfc9a45fdcabdb54d135a19785d99429772b19a9c5c8bfe2bb06da45ce067d39ae03bff0dc069ad67d9831e56ae86aa1e4b2efd33063681959a6735b54dbdf4b88573419abb401dbc96f514ace83ce7066b7a9a3387ca0f353fc6c6b4a73741b1c1fcd9e963ae806c8bd3f45452634f4b3a84f0eecfcd26c16bfde4850cbc8e13174bde15999fe88f71bdc3d016ad3302c55801655e722f3139c5f5f03860cee15f4aada13acd33a67c5f180a0efc470ea57c3fb835e5532ea8abf658236e8c6adf586cc498b43f3eb0b0c5cf7dfe1bfe33a068b4d885442d71c06aef5a85b5c5a5ab2a5945206c902b402b502674834fabd47a2d0d9497cdfe09c5342624acf9068442a8da0aaaaaa2524a6f40c8946a4d208aaa6699aa22321d1b99f84b4494ab6a9a33cf548a59133241a796937ed12923d1285ce911289a3bb84649bd0911269240a9d3b5fb59dcdda59dba66a4de747ee8432395153a954aa8569a0c6096572a2a6545555914c911265b249d4b4c54be57aca53572794097f1254eea4a893cf8f66e56f5b5b6b0a55557748749248a57d8644a3912874ee126997482351e8dca7ea549daa7acea8aabaf7de1b8742d56eb5daad76bbf1fe3b980a7e58ebca255b652f43cd910aa5a7699afe0ea6821f994c265b57b216476d4685c2ea9ca7bd76abd56eb5dbeda673c3e5f0d8e47674bb1e1fce3bef9c75d3b9e172786c3d36b91dddcec756abb9e9dc70393c36b91dddaec7a736d39e50b59a76c1b75bad5683a9408693c9522ac914294fa8d8b374b05ac7b40b8b2b84c72cb7949420fc8da89349f59fea1b0bed6028ae48a511f3d733246a4d5237b57fbfa537afdffa6ea1329d50a0121296159556cabae2ac190f9a8cce8e8e67d7937d7290fc93af88ddc872363e6e35369a76e69cedd011f3817d507e6e763a9589feead66fb7dbed26bb61d9ed865373eb8a533dd57d9e7b9ffb3c55144ad5f1e468359c8f9b09ea5affe327bfb6d4542a95d2d52841644cc74c6c38646a386670b434ab9a5255f584fa586ca53aa152f88bcad582a4e4047532e95465a198a8a8fa0bd37a4ff5d7eaf556dd0fc0dac2e54261a529243a3bba1f9c89b6d5ac2f3af9dbaf85f3faa24356dbb65ab7ba6dd7b5e3b66d5bad5bddb636556ed952b376366b676d9ba6743c3a2f05d3b00caee11b0a471b601680ac65aaa78fffaa9552a9542a5d0d096e88a09cb072b43968d8f55f396c3e3e3ead94942a356b67b376d6b6a9544a7bd3da7cfca8edd4e072723a58579bcdb437adcdc78f1a5c4e4e6707eb663c68b905853299b5333d9bb95697cb444742a2938432e901a6898e797c85585e735ef3bf744cc7308d46d3311d9b9d728907aadabb0a55a1298aa6b91269240a9d2b2bfba82854aa76dc8b8c1c1fa12dd6eac2f2af5ff9d7ea72a152c12fb90491a3188222acd58565052545a5a5a6694abdbb345553356df194b0ceb906ee8ec70d12db31a3c9d4722eb76c7c42e1dc8b0c96157e42ad7805dad6efae15d2a269676e3c0033d264720dd75e77a772773dd965c53fbb4595e5163014599867c4b4c92e2b6768f426cf82e2dd9debee52ee4edd2abf52994e287ed22a21d9292e14961556ba665cbec939b21d331e3499da4c4b63c33fb7d80d092ad0b616c73599e50367935939b91f3a3acc93d71deec141f04cb36aba5551fd0ae6ee6060724ebbc0e45ccee51ccb8972acb1d1e0f6eadc9dabbb3ce61912a146a4d2887762d2552a95aabc91e306be1430830603bb101196b9aa2b144fa5eebdf1c65aefbdf1c65af37fc95a6b6d2a474a67c8d4e863feb178f4987b576f16873cc620acb56d879c73ce39e79cf3cd5ece419c6fd6ea2a41e4e889ab16eb08efe90786ac11a9142292629e2111aaa5b2c212825fda5725a693e8b95856545a292cbebfc727c8cae2b142f150ae06bfd5eaedd56ae5839df501181f924de2a35957e4b5a71dd551bda72524227ce7472e3750c35b4d56225ec8b7ae5834edc2f22ef546e566e56614c32f45d9aa138a04ff0454869088e8f0c8323b5005d51404d31e9c9ef6de7bcad795aaa59e4620f3e5729162a5aaaaaa3ce7952acf7c0a2ec55ad3ded5b4f7b4a77d3da15c3eb1580a0a0f112957aa353457484a4e1e7532a558366beda9a75a99a4a8ad97b25852bfa2d24a59a9faca1feb5e726c7c9cfc68db56eb56b76ddbb6add6ad6e5b5b0b0512b4c4a48a46a4d2489aa629095a62524523526924358d94f6de7be768b519b5b5d5a8aaaaa66e5bf548486aa9cd4db372dbda52b9c5a3aa21d149dabf3727f5af412f8459c0620fe31ea027c7f07bc89e80b247d9cd11e400cc04e5356bf03f1b22b84dbb87e16b6f1741c68461f83bec36bc0fd9adbd1739049ae5dfbfbc0e0327d0630e6972b8c13904c9410707071df68586f8861f36dfb063f39f74a9575804fa94d7d6e9bd0b9ef7bdff177a3137795b87bde5eebb08f6963ce6be049a35e07618c8dad80a741bfa43f6776e899140b3f7f5fdfd3bc6b6a17fff3de5b564001913fcafbf8dfda7bc6634afff3df8fd6decbf617e71732c46c2400540d78773f109e6c5c14207bb5f7c09342b9a30c4dfafc7382accb0fbc30fc3ef4311fc8e59f17119e3b5bb092500ec305f201695808b0074efa6696213e37421b86006847f00d677abb96514df05f4a07dc1be2bf921fe519730b628767555d777691faa6e86227eec00040f3650e1c30a51d832a021c109201c7c40664c0019826c421f81fa40c4133d7648d800420f202c8074004203ba7d708142282630c25e9c144688e1fb91c2ecf321050df6e7ed9c933183060364c95e9cd60a5467c40b6f8ba0ef511899c5fd86e8d7cd9bd7ec85f90529403b1237d1c3cb0d71479039628e16860e7cb4a0c40f1b00a941091d3246f048220b118cb8953a18a183b74488715a1e5893bd38ad0c419462587515e4555fde8ccd4fde3b3ef997d47fef385e0dfe2541321cc71e8ee3a8c7518fe1f609510c760fa68ddb87e633d27a720ba6691ef60562c78fe596b183fd958345a150202e2177354d0f9ddea1697a46b7daa66b50a006c7b1ef38c5f2daa130f18f34195c1aa045a1bf4f4367a6cc06e3eee31bae29e10b1247fe517316c02e9b248000e8fe3df96b1a3f64c7dfdc349222be75f6ced6e5b64d5e376eefdbaed9b64db3db3db36b3a485e35ed67a88cddb19bbc661d327dd32c8dd33959efc8acfc3fc21d1e5c4fcd8ce66750999de6f18519b36608f7d812249e84e803397c146533d67145182bc6786756c332bb3f30311e8710c17135fe7d2fc6de55c1007460e2cfa9c96d17afb595f3828ecd3f3a62c5c17a9fe77dfc0a00a30007fbe98781cfaf0a7ecf0591c6f642441a5b7cf01bc82e2fba6c01037085d4b8710520ffcec82e1701004030f723f0a3089ea0f22c1f5496e55972d35996270882231979dde58b1979dd2ffeba3defb7b7bded79def63c10b8c12f79cc66c7f1bd171f2496e5389631e3383e3882decbc82d21bf7f068d1d228688e54fa0d9b2bfbc0e962d3c305f34ca458ed9918779cd62df79755d748709f60934fb791cfdc0dc21181404b9800d08346b8aa141501c4131bcf741703f49bef6e63a6798052cb8410ed32cd8637c322447bc31f8fbbfbf9f07de7d4720f73e0604c1d7a0cefb3fbe3b2cff3681bc7fbb09c67df05fdcbe6f89efddb87c21befe0b43c459b0b8c47fc30c6231fe6fbfc85b201fcc97c95ed63dbdde25040886666e11bbf8e493222618e28f1fb359fc3121fb75130cb17b1f9359f1c51e03f7fd217f4b9801a34814128542239833984110148542a2d09fa407821ee8795efe46a4d17df145fe7240bcaf2d7edff7f5d7f8a0171f6601fbb1e2c7619afd46bc07e9c93b967a944e7965490ec04e25a8a3e1cbfb4d1e8059f27f05d36b82f576acf924794bd0e52528f4a217118542a233f4217b9a2152448a4851c8fcd34445423848fcf285f8e4c761ad757d6176fc8f9f4e79c530f5aa7c7f717e72d88fc3ee5519fffb1274125f66c8923d66b32347c1f37bf13df21b3f6e1a45f1bfeffb185114c50fc910ecd9b501999fccfcc56563eeddf96120bb3f87c631a4f731c4f73eb39825bdbdefbed7031feb7f85e10772d387c15dc493f8c31ff8a1d61368165c60bf47621284c7f740d0f3c28d6ba8b4eab7a7920d0100026318000083300c436110888124ccd3b90714000b4d746c5c50521e8cc522712814478118c4300c043108c44008c34088c290630a526703c3054de9bab1597530f89fefd3a2ac14867a370f3e88b3ca825664022450a2fc2f279a5b6a461ca33ca9243bc088e29492a50265356ed550052e71425ac23cff828777eeef7ee8a55a4dd9cedeb5c96a26cd01d34393dc78a5b8732f7553b257713fe1884f8ec5ca862b0b71eb16acc1847c1789e696fcabeed5b5f7496c5bc9a6cdb5b4f994abe4c99930db9accab106a8fac4d7af1ffd72cf09d30f155fe2568544c68a3dd1e15935fba29edc4c26edb46c999b07d601e0560d3debc713089521f485673aa27958f1568c2420bad1e314025e93638db19992e9f00a56d1285a39c9046825c18c68a8b42a56256c93eba0a5a300595c98b4260bc0a1694edd466f67b7f43c69f89698000a93bf5eb60b3716228935401f24bb0a5b9dec89b70cce29dfa1d56dd3c13b89c77b256cda4f0055b70d84dbff1a0f78cb81d84e07e8bfe6fd19ee64d4d3a1d4e57fa5fbf9860029a01350ef5223ae5270d8ca71d3862af1f37bf61d6961f83d81d7a79371fccdd3901cf6431c5a50035007f0158130237d4c88117809b8558d4eb828f1202c64224b2971868c0ce1dbd885e9b39587b82db93e8217560603493d9cc008298805d8fc37e6e09a6001f2e0765c59eb7d82967119fc32dceec982e4e13d4418715932be67471bc6a71117871c59d320644513ddd8fa618dd18b0a09ea295b40c0808f48cb75dcb517626266a94dc0055ceb7e83716e93c85d5abc426a7c5b165d532ee76ad4c501a3cab3bbf9d2b79bbc5697d28ce770b702ceeb2e11428a3de4e25d842a57b70cfe5b1fe25b67fe99c0b6b47f75622bff92ad486aa3b7d93d0e3c83ffc321a9088a5f96fd238d22f5a38b7852a5d26826cb9d55b80f514033fceb9989afa05fea68e49a6d471e1f4162b877a73097257dd28b19675d44b72670c6ae0b540ef833151698a9512b011d69127552fd973202c8cfd20ca38879eaca4e5be22976f9f4b2e277eae6146734c8bd4b5ca167ee8940584a2855f9768fac676d1c2f5bf603bc8d62746f3589d463a57b2ad5bae880f69c3f3514e6f5a98b634c982eb5bd0fc699cbfefba4728626f8eaf169290ce75a43f1a77a916128be555223fb5ed9a2a95126dafa63112c375d8a8d24bc25bbba4e5391c59d989278be30f908651a3c1ad6359a2705ac30d6bbc4167360d331e20b185b186568c2750a5f6b823bc1818585091667294947893c42880a1485da16392cec3d40c382abe4151d4fb60fae597ba4a2d39d2b0accfed2bfe0f8938e2e928447b745f3a8c87b6abdb699d82bf14f5d9ad3377fd72f87a5ff9385e32db442a38a9941cecc4c7af02defff8a45d780cf33755e03f6c80438a7f858f35ec48beea7a775a060b51a5574e7aef4bae059549f57b4164e6b158355f855f9bf5c2c25ca19809836d2ffd7c723823333e8952b147221f872c8c5e8d9340a64bf5706fba674b33ffd51b65ab5e42d2ad596c151301621846c1c49791b3546a5c25ec149e754a490501b33299ab634cbe29cf8daa23ad9c14b4e5547f2fed8f029a8709408fe8b3e7e8e44a0d63c94e4c42342df42161a5a44cec190273c35951c2674e4a74847a0f7b73a21ebf37459ae10b54960d081d6d8d77b10743089b60bd7fb0efd6b4f04aeb6d107327922fde8f45f74462ff4e621c5ab496a8fcc970b8fc2181bdd3057d7f1476023ed503baf08fb6a85e977623fc65f9bd91036061d98dfd0a482a2da641253dd1e8f4c8116c440db7217f2536e2a912ed45710e0bf18728d4fb126306d6a0a3004ab61d1f1139c837eed0a276b74744a18fa69876a8c327fe23d13e4531f6a21cfe5478977ab13893f7b9521f484002d5dd3755b38b1f688530ab5edd4ffa2c2d11045880119b8a1c2a35877f0709a0f64e02e4861190db1b9a8eca377ac7f2112899ed8259246ad8fabc8175c789d1a6f09d0e25945485604991cd6e46c361c1bfc6464403f2deff2eca606e297d7412ca083a97b4b776eb3f8501bc952606db46f9ec29b5e91041485a77103c99d1b669ea34ac75656d22e275402da1e9d0dd7721c0f15dd235f41ef1cb2c84cd8a0deb01ca034fe99e32c6ba5a0e76d6b0587a260910d1e5118b2d39a5f78b75a3acd60b009343f2a6d6487e33717ee8bc73b9dfc38cc24a7889b815b11c1cf35f95f798945da20ba5af58d1dd49d9414e6763797cc78e36d1ade6a484521aaef9ad680d9f5f25e2e77d58464a315161ed3ad19c12bf31e31c75ad999c5004c759e182d55d145333cf264e8c5c8c24d9c2cb69d887bae45ad81e54a2e3f0ca80c99dbd1a4472652da3ddd71f58c2ca14fa4149df079f751a78cdd141bc4ecd0a6dd14615d8f8188a66d5c1f33e691dac2aae0beaf513179606d43db0377cbaac182b681fca1e344ee10a5d87665ba1b8b15161cc7ac2fe05b4c547fb69438b40efa404a9461f08a3a1c8286e29aad96ffd26e43c904aa16c036a347784a255838add233e0d9b267c31f3c0ba24db8333ca524b463f93efac48038fc3a2bc3678bc6f805a7bd042b8e0518dba7fe9e3b358532402e3cfce231c9454d834c53a8b12e28934268f8859d78d040a9c81471279819f308a65f0243a37784d4b5223f33e0ac324d44786c5a9fe55ca4e19aa3c17978c2af140d78fd13347591c3e5563a72e25b0be7a855d224744b2390ebd2a466b87e9515441e4784ec4f2de4698019e01cb580db4ed78c21ca25ac1940523d0c22ebfdf9c1088554756a0e7f3e481b4f457eb31829253afabb8402cf2ac0fb69cfc59782a61d81ac52e5deb9b83a6206ce3601dd3ac26e0447c40b80be9ee153c69372210b4c76476fe656b59caf2ccf9ec9d746eccee84e720aa5018a50e323417a8f695541b6324b64883485a00f1195b10093ae2163f024fbb1eb6c5580ec6b12be957dae11c4359aa50dc2d8ae92800a9a36a14e4d092f24e3a09401f58d1582ca1858232edf0f9df4c86e587d246669083e647765d4925598a2c23816a20223eeb5522c22143f44d9a2d3636710ede447e0809d3a8915a9a06afe1b1fea57a0dc9d3e31e23b88a489ef1db18f0de264c9d3a47c0aa28c1a57e9d0509c15e34ea84c88af4ab76a2ba6002b68aa641246327d700f9f6ef3e92604c84052767c28996df3949f81513391e0bd42960acd0831553a85d4a35a2d888a0b8e568e2fed1a418d57b25554828260927e00b6645d93ef581151d893a29543e9093428ac732ba9e4904d85eb57c77fce5cb8e787c9d00f12225639c80fe9345f9aa2c012a6c5947a025ec3835f9cbe3420447ae214c22d39a72a2a754cae7edcec0050d3f2d6d3dfcaa335f58674e15cbbaa86a31d62b01a892d80536af7386120125427c80cacdf9d107d0e41c707d9072da660f562c698b4b31cc8d0815a09177ba15d61102d8b017837604b6dacf58c7980c2ad5d696b4449056882834dc4f57d34cd131b7d35f3c1d81e3bde8210671babc1ea7027a082051eaeab51c9ceab2dd768546297dd552761f110851940c4b8bf318453423a6c76a31f2d9e901d473d1585229c5586acd7f6cfcc5153211c11916a707adcb3e0da2401962051767ce1eff374aeca03fcc2f139f9e43c5c03c8ceb4f706a39b2fc493f935b8dc35370c9c2f8840a9bd3c3203907e0e81d89c14b8b66c5a0a14556c8705d8c9f530d1a1eebe4de620b7495c6aab737f3a51393da28986b34dacf0a00df30a6c5f9ddb39d159a216f1ad0f7f3cf0a02e344a7ac49594c945a88203f49c759e827ab8ef34ebbb30577b801583366b28c9d5c61701b949ab731ea68fa488600d8c45106d8a422980e73b4103cc83be385d675628fd14a9f785dbc2641613faef44d0f6032681f0d056556eb0bc7579d03d91c267a5f8b0a2594a5ab8bb7957c5aaa43a48d9861657a3481264363bdce925b9242a02be2eff2287818ecae9c6e4249803addfd07050ad82bc50e9659108a230af8d6d7191cef575e585d8ea56873aae324dd7fcd575f20f39878a54530948c8eaf5009840f928db1111660f33ebd1bf22e74d2c758eb0a88114374e1d55cc33e4bfbfd093580e779168305852a1fd1722f482adeb07107522203a9438bfe6e44ff08851a4fc5962441e86940be76f906905434daa22f2d27ca533a7f9ed4d1380473a3c8861ad07181ecd358287213ae54135cb6b21b016f9870a92fe1d2170aeb70874b46069786cf6b8661dfc717ae9984c1258c461292ff1e8ba5da18fbcc3470802bb4ede77be371663df02cbb88fa6c83ab5727b33a8018b72717991d84c25d35a165b8f41a7c28351bdff5ad3a1a1112b76291dfc2234dbd23e5d9e72441cdb7308c50ba09cb075982ef263b46b827e87be210eec108faf120172fcf270e22afff66a9d044d40055882816296a8ef591722f2ee406d61d2032090d842333844d8cb0100967796dd75963fc03b765208ff7303f916c7dbe509a925484ec1dba4592fbefa6ca2fac50acdc0753bf77f22ef26019d06bb7ea03b9da7bb9a0f6c2022e3cec41f8ecf7bc0114011b14ca08a881f6aefca450a6b96f26f970b5cfaf03c428197c3b3cbc7674ffd4ea5b0bb11bba85db5fcb22c6fccc5ff350e658c03a84b844fec308467b96a3ecc6f300d7dce204ac327f0e094e10e272bfebd1852afa086037382016fa7af4c0bd98101943616363f359d0912844bd9335079945141624987f002b901a4c538758ce3b24aa5a95adad8863327226e36d9c93da991fa61d915775ebbe68a54dc2825e92f52d75c9d6ecb16d5909b997d0be4933cfbddb4260a5807dfbd4827f1995ba77c51f3934ba6ed000ea57d8fdde85d0c3857c51bf59d5f8432b45bbbdba8bf496a29f938c67998a256fb752647b62a7cf2a111680a8c87bad6d8414b33f7c22e989294f4f9943c3739c80768a3abec0ea9cd146ecff2026eda608b3aaeb4fd0fef73aedae8705e349041bc59f557a5ab3d62a88a32a2ae5cce84b550af0a6107b39954151644d1b772e6942fb5b78cbac5710336490da4aed00c3c5b5b141ecd394451307d7c1b050d164cd797f0d9ab05f1ef4c94643dd8c62f747c76d1d608459b40314aa423c51beacee04e24564d1a8f0576c5cf2138b8dc9210f669ec6c6880071b0fad0b159676656a257ef04e5318fa6f0648f8d6f04c0e2bc883983908d4395ac93a8c2291e2d9c496260e3659e5c0b46720def3e18cb4ca03dd36f98251b63c8b27f4349c395b6a6e36c72af85ce743e448a8b40400df5fcb86d1864cfc92de4d613180d1716407f5cf4a6c7b830643d3f0c6d5fd8d95c04958f3459c1f404e417b6c5e53b7a40c8fd0d73ab80e8af7b10c560af052f3796394010b589cfa981cfd96dc748ae018754f5957a04333ace81efa16ce8786f403fa2c0ae670a40f834cb5576ec5f261b2ca9a212d7c0e77d06d5f939111b536f1c85142894c36bb5d0c573e4a85b7b163df5a72117908c261def8bf90f5d2b62730579107e3d6437315d23e16ae3b4cdc6786b0a80c69cc02c8a3989e622622385c5ac1b1663c37522f62801b10e03f42cfd525279d1312956c75369023a9ec1df02bfb473d1b1622dec89646782d883dab127b0f088b526c8c87718d595d50ec96a77a181cf7bc6a1bc9783185c9f8eb5de6a2aadd5b2b2d64edf54abeaab35bcad564357adfc4db52a3e17be60e69a072c83a0956e39555506062b9d9509cd8101c586adc7d15c5990151ab9317777c5c2396868a0e292bf6a5ae60a74f1da73eec08b76ecc436dcb304d1ea0d594cb82c0e73152b269ac8fec1f764b2311a00e70bd2e47b62246771858c2f50df65c6895b2d0bf3b342f8d1e5c087c235d972d92432c3e30dd2689cd0f39bf1108c433aa11dc0bb87aa930c5e563feac19b7a2ca79c8617639ed31824f309529e1ca601bb5d0a6b04122612946d93b3a45a8e5c4b9c0d5e2e4161aaf252a59d62f3df186ca5c2a5c21b3b105e3316bcec0e4a1d6e92d20b47ca65a8a47d7a43c9a50aaed1cedeb797d2d95481a0c0b78f38f0a66b1c921513e0c8da001c25a0d555516b742900387420155b21aba135995ea609985e6ed774f017fae0322f03955ebf7a28bd67c7cbd3654878250775a9a640a437c22546815b8db85495cf972d9332d526c97351953581d75213fb6ccb465a7ccfde46dd939aa39814182915334495033fe45023bf991a574f061e4cca3104beb26c52fd4ab8fdbb0047b011c5ec5d828e15cea3f09c9e0c11ef549def2a9e4a2bb266b1d572249c4993ccfecbbd587c93aacb4cb21c29d1331d9480359b9857330bb83b38675258809e6afd72fb469150e2f8f8033841471f96f2b2f7be7ecd9bb9ffc41935a58a162c33093148834214b40842554e4d30e66100b44fedf1011a79caf41dfab7a71fa0da0996dacd98f39f1d68d2a02af5403556404f8e79403104244a0fea021b0de901aa59e1c4e2cdc1a9f13eba816adce431a8f0106480838048e47087e6eb5a98b65a405595a671b479c2576e40cce05645cc1e12f25c300c03a5e785cd25c3bcd0d0351c2ac32ddc6da8000db3a8e533bbed952a5e56b32f8019cce3c24b86911b64a38d32914a9391ba543e82b3b40c3c4913e8f255ea08ba246cf3c8f319134eb08fc9ea87d91ccc418fd255296d90d27c84bde9424c2b52da2f24e8327ee699765e30757c36cf5de005157af00bea3bc1de97e990ec4a636552b06e1fd96a91f94aab725dad00f6a6634997feb08b652b5f9a320f291e8deed1e5348972b698d8ec12a12235b1678b292307aaf7bcf0fea017bca03df389db32a6225d9ed0f79ac43751d2dc172d2a38c29ea1eab50be3da51cc7bce5d6a9c018468477048c859e6353752f287089f6b773a8a3764dbd788bf874869675186af27384d247f2696250affb450622cb2381259f30355ffe9b865f907c5771611737bf739ffafa2beebe8aafc7a093a1b8b69e4d6cdd5abd028922be1e4e489c3e7623eefbd98e02457936e5382a5a8429d1e99d6fe43d4e29bd045b6bda53e06c49e84f2a2d33cd791ef10c67ef68708ebfef48508770041f0af97b7213b46836d53516e78865bfc98621336e00d93a8703cc37307f164144d19508a43f2aec08416c65e6432d4169511b315ac9b366ea3ad2c58165588d2d5ec8de8c920fa31838219e14d41f14bae90362aa322a5d22468f6dc331b6431de62d9651a4ca20822f41081125eb30d84eb9a20b30db21aea674c173793128494605812d8fec519609b383d2dce9222b6337a99779d40f6eec0dfa78b21f0c69403850e44993178dd960ce1e2667941c8146466edfa9ee08d85c4523ca0ce50acc570881eaa1631ed57c3b45ac27427eef4611fe61421c6aa78bf3873027dab01e9543261a191a45f640022a24da85ddc8906807c27dab7a60ce1d030850b05413c4edd10f38de4fb4cb542b3a190315b7af0773ee13ff92a1ed317c5ed10d314fc40f509e4491ce54ecbecc9180d9251abaa05091ddb12f49721f8b11a5f617b36b4dbfafb98f8a46a4898df272ec90bb25d8d9603b6f9293ca2e0d6503b7ae0c90ec53379f489755edf0992f37188a7150888c702353f35418b4c8d9c1692665975f5155695b7db87e0a9770422dcc07ac66d0f65b59801aa43619ba5a79bcf570722a9781c83c1026a7a6edb7382e5e7c87f3dfd134429d22bb73c0b0c22083fab503d040ddef640101a85eb56db73f8ef733aee4ab1ea635193fb53603624bfd72046e0b11d88ace9952e998108fdeef61cc5760eb34cbfdaf18fa2d83ad843e4fdcec8bcdd93f75344de1a330302c325f37ceff61cb2d7c8b96ca1af357b869f5a32e3766f8f2a914f0bd1aea14dae5c885c74aa1d7543acd6e27f3abcaabfb0e6729b8d29428f313f84a27d9ce85b8f16a226a3d821a21dbea78541f7b435af3c8d460cd17681ca8f0879ebe134c203f7b62bb6404cb40ec4315f80753890b1481f9070ecc6048e1bc07ae5a085ef771e440919f1ed9147a01e0648be086e5fbd6eab643aad9228df6c6a3dee8478ed67621376809c81a6c508f432de18204ee95f34fc9959f8dc51bf1fbd8fafc84484ebf5a152b00334e49118a128f009e65ed25a677bdaa54023103f639e61c912c3da1907745f28d2c9f61b9e9d1a1036a1e5eeb3025abe97406371af6bc1c8564b4ab38b8a15577c25f1ca50303d32cd6849dfd6bf2703fa36f8543f9237dc6a0677c6d3c42148bf6e9b4fe7d3eea346725c0d253cd239f6a29f6431d7c1881e7fd279fde43506d1ccd48068060e810569016ab6d1c60dc280a3b41b222f8dcfd4a6c0200118133970e3c618d49bd6087c22785cb109962a1855a95fc87b77579be6f4686804cb4a6491879eb8008d25b8836cf23d74bf637788a559462d432121a3b03401ded62abea381bd94c780e0c79f0d8a5a7698e0284ff2b1b021a0245b56f37727991c341636bd671b95cf44c69db66b937b8602424830118285095bc79807a0ce3d5194c17c51121ee79436f90675a5da748579f25e7281374a5949a4ecd23b63e1a970f54ffd142b553c3ef969071429c38844ba0efec0d05c5a9ad445ac078007a665ec1496cc8cd9d15a0af74851d2b11e887c87906195556ff948505827d66ffd91a1d0c1bfe98b28b551523ae6d67da2595de36c0e5198add3d89246eb21964d60d03c7462239305e6facb35e078209c450755d9b7804ca746d3ce64ff1857d02a0c515ba90ca2805e09c7a414d8b12c32f0e5bcbe71bb53143be4ff1b247d0c3864ebfdf70e7105ca238a5d681065bf8fc1dd20ca2b98da7f1da1a8055797aa8d5cc184d8e94f33cc02a5e771932d4050b26e9f2ec1507ba82a1a7c6dadf6517bb4904ccf89c9b8ed4a6f4e3399cebbd0a674788d439851f6dffca5c5c56d06c536197b209dc1f91d9b9bf659449cea0a2517261a5148168ce91f27dcc1bc4f7a058bbf2b0ece9f126519c3810c0ded1601fec02cf8f4ecd08d6035af5b56b2c6839f7cf6443af27e201db829c1ab8f1bf7513da54893d065c0e98f486974145067faf465e05e3166448235027c6a4618c4d88153d339c19bebe060545a9ca6000756dfe6ecc9d68b59a3f34e2368405dcc45d4068462c272fd985d99aab20b42a81ceb3f55b55f01d54656ff9c5bd1c0e49649b1046917e342672e01cc1a2f19fe5aecab3af4fc58470f3d0665c716a08a896a7b8c2d7a4c211a8f1fb93b5e7ec80adb4d2024ca6f2b62ab7c6307ff57555978681c2298881c212536d4c770c4db51a16caaade245dfd8cc83e3e8367df17d9765d177776d9aa386c88497394aab6245ec0e792e9addfdc8bc84cc6725f66acc92f3028e94866349dd271b30b9ad0a2245e4efb2f87f3cbbad84443ac286ad4523d454b7c26419c6b6f0d9a7ff601a9f36c4e0d60aaafcbc5681e6838baf57284ce03e738ac70de6a578507da6619daa3cb9e4ae81d8d21a75eacf9a23ce7f0dd9233cf7a80feccc2596ce7a3d1296758c5f59120dc3b6b8d1c0d905ebe4ca3c69cb9a0c54016fb660218f8dbae929bb13adb94e6a3d511aa584a681cd7009bd23e427b2eed3189524a6ef8081595ee55d355d72ba17140dc56af2eaad1f7aab4234ea63947a470466808dc505909dbb0a42f41298faaf1e2ef78ec29cb1db9bc6f5f5825902acbe81302399ad088efe2f7c4433cac503120ee4d163f9367c12734d596e7a865bfd98a20906d2a34988f98831f49d52ee4f81246022155c5a80b6b2790d1c8bc7d802d406389a9d9810cdc002eee5072b109c06174894c013092b81c6586fe819e05d670da02cd933a62e035d488e34eacfdd1af0c8540f406cd1cc607c6e19a415ca000b8b0cce140071caec8b8140330ca2d1c1c0f80e1830d0039ec0229cc2fe0c9a69a94e56b705879f02bc251e160f86f904d56e2c80907c6becf3ccd8c5461633e298f8427d6778e14b6237035f6130b942669f452a39863bac7a35529224e804734071e6adb6389f378fd7acd421c9140dc8f848f0287b14f37936bc24fa350661766a406c024d023727f11214309e607ae306ab4d81010ac880e4c00c36e766dbae358117891e5334c12a2a5691793b41f396265d62454168c748c4d58432d4232a285c0dafa0996245034fa4a328f1953873085c68e0f1d1132e0909642b7063bdcaa41538ec3027585efccf85eda61200f637dac53499beb8163360aef4ad8cd33c009b2fad63b3d791d9733d0dde5adc5d8305e78be2f640dcdbc69617c9720d93820403a476049a5047b258200c5a351da6ed41f13dcd273a8d38287c409df3182f9b932859ae0aff740623d58ac17b1339f0b0f0b501810bec32af41e89965307cbe2e2a690d83b46f4a8dfe0bf4be5d075bf66a27959d8b976e64efbde5de52ca2465a50a780a5e0a634a701ac7284933a4c782f9887dd25d1396202f4001b2ebda4aa224883d4b9cb0e9c28585038b4db1bae8e9c265e5fe3892424ebb426e5e7aa4459fbf4fd9be67fe1ce5761956123efeeffb445114cf10c2d0e20fc9a66d41fc19df6c8188b4e834f2dafe340f91b436447e62e69c50f32162310384105c4de86b90b9d25a9e4bbb6e590837f126dec4dbd708a2868e02574654a7116efb1b4d191985d98ee3fedb37caf2a45bea9cb50e2995279d46b7299c91955118a3ab206b020808e8456fb4a4d679b4a406474b6a99385a52368e9694c1464bca62a325652fca464bc6f468c918d068c99889d192b158ecc5a1bcc513228ad968492ca6182d894515a3dd22d188bf6ffcec16f1f891be456ffcbc12937f2ba06002bb8248450a9fa1383124542900bea2d8f74637ab54d46574cbe60645fb2b5dd0a3fa455df573515751d7f821796a328834410291e7ddf63cc7a2ada238453b24fa34d2156d79d269e28de258ddce93ec5b75f9aa7d6a1c777fb1d6d2adbe324a46a66e60ffab1bea69532c28db3637c55aa1cd36d219adf1a4074110042dcd0bc51fc7703cf1435fe3443954c1abbb2f1ae9ac92d1cdd2dc2beb0e8d6e54daa8142d4c03f1611d34ed9c29510350fc89af81a2bc209cf8a1f751c3c7897266694efd45f64aa31bbd5eeb536ade8701450ffd893fcd5badf60e1f228d8ed0a35a1add92e8efe9a6fb34baed1a21f480b2dad52fab9a2d2a57b76c519e9bed7653b8aedbd314ee4a0994d1cd4d3f12a3a3ebcdd2cefa54c3e8e649f6882b3182b2fe46519e74ff6c7f232a94a96e6a6daeb5eaac528d6b9586deffd69c8d74f63930d219e9ac92895d35d255a0af7d9ad802fa1a2ae7d09d899d89389e4474fd4d6c9170b190519489b2d920d09d9b26be5aa0313ae2a63f96dcbe6d37baddc80ca8fcdc6a466b4ee0a08d76463afb2676bb9da5f9cf1940137d36ed8c6e16bcd1a7d1cde8e64958728019e9dc74333bddf6af61954cec4cecac899d899d919709a7a1a95531d8a006d0426e499b6201d96d27a74db1827c51b0299610ae5daf3c09167b20af7d22c3b00959e371bcd23b54c77a82f431fb1f2e6afe432411082786fe84974ee52feea720e9762daa54afd094a14af576df48057dbaae87a5dd17ffe6e742b1822e37c50ab2668b36e3ff1e6e9e58c5d7638bc60ffe49bfc0f7e1e61d7f04fa60342600cb6a6b1abfc2bcf1c77a8bc168d4fa0496c105c9722f78bb22fa222fcf7a8c31fef2a4ec01d0cea5ef42154ae8b34e59c9a62a551d227d0cbdff09d2cb2ad9bb1563b83c91e86820a82b93eda0204abb1f94c771bcb2b4fba3d6f729bd2ff422b540a018be75ba7fc1315e71baf4dc2127dcbefbdada7d31049f3ce9742aa8b32ae9b22f3622834040210dbbc39761778862055dc1a6584e579bc23e70a2a3fd4ffc59af6253fa745dfdaaf7c4df5c7ee5d583e503bda5e17792da3a75a7ae77610f440a91a7dda7e33a507710797a11670ea42d7b18154f12130524e738d763ed871d77d65b7d72338bc9c27145ecd3c36cd3f33c0f973c7ce300faf0efd7db114fd27f5f0443f2cc1fe6f7c88fa4a78fdd8382d5e6e6051a817c4702953bdcbc40a56ba23cfaac502265a21b477ef0ef8ffdd108df0b3175f32371e0ef14eb88698ce17bf8c11ede7bf8bb347ad01033b0c5a7d5461221b940bab29e47e47d3487b3d96c406a97ebeefbdee78e75da17e6b43d204af659e45ffbdada696ffb7efef0d5bd544fdd3ae996e33c49f617e7215a8281bdea559e7dd63844ecb372ed8b679f458ebb5e0f1fd586ef1e2265b416bb71dcbcef91bee5e67df15e9806b10f46436f59597530b25ec5c89a95459faef335dac377f6e9776c76ae7d2fc6186795ead595be67b49e6c0c8395ae735357f9ec18f4e8fe102980235a5be4931c2c02fe44205822fc43441140f1f651943ea812272471a4f6c3a99e7829a136f4b6f4f10326c5842d25d8345d4ad850fc0f9a07cf0a5b6a28bea9466111b04f1401159fe255943e846ca8fd98c04b6de853943e66a58fa236b51ff50a1035146595f73f5cd486caaaef63f80992d6b25b91dec573441c10e9fd951db8791d77a5831b728be3f0a8c3ae1b224f90b4b6c393c492a8c6a6006945bfeb28edfe0571198255f2bf20048d99f2aaf9c7f013405e405e80301828d1d55aa9fc3226f9edbd3e66231bf4394bf21767f6ce3e84d9159a95e5cc5e8045e7fbeed696d7912ab029608dd716d60d25c0ba210e0537a9623bdbadb52fe36e3bcbb3590d9d445a84e4479f79bb7093bef54a928063c7dd4433ec9ff5f1d7a51d779ffed44b186e962e9ae8336f5a472d54ea47a29452aa1369d1e9390cec8b4bdbea518f6fffaeb8e9736989831abe6d079e44310c34f78aa29fd14bf5dfc670d3c2b83ffb21132aec8f44afe21da601113db21a7684f6a4ef7ef79bb637ed4afe258554bc9344e528a4a2cc6e529807353f8eb13d5c24c5ac44519e28874a132079366d50035c12d1c88f5f46bef7f3ae50d3ae42fed7650f92e36752e8ea3fbabe90930913330c4cd0e62ce8f4afb1a75bf615df1a2397dcb4249aa8a55a63b869ebd2166da10daf2c6dafd67b2fbe24dd600c186eda7269c9bba6559ae066a5094ad3d2acd28a726669f6ad910cdabedbd9f55c0a479125ce089f04221a557c221a1407913f25029feea2d3a3b69395b492ab88c910dc0ce98fa406197093ceeabe329b69b7615d3dea0ac1b57e30d1298cb4226a9946d2b18f524af397ebedc0f7f40c3f87f7b0bf1f6c86dd59c42199e3fb3c7a5e2c869f88c6f612477efca218c6c25818cb0fa357bb7b393beb86c9cab369cbae6ffab1f39d3c75bd9e3b6037fdd8d31996e6f81ef6570c39a01bfcfe7e2f52d1c719a5ff2dd194e2c881dffbca11f0dfd733a3288d64978caa88688c40ffd63b9b76d17f42cab522c99e88b4c8e8c96cc53b898368067ef7f2471cd89d1472535fa044eba4254ffa4ae215305caeb0f429cd947a492b2d71d02d2b84488b2e51ec619f3709f9b1e7d51f013f0e98f8a07f1883ff790e593bc5a658374411c57efc321b919544f412c54af1a9c539931f8070a5e60c5a6cedd3955a6fc55ccc2bb0fa16c94d67e1a65326278b5c2b8aa2add59a06fa2421fc1d378495d44d12872d77dc1dfe8e13f6e1e3c80f0b4b1c4433ecefb07c5b8e487ef4696edf1ebd63082b73ae3524ad0dc5a7306bc91dbec5b2fe505be40ff383e449c2ceff915e499d051a339b21033a097f4708ba99b37b99639a4f60f5adfaa0fd5b497bdfeab09f57c1a7b00a56fb38db33c7b8eb5faf346ff9469e52fa97ca5c42aa01d687cd8aed455b7ab0a281cb095c80b02187eda6b51978999364af8cb2d9b57191c588ed3500c1aaa18d9618b6386581dafefe05469722c90030706d66a09a210f961cb0ec683861fb2fd140e6c8141be878d9b16343131724a090b982c40d2784d14388d963adb56556c10587baa84ab33d062dc36dfbcf5e0626dbdf94a16bfb23e1be6c7f0acac194293fbdf7efbdf7de5b81caf303b1ba732e79d81d7b209283d8c7801ea8bc6ec64aa2ef71ae14039bf617b11d49180c060404f432fc81be6967f2f4b1f3d60f445237635963505b8c1f232d8c04c14d0d983a207ef8621986e5e9afe3dbf9c1ef8576e66189be72460e63619044df70b1eeb7d10cc0587251c6e1c09540705ae5c069f5a90e842e164845316e9000449409faa4216416db29578d1b24dcbbef8b2b92e87336fbf0f77954ce53a5c2513d58543decb3dab0a8acf6598f18edb33e41797805112de4344f0ac10394624189942a553c46f424efeba54be8df7befbd67a5d44b806d940947cb5ade0928a000dbea98211c434f4c2822de17efd5a64fcb4a5958ebe1166905ad877788488beee761ab6125ad4254eb751731cdcca40c34559a252dcdac776133c2edf108f70529cdfb0cf6f7eea4772ffe42fb47cc9e44e451337276dfd3147ba3c027f6d0fe488fd26cac03221e2f29b483b6892de6f192405b04c74bea2d86e325655b1c631bb64598181365e325c32d028d1fb845a0d1c428340a8d43e389f192768b28c6d948a44245ec96981ed9b722f0c84a5fa820ba7e978894821428c5699200a800006a595aa074f29ef01857fc3482b6576138d1443482f67da14d4ba21942db7ba12d3a11f5241cf8712983ee5b5e9aff8f784a80d4bcb07d3d803308822ff7c0a4a5e6fd42e09604dbbfba162722b54a35d393486a7a1efefad7535cf2021b539af796f480b6c7818d4519c6b526fbc37a354224dae0ed1629a5b452bb455abd92be4b6aa3d4fbf5b18d32795fdfb757aa0564d7a93e517aa0ee3c71c5d9f57d8d2bb9cddaf0d7129c2103b17d5377a06ea047823bc15ee334ff3837aeeddf9dedfff5d1ed13db3618dab8fc74556cdb92c0521b3b9b76bd14388e325560dd6d5e09d893e278180b7901b2a9ea0383c3be332898ead92e2de1f7bcec4bbf78a5d6ab77df2d29c80b90946988e2f2d60a25524f329ac0b65f7159a9892bbdb8d63953a2b5fe157bac5689b43e912d6998268bfc6295027b91cc8b77848182f62f3259209d303628257b9094b2ba9dcb8bdfcaf84b83087c5c8e0f2bed1558662adbf9635617a7cbeab11d81a282eb72b566eb8e9327914e55b027f2b5aa7b6134bcbfe5597e63853140e88369dd011974832fa3eebb2f6d718a0ea341822dbebd7a5879fa16775eb5edefc50f46e38a3052fc91ac301a368c9b2bca0c9b89d1f8af3c497cfb1e8cc62d6d98f2e451f749e68b01b2efab8e1a963aaabdb25af4c8be782badf4e2d8bd6fd7b849827dad8e1ed178d0a7cd85a1f829c320d2a273b46dfbd1f68f21d00a65018ba7faabeaf1a4154fbd3b2a4ec4090a450d64421683f9e8b1c393720a4a7fd30fe80756a9866095ea96599e59000130b25e92ffb399a6c04ef916533c6e67ea3a17c05b9b9d02c09f3fd36fabe0cf7cea09fe9c9d3e05c60177301ae924e4e80990779666ad5216802e67a372ab686b5bf06669535609c401a03c7f5696e74cebb7e50aca336b0aca534f50da29b03cdf823b300e8c0144ef119123a0786db3d936dd0348a2cf106a43d140f12b4a702bccc959e1bd8a2dedad20a5686f06a14ff06bb7ed57da835f7d3c89a80c9d8436ab442bc3284bb31bf8f6ca102a097d82ba2ef08e2b0de0ede7a61ad2dfa71f38b5f80bbea946bb7f77946968dfc7539409b763780cbf800ca1c229925a1a6edb7731a5690de9ab40a07d8b6f5122953e5a94b412ea690da9c5d3a430f80bfe9c6d1b3a21972ccdfe0524928f2a105a3cadf451512032c6566b513e1e1d4685507ab4fffdd3fc40aea84b9fe1257388219427d50d556b2da56e5f1b8014edc58fac2f448a5ea21ef1cb4dfb2a489100a410128e3eefac92b86569f6bb76621c7127ae1175a498255eb9695f468a61c49cd845b4128f884fa2123745284bb38f9b4d6dfb5d51dbfe111946855ce156e8c44dfb2cc8d00272b7c32e4bb31f4e89b7176db3af6ddfeb76f5334d6b4724add17d82f43d44fa16227d0755bc63b091a4679e28d766b1cb5d387037d35d3fd3b86d2da0e06ddb07c1328472d39257f4835f562984dae0d7b62154084523073000f23acdab22d10c1c59478fd6b812edad8d32ddb777865dffbc3350d9bf69e4755afd8ba34775b405c027486183eb664582c908b78c705b184db403ac57abb52fc3df3660c9d2ea2f59ece1ef0a6d980c9711caf8b6c33c98031ce62f5ebff7de5b698e394c067ec7e57651b641a547f5eb074e4386644ba395a447f5497a94c10d46eb95de0b93619e409b82301967d3fe6032b49635e0dbf71da06928ca3400ef8ba3d31ccb3019a518d21ababdd8fb7218c32fc32b896684e5f9816d7713ae60995dae6155a11c00811b04f2c5ded75babe7c5f02b3aec8a47e85be434fb44f4a84fbd85ec958881ccec58ed274c871ed19f0d1166d3ea4ef18836c6ae4f8f9cb43fae7eeff6ebee0540a79d4bd456a9a4e07bf9f37befbdf75eb02c49f8fcde7befbdd75a6badb5f6964328a59d3db2a5d52134d456a9088ada901fd65aeb79de1359aa793fc407fe99a5d5214d35af1ce2bd2def7b49adf8e2c5e98316805e7036ed937e1896945e50db219188c91f7437b5c490355c6cfc54d57cd81ff2e328073a56bee4d192b2abdd77f727b254f3f24793949b2025b8d2020c40d2d49c9c9c6e4b35ff9b59f377bacd6afeb75cf3777222926b42645f36dbac26e4eb63b3d5ee3b11316b42ac1f5bed3e91a5da2d87d8b22a2c89c49286b9c30f4bb3d4fe23fb0806e3cb87524a29a54540859b6a157164532daf3e9b3e6c532d2f22bcb876fdd1adb5d4ab7d1bc36dad41acb42534ad481ac058b35b1367b7866bcbabba5b7b2fb6f762ec795fbe395baff962cffbbe9c416ffc7206c13014c73b8ed6eb0886a1288e232c766331eb358654c57184c16231d918a342b5e9cba6367dadb36cfa403813d78409ebd5047d3d5f7afa7ce97127b77985b90a937515668d2ea7a4425525d54985aa4a2a95fae45cb68b72e9f10a6169d30dc3055275b7f65eec5dcfb35ebdead6de8bb1f7ddef62cffbbe9cc1f086a1f51a7e1904c3501447d885c1ac5758288e230c168bc9f4d5da7ad5b0984ca635109089a01b1464bd06990812121a1a3a7102c5ecce66d6eb0ca892413768aca490908ca1b192276032ead6282e0a14d62b8a1c239a01d6a11c239a91777d0fec164e436aa88bb448f40abc9040f0248ad525cebeef62df9ddbfa457776ff7689bae5b9e35e4c8dc2e8d3ace5f541c42fe2ba45b1d64a3defabfe32ee3b254f1f9be6a730dab429b9c3772e31a82dbeafa4a5b53cafdb292d2e5de2a65a5ce2ecba3465eeeca604b07257536e5076bdb80988f4bf36d5b241cfae4934d5b2816b532d1be06c2ae36e4a8f683982488bce0fb7ed8b32d26b32b2d6282c8fb99b66e17e79d2e9b94d52e9f37e6ddb8de1298cc359b6bda21d16009ba6fbbc5f39cfd9fce13ffd0be6c14a5a9e6699b3d6b3d9f9fb34372c7c180c0683c160397bbfae2d4f7dbfbe0f8a488b2e4022460bfad42ca6e8136534b6b2ef565f4b315a58b2d6ada5960ab96b0c9258a044639c38bba2cfa5bde466f5c7b818f68215f4a9ffab9027d17d93bcae5802df823837b31c3d39316e2f7db72ed18d7116e370cefbb2a23d623dc952ea2f5e6a0bcb53e72bdba37d850afa9cbd5ba73a24f6497718972098adcd76860dd5b6a5587313911685f9a3fdca736e8112fddd57814109fafb73c5f67267532c1ce2b8d8140b87a99dad12b54b396b3d9bfd67f0bfc72fe37b7cfad86ec3a7300f4b0c6a8bfcf65e37ef8a7c811e7d5ed1432acf1c4b2fe88ddf929e277d35c7923ffe73e9235db8e9f9eb86c8b32b1afc2a86b58421453bd49775284bf3c741ddb6dbba4c79d26795c4f7bfb70b659564d7769fb6dfa8ede7cd62b5fddd8a76d91db837cbf6bfa675d1818c7c4ab31f234b297d622f1a16e90318a929cdde4a31fe70b78dae7f7e19caa1b29797c2964e0956468383909d15285373e8c0d86cc94152848619be04555ef5aa9c3b18b4a035d9c1892e2e4c2d34b3ee62e33b07c9f220c7e6a3c70e4b4ba3fdef96b6ee5fa6bf403ae1c7fca24fd35c3259f8e0244b0f5b3217721827e482b64369be445bb0f81ae522d3f63f2dd86706d37563b0f2d4db0626ca936a54571caf3b7ab6d32e30fbac5d76a8924991ea1174a9f2d97e855c9d6e64bb05db5bd83e857c6adeb346d1a6ff9b963a7cd4dab44bdf3b8cc1591217240edd427261adb545d6d6c75fb53fb0928dcda7998f3e9f7c610c1caf888b30f6d66a2d78d25aeb5c5582010bda5f5c90868ea614ceadf65941df84d9fe77fcc0ebe5fa5aa8d6ea3d486623de0740a729ad3beeb6f885d0d94d25fa2ef1e6a9de8b228854a96dce9b6a16363a3d7c501ed66cff3bce468db454e4a69b5bdb1e36369a85105dff498de854953f0fc6170f511ee7de3f5d777a56ad1ea36e9e393c0e09a8ecfad6beec89f6b727fdea416fbed6e36c5cae98812a2db1f025a1e7419fb4eb460ebb6497966e5d0283655befcbbfb6efb2dccf5a21379dbe16a2945e2f7fe18fc2499f59b6a52935f11ef36a375edb5f68859e55a0439abb84e4a6cbf67701a35e77473241cf70126d5668d91ccd01a5f91507f5919066e55507ba067a7583a8b1a3409b1bad11c296ae608c3ee9a534fbe39697698bb6578641d9646dff3b5ed15ff89f78a3fd2bb8d2bbbcf4a896958ce854f5bd38d36c8e6c7fa1151b8fb6c7b6de44a57dacd9956adb9741b101d06916db9f45add6e6b054bb82304ef21a27db7f14728b6badf8bf9a55eca02b00a3cf8ca4450d0bef3dd2ecd244f6e9c6e50f607af8dc1113489a2b67e1a6b3702faad58e9a526a7568000546edf4a9cd20faf4703a0fe7e13c9cefd9fee74cdcb9b8b5c5aeeda2d7f653fcda0e4279d2e9e140a8ed44a751b2fd85560ca9c95f94c9fe1eaa64a32ccd6dcea6b0fd6d91ed57aeb339db95b34af9f47e37d3690f97dbeee1b68da2548d7672d36f6e7aeedaeec16f0f84c289ba2d7d7a51509ec7ea457954c6112b6f737b51d6da6dff5188e692f4be32b43bbd0bada89cbd2da2684a692735b350184f3aadee9d8aead6f6fbb552c047dfc75f591bcbb06b58cc28ccdc591d2d29352ba5d152689f3e76dd72bf4e625c9938091d6efb17b1f02a7b896a29e53dade1c7a4b62d5cd0e7e76ccbb7494427fc356aca5fe0fc85ff055afacad154fab6cf1a35353585a60f9567899232e2ac11d1a9ea3e2d0241851b5d75bbd217fe41757489d3185cd1e76cf601d5e124d054f1029cfe00e7013d92419f338ac6031a1cb8a900a7b9180ad0425b2aa079aaba4fe46bf96951addff77ddf57dff34a9a49e8e12fdcb4699b2d223a11f99af7df7ff90b9bbff0a7608d3eb500fc68abf274ed5fa716f23ed6a906dfa2477e6595bc77ec5757b73c5da7a3e5e9bb9d7ff997f64a8c0bdac25bd8ab887fa65edbff12f91afefbfec25ff8c358d0beeddbed49569871aad5d62124f499bbf6399bbd1042db2f212b6b5b5bbdf90b7f1744f0f0a2ebfaa0cf6a73779bd74a29a5b4565ad625846c5d4e47b65722319ab4d5a6ab6d684a9fd5ebcc550f95586bb448007df459fbecfae4496c2f83ab1f459f799f7aff589fee3006b7e97bf5282c2c3d585854b2fb2caac770f385bf2984beda5c5e4ea3713c4947fdb32908e4002154620c2bd6d19d7812d5127264d3ed0020519e44b582786dea00a79d27d14d8398df73999fee6badb5d6deb47d1a6475b456b4d88a32196592c96231ca148bc1609409061b47ca348ef4c87b91323189621852a6300441ca0482f4c863caf9fb28d3f7791e65f23c8c2913c6f4c8fb7be991f79632c1debe53a6d85b6bdf52264b8f484b8f9c1e59af94a98664a547941e79efb00dea9fd88de55c9ef73d80237cfc3842f0fe99c15ceab89f1fccf673f8e183a11d2d7e99589eb8b40f92b0c1177dccd75efca078411104022a6979dad023294c830b66d16e40470fe4d701dad89f36db5207f8a707f4197cefb3b5405ea923564a1501fa213f8634d5f4032dd580fe0750a9b5d63fcb1fc34d26babe7ea7a69b0d080808085b0161279bad06f44c6c35a00c66ca0f959ad6fa892cd57439a408ca1bad5bd59114d4d4aabc12072ce8c80f62b65ad0218830b54ac5e98c9394ac3e40e86a2b785173c20f5c782852ab1ffe08b3b00d4412878319ccefe519f9cc3bae573fd7a7b53a8c01d4969ee7bdf54a221adedbf7caafccf16dafe46137bed6bacc510c118b7048790fc3118bfdbdde057ab18e4123f859f77d1f56864761f9812fa3f4e8962778e6181f249b701b0cc7d781c392478a1dde1979ebc0ef011ce07b8f03fc3be27104ff47b0d4119af8fa3888f4bca75bdb36a54740e5e995f7c73bbe26cf26dc98c7dcb4f1ab80e182f67e3cbd1f5f1c7f6cf0fa70a27f72909347093e4bf8f953137a21b576fb6a439a6a4225b5b4aba39e32e81ebf31c334c0777c7ac90df08b2f8a0f7b0f34edfbe007be8ef0cf1ebbe49162cbfebce02d7584420f0a85ff3df8f7de2b242ba56873f49983ea760ce4806e4a599ce2c3fe6cc26dd04471d8279a9177f839becdc3ee26184e74f8f5cf3ec117450fbe3f7ffeecd9df3b6c06f85f7986e599a584de6152b7af09953fa8ae564594f7ddb93236ed647b0e4bc9761fe1dbdf8cd8fecaeffec723c7b73f18959849d8b9c471ffd381ffecb1bd92470a100441102c75782a5214d13aeace650794c5994b1d3cec961241fd0ff0e79d3fef5a0a69b2e5a29e6e4e4180d86a94d2ceafd4e19552f5716971102957adce0a45ac6a5fbac65cd57cd0218ecc70abfd18626393b83272abfda855045189dd978dd2227f9aad12d196e9e2c2d8f75ea4a5cfa336d49a2bd5853100197dea22572afacfb98a0c636c1f5becd75a6bdf3aaed6af93e8f571c5f8def1fb30c6f8defa38ae2daf275dede685c9b818635c31feeefd3e6cf50ca68187312dba9e9b364ef211698d92df4af13c5b9ea258bfa803f5f1fbffceb03f2bd818c5c6b27dd2a45806306a0beff17f54696673cebfbd8731963172b65433987d65ced2e2b5556c9aa585cd3ef1e7937ae59c3dcf933cef49b04a263e3f8c9ac1126de1bdb7b43d18db032a67b099acf44698379621b8e93d2e6f9090bf88f67049ad9873ce38b29b8eb1873d7f1cd8f33cafe2ecde67f7444f2cb59bdee71894c96e8a1d48ced92a91404484ae4f7ba3bd8ff1de93e049e07ba107e6fc951ee8311cd415d4ae1f5e4deddf418f31ae5f2953f899c647821d7ead3cf2d661fdd876fe5ada07c91d55d49e7406bd3f0e7af7a0770f821e87c7e8e3c3837940eea0ffc0e6ac35ae603b4d0a7a8ffd19a3d45a5c9d5ce126fe72468ff0b760a3bff17778d2073d424099bec7430ffba0c743250d730ffd50c9c2cdfb428f85c820597d1340babecc2d1079ceb6ec357956b0656507ea8ecd368691592cabd59ee4b5e2a8d7dfb197da4d7ce68df1cfeea5b6521bab552351e9f0cf3cc6f04a860e8236e7ec353e7013bf887403c5ef650d37b1073757ea31179dac15b463d621dbeeb05dbfd0db190429386207028275a447f5696ff4ac66503d7f8fbf86279d77f3a8d4bd129738aa0de8ced5ed7ebe97ccd56c6391b10f43a434e95efbd6ffca00b7ef4029a598564a29a546889466e05e6b0bbfd7dddddd7d8e789d1ed1f25e7ae46177f7aaaf303302bfe7b987e9a547b4faf05ababe7d7777a7348d48692ae6e89cf3a8a7c051051cad628e3e73385301478fa30a379afe08dbc0827bb58609a9d8d27af62ae06831cb8a64e695b1626b2020139452ea79d4a31e3d4d8a834c0401e9a0a0a0a0a01431fafe358787dbd60412b11e11eb0c584850c5f5815c988b814a8e1d20d0a40a1e9a665565d94db20dda54eb01645cc6cf2b4f1fdbfb3b8efbfbbef3fef7b9b6ef746910195ded57e0d673d738d37d661ab27db3fe5cec9368c67dfc18e9c4f6ef5dc221f821081fe8bb1ff898e82b3f084b0f2cdfd2ec506aadb5d4d24befcc5a6badd6e9159e64690e7397604a28350dce5eeb88d8febd94eef09a44f530ce997edf77bf4b1d630f7b9ef51ec334f0ae77b505e7cfc33b7cbbf1c7f0db87318d4fdf2f192b69f600ffbd185f8c6d4d3ac23f92951ee11749bff4083f487a1bbb946bf15f6db37defc31f6c862daf676148d1f7bd92be78f1bdb71cef119556a51fbaaa3efdfadfdb8a4bea41c518cfa8e508cbf85b6037a78a8ac0dc5b34c2528e251b4658aaf6491e78e3b03fba993dc9ef83611cf48047ddb5fc4a1e787b258efbb82ca911b1967934dd39c2dc463aa3bdf7a72384b98d4b1cb71c77368166e5acf56cf63fa30aa8549c6aebb790b696be510ced17da184794c9c77126ce66e6399be1f48863aeba250d2c0176fda45ddf1bcb1301bb3ea66376fdf0e8881ed5a8319ac6b19432f9aed56b8afec619ce7339c1ae46bb8e96029e54df26eed179d1f684e4e96287afc55a6bad59ba6e51145decb13997a7d633af3cdf34cb1369c9d26ed0a33d9805d96cc592ae2a90bee8baab577ab5296c8373369b8d4ee7521236375a9ec8cce9baf2860f102f8d0744ea4489e109932668700a1e08b244955315952f249e161c2553b2a27cc5a0848da6275c514bf441b3441165745bf6381db9e108ae4fd58da91b77cb900d4890c059d961b3c4438382094c4cf82a4111556cc084147186cb1d1c0b5dce20004814377a9cb890a5444b04ae254aaac8c025858c15335c5b4a30258c540f706e4481c189902f4bc80044d6047d94c4a962f594a5c5860b3c4dbc9c315244d6ae0c0f3246f6408d6972831811c6682561029544975d1c2f13a7c9132229d451c187126ecc000398342a7cf1eac1082c165de2ec00449e1dbca246d84325ca1e3e759ee4d9814b0a3e683d11e20a1c11727c210933b582116d7cf0aee024e9f34687216ba69c70032b77cb010b0523e49071c14f9624a07499418a08b12a2a505852e6ece9618cd11b2770a2d8a0d15382084e271bf1c3551f3332fc39418c102549b6ac94d17d65a0013c4e6faaa0f0406113066b882f6286f05206852186a8d3470aca11728c8069c282e785305a7d9ef6f490fbb243e4cd0e5fd674795f540862491f34527e9410b9f126cbcd095f72e0fcb9e9f0250fd51f1d96bce9b2e606510c2854b59003922266f07220d052c70bc80b5d43d019c109c9172d45dcb0c515040c373a31bc41028e9d1c3810b505d742102b46ac11a1cb098d10373d7039c20bd533e5c60b1254bab4ecec20a60b9e2d23b6ece0e15001334254e173c32e8b9c2a51585e90520218ba94d0e50f1421654cf012f364ca0dea67c7c3942c5d92c0e3e486bdc3116eda74519287ca86cba006cfee8b9d2525d87936fcd0e4aa4892322db480e4c6053b3545bc41c2e40b09973b60b29adc4a504217121b502234b1d1946407ab2ba9ab23528a58992dacc992c3126682dcf13978e44479b9c1891156b64c115265080e2b30f1937503881b1cac3036c03922464bd82608175ae8a2a6cc141b097061cb1139ae2542b05003794547eb872d50a68c10a187953f75aaa6b850889245889a394be8d49142831036f62881c706222c7c716282474313273a3369b6f8c0a97203cb9c125ab8124545c3110fcb1129455030a14a9a9dd48d1108218286ac2f3524c9b994262466bc58e0e36449d61c1b61961f2580d0e3278d91193060458bc89e365660a6444883a58b9ba923de9cd006b71444949d1f3b76704dc00ce1821e343cec50e48a0c43cce047862837396c70b871439939428849828c17295e0ce1540c526ffa08c1474b0a6ca0ca9480c6852d395439e5dcb84085490a67be6e6c202565a4c9982a4e463c2a0f7819f146cd183048ae8880d19cb0478c9ca72b344fee54153650c5cb1015b47265b44210e50299345cf8c040e40a0d49a488c0f2c2458d9597620407264a4831a9539facdce1e3b4030d3c7c19e24310281eb8c0cce0c53e8d5bbe865cc12192850d0d3044e9c3431ba81deac0001565092959659ca8497263823a3ed04152a4842272569ab22c6122e44894962f4f6c386027678f911f946cad99ea2145ca9623391031048808396278ca0151420b982552b48e0c3924b68ae8e08216154e8b8c1715d450f179618225e8d8c963240529146c046581f3a50d152b564554311264899e228a68012286a62150c8584569b3644f0f1e952f16a418d12609115bde0440cb142264f862e58725bc29499400f142c24a171b66a80ce12a82872062d05d2314cf1337666c00028395126e5420cc1162bcb0389da99292fb92f3414b0c941239ef43a48f121f6e5a40b4c0270a015288a3e5e6eae94e1d1b50d2440159a1862248d04405c9b1e112c542104fe6dc30cfe4a278c86201c90b1e1473ce9594921bbcd68c1101851b286e80f205852a3925599e0b4d39e860b36d61d9b1334401a44851192142a58b9f9ac29e2240c87953c50d57960d10ea4c4509d245880d7c9e88208d4e8f17a7257ffa8890270533564cae5290ba726201266cdeb411828812c408e087af3d3258993343550b5240bcc001d2268a0e58e8d51e27384b6e678ef08105334870f8631b82851e76371ed0650e1239405902c39e264260a452c839c1210635224c5581a20414a7218c580285a3ebcf9cad2b41d6dc5e5862c8161fbecc294187156a12344f526fa68cd0298990c1891049f070e181c99f9b1a3657308cd929a92ac2c709ae0a1f1f9618652139828f0b4ae6fc60c3c80b393471e34394355b4aa88e883f2b68e113e44917dd25797183831c3a5a5f639274708301891256a204313253e26ac81e169e54d1a9a0a30e9a266beed8e6d8e9006a4f1519323cd4a14ae104dc8e889a2a66ac68b0d1028b86155428733353e689027ae69cc94a7226cd9908b0394188177448a1cd170a1848c1814d90175ae092478415f854e132a30392ac2cde094d44984c59a2464e122a4c81a1a9881b2d39cc10853361887523849a2f2b4fa25e60f2a5469fdeb8381340acd0c64a1d343e7c21a193f373858b4bd7d5121baec3c0882e5a4720a13544093417602265668c1518de105900159e5d0978a688d1f283053453ba1eb8004122a6032c44edd19db0020b54bc232144e979c29a41a7e7c93342819b204cb09c9123074b7d5a3f57375a52777e40d3854a18a7b2819f32d8f569ae3f1be399b05f106208354ab8e401539fe60a0061a7ab873a3bc0f095ad08e29c7d9afb03bb64956ecd2b92222a869c0f79be708152a33ed450e5073450a818e1a5567f56eb53769b03a9b95cce79e95c98ece564fb8f42f9e65d2e7bd5ad9c57cee6b63fdd9bf8d52ea2fab976dd2893e5c0fad035d54eb77d64fd4c54f183f03dfefa187fc63e3e6d696e4b0cd5e47bafc4f8894b9fd6eab62d7d6bd565ad6c973c5b178cb556947e0543cb7b3bad952dadd595419ff8b66f47a23c26482ef2142959ed563a67a1388841849eedaf80ac6aa3aa764cf481f70ea0b508782fe5950f0207ee5d3b341a869b01c8d227cdc550c0120c1b75e37c11d1e5be963eddebcc71b6769ec341632f5cc47dbc6569eee55eb466715c24deb9d980eb91112f4d8d46e145e35d762fdc85b78870c2534255b7866be6d1c4b366fbb37041de4042e79c5f0be9fc9db893469fd52be7bcee1a7dd6ae3bdffb9b7af044d50ad360c5e7f55e9bfeac68b60297c564a1ef746d7fa115970592e9a696d225a90652a3d0696ea7d56491ab726677a8b6ff8aa2a7c04d0562f499895caba79bbb56378ca7d30088ece5bcabdb3e3deaf428ab1dd5028fbe9ece4ea1b89db952e4cb0f76c2646d0d11c7eb833521dca0a3b519af3ca375f46cff3b2ae933ba1e51c789d61042df1f8098d1470ce11262bf8b992e32a3a2a83895b7f54ff479af6e97ed7f7a5917e74fb8edf74e3dedf35edd2b4ba399b9a3b775cb5d4eaa14c6e4ff30e5cb84599e0981dbfe2b8aaaf983e8828e7d385a68531d8cc8b3cfd9ce39095f2178eda8c1658ba1800e3e284b60c3528ca5209cb63f0c1aa6fabd9605d29889a6e8cb6597e9e8ea67adb5d4d68c9762f0a02f65fa144024459f481bc9050f4ec7c9671003e2779096d8e81890ed8fc485ab0344164d0f16104e9c4aae560f7b377b37536bed753f52a19e94680c90e8ece7872c56a8da8b8288becdc9a2faebdbb91a735b5c61b2bcc22c59b78cd8beb1833e1daa2ae5709a43dd3087d027ddf911a75d3587a365e9eb44ee16157f98bd78d4f729bee4cc9ca3cfd9cc2901d0e873e619d9e18b9712a5b42267e12ef00ba253a612a8b4ed94453d4c2a45910000800010005314000020100a878442c158301e104390f90114800c81904878561a89c324c69118c5943106104000000000068866a07100c0293ed899b5fa46d87b8be6087853ce6a1f438fc03712828be9653b91dd2df5507a819561d2a7b5c9fe79f09d55c2cc6199e6ab98401ba5ebb91b10689dfc7270701ced8452f556c5a98fa2cfbaea1ede5b1c2ce7c1336044916e47a798fa65b2296f08f496eda95803ee0c7f926fc5b017c3933a6946616042644f60c1d08db8faf29912bfe2cec4677299470af4a8887247b3d13b29cea80f13b0f7de16e4c64699a48d5fa2b76e3e886c2a20194e78d0c33b8e7c9ef561bdcf4bb2b7c482819059f2cc507d12d51a22263c0815792920761d0e36c90c306170ce6430a3a5a34bd1e88e1fd61dae52edd8d5c0552b50abd6f55aa1bd5c83031a9d15bcdae09a4901caea5482977b39692ad5dd4cc41a282f80922c5e8074587ae25483e8b20b1f89ea6ed9c1d0598134b7bfbe4c821296be75a063cee7d2d4fae83a3bfeb619c09a96d5154aab50e281d25a73e5ba5f60182fac47419c8f1238b412c43d9842b8eb91e780b1bc3b7523f2a9b5e2159cc3397a2c1972cebee567e8ad70a6688228dcd48e65d3430944ac733b68831008aac203710d0b11a26de4c8a5242049403e080029d7db09517ade40c8570d35739e9728afa891cf0e2fbb09e4c81aa1d49c6afdd91678be009cff51cbbac391279a2ccf3b70b3a5bc541e8b0e8f041d9199310cdbcec88936c7647a1675bebcd75a5cc378b03049543a3acac87c4b7759d78a4562c5dab86a04fdb5002935726cefcddfd8fe2cd979b797e80f97c1ed6bf9a7b49d3943e97cfe14d3a4e3d3c3f4ae5f3053ce11262ad6662f0cc4def792faa4f2496b2fab289d7d5342c1c3775439d049b847b3ddd66deba3e551a400b9acedf9a4b1872a80dbf8f16a9b1e6b1e33dcdbe1ae46712428337801a6fdb7ad2423663ffcea4fd71f9c5c4e1a668b73060c33b76e90620be048d285490e69b02583c6bcb52e355a5b868b122504e1e1e448a819b729c215220481e7a39e58cce30ee476bce450855e93fbb838755bed47ed982dd1df0b8dd40e6367fad067297e45fc51941564d862cbbd1e53086e397284a5f277599d9ecb098dec668b8662de6337c957377611c01d480f877dd7a17cf460773aac5db006c2251353e843d7a0a5defc08ca014ec139b73e01f41449d072ace8c505fd918f22761cd9b2f2b825a82603472e1eed9bf4739e1f5d122d303e471d1a5e73bef5b8ee72a15edbf3fbefc8fabb257e2c22e0b041c651d46240617190005c1ec685abad281986dfed7f18adbc9b6de153055d34095ebd17fb81a372ec34c5c5e0de0f9415c8c1d5997315e8f328e144c5ce29e85be535cc61446ae308ce942de1837c1a8b764e2f948d7f9b15ccd2f521b999154e498d166c86251b9b7f5861bb5eea0f1ab28b00dff4a8ec18cbfe8fc5b7b3488b8f4e3f79a91b0bb144691f34805d9aa0caf3b7f3748541f433fcf64f1a3194b5fe93ff16080ad44d65d708c1662ca27a02e9dd3062e3692710fc912f89c60747550521dfd1f2358c5790e39bc15c5fe37bb29e5bbc92371b8fa121f790f928f71fe6069c1e71b8ff4999423935abe9571a9f3592cba2d2bd587fe5129311fcd16ae4aed361be5e708e81312011410cbf4c5c35b87a81ce37eae262cf39a23edb1ee5e34371d26b2d05fe46a56a4dee1cc2309e7134920809b8349944c74bbc80c77f62ab7b5d0471f4317fa0b6899f4b8e304e0786526e7d497b7890519efe597b0e485040ec58b4be76eb8729f721b1cc3a74fca2e6704b7669aa2bd69d8a916f7d2cf0f169361923c50c356fd7238eb63ed42a2bc1983c4bb5a30b6a4100ce5f8c1b53e807ad9298434c32078e902702b211b6ac14fb27e1aef83cdf37effda3d08153fc11b4ff2ab50ce627a83298e930a2e503bf0e91ae24a29a00ca772474dc88d0877babb3ad3dc0bb6688615c927cc8d402fbb24da0e44e9d652feed80b31fc44175054631398b87093c6dd6fc9ebeabe8c99f5fc2ac0d6e78506412c2dad1987009f9c7900eb5a05107d9123504374fbf084ec9514cdcfd2bf6d3bb88a0b7fca5e6c8129683af19d6d0c6ae5d8fa44646d03b5ee0e510ce91790386d2f3937c28febedfe4e6a280a186b291142fc921ab2cd633e42c8200cf0a8fd517fac789e733c3b11ebe9b91be3308976aa577c3c11c0b372a37c27de04a2aeeeffedb7f3f98a50bde27ae5f72189ef4422c9b68bad814705f27816f132dca9d0be24bee68da7278b8a7623e249c6a75a59e00afda335a266fcb061a5369fc1a726ae343a4d52741463126d3fe5e235e6f65a857a4684beb6c800d2cc69bed3a7aa96337d74e4d1fb7a06a1ac98d9c92eedfe1d564a159223b47c2661227c6129514d89b4a459b9fd2fb24be9cab02cd7219856c212893b24322a7b75be9f20af560e2a961b5666a8a6ff83fa01207c8b5fb6850220f868dc7f0ef7185ba946ca45617bf8149c72137499431c9dd710448d8285178ca56c9787be4cd94c950cd66800a2611ba57d74cd112160a7912b74583c60e7e44869a0eacba32b70ba8f511a6c57c15e880fc3feb328a65dd7e8d2347efa488be18edd84ede7a7f99ea923f899a052af0d1cd7748803a69f72d46ccbc1a6f80123ded0b564b4e8018b1bac10040061541083088e08e57fda6dd6d90d51190e68ae99d9da7feb17a50af13a95c85c908be0ad9c4aeb4c33a2e44ca320ca47f2e7cd2a7da78cb83eee634b111cf41e8f2c01e66ec52d6c08922aa9a739b7ee21542d0981d589fc6e63580f571950a4907c30576beff09ecd1f4a12c2b6885dda9562d64c797e4f7355e60340bb204c1ccd40b180096197afb9854dc45934f7afdd976b1026c8236cecf1887c8a00c087f5937ee0410f53817a1eeaa64781312596e3390f00842f96703564b8b714c1a7c502c606fea0100718ef6dee5e14a64dbcc2054c71f1a42124cce7d2fff40330189eb48a1515671d6fef23ab5acebbbdd969e0ae8f172df39dc11b88ea4d6e1feccc1c9d6f19f4a0ba152aa4c1db8c8733893aae2af1a45a6ec8559d1048cedf47fab28cf2ce796bbaf14d1710608a9fad7246aab5beb1847fb6123d1464e4690d043ed7004944c8e8e4a22301ac8a12824fd4a8b7185961138138a72292e1f3d41a9aa25196f4c9966d6334aaba813626ab49869fdfca98619657c53a43e515d209a08f7df49c3ea4cdedd0732103bf460be340238b4c610e2d0ea76aa8d6a1521de3a9f21c72d1594ee201cd58011cabddd2a5c7fa6f914ecc927db430e49a004315189467f82732fa1bb6b12156693cb7a92e05c3d34e0a41906d656201a8ee4bc37e4a62ee8d3fb52917ac04657472330dbde4cc0d0f016afc46e71af5b4e08964461aea26d1a9c320882c840331257f5610ce0a3c53cbec712f8138d939bbcb6c3c2a754a710dab3811492ed2ae00c50bcf683b8991acfeb483cf595a65fd430d3eab0c2da24cf91153024b5d6afe9dfc4405b536bc49ee464f5e72ee89140ff260e9f0bddeb0ef2d9175ab023a667162e86ac0545569c0190f6add9a526b8a378c3da9e409d2ac4a09965a2a2205e89cf40abb3556852bd0c2b073e73f7887f2f0773a6ef9ac5820b292ba6bb476e15ab5308e93c90cac281154cab29d3ea8650fc524d5ac0ea857c69a76edef1cf9938d1e523f0594ad8a79a37fdb482795747da6f08b35ad495792aa6453c1e191d5aaef99f45a4f17d7a8af5b6c6bac4e57a0f032d02420b940e5703fc2adda063700afeb36f5a369019508b01a85e080342e73da4369405da872d78f3d5819fc35cd87c750a0651296d3b705e6b6796d5e3e18a73272abd1f51996345bcdc0e1b5fab565c7126c6202a708b15323a7282b2a76476cc1c8148f24ba7eb4535b92a8e85a245c6ed3b0ec0de565e543a420534e664735ad9b69ad23191853ad3472525f64e90d6353f5701c1dbea705cec126d5b774a8edf557350350d931b1860069945c7dd5321115339dd3aa67a890b529023eb536d160194e0cd93e4ab095ddd53e977bc6b57e9edadb0623176a56578ccfdb2c45b9e4cc6d6a5f8781e4fa863700163577aaf287bf3aa36cf96cf1090e06754e7e8c1f183f81193252ff9371cfac1df7792d8046095691f4b45802d2a41a170f02549854b1cca9dcad41866fea480f25c3dc1d83d8487f1090cdb9fbb2fe4dc03295c4c52639fb39e07c80fdeffa4ac8bd02d362dfffc549a8bc56fce91d3f9fce66b60a882cc6948265179d5c9ba5ffab9a8691bde607d5236229cf7e64eac7df383b11423056ebce828fd3d1afd77272c3f264e39366715d71d3293b30add29cf954bec84cfc41c5b0cc2fbba74314070cc3dbeae2893a8328a94e645f90fea3c5e8970bae2f009ab85c1f9963184e735454e80460283f4b2999cee601aae8953fc5c3389286171b18787a27a35dac2fee11d0f1c06ec66ad8f9cf4cbd1a240005bc3e35d35ed4aa5dc23b44904333865bc550a01e64de01c7d95c678b9177227e2927b1a14e59de167011e68cd89017bbd2353c7f134624dc8f68c94610dcbd1869b0075155f0e115e5d653756023629474a5da783e48151ce18ce3468a9c74df0f2e75c5bd42490222ad0acc64fdba82c1d889045494045fcbcb5fd931045f59e3ca77383081556c63df68507387a79223256796b4e5463932772d9f5dba372f4dc6ca3d1b5532a799cd8d064328a471534fc9cd216e39be9920a737abad397fa4855750ac33ae7b3cb3424cc2f3824913f5b8c55d4a2845a9c31dca6dda3105e2eed63e553302e279e4f6876b4efab6bc2c2c3003e82dab162121f6cac5a1515ea7c92fed38f70759f9d41de5176605734195f7711cc73dc307b608d2375dbd594fbd190f7fe41e728e53f131b2a3b9e70c36493444b03c3df3880c0ed2c93182b7cb1cd3af0e337e18a821bfc03684735e8011994f2263ba46921c6586385a1512b30d15d3eae3e1594a463c68a82898c0989dd643fb97967dbe15058f10f741bdf258ed802c145e845c51f968bb7e67f3f615d60171972ecbce54f2350a54a3cab7db3056cc16fa34085d602ec20e677032791e01b8bfc87e4d0c31ccddd2d8485a316e10d1adfe27de7b3cb561fec6b7034fc6feae1028086b53efad1b2e0fd72c78e94645e658209b4192957f1064d4a96a5c15ed1504f4e8661fd8794dbefcd7da1189f1a5530224acb6ef322ac62b0f10bd82258fdbaba321a4e8788b92d18a1b9517fef67b36dab4cb1842eab177034944bf909cbf398a8ab54197a9690d41a092a58edc5e2008f9b185f57873d40217caf86f022238517fc370e8f531726cf77fedb8e8c56ce37d791be061bc6d70d431895687f869a8cdf5571a556ac6cf7b3f3e7a901014ae8891b352d75bf88e6f3217552d045524790458bf034b86278016bc2f9b6d214e776d9b9d054ee93bcb7c0729f0c055f4917feb3fa71ad7ba52dbccf89bb4122218b8843c1e1d2adc2ae0e14c91e2b119c93acc19c1f2758b36aab1e35a9ee584cca7468b466fc14eddae336c4387adf6d0feec1a93c7083843fe8ddbc107c177a3d8786bcf2627ef96247aad11608d2a3ebca874fbc0a4da1e6f027e227a6147594e2d5766cb6ccd344eb950edd955863364653ea8fb740c5c813b23cd043e1c1da62753a302334a58ea7342aeef36bf715d3fddab0c52be8c820a6508d4f7075f39edb987c3b3f96199ef5e909adff3f5e6a46cd6bfe94c3a28cc663d636433134f7fbe29b0948625c59abb58ec2e638f95e7efcdb4b35e73804fcdf55f256dabc5e65563ca449056830eea09c77e89b593f326d62a53a39fc64f2e3be334064036a50270ead0a328fdbcef80a9a657f7cf7ec5c040fd55762bb797539a8af7cb3c736e8909b16f583f7fb20507ce89b04a1d26b8a1b75e02e9f21173414ed90ceac077af06d547cadfa2a2ef8dd82a3366505a2e46b2a465f3dc4afbccecaaa9505e8a51080637f927c1a532810317c4b5c41cc125be50e0d7dd5dcac002b0bdd5d7abffeb8f60a7c3672863cea9374ae840a817cbbbf1cf4cd34ec879e5dc10bcf2adfafd2610bc8b5772ae8bd4292b231243f7663126f7f36a373142dd6c828ab222a220515e44df020b13dbf1946fcc54468f66e1991df09c9b26a4cd7dd3f536e7f3118400d3c68d4a9e577559936942c6f8df8d1770ccef18d3cfbfb10a8dd4e192d2778716894e7ab900da978596dc9ab8756387a280ba2496d9e0958dd49f79736949cc00de151cf5062cf0a96d37a851471e0d6b87e315921961baf0d71fb19e21f1d798acbc8cf0cb69dc68d8ead7a8ccaa4b6126a340cdb72d696ccfa76cb36d2fe90f016c30de5bec85b416be3a8c6438d3bfdc6e045024f0d1e7ce5ff2af7df634f367b0cc005220c1c7c80164f03db5ed11d956b4d03d6f57a642fec75248398995d6a6b704300a12346dfbf2b497e867480b304777da8bf908b848279fdf22df967c2e5224f0329b58c91132fa91f1875ae973906221a14221cc9fe32316620c4ef13d3ac78487296f33d47ad2a95e17360eed38b632f5c1bb5931021bedc4b9f53f7682ab690ba487f6fce72811080ca5d3fa7f96914ea14ec2638dc00cc90a7ca0bc15a95f6e8940161a9d1ed36a097cd876eb01378cf4223462cce1d37982294602996cb60c4bcdb56fa228cce7823782308e1a733d1a9b3b5db85332cd9f29c9d8cdf1de1ab8e8ba06defd1c4d3b4c34dbfce45a94e1630186e3467a2bf0eee477acd33d04b9e7b85f4560d9a2dde124475bc3da5831b81c44009cf8b266572d85d1447262b289e08b555e1a30e66e4a63bbcafa224f9ffa39d47d2eb87b38e64cc7d24019f622a46ae4dfcb5692ebf43c332cf2d13a0189bae1ac5ea0e8c51e8466d57d36448600d1212125798c7ab76cb7ee5281315109ce8ec5f781acd5f389570417467d87ab5e9d7a194e234fa69588259a07a2a57b72d1969ba3df5ab6a5cd7ba871088bbed5cf8a1b113f2f1ff0450adca4c32d49d937e43c43c219c460e4c138ac302fbb38fd15000fb257185f06c6052a1d847210978602b07dc9aaeba57e49a9cf8812bdd960693ab923d9a466a66af1a01e5730915eb2240dd46e78cee481ce7d4991e3f2e82ca47b30e76f167e6f28c2e3439f6b04c0abef516dc39aee1d156385353995b08b584fab4b09e9426127231d07c7ebab9e38244c7ff579c02ab9964a769b49417f95a9395b3326f29a3ace2dae2a7436cb68eb3c1af14a51714aba71b1cf32f8f86d83874143c29d03ee93fc06c17133f8e5c865a7384712517c0e68003b379ba349953f2b9ef38f2ff5e6f84e5b50be7867326a9bef68b504c7a18101972939b4427e6012d7a21a79e2b6f8559ee3410dba5001174ab18e05af7b992175131a92d768631dfdb477ea8abbd0854fe26e5078f5a943bee104f87f99da0a53adfb98f1cb698e0ab7fe4f1f8c129d5cd87afbabeb2fefdef7a1b1c4b28a8d81c0309aa1c5b27601912501e6f99ccf0cda59b51b325e178afc802c29c7b2a2331af7a2e66617c43e91bdd7a12b022679f966fd71cfdc6f2e0d50600affbeb9d9b57186feb1694ff16b4c8602221fbdc9472de885986074907e813060cf1a7a86646eafdc9ecfa1295f99d374689d2376f81a2389da2fc28f660a74e96830e8b85ea2b97d3d748f1e1d455cc27a683a81ef492e49fb468b5647e7ab8caaa5d884825ed8c97e018617036318ab49e7fdcb611937e1bd2bfc9167c6b250126987bec8c01eb19ed2bc9137f9bd7bfbcb78e72eb7687cab48f72df5748885124d1a231ad281be19167d1c6798849e32ff58fbd1daab050915fc2da7e80b0b74b4dd71c0ecc604fe4e092d673624649239b31d97304f29c5f1608520f09b8a27add56e1a4cdb825d053c0d85ca6b9f6f63b476af061ed22e29496bbab605486acf102522090d2b7fb7990cd2e23f40163bff5f9cb040bc779b85acfab09399fc8aaff494ae1dfa57a7356f98e7f929f17a7b0cba37c83d4f6d8c9f6cbc4a688d7fc5c98912c8ceb16d20ff0562e5fc6db66c7c4266db30c7dab25ed3c36a62427d6dc80b35e88ef2c44ef32664c5c8319432d7f90c8eaa6ed7ddb3e5a6eefeb60558bb0e2f3c23c4b2bcbb1099d12bb421d76d6d9299cd63210834632e8bebcab210b2523091b56b68e673510a86518e87555e953e273c1fba0da6d09248d61dd6ee6277cf181d2a44c82702215bc215c4b12a6e498936421d5c0a94c28fe9a43829bc2632c91571f00dd1198726c272a0b2578c2d922a1d65f3f01805c113447d81152ec3015ba9e1d0fbed6a9e0769536f81f9ff940d2a3eaf80158b23e87dba34a1c96617015bee56cdd86ffe042c3441e49740e9ff704efcd9e07ef1c882cb4f510cd20720b6da1bcea32c2bb1660e49137d62eaa1e82ed4cc68866de405c12f6326b15bf058a97176665d6904684c149e024842d08838795b1f2c2b9ed0c870d84b23daa7d107220f8b2baf98a3cf7e5a4bf05c9c375dae76cb86e689a760585ee17982f72294d45e8b910524c82b5ac1870868159e58b468576135eaef9112aaba7bdd138c4a282c24c4d6461aab9aadbe2af7be416cbb1604c3ebaebdf9e9561981a31082907be7927007e56f4ea0c1ed0b48ec2537abb90291056f2219e9d482da35527d69e4bdfebab8a695b292f0e0b74bed81764dbdaa097491d9e0a789e9b9d098b77b2ca684b095a4884d0e640bfa9b85a93262736917045753305384cbd7c2dc96ed39603f4a8019d9894856e28d46156390f3976d1f8850d4b58159be2712bf8dd77415ddd61df4ffc3d21f478e790812b02b4e7dda43c0e60e8433f5c89e6c48600f64fcde808cc17f3eb97edf1d53f633ff5e260122f7b146727ec4730459908d84a5e69144a7657055f4c09f8b3673b5422173689e6e1747f72683250c4478074a6585272e59e5f8e3d06528e4f1b742bbf8e64edb68d33964442a92f63d4adb6004a68a711f9d6a16496aa04d08c5c911285b785ee371dce0825f4152f9c6c4c515608093ff5490b5fd5b128438ae5d4d09c39e4f025ae9c69433b8cc292ef831e122e4b7d48389fee4caf5b31fff04d18241479a9aec54dfa28b3d81efb960ffa2718dbcf42ed696faa08513ac1d8261b51331a0be62109f81f4bf6ead296aafdd24bf0ee9ad7ba21f52a9b18bd5974669684ceb7a2b8f6bbf58b9c1af0062febae782284d7963f8a509a854b520b931f8505f7ccefab7c83281e84239b6d0052e78ab2a92c171e7754f9f5a4c88b69e89a24a77abe9242831d94b5fc90a49ac0d4dee2106398a6782a38734f828b32188c03db7c4ec14a65b3ad8f9eac97623a2aa8bcdcafa82edc32aca002835fa2d377185093a6976ac7026fc5ce7899358594afe1ed5cd281afa534f72c46940314632916908d12588f5fe46ad917da775e32f1c862c8ad45463600a464c257c20ffb3326665093f3add3d018210344ce57c1764dc3490ed4eac6924d6521fcfbe09abde9cebe58a3bd133c1dc1b787363f5927b39f93ad9fdebea52524c7df95ce93cec5d1cbe5df269af779b18838474efc3d7dd86f73300833232912ded525acf39839e6d15f64f06a0b2ee196fef7c3e8e56ab899944f50b40841db255b76c418f758fbb036d8ecb1faa5874f4936a3a76f21579681c29be718238934ef0a3e019814a174c42e899c7c34fae5c5b681713e8ec24313724f880c0c65fa3584668f6ce3e8480d8fa02adc449f31762cc1c43506c5cab71e5fb0280f48629d35093b480ea2961030a76163e00c264053098628be9f86ab32fcfc6d87245d1c641abb18d9a3f2c8b239caa013aa07ac244134eba0347262d307a0022bc8d6652411699c1a00690e08a7498e3eb5f77b6f52b8ed0b6649b49e71abb5dafb5c7e7dc2d403fc5cb8d7feaf5e5f2fe564f832a3e04175e41df9b9bbdca8d4696e5287e6de33b86ee92d5938c6e160eb56f684cad57a2e7860cc1d33b554cb5df92197f45ba240896de3e53362acc0bb9deb662a1cf73b8653a4d4a73b8b9823d5c6ac2acc275d6d0fc5c56228719fbf1b972f2f040fa793a78d532cc73e9889bd97014fd488a801483da7fa081a691dda23892e787cc5ea7077d933f5d120acefd10e97405c3777ab30f62f5b5aba3afd1f91444e3b0f6ee2040e5761ac2f6f8693700a16f589c75fcb02db6b787ee3a9d96cd4c2732d6987d9a3913f712dd185095f20436d49291474a22187b1669830d1c61c2c1d15f538f2fee4fa0fafde6d07740e150e795ec55819f7adacec7995742fe397e3b7f5f506059a00722c86db5a9147a3d79d6cf421802cc3f475c7cc33f977d6856b70acbcd9f6dd44dc09ccb807115f44b5386cba9fddb81b3184e644e2f98db0c7a3234f80efee3502c3427be286134f1e168ac8908e9480a98613ca6177896d5becefe406da87dd7ceee004bfdbcb3b31f22b1f08d954f517b14d40f16e847ca2404d83b58ef708514d83377414006c3b31ed2c4863540661eb61e9fbd17fd44f887d918068ef24f282e4f00860b69f27f94bc086718c74af26e29474e145bb1bfd34062edac4a6f98cb6d14809141b4caa842e55a96e3d3cf531ee382dc0fc248f92ac7add55c558cd0454358ae2b8b14c7fa288a73b604149f07da660cbfcffd374aae9887fa45eb24ffe13a8555aa427bc6c9ce24074c9ebabf640b780a4ed4a41dd524ec96d7b9ae7d594dee788744c58b9c38186a402c1edf7eb31177f46c1bc72b3314a8382172fc70f71ba9241b73203b3843e96e6d1cc56af5dd0f9d0e67ed50566b43a44d1a83452d805fba5c712d104ed10c64cdfcc0342ae9c2a68f49402aa3ef89b00b17f026fc45f784edcdc104a52e8b90943a2840eb067d96471b4d9c49da532c6171a9706a9ef2f94441b6debb794ad98d415788c093ec658c5570640324d361403103b1ee9868da36fb575ef26cba00316b058c0b806854484a2a89fc89bb3407d434001fa169b3baef861867287a44333d30008dbee85ae4283fb975e03ca4e0d0096187e68522d23041255aeae14d9bf577901d28dfc95cc78c91bf8861e2e2630d545ec598bbadc88ed5424c28a3571e5faaac383ee736ccc2cf301b115317181655804ecd730e4374797abe8dbf1fdd5aa4b9fcefb2936f86eee65d12ccb7a99facbfdb6370b9007cbd9f21be60ee367d33950719e6ca321f9682a0b934e85d0af489f0981837ca4274990bf142b1f6415e1a8c2e573c0513cdf5918ab1247e56c1325e670b85613781dc446028d0a541ffea9efad700268e88b309baea1fe60ed018288fb9a46e9740c9fc6113de585b19b575cb98f6e1f256fe18818d4b3f2e26e84dc7b5f8a8f271c54a460f4937a9fe04136a70cd2e21db5edb88ccdd7b91f88a6d213aa360490d333f79755948b6b4cbad40c19f502c2dc851157cbc33e6d8c0108a4702d60f3fcae3cf8108340dbe0770fc09f10b022ded9a0c646cea8427903ec38540fa5160ac99a4052124b0d0adc090f61e4e1a42dfd850f8914d926d3f3462e854820b2e7fdea9d30325a4dbe80578176f946880c65abe118b3447c1c9a580c8533d2af7ddb3d091c582ba86452d887c25d8cc10a6ff15ca7a9b2aee1df554ba657e81b53ccc07f404f396178eac18da74e3474f95141a1b1da5ec812f34548682b239dbe085f98b725444c65826c86bcf522301dfac9596293648a97d3f4e97321d162b14d01f97aac08859ce4d6c5cdef09fa4f35dad7a45a49e16d270e1aed18fe4711680d6ba7d1622b4d4adbc3323a68416c6ff6a000cf9d0d350378f6ab37017a0fc3139fd4709a39431348ef454fe579791161990bc55bf260866acd3e76069f6dc2f4a4dad70b1577799f294ebd12f4340012e1bdd61f46ccfab4a0006c11789747cc56ded7afce0f1f84a9c1cf0545d5cad8a5ba209749f22562c8b9f38c9f7f61c8fc2ff46c8bfd9d9ea9ea3b2dfd0a0b833f4c6396f950daf85407014c63656448089dedcf0864da99071ab681ea6ec5299b6f451b35a3356c8a0375e28bb58bd15f60f5d37568afbba048811f2ad52d2eb362386c535ca510c20a1f81373b744b29f80864be2ff16ad4205b60f0134f79343659fda65fd95a2d68560e0bd2dd4af9679eb2da8cd724cb6c456c332be37f2e8dfd0125f4ba2ca669891bca848227255df1cc2c04e6b89ad707c5e1b2ee62aef5d35e09933561d087273d8f2d68d96ee3159d3573938a38ee3ad42895b77c140e387de559aaf4181eb26c67dfa1cabaffe2a4c922d4814c0729e83ebecd7e37c3e0c32dda1d4c6d4dac510c5a3cb94a65b4d5eaf3ab48298e443e34511767783456d6bebc32c0b397b38a600b07c4a8f578d4983fd7d3533c569e2e0c85b11cc08b98eacc0a0c89af423173a5ee44407fc5bb68ef30e9084b361f6a8553e505bdd0c8cb079936b3945e48f686f2428101142be63548b9c3729543485c7a10098efbc059cdda9052609b07c513cc93ddabf8782465734685a6d319092f4fe80b107336bd3ac521cb5731b3708ced5ded4272822663e7d28a520c7a417869e3fcb9ffd9f763fae715e6118f999bc8c650d5e42f85c2b92748828481b809442f741aca1334293f29308a15cdb0a1d089c3c457677a1c15f9082519fce785898efcb14ea85b0b483c8a315404cc8631e3d5a26cc8319b7385bda59b9258abbd25bc621f66d06c357191d6afa87ae22fe2d938b5442ae72f40d4d501dc989a97eb8057be41d5b0c794aadec173078fe48461c1ed08f46796267e551484df366497316864db6405320286de1d25e678e5aca70efc198a07ec0bfa5fe8529f36a44489dbca491db919626f32e8197d03499bf41ede1571d032239e020dd15de620e36697543158863a3e5ccc57331473de64278d852dbf0bbbfe6a4e9f03ea3658d6625552edac869b80c5f47fe136969d2a99c890904b8c441fd889c6e405e44b99882d58b4675fdf1371efd3dbcb445cfb6b1b7c1519f4d462e92c1289437282c262999f22c562376310c473ac76beb94be7de467f0a088c003aacbca25e314421e50714dc8a13d5ffe55ef300252ef14de2c337cc3b19b650a4e87dbb4d2b28d5776754308aeed5a0174913b3500e98785025aa8b38860318b0f0e522f7d6efe54e33314b63db66f631eecef47d8cf9944f3a14d988793a0c764cfd3a920441e37d2523df11bd48b5ad9ca9e6a75c059875821a6586d797096aa1e8b51005236a3d8a92a6402a8cefcfa5515989b6c9aaa37c1e1457bff963407ddaa3fe0e93a466956e7aae3f4e1abc0b62560ff1fab4773388523e57d4bc54ac86bd8a6aad0c3e730b1ba140f34b1eaa1d5200b76522724b50120f30840ed346aac4147f52748f287e4f361d0a6cd651f43be418f6d09c8931fb97b4a3c268d668c13db1cd37866ce4f63026254be4c972b8d545d66e9b165979f901d9c800c995facc899b1e0a8ffa7eb119af58b0d9f0291a5dfd9797c4a0e855285e8fd1f3362d1e2a59a6b55d35007be1b9d19c585f4fbc0e3126e4ffb5faee8a955c38bb1fb1e0f5eff0f2868ad95ac40758310abe054be014e6ff95451e0b1a5282b3c244447e911e91b017b6b3e63a274d41a3797024b03aee6f6d1552626ed6178bf7539ff9ee6b6d16cca12283ff9268c93a269cfe6dfe0fe42b0f13c55b8e65004a3091c13c8a997ea47c5b28a2e591ab484770dd25dd7daf7f10dfdc5e1b94047ba097e8830aec009a467d07d45fc7e9bae25224994b502e50a2942d8ea26c2e23b3d77823b58788750eace71e8623f202c3bca507b922626767e0ed54735e13a5a9d06636bfdc544e41baea23f78a836ac38d8fc68c2e68df951a8e6b220f740c502206f15239ef07e8e2a334988148f5fca2e31bcae505d4ef968d3ff32230d9e460236c006283581a254bdab603ffeef5b604f0de8802551b857e7dd4f7cbeaf4625bf8160f8293a4afad2c7fde4b2bfdf9805e9afb7e74192c4ac9c62dbb5c41c0c42a7733b0789f1f295d5e51e4bf72e3f0a16b4b468fc5499d1405c4324d83c8767466bdc426a0bfa2c3f04d5f1a165124d957e6a4a957f3fceedd46b06d17aa2ee9dcf21c3c570da5eb06bd1dfb818a8824f547c3aa07a7370be44d960c78b027295e4faa86467474c6ad3d3da1968b3bb386c3d940c51890869f827f22bc494732a3c9ac28a0aa4b4a1059923f58d7d652ca69854465442058c8d6d313adab4f8012b5886d362f4b20af051fc4f148408168817e5fc652a59cf06e305503f69275ec4514e63b67581b62738c4e7210be9599ce10517396453ce9c9c645edc4a7b7671464f0f445db667340642ed152649035398165986b06bb0e0de92fa4980511891a88cce8ba3023206b520637c0220a28aec10603ca49f44229108cc310281f4c794d8c230f67a8a26af4a97c48eaeae2bbccd208aaad47d17ee3fc3fc6820f930b2d08b24ff8f85313261b9387febec8df40e8581bfaa1a4b37ba011a6ff5c7dad37356ba7d54e30e6a12d05c8748c30911cf181d466e68d08eb6552cdc3cbea3d41da6207111553b552836ed0895244f7a8e59873c51c94fd64ec05dbb2d832b36cea55218bbfbd6e9d6c5cb60b1bb53ee7b31da9399bb726a7a4539022aec7e6ef702161aa1a8d353272b6e5e2c53e7b5848fc10c0332e0998748b8f96de056e0a8343455ae08495445a9ea3497a6947c1c44b8b55c59f181ca32bbf5ac977a4e3f8859bba9e8ee848f98b429f1e87f9bf5d4c633d13b5d66805264983806d198bad46408499d6039e32890a03d1525dae4d2b41005a9478d652f8887c8c8876c28f5430527747c9b60e367a67ef173321035e5fb5ac0fd7f3f1fe550a12db7cc11586895dfea22addc09eac023948733a18daea1b12210260d82a5e050b8adf19e1952b035a726255660cf1c2e537f10f31947c5c3b8ca425135d8c9a7d069d5e6113b95bb0e4b8696d189725cd37ad1a7fefe0453fc961c43c4c3152c3bf121e93aaa27368af0c9e0f6626ee522fe29c9c31be7b24e055efb904c06de4d902788d22fd597ec1c6f0f799ed3d3c306dc851cf6a820eae3cf9cdaf7a340a5b829a073b9e667961917a274ccc64c29814eb2fb797a4a78cdca731968e8bb74d737b1a0ae757585113f401cf481d14327c9a47bbd009514a1f54e489fc1c06832c95274986ce4318a85727bd2d2c801cef0e3d9821d9692c9571c7c93ed8bd5b25b66f37e5676207d2ec25beddee3e00ca03186a2329b0b36a2a1822cc4aa2cbc20dd6dd02beccd88c64964d7c087b9755b8ef7b46d143401fa452b2e48c93a4b3024ba23c547f83e9760ecc9b32fc9dce84c0749aae7e311f8439899b66689adfdfe6b90916035beada0719357a783083b594ef2f106742a4c9b7066630293ef3ee429fac61fa1e56b80f5de1b8d87c608f73e39956d33746f635f0b06fae20779982c22cfbc658792e6218632a86b88d742bcdd943283db872de510b2524d41d41eddb30e8c554af26fc325fc328d7eaf3582fddfd8699afdc492d67eb5388cde028af3f11b266bc2f16b376b825dea1b12504df79f355d73ddf95b2b6faa791daeaeea72e96741d9545ad722cfc7735f3ff43bfb00981b85d95fe422314fc4ee9c0d1436c638c7092759c7414ff3f621278d761fd26900db0b0c1c81c31f825b30c05717a00e453b90f46d27ad9611b425804e35b8cc6a5aa945e523097cedde937014d0639e067ca39b49fa6db5405b0114e19b57eb41f00fa530a576a27646f14956e131930b0746d3eb30d7991e6c00d4eea35721f9d487156dd72b46ef938efcf671f23af525064d7db14f0c7b0eb55d6291c8a7af25f048b28b280ec182d3306beda17e27140f3d97990306247fd9708e2c784af4d23ab07ae50e3f6171ce89f9c112a707c18f0d0031f2bd183190f8ad15d5b8aff87397b458230f7716ba765cd59355fd48ed9a83e4223cdf5593536f36a22801e5ac66976e0bc2201b4307c283eda31de2a4e766ea41a0ec24182b65b00d85bb70fb6fde2fe18b66c0a6107c584e069a5d2299bd4762f26c69bd46eb508e7e048a314089dff082ef12c5b665c9320a4f646bea134b40bd62de82ee7b7c6cb6f0a3105f805c406dc4ee6dc1d69a574ea079d58044f7317d68eb596e791f88d21e4be647334d95f2e3f6f0782d3402f0dd28b066e35d5eb796a064eef403253534930bd081669651f336d02317fcae3b9274899d810eea0fa10827b6ee49f30b5dca556c30de0d86980d1578d1d7bdd3583d5ec69a115913f904a925598df195add396d62381fc222198d7d1b25af5fdb6ae1641285129de9549c5850083304b91d4d83ace21c07e25db7455d4e5ef650c212ce6f3e27bcbbbad0d08eae288ed29c1b176d76b340712a825f0d0bb012e1861468d4ecece9114209cdb28ac4f21ec2ef09fca3e64ea060fa3043e764611e7fdbf6f40984485ef10c2e05257e7c33f624c310bd77ba058f108878e1c7638d1e2ff1deb6ce50d5c771f3168729fa3bb2a90cc33c7a1560340cd0a5bdc69cf6723aa5726fc7727386748f94c71f21764eadce8449524ed849975c27db335c8f412f6039bd74b2d5ac33cd2b91ade5182c1e66b84cac155113ea784dc54a48d1d9c77a4d5f3eac5eccab494cd6a5986c67775764e563d8723fc1d8c6a030161b4e410827337ae47bfc448e33ffc35a8c578351bd027cbc7f172c4a91d79db4ee63335a0123fd4348140459a8df6ac1ee728aa7af9cf33b1deb9204a5726e07bc4dc6761f4a3a62b7a4403f2c918e70383a10b1fe2c6b0f95a817f5c7c6029736c3ef35ab2aaa70adbd5ffd2884e7702fc1382a4fb1bad5973b3a33db5f8a8848f8b3b8e8370e6dee0f3e36b476be9a366293bcb1f38e5752831fb5b35b0dd70c9a67a8e55c6b31bc80abba06a2b52cd27ba8841e239ab665161624b8f2ad613e6160acddcbfea09057023fc87125efede61d3f17ff55fd4f20e86b80ab0abe9549ffe8850fa97eb56066a5d0c87ce55db4e97624d04c80ef0007d7127c66a4199b0f0c9d502e2a1e02664f82fcbe5d7ed4f1b61428b2fc943bdb838d5681779ec318a41f90427ca2535534e40c913e5f88dc801777cf49d8a6cff4a7ad7d64bc0108d20949e0e8b38fa1a9e8f525f54a25e8cf7144b7fe1e9373dc7833c3fa36d75ba6bf9f98faeccb47a6f898137563b70be390084139a88647faddd3b1ae983f450f69963a09b11a9ab24ebee9ea7626fe9a1403378cbe66e8ed8fd89a8973942fa3facccf640ba707c62a74547b2fa3219d1d719c35c447735c1b17ee32af54f84ada0f43b1fc75db790bd376208167e7c6b4fae36f7d35c0e8ef1bc865feef9f1c6c5c0845ac25604523e6120c0a0a0ace96a37e16182767b2de7731398ce8b9ea3e71d0d6ef448fb76dd033d57c32438e968159f6c7a4ae1bcf391f0905618c2dc0ca616f29f27bf12ecc9b9c34db97905d2300cb1f41c797095cc04ea876213914edc1c181b495263bd79111f88a692af0be3cbbdc27d87fd13d50a8aa10096d018c730d2792bcb4950d83065d4e8ac71416e0bedca6df1619fe92fa6703b54fcea409e13190eeb8793f6b7e51d18364ee65a3f1abc2ac200988fbcac149368d9cada14021d9323b5b4146d4d2911ff541a214d3a9ec8a47f9318289e62c4f50dd03909d630a83f5c972615de01796da0c6e891748e46fb1ba7b21aaab99762131d35be9ecc6cfe4ea2567de01b5059ebef108980afcfa6b9886a9c0cd5f58c35c76aa15a84d2d1ee7e3cb89640784e221c718e7727a8ddfd7634495705eb2163483866c835f1da7dbb26be43c78dac81b522a33dae274182363beb1ddf8f34254723e38d5c2714dc03baf7dc39ea6d5e712d72b2ccd688f0c17340a2d884acd0eec85995074923a94269424c21ac960de7209ba92e66d22e7a1903906753fcc72a5651b7f305e8251ff1cc5d26cf28c7904600e19db9a0770fa1847bc8b46c284778f92876572414e3bf4b8238f26d4a3989613a37324746eaef9490ac5a79acd3b07bfe1b54e595905e8e8d6210794301fd1029c1a2117521cf24960d06004033123f8340e379f1efd1a6128ca7490718794c8b843bd0ad2493769d61eaf500b4e4595f3399f175bfcd4074370ba1b66aa7ae06a01f5a93929024e5013b13c51be99bc0dfcae6905ce5d26e4693182bb919913c708db2b50c78922b357e79d17e8f511995661d3944f860040e839d79947b01cfb02fb61685c96bfc98e1c1179155a460279963bd80797c0b3020fc2c8949218a9f84cc5b6745e177efaa3b0dd7373d81d6e86c93501a24a9381b7a7af122303a633284635f9004fb6966857d197473561541d52df0e11440b1b2c98d36fb3ea57cbb60223c5ec994e96fc1e1f62bdab25ab18506e8051494e8690cc55212b453869806eb49fdbd3c10f53676bf19789a7d16176dad5064be4998ace2bcf4086beea38e37acc4103eeb5dfdc977d93020c8635f8b2ecc2f5331e328d925b424e3aa7c421263ebda75ee65d68133c8ecad4b4ca7c8bd679404e676b43054020bbe98c897d040816d8640c235e1bd0a850615318a01ec1010f60e7a5f4916c0c68aed2eb622b1d3e80e97599e334cac4fb4c60f68fb30e7e8234e33cf9bdc3e3d8a0adef30821eacbe9b6585bb8e6da085aa2439a3ce9c23a048d9d2040bf0d56c7ac01461fd7decd5d122b9ffdc247d8c895c1dd3aee9b1d7f9dc2457688e816806a732a9a89835027fb85c2a439888d357f500fc900ae4b025636fccdb164b775ad9df4634b1d80ccc98e8467334c7b6437c8031ccc81afefb5e4c72acd4970b72a7493e65cc482fb10cb646ec33fcfbeb3c3154a4dff79b8ee65163f10a0a49cbf963389986cf2a20e9c88ac35bd1c561dc606b60941560045b9b636075622e3e62d716ad39249c46bba14553ba2cd3636668fd7ff6023dbcf92555b90c35dac7eea35b798513aab8a545cdd11211d0c963e724c9a215108504d95d74a5da1ece3201250b67c97d8451cb6c16856fe33cd7e169635dab03c8aaa4c38cd25d4a32efdd480ce27a3c23e3d301f848735f802ae1d1bcb961ba29cf318853e9bd988f85ddeadddb8404a8b4eb62c22cb0cafd38a394abef4e935098acd1f7d7ad5ab60c3ae3c334346bf4b1f42e4b2c5e6d7dab0f04640f988411f22e40815f6d9c779f0a6fee2fb20f4ddf5fd04bbfb104ca249c2c0da58f8f72aaff7c10ffcf0ec567e8a183db584c15599ee02d520b38a91faa48378f78ff94752b9da107b2036b9455416a21420ad2dc79fb86a1d8af8aa4580d05954f28782c1244fbae0eca6f51fcab4bd3a4737e00492fbc7a97548372bb4ceca2d973419dda5b19456dd2ee0e43d58e464f7cf4f07aafdc752fc63d434bb3f84d6821d73b2328c3864e9a709445698d6adb7afe4b5f9de1ebc8cbe39ae1090488ee1c1ff49de8587f495fbe8af114e8288aece73c691f730846a3beb68b8c120113e741bb09f3f01f29e9cf43b11cf43592e8d250075eb65bce3b3d8ea7b0f259deb52a352bef86a6bc2cc842ec58b5b6a24c7247d868e1de48d3270054f7b219537f901ba354f63f99d7fe0a8f0a7e004233f56369b253744db0e7f578e1469f01ffbc06789e987c20a9fd27f2d162e007d8b75debe8a462248e454f4e30d2bf01ee1d45cd02975a278d283aa64c3a22024ca63493f9925105aafcaf58a024dac84d2ddc8d03635a81f9e408ea3c44a1a07645a5f4d434e3714aec05395aec3504b8a6ba9a700ce749b11034058cf6105348605906c64c05b92d5244447616f8f92e634699954b5ff89a7f3db25f8748bfc0080581302a5bfedff3cf94bd3f8fedfa9c0693606e7b3cf054057059292fccd343c6a1e1fd3dbfd44976700249408f77486d53408a3d94400815ece9dd28da9f5761b90365a885f23425111688a34e86694c5450e85cf164f873a0cc8f0e6a477a1fd4f1c3a6c163e894aa33666aab85e4ae228b38a6bcd8d246e3b5373b21f49a0785d988e4f88f19a57b03b6ba491aaf0c2a9a82ba2dd781187ae2a5423443387c74afdf8430b993216b350f2daf9fbc02b75b0f4abc5b75f20a4e1c70b9218be10795ca591ab6c555c48e4db23fd72d042de8b8d7391a1dbd799dbffde01ee482db89f3b21104a181224a2183fe6bba824d19e5e56d71ed4146994123056522ba43fa06603053130d54c510f693dfc9df3bde8582eb624becefa9f90671d734193841195fc6763ba4531541ba670b2d7fb8b377049dd63b8a2f4da6a4df825c4ee395f567b305289e766e061e1592254f5b8e64a32e5fa74d1e587494735ea38e4b50d3ead22fb9141b6de6278e7edbd2c983d394954a15b7e7513b96ae4d8c654803c9ec509f21948a2699726366fb38ccfd5aec05c8908079800cfeddacd2f16a21d4debda295e9e7310c53e85463fa21f947612e2973de14301dda8a156611fe45a45bdea8146771dc81268104476e4e26386b02bdbcb5cbffe94a198780e342c9adec3a888de0c2d95e68864b270f60347113b45a08e5bd64869cd7fce95206b6c24d20517543805364a1b3ace9ccf0e134605b784e567320831266c2b154983954dfff7c5aed42a2da1e440619fd043ccbef5078e56e2560e0b08bbf0618f455d72a3661738f09cdba8968b7254ef9825749b54cbc6c577e951f651ea00867fd5b51f0b1b7c2c5c1298497b5cfe1bece5a25e061eafc3c21fb44ac486e51f2a07c28c86cb19310d69fb203beba7b5d67369281745a0ce63d3a1e69aa0e19aae30c3431e52753980f932585670eb196a1dd32a2ec1357cda02d15e0bc4ee9046b8ef64f8e4fa9ae933e99a504d62f87cb1f064869f8454d409908910be18c92467b0b85bc4cb88ce171b648b60e920265ea0d9503cf28a21643fe7f01b466607200c41b20b78208fd872725b2f290a4394365201ffce04ce51457d8f597fc65b32afa0f863cdcfcc44bab796a2f8a50cd99c78d0bd6e56685179aef2d08adaf9a0f14f37bc4978e5d5eae0c716429878998d1a0f4d05eb77365756362062e1c3b399e2c8f70a1fc15b7149684be39a7f91ac25e10d02cdc7d9ca62f4dfd27bd1a4a08d1c7140d14501e48bce06573186008a73549b70d6d76a4b97845c322065c4471cd397ca8599665824c1aee1fa4c6a991a0ea8bf10927e87b06c58488df63b5c908fcfb548c9fab41cbe2ca8d8dff7dd774f701e43ea235ccee5c3b46594dcbfab627c1d1a4f3794e5dc0fea3fa015e56828d4620c55eb7008e441318aaa7b08369f38a729627caa3b29e127c3fe60ff42b25a255236ada0ee793da3f85b373deed7a40849294991383a71d6ea7ad4bbc60dd4b3a2550a076b6574ed5c92463cde6603a24fa0ef801b96de591d67f17c64810bdde867d568624cc8996cfb0f789d9cfa00e36361782fb03f41f8655c52f27c250371c6296cf253fc38aa6482a5421302e1a61d33c274e75fa3465b3d2c82fc73d5e13a29d0bf41e81113307f0805a2114500cd3a1322cbabdf926ff782f3c1f3fdc8776e554a67688ea266f875ca5cd67117a6925b87a42ca268820f17b723806e39d84a7d3bee1039fc7411a4d68fa12c28504627bcbd972face4321a0d0a16a10c8f106611b0bcabc8b827f87d4cdb1c5186cdcca5eaa9e4d88992806d183c83a609ca0cb808dc97c045b9e3650eb3f97db11d765870f2f7aafd8965120be1a527c412759873cbcc18e422832dcd251df298aeb532cdd6937b9df7015f59ede79b4c6be721014bce3907cb8a213ed6c11f5a20d6d1c9c01e70c1250b637a43e316861e89e784519816bbb591409604b4f2a6c05c41b8c7d00ba9e7498aa2e81a97e1a3e61fd0e664a8a9882b1393d52f2d5cd74c6ebc536f3cc88ff6c9c26ca1cdf373b26e1ba808d55fb9032d9bcfc3265590b76450975a7545a4f6a9910880f489f19c5e22d514c6309daf4fd5bb99fc1ee435e6d4825227938d0d44c8ed84181cdcd64fc619b8078e7efa1b41f867565243e7a4df6b848c45c7463bc9005a555e36b6758b989b0de2215ddd522711feff2ecd1b9d1976b1136beff1acc972832bb32fd3a251a7c664c53bea97977c603702f52a97e76e709ef7a907cb6c3df473871c5f2198816b8c49113d8625dd2b29d2aeddd3098feae5e123b6b0427d5182061f20a419408c5a270d9c20f348fcbfe757fd7e1cd649f00b952ddb1b689b02ca0529d9201b45595bd5bc74096642b98511887dbdea458fbcb4f17a0331d05210fb0db8fda2665cf8fe9d3b025c9ef5cba92c1ee1c2f1bbf658466ff92fb94a10e573606dca60fb9a9d48e19079bc4973af61bcefad26e1f0af450b5a20f1bbef02dcc2243a384f9c2b9a0b8a16d8eee11d16a75d90b071a1ef36e87b24ce2b176f039f141fd5fc6ec3bd8c5d5fc58080b7ce5f4abeff2e64302f533589998b70318d1a1757c81a96cc4f15001a9db05c7969847173e41528652ec5bdb8aa53f846c1eb1a12620b7919f531dec9d6ade6827cb385ed50dd0f4b86b5441a835d1baed5c427991a17e4d55c74b35a8e1a33e804ef1c244f9d3cf3f7bf8800d792c7a01c53c0717ab76fac58de4d88ae027d222e6ed9896c36b3813dc52f78f011ece44f2eda347cfe021d5f81da141073e21910383f1dda86ef1f670a6d6fb9ba22ac7cc2c3c574356e28ca030e3b47771f8278cf6839420bbe1b36563313d400a68ee2b1977ad9ff6a3c462a53bc5c23372b9faa7fa51778dc557b6fd3b5f7a9f3fbefe91bdd8142edbbbda36c3904a44a71ffb9c85e06f99fd5847411e02a0875b4ca20cc73b0c402b7466e99aa05cb36f42102753802ed41fee5d20850bcbcdd2a1e38b1b179c06ff560bf6a04286c1cb1fb9e659abe27de4d7b2d77df57f6941601647e61a9cb45623dc81ae6d8acf1b32c331074acf43d4f00f8d557c32348e5af99d12aaf931411fc674dfceeb72e9ef41bfa14f0986b436a3ae3c9b7a76988f8106e19d545e1c14c9f3e210b8c2a6609f4fb1edc7dedd0884e39d6f7c695cc1a1b297c5525243515ef9827503a7a964f859561613d85d1d671598aa8ec0d0b52bc2619cba984cb29b04a6769e9384d6544f6c0e2344e9394e95640727ac12634bb397b5b0be4d7984d44adcad1930ec2376718a47f4aaa3e50c2e80a17eefe557a9a488ffbfae4a5db4c0e20b3f95744f306e27c030bf1937eeb88e31e4005100a672e98e10be4ec695b8922f6a1b9ca45fe265e7481694a0473bedec1446d227f12e45a2c1c6202113c0c2d6d020d55b0b00bfd042c30d085acf9d97a72211fa086896c4749c628658f6227e77ee2883af0d9998b7f74b8d28afb20f40ff81ec88cc0a836086301aa723fed2b6b12191e234002953e1f29077c7d5c4133a1a714408fe0b3208b9b4acb131c23e169eae43e26c4abf84b6bb5ec2697728ff7342df37d16b7db7f71254855da5cece9b7ee7e664580289d114bdf51dcb35a4f2321bd6096c4b6ead2e8489022cdebf1569caa87ccfb678f2e51e6f361896527aa72c7aad9725f0186e27bc6698de3a10c2bd2794cdd799395aeffb155e7ee1e24af8959f49f0998d19ccfa929036273a9b99567b60b74f63159a531b180818461d12d83749e4bab2bc2ccd70892885700c96561897aa157c6e3a8cb287cfe3185c8ca58c7b26b56f65fc649c46577d0ec613c1536504a2c8ee98d96ce1d14a2f74040faa88f4d927b9290ecbe5fa3865f07a516c60d1adccc83ea904f84436950a62252ffaf025eab46defeeab7e1c69dff6829226e19ba21f2c22abd6a1e4990767a3141a09c457bca2f01adbea4d369a69a90e14731a5eec4c0c8c202b115ea2d42a18284805971bcbd0d68ed64a677d1755f57219e8e05237416655304278c8bb62e355bd786185320e810eb1c9da9d77ce0e6a0c0c346201fdb0b4cec9755c30889ea586f9bd2955d12bb6947936711f0359820338e159de5a5b456c07f9760ce551cb69dee023b203360ba4ba046fe533552d751aff4262dcd52dbc1144596339b0084ac985e9ac74ee13711cd442a5288665a0336fe00779b5f672b0363625d03457639ddd1d5f99c008e97549d534a00b062c967bc903a2afdb693eac0971aa2b9d4c6b137122bc1bd204cb94f2eb8c83241cbc9b01cb2c11ad90a859a2501e7109fe3898f25e1dc8785b568aa6b52530b60fb65fa5e2bf26288bf04226407784c3e423a2829a8dd42753592092a412274e55ea3fc61aae98280711a98c5b44630f9904430cdfba850710825fe633d976c52ed68e30c4cad09ce9c6f86609dcc42a0600d1ed32e1019f4968f0c9e9037d4cb14a026c43ebdcc01418955244a3acfc884bf57f9bc9775efd042d2fb2a9f2140e04dfd6f49aaf8fe12a66617cb83b5ecd5ad43975fed3ebb98c05213e38814ae175af441348a6a5d2fa1c2d267ae585a5cbe405c1fca11ba836d2c7b859607628d4bb36bd35f9d099d42bf1d7a0576d4ce5249bb3307f6e02fb083cafc45f2948ead1f3ba62c855599e67cad77b2003952f3c4faf664230f3035ec4e17de64486ecf538f07f874bc5e4c8e6ef750c9da8825a72aad5576dd696b18ea3043bdabdf3f075165e47c74966756958002c9da691d4c8ce7a4e0031ae9409203fec682910ba309ff3454c8ee516de77c402364e825a0866d698f86f3f997d1256e6ec315282209448f03faadfa6259a5dda06e51fa5d2edb752aa5c15d279262737a855a4125752557be32ea86598f57d30227f9e03f0e088fb3be07aac2e631b6c7b0eea3102601120cafd58a4e7c6c35c955e3dc84e0dc802e0a14fc9158c2e302446251b0f565db17508935ae8c99dd6e9176f28e67fb2e70b992b6cddd6472e93a36ac61a1c8f836ae78ac52344c9a1402cfd5e75077dcafd9432c5a45706ef69dbfa6a1805c5c93dbb205f20741ed11ce9f9598eb5fdaa627c55f6a71b54ba6acd65b7efc2b195b4639e757cf718b2c56a47469dcc2c316f5efbca744f3fb6318213eeb04dec87927a4e478923c8bf8edec87ccab90b8ec0af9f4d8c9db6bd7b48f09892a4d93538b3e6fc24e69e1baa3564848536f7bfebf6a5e6f02b481c77421a39ba0df2bab9c236aaf792455d93c8da050459e5cdc826cf50429267905579a3e54da2404c1f3564f3357c6772e89c22cd3ef6eb3b58b00fef7f644102e41afabff41a4f6cec36945dab512cbb8867c1e4f8bb4f65bb91ddc04e68432dff344d15a05596271a8c8a7893db67cf62fc31f69b27bf7b5fa7ee7261f6a2fcaf8d4c254b20171244aca7129f4b1cc25586454e3dfa250ec58c5e24b0e96eb730864533884b9f22704cba2d35634e5477c28567ae4544ed797756557f6909eef10eeeffd3c2c01fd860142d20b0944565b9e5281cad5e461adad9db7569b10cbe56ffbcb39081132caba43b6202a856373e15660240aad20ae16477561cb074e2ccf2840e643c20efeb64df6a48be26d331a64aa93af117746d96a6c204a9803bb1308e49a6a71e8feead80263fb92a51796c4bf1c95db0db79c0e31d31a03ee4e3f7b7c23f7902d1a7b2c5910b0f790ae23a0b6e64544bcbd5dacc89d8def95b63437651fed0e53e34b8f724be8ec92f8873273eaaa41c15a8c7129bbf800ab16934bd164febf2274d1216e23e9ee58ff1872e72b702a0cd50ee76cc7ff583d2f762c6643cf6c4a6d1f256952c403da0f31df38dc699ff1a7a538425b6f55b1a5b565fdecad9fc7f9f0acc4ee989cfe3a03ff71e72e9850453f462aba8abf7e042139792674d8f2d8fd1c7f49dabae1e0240c3b8ec4c8cab9961b00f96f1ec0de1413db21fcebb4834d8fae40943115c03126b55cea2d4831b5055aef2461f4f1450e211c7e6578861eedcd61b7bc14002663f4ee31d00460fd25d0df1dcd1ca15e2c817b87ec610326014aff7c176b54e7f9e2796281c3b6a97bdbde420e370a89e6c67cb406d6b93fa3213d5b6a2d66847fee34f78557d255265de528bfa143a543cee7fb541121a9733d8ee455b60e8598a84524d4cebdf44528c1fa37515ff6eac32649abe3f814b350f33aab19686647545bb5bc8056e2994a5537511e64c04c506b68d53fabdc46df0cfd2ce039ca4f1b5fad658f0c831df79cdf4d62b4dbddb4ee24b716de65d881d9a71cd4b2e0c19ee5edfeb1ec85c9dafd338de32a0a9d069c36479902494fbca4ec551eb2a1b172ac3c061eda39c1d0d81956b6c5c2baeae7acc63597114fa88c2d1f71f0cf9a2d9ef8f231298854520eb69e3276045965086a78b6e2e78826b1bbded04704710bf3e812ba0a383885e2d52bd078d40b7946d06d4feee5e61074f42fe800ff6441d7455b8c5224430a3246a941b0125537c667bf952e2a7ba975036f5e37f24397255320fb5200286f5c5751393d3129d55163a721d0a2ff9c2583614f122bd79308355b2fedd7994ff376a1e35a4b6196fea86435c0e808e0878373ffa16d62f5c3304403a14ff1f5c7c953f32ab8f9360c92d7feda643309aac9a9d3a0fb3878c52de13181e5be039f243293311c2011abd18214b420df4d02d921ad153280df2e2923d1ce51895d175aeb3bea47d5f4733c73c1577fc83b8eeea9513ae1b7384537cd83dc65345799b5b0f81bef4fc8a55b5a7774d7e8201c6f9614082305fb21af58f61f303d784e76f2f985d6e145de576d3bf1366f41bc25b3c57a8fe1a9345bcbae979d8057ad0bf2344fc91df2aeebd21b9ccfb3c7ba0e54a7c24162e1667b6f16d9343a288708c14462609569902bc68a1a8114b1d423b549f2e90a191f5cfd0de749d0f4788a33201a10c8ed47d4960f09febd2e2eca5697ab4a87f156787d8147d73a43a22360b63b257aaef351a95af635fdb33a56ec1c50878174336d0e2c22d8f5989b7cc100fd23a33e65dccc79070cdf3d37a77ed028dfc55e2f2835668ea5a3fc647b4d981d887ae1d47c94ef48c7afd21eacfb403ce1270dedccb4c899052a571d97d8753e4bcc9622b191303da79c19d5cc3b40ff1189718460d9f45f31d8087c08eb7918c1ea9c1d0ae799af4c833c3dc8d8c95318d59644d63b6cc2c00729969a4b3a447cb0e67d94f2570f98df8e2f74f80ba070766b94dec5613ecde1d8a2d085119c24e4cb23be948add915613f86266710186e93c6977f35662628dc7537a327f0a780b48fc830940bef44c1fafe8988e24724880cc393128d59f8940d716390fd5e1f162db2fac22941b0b4e686e85071dc7af9b991df95a237f3bd4d7eaa0cd01f82214e92d8c60d4f6895e733f709a16e2a535498bf2ca495ddf5aabf9be8824be4c34a2a20e5e4068374c5b8ee27106758d171729ae8aecca105de4601c5a00450d9b37f0041f38c15d5de20a41f31d1843a744455c57e58b4f0da1244cea84853e2e99e89e1a3bfb6290844925c2e485dbde80f6f5566bddaeaf5d39f5c52ee19dae828e58296703981648d13659b06d359fc254ac3d8d8387f036155f3104bdbac55625f3bd84edae025f2b30979388694881c91b335f182eafd16c2f5b506ba111d36f90c918bd674e13680d957ae235d2ed5819655c2339e6c81a729515022f187841baadd1d46164e9770787bce33ac60f62cafb0a5a39760bb0ac527408fb48fb9be63bd357223afea70e6ae343f5afccd9198282e2785df59534443d7ec147fa8c375b02488fb8216d40c48000a10d018114a160820e53db3cdb2c11cc052746bae89167eb5c333ad8f53edde1d7330cd4e445c93b5ad1396c2e494666642bd0d7921f3187d96551609dcac4aa44a7d54de1b21b324e3505fa46f681b37b30de4ac066972854380ecc7dc8e4da539a0844e8d91c5a46a51fe597888c2a5a980a790bd1845fa25df22e9df003dd6eaa7fb2669ebeec0b78ac79bb92b0e31eb8b93a72267a0dc4c68d58447d89459dcee2f5c044ea15a7d91a66a4fd10b034f61992704f53a89479e8def3a7e44058e45ce5c96be1d0ee90bb2bd12b86ef8201e25b986d4264ebe463879d44268dece70b1eb08ab96fdcfb85ceeda653495c06ab1eabe220309cebd167196b8578ac95861803fc3be626b5356e54810ba0469e8616bcd6e696e806d03ef4ecb003fb567f38ad7e4cef29b22654699a6105befd79c5e9c0bb9b7b8f1e1b206a150b54b73684b5298e6da8fdde8c787d2bae4a2cf4b0dabd33f376d12a94d965674d938bc470915f2848d36eb7d4f48ff6dff225ede0d5ca970bed7a600f98d3fc71f18638f0e21f6043e4ff06c991a1c1ee08908998ec237e5c8b5edecbdeb186530ef9d0c20e89cbc0b75dcbce02f085d042b4f536b74ee6c9e55a769ff588f7f2b354df17bfaae565648243a51809cd6c141cabc5f201fa805ff28d1be413505eaef82060b026db8344713a18f3ba26fb226d76d97a09e222aff7bdeeab49e2a429272ed9816bdea1f715fe2113a089b5776e808d2eeefd3027c494e2fe4baf528e3294d750f6ef8cf121970c788e6738d6504bc377e96f7f3b654947962bd2947ff15d576a7b945a4f00c7ca277c65bbf8ecb74cca9f9e3c1e42bda94e935c36eda84f478be212a8444d47ee2c6d700267e0991e103b6735cb8d93236631977b3b77dcfc74aed31cd949f775287e7a0027cf56994662dd2674229d8a302013b500a9359b6264047e1108503298d2ce38c2c37fdd0644c00750705998d416ee389179666ddfca1048cac544fcf93d205ac48e0812855344cbbf0d375633b1885b308493de10b8b6018e0ced0c03d9f668ec7f77748619bd959cf5ad73a97e7c3e332ecd73569d8cb5d1c7955c586c4e7256edd418c9697947e3f0b6dcba23fa5fee5076271d8b73e2812bb0574ee998060a5c44e46779058208084618a8e094d077ee6fff5109fe85857c01273499a356b27653a15cc7c275da85bd62238c680a76c373b1573be54ba2d8af93583658c7219fce13967a5171748d2626de39ad0e8f38873028403a3c4798985c8d865f39a588ca070a1da26ebf6044af350203c2d87866f16defad122f99e76dcef049ea28079c5c121957985db521036c94deec026cfa8bf857f32074ef03f51bd47127504f607d09747aaca14d81ebee868ab0a658ae961d8d581157f7051f4ef321c0beb0d01ea5f940f0b3326effe9adf62ea0bb08135c0224c55ab32963203d0998f3c22e92d087c257ae303a59aab2f5f9c1317446d5b9f9a26597eb11790579ddffe2ce8d7a7f8facc1ac25195ffda0237e1e908c281ffe03452e87f91cd5b53abf052d8437a2f0e17815daf6480dfc857024ed6c392554b4358b9481486a7a1c4e4517a3a38473af8a9b3ce52ecf7b6e286931b3e00b8c352ebde538fec08e5319d0b9318cdaab837901ad46d777ad0ab926e7cbd383b03a6813c67c0fdc29cea41a7dac2b972f84a84411ac01f9abcee2a518d4c0425d93bbf6000c1849343863921e1828dc7f7ebd69922e31b972eced19926d620292d08402fc82b540c82ebaf3287978a3077b982502abd65bfa88933565f02d93fa7eaa03707d04948a692dd794f9d2c8503a1f28b113e1568572706b0ea3a2eb40697ad1a8d3fd82ffa03307a4185cdad85b4bf3a496e59ff81894613c79d171ef918e30c440513f334ced64592621994071a71c06b0672d665cba03458aed4b4156631702898c56be328177c24d25f689cc3d450f0d56ad43a31aa276e1bab529bba0738622e0c493d5259909af05d289f2053b59a3d21ae15436a4e972ce88e2cf2997f0fe1531cfb68672ca6cd2c3e5b7b2568d59326fcdf7aa7f54a76b125a02295262cf3ae153d2758b834d6b3999ed1909a684b7134971034365b2e3ab6e2b9d72061d75bcb5b1af78dc6ce30a7ef8da53c24a0d3626f90c62712b3fb44c9d19562a8fed0cd2708d68433740fb26ad2418b9a18cec35b76e459ee3fed30799121a1bbd4d4e9802e6508c6efd4ec43cf874c3757acc7859ab1f38ec79f3eeba20f738c76230ebf7812916e5edc7befdb282323c6066232c79959bc0cdd7174e0aee21b7e034e722665e05843924b856a53daf7019a1a2aafa318637d08a97f1eb26a4cc80ea1aacb29f31138e5be01ce76cb01a5460daea212dfa217c2f2730806843667629142f7f66ba626ea270ff4a5209210ca8f27736567986f4d1e82d5e8420ecfa8441f3d23ab6c3973f842093dabbb8a2c9ac0c3b52f2ef88123d6f4f587cae92c4d66526bc10e92cc7e452adb5e784b058ed1ec501a27ef60cc31fe671d3516ddef7bcd7fe476e4a7670f6004b56b2cbb21240ea4fbbc4caddf3428aadbb7b44bea7ecf733ec10ea04a099f302e91909962bbb6c13dfb3811eebaf675c8caffd7175144d68ad3465d0fabc3ddefd431e019bd333fc3732caf28cc3faae51101165d2acb12017237db95610cb06ea2d5d06219f776d19282bd8b26e31c1b942d8a322bbb59e7d42c01e15ebaa5e30e5950066fa692783ece7a5c68ab0ab31f3d024d2f1e0bfc37aa18dbaa3632da5e6314ebfac376b0e4bb5930b3dc9695a128b38af303e230cdafc7ecbedfc86d64be0426feb74e143b8de0a2e84d0bb7e4db6af63b31c6395cbc7e5273c0936e349b8e0f55465b42b93c3da41f4dd7003c2394d1008fd8f1c0d8ed8efe8165099baa46214b314ac4b9421fc517045ac4f1a057fa64f89931c38887974c80c9cc4c67548a081712bb628c130fc1a1533517b757deb5c1b6e014bdf14f52f1cdc2625c2569ce6c4b2d8d6464d87c836f28effa16a744620eba48accf06c13e8cca16560a0896293be99db391d694a45ccc61596b4a546a9b80905d9f576f904488bfb55fdcc1d798421b5588b13ca566b380bf08d69505f0d9e10873d61abe16d035ce85c16c632422e1b820e7ba4b735507238158cea84535d020806c85e3b523c7744dac9eaf818d25eaf8154df08cb9c47e5c98f65edd51cec9c1339b8224925b45a43bd14e49b2c400f00b0d855a2edbe758eaeca385ea7cc30269f49668907338056babdacca8fd6cd7d7b292d62c2a626c5cf72dd11c521fdfa06961dbffca2c460be040cf343e8a1804750c5fe884a09a86c32548e2a0473b5b66ef03e85aacd27e153e845a5554d5d00dddbeef24e1b0060069946321ccecb0f53e9fc5af69283ee4661e8034653118c321694aec67e9b083d1eaabae22c610417e8eb8a1b683cf593e20da3d47bbb18c787c0ce11b63861ee7ee7bc2621f2f48016c09f23e82dee32baf53ea2c7ac4b534a0874fb51233abb3199095e6224c9d9d17c1354c11b55486834a9db4ccc62b81dc8a2ce3a1ca7c6aa0936c0a8a52569ab3a51710d0eb5fa7b8a2a5c91b8dee255f049369e38b2689b24e015f9d5fe88a646341651de3964040f430fe4a1c684d6787cb7256521a225f10fed61680b34f668e1e01e32cb06d5c982893c74ceb7448200157bdb826d1ea4233230b9ed330d4ea8e9787d079e41394e945a29383a0e731c1f2b99cb8ac6ef24113583105e0daf0866c7322e79932803968757dcbe0498ea986ae83591e554b4e66cb7ef98256f379077df0eaed91b17b80fad47629f916fee40c769190396f93822a7c422820255eb0bb71bbfcc0a4ccb03db10e858c5486e85719a713987d35e646415248b3b246ca984b387fc6237fc6dc79d8f90356fc435bf4ba1644b4b2f5ab370c1971b496927d10b9fefd89f6858675c20b9670e918ed64a3612ee914e9a072a6b00f2be0f8b69937d80549710c72921068b8f33ce620d46bdc3edf28978fed4b4099a2d26178cb83ad4c6db955a419f610835b0388c87d9c30823d6b8dc61d0b32fc679d1aba0d9512f351e556d55c6f8185079728d5fbeea1eada78aec31873562c99ecf9354f1b072f6ea6855e91b2c501bbfaae52f2d36ce61b5c82e1ee1591ea83b497995e6b42a04f3f648a1f3ed19d1c9e51c1d63ac1b74990891c83eb3410c69a99c2c9fb136004d54c435ae879ed2f515762cc255dfe3d3a84eca9f47bd412fb13e053e3b9a1f3eae7a3ad7896565f54d1fa3e82d42fe092824668eed594a8b880094c4ffcb0553b0f865149cc9fdc8e85bf532756b76131a302986c666d0699974c7182ee7d3910533df6f73bf861c4b501317eb0a3dfbc38d51486962962d4a343b444b0b232c3c7df60e3427c5766de53f0aab2116e301ca09e7ace0236132a5332bd4ea140082ed8df515a98d14f454eb74d12826525ddacc2537f24d065261aa850fc3690f090a5fef620485d3ce5061a321d6999fd5b1330ff306cc680b816565baaf97c3e4cc0f526d1beeac7bca1c8fec2881aa5b6f95f9443411d38aa8c4ccc20b8e1cdebe9948b6b84093de0c575e66aef08ae7f9cbade94446df7420e7056d7aa74096590d305250a39d778bbc3f067034021a0927fd1d6bc793c63c75e03c34e3db359a698702505bba4ea7c61b073b0375a45c53a7a159ffc0ce859a7b8b3e4bd35caba645847495b9d6aabad1031e014b0b3932f90bf347893697687eef526a5feab7359ac53d886bc924625efeaff82dc19cebc6888f50799606328a241a952ec5dc7e7373e43004fad8a7d200a652348d9c71a820538cafb2c05c9cbcc73612946c6743f6a58ee154a3d59d63a863b4820ff12f6cb5cb26a36c5bea453272ac46ad15c83d256b5c88eb8e285c123ed2d5ef39c551b378e2ae53bea59aa51469129589067629afa8b005fa28bfc7abe831572804d158ec5094d081c37c8e3e918447986d649d3a0ed3c42ba996abd80fe3d0353dbd26f1ddc47e8a28ead2be17c3cafe04f274209aa6c4926f6c8535002c55f017c1c4d35fc1dc5431b50801e4e5a377de84829a97dd0e36fd23dbcc8f8e88de1aea9b6b2e9c319408e332922a2a0114ded2d6218e2a0390f9603d1e93a26ed98061a2c84095b381759e4a2dcba26ea05f62f12a52586610837ec29b8d3af9ce3eeebae6608fe7c7392d7d9a94126bafb3a07d1daa2250f26bff8a2ed203d9216a9c1ce1580f431644ac429c7f6d7c93664b8283c222be6fd1dc58ff9ecff431d0b3fac4a0d109856636635b19e5149474efb7f8f8d6693752c8ec11955f8ea5d45b548e33ef767ca573dce91c1082630089feb35dbc9108ebc288492fbbe4668fc31d83e63845ce7dd50724c97de13887b57c8a56b1d5621a064c7713f9374fbca9d66c97b3e81a3758a7164e6025df2a560c2e341631a0a99bb3e9d83bae2e401ba0d0537cea699629c8791ceaa532dc93c726587c4edc3db1e083b9e67716d6a5322120632952a9b4201e4fdf174f97d69acce4c553a1172b178b86d274c412035999ca6c252725ca0bd3dfbc9eae0b831de808bff70c40975b9e9c32967236f371512e4079747cc161a44a150297d8e46be23e19d8f937f6608bced0e5ebe6a8205e808a501051eb348abf6946fefb62ab64946a16f22856b1fcbe2b6231407057c485bb89f3d065084ea92a8f1cd1a690699270664bcd1622b42341875feec4ae7d9d917b8f5eaf5e12d4803eca3d1dce85e371081039021d0c3fd6fbc0102215964c52f316af1ed6fef709c6ad665dd5c906a9da19a4d3a53157885066f7dfc3e9b46137ab65244ad8d3e2a4ab17038197fe5d3db4fa3fc75422351ea9c5cac1a954708a8924a6c239bfb75f7e7d05956a7efe26643793a917cd907c6fc835e2427223f41a3011be31a22eda1c890649e6308c3016c26505eaf966ce548027d996adc5be228d66d38d64d759f49f585b36b8737581f494b35ba20fb1b6631d7900bff4777ad3f17eea805cf7c4d349647eff60487f8851aaf6fa809051339f68a01ee6fcd600e465e2ab9725eb3b9668ae2338d686c7e9e3c05c62fd183fe75c9410ec4afb7c2913adc108b75bd25008a55c83b2a66fde79ff53681be41a94854af253b906950783188012e22eb2068d7d2bd83068d5a06170bf869de97d8841ec6518890974a5c8611b0442a71494f1c3919d72e90e631664c486d1a155c6c5f50f63c313a2c26c14aed24825ec841295c912b7f2b14a77aabda00a40df57f2a120d5986056b3786c08235193b9ebbf062871796652c7284c95146726fcf5e7d04c618ca4b45732c828524aabae9432c15f12a1b4cc85c00fc17a1f03a6a78a98e87d70571e4bf328e6e20be393e8cd14ee656378a414a16c05080624868706cb61f68811b29a7d1505f15c1f745abf114037fc7b41254697f524259b2616209a1da24290b21393b2f28eb44c7517718baeb633c3c32f7055d484b36534756c3d795b18126d4d1e335b817ecc850a35cf182d349b596006f2ccaa11a4d07356f6cfffc83854d4c7106b41380aa26e86744b6425dc3ffd9f92294f5e24338046f51bfc884a237730d64f5aa6fa840524d1542540b4f929807e8591ddb3785617c38ff21abd62fac2136b3146b2bac4c81cadbf1d1f5f6f2bc4c4bfb3dbb050181bab86a14b9a0d0c10181955094370b28248f7fc75080cf1fca46c3b9144d904d0e7fbcc816aad27d37858ec77039fb0679b61ecdec13b6abb204220526a1cd3feb2071023b21afd561b0cca3f30f434b8ec4ef7fa7378324f805def5562470c86b68ff299d90ecc846698141a3a61822fa23e938623e8e786785d365109ba0da30db7c5e3aaac20816e4a543f7d116f45810dea0bda822f349a68ae4b50a41bfb0bf23dafa8e697be1c400937276ce5fe794ba27ff10d70fa62d2da03666ebf0df815d3992e1c8ae75190ee1e24e37a542ef970ce8d229d81803a985e59e234ea1404fa4a9df4d1c1f998c5d46861487a53ed7d0b978f232e3b1491d851831f719368781099b2534f2c6d02761c127ae2d71f09b625fb6a073e3d4f0ebb6694502042636f82b56b45b5eba0d74449febe80b57b70176816e9232b331cb47f783f86fad13ea9af4f7fda6f94e632d8d32ed06f85b7a55bc8f222cfc053a54f95ec500c32501553c5604702e79eea31d96ab44a72e04815dba9b8a8c2163332f73a02e11ffbcd9b9a3ec0497d905c333bd52b48dcae68e52bb2c900aa2d7dd9161fbb63f46d80d9d807abf6f7372a2e071c2712ddc20f8cb3af6cf5e6cb84b17b6e006fb4ed663d88f3050b9a1ce756e0481b3a85174b0495835da89075e912f0488dbc9dc62cef24399227549402fda27c44e1284d0f93ce3b82e60eece08caa6da0b90d2fde829a2777e6a630a20a713e4bbf4a1264967cfba90dfeb55098fae5842fda2f540754208c543f7258f7be54ff435da1bd851f8afb2e8602d176e36116488fc4607beaf3d16011c21f13d3d1bf1c37a8f5d972437791bd463b2ad71b7310ac20bf906fd3741b0d191fb4bddd82bb79f32b634028b79b7b57a6948ab2a30e0a6c7fd32db279749b605b1790a21cee93cd6589526af0705f6caeacb352d35b092695623084d48e75bda2904cb4fd037a904a7d57409a55a5034d78dc1830bae02df36e6a4b6ddc09616e769cb45a286c02a99899b46615f8eedd4c9016b03d2eb003f4bbc1a1054c78921800b44c7ae7d670ff553357112d95d0966d042a1b229290bdf7de524a29534a32b107e307ea07cfb2cee39d1b07543242c9a15b5642a7c6caf3cedbd181ce09baf5111fb18ff41176255889f6d131d29b5f8896d11c1584cdb934adc1fed1fe6e5c3ec0e51867ee76e3f2cf5cb63635def3c59e45db469ca01bf33037ca6a9d05668358473aa69d70b9e803cfbad1b833fbed857ec32e46a2bfad8c7e3b95fcd67231988bcd60769b2fffaa8d1a55ba32ffd57034aecdb348449cab1b073d77de4be44e40397e888ec16cc6cf3faa354712285fec6d4ce0691ad6acd2cad2b15ef4d9b77d1ae26f8da79a4ce3580d9ace310128de6542b607145b90d03380c5a00324e4f88001246c4ee8431609fa18a0f9cca62e1a2f107df69beba1160a7e910dc251fd3456409d0f3e641ffa9e5a8b7019d78da467c3ef8cedbb8ccc3fd3033db753d7e6dd4b5db8526facd4779995879b7661efd023ae07a7047a27cb57ef7cfeb55299a502195d6f3a49319dd81a7d4faef7e1f1e13c30b8fd2dc590354c71fd3a24ac2104270e096a48d2464e52ae27dc1120b79ba7e826dcfea9aeeb7a1b7e2fd3f5a5773ef4b710176f7a06a87cc962a0f42aa557313d0654de8575f1ce0794eaa2feeca0d86d9ed814bb95aa52d54259a02cbff29b3b724dd12345759794f257dd52f7fa94ba5d7ff29dd5b81ee569fcba495d77a436507feba0d04037e69961055af21bf3d430f992a7f1edfa12eb5abb56ae95ef91dd26b51b5561b12bf51d75fd0d9731857cfc2a59cca6ee65a35569e95accce67473d289dd1e082fd94e4df55071235fa421b76fc8439891099c27a7eb62a49644edbef8ec304844dcf95bffd757fb2e2cec2b5dde04c71e1bf261289401a4bd07fcfb7f0749e05ff7d94527e89acaa29124d2c8a4aecd5d0faebfad931956d215550eb2badb3ec43a1d068347a1bee8e1ee76275fb9be35ed8953dad38f7aa3ce015d9e602bf644fe399880a6afdd6424aa516f243fef8212b6d8d0dd4b2ef67159f8e958259cdbf1cc2abbed6e4b4d5bc1c167aecaf7a41ec3d6797c886f5a2c7b959dddecd36bea11755d75ae63837ba8d4086556fd56767b42ec4cf09f753032ff7734527b9f1b37f2a52a0f15dce9831fcd7315efbb1ff843610b3301bd55b76836f045febe5d7f8f04168317e1bb8663b23db0d8e90cbafba8d7fbfc3d69ec4f1e19d85ab6d1ef6734e4aafd6a968557d0f6a278e0fd9875c4045a5aa9a9565b1f59515aaaa29aa3efce35ac3fa3e9bf7fe9c6f3735e27a308086649a3ce985307992ed0105fc1e26760a61d243c3f1e17dc80594b5b81dda026d1e2580fcd63a188002be7ce7321828f9d1cbe7cca400d29bbc1015b442945846e2a8fed1fc1b100ab481b44e6b57eb581b6bf19ea8ad11ccc9cf61f533c4c8dfe63b08655f081db3a1b51ed09efefd37cee73efb9ed3e66ff2a38cf259be7c185acb20e63d2f590380d63d3fe73ef28b7fa93f109d9bce49e9fe61bc0f19e23a67b7f8b2fa4de9ba960eba977656ce39ca6860e7ef3233334366e68706f79c73e7cee173f0c57f2e9dbbc1ae9300aa1091f15d97c8dddff3e7cfdf797b68b3b9fdd31a3bd7eddab56be7a063e7de3976ceb1732557a2a48c445b7be7d8d1119774b77b7b7bbb63373ac3d5a4ea5c6bde7b10c628e713359610ebe29a65d399abe85d9dd235c268e6ce5ea2aea4a4bbbb9949272728288cd2dfb7492829272929292493929494114d494911a584b2949414cb0ad72572fcda55a640b7f9446b8ee709d4bd9506ee6ef7f6f64dbb63b8d8eab1c25401a2ac83cbb977238d851680be9b9082a77758a7bb7374b7b7773be79ce36ee79c73dcddcdddce39e7b8bb9bbb9d73ce717737773be79ce3eee6ee6eee76ce39c7487a58a7bb73743737777737777777777777777777777777b773ceb1e3ee6e6fdf2a7059072f5c1792eb3a0c7d248bd61c10fbc3e87b8fdf0505ea3f5d0254a0e048fe589266655d5816ca6cc4db261c4884618ab0e76fd29393939393d1a8bba973aff243a9406916b4bf4d534668c261eeb6da4badbc03a5609e8e953e515227b9fd2a52a0cd14873b4f4e506a806668c038233559382c48254ab211575656e0caf3e6158785b61e13a56ed36aa99a73715fca6a68a90954a559a5a7a3ae92ab2e4899a94d2676d045087355057375a32c2ccec2d22c3c554ed7b995172f64c2f1ee868469a5a5868554f382c280f13f9322c9085f8c18de5c85369c95151616be308ec280f1436bee3faba9955a52776fda65d13333333390cb3a4892dcfcdc6839f116ade1682ddbc9f116ad89dc9f64c870193264c86876a1e9a82860513079418271f2f504a5aa18f95aab0bab12c37492d13258063465355664b83761c078f1a41b64dc4051ddddddddef2070fb9d10b0e53a843e63c68c9e318367b85168bb000e386828d48c1939e40053d392d948b9b8e8a0c3cb8bd36460a86b06cf782a46280a95d5d0e28caca65fc582b6b0eb59e35c5a3cd529862e598d0efebabdfbbd5f2aa776e021ab0100856119da70ae0e3abc8a11f9b2c2f2f2f202c6cbcbcb9b5e5e545e5cbcbcd028e1226194f41ebfd9da162ed7974b02bbd9ddb9f7daf61597c85961e522b53f182513b0372713de13359510e7e2deba30fab2e976ae734a5cf72120ef421d111f29b99149a6d1745712d6b5a6eb9029fae4844f60c884235efebe6cb7065cd133219d6435282728282824931214941145414111a184321414141c42ce981397ff45999fd790d7bd6738e275763a97f74e6e4e0ac2f8e27cd8ce7d2f31e9022ffa0a9b1866b92babeacbf5605da18c5a34945d96e8cabebf723d5cdfaf32b31a4c947175cec5aa9739ab015f3a3ff8203bf487570b7af9f5006b8555e7a89e952fe75c2abbf1c3fbeac6efb19f79bce7cff5308f29824ad4fd73be957bbaaf545d0fe16ff2ebca75555895d53cf84aadc57e79f3fcc1d7ed8db563713dab4b2b0768ada624ac9cab68fc70a3350f62450b5a1750eb75c1fd4071b999aa63faba8751cf20fcf83e5af15fc7c468a99bff62155f86233e7c181f6607c0112d8fbe2f28089bfe3344098a266e4a34ae070b1e710b220505919b548fe7a373bc0d9480148022c95340026238031bc470063660745412123d5734b3b4a4a3f354c46c71c5d38a6c8f0d7850900f506cfc407050d0b5b9b1443cdb1bfe2034147a9128c4cc6cf9056b99862492783d3795d2518244ca588848a5baa82ea5a2d493d010528af45beac23c4d33901f8e729906d2991532e44d0ba47fb8ccdcf9f1e3c78fcb6236645b8b9292d6c2c47ddcc77ddc6762d37dda8811233ba4ca7787145af016cc82d646dfa314ccf3eac262884614844c1b69203d3333f107c88f05f2e3a595f91df39ee8db88d69acfcca4529ab601f17154bb0f76b9ff70ffe148ece371826efec3914cccabcf665841fd43224fe2e268011bc0c23a921d3a3f64fd87cb381247e23f5270a3ce2c3dcac907c15a6b5220d9f1ae0d72f28f75fc9568400486c6c51ae5885cdd45b82505f3ecd3691cf5be049db3801c90fbcb7507d7a021447319a74927654e0e20dbe3ee103e6889384d8c3f872701d4ce0509dd38a72bb67ab43f9d34abf90c2d3c96d5a3040b092522f28c816ed68ff5e37a58893c3b5842883ca9cbd6d4e35c4e6aa0db0da69e7b43cecab57e228ff563ea69e1e9ee8ee95311a6ebee8e699df744414133e0b98f7f32dbcc24c776c36c8420110ea808e59b3df6332e63a1a465219735c256c8133c3c847d86740f11f9d6b788f925ff11a16a5a1acfb0d3c524d632a5d732a2c7301e07386a4ecbb516446b6c27110ee8013da08cad035ce6089fa2b59e12d06de6468c75c3dfa2fad50147544e8be9b4c83490d25a5a525677126c492449361ef166ff5ccf1585b22c140a5923e215d98de6da5c99ff765dffbeaaa1d414e8e67282a091f98ffff88f8bacddae7fd65f30777317f3f08b2346b1841539173e76c52896a822e8c2f9a24e0c26ed18cb2a312131b3cce50f801b0001dc4b3fb34563524bea95d5c9d7b25c66baa8c7da0cc01373cc2fb79c67d9c3b81ee667ef3214a8e428ada61cc559c52346165c1f5f0e4db93f918a02dd1c360a7e3cb261a79621358affda5a5c468f37c4800c40373edff81340dc10033258e1c61af0dfbb77a3b579395b6839695acb4be2c76da054aafa99399a0f9f545d8ed06736d7db54cf422e4c59b1a56cc0543587057401952e88f1757df5a23a31cca298257a6b4e3729666dd7a4f1506537672405c39cfdf300aecfdeb10c4d96fdb8343d443823d7665699868fd5eb5f5633ab8a71725c8677723c6756be2ed3e14141f7b24146ef320f0af26164df51a51950be253f7ad29bbcf3c1838248cee13360f42696c651f05d16848f123bbaaa07055dded981bcc33bbc0383680d028582cca4606066acee16a8e1e73a24a4018bbb4d24a4618a6b44c768445f9bea6555d569659126b9d557564e8b452a83cb9d36cdb9e8b9f37e0f3791a7a282f2b5ac66e9f856958b9ebb8d3260c74089b07a2e9ca924748b3db7ad9e1ed743eaf65b3d36582efffc6d9401583777d9a6f0c86002dd588058805237f63817abe7c2df62cf0ebe5b8ab6b306d6b92f9bd7b4666aceeab1bf30be552a4b5d29cbf23f0cc3300cd3b9ec56b26c65dfbb50da0c480705feb9f2a4744ea081139eb8f0f926e59c400327d46056d7a84e7d2014c2aa29afd0d3bf68285465979ca2ca12655996615675c9c937ab1b27a1d4a2938a68886614a392524ae9b46829f4598aca129d724eabb2aa29fbc1c0a9d3dda4dd4e1de332ea5ccce8b7232c60f950bef3fb5b08703984c3e5afe2b6cf7673baf1e1cf1867f68ca344668938dd0d67c89dd646be0dfcf8ae46a3d5396d6bb92c5f51ddb2bb519a439699dcac6ff69773275d0c7ff6ce0da0855f1a6b71c17259d8776b6166f89299194266d872e5e96e10384959bdc56e4e2bab0dca151bbae7ceee71763b4149d15cfc96e21f5a2687e9b799a95417b554532a4a3da9ce5124d13381f215fd131541b21b3529c9e99cd44c0ecaf4e7facc3292d2cf97d696ac436176e3241fca421ed26e28347343ff43cb509a8ee10f857aaecb64f6e96b77fbf9a2cfa1180be8cc9536e759a7736ece8f496351c889a556e21fe88681aefca71304ca5776abc4df7aa775d805a1ef0744225b89e7a327ec2773f8fde899bd1b82d0ad777ac7d57088646ac0f5b07bbeb5dc6ddaad4534b749a9e8b3bfb2ea86e0220ca3486c71b1a769a9832fb54460d666be6735a291cb6ff42596867e9b7714fa913551fab2742293f2925d8ff3f999a42beb8e1defbd96c99eca878eef840fa56c54b74ca051fdd39ad8e6d85a6e978450ebaf9749f9f926bf9d563612ca6fd8dd1c9214fb50acf5818da2a4526b13ee8cfa12c2ce7d0fc3405262a03fa4ec6ff37b40f9be3fb5d6377c640df6ee2dd7eb0fadf5cf79c22e842bdbe9b6cc8d2268e9b1a035ee61202931d0159fe666c906d72383fdf557ea5e33f7a241e35ed64c4afb129dae514644896375e700a6259582e916c0874fa0f0ab9793875f1cbe44155ce8b9f06fdccff525aae082102eacf3f2cb4a8d80b35d705d3977ca941cd5dd01137308693be7029ff51450eb652d15c1e3881e6e3749afa024ac06c9feaaf6d4287ed1ddddbed3b5aa8dffa3d957d5bfd905c8beaaa94e8dea974dfb851b8a4829a5c45a4bc0eb819c2111654e79125297b552a21aaaf2bdb44eb2bb67ef9e61b4e44e530ed25882421cf4dc763110c94681d03fbb15717988cbd62d819af871ae0907bf105c53261e96a0d6bb9731100740241dd35803a1d66f970e0ced8d52f67bd98d2e017dae552b28a5897431f2a7cb885eca189df3c7b9ce9de2855a50203b3f7858823adda954c8ed7780cb50ba843361c1edef1f72fb2f1d77e4590a0408f59f27e6a109ca365e5c51cf0b28ff76e9d09e2a0d5356816eff0e5dca1939be03e27dfc686da0bd6d82cb9dfcaad2f0c416fd252d88dcfe95130eb92171e386811b7fc36086c36f15eb8673e36f3afccef82f4380fce8291e507e77e1cdb8ea8636f22d0efbde752e59e4dcf757fcaef1bd7e9344403b1ca62bf503ca73dff7ce7d0fdba0731fe6598d1d3c74c83b9d8b113ae4c59c0b23a01100ed86639246bceb9c8ba9dfdbf673024ccec5aee1c186b2995f669609c75736884908515090b4f8b3aa83b9a594ddb7bbfb0885a21ffe7b4dff95f837dae306603be2c109e1738c2e282828685e77f2ab6391cb0980f2f37bf7e6353feb88c5f9361e8009c77b50d0b5ac8b35fba9430605056d71e3c7c7ac0e8f7c713cc957f69dd579ed02d5e14141f07de3d1b77a1e7c2beb1bdb261237f24b845ff5b8cf4d18617cfd821e8d5d78efdd83ef1551821a8a28e1e3f37395c1bdc8f021c1a670a1210a181409824f1545b2e073832247f00152e40a4ef80c2943912740c1070945b011f85c498cb0831214c9715812ce8786591ec0a33f67c3c5eddedfcf5844416dd4d0ee732e1e057d990debf604b943fe351c07eabe1ed73997b6a61af33ae7f29ecec5cc1b70ce75bb76bd02cdf57673baefdb39e79e73ae85812ee79cdbb4fb80cb0d71b9ead773efb5753eea0b981c4826cfcfffc38d03740ccb8a49c5452905e5a4644445a10cbbde6a71d1e125b5030f0098f1ac5d6de61ae16187d48b0e2e2d3918b941460e311efe15a3c2538cfaee0da7cfe114a3facde114a3f695718a51f9ca38c5a8eeca389d6c8caf30feaf7f87f1f6fd83f12f03c200301ec60a162e4a29282724939211dd62064ac3c148158e3861a40a476410c291d1d513203f330d64c0a7222690c1caa988099a7044441e28c21578c4529c584bc7d02ffd7672f1db8aca6f2419d3978c7ef46cb7f7a68a354af42a2e445faaa28f750b9d3cd758b7bfd9f66e9651e1f25f6cc5e5b7a8b85c05e0f24f97cb32321c8a1ffef529299ff229768b4f42b1cfb9c42ff993bae1dc1393a7f1933afab8c1cb25262526551882e4e08e9edf8d3b7247ff3220aa3004c1c12d19d9dad29a4864238d67fe57b5eaa6dda4a07fc71590da9b75b4a43091c40634463f883011031695fafcacf2fc33e7e41fca4a87a1635ebce7cc8e29fdbb48f9ffefff93d6728af1f1fbc873dd3ac9090cbbe15c18cfd5543737846b92f19b0070f86d07ed3714eab73ae3371739fc366af9ad9271f98df957dea5b243f1b7d41cea8c8aaa1a3f0e955f466596a77118f5c5b5c2b2c282430424040ddc17cf59dcf816cb8bb75ebccb6a5ed89d534db5163fc6db4a8309aaf2f0996d5ca97c612ddd507dc8f80ebc0f322c14b53273cbc954377a4df3c56f98cac3b0b06ef442154c056b0d3e347dfd37d5a4fc9552532affc36ad57febedcaca43080306cbbf781876835f7a93e92f99d5bcc05e9858aa6bcdf42a4fe32bd5c56735a5bf4a2a2515176faa4ec5c55b2ede65352eec2cd57753eabbf1679c71c619bf8a55ac228c6e26be49c93b8f3ffa29452b5af1adf85744893f3762f1319e92109ae9947ed0f821cb7f42aafe2675833fd0676b24333d9b03e29c1da0ceb9dc404f7460664034d0131c9839317a44050f789a2d656b3ea5ff649e743a79ab5775e8e077ce25fe73acaa1b3b974ac7e9692d25f6f7dadd3b0cf1b93b9f19a8c10d83d0d23aaad4a138757bd3be5459d30e68ecc8cc4fd4cf053031150047600050ab6bedc81129d650c51129d69004ca176b343fdc207dc98f8b139494928b520aca09a97e6ac6fd605c884205ee07e342144b881afa607eb9a5b5744c28543d3384909f7e267a1a1fd550f65726ca441c8aff44a17f21be26f51d85d99b999b7f57a4ac1b7c401bfa730f7f01fae52019841e3ea76066628522518a090945f4186ba42afaec45d55d18c242d9bf50f62fa3e9923aaad7aa2af497342727a73b0342141414046130735a1942862c7013ef5abf6135d5abe07bd86d8e02869bf44b2b1d337ac1c2625271514a7994e7e77144c7909ebf470efb60ce742c9919cf393c3f3fd1322dcf2e53b48c0e445ae6a58a9649c9ecf0fc9c056ba16500f038682fe36f78a67199eafd0350a7a3dec30080871db078ff52dfeb50dfbbbc27eefb1caa6bd48cea1a857af3f41b5fb8bd8bfa6dd6877fd50a6dadef9eeca36cad7e51b6d6be316cad7c63d85add8d61edd71a2f7cf173c26079ae79f1d70b182f60b0fcc360f9c7f217632feabc2e63b1aeb595eaaa6bcdc5fbd2fb94f72827ef496f8bfbbea43e2a621ff77d287b8f3dade250dfdf509dcba8f19f855f55f8fc3d264a90c46d4602c463a2043fe6082746576de898d7901fe69a185102162ee33ce618c01ef7afaa87f03d0670c77aa8e332220b8540a873e19018b818f803a7f543211de39c067f70af1752ed063f5ab6aaf1fd9b83300421409e200c2108f3a90063913b3d7728dade6566bb892af655f57bb9181a4c502cd45d5fd59961ef3ec4cfbf80f0dcdd397a7c1e3dbed773af8985e64f97c97eceb9e350d5876af51caa7aac4ed19defaa9b2b2bb88118a50cc95065853ec650cce60c7d66448b4259c55a2bb5f6a7955f798c4481fc9c551155258de04558d7439aa779e43f87f285f0971f44c7bc9736e674135940f7b6cd3eeacf8c3450fe9e2b3e56ff5c0f3c1c0e890f1fff16ae84471e3c72db676baec4ceb4a673f15dca23e6a4b4a4c55f5877bcbae3bdb703e5647a346ac50dc169d1984bb252c29a8348ad81f2f337e6aa4a14a2212c14aa7e34f2a1147bce6f55f91ebf1ad111360a8d46231a9aef755e22cf6a2cfad49a80804fdf067cfa38170bd5adc70d5d91866538375479f4cd2c0ffc723d26dfaad5d3385683b2dc395dcb2bbb5997c708e35695d4b04ace895552ab60dcf97a3cc8838a6e3c7adcf7cf6e8ea91081cabf95f5733ed632d7cf79596c9f0854ce761d87c431416b664cdd8d524a697c1e3dba477ffe1e1394671c8502ca90611234870aaa4120f8a64c30ad9de0967a7465850acabf95da08bf3dbb71d70dbb3dfb954cd05277495ca0d3cd5001dddeb34d3fbf08639b80681bfef621bd8761948280542ac157afaa9678e03e7dce3953ce65fa7476af0f0ab7aa22cce60e7f1d47dcccda58c0afdb0dda678e5f41e7c1100b5dcf14e0f7ae7b43bb41861f24f4d75529a02388c8a61cd532936a69ad35eb37edf98bfc9535c2777005a355c548dd75c53533d331559532d5e81cb74a550fd35abfcbbeffa239c00fad758e6973b4d617fb703db89b894d0e01f3044240a23e1ce6feb07a31bf0bad093ecee5521be8f6eeb4b05ab09a765a8397053983fff1871f9c0b3f4d7f6b5aa0fe57a01102f1fe7a1befafc7b9b36eafc78517c4b9b0f2c02ff167ddde8d8fa6a5352d50b71914918713d04d7bf5a580be9be4e3ac60ad69adb5d657be179f9fe0d114381864b8c7dd7adcf9534a8961d8cbcb8208781f5fd669b7e72ea6e9371fbcf32902a6fcd9f3f5a07a8b67564ea01beffc38808cbc95726e1025202023467e7e7c7c36e7a5d7b43999079b70cc873fad8db4a9d783c4dd47957d26616258a9e41806198887cca082809effd3681cf54cfea3a72faa26b70c18d9ec4d0c087df621cb468ecca44c525a06641f323180fe45ab49fe287bfa42644f5f3ea53fb23de8f7c0f16188eb4fd319fd0b7db4c6c3438264ef32e7209aeda024e946fda3575c41c81eebb7167f0784105a1b7eef6c75c8536b2418798a2fedf6acd65ab33dc1748cf55bcbedffcae5ab60e50e1fef70f7afbc92b22a49c13b2af97e9316e3ef1869846bae14a1cdb305abb47a77ab8f75a3d566c221dfddaa7af995f52c18ab0ba78bdb94261cf1e7c79fefa309877c7f1d740c7d66ca7fe3522cd506ba51fad70dfefe1cce301d2347fec3352d93aa8d185a5262d62cadfc24a105235421043707293bc321af05218edc90b8f07902180bfcf21e63815ff823641a7faff4e0e38e799eeb622d54ac85d65e7c8e2f538d0ba663fa8a3e9335b54267aaa802b318e67fe2efcf31d31a9f3ae6cdebadea996f55696bd2c6bfd2ae7ca935d212ae639add79e854c2990c3469ddeb4f5daff1a0c22aacba647d58f5f362d57b23fc62d608bf568bb49325cbb7a3b23ae4ad5ec711d7b2363cbc5692a9d6f21e4dcbd17bf93c635d7c49c9cf8f7fde07ad8a5d7f71cff84d50f86e7d8680ea8df06bd9e01b77605687bcf2751c712f6bc3c39fb49bc5f3e49aa0d0bebbbd4be2b0f75a544399dda42bc2bbdcade815478f55e5885c4cce39e99cefb4eaabaa92f2b99a391e6b48449b53f4257584e5b82220389f6d563cdf65d887ca6f34f724535a311dfc7873bafcae8729ab4d65537df61cc3f379b80d0fbe44389f8bab27534328bfe3775cf9bb6b445f9bf856e439df65d9ce4d5889385d9bf7300b973bf93f1ff99b7adc7019184108d13fc4cd8bdbcfa3ff088ed9669e0f4eeffd458fc15307a0df743e37dc5aef9ce580f031f66e252002fcc26f59c72ff0bdf27b0de38800bff85b7fb12f754cc7641fb29bd71c7e55586a0835d5e88b3d5f7e5229c8e8b3d15fa32a7aacca873daa34622f4bc0bc96ffc7acc67aafefafcaf70a23f4dc1caebb820a29ee7476ec3c041ffb93d60233d328fe5849ee2faa3030ad71df1b251d50cc03f459538dbe4e545b1ac5836fcd7bbe4840e77396314ee9b4f9b460ccf5b03b8f77042baae8b9ce8a2a78aca8624ac7f021acce7d6ab43721e73ed2a42d7be65f97d56cb429ed08f9cdb46686d48f139079d0492ecc5c6bf2993dbe25bc0bff9d74656ffd1088237272724870fd9b88d2751cc83c85cb27f3e05dcf3aab817df557dddeed31dfaaaccb1a4d40f83b15704e1e8450668e3f74a6b5c6dc7baecb6ab652e9ba8dd1c61853a999c831c2c78310ca5f446bbd430b28c422f505aa95a8bbbb1513b32aa9c31dc06f19fbcb46ece5baa3efc639fe1b0bc999cc33daed53733ae0fdf6ae0146f57fe0b6095470fb213371f9e50b94b6c639110bd331e1fd5c97d56cf4b9abfc0088c70540dd879ae6e3dc19658cf48f54ae2a7155e1f896f1fbdd6887f011b4f4c84da3fcfd8770191a1fd77fdbc17f43ddad5eff52e908a56ee47cf4ac235fe07ee3152308c3755774a0e796aebba203402ef6ae88b17b7c9f0da69da8c4ce3654b9644502000004a3140020180c08864422c1703ca289aa8c7d14000f7e924a7c5a1b0ba42087514a21830c22861010010000919999a601a1d84f23123ae064ae4213926fc04df9ca1a7d842d32fedfbc34464351b9d2a4bf571c3ea4db504781b7b6d1e66b194a14dec4f50ab308a4508a51afe7c564fe2793d46f349001dfcfb7df93806185219d9e5b52f1c5d248003ff86f878d4cc315ea4b2e23e741195f96afa6a249d2d1679180a1c55ed04cc998f87f56e86a38590487854d84d08a9ecb574dd4afaf266712f2f457a6ace1f4b60496946f400bf55da77f8467a50167b56ca1939a10dbc804f1c295bc41b0f08ba725410994b77062b1e05b0494cdcc5491dc65f3c7b7c0e22e3f7133bcc59b10102c6131ef22079104eec18da499a1f9368f060aa6fb584ed6b9e60d52aaeb87f7f478e074fde22dc1b5c60488dcc73df2875be2d859c63e932d676aa0759c8d13088ae5e9c3211712ddc770b234e16d51b259c156ad00d6788fd59396be6b9717fce3eb8b287b84f205a1bfc4764342d0b6e58806a5d5f86275fcade366ead82f10c6af0c235b545427406386b515808a13e253d4d81c72570355ea8988c6236ab47e88a0895dc2250e20e8f418053610b06545c4fe1424103cb17435cf5c22642f6987334c05c815b0440dbd5cf0d091431ffa9625910707318024b4a794169d9db055f536724b591fdd8dfd36c61ee7990a505bbb8d3a06f0f3fbd1293b618442144ced6348f06e8f1dbc98ecd6d8c30a35b6e4588e8d94ddffcb0a75c62665c6f1e906e378e4192b31c64e3ac30db9a58e1477128069c6051d1defd11876c437277bc18238ba1218339b2b0e9cb05c01a4438558d9d3470e4cfaf610e8bf06c17dab914e5598da32ba95826e0dbdf591b560cae7b0ea71981cd010548b7798ea12b0bc788c11e331d6db81a4f0aff5b8c762c3c800363ddc44c294a78a9b56bca67730e3371af65584b3ecb92a766b1625004e5a6d6caa90303b8f3dd59420dedb334c4929d250d202d0e880f10796871a458bde1f967110d5e90971154b9a6c5c1d8d3dad384d54aef3c42b1bb115076f37b37ce20915c68e5dfa8f6626464aaefb524b2aeccf88b6a911850bb0fa52150560a34182be4b665f6af43038dd861b00ce1cfcb6f6c90180e35489772f0bfc7a99252075df86edc4175345bb6aa18fed3e2ede3e175dba48e476cbc49b7243a9022e06074085d6ea333502b9614a310b9589256fc7e534e580d8a5497b781354912af1d92868b2a7d04115b61867663298090d4e5c2088c0f1d01c4cc72136c5f90db8b242f62abaf5ef64e1aeabca144cb98407f031e7ebaf83e159cf7c3729fdc171ba307d583a2a3b0eeb9d04052f12b0cb63fcd77ec2fb17dbff9ef691cde2d901bc0e1fcbbfc7359a737d62be268ee2c99b5ff4b9b22c2f3b7e970f8ab0935dc0354d9b94e48885262057c41e89ffb8494804a9f2c4119273c1579587a57bc903009960b67a53587305743aee3a4898158639ab7b3918441d13669bed41171d11c25cf536a0e5744561e8aa184c2422093efd2210c28cb7e57476d485485f1b921e35b60f35f0e10cc2a4e19815683eb7292f78278c456a45039e2f30f59f0dc013ffaaade8e7abaaa432bd2ac7c162318631b6837129608c82381006a87e16fd72060f2662657250c59fc1068ea74a2b495f51dc58f2209c97ea6ec8ec6ff59c5407e771a33d7afcccbe66aa5e297a7c187ad5ffc949e1171fd3c26a6b0f31a3936b0c035d9d9c0a032647482daa4043d44d55f0529e0f62e668bf5698bcd5e241ca3d4d005e13738764d912309d69e32e1b15c0c72cf3fc8e2ce61c8a4f99f0d63eee14cfa18e99b1f085226d95392f3a53a6ae9916b168721a43575b44b48cd43547150cbe35daaf32bb9fdf66f741395faa2652054c733101b16ad718a1ce2b230117e43a022f5bda1c9e0086d5b4e34987c4c7dde42c4a4ac5738bf14879b9acadcc630132fc9b8658577eecaf804cf028589c65c21615bf951fc9d0003b5acaa4a30d4f88abcbef5513d25c03d59495d8e0ea1611c8345195c54da13c44bfffe7bc57a859632b4480b14e4edd6b9549b7e3b68476d8f26528cbf1489b6fb50e7614c2eec54b5769b8324263ccbaa5c5a63e35f305fa8f4201e7bdf381467adcd46188be6bd8b197a8b6a1c61477bf0064ca20b2eb898e44e805188dd4254958e6de652235577d510acb8f0007a5af69171ad8237d55bbe3d78af815e0aebecdd632687b65e40deeca8485d857633dde5282fae62ed3f79042871b45b3a8dd956b2d278d56b8e0eb3d5ef8b5738d7d0027773397566afa93557c8018df0a18e624b672f80131fd1ac7dc11068c57b49b181832df201816f3ef4c1a225f110b2c46d92c9e9739fff385c847ef7a54463cbdd9a46ed87765a043ce77e1fb2cfef5bf033fe5966106e756ff2e4f68fc348c05fd0caf7a2609bb9681de4c1e8b91fd578eefe77f59bc91ef8d2468412cb3b8399ec5d703dbcf674ac48d95292465ab7a99677b8afea3677581f5fc539fe80cc85e94b828c2af4d274e583955492e645c80c4dd192efd004af710d17d472028b5762eb20c94ab02458d138c0563cdb5ad561a4a154ae78ecd8cb92225d4c0d593de0ad5d7d6a45a37db003f27cc2bc788980532f58e4aa44fdcdf2b308169c9a44cccd791458f6d864855152178295f92e2b6c4bedd2e25e0336a078c84d2149e02e4905f83bcd29f68ed81a731c1d809bfee8855983f985ba5bbc995865398ae9b1b847b178b924501f0079eccbec4e0080531362c3a0f88007bc1110a7f7bdea100ad3374c2f19d0c42b1eda68c84c34ccbfc7794ddcdfd7e5480e3d585dd637e48757fa527abd269c9c27eabb2e67eec3686948731124c69899be399a8d82565a278ba1185650a97a87e277a63dd10874d6b1d8fc967e93c6b36cb7bcd280a963a63228ae5e2230b8fdc3e88ece8fe603b50edb30636331e4b3b8fa20d09d761115400189fc8703ed2c7f38d6b4a16bce9c7b5cc4c2b2f5d39c1fcb16e62ea648f31ea533580bf7be30d4fcfc271096e676e96c5dba75301c1fceab2a8f6417c1b6782f5a021f85bbf5d9d38f40f9b8f0188614af35b057efd6bf9b8cc3ee39d637a7bdba388692da33b3c4da15f36d07a593a86040dc7fe134251c54c9f1d3fe4d721c8f39663d41e8aadc20add2abd7f56fc17270a11ffff034b903aaa183e862f4e101414f8a0b8fa6e7b04c3bbeca1eb18115716c8760a0eb834ba9ae6db0747f8830867606aae5f986ab0a8c64d1cf7cedd162dc6e50d271ed3679f59b5f76dea5b7ac09c593a5b6e9d4b20db1f7ae725fc1f7c2e9cd1d2f2700e667fb951e3efd4364182eb0d697f7d662a1cbfa4516a6695b2f2afee411e9e6e4d70f5b3ab80e63c67304e20e9a2a22710a74bcc5f50c5567f61b9701ba3e84e4e2fa269d86f27774c799f969f28a835eca76169c9e22808f1fe332f7a54bc53501054b07da755a55d2c20e6e50c6cb66546bf206e29cdd7172b50d010b38c755a7c9985241163f4a04a48302ca967dfa705bbc7517aa3811ad429235996078edd7e9cae031ccdf96bbaf16931657430be37a656e41332d7e453f8e8334aa8e4998106b501d9fc6034681d4422c8f2bd1518d72cb62b2e76ec15d7d8502cab0f61592f42a5bc5941e84c11a1f9c813d080b866a1e924d5cc93f4276925b16bd8e579c41000e53267679d53afbd09122b1a3e79deb485fd4632cd2084e50bbe114bc0a45a2087656f82b25d43358688f6ad36c45d0cd14674b8b15155f42ac41c5517aa088a39cfaf8c71e29eac65bb3cd7b19770a8432e4b0bbe4025eb66d8009dda5073204d3bf18c67518810b242fc4c35d53a051ad767182a237c234c476e4015a14877058e3e64d45c228e272dbf6f7e7d984fa3e2daba683bfa802491d79daa7c26333e1284921e02c5ccc577362d46bce8e98f98ac9ffb5422eeeb14be38ef4148ad959161e863ff54a6e730fd901d3529c4af25da7725b065068b090584e22604987ff41577679d61fd463bc0bbaa9e96f986c629970a0b9f7b11aca083fb53f9251762f29804d6da2f4fb66dc814a5b46bcb5b6a785243b38db267946491014b96d23fcd76b9c628e1bbf5f49b896a5a156cffd82281719974d451a1e06e68d300c0198bec5ad8f60a303ee34324c2edd8f0be56fecd5d2b2e7fc62e1a4517b8881d3b1aa01b8da207bcf44df051d9613ca6820f353972680945cd9dfc89a016e30642d25dbcd0a160f8031f9f168a2c3b9d5192731f2657b63243b210b0c5cbf79354ebf3ca9cfccbf52425d3181d77885d6525ff93854833e495d3c8c3e2f525b8100de30dde68b32ecb51af4fc7a02c04144a7c5e6fb7e7140391e9d1c2d9cb01c3d881a4ca586c6a28f734bcbc730bf47804d721295d3d2783dba2c420272a663529324a188f5378f849caff9f3896ac5a810a5ccab951cd9073837f9e9c13ee67aaaaacb26ded3e67e5cc9c190138c97807fd16d1d397991adeed5bb9fd6eea3e96eeb6bf6585ba8696aaf793160d3b924e51c54a9d4f064862471582dcff6bb3b0400fa3004a362926ace06fba176a2e3c0b2cd5519c4ec7adcdddfbe1f8963555f8e897e11c43fd731b33a1ccf7a5b22568e810adcc82334947afa989e596b2fcd3590f63e61b7afa804fba257033851be1119262a06bc183fed6e39e30ebea7ad2e5240493c3eed4038858ee073512614341586933877502d91b147971562be582b59259d8ac2626dbbc3cc1c2170e5eafd851dd61d6cfb02f9c2d69f3c20e46f2ac7016807c742d1a0829ac88386c09b09aa61cb316e3b2a0d3368de9fbc7e729c3627ec769e3cfe020c1827e27c6337cc3f75f8ef8d44be7f341afa47d234a1fba05944660e89634de735603b625404a3331033bb6c57b4975de0ba0812a6d0812bfa69126c96288d7ef39ff8dfbf83b20c13403b8a819bc4c33681c467b115df101dc2f4c60901ccd20dd2989c7ee18dac4184080d93cf778a36d075c626919dc5aac8d57a85cdfcc402ebd648e7a964215c893fbbbc31db832fec8621f38c2c1023f16adcb92cbb2e591eeddca717863abf06f212833def8f4e7ac24252206621fd3fea32b2ce370e43f657e0e76725e9e32a23b379d32f09a966e37e4e426f3451d5af41513934bed5c884bc48d32cdd3f1e7878bea866bdc0d4e4a1f5932da1dd49246e12d435fa80b855ed3c8d2051708c9f7ab5c3bc4df2d7a0335324f98ca22e787ea4e88cda49a449cc2c0d7d01fb50485fdc3d86681f977560d515622dde6f7c376dcc11b47febfc5df1ef29cae1d8cc13528c5f085aeb020dfc85ae013156c9817e650d2a999cfa4dcc6464e55bc513efcadf01aa93e4a5a4aeea9197014c27c873a5c9861e5a15f6d6125f160a87719ac0b1b3f226a51cab66e1744806092532d747990717056999da9f2f03005ad691aaff129e6c308c60d6838fa6854383a6e2e698a0ca81dd1d87549a3396228ab30873883daa2f415ce9cf5afe6ed926044ff3a4f99d9b8cf6aa2071c30276cc59dbbc09c925f48fa1d92e3ad34badd113031685d498dacf23db23ef380ad9a15e1ea6f863310b796ccab09bd36fb322f74010b199911cf1e5364cf7ba077eb3baa33d56b1c994940261b3a1cd9a495fa38231c246e18761a590ccdcdee81a6ae21751e59494662bfee39b3d7d188a78eb3568da15dd37c9815fa57cf41e485938f7a8bbc41b73928036cff66805daa998b730da2014f332592b749e9efeda51c2f8bd1fd8f0beb910ed9e92723f04910cd69d75d119c482200f5e4ea5cae9137de023e7f658fb1134eb42ab74be8b4d35ae2c779e90327bcf5be7536582a65be5d27815d0d3e668b34b4db10d4151a57ebed9924f020eb43cedf761c9f7f6148f315f6970dbfd4f35f5c68e208d3b4572d238d04554e1118a272ccb7d19229838f1de234c0a3a82a99eb7d7053cd802bf73df2213075dd3a193289801d4ca1b57f2e593916677c878ab106f400aae134c349016b9e1e24254c9694f103a76e93561109606c0ba74092cfd5396cd136022dcc38afa11584101a907a884ff8c9beb1b2ae667cb664f3bcda6a83f365892b482901ccec7bf659b8130c153b0d1413d71a90283e31ee105a18780c3eb242e02309242283f16f3d62f23019121e9cdec8be7b3ac20a87e2f5a6498d965a45fb21837edb7bb5dd4ba4f8b3de83097e70da346745a493e64fed831a1967524046e9f2dcb319693526f3f22b58e0f23e9b4aff3dd251aff6dc726525432d2a39ada450566799c6a3209549451abf15037c881e5e551ebc3f01d168b795e73745e0db68bccadf9da57667ea6262136cf48ba81ffe59094dd443e70ba28665440948f0b0e3954ad691102c90c72095df6fc35ac1f510401d4f6e20e3f77e5c77cd2e53980d1edc25cb9e0acd69327b6974bbe2b53653078f1982b8acba6263873d092c897f4e411ef1c79a808b96e6b0872809ffff6d4ded46ef172fe5e840126520e4f5493cf1d9c70bff49313ee5adfc2581415ad3ea1bab66f988949efaa62568e2b3f0ec4074d152b32f10459b2eb3ba75190daefe5f1b1c78bb115ade0c06071e31db1b54ca43a1d17df0dc502499437a474f09f8b23e586e0865ad05c8751c7abb7466db4024972ae3037ba0e303560c05b13b270be48baab0ff79d327398e585b527c6425253090de9042295915fb16b8d29c71fd18b9e0efacb4898b34c4d413c46f81df939ec403751a4e35f7836440f902915986104b22277ade8ec4d5eecc66535d96bb7d5d616c14d94d2e8d3b6fa48370a71b85591b8ec8a980c646d0980f8a6c076ae993ef2cdcf14bda9941e5d9e9c7bbc5d15da1d806ee7e4c6173b3539b69b7e7c60eb3cd74a1ea4a95dab67e8299007aee3a2a671eb43b0fffbeeecf041b606a8814edf57453071a5bce9efe960ec13e21024f6da7e6b61489ca864d77baf04f953612ff4e2eeed55286201c5fedb2bf24a9b3d7ef9e7f7fd5156135be31f8aaa404b6427f1feef41c4c729a03155a14fda6332baf53b4c2664d85887580a1741ff9facceef74650aa3747f5f6cda97b4abc4335c9a11ff36d5f02fb7d4cf0064bc5b0b06013defab01942df33de9e37b5eb35456532e0060855bbce9a4d009200e676d568482a00ae4a8cf70fd3baa31e3fd5616c618e141d100bbb8b2d86d2e34df08ac941becae190a5a472c32a19f70f53eb92e856d3adaa55e12dc309d2a4749bfef06b758548cf9dd57b25b289ccc9e6c01c4a959ebb7ad673f64f388990b53cef297a4cb244d961a1f6ac1d6f28dc091c58bfdbf9523960d8aa3fdb109bc38c18276e36d8c10aac6d38d4c0ed2947e93d90c8f7b35070c9fcb225be59f03b0b21aa5b17ef5b8ba20e5d3a882b845c340b5c3e0f18f35bb7b05b573c2d1417067eabfcb0328854cc4f62ba7ed19e85eae9efe12f6dfebccd5a0cebe799102ca8d142381c5415d1fbb281afcc80e2dff3a3806b1d463c69e1ed4ff5c82e1df859ededfc42649e8e01cf96ea776a3c90c75ce9193a17cf53e400e1471d476756dc5a920ae4bb2944ae16aae34d0d6109ee48ffde18351ee9c0bb438637fdee7a4e4e62f4e059810d31a04596645f2c4e032d0e17fec5c083205578818dedb074114c3dfc69374a5d3281448b9870a33f23e9a78db498423438a96aecf57808b76217d3b59af21e087c7e3f86ac2b9db57e3a285ec62a63774a81a56576f6a1b5f7b2ad85faf5049411977f0e012093cafdcf881013bcb8eb53ecceeb3c05eede49a73fa3f7f7f82e254f92ca3c4187f3da0bba5680e33656d0c1982088dc937f4ba2e9564403383997796779cab2ae66b1d484b3cee61a947be4e25579ca3dd6c0857a8b07871281f120a49a9942f0e45dfd4f2299c9657d06e0d8e0e6e943d5f847468f2411d64221d529e4a5f2a4304c8644f0035146920910e291dbb085fa05f43718ade45809d9af56b2e1a850dc2801461e568c7cd821c0b19574020eebe96fb370a742800bbf364300a0e1a8036d169ce7adf00315eb9112094822b3c6023c8cd3bdb2c783194fde9776828ab227642553e9d2a3ade1593987c6e7d80ad0a2b2a89b754fe97b6e339a1ed5136c805145f25ed79c745a0713162d84ef78f28e29e5dd458992c520721cf0527fe6908cfe1d70376f1e6c41471debd0b6036b0ed89fc2003d7660d67b74ac30e6fa04950b9a76c357692a798192043e4b7d35553f5029cbf239afa8370ebd068a286e613bf46edca57af628756403de15dbee8d5d6d37372aea6b79cb3c2039e0d4f14b813a44530accaaea9dfc6ac82c3b7523c6cf2c0fd347ba97411e15dff2a5cfe33bce8654d0a3db123d89eb97edd46b882444e5671fa4ae7d4d2da27bf167b07286472d80cb63c4c659ca3e6d8772e9e165c5da83b6d324502b6b58e94de6135aa8d4702872d57fa41d2febead44963810760914d0d80138b930b088996001dff0277f4823368d37427e28a737162176e1fb24917854b76fe2033362d185452a3abfeb4cf7849a218b895e41e3e982de36e12992ca1b0e100581c25ceb3aee64c01be8fcdbc435406f082422540e1f5dd0eb0fae31ffb3b0a8911b84ddc8a68926aaed4b990e08aa9c835452961ca9cec4102ada9fb24fc2fd924f17daceeec75901ca18e7bfebf0acc7566d2629fd1a1a25aa93879c4025a47465ea8d2f0741b29243d0ff7456bc1d92a26ea9ae80675e924f71e803d8d06a6891a9c70c8c4c6f0abc1c2957e844a06fb0ca7c28366bcfbd0c1f7dae623b10940a2da82539452a51dc9c5d4e2204539dbe914b300c66757d97909fbe5c632d906df200c0a9004189f2e23e7cc1024439f777cbbbf2342690a9d0cf8bc83dae8fb1fb0dea7e14e60d233ca41616c7105fb6cb794096245b5542560e3c5f9e3995bffdeb490c1be5222360e5ac4c515c54bfd736249aee91d59af0651c04e1c81b2b72276ffe3e26047230db751e6d030205504076156dc4d16caa415f6d881cfc7e53203726e4d03e1e55998ef01e0ec2304f1196ba31365214558ce1db7f5f7f44c376ab8308fa74ee23e0d1a2f3571c773d7524b1f7d2ef01cfe2b8a45e8f2015de0675e83c6bc33ecd9c726637f2f2c05b0bb988397976da7a82a85d0ade16695d410be572e538279e9151f84250172353d2c8ad23f4a63fde5203f7a5c34395c1ea24160680c10710d26b75990656ec70a655eddd377aa667a27fd1313774a66e3bc4f0b42a688ec174b911bcbcc959a01bac57815c5c6a2bb35146f39ccfcf7541cf64bc6fdae21d80e6651666c4676a618a8f007a5981e73490760e8a488fde5a239b3c193c56edc809ce024a9955dab67a6abe2d28ef8c0f468b85bc16565ba3636b82ffc24e3cc6431ca7b4dddd82dcf70d80af4e24757d6ada7eef5321fa08160c3661a0b4a0ddc829d7c386180c134a1a7e0e3ba0df138658291b8792eaaa27b3f33556471aa34ef5baa312e05052e42b3041fda6c2ac5187905d8b8a4c99d98f61a9731cc42c765a6ffe0117505079e7020c2ca125a8c5eea328196f4adda5bbc3a685efad5a6e572303883b3c71177aa512ff1d268db295f4815b9a8ca28d09f5bfde563ccca517d117bb397b7e93d7cf517a6cae43a349917c3fe2c0b56f91d152c0bb650ef57bcd83634a5a570c737d61040e57655de7d7f7830ec032a5171d2856d044226d836ce241999b4c3912025a37aea34fd10d08bf5ee8e23cbca1d95d7f9a1cc79a2bd515210de54e01e0b9e172a22dcf146108e7c1cf9e747e1e98c8052dd0779b7145dedd59036f0f7c80994030ec2170de743826158e0bd53fce29bc0114093ff2105f394d030b44b65d804bdeb332a845b9cba8ba486702b58f1c3f086970847e038351974c194a1da84cf42a9875b2b752001849ca1cbc19d92e0247bb00f9e7f8c7d2b172c70c2cf9dd1350fec7db2c14b6ad057fab93093ddfba1af80c5661c0c5e30bbc6b30ba54d781d5b88720e46c1f068a29508d819d5f7d6c8d419d5de2b4256a99aa0f849ecd9e3263edef36bcfdfd2ba69dc5f33cef9b5728c15c3e2d68b83e9568118e90fb39b78c90d9d8a29235fe7d891af2c5f927d9f5c8ebabbce27e201e001b0624318fb38bbfca87a64512bfedd0d62b17e0fab742cad56416625220b833b9007776f773903a63e8be18c7b1ca4dd5cb30ec664e507e69ddee648c7ae9729341ad30a9d414cc480bc168ee69c3c06ca8831ad83340f966beed55f58cabb79cf26e11b1426def051ceec9e5ff4520816aee40b8a3f39f678435a37ab45db1bb60bd8e6f6cd14c5b0ed17e47d98e879175d3e99d53ab8ccfa2458dccc7833753ee5fd001f1c69145e77f55956325bad01e11abe03502cc00c0edb0effdfe6dcf8e9f9c77988ee8eb94842211ff2382cdf43f1615e7f1e932fea8498dc6f3579f69823f51da2eb320fe5698d0ba5dfb762cd45278577cc926113c404aedb660d2508d4f4cb45cf648c63d7761c7a548c1c6e09ca2a6543dadc4c0315cd5ec8af2be4385219fc8130ad9e29d85cc703624b7edcbaa5c7f8e50ddb9ea567c47167710e2ccf30f2dc0e44738ef7456b1de91bd1337dd4f3c0f47bb99a9232de7406d050055ba0bda93c6db48fa778d7ba20ca0524926fd5bac86ac07118801a15dfd2780703ca043fcbf53f475ddc10f94aa1820a0219746307e894245d3e99339dc1385378f9fdc428e8278e850f09cacd52e3624e5c1d11db5bd0ef0d97dafe851435d47a1d161a06a223f796ee5662c77e2aa3a22a873231b0963655214fbe35415b76094a516e1081961465509e50c775ad634cc747b5931eb191f52abc91a18d6d400dd4bf48280e4bbdd9021d7720f42e63128bbc181a0d6a54b39368399cc0540a8996ddd1a9a4e55f5afac2f13fb3349562c303113c61d67b05aaf30ba67874875be8e08ebecd15aeb471a332d7942abbdb1c8e791c4804cbb858b184e893a24e9bd8e1cb04f656347c1cd05c9452cbc73f29a393644263ff990d6e1d97dddab07267ad3db098dd5a37ed1fe39bc2152fa130d33206388cb5b265b1ea74d8e6f8410639efb02065a8dbc8ac58c60d0e7cdf5e15ff56b1295277fe811b30683d50f962085471a452397826e13b76d8cf60973ceb147df1a09f2099e4a429c606ca063b1fd064cb61a2378bba66c76be306f2c45ad08d1261e0b14621feb5c75102ac9a711311ea63a99502d51a66c3a293dbf6d8a149c913d8502658eedd64f34db8502496a9ffc50003543414088ee85ddd528b201d1f11f52788cb7544e3b0cb8edc83c2dfbc1f442de1ca4da036adb8d494136786eea602771b378b5ae01137fef28d04098aefccb7ee7bd12fdf96d33624ec268d5343ab90e92de29591d88ca98174fb0d04476b4eb0b4a9e65ee2372365f2ae3d4b416efad29517a78d562e142f0bc7ff6d61fe1ce9d392dc8782186a38a03632d0593782345c6e279341067640b63f01460607e6f45deedc8c96d9ca0c94ee8387d77c390e01f3e4be7aa4eaca766a2b279ee3ac62a7bb1c0ac394c122533bfdb9413aa4d595e0cfccec94884b7ee61df60e0c5376ffe45b30b73f403bc2b1ec5775fe00335f7bc877cc1bc3be8a53f9cb2e80292d945a86edf36fac00a9e7e386c58f78b642e52324ee0c88a0ccf8eec24f5928ec437dc684be5745487e3ebbd320bb23058ee499595da0513de4a1c82dffe09811d41409cfa6181b4502846f82525c767d89d40ede04aeb90fd8e5ef5e4410074062b6a9888201d5a31debb518f719e73ee6521393d00005b43f943f85c7b2d29e426c437b2e12becf8bc33ed151f17d4b842c623f833fb2bff51d874c2d4673db42f15a869cde7eb592fadc82b53d8aa683181d6396a787b17b96bd9d665e7db2b86f0541439cc0913fb196adcd063c727255b5c6c1f4be7be0fc21ceb6b63e03854890a4a12d423a31f1585087630e1bdf0a1c5b43bd073bad71352d69ae9875f90bac503078dcc1ac01506b260e21b6e689443ec6ec6b3fa8d536d36b6116731c389c8e7fad79a391c308491164512d05739ca091c413c52c92c2dde48823bce04815787be8c1bef84fa8cadb872a2da41d83192603a4d060d5bd07c5511231ad8c955c736efe4b24f9142deec74fb8dfa8624b540680cb8eabf3e0211b496bd6056940d5b9b4ae69025c5a1fb8d435bfb0c53cc98a8147b853d79640d4c7cf7bb6ebd7f2795e8f58214762ee1056f804c6e4e5ee2a0919822d8dc566698571ea6511afa1cba44a837b363017575a4e8c714a67ea481d117246ea91054c810b7a0e5c4fd006115653feb007bf3d3297c16d1e9e0313556288f03a4008781674cf45e150921e1fb06435a283b732917b984519459f64024081e11559abe3e4edb2c12ce4fc1427248f97075ae11e4c6ba9bcc87c95a2feac5c3a8efa748798317b0b0e599a312ceb732384c48490bc64a1ceda6c0e3d989b74fd0b3cfb6bbe6a75a30556a3fd4a2881809a0dbf09db880838a678d49a2498d0b1bb1a3111276222275474a5e8bae4a0f7c86c51523369cd4b236385f78f065b212847abe10e8d3df1c2b83e762f5cd0c834575313cb1a4b19444380569c8eb2077619c9ca1d5d472f139d501338f12638b12672a24dd0444de0c49ae8891bbd26e2c48e6e263a4145a7893db1892c749a9843cfc44eace8688263a204092f9ac2dc98e64aedc1ec9d0bb287463fe9f7541efec1a2f26da3e4953166a3c50e119301b046edaef3bce20d5d6130e34e9826ec33719425f9e80fc7b745f413aa03194739f022b4f9090b674e4a4ce61f400ebf5cd5d3bb7b0d978ceb316f9a56546c0e51efdfc6466403e7629d18a8e13f25a542824fdc7023d8bb17305d7dc6a65777d40c39755e828c66d022a44a80f379600a377198cd34d2f2aed2dd4cb70ab7970521beba399550970af70c782518c3a320007256268abdebcc8e93cb7f0a4a651ffd73a6bf2b1ac1898055a810a0cb20b8c4c3c4f908315d31d5583e134a8e224d2c11ed484789747e2f40c906d7d73e2b200c86b0111861d7f5d75be5e1eace18bc7a758a4da79d29764245464a08bfeb6261bc0951a389ff71abad9c4e63f7310e031d156b1f793436e545edc3fc5bca60c6726e3d6a14a8671a69c8abe6f2a1990603b7722ab3563af66ff3761654c4c5dc4dc0a9f229d9cd4c15376ee37ba2e6756996314470c0020aa466fe8fdb3a1ff24447e69e40c77ffccef3b5fcc79179f8505e7b17e30c1463579ee4a8274c103ab8c84f4e261540285abb7c9deb94b38d63767443a8d5cef34a09671c0450dbfb3f09cbc75a3fd78ecb7027f0864aaf7f12ac64c7f91144314184ec6773152fa6b3ce20436d5edb3914c78d0ec67b1e0421151ea8dd92e7eaa2ab52bff20a195c39268d6e0b09125a2c3b1fa9ff61c81b1a6651cfdb18c54fa83741dc20d873629dc4f6fd935e09851c0f7ae7125939cfbf75fbc4616e57a1d1dd2e3e9a9c9de21933246f0aaf731c0d7c18e6d4b8508aa9efa89841062079e752a9200c6b944de084d948933ee31a9a18e09664bb51c570c6f65c1e82cea3dfc4fc99b12213adbb9d21fb8637b35accbc1c0dc34204038a41828112ec3f86c075efa03d597a1858e95b730a10b3d9b501abea2009bd922835f38077f510829a2d76a2763571f44561c9fcf54fd4a43570ceb0bf9546e7d819871a9d7cf13430ea3e87a4e25c6caabd7ca2edf086b028de62772e05a0192942f33b2457c3d09511611bfa40bee02502a49d123cb353363e76c61efc939cbee8cf1ba48159431af323a14dca9f3a17d47c56c838c6ff5f84b93b0da6d947adc0b7da5330c2e0328ced3da66487f869c3c8dd483032e1f4412d237bcfd707a745e75887efa9ae98b42382f249fcc42d5c60f5b2715196b6555f6ff318cfadf9c7be1b998032d86e61b779592c52a009f7b3096d8f40e3e6f437f9509b85f722de6b2aa630a02cfb0d1c1a0f520c0e3e10d41dd6020d47f6a0b28a42dc5c3df01ebd65761a1881d2f0fbb08d63974e3d530e3e2143677caa749859a55299bd6d2cdb75f2649386eedbeaad075c55a3b50628ffad84d1bd2bd62374f12f20b28b07c8c50b8092d0810a1b07586f53c373e8b540717b19a59e4e384007a2c7e911e29fc43c00b1da7edcd739337e462ad79befccbeeecbdd8701a7cf28eb221303d02b306054fc416ead9172ace594a3db5139a2de3aa68c3170626bb393aa603b29fd64bf06c2da9a43db5362405f5cffc852eec412b6e52306e9098a67e9728e78334b733bbddbc1625c83753b94d2ae9f2fa3300783f20b81a387d2abe8eb6410c25e0d044120cbb0e198b9b5b9d8071931a07cac4029cfa37190ca3e10935f828a5686d6ff1757de7528a5e977d84d07e38db6a33d4a2bed5f787316ef18379c212fb6b50ba92d0d8ba9fc46cd50a35f9e930315da091fb4ffdae99893d5d917096bc6cafabc31485cc12ffd86b295d25d81f4a2155844866500b82f495d2dd47bed95c7c51ed6ac882c4e7d5bf0f3fca7f7513a95b639252cfd042c70f934559a6c63996096408ffbaac5c71135f1381480e8aed2b6b035d96ed8432efd5cd184333061aece1314adc2eb04638e3aa0f3005b4858e174092b1ed102f712564228b0e1d67e6f8d578203a054956e46a7606873f1265fa5216baefee955f44caa1f155e25768bb13239857dc939ad0829aa4c85a5f11a997a24c7e49b5fd0c51ae9800562a6a07e459a485a5e6ab5aef00129f181df64ad1f2587d49f9baf81851a03c87f9434ce1b2bf84aea84857d80474987af5ca70e4f32b1268b8b5867e14b06a8d5930809ab9b8fdd11cb9e8f250a6759dfdb789bae008d3d17e7b411c53574a7cd51c4681a6944c27fce79a41405c25387b8ae8840d0cf77f83424162babf08dbd662ef7b663b49288e7e35c4969643b0c09107f6c1f3393edd458736e209d222e61150ebf1056b12cc788f25444727de4f7281755746a16f5b8d9d97309187c16885216e9030743acfb180aad849387ca6668d1cae0731d36a114db0f42bde7ac06ba7983ccc984b7375eea518bae2f90a554e9830c97d688893462c59ce3d470a0258297021162986c232caf0cf8e5d635f7516609964a06394d7dfb45dd2b50b48cd29b086e7881ae792c2a5799b1ef41327ccd43fbafffcfa430b40055f4865aa90fc7c4eedae43537034c6bea405a9d24c83eba6a22b65fa435e07940faa5173cccbdf44b747811c46c2de96525f60e86026bf293666f275c98687e210432ad3a420f990f8baf283ad2f1172399870aecd00f0d39b13167a1592bc156ef857ed6f3f196d41bf08c8f58dc400274ec21cf6bac26d4a8b5270b2efbcb11099bb15eb8db54d7cd663adedcde33b29c66d3ce5fada331f6cb682f41b47382253a87aea0dbf402b301d2c941c855036dd11fc1f31bf3e91759111d98145b15e2ee545474e80dbd3962a47f2c23cb6a7d46cc13f810a0503ce3e52c288710d73a923816c525a478aa339de49949326f4dae8b6d7ff0a515a79158f4b2ebbc7b22f245d102da061cfba6e911ef6f1698ddc7bee1dd9fc384aa4aa107c109000637f26ecd00e4953294b1d0e70df2148f6761929dcbd8ad32cffb608caa7102dedb645c9a9102e8065ad59ca4e88299884119aa578e0bc413ed29b3583451a8e1a71958d871867f869c651363fa2a6c0939ef63a5eff070b239a30fe714b981433c347d547ccaac3e35f76809c6c5cb9d70349016f9730b43d50e8f8665403a7192e97996309c26d2bb706e3ab8fffa74ba6552cdd9ae50ba5f40f672991cc86c85e42058483e453a2d002cbd8a2fafc55c8413de380a37c2c31c4c99a81a608e9450f221498907d61e5ca4b2e9accd81a74cb09900fc5045413fe44913b5bbaf13a74080d7bfefa4ad9a200492b6e0b73253021253e4759e04b446f7e009ff84a86ce2831ab3a9aa5e95ac4ae6983cd6e1cdbdc1e4b1fc84b82588600030fce5e2eb72f620aeae2c33211c5f3210c39378060fdab7012ad21c38a066c20ef992b6fd426f62f09069bdc5110b9f450bfe1decd6c732d6d209e778a68c7ff91c444677090f7cb815b5756e856b91e65c3bb7452544fbe8188c38ec8584863e334adf99290975213881a620cbd1db4224b5017d08ef41a2ac279d759f4eee8c30ead128ecf619a0cc664a9ba1f902dbd5a01241994a6693cd316c25bf070eb7beb21e6a9e5ab4426065897480a0528304959266f11ee16302771446fad85f8a0ce129091e54096bf22fe536c1fb7a954d9dd33c3c5dc6c7d29a92748529a52eb1a8d41a2d508fb85faf6610b1c5ad58e0a5a5f16bb7ce263ec703385cf713f7170c94935141f7dececabd027909474348890386792a0c0c8bae3cd630aedce8038a60a54011741e9523b5b46a1e26ca9aab8a1f9e926474d1fc827b4cd3b63b24ec2d14046aff51b73f8f84c551a42693f58c9045c5673ce557ff6a279e83686dd6026b152533e594de03ca3d471da918490b63f3a78346689a4ab62198d0d37dd6512382c662c451b168bc05c446cde246595baee46173953c441255f2680ff1c7ec9014e6e5a148c1456e4f2a2d23864617491343e210f9044737612167b51c70923eaf592b957223f0594a7f315116912363dd9f4731ec83ba02316a170de86333d2801746e5034929e23c83ab70878633d768200cff94a39e2569263ae15e9d44aa237a0cceec1e3fd2d5164db4801442392d7298b03a1ffaf9c7dbe11b18afbe244c8ec490c48850dcf0d178d0037325683bb9beaa40d6a5a0d91fea23355187ff3563a3861f2b23c98f11b53c04791420959250da6a82db31c0fb4ba046f9c7bcfe2d2eb8716c6a65bdf731f5a42745d50e26d275dc41744142f44042ec0db4ce54d9c8c9c942d4e43bd0c1a0a5efd427d28c09f144ec20a80e4829b5b01116df245447e24bd06701bffdf5da60cc553846e86651f879013a94244fafcd966156f01aba33db28630f261261e80c704b1a5b549b86aac4a14944e27102494a3d81dfd6ebdc7bd465df8e59823558d28da55629321a081c0df095ad7d0a24622ad0101e037665ec5b02e4eb49855aa46b8691f9343e98a13f86642334929aa383dc4249fff7fccfa3f3f9785aea2b50926b6c2732f1e127b2a90acce3ab71988d72f0d7b6679430ab0b98c2a8be1a95133abb9531ce3f26bcb5d57a51b1a22751142bfffd1c5da3845f070d203c7f5949e5510ad82148fa9d3f2694e6b2dcd5b47340c0953833857602dc37f1a12594e181cdf665efa1451f9bcec6a2766eecf19ebbd56db992d21488f82f812e4a399661864622b944bbdeb331511c19847dd3486f0f0644cc776e0fe623e2bd547f4522c76c8b0d1df99bda2d0728d7676562986bcd2628317d609ebae444ba4bd934920ed5760272121db4590ace02e51e62d6b7beb9a5211b56905a67915a3f29b6939ee797895a0e4eb33abeb5c2add2d0a327ab2f178772854cff583d8a52d5fa47524e571ab3480b91ddc117b6cc81acf7296a5703994ebdbf7ffd1d8e227773d883df3dbb093f87c9107285a0e0e1ebc72e2e81382008473026661e2996ff221834a2220bb75a4ba9a2336ed4e9a9804c509de574053be22ced90dc1f0131501414a342620eff949c1f5690cb759aa1cc07901b8fb4b6d54470419c5b2d7857980b8216798bcbd4943a30d2157ac15f4603cc2af3891ed24cdefb96d75597bec1d1f531a56d0d81a44a430594aab61cefda852bd6747612a5b7285bff0866cc62efd1c82e6d67e2cfa5630a8bb3d2d0796ffc354a715eade37b5c6a6e9b1381f7fe490395512bcf7660b8cf37ce9aa1631e5d3db7f67da0f35717842320a06e045421de4cece0841ec316f17e627b366814b2fead0eaf0a7156d0b8e3812ece9404d5e02d182b68b9ef0149484587fab80a3bd35424769396659f7d4c81b9cb7df441781510bc0eada802ae0386341d3425e1ec90159a98269542d4231a349df263148f9c30aa0e1105265980e34f8b91fe09658477ac7ebfe8693e4e8e9b4cd6c10226b59b834d988b85820e7dea5da523c98b464fe5d00ca9e6539a9768d5f67f49aa323759d06f48332eae322f3b1003d6f27e329452a82898d95fe225441cec8514a5046827f2526bdc46b416f50a127f9eb421c34e39dd2b887a0c3ca3198ea9f3000be2ee54e87427ca386e3d48f5e681e36ecc3b22cef6982626e428930f82e6a390feabc7a951168079f67b39256bd05cbd6c22a537368721205fb90bcd4beeca12aae60574d779fc7fa74bd075c99355dbead6bba6826eaa3246f3344738435bc45921ea07d8c1cde984dbdc650aae6e7dd8f59166f0517b79b053b90ca0b44a99a4b9cfce15faa3a28247e78e79d28bdca6b9b69ef239aa0cc6fe5957bbc8ad4a3d7c9a452f4bf86ba9f8e65f44d464a18207846a1caa0b2d91b1bba8e0fc3d2c10af62b1a1d7a190499d3039b0e4d09b8851b5c022e712f65e5772b351577c14f08f9dacaa954711b216bab8f05b28220e5a6fbaefefaf7485c188d5cb14547596b82819a4abdb7f6ff49981ca4f6ae06d548f03b14ff88945611951e127fc83e5018205ea3e2c0965342cdc927f51f4fd0c785252a791f80590812c209245d989a983fcdef12f68a8a20b59b6111c4b814035d15749252e607bcf597636b8cbb5e0b35976e7ea567a6b3a4bd80d8a610e78b087064f40cd433d6f0ff4b401c15f54a8c5ba8a859dec5a8cde69e38fd168d14d36907b13f35afca4d1014524318a10bb4b74facbc35fbf0585024599bf47a2f2866740596f5cf5ad7d36ab28dc5599306f0bf2376faf2025783703594d696048775592896c8b87654cf2b17ff6e42023b53cae5b8f540eee857d7ce676b572accaa1ee41207ffd70558baba50e08ca140cc99f686475c472749218bca7df0ac7d2778a5fcf887c13f4f89c16b130e4764b21edd5b3dfbbe6769e6d0dc1dead10387b2d4b4f43c7b9546e29fc0aedcacf6ee6cd5e31a9feb2a336fc717dd02160b65a1343eb39b313ae406e7be3e152fab97a4fa2c660967940be10c2c7c48c349ba0b40f8d9fa0d426f50a03c304061a9009e547580ebe2822e000d8f9f5d7806b48eaaf410da06a40768ff0202d6b4031e1079567c14ee0e5b301b4c09b74a1dd6e588b05ca0102c9e4d6615e3983ca7d938160ac6143b944c532580785b7db12396bf22ec651fd7b30151cf6c2282de14a82d718c81a53becbefc44fa243aebcbb6e8e45e6d98cca3d9b187eeb8c57367dd9d04608e1b7b9541ce762b6c4df7429a317feeb8359aad60ac80d89e3e107b9a09d1536dadc9fb5423c3377e1ade04397aba0d8018ecd2371ae4b06b99245e040fe188a4eaf8f76e89c5b3fd44acf72aa7ab3f0160802602d93c6631b30f30cf05c84ae9a22737fcb583cc6c65af5d2f73315d70cc94a11b1cd1c7d6223589bd90fd2f6ca9cf4d3561b72e7c9bc7befa15ac7b4e10d40408a58fc4d47c9458128ae9924b031543b0306ca5b0af54b091fd0d15c410607da60d85c62ac31c6e31de3b6c95a8fa6aa4bff00e800c8689537c742a53a6ef225005eb08e5cd6e5a2b2d581641524c10b40b73929f758651a54de80db3ae756ce26dd6687c4bb6197a1ec3ff1e29c36edf7033487daf96a8fe97eb39dec1fc674f57d82d96b5d5805e854603eaff8cb94b51a9858e57533a1c36f803e9b8db733d4f76e36460e04c7fbaa786a4f1b6660472130dfd2070c3081cb27629e641165cab5b4cdb2e627ee779200123e4fc39e761d2c556be65514dd447c628ffc357152f6a2b75ce92d92f091cf005883d4b18e5664580274f77637bb78afdb484c1c2b0a365d3082a742c4dc05dc28a64c8d24c50d2e1d0fc280a025f28f2ef5ee5fc1c81fc9206ff76be1b745e495a69162695c5677a917c0c67ea56e088dca3cdf54ca05fee43b5287b9e21e421686b40d798096effc4a0862147089932bfab9438dc3236f617a6aabdc5341e6d2215a38dfaff8d5670312b249c3fe417b2d05f5431699044ff435a7a92b9a04e157da3b3fba5bc78e997edc0a5f4aadfa94fa955a0044905e1c74df96c85e2f243166da577089770b50862dc8fc24262ce9c064bb7971c94158876cafccfe54246eafb7443d2025022a690596c45b55a338d390882e4b1d41df48e829961dab7f9236496fa6b939baefdb652f63b823b07eeaff4cce445a74239fbe53645e2abbeb62ce54cf2eea174f27b914cd4830f88488a64af46879886e68528875086cea00e486cb7268928dc4a883bc6423447b8a4d442857518cd457249e203550c9e6ebd46d270b2b37a6fe27aecd34ddd4059a40e4639b7677ce5d467684b66b7088c5a19642d43a7241ea4ad35b8398cb88a9c844b1d506c307528611270805382b1f0581cd4b22d406ff65a0c0b71cab73a38ce2572a93b41cbd44db6a434998980f5016cb44be8c1659d17bdcded9ecb34b0c6cc2918115df7837d924a262d86f5507001c844b0fc9b95439e00493f8f3e4221e3f513434248f5840e278768a9df7f20af6015236ee093cdc8ca69e2c601965217b59a1caffb15abb76d1d2c2c6bf84cf5b5a9cb1831e939b609d609ea62750ad51f7e7e985a851f6fc7ed1096700ca1bbe385578513e057f87d22dd4e5f0286893553f341176d95995f11a08aff0e97d739d04e99bbee06d634360039b3546a7405dae8c9351c9dfc84a0592eca7f588a375bc9ea197602ee83e19c488453de741c16eb710476310f99169c58bd98d8c0b3ccab42571b1f065d57e34961ea06f03bea2b23298112176ee68c11e461fb9b4a9746e8c90e74d9b2623eab7832ef8c92219f77255e8635df59afbc3cdba83583d62f84c20593bb12eb1ff58e6e7d69a4ae41ae762d3de708cd418714a4949a84d33fe75ffb383d7cfe7fd5730c1fa321f53848f56858a88b69476d0eb64199428e0097148fe1d0913ae1c3e462e2c83583b618c462e7f4ae5baf797e1323aa0c86ccea5284a0efc6db277180242e54a7d1c5fab663c6b597edf1809eef906897a9679f2b73e70233690b9e5162802b90e712320df24f12a57c27f94965058dd7cb2f065bf8e18d1b69a2e67a59a4fe0cac0c8b0887d634704e220cba54ffbf18987ed384b4b290f6f8e4e26bc069bdb45a6c0abbaa934b5d9aafb34eeb63b94d084d3e679080fa80c782fb05f6c255c73130c8d8d42690c72aa91f97d22e8fe389587bf50b64bd059ebb86ee904686bd26829b2f65902ae254b9786a26f9727d8845bcd475a84db20edc9004eb3735eb0c844e4b8580bd37f0e83ea0acb33a833105e70ed72cb019f642dde84a36dc19bdc0468114a52e5749a7d6878ac80c444d949fd506c62d7634068029301ca621b15406332365b045d1b439296d522beb16faf03289aec4f47899650be2419da7820d91fd9db8075ce722f3e40d494df3828202c811ec5727a22a98b35b1e3cebe27a18b8def35c92e7ab6ce0e01922ba9ed8540fd00936b0c20afb371aeb3f676c56bf0a1145175640b61789caa5fe42fe31ff8d3cc6d5260ccd2f869cecd45827252d72a76ace3b9834f169f9607c30cbf70b0b607e7f5d2bd45ddb84e8da57f1338a2aec63691b1b537733432b3022cbfd55888551ee5531f1e3f566096c67ffd5865bf455a87b084eaa305acf291ea2bd008466aae41a6f1654c3ca64a58f6b016d27aa006543eb869985f82d652bf074dfdf2991086e7aad0f65a57f88792b58995db74f7cadd50e632b1266e97a72baa7ddab453f8599504798279025b5c5797d639049ecf5b174a526b91c28d17ff08ae4a5b426e2176027581b3098e8938d911e69f22adfd1a47bc515633232f4c5f8ea8b4c995133d500029a8bafdda62d03ff75937cf856bd60f3bcc35ceba38f34a934d41f28db30b185049a2b8754958d9b360cb8c303c3f461141cc6d27aac58b8f62a42666d071b2acce0024484f21014ebf4ed2f13ed12db5da4f11080fdb217318bd425db4b5baeff8b48a409849e0fa92364e716b706a2a5fd86fcf605602f3808e8eeaa2dd437be3631c2cbc822a4578e8664ff7d74269d14ec682c3bc31ad761aa3483b3a9f1b6b880e8f52cd3835fa6db463925442d46c347c4d94e82b9e620e4f41dfbbea8be19b2570e3a40f66966e42927544f033a51d7ea82845735d3f4b33f5c7954ce6d1272a4d0070117e164119a39feaaf6b4b0563b5101b098298139f5c6f7fd03f9547e320dbf9930d8f3bef469cdf68ff303560a3a8d7beefdbee87ff2152e62807cac116bf22a944b6e05f815c53ad9e43fe695e37a38e1cc83771e53c00b484dc668fb80390f1fa2a30030d521bd8e0850ecfbefeea3baf459bf0d2426ba2e1d1c3ded8a8815346526cb9962b646792a6a629ccf4647e52926d1241184cada5af79bea864068ae5e482719fe6452ea6361385cc926d941e547324731cf27cd605aa7e94cfe6c1a9504a6ca0e7f320bec8d39adb6dbcd294872957840d793f611170e36e095307b51396e514593fb51f54224fafb2d6e0328b271113dddbd01f6d7869edbbbcdd72185fb49d5d65247c9788ea1d474a3a58f8904ee0ab428f9fa07f9d5b9f6d5e989fa6e48b725f3ccc06694e44655b1a8903d1e31ddf25cdab6226d6ef061058180ef3dc8d38e059802170a7c85b813bd646b230e3ef46d725938c11e48d76d33813ce89460414794f7cf7eb991bed7db9decaa87ef370dc6d070fa62a5e82e061719a7d194d8139729ef1a3d661517d8b168fabf9a1d46e57bcf178f6ecf6c01837e9f0d2cf3ec4380cfd756edc480e6e16f71f790211741eae5175afc033718bc8daf01804a1c148f5f893c4f170e715e088bf4050fb91ed6157de90ba720f7b7a3da386eb388b2994117ca4d70e2a7d2154e9ea43abbc9154903d31193f843a98f7f5666df28364540061600fd184a0e5bb74f02febb225c77a9fc2deefb98b64b4fe12e37d737b9bf275ec91217a7ab26cc8210b2cb9c6012425468d286111acd726594a5634bd4d333acd1549b3f0de2bf485923c4db4376ccdda8bae535c32707885f9e0f9b6f25701cfe9e9b0b148b7e34e95a4f845cc7c9b50911db6448347c86aad61aac8d57a776a79eb2a444cd2b42b741677cb14c24cb32da218e6af3979c050ba3d34ac5a72c7e6e4d4ade17751fe089baee70670343e5a3446f5c3ca7419dc3ecc069581208c8bd42bbac6111e173364343c510f42164647b3e300a9c69727388c2adac7339d1308a66df31b233e567c4aea25c308e82fe69b145d6f8015182358fb7b0cdbcf09910ddfc42b273e649f3723a1105fb8e481c2388a400e041e83485bbefdf109c48135e3b84a31c24ebd090ba89a2790d2b9f12870dfac0f59e4e45b95a44d38b5239f8d8185955290d21dfe96d5df36db6179664f87815aac3d668005adb957e7fb66e3aba10552d106f820bd83ce34ac19b34ffda0b19bea54711c0b5f578968f512c317628a10359c3b3fda9616b5421debf951be9c9ff3e28d1a451a21aae1f7c68494df7b4f04fcac9845d6c4b957794cb9b415d076d682ae448511e24827dcd18a9c7a2f48a5494491bb684972dd5d5ec02c2eac11a85e93b9df36f9c5e6c16a66a5625bc937465c8ae93c732c0a5170802377f983c0bdf7beeba636147f1a805467c1f92d7d14eb0cfc4a2b4d2cf893ca6803a6ffbb76dabb82b3b7f646a88cc984393e560afbe6bd65f59bd25ca332268c0022428d3bdb7065c19c149c05294d2e239bd45bba1246e3aec4144d995896c74b43fd4eacec29007d5547427274ecb56b1f186cf9871c8ceac3023036d4d72842fbee243d382b40743ea2cff57832af7ee730b5f63e35cf3cf537e781366e11e0a3baacf3d8b06762b918d5cdb1a48efa5271ebbec8825a39afc7f4a034c1da2208f38fc62696599a9f7bdab06dc531182394e7051ea04d8f77b3c753e3a0fc6165ddc6be685b9264e8e02a1132ce3d976466ca6980074e8cc3c814b9cf1fac00c80e7f71abf06ea7d63dd318d28089112393dbd10a1d7ea64e40050782575fac85a7333549c43a2db9c29c71393ecd662eafa2e47f71c8d7ceb89e7c18714eb816be45935280ae301e543895e19e62fc03994647720e35fa9ecebaeece016642b72b43fbeb38f0a8b2628b71533f5dbc7c70bcb6d8a1c1d806f2dca42a85652c175ff568246fcfaf99dd04ced17ece2158094f055a773e1033a899069c8616fa72fbb8acc9214e97c0cde0b06467f693570c47f84274f0d0be92864ab5d30630be7c43987c62e1a17cbf0f6cec766c25d430e3e016f9c7ec492742c7be79a8ac99ac6eaf189e6e2f9a39b9f62984f5b0a67d104de2dadb643cf4942af37dd2e6f629a5ea09b47f02f8601db9c87b97ed9659bfafa3087f0725ca25decaa94640dbd4c5085741a74508f7f84c6d15e5845724c28541034a4c5f657be16ed87515b132ebe281d9225850e6af947e870dc5e8d4c5e567a4fc9f0d4c4a3ca6f2c96f94d88512bc1628e170df2d19a07c3e30445dc1d284e34e6b53ba5bcf38232d7210270cc8f21aed202ef5d68cfd41806ff1f9b41a131da4ca3570247ae073aef6836e47a00f7ef0c50389a9a9b7042d9397973a739db2fa5359128c7ad24eef1d3fd1fec3dbd9c34ccff287fcc5bae13142c3d0f58372a01a356611480381a11b2710075ad193d20c96edbb17041cb3c196b6efa10565a81bf64a120e3443071c6e6334ec6bc1c84ba6f5c6baa9fe9f4665aa6312d04c4aa6371a90cef87c3561383f87bf434505256ead36d6f972121d97b8c2b27b1d25e457a0bfed709b13d58123944b2db22636d6c8718d1a1bc24be3b5f2eae7797cb11cb1cf2ae591dac1f21f38c3ce0d418dafafb471bc71a15dac231fe782ebc1c09a7efb4032f87066a8810fe619d1756a8c1e97a319db1f6c83353930fcb018c70598d4848d3d427f23927395db000a7c1f3364bf29379939e261b2d414df4b060378bc4a4a34521352b6af6170dd1b2d6dec74015d8e58454ec35f8e915566ed256436fc6c7e9d90479b80498d21e8153b946da59f7bba6061bc9306206690c59ec39460645aa30964eb8aa951910b13d3d892de0063d5d6fb5ce09d9fe5c03df68937983fa79cc631441ffcfb62204c3282eaa1b034bdc56ec06352d073ef984bc2bb5d17e8cd4356a3271b637f3647e5bdcdde0a674e9d15f10c87282ae8148da6206f462a56f6ef27e6dd7051f6240f3c794f63b5408a63559e72f23a4594e917fff3280d419284002da9f9354be3f35dfb16e3b2f9afc5174e415b0ef479ee1dd264d394b88ed9f983c20e72ae94e3ac3bca7c2384e2eb503b66cb0710f58eca44d6d86c1d1505c9a4f3deb94cb1ade582f062ba79708fadef8911ccaabedb8738d51d6cc08f5aff227c58775b498f80f50dbc635e25800245b0c4ca0be47a31cd93e910595a6e5bf46222ad2aa41ff549b3c45b3d3ae9e713c68398505c331aa4ce6fda3f88be4151671b3ebd5937dcf2afa253a24e3840c65f89ab315b4ac66943ac17b86858f2501b649724afd902e68bc5f6395e6ca61d98b303f20bf61995ed61ae339eb3ad8a6dc9f8feddd10a04b7ded1306a7d4152addedf8c561be94203d1e985d908e4331eb6ce94754a25cdc3a9cd606fa82a44e38aa591e7303dbeea20560af051ae222d4318697e31697d56577a680849dcbc16d9948a85359b7c949bb9f6cd9f3045547f0b5651b9a2c79822d131bc99454058da8ed5d1d34c49a88159f273a28529ecc351ca19a82a8ffa3a023426f3b60520db70340191d4261c7af26bbbeea62c29443ac0784749a282eb776a349153c24079326d2dd3aafbeb46c21c35b39a6ae6abe6d7c029f75b4ba641634a02518433747c46a89db4abf46391481769118d9ef50e51a8602b7d17da2b2396cdd6ccd2ece4ebfbc7bd50cd20e6079b9b5749d91d25145aa7f7cf3a456785eecf608b09bf4990355115fe020cd5245934bf2dbeb90a0ff32154c9e0548a4d206097ddbb6137700c15496c23b896c6e0ecde9392e2f4d5bd10ab857a7b98fd15e17d6039c62a160691d3ceb84fdc71f88382502f025cd69dd5ee2b23676af6c86d3691f597bd40f56562502b2477c4d2e07ea332d1a23be58f543a0192f529fb8a1112efc20378023be904aea2447f2c351b14c334a2c8c5601ff392bb0fcfe220412793c3e9cd66a5f714ac32dac4f24a7edfb9100424eda96a9840959baeac8615fded3b92d66d75115b713df83847d4f3c1ec87cd400e3ec6800d62855a5c2e1e630e782ec825b8309168baeae3a6ff1c99e002414c49b2ebde44b0f3c08409636f620736586a59cce22406031f906599d83743a2ac6e16e59ea5ff69d666b93df4be8cc068b7d7456aa6d43c8723a0a6b2180035392cc9b54c3988f060ffd00c2239ee33e84f6d1eb416b14c71600432610928653db01477c04bdee575cc23296aa465ee34bf1d10a300a74b93dbdc18631161e0260746cde0c336a2be4d38426fb9b1e06ac2cb1053bb15f8f3e2b16bc16fc55de0d5afeb8d52bcfbdd96c4ca4908b4bb03054ea4cd032e56be865a07f3e9dbd5caffe44b9d05cebd354bfaa1b3c26d5e8506be10a703d733bc27e0e08f2c1dea97dfda0d52f116757ee255fb51f7dcd3b0e65cf034b809ab4b938716d4db7feba561852bd98504f64e3b4464605e8a2079052692b585db917fc89c16eedc418b5b7897c78aebd0c2b44aed9a339d77697bebae574fa1398a9b94315e844a1d2ea8dc8027a9bd5860386959bb29564472fffa9def661c190c128d08c6fe475d2f4b4eb8a8dfe0cb8146c727a186a21225069d972666363368c5efda675d780fee39dbaddc9a029b0581fd5e7623bef23b8a27a4870f8cc1836819c3091a8f4635bea9c1571bd58b2883a9014785bb24142459a6e1d5b55741d5b57e3d2f361a1ca4a91679081ef797bfe0be769bd07f887c23a42603fda01a3784676f89f31b425c9958203539ea07ffd161b71192bc2f09557ee10f2c2f722c8ba7577bf21998e91e3bd17a440805dfa0a8f2de2a16c2ad875af2953de1700ce4e16d58b0e5cf5553ed1011a6f578d9c6366d2c7805bb0d61d26ff196cf4a44e400439c21a8bf8adda4c7b3d18f6773baca23d1d97e04449f58daddbbc79b569383b4eec175fd87ad2b438ad4930f687c4394c218f92011aeb590e9c8912904b764674b75471bfcacb974672aa0ad11cd75860b8f6ed010b03f992d593a87100640a2c6b5fa172a033008e54c0a012d1b3aa0bf9c855e9134b74cc546f11de413c0cc8e6d8d9b13ece8a71acab9a11d8dbd100e01fdf2e39a7dd4048884f8921be88a92a314d8778289ce9b6c46ddc226138084f5c21a20c48ebad3c31e1379b3462d6c8230f03e9aee17029c63fea17e1e509a1c606ddb0b843b183d7e9604b796f748d0171071d616fb20badf059fb903f594fed7307778ec63a4b5c601d19b115c30252e16b1c08e3b8fa605a12f3108de9a26c85260e5b0144bef642a3431105ea5d18dd20d3a3be08b21e4bca5eeb4e2f908249e4d24e9309c220a9244da728ab081ff68e44d44863cce4226cadff159c9e54d6b338a4bd0d1831d66ec292314bb96e233d7834fa3efcf1f4cbd0c8a0a43405e9c0cae27f57f210692f0f3e87e0bb1bd3996808cc931031a5be6633b96ebb51b8102df0b2da28e1c0baa6828222f4c484c0219783f0c6922a126c5cc455321737b42b67eb5eaaa733036d31efb2d1931285bc0adafa7edd0e06052424f33052a1a812a4d5206c648b0e58fdfa6c31f9cdac4306d80bd9a7f4a9b8aa73f14850479d2c2e52c9f43840491956211a56866013c1961fa5898713ed76abc767a2715fc5ec5ba4f6a2f395afd0cf5f4b04ccc7eed332043210cf58e23eee73b981fe72b35d6f97f99e7c9001b59c28140164365d5718e685d23e83970d10741ad9d0e11e056545e896155f5c4bf7f7a7d0b68dea2bddc86afe3504f9965f816b0029806abb02070e69877b2b2cbf4353cf79a52e2f7c89af6861badb940aea8ac5f926ad8b574f84b6289cc353655b94f31970fd886ab333f7505053293f2dce8be25e86e31013bd8fca40486db42a2ea28d49a963f44faf7e3f5d042789709d00048dfb300a02bcd109d782df369e5c23def633c8f32af03d21e39dd40bfa0f8a7bec52d1ff7346f0c3f0a8ac60100e49978c362db52e2017a05feac0dcb169e1565582f106394e321effd87ad7956803b4dc3e09348b099dfa4b3e7f7ac0bc5a4650b764bcc5e9bb0cd57eaa1fa632846365d50e2bb6c7a7a4d95653b3d09549479c12ee3a75abd4bcabab74ee83b87641b747087c18d25276e08fc4deee9069d916431f39e661374e66064ee0beaafcefab35db6c38082421e3aa781f6639b6ee823e77a983006212b47f8dbfc66b8d83686a3372bb73dcc3fc1f34b66520a397ac97d5c13e0fb756b1fd8771484a62958e119a66fb471fd7dde2514353065a2826bcbf33a93193db1230cd860a1cb47f80d110c7438afe25c563865e9bfadb9f7f3d5ff2c1b128f4fe8e86056dba2302a4c7856f651a1cc79fc65f8604b69f4f12331c78f310748daa520804a711995d3faeb0606c6c1e12e3b9de424e095b18829359cc5215c57af356ffc888649926a6bb66d51c03a9c85a1883bb6a85e35338d4149476051d912309a4bb172fc06bc2e5d21e2113d7a65add1fa54e02d9a7186686ef956df917190585cdc81af11170558483c1a17c62f7995cc149e1a9abde09276119d2902fceff2ba9db11e024d3e04a306835fbcd9cdb35f40cbc79602668f1c51086bfd7f76cd3c0890a027081fc7f4f625d55abf8dd56652d628f15dbebb3c0637823117996896db7d51cdb252d547522f6d3dc9cfa2063a318593dcaacbee3a640487f2b5bd74d1cd7ea822b444f41357d997d6ea5c49a4433b4b6f5ba3e01fa4f79a94d5d5871dc922daf65003d3706b2ac2661d18058a2734029bf1db5d8384415be439f7d4f81089a36c65b2102893eeb3047aa5c4459b363a6720bbafc68204a0557c1928c67462ef5c093e32a8731bed3fa007b8f9a45608d2c01ccc383f229f49992c4121f8ac30a5816770636f3c9f31c4d882657cea21b35b749219635c9708a075cb4e42909116e1722c15dec7e83608e1437cc41928e98bc9ee8540a3f3a1e07e500ac680d38983a898ac0e86ecd109302a10d998707263252572f5ff2280be10604e6eccd363ab0d3e2b941a1d6fd0ee2c0c34cb272780308c9194d950502f6cb2aae7fe8f770f61413c20eadf86f11d22c7aa66d18d50b82810e33f050f41c1aecdf5e45d4d17f50628af36a687a21ea86fdd168089596d1f4d5ca0b3fee3117c120b231071317c32b49a0b287bc88f60607d28c8363255cef64feb1738ee7967296150d574009406f6ac79743bab4387dc3a8054bcc0635cfc30ed486282563ce68389b32ebc36940f4ee68bde3c77e1e4abc2e5b928aa0535d17a78c57483ed6d6d7a9887306074aceb0348ef8fda39b3500aa1726fd78462c7797aac8912ea14c017d68391b58f4491ca0c6a5c99d268f9ecf2960042af4a32ad76b3d372188fb57f9708744600f799efceab5cb0fd17ba93c20042489c33c8365f543789523baf1343e2c21ee870099da0701a221eb245a40f9020e4fcec5a89c1cb108a80f9a78b0d4964fd65c96bf1dbb10a92c70cf08b300df19ef969a9c74116bfb65218504d35c649daae28e3b4c1bbfda0b00202c87d789cabd9aa5493ce54a25e60e9c5443d09223a82e355f722137a84970efb6011b69b90d8cbc01c28ec42244bcf8d3d453b98ec102d82217faea1c86d32cfe72fdc5fc4c232445f443f3ba79b6dac3af8c304bab5d63de46c34c114cec0df7d77304a9aecf9637e0993448cf5efa93f6ff008304407b38498cf81cee6a5a37e061f7fc0b1a5fbc10a207aef8b98d60c757dd75e0b3b3f93f3786c2a4d6099459d096e7122d1c5da7f1875c455d54de103611c738d6c82cd572ae62d8aa673d32f017969ff401b91b00b6b9f1be25ed18a61cd00624a656096874328189ca3c62d9c5a8212032fd6898d1555a8e7c33b25acbad3e368c8cf674a25fe48b9a2b50a4d45488fb50a4bf3a76c02b94399fd043936aacef95573896d36cc8595d0a3fb8174de87a4b299014c683d20eeedf3991b6386ea15f27cead6d4453d801e8e1407cbafee0ca560303cc2fc0ef41e3a5c8cd585fb8b7b8487c3a89c476ffeff9b3f7dbd15d7990780b7d6867df9be91ac25149ebec9d911ef0a8086faabab8391705bf2953939e81a04e895d37d945721ecd29b7b11363f4ea7aac9f36ca8af2b5f1051739c35d5ccfa5efac6c615b54bbe937b53d879b4551ddaf312e40c909ac0aa9d9c968cf88f392be34cdefc320bab3afe060560d5cf3f3378171cd909bda105e52be127ecdcea5b5ae0230cbba97704467890503b703fffe3eff356456c2738aeac5b753d7e63a4e001ad9779b81c313fe234dba68f94134beccd0a9b78c8bd5600e0e7626dcf371e77c77c93a5514004bf4c08b1c762f5e5866705e18e9ce108558b84cd96e0da85f9481f0ccb279f13f8e0b02de4c600fb8f99b66892566f21c682905a42c9d130486362687a9179be05a8343c37eef84044db1107eeb5aaf62a3f2c58df1f0421b57af5426711169863ca77e05e9582fa7c202a5177c177d4f409fec316946b7ef106ca255ff3342dac740741383edb7eaf75a59b6f9318d4e22e020ad0f7f4b94161651e5d7102511412b4f647fd60c4c8e97958c55664b9df7cf86cfd59a3002a79ec9e6fa97e27f048714fdc3e658688201848b328b4ac2b10bde733b48b7f7a395259cf6aa1cfc408dad0268ca7fa0c5023443f83dcd305b50e551d6a737ae458821b53f173999b238eee54baa2cc6b9cb91f1a2c0a35ea2268870c8d62f721051cb2a837924b97a29e0ee6abcfd140ba0d95cf8f7deeb8085014234b75328763944343fe989b02405670d560846cc85fc852086df7106eb2f43f9cd279212a66fa6cc805890bc412c0e573445fd7c75034de9cae828b2e1a94682b7ae8022732bdf6f73a69366d3d007a41a0e2e60602fb4f649c1fcadf6f11a471daa1baf6799cf7fd78c5b51397d9d64bd74c904bbf47c1516c0fb3a3e05a9039aeea810da03156fcf32ecc43f572b5d9406458efae44501b64847e40a025b50dcc3314ba234d0564cab01cf30c751babaa3b438f33112f6be240ae631e231c4b5bc799ee8b09af91ae8e9b08d3b4d7f892bddc7f3ab4db10e9e963c97a18dcb9a6b18125123b48457a13b77d1e80e6f16c5f6d5e32a7c78e4b69c75926eed7ad314d5643ef7ec385d0604adeaab007c438d7dff1d0e1bd660c6debad995e6d630c0c890055cdd5c0d847fe33cecbb0dae3b14838bcf20fccfd431db33929e80fe0217ed066780dc9d2e48974c1fc0dfbd0f74a4a52346e38a3ecfc6c9113e2800bc03c310cdba61c908690a22fd0b251933e413f8e1982dd014312780f62b37859e440eff65924f0eb5e3c1db078a96f9ebf51937cd29c34621957f165f17bfb1f3e59fadeffc1af58e2187d1fb3af04d99c74a9533ad7e8df67a5b06622667cc264b547efca84e1b9b1be2c9a3a76ca6acd0e93818155c5f167e2b6dfd06afc8692897f7609002c32ed26b58f30f6b418312c07f65a82f5cbcaa9fae8583182d16b9aabd61ee326de906abdd132c270fea86349eba9375335d3238031fc4605bd53fd7df2a14b4e84e34c6e3bd56aa7c18041dcb8f712abab866cd5727b9021f88d6a7aabcaa0ca8f4c16ddeb2a9f37567fb12871ba245aa04548a3831623d01b2d3b0ce68fba97b49e7233556b1a5a18d1de85cd96fe7a2a8db6072e101be11fc7b8cac8320b77503edd987e2c8365c64526dc0c7ea3263e3acb94228341a7fa85b38df7fb500a6b5644c64ecdad3e9685b2aa399b82f9604073d8b5d000b2d3b055759ed658beb4d9b6fb0d31d83b875d091b609f21067be7a869e103ec37c460af1c352d6c80fd861aef9d83ae8506a0cf50a3bd73d8794be50808416dde07caa1ef95eaab030fc2fbf31bd5ce67f68760214919122c35f9e63d9bf107b2d427557e5d60a451e63d6ced70329a5e6e3404179c7799b7e8a0df952170bcdc45c85ac037e587e64bcca2b6546957cc63fb308dd26b81dae1d2fc72b1fd955828adc059b927b35e9da67b423f065f55b3e477f70430372189752cb28759814ce71588d397202d4e8e9b13c18ea5e5988cf36099e62ca60f3d78ddc44a0c8919931eaebd66bc616fae4f032a882319be496a7481c869b3e13bac5a63c390da373154a5d758734d88a41604d0d9f6dfb01b77649eeae55971a4a84368bf3e1ee393e4c25d44df71fbbc296320f42668326cd385d683510df477eaee9ae82ebe0f909cdcd660188ce0aa9f2c12c8138e3f0132f0bc8b4b38b02354aa48138cc1e380c49ec8ed834394d239d506a748821f36df6fbfbc312e9df25ce45c99bd02a9e358d8a306c2150cd11bb53136f24f92ce07a4694816c812c0dd09d41b3af2c226f0f129ed9646cc8f080b45a322d824a3892e9498d19049b80910ce96780fd4b068d15c08aa14aa9cfd9c117599836adf6c61fb4fcec0e6df3a912d513547941e2dd85508c21b2d250e0f922586f78649804fd84c2ebbb96d217378d751aee156132be01616341828e4389998261050fb83dc6ce5690acafd72922ead594d87001828694da02c8ce0f96c6a048197896e5bcdcbca536324a412c3ed609d46d5e60d1825d123b7bd3487a95f33e50b8592d42bf8f2c77a9896486e445c9a842ac5f708204a88e485526fac1c1552ccc96c397170fb3afb2f2546fd5d380d6fbc6f6483c92ac7014c02c5eae4aedfe71cc76bc8f93da25bcfe7c6e6f3209873bd8a008021908d2d7b58e143907164c85d89eadc80db7eb68778c32ef195ead796575976aa829ff818f3c581d1bbeca8a5c8df41a46ccbf09480bdf3cbea84e0bdfdf71e81b16072a38fb09ecaa8902937d65cda81bfdee8846e587cff9d8d35359ad3401852c105b18344b9936e0c870517fec632a03ebe28c39c7d9f36349b0ac21d9ec48f7bc8a593f86a08abf30f94a486fe54dfffafc25415233d5a23acc7b5568a3a6614d8bf3e002072c9120acb7707203aff80b6252872daaa2f828b327c81149d494c4a03fe8e6e26364c81e47fe766a654088353fa5d45bef63cb45b5d4fa487fcd9f4021fc5a35faff92254ceed26f546da1f1928548110c8185d78f4519e07506fd48c3277111e7809c50de28ba0740ac6078919fd17e8353dd2ebd55f5d1276466df0a9eed075c0ddbb2a4522975a3e095468c0aa4fa12a5f2dbeade073595a957137cb7dc1df68562aba2fbdbbc7a6909184f2772e646e36e41cc2fe2393c08d1d7b175ddf487502f127dda3306725df791ed36f30a3fcfcee4a39f7280e38c666864766be8349d65e3cec1041655987df1fd105873664621e62619c95a14454f52af04bd0c67b1ec475cebbaf748aa0df8e32ddea0af7ca42c6664861276b5248a1604fa7ea8004f75b2729ba63c8b285ec9f9b6fcb91a34b115297e9dc48e7305b010cc4e9497f8d7ae8900da6a65e963876eb25dde6c139077e6a5670819bab61e21b20af02b83c19863349c1579c7a7ea7e296904afff57b04dd6e0209c5b7cc478bece93372df1920093d107b463fd9050deb6fd426f977ad3bb05e4afedf9a4f64f0f48e98abdc84e27bb4026dea06bd4b0792147dfd25013f6f7412ad26d94f48d9be966dd6a0c6f3a7eaec751296b509f48aefbea1f6c14e8e609948f58972a7a12835eb40288047a486e1889350273ecb3c49598ff4b79707b3cda42404ef5d93ad5e16974684dadae5bcd62227162e2503a55b5cc90c8b105c18ce92698dd0118a4da3dfb456e484a8af376b91fbfe2ed49c4befbdc75d8c8412780b48f01f9d04bd18092ad81ec1f696172e70d5cf6a4e8c1f2bd0c23905d3a9248e2b98c27f71f1150242f9e5420fac2cacf627b424f47435faf4008843731446fede05af5f802f9e0b6413c2670cfe74e122cdc62211f89cdfb65473b36546ac07405aac3a2b4e0519ead5676decc2be7a773397af192e9ecc79daa8c41b9079db9bee25d35ae40f62837298bcab4ab3f59378535c01c66828a732dc27c10c7a0416a1b54d1989e006ca14135e90238823100fdf7ca3867356af0ce0e9331e2b4ccc546bfbb26797f4ba47962af288747ecb61c30ade3b19aef3c6aadf0f846fb41619f7279e15c5e2acdf953d3e789f455b3e553c2b80d48b35cbce6295e2a8b76f97f8c1254b70600c323a5ab58b65e1d526470034f58b3dde198cfe02e38e37f9e60b1499bd516af857492a6eada92062aee55fd506f4dde4c660b9ccef4571cd03432e0151abf115bf3123c8ea4e5ed53ffb1ec9696fa309d08bff2ca1423334af5ed1f76a2442cddb2b4af277a1776569ef45f020c1bdf46ffca6882b07176d2a5d2b15d1157e4383279f8372ef3da4a4bfc3127cd176a0d54149e17672789fe5153a0530ce837b642dea1b6ea49a54794c630ecc6c4395e68b2386924907ac5bc2cc3c9db16bead0e931be03d7bd610e4c0e78c250f4e82a7aa8c1a695d3a3a6cd03261342ad9d69d74f1b060590f22b9cae6dec4a4e6c5496017475039a9716895d308e3a5207a5f1ab841e416006137f03789f611899ab8804af4174e7e6bdc0ab755f6ac44f83bc747138744065ff1f94735707875005d077f4b59175a70093ba25ac848651b12b5c47716ff8023f5c04199b2c217b37917b4b29a54c0132083308ac08269c08de74c925fe0007e8fb656ffc86de3bef45b542f84a960ba8ae7313e4a686e949abbd9d874da6707e7355ab288aa2484f4128cd375d72937386cd9a1fee8a496993375d322567b0d7dd195be9ecef54eba4b3adf5ca635fd5f2f4a03af356144a14734a4646a55aad6666683a1a1a9a4b43935332a511ea55e6ba924c53006542d56986cecc9c54a8959869d0a83468501a2b1aac99191a1a1a3458ac9a1a1b9b1a35686868d060b16a6a6c6c5aad9b5651a66439c77c9338382e5775b95caeae238b688046a3a635e79dac1a9b56cb63b069b55a3735705cd4355d35705cd3e5c2c171b97c247374aa8e8e0ed5d1993ae348e6e4e8e8d8b071e3060e1caf170c16926498a3337572723ea8afa3a3c33d06005461dd841eeb83d7db30af623b1e04efeca9e4dda1a2e3db412f0ca92cc7b7b7aae432c43406b395ded9dd9018bfe257fc8a5ff12b425d67517b0cde74c9e515fad7cd976c72b06b4f6a4130c463d357ad18f40524d1182ac7915d25d118cab1db718e393c007e600784123df68fcb63ffb6d01776cf9b9e75fa962c17f01f8ebeb0dbb169746c1a1dc285cea437bc306b31b6a6a79c7a1e5b93ac723d7be7d3e428a29e7da3cecfa80354613d6e39dfe3d62308348514bcf192addfe4721ea25465d7b10f59aab2ceb12300010838d23393938f757ee806366ae377408f9dead81ebb8d9a8d1f7a1a426997d4b163e45592985cae92562b2505740ab89ec92140686a1c925461583cd2b145a66e4374c1db10d117be8124973ab95c9519f50c74ec3a53f4cce4d8758e7472594967f68c3ef621484392aa6cd2f0506695fe4ffc3c763bda7821672d1a09dc909f9ef9631fc08d13b9fc8690c03deeb9692297384b9f4346ce18d1c5b18a53f44bac423c729a4234ea1decd52929fef40ef69351efdc74c9a05d2a6d886ea8e30af52147f30755012fc0d6b31a577cd952d4305384dda95e4b1b22f1a6897c6b2b2c3aa693a32fecd857473deb98ce13fdc25eae90be5c2d79ec2ba09e59b72192c1e2b1cb1cf54cf411e5e2f8b94e8eeef0d8b1eb3cd1b16e3c05ced292da6328b078eca50dd16317c7041cf50e15c5ef86e46337e53cc65e7e468ffd1bc238677c128f401b5228dd349167ad77a6e72c81533cf6326787c79e13d433d1b1fbf065dde1b19723198f7d0ccac125ced263af0b00ea1decb35b2e57495fe3451263478d41d8c12fcc5ae40804f5ccf5b8bf243df6214755461d93270499ff91de3bd883e87890ac925d862d1e82344ec94392c821471768c9d4cbfe72f4d85b0793247416b51618d4e7e3cc39292533a082fbadf8049396beaed31a463d7ba1881e48485fd7c8de246b93501793d88e0401885fb0429539c6999c749222908b4d9838c13eca865c8a40a20d9ca368138172982fda2819d217c674d2f27e594267a1bf42b132f480c64420d1f6d629c94112f9c716b91481deba084463b489edc98dc62819a49f8a184463d8ad8b5668cc73fb038d753ea405f736483ff552dcf22291c845ccbda564d8792982c95ecf091d9e3842a625c0cfc190be3c0f9cb576ad90540026bbb1049e49bda6708d98a8dd026c50a46397458a3c0928500bd4b64ff137187a94cb2602e1a6eacc1ed5074c4bce4095f9b0326c6bd9470f1ae3b95122fbf0014295d9ca833ba02dc0d6c198867e2df53bc286c89d972ec42f760c7499ba754b5f578ad9b7bba6dd42eaad8d5fd9f3d2877b40ebc9764e080e43074f24261bf6d58e5e92ac1eb56480fdfd8d9e7f26b22502759307a1b5f6097ba391c72199c1e4f179fd2c58670826c71ec444620c5ec8932c5f3fbb8ec4e0857cc912ffa4a74a62f042a66469c159659894360a17a4a338558ef2d5d8afd498e9493c799f4e33f47a0abd86b5d2ae14e8dc93d03ebdebf144ed0aed4d5f18bcf0222d7dd9fe1bb9ac4307cf0a7abc7d7ae327a44cf32a83f9a3ffdedbc3c7932ae4d2a914f07390f423256cc2f809b0a58022b30b21dae72647617e035ad3690cd18ad018a2816ef2f94d5fb7c847e85fdf453e2e7c914feebbc807f73bdf452c4cf13c3d9b3c43b60f28e144045fc488e03deec6452c78e95cae4a913379b34a501e40ea5eef4caf5d2b256f13fb705281890e7e56da797a3891400bc403132699b1b575ac514a635db44253da5c72755a21e69c135799a579f644e2d2436d42994f3a05b68e4d01d58576dd08ad56af649116126d898c9427d922626d0aa1129a7523b43ab688d4225d8bd4d88166494b37986e04052248628906563abbda2fbc286144ab630b89e6935216da34621313b576bf3ccfb2fec3a09739042d8db540bdc3dffc51c17c2306f29f801d85fc13dfd057e7df67e70f0ce43f613a0af94dd9278f4a7f923c2f7a1a93abdb49e226b3a01ec69e908ee7bb5cc56422ade5b17905438206251a424dab0042714989b81835f98401682e97a3399acb5122ca85e6a80c749353231aebdc64327d4a97cbb34007eb29841a445f56b4f2d6dfc68a9c7beb1e98b7eedd68cbcbe583b4134f63d43b54bc7d89819e3a14b9923456c0739c596cb5a79ece04cfbb77b0d7530f366271d3ae5be56cbb77f0cf39bdf6a9da1c94a8a2a767ab98a71084f800a9304b3616609360fd9abcb49eed48ebe0bd9dc98ea5053cefef96b40cadc5268ca5058cfcf4ef739c41d03b9084b02582e7a083240926f7becf1d1cc3d1e4e0e87d267c2ff6ae081dd912e15eb203efeaa705af870513b97cbd3068b29febe49e5f999f862c1c66ec5def5e5180e749a8ab1554993ce8e4411d8327b4886ff2983c9ec84c1eb356a193c7931a758b7986f29c643e7d40fdce39e79c737618daebb66bea7625742d100282045f9d005e7d84071c7df56f27b06cf9fad52b50659556bdb33d7a74c9c2c8092192582c5fa1f8eaa710ea1963defa88b7a12b3fa37443394b72290e894355d6d4de59444d45a402a33a5285417a9bb2bdf55225c667de908ce0cb10546a81e84b95110c62be54215995d25bd5d2db6ba3b132e76e34568a43e250ced158797f524f68ec12f1b633b28135bc753abdfc92de3abe1cb0de2574c9f3af4a899b26721721c97d89bfc6ffd4728052465e92af8acfbbfc8ca05f8e7e60fef3bef366ccbdf7563b1d08bd1789bee84dba475a72e7e53d3a9a493456fd7a4e7d15d4330c9e7a37de2d370c955d2bb77b74bbb87bbb546e50eee62e98d64f20348713b96461d64ccdba80703e7cb086753c3d68a99d6dabf550d68150f1c11aeab892cbebd7281b3d75fb894ae292a8446362686c89c66c334fa80c74ca0374b58ca1b24b65555e5b292adda127f4122911d198b56fad678b45a52a6b09011d248b4c9ae7b56d55262a358c92a2d21156de924a6280bc2a5ab19262502eb3a8f434bb803c2b60eb983aec1d59e4851b60728f2c6262d548ca657e3c36ad8e257c385a6ea4a7b7008df475529352bf0f0f9e3cad3e7ca99385892f79b250f9b203a23aa5082bad6000f97c17011d7dd177115093a74133d3b3ce59ab9e794e8308f3d57b48cb571205fa17607d99815aadf628b0c4ef6f133c78c209d6bf9dea78acf631599216a83fbf45a0762c3188e97c4ac27cfb24ccb7f4656fe7f5ce5720d4be6013fb62f0e9d80774c59c5ba1efc8896f0fd2b2ef480399005a966b6c40b5e687d532b156539e7fd828993c66dece69e79c330cad652db54ae969f74ec9527a1a7a9f867495ec55500ece3b7ea00ae3a9d77a0aa29aa409f38ce95532a94aaa3014e452758f74ecda809c864e9ddc246f0dd03253ee83b44cc6a9c3dac4c19bc294d31f9f0fe8d431693a5aa2b19a1aca8c8ed5b0d09127b2e6a7a64aaa31c255594d925195d9d42a0c69feb041327950ff54388c036b6a34f6fdd4b0d0b1af03353fdf518d5195a170a8dc67f4262f7347f31f108d5ddb1743c7ae932b45cc5da5a7b99a9f2a331d5518ad32d39292f9e3f3993ca89b7e6a7ec4dab5d1186a89beae938ea1cca849793167ca55994ae8a8ca54b80a5baa32955185f9cc1f2aa41ce8655619999654489fcf53f7bace73154e25549354652269a3a46159257455b8a76da3e486b689f294d26c6a3648e60f9498c9833acaa6f636485a442a4df4b1ca8a642d8dacaf03cd12f3df0f8db1c26049b9d5a32f57b8a75e7e473fa557551a465d665cf9d030ea285c95ad826818f52250b8554db5a41293abb2d5924eaab2d58f98f9431546f35849795ac353c7b915904a8ca7dea1c43c755095a452ea99854195847b6762da54bd4aa977a88b639114682814a94a4a419e5eaa924c9867d86bed9c21ad0180d27b3e718fbff6f873d46ea6c85e77a32f1715faf2dc757305e1d025b4b3b4a3889c72194fb98c9736401ad47a86ddcbb82a73dd6efce4a6af797bc9a45c37cf5d543c1bdb7f364ffefb5c41bce7b3652ea18679fef9879b3fa807fe4bfd37e3f39f77cffcfcf7fdb842d032972bcbe7c289a2288a28d4e9f42df1d3ef5095a97055f67d443fbd447d8d7b26cef2a967ced47e66c415d6f96acc3874e1538fabbb6fa6c856354e1af835b9f7b3cd8c3b8c563b8ede03426f4890cb0fe803fa9a785b34970f4967899f60bed421fa99434699b3f4b32497f84912fd0cbb58e2a7bb703d3b952e22229b211bdc4f9fa9bde7e58cd0cf992097d0ccede7e471bd9c51f2d3bb9c41f2d33f5ccfbe9f3e53eb99eae7cccf2c7b3973e4e70e29def396edc835ccf32933a7ef78a2f10e2ade7301d953cfcc51730955d98e5c85dd68cc0688bebcd2757bcfc11db92a5b2955984763363ff4e5b987ed97363fa28aec0aeb384802e86328d03d17910b04bde3b9cce8faa131d795d9f3870b88c9c3f3d226c907de8ba44ba8773ce7e0886c03d4b3d57b6e63eb197dcf71703da379cf772455990d508581e9d817437be9d807445f5ecf7295c9d8d8def37287d27ba2973b96f017830dd075cf3f20afdad852e367b27be7fa25cb12ae832e72f19e11ee4f30619ef155528e02fe2a327e3345c605a00f923f9896ea92a983f5a64bb6a720351e7bf7f77ddf77dbdaef76b03b4d07bd3035ba7ea0300fa42e21fa72fd405f9ed22c49de7144639eefa882c6ca12aad04f303400a9dc0e231a9beef94ac99b1f12dde2030a0366f92ede73301cc15a2b10e8a1354d13597be7a767433d03ddf3ef48c75cb7efa767d33def8eb9a6f4cbf3f2fadc9fb2aa807f7a66726fa5f4defd99eebab9a650f754000a3550e13d2f3fa0f730f0f29e973640efb969c420d73b27cc95d28e5cef784ec1d92d9bfc84f9a54be83d770981a415ea5a9bef95ad41ad773ca33cbddc917bcf01a05403864fc4094fa86f1a036db574dab61eea5f37389031cf57ef3aefbebb555019e89da3967ac6e3bbce793af7d101958514d6f9aaf3f2c6e9cb3a985cf274ced383ca72f79ee7b9b2aaccbae736d7b31aeff90da231211ad3f19ef779df75622e06f0f53d718e1df2f47a001007f7d627ad2bfab23776fe2da9671ab40bb94955c68395accff9e4ad3fa1b1d2081d858c405fb352d4245bd4fb2f1d41087d619cb39539e7bdf3ce2b0a79a318445f748e2084be7004e5b27b8e23081941088d5d9f75ce52147aebed54fa4e18aad04fd96c49beeea4102b6456cb58146f199f82e4efde116f62d0db55ab4ac69dca00adb74ee9b8af5e6bf5aadf076fbae45e32e3b3a582288d9037764e47cfc5a57bc40c2c47ba3bbbe5ea25f65beb1d4a236473d43bf5472dcf18f5acbd7e365a83f850cf3cafee583a268a535acbbb927a661d67a867264f0581a611bb78a34bc4294dbe7a35198197a4825c5cecf01507cb572fc5259ca1dea9ada1a0afaea4dea93ebb65939734425fb1f7cda6a8fbb6a585be6a0bd71aba404b9e5eb6867001b6967df8a604fe087d8338fac251a5e60035427099e5b6823c78c2c6c183c18d9a1a0c3230e13e201b46884aaca49a0c34d0792063082068d02307031e1e0e7cf8e8b141d8ed2f58cf2af9eda911241a1b069008cc3d759b538f101007c358e30e63bd5e34368885263c7efb1c539d9b8563b2d0abece418e7cc62f1789088be4cc0c2a0cc0e616f5726c282415c85e15b31cb47e8e3d463c2d8e320b4b95bcf8c7ad6f9f42fa86717e767cebff6ef876fc834c5375173e1c56f120dc9128320ed23c12d234874eaf148106794cbd5982f3e4f566368ac5c996146cffa53ca444f9d46673a99304f509a8b39f481120d073530ce19c481381c88fb320b51443debc1a2810271b89ef5065b6a596b7d7db73a15c2aa305b2b586b7737699b9c41c946fe84fbd34b68d94c5fed9472a084cbf9ada6af26b1d07fb89fe4246d0ad348a5a16dd21b5f5e5f2ecb5e215ca446a918637babbda545d540bf7cc16e4ff0e1a667adb5160a6b1dec3c6b2d1e3b8c6782b8429316a9512acde1c460d8a76e9da40f0736596a20f7ed752c3ba1af9ef3443ef5b47bdb91ac3a545265c5374db5569d31300e1ebc5a3b9a2c79961dca866c496c6f78c9934e1c546aad1447509e9e438c634aee5a41d003d65a6bedbdf7de6bedbdf7deeb996468dc8460cf6a9d06f9d1ccd8d09033ad1c7bc5e8543238aa1c991c6cc38ededfa85e6dd4da75b3e70d1c7676213d05a9f1dd0cb5aa4e3dd65a5babadd65afbb25cb87c51e9c06aea860c2c85e3de7befcd616fcf256fe0f284d9538fcd21e6783991adbde00545bde0468c4b6d48edc97452d9b06c705c39372e0ccc31eaf8dc857353a36523dc099374e45654268412a132ec94f4463291ee1526264f2fbdeb5a3635a7572edf7b7ae576f08e9907f238f5541e9d47fb7543e8be5e1e0ceebd56e87aad648ec6ad9d7aacd39c7abab1765720d4c64bad198455663215d19c5e4fcc9c7aea0e0deacc4a4695c2c0b698c83d424b633775eab1e48d23f2f47c7ae550a79e2b9e7a841b84f5f40293af9f7aeee995cbd5e7fdd9e03381a86b732c1c40940d7336834c4adb31fe39e71873d2a1fb33439e3532acc001e84b1c030e3f5d2eb3944c2d641a127852a42461b29251df455286c09b94295d185290605b5ea9a043c1155be499efa21b139e086e436cb909815e6e35dc6e47700d5966052092ac42e2210a982f4ac6df4551b878914fdf45518ae85ac832df45517a288ab283b744367d1745b98910c506c4031008a0843d3070c6f9a3e1f1415fb3077d21b5a0aae136639444c686551724a9186caa2c31e418946498f89044b421857998e4800485330ef312a1cca4862427274022939f25a1932354194ac0a413e65902c687330eb30c47322485590b24a62638d008cf40f4f1e0a2173c1e1f3c3d7cd02064c2986708251dce38cc361ce6e1c1432d28dd6cb41a94d066b8152d71e187d644c98c0e30d1c2640b85f5973817012911d19ee4448092c4d3c3470745508a1883f0c8c8f9bb088a0fa61f28b620b28b0cd309b27f17dd308549868cf35d7443960f885cf35d74030e265b4e7d17ddb0c4cb0d4890641adf454fc4f81277423c410aca47223c317a0264bf8b9e2451015881f7ba2c3e095d51b0df9fd71b0e1ef2acb53604c2e021092753c45062861058e0a28915a32c80183a62643a82458f8e0432744548587c590188142c818493fb7385a61281e4d61d814407dbe1174f6a366072260ec6e4123ffdee10daa74fb235bf82752ce9cfa064ca8152a60eceda14554a60fe38839227074a799a70df7552234010d4d1a2e648419bd6ea224b8c8c83871c0407070f793a9db55ea07a04b64eb9ba3d757b7add27dd8dca538c276e7add5fd7de9f53082c1effddb0c8d3cbcff6a4c5442e6f909b9fa405aab2cf566134e8e9edc7f5df4d4b9e5e0a3f7be73aed92957c797f5ac9d3ebf78a36d4124f1d4544c3a8e1a98761a82c753bd942a226b82443271fa1314f834231a91c28de9ed63a52a2304341d79256e8a66e5546c1d02a52142945c3a4a898141d93ea2a49739d37bd9b2ef9fabd15fff07c7627a0809ff68e75af5a4bbb77aced3edb07ae370df5ce7596d1f55b13c3f87b3bbfa9dcdfeb140c9589b75aeb59f5eb3427128dc189190a73af37bd97fc6c2096309e1ec9e11f223143411d691aa2c1abf1e2adcabc2a288c3a525018213143d44724faf92ce919d51e5b2d9dd557b7151691c5059d4d5f744957e6d890ef779113277062b2f85de4040743f6be8b9c602972f20199efa2168078f0bbc8c9942e30c662804cb8edf431828719d4dcb141054ffdb4836403113ede7a78c3c7861540115c9041280a1866280289991b922f5f449005122aa03908d3bb2958f0d969a8a00207b65146152b1b0b6fa7786babe2ade71a86debae9d463593ba0cc2f431a56785bc55b1aac78eb38d360c45b23deba87775099a185b7d8fe588204a8eea9d3892fc32219806862450fcd574a7013c3edad756c3d0bf1b6090d6f7d0445317420364363d6bb8d8eb012d230c9848ba754ac02b6f7833a94c8d48dd03eaf2d4a0af181c17a049d3cf41e88803db40ef0f9c98bf440bd0825897c1e92453e906cdae71f394de043173ad8c042056a351a4f85d50ea260e07beb258f08d731305f47973c21e03ab8e430102881c684e0a38740bd22787e29d03dc203664d84268b08592980fedb8b143fdf5ea258faf2d6beddfbf61245187ffaf61205d24f9d3207e8ad9d21534f684826cd538fccca7b093063bec4375f862f9a5e0c126d1566ebe9e6289739b5b79e3da796e393f31346f4923c7a7108e8adaf7e56495236158ec66a8a0a95e55c85d9aa2cf55361d65335304b5c94b62469797bf43665cb292b9fb5c84674ec63d234947a46dd3a0b47fef42c83b79f0c5bde9634c2789bc422aab2942d65cbbdc5a9b6b4aae432654bd9682c25a3725be3a7cac8a355ea473534aa7e185542b62a53015598751bbebd75d1a60ab2a96ae2a8f251fd8caa24f4656d5fa692de7a290ea58e5248b92a4b05559875967fb9dcdb9451eead934755967a4261d66d8aca5b9fe4d1db948dbeec9016fab2738a38fab262fd94ec00877c6b4f2fe933b78abbe0e711c8e5e882b7731e5df0d6875c5621a1fa03a5175cf0d48523b6704008283c8a05f6ba6b75e6775190cfb7be8b82a4fce9bb28a8c9982a4693a667d7a7cf641aa33e2bc6f38ccf542589cedea15f6def4cefc6db4d6bc9b292735a3a59d001e9769ec55882b00c59dae1eb9e7dafbb39e43a450bb404d1eaa801141a38835ac0828596023e1a1a75a0a520520ec8f0820a2d85e9c5184b569cb2a0a5b00a2ab2c110421cd152a04534a1020da8326829e0b0c20439a4538b20c638a2d511070c1ad885769c2750d2248956c71a2da0811e46d11e9c2831a2d511e787464311194865d41e14bb9690941ba16523b4f69bcf2c6a1d954aa1c8222debaf0aeb8aa05ca6088b742334f75161afefdc9227109171d187342d9345647c942c1a2f7d5c5ad364bcc4f6fa0815d65df284214d13d2798a3442eb200aad755b44509e1d45b684acbc45e4e4aa49cb640f14a8127a95ec4d6b00ca534e44f419bf3e83a2a15c7423347a1212de749d4f2bddfce1bb69fb6e52f92ee83b3aaf108d9595a806755ead74f7debb227d569e7220b468e9cb775eb9d098ab75c1d3584b9d4623299f9e7dc641d5585b79e82d22a1af3c24ab2df4490b89642e58801e85fad667c81eaea7c6169193a7fc44d69602e6c9ad1319f4d9659f7105ac3c456e20e52b9f2137d0039194af667c032bcf64cb9b366b13a0afce67c60a5458e728c83e6b0debbcfbc8129b3a96d7cd54376bf4d5898e92711a17e2e393f295fb649f711a19199f2e4343169921e7d08aecaa1a8dd0c03cb6a6cbb8115a6acc329da77c7ace2e4316e99ac92229b26baab32e925d9bbc5ece5a474f4480ed28e01771d4e78801fc267867b4a0f61495d4330efe02a9b2cfaf0ba93293df19a51cfac93dc76e225b950441b2480ff5f3ea28b2c8083ed207c1b1d58e6ab5a3dc082d47c441d051e4abc2ae1701bdc2ee748addab9fc2b1c14904371e3d52c4517a71005b87a7867177639f4ef35052727f2bf5f35b37f4cf2f89c922a09bdc08ad9e7a84f8f87c1efa981c24817c234febfa685d92c8e726bf6e32394816f15c619e17f9c8ae4d6b1a8d2172ee9deba9deb94e61d73b1d6fd2da05ee80034fc8f8b204e50a122338fc5083210b28c014919145c997c586a239645921cb4f4775a838201e80400013c5c1880d2bc082852bbe50523b2387173a1d7288c1468cbf5f784bc0d6e9bada8cce1fa8da47add0da5fe1982bacdeebcdead183be74d0570f19b2ed415fb5e4e0abef00230301bd7a75c7d5787c700902e82badd2ca4cc79bbf71c7ace32eb4fdd9eb706825bb0c6f19e26fed4f4140b2d5972c33ce0cd9b6d294bcf257efd49092db7996ba6ba90720104087109fada022ac1ec05e77edd210952472560c201ea5d4abd75d4b737c17fd003464848123b61811c18128c210238127bcfc90822286c2004323eaf25860afbb16f65d14822d1ef55d448587dc176037a57dad1540adb5d68aaf002e14b07b760abbc1afdb8429a59452ea75b73ba42fcf4f299ddddd6dc2a1c3ef8aa2382b89423de8f5a0501e36fd244fa7a7deb3bedd330b8661c7c23ea11a25a250e0ac2d0c60f70eae75fcea4f42b5d55a6b67dbb646605d75b7b5b3d25abb6d77405d3743c2cca5d5f3e6ccb244db2bab1256f3d25aafa5b5c785dbfeb76671b794f125ab289bc2309520d72fbe2c53b818b305cd447342888cf0001fe58b67ca172a58df455384f8f0bb68ca079e0a53ed8829054686e032c6a7043f4861748445e2e5882f48ce606294041066d2d16052610a064994b044f101244661a838a304616411650c2f68e824980c2561250b11415b6a1960e440c60d3a7c41228a151c79c13b325df8e988b8558440e6048a2a2a58a2036e694c4d0a2f4da8d8e06488d5025c98239ac00186256a07188003135466b84911ea00077246b430c101a9800c9c9511f048eaa288304d7c60071c8e18eae10a31c669ba204492498830acad52b2a8e8f094ca93aff92ea2c2c28421d7efa2294ac01b32fd2e9a92e401b85947f0a58f05ec1085043804b14208b868339ef82071023184983c112204478c9c0823258856f25499d281a7464522f08012188eb8a2684a0b5ff21c1de98ebe6cdc29070e30c9c2248b920cb4d2079525619c584116221052d2a488a771ee638616fca90f2ac315344ea680d1c3952c5e4431c2c2d26d05267ebe3431c309b0e8a863ea2c2daed4a248dae213a50820a48a048985188e6081982b4030c50652e4aee8a1488a1649f3d559610514368ca0084b142f901cf03386172fece0840b2d75052b740c8e0a6f8b96202fcc0871f8258bc67751182eff7d178101c5871ac697afef4249174face0831030e8b0c109943892a28c123061c509a2781394c0c80519bae0828b5a15508ce0c41061ae9871e40a92d3a0a7de18a9ab620563b878822b020a1217a81d9164c88bed0920704705c61507c403100820c4027b0658ef16423818e1705423c372b91542fdebf56fa95359ac9b8d202a1347ae83c6fa7e750f6c15c9addaa8f5dedba542639788af2b1a9b5e6fba9bc303f946a9d66fe7cd710682dc3e495338c7ef0afd24c1708e393ca1c239ce276750727981c4708ea5756b29bd264b5533041fcce11c4d3ffbf5f6c13a0341fee055c215fa3b49930cf993ec991077b5b2abef5bc01104569a1a02d17e8166eacaaaee4dc63b7bbb45db7fb66fb7b5a6bc8c6badd536c6debdd9507d7fdad64e201056ea0da2af767b4393edae77f1bdb5d6ea16fcfe50a690f505ceb8bd140b870fbb2fbf2a1f4dbd83ad8e16b5d65abdc2aaecc3d8b3f767f6f49ac3c4daa1ce7e417f6f3f9d46e84fe4e9dd3442a0513df5952c25f03bbe98b4dadb79d8f481e10925e6948c6a354343835563d3baa981e3f291ccd1608cc0088123cfd1b1918110d1fda13d208ebe6a5818471b366e6030badfb88183c75d2e1cf7c52367bbcfe47d26ef762e1c9c174cb4f5ccb6db7c4a389701382318d580e5d8b9c911de74c917e8b8c1b1c30596c8d6337cabb2d305444bbc9cb8c13a422b54fbc9f1930471f465415cabb513ee9c7a3adf51b14fd8b5426356e8071af3c0cf047e26700b48445f9604712c80384b63bd23b4b89e01f50cbf7073c81a91039e59a814aed455410a95b21901000000c314002028100e09c5429138301e69a2d67d14800b73983c744e409787a320876114034188186388210411408c21c614d1504500c9816fe54a02a389bac8457452bc6ee419a7d1204e2e2766a9221c9314b6e8d1208020ed8c088dd4e22536ddb352d0ba562d2ee5fc6c3f4ce9889c1d9f6891f9c18c7570640548eb320182007393e344ae008dc6b8a7156ec5a248485b752d54198c1eed500f03f9e6b94f9db47417fb340d3e155a0cdf212314f2160ea848572c28374963698101009297e5a51177cf6888efcb9ded4262374bef59e2f000e8e5687932774d5a3a4bc10e2923ecb45af16bdaa405ebb41bfa2d8c56cb0f2416988171a896d3b65a99491b74a92606b8a3510b6f62654c8548ba01fd9f10928f003ae56ddb0eceb2009514a0570f3da5c856bdea54be2ca13a4e53fdc4deb39d0323a887a9675146b5ad762cdb41221b0b15be6479e88ad208f7337a18d195a76f7404964d4aa17bd8bfc6d0953bf1a5262a4f27944cb96ad3b909426a8357d2dd93f9681258361a90d296919cbca1d2c5c0e05fff755f2b338efcc8ad0404311a5456f4795f17800786219dfc5b631e2f791e8637f39cc3a7a4b6a6aa6a8e751d36624b8af6916a12a29c83804bf3ebab86f9645f9310a240fbe081e13633ab1074f4664683f266551e57385ae7369ed6431f8c5cca063ea6458943686d9a3317d7c3241bfa5c147937c44a40b70435d055078a5be91e953c9f4f8654c6d16fe5e8a41c34880f2def9f56782a0ac4a0d2bcae721afe88f039b3cf05573860221d076931b76de6775d97114af0b02db6552515e63f864fec5f72820c314584eea8c761589cb322e78b013e260cc6967803285eb985f264afd45c0b0a472a66d0e68a2a84b33323c6d3aefc1a50941764a382cf81fca60ec97489f84bc9afc3c68bc1a5fc1a2e1c7e474d3f95569a56ce7b8a2f8e3072ae869987c2e8993181ca377a7ecec84c5fdf74fd20cf13a08e74fe3cd50ce5dea0693e01b4402c0a62133e54127c8d258ceeac355f08226ec9ebd0a105e27d47c30f2d30c29c04283c41b0f907b3cf4a440b410500ad1468a19b13e2351ac76133061a7c03fb6a1eb50a913ec2c96b55edca4f84e32c79e2b784b169777f0a8f894d433384a0074f406293611c24eeb6a4abc1e703f4eea45821f99d470c89d4b1150b2aa591d2b796ecef853788c44ecca70737d42737f80881b1e4f101a929a31bdd03163ae4cea4f3c934ff0782fb58d61ee171c680a18c3c5f228bb7730b15b0554899503b8ae295f8f010532914d29f76da4cf81caeb112d7a699a6961c8cbaafa0b498d651dbe48b907f1a5446661219cd31b58b63c2c7042928a71cf7d77e7da41de7c65b220b493bf925a6489936c394c8f4143f0d860a265615f50deba550758e470bb841963cb857fe790ee863900ac21f38e6038bd0eb6adf957a29fef4991421d71ce793a68dbbc76ddd66137d9fedb6139532eae47307509a127080f33f6322e024ba1a31c0cf97c6631ad04e47bbca458cf8901faa582cad4cf0d6360dbeb581222d1815cea4830ad1fb5b33415bc1961ad9168fb0f3077fcd459317eda5aacea3a29de17e37e5149af178a2ece33d44d91ab3f5e83702fa18b491e96278d3a83558cd92cf91e64fdf9eb1ffdd5fe4d6717c66a8920aebeadd626d7c52e87b7e9f55063f1b3cb7f681b03fefb3b9b4a4f7484b6d7cd30dcf96514e981e3a15c2e88bcb86e2bc6de28fb1ae484da8806e72ee6a7e8eaeb236d02d8505bc0e0eb5f9a5e902d35a565af25686ffacbf78897acc03341348270b6e4f718570e298796680d95aaba5a8d0365bad0eb260ea41c7822e708518823643c1bb922d157c0518eb437013537f3928be5f6d11d7080ae9321fed8b3c7455b8a1147999bcbcd6f6a033294b24516b46b5b6d392c7c0dd6cb3ca304cac118b5f071ceb94aa52889f4a8efc46c008498827a9bf2208581ae3568e64ded763173b3a021b217cf0ede4c76c0d0664083f78f3f201cde1280fea2ffc2dfacd0baa19ab1eb0ab13ae6abd00741509738fcd89ab706af6569734e7a9b971f49947434cd3c5431ac463f7ead2cd2dec4e0aa58272aaa8ecad9fa6335f0d9ef4f787f058b98c795124f6849f4c1ef2383d7e7e42326845d55c1d407f19ba8e8fca3c1083645cd074561b28996790c7b327a85abd8618634c8e03fdc500a55b284ca442929e743b0d63b861da6e2682898481401360704c4b6fc8aaf71de39a148e757b166c001002150036247cd4e3598c2262603825af0272257a82348e50f860c55b7edca77e5b69f6b117fd0d6c27e1685d1cdc49adea3828408dcd88a3402ff71d7365464d718418bb22d5b0c4e3069bc4100dd3296886a16e626a276419520ca580814cbd39fabdb29d355ab366f20b03bea34d243f4655b9559627b4ecbe7951783e82e9ccfa806a5d34de3a409409b68b788b29105ba888f7c24ddfe6be0013b954a2e45dde2a12390524d88bfdff834af15259bb6a5349c2a1e36d7ca606cc58865b0e5f0aa4f72c3df01b651d0cb4c1311484d963596a7866e806a974275ba2e61d9f4a0faad179f6be897dbd62216a0edf22d51fddbeff79b8d78bb875b80720bff0182fbdb94abdf6df8430c33fe7a00b2eecd9989f522ac71ed13b966de526528467d13e98f1237e78986137cf09f30212c1f44790ece2368ccf303b3cc43a607f6338a03430461a43f2842469f28f09f26c6fdd89f89702faa5b754516ede657661529c8ff6bf928103c9faac64a938245f297264c91238a73e63da82f448a495874439de9e79c61f9486bf4f69b246ecd8b9ba63bcb3c6a4b8ea265fd718cdb398caf1c6f7e9d0da01c9b9309e11699c4998df2c0a2e7d1b4e351fc964e8247911ec713870d91ca33879bee81ce99f1d99d4ff73b8bf83c8ebf1d2bcb7dcc6be656506d51453436a5605216885fd1d96f6badc78be8302292ecb7fce9c129c19f632aa50e5d5198952fd543adba1c7098912e8c3f729872eb41b4ee0485370942db997729f0a6202cc6a170c2d7f6faa021e7cf903d1ecd493e73bc4a38b0c38638d25f97c32034414f08354991f3a73e025eaa878aeb43ac8fb1c58d91d959a7f288dc78c3f27e052a1f814d9d6e4571466beb777c2b25da3ff76add53f16b05455050eb47286314227370f6e1731b0cb95502adafa31cf6483992fab82166ecad2f7d992f3df4cb7591e073f558928eccaa401ba451d9d2448d98d43941eaffc0ffc2a2278259063587b741066fe3653b931b1108b8d826af9281e2c1f83ba46945b3439ea175fd8ab1f34ab189e13fdd08c68f0678b5e09018bc982060b540821bbc33d1b4549aedecfdb4ead6bb18e50f92882cb10cecf9067c37aa6c5f85594f29a70e68e824577a1e0c822f58ad24e70edb82b9fa4f257dd6f43752c36e8686fd4a43292c6fd30924e8c99621ac12543f1a96a95639f5da5420c9a38075b92a1bd386a0ef56a05587170c503543a2ccd0b6b36989141a35a3b63b0def12bc7de77d6bc215c9729014a13a1d781d6fe5506d5c7962e477fdbd9d945c05a20d396efbd6ffc8a9a6717a900db258f7c3819dc54e933f21106c996d3cfef258af07fccc0280ecbfd3bdcc5a7bf0da4f8a2a3319829e75ffea245525f505da8aa42116cc65c2f8fc2be84b2f7b05ce8ce8f2eaf15968edb0c1543847c22358e23df3d9ac5603df774fad84d56158b9ee55269a241e736f5cc5a4c490c54ad5cd806cc8e3b8feebd0a47e9172b2fae53ec7211ee09fde8412e4a37babc4c928e5895cb1af11cec396534eaacd215c0944b1d4d1a1a49687a0177ee4a5142f58a5ca4b409c138846951562dc13fa3d6782ef7961c053a0379e53622ad7230f2a1d0b9ea7dc8dbee48097ce9ed8ad03f8615519a0ef91f272ff1f6a5fbd3732a076a34372f5591bfb052b7703fc385db40ead4851739d556b41955123f7d5511ea6db04a51ea2e1de75fdbbb285a7f274ac264273455272fba9e2e70d7dc4e99bc9d4c3c1d8da68a505bfb19447366514a4836dca580c62c515433a6fc75116066ce526ee1167ac47a4a0e43b41da048f235d944c4bfe13f45e58d5b5e0e8701ea412b46ae382e826d960982e9bc1b98ac50d755f4359ce3c93544d18b6a48c6b55d53425c32e598ccde471b44524901c8635f54174e721be5bd58b298c114473c4f6aa7fee05524fc3b6c6c3a8988b02f15e634f3b86c3e3ac65dd1c20cf556965d5a36c3b4d1c1ce02b67fe7438c0ce256c0e450beaff653d4211aea5015dadbd8e86f33f46c60458f0dbed9c70bb02d8b799b020a769f4ccfd036cfee535368c6fd77ccd2002741044fbe876bc10c2f7cb9a86310045f5fa2ee6a91d67dae311984c8a9b21a7f655086ffc0afeeb439070ed1d2fcebb4f0ee78020196c96596f3793e0fd63f4bc91980f5293d163af79e5c08e2cb0594fcf5285930813981ed828810457bc897efc52e89190b39a9150125d5fb6c115f03a75fbb030e76f8d2b513f36514621ebf1744302fa1fd196b1b1ad12670f1b28707d5e2d70738d8c67cb9f7603125bb58c4e78c1d0af67052be3791ccddd9a7cd8822912775426b4c268868a0484eef0049a915ad446ac8e34b37f8d858fb0218efd7aebc727c1add3e919427d34afaa4bc163bcaef2f27479c33e47352c13a1f98c0b4923f5a4e7c09a893e50561bfd01079204c5d41de0580be017b893da8877c52efffbb7c6e1887e7f7341e5873049ae179737349c30143a63b9aa742b3d061d53cd6461507c785e80d14ae189a38752da53650d83c2c7c3a38d710e48f5f0c8208e120bf912b9af88df36bb21246da146a224408bd3caebc291a86e0d31e69f169259f5573e69f3c05e177c61a5fa63116485358c1eaf9f34cbcc2d413e745db718d46881d85196c4df6d7d0097451131399965d5d311eb3cf3a9963dfe4895bad52a335e546490834493524716cc2857051ffd6b30d09b78558377ff2019019d6fac8c67c328915e6699c9f0dd143acc58d763cbea170db51c3bb104a1c0264ec12f2f4bc90d7d22b9db50c958362dc5a8b4b873c9e3bb48d125b88be19d66307571c846eaf0df1851239a4f3084f66c7cd82236bfee9a1b0d2f79261d7650d534711875832727330da2f598f8f40587585551032042c3c82c27bd711df9cd153b260fedaca8d3231ee95a9f49d76d4a7a27259b55179210dd3809ddaf00aebcde6f66228ddadcd3a26337f123959fad0b4ce866bac015e61363c9780621e8524d81ecc4ebd362442cde4c0709966ca5fcdf9aa839dc673133acda5d5612462136c7ae174ab09896ec6284cb3438c092d3ea50d0e169a2a24949b819059e0d7d2a28d23a85b93cd0933eb7f92cc4fa1a37c322a1dd3fbe2a1ec2d5f2cf94a41d81f2d61099326ef9aed5d151d0495dad226c7cd546cae5d357777f5877245e3b97645877dd79f3a4d7944d3d4ff30145914c320649b98df6c10032336fb6e1f730e01aa0288ec7f5d8609d76285ab4a32c2127a456abbe4eda3f2942098cf3a814367fd23f0595acf663beb06f97607916a29ff748703fb285758f099bfcaef144ffdd8278fe137dbb0d18e095e9bd04a423583e1b3ba571919cf63d52f0047fb3d31e12812bb7a969f5c96f060270e19aeb0f8a44a21843f10c3be09722fef6b05123a286aee51d9bb4f556924b705b793d161975c69119cef9b98432f6c3447eb5f19cccf84a6d70e5667432099a462ee863c28c30c303d92e2cd67702d941ad067db167ca257b52d7800739681eeba4ed90270a82616bb5b93293e13d10f1c3942f2ec9b9e517040446e0b6d2900914575bd2429cc23261a8ef73e8795ee6f093a837fcdf9c4d7cccd2ced972600c8ec6c5fa9eb64ad0d476e5d28799e4edc1ef9c1c1b426cf4a7c0f7ddc639a9abff8dd98cc4a83d438a1c5adb15e71698bd0adac5511f17030aa524a55d7dc38e299561e2861eae95dba3760d95011c14ca1d721a5d916363e0df39abfde32e30baac8d87b6f0cde75ecda14f57610dcc742d6414eda98d926b3ae2c18b082b1dde1954958382c32464b2490386ccd9151c02e2c3c6bbef83ea4b39a6aa5e4288d6a8f9f89dacc0373bc0ce8c41aa6a57d1a10e9f230e6a161d652273fe280bd2b7634d1025867b01ad0335f83118fac7cf1eec97100db70b2a5684cac2953e5efa3eda19215c62e100020c029750bc4788b8a3be531f1388ddbb8410b077490f83fda99fe3e587b6a67caff6882a0b1ae29cecee439b2c13e57d36a3bc19e2898e10a5d08527dc0702d5972dbf51856eacca4bfe648ef8561a5f631e51e42a8db508b178667ce2d4105d88d20a27e9e3bffdad5a1da53920cdc5964336f62089aef20f0516bb5b9f11450380c781c78969df02daccc44c42366d87784e5dc3dcf42600a7c81c9cdca02cb85748473f0eb57c8d358a88f0cd949904265b0c45906c5dfa7540e2a9d2d52e4f2a19c3a38be1f2f57a7d3ede9757a76ba1d8f8eaed3bbdbedf674745c5e9da4dbe17ae67a88b770b1ccf98d45fb3651f6025c11d014ddd0e0051cacbfbcccdfaaff8cc9f47f5b9fe9a277d3adf9beb1870a132eba8cd158c92de61ba77d5d2d91fff3762e3658c9eb6a8af9d7fbb9c50a4aadfc3a0dd1a3428b6c849a39eb3b5eb8a1f257cdf831bbc0bfd7adb5f86b1c27e7c056ac896a39296c9ecf875d471778b0b09b44e6889ce43224f41bc46914001ee3ba515c3fb9d5842493eb93180c3fde8b3eb0f32b610ee2a4f96f833f9a5b1176ba3b1412c074c31569b99def6a194dee98d7d3adccac9988f1fb9ce728829829d6b19c5e792a10e69b13b3b33467ff45dfeb7c0662db9a07f9aebf8013a154370210d8bd21e909b471525f5e1ae22cfc7c24436bbc33e2d331b2ac800a6ef0694f9e542651c621f5a055541495d0da8ae04f55d2d76983b02da64210647b91585af6bbe6ae696dca8aae1bf930c9da9b679fb1c7d83f68b86d2eebc82a2e242959267ae51da4b6363a059c933c50581a9ed78beb4ea32b12e2400b3ac2f6f12673d063aa8d320f6973c12f5ce43a5ee7e4c430e017ed5e2410ab1060fe3c2eb897ec89e624de4c8a76da432aec705c7b1aa21180f4329fbbd51cd1577eff9ff2447aff6e50691f07cb7e0726934dd892e4d714db6b4ca3b615cf2ca632020a08f24274f23956aaa80d07acb7e94ee852f3190a46ba498302d8fc580db55b7bae44cbdae6cbecfed7716bafe5db4535792cb6802104503157084c463a0bc6d178a6dbb23bb7370ca424e1f214afd4211ea400574e9a7ac420cdd03d2abb92c6d4995668e5b93ae7998bb534fa1ae7221eef377493ec238e39f2589cb3b591317f6071aa8486e55072e08607536c3b506fb1a2c83aab4a91ca3d6d49725815799aa5171118415fd51f8866d202b873fd70468db388e71b9cd846442839ef168a522768590606c97ac4120ba0d435ed1ee0b87e747fdb2f91ddf23d03eabf82d2653de9c740e9a63d928e4ad3d9264daa0eb1d495e0f827e855898497ace9633a85d69079e42a44b23933b072b7f812014ca933d7d85a8d98386c43563f0c9632c590de13fd5524aa706f20d23d95ef83c7a34b13b164f59e9cdc8f20022e2078e5dc5f696c75023ebf3068168551fa94a3e28ffb1c60653d9a6839cd919ebaf8933c4c451e371c2868dba6d57c386c3299fb10a762716006a0531918cdc26bc9d4d6c26cf9b141927ebeedd5f791d89fcc24ed5a1a8bf3d74651d969c6896a00a602a33c3a3843088544857a5d179f5e359092532eb0e6c631fe3572eea0be8282f0c00d3d413b1a3b31d60bec166d293c9d0fc13c248ed7c6c56a6904b6922f16ecf48f8653a3005a168ae0937c4461761807446229e334a30e31778c7dbf7a20dd0246fbd7d1a32f0e355e9c41151f55ad8e20a49eff4215eb3584e876ed44d8d49c63b17f6aeafc5da22074c416664b7b5d83c9b10aac8301155c46742bc44c4747c952227516fb3a6b1566d399ad1894f11dea45fe14a205af81dc8ec522359ab82bbce85fbb351653d982f30fbab79151d60220e655d691dad884f2640110d08d3103bfe08ad3bce7747d275c62246b6dbb031ee8a560542cbc9c7c55ef88192f62d80bd6190ef1bcb8c4a91490f46ba1e45298e2bcb3729f05f12c65ab15e83d6cf72650b54c804bf5ecff1f43e1d293a5faa3d33bc6536f1bd5a828f0782e050b9e9774605b71ed2190fe0da6f4396b415183351d77e881049d2bc95d856200fc52faac2623913f1b32463aaa23d1493463413f121d094843e1e4bb4cc89ec6ff75ce7d7941787b4de8b5ada2efcbcb98d833474957303c09ac9e6669f4cacca400881e392cc9475a0c7a583f989c1bb6c5e17fa8403f878ecfd4ac3b876a08319a2ed56a4b5f427c102f8f4c0fc9537a637807a68e7f3b15f93287fc74ddbc67eaf7dfd39b650c4f80c1b3974fc92afa91b2ccb83725f80aa6555d9d94fd260fab237dde1bd271a2743c6b7eb4c06048dbf43e16c5441436786066e8a66e650fda9d16948742c40e965fea3746cda4f218796e4622f59b4238e21182ae1d0e2979617aacfc97e55d441b0434c237df2b37d58c8906e2537dbd7fc3e701a3bb4047ea620b2669fdeb79d40ffd39669044081bdaac9777c5cb525d7afa3d9316bef810f34bbdc84da75d5c52b4524cc6d3c784da427381aa5a965a7faabc1b979d4f682d4477ea5d4830cebf8e1bfc30eb6411fc4a7c1f81dd4f16dfb6f216ebd900c0410c95e1629bdf67be21473e452d4ed7193b71cf4c9b90e0bc4b97c1815be27ed1b8800658fc212c71aae323df3c7f3e0c294d67e0c9330a679e4f3cc87502b4176f7e294bef5560e870eea87d9d004d04fa3c286ccfa9f261cf7e88ca63f6aae0599710d05e7ac162461b05b453e02ba140bdd80762c950281ec3111edd56b5edb6ab111363675fb2df39c10449c88ee2d95883fec214ec65af90817335a2b85de55da48d696a9dab1683956c7beb440b1ef652ccb64b0fd5da737c5644c52549321825bf448a00c3814e03f04c585abb95e73a751f5f13874c948d51d51e47f6c0ed33c9cd2cc1bd6bc0adbd2e9abd7b82600c3c5aedef34d416429651e32c9eed01aba84bf9fbe168c2c32ec38c3d0bbb8eb3c00d5ecbf06f8a8b3bc92f705c7ce71ccf5c74cb967e4abdd7850ce50052a374ac984823f135be3db53364a5dc727dde92485837e2cb2c17f6bfbd98c10bcb115b52367a306f7946d63152c9d4fd886850e73c54d53246ab818e41fb9d046a2ad9e94228b8c5169add6052f32c552cd5a19b5332300afd0d26951423085f99da262b78235c4a46ae09a44a639a8ea77f2e0a41c99c88c93bc2ad16f935e0c41db9b6088833152673cacb423aa65835beb2648405c725fbcaf3e01e6cedf4a09cd541079803c6f84ddf4ed43ac1156b5a8910958c357bada287c96d7f8caae629becb80712a9d1595a775cc22951d71ebd705c64c81ac6168f0a7c9948ad4040b5eb2af1bec614e08d2da6f89ba6c9a268d2896a6075a0164253802d08abf80849bbd7a98463ccc27f61738a0a1de2b8efdccc939b65552ef343798c1496d19711d80ef5dc33485a898e854c9db82d05e2621f7332d81198c15964d31b82b4b2be7e010231308efc9606fe128c3156ac85d6cd31bf2ebb0ad71ccb27d2675eb1123aa10863d793f9fd1addbf72862ac1adf6ea4c187c4e50c66ea8a960e78130759b449baae3064c98a80b7e8d5d72520dcc6f85cb27af4108b243d21eb2ccf308d745d971615ab510e6db90b7f3e1d3c3a246da7c8534ca979582ff3c1732226d2913b2078f3d7d17a298027121583140d08c722f132451d8f8f103810dab7c4cb6b0abf534f062b24756eb5c8feb14ffde0d571c430ca6bc89d7ccb2ec9d4a035c36400448592d3a170bda0f008ee6d62b129d798964c846666444501419db18179d6f36d46f207f001f19fc6e006215e64270e0992b42cc94578cd05e70d874b36fb7f062a4476ffd15d1e4989b1d120232c2b919e1f9eb4b3e84be7393eb06f8e3b1d753f6f900f8af5218ca8437463d9c8b12fcb88abd5d6d65fd0c183591d23159bef4aa4822aa12700ffe4167142148ebcd8f7338462d8d6ea4ee19efa3232330583402b324897837baa2a1abfad2fc4dc98822b8fc7c7724715d6234a0c905881410d8e9d7d21e6b1a26c6e4e04929f4acc06e997d08749cd05be190fbe629b5b777b2c9fea051a64241242266ce83faf40a0a1ef7ec7dfc51b139529001a56d139d9f64dde6a745f0e400f304f65e4487343d9a437dd89c2ec65c03b38072d08a2e309d0567809aa85dd2ad30398b9b5518f1ec5c455484344567c808517585a8608353746c45fc448cda07e8604af121dc2bd73ce7fc660e8a8e61981af72a084ab96430c74a3c7257c6af078f8e1725ba905ed3a84f1706c56e93e683a58e0248f3935a99fc62f29649b1d2ed09bf3d6e53fd191c1a2d0849a868f39c260f2d8d45ae36b4a4a1b92096affddfccb2d077e3f27ab2b596e5ae0eab43374e4b9103d1fe14b0a125736338d80d38776682b5226ab3683d51631427a9b3bcafa703eaf9c2eda2f4ad80dcd0b43519234ebed7b08684e7c5426c658805d0577c30de9342c3fbc85f10519405dbb5c885fac3a5142f24a26ab105348b5c9650401fdaf9a97286b6209c9696558f6f2050ea08f6e17bb0dd4ee4a1f8a99b9fefd21c5175565e284979f5c4b0cecff2ae9456d962a77c0bbe6b0d2a868dc0514543214be23f3eb3f597dad0339d6daa2f256ae33962838bcc18cc2ad1aa778a4c833642f55d5eca21fcf10de53501d2382644334586de28bae1999c99a3b53299f956b0f32fb263458b1d698b64cb74401f4414f6f85cf5033e953f2e67d20c0fcbf43d73a8df351a3d17c65a7b2273e0fed4928f97b0b9b95b7fcb1dd947a3c7e099ca9ff48a58d8e665527a45d982aac9fd14074b58c75d8f54938ec11118a9cf3641af41a9027a86fc4826bcb59427a00b8caa58faba199e5fc813f7cf30d62f99493d84358db63d901c0bd8d3a53aff0bde3012d4a1f92ed116e96e22632afd90373452623102c580d280e6d2157063122fa5f6d471414e220f24ce87dcec6992c11f12d22a3f58b9aa58469d4438a56ae104872a835b4aad2e1e8f30b2d5a70abd35625ed4563724d03851de2a94461bf6d7ca2258494be090239b4c94f8f44619939e9e518da215c236c39da7e596dda52aa240f1fc7f783a02040ed56deab7d9ccf53779b2bb0dd65d06a4e9ab2c552e02d2f3da1811014d32d3936d9f6409b5a6d21c02254e762b83e9cfa9034ba31daf8e35edcd909d0cdc295cb693a783c1fc986b2a7593ce4f893c770bea4a92adf201ad764f09384206e7bbd600afbf024f64bbb8eb1543de951de764210e962570ee9e34f22cd25957c56c11f36610c9f4b464c67a6fbd93a1a6097838ba637934159d6251f26eb739d2aad1985d3e65103325d321bf82385c4b0694616304009794f07b764c6b593fbc02eb272a711ca79bbfb7be2679a12d8ff48b7d468899ee32c21c88b56850b2b2b62f4ba7426b6881c932f86fe0658c4f8ed31f342896d0663e30870f2c719221734b42aa2361ec13892b360dfb451fa02b5941ca95090e46230506333c182db8a0396e1b43684b8053ea9c429233449d086379aef2d840dbcdb65d6a3201bdef6893ce965a720746c2692aad28b3fd409c183d784747f024b2bec7c239c839c64620f836e19310b0154a0f332e5db8b0050aca494846e3dd472b5091f6eac65512b27648e3a836165c4db4de7297845b4c406181ac8543c127b5ffd69c9c8c5089bc3e6c06df0624b85c4d925f4714903dbdc811f78d7380bc1abbd61151c6b016d8652954758b3894f68751964ee9fce20c102baf08dccd0d584e66bebad4a8d452ce7f95e7987827698303192aab29e7f7f6ebaa2f51275a141d0ae3529bb048517ff64e56481fb982ac8d0f63006d9550b3d14cc9e24aafc8813b1e9e7530b838fbf1f5b6cf4faace9d08545dd92ed812890c57a6ce6001c76eb862a2be2f3d0fdb4d8ce9492d5a2204536ada10c552bce33ca9f3479248a747908d36c46466792951dd4d1d9e2d00b31be2f15e2ba2853cd634d56d9e9b34d0e46f726941b4b2fe0e2a2350bdd8d59d5569780cc776b02515253e1ed000b2919b757c6e1f326b07180895570583467f0077e52def6c844d18ed3688f3a8bee705e13e9ecc5d55796c31ef424a5b153e7fc16c4250b340aaef1d207e27b2b0c7e29a1dba9ba5ae960db93e0b7659279b93ef09549bd84a6f06481de1e5abc009659cff32a0a140cc1751713f7420126ba9cbaba84be9850372d2233a64aeb5600c35ef658b7429f9c0d817b432266836e6189987907896cc13512254e1f84e3eee281d13fbebf8437b800368355f8ee469193554a97bc15cb1d0886ebeaf574a97609ce966d79a91281d3e830a8b8e72ea30c84ad028c66935aa832e40aba3ba6afb262ab0011cf00e3d4daaa7621572049009dfdfa4d35a2cbd4a82b68db0d0291e55e70ceea87c800ddb33744d71a40297e80b12506cc43f72f6cf6cac3e36fa3af16f43ce1e54dbae371c4870028ea17c295d2d85c8bd9f78a8d7bb721b9f70392d89f64cf5353779c67fdf6a382a566c8254c31995bad2426f557c6c270f98abc786f93526a4f9aca9abf6ad6c00d8831a93451b3a64b5b321be5e88f140c3f515b6a80f15b8cb3cc1592c699a2d6ff7a6e91eed843cf7f4422c12ebaa7c0e55876b0237c9db22531159feaecc3e7f4ecf8523e568a0cc4494c33cac812a5137dc98d5e1d3f1c042e22806f4e6bfb1a2eb0f72ab8556c95049a638b152603a2dd87d62399773d756e384ea9b685bb29ce04338b5dea4d445d780dc74f85bda220ea8ad63df1f9f45eaec921811dd8447240397790fca4b4ec5979b73a76ecff485b09edbce769b280223cb1540963af4e9fa0af46c00245e15eb4642de63f602fb75e131f086bdd55d691b21a88f9d165442b9ed0bc26355c708c8d0415f6944111f491dd48f4dab2f3b3db79725123029b78f94fc1d98a44dd6513c3314eed42c855c7391b082c6af88b5d60e3a71a16ee00ed8df8201db0053294c3f5a8762220545354c7edd46bb486577b0d08047a8db15da2475941c8c2da03aa2bb83da8060b8208062528a9f823fa157e87e9c0ccd0fc7118a2be30a96150b9fe5aac3c87f170a1451677bf01a2ff86aac804d776db5ec6b8d296833ef4edbd56507fe05c97455c710266c527df93d147e0a99c3861f5692fd36ccf904786654edc5271f1e552bc9f7971495859afbfa7b67135300419f51eaa266e0e464b44036f04bb1edf07606c329f7680440491c5db000c73ba3641f3e3e52f0e593b50a7905f84f995909301b8222eebc8a8e90b2adf994e71c2b9064004e4a2af4371f2adf81a6d700a3521af7e4c7a84fac3dc96036350fd67edbaa0f4e0ad4f287ec1bd13c101c228bf0ede0cb2484f15891a635d32e8cd6194aa74bebf2243b477c930b03e133a10a73c04beca29e25860ca02071c7a0335ee43d1d56f42f10ebda2714479c7b0702de6d13bee0567090cac8a2bbd69b94ff9ef36b9bd545097f4da771902b0411dba852efb15ed8fd4ae4db14355a00ef61c3739d991091e401f8b3d54248db45c20863a60d4022aab6d3b825f61a39105a26f2f30cb11fbf42bb238e57036b544881df3dae11adfd564a256cbb8e3448ee02f7e0c462df72514df0e6324b7bb8e71e057034d295a14524c460b791ed2f9e572a34bf9cf1059d466526f9fe48cab4f8c040345d2120c9e14169884216337e577c91bcb2d885a871428368a72b034a55a9b81874457d951b588bd9b7a2d98e18af17df9dd60da01187e52e0274576ac8ed790cb7e169852cd5dd18150ea08e10a15159da31ebfbdfa39ed7dd4efcd226b6b6891763858e320f4ed6aa56b96ce6e4db87c0b74110d0739a59edb5f43a60015ba63d9b85dcdc9c227dd9e830567b80de0ef042480e9ba615a988a6ebfa05b06a63648c5a88be3bc79218fddbd0466faff8d903bdd4e083b9c6c2800b43db7f6dd26ef5d54904c20214984d2670e06a76d5c7f2f7e8ce90e6522e3f4ebd273a6c07433f9117fae0355b3c85fa5fc300cb1a7f00622c3a522f356e11db2d9b06c5b019553e51b9d5964a20128aa2a87cb0ac733cb1faeec8012983f623426b75b94e29ccde30585fe193f98436a2512bd0be3ce79f8d78d1cc06abb233e7de1ea8ae1c059d9013ddc80f9a9c01b9b7de222a1f84c901d74ef1d60901362f5c52906ea94982d8691a29efbc4b167e6dbceba5d7f399c6cb65ad675fe2396ae78b808af3d09af840d374ba2bb5733e0191365143d5b9b11e47b1f1f73e98937be641dc43d88d844fb72bfab55e26195a04c4668c1606225c166a87ff2d7e4b435cc585a13adae99fced7192339790f3107c6813b699e9c6649c7fe951e73509af3284ef21ecb835fcf318a849ebcc6be0eab179f4e324cbfe6277c6efb2d73db7f35852bb260e13cb9603bc07d8575dc067148238fa79495de698c1817fda33a993c7fcecca4a8d517ff1d12e2921418c386f5510c49f70881f43bf02b3e248eb012d1ee27f2e759f43262ac8acd19d6871264481d01fa55056a42d3e95251491a9a573b3766f7d234664c9f2173c6cbe0ac7ee8cdedba40ead1b29bde4cadd809a3a1b83207e8e7b20fe0568c834a18363cd3738ba94b48f9cb53eadc62e111eb476843ed83dbfb68e37f6227306c750031f15b6481fe0c09a17754c0bd5d9669c6c7d3729f7e68559148a88ddc5054e9afcad4565d1e02a3fb259a644c1933d8ddb2509c59c4aa2c1979fda70edcadbff79f1f666ef310c760bcaf7447bc3b13c982176c1c15552439d01f0f3688495a7b87482c4c57224a54f2d2cf55f28e682265b678057943d5d2d9f2361508079977c1d2ff34aef44a4041737dcf0c7314779ed3bc086036c3c21613f718782fbe7fc40c36b17bf93f90686dd08f930c5ae5309f24a19f1955550d48e2496db80af7f414a0f1d013d08f802c82430a6126523b2cf437238609b4762782ea1153cc37b7022680fd18f96dfc1645f8c7b1c22b8ddd769ce7b56111bda24be2902b264c9da1fbe04a30beb8db4cb65691c1fe6c514838f01fcdc3e31a0f1ffe6e1acf045b9e97363f55b8eb1906b9ce8d065443ad83eea908303ae980bc7af2157430874994cffa18f4d8e8263bf18d07326948a3cff250f37c2f6e49dee9171f2a045019438b38c2446895696ac404f34a909fdc4b3682ba32c04a7528072f896c1836320b0b1b487a7a94eda430edadad3a015d31549f7037dcf816ad42be4b2d35dc1ffbb529d9bc03f0bb8679c8630c4ed78f32097c7857d9e3cf7d03d8309a18e3d24f7ffd0fb10d4ba3a569f938723d49bbe373f25ea8ce22edbd9aec5bdc737d2af6dca96a03d048f54000ac930ff09907f70e8a80253fadb6c1098ba2c95ed479409d015e3b7fd2d0a42789f78659d8cf27c8f3f9cf1e7f223132d7c3c2437bf5cd62504fb00ca8467271667b64273ab3ad1319ea948089b7b5ead9ec11cef63e1d690a0740b16ef7302aaffc8116b106d5ee46a5e59b7acc0dd5cefeecf3fbf018cb7f19c819e98713bf400d356e92f697a404d2331d74ac20ce8583acb813da9f480ae9a83bef2d6632d24d98bfdb1907d4334640b0c019fef216b2f2cc1437d01320f38e89a44b995a50d77a0936f340cec80490392ef3a9ae353a940f0d5853b1538bc1b8a0576abc1c50d607c0aad7b22697700dd88aabfde5a1f8877cef45fa00f8ece16fa084a750f1e240ff17045ce8a87d91d50ef328c413fad3300dced91db778a0cdd8a6fdab9fcc45c19be21bd70fdb9b7c3b3290789da3fe08a6b88afac6c0d06628f0e374f71da53e74a6e6895a1d085685b5f896c3f1a75c49605b270f9d5114e399491eb09bd415280f71dfe23aa91b3c09ddf1b6f7542149625de22e68ef8c2bd0c5ea7df1348131bb9f06f39a64313be3ac6b132bcc588771b92484838a7e79c277a37f1349ed9165ccf0f4908656710df7261f932b45ae1ab033e10a0c7d0f931f57876475f2bbb06b57d9f538e57ba75831d1fa2efe45bcc84d09fb0302ea45760da2afb9869c24065f0205dfea9095e5d64658d9fd1c347ce123067ea1a444901ebf52c2f90e9218ded968884b48a423b753fadc1ef70aba937820ed2f5aac1020fef4cfbfa6fce70c7a5dc42c4e5924cbbf1f880d71440708a6e7cd8840c0d6e2ff8eaa5266ef83acac024f73e946df49a6dd289561213e77bd470e08b9e1e6477e57bf2c5a65805702889e8924c0357f25bf79b8ae310be629d4b9a81c083a3fd2740aa7a4277423f80c3733456d1133c92f2d35d11274cea5bda7fb5a95990cc45db2d83d67f1025bef4ddde8e2b0124ccb1967036ac89e9d7fc85131af2a748c4aef62b6859afe10429c3b74857f36af746476b3540748b2ff76bf9ea330e85fbbf1d606086a1bc7e188595b0e704dfcef8a2a49ce1981410dea718d06c74c214074283bcfd21c1f5c2276c552bf1d0acba2efd51103cb82ce19462d50fed6d5021bb811a3ad87dd956919552869c3e1bd55f20e814c801970b4f569ca91d41b4b23c1118c4ceb1801021b6bb7317fd4633d279a6f4dbef98c52cfb1ba259c32ceff5afa6c74b01690207550f0057e544dadff05eebdce89853e0fc01f633ca02071913741cc8623bdec59d37db0ee4fe46ba84be18d500f2cf623aecd57a4a64c2a1f943d0713832f7c307308b0c12648f15b35c5260fbc81b60b86342e377318f97b1436c344a816599e34d05f214fd9842e1a4033e46f182d42c0e68503e3cd5c2b5230046f0ce1a7d8a0f2f21609c3a5552c056420b0d809fb850d7108466b7685ef6e27a0e59ca79701c3bf0691c8565ece5e1d0718bee6a612201206c70718f65760c3130ff676200457d3ee9bbc47432c22da5a02fa24713857a63260d0a3c3a5c02de08a48f269dcc917e888698f2db1d9c3645b795199069296064e8e76122032fcad193abfb9f49f833d277746889911d15b7b6a25838dcc524b716b9bca17d79428fe4b2b40a1efbf5f9944d5fd161e2df0107bc75aa75ac2c935152b963fd2f0eb520bd0bfdc62641d066053d7dd63a7a465e92c5e7decb50c4b1dafd775497c8908e5532469c712da82f962a6a5793e473602b4950065b32564e7220e8ea8ec4d13f4e17041a787feafd2d364d27596b364e2c3926c914eabcc1315ca10e17c65231aad71201d1aa20ba2bb3476fc4b21fc5adcd566fcf7d66171627752ef3f2b5488933276a775b448faf4c6b13037c7c47030197da231177d0e785f8381f06df824a89d53a74cdd86c315f71b4b6726ba43b53840f7806d572960ed0eac17358f32daf574a838fdb77a8972206d56917226e2ce1e2d95f76783d160b0c7513e528fc00334c2483f23a070dbcb2cf47a1e2fec9542fbfd3d6f2b4701a19ae421ceb606fc77004f610105f074d2d9acea1c296a20381d626fc1e087223a0de5ecc57c56002faf3422d01b340263880e65f88526676385b3e42119da4e6c8717707ae7eb6764b05d15c429d24a07a02d8f3870b0fbce71a69488fecbdf468d95adc00436d0f9e08584aa66b2fc8a0f52343063dc57ed4f00274ecb2f1ae2bc651b3572e62330ebb1758801569c6d65b0b56107a74393a8c7f09a2e4561dc21781ea220eeca049e57c18e7bb1a803437960570605c066fb38c2b111881fb84e9865c7f8fddd54ff5c9fc72cc10737202b23f912c81e43f31526171a550d6cd1fbea3685256f92a5365fa71788e2d25e5d40f15df147b79fccf047cbeaa5ae4bb9a23f9b0ccbe2552ebd51520439f09807638fac5ba4c2ca3e2a2399b07442a21f64a9bf3387c9b7cecfd3472b302d0242e4164c6bfc68025bd0cd689fb7c0608c25c853966b8b8bd275f65037a11805e44b2c8d19e5e37fef84d6f55430cb6a63a56847019031665a98e0e95209e65a3b740e871f1f1ff83ab6aaa3d52fade934d4a8a0f348f50a90af3b6824ba8068fba4d38fe0637f69c63b1b0b5233826dadb23e419128a97900027cf044395b5ca1162da9b5b2a520188183f886aecaf70266c296283a9912776bcf3dbac2c53baa7f5f1300247d7fdebaaf78aa70aa7dddb443ae3558db1939d4431703d6279b485d718b598193cfd1e44f4e1d78eac3abd28c12f246632e7f58645fb7c81fbde98c5a65c43088a0680607e01dba485200d197b1c28b369d4b9af3948721d69cccf6cf9cdf5abf2e4374791eb4ef6352804c4b2b70c68c41d1a36fe8125675c11351995c2b39a462e0987eade52aa150653b208bbeda6c16a5d3ba87a88d45589c452598b80cd54816f3644a9afb5dd3621ea7b52b619427d8613beaaf2ed45c30965d5a70ad9b5782965550cc3de25b484058b3d0693f6e40f8dab0a7b650a59001acc03bd7d77cf0551cadcb4cd397b87656ee3812c62d8d615ba60bcf65071ec6933fab42ae012d1d9d0dcd35a65bfc04ebb3105c82ec75bbd1d58d5a3fcd1651a57702705d4b9f11dbe1d9fa50da3e5c460450a1306ba1a4e7c049687941fe9682bf5d1244df28119d02259341d82efd406d843e3b3c05039d8cc95f5ee54b43cd0200baab138b2c391695da1a27023258a089d83a9809e59cc2483d56b234bf8ec2a0af3118ccf32ed1853cc29234943fa45821e3b0269f88a8f9bba5de11e2e43af4e4aa85865bb57b7333e6562254a92b3108fa30192c78b83eb9c56f2bf8d794001a678a835d74f8f2960b79be7da4eeb162819a4773572efe2a3fc72f2a89b8aacec2f783568706ca71541294e0bb27ab18448d48fa9902757b5fc3078b65fe7d940e1392388520325baeb8c870dfcba36d3b38c4625553b859f54a4b231c55295ba603a96c1ad68805a252e3999c1e43dd5618a8719f8c1c270038d11c8834c09c8a7bfc262218125e40122caecea42f1f20d95e7218f28a7ebf2814b193903e4e5054c8758b49d0c603aa69b4b79533444901b841b4784b60b79e8a73b520aa4b91c842484f39e9558aef12888f1d52d58413b84046a86593ec73ca4ed71f887c7d4a738840d39535da02a68f6259b7fb17ef154e250505fe2f5980468482c27e6489d98cb26fa19b3763f1a69c4949ebc20e077e4b2b9f8956e1bf478f091ecaa51200c8b07e87e0e8451f7ec033960072bbc9c625db2d495334b4a2fed68e8489bffa73852887c517603cd9005f9a79236ae1622672eab6deec489ed88dd317cced4fd16778cbe0dedff2cc1bb94c6b33e7819dd20c16e0c0c4c74358877cda1906104e93164ec52ddb23b14e42a23d073d949120410badf6b9c1651254a5ae121df17af12ea1af662d91881232508b3d309c0da7e448b8e49eb815a3a98f953a3e53788c8c2b4ea4e1fadc425907f7e714f0e586f7d9c9621fd5265f29a2a74a74194ff15ca3e0ad4695a4f7816788a172f8d6487089359b89edb164059d90d6556021abea0a5aa609d0888b882562ea163ff8935110365d389146be2032c5ef07a5c3512a229c35282bed36939b13755d4a22b52518ab0687572a58a8e0a8a56dfb287b7474f63b274109c11c3988d83972aa214038693f5ca2df5daef8648ca6d6a848d6b1428b080cc81339f2a08283a718e8b7d166c860d96eda39ff78532c1fa0002038e437a3bd35e647b6cc28fe70013c7ecd617a8eefb8a359cde80634a2efa9ce391d17d14b6972a0c8783a7fcb1c5c65eaecb4ff8b959398fa7bb297518dbb61a494039e08e9276f2107c78843560ca99021723c2e882b6d8d264302dbd58ca7b8543c0b1fa49f43df35096d5cf0a95e4f256c98387508e5f6daf864e985c717e5f25aeba1d4f8e722764a8463b3c489160f0c09f27a7a3fafaa841a233db445478ee23bb5aac87cb27c7746ce9030a8a7a49ecc911434022d02082c738b1cbacb50ef5b9568c40847b50c62018ab2ea3b46bcf59945631c48069d1825e43c854cc60eee4940cfdb8a9a660e65bcb3aed65c0e67986b087c5a235933fb10eef8fd1b5de8e1dad96ce2f132827f5cefe49fcce0e79aa85526e2145c5af9297d8b9d11cf9f64f06a0eb83cb7b5ad88631c17aa00a5549325ceb970fa6d02b847414daba9686825d57a9a86d20399dc2be25fdc1addfe30b968b7c021531ddebe64e14c7acc3242f855230c8b77590eb2fdbe7c8c00be043f96f1ca608f929a2adbfd044faf3ea3bae707085694ed975fb67eacb327366f979f300de01b1ec8a894584695d997a1d076a99dcf744d3d60c0891f5ae76b23c222a044e0e6bc5f7c14c24028775e4d12472ec2825d569c9aadd44d19363e5c1dfd1f49b33be2970ef43992d223fbb141d72dd7e0840a3281d09e46ed7751a8ad417a8348a6dbb72aaefb8c11240ac2021be18e367976effd85b3abfd9240065eed22927b4890d375c83be5db2b7106a0dfcdc47b4441d958cd9f32c8e495440815dec6934f4cc84b20dbe29d8be624acfb8039edac12619fe33a25d7d9f9819d69c9244377869fea116294bf042f7d4ab41d948b5d7d040c5e5f2a4db4252126b960ac82899a3469e6543b7ac5c101835dea5d9f6d8c98312da3d018e8b1657f1f8dcce85085b1d31de92ade7bff9bb236c4c2d13328a3ccaa386aa84888dc89bf065439d348f61e21f3a151ceeb4984b933b4faa3f2dbff398634944ba702d8a751c16a9efed01150ff25b426fce10472d3ffb575adfd21e8eb3c72fc90d56fd26c2db7bc5a32501b9e235c9f7350b1ad4993d6f7ee9078747cc5499fd5a7245627f5c999ed27279fa59f1c5040c0c6c2a560d303a10f19ddc7c2a939f71646b5a15a2bdd9b232479497ff32ac4884cdc444b8150b46f802dbd50ae00479acb0998f0bb85b5c849bce75adf608bf5b8fb9487ed10923a002e83c868cd05c61a8e3b6305bba9e5518e2e65e58e0ef3c81b726586f28e4fc3a41145a2b94892125c0b26a82cca409d208497264ee1be3b5c76567d84910369879ef9c440d0b0f6ac581a341bfa5814fd229d6c4cdfae933b85b8d2b9d9b6dd92d37890c80f303073e29d2fe31681a85e30aa0193895c299f9c00e87245720c8a94cd2105c4ffd8f3ae22e49fb45fd7f5177a3ed203fca1e8c5bbc7c51ac5123a72432acfc28a30cc50fe92b6f4d28186f6e731e2e9e5f347b1d39eb00006032949d51d642c0b4bd5139a59c49c7a000b98a5f37018c5ad74fc4444592b07040390dd78c9d1894117e614850778b6a86c32d4305010e6bca6f9a31dd36af830cc258bd20cb169ee06a2a78f23e124544e8807d96f02bca1fd26e0bfb518f547e9ea5b4358ab594568b1fe7f7eaa25fc5e372793cd07b61d3858cb443f3f6f9406721610804c7428fc699ac9918d9b73e1f7e3160ea75404d5b069f4a8a3788e54264e7a497071c05c5c473b40e7925ac888e933df961b12a370cbc7606d3410d7bc5539e4e577c5bf0b878d545e82a743efdbe283e2e4d799af3e9779f2716580540551ae08222278e6f43467ecdf74ddc892d6e32efc5a6fc349cbb4282744fc724548c5c8624cbe3ffd555a4a3084fea3f72e7fb8e5942f65084e0a0aa93b1f2b2430ca1cd5d6ef1e1cf869fe0310bb6b32809f21c3f482c22909ce23e51542e755ca789bcba2c7a5a50e41c6cfe5ad42c9347cfc4ad4fe2e0c2b17629325d9e3a994096d751f9978df0bcf397630f4c57547935d88d4613a07896e9091d3582d0dc50db5166e72f111110d0c973122b827059ffd7559e4314be226a212f1dc60085ca0e010bfe75a27a2673be913be3ea78e83db81a6f338d86f57f1feb70af7e4dbeafc081590a0329d52343fadf740dfacb2648af2ff2dea08e2a44a4466dadf524b651835848ead35c65220e9eea7a09fa8ac148bade6f785bb862b7cc8b04d0a3d8605531d186889ce4dc15fd68e3dc6cd72a06402b3c663821349c3d1b3e415e326774572a27452f890a0aa4e373fbd86eab20ba87a3bf1739f693de235c2ad189d185df4f2a0c1c31f9187f9fbd224f87a69451a1ea87d31e596bf02ea4bff672a4711e41965b33745d4ae16d93346cc83831d39391f146d3e4f9c93686fa828232d0105a14ab1bcea1811309cbdaa2b9ac75a92ffab73d3ed8db91827b9d093a472b1afa6229c0fc18e154347103678ed10b786cf430d98b06e9facf8b6157c63e2327913ec4842f7e899b2d59ee75371151697fc357a74fb2249298d1fa38bbf45d2b336c4a606896e54d854c074282471c042484148386ecdb801fef9da4cdb138a00729991a6c874139383cb1113201b84b0a65d2baa6d13d5cb743f48b873f61d0d2e526b778891b92d957db4303bf49bb649d88ee1b2ccc99dbcc9fec3fa00240b12b66124d2c1ff5dab205f49cd4db3c2d471be1e02163436fea193d18a58117f7e447847e848b6ffa34b791b770094a9c1ab4178aabcc6b5b34b96d7a19b9977e44ab8648150ff818404bafaa7e85f4df903df3f050e069e9e863d49427d67291c855821d4b6d8cce8de411d4d3caf3fc51e901f706d04ab045bcb92ce20737efa6dcf16803ea77898633b5f812292208d0a16964db6fc8ac903240c0a3ded660e7e3651e2f5b33b14af9e351b8e1d2032e25cb6d7cdb41bfe1f0c6bfc31a3b14de2b3f335f789855439cff453f24934c1173d3b9f67809f4d3406ed4ce9415828eb3a7338d9193eaa87557298c688deb132e0c10abbc19f29ff9448ea8b9a9d3f1b96d1c0df50861a4259c267e1f3852a2b4f30305d52f7fe4f0f4ee9e85e2133d5a1025f16ac6937712b3c89af4ebec37b7d43105431b503ebd4b1c6929fbc060ac4aa24b98d713abdf27b2bacb518d1af966b2dbb30295aa9dd9e72055429f8c83c4434ce71bb4be62266db8625cc7e834a77ebc22155a7cd94cd464cdbfe833c3e19439cff289cef74f3c2c07025760cbe6718bab1b13fe5b301c184261ef4648bd83298a07553e58691d0527921b534f31b8313178066a252f104b3e190530c9d3c2eb599ad769d536f86370e222e7f2875986640a87ac93e96988ec23834c3578db33af3ae5002edc5ef2dae2c2cee062a4aec1c6b2e69ba68925161dd094a0432252889c8edb2178ca9b6f31112ea41c0aaa91b4a6fd20d8a72303c3bac4cb609f858b71e067f01a6d030207a892aa09860d1ad0b594131ae4489ccd1ce0837460efb9ece020af26b05f1ed772912e0086b1d2c85bcf3a7e0b928bf451519880267f0fd58c482089af741f0629612139696d36da67e3065916eee476411f132dbc5140a2b56c814618fbed8071707ebf19de39701dc09b4103bc4bf6ed966f7988bf04e08ca5f70798f1638e47d4c49c2a6031d495059455af63052da0f8fb33dc4848c5f86e973911074e9ac2ecc9acaece3819abfbe7747fe376d968a6b93b48e121c41efa232788bc5a78d1751fa600ac8fbd49caf0d11c4ecf399c335a307a05eb42ea8925b34895d95eae0942d80008af1d4c2ab280860c376ae3889bd5f06e1d1c989e8bc20369c9f005254eabe8d883b23329947475b796406a6c92e4f2a7d35e365c8fd70b0a3262bc0a17c6754fa2e36f3566cb987f411b0ef68e49510a51bb59259df69cf524ef5f815541c4aae99399b0b7f8adaece0ab8234bd5dce5dc552fec7ef850d3ba1c7607b6cf0262f1cf5b0afd3dee0304aae4f14fb16b614d5fd34b732894c5068ca22c1d63ba6d47c13b0fbf0b9aae6b690d1f6a37b753b17839af4eaedae885cfc5a8e0ca7dcf475431070c51fe1a515382fa31bf928b8d1f6e479e2c7a78c608f1c467c1fb203a14a89ec464c803d129a845371a02e5b66e47979af6f04c69f37e59638c9d8e5595c74e37eff86bc43fea19082e3649ee692e2f66e48e111b47b03d8f97aa3b1be2bef4c0abf268ae90cdd224f2202e77e14f4f74fdd8b27c2a29fa00816d7c6c94f67e6498adcd7d306dc8cd1d534cfd5c57c5203ec323dbdaa1d005eb33738c583184ebe0a5e17ff800ddec491e071c0d6995866b329d1247d4d3fca5a901ec606991949f70bb63daf0ba4e2c6fa227f4cd8d0f1c42b7cc3983327fd184c4547f995018e30e78330ff6cfcb89183cde28c07d0f58e5992ca7b1f47caf44056ab7af0055e2c33d8187be96cb36431cc7fc858efcc5ea40ec586ce420c87ed1022c1347e7d9194c985225422572e5d241c13515566269a342de394ed6cd669a6e9ab859766251194bdb60b2019f68b6c0c5124975513083c6193434382e73eabb23ed72a48e2ba77fd30f7cf6ecaf013d69f101bc1a6c05cafbbc2a1fa17d5555bd0f8b5b2f70e8320e4b66420fb9916d11f00d42f9228c38426ca24ad6685fafac8b28cda21d676053835512c32bb66acb706ce77b4be01b01c9c5e4ca284cdc69b721e1c111a76f9d2558d9b750662d0801a35d1564a31b274ae6698087313361fc08b858cb56fb413dc07567e20cdea4f6b809ab5670c0c4d8139c2f85872d009ebc38f69c564b53eea1d562bfb982986b4996593183f7022f0b5f928ce8786fdcaae49f5f593ac9d3313855f3c01c462f483d55f42d2541d17bb36ab5f8831a6d186813f003a50303e5620365ad82c2268eb3e717029f714202865d889ccb5039b3959048827c44fdbda923cfcf91124a927531572f573e57cf2bd8a87776101d92b26385a784c650590349d93c17260431fd5670e61c6fcb32d3ffa8298b6f1818cef6d6a326e009b3b612dfbe6d570407e455c6e082a1c3877683591d2df5d2381d16d23f268a64abd21191457e245d33cb0f21372dbad324ae328c85dd3a3543c880f57cc46d13baaafe9fa8038cc6cc8783c5dacb846ca1ae11c65082632aac1f7dc7e31c2f3c87b592d4d8e6ce631092ed8248758c546c2d0808a1eea6c09a1216a332d75f1185efb26d5c2613e31b06fb3a4de56871ba7cb2ae9f48439dc1b6b772b4ba3b8f7c5d21cd92942d1c8e0d07e5142880883d237b4281dfa90e41f8a50bcf7922acfd02ef059513859621e2e7f0ac2ca5da1936264340413897c8d48043e821d613d1d9ad9e61e04a2a5f710ec56ae17d4f3db17a17f026ca70ee59e6d58fbc39ff9835eaf01c56401290715263599f686c71140c8e0da536d038e047421eed75dc7c67bf7f8955be4d582d97f7d7a717939001ab097b79c39ce975675d71c47bd67a76fc4ea907f96ef6ceac33c45da5b19c53dc102cf0ae2d7e9e42cbd3c8d9d51ebbb36bc30a3d710ccaee4a7443201cd348c2c6cfd29000f2350e9da5433b6cee557466fb44f7e9a2a363bb1c5f3dc0ee5e535a01a06ea2a6c3b3d3c480a3a1c6c1a5d9c505a23f7856f26a1959b849e9be7c3fad11a30fb0c9212c3f759ddf30c954529d78d9626c195017834e2ca2573b2d0a718a644ecb00cf36f2210699aac08d112ccbf81a6ce407e33dd8a92953a06d0e08eab328fbd28b823b045b2f4bc0499b024a753573815a3b28b487b61d86fc2e3e8b23d204b75e1f3535d1900490365e13767cc43baa87b89be045f08aa382c4e89c08d3d9a67ba3c36e84dd6203c09de9d174ddfee067794bdbe0731894f554bba42353933e08cec6f74ab8972fc6ef530d59ef317172f4787abab5056b3daf23bfa4f875989a6fdf4e6196bd5fafbf1f3722695e7d8189fad26da6fd4f1c47df206944598e48c05ebbcd6ddaee098d978f78a42b70e063f235fc655e5d153c1da6c7f66df961b49ce59ad3dd2a0e0f40355298c4790947614cff8eb1c5bad64fad2b9e454b30ada5b060cb95141950a525b0faab5346173bdeafd5a3530b86756a6cab547af1daad39ee634afe7faeb6fcd7a9c42b3cd26533d615982e7510431ac4147c67641995ae639a0be2e8b8549589d03d1f6e11d0d0c0e6f01d2b2a59474d170c2ce9064f740c32e14a4f6ad463568b746469cf5d59810cd8c3d246b8742b0147def463da94272ea2be0cf489ef447a095ab91f90937b67ca9b266f68a246ac2c7dde591d6c1991416a5169318a22fe0f193f1ad27e3f7a4d478a7cdf13b3401c7371c769983d49730b3a55889f752096da8dc330ca7135f1bf76a48b91f198fb90ff686d9b18d2e76ecbaf1e46c392dcf5edcf1e07c0f84e2ad24b89a5f686736fc4d39026de90960ca30999b5576805f31694db77e7551bac5d081e93091955f9ed18fc7a8bb21ca4febd58dc9718191e219d963304a41329540779550c0601523ea6c659c744ff1fc6a3ed1588e4e7038681a4c6fa8202e2021e5e58a678c23922d1930e8a4e31cd12d963c7ee73e3118cea8bbc7594a09ba1372bc2812207d789161becb08cdc328842ba3370b636b073918d16c8251db7edab60e9ef80ebd5c511561399ca991e5af27b94be20ce33f4408e90580ef440920c53657f1e614d360e1f02ac54f59e211ed2670d88975365af607eed40e781ada3a35ebc8fbb4f2aa8969c2cdf505509c98225eb3ae4743b245399c0dadfbf4c9dc6e89e31a129be3e10f27341d7fa9ff1a6850f9db0d0dafd5f7540d1548a413abe85c07a8b992eeff3eea733f4b6ba5e21a46951fa6bceb51936a36b1e89a36d4f040b407c4d8355f9cd0379dbb58b5cb8e424d730be62051d973a4914e71a178fd9a42488507788930f3a3e74f1e43f7bbca8d3077cea81a974658baf61e532cc03bd62b7a32d415b81fe4c0c610566cb08e55cadf0cefb14a2b07b9e3e7ba9f31712e9d5e16f6a7f7c2a86a3c0962905c78fbe1ce25f873ee39eb7d87bddccb0376ac9b79362378f61d2ba0cc53f10238a659a545aea55f5912ed196d8280c268d01c673dc9a675551048a8dc25f946987fbca24b7fecefe50e065c03428768a23340c918cbb72fe2c7663c57eb106dd12c4a07d4f6d7067a9e1c16a28525e270d57d00a1d96f4513afd078824aa4a5c811eb9469d06df8c3b5b8899d4b8f27d4312338c3826af09b3eb544ba32cd40f757c951e354cddba468e11df892fd9f8848b623e158be8292f11c8c1176e12fa27ad3267db02785c205c1e021da9e3d2cc1ffd6687b289ddfa39289fc26765939aeb0680b135e858ef23e553ca30e381e55702a4223a40d17dbd6289c42d4071b3a802a4fedb1ce745698884cd62feb58c7ffd88df84d713aca2280b85dadc590043671814f405b1d684794bbbe9e16b4a4c5122aed0cafc9ef3d8b36095cafab9335f2afeb7a890bcc881f5208d1fc47027241eac377433d02db7cf4f63ebe028d12d3fc4afd065d3606b95695b132d44181f56f14a8c976a325b9918fb0cd2d582bbbd90ebbb2ea68e46f8b603766086146759a01fd1b52acf4239a6773e69e0facca6f9beddfb45366d94e7c940aae37d9faf3ce6031df64a4407b09ba0f05d31ef36d37aaee063c226d05533eeac03059c6fc86dca3e05e820d1f823af2e3f743a42e74d0242784db49b9eef105a686a45ca48ad548e032e25ba628118f68ff3ff492ff6020104a1036a93d6855b3050ba9a0e6512d7f34fbee968193b47a7de584f25272fcab5a0d6d5bd1d8db5b5bb9dbf007312adcdc55594f1f52baa57c2b0820b08e4124c3bd13b33a91ae21f0df24fc13fc3463fc8b5eabdb022f9571be94df6de52ca94640a2308440886089e2765b77be3b0a176afbabb9bbbe7243323c781649d6dfbc89b4f9fdc46e7e4365ac3dc72e0e9520564be9c12874ee736b9c949afcb20495541fafae3cc0f3def3e7232a7dbe901c28b3de8ffc123c72d4803b8b90c921c732228628a9c94db867022c20927e593227018e2288ce75dc76d40a8e76c10f7285bb74759ce5f6e6f50e353fadec7c92f44210179cfd920f05b0584facd06a1be9b6abffcb0e57d1c431991f4b9142cd7a08c7992fb3c195bca6ea52bc51b7281aa8d44feea088251f59ee6538f7a2a40aaefbeeb3ef540dda76c1015fe200bd43dca0671e77df7443cebe22742635d9c7a22dea7ec122a40aa4ffd129af79e48ea57bf7a8ea168defb1368622eee04558fdd2962e9f227e41b92c44cddd653053c88e1a1828db6d1c01cb144115960d0c10d564c2ba629aad478203921c3131d78c151c41849e0ef6febe2fe46965346203c98a2931c60aa3fa33a6eab717dae8b7e24c1e5ef3e312e3fc7f49b5f3d8175f839c75dc041c50994e8c22486c73d2edf39ec619e9f1de6d979e6797f318f2a4961d424161e564c1b95de2ba616d34c06d0a4fa5377f759bb96739c0de5e7491e3c0cbab3bbccb0778d10ca3bc6073c54168e286aab0a51c31d2e465414f183e3b8bc04965918965e1b1ca3868cf4e342577acf17f51fb7f974e92a1e5554fe2ac8d41eb63b3c902a7f65b14fbfbb33eaeb3ee99b5be94835f8467327dc4340ab2ba623c8ac989e682f3d7ae038efb8ee4b711b9d72092462b80e061495ffa71d041e0834b0a83d7687870a2a57ae3b75092d4597777880a961aa0694f7ec91d0493aa394d4ff7408a98ff39715d260076378961db5f55c74e7ee6a624321a594746e939b93eb8e93296fcceeee5488fa312827f711c1458c189d9de3267fbab36a4d92e2756d713bb5ee80d03b67aaf4cb4112ee92bfe73227edececc814705c8e6ed87a024467c31053646d08d00910f8cb4a433cb9db65a5215c602ab3d1daa8487e7777b7d7498e67d1bc61fe307b983aa4e086497a417834a3a41ac74920d7f33b9cc4cdf37f3657ba4bf7f849b9f1e93edd6f966c4856eda666e37f3abe94d67b7148832d02abe692f5e58b64d5fcc67bec86cbb29270fea725eb0b0b994bce73f3fd735ef1440a315100937a9292ee09456ebe06f090c9147a376425cd324947a830b59d245451c84a421da1689b0d5146f78955adc1c6b16a82099b6ac4da52e37ab4864da6068360d8bca20c3dba5d840a133de8dfd901c1ff224771616ef396af7a8ef3451c1b3f2938eccd52ad8955149735c4dac21a620db18c583356118b8895c41786a196fa66e9464c1f60e86338df7f53631f1ba8296ad8fc8d8c7de6b35644ac304bce73e3fb594ccef3857dacbad0a8596d9b8defc63f6425452938f666c95ffda8296ad8f026cc452d35ece68b8d0d6f98587123631d960d6d3cdaed777d2c26d692bfc234d8aca65597e883f5c5ce5811d5d8d086a86d6863235b195d56d26ac64a5a15dd6635b1f272fb3d5692f3b4be9d95c4c558b59b56cbb2925849374c378a50c51837ccbd585526cc1887440d5147b3a31e6646b3d96c368dbe19ea08858a1d3bc6168a6d1fc7389781c6a3c9aac518232ba9417f0f8a785748717564a686a8d9d19dc910b76fffefbbb9a9891fbe2e8eebb5ca84199b43fe5a1dcd9510bc3a629ff9ad2fb7b9dcee8eb8fdfcd371b37387207e8f9850b42a412277379c330e55a5c1c8527a514d88b1e70f45ebc17928fbf47fdfcdcd6c168db69ec5f828215047a82f282ea8236efca616c5b48181a2ddf893a801d18bdadfccce1165a403f374fce2afa9b9b9e10fbfcbc50d5f973f7cdde63b6794da59c4458d1ff9a6c686dd8dded3dd368a66c5e31c4fca54cadd3d544bd4f7b33d123d983fff06fb449b8475e6f760a91f6f60d81f711e0ef5f3a3933894faf991a861aa9ffef363110aec13697e7e0598c6d6596b5db55a51dac0bf0845f545a2f8243a8932e739d2d9b0c7bc81c18d13a148004f553a5d019051e5fbcfb88e76658ccf31472666b20200a3865d547400a20a7611c7d5a62e621f9a58b9568d4cfe92b28b2abf8b542fbf08e83c7d027e31cf11254023fe4acb87d64dad71c2a9554ff5758c9364d4e94305ee72ecc359018c51fdbbc6451ffe9129fa70eb4dd103f9f2630cd887b3ae05eb789f807968bc6c2957b60f57f695d5876dc495adc495527a93f3d07899f250369f9cf94b3eeb9334ac37f94bbeb45c8dadb5b5b252aaac2c12000daa7f7f18694d57f2472210fc6fb538293b8f0c8f7386702770df7593b9d6fade5f5106ad21fc8a1fad6c59550338e6a4b3ec4e5d78dc53a7016ad71fefbbdbb61934ab5a43b7774a39c97deed3e746e3f48fbbaa8fddfb501f0b9d10d71e460bb03c0d5184c39b072b49494c8c85c4424a2c66ddaceb665aaa965ab580a10186060d303d6c3d6c5b0f33fadb6f1cd356a51f7bfc155fb6941e00a755414add9097bca5f78f90da10202e37524ba53fbae6e4a056c4c80ad1c9931bb9169f5099f96025fa8043bc8931c69fd2e387d0b019373e90bb3b90aff7bb5ca1dd108c334ae58b08be88808692dd6b80db2f7e355763f15905c62f865a39afc70ee8812aad1417d0fc10bc41f497b4309f5a17916d09fdcd52e1a81a322df7e0de520710c6e470b585f3299662d410ecbe72abf3c8761e98625073c01ebe835359f50335e4a51c9852d074e5f5569c4a4c65b542255392893e6a931a725294dc375598acede9c7ca3d5f5a5b9d0af36e3605fadc141b5c48e684a6a85a6076fdbd9734cb386e0f4e97958c9cee002e2b1961b9376c6e2d410d513526a81afbc4f7483f443159d558c73f9c4c5632d641f9d8505935f691cffd385767d77fb821aab6fa72fdb72f497426d7c3590b5fc63e01de23631b352c5ccd29d3c884158d468b6d3d1c6eb8e9883a421d39cf84825ffe55f8cce2fae42272e48e2664e1ce0cd8247145bb9161f1d49a375c67fd8083cba4701631617469ae24f1a6e317959671936c96b5a2dfaa4a83fe92a6a9d2b21043d344d3b4a215c919d47045e4b3e5444473a815dd0a2e386846349858c78d9c87a60cbffcbba0d18871747dc5e4fa37d1e050c3154dd375d415d6f19fae399d674e9a8769182b9561f21c6e98721a26325b9cccd00d67971a3e65441d352c5cd1901a16a2ae606918cde23a991ba63c7efd57d8a79b35a38ca4241acdca1337e430a89a9716b47094a38aa51b3219ec5d8750d1c08a0ae583175730a5aea2151d8f7378005ccad462f4856242a28f78e937bf5593ba626aea5162e8c72749141ad6354320a0a461ac9444d18d4c46129319e91d73dcf8f1c6efb18384c50d53636ef8e373441f6e5f8afa3704c1f85c0423926c8eb631dd2dc64823b673185e6226769a52a5631ead056626d29513ac685794ae3ca111c588866482133150d49036591aa6c16d8931626e2c525969ccd1ed17410479b732573648e30e611dffa90ae25da663725c301b536b4a1a530bd3e494e444a5490d6e3c31d6c573dfd2c36908a606ccd85634631c3c51435a6b42399d798f55415097552238eabd38825fd4bb6a84ee46b94698d77fd22e1f4ddab622e7e198b66ddba06cdbb66ddb469fb6c89286881a16d23177a34f4fa0dcedfb511ff7744b7f263811b33dedd2305a5b1ad3b0eeb7a751a851c3386a53f0a71d398ff7db53a486753642f1b6975f94d1595083dbfff456c4c1520de92c16cdeef6217dbadbd3a78635190df3cd492c52b24283dbfb17d460f418db4636a628b6f3197e8a5094ac1014657128426115109c26aaabc631eb70dd37bd685ca0f4779fcf18e73c4bbc4f3d8ddd64fedab8671590f729cbb4d6e0f6de479ffc85e250a86ece80a0a13a8ed268137da235eea3ef7df329ad614dd40c7dda64b4067402041ae6bf7d11148e348c6edbcfafe5afedb79beb35d7bfb24fbc6e7b785f4d0f6a9bd5e0f6db47578cb2ede38cef36efa6b425cc46c50d535b0d883136206a378c58eea6da6e1893ee168bed5b51c3c29451c3b61edb155bd1ddb629778bc10db72a77ebd130face337ffb6d7b56c33626d9392a40424c114345e070b3492e6ede73cac9429285ee6ed235d190d12101050caa05952ee6f4ec68e1a2f51cce65252d4d6849e2685aa6b0162c1d5b38e208094b940d090b1145444ce93c7669d0dffe50632da995a4a4e393eb50a8d73526b4c07359c908a7db7d38471a9cc14542d83aa2a441ff0f68a8f18cfda2c6a7e8d4a07750821a3f8c66bca96193d672c271a6a63e2754e6a77e8523a9ef6fd7759d9279b7d088dc6c28eff628a4db7d7f6194353d313935755f5bfaf03dcf8642b4db7dd8d3366481e976dfffe3fdd1df6677aa80d0b64ae69d5b6adacad15ca9f169aad4d842d4505e97754c62fce9d10dc51365dba4d69b5dee7caeeecc990da167444aa2863db29e1f53c6d8deb7ea2784a8fad9668c14656634338a520b7b7a8c8c66b32833a3591423b061b5d503758337df6379c32337857fe6121ab4407c6e4cb7a7fcdbf723a4c12ebeb794524a2408f0cf023deef6fef5b8326e51fec7cffbed0b81ae911e17f55205c443bd8732d2e3364c0a69b0fb7ebe101af46efcc0a186dd951fcbb8250c497d4d52affa23370524d1e92a89570a0752a9dfb6944dbdfca4ecc6121a9cf684e82a20f1b7a8da6c2462525371369fabe012332c72e9ab18f037ca38c555f1bd148312889152534a29a5941d55f259e5c10c363be917fd8c4550e26cfe94a1a45f2c226a7012c56dfbdb91a86133ce8fb2228f426c8ab99c74c3e8e4cad87c21254c1a0b0ccffb240e1a54666729a3bbd330eb44291ee7782ba9221d715cadadd6a3dc3d46971f51326ea84f7d38be305e1cdbf6010dd50be3d5f139787c4d787c8f1f725f37aae0570a07783cf7f88284623c78581b72377640769d478ee7f1b99023478e2f569956878d474f1aec48e4ea52bbdf9155ead9d0482a954a4d4f01f59bc731c618a35fb761d051838da5c1a69163c61c83c43e5b1ae6763e81ffe2e003f0fd8e1cffd2e6594e1ea9119ddd904eb9ed44a3d0a7469cf5277fe978cf3f6af4d15991dbc1eb78a902a2c3292c766091846e8c3d46feea1e3c7e033d74e8e0f1aec30ee9619f1829c6c3ca223ae5b211542ee3a041f5f762ece82ee78dd67558af0651e37b1d7cfc6c74605dc852280d4627f6893e931a7c59d086ad00580e6c5877d8cf86dcdbb02feb73d8b0af8dfcc97a9b682712eb748e1d2635c744caf14da779e6ff43ffe9d4a0530a0ef674a2b3868574ca94863de5f8e934cf34ecfb1cd626e16efcffd5ff47e3a9ff549e4cfd17f6d16da90a42bf70614c165f9880cc17454851e6fe57f3f1fb3b5541a8920c5a5871fda34a04111440bf70610c99ebf6082c88ae8e8f8f7862e6c69fd6f1a170e591c482e43c3c3a6cddd68dd68d0fb9dbbaf1ad1c1b7200b0d6de7cd84739fc699ef97ca8f3c94e270fa3865d54e4fa76fddb90d3615d1fad3f85b98df3dc3838ef5f089f907ee5b03f38c250def8c29c7be32d7faaf125894e49e28c2fccb9e144f2253feaa37ebafdaa3e8a4429d4179fdc7e2e34c2351a8b71c84b1df65cf6624576fbb68c75b3185ddc0e239490976e942c64dc304e5132624a4f4f4f4f9be124246cb8618bd25efd31754454efb252162f9baca22e2b65d9d242962bd2cb5684141f90639268114cc0e05834cd312ef4182759841243110b237f315704163245fe620e849e1fde0b71d2e0b6c0b8bf614485854672388f4b13a5ed8a2645a0a0891037e49668f2c30d9a4ca97159c9033d70351a781ec74d4abbbbdd36e5660a2eb7fc07a78c1d3d32f3ff7fc3fe5f9220ce6ee7c278db3233cf1b043f43b6108494d0b0f92740603e5d4ac63c132b75e292ef2f147349eb6a40bf3f50bfdb202af2831c08f5fd285bb932ca0ac5e2cbe69967fa459fc59a4e3ebc54d039ae7fd8875efa4268d3a5ef3cfe94babf021043adfea2743af98bbad3a14bfda5b8a5327f514a29a59c8be3706442808a187172a9d3a594525aa9ac6161ce3c43dfe9727143eae4d29c86f5d36f390fe5ba3003ca09c47dbac748632839ceb9568373cecf81a5522faa82208965a05e7e42dd573dd4577323e7bf759603f2a968b906a795f6bff52d9c6f7f0e866a6b83bdd35df1bccf8165c852971f9a56f3e58704c87c8e93614d5b56ba0287cb4a3188a2140318d86e6558aeb3d4a71e506d84250ce578638517fd6563eeee66ee19bb9bebdfedb465ee39ee74b39219b63a9b6401fd409c0bc29beb966a5df4a38bbedcbe21ea90f8d325b5614d71c8f11703578b1d15d40e5644e1640512655851a5c585921539d440b3020450889698bc289a315d218210644862aa8d200422598b8b32350097953cd0c20d1909457ba1cc86c50b60a8bcb0850b24665749dfafddc9f6e8ef5177204338a791e2401d0b5ad2dd60449ba51a7fce3967ff4f6f94a31da55b4f4739e9c6c939e7e4e426a99453ba803ae6df31b7414b626dd95f2c746f61f4b0276d5648c76ca0e0b8e9c80d916888da2350d428847584b89cd2850811f2ecc39b1231723b1edc69b57429a50f492b1edbb98d76bb945c0eecee83dce84643e0524af9453a36119d98fdc6cb1b84c795f65950e33333b781fe5ce9d71be5a2d334c384a405494b181bb5ebee34d6727d29d2884e701ed47b34baeeb5b65aff2078435efa711eeb6a40c736c9453dc186890d020d7a91136850c348a33917ddaea006d58f8042ce299554315693500912631a600242c5508030f8c0f5266020a68a20984060e34888fac49404adf3a823e6764b2266e05c0e15475752115541644f22f681d230ae614713cb449a5bd867fbf8138b2a754a941b7fe2d03c7f8011dcf85306f6f1237ae082b9c474e2c649c58d9686fae24d75dd1719ecbe5523c8ebd9b003f33def3a5942bffa8e090326e0003f5eea0fd2ff50ffee94949ee10e005c086579011143b4600c2d2a98c2c9183d7071664b193a70c142f3821d4f8e58925dc020bd890b64702f382980d00e2d28638316803082e249cb8b215a70620b2dc884e18211b7c5055aca4940a7f4d6944d943a70876bec344424e607f9382889b1c289d1e1769795c414a59ea0c2488b96f829e61f37547fd9aa20dd755705e1542274acb35de70655416a10bffe1ef58e9452a654a268fc7f3a480e1e6adf29628668636c368a6aae9b43254d4183779cd21421f0008cfb108424c00c8d8b420a1b6c1b4afe8420a48496115e709c0403c6086a60cc8020015b38ed80d0f3a365040593aaac9e1d10a899178e6ec8b58cd0c163256a6cb538ee5fc74b29a58c524a295b4a29999999dda50d3b04a83e523fd454833b3b3b671ad630ca71ade7c70e08608fcf69e1842cfe72a50fb7550c4aaee9ecd7a9bed3009d7272d167cfc1a02ec5218f915da073439ceb51d0aebb4761e6ba143f5c1ea118d72da892979e649c0076e80f6e985a1262e906d504666ebf6ca11eaa209a2951650a92140d012f9298fd70e603664410eb967406fd2174778d2622144720449a4c268361082f5d77d8e3767bb9216f690ed3c4076e7f3fd744edf6d7561351dcfe0fb3c3ed07c37071fb77acdcfe1e27aab8fd42a8dcfe210d63a5279e2e2fdd2660c4b01218302e2b813173f959041a17755341e24512cb78210e4931837ecee960891a2a99b3bf78bdfe695ddcd703d74d027f0f08413c11e356630293317c638c9cebc64baf54dd7fa23a9606a9233548fd8875a89cf98bd2b0a2ca99f3748ca22e4d22efc64c38358c8c1e6a2845b565605e7067a4c01c5dd5a579eed952474894ca2025831bb2ccdc1027c6185b382740a008051a368f18699036f54b0bf6012336451f4ae2059887f5343604771efc1ad69bb7f9307e51031b1f76934bc386e1d2ef115c1adf36dc59c1796e6ca4b56cf8362cfb2b456d6a6c08d2b0a1ce657dd14cbfe8479a6d32127dc43216609fb805ebd0a7ad43a8536dd8385cfa2b1b825465c37ed26a6a43ce9bb18975b61a135a082399db50380dd2ff69b97df2721fd7a8e16293f3b49cfb4c60128661f237f9a15fce4a06b767700bba1c67bf41da6a9089181b4afad14c83d446a7cec626795df4364533cde334994c56821855825fcc04bff892f0050a26e10b0c18e41bdab8dbe6ba396aa8b12fa5b2b62a81c7396145bafc2eefe3f57e341869d498d0c29306f9368965c4a8f4cfcb5f564082eb75eacb0a60b8ac4212bfa8a08cdb359ad4300e19f5300fad9ad8e700973fcef8397e1157103589ec6088164e6d757054f9f9bac0dba341fe7a005d355420d5f30b29816f8c32a4612aeb4a7dbc291b67277c414422c6dc38336a90bffb6294297116639c31cff8c5cf1d6d5839876d0a95267260789c53c33fb7d43fe2aff933e54aa56aecfc10321f684e10ee9c4bb154ecb0d631773eab617cc59d9f33bbbb57c07952fbe3bf2bf53de5863de5aa9e0ee1e552d5e4407ecd9fa86e6e934e228a82aed5baa3dc5163eab818c6be7dfb3fb24fdb8089a523868ec08146e7430ba49610d4c09331239f029c96f3fc6c91c344c2871570ddeab29297315ec45ce1a50bd292136a8930463421e5e82266c988198ed2982cb22c53ec0bccd096a60c355cbea8a165bc26f933f2d7629dd8de2cb490ec58c663e671fa7147ca2f43b798628a2b59c088114317ba050352a065042d20b1021c6a90818aa0a30b3d28c8a2b9cb4a5c9846d07a9c249c54a1a149110f35a8c2636860058b32b21966a075b13d512d281659b8d8c0458a0f28818403c81445a1e28726c260a28c46c2044c686819275a5d74cc840b588481c34c89329c5ec8b285062292a2d8528218cc2c50b4a946a5e0707842d229bd84d6c6c5082e555a5dd18cf1a79c5a393b3f682e6ba23a2951831b824a20dd10841b332c61e6054eb8400397253ac8820b148cf1928451162cf3081b67b03411c1161f12c892e50b185441a6c6041216e400035217677ce0018b0c282c2550d12f6670a5858a214a29a59452f24b29a594b239166129a59c62c5491047390011821810bf1757b297a51aaee42f9878310a41142c00810762524a296b7c01e60832375892c238c3c5c8260b96198e3245649951d352c4cae208285653906a600db162e6170254849999617c250f2dccdc6e8e4aedd836682166d24d725b86b618491e93028f2750b9f6c419a78671cd092cd7b9f644d2f5a766e899ebbfcd244b4b44f5fdaaef8ff1696c5090d09121457e9898b86683a84869816c6abccd0bc54276fad573acbf7e9a27127f6597ac3eae6cfc252e196e328ed5fc8ebffc3f88cad350a936b6ae6c10cdd3588e85dfcfb1d54b9a27529fc6dafc929aaf4fc466498db7798e853b0f583dcdcb9acf25adcdc7fafad19811692dd98d0d8a3322da4c66e3a34dfe7a9245a2d6ece65d2f14bbf11ca3f161a4798dcdd7bccd07b90d1b1499bec6065101aaf91a3628d6fce53636282822d56a906a6a3562fd303addfe5cf355df1ffb5dd312517d7c1791ee7fdac534c5943360cc683c15a0199ffa54ea550f947a55aa67a9a761833a657b46d4b2d4ccc6bb6cc60c16130d1b36c8c6ab7e860da20234e35536c8866d272b63b22fc4188d7672dda035cf4c35d635c376aaf11cab3183c6872eabf14154805a9f1a4240ccc68d0dba7995ca06ddd8ae510bea967d21c66c58da64f361d36cac6bbe500ca95ad7b4439c08c5e81315209b57bdca065121bafe2a1a90ea6bd82027f297cd8cf5d126ff195fcdf7e30455f5a113a9ec122a40f555b325d5ba805634afb22e22aba7f9950da28274fd89d0a71ab63081099264b29823f9cb3f68a863f559984ff32bff1f3a548ed56a488e05062b5ebc8c91c996b0606529eb9a76042993327ff9bb5c43ea4ba9d699520f82918c552dac08330343a34d4452b6b22ea0d4af3ef52bebda9e63a124baeeaa776d76086df2973f7572a7ebff62d4a749ce439b6813a549d78da0e2aa3ea447d725ad3ca248d7e527679fa449544cce3696d0ec56141ee7c4bcf861bdf3bb6f0b6a750532848bf3338242e2d8c6316e58ac490c78cf492d33a06df817f5294ccb75a36ccb561e7a768bd5f2eb9cf58324aaf431dca5d30d0a2959b8f014c5165562e030ceec30e4c529872a64b458ea7898d13f48199262036d8627b62d335041bb087b42b0a6050f44435900b14319156cb48b323ff01622326a52443943c6918ecb4a65606822a3ca1435a6aba46f0e313cfeb9e29cb915aef3c7ee6ee79c27f2abd340d791e7f5561be7f6064d66905842c20994362e6761e6329dde61b707f90dfb312074e3cbf9575ef95288c62682f666af7357ba46e064b72fcb6e7c067037f67c6eef8bdf7cf948b64b73285193448b64e3b7dc955605effa7ba194510a6612a3649d792309e9c909c4e79c4aa612ef3e236d5030a7eceb72aa80c8a92a41a8bbd3ba4610baad1aa15dfefcfcb24efc27db6a4daebbbb6503e13dc3a4bbe49c1b2fab3ce7fe747422b9f873ba9c72cad864b8f66ca254124466ce83d0acae9c739b91759a3e4c72a5656732b7cab5b6eda75b33de1c101ac656e27c0ff71f3faacb193c66668e53b04e24418d2a6a7cc9decd44c4f96da73b50832d19d4f82d316a7c3335fa0e1ef5c83ad2a5949267e3a4f3746ff79eb39d9321c71725b252da2a53d929e539ffa7552508dd905ee61c9f71e3f5d8443633b38f0737ac53ea8c1b7297797a2693c96688b90ffc621f24205d610ff8928024855f7c79c6fda2c7968612ee2e5dbafb94d291f8299f3aff5f67f7e7568be3a6fcfede305da8fee1fff5778e79a494538251762af497dfc489ec6e29653fa5d2298d1c0cf1caf76f5939fe66638cf1abf2a59472d2ef183d88fc3a6ac32ad31a575a25f3a9305d7ed944953f656824b6e2cb6b2432b5149e73df6da10afe9c12b74fa5fe949ac49debfe6f6755f0245189ffb42a70977d92748f440afdf9ace3bdea1bfa5893d5d37ca4a15f381fe5de1dc6f93e91045dea1a81feca8a990b76b132e64a8944cad0c7906040a87b1b2a77b142c6e52e41205df939200fc72467a5a8de55403a2b85e6575fcb5f72a6ba0f090684ae149a577d4d68b66715cdb77d9f03fa4bceb9cdcfb5cd39a7cfeea9b771db46376e9bcfaa05743605fadc9d2320d9a60ac9763110df7fc68fc2e37cfb64abc571ef3978de0e78a5949272b5e5eeee5050882af9ae0ad2b7633f735038b282f378511687765c8e6ec31e71fbd5c7314e54b53848523d40f59e65c10ea562aa1f9231279107d07dea8bb15601c998cabed06415f3be631dfd38669907c0d9d8bd962c5a620bf19e9e9d1d10f4d675ce88bb8dc047a06c27c4fd3868e80d061ed730e6daed1bb5d24a18bc1b59ec4c39a5bb0dbbc3c87e7e7e42681e14ffa03e0a1431a226d1fa38f0b2512fccc4e4b15d4eba711dca4ba96856b566060d968d1402dcf80f0071283e6998eb1907478e671dcf37cf1c4c019f5fcf1d3cf378ee9193d4857d7408b0c37edcf1e08367017c1f833b3ee7c3f145e0da1c1d8bc33a3b704ca0c7d76270c7b30a080e0578ecc8d981c2b59565e4f5a5ea11f0e3524a02f0490677e47077c70a1cecc8d991633f3e00ecf737e68d1b371feb060a77d639e7bcb9716327013e56bf5c635af28301ec781082d4e91a1f90ee7c7ae5d2e604f54dbf7b8e49da3d11ffaefb25a877cb4ea45bd2dfd9ca44b8253e871a9ca82f003154cfba80baefefbe3f045daff92ed710d49bc03122525036cefc35e7ff883e86c09dc99cbffc35dfbf4d6e1bc5419b0fb485a8372a23d19c33752792f7a4c2e39c90ce2efde2e51ac6bafc1fdc20a5949cfc204aa08da8842df645412b15d140000000f314002028100a0744e270382c18d38555ee0114800b7aa43e7c521a8a83519002290c32c6184300210000008821646686a86aea94d47dd2fae5a8e099cf943e81ad240ecc4163d2344f0f046306fef3d1608dd823cb868a8290fc286eaa502260f09406f3ab1abd058440d9a7069da3a0584dbc7e686abea4f113a496410fddb61fa3782a58b03e2f7da43fdf3b57dca6068a9707d6837c1f6cdce8155a94643c0ca7cd03290436c7979301e5ce36afa5f52c6419e3d3c46268aa4ef76458b3543eb6b228929aee9ae0b74203bb249bbe34b6ed8f97375899121340e7ccd22bd19f7aa8cb1ebbb2601fedf8edd77862518004f0e2c7f445c610300a4d048ed39f57a688995aa95ab8fc4497bab8f8e41ef54dffb48a5a72fd443775bbf4c91df59bfa943587c4839c08f48f7888fdcd2547259ab61e647c10f07551423eccf206c8dd08ff7bd6970829baa670eb69193bb1e8034d1f5a388e5930691096880448c84a3b3e621ca4dc59d91b638a648bfee6d10e0223afabf301c81153740a65acc6008ecf8f1af1437ad66c85998448146ae69c82401467e8159ab7806c3d245643d14ace1910f55fd59e5e04aae920531b18bb1fb3ecce3476af67d89c0f8696223e544c72f6c8517caa669942e72dc46433821e6109050c00c21188f7ac128868d4237fc777dd8a1305a8ac64d8b0507a86ac03a0744e5a369445090ea0c5ebc14da40459733dec8330ddc17a534d8be74eff814c1d1caaa01ae565f4f0a7f5b1fad680edcbcff7e94d543e5d7c4a86b8ddf9868a813ff4d1722f944dfb3cf93d2369ac96936bc970121bb39f75a30e7f7f7c7496d550b10987c360ddd3561815cef0d6aa077947a0bb885d630d2e8a540c584ed79093d7a49ecf30b79cdc99a4bc09b2717e12caf9197796e6fd1bb2972724203d21a3810ad397ab9fa5a852f366ef0c751697022050a1a86af4a7c5f1769679089ee9b9e52bb4ad0814c798108af197116ccb9cc31b116b02828a49ce4a8fcd5badc5c5778b85c8f5a766d12901b7dd870fc4e5b2d5a97c943ee1f7848ea24b51d9a5cd54eb60419805162fc7efcb699708e4702ecafcdc37d801561028fb2e4d150f12f912a176edf853670a8a9d3ae46066ea493a8e97495af4680dd6cc15b0d50fe005bc691b6ce8797dbc493674b6ace87cb2609d0377656207d09e8af431b39d3f6522cfe241bc4a1494ded3a6bfcdd7f9e96fb221173b12e89d24397547b31c8a354fbf82eb657b4679ba9f5be9151928f5b9f6cc54a1d223f42c8dc968a8ece06fcd6222cf65ccaee3c50c79e427d642d4aa8b27ceac544d692020dbadcea71750ba5f2a1c56e131cf65d3483d15621346c04cdcf842526315c16c97e234a626c20ee58bae1444678d9eef05823b7ede794ff2b881c40c405e0335fb2644ea75e1e05558971d292addd997238fff2c7d93e8c3c3499ef09cbe97e81b85f199779a9ecd84ef48471019747420dfc33fba2a7a7a156eb7551f21645779d5b43b7760c2b0b0a7b69f57f478c020dd20d676fdd587acb8eb042554e75f7a2a301ed15e5037c71242f5d8a751bceabb809671905c096c01bdf4d414ddd46bce4bf7c1b31308c483b027d0bd14a1e0e7aa114ea44248564064e7c13443fe8919bd784934f2f46bb65024bd9731e390468d6c3e15c7b63212db9b77a9a682122ad297abb7f627613a0cd8952aa4edb0c3fe63e5298895502d33a1c2c9d927b66dfa0302640773d9129087938140351b01149f96346b16b8a6be26ab2d04c925512e60a14af220759505fde31cb602de28bb89113a3c305e19b2ebf9389cf678951a168072db716dc9e7e123d5d00cb96e36e92a1d5c4de61fa785dde20879eb4dec50f077edae3a549e1d18807a0ac1a918b4cf4e6fa463002f5dbee7bcd54e5361c5953c3861026d2651c8bf930619a91e75a2e27ae8be85f0309952632684ffed1b9433b3910e44b100d7539ac1c9fbe62cd0e3feef363ea94a856b31b37928de39f82aa73cae8a4e64e0c001e4ee13003bb8714e8ec63ae3ccb50ba2d1b0dbc0c9750a2a1e7d038a3c1be1ed2515ef923afd64c41e38b58491adf14bb011c2c902ce70ed4811b84fd0ccf1d4f59f910ef4dae2e905861026be460722160c069c57df130af97142738d5ac2e4eede3b27076227e075cd9c61011de89acf46bb35f175b7d0a1a1d388abf1f6e9e23d83baf8d9f93b88281213b74878651c261323e94ba04092a43cda5a69228d9ecc15245614b4a1f0bb0520b1fe291a56602e9dc105f9012ca3330607375fc4b87e389de43e82aaff2204b414781e3b18f9f37839afc6be38a9b40bdce9ddbce5c744bfa8f58e436ffc79c5d44eedfc4c4eb3e1794f43b498feef1978f05f641b3c55ef23b027708f44e16805dbe91421b678560d6af7e8105d94b473366ca88438d88308014691fcc0640eb5e2b535201b8002a874a5f35dc19fd8533449248381daaafb79768c3887277cdd5b48b80c815bd99390628a7a32e8fddd757013e20b41f90955a202ceb4377ff0a4ebad4b944ffef54bc9463981164387abec2eaecf13d2600d42a94d7ab996aae6beb320535115524f79cca6e67f15f4a31d9f58ad6b19db02bef1cef22d1dca3f8ed344eb5fcb0e5d116f5bde1efd71f2be2eb0260f2aabd3d336ff6a71f463db8674d66db9166dbed38593064516917ffa88f08aa92b13c57c7dd26326930fdce46ad88de073d16ed0c892e1ee3559c56a7ac857118fcbac0f732be80d6e83e8ae58dd9ed805b6791b5350cb16da2208e81cde836057988979013f05dfd5722ce9864255dd2a5a39dca8699cfd53c78a7f7a5845657b056aa758cba007856e4c10773fc87826b69aca46517c445e73bb29dd990ac280242552da38838564465899516142ddc2fe75ab4e1528222cdc26c89803c993ec47355ccaf549cc5e8194a1f34f47f5b5fb31fa7a1780d393e0eb60b0caed8209583a36bbd16303f00866838fa293706ce0bee804d8668a048f522190a656e875687b54846fd7a9018412dedb4263c4a744bbccc82d76fe64f0d5ddc3faf1c1fc64fd7010e0fc824afbd9f2cc12e1acd21d9a57ea6d213c4437fd1df94459192474d1c838402f7961590ea4e4522a17ddee2e32d4e57f1c3c3bf091dfd2137fcda3045d184513318242df6b86fa15fec3a7178d40affa0df67172aebe6f212745f80392dc2dd561f42463720bc41e762ad4356397fd325e28697ccde7be8c3f3b7bdba4cd1161edea1e54a007ed4803f1a529cc68c5e15bde83af5a0cb937381a80899d258d57d10b0765c51a8d2883b34bdb4b46791fe156eae50d4ec91a35eb288961a97d403174099b540631084301d985fed8272d78b1e62cb15591deafb421a04a79613754fcfc2ad891c04053222ba0a2a7b37bfe43612de9661ecc4146a6cdc8b1ffe3d5f7c74f15662a84ab199171b3394db1cbe43fe50e041576762f7374d4e7d32b517c5fdec3660956dbc02858303e46e93993ea643d640976f233ca883a28ecd693853c8399833b29beac8b0a8dbb941941e160fb965dba9abe889367a89a8db2a737fddcd4f27d7f29fb61c3ca65e2b112d9706129bb6e955cfeae1e09691585b5bb806ffc49541fe68c551c3f7075eed9d9769a1cf55ed1026e3e7366513eb4af38a52699b308063c6b12a625f39bb19fb54abfcb978a35b18341bb03de6e3e5cb2dbdca964e30def322204e93150c56d12c8589c80cce3dee1897305625f6804e1d08a09e2d59ab2870acd30aadc347b6dbd528b1a3ba5aaa7bb10cd6c9b577d8d51bade6e4d6f965b936fe2650a3e936ad46b5da377f39f1fe51e2b5cc467e2bb0b38fab15125611c100e27cb243c85cfc11186b99843fa0e348d6837360eaa1b50501f3f8986559f0b93e1fdbcb3a86fb6a5db4890a3bc012641d40ee6b5f35486583286ac5e935d009923d72fe8172d926c93588bc52c464befbb692864567b8e2f2257aa4424c438366170d8f97ad31be0eeb22f70e744b69e092bf895b1605db392cd467cbcc1726f9be57a84f41adc924a6881d678338da92c2234814799de58a6ac5dfb9d565a46f7b12d502538ef1d3f0a429214f2cb54b1afa7caa2c8fec7b528689d05a5337ca3260b4796082361f9718cf84ff08f39fc707a2ae0611b1ebbf4f95353f33c83318533178997cb13993d847e0ad03d2923b0a3c50cecd675d670f3d48524a5afa283bed121fd7e4215c224223923a57db48ca24f71331f4207301460ec8f6ded41648995c963f0777e4f3f8885a8e3c879cc2425c4f9b39608e690689c9e49b73d138fa3cc94a3f39f0f87d804b3559f54bb7538548f7793b7914bb71f1515f3aaf226ef1389abae77c51c8b5039d08034476b1d81eca3dbc4dbd428eec89526cf5c7c807618f0434b4ef33b6d61e71b5a50be58bbcd5a4290ab7482e853f71f46bb88cfa2aa8537e7ac573b1198b414f3fa5b6cc06386aaaf33f1759e441138bb751f2d449e66039fa20ee413dd88311917edd8f1881b9f81135494013fa1809626f4839ac4dd139ad1aef2be6b58c6e99dada7f28806880363f07126c69f806226fcc46e2d1e1c28afab304b238c7ae91264baabec92f1349da7628f7971de73f42403a24558744f24928a13887b434181f61132a108bae013eb94ec0293bca7e51740f3c74708d06daa8253b6de9dba1a73680a37800c1ac216ffc7a7658c4a51477940df17afdc02d44e9bdb7f0eab722c52e399955ad437a55cabcdb9c23379031ca5d7d76099c235d0f8904ba33ed833263861bd156c006614351f4b7efd23780ed91c6bfc2350d3b3ac712cb96867088dd4873ca14f94bcc258477f4bf91438e9c5dfcf50fe9d682471193fcf5375653b103864bc66a9d63d4ae32b1a9a2973e0b2821bcd90f1949e1c6c8c9aea734d5fe97e8ff3edafaf079d1308d4315e726c4f180093fb52d03c77dbaf29456e4e88efe601b03f9784dd8fdebf61fb03e2fa22843f3afbb89c534f01118286b8d0dfaf1406bfdc799a80151fce7c2f326c8b992555e0bb1e3acf73334e4f3cc979b92c3c5ba4b320ccb7830120dce811de956df279d96742781e1914ee7834a679eae77fa8760b96032907fd0df52f58aa9ca60bf21f35032c7282c84fadedc5cee3c60eb2146adf115f83fc5311360d43909ea39805c2faa1be68f7968b0149e194ff85930e61770a836686520bd74490e0a188b94801c395852a9a2059864e83b8fe7c24efaa9d89c16b01eb561cc9172347d20eebe5196ee208af783fd562801190e2088b680124e301ff0f044a847c5363957e2cc3225e90a1fe60edd9ff64411cffca52777fa241f5881d66101014d7fec3c962e032b7b51392624f8519785c2ea05259525c82867b997eb79735f7d81591b2666f81d057cee468b917ad18a6211f2f95b8a83546d4f2ff6428eb00f55988cf38d5156eb17c262f41f251cffec49497f5c0cd5e00e761d4af0b0fa210ad45019aabdddd6f7a705690ac473ee3983808c25d8a04f06d201b1727f7df89d9e19e1bf8d5793ac1f4876dddcc9d7c1f24d8d0d7f31aaad98dc375cf92ceec1b0f068d67f0f7f4bd98cd869fa1e0462bfaf63d95e87ee8d8e4c4e457e14407ff2855bcfec525a0d471fc8a1ed70a7ea08b31495dabf639ce301ce00317d4d90b28de7a11d29b26cdf344a7f168587c26645adc87d880a744db7e6a607cc1113911ed3e149364537c8207cd7730a1e16f609376d351e22527d725d5d0dd3c0996d7ad3ca6e65e2cca56aa6303d5ad6a80ba2a87c09d90f59e2230e16a6a5824f3f6f6c667728141004408c1696a6151f281cd57fe0de23d3edefb8abc0a5a3db4ef063afaafb2e8c8d79237713aad7a384779df890744ba74a31d6ee29dc16f7d1a5009eaa26ce472f1d9b61e5a28543dea3b8b6559570175cea124750b2c4db8a58810c904f5694ced386672e7fbe6b88e3bbf9119626f23a4374d855549e3e2ffab2034b7c3c7aa99ecb4a0714441d344533427ba2257a147d58f4ba52f6e18173d0eaf747975b00efeac29201dc2294b1b3a6fdd89f6e1f0a6021b2908f952dfd9f1268a404c6a3b868653e513aaa2398a0742691a6de68baafd1e4020f21da61fe1c517c5007d122af5a4caa4f080bf757a043874cad96f265a868147f09ea99aa154cefa0570aa44f58f5049d026aeeed2f1c8965d8be8546b2a60f94f0e384a7499cfb3264207a280c00b7685a73cc2fa27318217b556c873562754ee373157358f58706d2efd5b4d559eb0fd9a9e51f7cf334832c5297a2f8720f14f5c049f9d2fb839bc201678a4238e1ab89bafbf3316a12439d1a53ace70d108cfbe58aa375f75934bef183520c20ba41962647c029d84038de992a456534081aa0aff3f4b1b3aee59ae2c66c8344d35d3f0515239828b375444b9913630c87b958134bb01d9ec46691413876458daba8f2a9d16374583f4a7aa35dd175c3819d0b6ee80081244a0e1bbbe4ff59f8e044490511e6788740b34266fa644bbe13cc88a2d3ee906af4785aa408f2cac2d3a53d3a82e3d8888704187b0a02cc8e4a65be66e870c77aa1c2f3e558a417c78bbd72692d757a12a444eb4de02996813212baada11f70f23d9249893146b92b551079a529a1d6441b4f4a9da036006b9cacef9aaddfc835c4b976130c831f4d077e540b0799b56fe9784d89ecc458a3fad1070d5e14d68ba68bc2a3f806ff97ef8955d3f8e62cce36cec9bd9279755aa4fd0c8e322cb0e0d1ddb3a27180f6109ca4388ae93e8ad0f80f8ba48b5ed3e2cd4220bc15289b7d99bd709d3a56d1080ca87518001a0bb2160e90f97d528989462251fb3b1aef4cc5470258b3154256054d8265f540f20dcd7e73248eed4d7499b19357c1e1c42c86170488cdc0e3c185e0b235d489e0ade7ed1317c75f084d75a2906a77eab811851d7db4231603a0f1e5e772a03c81b3ccb8e164d63061034ef7d180b4f3f3509c61c17f54a4b264d8e57fd247922c9c36ce467d857636d31636552497eec03cdc5475ae726fa7f50de060452f78056e7143442027e7fc8d16cf90a6b07779ba69b09dd37c5378b232d64ebe5aeb3592b4a7734b1b5c8b8b96f55d1d1bb98337ed3283a45dc4c5a42607ab3597282cc6d592ed8a4910577451d5d735bf1050d050f01391c561de96f0649af623e7ad7422ec35a66d86fbff8d93100446db440511a67d2d2ac83a77625f9389619a5b5fd58972f026596f8e361f12ed6aac713ef03a25ea9cafbf884831b1bcc05ece55bd119474a447dcdf49d4439acd4de383713d19f97027b2037bc525cf57a01009a19f124737e4c40f608bf88bf05c8ae3aa16f02641e803df6f3ddf42b91327f5d1608248a71ac68a845efe44146cb8103954f56afa1233b532c91cf4fa6393080cc48a927a6991296b254f4e0c72386a6216edb70fe0d685cb2b1df0e59301231724a957e04423e902c604994c76f8ea9bf67a1b45088d08c41d2e5b061ef96a186c73b2509c139ae65ae42352b460b7682e22575d61c1abe2b47192c3922822ac61e5190137c72634f5202b0798c46e8a7e2388149896d8bd0d6e5bc92af0fd431118659e764d01f7b2d9caa56f3409163c8c638db03baaabccf916f8e64fb51ec68f3b33121e92f2eb92125ef602589906d4a6d7aa6d983842b150aa628c56ab31de0e45f07291215f529f93656d931dac0b264f4e176eba6cec7a9de7c62cdc5f607f699b08b5318ea97511e6ab08f3d186bbff7c93f49ea8702bef8d5c70bb4514700929eb0a2fd04af851ec253943a7148d70b7a95f3af845c9cb9b47f66edfe5d8c3fb11078c990e5f6da020d06e6368871b967614a65f17ffc9ecd539530be73190493f7855e1e4c5237b06ef7f864e5a11e9ec17c466365f55e2f90181b7f8f90f4fcede66d92a7777ed2881220e7b18e4f7c90c0380936e75a639e8b4148738178d2ab03c9dc45cd0a7cb0a0f00def92c0747bba81dd8eaef40f99c368aed49388e6b0de2eddb32b0d719902c2858c9a4d495799faa21cf6a98fd206826e5529dc20bf2735175ba76ca2872f3862eb4076d461fa75b35da3db82c119eed9c22225b1e708fea5015514b42fdc33394ff7323fabc11a0610669391a6e551a06cbba3b630d5009115ccf11555c34a243dca918095c31a2a1d7604a0828ac4625c401f0a9257eb1b9c182887929b55101d410e713578e5f04360bc94f2d74a9cc9b006881b8aaf27f32af9f37b6994381b8dcc65022e704ea32ddc3b5e935bb89c71759bda014c4fe18d83455ae24f2652ea4369db98fa04eeba90a977014c378743fc841388d9363e3e939adb5da8bd5acceea7dfceee511ebd99d83370f2112fa62195210fe6db117a7dc74b0251fde37d076dc9065847c14f750f17802e09e01d56ae3d0d25295526ed2fd635a57b9acf796b87c7ec381ac80382e48ecb83be2945393159410d25715c1194fd261672a472de8ec42321051dfbd0b1e6a8a351b973deef6e657f161a3d65abd4934cc712d21936e6f9ce4c15519377c1458e54cacc0e8aec57a45ead51971ddce39f4c02114d04dafcca41fe564007a94bfb50389185a44312a311d3c1597b2a86e1ffd1075e0e02350dc2cde6ebeb3a33b0b9695e8fa09681f3f6fcbd4a32adf33e066af02c9ecbbadfaf2690a4c55eb26e69632d5031feb26508bd2a8c3add6220e98c42d7d0ef65dcfe53949ad2f4df0f6d02afeda89b4609f5601091b034bbfe2c4ee8ce91ef1b1c791a1da62cdcf37ddfc59c136a052410f8a7fce1893edc13906e5552e997b8676e7ab8dc892700a524d4bebc02a567df803a4a649d425fc3d4fbcdfe078844a40799e14b003d898c3007acc12b50eaf077afe849ba4a56cd63d52e6903c0c39f253ee3bdf9be50426d86e29f7102d7d19a16ac8dab417bc38638b33665a13e9228aac333a456b04a5cfdd6cd7be5c55976b998bc5e428d545eb8ed6fb1844032673b14fc8f73cee5663c4ea1179c6d4803c71ea4536536c9f6ff6e5391022cee2a7a8a26e5f387fa3fe827d1d3746d7780983b45261e892544eca3375e74d21de1c97bde9516bc8fe469aff653ed71ececdc592a8e93d99b72489cc4c70c377d1b48ae8a17547497750a2664e8e557e18036e044640c981a5db251f08c8f5466a6bb5da0784f25a038e3bff2c709fc5e8558f5e390a0b21317762294f1be92ebadc641835121cec3270627dc093823ba4bff7093f6c80fd2d6e9158af6ae431293c7263dff462ae912eb96b1e8357946e3d61e2d9e6c1754b1a668e1bdbcb0a6543bfe0ff96a1e83b31b364b060007b5761cb94d1edbec1f640529e477564203f04829c10f10e3c011222dc36a71438567c2a80db08689c8352ad3352676438106d98799c8a731285c3cc4d93ffe963691b047aa692cad3fd3e15065f712f6527440939f42865cb71c2e23b46e7ff40b87e94c6a470326cb9723e62b78ec0d70cb67113bd2bddf80649378e005075a6ea3f95b578b08f6ae082d83672ecedd08ea848502ab31ae062e9b838ced42b3ec1e6111d964919dbe421b88586bcbe7e81885d8e9bab67d905b043f3ec6fa3fd762370a19df7a719aa72b444d31762931820a41df4b7d9f13c8ab8f30d1dd143b7737184a38f963a8b8caa2857756501a3a754944d76014e552a7e81dae2d6e0da48cbe28d8dff782bd35f7d128d2b88d412a4616273cdb487d778cc2af0c4cf1f042621a3ee96dce25a6574392b3205eedb79f3ed67c383d04d4aea079a9c1cbbd8e5133617279a909b327b84cd35785ac014c4e7ec09c3f6192387516828cc641f6a9090b9299335350241553b69e048b903af4e237c54ccccb17cb5234146462910047d48115e9d09cb480fb4088198a5b09aff4c83491fef6f4b19146590f2c2e7c46dee44ac269eda1cf67698dde7d87a240326dc173cccac1fcd14ecd1d20ec261100746e5431016fedc295b01170332f33ff75bf6b2f8bfc97339b39c6a7905de393f68aed4163927e1335ce3704639cf28162c29c03f923e3e76e8f059e2b1360e694be7c8a42178f7a9728f8ea345b1010f60d23b3805200678041660a6167aaba9980b37f811bd9e557293072d52a05eac5cddeeb33a73e556573c13d0aa1141855e3c0dcc3323c58ed3a297b5e825be0c7bd7960302313bb49a77a343b5c7a9b8ef006cf6e970c7f9e2d6f018f341df2df5f83ca7bb675e29fb5408964de566aba67884a80036f3c5849cfb66b82b3ae4cf6caba3ab80d530d4658badfc321061a550800fe46614a52b624c391466387274fdf2b93606575ec274092830a05a2c7670d4274b4a2666629a1fc7d7214dd2d4656250ce51201777f74a79571aaf0766bd98b29e1431f89429c0aaaa6194a78fd7812a4b3a4a12efc5a881d9f8284693636098cfcfee3213fec48332e03e6268c3737557a1c88ac1a921db06751b4bc13cc8d66ce16d164a4fbddf999b9676dcc6cfdabb513a5a1c91845af90436684e145f42fa66a557a01accae11b51abedb6554fe8c42601010de0aac36e1eff757fe3c982186fc6681abaf1a9e4bbe9a1a2b71bf98d6e4338a08280cd1914bfa65bb834c9c727dcd89f1d94800296766c4b22d2f9ab2a92759a4e76ab21ef895039aa29634cf70719f6db2e299684dad46b474b251773e0f2166d8902af7814582dcca9b396b6cee825e826c852c9051a2171727af956e55fad047707f262c2247967b33fe580d5b3a83fa376c56158c33a09382b7d7c5cb226b5b25a61b6c9989313094bb35956809afde13e4bc3d1cbf70eab1ed241b6199db50e3317708b912e32a6ed0c12509eb5d144f7fbb8cbdb5f74e13ebb94c49f971083cbb86c3013fd7c5e3d2cd33eb854cd1d9d5b10a7b764205f6f23b918ae24a6994efba53745b0e98bb2ed0067f260da39986b5bc2bc258a857b1c8b08e0e3c7805ab3bbad90c6e66c3ab4ae73707dbdb169434d8c977c2a7db55fabfa9b3c9f52df6a24cd907d63475bb5d5b68c1e90dee67ee22b40ef31262448cc3cd86d44f9b9107aa23de84ed8b44a641721f4c02338b557c98e382c162ff496b0da5381aedbdae30b7eed73f37f8148f6101dd0db1c2ec94b655eaf32938494b8c20795b87605b40f77771062faa07e27e1aacab378ea484c2bf7724625ae1417c6a1cd9f58059f019f544de79fa7fb34b14e56164522cbb8d6914b12c150eb3b56b2e3e878ba531bd43bcd4c712def31500cc4baaba323b42a25407b516279049c4206521d6973643aaa2bea9c1b42c26aa53d04a95ea5ddd46419da1ea9ce145800b4e34435c538b325f38e1373cf74d55f62ebcafce950db8cc77b4d7c2557e5e1ecc75c100813133dca67859c9aa3baf147dbe6e3473e02ffeaf5762f07ff009158f8ff39ed930faa48d5ef02d15f32291716ec0b7ebc55913a7648411a8cebfe32f33944306558a75284d72c5e2e91b089f6cc6f06d17c07696a3c058b666bf82cc8ad4718895021af5ad0400b7e04503f23556d6ad01768462796a743b246992bc82ee1cbaa0213610c95f1717d176547fa1e9917dce15385138f5ed50c1cd2aeac699e6503015eea3e43dd102e4ab9f825061ad8863024e81b352fcd59b2bba345fce50eda201a48936c245ec7d4b8b7e3b9508a33747146ecb780a9b777205830d48c4de859240f869e427d1613c23b5d08b7d0a5f26f61707c25f91d2537d247de848933b5fdba4a1b15561ef9fdca43aea7aa1190324c42462c4a4e40186c753db74b3e77d9a333cab29760102d9cfda420167eb8d8ee31baa23d607fb9f63cc018199a3332bedeafb5e9101ae75cc3a3c51bf53ef44ab83e050141c2e34ed72ebbf8d945aacea7e35372297b0b27a736ee2d6b7c5aff2e0f7cca34597d6ab5adf21d973bdabee624bc6d6b8c491dc1afddda9e04efa87e670dc27f946e15394100d4b3da557a0771c985e18e95d87c88bb5707e79adad539a46857e3f315850269e1c04787f2abe7fa4f2d3b6e06d3d242154181717c370c73e1c691722735d87cf39059ab33e54d601d95bcf09090fbee7b82c34dc3208a0fc90b3df0af9f83334e6802023fe47c4e102bb526ec74b5390f158e47e8ac34928998e84be853177ba4a2d981e54a6c89546fa9dc219d953ebbcf44a4f3c11278e83bf8a6a14c712a29e433de4ce0895aab8a18d3c9b269b3378379f8df6fe156cad15c92b490dd28d72623a88ce69a1a321847df4c9b41f86650a9ad241654cdf367336f198b1a13782e3add4d25ef13ae9c40c8cdd20815483367815aabfdc2953177f66ba5929c9923e95d98845736459afe9927d18fe1c09474a67e18e19c06ffdd299c44672d497855d42c649cc46f1b18bcae75116cc19be0dcb93515d7b1351a341fa862938e571eb34fe2d487a9fb38a8bd8d76095427624f9db1411e68d8044dc2b062072d44c903336fb608abe2e2f5a09a4b46f10d28815c7a85fcebde45a338f56993ee29289b2e7de1603c2877bf5c4c18fd37c64856f2a96c85053c68170f8c1dc9acfdaf89bc03c6531e5a42111a34999b2cf37ba9337596f17f0d809da36c6c4a8b76636966b19b855d5bba5d2404879a12c60b8eb79c65cdd530f29593d281288b1c5f7240f6b6635079ce662b62ce78d2379160574ea8e0a1c97bb4634bc4d0f207702777f5be6a7e0d065444e017422b08377f9a9f2cc5b1f5afc558b8cc5cf8c3b6f8b6d9fcc7babbb30f1b14072f2673d4a03334863ce61fb0415cfc0ecef85a6c4471d50946e8feacd4f7b4fc183182f9550f6c2db2b7228ee58658bb75de9ff9e8683a216639787b3d0837c6b2009e2ecd979037786b4534a13158f703df37dae8c55548813aedc793fbc6697d5650715ab995ce4072f1d232a4cb666d31936040f8c75ea43c432fd6584172ab8d18bcf65ebd544d555a2d43293b4a51490a5bd1a864dc984c352d1ed1535af31531a17b32c5150f1fef2a39140200e3accf74494762cb7554f2db117cf01b3af0b447c272718d9e250a831363e047be1b0e40093a047e19c1b4b19ff578d18bd2b7220ba80015b611f374dd878fe9e9782108f9308307b6120cdd5d32844ac9e9745335d521ab810308f3d0a724fdac104e82eee0b95a26ce14da64e105060a0eb9a2e3aaf94017e5c654783d932d67dc4920b41e046344077b60ea37822c524297fded04ed38e55477d3ce142e0e5a354f66fad9decd6b2051ae862d79671bf0c6012028cb53360b7fa9c8d0436667c077cb36ffa2113ada8657c7ada76cb93fa5a4e43c1108e622243c982ef80bb2d01c129318f639ddd95f6a172de4854bee38bb960379436d4bc2d8cd19f71d5c0afd6a2e32ebe5a4a7abf540b9d9841de064e233535314c52334d878e6b62d671cbd203e76c4de154b04f07746b9401a873434cafe8783afe036aea253e2b2566c2ba42ac4c192ec7e4c7b0ce3d42add17f6d4ec3688f5c8d4034054d4ab65bf23ee806c2fa5c3fcf3572409a54eac8d3e402c0f9c8c701257733c784142e4f0b4a8cca80a0656f813cd7bb839d6c2de483829140cb82cbd564f2e37448a00d5212a62b66b07ede928982f1c8979c8419ebafeb675e2f578289f2c6d37bf105336a66302a90f4777ecfa6b202d7b14129f7205216c7e42509eda5a87f3d4add07a259f9b35b85f88d5adb9f0d3dc8be4c586a6daf1026a327bdcea07430a495d7e743557f98fd686e47dcd6d63e32c94ca738db704f0116fdfebe4fc521ffa28589815c769766ea01a40f8c31fcba380b20a2a99b0647ec874f40fb5102225707cb44831ac97527ea29a67d76c9fe690c2b5b0d5fbb56918d8b7a271b3dceb78808906997a91f8b8744248bb743b6dcd8af150fede201cd9378b685f75679c9adc1afa96729dc80e61ce197bb3a1e172f17cf526da73c65edaef6c2f6cbc0e7914a1dd1acbf0772de8f286e0551bd23054ee1a288413042348ea378d055007d886e40798914704b29ddf5a61f29882c41436b20ce3527c1bef98c2f5296d83e8c4cd93ab2f42f10af66f27cfec2f0c11ab75146e5cc524487f2dfd0cf0ea04a9b570d34e4ade2608c142b73a1bd2e81e584003173ff0895f6aa99443e5fd1bb2570a3fb5b34162bd752dd3214263c99aeb5ad02bb94ac6cbf3f5dd1346cbc3b75af8ffd7e223ade405c0ca962b2d6d09fe7535a0b64e07b59e669143fbab9a7d01c22dbda90620ea51a346d0d83035e84ddf2ad3d3a258db952a56e59204e21005272e3d58ae9414695804a885e1c87018b72aef5cb0db4da19d1c8c8ee262879008a87b4b43080f46bbd5b554c92b6696583140ad2268812e07147630ab0003cc5badb2c4434b537c85a8473cd457b62fc47ab4a64dd1be42e81107e5af22fb02aa740542d3207ecc470091d31b143da7cabeeddd214a7bca8a0861682cd52f56f54c198c81f15f9b4dcfaadbcc115024ccd6dfe2b6b95f7982ed4cc2550337a2bef741b20ef8ab0669ae2e488b1a0fb15a184c16a182cf510f869e9c238f6e76e4224fa5cfdc32831ff698159c0afb22c761315fd8015f05b4adec2fc643df7f79e0ade06f287ab71baae5d09598294b0be9a46f8697a9aa5653922f29691388c34cc43ed044040c83dfecdadb16bb3724808858e9dd0b26543bde0249cea128ce399c9488a00419c2843e1aa028a2c6af65d8cbbc070d8a403b9292753ee71824a6d4ce485db7b2214ee2119a87985eb938987f68a53ea00be0af0cb1bd8b3e4a1616e8eae63f9d3457668fe7a9a595bff4ad2011db8df45bed866bdeb972566f0b8a8a7afc2ccdf3325645266b55117fd0f837422511a56a945b417f55c059f55a1aa28c62211721a87a40e30c7ad953e348a136685930f4c503039b6419445656c5917a96f24e71ecb80f96304ee1601627b9892ce9bd647bfe7cc1132eb7cb28b800d6fefa8e95d5062d0c45c3379025bb715a5674563e345fe71de98822e326a013d5d41050709c00c1e8cbe2386401d878dd30513e1fbb414fd27a529779331a63c0aaa4cb80ddd8584132143c081b57d670c53366aa6ef368ffbb8174372d85c661b09ed613819922d87f7758ea9ea5dc41a6fc9f94415a29a0b4da1d4cae611cc0b7b87a5e8506cf9aa533d221f08010bb9555e85ac52878879ddc1204d9fe4d40e19bc0e2d0bad67b5c20b2001bfe7173e99950ee447ac7bffb89580fba506d8863b0a25e293aa522494bb6bf81cbf419c19625a3cf3c85a3c32ae42086a10f915c01d27d19da532bdb53d52f8406fdefcee89a630fb64b2a415526401ecda590e33c4a82304cfa976958d6a2c93f26e067dbe025bffaecde441dae429a6d3e1da1a94686aa5e35c1ea060b37857e9c2a87369671f66d8d31606000571d57538da62812a3f8cfe6b2cb68369739ddce703a9540ed79bd982b9b604670ca92fafc7e0a8df5ad51ce4d38266c22b2a14fe3cc9b6f654313f6265698e350e6d6a4769e099d7b3e0e7e54d66bbe2f7907cb9e5e14ac6421e12404c1bac0a4b8712dfc9497059dd674e039a23bed1a8502665feb37270bb44a408ae87525bab4a96c9709a5efa1d97815e087d5b42ba080eac6e82b28ecbe2cfdcb361497caeba242cfab40507c46bf445c13061a16fc74753f332e216c235ea7ac4ca014ebf830d524a53f5ce0e69e18e3396f60b2742e9b83514b249c8d7e624168c70b8d9f3bce9e8a72ad032f6876461e3db05cd3b74689a418b49889acb536cb68159e1ea77f3eb334c886f27803a13d5bae3bc024e0c942dce2b5de5ab0e4e58367fd12d8f508c66805528fb44aee6e6fa16ef2a2cd291eecb8ef4f629431f5f02c64ce3ae191cb2b84e7f8098265d55d237b5bbb0aaa0794681ee5cd98e40ad7dd4eab02a51d64a55a1e5355a8a40841cf16674fdf54bf06d42e9744e5744d50842735f53883a7b4ed8b3303d16587e413ee721a3c92e46c223dff1ea64d107ca45cd574c32b773b9ca3619e4a76faf64c6f1a30bb74effff693c453df991be7c2c56975c6a4c8d5a1cad54aec24b2bc1949b3292c2d42ec738c3860854caf306b1e5dc36566836a1bef87d0e58e274e0386b008469dc5e9093ac6d5152c2af5f756e36bd990a083f395adf13737fd00b8107c6cf1ad6f06a9bbd029cdca351770b4db51d30119c56809f4cca6326587107d365e064e7331eff77d9adde1fb6a76ac3c7c68395179e47ae53b7e65f93cb7096b5ff22eb8f32a60000ce7d8f48ef81004ba97380f219c73d9ab22ba6aacfa81aae6bb0f79742d45209bd6e42971e0384471f97408eb2159dbe8c6acd695a404e64175f815fc2cc2890c878266f1e954138318bd86858552625eed8d24c4484552c97501d6ba431dad8e9124498d126fb3a81190b4d839eb82bb3761658f0a3dc00f7f0457ddc069432e8b53d9c2ee86380697c698cbb4a0c916d1812913abada106bd74090bdce99f13b7b89114fd84e0a66c8d92d55684e1b97be40ce1345363281404e921e6796b56358caddb4e0d209de24bc627f5a11e3589c75f9c2f4d06619975c8cacdf2308353959920ef056e8102a36ddbba8779e711582b80ce306e7bf233d4da16cee5fe03651d9da519c2cb47c03e9c9fb30138e7c4d123d40529f4e834995f1bde21bdb68edf99039ba8eaa52618b054d678a6a75356964d7b9f82cc56463d4b80a97868a782fb33acafbe0823ca17f5456ca3f064005ad582d655eb97515c65c19ec5276557fa191eec20ec4d838b2e2487688167f9ae6ccd08e5827e4235f500d6212caa2ff4652d1a2bd2febd63c751b2a324a5206e96b86c7a20eca9a6595fa8ff9a9a114fd6b72b2e8c2e9ec6e51c6e397d5a4e201b64393771b30210b30ffca4c73b2ead1d1b8abb6d4351838ac3409909ad2b9a78c80e821e84429fe9b46c8cdbc8674a461957960f8a48dfe484495f783834ec951abc297fe1f50c13ae5ac53719a7c20e50d0073400747ff431a945909973aa6d974306aceca894b1e61c901ed5dae1c5081b49069d4a437e4d76ed334bc3a2ad1e90919d6e5e80f57e80d5edae85ba40fb81d758d47d4e5dcb9b17c3610083cecfcaa968bcd2fb8de2d802cf7432ed7d4949b970c22fe272a044841297422a8abfd13e3741e962dd773a044a32840a86a40788c9059feea250f6292eec0ce6b8988206a87f8d35c8d41cdf81326c2ddfe91a00ac400ff74a73b9614d2cce586a8384bd65bb797c937beca9b83a4ee50f44131fbfb8d30d6aff047fc3677a86979fe136cc52e214a450468835dc4e5b04a39cff84620951cfca1787ad8b7d12eadf0808e2cf0de82344bf478bd8695b6c44f98d402f4429d510e5cc6725dd6692b87ba1885e56abbf21aaeb2d823dcae71d9448375f9f51aec05c2929a2e6681e972d21758b4dc8b194eed9f026b5f7acf678424e69a7f7231697b149d22d1f46e1516fd32d6a3fad6a5b23ff268b13812ebcd36f6b5b174fa514ef97b651a7547d0aa8dee314b9896713f9081412d139dc115c8237bec79e9b9f622b31691e50534a7b062d6cff7d824300b5b1a07b7133ca77fab5f22b9bed4fb2f22edcc6984e283bfd03fe448566158649dcaba7372902d4b53bcbcb01435b646fa1a638825edbc0f1c978900f9ddfdb66b1e840b5e6bb6b6731ec798682ec1ce6895a6244c2eaa67da5fed23bae20762d961d3a88e42683a80b0461d9fbb2ac82c8388e7690b304503110e44e60b7cea02e1430e16ec574ad4e3d3be2f3e00ec840ca1ceb9a5bc08875dc91bf9d2b75c7fde9fbf58bbf2afe949f340d85b64b69dbfee362f8a76c4e5ce8da70215f7d4ef13f12fa6bf13826e228d5484f581414804711d64542810e159961e4f8fd0972d467cb031098b5f71e7ba1cf16068db1c0ccd51aa83872a89f4f9b40f8f86ff22cc14d5fc43022cc27c4747431c400fc57fa0bcbc2587774c9bc24805a5199a7040cc3a8654ce1c78258d0067bf76ea5810a37a9295156c623a945f338ef4ebdf750f746aae4bb10e5e643c1c62960fad960a121765cc2d01512ce4e84020cd09acd47f408b6ee2397d1f8e0694423f409959577fe267633b1f096a586848fffc0ca243f14560a0891912fe037f4af1a3c4d2750575beeac819e2942870579ec1965e239204d8310a1ec27c5a3ca125b3e92710d5360d91e5ca54e88bc65423961b82b01f9d86cb5aab765f3b7d76f2811c52338bfc21156a3b3b8bdcaa067f451c235a86cf511afa1e615740e1e717e0d20f95db3fa03fa86aa68ac4236d465590c1508969b4f775308239a5cccbcf978ebd1305c5ce5f86086f90cbc730645c2d8dabb056c7af0b4b4418a22e82f9f68ef359aeec8525105b1b8e51e53c6867e2595f82bdaa88b0dc2653400b46730a66656f6127d3a99904993f4c39eea5a043c266df81d594007650d745c8d43a18d5decdb7be312432b47c72c878d5275df5cd9942802c5ca045bc061183eb18b68418ee047633fbc7313d2dd52c40ca47b959090b337270dd24bf8a41cf3b49d666d1ea45cb5ba58ad2accbe165e455b533b7cfc87675c12c7cd1abfb753aa4c5790c5e9580ad912c122de74b34705cbd8c0d075e4d382c904b8ce912ef3760be6b84d4573a726b3f1e75c543de16b2bcee524807ec53750775fb4434d719cba7f297874781ed56bb8c4d163ae60b4fa837585c33a56938cfaa3db93f8eca055de4a595e1c5a18b0682c3ac615c0097196e40fbbbd31fa09cb0d31214565e305caeb5da74c685ba8891d7b5a84e4f0fc0fe61223c829b1520492ed2db4d2fda4639e780d743076660e7382879f238b289fdc89e46fc2f88517d2823d311358513ee0d33d9b5a36719b558d17680f214563f48b66b674b5f1e64d33eefb66aa96463c5f05c57a39902433d01017993f9bac37545ebda87b82d641f4948b7b3763db0908f93627ee5598e79d4ad3c8d88742e92266e35e031f0fb6a9e3065b9a9b81b62fbdef0440b76c846d012f836bdfa1d347c7f8145872c64a8bc3ef1474d2102f2d96523241a5a2080a59dee1380fbe81e6a856de326c4dd75720a816469c28f2e168a54b86b3d0485895ea10af0506b4e24868a406d62874d059300ea69baccf056170836088d62491078f95da4dc0fc26c14e8f0b8200720ae6721d66c7b21f3f33a42753d4e26311a31496da152bfb1372b504a82c9e3352b1e4f73d863acda4c5704e542675e98c6851349bfd8dc2650869c22595563e0cac07f142d0b2eed4129ebd8b9df0e84dd6498c59f48cba170747e0900095db18373539225b1576c10fa099ec639c61545d2398ac37ad641f2b4d856b8b76492c3141fb6cb5851dabf13f90580c79af3af21da19f238c38523a9fb232e6dcb03e5978e7041528542a1e7c4008d45f69df3605e273f1a1de74e3a906e6ad9916a95e388641fbac8206d4a892acced5aa62501c604853414f809ca1ec0b2cdd251c0c042d632460f539dad1e716986562e5bbfe3b02ab38ce2cd40abd52e8f84b1a0de4dbba28dd771263358a996450ad26e5f1b2da33acda258947046c9102a4036b59161e043eaf6ec14648a109fbc82100cc184ce8727d6efc13a7a26a8451f0972a1e7fb08cf083f14247ebe7ec2cd620a55ce7e4aca5d02da8972afbdbea270032c58a85b9541ea35ecd96920b4de547c85b79954872421b50659ef4827a9b8894ac8dd647aa485f2869f9ca1d8b5ce71f4888a360ce62833533c17e5bce9b2ba65afa092484f37b17c1f9733b9b280b8c806e7befdd56f8a0fa324f2320dec8f1fd80ee70e4a98ae126eacefed4a6692f47785ab143e9c191bd964bbf95cac6b791507b5a92e9a8609e8c39c3b0a76038e1be0f3776106ad527545e2e69cca0053ea5e4383f9220d02a4d9a19deafd8196b95506324027b6bca48c07930a09115b89ea1736b814018752fc11a34e7b29ca78b86e96f42fd258f1c6309fd9baadaba9fe82c0589b3eba957f42ddf89218cbcf5f046460aa5f4ca6fd28505e066fb0bf2ddd360459455ce10c824ee2104e02670e19912b0eea9959463c04db2855c606fb3c72f7539e4437ae870e0d4b49dc81b078efe34470e7f6f5f01509980ec309ff2208638e77235635248884b6136069ecfe221df77752aa94803d24ca2ec30c12537c5134267bbedf4461053322a27df149eff71fca96fbe140460f14fa73931995d8e37bf46c64e9b6e3a4c9858e3ea145f877a9619fb4c9ba344da54489b02ae89c3bdc468d5b5ec2d1454b5d03a45ffa2fc5f5f5b5bb432d1f3d86ca137c73e964bf6bee316d3e81a939b76c4ee1bdc72593daa9a69f7dccac7a5fe4677b03a1399747b5f2ecd4b278909046107691b95040fc6aca15335a011b2743a3b74cac73acd2de163c47f30389b174eb19127d002f289b5c25043e628e5b5f503c23d89ecdbb009c8db82ef0983ce0e7cff96234bb78e2d1a9a95452dc19328ae3268e9dda6ba2a48587a8a1e397d955d94b786d100e98784e44411590389688ffb26b6c13de7a69615387b19471cc66da60dc181b88ff614a34a82f8dab34a6cf45672d5c33de3618d08b5151eaba1685f382a9f033a5374b4bebc465ab529f68ca35162df4f2249ec591d1a7f724b589f56279916b6decffb3915747ce1f0efe9e3b7c5ee2217d2230a752e25cc778ec428cd079e83ad953b2fdbfafef8952ee4b7f69d06900a3ad79b107ec3af029a491f22df8c1e80a45bf63f4d19f4690c49d2457dfad0ade56486cbb2665fd4db7534ba4c078ce841917269bc5270f17984194a81f792c0b78de9dd05797970db57fe154dce080facda9daa77d0cf3c2f3071641a1935826891d31865546ddc0c21b0dc607b1e893653e85285ecd11617c2f6a53e4b2462a2524cf761b94bb4e4982cb4576e73f306e45d23406ab855e97703032fa1fb24214eb8887e2cd601a20fcc105ad5718732a939d17456285765be587206fabdcc46cc4cc33b94d4572ad7d0fd2c49290ed87b70e1baf6f23c6fb2c82bcf45aedcbd484b378d09351719c984379379585fff5e94f6424fa9c848758fa37cf3d44a6cf03a527766a6445d5f4d23e5e6d08a8632541f19f693c8b7272a8fbd4b06965cd19dd7999b7bcdede8b31a8ba0e580f440932ce67143c01465e4cf4edfde96f519ccce0f8b0d44df5e3e6c29231712ee2e1adbf41292348a007c90b462f3e4144222f446c6e14839feed21b0c986b85989f49547151e90675dd9a50b1a9a0792e9357daba48fa819a088510a76f50c2cc04585c24ba4b8fc02a8d6960a3c7c5824e96c26369fc488645b7bf68f55360d96d75a70c314972a25ec02edf244672d150bd6683e854026fcc193723e99382788ba4936900698b9313838ee8b32eddab43061b5f34270458fdabc7bd5735199d81459cad5a6b81ff8a61c342711351e7af5e417f936bbaae0b7a0b90b4739d8351b7660f182a88703cc27cc03f70b17273a7e306b36f54bf7bf993a80459b3010bb88f43f03591671f09711216a703fa7b3dc0da47d08230024830d073f332b21a41460cacc0989c092bad4878025844ca7c98756ffd195e44f983cdae0ace1030fc641a22549754c943b434b56e298bba45bdf242aa39dd50cd62d3c182d3603a944e3753749546855d276be35821dcde468458dc7531134f6cd32758e358b40443056f72aa4b486ab40733b522f3a69887e19c41389b624572b4b54305626d461c7ad3944f60b034cea38796b837bea76a164d5b31448f986634492986a881027119704991b1fa38f541e609691c5ea4436f4caf5b2f8da5481b078b54cfbc876b3b6a4e32e6f23ea64ceaf755f616b81387a063a2302836c951de8c71915a95567fb31bcec6f56582e9100a4e1172e6da6a19dd8cdcf4129c63593f77a063c305b988da4ccbf94e6caeae0cec8dfe2215dc9da1cd539e727fc6afe973c5514acd9350b839a9dc9834a5afba5842765bc643d42c78eaa561f0cb3d0eae0ce3a19ab22f05d39bc8d28c271dadb74d4fb75ca09e37192788147dea18346e627072aa98c1ee12653ad01171970f49b0c8c4b9e830037c629df6081165e6b954949e5a345c817d1c472561f82acbbb02e598e5c171a6d064d4b7896b5ac611170a8907e5fad243172aca22a6fb2ac995396d56848daed0365c27fc0754351d0113ad046a135a590567851dc1b22997d26d8f2fd831f5d57e294e11689067a01d0b2783ba96848a14df71d5ca2e5d0e075ee8279f2ee3651c9057aa561ed2862d6fd4c0ab11f276a2d470e36d6fe4222b06c2c08d4a56a24efc253cf40d5664f9f4f752db72f30f792ae577e03d0bdd155d9926c8764d3712e22e97c1be4ea6945f84a0c59d06efc1d957c3b9bc4e7a5f35390ce65919de8795b9b8fdab1baa97715199dc017978f535d87720d1e6aa2ae50a09e6e0c9282a1fca2509d5c23d697db13b105f070add2fcc6a71b8f3702393ae57be8c0074e13610d589e7a8164af6e7eeec85e586dda784873742cac4692b34645b1b3e08131db004dd3f3330cc156ffef26322192a478cae6741d35fe108decb0c78f61dd98075cc2a63ba6e2458dd4b5250cbda703fcc76edaee4bb85709e1b5c76b97b616ddce6f629c2c4eb78ec6f2bdf62e77138280fc6df33cacbed716bcc0e31ab9ec8025bb8b188314f41dc424b8826816b71870eb1c89419547f931ab94d00636f952acfc37e4506a4da92294bf012228db848fc5a2050710c163b527769e03c5ffd3cd0c1b05883ec73b48faa4351cb7666f8d2ba4eba250b093718d100d5db694f8a318796ccc691bad06f9b1646dfac7eab742cf6451880f397310744b157a7cc87a7f70b010338e00dd621513065b1f10896945103e62e651d12c54fcf349924c92a5986180be4d9a6fdcb08e187492895c63942d25fa518424d7f8877e02126c2625463db1e5fd16bd76022ab90648e8375aed018b1dd12ac9d0a826418093250e5501dc284a2eb64fafa96000ae8b426f99f30b31e5115ab6960d401ce6ffd4fd0784a03593a5c31177a05d627ea165f018912ced5e6f487f791638b396ab5b4bb29f10479c008a6c24bb97fe0ddaa72457da42fd761482aa394c1e9c8e9c16fe4cdfd956f2989ba0c37d7865701a886619ffee989a181a1210ae57ccb12c29e3ae24c62c8103a0827852916b01db3f225c2dcf6d423115833ec8b102ac02173fb4dde82e642e502fad549f575086dc2ea5011f332bf85ed860dd5558b7974e89a7b5ee525425e4c40ce86a814368fe874a12eb5361879957150b7fb0deb67a347302bd27ce094d32a67655168b52bf94392fded104396cd015c60267df80c30c0bb5b181a157e2cf97ae4ec382d98d2155055908295e38fed7967b4a5dddc11bd0b7c02af738670d3afd011637a9e799734a367ed730ba1c8476fa0469b5f201f35188e44b0d1d1c87dfbc1b2616320eab8460f1845da9ef5ed0d25e4e7649cc65cb5f659e3a6b2f02a1cb11e63840bfb0176b323636a486564d33d3b95e14dfa5b8ea98eb23a097848195f28b05334914366b9c43b1ca3c0e31324e1d12eb659171d9a87200e225f1c57d070f0d8012372db73dce99781e32b1ea4219f7410464436934725ac2b2e2c586fd44347676129b06a91692092eff51cae383eba9c19728116c36c663023021cbeec88be4f176705570f73be9cfe485a804b319c60c3a2633227f4a10db17e1207a8346ade2c4aa9283dbcb8cdd345257c0b4b4a72344bc0e74a8bff264482856e1841459dd9d0a6136fb2d50681d29365aa3723cb86cdd6124514dd8d0866912ca251bd4eb57b7b7db6d8f864b61cff08d5aa82bc8c09a3b0b4b0ce71e3f8e220ad9b20e26be7b6e3488cec1fbf9d3dc2d3fe9a9cbbfaae65d608cdae3edeeb0c277288792388bcce210ea6bca3e4d31c2b19dd1f55b2d2526b6b15ae697768cb740d48927f580561a001f87550fb6a51a4e04dcaccd8cd69115a98ce82f5886507bdcbb5d35764a567c45caff7bc34f46530fd907484f7853be03872f55b278ed7019126ed457cfc86b8213f54c6ed6c84141cf573c73457b2ea1ccc6c19ca98a2845e4458b2532129ed68c962c94f6f555931507907e5759f3b207735b71976700c942b57a6e0eda6f66f10f8b73abcfef2cbe1a6e1cc8a6a55503761996ef39d0e962d58b4fa55352b10a60dcfdb0312fa28ae9c2b298580501c9b4787f8d1d2f37af36886b7117af4664606d582105fbadb8c5c5d500a4e822325019ed349127c17e88182444fa38018b6baa71dcbed7c76e570340b76e9d0f37fab2bce95b60606ed8a66df3701e2d7edc6c170e3e1529a4dc8290f712f71d9201f4906cf58e4f6fadd7a533c4356c8c9c4c8546da7f8b69185216a92c45cc01c3d4123c5bf626ee96b95827504e19f55a8ee327dcb1341835ce068b37ddb3fbe5524b796bbe881b3140a27e135d77bcc03c28d10c746fc7289fb9020d266afac9ae98a4e12b1d7a588220cbadc525adbeb2df625a1666ab71e05bbec3675a389e521795f3a1a32d804c1e63d0a3099d5d7a30093bde1221c349f356fa470d5811a81b117823cb17c72cc971ceb6be84626972f199d32b9aadbd9cc8f31528dc46114b451469390dc621ff978b02cbcf39ac7de0bee2d3e0cefa95080bedcd6c211810e64d395f32ec79e70724f37edd5555864b37a0016c8a51385199f8157108d3505c02efb2c452dc8cdea5e37df38eaf820022b645b403b92d66b21dd2e4aa0086f5d0f5961398ae78df05a96c7e877e0765210f0130309a54c9c3c5359a13052084d71bc39900c760775f0744034e2e91e7deaaf17e9cdce92e5dd338a270fb034273bc4903e9f6d20c44f0681ca0317b69813359786c6ae8a1bbafbd2431e266b05cffc1e210120d8d04d7cb31638861a55524047221eadebfd2333f119a64939904bdd00f484bfb8a2860e4863c8d060eb3bc82ce87340727d214be64d59e614fa34d5ec7c8667bc3cc2b1cebe07637288946406cb243a294fd6dd6155037f5e8da1e25ad7908110401ef011ae1c51098ca73887ce3cd7316ca023b06bd7cc9fd87af5e41bc390bb20cedc06eb554382487528f5c68f2d369e8553b2afbb9b9b2e284a271c7d6b3517d73f9ecaaf35bd1ae9ef486f168729551c54047124fbe08a51daace0e9161c2807a1244e41d8bc82724040d2c025ed6e69dd811db0ee6e7ad523e2c5e2cfd901a3c14ca44d3f9b1cbcf54625097f734e3213153b0b3962f788dc4491876dcc79b9810b9224058ea3d1e2ad948fdea5e0e24a9c251c0bf899c4d22269061ad4b506f60a363128c880d44da81f1df08fd2ac19a7dfd9f70230e60560f62caf7442c21bc5bbafd91cdbe95e5dd48f6f3e646ec1db82b128a36b83af958e1d3e5f91b745dcce5d48010e125c48d7820c6c5809b29c462f40949f9c59de2f67a28d48ed7bf6c0ed4d598fc286be09dc94319648c83bc5802e1c618fa0820be5cc94b78f771e01e4f88ed48f65fd7dc3cf7ee225aa6564986cbb4d901357a099101b1be5552728cfccbad4809b1c11522f442288e53ad36067ab337bfa8d08db6b018880020b87337a55c8bc19e30e32b444d22ee5718256beb143d962ca92f16a37438c64c74fc7de0ae6c0298bed8f36680f760a7b805396f71ce55b8bfc06439ed0eaf79a22b65b7d45c0f56aa87506b4a5b53d6b6352bb5c949e03ef4cdb7b1588f4606c494fd596acd373ac24095bbc99c648b7433776f2782a0a4df3e221543638112b4dc51f802a8506d0c8a3a2609e60e8ef5e508dbbf69b83e8ea1a534aea99da5d89bd065e7df0ca1163fed6a54e662b17b2de40cb85c292878e620601cc80eeae4a6202220bff842685e90708913fa8f6d364a4c1aa4fb6d8961fc444a3ed35b0d1ae54ba29e597d44130d881806d8a03eeb56de1c7eb00a5ba14a5cf1b9a9f5cfa94fb4c1016e5c084480526a726ccbd1a7f8dbe11e1a7668212cb9853cb9bcaad2d96b2ff25fcb6c3ff892032d47f8a17aa970a9f354213c1d9c5fbbe2da0cc20ec70280735fbf35fbcb935fddcbd4e5f74824e610f9e3a3741d2e7800863ac7d7971640af76eb9be9e567bbf7c1371dc513dc78afe717425c4f9a2d403b1e06bc16a7813ef74cb48432431cf40bd797c2113a42d58e1fd948358ca443a52048e124d615e207883fa0afd0854b300869479468021d03b9a29536f81360ace7848d2bca47aade0cfde053c987349cd13ce1433c749c2dfc5a474267e4e921d3132a2d6600bef8b0d45a22bb3d723132002b941c06369a743e04ac0aa5b612b31d9ac2e74abda39472bc9f046f951010c7dfe12ff77fb99a248a7e7e13b1dc18a791e2f010569293043a8b85d4c8ed38c23b9e9e0b5059a04de2a2ebe07cfd4e05e5b7e3b9a59537c714b49964621f5f71a133683538789af51cd8e1a960303f16d49cb50a96478564bae0746acb8e5e6e94f83dcde962fa9882820146f8d68642b7ccf46f9d148257137516197a04a822db6867fa55a225f5f7f932fd3e433a65650bbdaa7c4db337308880bc05ca9e5101b053dc68212cf3ae7aa64a58703a1c0f60736a2c79b86ee7a1a3779676ec83a0bff706ccce9e3103373c66f2db12923681f506cf23d8ec0fce3047cdea31e826ce21edf41a9eef8b6e26bc4cd4a7a6b07e69a2a9f413b1b308288f62a0951e5afe80efe428ef76df9664e83aadd4d1d50b0c2ee62bbad3746a0a194d5be1f830898d81401ee37c8405312a4fcb1992ff8149cb351075d69cad2f4ddd6c6dcde124123aff15a7566b650ab296052bc3fdba49355af98767a07fcf0b29c24a60c52c0e2bbb934bc46565d169d19b43934a8f19d0f3f6cebc08d5be987b9505bcc9758b1ccc441e231c000802e180c870d6ba968ffbef50538e4d3bc2f0d79d11f2051aa9268dd6e1f07c518ba52de606ae9273277c9bf0d92d4d37519b306a8e1b3b20d9e9641a01314c0026b61f1946c811464441306280114b2d8c7ccda160c3c0c25a9b9d4ed4c764a9d3cfaffbf9407ece240eb0e8ecc418c54a1317c4cf06694a7b64ebefcb140556c4376a6c302ba73c4fa977a02396b10ebe44ff411c80f558ba59fa410131c704d842e80c7bae8754002daa243c6977d668274fdaead1ebbfa86525ddb845ca90b3bfb4337f0e5792d8b0f140c7cad252030e3e4b4a25a0d9ea573e89d7e81c202c9eab9dee5594913c9ff5c2cf5e2d619657e35150c9c5ea0b0a2910bf4250f9b91767671344f8d9e2fb60b2f909d7b8789a46336f441d0e2577ab209ed3848dcdc540311becfbfbf8ca1f354c17505d5673f7183490f0b6385292acf3facd446782b3de9d452342bc8855f9e94b5e21fba4207490d0f4ebe0fa8a4fa4bc256f4feee6f13c4a9766300f5546987bac3093aaf64d0b8d649439e04f6729e82c15e43ff867fccbc62b65f6626508ffbbfe0b031c541ba2c22603a159e6012ca75a30f506a863c8a1a57ffa390e68ae1236d0039ce7c12c97865dc75592d03e6d784a042e937438a2f1c2c1376e93d1391d97a38055bb13c147cb9ab5568b91959f7be54c061f8c87e7f47f59f7317a4754786f5510488e36c6eaa3b647b3dd9ee8f5873df2803a3398657559570da6ef0f926b995ae72961b8266ad97f39b8fa45f908b86987eca02d28acadf776fad336a5b1d660323943e29af0d98f7e4ecbe6b499776a143eae671925ca11d371ff13c8bfb2e86d365c85d39d3befdb4efff76f903f591285b8896a09b3e9c9bb225091331ce5191ed519badd9ecaabedda411c48c6f0ebc2853f7b9cfa996574529dffa1a5189baa0472718d2da754405ff591db49198f5ff021649fcb782cab8ec368be19c156d77870e5628d5e94352fa6fe63124994c55e93be06919881bb036473cafe12be099fcfd84f737d1fbe69fe1ad54d2e69fad83241d6f7314dafd508cdc2acee182ff6f6fe266f59d32a884d767eef3baedd5c53c2ffd81ec2376d613202e27e475f71ff79313f4e8c3fb402ef505c191364675c1bacf2f6aadf9415fc0a23eab3088c54388e09843c4825cbda9b429795e204ccbe5954475812ad8faaecc11bf1dd2919b65fd6cd8ceb55d518ff817a57f5f63726ff4367de434c4a88eb9a033c2529caf079eb7e72f768457ad1f5ad361e5fe56f29bd883feac832afad0a6a1fc4ced01a5a8fa6f30ab772f735617d9131e13e0cc7025a4e97da15932c6d914cb89476f3847432bbbcdce330d86e4573c2468f96d4c804b11b8d0caae0271974995aa2b3b80b386aaf12e1e10acdafc1bfe145632cd34d7f6851e47743e3e2e1546d26ba14a89f3be2059867daad0682ec76369c7332a2aef204fcd776b51e4ea0ff22ec6f8f0372ac5301fe63ea107be7fce0d48bfce3466abeba2d22afa6ca09bbcaabd60b671eec6b0a7ad43b7081b0680569557e4cc6482a3ba1bcd0cc3f85199e5699d79c79546f34f6fbc0dfca596111fd5511e6a391eed12665fe48b9383493a90c22e89c7074ba92e3de935ff90edd10b41a9191b95f194e5866dd2c9f5460a1d88f81ab7163b99c3714056c8b309821fd77135df8a4557ce97fc42e044ad7ad36f57021a2fbfc17b4da59fb33f58e81e8ea88a064ce00c98c43179307d60a4fc04e8101ce115fda669e12f9ee499777033bca7f756b45f2b56b387ee4536085dc3ba6fa3383fe67a84aaef7c833d39d3788fbaf774139538d62f831a4f7e535d3ce3f08fdd3bba198a94635fc81a14c0e8cb8bbc4bc03432ea5708d659f7d18b6c39f1bab977e5c2bf7c4a07e7725ed4effedf0c29694340c053e52411e7d32541224453a3d4f6010b2409d7986925c9beaee28d373c2903039619e8e0278ca172810008c7e4171d5a41c53417a984e2f528409591db7026af62dd3c10c62a3f20afa56ff7149739f688b875b86869f9edcd731d38e026cc9aa67a953a7099e6701b675d2c6a6022db4d30d610708ae674b7044f7eed55ff9954cab2ad55cc297e6c1939744f363f729ebf4e5b63799d45eabc892baf376e32f359d31fd1f4909b4e3de8bdc9afd507480ee93495bb14656d5408102017aab8c88c34c346b390d57ace73ffc7919a6619a4a895167fe1d642d38f4229a2e33cb5315816df226d83196bdc3a98c06b114ac9894a0bb2ab571bd263d7b0915d5287eccd1f710b4392cf0f4f3c98c6ab0ee923c2a142883075e7b30165f5323f88fce3afe0a8538c56c7062fb4d035d06767fb13d6361cb33a252f34185ed2f3d4eed5aa95f7718875d027b99aba3fe6efcf28c6537472879ab89c3dd159b1d2ce7e6898338cc64155ba15594a9ab9b058059b1ce6d892f035ab168913de158386873e340a2b473dc2858e055ab229961f37d76b19509d31e0314a871b9958f7aca994353a8d70937a28d1d55e8b1ee3105e1204e11e342db3b874ad2b44875be264d0633ed15ca283206f9f38bfa0e98db59aec0d744d04a2c34c4eef7ab803c8800741be902c62e6b2e02a38d36644f712260ce2f097f6f0542282e85ea0eab1320e65f5ea3437e79f0379812f4c2d935b3ed7ab0c92b68ddcb518c71351128eacde0642f039b471c7ce68858d44f7411adaae0db0911a82b3afacad28a01565caa4753305023d7f04e8f10d038dcf571b463c2d5a07953669c2143a172c4e1f12e649f8cd29586022f789a239e61bafef015ae5931d9df14cd8a94450fcbd7beeda5b2cd26d70e6b415f09a3bb60fb421cdacccf94869884a00be10a890db10a9bf015e4addd19e34260765dc00e2c1ae76366f59ce7ed6889fbc59de93b8b7a743b1d34ba4296b97a94e275dd4ff3e6c529a1978106f489045aaa3bbe551048cb6a3a84a3f14889ab8ba2ed22a4dfacb67845d981c730dcf277c44ee8a9311762280ca5d42cab117c17af9809cc849c9897acd00d6a5a1b229a1bf8ccac9d37b2ad7081c23ade0ecbdabddbdade7bcb94920cdd0b110ca50b2e2e3d0a7141f55bba681f8f2284d0b774d16c6c643d8e2284f0293b8a1082e53bd4a3f8d4a36a8e1e9b013b1fe451210380fc8baca057430becdf0a2c8865050579fb56149a65605084040b92829afa72bb38453e987ceaa87ecb5711925a8ffd7a3b614150f0717f945a64aaeac9c910be3dab7a523dddfe959321882e9a0711a7b7ac38bdf5e2f496eb8b9035eabca58bc63d7d26f541766c2c057b19c552b0a3db2edaaddea23765ed84fd84bd53fd5be2141539e9a2cd563fca45f3aee314b9708a6e7f41878b36c33882c28a4e330007feb9e3323805795ea754149b1bfd5076c2921cf1e50aecba8e6b4e82b32f7288611a7bcccb752639e6b8e5aa0c39c6f8ca37d6d22eae07d7ff4b514aa99db02ff50798b029b6c0e296ba7141791e95d22970993253c8c8e0539838c932323217cbc934e7d3e7a6701cb57cf91f610249ec4f3dfd2cd7d8e80b18b0000b2e3b88830cd8a4230b2e708cd142c9eba76007a09fbe337126a2778f2851b934398fbc26ad6ca922168bbd71030eac8005d5b0f10227ea188ac5829c6896e42022a3e90444aa0fc5dca79e76cf7dea83b84f75ff812108865fcfb951493deae77fa1078ade6f62504a1ca974415ce73175eabb4fa5beefc4dabd88127bbcd5f4a9704fc57bee4b759ee81d51e9c24fc57b8e0a8aa2bea3ddc5f179e7714f592c741df79d38a6fefb4e7dcda19ebd399df45e7092a262108a592c74cf7d50f75cf7540cea1e250a75e2b7e731b5e77def81ec79a2508fb73ef51ef75d4ae4a818c43d4a14f244f15b0ea29efb20d473a250170ee2be1385503fffeb4ec19ec52cd210e43dea0f30612ba1ea0deb3cb10b73937627c7e7b56bce21bee9246c7adcb36ffde366bb3275ee59a4302b5e8536afc1bdd7a1a8cf0d56bd6e9663a2ba78bd80e88636247ca048c82e6a8b43892d0e2ddb9d65c46184f7831746163aba2e8c23c66c5e47e607649c3a5693eeceef3a324c1bd7510fa9888c2b6494569cd8c1d2184f4858907490c6186330ade030dab88e7a70b0e0ce32e0404add9a3b06cdc98b184b63ccc460a748bef9ac267ee7fb9d22cdfc99b749144760acbcf881cdd18c556380c22204109313ca3881115c6ce8811348f8d0c653930e9a7a5005921757cc7801c68b7bea628e1ca2bac5c08b5a07c68b29381ee0a8f283d110618451c50eb44401061c6ebc6185185fece001718f381ea0805b41b8a9c06b7e82c253c1135b1b47402730516196a9a1be7062f5393ad0a157cf9d113978a38d9d1c287b32a851b0c7a67c3daf4a379d738e1bdbe09141a181840ef009065d19153ed59d65e4e00c3378ce31ceddaebe737e12159c043d993f6bcc1ffe1e373f7bd5d3ff3db852f9366fb825d754252f316d4a4a3c30d9456c83b962a21a71cb1e01c331bfe41edcb6e1cbdec1c949b7114bafded29d56976b27d149c9492f62b6fa97f09ea84ec56d2742a3f297eee4aad6551f51a1c70dc7d5167a9fe139723fc3966f68b7396ea80e802208b66f8594c873bc1b4a9c9387e33cefb9d6755c0bc29ec81b08360fc364e1e6253e1270d8d9e8ca37bad1eec177645cbdd87f7f6c51cb1fede21ff367de6ed56ccdcde96c6de055c9503d4646a579e2eb39b16811850fbc21f1cdf9434363bba7bb7bf16db08fc18db33c5b54039556712bdeecc4c66d1bab898b69038b0d977bbf7c3f8e5266e2a3eded77b3f36513d5c5ebb5d401e105b5850b252f8ab660e3861570a8820d22a00843054a5f9ce81718d43812a855150355040f211120b419be0516629a0083871d5ae09283ccd21a51b0e04a0a8abe2d46e0e418001a3784692329054c70f15f8c41c70ae0706248065ddcc0c17481031a2e0e25666c81830a1ab85186072cc8e6e8628d33ba0481032ec6745cb4f1c304dc463d9603f6bcd8d9c9c9411149615ce48004471e2a0bef7833d579df02ea53a8f7505d9d99de87df42ea67442adfa7a8809b45fd0b30cc26284efee8f4ea994c2f409901888913da3dea41d0033beb59f17617d470dd6a06454521263a52ab34fc2e41601784fa5014f2c4d15b1ccb4a0ff51c0a35f3bdbeefc10fc597b7e87bac4af854988462972f5f4a809f98e488540ba9ef514f25d5028aca27d6d45b014a7d10aafffb096b9185fa94c8ea3e8512591dca0acc0a6cc2780ccaf2ac9b98a947ee857c51a13889fae6b888aec5b2765dd7df75ece4ed61c13d6777ee7472b23ca722cbf37c04f2f7d664ff163a086d0a2491c52df8f7b77f9fe2ec0cc7e25c7c716210088a3dde9af341db635b9367fba1439d1bf7d9717b94b76d9bc7bc11b9872474a9e578c77695df43a45d25919e232f6ee2a69d0677da757be92341c6e2e7de0a6cb32ca61fc462107d4e149a40ce56d5637d5db7fe44da3569dfbc1e125f66f2fd0b097509f2ef17ea12847a7fcffb8fbef72951a84b4a6c178550e2745ad694e889d35baccdb23691c582bff72df4a7442a292a5f87e4e4c405285228a59476dc06442995d4f7bba5f23deabdf767c067bb04a5c4ea284bc5137d3bc1d7fdcef59e4a3f8f9ffc4fdf0a6ca6a31fe44f45a12edd0bd119b1f46ef89d18a4f2578d35353348451cc771dc4f8e732389537243061cc7715f33d3719f93032c97ebbaae73428214355ce1bb28d4c7819dfdee0c531e8628d403f9ecb124c24df5dbb6ce865dc731e011402f77869428eeb058e8e7f1411dc4bf23bcd38943e36335994f9fefd3e1f5b0b7a53bec438eca79943ef9fc9ee7edd19b73e92fe144d6d35139aec9f6ac14bc18baf7ae7da23ef9c26cf93bd3912732d106a3facfbb8d7167193850ba7ccbc9e47f823f9376cd32dc0073fd5ff087c143664e92c2ec919f49e7c2d02ebfce9af4f6cc980cbeb9f32599f3c8eb0ceafbcf77e9ab6696e6cf0c73e9f6ba34e7cef7ff6f6c175f1f72549c15fc89f4c8db2ff1df4496950bd4344534454ed6d9a25f698a2ea5515aa351a854ea3df5549c3fd4a73e10f529f0fb362e352cfeed276cbec7bdf72d74bf8954b6efdeeb44168c0aea3991c5a213efb9667b446599c8b1d273bab18f746466e62d3ccf5b347c70d5a2e325427b2eed717242f116fd17bc45cb39f6f82bf5943ec728c33ea09e308a7aea33c909a3286ec5711cc7596662bae4333720dcf0cedfc2034cd812be41f573a8f9e9ad0df42147adb9f3bfee88cb46842c783279d89b3d535a5182fa68c86261fb1d1f74f33cfebbb12c27dbb30e30611bab850f429b022906bbf18ebeea5d9c9ec86241f53d9e45c51654aaefd1e36f7e7b560b37bffd8dc8a2f909bbf94d64d1d8d58d7dd56af560786718304997f6f4d8fd665fdeeabe87ed01c5af13b968c29cb0ba9fb0ee3daffb42079772620edcd7cf61fb9a1248504bd672bbff78d817bcd52539a2de7c8f6f755f1a59623a754a51b4713b23277d46e4adaef39ab7ba779ab7ba4ef59dcd77cfabefde8d9c04e1bbf7a39538614e866ebec703358b05eeeb3fd978ece0a219208a9cdcf11d9b609213d6cd1da98e879db01a30c5460c8695584c118fe12f8cc469f01233dd2ee5b47bf7d2fdd7e10e18a57449fb518fddf7b037764c75232feab11ba244f5ce4664a57ec74f58a712592991c542addbd73c15eeebb7504365ebba671a8fefd8e87625cceaec31d5893b3dd2221eb6bae52d2e2729a243f58e1e54aa5fc1df9b4d8190a4b81eae90c34fbfcc7591294b7fdbf5beeceef4b62694099989e4a16a1697fb3890861c09b3c5fd16a230733db167f2705fd346e56a5c9193394c9289b78638242ec7097139ce87cb3d7d83ab714f4cd243959c092ef74c9ce49843e23a663a9d64791f7a419e18047e280ad1884233e20848d407b9ff423bb9873203442db99a9333cfd52ef733a80eed844dff3c5c3223438f5c8fdccf3051fbd2f7a6ef396ee501cefb3cef53224a2c579ee775ef794a5b7f6c7ccc71dcb672eaeeeeeeee796dd46ac197fe12beb4b472ddbbc3bc5113218420f367fb9df9c3f530dd30c45c16a374735c297d594d7eb05882e0d24c9ea699118129e7bf79b6b9134b1763f0f7b27b16bbca620cfef4c6e0af52d9dcd81e93a757459c6a4943337ff87dc8516deef414cc4b7244cbc950277262872f206ab9fa6e32c9096b6d93d207c3e9f2a740cd4cbc06930f3e2eb55cadbe9c76b5ab1cbfdcb9e3f5294ee49f13f5d8dbb6b1faf97b64eef77e37c28d21304860d208c109ad0c1ef4808c2bca0813421264d4f8a1cc1b4d6421c5f6c50a94bfa00c3030146ff1bb605fa5fa6ae79da93333343334b5d6fa4bfa561474dec51998a86533b5ca3e7f073269d7bc51e8ad81753760e5ea7c2013279be94938a69673e98949e7e7d0941ec3ceaff333b41e861f8a52faced0cc88392058bfa4379c0fe43336c74611ba3a3a51fceacc199df75089ce5b5b3d12d4f275f9a3099f454353fbf2d19524dad3e5207866b684979bb444ec969c85696a18fe0d7f49dff05fc8f9a12e5d7eb1d41159363fafeac1e76fa6768122cbe6e9b579fe21b258cf97f5fced46bb582c714a8f311f5688f52cf03363e98a14ea67be9ad1822dfc7fbe7b34b599bc152af11d17f893ea8439d50c356cb821f82ccb4a017c7ff05d64a5c07a7f1763e09ef5f37e340cce50c94c5d81350c439aaf6114bece45979f9999a7f4c821f8844d13e94ab3359f5fd52e9648653dce6f5ad5a53b5fc8ee00c2aa470f4f42532cc7befc4c0866cb751e9663b6c60a753123d28865f550a4495ec31ad62a965e3a0afebd7bf8a175a71ea77f9fc8fda58f1a4c8f483d26f5c87fafc8dd29d74651bf999c9c325bfc1e9377b5309a4fb689c4b1a2e1d941193148b8fe5c111ceab8c1758f070544c2b8c0e920e2bad37174bd0e1faefbaa5677e7a2e9ba3f93207707ebd11c2bf0c850828e2b3f48020932ce8095f5c270822c9e00e24a0cac0003f39f9c0fb8a04355aa7e506b95f10314ccc1c51bea66118afadc377341c746868e5bf6d06e6db1dabfb371771ddfdc29671847b783fccf573df528dac47892e8bcd4cfeb6db39bfa0f678d9a527533b8a92fc894411068d07688a595bb0385f1e214ad514b170d096d0773eca771aaaa6fc97c68f387e63d4663793c8d53db40b383d4f7a7bedc89a5be6cc9544f5beca69e47a55fb6787e94522a1e1e43c1c7558973f294b4e8a6fc6e62b9d9e0b5fc80d32dbb97619bdd1d3f43253bb6fcf820249061031a3aea8e1fdfbd8bd6aed4ffb02e2dddea77d160e08921c6759b584a543d9d515573d8c46c86be1d768bf829f13f959d41f8dac83670a6ea73a0cafa75a3c77662fe1c33ead73ba020054f75c03e9c09c2e38bcc8acc402c341f5a1fffd1cd7b07cde561875cd6eff820b688ac757172bec8ece6fb554f37aaa79a1d3e688ccc8ccc8c6009224208eefe851620331842c9c9204fe4aafe6b30a6c3123335a6b089a99e7a14f2442d5b32d5930821b88c5ce112bf3bbe886cfef0f846635e107e4e1710108acc788ce64fea8b307d3c2c12dae4e90782468f1edfaf8353f4aa2714746e4afca1d5d2088d074f4a4cfd4e4c84d8687f18fd08627f20f90f25fb0308131026275d34af56204c2ea875de1b9d1e3620d872b3e1962d59697305c40155b66f9122b322587aecd4abfe13e7509d375544cbb545b2cc11b400759d412d5d344e9a020b9a8bd6ae949676ad6aed02e1038cc463b0181c06836130f80b36011fb117dc0573c15f780bd682b360232e0163c157b017b6e2fb492727990a9e82bb7014fc046f61127013bc042bc148f0115c0413c14270100c048b807de01e78070e01e7c038b00d56a880606c0955e118dfc043ac03cb9807bec23f7cff0f8f80b1f0104cc44670164e8269cc046b6127b8c6503097ef47527392a5f87e24454e72d1f723317292abf87e244748904e689a3f3dbe771e2709131535bb3334473f9b04a2e4ad2f954acddc6ff3ad799b5b37a37a46b253fe03d603cf63d80e3c8b616b780ec372e0198c95f20c86ddc0f31796866713d828cf477686672fac069ebbb01978e6c262e0f98b95e1790b1bc3b316b67ccec25ee0d9c842792e81b5c03316b602cf57580a3c7bb1303c5b6127f05c8515e1990a2b81e7223be4790a1b8167292c91e72ef685672e96e7390a0b8167286c91e727ec039e6b7684e72dd601cf4ed89e671258179eb558129e9bb00d7866c296f0bc8465c033cd1a7956c22ee039091be41909fbe4398b3df27c8455c0b311d684e7226c0bcf44d6f54c844dc0f310967c16c2063d63b1afe7202c029e47607d9e81b007789ed99f671158169e7fb0489e7db04e9eafd824cf3dd8159e79b00078dec11ae0596603f01c025b80671dac009e73b02a3c0f59a0671c6c0acf37d8fb6c8325c073ccc2de8a6df255ec009e8a65f242f68407815df22dabe4478bc2a72ccded618718d1d2631ba919a1f56884c648d603cf63d829cf62d80e3c87616b78066339f00c8695f2fc85ddc0b3092c0dcf4736cab3177686e72eac069eb9b01978fe6231f0bc8595e1590b1bc37316b67c36b217782e8185f28c85b5c0f315b602cf5e2c053e042bc2b3151686e72aec90672aec049e8b2c91e729ac049ea5b03ccf5d6c049eb9d822cf51d8179ea1b0233c3f6121f05cb33dcf5bec039e9db0243c93c03ae0598b2de1b909ebc23313d6c8f312b601cf341be45909cb80e724ec916724ec029eb358139e8fb04f9e8db0aee722ac029e892cf94c846de17908fb7a16c226e0198bf5790ec2063d8fc0fe3c036111f03cb3489e45600ff0fc834df2ec8365e1f98a05c0730fd6c9330f3600cf3bd8159e655600cf21b00678d6c1023de7600bf03c64ef330e5685e71b2cecd9069bc273cc0ee0ad58027c157bc253b14d5ec82a79105826dfb228fc68973c08d6e657f6e65356f53d6ceb0219d363036912813019a1dd7e214cd4d2452b8130dd7e2334275bdf6fa4e6e4f8fd468a8c18b5641fb045801c7d3f10a59a078f6ed70051ba1d53d9224c37b6c8181b5ba40987561661bafd2e9a931ef87e57cdc929dfef2a72b203dfef3272b286ef771d39c981ef77213929e5fb5d4a4e6ee0fb5d4b4ed2f0fd2e2627a37cbfabc9c919bedfe5e4a406bedff5e46406be9f8c398981ef27654ecaf0fde4ccc918be9f2472b2fc7e92e6e405be9fac3909e5fbc922272df0fda4919315f87ef2c8490a7c3f89e4240cdf4f2a393981ef27979c94c0f7934c4e46e0fbc926275ff87ed2c949087c3ff9e4e403beff1573d201dfff9239e9c2f7bf664e36e0fb5f444e32e0fb5f342717f0fdaf9a934fbeff55e4a402beff65e4640bdfff3a723201dfff427232e8fb5f4a4e22e0fb5f4b4e1ee0fb5f4c4eb2f0fdaf26279d7cffcbc9c915befff5e4a401bedf27e66401bedf47e6a40adfef33733285eff721729200dfef4373b2c9f7fbd49c64f2fd3e454e2ef97e1f232751f87e9f2327957cbf0f9293277cbf8f929303f87e9f252761dfefc3e4e4fd7e9f262781bedfc7c949017cbfcf939301f8fe9f989300f8fe1f999349beff67e62492efff2172f2e7fb7f684efa7cff4fcdc9d7f7ff1439497eff8f9193aeefff3972d284efff4172f2889341c421d5ebe0c1847cffcf929346beff87c9c912beffa7c94912beffc7c9c99eefff79727284ef471243227392e7fb91cc901021a1e1f4e368f19b7aaff5a9f153a954eaa78d299460f6adc0de36b940f5290f5817cd557315b98c5c472e2497926bc9c5e46a7239b99ec818292367241149236b641169441e9148a412b94432914da413f9f48abd64afd98be8457bd55e452fa3d7d10be9a5f45a7a31bd9a5e4eaf279f988fcc67e643e443f3a9f914f918f91cf920f928f92cf930f934f938f93cfdc47e643fb31fa21fda4feda7e8c7e8e7e807e967e987e9a7e9c7e9e709490c890cc90c09111295dd12e4855899070b22fe28794b057ac0e2144db138461db0384735581c240e581c252916676903168789068bd314c5e238cd60719e346059b10c58960c03963593c1b28862b02c5a6959b50b58561114cb32b280651d55c0b2902860594a3058d6d2042c8b490296d51401cb727ac1b29e206073620fb0393207d89c990b3687a8013687c6009b535b80cd297a62738c1460738e5ab0394809b0394a41366709013687e90036a789059be3e4c4e63cad60756206b03ab202589d990a56872805ab432380d5a935b13a454cac8ed112ab738482d541526275944eb03a4b03b03a4c30ab73ad0e90d51180f51100eb0300d64712eb0389f5f1637df8581f2feb83b43e5cd60792b7fa4db03e94ac8f256ff51bb13e4ab03e9cac8f276ff5f7d81f23d81f45ec0f1efb838888fdf143043b14e471a8128389f04276bc0e3db03949e0f13ed89826b44af4c134797a27e6439b2474ebf8d0e68feafb7ffa7d7850cb96cc12914d1e9e96c589591c99c599e110f5a87a6ac9809a872d7dd4c0c70c6e7f4d11170d491292253036182176fb41a84d1e1b6a9119679f0bf241ac6505d90ff2416c9020d606f9256cb0b16db533a85852aaf64b1aa75493b5486ade4a358d228460534829a4105248292436ea2c3273d245f356ff6ae5e2d2aeefc522b3db1ff425a5bbb7b3bad111788bdb85cceaacd579c4f6c43777aed3b94ddefcb7ffdc3b54f7ddc7c04bb8cce5b66ddbba7b9465f55356b3bd018e6f67d2f1c114fa9d7ba26721a4e6cccf1daa6fe66dc41ae8dd21c630f3e003f587c2cc83ffcdafc1192e017fe6a9092184f03737aceeaf6d6c942ad7e6c6e62fcb46e16bc33831e8587dfbb35ddf7f9f28fb7ec8e6678f37ffd9d839c325b3471b71f6f83d50dfcc1ebf8eb52c663fd632d90d958b84c05155fe3732a825d73e226c6e3e2fadf34364d9f7eb581e5d504b96e9bc3f5f69d204988e0917a83bc6b24382882151f108fe35407e27c8bbf8fe2c66fef0ff9c3e7ece1cee717afceac5a25a72ade634ef2b1e6f45ea43d4ef6da038c9f27205c5c909f36a932df9da176df9bdf8aa9aef5bf340c4b2af6879a9477f6b398d17cbd5ddf13f441f6259ef8ed7f1ee8e67891d0ed7566239f610cbf196af6bf31c2ab91159cce4f12b3c63598fe08dfdac6723ce99077295a5815e1e76872d4db825974cc7f5af76c6a63e0429f47ea2375ffc7ad566407a4713dfdcd928a5be811f9de1a29eb394bd3d90c3251cd74fad5c2e67b852e8a5288c7c8ff4b8a5e089447adc7adc52f0c49e1eb7ef442fc9e4d936282f0c09a26e5f4ea69ea20cd349063feb0e14f73af4c0baf7de5f070f86faf1696ad5fd4e52e0bb89aa1e2965b1871c51cb99a442bb5641a8a8e5ea5294b52674235cfa1ccfed77cb6a1168f2d0afb9fdc409bfe7b4cb27134dc137773e28eedcb7813fc2d4548ae60ee6bafb7f28cfdddddb38f5fe8c42f13649ef75f0607548f682d5d7c1830d0de9f0b0f075e829c3cbaf336d2608d3b54d57f76c2fca76cd72e19299cb62cb68505b355fc3cc25ea5362eae342ed13b58bba56aba5ca0345504b15971efd6bffd4b1263163c64f4ae38913d31112510f1628d3dd7f0e326eb8bba3dc1de55d9b3cfe9d6da32f3d1e35987ea2a9b8fe335d826ba40c6fd935b17dfc1edd7f8817b55ce5ccc21a0d112dea2472469d3fff031a42449d5fbe6650b7ef3ce7e80c2af7de972fa09793a81791e86c7e31e161177b860451e7cf60d0ed3d71986ad3f9fd0853bd3b516ddc999444d1e5db453349891897f672b7ffa8ff58a3d6b86caa1eb7ef5aed21223ed4fed2aea306d32ea6b551bb90bad6454e64564bbea81f616af7234ce57e84a93fc2548a227b87d9dabe6528b1c75bd5a8aa56602271880e6aa9badbb3e61502442d552e66f26c3fa4071c5fb12aa83d137b8817db77ac879e6b3d6e9cc5c16ddbb66ddbb66ddbb66ddbb66ddbb66ddbb66ddbb66ddb7edbb6dfb66ddb3ab1ac3fc254fe11a676932ab7f48037d371e9baa86b3d6e3fc4895aae6a77db9eceee04f2a1d160cf6f9993dc85a96759bad62e2fed9a49483cd159123edcb2b15c6f5a1231efa236ea2fd77b72874a72982a0b0706955bf6e4fcd49335792f71927375265ce4cf46fe2cfbbede7577777777975514e3e0a5afd0e9ea96b59773d9cb965db634f78a6541ed59cbdea8a577bf4c1e672b455dd50ea296de0cef0c5198cfedf45d697fa15d76f54fb5acaa206ab542cc830f6a46b09aff6638abadf9cfa3b931a3df7312d4a313af9b6ce7edba8988a9e54c427ae2531b4c7d4ffc69c2a7791a27b68787dae192a027b6e41b3eea6798824a042dedb169a8244abd21588566993265ea106ba0371463e83e752993767d28b0acb7c5eea66cd93da7badc3b993c8f9a0ac00967fd5f4c85f2428f4388a8e53c5201e884243d7aed3a436f555f164a8fde2cc220be40048d5aaaee64720a3bdca12983d4bbdbbbdddbdddd79a6d8e076ba719d87a294db50a9cfb90f7430e46ef34ab70e04393092aedcda71bb37d8dedded35d7de9b534a5dddcd6d1cfd4da4de5c37d8dd2111125f6d5758a301ef6ebecd917eb7510fe4388e7a4db6db8feac0d65bbb5073a4a16fa8b0098de711cdeacd9d494870b9a5b7e1a086772621a1840e90b8d2b55179dc9994a5074f557567529619009dc04485383c9b1ec05cd9a14693aa07122851a354e3d1430f4654339e76f0708325685020abe1a10c25665240ab3c141d11aa6144c3031610054d333bc891c49782a77007359648a92103b340a1f1f4793d3b2091c5f3aa0756a7541d4a744ac2d05031c8c22951c1ccfbe2c4a6e40ca52e499644124b3f68a2c674841233944ca08492349242c04508256818f51c7925f9614616511091455d9541d56a60022894b61ad47a6719501cf1000a29d6a8ab3bcb80620837a088795e5d75d172af783ddc74e9eab5eab942bd5ac647c8cf1a448aa8a30549462c1d01c4115bdcfe3f228edb3f6651bafd333d2c00897c5004096e7f182ae9daddb4bbbb6f70bbc7edee6eda60a8a4d97ff49f3eb8bfbb192af0e1faeababbfbe6a95089f38634c604d3f081c62d67d24c430cce2de7182c26100209219810e206b77feca1e2f6bf86e0e2f6030171fb991031c5ed7772c3ed7f424b226252d24d687c2e08a2045e0d62ccedef56757c392378a2c8edf7ea085670fb574170b9fddc7321829ca65b8820a97276881c0122073140b071fbebeac71efae2495a438d1f7a2046046acc5645725041a96241920f31f8c1e8f6f7aad65893164a28a9e00a1d341cda1060bcf18317517801ebeeee396e6b21e636eb7677b777d33874e8b9a58a480f4a23f450e6ca1457b4e8b95eede1435208b29ce0a90c30497abec79551f9fdbbfb1a8bc52e7b89f1508f6d57afd78b480b6aa9badeb4295ceceefd9d5755dd7b364a95eb7905e8ef58fcf37694d2efacea6b50475108965a8260bb362b24884ac57871cc7c7a34d0e106b7fbdbe3623af14ece0f92094335af0ec171fb5742dcfeb76226e58083db493a5cf15aaaaa241cc8dceee979164997de8f59a0c451a4415994f6524f1226415eae8b7182028e5b82344c5b5a9004451ab79c493a97f2eadbc5e00f5e05758e183f35538f4c3d0ee4fa136917cf234a93e60f93c9c3bfd3dc3dd123267931aab7da146ff678cd693d5bf851bfbd777da2dcb538adc79fec28f445fd37d5976741d99e2935a1287a509ec537d1b5f4a05028d1695ea339cdc91ea739ed3a753f0a99beb953766deaa0f4d8cc3444b59c4d53bcef97418a48c51545892a346b7beee765b23d7def7696b57df7f3a2d077fb6f9a602e142f5cc414c5094a57e030e6ce3274a8dd227726e1e0a5c9b83e3d8957e7cf51fc2393642ab868f238e5d8941e85c8514bef841e81dcf39e8e8e9ea228d99ea328e19e6605d54b8f46fca5c7a30681b8a84777263dfa33cd42b3b48b4ef931462d7bae5756cfab15e4228ec5297f71985e37c5dfb9e889d2ea605eb1571e3cec7ce1ba58722f65bbf4fbfbfa0335cd0f6ac9462b9adf3ab0fbbe147d71d58354fdb92f2b7f0cdcab762c919e5792139838097a420487ba024dc8fa68be55832b9aef075b5cd174e066691a04699e9be699e63d5442238e9d05e28998284c12991b76cf653fd83fc37db773dc7b98c20f4e1cc3ea24c318882effd2ee277a22222ea2f8da335f9270d072e9a39e2a5d7eee598bf3c72feabde9f247a150ff31ea67e8a447d4033595c1b95cd4731fae40df7b6a9bc8f64c0a2dea91b9f45893555a3b6a174aa9c1b40ba95d9ec6929876d9a4cb607a3c83b2ae54fef6d257da5576acb9b4ab94c2485f2e7fd3daa5a56bac999f377cfe2e62d4b327c9aeb8531fa1680de5a150aa1e696da3355abbdc472cd4d39465a17ede9493f3d38491e0115f06d364e5e724274ed2187489c19a8cf8f9a85d335b4210e3f68a6a3426422273d98dcb7c144583393aeaa315b758d63ea24a33637c2b232a0bf5b3ebe9e9a1a7bba7e336eadd0266fe1af4a17a3db61039aa3ffd7e838a20654ac68a51fbc9694b2ddb8d4bbfdd20a2fa0aaac79807493760390112932b294686c648920d433b35df8f076afe980b088245cc44b8a828c9432de7d3e3886957c7d42e5437b931f350388d7671ab29423ae41332a982597db9acc5a6dad8e4d8e4d45a6bc5b24433e95dfd4c8f0f2d6bf5f382f06e6f66a038396586ef2f65bbe1cf7cced358d68ee7bbe3ab65ed709b63a720d5f0fded16203fc326e1c7e04038d62efe1282a8e57caa329ae79fb1cd647b0c8d7562f90ab56bde5a451598f4c8217fe81531b1148661183e50e72051cbb9145f40e0f259ca68e2072cae5cfe0a6cdbb66ddbb6bd6a73c1c6b19a88ac14e883d103e4eeeedc3747e74f0dde9d3f35d43b7f6a586d4f275bdac4bb3578b7b78ed5e4ef168377fb995f4a94ee4eb106efb2d8af234051a41ce9d19d29333333534a5de9fa768233be64b93d5dcea4fb7f5b8bdb67e4f0d350037d29ac267fbb9fb71363106bf0ae471fc86990e27d0cfae5b8ef5e3d4601737b02f5584394cee7f6bf8ef4d8650fe76c59deb1a54b97aee03233b37f69e5fabb5883df242c926ed33792aa9883864bf9530b7edb281d0dfd33ca06acdcfe707235db87bf644669514a14be35cc28dd0541b1640ff54d5949147eba9f38bf0647d928dd75b1a4dd7397592c6bf0285d99261da36595d8f5ff2e757235f49f4be552b99fcfd1d0b11ba56bd2b1db82cf6b7288723fc270e032566ec9df94eaf05c334ff1a1ee4cddc9bd953bb9e6067cb5c9a1a12e653fbf0e3d30fadb4fd1eaf0b02ed4bb9bfde1548519023fc17066fe78574a94eefa6fc0ca9512a5bb566e99ebf9f86893133697b8e07c7367b32594edbd67de6cc95068e818cff6de770c51b8cc854243c76e2746f144ef3db1f608ae3c863979ca9aeb0f65fbdec4283c608feef57bbf7df7cdb565716de9125dc19d61d7dddd9d75255945dd995405d1e5ff38a98ad9e5073ba98a5812154f97df9b8c0325a1a8f67b2a954aa5288792958ce5963928cff352a9542a954a656957698584a25a36adcce4f127610a2f73fdddaff8cccbcc9f9902df89b27ec5673db2cb98c8b15cbfe23367227e35be19a44ca95caabbbb9bca50dcb6799ee7791e0d3b919d52507d0c99a71e6d6ab59c4f4e93098cc9bf755eadabd5e4be39d113a914aea158a2c43d9367d9cd4f59dc78ebd19632c0e09bb3a57c3376b9634c5c34477984dbc5956166513a9ddc31fdf213f192744b50698aa65baea6f872cb9dcb47a8f77ca0ee36f2160f362a7fd94660b3eae674cc639b0dfc3f1d1b788f9a1eea3dcff3746c505fc70695d3b141dd363a79fc6f72a8a53f4d11e2d87be46c910616a793c9f344634e7eef4f97da2546367f369fe2a896058d0aa227664bc88ab8ee3120aed3a4922a3ded10a500a8e79e85aa2a55c1a7ab1cba2ae793ff0ec7a6f4e83f84873a613c65f2f8f793931e9845533c515b825604d1757f771bae3b939133f918effddd0d77f22fed469fc62a09cc75386a399fae3b214df93e1626c9d30908a08f2c6ac7f8e9b3637b5eadab15b8838f86f9fdb13bedbcb13b2d6b7edf79f92604958f18c90c4e761c7be9d64cfa82491ab8c59f4aedd88183238a42849470cbe954c22d995ff768061e81b9a848522d7b9054e07690c3981aa4c173cc7db2a3b73ad6adf93ceb995e8be5f7ac0664bc210a66c8a340173269c850e9975e49eff633490a30777328dfec4fa9ae92906a5a86624c62be18116121e3a494c4a5261b1ac374f4250bd1139934947a88b8c14a495c6a544687c6301d7df12c4ef444260da5222e7da5654d63c01cddf934ce32c7d39dbf74a467f24c9e71562b6a79e4cef726ad4befe4adca9d3dd218f1cd496934cb1c4f4b697829f2995f71a309098c96a6756c0e314b465e78e624039371e77da82cf264dfbaf8667f1f3f6771e0beebe65ec80a8c7bb6a515eedbd2e72cc8a0d84d9fbdbbbbb9bb4a5bd9dac17cab1fd472e5d4ab32b5b9bf1d338b2ca79b2d895cfe1d8e6845ddb8fc1db8caf9bbd14ba9c8854aa84841e65fcd51cb9516b5a497d30041b8a1f6fb5f6a8f8cf3270aaf723e907da3cd93ab32032c783e0b7c1f3f018597c9d5803d529578f331155550b180d8821460680856547e700a2ccf4554cb4a3dcea53b65f8788aa2ca5c8223463e3ee2d5e54eca572a7f0f754ed737df4aa943b7ccc1d9409d49528cb9e52a87476065a0f4bd3dc739b73d2211a424212a90795ead93cebc9565fecc190957d4243e03bfd9711cd7bd731dfd2f4797522f9756ce7233549dc8b118b5eb4bbbfc68fec89ca4316fd1f756b298ecc67aa48d458e5aba2c76a55d8e65fe5c317f7ad61ecbf3708ebe2c3598ced2b5f6d2467dd4ded62d89e9baae9b39a533293dc407757ecf668bbee7cde813f5bbac47fa4dc91c264aa48516d12f3446c1cc688d7aa146f488225dfa9d3795b5aba44b344687280df48a2ea34bed9a495bc4b44bc8a55fba0c2887885a4e24262a38097ae2c2d387a15d32b42bf5f4679032a55d1fc33e10653b8fefe720f89b05bfb3dfcac71ef1c3a70995709c8802f733df4d4291a65f4aa750723fe3851fc79a5704727ebfe01772dc94eb1cf840ed16c8fd8bd25df013c71e69093c427ff7f37220673d2c3de72d21885abaaceb3aaed3323f0e14125925f008dd538e1ce9b1bb28ddddc49c1ebb6fec69179389d4b38ec90c0be538a21ea6e5cc6aedbb42739172de9dc8443d524ee42c62cfc231b5ec19d1a5b276cd1c8b13f5ac5db369d35937650279e7082e4e673829d576b60fc34fd9796b580bd34c5012eaf15876a471f9b9ec71a15d5c1d47ba079c79b003c399f203a09d37ec2e87e10cf8813db2fb10a2eef8f9359ffa1eaf87417feedbc640c52445d4a756eab7adef333889f3362ad1094704c1f356a2d2f72831654bbea9fadfa3c0ed7bd8efb3dc7796e53f6b2a13b564239aa41ee79d4e527a9cc285f3da63101b6a5957392bb19b850598bfd3e3f7c84a33cc16bf938b9dfd3617d3dcb2f8000c6b9973d471c42563d9714b26a271fc41fcdb47410f53e07e86208e7b4e2cc31b2e7d2b33f4c836d4c0b7859bbf11abdcfccd0360e8e87ed4f64d0100434717004347d7c52d31000c1dc1aadc885dfabbd0e71bdba529f58196428f0dcadc168ff4d853469d3f513507b061fe914d57a727c6982133630e136a4b4b4e02d8420b330be32500368c29ba5b0a1c5780eaee3ae698638e53cfa13cefe6ba9b4688fa48b3b5bde771dbc68965eded4114f72c14ea3b6a25314d66eb34eeb6b51b77dbfae96e3dc7dd3c76b747751cd775bfa46fc7897dd4637f8e15b5f4d9ddb6772ced1259dd73cf6ad2cffd6f449ea55d76360b598185dffd2ca6fe9b61932d3362952eb5a86e1f5ab9522cd0dfe296d0fbd076e15256a8942d5299a18a9472cae54aa1cbfd6cce2fcac610650af75f839cf5aef79d8dd25dafb9eeee58bba6ccc999dfbe89266cdbc424596ac7eebcdb57ebcd56f7f68fe4e4f446ba5bb70541aa5ee4753886e3ba8d7af71c79251972671945495cfb0bbceac5f11161e3066d0cf970061764cc7cb1c6510e64a4e00b0e383ac0bc804711386af9979c302297939cf08113b38614618608d304920faa1768314616242cdae8a291c070018f0d0e3592482046a53ff90a982d37f7eb53500b0e5f6809e3f6371751f06035d1e44d2fb5d4c4525251d28bf273ef5d6f35a29a678bbec71efda6da338aa09674ec51ca0c4c8b82d29ef908beb9b345fc21a6cea7f30b15f2469db3dff99b3771a7e79cf367e9cdf9f3a8afd8e87b8fea0ff50c54c20db5f4bcef9d1c7b5ecc1022dce3d84f38c2a262cf7dcd2a132021b35a528a060d96fae1122b97134b2b979362e5d239a43667f86644b047e619f1871cb19a11a833896674670bee4c5242cc5522e96e4fe48bea3d1135aac72068a58c2e90f24dcb3326ea9183a83cbb350ffeb7e36b97eadec315aa30955b7ae04eeacdcc07ea999c5f39512d11d0f61c59854ae6b3f89a3ccc53c4197ae41fe245e52d65cb9e5a69ca29b7f4a76e65f5999b997b35d3df122c9f1eb4a14525a4fa9ab712c60026c4439cf1014c88c75711e105b09ab752b3c566137f88a92ebc203e79a36e4f435b4a711bd1856ed1076abafd185399b282543388c7dbbc8d28e4bd15584b06157cfa4280a8fd655de588a0f6ff8841dd848cc880073360558c84b1c40c6035cfc30a8545c0aaf0b801ac46ece25f1fa83f17c0df9e636128ee3c28bef064a67183549841adf91d230f1e2af44879bcea9fa05036dbd3e9e27ee31eb421d8bfd9ef39cba4470ad41b0fbb8122cb28134af3334b29f0431a2c331f6ea185c1be40639f883aa8fefd43347ceaf70591053ebda0f8a447ea82acfe139447279d61b41073b71db6a68716f6e0f329fe10534767299ea4472eb925535e8adf397950cb890424be9aa82aef039073996ee1befff302a647a8e4acb486552d99a2110000100041017314002028100c888422b1502820935551f614000b8aac4c70541909a32447619842861063003000040440006666b64d948d99c2ee6b9058bfb721277fdb76a8aa23ca06bd1ca28ae4383c658aa2c382f9d5731134e16eb9b2a16be881c8851291f32ae795747ea773889f33628c4124a05ce9f913a620634d3fb9d8efa89bc55ed8a79d7d49ae9ac05373a588585057542d58fe080c59502b2263c382d3e11956190cbe7af3993f57186786a623872ee3eae928703edb0705da3821166d318771e07fd0381a4133a3cfa14a876c1c364de0bb1c6b5258a7528ff74e1eaf16443ad87d7b3e7ab0169352f892a546e4d1dba496948705811da32c83459927e600a4316836e56e858055070854a457a7e68b4898769ecdcd757421395f2ceb52d9c7ce715a68454a02987d37cee16a835666cae6c731a3bbb4fa4ac7a58f34d2e3ca650fe57d990e32602ab8b0ac0a6154f92c45749ca9df1039f3f7a6b3f68c0407a79742f4e286401ea8318aaed356bb2b8d702956c7e71c6da77a50f3a33ca9270e9e9c9ee718eca48b3cf7485cff54791fef1e0017a677d59e2324901b84a9c2dad0fa2e2c70bc0ab3eef6698d37fedfaf54493e3e10545e6008faa5c9af2bf944d9ca6471361d7b18c9b02bf39d08affe88f258f50a29903c3b2dbda4322e594493ace5780f6f8c232c3a033031a0fb32c063ac04e309e630eb6cf24c070574c78b597fca948d33a219c3c132e65a3cb190dcca8318803717f0d9b81a40f0f5ba509983bffc00a4d2e215fcc8d5345f774764866f3ce0ddd280f1e20d06cf49a59cae5923fd3c43d585711e6c07cb22a50eaa76c6683bf27e40e17fa08b06851640930f894dcd4a52d2e4aee3a491825fb0d36bb0bfc8c247687523843004f7f7eead97d41c6a2183380b62bf464a6735bb0ec460e799a5bbddfe8dc563886b1b0531abfcd01e7e946ecc5d3b34a69ed0eed4d3524733d1483cdf6a9e20ef60c0ab89e53df5b0765dc462edade1e78c66e4635bc28a3d27d5ddcddb9558e8f93fe3ccd390821fbcd4950cbc30db4b80f3a02d7b0462e6c28b2dc1e0800f13a9b5939981936872c00b8b0d23f4be4d674500038990a1e9424984a4c59b68cf6c6ed75d2b4a2e3aab610000a710b5b72140ae4b716e2c543e16bd2ebaf067c665121e5665c9791af2154e886b68b21bfb3a3096e42b75dd53b5f84c476a8836bda30364e2388181918e907073e9ee6fa70b41664030bc26e269ff303f356ae37e70b3925cf3b965fd789c2de95ac118d2b356abaa965fe3e8a2371c6d038738a7aae3c1991f668db81739de981cab52c2e16aaae7eac0d4ca056f0a1be5a8a338aed4c6b7c04752d5e9502f783e9b75d24578ece435f71bd2f862e183b633c8617b7c7826b8c4315077c3fdd652a66710192e2b6c1bc38135ff493c8040395bac1151ef0d0131c33ad09bd314cd1642b3ad8d96d74ee7baa8aa9779bdc4bf8d1f996f9b3f602158398d999d8c194d51bbee7e09cc7f86c22f52ef835f8ab6c0fdf9765e6e8162438aff9b5ddd7a9f6fdb07c3b3713c4e328cc17795b8309f52ca7da0ab9b8f9e5dc7d6af66720e10dcbf8b86983cecf215708f7962d60da6bddf98e64ed4943a08c9667fb5d2e285c7a1e05a84cd70f419fca9b635e9a5acec7892f324c9cf78dca223e8081c400b8c7a3c47f0212c644c1ef18ce9083157b0e519241997712b1dd0ce016f06942fa2ecc976c66c1b5bf3916d35051251402c204c641f629a569421ea276dd79fa51181b063c2eef8937851be4475dfcf5c711034f58fd8fc8f0cf7c7cb82e0d7079209175a9a98b975138b701a953feec8e235cac54e7958010d8a7be3af757d68533d3a6f6ac56221951bdde9cb3c54d9b98a36bd8c36ddfa5adac2996adf829ba6fa4322bd90fc764ea72a455a49c71cd53b5fa70f4855eff6d3dd22731f3037f030859030c7bd12e4199579aa23bcd3063489f83f80fc254593ac73f44c9f43e3f874d290373c4dc3bcf9d2ba47708a7974404d6efa54d97850cb7e691d27561b6e0f4539978c86df43f1f0a8f162278ce4cecaf8a28156dacaf168ea8c9bafe1a52494422491c04cacc616931f4f383d5b1b6580b4662f933d59591005e7b84e789baebd67b7740eb983f1f9fd45d0733a040d067000349d7fbaeb0df024045a3d6fcaf4a1522b292a007798f871a44d4f4807066c6939b0da54b53488af0a8230762f204306b901a63624760e587c66e18a1350a1605a0f3b4f0217faafdddc1d89c3b1c72f17e54e5084fad7c76505e79df0eec1172a44da9b059765148ec80503156fe494ce78a414ce2df401611e042a3e201688559219ad924133e827d3fffb0e91e26f8e4f4117c9dd23156501152900f73c385ba700c7ce7f1eb4d51da9ab441e22ed618ed8dc141ef757ac147fc28fa65cc67cb89d86b9ecd2c0e534ab1f4708f111e795efd0902af1b2d1668f3f02a29a5c86a491ce8000124e92f51dbe6c0f6025db21cdd7ac950cba77a85249dfe2a6c077d559aa5bb044aaab2d9c6eb733a6ea16691851e3890d2bbf9f9c0f0d710c406b88a5375822dadec50b43aedd5d70468584e651b79a3d5b0894fb1ad9f4cac050f4d7cef22935bf4611780e251565be1e2e48bc879126a6c5b25abadf2893fec42c7021d2b6d1a61f95015f987954f55251a3d2d5546c9f638d94573d6880a91e65e3e2c20f73f6094ca74cb0b423aff59481c3c9559a3e33cb9e0c1eee42591ddf9a1a7d56cf233ee7ee3923420d1f2ab64554dd26a425a32e0ef52a42caf8ad5fdc88b065cb3edd34b8ccd78789dea6d05d880720dfa9114d382f6aeb7e09ce49ff140c40708cfe66ec74addab0b6193ada306bcc170e460dd022474d9e854d9fb52ab893dea9168e94f366dd76356e8b769b4747d5dd33bb3778a9b508305f378c1e700e2170eecb04de735db76f6a5ca05663fbf206d9b3b6087492b7e16e7e0a5bc1a46b94c9e292a6509ecc140b63a710efac14844d5cc3820ba9f9eaf2996d05582b669a35a390dcd2ead4928f035cde7722cd1388aa40cc329825fdaf122883bf1ef8ea630112045fd72ac455e4a65382c2f5f5eed77b4dda3d6b8921f0315710d6f8e9e225421153dc546f650f5d690acda7f1e9aa7683625b2e9bb70875626f09a402e924fa337879c33c960660e9549e429346ea142a068ac9fc104b187fa17fffe3705a61843bee01cd7942e031198cb9cfc15170d3bcb01fd8fb5b6af68d361484679ec4c0d86661510720f539297f2de2207cc38c8605f230a5e62cded7b7d2911ca355671e4feb6412de2d62bc2536ddf0e819bd423265b13639e18d570b36b27d81257d45e807ae1f286bd4625ac5b1fd2fcc409f80461aee224cfcbdbc7a5e4a7f2b9b95832821071f86d1e1d3fa4b0431009b07ed03570cfcdc7b5caf7886174cea044771ba87304ae9ef1896becdd14229d44fa11cd7d128f18f6ea928347d9d4d358ffb0e844b090f3cf51e38e4cc2ab03bdae0fffeab3e562f394f64b1104006fcac6af72b9ef63451ee0e5b283722752d7ccdb51e9895406a07b327b264dc1af7e6cc62566b2aa7b4b3294d2bde26363f74a9e6d0bb532b7f880fde66696068ec36d7cf6c7bbf9a3d6de38faef131eebe1a8fcfc7cdf738fb343ebe1f97cfe3ecdb38f83c6ebe8fdb77e3e1fbb8f93c7e1f8d83dff5b9981b7cc6f75d1dcb8393eac7c031403063d0ce0892b13241a73ccf14d0cfd2efec9a0fc711ff7dadc077dd67e38a9fbe5ee0bbeeabf1f17d9ce6cf6b0e5c97b88fd4302e717bae011d5069757649b51935d9537251a9b3fa76aebfc524854d57336c68b86a32676545e1192b3743ea17b9157afa40641a514d5bea8852a48a72b8ff3cc32ff6abdffebaad95d004ca0f76cdc7bdc9824b9d44f93ada15798f45e9b2d48832817a34f6ff4704529d50aa92a7729cc17472a94c9eea08f0f6fc6a9738d525e23ba9687ae3f3d14c10cee7d216efd83c0f64f3fc38be113ae21307c121726a679538640f58969e238b2022b099e35dfde7520b8a2435bc12bae29375eb38d80415537f55d0978c31b5e0bb369e2899ae8c6d0d5b5ac10ada047cf93d689d7eb24759f92ff075baa469c475034dae04748f360a2b826ccbb26e23e49a7453ef155fe0bc2aab7e6c94e27d26475dc53f6d579b543ba6cc1316a714b46bb3e6e8959f381969ea0dc46fe9a53a7412cd70edf80525c512eb2449d6d22feb98b1e3a1f9ceebf6aab876019a8602617449fa4bac05a53b945f3c9f1c0b99436050825ba9fda93b196e0139b5501f674e1928ec62d1bd5bf10c19278bc0fdfeb067c3ece8ea9bbeddf172cb7ab77fe30413a5dd156eaee89672e322bd361b8cd4d97987f43744072a545d06e5b324f303a4ad6b23ad9bed323088a63caf637140d5cb094b6c35ba2753fa93c9a4c3b21ccc32b818b2c3493d80fe16e48913af070e69def1a3c54ab1a5df643f8e105a5007795d582105205625264c9aa53deb6ac46c65462b8ec2d445ac2bb880df6a9fa83cf49b677b90ccc721a0565ce08c5c698973d31abf76ad4a01d89ddf7d5137d3173bfd386744e27adf78a9e8660b68c0638f89864ea9c6538fb70d1b7b840cd991523807754146dd09101dd21fad2bfffa100d5305ae676279a36c0538849c3d81d3e467c02ea2f803052634b06bdf60c247a0fc696decfb373805095a5bb4dd8098d654ca5a7bdd2b191a5764a72d0267cb91083a960a87afb8d06f8b4b089480b21a15d4441dcdcd90f3951a7ad2c0b8b3b26d60a41270bba4164d26287438cda0ba1bd83d1deda30ee3e474c70813ef4b8944db7b53f88410674e41aec8fd7a06c3b9fb8eb4c5fe970c73ad694a837ff7173a3a8a7ecec1662e614b0653cc97efafa486f25ade2aced3c2ab1e2fbf04e69bdc2229f03af60208ad80c8a136ae5578f082beeee21cd6a0e82d7f87b790e206d94784f74a7072f421b5c43a880f0727ef13a580c04c72702f3ba26ccc1150d15b891ecc10a92c409c5453d236a7a2978e9780f894d0cedb528ed58e6f4193c1112f7d012798c205bbfbb065cde929b367324dc491009cb3b2edb9dea31f6ebd9f7099bf8fc9a780eea243943e3ed47e2562ea355801222d297a9897ea8b74913ba009052b120c09581243bceb6e15783527669cef9b308a09a3d32e2f4c944de92f01a175641d3e355ab09d76054ebbe7fd0cee79be2949c40602ccd08318bbacedb81ba6b33d65c9a22df735f2dec86c4cf96b808615288a62fc130796beb7a84e6272c3816fe7fc632d8aa29e051337332400c5cf453a206734151f83f9ff2c09c6d3b908f74205d4928a35b777b4960993f4688d2567678c6b4d5860d3cbbde6d01a8c749a2a2351fca5559cc250165cc29fcdbc65779583cb9557fe560a4e044127b5906345f34dedd865a6a89f6360f4fe364c5e2135c5e2315a374ea55863fd21a82e0a2363fd990a2f3d9aaa44ab6f325d25e96700ae41f0ffcacd382ad1c6043fa19153a286647c02d4c892fa8c5636b2acdc9f94ca7d86ff160b28b112248bc95292861679348c4392f5ba76afa2bdcb25506d705ae1eb94b6585aca22fc75488233fc06f525cf0306dd201593da93ec42d416ca7762d5a23c1a6b55f83a2a8e369124e3bf491a80ba4c2f4e09a68f6c9d691f1f6ea8a9cad3aa118dbbea5ebbdaa77d0429314c60bf3d2cc766f9b6db3ea2579c2ff5dc1d4ff96b73a6dbdaad7b9f43dd3319dad855dde51f8585822696afcdc1022b821d584aff527ded7f878e6efa5c58f08d02a811b8e4ed964263ab5a8c73cb85b4fdb006546bfd2a7bfac87c3761ad52679935fef6313f61223fccfa8d21f27aa575a6a9062f51aef575753bcca8277a920d0abd3a8cf895bcea0279e083352dea5be116dccabd29e9f9dd47dff6b43050c0858e6f198358ebfe7b6f738a9edec4edefc4636fdaa6b68d35c1dc2a06b5b2234bde35eba58186c7748df5b8d3534112dee157dc9063d0d0902317139285c9027edb4d9e45912a3dc9be6ea6843914849c49c37c85219e1a61c39de05bf8b0b530bd6fe888b45fa4bda7010eeceb8597688c22ca4f9e7b6c5c3396b83c386368a9423a14acc604dd1c425df08f63e840c2f3401cb9a0f10b4560621cc58455d85a3eb21b0a6e6054ce9e15f8847a762447465c2e8dcdd0430d0e8b291a4938bfb60090646ae71fd67c8dcffccea40f49d236331616194990d32d22a96f422922543f1f90edad5f7fabaedd5f4382ffc59061304281e0f5441462fc5ea0b75951f35ef99440cbbb09a4a00d3b9833c441a1bd9f53f8b15c3fd8bf9bac179eea1243cc5d02d97618a46739c8c14390bae5b492751605af761dc3a0968eb1d15cda83899926a09fc771fa7541712304c8c61c82b68a59758855a9ca3eeecdba167f67ba3f07bfdfdf667f5103b8d33307cf955457d34a02c3c306f872e235702ca1f04bf093134861289f28217c013a656a13f8755024f262b5b707eb24be39d870a277c71b271b9475d6c9398c74085f96ec160c173c5533cedf27465e59f9fad8a16efaf3209feb32eb822f6e3eb012813f076a1d0f59b2b6f9feb995b039c856d824f3b1e90ed5cb71143493722b02b60e69aaa8e73d618dc3f6708ce70dfa4e658be1ed8370c13743262f7abf68096e681a083eebf9430eeff5a7ec0f24fdc39866c2302350158340da4e5e16e3a65a9bbeb04cd2f77273b0caae511cfac8af52d9bd818d70e17acc82dae204b44a78a523ac5bbe34cd6675fc67e111fc23b44bcc320c19f05ecfa0c18995c42d1e895a38da644bbb7e0e4a96fde84b57f84e4637ffc006afcf55ef45dde6cdafce1af866a8bdd668b782e2704051129045c22c7019b0fa4030847dc7f56583f294e141c0c16f12d69e538e0e4660618fccef68f2729e5876e2d02790c6c0e0b801e0a181583b4c2e7736d6d74b872ad80a426bd839475b1b049cfdbfe13a8655192494f2c5bd1c025bd5da89902d49f000374cca5501982165432526ae8ce4792ae5dabf65b02ddb2d6506543b9ce2fb0c108c1008951b43c9ebde834067d093543c4aec981f3e88e53134288824786dbfcbe0444560714a4a877a74604311be8fb9afc3a2e6dbd153d3e94b774974e9d59313ce3c16d9971b6d097d3b062476f70f8b9ad1940879c97323a1933828650de83b06d75ce57fd7c5784ccc7328aee744731d7bc380dd8bd2ac20234f71c70ca2c8dccd74fa9a875e42befab1ce132dedb4386489cbec315a551ff211da9eb356be94349baa72c038371bf50ff7b264c0971e44a83ff5485c096245a8417fa5959a9b141b454a334b787d8229e6905788a37d26ac0e862f4b97b422087b6bcac65a9fa8802abde88229af2e612b04942fdaa802eebf472757ec86bc8acab76974dcc3ca1f6553d49af81367653eba28298763250ffa0b586a44387c3ec10ad48da3634b8ef29472b20a5ffeaeefd2895e41ed97cbf0704506da4c7739240f6d87512d6611779980dd07e74374b46e4993d4f33abad0bcbef36feecb804fbe20ba32c0020c2188c25d939a8ee4232bd13a3e3719b82d72a56a5bec35aebbab88676320501a399f4409ada8c8ce4ec37967c2f5bdab49b0e57b4e3a2887b74045ec73f72144a1ce3125bb570ab8cfd71a605dbf77240d08705677c4940820502751f752fa5aaf0b2241b0f0fbb0b8acae5bf4320fde3a93457fc5217aca0db90f5139e547d3d610712cbd6ce47eb207bdeba614fe8a9c1f212c8a1c2bdbd8d4b835fb12a80b41753ad724461aaabc0790896397e56ff36cc94a3e607abb695e516aae75604b34ac498774da1dbd559f9a1bd91054238e64c729c409f5fdd49c029bb9c01fb76dfc1d8b6307d2a277390e493fdd90211bc141897d534b6122a4efe3fc243361269a4090f98ba74832665832c0941560bf904e3aaac6dfd2acb6935e07cd42ac55e3e9923c30680bf42d15c2b15fb161a7a5301ec3b0bc1fa6141c392a6da260496d0e8ad8de6d5c8211c6e0bdfe6b85622e03602c26228be45bdaddd23995a4df361e8170ff33cf749d884e38866947c076ee7498a86bc4a55fad1fc339a11725788e3823c0ef524cfdf12502fa501c851ec8fd97a8e768134ee30886cec1d14aa3aff70da50edcd4b32893a994990b1d449e15b6bb8c8f9dca8657a624c30d678f946799e4bb3983200cea73dc5cf19d11a969103b0b24cae67aac398e40cca5acabed5853162726c3aa7e34986a4486df3d5a67d85fbf3849bc7750ffaab625ca22f27b73960a329534cd60dd3a0cba2620c2fa3f09967af8075ef5e4c0f257978f0a2f5963dc185a5ea91d4b31addc99baca637b41ae864edb6377295f3aa0d07b46722d38e8db3beec33507542c2ae3ccc4024626d55cded18e0a96733c903a2c084876309e3f00fc307723f4731d187c4569dfee2c5a0f0fdbb871690758443f5ebc363203f5427c45e5448f779321888ad8687a907df97082551aef75bc436d6e9bfc53293219c025f5671fd1af0455f004e68d7701ac7873de392a9c1aa8ad089533d831ad66312e1259bae1e368d3d1703c764d285b9310c40c88f1274456bf641fb3d996ad65919d77c1b90be006e6e18bee4dcb1ae2ef31c39980ec9e25c6b3bec4460be86b9bcc1bc0a476f67ead41deac318515c561c3b1b7f812fa5548a3fbbe8878625a21df4f26a759a2dbf5f2962adc7b9074c28e796fef72f5e069886d9bfdeab2da21a7e084fb043a77d526e6485c6e2f56c75d2fefbd925803b8b37dfbb08a3088800b5b245b9ecb320f775df47356c466f9ef2989c7ed03b626e3f35b1f2c01117892aced75e87e8e965527cd0a9507e33a2f388060f78fa47a929e7441b8eb1fa4fecb63bcadb881d08019ea9f69ccb5073d93976f1f2fb39c1f3e64ef5bc2e5235657f9ad6714b99ee19afd4ede4dbbae769ea84b4da014e78c677d8d6fd98d45250aad594564a6bd83173318d81a8c82374deb7b86d87e05f638d2c3b18f574f4f9093fdabe5d3042f7e439055f7441a6a8096f2ba6cd8279d8960eb7c3461f7e190a4e6adb3392c5a438f565ece17a78e71d247bf17bb5e46e64db1044b24f70d89cbb4b2a4104f1689d957b7379df4d3252a94363418712e7593a1b604ccbc1b9b4f9475a56402b92df41652c6280d3714f63fe6c84a63e13e859447cf1f7a83efc16e20438f19ae14ff580ec736aed6c9f9b784ef3d9c79e2c9ec501f91e70f87f262158f7b703594b3b1a4df0539c0bebeecd0caa13b3a1ca791eeca163a45327579c1ee534cd06869bdd10442d11c82d6c311af7968e1dbbdc2926840bad24e9362c5c74755eb74c1efaa6d84bb1a90805d0c5aa6b63c6ffbeca8e17757a075d9725e36976756a60931f852f8b2328909dd9c740a0cd3c1305a0c42627319610ac1535a4685ee0c7825e9087218dde845b10016fa5b025bbb0217a68bb5e10342bc246e988bafb872b144d962c91ced19e7672b1ec424813e8b09c85025f0ad5115519607530ed757dfc5089f33f419d8f5d63514df6cfc51335bdabb604ed1eb29ebbf5d5de54f3975984efd820b1a1b818a4e5385c7bceafc4ab0fd1ab703cbe71d85a577c7fcf711310bfe4bfa997ceb5d20080ffb3ef312d71620c4b3a274b23a22c0f105db0a32fa0070fee82826f731505d8ef7d39434130fb961d12dac19f5fd340833e6fec8963b051da5e77ca6b128e4e905443f34547d1a407bea6997e253ab33fda64a263e23b23f01e6a9904eedc0eedfcdeff73cfd8b64ec21b72f69e31c9ec6e0549cfdaa0e99b6644c51d45954a9666ffea4cd3ec67b3baffb9198c6676592691408bf311a9072ebc4b05e5ff0e3c7aae3489b036e9ab25bee8842f16273342d04a3b4858605d637de58573caf981dbf5dde16cb767018ee493a12359a367f0fa5bb15bb556621492aeb5fe1c0ccd5fe4d30ff2f9e3350f82d93d454197a6c01735bb8b0ce83a73628a521a1588bfcc3cf495ec610858ebb9cb54f9f0b6a067a079859b506da6402d30e9689346a2890fcf8913f4acee76ff7ea862950f9a031583870b435030657e2bc0ef13292ef27b86c8a98418feb1451b7ba35ac788f6d1298789b6881a5b175541d1598833dfa98b41345759993978d478e9f9c3fe39aa84e3386877f79b5006e5eb8e9a2407fc45ddddc8e261508066c4b1fdaa44820f1f9540fcc4a66c1ec328a252863b039350465684680d0055493a3fcd104e1404558dcc944003bb1130d79e1bfb0a8f65e1308339b0f539520e9a05dd86b8525c0afb586709589ae10caf6a68a73de07ad284358b2022510c1b6cddd14980d53d7eb71b4593d2d09201c92bf56e15f9bceb9cbb847891c2f4d9945a5e7483eb583b70047cea2933db3beb2365149e1fe14acb61b43f3510b14c012c2630d18823bfe9345a45290dcb901cf9c0ba2143d3beb7c929710a3a02202f304d40c26cd4bb619c519e43534f2032e6d16bd9d821c5e0a275b1c4315b93faeb80ef5bf08499fdc0dc4a41fdbe704357f2c371f7fdc05928c8de11e31c50c6af2859f488b88d5f9286839224e7e930ac439a40afb21e323df0c07a003fdfd4d74963149a19cdf92d31561d1c46482c0cbfc8588a886119c60bfb1cc94862f70166a03d3ad3288e9fd04cb1b22ecb065e6fa8c1303834272334985076e2dc1af3d1a31b25f330f3c6838a11920113f3ec20cf5af0aa63ebc63c9a6d21259d333b3a150fa0a6ee39fc5181895612388d0bfb7a01bf0e24da0b0393b08c61a13911db27c2fe4d17c78ee26f4bc007a86e94a1886edc902ee07f4cbf017e3bc5927cff4e4316673a46794e992adfe00fa1743cb90f38a409d82c7c062e25249c26c8c5ab32670c5b79e89666ccaa123346cf62f208687686566434d7779a28674039560ef5ae79b09aadc4250bcea899adc1af1846c7088a4dc94ca3a3e0490c0d8275971cb64710c72eebe266a7b779069fcd078708e6ffd1a12ce691fb981d2e75129027da921ce92ea0bbe5b146ebf1f3f11312e54c2df1de625a2b1bdddb1f0b6729956a185b3ed72287bafa9b96e6e5cd3dcb1bfebbe9a321bf8006d3a86d1daa4514756d6626afb0b3df9a5bd85225bba8b0579f53c5d73367deff12238ab4e445164e37891d0af83690c67f78d90d44ca7ce1ccde0cad0d08fcfefb7b013df089ce43c006451b955c2259ddaedf6809492b5c99cd5893e6b220428cbecd74e89b6273ee74eadf682fd546992f97f49a0f031196890b5513c72521850277e7c091d708423ed18c528b0aa1c6941d151160516e7a69fcafff5ee58059cf668749891804388114751afd95d61423f62e19045c78d6467ac036babc9aed214d9be9ed565475d0562a581db0f6d9d18cebf9cd0d845f598023280cd96a81545df7852f85c24a8354d2b90ba707e2c524030ba8d0ffdc370eece9caf793a20689a1e77adf7b81a6ca66c902a18f1e9577df1e28a588ace6ba46738ad97f83d0da1b11c190a53414dfc3403b9fd2b37c12337fc11fbd35d729a959eb65bfaf1009f3d4191eef08a65c440a1d990bc8aeacbdaac7a2b409fb572da886a22176f125a8fdf010ee59732c6f74734fa6c11563c7deb1b72aff3903c5f8db613de4705990478a5c70881c5b91e030f30abede23e70fcb0eef078ad3b8d91786a826afccc04c4056ffaa81c5bef0d165722b1e73852306681953ec568c82d395f6c56486d244cb38c758cd92a9a9125153eb8859fcf2672db1122b900cb81b13235e59e48e7f9d4c54bb648ad54256504bcb3571379b024332dcc85a9a336aea106ef67ad5e83970f0c94024d49d47256e17c59df2bba9bc9207d54999cf467fac67e8baea6855fedd933b8a45c8ce6580ead5c0d15f305c591286f4f8bb0ce6d6dac0b3d1f1042ffe2b76ef8deb491e9e975b604175a3f408025bc3c1377689fc1ebbf2f1a84547cb551b522c068f16d8f58246d15cd28bac7e7f0ab6d81f8c9ac0af2937027a3bd591a0145acca04eb799cb50c9037935a43b535f94c4568d1990d8f553bccb80c25f75a3ddd7e05dee4af0c53c412d8e4d5a6f559d2fd45264a6222fb6fa117311bcd468e3d0fe8e061b10b81301487f3ba9efe5f02125d1f06d2aa478b090752d38a29c7126388fcf82ac3d3fc2cca85014e371d7995d700ca650f5560ed7b500ef38d714691d5055dcb71665e8c9583a28d11b97c8ca4c3e88d9237cfa9e10c9b693a8f32c66d107cd4315809ab3d783c9f1b369bd2dcd10e70534f58aeb28deb85d4eb6b5f23d3a09b3e262e445235a1406df4ac09aeedade28856ea6ebfd110aebb3de325bce59b90de3517cb62a216ac1359485d9af6a09523bc4c2353a0f053f091cf75d240f3f43ab17600828e1f1234c82964168b33387d93a237cafa1d5c200fc4d3c8067df1e0f67eec208a0a7708e3edb37a48dc7b3424d084d58805659313944d920f26087eddcd95f2e93e072fbe82dcea4117d2bb6f49f0a0d8bf641113011c08f590c2c8ad079c603050f54f73d906531b2875a2164fb1df039a5400f8f5e2d9320dbc39e1e10d9386ca487a2e1f106c17112cbb2c1aceaa707e3f026c872fd84bf1c55d4a817754fb0c0ca357a2d42eb7447f8ccf783f447d8104469541b76557087e84e99dfeff4f22d279cac23139d8618422a60a1b12118b19dc6cfe65e0b47379ddde2fa88a6d0a66e2b2ec26eb26e162df66c0670073f4262b95fafe612c01fd974abdc5848722539e79dd1cd8c7e354f2bac64a4182b926efd6b094bb3bcd08612ec6d3f79429b69b65cf04aa5c53e75c086b1e56a16c104fc1d15cc0291b92a158269019e8b92ffe2ae35cc590022fd5d3107d427c2ff341d653e7371c0453faad52d2a35b88838695aac1243cf2e01daeedc3e321c850fa3ab9b5f18440fae7cbbb2c63864dd2138673c25fb6c7c3218662a16c932245104e76a589fc9024d4ce40199bf53284b5c3ce1b9a2d21f5ae1a265ba772f4e3094b200497b42a2c2709c19074741638a11580e156f2e7a12ef9e12479d6c1317bf5728589be7f7374360620d534fbb0bb34620abe60752acb6cc0a03554a5c702b1959dd2fb19194213ff80f2bb3cd89189afb8d53bc7ea4533d1558ab62d0c5ce070ab72429d19f8890ae6bcc5c58a5b6eb519aef08f107c14bfd05d2221cb1bf3b4008fe661dc95e9c5c831b6497d76860d4b67fc789da11e6e6f46f5fa593c04d445475b3e7e397c4c06150133f139a6e4d6dd95ed164bd6aec5da31ffe8756856714a3b200215bc7c21b2343dc7ba5357fdb508afca45c79e47b37f07c3ffd478ce4c73b217f8de49c67f9ce97ac97f2c86b12df00c41f258640fcea69d4135406c02c4c3de8b05e91d079c73357111c20f72b60c908b384dab724a611ae3d9935304f66c1bd00a59e83a682c741d24cbb993681c9cbd2c9e1c2e31096428befbb7a0dcbf913dddac027637ae2da81e4fd394fd6abc5ecb5dbeb832bdaff5b52dc4c0814782515c5680f60c410581cc0869b5b45b8bac4a4fdf8827143daf777d6a4ea6530c9a699bce3dcac5d24c19e405556e04100d28247a1a15640ebe0b8cc7585da89c666406a9ffbe3ed340e630eeb52980d7810758041117ae137095bd0d1906f45a00c05c2ad7b24cdc767b625349217333fd53cfef851808ed3c5b9e4187370ce8657201750634f5240421f9022531a072d819721414ab625e430ce814005dd15e31f9babbcb6a7b5302a66c06b461241d7176b89885ecb4248d65b6d981c82b16cd01c50bb4abad4907abc9333700524a9db219d0b2ac02bd86d15a592aa06bb41cc48348fd4ba8e588b64019d3db01c0d6269dbe43844a651f933d21da9980761dee901118ac23f797727f6d5dc2bee0bfa8306295c1991e97cf42266883060aee706fbbede3e75c8b8161ae6e251ce146501e8aedd7dcdcc4ab5a7bf6f9ad178ea9526c2fa3b755332155abe523fa6f445f6b79d4b74a2dd9ecb68a3ee0b1198127262bbf889a71f98e630e33f970512486f31d9f4815957c2855b6be09db6fc11cd74a320f37494c081dd601b67b4a4a2b876da0af150a3e473194f5f5ae63a0a06d41bc2b7cf3c601b83fc97d2494c8a08f7df56f0512f7f649c1a6e6773e7a49aa000ff2b8a0aa937233593bfe0054894dd34fa3000c3bf3ac97fce66054e4ab4b7cd5048fc38f9e769cf68a87e7ba1f285e7573232f765942831c34f1b84003f2943e8487b66f11d6795cbf0c6ed307233bef4708087438eb6143ae4acc5c698d2fb96dc08dda7737ae7208aa699795368a7d50eaf4b63710b5dab95719d2940c3a3e4e3b43cf1690c0946ce196907f637ad6c42804086b0b1aa946f23e79a66b434495741c26b1a1dae85020bae8b6eed9098ce31fd6be90ce5b2f3b98724c23fcb867ffec83d7bdfce2439a2cf85849f4d92330f038867d8b78bc1363e45603eb0be23afc3a97404e76d67744b89aa00a1bdb03f9dd9a7f6bc49366c1fbcb29e993f9d6db0cad1475d6d35d206f9df4b7ce71587ea996e9fbb883c0f063861dba6e7274c9dcc16d706459b180c17eb88c89208321c0e56b50885baec68cdc8304b9d847c4404cddd202b35420cfb358f211050ccf4fdb2600df0c5f2117f1c2707eef55731b1ad1421bb5cd20541b0ed16a68daa615c013e5fa7dad91060f8eb688622ccdb7906b7b11d191d4aaedab082803043d3135ca1a7a35b49498ed9d527ce1a05a75661e4c7bee9d24fab255fa71cb9fd2528084311232b61bffadd5b04f5a4c0b025b437da6ef1f7407ba3790b986d991b31f3e1a1db9959479d6a8d700cb9257e62b5b8e98dbf989219e421f9a40798ed88e67585a0127eb72280df844aa9607ff44991d3649fa731704de8dd05f6a4e060e1d321c82e20e88904b63424729a66b1a54df98c10514ef3b5036af547beab3e372268890d01555f385879bef1c58f00b5cf8f9865062cf0ae2fd5ca10df992fa4a98e704ac4e5776ed4203658d89aca2f446594b52bdbf923e88e6c9017d7e83ec7fb757c58e4fe9512b5017c075fae4a30d95ef1f336e13f1b961b9213bc92b20f04a91178db6b6a8d558e4f55b3aa7aac671e9cd46b947e4a4c63f20027e89b6a4a3e2d028c6af5ddbba823af35a180a05e291f7703734947f824730ceb53c288a540fda1c16349747e64786bec371106b2d6ccc5b0dbc8245f6e8dd4b75d42be8631ed1b75670ba532d39ff3be09e3f43e96d70c197d947e1b26a25e691e1c2b3086853c574113aacd096d66e11c6c446f9f833a66f7fc59952d5c119f4500a42d366f6527c5cbeb04fe7d4b0cc7374d10179cfd708a9176d31e1335f1e5bc8f5a2d72b3fb7173159650cb005642a5fc756d2a8e37c5937b383957fdc4efe7827157f9c1f238504e9a5c2f61d9b558840eaaaa7618798ed73be174776200f4b5210f1a48fc60b325944f2656b1d8c6f81eef4d63070a425fe6a4721f8d775674fb14d0f0a117e0a9006c2bddb25b63e44dbf9cf045b582dc5a9f67250820d2b32442c6c0c71c25f7d68ae25774aa11909f582d4e2448b5e0eedb89c1f9013f6d1a1b84123fe0378df2b0a889a1344d667faadd623ee98be2fcfc7d61987bdfcdfcc46377ca761d255bc092b892aa51252a4a1e4a0a076a7cdf19a4389c25d3b8e291fa5951bed1ca838cad64eda7044d3505c5d6be27841a3bc72fb3e340b6585734d1ca034ca552e9a734cd1283ab73ac87ecd8a048a65fb9ec243deab8d7f62c02347f82d69e1fb6adf51fc4529acdd89db35772abf20a46bb773b3f695f2574d89b5743d303e21e82ac3b0be578dc47380835b6dbf4c069dd088adc6853abfac96133462d738a1e62f268bd8afcfd4fc62c65ac1a759eeb73fc29a2c729ffd01d62c994da5a9133f16b827eed58bffa1eb0f383104e24399204c597e2d250b5395d720cefe60e70ef5bdb0e238b40084fe1293ce10d44d3673b8f8fe9f472402de0729f6c5324321484d5a98fe364d5b8b7a64f270b471545602c172d952206634c09060fe1dcf8cc80e6e70961e210598fd80bcccd0049c305dc37c30550eccd5b2193a02f5839df62400ce364bae899662fa4655deaaf1e5548da00ec7f1df68dba8430afbe1cd5b91960812d07a167560ad7ed969e2637925f21c607642866b6fe9485b4b0129b38aaa3ee2e618e8185ddfff2e0898fda04292e58cf978897c16442630ba13b168faf2c6dbaf315e410044ba0101acb7ab00de91bba4a7a6aa1076513252a2a90740144e720694252699c36befe376a23516f31ab2780469a806653fbfa831acd224c5427fc8907004ffd9525afe90649ad1a7654c6c9513cb17df93d1776f28da19a98e0016b09306c4dc0368bbd3b72153a9c4668bbf6c5f403d91d0843cbae696e130c52265875bbf5698060097a613c97440197c68ff60855ff0d0d723de086fdcbac4c673635343ef05ef28d77fdc29c0de59c1509f92edf2457b22269e3ce2bd3c584ef0da176fd006500bbbed11d7cea45d5a884072ab310af211361ad7c6756d049155aac8a54ceec573c3858d0401cf9dae33062d4891689b8dd72cdd00210468dd0f3dd1e3f7772e8032f5c7339c9926bcd577397255366391315bf087ebd47f1bc14cea91fb004572d11f7f1fc96d44ba5b1d76ad2b0ad7dd43c2d3807e1943a331d9c46db5aba8f8920c77a52a487ba15cff5b19db71a8173c0dc56e99f22224d0a57e4333a187684e61ac177ff4c9f3d3ad97eb7c20ce328835a420cf6e3f824a3b55b73066848b696c535f4be21844237d39de43c6c5dc02369506cad7da6a78e68f6c8a351115e20a2c5e26158d684c3380671d092ee7a5fc47591a59e5059f28c4d0793661f92d513b167fb88e70e3800466bfa12fe88a91ea93cc5f1fafe0680348643eaec33e25d236e92b13e157b8ad7f1c56f471cedd6dc626e46abae15207db43bde42a4807dd0899dd38ce772c4e1629538202b5f38ffe8b4e9cd8afe42898eb15aedfec9bdac65d40928cd00f7b37fc184bf5b857218807ad5434dc21b48c980490df7c056257004dc18221891b03604080f96561b2df0078e382edd7c2c8d647eb4d33e496ba6f5b8b7456c786e4ca959266b69d3eee3c6090966ee5f9150b5cf56c221705bf57f90e3482c6ccc93c21076cb123f46c7c7765285b072787b32fdaf354520e90d136a66bb41f89c60d91eb111ff9ca39e4a48bbdb6f17935c7cb506be31b5628f6a044953c4527b41f44df453d6d1a18a450d0b4321ef3d627f9c983755741fba1aacb7a7e40d308d3ab7b605d5de2d3f6ce8e66d1445e2500da549ea4743a2682a3a5fde56c1485e19c1cbcec3c7fb5d95936dc2805cb3d6c38570827b3889e7ef62e322f8a622234c2b083fa264b680f6a488a5e1e7c483db12cbb6736e65046b7c93fa94c3f2b88ee909497a51999d43e54732e7fe9359e0b41f1cf1200b9ce0e2913e9c63d6a6d78103954c23b8400310980dbf6c9272a87304799a0129a8da23622f170271bdc832f210ce4f289b905c6fa8bd26eeaa3c5e202185d78fd9da6debc5ce45bd3c7c3683b70f3682a82a909b2eb279209667cffdca4b1d4dbc41312b93031db52b9e352d9444a43205c9e1704344a031eeb4113790466d522a3d6c8283d7fc25ecb7887aed4df6c7e9c1a56225c134db2d04655195f65629a169a0d00f637ecf9d834e6f47af2d51b0fa0f46a1e2622e6f4f08a9cc47fd44dad10003d267370a64ba47cf186577b43c908aac18e3d11d2ba931b4f68aab77d8d2925640bb034a0d915acc87fec9d8d1bced73129eb6a91514a75e10334cca42b23f54e5d9a7f3118de94710caf5dd1c335226ab2b45bef6dd88b7555841bbe902f259814fb7d798c08e6e38fb14a76eb44881c9c03a1003e8f3d8f48af3620e8f0faa73885fbdf02ba76f28390869d9dca13ff9e2c9f6ff0b52b6523381a43c1129348757fedd3b197ade5910e5cc956c8acd75891fdc4b2c59deef4999cf8138974fe88a0ada849923a53fd89c114aabf50c64fc38a9d205083a0b117bf176608e59837b7acee57a9edd0c0c816119959354b26fff0adf7207c781414cd6a75b372b3141ec75c7c7b1fef0f12bac7c0a75482b66adc9af8cabc880ff3724c91f8f48e912e05bd7d1743271398005d739e3378e8c13f22e2da4715ffb1059926c7cc454623092821892be6b31a75bbd5ff5d84e353b02506440675a8eb22ffd2d2bc70f1b8ca0bb8e2623e1560365eb46d9ce19ba4076819424af6fae303e49eeb65440eb6a81cb6319626703ff119f368b703e34ea6a1a49baabdc7dccea78bb66e2df7e28c06413954f4a0d7a54fe7b8f92dcc6c963e2bdb7bcb079e6c3f4af38397320d94fbe4152ca835ea3c97bce2cc6ae494651ac1a9d6708f21a7b827770d1671f1e90cd9c128cec297f93923139f3918adaa4e84b901e7b419f8a62a47da46122996180a8ecff9524f0bb375402169ca7e56d6c37c692e55d07270a56fd8bf785489b940e0712a0d0c142dfb48e926a4b36d813e654101443ac8d895831bbca3f86732e066edce750cc0b70ff25b69b307339acd9227ef3de9185b184b25708f4653d334513f0b773677521db9845f05d76be48be4f3476f6a64c1e67f6633e47ae7d85966512fa25c43a6ea7a22b3484db6badbcba4c725804e962a2cabe8e392e4ef8b4f021d8395cd2f036e0a96721885dd7982d995666a89c411d837b35568a1c465a84d47cd8189a67dcc49c8685c8294001939160b33f021e4cc6a13d104bca760f9eafaaa4f8e5bf52fcf85a605832c0fa7d73e46bb2c659ef48762757f466216ebc2665cb8a55f27edd36a162748dc9904269564e11067f25dbb059eeb229e678d6b559aa3fdfa5517d187e32a5cae219826e3a0defb67c92f5de2ad04f5bb64c0faca1bc17dd23a1a6de8c0fb77822f1ee643adf0f5b99c7ba4c88d83af672f4700e9d4c3cfc697c9064e41f9afe8e045c189138f11a78681abb2b23b43f7d30065a13c2d8ccb02b9c60d32a6bfdc6835409a7ac2197006accc45ce8bae7b574d0fce7e6bb9c94f26fea09b4023e5cb3611607ef35d38ef1a9e161aa4848541b434d64730cd704699771b934b3062b482c1fe2ec705ef58ab8c8918469fe43752562a8ff8033a8d369eee925ca62c25428ad92ee59058c2951651ef615676a741a1059d1027001b2f928344048232c2217bead31b0fa3ee282a0bb16e94d0016c59533ccb3639156dc023cec9ff5da16081276093fb504436342b0f82e1202688f0de74f38314abd14db9b297258b55a2901e31d5ae174c9fd712f86d76482858db61ae81ea9328ab5885e793f338c8f49f0fc35d3388be548583c71c14d2f15ef781266404866bdeb082d1e534c95cbd2cbaace1152e137146d015e2169d3d372f73c657c5149fd3b445df5d9007e2bfd0b362f8823875a78ce9498d3c656710184edb6cc1ef6699951f905667ebac61f52e77c79b508237254c9de5c395ef7e0653b401dbb07720629ddefccd13034bb87772d58416d82d0fa14c3a6fab4968a077a034577d22c63169c71072c1c51eb4eb6d2d6dc1a6339ee50faa97ea7690126f1d976474008a68e3202bd20940989e2b12abcbb905e8c7d053960eb9f58e3ebb5cdfa2b1d8b9adb8c419b5c9f215f24722593c692c5934758586456283e030a1bc97829f9e20abefca06a95c8e6c00ed01c30e16599f61e344b01736d489a394a5f1894c4e5f67b679e474271c0474ad8e234e364e18ed11a4ffe855494791e4d6572e6d52a2ecf39a99642060a34caec85102a3dfba16db7a322b3f9be051e97b4c6f1022afefba7fe6eb673d480e1f7792ac5500ff16c9e0b165700773c2d785c61f392bf7efe30649d1e0f445e9f6e960c24cf2f038f99359f04f2ac9fe5cd86467a4d4d597045ff243194438fd2883a139164aca677e2204367d82dc7e780722c2a78c17b47b8d77d4076513861af0883f1a6c0921197a47602f9847db8872885d9273fd4177bf1626094053e76da531a893535a32ab99f62c556c129dbc66e0da34edb3a46c6368ef726a780538c9bd8ea555a581aa698c3ad695a30efa4d63709e27c4ce5a9ef00ec734954e21d4ba95696689de3e854c5fe509a47c2d75e8d89183a090ee62031c6b2edeb36f98cd4384c3ca1921842dc4a15b33e122be86072ef8c080153b03cb8cae51dec2db229db6b7244d3792688a78925fe8fbf78ab8db1a381e62ffc9c4ac183924e6e030e56b263da4e07c94a183070862f344ba01965cebd1ffa392a1419567625c628852241d3d013ae8638daec50f9f46d216d7381990ea04a18a25a41fa42d8ce0941155ec00e267e404baa41860c38d6ea7448a2023d11131f148b8c0b3baeb73aa58e0ac0929879064b4b05b55035830520bfffa8f9b7452507d4ce70ff77398b2205432e8f42a60f363dcc0ce85a2e2ad583ba108d1a38bd4705d189bee39c49786359a98c006753fd0a05cd9832939968990fae4fb73b2effaba867dd675e492fa5342f1cf12730cb116d50e2d166746fe68c96d42fe2d2138de69cdc5d70f3c76ebeee2c3f39f776111c1aaa8d57bc6a51fc68d24109edee97f6ae2c4b422b8b10581ab416180dafba0a62673a39e9677f322c7e933f2b5e644fb2cffe49f601ade1a4b8dcc0fb0513844e91b8eb57467211f0536737c007911e0fb6b36d1079fbdfc8ffeaff0de2c0d440dc44fa0717e284ae09daa87a879b871b52f175b1eb7827a2679eea030a7fa679662f84bbcf64ac680452f9409300efc666198b88615255a6cc4b0ac52e2a30da7250828b8d60f377c590ba6f443dec4596e877cffda0bf2adfdf14ed2e5af064d5e4f6fc6810918c536e9694490a947434c57d1aae4b4b25b5d0f0ae811fe665700ce248f6cdc0551ee59d03c07756b0956c0316ebfcf1fa2c1461515f281d47f27968763c57b521ae840cdf9e73b9ad9039273c0ac55f0244cb5c8d09de8508659ef979eca2b5b6f3bee8487f6a07dbd10e0f66c6d40df60f1069c6d0e6f4f0feffa810920debc589838f18ac87e87c25180d58212c0db16b22d6d641417abee3222d952b47d831f19623b23a5012b5502e5c8f7b4c60de4801446352043343b5a433d239ce456b8e7d6e81b939226902d726d1279bd5ef4cfef0335080dc8c83b8996eaa29392680751cdbb9dc3324296e9bd122bc88aade2c2261c272a448637aa0a59584bb0adbbe49c4f6fb1571a5e6519852ee0d35ae49f12c1bc436a42fde4b893c3676168f43f8f64057021e74b40ebee1b70e3650825f8ce97c59bdace6c0fffe80c34dc4ab59b532816292685c421a25b1ced998150da9e63c510cc9a0f44cee6af24a3df406f688c8dd816e7582e728b2e9caa3f6e0d246151ddb38f92ca0a7c349eeda9061efca96a4e0786347bcc85a0cc9dec291a0bdc0b9ea031193acf5a12a9016793ce5e28b237c325c118785b7a26eb2c0da7ed6d88e34e4bb75112407a46bcefe32ec8244f0f4a0ce3e51ee810861e94ba5e817838f8ae4e10a7cef480453c839be9648b7add61315824a64471d594ccca6a90b2baacc0c05a935ed9506c86e6f7fd5e2b9785740f41b325212e70d59b51bf44a1af78fb7be9a715a53c46c502306a0390aa18eb698fa03e1e15752c0c792c4df5b7eb8d843ac227ebda1b08ae141505be3a5c92ba9c72c96b1abeab90a4f327b7ef2065bae77e99c67a04642d50f4b8c7a9187a800585a8be22233ec86974eb1dc5396dc283e4e7b265442b5354d7b73a153471daf67cef98e6791acfb2026ea1ed9dd3509aa45b7a95d911f270d5dc047c72f6b11c857d73fd1c0334acd4f63ebec6f490740ce9a9cff76deddbf5e08029076926d0308da47ef51e981a86c8c9bbec87fa1d993efc842be6ddedfa2ef1dd12bf63be8a5368f173a6682c9cdabbc862a19c73617beb8fb52cd8b8e82c024a84313d9d02a046e88b667b5dde0d9835b94025955d4021d060c68e70ec7092d18df5925cd144e103dc3107b62b166a480025a7a085058e7da2fe49e88dd366a8cc515514c9061111093d64b7aade403f253f19e6e72d29d02b53af5bf923c1fc3e1ca12250dc3bdbf705c30e5ab726002035978b4e891db09688c962dff7da7874f8ec6c4e57477432a48c92c10eb611583a557f694ad009dc62308977f78cb1d04a236a0dd2bbd50968493bda533ce2391f1be53331060dd05060ecbfbcae31897eb49ffcad3dbf10f76df5bd9372528259ebc71bda0970e0987cf3a6b5c56bd227c121a470ea30263ebe82619dfbd4c36e4e0dd1eb2f35fb8ed27917201d34bde723550d2cfd8991d361a83179a52bd26d26891575189b28d8c642a403faefce1acc9ed0e1df259a9d84238b1de755c10fef1fa9e94673035df28b6089410d9c33f164946e8a1b6a4e32008b4b56a33e2e201234b10cd4f7df74c7181da60d256aac659175d0b03b0980ce4b0325056f6b26d6197f78a2d0e67fbf39f49f0f412f43708b548dab4f7d4bf80b6e6723f413790f0eb1aec688f4626aa9ca7852572432e34dae209601cb312b923db6532f4fa56228b1d95b2a9062597a1e06ec789cf4f66c5fbe873aab463f11664dba50568e7c56e3f87afd94f09a6632451780daf0b42ea68db3ef794577017c631c8b273b0ae8dc6a57e0da3e6ac92a40536db3b8e0d89f74f0aa92a6e982c91bcb9974f9eb4c1b75158ae8ac5b17bfc571aed361ac612755cffd9912eb3451a0246ba6b6c6dfbbb903783cda2c99366627778a77860d17979e1fa5dd1f6e033f251df15f2b2303ade8b124b679fc35cc229313c6be8dd99de025fcf0a023f5d397c22359a298b222950aae470884c04deb8f0224ac2aae08b78faaec8cfad1ec42016c9a053402af9594ccc313e744accad40f39a4dadbaee3d3db87dcc17edf79cd34506b313ae9595ec5117c75102dd40f888adc214ead654cd869466618a18e1a39dc25d45152e190ec6bba892e5ca272c009891026b3369162e6c3487c93ed20ee386656eddc09d704835ea3c6a08004aac389a59912f5b5ec04c20afeb76cc8530c18a6c19712d29c1ca377051bf917cb242b481440d7f3bccf20503c555301237180bb32357bea4d18b21bc2d941a25b1af46a4d0237361cb40cf7d99093cdacef0a152e1cd3232bcece930dc5220a6ec304040140ab6f9b3f4df1464c146aa5b24439fac1300966bf651e229a0a39d8749e1f782d4d011f927360637fd0e178e09629bdb9e2f86d9e0b9c03eebdaf4a3b97a03761767edd0df55e9403d542127b891e74cc9b4b049d2c3e813fa330f71ce1d1f5b89a24d82327329f0a4ad3a853c7aab0435658ff68cb802dd98578e17e0021828731ec97d7c200f6c965a2cca2c6d262d4581ff697addcde505f41a50298f104e5c4039119209cb0dcc8231d9c99f4093c54b60d81279b4bf55d70234efe58082d5cc5e8ca297666ecc03baea4fcc30312f93fc64ccb303f3aed6de0b81883d43d71e4ca6f8f42d667907b5e4cca36cf0b267fa04fd58f5223ca7e484ffa975808fe075a4751c3e4994866391b409977eddc558c9e894cbc82e966fd64f71ba7c02441feec9e2b942966c4a06942d5994b246f44c8f0093a1863b42b0a5f7c9a067f5f15691ed323a7da81651d04c6ab100dc330703f6762f03cb21ff44eaa20bbb376009037a66a0a34d1beca9e7325f5f4e3347928e58b66016587f3b27dd4594f9a94ce3314807c0734449999665077e4a0915dabb9186a5a5ace9f4d8ea67c8b066542230bd0f0dd6750d33302e90c9773066d6f461acd704766101e66447919ce5806d92b23d094e192c9a08564a41719ee840cc23c46f49f6338cb1864be18012a864b1283f68891f661b8c730085118d1230c2707832c82117860b83460d0e62f52f885fbf50521f9229abd705e2fc83a2f02c10b17dc05edd945cabae04153a7ace0a22cd2259673598f5c1c3e2e511b2ecbbcc519dc12e168cbd20f51bde95a0aa116544e8b20a185e7e882f673919e5cb8332e080317d1bf85b3dd822c6d11005bb81cd55ad8cee40b01a2f0856490820fe71cbd577f62a876c5e80c787e228123c1c4b2e02a6783fe83684530c5e40b06531a80d1df67f7fc29934c55a2ef0d93f149a7c3c79cf691618171ed9d838b2c8d1a6ac83100e970e26d1aa479522fce2709934857eb2279c1d365ed67db4566acf90d5734f430aedb288d6a926b1a1d756529da4ab1358cdc2cf8df9733ec919f97cdac261370a1dee097254dfaa0de7c80794467427c81c99f370f8435e9192c68a7a3a21601806e3d238cdd2efe631cfb882a37630da659f30115740c398bca95a729c1338fff825a5799167f56a2bd17dd988736262efd728ae6abac716e847bcf77cec4594f12fe9635f0db21c1bc20cb3cf567510f78139e9e09b01ba6fcb76b00f70293f9689641bae7a61efa26203719619f4e79ec5542ee1b7d929879c45ca017f5d289da8fa9bdf520c3e8efedfce9cab319a033eff34d7f4f652c1d3c963f7d7b9f5174eb41cee8efedccd3956773a033ef339bfe9eca2d1d3c9679faf63e5774eb4186d1dfdbf9d39567334067deb2724d47b337266882f621594c518e63944d44e8fe9b7b30b515fa6f767019bca40a6e23f102d44bcac67f168dff01c1d5fc094611402f9d0e341ba05ff45e5002c5764434a0ac6718e6ea73916a45c7d2cf73bdd4bb55aa4191a0a02f68b6887e8d71795a6006819040e1b38ed6cf5dec8b83856cf3ecd3ae6896629fcb18fb27c9ca6b1c8d7dd6d30e3a909df5d451a88d7c7ee8f08586f45bf9f468a910e66df5c06d2cb8a01cecba0e7216691a16c054e6a593b850942f7520b114dc945ee163619d1602053e3f3850cb6d7d71e98dfbec82e188685db6dd438126fc79d39d30920bbdfe10f1202e05b38c96455ed108cb81fd6b8dda957bfdf3a29cf5286fa40cb6aaa8ce72953e2ee0e419e2fad3d4fa17e95c9d52e1d2b9aae66f4e7702eae9ad31bb7ea5f5937d97d02745836b96e8f879754e8f47032c5e619123da3c322c99b475e6b39ef77e3c497a0f596c74f2ad551238fe785b4f632db4ebd9daeee583e8cebd20ffac8afb4690c7f6e37937253eed350b8c185b74478bf2672184674c866790e631c980704d851de7ab457093f4858573118b4754dc724c19328de522cf8724af4143c451b08fc819af97279776638eb1030da4bdfeaa4a70078416f65733299334cc9ec3e819865da9c8734b1f91dc09875180e401644d089f32aa5634e88916fc807ad880cdf164152f7581a5eae11fedd399ff7935a95e09161cb4c01357d0ca4ec47c13e9e8310848a484bbcb1d72a46d2544dbc3c739e47248c389a3b20072449f0d98431c4050958ed628e601b7aa7cdb69ab4925be1f17fb343c205cdc23bd8fde9c72d8d3acfd8474cc0aef94f7a7bd45a87631c18b932ac2444ae079b57b1757cfb1e45bf0c2e9883be1ee35b3703b65fc07eb34f66b1a48c883d282bd722ea1e7927b99c8b12a589f089d5979991fa3e3c3880c349dc5f1249ac40317832c3f17adabb505276bc43f1d441210230315add864ebe0becb685207fe9e8f219155a2a3a548831c547a948ff939eccf06601be9b054bb03f4d14e3de084f5a574dea656bd31a5b9603e1d86e748338ed598ef4ed7ec0c9cd4b2de797e25a38fa9da1bb4d1ae08e3169a2a320000a815b58ed2a1cfa4d4fe3e98dd4ef29181c3704babe38833b572585f72d492aea0cfa3fae4ba5a920f4332e7830b805ed69e13b7c015ea3a339adba44838e5dcde10d420f34d079c0dbf92edaba5697a00f3372b84455cff7d20093aaff501c551dcced18415302713451c81475af5d25de84d895a50f71ff94ab0c7f953950fc360acbeec5bba8c97b86a03d549c0b8b14d6758dbec6e2fe33a9015bb839c5f56e2cd50228463fb563c09ae8538fb0b1fc93595f0dacd36534330f29e15d336711bf9f135495201cb8b6f1d89b202c473ba43419f076569ad4b763d7e71e635be1b44498d891db3b1097c5e840e510520574fa7730259f041f746e5e224de6d9b758b2d97aec20a02b4677af5473ad2a65c99da5e47c00b8d37f14a86f49f89fa63830045022f81cbe399ccd166b4a40f20428338df5a91d00bd25f9dd9a1f8beac9591f8df9623d02395eefcea66e908d54c5c749dd1ce0f805038fb1563578cdacd4a5aabce1cfed14baa8a4ffaa8bda445e64eafb4e0ef620ebea8ab567ed3e937ea12290cea8f8e8b9aa6fcfed1e4fea7144749d851130dee51cf18e2bc6f2cd38fa5bd3de260cacabe94eab99fd566619839d2b2092df01f3e5824ba7267770d537b90c021398f011ea7b25f4dc6132fd5bff91e23600726ae41eeadfd9fae6b59959438d63cbaf2408cbcfa68a1f2e099786d3a24a0ab40a5e283d50972ba85abd84e68811d94aff7c4510cf0fba2b5fc47aea815dcc6cdca7da3cc5582c86cacb4c377f2759f6becf0ef2529265e3d6e8f08f42c120f8fb528d20b1302eae490222a0d5ef8b9077d36cc3a6d2b060fd35723fd7495fcd53d1798bc6a1dba8bf6a90de08a82ce2ed1fb7e02e7342bd0300f1383164dadb5972c1c332071e705cca87a3f469f5705561f54143386f66a2bdc57d875743d5889b814c373bd21bc1d6759aa6915adf73bebff0ea062245f10e0859f6c0972ea69a4233fa5b0b4365b32e9d76cedd379987db25b8c0bb1a369ff011dc4077dc3e03d6c108a73034fc037bc992c5f088f9e26abf43158e3ce07bd8f46bd01d183d653e337b67c157933c1c7ce3c0054652a3628407ba903fa7041f972fe39528b08b1e21e9ad828b4a9da7861ef73412ec3c0d387c191e61daf9b2d46205a7bd59806f5eb7aeeffcf1d2f54cca983129f8974db845f4493652d2604e8e2842d8916c2b91e6ea5564a0f5be0506298c8ea66d6ca82bfa64ad6da8375e87fd73bf4404121c689c3ffcf5e11fbff52a1f7a982d35805d74cab09555ef8302c7aaaa02febefcbf868e994bbc4f7ae17bb4e32df1bb88b2bc7b33aff558d54a40a8016a38a589559aed80cf858af0ea9075a7590bdfb75b7490a9ffa959360bf3465960b8f417694c2865714804eeeb66ceb3ebe34f8ff9a04e617022967995f214ad55d1204225f5da58577d565d7e5eeb7078ff4ca55549a0a59e3706835ee9c48801e7a4a375b2e58c3890904eef5ad0b5887b8a6de4df709408cb86746f7eb3d7291de6f40ec9fcb0ca1665c2caa8c3313098c2c945e228644e75a0e2531b300524c9c5f2560be700173cfe134b7437726819ae919cc4d2e34a035990ff62d3aa0280f03cb42ddb9dc1b7813a007e0ee7a31eeff79ff47dfa3d22f96fbe6db7d0d1d4f73d0ebf7691f57c673f0a1fb4695f46e236771e46c6231b574c5ac3656976713c18774abfba3dd629f4bd5fdc43572b8cdbb37cfa4b2b9f14d7959df2d38232a979a66436f449c6e9a768ee288f4f7bafef6b297765ab29746eea61b07d5f437ed9a6e92817ab59361ff5a5213edc8af6710eeb83b88a8159dc0e0558705aec434264cc925dd898ea6e20cf02259d6cdaaa7d2ab1fdf9c8931cddcc1280543eb8e6ffda31bebeef904db411356d9e34884e6cb3f1cea8ff77ddfd442af79c078030d14e4f5706a2bb6d4546fd796dc15748a91b0d282ba93a1293458d61a32f9d98a3d35e88dba72dff00e4ecb6f1dae41b9b880afb54c9ffbaba74673ba34976c9d89208c28e06b3a65cdedefeb2581d4f7119558a81442cc47bdbcaabfbfe02b14fda48a4a47c8015428ddef2d37f30a194015773e878fbbbff0efcffafacfa5a10578468dba62c7deaace027b99fa38477e4d4754870db8a8fc4c41d323e6dd253d1b42e7284bc6e781b9e23339af52dc0c9949df5f671d4daefaa3e3e1b3a1a1fb50f48de9e60c8cd3d23b34139773204155d1933eb8f2df3b197a667fb84ce515742d3990f63e37b88667135f8afde2d580fb9524d67e818aa7f3540410fc40385ec0575b8141c02ef311df8021d4ac09da51303a2c82d93964247df195704bd42fb81d72a1f7399b4d6a3c3ad4534a98507d04132d5060428a9958f0b6825885e6070250fc28e5e0a3cfe2663fd4b4f0d537150d62457f1b58f1a7dcb038fb904ab9581d8e707c6cef380bea221881a2df03cec63c3bc662facd5ca80db0cda16536744ee63cfb120f5a663152180252af4825641c1722ef98103728b6bad2e3a107ce52e9317d4f70aaac0181178de6c731f3977102f708c0ddcf5d57cdfd8b80c1077eae2b3b5ea6dc6db099db2fbfd15d3828360ef74be1a0f111928ee1acd41d05df7ca440b555d23a2051e7926c9a81298248d27b19b824844242ce1916253fce3e3cec555599b5e19a3d0ffd149f2348dab8c35e549852721c16d89af19a1a93568ce2811206db5cf8f22f950e552eb5de8b562940484e362d44f543eaee6c9dd00478952776f2fef1bd4529c66eb8ca9a5f6d878f0d0466dc71c6e6cced9d93980d82eea3d9c5016894c4e2e23d65c11b2d25d69bf8dbfc9d999c042b70512494491669e2d76245c1e5355601b8ddb4e267fabcc4394aabd69087d96d00ef5acfc10e4c48b36abb97971c67116029d25bae248c96f2b34538b4289e3f5901fcb4e2cb08cefbc6f9a9d53d284bab6d9f3bd698b9af26caf6707c054f5bbc6abe4b57e863f752716442a61d733f7b6c105b095d095178922cdb327fb8f5448b189a16518e18c6a624f434925db05fdad0ef82ba17a8664cc4b070b9af45fd2b161d6cce6c0a7206e807dda14641d374208801044c65876601022f3f159269485961cb3a0fc8850ebcd1c5a0ace7f4ddd2c6c51257f08f65ae1111989f89539bcbf45c3c21e7d5f98ac94d96101fb43b6e6d025885ce9b08d5837fbc1fbe6d6d9e14e8178031c23c0bf0dbb670a36ad93efadff7a3a0e3340dc410ad83d816c6de2da1447c41698d9cc0c2136d5db0aa9131771319fcb8f8e06c37cfdd6878905e5ad3b81344762fb828eb75ee70d2a20238fa43436092301b24886423ea034cc9330cd73318f4394d7c3de8003b018475705110066a8338c076d37033278f90ebf1826b4274a03fef5b1c330702f26899043ec987975b05ca3b344926e9938d96c194e1791ba1e426fe5530cf9c1e025cf0d31458c99576284c6ae040085934cd78aedab345e0c44560e87958d36c84c377048aeb8eb89586e0ca23d474ee6311c7268e2d832930192387907dec785a83bb9623f42f8545ffc2cade194b1ba681dae5dd98e07b821e32a7dfb894e68ac68ecfdc9c2561b1b9774a28ced37149194b0a7ec7e8b589494a55458e1a0a4328655ad11a73f41c13cd1dbfe01cd35aa673640562e5c155f8d8b837b0e44910ee6c2b6514345916b224d6616ac2c893baf14abf554c22b78e7992b4831fcbac458fd7964aca102cc40de2d108fbb1c4fec509b121a799c15cc23dec0f0e762f2cf946ead518ea25b55c711a7fe6ae7dd98d4770b1607b580fac04b77bd6e571f90c901d2bbb36f6988cc8dc94ea9a634c568c72a267499e439f118075fe8f4c66ccbc4fc48d8480009f707005236e85349da75f405ccc50c7ccb12bd6f9e96e8b1639ceed47e3c676378fab6360ce6e79a170ba2c0f0ce4fb03d17e423d7dc3857b6e4b1a8f16690891e2f5a5924ae13d0b791c4274d17dbccd3139aebbb67953b35eb17647e6f49bf35de287d66d04a40d370c31a6bb54a1362ced6026b3a47e2363c460029aa391c74e4e44e997ebf0cc384b1e8741e75c64b001720fa16e5fa712692a94837307df9f2111d8b27dbdd3c509c30f483c037a0c7cd5eeafd152f0bca8ccef6cde6071696ce6ee6c76a83ccc9269e5ee6c94bef1ae8a3dfe765ea08ba5cb9ddf50348ac88803ee9856ea83c5f7db60323acc11a4014a267f73b0a3072c2d7cdc34ef458402004cc0253b2a53bdc5655a929d931f6625368e98737fc3435b6c230ae6283a692f23183678c46be1b2531777c2613edebc720dd2563acf80f05805addbfeae117de6b98ee906e44ed731e3ef3e3bb72f84044ebfeb5411d98b5508b82065afdd896c44c71dc48489a75f999e2243ca8e26c81127394ee0bf35183d664af68710d187838d960947acac11ea4a1e77f0791dac53c4e2ff801aff0827d3b3254f9ae1c2c55d977791ccd12161be394556beb7ac7172c9641f0e7c4b168e32c337b29249d8e48991c0e2b65a8b4c382b0bca91cf0373d7532d82b9540e0b2a04d56fdda081af8c54d284c01e120287e328643eb66927ef3940c13b584c007eb6f8808132249f801493c12d634c4b624435144c2e29e0620773bb859fda98468cb5d7cc06dfc1c2a4d1a707ee020178e84ebfbbba0b3014601d15b60e5e9c4aa99a8eca9cdd7777e233ca90901bab011c92d331fc9225b14e2e23edd42a051a161cccc50c1163afd0d9a5d295ce09682689b83050a153af8e0465c664b4018b003128f25e4cccba06396645b9bb44c2780724cd217bcc25824a6abb3eab4bf7cb04a1b9273045e1cd74970567cdae59426ac2e929753ca9c267bd7ea05411f3eea960f34108f4dc24e067d012038df991e8cff107d494d6a99845267a83b3026f30a863d605e36d14066115a82fdb3fd81109f4d59b01c3bb58b9947104e0c79a158962b6a939b01beadcdfb1ae105594d44b9c8360fa69f57d1f92ffada161b59344a62655cd981ace47011a0c59833839b37153759844533e3a2512bf6432eb886d124af5d6ce729788960944cb4b12f2531cd0d2bc63f2e0b1da5da281f1fda951f9fcb35d23a53a11ca127553cc6d328157d52c065069fccf1bdd3a9d25e0c094b39753ae2b1749eccd739b03e974bacfb96802e092f1884dc2459f3278f35b199d157791f510881e2e0ebd29402cb62aa71baa1046986b65dff0a29f22484f6a2f5fbcd21232397a2cd3a747c5bab6cc00ea71d684a5212359e90e0d16e0d75169d1c21b210a0fc718f170630f5e9528edb1e48b338f1771b66e651a08e5f4572258300929b9288928d9c8baa2f701a9c3906c74250b8dc8ca4f4e391e5ddefe4edcba626a200be076b0b387a1f14d2dd6103cdfe0c1a88d226f683461e68f1946c57aaf6eb06526d3f2d790d837ca6f64272a1e46e6b36368439e367574e2047a3a5a765e9e8d9eea2644fc8029312c7ff9f86f6c6f1d2fd53cf442e0d2dbca4442392cff538eafdfe71738c89bc4dc12b7706e6aa4d1f92872277554c4b9eb7f459dc74fbaaed78fa0dc6ce3a2f1c9ec3bc4e7f12345b0cc32e29ab9f55833babe7789f1f111df6b0c8200abe1e30ca4de3d7db1ae8e8a1037d8fd626f261da9f2ce3f47e9bbc4a3b6ae3a1b3fcf4137bdbe20ac7ba835ddbf0b5f9f42fc1065c6defbeccf28acead8166c4c201ef8cca8ed08fc82cae1e205764bd638af235060523697e72b1b6948d93020fd01077e81360cc77d545484b3344d60abff7099e72688ecf644d8fa88a31f0194823e8848e72c14b7180f98a53cd7ff1570661b19f81c25e2dabc42698e7775d565a7c098c3c99d85c6722cd2bf74d0b07b2ce184a22a50fac55da482a8573bf9d49846e0e179c68e776f2aacf343f593207f3da89333f35eb13a1489783683e6b9727092c9f49bc8cc062a1123de210e7d9d6f964a713c0c0b8ee4b33b43006c397c6d80bf493c3675e62bd792717c16c98806ea7051095c406240a2b2bca49889bc61a74b852a86e62a42a3f8280d2567ff82861a47034cc909bd124320d66409ca8bad2b2a6a01605ed813f631d9542d7f49deeb0f0519a0599904ce803b6473fbc1a6ef2c707621d66d12afc58059e2f34e3534c9d66452a27d1464667a1407d2c8f0e3e88dd2179b60eafd65abbee086e6671ed5fc008009a70bb0027c88eebc1b6210e956a315d323a5bb0841aa0ac337a085a597136620304e098292c1cbc2c14f286152a988d806dff4b301e624037ea91bc4fa44e512d6f6fbab9ad5ee19cef40da12bc5a0d80cc3f0c777f40c9fbb555a69092fef46783489062fc15894b0410a285ce60178be49e6e838d7aa40abce9ae69c7cac117ad0657c8bfe562f4f159acbafb6ddd8ad7ba367ea82fd0cb13ad1124d39651e97658c0e11f01e9f87fbeb0e7f2b15b1f8944b3d81dc020eece15934b2a0da1c80c701b0cbb901d40201c41fc070abcd3d504d10aa080099021c6ef2b22e6cd641f8d6bfd0ae427355b516b4e872e2d6496abb1834927b047022b9e903a1041d5570e3c03acec8dea6d806c1a80381af836ac900c67dc106f7cf005fe7f3f65af7d4a09a1228d546d1ac92fa6820458b87731c1c067905ccdd0e44297848821c2f6162f3725a83a9ecbe7f8a3d892349bb2aac68aa42c21490e1f48bba6cd5726ac6790f1e8f2bce8ecb580d413352951fdfd8548644317a186db54e623e1fa8b0e6d4727dc7cdb8e314ecb0fb6e4729786cbef4aee853fcf1ca5e84b99be389069cfc80ef41cfc058c0ab18f96a410d1893fc86fca58938a5a649445db571e6075afbfe4892f2a6027db6c1d821c329a2344f2f2dc21924cd5bdd04e3b676b6fb7060bc262c3cdb89cd1ec3967d9c194c0f32113ef5456982b1e272c3838faa3d6eff04ab8ed8a0c4c7837dac12ce137f743dc8c7cdc9120ccb6e8e2b3ef928eb6d2b0a11d82e1755f0d2d270c38e7b7128de95ad156d4e560f8e9e73fc1c652b05b16459cefccb18e51d1fa04a57d861713298ce6f9d1f72b7fe04e23cc8cf7c1795f327474b2ee00b0a717ae03ac832081c994e228a77cd8fb235fcce599271f16009e2e51b208a5456f98ca40589a13082454e0c11ba78f76191a921435bb21db067747b7388cf4bd1667e0ec944fcecdc7576472506096f4a275e9b90c7f5e0397792083507a2e6043b29c4636cd6e71b59664eab523d615302866733849d095f0f09fb074225f53f095d68390377b2b9e024f27da2e1f933ebe2abc493ab2bfcecc037309fcff3910d980a809582cf28679269199fa5a9a719380d0eb12be0623b0395286c15a1bc5c72b22122c10f6e9545596ae8e5d2b625c0b3d5f78b2e64f797e81f2912d6afd05fe8569bf749a3863469d33e22580b0b2ab0bc5731b558e3bb8c1d2693b10b5b0424b343eb7d0deb4c720a7dfdaa610d3d4fd7a8dea5cc5f8022ca6f30e4da1c6e6e4618094b14279a6213324a3867d8b8c60349db48861bc7366682e0d152cbf1799a134fdf3a033fcf26e08150f03688dae3b32f0ed2e879673c86afa81f2943f4a901aa5936506171972f2746a2c4896045e79e68d1c4f441bf29135dc83994319f61c390d687d5bc76e808c686b416082cb900d31db1ac6174e97157c0400892141cc9564271a3a882d1f3ebbe12dbb6bbfc7dafa117ff046b42e8194d851aecf92be798924814864d10ccc3e8bad39b63d4c6874f7275be6a1458d21fe0991846833a2e82bd9c94aa585f7e69e3384d3c8421a8dad7a99c975ec44918aa93b4c1f04470d08359c2571eef7435390bc5977a4e0b43caaabe1553e2143f6e728692b8dd7e3920933fa07c66b1488fd3b9e7614ad6e7a929dd4bb091e2d3ad69f989b1465f099be0ceb003ac09cb8022ba2d04daa5b5acd56e2c31c9f816acbcf484f9badb466ca31bcae4fba8673a2179cfab68d87dc66bb8e1df6fdd33ab7837c1bb1acaff34adcb2389a96945b8aaddaabd3a9043ba74501a757efcb74b98a07ecf01718031e8bb3c572eab67257c38a8b09538fca85b888564cec98bfc4285c27ca9a134532020bdee0deb55c6ab58e34cbe9797c38d1ef8242d79330b9e1b8a6a8e4520a715ae0c763ff383156fb2a6e38307eec316c42aa43fe44627497dc5cd9c91b42b556a4d3c8bbf51b15f3ed85f322fb357e81a99d7aa6cf847955fb899e72d5c009a046eef06ff2a1a72d4cfff0019cab3ff76c45311d5a9fdea734dbe4f9cbdc47314f8e4eb45eeded2dc4a4701a13cbb9ba509328bb92b7df2dcc8dc8cd32dea0f061a81da28bd4cba0ae9070ff222deff24f584d61c854572f52d51c99950288d0d18c65e374de92385fbeb886bab36af92f0b1a1efccc3a5f15fb685ab57b18415225895780d4d788e1e02e4ad75ea494c0dc62c8ce3809bc8ae80c546dff17deb1bd28f1afc8645810963cfaf3ff974367ee2e2290d51a9c25c8c2956ff70c48b795132b91a80b85dca90bbfbb3d26b29ae8bc72ebea091b9ce7ccd0a13a2067a5cb29318c561b89c2d8f4c3b0c033f31f39d2d06235654f7c46d9dbb713c9123af36ce4106147c7933d490abe28eebdbd477c2bb21735377eca9fcd0be49d9d8f8b2f5379270e21e71e88a600f799dceaf141ae5d0ab1f4b668041a6152c51c23345dde65934b9a67340c11f79e6dd1706fd83ef906eb166cc28b1054f1e02c0a6b5f0ccb0fc533fdd121331911853cb3e085bedbdb82bdb581274cb766a14388416c8dfb699bf9226c9b3ce026f34c8b04d653f15dc0665a0325e4b039c808628d5d6d5dd2b3b77eb9a71e5e4d3445fd57feed5b684135e8d8c777374dfd2c5245fdeae0c575d903e575b9e9772ba028015d76c60c87b418b8cdd0403966167a3db28286aa2ffb77dba5af27f47d4971af0c62a6a557e9f658370e64d1661658ddd1904b379e56d88cbc4d879abb0f0479f4ceeb19c7a30692567368002cbfb46edb29253fe128191742b39d3b72c766dd10f2a3889a7ca28140c341b0d12b85301dc6cc2221aec5240e8ce9c593f055478bc5fd4e09afed9606016d8220092447668e56a88436272bba4b9e8de2c2205eecbc89a1c19260fdc67d9bfbfa1eea6e7ade90164bb7bc2a36c081509b4b608da0843f41c8a60bae84d5099c06e4ef4a3ac96e5118cb4cb489e1a0326cb9a4b18ecccceb740d0b0e6491e5ee7957c4901b559d4b09c13557eacea7bdf360e82acef55080fc65b5b6ce31236f58a77263bf7e9f5247c4fc41704a2411d5585a336f92893c316f249ca282d3c21838a28f8029303ddf4a1707825fba1449edc034b360726a9a0de5216fb3d08b6fbf768af802f2f52abbfa8c4fd3f33d2d0b884a866bcc5765e0788c9ec22f49e0635f0c672d851e48ffbbde80c108879cd2e9fddfe6ddf6ab29220a621198824ba05bac7f6bb4f8b2b3780d325ae5cdd9b56084c36ec5d936f13dc3825cfe0d08143273ac02288e7fe52809c2e02ca7af94b5a3098594d077295699f6fdf369bd2167b1095e58a68c10b5394f74fb22269b114efa7cd663bfd9dc07a16754054a5bffbe031dbe3f801749773a2e2c6ac743adf7b9503329e61d9a26ce2150e76031621d1daeeb6b79432c99452c504ba047804954f37b0ecc32b54a87dba81d6edbab3bab1fce05a817d184fb87245f5d30d3175bb6c6db43a4f37c4fa65a066ede5d06a5643d78c676cdd383fe590f51b6263871aba6e7642a1240a3551dc9c33a2e6e6d3047bce39599b734e146a57a81c34892d3591e2b5362698966db73b57450824908d18d45ebb03c5577bdbe925859fd6f63db4366629b78d526edbd60ca7b580818179818972bac4e0b498979717162f2d1f83050b162e2c605c466091c212a5328b11969f5394135291cafc9998485313688a998e2a7bfdfa879e6c70b7751c779f0956797b975334bdcb0907edb74e4824822ea7036c0b4612557d1230c1f664c323e0ef72d2b152aa77a624ed3594815053ea8e4f88708044fd6292da1156bbb7e686496a7f03a1ca80555351df6c145392bef922506db8ed911823959fdb1b7fe6acf5c114a9303e95f9f9bf989f9bf8ecc2447d01ab1cc5484dfd546639d9601a4e53ad76e001a416b48cb2b8c069593659502f709a8a9900050f5ead4c050388d6e6719aaa4fb0e407a0d6b6e2345583a0ca1100384d1500256450d48281cb543238643c00b5b22edb6ab893dc6454c99f72b270b1e132550fad6d3bc51e5a9b492893aa4864f93b06518e448d0a1744c986d6a68fd4d68eb88b43dad110bb9c6c784f659567bde210f44f1fe955ffd6dd001d1ffa6d94da434610c524f275e2105cf5eb18a1f2bf3854a3aa856a01d8b8034b2801d4ca543253b4603494b4e52e43b1d40d36372a5b9bee14d017774b39f540d04652c50a45365826826e931ab8361883ab8e71b197d84c8ca626a342dd4aaa48f4a9db33a56310475b8ac56231cd8ae988c9c75444b332934a498eb5c73580a05ffc893f0e2115684c5b82ab7e93ab5bf360da12c47124b8eae7ff25d7965c5b6a97d2ed569c8a4fb9f1157652fa463a52ade143dd6538c6877e5f915850a09f6b4b3108a9763cc2a43212cfeaf8103de1a520584be9312206a900b992d5e91a7f72ac6a2c6aaf3fa3591df632564e0a16a369ef532f2cac4d73716950ed8cfefdb5a1017dd2e4ff6351cdd3a466e8cbcffec055d374aa068deea5e6c6d3c097c24a92ba52584102435d29aeb0c42f09f06515c756486b00419fd9612f47d48b6b5b9aefa454fb83aa57871254a1402691212cf55e8530b8ea8f41587f7b0f6bc8a4bdac7a076135a01f23c1a038a1de4c65bc0b087705e17a904fcc62ffeaec7b521aa2055295bc38a724b892d1b9a1a07f4a9227a5d312cac54642f5732359370a228678a28d804e49308655edb5cc849a9082e81ad5958f2a42fd9c92be53d2d00b0554654ccb906908e2b4f4cc96216f19e2962198a2971f97a496a1961ee9dd1a5577589977e012ca46f4e35e43482f3f2f402d207879b51c79196a41c1cbcf0b514b0fc43975dcd253e57313f47349aaf25d94608b56c12e4bb245a9f44abab845afb02e49a797cfa207e268005eb972a5876b712f9f26419c5307534493b80e0abab059370a228c54982249534555c2205509d37d304a55ce585295df4fd0afdb240645aa06c434f0cbd74eb03af3e55431146be3cf2a36e219fe31df8cb9d129898daa9c3188a36531355640f9e35e2d4355fe07e58c712f66827edaeb25a9f69adaabca6f81848ab2c100e880feaf8a50c8ccce47f0db901103c68c4ccc8b948b16302f2c5c5a280beac46d262d9b323a6c86bfd3dd1101060f6ff09ec10f2455776dbcfe01af58a9fef007bc72e5f9a1ac4057ff1fb07ac73d247a355edf70d79dd0fccca5930dfe0c082184703b21f93bcd515c95ebc6d234954ac576401c971f1e0ef0d184084bede21d1c3ccdccea9bf8fda91cd455e35357ec64e2eac42ebab6960368604a7b5bfd83287555577dc5206410827ef45301e96c37cb3e1cdae3caaec9ae1d9e29a50c4250b94735acecd7f49f07690f48fb68afbf852ba87ff64258082a2091c2526b88132182052dd3f7c907124d0028341c0024faad124fe3143922fe0fdf327547c8ef67f5c76003fa4920a7caf7778da8c2ae17764b68f707c129325b3f7ceb88ece7e3b043ea2249c99d0f151014118ff6fabb1f7504f501fb7fa2d83adb36166f4df5add24ef3e7ff4b9d27a86d7dbac1bdfc3ed9d07c7be50fa76a33bd54edc7556aeabe97ea35c477f91c3bcbc90677ba316b37a59bd209c1a45eb468e1c2452af5e2454c8c8c4c8c182e5ca4522f5ec4c4c8c8ccccc0e89ab03635df3561b522800db6fea5711173b95c4468ecd8e053799f28a91f47348422d42fa7b2f3cb4daa532a64668e31461796a0def90d2b37a20a7fa6ee3f334bd741dd325cac0d7cb837daefcfbd31dddca07e9f25c533206f3ded419faa9a41a2dff6fa1cb621d98622901487f58dd7d05d3db821690f76f736b4bdbe1fa5f2224fe244eda160bd207604eb2e2fe23deed31e54c1ae624450e1c31f9e011f3a1215ba101f55a30abf83ba7ad740db6b7b41876d445c4b2aecee427e0821ec95c9016bf7f1a84f361860a800a847b05ed84eafdabdcb217eb770e5ddc2550e70b38cd29d57e59c84581e0c30ec2ccc53f002bbd02dec42082184ce42eca46a7e26438b6182b1cd7032a718d40b961475d1d262f783ac03532164f1f2c202c6a5458b0b9a6279818a39c970331b0c530c4d46f65325bbc882d3c016da057e81dbf550b05f8ef0349542e15e75018a06d0173c175af818e567a153bd8c18306664625ea45cb4a8fb1b0b6efb06e625cb5e58b47071d192a22f58625032dcf6cd69fb0606b77db36ddf98f6fd33d5ec240b34de42bfb0e2b66fbc95879a33be36feeeafa676e4e8ddddddddddddd1b9bf59cccca71c4292a63dda708a96acbd9a0a7f933b1007c260dd2645f0a9900a12887235290213d8640846400aa18c3e5f2c7777ca9dd07c9a87c279bb0f5faaeec6ccd57d94ab5f66fec684acd48db9a089ea376466f7ccda93df300c41534cc37cf9318c0397b72affebaa6451e57f00d8199f6a4fbe7c1892a012763235b0d4f97b83a5ce8ef5816d7f55bc33e4d7a96df3b718b518b56cd37e99becc48d5d378df84f23fbfffd238956d5f4e8e147593a42432ea3a0102c99109a60aa07c40892a9288708419ec122bac30831fc09c00842853082246a8984294281051c4496b8b48f0041127f041131330697db4a78b80527a58e295c48a2abc008a0f45d821a4e08a269c5812a50a53a0a18ba8fd9bf5a78892b6a892c99c4802b6d45a1d587125a9b5310d807a36c8794768ffe64898c21043b188253f10492eaab6090e0015b55cd5c5a0b54d2eaa784a1525306ab92a86e004ad2dd372647588254b2cb55cd55128e12bb494bfbb0b218c1a34c10d42f7cda4755f2a53c157651f5537e4672a183f7ba9fa0cbe77705d88712fd89c8cc055a350d2f236790537e18a2fa0b0c5400c14e4de0327c4ad5552b6942cb9bbbb635d8ff9a48387fa61a04647edda7857a1f9d5854c519a543c3b41b0d37afc9eb6c30c66a9d4476dedb443ed4cd33a6e6df8d7630e486d3799aca4bcf5fae9efe0b061bdfe7c54aefa6ff743887331081144f718638c9d674368d64c2bde3e4ab62a5fa3c3ea642c560ecf8e1e5258464f8e90927480c0deb009d683bf3ba57eaced58de31d0dab82862d1c08a6808dddd214cedc01e3388d07626d13dc61863fcae8a0e1e6a54c51a87d4a8eaea0d79e31330eb62581b8ea1366b6dba66aadf15c35a85a6396fc16619cc12ce81244c2104aff2e2391781923c3a424a8272245477e0fa7975bdb5f17ea01fc7624fa4927f008920ee8a0d0aed313ac7bd40188b11ed37d8dd65ad8effef5479c75a1b566d1fb59bbb3a717729a38c1e77d59acfe54e018c10caff16c6b323fb1bc25132773bc86e1eaad77e5e6f42280524bc7a7a7e807c7a9ac480d1af5dae223fbf5eff0f21f5878fca942b62eba4e84a3da5935245d0af915a0a744108bb2184707d10b74ae910babb7315ca68dacb4eb466dc139c90c03f669d659c7177776bbf6d4ee597b39bbb79c8d4248ee9c5d4ed34ac399b0d33afa6b38cbd59a3c91fda5e77c7f85e77773b747cafa6bb6364f728a46941c05031e7d4b8a5e5b3009229a468d6f3862a84f065bc813ce4eebe54f7453f7ecdb96e655b1a1c3f8314f9094242a2d47304885f53de7c2c2e720092e981f8680ffe8908f5e731a794cfc37dcae7211f7e0f8f73763ca4a9477b2e38a15028b5473c6ce8f13934a415a99497eaa173ae03815b9d4ed2a11e59230e4a6217fbf00f03f1cb238e6f8f6c308a31c6f8fbcb03eeecf0f08028a0df1ad507c440847e9b04a46fbe5542ed1b1f0f589ddde1d9e1d9e1e1611e6e7538a2b9a4062edd5d4a29a58470bd79811cb1bac32831e02ea3ec7690d085c4938d29a5ec1a66006e378ad786bb6b96b9944213399003f5ab6fbe1e0a027228acbaacf65880a05ffff011ae89fc67ce39736a34cef6a6df5f9611ea271ddb0c34d4e0e119746ac80650001d9e33c38dda0f71d47ee899ea6b74a8fd907aec9a96d337eb191299a669aa5dcab90bfa2659ac1c9e1d3da4183d39428a4b7df3c19e6a0576772369162c4e4fb12bf8471c51bb673083daada4bb1a2ba08d2476fda4e60af46ba556ea1bef689a4adf3492d5e9ca413f8aee31c618214b8611d01d3940f950b2da9b59426d58ee599d75779f43788cce10421c3b70252308476aba5e2fb7a03e1ff5870fce091cc42a5518b5ec8b6d07f232738c9c1520113abac71863ecf534cfabe1692f6e4b34466e862ec421f710dfb6764965c5abaeded11a68ed0f52fb997246e67477e756a6da5d35d7439b6594a652cf62a101cd529b7913b6a8031a63e788901bc6287c597577673ee5d0dc63d4ae1b4b748f10c618636c82fa7f9e338e841181b98c7075a0159f631c13cc07984b890b04adf82f8c4361455c2e2b2e215af1679886aec65fbe7197abc618ee9e14dde322d1189f6d7a48bbfa2bc0defd15e81aa3f42105a343e8eeee7daa81eb54f1f40aee0841fd4f39bcebf524066a0751babbbbf3ecb44dbbd49e400b8b1709c04480ae4d37059c4eda37eb32092a9c11d1639c713af773e3f54cef991e66a637c1d73c8781d5f0a9ddd4710ae9526a9a0d1c6419cca66719776ce8b12fdfd8ca2fe42f0429ecb829e073da7820ed448852edf85d758ed6c96e9bb5b1eadf8eca3eb68d933a6877f7138559af5facd730d6eb8f877dce694073c423481198cfd12aad15578f9c0efff378dcdd63d6edc015fcce451170050835a7470e08581df69c1a1dd7d32ce033ee0c9e953fa7fd8b3578c8a99153635f7303cdceb0eb325c888295f31f1a56454bcdf44da6ad945e2450fb9d7393698a981715c18c9414ed159647857e4ce41cdb01c440afa120202476b5f74207e8c73f3f4776f4cdba5857b0acf8e19a00eb7d8dd0ec7cd0e1d2637477f7e6660e4e814d39a74318238919b1c43f1cc4432e777838c4a494d279f4681c97df9cdf0522c544160a4f33082cce0e1db26c3d4de388fa84d41f4204b05e7c577d3766e0530edfdcd4d1d021801abf4545436b7ebdf82c2a7a522d8daf4795d283310fe204595a5ada946822cb284da5fe59e0eeeb2107bba4f207912b5776d57f8bb3537595bf721c74adb7a52268cd139cd04903324829a38ceeee1b3be749788dd58975f69c71c6eeeece5034726aacce898586dc629cf35bc6d9b1d626861beaef2a3e4a15bdbe813b4e2acaa572a82a53d4b46e21d34f55c7d32bc9bd68f76fdd107a429807ee6e8fec26538dd589b33526201c8210c29e523f162b0a0a9170f00a15333d48d1c86e01d1eb178da10650024979dda332d7c29700a9628049a5a9dc47e5e6e901d7993ea79cd27b6c6c3021e8c9cf5290be71f50859ca590fae5a80807eabe403c8cf0f9b155e299a5f1d19dbc1c3013e844024495496d8c53dac1c201aac676554ac6258826a710cb02bf8b2dbb136f06d4c403fafb2a057133bd8f35747fa2b422152a204997429e3eaec92aa2343dd07c04a334aa9ea66194d753172276919d154020eda15b5cfe6fb43ff2d4a539f74c0a8fe1bcc321eaaa68ad5a76a48d5545de3c9c6d4b4ccab1132753c55745029e79c6c323995589bb9d78bdbedbd64842c8369c29903465e36f4f4fc00f9f41c01619103fab1cb55c486d7d267e180383b9c27b548b239393c1cb05372a05488b449abc44beeee395c0471d895431020a09f9f9ea52525a52fa7763c8e36e99136678786886a7f3945aefa63a73252ddee470e3fd35e4f19bc68ac4ef7f4fc00f9f4b8bbd3581d7ff2c48913279ca83b80879e2780c551c0aee07bab23e8c72ecf355384c64b8a1b3091a2f13c560d1c3840f1604d624748523caf45129e137d80725e82adeddeddddbedee45234067077e9d28b31468f6580d5d17eff009b05811a8109753df5c43231430ceb59f6382084aa8f01aa1add630f5996d4c312b53fea90a49addafcdc70055e51cda33196075ba66567adcddb38cd2548ae5068e045d9d6e293b5f9b2cf39ac94e48eb8c504d6bf9c46374f7fae346b0419391c9c814eb9bcf74646a623232050167a4d0cf0433c14c4ca00969c666d12c32aa4a66d1944b755bb0807e936812cd24330683e81e79ba7fcf0792d5bec11f7e9c337a648f9123bf33d72ce394205ffce2a124ec3de1571113a3988cceaaf1bcede3217ec115fc16019597a8348d3935317466440000a080001316000020100884c2a190503c286a9ae81d14800f689a3e7c521ac923498e0329088220866110020821c418030c50481a34730de576912d14b9cbd9a0176b156079e2156016c8430a33d63a2712126694cca597d8a44ea56f62cc15b801b30514a5964b8ab439db0a06fa6949745a547b116f36c20da6373a07a338fcb2e94c2874e8abd9edf1a65ca74c49430eaf716bbcada5770f12b79b0382ee3a7555d3d0a10ef9b95817c19dc421fe99d2ca76dfdac0143ed856244c59f61a95422d64a1913b09c4d7d22219ed4c8f9764f94870a65f08ba9c1f420977551174538938511cf43d800bfc5f36afc05b85edd54504b8814f95f8550673b084af6a991a1cf7565c69a6beb12144a9241caa9f368d436fa783adc4e155aa55525ace3446cc2aa514276cbcf99b31fd0517561db2d5f09cd9bc893bfd19818118de24a2c3c9d69292205bcd7af35df359705869f7d4561539d9d830b3417f6c7df70063298729759f7c9056d342c7edcebe988b7d17adcd9f5fdc03a1f13a04f56c4de0ab49cc2a212b7c0dc06f05c4603e1b1530de29302778d23e93e6aae0a928b2117b670fc34c18dc1563ec6a75c8902ea54247adaadfa593397c56c0032b61e71314959d8477dfc00cd06b1c47e1449615bc9871ede547ac1967d46d951bf258da8b0d8a73d07ce1ecb63c1dd267d2154a31daec48b42f05d31d279dc8acef2d4439b3c6eee726971a74396ec355097964f1c1a7ba1d7e28ace3e4bfa323c9a7b4786df178a46d65844a0764d661b06dcf9b43d2d9c810a7245feda091cd787bb0690dbbdf717136c11da54c2f1a42bc18fc887ec251559101ab071da2ab680ff30f6488b0dbf1f52e2477f323870a45b80ebeb85426af224b6054f05ccf80ef908c28decf8ef7dd0274a64856dd05c01ed3a991c20002b1249e05c3416b90dbe281fa65b20b16d0cd8cb9333e1bd0f6a3989e6dee8db7c13775df0c7d4e29ae946b8101870824c3f02ea0485b2d9857cae7096c972a12a43297ab8a695a791aacbbccc1698e742c2905753fb83d0d699032ba90fccde94a00c95a93e6b1fb84dc4b3da6677d622f903a2885909e30a2478212f09e995b92157fe44b5dc7cf087c0ac30483a94235da60aee560488f189a37c5337580f6906325bd386ab16f1de470355541c09f90689083317c788cbeb865582cecb7c360f6f726272e9529254f4b0b967b5d324328452385fb012af65a6c15435dc2ccfa4080280d82ea9e24a101c1487ae24b393d1997eb7a17e444ed6e8356b50a8adde8fcb382e2e202348d4ed6a7ffd21ae4d45c2c842f673f4f952f71b78202b1ffb88491fbc2a7ce5f3b15184c6ad5bf50307d6fa25b97fd114b44508f01cf0af8c3018fb8316fb69f9c0bc36801ba1e38b70baab77acf0c8da85d3d7d024fab6fce7f00e10deae01ede4e0178e3326d5b818717c0ec024620b0f1b9bd48ba8ce995174766cd29bd674dda95090b805b4b352d5900bf1b37be6f01422d046ed7d381b8d5b876fc43b83519242454fd35aa20c2a6980ee67483e3c18edcaf594f3cc21b2a91a66c138f3e887356a4e0e6303830912f2a06fb0eea5e13efa610466e060d940d30a9385f00327fb2e04a38943b61635f2090795f066bc17d96f08b60d9f5de119b744cee337f5a537a94a3374899a42e7a9f6ee4c4821ab5bc8328bc371b47993112bf652e02cb8dd8d7b58b5a8824d45fa59d79862c5d861e416d9026b740aeefe6163d1da08ad6521641d8c9569c51bb0d9b538d8d24a6d74d6f4421780675ce06943ac73c086caedbb54d78d4d5ab81001666613fc48a3ecc56c9c44ec489b6290893c30ce5c3283d503c5bef6a30101341661d50a9c11a8369ce82a8693d92aa3e7a24556ce245a665b8098305f7f8ed816c607601e7c843482d7233fbcbb015984e87fa7382dd52689426fa95f6f06b33582b00e6fb2b58ad32377e8043b57e58655251d1925a266007ff8da507d30297325b66876463a3b144b5fab8b6ec7b56b56e2826faed65d1016efc67804864c4dde4213098df4585ca4156f5bcf0d828e88eede03636bf449a9f4d5ec8df2c08ba26fea6aac261864b21c3f066682d96cc64cdeeccbcbd2c0c26c41574aae8890db0db2ae20a986e72769ed7b359b91596ca3ee6ada873f44341d18a3fe50c2263bc28a579cbdb05225a2fd31e93128f10f4e5e6d100855703af73d684784c7548eae4356fec905d6987de5462989e14c391dc11b11b78dc29770a2d2d3dd62efc777e29369cb14cf1a5d9261b92021202ceb3f72da48602e0f2445ddd4e4c938ca01eeef07c32d0400caab7792aeea8991704e4be963faee2cc53e9cd22903daaead83d44c5a5ed4d914d102496f1853d260214e57baf0d532c1066b338637b48fc310caeb2799cdc92aa095028eaf4d92e780c0f5ef338e816600aa7e07d94931a6deeefdba89861967de1e75b12dcfd9ff2d7af1fe72ffedd3cd20b76d4367a5aca8a9be30ce17485f5a56641896495273c9a21a052e38cdedd25953ac9e161ca4f4e15f62113860791e84d000bdbb8e099fb6a6b2b370573ea4045bae78ec4cf7abc10e6fa39712e5bebf8689203e580808d2e853db1b52a31a5763490f4c0cab99e814952b2e2679f637e7773df84cfd3b5dbeb47cf025101586c1b327806abb554225810b71b1cd0afc0ad571906a25af5ab0b6e00c44b4b3ddf1445e8863f6e186618a073d9b1042ffbaf9cb9664fc1b62d083bba9e684299e2cfe94f70dace3bccfda10213562171914e4398ffac514b32f616d1b576cf1083314a0c5f5550e90133a220b3011c45de959a3fc966bfc1e4e1be7762eab1fb2c6b110d243d14232602865871451fadc8933508e46620393528ff6ae49c52b05db91b3ea9921a7f8e72b8a6fb2ab80e30726be52834b836a0800152092e5d661b95816c60651d0f624242ed387759cc867096cc7673cd0de2ed7bd56e54ba6ff78591de8f28a06cb54f6602d2005185f14c01b602d91bcb8a950b68f87303e8f91b55f2229421f9f67d188faa081299f0b68cca0bb9be562d448ac9778d496e221af705979362ead23d5c7ee341a24f9718fb6c69a4fadc930ed85c99a08e0ceb6e0836b5c40f7da969b0a0f418782fb5f32f776e4c28f02f1f5663284861f710d48e279c4ba739142c7e990e119ddaf22da5f6a6dd9adeb81e1c9a00ca146714d9ea46ec204bd3fa5a7fc8c0fd653d2ce3127cc7f035bdd2101afa370f53571a8ac23cdf6417faa5f011b7e84fd33aecae4c70f3435813d69a63874eb7b47eb7365ee65db7b4e5d2672616d078d4a3af037bc70ee82ad092e9dd6bfe2b7e0ce76392146d809f99ddd68cecf51f80cbf8f35d4ad27579bebe7071653a925cd2f4eebef48770a97aea1f33357711c90f39d900c176404042237b9d1c9f6f148205f40752cfdd7299d24629f33ac3eb1360b64b78e0bdb300ea93d591c57e2bc4aead45e77fdcc2ced6133607b9b7cce13346fc62780c94553efa99f58d63987b2008c8c7c41187a36b4cdd840a3a445bf37755e88b7d08f20039e8e07182e76dc037dd9bb9e95e0e4c64bba9941108cfcdc350fb22c0684f636b27802a0ad4831bee1408b6ae8cae87dc9f8eccc8476a6a8e8dfb66bfd138581aee98f1fc96a16b49e1c0986c841dcfbf14e8cc09c89eef873ee6569f348316841b000e9317cf33b8f80f9527ab26f7a0c5fb7eb3f7ae3f054faafbcf8959ae62809934808fda03d36edcac489664fe589be0163a1157cac5691323d195b1e35e1018c08c4de75e26bf1d54c62bc482890341054831011927c14194386763897ec414b805e4d7951e115b43cf581bc5d2fb32360eadadf080748866d7a249888e05ea0002945ed6f243fce26975bf16611b6774a254888afe426267818277a975bcc0dccc73b934b915fcc91685cad605c7c8b2f10a0321cc29202369a92276ca54c6583fb3c52b6bd2f9bbc60ad85b82a9709a24e181338e4d3c684ab27f884bb4c1a0384a6143aced76d7984a6f27115328e7b903a19509f3b876d26a1e97072e32b035d74b72779fcb2022f90c05c3700e1ba7cb257cf70992a02a6d1e93d4c75b3396c0f8616683d742cae9712ad05438c91b59ab6b3a2b3a6ce268ec139e01066c2ace2fc8d041309a83e7e53e17722b4c849f70246993d3743d2ca3f005593e24cbe4542a3cb46986a5220a8412d114aa34a42f7073ba0cd27916d40137e37cf68f9dd4c5d747910cfc71068ee573c4ce007c24b36a58cdd1f0422b56b34535a6aac019efa4eee2ac972da90946f505eaa98eb6ca4f16f0fe2c7b50640a9b3cdbaef60e3fefd21bfd08b0e3a7e8879263346ab633deca92da62d7b130fdb4b7b9f7db868d4bea47b589e61e0368b890b5e1225e167be3ed5ff26a11291702fb604d8050e57b59dd4e450ccd4207c51940ce9de6f54b25bd1bdaad359e42aefebef4aae9f797a90dcc5101859098a27164a622a0e98f17eb91b0f5580c5c0f44e1170d8cac7c7eddcb97bbb10f977f29d8fb888fcc99a2f5cb60e6ceafc252dc7bed8538bb890ccbf3585471daec88df0c8c38dce2439e55e552340e7ff35936a3c68183b5e4309dcc9d8364e0bc555fec90529c368c5bedf9b954c11533a34c6f4ff399e8af475624d5b1cd1f662a4be3fbd0096bbc6cd122e0d688e62b90c72955689dc2f123fb86321e7dc141fc2e28cb12c8d3f970d0494b3cf19044dc86c514403a1c51d1eb82c411db08ebd2953e26352ff56f7a014db6e835dda09e72d485d333f410436a818eaabb61fd62bc0c1fb3e9ef4c716e879c43c6c32880dabc0a12254878f3a4558e8b48739516a41879e284b9aed12695d12750f505196ac1439bbfa4124c72bbd9a540f4af61a8b52993b7fb5a4d5800a40ce9798697712aa0865f67faefc93a59c905da8c51b2af95f8c3a98639eb457729754854ab1fc50367cd1247c58284544084781aab42112a41f8302791d900f3ee27cc56e2b7127ee4e989079c0006cdbe9b6aabe3b258829d736825a183ffa6c4016273bcde64b49d6ad05f2137bb3cf71af59e1866261b27e7120ad811852e7e52fe88beb4176b4f7eb5c24d0ffa4a8c280a788f7aebcb85561e76cf89a6eb8aab200f3881be06934dd767e2bc174c45c3f3c6128c8c83c2aed00c5a18e48d39d03f9ff30cf704f4519d6a1aff40c4510d37a417ba360d10aef101335d349245f19a69a2f8259341f8036f527501033924c20fde5ffbb82d6d13315e387bc9bf80adf62a4d9e00e8919ab09ea7cb8988790db1a4f643e92ceb86997b9b4216f50f6601b07fff628e1e56c955a67fff2f41319f050e2cb72b5cf9ed97fc3a9da657db173b84fa69a3caf92a976a3cfbb594df000f7e5034a046176a52e5dde87962f25e97a0c8ca0cf395e4f352e2f1710b2e65435948fed96355c88a918aedf03efa9dcd81c1e286b2a4be4f027594d824cf860d93c1cef4069285f0fcf9dad5f2e1d1fad1f6224bd468871c8ceb0f1610ada36c395cae6fcb00693b85b3afa11863e893638fe81d5cda331014ab24590ab99fc7f1a8a7c9748ae57255725e31f834211e9fc10062950e7484d3f5a4db8c547f34ba7824f2ba60b413185c0967151713db3e3eebd59bb5c660a82cba7c0f85816130d46a2d89ab00111e407582a1e1cfa5400f860acdad44e22ac4922667175c3ea5930b861a82dddeeaf6cb05534e1d491cb9817741616af551b84df6002bc592c26e89cf5ade96ade7880bc992ded82ee393b169de773a083ee5f0f693ff08be446cd31dad158259a1313e8ad08f618585427bfcbb8fd728a9b809dce70bd006fcd1acb8fd7fa9996ff0d7f1654c2e97242d015f68dcf72fc04e373ba97f5821bad13de197531c287005150de7dba8757c04d8e5666c0419f793e331cb936cc1d7a63203b1e754131c1cb703376c3149ab198d89278f63a1011aae94ef15aaeb647e645d3354f7c1c94c35f255c270ecaee3d0dea0ab73f5527dbf4881ca7f0a1512cd9524b3479db0d44185cc76ca5683ba7c6f8f94576f18f5a22efb70142989baffa0e8a7fda62ac465d9e9f63e052723e0d3cf0af68976bb2fb3966e6de283d8dc74624a80b3a9663267fe6c25621580ff03d40684f65cc03934aa8eab4f6d854ee633f0320a0abad5f1deb278c913739e8dfa6e1a680a3c627ae9e2db12d16c29a53fef2726254d3e3cead627ca1bf9ba236a219811317f53893d85d6171e45774b5a70d4d2347b1a76c3ffe28fc8f5fadbb81edcdeded7216453246bb747657c6d6d77578deb971a51e66c80f3409ce8f93081975ec405528f13ad8d9d164de35f664822552d2bad414acc39f236dd7c5eac84e372dcdb95847e5d21cd3d309063df05da6dcd7f6debc0f8e00c0e52d69476f50a70f21dae80d55a940d58cabeef4100f898f94ec8b02ec3618e2b51f3f0810d1b437b0c9a66e1ae0dc66cfe966cd4defd568f81d82f268b905d88729847bc374d680781d7427197e86e28889d845189a6218575d8b51280f9f75fca3b3c78fd3a5a3aecf337cd958d2b10cf9936f1d14073e7d8f8397ff730e9e8537dadd0bd0022107f136e99e9de431cc7f7f52f2ce794c86ddf15944b0afba2309ba144c858a4ee1941740f93397ebcb5df676c60cdae6e0c7a98f8a40554dbe265084ea74d1a8cc5c5cf25fdefbaf66ae31cfc4b9322aeee17cbf156a19129cfa2ae6834bf4272540364b967eb51c96a1ccc8b97ddc624b2a3bb892912f98fd28657f4849df585030ceea978c0e3541d2754b9342a8cb364ed81f31cba02f39b3949ecd14b413a8782b9982b53117f3e82511bdc25803c6ef950f2d7fc8e77035877666f7e54bcb002dc9d6d4d1311712cfd45d4ce54a568a6178799c45f1d7f908c28bf65ea309adbe24179303e2625e87ac8d4b5785ac8158223e0b24d1db2e7c0a7e7acf138674842e3a4eed4d036633452453be86c307160672e87082d53ca1b03b3d0071ee14d14bb17abc42684d37c9182c1f59639e15afe0dec570878e65f75dc35d74d758c2f1f79785aed626567e0809e5cc90b00ec03116a1418b32a9ac64649e9c413fb0b20bbd7086a71939a113e9b8d7d873f06bc8bf7f606364a1a94acb7f1649c35051fe703748426ffa6130580b777cec48519215807c55d7261cfe41a73f75c7cc0b043a7a2440beba7d1697b9567c2b7852095a440e61f184985dd521db9bd0eb0ec6401b2b8f6e10eacc9fe353f0a4c6a5c5519030db8377f6e02764a45450a41992b03a25a86903469613b26f6f0609a1d7511ff3cfa482b63e2a6e972bab18af929022fe190820e2d16ae514c1e9b8877db5523fa0d525eb331290ee564085d3058b1fd4e9f5bff697218946b7d83b5e622413f05a06d7cc3d3ad5835e9c3c78659371c858c152336888f6d896134deb54e887c24b7c082c848d32dc15b2e524c8c647a12f6512dfa3bbbf0b910dbbf250068a57c231a71a2d64f628d3f7a4cb4a51eef028eaa3d914c93349b45460ba1c38ecf713d52151d73ed7d12b820d65af68cdbb683879fa853ecb8b75bc8c172e9428bd84d58917789de1bd7782ae0d312dec4fab79304b8b3239d8086e2ce7f8737ef928faae737253bd33b0cf578002741e6e35908d6e1d2129d7d9976b6e3b89c1123d641e252806be581cf8354dee3546c85d7fc002742b53984dbe0453035d38863e480a0b5960b5f3d5227ac48f616250441ba8aec32b705ceaedc21192c2d8255ac951f6bcbb8af159cb573355d95bd7077bdd85cda6700249653c470f09e73a8da210321929d22a281b991713667f7ec5331e471184b8073806e79537a3556cbae5832ca1fb5737cde3023a00ccce44131a8086c53c7e984f85c981875a16281b600e8d6152b913c574fd917682681892c6b7b5c00757cb4106451e058a6de3342428f8bee1b05646d0006771f17a5811d270ea98f402bd67cacff782243ed9c43dab80a434c113ce1351506c94e0e313aa6ec7973b4b3ae87fee1851b4b7452bc35602b59437835b3e1d136d6a40e8429b6b621c8b7707656afd13a62e0cd88ec2a22ca511e901e989995ce2aab4f38e8e5d86ae754a93d0f4f2c5a98cc58521b67e6140cde46f31d2720b1c940ef3d5648823ed1b53c080eb6e5dc63275774dde2aeb0c78b1e0dbf9a4d72c5f29ed5f77433ce68908355387c745b2829c42a4e8022fb1f232a7822dd066987e649e16e481b28a66f5939a5ad0fff2f59ca6786bb3aa15f8dcd9fb25852d73ef51973caf5311b2a489051d8af68e653939c6f2daa4a418a7197ecdcdf831d2ba4add435285a67e6db49423dba0c3dc4ccb5836f83c7d9705ec75b4f019bfb1dabe622b7a0a73242b6ff39e408bc5f946937d18760fc4a9a082184c2c5183d3f9a6e81f071a96242d6b0cad214b1aae4a3a06cc0f0cf4d287b5d50feea27153de3bd1c3b2ece08790ee74de111376d51bdf641acb0ecbc75bc735d1427d8d3be1f9350185849d781537e22673fbfdd6eece35a9a22cdb9bcc98a903d53f3abd2eab55e68ff8c8d5fbcd28f4e212582ba17547a6835734decbb1bdf54122509ee113e481e48a1ec1874257c7331559b2945db1e09c58f50d0dcaa80fb82748b79d5a18de4bb71174d3dfa1f5c18a3877f543374a95a74c56de223a795354146dd9814322b6b6b520c6e630de588fabfcb6b51a58fef58b37e5aaa486dc683cc6e7fe707b173da9b723b84bfa8683671cac0e22fa79fa076f02d0ef3757aa4820d66c4d49a0b227b585c6ef29e1e551c186294ff4a46d5e36a32081536aedae4b8177bb0806424a1795ba948d019da57ead7985825a588b171aee1231bf46612f34906dbb97f2c0636204d1b9cccda743e7837e9eef6a4ec04c4765792061fc33226ca3b9ce275add9f746e5e04a4ce8a9992760bd19516b64a9b6abf7d5838ce160e68fb3be6c84d02203e7752c41eb827a021bb3f44a64c34c6157f9086429c46f7e1a2da7e8d67ff0b1815db9b50fc0134202aba0af5bef94abfa2dacadc6adad97d9032e4567d1fdf2a6db5a96bcab6825ad03633b53a073ad332ea121268881bc162245acef4f500ca069199a61c1b235cc8c322fe1ee03726e9d8dabbe35212850014f0067356c84c54cf92b8298064a5dd034375a1bbaa4058b7f8c2da95b6b8604313e08a05cfded9b549239ca71a50b1126f3c700eb49d4884fca590519a8328e94f803cbf6cf54e4a209db8877c308eb94e2b596cd037bd0ceaa63773859658ede708eba86977d2ec706f26047a54ceb397328b31204e82029830a3edc1e8d05919d6594ad6794bc940ddcd930034b6df1ff9e8020223680c15d2e216d289961aa4ec4e7e7eb7204405ae2628d57ed8a680eaae32f199b655179d016ccd8143d7581140523e6b3a8f8948b7db2a88a31c28c859c0ca375a76b5d653e84ec9ef10be6dcfe1da4be9f26cb1690d96ea8bf60d851fd435d66ef8bb2c0a12d26d1f3527dc9b03fc8043f832ec93fee9fbb56ebe4bf9f04c6c6f49d92bba5f52bad469fed90ad05754707f292064bb1b590a05847973baa27f3189eef28ac9f24358e6db979dc1a8c2106e9c966c85a2c61d421b717244b6017a43869ecf8221d9459504db861dab023f36c97e197b1dfb2ade30656af57527907fce8c4e8e452194d1c206cc6387fc48f8b8dd6039aebe032b8786172db00e6025187f0127a7e80d3d25113a98303148ea3948a98e5d9dac38f6e04d9a2dc1e2873f6fbfc1d43a7b264d5567a0bcb76727a0012b88529ad1c9510891998255e2c25b8ceb8bff09782b873d605b3ba3af40ee9d1a7cd01b87122de29187cc3e865d3258c63ce52bcb6136e1499d8bd2bfab50202d62542a3bddff6b2746784355d592d9217013e2198523278979e4621c1a8cf4c1be27e46be2008d6d69609e21be645048abb642d9ff9ac8b031b5196e0ed61d6966443ffe6788d621c724d082b137f9403f9867bfdb792825b6f3f17b6a44ab655cd8c1c2d09af92561ee9f5788db9ae76e354444c7f1e9375d05e681708ee52b959cd624e76ff8bff431d05133738ef600d7eaaf162893f34c979480434e2eeb41506939030d9093fb6104b6f79519997f92140b600cf6537f9546da687024a0135497199207e64dc904a0489a3d8948638ddb0d8f261b2ff226f1c14b00b526ac77733076d019e718926e9169c1a80a0962af29d492493c2813d78f0c1dc16000a4117ff07c814500d604441fc13097315cf83507645a62a921efa810e9a25c0332d7a9e1365689a3aed987202a613dabd44b9d6392123bb8774f5993ddf17075c28bde4ff7760a07309d567df9b741cf1450d802e53f754445a373f014faee1589e0fe2e71c3da7d5a51c7b65f801266e446a3ee39cbc05d26cb899adde6a91376bf6223284036ca15aaa928dc110a6980558e10413d4f483eb29fd3ec58b0549ea1af52a3f127d1f0d824248b88cc80db20489494545de0ec78d2e6af7a25bd187af8c81531ea895362c3d026f02da324f2dd17fa6b788444416e06945ad926ccc1011817426eb51915119666a63f22fb8881565f0dc976df4e107ecb691308bb5318a81d9e966a17485ff12df76840e6b1121871e67915777f12162091b41a7bae51b0869812dd4227eb409a6fae3235b064956b715cae8dd45d179905c8763abf797406fd67baa03554d7b1cf1992d1bf91ea2e218ba61debe7da0bfc293c5d93202984f46990e0c6348b3b10fc72e8498df31f0ebf8704c5319503a7da75e95190632ca9bcee9c065c89743d6d4692122afce00001bdb2b9c45e0803810dbc4c824af9d24c86afea17298d6018cc54144106a152d6ed0d68823c077d77fc13afc1e540896f89480e8c0124503feb387d1aadb93fb10c1a271eada07a3ab6a9b2dd83ab6851175c08e5f1df3580de7612f17823152cfdd6d9f7b85b36a98a543d2bd2e9694dce1f5135863e208be3ea6177a8329f6274e72eefa2918b9015bf810d14e5673ad4f48d806609a0938e402afc39d6e7876127337bd955ef6ede26feed4d207e2346c0b32ebe1eb6c558f7fab062c02284de633eb3bb6790815a07a9db645ed407132ffef37b1a50c47858a893c372b8203f9d072c87e758e08f13b232ebc1fafc5eef9053a954e3c1280c071008f0425fc0100c5ad207aa6742180952ae532f16c50dc124f4a95c3ab82b84a2bde308a7b3b4c7787a2c15f4e1734006e3bbe9e9d781c4ee6e9173c80949c02e295233d85992e607384f254228487733c0080421bc050692ca084f1f85a9e42168c18f6f8e4023d041efaa7b295092199fd21c9a5c74c854502df06ff805bb5b6bdae341dc60c7da07ec26c9699d0e7d4b63b422441c7da9b5d6c29ec449cdebf4792615d687c027aee099e7bfeb917c0bdba601f1d4e6ced630103d314348868ebf90e4b046eb5163040cae23ba045a2f1119b18951b22cbc09c70b36e558bccf74f3c5f6c066aa62e78cb18ef9ff9c9629852920290ad633b3352e75656194fabfef8ea9aec608c9bc7956e305f6746b9b4eb7703fa8e54d6df80588ee425c7c1cad46f0a395b2b04101f30f97a060bfe93331bfd3dee205589179e41360dc1b3121f2453d9eaa297b9e4f081681a1f5a19f82cc357f3df81fef9e3ab138a2529c66f6e2815270964513046d4c2dea2b0e42d9b111f994425fd610741ca395019dd06f5b5b8054b7ad3de72ddc5e2dfaa4f64466d5b48270be8819f05b58e23c5d58be07fec2527d8342a2a3df91e1626eda816b0a649317698456b408b5eddc7992fe4f3a2f3f8f0aa5a37f0e316a3e5ff84a83299c2793657535c808eb405ba79d7a3a767b0300fb81210f1c9da48311b436439cc6dab7a49ffd94137e0d5c2d3f58de12ebc92663234f75694358a68c0355cbf27b75dff2b4c37732e53c0e6c22202c82b08fa20d2e8ea743d3b1dee60e61da1357113f1fa18f9ded24c56319f0037bd4c496141932af1847ad9418c00ddeaf7fb34ffe59b83b38f83130c30f9ef8a29e12fdbe5591a64dd5be0021a4b5b0eb4280ef579cf49952b3e19f73cdeab80c756adfe5c945fd759f60fdd4ea838cf7b3611bd7b79ff9a5d160b9cec387debfb87f68f36a4aa150b3c864e7b0d57dcd33488e7b2df9a42d7d46da66c4c5d6bd25f0bae62e25ba43136fc72ef7d1334841b2e958f8c0667501e61677a1ef4384aa9bc7ca21493ce9e53d161134131e8111417551a6b73cac21559401b80e82f92a746972d4d5c5437375ea6e741eb3bbdaf440695c304424c9c98634c781d03b38b0f9668fc9ea2d8b5d9852a95957b07686a1eb0a0950a2a48ed32be04786cdf4a4633cfcf0bfa3d14e4f5fd2171e60ceee7e507d8a462a540c592599fe8480329b6e35a699ca5a8101853937dd24a952701f14e2b5c3b2bc47cc26bb5092969ecf387c59d113558faa029ee22c4c11de06e0d596045f9dc84028eac861140137c3d7818f347249fdd2eb44254ceb54968f79a682ebada217da25d39f9d70448885339ec650d4056d1705dcb904f1af0f122ca3870208f324daf1cfb33c3ce5ff5ef224556f92d9ae757346505c639ef78698b5a02a327e7279025c330d43b96a5dcd94de2fb4c0fef0441e784859ddc34f57493492d85ffb9c50784a6bbdcb10934ed576cdcef794d3bc8815386fe34b65347fa45340c5c4a209aa235922191fa149d977cf51e1e67996979cde23e843095d0247ebf6e5a83bcdfa44fd1705fc376d66816111ac9591859f255dbeaed94a031ac64ba81f1f3b64c4d021f033ce3acdae77ce49d2fb04d8dc9c7f86fabe7d421024957704380b5e046d3089c16e7ebab9fb3a01ff0fb2461bee8c4cd13b354a3416fb9485a3a0fc801f7b484c6bf8b2089146073eaa7808403e078174b313a236aaa525c4479598055ab10088c044e3391c54127dccb735fd1d5389abe1379d86eb43bd9a11ac6983b99faa98def0b9d8db78939e74155cf1f16ac6bd0a4ea61b2de1a0e9ca75fad2e93ea7d15c488096d151eb64c3e8f17f28e262fc69fb757c5eef6696aebbb4bc4449d8cda19b7b81ad96738a83f9c8395f895e7c1c1f10739e350f91c84e26316517c6cdab9839676060cca43ad06c85f88b34b34cd4df4d6be06b5061bb569eab837d2e8513a7452fdd25635c3ac734664109a77f7e6466226d3b9736770768d6fe70b80c7883c9ad9c9d41945a0064ac336450dcb3bed913992dbcd492d105acfd165234eebb77dff6dfe56fb5bdddfb4f67fad40fbf918e1a4a5558126eaad24d546a89ae8a393ee7611aa12184c2cc8d6cf06edcbe5dc8969899b95960e2c749f226b43aef66fb5bfe9fa6deb6f9bdfeafe56fb37addf76fdb6f95bed6f75d9affdef3ac4e0365c78c9a1011b0fdcda64f640626cd60f03a7ee6d1695bb23884aec1ea4b1872c619c63e66428f54168e839893d861883dc5215012da67825054b3eb441bb019b2df030eed6fb2a73ea343e93b96550a22ef744f5101860396ee4abe69b552ab2d695103870431c0436c8813f8203f145ecc21a094e4e129c94e5eca7a83a1a42134c2dc4402c7457c000175a6f0921c838840bae5d03cec59cc542b734d630878d4724f48663635261d16ebd57c238b8cf4a28565d77b18de1a0c4c88f311ad8cd910cad511f8b09d82783ad66cebedad83e3c09ab0661c330882df31a99c4dcaf5603db1e9071db2100b61d0013e7e00b9cb2d69300bd31d45c41f47cbd2841f8670de3333ec2aba2c82192e000a4886722d7906c7f3bd1837326f101a38111a16a05e9c9fcecdf894de83a3ba01a0eb6ea94950e946a69350c344b02c0936d8dec7c858e10eb628571f5e3ef3e7947ff867fd5779800a1498cce95a8d75c54d3575426c4787bb52aa1b443da57292f886e49e74cdda32326aaf6aa7d8a5173ccbb8be839aa395b651c1187a8870753740348ac0ef01a2d826f74105e8628aac88eadb829afd47bacc3d1b8e23d0880019d024032e8409f185381f064b062c5ee18e81b11bc9d219f6e1f5c1c14165cfdf787498143471085a0e134f8baa4be14e895ef4952274935d7d7c35a7cfb9cd5b46ebcb3b007d1496afc722e69d07518f79f44b021d98944532dc6639c82a0594e0e927f7553376a5c5aa46fac6c06ce5c4db42d1b7376fa441a19049c6bcd3270853f2f5f759e1dafa78bb04a5019ff5a561824203f1abbd7a3e553f7346df33fba9186158cb8c720169aaa230484c71b4e83bc7f9883f629acc910f6ec58156ad8495841d3023108102ba5c85f7ab3dedc86d1753e184d30a1571e0d3e9090fc88036f9cfa001ae4256fa1c45c4d31aae3ec942d2807843e44d446de916f3c7fb08c1b25df1b539a8a3651af31d1ff2fc9ce7352f260e56e6033ec751ade8d014dff02edf9f4f3fe22d7889cd8724ed3ef3539111c4a932a706a1c54af9af0ae9e5b25d3d77f92092a8764a2bf86a4a162455da5b4ca36667df4f99f8e2e4139481511415bb1dba9036374759beb7f52752e478f83857ae99f34b86e1581457bb9481d0a11aa3a648a30bd34300eae48e09b6811731aa0bfd23700830a76eb86fb09126a9823f816655209da3a14e1b784ea7a16f011865c17e56e83d4a8bc1aea95ccec18b7d6e3ee70b2600e4f0add0b886281c6a7f0b0add3ee3147db007e70dba154c26e3b7375badae531a2e3748fef955e6bd34d35784869a468a1298ac4671dcb3bf1ac33c4c47262423c37da69d3c1df7a14f1ab5bb160d507dcada05f87cec75ae55919f8888352f69eb1e4d87c4e5a9ecc44e60e080b97c77bf88a97171e8b83c97bcbdff31fb2cc7b76a59f3c40688bce92fd2ab0bf610b1667009bf2a7f4932e3729d3685725c59fe710ae4bae0416ce57c562e04913552b1b4b4e9ba203b80c2f90111559de35a259f180de14ffdad408c4e72101322287a30f847703024e4f406041480282dfa4c61e16dc886f2a23b6afde0d35113176532dae8fdd91a494b0795e3f4804947e66a45ba1a2168040c1d946e92e3a76f4bc97f89ad0c181f9d951c1885639b22d0f602369625c9bc06c295c502f88d750fe22d15e3ce3d32a98fa6902fc651540e69bf69128f6a8a0e3946834d08eaadf4d4f6599e07843a29c2864376aaa69a5bfe309fce54dda128f02add17a0ca2656476fb052395595a1a2e8555b8bcb6eed8ef56a540210c1e57070ad3d8008e531da2e4a95221c9dfa7d9a1b3a6cb309d98e934e25f6caf79cb4b6da88c39a79fc17dac53cd94dbc447686acf001d52ca7102274365c9f80057e3d18ee92797053d2abb0a2adc9ccaf9295842125631673a1ba001db8d89311512daa1011bd4955b5407cd27043c46c6f3333a6df6c4c2e3b22cb496656198d1f9a1e7891610e2175a8909d8f73093a95b4d9acb46e73c2cc229f8d5b569a3f37f3015d2d9d99d62bc45027cf042f4297d6d3dbbbed54345f52673212f24feaf44fe0ebb1e44276199dc07c9f08e7c5db085d6a9fe1392e135ac0b16d9ed5cbf5d1bb0ae3d0856ef34b3692712bf5e8612f804d9d3d65e54e6b27c6aec766962c6a52d9096b0e8e279511aafbc647a5ad70866aeef2f035710405cdd51117a086f03ed14b92845f91b41b7f4cf7e4cf4e808804d86dd0ad4810aad07cbdfa4d6790b5b9e838762d7007612a01f183b8a8fed105eae48d10629cf072e2ce9bae5bc4178e3869d01fc6ccb8ef0600379150c46fc68201e0cd4eef1eb069f864bbc7b1dafc289ffaec5e9d1405aa48e500d2b85922246a8b5d74eb1b40a2b58bb981f5910e47d98fefb59518215ac4abc4a70a0eae9d2a72607512d4cbf2eef435dc7fd57f9072b3ff0634a73f69a342d30cde4407d0e03b6263d7fb050cdfc393c965a313eb847b3f09786377c454655d9d0233f150fa71b6ea0c397b4baec897d6dfd5092c591f2cd60c52c00ba9108a8279712a8a7eb5fec9cd299999e241abe8023422175609514a6ac749595f03fa48838ecfdcd4680dbb81f78044e9b1d69018ab88d24905d101ab0b570f6f72aec5c3fd9a225f281c44c36a9d3000048522f5cab3d48532b1fe41ebc1f24ea81a84cb11cad6cf73918a6dbcc185f2f2ff53202b89bb9847678514a24a6fd4439b0bb6d9ca9265e928705124f9b6fd4cb55c4eecb8b2a26dc82e12abfb77161ed6fb11b8f08c3260ee094a60b7afc4fe11c99eef528d6bab0fcd7ee0dc0a1caf48c3bf07a27c16bf72a4983e57732a0649caf644b9905ed48ec267c5ec93b2c7e85aaee92ff5f78fbade401b27744e9d83a6d10ad3418807c81b9b22c2ae91134328eafe0fc0cb8c2b260d83ce6ec51a5580edb8983d0060710945b988b6045176df65258831cd87a20ea510521b62547cf15251d525e38876485cb713bd437b3a61da0ede2a32aa46ebbb0570fc6f4b8abc0847cad1abb809276b8cadddb9339df24100e9fbfaa2a31664d7855b52f464fc861a97fe5b704eb340a4be06ddb3c6ffcac2bb42054b74e417c83658a5a43d12cf138ed58d8aba5e248ae85317d4d1bcc943520fd824a0f10aa157c01ec0aca9da05041b82902c1600f1ac10242b095a2ed8af0dd8594b50e39200fb150e2401d2406b02e0303d12137a8b11eb211c832b06c53871820129a50a147f74ea4c3cae9c62871bec082fd8906bbfedfd5996a4a1d36294f28d941433ff1e2d163c62e16d0cc5df34d0c358002bc9a2c0e3dd871a8b5bdc5c03045d5dcbf733fecb84a0ac1c18cbef9d787a21d7217a84c328de6bf57fe141aabfa091a6966e9b11cdc5bfa7e7d7a5d3b04edacc417716680212e69e85f94ccf4ea5f656961ae396760a9da1a1c45de742915aa05b8bb8010477435ba60093392f43d4adc635c0497c4ebde960aa47a4ca384227af57d43008d7461fb4fa163156e841661a5c71d52320d2bba3013d2b9d41d582dec70ed44626bdf845a87631e70225269947a3363c6b0a52329d808de7706942f2b348d0901250f901de4feb156ccfc87039d40c53e379daafddb206fb0f17637aa95dbd5b7e1e0794fbe997adb35450d7bef1f76a4cf0934e3610f031df15b3e6f7aac85cbb212f035b35c15643fea561c8daef4a293bc6277df95340415498b6634833617bffbf137c109bad7778030a314227efab112dd5905302893f294292a4d7e5ff0233b0e46704c02eac838a489496ce9c4bb2f11289c40016542334237764423d0d969e82cfc7a87f2e4c61ade339d3d6a0a2c0b0e9afc74a0bed3fdaf2203f395ba8d53b0f9030227dbbc00e8550ef38f6c2c3d512ba1b674a0d75c6911815af01b8227d1002d3ac32c49528596c7d2537782b87c6efb7d46aa0d3fcd24392f38f356154195c56a47331209e6f058402249b1df8e81ce734a703d99fe814e4420dd6c3854cb7dbe6a57eab169c863e9bb6dfaeff245e3b67621d700f42cfea21c1440d81bfeca1f008fa1b6bdcb65a49cf8ec8f3f97eae7c44cb4d86d16200368e5269a070048ed0fec403a215599648e5a9738fc7c6718452615ef4114345ca3e777601429f6f7157a967bf32f6c25d2286c9de2290b2f678759a0df12fcec002b0cc18e37f7078aec6420150f68f1861ed8c5983f035aab50163106726ac71ec46f1ff12859feb151cfddf9e3163c533c3d087190061bc6fe0dd49e5f59c35cf282f4b889e6d1350a1bbd0250b403e2b9b10b31ddd7e93fafc56b157b54a632550308df98f14051549aa779f64c784880d2145049a2c160ac4fc08791df6c655240ca1443df18c8ded7fdba81c34c00f7ce722ad5164dd8e3cd6cb0c2bfcc30510baa00c9b444589aadcfcfef5d00bb36fa5ccb732bf5b85f6ef3f536555b19c386e817737a580159137976783618e3ad119ef92718e6510a591674364bd7cb7109899f9bad86fd71d1804dd6aae54dc49d98dc9b62eefcbf8493fd31ae274c1ff4c08521baf19d6cb7664f4932e42ea9d698118122433b3feba0895de2d2a82c7b109f8fb3283ad680346590c2feff5e690eca4df0be81be3af3c55918cf699a6a37268e7e0858bc59683a40cccc50cfcfd0c08ede2301bc8e9634fa699678c143bc73d4a0dfec4d67f445945b45c050019562e2a366bc5de65d530e2d19dd4b1d2fb516dacab4abb305456c4c7daae29a4b3b455784be64c1fb840c24fd4aea1e1b11ba4d8531a44a8f9b334b07b68c262a95788c0fa3c04cc39d1193c9f8a484abc8c04cd6bcd9a0ef5c9ee932f1b1483769732461a5f702d725ac76464e41172e914a29e8a43eac2c3ffc98ab100c72bd74b8c1ba2f8c4c2899bf5821cd9c4d106032371024047c24040a2317a05b3054b24a0799e92cb0b8a3897b817ddd2944e71fda97e6d155eca77fc2f5426ab874ed93cebc20428697c756a1f307aff1f3e3e48c0f00c268cc0974138e5185aa16a835c060287f972b630349f5ff7f49c8de7b6fb9a54c324919c209e209f509dba54dcc920e917c38fa7a8dc0a74ffdf422d333df260b3ebcfa515d731fd5354a1de8332ff5d5c35e5c1ce340028525f90f138281e70bd56347a4abd78b614d86b08b7209d16b67f8cf88eaa537eac527d860cdc7153d466dd48151276c83511ff121bca198c31bcc3f89bd401d986f329658aed64fe503f5f25217bc19621b77d020a00eccb10bc01b98c79c09666146f006731f76310b73ead1261d0cb34589a40d765d969d061a80c4afee033e751f7066813cf031a740409e766c5a790ca8e9b13a8da87b15d592457a8c62cfb9e456258d329d0fd4f3b5f006f3118cb0f23ffbd5f94775f81cb330ce2575a80bda605e2be77accf55c11d6b4727dfb3682119675a833763727dfdb10bcd9362ddb138d11e451e1dbe50dc2b2cbf9349dc2b04e4adf6e83ed098ba0ef6d8b6fff38a1ef2478d35158e9dfe6f46daf6f23024ad436046fda695097d49955a890d4a1713ad92a49ffd1984e52677baa4e2fd016bd9b93b431618afda8d06f4edf4e13fda342dfbe0d718e5ccd18d05443a74dac2344da50a707c4f73c30ac0fe1eeea87fc18c0ac9f1f77f44f8721eec87c4e2d36490d411ed8c44f9f3ec30d5247c6278e8c3ec01d41a28ee9133ae11041f1c34fce0a759acd398e522efec65969c2710a32b3f8e893067df4f9f41342f17332c95479caf4d12918da01309758580cf3a0bcbc5c19a4cd9c9e02917d711897e133ae848ccbf021322e4366888ccfdc1f973673a6a69d2f9e72222f7785f98b03b5524e0426f5e22ff707e5d6815a36758f6e127b51d7a346481fbda34b1f5d6261e95d518735b8b0d9a54f510735fae8980ccc6a77aa9c55b141ff60809867fe41c16e50769128115d8922468c387b74c095e1b2277360d93f9739b0b30617961d4a965d1f69834364e9e64a0cd13cf3ec6eae75639e39117a57337322f5aee69d3eef10ec873ae6402d2cbbf526c1e86df994c5a94948083e262424242424c4595e3a3bbd746eea999920a08dec3be32ac104f643cd30410d71ce37c3e4a510e7b48b732a6a09e77cddd14bef192671072a48428e8fb5e65de212aa4221b061647bc52b229857c7bcde4884bae6d4b5bb02c27e2f472111cde9c541091c6aa58e5d185711c332c7885c1cb26baf531b29b08e83c362d93082b1fbc177f583959a5879a097154269d34ebd1e0fe8e1037e8437cc0d21134dd2f22cc49f1da3843c50eaa0e59de08d44b9325410d046b2807abe28d793fd665c8f72cd081177a084a28e99269cf37593f39e2cca157734e11cc8514e8712670ee6a43ee3f4797f80aa03b522e6d587605eb10834826dba9c4b9c33a9709e184ecf7ec311158e844c4fec079d8ea0be76b1d3674b7a577978c52f5179b8c42ce8b40633399cd80f45e36db1abcaacf89ccfb8a40eca056da4d73ae362940be57a19a713c97c3a0e945bdc21bd641c5a3a11ac5e289bbeaeca4369635d2f77801d916ecdfb83a435dd87e7b54d98258750ae12b058e81fca5584dab4ac52ff761e4b629be8543efa165d8b9e458fd1b1fbb1e853d411bbfc37b37cf40f0b9a4cb3695a6156ec60c0829e4160e37f95bb5892222c0a1584053d7b09586cf40f1584924a12fef625d1e474440f57968800e2ca900e436009d242084b5a585e41ae10255d091281d51e264d51c414b63e4c9a4203419a9e643c4cb20118df0f939a8a5210219c73ce597b0a1bbde79c937960c1135a1369d0e5500b49e9249b24927cb54a4569236d1ff8d6410e48c6805978a0f60b5e7a64a24a9b7c2b5fd246be7a78f54bea4c26eea2e736fa792474d4aea31e62923af3f56a7a39cd2069c35e91ecd2917ca9aaf45223a954d6cafa1f922326957627537c21613a3a3a3a7a0e0a7a513a5fb24e97336806cd201feacda452592be9fd64f8795946a6415e54968dd5f550072eba78297dbbc1228397d2b55a3b47fa10cfbe0233334711458de481eb250f74f0495c8c600ba767598b3d11841516204501c5113f5c8e8cb0424b131e88d2c23ee0849386753d64effcc759b912c51631891346dfbd6c9f5d8f8e36ff71505839da41109ebdad952f18821b610754683ed0031655827ee0c3095a43a49452042fb597524ad95226ede0045c6d4a0335b8d88e396260a39412494a49d4dd4cc826f6986563e2f3360108cb8a4dcc7ac1d7c05205f0867d9346f0e6ca224964549fd9ebaa3d3acf58b5d7c0ffb158ca2e2dc42cb9d404121f75921c382624b1b149d66a6dd3b3cfa03a46fc2dd54484d128be5443d62594b5cb50e1920fd481d0a10e0b879923e51a5cacecf172bffe17daed304ebdbb3b22c9c425991b9180e8199771497dc6619c7a289799f13a9799f13897f1fc379799f1accbcc7829979991811b8c973985f1aa530ae34da7301e3bed86f134a7dd31b75f607ce48b37c35baf86f8327ee065cce165161f6ce2f36810049657d00f4fc0c00932c1234ae795ce411cc441481189858298254d00c27ed1296a894e318904362e45a412beb03126c64cb1d98d59d846ba94fe05e0e3e59935b8583621a98986f8f14ae761b19f2c92452ebc7c77ecda31a397de9e90d8246da4b7012b8db02b8b4a80818dfe49c8037558b20052361bb1a44ebc52002e2f1d081ca55b0bb3643ff56da7a6976e4212fbc9a222593483343021fcad35e7aad5d8c253c7b0235686a742349fb57acca026a710d537ea6d3f40adcdbf2a6413c2cd49c4bac432df380f87cd7f3a6f4564c3a1c3a1f3cd6337a4cb309f98f5aab4a98e09e1ee0f508b336221dbfd8942380cc38648973a5b02ac6ff7a7737bab0dd89094a3a417944a80f4cd6d13db90f370e01c1527d68fc50c49ca3b8747d8874d199ece30bde898075fab9f55a61c94ba4ab0a32e0edd0bdd5d0de1ee8ac8e6d2b73a6473eefe6c1885ade9d920b9257a0096235619f66fed18977d0c785a318cbba11e793ac353c738af34c848039b9e616f99d8afbf2b867dd4cb30179866bea21e9d76be55af08e6dfe6d5336f05042b4294794536c73c763f36c76ee77dfd5874cee3a8718f2c9cb2307a76ceee97d523ec5f8cae459216aec7f130290ba48f9d0b9c63f7db9cbbcdacee7e36c8ce0576eaecf487035c908eb955621b7691ceb52a8db06ff57e4d6bd6353bf2d486a75e69f899409529ad4cd901ecda2d529dded50f7a574030978eb9ec56c75abc1efa86fdc0bcde0f32ab529fcd25519bbb6347666666016b4e4ae9a47462dd4d29362785526b55d59a016dc596ed0cb8a3aa204f7b8d3b688056eac4e045151f2a9bbb6364d657652c33f33da2e2c1d2bbc28582e9e0e0b072bca258fbc9c848e963630338355689c990353f24736240d42c519f186358ac21c222d710d51471ce578394440d1124923044af79110c91b439aace5e74f61aa26aad8a61340fb2d837efc60d10159ebd8648ea70354491086e2a681f10bd5034676f5e8dab59e2c11416dcb0c3185dfe8bc01863746129a564e588dcb30bb69c0367cf49930234badf6a8cb0fd66c3b3dfbcfadbc57e2381fe56337fbbb1e190bf01807fb301008b859217f360ab67cbc9746936953cbbbb79ca49e7a4146339f9c2cb30a0419689259b5282f042931284185915ef0454849085890d2a2086069e5c2085131642489a22440f42a030106441449024c612cfdef5e56192184588c101d80d5dc239e19c734e6fd8ed4566dbda031a2f330bd8ae8f79ed84c3453e104328045dc0a8a24b0fc4e892c552172b8ce8b2d405e989071e9ebef042881594bcbeb862081f8abea022b48290d117525c2bf85065cbccc2ca20e1c4600546501981b685ca6b7e91fd808a10555505931fb66d76cf49eba4f4768d64a60e21e6957358ccaa396e0d4be6ce61960cb1a83d45963de9eca658ed9a499adf3c729c93d230d4716993c2f7bc4fea7de00d1336eaf858eacc2aecfacc0879bab52dfab48f0ff5e9ae6c5f64e1f8131ee8eb6fb3a3d87a57f4510ff41b9d4d81628c4f6c9bf7ad7264fd04fc56dc673046b16d53ca8e31c6eb6ee359b38699650e29a564296bec1338b3aa07dd90639e2d3fa0879b9414edb84d7b999bb669dbb6715ad723db5eb8c65a6a4b5e2133864520d3b616d49a6990d58eaae12585acd620f0d2e3cceea4df1378197bcc29e551e782c8b227c56aa66d5c874ad18edbb4947d9935dbb2ba6d9cf6f3d230315c0aafc1b064e5601d1c66085beb7a4c67815f1545fbc754e69134d626ac3736bbc21f73625d8f49bb1ffd10a3282d2ce8d4b2cb11323d9bbe1a1d679be860c8a0d321f393c6cf9a9f6050f929839fd3ed067ebaea033fc3d8c20a400e2e62541c3f2cd9d85044e7121493482a683c6141002e1be0028497ce0e45d29312c824a59c82e5a56c3802eb830dd6d0a9b2ccea806d55ec0e507d9bc0d0cfee79abf7845531b1d03feb4ca96cb61cf2c067a19a208f0a743accc97c4eead912c2b09f3b94704ee980fddc7d6a9ae6d06934ffa6939f997ff3023f7d0691f91ce21c17e4e17cdecfc86c7ad9014b299c2e6f369943413c3b5131c9ee85d9ea1c68e8679cae9f3eecdd6cfaf9c517666b7a6d0ac0d35b996539e9d57a4f58d6751398d8e8ed4d29f0863d7534831fbe28026704767b98f4c510350876c6102a062c7dd24060b31f08c105c5c2561d604b6cada20261332878605147740626984a36f530490820d87460510f93848001ac055b86f0013bf330698b46650bd3962a3e2bb41024694b111d420a8b92eb098b141faa6061810f465848b064088b114ac240c2d2810a2c61b9c00f06100213961e80304017d7952e4a0cc085e9ca5312037ce174c549c8004faf2b3c5062003086ae5ca1c40061046d21c2308dce96383829ecf48890847ac0720f93aad0210676c6c3242aae0461611e265151057681242a64d0c5f6c3242a9c58a2a281212bc2c3242a92e8818a0c0c61e9c3242a5cbcb38587573d4cba81d21217db1777e00d0efde850caeeee962d25c7ee141e28c6c8029962d970420aa59492f2e4e68b6296b265c76d5a562565863c6d9c604e6696dcccd6a6a4aa62159352b664e68a554cca96b14614cc12325bd4641aa7c8b227c56aa66d5c874aa1521645dbad7de9e60b0c0713b3c5c868323359aa9ba93264602f0255a9381877cc87cc9ad133389fd1f5983e83a6a9c32ba8304ceaf1d9f11d75409e962ae992258554696363091d998e3bb21c35758337ecd8fde8b3c3501abb5ad1601a7147c755af82533d4bef4b0dc8962bd72abfd648f26ad4c1fec235ee801f77d887bed917abf14a0cfa5b6afe9692bfa52004fa2d7af3e7c38f2373e42b5db6648c316e2a2d35a75b623b9863d441674b8691259cb3461433071b1d87590ebbb32f521be378c95005a7dc52b0c692451161b12a6f034b8f178759d0bf0ad9a3b4819c740707078771e08d6a0a9c38293029562bf62d719019d5e6d69cccb4df3aaeeb7a64dea5b41c35b6663938145cb09302b37ad88651c0712902d870a080342fccd676718a6cf4a31fb638e5c762a96cc0d2ea77ff34867dc71d10ea60245c6936b5de24e748a9fdc6715d0fcd39474598e3332dbe40cb6a30aab1946cec126c2e94b6a405752a3bf4152e75614eb2c469ca8e059efdeb599af0e8c811504b74c5261c9d3cc717d10a9e9378ce9268633cfb17ad3c17c15e8f20127c622d86b0428a2bba90e20346ad2f36a580092496b0208a962cac16197cb7f39d6f5d8f0e05859e7bface65a4c046ef50d16df44e5dcfbb2040dfdd1afab9edb298b055685a1052f09ae60593d7bcd8e23555fcccb1ae47d6b57af5c157200c7d75daf5a8164ad0633ebb1ed8009e3a5151adfc54fac153d5690d7d2a6a62d4a52f8eb2582dab1d7eb00ab308bea1a00283efeeee76e278830bb1f87234d105932e9a7ea0e4075cf840e919e7043e70f25dc1681bdd34bea9f8f66eaf4a7cbb2a4bd2777b0edd83107c77ed6ea426a9eeeeb64958b0a83dd860cd0a739ac1ba85375186e830ee80ce8e5119664b8e348883501dc3b0886db2d65a6b7568848d58acd56b55c9ae8e79b156afd565adb55257add5656dacc6af9487af0ee114e3ebdcf2f59b3ef88ad5aed43faf1a55ac6215ab58d5eaa6611a8669b23acb0a44734dabb5d6eab556add6aa55da8459d5e90b0c3b50c92e7531abba0945367a7558536b18f3e8a50ec736d13f9f45acf65a4737b882204f0dd335cfaa67577a5b6ce69c65712ecd25a0c8046f249603e499be596fb6a697d28e82908424d32bcb9cf3c0b0d4677cfc6dd61bb0a0b88326071c275693333d84920a491be92ed590bf58442f8b5e62387f73b01f5c2ac00a08f5c9d45a247a3d61563b0a49f68b2f94f78c56fbf4569a4f5ffd98d5a7033df5563f2af57a2bea7d987ad483a40ee4828221252a9050e24116ad4eba52f44237b4d7d7ebdb9948cc92492f99d8463a0df227956cca667cf2d12c499fba417695aa289ff155035b3da5aa1d4ad35ef3609697db6b34e51bc6594fb6a6877abb858d4e7147f66d04796013d55a95ca5f62d82fbe56a8cde123c527c803b9d51e5f55b314f60df0a68168b5c3b803f3e928af7674bab5e78d2f1b5ff0a6ddebf245a7f67ac59c4bdccc41e424756ec831036bfde5834f4cbcc6d1265ec5a7af9f79abe8e48561a9ac3ebd2a9f26c713f68b4eff2475a891b7533020cf8fa1cfeeaafab05633ed46271332c04dfe9cbc11f8543dfa342734b1d335af52e7c533cd85ce3589f25aeb5dad5e8071eb4460dcc25c28adf3107355d5377f71a0efbc7a9de655df66f8cc27eab2c00ea7b7b916bff3cc33a2face51dd8d419b7703bc41eac4d70d52a7b61abe3c0b1b9fa213b34ce8c162fe456ae46d1b573deeb5cd677780e89a03fd56bb0374502cff100e9236ed246d3ac6617cc66160ec1de232b72d0e8cdbce5f88c4dc5577572f58dbf94f8c5befee1098178771a0168c951e233d09cca546d2463ae7120b8bf2ae0617296f437950acc32e01f60629515d89d58fea28af8e4239ea16d13c7a55f27aa797de3151a3eac5cf3c8ebb1e42340a06bce1a0047d76a91666c9d5bf98275ac4ac1eaf5b391e04eadfb8b48859373cdae3c9162d8237cfc3658bc7fd41d2eab99448daf08eb7bae13c6edc1a88ecf01e273200e7e13dced3737f2a8ffb0337a62cf0867d26c939e6c9f57ce3ce202d5919bf41f3ba342614d919ef71c37d3805ffaa00fcb33fe33000fea97e4604ff6cfc8c6f59200f0acebe31710e8eb36f56588700cebe31c1393a9c6f9c7d33823c2798e09faa04ffec7736fed5ef3a967ffc29877047741275b0a75c05afa7083b29112c80431e2fc0fd5139ecacfbb00ed6077c1cf701ff060a8ec35570ef86ab80c3d3e127b80a5e7c1c1e8f24471f83d49953a04dbbd2b7a770bf6a836f170063f1ed01e069e5db457014da09e04d26d8a61dc79b46cc6ad7e1cd26cc6a3fc19b2e78d37ee34166e5b8df1c9a4ed8a6e38e1904d4d133cab79b703f5512df5ec2936fb7b917f876961300dcafc65fb9e4e13e0c7978d76b78ec5ca0e1f7c28e056fe515b91eff8687c37da4f4dc6b3cf824783d40ef4977dff156ed3dbee33cdeaa6f0d29a4200b1cb47e761c09842207265a4a48e7e13c6e5df6a3197aa719e29ccfc6e8798759343c6083700e0dafe1ec2d08b1e1c11fc16bdf564e98c52be95ed7c55bc9db3da1829eb034afd5a519ea9858ea1fcd10911dcec363476400dee3b11bc2e33cee0fd38089168fc76ec88ef7dc1f288516242ed0da711f49e3c19fe101bdcafbe812366478df9432e37d9b0fbee98af1687298525294e8dba43ccd946f0e695fcc530d911deb9e763c1e6fab418bde955fafe99eba27a943334433d43dc9eea97b7a569a82e5a319faad06ad1d1707b6b6c8f124400062e8a04a6b0097b5e3c1562fd98f65b301e5b0f712c19342de1673a30b2bd3b9854e3dcf11f2c0788f8064657c4675bb99da671c421dc7c9628cd228120955573cf2a472b298ad55ca8871cd93f1fe627cfd079d8efe834622d38c6f3371e6473f2184112fe37c8d44a69771ee80f413423c7919d8c95c9c18184f27c87e3bec3640a7cf061f214374208f0ff86d03b3e2123454359e6c3b0a05f6c55fda3b3fa224d290b90ffe2cf31166b0558c4e2391e9534c1f639c71fbbabd0f0bfa7e7a72726a6a3a8248d15938f743f28af1e7e8db37a734fc7ceae8bb140df253a9bbe21ca23c1b8e78f13d0b587fcd09b584ad0e80306cbd324d6ce6f9161db5aa691984c2c55a2c3d27894c5a34fdc775152dab9811cf972333928d1e6c89e0c1966b86555684a46850ec10fa61ab80162e680737d841070bf880054858250b204981c11229868432f03245084a8d274b12892de41414833460228ba02b48d8e04a152e2830c10503611491012743b0a24ea9401631f206594bb1b2a3c9c98911cfee23831298269864c4b4ac7b36c69557d8a22861f29fea099b3d5452c2c3ab96949404f150ebd5303a9ba60542625800fbb0e599ef43251fa6f0c1091553120d22ac746700e7c0dc18bdea9bf758bd30e3314e24e5314e044a9b940720898d71194742ea7ae8c53b47f926616060601ce8a57cf1603cebb0ebfcc5dece40d4d179cafb78e93bf79128af8759bc734472d77ec6f7ab313831dfce0b46763e3aad416c38f21e142ba4059f17a6466c10ea64d5da54b794379bcccd6e668a51d93dbb677743299823a639cece11929d96b9e96c86ccde53b60d724efb566595b2bdcaee6ee64a5972d532b65de79c933a63fe4daf4de90636c9cc118b0380ac56edb4f4a802640dc0477b6d0ae4a547860ae8497b4ad9eddd824db6943c008f5505c8ba016da463372f1d02796ef6972d218437517a41a7b0f4a1920f3f64562ceaa19290185bbc50b2c549488b20b61c255d0972bac202ffd51f7030860a70a005c7c3a42b3a00450b0db10955b145a12254d87905e95480e9092c414a9b16a3e9c4c9d0a8c9f1ec29f4304c8259a0169dff60971c50f03ce5390c251da6e080d20e5b3ea094848b120f4728f100c643a51ea478f6234a150841d0e3606b8735406080463c1ee0077bbbc0de3d7e60ced769f454455640b848bc2b20d78342a17f38bff30afa79ad84d60ab9612decaca873ae79e6ade825c2b9b6225233dfaab7a29cb7a2b5d6ad4a18390fb62a116e6d31b4f046767ecea879ab786dcbb6ec9a61d56347845bf5cee875156520fc0ac5aabab0d3891290406362b1874a3910614e763e54ca01043e2bb410a4c6166d658ac145931504890105112ba8a0480c15acb0822a2d11866974b6c4c14961a7a73209c256e08a85d9a21d59981d502d53c8a08a295b5481024d08551c51e361d20db6f0c006a5dbb8966147c8b329ecac035b4f2abbab43168ecbd9950697b506610f2233334b29657310c2c99021c31d29a54ab28cde07f45b9ced129852b6477985cc0b5f25a594b7331d365ae31c02c7128410564e4a1c4b73e258a2fe350e84104e31e79c13c36200216c0821c56aa675378510f68410e2a0d2d3238450353d72f006faacd365774fefcb308a83ca477fca4fa51581848484d418682424242424a49e1d45636022216db15589a52ad6458c2c7b52ac468e424d5cae9a69f48759413168934711e93987339aa1c906a78deb05c45085013230715d8442baae87e65de7837fa21867e7599ed276c42891833c9305c5a50dc72326866294b823aa00ea60c72e0be04d8c327fde6894819e9d9d157c52aa9c6864c450a092f568a89e506557da29d66086520e4b3e7acfc7181610678c3162f9fbd3bbeb31db2a052969a01965154260ea605d628c19cc18c88cf104473dc1881ee3c677dd818d3b43d8408e21c8c0d63ac56561edea61cc1144a6af47f0c2469e9d8359fcd19b63a4b2c4c62a71a98427962212af9f6eb7b031c87dc219fcf41c1ab0edd14b98c2c6e0d35b903aec4e3fa57d0d35575863be8874ce7eddddeeb7f98996527af56e2965adcc51727767157677bbc752e9abe6556a2e359fda959aa669f7cb6e95d21b366cd99285d058c39a397661e6366acd738665ded9ed6c6634cb304867eb748b65d740d5f1f3af11de0a55cf1ee5bc12d66a2d945cd44d326fb57af1dba35fe318bfa2cedfce2becd2e0837ff4cffb2179e9f4e36781f1b62afb30afdee718a6c52cd6fba9bcbb1e31898d0132306b019cc3ddb105690399636016775f9bda9136d09bce1a43367ac39e01de40666646c24092ab13d4e1bc75fa4729d5c1fafc1c010616875910c3e0ad4185be93a980b3bd95cf6f1a9f0946b3ba69389035a13623945e557242e950dea6425ea08f1e3e3c923a357a5c923a5aacc279fc62d3471859d2bd78249b524aa7c32336a8ab089a9c8237dcc236e7c421050e296cec49b19a691bd7a152f665c274313233324450cda059d1a831820df72e09353770cca49ab5363fbbf135afd5ccd5decc576f8723733e00f36da67fbb217fc301002e8b0060a1fa36bca1002dc1a6930473c8e9642fb9ca392c00fc2671fcd6377e9b9cf46a3ef36807036c552624fc86dddfaaf75be6bf69367edbb81abf75347e43ad7e4bd1fc6667fcf6a2fa0d4684df626438b9c3917a21b4917126cafc2623e63711607e53bdfc36c3fe46b342fd16329e70c5a9e01b1c6161843d3cc2a2e8e11116af874e1f1e6141c4cb787884c50e17f244e12ca8280521c5c80b0c3924f18a5bbe1c41086110420f21840c2194114208236408238431c6086552942c28adca593162ad0a79620703d6ca2e0a3fb01f7cbae1877376526056749b8fffe1c4200e427b750a29f5b18115e442b145310b97c5a6ecd6ca654e1e85201b8026f6834d31c6196be6510be7749edd9e226c7492f18ef14f367d4b2718db32373ac5c426eee5364981114ab2f7c62b629391fd2012abfdc87e314a9417d82f1211a9c07ed1891325ec175d2e22fbc524314909ec079f9e56603f98250b0cec07999892b01fa4422505b65d4dd8afa7c705f6c3c161c27e353548d8951396faf41509ac4e6c92362e7a8bbe2fbf6e606b1c1282e5e2cb09db0ebfa53c546262e5fb5f2b20152a59c0cacb6f8e926559f6da386f052456090afa4d763e864c603f7edd786311eeaee8bc2b7a81c42af103162a59a0cb532ef3da09b350b79bf41073178a6dcac2ac8e4e4bf40d6ce61148f5ec4220fe379d731fae1ebd58fd7e34723e3dcb327eceb3cc6996c18fb7488d2e66f9a871c885ea597c41a57ed22e29b3eaeaa157515337e19cd84f388e731ffd5c11e7ac8064ce39778b54cf6e642792727b7f7018a2092e2d6bbd9f55920e783144eb47e603ea86a45c89eab556df64bb9ed49bf9f43887f194c89c7ab46321f38a705e3b17d833a7dd0faefaa6753110d9601c760978f118ffb18e849b5c5081168c731e04e2c77febfdac7240c1095c2d998bc41231a505e331de0f9745eba787d952a273241d8216cc55823d35a3d8f6ce13e2e34aa53c7ec236ed5b45d9d48d454deeac32d3b9ac8798d5ed2a8a4d63d8a6a2f60f3655263292e3b3eaf19324f825a304f66bd72b36d91cecd72ecedbc539fc84f36213b3da338fe6041d587ed2c4328aec079f6ec8814209e09317e74c67400cb20521cc6af700f4602bbf64c3dab036ac5052ed1aea1638677a7b10213b6c13bd6f77ed1e02deb46b5e6300ea68c7bc6e026fba87a42be3b2ae07b6d5ba39dc38af59562b92e7bc6e1cc7a5547fb9b915c7f14beaa4e08ac239479cc43950c9b5f431956fee3f36622934dd5c0e16826faff5aee2d726a9d34dd8a64fd881fd3836497ead4c60637b2c8a51a2d176abf7f9578ee3bcb2505da35acd1c76358b35c2b9f49004138e3608b196b29bcaae614aaa522483242bb4b0e228c90aa3d84e1bacf91ace3aeb8ca194c69692e5c0e96eb8f5b6fd36b90bc56eb0ce3aabab89d4a1a1f1d80a6f94a35d8f7ec22c4c6676cb7018d70379264da5d1b43aebac2804d998ad3d6656c98113e370f6401e76e870864a0487243809b7a570bc6dd447370bcb3940b222b6c23afd92e123380f22780d95bbf330c36d38ec68dc731e564ec3c75b4df7fcba0d6f55bdc69d041fe24e825727c186d7dc1f12dc86ff5c77076ad1f0562f787e7d35ef10cfef6a080d27c16938095ec35bcd11bcd5ace1d36bd4f09afbb39a0ed4aae13f357ec36180ad2456da607efdc6fdf9b9e1376e953618506b04121cb648f04ff5180917b6ee85d2f38064bc3d1ba8e7018d005b343ca016ece46d226661de37866029e63197adf4eb31df3c66923102bbd4472d857334df1c7b71ed63aa538f9e8c4befbb5faf741f29433d96919d18ef3372bf1dc661dc87bb73c17a9491f1188f763d625a803fdd07fce9403fbd1e22e88c8cd7992b85883a64985ec67da48cc7798c87f217efcbf1304ebd8f7b98fbe23e72939c93ab496331de6f76b710a42184137be8995bcc535e74f2988a911cbf79c6b2ebb161ae79d06b295d1fa9c0f292d4e923698361be6d5f0c400fb6bd8f1e630861748a4f478dd41e8e200b9949ead8aab2ae62390b7b499d5aad55a9fcf2eb316f6f2ff5ddf990ce43ccc2bc3decdb0afbe13c8655973a5c3b86798dbef2228d07e50ccf8352040f4a9b0fa72d508a5d07d027852e215c61ca39af845162f70709d6c22e840e2d60a3c71ca8c4a4cbb3d755a382c4c08025cc8402272520ac7c03e1a469cacab143954aeed0156c90634f996fe7b9846f9bef1c37ec7d383bd2dbebee96b023eba061b70761cb164698b2f35d7b0002ac022811d4e0828b098ce0a28b232c558722bad080164f787801096812295e30a29d236a0d6df2df0e0e5114305069001ea93295d330620993951faac8c194244045b8e055051155a014910413581227524e78e84074450822b8ec9005d2c08a2d5376e0820a97a7244c9264000706a56685168268d912235687049686d18959d1ebec9edeb3482f61a1c7a48dd26ed8ce7476ffe8bf124a0c92f8309de02203b34b2771b073841735063b8411a7173b4754a952292d11c1f650698907ba701a0f9596ec204216be62f550e987235924f350e90727d0b3c13ec042a51f96fcc7d13c54520204ae1bf0900359930f32e362997cb01bf4106b78a86287ec68059f168228b101d73c545252829f0f959424f10456181b2dec8eff7cca752c91ea9c7394c7f9a6238a85cc424929573ea4f4945389e27e14e61dca53bee958c24affe28b7a3ea60f7a2110656ffef1a6529e622e76f356d9168459ec4393870fff8b148759dc3b4236d41632ab17605cc61300e33237012f1e736160fcc55bbd20e330320eb38af117df64bc15e6320e5b31de0a8b812de3c156cc4c37b76a8771d87af1567d5f1cb6ec12e7b6aa2cb7c492369cc3785c8c175f3cd8b21cc6c52732c638774438e7bc55778f592acc6229cc627b64916c956bed927cb13a212fdef1c9141b89e2cb535e421796fa175f3d1db046761e753fa0e82f2c6e6ab2af1166c93003b398015dec078f988e78f60504692afb4884a20aa78f5f7c75cb653dbe5e2fc803a91431d21e61ebc501f23aa53846ca066b3e9ba31d1e87bef94bef06125dde6df319de07f4337c86d3958d1c1db01f0dcd108d13ce198186b3c7aec708fecda697524aafe13e2c2502e2d738ec6cb80f3bec7a90e03ebc6db15b01c0a1d4e1e1edd72bb272f87236c9e170e3e1cd168df7c5a7f12fa23c0dbfbe315753f382dc7438ec7adc9c60c3fbe2db78417630d0160f1578789448daccaf7ef6f3cfb6cac5ebdbf550bec57bb7946f193763498f192e1ff16708c19bb67e82751c14acabe0996f3351d81b9b6fb3e994a8cd28e55847e39b53613fcec52d256a8690f6e25fccd30bb38572491bf6bbfa11fd86dfb89f0e8f0ef45ff7d474c3e36f4d37ce37776bd2e16d56ec1696668886099a231a2990e786b3b398208f0acf325c85fbf967321c85fba93e93e138f784fbd5cf704ef02ff59c439839843a1d93c78e85d4a380c20a05f7f196f3788fefb80e1d3c3cf079f4782b142e941e2f80bf774c3897e94fb85feabfaee511c07bbc96bfd7e3373c9a2078c34e3304e3a987f12f05f31e01bce5cd78427debce60eaf16634cd7092364c33c4f4ddd317f3840ae29c1a6eb80df573bce709f0f7c7e7d2bc08707f56437838019c00f7a7009766e8ae84ac5888373c4e69e2d58a5058616a01b576dc8acdf174bcc7e356cc93d4a1a9d5c6dcb8e1c5d7f16ab041e5b40450073bcd109c99e9e9f1a50ba8148d36754d0b95aa1100000000f314002020100c07444271482c9cc6d93af614000d7e9e3e76589a8bb42c87511432c6186308208000020044648088883300f1233dc602bffea6afbcb3320d16aeb9037f28b514d779cd1f01c7ed209066960d9df822958ab7367c56a8210083aa9cbac2a55611cec40f04ca1acb7a4110d05ebddd8c872622f1badd872a8a5d1556a5b739f03d52c3ffc9a90ba3b6bf474dee5964a481808a40d99420fdf3842ba5146ac92c3ca1a50dd2f0522cc53c5ead06b44e2c9b53dd311ab5ddb36763dab46ede6e95d40b0ee012ee490f216a7bb33524e4f2e8ab998ec0e8d1b4166a6aa7ce67ca456a5d089655b40558c40611857d4f92519e87dc6c0268de1fc894af0dc2faa999b9baaa25922d3d2614389cb9cb861ceba8386cfac25d9e31278cb5fff9c096a056132559b0fe4d51dd891fc1209af7a43d500147c2dfe0e3e6d18093708b97b991b3f65fa3087ae85fc8d8e431aeeda06d776823eecd3de4c1c7311965a45fb925d5734de015882e85c24eec3f11d821dad253d5c47e28f27e5489c216dda7981d2b67e13b8622caaa9c0f7ea18fdd9ff53d08b38667217cd12e64777d8bd38e223957b786858b7b956a341e43d6ceb2efe9e6b2d7f441f156c4bb0dd82159115a06bf2f6e9e55825921741485b021b4d66e6bb27e71ac50de534f33cd4da0d6e2342d9eac01f79a89597d4622e8eff0b5272d3828721e1a6c2897c27521403bf53c07380a732e9d5d78963beedcca3c496263e836523fae9052fa8d4ac72fa612a2cd2743cc297ff4d7e33949c2aeeb830d58bfb1016161ae2df3932ae6e1724c970407be8b44f4cdf05c65b0eceb82d72d2236c8b54669b22a49f265912039cb61af84b1d751809f6e8f19b0938d40bf70db067bbfb8e0533456ecbd1b5909996b783ef5060eac5fe1351632003ed1d1678e89b0f69a22d802671077e25ed7edec1820f54f65f7223963c84445ae08e072d50aee9c5770ba4b7bdedc4a531349ac5cffa2017eea38aa1f0273a73dba2f7e6ab3769e1c6d236075d5e12b99a00b0fe5d215ad8aab96cb88552de91b7fe0564961c8b5c58eecc0601940a4409de6bb300a4b6b433dbcd2791942695d23c00c06dd73b49bfa99feef38d45cc4f3bfee13c7813d5f46915c16878ecbe3f6a4568b591ac4e300abf023827e327193d91e0c8b60f07d8bcfb1fdf6248eb60e2a08649ec30a82bb0de83a583231c8dfcc35700a814752f6f0b05ada14b99cc50b860cce2177d354769005415035a131a41996f25f185fb1f031b4d147ab22bbde70a1a9055611e2dd1616a2cd4a9a9f32d9237bcddfa0c0c801f5ff0a8bd9b9fcbf7df3f1db8179b24a0c56a884838d22dbb4c88400c1902ca2d5dac409eb10ba25824a4ec9fe419d71d3033a42a4239effa292301f82435e536344ea0b55d648493835b15913be42da96180a20a5a9a0ff8129329e30bdc1d55c7649c3d3e7389b951aa8113577c41877337e73f65b9aa26c9e651b40eccf46e858979d437f8829d743bb616356a2ba6ae2bdeca4ba2cd0c78f08ee71a9b0daaf5ccfac78eda7fae6272f1155c162b5fe191e76285111f1bb5fae6daf0acd5d62fef24a7f9f3d3ca525076d0e1db9c1afbc06797efb519b44d19daf34018fd59b7b2fc1c7376b4fad350d9ef4024da7ffbe56980e13cf5a90349f9517cf12386a07af780ba2454e46544f67d9bd000be464b07800f7673698acfa55a90777b25e7c7424c24a20cef768ee90031a18624143c34e326a5128b1d01bd855a410b2b20facd1b5e8c5e18abe5be0676df6749aa8a78905196a048e8533c35f7d8e2a646c32a03f9709a4c16ac1751cab3601882943a74c7dd4268001e69efe173eae6080f4baa60227bc4e7098b3d48412e535a1ae4f21b01fe01f0c2cef405a14932c57eb4c2b9be36942443b34153a043ef2ac4968523de3dd92443046a1eea4c8649598ea5668d0f0df3bbecc2ccc4baa34c905c37f785f3dfa039298b61073c16ff06e5c91eda13664a8ef8e7335d6f3801e4a0b35c8d8312f185192e821fcca1998827f3cfb1070a6233602fb7c398f3c2d1bc888f3c1c8e0e30cbd5e8e9a2d5f4df755459dc87a079706257041a4f89354652db6e4387da43c53cf95df13831c66c67f868eb42b7825179233ba957e8e544c1e172748a8e48222b969436c7a1b6124700da441831beb6baf46b7259ed996b66c39c523aab20cf1a1317b337e404ca86d49989af46632b96ffa0ce40a96ea0e5b374c5664863ce0bd08b382b0bd14156a39c6090b129471becdbd58122714ce6363ac26888f3a533013ee4060aa060de8d70a4b62488c6fce645a859358c4410fd4248366a79ff0639ae895aa9a9daa3607f1418c5a84fd6034c173026088014da939fd73718214fc9cf4cc70375dd28abe7811b8c5a2596105ab9c911c059e77235cc9089c8e9c5dd41780535571ee80727a11f26a419ad50564aac8071697e3e2d05c55822e63aef4cdf5faa2613e5cde316d32a174d040b143171e8f785143631c01365f7a66baae9e95b920cfb5d45325586c76b3e0900cd5dcbc287df4d948ad7dfc9e96ee994a1198d9ae7ac6f3425e0c90cd3228a32e1acbc3bdda82fbfe1028438b8f174bfd0614322a6f1b8cc1823719c3094993fda72ee2b17636991e1898636469e65230a770c5fe724a4acc8274847f501aab0f9bd185f705d1a961a98f2a10b8c61975c4c9ec0cf97f3daf08cfe23091d480583514046af01bfdea0494b392b950c76de5924d62c8440de7a77d2bafeaf26868883fc87c8ae3388132760370a31a7f2aea3ebfd131ca0aa017f24f606d16fb54a8d4d778963f4fac4eee846d625c5b1d6e0dee8b8391e6d478cf85c59abd20a7324a92c433097c00f713bd7205ae6ce8eb7500453ca2e199986724d00fa4e71fe6c70e54748429b0e46e3519b5481727ffbfcf7188dbafb263d2930612695f178a24515796cbf7fccd332bed37dc47af3e4f749d903bb3a216ed68d33e041f7101e92c76eea6b1fd07f162990c070c0d8a07388a664a6c472e0dfb9bdf6288b96ecb431b306ec0d90d4f9b8dd7276cb9d70098850cd41912ba2335cbf49a71bd862d08d03ea1bdf48f35b06045a1f1c3dca7cc3c5b03051b4d8ed81ddc08fce7189909bc6a3839c7a4c0a9af7542fa3eeb0073d2aa5cbb8ae203628eadd68e0a841a21f6bf4e951143cb6925f8c5437fdcc8040de74da2c743a65c6ee3354e4f7f287d22fe01d707d32c72f922c5f3410e626c80463a8df7990148e4cd7979e2f9dcbf059d8c132a7fae9e8f5513303b81f1a927727238cad09661eca44c78dbba0cd4c020a4d1e67c24d4d336e531326bd9038c6f21982b660bdf12f84362c0d9bd484cb82e12b84e47a81f4fbde472f229ed0d4e5139dce6343474a8d4e581dd1b4abdf011a029bfacc404c839119c8c1e915014b6684c8425d259b72b8a037b0abf7b7a870eb0ea893d3d4a44a86374bb9846c16e607b129312bcc0c3f0fceae3cc482779cef9ce0e5e532cc1a86f6c280cf30c6015fecf19a95d1edd83e98cef41909be0064e01d73313814d57fc7b46af8afab8c9a7728153fb839e5ed39f8b956185826b0c686d572476af426b59a6105a650e2bae421f0447b7fbca77a4e3daf77ad007e926d03f5631210c9de5e69e2a146cecfd6c3e5aabbe18f3d26070b14711ad534c9cc84ea60835e0ddef09daaa709b4ead715fe7009c025833096b970d4898a300a9ec90641612d92c8d7daa06a17d5d45772ad73551973f157460802c1fa24a29ff931469fa97ad1a628455ea4daba5098655ecefeb2eb1fd8ca018b765e17a9d7bc3111cc09da467d3cb7e6507901d94f34bbec29c5c32c35b285159ce7de2c0ef765108eb634586396930bfcba43aeb381aa8df6547737e2d1bd8a75758b95693dafc3ad5c7f64c1fe80db20e570f4c649d03fe64c850f8d162d9fe14a1c2cf4abd18976556a740b09360ab7810ceceb472f7d4cf6e2a039ec67b1958a8351353653407c1a4afc5e10771ca298fef4a48467c2af401abbc4d9bd93d7a820108376786958fccf055599925989578f5ac77ddb49f8fad4d048d9527d1e27327fcf1aadfb51c34fb450e60cbbc568cbaffae41bd8bbc48b3491004f284f8c559e9da57a1e7b92345781d59aaf8eee77aed5b69a6c0cc50a1251dda38a30ca583a3332db4cd0e0f74dac646f4692e3d32ebb1e171aede5e0f7ca3d7c2c864ac9e8c4127cdc13a10f65a52705b5ed387e05323e3877a299332777134fffc20526e0ce0ec9d37606b3a5a8e1c63788b1baf9580bc92373f53fb8f1f385ed527fa121c5fc3e039b07d1a16de4670ca57145b0475b7e55a9371cb58f8d41f2c505ce10b3bb102cf3140416cb2c6c4004d05884324567156fe2116478881bf2f97d31b583cd7d844726263b95a7877940b0ba0ae4765075cacaa1ab5218362fcf2899a2f7b2bb73760ec8a5689083c56ce82995b410437b30ea4a7800a89cda4f8aa5fc3d6f91669b5659e415e6d1191c807cb9ce4dbc76d86d52ce45834dfc2a83e089d81cf3d046814cfd915ce0dedf03d6b6acc3087d4d19dfab3ce67d5077b7a3dc0c66dd2aa127e8dfb0c77f4b54d2aa17d0c8d0cbe13960fc6087d63212a3dbe7304b534a5175c4be0a5e283ed11c4c7920b137953f38ee5e7ca3661ad8ef656dcd181eaa74abac8b942d38bbc0d332630b9f3e15f0d28b6ec35be49c373b7982edecbffb2f0a1d3b2fb0be55cb38509ebeb6d618077a2cab3fe54785f03377438d19c8a3981e03f0dc1a6f4b8e4d15dd841298bf0ecf2915960f9ef9dad41e877033e99db31be9ea956136aaa67089e45055b85a67aa1fc7f888cfc3fcbc77b3a2b85903420db46482feb9a25b41f5561003826ad9efa420a6ad7e3c44d4cf03dbc2b4a1a998e13cadae832f2777ebb522ba04e5d1917f685316b46eab6416eb4f479e629779dbc2acfe4293a79edf22d8c0999f36c9a45ded75a008db89735eb539407fcca5722158552823285a53dabbc935a2e2f08039985ae8daa2bc750d93308a5db20cf5e44ee980846e49cc9e0fe18637cbb51390c9dee3c9497b497e11f8434b3e99123650d8cb232a5db30a7c089b376048609ea00acffb2f670d99b3fa044e89787050180667a3a461165a54e969e9a87514b1cb785a87845badd942749cb06bef7886bab391f4d269882600c8fbb759b73b22f8a0f2216d00090f0affb0b1fd9eac580fcf31d650212ceef3874818cf0a0df4b52aa07a24287315553c7aa0dc35ce780593d8c420c7b241adc862799bc87945dd9464a7f596310d8d8424369309cb096a7eb884e092b39254801e7c975749a78d26cd2eb0055c262f95c4e9c2d6127af29b7339aafebc7b0c3c0ffbc5282d82538c3bba328bdbc319d0e9b4af5940a472a174a93eb69c75e3c5f7aa61aef33718f8fd780047797b5b2fa938baac04aec719188ac0b3666015cc5fd03db1723e9a26f390e2935053594c99cc6cffcc1491e697de8bebe524a9e9106640906de84813aef7acd293b03ad3a5c6bcc3575cee4b02c16dd0775fa3d25ea2afa206fd078b2251c093bd900445cde52862c7d5150c0a23a4a2b81a00d6e10edc636210b5d49ff585a045a8d8ef8be0992327a4757f74115772b1b3e5014e05566014bf5acacd6d68411a4205a6f0fc5fbeb544b8aa4bc896e2c074dcca8d0918d6b046dc2690ac09b72b823ee5f113691fc8c1d82073b1e7c5df50561c5ad9ad1cd809563ca83a6774d744ea32753ca6e68db541323c64b08dc19a289cd977fbcccba9a582dcc69b2b922bc8cd860d0740610f10207da135092eef5178f794c864fab4e0fc5669ba9bf3626646bb3638aacf0d8362494617a827b0d8da8be181d65fe5adeb439674c2b4382f22f07f7b2ed0691cdcee46444ba7fa320e77917bdce4498dcf90fee5e430dd705c99a7f68ed8af326f011756286344e0f06acf43cc867b11edf132b0fbe85278f7aedb209b2be2fb9e1870f6c6cf5b09f87e22f11801bdcd65db5705b9bc17c8a153c7f7970a45ed8545e3998f894019eded0c25416930156618f4afe820fc19a6534dac7a5d5ffb20487d5052269e351dc2c49f94da81916aab2053b33a942840cd3dfc0de97c6727207d8a1e3ac60e621b2d2478f7ec6875ea0edc1bffec59944970426ee0f07471db4289f4396332e18369ff6a4af8ff6ac8861135a1027512873a2a1e8bdc252cdaabda9adc25279dcd9097f9ff02421283d079198f3650361ab648381a7170c179b29f58a03cbfc9b0bc0cf92aeaf71a03a1cade447c972018107fc6f2f74790fd75b9d5a19953ee24e21901d16c0c38d9a62712ba2cdf34709c8cd8da319b12c270aef4eb56a6af8365678f05ed3c15d48093118e10cbf803bf01bcce98401d5139d9fa4322c19d0b48002567c104f581077c4d172cae875a4f1ad19623169e8c9544c34a29c77a28cbc745237704036eaffa06890dac4f87113cd9441cfcc22fe5ec2f283b0baf428c2d9c6c45810b81bbe4023e0056c26b63adc5696dd997515c00f43e60e9eb70fb702f9c75e28fba331fb4129ce57fc158a40c22cc49a7af063125aa26f2719b29a34d925e1897eb35020caa4371abb09063128b91a9921ffd0f8e156af03dc2aa6939c90f398fecc629d678d5c303fa265590e1c0beb7cfe60849b2b928bca08badcf15679e2c9437f968886c3e1bd88433d1c86e3948749e91cd7e399238374efd91de638e4a40a929e6a64d372890128e813e24b5f3703d7b755b8519ff7813cf3b1074ed28441aefacd7d8bc31fe677982c4457e0328c218a90e432d14eb80d3a940f4147ee7e0efe9c2a67de07f263d21ecc29697352d5bc29f9d8e4c3b79c1636e75b2a5e7fbc1e773fd1204bd1cc7e5c3da2676f41da8472c09e52b27db383cfdd4fdf14cc47521446e0cd7d4a0cf101a4e5de90a5c2842602c16030664c7aa699a1bf5fd806f7cd49b154a7d4caf111d836018e9094e9f5503b2e5e71c6dab242bb8d84b5cfa0597a9326c49c06d4a36d203e1eca9f4373c031cc3f4ed04d7443f3bfd18bd056b4aa05b88e833cf99323aaa1b116816569770ee3913ca4bd0811097a14c3698b70a5ee70c86c3d53bf1da80e1e7cf096cc369d49f66034ea5433a508c4fcd33b1e48b2b490c23f81a5f7c9aa4b6b113f0c9e221f2e0f2be2ae1c2e28a2fd383c67bf6ca44949c496cde90cdf4a44eabd150324da89696b8923a9eefa1d76958490472e4ba772233ccc4b5b89c729fa0e0e789abd948cba2ec06ff764af76c7c193881ba9a25b6e79e6b2d147a7d3cc48d6014ba6a93b3dd98844408ed68626c9eb0de83192047a860b70c62ec6654e09f387b6892ecf12587b7118af5de5aced0bb143c7a20ac3777ceb08ada9aebcb0cb8d6641d2c687c0c33ddf3e1582aa1aa8acaec8d2475b8c8f5af48edd875699370678ac8d0ffb5b70e6ddaa12590f20580c4dac684ca033764bea87e80c6c67cd1806a0426d7e83d4a23dcac361e761ebc84e427761333423d1085f3b2d501838ca9603df0c1e904c10a823532e5b7ef592ba1f3b28fa74c0c818be139386c13bd747bf4fa1d1ba573362ed94d97d5fdd064c42dc054a7c7a9dcb0614a60e69f55322bcbb8b1ba229cb85399efff7d39e8934c9c7575adbf35a15abcda6377d979a9024034c44cc488ee7248a65b32f5613e869a93321773c0c243640a40d481dfea88004761b3aec4d7dd9ed4997d98774e240a6ca32343f1dce53a1ae9044beb2594fb1696798c0e99854a4a8248b8f8983b1ea2b3ea68c5cae0ed5bbbf73f4a4371663860e6059a04b396867ac9b7179f1bfd01c8d9a0698165491ecbeed69c39625863a6132740f1b7f79827fd9628572e126acd53ad030138b37390fe75bd8b928df46751f2ee218c1438846c04d2129da973d6ffa7486121ce72244143c96544b4104f41bbae82fbebc225565dd4708316decdba70b72ee7605e6171cb9d1a0a382786f13152ee94b0f247e66446c85cdcb776a345c67d428e91815004d1e700d904a9678c07e3d4b39fe8159ff481d7866a5c2caac3c71f33e7ccb0033018e1eda10cf3453f8bf9f67c9a1320ad17359cd494520251569c68b52582f0009c698c8347f7aac746204477f43e09871fc90216d2d3ef84d4e6343c9f2281fe35bf1734e483e5f63545ff52205e9e66a6a822f6f31c0f99539e44b876970028fbe2194a0c97e0950569e8c28139dd75ca38c0334cebd55c162574d2c58a9de39eb903e7931d8f8e881ef517549b4cba7d55bc381cb9a75285d9688baf3ffd65ad5ce90be4c41604d97cc79f276aefb59c083c974f6004ad43d7979e97fc892ecd6f4755e6a28016122837616197a8930f4d7b67a6bdf11cfb9dcfedd46077fae1c5ecd5b28b6c3aa258aa8151716dc3e49eb1f2e18c197c28b38efd344643f8d57641265bef504830f098b6f0e17fa6e0650aa1dd7d1a3ef5eb96dc64431cbf05b3ba13d6817a1fcc40256222744d9ff560092dcf60f5a75771e088dbfdc305421409380e0a956e49981c67b5e6334e4f2bd2437e7da8129108228e6541701bdcaa2856a3c1677aaefa36f2b8eec2354db53f72fa457b9a9476e7f5ab5726c3fe578ac9b606ba0f5718b1cefd0b898c5b41587dfd698ed352473881b7994225be23fbbd61fa6a03a965c7fb10545b6c735c8b537454b4d1d7083399b294521b8f9acbfa22596e0c4a6177a04ff2c9693b7df3b81221dc037a1b33ba8e7e19413a476628b26d0811bb72f0ebd882e4e10de949bb63b0b49a09d1cced0ca40a1e0a2506e92f4d8231dfab966e88f169acc0470c6e3d21f04cb19d965aaf47e4890ed755a1a5bed89e29b126340ec2ed2d4cf9ca0b3669051c834eb905db64e461938282001739c5d254c62483f7d3022faeed098848e604878d72fcaff134883b222b54e57aab0a789db2ee7a7c9b6fc79321ee6e3007b225ad13b2730f385aab6fabef4eb317331ec2fd155038b0564f6e0a9676d5064bbda75990428de9af2836528bf1bea6a499c0d52346540115b49daacbe1c32a4223207dd17f4fcb8fe58f812313bef4811d45ef6085bbc76cdcbc76c4dc01e56501d40dc843b6d3f60921cca801d4aed2b247400d8c020b755b7c5f547ce26acd61c9cdc5dc6028889ba8fd6687d804e10deeee09e58ae7c579ee35db572044441ee8bb7ef83e96a8bf915ab75b4840c1d440ae11c1a7d1c46938d125d7de83a188e9bafcd58bb4f7d575659fe44009bd00a77a6e6fe0e557bbab5b35abb656592c34cc64f6941eac98eb4fcf7d7458c4345ee2bcb1fa798c96f5307cfbb21d0267170d54fe2ef357667f1fb2ad7d25d06cf29530f3065c65627c6cfa42829c8d16862a0b7486202a83c7fadefe8ff6dccc04b9c2923d53b954aa3b50780e81f01a6182b6bc245c327b468c3d71da5f4548093b57a76e83e5be03b86e1f0c383fa5bb0e97499d23fda007e5ad3929dc17118143fcf66fa247f7f10038f5d9474c9f7e9b9b33b7227fdbf7571870b1ee3381520dd7fa6a3a006c07e50bbc82331e61dc97b66e3d378db68bc5bb7c52c59af367007f95518ccaabf9a731026053d71c5f2882a46c43bd2c5f961bab8cd60651d5b0b2819e8abac72dc9a2b22af463248d613aa570f8917740c071994e6609f10148e9fe8751d00b90b4692c59be2561151e98a55d2c3c15a1f410275c58d2afcab2fdbd2f8b6b1ea6d707faffa2d6aa1f3aba6eb2423fe05283fd0c9ebb2002c46fa8371c4b3e50b3a5b620fdf08ddd9714c01aaf1ca45ccbf21fbd633215a5312ecbcbac925e739bb3a3ce54e8de02eb6dea4af85b34871ff166ad5bdef4ccc4b5b5c6b361b2e5bf7f6a80d5a827365d93e08cc826d4fff0e1b68d7b1d19a04a0338b888ad98444666e53980d417b79b80c477df94c9e3e3f91ff693f55e23ae34258aebaf81aa442a50ea3dd5c789895e98e03caa83813a35a05e97205f1f31ffef612c2b8cb55ebc3a44435ceef181aa946f2b68de245787e42255fe92c89d30c0b4af86fb0391119bd5d97a6384a27bae50dd7861f992fa59b474f15bce1b783ade9e0b38d1b09c58530b969a66ea4008f8a51ee1243c8c800fe6bd316cf7d5415c8e21c3eadc0edddeb36ef11621565cf236d75f88c49ee8052b68017bac3f3f6ad090c2012153b0e3425194ef5d02a5a49ba18d4e65a30ed8c0882724487efd35408d62c9a236d8831d741fb20b2c3c39c0fc5d6cb809ad9b6f05605cae5cebd4ac413c0dde2627750b7929efe53cafa4e500252c71cc91301f3fc8f8c5bf5c420cb5a98855845a9ff2eb937313ca06bb607520fe3a421eafe4a4a0745f423d767d8c5b52d29cd954278a804fcf01478be289d7f00593067a530a73b907ee35f913d4d441ffd4ff685f3f1a8443fad9427a940c24155966f7dc2226b7a683108f061391adbfc94f1e1c8a68dc34a43d1e4407949554171865f6fa06ea0c3ee481f9eec788e3026f2650fa956112f007a8c291e39b583c085d97a7bf5acd484b2ff202e3eec7bf1120f4b48acafa3a46d6da6d1484118ba74ef8285c1bb3fa66c7e055f7c98dfe8e83218b9aee18d866e156c8640950e6ff8a045d48189fcebb4231ad01dff0aae4bbd62a355a7cc0524fa79a7b5724956a4d687e90d5bb92f0e6be6b93e5f0fc8f63be20a189458c9cd4118dbf1f1fa08803d62924575d0a2b4b53029f729dc88dbe727c32da13fd6385aa1fd1d17a58461bb732ba1d7e4853572ba614e9f4792133f99c18f5861c9bd931794990c04ca22d634b3b5f8e26e0ab1156a34a1b91c37275e48e8c9a45c6126386aabb2190aad79acad100509dc18d46ad805160bd2920f09c44709c37eb9ad7552d2c728ad2550933ef7c37791f11c5315666b528ef8e055973b6af9065c203bed64da36892734ebe186149c2039a857653a1ebd25d473958ec2ca133825b83cb71e9fca85b4ed182d60ed2b6ab1e6855ed8832927b44e90558e24eb72f11a04f2f5f10dd5fc031474bd056dc4525fea9053cbdfc33d0cbdade1c258d7ceeb97d8b638b243f1203b85ce1ba8c3d2edcd1141c23559a024352082b325ebb0df05e5287f1713eb3e7b3bdfb6159844227625c95f07157879b6fe19f26e69a95add7700e010fb554b177295364a7d932b0415f5ed75beb09a182e8363f4c3533a227f1f5901b2dea8a167b21d9444445416626bf29bfbcafbf685f3d20c3db26a0ba5d0a066862c0ef2a4125347f585571d4d7ec043c3719d0886b88070c449dd94d7638d48358c714b72adbc200f315da539bb5a430e0f0976aba403f67f851071e4be0bc16100b61fdcdbbc2781d3000ff1cae3f9079337da7f090e4ce1e99b50870e6715e22d0aca5c9f31cbbdc5ea630237827266f0c2672ee6cca6420c691cab60052562d8137600b101e81bbc84970a86aafea2081bf394e32b42845cdcf9793d6019e1c3df39330bb3261af85278fe099a12b79997508389a788fb35dac1a40ea5484232f4a36c6e5f721d8d5e92f70dc4e8ee7bd04ec3d9404ae6a3fe2cf0238100710815e2229cb487530adb6c09e2bd627a0462bb2ea1cad244f77d1861f3f6eb6d950d62b92be4f00eccfb1ec486ba7f98c059411ec938b84294c010d694c6a0156de2350bc8608fa01a10c87232ee502de81e6140e694680a2522275802a5484f6c1c49eb56a3ac7d4d0d48c493be6698c414339a80cfbd49976079a56b7aaacc3d9ec542daf6912125a8d5b288ad10ce9e0199561b5e6f14ad83dd176a276ebe4960928300628e99813ee3577ca4d6f8d29b0fe3598d5c212f2429b2c5041e2dfea43c043517519137aa90a5b757b78d91bc0af96befa059890495686da6483c8e21c817810f2c1ad7990fac43196ca8cb49a47ea56f19e9a86a021985a1c47f4463fef055d61ca80ba586e596998724d32f932d063af1eda00e70485d941c7ea253d0cb46a2d5ba6b6203b95cea66fbbcbbd0e2f35d250b51ed15414fb8df398bd52d3109ae637e20a0de8177007fb9d0e0b40679a0ced73f2ec64ed1e74afe394a3ebc5495bf0f464acdedbb76552373e217e79eface0440e89f6ed1a05d7a67ebcde5031ec78bbefa0267e0f1369cceb1167ad39f1b7faa36f323e2913ca7e827feabfe472f4890440daf2fc9b9323ce24915bec144daabd6cefc2eb38c4d082d5b780e0d7b4b3e9ac77560b0c931e7c65fbc3569fd45bd91bf128884ce54afbe5c738b5b6c6629df48b21d19d08f9cf4cdccf12e2d90a1c775855c15add606ba24a8f229ed3b58381bdeee7ed32eb74e6f63501fd67f6f9efa055428569d9244411b86a31ded970d29317abfe811413d0e93c0d1edfc532e1dd883088e113325d90b53f20d44897105caec244fdf9fe2cf3e8ff48228af9bdcf6fe68a87f8f806d6f3470d4f4d00280b86cff01e00fc6265c9bd12b504264d14339e086045bc04991f1cb9d750442be5ce053deeaff38cafe3ba8d342ff1d73e25464c0184f1fbf895083a9686aa4a81042dc13227f83c8f91d43b1e98f914404144fa83a98d373eb9a7b9311fc9ec783f2a0de416184cf7102fc69555250b050028d3b76454ea070a92b29fb885ccac6905f3f02087e60a549af57350ca49ab80aa6267e3f72bab3cb909a1c016db09b237b832bf05842229b0ce57bb7d1db092ef7c793241c29499a78befdd7749be325e6075504f1583b061cb71d952875e9395e7dd0a64595bf64dc4aa4eb92491dcbe61b15a1d3c81e92c29c9657b421602bf3ae384574dcb53df6e0a7771332e20f93493efbc590ebc71f74bdf31bf5fef65fe6576afbc71d5d38d0406ba9e7917f07a09d8642860cbbf4a82165d5357374eddc41adb835e875e92ea86155e1994f27747ec0003271f2562cf21d82c263dae597e49557cb66922d96e84f3e95afc468268a0b3ba9b3a14e91b7b8ccea3691b2720e5dc764408c164c92da704e25108c70c35be53496a454acb06844d150b6ad7c1807fff298278c2e8c5a5cc2a3adfc1c098f96956280c0a419f4fe714b968d36868bbe83b4e087482d38c47c0adf11533fcea298e77ac06535065ab645fad8c9078a3991db3118cce6bbd62c08bf04e645cd513a2db65ddf65d11eb57a4849fe9c529ef419920308549e27d0de69c4c562c6862ac964154b316f3af0e1c27888f0736109287b4ecbe3c374610842520d3e4a260cc043d562814ae49374976087ed2daf43177ebbc0c73e0d72a8391d5b652aa9102ec046fd9c8abb2bd4bb3e31a56492d894fe1c027291b5e24d7f9c8c5d1760025331b495fa2975bbe56007b3b5de028bbf5af0af40633211080824d9582c07efd5bc173d5b6b32f73fef7477e3a2c5406a29ff9420751db9b3c0ca817db53b79b4a90a594bc74a7ab03d3e966f7e7ecd58a930af6286e3a7dd8ab113d48d3984bccef2c4bec4ef1ea30a36dd65303a0ae7070b0b15c87adb90ab0486769a7adc5baaa7afbe4bbfbbcabf0b971b12458f896831739a4d053bce11bdc4406ee8122d2181f2bdf56353c09bf20de9cf329224ca1e7dc195f2e02c954936b02df5c296672a881c7cbb7314ab699708bc803ff8dab2bd27ab4cf48d1607b4accbc3465a186d5144b4a2ea6f09aa3c8312295adc9203750d710abcf3eec3161414d116458137b4e802cf8acf48c02cc79fca8de2c347d0f4930b1a874501d213c4f6915e08858ff6f404040b1cd60bcd495b44c1a9356705f6a895b2b7f4eaf4914738630400d86e68115bd9003963d7e73bda321f5f9568f6f01c15f23e8778c3e9d7cc291d1a654efaa51c82c78050ec5af6290ab263829dcd99e5cff8920120adccfb7581e051d0a8bcdd5938ee0746a8fca6e96fcf0c024d17ce1c9b045c636ade1935da46121d082a59644114978eb8d06f6d8ea410518b3a5d0fbc8766be7cf19e45f01c783c4b178bed13dde3b16789c98c780afdb125044505d00438ab5d532b9102e5de635cdf6586d1cf16643c09b71ba388158a73ed88610a8bb8e00c123cb009ea79831e8c6c9e27ab1a970d1d9e13511b4c8472d334c0563529f63e7b047a926c63296adcf028e0cd0bb6281aa4a6f0e3ac6f518e2b0dcf9cadeca01c50a1bd0ef175a0e5b56ae71c3928498596d19d0ec62c41470ffaf03df9cc195f1aad1b27e7836ac7bf1c054a441957af04174c662cd9b26e928ba5e2910ae4a5d0e0508bbfbb161ef1069444fc2e50c2a4b81624624f293ca142ccadb70fea72c39b9840468c163537c287890154f8a018c94afc04eda4c0492d90e113241090f24515c254850e8baf40eca04822a3c42292742864dcf1e9695f007c09a1987f7e15f048b8a1e7ee35beb8211a04dbb008e41e55c60a334746029494456d0f3c288993f29f1edd93cb6f0c7ea0ee4cc2216f47cb1348c7b42db525362785d10d88b6b6335385f1341719fc95d45decbbc4a8dd1bb565ac500354040c4bf9ea8244655bb70efe4ed4e7857f81fba1e7cb3563d930353f1dadc5498cd2ede28cb1abe06decab45c252c8448f401690469fc0a2335c91a1fafef283019a0bdeb336366ef97cedd9ba65065eebcc3080a6e39158d076b4045e61088e94ed3f584b631348d09fd1bdcba3c8619784e242039603e02c9a8bf5efc70aa8fc7400b5c957e5202936f73e565f4530415f94038c2187363d7be32121f6efc08a13c61e2178b3bf30eb1d2594bb07a167ded256910a506c410559c8be1f7fbb4ab3b79c69bc8463e39ed6da280b1b5477be9afb2e9c94ac604bbdc7b2cb3116daadfb784d1a21533ba4f4623935afe8337267959379c298588ae49d1c81352f65bb0815655cb85bb28455adf81b430d6eff22c80dc17ca5ad3a6ec57e699187725498022181f2ed861a7a737010ace939176e3a8e0a1871c6b913368c565d9cc0488396b5134e0b34d88601185fbeb605131d9919326c693fdae15cd6ec16002f688a4b59bb707716aa019b3a5259b43bb01487de7f688cd715541386eac208c9b48af459bc15145a7992ae90b77f00f16c5ef85cf10093e7a8e7dfe0bee2d5185431a4124b6ce1367956db4d22ad7d6d4b25e04aa471738365c4bfaac394ef3cacaac53a8e600784831beb76f3cafbdffa466440363603556eea4384b0f03e90577342514e1517106d04ea679e0a6469a8f7ea179edee9063f5abb10a0810920908d9a1a5a143ae61834085149733f0ac5d832d2dc47f0403e494a1588e89ca96ada9faa0b1ce8ab5cb5b3a613b6ab8e29edbb57c0068954cee3a84a085413b9018b61bed51db64de0503efb5fc4559ead42b53ebd11fe33aa98583b728951e71356bb64006d5dc442fb4e740afcbaa54013c6d15b97fe2688c0b7e95d6d55ad2c5fd576d37ecb229f3f315c4287af3333760c487b07dbc3304d1081f541ee125d8ab3b5b2b0099fd0f31ab1992fd618f1a118a3ff5bee9884dd901d12647aeecaccb0b5928f72726d20c0b1428a02095ee115a98495745d53efcae364e53d4db2ceae68203a1653213b3c4272b41fe679dffc33990a738d24a4f59cca1a579f1dbf40da659e4ac165a4aa95a44223d1057510235149a290f49cf61d6f10f634ccb7368fc500e39e4a5a1cb03e5ff1c05f731f9794b98476848ce94d6dfef475e75eb66d8a9629cde3f10d681c351969c64b129d2fd7f34650176767273e984145756b08b1f5b7189cbd1843c8e2f08d91b001b28c90204ba67fac1fc3e57c8309cf86aae391d1cb4546ff20d02ca7a72917f1d859cce75fa9f3587c709927b891fcd10552aaef40875249efd1ea9b5ee107b47070ec666ff843e9e4f768f54deff0c3d2e4efe8ead106f0623ad0ba78a8137a94bffefbb6fbc8ea73491f0c93c225297fa8c0be6cee1dc84ae1b24a129b6ab1b4836ec73b46fcf453baa786c5832dda23f3c9fad2cb47fb58b4651640136caa6a5863d121fd9367fbc079a78368878b6857cc83ebabad9f74a7363fb5ab74eb8b522ece28adaf5c39493c72c63d45f18fae4d664fcbaba092e9859d9faa494398c02a2652039d070ec315d2c1e4e0304789f45256f106d9ab7aea974420b4888f12d1f80cdc5d6ad24ad2d3cd031e624f3cfb44f671c48e9ee3cb488b538fd96796f3a83b4f5036326c96d9418a5ec8c98f9667e75f738da0da99ef2cbe07809403e66101def5987dabfef43595731ad6bb83282a9d306b62b5e2864e85a32bb108d2a549469b99f6d3414d63dda0c0f46afc3c90843b8afa74cad7ccdd26d06f5532558e0865e5c5a67264600e14a7294c413a75d780aa7a51e8c8718eb62febfef8f641499fd1a5db9682a7acba304fc2c74f21a3a4daa96827bd47b20fe6c846439ab7c881784fa5c8dde746d2c73829b4268fa6e4e5a8cc4a595571336d7cc659b03f36d45202acd85199181c3eb48d8edfaca2b2cb41082ed48fca7c4eb43f6688d619f6cd680fda1c182bb5554b42783f9be21b5d1a1ad07de3c3c2d38925f4ba4556458b2e3537c7a6dd0b94b579f71b1e8503c594c81e2dde500a14c561bc6147aee087be1cfcf905292744476c35d36624477087c2c112960dd21fba7f20b9b46820c34d0c83ae208a91bc0a96ba4340d73f35ad3e488abac47ef282d1021329172e9d22c405a8b71e62858bb18ac7ce17629a8d3a83567d8b6dbd45f0cfaa0fd81cf1f6434ed60da4f0a7130b46a9bd4c99fa6859265a64c1cd968761c3a80babb97df683a86147404ef1122c15f3437f138ede5d0f602ebac991e8785acb9a279059399f6e6050a363aedde20a83a361faa5ec47706d5f4917a775f6d525ec1dee9a04951db64f2acc8623a5e4e3e15a41d85175433746a2142270783af70e423cb9132bd6449cb3e5061f0cb40c58173db1366aa9434103d8a11e5ae4769223f2be2432fa1a83d55ac8c1f21dd2538a6f47eb6219f037a3289af1dd7c874bcc78d7c2754835309d3f5acf1eeee5aa5916cb6d9db48472af070d884f5e08f76ad22e94666bba7383a7ed7b136031ab1638739a958f9f0d06ab3b6513bc8d7218e0bf8ef4cef7ed32cc6b5eb74ddf8a6c2dbc8772ad8cf1ab61bc1361d6db28364a6845550f2085150437f11f010774d5191dee371b857a955ef5c00be1772186a0b27f16383f87e1f9edc3750ceda79c9135264f7e045f4d28f7a046b272ba12b887d03b771f3709e8e1c3a63756ec4028757e2371116090bc487e490e1b031c4c374315c2fc48478780218c23df9ad7a5042af967b69f8d8f211d29bdf45259d4065a9026d21e7012601e6ad92b0fa088b0b775eb2b065588bdb4b5896c0c9aacfe62c25d2176496f6519a4f55ffad94c20c4c477dd4f6959ed11c3be23972470908be52f9c0bdef44aff436f63fe8b44d8e7cf72941e406e9317318632ba48a37f78c04cd553c96fb71bc05e95b9de4d793086a6d1899d5169a0c82b918d01e4568db7b267d750566570240b4aa0d3c86e508f5548b74d26d50c2a4fdca4d3b93240cb1ea35e13b04040234b99bbbc32c88a3421d07ddf20b914cabb8373ae7ad109872ef36cf98fdef81e9461a6238ae470ae67a7214d719b52aa346b2b9e2533c7b02071dbfa1609bd05409b21c6bebd29039fa60b1d0b0365689461dd5a44dbae2e07a8c0992381de25fa7c29130118f0cb6c2c9fdb415e76fd687d0408fcececb3efc02a15a5861a9c5fcca397b50f2dd9654d1a06b8de5ad46faff15473b7323ab0a13b5b45edd16014346d8e5343ac5de9d781797e3a0a184f1fdc4c302845922927153524c31178f8a31c4da9938f36a311a66246cdf42294cafd338019d7b27bc332ea701907bbe8b140b07809e251a85c6a10d85caedfbc509162b56a72d2cab452754f346b15d2884f28add106c955a109398c3a04e510e23c25a6c704ab1d67970737cc2162afb339013815d4933854c8d63dab24a2db281314e093ab9ef19ad3428a9ebb035471d228b2c066c08741cb14eeef5f08a3f71e3c669b2cd3b9f21c81c0243bad9a0666fd502ccc9fe15dbe0057e31ea8ed68b94b92a6bf611ed8d5bb675c9929c56d13a63628f80c0c6023473590db25d315782a8a48f65423f597a63c759b0955910cbbba2315b32c85819f781c2eb747727b7d19cebcb34295b8083039acda3d27b60d30c467b3a2986531a82b549d285adc3e4379ed54b0fa598f1d983c73cc266720916c5cc63ff3dd05357e21b424347eb5a543e209fd88e022b3f2d8703212a28e7eb374e0553f1bc6f3ac74d5a0f8b4ef5b09c64a067648e9d41c3c941d4f9984ed8678ba335ca53fe0abba70c604a11b9a9cacda1decf57c4391968f4d25f9e60a0767f9f451850b85a84b07bb3f0105413092b29e2fdfb88e67197d4396542069e5fdd436c8c5b247aeaf91553fd27c652ab62c7d0f5169e0dc716e9df6fcbe60bb9d397f45f66f999cd5acfc59133b13c9a6cb412b3cfdbc05e80fe5efcc333416f249f912c0a6cc47e1f3901dcfd12c5778448a6016f5f723ae328f09d7f05da583e747c077b36f692d32f0082f0eb6cb50535783d51e116522ce395b145d98e31e2d59335d4e01fe9406f1076c40fe948cf19ec90c7d0a1fbd73409b61a8b693c420ebe0020ebb158e3872a99249f41b194a878531f7c2fbb6d27b7fc8c5a4e94368bca0aad10aa6ae2cf7227b5bc3e21f4676878ffa43eb2fc02511345cf0e8166391384da0bc3250d74d6865650ef76c243c83a2c2ddd3353c63b42ac0e2e2b60e9e367242dc404c6ee72ec8a12a62f5fb4298444b59cbcf89c2443fabc54ae244df255407ee7d348accb76b14374a632220727368559ae095489238f1d4c43a2c4750aabea39dc0ec39f5c815b27e4536f287b36633c712d44fb84a9ffda8464493501fba9250c7bad6387f31dc208bb210f76eb6a8eb263ab934805318f92c81ec5559f0f3716086bf03f48bef6566c4d02fe08b64a2e19c2027b793f2ee46ea2c8b26fe8f5324e70c05d5534ad8bca0e16fb3398ab2e7afa36cfa74d119e037b8b2b6ea3d83ef8883f62d7166d0b9ccde282ded084fbafa1bd6fe2f016e202fe7014b0a00fc79e2cedd494161b1b390d518d8595c4fb203efcb82ae2f418b45b921143cae471dec82de5b824c6c084f1aa9a507586d7631d4554dd8d1be54435926410aee4d241f5ae3aeb632592f8b33861c218a522e973014116a920ec023f05b05064eeb84fa284625f2b117b109494cdc94cb8c4dad37ed077963accaf3b628e4f71dc29ff6fc7af2d83852f9f6325017abfbc60ce8c50c9d8b90232add95015a5e52ad087c108a344c40344e9ec3fba0f6b3d13278316d4e84118bc0f26bf6bea296daf6fe279fc373a996bbd5b2ea50e2dbe5f747dac7d1e04be22068adda31636d8d054c586b9c98432f182126a78efe5a3dac46d3702410c6be5af56f5420bf6741e816335ac8d14049affe3dc2cc901d14740faa232eaa23d9a1f5b5abfa3c27a3ad5845607dcb17dd93deac0a8a8e4e72b9050a53e24a2968061015a35e6f33dad24b0f644b9a8c9e5e549dc647700b1b13476dacb8950b97257a41ea977d06afdf74839ba1469293368dd6753ecd15cf1ef637e03890df6d81ff01ba0c2872a22e427f0bd51f76df101d0fb8a978ed3df0866d0682c5ecb00b9c6931992b7aced9e4381b3003a98b8f69a91dcdf695ad7295e37dac463c3918a35c24ba2a044288e3742250c76d2e982c366546f9aecb8755717de309456fa4f7d47ed3e1d46d1ad4bab5b3c50ac0456b971c7366b01c5d9c6a9f09679aca185deb59586a154d9bb534a0b6c721ad686f314de6ba3606fbaaf59782ee2242ec3ab73a3aea4689b1de73c4b2fe7b5be10ca1474852cd552fa0aa5c356e24e3ed86ecd4725e081ec90862fa9203f3062eb99cbdbe8830e7848e8335fe6df1038973d870cf8803f5acc77baaa0b79485ccc73fae17887e3ec4fd3303685f90c8a0cf61cdee4de9d97a0350de0b3582459ec09abd9052d9e21640299d6d008f7e6e02e90bf90cca997c26b9eaed3fad57907313a3cfb97ee6b1dab6f856616b61f327c88ab6ed5bddac0b435629449bd7e103bbbf68f7fe9ab1d1c1fdcafe06713ef5c4a0c00e27e2612efa3f419bd5b714c5d0a79471b53ea1a6582105962c1b7296820ce7821226d558ab13a7b94c9c09e2662b3d0eee975a9d380e7ccd7de33096ad9a75d09d23b89a03b8c3e3f575299c6f180e16ed89bcc62714c00d17ce08b3b0f2c2b2296d2978bddd9a61491cd128c6bc91d8585636f2af6fa284d21d1eca4f71ad20c0a35900a5d6dd557ac079727d644ac4824ca1db64247bbbc67e328a2f45c876b86765ff1a15fff4c9c10c16bc23b62b0d81cba3e3e11df5aac1717fa8ef551297d6a2488458dd911c197bda3e7bf73afb943c493059dc2870b3506e8ca4da1873309357e84510a4950e456eb3bc46bc865359b9af47d24a03375c8dc0ebd01e576c08ae1f8b9ce5c6b0c7a3b9c83492ca173b54c5e958e7b1061d4cf2dc855818ecdb515c5041bafe811de76ff8153fdeacc4bd05158e111fa2500be4f3af74381fa0e13b9783120c1c58f68522fd38cc626a187b37e30c30222444aa810cc39d6dedec7cb805c900c41bbf164f999204765c2cc623775534177bd2d65decae9f886c36fd3695dfc8cdb9f89ddc9f6974d500980f645578800dbde7e2270619268fd3a64c9c6de2925e29351f780f7d46a4c71394ca7268d3602c24a1acc664afcc8af8e78c7818896bc1a25a5f1ea29acee1a1c1d557b9ba880c3745bffe9f50bd83e4a873518ed1e5366406b7a3cce4a86f99729b36ca5419d7bc73662dae25ddee613fea62ca65cfcceaec47f919df2ac0d4100b4a958d64860d8b2f2d018d72e52691ca4c7891ae44b37fcc6f2536bf3582f8c0b4b56dad97a1026d5b4320086fcd6487cbef14ba61a9192a99a5189987b81d9997ba6e742178a438048e15b6f755bc390b1c8ff7a86ed58e813c59f9ead4887438dc5031ab68b7f16b6444ceeaad22ca5bf58aa0ee60deafd3770ef8527fece159caea8fb59a9e2c26668b40d6e6a1c390b0400693835ec85ab6624a8e54e5eecffb124d74806f48e009ef804e2cc77b3c2ab07795cab75ddc284b05566cd37bd159ca6fd24d7009a1386ee1e0e72d17a9bda593f3421a83dcd0e82c4fb758aacbfed7a09e75073142f91f00d42974f2ca9a71c8a45d73abd70ce2f5c951358b788ef95b23fda906ad106b4294df44b5083801f8e884800f904e7b28f31e788e92c8ca3267280d7d397fa0743646c4267e10066e9d907085a0469ef3036ec3e78d7dde5ab6d325f1203aed490a4c3402233176065be3b7a780e9d09c36b5d7af9d1d78cbea6396fca53f1d6e0bc2895baf2994f9d0c36eb7781e2e08bf7b866d91f0c64be1cefca89bebbbde969bee35f6d5ddadf4f0e520d280bbd87e4581435d516d6c862a61d44611d4161e884ed4461701c8adbab5cd134aad71622bdeb3dbc9ad4d92f7478440a9b0bfdd7c43ac4c0b15bca88471a488f1590edf8883b446609173fd9086dbb7104b6f1a334ea1f92df256bb41144519e8cef4352780dae1f46d74e6972cc1df09e1cc948f730a9906ae0bd206ac398d946199c391d528de553281ee34a53b05d5be8fd4fb0ee14e516d09664d47819130d7832f159d7daba3151a7d7e11a09f78d2ba46dd8c461ddc480566a0ac5403c85f5502dd5b1a9342d20ae9fd056766404145b0463fd959468f10e542315b383340b8bd65bd8096a83dbf0dc7a98cac12b35ac921b3ca197621915be8b7ff768dffeb79d0411217a81dbb7d26c055c6a7cacde22a128eb516f22826a5fe5823800dde5317d55df2842c8c03b0b5e13b3280a37bc6f24e140d16c40cc7a2b04ecfb9b253f6adf3d884aca5f02a247e26b084c9e3d457520f0158d6939aa8c305d4426bcb73d266d4b385daebb42df2d48c29016ba3fa390d1285101293a0500630118e5e0a1d04576ba9af65265d53ebb841394fb5e76f292eeca6ea9cc1566d7410ed4c40c82dbdbc4076f5f8148818f969660df7d2c2d7332abbf98d35290a054d74cf243eafc5ca519e00f82a09bc786d01b9c4882fd24ba335b847dff3ef3ad7422be7ff909215b0b420cf84a779e13107c223d36120895b35416368a1bab5d452fa35a42180d75c6a47d50ae3626109a45d8ff8e412c4c49a31669f95134ae2b47f7afd5365f76f1c0efba30bdf001872ca41bca55f8f8ffbc778afc7e080a8c13bde0d8fd64fba23c63e36d8b2585c668151d8358703081293c07605488956c02b6b43cd0851d22f0b6c50c2ac1437e121c5758ec383f522b8469faf1eb93b32c9140e49abe90f362ab35317ee571df07e95dcf7f2d53771530a4d3d88782cbf4a2829cee498ee5634b4d8d36c4eee76528c79ad99086cc8eac369b60506a1aa26f0a588f709657d4eea10d08b06213bcd55d9089876c78c2182241e819bfba1555450a86abec05f26fcfc712bba3f4669824fc82ad4a0a30ed6925e4c908b7d4d103ed2a532034d563085a3c0a37b31f78deef7629d430cd6c5d383a2e87240136e20906f7cd5baba35314156c402dad8f19cc7844e0652b05e575d7e97752a90a2ec00cbeae36139261250627a9d99a140e6149534c6ad68e7b06f70e442d9997aaadf28ccfc9e42405979e1b6801357b3d160b8088ea1bac351ea8d0e2efc17bb6406573370e0017ddb233424148f5b65e790b83abe90ef101b5d56876fe3c1a8b8decc330baaeacececd2d65758023d9958e9d966eb456d707e40382aafefe8ca71dcecf4ea2ee088f9fe606dc96399ceb8cfbd7a701d703021611371a8eda79fafa8d4419c062667ee6578b4bc98f1143f5b97e53196ef4d389a23884bce6cc331f0feb32ff9150f94adb5a3a2bd0831e811c492963cfe41de6b1f080e2235a0b87b448acc93d46f4a98620333ac6edac2906a9e6a078c6cde340e4f15127cf53497d0154f388772f2491bd0a7a0587a7ab17d48ac2c10ef1130ba2107e046e8be30c2d7df219d254b9896867d3c76774787e26572a6050d6dd8ebfe8dc4c37e5b7ce2b31f01c317e784936f7097c85863e50d0562e71d4ad9671f9510cb2dee75852961c98f2fb3d9d8561f1d85e9b7adbcde8b30c967210b66dedb69b0bcb9e15c92aba76b85b64383c64f399a2bb3a5253cf871b53ebfbbccc597aeeec0c0c1144b2054b41245141cb243ca73d029b213b15d4a569a766462310f7fd78528e79995802902ceeae968b38a5f00440864d17ba3810cf365de00674a5e620b073596038adc27eb96901cda40c7314f63fd77587fa447249b1da6347c43a4a2b1e289037565c4a6c64fc6d0996701630423e65558395f210d79582227c16ee47b0a25a4bd3a7db3a272dd8a429132cc1c5bc4eec5c2bfbf01e06b744824c01db2041652c94b6b6f57353ee4ec01cde38b8b5db90e00b54269a70efabf482b73d365466bbb911a76cbfc0f50538087249f75009e6e194d4e439029c65f80820cca65bfdb0d3c543a62fa0d044e275001a206b6077c146e7e1c0f18944cc4addb1ee1146012094b2f3900cfb7693dc13a13176694c63310d7a035fd0433e266a12fb2ab3290178652b32b3f0ee4e032eff50b08ad1d59b1330f1cb744915f92edeb6cc39c335e77a66b21ebe4531145b31b46e1361147468fc3b44193bc570af9ce4ac09e320ee92b25da1285f935327c0c68e47615a08a5ae62db99ffe590b499ea0af9710c2f67c651c4a49b4934ed8c1fb9fb3a42da34d85bac2c09dac894e50d7c2b5d2646a6a54a5dca55926e0da11b6899dde9fa2658706316ada5ce3e4b85c61359d02165c78e334429a23f2de91ce3b4626009023205ceb1d50ae13eb055a2b182fede06252e605baa17f8cc0a26ca187189c017e0577e48cf509c1cf1a3924302070811bdf674ee67305f8f248024dd61a026a14c1249d533298220b9be36900780328e756d8c68a542b1f6290b78887c6b35064de967dbc126d7b690aceff137af2c59a411ae822c4a94a1a0181b29b629da52737128651c7e8ca528c83f0a04274505337b5e28b32cddddcb1a3634f8f6774464b7305d4dda73d50a1c035c70d8ff82b408624b9e8e1352432815eb206dc35962fe946b1128fb69622aca0231ab4e801516f64825a4d1f27a3a6ffa56750f589c24ba1834a4588b47f0261489c0918e7e32d68f990fc62b9db92706e195b5c6d9e10b6d61797c66ede1cee8c938a44f018b3cd031689f15914c83063fca831086fbc414e9d07bad8e2e83e8247075c7f5df0e090c44a0f8e54f06626275382d22d91f9e9088bea8761bb3971137e3bd0e5595f0f97dc01383bda30a7174a6c6011dcc36750178da7fa78e911f9556218a653acb99c1286ab1b060baebef0a1d108e8c022ccaee5c131af6cbf7f12c53c7e4f1fb4d84dcfb2f1f0b8f3478adee60c95899b8246ff66b1d106685e1f6f06fc1ca8b22afe280004dd20ca3284f9c88bff325a6334a0f8850732953896008683b41615c3fb32857feec858c69eafed4944d458608d5e469b55d431b3d7095c50447f5ea14823b2ea66afeb828dbc6a78d2e260eb8ce652a87ec5158334f13769affc29168ae4a55426a812c4819ae0e80499d399115b5c06bac525c6525eee3770df2b59ea647d2940bd88d3b098c7f299e7ad931099c3f8d14f730d1f43bcf0fe7f9d38df46e877651d57437702046c8354b0fb7b46537c0d30f1f77b913c69b03e2aa89b54165cacd8d9e45e1ef55f656339cf0b019088fc3ba73a0c8af31db85c46d25ee1ab245623a976a1cd3ae0a9d01fab040eb5d8e5995e8aa99d1887fd47a9e88ae6ba9af7dd796144111be04650389c89006f82fce479bea73e6e6f9fba115eb1e8f402f2ac1b51cd2faa47abf0205130e228777969a4fc0f01741bddc27f728d9af3b858ed8458838f0c64ecfbc5140f248529b5a3f1c0a052f2e328c585750361da0b8134ea5607a991ec88bda9770c46f8d6ae440e5c89a88e78e05b89e1756a109cde1ac59150ae150c52b5571a8ed759f1e6387fcac6bf92563667a885ed4085d8fa43d50fd524960ec1eea49b7a1c11a0f06c5b50c360f1daee1f078a74431f64c1ff3b444be21c4aca92d88315d936f3b06dc37ca89526cde13194445717b02998ce76fd62c3478dc8b0b38208614adcfef4509a43a12e52006889aaa5ee009328f2867a9ff459a1b4408eec256757f0de1878e1f62bcf546948186c2dfabfe1e001c9253503a2f62336112d6207a0de1c578e7f53586e42fb6c85a2320bb79db51fe4fc31e2282ecf5affba4fd8b6cb48d9ef8c7f4b72c87be045c4446b4ff3e718844a1abdef9bcac8a16cfe826952fe5aa6885040d2745688417203d0c8912b9ff762e8dea1e44c1729fc9294eb29f96fa9cd677c8a35b830f6c878efd80638196e1955440b4be7bac4154ec875e7c53f4d6b8b67599cdbb95056c59e8ccafe90244552c2ff6d6e65bc54f019c30b161ded69694e7a0fa22181d57a561474ed66d664d2db219f33536928323694e0f6a61c366d301699c3f6da6fe51cf6ad47eb54b71d41fd1c0a1d3997006e6aee54a3238e5bdb71b5659c7d0ee19fa08fe6ab554b2f57211ecdc78ac62ea7312153dfecb615af96843e6d3a0aa1165c19cf81c84fb4cb9ad78ba52c40be28c714199871232b31e4df42f3b532a207e36b234a46b169ae7da855f0cf71a766214cd0b83a669b354d021b3266a40d665166733d90a4205ca8e208b410d6de31a1d1388823f40a2c177f0c854f4914e3e430e2bd1e722336186a2162e41a15ce92f4185430e8d4181bd4267588b740ee47f6bd300795e206f5ac6cf0be58bc082d54913e300d1d19aaf5d3504c9d103d8d021d15379753a40e8fbdba5fecb8ee9310d21ddc55c5037ac15b3b3d913863086944695edc5f6256f50d3acd8f0060a4cf541ad2dfed806a75c45e4a2680d23b3dc55e0f9eab9f5753ec096736122c3b291a28aedd358544e0caa0b71132de2ef1402d7c0c47c4488805f309ac4b8d83bb28282ec75b1201bf091c0af5461a5028889004d0c03fa3bc5074c73c459ba8e7c7ff13bf9d4257cba0c5898676bd86991e156298df5239983e0e11b8ab85a2f50d6b3bd71744232654380d35bbb92e5d8cc6a479656f75e008457007d64ee1ee04d297f65a4cefd2f073e0e31fe22023769947d0e4e86735eac822f5447305624f292363e015d8cc46e852fc5568055abbb785bd1a0174ffee39da21dee2499abd4ab1f2ab7174136ecf209fc4ab6d0b06fdb4c8dee428ebcb52eb8f8466a830aee80a139c540b2ed01acb6309c78209483bbeea703e65afbe2159605b81ae63e815a95e6b3ef91e9a79990687b1016e382e188164307a698ef11869bcdca6378153a65a65792979cc959873d6b3de72296f8a63bb9a7e3b5aca34e072d257f63f00e72017523198634017283379629d0a296ff8fcb964edd00f32b69bdb00c6e80e3b874d91f2702c0e40b5766aae8f08c399a00295006b0ccca82961c0a7176b19088fcefe4d232cd33f288dadd9d6608901d0c04271c48ceae3f7c1e9993a62e1f8e8b4a7bb8427bacb5ec3c1f2bd7d19b42cdaab0bd4e47fa1ff907f8319d0086449d28facc9a1026028698ebaa94aff30e95fdc3d4c45c79e712b17ab3f2e954fdb8d09d2958d84eabc470e88452c8befed1d2083adc1fb086cf568ed9ad1bcfe253143f47cc1a59128040cb9bc6882e7493e563dd51326c1a064b244e56b81cab6171c55ae1e05fcdde460acb830de6104c7c855063a9cf43ee5835ba419455a8a1ee982d1dbfa0268ad956a49a4158e08d4685032e81de8c4559961610a44cf10eed1ca7f3687662c51481a3cb3b409036d9aca56092fe92aca74f8bc55131d35967c65ebc96c7a392cb0ac23256f0ab728c8552deca3c96e083ae0397360b84e11f9c41d81eeb4e43ecf689e133f890d2181c20d599f169c2e8f2e09f3631aa2106c9669c361162c8a81510c38b2986b69e5e2e9d1704d96316dac7c28b2aafb7ee685d89e8d7e176693acb3a57593c6c36d862c2a2a30f8c8ef17d09dadd06acf70191c6501999f444ea3b80bbad10e7ef3c26646142151779e955593cb8bc645b108bd4c93ddcf1e1f5ebf1dfe4f199a0ebac7e51291a7d6717644325816c802f57726bae2e461ce56ce830c7316d957fbf553433870c0fb14430cec2424364c411fcceefeaba45fdd4cdfc8fca710137e6ae4661e3e247c0f972ff7e8fc4762fadd8184bb95b313024ebd9de63ae2b3a0e2c1dd6fcba3080de64512d664c5cfcc93fa1c120ab70455eb276915978d954243933b0810af9f37493211a2449005771aba1f0b11050fe9b2d6624a144cbcc868e59fb8938afed914fb5a28e3d85b57b9e7199d32e63a6d57808c9762e651a8672fe4788a959669c0ad40af0419ceb4290941ba027fbd2043f99be8c581d17c4f7d6f65aa89b059fcbcfb978f315ed1c4146131cffabbcae41d10506cb4f56f4d2366999d690b36891866418e13a5d7151eb7c1d7bd4413e51edcdd180886bc6939aa37bde79ac8a53479de99a762754efe6ec680f9a6c9ee91a3054661c4a727e6386ee477f99e26c160f8d1cc0ff4183db7040a187d9ed0e77f3939a92dc4c7c78c9ed1bd12d8adf8eb9cc9d567806d4cf3f51f1ca742017850bca1de7bd2ccee060860c2c770cf0c0f50122e99c698e45c04c41fa264b12dd0b22e02f25f05ee978c175414dc5422785af196f7185e67fc897a45b64c12761f16ceb6e3909807ff171b8c3d91941ba1b608bd334033f83245a3ed408ba9de6f739f669120febf9503dc0b8edc5700547e292b4c04eaaaea4059325af13b2153712c8e60ca7defbab3950035aff022b095c550cffd08c503f501557f72630036b75112cf07d40c3d819986103057281e00f756672098f5c0a804a3c891d97257dbd8398bcabf9d82bb05dab00a5cc7fbe95fd5b48d5752955935ee9c32edb2966328b0a5444f9d80b6f94892de544374b8863ceb8ef3143b17eedccccda82d531e1fc6b41aa530713b8fe5ff826ee41367b468b0a21c4009f873be25f755367c958167fdc0a4a2347150377ea3b73c69a16b24cd4c8ef6302979e55e55c3a35cc0026121d805c155a68e6aa17fe5671a0750428e416b686ee7490793fe9a1ad74d8cb613893156543f942025ce448bd8866cee44155763191c4b24a25ae9879629b32d735bfe5f7f292346cb09238e11132d4488d145903e6d021c3b226b601fd944d12310e4318f70e0ba731e7752f0bb8239881382bd074664bee02009dfa7b3c49848ce071539693f71b256b49228b912bee6d9f6264a258ad89daded00863084c30de4d393bcb49d284954434da48fe8e9c02935b391f70b424caa4d002c7defce593d5b4e5b0ea1f44579c8a95c2acdf031e43c9f657a6e9e77e6e9e4b0d9568aa8262e076acf5ccd76e0f6f33cabb792807df3800743ee545be550a16ac2345be5deb92288632506f0e9feca75d98478fa0a2073265fb0b4900290cb991947f46edc158712751effd1a48721c8573b849dc6e3bfe6d8e6979c9046c3c3689eaa81ebf101c41c36f945f8719e3b737c006232f8d8c8e52d9a8f1d8f6066d81685cff8461ca6fcf4da700af61928f3a578013a0e05e6ae112ff243bf19adc734262b9ac5493efdc400cb1aa2ba53a2ff488b618066ffcded4311b1ca111ce0d23be27ee83c79c08896d62f81e2760d0cbad8652d00b47af7fedcfa0d64f09ec3f26adaf5f9243c474ff78bf78043f3e4f200c705d61809eebcae4dfb90ce6ab5439df02b5494d6925bc083c6cff0ee154566676bcc1439b9dacf630a1b618bbfeb93126928a035d8bc483dedf91d59afa90bd49a934880e8d529caffff6930a40925a82b0e7e7a3d70ccd2757685e4cfade8ab2c48f22dae327a1eb7ed683491f8d99b60720f38980e9518f0a6df6fc2b645910f1e0ae9f0eb4261e3da68a0dc5d65e9b1a2ae8de4c15fafd9ed221866728a2ee9cc4bcd9ef8a689306aad0339004727c8c09f245208611e6138b8ffab3abe60699fc6259cf4c48f83ee2699ff2f858602a72dada4ede293a55a497eb9ec9b25c1c322b46d5dfc34aa4f0b57a861e859d031b533c2d4cd921884aba562ef3a38b10543d8c88dca1d0898975d52bd1a56577739cb7daaf41a3c0f3289e12732f1d23e029f62ed4718ced2dbc4b6b7828e818a0c6944737c094d8f900a113eb9898ecca085298128bcd8b282ba8862eba6df9ec8cab1ebfb5a2028ae056c5cc102408d5496a7bf0e80bf70f9e0040b10642f51b30a75327a4ec897e48727f96e356d62f41fa58111810286ccf4e919583f8d3b7cef621bd6ebd74cadc8b000b01bb286021164a9bb6686af684031f9468ac3f8c661397ef327ad54a5c53e775d0036ca75b50c7da2d9f30fc00597993d81552cf03b34941664f48dedf350351d78c0383e3d645073b07e07c6dfb69ce645b66c67c06fa2c94d89fdf53d10ab304074c897c92348a9ab829d0a64be5d7cb2aabbdec4e64950dbb4f3014631339eb539edba4b4aea354e2424d7458eb1a962ca9140d5c404e38ba882e90ebfb56887bbc120fdbf4fa11a0c8f59c0311cb9a6743c3bb7c634007dd85fde58a15bbf7621a25472b49ae506bcb5b13b9f2287388af4e66ea6479885c04cbd429252f37dab80ae8941961872d62bb342615be6216b35b0f8bfbe9a26374b5c6c9843a88624d8cd1072a11092cd23dbcbc4732fa6b62bc8a29c0b998547a513c39ca282a7d92881ebe572318fd36ca53393d10013f4f718bafad0811fd1d55076c1a80e8584da1d492b0ef7ac4d47111b865a2d9139e0db78352ed5c58af8cd1973c74bb8bc2b3e4cc342bbcd07329e1b15ff1e3f7a3679c4ea1b5600eb1262cdee396c1881d93ed61a13a9b5d8461fe5c6919960d486d04e68e2a2243854b58a23e051d7c97c9bd42349825c136142620a64bdacd7532b1177e936dd7ca7cd01ac9ecbed38f38a6c0099d9bf6ef9658185e363828a021c11f8ecbd035054c25105206cd3523f7e85e1ed10365a0656485a68c81fa5571ae0ebfb1d5a1a2e06079f616470f4739486a0d810ed72034f38eb28879402e66cebac618225dbf1397775f48548f2e5c6a1bd7daf5c7c38c3915b01d3f1c333b052cfbe129557dab1fc555a0796c6fd3d4505e5aa2399e2cdb96a89755ae68d26ff94f7863a225a24686f88268b5628e976e7b3e5bd6535f99ab975196dca881382cabf245a98c210291839f04efbbf9fd514c3d52333e0a20536d160916f6096cb89d8d8f88ccd9dfd469f1e9c54d5f8a26127dc9441355605050bc062036ff358795a69d963630a456ccc8154409f89f7069f54a070256a984d60370d1e655e300745edc34f26bb5201e87ec5e13eeeaddebb88d606b6a51d9033a860599085c6d0a19ff01c442ee2492646dc5b093d98919de4fe59e4dbb47bd89ed30d39268b12b39eda6b87a3580ae57aadc70c46451f816ef0f297bd374f002300e637dc95ad13c781d407070413bc73b6ad97179f6ded2a19173e21c6caa283640302036fff5090a344beed50f5195e93b64d0ecae631dddbcd2f1e322c03e074c9667d30dd8383a1daf03502ca58be80abab6ec56c64b3fa788916d7160badabd94e80e882a3078050a4d4a02c7d2a3151baed265b35a0b1e16ecd24c837510a9f50ced50cb88948e651c5940f00d5d5e53e0e5f541133ef0f672747941364838012eaf7c31d4613e552fbbe5457d01fa2e04560874b8818704d42cf9825dbd829debc776979534fb7170bd949f975d0d535731fc0008fcd10082abb93523f62eec44cdde4d99bfce445275d5c06687d4279f1b1bc2c1159f8923d11428994f53249f20606f2848c3d91a6ce9373347e8ae558c21b45f726d4993cc01690c0aa04e008dcf3ad4e2c2163389d407da0569861488ac5f27ca268f38730e27b1552c30d48e78af983853b73f231ccec9b20f15af1e3ff58d3af78680793e20447a6e45790bfcbe9cab599e4db5f9a1d142825485528641402f260983a86c3eb207ff48c025d81a419f7d3b00635d104ac709d79c75822b006742dcda914a02db077124a83709e6514b36e6f13a5f6478c4381f28a15c3454acfedb6e30932e560bae883278055d45c19bd6474088816ece1c9bd7ae305f50f0e4746be6b125dcbe177a7d5b609107fe8798daab5595d448e6066c74b168d6c0dcc39dd8cfcc860cd8cf12e52015aa327f8048a2ce50ef41c95df6c133189d01b9d7776a382eddccf5e078fe24e407149f870ca257769365519e22fefa49e8a1d32246865095428e28fd028660158dd7280ea100fd89261c4db7dcf818a812e3a24bb4f6d6c094a69a52944684a87ad91b016d9264a763ff833106abb9831e94e9eafa1b45178cc73fa4b09261c3abb417aff5e6370e4e74dd683f8790eb0fa8f787a076281b41cfd4ca8c03079a748bab1ead7ec9db5023e461e223d08d71b1a5d665269b3cb2b104a51a42e9fe88898db846ae5174e721086eebcc8c80e380910115d543535d756dfc2b8b499041984af4c446f136f20334d005b3aa896223cf04b314ed7ed0a84574b711601bc8a2f665046c744d1bedb7db59a6290293eb4b2728a81f092a3abeaa51548db3a140e0d666af93bf37a3c06937ae19e40321aa51d0937246a3b44a7c97dd916ad4982a20817b845ab4e93a0cbdaad19aba2903a6b7a10084204971fe5cad1700c5caf88e30ac9a31278392ef6834b5c82009a398b1bbb553c62024616fc62c20468988b8c1e2755a17732b16a54e2169bc1bcd820dd4dc135123e4114050c617dd416b83081a11208f7f0cc8a99deabbe7e460aa96c188976194a29b783d0dfeb47444e7d541c4ebadf9fc6d295edb274d09d9483f20b76e7cc13ca51a50a7a7720b38393165c79c1021292a360cef0a3fe3265d89b8008751452a0006758fa61ece35f8c468701b75444300bf4221780d3da787fbfb6278ad6fd09d8a3122c03d99bf5105d350c9be32fcdb693ad6ece0ee8cb43a2428777a50f7f144c11a2d9901533119ac7de81b3e77bd5974fd0e480537e35a0e22535faebaad439eb71b2b8b9d6588cb448fb7a516d37aa3bac1459429aa22e509152c8bc2328064bad70a98ece2f85c76ec5d2d409546d193805f112a6621c72a0dfc2548212378764a24edde3a1a0fe354ed7f3b6756e5579f181206cffaf61e0994885528f794543396adb125e11eb30db50de6385e1cb6000c76ed5c395bcd76eb8f6f957fd1af83879a5084e6942d55d53244945249daf37d91ffa6f798e3b6d8233360cf35e36603117f040ac66ea2f346b6c161ec07f2eaa7cab24e49aa1fe870cdff3f826da1c5dcb088eddb266afc2582538a704496b62d90bfccd0955527158134cd86d603768fa6950ff328a88850712ab8685c350d1eef9b6ad4ac2bd183d7ecbc05bb6944a46b6384430736a65b279c1519031f47adebdbf439d7d87803a2c406b2ef744272ac46d42fe444f5fd9f95f28aa359385f782450c20e955df989f2d8c0f53de3c8dc33e88ecc4befd55bd99b4f1c944be1014aaf1786a54d2d0937569bf9101af6b2fe848367073ba1441be5242533343b7adfd87538105da0e28228905f9284237de07318eba50aefc557121fc49643595045e4528833f9d5dce091cb6959718ec4c3b350aac1a272a6f5f7ea5b6db0b53ebedfc41e4dcb4d2aca841e28e4211bd617cdf74736bc3454b6b47798e80c98df1eb1aca2af5d553962bc8905cfa5c576edf9e5fa42dbd5639867b7bd08dfbadbee3c2c4826bea23920fa81d50989ce9238104eed0c048c6a095bac48387a8a88ce1235f2b74988a0781da5d8b086427be3fe0f8a826c2f3b9e3ed1c6893a3335bab5b0f2a3dfc3520b38988ce324c8c4e42600972f18d26c4a6a12f45a0518a439826e60a09114d2404722acaba86f3fc9fccde76c8eaa09ace4f50418cee21b4f8d4ace7deac9ee7a397c9336e701ea54004c18705c7ea1cca774b95be5d1acd5676207f10c8a219d98b03861e5091a83881cae00ea4037bcf1db741c764c011a2067479c8dbdefb62a38a4887a98628ba1da33f4fbad61bbf49b1c73dd66e3b78678a3f51a3431747a5e38b6db8dbeb2dfbb9f7429fa7ec2e9ca5da87c12cebe6847a37e07fd7aea404383a7177f85e4247ccae35cb4e8dbbfe7c17daf18e4c6455ddfbdf6bcbd22dc05ba0d3dc00326d0ffd72c7c3a00903a24748abd632a648a529304b3cbbd3adc34eea54cd0e81e39d863f69c6053146b7fb64256bb31b6cb11da60eda2a2206c9b1d3ee62643daa654d743d41c4445f652631b037472022aa001555f40103a520749337a2ff74c9eb17c5fd538234b5d7f94b0aeab24a8194f51eb0b914295d6a0661834b85a30fb5282dac12026a46c81ce0077c299cb62cca54decfbc115a33efa5a89c860da1f697b9e45a2f69b07e511d94df6de52a694640a2c05fe043705d3a548fdffff7006334c30816a41959672ce7108342d42087265195c8910028d09593896fbf7301668ab9d8be511b3425705bdd0ff0fed38c1554e432bb5f5411fe4ac2ef46edb966d97c1c2732fe590cc02c73949509dda133a87b33ea8ebfa9182ba1c246efca01dd07e2f8abf64fca117fa3f09a9adffa0eb6e7f4bd7b4fee81e750db530d7836055d023008604a798850058145671dc78811cf76d0e677d4f685d5c0682c1aedbefe9b8b1a7a784c28dcb4cd3a30c0664a0059b057eabecbceff597c3cf4f952a8cb2e99c65c50602dff4c018a4e3851b56d85812c314214364c780af98e224091f3220aa62053b4f86a080e8c847902a349c7904edc8fc232fcc716bf031b44344e767884d2b05432263004ba2fa61f244143ad0600620a0244549b4c072b0e1c36b494d1180978e12ae08c1a8b2654e0413dae658e52073285ac827a90cd163d32dba60e3a20b369f3bf1893d94144c1f2170a88f0e3823707033d4812efe91d8cd4322a7062f12d00caeaf881bc8a4acc7e622ab1ccdc0975e0283adf402baf88110b358055d9437f88090cca6450904119b8f0592a31268b5296c3e174f8ac0812182899e17516043309a184216e3082747b030e6c3188eb1026ba266f3c9907142e665c8b08c8c1334b8a049cce68bc931c5e898d884237f6e301e9b4f0674b1451939b28d49069b8fbf19d2ca9478d07106cf90eed26ae1ce1703a92048cce633812e4a23a288c166d3a20c6283cd37430632e0f10074513a81a246037491067144cfc79fd835bc00064734c655139b17e58f1c7864f624458711f088281d45aaf0c85ce35703e492b42cd8b428b380b6f95e74c1668a2ed8c860f3221fe192c1cb864b9ced2ec749ce85d895626bf2dc5389071df9348a96c61896c06f1cc771bed768c92b9797296220b369d154021b17ba856f1817d8808418d12bede7a05f069ae104ffdd820e9c89fd7f4e4a39aeeb3cef0a89c4f37d2058faff77e9b8ffff7ffb55d03e51a7adf36dc88205c7712dfe398e724fffe9ff3f0ccc8b1730beed93649c2c15f97eb64d9cd384ea145857785b61e6093d671782b5eba8d7cdd925145ca4401bdd1e3d4a173c396128d3952bd6e7b5b54d9f63e67c744a01dd6839497a2aed9452be9ddd3b9ffdbc446fa860074a51fa90ae5b8591b9aaac02e00baaa95333b3da629902c250f6773bdf17b303496d71da8ff6a0e30a7f7fec412df7b747bb8f2bfced41d93aa5f2d9b6387b4cfb9c5de1dd4e3af670abc2b4d44af719504a291dd2dddd34d8092484e8dacb40428e70b1b3eb17915bd5b988dc2aea593ab640fbb6649f64bfee1320a78c0a9e3dc14b25913db0fba6fd1f7972d3722329013d56f8eb8d322a4caee3c49ae4fb627620a92d3bf61819013ef698d6ed1c7bf4f011ecd46a862a4729e4e38f4116ed0b10555a3ae79c7352cbb99d28b0e52cb52fb29da25bb72d8e45ce999dc1f620cccccf2477676667d18aef5f7e3584093bf5bf82a7563faa5b27b9d862e196c6cf798ed3aed0d76d277222d8bdcdf7f031aa822c655326206f1ec9b668749be443f7b32d6fb7c5020ca480bcdd6f6cb5f0baa4b1c7db1ed472ad1fa9e93a08829641b16bd15d5c25a9a06dd502c08c61a7ea25594bf2c6aeb3f91cb8fb689fa8ee7588b7ee62c7e3e7a4942bf1e0ba0e7c0f7c91db2d898769fc7bf14b3c3caca5ce1d76fd4e9c97177f77b79f14755aeaeeef3ee79cd4dffddd7ddabed3dffddddfdd8693649feb93b21be4e1de318d8fe0d675573f29e5e6a44fbb0761f76d3761606a3834278fc9145a9be738f739702caa933b35168d45b5d5dd07ed58645d9c008ddd19449f6406c00dee2f2c615247d61c088c9d72bc8953974ff0e6b64c93631e16f76aeebd3c9977d32c50c7ab51596d04615e0da67acc7b81b5182a4c85e95477e5efa1529d368a63d4aab6738b8f20f48673d861f306e3d76d392cbcf376e2a17e15137853f4a86b9e8775388b0a0911111515a53c968a5197c7fe69cc7178ddb62c9aee777fdc9677db5f77c85fde8b039ee542f9c3e49475308bf2aa3b57558f4f39b583dc964972d679e894568137156cbba559de4dabda0a09111115155199f3c8788ede8b36a1317726cda253a80c46563d99c77628b3bbbbbfe9beed2167f7d1aab6418afcc6c8e8b656dc5a05b96c63e4e8ae15f3c3b4aadf7dd6790cc9c7a816cb4cfff4f2c80c4565f14dd7533766c41c9da2717d5c75aaede98a7555e8aa192bee819626095351564a79830e999e2a65dcce59e1c949953606078333752f505606067c1aa24a7b12e2d4a44a7b0aa2049e4a50a53d91a04acba75895f634822aed4904a453083ad39d999d9854694f40c4004f20a8123cfd50e589a74a7b3af950a53d2da93366988e440712b343893f8e9d2aedbb8b6978ce69278c7f7b825569df7e8ceb930867a594524a29a594524af99cff4ff13f34d3c0b8df75524a2b6827f818d5026b0e84756069562864895245dcbde5b6dcc7f5b1baedba5ac3b0959a45b72d8e7b8ebb9552ca59d42c0ab375cdb43c463d3e2e95d12074466f688de604398ef8e2d22067791e0f0fcc59df9656b5f5c1132476d39323bb3acdc4e7f81df154695b1e8f6409e37ad7ba2606156a5509596acbf3783c26cdf21cc7a9a276ad6bc27730a6836ffff79e6982ddddf97fa6099e2b8963674ee7e114a9c65dfa0cce69a79d76064bcfb792bb3fb295737c87e1b9cee3ba1678f2c6b9a4bc55c0eb3620b9e9799e970a9708e5c81fc3ac87fa29013b81002c7bca752c1b9a22c8d6292afe0f56dd10e42c9fbd6b0aae79186641ff836aa1366016c9457a915c540724d71592abbbd9e1b6fd5793c0ee3367713ff8067ac5ed1bdeba5a1c90fb409c458f66a547b473750dbd75ea3bfb37f0ff6056aa5b5925ec8ff4c63492bb9d08a4b3e146da4adcfca6593e731ba433c4c4d6e4e67334e55def7216bdb9aa2dbd7d4675470e2a3966cef2a6b4aaad152c41424344b6a29bd111bdbd4b86030437c9ed426ff4c63a4830128fec9cdaca715f1c750d0cea01e91a0a05a77ae6aaa6419d6a2dac6a4b12fd0ac6d196ce3ca86bfcd6140aae711b70aa9dce5c0bf7e3320703b3e810bddda61c0db7ad9778d0a14eb5a5efe36320811bc25774372ec96d5bebe880eb5f52c1fa91935a5060de4819a00ffaad6b9a5b1db8f3fd0a667db6ad6b61562a0c39173af6fd69e5fd44209d0d69ec3e48b35ce640bc87754c3bbba9e504b90e13b735f1db8beb376771edb7dbae113ad576257396273423aa157946b7adb78573f9e8d5967053925ca4253e4681dfbfb74c91fa5f3339b9ea1a0ac8a1dba3b20dcbaa564f8c07d6cd6f8170061586235fae1281194808ee4d735bcccceccccceeeeee37d89d4170ff80aa386666073244aa7b75cccc0e64081bdd8b449259af505469e7cc7c0dd5409d666a3594a9531ce8d3dddd5d426a7ff25017ddfb8620a44a16df29eb1ed2804638030f98dd458b1930964db725c3fc029c31b7e5eeee73860919cc3142f7903946864c8c3bccac35653155aabc31556a8f8b49396ef2d0f08085a55199ffe58aeeeefe2f978a033233bf8852a5fd92bb7f2770520aa23e926703ec263755eedc29ae9269dc1d8493900ffa03366aa4c2b80c54e4ca6d851c14f5c565a02244dc16cb389d5aba0c54e45b02ba4650c244052428a2e449141d44143a3050b103262c883d15b513d8fbf95cd71d5152848d072d4e847c04e004162388f8c08b49139b2943cce0460054a4c80744e4a8080dd72f03150112d6c0fbddfd03feffeeefef333566e6999a64d7e7de726f7e99596ea4a8a44658515999422a21ca52b16047a583a954eaf30098a6ed000660c36c369bcdc250cec974b28efc117674ca1d65ed8d54cae74b5a08ca3b994e3a3d8927bbe69cfe9ccf4c9db9d9dddd3d6666a64382823a45e47bff4fc16ac75c9fd3ff1f94ff11b8ffa4fa3333f37b833876fce9e49ea02f38e64eba0ce6628689c9f1b4e0ba8fc402fcc09fb0cb5f4b964f3db8944a3cd8babb3f8f0bc5b9a71eaab4b3e4ee140c71ecb068e102e60507c1c0b1e313c78ebfff0c13a71354699f99993d4646cb788c34b1e02798cbb89f9038e184b4d5f07ddc124bf763979225d317579cd759f024555af7efb6925d46858fc0ed96ed62fbdfeeccdd4fcafd7f77b7e4cc952bcc5c6b8c18210768133464769eb9c2b6fd9464e68a7c66a6cfccccedcc5c7960409917304c251e0f83c3072ea4b5414a394aa9c3ee0c1931301cd6b00eb90ce8eea285f3d8d08662eb41165bf37a0b16b4bbdc6df5b84fe79c5f8018f7c7a0cbf25a1c3e5489c3076646fd77aa06337f5f1751351e46cf80364a3c3886bb5b8bc38769a5e83e7de6ca094b9533573a1040b8718326090d8d291544dad067ca5523b4aaed8e98549154cc7577af6296e5e727fc6933d9c6483bf1a3e4c7090b2544d044254458415332e77cda046135189a92171ea42949e2e727c8121a8d46a3d13e4aed7eba2a684baca04999732e9956d09ec88fcf8f15342b6856d0989052fed4a494424a29965002c8ca4f15dacc0454a494b424a620718414991145b8f7db8086fcf780068d94cc5d756aa8bae9825dea46b34ca14cd73534fed988619dd2714fc7e3f160ffddbafd7fbaff9cf461b43b8f7e5ec9a5e4c2a2850b98173068705dd766620f755007ddb61f0d295bb6d33daefae91e6bc5b09b74ac63e3090693e6691ed48ba2d93a1faa86a753a3460d0fe6cd3c2bde0cc499e1ba69a341e3f4ab3d06e3becc8c54694571044f39f5046241d968e1c18e6eaefab97970eb1ad8672424444434c354da69d729c5a2c8663399646aa8b869231a1a9ab619a3527d000208373e90522828282605c2b43de7c1da26adb44dcee6f4695bdbdad6b6b6dd9e2db1c4c828c99a077b81d5c01a0d4d890788a553cdeceeee6050a71aac85f002bb01d63a2c9d504724a748d9b485b7eb5152bf1bd346c3859247c2ba9ecec63a7c9af5a2d3aab642424444454546602d9ce30bac27466947c6240d1802c876dac01a0dbe9b08e06a7a3307875987f4212f0e100093276fe028005c07d69cd58dd3d65ddb023ad5f601a6d24e0324d3cc1600009e72ea08d336b90eac75a5182a014e7881bdd45e749af519c1c01a0cacc1c01a0cacc1c01aecbbc914b50dac8135104bb33c1d0f76000f96c208239478ec70932361729d4f37a5b3326dd3366db6ae67c9b4b9ebb46abaef88f94820a1c483ba33333309b3663e751bfe84d3e62aa34eb5a5e23cead496120a34ee1ce7cda74a19a59d54aa86d530b5b27248b0cb51027b3a1e6cda6651b3402c60edc11a97505d03b0a15d210897e5eda8e872ed3ab903212128b676562410926258e4e835a6e923ef71993bb92d196cbdf655b689dd16e7f28e9090629d024f7ededea4ca64db75b2653299ccbddafbb0f058763bec2b2c16bb75cd4fa366793e68cc6134e6301a73188d398cc61c46630ef35c34e6c5e61135ffffff3376db7a55b89e3f5a125161222a4127b8c1e2b28ccb56dad52d25a94f3881a5892b4cd4965002c8ca4f15daac8a09a82431058923a4c88c2882089f2854a03ce9713284104d82b0d6723c50aef3487408275f7ccf839052cad907aa54931b4216a49a295563004eddd4a4a0805aed5a2b8514b5da74590370cd89536d39556518b68a01cd9e06bceb27057cbf3f1d8774a238fdf0e42cb18742a345a145a1d128f7f3f37393cd59141a117fff2834ff28341f9969b44829ff7b58a5dc062925e55ac0b839fd273d22a3c9683f3f3f61952ab39f306c420394175854283a9eec3a124fa7643428495481a23384d28732c3cfcfcf10284368341a8d46aba1c2d5099dfdb1de4afcb9e94fabd070b8215ed0bb8ca6027f7f3a87bccb683ee04b75297d24d2101acf4ad9cdb8b2febcfcb894b2803f5307194d4693d14810fb1b94523a9fd2d51d810842c0040810fcc0b383f201b504a583ea01a504c58394f2070543254121411d41e5a08ca08aa088a0705043503ba07440dd482969a81c50425038a05ea81b5036a06a40055935177b71dd07ec398b94253624258bcb2b26737989d2e6932419da3c09e19678e86e25587793401fa8522e6ad219e67147c577b79779b797b57551bb21cd1866f6721c87ad9c653d972d69e6ac1448331e2908d516498824d4352d1214b7ad0e9efd51d7d51a8636bca49b020a66a4a17fa1c695b107541554b981133749630de5bcea759c294475aae52ce5c92efa1825fa7fd05c0205f74562d186ab548a8a29932fefe602d774ca76c86e0e992391d6674b29a7bb3580745d1f9fc72d1f8d8ba2cce26adc72e8345ca552d7c71c4aaaace5f031aad5e2b6ba1e5cae23b1b82de5c9ab050fa59b6c012bddf8e78c2b5bdc94cc30adaab3de654b1942182f6ee72c1fc330b401545b3b4cb8ad832510753ab92dd59c71d286e18ccd2121aab455f6922d7cc638f6e20949caf77291a47c2f8f8145e556ff226ba894359de56ebab86c6142166cc20c2245e84e24f19074a4e42409365f9435e78bda9c0ee726883461e290830d2fa669fb721bbc3077f28587927ee02ce9a8eae4c0e60e462edb49844d98375284e671d6c41927ce8bcf8b4fd7b45ea2bc44f1483fdc261dddf6665367c266cebc91264c22d3c8dc61e24c9c17d9ffe0c2e5f67b80bc93db922eb57983e315c19e8f14a14b543c69b9d46e976e321a4939ea59b9edcdbc293ed2044f0a124cd3b6e51571fbc513990bd70de0fd1567679615539e364bec87da8897d524bde68bf4f2644ce3b2c37c59cd66b3d56c367bad56ab5468d4ac06b2e5b6cb8656c2ef1792a1dbb66bac837e93db82a1e1d86e7b4336846acbabdd2b3e726ecb1bea9a96b7c45dc20507da6d6fc8e5e50575aaa987c5ab79435e6d88a8591e91373434b4828f51adc9ac63257b266a9652eff9ee0e93929ad457ab550d278fcfd86c327ba6cf944d188eafb18ecfb2747d5e961993de1089eb2c479a719934f2c8755c9533e62a9f29eb548b73868208d7c7d94354db96ec2cd2f29aa0b6ac458142f57f2585cc2065a8d2d2dc90ac43ca4f8a7ca5942820555ad48f2a2da362a8d2a23e80f28182a14a8bba280fa05e40b9aed8c1c98a2aed962aed4907555ace418c531555da18a7a32aed494b9516e684832aede90655da935195f66483530daab4272aaab4a7db890655dad30c664e32a8d276ab535195f6344595f614832aed090655da93edf4822aedc90555da530b70ac70acb82316e080e6f044880c95e1c80f1406f52f031d5982834aba0c74c4487704871962908a8ec40083fa721928470b079413831a6064158c82e44305af783e6abd0c947384e753635c06ca01a2ab22078937430e0ddc4e05afe8a4a8df6520234631232f9012c888100da49cdaa24ac7a4bab80c64c4c99c551997818c209937a8dd6520233b501f8c78e0a6966ee025514b0198dca8140d9aaca964330300010563160000180c08064442a15818459a9a730714801061823c6a563a1d08035112032908a22088610801c6004094310621858ce606000017c8c4065e76bfb25c3669bdc369148d411174584935211f9e0b17d910d4ab6bf9d078d10a61fa1b236ad794246a688a93890b6d3c505daf422192ff410deb3c2b370667e6708975744ff4fac4985edf5ae516212844d229c5b1f264926c40a11235d8d089ca45cac51040a626d22edf056fb1e588afb7d534fdb52baa1f5c8271d8233031bb5351be1e80b11f1a1a8a2dfaccc18fe01c9b24c4d5dcf52e6f84d9bcd632a405a9e4c360b00fc9541e21f77a9930c8ccec0f28ca137b0578dd7a123a15f433c3a849e1207517e3299443ac412f0da7bfa430f6c2af6ac6d4c64174b1eb24cbe6133c42c4adf2f429fd8cac74ca9e3ed247fa1a987304f58578af811b9e92c6df755fa6a4a7b1789c40d84a9096657987b477c45fe77f8a192f602524df59098a814ee63bde996946134850821fca4ce20125e6252de2015f040f9149597bbcccdf631849fa7c71a29e1aa308c868ae77bb23f56ea854c2e2a106532fb6efab781279a2cb49658b2b3c74d2c5f0ae70127ffa1b3af5acb026993826ce6d67b125d6ebdb93a4730a486459c8839bf8b233d54ccc306ad2ca08e2e08a89fba22434649d2bf54625bcab15960d304515ca1bd2125f8a4a06922a93ae01b5be8e39a3a34dea3ec9295f99f9e08ab548afe71139a9c40df75a28f10446285c1e8540801bd02f9b27b1387d3f07a99f41e8bf7c60b6026713bd130835e29348be757457c67f0bad08d9c5e2b1f9fc96f1bae61f31847dee2e67ab44be24dd8ada343c2017e199a97a322958a430919877f7e23a375638905025d5ba82518f5abb0312ba7cef86ad91af5984a1fe2b2eb10943136956fdd8816549c5923ba0e16c25d4fbc12ac22ed5bb2ea53096ae09817414865d49a810c9d7af8411a0b944e28bcf1a17eb2fc0b7defc34237a36c328cd276c29882af902820d68a4f08da5998953d06bdb4729f8f7984c5770bd845ce173a6f1d2b9f5c4ec9d053430514e73579e6fb621ccfdeb751870104db35a24897ae90be786cf4392bb0cb758d4720f87c94543d4d7daf75b9c5c8bf0e29910dcd489b7c03587037a1aad8321fe87e00ed6e05826ef550870e33f08b8ad4340287ef59335112a52a6d61fec3c60946e6f27c4f2070b8f355b83ec802dbad37b47099a5478f38fa7a0411b83da07bbc616048c4be200649db55cc5167ff1561060ee84345abb1734760e536580e8ad3303bfc893204eb9aefaee97468f19c2d99a026a153bd4225ae6edfae28040054e0a53fb46bed9a1ddf447842d22582d981abcd7e4617c3016f55a42e405140fdfcd6cf08c38b8b70c45201d4f6e912b3d41ec25773514aed5687643697a91e09308821be997e51ce0f8cead99f8fa73876be945ae4a2fa2df5bb2872433eda6ac480f77bed2bfcfe95c2aea551075fd9909ee4eec2e727527178c2e2721423008c69c94071b5012968e8a70ae91dcc5cf808a859dc881133f66d62559891904a88b76a10067e989cd9131365e873de257a92bbf1875802670c1aba0ef30ccec53df674633b2e77e658a0cb6cf58e4b41f8031d6aa73f5b9750f18ef2870a42fc6b99e5874a2f756d72404bd9b5f9a0a08dbfe9640f0305e6a0dd52042e7e83293bdaf94387eb3dc429ee6a9685b800fa40aa0743c037fc1807a2221f093b408838c7e8474ebee063eca1393d09b44ef41338e4cd5d77eb7e3d747990ad17387e22b1b639581d7ce3f298acccddb4ed1d017c40958132f590b62fe24edea512d5c0229e0ff511d61d98973b93b5ce5de3d349e913e2c9b7991c778e02cf90dada22177600740430567132cba3acf058d1ec9ae1e891697a98a52b198cf5d9e7484f300351c11a94307affa179d4dc53aa7bacdc0635b3259516f18c8a2af6a9759a258f052f333a51b8484276d366a3a8bc6b11118cbd83c8b18c1457f1135488e9d8081dc2dc7041561351fff13f33db70f48cb06ac94ce336bc16c69f43b068b8cf2695569d3c406d38b266a12b9adeba2038b8377a9515cd5d172440f45c702f48ec7597ee053997f7ee9067c8f97c138ecc27880406e99e09d0fd13746b80b8196fe3467e4a66d2891c29b0c98bf86d4e468d00056f46e3c2aea56e5ae03fd725adf8383d97c25af85e367527cffbc43f13bbecff80c45ab06e901576e3023d15664851f91e7eab39a922e179813739681a63828172786c50a6b68d72fd2f6e893842e33c5bdd741d375c56569a964d5b2e79fe4e8b498c3c3360ee762a6d83411926514bb56a043141156ea835087260bce2fe32e6185ae7903158666ea60be19b5e467015c2df89fcaecf22d8e2c31748175a5c592d89e1467f17e7d0bc97e29445491e38c470f8b2b2a5da1098a0d86af5095c4d4842aeeddc10069dd0bd100a7303f4a753c121fe730323348eba71a36df1fca91dc2a382f18da920ab58783f19ebb94124aabb0d01c9c14666c8cc6cd64103e16a1172971f97ffc5039455d8c24d0e41e509bf210d98312b9ecaed49bcdac2c96ec7c045635ec94e48962ea77e6452d58ec94130a8b262cc561112e40840d59a7b8c217a2ae703eef5ed5b095d1d864f3e6ced840a9dc0e706a43a68219d280101a3b13cdcf513879e21759f7fd638e0a9e40ea8a0067c5fdb1bc12d5d80b0aba32f2889b46dcde876030057c2f74140c99f4f44407433b656e90a8f41ef503d618cce2e719d521ef18f19c6a630fca391ed5613e200c7eebe9308013995211900934793282447845d722ddc3d43d904be7348351ae82fa7607e6a1dbb0a8f8f8788a6065715c2073fa5b88d0a74bb64a408abd5666480c37b2d569048fc0e9d317cf901987a11a225868536414f545b25ad3a0483721a38972565ff3cb949b50a3abc0729a3e5a0f8553ac890baa5341b7abe5561c5e971c3b52e24c14824a429378963f9596e728f50edac409e4c13733776b69e454b8f6e6d63af7a54c72d177f4a5f6a658e6ed53599a3c0bb825bbd0bb334f76824c98c708205e3a27a4d16e0078605e4d84d7cca8fa387ac3ee7e70e9b372dd83f37805109d5b0e205fe8eca0dca262f27b385a55a7b56bfa2db2503128c92a5409be581e2ec2617819482ae287789acf904b45a86500456a593d160cc7cd78bfc007580f2365c511b5f9cc03cb5e4e0fd20ddb63eb4ca870dd85e36dd518d1ae04a82c6b1884e140502b9d7faaaafeeae975e7d2a5599e8404ee50394bc3353a63d54e90168f1825b0bf73024be813b8acf82ae16734b47f467996b8ccd668149fb4ef6a89d4ecda43a0b0c80dba6ae6a3a3b8a5e9a7ad7c9f16ce27d2462297b32e997a7df02e623974c49c0394785b353857d9f52d0b7dacce4938bdd9fa72675d562de15dc2849800402844b9033105b5d7156d297642554e8b62ce010c16354950fce03b9c092d8936758be08259cc66a943649e29f381627d51494dd9aa6b00e9727a6344a309b89bfd5f806daa366e6eab1dd7883105b26a09208b3f75358aba21e06b2fd11753f86cc88154c2889d090038ba7da78261924181283f5cc190041971b9de32119d9750e11a7bf861cedc77421c98d641948dadc2fee774b6f6ce040a167ca5b842927a91137ef28cb847ec8f5b2e9882ebf87e5f05e04e80b0c20ac087447c3cfe67c5da2b48fba987d29e19051ab7b6b4379bb0713a11cafb5433ebd24bcbb730ae4c2aed09caeae15206fc90853c3820825e1a2eff2de859a11aea95ecc3bbc461d3d11f375ce262e5f8407c6fe1c0985d746d5d55bc7eb7dd0f2e1702beef0da07d6165d149e30572509e6385f5c6a86b4939ba37f64c2d8e09766b0b61f77e757f600896afe4ca53016d34dc418ac46795250a0819a09200cb8bc7d56663ed2d1fac0d351ff9258d6baecee52571442d2254cadae8e9e8a94c4a431a4fbb684ed5148535299a51877d5aba8a15d296c8d63990313c65235cfe1b8340d7ca551db90a42d956e914bb767df20882c42056c69261008f2bf5e586d0c766ee81427780823ca34e2f2d06b66552512240083e2190c08d8a58fd6fda6fe0ead5141a5b6e15e99710f430960a4fb0b5c0c16f2d1458173565d50a0639f3b0098d70b95436487e0dcb9b29a8224498aa2f5b740b0d2343663cf21b8a86ac8f24668fd88f1dba73613771af2d1ae1291602bd2b084bb19035cd8e0c2c5b688635c7871ba1c3a8ee50333a1eabb5a5e1d6c570bce5b61870e1ccd705ab314cad464928cc76c11cf334f89800239b63fa1f44f72cb7968503ebf3e38b45ffb7b38868e2c2bded7fd20f4bcb401d62c73d189292767c0c93f6296caf08d166486035fea98b54af7c9229d1f03be4165c33b23e2e021be9354c9bd1a2c2d7392524d02120fa4dfea24a6e1d6a87f2ec0d3fbf26b31ee40cea15a0f1a53b986bec78c5977e1c73881b9aea24a239dc5952fd605fcb850ff7130b57b813ff98140835455ebc0a586b33470a03e6deaa98304043ac4c93a5ed55ed7efa24d6a7fb43199ef8ae2555d966a7f33e6c915b0394c07916ee042c542d712283ca4ccb6374bde0aaff924e8d26b25ddf4fa5be9affe9ad4dd31b8989e4e831986669ac2fe78aa0e0e350877f270d5b5cdaa06b20edca9a7fe838b2109e7913035969a1bf0805ca8108ac288b6025b004555f9319506858df50d99d20e68f2d5412f98e21032a97a7bde9f65998a52922e1ea29e43f0d93d0fc290b143d061eb7e91d94f6e718d84ed143ce7cf4a4aaaa6c7b0efb8465cc49acef252997d60ee71409b853c14b5599074e1dff7580b42693b55a250246dc4b655e6d298907a2dea43dbdaef96d7b5a96a0628a8270ec05f93c2b92bb078d9ae8504dfaa0b5f10ea9f80fcb1977d1edfd7aafae57f813ad0dc6c522dd91a86858fded3167fca20ce2149d71509b92f783812752d9a3f4885a00461fec228480c00eed6574e68be2ce5e78dfbefdd39be7c3017ae767623bc2812d0c31fe9d3642f87a3ce995ac42dc8e71f346480e9b07b6064ec5a496d5dc7fa5cbf610b52aea90463a3e61bce091da162c4d337c9faf9d4148ae0a3f3fee7e8c7c4c37cba0f8b167a227258c40ba18b07302505fa3234e4afc7f23c58fd0073f1ed47308ed6ef60276255541e58f04070f1b189d2cf7f1f64f1e07a0c9f9ffada3df5111e1115066fb94aca842cb6a541b9b1e8b123c9063668ec4c3698457eae1d908f7240d9ed6ae055d27ff2db88ade33a3570683ab9b43e6fa3c75389ae2b4c5e2bc2335c06059cc622ce7a25cab732c25c7d87394ce6f7b470bf8403f832ab29b8d26a5f3cac74910ba139eb85420787f9071d9c0c192634ac00bf42cfc370c46344d703a8a50ba627fac6f630bd01e0656dfe12a539d629cff1289d94676d64d205a4c23f1258c917b5fad6b82c590574202e5a34e7e7b20e9add1affcca9ac2933ae01b41583f272f1d60cc536d7fa7387e0927a01459234b132d4f57bf768b554d0c84fcea0a42de0e59a8ca60a80ca67a60aa8378aafda6baf5636d3ba0eae190d8052bdf7175e9f0860617f0d5bd19314b2d8bfff58186612fad68d877810ce3b0cb3d6821130e06bf854cfa580eece7e35fd1786fe4fb806e74247b528f60b782624cca8abd816913d9a8edec2e94f7f04e2b20ced64616ea4476898864b08bec6ca0332894aa60e82fc5ae5ca2544db1721d7800df13d158d5a36c37b9c9430b43a2715b2ba1ddfba9d5d96f1ce8f14f108dc99aa16efa70c3d3e942ca57c985a11919178d0e8b77cd546701bbfeef5fc97a0870a6c9038d3054ca03cb8c4f120258d2d23874978d4c72b23adb700cc4d83c23c4a71f30254e99fc5b6099f26b2b81f7d8e445fcdad8497be052e31febb4f619ca5bfc439c240056177478e943e178163da703052b739d3b0f462bd2731e036c254000446bb472735e70849490e49ce500a3bfa7a6f42d4b0f5d9c73e5cf2d0630afb1cbe4a16f29b9413244557d7d32681e844d6c2412c8da46892211847f3ccd8e95455d572826cbd03058db724b01bf5efbb538ca54df4d0c772a96ff60873781bab0e5497d5fc9b94a4c5b83c5af232121fa244025318d0660f044d544d6b34b62dcd8f99dd9c00e373fe7194b1b9ab630b64c4c93ba98307135070bc3dfb3855009752a890f0074301370ee28b14df94badb5356ac68081615c2bc62c82ad3cb327b372e081d9ee649e51565c3e3755e44acd6ed21a3ca262c3f6e7e7c8d89d88b7c09b060d485f0f67db551388ff0eb05e400306f1982d935731207aa69d30a3ab41603398685f6503f6703b69858a6c280ea12686a6e1e76fbe0c20b1254724b5a2273e04e2c6ff286ab5f54c91717c334cd46e70caf09f07da6730b2e9af6ced3503d791a5800d1dc5244a148d10dce761efe6a3e74356026a288b39df6dd24062623d0ec1aaba113f926cc565c676d036e64fe78170f4a7e2719cd499d0301f8570620036d2fadde038c3db288b39ba6639cb5508d99c54fb8eb07847cb288d5bf961c74db4d00dd6b2555154591f1cb833bdc5473104a062bc519d418dc993b5adea51a619482d6eb1c6d73b63fa13a5a3e8a71b4c73194812d9e8ec00ea33e439b806e85821446dec4949bc918b68a1ca18eecfc0cc23519ec40c62ae12dcbb43b7c877359d93f4c426c28fa20bdfeccc239d04e2d35eb12e1f8bd9829d6b5548b1c8cd96293e9bc7cbc021cb8c14b375c5b116c3c55d87c45209a34b28d868461d0943b75a54373d883c6883b6dc7a7a191ec7323eb2df74eb2dbe42f7cb15e681f770aa49a99779b72b735703d9a9a7a169b9f75972e4dedca7cde43a335acfb03ff4e1827a14d2b2055a5b19ca5b3ae82a7734971cc061690c2433cacbcfd399b4da88598ac8d0cb6242917d5e0a08192960ee680e758c6858f6cce1a1a49931c6c25d4c134454cf81354c11fcce791ff796b0fa894b4ed8b37af957161a87f3f033ad139f357ccecbd900b44f122d467cc9f0b023c0108957f53b020c47ec29c9a2ea820d010f3fe49b406a6a05f4b21d6fa6148d509b9c0419f31a31b745cc8f3d7e6ac2d7eb72dd2c2a7891835a153750cdca9ec9862c0a0aaabdef017d44460302c2aad21a97e02d6e7c797954163143c7af60f52ad8a4fd53e5092524fa8901403056d3014baa8d3e2ea4e5c79b21d7c6913287fe6b865f2690de1c9fbcec7b8c4abf812986ef79c2303fd2246120770098dd8f0fedea5a1af9c313749e6ea5dfdf050d314af5458f445b552c3d0082f06de2d6ff424e5062795f29dd0b022f095e74e6a3deacf743f77a2441936ecaef49d5c47b0075cff4401e4ee9a8847367d2151528dcf2824327c445dd9edd9e84f7e664aa260800ffd3f9fa014e1759d504702321c980ee5c36b3d9260e0594684f0e1fbfa04588f66d4529d1d9df170a3044d91ff5141001b67cdc6931b9de5a5a889d51e4ae094d17efbaf32f4f4910b86e0536fea8f430a75d6922d7a260134b69cc30f88b2564b99a8c532c835f6d32b2ca6ac8009161fa3623690d44e185f40a98e719191905136aad91ca296acca2ca08bf1ae414e14751f2b5861d6b2ac5eed27848f8c8e095cba2795cf67cc635e02e8c05e3a4e965c1b54e3917558a4fef5347de57888860d345fdf90e856918b3860df44fcae3fa093e7f0bee6bf1bf2ac757d6b58eefde46162b815d54b09a81a9e82e84e0507a5409434f4c2013868b2ef044c24fbc084fe197a40779deb54b3a8d536df5de6c5d93db88e68547890c3f90555987329d176a5fe0d9410c9341a5a6e0f1e7f79c7ee0fa23f6c229bb10289b3804d124328f7f75b0084b7dba2ce7668c61cb4d21e38bc69c64e0ea869749f8202db8824fd20238f6c507830335c9910d7d9a1cd65c6b92dcf717987f30b430c9b86dfd35ed56b2e92fde4b200a18e1bd4766d30a9d029af66da6f061253b0f770706299e0c5a1eb94fc87cad3d41b7d0ebefd9afc5fcf424e6a933314e044b1641e2e22e2a8902f808af2c2a87203e8d41ca218b8c81401852532817a80c4655c40c3e129e40d4e2d977dba52597f4e2b49d3a9ca7d7b163e0869fb8cd18099d55f2f73f76af04a6f529b7d6d95a1a442d52c8e7c0d650db6681433eb21c28e8afe3cc49dc7ebe8388cf21a33c89ff2f6bec265480f10e8a422bd619efac3f49ea4cf1d329526d6f177a0e1f273bfefd0540b329160567ca8c2d8c4928d3b9f0cda96ed77994834ece298e2f303280b7703cef1cfe87c639eef237c8b634ded8adebabac42a5b9a237a3b30e502c4362a1df61863a2c8eab973118a951f45b3ac880580cacb60dae4aaef66e723f85222b0446703be8a8b208832fad7c5a9a932e0b531bac0f66b97e564f24ab1e08b318267c2c739fee04938a2ef15ca6477e367aaa764df7856b6d7753648a6bc5641304596193eae07d25d9a4b1a80f8762a6bfa5a431aa835eaddeb6d48589b645a54ce92f969d85e86135a4179841127017f46f7f1d16667afabaddd7db0e79140f36e61013a31dc77e8184a114a1b171d88a1f0883458dd9ab06c28ffde4b0503bf59c321df095bdf216fe31328adabd7e958580ae129125a7162fc2991b8480a9fc9c7f10637b093bbf0800aefb597d22b94d5563bea9b04f33db8c724e6429d5824d5b437947bad43766249ba61f7e47af53cc786cd6291bf2533f0dde01cc5837d6b4c7de6277d471ded92fd9e040b9633b0090e143b767dc3376d797aaa0e656171a89f57fed2c1df667b3501b7f8e9df9e933ac837fd3aa3d9505734076ab943a45b6c4f00bcc446b01bd15f77881fee893cd442282cef605f7845989ea25b0277181f1ca8f392a0c9c229e30744ff79caf2ebebaae197a2c5f3be58741f2ac6a298c14d7c5e844d07aef46d80b792c9f3375fa60eef8f045dc5f223e1c3e406e95c48a50502f5691d8be1cece50d4c63846220a88aef413179445c3d0a2b783a985f416d40eaf744f43c4b79495fdccd59a733ae3d2a189c54070b3fd4a0bbda396c1fce5a6ede4e0eb0a94f3bbbe37330a7c1c4f79c6d769933b2f2b164fe43edb4984aa985923bd2d541f4598c29f9eb3ef670afaeced996ac0e06e26ec7fd6221f7bbb363f4bd8c4b4345857f467c3a62dda7b31f19658d5e17390027744fd032e9055309c02c30f115a71287618a383b04701d9a6451b08f4d62877e7a88c84cd22bb305288d45956f0812b113420540ce1e8082840c66f29eec32ac1ec0e3953e5b2047a5db5e0f3a270611754d3ad782ff131bcf3fd46c6428352b6abd9c35f6c1558cc0887bb4182ecc05b8ae0b344dea44a3ab641d259b759b890474809a962112cf56ee4980f3694bf47d5c3056834c40c40f896c1f687577d45a43cb565113be6c691b518d6763e806a53df22a63d194331fdd061fde41fc3f6d431170692e857aa0aa6c41e1c815b6f8b66ac642e9d576c53c1e97c856c522b7d4c7efef718be179f80853a0661e2cfdc8e72646d4c9c3c231de05c21350400fdffff0bb33e6f51e51d890f80f446bce5c2b1cb23b600f44de3b8086782cb1a2dcf19861059e846faf9fa964fdbce1130c3d4a5e4706c695e32bbc02a3bffeb569c09855b83dc77f9a4d0c0a13bc23dece30dd8dd7d09097d612d152ad735f8951b2fd0d81af878d540302237e91c1fac738e17e3f348dfaf680500c0c91bd5b3ab795d1129b9f90664651e679c5290eafbca86fcc600f94652da211cb590a07e4a05989806cb86bf25080ae10b0fbc376b6ba818d27fb1dd9cc8415948f78c98e170a166002d9bb9da663182aecd6d83fb3f78547b5fa02a68371f7068d69ce6b996d9331fc0724f579b072bff7d7f4ce43fdd4784350205f3f82a7972e4448cd2d4e01f9e02d8d36129d945c86799d1ae11b1479cb4609a6dbf97794be1c190c3d847f417684cdac040f0e785dad8dcfc61f43974797d2c65626e8c3b3e2b3cdfb189858fc9b5456793ff0befe46292be6b1419f08dfc03408efe725b78f28527aaf027051f4685b05e88209ce4615f736f31cc8aaf8de92ef2ea8b95e513cb3eb39c2bebd6ec50da6c9090d53b4657e6dd9d70d25d18dc76d029bb34a5fdcf719bb7beaaaeea55f104613cce403ec8d85601ec4fe9db8f4b6504691144819012cf64c9899025e0cd02122854e5107ba8210fa5f651e854a9e011d9437cf8011b16e6e36724513ee5246dbea0a9d11ee06ed222361378d48da3503c75d9058e165a809d4c056a65799cc52e329ce353565c4223796c578c7d124d1d95cf08c9520b3eea2864ae1387a428f556a3a5f1f452aaa124d463d55b5ad2637576c2691594860efc6f3e41154582461b5bbff1f619b13596b60ed4b7caf584639093fb3d8245e933b7e31ae73a3180b8d495fb32c0fb930b7f0e5a46525f290883112beb93cd1236d02245ade41f1a7497974536252ee2d2f32b2ec5c63507ed80c535683a8cb2d7c2af2520690f57cf895ae739d4f37602c8015ddae31c4a33b2276321ba02e5ca3d005fd73155f1c4dc242f3292f0e72f89f3809e043bb5409a76aab34823f64fc768778a4424a826d80646e0c12178e7140d2e1596021c7e116e1f0698403a711eef488daf5eb1441aee1492015d57530b7b133b6a2f5b04cc1b27a75f49e434a63414e2bd8e3158d428abca208a32877c403a4a9278baa219da406774b56689139afe6140b16596fe10ed858f356e746ccfb5704cde0d1e8e075c37ea0478008300c8fc3e3783fad334b0c5d72f1301329397bad89ca63e5c043c11491291efa49ae88f6780ffca6a250cfbf7df7c39cecb7936a16f80b00588eb0d700494f4aeb2a7c3e2c09380d3e220401076235385663c421a36a4f71261ecc89a0a049e1f8251bcaca1a6b2fbe43680de90567997e3e7f2839d38f340f17344a1234b0120fc983babc39f8a4db9f714f44864a19c8e319faaa67a3462622fb053deb44205c6708a1a5d8c4470aa1810e4245242fee29535e9360202cb46692f5033049f12243101275bd79b71158ba53f3a98678619ce286b243c348280503b0bf879dd3524973a3d94513cd2fe9a78c96878c3aeb236052b2b77f4f28247e041e786f947c692b1dfdd83a38666c2ffe201bd0df9ee16547e7c445e5081ec4cfd6185e25bfe76e268197314c95d9c3b14f4ccd01b1c853010a74bc782c461476ca80c63eae96d1e2bcce65c0866c22f54c4d1dab0f281ad780fca12370c9a5959c54a3fbf0a632250eaf75a8758b1a79a97aba3d7ddb840b75dad81475f420af28ff5e46c20aa1b3df6cb820c9a17f98f0925f850cee0a5894ff1640213ff2d217cea27c4584253393c72d7b7bd05302ddb03a382e260d8e5da8471cd6f0c694a67a36404781fc3a187def450588cd61c3db45121c18c725f2243a417504b51955cb5e3646ab2f1f9331d93d582e9b510f7a1e63d9645fb1cb620f1dd150e66f4624431ad1fc9bd40b1712dc53e63f5002f30861f24cc7d2466d550941896b0ff31487b8b878f737efc865c33b4b611c156ee0a90213090a1794717564bbed6dc2a5ec31c3dbd27bd987b7099a92e3fe0c139193e7abc6ee702ab8f8dba436395dd53aa760cff764aaae0d422c448500ef49fb162280cfed3d98b3afd91dd179f27198b01434ec817b5fa1f1002ce3502cc5a6b15e740972b73bee78534f9b18ca25dd1226c83852a4b9a6b211553a3450abde69f2c1a1a7cca3b13d6d7a65322ac3be9ec86ac408c7581634125f5418e9bf15de246f912b80c4a3c3da206f588a8a9bf1c787efdcb2973b33aceab343aa7f2608461b2a5389ffd20680e87df965799036823015fff3126fc85c08eb42823c97168cf47f702e831640ef1bbc46183b322de5d2e001615eb886379e78163296f1959b4583de94c191bc2772411677d159668e1c5fcd973b0f4338ebe1be37ebe5be1d0a3aa05bee2d3025520cf74a79e06803a6b2e70d171433eeb5f053539443fc115462101b8dc517e27eb15525af886640933b039d6bf599f69303e9d49158ed18538b1c6e099f1b81ebd24d99beef03b3e56e11a8b29afd35cbdaec0ee96e0a230094293b98dc27ba42f021b97381bf0eb38c26e5ac01100082b6cee587245085db1c3a004c0c1a6c90aaf458345970a6c786a785021dd22a890d6c649b625e125c241131623b71154f34462bfcffad508118421f43930a6d60078fe18ee6a04c905f3fe462fc8680ce74f5c414abc9d81d56801001ed1129d2c34a9c140bf1f56ac5b31491dc09d255f0f3f5ebc7bfc2747d1f16e19810c1dcb880376a91031fd5bba5521102e04665332e885f18521f426892743321cc8a88f0a8e859a937d4db30f33c0e5b93dc82c9b7d56aab0f888d6d1f525a13491ed2c4d4377d128409900a228bb2447a7fce35ee69395095cf1945382531e07c6ac0038263ffe8d9b27102cf899b69548d3161579ea7f4ef328e9eef3bd5ddc41aaf5611c6241d7a804904bc7cb85641170d375520460e40560b2913d1151d6302a146efdac21bb48a47ccca7f3852c4a1511e4ccde0772f899243f6f12486e6f38c517771f4e64bc6c4afe835e980552ab6d9a7ccf068381ff34bb11d66d802c36308f255852c6d1089708adc48cb21d1739ce9b86feb15cd2ae088e4a5e66ca8c0aaf152fa8dc5ba2be23e968e2178c41c93b229bd88c212a190167fe7d03e035a4f592615ec15ee616e7140900a3cc0260735c86cc74fb102d554e61f3bad75d3fdeb61a6aba5588ef4be975fd25ffd9ae8f6ab0828dd23c84f510977fd1d8452239a3af7a35041313c81247573059426bcfd51c3760c6775a36b35f9c2692d37c415fd6639b943a4fffa927b33bb20a72edd6b314285ded9d4ec6557a4c7e5a06978e25183b41859a779f78ed141b03521513ef6d78bbcd5ef660c635d21c40171431cbb567c87de48272f626103532cd1608a0b205d93c001f06a4f912bf95f7c453473131372c8ed31f1b84e49c41ff6ec943667a0da46b7a07401d4f87a22fa8a7adef507cbbdbc15f2092e77aca23a7617aaea51e7970beea8314a63827d1321ed895a39eaff4cb1594b759b84f4cf6eac8c1ccd01ab365848508cd2bba84f7dc4a12e0f92763e3839cb893c18c0ca69ce073f02ecab5617e2bd1a44ff58e04c0c02cf8445e85fd197084e72f26333a2ceb04ec17e5b75ff98221c1e4d68508fc28c5342b6061a1f0a33fb9ecdeaed70e183609bc945ad245a4bbd842541c5b4996b76bb71964ee8c2d2c498aaec0811581487bcc1f4ee8fcea0259081dfa7400ee41c714df622d8950101231f04a51651cdc7cd2152f63e0a6f35d67b57855f2b9c7e5f9c7a5110a1fbc0a09f20f8437aa357d802a42126b2fe6973a827b892047ebcd95dc2d5dadb4678f16059e81c59e4bda8d99c8412e5db9fda6e393e6f31e3c77ea06515cec4198cec6ffce25d1b42b11c83293523670938cb356c0c991ec8299989028bfa1726d36d7d7908ea92c92d07f8ce2c7937a5fe627b3b80d65ac7a32b9187ebcfa06baf4e66f3df097fa4210b70fc3c2d79352da279897864cf6236fb32ee5beb92bf465e010465404f4f0834f2321c7b33897dee0c04d281708046ebb6fd466ac4222951d11b4b863e20953a5d609da7a87872b61710b60ffa58ec41dfe7a6e9529468e71d434079493173c28afae6d1025216ba1ba83433d1ca3aec451b83bd2575534fdfb4ade905d9807e6427542473363d26838d09a5f94629efcdb2842e2465cf6f1c3b6f2e53ae436d911d251d6fe8d329a4ac65812d13446c87571f839b265cff92fc19dc21c50246c017228000cfb11962fc8feda94b094181a0b7296600ed222a22a3905bd7334ae2c472bafb84bc8e65e05a0ad2b8a4334fd192318ce6e375f663b8100a5ece21a39fd03e1723c856d828862b47d301d5516ac6726e6ff7956b28b36669edac153ef43897fe0878a0e8f7bde98a9036ac8f440f0e08d142154acd347b0b5cf789a4a8e9970381bd0c95b1cef1b190f07f4a0513249e29bf2f9d26bb1ecc9e9a58ff9245f3cfd97fbc7b6b9eb9316aea1a4072a5b89d5b05f8d1f1fdb130e3be036f0bc9a457653b7e21da4b3efec4f0d8320ad73c66f88299155ee2534b3e88429665154cc0827e752a2d1f49b8bb6390f7add1a36275cc22ee190e99b6b988b908156593f35dd57a9ea3d91c212bc08ac15fee19683e652218a09d459e1d92e126f4254ddaa70247837f17265e1cca2630e54cdc952eca2dc888852fe973cf6c2b84ac8dd30a4c8b71554c210290b333725900698410d57415465ba1e89b74b7fe127a3a559257b9cbf0e9e01c2a5084325427cecd7ec5048d923cb699887090679886a8f9d8d72b38492e35a1d311ca530fa93ccb4f24ae895c31dda14870769c6d4f2560397e6030928f8c76e22e3aacbab6bb20439d271b33a27b8bc3e3724229668784c593f612d8fead6e960d08eb1538b2d624d904dbcd54461d32fde0e11f8f2aa24b516b0c7d6eafc562801b33ea52fb5660d5a51430cdac723fad9579cc35c0feefdbd9159c1ec0076eb81d11c63b7649552ca5a664298de8d2505e0ecfddddad0ba7ed6f184d78ece2063d38e7e9f1cc0c6fdbe285226f6ca782da34f8270f8ad7277c7dfa13187c4e546b585d9f675f43828b6c1b6adfd31cd8b5c776567b5d03160a08211477e906ca3c682fdd629f077a920ba4748227f6362202bb6621685af65c63273de63662977f6e3889a5f08177776c19baa361b03af2ca916ac6cb900041a473c83fb4fee3e54418fafced79eca298090034892aad01090a05f20c370b15ac335a4f8a9bf15b212b9b1cf7318d89051ea2af420be1e8e092ad4c2e492c20a374fcf54e797e7ba6b4f164014769cceb58b2cc16322defbe3fc35fd1c35aad6777e81d75cb459d01e829cf48807199655fd600d8124d9c9c18183698621f80334937b8cf087cc1869d34b8b6cf1241cda4f9010a25746577561988d12d189fd4004524e2568c50e60f7c83f4a95d0532730c819d46a20893237ea936df69a51362eb0e0219be6f039149fb937b22f1122cdc137e62569fc624496b22954b764411931e2dcf04d301887080bb3dcee827ae00624c5ca4242ddd534ad45eb9da464255abfde67803e460467cdcc4d06d524af71e2b33f3768315e8498afc32063a3531305850a288605b752f3632a6294d3890bdb61921e758157ab8f05645fbffdf97ac781144b6671e7efbebf262672e3402b119df6c5c942595a3f2b7ee968c5fa3d2813aade7b89c12ca2a710da7d81894ae7a229e02a97f712ccdf7321d364808f1d70714d2620c62f5de29d53060093cd6fddc41a99206c41dbeb01be41af7cce53a931b7379307156523e887e9e6035c9c296ff331a04c1c43da8e0d670e8efa967e6bd34667405d76fd8711a4f66444f736c145fa0e68bc1889eef4e839c0687ded7964f1cf483bd1d867b4b10cfd4313d06b0c9092e230b8029121102046f3c97fb1423ba9273fa5183d88cda96a3080ed4ccc7c6811a42ab5f51961c0054793c2b1a2a8ba8c83b8d679d2881295cad00336d0995140c5160af7b1b276946bafcdb8b158cc1fa7347fad0b5866f23280b3c0acc1b595517c2e559f6442cbd113256503d871fdca6a4698d20d2bc000306ca8d9013e72ffaa8473a53003d3c6bb874e49b6e8fb4b2829e77dda4b484b8894205cdd9704611721126878fcf1c92132cfcd73a60ba2da4da5a97eadead48df9802405df48802e7fbcd1f51ba1d49a2a7cac8310b8500cd34e3c8db04178b33195640389a2309ba189cb263e21d1122439353cd5f412c52c36ab71256307f7c623fb2c40548d9e6c5811a2bf09121ff364ce9c4431488b59f7295547e18d372a026a0360f3a275484970122932c1e9c51fc1a6e4bc8a1eefb5b8cc44bbb5f76e2f5f7342edb364a62af98c022072a84de20af74e83401038cd5927856b5f20e78519563a5fffc3920ffc2e9417cf8ffd8bad17ef9fa74d7337adc7aa3ff58c878aa6993b2c7c86f1dcb55e0f7640bd384ba9b508235bb1798dadf7cc5d3065a5d49868c324984d16dc7051e66abca35ca7ed67a674fce2b93c339e753cb5de828d7aa41c20ca90a670d280eaae01118522e78fe9703a294b516ed0b0d0ce3f772b27ccfd34bfc8e77088ce69640fa77830d7617fdd668df44c55ea59bfbddc11931c22a24bfd1eba50b89e721421d0af9f5c2756147bbe1776f801725295ed814f516226412d1e047a9408ef45420935d8106624450e1184a388061c63a418800eb9f21cc97b87c68b2caab70574f762e2873809132871610dee58eb34342902e8f99fe483bff0029c187cea7262765dc69cd93047b1d8fd0565aaff880e63a0a89569054e6b31c786bd04d3c0635fa79562ac208e1004fa5fb3da5cc398fb04d61f049232861b45fbf61330d0d1dadffb73002e8ea9c907748c8838d9c4c24837f79c47cb9f83e5f89ff1f277443649c458bba527be9423c2ce8777ced5b5f38bad2ccb29a134edc379022d2e39d62e0801d65b96d8c5b93234d1a8b9304d05dbec8f9aa1d519c41bd836beee01e9b67acb3cfcead7b57398dd6041c472e8cf55fda2f0d4971081d3a710145fd7df109fe04bcecb3c80d7bfa3fb83b80bd76bcb332a4ce804000bff360ac5069399a15e1cb463db8467d1ddc1039d525a43df75c5649c18094344cb6087046a5bab4ec48a00ecdbca13ef2ce7cf31bc41b0b76f8d1c35a145445352c6a66af5ead69af0f1f81495e8b4d99f208dc03b7aa05c1631c1f85d1b3ff218e5f4d313062cdfd1623902465c48586106acb183a2d83453d09c94a2ce61ffd65287ab69d284091caa5cc1129758072eba8071e5306e932834032062d5f3e4773b7c1fe1923adaa052947301f1986d895500432f0b682f6787969f92a2932e4709af2063c07813d817dd4ae98dc7cf505f49e551aa9ccb0bcc7414e85e43e88889f3e861a204f7dd8275bebfd15f8b706b4f33c3f4745c72b2d381d8547d330cac4f052beca757f4c3f8b492d6ba1b8cffd0d8b8674a79d03f4353930c47cb9686e049a416139d0603ea7638d772caaf180d2235a0200305c727cd56e031fff193afa5fbee3d1b900da5b80ba0ff4b34ac0342f9e1ec0f12fbf9b1874679a8c6a203b174f12c0d198fd6a36cb9abb0477989df66ed319dd7f453e08b8152790d8cb39d95489b6884f7fad4cdca3e87f27c7e95ec10141d8bba1afcf48cfdf6b8953c0ca459c531a7c13cfeb327abca4d2e0581f9d7be2c73ac7a6a2503acdb4ed40ba4db59e983e55aba8b4943d16104ee98544d1f1d91babeac4b7fab69db8f5c7ffae84f97a00cb4e50d02a086d5e091b48c48ab0929f569659d8c3f7f5a7ebf8dd5511e469cb4f877b804fc7ebc504aeecfbd3efee2c8b7a7ac1c4ce8cd7ce401b19773287a649112aef25ac95c2322057d2f26e4a03991e669aeb623e6b0e0de84a48a34f25e9d781ee221a626a0683e32a948385e1c37d9d8650246e80688a3432700e959ee748147b2595d4c37067dac072790e9402eced78c6cea0462f7e9c0b372badb850b9159b24b38d99548fa76ab93ba0b6f7fef27fd789b1465501926e0d3448af91f6d43cbaa26546b51163a49d63137abb60af6f678b87b8b13c1a4c825a4474bfb5ae006ac5c334b0a2e7fa92f1c48b74a146296561ce9c7da085ed99c9ec810f0fe52ec7e10ad3738900272efa68efa3cfc65957500f0d8aaa2d01457f8b02faa1f6eac59b2341a20728b26f52dedf3aa7525131e86df5ab449390ea945eeda3367a5c6832ab05a722243c5db6eb3e006778597b72425416510b6e0cdccacc4c9c0d511ff0be6d936debb899e441e5084974fa198161cf5e1b5ad8a490a44a387e6c341472b6ce54c4e12c174f224fb6c3593b92d15351b96c8df27c68bdff4d602b70b8f2550c1df6651758f2387fbaf5564f533334ce77c1bfa594367d6538e17a228032e1b2302cae0d570876de13bce74da3c1e467b19036c0b8c4ef1d6cccef17e34c3993a656a42f854ba1acf3315d62f6dd73268cff797217e86beff6fe055998b5f00deaa9720fc98447ac5a3a0e0cc1e85b56a01b3933d1bf950e5f50d28f702a481f72a911d984b682c4b39a4967584eb8166ac82a09026a900d8f9d94543634b91b483edfbb3ab47919abb9bdf400d2f1f94807f852a8a7bf7a8139dd869b1f3d814a16d0a8db4c5bc8161c66bfd592756a7761fc06e87d1c4b36c502c9f8f6bee861fd7fcaf9af825564fa3914013654857fa2abb94b96c20aa50219b06b78964587e69ec20c310d8c8056624e860bea016fc2b214ccb400b8ef2bf262895e15fc4cee7c9f5a5c46781392bc16424aad3b7190808e0733b72234670271de1717d029e7c23ba98a0107663cbe8960ea49ac5e97b03a165cdc176ecd471249fec85ac1caf9df74750e291e70420393b2b62b76e647a5ccd51e0262ab3169165dcc2466d740f2daed7ae97b091bc4705d1a831a434a264378ecc50e5fabb7303fc52336e32b8d569c541b2578978b91374c1a40166808230d6c397a22a85db3367315b30ff5a23e749ce6142054b35c5ad59a0d0847afb6d019a9d8c6380d31f1478bb90c62ea0c62eae23c09ee30792d767a6f56d30327cac70c5bdc1be8355d229117e7218f298049917810e145e8c9c0c46ca91b6f618bdb66712824e80252f733871c6a46378d11482008aa3b3f615514cc235ebb9b6e5bb3e22a68e292b3b017298103b9011fcfbe1c2010ed591f58c440c265014fe6bc93c4c288417185b3b992fa794231b0d1cb61b702c41ff5f84c7466c0641693eb2a10639098454031ec773fa394c1950a7afb05bd4393c7d56328338e503584fe2481b96c8e39bc29ed117231573ffd914ad8fb0807fe6b00665ae2fd4b2f8e4c7e7f9ac0203f9d5114443b6acf7e82959289b7d617aa1d78a7948ef905a2fd07b7f875b87b0631eee7c78651a2210e5d00410cb5c657b6999349d26d744f00179c71cfb48408c92c6ac03b20efe0bb84813710a785a88599b1ec8e9bf967f37f172efb09ca06bace1bddf5add55f6ad2e0716b4f5da1c16da6b2dd6236d7bffdf858027629086fc9d04f13fc202e23bdb53240b97bcd36894f10dbb7192dab209865591368edf3838e3ed0d917bf2372254c371d78b069985a2cebf2c7c8f7b77521dbc502d31c3585a6db7573ea49aded431759e7c6d8addcce819bc16b60ca50050c34f19c270b1cf127367b3ea4a584ed7d8c45b65b760d26780caf36deb5ec7d90ae1924548d3f1d755d0b58128395eead58bae640a33602a8478c501dc2e552ccb51e94a4dd7e8b4f1dd990143107f3c45e017af867b9f71c91dd2704ae663bfb2729419f03e17f1122e54e08f7d94f60ebcf7376521100c5f2a9ea41a7356316b922faa150e0e3f9353c0229455a9038946633c50327c92c72893270e47ec812647fbb1491e1a6f1b9f1c5ee903a491d4baed0b1dd5b782de56ad4503a8727f78b454dd6b7c27d829ffc5454f094e17c0e39e64233ff4e2de57f4ada7442b32ab421263d9eac72a0f51b912f867e11b694fdb2b2ea1ef6cf2938132726ab718339959c22be16edad268ccc8997ef014b5aee28058921c1daf452915f5fb9846786ebd7097d32f19fa5ff77c19a296dec8c8919f2b84e951e9739ec2afd25ca7a3dd4a3b0109826f48a8a8b7ea12286253e1ba94f3cac07a26a5535208344a2692aa871a850f753e08122b11c8254f5a5b44665154645c5a0fec51519385a849eb230d7e065e01c92a8199e903b72f7840c163d31c88f9acfd2926f5f87f72f964a1e3cbf84d6e4d4d3289b0f356b793658e2c9ea3b0bc79cebcf765d38ce301980327ba5f8a1b507543c1587520446fa32860b09eee552015a4aced03f7ee98d24d8cf78ff895a355a8cbeeed01fee29cc35d2a1b24f0204d7842420266720aa3093aabc9c6d83dea93ec9137f1e2e2b4ff6c593d9acbada32f22f57f43e80a368e2cf73bf41fdad935dc133aa38134c69e796ffc7507fd23bcba21a17e2c83c8fcaf6e10c8ff3117f9e12d8176f35efb76ddfd3dda399314447e79b1b0c6a0c8b50eeaf5daaf0911f408fc8ae2ca22bb725e36bea76647bcdef646524c85b24a223dfd75e1b30ccb189554d32029cb119feb62d652375f76c60545b7258d049e435546deac526f285799e4884ebc16e9b842cd34c659723582a0950e8de7e3a4e008c2116d749af68b83a9c0f55c9a98d7f93a229aee6d03927595fd0e091355b2a1af7c9bc2d391bd256f5310b950d5a6086f45cd2391ae9c5e26957b7ecc3ea4bb64a4d109059ab2b3141a9959dbefc76880d18e78110e0936a2d2b58689de6744b970de4eef9fbe878cfa29919ff8b8a57f8265c20d0b512c32a288b51380c6e529a0c9ddb9158d49c26f6877ddedadbc2cda1ada0d099934b7925e4b4df7eeca51710ab4b17a7ef4a6e6aaffa7ad4c8f70223a978765ef28117917342d73f303954f702caf87968604010b2a20ea53a02ad4d86ac8827d663d67a610aa469b876d11705d1142d635726fd12ca91ba22b2f65ee88bc3eead9aece1e79c4cb123300fccdf5c4683ae1f5cd753fb8143151fc40f1367d156344860869a8a288decbdf7de7b4b29a54c01be06ab0609062ae54e2997744a2ae56efb8c25ed5ad3b121624972483828375b9398125419a3d168f279327042a5dcae2d640e976f216fdcdd915bd4b2ff51cbf46b9335a335d488871c6cd06f0250def7bdd329e7af5acad9f659232816a3634939eb429212c3651d7b12b3f92b568b295162d767cd688d5892d810ac5957c668349ae4f39494fa61874a392b9464f558f2185957bd53a2200a026485a09269cbd64555a30beb4b9ae787bf92fce5c2e259c183f60e16be338557b18d643c10e560c3ae9fa4da678c568bd174cc665d279e1ccfca0a6943c672216dc8588fb7a865ac0fb5cca4652ffac7fbb8f83b85d600b5a035f5c3a04b85e78429fc59f77993ac4ef8c3baaa4eb8c45df5439a75d16a4e68abe6109e132a51a33af34d0a6f2e4e493232d0cf45f318f11ce159d13c45feaa0f6a416bf0f0e03529f0e8b0836a0e4f0e6e561e23bb3e086ab173d23be10fabb33385bb9638d2a6599d9d18b84b891d28bb7edd71819be55913c404f22ba87850d875078b5ddff463dfa17dc668e1ad54eee44e49326cfeaaa68da54f228aae461921d92b4f49ac92666d3b6a99cbc3e818cdc69ae8984dc76ed6555d1e458250b3bf32568bb1205b1788b33a2a3066a563644ad22b4d5b355d5ebce81b14a4652e3fba942e74b62e1717258bb5cab77361b156ab1569c37c629ab69797163a9719f0f5b978faba68f719f50d0ada2f5a8696bdbcd8b2c8678c2663c768d51cb23a1f089db11844bbc668bbbe68169614eeb0f7383ff64e49272d50a48d52ee9454ca5d7c4b39b3c32b2b2a2a2a279b0df349354f492ce43346b3edfa315bcd394f49bbd294d49c1b6b724aba1e80964a7d55721f1742e45aea015a10ddba5b2fcf1e5ba89ab4f43799ea5b9f7a2d406bbd2289da0a4423538835dc0a37b0f6c457d302851a9e917dce38c1872152182aca9d80033e4d9086a080480c52923022851a3f76ad827ea00882a0d7596c9a998ab6ffdd825e7727dd0ca74248bbf7deeb55c9c5d8886b0405c2ad4ef5e9321522ec2144c1125c0eda819fbdb6f33c12d4c0c43edd1684ad86230dbc74ecb841fe82003b904523600b35e010a5862110d4c001a11a7c844d327905120d4834acc085165622a3927828e4d10478ca08f6e9b6246880e1abd1b0011966d8c28a2c56b14fb785413268035b0e5b8e1d361d3bf0cd66038db0e5c8b1c3a66307b8c4115b0e1b0750ab3f9247189c6006187828f082ace0410104dc802d8a2d872db4c187016f06bc021b08a5a3d93e266cb61c3a7690803be1707474747474c48409931f98fcc08489576b5bb843ad1cfc64e2c3a87522c07657ae54b2f061d45438d5759f8a00b3416e39fe49fe09fe19fe29fed9bd38d29152b2547a52d7ed254af7a8e916c1717c702ca9287e2876def7bde7b4c7da12cc4d6dd0d25aeb84db6cf55a8cf1fdbcfab0c3430b4b0831840a470491c08214178678008a253a28c18dc2d6e3c920c2ed20899c1622387283911c30f18128462240e1e88885871996b833182145eb8917300060ba9c40427a1d44d156530b8a4c1f9743320982b9b1636ca2d59ec076de87546f37e61315896e2948d535c5d2327e3d08a97ac82e54564ed45e1c63db120bf9cb3f37415f56acfab8eb3ad1dafb27feae8379f33ced77317fef16f578cb212098bb7eb6ac5766c432adb14529b6d6ddddad75b76e63c46839e13507860af00ba77a4821c9b4d2982ed7581343c6cccceb05a5196d6a60f6451eba193f9a4292a90763c5194b410290a3334367868c18391a632c56434687b1b5d65a8cadc5168b3546ec6edd7e309682086074db76182a68d1a068678707f3f44c00a77ac03d3d328a544beb8877b4a6987eadb5d65a6badb5aa8af1b5d65a6bedf781608a29ac25836a77c67c15be560583550b05d529415365339b5298d9ea986636a530cd19330b73fc8ebe3b83f3a6699afeaa262ae719538a79dd34cd2e08ad0d79cc7a4c6a1ebb5e111df66b7d075b0dcdf61fa9caeac02c2c8a4c69b52fb7225b1172cdc578a6a6769dade962ac544b5438301accf5d59c1a7c034b4a898b7ff0fc7daf5205c1350103961414dc799e0ab5ae096e226b416931d15ac25edc79b49250c9eb5425217fd592504908862ff7e5721488182e73e85e233e6e434b4091393a3a3a3ac2339c6f3e9a21b947f728e7fc04a51477b72bf1077b24e80c7fd51933404029b5d70e913932e7312773ac4d92390f4839927254441145a8a41c91e076b317fb6015ce0787335283548c689153360af27d1e160a874839326272ab0de1213c84878c82d068349ad1162296859828045b3298e5868e8e8e8e8e8e8e8af822bec75ff53f803b7c6f4c86bc211c08e23ce64f6e56ca910aacbd9dbfa8942312802329869fe70dd199bfeaac88b108318c92e99f013cd017f7b77d9b94525a29a595e2c0c9910f44d888820062ab95521c88107aa1efeeee9edaee64491387d97810edbfd4f2f3f97a68751a12b65a1a1d66acfdcfe5031af24981cdc33ef862fd3e120420198300961aa86eefa98ebbbb243657396142c5c66d2c746bce22ac34f5cc10d96ea06edbdbe16ce640263d56daf5511f6c8609b3d6856cbf8ab0b2ed77268e5bffc325c0ea98a52a49bee2c73e4df0c80db513c16eb1c5165bd87a2d0902dd55d3ce7636308e2744f4e209215c6be381bfa8add5c65ff4c55bdab84937eee877bead8640dd98e213e87e0961d712effd0561dc61dce1bfd83fec73a5c8160771977d1b700edb621eec90bbe87f5fceaaee52ecbdf5f1302d08b13e16c234ab036e0639dde6d9fef5ef9766a823541df7a90f42d1712c7c101285cc06206e90f3b16f774fcdfef571589d17b0d3d9baaceba1f86ce983be09772b63dea45558a71616138ba18c5a113243f001b0fdf3eafd5f780d100aada9244c89c562b158a8db8d7563b14a130a8b45f3187d5e99c98886543f56d002d4edc6badd6ea5a865a549abb4b09c58321f2cbd2a1f9459bd46adac6aac8f11b5f6a161bcf816285a95937793a4c3c285f1a265fa9852df582d2c206a45b42a274f8785abc342bec99c5a6466468caec8d104d1a4e0e2429522697edc0cdd5a2c466679c5e88a1c4d0a34412e462e8c54462923d2e86544f3e366e866689f5a5037f1a4a24d1f53b6d46aa79695cc8d095c0d4345445efeac71f9d37c55d6b3174ac25b8b85895b5ec1d53044a4e805e782738153e180e4c7cd767d99174a5e28d9f54f2da81b289e54b4e963ca965a0d865ce922f8432d7b51e54f4443643aec008ad664b28e7c2c244646ac161622d910d4ca0e3a80a235a99c5847be23fbd4726a11b54ce6432d6b3da8652ceda3488bf8f657322e542952c88516b7164b5e71a14a912a999aed89bfeabbd082d688adc06b622d70a1c546ddc432e64d5ae5d47243bab5b0422d937951cb5aa05eb56e4619d5925961b995316fd22aac162323b185e5c4021fb5926f0fc3caa85f3d0f4441be7683e2afd80bbca67e1330d82bff2178d23298558afcf00642a1354a5368cde9a1ecfa2b91d5c29242ad90a035a908edf3a3c5944ca13562442c3995a6e4dbed6f59b3368c367d0c08f32b160b66a563ca91257624cc08f34af3564d168c91a5dd0726d432d68bac150c2bd43298076160605837160c0c69c384d204eec692e106a509dc6d25c38df50259acb19ab7db0dcaed06e5966f2614d3cdebb08a4b4580153242b27fbd94de40146584b8aa095deb1d6996110223071839c020d2a22d191999566be6611031cd9d2442e46e14770a156ede5c35ab4e0cadeaac7458e407f3fe7888e7641d1f2f780eca1fdbe035ee4320df80654458fc14424f2479d04dc2756e0feeaa7fdedaf6bf495c07cfacab3e1e02c9f6bf3e784ee955de4fef3e8a88afc2fb14097d14195f85f771f1fee08b4e69ad36642ed8b4e12fd5b1984f3e99cbaf681e232aa698f2c42c071b82a8369015543a1730ac232e7d312b7bd4b298c79ae7e62f9e293c483c381e28d5ac4d6a349bcc87cbabf0f9c030b1ebf3b8c06b6a8cea5f05988f8b8761a2248911397fd5770153f37c5c6828c9a78e905d693ad311da558baca2f20fa37dc0c8bc0a309ff2f58d105a439fde10dd8c37453734b8c1dde46ea8b0d944bd9b24a47d9afb8cd164748c08f7a07ea9634bdca6bed6b1241e805a3571d65561ca18cd6d681871ba117273c30dd17823e4a6e8860637b81b2a6e72f6a6e8a6e8f779537453e4dd14dd9ba29ba46c5d2a4c6d918acae955f422f29abda47821bd925e59b4b0a44031a4097a117915bd662f295e48af2c5e492daf19cb6b967acdc0d74c7ccdc2d78c26485c41a146fbe2056d6565e56dafc4df740c5a4813e91bdca4affab3a6e54ff3555917f92bf1b6bfe918b490be4113a9fed6f23796bfa5fef6b715fea68b50a914ec4638a258b44bc4a2b184582fb084764dbd5cd0b45de822f8452d638dda840b94b7e93f96101611eb86f09454bac25da59c65155516911af355d7b4b18e54d6ac9a44b066bb7e8c565342336209a1357892b06e40c222aa393c3e0cb1eb8742760dca9d924a57b088b08ac653923d2591a7a47c4a3a259d9286f05712af5152cd188d8611f97409126209b904a5522ca40def4d281921f9ded21eb5ec5fd432fda196691f29785b6cb1854f111380f21edf7e4a28e5426a28d50277d1a7295b0a978ae2267dca425314a4220d690a69535aead22de564c8678c463d1790ec9450cde1f1a1a4798290fcc58342aa0549299bd5d9992215654a0a5773769e98d5ec3335942a4f739f297028c81090d4504e21edfa5fb3ae61cb14cb0741f63fdfdf490278d92a5f42284d1b4e3e7ced3d42f4a774fcde35f8a23ec3107f085ef0cedcf2f7336ace57eaeef1d9638324e8794928c594522156d81866456e9fb06b2b16c2e442fe7d9ed0e3f3bed33c6ad78120f895e5596dbd9756773020023527a53b1212b0809a133e502a45420252e58e6cc2dd67ada55237bacffa84fc9236bd8b4eda608940eae5bbeebb477df7be51e5897a95f76aaa543395bfc4415641955ecd54798e5be50cb757f374d29468483d7dd5c4f1297b42e5d353166ace596b28d4021c4001fa34a8e6e44de9d31f54289f50b59a43cfea116dfaf549cd816d8a42a1503b584a902a47e84ad9ea29fd55e9f67fe5f429eca2547a9732012e34095a48559edf4a797e79a3ca14062a59acc82a4f42cda17fa252507f4abd982a5f44287dea6f943ef53d76a7a2cfbc551ea54fba519fd25ecd0ee53df64a4965406b4e47b603f6e9a4c1cf3acc207c8f2dddf713e8e52b7790a091d8e12efa377ccb587f7e2beea59698b8ffe7bc7c9f7fde7a27dee489244931b000d7caa37bef45b13e0f20bbbbfffdd99e3ae384f506d0fdd7bf4fdd6c55b362dc9dd1a75888b4415324bf098909893b088a3f6a0edd670958687f47366bdfda897f6c2f891434d9e6f6248228c1066dfea21f165972001e655ffc347210b4bdafa40d4adaa0919487fc45df7bfb9dd0fd57da1bac5df3570958c86a19f82526b22e98289ccdac0e26fabeed74b6692dcf8c894cb40f06b81f4008f59d00f5fd63f890fd3d06103182c8feeee58b6aa5735095a7a46dabc8dd1bbef57bc330b4ff85f6ba75fbf7be8ed879f5de1b3efefeea30bc37bce1f789e54f75511d23dc9e7bc30afac084859a2933eeeeb2d2f7d452e9f4c75b9e1410b125bf3ba1c726bfbbe508bbffe5e0f11282ffbee557cd3229d3af3e432c4f9848c72328b5c1ba92ac209217f257fce2d70a82fd1bdeaae6ffa0d429a54efdd63e1002e287f533f95573fcac45519ff2bb2efd29eb5bcdb1ccdf634ba8f1c3d38761b8f2a757d1e7dd2a2a2adaf4272dea5bcdf0b32e8d9f75f8a692fef4688665a971f937fdf0f780a298886882c1fe77034316c5b0bcfea2efa28d0efebc4f1f87cd16f9d62efcfb11c08625ca86864c3f7cefeff6deea17ef7d7fab3be3abafa6ab54fa7e63ce05c9d5a438eee458a26aaddac8ee5ddf5b8123dbaf6bf2c3998795fbaae6d0a7ff81967cd8ee3e2c75e1e35dbae4fdeecbd3ff2c01b6c9c79824316e971eeb1307b64db05d2acf1c1b973cfc79dc27ff84ed8efcd3cbdbfdfd51ebc8314235a9c6a1ebfcef1d99d0ce0443fe3ed767a641f961341b6e7b6e7b8ff5f403221082b74f0b6c7c428ffd40179624e3b73cec5ffbf575ec0a9d11d10404df5b5617ede6e14f9f3e9015b176d79d53edbd7fdfdfb86fa9f7d8bb074b5847abc6c14aa44db57d41b4995ce83adc75dee1af6f3bd7d8f3f7eef7b0f6929a964f34e87ea0e4076abd74972ecea826fd1630e083884d9fa506f9846d6ac2855593ea1722c83760c2020a9b3eca636ec4a66f4ac9272c17a9bf29e6e1b7275b7ffbde43a6db79b8f6baf2467daf3cfd5afcaeff2b61d6757ebeaf856920c34af78a9342a4ef2dea4edd9d3a0c6fbff75f42144db45ad01492365e2891fd45d32c3cb29dd2d938de71b4751c4767299d3d36fd991bdff6dac49ed556a95b8c3fb841f78cce236fd0fdd1effb1c979cd6786279967069add47330f4ba10dfd08661a5a1b34aa704368c2db36b798eb069f9b9c0a4c30b59b6b0d615f9d62fefdd41b665f98237dd3d768f5d03c73366f9847dad3957a4f5039c19e707bb7402c40a24102b92192801dbfe6f75baf7209a6cff5426adb687d9f7097d36e8505985589c107217fd8f529adbd4a935627b82641b12626492c1bd5e777789e20e83e97425b5dd1fb627399cd00b3426d4bd627777775b8776c52c4bf23b922cb69341ee14b6833c03c7073d3f23e4d0517b6ce80cdf9fe35ad0aaffd5347f466dc15c2892ebeff78af7adbabad581c196d8b02b45c16d7dfcd67eadd65efd807bcd9248b64952b0ed13b6daea0405571c19220765adb546830a1b4175a9413ebff53fe4903425065adda4cf82c5ce39f6cb971c24b80408897bfa0f850bcd5b238bed5f082656102df166013a63673575d7146f4dbf9de92e59ddb968ea2f5a0370fe56cd8c56bb6b487575a58b2876023cdbe07718d38c58cbca076fc9b2dac5c6f7b3ae981818d7906a5610c4ae221791cd4c859ceec1ce068f38a3d52b1bbf781999bf3fdee7c5cf8f0b6efe08559a229aa29f21ebaa43e20b8db30407091facf7a1c2e753c4041380b2cb03a9f67d59bd0aa6093f4356e767487521e1b1d3d7c739c2632a4e3cb6e231d4d7c731c263a9af8f5384c7589878acc563aaaf8fb3c4632ebe3ece91c740223c869578ccdeaf8f2384c75c622fabaf5400276a9f38b4983f59368eadeec47c7df16748cdd9c1ed73075769add56b57ea7ea55d44da65832b04da75836b887689c0858376c9fc555fa55d45ae1c5c23d02e222e1d5c4554b48b04ae1dfc553f06b54f1711cb76b1749117da2573d70eeeaadfd29e8f9337baddb988acce0e92abc8d677ede0333f66847e82acceccd00cd14c908c241955c8a8c96822c326e3898cdb8f0b333e7e846ace0c0a34455647061419538c76952165d7771191a58cdc1299c60d87864bedd6853d4023891d5ccda9bf836477a4ec5c713a7790b096b5feb6ca9f20eba2a9d118d1e0cc708c90ac0e4d11ce6b1c221c213843da839c075554d383240fb4c8020707c707ce0b99051b548b6c6636336b6354cd6a63a364574c82a0da218d2838b41d8e5f1fa709ce101ec36127f11a3f43280eedc747d581f9fa3f4130dae648b5f19536b3961538b49af37d7d1c25383558924d91c7c8af0fd3a2eae0f8b099d114d9cc688a6c66344536339a229b194d910d4d569121d4107bff9145a39021414030c4fbc0d4164b1c1a52e4d366f6e19021d40d7ea73d30a44444bb3e481feb4f0ffd04d11aee352706b2e9535712643568f5b7adf67617a325536a7c5b586ad84a4d29d48acaa9b42ab194cc5eaa029a181f3646413bd0fb28052fbd94d27b29ed3a5adabf17bcf77be9f9c1b8553d140c5001374005fc23c280ade3fe6a841caa5a75c6f1bbf5f558e220d7f38ed5aca5cb088d835aec4fb07dfc5dd781401ffc1bf7c112fc1e1bd4677dfc62edf179230e7a1d97b2f0c372842719071294d64c6bd64c5bab79556cba4df67adf83fffda7dfa4a19ea1b5d6daffacadd6ba635391fa1ef8511918987192e9fb5f0bf2a8dd574500ffb416c86e0fe3ec75228b4f7baa48ea1e3edf88811e3eba878f9337e8069f0263f9f2816e5f3ed743f1795db54044eab466ca8a52fa9363a43d47b83badfaa6f40bf507ea513b81c172865d00b65da4f688ce5d6c0d5517e2e8ca9e9ff03faff32ef4bc1e15adb1a22abba235563fd5aca58dffa335f64713b1c55f19963f2f3d2f74dd124f02c14fefcd2e2f469007e0ffe202b9e7fb520a32dd1a03f93d4908360de1f49191a54f111e2594e04964400741fb960db0506cf002bd5f09fadff0ef0dc3f0e6eb2f5a9e40d6a25efc2c794384effd6ff87fa5dd62795230fc4f87e0f74190fc8f1de3a8e64f8f36d2a38252690dc2874dabb882e8adeef3fbdfa7747fd0f35373c2cfa1c3526a677c945ad4a6efe995c7e214a21d81eebe2f2188392abac4412e89f737e9d52c5d512cb1c8f5c727c9f2a47b14e9586291e98fff5e73c6f14fef7b2c4fbac9c75fe9abace8fb8d237db2145fe55fb03d8ddaa2e8a40dd3d3cfa69c5f14ed98f5e9809def03e8a66f0261d4be3d0d80136c27b3f818e8ee8efc91d440779f2a7ff5e93bcd23bf4a3efdfd938a4a59cadaf73de91239eab3c71e45919a4a3a6bd335e91efb62f229f9257d3a60833feaf045907e106a8fd99d76274ac4b0f10c24ec97d65a7badf802ecbdb72a8e1f2cf2e934fa4eb3ee53be14a0fa807f521d0fea9e1cda477ef7aba28e3b12bda54a92857230afa480f75ff97d0f963d7c72ad2ffa77b873d86301fbaaca7aa5dae90c17d15a2d35479841b6f4ed57935293522f65f796d4863c46d951ec8bb50ca839753b212201e846673ad8e7b84faf8d42417eecd37143b97dfa94ea97097d6a5a076ca04bdef0289b46d9b4cc3a8ad8d23fab83e3cb385e901f47add62a9214b6ad600656d6bc8944d5fd96ee25acf6fd19b2ebacea99f70973ec30ed7be85e9a13fb8a44dd13bf1577b04d33d0a56e56d3bf9a4e9a2088a985fcbb5695fb0b20f2e934557ebf7f029d4036e3e771bfd3f75dc7f821d38f8124d3f70d64c910aef711d8f529016c99e9c656f3b8efdf0d4055aa6c566520df5ba2be27ab7a6ed868e34f287cfb609c1da884edd7353965340300000073150000181008068442916830cef254543e14000b7588506452389646235990e4300aa220438c21c410428021861084a8886a000a94ec377a98db164184fd2ca107bc803092e20d8afb601d4a1831d5f538326ac5ff87e9b58b9af2dd0282cab05aba9dc72a5ba24d06ec72cdf5820c1ece49e3510a2a430d3dd286cfb5a2c8a5a73ecaa7958dc1e3b8a5a71915079d91f2435a0ccacb949d9cacd2e9b82a295e49c69c5dbffee974187aa3e20db41a2667a323a01c281e98f5b054c2e4bbb818f797a5e01e05b1d8d437c6f1c5671114119245d029372eb8035cecac51a41986fc22a2f3c6b27fb1c71c8b930eefcdcb45c45f1c3eb1cd9680a56a998938e5d9542aa73c6dbe10dbb27c4f0afad2e77d2634aa408238d5a751e9acd25ffa07b29ab4c0aa8bc2a14fe11cc52bd4a901f6fe5ee1e4c6cab29142478e4c83de2889de935dd6d3c7135d0e45f06567e33fad05c5e792bdebf0ca19cd6205182c417986a3286ed6b8af8d40cce5dae75615f9932c10c1701868de00b21fb7aba3f4e45c36830741d1502a2b97be6f36457cf96fbea3407cf354d7d96574a9a0679700a230d53799049f9d6f70a9bc2a457d49edb60905bd38892ee88a2afc3fa0dd39cb576033cf47f2d9ea70ca4e9e4f6ab2e1f15b5101b53d91ba3dd9251cb4eed1098d9d445e55efe08af264369cd9935af54e647035f32b549177afaf3961fc934b2ac0b77596b5de70c56db73650915937f012c7bebb11390f21099fa53e745b03b49c9b2ee91394573a0379c4e0c2d3a5c52f81e3160c66d70e0dadcca2151dae51da25aaa0154b38b80134bd06ee8b834cdfea52567f65b2f235ed5c15bd91b32313c1a05d51ccd18818bc9c41119e7c18f82764aaac8c145fb0f508d48a6293aa7a3557eb9113ba7f27ea2ad017b074c4deda824ee165a8f00e3c24eb160a7c7b74ce2dc4e6455fbce00125151ac35064262a36bd5bd93661b2a2d35cd717c8439f22efb953ff6b75bfaf5da1d51e1eeef6bf81809061ab369982fdf98a97d0254c8c0501aa0aba290884aca49528a684f14b17f77f2c25455a6b2dd4ff681e08d13837a0bfed743c370b9243276164ca137d171dc5102d22b6a93f1627214993c4393ef079164ccbf58a640771c3be274c908cdd266a1909acce27e091700171e53ccb985ecef4187997c290c7f5ba1a51846d5d0100c0a63804695c51dcbece06e5a0814e17005737a06d4275055a0b088d482a6cc637dc6c58fda193845657584da07478d52bb3ec623431ac18d487633ae6a611e90be9d107bab8dc363d618df2994c7408f7e1d9c1121e51f15aebf3166f2088530d85c550ae2d5c85cd834c0ae6c5b711c4e4e22d74e7dcbf48d4eb3443eaa1e12d2cb920fc4ec22941579f082be1f4af184dd556dfc423830fcda70dcd5ea8dbfebd505254a5d914b007eecb4051f747613171626f3a8ada7d0e201032726dae64694a57177b757f512407b021fb8d0946747bda9b08566cfb569307279fe2236d0c4d3a61397aaf1cf1267c7836f3f3cbf60c78d08ce7a454952125f63f9f15cbd0fd27aa4a58d2256ffddb05aa6d4feb506a20ffe9f846b1d7900c85a306a8751ed40966b9924fb6a04665e35d5329667ab5c7c32d1511303b4381e60104abf920ea7bfc972f70be16297e32cab58367ff8e2a955aef1bf5cef353e72606fb210ad4d723d8c406edb95b3d1ef87af4b2936a84323c2e63f77a0543371f4649541dad05d1636a5139c4420f871d3d36654bd87c6e1722aaedb69ac192eec761f75fce573e8a22cd01852580c92719f0f215f477523deb7f5c8213ac41b8cf55c1709055d3b6a6fb8c1232fd0d45ccbcb1f36843c091229233fced0c9cd6de12200ce41448001f803c41e73099f72d50b250d6631a6c97f61e049233b27c8c5522f169c5a8218b8c48c1b9a9841b02325f2b6cd1d80fefd6f668fa9fbdf587414e75771387e0645a57efccbc64667e22d272776a44e89141cc1a6a878b45ae74023703afc1396018330b77a05261c26d3baa84342859c142c242f52f53d6944581dd2d9ee9de82412ab4690fe61bae6003c5302c4e0b401b3302bccbd65843a087748cd707c8f9219be551dee5623d4b00cc7a978d59cd4136135404f39a35cb8c53dafc827df415ea6fb5d3ac3d1f7d03bc5862c5075e05967557c6522ea35797e964d41cc7515a45f83ae3773c83aa1a3988c7dc10d6c60119b68096ea7a42072731eba73814b238b7decf8d7f9ad8e506d0b25f553c4819ea108a6ae57972d417025e79aa5ebd547b29553b22e090c17e1cf66e48dc6faf3f0a24c37a54482cd3f331fd1be850abd32bfb9a764407fcd144e9731cab47495eddd91301bf3af0242e857beb8d2e35c09af5333386cd813e98b84fee3d9c0beeceb7f341971ba41986075a159ef0d3d7e162fb6358642deeabb3212e2ed6ca903da27f273afee07721e7313a441e0502d5303e268bed0e0d8931c17992e6423f597a1e661ab8f71ceb6c8d0fc51feb20d885192c409bd7b0c756e11ac3eaf82d8d8017cec21b52e35b6496687547e777e300cb0c4389cb6c24325e0920aae4002687f1015e24ff25476f3c4727608c24e0b88a2e7b4a70df1ffcc66c840d4d6aefae3103a928e60c6260a08a29cbc136680f61e69eb48b83c30a0284c87748fc9d97ea4db5c6c78e91182d66baedf978f5cc08af5ebe6c5d9650cdbd21fc4f508169ba65229cd999fd52251b126bddbf416620a8cc3bb631ce880ab3c2f4164e95e1cb6e00a35e0a815a81d1fef6a587b403e41347e56907495c59df1c043717fc6fa8e24cb4913088d20ec5b6ed28de2acc95ac65d8ef8cebcc2800e890a1f186eec7196ea04f478b6317a27e38d214619afaa8b9d54b5a253af62230e27636edccea7422290a37548eae1feae60e2036cce62311c29c24a84c4b6d99053a4d7c903afa9b2e2760050c6b905b2c36a46e9e026fe372341b7602a82314f6e8cc28a41bcd4ff9c09520760ea94e638060c31d7a25072348a232de7e6c63d126c0b353b7830364a57a0d90569a7045058ff046dbabc1b9293103017a369a44aa8529642049cded9d46a00e50fffa08fd4d533b736704c012f69ad6bfef1817726f6b21fa6bb6ba619d9c08105c57b13bfe1a513c883e108a1106ba73cea5a1892b80d07ac4659f994154a88b8ef01028f01e05306170996bd3e52eb1ca3447693c5584e89f20b36453d11526f89aa707d5da65621ae36a28da0dbaab04085af70023fe769760d661f5003c074470f7c69508f56f9af92cf751b4fdf128b76ad7f71fce169e67d9fcaffdba3ddaf880e64aeaf24a111c0c87e8aaaedd51d7b27b4502e33b4afeccc7e6496ea5952adb80a5ae5a178f1b6cdabbe93f32d15ff978134f7196301d401463d6616630a8bb4d18c3bb66671081176a38925051c4f5f5c075da68b1eb4e7046f51f3a14eed8257714e0a8c4de9544cf8942a40ced808d08233001a8751b248186abf30a55185559522a403eaaca26c68ce53c06862867cd590f94799a019e6dacfdad319574cda18e52d4d3b3834a1fe6cfb9282d3805582b466e8c05c20c97c9645dd8e45d78fd1e005803205688c51b9f145aec3372e66fa53de2688b0e9ca1d4ecb03c1aae679ea07b640e21d57546c23b5ab8bfde38e4b3775ea9308150a9cba3e2ef9479758ece2aa9b834462bed857f96e4f60b367c6d8cafdbebc0b33f48780e9aeee929d1d44dc86316e5a5ce2f22f5fb161c6863dca2d3610007d0d80b02fa1e5a012fb7d7bc07a6ccd81f155b72945a77883f4cf52bdb4f0c5cf82e0c65494e2ae5ece4d809319b8406a549b0a91a54e6a950097405a1115e4d68e7cf3f879425f04f47b3d466d752710c0f4d68c80286d7a8a46f62a0cf16acb1e9e6c6e1199663806d3cd903d8fc728fd106cc027b992b68a9c3592cc19f0ffa2e0d4f75c3a78292a183dd3bd51236d8a1f6e25302a10ca30e368e19cafd0d677c1c8f682faaf933d17ed823680aac9ee01270887d10b7b79a818e986bb2bb7dd10a0ac86494c941dcd05dbb28181f6d09d90fd655ec2d2f81139ba5b80af877fd2e0be8e6ed62a09c5e08d7bca5a5338fe727841ef32732cccfa7e7eee31c8b6027f4c7fe5bfd0e6631504ef0e117d147528c31cbb4325d81c10285ec0380162346ca6984301c326820228f80442414342dda2f32d482500273401c9afb3095f172f834123880c1444a7248539b132307008e353b33a80f74d118406dd2078b593b6f744361f0799cc566adba3b70b7bb389d59b84294bd3bc17de327a076087b3c2d89b0de0eb2313d326fe86d14f5bb2f582e1c8563ab3f64b6ec8ffaca352c82d5c3eaa398c0652b09a5fdd92e90dbcf2fc79173b56e0814e89e965c466544eb36af0b1e4bc01a213a042d563b2b55540ad7c6928146575ce1b817ea151c8cfb7ce0ccbcc52b344ec2b4df43122a1a806d77fe86ad165b403c9eb3ce7f3dd123a37b1a7b120944afb9e66f1e573c72413117e52aea858a1f13cf10ad9ff85689fdf2f2e1af939d99f71ed8e46e264228ebcc61c1043c9189f8fd583534bcee9bc003443bcd054aaa22db0c8c840b3a50109a49d1c2f79f2f5be1e4d0aaccb1d3284bdd41cc54ae9e4adf876e35a19547695952be2199e7dd8104eb9549c355e164d1af5c67b250f67c8d8fe8d9aaa0674eca7b4d7d573c6280e5218c01bef7700abc165d96d7f6ff8e0611d6cdc76c222ff855019a05f7bfe475334db83d6d9ad56f085fe80cd75407b096560684ac5e19b9fae89cfb548132001d521fdefe2be2e2ff0a5f4ebc7b5e49115ce805eb67004b121a5bdab8b70e45c475d3ee60bf1e33b01f031b76f8a89c6aff35c2b35a89190a171783ab7137d90ede2c07b10905dc317d07b1118834aa4b40f3f576a00da5e3dc3ad647400a27c0e3e95f960908740f246808e716885d6edc4928d575005e00b42380e663d3d3823cb7af743decb57c63a0b88cbb4c4a5462bd5f1b4cc0915111ab7ef0ba70a703a184f31808acd80e7c3fc10cc1a540001caa38d4272a977f3eac5487ea1d4e093fedf7dd82ad7b4de6879dcf6524c5362ca3756196b83ed8e3a431aecc77fdc25a64847f75785b8fc85e3317d554785719d5c877dde3616b15b93b2702b74192d7278cb7929851abe3168b73f31fb31e4275db54438f531acf45a0dab100a79eb4fe11050e8519a128d91c9c30873cc91d9ee81b21e4756363eec3b7e10b2feecc4c285f64061984e35e5e06be415d3428e3576b6db261737720a2c6d78dbb94778153640c7a21eef50bb2ae67ba1bfb1d07660fd597cae1288098134e1568ba611acf2755a964b3cb117155b491764c9e2c62d1138e4ffe820753e69a4f9cb05ff920e46bf185e16e8ba220a37b8124da3669b1a386a0619c07e3f3a03f84a568d1373b2f63be859c964bccb9aa17541611c5dfefce4eb5d33bdc0281ded3caa065c1a2e83323a3c4c7887b07a8e6f3564399b4a8e5f6e4a554b029a166a7b8c88e987ebb704b7f3f4522c92af1860285fe84179096b732efa713068bf4aaf9fd9fd16b9f79dd253ca3c4ad9372ea87b92fcc8985086be8a94efc9ca87ce2535e72a133d7c5c94cfa9930073dd90d76879f94918145fb3a680b0d434491c857e19698fa0dbc7f3b2486b0168255a68553326751032b12742c8ed6d9311c54dd3fea4683cb2d6548a77d323f51bfa212f271eec42992174335c1a3764e572c25be136d5c97d528b23fb868df00f7b0e7a1359a066a1a569b8625bd42ce7c49093ab119f142a984d07d0acb5c34784c4e82b8bb0f360057cefcb93812895a3ff61f8746f89e576c354d547563cd976fd102cb02b617a6f103aa597a9568221802c1d5de32f7eaa212a7987e5d98e8369fc0b94382901cabe7bda11c1e0f3a26786f010f2bb9a19c8e6d16ec5adcf7283114324251b6fa2b23e7ba5c3a7c8078c2d27c0ed2f69e76a4a65d32c19811912b6bc52b5971dfad11bea9b0d582a28e2839105c4455bb5e18227a17c150151a877c02cac9544df7f4af592d951e1562603e5ba8f33ffe9a06307914158ddf0adb1e8424b5863163b30bf95e94545f3e429256342f086be13bcac0694e3c58a287a01eb6afd0d56fb01c06443afa1f54ae70b1bb102aa2ce18043a0ef3af5c3b7b2e64a9f6921195cc1d41b31281f5c022fc97cc53a963024963394bd10b5ce19a9f30ed9de99e7fecbda8065d103190d3c3e2f669f56742cd1f4094832aeb76bae55964249bbd14bc96458a36eb9df492e1f82ad495317bfbed7fcbfbc3ac742873fc4d58ce28bc39e6cf8e6ff036b6c0119e44a303bf37b311fa97026e90e7bc676c9c31a579801e116388ff6fc581c7b673a460041e5f67192a8dfb59593e3c7a1e946003f87834a4a424d53ac31487f664a6e23ba313006f6e02b8b85c4e71e85ef5b1c3cb10888223b2a2daa841f1f207aa8be749517211b237daed9daf58c6b556f7730bbc7b41b30617875f91de932dc80374e576836a2790131db99033370f29948ae16730a90a2ecf02d40980be967de3b6a855cea56e09d7d09f8c948ce3677e1f28a9ed399f54174e360e15ff451a580fe1c928057d60bb00f6dbdc4f40b194c0299c83af540dfc96666179065ab7a950061b423b570a53462e1b1b34f722ecb0913d0e3538a8b390b144eb0375ce75ae1c6c8edd09d6393b79737035ddb732b0f4152fc0687c70b621c77077ab1754573cb00d5c50ca529063242e97c70ecba8752092dea05999ce42793ece5c1d09ca62acac7e712c515b11b008246aa9647fac6a1e4cd14a57905e334f8e6b33c0bc7d19db4e630b8f82082b60941f2003f8cd99a7ecceff90e967e4a1c6b331acb38f79b1656c2df205cb62f76a78cd987b510f5070a4b51e048cd4375b4048929fd25fb9175b96855058c415cad425e0e8a15baf94d141f848689139700137a0edb6d4f9511f5b6f79bba8f7cf5b0c19f6419562bb4d95824e4f43953b17109077377d0bbfcfbb70ab88579693c5079b50fa8c336b84a11121bab47ff03d15221efc6091ddb4768ddc40f3f589a82619dd18ffe889800f0d9e2ba446761473cc5ac5d467eb40847ca4a507cf6956f85a72c39723d47c2735d97e58e41fc02336bf2d0518910916a2606dbddb7eaead09a3d60b0d98046e3953a3c88c66bc032015514c734f0580d2491b3d7005c274340b6b56ea38ad1770d062ca8871c13318c84ccfc58c3f06e6cf918681f90d5c009fe82565fd1381aeff8c720111d748e4182a7eddda1909439b59f3e43a1345413f9a6ec1dcceb41a818e5ca247cd5b6e8705b2cd0bd7a7c555968ce144b7c2ae647b6f34e8ec64fc886e850ed2b84507510d85c18691f74c0f4adbddf0c802b750c087e447d587eb512af4ff50191d96dc466ff4cf92ad3cbbb29b958d835b12fbbf21903e6e7f345b7d83bb8f98b426d27d6d1565845eeedde10f67a911943124c0ff6a7a7da47b52028c00cdf3ba7f0063b30ee682f73208cc2e0f53689fbf6672300ff83b7182a6300095bfeeaf33ce6d9146180da48190d7c30674c107c69bf0a2afc170ed44247470285c30836502ae924e8d1aa19d19c78f2e634a0fc3664ddd3439f29d7fce49178a1768613370425d023c2bf6b2ce36167c86f989b6cdb51c4f7836e719f5b911905a67b7e7f3abb8af55378d05ab7e839455a33266bd5b99ec9c2a8d755b53259584aabc400879ab10f19d95cc93d823133e729ff8c9defc1b12609277457654d7f40f60ff87bbeaf651265a9dc02f05e460cbfc73d5a80486d54664106dd4e97e29dcf46bd29ad47fe9b65ebe74e5d0a4f79079295563e154f266ba86ca38aee6b94d9a25493cf7568344668259f5a9b816987114dad41107a24935458043a405ac0a7789760b21aaaa3b27c62c9d4ee72dc67e8fd4bc0d43da362f5387fda26a996ef9993573ca61b3c0d09b46b06f3a7656d3e5181d376956463885a039b67e41b6cbcf7e2ae0e0cf7a55cc224a04b8b04e9964fcfdfbf06828ee47f92b8a42f8375aec202423a5f2c4fe85344f144a5274e8d64b8359c878a82eb11e77936990cb158fb0731626cbdaa97b18cb15b6b1940a9dccd7e044c26773716c40a9585415e39d6a87f7afd78947faf36983a5e20dae98769172ad1d0c35e1529db503ebdaeb264e60370a8318999f6561a067a2bc9495f7914a6e015d350be4d40d1d4f328029574bd2d8f22d3eb00235fb7ba91f12472323ddc80d386511660f509bfcac2bbd7d16e5a42b5c671ddeeff40b24ce97285b02a63057dc5e635b6924ef009753a728a9a5e8a6accd9aa6be00ae8e236d94dc9505c2ea5aa9cb70c2249f42d9323b032db001193f6c849a3811778d1a44fd2af62cc9335c267f26f9c5f747d5473c6f568fd8323f16af7b0a00cf327df20c255de8927d09322a696300b24101abaadab873f6704f8185ea79ea60673ea540655b0732001b6c14201f4ee46a8eec62996d064326e81cd08b5d105a3ff7fac1962520b96d95ccf0ed00350f79bb2d5c7b301a4601b8be3156fe66574f96f64043b0ebbb550fa41c092fd2cd99be3aa808f9dc94261421a91904c0990801d83cc2f4d1e413d67a654cc32a18411d2e505abcba267c78f2da5b720315ed2582c0af33472e4cd4b1af5b1c5d2b9425f6588cb36d12081ec1b30d502dd1f74642eafeb5c61dbb13fd0f99fd7cb0a5eaf4636e8b6822df31c2d0fab0823e09e7e068961eb65b5efec8422a03a89ad8903876ae290074f4d6e647c35ae866744073e985df6379b188408f596efcb734af6d090f8e521c4680eb418f1ab9278158a179c41e1f5e079322eee61241eff277a499da34161424e088f018599d26e0cb8f15a99c20aa6d91b5a8ab133d609ddf8bf6978d9b669f69b65bc8c71e24b93dfee4698536d007fd6ff4305d55c8e9749794c005c2255b554f5fd50b443014586eab7a364d38c5bc7d58e949409b6a9a06d71b0845ef35772fc34ed5f4a825b132d5bfb2566675bc23080d0bc1edcb7f71fb80f3cc88fa048f2062f1d6f2c91a233f9bc7b7b3643595c555bb4685f9ebef289e4ed5be3e4a0ea2df843a8fd1a4d804c2fd7e54857bb10323954483ef05e37fd7df8fe6a0db48e5ec89066da212d41f082b7f22ee98f40caec97ca9b900ac210108f8654de1e03473b1ceb88ba394ca5771fd166b5c5874326a90ae9bbc286f535fc9a9144ebcd7e86e18a10bb3276182e0514c25247e62969a760782c18b519cfe179da7c9c1407e12c803b9ae2f8f8a7341d45431d438bcba416b500af57c52a4024db43e264f901cc58971c1ba64a504f5a59168507520d5c5956840b141b65b02c3d4caf957622abdaaa5010679a23365138d8e068b20feb4025597cd42e48ea653eb2d42bff43a13957cea87e672f31a6b960d4cd12f9bb8aaa229582322ffe794a52f204a342b6d4717ef1f6b6ec80eeb2968a9be008be681fccb3b6d3464b873a20be208c937c9f3aa2f20a6b53689f1cc79855bb20cd0c66b1599f396d3e8680fdbc829de18b1778e3ab96f300a5133b49ab75b17db682bc2c204b07d8baa99b2256281c36f8a512ca134b16cfce89b863fa4b0a446f45848083dc7f2d866b283f468083d90495032e9f9b8991f61d651af8cbf141219cfcc666cc77abc88fd9a359cb169fc8cca507bcafc398c1b0c2eacebb42a6cd623e4397b39b73883871eaa5130550073ec3305f3a1a57a3013990e9cd786e1b3b92609fe893264cd877f000e2f10452dbb80696874e06f2b2098b880497421ae1881f4df72f3ab459cffd935b898c9ced35dde2357031510b7044f4c810ad0257ed97b13f9f276786f2a7a1508f4d9c5949e53ba3fc5e91f05ee43ea12d2a6a5da90582623f8c4f945222eb0514be85266771c794d1601795b1ef0ee94304432eb181e8159a93062cdde594658ab96cde40fd7f2e5387cbbe398e28f5ef2c92a1a03f168ed37074cecb3e01ca5d3d279137bd77849c70913b8c821c29616a154ee8b412033ba2c8fd601678291be8e0f1e800dadb78d97e8caf19a1bff07bf0d18c0bfd65471b816e5fb720403c1d6832c4e671b5550ad4875c5f92a5c900039b9964cb21c45bf1dd2d23942d6d8979a596fb9d1f083b8527c4f91c1ae100b971698c26a39f684374f92b0361e56aa9ef255591739b2e95c8e9721e43e42c42e171766c6809ffed8378494037d2a3c8d5d26d427b6d88198e3f1c77afed139d46ec31be10c537af69b4ea6005f008f605ebd50b750ed78618072ea6427cf9850705d8e15220573a6da15cc662537472ef78f15cf1cca0a6226299dfc1e3a9c6087d1f40c15d28dafca318ba3ce7c90f09a7ea62dd4b932784255e477553e2b11d1e7101a8526696cc7b68a341e0e3b6cc51e4c71c8c3e0f8860141df00217b784da5848185103e58bb1436f54236477f52e23dd8d218fd50884a51e2849e4d2b373fab497f8f1104cc79337e9839ef7f1be87712c41baacdac501c7f04e5ed3dee9a9811d6947bc6b48b8cd01419118eff1e3c080e1e776eb9af92648e59711e52da58248725328b18752a12d6f24b67c96a958c904396eb6c130a086c15d038796b7c0cfb00ca4ad8aa54857f8c70aaa41903bfe5cd2bf4d5ddd4dd308d5b860f51277fa6db2e80341d80e1596d5d7707b2dfbe7eb86559073f1bc04b547034f1bb424d5eed660a55b39171533c8eb855dbb2d9e1c4953c10bcd7935f0cb7dcc91afcba2ed9f2d7b8c40254c5da56d255ba812372305c5695a2f89b1069f22b8c07c301117c88376b99d9d9d88bc25b303cfd2de1b33620558eadafbbde0ce60adf8e1d9ac40ddf423631cb59b27ff8e2a5d399239278a96b80405ad3c693a81d5a62e855991b79f593e21f5eef32ad90b3884ea7eb3d985b629b4b1e02caf17cb9bdfc144710f33a9fab8b9243079263725624d1f95a22aa2e34804589f40889f32ab038e2b8d0a878029e29b767fc34726da2b04e40f869b49591a82656c021968d222c45d3972ab0fc0f471a9483cbed38ef4f382880d817aba8ad801530457c15dbecb0f599d21939faadba36a26f6e2e986280bf1e6833ee9869ae3cf2e5b9d2c3f6441dca014d86ab154a13e1293a4498b84e307ce4cdf124918d6b9b5ecd2fe84200872f74f5546e74afb9532381f82fd04b9fbab513e1e04c3030ed7363b88ff9b3b30036c107db1e297db2e8cb723cca2fc8656a35751a6db3128bd3ca7ea862f3d01e05f66fc79c7312c159974d695b5c12e26f10986b47c89f37929e7a945508b71b4d765d8667c02b0cab3fc8ba6d2869983dec07ee7fc49c343f36b3d8105f8283ae3fb980d317fa7dba838acfa37f5d7b3e7c78efe359faec2a2de3f78a403d96e0d8a725419ca020a10d955a18f585af487ad99dba191114fde62919b90da5ba7d5cd8ab1612b05b1ad78e365c1a4d71d2d7a5ba9cd9951f0f4a774a5400951d485d36577cac0a92e4f40ce0203afb80d45888ad2f2e9f08e526eba9303fd407bc393ea9f3d362ee935a588c61daab27a0ac4f81100e68663c849e0f89e85039def13970094d8f9828acfa45492d50a62f3397db0d06e4834df916d6463efa911faf87aa58ae8dbf7a108e57cb470238b870f109a43202a21f085e12fb3961b7ed7b857da9f481eb140a1109cfad61edaacc9779fc3df9a70e87e67834a1e8d6544d8466b463aea4ad680b3b0942c22dfa02f487544076908bde082711b005132d5eac7d5a2d07a16515dc9057cfd98b02e4fa071b8c9f7c3b789ccaa96f0307c4eb727caa7a88e5f38a58e0949cc9894076b63a4df18515dc2b06f4c789d0c8592ef1163d1b6cc5f95e0be0c0f5673aeea38c6fdb0010b77863d48b466700e25975960f6838c1717d6f03b309ac80e29d6819f45e2486cd709193f04e1ad1283d39e9371ebdac5c72dcf54778d48865a099781eb9c043763ce5f58f02eed11f42151083e8baeca35b3d08b5c02973e368bbaedac239b61d63432babfedd4259c2ad3154b499076739e006f54d41a4fb82b3f02afe9610e567912c6d3f18008bcd19bc02690074c3c9cc1d73b107d7a40abfcace018ab8e1914dcd076823f712a345f870546bb624ba71629cd2fc908c2ad109c1c43125507efd732bffd97bc7e2b782722769d682123342061795f0f11f4cf13d18272fa45db93009369889ee210ff2c091724d2389ab3aa1bce74c22b50996997d20446d922d497c2fe38bac679ce1644111114c127aab1a3a3593b15a16aff36e9f12de425a800442e5c7c220f3099f3e0609bc3a5363114bea908dfbefdb2789e71641f7299e32153371631aff063a7a3081ae44de06131218286929e7e11c95e7ec8ce86b1c4361e044e07b517e6d224c85f2c68dbbbab0bcd230bd5038842ec31ad0e28c36f6e1c802e013b98ccd471af9eb14912eddc99839f814b4c9e58d59545c20c4f36232fac07b5c0db3b8393aa28da48c581197a94d7e22cb1827e2d33368257da6d9da04ceeb699a8b0a4a050dc2b0d4d619defb9f472c273889c2f44873e5aba68f372365883deb7641dd576807ca6778eed070dff6e1dc16b915f6715f931282e8aa6b1105432f5a9c1a03b3e05133b41c5e067733947b699257b4f97d59bca95fc113409763bb5eb2f6f70d94827cd9e0fc71dde2ed4a270d998c944390536dc943da8920df314681845a4fa8f1d1e300f0166fe8bff529f61dc57e241c74ff17dcf19864d5e99238f4db39307240090e4552aaa8573096ea7eeb275c0e878cf5914b1886ecd5a2d2b53cf674d46376c12427db66d5e540f350e94df5080737d2c52140bd2930b1e9edbb46f88113c133503da547110dedc47e019f792e37d7e67cfc0e142dc4a84fd8dde008fc365367ab23a60181c17f32ebb0fca60c70870d1a6fc5450b5c9bdac2c355bc31c147bb3c8e16896e1d10e38f1157df3f2ba01fd26d9aa590a3077ca3fc1dd330189a1830105293466c9184f2b03f5f2af9084fa54c9cc19649021099e4b5381b36a026ac49e5ae3eb891432c355c6afaed37d326f21209ed0d23b537d11971103ddf60ce8084e594ef54e88c7709929aaba0baf47e058dddd99a384a5b257d4548dea40e4911c4d2dc6cd48bb91762dd92a35d390a8ac698924f0a5cf0357175ee2fd0e19175393bce3581a4ea88ace681eba6d821d89270a827e9dee8f40cb4f4e8bddf82fc50e18ffac78d28177213b282074c3d88390b58f07c96ecee28e259a3a6cab4100a1e01168f062b39cb4851fb441237ae47a99cac6dc220094b7fa7ee246ac285f838ad409fcad21cb1a6989f8f83529e4f9cf3de7a46543284c0b19c91d83509635126057c8bbe45d6225e6e3c85b03b7c55dc0798c37d61576d30a849ce2883b36ca650d40860433575e71e5fc0ada8a96d319ca493ce52d0a40f8bc22c924997781f65ed0624bfa4526f99874bb53a9bd4b33d99312a94d577e807dfb712930fdd244147817c4fdd8c0e97789a8c605148c44502d77b6eaba9b7715455da111d04656ed058e6dfb6f41ad6f8b439a11b249bf667743b64a2addd233dceebcc7b479d9d2cbc31095ece4996acd20005bffe921d4b6cf2bce3df4bfef40dfa715cdb94c8768c41289d55996e3006267800453fddd62437957f6871c172190d665bf6839e61a1cd433e031afaa8267541bae1dc0bea1c919ac346919d21f02268355334b00541592444932551606f06426ab51a8de684c37d86cb66f82ac78eada9fe0b5d38d36d29956a723c5f2aeab2e74c24134742cf32e10e217a35eb25dfe8e5f831c678b806a025a02d61a789160a0e90eb7ae4185cb81268a00a39e99f73b5806c0ba71a41ca1984de7a47ba22dbc090eb03141c8d6dc439799f13ea685145cc04b48886b3178a49576897e83b4f6546f31bb42bfd88ea49a5419e091b1b3d68b53feefc5eebb277a102f61fce2596de4295136b457e3638dcadde4472e255451f288874a43b532138759e3c0213358f0030e96de06b0993592b9f857a20ad0c2e36e61b2e2641e6e9f790a0700f8c481206e5ffaf08a04380422a695346ce87f5bd5e3959e4c59256b471c89aee1b13228c8bbb60383c5259816edadbbad3a74e261934b7cc8c553dfd2450a86e41bcb98265d8bd8c7d17f8243fe97a6839ad53b0b0e148077940b8110a252867ddae5acb9d706ceba685f4f89ae650810ac69e232bc4dbd5c4cbf4d5c239b8085c22a5161f0ca2d50f280b45424d0c95320232c4968e0e61d4749fbe10574edd820f573e52762c717b29167906ee74e38e75a4731a2a3b573d3b0f49a0803445e83e18dc7f024135e738d04f639b63d5f6f76423e3a52e7faedc2583af751b96a19d9686adbec4929f35d3b06df83627f0dfa59e7fa9059a249c269cddc88089dc23f49281b9982819aa931540f1c872a2177f470eeaaa9e3b6cb583d03fd38ad8f981508f5bef3373440e37fad407e5c35bfae55f4e0fdfd3be488c1d6bb8eb091f17f26b81d6071b7c73ffdf3e84f4f42984d8a3b541b298163768223d5fbc6637f964f1608926c2536164084f404745ba0763cc94f70b3adf88fccfaf91387f83f97d78bf6f70015dec83da8049eb25a512105e2ff245159d8755d9f1b03ba1bb6299cb3c711e93c46ab3fdacdb78edc65ef4cfa18dd380ea3253ab7cc0481ac80859fb0ed05dd2c9e0962c0de70ca5c8738c37321d044907962b222d520776db5d93fc1eedeefca3c32db6833ded4fce0cf1d641f1edeb46dd95b2a2572df077ff0574691cc7bcee098eeef583cd151698221caaf7b10f56fbc1e2ea7d163471242efe253154f0105683526703ac0a49b206bf57833dc22ab45428bb455fb2a7f916f5c8c87a4a242eb4dde3c870178b0ef9f0b4a9dc56baefe159d7273783007c83399779c5d54a162244aeacb3945406c7c2621247969b501b65082460d00acde7755bc1af4ff466649e675c3d04621cc3977feddb125752370d24341357bf2a1a029d6c66294ae8dd3dfc00adc52478f46506234d03bfbfd8b04d99c664d80956cd2e2819305a9d77111c5addccd8926c0e1f42b7699458a153c5c240505511d917249c8883d896ade446c84023c1ee026725a6fe5cbd262a361be7a962e24204418b15c4fb2972f402694df1dc2b743ac94260664f6480b4d20f017d2e5ef6d58057eefbc51abf818071a922b11f9aa10210c505e323f42ac486d2451effda11b2220165a77e92aec734e21f0ead41fda44d8e213ed10f28d21dc078fbca40fdbc95bbc2983af95542f63ca4fd5240c166fb79a709694e5a585d48efe129956973adbf2f7c79814f365119973cfdadf952a2d4ec5c436dac56fdb0460a20521f220def94752d3229fdce4cbfa9f39e66fa9827102dfa6145e412d6ee6f25db229157f7d01cebc86651c7c54ae26ac92893664ecc3beb1ef9f936ffa5f659475b5f2e70fe66a9bcc968b025f2e9a0919f5ec215c5862224c89a80f59c55ad7a82872602dab3c5c7994829e414d74c544948a245b3557eba52bd74fe6f41f6707053e2bc2f79702efa9cd3ade7a7f71549f6de5ea08bb6afcf940cd538cd70809a0806c2f380047f94081adbd6d263df7a3c5571dceabf1029f66fc60845ab1a01bb0949d8c93e281f8649db25c6b9bf88aa9d783b5aacc46c9349f8c78b8c2c8b1269a2f8b06e6abf7ce49b7b1aa56fa069c59f5b43412a0aae13a26b6f69d0c5d63ba6e4042950964a85055ac7b60359587fe9c4f3b1929437bab9066f55d7597145afdca2f987c75286122aa650e8d05566494c1a2beade28177d6a2871cf0eee2c746aa9e774c298a5eb08ad6eae3d5566e07c1e98f8c9d1acace6764261d0cdb950a28958a73d72e4264766d9f3bbcb6608ed97684e79a982d36bf2d1e8bf0b7fa659423f698d93e05f180c2607449c0e344fb047d4387aeec58d965ee86c3178f25855d21be557a4f489ece4c8510816638ee1081c007079bc70b2c4b39273f54ea4272df003e4bb18dd4d135e0600e823296faa3647a9c133f01c245d37cf21887ca3f2062bb15370562ca597a725d9c70757b62f024a31fbd63d01ef85220ce9aa129e8e689b14159a5f65a1a822b0621bb545c398c2ab4d549535eb539d78613edf94a73b723695c8f53534d5137a55b606ea1aef64d5f73535912397e0f815a776acf6e58a3bd72725c064983b3aba18efdb131584623489d80afde0ba4d3d9869727702fd623e4e175052a972796a7cd6fd76223dd97c68249392f1d47c3570b9e1004222dd0dac69bd5b086cb83c2f0280f14e65afcb21d97e088c17738360c69484cc7c9f4d4670d365862cc4a34c1323eb0f7fbc34b4ff21ae8a6fbab55789561c63dd58993f62f6f869b3a098dc7f86ee56253e5e808c87d22e0de9785eff4cae4c6230a87f51305773aef1bdcc795798a0e13b59022ac2bfd0d57c06e791dc9d3d21614834bac04c329860ebd70ed13cc5a38a8d4c5b8ddb73d93e172dceebbca9dbc46c69cef58039f8dca9d3886d498e6b3c0580193cd55591a519ccc3e04ccf8199615dc66669a4195297dd316e2cd90e732014b7b77ffe3b35f570d5a4cf4307516cb99aaf4db081c04c4915a7a6b07090538fc47d048023c6a2ac5a9ba4140b7c1a622fb68ab41a0ce7c0be4105d0a9668cf9b7a76bc15ba92167f82004d02bab453b0c3f80b1436052107b521134748f70310a9afdd5cea95c10ab8ac5083d2f8501f3197cd261272905979b74fe927ea78ab11b938450f3038a446b231b5c1f43e92a247e0f3ba76e4241ed982d850ff4746afb0ba271691fc6c7d8630ced105b5d0347e35a8b4c4ae4db8a63cffce4059a2fad7464f803875118f4e9e163538c3b7c3a502df9ec0121207abdbb87afe7caaac53d6c209a7e7dcc2be9dd6e1651d8aa6e82af27684cf496a48c043a0f8e2506cba568922493dfa434559e54240ff776b09d19b8598e69d8a0315d080bd2a19b2bda21afa83ead68d1594561544945868ae97078bd8c1e885d29724e1f7c020c73030d8da0fcb18d7a8f30d4cb4c5e3526ad62de88a75a010502b2d90d040eb7d1959186c55d3acdc95998d0b9887f493f9534c31571acbbba7bc4048790b3e4010d8ac51f5d9339729d49897a376e167cdc116fe3f206131e8eb30e97d5df87f8d1cd560a477c653d068c1f9416ab61abb971a1865af4ec5b09218841caf5625c88879322f7af100ba6b8247919b2d88fe800f1ff37846b24c6190003f2112acdbbd1178c3d193e6f7558a128ab73620f4336b0c80b430a2b68d582ad1a8880a3b8197441501c612eb08ca05103ad39f1a97895aab068485e9e36343cdeafc18441907f5297c347f1f8de6094cb60767ef469e57098f89408038d12e4ce9ec5ef992234021260177ecb2e534758865a0e4d9fad31a43e6a0690e4623c33653b7beb402806862c509acdd65b1fca143f28afae5acab4750fe76e59f064b87b6409b06241788229b823825bd033bed6c53f6b1d1e1b9b29eba30837771ba05ad52ba026fdb30cc674b33ce6af96d894dcc7d5809a0a36d02ab6303f01a340632fd516b806546f1028ddd6966ac3b76b6543c89a74e6438bffa52871c7a9a2d7f7fbab7f156396ced9ea7086ed3fa96ad683970d6fc3faa9ff8530d5f9ea7387b1cf8dd2cceca0acac0c7621f2e01491861605ef5e85e4ac8689e8f73d9083d3a620410579650ef317232320375e5b7ce6031274bc09427b8beb17b1901254d340baf0f8324eda1b36047a5a91e6fe923357044638c47c0d87959c9f4a22d11d1b1ad95b9fea7e65a6f6bd46da69d29b2c231418764520c64f4a34b32eb9bdf3b4c2097c21245c5b1b2200bd43bc53551be40bc64f774c486c9859768792947b865088c90afe182c7cd360854108339cc05f44db32a59eeaa49a971fe2994600991f418970f63de3e84723007200ee893ff2f00547bf22f8f263ff1c7a227d688b7e9e30b77d5928b1e9200bb1f1f8910dc89972097cec87f7d525d18ed31ccc70a768a4440f1deaea7fb1a13b25f1bf6dbee89fa93fbe0e20dc604d4726c19ab94a131783963b530feddce355f81d72535bbb841510357d6e9918510d565af3779c6c53ef8c91f53fb9dc6d0005e3e84168ce8ccd9957022d5ad9b781ca654c360fd160499113aa46eb8073621765ed841d1132810ef4f9b6f8b174aed7e3c94fafb05a724cad8d2449f214a248ecaee69394d9b29f27408db284c5cd88c50bca6678c5dae7e8c5d92be2b0832e713414ba9403f54184523ca6b602791c129dfdd092774251f2141b0dc8dfb9b4cce65c69017c0ff419fdde97f6a981f5c00f4416565bb28cf8aead4b3a1b1d43aecb94e35a6b75e94f98b59589fea14b5fd2d1d9eb72f36a2dc85a85718760e48d199251f456068fd984ecf3d533bf6bbf0f1e64d2a5d6a39be3d41155f673f03a170b59265ffd2fcc5159a8e5252dd3617665ac9c1f2b58341ac65a066a0d1b796a8d59102c53dd45b11c0e80e7d7471e3a4b51c9c2e3210e1212ded8a0a125f438c600c5a031052218bc6b17d83cd7562fed537725d58e69400eb413a7154bc22028c7a6218f01a5d98a0680787d1ec1f2f48e96d311a52c497eda3652fac13a93d9df4bb26bfc2ddfc02751e9ec418a30e32219768414af2420543bc99602df574b4eb8977e7354408e8cbf4b8345c8d8d32869aeefbb57a47bca929f9ae6310aa2d4843ad83e67c802656af922dc7bd546fdf4cd632edac8c298e4424aa938a4d374b1f80e4c9a41aab2539694335ab41032d650cdb1571be7939571763c4338a8af20b9411bf70b17abd7beaca3041ec84f284dba32d2315e76d61a5222d303cd1fec0b686d960d5d2a7562b4e6a00c45bffe4aee6c0f8d8fcd4032502a0f6125df3444d1253bdf3c3ae1669050b43cb7a7e7e9ef74cac5d5bab311d77a0a3c7a42404fe0b43b32fe5dc59146ae565414b3a92ce41bcd8645896c41cb67fc0db55b647029f3f78a52d1de312112325cf6d40062bc7b287e6b85e377a9153ad72328a84cab86dff10875b80e7964952cfd8e0ce9a493626a969a2fb7d06cbe165db2011c2de2b050e5a974253baec2d567c138dfe8af01c22d2a2c6d811bb8ba219ee985e37b74a6c20073497974747ec59c36700f0d4ad77026d283106de1d021ca879160e66ebb211e5ff088c7edfdbedb93e0d4ffe9f1b94f196227fae8e80ab5aa51da44f8c11e9e9dce45721f8d116ab4647ce13da1c55ff43b13be34dc78f818660f1bc73574026520d865794e81ae0ac5de9009d4ee09a897bec3fb55270fbdbde52f5796f05e84ddaa0fdabdf3ffd720d1c7409c3a6b41fba240cadd070aaa057f3210f2b5e627cd14ace510cfddee9fd6b97dbc6dad6d9270c9d6b259350023495a5b4b877f04b69e36d9f7fc5fc535fb7ba078fd032627447045afea3056097391f60739106e9d37b98d4fa326264f78fe1e3a57eebda44cbd9b6c7644986e1b00fce1e771d2c47e3de63d21e7df36521756e1c234063ef8b03cf78575a103befe90f5e4783d56206be9271b527b865b0470c0bf54d8d5430035ff605401d8ba6fc1ba925fa4f2d88b4f0fb2dfeeeac085436d58bc4699d95bcadbd02792134640b95fc9cf779d27f141557f9b3873a5ea5511a9803ebe2a678f05dcb9ea2ca3fe622f2850422f78785c7164d5cb6cd67fa9f94237bf7e533b409412287723b0a93580d1abbc52976648b360c99d5a522c7768818c9655674a8bc957588ca11b538d991fd158b4e653713f1691d862cb91ff803047e7a43d659adaa7eb7b663331257d65247ae2ccf292492ad2da0c6dba5b20e15e08d83cd3a7d1d1a4666ba2dbb51cc56e88c6a882322916e3c46017237256a0459da06cf082bf6f45e57fe68536a5e7fec558285e4a0ba9327d551deac6b06bfbd3dcfa75cfe26cbba403c243989fe7ba5d1f237bb7644767e5d3df8ec9722e93a6c7be47d80cd88f4100ad8eb9d721e5c7b64a598c84a540a60d6c2b3b3c8eff53135a57784627259cede46aa3a4b07502e95e4ee9a76a2e0f7f6676981ce91560b8c4b64e47104a7c96d781b5cb319b33cc4fb6087122cd08382a08093619728073654edc19fb4cccf7655e9989504abc77fcbac9095af27a17fb0ed563e78e7ce81c1e7f4d4e5eb8fccba72f1bca241706d5173e2b9db9faf92a43bba9309ef0c9ea0475bebc04952b52c47f770b24914f55c0bf9f0216cef75566e50a65e7223228b1c413b9af22fa2fd7a09ef755ecb158c1f0c7af225945e9cc07fc2a76ea55a04a821e10b702d41971c7ba6ec4f954f45873a0c8585568c229ac200790302ea21208246d07e94cd32ad45ab3ff597b7b7df7ef9ec85a480e9fae196b6bcd531e793d4ee7e8813c627855e75d67bed75a651d819b219b6add6271b43176935d2a047c8abe4765cf891944bdf0fc91a00d26da3fb10f67de6a37f8bcc3374f7e47ae4eec2684b8ad55833bc8f0dee9a131259c4a5b276cd10b9893119492a047ba3c5ed3ace569c37734e341eb82b1349d9510096d5c07b735bd9ec54ab4df63c726030e29d479df0e1e2da1b26665065ddc782ad053157937f4ad0774bdddcf035a505fe9c62e64f32e38aa1d47587b89b89b362d754b67f6d3844a607f94aba8c75189f661b05422c40f0c9f843ce2f86327573f279f7720815be8a2d2eff4e7d14481f206c25dbc8fda1309f30bfc13a8db84910c2fe6a7ca22af0f009964624fc337c8a091eab88132f4b3321ea273411f239004912bfff746d2ec2b956489c843466dee9224eaf1929899e2da0ab937c608dcc07eabfae6fb85ec913c34ffdfae2876edc7db46ca6d74cbbe36d087db90fec8e31a7441f7370165e550ece46d65ef203d0ec4b341d71fdc5ae940cbbe11aae62f90697ec72795218ab0b8ad4af9c8c3ae3d8b4088ecd9442b136850f46c3de7d74e36ff4f5b6a10c2e619d89a5e8a4a0f77d699ae5e2815428c2f6f563425b60248085af28c4faf51ef8ae456c5292b50a0f87b54b80b9e304aaebf6697be2b66f58a9a768f8ddda3d17f521110da5c28e5241ac651352184faf6de8e93521062bc31d6ba7f56070f8b1f9b70ebac82312cf15c48072ad08a7d9887d3a810c78c86dd63b1f551e1776af65de0bcc4bdb031bc8f6e668839c3c955d830c56f8877a4f068d1367d27cf54c6f17bb927176d0d9a21be63db5da68d8721e0a104fb6b3d3ce73ca4d80224fb4b6d0b2728736f985b234e70d4480ab8ecd9ed2d994ef2e9da60aa30d25cab1af88ee8a467c966a71e8d87b81ae29ffa3ce5739358dfb7c2e60cb6fa97d9e0e4a7bead943b2b5458797c557e593e133b0c06b32fe713846527b81e1b5d9541eb0aeb0c69bd1057b56db1deb5931747b79da001f6b11983a6773660c43bb001e89f16b588ae6ce2a2e04729da87bd7747a0aa8bbe8f3f28f658b8845fc09c6bbd6c845c08af35d0515d66f07aca39aaab8d6ba88a2d92e484a2c4ebfa01437ad5315ea50cbd7be3770ecf4afa8134d6decddff2660e7413d24b09f40fd0c001a01bc00df0ee7d3fd04c44972744d66bc309553e2bc9618efbb51925d89f8be04ff0d8424c3bd51395ee24306a87e903233412f11beb78c60755862be26a3660e44125a6cefaeb4735f2ab30980bbb0767574f178a0b31efc783631ca8a4164052204b0a74178ce11a50912e581638eb18c8c0c15095c2915302bc0cbc9671c0aeedc728bd01648cee0f03a1e3d0b3fd21de2ece83616d3d86f92bb971f79a490c9a12dbc003c1e02b52185257062d8bf7550913f86cd3cfb592ee282c2ed71f141494c73b4fb49c3b0b63d18ff46a0636225481398b872d6c8c250e90913c91226344b94e11379e3530370616b54e1b8aa502f0a12fba7533c9e7fa854a63830e840b43241030559f412fdd2f9afd309aec859aa845f3640c0b810a38fca92b852a4682f6a4b07f0fbfcaa52e2e1e3dc66f81acd406d59e9c45fe2e961ede3ba82035fa439ebae810e57ec23533862cdae1cd03a8f3f106bd4ffcb24e1ce1d69ab1a68ef177f50bc59ff62f9b1d26b940030c9dbb1483e8056abe89640621f55cad3591e0190a8eac0080e5f1518e1a18f60c9b2504658ddc9e5ecf5dc6c6d6340c828326e16984103f6a00450b7ed289b58bf4c6b240aee2b8d887db4f415bd298efd19102062bc6c807e0f74b211f1910ab795e0602195f4d462162dc90297e07fc3b39f2c506a9639f083746820d143e6ba0cc7a5cfd9604dde35e7b4d48cf751066663f96d2533769510fb7de444b42ed1d4eca9317c2312682498f76a0c7b4ad8833ef8785ce249352a2d9b4ee585566c902ace00975219f332e671b4c205c2f5b47a4fff707612e1834e068f010d4f102692b815d7f64464c62de47385f7be8a7b6dbb1156449381b4728e8e8d7bd49061aab1402ea1b0418721968e62f314e66f2df2481bf7ea042d765df79f80e3fb264dfbc401407199c1d203c08e5fa7c5cd06bb76232eb3ca6db357850739fd82cc53806848021a4bc70be04083969502f2fe636b28a79e37de672b64020fdc28e0043547c4bc33de3dc019aaeafbec08da0d8196c6d83385493d6b0b3ecdfc2eb11b829923e75e8ab06c207d5b71e960e9f9917adbc1ae265eba1e535051117aa439c41952068244bef40f129030501c132c0452dbca3dd9093a7effa9891fb3a6775f5cc1f2fff03c7f9cd4473bf69937b7023f80efc9daca5b822b208cb0be24d026d9b51dc283e5fbf12ecfa6e25305e7b2a2b4710f968c38359540d2e3b8881da805d5eeff5d631e16e06f0b7aae652aaa975de9e391e66ee28c459e61ae428dced98c42d4470780b1850cfa1eda72740613474f7f6c8dbbb39d9e1a89c2fc0d242dd01eb961d227795bd36628a005e183884322da459cd7e228910dbd0e6ff565be0f6da2a51d457d69be1b33930cc7bc3b90e82eba909ca6d2eec31afa9a41a57fd84bed391be814a22c7928955f9995640e2658f5de554ae26d87dc43f7b3e8379cd674313c61d9c00b259b6a2a3a1161c26b895086f9470374620bc2ed13ccb083e0203226497f8f82b48ad69e83e730b315d6355a05e7e3fde5833c994a0dbd1002f1f9f20dd6bbd3e53e1bc052ab027d0c0aea7b96d0b61e72a46f091139a429daa81e88377de936ce7d9680047d9552c6711a3896218ae1f45c00e8a3113debe203f19bdef0bfedc77eb3de9cd73ebd02efd7631bbde675d5abc779a55160bb1d6bc71bd0607226f3f55bb98928ca8e3c0d02f6f32fdb6c67512a925365813c280fadd0af7671fb476aea4a7d00b51bdf6be20ddce124f25f0c907322ba1fe5a3efe6df310a334f2b83575315452f0c28aae9b4a4fdb78086536f8ea489250bd0e97ac45243d9d3eb34b3986455b4a681ef9c267e20976981c5a7e9696ea0e771df16feeb3c37335fc6be3938e34100c1f5a7969fbda85dc79ae381819bd238fe3f0ad10e95abf1ca9a2fdb5021c5773a857587171fb5d06c74db6883c6c4abdcddbcc0df12b667ee127fbc94de140546299a262154f688fdd217e91ebad781dea3858967091cfb45052fc96e79b9ecfb4907596a513ac32f3b5bbaddb650f70600e0f1dc9abfcf446d503d71662a5ef90bb9ca02ad82b1ec1074cfa05d6fc99be12b5490b56a66a63f8e1f82c17801990657f96b96293bd767d8297edb5717078619fc9345c717333c70ea9d72bf080455f013a1f41bb27eea2f64254e44debbbcdd2a6249487952c1fba805c37f23bbcd8c77cd01fc96ec8ed8e7c875e7f8b96426dadb38ebb6fd7aebe57fdee1cc2d5735e60afc6e44f64c7396fe5b5a8ea97a4088b62174e2dd6eb3b8c0ce46d8444d198a9a1b7ed3679b1ac0716be6452f1cfb9b798c66ec0f60704ee5de0f4021372e3fc19e215e38da5583bf077c1e14d99e41163fd582513ee41fa682665638df1b2e7fa89db9eed057253fd797ae3f88ab26ff7a61717dfd4de4dedc2226407df3333570bd1d1b02cc8f4d7a24c35f357d935cae15ca6adc39cfd4af70fa00dbae18aa2c8994629626c4591309a5ae462f286f2455c78f5bcb4d1e0c9e80f3ce6242386bd4d6fff922166341d51b1332a657af519fedb8413c362c5ffd7abddfdb97c35f78fa58627c86ee9402fb9cc704fae816f70b243fa33d5c4f52a7059fc6c3ed73708493536a9b604e5dfb86c1421bd233984486aa347561130894ee73e4f94e5b4afd8577cd0d80a9d40c99f615d969b3d394ceb10cce29610e3dfa7d7a5a9dcbb4b64ba27443e840b015506ddc36428b674490a971a14707263ab8675e1dd19ff3d3ed15e889ed4612f36895a0ab049d7ca934b6f242acd20b03f9234f26a430e7772cbf560cf7db14de9d7037c38334d084e794983cd0cf3c743cb7f75cb103939b2a3fa255843e6be1ed13069c7255d14c481f78971416c2d48a437fa84ce0fac949339bb1aee4398021f6046d929e4dab95fa6418117f56e53afb07b92b8698dae995ee1607ea802702fe1070a826883e1bb9070567e1d06deb4529f2cd81cd35e2e52cc2d298eb9821b22465211ccc1dbd3b69ec89b08023387f8d75962b11148234791f01199d943d86c25a5d86cec8eca2fa93a0130a831f473fab960600ce58cf95c1fcc5a5d9429a8c24ef988a96858dfca9b548b1057147a4cfcefcb79e1923ee423ff37169d0a13c7cadf30c980fc12256ec759beee4b4a986b9c27b72a44a8ea867512212734da3d1c8963582fa2dd3a7dc6b59115fd18e035b149c04a54db03daaff68e5a20ccc29a0e75fbf31e1222d880cd51dc9fa684dc10d4a1ede1375973c25dd702748e775300b6da6604fbc4868ae1a7661ddf0238f0dbe4e3af96af43c7a4e085eb78e70aff1fa551d86b4e0c0ceefa65db7b77db724b29939432d408bc08cb08322c1165043332515d11db7dabb952c5098733b4a4ec40ad9d168e6a84eb7d6d46d8720e0a74adb5d65a6badb5d65a6badb5d65a6ba5456acc504da922320bc644d3244af38e2a7a3a19213342d6615809a4a4aed5314353d377efe18f9cb3e8eea71395a5ee73ce9f7f481da9cc48fcd538bdd136a1b428932a40c5585f5dad7758ab633d0d23409562ac7fd2153e8d89a4111a13bfd30ebb1f8e3f26835f14296e209f055ff4fda2bba7db7b2abb7f83bc91167523bd9f7198404f272ab32d8eae57acc761f77f8c7e30cf28a72c654ca0518ff510b9898a6c971923d12118f708dd51dd2fa432127dade89ebe34b1e1b834e51e6c379b6da8032009b0cd76f9baabd966b00788aac2351d68f02ba40e70059596613606c90527295a649b518f4f10075ff3b4204349df2afc4293a95de66dd3ac3d9d9a8021d3644b1158ec5b8d3d9d908e7ef67dad6cbdc3d75eebeeb57aadd7663ac519a8e7cdd0ebbadbe19cafe7d9d5cafbecfc3cb076b8c666d69d9eaee79c9ead5e105f3927adb5babbd53d2f87cdb7512cf55aa5b85dd0ea96e639e7a43196e3a657b4936776295f7b7eed6e16eede842f09f085c2dd9bdcbd095f12e09b67ee7630746d8f3bbdaeeb7c07be1ce8963dd7de2f4ff75c9996cdd9466b371282870d6ebac494a29b2959dcd0946ed874690bbb4411b923e2cbae5db60ffa35577fc4a75fcdb03c4f6a9bcaa1008923a618c3c51a21623ca099aaa20433e84046acac4a15aa466dcac2d92c22ac6c16139bc57a5d44d166bd18fe60b5c40fc31f623884173b7c30fc11662c4e4344995c667bf55ef86365c9eda9f1328d2399ed7e4e6982975ca14286268b619b288408226b2cfe794c10306cd7ee026e5ba34dc4a220954613e504181e10501460d7bf75669f58f2596badb5567300c40c9507cdeb98d991f318db5a98534d4b4f135f7e307ffa13dac130ee96ea275a7b3cd080634944e52731dc9895a92ef79c41b2596750df6eb80b247e45d31d53af98592f47340a4bd416edb483963163b054d8dc65d60a9082d4752b7cadf7f4f80471e0f4434e4c8ad2e09e4e348c609799890460ac214194027e98aa5937216287d1f27d140323bd85a69fe6cb5bc1dd755dd500b83169c21d4b8cb7bbc5e18ff91f06edd881762cbdedb58e2f2eb457ad0768577197efb51d78ab5cf8dbfdfdf2c501e7e0e04d1e987d8b6d0687f0dca6eeb762d002d12a5a8524bffd4965b46ac6726ea05579f274dfbd7b204fbf9ecb59a8520f69441f92087f48b2690efd493b4a2b67d5a6b2dd6de93a9d9ddd69d95df7b95b72b2bb2532bb6392b23b26355d0f10558ae1bfe10fec43927d65b6460f5218069a92125cd6463c529c2693c994dc701c4f1c6cc7b279ec726ee1816afb5bff0ce5caf6d72d2862b6ff2b316dff5714d9f6ef89f2b4fd8372d8fe43eed40311dbfd2794951d401ab3b63af17084d4ae8fb56729d35d9d9b8f2ab395cd5403132cd9f5eb0da7262e20a1008b2066f0c0243d393303539918dcbd1b020b1277f70f4c8daa3e5284916b820a3282202256eaed56342d0dd18509a06c42c47437254b92ed3cb4984faaec1aea505f57870717f7feff4efdc1d47364917f0407ca99dd1f62420ab4a875b4ef94eab6d1beb37ddfcb5dd074d281ccbecf41fd9928d8f771dcffbcebbaae52327f23d49fb208fd6a1802bef7872af879b9ca6c77f4deb00cda41f7ef972f16241b5fa35d4ea84d13c2a98f596bf51db2a62e94aa766bda25dd12e29c7290da2575cac1b64bead4ccad9bfe077553fafbbb209ee1365380cd9fa96952fd0102d69132a148f595545f7e5f58e826cff13bbe7ce9b87df93b5e07991d0749de20a991d31945f22792e566fba7e0404431a2587d3afa76cf41a26fd4877f0afeeeeeeeeeeeee1e000a34298cf5394e6b3cb6e8262009a8e5ec8273b3d17abaa5fee496ab076dbffecfc0b979fd2e1c4f6f3c7d6aab40441ce64f6fb4e9a3142aa4748a4ea1365a64a99fd025a01a24254b2dd5a27a333d39246d7258de39de67dc6432c7d8dd27e23f83b4714d06edae67772f7ae79f77bf225d238d95d4b62f937d9f26d12774c971dcbaecfbfe408e827aee97af5dd25b49a3a85a525a8cb7ee1d67ecc648ab1c76492a429dd5a839603eeb23d38ffbf492509bc688a80a5e89322583a5d645d3ff28add531b602bbb54af7def007eb3f8c71d7513d79329829a6947a5ef8035c853f30d62d100443d0022974799536fd501445960e7f68cdd262a86d6cb4d65ae36b43a4fb0b47babf6fa4fbd323dd9f08b2c2133e2b516b7691f9748b6108ad1bde54a72a207fbe238d9f66bee6fb92f953ab666a64f32b55057259911b55a087aa62c01ff1e7d72947dad9debbf0b11c3f5fa702dd9bc3ea0dfcdf0776e007ae40d64877063dd63831fe8169e4d0fde110ad8f2d03c8dc00d713bb55f8e55cebe38c2f4a91cf9a8a7cb66c0faf3a3162c829d8bb4b517cd8f433c963810ecb8685c3860e6dc74c87b66386937bd4bc118a057f47283a51bbecd1a3c74e8f1eb61eb5273958c0638987d2998b65b5295454817ac4889192a2a232c34389c6eec89eaa1b331ab22c9bcd764983064823fc817f65b78e1aad63dc51a24d9e4a951b5739f80c1c7b4ce911e5e1db0131f1b9f9dca42a95cfcde7e645bb84f9dcfc6897b08e049ab172f66a56ee6b843fc0aff15fce495074fc50c5b5a85c41928fe10da85df2806ac2f150e251b3b56cb5251f259f221f239fa3fa9a55d9d5878a0f1330ec01d5c3e63d6a3d6e3d9a7a6ce9d1e5ebf26541f6ada44212247d04c7efc61da4c953ff8f8ca2a4a6a8eaab7e8eb1dc49dad5899922352d9ad2d294d59c3d3efbf8252199a952b323db296272d4a4e4b0fa3a6ab62a58be0dbac81554304ecfebec48710549bae280e9187790680e9b4d9e6a54248312132535555f3b46a38fe5007796cc0ca8ccc068b027b0a51cdd2a83dc0e2d0333301a8c06a3c168305a267d642b1e4b1548c7cd3c38ad5fe56b016841f8e3beeb09eda3dd3fee20ed30d9994d5199a9aaaf0cd4ec1cf96ca7e806a3390ca604abc1a6c0aa806406b90dbac8f598528174d41f9cd3e57cf47dc9df31fadc1c563f87cf962ebb861eecd899f469c2429717e7435e9cd3bf4dd9f6c5793ed67d472801760a55408ebf43470e1cae1b365a357268e0dcccb0d12c3104bf95973b7cadd728727b7ad0e95450c512041590a38b65087e870e2b964674e4c0e1ba71c5f208164b153af1ebb228c69e25d012687df7c76056ded3f748234c74f51ee9ca46bc278ab9f2834f147357fe5e48f87de2836f248c85368b21c53910359a64d9a2463de3f43905c4ff81384962f2d2a439e7caeba0befcad514e944c265b610a6d3baeca766a44697489d6a88dde2854fd292796ed1437d4410ed79514171ce9fbf8cbdc813ba577ce69a9b5f30677a388da4ec34eaadd65547c53c7e581b881ecd7d6f182ae3b7cef4700ffa3ab11c86b53ef57ae0578ffbd10efbfaf47560f7e381e61aaa490fae047ba2a1924ac1688c3ec83631087d9ffc6b1064d95c857b6b909dbbfa4105ba780a062a9082590f82217ab4fc3ba1a33ad95d6b74d34176402241499dad6d26a2d68ade4696b1d1a9b47f88638908b3ea52bf0bf37e2c5bc5f3d510cf446d0751cc51bb4f7aba02904810d265004a617db6ae1c41ca3c40893c7be4f7dadbc9e111ca8551b62022dda627b7c7a7c6867a36d6b23436d2887d99b93da36876d7f6565d7ccadba6a565553a8be45bab76f26c554b32d6e25c627dbd668db10042b584921e2830f9246c0173154fd1163c18b6f858ab2f6ef0d577f2e547d49317f2e15f365df92d6896dff62908a1c94bd25c85463b7fab49245eac67a539c4d2d48eb78b32906691d5db8cb601dc14d3db08e333675ddfb38bbfbef03ebb8da1404e904bf708ae2353379ecdf30e68efd3a869bded156724e1e7b73e0cff059ef00f141f208f8e2b3c8234c42c01749d711f143d25567ece21c8ef6e67298a4e078c551bff66b0a5d2f268f7dfbb00974bfa049b6c52c6cfbe506778c6dbfecb16d4ea7be6ce80261d74a7e7dd99bbbb7fecd39d08d5b651f273d99d9163fd996c9b648988695b6fd5be9973adbdad7f9689a8b6fea945f2df47627fed45b1cb191c3ee06b39d6dec0b06c0c99c1477b80bc0135d62d96aee8a547f6ad250fd1267cf79bf04418d43492c1b81beefb86804c912e3ec12cbb0ec8218f47d2cc3e37c3a41d5fe30d8dda9f1528d37773f1c6fd50d1f6274896566be5c01b1efaf6a53a8fe5dddc7325c547fbc3faa3f4653087f17e7301a16f368dfa82fc92ebf59f9218553fcf085881f862f92656689cf228962949cba54e2d3cf13cc159b4695b476a3b6da145aa5fee4a7bf81d10631501ff407dd60e6818e7da7ea4feeaad917cbee54bd58cc9f5b664e7c1f0c184bdb3d3ef996993f73de169d2ffab703e9a89dec2f8374c4316a617f1e58b5bf1548c71b32fbfb403cbaf0cf271154d9f3490465361d5d983cc1cb906d7f0df6cffec02aa6dce809a49caa38da943cc1c8e640ce2e224a0166d99f06e9386388fdd9807874753f9f90e4b0bba7a3ab7b9c9dff9b01d271d5647713678a62fd70e3713e2151b2bb114af839c4469804495c545f57d4414f272ffe063568a88a491aa73585f00a93e7fe7d1b1308b34061f7af0d1bbb922596c186c230596225fbb6f6954d9efb6292d6f575c570c4b8c3ff652ccb58e64038b011134cab3f77dfc74b514cd9b7c453f6c5b27d31d2beb8aa5de227fbde2f716d5f6cdbf773876df5e7de6b05dc495c3cd20775fda97f1fa7fef87fb59b22eebcd7b2a27052704e507451eaeab5930bd47623b63b41419b4fdf01f52979847ea55f8fd4ff48d7cf1838664d8203b93c7f220ee4f24807801f7ebffa7045babc23ab074997179b636b7ced3fa7be7135f6883968ba6b57b392afd26a67684bfbeeee42e66da87ab6d2faf3ebdf5a69add59b3c146356f96dff6f86770b01a1eddbd083d7be7f6bfbc35e2f9d188b2e10f0c65f3726cb07ec4b7e9b061d463a01b65620dbda3df4b858f55ae55a16a8ac4e167ac82b3d81bbb5f9e9de9b1d46438b33dc597c5d475ea2ab42adae83e7eceeee4a78de3649116b9ca4d9220c1458ca0091f40210b32e4db1b2675b7b655b25b675a2b22d8f2d676653aa88920554cc3a31c132c66808a332c088596badadff5daf82249c0755238907358574f8b02decba7d0fd5228cf674d28245efe9a4e5c976da82dbd3694b6db7f674da826471c531d24cc9154849d702f4b35e887e16a9492336e47ca298fd4250b4218f301d211d607364066943466da24d6f689d2c2794991d8ad0ad59b3664df7de5876d9c748bf6b8dbea4ff161c1e628fbbe8ca4063e5aa458d57b594d12255df9d7a40d76cfa816f5d5f3860c8aff91e04089dcdd6f619459dbebbcc917c53a7874cfbcf1b63e81e138276aeaf7f1ddd33a5cb1935c40224d41f202370d0cd903eae63fc5d62e8ad63336d485b5f4d3b7b00becd7fa3cdb772ced7c0f95abf35d61873461ae3579174ad38630dab26cbd7fec2fa0efb6cc6cf8e45aecddb904f95282de44197bf71393b91a86d5b55dbd6a91b9c204d501f34469cf1666cda43480832c22740a702d178ff9d0a94f337dde78caff11d8643de90de6daaf56948da3cd1b61fb2f40fd53a126d5be46e16799ba01a84b6bec2909cdf371db4ff91b304371d729c5d7e39b1e8ba6a9ac9601081981898e492c8b7261ef20064482267450b0eb92f625dd7755ded3adc7d0d24ba6cb5c0a89034550605597820438d952c57ac22e0a4c995284aacc031117377f79e3274f9da3db854a22a5364c43039b303d20b9d65da5dd7755dd775b5ebae773edd7d8b290eb2761d7eb7dfe1aec3ddd8ca49a34b3def9206a460c9cc87334d922c9144134dd068001425502dec00831360bcdc20061326b8b202cd072498a0d59851454a1326baa8a108db1134b6d8a205aa32686a10a2ca9a2b49c0fc1005134f43100105092f4c7261a48ca6e88e5822089d19a868410baa24537039e2dfeb1527ed911df9aa313c63fe2d28364a5a9862885b9634606243bad97546ec4e092bbbfb0eea8fb83b1dba94a144aa488c24625dd775dd50d791b6c3b8eb7037963387871290c3852e7502a6d0cd422482335c64f9a1880526b6a0724341ce0c2ba2aed40003134a2871040736cc9ca850aa628b1d6e0d892438e84aed0733b42fb8f8709bb62a22083370932101e505882ec478323a798823a2b036700a96ec4065ea40ddee1a4c109fbdb703d588c5f8fead459470dfeeb9cbebf686f56b73dd1d768140b3d74b62aa41922eeb2e81fedc942cefbd25dcefb0bbe32f0163f283b92f69cb29e46848d03ae8fe4e66df28d029a400ba665b12a34037c598fa98f69ddec903d6a7fbf6f0b42482916d3f8770e9e75977a5f4f1ee403812c2fda7a5a21a7e40b4edc6395440477d85af8296ce0d2b1dee74f0d8417dd113e096844d2801a9c0f0311dad9e24f14ee0e4857c8224b3eecc0c984ac98d3ab25d8e7c08488b8911d4d6e32abb4b12ebbaaeeb3a2ad4759d98daeeac0e6ec1e26bbdeaec00f10df674a2c20b313d6c1b2adf7420cf6221450b1e4a9053051022c84256772802e389db6cba22e90b1c104f525aa05292c9625f680a8a25d1a67f69ac92ae42ad2ee549d39f3fe80fc779b552f47cdf96fa5069f8f2a2e9d310b2ed800edcaeba3e4ba902b16a2c9b055074a943564d0c3d59b45db26a33ccce5125d3f35935cbaad59fe9b44e179d64d558b54d9f65db4375a8eac0e1ecfbdb79eb0112df1fc814c2ef2f8ee1088ee3ebe43067507fe4f4e9c4ee8a588d16ac7ec276f7808c499dc9d3756fdf4a607fabd54d60dfb75d8749eb7374d23e511c0a76cea74395aee03d3ed5d65adf9fda6abdc77c6ddbe3b55a716d0e74bb3565edaff7c08a817d611800baa755cfdfdc403ba5ff690d3ed081563f7318fddc7d2b31b401bff12ed1a1077877e49de58f06a883630728c33175d8a864b7e580e095797b829b46e801de99cc636b0af1e0d9d919e4b8403653dad456d361c1c4edf80a3cf7d7e140079dbf46f8a3cb38bccf39c7b176203d661a5ec31db415805726f4fb085de61c16e0b8a0a56fd8684101400639b51d1a351e1e336c4a51966abfa32cd96a4b516cb525a570cc2b0d6c7a740f166d41c1c1b169611c1d00512b21394972d5b4e724498996e4448976932ee97d02082f6dc945f2beac3433e996205911ff50657267ee03662170003f409ead3f368342f93fd077c21f9d0674f25070f431effdb29557b7ee2f304297fed8bbef3bee59933d376f0e6e10e08bac88a9fbef439ff3de2e16f1bfd0af1f8ef6bbabe4b029b77db180e3955da32374796d3e433c85e7892eef942bdbd9dd7895c20f5edb0260e3f1ca6e5185d53ba5c39684ed02540380b6025d99d17641d11f061a5c60814e053c7876320000ec85414fae3e513e5a1b62d5af33beeaaf13d7ae4e7d4e12423f8bd1b5d65a83e8d10ffd1ca46ff476f354b7a67d165966912cf5b5d4d2d64863e0d0fcf93386501a0dea79cd2fa98caa35df8656396c929546b1eaf745cba9cb6c97383e2d1cfa4f57a8550e8ce5eaf6c404af26502a0fe81d3a72e070ddb0d1aa914303e766868d668921f8adbcdce16bbdd20ee8b985698a134db198ee71f64f58cf0b88aadcd48c429a19c9a0704db7fbe09e385dd38daae044b552db37f7fb13551ad349c3c5a4511fe540396bed9476a37d5425298902ddd4b17893dfeacb1f00686e733a6e0b0fe273cee9412a509edf33e44034d67d140c3f7c4a29a55aa4264ca8570f142d8a24931d3a6999b24510443102343ee785d4f896485252088dcff91cd208ced398b81d86afa54b484508c96cc3ba7996288affc1dce213ed702c7f0fb9194bba6f5e1cc31bd6dfb0dec6d6179ee2876339a13870123e1d4b22a5d18409f50db937769fc732c7fc75fd197b5ef375dca6662a3d497499b7ceeb494ad4ae0f5a27bec67a5230f3f444b5e7d393988ddbf3e9e9b6e7e33d9f9ea8ecd59e4f4f5236bd8013154ca891deb2e78bd9932c525f333c3dd9b0e9d313934d9fa400ae8c2a3635787be2ca90614f5c194b7bfe991dee8923036ad3999e4f4f454fb8aa3d9f7053b8a83d9f70381c13db66cf27dc944dc959814fdcf3098ad9a64f50c876999fb88cb1e9e76ecf272e60088511f3e5c916dffca933058dfe0728e8fadf1370aa36390e4dd8305032e090840de3c427c4c54b843ce09b8488d4615b0e530be3831786a6c3ccc2187d60aa3278c314217dd61478828c030c98bba713982ef435182c606cfbeb412bdd40830d18a41b5e98916fcce0786568cca042e3080c0b3644a9015503abd136564b6e581715451647921638a0a8a0a26c5038bb25a98a4d3fb4926da84287beb2c3f6504a50b45b1214921e2d944e7a81cc5046339d2f55d9822f53f9824fcc05a0e705b462c61318809e17fae013e017237217da17fa61cc7cb9a17594b3a7d317a3eca5eaa3a299060088f13d9d9c98b1509f01d8f41981f774f23265c340ff4997f45e8e567b3a7991e5a6944ed0d3690aa829703bc3a0c3291e395168407ba6b86dea9afa2e28c1534dc9812a90c4a504392b676460c47adc92a86021440f2e10119b3f7fb6ebe7392f9893d1a97ffd0711ffa360e8e0fb832e8cbd4e264fece92485b6a188645028d2f4dc753bcc488e248c502c8e9850c5c0ff6cf81503347a3a8cce1ce672f1dd4921e2b3482354242b4d365dc441eb0fff48f8abaf4242177609117ff54734e9c2a490f059226984a974808e2bb4180b0428bac554985c6631f13530a53614913c32e459641087d50f7f7299c5582f3e8b2489388c831a9c4e39acfe8f8a830a2f2f3123dfaf1e1c8da84097ce8c0087d84732595045ab4097c88cd1456c85a5deeaab3e8fa52d65bde57033fcc85ae342973557cd9415875d99fc6bcd816acec95ab5d59c124a0e840349ab2c9e314a2b0e69ce81c010fc5644681cc936fdd86a9cb14f93d165cdd9d8a69001f6f7f5ddc87fe540e0bb7e22d60b119ff59e470a61bd268dac8c804434259757bf5689afdf01acb761bd66912ecf9bb14b6df4e6b699dd43d285bd858f746192e22814e9f2d5ea3fd2e5ffad1e7c7005feeab7d0bf6bcd4d587def6235f5555763ad7258ad329256d557c57964abe2aa1a5b737ee44533669928d7d781fe47ebb6407d5112e8a465760b0ca93f3e4b220b90505ff6abb8bbd27a5b91512498c0529a4204a0f446161d9ee8fcd2a7bee807d517adf93f3b290a364759fb8d41f5e528d059c5211dcc280b90507fc217ff3559126ded7dce5a97ad9dc9d2fbf06dc677d88af543ae47f1bd1a869ef8ab2c8a9f37387e506c742a107d1b7288052a8c3e8e3752a13f4acb7060085d74f97a01993cf58980a0657e710bba4146e8241fbbee45b76c2e9723921b920b02a3cb9c1c7c94233f220e34348488030de5a0724153e832ef1ca61ad01faa7826aaf96664a1607c4af5a7c4008329e46f2b92a4292d967479a79ebceeebde5eafd72b686a031f578bd77d1de9f24ec9a6ee575b9df9a64e79b5cdcdaef3ce235d06fd941cec6a6974061243af219de78d133117ea5249dddc8c0b11382e45c8268f12a5a5a827b54a8911d3a5cb162cf466866af3c587f6123d5dcf0dbac44d402a7dfa5886c568cbee82b4c5d06c0a1f48b344db777cadb7aaccb022ea93a23ea6a690bd5483f29620850fa4c991d091f04497b8694e357546e41adcebba1170130426154fec3a71403a2036e812378145a0d114b25356a64453aa95442e31df1a5d521995c9681aed5fce1c119a52ae92a1cb193524bb56a3cb890b626dc85c9bcd661bd2b9e5822eb14d6963255cc3369bcd26457fb8fa63df66b3d966d46673208a4467363ab3d19a8dce56d065b6edfaf9498eaac105e9390cff2a17aafa9d7a24daa373ce40e4db7e1245ab54d52c96c974b6e490cd181aa9fe787df7926e0fc1078a216bd970e99ca7f45f9ad22e3fa9a6a0a6666566572a8be29b3a9ff893259247c4078f7c9a3cc262fdea8fd8acc66f7451f185b05e7cfaa2f820e9a246c067912eca7a21dfb39e86a411d6afde88cd7f1fd2983c19045fe713183063e26bd28891a9dd0893109b120aeb4919eb59a40a47865ebf0df859d701e4b2c7fdc19128f636a3f87a643d1d43f04dff03472a3332c461fe209982c3fc5160c08c9dc080190ba9ccc6e6cb99b3cf7a5d69a125673424da4c7c7064fd0c45f0fea3a34776809e50e90d0523e28b24ad39cc193063455464b186fceb8b218dcd90923826b476d7b9ecd39fe1255d960c811c9289ba9ab1d0ea17ecbc4e7f22a8f3baf9726786cd973e3b68f2b85b9b71ce57ad20784854436d0530686882690a2b62b3c2565fc7902c6233d25d8d6cfd3e9a00ee1950707e060ea95361f66d46ba6ff843874834786d9eaff06758041cad0d53d8aba72f8e73bec2f710244b237b455a9c51677cfd11dadaefc126b096fcfbb7de647177af2248fd9941863a00f23d41bbce39a188100197bc47522615568188e4ea6cb73411affed449ebd576a1d3e20e8258bfeed7cbd8ee57aa20d9eeeeeeeefe938b2b479ca9626326045d1ef21d7dc61e7f4a963ab90a1918321912e854201bdbb1024a6a8c2deee92445d5eae17377afaf5c5f157477b7d6adb5d6ea3ac5c5f75a6bedc5d6e2fb81711fd4ee6efdb670c02779befce7ef40ed10babe5b4a9f4ed14e7f70f79d22747dfad9babbaad1e4a14f378ded4db568d327da55b4d0163050068dad0a63c989354be4aeb460656e7172f2c416c51563aa545d3442a460090f3b0cd103166b88b00208a918a280f1421755f58c0e2bb22bc46ce982c68814be60a1a20a2b5fe0e0c4991c8603c0c82181a1891460314410275344a191aaa20cda19d1cc0c25413ce103172839dc5af89246891564bc08d3c5c148a20c346b72a86176c44252103944d1e4650b2aac2f66134740a1e2a68512499a2080cc1819b850b1c36c8be7050296a449e26987269a8c70218c29906842022f929af872442e48d96104512b8431430c2ba489020832a238220a49d8c5ce0f49b620394ac26136c9c882ca0f567e18c3c90e980b2750a08397309c38a1b261833267ca9c4982448d0c9a6e8b28a6386121e98b2a3150d0b0b4c5d4122d0731b2548154385dba60418b318a6861d45052021bb4d0a2872d569c01d2000c2e3488a9e149154f5e664fa71c90d0b214050f5c9634a0854793e18a292da4712244acfc20a2206e4cc4049193c50015a3dfd3b4f39e216d42f39a5fb24c2d16483345ca48ca940f68ba2cc25853860b293e3c71a4040353524f60400208a8a82450f46736c04d0a2427a81c92a4c059985ec89a907841c612150ce4f9f3025d0aba28a5618a818c2a2fa22863d39f51a4e182062758c8c10b08a634e942b5a4ca122fb2f8a03f734d22c8816cccc73743c9099d41ab2d1bae9c6db8e8f27e47f5a7c421f3ff6a1d0576c613f7c688297c63b31d98b3d63d2f0702e2b0fa2ffffb0e93a2aa3f75aafe7856e3cd2832f3ab5cfdb1d75b3d3db6007a70d487933da8002ad0d7181dcbff1b29babcb8d514adb0f2267d6249b42fd237cba9c2c1e5f7f05b4c5ed9142a217cba57e0686b9367f5e10bd5b05a09d9a0c1ff849274f89e087ede20f857368570f6eaafd114aa7bf5dedbda14cad92b121c73f687696df2dccfe183f6cbc27df07960c1c622640fbcc7ae10f2e3cf227424c629536942306983dc99c3ac06b48aaf56b2bcb76bbbdd17826d0ecb0ebbd9138426525bb67daf3f63f48402ec39792c5955a095ca8278ab2e77d923bb37cab11599b11d4b9b7d838c82629e0553c8df8d33e67527a4b02d0d45e8f60658738e2774db9225b8eef07fa0a35dc657f4e05a387448a0a71316b75db6b0a8ed32b770bc8e2b48feb9855459ec5b79abb1c885a0587ea31cfb3e72f579f4880860cf308499823d492fff073c8f9cfb32cd5b82add665490f6297340aaa3dfeb27fc73d8d5ab1fbdfddd348c7ba3d1d8f463976dfef57bff2d347ad94feb843c9ef84056d97190b249ca7414fa731b8ed344636a669572ba0767d9c6a43215516f3429c3bf7b7f545f30f790adb625c4291ba2dd901dff36ff60c43c0604f12ca07ac4c4665b258fea11ae69bf3c75aafbb7e39e79c28d05da47e39e958a4ceb796bed349913cf57867635d7e1f33595d0bf03eff085eeeec58d4bd2eca3f1d8667ec678fffd2862080ee7af7f8ffabadb5deb0fbc953ffa7d074e19b3a4cf78e1364eafe8e28806065b2f9ba21b81d7847148450992cf6fdfdef1be98479207831f01b29e86d8c6deee85a40f8e08ff023cce7c086128af8f67e4e6da21851acfef78d451f3816813f1db6e280a64f9855bb886ffc3336678f8e5a0f88c071c6cad6aed88e370c41003bd739a787a2477e34638cfdab1963ff72be5b90bee9404cf63d26fcf6827077154227ec5e9b3dff137091d7037f1d3b4ae4952a9436841b3ae7ac57a41db59d659a239de19b3a4cfe391e99e8fb88c23bad36682b8462928e60c47442d02ea1c8268ae1ff4ea0b408bf0b4c2704ed22dea3be0b4d1f63772cedb65fc3106e6ea8005fb366bb02f2f5e9b5567bc5a2caa8244ef555310915b28100000000c314003020100c07c542b148240d24395b3e14000c76924676569f4ac44992e328ca18630c228000420c00426066686856010ab84147f5bf29e40739a288bf58f6f3e206c2ec85a9cdc49152f2c85623a5307f5d698740a5a34f7238bae1fd53d60c1e3814eb6c756c84e35ee683700bcec65c7c19b5c071b2b9634d4a3643e8bbcb19375c399aa46f71aaae7f9d973a9ef3b3e2ec0a706201bdee2ccf801127628a477555f3a9b99ddce46a179a67f424207abd4555e34ee4d3f6ad50db97dec9e8245f25a595d11dc3de5e06b24141e451815eb28c7ad279bc8cd8dccbc6e160cd8a33758ebe6a2f7ac06f6027935ea6c1c669463ab32caa7269762cdfa34138ca8ecbc9ee36e9e6141c0f430508ba157e6437c3eafe00e095e714c5b35c54ffa9588ac0473efd332c7b36270d76f1bfdac5ffbab13dcea52054f1e40cf317d04584d6883be108f2136433c6437e8c274a75f47fbf21072912fee6668cd24318171fbb6db89725cd2153e85e2837851bc54cfbef9a6f3c9e257b2887dbb6914ae020e2245398be0b8bcbdf6732317b7a2f89ef92b5e7c53244adafe37b477735d1ee8893dcf681e2bb252a2e0878177ee41bb7ec9c742065cf6b593831bdda386f178bd9cbc280e7c3f3dd98ff70de591ec6e040481365db0645efdc741f43a4c957169ede04f1d15048d6e4674ef4d340a4127a98068492e842fa436a63a205af0ca745bb9845b6af47d613c9561ee8b3e8e1893e1b3d60f696e0a0b1f1c09ff343b4895e55c606766d70591160a4b705e224a71284c63d92053edbef334d605985affc50092cfe68a7a0cec3bdbb494a6d94f614aae4a70ea0341c25633f1bac6f0db80fe135154a97da1801c220334b8b267554bb433281b2d3e96541ad5f47dcf291b7fc7a11dfa95b3e4d2d8f909992f46f413b65c05cb69aa5479c7210ad709473aa061f248243f61d2a41a4ccd2801cae1efde9181b679d8ca011f9e78f4e826bb07f5be094574b1dd50d6a9dcea3080182c4d87143e476e7ded81e3d91498152692a5e37d445ac93b647414a6486c472daf23cef58c7926c5a99f9f7a192436aa93eba1024e45d13f518e89e98c2e95c2b65efd0436f471a8505b9c32bdd318ca218f360ba8bf25c313469493d1a543b2450f31013f08eb1d61329a4cfd39e24c15e3fbafefb08692a4e6aafbf37ad42b77a30ca561bc55eede9e183dceb4346bcff97f3d50614d46bbb9e367a61963582c210ee327ac78ddded6ad9ab03f53f3c07dbad9f4078057eb7d4ad705c5025b427fd8a3aadf58cb90874f97befbfb66bd40c38a58c6499187c589e1605fd0f0a51b4f3f45c5167107b6ecf5d5168c4b42051e3f4bc2ebbebd3c364341c14bd14ae277123fbb7b125fce0124c1cf1f589cbd5f939935363051526d4bd7d99fbe007eb32a81d7f11027a3fd8f1446ee0043eabb9cd393762daadad132ba383c87afd70bb83bd84102b5c48ad5e5a742253b4a6dd237d9afb19366a855f9cd1de74f0a341140b377ee616a0f78dc3fbf9dfb6d526ccfe5538955262fa214efe1e140dc10cd490ebb554341d6fbd62097b373a56dd5e9d66ec760070d875955df97f17c88a263c6a809f431a9120d96845b419db162b7e97fddd9c116419a8172159922709d5e88ba8b0a9f25652a7e79452154744d783a01ef777bcc562f7225400ee7e6224a316e20b2ca729fe4b9b3322e5460e5269f130df9d5ec5807006cfe0efc0feeeee4f65a387ce9681ce82d6a074339cc0946890407e3f60ebbaaafb87c07f98e3b9d382ae4f51729e67f0c0e1b90c13692aa6bbab37f73bbdcac63b5b31e03eba4705a1ce5a01ee1d442b7f0010159c092a0b5b4b22ff6a47dc5517d26e2986a8e9ae8196c5a0bc172050ecaa2e9b3023b78d4c45658c4ad404f2343b9b9d81646e3ee4693613ebb7e9586bc9db123ffad0b73a7a3f0039c6279428a0472499d8a54539c244b9c9aac7f7aa5a70e5d7e6a06ce931c7936ab115fe88b25e23edbc190bc0d3c8b32938980d4b181cc08c2b4de62cdfb4aad1dd3faaf7c70374da807e0ef4de05e16a88916403a93318bb783440a8dc3de5f4145ac52050efb4a5e2c72c35e91e22abb4effcfe5c3f469d2d7d56e5e806f330d075b22e817ac60d620aa9792a6e3debae4c496d0c6e4ce3d22d5c3bf4ea087fc4846484fec7a23d49e6d03258595200d46eb3269e8ea149f6c5d336a0897d72fe62dfc27678d80fc174a06823979cc857522214020c9abccadbd7274a9fd850bc8a52589ef3ffa7f486f2b7ccc8cb93314f912107dd31a4d56810c9bc7a59ec3d1c80c1c799bc2fe865e3db2bf55fe155f1a9edc352563438cfdede8dbcc2161dd255199b44b9032971e15065f21f6685faacbf89b4470a7a6ff47505bb2ba56ddea3d3ad758dd52d4c7c7ad25a44ba37bea2df63408be28710012d5f6d4b0f1bc067f2c481d133f41c6da768d0b84eadf7bfc4129ff022b2b54af3f4f1dfa1f248e2023153aaf8964942ba5ba253357d7b8e353a5d7d6809f4d4f0cb9b96455e18b591027b7ddd50bc4f502357a7a6239786749045755d1ce1e09b33299a2bee3a3e4cb723caa325888a69519e8ffc04f55d9054f3d0acec8dd3a2e62e4112666367a98dcd94159eae1ddd9712e04dbe465a0bb164a187f5388d81e7acc38ca38832a35ca572dfbd80746aa310b3d4d4fc607dea1f0443c166d540466599237ff0e67e4d06b3a28a69f747974bdcda227674f01d78c3ca9e2bcc5255842cbdfba62a0e5c8df4d3a7a78d4a76a0766ff777a879e3b6e16d3b9e3a316fe0d424b138b9ea46d2aef073c10bc1848be3426dfacabb2bfec533155c3fb31f146b099436631aa2811a2e9b86048cfb7b82a654db1c8db3d913cda8cc2ec62c7cf7c49d0742fb9b27667a03c3d79dccf976d0d5d3b83f7f4ee809d989b21ae4b6973ed3cbc23e19ec730146ed2ec3a7af39cb1567a04ea05adc8261724876dd9be7d9bd000e9f43def0d98f6916acc1f65ca95b21852529da9464f8317aee9ffcafd0d35c23516c91040aafb51eaf0f7c25340c393fd29c5b9cd9673a699775908d9a351a71d9d30e217fc667b0664703f2cf13238d0a0cd3f6f8d362f819d0db14e7c64a1a8f1dad0e09167931eea9fea7eb4fccfc6c43dd3525e88d7d4c9a2249bb6887dc313c4ac40a9bdd45077d95855802653c86d5717dbd9c3fc67a543ca216d62d6b6f2cf6990811ce15c892ec15bf35561ae33d9510ae5bb2d87b5e09848b4299e0b70835a6c636dbc6d626a798524d4adb0db0c8d444ff0cb1ad08071fe63aaf4c74709cb1fd3e79145e7eab8dfefc02298feda7b31b2a39bfc09ba767e55b2461f58578d7245bc375f3d9c7bd9fa2add836177590f4d41fd8befebf4935aebb4386d2668707328b609fd42f55d2d0d9ee71625f59704bdcd167f63f498894989d19b12cb14fa7f1163ea405d3c14770c0e474badf272e345485511b5f9e947b03a145878c861e39255fdd44c37664b03eb46b81a7cfecf205247580ba49115001e7529ab324b3e127ee402283d3834abf37d87c5758a77f4ef71c0113e41d622fcbf292e9be95b08a8f0c03e70e97e6e39dc9e9f012e7c99f1f47d2862df2ff614913e81c1bc4b6651920e5f88b0f967bad06f0fc1974b0c4f81a4b8ea913aac8836adc3b55bc425d50be0b328701ce594f590af249faeb7459d23e501beaa570fe1d97472df5fa778ae9d5e3fea9843fe76bc40e4761229a05a2eb0bc8e6e722e75476bb98a1bdb56661ce53d1366722abb79db14f4184ad1d34f81e51583bf6e44685ff46436b913193e420628343d2d9ca8404fb734d4eb6bf7f08d296d5e89c0a3d253b6f699f655a54088318340f846442f71e1471ea839ad44ed24787010631e465c027f6d27f4399e4b45bf1f1e0936809befcfcd193952612be140ae9dcce9d473d7d43503467dfcc7332ae81003967404b394f252696d963ad9d7762d7ad9d35efd6a21cdf7a4a6139ec7b833ff80409beeef104125b93dccbf4d9142f2f6f7f1b91d89b3e363abc8c7e767b13d10fdbb8f3db89cb2e9243377f42f463e5b66211fd44cbed4e843e6ee29ea459143bb9bf09d8e78d3ba6c845b9197873e5900a1a50212446ba412b08d72676ec02fa33ad1503f5ac7f7079984574545a6e836e2ddcbe8a6e94bb41b71677978a620f0fb91d74bb941bbe76797572f327e01f36f86995dfc959227591b457f80d9198e5c31a8ae847edb97d0d4c94925b9dfd7ef8165bcd38a2ad9ed70cc982686453155f414c7d41bc63d267d8df46dde75378e35ba58a89ce5f91288ee7b6351a339aafe333d1bb4e782bfec51bce886e580d67b937ba2eb0923edb42baf2f1f22eefd5e4ed331384db5c81e75e350cd14e834e1a852490c1d534b20e13a255399dab9b242809266abeefe08ff6ebef58f904b6fa7f2a1498cf703cad64e71ea855511460bf0061d9b29c430b35130edc60e509453ba81eee4c8874aa2db52c3e04127caa743fbac4ab0d536566dc3bdc499a6aed71cf6451122a912f857ba46dd49e17bb6f65fced1dab1c4db24db8874eb10251a1b706aba2a3dab77b13218c04475b09e9d123bfd45d1a2a32529fa1c5cdcf5caf3a4fb6feaa682d6bf84ed07f98a40408d95e7689008afe284449a89424c1d7de156fba4c3da2c4bc867392a1ea63067e66fefff57c5d81f88911f0d410775be2f0c6ac873fd06de47bb16c4a1367fbe9ebe986de20857f793478599c50244fe1574a5729d6a90c03d39b8abf6c6b0c39b5b16783f92c130dc8cc0e727cfec07d63c6b0793d9b6b2ccfc53600a3985e6e969270475fb80124b638df5c5795fd28bfc07c30fd4f2107ad3712d81e40c725b775170b73346db09001470d3d34b858cca2109084cc712d2000717e4d8b7cd1d997b8e4007ebf10d2f7886c2ff3abe2fe6eb8c94aab49b440fba83e1dc9825f309a6acfc3b408c8ecd80724182a1d4fd12533fe572c2cb5f029b9f2cef0c68a4b2ab01debba674b604dcbda2c0611bc22d6844cf77d7f32dcb0d9d5b358849879cafa2e2dd12fa3981e88644a45af65ce6a32724b0930c915732d8433c132abe7aa72d2b778f819b3ed5e1227240158dd120dc06f83afd5e6c77028890068da2ad5e30e8a810a054b247d761ff7a988d3a94b94e215c8866e477804f0464464741601c44e22281d1b730cd60d8881d925634481176b8da1779e7a8706443314b3679484045c1ee96d2eed477494bc0712bb7c6ab98849824b579d6ab2d75b9fbf9c0143641da1106352454ac5ccbdfd82b97953c1d79485203ef846923b9252817d3372ebd98802c9604374aa50a378d33116375af05aef69a4e365727a7477a3f04d7e4fa082ccfe540c209426a0c0a1793cda4e52467cf86fa838f9c11afa7806e2fbc01836dac9af11920c2fa855b2cfca53fad2994f596c79edbbded7319cf10224d4f55518a13ea1f6800817dbf0ac6f341b2ce4b7f8e178c0041801a14e33d342e233296302415ba722d110416aff8b0200f4c95a199e8e920bfe253205008f25c1d3a760b03216250e3bc8b0c5f22200fad89084b4c0a35da28eddaf9d938d60e6bf2a1efe52a2eddcf621c2eccd9d984a725b17656cef4252c8b4fb50505a4bac3afafb4c2e389e082b0e0704989f318859377d0ae7fa8ac62fd3e89c1cdbc628a2386db7a28a308108d0a92cbce365d7341172d330f3795ccd8d0b65cc6d36a4595cf4d69061ba0c7d7a8ff3a7103d175dec4b59eec4a0e8c78cbb76cc9ae9047439e048735c3b263b8b4f483947d94acb53f99dc1346352691b1cf1ed5116427851690fbe0b35378f55f2d025e0f6bbecc83e65c86547beccd1be6b7066b0e65423789f2eb7004708474f0c22bf5fe13a268e94a139183db4abaca2e5ba0db54ace155491fd458c278c3d6a2a684182a0372529d246b655cc74a675d3e1db9aa74e34fa46ef3c6756f8e9f74fa6c626dc133546b7d002b9015286e2d8972f92dbeb7b9a575f5c65203f1456cf28685f7d719bbff2388000ff4b05778d847de5a6c28bdd94c560918e86fb14ce1085f3c9e131c498d38da06b6167bd1dd2b15a9057541012475a5d5f22f46acf05e9f310c50d11b799736f658f78cf3f5cf3db6c090a8a898e5148a9d4f82f7fbb00f3f45b445f5a4ad00f138e6e67edcd9be5c04156c405db19dadb0b0ea3dea86cced164a2008d0a33c3708010931183c41e855804bfaf0d4a4e85b0ad9add58888b90c62cb6f8ccd61a71e1998b3c25b9326cfa80628f20e87cdce008a92593d60f30abcfbefad76598a792aeb1f3ade22dec589b6e893817a8e1ae02a25a355698bf893724faabb9e6d4b28ab13588c64c433426d0179631c5e45da18829535e8dfe1183e2b4ac44eb47cc91a7dfb92d47bf61012ca9d8b93c056b72a9b7bc56022d768c0859ac88265fd594eee8a82219643e4ca8433cf7a68e0c292286a6d5ded8048ceebe28b0d317dbffe2bb665242eb0f1a69655b54dff0567df225dba1c9053cf8769c39bc426173e20db3813a1bb2f5a427dc37dc00d4804f0b7a0b8f061cd517ce07ffe3003cc4976d1c7eb2e280c1372ebf7ee79fcb990f44776f3e5e37e642a1c92940b4f61d3667fdcc09f0aa13379165a5baaa867db58b25556cf922ed838fc0dd202e6b000ff916a4d48ff5c23207d38ba262cab4cff6baae29128990706480aceed12c95b10024977f31121db56535db6ce6f34dd78eea24e2c03ae90dc1625cb2082eb6b01312d2fad88a8eb3d935963899ff7149b9fddbc067ab507be4cf772b8b04677937469485662c1d0abcc8a983d55c8fbc420d388706dfd8921aa232b0b8feef723af07218e0b124c235ba4bd826e6c8bbf4fcea21097e11874b99d55b2d3b5fabf11dbaf7e18e94c38301937ef67fbb6dbf105885894cdb0e07adb7e9e85e1a459950e063e906b086f3554836f3fd4ef81f0acc02caed1ef99f268cf50e3b9210a4ec8ee358f363e09f8322f9984a551ee1b9afd5ce1b07156f1009f0d549d7c7d351a38d3673b64ccceaf983016f1d018fc33c1a462df98303fba51f68e05c2245d13815418f29ecdfc00fb74343aa57882ac872681c45e74bf2c070e43a14571beaa82510242e520a8903ab0406f85ca521b3c85d4a27e561ef440ef098957c866391e0c53af7dfd45ac26828e9c2a34ede99290e0075ea5fd1229456fac5e21edba2b16b98c84642892ee92d77ba49fdd2ed82d2ded75d0febeb144dcab70069cbc0b7451353992d42d38d99db4b250750149a492534d16cfe49fea1af79a1b02d25da4da09a103815b07a997ef7b271bcefb39c11291f41ee595eb3e93f173d2e9de4e675f040dcac73472cc0b65e121592b9ffdfe2c3a4b77675564cb48e10a365495d0a94796cc717e0946ba761308e10c3f6f11cafb396752c66d2a575c651449c2050244a34241fffc5b2dd9c71ac163e86b1816ec537e90e0be7e09411e78792c69c6fffd35abaf0d7aa2f9dfd4500718de5081a8bcc368717081660140f58577dc1d077701abe99be1338e564ff47c05f249b85d92de58cef003a16eeeaae4ebddf736e8f70d5f600fcc99bb21401961c0e56f9b772412585df53cc026729fe20f5b18b0c223ceaa97a1b5d2508a9ce5631ea384a7e7e85148ba5e470174526aaaf671ca9aee9def91ade0f5c8e7195067e7594b7d12e955d1c930d3c23f633568ac979e297800e8f9e384e056285b74504ebbb52fc6b49d65ab2104c2bc2ec73f8e1906b571ceba643159072fd19abfa5b65c5dcc3bda60913b1fdc3605a9398f8ffac5f9aa246474ec00ffca32002b167d4c75c148c95e457580fffa48cfa400de4be945ffb31340ce134f1ddb9ad4d6becf315077ebb7997dd2d8d27798f3b7213a0c1f155cbc34d64c37f29889870275b039bfe62f1d2dc0f66b510c10116f4af8c880273bdd3c66bd255fec249904bbeafa21426a22d0fc6df7d37f9d9862247749d5fd32e863b5994fccc86ae84ffc56de75b986e600074a5e0c617fa16bf2bfee30b6e0f6f9984dd04ea88a2a5d9fbfbb81bccae66cc15e3d5f603721a0a054801711031d0c59c881069497aa15fb765c07bb60f0f3f3e76240ea8ad7c04465d9a21972da52895e3ba4d2eae4835f55bbe6d725c8b29a5eef86e92932ba94cd5e5db46b925c80384f3890f9f1219e0110843329911a803a5fac562588d5f121a3df8efbb2630e881510ceb5781ef1fe55f43b34360c49e444b944917869b97260ae2494aa2b48275d4aedc3aa4924cf6d994baa5f0f22005d71038a83eddab4c1cda3985aca60302135ac0f259c3a18226ed3ff2edf579b75507f129a63a487033d292c8ef206670373cfb591a8aee2b251b0124425c3884a16559af16137115be15c054a078c1acf95121f41fc7ff031ce70ff8147b1d4d2e2806294711a6b7935d67b949224242e13d07952105b7c3fd145a816248a4900235649dab0a1700c00a97725523b35fe89ef07aaf3adf93adfa339770a2001039db61c6ba8f64b0c2520fd9fa8452d8d4395147c759574b7f99a8a81b628efe4e05b3f005f712357556cd95e00e0c0d5cd87023ff4c28fc3ae8eac6922e3a69d804c1abc4dc94925a8eff5776c1ada2f1120edd3f0e4f36ebb6bd1c8b32a6ad571511ee26ed94130903bac9d45669981fce989acf9f913fea418005510c8c85ac7f0dcb8181fb478548f52761500626ebadc67ece56c204b038f8474c7b1cb3995d7307bdf448e2076ba37600cc0335e1eae02cf7609bb040503524ee1e13bbda4d417aa356997ff29a315b0bf8a454fc4c68f5abcbb51c03b291f43593843b200aba8f689ffbb84bc574b4a54faf27b85b554724a4a31104a7e00840e8b84dd6b384d497b3c5bba0246e2a9d55326cb6411f4365d762f8004f299e5d93bc8dd4815121cc633b96db5e0d5b01629772dec5116b6b979dc8ea23583d0def9776f801371b5b5167b3d668632c8a2970db180b19295cd933c2333be39375b74f0c0e44fd3c7b12c4c25dcc62dc2cc44e805854733155360fdf484329256be245e64a99efb32f4f9b1b534e588117851d27ebb41fdf77c5b9d5817dd979fec7cb5012000a8069f21b8d590195f079a93dd77db5474a45e675df493028ea0fc1e47b4a3b406e38a93e9b2d4c7b371bad4046ab04a22184d4f8ad7750c536b824ae4c84b0de1185c63f737a39b84aa5dd51215d2c4e669b7a1689b7b63604a98909f5676b86c2f2802d786a887858776d18408f00795578ac7fc42c3343b206146cbfe5997f150a676e8ca84c0cad2ba2686c30079fe10609e0d80c58890fe1e1562a6974e4efcf0f586f807b99b06a44c1b4568ff78bd15c84c4eacb42ca3cd03f0163c86c135537a4fbe61445c457953ca531e736985d0a3729ce60125502887c70a74392b54a90e37b65a0f046546ca2d4752c7361e2cef83329de4abc3b30cb9394e04d0c1036dc974555d7825ef57a57720903c39452a4ecd3dcdbf130b8082dbf66759cca5fb77865927c0ba8e8a245b177240c8dde4dd0526192c25210fe16adcd4053f49edc4dabf2f4c06358c4a9d50d7549b12334b9fcf0c74a6411db55cd87876f4427bbf16f0511931182e5c62828be30e6dba566fb1018e4e6416f291dca89953fb54d9d14071c375a060fb7daf14142a4c7cb54d03ec23a48d8087b9c887ffdb80e922318b23b2bbee5b058c51d0dc990b7b321d1f87afb3d55a9af2acf44aae48b00a2d5fe4db2efb71aee7236959bd96f95d0fb353651fd045af3e9a724805123728d80d11a9cc992676a7ba871b8a134dbd838ee349c3436cd4531a933ed1b4e71a234c68089a8b7dbdc3751bc7f09e6fb4c594c80fa4d7ecf7ef0bc380df826fd7854506194a13f8e21c863b1609874dd8973bd2a78252c67c0eecc3b4b333fbc2215dc3db45e8c4b8c15a640cd9a5d441ad0d240d7e6e688777d957b5e1ae57bbe07369adfc8cd411d63a104af8a654d2258ec70bb8f4f7c38e43546eb936a9096810f87146de51c456ad6c022ba7ac89cc68fcd1d836f91a658c74321ba7c13598e3480602f8ee7625b6adeefbd5d78ca58e5cd1dc8a18174d334e5220dcf165d5c14a057f022eef752ca51197f6bc3a2c9a37794b989a25e33f95f8275c9c04b4d9610a4abf664878cd45044d51d882fc60efa3e280eee026e6b286b2ed534cbcc2c6c1856ab964a2825b6c655ddabb8686ae01ba9acbb14929499058cbe915c669653f54564a527e9107a2f722144277c53749033352016f58ee298f27de274ede7a92f2a146ac9148ac52e28a6a997900a18fa9e10a67093edbcea758e02642504a60b47da7e3e43f5feca433c964d1b367f43efdc69ea37dfb2c4aaca42c86050167f0eab5c84cf6a86d2f058827d187dccbb069b7e5ff24e1b8299e3cde69325623915a679a1024e33b4e021d18f452a0af7bac907d67b84ca9df0100ba692a122aa1783e721d984d0272e1971c460430b71873dede9b0002bd27e89fd24c613aa5fe8954b36b1f5fa8dde50c0c25aa6ad4f657ea44ee0c4525cf862e3ea30c06e14a09b77ba492531c548dd75dbaaad5d1fa1597ce4607ad0e24f6756c5bf0eac45b92a2c66df536f67a6c3cb29b3b40fb008aa509caffbcf6cd98d9db79bab94aebd1433ad982cd2e55384b4c6a23f9815a46b185af4067c2254c16cf556c68eb5f89f7f75b44e71f3da138fa316f70bed44f797ad887e5128851d8384e16c63bcd1297f9aa1d2b621977639d2eb1f7685273bbd288ecc409512020df625f393a1cd0442a807441c4f098173c6bba7d4c335fb915689ff9a601be3a73c32da664b42fbcba72e6fa36d926e38e984be6c4d43b0f12409db9ab7bebcaa073fa3dfea946b0bfbf4394a2e600369c62762fa74039fa0aea21ec9efff04ecae9b710a24c3104e2d9d79398b6f12cdb34b01c27d4bef0d2ab0731fbc9219ad23e8e34c267d0163402f997e9aed14a973286d9470db7c9ff2d7f9a014e552504cf45e00f34d4d1695ea85223e509df8c51bbc8b6c30626bfd1db20ad1fa967f9f127e702456d321008acf5b0373cbf1f00fdc25a8ecda23c7db8d14ab90d02fdbde092e88fe746a7f03efcf9deb2dd48ba13521a19017e0dd9272669f2c90bf090f3cb7426a6b78e4e8401af2e2cdb843dfcfdd77aa07c0718da89da5ed092a65907a8be91ef814f35ff94e2ec064c1c8c20bdcf0bc2b44145ee07b0b720b15f04ad42a42f07ecb79980b8e0c181235416e07b53db12f973ef81e118c27c618b647aefb2ee881b0817cedc8ea06b75a71d1b23b5606ddb5527cce3f4cc46d86fc2ed8525db0b54341a1c229c1b2f90a41306453672e9299d8008632b69b90613e22e8cc5a50d0ac2e878d84a29523055422d67cf1e6b09f53a397bc107f8f84b5476ccf4b01dfa0fd54bb58531b9ae74d70abadf1c27427d64ea87bb388e13d30bef302f49c3ce82000b1b6fa469af5025c5cb5c252c294230d8d45f301ad6e1c8537d52f8ab0556119333badbc5d7a05af6cc17ac18eec3a294e393a572cb1f4c2a55e0687f3c26c0d0c10a91a6c69110bfba6e5056cd87c6f4e8f63b66878abc293fdfe0ac2505eb0e152b4c1aa5cfa26780a4903e258ca0b8249937e60eb994f3e9412c717d080832df6faa13ef0142221aac58228e1f923d6cb70a102b346d6cec3f1110fe04d8976f4ba5e61174b5c9d42645fb26ab793f965c02e8b6920344dee42b7a0fb6cc14557301c9bfd3231453b8685b89650b1d6acbe14c680b6215b2a73e342103969d842d86d9c5efca69f61bfe786ea412369ad815dd258771aec920675d1d8ae34ae3b0d764d63dd6868771ad74d23bba5a16e34d8258d75c1743792f92d0d0d7e6c3309a2be926ecc8cb671cbb30eeafa82d1c194bba754e166ca79df45d46e2f04b78040c2ba0a2f87b89704f416e22e1dec9d0167261598842695fdd149a50700a5f211343ddcf94ab3b8e0ba1c3357083dbb3b2709fb9ac426e8b3733a1274ab98fed17d289af84b228dd0cd26f10b6bd3315740c772790bfccdde1aeb6d707ada299c06d5d7f3fa934e52a92b85973a8e4c945ea5e2ce3288604b9b36c8e00548d381cb0ffffbddf160ae3da3a2b78c4ca23e1a652c06d6cddf08f61e58108a43e4c0d2444985910428c4b9e20e70dc68023f2437f9fbc89dec4d0125c6742d1b132bfbfef09de8a6d16b942151e57a6a426764ace7efc97d7ab81ac8757d9981ba9b5d1d4470fdc98e0ec4869e4baca222173d588ee058356f4f541851d67b7e783e88468167cb81be0ba1a33161314e7ea4a2c38e9f6acff78646ccb8fa62d3716410689e726aae7e16be7889d38a843b3c3f0ea016bc2e0a493136734fbfb55a7ecb3e88c7543279938301440aaeca0b23c7ea9b39adec84431e4e114011c333aabd459e60e3f632dd874bd50c8321af274235aaa473fc7c47d08738074f1f58285309a092ef264af732fe2631fd227603a998e877480426b98cc1ef233fd7eabad2065f70683d954250715d735651878f6b4e6cc2111003d4914d357d35919b0716970f879b56d2f4d456b550cc193ccca4adc17e027bf98adf491e96f1fd238d1d27ac08246511eb2bef83921b5186ba140178d254e3f0f936709b140bec607939ff0df9e9c65faa0e42e01da8b9c9c1f7b2e4f062e322868b2396bf808eebd890d2d1dc557c2d1d1d9501b9e516d735b9f0a5cbff7dc9629c8a856a5037fca6def9d0c1ea9ec80b062e2dbb50b5de8e779ae74830b984f55ad1a1cd2a6e79e3c56986abaf9fa225cad405561b671aab8de651fbe29c3e305204b2d7a092cb2c884f69672bf005ee5de745ad43f5717387d6f103ea8eaedd34fbe1908ecb22e971ef20cda87fd0a3a944dc7ef0af84414b5fc67a7de711911da53c30f07461e63f61ca7097268e71e610d1b5c3c2408b7012378a9433414fbc318c6f7ff6d8d52154243535c1e175869ca12d031a3e39322b92400757a730fd0a73b8deb82791fc52a5375fdafaee29ec7ef78c2d2996b43b453d45fbc1ced874dd5db4c724ba3b91d5bdee140a4cfdf93b8d70834b645a4564e79d06e2d729f3a4ba422f096dd798228643e38a466ed21ce1ff41723408e4f747c724647d2205096b7c9d632366eaa0ea506eee1be45f642af3c35e8dada01892efb26d7e7722f87e641f71f892fac0e52e73ac200eb9a13c2cd491ea324de50da514a8d9b98d56b3478909ff9eb04bebd38807a9fd92e3b8bd2a718d70b29bc8bdeb6c505e69a1c2773d1948fc2a0f05c77bfce4a428cab734d32b329d7162a01a9b7ff39508af4906fb0d571f8f1ae7bd4ae3b2cccd2af33f02278892c3b68199607a611434ed4934a278678a16f5964b8f11f787dfb60097c33795200e9875e90c452c898c7d688782686563d346c858148155822fe04d628e3521e737015e387d375ddf3dc558b39e16a0a2662ee1546c72c2379a349fac7c4940c21e7322639351807af57be27bee1ca7ef11c28274d20a816a604c01f236eccecf3729d115145628cf33224cf56e88d0183a735afacafd6fea0d64e417c2b207d4323969aaf266371181ffe1ae2eed5a918c29bdcdd4e6ca720816db489d5db7d83bdc54df03b33b4138be201c3ed63f2130cc362cf3d8abc3b83aa9881d9ce036ddf22bacf2af62f65ca8d2aad8c11683ed0649765f2917cc27aad703d6499bd744744a2a5f7bb1eb1d992610ed550f4a55dbd95a987423939752525c94225e516d6c9854a79026d355f9d9cb4c9440e578fe350774d78c6a5ea696be1ca9e37ae1aa0c3dbc9372d1234881130650132a8baabd4e3efa31d24a50e1c25e83f1d495b32a4bffb3e21a85053c2569e15c136ba72025a9040d9d61cbb784179b409e1ae251fa1849764d14971c0b56562d554cab3dd3189ebc9613efa990fa76c84c05f79a772a36dd21ab217905adcb913c920bab6d94fd62d6c3fadc1003b786ecd2e393ef0d482fc072ee8f97ce3bb2c7b3e5c2586edde2ce0197ffc0b0193093bbb593420991c6ed6432b552e9ab83585924667a128032b7641cf2c7e0028054abd8c6c5a6ea918cc1dfb2c703660405ad8b5fec13778ea61a264fec90022a53194d642c37c97f800ff9dd46e79a23d446d31e69b8691c51b4872236599c15243db4a777875502584261b3425cc88611c7a99c6a03f29b6f6ab0088f881f5b740925ae075dba1b78fb06ab9c79f57d8cd104bf12c51f68e13190eb6ab0ee2253045bd38a4b55e541213afbfca7162e8a4ff03fdd2ac8793809508d4d8ed5f441ccc9107d37e6097b36ed946b9bf225fb947fe9ff9ad9b9d993b3716937dcc03764c2191dece267cbeef66708f348aeccfe4f59a0ded882653e78752532de22ed966f9e022de03cf4a1dcb345ce2cc063ef97aed9b8504f3714d5c42797bcfd6023d2729bb8d53dabb629ac4d7d83f9b45b4c47fb1256b82378965dd3509b62a9597244320fed618336889c2ecd06529f4860c2108102768d5c508e229cb0884731ccfa72bc6e4424ad82af9975e48ceac86c06eaa47417956ade4a52da3de6eeed31c6d440661bcf6761bcd21db6e602b77917c3c775471c49e2a0dfdedea2ef8a4673011dd748ad9241eec300b87c64702f72e142b4b6ea3a6843e19ea39ce3cb0cd4a0c99cce961429ba3f9f634e1184d9ee24d5b23b78d3212755d90a68ecfaa41163b14e7a2a4f925d2e4296513e3929017297352996336c0909052e053b84b5abbad38c9eaaa42619b2486b7df73c7222d34cb4246cb59fdc012577120ba28aabb291c5a06b68a1065648a2977cf990efdb5b3cd952cf40783894ab4ed90314c554e4bc84eae9662b3a8a5609f67af8ea21af02d5499909158420a097e73650892d158a9189556d24ac4e55b57630ac6e2009f6ce105c9edb9d35e7e6a409fd5ee1315c16ea5b7fe224786b87e8929857588ea433159c40b989b640d98efbd87b8d8a5c0d6a8d04d38d8c34f93d1acf7e8d1458b25b1bd47b4af604cb2759630e60425104eb389314137d09c26358a53844a26a4e11ac452cec08a3bf4f510a4718b61a35ee572785291bd165ece53d5d2141ecae3562e55ca954834268df9df45dab338753a19cfe5a8b41e3dbab92c642b285f435057ec644975384f883d4e79d224192fc24f2945172f1c4394b1c85c3425f83a56539327442dfef819724c1738c873dc818dd208c4d59418731e3103876e101a82371887a42fcc9ac421bbf344d482f35b074698e4fa6893c8080b8d12788acea47574ad6f824b15f02700c42870cdff4f1056c4a5b1fab6c2fcda632f6981ee9a01b84bb3ec738776a190631618c2f05a312fe399aa9bf2ed1cc1c4ada2d4ab3f001ea8d0fe78bceaf8c08a7b226cb3e1319b3766dd606b2f9921eed163ec8bf1d05aef8435279ff34227cd66b2ba27e9bec7b1441800874709f22f36a1657dc719692c3c83b754461c05bc25c9a056505dbc12fc72a42b48579410be09add0a6acd669f24ff682744c5b1cebc41a4572536146d2fa4b7e3bca24aee987c79506c86e7ef72b05ef78b0abfd4c0dd905526f89ba0b5811d8a710c0a70e79871ac1210ac2cafdeed01a3f1427e5048c686463c741c9c84fc19f1e852fa2a593eea6c9f03306dee60662f8074cdaa087f8710dbba37df033de915a81e310c27722cfdb884e530ac30b73e03b8216481883fffcb9dffad59e8fcfd309b6bda7088038ebdaac3fd358a97e364a4421f9271aa19ece877ef4a1e538b1ffb075118231610b9bceae81570583fad155062c8a5ba7b969b270ee256840fd9f89b85a0c455f6b8e55c72c666b5b79b131d737d900114412aebc8215cdafbdd48838b15434b2fce64550519670a1fd81416d1aa514e143f96d92e71423cae69973a9699497c67c37e6ec823ac2c1e00fa7ca54a3c71638b1537b678b162c6165b5cacf1e28b1733b67871628d175f5c9c71c58a1333a67831583c6d40095f54bac6b87ff68a6f577fd9ad9740864c6f7c74e8ca720dd12420e593724d4ea21815053a89bacbe6b7c24022570d3f9991f8d2153621f2f6ef83e48c0f6c721585ac2922c438a1895aefe35c630cab1633b9d02e8fb5c4ea6d489276919579a6c8301d855e549e16f5264e2df2c2b098a530605df86ccdcc7483ff0d12a0a0eef0738d5f7881b3826c92a3c353b1f54156a4c3ca61dd0690d929d11ce9d6ea0fcf960108596dcb292081c612318d4e40be30d2a92f53d02774e4c8be60c5318fb5a7ba25469c06df33a91fd2a9cfdb29394df8c32a7e1ba0806daea094d592f43a3023e6d563b52a8e5b8070315e7eb421547599154c10d3ea2a28a2f9855699dcf5641a298c5e50021b2b22f223b914ec1e46347e8268c7475f677296d16b5a8ec7e6e13116b9a056afe145201416d95a4181b47162021db688150edae880d5d19da310e383a857f37a4c3b232c9455d26535e00b454411ff2f7f42a4354d8f60adfb651d05a08b1a0f01aa96489f0483ed7672a640a02d085705731bd6c80144311577a282bd09c38f4f21090102ed6ce1f7f1332e91c5e37dcdfa7d386d693e5a3f7d92e7f9017cf9a3e75101acce9f4b61b9237e09b5547d2bfcfa39b42e40b3569fe7491c7d2cb961b100a24abdcb71bebfd45d26c85f1eca6c802d7b9fa9305d64f817ed3841a958c18e7f48fa20a448e4170a8ef278f85d2277f4100ea61c03d4bb87bbd67b62c02b2492d1ec67f121f7f033ab0de4826cb23c03bb0c45ce8fdba3176619171d680e92d1308abf3d0ba3cba11ee0573c268867f5625505d441f7df12029612d6e2e157c9f4185ce96abdfc4cf5498261d5e291d1893b4a5572532846277ef47bb19baf5e10992d3752e918c5d507b59a5df39680936478c30041610ef6bdf109b78bbbcedf2afa6e3b7432741ec8108a64ab0146dc148f3df36bda5549bb209fa74d1217321f46dcd474afe36057c0366d1c0338b09dec0a848a1ad2015a256a7582a483b69b6ed0191beec2d84da74d67e8b066da44ac005b1f91505b0c6c1a68581a6d720f833b8e335f800d4d7a9056a69a908a582cbd84b6f42b8ca7615deb5cb19700ee9d3add5e21e10bcbf664e43579afc0d5e9670863e38dd1ebcc3468f85e00f985783055c6b9af5464a1a3ea823410b17512ba24ab120aecbfeea4c6fdc397db16e8cc52c86248223fb70bdcf27815f3770a5459d397d477b057d2c02ff12a2ab763083bbc0a59be40a993757885d03eeb699e105393576e29e284613f5a83d33a1b1213a3b8f1a51e5a0805dad38c9bb1dfda00e0092b78b7486df01aa76b4091337c436f70001787c8d323a166e0e32537b0643335634b56ef47182bfc2afd146cc13960140d5601cde17a76c41988c612186be6e414f40a7af450aa1580703f54d6815d0eca1431b7a83ff507bcb36881594e2759fd3d029a463a20da9507b6322a7f51f3b76c8c1af1950f8c51a62b9827d063e340d3f1ba3f3dba70501d8918a527babeeb269bfc8723ea19958c2f0c95bec967ebc5d96625a869e2a81232b1cd60545421e4a08abafeb8baefde3cd1f7366654843c6b4e955186253a5441f8a67cc0c9cca51397c7a8b11f28f41f86c021a3b08d73c99e3507deec2bc1a311351885eb9b0cca8ec3399755d4910057c7fc572c25536cd9f7da2e25c5320b52e449936e653d43837b27b8dee49528328b46fdd8dcadae99d2896c3e4d79ca4b55d4f964a9309290c4c14a213e01eca3d0d762338fbf9b4e3dc7d91cb97fbb409c9e6ba2c87f4b12657ab0a26e6ba4d860c614073b30ef361b90f7e77215e5eb872a405b216e2fad261b1fda2fba432953a3506a664c31cb27da63f4c4121d28c1783976e8fe1127cddf908ea293e594698065b627597610962438f78286c246002c4fd2ed2329f0c7a86d83c0a1e89cedb3b4570dce2a1ddf489b7b3de761497a0baae725e400b18e82f35778782374566c4a92da3d8e7a68bf6413862247866e67c16a21cef152a23901a0b853d3805fa2c45d352547d12e68fe74581836513fdc7db4b5e059ed08b9086bd44c9f29eae8ba0ba7e043a268b6bf88a7891b989ab1299af266a462396a26d7646f43f1c14fccfa543a61a5a1c6a08e38c66656eb788ac9f072d478faae983709cb976b096e043c171fef32d1415c54f863756ed5d6d9f1afd19bce06c4e9c134cd1b21e1fe34fcfbd33588eb02365cbf2b9be2e780e064aebf9e4b3b74931b7d4fa40393c801c0ae05e81ae2ba12c686f86ffb3de3f95b56610fbe9d80ab8be6a6ba8ec47122794ae5746cc26cbf069739f195fb67995cc77326fc339075b1e83860b3a1c14b04a3c6dc30fd4f1ccadcb0e12986f4bce714c1dcefdb481a4714cd0271638b052d44e933808388876b5ed559f38563b28db46167796b048714313ecf247b70a45b1811bf4716a55a4a01f395eeb9be660bd7a0e049675b1830e8f8fa10cacd25160304230808241d5cfc2705a57ee949c3c4a69a10b7d1ca033a58b39e3a380d3a293774d27f8efee653d19205df26d2ef5a4e621e0bcb8a6c4c61a4cd1248d876901ef996b8232fbac948ff4548149ede3ae8a063cb009290f904c60bc91fab4cfc3f0f5161ba179d84f97269ca13611bf6f0fec64d1f520f658d4dc7aa093ac2d19de24431095255d8e379d4df005685531e2bf9447183194770f26b0777bfb221e0c5abf64e4975ad90fa7f7f1d781db906dad5962a565771d96f19d0b3ff92c37561ad83d5b6d6fe343c3696abae364d4d2bd3c7ac32df1a62f6940c9b8c34a5087855f823019c7a2013aa2fa4c717ea4b79a015f6e616fce196c21ba80c4f7863435b45311afdaf04985345dbe2f807a4e93a1ad701b98d2945f3cb7086ce81a70331a10342435306a3604d402ad8e6df83373ec0b96249c3f297ca42b739ceee736e3167d7cb33fd8863d3156c514dae171c2373641535735cd79a8abfa5571d42c8bc0bc3b0d761aa54fb73bc0b1278ce8a27a74735cc3df1e380cf22ba602cef672d2745b6eb48437a2541cd67ed5ac14b577d6ce81c1e0c0b49fe393a8c4d084de540902104defb529196097d0efef626b60d195c3cabb591ebecea6bef04169697d967794084300cd063b2c445c89110fb981bca8b76d89c552fbac292c4f42b7a04034a3ed98004bb9a63fbc8456b5a6010757ddbcdf4e1efb21cabbfc908dbfdb8f1b33a6a05a2fdbfee5a3231c4f157cde55e0b162549e983cdca5a11d7f3bd71000174fb84afecc6f65337f620022a2b6798e3a89a4f13d5ed205d11634b7a183a1cde8786431c8f9cee0c2db58dbf7e161d5a826b45dd803163bca6f40de11097860d87c45ccc5111f30a71565058b998a82dd2ae9e51ddeacea81a422be5fa73e93001246fe815f12f5a0417b7ce82465442b3b957def2c8ebbbe1db567556c04bb4acc616706694309216babffd87504ee9bcf79330c29d0b7f9b87b7cb2d83ee6c3d6c7dd5a7262b654f618ccfc31007fc2271f4c0126a8d4e13b9a365947ded5425e9ea7ca3da9164eef8a18af4980004bd9052df3f69717a70f7543171cba232bfc648db6cd43762b40f419d33fb6c64ddeaff619fc65f38e0a065bfde363a7c5d0c7782ed77e2075a6ef7200d8f51989bda2d03a67036ab06b4adfcb07ef38a5672bb59ae607d181acc24cb16f1c8d7bd62c5f5c978a98b9d7f768a9cb2b326dab66b6993b8045ed7dea7c9a1fc0fab73593330ccd277394ecc9a2956f3d5d71c42bec07808d98a5c7045721fc3b7424cf43c22f2ef1859786b327c05d6500b8459cef6ecab228c23916c7a9a3b0f5ed1aa96e86a52bd9d7c608a763a9ae3db0947efb7df65201c1ce4ab71a06bf6545f11dce02d55a68c7abc6f23ba90ff59e32de7ec9df45deaa032062909eca0a7b9d0c6cc482ff4648bb5779446cc9af0eec998b831cecbd6406b17f90ffb899eaf311ba17d6170892419327384a9c2898788614ef74ba33f2b70618bc172037ec68a535df4a2640b49bc9b20dbcf6b712df8105556b44b5026b6015e218c7bf04bb1cd6c48d8ee7227696312118a98189dbbbe23686b08c4b9973e1beb11e98f9ca49ea41becad662c46a3f98c37a828c902393b4e6d159e2e002376febfda8866433a097f1eebcbe7704bcecb475615fb74064b63f1086e5adcb04f7f67c0e0d42061b73021097670057878166b1caf06b0407ffea9ce9122a0963bfeee95f04c7b88c615a620305da3d546bb5741768f5c7a06bccfb730bc4270973293af9f35116e1b8e0b2e60449d7c20f6923c9611e1605b2c0845944f427e1d2a79b88943d739b1ef3e622fa05ee189d05301fb080b6e8bc8a942bbc35a6fd93819aa62d4c9da5c186085c928588ae35e68151813cdff9f15357b43151619c41b1a8dc0b53eb2dc09776e2f0e5416310624307bf43a29b00cb758ddcfce337359f37faacf02c3d80c5c46c6b805180ecbc3a266ce39acfbdeca437efa01625f6e092aa0ee93c07f0cf984dc3cfa0fd89e7b0eb0a551e803233aa96fc21660a6082b312a950d263fb53d618a7d2e32734a5c75e2196056ee10a5c0ed126decc61810981e1f0d7f39894723cd41218ca78c51be3226d10f243e88dfa8e73b2622797f4c72c77c3a5dddf24488eb62e49e096362ec4ba1d7321dd6fe40cd5ea75293bffdd0ef8c97054e0ca0e6b96767d4ad94a402d402f557a52b13527d930af807149dc495649bd6a989eadf14f386e1c5c2ae29bc60b185618d9177f1b86175f53dd8dfc29527fc7323e9729c120aa45fefaa09e1047aaa7538f7848af113d12b255931f12d56cb1d416f49706bd7ef8ea466a7c3a1b7f0efd6c23866f783c5a71f5342662429d994e86096289c12dabaa1d5a5c9f3a4955e8cb3b2a53c3fb1db8f167ca933d4b08699c254f3f8d0bfd1a835beb5548e3257a3bd8a38ffe90ed41f0a4936bdaa02ec7c476835f3203c193b08559ab6bb63df410cb2750670cc910b0b789af7f31c7ac1c95e0735e3a8ae6db68923e1ba6ae558393fae03668add06f0bda30b5f41d0548760391681cb0c5e3455e03320d73e8470f3fd5d81bb6cf68144ff6cd6f623d23c7dc43dc77b7415538ed6a103f748ad6f2e7745158f7240040a9c7726219a10d227c3029e7f1f85330370812663a0f7bebf17d0fb0862f6a83c4a518015e4eab418b9830420db56a34ec4f4b5a3a67c1277aac42d87a2094eb4461518f9044e074039cae4213a4b9b1e58511f5a72a45b4d3091ef41371f992304ac3535275e3dc7cf11d076daef7cfe4001a884ee2253092c7ce2785ef01a0a498ae256b6f6d2c2052f5ff16b23346281b3954346b5b541973b47809aaf97ec32ef2b3b03645c58305750e8838305e5c4c550bf41c93cba14f52bd0816f5728b63becd50f6618eb97c455e8052ea8e712cb80e1b1148db18b944918c32a813c0196b84d2ff747da0dc1fc07ca67e65114a962c05c35d30eb41f70b392ff814025c2f15b5d609ded73b718afea36f20836367e21a6784bb39fe6535d7f8a4618a349a11076e83ccf0ccdbf81332ec518668ba82c7fee0509f3da07ba6d8fdd6286749703b60f440c340b70f4842c88df337db566f394d80efa10900210a4623e0d03e8f386461fec2cbae5ccbfe67874f53340f8e484922d5b40704912355d2b48f2976d027fd471ea05180da5b8bd3f037838bd42bc2bfee61b8443d0101b004e5bbf935e486ee35172681c47363ae0297553c2f30922baa105c08102eb0152c62ea556ce82b827745d1f4cb7f28132bd0776bf48157b2d7b5789bdafe45859dda175033f85f7cc437a52be675930060b9649ac8cb2cf8dc677b8ccc9461ee0ace6fa8a37761ab851621d790b7d2ffb1ad55d1b9bb9d25000174248eae4b47cb3cf7bc3342f705153ef9c03baf41a4ed48670a43dc43255252645c0afb6da5024d324de75e8407ba6cb38139d3131d884b6ba29888d975bdb6251d9a352599799c83eb9bded27d61a74b6b0468c314b270e3186feec7224fcde8c502d778cce602372f4cd5f72e862616568be9d8b93f26475743af5524f4d612715741111835d0e841fefe5beeb316e22081a05e5f9509fb79575d92d5421f03d186b6488c4fa61e713d66f0d097d7f5b90f835a3d40827319619ad6803b326b641b183c6b08034787451dc221003774a2a5aa1cc039ef8386bc0168c228940df3e2527247db022ec8d09f35542abb26230e5af439e054d33311364c235cb0e9f92a4efa0d28dd93f36b021bd27cbeb38ef69df8d4221ab333535c42180abdc7e9d6c7da4991f8bb1bdf642c75dcc37783d7773f3eccd7565948daa7aa501c74115a63b63375c00bce93948549004eb8353e18518b5d50bf6b2a5bc9b03f0781ad1064510e8bdb1f1cd6f31f9720185167d9c42323d17fef5cd90e91d513460553204255f33a03b4ac04869a2d7ac54899a17b59b6dab912951153425dfd178fb80377159dc8f427904646e249625ab2599ee5ef7039db1c427f065947b2c4f843e5f1707c1d5666aaf9faa5e0c1a7be67ed392258e770fb70da221f3c95cf318b7cbea886d40b070ddc60cec1128d174fc911bff2ad086c067b96559ba39b09958399f4051d7a36129588a40b290346690afe9f9b2a7ae1fc5452067cc6504f1375119f84f55f70f08a1653abbdb163446281f476dc65ed1135277e57fedafb23e62d5b791eee76120cdde7b3f07da1e5e2194b4bea18b0c77c74825614d3b07826af2f9735c1693898331a88598b864c931b4e129f8be88c81ac505fc5c17b6a67c584dd67af761567187cba9846665d0956a12fbabad0671fb81db896b3bcc003228892ed7380e330130d1e291858e3ac0f1452244eb7c19b48629b3fa78e63551bbf23f511dafd4f8d1b7919a643f43d1bd12604fa0047d7385d3f277f9d57a80ac2c252774b479b25b57f3fbeea5b08ff181fbbcfab20267729b60e991004096f2985c938dd2350a9710941b395ac359547902270aae9812acfc34bfbc50b63d8aa9c310a9fe285d14f2bfd16a7246d5e462aa82f85c8753d178668b0745bb74a3d5cc36708d01d14b2e5e307daa67b24d57b3716276475dbd56c9db5558f5b5a53048a8d7016b5f138df35a93482563d2a66cb475710420d4c87d9891967886e257b3e33cd83bb75b338241b4852c858051c166301e5a279dc9b99c874baf59cecdbab8ec9661c72205a05b72d9837e5e13a995cf0505472f3928f9803335cafadba31ed4adf6f454f08dba5e8c0abb5b90fb0fc5d76d14fea831fc7c15e3a680bf2eb8e26a8c7179f9cd5d80f7309dff634409cfb76460e465fd1583b901b447ede8892a341a65c38e8549957f282041697971f459cd87d4a3434b7f45ce40a31c36a462d3e809f1514d09ad0efa0c3dce01748936ef2404119a9b73a62a9c03f12245307e6bd55287ca8faa7a1e86b6078342334925f21edfcec35da94ac4ac1eee8b15a922ae04eae5b0dcc4661acdae211b9d1500ab17d191da1aca8225d580b5bf82d5c150ba5dbcd5999e2e3814232e195de9b4994e2d6fb2fe3f6b43b29d7956d2119573b0cfa34a1ed8133a57459422e3c4246c5dc60144cb293d4da98dc824a2e4cb1bf4ddee5679c99fd5cdc4b9503e0275963e13440be59303d4f4bdffe607d07d5f6c52aff76e4f1a5a8e9b42bba97835b4955f0468918674b04ba95591d98b420294f11d6236aa4648a4ca46245c9762b111f9c566d364818bce9ef120ab45d4ac598ead45986c4424e8247a28aad29e9da3c27ecca70bb37c166480435bd2028c6c5cd797888a6b8393689a296da0866b1024f792b6c875948687aaa40309a89b5f903cc2d2d63a4a1d3991c28e5f2d7f446dcb2e679691207299efc15ee87572a10ddfb335e6da67dd400b9b9b6256971209905189e6ab36884ae30d1949f5ce03c81a7d54d3e7d1f0079ab57d8158137b33d5342987d98dea08b4c127dd952ff40956d168a346685049f961ab6635c1627f386c36bde4c4064bd00acf7ffbaf413678faf70e08cc54103dfc6a6548bef5ccafa60c5f365503ce0e70bc581b414fb0c501880914d21e20d48c9f3e2799c3abd16b6b5a9401baa07715534955f87cfb1bb627a795b610d804019dba52416fe408ae2b44166595af440a700b5f43f086f34e6e6fce4afe82e0eb3126aba3aa32cb8bf9628828a5addc522562474375e72d4d6c790aa739e3a38aacda7a7b2c440109413084bbfae1f2f7c5bfd408d53b8b9bf755bda213cab3b9f3415f1f894b30b3dcc7a6046e7675f5b3a28310bb74eea35ed67c83b97a47d8ac920a2c782771984297ca9234b6f2bce007c35d6d6627baa2bfb3e5487a77f84528ec9d50c2e7b7a152569fc033cd9558d6bb026a08096ade932c627d33374690180e24095b4c8d0e7bbc4831cc4ce63583c7911b7e9679c2b91a3d8c064ea461f2735d1f15d99cba5576fcc87c32ac093a0422b14f808cab14f32fbbcad8826e391ea1e808e88866441cace1a483a270c74fae90904ba7fc11b9be8d405af0ea73f7184e6528196dacc5ee25e518e734c4f5cd3f9dbbad1546928bdead044612bc0d32068b7f301f9227a1ae0d35cf0b61377d1ac1b57798eccb17b6cee58f14890b28c58399f3e67f4229c90ab951fc9506b8899fcd6e95a49abe20fdd6999f26d3d026f92eeeeb27c6d83a8cc329714c8adedd8f8be95b323818af50dc2166cb0e7a295543b0c2499701b31dbdb57331cdba7276422b4c1e2608016d8442d3a370f4fa22f9e62ef35dcbeb27053f284ef57872f6d4b6213324f0cf654fe68dc88f97ce9161d51b28c6cf16662a0358a38541a985a8cc34ef20cfc706ff106a7a7211ea3f76f634c1886bd18320b8ef62307add33a82e79812d3ade33ba34dadd96cbc9be9cee55d4264fdafc7129598367ee9213f9c2ad63da7554e1ca7785b2f1ad0bac235c78f70d790b38c8f61a35a02e9c6e11daa8188e37be39d6b40560e24a8825dd7655b15d88ef41d79e30ff0e7a32041325f6b46f3dd4398b229c71619f8b50ea0a27cde209418d26b83a907589ebf708a84ec41cc30b124973dea5a37f2909d9d5bf2e496908abae571ce359fa4b437c86aeee02c08b246065a7760dff3c1cfa817c31c59f620cd32a746ee952b25b15e1200fe329b3bae1c686ac7429f0d48b0598b9d16f9760224fea9753703af5d12476fdd999d6918c2d77b2238827d6f518fadab2e6f7b3c98960ac5f2609cc8afddae54e3ac61833215132758ce405bbc0a70eeeb4314c5f37ae2e700ba73c19f170ee0524928db512abf67111c1890a6c2fe4c9a4b7f3eaf0f664382dc193dde435fbd0ca84cb1ac323ec2ac3f2996055a918d83f173cb9f27ec041173cf9cdbbb1e4742585fb45c982f3d71eedd28905336bb531b9a7279d52c413ed64ed02d9866a145e96c6e079e8784224639be5b264167f8de8104fba23c06ebd3246d0d787c87b5aa5bf1ebabe96868d3de3cd8b537c2af1c7fa889419a6c2695b909e04ce98b392aac68fdf84a4379f327da6aafbb8d3c5036506da0b7d30e8b5d151e223d0c13f2ca85f769f8c68f2c8b1a899fee278fb6d5c457a86357f3501c4469c0529931c5276d7f5f19d7ee25c5bf8f037a68bee0f3854fe83a5fb9aeaa96736f99b8bdfd7624b68ad9a8c7566321256631aa6e65a364c747d95f320c434d3f25ec2a8512dd1d04969ecee0de828e6cd640d7cf7e03ae114006ef0b50bcad3b878364e32a5b6ba0718a7e931e780c30ed043fda17f5f94d2752e7592ab4f855aae610111b735f6c724d24d3feabda48b575769dc18e22ba5d353165b8ed11b0823707f948cf601b6bae78f602e83ff6544dad28e6df16201a94fb608651f749882b14583c1289e71de9fc3a44a80f9c4afc50296fc1c1f6b24e1bfc4dc029589f742496ee228af185892891944eaf254a4c59aad4400541a1a1fd6f4f9afbdee8115e5d52007baba076e98b8cf4ecf6a6fb65311cac83939213f9c21cca0a7e156262e94b20544122ef7a958bc74cea9babafd90044db0f5d27acaefbc6d5b79691d4f9e2780825d3c7257fd1d848998b181e0cb17e4a3b18941c1eaf5b0e4d3fd8d2355f110d2fd96b1cf729b773378538e0a7d9f06f06ec5159bc73e9c5918ee88603cfc92a9da8e3c590507e71c265490d57c7e288c24e1be863b37d4c610b6a3f8009ddd810342ab732cdb7a6c6fefa1ee81a894d18f0a7a82daa61aa1f6094281551ede8e0dda645d886a8fb143ca2f88982efe046c1c9e0485717ccb3742b42709699bcc901389c43c287a22a6e0975ba5c417ea9a74ce4b827e05d0ccfe7e1cbc57d2b3f08feab6316383e79b359f46044a8971a434b8f1d4f2236d0f47960caa0b104502219ccb59742abbfd9f2ebfe70a15835ca071fe9d2fd1043f7d76b85cf11e72059e2d75f829cbfd20b0bf613f64c6bcf3eed0ac7f6b3a893ef2bb23fa7942bee9987124a8f8574fcc067d3bfe6264ddc47b73a2118575045a6d9b1d4ea6da1653b1b6d7318a941dd6fa4122949195437693d9082d22ea87e2b2f3b442ea40afede338f5d1b28f70c546367b0157433dc98812ace281107dd4152ffef22c5622b700049e48e453ab447670a7baf217f3b9483cca50605954fa48050feafae6c1796b54ee60e9ba8cbe326bc68981787c77ff7635dddb1aa0bb67b9ccc4c2ba0b1ed395fa2232e43a7e693794c321668085a794e762aba4afcd18f308adac9b057f2540acf40c648a8a4dab45b052725ea7a09fda677ae0ed3af346b9a93e69c81cfd28c00e584a55a37f812a9bdd854f3c0ab7781c931d232eb1c2b851a9babc4c74d240042138a776b05464252150e11bceaed4fbb0bc686e82a5d109d56c5805c49b5bbbb270f49440e654893116741a280feb56f114870080829707a10cfc1dcad865925e73908b3a83b720b39ecbcb52673ff4c34e45a8ec2068288951999c8cf3a48ba53476d6930e486c3d8de941788fd23034e4a0bedfbd7da502283fcbf91f6c4518b28ae2e6073c055aa25ebcf9d08fdbe30abefa1251f712f829905e3ed3b0c81cb78015003706355c889d75216dbb505d00c7c40367609d9b85f372a91cb1d65020c4d9c82d783a255c7d547b208084e2663199ba9f0e579dc841b20bb3cd92b2244614959c39d61b20e7f1ccd6e0a43fb25e22c650bde901ff21651bbc795030ad25548d1373e85d05fe4300b64b229d3273f60d90212bfef5178a98be82dbe7e561544febd003b29ed679fe3e421f9d90615194c1658c7c0969697951fdddb41050b1411b1c77ac5c7e8f25d68a92a514d0078b9014d2c800089e31f838b0333d8d6c46a7f94046096c0679d07fc242902f03edcb7624bac8eae969c6fb4708639672494a4f67365de1c6c40d00d56cbf2209e8e0eeac77fefc73a0e37a0e6ae344611fc25fdec90e778f48a20eda50a35e48256ad1595f6949dd1c7496b3a0ac517533cbcc9ad42e700cd815ac405215e82ef098b6b7ac9915377f5b94c7d23b1e451ee06a79384849596576bf183d8c09d4cbd2283ef3cb8777e5d772a6f9395db3037a67e52b35f693718d1c5e505c890260b051d63d84edbfebd560686c9921676b36cfc55c20ca35ae0eb8e11a177a0e634ef32e2253746a782015aea5a8ccba41199772830c160708a419067633b9d30bba933807eef33e30f406973113ed4cacecd1750b6190fff58b38daf22ab06ddb1f3bbd8fba2db0d4a25f969ffdac06ae3e086e8a80fa35dad9042023586c1c830ff7ca299eba01fc93f5f4956a2ba882fec6353a113299bd84c5916bd095493e584f6b180f81a63d5876f89b3d9d9413ad61fb50df11fe2fe24fa7b188e98d8e509daa844a3609ff1e16d1e76f8ea46dea77d83a8da4b8b8e5399bbd0774f4bca985dff8d098140cb8b72cd249c61fb29c4b710d05129665e4e016830ea1de913b46dd8bb95c2db5395125455efb85e17855aef5bbc6c1cfa31145a932d00721f9c33879bd15064f78a5ddb242432cf0ff0b5a0a989e336c7312d04829aecdf36169e89fa4d025cf08c63cc8e750977b2539f110887e395d631633e2fb4715e36587a0bde726c64c72fe0ea988ad9a0a08923e5a3bca925d466191d367fe7899cedfc3a647421b4a7cc2b72fd744dd78f3b9a791c8bd5c27b7a02058f9ec91e89999a5a8d1e86368baf6f8240822a4c7f7ed5be7d9504cde1c840612a0c1bf4c33c4141494b4928af983da6d36dc8475965827140d9c872c0dd1ba16a62b1c2d3a94fcc24c179cfebce3cf108355d1c09eacfa2355a51afaa8d92470707def06fe80f01109bc037aebb9f45f3e7ab738a1d7f23410175ef227bf8d835ca8e6f4842e231342a0fba57dcf6f54a308668480600cb9f1e063ddaf1e9a0189bb35373d5aca6402456f1b94300f976596f8ae37500bbb1cffb0d4052c066d148af94875720d8451a6273c99cce596b50a220bf8b3916e8921c8d1759f3d6058b2c0753a546bf566b1900fe7779f68f20d913127b24e9ca2f59cdbf57d71096918550496405f32896858561098065e98602d37ce9c580a14d4ef95db564ffbf615dadc30844a84c9d4f42b98b3a7ad2efa3afdc5297c3ad22bc8f344207df2d7a89d3c93a1ff3094e8cc9bed49727f6f07254371deed2d7d6e3244973bb33ee7252df97afd4984092602f83a4d470a9ca4e290ecdf57e5a3ef3fe27bd0a0892a56e271836e4605d812c28eec041fbb5f83ede7791b449d9550a3cb3b8907be5179631f0485ab0266991b348e48a077d630982254415027cb09e942586ebc9eba1beda2371f85b98f50ee28853a4c105e98e92c3abe90a7b015e85af54f4e49b30ba8eaa281184c3bbd466fa096526a0e4d680f3f71df0e5a29f544c8551b134233d564e9234484833c53796ffaf81807906377ac31a8cae12d313e0012b25c8ecc1605bdd5c305320ee64b4a22afa64b9d0964f3b6550e3c6f26375a1c6a2981876b588f3e9b6f026f2ddbde7648e686af1f01179a46109bf2fad16a3b69044923b49addf9816164347449f27671dadecee680886ed7b623ae0c5549a95d2a00a58f9f09585166515c8658d34c9e25b92d4ec959cb3d1fd0a68fc7f50d22b826bcb55721acc6b226fa226a2cf5644d379840ac7ef90c0c3924a333b181d7381ef3519d9551c82a810f952acc5032555022d70d64a9c4c0694cb7876d0ee1278e207b34bcc18b91a48ccdbb1ce24049c0bcfc2af17d00b461053ba5407210c6838ea5e113fe544553627c1b6aed512217192088b9d86b02bdb00af465afed0069340212c933d3f677e1806a6a78755c80570cd60ff3838f1770f18d84d0b5375135cffbf4143e9404c682756475f4bd0cbc129c2b45912fcc6c3cf22dc92b94f280497a847c523676ddf6dda7fbb9bb0535f4c9b366e467761219536fb2cb310048e2f4721ff8bd3fd12f5e0120b1f25abee2c318aa903fb36217240a84638511112de160703851000aeb5b29dcc1e649eaa42744e71855919a0d360700d2506326531d6a964bc97d5969a7261068e194a8d366885ae64440d9eaa830600e423700673c4f1957b111e315eaf2a5f12da874d994307c3ecc4b7e08d2704f3bbbeaef9968fc25c3fed325a878581b01dc9a1118c62744b5e61c2a0a52030b714367ff9474a603febf52e6094fc29c257d10c565550c22d14b7081e21ddc2d2ef1ebc600cf751dc19af67ac86070d80528538e27807ea8aed04aec8353d8fa1c9de9eca1cd48d8ab4234945255c6683bc8960e6fe199056b294ff2fbd7ebaa33b325e8d5b05740517f42277fbc55434f670d34b0ab576faab32981398dca24b8ecc7c8e31908cb2e6406e446bac7c63539a0ee3086f882cd3acc128eb08ef34dcec5f90e6d8aed52e793bb0dc1bedeea7521dd3a481db3f9d550cf392655b297bdb44659cac040fc12be2cc69c16f875cd26e197f00354864965cfd829846cdf0af58d78aaa2e0cb66c48d6c3e636bbe6df292e62dba9e21b8925b64bdfea254afcc63a1a0c7a93f46574e37b6db4fc5636014c0aca8ab0fb041e0ae27f6e0e3e67127ad7e42c68afd4dec935482cd1d97d8d40c8673b8472be98b7e72fe174462c6cdfc0d4b0cd41c756d73ffbc936325f2315f349924a356c62869763cc94b67a257596f051a5df24db7add8c0bdfd6083f5911599d325bae0a297d59f6023186a921cacd9e20634ea31809b7c30c61d9b1eb914ff3d94345513660a5c4078fe5c289082f573ee39473713183e1fe88e1f1cdef6dc1efd122c5f9fe8352b570212c0bd7609c437430feb2713dc329548c361ecd13b7ce1f2bf3aed689b69a5e7ad2922336ca2de918e3905856354e94b78ffc29ac4bfabd074c5c5329c68ccd0e9a4db86f8acefa15c5fde2e46b1dcdae93d5977061fdeaded28e9bbcca92e23ffa3332913d0d99ab648d9cfdcd6eec610e181abc18895c3ced82d564ca7be8ef9b45bbd2909307aa262c0391a0c21cd28c8bae16eddc5b712e1195805c495af0309841c8bd835180a7f822d2b423fa2be7ccb0c1bae1cf4dae1ed2eb6b174dd5d67c0339c70b69503e0c7e4158b682f5e6c5ef0fe625d8ab983cfad431602e3eb2321fc47583712b61c221eb30340f58337d2f8b6e38d8490b4c7290a2dde38ce898d8193294722a343f9e43ae6cc7374e5c10f3f223e6f4e4686a7a99aebc56ca4206a441fe2815325888896b2cbcc8c3a55801c22d3d393fd620eb972d8ae15b76d81e6cf645b61929ca4ff2d73d421a83eeefe012d01425bf66e8e6b94721083d084c1eca9f5604b98df779c072ae795a22672c96f00fbe1e3def533bb65170aeaa8f9e1b3a6428794d3a900d1eb91a0e24b5803d981f728ef343c5dfba59fdc7c1bc31ee8241b91d2179f7c005c9b85023f6b481729fbc74c85b85b6a18660c8564857bb494d24714b23a4d2ee0851f50d2c70f0ee222d96fa4f5619bdd08f5245855fe18c1e5706a3e17f9c0d6ca362b44551106fac2af47343e4f01fa7103e263faa84e81a8215667caec395b8e5dac707ab3fb53aa88ee5b45a984a324865112b208d34df25610a994f0f1bbe12344b0cbf41515d848cf750454e34c8c7b13478cfc42c9e309720bbcec80312a29844e4191b48137aed2f395b6a52ef594eb8a683541f59ed5998b845baec1f24e47b0ce16680b641553eb148c77516e894c41e219be26d9cb2926d18c5ba737fc81a1a0087d61115216090d889f9f19f0d85098793e4edfad9fb0b720c3f3acaf17b87bb90ebfaaf3bc72cafd3d5c0ae7e0dd462dc39f88f2f4953cbc9aff8611ad936f4682f1938e2a6ffe8ddf5646979030f9c9cbcb0baca3480b3df9c42bb0063bd8773ea7855ec6e0ce6dcfe0f07b6e6322e4273032a4115773e14a17cbd2a2446311769b12b9885fba251d79a555f089d337035442de8716221f6399799e39cad3638559a7b0d75af7e64090b1c1a76921a3eee7666a3fb2e8a3a21ffba77db29b5b8141927d68fdc9464b2cd384e2657135a44c65603830a5618d9eec19dd3e47d0eac61e378eb475cbda24cc27d23362cf1acf0e12a807564b15a75e710b6cfc010a33753f60508eb26d0e6b351e9ec86702a736ff899e10b52c90f0c8fa099e3260389ed0d209a4e3798141b94b86a44be276a80de12082fc173cfc5a00d0bb85b7d1fca72cb028bb135caaeeaf30eeaaffcd96d9c4d40002f46627f1bb6bb6b79432492903e603dc03c103d6bede30c899192819167538165d3c638e69256c1e1c9b8deb59e16ed9e6e9689dd14cee70acc3446378c3ae8d5213a5a4182d17d3748b055a3870a175360cd8f87d6d8f16c2f73b56bb5e74822565155f1ecdb3d9b8b67e7a298ed13d810123fe9c67dc8f6692d096decb8247550613dad6a1a5e642c602c45a6b9f5a6badb5d65a6b2b0cdf3d320a002f41887e3bd653adb2e3575f63ec5d9252b275bb604b7486e5a592918b058c8872a6e26b8ff7de910c0c2ee09d8e306032458beb5e2653cc3d05674a1253a2702864a0ffff96ac96c579a70c654d1dc348de1792ea27070b2aa2d678f17e3c80413be49ca9c8a499128944fa170ed4dac472dea6c8832b423aeb11e88ecd2237c457fc36680104a42443a3163fc4a450c9d9a2a2c0fed7cfa75aa37ed6c003a17b3e85f9148ab8bcae10f19d62e546603f3dde4a4ca9c4f1a03b6e9552addbc2567cd01db74c7de35d1247b2343d261e42f70d3ee8058b7168bbbc18690c632108733e2ab81826269fb21763b7c6f2e95f3e3ad8229e3b7358973b028332903920722cebca54f9944ff96473e52ec7906748e5533e1dd13d668cc1728ea19167a07b8c3e5d5b95ad72276c39699f138861b59e973d3105ef616674f66e704f74e8411b6089e1057580d278fc79812586f94396ad0b9483a5d845627b73e566614c3b6f5da86434c6599e287caa74e7799e3a74e8c0202074a7670f81db1d836198057d00e3900e971732c0309644777a76e7cdabcc86b302440811202a8744891a8290729e6942690674a7a3492507026709a1cc0d0e4b0811204244c699171068935028c4449b5a775c9d5c789c900afa8105ba63f38465c2f14d1363139b246c62a020ba6313336fd6999233c69d0cd905bae7ce64f5dc61149098ac13a782501da3a4121c0f93f7352aa0d6c021864b9b8259bb14ccceec521d0aff2283eeb903adc9cb8a5c3843c2edbe516bfceeb0ea5f2a127bd3755dd7755dd7755d07ea9e3b24d6f443f8909c3241de73a7b2a69e895d8d0db72f18709f771ed809cf461740b49c6baab6505fa8271d3ae88c262a14a1c5e13ccff3739e2123f0448622ca64193a838b5266241606dfd897f33c2d8b87e0fd11823844e1f3029535f56bb3412c14af912ccd560e97824b06248356c3bae1a020bd0e0804ad9649e6c6e72c09dd738ee7ad367a922a72759e55a5e3f383ee993c5be48a8524c94c922449dec86499c93c927142f74c8667eee8494f7a5a11f29e4b6bea1ac85510254be176e3110e4b3a74187b44300bba672f0bada963d98d35f5bd312b5aaa345b673ee514aa1c42661b8b613ce89e3d56288ebcee5e22792d79ddff51a27bf6b6e889a5d93a31c6e14d200a22e30d7ce31303ddb3277a58db32c7c4d71e56cabf7feddb6d2f232c0dba5fdaa839b126d29aba3d45a1820294c6ef0bb4a78711314a84ffad279df1fb026b8d3baa31a022fcfe2b46f0373f25d0fd02b9d6b2a6dfbffb05f21f5d2a28bcee2a5657785678ddfdfaf07672c199a0e65c9a31c0084d483bef50773a9a25038cb0f38e42773a921023521c6e656e8021aa79c642880a5cad562b8c3f1c4782f056784567489183affd0cc91ee35889d95be5d5157a5f92247194709024c955a13b2549906d6a8dcfab1b330ba034eecec02cccc46ccc486ef1571cb5c6570ae59385ee9414f3ea570f43fff3eaf32aaff2cae6921b428736268ea5f005198db163841964e199b0824b1213b24d33d184153213b291915aa372faa24ec7d66e8116ded978e31777db18fb6ebb7dba0a8326b05fc12ae44cf094c25532f702ecea01c34036375023a020419b08419718a14976d092a851f07a650a2da0a8539ca82950c2513ef8169d99083c21cac8f4106a276bea5a8bdbad9ddceeedd43ab7d3967a186db38c97876ba186641c11194ae36a1f24afa1f806fab694b9819d929d929d92295e3bbd21499254752c3a48c9a80c520973b640d5b4901411bbdcb928cb3297b92c73994b100c24496aee862d308e5be99e4b1b991b64ebec38b2ac5560d80707ecc491e312a15056604a15250e1c2023bae7923cb7b0a60db6ee2d0298a8e13ef04869413174b5c3b865058358a89d3575edabd1424dac5f05b54f03b5b0d610374de4116157087244e00f8b822488874f0d1e583fac65856ba27b6ed55853a7a70e6bfafda981e7b3c2524cd350c6a160d482b4360e04505c7c44a0413470dc129f8f2afc7fd007a7d5d2b292d23db77616f388a3f9a034eee5000b00b45a2d9c5b5f255b0f53c1af55bfd6985b9f148472300e5b4a8cc04a05eb87eb3745f7dcd27c58abd558cdc66a438ad08a701d686387c75b1a35e4504f5b438266ddd6733fdc1b31319f2962ba1a7d5718e3d26980116440da787740777c92a059378f37f8c432e058bae373db642b6bea24e8500cbe422d4ee1c1934e29a5f4ba59c6137cd319ffff9852fadfc27fd7dbc269a682534a29f5800a5d37bd17a33b385625f3408a6affe26caffd2d4f29859175346d497457194e2a6eb957df7b6534b9ffffdff25f6dc79bf2fb5bc6debde39a41ebe28b45199928e1bdf7de7bf1a62449f85756733c4c3692a961299324ac35cbaa1332363c9e8bf386d1d242c72cd3b28baf6c09bef6e38bca50b1330a6557f4fc13702349784732b556259bf9f7627c45715dd7755dd7b58917cb8d8b6b5f8ff385fddf60ff5f820edd9efa1e4f8631c6348ded8ad52c0b3d4ce77e47d3f0de30a6176358cdb2e839cf3e5f37b6d27b5972ddfe5f64e92c51b5979a4fc307d2f07d20087e56efbdd90c1de9c8b54063f33b1bdbbf714e09d8fbfe25dfdf33b5461e71f7ff4159e8bbf3c62a6c3343c919bd6a4274c209100c6700c119401004c1b045d74083ca9bf2abf178aa029ef1cea3170ca0417934288fd66bab60ad89f23ca2417934a89535f57ab2bf69502b9ed1a03e4ba2fcff6950452c89721ad4d568509ff52c8ce634a8219c06b5e276a341ad284783a2410df9110dea5ab12ebe30c6b8096df1755d178631be17637c31be70cdf5e27bb1063defc361e5e1b05a799ef75589b200ca6f159cff6fc0f3e06f2d8f8cd2ed7757c1f2abd17ab295d2968bdfd2a7d5565a29a574db8da37674354e0204c3acba37df9c55b546fefd1708ff4dedff2542f7acfa70eb3b6e9918988119187a4aa0526113a3d86c991734e6acfa8c7437d5dd1afbf4cd1fc803fabbd9ace126078920715b6daa4d051ab5284030ec3eb0fb3e10044392c425909104328e2449e232f3b2efedf7f7ef5f2cbb248d85344807ddb199d5bccdeade22fc8ab06ab4fbed6f6adf665ee6b5b0b935218e220c61288a221eff3442d36f9da7c64e8d5d394f9bf3b439cff33cf369c912294e97dcd4dcad395d7283e255582e1e095dc5c66a39df1de2f10112fa8e76d796147a41b7843476b1694d3c3a4e99b43ccd29939607f8a96e0eb8b7e5d2c75aac386552a5080f71ca240b1af0ec2bc6a35e9ccd2452b8203e4cdc2a3e33a04c500bd4b4b9cc2b44a03b36b3212c5ee236b4332fe312665eb14329f3d13d8cb5093f3c78564574c722076583af317e42f7fdfbf7efdfbfb98897608e06ba63514a694dbf3170453cc4bed4fd188f76ac600256fd8ba755e452696c338e75ac631d47521db92b18a67528b43789f45d89999a64634a1286fd0fdd43dc86b82b544e8664cec9694db4f4ae9336ada99332155c478ce58ceb18448726dcaa6a976bd7d201dd313e7638ded80ad7a9888d8d5249605dd07de3e414ceea4f210e26decf123678ef7ded546aa7b6b7bfdca5766a7b39f52f9c14d7a531ac05dd774ead52a99410389f844731af4bb56bb0a14b5d290de8be5337ac9ee170dbb3108b8f89d684634d79e358ebc46216efbb0831ec60cb8112a2285e376e90c4a7ff2283182926959cf1f5b820e695d7e28288e01df4f5926911dda9585ed70dba845226ba53b1947293008c5d28e1a8c040ef182596624d3d84520c52081bdba17ff97d6591f658fd507d9ac49af01879b70bbc407bb21ef830f6024f7a7581f6f401ad914af0ba99284d603e69f1ae9ddc08af5b0824b869fa50cdba85ee9524496b3b1b839dc19eec091403ddab782a2f7bbac40e7240a9a2bc222f3486d9a03b164d8009d1f9b4c0f38992817e68a0203650c702bac1c587069a1b0213b9213e1f0e5c221f3ff01e83c44b94ee6d3fa059c8c11c0dca8725518e457b3bbc8a70d7022262838f83cf1400808e843816b87c82e800e4c30b24608c01e0420180bc012cd95c12a29bfc320281079f2362808410600a199c0f1fc440a2f2f26952004e091072cea01e301c829673fe1899f180138060d8fac0d6f78120185e01c130a4431ca8038cd21ef8b3a6de12daa11d0a59619aad20a519a42c4dd3bc4292e547921f499224598ae258238a35a2288ae2686e66cda6ccefbf31a5920674bf52243ae3f7957a01e17261874258e87ea5b6d29a3a9649de4b29cdc260fb4bd8663337f3eaaed4d662595913db2fb4ab6d16bab1ec13366deff8bd8d4e60d976b4c36e9611cfaeb6b5118c18518988ee3038de18e7bcb17b6d695770317651addf7d5ddabeec66c38355962d01bab7ba7bd0bd69598707bf2a5c9e8dec0aaa089e1eeaee887aa01babfbd26cb53cc254741ce3f7c3f2c62e90ed38a23bdd82ffa64cb4f85e72c8603ceab475ed7b3dc639be7ed674f78f5aa3dac0fc12fdfed2c3b02f8fd9c8f130bf53d0b1ff72278a9c1c044e9954a172817a801c34cbe79da56cb7afa68dae25613ae38a5448e1d49d863294c609d694eddf38ac49443f8ef7d572b0ec4cba441fb040dbedc5b6e5781850eb743a6540859335551f5a9cb4a665845239e534e19d9e3941f05e5103f88de50a51263929fe1b863581c07f9ff0b55efb8a5e5ca319548fef817177b4c3d355b099086f36b2eb4504b604503d740f17a315487e6d1e3c8b2e2d8b36962d6379f07bf936b23faa089ead897af5b8a788c733a296d4c1b68cc576dd2ec6356bba59a3d5cb4129a594562f877df94e4b4a3fcd726796512a60506bbf5e8c73be2e0c6bd2e9641e0fce326ddb2aaeb5561616120bfab78baf21ad31c6dce70513fa471d2301816aad2050cb66a3830f444d9ba0402ec20bda135ad27397908b8b0b0038d8c0c5a505e4e2e2f271e1b48b8b4ba73ac0c36fe7f15944e258d42bdd1ae0e1d707fce606e33cb2264e8362d9ca628522ce632c5a12e534a8d19228b734281cecc6c2d03064b1404ba234a8d09228ff9c3ca267b4425be1113da247f4889e511447194f4451c6135114457104c170efbd474074df1b0441d206497b8747642f7f40e4555e652f7bf923c9d28691b4611c49922caf8060f803047f80200882e69123430c41529156385ffc9b546be4df7f7ff702680c43a23b89ffde2a15a9442a91481d2c8c885248010a119253dd11fa889ccaa99cca9d5903254f9e80201816f181457c1f0882a1e77de0ca03572bcff3be2b2459b24692358e244996579070e20434cd560ca61983699aa6d922c9f20692bc81244992069514a8644d9b2b1d640320800000b3160000180c08078562912c8d23b1c7f814000e4f6a3c5e5240368ec762e1502847511444311883000c02010c0200a48c430e297c004a88ce44bf8ab106874144fd3b54967914ba45556ca8e1d83862b39986bda97098b486de66dba4e9c0c434be265ae3d784687c5c5cd3b789687c5c98a6af09d1b04fd101e6e646a7e3e1cd9f2477ff83c62855cd91d70bca9e1a8fcedfb5986e0854be060d0b384c48f788f82344737ed2f90ecd9c5b2516d7399658832748a89e458873e34762e2604cea91ab4b409b815f2384c4a29da67b7e59167bf8df016e60f38625a434b83cdfbc33014d5f13adf175219a3e26aef1ed229a3e26dcf8bab8e96322353e2e40d3c7c48d8f9ffa0b2bfa68d2b9b551ca0145475b350c958a37cae6b746cdf269de9cb0f135319a1f276e7c98b8f9718234be26a479becd25819152ac1c46306d764d22320d8072d7c78525ef5b8391550db325ec05ad7a5194c302565488db5692fc5628691846025520d0cceb9e829325ad24052e0c7e92b8fa7e20584b0d6b5f93d0adb1250d4d8e0501444ad4a10009ab8446c7ed807918cb9a39518b66fe8bd3803d2120c2b86a8344575a1c0c46e0917f2127576dfa202fd3aa12796c03a0329a7fe36082cfdf48e939385dab2761358ca1de56f6f47b9878a9d409e29861e94727ba63b26f2d7b9e74a9d009833163e9452fbb3e76806af4fbf1a7bf961ee35d6051290304f68fe86f21b85dda231074375729024369ef2bb00bd0184ce1797242fc6833cccf8ed1f0a732da80ccfa7b36a7c4a2de56830f180a3063a8975ae379ea0126ed20965db86b319637f2222df1d827fd500ea18b0accb6a7533402f6ab00c30f4e12387ecdc0af83422d12fa88fde6c518d49455b375d3a9520fbf33f2586cab77c76671f7e4c2b4dd33d53e60b0e7027007f5d2f62645d888ba89f88300f08e464781329d13c2430150bcd5130ff94f7fd86c0af989ea01239b577847e6f276ded9132e79cd1ad9d341ea3bb4b23d260332ade21a31be1901bf5c00823c9488cfd9b3e4d37b30b2ada007a772b5a1be91b995724bc6882011238848fed057a5576fc9a3bcefaf5e39d1e8598c86e8492a79cef7ae5163a571a18e953d370589e9ca098c2ddfdaf92556b611df1e6b40bcea95073f04072347059f3632c33160d6261f2cc192a152aac15697c366db5d88e123ffe06ff5ae17d33acd65becbd357e72b5cd8db582e06693a1f504cd22aac9ba52d272fad68fdd4c00988e0cfbf163f24c7e5d1672d39d1c4155b8b40548dca1951716fd64dea7a7ebedd33ea6d104b214ced1ac4f76d7bd583607ad25584241ec9758a36aa18d26cad19c1af63bdd1a2f4260159dc0cdc67d2ed49002b1f3e32c9b4436e42962bf7c8c58428dc5ec0865181d76ccf165dc030bd5a3402c68bbc27146367435415a1ec211b1c4f4fc26db54706c34782ca2816fd8314e2433d9057063292b3079d1f05cd5b9a845cedae96441fe0f17b900aa770ee7ed5100cd8727a3e1a413d13c03b9cd0ad0da0b040257796240422fd270a3bf2fd4ac4d11ceb50cb1a84905edd1128799c89263016f4f762943c69e4a63f558a01038c8c4bcde3ec777bfdb27e1afdc4bbf89c65e563fc0eba1c9e55318e2b0b100ffa4fc9c09cf96485bdef7da31113e520156ba49951bc8e6908a67717c43293c61f5a8d3ee8a390c0e9ce3f8a93877c7b6727102d38f7281c7e18ce28607d849413e753bbf35b2cdfeef8ec8c09f5c3513cbb91bd1b294a8316cae7ec7b708099b0dd2420642e28adef32f9890bee5b0cd6e3bc6da52f88924a15b7c456f979676687463da9b28cc6e3c6aef0982d7b1bc34596860813afeb6d87a56cd0668ae0e1ea9410431995cb02b6b2e3c6219c4d4bc25aaaf52dae73ba62108d9b241159f62f10120b46352003e0e79bd68345b2b875b9b258c459908c5ff2995dd27b6cf46f1be6739741e2a5bb621694a910f89cd2ea5dc1b4737f364fce3eb8469aa413c15f1cce289d5720f1c9b5d9ba25607e39e9245bc3168d6ba2b5f3ef0fbe01f4ceb1d111df4083c4c9e632bb0cabdd3998ba6020149ee8e232578bbce050bded2e493249879d2fe484c464fd89c9014cd84958d15d589fb727f9ec85f34ec85f32977b967ab7b0305984252cc22f2966d7454f94478b90985c9348325bfea736a5fff7b38057ea033d5568fe05f698f2c2a4a467d45095564d44dd4a975d2a892f9544c627ee7f3b7c56ba4ca2d7cf4511f3db8d99c6b0cbc953d45082852c087440e9d65a88deb6d75053fd5f01457e779be118c495de20ebecd6d7c5bd16f473a7871184271772baf86c0ae6c79343c10d60208a681881967a163d99e1d34627240ff87278e06b198bbe0413593bc734e5de72c1b3bc1a672f350b5764ed8db3fac4c60eb5e49f6b8a651b263cf9deb7626ef8ad042d7de0cb46f06daa82e9d45cc8c44ed5bead4de0b94572b5bef2b084f7756eddda2aac61484968d63093051edc8cd811d05cf8d0e864b20cef93dbe5e0c2f85990f33abace889a3cb652b57b33d1d749738f3ca1721b34384d9d219c2fb0f02070ac6d0ac6911d45b6a300531bcdecd88a3bf244008d002a0a0ae88fd6bd3e25e3312fede1dc0d0be4d5d0800a0a286a919ae7460468f0521a133c8a26883f14093180fe96e3c2b7f56541b3e2f878902b29c20a5c91c31a58d4559fc45d9f5df4006a85cf4911e44bfaca1d9c9fb76bcea957ec7215672acd333d140fbd9ca44b2f25e7e7047a1a1c58b9a5991a0e75c72ba6c9323dea27fb00e0e883e94fdc78581a501fa4e80801cdc70cf3d3d05271140c92d5c7003b79c292cc850f3d2a10f212dcebc458cf5692ae2671c3816d6780fadd16dd25994c883ecf469a08bfee1d3ee56aadc5d4ef46585304b0166d686cf1a4faddca71c2a801d98c469ec72aa67ab77bc7e5dc55c23215257d59097d7630c6ea76cb247fe3ee838d75b692747fe36182bfaf41e93b7576ab5e92d5b1fd51b793d33af11d843ec720fe39bbdd5cfb6b5513e2a3403135a2f4006b27d252c18d638ed5a240783c9954dd4c14863dbb62e9f9d40a21a5329ed1434d055f59a35397fa3b016d3a87107f3aa74243175e005787c97c3524f4687dfa2ba6669217c6cfd78a263f6776ee8f672c9e006ce73512fe6ef6c4ddc51cb443b99e5602df19a31a31b560f55ef05a5229468c7bf30b1957ee8035c0643f289a993ecea334038a65bf575a95b54b11ce7a2ed6d685cf43c215f2d2dc5b7f83af112ecaab05abf7c3b7c5976ac42aec3c6f21a1f71b0326f87a0dcacf81dbad9e63951d29badf1830a1cf6b507e0eb95da817c08eed8dbca594d67f4c88f0ef372abdce4e97ea0b4c3cb607a5b468b7cd22a518c11c775e6f93d17cf19c842ec720f639baddeab3d2b6b1272543ebc53909393e43c4bc6bbb25349278597935275494790568735c9f6c89bd6256335d39f74398c8230563633f01e73f0e4cf0f71a949e03a75bbdc0386b921a5973d760fb81f0d15f041d51a0c403dc867c5ad63460c2827d4258fccd8852ebfca977c54eab7b04f29df2d7691db4a21e8861832b37ed7c63a7edd81163b25cd86156b8333096820624fac63ffc22f04a1ae36dc09ec9297509e6d2b85ae0a54c2cc461f897cb70330c8ddc0b198ad32af8623653af437216d5cdd778c8ef8083b3e47ba885bead9ff472a51b37a8e92e069fc07406b032ce4e34ded27d61f16221462d30179d71fe30bfccc58c674e915a4b0206906d2f2d030444a54b6404c86b7c8f5e4e7dd35626451a2eabf783c7924aed2449f70326a13bc63b085c8791d87372bbebe4ccd73558bf27edf98bab60d77e6561f6d041ebf5fce3323da30a1ec6b9351369b3d97addabfaa9d61c022d4b1be454ac5f2908e58b3b8617b4b0a85a511ce51a8853182339a2e2e748cf14e18c44b2d8781998e4559fbf0b257d2588d6783b99ac8b925434840d86181123fc3bfc40db3a88b84089e4e20c5768e2c9b738cb20fa2157dfa3ebda769ac1c8185984b51927ff847b28bc13f0686f720168cfb32658f999f4c153c38f889411b164c8fc9a50f0adbdea7e56b5258a613d490d5dcdc00ceb89dc44df08def28c9d912a0fb621ccfba0d259c26c7c2f65cc5ad48e38cf2cd3d5a603286563646279da1bbe176559f123531954468b307f3a3458b08c1c3c5a1a21a246fa150c2ffef54dcfb8a724e68c62d791f8c980202b65d270137ef1ca7057800639e5ffb46c839f7cf44973a10418b98d1b0acaf9d3e77b08695ab728b5e18612ba9ddfac1ca0df5ac6095170e85afec27e291348487b9459d334d20ca9a343f785494e0550db26d8d6aea19732c552f9af6b609c21c85dc8b5c86f164012cd20371a49208797f1c5f55e6c1c032a01f0b65ea587848bf726e192dc507b9acb6eb15c7f3506435dd583336c8e573b6681d7df651f07fed217c1a535ea61aaee713d3a50cf53d06f1c2b98af21d22d867162016b70cbe4184ebbef10d29a9c9b79861f77cd92618be4126a94ce858e16dd8c8dbe534dfd689eb763653ef68178c5e903632645f020a0cdf10815381f8b530396d2aa222010be03ee5b6efc158d7d0d24b5aabc81218883ac387a01bad765a9340cd375c58b4d0730565d3fc0da67497e9140c47f9062c0f995aa50f7819f57efbd5705d4e429beb1a1c643fbe61e92de965eadc35a7f86e50a1bb4832f843572e2248e148093ccabe59a0c94861541138640b9165b660503d34868b7eeab712925328572b5bb0a56159c2405ca6d7259821f281fb6ed761f7d34dae4b150575dcad84172555f44a003c2c91b58f3c89e80f4eaedf34d14e3f59c2e7b937f31f91117268c46c7146623afa289111965aa0ffb6e104b56b061720ec54acb572e3f49ff078df9243cad95119d5e2d9fd25efcc600e4122f288bc541ab5ee7cf83cd70f092654d4273f63d6f65cf679cb50737dac314acfc4563b2ab5c2b7c3a084702d72b4672ec4cb93616c934a9962f06127cf64d30ea4e672b5c92810de1070edb3dfa994d2a4489f654f2ba1ed32e83de39fa723e7cb8ec48bfd3ef3190bd4c260e9d09e151b1a72e87e47bc3aeab4ef15ba1ecafd29b65526dd3f626eb0d27ca8684e59e422bcaa492d17cc70611fc7ea3d2cfc9edaae359cb8e5451be400c82a5b451fd6910ddb1dbf92b175c64cdb124aa1f41ebdcdb6ff0e10ad78adeb30c0f6defe027b5f97c604e88c752eda0c7066d3f06f1941174e27ab4c193200fe2bfab7040338bb6450400f6508327f5bab860ff9c22ef3b47007811dc4b68f1ab7f7d59bcd8a70a4266134ca067ead3ee23327969251cdbb6e38cf20ad55b71c723a3e42a3c8210edc1514387611cdefd913d0b4553727974ec518e1e01918638312d560b5277c9c961a9667723be82129bc510db2d07024b4c8d96843c679de274bdecae69c031af019f2c005689eff5ec7497bc362f4fc52b31049e494733334ccc993e87550513fcbe06e5cfd1e956b78d85ad01f0d028a3ac828470033afed879e90aa9373c7c14d68194287d4c6c0d6d5bf7b0316bbf454e8d2c3f2e2a3a08bc1e83f87374bad50d2bb58d249fff6f6f7cabd5b38d68e1f10f590a85a64b8de79d67757fe567174d83731708e3134bcd5c6f0fc86e49bf8e484d5af1f5bf177029b1925de93b7bcc575057193617060d36a2f2dfa058b89fdcf3b33357e1ca6c903e88ae4753465578070c4810694dc9597459fddf835e3217dc2232d8724e41aab88a636a8c870c5fe5d1735d2603aafdce79b5a3ca250a27be1242fc57c4aff54233f5d42f754416a644ce5547829cce17fdd939f63f0a4c866c0f0719cfedbcf2439a37f1d41453cb33869a787538994703ddd3574d18842b74c05a9b3621ad75e4fa238493d6b8b4063a684a1f9c077f021d4d9fc4b42cc142376dfc31fa9ecbf37f31b6777a7bf212e0c8cd7a3b87e089ae11b3a603f257115dac82d69c5e7d34f76b0f09c98d58aa6981e3800b38a0041bfb8e60386e6086af95af6df609b517d7d6c21c9a796cd5ac7686e208b761997559f81a19be9961420a9a138af35b2df77c12266327274597db3c6e5dc6ca1d6367e78d91650d8d821dec9f889b390ce478ba3eb74bb5fe7d0c5c1efe6020accaf9f1f2eacbfc6be46965f6937c8a333ec5e6948183d41280b20a3fc3b86bb5921ef4a62efdd8e31f1f8cde9fbb2c0a85a45ea29692784a93351af5c9b1dd0e71c68f5c63fbee0458361348a6597df6c93fae015ace9f2064de32075bc1255ed5033418e838bc1962bd4a76d1d5bcb7eadc8225b20d4d3eec1343f345f74828f85cd04d886ed6bde249bb5e9a2406877c22fc34afa12a262e70447e3296ab0368a8194f6175af82b6241f1b6a4face92e70483d29e53928ff137ef5c0f5a17a9d6eb76860ac50730a8490f36073ef03a874101884878094711120d0a4108c1853b24f0e7ea6b4cb09b8a2cf91123caa6f998d29b79fe69a2903a5b3488d35613eb7a007abe1b0f92e45224ea33b8f9d75ae4d3426ee1007385eb8f01174445f204a8f18d5aa7abd86d84d417463b50d8cd070f2f7fe82021a4023fbb982b4cea10e30e547410cceaea76285023fd2956640d3c516ba31baf4900a1a1e2b716d590b2156172a6c824305c41c8fd0e43a2e2ec480c32b3715f8d45489e6279409451020a1f16d7b01ac2918921c22ea2f10d24542842818275b264ee98671cc39922b827c5df3a12c581f7cecd4c670834fe87c86b84640222f9a6376d707463dbf9d3cd3dcee394d70b3d11765450b8d134c394b81c02e84277812ca00aaa083b36ae08e0f8048ff7310f08030b43f2124f807ed69115620419f70420eb79f336c812052113c8d31cd0e88197abb051133d09bee55fc88ab83448833453b2b40a07c6a17c58a20218830a4e34ca1b6f51297c1af21e006ebcb5406ee16e8d51beaaca2631e82a3d006cf389f01ec2bd8d318d5f1587f7dea087b78acb399f783842c24b37a0e2eb7c759d1926ddeea887d944ad845de0d570355761c2c459bc41a1ea6b672f6d6845c6411c53b48b8737d6dbda1b4a245caf51fceb75ea78a640f53eff387c59eea987392d90c4848f7557f69c77cf0c57042f51b52869404fa97a5aea6f95c7e3bb5aef88a58c4cfddc35c337fba00982765c38bd404cd2d1efb3a998642155963389f944c50235af0f30ca5cd5239423fe1b6c6e5eac300545557118dcab4217178cc1528f47600a041c3c7473d9c66c50f51fb233ab498dd3ea12afd754d38415553cc6b85627588fdf59cd0f6fdc832040f4482d96d690bf469623ae9c92db552105ccaf03d1005d9417757f74928a98230bd24c631d1af294b1fd49a973ca3cd5de3be16961b00e04cd31e03802fb8aea0ffb431ce42a99d1e92975b3ec68fd0d86f41336044b67305d76b020bc2bd73b079c34b98822ac3f0fa210e851ddd59e1f22be65442d29d0df67c2644760ac4b14f12bab386eb69a4747cdbd8002c01a0f41f8f442c2c6ab0ed77fedb812539d4bf8f1e5c49bf1eb18c7d01661089e7be9cd37b74f5df80cc10e2888b69f011c7fefc02d748d18846bb5c3328d26905cecf3b1ecf54fa49e5539c094ae8ae715a941d348f73ec5f3103b8abcf2e0f702567b00a379617d431ab514d956791d6147404dd173f786fa4ef045a3a559d66c0696af0a5a8e99a58f4308cc0253109d01afaefbfdc0cc3db5c9d3544bb814c413ea5e13427c8482c4fc6ecc537d347bd785dbd11a61bf8ed82988a664a03f9beda875eef7f745c5331b2929a0b9717474db91cb5c41f796c27ac32304e1bd1e2eb9835cf77547ca09133a43013b2a72844ba23cddb209e41db0e2b81e16eb2205f0b6f7e634dccf10e237dfef1e978a41b824368ef6991c2df2d43d49274a9877f7a1cb771cec48660e77a05cb79aed1a19fd27c61605342c176cf5b5f25db3e4d6d2b882dbcea6c5718be2b33ff8722716f8a14c983e16a16470bee6317eb6f7f820b01caa1362f2b7cd40535ba0640577eae736655e0ce83e2145ba8d811c100e22037bec6c4244f727f929a8db005048871192e994c9ae7c0fe7630b9bff92f5615546eac2bae681073218b2b4a12ccd013c680fb18b2acf207192d321a36b3ec12c4b27cf6d1ec1a0ee124a48bffdebf4d2a07b6476b1bdf35c42f75a65a65e1bc1e2c99261ea30374aebec58e29d07d640414eb542ba8759e9234b97f15ebb0677e0645093dcc0e6b7d037d9b7f6f5141cb14e363bb856c5cda8e0cb423f7d8d24a71fe463ae7b3c982ad489fa244d31cb1cb5f4d32eed7d090649465b9c1a468112c074aaa25a351ec2fe287704189598e25f7c423ed85c145fc8c1a70881e156a0deb5a5089f552caec664fab3550bd325770b22ca7e82dd0fefe298bc2c54f7752504030a8b12dca03df603e886de3e0bc4176471e4d182278e35c87ad32724bdc890671613deadea39272ab6d01202eca6aecd8838cc0a520372acbe0b2ccd951d927121033441979aaa241decb0f3d2ed07bacf946a537d672bc2c298acb073409c23a0eea0bfa7407179f28ac2bbb2fb861e6ffe3640b7e45c63ad3456263076f9961c296d0335be6950ece3a4853f62c0601ae0c3d387ef1113a34889ca39d6a1c519547c74bf289c40737dfd151d52663183312fa06233e7ffb8fb1444056486490b840f3831781c1644297526267f4c15400fa351336aa1506755f347e210e6128b3f8817c20bcda94851dc88aee800a833d6c8f461668c7830ce655746a5c35270c9801cc0a68e36f1c044ac279f1f9a36e32d7ef0d5d09a6e49f07f5873d75f9bcd3d4586a2be24d8dd176b48706b14ab0658d60636487551d8e8a8007a7a63cdd7be7018a056ab8994a006043acca1ff194171aaaccbfdfe491e8d0d84a3ef09b09aaa0caae9585dfaca7f03f1654e6b63674de76f90df1bcf910b53f94690d034392e27b0e322669003638ca80e3e19646afcb3085cac86e12fa3ed57d8509f6217cff30445c23999f48008084c215bb23ee98b1980e71d5c9287c63a1b757c25bbdf8d77fb9dd434e11fe9472216dc6c45bbd901833c3d27e904a14fe708c3bbd0db6091e8851b94f7b760e93e6b1f6b708440ed085506c97c3a4f055436d80d6db754b982631f6271143163092bb6914a977827ca6399a27e905951c68212ab060181b70e5d6135859eaf4f586b18fe80eccf3bdbadbe066d6775a31e243bbd8dd2ac4feaee3a1d7ff05fa89f18c49144c98e5f313e40f605d47d0d54d4c56ff460ba4be182d8d3e3654a2da5d3cd1f23666414a739ad789d9af06a00d6dbc2700d3f6a7537c94e4958de23566d0b197e25316d16632040116419cb2b430930b587e015016f2f5ad50b050b571ca3b801087c81a40c3121a3155afaeee94ba726fea395c4ff3bb34c2090deedc54f80a6bb80dbb1d78c1a8a206b0222b5cb93a83f18ecca18b36fe4eb2496a4373697959d1d1f0f348b4ac34ae026216afdc3c6824f8e88a6a28341ec059229601b48ff9e2e0727d886be2b90498b9d15e269b5def42654e1a66b60d5bd194ceb847ebd0921d23a28a7423c0410fff25e3c201a10f1e2f279a439f4bc7fcd8c02f280cf4b5e5134a1463f2cd62e67ed89c4ce4fc89a5cb7a0020d04fe5c4f13704d16ecbe51bebed16a2b637ca3bc74c1c2d553e7ef65164ed9d3a64c680f202e95c276888352b52779b2f4ab8ca06268a0010851480d8a42851db70f5ca3908d4a6ff4c2bf26fda0c67adec67bdc25c62d1bdcd396164ef8005ac9262214d867c36d6ccabbeeea078331f3015379bf57a5c70e6301ad33e98cbd120227a8522b9441522729addffd274aea4b7419c6581881a03f81ced22dc130dcb311203fd02bd84a0616b8913819d400a705f7e4317a12d69e99b8c27d5e3085ecb1f200a02b02872c6ea382c06c1148d3178bb31b614f08c37e5e864d1c9396b51702c7dbbdb800656d674c18945c4462d184eb3150a7d6942f970a103e2b61c3125773cef6287a2e8a52bba444db3bcc4b80c2a69b732ce44c4ee41761ea98e022433051bd551b4df76c538e553961e7838f321bf75121caf49f2bc0704512c54191a40a4d9c07e17c42f8444e573ed6f068d55e03aae5e080415c58263fb9a632e4c909fb443ee14cb0a542df7402349d12684f4ef8a56fc75e2289439e30e9a4760fbad73a6ef72606642d079ec2bf46a5e675eb5e5b6d024cd02120b16622d08accff5189aa2553c59c7947e82549f3f5dfeec1ef6120bbcaefebe7ee586415a7077f667ee20e7075a7d2a8942464cb3c6a4fb215948959666c363998e1dcb685c81fa0c22b3574f0552114c46f4e9ec8787a1f387e5094a06d61d99891c4775bc647cfb1fd2704d995d408a3f786d82c40beb72b97503d4b42193fbd0b2768bf352758bf51a421cb248cd14eb61a00e2b3a34edec661c8fe9143b97986406a77a1e60ce40c00dbb6c89a9c961c810467d88f7164e055e9ea14103ec4241f4a55761bfbad12e2761706ddaefad0e57ea8e73098ff1ef739e36fcc44ab705f8bea06f8a62a4269c05490808d08d4a8aeb02d46dd2ecb49ca979a93823e105cabf85ba85a82d71a46904149241efccaa3450121c67ee930c2b64a41006e2c49406ffddd5c2682e6d772bd572f97bc7885ad9295c21898047ce3b76b54b6fe4754190b6bb97c85f50e54153c4a6787ee743b12ce116580a5aeea936c43e1a0c989086b250768d691204d12680bee5e39f69fef0fcbce3f703a48ebf349786c9b0f8c1bef5bafdf7b320c16d8453b39db4ed7295ce0144fda07b486567023896ab63c8fabf3cef2203cc99412cc133034fe8ee55d36228ca28ef4846036ba01961886f75ba767f66033eec5279ac47656ebb84ab0c218b77b031b13c841b61c0f954ca864d91fc4c7b8848048300c93208a44a594a4c95eea9392d0401e745b7cb1a90171bc1b5decfb450d90b1942ead35ef8fba2e02a8c26c9f796a2699b51711151490b0468b3c63ba4bc913566fdafebe46da53cb0f7042bd24eeffd0cb4d22a89efe50f8890933e6f90c1747bc932189a00d6a33af76d862d9bd5e2ad769c0a6c10189cdc13eca89bc68d93dc541958c8b165f60c9bd6093d3fb8794dce11eb3041ba7b683ee8394c1701af600cdf0a4964bae4a1c32489c1641744b1820de30d7d088d058fe519b2a77fc99453b8b6132926804fe3e19f29a50a11291f8686753c08a11d1f4bb91950bf395a5271ede02d9bdc1f563f96e998553ec3a23d414e8e82eae50ee7a352cd8042eb4b559233bc08e6e65baee2d032acb9da4578b0b791dd7481edbce7b39a3d8de8ea072ec23e88101cfa4eb8283a97bb1cfa9503b72426866c6517edb05632734c665289433d72b560bfa135afc58325df1f71f1495896c86c7e8c88b46cec05d70816b24af82998802a4147e231461498f0cf57127a0efad5ce8f1d035da0f52e131441730a087288ca37745b4a23d0eef4ebae3427ab37200549f0f31dcc59c63a642797773b7c9d60342be2d4e191e0ffd584496ca663aae987bc7f604a122a9052c863a9e4e90e47eb94e8e04e56f3a2e48825b68a2932df60af71e726904bafa86de233b305390ac82edd9ea496a446692df532400118e988065f2d9c64a82200470522ed6e011a629f4894d5866ce4e01634d2b0031cc019c06c66358afad872757fc3511554ba060f5aa838ce8a634b81fc3536e019bcd51d261d667dc3a49093929b41cad2435c504dfb0483925548353ec2c8bdee43ba1c7640641e6741ca4c440b239e2b0b085452a6733a7990a466f09f0f8564ad46490bf315b17d59ab640d56414a6bc233cfe7bb62e3382c6466b536ce974af2dd46e51a0e22181720a9dff29b93a8358c744fd1a9feeaa804141878e1080c3ac23e753df53f14b6d0002cba9cb28bda760625c7ffe0a8bc98aa180dbd602cc584720c60985b5a970dec5372a5e03cf4784c9958e8ffca05c11ea9bd7362bfe1c87914c6b175e2a13ea6f59aa87070e46b03c24af8d480040897b1db6bf56fec692457b634499147c075a189ab3517a01b2aead07fe7cc96d8170cbb1ed6e8546d61ed8330333355b124e5db9d2d886e3ff315bbf228f2eacdf69609037ce5659e7ce581d2212a21b4f7214a6821cac30a9bd17a6e053b6e0607bb362796bde218616d70e55e6d8a2613c466ae0b9837acec080fa67b0bb36ea527971ec22cb23c2e3d95582293641fee0ed586688e9169e2030c865b96ee4ed4c459cea47298892cf53ac4612b8273291c8491507870cce0aa157da3bbc823d4dcff13e8103ac182c291b3fecf50c9820620e1c2815de8a39f645c4fa8b3cf1e5ec519656d3c544d4456bc9501ca6e104b4fdcfee22b5f75573b65b07ce23446258593b78859594c5594edc894594a087db85b8e8181a17956186756639c34544658459aff021579139ca4ff6ff6a566f1197b6ccd7b7e19e384d7319d6b57dec41b1ff3c3e34eb3c2f37edd498c557cc9db73e0c01fd0b8a673c5a90aaf9818ef5fd4cf15f7f9d2799dfc1496ff4933300b27eeb5debadff8839d77eeb513181285c8631c9c956d2149fe023a6031efe5f4494b9c1ef4da31f02d91fa84f215ecaaca015211b936c3194081447fcdfa7a8ac13a4b5dacdac0517b67032d480fd4030fab18f28900635b62474ca6f89ef2e327b889b5ce124b73d39ca4e4f91eeea26f583ef86b201c18cfb669d44c92b07687e77c76cede7ab083f7abdaada4366152a840c9ff9178517f29373cd64c936495095146438172ec201bdf48256676907666b11876924feeaaa91144cb20c4f8ef0ceb8c31b6803c41f5a3cb1705c8b157f99a1b44c0c4d3bcd848975cea1adcbb69d780cd23f82ab6256a2401b6a3382767f06acf8a1753061450dffaeee5d0284056cca2f047d247ef9374f6871c3c3bca9d415b06ee21bfb72882a9262434c4cc79fde3ead131f4440b40bed948f4df194d25933f780878a4bc9aaa5e2128ea4baef5c56db30d55367dece5d42e2d4c912fe7de0dac04bf5c1f00c563ae78387645b082211e793d43659737c876dc2a8eee8f21a295fd8ca0c5a20aae5dccf6bd234a6c1c0cebe23790b9d86fa69978debef3fcdc2f66b4f32ef0c7be1795ade3b669b0e561a1fd1faf884344db4b1eda33b16d2ca64208c5e40dda41967c7d50f669b0de6c1ffe4419c6f22753dd0e9ef7c8bf3c974e390d0751061fefae6c484214bfac0c9e8ba56123c0ed88770773a63ab6600870481562203f8ba3dbb8d20e088467c8b44174f3f37ee2484f31f5e874953f6347e9f548736ee676b747dd0cf30c4a330045d7f374d401eb8f99e16319466b324e9723486d5267aeb16c87de794c42a9fa67380de4d0cba9b00c8442380c8d818c1830224bc599bf6aaf4653a18a53bb03a6cfba35a1a90491a24d11ea40079b737dd234a04b036e5b6db92c59be1b4cfb18674d2d6b2b13d2df90bdefaf268ce2836ca45ed2bf6ae8536603a235d8f051457a1c40cd007cd8af38d5ee3a2b59789ffe683e43ffe9b832ba3196cc8118afddd4df2ee7d4b630e8aad5f438d84df63d6808602d3401d8620420513502aacca0b8c5dd6ef05449ed07f6ceaec6019243fc39eaee4cfaef559b88d79ecaad43994ea87baeac110733ce4447a96e7ec76d99c12c26595247ce0ee24422ab9ec1e8a61174d73604b34aecd7fffcae01747141a1a56a142e8a9e13ac241e6209828b0837430c06ee1c9304a2fc78bf8336efb59c8c39b0c1571f4a477972d37de16fd67afa22b716d22874d5b0b3c9f18ec0af850a553d991810246f8fa2d52c6903967b250ab4398a42d228799cc9ee3a20a32843e8ef1218b38698f1695231b9b24b19ddfd737ad65dab8cbf4da3987b2351b6a5965447f0d6682fab31d0076d6936d3d14d5ea456d8f08c3e84aceb0b03379e7a9f8227e6fc40988154f9ef88e5aa6a2271b7403e56486979642ba46c99f6b2db91a52b0b36651e4a973de02c312a249a6f52fdabbf2e549ab8512a7d02800332752d9c53732e24703287ac5256ff3035d2d261cbcfb725b1a77184657b62f21e44b40f6ba49f194fc29d457ec5ab0833ed07ceb0879eaa2d937b869688370197a9fea25300b3bb25e1af9f86edf558f6af3310eba8d861cb17918825bec6de01dc1bca1989846f9de6b905f38cd6b8e64dcc9a684363448b407b9a0d0eb7d0bcc252382a74636f3648486f577a975470837a9c06d54ffbb4e7e2052f8a1ce913b713b144cf135362c6706a010d1de31ac985638408e9a92dedcccb76ded5a0cc8a9415a0da5f3bfc30474ee3ed15e1e0b599f2a4cf62a8f306125413d449035d2eb485c2415305c60cb15db190ef2b57dab4835a43f268b06741c49cf43e20d0a92002d0cd40ea813c350ed5544f7775678d6ced8890dc4ec0eed2de4644eaf05dc62bf0e129a501957b4e14ee082a87ec4c7811aa6fe3b632dbcecd5f6debcad79670871da3c6d7158aa5cfa285d790087b77484d130b9ce3374539a10240c22bcff429e4adfb7168118c6fb62ac93a57238d8a733c1e4bce36663c38c53606ca2eb6ba847a6714c3f65200fa7730670821225b5235b7bf4c7c7342ba90aaa50fcda5fec2f99f0d06cbb6de39b291f9e15a3ffd58a3e3c3a597fc88486056cc476f175509f8ca7ae99d45a2664b4296e07070e9ebc954c58cb89a278b416be305a540ca04cc04a2b88604a3404730596bc628e9fa6282d71efc9ffadb2a9e6a0ebc82af23d2206c58bd391a2593630708d51904a389a802fd0e54b60cdf50f782d0c0209b9c3559ef16af2f522907273828320b4a1c165e6f15042a6138eb2a6b14a63380fa98e016f786081cab365039538bde27dc2637f69ad127388212ae55e51fcd9e6c02af89f09f256a17a1824634f02080dc2635ad8642182530fd1aff09467c07b03aaf5d1c846651e7de75061de69b145755db254a9beb16f0571ae2ae4992905222f5a3f2cdcf8a6de20b769b226361062609188f53ffdbc0fe9fbb9e19f42d67252ae2dec9f92bfb3fa07125bdba0a8d41bf3d0399b398f24b3f65a9f6dd1c8aeec2d5d44cdf23889d8cd1665a011ad725b118b508e610b0fd0f6ffd2fcfbca9f374c7c0642d3d74307f1b7244c88ee376bbcd86ffbd0f1f74d81e06c0595b22f4029d3a3e98a9b6044c25ef3dce2754f5f14e5b4fac5d64954e1d186a49268db54d6515ce9504239db087c4fcb7636c3f33c03173e4b4cc5fe82acf30a94cb68595605a465c392b50a62f647d7bbac077629f507d8ce7f6338cbf457651ea9a770f9ad4915c1e03e60b61a0e11cb704d6a61600c4f99cbbd1ba40be51662bfeae86f10db47d7bbf265b71552643e0c933afa7a52820f3fc56c0b1b4e04a1ecf1c00f13eb2f0e207c68c0c09845b9bb425ce8bc7a455f6fed2860e73ccc140a16cd9350de518d77a318b3f69790aabb7640bc23639813c3969b771361f72d5acf9fc9a36d6b0d4261fb86e7687325c1ebf5f62ea435be3f115a83cb2cbb5a49686c6017347c6b8d95293cfa2f65c630e5242338825a91ac057acad5471b7963ad5ea0a947973e10abd1889d78b0f21d0e225454b377ac8015792c47f6d77d64817cec6477647de2e8ce858a9eaac3199159141011b0e7c6bb99e2440f2937c489b38a1e223e06d707254ae226ed3dfcbb7f2bf95bae34a42d40a3a150b3b128529b4917d3d9f1723e94e0c693c9a137b6ec64d1f543ab230251ec6bb3c682e664d9379625d87de56051a3145514a2bc1a8e8c8ae61f8a317074c7d0a4a9428ad0b80d8878bb4ca4796fc1c603dfc5b1e930e31e015de5b3e50a695188e88085542a5c86364f10ac644502592a48ff11a59a495e19609c74b254c80fbc0cfb42fea5137d81630c9ae6239b156d838c6ed10bc7e8af750c9ef2074cbdf3d1b71323779db7cebc56cf7b81d165f9765710c7d290cec5e5068f572b23e8473e6752effade88d2ba4de2086b74e2af9352eaabc91fbf924bca6d06c19d9573fe4e79acb142fad39040de0d7cc7c62a3ae8e95094285b764b6aa09edc3b29065dffb9d9a3078d4fa05864a4701513f5a11301c6c7071e5638b06d05143539aed4cecb3d0ceda6df04c2437db1051190c315b22c7e2c19a709789bc448ed81481ff203d5648074a1681e083fd5a459ca6a8d16631acc86ef23593a400ecdc0fcd58881495dc842fff452c076db1b2b1728e5487a61c7b2321403f4383f5966a411184b7a874824adbd8e8142ac3f9c011f2c9113621d81f4ae209473ffe519d1c209a8b5f980d5a5e36d0477e42940f64056b971341f0992983149707d5b27af62410892059adacd5e16cd0443f58339387d1325aa9d1982d400dc00490e677db498b250107e0fed1e2840ec1f3655105ca01acd5255dc9cbc9ce988e00f5856e1a0d7abb66aa7cfd8843a222c6973065f9e2657ee762759ebacd5e442f05257c7ad97f52cc0b1b720d84b4d0a3e12ceb0bc53df8dc321d4b987fe45b3813584231546a692d35c65e461f12e59d22031825f9a73afcd728f4c27a7328fb454e82d79cf773fc0943d41ebf12ac91d85aa213f65b15049f37d8fe2d79cac40e0530ef87a5fe168b360c8030dc5ecad2685f42ae6a14743bb6a45fffdf1905ccf31021665d36b4d27373d0ef556676fca5b55fb61876868e59b198e99c8c34ca726799891fcf364fa4720a14a57297a15a1d9bd9330e091aaced2dc86380242aa3335e2412575e7fd558cdb92421d0d6af4652a9cc3d813bcfa881ee62806e16a3535e31344d3126034b54693a80c00f62017272f1756e199afc9e47251c8113ac1babe4eabde0a297d4bddfdf180daecdc0894a870bdce2c13f622c191fdfd07281642eef6b6f129eb709faaabab7eba17d9b8ecab5001691a7172308c0e71b3dc946e3e07c92255e4c2cdc620f4b746ce360371ba209f6c691077a3e92ce1ac4ee8131ba8fc8c8992647beab6b5dcb09b3bcd52c76994ff2177da16b66681641adf0a698121e53829258299414aadce767b62d05e731661e8fc5c125807b9b1dc3ffb100d030a1128e78ba347ace2820b7adb1deece982d53257e00bc8522d5bd221941b2808b8c7f4376d0ad11cd03388068407030a0fea685c30903a4e07aa31e9ae52fccad045332e99a4cc8de263f87da403fe1803e3897fec1d88fa587b5eb289ddd13a66b131ed0530300cc380bc8620c7916c774690f808f0746a8316b6c1244bf7429dd4e1a309416defb626a59432a524036e067a06600634c88bc2acc19317c5764a8a94142914d87c0a0594e8c6cf64ff700ab26b189b103f7e2f0173bceb2a9658e9410e13587f962e78135f761f24fa39590fe6a9f8d54a52d73476e3075bd8fecca8bd28a5bb7cfa405c76d1a8bd78647360632b4254d58c602a7e8dd18812318c104a29a58450c6d8f9094416fe8e2f2ceca0083a090bff535dfe0e858485cf3d25833fc2188b8257a5b2760c0b632283353327b0f03311b05898d397e9f377440d9975087d12438d06ca00500b9617fdfab0855240697ee961eb48773f73327887626175fb57b7ff3b2283ec502cb4ec28d05ab0bc6ef7ea3a07b50401dd19562805681828550ea580b911c99e76a82194f47476444aa59f1d6ac89c5ad73f9e6281fd4ef73d75f2d48f613fe8447564cb1054ebbace3406c426748139dcf51578d332de61b9ec910b668813213d823763c0547fe5e1e2b692dbcfa66b7760bf18a331c7a1419e6a1a1414330085c51c878853e801e6c800e2b41430f5ea202e95542a124808210414ea7e34f6c1fd1ae8a3b3734061fd3f1a14c1d80f16452f9b066c246a2fd65ef308c27253acbd50a8033eb79f3be6a9f6eed740b15a690c28d6314f7db15fed890115c5b6b05c7dea930ca2604bcfa552778c31c6d87da4f1c2ce8effafe0756f23478197eb2260b15f1bc19c460dc95e1ba27df6d9ebb4645ea715841a92fd96bd4e4ba7c52fc335100beca7ba3341d6c6e6a08425ac0851eca7ba03d8096af9fd7e5c7ed8cae2973815e0fd01849517d5517cd1e276517b03d809baeda5891a4c908e19c58c54465607f61da7c88d19abfb76a9f43dc3cac00f236ce949df5d2a75240ededcc8e82a56afa0bf48d1e7f257ad5a1289d25a6bed4a690ded31bbca7895c143c618444cff422a9650b9700b42bb3260f78b3c6508412e62a2dc2ff27cf18a81f28a501a07b698b85fe4b99f47d7dde955bc42e5f2472711cae5f7235609ac9123f6ba0cb68026f09b11e5bacc80026754200599b32fa5b4371578fb0184257dad9b0adec7a4cf2209c29bfa24d6086ced68f775d7d09e7f6f2af477c72e57afdc9231c2189bbd1dc352fa2e984fb90874d195a1390cf4c85c278237fc239c61f9fdc771b40f8add7ed94f6a206f695d0379aaeba9769f0315e9a2cf381f47da1353743f78c47359134374f99170e42932525f027c499d1103e8b8241748ddd7ad73fb935d912eba116a375600a6f8955c661cf8c9e5af8e03996b882f126eb09f0e8f4d7bf0cdb030fa646d83325813e17743d890fbf8a563c87e9a12fb591af8d03b147cbe901b01bcee820cd67c0dbbfc157ed695532005804bd283680c268ec41851a217940ea281f9c82502111635c57e7c04a4bb677777b7cb7c4f2eba5f929edb5c74bb9361be092670d1e5d2cfd151ec0e0067c27bd72e97085ff321fc07c1dce6fe8361a0d18ed7f1ab1ca8ff78e736cf8dffd8c9e93f7ed9f88fa1d4f88f7b40f88fa3cc7c2ca5398b89b7dcbea2d44cd41f8cf1ccee547777fbf84fd5e33f56fc52a5f9a8bd76116159ccb304f230faf3f81338159772ff11388ea63d77777777777777777777777777777777f71e1cecd46680beb3ab257c97f026705f112ebadea16e1e5eff123ad4cdd77bf37cd4ae1b1beec5902079c42b8211b66aa9676f8495085ff32174cf5f64865efdf31fe9d58ee7afa1573a9e1f49af723cd33c3feff40af5389e9f9df4eac6f3094aaf6cd400e17986a5306c87246e70b5d883e7e727ecf373965ec93c9bb6c07aa5fd2a2b912a007e2580f7f1fc407abc07a007075b3e38d812818317de8e8397dbc1c1ab83833707d77d491cccee071cbc331cbc2070f0d6b81bc7d70400aede1597e2ec89a3e184b4c7bfe286748a5f048e01355c089c033a2e06aec8733b7470357048d8b5c3dde0d809c72f8ea130081c47f169ef0806e22c42bc856318c757708cbd30110b807b4ff1fbe0549ee20f00d783839ed258b79bc77168fa1585f9a8a9f44f03b5b7cbe8480c1f31d721238cb8fc1cc67fd1b579781ca7d6e6699e587d5c2e97129acbefa3e8327d35616bc4650727602e6218710dfe11bd1b7642734e74f887949edf9ff9618704890e64c8b209760169aff46f12c2f2a3005354b07458b11f8b056456cba25dc7624d21202492bd02f8618701f80c9f31c05fea5024d8fa86092132190a81a95430150ce6442c4644042c46040c168bc58866cff4f161bee66bf6cc9ee94382588c04b1582c16232a8a3f1c1dfd7074747474e4841342423020828480080a12121282d1c8c21e64d147380d2bb18ae499a71b1d9185bcb4c7441c8b5dfe6c0719acf91a08e6c048aa0d43f358819ef759e589c26ab97ca2e2fe7085fc0383e3308fa798878747c7fd2011acc2057893c30aeb8d05ccf1a72ebfc9e0150c7dc6e5da8571f8c753fc5fbc68302ef30f7c860c1fbe0b30f48aa580f9791ec7e92e9ce2fff2f2e23283d1478c040f3716f6840b5d1a97a0782e95de028cc383533e4930e5a5f85872727a70dc8d1bb38b5d2d793958ff0a7370a4627c203de3ece84f44487b106601c681bc00ff0ac09cfafe4288c01cfa3a7a4e3dd81c6cb80e71701d76711d6e61a4be9442aab8fc5dbe7841afb7839552ce0018b9bbfb8cd0af0c2929ba1e638c100a711462aee53f1e975f72ace521165aae138137aec4921e5271f9610e2bdc9df4a598821bbf95c1ed0032c40ec528da0de98f620652ee11bf2c7425c742377a6e3a23eb8fb7c714bb1561bfd33de9d0713a7556ca4a9991d8f5181bd7304f716371998ccb5d8825066f82b01fbf7e18fa53e995771a076b0e2d2a3f00922abc710a13c269a114cce9bf2de4b57af7412d2bc01b1e2978e35f04c2bc63310d6b210fd88fc57091a7382fa2439c82905318bf1c47b3027320dc54f02c29b81fa96156ac0b7910094aacf69f074df9fc6c14c60e1456eaf882f6dbebdce66ad848903be297d4d12136611a7d3a3f973e10af2c4fad006f1e007398993f1d5e701897e378109ce237e2f27fe0e54bf45c061ae232ec1c0878c36cc445edf1736f69af612dd41e0bc1b296d992f90f29e9b9255c38e4e2223e81f1697461dfe1e80406cb2fb75f5a8138de2381608e9fa8b05a7aa2e02f6e7ff7491e48ba39acb09fe4e1a3eee3239b22acf652bea41230a7077eb204e3c42b724a913422881e3d7272b0608142425cb4026ffadbc60656ebbac753ddc7471f1f31fdf8a8a7b41498c3b77f0acc911fc59525ea9135e2587fc00a2bb18039ec400ee49e05c4e198a7bac01c3278256d60af9fbf00c3d600c65a0c28c6fd244f7c6d6113c6fa7ff1d5536e7f2b71fbddcaed871dc5ed97026ffa59c7e9f5f21ec7e1d3c95a20efb9fdfe5eab00eae2405dc447314648828b0b510eb86cb1437784602284103a10ccf1dbef2a74ec301ce84b9017420e06cc5d70db085f89451fb5113f0c1a92cc2104c9978c22a5c015b85e924acbe83dbdfadac8e705a5e32b1ee1ee6e0473fc7ae740bd82432e286ef3f09c58fede88e8b4ac10e8edfea987ffc12022ce0981c13ea24aa8d2730b25c4f4a817608b5bda772a4fc5a0d3d2def4a6105876d5105f3c35521eedb3f8f254bb1598ead771bfa8e3be5e2f2a4e5a6e33904701c446311bf76b1bf7e3587c398eecc2a9fe2faf973c0a81fc7120c791120798ea8f010101ddbee109ec2416eee33d3d502416fd9c141b7bda7b491ef85f7cddfe1d57348d1b5cd7da23e753596b8139fc47b55adb2ae0f682f94b972aa2f40d7c62ab8053fd55b8badcee2f5f17f940c5e5aeb570522c0f30ecc7e3760e2b2cb30ffbf81cb90073e0490b84285c3efa4eb8fc2a98139f2f1fd56a2d842f70b5cb49f7f4115e7ca8dc6ea227eea76d3f63aab85fbf88c2dcafa3dc9612217339620c13375a611fe77161adfd2978038d4ab287d807d224d293866c5f8a2a8d93ad1257a96537a158f0db7d44ee89eb56737c4b08b2b0055bf2493f4b2f896410cc31823935ee7c1cfea6879b02a0972212d2679a6d6fd69f25ceb600e6d43103db60eea44d29938c4a1065c22dccf3296fe9d57cc19d3f2b732fec78d11a87f4f3331247d3a9796a6fce2a6cc72984eac17e304848af3e29f4f3e7afd0027d219803ef9c3fbf005b9011ac98324c189803e76b5b6c6772eb1a7ad3021b0b3301f1d2799a949efae773d78ecea799d2285381d7b4f6cde0b0fd32acc7838aed875cce287a6666afa78cbf7a9573c9b22a299d916671c628e3fb09c2e95d9452ca8e177a476eca3d92795e2d65f7757b0d10a77e4bf91e83a7e0e954ebec38f392cdfa31384ee9e1d7005ddc035780156663b012b154a78737b8fd5eaf50844d28027af2bd153cf740165c8416cf4aa0277f85a7cfa48ceb31c91807404f16a1af64ccfb34d50e0678239f4a22ae0b81299922f2b90e04a6220cb260815c7f8f05cbb5be1149074434ba740f0117642041c4151be0608a211d4a80840f539668e2ca0caaa1633411a3c89c46e02c2480b8bb3bbdbe0212410500e2579e20b5149a25463a4389db0f8408049a9238a34b075280d819438861e7855ccee8c94070060e672c69d2a73fd10829c3fd8510810d113261ee66e4e0fac7f61a2691bad7b0ccd082644691193033b204f634a32568d68053f82afdaf54fa4fcbfe33691e6c9b698b5cb7ba99eab699344dd3364dd3b62ccb322dcb32ad542ac5772f6532524ab517b00f150b0a696992fd49fb19afda93a72568cea04e2aa564a65272e44a2b2d554a4b31c618238d4292085653ad3d9dbebfda138d4a08468411d21fa91a4766aeb4c4b596b23967adb4c639e79ca54a6b9c71964ab546a694d219e9cfca5752e1e3b24edacf7834aa1a968db5efa982b4501840229b156c8fc572d6c7fa1a161572fdb827ed673c9a1ab6898c63594639e57c2999657d5959da6a4f42efa9583746596bf745494b924a4a4b52b694f2e57c39492492c32be47a92f17f304bb5f6747aeb59b68c633dc535fa67ae7136b93effe418a1b7cbb78e53abb5a7d33f847d96c5fa71e7d7973ee41448b5966aac92ca5afa5a6b8d55525a4ba54aead81921fd917ad908e9e3fb76806ccc98fb2171b96eec48921b7aac513ed78a253305213dc618a3bf2e8c4117c64ec6a8e349bafb8bacf6eac6c9460d9cf9528230f3810734ac8c69d3b212a9d229a33733fb66448c2e79da83efda0b3208536828256cd90d3b85be31c61863945d8c313a0d2e846c027c18142f8a7dcf657f04924f1c07b8a23ced79691cfaea18d5c1c9a5403f62a484e1437b82a27822e57e5bcff6d28874d8a05c9e5e6d4f209018293d9d29e807fa721c0d4ca7fac3f484715bcced6de7f6e6a4f4d2884a2fee2452fe6b0a65b55738a84ced158eeb36668d9b51cd39102ee5244cc5b8b150effc8c568da439e7c1a571b34cd33493464d9a73dbcd6434e7b49bdd8c86f681463fd0663410341bf16627ed8646730385c3e45d8437f167e731c8384915d401a08d3f12af8dd7b99f07df1d8a851a335fe375aaa058d8baa97d243d8c98617e2c7d2cbdec20e7daf14b7ebe5819861258549902045a6046ad1d5c735af5588aa5d873ebdc82c8cecea0a6ec1d463114699c9dba448ccb7fa406c7d9f16068a704979f77802e0186988471997ba6e4ff1d2f85ec7cb7a3b3698f3d1c6c4b6e6326a8cd22d42bbac5b4712c1fedb5200448018076bc4e4bfeeff821ff3b3a2455fc91ecf8efea1024ff9c0d1b9f75bb7a2381fdba45840347108e201c413fa7d37f95bb8ba20f9c0211d785f0df09078eef381ccf71389eb405e9dbae5ee968278d53f3dc7f15a7fb1b5fc375085cbb8c3a857a4671fe05e9502bcc8efb66b8d252c961e087c607e2cf3d3fea4bff5c733a85606d8eed9f6397a7681e88dba0e1501c0ece48bcc17d0de5f29fb8af8fe88e7d523b6016c3cf001b10b6d03fdd6769842cc3186a941e840e45fa7a49cf5fa4571f3c1f997766fb0f3ad45744753db8f669742899af57c6dde572b97c58ccfd184ac3a12c98b032a61f4a50362d83818b0eb76380c1012af8e1b20424d2d13946907da5edb79fe995f6f0d2876dea96981e894ecbf459c79f5a4703e9bfccf4f5a3c9f44c745aa68e89fd2feb6820fd9ce17249c8bc95e970306ddbb66ddbb66d2693c96432994c9fc4b46ddbb66ddb165f6e2693c9643299b66ddba22cf50c8369734b96d435467267dabb7183e5ff54dccdb407add7633f55dd82b094359769664894d247f9cff727912825a9be8646a3fca8f8fef1274f29e59491d9c6534db1fc38d89af698344353733bf3925966394b2f29a594524a49b5d494524a29a5a49e73ce39e7ece94d29a594525a996c07b091524aa594df84d592cf0489ff69cef94d58add92561a2d3921d13f9b49b734ec99259f24b4e026352cc60720598169c51a585a40abfe524b54b5eb2f82eb98f24a409b7b2cf90dcef48bc3294b447a2d3d260a9a3a1fe079f027e93b080f6f48174b7044992312ded99e8b4b48ec9f659d7d1505ffeb433dc9984f61b97845d413e2f9e1d1b94b496c0312d9e1b2dadc341cbb22ccbb22ccb344dd3344dfb26ac96f649b42ecbb2ec4f3ed84cd3344dd3b42ccbb27e773ffd1c22838ec3443b74016a411e8268c187354e3ec81ae3a203242e3850633d508932a6ab104363215183e285434b74200905cd1d9c8b1ca21439987121260b87a12539dcef54e3fa85434b9a482d58043f4ef8c31a8e45cf8889c2f44e969793db43368471bf9aebc1fdba5b5f477ff5ea83625c52f8c7092de3f6f395d7c7475032ee57d3b026dd28c4f8b9fdbc83a45728db5edb184cf5438e712c167322129470cc06c6798195f01610e704268239fe1ec7bc9c9c1e1c77e386ec5ce854ccb156f3c09c293007d60a3dd50b42b9c58b08deb48a042596babffc386f1cc21f32c8cb35a716404829a5ec5841a01c796aaa24cfd095f26724e501c853f0caaf4f7aaa34aa69c1cbd48cfd832b709cc032bf9d61752a1f3b08c32506366a293589a2d6c04628e60eb692601e218229b65ec8254c12f7abaf30428009f3c3fdaae34404bc8a623cf1b8904b982c5e512e0c56a3b5b6c6292a6d02053243af0c5c7e66e6e18a8b02a545314a1e90d861c8f295226c50a4412976a872dd8bebdf168c134250a10110508061d4aa528b1a6b904f9dc2ca46796460270514c0b41b573088abc0891841bab27ccda0b21400c09eb838b1c5317e9a0e5e6eb71325ae8d3a57a8b0811533e892832e6010e30d30308a220c1035c8402b4ae1a65295c05ef20596a55cbe3ca403161cb76f2d4d11344f4f038124620004151bbc5060850b3620c490155fb8b0f8a2e5b3055a75d12eb6a059ba28a2932705d4bb708887089e1faac820cc8931c648ca4a5b0cd0a8d63a54bd162650bd5a6b1de386a8031dae3fc31630b37eb450bfafb859ea16d8021d10509200ae3c808cc9470b1b37a3790d03ae97b422d79f3d19914458edca4303be0cc1e08a2cb400c10c7490b8c2c91721c8a18b218eb832b334d1458a8e914ef4b049b14419590ad985433c4e4a57c84a659c31ce0fc8297aa031c6219e1b6e8c3fd3ab223593499c205c38740318376c518266661abc2f6c752a39ef0b5b3bcf53fc3870a79d40b9dc512ef343619f1dd80d9e78395173f56373e517c048caa0af66677b3539ba9faac909eef7e34a39e1195742085f8578e146e3051ba8b407650d2b563eac91cec318310a1fc154dc5dc89b5d06590d86357c2c3f842fc09657fa3a9c849b6340742af56049170e35698289a5170e3511418cda85434d3a703f15515402dea0ca17452f1db89268c2004ab09ca0035b7cc8125445b3e20135c3889985045224ab9a1471fb6313236e7f6d92814be3c2212662a4bcadedf5c7244add5744bbf32b259de4ac4f9a098825f95fed24f4fc6b4fd919217995df53caaf11def805ae7c08bfe8041b989da5d43498c327d81057809d73101bf0a6ff0425f6f36edb606863099f52c6f625648ef4016fa6ab1c764ab6775143d5b6b2bb6b47d9d1cc5865fd4f4a39254b035afdfaf43ff8f0366569c07a51ca0f04cda6a90e64083dc4602c3b1f484b2965f7b953a72592fc3ec6e891c8959f04932edfb6ccec92898c5af4c844ab9fcee5cb1f716845d2d56cd466da8b524a496ad91af58ff25457b53dabc5b6b56a362341afcebab5a01324b260247645bc2ce6ac7476d9e4aa26e7d3f93e5f06ef2cf5195bf071630ede530f58d91dedaae4b4aebf229e663d87ac5a2d4e49e5894e2b63ec8a68d79f708eaba0c11bf827ec58af32de2235d75f90d5ee29bbfbaf75b6cfaf7677ad1ee3fcfc639c9dbbb78ced710c464edfbb4f4a2244728518bd7d68f76ef70ac0ae87e0823f4de650b1833fb714d434c898dd653c85615965e3b998ac8bb0b13ed378c10630dccc094938c7b73dd35e4b97fc69f9bee57286b08c829175fe0884c3ee88bcf2ad3f8d14d29eed15853bdcbfbff0a344c922aa0bdb8b31b248dd9c13beec424acf53313aa4420521346251f2f9721748732ed860ce39679c73ce7903c990a103b0b045682ef3176f119a8b033a6577445e8eed822cbabb17eb2ee4ee63084991b9bbbbbbbbbbb7cbdd05df239412860f9897179e8ef43359760f0dc3478c1417ec28fc4fce272ad89a38b4026ed1c2059714f8bb43f992ab79e803762a3873a3e4663a0561f70fe1373441c9f50373a2cb792e2c037aae04aef80842082184902b4f7551585502bd7e08a1bb8cb2f8713e579afe7e14d42a141bbf0aff4b72658cf0398c16a5fc60b9cf0c901f34fe81d31434b0ef2ede0d5436d41ec2878dca9dd8d4784735118c5d0242c8f2a1b2f16a9ee63463356f08cbef3e48950fa15ac40fb63eaa0fa54cdba8c94429d5d204918dea673c1a558d2744f48477d27ec6a379980f93966a966959a645aeb50a1951cab4cd2443a5ad965a6aa9b532323232323232d46432994c26d3b66ddbb66d54d3342d324552c5fa0113e24264634fa77fef6b5844148a59cab4cdc432325646c646ae3255c65619196b32994c3226934946c66ddb64a4940ac9b0d5004b55c3b2511915c91dd2ef34b1a5df69f2ad253e664cab4af64d54ad2a3446d6b8568d526d524a6b8d7552aa6559ac91d2f91e638d7466b1465aa2344e4a4b94c64967294e4ae79c73d239e7a4744e4ad2015653ad3d9dfe44a38241a1a9d69e66c4e0f73c958d161a065673bd93f6331e8d559d5e457590f6e4094d23231fa77fcf5379364274074436aa1ad67b2c22258268aa0d9ccba8640dd934357568460000080043150000200c0c074402a140249205d2aefb14000f758e44725e3c9586a21c866110c518638c21c010600031c01043886ac60601222888c38903ee1cfbf2aec6c22181a7c6b8895c8ec5a2bd5218a73d02b5d7039c78ed5ea5dbccd46d8c12730d1ef01dde522cb6cfb7b4d3c7668e474735d1a1245940384448f97410f9f74ec2efdeb8d9dbb88a6ef659c8aff1862c2c945fb98b9a479d7ff19d1b87f29ad7eedb88405bf59d4a0ff4e8a5c6fd9757e6983e648509f1154020f3e0a311c24dd7e899c8013830e0b34e80039a2f3b78d0635726c2183abbe21288a1e540d7f0afc8569ca6a32a48a772cd9151a1e53106b90ba989ee9ca7939958b467ea7f45ee3f78ad0511a23e424edf0e29182a70d0ae20fe306112e344286cf322b2780d0270db4857871b0f348b26cd2eb41b93488c70d6e180150ad7d5d72697516691286d83d6103135e36453cf3ac68c329aac23e90c0788ccb21ed02c8be6502b23a17f4888c6645c9b30f80c104ac4b4844de31845182e5bddbd6c891237bcccc372b137b52da161dc3559cb2092f43a4e2f0345821b0598701d81597046a8ada3ef622141dd47a875c267e5368e68350ce999ba48e16181ebd3edf72052fe0a92cac67d48ae62440e45a9ccf110bcd00a3433a8a291aaf4f9564fd437e4576dc058e3fa07ce2099b861e21cc2f09593a09a2ef15ee3cbd48d060c1c448214b11dff54ddf64d4b18e6e77aeb66b28b3625619b2bb5a002500ec795ec0c77ed19bbdfa212acbdc273315ba2b6db2e727d93d6fb5bcba081e0b72a3b8061320a526a0959d90fa461c24e0cc840233ee6a3657afb1c8b38bfed19656401ac7fbe0d27230a35a6db100f7b3153769928bc5f6dfaa304bfdb8dc4691fa82d6ec127e33650dbf245d7ab38fe9d16b024613c02b5790ff9b6ffebd234e550e18031d8793741c8102259b5606f07f817f19ebd9409ed971f0b75435b3c3d9f3dc29e0c214be44d87fb8d3819e326a765085805e2c6295ff5bc1811128e025f079345f5110d2d30d399e3222680dc31d7d428a04a324b5f6b835b6a69f9a41fa67309ac0de9474ad1b2a92facb8ca9e6cd8cb0766bf5dc91386e260b3692cf9e2d6e80d652ad4024f95965315cbe760bf2f043c30b4d8001fd19a2008815c051c3a345f62a390fce77e165bb13d00465487ae955aa427afcf87b543776ea0a52e88c5913a6090d41dcd785c2ba9abf599cd7896da001de1ec23caf9239414492c0e3e1decd80191a2e22ef3d6bb51a641a3346849a243ac79c0b1cc72ef4092e86a0e28d77ea33563e2d7a392daa3d4e14ea11e8534c8fb3ccc25ba12ae95da887c300ca0e0076d471bd78660c0f8604aef3e7cedcd735f3b285e912363714e2b4f643b215d09086e0d66f8497bb8b5b93e3a4c406a0c4cc7ba301db53bc6106686309dba555895e623d5fb68ed9f3797829061b63ac46940d31dc4e8f46d484601724b39099a1e5cdd447a7373bb27a8a6033f84f58600c3472a768683b73a09490aa14fd9e0cef50eb57f85b372adae93abab6166bf6e58f4f1b388f81d93acfd3e8cf51711b4ef32052812c9ec8774762e2d2fc107cf4ef22e9468d7123b74f27e18928512a9987dc07d4b3b0e7d98a50d73a54c8284ea5a7b775433495ca451d66a23d75a44b0a27694311cfdc869ad4f529c1f8979031797164e4924007dc3ad024a14e831c3375801ad6628cc94c54024a27a3f64510110ac4b5bbac474cf3c8962e58ec262572fe923c52206834c5c7424d1dcbfb18657bd076d8c252dbdf255f89f6ce6e48858c85c902adeeb5dd74c7d9f93f5d2671c1242aff45dae54ef3057a3de009256ff044aa52de747013f5b83863091c9033838c7428ab1f8d63733d85df56072dd769615972ca604820f0128ff30597857673e6011102b3fab6d7936f6b5d8a54d119611e98c1320ea8a46c47d641a1348957d2462e8708181df58078e8a71d623ee5ed25a7c0868f81ff042c8e4bd93d164d3ff881bec1cc08b43e695c3e548b5eadf34443cf8618a1374740e6251a9a92c3ab58740e3aa035382f3a5ac97280d0e0a23a813a59d1b1933d0f3cd36589404ae2993bb2cbbde7d1f85ae453d8e427dfcd5939f079a12a6e48f32b57b5d87865a53a9b22b483822d99ab1112c8a614af630f0b47a8f0a7d27fe4840e91517bbf50b806212665ae73d871319b4148549c551d652c45261cb9559bb91e864fa6f68e91fdc13f2a6762a640de58a2a259d49954ef2284754aff6e07aaabd1282d764a9e4971124e202389531b1a9e336b731b840e0cc60394d5dd412e5a553a67e51ebd96266b428c2432a640a2ce91456203c3648bb97f862080eff3ae409e0f187801175bdc8773b21344175a68e18cb4bac38ffe8b5584b661e7c8c3da5a39c6d2159fdd18bf7e2d42c64a6e0cf1d669ba088e12c05bea4ca6bd9a63a66c4ae08b1ea3f1ea53e9973eef0673733514863f755e5b29b97579d5d0ef6f331515a813e8ab64d0f6533c83aecd58e5c24727ad923a690e0777e5220f13f19fdb3b7499a5f3b49d0d9928d232c18172aa7695736a0a1b347a7c9063aeed18bc176c454eecc22c237b91a10d147e6def467475a5a338b1c3c515033ec355de605e6edc9063b65fc2b2dfa8383c9b482095fb7eca4af9d1c8be43ee3b1182526bd1f6e3573bff996bb7eb1fc1850cd0b9294f1a439ab4f132da6917c5cd20cffeea489c1e33bf84d6f7bb2fb979ff90152d1d5444797d3048825cba5c723ec0e6ad12038fff499da757a6786a8ee34ff454e4806b87cec47862f6b2c924c731160f3829324d59d1400a54f3d1e6b0a05900840480fbbec28c13135b054d95bf0c261bac8b7589c4b54ea0a39630950455035641bd3b521601ab23de41c7eb78fc0d808a9a47f788d90b280822291c1c2712408e09e02166086050691486b04f0811a20881d397e64729098342d9306aa000b15f6cdf34cf02225a1a10194886836e060beccb8273648ee30011cda6a89b04fcd4b11e97d25beeecebe62540d3b581895a5c30929189608c9b262bf53712821cc60f622f9d388cdd06d8556ad88ecadbeab51b404e4198ede650bdf93b5f28c7d5315fa158dc432561296fd811eac3726f4aff8ab2ad548bbfe73563cec60996aed43aa0890fe0a671383f1b20d96930e841f10514c229d8831e6930f664b56aab3af9159d0bdeaf4efefd3f639291010a7bad8c59a208cd2686336b28faaa1ccc870af6b40741ae2723b8a0a719e146964e725808f4849f71a690032c1bc17db55d804714538a52bbf2fde3f98bb9c42c34a872ba253a77b5c666d0ba12ed54b33154efbca5755ccf0acf74568576d103c437740ad60f3132a12a2d927004dd8314342ca4d79263dda5c5db416fce27f63a28db1e9fb3f6c80184d1f0a8c32acdb2937c3c286b5e096ed0ffbc0d39aa984839f4749be3fafc10e3f64ba01c046a4943711d4b18c2aecc72384435b98a9145d45fc34bbeeb059d095cc5eb9cb2d40f4212775d1aa3b802249f88609bf0cec4e4ad5dec16385bc0316fa734ddf49d5e25cd259046bc44b6fb58f97e0cf478f3afeaff58a8886beedf96300bdb78b6cf30f42b176dbcd8ea12c13c1c2dacdb96a9f728b46a78d0286edc274ba2f46200e7c917922d5cd8214b96e03c4e0035c5e009504fb96442ade137389330f703120684be7181bd9580be44c35aa2100489e85c41460307943a83f577c433748a30d103c514b07e1a53fb4dbeec0fee3988022938ba075d97a4630ba30b7b7a09160dd365cf615734fa8f188ace8876a2e4a6407eba12b46f3ef7a028a6c65950832e83b88836cfbd416f27f525f65149abee0c1449ea516ebd24ede1d18550b14078e67e2dd3e4a14d41d35b7d7e795cf77879fb65d0cee896dd00eef27b6db3d313a8f247b5576db3d26850b00bca4c3f6e290dd6eea31af169a689c71d97e7afc113b682e901768540c2bd6640509d105726636e732cce19da63bb8100077426a303330526b1199a34a006ee0fc163595e72a7866fca5ae4e9b182e48e6795a9c291c278ca526f8782f65122c74d675b0826c0a84b4c89714bed3e99800ee90de515b1b06fbd07ddcbaf80883cf26914a89dbe2dbba15ea928b6c136b8c91a75e6adad3d533528b14051176ef9d7fd10af0bcfb0c14e6aaa975138e05c8e6390309ebb101a314db74cc76445842e280acceb964b336a71ebd80ce03d9940391f66bb6a37a2328cf116e909f52b8013b5d0c9200b523de46b5cf64e66d1437cbd8dcaf5c23296318f6870b09d05e730c7b6eabb1a692c5741d456510d695c4c42fcbb7d26315ac7610b0aa21e002f002a9007e9379759e919ef47a80dc27b6bfd3bdf30f1a50f5a128e696675da034460302bf281dc2fbb488fdc6da482dfa02941ac6535561200bff540167488035a5f5d4eeebe7a5ffdda12803e709e68bab95109f8d96a237533844eb84e34974c127e1c387bddaae4177370646f29d0a25de696a8c94d7b5892fd118b34b411c57da3137db07e4136a0120159917968ed7e113115e35eb47dd2ff90a977bff5299f72354245ce5c1084a75479ce9a2b23ef8ebc283e004ce39fdf50bc284f95e60a6f23a2679d278cfcb0c7fbc006a7d7bbcea5ebf245df9b3d7cf34562d9ab7b24ca1a5606c0fcab03d3180c46016a5d1015ea1be0573d26d4cc2fe7e9e8504042d26f55d48dc463db4d186f8e8e7323d26a439a5ddae62a6a9da381af0a7c8f3061a35d53ee285d0821c4e7eee9eb81cd225412f9a202a284ef834d00bf168bb6bffbda5ef57204b64b8ee07fca4cd8c39f2958371f454c46485e945d9f4169d2b1d8e71a12e959b87d51d1c8d1c6c21a9ad41d9f91c580a9b999a1569dcd271feb2f306623d3fe42a0dc28d0e5012d73e3b1cdc93154a18f18fdf6735d0a6337fec2ec3ea40284d77ec40e64fc29e04c2b3ef7a7ad6656e90ddcc97329ba64a328e191247faa7c121522e9bb08d850c869d51a299f465b002ff42cc1580db3a7fe094e55afe8a8e2a9a1b29cde5a4b24ce914f807e6a2390f5d3c204a4d02f0282d4d282df4dc41f5efc4c6421432fd2a342ae11c433ccf772e5c8af91f0786a8ab3c93466818ed614f241616730a9a1ea58c1db353a80b7ec0f0d1c4042a43b302a882f50bc5b70249a946fdeca14cb38d6951a9f12e31801e99e2d3cfc8606474f625fdd756814a23c9ff78b474095fa1595e3a8e636683f447b1f17ebdff08602d1c465c0fb2a2beffd77de144ad4e21feb131a880dd523add2d7ded90b6340606f564bd9ec25031e6796e17f895ee2bd2fab52a3bd1accfa898a8bbe3d56188138c7cfe45750c1e6614d936bcbd89f8988e695c0c0e55a690a83569f591dacf81fa636286be2ca54b4adf10a0dad4d8e3e243b2d1d8c603fab87acb859091cab49a8248e1fdbaac0f8ac50f52c5d706f41459a1c9444528270125121d02310060cfe13c701eb44d307bc7d0bfcab99cbff24a22f17dcd10ec3e9b4731d37e36bbff900c50e06090ea05bcc3f3a0c092319dd5c3fc9b4310ca6298ad1ede20833d6391dcf29346167d4e9e7c7ab731a68a1934b2429f3a8f1db29a030c7fcf570c9042851b57f287d9e1db51cc0b54cd84534b60955066ea3c7ea616a6aa706af533fca053124dd356fb77ead4685302b36d787adc33ba62bd31ef2eb5a218be167223e3e7a3fa8b0b70fb3aac5276b833d7544c33e9d59c778962d46e83cca61773de4545b38df994bed7358c2ef866141dce97224c84a31863c07ad63307197c000cb6d82619dc31345ed4b376af5762f1817b8bb524927848d0d93252449c6a889416baa7f659dafcb162543e656b555f48b5454f30558206543aa1c9c55fbaa0f9d90d6c5dab10e9470570e356cd2e3f9ef4818b99bfe71cc9137dc5cf43a997d9081d3c8d38b6ecf4b6a5a48d10e8403654c33b67d0ee1686133b511f395212dbb873249459d73c67a646c3b7444a77134ab34111b7028717949a49a9750f24c686591ee87f7774ef0471ec43ba662d2d31fdd2a4ab154c625f43541ebc282606bd9dc2afe51f61573441bd49ddd9641da30bf40f38014f671874de837c45d62ecc39bcab05874ade4a176d16fb5ea196030d9653ccf121af3a5fe55aa5db8fb4bab6602b51c9cd8081071a78403c9c603d8eb3b95ddb603adc6e6cea20104d1d9e0b7f2d985bb25f6d4765893a8d7e45b4f6d24e8413413b9c8dbec2bce8364d6a646d56824062db0a7febf8266a7a3d84b0b147454fb6ba43a549059e0a63268012db06a126df51b28ac07b63848f3b80498b22aec2ea874cb36a63a6e1df3ed3b6650dbf6ef8589e456b9541fec6b9899c31622970302f25457159d170df57fdc0424322d3a4a2068bc246bd6957be05a96cd3943cdbb64dc165b413445630826edadc583b62690c040969e882dee792f8653b11afa0cd989aa70979b5b049be7eb3c5b53cba23bb5de4605ad4e16bc0934c66ba9025dcf310d02bd6f2a5ceee40badc2afcdef04c71700463a3b4447eed09005d3ad27ba1a70f9fb8f734a3f794ef1e826dc96185d2913f244847172b5c6928df9132c64f7bbb8fe42f021c48b04af9605a9f3eef4a210a5ce52583be62a58d4fab9f934de0144614445fb48901e6358da5257a937adac42e541ab7cb41c198606380767f10dc08fc4768f6a1b81163f1a83baafff661def6d5e85ab48d13d9d629b9781d297f69824067bf09f46bc5d542deebc89433147531eefd0d9e6eb3ca376b136f9df9dd732d98078a0fff7ac4e48e1e49bd9c2b649bd545dbbf72d03f95b33d7bf3c3d36ca1fa826e116adcb45e81ee9624513e88946853239b9c0fbd52bafbd3de130e1ac10ff527e501580cb1492c89fd660bf0d33cc9d295158fea75790dd3f89d849a402a7d238c018ac1064a7c901d4e50613f5dd1da15753d28f8147edb13bc1457c50ee296a063c064ca5541107e22b61f04b25dc418192a3e0467c9c61be9abdcb9b9ffd502f22e7bdb79fa947830ddb21d194e643737e9bb5f4226a3ac855ca17bf802912573ba51c6e2a63de74bc924965dc0b5a37bf6093f35ad94bb4e947bcfcc22424526608b047e7f112bc66b0fe64a6f6df6fe1a4f7fe0c3be1fca4de0d16665891652a40b6b92d16b5f0e88511d4b4c9cc898c45ad7e842d9dd21495fb931ff084069c05e046d91dbd6f9f59d558ca3ad9979a25b574b40d6296adcea94652d36a09b440b7f6c93ce3282baad3af3c4ce9dd03484070b4f13c46d1d56163e559a5b46417c21d6def9ba17c5cb36ac5f8855e0d8d6c3510b94865831564eb8814ba1ec46f7e3b02676dae4fd913d2c5ee10e17a1ea1f75cdacbcd7f08543e8733a077e20805127f6e266b3eaaa1ddb4d14054f4beae5445062fe69e1b260c0eb43b42b821d247d406835a5488d14a4d60659ee00dd0ba468322a13d664262792162bc5a169658a50896e1b86ea804d6daf54a7d2a14a51e7b308f4efd74bd5914891b446e3944230a821dfe55cc307366f8780c49d8e12a12633baa561a8101b10728ae10dac4d17f586a67be9b984854d83a5866aa6b8ab95b5bf9f35db8a183b1a7be53f10d0bdd9de1cbaa55c89733db6fa5aa5b007e50b1ba6812e457be23c5ead7f51cbfaf702f20bbcf6b9701740c764c5e7b9a9feb5143e2b4683fe6137243d33c2bec8a078a53949d1aac97447b5119806b40bbb37eb16ad943ef230a3022c6469501efad94264b6efdddfd1e07a6913f403479b2ceed91fae61e55fcf46e018f248af36de4cf268997b1dc61bf0815d04cd06dd6e7afe42b69843f4db546f7f021faca60461755b7acf0510a1cd920f634d10e4f763abf5ecb8fd1d41945079b0e865806e1ab0ab18b66af39499e3243b844e31e912aadb4a8a589c2d5cee76812553bd5e96392c7422db72f96092063ece5b00563fa43ac3c3a18f35a95b5dd507a35cac9d670a6947fa84c37481d301729bc74a58a97d7b208b09dabf279606c270c1a70090b195eb0692c08a7f5d53c4a2a369be7914751eeed04cab897ed9755d0547e1765d95184ac5297438820f4ce92a4719797e058de1be0b4443255796e389fe0150154920a3e3707954689343fa032072def4f36b8946b06ad1db10910fb125c192307f0d97db7de4ac5a6cf181ca5b960ec0d3553dc731273f9d5634adbe043eea986485906a196f1a35c0e2b45e209fe817d3217408e9fbd15085811f5ec2191c134b99aadaaf7a6fff388d891f12b5050b943afa2ba1616ec81c24f6610df855cb7aef574eb0276c9848ed44788e7d5ea2ee81dcf170ea64b2b79ef6ab8918a7de6ce00ddd2262ae3b4a91038dae677570f1b077029967512b656202f7c45701ec34f5c56086d9bc289a0f66375317561a4607606e2402fed36a6095747a8d2c67ad19f2d7665db90a15bcf9b4bf239691abd254665ac539f7333ce552c767001537816be7095d1b9fa5d673c1f38981f39fcb8af9b4506b9a0406bc093560fcb0f237782fc316880e8771c43d3de71fbf7490149b59ba7057480f4f88c4d6c1e33e2d060aedf9b6a766e4e452051b0bbdb55b49dee94e3149fa65599e0967f4ba4ec021e8ff2d545715d414d45297f0fdbc155054d76a968bdcc3633c9eaecc00d4fbd41f70ce7ae1bc5bea4c29a3206c07875bf26079e3bb8585da2f57698ad7a568757d6120f656be6adfbe3df54d88fa118483e308058fb8966f1c2931f6c65da2925075b8921806920d984e123618c12f0e1d6a58d3c3ffd30de863eff8cebe1880efc26c90887c14e307b602beb5fdb15db5cdf2bd823536533b488270953c0bb4aeae543a41f1a72388934485ec0ed8c9227f05160ad1509f1cee4b6000110b5b2153b08378cf2386d05c78e499a46e6e210933b340c2c50139c25b0acbd6a21d4a8a0bb308282f0747664c7e61fe49e21e8f5527bc804e1c4304f9b1c3d4a278834b88e0ffc3476d4add4f968ca1dddbc99ad90acbeadc38c90f21d94aa276a7058136b0c544a2196f7ac201141ff6250b15665d677d0891aeedbc0b0b95c1a95aefcc2bbfe70b776f5851f08eccc155b9766e2f5b55c5240e733f5fafc2346757055a589523b0084e0da0ef31a8bb0b26854dd5140c48e405954f4861a9d03eb69a81206c509e9aabb9efab418535a54dfa497c640b4ddce5e60184af7f8c7cc517c71bbf4585157faf81336aaee6bacd927e6d9566749ab95555e566150b8604ff54a4fe855062a06c5ac1e66d764db4578330ad3220e1961896adcb97232dd5902f8a49f55b07131884981f19a0b60964cf58cf9700bb962590c6d28632b0b6ec1e94a9eef7918663948d360c8f6b3fdb4265db8741b62c43fa3a6d965d9e191a543f399f2e2131a55ab5241089d9d9d08f391641db21ae2510cdd51308b3e9328362e988e442c9dda6c9b5c727ceb15b5e089617d5e607f11f5df56d82d53733472c1903b309451d56bdd8c878d1ae2035dc4503a5e505e594094cc2a8441fa7a0e6d247ec1974bb027d6000f8214c5c735df400f8695930a014e34364aa15a644f26822fc49f7284ab991f8f6c10fad2f03edf32f34f44f1a80aec3431834f97de1664f7002dcfe2a6ba8f340402977970a5c95ff6247c8536aea08604fc44e4083ca02a37a93caa12092c0f58120a681e5a110d381c581129581e540131781f550129340fa90c0c4e406913bf318e97b02458a7ca1469c3ffd73cfb4f8da78f34a36a1ba2f42e485588794906354081b1c445883db2b516bc47de25f9e31dd991d2bc33ba96cfd170bfc577fa71b942ef713ef33835b7efe28284e783e11e0c7032a794db322cc1fd54cf247ca3facd8f5fa491f3558bf83e8a80472968ed0559e073874b0d767990cc3dba1818d445fdf77e90c3f2fc02448f818dd7c6c854262640ec4189007ac9be21baf0e561fed65e031350e8ae739d0dc741e5630efaa199df9c77c4dd7c5c2a2500bcb995f90aa71d68f9a470eeee04caf49d009bb512dc9b178219b72b5ab44ef5af8df73a52dcfd2ad0d92fef3a11d7054aaeccd5d739f682a045aa284abe391bf715ce7f374d47241b243a4ed80fb7940bb22a1c9b6302cc599464aba0c52af09c7fb5b46ee43f3b52e730b16848082c1133b5cd85bdcb8812623fdd76ecc716c429cf4d71b0b9b468b59921a5a8050bf985163a4fa5ca8c64372912df4aac627fb43b6350703682d661d887acd98f9f90c06bf775ef3d4eed2030fb0f075d3a77b41560f14c86c829c9d39541c79299de3b6d304c8ce62f68dd0b3362bad5bbafcd60dfd5a75df177ea0dcf3f05c170712e5925db701e0b799a6e7edcf97e089f45e2ff30823c7b01fd6a5253d18ea02b0fec91a8e99d6d2d9ca8c003e9356da2672f629209655a189e7d66f83969b85cece700cd94cda337f56045beab29fcd35a7316cabf4c59339eb6543dbfb2d8c990103e1a4d4ac66e8dc7df3224425da0d1d61c2a143580e1db324264271b8615ff1729f1e9b72bea3f4b30c8c6e1dd482e2fbbe1a54cf3fbbd39ae64d821ba35c083445685b8adfdbbc20a16de3b58299bef521932ea988b02da1cc4ed60f789eac7a0576ee500129ffaf88d09de5a64b7f39721948f0a4e0f5323b653962a700180da0f61a410acc833000c901d627af760282000292fdcfca87c362ef907e3f46f1ee4c9423c3ffafad77eeb4ca2313d9c6b6be44c83beac19ae82e6c9a42f03b5f0ec80b21f9081c48ecc1cd87120301b0a442f2227f40fffafaf75b8afc94e020a4ffd66dd95eb635bb3528964176cecc0dee300262ae3df7ac164355bbdf12b6e49bb8cd83b85f2ac5925a71cb8015bbb2eba39d6feb4ab3efdea4f079d23f3fe6b7813d18a0232a04af436b3053004308c43135ae521494cab61f6071552e9767b93490ec84a854ae36ece74ef6d00e17400fa2be229a971c12d75725b20f4e74c179e263f9cca8fe19280e2ba096ec71c4aa812a11a8febbe1bbc4239dad578f1800d2c041b58277b95944912b347cf40724503d1fb4952c132528892283786aae865be1c4e0ddab26230711b7fa90eb42a0c1b26b9629b93c856e1cbfea9f8f2cd3ee23a8fdad53a6d8d3689668d8ef5375434531eeb244186fc26c1eb00bf9427ea3e200b08bffa1a34095069946be50226e6e3940e368746d49fc93263fff91fd9ae32f00068215f6237f7b5aace2c03a1bf58e012e1146dba93dcec64595d7a15ce281609de11f34f4e51e00d9d016ce7b2a417c6754ba3188e07b51af5fe38b6d460afdc3975c897494073a00f2e6ad6c02f8b507bc5ef3f2d3e8e89cb2fbceb350eb3a7f9b4369323a1ce41c124fa9c070023dad662e54afdbbde2aeb8e66ac79aea9e4efb5e5b076dec7aaaa4be064330453fc285e24d39dfc48d1edd4d301e32c8a316187d762a1b60f2cc7285ed35907d35c6579c744f0e4e9149706d45e13c96746c404372cb6e2edbad39f144496ff5818da6ae6c8d2b09b8b49f1291eff72cd7c84d0a38073dd3b905e1f4df1952750234488a02c28ccb487236312fbff8132761e565b393bbd2d5fd8bb6c3f2de960a5b9930f97141981cfb80411fad3434244a82ee731038a2174b124f9f971adb82838dba949c913deaf854d7afdee138572a448b2546bb7f3ef81ae017111dc63d025b9a7bbdf2c0010bcf7c0f4faa98dc7ec3669aef60b03e53a28de2534587e36e3c432b8e2c7a1cc92cf014a1af794d25b998500a16ec283188a3f2f21ee74e3b7b02627abcee1686afb3953b4689c804e322984c15a14ca09915066db4f4a5d926144505846b3de06fa144dcda2607847ccf34f91ca472a9440dfca9d304bfc32137e0dea16d820593a8d414fafa696a32949517915a046f65b295b7b1bd90458e5fea0781d02cbbc83af7bcf5d73230ba44215156a751d665f66a060e80ea6b8bcc74d0aeff7b40147d7c58f9dff33da88778011609e9dff30635e1beb0b0e960aa82d6c3c3add728eff69d134911133bc8e8f074f3616edf2c257d4374f48d4fdba845afe4ef74f028b4447e2b85719f95a4c2bc7b8945f36a98642c62e1a746ecbb60c67749262f537a76898288883d770f8961eb48b061870e60d1db396ec86f7d055d72be492260d541074eb27e842c87e339eb84ec5ae715266845fd1967242ff04122b8b31bc12f3b4bb9c8158c63e1fb677a7326646a396b7b3f0225c810c7386ee902a52f9ad6b2b664636eb63a4bd29a189480b0626cb0d0c8165d78002c2c0a889a11f8b762f8c3660180b0c71a623053405ecc6c47f9c158b054e0d454e44e6c71a3616f19be785970a5c078d196103140170d22dcdb7cae7e9ff081c3aeedcf9540d31c8cf21002048ec64beb8ff86457bbe3227c980a587bb36ddc7bce824238e2268c4d2755d24cf64f77640ae7fe99cb1a412cef54f5e56f00177ac97d0fc57760c00a045ba7bf06e975117f436ce1e455d021ff88898700a6af71e48670f5c70140df7a4bc1a3423c810137d4807305ea49be8956b4adb20d49ec6d86edfb908477c1743ab1d23b51d19efb7fd141b9b588cfcd643a7e1042633f1783dc306b48c1e61179087f479930313e92e8fbcaf2d827ec5d2277afe8ec6b10aa32aac318d2949f95807b36b2fdd5fc9b502076874dfe6db4e3a28b098e64b92351617296ad36c4b3a0f250e13a5c2afdcd1b6d1da631dbc95800c990b39de62dc497e3163ebc57ce06f4c031991d65d03b5fc709666b7e17f1c0b6ad2c9cc0ec61119510d3f6d4a85ff0a5cafafca108fa99794b49188e5b4a69cfd8ce3ad80bdcabbaeffc51a42f28373da41d5cd9327bd84558cc0b51fb8e23ee3218c82e52dac80e83f182b09c95ef0bf0a97b62144899b19a13a5b82799f96480588790c80ca39948529f9d8f3cb602983dec327ed7d2f4d9de24354cb8830b34a1900c8fdd4c829723b0035ab4d94289f81050cb16689305a4d97354f9ecaf0a0e276271e121b8fa43950187a05a7a45ae4b4b8f9f42aad8776304b20a11e2f13584f56df7e33b943596f5f7932d3f660df60ebe4f8e67f1b1f7d63ce4311e0079baf5c0b45ef265bd33ad0107e344f1010e5014bba7d8cb2c46cd484ccab3f45d2d1e57de91c36b25f39cebc7fdc82d9f640e9089d7cc03bba642f9fa635e01fd046d6907fe80bd92f1bfcaacd1082be325c8b9236cd9ac2d2ff9d6cc44fe2a599850f5ed9d67803eb072e6c73b74835080ee8bd982950ff74beec8abab7264271f329c4a31992839b946264e0b459d71d0740cbc87aafc4b05e78816494aeb98eef8e73e38731a8253ffd1cecef700e53f601625821fc56cbb8005c1b9b7b1cf0005e853f256c83231baaf8a74167af5c2a05fe1ee00b6215ff04a65f16990806a60a300e839c50914bbbc62f2d91d0abb6434b911ad10fa6f55f0064b4fbff5f3deba4049252bc11bacd4789259ee31d793037941562851560588713dc932d1fa4ed78e5edd8e16eb45d5f7fb7e9d6282beec6fa7d4ef9a04f86182a39f4f5fd51f2156523b2c8e089d0611a8166d3df646c850b4dc311f41d1dcb9ee1bf18430b152df2a5c4bb7aea1be94120857c1a9e2bdcfad584cb3937c110eb856497103e5ab038944200c30b6891ecc684658e15b2dd4e449e0cfb2b26c513c1610e8bded7e31abd0fec194f5eddc0e06fcdaccb31ccced08577e25d26797b65711a589fdbd37f9b1f5caef71122a65c07bd78f0805c0615d14ca10a285be1f0ff4ed10b49e532763753afb709092218cd0df0a289e72df82ce77ad771097422cb89fa0c0bbb6c098546884850169313fa741c8cb1207b313ccb65a6540c5bfc9afd3c113ede53fdc4977cf920f1813d030e39ea523727bb1610cb46e056d4ed359feb8fe99a441a55b8a91255e943981f433a059af8ff105cdac143beddd60ca60619b1c553d3eb4ffc63e06e783e69fdaff2abb47b805207a8cba1193986b42679274917b7f49b26f3119807771adafd64946a4dca45744ef1a0d682260103d57ecc59a306f266ae4360a235225cd42bf393cc1b0a63702dde2c6abedd5e505291728648242a614222b6c9da941b906cce99a2291491f052aeffb50ac5912a20e2656349f2a4822d7437390552110219d7b58209167ae1d6803e9996e8a82cf4b238ff80d000d81966892854ee0ac62e07df3b89f6e3e29f8ee919f4651210de8c65012dbf31621ea49767ee4a541456e91f930449aa77625cd8b696dc3d3ea923875f51eb6312f75dd99ba58ce42242cd3d0e2133044f00522acc920e96a660079a51363e6bbbfe559b11df2c2aa97d5d44102162f802d30fcb6330e8e56fabddc616fc63fadc3bf05110997a0a62e54936a217bc9891ca800047ef49e830021608ece819cd1154f9ea37d63041dc83de2f53214291eb9c459a5777e2467f46f1a221732d1275a1330f4cd38d6b258af4c55c9400b47b9fe3de1b466c19b6618f8f50e82177c847748dc600317d20b4d51abbdf2065be0e4f1f45330161256ee4a3a739da560ab95878ff75e3288f320b32bd8cd26035aaf826a1f93b5a2688b4f39556a29e06422642a88148ad3beff85434aa4c615b056cded2e92bac84e95419238692ffacd94e5c30d64df0d712251c2833ebcc06a4c406d2ad51dc30ae984a05c0a9db0a58408c799b61cacb0c4daffc8fb5cfdf95cc3a2a599e866ac370c34333da97d0f2415817818834659cc78a8c39398554679556b7014067e4b57edf0bdf8c81a94a5c2867e7fb058cbed460ef3d2fb868a4c1a8387fff81e7474d5ce86539b908b4c07e0ecda41a0fe6e59572253cb961539d6ce0bd81fac9d46a93c80aeb7edb30678783d4985bca4caa055f5dbf9a4fbe611391247f14a2285659e22cffd367c0a93cccb00d404c21cd28baa4ca96912ada7cdf5083a3db2ee3395e8a49945248ba13827bfb23beb64f1d3821ba4e3d7ebbce2c74ddd8fe513fbb48efdaa871b21e6b96d947eda6260e5409b4f324277da6a54b626e316d3caa0ccfad271503a6eb8512f712c3ea0e943abbac30c46b6ab1ae588c715ecea5805ee03167693fde5acc0ef22321627d282cd1ceb57790c68fccb57d0a8dbea006f2ba472a0ab5d9ebe3eff21195d6ab7444e623c6a96cd9da5db976db569b21e7516edb2a5d2a0ad54e8755ba105ab75b492acf7680f69211c4c3fdc2b5ab691b77c08fb867996f3f29db23c21474788f20d33f40a35d0fcc693b955a2aabeccf0853f63c680864faa5d513c3e17c3e7a9d2ca9068c11590af3694d2c2afa2a1b2bd8da9e7258ef787bba084eee4f2b1e506356a6cf0776a11519c1a9fbd214d981dff8bfa3951bb9d63f14b972992c4a4c9baa33f43b2a3092cdc35cb65dfd697b5f605f1958c1fa001204bd41216ea28b72f8589d54ce6154e70a6481b0a93076aae29741b0b7d8396a3d82a4451ef34e2f35f49ad9caf5f546d2cf9cc5a8d0af5594ba8dd405edef4456abb7f534367153e8827a4094bd5530043bd5eccae51be53565f9d0d3ab19afa6dc46e32179fb5b89c1b7bb7c0b9abf559dc16468cf4cd066c69e72190d6570b368cc65d515f79a7d26af4af3c736de24344efc0ed650d0b32287af3a65a1b456525802c1cf9dbf27f7202c4fe03848d087a2f15e176fda6c02c2d4b1bde2c4ecd9829f31364db839e812ee35581fab446ee07883916806994045eda24771c5ebfa43568512fcef0e39b3cd57d535b0b161181702b1703a14ccdcd36d1caa5ebcb6f15f05a74c694ee58788a2ea2c2aafebe6b1e5e64cb8e5587493b27eae3c645ef5a220f837e5fdd19698a596f4f22eaecb9b44eab2993ff5417742df522907c6a29d5e7dcc58930760ba6620a0f2b10bbaf8cb882757d5f39e4b6d4208997ddcf9f2dd0fafb02ed325579f025d662d62d5582639edd45777c0c5b2e5633a899740b50e56296972f6653e20d7365c89490c6be223691de575cdd506cc8973500fa67a89a43b91760370583a9eae5c560ddac8122bf4214bd6ca2d33525aee4dae8a27f5ae5624d30979611e80732b8c46245a8cad8427c1b365952de9324074901f27c597e2c2406042b317c1a1bc26007116402710572e103321888dd2a2a664479a8168690f50cf8494aa23a7fcac39b4ec4affb48f0cd3103a7e3d2f995b75e5a3a11b240e466eeb90b8ba8a800de2a279d0d59b8785cbf5639e7bc300fb8ac37bb7632472e3d457eb1d135840fae6a60800fbcf67c393578f6a8b10b3c4af2a959ebb449eaccb5aeb5370da777293c19f3fedf9c4cc5570e29e7048d49b01e862dffae96c9d994ced52bb5381581dbe3e7f3ac35435e1b2c49a8cae17975dc69969d601765d0dc0823af0d51b5db0acea54866a4392dbfdfbf80f7628e8659a4a63f6f5c47a23b8f276cf17f5c4e9b81626aaf37e306aca94de7abc4e8a3da85848e5dbfb6194367048642aeb43baa2836ed947d205a091117b81f3e476398deab16833deb4b6f3df84417fd3119a08dcb88470b0ddc8286f7ce17f5b0051fe2fb306e315e48c5af6f74474fa68e2e7668a79a9578ec4c67a611c8615488370b3d23c2702458dd4ff005a65b4fc37c4277c95ec0c878305cf9b2c4b0dd13cd493f3aeaffb70aba44e6054047f15165fd67176fc14409f4fae8b8ec3e4dd1ff224e9e1821b463a50c519e3c07588c224fbf705eabd63ca509a1c80b7144579a2fce4117bb7f95b444bb667ca5c593e7081e3c437045644de8ab6cb3144df8cf3f005d082786e5773712b8b15f8f8eaeb060033474c0a585cee921481d21ec63cd047bf9cc05714d54eb4f73c2726a7157b385f95122f1ebdbde3d668acfd8b5762d4e1afa738a85e61e4eeff9cccb44b7d0b2f09c033c5bb0ffabf4a0e806c830aa0bd8be17b9cc053dd86ca7975842e985f3b6c41282bcbc84c38ee6102882c0752980f16b93a5cde54822a8bdb1beb10a96beef437542918a0e9088662eebae50e4845882ce19e4e00cb89c5a8861eb7a957ed45a0b140565ab91cdf04f453459f75bb7fdd71b26374f2252e34f5dcb501002a7da1376afc1ec8c838a4b741fc212bb4085f4037050c8d0402ddd7b8c46cac2e56f53414a9bf5a59232793bcee8107c462c6ded846e3eaabddc1adce65959852c559e3d21c4419712ba55a02c369caafc53168b3cd0b331cdf3652b14304e14607b5ec4c3f9bbf7f98732a47509fd4dbd1d67df4f3fbdc4ece0206f0234e31eefaf4dc8e60ad398d0c1c8468562469775341dc4038e5dd895eb2cc8e2a0d6837234ae498207306e07d2e135a627c16622116ce2986654fc9a52ff05de427eb63a9c8d1f2b271102d028f552eb2374f9f1aecb6c86615f493c986dbead489f6056c48539a13c2b142465387f78b3aaa26891948f22001a831fe15b2833ae7283faac1393d417235ff2e95e862dc8d84207fb3e8a7945275ec5849f10c3fea139dd1690e1bb5cabf133f5166be34536c7d64937334f29980aaea7d007c3db95ac3ab41da854a276f63d6eea50d7872c99c5e43ab659764f45692e7624a704dc4a6d8b086eb9490cc5d45d90bc73f22e9b2116d9b1066cf2b9f7ec0da7b49a10865bf4130e88afa0ef2a2055c41301350ff6a7490c6a868d8840b38a62f5a062ca6b0491c35e4799c4e39822adcdb07678b1c34e4d8a941e1d3d59a270644e461a5b53dc9dcc1870b57b30ed92b74428b6bb1425f2be37e45835adc507cc13d5e1ab1006a282b9d5220d22b2d5481aa7a65cf30a69b924004782aea5ef4c535ff5e2a938b0d7e6ae605e887e64a3c5b1f45660c0bc7123a4dc61cb349f1403d935e2f988ecfb55b4914aa6b72415a1a0bd2bfbf8c70943209ac741b0160e47f2ba90219363cbe597b5dc8646b54edd8175ed3972c454205827f7bed59e9bc30c2dddbb5f317222764658baf92845afc94f37035be396f666ae296a258f52c373f19272a4a5153341078c27deb402a4aee08075e350914c9224c0fa083d1167267a6731d7ef14bd9a3df0c6e87fdc89218cbe74aa1fd370787ddfb9cece07107b87ee4eae49ffca5919535c5571b9e973f48b70a915e395cbdcf7392a173e2474ea28a33e5b97d16946c972a939e8aaea6e96761170fb19b3d0de877db194fc4354200b1d92eae5758e29b78d89f9e5e7a7c9d8afe54007345a1bd24af4496765e0d0b37d91a2888b702363d161099b32d9e7068aa8c749ff603b05e91ad3237440ce46b3243034a9cb7b3f77e9438495757ca3647710bdd36bfcad11473f3e7f58be0719b1b9053f7582954d9a8a4f8611b19e92feaccb702a53598b8f31bb2cf7963a1cc4df8375f5b80e8cdd7afa8d44ed27fd61438b2516dc48eb9c65635f5246f59e88b1569caee67cee362ba39273276d85da021e8a0dd99fbb03eabe4865f13cfd880a79d5dacf0d21cbe0d0ada22fb9bc4518139cc6520ad2f2491232e1a37db80b1f10ffc7b22d7b0dd4ec8909ff47ad884ee9d0fbb45021b3612b85473032cadad230b0284ed19d3db0be753fdd71b56011c87eff547d63244d7cc36231f4e48c3b76dbec9e092ede4f5817175b1d76f259f70947daf66a77ddf444c3ccc8152d2c295b098809ceff6b700d8cd85e0c139bbf68f02163642ddb08ff4c2a57bab819fd15792562767d66350584c0ab47c53004f54f91e3cb0e7b40936c2fbb211f729aebf9caeb04ab37aaf0e6fbf019514877e94261b929c341345669587fac30bb4255aad9dfe0f01773545a15392c536c5370d08ff729e6d3693b028ac0e343dfdb95c61099e0db90e3eac07e089ebbb795ddb3768797ddf768020c74fdf524effcd13ddcb53cd3d4f04701a35fa40f92975a4604147504af4232760b34298881e7958f09dad8bf13911fb77a541d325e006e67b261c96f3a47996388b9705e5eafcbb3ce34745863d49d0cb2c061ba9bd0b0403fef3c5317ec6750df25757bd1c3c763c0630303af4b98d80e6ca92c841737478ad832b050279b9c3fe977e2717c2b9803efa862f8604cfc3f48cc76f5e4303577ef04b25ec041285300baa7829ea3fb4c9c0a627cad08d2585b82ebc982cb0dccac25554c40f87a964f4fcf4bd465156ddcb51fc6a0b8b00050fd85bd4ed2b58870172f034a2a26905a8e0d88c30cde653a992768de414c05e1fbe232689717d888c8b7b4d04a7a63864a934fdf0ecfd606c2d2d50ae67d7be8ad484c810523c810db76db506fb27b71a16e44746ff775d87aeef926d38fa37e00a428ec81078bb83804c4f0678048eae5224b55e0068fb3ef85772b3d7aad0fb73fd69c685f8904fa2fee016e5e7c87c1b6d4bef89308532d18999415064565e9e8fc2d8be2af5a029e62526520192b036577fabc18f07ce82e6d29bb7c802dba5d2d9f4d32cc6f3566135aea079d24072c42fecbe2eb4eeb4ea4a5001670a628147f6f872195c55237fb679ce76edd2797de5dd0bda5942a7829209ce2f9c6de69bb5144bb93349de5ab2cdcb8592a2d5463f059d01f00278169ca7d48698cedb91b250796b87f5a5e652bbf1d896b710de9b50a6066c28ec0800f1d63d83d11e5f036c702e78220263bb800229061e76eb253a4946162a27860aacffa6bf92792be532f6a9d68822c4e038465a5a1e8d9a858e6db88d40cdbedfbdaed40e877f74674dfaf6c89a7afd474a8a606a91fd39277bd8c6ef583baf041dd5f1aa8c97c25ce339190c41915a009c9a16ba783d62f971737e48ef7d6b111c82bb92fbc00581a31141ddced818202fd12e3a930516545f4aa2abf753dca314b1d02527daf0d1acf568ed3f1248dda1b5a8c62fb24e79e8b82c8fb24c6b4890982ed3af8589495867f2bf1a706a1f13c8552b1a750b83929c36af67f84f0d1c0affd7c0e595968d6df8d2320922bda8c1a9857543df386d6606b044915c2cd9fbdd17a57819c87da9c9ce278602be7afa0d56ad32456d6d6c2263d621c9c139ea8dd606bb7a5a3168b09a8e97d54af5a10d31b4835c95fb0b020987c0dfda7d2a6512c757f8ac8a0d5e43021fa6182534b2ae14e4f5209bea2a11ddbcdab04ffd1be5956980e7e4a560545684790df4e1f9074de605a23f7667d6fbf1d50bc5adb0877a4e7b9c0d0029af5dde03a28ae0c82f6fa8a9385e9ae3827d67c099ca3746f7411b03411caf222fb4b803fd25d3c87db39865309dd35bdebbc00a1f05fd71a5d18d142e738c0c5578743c0c32d02d6bcc1a7c754f602964e45bf4a59848173e4b947d8a59042744ad12d777c2b5cd6a0ad5eedb65da09042ce16b458075b6193c8fe365a3f5c4af76a58fca075b7fcb30d87c90f185e4d20f96579631fb1b2d38ba634f7cacdf3b3eaf67f701aa47b3582cb2f80c1ce7e00179113db311efecc575c8c12378bc13776d0f736512c39aa9783d373b3de83144372f79a482b1f04e1e63d67e0f38d3faff7e0ba047b498b8683a8dda91ac450bdf2fd5d5a0fe374f6e90ebd02ecae1743135d0ba353ba6f2488fdec95f7cc8c37e9643acc1dae07b4b17db027ad725dc9edb1615df4f563a8e5db6aae5e0ece61913d3685ea1684c8ce971598f185408aa13f9d1dfe3be931dc79be17127d40867268ad607aed970b4bbfa39371dd034293528e6593b7e8872e21d43cee3f19d13d1bd8b38046f19e1ed9e6fe261e60af251321d2d98e2a268509ecfeac835dab8ca83c5d989a68a4f1117f25298dbae649a18f368fb70d5258c38059429180320e369b6cec9941c2ce9ff40b9a28946ab8dbd4fc1805681afd157947451a9a8937fddbac8653be707db4bf19fa8c144c9a15138f9bcf6f1fc8ca66fd09a74c8bb513a40316bb072f148963701ab169d6c9957ec85692c4aea1000dd31e70133540c6f1efd082b6ea251e48c3d2399c9261b10e262f348d6564e755c0aeaa53e9074ac2ffa13bac0649028b03f3f39a8625a343dc4040f1959cb9016eb6d33bc40b7727cbf8694ce0f7adf170e6ef5967c9ce41ec75479ba187931d3831800310a3022d64bb7584e57b7b117830fe6ca03610aa5a98fadc59cf700433b8c3887ac4c6274f52cdda6da1382897cd6351c0b8d8a260f25ed5fb1bff3e75cb42c4485c2f5e379b8da6d2d6e834e4e9c10dfca06983dfddbfbfcc8ecd477951579815cdca9b03d1b148c2bdea816ca405862fc76d9cf1b47e3f54f7f12ed6623633148ad33b238d65fd5d1ffe48b519d2de0ad16f18feff58ef3644c78c9f025472fec6fb578447769187e22520f1fed809b1b54697982ad3d91773c85d7e95992b64d5acbe27ba12c234c162ea391804c6ffe14c2de510fae65bc0128eb0b690f240af786ca2726b8628a7c4a3cc18504bcf95a0b61a5df8b4dd52465309366e9b7ff8a54c5a7870e522eb9ea3a98db69563222068def994e9d0161178003f718c022b94532e86dda6a2189d798554d35ca1fb8cb591c56829e6a181f22806471b74152e30c70ee9eb9f64a4d6f6b2aac3fd25412d2927c0e05a10d65ad917d7e621cd99bae3a75b9716b92e17c446963dfc10e433a2cda6b727b64acac2663137b00d59d8a86526120117bd7de91231e53d9dab956367dce1d890a69f3a58f133a8161e2cc761c7e49cd3b0ce6f70ad33346529102be4c5a75b0f53dbe66ae287508c1ff5fd3bb8348fa6b2cd93ec013b36452138d466467b6212345b3aefe3dd634e3dcf8b88cbdc2d88cb58c3a9937bf985f32290496915d5547e510e65b5a37e3d9f7f15ea602dfa3889544b8bf376ae61c2f07053484eed422cd3ae884e5ffc47e905eb78b997b2ab955ea33fa93ce0afb5373bc821ce19e25ea99ff1daab0e11df3f477c37f252d22d9c5815c402943be387000e4c409b5427c8789131c87cc0ec902f415726618f781c4b238ccff26feeb40c38b8cac2e253cd83596b5e22ad63a05e861ff7580dea74849926eef5747ee1da8ad41e709380069f53d3100381bfc213343e43db760167e44989f468771a44a08e3aa3e8ecf2f033d4b043e3a536babd86d5980a4f70de6ff13fee1d5c41db21fc053f3d76a42bc429111fdb82d7bc43c7c75a70af9a44f4be62849898936a37618a441da7803ddc4338544f4d56117a60a3905d909c45cb7246373e4e0a11dd3a65c867701a0203e919af9664a9e749ad70f57cca2419ea4a807a3e33083836cc935d7f288b09a53ed9705429589d1dfb9fc80fdcf0a5619d7bdf961703d8cec7ec0533cc2cf22c3a30386b9e322ce2ba7a1a8bd5f1f49dab4c86cd44433ffcc26b0d00ce7f46f5d1e5e4260d6e06c3438d7153b8876e102ddfd2aae99b8eb67813b0df27271489e2ad4addb0a9cfbca5ffd572204ab3baa20c1747e042c53d03a383c070ba958f1e4fd2cdcec75341710efc41dbc3ffa14caac836b03fb04c66d606afa64279cea5d583cf22921e51f8356d11dfd27668fc6673beaa1cb24b73ea0dc27de3c1112deb5f9489c98207674cb0cd88b47d99ae28d28d15ebd6da43dbf84ce1ca1145dfca82b1f347174c345a310573f075820cd80a8a4a0eb4e7fbacabd12a16dcdfad4e2cfce6a4338159bd24eef96a0bf31cb2d7eaf5ba63c063ef1793839569d522c6049cfb756bc6cdf82778f84748b46aeb5f5174b8459d01ff091d4592d2579bb3d52eb3aa236419bddc1ad19916b96ae415ed48e0631b885128d73ad7e2dc0fba369373da84525af6c0a324cee285452a6f77b77e8aa777466c485aaad60e675d81768f5bcca93bd53f9da789248b9dc1a091440c06654cc3b6e9363da6130d10c4bd18f2b540bb962dc39610344535bd6427a5303118029a096577fa60ec57f26b395247f0bbadcb1f07453cf4f13e2280547e9cfd3bb9908d5991f87222ea42dd822da4b95e26f5b84821309460a6662f82851167fb3d920ee01d546d83cf3b7491091fb18cb948fea8a279c6c6ba55c107bdd41c69483d466a7e0d72d4ea88095d6e2ccf46a18f73a160d7b88ca4446afd5e88aec5b7edc5400ead898d1c024d4525ddc13324cc4ad40024f42d832fa377e4a58c0db256b5591c43bc625cc8485a5f77fe862a091c790408941f60fd7509c705bb289042fc8380d8145eb6a185e33f74509142e5dd6f01db4163dbef823dcb2b6e080a51a0dc36538ff17877ad2bbb77bf1f81c303e97c15a014c9da576eb918ac09dae3cc34725e881e0f14c95d0a4c610ad5ba03e59da3eee07fe6f1039a81de879c979454acb1e427cad1a012497b9562167c76986c8c84b14089aa66f192613d2a3ade0596c52230034236fa054fe7c2704dc45b5cf041c2d1359124f8b2c1617b46a526f37b6148c4e4b102f0dd25525b6324032e963b3a73d00be07b1ff8250c21fbf25a8efaaa161399d437b27f2ce12c67988f42d51e9486637d5a2d7a1aa714fcea1cfb32c210e83c3a9a9235d95264a58a64be9219777905b199dff4864a5d2b48392384eace41b29230d99bc571b3b62c29af02d1c16679e364f0c8d21e62e53a554f4188824578f87c20c92a63e9c6a77199fcc3b2a6dffc1a17c5653827c7e86be99eb96cbf32cc7a89b49efe29e26b58b53e0c12cff6220ece447cbaac920eefe93b3204f519ac636664084605f6a6cb1c3a0ea1bf3b8dc78b6a02ce4bfe7e0ca95f96a104d199304f66a8299ab24b905bc4d580a256ef191eba25ec5ccc4bcc9279712dbbb3f76d2178c9d231d069136ea641296afd069ecee03683221bdd85bbf3199794fb5f6be54cb9834d40e58c4355a936e08c87295e8db09b413d6944f1724ffc2183116c06ecc6c2a10da996247d9a78cc8266320e1fe4d8e13774615be2a3e6993cd44d9a0e99236d4bc2918f53509f88ec550ecd78a534c97aa069d3fad9e37986f4451f697e0bb0a8dc00c05387311cc6221adcb698b8ad1285a96f7e46e7377ee1e3f4d43fd0664eb77925f2d12b10ff42748ac53bda7da26d56d52d800ed4d4f449644da917d6a7b5ea87d9b033682c040f930773718570a98d4fe6c1693c31ca6cc58d2176913525a96bfcc0ba8be2393f1ddf22367723eb39b6f5bb2df9bd41170cff99c493138ce9a6d8f982c559e6a47f7de8eeb3d0f9655616c35c3d5371962a090df57cd0fa4aa761d6903cb91419c4f42512d670213dab563c451fe32835741f8ac5f71a0624ac62748d5ed41bb548e77a18f0710d695bc47450a1b874c46679bd075dbe5c57b13f30ebe2d0a68a3576b6e51c1a3926eb6057704af20ac0a6a1be33baa6806756e6ff88b853c58f405c404f0bf36bb05ad9a2847f29b97dd3267a80e9ae813aec42c9ac1fff6d468470295b506ba5cc5ae7bd70a52b73104bd782e14a430e15480e3220cc496305cef79c980ecb25cd2a27e4b24088ac63dc0f5eea02c7abdafff3cca10204f209b6577c2b7f643a3e90be5c891a37f116285d5728002ea8029689d87318be5b68402ec80845b5eef691379b6a9b24a10c6c978967e054adb6f4b265fa93f3e9b95f4399f83f4fe5954cfc460d0f63fdb35969b9599dbb2becec7448710682a7fd7f1688fde05057f9f474edd1e6a45aec16ed1943d3b45a2ec03504af1cf12ec4264400198ae7a256d3dac098d5b44cd38f0add65cac3336fac4d1b9e381badecc19dec652f177fa300d4e3fe1454528f1d78330d4d5f5503e80b0776063e1f50729461b83e63904620824f7bd6b5fb88bcc91a1d2fcdc3366852c864665897faf86aaa2fc31a99ecf7b7345d33f3891f560e19ef426dff389d1f97772ab409809c1c83a8c909ccb4d1cf546e70447a01a4ee37fa6189b94989164472462bf5c7c8885c52dd4384e5028d9a82cc901c89aa770095e15b4ec3527c533911d4fb145134bd962ea0318afbb5b026cf0f8739b1427691da997acc8fc15c834bd27a9ecf65b1b68b502ac1c2231f17b015790225936d90814f32a6dd7688876e992ebf656274752faf44832fcb5dc1cf4c05b3f41440f7acb68305063b32edb28c736726045cbb6325bd2e9c25076b1765a9f49c8912a75ca97f7d8285ef2c6df4d0542044968ac4c2da0d9552192af91d83b127e8060a6c987493b1f4680fe9341c4037b6b32ea33b636ecd8dd3503f071dfb516ed2055b5e592955c0a4841bb647dc61c1676be08c8f75900dbe1b53c993f3063b9d7919f21a56e7d2b977b0384e99b6cd946e03c2f08d5632b74dc6ae62566e2673b4bca0245d51f5e601682896ca954c834239e54443cae9971c493a94c4196d2346ee3a0bd040f668e5e49e943e924854c7b9d1dbd2208ca252badc4c394c84c276b28c3970cd99ddc9f0084502b15e1bbaff56707659c10db613f0391b65e33ba13c26c85ae3ae8f03e11b8185f126ae053e723671c5ee4ee8aa0f90a1cae00f01fd15839b6f95ce2e5cf9b627fe42041fbb976c7f04ab5f82e4eb0c378f559c5d57ff4e80e206b503fd88e04ac22824c686ec0dba2e933b191fa14020de6b43f7df2a8e2e2b7183b2d5b2b1fd6f668aed87695869471b57fa8334569ac38d15f6b8b92bc94a265460dbb573f415e4ed032af3ce965a20181ea5033d9acde470a13059648294f547a6e8ae580f6d8cd2cb48845e92b3385094645a8948ccb06fe0d102a9acb732d90397473a1930848510a016c9a10d324fdff46c658d5c140c3a8823961049c8de7b6f29a5943225198c0921090109398eee676844e5287fb088e786979be40f3e069a64f99f975b42dc5f90b867738c26e08006dd35a5efcc175c2e43232a3ef9b3ad0cb118c20e41364600d26c88b9fd3785036e0b1d496cb9011015a024888087ecdf120192cdcd52128951823bda1f7ea96fe6bba18347f6a2ec53b6647fd510d9ffa111126040a310c820fbdb2411348567b20028c7d782b2bd4289a81631370ec6bd8885c9f1498d1ecc1b5e5cf85144815b9072e14b16d09d712fda50c1c540930c9f0bd2d18c35313beec5ef4ecb86dc8bdd123fd94facc986202b7ef69363673b5298bab812673e992a891412f79b637a72fc6cc75d33ee15616222869423769423260624aa2184dbff65431f06e6b16cc8e62b7d3425528ad7681fb78f3fb94c2ff3cbdd7c8a4bf127cee3acf85bd762382bbed6f5f01b2d5ed362a2f09a0e93c56bfa8bb36ada8bb3aa784d73f1a258838314c3a5189761bcc6f4f1e517afb91f7ff29c3ebe048a35a88f3f7ddc35c56b643ea63ebe1c8a35323ebe0c8a35333efe277d34e953147f4637c486c88a9f8a2fd3f516671175385d0f4556ec5a8ab3e29bba9ee2acf8a5aeab382b3ed7f515ff0ed62f572a927b114cfee618950ed7c6145745e57eac9963222bbe9d63628e19dc99a111152436a0ad89f58d8a0d221b88384eacb9f488fbc1239c58f3f4c66957465819e1faf76feeb27805656b520c93c04c57b5ca3db7780342a55a41a90a324283e2064c88f112822d5510010210c42001842e437c49e28819f48483810a2a86a04188a1392acd913f9bcf6b1dd1041921c4e0a8aa7e2608323008725299608fe8a28702210a45d92106587811e28a992300b18322f430040852d0022e062ad80ef8db2e43a323a4c8598646476451691e5bca2863f4e812045a92a4947206910a7f5785a143d8337c9ca6551df67cc88517797eca06105cffdee9b62de892b0ddfa3560f86b58355e35c5abfeece16be21c4d47c3abc638db6b0f5f4d362deb1ad369f25acd5aed63af43356fe8d176aa4adde31747b9be5663c86754ab4701e6a80f13005fa8df823b60ae5f1a0377402295177707508b0140e8825c1fc26e46d54213d7561eeeaabd4a00fdf939f4275ec99cf92bb97d0ef7db4a3e075f8e734c2bf91cce7dd3c357939706bb5664d5377536f5b7fadd1157e216a8a83f74b11a4377cbb03322bdd31ec3ab20d9633f43f618869de35bd259c39ffc1b3a63a69c754922d69932ec9a49a403d00e8fed9a4aa403d24e969d272ffbb76b4c443b20ed70d991f2b2af6a57f3ec0065fb332d03a6d132f8d7af7087fc5aeb9b8ea8d0e894d27697a14f8f98a3605903c8f03b58f33fe3bfe441fb68eeee54cf5cb3e6ee32b9bbfb4bd261e4ef9497c87e4967e09463db9dbf556757458f51da1c713ca9683e991d1b0d15cd386584de320c9741be54a188a932028451060863945ef731aa50c4fc2fc618e34c01843046995467d22a65a553c21db336cc41730812b3a451f6c3ee49e5a4b7093a618e1cc4f309598a2811ed51ce496985453ac85e531b0673543b6924a40f5b06fb10e6982f3dc7873433fa9c9f69dd32ccc722d113a932b435443a07dc616b0da14b89b40c18cce125fb57eec754818898e36b1adca22ac6f7a21767384137aa3d518360c4511423ac28aa1fb928795168d053654c142b7ec8aa146d518682c0a81c6951847aec0882b2284a00518192b0285084a023b0519c00319500a344d2852246082022283e0842044750b47802e403258b1e2a3d50acf0814ad0b49fd20124d2b4b82730b25ced0b0f2ed5010f182e581260b6f48029b2110212be89975f441732b48c8c50c109cca645fe5234c8df4c1140c89326cff9f3439e3f5ff2d32844d90631c6bf5be4f8aa229a2022042f776411128eae0e7a4b49d1c116430c653722224a56b940026721ca163990b202c7a2411aad0613f7f6091e37189fbe72568d715e37ded2deee886b4311ebff41a4031019e25e6c98d449163e843ea49cee6a896589002855401c307f160150a4802c8965eb47d020cdf7af17dca0cb5fcafb448334df492857ae0613b70613b766c8c4c532acc1c4b53598b826ee29e763f32029b70bdc016f90ebfb4fad379ed0bc1b36aa5ccffe867b9f75369edcd975762120abd660e2cad460e2cea8c1c43da1061357cb7006ec6fb8c7cde05c11b7a5b8eb739e9e5cbf725fdf9fb8abf4f59da7cb8ebbb4d5fcd33779ad26fef92814ea7564fdd216d44b4897be7c9311b5bf7edfafdf49b1267eadef4240e91a872c1fb8c3f43b76c000430b2d609c420a1f2a6779fe97e5cf7d7ec46cc231b70fc4511fc8bdfaa8ce7d22ab7e2928d7dfbafad77f72fdcf725bfb11f3094a5cec391f91551f5371bf1e62ed70b5779fa8d5774e48f3b8571ffbc6f04a95e119f7eaab9ec3381a7ac7bdfaaba771af7e2de1cf72f8b31bfeae863f55e68361ac7fbf06249155ff947d915873fafacee5a0fe621d2cd8ebc847610ce7dc3f619df81717892cfbf56ffc9095ccac6466ac7329cdfd88d962b7f56fbcdb72ebdb5abf0530d76bfc8a8d9c84b81eb0f6686c3bac125239a78c31c6585dca6e6cad4a25a58430bb3e6859967387c54ef0e3d3e0f6cbf8b4d66ae5bf94766298ccb2a8699b675c3b845e4b5e196794f1ffb3ef3fcb4ada13dad70ebd87ccab200d719452cee979dac83499e24cbbebb256b7eebe23209105bdc7d75ff78dcf3dff7e3a5f03744e8a69a463430d808cbd256f689de9373f6a73f6f0fa59c62aad5362d99cda5c21db00c0ec708e5df7c11f31c35f05f10c71bf20432a72e715b8d81eb22b8dcdb5d8142d2d84508b0bc6282595534a08b718218c31c6f835e0a6c211b60610422db048859fdb1f1bc20dd6e0a87adf8d96ad58490c972293d49152c7c405915d141515cda028c6a2188b6691945372994151174545453328e284f01d13964548e85ad44a08514a4e43c9501d1cc6344f89eb9d9d7ed23dddf3b37156dae74a0705c9a4e61224476c9031a3a3a431620874399eb0f500926fe0566e880c3901aafac6b76c8888303fead237aef2c1ddb83bc3a9542d2b8fc16d0737ddb15a7a826b6aa2b5e56a55a8954b43e0734d191a4109c2055078786209da977bcad0488820685f5c9ba191103ca852ae96a191103890428823218a84000a632f512ba2705313db1f5200c48c218e543f280145c6077a4ef8018a10299f31337ee0e189cc932019b675041f94bd76bb4324a55a2dfb05caa965d3d26113658bb360a651b8380b6699eb830d82b84f76503e5cf1c444c4d0c987277c4a3bd707267ee0a004997ca0f2648302a6e4c310406849dc1844643f106d627ec08010d27a0002101688a3ac871a48a93e45986dc9a0c7de4b840f6581102d1b9b0f10d12582b0f70ad185f610e5a765632325888e3962ce19534a8ae7040271c8b78dd93a63c428a5ffd1f8f2e50bb49ed24a1b23ad31c6183b463b2d9c73ce15a4b8fd3cbdcd1a4d5ad1a88cb26a34c82cb14c398b56b111c620cbef39e77c207007f6934529a52ad036ee69b80ececfba15a0275bc8f05703b6b142960ea00fa05b6818f6daa02945cca41ac4f323871044ad8031440403088c20bf56b8903284232b4551c0e86245e827a98b16410063899a0cb100c38a1783c411e120bb05ea2027b2fbf021b44739a90eb8c3b940b805422d1066456eb5d28b2cefb20cc9f5a2b5f74ee8cd23d09b17898d1bf4a4c541fe50f1cae2558eb59f7536b2e47f164b7cadf10c0d2fdf91a63f3343e9fc1c3be331ccb733331ad6d6fa4724b0acdb3c069c4c9f7b72bfcdb2b2b7c26cd9b225ed8eb4727cac5ffd722e6eee5a05e95ee560186565af762bd8c8b6d65ab3ac7a0c14431f510e4183345f3ff62fd9f50bf5db6b7000b76ed106b59d5e436ae9389d4ea8d7704e9d357c81566e6c04f5a687dc0fd49b5e43c5c6a8d4cb4ee6b51beda7e7fddf6badf636eeb53e7067e0816f4a3f45c3b0875c8e0c5ee5c88fac89539fc23a2955644d14842a20292cdadb129058b3392b6eaf75b6bbfbf15375953cb59f280db59d50a7d30bd55ddacaa7bf9887bb4e7f429d7e8542694c489a737e0f41232b405328cf28f29c4579a2b4930dee9aaa233309cce974fa19643ec9ae7f3c86f93819f5d9f79f7e7e675bd73feecdc8f93875571fd51d69cd1f77ad82b4767672f632bb6a27671c0a7f4d32eabbfb5adc93fbf5cf76faf9da8995bd53b7c28d9f3a1b79eb5276e644a383f1f0012c2e7831e93c4005062ff9cef938c5a0e1cfa23aec1bbe28f7a35ea095336ca4fea9bfa27e43fa3ca3741a558031c66842c6be7bfb1b4761f802adfce1e40d1bd11ef5e164143ed2caf6d47d2d6bef55a9fef479f934df0484eb8ffa29f37de8a6875e7217f6cdf94061adfeed3e9bb73795ba4f95b7e7363c5f3bd2ca98fdd47bb126856d64c9e0cb5ae2b62ad6c497ef4d4a2e830570879225496af6ef0c1d7f3a8a800172c45df6e3db40c409a4ec39be72e03cdc2575bcdb4e89ec3c5f7674351fbef0e47e57466bef6555b91d45d0c4509ec9d0a889295933c1ef32703f9ba5c7c0115be218e7c3243537fe66f7dcced0a8892fb91fde15dccf1a35d1f9ae84402313e2254d78c90e8d9ad8e25646256849aa96a436f1244ae0422326a6dcc61205962bd93f2f89524a2e48bf941c0dfdd363b7ac75ee4773eed1bc85f661e9748aa7533cbde9b1d39b3ecbbe3386598b751f663130404d8231d976966cadb5dd25dbf692ad7d196be4cb47b7f6dbe9b1eea4c2432e890f1affa3f3cacb529e96fa1e776118fbecbd87e37cf84e7b0942d294c680ac07b286e1a0f15a76e365bc4c966553fb58f815344dab618090b28d5dc55d3a3a5b1eaafd82680452a6645b6d0a5d671d5d4f71cf6671af0a18482ec57fbc8a07f9152fd9be0b65b1ef43b10662f6c875af05e56216abdcb35feeb764e699b75f07cdbc5dbd7d1a2a7c36ff5fabfbcfc3ffbd8ae6bf3b24b2eca3f033dc0174fccc4f71d78c0b2d3ce492f8a0f13fb69faf09e0b5efeebb21e6d6f76f39feca55b224e6171e86d75ebe2000fcc120ff05254b6236b2fdfc2435b7f097036f2e6025416cc63e88cdf26547e3bb7bf72c06683c86695ce860c6817d707fe3bfc4b54eb2e5c9dc6b4fe3b3c734ec6f3cea531f00ac3be16f74aaf71df7a1a56e74331eeb240eb2ef685d67e771572a1580163ae7f11df72cf63caf45d14343eed97cc56671cf3e8438dcb3d0767dc53d0b71d8875c907bf6b14e3ef62cbcf7abff3ac86b165e76ab962c484faef0903b00868db0f036afc0c2416ca6c142e661cfc32ce02435d3f815fec64fd8afda8bbb3edfc1ba947b5850ac815e08d96fa12cf63b8a18dece6cbfb7d855b6d0880444d9facb7e4bccbeb52bbc7de7715767fbfec45d76876cbfb91966dedcb3bf02be21661a4362cd7516f636f06769f07701803f15f6187f8f7d873feff1d7c2be06ee92edd3e854e8ac7bd8cf749b7bd84bcec7ead4a5d0c11c43173b25d23e0af655dd0af52bec67bccd33fe84197815c466ee5742a411d4db1c1ff532f00af5728bbcfad13985bf19604e5233f637c48ce12f3eeaa5972cb9c8327885c2496ae65e76c9528b1c391f9874af84a17bf071dc355fa5ba1746fbac2a37fef733c4bf21660ecb94bbb0b7afda1e72da2ff11c64fb811320c8d8bb7342b61f386144c6320c3b0e6fc1031aa499f10fb353b79a39328fc23aa997f9d9e5c83f611d192f3f85a4dec1d19386afcc69184426cd7e48ac815f7c8ee0e02e996915a14ce996e98a529f4c1fcb66cbe6a70f99d239e774cda7df0388a6699fe12ceb9bd8dc8fd2cfc8d5105f06ce300cc364f02ae7f4a8476d1c0dda6718723f4aa94ec69f3a999fd1a59eb3363ac67d66448528dbc7b82f616f039f0c4b18c39ae37ec44da63b7daa936132994cda09cb07b5641cf7280d2fd23dd363bf3d8ebbba4fd2a03d864ddc8f12e42aae21be8c97d1a564b8b71d8d7b942b59eb91a3a1542a955ac3b2191d7c9d34f9a2ef99328ecb389389e3b8bf711af7b6bb44fe4d6d290ee2efc896b16fec4b9f659ebbd4256192b3cff2ed9274cebe39cef4d5d441d9a15e86e3380e857ad489e338bce21ee6205d26d3a15e76a72f52c46bfd70b3ff601722ee515ac30cf9278f81be44e1558e8ccc118ecc30769a1c0da60deb8a10e98630718d5460fe673871118fcee7de54eab8d738ae542a954a1808d7ddb847bf64fa2ceb4a2f653af83275df129fd1e5202387d4c7d7b8ce732f637fea749abc4e9f65f82bbdc984bf259eb12f7de50e90612b3dff423df796a3817bd4e9b3bf8959f619b672fa538785e3b0e71efd1b2f7543dc853d7d22eeca20577ac871251db27d5fe242dc664ab85f2bd3d7e12e687fa3dab66134eed12c3bf87a5a837c190fb99cd4c75f31e1ba98fcd75df5293744fe09af7264a05ec60cbcca49a5bec9abc38265d90fd192873c0d2f5eb63da7fdf6da10770d71d72a486b997eaa63825dc1050f5d5e5834ec6272f6a86e95831ae231d0477d9397c9877ef6a76e95a32333c45d329dce56050b1636783179a331b4bca0c7f0f9979c7d34325d972009431cf1f3ba89a80ebeec6bbda12cb6a70602c0914a755f9b72ce988424202123255e903fc8c5084a04adf20793a01833ca88002a479005bb5824c882190154c04016cc9f0a08c41104b2fcb5c8fe01eecd57e169ccd73480c45d5cd350fa7eba0407b8831352fabe617bed6fa20add4c6785e6571d740f854e8954752774df36a3fb545946a7a4a6ba259d65700ff7e6e34cd4295e7c136b68f0941d949f557dd6da4c916a380b4910ca34fa8624d6c878babdf630662fa36b185fa6fba1b6270a2d999ee409f5a697df48b4568c6489d65dbdf8477ffd1a1b09bd91455fe5c58791691232fd8e90beb5f766cd4dddfd5396244d6e51d2a639b25b6aa628d2cf6056bffe9c3750f79eee9ff00d9d51f8ebafd8c895528699b3fa29c6d3431df8bb53507868481b82435483b17d7c6ca0f1084d5e25e9dc7f83ccf5939872c55fc43dfa1c0df52fbea173e3259d2b9e807bd4e7a90dee51bac3fdbc53d751d07f95eac621f7e8b36c70efd79f2fbf46e2c9949e30ad987e876931ee925fc140775d2c1b4955e5fa944ce97f3ab01f7f92297d0d066db78b3ab45a21c783e77e12c90931f2d7434344994a16640a8d9c1043db6eb165a6f629fdd83526d2534687b4eb37851cc77d6322eed1b75b3d951e1a9d004cfe7a88529844e9d752327d8a24b2a8d528353d4d29696a37aaa98a609d84d48538a71103edfb0c33ec81c01d9eb53fa27d1058a37a085559785a644dd31e5a4d4cd6c4c8da8dc3ee4ed025c330191a0985c95a732d3f995b561d33b64f38edcb6c31d4ea296524d4257f3659fbc9c35d9af640b44d4b6933db75427b07e29ef6d0ddb601dc45b17c20eeb256d31ed268d848efd02aa8c45ed37c6bee00b4bf1e23db3132f69506fab633653a83294b9cc494fbfb89e81fe1bc2a77ffe813cb8738ba376be48698ad68807e0a5946865c4eefecc864c8e540a2172a47911d43c7d03358d033fd5dc447605176fc9d85f360815888c98655843d652bffc62376413ee51bf70821e72fbbe8317601022ab4392bfea41de3d3b88b00f323ac3523eac26e2da5ec606b5a51c16aafb47fe37425a446f66f6cc461fe1caf827cb6ffd9e2cfa6e5366fffb34f71abeb74f8db3762b10d6eddb8d1d261e32e8aa34dcbbd9863a6d6d2da7257679aca6ee15108608cdbb641f11ae33bf6a19d619c1525ec31714c4fd7afee6d46305f8f99ac7e9b1b8be12e982b52140262857b2040b8879048bb34352dd5a7cab06aa705c4c311d05191de891049a5ba77bedb80deb40f5979be3320f7bbc43410c79cef4031d2e8fd8e437ff0e5b50e4877e3c4fdecd66ab9c38d072dc397ec9d95c7d0595f367094a3d0845107c2d82ee83901195df1e16c0f279f287c8c96c8020805464b68e17c087ee7fea80a93446db55f6bad56aeab60e5ce19e28c0edc6ffa14f5c7eaeeee5ea103cd1fb8a33f6260c2cc2e5e0471c4b873f4c2bc82f542c742a13ba1809d9e382b4e1fbf02b2628f7b518e7197755177d5d9e32e2ef24886915fa4971cffcee0eec82eaf550d580e2bec79705f1ec51a2f82acf8d6caa31ca3b8227b8c562e37a304b7c88b44e0400ee4400ee44091d5e39e946c2726cd1b71d8714a29a510d2a949f1e9ee0ea7ff207d716f060fb7815a06a4f8c56ba214b82552e91aa741864a01b700831f459f0894fdc85df18abbe01781f214f7da7b9c75c583dc2743ffc910c877b4745296dc499b11341892b8f061f576f88682608be0c22c8cf093b1976f273d8a44d1bdfc45a28f18aeff2bb83ca8dcf692edd78e2ff36ca4c8f2ef8b046323493c5f6b307047e7249d9421c4f1b517d9bfbfd080bd440203774021d84bac93dd0d9d6d131acb0517d3200d0ab96fff4c865b9453fefcae9b7b040002b3c4aa9edbcfffaecbd9e5208b2574c9420962d89eec592c8127fbe7402fac8c436b8bebd342c01d504a4ff3e4ce6f64fac9f9324ffc31691e1c6bef55a9e2518e221b500519197e4b070e28030ecce1352dcc6ef83b6293210ce242a8f1f8c0fdba07764fb79af807969d81c0dc2477cf344013bce27c405f490c5b94f2c854c76c8f13ceef9e070f17be4b4fb6842e6969152443fc31c91d810bb340c24ede91611647d072a3ff5b792b888548f79c0794ebabfe6ed5ef65fa72d558889729f6b2ec96482532fb432b3df695edd063bcb2c737a21fb98c2df75bd5c7425ebed779acfe8d0b53a6e5989ec33a58b4d7e1de84350de3f4906c9f9df6b1567bf950c31f14a33d9452d3b477cda45deda4697fba54669c2538e8b84b7bf913f09afbf273bce654837af9a8cec29b5873f1859035a591ec29d59e7ead75dbb69f01e60dc70f49fefca97dec6fbc6b92b32e07a18c59fa3751526cc5f4d7a3941213615ea3180915e5ec3f834947287ca70fcbfd0d6b6c220bfbd6deb577ef6e758b7bfa5decf4d9d8c01d9eb127e22e98b1b7c15df1b11f00f658c4b088610f21866112cbba1bee6198863df7b0fecfc6266296c3d8853172337cb7f6dd5996411a6c06416b0f5ccfd0c804423410ba4378adad74ca083dec21570386e11cb0dfb0edbd6fc8d5e0afad8132ee6bc0b0ed31bc8a58c4197284fdf65f13190159fd22282ac10972ffd078e2fe4fac59c5fef69bc8eaff82a87f2a6e2a1dfffcf060bfd13441dbba9563ddcadd71cef626ac63faed7532cd94659dbfb4de4cee3960c9293df6397038fbecb72e8752f62b8973dc0a1c380982091f42e0e5b5cad91efb0daf245692035f483c25246561841d2821042a5ed863f0cb8bc33a364456ff865959c3a3177c61ffc1312f0d2aa05fd8ab5a155930671d7cf1d02f2c38d84324ebd7055aa7c5fd92183e6374d611f7bb2af8407878f817962873c45f13dcc3e14f77c37e221b433708a1ec502b753aed440ba03cf162d256600045d0cbf45ca7d3420f2f2633827899deca9ddcfceb836b8aff9e0fcdfe3c4ea882fdf62b9bb7b791a82747557c75f7d67518f7fcb1ae93bc22adfda60530976268d3473a79899b70cfbfee8d9b32dc3e3eeda3fdca7ee78fa9eb9ecdc411734ae21b3992ca115f2095bbb111fc580fa40cbd7c6cc45c21cdb5a239a7c322222f33c9c8043a9890be706254822f2a4c87d562363a2c80799efdaf2a15fc6e26c1f5fee23a1c7fde8d268246416332342a410f2bff53060271f4ecb93d3f7a8c31c618638c31c224249d43c8852d3647ee8adf5bdc253ffeca0497477b2af6636388b4b8d7337aaeefecb40c91488bd72c71567f7fdd81ae81a159fd598642a59002c62db4d082a16df2d7caadced15fefb40f34c18d9dd778ce6ad9d01cf980522d56fbb3e994bfc291a4b26b0d2d2831e39e50af79d997d0dc1fa57bb7fb54e242bc5a59711d7ff7c6d075c105175c70c105175c70c105177680e1445df0770e9732c7af128725eee938ab65480273f83f710c7c46622dfc205e85c23d21811880403a8aed1db2c32564d6e2ff167a6e8c3516b352eb3a638e12533b1ddb4208450c71b30c8d8880b052887a8876b8900116dda738caeef0ba47055164418b80298a301e3343a329ac5822295811a63062882b2343a32978b41e5c99a1d1143b0890628c4c86462c6041964510437a509112114abba3bbeb71971389114adaf285a8884b17243061c6788f509628babbbbdb470b8d488c50d2962f44455cba2081093366c85d1fed11ca12c50cc2c1832875c3de99f75a3a6c6eb048a465121111f970219fe6c675bb59a47eb8c3610371b9021494a587888ab674c9b09ff669293da57df2431c0dd4551aa8813a49cb110e0e8e9d3407e7286748ce5115347087dbf4b474f0f81bd5ae84941e664c6a1c9ef45e9c61a27b1fc36ed70348dff8945c23c16b99a60db9a9192bffef8d1eefe9b0b90162009c213745e3debdf7d27834ff566871e7f3a428149fc42a7127fec4a078250e6598189e94e34edc893c7127eec49db8138f2c1531c6687160641d41204c8916dce1407e6e7a18c0dd55feeeb522cb06e2c8c139c201c2a0f0b137445ad64282834d4f4b070f57fdbdb10698c00d909c21456c38820407bbf570cf6d0f203d886e7cb02737ee82f006ead868a04da9debbd1b29130051a8dbbdc2249699f23cd53c4061cfa49ffb8be0ec2c921028b6e580b9172ea86bd33efb574d8dc1499409122dbddac15da022471302dc3340dbbe961007777c7699e1cf7e051ce909c239c2584e00e1a70024d2eeefaa60fa669f38a960904873acf707dd225c308411c7002e58d06da94eabd1b2d1b2c5691ee4d202069254caaa23526678f7d5c69af69453a7064d30308ce10225a86794fd36a0cc3b0f8d0dd55f68d609ffd87fd96e154cb5bd84fc05d9147ce0e924d0f2038438814b1018b61628c313a67a3b5b2e9702fdbc7f72d4a1d31dae888b86575d0822cd97efd5a315b31ac3a123e60305b81b4bad65a57cd43892ce923864cc00624eeea19d0a80557b27fefb8ab27d674a5328c534a69e62d9aa76fa4befdaf06a91a942705e85005e2e88757dc83996a3f678b6a3cfc4bc3586a7d5b63adf8dd53c11db0d62139ee398e7b9ec53d98290be298c91d51d09286d84dd84563e4ce1536709891b8f127eef82b21463e23af9278c6563146bb8cf1657c8ae194585ed15c9a561a9a4a051df26136d1038d134c821ee20d90a229d9dddddd83706b86464543e40f12dd54372a92344e246664c515333234b2624ac632342a7292a12062915ae207405488c00318d0a03502275fc4a841115584c183e843f68759a693317ac42842d2109e24c105da261f30c2d110493c71842614e1070df80289158401c1159f2f70c08424ae502103268c008a11ed9202072de8c207598ca9220a0a7e2840051228b8628c183b2f8063f020063180220b9e1d2bb0caead67da60663c638e119421364a0c3eb536d2c38028c116334c18a15bcfce1154fb2d3ecb688eceeb6d229a3ca480aa1a61fe04288cc03114a0c51832e8e0045092329aee4cfd0688b30b600c2cce265ff6c89c82e032a54d005142f08038957932b4de820cc0e55bcc862042f7f95cd90f35627194521d4d8cae6e0dcf01a1e47ea11263b10429e0e1b286090fd6f80b86b4676a328bc64ff21b1c65ffe5662aa9b6a657f2cf7c9887cec3f29318823fbaa59f93008863df7dcd2ee2f4ba043b0e2043d2f880427c62892e145182e5ef199c8d0c2ca0e2f39f3c58b492a88978ec458fa23c6e207d060acd1915266b0a5e8a5c304ce000b1308bde4eb3061e10736f879c5d761a24aeac9e2259f8956448517af5883e8a5c344c6102f89b1f83391217a458ca51f3e136f1cad87fa4ea494928993262fa5940f696aadf5850f6281236badb5d65a6bad9562085fa8554a29a594924a29a59452d25a6badb5d6596badb5d62a6badb5d65a9f498db5d65a6bad9276d7651761acd188d0fa5d7e779d020a3e5cf8f761730a80afe9a73fe1a4f00ea9d769f24abd46b91f497a8c4c3f498f41b95f957eb57d7c137ef756252cc4cba52f61faf439bcda80b847538d934aa5b013993f6518a69d95fb3217f7907a274c9c34f9d3e97452cd4f412c4ea9542a954aa552a9542a8545e22db93f8555545cff88757a38e1e58449ea743a9d4ea7d349063be1c015521401050fbe4042e825f3a877c2c44906c0b8e04a184854418223bc4ecf24859ddc542a954aa552cf83cd2b859d9cdefd653a94ffa9eb2ef92d8106f10d31001a8c3552b6ffd0a4bbfaa5bb7c157110ffc68066c497c4ec7815a41ddf10b311f84dba5b3768703febd2bcc94e8edf648abc9aafe426c7863856130b218091f8107690c6d3c8fe340a204bd95fc92d65ff528dfc35c93ebb1e237bf731d121fb4e6ef8f1261a81ab88851020fb2b814419e2551002647f028c91bda575b2a23fa9bb326471b9fdf63f1bb31a35b2c5496a64fb5a7673924eca36492365db3274cbe03a681f4767694ad3060fd0619fc90ce3381aa6ed0a90212764fee7798602648b93142076e6519630e0f9069a6d8542b4e88c9427cd774c685e4ba2e9b0686fd25e6a1accfd7133bd508ecc0226cb2019341fc9fc25538c3c352459f6524af944ca1a658f7c9204d658697aad93575c0ac9229b4dd806d7b9b837df8f7e86c01db0a6214bfb8941314e3c1a1277997efe12994f827f3e127f0b937075c813ffd73de429a73665fec64d5f391f49847cd6381fd8772731fca3bf6edc7d9d3513a6714f7b2d6e5bfb164b10e2d0beb56e0c9225eecd67e1e0664ffff3a33886d330b4a7eddb61c7923d5df778f294f12183c74431dcf533fde8684e3f32e1d27750a94d12ab818452ce47f75a9b249605c9a1a8ba32c8069954585cecfb769bbe6dee9737ce838a69622f391fdde3b40cd9cf5700dc217ffe02a0abd2fc67b38c9b65a82a64f5cbf8da4588d5c8fd364ec8fd349de7cf98fcb4cf4c32d502c00600ba935967247b259db16f5bdb643299f00da67f23a64ffd8d678fe127dde3554e903d3d793e003a27726fbe8dceb5386bbe56ed140ce8e7274806b90b7f0dcdcf9744b2481eb92b8bd422b7cc28f2c46da70c823bae4821d3df20737f12536e3cf332dff886cea64f05cd9ff13aee92cd83240988bb4cf8cbdef4f37b689811e81ebec243871b6be2188ca5509e4191359f05c55dbd0a4fe3e3fbcbee71978fbb68fcfcfed1992d25979ee787e979e870b111d32fe92cf346e4949f5813c7641b6c99e72f19c3c3bd99e1ceb3b8379fa6f3a1c89aaafffca8793a8ea13f6b8691b837fbff53f8d523598283bbfee7eb409b51b8283c8428749dbc3291cc2415f7580605a2c0a8142d3765412b15a2111100000002e314002020100a0744229148341ee89132fb14000b839840725a9bcac32447511c841031c81040082186100344466a884800031aacf0d5d739f9fcbd14924a56f631046ea8a5e3fa9c22763b5c90a02edd4e786d3efdf95fc828c0af6a32ff02223cbe7ebd216eb8f23083ad3cb6bc4956b1326309f510fc9b5adc2e40e6426f7185fa9c1c7679add2fda25affb855f283e1ba02d0d4f95b44a06ce4188c6887b1c37956bdab508d5bcf75b7d152370839da51e3ac95db48566dbc3918d735032b92053175b47f52004659d8c23c2df1859f3dd15876c9a87a51789d866c0c60068a208aa4815054f182afd58b35de20399d5f9443b5a152b00f896f46e73833169b6e12c1ac48fc84b79a16a485c9d2a6155a1d120baf45ac96087f2b33735f178dc59ad86df98fb871c537a2ba6e36e33b6c7224a7b416a2a01b1125f00e8909a3849c9865c10c5941454f05c640a52680ad7f39af52191e940fb38b8d812bf0088579360082b07cdeec3377aac05808fc6e4c39f55d4bcf9343497ae636043d4490a4b32309098296ba4a748cb6d72e3c450976fda83e1cec50377735856ab64a0dc6a7be1ef3e5cc2062961ff6f0b06bbcfa32bc45c0281de0066b29ca1d7a17be7a178b1602a77db4a48bb75ad79aa495c8f0beab2d381d9293c889512da56f85d2c205731a0d68937f9384f2e0feceef5261d379096603375f9f4b515b84e0ed82e84bd9421c441bc5e1441cfc5c33141f958b714e1631b14199c6387bd96bd006800fcaf2f3e184f00e3dec38b9d9c2552f7f740b75093690bff5bdae5a2818789f5c80425c58b6e5034bf08306671df7c1f8c7581ee90696ffbb6655308f619a1e679299fc33afea08628d4e82052e256662829ed6febf2b6f6c52f4b8526d14d88cadcca8b62cb977ca75a478e0158f294769262995f6fee3179da1009b34eb51df62468efc9713da65bee239fd12cb105e1cfd4a7dbb8d98177dad4378be147b04f42c6f7434831e573c33afe331f7ff7a3056fc98467e184ebc254166925bf175c9a8b55b72b82e2d4ffd07695f9786cdb0b77de2a61c3434770b7ee1f1455688ce3c784b12355b04191d5de47fa13e0c00c2c4733040f1a6ee8b1e96bebba3bcc1bc48bbbf26cca946e9744e0575e0c50ffac3e049d4928cb7a37aa018b16e1d8f67698101694a85dd91b816466c658af64ff8dab9325fa12fb510c8c25655376eb968f3e9b31f961954e80b04121ea6873f914925bc1b33c243664e50184611f90e3dc217615f07dde084b1c04ed72ba845efeb631e3a34588e1eca217f18bcb2b43aa654ef6e8387debfed9006096e3d7976c3b1c00c419c2706c19c37e4958254c6686005e2dc9d0e1629839f8e863f876e09be2c38712740c32638b6a669e83d1f0457aa00d535bb754bee89dadd07a782c7199b528060bce485fb66fde52b4044913dc85473eda78814f7f19db6fe204043734d7969cec61220df17fba9c3994e968031443f478018c7a9f630e05a47f47ad10aec5ba278eb45c1df56c48461556fcadc9f0e80ae19d0b7cbfbfd286e6266af80a6718b68dfc83ad78c3254a74bc26cd7f8ec900e06514fd7a4c4b1d3ab17e3e356c8b44b3d8783ad27d4dbd518136794354faf79e006a66301e192d2a09279300ad3fcb540b15b345562a55bd1a0c5eb62f4864eca05799df1e5cbde52416c40a0156cff90be3ebaf4d3279e4a6f033aad4bd583316e27ab4686d336f2a58822e600d53817e61d2fb8aed9c11576f5b79c88bd71fddc688da7862cbf6886d6c7db8ffb2bb48c8da6e0d8005ba0430a6b21853a9b0bd9fd41137ae6bef2d93297b8a309e573c4733d2d467bd1a0cf04199fb855c60628a90c1e0e0aaa8cce001abeea86a2797055772bc8c597e6a9df4e125aad253e07ac8f814795de6772674ccf1b3d234ba9f1865fec358cc0a5b362d47652ee674ed5a01193425618d348479231a20b69a212f3731e7ada34595ad4e2f61414bf34d3ef23cc7d27f64f017d312c73532418ee7fdca39d6e3bec73337d127c760f141755e2a93d8b9d6393e9b066b53d8d77e6cb483a1cb72e7934dfc3144954140e9a31a1c8cadc40af4508dab3888da66ffbb6ab3781a9880618600bd0c727cf0c95ec5af34e8e2b6ba0a4878589738176cb33caefcc29a0a59e7354023584a594311b30e8d41f42f6a6a7c30d3c400f3226107fcdcb53603e97c538017518e54805fcedcf8fa051a189929c6631c79049f25824c7b0a2fa1ed6d61ccdf8b81539ce48f21e6138eabbb4172dabab51f6df013c5a168db0687f0fca33918463966871469727ae837286dcfcc87c04c67225d6959601b9e3bf01bcb53b71f0aa9f90fd24423ddd123c11c9be557ac4a90b7f1de2486441184b1de9857e04cc7db562f1cf53792402026c9b1986d15bda1a217b5a31bb92889517abd2c4fccd0fade9bf7b8dc0b813f94750fb25cf0c1d9a83067a68fdfc53cfd8f3d8bb8269647adfa822bc1ff1232e8c5efd4572f16b53466d7a0a09aed125f085b18ac29e463da9f3df77f807dd6c5fb207b6a549244576b2a2b209e6c717f6f4c345fac3c7fc82383ec826d73835275b403a21e28edc93ede92afe5e8f202b16539075c9c420806a3aeeac7250174cae26aa05cab640b24b6db10ef7287fed50d2478095929697eba1a9a116109d4bec9cd0c9d4c2db3c38f17c13033564d11e13111862eff1d035268d582de491f68625f8a17c760a3e96340d25442b3c182d7367f335c1c162ba67b550c2b45e7f3e14f2c8b5cbeb50a1d3f11923afe0c0fac7b596c39a52f64671eaa53ad17aa3addf58805f4a91c2227adc0b23375c117d7c340141a468ee049200326d2fadc8bf0cfc873b748a29d9d23763c5bab22aca3d53245f45fd350087d63cef5efe8512e7152ffc5c6771b7092e9a165b4ce47270e69ed4fa5426b6a2d16d4e029eb338667dbac52c344295f66f955c2051698234f3302e5d1545d515254b2b9529402876a6c09afd703089acb3d4bcb091d9ae3d8231826fb277b88d9a78b1667e3257e17691e0ca67e4b71a29c813f750479b083f7730eb54908b19b156e69c01444864105e186458363edf187a6caeea4d10c456fe3c6318869b47381681df190fba287aa0110c997a49908c71610a011120563e2e6ff8c508a8ea37d181adca052ba1a93f497453555cf4eec6563035963429d1658b99c888188ac9ec1e81381f2b085f52e419c0ea617003234e9f06abaaf1d39dbd57031f227692e093031ec5d1889a5f5b38c4f1f51ac1f366288b2e5cbcbf2ab42737909e4872838af1d490dbd5aacfdfae6ee012dceea0910cb2203a3d7fcb4f5f645e85c83627fba56c270a335b34974c08f660e55cdc4db07b57a23a2d63c17f79c0c262b79cc3a14d26411cb775a2d54187de7a048f94cfa30644f7ed829b29c66f8cc4201edc50d82445bb9a918a00b09f0b5168e500a9042e4d8ae62d1bd34cfb6e2c5a1910f615032406e5b8ad1747260dcca0ce436b185be4041e280d4f6d1200a4d06c526b54b3aadb42862850704b5a87552d913e9ad7a2fb209d02d52828f0eb0bc036f338c706a1caaec10fba37c6f9699fdcee47163331b39d586cfdcdbf2fca20ad74ec4d86b28df7e5d17cf9084f6abc15de52c831a10238250b74c0d4f583549af93d1df88f92265247c1e5c2a40cf5a4aa49b359af7fc453044a2afb2b8786d11bb937572c6fe46fc1afc4790f68595b76ae9eb1a71eecc60ec0a834114895191a9e8ef9cc24dc714210e2490a58c157872c8c308b6a5715c8ed8820bbff8d6350176dcb04d3f1247442a0fd41620487cbf2a9fb3033dc0e14a4215c6f4516ed118c4a65f1ab06ef9a7e6148031a41c5c26a490213bebbcd43555249d7b6d76e0f2798d67c3e455f801d961c196f0a958aaefcea6db962151cb2b96733d4265b67ed1e74385838ddb069da6813cef186ebb449fd04fd71851d114c19ba2253ad9a612003a65d772e9fbe9f23d176bfda3c370e21c77d9960b8c6e413451525b0ba9e4a2465023624ca95ca7165c1ac7a8ed0cb2b79da620ce60c91b961fbf1024d0eb2d6b3ffc96bc3d8edcaf1ae1a5a5954d07c5d02d5cff74673df6fc21c5e0ad2e52c66e3c8f7d81eeaab95ce560e40be196bedb3fca33761f8fcbc851b00bbb15c6db8febb128b5a42936198801aa8faa0e9e866754e5b88afe5ff443ab0be2bf376e405b112b3d941ab6d3e7603101623c04f9f5c6f9dccf1e399c3891008a0106f2a0ffc97ae71414a072aa96c26fda4043b86ba6b38798d5516114cfcd168fb414483795ba2896c823ef89fc78b429cdf79144af284939079352b2e2863f4d820af778358d710dc9ddca2c3520c546cf42aef31d4a0eb0818865860476fd1341b0ecbcf4235d256bfb2292b0617256eb5de696b54a18226b4996237c2d57a938374a209c9b191ccca099de7438052cebce821c131385460ba27421c6d9605bae20a45e00cba2776c674cf6b76b1cfafd7191cf9bb07bd7b29783cf38d72433810b47b27cda0bd54e3f11117bd8718a1e0f3ee0130818ee4d78fc4a0339f676ab8c3be0af5e83fbe8b28660ae5f9cee000db6e070d32b285bfcf7b402ebe5f38dd08147d2b3ebe837e26c1d3edbea5320a5e3a51d083d70ea314b50ad9a82041f35684053b8feba20cfa335bfcd98b3283be7adb64025da9cbc160cbef202778f3c3bb79212ecd847deaf8bcf1f8899236a082e3ddd7b82779de5a94da0df12dc0e7bc1f307bb281460af8b21bae04c8627893aefba522af6790b3feb50b4d4f155ff30bcbecbeb2fcff2abf698cb09b22f19d918b058dfdaab8a823cc7fe6936fe2d4a04c6039b99add6c4d94fd6a246af4df30038e1da00aa081783446bcfef69be72a2020f55ca6837b442ae75ebf3a59b1bbdae9711166dba81ef6e45b7b48a094e56bfcbad80e84a16397051f29f21ef1591253ffdb1eba1ad17557f414fc98357c256e6175d8ea568c8c763e9ea7700aec9e5a718ddff4228b8091b9ce6791d6a2af0c1fc18fd7f82b43062d9327073a9a81a0c5d27d7a76a206de6f7530cc6e6ecf022ba405b73f40a4e145003cd9e4ed32563b13790cd28d4381b4acc82b3e90b32d5d3ee05a6112b7ce6e5e4c727bb22aaba41b0c72e7616109c20f6d2dd04833faa83d14b5af71c7a7ff435ffa9f7ad8cf85297f193be949cf589e4f5dc0e6a6f36418854a9ea5fc0a41310ec36e5083e1de5e18ecb8b10aae6e3243b0a5441d1e5391645d217a4c15306e4ae5496ac090a9b30293a013041ea49f099c430fcee57812f80508c9bd47f6f46c01e5042f7b9c7b4c7d1639871340831b429be30cf7fce2614f7bbabb731925f81688ad1264971f79a316381e4688747252a75620eb68663a20386d8ab601388048c2825d9c1fabcbccea6307ce2b19b2dde034362f20f9f7bb8055cd62f4023836e9367d2572787b8fc44437b3e1986b48b06f4287324a7bf4313843abbb060d96c3b121ea0855b4572466876f64a5797b4d3f3cf31b42c2bc03b938b4ed35b59bd5a8a23b7612094e4f1d666441d8d11f46a9c8d303d92d551a04e79efbfee7a58baa53b00586b79375f7f09bbdd4c5bb14e899ace2e3d902b30cca123e1290624463595234cf3c362c5841ac741989361cb693a2b6c3d898934cd36706e9d3ccd9b3ce73404984b4a831bee0a5f095e4e56c0224b5d2c3c98b5a5acb12bb2cbacbc11c4a0241c7dddba53a09e732c1758ce2a35dea4134b1856696f3aaa52e7acbd7cb404a2382c31f82fb2d19b8e5945b781228902672aaa5129228cc6d3a445232860fd80ffcfdf895db1c0946ca8e89c2501016793735638790c13517ec9be4aad436447298056c4e1642cba91c1e2fdea13e87835527c1aeb614ede34aa4fb2e8c6ead4fef91f947d9e5393ab6c1eca8914c12308c486a8be203d088954872811373b9cd8472fbc2c3cd27e234746218db7983f91c1232a01182eca3bd9fbd0b10f0494fa677cc0a4c1948dc59683c6c87cbd5fa3b3249d444af3d7c0584896e90a848cad628e2ddf5361b133c7a3eba1e1eaa1104987fb2c84bef1201c9dbe349d9c583f217ceae136c62fe8da72bea1fd96e5281a4ef97fc58f8ef99258c2c0dd5421872aadf7da92e344e441b31b9dc38e626558540cf770377c0dae6b8d1ce47b0c93e2c66e2c42cabaff80c9fe19f123912edb17c6a5f865988689dff9d02b7611d2f53523a8eb9a64c5d38e5f3c2578951f1113b989de2853a945ed6cdb188fd9fa58104785d80ec81703bc491aeefd4da159ced79110ebeae729b93cb7f8b804c01fe58062fd9eda9918901aff4e2cd0707a810dfa07efc45475647be267e633cbb8642463e55689ce3c7c897da07dcc0f4a178afd129c4587d57a734551138d37f2d7a407591e1319961dc06c8ce9dd673a101df790600d254b23c4b81361ce00be42793cddbee0aa38185d63bee874ec16731294dac338e07969ff053a6260212ee3a565ba1727ba6f388323d0205dc297da33671a831728a1e1c419256952812b81345c603a5f179145749a86bdfd138aaa99132697f7bab48b4b19b1d003622712866f81cec4c71fd3772e6c0eab033b1e3af6b8c09812167a1e5f51044eefe6e27350c63408baf2a9c86aaa6a4d61f0128c6030140a63dc3e889de7e430319c352eb7452b3212d9239753bc9589211d461418b61881fd70283d9cf6ff62f327aa11488ddd20ce18210fdc7beee8e7e8ff65149f7a327a084dfccc49f682d57d2bebf3a0211ee402a1528300596c62d40b6552f430cce454c3d90b3c8608718ea7fd63200db7192797c3a5183ce4fb163b012e1950e89057457ae2aad81e786a6f32d1a6697bd6f4d9755b7997e3e45b86491e6fdce60a5ccb7d1e1cdc7d52b6b21f0421ce0021b7a545fcac833e4b217796a26370559e7f11bb70467b84c8b624da78db51e2565817d214f6e009d47f30c31e8bf72a623f2fe755935f9cba7620da556e96de9fbb9278906a8154a692af9534b3a499fc8a2a9429ab531acd546bca2294f0c47c3f1c4c61c9a500b130bc642874c429e9ab14f9bbc286034300407d00f0388315bb905a73988a2155d253382c7e779f0b540954a1f1626cf286e45dde60147de499d00a41042eb20f583e4cbbe1bb05c07eefc685575692e781ec204115a2da2268bc1bd7934280815090c0a6ef0db73a44bb238b8fe9b0b3260de0c1afbb671d8e7cacd6a8cdbc0940f5a30fc71c0a9cd837473e8e1c54ba040eb8eb863aee3d591101dda76987b6533e95e00e27655986e082b6773ae25c8581d60dd1e3042f6242adcc453433ef5d930600164764a41eec607706fff292f69a09270fbf57fd7c07a313e9619ecdc0bb21e95496cd275be2e8a84876575222db98e628a1ae0c5ec8056ab2a7c6476bfa6b1f99fb9486a8780efb262c1851f30eb23e8272ea47ca0d4681038cc651304582075a225b3913462a04632bc51f6b03a03f034d1a5413693f878344549006977d39d381f325b4e04afc3d8dab0e509aa75bfafb91d79b5d1481b222e285dd00fc0da438b987916c53ab693ef0796656a14c117d231f2b2ac32b3577906c7a2abca466dd08f2b195268f5b0a583c0378a99530573761f14de85b282748c8fb29283ab8bdf082ef83231f73978e3c5dff144726689f26ec171e0df3b28b84da7d5effb2f635839122a706e36ee1b30d8aba8e8212b1e9adb689d3f41ef1be947d78b0fbfce6fa3dde122e5a0a5eef00ea1dc591848f0655623d67cd5e5e99aaa03253fcf4f3c74de621cc2f849f7fe5da5d7aeb5dcc1fcc931bec0ff9006e91ff8eaa134905e2504f66ee9f97ce84b9c6b1376e29ffedf626949c9bf79401a38fb68abb370fb23958a313813131101745ca4bdf344ab7df7a638bc41423d5d7b86dcd8ea71b7b1ba1a9834ff91f2648c8e94f73edb866edd46bc4c7ab80c7ad95a1fc222f8ed6a199412a5959fe6de7968d2221e2b2a496f2ed01169153467f48edfca0a04fde9c12e792cdda0a8538e8178168f505bbd4c6c7fd93651901a6facf610ea956dcb6e373d099c9064cc89027f62a40e15e150c013357d224f240fc717f19491b641f56a1cc34d77d4343692b50d6c609aac031be8d9e6395b9b9f0457f7ad2d61d61c0b896f03f667dbd2f4b98c59baf03272171a8f4346e1bed193873b2a1d5a5272680cfa66857f2a934a607a2ee4c3a0dc790a4b5d7e73b01430d62bc75a6fc0e1c0c1ff09efa48d3529b4b089072875e88ee1d8cdeea96d7ad99a4d0d4b36e74be7fae3ee493b3a1097400b36679332ca1bd8742c3700acc945cb0962de4fae334feff7858702e1bbac7ba608396dc946ee61333a8deba1693cdd4a270201e0036c2d45da7269ea4216847bebedd9f60fdb2592160e6ec5f24961783596e0beb22f7010df381e34b03d5dfc39900d23073a2b509a7f99e492bf2c783e52204c9ca557fb8cc0e98f4a9c385f294629654e4569ece12189a678083a5dd3d24b3c5c3bdeff2ffa55ced1ed25cd954dae11bc37a01fa251641031c9b5b1bb21241979aef87bbfac239003f390d9997c14d8ba470911a1d99f06cfb60bdb632c729f3d79a839c84113017d197ad69154ac2184c2f93612c698f3564e031db719a6e5fea7c4ab4d238dab7235515a550a3b048dc3f996bce6e78f59277ded9a6ef0af715db0d5bccccbf67250b1975b4ab861a2d3bbe85aff87d135bc46f6d03ae7e4b313bd7ccfcabfb8ad1ceaa1a1c1be062e16c5b9293e2322b812cfc8e261f2eab294015254b384be12ef05e6545902cd7243e8a20b6325afe2cf1d578e71d62a9c691d3f62f655c5ad09dacd944c135f3cd27eccacaa9247db3d78835d0635b495a1e8359bfc5427e73222ea465c6d223f88bdedfcf2ebe2637af9898e9a1056251c65110e41a8ab8a6a818b4bffcd34abc655352e9331aa60e243b04ea0255a19db044d0e3ec74871bfbfef6afe40748428d7f94898b788f6ec82d9743820f86d38a38d084ef201e46c74e849811c876de555613075a51185914c4337b02ce81deabf44093a3790af912807057d377d6500f437868aa55d9130da23b8bab0e8624bc800b8eefae41d8f32821e07d0930367a3a70d218dcce55210282da01029e3ec15bf24c67cc6dc938de9fea6a21fd1c8cf47b916453b8bfaf0784f0348c4f2643fd6fdd586a4329354949e4516369d0c035629f302d987fc0365edb40bd64ac41d5d438a09ecf01f7497924edd12671e4c5b7077e40b97f89ab2275136645cab0a03b53e94328dca30d09e9a6753cf3d6bc39a3419894144a095a75620f8b53f8c80206ea6f09a999240c17dcb401e3fefbb76c73b6050810c87aa94d0f90fe5797e6dee5ba0904d659021532ddf5a3eb54f612e329c8becc62086bc8b350e87b075dc0a12bffbd1116cb30dc2f780d13a5f3a3a41444f9c46654e2790ae9126453858b602984a007e7efb30ab10c714a80a31c29b309cb018211294b69e0c7a16ead8c1b4e56612693a6cd7b6be98ae41dd409eb45e813f5f3d1440aaf1d80635ade2b0fa5026fda5f43947cd707526cf9f9efb08049936a610085d4e39b210b80e65cadd83a4a4cd6f0496fda19fc28dacb6abe3f3262204b338d32e27a51c42d9836e126c8c03e747881db53d351941cffc4df27e9c39cb67f56ebe91884100114b64e171ec2425de48cb30233e74d7564384921a7d64018ef7336810d44bedd7e159b49f4e568bd5f70d66fe4a3a951fa471a30f2cd3aecf715e6d228c729f2c3a0335bd3d68770a52b5ae954723e64e9deaadfdb0ed527afaba516894967a21e7c0f23ec095a215e4704057d383e897aac69210f8b586bb8a9431efc7773eeb8091431b12270629b5779722334d75f95a22fbe9f958b1c1b530aaaf491604d89ca00bebc722bd1733edf3712f51ba01b97197a2b48e8c62d3b6895852ed5e6edd1d0ed9d9a88995446cc06f0de9de03bcd5df3406a45807e2065605c3cf22a14b1457845ccb055de269a61852c07bc8bb267846a78cde558b5191992e0f0f6b9a471a26ff6e73108a174d3c3c5e020f001e888109b8cc2db315f4462ce388c85efb8cfbc6954c2c0b8f8f48628d77a2284615c263db3cb3bb656fe01171406c8246012193ca8b32082a6aaef0aec87adaa1d2f43bdf3ea43a4c24d5258fea705224702567756ddfe2b9afdd4f13362a3741fbe0a83d0a86586eea76fe9cdefbba0373c3e360affc2be04b72c004aab2ef075ad0875d2b5283292c5495880c1bb13b13611a66ad2b02deef77d5c8007620069249902b2814edab4c130a53105b295bf79b6e23a41c5ac617e90456dc69190ac113c67962e89b518a97c5b05bcc5eefc6130cfe65af303aaf30f74c74b327a270418cc699b3a3bcba68fa9fb7604f6f80e1c435dfa1527df580359039422d2bd8c3e613d7042cd4478601159977e8d0e943d563129a9fb5c86614d2a72cbe8c72aa3bba5ea5798ebb8dddc2e368b5a062e810dd571529f6fd6331aaecf6f23b8fb6e03e4b8d0ee99383e41a249801530d4c28efc5a35ed8e7d0292d467441706cef7c7a365554d5180e44016245155ef7226891c67375a32fc97c3d27df14d7e38e8aaaac7aacdcefdf434c9e01987b99f352dd8d44749b01ec4777f9e1f7f2d0b6326dbd73493a0e81941401ab423b71affee63d933f8d9e55295dfd526eb463821586ce5a0f55a47951644812fb5c7f25c201030d546aa4501b2ef632aca3902dc782d1838d6e19f1229c47dbedfdba512e34233d8642da4b3892e193d6056e03c8e19c395dd37a6c1d924d7bc69f088de661e1aeec25ea705344b89d5afc7c3f8650d8c2d28db43ced6c92fc83c07128096cf986a5768687ada8c76ead378564bd5387ed3dc2ccbb0f9d15373600968e892b903320da3a2a2b5139ed35a788edccdce0e6f33e2e77de6c921f4645a29cf60566050d6a775bfdf4f5e47dc785f1cac5e72158fe1c0c499d6a1a97834ddddb7fdb822487fd2dab252dd138d8b0994812d9886727de3d4b32c197304e597b7a243decc5ac759b9752afb42c9d7efeec5c25eff2a034636be4aafae896babb4fead4da5226ca40a29ae0d84beafcfbb336108a82f95b9aca1ff1efba00a17c52b361f09922ecc388c222aedcd41d03584845f7a014b394068646ed0690f17dce2cb4afed57527b6ba066a99760dc8ddad8da73c3e422be3560f297dc58c01bd217ae101795a53bbee5f29cb532bf1670eb87f02bf2aff8422c6869ebc6d845167814018c704387340cc97803570d03b5c7558b4c300c4163995bded53ce33afca44dcae6acd4dff1900e7d03cfb65a7c5b39cdd086adf3a8ec996ac376b399f78fc6c435b1754ffac16d4bc15da3ad57a4c12cf95eca996bfc6ce045a9083edf7b86cdbe040380e2b2ea548fa1703babf3ffa55ecdb3e778b55fbd70b763fe6c6ea310e9b4e806d43874efc20c11da9bd1fa01fdccf5980d051321f2ce1ee3b0df5f2a2ec27cc70958cd1625b70b694ac4f1d5a37b67053e0f30974eee66174028ebf43cb1045bbedb486f64d264dd1ebfc950c31e47c55ddfb3d44c87f34c2b669a99643f169fbf65ca079d55c8faae32e37e75dcc8e1b0c517a9765a45574f5acb854bb0e15cd845f606d580a275a7d5966410590100abe4bf974b1348c47a8a1b90842a2728a546da8e23d4637f75d08fa76c83fa59b6820baaf8bb00eb5d7451160fcee6b76721cd9f518c0f6370c7125358a9d8490fb453733ec26225dc5f0a389314150a5f536ae150557cd1a072a2857c80d9d79629a66d70f8c858a24924da042bfba3f6af8835eecb9f0d36f6e84e1a6d02cb0b34a4d8c4da29a0f2ba00e07d589f82f505e1506252b968dc279edcadc51387c93082674dffc2824d6402cfec21579c0d2e83adb1cdf61ef5b1c133772fdc9735b2c2b6609d4ec1037b393c073b26b741cde7c7f622c33ffe43c99d2df69c397f39592b7edefdb762c916778276a5aca98a9fdbcae94d9c11ef9c8b7ae0dc6644e589104328e2c4c96b65f85a6d5ce758dc0c7097a35ac842e81ab9be07f1eabe1f81348165ce861a92dbaf9fae248c8a62212f7f57e3a5bf54e3e7c1cc3c99317092b7e0f190710dcd2c580686f250ad61659eda436ad016254bca2ec15910691777470acb49ef75e46e0255a171af45b97c16b3aea6c8149dcdd04c31ed46118da98e6a38798f72393420231e084e4c924e25494fbacf9d1a327d526d53775334f9e5c17a035e84f3e38cd48557e700808018cdffea5ed4b515335813ccc73937da6daf888d1cb3b73c7d137bebae064e3cbe4d0c7fbf7d4e7f933bbe2e99c64b39f3bf3cfbae4ef24c76fed2bb02e60c270102a883e20ceaa54496b0941452aa283457e96b5458f117966759a206cba605bd90648f3e2420d86ec29a7201e081c0146931c03c310a3b8b84d6321b0b839d958f6c1c8051db7aa144133040a7c9782b146bfedd0bd01f31376f4c1a0474016f2151925cf38a89ad2478c6200ea9526e9f4ed26d1b5a7bf2a242a08b240ce91b8197cda2ee3c0a0ae23a2176ff0800877ff70384c5cf8822f8959736d40f6ce323ae3419b911aae896c15b143437eb2cdb4c1c1d4148c836634e7012b986675b080dd80618cdf61cf88eaa353854ba36d0379f906dcd298aac9f8f9ce1587c624499d72b5e7fcb996624e4dc548869d055a4f56546042cec04299e8d4298d909d7df438e3235d16ccb6150a3771cdce36b03ceadff59d8f80c708426102d0ca23cae81090429605513c446d4e623b522d8afe788f613cfab61c21743b9d02560697803ea1437a585ef22c580f047ce195d1fd543765bae341b72468b0a6adcd95e0e048a553bbd6c6b7380a5f6cf0297ca6bef1c0f12bd5413c5327aef9481e530feedcc1f946517a315ed17456fa2dd39a6b37a0053c88b621865afb41d47452d8fa0425c6092c08a83ace0a48d4947f628b961b8740fd9ebd31799ca40d2396ca0f86ba8e1a06190fb9e8469aa8f2e2b69282299c6480ba124df9d8652af17625ac1dfb7e544e79fdefc57eb0edb7d84a5d73ce0d0053c19e79c6adbe900d744d118a2c11f7bd419fb828b16810a8f6d8e1dca65a155c2b434f5e939cb7587503b562b8404eb695fe2a1e233770677933a400c6ef6586c60997e545b058d6028a06a3e17eba648b10a40ff55809ef3f938819156bf7d4bcfec0b27f45c437fa23b563b5b9a2cc011c653abe2017414deaeed6ac8ed03488e25543a93a7c7e7ffbd75169f7e73929ce58e8bc648c7792375144290478e9b0d0965bb8df146fcd20d807ab06a9239c992a4d5600af24490b1617a01b8b030008f460189013d35070d952e20efeaae58de3a6f4c7f9e45042714457400e600454f76b4b9106cdbbd2c1aeeb73e758e8faccf2f99383eef006341672e8d2ee46a93183f6c15ce494821b127bc89063292b0dc32b3c78f3379067cfd2a4dfd64e728f96b244c1576f4168aecdc7201c1cdfc2c363d7961df64bc9a1279b2de49dbc3602730e3e5ab3c9c171af92bbdd44af50cb817376ac7a8a8ab659b589ff4be915e35dc24193fcb7691a1adcd4f4225273819364590f660ab5f954bd80eaa1d9f61c007fe0d111078b00c47c68b41b0b50a5fabce08a6bb7e57d626fdd0c278220ca0810134ceb03ad0181eb6290e0732e396e0f1d67446cff7d49fef72c43b057fc3137cc8986b05f9ac1f3bb961a97504791be5f27eb6438a0a5474e5da4ef960b42606cf56d21f8fd5eded28c8411b5ecbbd4c3800132d86fa5add89267f851baff65dc29424e687ae736c9856b7f8b78d4770bc8816e042d33385ea2d0d516b75d1d93f4224f43233204995088f436c5b1a371a5b4f09a42111c850df2c2131f504537af3212e81beb49ef438a5fd77096d1c21250b64380bbe5e26234d609fe7041f160aafebc8ecd38d2996fa2730a07bf1139c70d39f1505dd0a9174274c425a62f654e9b82ad202050a91f49aeaab65ab9538c37409c7d3e8a0daf44a65be862d58e3d59bc1ac380a10db12a7a82ff469544457c92ab69007329b06f196ca68021f721617cde69e47c48c0a55d851a5d02e9bad0c10c47a3ef6fdf34ea3eb32a686138294597656f5ab029e4c3a04c9ce0fdbaa7bd97b265346cc6bb7b1bcf5b96e4f50eba191be03120770e04a7ca00c647ae64941791185f34cae3e502dada49fb52b9e68f08dec4166375c0c0abe3c5096590e38ebd1910c003e7201311303478521e5e37d473266e7514b1ccaa068719edfcc75d32c22f8256b8fa68c54c705c2a93909eead5e999dbc116613149ee5f9a00833448a3e1371a44fe853f85c559c6f58579c1f14d35e1d249bbd64bc6075bef6445b242b955be94e476d26ce77eeefca37a243a3f9ef0808e009da7c46f57303eea010ca4700e3c89c63d57ae8647e0103a8d9298a2b07710c53736b07454ffec31be17fe99926693f30712541f7a4233032cd2799befa2ab15d15f3af0d17a13e0150f3222fe4b55d7e507a3a28fe7519d8fe6e39bc9dc3c70e6d13100a431767982918e0bff0021de3a58b33056c1bedb280b093169ffaa030cee03fe152fef2ddfec857e297195de120afc73a8f3d13f04749655e4c2ed9df1b57c2e32c1cc553ae40629f1bb3688d4de1b01005632b24cb1335d8453d25110aa7d2c87559db5d6aa2db8256fc1928c0f7dff7670e685101999481a53215a443381741556c70e99b78be9e704c85d0df119c9dd413b88537bf06dff8ea7abbbee5d6a9d5fa77a336ec00023dbd2721b3e46702c8c0d69a6b77904d3e25bc1de923ce62b1320f3a0d470d8db97e2054fdf675d8e4e6e2be2a22cd494f6f3672ab0e31e302909fdae2a6246894585abb25d12e61949007e6c0a4ebb62d86c1c2cd7411f8f42aa90269c0ec60de50c6bfcb623863950418651126908ead51393df4593720507e2c044b7ddfab982a8f94cefa985cbbfb6d1020d35fa831bfc1d02ab942b37e7695969a28bc5e8ad2caae4b3f3d190e28330dc26fca6836dd25b227094c7be77964976c9a727ff595c73f9aa6eaa4942f904d4ad2bf27204bd384d22bd1b2d943e57996f3ebd242e968429f6f82d0d8dbfc92c0a9b1b7c077a0ec16c5892979f8319262658a2f2d41ac2f953bc64cf1a33142da1b8a3967a0ada0eeb5d3054e4267c6f6ad6a445304b6711d38516122bb36763db3d066b8d5ea68b46fb0800d9293b2ca51b58e9682cb656a8f951e3858d046967d68f40f5620d6e893104761431dbaf71ab09176657386144634d8fe1704074397b1e330cec3c66bb88f9acf61420834e78380366fe185d470eebae6a17b5c70c81b6a9800a7637f2b3180bab1382bcc8347ffd998272910af2b2c0da1de320914e49c93885f3cbdf5d5ad125983541225498812b1829ac7362325ac35e3f5a9b78f9c1b8e5f1f615cde33a61433eaeb65d09b49a86f1d8c78ceb7477a60a4eb0e76517020f938806990576440af4a574fbfad52dd826034c7a1d28b800ca9552fdb39185299df1e5bca918a82b55d094581ae6c605b6c3d244c385e89eaa6d7ff87d62ca3b8e36de8b10727fd231a678f8caa682ed383a04c6aac7f957022110b4459366580f206e420e93373e8605b6a04e37d790743e7243325985979b43a9c95e032935038615325b434736bfba38601872f7ee91466403788354b9c1001df73dbc8911fa5f49c6d4dd428e962b0204fa0c4f8a0799a72a61f64fdbdfd2099e4a99b648cc8bf9e9901ba216f1639e79d79abf5123a31aaf3e7384199a037eb3485312d3e6f9e00e151c6cbef41088dea41124c389f26ae1a646a2aa5a58fdd91343205b3994d0e4c354485a4ab1698a15778fe7decb78e53c5734123579fa4840f800894182572d2a7baa28677a65cda6ae8bfe0fb28e32e95edf536161d1a2296b49445b908d0511a1328a1b100a7d945e829a5a9d7b6322ebb4dc07bb4605c4e3ca84901dd4803ba5881e9dedb018ba2504123f41d54bb77264baea473f6470ad1096561eef4e02723fbc321ed43e5f46f3c68cbf90b90ef4c7e28314411e53bda5fc2485da9ca4be4bb094815f7081f091572edb35ad32a935ab23630d643afd3bf671d942eb1cc280307bd9025a069879d657ac161a409d243b6d3c2de5c1bbad6b72561d8f0d9ad0400c2027106a429828738abf543d39028e9fb16aede11f3048811e79d08b71f295f1a2c16b6e4306326fcdfb09f0837cba0083be8b4c93198178eff15fda11bf59a7b2939de1ae3942d20488f156fbbf193d85c7e9e0158d410e4baf6f11d340c5d77f4b27412aef2274a3de9ff08934c5c732ab2b7031592b2e667d19c5a812e54e250a4f7327d7098f697985cd05ebafccec3f995f26fa385c7136a86b22194169543bc3be1fe1394a8f897c95daf8a434032841d4c86277cc5e919b88bfb00c9756dcd1a0424185cc3dad6098ab84b1d05d435a441fe70d41782a56f2bd5b67682838843e1c11ac0cc397cc8baefeeeab692d9cfa2329a132ad730c107f8336e4991400069f793662e65de67cdacad5445f299faf6d11bc903276dfb982ad3167a3405eb92b622b6ee0d2f93d6a8af57487cc0a114401a4346828adaa1d8531a462a9e84a143a3d871768f60905b0d8d08877e3082f63a595b1bda5e11daacaffb7f4498eb07b00cf5b6c64a17e8da6d4d70745bdd81d9e16016afd33f9a7d78e1b6a60cca6d9dcbde9881795b3797e426013e872bf5f0d2db6587879102884a76f2e76d66e1b5ddd426a3b47607a84aa2a51568bab4fb141c02525ee5e1dcdc0c423fbb1743c80a1bb9eb873c1c6c8103666f394bd88962e487bfcd4f45afd09d5f90fe6f60071bbd02e16d85456ee0489d9565b84138add7aa51a4437ede145aeb8f26792126b036630a1687cde9b492ac5800dc0b4c60cbe79467a7d6d67e63741a630bd274df396e915356e32943e38dc9e7d4a1d3ea540a4db76e04d263798c42afa070dc437cc4649321e894d2fde5dae700b1d0b2ac0135cd314826892f4ac68842e048e7cf2a2d79069587c17671b416d953ec0773724b0ec11a8ce7b42c7e420df9a000f85407133aab987cba19a98dd92ed393112267801c8d58501ae9d5f0d81b4a0a5d5427081dead6f5b00b77b17ddb8dd623d7407b88d30b875283c298b03e9ef610ce309cac70204b8346fba11cf84b67941d03889a3ce0c9b3d49521a71a036389ceac62521cb0d607ebd4d4681523eb12988d960cfe95f469c4eb89ebe704164f7a7c8f250749c1a02e95e2a37220988b4df8028d2d24f08a827a7254b5845afb955bce17f8030cd17ba503cb4d5b52918fad9594f85f3608e0c65974e715702f5eae4ca0cfbf8efe58a31c9eebcf3361a12fbea7d0aabc307c93899a7b24a8ff4f3e58956e7b170396405938b7311b478b74edf74bd2ea21cc6a9b3dc0a750afdb73e14cab110920a0f69e2959f8ce7373c27edb63379703f06c5bf1449363f58b1e2b32726606c249d3bf19e60d669e738e0c448049777ef836d3c557d6dd0e51f3fe6fdbe3e8a60206c0e5d1b5bbe33a057745421600036bf11f9d5f6f53f63f1f01eb20d792eba3944123b3f8bf5e60339c8ea39211b35932629072d98e4a12942af1d3465d281db3a05eb1d26732b88b1bb0dca412c4cbe87d53efb724081791f2e9fa155c875446c57cec345aaf021b3a017afe3761434a5116a272e388edfc7f35cfc8282d4d1e93588207ab1510507f869474908fe9bae67b105ddb1acd3168672ac71ccfa9a5b4856294e0fce56781760c60134f0f9cc7788da28957106af42fa0c387e95983896814590036c0948b7fd5c69a6ed21456a348dfa97b4e96526dea1722861377789ec25cfb1c68e693c7a24bd8ddcac9e19d13a07dedbe77f54bb0ad7cb2a70646c1afbe1690810b6c147c50e6ede54a3f8625dafbdd5483827a2183bf2a655d9e76fec0dae49bfc8406dce6117804f8985dcfa7bc518e127c28d4b1636593f71969ab8dc7909d7ae9d152ff7b7077039730353728e6bd617a7f0ff2caf53821a144a83b13b89ff418bf0967609c144af819c30244079a7255543e7c8ace0dd06435c93e6ab55810dd63e0e57fe36bd79a7aba9d829001dba926b17379090dcaa80fc9162ccc188ff50d101cdeb6e6e5e639f0dfd6e33c45a22b4a50e7275988a553d730bf4c652c7dd4ab0ecf80072505c3dbd231471d5d6debe8d798ff6d3418b61810249d07f91c54e7131ea2d23a2f1499852c1ee02e9f49c67cb61edadf5bc8d8da036403401a9b6bdeb6e0733702bd000e7192662e59161c4bca2938d67f5d772b2d95e0c3114987f22773fa606c092c11a9c958158b2f43fff52f531f0f0fcdf25539eee75d1d78b56af6b4bc2f471c2379eb3f83daa4c88544f8cc5ce82e057c8e75f6d1615c39844901aa6112a8e2e1d67d24128a17feedd69a48080245e2d19a02b0a597828ac5e0041d108e450bd38efd781a6f46521a3ce51ef2668ab6403ff65e0f81db220ae282799105750930468d57daa10240f397a474480e89742ff9b8754a460e344d513a071942468aa951520db133fdc95f5cdcfe5fa7435689a864770c9428076a57b7aec5b01963ef583559503dc677c4195575ff6d6f0f5e9511698a9aba00d49c08069762859d5602611ff82dd2b8f8ff92373809ffc36049d7776e439ac1d5e03b3c8e424aa3012a257da6c4c8831f2038280e58431cc386e77c4a06f108b9915a7a0948f51a1c1fa7bb90b7b6de945241282abba364bd27a8717101b9724e81d4c3d4eeac948542b1099a624ca15569e4e304d959ccb4a310741539fd4dce51bfa6ddb9fc497446132d4f60c4d9bc1c92c65818180818d74e2959ae5c823e6c225e63f9fb428eadd71f83543981cf85e9bd599676409ac370b7396ac3c1ce6601ede26bca5b5104a431600c8816cde9fbf43e2266458ebe9d1b9d061fb6d0a06209acea77beaa533e7c4f5b07455b5f6775f5ec9476ed292e576dd84651a9aea61bdf6dff3a66bd68b674746fc20417a789c7b50e4d3da1f53c5e4eeb31e3b09e02882ece16947a2fc661b39affefdc3592528179093e10a9eedc5a086e42e4afa883736edd0326dae183d5658afa216d7fa06e9a88535edda315df216aade1ba283f891c1177c1a8231f25ae0fff6cfb58d3e126379f8cfd06305894698bf53a6e44fe7732fe9199ba8d5525a6d327ad8db33fa2e8e476b7ed7d1068436c443b0ce4e7033de9c6781f14a6c594ec6b85699978cbbaa991a3a9e8515b41d7abf1d3f5d462147aec9a3c7df8d0301c6cdfba2161a24d2516c74a1fc465f383f01c4868c626a11c15c81727fe86dc23535a0e661d05f4c9dc52c6b2387a0b82790b84a56932902fef4a03b61867456bf8a138265808df82660a387e2ed34f5e161e1ec8b408ff150737e400f94e6fdde14e53871b79c91491f71877710ca804047ef6243a47504cd02607c8aa944f43710a318a5f80b5c7a33ff62ecae4fd7562aaa38cf7df872857df97ec79f5c392a61097d5da097d677f5628662b9800769dccc5d0caa2511995317b5c95b41574e86b97af9c0bfe6710152b135281729bd5225d90e65d512dc111163e7d24401f8b3f64fc66236fe7f45efcfc55ed2eeb49d1e1c801f08901f378bd69f8be916fab7a00504261aeac3ce61a247ae868bec530dda8bb0ea3b8f0a8f44ed0bd8b81b1053a2faa3bd4796cd605ad06c3e594346a78a1a6f79683a5b26413c2bc7472e9769644959adf0736affa08c4bdd9c7fa3fa72dc64eff888e6fd905c458f325e3261d3422032caa06ef85e450fe355dc4c027897ea4ce9714aa2e40b40f6244f992db51545921e757d3268ac770c395eab29141d3c6a58730e8bd428e4a11a22eb45f5284d7c49700a8092a0b6ca6fd765c8eb60394ed0f444cef0c2d031179376b998bdfe39c5224f59ba4fc7de184f953e5440fe57f70cb5fb5446a4bc83465eceb1e2bea923ebb08e9becdd08c369872f7b490dfcba695581811f91fd4b5d50683df5e4efbc2a7b6a329b7485c12496f154fe962ff541f153ffb369ea4c2f9a819b3a9612009e500d57c366d01ef21b00cf1256416bf0aa64df84a8f8d30bcd6cf3de7ec06c40df56a93e9bcb0a9a0d746735f8ab3d07dddcdb36b8d492fc3da0a6160b9ef4bfb8cd046b8c5cc2983347d861ccca2ed6c7b44cd3bc1c15ada01b773d4c06c86cd770dfbbcfa7d884e87aa8b3a00b04ce2c91a4e61a41d2055bfd16345ba3d56b457eafa1955d76c6e0624bbaa2dc4cb88db06d6162e24aaafaba73045ab3e93f529e6610ee4163a2ccc72b105fab6addcfe687c6a501a92cbb00bfb239dcf0cba81ca82b66419ce0816bc92e5085b6c373d8455a888c9066e9cb5e84295df3bd9540c583da1dc138aeef6003b22ca32fea90308dd6fe762ab2c4d3ccef85884d699cb24943fa1f04490478e95de1236cc9b024ce1785a4590932b57bc40d04df12c0369326e29b41053b4b30dd4b7716e5b7c91178752451e224d6c856ed206a345f29ec439be3541e0c773babdbb85bebe3683cf923ea474914c0c22b949001146a09c2bf77f0d8fcef1c8a1c74bd5d589b6689e8aaabe505ef3dd1e7c233fa3146fb5d8b5e4fc5ec37eaf44ee9893207dd389054535529e15459921a0894e40358800089c5600643022c28eb971778c30eb032feaab460a3f2cf19ad2f04b2754fcf12d1af70d04244efb0d50aa1fac02f85ea5c51c2d553dd4f12d254addcf9745bd26230bf0fc4924ce91bd8b482b54f1885b5adf0594e7eae90bfd75b18f6bb0b107122950169c8bc716f5abdd4f3b3ec1a95ea0feea82bd2415d7f30bfc7ebf70c542bfe83250086133d754264cae015d641968e27fd40a83921bbf107181c106892962296bf84c6d53b7bf3d2dbc0ab928185e054971f1eb25b0d68de02c0da5e928e00d43390ad11cf775a20ca66202631962f650f7c5d402fff520be952d3630cd16305095e007571057cdf93754e91abdd15f2cb8d73648d214b23a4ce57adbcf8b7590678daf8a9a98ddba91e8e4664f7a4f27beb3e43cd3584c522756100a94c5596903f287991b8d41c012638e216229f9f9e9188a0846b0563f5c34a0c47a2d86ee7e9afff33af7da4879abe57ba2c5a8a046883d58b1c06c004b6860ae1a40c302f740d11e9fac72d59fc5f665cb335826eb93b3f259d99c9afb406afd7fe37b377a8ca4a3a72404465d28b066624cabec6b4f472f98e43d96682704bb96a9307401ff1b41dfbb393c0a05a9956a40d7ce12758a2024b99a474ea603daf3f5cb52d3bd9c328d126735f696c0e76b5a7bf060918e53f247ebb456bb8b1909a0cf6a2841f457114abdbfc2e22bbacc7cd9b025e6765519f59efca39af7e80f539274c8c0d598a8f24fd7a4addd3861cfbc568cbfa0de87fba0e95be132e79f446a1c385a76248cf66c816b37588bf188fa6a43e92ee6a222b8af297da087342e1fa717c2b62162df5621779ae4fcd076da873d90ddf42c6101c695c788cfc579672e537d06058457aa63a1aa19521bb81024fe6861394e665b2e7a33ec0b6f384ce0b9bcdd794edbd7b05284f2324c7a2123df064dd0a84b5c59c22446329f66878d7e165168ab73ec271c651e9b43292e8858874eb2b5765c02a9b34408897827aa1912a695b6bc7fe635b95dbd4d73c87a6f4a90eab915101cabf5bd1e56c8639a346030703316f5db63f39a8a64b9fc490620f7000073469f68f7b5121e1a65af4f612cdca6b22c1febfd4a1a39ac4560b1181799bb2e22b2e57731bdbb4f6def27e7a6110c673150e8f906e724d8ee4aef2e7618350ef308ae5812e388e4ac852d33144bef5072b1aad47f5435ddcd132fd0da41a3c758a16dbb7c2df5a4155d0a191596d98144d36e7a0a537e07f16b92ee6b28d81b6ab3f3867a448f03edd91bd0918147605c190b8f2c99fb97687d6b550e086aeb17a040ab9f36055557bf32b59f7f6a70454fdf437ad49d164c7aa4cfe6aa89049f4110619458b390fad1cf9a9dfb0e2636e8ed61d25188379986183894544cc9595b4a761fca035b95853da9f1930b3da110d75bec2b616729200502cb56e3be43f3feffdb0c86c5c7e3f011efb009779a932fa6dde2c2fbe453441aa2c43edb050a147b8594494620c4ff0097350863b096a3b6a647b646bd0253e8f36fd13d1adf7f1476857e90812268c518c4c0b4660b592bd158971edcd5a9ca8273ccc68791b2c5e50059a1c359376e0d98e69be74d9cd0c0f93a27392ce7dff2b78d2222c846c4340e1edf389b5872794963c07bc06425b907617d177621ae185ca551474a92754dc3129ab8b5f7c24782b6477cbbc2be80655ed0a1a175c2a9e93dfa732f5a4b5662a508ec6db6edc1214b15c0208bfda5519d40e44672b5109b2711884a1f7a1ef8fe05aa7df62baedb92973a601b610ef5c3cb842d1b67d7f0ae347f109250f350698e31295a4f35eaed8a5378d5b8e86f7c7332c43a255daabf05eac0dea6afa76257061744e6c07f7701c90e48c0dfe40585d475a3ff074fc4781c90aada519757a13e16b2d22a94e1b8f92cf30c98b1578ee82e1ff299f4a8bb11543a985044ad95510bceada89198663c5edb18e2ea0af58638fbbb71900325d70dccb084fc37325b6a3234a57c401feb107d55cc3a0b4c97f1c2fab8e277a34d07599d733038403fdd1cb05bacbf7ebbd8a88bb1c8d7b895d242354af496e3b81b3e0cf1a1d65b9a5bc5a4e78b77601f19d7cab8bd5af065c582c247d77376bc6f0ec791e6eff165fce852413c5d649e4c002ff5479a2271eb09dfbf8f731485af3961ebf6c68e52d61f9ac2d8669e8fc4c4ef025427f8d4ba920c1cdae0da4b73ac03c0db74a383bc20466793278184f08f81551800dd256c9e473742495fa4e08a2d23599b60f6892378d4fda902c07d5e386faf2335742218acf8b52f527924710b0837586f3a09cbd8b8f6c1e27bcc87e6a9f2bce042f2c84ba1ac2dcb2191b6985f0217e446d38abb91117cdb6a75db583ab74ed3df8b676fd9f5a909301ae981c815d0573b6f45282713d5fa2a49e4570ba6ab1d603af091f1ab79d852e94478b708f4847193250e0b3b3c9be3a97bc8f542ad69d5202fe74adb7d08f1227875e5415826df331d3b8cb53d8128935cbdf08708b553f98e9c9c42aba2415408882a2ed0664695fbf6380d87ad60bb39a5528b9ad535fddfc62f5cd6bd9928101e5a547c6f7def1417b81cfa81f266db7f23760e7d72beae533b01ce04f330e59985c5c0d154aee9d6aa4fb7f7d42c72aa9bb5a2bc86af9731cc640d40d75414fbe8cf62b11d2c67581eaef72a96ddf3b4a1108d626265bcf9ade1201c21d6c08e6b0ca846c72a3939db751aef892d5cf856b02286ec36a6f07fb523d3cf719f685c996ce3fd6dfa375b0fb8275a9b3830d7f344b724ea3838dbffa325b8f3550e0e573466fdbd59bdb943bafe92bb49f532132d142a10fe087f400d049cd6f1ec68612a9bc6a92741e52a376265cd62c1f5d8959337cc2805e49fbc2e6e42156533291632a8cee44a64db9d103277c6b0a5ab2c8993e326d694793e52eedb9f27905db60de128a4182cb48305eaab2c35506963cbc95cb990915235c78b02b322411b00bb8b70d7cf666b374c4578bccf2b5ab7f7f713a4da8d7298c18862b0ee9cda16705d11febeb972a87b526c5d5c43c37e8d09855c76760b0d3db2ecf9593bae8d22a90d04c11e872fe31173f536481cebe34395d8b898ae2de9ab094b713a04ee2d5292d475c67b4374c9a669fa524feb8a7c60b1329c846fa993668cb2684b4a80fc1094cba507b157c7dd7b024d62fe77a1b83c14ac1d67a7d4b794e352f271b5b05c6a223830211d7b6f86acaa8967d69d515f0791050c0d77c6df1d414fea1a2e35b8c1ac39967151cf426bf367592d39d5c2590c0bc68c8b15839471ebd0658c187c671a4c97d8953c42d738d5477df101a1eba6cffac020b05fe807fa1ab7976c778739176a5585978dd6704432cce835fcba104e3f25558e62d03562c20951742df3977c129bce8e47d3f76ec5f3b3f0d6337d978b9d1ad7cd10dd1de92f6335585f2455000c2a19653154e7303f874712109b35b581c5f9314643e1bbb22058faf42924852cb0490dc08cf95f0e8e5114b45032e1c3d0c63959b4203e8a4044009021a725b034a7168c43addeea5159b34397e68d7970187c63b630ea6c5a1ea374ea8332874308f6f771eb998cf1063d32b16c9092af6732f277d1bef3f66a7a77ec1fab91fca506dff29d222868ae361f43e3a60a9169cf82235034e61261bc961959592f5c426e7ca4401e8532a4e6b89afb47451b5a20e34cedaaa4808b526c10bcaf5a229a55b0817fa790af3215a7023609e105649f71d94785880fae7c76042a42ac99a5ce3db8a433a1de5f702bd5367118b436ea048d4e9ce0338d07d0bd039c8044b69a4c7d2a74ed4f0144484f4065f47c50b284ec2cdd41072962b0013d7b5435a31b87a4d2acb150a3e613a3aa147dbfebe279066d74f639af7a21a48c509cf6a638d138d5dc86b081744b02b085cc70687cb05a20f6c09e6bb2bc023243d77002c2c38a445dbb9f30d262edb70544bcbfbc8a8abd6860806d5f4f527d74661e0c13a26ad0ad959d4947b4ccd17d6b19e0654f087e0aa4b51912cea40481439d7e638fd29a671505341770741c575e5ea495a8d73f9a31566c8c17952c1ae789830a602a0a0a72e6343f5fea6778379ef5c97a3fbb7819078228f3e5f8931851cff050a899ba3d0a86a25d3b875f23d5f023bdc5668b5be3bdd416e022244af975154dcd957e1acb119a24698905e23c426b936239b63c6e8896b82dc247b3ceeaf2a423a9eb043178b923a0dbe033e2d5a823047f1fbd01e2fbad3aa9e76f84ab6ce0156336f179412b8153acc0e0450099c3ade4801bc9030bd972c68c3e94d0332a4281336a292425b9213b0581db7a8a3863214bbc0408e62c1ca997509888a425c5a249ed06b8f86de6ea1ed1e7149b4000533ca8f3622fabb6560ae85a44ce33bb74cadcfed829bca604f97e3c9f152bf09f1607ee00e3cc8f08ba5f2be10dd362be2495cf9c57ad4ad0cbb04bd29b33d48b89498cafc345050ebbbdf0d363f97f2dd6b51e9f1b0fa550d7bfb65bed016f07e69652c95ab47811165d7d8ec6fe713d2b9fc87f02bc8c8c00b89065031fc7ffc0c724041fd1d03e0970813e8d5ca695dba0be80bd742cbdd017c6e023a1885b0a644acd8473be917744078c84211d01f266192c7a6472baefc13f7282669f59ffcf13b94cbc0b2e9b239ee237e1cc82456daa16722de219f12d874767ffe4bb2cd790bf6234526a0fc183544a979ff8178a1f57ffed956d875aa1614179756817285be69274ded3db2135b0004f04141e4a3180ae2001e1af88d36c3a818298ddc6003e17b537365cf238504829465cd4fef6d64cb6f7256fe1c109a725debe5091a63b917b1db2c0e202a42a020bcb01d2d534088faf9d538e84b8ee264b7642785befa736d7783a8d82658f09bfc4eccdb4c70cefcf15e9be2f65a2795f2d1ac8406e44cd846c9885a4d294386255debd915fa18fc81c043997333b196fec91640826af2bd1f9f200df4bbb4d65765464885b4c0b81c51ba45d51c073ef65b062dbc05240ee9d22d574543a7fd37ab349bce9b15ee63aa0f37b31c00d3e6db0b04ad8701bd4a4bd790871319367a08d98541fc4ef8b38a803ae8c7d048ee061cad9c7a513e55eea5547d57268c3edcb912b637904f1a87df6de1df5beff5e64d7807b1d15b804a0136545b092f0d5b3adf53ae6f5bed9890e21a5d384d34f6c3016f4cd9e389a31b03a65bbc1e0a4b60ee4c9a0a8f29baeca79df10017b65cb9483153ebbaafe8c38ec540f54cefa4111482ab0d6e6185e647ec0e2678ed243c1ad927fdc172cafe41b087a8f6b10074e2149cf6ee8f3fca4f9932d8a6f0309e58b83d9537501fc76e43333143ab290ec6466bfe674d627e5495851d7a5969cd0c06c230afd160d691e929f14c76ccb3109b68ddc212d8e6f91919be4c56994133c49bf1dab66be816a5e0e1be92b23b31d2b6d85199e95292af83b2e46987e27d12503e9aab9d0dd0f2508733bbdecb76c1a582a3fcadaefcc7e9da6a07c0c8fb090473af0f564fe8384360d0f399d7cca2044db2614b0cb67e736dd7b20a18ba8bdaef1bfb1a49887cc82e0bb3049fef78a4c0ed31b78a23b286024636ef17c0f9bc45b1d74009980b01db65cb1f32ef0d0005a32470cb8047c9c5d31f0ea41c92b795202877594e0447ea0abbfe7f56579cbdb0b54fdf313f4bc3fd294b512d4fccff33968216d47e6ef3f1fb004bdcf109dc455eb6c5869cb8713f70082db7ea1df5e282e91a8643f0d12eabe63e02d31afa911f77af62e19e9d4846aa67b029a4fbb5b7e234ec6d0093f27a0eced70d88b6033b243a4e082843dd471a640e091064a459ec83f26ceb2453c83fb80791ef715560f0e6042fdb7dadd9a4c9eb75653fdda951240c412938ed14314a749fb377fce22ad9ddb7152d5d970b76109e7e98de96190d019ea9bdc89c1865cf4e600d37049b5a14e23fd2c15ce4f105ba86c21c2e14a0633475081266e1c78149c3968c4a1aec7b91913b42fdb7f246198b76c1ad964d7646f9cfc6027f0904895329f8865219960ae8868054bb2773e5eaba205b813afc152f2f689ddf3385f7180821c6b41431c248f0b86b53934cb6d4d2a8742ff909561e20270146c75749cdee5797da0ae59d069a388cc6dd4eaa5523914066134d01fc2196bf1440ff0340f7a38427070b9dc326efd5ad9a00e60ce93651275f5a45c7af3fbbe45bbf65320a02b9a449160451d618d8acc4e21513db31494c771f6d1931fdb58cc53a618a14e181531fabdd4fc3880d29875b240135e4034f67610223c1e611ab5f30bb30beace537e79fadc5b82ad569807283cbf3a8cca8fd6488c9ed52ac162d36757908364a9df7b6228dbd948c5e3599410aed018119282c61af6626ad2558f2322f006943fe923817add1b42dc62be8f3fe9593c889d54952392968be441b2b088c352c3db624ee308de425a12fa85ca6e8df6fb763063c588487a056c41d9ea86a756dd8c05e0c7ecff202c3ba04751a126cbaa1d2bfddba369f47c1373566823696b85bf4aae56559a4d2c30bc01301a027791144fd7940f9a193690a2ede09be857b21ea51ccf81ef593d0fe8d6221396fc052b6372ce73d6d9093ee9440c39e1907aae8e9878763b19cf6e6b007493a27e0ef976c7b4e34da5ac7b6630189b5663f2c38922bd171af152782c5eda6fb52a946bfdc92d0334de7eb2cad55de0269c2ee1720e3c88086944923a984d5583f432b36ead5b30aae581e0fef623f6069e5ff5aab906a26c32079c2d1fd7e1b840dc7479864c6a8a74496ee531f9987a08966c598fef37615ea4871f38ddc1f70312573180d01204ea54c40d00641e7065bdcc1eff97289d1ab609fcad7df273109073a2b5b596832c51a897e5fcae95661668af77243beaebb53c7fb5a2f902d5fb4ed9c6ac46f3b223dd83ea747c062becaf6ae04fb0eb3cc05c71281e017fc82cc90372cd46e8892a4d33194ef39265fa343a141b1373f17f24fbc4411a64007d1cf4e8c39bdc5b7f97e8634e275f1f7c8a57c0f5beb2eae2f3f34fd6fbb16d59947f5c21b85646f7682eb8eeaaf4f0eed2fdac8bec4a852487fb72a9ec0f49e5851ab771faa445f798830e073f75dfc6a6397667b86489e5cc3cf523cfea07894f84271458b86a9a5f8f7a8c2b5ed58f05824e1012d2909fb7dd98904414e1ec86144886415fd19bd77c2c443d1704730ea4eeca354028ef207f4420e30bdc50e79cf743dda8e0ad13a71ab4635075d319d53ddc04f7041d878aa71d60498c38d1c9194a9e63b88280346e525ca9e2e32d7c3bb93d67eb3b86f98ef210308e73f3ce6fc1cce5db96184cc4353a4cb69f51455e889744d50370ef238fa918043ee369fac8a5ff72491804f702179d25b4d11b63a5a40a63e685adb823f7a6ea827664c828c18393fb8c8efaf7ef4b6343450b1c52d1527caea6b7687019ba9f004f7e59380dc2ee94f228ff3298650b4661cfc8568b6dbfc4beb498d3ee56937f659fe04d1f8feadeda501b39c8b6ddf2516882db2379a1767d4d54ac4e6c5ca20643f13ea58227ede28fa8753452fea33dad5f482649b7637278030983fae6428e463753222796fefa88f00508430d00e788ee010e2d4a8d54c5412ffe08164eb1e3047c3d7ef0422f88ce878265c8afb812506c25c2eabfe09b9bffba90a073b1b52f877a22107896f316608034de08ab0f3157a601bbe0621dbbe8dccb139887af73f25fc8df0aaa1d3a20efb04f165cb02dcea9175c435c80ebc8e01699be33893a9b9a3212337b8293410f5e4c2baa7c573c6c9d1bb2d6c45e3278071a510c86a9d07cca10ff971e20ed73ce710ee3e8165ef5030d444efe0484c533273d5d10d7a0b143c516b28b165d3bf174e28e202d13d876edba8c09e2d7fcc0ad406919b9e60c22ba2c3dddc5374dffd1368323d50491457a114e87d449ec6b171ca9f875da8993ed8cb93c881983f20465cf141ac23471d04828d3d24c38e27a0746b3e55911f557c4a87dc8f86c5eaab57d5140ec9b7f3d6c438658b9f275e217a79f0e9cba9633dc4bbd7116eef70fca65f7a8a65632ba560e86a2570f85858d3a5e12ac698670a8a056757a20b25120d86225216928b1548148c587970a0688a1cc0e2cd565744b82d72e150aff899e0b30a1c412c69704018da03821d3e9f3a4c82e34cdf55ad87b4d225b8807e950c1f0235df33daec773ae4566cf1a0ba0d53993c8b5f21d4511f5409b50d80931e589e474dd8801e3336cde54c1d7787223b9a7fd0dade6d90fb8adc659bb2cf241c1296168712ab2ba13201fb6422f0b28a43dd40be377b5114dfcf78d68289316eeef425edb2c5428e795664431958c74da7715038650c64efb5021f629c8e3091e6c6ece832b26aea96b04d8e78b9ed3e88d6fc1d27fbf12789985af71ee202434d57405acbab65d6b6e9aeb9d6af69784662888908263ea1fbf1400aeaeaa3d2cb5eecdaec275fd18441711fee04d29abbe68fd0e9e7b12711521c620fb8f242e2da633235f8538cacb16abbab5a9bd521128025e0ae995404bfda91e6fa58d15beabf09471abb64c92357ac6744e9ccbb79b95db5a5b5444892dec56ade38f32de626ccdd840206410beeb8d24d95d63bdbc1ffadfda1d32df2cd84d94e76b45f4e1a86c4b133c4f7fdfa8a97c192c616feb0fcc32d1b0ca8f8213f0a96e60e0293361045b4ef84b9a251497cf7c7a0337d07916b417218509a97bf8acbb865f24a5731606df32f4c349ece4a7981efc45f2d1d0ddb3536a8450678e31af0dbb03a4b656714f21554668fcef1a6e4ba086da2b9b0a9b7ff59d31872807ba86f538582458cd2b249760814c3b7ecafe9fc53f321faf1b78884f0845486fa09d3d33e9cf9e7dd1010256be375bc542479a360322e44a6270f893445271eeba74e382591479a1f51e17e4656cf45fb6af29c5dadc20c95baea75ff9e36e5c832ad3b6724fb5ed250181b8379365706f2a162fb10fc081e7483ffa372d2d6a790db074cfa156899491a8a2a7cd97b01f8029b1676e8841837b249bbd416cee26956f01bed5210d2057dd9ec955e82500f16298523ea5f6efbb8ee678882b661e6f7c21a307b9520b54a7fc960062c93e5724d4481330e32bc5587f048d0a62b7392fc975dc3923af8741ff3a1a1ded2b97c05fef7a98151ff0c2a4cb8a36fb971a398ce04a5d06645e2a4a640283b3459f526f49fe862899c4686e151d6d85e0c24d4c4fd868cf7683033dd01140d858089b4f843317c276fff5b7a720eeae37443a60e994de1cd27a01df90a159408df370b1dff74a5225d7c2c9426d7c37162021b95ea2a3a02e5de239f60dee077746d1bda200a4f9a1f91bfae52768060b666851080eba66ef1700949aebf1a0f5f9588f289f914b4a5a98c8f19e2aa867d52183f04a09d62e55211f4e503395f656797b643b8accee5b5af33aa10e747f3fa7ac50700ced32b9c8e21d89a8fbb0dfe8cf841d9e93d91fba7be2dce745b59fc8dcd96af8c139c75a03e5f76b5dc07980a7a4237512dfb7def9387a6d26a9b81ccbcb30adc4ee91bf88ad95f9594c3135a66340ffa72addc4d8e6553f507bc0e0526df36eedaf952940d7ca86f4246dd10cbefc97d9226a2746a068ebf41ed7dcfe7f189c58ccbdc5d4ecf57e84733c772609a049127653a05b0e5e98e573153267a572b518ab8590eefb448934c0cc0a016e014013bb9a42130c3e0cb93c62c1483740489361db1bcaff7fc342521b28eeac229a0bba86197f6a901093572e2cb7443fe12d693483215cef596ed530f17a7802caa62c8ed6c88f571889c9d30a2ef19c40b0bf5985b1a8b67cc2ec997467d99aad17a396f7caa5a5800d73e1cbefcf3ad44d1cec2a1b19a5bec21df4507541fd5b9ed86e328a67a560db1b43a9ef78e3b269b4c6e23f1ade4777d0e45e0191121e8811a35afa3becabc27358dedd197c82a4d0077986d18b1d5c1d96e8314a88471c3211a26902ee86e52cd7f7ab92cc448c3d303061b6d6bac45892e459aebe2e7635c8494eaa66f355610ac82f428a5108158fcd741643126696e7acbcb537cd9de236d603276a2c1622119cf89c2af9c5a16f04711f68ca57ac07246ae80dac85a2ba50cdc5fe0ab25d90c4a0cff7234edc5a9f030d2cca9ec263cf04658b6cba79755a645b9705af0c3ad3aeda675ea5a6b81d2db36730f826f07912959f784acbae186e011c21279b4d97d8a726f1026a7fb62a72035f0c8e7f705533e679cb516abb31f4ea5b3a59835188f565c2dba4ccaccfa4ea117c3501d7e5d1430c3454bb9648ed3a2f0f66e369c1fa3e82208d7c56c34946b6503b6b471bd3e3706a30c6d0f097788beb5ec769b179fc9ff799bf06b2b350cad5806152f874a5a9804dd124fe0c5d586c53ce58b41f8b73e07dffbd7ab7b1a6b4e1fab5179c8bd13e5fcb7b066050d0f2fd25e35bfc6a73dad66f393aee650d242d084a284b7715c83c7b527ea137b7a2a23f538816dd473008c6747a148b713c3e6d26de3c1297331f3dce3744b7c66426169383125f22a4dd27e625862fa297f40299082a3862d0c1a4f9670921d0676dc2512c14a6f14568aed5c586fc28f6365fe5dabaacc4bc70a935568c6e094e9d2e277735f9c5b801f4a4250c172bb16b5a624df8c826ff0e559bac3962d1384d825109d25d5dc2deea44381a402e713479fef46c7446365ede51e6dc3a960c5028386b0b72141595f56aae1f5ad65c41ed61e7a244a55e9af5bb96dc2c35d04d07505f9e0f2af69a75b8dbcb9aefa5820ad4b513e4009578786b1a567e82229c114fe8e7a35164561c621315dec9aaa4fa052169aea2f6b8737b54548bdc7e1342132bbab7bb6a1bf56a25dfef109a5a742f77d1328ab596ed57a134b522f6b873ed5153ad72fb27046af605bad002ac977b30e6f629087f83f2357478707f73e81f41901f485e85feb7ee374df61904f206cd57689e08c10a06fe9e12cd613ca2b41fc8d56cda4b27b294e81de1012420024a7a394b5f509419271d93e39343ee3952de4e72c088d36b79272185b51e23c43502cce1281a21e1b04710ac8d00e914c75c8120d23539c01608031182014ce8ef037d1df16c7ea66f033a845ffc3fcbd9b8977eeabef5c04317adad6d239becbda5dc017c0475048c043e3d5d0ff1c0e3a2caa7a0dd9bc538f66a6ce06b7a3c13f16a4ef589baed76e9b3f2d0ae4a85b98c3ae32ef5e5b602e02b75b2946a116b537d97ebb8546a1116be42b577398ebd8bead602c9e1ae232c3f525a41c657b88ecba6faf63aae9dbb58b0a9b6a919b8dbe370b72ee7de168785db5bebc39e0515155b8485cb90711d970ae6aae3c05cf5be6ac665581f323ee33e5487b98ecb06e03a2e9797dbe3bcdcbe6f01f000581ff600b80f97bf5cc7c5718fe1be73170b67a9ec84152a0fc197f81900a830db4b5da9f372958578f600f3344f8c67a9efaed4c93f2b6cb7ee405b5866611946df3a208dea321ebf03d2d8cb50b9dccaa8a9f9c397781d97aaa6a68c78957d50f5e7b22a8b0347c17564d25a9ab7f5adb84b7d715beb9fc9749a77fec5505b5f5f8c1715860a35baa81c832ff19ba8f29221ae829dd0b0e6e2536aff5053357e548bbc784bddb9cbc55115480e77b9b00feedcf5c2b201dac88d8f403c2e3ece7baaaf54b7fb5ca8cd356dfbbc98f81da86659168608ed46e9b468161668d103142b4e4067dddd1d219b84501ab72d52160df43093457022082736d0323637ddbfa9397d9ce660c7186bcecd93c59c9e8934d248238d393738b2bec981dd7d8393d3af95e4d11c8cb1edb1a3b9a80395ddf4cc1b6ac0d08d8c5219956930b879f5d62d1289b61999349184219af0811336e8ee6e668e0c993972e4d867abd9ea318182c38db0490b24303ed0deb66d8b318a628ca2d16824aaef36ec36db9a968342fb514411450d2a5310c1e20828989939c618f9bd5a36319bd60b76cbe6c5e25e362b1b2b284fc82868fce6ee777777373f61e309a8c5be9a1319b8d112a104d8ed5ecc888cb3588e9ed15143acbbb9359b4528535471bb179b2be019345ecc7f71bde1db680eb2ede263bd69aedf373dd3afb7be79d908e2d70dbf7a8667b815ed8a0b7adde0f09999b907eeded880b063f15fef059e0914236e0c1a42d4a00c4d4bb65ef3f134c4dbbccedc7ff50c6f87902143b8d557cb6625448c1b5f22d8b279c5695997b196103a215c08ff007ee1710b7d781b10b636e8841322a89069424c9a50c244892976104510b1fa002604d400bd7a4fa6fffaefc6de4eaa1d8b0e02379e636a68d18ad7023c0fd4ed8e86950b0f573c0e5ed4dc907611d0aaacdd0e8a0309e18c8767a84108a1256993e3571c1932b3954f76031f993811828a21128804a62601be8fa3f639a3fc8c9a167780382011833c425c40d10198020d137473ce77abdb34ccc339e5b55595791f00c7e5af5a83125aee3dd06eb5623de92738bc5aad50f731494287d56a9684637d321cd609f48df686f91b5ae8d48b692db63caee58cb1e5c5b416512fa6357909731989d4b10794dfdccc0c7fc0fd5e0a2cee8befde1ef7295a7e35f1a6fb5264ed94db87d13ebed3edf80aafc67109a48e4b2b97d4f2d64b92b0593e798a1deb5c528c514ac9a8e65e44426373ef3581a48210aa0082beef51143f184008e106482a6e7908212cc128a36469a6516acfd22dde80be73bc017d31420ba120b5d4485a0986187768739b736e22d13677d844737b13d6108d78057104e1c604c6070e7ff196bf384b3dd236250393d9f6b6a98df41c96b1ace34a0582ba8bab5420a9b35c850a44e52b2521a88a7aaa1e119da5e670970a5fa939dc95ba4addb94b859d0e02544450ccf4922c76811c49790e77a93cdab33c9e1edf417a66c5234df266b8a707d2b0cf505579d743807e7c7c6255f8f0b8f8771c25855909b46bd9657196c1262c200feda6d45eb1f2532db262539c528bb85cc552577e43f515fbe3c80f18575920474ebfc172d555f6c7ca59fe5c326c8a77ee72b129b6a9195457f90dd5555cfea3c555bfe1f2a3e52e7f29b688cbebca9fcbdac3f88dfa15fb63e5f530ec8f23382bafff01e3f6cf15a3a6fa2bfcb9fecbf88d185fc1fe68798ccbb03f8ee0b43cc67fb4f8ff5caaaaa2face5d2e7fef202cb5613586dd5ee7555597da3c3ef4fc00d9b7a2be4bebbc293196b60d7432591e62994708d2c090419a19a469795ca91406aac55bded1bb5253fd158c9a6a7b038eca57b815ebe32a2c953f57cbbb958a3db2fdb95af48ff07b401a95168f67d8dd4e6a116a0e4a90e6a92b2ad416d22ff12f6af7b0d4075395c88d4fa1fadc781728dbb0050c71e36392375bea73b9a8cfc5529f6bc525aaefb6d86c03bd5a32159d94664103d253c1d0e3c5c4dee1e2931b6747db3cf4c25ecde9f6bbe6b9f1cd13a483700fa4319dc4a5e6696acea973a2d771994ca6d3e97dd09b4aa58aa2a777ba69339dce97aee31acdd271e8bc0f0e87bef492f541fb3aaeeee613c70882763c84fba6678c60c55c007af9a92d77f4f778c51d5df46878f4d733f3a36faf8686fe6b89fe36196511a43171ddf4f68a9ac2ad4ccff41bd4f4d37f9436d34ff68de60f6952239b9a81fef41bf4a7d3bc71fa9162299b4a6dbbedcec83086c5219066a3287a1477ae5bc51840a3db75de68aa01a1290190df0baf8c788955a0961ffea8d2d070341f0389877647a36fa28faae80d83c8be6c8f2aef2bfe26be473c8d31c6d8b01713bfbd213d2d7e48529387b147507372c9d476faa6b3cce074538f17234faa54d0ae615bc380749057cf7412d0b9a26fa38fce3c50528a42fd23397ab7baa377ac2ef57835a72b8168cb506988a48b488fa14f12d94efb0458e76e17d5ed5aabb98ebb72de1d2f464a48b3b9e45f3b5ecdbcf29b9472c555bc18f9d34db2135dbb8fe6e63bed26cb3ccdf1101622ba4683f6926521cc564a962c79547948659ee63412e9424dfe7622cb30db0dab3d9a933cbd83ec1dae94ef1df885be7c0f5488d4ae699afc2635c93cdd73b240ca53314b260aed568dba5c73244be10bfce9a91ba5937da49b4ebac95a54e905d48a073adf5195d39ddddd7fc12fd899aef1b885f7d137dba83bb905a690c664e1514e50cd6e2f04f4bdef53c1ad6515a4a7a2c9a1a3b978687134571f8bbc783c2f79e1628bef9efea1675256b41ee311f598c2e3df9b3180900a5d809678928bfa5ca8fa5c2c6300518d044baf5773baf1367ac60734755d6e0f630c9cd081109aa00312865cf1303fb4e358cfd047eb6223ac2c184823016a2e7eda6e7116679901ed3806833477c53c9e8362e991e571e5f10dc49be12133186319909dbb629ec35d5c0c393732081b36cdb9c191e1dce4dce0e4bcded00e1e3d7cc058ac85030fa4a722b95001dabac1116d5e38336d657aa571336a7aa9647ae94941f9241aafa6fb0a8007daad6e94797ccfa09047130c7f3191656a02edbe83478fbf1ad336699d2618e845a2ee157f26d6b92b2cec00b730dff3a3dae1b8f32d486b8a95692ece54bf5b694f33699606bc4702353dced86d676d2bd956732f12c9da3417e7bbee28a3ec8e3d8a31b6460cf9157b7cb59261d18891638c71c793f1c4a52745d886627743f8c316fa18813e902184273537ecd59ca2298653af56308250d1ed1a7600193a5078d22c35ecd574c74d0505fae0cb0d103ade21c59ef9fc00cda8853bf025a2209049443adc81a70eee90b652c3204d3f512a71945310e8832fd0be5114c80e6cc8b6513abfd9596fe03b2a954c3442407d200dfd369f681a6a1b08be44fba2b827db4ab8315a6804088c20b4658d1d11dcd9e29cf3c194128c24506ef873e8d34035b40b2df479319148734168f7c0dc80c69be2e1ce8537f1a870e139be187852a90423088d979f6653dabb9aad119b5019615040fb51b68207d20b1f1f1f0839087d7cb81713fff27c5ecde94410608becd3a91f91c8e2904836258af7417ac9a64456c7157f29a8663988633036a0af39d21950b22406ecb06fdec9714771289934a2499415311a414141edd33dfd43ff20a7d641f315f46a4edd03e3a3bcb677dd53ba6946ba192d694e760f7c912fd5f6a9f29507d9300e0aea9eeee99977032892b8d2a767e20fcd499808dad456f2e9d96ad0d3ef1424fd18597a29e8b4bd036b0ec60634be6b580fd08bfc0d1c4fddf6f562e4678b15c45a11db8ce0840849c4600750805df99652f619887f18c90f4766feecce5848d69c3c4c11949f6019cb30bf7d8396a3f00ca6067493d4875e4ad2696a3ea774d1734c1fa520a9a620d79493ce6cba019272d25b4d7f43749a523ad91f249b9a760668714436554ae18c4ca72050f82277e08be427cdc96ebba4284232c93f2f3ba01dcf64fcb3e40931b8320acba03c9b0e20837a21ba1508296750cac7d8fdb8d3ae22f7d557c0463a909e2a6a419663453417cff5e045446df662e2ec2f269ee2551308ec761308ec6a8c3a456dd7e2f6de6c868a36beaef206be70d68857b37f31f131d2866932906729e5679685cef25c0f68c74297675086b83cfa768c9760217ab9ad0d1c85d88b91871184b65e8c3cc74697afc614c3e9b66521215052c7423575e3ad841a342183b9463675e3e3084ee9a3a7703eb23e8e744576d8353a0c253dae91fdf1120c253df2344a50b2872ff2c19791e52124d7a3bd816ff76255de678a816d6aab11efb4adbeb509d52c0b0605435a05b60d082169a209264e8000b6e120670c4cc304546ea2f868d3c98b442279d1697845511445d1166394716ed3c6b25aaf18638cf13146196514da64898e4855e38f469af6ae941bcbda691f8d46da471f7d1b89e8888ea83c9f24aa2fed2dc99bcd6bb3ace6e4a59452e6a0f2524a25985c4d762f269d3074e35988e63c1192c97ae5f3a3fee9999ef7a6b9a8d2b200d7acefb2b48c5c1c3f6300d1ed764fe8b6a8297e8ba7f0969a9aa1c553788aed8d164f2175a3254e84544c6880074f7e0842d334a83961016b094b1c217f70a0c3114d0845a1890a9a24217382c414b2084a5060fecc9f2431ee63f26489100e4964387012646588206442c8a85c8e528bc1603024ae272329bdff61a7a2e776df81d00e4108f590a40510f32789123260c2c4921844a552fdcd5cd5c4081fd309501c400209231c7181d6e6fba5e138846d5d40bbedb6621d9359e0f6d08d1a40c2481048f03c264f7230cd8e78e288274990c1c80165e205ab950cfe40891ac4981022c42a88257850c42c89c8872492b8c12f2c2183264d700282274a86624d782067aad5b66ddbe5b66ddb36259c724a4d4a09653225138928495c60fe285102190cb61da129a89831610326c09a8ce089105b0f4b14210651c205b21e7e264e9424b1446c453c7f83c160b098eb3dd6d7adfe34e63f2642513490262660c18418640920926cf0bdc984582c23f02c42164b888650cb064208211218605101bb1d4b090dcc9230917d40fef0d08c09e77cc79ae74f39e7d662d160c1b318da55e458dd8392034ab39830018b8ad8ed5843311e0d6a50831242f8048824b21f55cf44c9a518d9543d93a25d80b46d52a55fe4dfca092a8fe3cd1483ca8db119ad0d3e2084f1c41393491cbcd54af5e2bdf0df2673b9512c5cf96e008d72e1bd2043c511617b1c7739e8502c6889ace3717bbb36f840d19c783e8365a35ad188052d91bda89d60e399401841abf56ccba66948df268d7e912c0ad08e5f4905c428215c49183b2bb759575d8fb992e9d67e88908c4012638da0ecb17246739ba816592107b4e35ee41605480911340e24b9f23048d3cc18d0abe99ee11905c9cc88d00f57ca88402143ac23282142e340ccc4d39c7c14234241b1a1a16e75632b9918a4310931110932fd2033191142321b224a6cc8c4d3ad6e6c25c362b56ce4e478453cd98312e54501233723b651c18384f141f6df525e762026913ec0c09570a785a494524a29a594b203579a3860e2a9252027f80ce11808a6a6c94b29a594524a29676976022adf8d7856bc186671b983c0ed4780327dc1c93cf1334414a5d88788e27633aebc1c1342e87634b8654849e1993c909b08ee9b25c92f5e0d0c534e08e59c734e082543295b4ac92d802a08655acdb58d666d5efcf87ddbde7befd5b289c1d64bc208212be2e8991c3a6a8010c2586b067b43c16ed8ddb07340028629010220840d2184a9fe68f4eda9b63736bbc3c4da4da9194cdf9ed2ec0dd3b7d40dd245dab33e4c36a57da469da7d94ba9cdb0f561c9aed76dcfe83a4543fb5dd52370c06bf6d70d61af3eebc18789e1c48b3cd0b7f187b41505a22b23722f2060784c7c96900a35433150ad26c2e544000385018a55f94575b2e7ce0bdf75a372aaf06aa1c0974bb7c353407dfcb2636b2cf42660819f21f4386d0a6f58a36af1d72d378c7460b056325f9bc66f8ea1450160d665142b777181238e051951ed0a62a92f94514440e8014b45f1f4c0958042ff6f9ef32421bef7ddaf7de7befbdc74f0a12a908e93fa0fd68d38d8343478e19db1c3a74e098e9f87f1d3872cc72e0d08123878e9c570b088f8c6726640811de61d8ecc50c078ca783c10e6970ccfed967b3fecc00d70b50954ceb6513db80468497d2b4f73aedeff1ddc0f7ee05bd436ba3b97ecc0c6fbadbdebcdeeba6b7375f369e665bab7eaf1b9c0df59a851345c7d60d1b769010d253a55e8d0d2aa517035f71a028982213e033ecf22bbd219c679bb637350747162d4e4e9429a8b44d983439e910cd000000410093150000200c0a858202a160341c877b5e7b1480117a96426248944ac3a12447631c454280006200000000000080cc4c89885d01a288a83f0c36aabca9d738a6452a7926df2a073442e3c1abacb4c80569a3ec7aa5ab3fd71ea553cb7b6740039901d696ebaf867c2a6ebc79970fecfd5f444ffa7892f4871a23af129176600ecc977ebd212fe76478472333cd0e57d0610af97f55b3a8d3c2dc3772939170f3afdca116ae2c152d09f347dea7e53c444d5a29352f496a02e55fbe0be2bc59d6195e523b653b685b9ccc5caa24930d37bfac2323d7208cfd3cd904dcc919d0bb4d706ed50a980840117ea2af79e3f7a8331f1b41a3ecb468cc8a59c63dc01fd79db778474c1599abf624323c0128e02ddf936ee829efe6a623130461006df38d8ec19d961337fb1289cc3ed06e4fe2709610c56819c225f1c43c6c674016c72c92d55537a762d4f6af3b5316fce7d0389d0599d542927bcf64cd70e73fa7ead95f969e47ac9ece46c428f49e0e6d40781ef72761c7a3d0c7b22b62834793440d56b44abca6e91f18f98713c5f01f45202f85b43da167e2925b1e7d00bfb79fb73e62058eb715a96bbfeac77b7b6247c720a9f8e105776fc95c1f2034d240aa999bd3fd10d30347c0b9beb4f593af0b2000abe9e104b833f1af3257c22607776427a3fa7d86035909ffa40c9174aa80753c08f086821e6c337be3ec1c9087bfd36c74f71b640aec3d43d3b1dedeb8c7cb2f384693acbb57f82dbdf971b509edeb09497b70495a616e54068e6e757d887b6c88d3e69f3422bef93fcc4d3bba8ef3b7d00404386e8f4944e84ca406601c577c158e04f7f01057793a0af76cd4ec0c1c9ea09f58f173dd17adb91c1d5cd08598e58449572caa8094fc268d253885cb7fbb591ae192f0c4675dda433baffae171bad5818b3e382c8ed72c6d67bd0ef527a28587a692780fb73f859e463918ee255a1fe43c761cc5035e0e666198e4df531a8f4dd0643f21da2d3ea675695e5736c68214153d0316db1b9f7f63697182d03e9259e9c45ddd29510bef44be984aecf4cce5b776b34c33f1b14f39b417ee269adcaa1aff59daf8bec4fb6e54aaf59f4da8508beed5ce226fb04fca930439cc5c2a66ee4ec019f75246816dca6e966b4c997ffc26005d196d36651b10dd42acc3c09f90b72086bb3a1d59dee36f43a5a9abbbc2ba2ef95b861f41ad37dbf15af4dda792a4a6ebf5890ca16e19814bfe6c82da11a8bcfc1b22fb93dc88f8e54ae2ecde1796cc860854efff70b666285c5b0c1f95538bf48e1a7165eb64d3dcc7de19e16bb6c1ad0b7fa03adc6117d31e3f63d1e342cbc55def9e895e97f48d7aa565b8d650939e634675a0df12c53555fc8d9344813925c524d59ae2542c136b6ba8cab54da337afebffcaa3acd08793888ea9fb6a0757020dc62c20353b92b85da758c3058a96ac863f02945025791f776f36ea31e73be1dad041491b7d103e5832664512b0941b1b79dcc1b35a1d7a69b44187445a3a525c1b47d4fb6c9355b83be861345549e51f69987adc887a868a317e13e96f1884a937b4fb1713dd5c1d874c33d6a33c63791b58e761a823d1db4cc56476f13218022d182579e7dc0b2c2a75a91817c5254a82f575c74f863178af04e040de2b8c0fce7a2d0280354dc39455b05941ae9228bb60e3db1c7383bbd19cefb7d962d8b74c9d3457e9a11864b4c893638cd63259bc7d18714d895ec7edee3f873955597e498cd179fbfd844b0c7fefa044a1b71d39b23e4e4c3d820b34203c0a22fa065c0d759409f3ebc78e93bb5c1e56d62dcab6746ca5d4c43bec341ae9b1fe6e08015775e963f20cd867e2058e3a2570faee3ac5a29a3bf6abcfd7cfde3e0f83fb74d23f4119fd7785709f3a3a03acfeb539651f52ebc7a791d5b85c4b2543444db1e0412a8ae772ba0b2383ea5bacce377b9f607c45ef68533d138ea4c7977354f44edf83fe4885a2bf7e0a0a0d928f9c0f2216a65f72d307b9c519876985c71c3e930fda2d35e99644f68e1171c56a12a3da5e72287227c516b5300c457209516addda4e1df7951cc0b0da2a4099b610084b913398e32be2e613329a4e05423b0eeba8e6e72cdef37319e7642c19fa5fc8f6d923db89a7c7ce9077ce07ac9f482380c0de32f72df4ca64c0a877143e76d44cd4259bd9f866e1e1459e69c0c81b992a8e9ca6871f45b6e269526dcea8f309d94ae3d13be1e5c9ffa5ac0936fd126ffddf0a76e5ab62b8371e49e245f08af874113714c966520e7cad3a9b97339a58cbab47c080ca59a8c69945100bb28a2c4053ce1d22899ee2b4f0a806125bfe2419afa41b8176596efa489a0103ab0a167c85860ae4d15b4c7607042c38141fdbcacdea6cd05a96194372d28ac1f251d5a7213c39929ea6d62f3c968f23a281601d89d12e456461eeb707f14a5afa51bef6bf2dbc15d3046f7e2ddf83c8f1a16ae443cadc6a797dbff72c870fb0a90b9e621da4235e6703e91c1d70e67598d3cf2e738e758497468079dd3189ee4aeb845db4e3d9604de3fd6ea503e8c9be96c88029d61135596015e0b9732f4029401364caa7c4661babce2ee88a31178470249a35d9ea06ff4b5aa481f44042f9e7f081290b88a0aa8a8ccc9e9aec39a8f508e461a4ef49d77bc38a6e044e26530e32843578c6d7a0e19307dfc81e521d10617df72e9e186cb79459d587170520c522568407f88343650afaada0e39cfeaf68928b91fea9d5038581c61641f5729d458361c0f54ea408f6db20e1477509a23922d63dd222226a8bbe9aacd278bfa5cc1c8d32e0c455751f5adcadb4be20ccc48072d54a6b63cd7935ffb258e847ddf502c840256272abcfe49929a562c4c39829aa8fdc90df163ce7bb2f4c49fc1114cf6a37d42c6925f0a8d6d7aee78ba9af9b05eec95621682e88fb4327bd40f1a9d7ca69ad8afa6b3b03d00d63f957f2412e2e39090473de701fcc6158edc683172a2bd7f10d9e81159d62b3550f6accc32e1f586a17ca8a87d6b75f8fd8b7ae23d5e35dddabf99802eed5dac876c388beb2ca8f0daa8d81aa192b7215ee59b5b2af5a68a8aad054f19ee5dac515f1e54e0504847725b70853ed2b3ff908b90748b91b5321e0ed4df97b26e62c7c9504590aa71c7301ffbaa1e32cf9363a4edf1e7ea0e8fc2131dd579c21237b4b69119459b7474c3af1863ed7937e3966fa986fabe19b5aeb7bbd87148763de70f9d64f6811d3e8e0ce0e05c5e62af198b889864c76972403f2e77d698cbfbdb0d35c1e4ebb1ccbf3c6f5c20c5eb1767daa1d7c081ec10d102661e4676fb8e378c05709c6158da63889c77b1e47af445655307f6fef165007cf388616d7816d59947c5c3fd8ad888c91e7f72e3dccafd06a1d5f3e6bccd98893142191b7ab3644756b9c2378a853e5a47736c8a21ec5e0e201af14290623f1a1ecd4f3e7302a3541b8fc896b0ebe713d04d03a85152d824f2a479d96fce279b7a386fd2d1ce1896e116240e3f1663602d1cfb0a0ade69a593998c8274c4ed4b3c4af5783b1d2da9fa958a5a0c8ccbb924e83dc861396027e4cc3bc6f741c0839e153203e9a11593357e0655d962a123d3693129127445099e0bdb2663073336708f9e57bf527d79659938f212184b6987819c3ddcb3d00be3e4845a2512e55088e10945ca03fcafc607d8742d0d893ff32a10ec9efbc9e0cb5915d7a50a6ed08fbf32625c800ff87bf92d385e0a9617eb0a0cde429c49fcd2e3b02358bdf74d711d8b9215bf4fc3a82e99329e6b085ca47633b52ee086447f480af4b370592fe27308f453fba42a569f3899805166ba898d3892938ffdccb2a5dcf38ab80d6a1b00d7849a671042729812f6380403f7626a2e24a09ee2800d09c3e56603ebb0fcd2cf129f0355e1043b209a652df88865a5e1e46e2f8611cdd60694cf6714009f758291f33bb5b32a3116fcde2fae46a7767b6f0d9200b2e8567d2f8fc57ed0dff36194fdbae1744dd8d7f2e5e2a81950fd99546109caec420e4c49d2f79f7f5ada965ed3b1843d5b5086150855a0a2b26931d21a66cd3845f90ccee0391a41e507ac44065b2eaeb495c9dde79c3ab55b575f174d370a1f005c1b9ba13022427d21add438d15f0d3309d8e5fa32b22dc1f57df860602fd25d5390dda830b09e6620f8081081e5a93aa229d080642a36e663e96971570c0498c88a7a07cd070ce43baaab48cb65d6f625b883ec2df39abf828c8c947c32b7274c6cef04e53b2419bea843f56d04167f68c9284cf308855d7953f87fd3aaa8ef2f2e46427333e070515101eb0ac5c71c2a8d242adddfe96de255a9e7de2b0f533bec8e641c530e502414c02e52202add7fb04db8446f743d3a1daafa9d8e30768a300cb1962577d912720050e08dd3489ff0cbc3e415d741b188d0dfb6febad095c46821e4dec2d05653f85fc9193e26b9dff1e4f17f90ce1a1bc98739bbb847dad131ee7e0a2b8410d31b5236dd49768eb1a434b3f1680afa4c1546c2e6a0a807a322b2cb39a0a7a118810cd493f03aa24cc372d4c58178d8efdcec3d8a177ea33eeb5b2be109fb796a3cf310239d32830661212a8a271b6163ee6007a594c23a288a383e3bd376e6cdf1b17a591473d083d32d1680b4a349a2acf7425e59d6636bb31316896314a92b96d76bb0efb6126a02939d4bf45d789c83f75aaad519ede398355a2ae7d138d2a900dbf93612c550688a1ad02b0d065bffa65f8c0d582eeda80f42b9dd7036d159b7ce47bbfa23854019bf34d7f1ce7251b3512b0d79f4093ecc41b58e4270757e4b3b86ee164fe593bb9dd80da9900e9dc855899c046e3de1b6915be5850bd35f26ea7d0a83e90d71227faf3bc50e2570f5d96c9fa432e41e52bd95323c5d91d2d5067e885210be6c85bd8ba0084b1fb5e550a72805661ebc3b4c691f652c25cf55c62049708f892aa539565026285e108e37142a8b6beb53a199f1b8b89e7a66c5214c7e8faa5ce12d514d0d0684c7def4d79e42b4a6d45024f0170099427d2847268e602ddc7385d270c81faca903bb497d8eae101947d60dfd7970b62f3b2012bce1321de810fdb3fe92dff4e4ee29883e8fd6f193b4d6296f6cded598b92457ea94f32a7ef1cd92dc1f18900e8b3e33f68a7b23a1e31080c70760716ac97a2eb6464236b5e283dd9354fb9b7a6f715176f2e4636548f38d1c76d1057b81006251a932bc9fb5a39f01ccf8e233903bfd38b14f9e6faf61c144bd3becf7c9fa6d68d0f43e6f4546bbe9021a46f1d9775f9a66187d37b9fc2e98c9cbee187db2d41341fd11f9275dc037790090f4c643d99ea8dbabc0c92dbaf97667b5e16f8eaa81539b2ff43095644baa520637f51114f9ab42fc7c7a36e9cdcdcf6aff7e8d7cde54eb53cdc7f8aa830e62200c9eaca301a8b44699c228ad114592333458092460c13086568b7ecd2bd016882e9383c2af0280e3e1f12dbce2ad70e835b2bbe2bea830487ef430c977ca078e3bc52646706489d775b02bd4b5994c8d1edce4091e39cc4116bd5a57787cf6cc03d5a736ebe634bf3663d948fbad13a2d540d3bc245295b2b848066239df94c7b4caa63076bf8ed621c5c548826d81d9f6c3732a00c2287749d00bf5a6a5c7c60a676eda8efb3a009b8a12645721c71555ef00786276bc0cfda14cc8742d262e8a2d538f6bdf1aa0e1179dc2ff2855357205fd4815193ee1bac98ffaaec181726d5c09881fbca2fc918ac995936c4aca0a7b8a1b6e34b7ab3780265faaad2df88ecc47d17a6f228e2ff3dd4648306f949b5a6840a131f111bf2f02c5cf6ee7375e9b341ba50909914948b996ec39d99114e34eb51ef476e6c7814c30932e175c2042c10285f824ccedcd3899b604d67f1cc7a142ddef082b2677e63355af19c139abb85dc68b5c12bbc7e327064442d2806aeac32cb137fcf5f0d6d301c56bd3a82f95c1893ecd3c1339c2378b8e3bac03a88f463a147bde21524ea3bf111d82df5f815f349682c51040d46c483783a83d29420a84ead44b05ee4a166cfba36b12704b78a79dd7e8d4c00c830c86061d74e1987415e2eb24e3da66d44bcd7a9abbb923d87181ef732b82004f6397d9ac1ba6a06d9350348d11b032aecc7d39b47fe499506ba37061a2eda9a8089597783024c6a90737d16835ac73e262902695f0fb09ba13d4bfe3bf489ca492f603ff32cffe4c71c4b71d60105f78ba23ba299c36e4d52df8269a548c1eca94bf54386c6b7b2337df45d7f4109ee80302119237053653485021675d2d483f130d4655d10c249bf271cfa2bdb0f971038bec95d9c201235ba44377082e5862090e329d3e27eaf585916b4be8fac23c6ff63c21224ada8cdfdd22f8c05cb983ef1e234565c2a4eb7da5a029f32cc82cc704ae987c3d86132a8ce98463bea3810e03c709705189be36ca038de66adad24aefedf964cd7544cf3ab346f9bd881de68d507fcdefd270788cce2bc1349f6389d66e9cd70524db14fb86324b3de40162f795d3da35936e844ea670950ec1cfa1b58dc8e9fa935742d8e0c27fc92aceb5fdfe6831037f09ac367a6511a4b5455014ac3acc960e33f67b7f669bf168aa0d98e01e75e0e9a174d4b41a41d952f7390f9ab93c1d2c115c08f4f439ca3e30ec167abeaec18551b97539332535a9aeda5710ec8dc42bbf4a73f8cb12a90814b122d39f0497b106eb802e5c23aaf179cc53de2d6de682f7250286d41126ec7c098cfedc7e227ebf822ee061f22fac44f19c9107db0e90bea52ee62e66d3a9e681bf20cca2747e95f5f80262c10a9a367685d3a0cd42ae3ea267314a5ee276e74a87f14fc5eec8b03347a073ffc4c9b4db49e4a74392893fc3cb46028e263fd817860e035a33f30d0d9a6d771285c5346f38601f457a756e25682dee0b45971cf0274dde01a43b634807e1e84cb5c6a06f7aefaa2955f5071fb1d045ef1a2576353e8df561f55bf22e344125c0afefc6592d03caf626955551f598250ede74ae0ca8d9f4113c3f1c9486bba8f58c1449d9d1075626cc9afb4e637b6bc1f6402aaf1f322a6b88ce08eb033691e7e0ec5b7e0708d88e8a67b27210bfd48b7c1222340cbfdb639f362deca0c4f41811324f3f482119016bfaf454b1b57208ad81686dd136dd69b5bb85c583176290ecde3b0cd09674bfcc21498bb22c179693b8907e9527c85c79c604419b34ff52ccca51ef8e5145d4a6bcbeaf83e4e227d9ed23e00f07d870e0ac740ad3c40cc35474fb5a1663af1689fc45b2ec22dab8211ff7ce66b28466c0a6fb2c6a3a7850d60f5015cfeae7518484438b313457d6741606e37f7cd4f2ff91e909f847a768ae18e68ca513cdb2dc664ee892df0b30c13b13d8ab4c5a1bfbdcf62ea43b2101ba1c8d0241d44b1156f438979ec4f509e6bb943e96b178e684bb61c12da4e7a43a0b39113ee7c4e34cdfdae766a2433f932c978d2c35d47c6208701f00037d80e017b57b6e33a98e866480bf531fc92dd82f914920af0dd96c2c233c502fb6981ee27d07431cf106325de81f48a925c4c7d39cf95f50bc74d770db35866c7b79a943266b4a6cdd345527e8270b648be17de6e60641a635661811aa641cc21900fd2cb5c04b693c51df3b9b2be758a6eb0bd2a07d5df8efce70959653a604cb2f9d6d0e467a73e7341aa20633685c202e841f6bb3f7bb7f1b9326c62fecdfd7745dd955721ba2fb48c13e85e6cb7d46d09ed9f7c278d7163f381d41477c4576cfd86a117610384e9cac79b1e6b0aea972c549ebc612642002016c700b1f610b8c9ced1d89f34820dda58a3852572caa2907dc96c90af7e353db508754c1db1725481a29a1e496b4bfef3b22b23cc108717c07cceb2c21ba0a41cc0ab1586b89789676d49cabe2b7100a3d738dce6e40cc31c2a9665f17e69676f4f82f6f65778111132521a9209e68b5310ef0ea05bf98379469d81705bf327456b504f54247f6991e8a384ac11066b61f62d4ae77ce6a2b310101a71349fb8e5ab442f04e4bb632fd39fa0684ebf61a475229d5c5080711bd91687440474108be39ce9a78ec50945dcc883d67e523e12f06bb44edc222f9790906e91a9db0d929ad40d101688a497973b45c98c8f5b2cb18b6cac6c06768d147dd9241ac46a5fdb759dbe134a0ab06eedd0f62ec093b0b8ee21f72f9291065f7a9104e008294c98b97d655d0550dab347041da13ea97890f3db9458e5647925cf488f0832daa5bddffb75c35771f174131df5528388e488f87206dd9309e0980627582e947abed8dd22093cda2d01a3e8d4c41dbd271097cecc79692f7886ab53511a85066d07378f0e97e0c011012fcf5614ba9c4f12f1c8d4f39594ecab59d5d9f5aa87062ac24444deed4000b0fd0188b9a93dedb5ed6d0ce9d74ebdde13af8140836092a08e3aa34cb69a46e8078c6b850650627611044747da20f0a8055438c7307d19114ddeaf1e2af29e28120f66cd50cc7f6899e23bb11311915d4210cd03d5f14a2a6cf5bbb3798cd9fba4a5d6d9208fd0767e255c5e1daf7df71f85cc8af04e15e2ef694c1c3c387ce5084f98ced71a769699de2534844606c1c7025c8dc1b762f0b72bff25f13089f1b611c0290e39e503cc906801bf981bb06970902145e745ac6f6e5290db1f3bb7d5fac6a84b02f593086dceba5bcaead6da902c030bdc9255079f3406e53cf6c2c7ae183dc28978d7ee75af71fbdc2b3c8a7fa3daafca02492a5507948c80faa15659e5882bad2bb2113352afbea574d520e5495733a40fea05b70791a26a39fcc6fe380775f2029411209a06d8539528edb024d8686755e0deb02973f700986e625cff752ae3df0b4704273b062ce21a609c857cabf4360b0921d921b964dc1b89465b652d8d3118d4e632476931f096db46627aa6fe54047168357630c19f1ef186a4f347999c607cdb35091d884440998fad1bc63e8c1ee0f402d981fb0a03167ffd7037c501d5caea1f8cbf0a79eb3eb1c75156a5da3694443760f6e06efced69ae90382578e6da6019af3534e680ef284d00626b0a956e72087b4f721bbe95aa3ba5889a3d42da6b9e4f27b50c43c873cecab6b82f96dde4672b3c33c2bfa7344879eb537463042371b1ffd972cf67e0fffadaf9ac3c7eea212f69aa98e335e21e7f0ebe056c1d257e29d6a392c51720cb32e29a50f4df392e3d809f4bfb86da09bad738884b73958782cba3b58b0e6f7a3f5be1cd2e74592b80bd635b8b19ffca331c60911f37e990fc271f47859583b8c7fa70b900cfa0a5823c24be84603935a6e034b249aae66d0de690756656cfa870807e90dcf4d899bc9f2d8cafe2ea0ba943639a21c87fa30bff5d7d34392588b0ccfb1977763d0c8181ba18b9132912371d39c3b3a4446b158ca8e7b6696981c09140c76846f7afecdd404402fea9d9b91a73c99d2d974f7af5fccf0bc7464fc938e758fa7f2dbc54813dfff6fc3a8ea2942be6fb2c16c39336363b6473f762657ae1ef495d0cfa16fda3b09ce8443dbfe114a7c65513d6105a2558b31772f21e3ac129f7fee831a7ff93731459b27a5108e08c5f9e9aeefc9e7ce17c88118c09a47edc4f54e459b7fb5585d36ff484e3b6c7ab53e626b87bd9d5114a140c08d3f09d32d5c490de8152f5c5f86b3f8c5601712df41ce232163940bb610795f17a87c72151eb08f5c07b9a71a620a046768b79b7db5e73bfcd7c47a1e01f1b326d8cebba7e94b814cf37ccf454809a768485e0e3b333d0da6e03fe1bcb59fb5fd7d106267d55426f86fe542fdd4112bd79237c0eb7f7b3623533c30b5d062dc60840d6f193e2f46489fc45f231238785417b1721cd0abcf9a1cafd1d7013ad5a0362e40eb0671d01aaa1e8cb53a6f538ab89731ffcf97d03883ceed9fca16edfe2ae2d256430ced7c9fa730c967620b2c59c3ebd0475e69a2c48d471bc7e1ac7b5c8350493017d8a30dd6283c7c499f371ab8dfce55c7afb49be1979cf1b8efd52ce231888c8c5df3cc3e9faf8b897ca2f6ce53b05f6f61423ffad826fced124a340fc83912fa06d263469455fcee4fb06482df194743655b2fe43f937b0801f71e5ec6d8f86ff3c05cd2ba97a2bb7cee3b77575cda2eeb1fe9e1eba644dc308682a984dc72f95adf5e01b93abdc4e8839dcb8516624a9cba5b6d1080f04cd31d94ca9633d999b25ef85f1bba66436de41a45f59ffaed7d966a9d3b8ddc0a9026e6005efce7ed0533960abfba1f7654ace2dbfb1d23ec3271447d76c01d9867e11092b718775fd581629c97009b8a50799753cbf5c6606f9fe9de1fb43d33980ebf517a5a754cc39ccbccbd45c3d9ebb3bd38713fe9530fb8cf9891cd18ffdf565469b81ae9924bd02883c318b11070a96a0abb351483674e619ebf1a82cdff6f2b3795cd0a0de80e19cefafd7d7a2b7740c5ed2a3cc1c9f058db1c5e9889fb84cc40cdfbd8afb1f1518a9507c185cd2a2955c80641f1f8d815ebf382c5377e2b38111804ace428093c3a9cf32282f570b7916f9fd1c56c260e67dabfcbfe39d3c1b4d83ae4ca770d949303e2d22dab76fb0d723dcc4bf633b5d2fbedd3aa58e3b7e15da1bc3109a2df29d3b26a0a04ba16c96eaf2e5b7b7ee382c444b0f75b6eb8a3b963d06986d99f98a012f2f0cf78921ad61418b245af64b401ae26e2df4d868c9a7fb987578b8b355fe21d24bd21bd95e157615f294c99ea5a401e02979d3f604f0c9068aea33c89e86f8cd3736812d9fbe8a8ddd609199239999fc58aac2cc0012cc1692e4e6b4bb1f9926901284a381bc2e247d74670d76b9b410a61240e72008acbfdc7463a677e33c2704d1425092cc0a379f647a93cad8ee7e101b9a639f26b4967a04b352715e37abde3141afa21cfd6051301d86be5bebe22c6c1ca5662249735b4b801d9bc65856cc3ebb777ea716fc7c9a7b1a780dbcc751805e3cb638784558ac3c4655313bad71e8ca472d2c61d003f7f113e666f69c06300bcf78f544353d2c13e528b44e4649ab152643a185c8252096f92933e0d45a4eedf9f2b6345c12c1e2c525bbfacd85c63c1301e645a541d7a91c12662a287def63ad345e9c2ff378af8d164eec09fc8ca32349a5551f553111314f1932a547b5ac889ae3a671f72a3d0580c0f18c4562abb5d4ee2b90e0d2155d3b03399b453f71de33075f4c523d4aa907dea43204c573cc16186304d7c86c627830424647468c188b437f0086910f734baadf6ff3335ed89f941630d789c07bcc3e360e1222a27018368e8f96048d5f2299239e97977d7a3fc0932644849829fa63425ca6ab0d3f62ffc483f91737dfd358e98116f480d2bc7866a3903472e7d2fb50d490c51106a04a3ec262d7e3a708b184c95300fba72549ac56d222911d8b8e254b82dd6754e1e98f35abfd3e207c5783d19fb46109832e7d558a1b3330298eefb548716805fd478c68e865d259aeba546bc9a01397ae444b745cd6033e2214cbb6e4c802abf0957862bd0240cb94d0a6e4bebeb1b2d56908513887095e74df8ba30209fa88333a3984d00c0080be8e55e945bca45b822d30a0c4e130646ed0535ee4ea4abbfe4156f89394932b8e0cf12180b8dae500878178c0d5574d7f775e5b9b0f2ef86ad0b3e1b0721bba3dc9cf645cd67874f28bb13db883795988ccacec4409241770c4ed6e3d80d8c777aca29afb046bb7a2d3f13ea1c6ccc6fd7c09f9f7d24c36e4e1825ae102ad1742bd27983ada2c913022690bfbe510c2370fd1febe8726ecdd30fc69abaff5c627456d62e434e4afb4413c4bd1f27974db5bca2ab0039eeccc210bc1b4298947713dbbb3259f300b183a4c3cb62ff47a721669f0a27d0e69d387b084713fe80d629b5683a2390e470c27c748c7a31d068393e3eb1bf3ff1bbb71b23ae7c3137ab9e02ff77bf9fc5b5046911ea98efb7198dc349fa3db3280e715ba5b43f36f1f3f44f2902a13fe3a9a77783813020174674f741a6ba2cd4141f35d754228922dafcf4b7d76a10b54048e3f307305a696ab5e2184e1b530c18740aa6907abd55cd8cf11cca66894a15178c0fad8ccf61807896f5e90cc7b05cd0d063fa4f3aac6f14c1399a325fe988cfc59f28c223a8d9b5264b0a557cfeb685530a5338b1ff62d9fbfabda97c7818bf5dc8bef1d27053435bb291ea7e5ed049f15d30eb408afab35735fcc7e93926190c77712aef9c4b194374c58dbfc630afaba0d88d858239e2d92e4873159cfa4f636bfca74199fb6ca761c3f66993f24e78664cc78ca71dab5c5fd27c951cd1f9e39ae5c83d6e3f1994ac4109ee9eaf546e7c03b4d64e96b8c20febd5f916dedfc06ffa656a733588d0b5e5f6ffa2bafc569285445220fdfde37295eac81ed0aa55d3ca3929766056005b24de6544a198df5896d15a9a3eefaf52b27dd05b581993fbb11b2404fe1f8335f3f0234414e113e6ce61f474db179775040bda69bace1db587b9745d2a54677a6d5ca63d0ef9a1fcc82924761e5b98b342bc609ee3b236f5df4b074a986642ae54676f775f4f8e642265d5682519ea0628cb9b5cf640a98ff2405d05d48d090b1415e1cfc2e97326b9f8260244dbf1ca6be8b29bc1711cbe516cda7500cccf34bebdce1c1d6d7b912aa7197658af6cb2881fbec1677b49d236b7c495b5b009cb327684d9aab13e5e217830661c1a63a3f2b43f005d00af8f918d2ad5638048f9389982c892b047241ceb4565ec75bf91f5bfc7d2ca74637cb0bbc135eaa994891eb6097370550bc250b7e31cc7c4fcb4aa82bda256e98297c8b76a4a205c09b74cec6ec48be9804851d2c94a9f01ed984a3a47862b4df1870212e1b35059bce22d5583a604ff398914875256a4b688ba42e1737f6c8a966454c3281d82be3bbb9e7696d90269d3eb176e7099fc48f29c26cfd7eb8db1eebef8ac25e1aa341a83fd740a42f794ce6a22bb68c47b77c11bc4d6deffbd47eca7397f5cce79c1f7c2f81bb53a6598a62ce4f39370f3e2b92816eb86483460ca65dc7f8209ddf86905eb876dc613750413aca38d97443d74940f6226aae366e935d9180fea521423c0afc32d66715cd5a06bb708f70d1d85bf791a96d8ef56a588c1a3d9efa7a8f898a407158795f062a4be71ee2265aaaa041aaadc56ac71737c1a8adf38e7b25bca09575694249394da1836e45bd0bac64b8141c175de9b525b1922bcdd1ac747b6b0a8766274049565ad201d1a9be07111e41688cf73c1584f74ee621d6f9b548c4aab76727af241031b5a49674320fa2b9c0e629d82bb7d71f7bfe68c54b3e1feb61f9b89965e49a070a108244580ea13207450080a7c71e85605e11ee4521cdbd80b73550f2aa06422fe84d58aa687b5cfae8b8fdfca9237e0a69219ba8a18d5c129e3a5b8caa64e949947dff40c4657d64f4107a3312c5352bdc204c301fa442347e059f39d8da31d8eef3c42856f3183a4d999283e408c422db9c912677e68763b91ad4ca0543f732cd79642a4b5a487a0c173ff7835aad2c92d6bfeee6f0da6ee46cd537bd7174c4339716f301110a4c999334a56e489df8e2ff914112a56a1e60b69184d0b4f9b76403aa71ff7f778ab7ae53e9d09f2ecb431675df372ca5d9ae58ce8901ebf4bb74daa81b2772802c8bb40aa8a6725048dd6029e706018d36fa7832bb1bb83fb9c128f802c1625f23a555fbe9813bfcaf841edc92ff318998fec2f10b5843412e6074dd499bda6cc396cb1ee180493e10cdf67c205908d4b17795140ca3b2b908bae1fe8357ab0fa27187338e3d482a1a62c1c7c7a242a077270d17cec50807f7b5bb2be80a82a521881e3652006671a37a6b596bf718e2f9a634857536d92a218d2e31eeb96b9a33ba4a2011567f66536d610f37c44ab12ab8cae0cc30d11bf6ace12ba207f1c8554a767d7a470df195de2ad3636bba2adbb635fa8c7c58ccaaf78ab4fd17301da87affcffe80d0acc687534b2dec9f892db370a5a3b2673c5909d69a1c88ff6c12bf1a2dd1ec5d3d6bc7071271e3a89130a0b19e17e3aacc499c32d504abfea0eb00c9566d9a2217a0bc854fbe03399de87d01779969fd0ff4793657cc2f239076c8c87783d0c797387dded6d728ffb4930a6a0281c55833dfef3a37411aee80468e7869fc06852e3ebf978b6d933d7a0f33306e2b37ab7190de6074ee88eb85bd0226560beaf96399e040440de8d1bf5667df01e5b52037524d340a10a489b3d879b5924e80650f9637d31062ebf1d2d282581295f64a23115ded90cabdcb8d16488ec7cdb8bf8923701565cad18da95c6478180788543244c440c2b89a484c6e90278de75953ee494fb5f4eedeb5304ec86aea1aeb746aa57e979a968c00326209283f23c385d5b6d545867486e7020a653f553383371525afa195ae43ffa795dbaeefa98956f0be8e20a7d33dadd5f407dd70f1cdd5485ee6d2f5b49479b2a757462a6f41285213f41f74b4a597413ea84eab86d7652b2177b3515cdbbc4083ba7c4fd3c94fb5a5014116263b592dca3ca7f3272099f416858cef73c9a756c047a98e79ed1d76f98f6e60131b027685def3be6262f33a25556097fd4eb6f17246722b3fc8c169b09ae8b783fbf6327caa73191268d04e8d383e17427d5f04f68e5119136729e1aac2902c788a10d2f6de3e4f95b6f0aea5aaa5041856aeec53256613fb9adbedfc1a1b0236bb66974e5155d7c1046be4c3e7d8c6268e186291b15e3038fd6da0989f8f78d47efbcc9d387669b98c3c77c111b03c8105544314c08fa7acf9c3cfa1ecf57f5314ef39c379a122b0cd220a0894a347f32418d2d22ae093f8c1be03286002045a5e41e51d2d92cc21c0bc199c277f618151b9da0b6e818c2ba28248cf313cbb988fb2c59a53c7ca2646873561b8d4d876f5108f0aa9e9eb48df288b3470189d246986572be4ff4c1ce00f35885d39765ae6ea7d3e60fcf7a1c3879064f6ea7bca5478711624886c10e47d3948b466401b730015e13a27e2e1ce73bf110987d61112543034cd9d8ece31f796574971824d642e80e95f47dd7f832269229be42a8b9a349e1d280507372f4f426427306bb600af20815d383ae094118efd52b112ec471423a8afc84d843d7ac391312c445ea423f6cf5692b051c7996f3f5f501bae14dd1b6e5909493c73f068664ab298e0126e501091d437b22b3d8f31360fed5b3741d11f988f8191270e229ed5a89b45b63e167129f5933dfc5578f782ee27b0e37239f9881e052a9e21c6bd90ffd99e2100c3912903ec6053a7255f9df28270fed81934a349d1c7b6ab512ee3eb58f89aa4b34e487b97a1f6770ac2772dcdabc7828009f434b2a4aecac21305a832cb471b81fd87c8f019740dfaf37061ef9001ecafcaa6abf0001612b77fff5d54bda079d71164e05f11a40a97ad971d708ef64bbfeaea5023e046c282bf9ad5538c6cc91d12061a93d590ba6aba6c828dded09b3933a21f89d26f8046fb82ae37d3ace04ece50c34b2073bbe9500fd9996d9cff26083efbf4e0a54bb3f893b20ddea7b47268b62b054c7c6fb36866744030d0228fe3bf767836f8d7c391de5108807ac16218e3170086c671eb150963d5d3cebddba41f9c36b436ae6b620f228e16c6160c10266250f29153ecd4a10f14740857062a261f525b9273180f80d431ac2672f8a72ddd401f1c92516afd4f923bf8553230959d16412578b040a5cd52cfb187ec097230dd8c6bd162f3fc97a611d4c6779f0d5ba63d6ccdfc2b4d863a11b56811d2acf888e9a26983958b5c671c345a813e6a0699797dc0d1426d962c302b9b2662d4d18e7ad434a82180d6581cd30291209f29bd97b8e18c7792fb150233c1a385ef1bca14949923fd1d4a410bf7be851b33199e337403e0cd7dc43fa6c5fc9940049844c1b27e39a9e74a1f2f9b5ab8f1f357666fd197fd3cb087c55767e732e2a3faa4eca135856670ce7d2a0b6330c7e5cdbd0a69d82454f530529aae8d26eef322f7a0498c1bb96562b3732577f82c2164f07bba6c7d368d28626ba53e0edc5e991748b1efb99fc6057a861c8359519a12ab9ba0f85eac437d95ee4a83e7da8b4f5960db0e634d380bc7baf4cb6608c240e262450e40042b427a9944d049f2707ae392811cc00bac1e76a96071a9f6a681da60c941a63055c210002e9d3b998513d8daa3e0fa95550164135c7a8ea8145790cd0026b8d6affbb03766bf4e061998932e3ab632044976d1862329c6365eac33fb9dc8cbf9b471366a76436b1d4fad514b2b5f35e4092eaffc825354a90765c9f5ed6380f9627052c48cdb54e76e5190c17e6ea7dcbbb79aaa496d51f8f479bcfec9dc5a5d89b2098ee2e995004151aa45ced007ff5854ed73f5ad46543111c7c1a2c051e722df30aab27107a4100040431c2d68cd9afe60d86780663f219bd54e3084e39a9608289a05b9152b58faa4213323c334ae5772d777d0a90f627e42aa377af4a066284ccbcd70d82a5a89e940b044405f3de207bd2db056d68df3df841e98812b864f3e7c443317b3b4dddc9d7260c018f37e5890aae32e780227e95af55e3610ef37d53863da4e348d811a9108063b54ecd6988026223bb9e772aa53224e5c8046297a52c0d83ca3a02906360561c82573f579c015e433058d1be3cfe272ab3bc4edee09eb6e7ae42c9b9c9b979384bd5cabd95f6a814820e3924ab51a146a202fb50430cfdf354ad2859e34154ea8792771da62cc8413c33893ca5e0f3dfabaa900c59ce84ae148a4aca18206d33c316b48120936dc84e7826fbd54059d75e7775452628c1fafe2bc022fafb31e2294938e7ad77411667c29d5137a15b5ea6ae158977560414ddb096880973ad39996b65207c3ca589953b76f5b2303de1231bd4e67c51d7369f7c9a362aeb232167950308a6f2b6e16e2383b0b57d48281b787e6c3b10068b9a08b01900773ef9b9a42de2987081a28a45102b2a794cb24296a7ad87c24e9d22762b63b4349b3470f3fd78eb85434eb0366a6781230b76935fa5b7140e5e97e49935e5f12fda2a442193eb5979623abc3a29bd41a29ea81a16ba0fd7f5d7e944bde5766df5e28ee49a1e160101a7a8a44fbf3f86b3a52626cca8af53254f746471e4ec88ead99ac3e95bd52a38f8909c251d86baebb35bc355cefb2e1ce646dda81a0eef7350063a2ae95f523b09ae4d63eaebd37b61364b24e0e537a00cbcd78392c30ac34ea98e62cf2bcc9199e548a10085fa1a52f3a077f263ab50e462288b629b1df00212cd3518c263c57f42e836498e5f5f762c547915b8932d9c13e89e20a06b71de73b7242b828dd6022b26b4a4167808289b3922bfb5cdf735e62a35f13976102af65d8e5bcccfa451c43581785aa5b1296415112868ba078c5d440691b480853dce5b78de947c56ffb67071f4cc9c498f9151624d58b1837735502ca267530c2bcf46e69ed4468598387a8cd3b1702135d10e1a66de9bb024c7b1c573acdacf33c84c1032fcdcdeaa8caa5f7a620f079cac02b6d88aa9e30cfa9ed23bede05065f08035853cb5df485614c1850a7524026b37b8803b5e6b14240bb75604c06785db135ebe7c1eef5041d3d0cb86e5ad2425a9fb25ea2dda1b450830a1df9a064651c7d44be3929f72fcf241256192090a5e7ba88964ef0c85757f1e873ff810e46546ad962d85292d283e8e4236ce982c547fdbcbf6c04ffa65042e51914c595f3edb19d31e4be5c980cffc61828eb9773a73d7f0363cd822c3197a0fb989e057c0d04eaa1ce8359301856a503398aa0449502d81d8c11bb8749ba20ffa05aec8342ecb5596bba7afd477bb3294410d81fc6a7ba8e6b7d06340cd1078568a048ceb12ec5e0428560216c6c185b8d823ce8f2d03bde2e3f2164932da59432a594020a08ff07ec0775eb9fcb9d6b8627494507510fb1201dca116a39828d25818bdc93274ffa35110f51d97edd361879cc4f964cd71debc64faacf5dc9a579b237278ca6baafbfff908f5197ba8184c33a4da45bc469fc67917b42cfc36ca029d2579fad4d19e15b7611eebf8f3487d6b6f73a2f03d25abf7f14486bfd39b4d64466f0e8e4e0bb638952fb47ea622f9f246525a9135f2fd10e26a5d77c55ea9c1b3d43674fa6a38fb2c183b9bfa15c30319df82c4ff66686ea4f97cca8b337a5df2f6230b1aca5ec54df573ba8d8968aeabfb1907696542a5929bdff909d22bca605941e83a5f8820c663f77c5d6542a911eeb52cc0f66bf9f25fcd661e9217cd29b1e50a245b06ca7b592ac18895e1487d55aa43070b6a6c51526ff24b16c42909361be7254c6d11197d136ccccd97e511d6c1f1fcd676f4e3629eef7a1ebe577566a9ff639623ef2e123f619729f1a3b8d4dd77346aa6bda21bd7fdaa735afd183b9adc10abb664ae6a38fd27c181302a74cd79fb337a7952461396d43a43bf081adf1154fd330922e7212433e9623e377164448d3f863a730b097d8cbc7017b893d0ef207f6248cce187f9a1a2085b9ed955ae405119281b59961357fc74fbe08c758ce103f9baecfa1527a7f66027b7f0682bdfae9dd07a82be614320ac982f91bc3003e8eb6f16bd5c2c7fc956dce0dbbe2253e433e43ec63e4e563de08ccb7f03e2d3ccc1b89f99737d2c2c33cff8f978ff91f30dfc2af29c5f486978f791c5e3e86c7c90fa781f91668c4d079d1ed48952d2eeff29807208ad9d8fc2bd3817f3ec6d8bb66d6c2c3f497e92f975fd345533e58fe7a1e2c7fb95cd7e5d24237863393cff259326ada9d19c4c868b535fe9ccdb031f86f8d3f4cf692a98c988f6a9bea83669344e02e8585380a7314f8ccc39d7ba854a6b56d7f70829eeab8289b81bf5ca157c4af62cdbd7e42b99a293102e67cf4be5d5321c1dc7a682b159c7e59d4a69b91dafb45c5b8f4fa086b1f9fc1c2d4c1fa0876fba8c030d9c04a5af3ffd19ae70cb9bbbb7ffb0405755050f577312bf5511ff55f6937edd8a3eabb3009ac8f3aca3d45dc47faa0a590620aa1edddeddeeddeeeedeeeeeeee9717b0144951c3e8860e1c160b3243862c66e9c0713a709a9d9748070dbbbbcbdcccdc3c05b69201ae17b6404004545a9bc1e1bf41a89282e13bc80c1932dccd54385ae367150e966b766666fe21046c679c736fc8d6b8eee74710fe5ba5622ab63ccb4f6ac4c98f9667792353b5370ba17f4bc63ed990bd39ed28712ec9a9cae47a7a96f7ef66edc0b5f4acbd31a1a42963652757a24110b03b1443756b85569d7bce39a0f7c4730d5fee2abaf79e8c5062ae989bac42364a75ffe0ebc70fa60ad9284f4587a7c421be8a3a3da21c5505d49dd2832ceab6443a6d438477d8c0d6f86aa769fc688718911fdf08e9af3792ba417e7c1ce447cae30407d25fb1a330020b7a388d3747a9f037211b45c329c164ca243ecb5743503c848d27ae230a9c9919250525e59314e153b967561804ac3f13351f75e539af02fe3a4737f88f9620a493965a933af53dfc182c228cf051215a752d8550f5bfc158cc07341fc8579302659acef214f2fd1906ecf5c6686fd60745e0629357d08099a8fecb1cc5bbeb4460576cc412510632e2c4c8cbcf67eac298469cfc98fff2465adee58dcc7f79f93f5e7e4a7a83cbb73c0e2eb485cec7e185f6e0796338b922494fab85a2b8054eb4e869c177026c0e6ec1c6e02ff905d57f2b40cb6fd345feccde945efe4be69209d1aa84f0e147151493ab8eadec1f802826bb127c20e6bf4e9207f6f2d9e40363791fa697cfc3f492ce6c0dcb1f49f55849750c319f856e42b47aa21b3f07f888c36798056d6b7a87f4fb457f8f984cca40d259be72adede1ab6d99182833623eaaaac1046d0a99daf40526596064e62a0d2aa6d3665c1deae79e21ef676f96cae99b93acdce58782698d73b6a69f851a7122df88c9488b164d54d21f2d74b64ed9b6e8145332b36a3ccd8c253b994a376c2b6ada4b291f89b93c66de2469ce45e2217fa51056dd1e75ad6dc6b3b92a8beaee36b9b2d63e29fe0664f6b42866fc477578bfafc3fb99d61af01e62efab1cf972984c47517388e588d4a4294ed5350c2598dd42a5dc1fa98ebe98aed7f2a7985f5fa17eb64069645c44ed7fa11b0751fb5db26dad85a2b2759a4937ee791563130b6a6a285446c4743a6b4c41a39d097c60ae5c29e28f05808cc957b0c7d99bd2fbd75d3d94385be37fca8098d608a343ea12adaaef16aaea6b85869a42ee83b56a6f56a4efeeee26637ad5e9df58285f6d8434088b8562c9752cf83bf6d29450323333ec2befa8350ffc545494ce843ca31afd31256db3614992bca3b629e35d11f28c4c37c016a9c55ed46dcaadb9b2eeda4db59837bcd7c2b09eb6619af2115fc833aafcd93b729ad75a938f4851cfa792b2d7928f623ed009c63a8c5c861a0032e6fb55fdaeb6f5bcd85df5783204f9a29291af520f4221c862f2376a7fd44f60ccd29bbec998ef2b03edcdf5fe6cc4ae7a784b0db235ee92720f5f61c068bda8be44d5b3114c47716cd860c621e8de8b8e424919639cb2b58d30ce0c66d9e8c174cf5f627202e25ee48f35c6da1bf9fe08d81505ae174e30b9625e62edcda9facf30e61601b255ac734f12d26fec231fc5a252b5268153cbbf6fa13c4ede894e7aa4f568e901719a9da7b992f9de81f86a9d5e9491d41ef982165347d21c1b399891fa6a3e03b5cf8c253b65920269bd252049c1137a5a913ecae4f4285f0129d250fe32244c47a76cadc619b38704461554aa1b90b688412d34818b219c73cea954aab8854a85c3624928657cf1c507df7b44443a4960b19c900ccbd288706820dadd6536c29d93ae72ef82d16439672f95e874293d7b215d948a7ff220184dc53fe5a46e383df6a948713899be64c61ad5eb01f277f02aa90650750b5285a04c99bf5051ce5d088a88e9a8d65898999971ba856a1ba73ac8bfde8504a9f29763203d4e0b4155ffa983a986eb5d4a130f29976390976aae4975041b434c219213ac64812040dcc62a42a15828162bcba1357e1d140ad53f54301d9d366ee03e1c44751563596ea11cd492b11323981b07d9ecb4b470cbb7d06da72bd3213bdd6b8649b584f9fee76cad7fdb1932c4848382ba494d6a09f302f3450e04e7e01886c9765f481e84c9e5fb87ecf42a6645e305e6afa791f5c8709c068636ddf620568796373de625976c9f4da7c7d8fd796c757aa472b63d4f6da9fb26969f476a4b6604a8b20301d35ab3561809a66e7bc4411021e2b6a67f09f3514a847fc78c21d486122d4d12c2766663a1ed8edf6aa761f9f6f9818f93b0509d08577354d3eaf53abe9aa827aa477a5a3abeda5691516ff790504d50bedad6eb384dff125d194b08f6c38183ef1dd6a427f562cfc31fa3d7468a72cf677627b1bbbbbbbbbbbb3d6eccf0e01eedc636e39cd98cd6f68f38fb107677bf9d98c25cf6b1b252fad1fd3ef2a426042ba59178b60f998061bb37f039ef5de726cbeeb6e6da19c130d13d93d9503255d21b37981beafddfbdeabfd3b9087d6cea88ea802a1381a93baf3a3d526d48e16a20af778fd1dddd2f974c60d8ba27dbf3d1665a5b199665795dd9d7dd39e79c4c6bfb6ee22153b98c1b4d4c860fca84315b468cc950bc603d2a0e0034b3586203fcf8beebe07b6ff7ad0a07eb5d319841d960c1dcaf802be2dfdd5d56edef0a678bcc98fb4c38a1a1306028418d29766912747b1641e510d4fda2a67c740db235fc4663b85684b9bbbbbbbbecc218bc3b8223b7ceed0b15f3d1540d5eddd6c00a1c3038aa73efce9d83cfb97577eeee7c092f5c3167eafeeeeeeeee5ad12a151898134f9a245104904f104838491133841c01a9f263b7b51552e198d15032a5246ce4c8ccbbccccfb7177e3f2261104b31793a6f2d3f062a21a6ea99782345b0382c96114d1d7fef5f02390df8b3e99cde43f947fdbccda2b775f8430a8335456ec460feca3a23c231b3ff81035b250c5a868071e472a13f83fde7b349078bb4c83c9ac41c6dc18949c1b633239428d283030bc5a260316735733ad7e9fd9b7bb13a7dd938dd0df935ebedcd5f592a2beb55d99182c3618267892079d8ca898c940d4a3645a6b94cc37db58626e73c1da6f0d7f0e6606b09d119a3586989bb36b002bd37f9f77777737e6c85474852d614c238b29e9be8d26e64ec1ae049b3ae75ea63577a33a4d43b50d7c674305b206ced14081f3540efc19eaab29567d33541b3008032b552272c0b60db084b571830ffc33f38b008a5b06c0ae50316b03f36ce66f8d8d1dccfdfe554548513a013863c6f8bd42b4ea358498db4ff69d7735124c7e9c5e77ba19a9bb355fd41a56cc652f98983375f79979eef2645517c1c1e782708155594e3304168bc562b9271cb19ce52c67398b15646b5230fd5907334ec78ecbccd281c3d281a33d8b85a3727de3068bc562b1de17af57a4528521511a9aa6dfd5f9fc6f4429a57c7ef0e13cd65834f46000cbdd5d4a09afb07be618638c91d9d9d9dd9d3d875e5e5e66762f689a0d820f2f851338b3fb83fc9c1f3ff8de735591bb226362f23b1c2c952ac618a373cece9dbb3bab543f3f103dd18325ecdadb60010f8fe3330e1ab83d76ec38711a87861e34f4f06e219cee6eefa3103ce71b3a222bc6e8ce721d38cdc45b91c2fdf7dddddd9dc3810d5230e3b226e64ce5dfb76166e699224851c619ad83f7de3f32bb3f7ece8f1f7cefb9164bd870c2f91d92d29412c89425294b539626f7b4d6ecd345482965510b7991c3542a2993fff9732ec618a373cece9dbb3b17a10a025b609bb5a5ec6e4f1020bc05b6dc9ec9cd4aa8a6737f9b13c0766673e9a9fe4646975193066279f727b08a51d1d64040ef75b02fa5e0294bc554c461be3ccb1b7179d9bac785a6e26fcbe5e7ebbcd054a44c60583ebe7c781975516b31bba1f4a787190ea46f895d05265b79ca8c38f9c1f2a596cc080b4d3d9a82f44789a620069f09b38d8a6a5431fbb72e223d5b995917b9d1915191509191509151dbf4bf4f693784847cd5455ed442b0a881dc7bb8f3139732a4c4e8031587a552b9604762633d0c5517aacb4f561cc434fedb7caad812db412c57f6068784627950dd39d73f145171eda7ca195727188612cc9e39b40dc38defa61d444c20e45176ca142ce4040712ba42dd588a5051dd984a75262ad24112cf3133b3a41b2a5a601231c590156419d369ce3937e426d11095ee86ce3987d58d9fc020c55ca7c1a188d3b89f5be3de2cf22a40963ee0cb7f116397a6dea33574edca6a9b652c4a299d9da1bb3b1dadb9bbc8d281c31a8b45a483088706e7eedcdd3944624807eca07622a89fb44d50db38012184bd6457bdc405e90e9c851e39bec2520cdf6746caaeacc6871e14e4e3e31314743deda08ba670789acae1b9aeeba24648543ef6a4dfe40b4854731a9722a4494a846baf941e4e0a2b5ec17ddbc4f59730e67bc77f23c70d6c863165d89bedaaaabdb9b67d792089b0fbc5ee187b5a7e1cff9df7373807cdc6e0bf37b42c93c17cce060d58865c1798b39473a250ff9aca65f5d7cff5632c1811d1d35239d18a0f3bc180f439482e9aeaa7a92ebdcb8a1ada2efcd1ae52bdcd20eaa2ea4157687ed0417dc58b9c46fef095ee69c2072aaeb4a268fdf4505369f9d99bb5a2faf5397b03ab2451fe691b4b4cf85b934a7cc557dbda4a0a8f6e17b852df63dbcdcddccdcd1de4b40d71521fd556735dd757a91c6eed4be652c55065e2f2ef4acf85201f24e167e2617a5734a58a264d000034858a1e18a65401f4de7bd1023c271ffe222f959f9130fb44f8de7b8fc7d71d8a54d343530304504d10bb68ced670c6a4e5b3114c1365e242937cab85e6d0345f2f20ade5a0d31a7f15a2eafecab22bfbbabbc952e9ddb4a3e4a4bce8eae547caaf0299bee01d7852103e77777777770772ce392e02cc9441cdfc8dfa70d4c772833e02b6336d33518fa46e3306d0c1641f7f6229ec586a9aeeafd2ddcdbd0cdbb9eeee06c2465bd50e3cb56de0e6b66bf6af816b3fcb000421a7a2b5cb2d8719b7b87d05337bd352a2507b93d9101fd0b54b6d23b1b8651ce1334f6477db07b64739ba18e376c4851e0c58266907f90a0e0ac5baf67168e8d136f16a930d2cd55dd27dd395964cf7aefdb97fdefeef3987cf753be79c8350dcb9b6d94ac4c580852bf9df9b5ec95f275422bc65750d8084b9c9394f3ea819950e190d074b288a94284b1e4591ff9052873380c91b2200050ed868623707ccc519dd503a0835a6e886ddeddabb9b9f737ee420e71e81ad5c1dccfd1e5c303733ff00fb674ac1d4360a73793695cd602e45cd1e9512b065ba357ac0337b6086ec1431627acc8893d21735c129e2a6efbd9144ff45ad9689a6724a18f6bbbbbbbbbb53840748a9a8f452ce8942fd6b9aeacfe88946a809337b3543cb67a8fdd3363aa4e43bd7a1c6bd694ac3d6786bdc825ded581a5f4d2a5a6831a9484002a49c1385d2321bcc142ce17fef9c93f2fdcb48efb1953bf0b0ed38d263cfb483f4188914638c54077e57657691b2f79bc1c7f882ef247cf735c26c6b37167fc2ec6edc1d1ec01f290a664e8e57103afd50f6f9207c867e7eb4d61e84b38b17e5fc98a9259389e6b4f62468c8a727c8e7ba7c2e9fcba727c61e98181a34b2dd980ce63a3d4eefb4cde9fb8bf0b4cdcb632b1a2d7cc7bcfc16321ad93b4d0c098624747a1696fd93634c5d7e7b976ce33fb51ca99b1901da7975a25a3b9d284c6bdf5ae9bff4a52795de6344dc4cdd76e84d5ffde802356572e4d02506dcefe796242d3d2d488ab605c9839047916a36f1d2ffc0be2465b7f4d4fe969efe16242d49da06be942d3d06a90448947f28acaaade94cc98cb48615737ffb2a5b7c9e13ccedbf776da3398ce65ad3948e21264ce55f38e7d6f8bf164c3411be443c43ea4e0b10a67c285fc8734c66fc48ddcae882291f5ebb10eefa83bed0a13f775f2a3eeedede03471392743a8d7b3f09e67e5ceadf9a3637544569d01f0b6cc843b93c29e662661eea45ba6a8c0b4998f01fe971dae5a22e990e566bd257aee574c70eb35e18867563cc7c750da7d29f1ecbb65931161af35be8e66acb63da0dff03700eae979309a407035c5c32977fc9b623f58564c391daeff2301ff3476aef7485a107e0185c1ec75f321ced4271bc2563f999b1d0781c67a1f13e4cfe96f4548a3035e2ecf45eabf58074ca27b8ee7969cc08fea25dc741fcc43966e71cd31622f2a1d6daa9f89013b5cd90536108bdc9d6bc16daa690a3358cb298bed91621c176a694f34327fef5fd38be3272fad2ebec75f2ee3d3a55995bb775a2a91cd3a77e947e1f85914818866154879d57e5f3624f7a99318d30870c27b61685aa71064b11a92115dcc5a97d49c2e4fba7f8698a9f7449ff6dbe36c3a4092a219e5f1f1f93725f663aac73bb0fdfbc01161dee8e936e378392c61691f62a423a553eccc7e33ded57c2743ec6dcf7206cdf5df32338ba4e6b4ee38ab9f10f13d24312d57e878445e8e460346da060364e4be8957fdbf197b407035a731a70bc853fc559b4cdc65dddc911101207c482eacf49405152fd541d0948aa738444d205d51f4666989fb9f1cfcfb51e95fca07e22659f244fcc248c04c9a9080950eacb96a83aa5a1357f940f13d37cc53fbb722dfef92a13d55a0f17190bf969d0dc542a370931a9c8b034fe08e0c1ba9f60aaea0c94ae72e932ba3b2f0e964ae538bc8c1d77767700d49dc245142a3b0f05014a1d128294b6f2f8c77c349523e5a77078920e01223fbe931ff05fea86f778247cc17c34d5294771349d9c211f035b5fbd250941551dfa5110921a535dc12cd8b7bca9647a6c4d3c8e54cee05fdc3f49d44bcfd9cb64b6c95aa2d7fbe88a7de4276539b51f23bdd3940fec99547a57fa57fa96528976b605a95b7c21a80abf25733f3323291fcfa4779974ba09b9bee55f361f66a4c7b2eb796861e69fd747966cc697d929934e13699b6a70efbcab5408a9b461abd48e4f3004ba85bd5b96eae1809099c975d11b5738ac21fb41fad2f5580fa42f416682615405bbfb516c6304f372530ec0c3abfb3e0094616f52efbd6ecd39008b8757477b6c0d6f590aa6d3d954e2606e0f0cdb1fd294fb232d28910c711ae71ea7f1c7d1646e8bf42ca7f17f1e273c4e7e5c0f9fc7c98ff7a4f7280fca699c47befc6dbd16a43c4e7ec097ff5e9394e76485104ece58e2889e9e16fc6d459a72746331ad51c6e46e0f02c68e999999999919720b7a7208b2234454798b2aaf95caccaff7bdf736e72b323233730fd89483a2084a28395128997f191d8c0465653acda11e3513843d5282064b9061b16ef72dbb65d8c64e61ccede12bed0a821df5d050d47f294acad6b410e61ced7675a30a07932b84912ea57c1efef282cc84f4d35724fafc358098fb2a8f82edbbe3b1153e4b15162396222196293485a6d014e22457eae2ec8de3a8dd4b847aa43291fd17b337379b8de3342df4c0f0a79343518dbff19d6947dc1ee62ef3ee83fc961f3ff8de0ba50248b91608b2e9a813c70cd68f1ac04c01d1b0ae1fc2e722906320e74c2c32d03090bc7a7ad68aad0fe440b257b294529e4abb372fcb2dc880c8a9ae01ea5266e75c77fb200261a84c89a47ae7d5dedc77fbf3c1ef32b012d979b533de79dd5447f52ba0d6b9f7778bc3ddb7702c958e1ecd7265a830f963bce15e724f4008213c8560655adbe7c79c0b8d8b947f5669aaf6aabb6275d26db1e41c8490e53087c5923c9976b8f76c83cee483e574d5699ba0d40ea32461a5d3c9b403b2904824928358bc349d4b5ef2a25b91fa7e1d0ff6283663fa27b19c5a3009dd2b99aee843c2e4625fb1c74c19d77855796158c9c425d2258342c909a10b742f5e246c003e30e460f25b4dbe7c86bc9a24c90f540825ecf196402b9c7b2e2ece6bc0112abdb8a909e8ed304a520933954ea575af84e3d5bd9cc3b2dd8b2e4f29ba479af9bf7c965eebc139dcb42a1c2c1d3834a8349c07f2f5511209bba40cca997c74752ca51a49a6fb969294e11cfe5be59358b21e3803f8c1b9e8a2bc1c8ccf7970261f0d5f1532ed80afa36d204eec815382596aad75b40df429f54909946a8ea032f714254c53a870e229506a8e1a1f9a764469f34c3ba07beebd7c3fb510438b25a81f2c6656b75275a1bac96c16759bf9badd80a2fa4f7f141645a8fecf82ea53b210e30a30aabb6ad5ad6945573e9ac215772e8705500841142e4cb0648cd686aa0e8812643185085664418812fc8080074660c2c41196b472488162a88c2952008194164a2221836b8da40580b46685b9693df4153aa804a8ec57acff4e916284cadfcf5d6052be77eeaf1677a499a180556c4d36643a6c0c1bc29d57bbfb5980edcc4643a8ca777fbf5b72bffd33ed60d92bb5486bf2b70a2faa70418b8787090252105860a95fec4aa70a1b9fb063541ec41934142822cc2dacc09ccb27bef2971f9bf80ac6a12a65eca2d628995b51db1c61736c95d18b2a5b8821b590d1c8de31e95ef86172d7df1bd38d5ebd2e9a463e2a4d237ff7e601616be46f03c8f1b6d818ae526f3475635095d9aea21130026d8dfc93189306529e67a50623f8e96945cacf3f5800f5b4b67f9b25ccb4e3f4588d24a6ba3dc9b4835b3159ea079c818816a4297f1aff829c46c638bda169d0174379d60a44082d69f1f060f2c109091cb49c5c7fa485954a0a50c28b29b47878c07c683999cf037422a5e5e49a8188d686aa12894cd2362f4b23df09bf4528aafcb844951f6750e5c72a339af148318a8c54ba00c2bafcf68c504fbf3d30aa945f83ca7328efc15ce9943994d6e43f20134c98dffc89bbd04d524a77e4a45c1e90d3c8081463243f12b5cdcbcb8f54daa6e5e5472376f586e4d51f9de86846af743c76d11d4495524995124995b18c8e56aa8c55249418256651e5639ebd275bf3a0581af9f249f1a2ca5ff92ef4866e41e9a4f0867eaaacf2df10bb62bd9f19b096a8d2a59cc2989ec46b1081253b7e74130ef0235095f94a2ffc30b11a49e616811e50facd7dbb7c948f23db81877c1f25ba438d0f33e6e51d898947cb7b4f8691aa0b794bf692c9270ee5bdc7572dbdc77bbcc77b5a8acc22268b454e23ff94c52eb2d8527c15819c46be9473a250ff9a1681e47f13a60fb526bf85ba94d66ab8606e6fc8573ab3ba64449cfc223abe72294e235f9e4ad95697521a72a208d49a7c97225fca3987acd047d49afc127d43ad61ee09ccb4a314a97362bee761a32e96d9a82c5f45a0a5912fff052be61bc2a2ca7f54dac6f4f25f179ca35f9ede943d2aadc9d31b3a55137d435b231fa76b92cccda554293f0249a02adfa54897f280ad52af67d38e8beee092ea4043282302c6ec1c7c8733ddc377cfd5bdf0c3ac41d2f8a4c7a0c9015c750c31e1c5d926771e468b60a7376e30d939e7a02f71cf45298304103f3550c2064710e2052df8e2ca1146e4600a1547b410c32545ee1cc698052b3c49c2103968c20c2758a10415380081122730a182bb422663c04408254e784117538042869210a060c915a04c3134858914362049f1440112062c24a41799a22c5118b51f070a4525222881edae1eb7cafddddd0bc005fea94eb235fb5c63aa9576360ec5746109937f06bbfdbaa39b6d33759ff323e8ce618c477e888084268e008612869c58820c31a650c1159c9c6186142980520c71723d745c2cb870428c02cad5e46aa21af20114e9440b21be0cb65eab8579c945e7dac78c2d2cc6d36e8d6b316fc183254894a081cf1320545284d822046264e10390348208084f28c20984b872020885951e643144122b90c0e804a8d5018bd52476f166ae68c111a0f0842ac838430a2b6070c1842d4ed053e404162c4c0924c50f6f098e85c417ce42820ee1b19088b34a54a2bada3eec151f3180a001135378e2071f8e30e2c910097ab0d282d61457c4e841144dccbc808584640013b56b37ea7de0c39204ea0599438663d0817f7bd99334e523bec3e91d380b3b70162a4e3baf2fd47e1b5b801dc06a312c0df7afa80b2e00f730bea3a9afd03d184ef5b7a66d9ef3bf59c9d713dd3fbdea265c4607e037364326f55b2b7161bf8fd89b97774f5a606f48bf1f4408cb6fe9779f93d89bf9bb2ebfcf437b03f38bfa05c0af007cf5c2ef7317bdea245ca585dfd8ca169ba38f88f98d8150f75b06bbe22327fdc4935ef1d090d03e3bd91c6cc58a12759f83e815070571115c82cdc159700cfb437ac53e572eb03938098ea13f0925d47d06f46ac8f6a0eed7cdc14a3601d4fd006c0e21dd849bb80c00dda40c7581c90b4cb24f61311f74325ea613a984b1b4b8ccff35ed0000cc4b130f580180ea247d440b34682789a17d04cc0d3366b034501cbda090e0d2326425ca9095899582b208ca82a583b24ec2278912dd1a4a12a42887b092a3235672755027d15a3bd1423847cf6063e89774eb28b53fd2ada1d4fe1a6c4d3b7ac38cb9ca28634df8ac9b384dc7df2ae1e3f4be7792a5f24fa6291fb3c29f15d217b24ea29b8460320c0c3e981b4c1d9269171af0cf0c899393e94def5eb53707a832c850d7b4e38218c9e537adbe977f2c34751da0ca5063762d1575e73dd735d9006ba665a8348e9734b9748846000000b3140000180c08060322a160301c10a55df70114800c7d9642825e984ac42889819852c810430c1100000000001161da0437a6b0e522f92cd1dea1f89e37971ad5d5ae6a19f08d1fda4017a2b26520cb6aa0ed8c4096de7a7c85e6fcf3fc69c0e1280c41dbb03ce1dbf8164a0ada0d539c14afea76ae4c05ebbee972f67c993ea3ac948e8202c3f4fc2926d0e056b3a5dd8c5f4e19e78cf55573264b50c74c72e178684dff59667d6b287d758ab6c6b84147fcacf6a41592972c166e59afc5377e3e1147ab9e622219a8118325cee7107ba4ef3e8ee2dcfd4d4b0956c934a8b32cee0b8ea6e1470a4a8a4867d0e040498af32353c25cc695a03fd8bb5d9833210360401d1bda1f68a8f2abade700d26ed461111c2a3fcec7172ed24bda9be33a2b4f4bcd1c37859ac64a7e32d4193173ed1221198a1642a0e84990695df40bbb8528b01e40299b5a7a282d3079a3899448abd99005d84a349db69a06748defc6d69d613ab6c191cce9872ea292a7b994c13aec11c00b1d459a6ccb7e5fd0393c8a3b1ddc41bc3108046a34cee57d58e4a49eb76173a8aa42c064dbabe18cfd566842b1d2afc61e2c4d6bb250050ac8c0d185b0e1c87070280136c1ee2ccc5def23e24ced8f71b9ab83afea5634cf6b76c1fce88e58213a065985221ec66ff4fac3ec96a850b5bebf57c567d1941a4434848bef5d4b0091f9327a3bd742eaeb26e487b5bb09cdf252ac5ed35395170539f9ce0f4a5654b95b264c943fc941fe1f74b9894ead8263769b0317eb466029b8d220459e3839c28ea990c7d3f4370633dfd331bb285d06acbe1b15ce55f4922409f3fe1375b456242cc52aa2a67666a6b94d178881c0a8f1a265362f63d748a7939809de85ab9590cfc0bb9c954f78449a4485e9e5667e89a4a5669a973568b732ebee259e57f34baf616c957623a29b7dedaab895ff888f41375c9701b6887c28e9c80d2d7c1e680818e368cba5915faf72dad86865638e9a3ca6584615786e18f927e06d17924563d4471c6fd5fe829081fdf011d33bd55be5d69084183c481668173b227b7b5bced22a42aa97d7ebc3836381e206d5d0bdbc87120913324513483a85696d75365fcebf717f041ee7efa6ce4a602a10889e5502fd53c0ea0cc95f32c5d09eacd083db3def055333d5ad4f65a023b582a704cba1d96a4e80575765b5f33b07a9f2c37af8a1efcdcec3f3488194ab170633e432a600cd5d64d6a6a684cc5b9e468cd019dfe805dac20167afd0b31005f190ebaab8ac58da12bea89604fb44d5f8bc5bd9e938cb9a72fb4807b061b9d3e11c7d0d0720801ce5f38a0919aad0d418aed7a138b95906c8a70f31d5f2856f88961f0683cfc036d9af8905ceb8b28a0bf9823f518eaed2e527bdfdc401664eab89ac9868579fc69079984a802c196546a4c34e57e35371e39712f5ce9afa8383764e0bffa4b8b47f0043368ac780e1e5b94ae037453de232812902c616971fdb0a8d9c00414c4c9498b542cee512ef9618fda4aba7d9e685f6ba3e3bc2d4c6c89e7558bf29351644542f5573e53044604c50e1274cabdc5db0a2e0c5b9d297b631cde910a1c73838bdb93fa2567455b40de3cc09e8ceaa256d8deecf131d1b10a290bf4f60ae71d69686d720e20738298acf90e446044250b39da5af7d6c7adbfc574f8fdb71ccb2d08c7c8f178028c78b5dbb1f705ec4371e03535c2c5635533d33bacc1fdf70c460d1b64f46dc11dd812c67c6cb1fcdec2dcbdf97dc69a7c77d8a1335967d90cbea9e2d24e6126e9fcb2ac13a09c385cafaa9347048b3757403c71ad60b3ad0be104cf7b4011a79ad4e18a9fa2b9252446416de918d40d8838f375fcccca3d40c1a59033d030fff5a34bfe9ea5ab8a6a52c80d94333837551bb82b90f9f8a520b1ee8584205f216a27e4be7c432bbf922239a4ee63d363e48db00ba1ae842936c500d13e6091caae4a831574e97721285bc7053534c9462fc9fcc681ad695ea818883c0be88cd9164d77e7aec733c320f2f150b743f75e1d81735d02e086fe3e45cf99ed5d601375bddb55c1517d2f3adf779c06b34f4366ab022f945844b6c48e75c948755d8aa5161b63f8e6421067e633986f30d3ba20fd1b1f7c1eab219259830668996ba3f60738827bdf99925ec8e381f92765bc5029d60197c94f2ef0be469ee53d1adee894e96f03abd70751ff87b5f08da0a80c57627d1ac10e2fef7f0e3614771621d55688ee881ba39fc359bc7a8f3db25e264e8db84c65a35b5741c928a4cba3eabe4fc0027d44b26d811b0fd584a10ba43ff18e8fd26ac3df562de1c86b466d161d8bf206331bb325f02d6724a18ed9bc4943e0183fae027f6ebb1f3f735a1c7e51417615c05b9f121a6dd55f02069da2a0464491ff376ebc91c726b58e255b9cff9767b1821355883fc6007e79767eefc56f29f1a0548e87462190757dd8d13cb861a3bf7a9b1254810d7e72d73e98e5fded4b29cb34c8cee367e791c9b84e6d5289724532e1cd63eb1302fbd00816a9f3b9e22af4cd0b92a340911a13e5edeb820f62d3568a09b8f6dc972c8742859c879ae757b7e9e49ecbd1bd87adeb42042fa91e0b55317d40d030f4521d95b76b06e3957c9e68083e75c45911f79f312a80840a48371f1e518494b6cc00f9b2265bbe9f6d71ae25e3a161bdab4bc9c7119c4f407562e13915eb4cd6d97cc97447aa3de3c0b6db5a2705e3842fcca8885363b86d9dadff2f08d262111baa1c4f7cf5fee4654001ace2aced542a9f48f1e00558138da355864e9a45a48cf147db037a77ff6069fc1f1011c1a191ff7f239732bd9eecb9c2ebb5a6a621e21c6b7e799a3de30499f63385682116298a99822f1e05b626e6ff5a465fdc8a35796d657e82513404e1efebf246ccdc228798811dfa1c5e1a2e5003e9aec41d9cf159be304a3dc48de91ca874f4cc8f7398f643b14cfcfffb2567b1a38cb9bccf422f4a11dd75a26f2ce4d3b27bec7c807cf4e2a32b3f5a5d9038d2c1a174d9d98054577bee976f00519eaf6fd4b5561c69f6860bb98052cd1c20fd0287ad0c0497810541dc22f7d76854ef36bd718397ecbe8c0187852b0e193a0d69d58ae2bdabf31486d6e95c8066f538a6b60e6e663ea7bd8f4fd7e740846137c2bdde6ba8e6993482ea4fb4dd898159619d684dd5df2a0069e6c6faa28270a8118a9027212e0f214b874c110fbf94d2c1d6cd8a03501a97e14e07b6ea6834b899bebfd6c0a3eb22f14f8c033d9b7fbbc4c7a4a494da664232d583fbfdf8349425fb3d2ae473f6c7e475f7b282a0c6eb74c8a76aa0009eeaa7485ef6448a892655084d9fb6785c79a71f9382bfd123e92ddc0fd88bd0225f0dbb6145c1c18f2b8805d05b1aee7c1c431e3c1230e96c11c545866c61d1a64c25f4a2f38d5004ee03250c9e9ad20c5d29116644dab3e0efee04961fea8a04f25aa9a0872d825d20aafa1f95b9ce8625b97d906ff39ff458297e20462a7e6f213d4bca78e01f980d552ee0b8ce95ada802d7552a233c3449f9165641387f1e2a52631a4e934d9c612d0bfd2dad6fab2d35502af48da2b51a4e8b3f1e25f14d72b96d842b90bf48845ff47151d2496f02889f89c96f0b220afb300f8d68111a1d5bb689e07d067d22b647e7a9e4c2d0d2c1b8b278faeef76a7e86368d5d1f8874b043815ad0b381934cc4387f2cf8766484e2312248706a90fbf4ddf681b9ae17da265c56a0db447013a7f3d47c0f958f9a302fc61f7ae351b84c9a4311dbce5c4b3956170e62e0bf86cb54216bfd71e845f353d28038429f97febafc4d49b4950472601993580b20afc5efc308cd518a7682e9a69c4f4b0c926bab50d4dcd03ea03c3cbd514dea36ab9595a472bd3175c736621a9fc8deee642f5a5c4e65d2af890383d823d498f26e37d8a88eef40aface055d49b044a1595ec14893624282556cfaab802dc134d0cae99dba1d3309594d33c8999095c5a9bd3619512d1ae85d673e80e2582cc95c8c46d3040959fe7c1f4d2e2992493677caf04c2c2fc05db515c9dcf02ea0e1c8608615b72f412202b3b84191d3abd6afcad98ec896c54b4b2133d9fecc235abd75b8db7de64ca129e0d23b2a578cec8b3d51b84b81d59f3c90a8e83b57f9d9880d0cad8ffc56686fab428f0b53ddb41215c17312954aaa9e4ed2204fa5e1c0ca6197a71f861456eda9958ca223266ef1cd45b794048e1d70e20c8ff6283e8e64027818f4e2394abe32e0c492f0b650d014935d7ada36abd6b794e22b6b6ed7b4bdc0b45a852a252e88a896da978f976258e73e1710957c9a67ce9a8dbeff41f187845e86f2b88b9144eff427aa44d8c1f95e8f910ce78104552dd1c295fe7a47a0014435b7613b5cfa8377b7731544dd137aa4ca6ad1054fb5c029c8fe260469b5630975d49d754b57ef06690728922f96ea53232346ae27c181186c632961d412ad5d28e404292573e8696a49e7c9d31e1d4de43a6f464213fa844b8937b2849d5cc86fb0efa101709e4ef94d502fb8050016de7b6525090b2aeb0a50279cd6934310ce4b8420fab73ddbf06c4e28ee6ac38cdcbe580c6b8302f3ee70a70853c0699d5d2c2b294c30f6aa6b2e69f37c6254458f7fe42420c9040ef993ec7e408502444865f21355a0401b2b88d8314e1bc11a6c65bba14124abb8ada805f9b84f1f271db02cbe351c292d52ce12656d0def703c6eed2af423803616acce8821637ba2bed4db1bfb48a64a4ada9b23d3780cbb9c46550c3f3f661bdb0a8410d3dec0ce54d5275f50e0784051acffb72536568ea374cd9c9324a5f9e81ba8fc15357bb4c94057ea709b3df870ddcc7a6fa9d4a68000238dbd23ff4d051123b67590c4e2fc0b5c5e1642c17052aa558bb17adbd32ddd30053ef4c3f09fa4b278a491299d7afca7a6260f60d7589df7ecdfb060f1e80e5a20eb5ffec13750963380f02c530893fed0116b10327e0309ef6483b764e184b70c4a0035e01f95b0c5e634816925441d19b0a58041d7b220ea2241015a5f088f00eb8770f41937629b3f21198e18724df0696d4351765cb121623e33d5c0693abcdf0d97db1da4b4043b2f987022f4447374a97c14cfbdd72ea40fe0addf7f262d71ecf0f5d94981f11e3930d99635f62007beb11741c4b1001f90f87d43c38353e8438f382201779327c24199711ff8e8933f11d5186340fe87fe66c48853075bdefde6d5bda5ba2485c5ec687e7908f973eee0ca78f5eca8236cc7a7f21576a50aedf7b811008a0d5abfadb6a1cd7e86f2a9fa23493edf886afb2862c0ee51814d0f820ac9903ee7b622766e8f9c358b445f7994830668006e9619a4c4a23ddd47e8f4df0aa5deffd990bbeaf34c24b897fc3c3cc4ff70803ff4cb55b810983ea99c205ea9e128182ae65cd3231a8a3efffce7f7d8a72cd934a90f7e7c6c0eb7c229657c3f44579ad0f77e96ae5c869ba29402c891dfd42e2b3f4b10febd750c690fe2f0ed26fece5e7b7c2cde893ceb77b1d267560eb57b5b391b7ee684d1ebffa0cf2483f469fd3f0f9c524c99430d660c21d35d5313636d32581f998fffe21815a7fd1a3fa087bacebbeabde37997a913bd0cd6bfe3159e42c6c9e85443dd68635559c296b53a48f9bf1ea8550779f48891a27594c42bbc99b8fe6ee57c177fe42e7fe56032c4ce73907b4e5fa5cb2eee0dffcd4074c19aae70d0ed6d33b0c299fcf0eb217858e200a33d313625a32df5713591f0a443fc05472008acf36e5ab71ae7e7bc8d57b89e6698a26f775cba95603716b8a35d9384c2073fc7a12b6e9bca91cec855d39a3b914599396a95c1499058fcb7e48f2ab54bbfca0d6936b0422cfd6f2efe788d332091729ff42269729cc190303b89134fe5868ad77d8ed34ba9fb7f51dfe22678beac53b8bc8c64222943305ea2b173764d6eb4dbfc50c22ee72f2c7fac9bd0639f93e5f357a2a22228a8d1158558b109c9127b565211112808aaf084d2a1ac147967821a691eb23e1662e47b6807b04d28f55dfef06dab40975bd671155295217eb554dcfa3db8c860e55854e8936e4f474c457ad17834bbd5d0a01c4098f2964124baa8105dc53f11adebcc69bfe98488aa9cf1d82f186b6d89d468ad922860725f27d83a9a4b7319336c510d518f54d7e385411dfc886bf6a2bb8c1a97acf43b1913d38f2124537b6c15d5c0a6eec27f06f28cf5a3541ce201c01698dbb7eb7d77789f939a41e470a23fbcfb2640785cc334a12255aca1ea88d20bfc319cf293f12067792a1271385d5ef0cd4786ff98faaa86d9029a48f154032bfb8e06992bc93f5a6358ece5c98deb80931b0a686b36312d4808cfba99c3f4b408eecf19170a908eabc62178cfb14cac67141e9ce32f0fdee7ec0950425f1f4070b0e5667fe6dc9a292dadf196dfc42c4ae5e8f04482e1752d01e6e6cc67cecce3775b97d9d9ed749f593c8b207cb60cecf3e3c787c59a41d98037647461aae97e653bc1434089094772c7cbf7bd626e94c446a234932766f620ffeb25b96fdbb2a1048785517106e8d76a3f328a756b7c134165a40603feb6cdbddbb798e70654e7e1a4ce87c3efab2524fb9ce265bc8dcdc32ad0c0836cf0974a015027254af520dc48f9d61f2f011e1ec523689ab9a1124cc76a0e8059ac5aafcf2a49a33b28335778f8a6f29fdc81d68b3e14ab97e26ce1f0e28b80f45a99970cc615243d9e6caa6e3eb9857a37d1f7a4c89061b1db4b29150ce61c290b5606fec0a5a1e249589dd65982069a861f6c39346187f8a0bb1a7aeb31d5b766c2aeb88f6104c8b9ce72eb8701585b7bb736402f925b404a8ef7cbc7ebf2cc2250bf08e3a0168b9abe070bec1579d7ada40e033d43077e3ad636d1f1d511d9935e0c2c5c916ed9456b5aba20d2c9e06713a2b2a18a6a066fc88e1fa0cac0ecece833f9d94a489c9e88bc781cd949c36c9956f64df1c9becf7642e3a8f0ea9df5c28236a318590a243c21290636195df81eac5957e911b1a2a56a5adcfd09e03b037429df89e6e3c2f3a7596c98e992fd583b1f6a5730e1442499bd1a0148c8d6708ec1502714633da40ea5dfda27083099c63a9ee95269672607cb63db9ba78e9a1d203b7a36a70d3fd6a6b220a11559687a22dfe7a03901ba69a05f9730b59229a2f6ec9f1ae82b6a72367505e4121a3c34793b3c174a3020be757a979386516b1cc14b411e7738c2fc8b8588e597b13c76fe64ad61efb052d096a2f23a4e1c32fabd8bb6ca479248ce243846106f544f121c6455496e96f4e5b80cb120c471b9b9c5ad14eaf0703f3afe0bc6d138ff7b5162201ffe63634e0c80fea68312e4780d99cbc49651f59f3fa1e6a7e36cec658d83ae0be9a481785746be9c259037e87d1e6bc2e68ea42d2e1df34f17670764f6f3fb584112114985abe277b58f3292c13ba94bd380d2d47d0d75749d42c1b520bbfb12faa21a465fcd1e66af1fc8414212ac21f9e2c789944b973ec5f49bbde62284074222d28bc180219db376c7926fa5f2ec0bf422eb7f5e6ab13f34853790fce584a578fb2be09f5ae194c406049b9f9b99742d9e59052316e38d8c309847b42d029566238d36c3de4fe7a8b3dc99d8a4ba633dad850b07219dc37dc18d529a7daef5b611042224fdacba3a0af8d3a5d6dacf36c3563db07b8e98dd0850621d6501da885b7d8cf5f99fd783976d320d79c28f263626a701301e7f14a40f4c5d8115c9ced7b8612816733bb6472182386365e122d1b8b892072b549901649ad8028eff9a74e3d74ca0a269dec98edc55fa2c59d8bc48bc2b641ddaa6b002de2fd3730f47cbd7b237abc6e6f462c1866e100dd20c797cc1e57521286ddb3afe87cf8ce3ec36e3fede6235cf45c4eb821fd7dd1146d6d8daef728df8e4b242e400e852b7fd6e5e2845df51ba4a460d7e1c2726634ebbe97bad0d103b24e397027a00d319ece199b8a85098d60a654ae268aade236ea2d12f5f2cd28804cb86f4fef09b4ae868ad2e0f3678fab4830190ed0903fd833f01db83fb12aeedc11e02ed1470d5cfc4b70924f3ef1e3b334f50eeda4815690e84336782572e302242c89a50d2189f0c897d5fa28fb74a228ae30b813d1286d50f9e061be052bc2326192bc4a1436a4883d02718e1e25efe8a80c6cc775fcecd1f62b9410b4cff5dd13ace15a37eb10cd2b22779f34d3560b7c30b1d155dc135e3b6697c6aa698b4d0212916f9c46fc4917ae01246b3809eb2e501ab30d29384820a15c4a0c506a1a72bb4e24ce53259a672916caf7b40bfa0c4839223c27f46787760f0e4582f66aba9406501e04f15dad249001f890b47c6d69a11088fcc25081532269fe1bc5ebfcaeed1abae16e410eb7b9edea4b908b9328ff7f34bc8a979873718f6e70eb40f57df555081708a63ca89e73df2295ad03599d88b53bc2c8e9e19e8a5d44263ba48c045c8f376d59228e0cf549f6779613ccd03599b5704ebdd678f436707f250965af1571553224aaf80c0d112451713a9fa2f2838f5a433fe79b81f3060c980f2850e5cab8ade9514b49363221639a420d0e2acd4f1db509220a394f0e6239601ea5bd9350fa834bbd9ba1bebe2b11c52843a61186841d3d22b5e091bea95d987ccc4058d369968cfe462eace3ac6955911f0a11976250e9fdbc3e6faa6af57f3ffd368144ef358f73848fd6f20efb4917d28f8bb551e7e61c20814c9a41245f523f36cf4a34f2f515f1b30a2b531b99bc4378836c33e45346e2525b027c56f5362251a3faf55ccab9244164716d4ccc8b4716e59fa5f9788230dc8da00fdb6c236449f83e51cd13218ea7c27effe38cc67793b9acbc4638ae4628544b9de8ebf291198933bcd4037398c507adcd6612de6d8dc2a8cce52205202fda76f21851767b2427126df9726ebd0434b9438602f1a3ade89914aebe119485b48a80471237b09a1962c0b213c3c0321350bde3420649403906511c24bca715ba30c85f02c030621e0232ae620408e7366622ed961327acf24784f5152af3426b39b5e4f19d35c086200e2ccf07e6802672cd14dd747662b8f6f50b0ff3021b1649af962dd5cb4d33c3c2600068ddd0a9b905f8b04683440fddded5345cecaf6ed3798f95a1a46dc318e3ab5711595c7fd74254073fe4a6b12e9dd07e9b7d283af5ac4d2144be16ae5183f50507b37c59da04720131f2ff5122af2232426b7c30defa2d65510fa8f47a6d5f14dddad6ebb1a3d88caaaff9d9e20cef776449e883187acb3d79ecdd38ae93ef8abf257bae15eb97ab3f8712427ae1ab0dd95d7eb579156f8ce01b1d3eef83ba0ca91aa4b01fc0e8045c8d2e9eeb5eb70f3f91c2890ccc64a7bba8b8919940499d2e3db1b62059a1c1dc081f2e5b3d240e805d6bd83818fd812299577370c43341e9cd49f711f1761aa50d9159c5d10102954afb820ad3977597c206c5ce15093744c2beb582cb7a3c1b41954a705e762790c78e60f7fbc6af2b18e93f7dc37ee36388627ac04120cef317ad0b1a8052f30a9cc9c827caaeff8892f15c97830bf8acf09d26a3536f795b1cc2e9882048b0f822d5fd141fb9ce9ba1c3dc12a37412a231dc0d43b9c6c4f2f461d5da5046527c7cc73138376e9b20cb006218912e6acb3e2b7ca22ac4ce4083bf5ad022085446696fee410c4bae905694dd8aca8b1441c5f3e1123e7c16d51ade484ecc50cfbdeff24f50e3f5a8d6368ca4cd30f8bc16f59a879206f3d1f06a83964e0e1762632001e3c7b47fca105ae41479405c2d8af40a4cb4c91af709f04ac9b303dc31c3867705edad89b30c7c11d4a43d762fdb94176241a9f0ed57231f3d0efa16c2487574ca9d130287eb67e6d3e6f8565bdb0c0d44df43e1c0bb518a8ef9341a5fafba1a41779bac1cd0654ff1be3277c3aa228af50b4711c44080ecda033f23a857bd0707bf4d87a860c8916ac866a15dc3f97f8eb0c66c002ed056f06cb9cb84e1fe8d917598b4800ac033a5533e4230c5d854be1f7156c06623c6068ca30c16064b259c1bbf6f1bedc291d7b35ff83936c9103375c736215c23b06019653198b8578b6075f99945dbe8c9f5ba7e7d9c3ea1fcd2e47847fe3baa53fe300e7487ce7f23d4c3f589a2e8cc36ac6dcb10a90595cde30efaa117bdf8b5442525b9e5b7407dfc7aec4cd2c6093423c79ebeb6eb9b9d1044d149d3951387c9894c998620fcf868fc3441c1ebb224e6acbbd1ce82010a3212e5335a2da0fb85b4c6beea4210e81f2762bb5485df0d08588cb556a0ea3a7c3a9d211eb63e8c6a4ca8d364c557c791581c3edc60593926a09d56cf73c1ae1fee92a2d252d2316ee06cb3a5e4161ef295dab91512ff1c48d2d161a83827ecdc4ddf6960137f8c6f5bbbb1a6f41ce82b8f94716ee03267f14034ecf1d316f4a7bb1461e6eaacff2111cbf10059fd748821cdbbceac659a73c6c0876e008b0a001836fd860c4cc5fe9ef7e7c614fede49f6b1985d70bdf98bd104d99a51cf3cf01374ee48cb80a11eeeb698110b5c2a0a0c87a4b73388e7a556eed13648e98bcd23072bb27dd7694b3344577f07ee9290b2e4ec62d1322ca4cf0091ee2167c10fd2c0bde344fe91206793e4180467b1b86e0d32a5805bc254a6df8a1cc3b7b0af76951650627b24af71cb0df512a47b87b7a960da0d0195b61fcf10cb462754b98f93d0c1368c89d4b9aae382a5e3a110ebb0b133c1ca95e0b9b72a9414a5ff93d0e3d45d75c49eda0403eb2582e47191cf83d73d79a037d1f16612454214e1c67202c3ebe92a9b046261523bf5380cf18b279731e7510d0e215f8c459ecb224ba3575e0dbbe4f3214e34af0216a453daac63d100a9c4a0fac5987ce78c6788de51a7170cc7b4b915688fa88c2c4a88fbe041b832315a63ff1ba0a4c7f1c0eabd2f6c003390f4e3ce76f9f2de1137f801efb6f43f3a3e67cd4337b30e5be8f6085aaeda67c098081f3866ae9448238ffb79deb79e10ddc99bb591cad9552461010fa4f9e5b0ae60cb50d78d26191da6b31ae98f4a2051714a194735a74a53125c6b408c2a8cc0a5e52e51b0fc4e9108636c213fa7af17135011750a318eba568c834df544016a8f77dd5e4907a78a4430236a55c9009667b8935a0326287dab23ce65cca5185bbbe5a6d93622c41452546336e4a4712a3fd0866d0eafaad3f728b3a05dfcf6633cae62b494cf9c3daddb4fedb5a009c98ea6cf979129f5e2d9063cae6af300f0a8443fd25b414e4f5404a9a019d636f365866a0bd716794c933cf7d7293eb5b09694d7245e23ee1d314223d86abab9a2cb294310eb41137c836f60228e5d24930827106c16f82fdf1c69716e4ba0b3d692de91ed517ca5b28a6c74b74a8e0c823de0db44010e62eaed6b0fd756bedac58cdc0cce2b20fa05b2c519db6867c4574a42a41a8a9d903b3534eb62301f258c22d7128a5319d5c73cfc0b420834ea219bec8f1a6a68a0ec93a63a8e21c16c766037c208948beb268c83b9133b9af9c432c755cac7845ca47ba5425d583ed7d8ca9bffe6e04febcc5b374f02533ea6bb2d15ecf4da2f10d8c01ad4a88a7bad6daecd5a66ba4e0f13c38c61afaf3100a48af17026be56922f5362e717a893816a6d6cbd504c7fbd2c068b5a3aa5d2f2bb2bc1d1840b0d81a420399ddec97170378d18b60a0323bc60579e881932580639ce3bfb6e4cb8631716fe41803326b0e65e235cbcd513dddca8182a9046a8b5e74fef09875750eaf38bf991cb3800f3e3e611e5a3933155514d2b41ab50138ec0be376befe13a5669026edbeb216e189db794969b92e0af37e125eb51319332ab9cee6029465395ef5c2a26fb912d8f0ae4ac9b31cb265de939449ef54e4c565fbf3d7ed769f80174394688fe6a06f3c977ac5e3bdba9f222c0e67116b2b59156217901a27643d93ab3aaab02e42d1a6de7148585fc464028d58f726b21344d196aba5f18ecc1731e99f4a470494d99ecfc4566fe9df8e2f6b7d1ae10a254db2f259c27ac973b29dd51a917faeb0656d90913b0fe5d7a3c5b3002c39d0937c1f0168415f24bcd9c361ceed1785400598519bb249886f80d0c4025a8fa5800ee55cbff79c9df684c5ba46c0faae04896a8a4297dc71b2e30fe8fdc89a5a744e1caac32b915630a11f5fa857823b7f9592b2ad01a3fa8afa79c6bbd12786cad632a206797fdef9865c1d934f8fb3087099f8e9d78748654747cfdab18195056f8dd02fce06b6958d8a1c6914699797638e0dbc1bdd8371f04a55a99187ec54bc016424df0a91f4bf3b448939a5f06ca8931974d904de36be11e24b374e5b4951c311d87cfda86a52a9e1e77b3b574d27217194f9e14a0ae3c8c6cd09e56b049e6fe6400dee422af6d65d8fd055dc1237d0cc3997b0395a90b8af31a8bd48bab28f787835d64c0dd099cd9bea5d0fe1b9e4c021aa6a2cea4c1f7517781fed540809d3a468211c30aa12dd495308fc9044979c087669b4c537bf4300b970469481cf33b8322cce8280b1b621d354cdd4184d2320bb8f1f9a47d6ac7d05300ca9c7d39645018f9406afad6d370c70b5acc726b51152d2b1578ebac3028892c8347ea8a5c3d2072373b2b21167051867491ff86ff48de7a2d7ea7303d66224a497c8efde42a55242f285342e11a3b10fd9c51790c392ee5ae2ee38592564fb86f9c0e10e13b368cecdf5cb27abdba24d1c41d071c256900d7a2a7e14f35b985c385b387485c2a0ede9171436a3c6f1095b27412b9428a394372c2fcdb1672c61ad70ab11e62c8b736f3a30587e1914a6a6eaea740c45aed230b428d1d23dfd853e1a290f540988cdbc6f455b66444a28914c747d9a963041487c0187c1c1b9e54afef856430cfcc6cd09ec0296c1fd63883690a43d2c6c634d9623fa482336c86e767fc563a56e1a08348a4d4433141a744fff8b5dfb2a222e1cbead10374aa6b5c09c621c95d3d60cfc466ddb326fbefe5bd447b98cc95c15ba0f1b7f2b8f01bd45968112d9b806165adbdd9296215d6898240e71db522c05a8e45a749ad2fe33d0579363bee81cde83638e7d8393860cda0ac915e25da03fcf51280bfe12f38785e4a8f266f35fd387498185f8aac2e762f30a23f4c46904f89b38a84c2f865c4365bc6392e349514d3696c6445d300a363741df73c8d6bdb85c01da7d4aa3cd87e9c128c1da963f644bd041fa69caacb0128a4dc3f11d074a19661c104ad66614d4d9b909fe65964d14aa3a819d83b1d830266ef6d38f72ec17b48730c500ff8156baa89d3d1d9c1c2242a430dc7afb802b53c86707fa0349c76925851e9e5f0ee94a1a90d0533dd8d8b6194baa4c045e8b0b18081a2ab3974a1aaf6ed4aa60759d7d0eb1c0728abbed2ef0c1d02a49017a1079250bb69486b416e786544d1e47990bcf71af5bdf3c1e8174c6fdb813b63b285403a641aae0b07d8da190b8c9ee397cee5fd007614851d3d2b7ae9c85b459c6113ee2debc0ef7a2817210587af26f1dd61b6eef953247830c76ba0462068a9b1a61cf141b77c7bf6f759b92f2455a653eb70a327e4d53dfd5ebe63ec0e03346e42ce882a0b6095ffcb58e9d2554478265534ad461e37dfa77a819d10e96fd82a2d6e06cfce7ec9e2742f790def169297d2b802149e75a08ea9d1651882ff55bb00a4386be7784d89aeb2fe36ae12995b13939998cb8b2e74a69ee433b450c914eecb5139afcf1a4480e6105cd21973baf81d28e75a42ebf464ace214a7949fd893b526d681c2a8d6c22c2c5b34068fbd2a1a7d228c719db052de518405b313c78dbbe1614e3d7ac24939d24ce4094a7fe3a4cbad2e19fc701d7c63289a2598a934def10ffafd247eb039aa7c4658b8bdd6478527316149b5da8b2af41a5492923a5dbe1748fbd69a7a9101f06ee62c081c3aff8eb2ad87dd919853b939e5a4b58c8d4a08c8ea6caf9aff75d4f2827c88b14c3c7c56575770d8ed2660f7c62813454828b96878b43c46a5b2a27bad018abebe9fa7c77fb7893e4c9c71aa1d95a7f5ba9b9c14a5dcb785a18a41f6e801fdd2425aba8099091892af4b5beb7dff39d8ab85d89db87871e09836f78d02679bb8af70333d3230c09881777f4be66b633a55137b4d1a1cff05ed846871e0e1a27781a5eaf01d22e841e881539ef3aba434efef091d3ffb4d499ef07f57c3b6436566e27bd52df34c1c90caa1586010b939051c6a2c26bd462050f4b1f45606085082fff7bbf1480b19911af71801eafd49919b38119a581c5672e3da361e11330c23be2b506bdef6bbf265391501cd63044a3c2992ada7001d4a4efaf80f5d9df65d02b874f3f622cd888700dbdafe214fd414ece95304e58bda70d860ddef26d5fc02b1e3b1ad26538940a65a09e1e5e306904106035053d83d92981e7c10436d066d12814a324734993797932dbec9348d8a3c1d47efd08deef0f2feea8c46b5a603ae84b91c7811ebadca3703c55d973a91e81d7cb3f212d1424e0b471880a4403bb018c4a5bc3d26a4646b91948e32b0d0c176572a9dda8f2b46b4fe61aba58545bd8c85ae4c5279bd4578db77a21ae77bdcf845be816f8be65ec5534376941421b4ff45311cac7a0417fa5247086a4e334d4d5bf2ac4f9effaba24918e4e44492d77676ec21c9785e5f860cf0b2f276e767120032badedfae1284b857fabf681f4ffbb3622bcd66667bf8f71c4ea7794bbe4bdf4e6489d9066ce2a79b9268aea46e80c496cef2938e6290c4cc1b1b893e1383e01dd5b351868d9e6f8257e8903b0f4592aade8b13fe23d842ed52c3c557f1ca1bbb9c4675dd81a8925fa0dbcb10e1a823e6ad4bfbafe7b59d6886f7e44bf15a9b30ed5370fe6c2ac6cce310f07e759140d79e28dd9a1fc73030509abf274e5eda474b5f8392e9e82bc36e251913e0f5c486f6a92fcc03668e09455d78a8149d12b9b5354862a43a3de804562f953c38bca13780dc49be93c70e4c09c22ae1f9cb83afd88eb34b43a9de4d4c7998d878b8313f5433090f13539927ab982370b5e6bad30aa1f99037d650555d401a6e869da500b5acc5a3d41203ab974d94ec06c129875c2407490cba3e8c6e45654ed2f2c4bdb55a2f6059d20501c16d9751573293bd2dc067a97435feab151880643c3c588b781ed58a63666be562c1a9551dc09243db5f419222d17c086f02e1e49c79b10446080a5282b1be99a5fff411c59657d923c778add45cca198301647fdc85bfe483233b8944489ea6d238c3506309c57372c22c1f7513338238b70e138455a109c412ce9b68a85b7663b9b2681e10875717d2d24aaf2deffb7cbf04b2b629aaae7354925c595e627ecf06ff880679c4a55103f549f80102fb7df80a6fe5b964f54f9a9ef2a46a35126014edc53a21e0de85c492dac630628c8298cd5d848e369f1db4b1bf4a684ffa6dfd77ae4e97ed96a8542aa9c3e467886fa81d13be30674e51a1b3ef90c822daa1382e0b8dc3f78a7ced51320683b4dd41afe50cd611d870574ad7206b05fb7885f5e53b58737e50d33142f6cc42481e0e8a663b2e5623ab926f0ffdeb6d26f7a6cb32d53cd41780d7bfb42cc5b2e737121fde26048984b03bd1e7cc2c1c980dbc9254358bfdadddad3cf9ea908fb4e8dbe610228ef7945c0823834bb33046fe11ecbf96f0890e72ba9337b2183c32abc4f6d8fd37c4455a6fd53a926ef3b5de169b52e8054a09825eb00db573d28cf5153405886ec37588fd815492de1a119f49de8c9c8e1fa92647af5085b26e033a555c76ddd2fca82da19e2097c6202c1fa03d0bae54498809f8e34239aeccc544fc58d662c34d09770a6b13421555a711b7c09255c60f02483e150c5c32405c3af12773816800d04f17c0d61611f29da5d2a06b6c2060aa8332994c399c3907a57b7632a42fb7babfec50b3fa6f958de6a53842799e1412a52f7d2368fb2e13cc335aeef8c25906dce6bf50cbf967035906623eb24cbc913731d2a1c6bf17242a1960a8f982bb47a4b07924eba5eae212ee8311ac144d5f37c9fca0a485565e18ba1b088a10bfe691021fe624d2888992c0a1bd3faf8a4f3ec85a9a06a2ca4282881acf4d3f96c2e77538f4db8a9b61ab9d1b8001b917f540dba8b2f1d51e0f8614c7785698e8d3484004ebfbdc763921b720780124e876fba631a35ddbd2e9e8d980e24addb94374f5fa5252b018ebb6787840ae6f1a2ff6d4ea118ed902924acd82251087f5a52ce9e63954b0f7963e99f169c95a82d4901644f025fc1425673c02f648caa7de8942641ea80b92a2d45d88b0c7f2ce993f037fa58921caa5d052517f120994dee841d8e90f01a4276eaa613d98a64a68128719715e05e5546bc60c047913186f9ae15a4fe0040fa6943d015495ec08b17b2eb20c18ce3b279d06a0976f14ee2ff91f1bb36447c54553c384e5d0c82b36cb4d44fd00a1f6d19a7d852638a5a919562bd74634ed8de4d1a054835b363ad503a08f5ca1c8e660f7882edee207feb0021ba045f8f1ef542a7ff3cf744c5770ecd16544be029191376bdc2c44b5303a6acbe6c16d57c867532b4d669a9eda34085b73f6afe660502926d784997651184f153eab5cd8047b6f8b9171db0342bda4e2d7bc96bb440b5e66acf246241872a5b058a52ee5676c9c8a88fe0b201baa609e2729baf49602dfc5f69f389d6802bae66688660056944e4b3571167455490a94e6be19afa3310c3ff0f7b06145b951c5904c6f66bf491d13a6c73ec680039fb847c2ec5542b80fa4b20b49b104a0738585dc1e82d6ab3c538c192caea5f294694708a52e7a64cbdbd05631b9ccb8c916f46b725d864d1c942bc638d775e1eb5eb1603dbc85b7cb069153c4fce39115bd7041d1d45eeecc7e5df631b1e01f0b0d67b3140c09f9da91c320d13ba946ddbe1d46e13238dc47d7513a38f0350021c95dade483e69a8b950e1701c7c665af92b36fbbaad8b1cd8114880443b03a69666da42361425d951a1e604cab4c488949a8dc4162b390c3da39418b19fd839d6620047d22582462e0f67d8c53a241155828f2828248eb2b73fb8d6a329f361bef0714ae685d5d7cbd67ae4d08c1e0115828f24aef2c745252c4eb2b76b7243235ad98c15fca4c98465122d91b02980c25240f52c464cda14416978ae90a00fa8e15c55ec18e7779df81a14565ab81ec712beb1e0516461c4cfe13f57c8bd2f552ea3753f6af4294b9d8f98b236151f3a9ced565c95b12af16cb0d61142c774a441793d3d44448537653d444372b9aa01429a33169ca4bad9f0db99c3a80a856d7c08d8065e9a42554943ecc9061c1dc636657abb2dc009b0038a4070774ca45f2cbddcd1ea6b8b156fbf1171784cf6fd46fd4f934929cfacb90f1a7a0403ac011ee78e88383b4f92c561500cad97baf59bb32735c218ef2baa09295c87cf9c002825b8e95ef60799a6a947b045d32410fb75138c70e7558513d9a7b94ac13f0aa63b4fbfa05484fbe7102dddaa4bd72416cc69641217b413b46c6597d3eb72e2401ffe80f98c562d7da751fd9ab84b8b0ca6c3c53ee09ad1cf0b5a734c14a2056c901206e97376331214bbe5bdf91e5955e7974963379482a83b6b4c63c02586775c06384be876980a8233db75b3a146c8fb97f8ee1c3b657f0769f8aedd0d1e28029562fc4f1b20770c4fd028c4bfd132715a2aa8d68006b135ad7cafba801a53800718f4e7804462b125d80930f141159512f276622cf55fd592c06551c4e85a31d51b059fc0879730a70bbcf362d430f3fc20dde6be140a2c711a75b2602dbef84d25260489db5b3e6002f34743f4367f6541ed994009d660be00238ba4a22076ba994c7c1806eb383fea291bd491f31e19422998b3d4276d302801a023a203c8ce91920161bf6b0d4930b1d408075cc0750564b22a7dc1d5f9860ad12a398ae43df821e01116a7d92a421da008473b23305e1819bd03654e61041f37086018896ca59f9edeb4330ec16c95bc72cb3eecd1ccfdb6dd7e1f43d0741aaf264210dc98ea80da625ba62dba112766d5188b8949ee2890588c83c5e3e36c594491a9826e5ae7d3a350230a45a0dcca52a415e905608d03d78e5f38e06e4d7027687b921b730ad3909295a764897ad2c884f1b4a61b931ed03806388a11e2637734a3d409c8c273047af48049ab39120d4f4b943be1c342032f47df8fdce1a00fe223189958e9aaa3e406c91279bd9f727beb4ba64d757c1038f1fde43cac27174fa0ef8ba0c1351c3eda8a870ef291b6da7e6918a37f9172855640953295f6a5e1c1200a4ddfded5f06e333bc02e94f2df9016084275549429da16f08bf686b632f800073441dba5b25218a95a64044457b5669af684f7c61c5e9ad1918f2ebb758f677e95f5274faa4eb4a5589205df935ad4eb30bca5353448f46522bb4cd6b73b7c1922e986a397e281c7fcbf23ed7566c13eefc0e293b741cb724fc50afd7ca092541d29602c51e289de3611f0001413300592b845147ce0c064032a19c8a4d2a6c9930454f01f3d40cd97a4c5f03657f70f42d60223aca318881a1e396ddc79eb36a89937dc111688a6cfc83d9d9f4ab0c244b35787593bbfaf4efa1169f5a0f268744618cd054afe27b02e337a7e91fc0eb0c44d0954ea5962d2128234e2e5c705ad25bb665a8f5efd734d2742e9495dc7efc3779b899226d7099a73fb1f62af4d4316ffd697dc6b32d973686825ac7d7b9187c6352253581f3891d57bde4b4354a64f71838c7390f5acb7a911c7db23988c7f09fd4d87496e15ee74e69cb46d808b417abeeec7f75ca1c2323e73c361c082a55d38d35cf8dde8e0707b0a02c07122c19e4434f190eaaae180825d027680f1eaec4fa891582fc9f68519ef7c6e7b50acbb077f456a00a1b92603712abcf6590cf7a5ec80cfc15e172f2ba4452e13b2c3b61eb3405dafa64d71a467cbcc814b78af114024d96557ea8373f077e3e04509fa69473f086ee89d1720353fa441478a6ebacc21c86d4c11db6ea914bd235377e96caae9733cc43445a3a9eadff5f57e748f531142fc649bfeb5df867884e47be2e34af9aed43df99b00812fbae3ef04d83788720bbd7fc6f80d4ce57dfc97273c30ea4011ff600a1faa203292bcbc7f59365a8a95cce5e9f42ea41933a1ca72d8db8960f204173f51982a85bb10061b4304497b56c84c548de60688ebd651d8625860c5077cf26cf2c24ecda4e97875cec8cea592ec24076cd56d76faf382c827b6c687b7873939fbdcc036cb3f97341314740c27dd272a8a43ca6cf52abddc423749a3b10385423c7acdee2afd7a4f1a21dfa591239929a3e0a8c7d45678d40a167064946913579884b2e62f42f2fcff68fffd2cf05fcac7c41cbe85a0178c1ae667035ed4f095d14f9411230329b487b5b349c367d4cdb30f38a67277c778d4d67f42b8a4ec9b34435e86a313df3f5f30d0273bd8e4aafc5943b17010f363879ee3c94d3fc39f72c27ff53580de411c5b686a16aaaf0d3eacf61552a5380c3d2e97c1de9c0edc0fd02ff6dc2e212ba5c20cf78a4a001a39c589f1e25bc0bb506e999989b6e19c44b16f98d86179da0b9d9f91f5e36f589c8e272618b20ce4361178f5cbe15b14a8a9e8cf260194701a2658016b96160ce9858aa1313d0b1425611a94d2089a55611b4b576033ce44fa6c8767fced849076fa1d7adc48b4fc062b99d3900ed9510521abf3e9f53cbd43059249446c3c2d589130b9ebe79c423d688b94f91b59cc8c1c4faadb1a5cbbb77fbf8fac209bcdb850fee7faf44df5dd197af44a8cae568749e749b3fc32312d9ddff3f01635423f677b8ca291ee28bd4a8741249fbb9d30c5709b17297c4ae18efc0fad0f00db08392100cce618f47c2356166ba18149ad989c7d043189c18f553a89ebe312f0211f2685408fcf36cb08b03d4943328cffd7aa3aa5054240089ac038e304f4ea99388f5074472e99582611866834a951ec5c8dd683f5f0424532f0bbec7690de72f28d9b9888626ed1eb038ac79e4bf7e1b976c6b28d1934ec0f2dbda7616c0c0f12086fcc5619ed9ab59b241474a0244ea76ecd83c0cba45cba45693e6c418ba3878bafec30d0c07b00bd0db7207a12c80623d08839f60f4a390e020a93c1d9b43c3e493bb0a76142cad280ad64ac0dfc1b169878fb7a221862728e897b8bcf5327d77f70006270acba3cd14674541fcd1bb64d655ac373c4af7ae7a324b53e1fe100c51d82986e954f26959687c9d2e67f2295f788673419be50b4fda3748fbfea585dfc2643c74833a5e441847e9ed36a3e79c20fd2bf6f8e38017c503ee7407c079fed559c7c4a940af76e8f8c087cd8f8d97ce2d0fb3ff8f9be6261c3f2f86359a3626d84dcadb16a05b55f1a892285611aa3e91082898f75d873cf16831914bf35a92fd1685cd0acde5de7f1f289cc13e3d346ad212b9343060719932b8ece56014855bd20e32533299ac63ff253d4e74626b1457f2a5990236544c41f370d055a30906ab6c74cf09da6762d96057c600d199e5d9f8c37b5db67c12c3775b1688c6e48d170c4254a0b013e0ab6d8a86fe23282eac0cd1e43859763f83f045e2e8f0ff51c39dcc4ecac6f945f8afbbefc06a43158999f4935466c8ed89c2587a0157e2d26c4473ede6292322e6e11810d44cad0fb63234c6424086739ebcdaac4ceec56b4a25408a15b132b27cb03666960c0877470a136d0d0540354debadaba2156f8ce8ab2711230d85b20bc133374c361be8c6b1092805c7c19dd2089410e7e86727bad81b97bba4fb6da6439b287ade143be60c0bc1d92205afa6d3e7b7a34f87d742eb182f60b9db5ee283eb31afb1b8354dd723f5b9a7f5c2242e8c705d5b32b696e5f2812b5f9509474f48ebe5bd95a76bf4538b4351e736428e9025d0101ddf28d57496ea12e4cf1d603643a6ae3d11048acd2f56f52464bf1fb49a313396fde52fdb8abea8510663a4a2c7a1e72a3298235f41e23ccb66bfaf925f979080c73320bccfb8bfb5435b647b2cbe7fb61facb9cae926a48cc9de11cfe4fed76cbb1bd3632f4893fa480158b556facb408f77b6893188de464046ac00c9566d8ae958a6b8fa0c1b3a66453e13854c85321f389040cd367035a48855aea86695cbca1fc67ee9dab8c80c359472546d884f0a6b8a9309ce13bdb59e639d4e8efb78d3bd9cc27cf746d6eda11aa9f2e0b68782e0faa27dd31db38eb49c238de771b457c38892ed42fc6c1b4d163619f60e671ce65f7cc323b3072f8d68db1a9e5ba61e42a3133c8e41aaf4d8d714e2d74b1dc8f901bf367d4b3b74a9f0042bcd42658c3a596e8a900f61ee10294a625ca1728a94273ac3faf8f261bcf65d3ef7dadded34b246624b82a4579d41fcd6f898388de0e51ee075143e2eb00519811718306cff5d57565c78909c58819360fa8b4611ea61c1171e1558206c1e4e77731a143cf0bc1302c9d1f2beadb1e2b66ea2a45e812a8aef13c9ed5a55a24521249786ef0ee1e074c91dc037a25fec35d6c92451e5aaebb2fc3341062899666ca31968e7c6c9094d72ba9829e0cabbab17324ee416cd852c5405909abfec57375f8c36c5eff2d511ee222a7b4438ed249f348a68b5560339d1ebd4eed9088010b9120b2e9bf8ff2534d166daac503e95828e45c1d5a7c57026ddd122693d749d7224a9adbc0eda0c321574a5c20b17ef5bad6a7c292314b4f413323bcfcd15d4c3f014989bad19d368020dd9b02eb83f5975c68a17612d9f9af8fcc07d6ac1b555e32ab1bd450b02fddfde09c94d26f6de82030cbf2c2c0b5825dca086b48daf0f234e5c4a465fdb071b99808127426142cc1c11a1b5a565945a389855d07e98b1e553acbd8d1c29b346835c440731024d4c6c64990ab4402814beb87d59e75102086217088f1d23cde8d549cb480998436acc40968255722f2c5dc535be8d113ae621ff1bb1a8b13576d09314845ec528d98f0557c5f814898d6f2ecb238b3009f7181ed9cae1d5e15178ab2c78f04c17b88a9ff7ef794558c63370ca7865b91a93b8a60ab259cf2792211ba4f6acce88e73dbc820eed992da8d365d004f35f2e8660701821be38c107c5493b16fca105a15ac217959b39b83793578046e30ee6514d0971ee35d832fc38e96aebef7e1967f9b3553df71e230dbe259160f51cdc9a0c55c60f63b4ca39391d6fa9901b535d20837c6de112023b8c7121b5635c8d8b5ec556292bc7a2b85c8e69a1192bc4c3f705862b416530b8149c63ffed98fc8dff876fa21526450901bafe1b588f2e80d5022a7b99b31b4b0fead3fa32a70b601cb2eaf3b205a9fcc48cf6adcb370a20f88cc016741338aaa98e9d001142ac0ee31f568b8f9268699f64313b22cb4f7c7794f9f55595d863399710c8f68400dc4c4f4d18ae0505ae9524608e1893370e4eb2a5c74cff7729faf308c1e06d1180de3d48b982a38d43b8bfdc17ecd4c7349a329b7e64e7b29853f48551ebbe43c869274804bb2c6529d6966f106a2e1606a5c303eee0a47bfa3aaaccdc1cbd0f3af68b14b19f4189946ded51cc35f191a546d82cd09fd1b7236d8598088a5d663efc226166e76688fe2e60a1de7c108f07332c1ef84ea3e0406c2f3ff8399c9dc6ed681856a48c15459a4732e8221011e5fbdc608ce420fb3c4ea8f87779c2e8dbe4bfb31f72f6e1ec75aa410746e01c1065c13b6545c540bd87cc3527f1726ae10ccab059ee9829c7bb90da6ec3d6b2ca8a2a52915d2af6c932f5243d93d6867740b289e480641120d7a0df971017d5a6e74695c6e2dc515c70d1e0db18d4bbe1958e5b627a6592f5f88bdd124f35628b3825d26ad30c058746748d870a6a9442fdfb7b29520a2626bf89a4257f83df77b9a5dac980ff0b81f450a3340aa7f484a87a949e5ee6c14321e0f1f533bfd759e4ed3f7962a653a811439460ba243e353caa77262979d0a3019e105ec7ed053112351917246045521c8e408bb766310240afe616cc6769c9b7180ee194b0f8cd5046a6895faded2c84187f8fcbefa9e53d88594bef08173593a8b24d62715f54621b56e9c5de54c4a87ca48cda10c13154a9682d40702c104f55890ab034d14ffbdd6017ddc4c2bea3d4a03803930cc1c157e9a959b3dd9898c77efb09f4f20661013ef1b1c7f4a033a004ea2043078e0218cd9e60623f7584a8302eb546653a3ae78e1bf6c8917878d66ce75033caf18bf0891d5f4fecc1a69d6e55fe1641132989dc91e6c095ce93d9a527f34cccab22b82716bebc09740e51ed84ccaef1c122e9f970eae1a94f769f46b342befa5e7424307605205919ea90e1f7a0dbf3a4d28e65f1d3ca04c957129c64e3b6420463c8d4dc6800c830d3bcb963ed535a30610c7a71f7c51687c0f092576598d8931004b44f01063aa77c0c1f33f75ebb6307ea59b1727877e8c104ea33a29cf164b4b5a0205d56907bf6db7f5ee45fcdd79d2f15eeceee2db3e458841ab122a049578e6c31c69b35a350db543d39d75af890427e30914f3d5c540da34dfb6393218fd76203f2c614491764932ec90a18c138b2670258320479bb0d1cd606e345532564ae953ecef89e7796706ba1a4549be570cce8f6c281a0805e570c74df88cc77146790cc9f709767b52329c7dfc27ddc31bf52f1524c3dd8db6f99dc02f08e6300403c7ae1d2246ed46d35f4e60bbe8887531772f4e4c3f953add4f1a6c0d8a3295a300912a36b00ad048981b0f4b6e2248d445d5e2fdffc0006f490f078eff3103d1b22dae8fd4d787369404c812bdc1ae8bb4f12159d87825cf56dbd22b5848559e1412804c2c1f84d5eec48d57cddf3ea3daae01538810f86995be2b22cfa180274e97d3910d05845c8ed00b2e2646d24538c94d6b57ac1142927f25ee54ddd823d2bbf2a0c1e35b4b485c85fe4bf0e6ab2438084cbe38cf3e5892ab8f4b42e0d83ca9996de012fc136086710949bd892e3ff20f4b2fec76c1b26fdbf3e8529ff53fa6f584133fe492df2a685058b2f12302d6690cbe3c23857c4e407d85e47f9e66c2ac231be138298b39710847e9ca338f5c57ced675f4d7a0891842bbb8c2a7b3d534b01093ab88562d684a6242417f3b17db7b0d9dfbc3559a514aaddf78455d741900249d7738a7b12a5395f3055e01c0cf7c1f9218747092f0ff753b03cad2f6a2358a7dde355948485d20f011f93ca34154a93a6e2f1e79a7a4f976d1ad1b84318f49ff4b4ee383342456ff302cb26e58e70a14acd3913f30b6d3124903565e9577133140f09893e952374c3ad13c5b49e15ea1edd8a58ce7fa4ba4d0acb9a5d2f5784bd1bcafddb4a311ffd6cc322ea93eb86f7e19c4432bea86161598280cda6c8e61965b5d53b7789525142a447570aa874a20ac0a04b67f9c222cde5418716484871559d00b0bc1296d25159f20cd1216fe0558c8a5cbc2ca91944431c4fc11e364479fb1480d6a3c4d5d5338832355283efd365433ebef58a3414931815219ffbb4107c8817b8c553648a31facb61863c3233df7d8b83546d6cdfcbc167b30114ba4436ec645606a0266c556225f2d07dccbf2398045a6bf1c45f79410ad0cb4ce7c5841962aa00bb797c60acca554687b80799153bfdb51f781f0df387fc0bc7cdb31e507d40aea310ffc08de8673cf0c0664b2d20c8dc1a1e715d0ff4f052d0d7e0be4a3aa2218f982bb1bfd4510a74a4f95dd744230d2ca617382a0a24627099f39c08d30f68c5d8309c8ba2804ff2240ad35eab9316b104953ef3963ac8c7bc9988cabd4e4b21040a181f1506706f26b9ccaaa4d41e63461849c494283d1e3396ade895aef2abba39dd3f154376fa613064361c94dc0435ad0b374395db17635bd4af95c110189f4425ec67658d59ed9fc4362cf8000f8759679689d6093514a606a812ab55a67300153ba6d37ec51c282072f1fc07c88398878c8ab3ee1e1653ef34893df1a419291362714e09951e83dd9e989f321e002e0efe044d587a665dd031e9cfe219a4efc051df94c1acb29022708c6504d89395e8cb5e14e40166e5a8e09298bee70664605e34fbee761b78e68bd939e2338955fa92085fd53de16cc22b3d8a1be709b38775108b183aa4963b0f0888600e1575f0575c42a835eed62227ce8d6db6a5f876f5a8d7c4c5ff96529a3ecd0c666613c61bdb0b6e230a716c712fc48038d99af22e3f31c110af2a84345058715d92222a5a5feae614196cc0d106de9e5c3902b3e6df150681022d8a6e827bc0cf76138376f79d358fc78a25563163239b24524aad1e595e3466534caedb154a7c8595f650eb4f355ec95d541e7383a68da882265497711c078226d8fec9a30fdd921946b8d2c60927ebf208fc63902f0ca85e72f8415ffbbaa96d4e6fb56988114a42b5a03173c963f8e34f4574830172efc258315e7d7ba92287e32d79250466471434aa322018a31e4c4f157159a34cba1e8d2ad19e82bc3b011161d95a8959da3daa9f46f2efae1129da985560644c77b5c04de98a730cef611b66ea4f8ba222722a00191b0790368a942cf73a21d4cf26f837451c013734555a84b72deaae38744a2b22405026a2d0b677aeacfef2e407a1ea07282a6f6c3e4ebe6abaf342eba52a442e342d38ecbf405cca44ce7cedd0f3af307749cb7e95dbff4c9261457e0d9c686c5f46f5fe8fe384b2d8c17a09991892b86b7494cdadcb72e78afe44e1b862d0bb8dcaa4534eab540da915518566da86e0caec121d056a0f1db1bf3acaddc9041aa768446f97043d01598d26df356c10063e30a9bf6a8a21589408ddef932809bc2cf8e8cf03db5e1c8118223bf02ac0125e0204f4f5a7cb185c8eb8f39637652265ed8be8c4007f78503f5e8ead8b9e9e0e7db02c35ca378086ee076af0492c708db609924a80e59580000a368cf888cdd38d895c0106de2c24daea3f5229936c404d3e30726f18b1caf69836146b1106b6fe428700aff6a4a06276098aeeff7e5416e8fc62b621769b47682fd37d352e72008ad95d13d6826e135d468cef1deb67fc6277909e2da0ba8f0a509a01f8857596facb6c1984ce38abc812e43c710d6d187594cf677ce40368af523822f2742c61c18c62d4819727aca2f5f1295ee4de7052fe7458db5e7a5994c9a41cf3578bd65dd63d4a280495d8221acb89e3ae0a8b315d90360631b3a759007186ea47ab328e592a70e50908553b343543de48d8ba881e0c1c7a681129a51772e6a8ed995a9bf7d60936b3fadfc8872b23dcbca4b57ad260aad8e5563a222e89b22463238f82f65ff317d420b362184b01cb5eef28a6e675145929b819c046bc7172a1a2658270774d7e8d06aa1a2b189f8a0683f9e4ca99a41ad94237b9bc7850ab29d5d64d89c9085ccd889294ec3b1d451b505a21b556ed946e4929d39cdbbe895ea2bd8dd38aae85b3c3fef9cea2ad2a2a8b729547168127c2b3b42a0c895044271607d110f4fa4e7ff52b79b206db45289e2bd21cb9204faedaa0e207175d165cf4a2441011a081180b78d1f2decc997e70144a602eff2c1bd0b6ad0135a30d55ada2b08e286800ac096c9d9838083fa2b41c0361a4ac0e069a9e46e4594a4bfae27acf7291afb32e40089fea1eadd748f817b40ad6dda229c3ec78d0696f780fda7dfb81ad7c9b1ce4683660a7db797eea7a00a1874aeef1a47c4e3f6bb74814aeb95c144815fb1edb767c5d484e4b50f3af23715868c2cb3bed0317cc27ca7143c3065e428d06933b4409fe8394f7344314f5c566aa8b55dc6823a4812d03bca708537b8bc9392ee0861cacfb8585fed01a595f619c70c080381c30034f327f75141c597ad7bd2410e2b760fa582ae820b1336ceebaf7958a57151718d57922d5df4b9264db174aaba4af2d6716449e83023e08730a66bb2e60faa7bd3c185b0ac66edd19494ae508ce3add8a944431bee1d6804f12e431b27f1725b45498fe7d834a5b47ce295e3053a1e798385799b6646ad7be246c433024b13722b7b0ffe2d1819ac408734d8e4bc780f6b0931298693363c99e68cfdad196cb56e21c4d90e0207f19701b24422d9a35742086c71d83f76c55ac016d14983a0d66c0f38b55f7e0eb3146b36c0e244396dae4bbc426b42e4052dd625d10691c1df3d645a01347330cea9da0ae47cd3d2ec79b6ef016ac1250665c0bc024f6270015a16ddb317b0ce4efabdac72332fa20c5ee1cbdb234834ff63e7f291cd75b0047b0b3d21849f1508e823e1cc36a5a771beff6f41a048affa0b1292b13308c98686b105f33ef4467a547e6b40c5e91f1e19650e9c4095373b6075118e5187494ad8cb0aa8b652578618a2cd15172314183d39c7ea3693accf65d60cd485750bbe2390b2a41bd7935f219781bb1ae312e332dc5a9551770ef3da9cc18fea40262e75c615c81478091d3d230fa78c60878c65edf35be60e32097724629e75b63fcc586a9a31c3e1d7ae733e6af452c668d1984a647add56ce19f8ceafea522f38d182dadd41c2418a82198dd36bb39c955e1df1edd34c128e6dd64482d42fedc86f092677c9a496268f6bf083f2606d04f95fabc2a9fadd5f8d1373fb75f0b5c853f23187254439c1af7f65826853a1f708a2cde091595545b5c0ecd29366ccaf67122c706ed6815d6f7204b3499c5a7fae7f12a208d46ccffeed6b839ada70682f419b7041890326122772018325dfe81ad88327eef05415d8d9aee5c79d6f072341335f192407e8aa28b2f07f7df247a6ea55578e682059b21dfc10ff9a32a656666da68660841d078563b0991af95a1ac43315d7058e306a2a30b5b453d13aeb96501356cef3b6961daf6480ccda6086abc98fa214efb568a6fa9908d7d100cd8549552194665d19688794a020069e9d1a1af2773dc8b616d7f6dcb23d3e9e16398e629d8e44408efed9ae18b72806458a61a4ca1a79b517cc676353d67faaa99f8e13a2697ea8bc4873f555eff101f48292b15611207b55ed6cf9882001fb8f45b0fc4da901e9a50241f860ca87047f8c41296b7a6ab7be206841c60a8a8d704985ec23be7bb812c33d65a894f650252498aa6b349d1d6fefeab661631ca5ff6819a32e50ab4ff81e08c63ff59876096c40423fe676dd7ea018c42658f055b0e7e131ee4a99aa05d02737505e9ac5583c7be063693744cb41bcb903ed4c9a5202a9761481b0101d0d5731e2c4131a6686105241057b44d8207ba1617c1ed41fa1ec5b4c357e9d7aa6767e0300b63466277fce2aa299aab550b32cb3892056717c000b3397eef1b2f4d7a655a7dfa1267a34b4f5c67386ddd613872c4632119aa4b0e0bfe4a949b58ce38d7b7d5f8f9574ab07175da1df8287b72286178dae578145304c210167bcbd64aafa6d9d4f01d6580feaa77dd66721d408bed1f770b04b560614ead1ec5accec125c78e1dbc1cc264e6bbc4f2b2cd05d9ab92ae8df35b53f565018f05bff7cd8bfd44e3e564b37f5dbe4b9d880ff2a2af8fdc8e2ab9e31fdd24a7b91672fd251a7d615340114dbcd5d213462147d425ba1d45bed0baa531570fd63ccb1526a84dd93ef37b28a76bdc0a55a1b40837337708dd7aa67101b38009623685a2ea30ebe65638415024537b8f6411354cb774a6ddc152d4f9bf08760772d6f82436594ffde49ddd020b381d441deaae7b340512f62cc14f17273e78cd756cc6deec2a956bfd10685d2bcb7b867e6684018a2abf82841a1eb052fcc4572df46dd474b5af72020ec6ed003a251f42eea7ed09cbda5f6e03de3847d108630fbfcd523dbe254cf60024c20bdd621f9ef752309c0c2937bd8471fbeb31cc128fdefc9a5e123158e2932dde031a7d8b3269836ea02d4f09d0cabbc752af5260ca8317d453d8c9bb3789db69a34e1f070ac4263ee9a53553c44696bd91fb595296197c2c80e8f63c1ad24b34374adccae4f79bee962e46099b14606d7ea98db23fc0f6d19b0ecd6d0a622936cf9ebbdc434b2f66ace6c8737a1af15a10ddd9dbca46904bac021a6106706cd0416e3f1203c1fa40e3195fa4da198c345f23d56d031807defb50ecc96b255cd25c0a8ff1086211eb33f1cf90c2c3ba6bfbd90e855c10d0242ecfd509cb49f971bcb8a00c28d2c7bd1f988e2ce9ebb74b4b355360b5008131a6cbbe12ff019ee30b4d5a5ee0427041e596555ea7704b86199819a627414ef0507f92454424853f5afab87042d735ac15c276feb9d38514e3d736128004ada955ca067a2eced1aa8b990e952f01ee88f03305f0826cbd1cfc40cc8d6d25dcb2bf4f980fd2a05b7132c7e46c9209911d8fa4a183e32c4e944a7146a29b7cc8241d4a8c13f326451210bb28bdb83304ada288ef0db846365e3dc2bf1739c4e993234fe6b21eab5290f0da895fa7350b1d327bbb67be456c025afefb3dffac3d135ffab55feab09b46710c5c9348468cd3b93224815de79cfff281ef062f045540bb2a3c381525da0fdf48303318357ff16cb8106945027421f90f293d4066a05f9ba1c9625f1a928c052dceea3a440f4299fe26018a1d8143e35da868e572b1000548bb72c2b148a5e60c9a34724041350e9be2c146bfa15eb0508e9c6b3e940c7a858333eaaf4ad0088d1fb148abe36055f0fd89a2b2005f938a3d2b70d775a35057db6462bf74d491e46bf1de7ce33a9790572af0f148caeeb841c48678a1590d36fe0ea40d129c6003e5f345e2cfd0fbb927b54ebe4c0fa70b846c760ac90faf87f0c3f2620c9484863595e3e4c434c54fa49ea6f0fabb62995223943b9f66acffc092e430c07be273f10503024c6299c409a4511cc07861441938a69e02f11dcd08aca566e9e351ad2d1e0f4694b63a8b8dfd0006762ce141853900ddb1aee2975f2d400074fedb4625f023dd756442ac088a1d2895e004d12082a6dc6789690435887ebceb74da86b198cf0a6f019b7389cca9616cb8883662e4564561384f362c1f3792835c3c38b3aa35a9f7ab5eed510e2f06e561d3b937dcea4b5a85c2b0373289d742676aa32a421301a1c80643e68865c02c2bb0013b6b19d914c1a17a2c91a1a916ffdf7ece1096531e7fc83ed4bf20e34b109b3458239b173063edcc7d61119208093f5ed8125d1bb8642a12f0ba1914b783a8b25012d434171b03eacd1c527421b01d9599438654961d119d211873c93293f04683e53224198a045cf287203b4eb18de4690ff8faef85d22ea54b49553749182023e4f3f7d30490a0ab19f1b003233996d243d5808a4672c3de8d3d5f35c877635ada3d23f5c2d4428ede9c9b8111f2366d4608c125e4baa7958bec4de8d7cf206a4f27651b23eecf9b9e62d8b3624cd74a2246c9527f12da85fadb2b08cf44aa78e15ba2524563258bfede329d3eb26dbc8564e9625b99306e3203eb583ef53792a45db09d6bdff660c2beb03338d7118aa063c03fa046d99fdb686ab3b21f01a2dfee3aab8245a3f9cb9336466ff176ed2b60f26d4b8ec6ea8285a0665be0f20a4db33305edb9fc22d6a78b56b0151deb719e42524dd819f7b332a74382ad63bc6cab69d260125cd9b8a426383768ecd88d180fb9210a921e17dfc93908e0db90d395fd04d870ed932a297c3e3b7a4188bc43e0419558e41f85797fbda6bb77e39b45097fd0fc16968ef8ae522e1a7264dac444a2bd0f4d4ec22db106d828344d37d1565a1fad795a36087267b390d3b7753465666e7eb91ec7afaf048682e17fd89d25c712d518f8d43e84cef34cf632629fa2cef7bf9a43c8fcce4bb631607b67215ed2f59c6e9ab305dd8cf58a980bc23d4b8a0022f1025a086cb8d9222332139f24b40a79a0df278d70e38720a1c5fd8c0e2fe204b6ba7b9c88c34b4d163763b439ed7a404ca1d09565e3fbde52010e8852d11f0eb7d47812d8c9f80c884aa240103eea78213a849edb1a4bed4d3194b929e9ea7011c4791776d7bad1b4a16375e1f4360e0cd5243d60d6a0d2a7c5abb38aaea0a4919d18135733a3ceea1dba37349d09d706f548929d34012f322d56fdf078d4029769dd71448c1dc43564051dfb94f52a843247bd320bded956caf554dfd85fdc991989d12a53bc1eb81468da0e5a07e089b8b09c8cf212415ec292898f7275acc2ccbb219047178557048d890ba85362df2aeee83e74de3b9b2f54ae8bef732365743fd4f5e150e5729446501d5c84538dac530d60c34fbf36b03f8465a2bfc1609c251465fead4ae71a0628c4bf401805efca8c32262c9b0c851b3a0d39bd197610857403b4bd7b6af10e43d586716e70487cabddee304ea3e06ec5f15dc544ce978d55202c563aff6027ad16ac1660856dea9a91b079e18b3c1def01556243b89f38e9aa61e67d39375463e6d23858e7c31e73ef2678272c6d8d029d4522fd1197916058cd41e73668236b3e0abe2300f19d2cc7489056b57c45894e6aa2432be00690100eeb24b9f047db98191e67138f83b635f8bedc2c5b2794c571cc65d6c4d20c46064dfd29595804aa7bb2041469aacc34baa4b1e240b203123d11b41d4fdcf726fff5e88f807b67e88939f11104cd507bb3dbd0e335ea1fb6c206173afc214c03c88f5db525295f8558742501ea252ff895fc5d12db02809d0322f47a868fdedb39449537bdcd6089a835bf48e384c388564c7b325e6b0c20329b3d361340e18aa12c56bbeadd3c5f1c243c9cb3df5778a22a6c9ab55189860a535c085790c6ce5a9c9a7af05b5f802bd04e71c016250c3c350c3fd0078fe3157c03495cbbc65cfdc62b6bac8b35deb2f0cad67d2a4d604a4d55b67e2f0dedfd7fb30980ae7b185067d1fd04ca80ad38fe066745e09151f97a31b418d1c377c4682e6e4926b4902d9c74c0d4d4c98a3f47a89ef86c89f8cac2e259e79431333023568220ad3645cbf8b470a6af254a00125669d66e718811c7ac785e1d8ad8a9928fa43bcd4719095ef7cc26c860d88f0b034793e8d776b775c930f1c84a4f279cc1d7263c0c94ec4c909098c546e131df07e36870dfbe73dfe5b3518a57568c0a2991cd56cc742dd213ade0bf4caf2b12419cf71abb861bb5c4a9fa915b0cc0e7a25cb340396bb4d2cd60d1dc1f3d54ee46cdd13cccc18a03db72c33311ca03cf9769169bfe27537355fc8db6c379be26d1522d439f796669996262a508bf601be8e20e7a74f129a603f5545189b0164e4563f5bcf97831f93122db697506dba799107d69b21719a2a3460110c468fce7930839294b3d7268fe63de89128243e35faa6e78c73d4c17f0a29c1c137f2f39cd5eeb0cb76498532d2787f746112d55f7019bd06c66602b18403534b32bab2a7600881d25026e1cf711eeda544c78f962e39cc3e86c1366fc6c2ce5d887105e1c92eca6d5ecc42296dc97c332fb0ec7145463a2183dd8654e875ca4ffbca284e0953c0870e9d3072c3e7a897403e8792646be44f1534aa77ade41eb96e9d5d8dac97628255b869335223dc46ae507021a141ac1e323fda5234f3a9a9313b501fda6e607990846aea34e69e5202481b88420833aeb35fd58f3f43bfc9564190d59c05389d90e4530960b6c83ec0ba54df1cc62b784c6424e946672e3c12d7f5dc6673ebcbc90da85d8d74c9076e4dcf0fd7e1c5906b15405c799c4e00702a6541ada2414ce1dfca6ac0a682c7209744ad60744d5d3270fabe412051310a9ff89f9a133b004d9d116404a195b2d06cc541a23a4bfbd8aa93e2c593cc28c66d959d4b94380bd09899f944d16ece81ef5a708dcd290e6c4db7148141e507684aabf4f12f792dd639d1571c1f4eab50b6b277a2892779df1a9e30fbedbce0bdfd2f13694d0751a452d8146d43fa6be177ecb7ea7f0efb338b8789fde96bf050983ecce49976e42264b3710336ff85833549c253fa58e1e82110dad4664b9ce5f2fe0fbcba28fe7b6128757d298154c81174d19b29a2c1edc4468e5e25b56f8a6340ec41840e2bc2491387cfb18d889beadb9c40cd0c3a0e8f29e580e7e7086b57052fe639519053f2017c5c3ff4c51677d9ed8f209a639511b11dc7ba4ce75ccabb0f0a33e30371947704418898d88469bb1d462a898466513a41592b84d66244ca425162bf475dc06fe5a64de6074674bd62256b82709aea1080b82f2b85901d1762c9d2eb0fdbbecbe43a6048643b1f0ed65dc511e33d2b7d67562bf32dd7c991530a44b8a03f85993ebd4ac1a32c98e64fe30784dcf078332df842cb558bd677619250dd76efcc012ed493ac303056e655d027aa40f00718ea0183e64c1001493518f31cce6a7c76b90113ca62f5187197219fa0748ebd125644410666ce70d0ae0221af3db1110d217e3a9bec92a998bb511641d4a3299bdacdc9751184a7f67ff014739171ecd315f0db80100d92ab08b6224e0efd08d2b508e385621b859f0ee0cbf44ae2630dbbecd260829b8165ada8a120d0b5a7503cd26d29e3930e00cf2264a1f24bb7038535c001abee20c87b2b49a5b201eb8a2a5a6f297161b8555de6516297b4c3d66da473f544662289cf1551152268f912c1c9c5c2ee419e15f83eac8e526cdf3f3b8cf12b0a2d1a99d1091e33ee5b7928bdcb35d7db1c4a3714c222202eec32494a2151b96ebad066a0e18bd1137bcfb157d5a42b108ec3bd88cbe0bac4772a4163bc633fa35146ea996d2a107ca3c56febb4d1fc8b326b7ce081716cb0125fad15b5eddd8ee09bc17bc5b1e8d4acbab5501984ccd6c2e29f5ffce5e15c5b5f927728ff9927a04dd6d38f1382bf2f4a2dd9093389b5fbae5501cd12bb4a8d2caf18a625a19ab872091052651eec5fdcf2c1ef675cdd29ecbcefc4232e1dbc77719d826c391d2e4e238bcc2d15d6fd8fde830ade9c82ecfc242042c3948d844f467cc25f26b35ef27bf098c716514fd2e9f9a3ce46743065a11ae3c66fab00d4b452a8850f204f8d9f654eca932822a66b35a23d676a70d9b78685f56d11f6ae62911c217a1b39bb03835d030c2dc675cebb43b9d82f7c8542102ea05e9935acfa19065ea00f2a35a3096f0771c647a20cb14b92d7475bcd670cb4fa5f7cfddfedb25c9d4ad2537376cca37cc6506d2aa28b15c8b79a06e6bf6c6021c0f2793749618184c68b2abc63f467518a956e5116ea452873373dc2c53141bbd4c044e2b0403584b2a7aa2330401262ef25c20637dc9ba32d2c66b840d392a8a55b7c34719bb2a52d13ebf8ffb9362253734e148250b518865aa76f5526c2f635f52c5df8f922140cec21bc75ec9a1a638b10ea109a98319d388472cdea01e9e09a0ef30a7681ff9e76174d5eee00947a034c859eacaf2611ca57f7d1715ef02a5d79f8546b8895e3e2537446ed832ac999ac240f2d7b002c4d91741d0b72d8628eec47c8dc8116a22d76b0a9aa9fa9e61a0a4d850d7921885dabe0e0d71a84cfa9b8030e280a404fa3ad84cc1e045ee1b558b27f6ebff87025885b44703b77e740f568f4934ef884ce59c3ef3801715b1ee243b3a30f800951dd1b30582c91cf22ee59d7f849575f3f8128f49980fe5c6ca03f177cbd0faf2d1e6172cf93504321b93b7f5c50aa3c157bd8119d1b64fa6a25115e4dc9400e38346b218aaf7df05be2898b5d1b01c42487a1c0a84020d14f5dd71df006ba046b2a07f2515f0286bdba40ca6563751e4d32b458c6ba3af6320014a01376a1cd078a224ca8f8e6a51fc256379a2ae89a4d902a169076f5b728ee11d3ff3eac96e606737e003af8376d3a1348384c24591f456859ad235f869e285b032edcac0a93036ce8b0e17f23863591d52767b59d31a91daa205d605922147fddbf01ae711609aa1058369914ee66efbb8867a23500c36713bdaf0ae8482a5c2c779f75406b98f6d257cb93796df3b17576dcbe8c48111730c00f75529a22a2c4427f39c478f501a52b1a8e0c7cb664dfd080202d3e86172a178f351b92612c9b1dade46eb16555a807bec8e07466d004c254b6044dfd4aeb888cc00242e068af9dbae20dc6ea015cb3031a0c4faa5420ca703a1d0f066d54822641171946b47507ad029e8549a1779794812c50852ca99acb720ead9779568bf57d1969db560102c1ba6c6778afbb9377a3a6b94796bfdbd16f534c23af7d662ca631369bec2da59432d10b950a260a67e268f579abd13667238f94324a82ded4ecc081c6250b180554a07d9912c7ce9a160de07213c3c40c15356dc8e4c061d482511bcbe5a36d84a96266d1239ca1bf336f80dc5961b492864593734c1824670e23725ec8b933e60b961d179035287e5c619b93858c8e0be3436d8d991b993232753d277b2e1f4489d462c7eef48d0f81cc193a4ba69cd181c366dc29264c0ba02653f4c46d41e943e74cd52fd9884031e5a8e34e8d3a6d3a0c983c20c8f384edcf9d307aaeec0de646482d851d2963415274b903060c99ded740a608731664859b1c3960d28476b977e74967858d3f7ce00411e285035ef0b429a2a3870a2970a51130b32b695174d47003223423248b8fb0e3169af30b98261bb820b3e7c899b3326f6c987480094294f315a73e72e8a009a991c46b878189a156a00fdd16252fb0e6dc947149f1e2c5103a491a6f7780fa9f1542d8e8ec1892e64a9913230f2f5668b0b945d2e0de8c20e264c699990933616b88c4f479d325c5598d17d98b172408a0fd109312034e720616377e946cb5f08af1d6a30b1c16f4ce0c2371a80439b224cc4f804f1c254fae80fdb972751cb824683ffa68c12386c4051ca413a61ae017da952326b214d98c7181ebe12b6185860f2b57c6885129b16091e265a2cfdb9c3aa71b1c0a23117169d0bcd85263cccec50257050f5814b02957ce2d5f9003d00a019136b917466a941f941d19a10f9614639c531bbfb829594471c2a58a9f2e2562ac8cd7b831be53250f1f0f215c68c489ed68936cfb42a65742851cb12a78ea548ce0f38dd94fe5d5d8110f0d167d6c40394fb62f6b74c00811c10b6c479b3c3605cd12442b1b8373e44e193579ad0950928a392776a8a0e2048d39e7c879f3171e4f3152746dd941064b912cade54b0b366408b2b68ad869c36546af38c00834bac6ca68e99344ed8b8920ab3178c6e008b91167c71d93b6ea9dca73fe482983c1a298e6069767dec010c9216913a6088e224729695cd8d1c60f3f615b84acb870da98c83509b22699cc02828d588f1368289aecd1f101b7b6ba275933fef031731343058d2a7dc878dd13cb1b96a122712edad2bcf0c0b5c84031690b82b6bedb6dec1b89c58f65aab7254651bb3c965d514edcb053745b63002d6865c0a29c719171654b99982e30b51e492278bc2c515387434a1d3c7e8e6f47be889b58113131b5262bce9c801b4a6a44c92ac2f0d2870b08f0a0f1f2c787193569e69508c01102074e599b0c3c416251cce4bc65ed5961a508122216173b3a4c10f9020357e54d849e05548ce122244e93b433baf266b1371526501a13695b4e64d1a205803a798e7ac04cf8c8a272d3fc89c3626dcf0d185f8e5c294e92106961e7c8b14c9a973b441f5eecf831c2973e2c44c045bcd009456d736f0ddcc94a63e30c189b277beb7dc48b170ec2dc58e375e5448b1545f6856bd3426b09111866c6e4285e8c4745ce529881e51102a3d7a2ef5e1ac111a7656e04639f2c53bc8cf9ba077ae6a4e1a14336f10196b66affddc965ae1730a0173ae4f068b105ec461c1b7148f14b0996231e3374a4906950d303ce8f1b376436a68c5cce8e56a2dcf98ae225c70e8325696d578824cdc2f6ce6135d6b8a801e3ed09dd69f4c2c5c34093b4197bb0b8e499c2be365964689d9961a37342f1b26217813d7accbc49c216e48d9a9e7befedf3c571114f649da9ed506bab01b7697831718659c2b676878e4e97277b977befbd77f9e7ce4f505ecc520706ac5029feb0012589579e33016b46306df6801dcf7a6418730c630676747b73f7f4482142972e5374903d71cb20059b1e4474d0b0c18664136b74e9a1c6426b499732275dea5089bdbd986326e4459cd007073245d4e2d0a859b1e54473737eb071078e971b347386e023565b8ada582e1cc35cc4c9012519e79e7539f34a82e78b2e0cd2471d76efbd371b11a58b4c15a4e8e27173b163abc7091b74c6ce265dc2ca0b770647943a4edc8cd129cbb20cc012a310375d61c60c29cbb32ccb3213e965a9d4cbf2875aaac40f3334803cd13337025b96393f9af7a7bebcfb7dee76bbfd278a1d368b74c132a7b059c46c4a571694ae1f778371618394abdc989b526ceb3aa326888c4f1d8d21658aa4d5ad38999c5951c442c48893166390a83f3af54ee9342ba420eb9898d97364ec9ac105f9a48800c6e46c4e51ac82595a762dcce5b23b95bb1f39f3b8652746069f3051b4ba80bd403291f77091325486658db9a121c7c9123ff74f133b695486d040c1b664b780a3e38b9626264608a2828b8e2224cba58f16b03d47b8214a6a98178d961c687ce4b161460a05f2ef8d02197929b871222ceecc9326713a49df7bcf98d68f3b7de8c8b68890b637ed67e7cee1eb564742df7befbdcf32c80cda05019d69bad37def9a382e54d83921054ddc59e6363774f3f4908b92f6c28e9c1686aff483aa9b90b0ce683a373140267611a5ce45dc1958925387b96d6991824d0f9935b43626ecde7bef175e0bf93fb379dfa9b2cc453bb272dd824ce1f28a1a1c2cf6d829cad8a3857708dc6ca46249312d59929405413285b5fca53127038f092878e430b91acbadabb420cc1e2763fcc88d90dc4776e8f4093715bdfdc3a85dee3628cec28870a933f346242285c79126469a90c9d248f6f088347c8c790286438617d60f4b3ae493623a239d6ed959388f4026286a9b498c3ff88cfd481e79501dd9f2762ec1e146e52d86153d7419cb85b32b4cd8c0655959711ad96265c84452c56d091b154392e585a3da72c6bbf28489645a477a883973c3069b4b452644c40d11b464c57901466b579022bd7d17cff24949bae744991d1e67926367111f40c4194aced4d48001e5cb87dabd59b6e828d58c372501e6c69b23689a80993112e5cf1e2349196ee67211225b5ca40a10b31397e624cdcf0a06c4291a91a0583b636288a2f84f6954238eb9c1114607c647580eb23a387ccca1393202d7102d1e56cc7abccdc833262cc91f227dd0ce8ca17811823bb645ebd440999832675b38dedab07befbd4b354c3cee958e356b51e478701161939d42904e69c0c0a814b1ce98f8f6ffe0d236bde5e724518bcc092f3073538c3cd15c580b4ec5723aa0681de19c357992e59e23ec36408b8122c81c96156a2bd2364dadb2d672ba081ad69c28c8b83a3b6667102c421dd84326075918dc93b3320b76801d371d76a2bc5133970139383dc25c8c5d99bb9a4e73693bbb000e3745e4b4c4b0e0215366ca8e2f3ad09cd4b166c6e263c3d69c386246c588442c89d96f9bac217bee88e9c2a449bedb579a9d655eb898e167ed4d7206db3e890121a370274c8d0a3811d6c8951b6f8d45b854b472c0a913e38b6c4d3693410b911d31a47889f145ccd053ef1bc91a9b9fddfee1bdb7d5f0748d5359e3281feffd1fe4aed78cfc0072c8a3e48a63de01a4d05a8fd070bab161c3cd860d1b366c805df916678cec184b9d468f3f6365a9a4c7fa99ee14d0e31f5618c09d0a7afc06f870a7861e7f0738c58fcbee944f7d2c410ad3619ce452e52cbce24ae136dc16a3e3b66ca3e3c739301eebf8710c2ca3e30701630c60391d3f081c47009ca53f610a1d7f04adf049375d2a03e00bf9712b3c4fe059fca47bef05629577a759ee7a18985d824a97b3da5cccc51659c51b79458c361b6dea1aaead2e32b5948bc7f356d78a21aeadae15614834a8343f8c7803746ee5ae6c004c9557986b9bc37ace397afe166a450b37d2bc557493e6e2250be6d4caf91f040484fbeecbfde679feed101474aebb5ceabd77690671207e0acdcfa529ccbd2c4d0984ca72af42daaccc1288079b98a016597a7b26613b0b4292a2fdaeb19189d9bd196474faf4995ba627a6ae012a0f8db8e87919f20183e706278405860cf42dd9c362e245d0162ede9d16405ce0d038679b59909ae2ae0b101719a697aff3e97c32a37cd2f004a5c1cdeb7c1a0bc13ed393977669339f9e4e1cfa49c31394863732dd74cfb52b2c627029ee81b1e5e886cbcb11016c624f501aaa80a83e50a443fdae31496231a13bc244ce2df5f5bbc644ed4912a9dfb533227df7bbf664cc0c8aea5b966559966559965a6bad4bd3346f5c59963bb3cadc69d06f9625d4debebd77ce39e74bb7f7a6c0bd77e79c31c6186badf7d69af79b7326d1b38989d430ce7a97e6ee040a2a3a4f11412854dd75809455950ac5d4b3b9a61e863afec212b1334d2ba3b1fc0a2710cfa5f10bc559efd2347b1df46e87b3ee14bec2aa9cb14dfd27750057ca4a3765a9c7e606144093e21f966fa785797116daad774e2f61742de25e2457ce725fafef6fabfdc8225aae177d0b52ac7747bba2ddce34cda223bd43f18e9718689ed9db3d8f97fba8c8687fe760a7da342a3ab2d23b1e90fc2790e2b3f9d992630ae426f345e4a199969e8fdf3c32459cbc2dff3cfaf63cdb2bb7418e3a57f369833c347fc745700a5c115a0ab3bd0244a0972fe2cddfc0347b1d76df1b80067968f20f7928228b02394ae8cd1f91a37a6fe2ac1cb545dc26f3cf27a37535875ad548afedd1df237e1ebd79b49e79e85bdbb34d37277acd831e7be875e03d51fbe5974fc48305487f7ffa7b0ff1f634779c0bb90fe5cf94875a63dcf19fe7699a3b4799fbaec1fd668a532bbb9cb3fabb4ca1df3539586ad4639533fb01da99e58ea0dfb5386569ea24e8772d4e988eb2598b03e642bf6b7280aca5f103d7cf8e0e01f50ca86d47a3199958c65c1a04b583fd214bab6a475899355cb0e8b4b0e6aaa54197247133749461c2c696ab08dd94db2bd7adf2f9a7b7433fbdbdc63ebdbd707c7a7b917bfaa9a79f79fa9994d1d6454d859f2d42583576f6e04003e54e991d368ec71cdb9020476aa8b17b4d3b4353a48bc88f22db903842deaa3ce1aa1b637f9c8833a3216646094be64647eb840d316dac71cb8cec8a9d2bc6196c8b8f279e3b64fa2063f0513bf182cd70b2d0f14873737285cdaf069173d6c6a3881e36b775c123c348450b071356277fc8c952f7c2ab8ccd64726c99c0026ba302069b1f042d9501186f66e27e20c183c62e8df183c41ad2c54c8b251e7182a40b868bae1f746c7e6cd452c41827d818a9f1c4c79f38160ee3a0ca7d7a9e52e3ca5892af2343cab0398e0089a1e518448f1036bf3aa726cb4d73e1f08b9e91a848be7861e7eb0deb013e7892e4786b01a7caed57871a9564d5910c2ef334bf56e4e4cc344f73bc820c56dbe6699e55687199b199ff1b5bc11536e68ea779b682034671c3165692cd5c9886c574d8e6dad8cc2f7bef60992184cd3ca9067bc7d85121c6663e418dbdca689c0ca1c39640e38c6087d59ff9d50435b684111cabd3aef9ee079f2872e78f2316326ce64f652c3ac4cb3c821cace657984c0710176c896c228d4904aec5912df5d80982c5a25b67dcbb4547422dd164f1e30e199bf9a532842d91b1c6f1263208e27ffd75bd0ae2ff553f91551202dfb7aa6ff47ba3a527b2fc89ec8427527822dbf444768227b2169ec852f0ba09bfef282928e18fd62088a3477aa4f50ae98f9ec8fa9ec8fe13d9f5892cd31359099ec80e9fc83a3d91ad709f02aea2a5162af027ac7402270a7ad2263e8021ff609652e01a48c0452449db0bb7816ec2cfc43b5092b697aee39fc0415839c64292b637898b98ab2c5de2d88be3af2529061f69bb93acefdbb363313c9687f8f1ff12591a36ee648ecaf9f7f7cd07abf765d736c3f210631cdc747988df68393da7e97cbff4207cbfb45e0dd17fe5f3fdd22fad57dbb7f9875faf30eb7b34efa469eea4699ae6d09039640e9926119199979e0f7d7e3ef43ff44b3b99a3ccbf9b4f96e2bf2ea1ffe8afbcc4e27c27cd2f2df5424b42e65791697eafd72bcbb2dcc9bd4b336807ed24163235ddf9a995f6f1cb9aed96ebf885b807bbb73b596e9d3ce572ed36eb1acee9449ac972d479a7e3cf5eb94c6bac732ef79a98316a4bd228717cd44df893386ed34df87f3ad2161f3b9c6ec21fe458ce061d468a90b698ae2775137e249eb14ea43f9e8b3c1f8949db5ce457be4f9e3e3e04a8fc5dc97bbc1eaf27ecb9f5f57cf24a0e047dd0ef64e905c4e5e6b6658e8e4b31336abd2dc73ad665cfa8e47de666ce652fe79c332f5f674d02f7cc270ff16bdc9325d697d0b73b598e7909097d29b6935a2a48aec4caca52591ac9c64ec75f6ee5282e2d95d533b6e9ea3847978e79c75f86e5a8bbc626ade32fc9b41466f1a75c0159707978fb105780891c5f516d3eed749cb970f378f944b99906b49ac7d534e621fea09fb4d5721aa97532479d751dbfe673a7f0d56ff4b9cd72ce3d332db593a7695626cd76d244f63ba18490589499c4b0b080885acdd5734e0e3b5ec09d2a1f6badf1961ca4b1e0a6fc6e58617afe333d6b3a1a418ff90c311e62ac3263e164f9b9e7afa0a0c76e584a01384c983367dab4e92dc6ca41fdec05ad1e74dfd2f1972511ef51203d1e84e6f3355616cb5ff9aba7ea267cd105dc29ddcb070eb728a96a9a96c9b5bd6ebdfc04dc29dea3497d9e69aa5555a99bdf2a25bf8256ad93ab462681bed548bce54e057dfe5b7e98f34cdaa6977f953a50f93901772a27e05a28d72009dc5e379e6caf5b6f73d283be60c26cac15787766c0c1eaab5b45091d0fb6440f481a7be60cfd39fc798bc396286ba861776930b2fc614ba4706ed8dd7a73865643c7e74a64776b30c4ee817e770643049dbb075a4f7ee60ca5ee7530bf670022de6569b90af310e70caddaf1a99baea6c65401c29628ffb0bb27b24972f97a02e95d6382d6f39f6b725e3dffc6ff548e41a76e578530619a4bd52ba1af3046164489c261cb35dd73e9de3b831c1038b6e4281c36d7484bda407ad7ca94e1bbf7de5cbbbc2557bf79f8e7cdf2efbe3783fd1c5cbdde37f7bdf8eefe815331e36b060e9ab7b5383c6cb840c98154e3b243043823e81329670c172d7884e0a862864c1f0178c9a166842772c8eaf2ee496673cec6a04c414752471c1850cac4dd3863aabc9569c32a42e3b805c96fce9dda3de8ae19a3640a6f2c6d0e907302458b24264e1c39e18840d416a1087133262dc61a0619507ff9b4654c35a68650d4ce4624b3214c8e679aaca1311903e39c5263e349b20bce5816d4538521139edcb38ed2564113156cb0f0e031f146974d88469c6409d3446c8b5b59e6dbcb55b877ce755af253066ba8d84972fd2172c2016de0640109a3960279cf376ec6b0b8e04b8a1a2d2f3fd07091f1c6c78dd915214bb67e70f2d39e3849fe207bc0e072b689bbd3419c570b293bdc1457d061b3913557f443b7a28caf5926ce8ad19a33d300a14646640b8e2a648a9c6cca6ad40d897b92a7c59d176ae27a1805a9a9990186e58c8a2363f69c7471284e0a18e1e81882440b1523194bee800d4711ca0b2349b2d856bde79a0b27a64ebae409968c316bd6c4e49c0570c3479f3c5fd6583039d588315374c9c0d8d1444ff2861f1854415af9bc38d2b2d4f96f8931d6eaeaf9f19a98b05e6abcfbbb23767d579dcfdf712b6d06fd59a2df0fe71a2440d0df0ffa7bb61b74902f2fafed799e98b723d047879ec785fef2201e2440f977e814babf6f1b02eea6f91ceccf7f3ff316e3ef5dfcbc6cf2f66c392040fe5288f3dee427ee5da0dea9796b3b62bd1d3640c4bdec75305ff732c8ff3b03fc39efdf25361128efdea069be367bef1d021b3077e2decc42f086c6038a4cee7cd13366079c338db9437b6de02c8b4a914b45961e1762e061d13564ae2c8e2e0303830f275e4efcc0c1448e91aa16509e88d17186850d093e3bbe72b0a013c735278112b03845a40439d307ec66e1e28285160d28715960d46921829799923068da9248a0cb8b97a21a8ca48c6b278d17133ce2b00901da991f4884eab6c088b94991278d59124a8c1a2d62c4f93acd9849f831cd96334b7888d1413136f70486989b3353736a6cd5a1d193e3c7290742903f6c8688cdc1e86196350982c70a181230278c1f8e5c5d5b6f5ea868d360b299cc838aecfcd3a3850d185702999f1ac4a8fdaed1b56143f1850d7ade7bff9eb83437188a629cdefb4355dd99fccfcffa8d7b4acef929e3ac772f4d9db1794b21541337ce5450f4485f7df96f6df7b7f9efee9bb76bdf8feef2dae2fc0745936aa4493552fc17e39c73ce39e3ab33d3b5703fcfb9cfbd33cb19fbdc99e58c1933d4d0dd0e33dda99dde993e265d018ae9dc0953dfd4042b13cedad4773864ba533bfd56e0faee67c7f72b64a9920c0b0b4b08cb86024b0f8845cf7fa63df5092980ba69b74b5cd777aeeef239f61667e1adae1c5de3b0318dd1a07a8bd3d470178deb2af416f3d17fdbbcd575eeca39c2aa7a9bc7caba46cb6d56cb5dd64075fd675a87a3eb57ebe074fd4fa5eb1f9a79b253a7ebd770c7ca47d73f800f78e074fd1a8848aae8fa3708d3f5770042320d569e24c53c7db8ae787dd175fd588c4ccf1db33d598c388e6d7cd8e0e444e8fa315d9f6416961fb262b1ebb53f6a5e2274cd86b547cc0417323318471c97ed441db89e8d141ffb8d92460c031551c9806e6693dc789b03c506971d36adcbc3e5065a98b6387fd89cb3be9f8156f4dc26a5e79c73ce64d2f63bc6bf3fe32a12baaed3a3eb3ac5ae95e8d26702d273cf9f5ae91819e64b73e19caa689a26d5383ddea9ab86bf74938b34ffeb4f8002b01408ba49ff19263da3350d0f70d75bbbe9d73a2369740f30feeab797b75c0b1a2731320f9de87c955e74d2fbf80d80914f5079981fc38c9487e5eea48f8bf92cbb740f76f7601793ead216cb0551a4ed5708eec75b7988fb5e719123d11a17711159c2b8dbdc1be313e889d22122554dd7a155e81cf6d2270f70d720e4a146d28196b3b98ef11b8cd44d5617d381a54db7497f05e74ec2937e17d3f8a89bf4abc6d48cec3cd3f47fa89f7ae9234b8d57de8c38e6a852dfb8ae776b595ae5a83bbc163406a183a35a8eda5f96aff2d2b22ccdbf24708fba1ed20ffab5d61d4c80276d791d1f3b38f5b16b5e8dbb6f137edee9130ebf86b49cd70442a373834e570a330429afdf353ac5b6353a594c5a5b679c55348f9ce956c524a30694d7aec25ad05062850ec99823ad3e4cda74a4a1c205472a240854e472565e2f9d47e606a62ae7d499533727cd1c376c042db55ead74d37ec28ff1df0781ffe207817fb75eddf557fc5d3ffe10f9affefc72345001a0f746e57b31407d8fead33ccfdd6edbe003b1be351c7b6bc8346b48b354438e6a55abfd55fb6f8eda7d69304dd3c406c84d7b00bcf6e9dea79d9fd6611e9a18c3be32ebde7bcfbdf7de3adf7befd4bd3a8bd56c7a9eaff29295004b664bd62c852e5c0bf92ff4fc17eed47dc24d500f784cad69fc4dc167793536f5be21d2cfbd10413ad979edf943736d2b9c6bcbd47bbdb64261cfc2b60a5d2ba0427f7b2434c83f54db3430b974537ebae0d07aa5e156ab8ce25e08dee35e88fb81f31ee50d3d8fc896436bee81187a74bd427f48d3b51aae67a1f5695575931832c9e3c1f28bc78320ae8e3d3fdf46dd9477105016eb2d3e6aa98da5dbd4f4d517ebf9d8731b9c1c9d46ea646b7eed5af3abc45f24f09f20014efce74aec569907d1c9affb9575f2de0aa6719785ea785866bf3fec85487b214edd94f5b3a56c53b67a9000e567623fef4df6525760d2da87e65716f0b479b1254e25493858cdafb29c1563bab0257c756c4e01176764330fb2692c34a8d0b1994341c926e6b6b0d8cc97b458d4e469ce6b50f921c7669e44854577c6770724d412fb52db962c586ce64a48d812196336f432b519b0baf9e88d0063ac817231b68cb8ca8db5993147b5651b900a53cc444bd15b336d69c97cb3880f9a75162a4ae6e152d652b12f652d6d29f1e94ac93c3497b03a061b396bcd95ba72ce5c5869ab6ba7628ed2af1fbf52310f4da7ae1ca595b2bab9a58485f1ef2f3a1ee5a323df13d2c9ec282f1d8b8e405a278e5a0aaf46c53c343ff73ae8d288ab9bdf413fba7910c43e810f5caf526196347b4832124b3a159d8ad7428b04d7cd477d41dfea21779f53514b51f0e5c4e594c349cca9accbcc09cda9e8f4e534e644d6cdde3ef5d6a9e864ece6fb764b6d6d51122e47b56a72492d4799c86e02652db54b71dd6c95b0ba07c1106a084a66bac97cf52f5bf26008094a46dd64be84bf2c8564badfa9e8c4d5cd8fe04ada3a15f79745272e2db557b26ebe9258371fa5c292b64e45acfe01ee4b6d71d82a757573e9782d98af04256d8dc4baf94a5e4ac7e007d7ab720d86088600fce05f16a8d476b5d76088fdc217ae2194bf896c08e0aa145c95ccf6b197281cefabefa28890e54a8424064100812bd113d9cd8bb8749359f2a262d198d64562abafa8acc80c2d0f8382f467ee2b22f3f122315e3456446686a24345c9a1a2e4d05137999f665e84a493a323426aa92088b2884e13217593f944d6f43d2f3a6aa28c75c6380b91c0186858671445725a2af8e617d16929e19b5f84cc51bf37bfe898a37cbd9ed7ca276d97c8ba79f419c5b84c0e7312bb5a7375ed5e07a7adb234cdddee3c8180dcca3a5c300b2243511eafc7eb39e570fa72f2ba1684b6847a1d9cb89cba2e845d10bb5fd782c9bb90a3c385afa11e06bb5ff0ba16cc47531e057329d1a73d0f1374a7af099013d41111f53a38e5c843f381655227af3c349dbe6e5151af831357516fb743ba90861af190f805b40b461f0f895f30a65d40cb43f3f7c3bf0b667968fa7c47473f9ed3561e9affeb7570e2ca43938290531044f0cbe01359210f82100a9fc8eef56a3f707ddd0404aeab9fb4752a1a61191575d387a2a46e325f294dda4239717127af8c71be4442ed073abe2c945c8e22f61fdfbd905f3608259787e62f7969295368c96bc9ab9b8f7ff8f7c34e6d6234bef0cf87a1927c7214d00928392d05758492833a421df3d07c1f1f760a6ad2b6c82dcbacb750c76e3e945c8e6aa1926639aa35c5928c39cad85ba72254f24ee16ebecfe89d8a770ae3be7b33084a6ff1d1c353c74b6641c6aed7fce6efb8f6f1615f42cb51476b3b822c47e5377fa96cc9a835913ef2bdd1588e6a9d8e8e24e0cae2ca91a32e1b8de2578ee2cd1033223bf269a7628e6a97968e392ae821a9a3c56bc134df692b470199e0ca51bc2eaf1c0594c3c97ca7b01c55c2d150e5012ee21e6c22a762373fe51eec212e0425d7cdef151dbbf93cde12d175f351de16158bbcba6955e455968bd2ee54b1484c4b9dacf944745aeab25f68455c456470bd35c580d69628d9d1230fab9bbd554f5ee5232dc57a5b9434c58a8c398ae8a8a52edb0627a76291a9c56eaa5c5e4466dd244aebe603ef51ac9bbf5bf7e315f8796d4f20bfb70c92c0bd94bb16cc28a064978e497fcc97f0ea5bb54bc76e7ed956c269a9a16e328b92bba2a496928065be11d696919751d8576b34d6cd6c74eb2d0f869aa2241b0cb3de1ae500f6d6e8cb48cc88ac9bbfc3d91423cb51ad06b31c65aec1ea2dcffcdb9a68ddd490dfcce64be0aa9901ab284bf1404f34680522240110909053141305c674f72657c9557a6929a13fbffc2ac3c4721450b11762ffd0df5e88f285b28604a1df2bbdf2f07c949739f2f07ca1f3d10ce75be534697bb625573fff3ccbf2f0fc3020642a1c9e0f7556f55317e9adaf9f6796b3b0133d4f7c9665255a4f5fc3f3fc2ba5bf8cba09bfaa7ac0bdc24b61b03ed00b39d0ab684392f5f5af0a3915c6be3e5ffa25a6677aa19010be225a60019a2db0c0020bae8842a6b7c04978c0bdc25bc0f49be90affa2bf5fd533fdf374bfafbe8af43e777a95f7283cee59087af87dd34bf0b79f39ca47f5556f95518c2f78e24f4dad20aafe82f56ac357fd1587a7aae2c1a9b7facb52f1e0d405177c88aab75a43d8f0557fc186affa10389455fcea26a695c703f282bef5cc431ffa2daffb7e6fbe054ea24385b760f520f5c247794bd4f903794bd497bee42470172e2d39dd2bfc35f7f89f40bcc55df897af370f2908ffdf2a93c05dc3da5678a99780e32ee4b7a3403c2025b4547da2aa3160c050c1d37058c15be50c19a0fef63a0060b783fa75e5f178bcffdbebb03e9a7b2470e79dbf55be5f01cf90e195d0d2e097bdfbeacdf0197c1b783c20d08752bd552676e0dfdf4ffdbd55be44f43304833fdfbde80a846a0d0a9feaabfeabef37716f9ac4654113d76f16346869bf59d08cf5aea19e87f5d1ff7174ae4bb1ae98d6d7bf2bb2fa654b61ec25ef0a854261a99126cf443970aadfc0ab5e8affbe02c79d73dcb5f1f1ebaf57d5346dcf60f0834ff51c5e03aa95c36b50b5de0e3af0373c7a7b4f5d7dde87cb81af7220efc25f4ec16ba43069eb5e06eb077fbffeef71312821ffe35e06ebdf35488009fef7b7a3affa567cef45efefff35e7b773e044f6c76fdfc089ec04bc25f6f5c3847ac03528fcdd0fd16030f8c160505541e8c1f6ecbe7f20e7cf7fbfbf3dce7fbfdd3bf3d0f748370f7d9d03dfa36b1b5c3fc89df7be15c88635285c0bb0fbe157ad59432a787aaa356b081400d62001763ffcdd0f1f25e2754de997ad88d5716f5d31f23916ae48dbec85e90ab0fba88f5a39587ff7b7e36001861cac3ffc0bf50ff5b88216e82b78fce8cacb476f0fb8b6571dc8e3ed55e73d9a3d90c07f56fe5355f4d50ffad46f89fa5551f5a2ebe9417dfe17f842de3f9003fd8fdf3efc9593d07f386faf3ad40b797bd52b7814f3d6e1f09f7f90fbeeefe39efa2786bf193ec3f378c7f02a27ea19be25ea1832fc9901c30361f8dbf39061c5f000d869390cbc02aeebb84e6a3e4ed2361f858f12f19ea7aaea9a8faafa3ce003d77677de2ba1a52d0f484c7b1d783cf5d5351f851d80e472d45d79441cf845bcdd7259c3c1c16900455454c4814fa491d4208a7e9bf6b0d2ffb7e7caf3f1d7e1fe1ebd1aa98fda48246d35b2ab5fc6f34c4a8ae07104aff2b0ee9d027e7bbb745555abbafa50578ac3abaf26fd069e94f4543ce9813c495555fdd5795c23f310bfae7b5287e9e7a46e62353247a13ff4f8359f8d7597962614274cf87ba570f15120f468f38638f08538f087f8edb8a851f47dbc2576df8fb7bbff90823f8e7e10453512090909e95121bf3ffd05fcdf07794becc147354f187c1e6f55551edf00eeb7a7f4c46e955515aaff1e05e2b8e35ef0510c81aefe13bbca5b1e011e575f0217fe08de852b067c2143ffbd95fe71f483dcb7eeb93cc45b833b857f6dcf756dd3aef4f7f6fe3e5f5bb52b7d4ea647463c0f83823e0dfaa3a0a637dfe8adf44e7996902f9c07b8b71ae9c14e6a892e50c04754e8f9d1a1a5bf40b1be3b2e2e495b5ccc49b9befed5fcaf96e2dd7be5b0962b8db73cf65bb6f55bc251556de070c1b7c36f9f7ae7f75af1a0bf80073d870d559ceafc3a7f88f5b7ca39a993582d4731017d0702aab798a3a4745203a4b51dfed6f6d107aeadaaae17aee3388cf116db8a64aff014d85814bb04af6e20fe13b89e62bd0eeaca2b7b1db49c6b01137b7ad256cbe9f81fff9ecb51490f7cfc5a0edb0a3afc1a99a32edb0ab98e5fd7f1c1e0fe0ff73a28f1b7caeadaeeaefeef712f83dc1f49ed3081733dd71fe955a5d5c35dd5ff21712df741ee13564de73441d30f87aababbcfe12ffbbb6b3001545ff521363c8735040ebfe1abd6108680e0f01b3e84aaa7facb4af09a4ed351d0729a4ecbe9394d7746f013d4af7aaa0f51b54a1082c3ba61fd0fbdfdd0473cf508fef2dbeb10c17ae6e1840914dec2dfde01287c85b7ca2acf148643eef4162a3439fded7598806735de14c14f58dbdd2754e8dd3ccc4a8f7e95ba061340f5550f82eaabd6ab212a1508aaaffaaaf52a53ad445655d51f6f71ff21fd552faf5fa5375fa9a5f0b77700f525c81a8957347775bd7938e1253c7f2b3d810f9f02cfc53cc42f41047cd8c4335a36cb43cce29b39b8afbbaaaa2a07f7cd8c6668c6b6446f6b538f6d47b536956f0077f5b3d1f8fb6c9687c64733f20de8aeae2470ceca6d6d79f8ed07f9d822bdbdcdc7ac639b59360a85a46df6f2ca81c3941e2b8965b2ac1c9524b2787d0a7f35ff082620fd93a67afc439a1001855582758d606dd33e61e52b6f55d776f8f454bcfd20f709effb24de127bd2a33dded9c3e05ff87071dfaa5d029cd2df15c3e5a1d2a3bad701f3f9d09132568efaad2deebfc71bbe3d39fcd55254df0ebbfaafa5aa5efd0b386c507faff4e88f67ac3c4c7aa5bfdf5be52a2adee1c76f47e2239ebafa57e933ab84415eb15c904eda623a8ccc51b81df1d4919e0ee9f1a3b727e17faf72a4b5add0d5fff15603880ef7fccbcbaebefabfbfbc7de217f7d72abffd4ae0ed0f09ddf198f884c7bda447276015c85bdc25fc8f23f11677a447558ec1edeaa39a378123fded21e07675053e4f551f83dbd515b8b6f77ddff3705fe9257060f9d5819ca8c43f30fb84f5fc812ff83e89f330cfe713ea2f15e9db54c2e5ad6b7ba27f35d7c6de4b58dbf3445adb14f7fed5f5ebefdaa6bfb555d5f314aeadda85c2ef0d9f84df6a10f2e03fd7bd0ec05f327aa517f296d8859f8484f4390ab8e254c20f871a793568e4da7e6076e0071f05f2e76a8d14f85659237f8fd3ce3b3befa7d1df4b585b95f7561a890b5fe549ffe3ed91d2aaa4c46f4fe2edf7a3a0fac0e0a3416090dfbf3de0bdc0f503b3ab57e51cc0fdae238cdee883dde8b7b6c4fe7b95b7afaaaafa565a45faa3bf48effb21adedee482fe155de626357b154954ece781ed336f5db0bf7ed0f9fd6f64357bfd50004aafce891f8ed123889ab6ef4aacfe8ad3212bffdc749e86e74e4fba02c0008fefcf9f3a78f78ea467fe661fa5639c36929de6d4a1fa75fb4b667fa446b9ba66b3ba2421f7af385de4a6ba10cd7f1f7f86b64c76a204654b87dcb6d3a8ce147da9e7923a7328681ccf94e95a7861e4fe8c9f410b7690f77aa803b4cdf46e871f636ede761d96e44bac630d5f795f284a8efbe020c7d236097db0a32003af925ea5b03445d6fbddb3d2e1fab1d65ba4dbbbfbf81149fe5fd1de2f4369978b7db958fcb15c8b9f68abc92b7b799777050b694d72f9b165795f4ec974dab68d4c28af352be2ecfdd79ee765fe9ee972d0baeadec972d4b0cca3542498ffa5d4bd3a5b767993454c4a465bf6b698c19647d99e6a7019e4ea8044d35463d0a2ac6d5080000003315003028100c078462c1582c8b5245567b14800c6f9640765230940763518ec338868118c61843803106006208414c19a3ea00b67659f1135a3f45bb41bad2b8d5fd8c33c9e01ab613befabc027eb857c0fd863488540de45ddcc94f31f93877c89690f638aafc6c196c195a4206df681d70c0a426f1c7241584c5a05183412c5310dfa43881add307db713a9aadbd5f26cf4e542033e0a9ff544c9bd38f1a1672baaf5a3dce3a4a02da4ef56b91cc436ab2ecc7d4fc662a072da512d204dcee46fdc5231826fcc658d7c89af47291441e64accf7c43f0194dab7eb313f98add55c9a8207f981c62c7a038a903ff5b05b89118c34825ae39a2b2e25fa6c31040eb5f512ce2b6e5721f78f58bb2b5d4550c26cb0867223613183034ae30d42f334f97c503d136a4424216bd7eae6d61f6923a2fb9306c421a4032ba9e7866111c092c50e169ea7bedeac223ab29f2c9e2aefd727e33e90087582e562ab5a3bf608b16b790c8110fdf647aba0a5d5f35c378b8a28c95303319e58d694067ef71536302ab034a4201f87d3896a64f149439505d5090a699c690dfe405d618ae1decc70308024909451370165c2122b9c3b018020e3a66d0050b665a2090845c1469046e17c3abe465d1684ab5d43a82095a584e7072eb1fc6b38091416a28c51a40c7085f20a2197c9fc48cfcb2854ef6c2a3d05edc9c29c22b5ce89d3ceded19536a5f2d4b84c6eede442b840d10da9c4bcbb1c5da6bcf6dfb8f80d5e95c63c8a375cde32e435fdb4e3d1899245ce7541f849c9adc2b99900e6eb8a228953151ea111cc12dfe6381b83e48b167a56762c87265cdc8265764edbd0703b40c27e430822f74d5745f7afd6d8f55e6b3c88cf628d037b81788247f60ef718d20be4be67dcd86154a55e8882fff19efcffac494a9144b792ae5abc4b2ecdf4e12fb9b5c3d34df5033463330f446cd0600b8583224dc91c2b1a88863b4d487ba9f028d39fe8cb7741d64edc907186410e101ca3b39ce6dd81a87874de7674fdc073c357c9b969834ce60243e110c99a9d6f23088729bbed4505aa0570cedd7dff9ef563a305b90b8b399d2018e10c05719ffb34c3e1ea3c10afb94f27b821550b3d6f5031cdf3860720684bb95457b6fcc365ce1e0dbead491994d2eb9f9b165388e2f56310380411ca4d341f42d2aef50419a89c12a5cbf09d64224da6f99df015f5e1a6e746c420095fdf74500bbcaa3e1a26f1e40b2d355ddb7d52f9031fff0a12585477b4661db903bd8b2f5cab3438afe9e578cdf63c4d1a406e75d25fccc976257df13ba7b306111ad5232b5fb431fd75224a3353f6307b30ae5868f7c4f3de595f4e7d9b706c960990813a1d7b34a65825519db587be4152e10810d04c7d12b29451cda25f6285c5577c1c0dc2a58fdc508c844ec3022b4ecd930267243f5c97e4d33dbec7327e936f06d7c38e630d21892742dfc34f720950be9b2f2f6093ba4a2e812d99a4af9ed450c2e2937c57844f3777fc29a57646122627d20dc4b7086cb77e0a1bd7fb155ded046e3d9027a14453e1fc2d79d570e5e27271259d84080cc319faa59dd6b75018aab0529f4d36d839b1ca1ddce4d440de9c38ee5c5f504201c04714b12423b3837df9b29ac4cf9e9b00578c7c6528898c13dc37d2f7542323577c88935044433a6e496cd461668941eaed91ce39242cb239ec18b1989f30c8e1aa6e5a2f5b467cca2adfe638bebd351fb63c565505e0e6b0eca2b6fecd0154e7d4701daf70866a3f410dcbe90bca91c07090e1d271222a89da4a0ae6e844975b3ca45b22505f06c44f4161fa63f178df13901c85ff2809c46884952c4fe7517c0c2cf46a202f18523294f2520512d73b8e471540c55d1b780300f898ae8c2a872fda11f42875b3d80be5193808815bf3428c8a47cd1a969b7085dbf4adf6508e930a69c17af7e1b2cf0128565f8c0b8b3e3e0168947efe84ad3462d891e62651174d862dfeddc08f94abf00f64c39c2843525636739ca7e44c61a2c5ccdbe7f051399742946c0e02f37ef18e9edd70097add5244f469275534a56d69d791737038e044f56f4885de4f1d7153a68c07d2faf0aa1e8c6f2c1343e3f7c58243c3698ceea74a04e7b969850ca0c4e7fe2e0581894b69ed3662831996f9b16e81fa66c256355e52015d063115c235877346fe298d6c620cdb0c8b73827d065ce22dca0d463db6df06d11ae64894d01946a9fbc85e9d9b43439ad9a28f833c7beadedd5be39dbdc0c1e7a4be73ca01965da20d5bb5b81f243f32d0b3b0d580b4c2a5c26fb825bf33db860612442a07d4cbe967415c000951b3ba8dac5113a15b7a8c07fec3ffc5a475964d01ac28c6fd77e3cc79a394f0759803cb4627dd6112a5bcd01d90f6b0c3fd036a497cf1b1770001b6beb4b14a4b55160374880e720b4cfec776a74d0560b1b9fff537012486881875590d1a828913488281b485bf7c9c8cc20159d2a479e392e596f7556af0f4ee7a756dbde2a26317e4e52271a42f7ad732d7e962b86e6d9dcfd9121ec1d3a214644f586c681a6622bd79f920a9a40ce26a1dd807cd749d02209a4b1ec8ac932415f2e3fba3546594f33a75347e64545774ae309c6a1fc514c34b8be3b7823b026564547f8378fa7363d40b47e1cc2a8e4120027d66cfef17d83e3082f3188b4351d082402537e7b360e707bc32e7cfa3c20afa3221f0f820a4b47eb383174f22586e33956aad1ecbc26b0ac37ee74f01e2cc7ba173d130762e4ce99594b1eb07aae29cb989ad103c9649f32a6af7237e79dae587553878b5a244ce64594f308c7cfa14d26268caeef72f2ceaf757674695a8f04c6e8b2d096e08d4ef278b3068bf6b4dd57cf516842fc98170cafce1accd7c40e510716cbcdd7857e50f16fa4fcf4196ec26be5272468da00a4d50744fe9b5c33e62c27cb7525cb93b287c297e852f12eee50bd81ca12119c255be343f9b229d9e1301f85e1995c03ad781952a5da9186c809ef1c905a3fba2eae54d084807374fcb1ee0ffc7cf1bacda35320d7905c77b3498153acf6544f7e12e77e9b31cfb5dbd5db7ac6c7de2867b5002cee598080c979c0b184c2570f231731a2e994cfe5ef06f99c2943fa1d7965223c268f7173f6ca531131ef2423cc1734075d143b337b45f3e69738a386def3d58b073e6cc62f3bd64697511b8814c8a118e840ac980aad1780a439c94d176a88c9d7d68a88ef240b77eeb655c54d381c36a5eaf17c63d741e79d95e31f655d012df80f543f66cf4ff0b2b6be7244284281f0345046ee784da26f8d9d065ae6a93413ae34099df820aa3a2c8ee42208872cc90bf450ac596b2d3752579c0176a5a2c23374303035939335ded550afeb2531a11233cabb85b9dc501afbdcd4101fa553c279c13f1c2b3fcdc452868d418a332badc9a6a5d6f3d4ab7e44af544059df93d2b011fbcd28477b448d1dce163094e05d7390f8ca7a8a3ddb333ad6e156ba7b0d716732178933fd507835f0b1e0189e503fc1b25a78262f574b91c084c4bd82183b5ed102efd19c0f6ef6ae8a97085931236693838acf320c4e59cb77cc0092d5a55db516f2e86df06234015c4f9a356d1d159048fe27c1f304055e5e9dd102ea4d52eb88957cff1aa907c04cabf2f90e0752deb28c228b42b35c2b7bf4e208a11fd89f5691b4ddc84f9c41cde09cf3ddb0be830e382389ee70e5ee54062da1bdff664b42edb1349e671c1f1b38a8410129245bb3f205339551006acd5d88ce5b27e715702b4a480c6780aeda3c981b2078d67b84a09df88c8c328105c332298192d18a86418769bbde714538eab4c4d3fb13079515ffe3e57239e2e99f529d54cd40da6f495d8ad9c50ebcbe3a6f0e7dd1ae2797513a5ead0e8aad8d2d6960b02f06e38557a184daa6ed0c3f773cdeafba390de0f88a885d7a74ad762624b687709fa35703d7f1aa92990d9e8602f5afac01936eaa5b92e2cc07bbf8e188e7a5287582605bb6410e648722a49b32e6449bdecca57e85581305706e8c95f2462d16a2989ca7f1524de513fdb281fa6db9edb85d8ae1a4d2358eeb72df4887f80aa09633dfa60038970479bf7e3da6ba4db54764d17333d5098a22e5cbd026e31449ae87774ce85beb1300a0eb3f39ad1b618df33d0101e4907796d6924ddb98c99a567651e5cda11911ac30033f729b1da413d4b5b29aeaf309ea3301915d248f0322bab1109c624edc666ab45ebe77c6a7daecb4ac19158997614d32815c4824e224b61ab0c86e233507ae1531016c0dc32bac084768d3f780e0e5a49366cecd2016645cf72b3582e1151159b1f18ca96974f086724586bc6f619e57fda3e7d2ae8459e2308968ab3f8bdd353b9da7a3ee029e684f7684796bdd5f06acacc5223f9bc6bd169f912664ca661438c7c92cbe4d33ac438c623816a241a74244db1e8e6455e591cafb8c1606ace5def66ae4813e81d589c975b4f82feaaac4409a9a585d36299290f360f8088b7daaaf313e10a4122d65579bf97ec4619a2d47c4bb34b864d5bddd722c0e3b3717d857a47e7c8f7d251e0182c98edbe0d70ddbb3b58c44475d37ca30c046b7974b347980e5b516d545b241c84e9ec16480db1ac84dad7cbee454be9d7309b90dbb582f5048eda534047c8b6f6a23e4f11d5503ae8a9c90958b4007d39003eedad5017a2e934ef45f4494b43c3017ed765c9e282b082d7dd98f6d8f4924cd09e07e3e3af93990f96a3858442d3f69b336be43f8b534b52518a37ae69cf800f76991f52385a9bfea33d61744fca0d84487c800ca4bd0fa276dbd6023eab148da1107950b03cfb01833a4778fffb7d0d04a12dba0143d554c4274ba1df85873a1d9fa92c6a5309875bd7b0ca5e6824e1046034d3e3143b5b99b5ba8b6e34db689eb6d39c4fc52eb4705531e75e0278e3c57c55c47e5c66b604b6da7a5ad28d1caf5a0cbea16d8b0e98bd8ab87c1ae3ad0018008a3aeeacbef46405ef3d1c623c07567b746cf49a95cd7ff516e2d56d6c7b889b052a1ff8548491f18e543880f75cff4c147fdf7857c01cc12cc48c25181d713e5ba44de7e4eb3cead7c0acd39d642c99ffa0e07e345819ff5925484fcc662ae48ad2593cb02307cbb03b877d35c6fa35a3bb2131ffe011a35ae814bf3b6343453f30df928840ac1f069e16793151f20b01dad35f2579a7d37634b2212d8cf2705db83d74c1bfa8ace2ed6a50f808014a2a3b4afbde58e285970bc8a5f39da408ee532603df14acf8db568ebec843f52465ccdf37a7710ba55e39e7f21da3b7eb8cc53e3ae289704a51852d7d11e66ebdff4f366843772ccc2a4ea00021313d3291a14f5f93fffcdf6fe71d51a9e2a70f51581f2006a3bc6c46938b7148278efe557980d04e68d88eb6254288fc0f389a8e0f4b96eff3d2ec54a78fb5c1f54cc1e9b81b2a21a3b361e993f789c7b4ec3f058f876cf7ff84a2e634c4ad28a62e84d429a153d658c2c8da25fdf935a41d8da4e43fdf7c686f42663706ea7e765595b345ea788d4a0d33506ade385ce7c6a88ed51072b81c9d0a7cc6e81c51869038fdd109392a37e206b84137d747a5529da5ae76950e866a47db6793307febf010784cc75d34a68480b17aec0dc0f82b96ac01bf0a317a2e09cc69c287e22cc00bf705f9b015b4630eaab9db69d166c1a077e2cb30ca5a4c31aabb614cf26b3225d07d0303aaae80b4a2a23ba7ddc31a7e1048456f2a6ad503f3a4207c584bb13b64dd355ed8ab4a18d874de53166729e76cb502e1b504fd5834192f4e804b97543b822c24c0206842b95c7402c20011e4b83892b726fddd33db9882f1342fc6ef32b5a464ff1ff40d712e791c3155874f68894772a35a2c9e76766dd7c08f68a7527d2a32a8e2582066cdd0180066d5a246bd9f596910798ae77b5c1f1667689b31805dc3fb9507ad33939ad29544d1c83345e3e2ff87fd34086f3edc344f3e271c36108b364e1a9e3a6c720aca625ee3e43b7f6480180e4b1e42572342ad0425c232f4d81cce00d3e4a47833d72a2879f1d4e4b8e5d309b32589946c39e7c08faa1e7d93ec8715724b75dd28eea4b927e826e4ffe6d44711cfc6986e42802719b125a4c63bd3b9b4fcf855b0b01c05704d3207ae5d296954dcfcbfaed449fda7f7082cf1c138968ebd7c515716f4eb2731f3b9193276dbbf2411fe7d8263676f10c9bb507c0f3184ba761f37071adc5bc0f5505ad2bd31db72a3ba5bd58be055f6511e01b9db3d6f80ed32ace09ef825fc3740cd9119d8e5458b9732afa6646e48c4ec003c779d4b7724ce4bbb1867519578b569d6c55d8f13a8e90844b9a167aaf6a3e4474f82c6dd74d4265ad1084caea0e81b282f468eb44320b0ccf7ba810ab665222b1f590546181a6f2d3ae83bc5f8090fc5bd094057d39687cc646e0532637c9bfce6cf1fe1452c574257696c9b74ec7d97d719b179c10338be4da3d85a92c55c1724a8b3ef2b05dc5df459ab5c88c035ea193b69eb949760937c3db8a46e18e8180301649f2cbadda64012c402248f50e1aeccb0c382d3a7f832c654eb6c5451d9e42d47a60365bc3d323d4f0fce7530de9d3d2981d537772e7c81fa988d2a3874e04f71bc4dc97b3087fc7c2950df300b8159a4251598966df15de001f160314164e0ac4a43743181390885b768b905e6d63ad1e14b98bd9694169c2bfcd8b22cdea3f87f0174011c016f6a868881364542a020646305b0db0db027d0cc5ab6aae5d07b2ea0cf9dc121647d9caae74f58215997795a6494c1660e4d60ab570578ed90d325d40831c6fa002ac15167a15276073d0f70c297cb67e101ac0d77da2c3efc56448e2dec945b95d16476f4fd12ed42774050aed173275fd9e0ca1b661f3bfb7eaba382c54f8fe4a7de40b8947b6efc6f7bb775d5e3330b5d1224109d265d55d156b0a114a0dc3fdf0389699f2c6bc6e060f2e8b5d4ce6a222ae5dda224454d08af899d1d2d403b5ce4423ec4a3ec2086276ff70ee9215da4f4ef518d7cda2e40d38b5ff97f690da862953a7e4c49b5a02b4c374c3bb5e0150fdc7c705fab8236118981bb2e0d719a86b16b66dcbe012d87055627631bba118edb2a06faaa7a28229c2411fa9d57541aa2d785695b00d65ed2f7c68aa03999f3acf977e6d36f57ac2fd85ad184d420653f0d7adf3f0abd24a17041bc68c4eb38b14a1757d4f781721f84b4166773d320a1e6c773dd20b52d1a122fc41f94a707f78f5547bd83fe61715c91a809783b59a1ffc3c41533ce01bca8153a040c6e4cd046dfd163c68ec0ff4f7ba048e4449d9e66eaca8598172ddff946564daee7103275a37cda11c0339f3aaaab9f65e9c55098a6e052c033b320a3930435c92321791f352837f4955189e65e9c4b0de4efa13fa12e2da20be72fe7585fcb4d6c0205c53475435b68d20c4045e88391b89a8f1862ba35a80f56c146fa6dd3a475ffdbb1b5b29d4d7466966084439c5c76d6e2ecc0778a76c7f38a4aee1bb0aa5e93d0bc06423c17694358a4c1ef2888386c87d4130388a8177c82fadfdee2452719c30ddacaccfcd69fd5b4644215adc3e9e136a07174153a89f99bad29bf1d9da113f03ff51e3eee53c2d647b62ef42e2d7441d33b21e5ea03854ba10cb7e7abd23b816e86756f0a5b2344820cef6afb44f0e4b25f01a5bf555e1a81c8cd52c8910c458cfa8ed1f5f885e59fa18f5af768320f14138acebf4e86188bb69a82397f5557cbbef32b31c8d10e925c018343fde051a5a4570a36d00ad7726f9d6fc782eab613af88fb71138043d4e4764cbb4f52af29f701b01c6fc67ed41dcd82e5d89f28805f0a0f4e932aab74d5d837dd4b31c6d7e19a5e5dcfc85e2b0c4c3256b47527e7d3a9729d80172e4389771739a01abc90e02efccabbd8cf1bed38c9d7db0b01212086e65ff2cdfaccaed2abd3333eaf84ea89d9f500d04304263690b8e43185596a3ed21e7288b4115092987346fc83d91825d0d119ae3a28d0af4ed22a3941903998355ae2e21ea41aee1d7104f5992e800d2b16351261fe7e81d3ec923b15b397a399306427a8e1d5b930e7cce108ba66a9c4840c863d30e265b25353118df0a6f12b79cc720e8987568b776f30c08727b79f110d5e796fa3360335f1bc60af09b8c3fa107fa636cf43690a11e05a4ddac6d725a0f7112394eed3bad53e0c6228afea810dbab25dc517d1dcb5b03c1cea2da329a6b60a72d78dfa82cab03837ef1cc759ed2cf037e2166c16a7af083932b63abe5413cf19459ce7afb9f9101d2921d0400639953dee1ef7ff880d7012803c798cdc1faa57f20be8aaa87b21fc801f2663fdb913df31c697a953d9942c2311b7d9428ee72e1f2a669093021e5c944c8530cef16f0df94711a012e6d878e3715851547af58ad2b5d18f4470b065794431e5b073de3b8ccd547983b8cd4b020c4ce58d93f12e022e93108d199cf0da6d9909cae355d7496f9745a1b8a010c20e572be790bd42242e10242dfdc064f42c8d9b6a85a24b5aed801d8e9d0b6de0a15536fd13fcd505ae093ac951cd35b8871d9d7ef024f291497ab13c8737d5a701a8753ecd36ea44db0e2e8c18fb6002c10469493ff2f2fd60353d2fc38214c745a7bb0fb5150e79909409c6161d77b071e4e5c4d1058e356de6eaccc7a91b9322b845b8f2201541b83798552d978c5901f5f6d0c59aa96519f6fd6147d4dc5df644bd9822c6ade50f51477476f31479aef5a91cf2bd36102916038f98a50ade1061ef980ba5b874c8a290630f0dc188e1d7a53f8b29e6a91673f7bfc07fd66e68f7b788adba51bb0fe5f2e94efce43008723313a80d6d32ac52327695bad2f0809487e0d6dbbcdf9dbf9e495e256bba4ea2ae3190028b5dd613ccd4caaccef6839b232ea92cd1da50931a9014786f2387101831a0ab81c697762dbd822678a222bcd2721bf2b875d619bcd396bfc8dc6650eb10c9f69ac701273e7f2a5d1f32297729d2d9c0f3be1893e448767faa8ce187c9e2be90aaf0f736f00bc42c206aac7f5528adddad0c40a2733e60e4adb03a62ca7c6d3304f085291a5b47dd469d7cd753f60e24815d71450340d0b73f93bc10c0073a5fc18107123847d6d69cccf3dccbed62db1d6c5c5a1a345333dd2e804d3db817d5eb10348af657de738fac7764b96c9397b4d7aac37fdf3c905f9c026b3aa5970c9d0424882781cf271b9b6acd0d65019a6a4182b16289c0a33fc64060715866003ab1f34cc16f3d8b92789046dc29ed208ebd0a58adc4869d340ac5c4013c3291154ba7a90d5783e3c1ec56bc787d4298dc1267fe9ac70a66815081205fa4fabfc8526f5b8373fc380c1e02aa6031828c92796c28eb60068b2809403af78af24dc5c903e67db74a3a22c2a1a5a8946dd6141f1babcaf2df6743e8c2ef79c471de8a29bde2a78df7fd4e05e7b3d5f8648d0ad31f2004c0e27f96cce651f57ec9c85853483b45477581c627cd4c1c5851ae13a5b3050cee3e9d263452db69c5c643017f67bb0184f9fd141cd856f5d281f7834374fbbed77f2363c4d2690d923502b7b2ca21cb3297437576652c0e109f55c402cb31f50a34a0b38aff0b336acf2c8547d2bb944f1871056f985711eedd8589657de578362feda46432335d0b66e442b1f54fa715d20e7cf6406bfdbe09d6106c0257868168fb30e930576506223768661a7de2135c4fdd6aeaf58e70449dde3a3a88cba420a636d1d615e47f5cb070d09d0a84aa2e08cff65ee05ccace999bceb9090ff7c9772f9c6f6e1340b05258b6dda87eb761f9fa92b153c44828ace90cfe8aad527cafccec344af98d2cd616c9cddcac13ae2c5e7cb709b447beed54482c6fc1c5c375e6f6c2a32ed32c5c7a541f48baec4f547069b66a9040d8759ebe4f84e89ed3432c8f6938b30dea144168c46e2a87363870d90a176663f92726c9b2e4ec69d490cbfa7706f8b4c4feff5baf1b8630d71a46ed5f1392deda8aa15277344889de2877b0b316d05f5d7ffd933a1daba3a08ca7db00e0e2245eba4465a5ff650b19daeac5d8d0199c70941d7e93fd8401ac41aed42b972047dada2bc6126d18fc4aa6e6f58b766b087289f0e3d951d784c376a7b89c2d1b8b094d5a431e04874dda4514b61f403201551ac40c6f020338ae473350347874582801af1eb0b036cc4939dbf90e87521111ebfbbdf08e7de989ef347adc7e578b1e992b8062f942c36cfec741c6ff6a33c6571752cc4b3999838947ee7422f0421089e2b91e94c532f3140e2d9489e909e0d39622d59344c8f95705646a9169ec3c88c48f982a40053423e7e96b640783505f7e7d50d8e9ab8af2a4d0582271f7d4493c245e6ce7063cf99cb2982ec0e4bba711dba495db731692b490d2b406401d09b000bf0d80c3cd8e4c223f52ddee6a1c9b82424b33955212b9b73ec216e8a58776e22da0cabb24acdda98315e2a2da88f7314f151b8e4f47912daabd5a6a002db294b9e2095aad3bec0e3d61c274b2da2b8ddd8edc8d82849d8b4bb39918af8a392b9d9e808b69bd0a419c7d9e323a5cf10de66447deddc22a10b67f827f8402ac9e1566ea3636f946de1f0f36a5c675de5725a4fe116cc04860fa1b8e8280e7add130a58152722f5e1fdb36127479d7a393622de81cbde593f3b0ef9b44275588ed96ebbb7737909a3f8199806bc10fe15f172ceeebb9720476ef38037941744d408ae76b0afc9b256293a58ab132ffe5d8f737a601b18f6001a676f446a15365c623187efc1a4fd46705fb5e47d8a30c34d4c23f4f2f98adf208ce6d1d52030099d890a4ec7f2f8e46923add566e574b94a4e532dcc202c4f07bf45bb2cd3904740ab1fb504465c05ea64b8974c87a3346ca138900d7c4e940fa719f1f2ba13ea5b4128f33e68bc2c507763e6f4aab29c062ec8db1c8b2b1b18fb04c591955a1ac8f3a829ce33d4e2bf66b71b4a3a787b89413acc1aefe240847f92d69f1b694f0d950b4a5aa887eb863048032683d65566b6f7cb4aa54c8e0d436ba1332ed8105d0bd5232e42c52f9d56743aba41ff8266c0be8d39295bcb563a5eabc27343b511b288f056165fdc56335797198e61f0217e1365453afd971c3e8f4ce0c656557c64d840c8ef3cb2fec4cecf8c968d254131919f56ec1111489af8b4df6fe4b1452c908b0a38d98ddb85d590d60ef1a232f7ed30c001e3240eea61a45bf4a808d9677dae29d7274c3d8e5e4effe962ba69ad07c7833cd6cb319bb2ac60b0ffe94caef6c4f67ee6b244866a9c0cc44634ff2b1db1dc13e299e200f04128e5b6eca20b12ac4a10819f800f25c16978169f62d69ad2fd20476f68a85db0893116082931048ea1edcc124667ec058feb9ec5e0768a745ec94d873239a33905e4cb936ed040d8ddf695702236f1353d41493498cc34a3b9c077e160740d4898c0b72410779d9ebfa931a2eebf4c7ed2c3ed121c04becd70f60aa2d89808de44658add1ea2cd71285b003937a045b68461fca2c9ffa7e34fe6b4fd1b8e094551463e9c9b6927c6aa26119f65fc70d8a774aa54f25d34cb338e8f145745fdb260d7ecc6a787f0e5f0347c2f389bf464b2c62633ec5c297bb21116b2bb62632380c1b9636e70e2c830d4702e7f6b8a1be3acf4eb37d2453df0533ad083838a2f489c8f8a8b77072ec34ab20c37b23a3730675fd10f672b1ec5a8f7913692ae92153e296e8e5335e2aaf4f928ff72b0c869be5b7b49e15cba0208894b3bf5a5ed815b6ccd05418b113a5ee2767d34f435c508aa56108e73a4cd8e1c017e11b70abddeea8167fa1ec628ea45c0ce586a6d3bffc01c5f137abad42e48203caefd91be2fa423a7d3958a2f0b6da2124a50e316959fe28e2c8a67b4dbabf2992a1336d38558e13942818e793a67b6caa8cdf8e50e31837280382eec9cb97eabcdf294c3bd1b44d72e87f564fd27c36037feed8cd86407b4b9b9cb6795ecb00f9241049f953d0c6dd51336ae8290abeec4fd643438df8096dc96c1022eb0172f06ac2aeb146e336e0276e9698bfa5e5f3c24e27c0e0c942e693421e3662ec73311bed66773a97b5ba73e2fec4ac63c7f311c5be35ea392889d5cb9329970cffd63dd45b715f04994057d33864a3663b7ac44124db8a807c95202866dcc310c4bf01c78c38a23db945cafe3de182c5a52cfa9949b92c096510d3a349a9e91d58eef7fd0fce5a3f9eab628b9fac1e4e2f1caa75148d0dbd81af1bb2cfb36aa90d7e210b6fd5ebc48e9b3cdd6fcb8b43eac3f426c2b431da18e820fc2f9cc4aa156125945bb6da94cdbb907a57ff0aa05f4a4d39c2825c2b662690f3e6970c20da477d6e126831cea533396e1a994d40634ea78acd7f41c6bd25ae59ceba5131bf28d5f12df564cfae3844f800aa7698342bcef57a7970cfcc66593fca42ad3461e506fc6ed1b347623fc02e86f42c071a9adb5daa6138f50ecd9b8bf36f2a9cc000fc07a278f5c041bb7a9d564003479d85eca42a4670e6f661316f1d1c737e88f6ac1cb27ae8ad61ef14aa0e2e4ee81dd8a0e713d61801bbe42595dfc24b6d4463e9e120353ebe10ee000f96cf472574578f5cce16db6849cbed900dadd4d28d6f8a72d87016e6ffe5da40c21527cc3142c4f2027928c4c6f6ee586812cb6fae88374c8a194a923f286a098fb9e1e2c988be0a40ea0bbf96477f64c33409a55552788b97869500e4ca356773ed743ef243aac0a6745bbae2a5d4381da15cb025c52b9e80c197f29de0942dc40f1b367f0aa82568973a636baa24bd71897687fa5107ff5711f53e3c93f5e2df8726aa4004b417621d21fe7443f8ed40f335f125599a86b1c34064d8b38643819a0f54496ac2b5a2ec2f866b17a5cf38c2b5619dc42b77b4851da535ad971794709874dd24d3bc7aca5966645c6396fabcbcadc84f90978c5aafd966af79c0615373c580bee80795a045f3a2f538b619f66e7118c1cc23716ec9f0b70566065edfdce0944fb47bf508fc43d9c829923fdbc8e7e5eee8b5db670eda48e83fc9657409e806f09d5ea26e6e80a1397c599bcedd92fb3d8fdd7602e851bec09cee61b1206629d9239ead70ea24f4d6e97977188f0b320d2b30ebf47a2516fbb16a5765d6dc5d96661211717b5994b27e353b8abe90fdedf764697d5747052316a42f6992af9b220ddbe9896c9044dd8441b7902e47b4f557abc48d59fdca59fbf1a266f638e0855263a1a1f7acccc173a01b9efa239819027f550ca1ec22b2d3bc7ac772fca89a05262e964e16d08cbde6ebe9a00e201414d24d874435f95d6fd7cf84e1965c9f4c4d2a3aca0ed8550e8527ca69932133a25ffc7e794a0625e534292c2b8a932f44f117a252a0a998acb16b02878ade41157638e88c4eccc74bb6793dbe2614271ad6ab852d6dd8fc3559a5f7ef59967e41d7bdaa4a87f3f3a7dc0c369b464edfef7eb04ad55a4396d2c4a49a638b0a7c612c7df8afa913467340887d6dcd39cc8617cec2d80cffec71ca549efe105a6b65ed66b5bde53b26b60e28d5190c383a9d0eb0ac944d6a28d86b7f5f911d150f4937bcd216ad63cc3f0e18dbb3652f7f4dafbc7b51c5fd981bcbbb7e966226133350acf6063c867b1ca996e95a348a86e361cea49740b2340fa253a0312cc67011125c1e40574b6eae657869aa93217c320840a37b30981a3282b1706df90fd6cef2da4395bba987585ae1e904a3310a2acd910624ce8e9b65750368779ec7ce29dd853bcd376c0347360aaa1b3d840e140014bbe4c5c91aa2c95d4b5cb8274402b7867880bd3ae2ef6052ee3f610395c479ed8d4bef4020b5447d2db908b6b7c9b8e3071ebcc946818186dd5425c33fcb1e37ffb1cd008db91bd5b9094a61727e00a79325cb4010713dc2e26eb7e7d23d59a64c9864a2c6aa767af1c1dd518abcbb937e34a3ee043e6243812208793d23dfc0b68fc6e955448b01695ba7b620a801697eb69b291257cc1dcbea6571362e508fa51e0ce6df0f69b55fbf162de098fffcca85e8933be85cea3dafbd66d989178600fece72fdc3468999fad43fa9f472bbfad25b370fff05194243cc926c2845a09ee24c9666c6cf03786748d0c1d3728e6307eb9399d3006f7c728ac5bcde0e02ff6a96010cac1d3f6a6f4dbfed005d97cc963cf6139b1f42c74e2d68fda1e40dcb604ef6d2651ce22ad06e72976219ba94a6f895de1b9916b75431dd5e7330e69568b3b48d46d4966b75f5d1a769c0ffeda6c01968654eb0e8ebeea379b086b5ee3fcfefce9705a0e1c040cfbbd477a10efee9f393ef02c7fde14b4d0697587cefb35d783773874751b1ffab59211f3ac46d18586318b12608f4abf3eabe50d527b4b44f038c5f81a3521b081a3333a524c78660b3b933d0c8c6affa450f213101a4297dc6ce82533c8576c7935810d7523a625e5555750bc49e73012b2a462a407ac31a286170aad500808257137ba95e928cc6ed8b17086960489065ce9b3d6deff4c7411516ceb7fdd8a52c61bfe876356a137ea5b0475b9292f82cf958d7ea4dec8cca0e31d3780d814a4d1b9afd30ea9aa0702bd18f2db8839d2c3d560cec63ee0d3e1c6c62ff44aeb6a6b32ca23f3dfb3164711ae1308a7afd3c8700860ea888a122365d3eaf14aa098db7203ecfdfdb6b52891c1131f6ba341e6da6c3c1fbf812181ec0a5da512c294e7fd1c2ce3ea6e1c7d1c17630773f23980f3fb856f7dc7b5d1caf0a67439bb8517420c47752a9f05f70459743031eea7616c8f172a3d3fa854af78504bcc5c0d930895c6fcf5408462d05fd518bd0b70ca1049492d43be221dfe7883e5be4965f77aa885c1b08fb5c25a02a122877f2d9b73828152aa73c90ca4f4e22f3d1f33bce0f9919b0925bf9460312250aa595de858ef034f0bb1d2662c7775338c0d88c5d0c4689b3374643cd00f1ff1bfcf5dca0242d492ee99cefc7acb21825b41754dd006de0b0eb5db5d2cd3889455c2209ce955d453d0a1871f7721dde5f12f7ed3e2dfe22f3760de3cf2f06f1e6a6bda0d2321d4d9d441bc335a721ebaeb167b4d7f70b3821b9825d0797e1851fc3d43176c980176be909b123b6b1eebcbe9aaba00fab04aa4d02b4783ce2a1e702328fcf7a720897bb582e5b2f378efcc567c3c5005938c45510dc586e82a32d328ca6d96f8ff1bc5718b8b352633a442e8d31250704457e7022d6eb40e855d9f39686414c806491a38c2b87d21ae42ce6dc0da17f26b0ac7fa9314ba796197ad2b9879b9595e61dae6775f5443aa3097568a6a2088b0e0dd4a31b631f2a9e5a0feefa4f9b52f883014d9d863b3c627950dc78744a1c00f9f615fcab68035d37baea167505f98d42af5d08f8b7961f17c6a0f2335bcf6058a0ecc8da98141b58698355854e03bceb9edf1fd1bb96d00b584d60731e09908777c2b7d0b1ba9301cbd9282313480e059722464f42283881473fd916c993b0546a1592fee2d73edd2f09c374b1e0e3a6ed212a9c3cf5f8473031080b61ab42aec3ead87b07e218df5667b64261fad172af53d0d0cd8f9cb55a65e0dab82c4f0f85f4a29cdf84c5c2ee88ce44b8795bb001c7b7b5927009f198abe57cef643420b711f308a216f2ffaf36308d82a10c072ca1fbd799943048c3fce63c340703924d7bdeddcc7294f9efa9443086ea4585063165cebcc40487c21bd0b4c8b632ca2426cb3723148b7c4fc9fd7341e78eea5318ce9ba5e37102359a80afbf515f177452ae28da8b67169ef4abf393618037e2f022075724c7a7a989922f523f30020d215b2b188d6a790841044e814205b7548e199606537565683be5942f693970319b8d83304f7e279027f4c462429375203518f5f7c118a320b01c2cd621e7824258720a1559c685ee43667484bf94fd67bb1c07f321894f996d8d27fcd0d440d29366a34ba5d9ebfcb4aeb89d942161a57fe9432745c05e4d85e5d02bd22e1d6be361a7b5b8f23f22e1e07042398cf1e81b2f4d06624e65c9f247d989c4df74f5ed077337589d42795fbe59910ca24f0c42bfab0301222794f7b2e8d6540ff47b88489d007966e9010cf2a32ada8ab221e45b7752e2799061371bece8bb253da2e5c2984faa2501a146215766ee27cb3a38843e9f0a31f03eeb900aa55c21ae4a536379b9fee0cdab6fe834c010a38acc3c7effc7b72758639436fa8d33fdec4501e87ba7f0358003113042dd0bffd1b46a03f858349158913954b93f729e2b5305570471ccb2e24aff82c8bea6dc12f6fdd070751595843481845a26dc0fc4672321e58b75db1d21837ccfd8f07650538c5963821d07125626ad0dd4d66a98aba259731f63e9f44b018297f6ee9e4e9448f84d436aed7ad9ed2587869d707c12929cb6b3ff5ccce4945c5fc8db8d41570b6475b3089ba2396f2024b8457655a78a5ec74b80032ac1090a6141b75ddb0d072bd68a7c93195fd161d47bd4b2f901c3fba5a7ebba352ee9e459dce552575ed250e3ec430344cf676396c4625c4513a5df1c0fb9d272223e294ba2a8c5de22bb7cf0955b97872d7bab31bf700090062498b090083d75df8db9f41a11da308a4b04fbac3b3c1309b6df883bdc2bc3ad13867056c51331e666fab129f9407d775025b58f072d7682091cc30355858c3afc7d3ee7a1c3b029b6350550843c174e9afc994574f71bd8e7fb05d07380e7011d8ccc474c3546b22708f7925c5714e46a72cf10db463da51908eb89efcd694c01a2f3f1e10f493e99e469d98ecf2f804b70b164dd50e9d07b8d716da3bdb631a7bd15a1d73187853384dff3d4c8a54de14081b999fc8f250f4d674a8999a6d58340b427d7e02cc540e8baf6166fee897eb19ae06234d804c0ac392836fa79f0d7286b927875a1f4f9a7bbbf17211252ad0ec85afa2a94aac38f67f29d55c70acf684d6b211b4ca8d101c15c022e7c183f1aa4e8cc5bcae3006d6c348f605dcc7efc5fe576dd3cdbf3b7f1c4f63b0ae22c8a7c88348273c9172f918dd49295113b3647a728c4915de1f5273967e4ba6693c65dc0fcdbed6dcffc3818e801d7088225ec4e347feaebc04d1ae33abef50c5489214bd14a2e355c47053141012bc0ef0256b00bcb1fd0a49721e3bc9f9098696094a7d2c43fe9a7f8a641edf296627a20706c0a5f74c040fd8b1059226c9f3125c5ea1cd9ea953fd48f3ecd2f4ef1b820a3bc1558963458ff6f885f7d98d1f6c450aa8232ac78a818a73d24cffd8b01822310f94bd070ab00fbb1a4f252a41b9e61254645c4bb35d4b467201d33423304dd0bc04af0d962920e93c82e7d84e6d24ba0fdb9d4d561e5a36674e5cc66958a1a438c01b1291a1edf561fe85615b919200cbc39959ac8b937f8798a19225fafbda4ea20cc8dbeac897fbe6b061a3629289b03622cf05a37fa85fa6a02843773586f94b75316da99bb6db10aa2ee03287f999fbbb50f4047144b2f9b5615fed2d165155bcf15376d753fef35b8eadea303c44b663f5e537d5d2a901d4f0834d97533deea56c289e6276fc7e5bd4431f6f6e8bc3eac34430b284fc14ff69a0ed097ae08af24c33e7d9a943b55ad6d68008c0b1cbe4235ecbf2a94050bbb7d4e6dc239d26582a1eb58e8c38000c8072d8bb13db9d730b06726add42e8cf94a241897d3beae8c3afba0c3465c1d4f061ef05dda706740ef22fd0db5e1cf9d14fc56bbf6b2065c708049abd88e6c3bbe9e47a41a527a8b87755ca21c3fb03b59396c77da85dbe9bdd876bdb181af2545c4fd1a4925e6927a9d13500e20aa99b0a8ac8ed0ecf2b43ca08626e052ca0d18d131dcf076c0a8f5464daa62f37a68f713b4ed9fdcb81268182098404cf71ff4d405148fda95c59a4c16189da52c1179db7a226fb61738a4395ef57f0744313f01a710a9ef01c14087f220c26056445d53d5d1e687eae2cde57a84c61e9fb3eb7c648c3fd52780c7087e5bf65a81475a797dc1fd88b7f4e527f01d6987d86013a9d4e93e3000224bb924ae2891e1011d27a99516741ed123bf685a881b991e9f7226c7c12285bc8c61db86f7601452265b54ee68893dd04802c4d86b4550895e260cd202cb422c83a360601479c329dadd74554fa632439c441fc874dcec59eb56c0aa008d906725126a930558e7a5ab9c6671b5e5bf37336bbabd5087515ad970e9e5d86c40931d219fb478e91f9b43eb3a322030bce84e608bf6ff9e2d68da4ce00f10310dd8828559e751c2d0f96e4c9a5758eeae60e968c68f64244612eaf5eddd970553c3712db89f5935465814e9f974e5e64f73c7a13c96acc90e35e5a0308858f7701163a52efc41cb7af9613c6eaf0df8ecf950e3ace48af4273eb8dbb63544675c578ea24e7390bb490ffff0d3ef4928cc74c121d54e592cad248b20e9ae2f88eeb6eb85bae19b95026c6495fc11a90cf3dbd8efdf9d9f0a370d0486d71a7db383cfdb924d0f256537360bba346ba3cdd736b519a11e3fd93125bf098ed44918560058e8bd54753a863d8b200ed7356bd24fc60bb89fbdc0d5f10d0b5a860fd1f0890a4edd65c65590830e2de4ec130a38cbcd4d2df316549b7eb4917935c0e78893a32497663a70ad0a1b7ef23b90cd87210c63d633d148a28df54b72899b7b174fd940b9e54fc8d3d406257d4581d8d7ffcd3af448b532c5204896dbedd8d24b1fd09a4ab3e89dbabfe79c3743c84a102283ab523441f44d49908a255b1582cad7b644b5b456b6dd828794b12a6efaa7604b138783f8491c4e53d2dc91443f1bd70755e296afab4aa84fbe3562276f9f5e93d2660a1065a96ee55c15b12b765046d5f0145e7007ea0c2aefd1339e83f6566af611629335cd4be69a71f20023a73fe871a50606c8ab98876b6b5b5c005761e99a98e2a7a6e11882c95e5c210bdc4618e546e5291021694270c50f171fd8540af41cb0d02d94667adaa02dc84b812560e014d4d870f0b425d63f54f42d23d5900379401ddc307d925e29cdb6cf6a097b0497f224587acde3a898dce8696ddb85827cd17040014ceeadf6cc9b66cf35c53b6d236835c63ede93f2ce496b56f9ae1b9d9af0f666a69e903e7ea4716f463e154b22bf9e933e1d3f603ea49213724925560993431173970c6c9b2417cace14f38e083e42ea59594b9f7088b23cd5fd3cf0548740ea007672307e5170384f50fa034bb577ef518914db8d70377081965513d2a87c999c0c3616ec5bd6538a572fe0d7a6fe944fceddc8bca9a7ae3e325373176fb1d048992ad6fa29f2114351131b3ff5f7868377cc53bef41c6de8ff173c54bc5ef1391a0a319a056629ed4011658813f399cd7ec64a838e8d7bc05d7ce18b1c68132ba432778691ef43ab0474f42548a5c7c631bfeccd6e9d3dc40940b02c7b58f7d3ef310c3ce1ed74824747d01bda61bb7f9b91644da9761ee9706982faf4092f99550d43ef378a5a667411d3546c4ef4af2c6df1b3b67f48c6cc8f86b5df394b1bf922825ce8137b5874a3e5ff3aa69db4a2761271a97d6743b0ba9f9989483a138fc06e60903fbefd205f20c0d2ba8ab18cd707d8f545053eda7582edd37afe84625491c75de97242ac87bd7e54b63530c494b6c235fdacf319d0f9c16d7c2e5047989be5aa85549ee24e02ced025bcce5c5067443b924a9af69477ad77ac52590347dd1d41dc578fe6acde89c944d2e9e9c66749d9ce85580969a4616f370a9cbeaa5cfe95bdb7502f5d2b475c5ba80d2a2b4fe5c411ea607246954b6735aab1c29eb6ea9f5341534c0e96d332eeee522d251eb4d4b969681756b67b8bc000fd20ba6a6ca6efd992db8fe4a5df853abd27eced2be471a6f56252e8b1f3c5b10275e85ed56f3b420b2e9b800508a490104baf42c8cfc008a79b7ec2529a377027602815ec57737ad7c01ca99b6625eb8a049ba93c82ecb575299a469fb03b8254e861d0db02eaf64872fd0254c323744e59332cce6a402f0e40aa1494ff699b293081b1aaf0ecbd39f3baa982729494bbc3a4516b38bb50ad92f41add9f5844eab4ab49c88ac27d13baba2c3086674d485294de3936ac39c9e72cde129830e9fff0ff3e4108a3a874a0983fa1ae9a56551d189d8945e11a073a0a6e92329f6af61459408ba224af8c2cb49d102fdf366c99659e5288deaa97eaa8307cfb9ea3deb1f31e8b2b1bcefc925339eb8fc48b7338457e236e3eb4914167065a47153bc64369ae10661dfe1272ae9afa411ea1b7de787a370929829c9bf7ed7348e5c31ef19a1f82c750364c91e41fda0318377814320a0fc5274695bec98b6c318846c2f6091218886fc5559222046848749bb4f63348d87d0e28d6e205505b67837eed442f564d690899c5cccabd71180cee6026e804c717894e0978861b54a692b2c7dfed574c25561051f0880b91c2c5ce44a7e10704191f1d793e4e5b121b95aaf7b6d88fca61170460d1cf9ac106410c78da688bd9fdfb11d3b7543d91dc640b0254da0f84920e0610060f6f2c900b9ff56966bac9d5ebc7e295306151d3a4928858b5f423f9b888a98001721de556c59f924bdccd8429d8ed95693d64eab5bc61d71a43f9098ab6d3ad0967f4eb1d507d85b70b699dac44d4f69ebb3cafec6da74a80233a74057a56d26b496aea60901eec40c5c6d5038d2abf622bd428104d816c166b54702127b002c13d7f180bee4a7b8412ff36ad3c9fac87612e2e1b95457e09b9557ce8b1474ee36980e282b2698e29ee2df0966c575076869e98fe08ffc5503533aa50e0bd52c3c81f460fbb327d899e228462cb3142d29f68e70d92ec5c43114b863749a1e87f36944a5144e23f589bd046631f4c1b1e3de3d4d5028776d76a685f2d83e95918faa4b04a75638937b91b7fed3ff3e865971724815dff1318c6f63c532e040e995068f77c7f1262f4503dad6e1808a1abee2074a5b57206f10891c02b6e8ee4c4fa9f6cd58e92a769a4ddafae17f69007d2e4a3e872ba54405ed75e213e821162fcbd38c6e23c4ff350ca23e9c90610f4a75e2d7d8d5d42fa191f4b2571186fb594fd5329e918bbb1a682a766dd79bd54b7228e320c7354f6abb0259836449aea68335992a821d3c0a647e8aa38c85ed983e1a2bc703fc3bec4ad84d0db4b4e68d099aa953513f969c3111e027b149df938518d998eb262be9fd15edc227f001678173081cdcacc9e82503e5700636b33e04a287c3abe20ebd07eb7d68f15dccccac2456df7393860aae8d097f61a788229ddb60b3791b8dff0b5c251be621e95b84ac455338326500153b7d1505704994371ca5955b0641844f6cafdcc4aec5f6b8d27067db24dd2924f112fc111c0aac0b16492b2c959f985d8846f4d13c8d8e51984ae4fbee65dfa1d7a4cd28b366d258a2d6f51c1b36a4b7e7e543d56c5b534e7e68d6248d5086761b533d6409b8f906c78dd6001cee515720559e6ec5bcdadc55eb99b5497b5dfb929f7e5aabf49d152943ace17c12c569a53f43c29cd5f57facdf4ca6f177a4128df236aaa825e0b4a0665a8b78f832e9669c6c5369906eb6d3819eb5e8232a1759ae60ca101bed6e05adebbbbe0c18f5b4c59d98b7a194a4be018f8e05f60854e4e3e9161bc06e06266874176891f1a3f41de21d2b0626495a7c3c16899062a367002629cbf8e408d25c76f7274badfd088d3d96c6f05a683d172c3919017137de46b65d1ec95904052716dedfccd32d1a4c50db6f52a0cf42dc31680105e948eb7997379cfb6e14b2a3ea0d853e823f839b8903625432cd7acfa06430c175c2e6636e631aef4c260f883fb0244a31c2418ce21905702d2baa354568e957d34ccc802d8c042517487055af30f04cd01f5d2cc653f7a732eb34dbb3f2bce2eb64ee9526b77e0db4980fe23e407a8a51ce36a7e791f6bbf55bf331abda96697a450666d0a372bad1718d202363039b2e0b699668293e5140ca81e81938c218d640b8f420863dee6a1991902b4873fa90f2b969e4188dfdbba61bf722387cb99833916c662ff13481e51f5599cafd31b369712dc8e109e7b244649b4c3f5036451a030dec16fd9a4dbb9cd51893141c6b06429ac375b382ab4d0b5394a05e730a90c031c2d4cafd17f43abbbf86947da3029a0d8e3ea3327026b205562c5511761389d33d539bd8117bc7909e7041dd6522fafd09c01ae53e0dc53c7ec9abf2398d9091fdc49a52ec12f2e56350dbff40a97445d08dfe7bbc01fbed24e10335bc61234ef0f10a1873eb2cf22673e67f351014f48183a0470e7de27252fc4613e80466ad1ea1ddd8b41dea52d582e75d88c29ac1f753acf2be29cdee452b15c2da709e5c0adcf1770210a2dbeb0be7fa4c01754ec84fb61de21bd34567b3c68388678ea553b5a23a99bb4b49399f87fab4733a057102572878821cbecaf5a2192d05a1a7bb0ad0716a85e41199332458a3eba82ab5ba94a22672253ece07232994ad1fd0689d37c7bbcaa2b32353b52141db412a1ace8887e0b1b9d931f1807284130a2d8264c9fc2a94c47ccae0e205762a92fc0ef4abed72f4be4696b0fc46375a0a51f6cbac15771e6b13fac02cd8c3facea81e303f5a0b4359414c1360e2e1cea8874b84b8d32f40181a1896544241b681f42e1a4b37e03f367dac85a6b6e9d2af383cd3789bd221f808d85cac9ee9f22f16b7b64dd4f685c300ccd306ed09b8f23a93a6699ed8a88019bb6aa313dd7d9bfa7a49c75c1935d123494071f6ba68f67104d9479be9f241096e5cb4495a9e9172a90bc120d04b21af022f8c8fcb0860ca647875e09c2ebe5c1b69970172870991f93b5874abdff06ca5128fa93911f2d943feec0483ec021e0dd988c7c5a971ddead8225ae938bb9ebc4ac18b43a5342b5e39a223b30df162dd066525831da8125003458c81660166b5c54858620a7e27585594c48edd7e8206f125ffac308f5e02f31e3bdd7380e119f3d2f81fadf21d946d40a377e9629eb9f1bd7227004788e3d3124a07e1ce43832eef9cb8fe113d01545402dec0317603e45966d9d68585280a4bcf93c0239c65bff168812d227377ed85a0ea5a6af6afb4e84c30f59012d213b63d3b2e0c74556f2e5f03bdf6c66aa3302f47898245f92dadd5a9e1c84a768ca02ebdbe83bf6ee7d6399fbe6bf9f753347f9abab077c0ac7cd9a0aa0ab1ece316bed289259c500d3e0ce58a1043334457aa58008c1305272e2f8c185bc75077f5209e13c841a7c3bfb5be90af1053cf382b7167894979150932df731c07619207c066fb8336285c6302f2e9184968767d4d4164276fd274cb4ce28ca268521357796cf5b2fd7a8d6055da38db9c7774baff9091989a28141a3d5063b65421fb307eaa9a1496683a14841f9af46a22dfeeebd4a0485407f1ae1b643debf66c28dcd9491357a95ab46749880107b4ec779c16799ecd127c78dd9a39edceea8a5a31de25bd277bb8f51d3394b603b26329fbd897fe5cd6c48ef8c7ce4a172b8c099607766820bfecd64a5cc3f4d36a9173ec2ee5d6f57ba9e2103f0e581cd62995834589d5ac5345486bc9c902167e1f4d66d236f37474013da25d09b8d1b95303979c58d81dfc2d02c5e6ae31cfbb41b96ec28faa70d508b400468679b932799141ade97351a5dfc0fd25a9d97fd262eb0d02f9ac0075f22bce782d10f4164077c6d1b1fb8a947d6d30766ede165be5ba0b308a10f4b5e362d609f4aab4c2969e5b942310b4a33ac8938ab02592dfe858661f1c19509df4f06e47b21d29b84300c16b72d3bd1d00bb3d38dd37985b559bccebaef28b29a15da12e9adc3b2d9353f7cd0c6f65956e98ab6f615ab238f01a51a1904861e6f5b60a14deec07fef3d104909c185a1468c498475eef0f1bb02ac6ec5b75ddb4decc88698b4d11dbccc1e367a21cc53b5b683cd48796b3080a1e6deca281ff68956c819469f0ad0dcbb779ded3b7d18c8618044d8071137cbe862be7b36ca972a542c05a2a30d83f113eef050a056fd078f8599626a321d4421d85c79f5c16774c429a3026213058ba2b45188a6611e3e08073697146bf1aa8123c6ac65b3d38081f90f10abc76e8a7f4e9abd9a814e2eb2998f03153673680af8d49f943137a9ea61f16619e16e22d63743b0be7d1ca4889dcf949861bebc02652d00497113cc07c48ee209e437077bb0a8e2c8ebe536b9578843b4fae1f16dddc9513a5d4294cba8b9819a2056cf6b31d50c0cced818ab2812159f37017d8d55937eb605fd8d134ecf20aa4dd1f547125fa406e11a86bc14d089592afbe5f8048464b19d087734ae2c4c88ee494fd56847b537169c2b5fbdc8816eb0ab5f8705ccc5db323d71d1414470a0261650c3306cdf8084d9d5157595b56c07773c732fe7b1a3229b9665d13b3a8f53367d75be912224744d14a8ebbcab006a81466bd85bd0dbff24c35055077e50e621d70cf90b28b9cebd400cef29a1b536ef9361638face3fd742a490bb45adbbad4a6bc005fab7844e6e0eea8a3e0916210105d3d36a66efe518572cc18ad154e0822bee6a2efeaca85922eb0d05bf56c70cb70e08b06b23a0dac0bfa28cd428cc8ada00e26c7682f5a26db409232433adc4e29f5330688682de3b2609e5d03f09caa749170a6581e020772a7950004a8c77a1ff69d42109bf0511922f6693927a65a0012a1687d8459eb68b341282360da20fa795b4c461d367a0c0bd1bb71dd46e94607f6fe55b496ece0ea7be44a529217aa47a09200eaea0e5a8d89064b75d6434640cafaadff0e9e92fc8d2f1e2861636815a59f028dc2725523a4851863e4fb1cdcfb628bd16b16bf2664bc32c4b2e681f26595c204d57d06ea15a89b1cc1d817f568550247b27a3cad850a038f808d8d88a05fcbde64e04d3adbd7763692850247210452c30d3e6674c4c7a7d4b5410121d3f3c845cc6758f9bc2b2e8c519a5efc79850ed0ed736b1d5f66d1fe5722e4d90697e0e67610b86a6ce0b6e163d800fdcb085c122fb197c70fddc7a9ca4cc0bdc9f73142966e473ef48245330df44c39634ddc0784dc4ea1b03c7076091cc8d35d3c5c0582361081dc21a7e95628ef8afb0372a061fb776f2e85e85205c517bbecdc2d353378b8964203537e5190c3c47180be2987ba262eb3d59446761171f69df24ec1e3c6f4f122470bd88b2b682a7b9eb14e37356f268457c68ac5c3c951a3ecf90495327b6137f283a1a831fd3abaf3656ec42afbc2330eb07a74b326cf68403a4fabb21040680fa63c45a208a98ed53a5211b4d0ed88c28fdd38f5758e53ea118cca426462aa6178389d18bed9ff13a13f6827b45df8a246e2120f7abb59ba1476c25860295ee7f12dec1a850cc0d3067354ef7ecbd7bd43a359b0226d0dba72c3fb9452483186012d45264775d7d46e66600e1856ae6028a035e1a4fe0b256e4a88f457dd8399e50c7a398954729d38d17e06d87cd6493e240f4dd1d745c4a06dbafc4e191338ebc9ce880dfa91c9b72f772a2752cd0002aa7068b26db449669ec8c9025201a19cb0922e1d5443bd4953c074836f3a4234ec45b3e676a13972bfaa742ae482c39d67c072e7b6e84d8156309433b7e527db34c4fa2271e2665aeba266301885055bf3c10ea0fceaecbaa858903b716914ba59d83dd3026263c79dcf13ee4dc4f883c0c6a02a12f392cf5267ea86beec99c899042702ca4d4386893b14c40d2482ccc0925d174b1e1fc7990786b0200fbcc501260e648194c3b3ffb87ee8c69b00c95245c5f4c35dca94ff468582691b40725c1fd898074ce4a4201296bc2b39d3f58889db25be33df67ebd3ab895d2797c97c4558c41749a507797c41e57a155fc1749e46c8266b9ecce9e6312ec47db26e8d248116cab45212a651f5d64534de8229e723c72b89da429036050e9e6e17f1b6763c09202408687dc2a47cba95994997541079fe8b0a2dc915072e64601a6ede530bf93f09b1f73dfc0b44fb5c0108a50f27aed46d36faf2911b6ee3bb6ccffb6dd44e887e2625e602a4f2cfde0904b77ae7933e4e68ca6a86850b0dacaebd9f1e2f4d54b506ad926abf4340a4d7ff808df6fd9b40121ba78e0ba200b286903a929d81471e31381e07dff25e31aa3107763c0b8a26d87cc6bad38fc01af16472e8a532f7906460d8f2d3230667cc76c6d80fd6e37b79709c3ad31aa0b79a6f9bdda008670e1bc651441766a23c8a05e1b02b1897d7b8a68bc6580d136365a85b7ffdccfb35b00b8a9179775c52fb8b7b0d46a9d983aa5fac07cf89756ccb1f078539f54a967166f14f7781f70fe209fe69d0c61d9c66c67e3f558daa0ced6de20f87e38784d287ab7e2f1f939e25de792548fc13cb88c51b77c423cd81b8498f7aa976f4270101c363ea590db1faa9280e41b146a60eb40b6913983a565cfe4b4bf6d4b9c7f3aee0db224d000de2bffba87087d432db11f71211f3e5df9da93d3e2a12d5c633ba97e5361a897c5ab4cb3c26905cefa4c09b31e2cdabef94d6eb7640b9acfe9306565cfe46c2e545104ef6c7e8045e7c1357c21325ec0d1da3451f2bd9d4e41eaedcbe1955b7c41367ee87a134388577dbce66b7fe91c4136375f6bb31e87a5ad77682207b15efb721f3638f8924600124f05288c9b3a21f78032152f6a6b48b395c0b3556e23ffdbe70333bd9d301c843b1d0a44df02d23bd7b03da298f3d107b09921f0e07f3b92425330bf69faafe3422dcc43afb1a12b2e090aeb13e4f5dd90c63a7668eba74ea5792f8831757c00998b5194148ca6a62c8e45b543c13e6d2b44c9f7d0f98248fdc82961026d9603fc25679e30154987ba6d50fedd5188045d9cc0d610343ea7931b347f765a12b05fb9934045033f1807749105d8db240872f4db5e2c06ebad2ffbcc2e26126ec42252d8fc129484a16f97a431553642834509733af9aad4b54586c898e4cd91ed9cf0673cc398950efe78623e0c20bccac83f71eeef0f8a763da94b08acd4f7a6502e643f40c777902ed33cc8a3f6b919af4aba0ef2b68fe5ee70f15ab743d67023e5ca13d659bd0f17a7358db4e5ae80455d5c017f6ed3f3971876bd9be56bf2a85c15926ce1e145fa11114756cf2ae51bf1f2e63444d6b204cbb8b03b89e393b88f5f958ba8af4b586cbc4c9be238e6aad69c4193742d6ce9291ce9ad0e261015b2a88c1c4e59304e75735aadbb48612f64ca373ca8411b10e37ddfa5c23fc5238e1c3b19970a2b5fa265cda612002c7c945ac466648062a0c1bf772d2becdf2259ed5a2f1de5bc4ae33eae24aff48e155e96c5ab4b113d825138cb70606501beffa4a15e5d74073a2db77bca2efed6e8a068be79d29e9353463c15c8559bbb0a8b2351d3c3b81c6a1908ecfae7173ebc18113f979168f7e9fb1550e56177949d618c58d3f126d68018a7c4c36c165afa010458e1a19148fcd4fcb87e2da44704fa4f3bab5c30145a4d07998ce5c354b49dcbe3d29a5a269d22d7d66deb35b42eb7a77eb363b0228c7b210452e64f9cfec0873e047127731cf34677bd0e9c838baa691761bd61abde0f109832dfaae5f27c34d6a06051486b7b77db726f29539232540ace0a630a2b5b63f498b596bb775badf09f6084db46f34663fa1385d4714c7b9036f469764c874e38e2faf78ff42729a963edd33f2d6b2022229a51ed0869ca6690d0d04c1ec520011b666cfacc9f9c1c1c3b7ae41871479be9487f327d7b57ef30398b89384476e8bb91dca1ef3387d11d218a349b03429e320803a40ec5530769d3df30397ea9ec35b20d153c16934191d98ee5f028663ce2548cc35d0efbf0ea520fdf0ecfa8e0e2d8f0cc3a12abe4f602244cee5cb185344278e4ce21211a38dc117834fc941c0c7c6bef5dad5c7e39db1652c0e619b82f9220f10c934bbf77ea7b2514ddd982f725fb43bc2ffd90ee2dcb47e9bd2fbd873bfb1d2e6169b175d7c491b46e2af3b6f7e6cb7f61de9878ca80489dc6330869b33d87236fb07870df7d67cff2e9f752c41d25510fefa7bc418197443f65c79d34776ddbbfe0b012163fd33d59bb6ddb865be81d0fcf99216e7dcae11390a87182dbd34ac3a68ff7dbbc62edbda00eee3bdaf6d3675ec9db0361c9db759fb217a7cfddb66ddbe44e28c40d62c211576cb105b087f7f73a6ccaa46b7b6b370e6fef9542ebae59caf2715670fb913622b44b49ccb6e6ba7d2802b891404a92225f0c7cb626410c5b802007d91757146104992c70b0c2050cbce80112b4a0527ce0a2a1b03d77e87aee6005122c5cb22c42f2446ec9b208899455a783f77ccedab2e240df52aed65ab17feeee95d659c35f407fe28f72fb277569730d5bfe5a6c23fcbd696d2bcf64cfbf2e94c91efdba9095bdfa75614cf6b6af0b61648ffbba70953dfb75214c5665aff475e14bf6bcaf0b532ed903bf2e6cc99ee9ebc29bbdd3d785a7ecddaf0b4dd96bf9ba10cc9ecbd7855ff6505f177ad94b7d2f5f1776d9537d5d68b307f3752197bdd5d7855bf6607c5d58b317e3b1be2ef4ecc97c5dd8d99bf9ba7066af35d3a2a99fca14b3363c830ed40a34df37d4f9787e94d24a316d91d2ed6b9dd551405531b8fef2638070fd37775fb5986a517edd2826929b099d9712d132ab2f85f36869aee1fdec479d486e1aa3b925c5a548485b9fe2e64ee0e2f6cfaffbfb32e8df9765bf6aeaa57c55ff99ecca385a08fad57bfee8fe94d66dabdb4b1cdd656fca292735d52a324b9e3dfba86b77d3aedd9614b79fc6a06df7a3905c393cde39aa90e7afb2ecb467c938fa42a96d72ceef580d6340923124a42c3d9182dada90690cabd5bdde1fa4c683d85793722621b9d73ae79c73942185c32bbd92c74f4c32652df64b09aefc71be7455e92fadb532a6a4c689156d146fd09e34d440739318ebc7dca93f772af6f976ea5f77c967f9cfcc3a65c72391dcc402155b77fdecc2f3bafb4a29e7fc666936c54879ba1b7b373fb8fd5248daf4cd143753dcf8a0c6dff88000518a6c000829638599fd63fe857e18ef591c13f332312ff3ac897fc4bc0ceb078c673d8c67d51a0d5cfb758993055c5974a48b3c73c8b2c8c816790723a8ec6084fb2678f148245f16c8026740ec83a6f53438c884b530ab9bc1ac6e06b3ba21f2a8339964fc8c909a6f59158408bf84ba81394aef2940e61242d60cc3d6d3fc8f99c77808fe1971c8bb207730164264a78559d347f8ad9fdffa9fc1425aff2f64e6c3dac5e3cca6973b3558bacb11d07d13166b3ecffc1f319ee669b010229ed7df04758078a4197c8f559a0959a52f95b08fd6d36021257ceb85c8f89927b22341560be18f7f56a9f5343f64063f919d1f345ff3357848eb6930ab8469f041f3180bc97157adbf43eee0e81270e963e027f3e58e0c5cf212eed1e27f2f77c050e6d61de28e32e97b1b4010cb16c1fb63e65b3f837fb486d0601cee32e116963b263cca1a11108fa5ff7e89f92505b4585b40560bfe320fc45f06b380f4b3f0cc0cf84258a56fbddc99c1acf9342f7740ec03ffcccf6021ac1266b5107eeb7fccfcff90d6fff80f3fc443667e0763d6c44f83870c011fe3fb3f688680d703c3074d263c7636fdcbe33044a1b08f184f0386a56781d8470c1fe16356e94788f1833f4f6f7a80ea1486d8745281e12964b15ac04ff33ff0fff889ba1e1e3ba33ec5327d8ca7f91bb258ab373dcccf67b1540ffecb97fe3e0e592b13fe1103d36016f83160304b855b88f134cf02f18f18341f3e7ed60bfe113efe103f0ebf61cba31ee0f2a59619646e7109679879e251ce9699670c2dca84a17d56685f82134be0c9bdff99f048249b7e965e3af8f369c0a720eed1a2e9f4a5f0fb1b8e1eaea11be439bd770a4b35f8ec7ece84ac39e79cb384470ce4d2cf70b4add0f43398059a300b047f060b7902621f334f8385e07017f8332f04dc99e1875f903b5e49e2202d5656963bca208db2fd69ebc784720746287770f8341149b52a09504e94a25028144ad51406068f44f244499a7d1caee2662ae5a1be460317f5f463b4b8281898944af5425e1ee5d4b75e857dd0d0a8706a4e0a5a87b57ebef45a1fce2009f2a872c495b58ffa71458353293c06c93327d31818f518e3b80bf5385a4a7fad295377869a28262da7934ca70cc6a3ecafc2710a8d32c89a0e59a80c65593465c9941b321330c7f631ef12c6bc3743160a8f4482568fe3309a8781791c0e83799a10e743e92e18cf92f9182f775a3f7e9e333298d5c23ffe1fad8f8187d07ceb311ef2c407cdb77e48f82f669c8c7188c35da96f8538ee4a3d4d88e32e98ff3035e66418d54f14ea67d8a3451814cce368160c663d0e6785f655e178736a944839f5a9d4cf9048f65621057950dec340a94279bfe55b212bf5e394ad302b8573d830c135fdcdf956bfe6d351ce685a3f811cf64f514f5533e8f412b42da6b0a79e26f5ad30f51fa61e26a44067d54b1c0de6001fe65121ccabc2514856a5469a51ef2002b815b62da2ba57955cde419eaac2a3ac65555086f954ea2dea671516ea5b307a14099fe65928cc6ae1bff53efe5b2166a14eff78855b58080d66a1708f22533683dc45ffa5bb508fc2234e9e6f23e8c678fc25bc90273e5a1fbe90273efe5b2fe4890f19ff4f83853cf141f32560212f7c88853cf1113e0d161243885b8f85c818a7ec4749f4a33c7ab94383553f4aa316c67197eae5cee37797ea670c8c8910c932d5a345d5c3843248a33c55ff1272d816e97025cb221d92e47116e950840785472119158e34a77e94443f7172eb5b44763e9c3b611886447668c2e9a3821097e65bdfa3080d6e61e9ae897178f7a8d093b925bce1388dc85428d3378560387adf79b8c3d257a83fbfce8914d32b1c3cefdedca3b294dc512209e997046ae8570d1b6ab458e5e4da478ddfdebd46769ceceefd9bb51656e4c936f390ca53915b67873d64a8912d7ee1e78eb216a4014d80eeebc7506590b5fc5776655c28b270b8638e01fa35d6d079cb51a1fe0eef041f5c58f7f2fbbaad84679176d5f7701a69f1c75db5fe093db8e3ca48bbead7b7b57e2de18beb0fc41179a33ffdc075e17166d77b9e9c19f47a4a2adb57c115c0d583df87f3311f53f3aaef8b898989514da479e4a556dff77d48dff77d156cc5e08ed3c7915a31b8d3c767b5f23e26f4bec529b36a71e2f083432c8b38002165f1c5a7e5180e70c007e49a08e535d30bf8f2792f251105f167163f0cc598989897a04a15d3a06d31d5443a623e7ccff33c55ea1be4f126e8799ee5c01c312f23a59c3eaa4d05e68879eebfd0c62402f2787dcee3e85438fe2ae78e01a34ce206d9ff2493a0616ef209a1ec93ce49f3c9d4a6eec658b264cf4103a2f68706945b3e4f56b11657e6ba7932262efd96a134cbb46ba6009298a66902bb3fc21205a99bf334815c9137a6dcfe0ca4e6b01e2cedf2286a8deba53241d91fe469a9954b41765d3e85216e4dad86d62e9f2e26ae9c123392251517c8324f4e094fdcfe31c7014b5a74160b4c80b2bfb300d4afce0ec423894717fe2c1461c1c79f851f169224657f1664fd7ac9fe2c2ce9975ca209a0eccf42109d0e009205f56b94542c7100130738e957a5c17480cc014bfa35ca28b35933a586e825558bd5f470c1cfb34cc48514fd2f0fa4f766b8e5140adb58f51cf15043aba119716b68996be1ea73554ab9a3d820fb4b589ec98c8484f51c4db9c1e18eb2762f0f2e782089220e4ea908f5f028a5f03122c5cf298914b2ec522cc9ee0501794c3c275230c91e508b3e7b36f6642d7aebe0f2fc19569f1fe9ea39029a228dd41075ed40eef7a01ea017d7fb51c2d0d97043f6dd90b5d8dfcfc0e50f869f161d06232d7ac3e0d3a27758661898f40b06188ab488420e77f46cc234ccaf148e528a22520a9f7c6f142320b57ef595232c4090b2004902d2050cb1262f2f7393d4fbc350a45f271f23fdfa812149bf4e5887cc321896c000d42f8a9ba0de1f0626fd6235f698b4e8ef2d9161e97679b270886b0987fa157343091c140d85593f548f7ad48b91c3583e500ff3303f5243509f7ab9837a223b13f5e3057bd89bb96ba65a5a5c6a475e2416f3a91dcd192486574d96efa1bef95e92c3503f7ff2fc8ce449e96724499ecdbc17eccd584979fe0b1e3da21ea429927af41cb98b8a965778d4ae9576485ea50da4d6af514e410272c5290fa47eb950c48a447da9971790e7ce15c873bf6e3568144b6963fa5a8396fd4f9b09f720b5e87fc31e2c3d4732556e6d56579f97cffa0bcc475f2607777b14a53f6eb1148ad60d66f5c1f8649eb8b53f266dfabd188be226b0a8926513588c009065135898e40aa3fbf44d373ae7349dde9b734e9134ca29bac8b216cbde7354a3f983d10303e4e9c1d2a22a8b59fe0d2a7794531c4d8114c3f241478e99b419abd00c93f5c97e8962167375c105d44b77398f0a4a74d4dfe836b76ddba48c69ac404ee9a03a6c8bb3e22b23333333e5fc36c9d6d773d2404515945cfa598a8e26a8a70e43e1991918ab150c8c419edbf198617e912930837ff1c5045550724bdf7d3fadbfe1e89e29983ccab0ca52b630cf40992f6cf11496c2164c7642ee150f2467c5e0cef729dd4d8d61345f4fd9e2fcafb586d8e22f648c2fcfbe12660f2a0cf54d07ee28a3a80589c9578d1352bc42be6a683d480e4b7dcd277bedc70999f74b4ab1f15891e47ef73c3459134721cb1b1e6b50a62df3627cdf12a933a54dbf8cafeb41cb15758b3c2514257c35337ec60c9087a3a20940ac485d2d326a1545117a8ebea3206e4d30c1041a0da001eaa03988b4f1afd18184bd601ea93b458d153c90909830f3645b5099695bb9d22fd30c8b294a1747c4b2632058808cf288224090801c01a901b9d2a2df28baa3acd520201a353e2a6f72b8433e947dac31eb7ec199a851518c5a33b83580b23fea533f7a403580bc469037ae01d42f54ceb31b4b5c290025ee588334936d91b2bf8d0dc85353e5e6e78e9e2c4b295f70cf91bbfc53d6de97973c8a20612fb82775a77c911d53c101a78606f2d45469d17b8e78b823cca706f381197199bbfcadd0f564d953c84013b99fd25278d47faaa7201965bfa2240348ad3c710364d2c6e702f5cb26fb7b4c1a16858a939c42f14072180a6729edf22949f966071e729661aa1fe51447b518c220ee6ad4a7de5937b8a9b067863da8b027c95ddeb8e7a8c59824d4c61a5af6a7dfa30cab5cbfc54c946729195cf125bcfda8efaf31b23981bb39926752274524753aabf209df09379fa4b4be8ae88e328a5acf11ad1d865927889ea31ea406c8a48e8a0749ea9cde3f4b99efff49293da8c116d92591114e8b27dce196eb46c3f66e3e0fe793fdb238287c37462e0f520c0a28803c5c0c00be09ea287de3f186121701decf921494d05c73ba9936d37b9e9ddeb314222afdaa6fd4affbfed94abf2ace44a7305369f1e22ce54433ad457f5398c1301bb528df5e8765234f85999689dc95672dee70bbef7b52007370991691c759b3ff8534ccecf9c8508570ba96fd3b907e0d5914e708a99cde079a4e7d5b9cbaa052f54505b38211c3929969d17cb87138868c126aec0cd1654247a386cd093738b23fca2c7b9e39ecc64f26aa926b194b92298ea264c92ea350c95ee48125799451ae64cfa38c72943d0a52f62849d97fabb4e7a8074bbf3a23d9a910ba3d9bccc0933262720a4a563545bf16536072ed8bfb9c927165defbc57ce306d7f3c95dc3cc9f0c63fb93a7b724cfc769247f2edb7ec9642985a4ce5057933aaa2cc75496b04fba52f4351b4b1d2f668d625315122a0de1ae26bb3d549a499d23a9a3a24627244fca18712bcba71f874bb2cd7f3e30fb0896aaa90651f2785a22cba61a647f1ac224491b2799069a4f4bb27f0d51442dfaeb7871fb4714914cf674bab8238a28053405799c31a2427926266d5c4776fbc719235af69f319aa9611fa9a3ca8e22ca3e93718d084fca10401277ac31d616d77facb1ecf6c33a644e7daeef620d1c0eeb902a649fd87fbb31e5caec9fcafeb6fef4073c19e4ca221faae4fe79c5bb0fa83fe748411e200f0be4ea74fa03e6f70af3c869fbdbaf2c3dcb593bede4388ee36aa972dcdb0770b766eebd7636734ff303b8fad4e2b9235760adc0e24163b15c3f572c2d53457fe92e6677aeaefc65fcdf57b5d64a25a5a1adb91f4767dfc29adfbd56fa8dc5f356fddabc66979f2292532228f241ca7dc0f68d2f8ffe5a9423cd75f2e061813aa9142b1ea9ac0fa0fe54bceab552f1675684840ba0a21e5650a9fb6084bbcc30a11ed02c151f5891c74fe80737e411c707b5eead521f1421512437a5947a08ae406319c89ec70b64fa8ea5e4415fe207f8016456520b191919191999ee362fb4d26567dbfad372d972145cc1e6effc56aefd6aa052a4d6062a843a0a5c6635e5a4ee33db03125638a14e4800b6176029cba21e4a800324900051274aa438913b370a50361063c1076a55be903d48410f412f18a20a124c8e6e52a7031250e050ad7454b8954e07d5ca8deebdc84b6c9765510f3e5809000b964082021b3401e58824274042104514a5204b111fa0229841877082225105169523f26e309d204977829f1b3924b02e23afecc5e1460f3aa4b8c8540b8f0b4fca18eb4fdede731fcfc1083746f3367b623a9b37db6f8de911d2667bfad3bf3dfda12fa03f5b6a436d2e5b0ba6474f65ba901bb99043e1422ee4426ffaef4b75868871956707354dea70bf6ddf4dc817bd428adb6f2d98ceee8ff6e2b14607a54e78b4a6edbae015eeb04f8ece1ca647d266fb138ea0b3a4a48eb54fbf3c233f9da45f326f20bf8200ee2dcdad1cdcd2fb95120fee2dae34b8a033d68f212ed2a826755ac22137b43cea85a030eb87cb10d4bbbcdc11d2f2a8166cddf564db36af49d7f676dbb66ddbb66ddbb62d07c78e1e2f04c931e26e6f57bfbd88336fa442223bf488ce286da3b35c7fbc3eed2047765dd7755de7799e47c548894b92f592581981bbc23bc27647918a2cab7e9c7efbfdd504a62e281355fc7c5ed8eff7215312da3dcff33ccfc3f032e141eea7f68b1bb9dffe9c9ee8adbcf74ad86209833bb3ccb308d9620e8f385b75ec92fbaa80f04e40e276de4fead5efceafe10c18f8199c043946dc91ce2811a551235aa3475ef39a1f399227d118f5f19807491d7ffa1ef312c817ad98fe04b320df6a131226e9aad6996b8d2261a174f9e7b4b86e5081dc455f46a61b664956fde7febd0872589df96ae2f1d6990cc5dbac45040742daf8534a931c460329c6c4654eb2bb7b0dca5edb8170992ff1228f9fd75aab0321759aa707f51a6bd1a1dc20d2c6dfae9a7bb73fb664b73fbe64b73fc264f720ba854dbab75c57abd459a5b5e844b31a8259aa461bfc29ed78589ef04c5a4873c395eeeaa7f14c1a0d072eab76f7f347177a2f926c70d8a4c9499b575adcf0685d68d2f0acd9e043429336fbab4c239a4c9ab8895d49a27731f79855a6118dc60bee7c1c706f59f6c52d64594c838ffafe34b82077de5ddcd3c0fd0c2ec89ded1d0b11b2c2c99cd7973b746723b25343223b3638acf5c3fd8c19e124cd4912ee04b42749702f99e4712ee1384e28735fb21cedc992ccbd02fad5f190b91d3267adb539fd92b413042173af42bf4ac8dcd7d0067771f3c75d2eee71340088ee2893f2fc06c00dae0d3e4930fafedc0ec7f5f72842df7fceea90d759e4ec9928ee688566a4b8a31ddb869d63851c6685ae5006e26ad9ab9594508ed49943b3ec442b5af6e7ace82f0009eb2da4cbbf3d86a404956b983ff37cda595a74f74ea888992866a4b8fe6fe53656c19332c6d36d4741dad41f698b08b794475aeadc5f763b1c26258e0d5352fd9c9f8e6db4583b7feffda459d2d5f4edfffb0e3bf6bec34f29d8a3c8e7dd704bb8c3fd230e8eb4a94fc381add6df30abfbd2734f6bad5f6badd5ce9a0c3b1a164fca18b75aadda98475276ff1060fbb19303f0f99ded4750051f9c9d986dcb63c764370522ae0e36d926db803627dbd026c568a3b2c936a1cd886db6116db49a0e8cac18e9a0419e1e74c906b4c9a05cff7193e56e1bb96f3fa5430ea3814467548cfa35fba8a9155aa347743652bfc68ef55163e917854177a48fdcdd6352e7a957c326dc6fa18fddd9de86453890b3b3bd8fdde1a8e4915239ca43211da244e1151a826bbbeea933611eb661a3f438e8564a582cddc5613a94c73edab0f4fef1843cf6111d72571120dcf6c11d6b910611b7e66df192021177b491b7f7ba7923c41db758cf6dbb34aed22e157733da68f346957ebdc8b6df921c46dfdebb7a2e96b7bf3f7f7aefc3dff3def1f5f13d8885f87ff86e3469d31ac20d3723afd68e3723776db4ba516e11e1cef7c2edbb9574df277813c41d3b48878c3c3770592bb06c8b5fb6f3bd886d31876db4d5b644121fc8253cded047d8402d6e9badb1bc7d953ca67ad01a87d4996f646bf7688594379a94b7ae92695de4edb3846d4d48d7f6566eb58d266db6bf51d222c255b5887063f4f097e00c43f24824b37270555adc7e7e91c30ddccd68a3b5b83d8d19dcde5ebe20d1a0a54528cf9fe1c19db972678d8a3bf68fd4a9797e27b94652c787c877d77c2f74e7da364e4e0987466b5aa5d1ba76a9f41f1ef2c4c7101924865aad7f1cc6f2c9f2ee7d78dfbd7f87853cf1ee4b9ef74476bec7e27effd8e0ae1933f1e0f2c420cbef3e4bffb84be6ae190a71a7cf71fed46eff8c0f6effcc0fae909090909cf3a74520617267fe48e9f24bf174f796596b51b608d976a93253e58eb37b5a7279fadf65bacda7d2c336fc5aa48f82cf1de52c16933aa5223e4b648a7a0aba7debb0475d19f777f2307d7d22d9dddae738e724912cef7b38f6957cdf86a304f2bd61f7a7b0f4a650b6e88d53966ab1f350f0b952babb944f5d22492ceff49d558bc56219483281a6932428990e511a3485aa4b1bfa2595d0810a8541a6524a49658884a481e46e5586ab16e96f61096ea56f4212aefc0bd425e179275ca12101e0792928b9325a4c81c995d27573e4e6c8cd919b2337476668d97f66abd5a75dfedeeeeef333447fa4fed23b16b81c491b370d19310d350c92b4f1d590c320ad8850442b9a4c92d451998666689e8527658c357e327d6fce39533018a92c0303d7d28ca7e79c73fa0ac68b0a66868fc36060d4cc5df7aeb0f49a83b499d567decc97336833aab869c834d42f234cb3af32493368fd72f9f933aacc90396c45e4337c6618e957e9e7cf983f23899c8d38e26986cf29cb90c9583273980c1c64f060650091276a264303529caf0a637c21c5f92f610c24f8cc1b3192e64d0e318e90e7cf4f85318e626069713e2a8c8114c6c8d2e21c710c13612a440ec3abd1fe154d785ce18c2fe218cefc52888b601fecb9b7f29741c7461cfbe4898bb438dd318eb598d4458bdd333199a416a74c529ef3659264ba98f36b5015aa43ee4f6952677b23d9dd578fa363c2eb2e7f18a19c3f968ce4f9311d0bf2c4395a7973b982a6c03494296ad62efaf4561f50c74cde46972b321d51b34cb91f61b60883a40d5d11c924b5485fc78b5b6b84a40d7d9d2eae4cd27c5a23547f9c816399fe0c1fa993caf467fcd018923aaa3048ea704f659232a54233b986e3aac64faa5b66605ba4c2795e66b6fdeaf1bb593ecc8f2bd8eac7ebf22345a55ee6b015ee2077f9e3eb47481befd9bcf1afb578a5c5ee80b27fcb77401d932ee8258dd640af3995d12514a899f48bce7270c4a7b2539685bda4456f1ca4cbdf9b87ec0e446b408a5f48d1df259c4890a27fcfe68d99346f8e903a476809e7d1c4226ba21d2cb4e8f7c3ee787c91c335fab5d8ad0fcc9ed3536e222a2dfad39a2806923b36918c8888ca3aa154773a7894fa69e6f57bb4c58903ea28e5d651cad505dfe6f47ef76f2127877add2a4779c61c50472953504729ebe0b8d56a03754c0b76bf479b524ae994d6d4a08efa5d71632b6d4e39668f221bd8c0d4114a9bf9a1b4b19d8ece412da863b664d9813a2807eab8b7051d5c09742197401d3353efc4337679fed8d1f7befebeeffba6b4260aeab0b82467a99dd68db32553fd0d57d60aa57102d931913abb291e7d8f86373edcf932266d64cf9063263b6e9c8388cf15a8a0efb4ab894e3f71208f69036f0d92bb7ded544fa7c779f673b7a72f4ec0703c6119aa50be7f4f9745b76af2fcbbf9b932546ecd28773cd6a0596794823ca50f12eb49ea419a1cc853fa7b3f0525dde0373dd1d34c0ae2d3afed67eec1d22fd993a5a78b7ec92045fa35710fd266da7a927aba08126bd1bf7fef6f2843157a567fa52d8622709c37a7f75ef705f1f1ac37bda9006e00e2d102b40c56a1b0fbae95524a29cd46609e1db9a5ed65a84235b41aa3f0da59baf100ebc70babffe577502921ac1fa857bd0a0f61356eb27ad4ab1ef5722795e7a70a334cb80af3ac03f0bd57c1ef842b20988346e6817480598bbe84947cc29591726d92ac15668cc563c672e3185af49fb9732a0c12c56d6bef5dbd28daafbf07c11de595a4ec2d1ced4c6d92408e3a00fe3048d95f1545920bbd03a4e62e7fd592eba51e853311f803f5f4ca4bbf9272ad274bc382201539aa463211d842dd79c12d0ddcbafd3c0dee8b50f6643d4750b24bc952da4563c604977aaf72a3427bed4c2eb1683ec16d7a93a4070b0fa4da9e109eec7493e47a42b0908407120f242a496a161e493cba88b16084455948b2840a132a41549c5011a20225fbd3aac3c5279b4e271e591af340e2e19ede54e3045843bb325c667582b65afd83d763623a6d8f235223d463b264a314476f3287a1308c2cfb732d15467643895bbf960021a9d3ef9f80a12d1395debb17e499c9f2bd1494dcd34bd0f4a93007adfb20e0bbd71ae72092dd939702935646210477945194649e2f0ede118ead76a80e4079228f72e65181b2c4132851a6dc529645505c60e55ea1a11c402d66560ba22b544407849024c444072e868482c8610b9f2d7eaed091235d1633af8824f06091804a4564f1c37161d415d183239b10862c8e7dc248c5c9c1b19163e42e69718aa8c10fb5d77e778b2177495bc40b8c607154440a7cb0002a020809164445f8704408b1e6b8d256a9e3e0d8c85161c61dc982491659f0e015c9220359c46ec062081cc0620b19606145c7040b28bcc0a2099c2c99d02207b4227b5770451114225c66965c9ff60b054b7df0a428fb0a82a0e2f6f522ccacddc244bbbb9bae54b044767b83b692c7546e192129723fed4ec192dc29487a1b2c454247c048c136c51b9d813cff7dfaf49942a6a2bbbcfb4192a11d4a7d468362eb46d0483c1a13d098e0963a2582fb49970609ee482912d856a9ff0863c3d55a8ab1cdb141022eb8adc47a9184a04b33458de2ae6cd0c57d5141698b9b020157c5f5b22c42810f1e1614e0b07d71c123949070bf2cba27b7a3a21be27656381b5c5bc5aa608922ee9665d1131b7826b85c96454f664f86b6adabd45bce701860070b56ecb684bd56b4b7f4413ce1438b4a104d10444766a6d45a41c84007992664334134f98105c5904c10497c888942c652420410c0a8421403f258253b50b2a2920443090d400073839f9592263255133518253f40bcdca0a6baa249ca09da4b076e976591ac96479c291e70594228d5020fb4d080504420b9472e40ec70324095580b0ff6bbbbbba1f07197b5321f1cc02866272e3cf04d1132e15820ac30e2e1e4e0d8c8c98191831688288e949e307aa2f601c184914e07419ea5c562b10d5819646bfbadb56f6d78ed8ff6b3f81611b54bd2acd1adb54b161dbde884338014409d10483603d0902c8e05e2c80e152707c7468e911323289cc5c9a284da6bbfab84358012f6b77be5c70d4044542d0e0e7a704236ebc1c8e807284672f0433bc4b191a342d73835218e88c8a3b54637264b268e74b046e06eb8a620944e705da028f9705335d8a0b897059d066e15f238c0e488eb65c984154a2ced7e593261c511ab81db65c98415459a30514517435c54964c5471c50c7e50e2a20a1c507155593251851145b85b964c54d1028e04d70439dc52964c5401b4a1e0d22c99a862899d794593251357dc608952ab9135591bf7d9affb6628a548221b3900d903bf19cac81e4864ce39c5f34ceefddb56375ca7fbb7c232fbaebc4bb673574da19a423585947de2dc7b73afeeabb7978aeeeefe77e53873dadb2b7777c7a3bfd8d3dddda59527d3cc0e663bad9b69e32cc8c28e10dcf99b3d759f77258fd6dd2dfee072a5d2aa7a5fd7ed7097a4d113f7a33c422785bc6307c5e3e7e8bcc307f7e2add26eabb424679b1cefb092522b613b24ad524b4da7d3d6e4cef770c7775b2815716e4ecbc9c52dceec7339a1daae5e94d451224e2e853b2e4dbd48f93b5aa43bfaefc9b4432696bd66d9ec0a3f7bf59bdba55c01a44d5873099995637af69ba14c6afc6860e598998eac16e68ec86a61eee0e82a0d36d750f3953f7f622b3d726aa4ec8cd900ce6e771b2e7a77535a719204dc25dff4cd90b5c2fc949ddf0c6df6ae6cd972ce79e7cc3dace7f2bd6497d06617ea02b224ee51b1cc3ee46c87621794b747450dd5f30ad66e63d912b8d076c7ea21524ae9a525ef132a80b411c2d24628d7d0f9fb64bf6452239316bfe290e51ac416e58f5b3876d9c3b104d67a2f7d9956aad35a4d3d726aa46c920bca5f56234fbda8589036f26d4c2a4a1bf9326ba5fd9a5422f56baa66aabaed6aaa783ebdfd47eaeeeed4eb2866a9fa46560f9b5b684d95d1ce55bb3491a917fdd1dde9479d526c5194baf8b7c2d24229a594522aa594525eea5e4f9b9b2848f3c30f3f34e09b18892c697ca0f1008d07683c70e7734f93e4ceb74f93c413e58c7e291929e5cb9f927ae9a39452ea811fa594522a3ff497e001bc866a29e5b85126d91a34afdbd738b16d55523979f56be9a4d49b6609a5d4be648d1332a688565ab797af5f5d75aedb5ce6c4558a95ea0045e9d672c39e70dbe6c453dad4a695d62d884ab71f4c99f6b0d1dac3466b0fb5a6c0156af64a371e74d8a80e558723b43140f35e105e82eea48955b5d1fac3b6fd50e9f6c3121baddb0fdb0f95feb004adb46e956e3fd43aa5582f909d565ab7252add7ed8b696aefa2ea14bae3f2190fd279dd2a66e7ee9122e271badb46e9ced4ab6049a6ecb0b4cca49bb9a06042e94c75cc17069a15e89ab265030be29612e2f5b5ab24bd8127e908ff9649e52c75ffa64b15832a7ebf232c7256c097be72ab9f3c7297ba1fd924ee74cab01fdf23f62a5234dc8f2b18475cc7672052387728a9422cbff41965f0009eba08eb9c7fa551d84b19191d3ec906ad17caff35519f938707678173660e22046ee0c65e0700c1b968090169be6e7259c3ebe83c23386e486bd3323d092e63d2a5ede4b70a7c49d65312145aa0257a0d9a3a101794c4fd3c5b03628c0fb7cbcd0c2289dc8f27b489d0e46bbc9f26de48d49933af6af8c3c85a89c4ea79309acf295b0aaab9c11cde8c50a51e1846aaa8751931d1ec6200ff7b8cae02460c3aa906964f92e79630ad5c0594da32af3a67fe47207b564a1611dd44156a851956e4655aae47e979630a7098e0c1932b6547b597e276f4ca3d9afee67373ba87f5a4ab094d29a1a90877ecd0c9dede57f527458cbe33cc74da379d3ff20dd3a66748de8ec02e6a8d9710779e0b66c79f9de0de54e8b4bd812813cef0de74ec5d973d1895b6e58744d50a5502e2f34539b1084dab41249c68a9ef090e7a8429e2f2860ce62b19891f976d6272a4db5916bad555af1b94145b9feaa044c72fd174b8044ae8f638225b97e4ebf649109b6c8f57128916b2d3ac116b916394122d75aca336c3245f66f6bab0ca8bdab3b6f8e104e2e68128294c5e20919227e0023411099fe8a045a64faa8878194d274a136882822b7c542842cd3a77f2d92124c7c9260c244a67fd2e21bc11399ee20d3215490e99bee16320699529b452a258222dc11e0c04357f28b243e80f2c654144041c8fd5704508090e4fed2ca065fbc7426d0426045082c110a4cdc0938b8020440f0f41232a0596a755aa2280201073ee024f7d3dba334eaeedfe1a3bbbb291414534a7fb491c54cdf5e1764faab9f4c7b4025d3a2252ac8b4e80346321582874c738e108277670471945be58427658c2eb56cfd732921119dcda27428b06e2e02e9b23e8b99373a037ee4b07eb77fa9f099d491525c2cfd403c7a2ddb9fd1c1e5fe6604b7be149793b42cf38cd11d7d56f32b3387bdbc45d11c26771c0b90bd77da0d3b90a97667963bf66f86b815bbacc597d089bc95e43e9336f65b46b848b28811f741eac8fe49010be4ab7b4bdf7ee3a07330a3982eeb17f7b6b990cd83099b3227ecaf74700d20210721db97b30a284f64fb2d0309b3c15d2a98376692d948a412358062955c1158fb0dd268e2cef71a4440fd1270b8f6ad5d4206347fd4ffe867f5403dcb05b98362fdf812808a208aedbcd8d4e3dba2b58f7279d6c53f52a977c12cd68f9747fd0b66b5a030ebe2e82b8496506cd1fe4b88e32efba9507cd3566f88a345fba87087bbecbb84d667d265df84478badb53f651b946d9bb2ed4bdb531beadb9f49ec4b0903acd6f3389354e9b99b64f882c33a236a5fba0d2795b3dda5566a141153468bf476776ff9137de9371fb8fe5ffaeee97b5d284b383eb384c91d2a373c5e49bf93aff9953e8ebbe873a1745778e5ae6e08ee10def4b95286b27c23b2446589c70b64296d743c29634cb588703fd3b488702bccd6a7c02ad3ea4cea98f2cbc7ed3bad138955e58ed75aed97700bb9cb5f08f75d0f65afb84a69d13da607a804ad9556454c199a1100000000b314002030140e08c582d16030cb1349f13d14000c7da6487a561a8bd3244751983286184200210010000020325043b4018c0f58006069b7f5681341a15507ed5c4750d5fdc46fda86218da9d6d378c9a6ccd1530461e85b913471aa077bb1becb7a0f02d3a4a2425c0dec7311fde01ab3cc8756bf877d3f069020048d8fd86e581e47d6927c29ce8a491d400fe3e21d540213116c00b940e9628904b4256b74159b79d59a5c1445d044110bb4dc02d8af8801285f6162cf65c8d71e09dabc420c44389a4aaf313815ac8789d626128ef3126112c07892d0e13ed17123260508ddac8ff740c99918b0017a590ede49683688405c23942d06bd0006a8002a2c30f01515093fd6815bd97c0435ff4dc6c46dfcce71c529a12b5d4bc9e84280066ef719a34e097404bcaa8a94408b3586bb5953634f137de1494f7bd93af997655f34e3ee69c0e36dd9333a75e990e841ab09b1f109bbc42d040f4603ae998616d10977f8a2e38d6502b6143f842c50a5563515ee9a4f6933dd70c550927864719fc0010a4d519e556f691c250b4cabbefb31108fc54d0d43e6b17804e54a54cf81b361dc79a2cc8e0bc302e568e68aeeb49ee9803e72df69d48c93f96959f6ecf9656cba64a3542f4776398a61a5c615095620a5bfccf21dd3649b1a12d9c07f6506852a5e46781d0235b13b84be4e9437ab7f98e948944da2401824a263b7236022c274a35e6111517f100e3a2f72ff07882e87b02abd73483a30cb2528e62e1247ba340d822993a422457cabd863958bc1e91246ed9283c69fd75add5c0a5502052aadb6cacf286ea0b8a221473fdc6e5fef1903811091dd1810764e857adfd177c6a1aa9cb62de1d67306b544978816f98bb33616487c22b662cb0111b348aa686ec50fbb500b68436a90bcd04080ae3b1807211720ffa7a061575eb7bb70b0527eb96766d75ea102287ba8518e3eb3242f5176b84e55db20a58aeed861405ba9af5d2148144cef8a9bf1b32739192a05c002b0485527072fd4e7293eeadc26ca903b51d4c9e7f1496f5ae6457551ea945cfa58c848175818bace80c8dae4ddee487418d3e7111345587603b824354cf71652e3c513f6046770fe3776c7ad1a59f6f001ae2c76a3d4d7c3f9a0c0486de681500921c0e09ce456102ac2c3f9b080926a217808373e87fb762f60225b61bca67056e3558c7ecae79cc64ac13ca1cb103b3cde58e3d8184ec8927528491027f336e6a6f75bd3f2d64a0cb80ac0a54a18892b0a6a778903500681ec58c5002d5f00f6155140cb1088bd451c50191562771903a44c71a4908c9d82b08aafb450ffdac089189ba81cf9d624cca60b0f5f6b0abb6985c3cfdacadd641cf8ac5f583747ee455102161d0bc6697903b2574b33b322034a2234b3a200a2fcce46f69b4e54b3e51ae745b3a902fadaede5f7de08f804e7101b0a2b4bf503770fdf30b46b8b27df2f02cd4614509905087bf6e893569348de8ab3c8974be506f9c0f274904cd8e2c9771079067b2b1cbcac219a36af0882a9af72579a0b9b38a3ec0f4e078c94bdb9bda14498e473ae1d67b0a7bc8f4b5fe28b4b9b4e69c3bd346f3ec0dd3371191773c5c43504ef71363a6c56ae7bdffa2414ce6bc44884a209f848f395b3ddebe91068624cbb190276aac08d7884e2e74ae2388bc9c68135365ee680d4ddfcfff6be749004353717814a23f9e0d5ce0b80250e28e42e16db6b641ec5d18b0e62bf0f37d4da6e00d2d1586e8a51900cea04468e53e277edfe311ca240d8de062bcd89c04cf6c55a3672554fef75b7c1fc008eb8f6a358e354e1e829430967c22f4dbe058353c1884353ce24a15b4efa531385700f821b0cf091e653150a94a353fca16fdec6a9af31288dc84374d278397de82174410cf612a3e750b96f6f649836274c474e5a333108dd0d75b773746ddd002f067903e84a206fba65f9d8fef5858f3c714b3b5ceddbc9c445194d359cef9c859d1fda047da2371afa89bbbb653f6aff9a17296e01f45d960dd73fef30136a13ee49ef4310c0bbd93f934d5d7385d82ac03bfdfb3ffc888b2ac8270bb0bcab89502f47a145c7a914a2b1f92643ed8d7edb032bdaa997168128ab232a30ca182c5ba370110e9c14bf857bfaf7b4a895526f82e9567a9320a9b60b739635ca24a64540c02839b6f50b85254d4bfc7d8ed2d2afcbe991f527cd8d7d79fc42525bf60f2921bda49d3faa175a7853d748db82af76c9451ba5fecb20a7f952e8df1c7794bc6cace4d36e6df326579e228e5d702abd32ec93f5c7527fe21193d8aa25282c215fa54773bedd48f201904800efe9f951b263a25767671cca6c746762c6b3dfa3bf34a67e9a0d786fbf8fce28083f3783be7c2e1bf01ebd39ba410832dddcc47bd2d03ae467a92ab1f59dbd0faa2ad71ec27f76e70afc0936ed2dd389db80e06efd80a73f7383069e234eca4bce08ffde8ad254fd09855d36f8048aef1f57e3119d70d472aa6f96be7e286cc5dcb60911d6c2598a0ae3289eb77e32eefa9f8fae899251669be66a6601932cdacaf9702902706303a7468b8864624d63a4ac4d6e046b0041e08a5c8380a3faed02926ccccc9c0851dc7cdbc491635681c3f9776b7cd10f701a0c98f63e2b9797a9ccb80f7b7eacb8152ad2d77554afed3005034be47c35a7947c6c94cdc72dd0eb7824b9b7a4103756816bf1cbf33a90180e615f4b000b18368eb1bd323a1f0ea16cb88fb17577d4ffeda2701211c97b81c15ffe0503434c7b9c4ed60d66d518351a06e3da22ee37c1cc219dd67706fd29a5ad127454a883c77d36c75f44eed50c20cae63db1376603b9a416b54d18545a9f8e8627f7de53a61cf89642873c2069793cb93f44d0169ce530b1b722f87f28d8354b1b510bb35fa8ab0de074cfffa8e7e5e30afdb125f22a685ec36acee7bdc1718f49bb96efa2c856627d6819d754fd10acc0e0e86ceefe6251056dc271b5de4f49deba376e228c8c69456fc1bb28668e691a51f985e54b94d6281af5c80e17adb30632ddf035a4ba6db02900152b33713cd5874cc5c920c8ae734fbfbeb2ba7ec290b894d3999e1b3447250822f81dbc0a1ff8e471591f234a3b80e7e2725233077aef9a5129ace43ca62fd2a364ecea94640ea5c0bf84bb851fdbd032b53b3d343206ab100288c031ebf937a57d37855d0ea6953416573f84182e45c851ad0c3d71449cd14d72fa988f17738d2d828ae9a4ed973cd9c9635066818b213938407d56fedfb61ed023f0f22e69eaaf09b62f7e0b677cbe54d8d5693538c59daab93dcd705617e8319ad18a13b98f90f64e9042634a632bc2a86e1176dc5d0d9a43d9f371fe3b5efce1ab0374e4cdffabce56949952acceae19e2d863e2263ce19ddab93f04147256bfeec1e87d4374aa8f83a2ca74d6c9832252532a681c07f9cdf736a35fa225163a6d8fd1849fbe4afd156fd9988dde35c829c90ff9128b0fe4c593d059254e940705e53728995478b17d95c85c70647bee82c71b6612d40a6bdd9b52615d91e1be2978c21a126e293f710cbec5bcda0a248d66e2117fcf421b9109097f78ad7eaf44b3a81edb228a321cc72ac1597fc32976940fc19aab9920384f8db32280f3b0f2a27a62176658c74027f090bc922cf3d9b39c0aa31b3a3d8050043297ea0168dd95d46edb4cd815f3999b7e12bb4849b654b9df8da4bb7ad5bd03bd765fc167c425cff115169dced3700158c5dc5da7e120eb55ae9b1beb93adfdb908c98b133d581cf8037cfc913445fd7144fc2ebdd7cb0037c7e9b356854f6577aa352fda1960986218a7a746aa042cc7ca261cd9897f361cb047c331b0e213e78ddc55a2099ee48fedab931d8c789714c38877505499645f696e9132db7327822e1f763ab1bc51f3cb6076ad0c410d4d6b450e403133ee2a6df338ded6edd6d332aeaa02cd9df02e1f637eb67517d5bbd6026ce85affeb0f8dd8a39eb353a45af6551eef5d9b9117e0be6f840c7b840ff9bab7e72cf3d2956aa26c853cd8854dbf11c0d7643e95b118077ccff4700c4dde15cbbc40ef266e021f288295107af41e03d2df6a555017ceb1dcaca93de1eb93da7eb336450aa8d14c19bd3d0b1ca3b7cc51292ec58ebb0ab0969e763188b2d50cfa31199303549d45b1162c6abe58186db8778b7279b02031bb6d1c6785f1e61285d8b754f4db709204b6bf6e88e84ad15e13147ac707149d197010901882689ab4c11fa3f065e25ec8c3eb00b08656a98b49112570c8c5843cfccc63d0f7b61979dcdc8f1be2c14fe71bfa8e577b0154f87920cc7251d0d53f62834bfaf9e7ee3491e90e145a950ae4d63c1401916098f0eb8fc3c4e92f69fda6e630041c06bacf495596df6ce03c7e7c36cd18c7fe69f4c6882382638b6a705286ddfab1bb74f961d131896e0229a65bf41204733792a67e24a912cb1719b1e58c8d5185214b4da6644b4f31cf7e2606feb667c66bbdd518b48175d3d2c3d1aef8229bd91d8a0f00f17b08d712a6a2c5f996d6862310cc612444523fdd623311f07970284888d9a61cc697b87d72d8ca66c3162d5d023ba160252fbb571a108248dfa647d26f6c86fdcac1524e009539b6143c40f138aba2492a1f338790d869773ab175429ec243b8c28e4d1a71a103f9721ef0891737407cce1de33e62890793f8cf66bdfccddf132791bca21a1745a4caf4f30b67441fa8cd16c78e6e6c7ca8f088f5a4b73d8a39ac6164a9542b16142059a394ef8338915465a8bc19ee720a9f2fd0768939b076d073a161b7cf50679b4a3990c3e393a7d1f7c60d714f354e4fdf0eecb2964018d0eb490af9d28b65ae558e2895262168175cebc5116b7b3157f3399229dd239dfa15f78182f17e5e7b65e4d218e9bcdcc1f6e949229603a397010c8dbcde9835e62c90063d041491243d168e04e0636003fbca694f1ed7a6b0ba23410f604fb68194d54331570f0280a5205e472c96e86b81fadb030098c815d49512a952631eeb0eadb378b5f3aab21ce16bc62ffb05dafedddce6aa5aa003bb824a927427d7bdf9a119a73bd57a6551c721f3b6420d0ab8925e744909c5d204f3ed258e208be43c96675d0196cc9d326c33a2412866ba9b003edbf9625b11c0241503eee7180a076223ca36358dd184df8f8c93e52bbb55518b7fb02f95a96556588a89e02ee1469d54f40fb694e324b7f405f945eeb2143e3c040b4b1acb4119179ac415ce33993d9f9e5bda0408f7afd44d63abe145da0124172e792e2ab89fc614f69fe698546bea13b4634e9b02c28f3e1b40aee9614483794d55aaffe76ab9f9a131767c20ee4bfed08f54d742ffcf5e0fcf33ba51496ff992e4acd0400ab83ae5ada101b1cf75fe54c3c83fa7a0002602a2fa53401159f34b2aca8d74453bf6054350216402924a2c187bea80e5860e5bc5da1127a7190f78cbf2ad6a083e8e903a873fbb5a85bad2d2d09398a40b34a929a95643d69c01904dfad1daf45b05711fd6a321a92edd8ef0cb1d391ec155ef363ae7ae929dae22682bb9b8885c8dbc3a550176753add7af7817860534f50824b35a6b7bae74cdf3a7a0bddc2e21722c709fb1d860595c7ec874103fb6975632d6b92451c52359fc14e2cf5008cfba4de4b9a81b9762564c896f71380ec7f328b00b133af8b694b1158025e12cac7866086eb3ea78cc21dc2bddbc11f84e76c029768feb27947664d0b0e8f6e717449e5b4b9b0ca11590e30d2923cc9a3e6dce7d5bd5f5fa9ab4d7ef8cab576685df8be9912be5b7048026b1fa132125197e988ba60dfb26a5efc4741ef4c84121fac8184dd335c3e72af182118d915f2e583ec4181c8e79ce70635f6cbdc9c150331041564f4bd2152d8fb1bf52fb06a5b0109dae8bdc28b49656aab8ffac714503e4563a25d72f153958a969361362f56d8c0b20ecfd88f825e093132d6019b851c23ec0087bd31fac7d58311ba041be71550c76488b67991c0f7d2cd02920a1a2ea43b25a9a7c33b2a67984480088bbbfb661a1a22be40ea8278bac04ad70c3ce6777e840eff83dbbcb9bd4268892bb4f403fb6f43283e19190ff911eb31086d388d6b9d5f7a0e36e906e891f9eb7c46b9d19a26d8d8b04541cc0adc0f85714432b64118c36074f51429f7709c25973f65f7b6dde65abe33d331b2dbfab471132c07280c6b395803afefe95b79c8acb2069295219bd16056a978a1b4056aa6ecfebee43fb99a8724daca8a34730e51b3ae40acc05d13d62b3e564f31779cd8114ff67ef880f90265a4037196cb4ea458c3c4dedaac4505bbc20007fa2c0d58526390032f21a8ef647ab2cee8e053cf1ed4a08ea313c93804c415799e8777e874deddb8feadd14fe8e2642ca0a558bb81b70dc9c53355381459d046e16886079671dc06b22113ed92329103308401e60415c0463b13e09479aa947de329c7a0482c26147b4b44c7b1fd535308269a505706e5e7c34001ba2ec7cc5a76012021868e53cfc53555fc54ae6bd12e3ccada661531fce74e22d6730f72960dfe6a2549c9b99852a3a3e0bc12055a7986016591bded7153c43df16fb48fb74162be2f43a052514e74e88a4ca788bd501d35f7075f3ad8742d7495db69404cb7ae45b75485b4e6f2a6223ef270ff703d2267929835b388ffb07a628e1fdcad5dc3bf536cbbd21407e4bf38c467f730609c29a2325b6bfedc715b6e479224870e3e0cc9f7a6667217b0ca3b350eb49f3ec7c2e0442f9f4b90248b1a2e99638e4f3d964456b8cc2439793d94740111c96dda5eefaed101b7f6a2b1a6b214915da55e5d66e2622545f5832740fa70621704e0ee9c3c570683d02666c2ab2f7f720582a509c0e47755033f2195506a240101c1a0b6e4de9ade24b80e46bb2bb4f15630595c22d73c89905cbc6c26bc3246550112fb95877492627b330be1517b8e9d50e6bbe92968b5811f3268f65f513108382550978b448cf603554b0877c0d44f93469ef043e84c422c1228043ec1cb1a8584e395f438c522576c8898e15c08f139c5e0a294a888b84e7ce3ac5999869f650637b1472b90461ad7a974b9d6edae1ab4b91c9a65e6278ad397fb3e39743cc4777d5531bd9355505cec77121028f9a5cf9359e550d6ae7ae608f0548bc2d2b1868e16c55299d440cef6dde2374bbf0ba46e7d04ee29becf7b723090bbd29d50c85bb8c7652a4af0525ee750bcf5803bd65f393e75d4b47d80e3d3e6f2346f4ab4a517504e7db3a5db3f513bfdbd1871852667d075e49228b374023d4b4f13c0164eb8053dcb180aefa42b6c727a9c0367b4afe049a1b3062ae984ae226734e88b301bb6213ea7b0587094bf835be56f14d5be81e7491edc12b8c72ac99319ff151dca3c321c4b41de748e244dc7dacf35450d0c1b41116c6466ad44a85680592bd9ee5a4f654f6e4c445a400844b8c2803a7f7c609a7d8ae9dbd2a3db50a71b647eb35992443315f2951b1885bae769ed2819e660aa22218f16b5cc89b62e629d6d9d7fb2afd68d1068e8ecfa7c8388b1903e2a50c5207e8d3dbcb0f9bd073f189ea888fb2b8de2a41460e5c2338220700e45a4dfbb602723673c0133aeb65cc8c48d07d84a8a1f2bf21396a146c4c8c0a3063cd24d8ec12363d0202f1e4b0f0da3f69c2a1d46ed8e035752e87559986e00f63c377e15ef24d2df6920f96238df4363f6ae6b86bdccaecae783791f787d9e08d36dd3dd182fcc8e1641779f2b6e4ad7fe89b60c01afb24ce95a9470111b87d5e8ee18c106b2024bc489bbb028645c6fb23665d044b092610bfcfbb354dc08c09100fa238ad3d9640facea5e51fcedb5ffb0f0f1ff882ebbd9c8c117de1a68a96c663e1ed5a18db5d152b2eddc70a8e1eb1669cbadf436250b364e0736db8fe9d1df80a2c7bc04322c6da978dd4d7ae55daf49e38dedf3f842936121fa32f8eec3b30a998da3ee3973db00aa607ea669504218e19b94f012ea7b690cd87398204bb93b42d3e7b0d6261ba9b0630ba920fdebf71d6f5636ccd397c614836c7c66a894a872dbf22002cc99bf184a5d0add5beced8ac15eab7676916acd84f597f3941c7dc0b89ec2bece60d9de91c5c279f015673df0be264ca6a4f775ddf3e5c65fa5e232dd74edd791ad7b4ae531d9f3986f4942bd5c5724994c7cca2e583b89598f4e947144fc7fcce9447ab29abd2d8ded946238dfa1a4bf336201d0f3df3464086364b4fa0686e3585a10881f55fcb84b5840cba413b16c006c69b1fa44403c1d6854ebbc0570b81a0b424504229c5073c6e6df81b5aad49815d4417ee91994784e3a78b044f97cccc0d8aad2d265a63161abaac2092861fc0d366fabff3d9337d3b589ae3c6f36471d6736127a61acc4d40268bf8bcbe150ccbcb6c55d433ff4a29c562a635675f353dec999dcada8acfc9f0fb4de2070e8bf28aba3bbf8ecec04864fe487fe7c65f820f595bd9e27bc3a4407eb45fd04fe6e521447dbfc29babf084575005c753bda17af366f3619fd975bd0b4cb478aed973c0ac7f55fbd39b9fa870d4924a0943fb97aeb41b0bfdf7399c9fbbaaf1a011c0f90b8efca97f01230e7da5c8076b7f561c7f0eefa3b1fb96da2786be56a028cfa9ab8398ff1f40d2e947cc32402a5f479e775351ef0b450e6d48321e61945893ce090c60807586c9eed9d824289f2843fc97255cbba04e4e6eaac52eb26efa432c45ed96218b83a1e04fde382beee80862482b2a633fe50a0aec9733c0dc0fdf038a743f1a1531ed13c080fe49738d169ab8d4c484e8e68c53854e8976358eefdd8dcc1d9c270d24a9203a003958b55bd474734c8eabcc553c78331160501539f4915c4e75d80bd45a2fd4911b3041b1182bb86a42d8507055773a7d7f87a9b3e48038fb875c7b5cfdc76120c9ce522ec2abec27630b7cfd2f55ffd0eb57270e3933ce958566edb1d35ae41708a16c1420b30451e44373a1af7c1dad9004545f474781dbefc6d214a72fe058d80588b6b97ac7c2165f31802a0a98fcd6328592b728551b080414cead34325b68fe7e0205f2a11a404121c3f6b76b988d83b49fd0b1b3658ed5e4f68699d8764cd43ec2184ff61a7aa770b165bb38ac439f01cd58716415e4e74a7a3bc983b71b493ee2229c6eb8f99897881eef693b7333647b4ab918a42660e5c67eb622b81e332275896039a7cd8c0b6a4cb6dc7f26c1239d9e8f2f0ed3425acddca7d812bfb11504c34e92a3c3d9b5662048a348721b023a71f3341db92b7982a8fca00ff96bce32f282693524c7292898a4a0e3384aee1396512eede26784cd3b6fc241d55033cc5829e0b6422c06db4b3c92ccbe706a074443a587c9daf6ad06e5fa9c8bb6ef8f8e59d3a5e2c9e3f6ecaaddf09f284d5a21ba2cb7bad612b7c3681cc9249fbe086c8c5c3aa3db7691043e2f5a2ca0be2520f09dcd941f82bda99fbc6bc4dec8185da93c4d2989af2ffcf60122269e59ec05ddb2ad4b3b7e69ebe4513fb3961ed199861ee3372ee17972fa257b4267b68bf27aa90d5727799c81d16c94c45ce6f72bb62db9e3433788921a49dff5095f7b4486847987a6ae67dab03bc356f230aa0d39dcf3a51433a701a324ed7a72c9cdf39c8f6bb54cbb2d82a33379bc0546d12b65d8a917eaf674d4d96f4934d69a96e77c59a7d8aa0f18d43a3975318e79174f57d2cc7fecc51de5a182852f09d46087c279875bf2c60da33f43e3ce6a47f0b68be8f38071711f9445cd37af5092dea466191f0e83e43e66905b7f0a04930e02cbe26a8ab04ab388050933275c1c53c4d1e12d0c52b842e9c4baea7e4fb7f718da01217136c9784f0695980537820d5b29c07f207173c8f3a17230a3b21708a4e08f166eef94b2770f01e686abc8a2662065869a0fb7bc077ff52be60aa38cb78380040d4e25ec7261316564c8a30f85076c50ff8630bdb9e07b9df694a268ce1cbb6d5d4ff423ba3ca57179f002716a7a3ba3b4d0f9afa3259ab40958118c99db4b87bc99145bd56594be54d7a6e2b14a831f26377e125614f0724ee42284dbca4ba891b3c680333323a311d0e7210714820321839e58a90daf00ad60937d8325741c44eb96827f3adc5ddcb806976aee1919946909f3ec005aa86d59b133918b6261eb1a4efa8fd03ce1283b61967eda2bea5be072e8a137a472b54e9f68f8881b1f57fde67488a6a45aad44e375fb9a90b5dc58a6ca02e7b3768b0b9ad819411152b6de189451e76528676fd2b152b70bce756b8270d313011159fd12f907bdef1cef2212c3a114c3421f659c2b0e27b8b1dc1306508cf6d4d5bd43ff74f8b6a2cba95d0ad1f3650ac059538f4d078c7a7776ada0f75347354660831c600fdd89d0261784754b7dc472af8fb1280227247b0c58f030f5c452cebab26f0b31dc7a09f3326538a645a22f466644643f08c1a6c6d9439e2bf0e496480270707fd9817548e7f15625ffe2ffd4370488ad6665d1cd3b55f0ea08175608f2a4c3118ec1cdb7980e4fc85315d49b783d25437f911df8b948724054d377a0a549d68bb6a3d28a42cc3b30bb1ad2c77294e020272275107c197b5e3fce910283afa3097de480b8450d744b24206882c7ed3b774d9063c174becb286f7a9cb567ebc236161efd8985235d34ae25949c88b7a6540eff1acc3061b6426793c2d2d9fe42a0d2116257039e325462e5a12508bd7ecdefc0ff6e32a450ea45adf96347dd610609acc9d09f61a4ac21db027ddb48ecc44ba4b4b306e6e03a4074682352ffe93d12735aadc3b014b7ab7a345cc529061c5d326a97f55318631202b064821e20faca88467b1a0b9b54bc1fb435b6d2d31b3ab54dd4cf7854948434fbeea2374bdcd37d8552e61f3ff8888b7c53996eb5e84513a8cabd9b34024edfc79c8f99e52a402a1d8912de38318b9ad1eb1552c8c95ba9689064ec1c714a1953d954ab0f18c31b18d416ac616b8682b5e0c98cf14ba64b297d087adf74236ff29bef87ce0b1d0432a3c81c4e685e2df7e4c46639954723abb3fb488c2aac78c581fbe48f2c32ebd6783e72fac1470bf0bc3cce41bb0b133925ca5175ea9baf8d6b64ca1e419838d475a246b4d0acc07c1fc51326a206e26090ea51b8a04b31f18809b2b46888d5a6b3b6066d32ad837b88f248df179a6c3808a230cf28822a911c11b6d2ddc9134ddd551ee08fb3a43181cc1470dd5582c7070e52bd8cd8425fe7480fc901ba76ef6730448fe0cd824ca0a74efad0f8d4420c9101289cdb48a246ee88f9d9df14a3e030a4330463bca90f1b6336f17135dd50cfeb6e9cd65a4dd608b6de7d7859abff6c5c63487fd3151744acf50ab1b72eba2ac65f22872548476c3b6c128269824c6032162d0ae46eabf087ba5290b383a7bb72037b54d347130634aa625dd6648870cc0585a25dd8a0167e89234c95923220c5e9411135e24c2e39a9991eb6e0165d914290af9210781f8bc133320c4d9abe26fa491f9243beb5b04b29b3f81ac1a7142a982628c8b570a1858a9f80978c3a52946c37b4d9be800d7fcd185e3ce59dafa5ff027b3d9f6428123f390b8ec77f9b1f0bae1c0419b1a8f0c032f250624dbba61408db092381630e4a043b4a47aed704fce3a59c74c1a33822a3c890a41b8a6c2cdf6a9663c545a2473344d90f5025af71b5cd801cd9726276a3e5da7909ae77695f8d18412180a40c98b0e3098766b7a582a577494c33d3734e156acccd9f06950755f06468b36136b1e13925f1d074b545149aac9b25ef125d2f70cf47f81e733c4fdcde218d08054aeecb264dde3f1657be2d6bab3ceb3376cfc9b98269dd3e01404bc1e907011f8890990f8eb5ca1386b79097aec48fe370525d2c6aa6b4bf3beea90ff2cdc54f10cdd566c082e93a2c7c19cc5534a4fdcde47382f6255c84ecb138a3234083a5d7830c8cf5c54248e3fc729fc3e84f0f3244df494e2f40adea8e2da5cebe670ec98561a07c93175a4fe924d360f4edd5b711582ca447403db33b7f089c9e7e177c47680e262a6de1f8aa26c804d3821ff44110878894173dbde7517056cf58711547b19cbffad612d85eab1c5e0a98b68189b6f1f8704a1c76cb0220a7bfba17120b98f517108381b40aa592ecd436d5233b653c8a03de51a00f7cb164dab42009a3c4ac35030376d4bb35027be95637f9f66b135f49029695966c6c3243fbefb819fb4d5ac8903e76c26ce19e89525774790615216f33f38fb56cc6b81f1c8f882c8332b48996d78d02ea5d4d5c4a3487ea7e711845a50aef0c86c410d932b985fde669f918bdccca572aaf6bfbcfadd6d29814e071a450eff99684d2bd3a6d4d756a94792c3bf4f3b4f41c5caf4f50aa151f26bf344cb14ba69b29ded698f9f4410dacc6ac7e25775d835c0b0d2e07c7528ce199bf224af630407c76857288ecaf714cac37197aea74e9a52d891c489d148cc39d0750b020af9b515f291dede25d356870b3484c7e5b2076a25614949784cb64f27f0766280ad76bce4da0816225642125859eaad811e563009a50e009276900b689dd450fdd5fa9171a080e2864894ead60ba9be13f4586182bc29485cc23fd13dcf0bd8f4e5d5541b6b479c1ef4c34cf32ae6f2cb10e97bd7d1b1154fe2827cc95540356fe7c57e1d88e4f632ba49ce4aa5bc29276f6eb638c0c04d07cf113717b1445dc8917f7471f1b0e4e41a7c97e26a3e2784a2ded6bd9bc705510afbdfc36cc60e74f8b3ea5271b4fdf34c9dfd6c62130feebec238a8a3b43c858a87353d2d1d9e58d271db0666f492530a72afbf64c82046182e39c7e8f3a2954227a1f5398ef79d46e23e721c6c1d85b73e72cbdd5386b37b1c0be0e71b942d91701c8b1bc2bcf2bc24b17ebd8eb883fea163e5d4c8baa4a45ebf3cf1b767b17816ef4b11e147b42173d9bf96a28ca9026d48f756df1f1b3296cc4291fd4fbc9738fa47214dc1a1fc2d887401f0031fc700491196c03f3242c21e108dabef433c2840113fda35a25ab347790345ddc996f481278c1ce700c7c28551e6bbba415cdcc23411a93a46e7c02f7ebe5eca29d623529b0cc0196a769f4f4c91299815dc47a6f13b62122cc9c45893a1e8f664f7af0291b5446ce2c72c92bf097272eba39ea976f06bc7ac1a2e9d927bd898c2208264f2aae4b458b98e547e86d6e45d7420716c87db722685d6c9e974cab0b38f983dcbd4765652d57ac85f51335c6469c284fc48322d793052be8fe580d093a07352a164128f291be0e677ef212cd3f847cedf2834f619665fed38d3098d5d522c88691cce5a495742db41378b5b229a0d79eb8fee76488cee2d47bb0ffb9c672c5b18dd9394c7b6a40bb82eec61cbc0b01fdb4a21c99ff8b1a8d278e05b6ffccd2d49cb31092c88daf85e7877701b27668211a72e1f0c2218e366bf76fb77ce2205c6ad410707dab2edc604d76cf7cf988c37ad2e268dff4ca0af24ef3ff934db86932d7b81de1402803cac05a19427d3fbad7309bb195deceb36f3852f5578f06901434773e3710b26cf10e3554dc2d1fed28d5b35da988171364c6b9ae03ff3346342b58daaa02fca85075233d1ece0a33832585343421029a7dafa7cd09914116852d6bc3b3a3b6a7258445859e0b3955ec5c245309ebd0a5a05e7fc56383132e3cec162c4331e411b81df0d4c616f94e90c8cd946c8c40edf1ab67f8b4095911407c5973e16254dc24d3f80e774570ed30f9266c2494f1c37ff0c0bd05c05e0929412c3e063060b08c719b3c78a0a8a87b4a4a33edc023753a091d286b8651fdc0d9f65de7f00c37b92e1f18a3d55e0a63a624d859d0a9298956f3dca8104d789cdbd0c67fce256b4193795c848c552b5228aa61ca44ead4afccd2005252ae28d2da8981618b48a495cdc4e57f1d8d822e97e2cf7333bf673bbc697b2bf7996dde830aac5300f343dde4bc2634ae12aa1285cdd5ca5cbc7adcb3a6e3a950476eae0acd053ed405b5b19aee99810d55e769e0e538185d4bf10582bab7b6715ed32cb4a3985bb4e21c0e1b15e3ffe57cfd4785d3eada310061efc06fc0b212ce26ef931bb81eccf08b3d2e92aee9ecf8d174ed15c3dc7fbcd11609f0ca8d0a0a82c301f612fb22f5894ac20688a4ed1cc00bd3e93319d3bbe4b4dd0f018533a1dba06c1306d4e16d463d7a0757cd1bff2614641f1ece28a0ce2ec9b8f3f88cb5feba607e519dcdfc84e0fe38ecd9d585c7c19f2bf3b1de79fc4363102884f6169994eca8d671eb8359e0a206449f0c21467287d2b6855f466d96a25991f095008f99ecf3b79fbf682cd360677e76bfb6a193088eac40b55ec7ed10d418bcdf74eb41e15198f0bc4a6adaad0ccf06ad9bcb5c8e4625daf3d2e5f5dcf51c020da324cb0c6c9fc13be0d14844f38da99408660875118a996cc2e14de330c41bf5e9cc7310f9e13e5d0d40690e79edf7e4b0ba223619bfcd7beeb505255ba33a6fef3c80098d82c109f2aaf97f5c55d1a4378e6453311c0918114829d545d464be3b419ee3611462e6997793102337784663cfe948740cf4aad28c2b408a8956ae64a1e992450ed931c6e7963ef32d22ea906d326e70c07912f617b454f66944538a3e251817278c44961910430109a0dac2ebc1a806e151d3d157016aa490bdc76cd5bf383fe74fcefd15941bb0d30b29c5a715a338a230dcc4714ceb8973399e871eaeb69b05e97a2f95e64b9a313b65b3873793130bc58548e80ca8e31300afdbfce4483fddd6e45e4c6c670ba11981fd9a2215ae01436fbecd4b6367e4da26a820c33006171ee4d86600eca1516ee3e14ee9b5e6f15a802aaedcd7ae6523dedaf6224e6234d9095e3ea914dd93910e672994de12d279149ca83ffb67df67502ac723d9544d68e72d50f69bf7911b4d5633738e2f697f980f02472ac32a37814910c628cbb294ffbe1d64fdd7da7ace79f69753c8709934aa2809cf0f2d20442ff0a3f3e2483f47f4d65ec5d054bd03dfa2e8b40c8b867f645e6df586344f5ddeea81d287ee54ee7d8f6ce51a315598ba53c7cd667594faea585c548bd8e2e7bb009577ee5ae81dd03d03eacba373b9c1cd529100760f601db3d70c16e64f3e5e6e928f839e487b3089723fea3e65881e312346b120259c47394b179f74449d53239c33ecd454518bbaac4b0495640584d77545c5f478c3609db8837340248b6cd95aa238e1e27399f56c0ce0755793aaf019d943c876413233d017858e544bfbaa9e913f8bb2232d1c8fbc01903c282aacedc4ea787d82ab016266a11409432351a1d3820166325e2085fba8f5e06eef8125590648ad138fd06b0fa0df58a2a636ee62071a39d4591c452cf5483c423649a4ca80ccafb3998409da56efb9f9e6c89774f4d41e742f4344729f304ad4a3ee9cf11dc655b543d56715628a37bdebe325b6d857521a913701c1b00a56fde2d0c477eb280d3bcf7b940e718eebacf32de8b637b5f97d9320d47186d9cf49ce91c574442ba86d273d07ee60b4a2d8b682d351f721269565392035b878bc91f4eb058dfb577ed1c09986d5fbd136f255b74e7f5b4bd78ae475b6a735a4a75fa7ab654393156562630f9f5df05ced26ee208f5f02a2b3e31bbee73c99a1a93f1fe93047a8974e08453736873c5c61ba879334a2de32f7a1cbbee14f9826e4920bb797ac7d67962df72938cb25960e232f3293d96c14fd0e123d158da6bceac37d104697f41932f81396226f191f919f6f6997d7fd73f60ad5ae60498cc6d48e840046de04fe5173885c1e1132b5b40145a1aa523ae1431768315c4e754b5146537b1f031957a0ab409293118d4fdd424ee3107b6897fbcda0ea1554059834848cef503ea23df8fd422ab062f10f14b4d004c6843bdfc98dc0bbfb92398ace29a3101cc1deac54770ba89a95ce32f1abf973950c4d3b6dbafe699cc9b008218517f8980bf482fc19b7199089ed89678969a9f532e50ab3460cd7ff8c945b8f960bac6098ca3b4e44e0c7d94b3571c869189522806f430e0c31bc558e6eb0bf1924ac6e4be75b8dead6849fa7d87377cfda02686f930f67cb400a2d92a19fc3d1d74a7987d79afec004dce023256601dcf389a31639a727cbeb43d47f2471f40c415d27af27077ec47cf6f0097133eb8db9ab2895ea51bc3d5292fb66b828eba38968262a18acdba2ded3f0a29a38daa603f4ae4158f19c5854a69c030e0f8d4f4313f81498dfa30e545d815ddb7f5b50af96e339c8ce0425da997a8cd6b7e5bb5caa3df8947c582f2c3d2b753f037bd48784a5465057b1e3bfcac3b3f2e264e3cada2f82f9b31c5020482e4bdf5f96ea23cc3012b15297bb37fce57db873e540d172b6d2427e24f30dbcbba8dffa6bd500bb5e8f681fff226e9ad43eb9612fb6b71ddb2abca31286d9aacda5f4f337f57c9704566da969b9ab1d4308788b1065528e6926465b364a69476dd861caf517080317b61010f4d07d96e748c1d17bb174ef4f660725743e8f913a64ab1921ba980d5f65e630ee985a6c71aae344b12bf8498d93131df11cbc0d0f4fb3adf9841d92a1df52b67c9b27349f3fab3ddae85988507781543bb653c99506090585d647a1cd9374050e1262e9536cdc8247b79551e21a30bad46af83904fa11b3db065abecaaeb45064a586e576bd000f0a3bd8095d92676634a7dc3790bc8d57160a96ebf64831795e12c7655c62edffa4506e4c121e5e7787bf4460f7e7e3d56173f699ede9886265cafa7afb3c41b6a2b78ed1bef983780aee87e6b95295eab58c9cb113aa86a507a234faba7405397971cd912964539cfac31c19519de2f3e704c3ee652c24f51f1e1a9c6146c2b115cefac5a8156ac20c34380562b84369a34990e5ebcdc2394d82dbdd139bf4cb3146fac34faf0ce2befa7b6a50417af8763e0eb6b223154acb23c02a018d7182a5b2448f062c2114fe9b69311f57aa78b2485fd8f8067bb3a4d5de4254a3d2145c77b5b0d67dc58d22d6d7560c6c44120f14b355a951805b22927f20c3ef69b7e9660f4de9bba747e3a79eb3839e2c0252cda83edeac07d99bfb2b587ecb27ca663378736aace4afbc90edde5f90c0a29b530ecb3a353cdba472b2c42d5412f90edf7fbe31edc0d45dfb41e91a563ee7ef60284d8159386d7014c9239c6bddc7daea2c8290a16f809880780348e9bf495ce1fabd1e505762984029a4cc1a567905ecaaac00e1a1fe15cf19c82ad1cf43ef58114150b3e6dde4b2ca8f341cc968ff1b0ae998d03451ba5a2d7f480563c18edd134458488e4ac888b82d784e18de8a8729d2574492d887404ebcfbc7274935092687771ac279e15db64ea09e8bf4bbf3106ea2117b9549f2753b885b42a061cf3aed4b030ae5091dbc99ddb4a4019cedb6cf875ce4ba72fd45742c5aab41270be858eb6864fd2c1b452f6e31c654607b428d0e43d4087839735649d82d2b428eb93471965acddd1d403aa631dfcfb90895d7d6943f393b0282e5a41246d011a4f1b938a1a12fa945d88493d0a3fd4ef7cbbd63a9ed05e54889d056df592b9d8fec2f73bac5f96a26f1bf3a28413260244dd7832d04c94d6b54266edf21c8961102965e31c19a09cd236d7f023d8a6da2612687ce4135613c3611d60612289ddd4a9361676781201a9dcf86173f5ca0f37a081b6ff292269b319359fc02aa4b2b9dd276ac4a32f95a254d226cdadbbf1ef7750f49803f186946108c90215b431131bb29c34d2abdd5bd06bf12d97152a295d00dbcfcd07d0f881958b15787200701574605850ffd505b6d5b09b1e68fcb8d25c3eb1b14431546b58190079dc0f41002cdfc75cf0f3f9ab30535515efc93bf47b20a50cf7fabb19b3031e7b377fbc15b056ba293b302a427a5620ed91f971dcfe1b537549131103813c4f8d83a45cd504e8d27538bc29d938ff9712f7cf38a5431e4a2cbbf1bd3427b686355ddb5bbd2044857632fcf50ff2d4b4649ab0f1037cd01b396beb786fb7af8ed3e9ed458eb96f5a4257d842a6ea54d7156e77c9f418e1e4fd9f43d48d2d70fe288a0e010f4d8bca9615302fac56c4926085ea5a460105b5b4bcefcbda9787f8127022d053c240ac657d03c424dde90c1fec2e7fbaf682f7f21597ba18357363fe3b25e9a460321980a6c980bb7670c332e2bbc49366251ff64bfbf15c80c220a1a1d9e239092ead4067eaba84497c4fe1eb7c20d66e803a271afd25afdb86bcfe3bfd5dee7f0cb7133816c5ed19aa6398472f496d450996021a6bfd814f0c4933a9dc30f3c835360557f4f815ed2f7ed03354918d6a89d44b3bdf958870dfa94ca920e2d4b547d52460a8583392961b3579b04f72a5da13d6b7a9605b78d7e53cf1830e21dcf9883eb4a41f7a09ee0120c9bb8bd43e2d0d6e0be2d567fcb35c484f2ff0c360d1baba6d2d28e664145f7a308ba9e516ef54dc76d47296745968f48fc1d7ac932da43058b1aca518092412ab84892648db03b0d1f1d36de362cffafbdebffa79056b6466ac00c1e0e0dbe3cde4a6515f39c969792142204d0a703af2b05442e07b88966ba023abfdc2fd0d64ba7ea943dcf37f8471b9ab0fed0816efbd2f77b321e4e4f4ea43193ef9d7264880869bef1786f58f52d17d80e3737ef1bda9a19f9c0e204589a7efb796ee0d0b8e6b0d1cf6dcbffaac4fe49c8f80337e82236760e580d0aa88c577fa589e50d521135edfdcf60627b372c4b573a5075aaf8ddf5c1bc82d5c4a31321be90a07a3b0a55eba71a77cdfdb5fa80e52544172138bf2ee1e5a2e7e79552780eb965f683347558d8882aca3ae15cb255c04495787cef7c23f0a8f7b43c1ba1d8ede6ba5df21d2efb3cf42772c7222b0a74522521d2eba805e6aaf5ac6bed2972cb7dadbe8dd05a57664d58866f2ca513d5ca5a328874cb5636ddd7980b5f21e3ea7fe395d1a3a1cba00802dee74ef70709c6deb2073d76cf76f3200837947831a644396c2234f46869586699c17c87a98a45d62e9a59bda417b82e67764e34af509cb8a9cd221acb8777a3dc13098c564ef33a3026711e819fb4b3493a0a134ad615513da2e966463b1df4e2bbe6a1cd3955b32be88a58347d87689609a6aad00d6e4368bb7f59fd4c610ca8047e41fb686b9810ad5e4efd0a24b48f63a0089d7b3389cdf0f3b9b2c3041d8704ca46fab2edee6fde20263d668683d0927bd1205626c27ae43c55626206d2d44277a7d55f5b48ab5c4271ae9479dd8e9ca5fdb2d215f66253ba8d9582bded8c4c6dbf222c9ec1a7ecf50472c12e89c22d8ac23ecad805f552178997fcfd671630cb2034a903b4e972a777255dbb6a9101912596180e1aab64dc07809f9952af5062986212dced7877364dad1cfb29a67f94756cfb2ac898cd072c67771311a974304b8d8cad5765d50d74d3090b005bd16c4a0028832b8885cec1c28f2eddc946eb7b69e4027bfbe0b821081ae325bb571bb083488f6016250de3be2904a9e8cb0a606c27ffa366ad93df3c329bb60fb95ed0162bdbc3368aa469e058600318727900b0121bb9d3d5a8ccf6838f0d102bff16b4cd0c1fb49423b13732e9db6b51e3c42eb39b4eeb19ec35765646f8acd252d703202c08bc02e3590fae9af79fbc029375a8ddb73e262794e6ec197ab0ccf24281f687f170133d68799472def59751d958b2661daeaf4003cd485062ba9646aad2bbe4b58fd294526f4be95fe8466dc6b8f0af9d6e88834fb7842093bdebe9c4cf47f01096132602ebeb82b49d2259f10c42b2fae62f1b5e96aa6b56fae5b2e8e5c91cc2c6c304240e1f4337091a4522018347555713148bc17194f7824e597984279af901eea8bd0154e20f0058f5f112049d29ff8daa305ae92b0298e6cbab6120dc0bff790d30f197cb383356dc3d02c38873a809d17bcac46b5acd5dde402e6a0889cf3ff78030f0711c1fbd78d0576a01ec1d3bcdca6611eac3031542b7eb5ff6829fca85e4325ff9aad19270fc1f9582fc0bbb713f7bc6b3bb6ce7394158f550a245680b37330d6be4766bc1632d0f272ceaea227b021108085acee18eb49a911ff5261acd4814255e5e688d7e51e470996eaa85f146c60d2ab4d0abb9ecfb58e79e8bcf1d9c9b3e05c10b93b632e9c6b53740b0426c3a44de6002060b54de65d86940294c33b96910dba089fadac1b55002d7fc980907246f76353ee2e74e3f69dea6756addda0a807495c57d46da15bce2d23cb82c02dfd24fab0dcc59ac8769b7edc1d3a9a6ef2e8b007631e1853523061626e0daa78cecf0438988fc35ab06e1ec0ee97ea2fad185dabd6302ae0795a862fcd44f01380fe18964cb6093faf16745267db806ffe234ed82edf4094ad551eeda10c8bf1bdee80076223f4175428b9a4250546b2c2591679bcc25ecb2504539c9811b33ccd0cb0140ec9c3c7fa72060304b0e7d606fa49be9bcc39021167452e3f22cb5c2d0f368d4bea976083400812e4fc0e3a2277a27167a2ea885edd107a5c2a50f1852542d68183d91aa23291421f5a5620399df757b8cd4c0af98a7b459b9d9d593f884d9fc0cb55179ba9282992cac43e6e3821fa4a82d718518a94979ec5a627db6a6870da25da330901c1b88af7578879ea57426e71091b9614b55ce2c8df5ce126de5503ff7ec64c76bf2828a8c340a6c465daa863c719a0f6df289b7a807d0c767c99b84f85818c5913ee23f13da00834f8958b904e77254dd45936529f8890681813c8b0252e2392750acd07aabec90205eb6a46b0aa83c90023083066271c2c512e8fe04a8941002a8a6c2c0c05fd2c35d20650308d2f4e3ab3e8a3162cf4e810ea77aa2c256e075c1e65f0ad37e7c8445428104ba1fc0dd6fcef5c240a3ef2b1795bb70d9a407baa8539767fcbb95253c0ad6e95a30d73ccd752ace0413dcf5e25ffe6789c8f0d8037315f1f8aff25299ded5b4804d097d21378ac88543fa20e6f209cab9db2d3b2eb46163d65c4a366b81fc58b24b40eefc59825a351ea546e2b11aa3af35698ea05cc1134cdadd118db7de148ef88d21d87a659efcf2c8231af51f04463ed34595895420d034bcae25deeddce40ea7b2ec478c35b8dcedd5c2ad99857506a3a19437c84f6a1c4cd604e99c83de14bbaa31f56f4b9cc5b7b2a712520aff06d72388ae38a57ae0cccf6644604b0fc476afc30f159fca169580af7a1bc702288b23693588fd7a04c676bbc14e9a082d941d9edefc81940ce28c9c4f24389c7a348bb9d5a2ecb5c838d2492dc19c87d11928b9261ed35009611b3258c8ddcd1cbeaa4c52e41f3543e5a3758c91de8ecb3598d2f0fe5a9357d6e0a89ae0fd7699cc42d274fca3051154ae516f845c58b130543e883a1e79d11a2b24a228c2e461ce4a7a59c8f0e14e9b33ffec6b787793f3b58552a5cdc6e3f73e43734df340fa94e5fb244d109767bead51887e3531380f464f24a134b8e9c8e9959a4acbd1ede384c3ac773190759b4b689c23967b4aec329ae498af8f1a1dfafac8b1e9c8ce84d02e163025a6f42cd152a5eea37e56814c7536175448072f36508dccf4bcafb84e7ad91e4156d36b92972efb6dfe65ae3a8c8241b425734e21b987130c4b130f01e0763de38f5aa4ab465b002f4f1eb3445397df7e7eba40f5633c0ffab75e56c40e4501037d066260e62b880789cb52aaaa029556790e16a89c1b37eaa70e02de77fd7079cb03fb9477dc1a1623785895777b93966037208c39d8c20808f042dc8de218e2d0655a64fba71b44082ed7d5822152d459ce148eac8522dbdce1938da9381c17ada130edd7ef4bee10584c89f1aec3df9e373aa4b81c44736f6132a995b2e8ec1d4f9846bf2cda4c2ac4726774ba50ee4f546ad152d97f104ebbe439f5519649b609b2769576715c0f93436aee6b5a4dea6a1861073ef5eb1715788749ae8a6102e5c97c97373ecf76b94138accbfe3c2f4f8077ccb3ad6c528ab7a2816b7a6bf86e0e934061b8fb2ec73e10f15bcd62c05159aaca17e097c05d3e29cc3a35bbe30b10daeac28ab9df28a39d464f07d9beb7550597a251042bbe8d082cc711231cc20107a731b57dc321c412dedbe9069e79536eb5bc031b0e28dc78facd97fa8174b31510ce47484d2353afa24e230abccb0f7e4b36d55fc316e452c71a45276e861002900340caadb69519c83280de88034853bd8bbb893d1766520dc605bbe020ab1199deba51ec997d8fce5e7052be09b5459c04bf884db2d6f029b1cfd479d36146753bf9d4ba681b957509a9f062ccf47c63e56f3214f360e6b3ca16e141ece512deb80500582787b3d221aa53df50fddf359858b65a834f7e6d4af650b1fa1c89e247ad867bb9b07d1044d09c1a092389f1de579b42090439e0b1cab51b4dc1d8fdc5bf89ba396fdfb93c8812b9a29cfb5e4ebf2c397a3e317c2fa01c6c552924f09103c834eb19d1e5256c9c3fabc61f094065622793498a04428a2e69982e8a8cd791e7770e2a4b23b698001f862377869bc1c42f4b01478d1c15e7991d295e2d0b30b6ef8bc405aa29b7ac4818e0eebf8597aa060d7a42200c732712c75f880000c512ad138ea9a8f72e20d0ca2e75b79a1ac5d9a53ac292fa970dba48521f3f9637915f8c880a099938f86b193b84e86a9bd5ce177c8092d62e3165fcd4bc52900517c007d352f98d1b71249c69621cf86dd765b2998f98fe67e16c08e5581a8207b314cb563d7f60a62d7dc70e7adea17691f54790cdfa8066444444578e2a98ca4616538a7be67c9008e640ce4aa18b9fda9512b7bebb96c69102320dd0a464a32dfa4524e4dc968edcd78a1ca1678bac09b5c7385c6553f7e310a3def7e921945a56049b40e35e2555570b1992d4ac5d1e85593b2b50dc66bb2e7ce360d825518e557e84a13d13162206a87c75f4fafeb1151369c58f4c306f61f5470d3991aa2361d8c0358735d0a49030d465c5ccf8d978edec6bb198d286b8bbb384ecf8477b78eae9544ed71499e3813c67daee0ede79b10db8bd06c1f2bda7e423f3b8b335f5885dff27a777a89827e85bc1098ce6b1bb9c3ee35e31dc328f68a9dc9501098b355b2a274105391f41b5e97e6f6e92790a384b17964cddccb5e5f907a97787d62d55c09e7bf8c37f1904a1160b653c8dcaa539930aed8e2ee20d1971bfb3d359cc40a4a332df16dc09963d6611e2e9c03e307e74c725ceb272a222825de6d0777c0266844f13b0652c736b5e404143cdea75c047ba2fdfd5cb7d3804b3f16e4717aee5e17a20d5c224ba38d9e0975857f3eb5702df56933ccf63062bc335a72cd3e1e821c0d0c319bcb154272af4188cfefd761e949f5ce4c9f71b5da2570f2694a132cab7d439306db0d2c1d983e9257329be9d91a5c2d048d6044fb2a4621e37e7ffbbe37e63122a6e90151981b4a2aca6f82d7cf1d208a7b64b5f8a2870954664df34d36052890f50dd6d62e66dc242314d2ff94a395f32063b58a673d34187f7dfa47422bf263168cacf89eb860fbec232ab48c40168c0e6a0f8791ba029e32e32d31b90bb9f49d84a4fa55fe64984d1b7049e19dedb7b6bc6b93644f7aebf404b13463f9949a6b7603fa61c154c421e1015e3c1e13f0bc2c9e53d4147a7c3eaaca6884cd7efb08c43146958b4f12a029d71a82c9a5c119ae9dd2dd3294b69a16152bb65f5a21df3b1f7f52e8ede11fa242e44bd68ec17c7b0c625cd3b271180364c54c298798a48975f06c35ae708113e1dfbf225b4579e9a62f376dc218c034bea55332c9c2107527e7c86dbe235d57ad5ea28041c69d0c611c728efe1305b563165e78420fabc56ed96c22f51762149518a696642c83235d4ecb009ae37d8cc9321fa7f0bd4c9c860de6817e869822f5e7aadbce64d4117a99160d7ced1a98275d5512f6d49dccde0d806c91a651966beb9ed02401533a9f0a55578a1895a212e7ca7f16c582a8ba3a64f13b5f756b04f9e0388ab16b0a636192a50e132163fbd332ac0c8a57082d0c7e359295cdb0235f2b341db19843c3045603d4ccda05ad847fe3a7f3e4cbada8c33fac08ec6eacbebc731a42d7cbeadcce026818ae7851892d63c64ca69d2082618d17ae42bea52b7117c1e810f0f0f277eeb7153812bceaa605570485d282b82afecf80be7f7ce23b11fc82e62b591e7949c485470f767544c55bd5ad4de8792115037a05f3498afe6fa10f7b11229bdb12fcbaca9b1f07a16c474ef13c883e843f7a0b011c526d00f589ae2ec87bc323767663276df2b7a1ed0791e7cef33a9c2e45275244d708cbc8541d2eac4d518296d1a58ea94ffd8a72a2102039e2ce59d28b94eed21ba5fb40f27ed8d1db3e2fdcc24e271938f46dd52b20c7ae6663d629e6101121ff177c5b8eb1c5a5d4dd39426333a74f162adfffb2af524811b73a05b1547e3514dff15148e5059eef75aaa18e0218f00bc6d2a5a910e5106a05cf6466ee6499b318134edc4b9e10ce677b13bb4ab8114c9bc8b72984e94984d4432a9bea8532a657685371341bf4e43095916db038031c0aa723650baa8816e6db9f5fcc8971b3030a05bad9b52acb139513c9bdca0df230f81364f9baf91f4ef599bc3763461d2d9a39aa60b893c4b69081e0044a8b3bec73d7ae594bf078f35b6db775196a37b3aa63504caac07a63d7fa8db2873c541f273854493a17b374602c4109d097cae77d3680002bd158eb365a51c6674e8bb54a6f1d93d0409facb494bf8c3f60f55fd779563d0103c1143048c619236f96b7bffad1f9a1942b4264c09c05f584417fcda8f035c50a7438fe0ca786dd649bc34003a0bba2bd788735590a272108b070a105b41f0411955952299d1065d3d836bb8f91811a39255f0e6e86da0f7694d72b78a9ee138afc9aef1c0a4f469af6283bd4c0f44d0d280fcae81dec26af7e23797295850cd437384541781b79e25792d2635cbd37622328e7cf3b19aeff8f730deab92ef3dd88359e1c7a4831ec147e9884a715aaa892a2f6aec359af7213bfbc8d3ba628d3b6c5536f1aab28b61cf7ca39e02e947ddd59b638432e72d3b5598d01ed815c3c78886a39bdf2e98b397764612dda1c7bd00f88887a6747c48ba0a9f40e91b2d8d2c5fe2c0df0d62013d06dcbec20b7b4da36c26695a96eced3daf276baf48e0959afb650ec2768271fe25770134089b0276ef42ca1427339d20cb3c1755195232f5bcf816137b6d41ef702b0224a4af76383a99e8e6641463f2e50ec5cfd3b1dc416be5038751d564b83c7ea0dec74fa290fa67310f32f641a995781a36a844f244ec2861d00af69b657fabf1517894866ca76460bd8c997c7eaf80a77491d37faa944e963c1c6aad4114dc624ef90a28f3e7298a82f1e1a11efd01ef4650c82f8e03335bbaefe3af99c1d561e345e2beb10d5bd5d807937caeb12e4e898ff64f6ccdd0a74be8a71e4309e823d7a3430fd1d65e0c97ca7d2fd6f34efb3a7877c9a3db947d6970dfff83d34b7a6e8bf97914514908f359a37e0cabd1811749455ada7023c8585d41c89391896255b064a1d806f261988f730ba42c0420e54aebce711f97c5179b7b08071da6e5653467cf6e53982f6b79a30b1546182182f950a72ab17222d31cf52006996f1f8b305db07ad31cf3970c16e22f3499adf6d37c969e8460b13974d0356c5ebdf0edd7b6289f8fd9049579de0386d69deb79b32d862594b7b77bbca21856aa427c9eba060ad414c80ed0b906262ace17144cf817a4444af4fc924af7a9df51ed925c21df86a4c4de168c048c1bd154a22c4c52c22c8a5616e9718c61cdc8d5df28b7778ec6101c18166cda46072b834c594cf231d93a4314ace5abb7fb2079f7a8a7e8e98262e9f7c4969aae7dbbfa6bcef28e916a77f86ee8cfb099f366bfb9625de77eedf68790d7b054eb7452b7582b4abd7369e1958981af589789bf7780b82c921e30a70d0f1c053f5962510e28b658d43d3a9e8a197d73b50b0188cc3170c2007f40404d168ba71c818831c4876862160d6c6caadf2511cebbc22c53ec98f58ee1e2fcb16b4b6541b3b9cd788e27862e038ca14fcb75c2e58423eb3390b6e225138177f63b20df5393a4117fe416184b574610a1ce780d4840ae57431475de325ef94d774081cef7dae04ca4aa0cbea79b1ed14db4ec52189c129c573473cc85c4fa45f800761517bc43630d6edd2f17e74866f1d21bf3c44ac734d5e367491be23c7a31247e88241cc29a0880d5f58cffb8692ed9f2c8ef768db35b9d148c4b0c784e380715a0243e873da7cc74ac325b873606a8eb82938a0347cf95e94e6e1c3ccb0d5f9d86216ddd7f050898de600ca358c690e5db0d7a0e1ae9ab5d4c69a342bb003b548ab63dd9bcd53210a35092c42462f9698376432d23955548bc5700535f5c788d02d8383551afcf32dbcd9d12cffc0e64331801913b01923ff5d2b2612d6bccbf419a23c2004463185ff3185c8cba703c73b29853c0a9b608108b1a71d3986da223d8d7ac13c3f4ebb85e064ba9974d7508569cca22828dc1a0e4c05998c3cb3cff24ba6f3d0e27d70406fade30180e16011d3f4e9d207dd5034919e564bdfefc3e83182637646f2de6a572070e8a051d413c72e0a1b72efb68a03df9a6a015f1d51553e4230c29146ea883859a8e76270b52b2d043b43cabf7930f9a6263b4619190caec406b0a981236d1b24dcc1df61e7b3fb24797c8c2bbc0c58a5ca0006c8134239b3cb780d0ae073eadc2d9e71db85843b3bd1735800f9fdd3bce5fe26a4c64ef0c575532fa41bafdf93ec78c01971faa00d1c12e030dee75b7de47770a02a14e57525fb1a41a76ad90b9662154ca4fdb121ddb8782f5b1b24a6026475217827fca025b1d51431c841dee534c211c8a6900925a8cefc170f5af5cf15d4b51678f6574c10ad3833ae39c2eb54908c4fa6a8805d2a70376a0cd1a6ec6b5a48252ec7b624d3b2ac51876608b80f016b0860533c12040bde77948e8e5c37db7fc34a927e03f3115ee585d47049a4074ef324a56ccd38d3f0eff4b59faca213e27a71c5336ac4b953ae4222db3218a4ca726380e8833cc8b7d95acdf00b84627350e413b9ca13219753a914344bdbbb653080bac60e53aa6f22859bfb20dfdd8b1d29a141697fc13c0a44b7e196c679272908c19edce8fce4941761d78d05dda4ef064470ee275aafab92355a42692fd307a2a2ed01390657adf00690c9afad6330029f70667f650f8cad8a36540142a00e1f776b96a38f3419f0e49fe387db91fd03ef77c5bb27c2dff1e12412e9a20f458de5dc68ca553e2ab9fc951c353a846d6391e46c8edd3a1b92729f335de032818e9d62f142c09339982a36b39561d944608a319add897e68889b72914cc165b18cb9fdf9e741aec27cee4d118875225133d2c3e76cfe603670fa4b14dcf41d4cb0b14874e4e64590e39c50c29172c87f0b91869c3c4b2d7c04b214c940598a1b6fca73754de31a6ca0611eeb97b75a7600b6a3cf58a39d94d8efc38c897eba9cc25e54c5f8c7d6c9b5bbe2d672885849d851b706dd690bd5c36791a04f4c551753e1b786273ba4ecf3830e435fc6bdc3adbabba77d1e415a3b9bd13d5fd3b110ef0cf567e35665cea1313752a276f40dfc307dc1c73c0631405540a5a1a66cc540f82edad7cea37869896bfae0c879d4a1e2dab26f6cae8e133878c23ae397c2827ac617ace9795746029e7cb25086aee560eb60a049e954bb8d34890deffc0686534c31d32c48a97d98df53886f7d9a75a2f05cb5f002c48c5f7231d6fb1b8a08a3f7ceba2dfbb83819967547eb2f4ae4f215994e99a70fadc66d6d60cd8d9f4e74d47b9fcc39105d8908791e5ed01e5f401e5665619b07c0e7f7ed733bdffcda82882d25d50c904c1a5af78c8de0834ce128a4bf2656c00b07205146d8b03ca27ce4bec4408679b9c1b67c5ecb8f221b91c12bb39f975e6a78e0405339146cbe424ef87380286a5e057efbca8553d1f156418542308943af506b01bc7309e654964304f864b60519504de4f96807e552466b6b29f79a2df5010c9e84612c46f08fea4a865053a17ab94d6cf15b24db344e41a4d29c0a3d235edbaba926894aaa8e66ccfa5626a644afd986864a0b9fde1492fa26ef8e128a814f28237a2562ab72c97203500dbbf59d04276723a15780708c88a697b28131a9556acf0727dca42f5c23cb8208b335041149e21cd0f72454ceecced393267bd73f33b36b721ce5c21ce186bd8b5cecf4845474a1c50ee9cf2be2e79729783fd322749989f25810d4db614e47352848c13efdbb6a4196018ced538718856219f5f2c94122780f8bdf7055824565c5ffa58869b927f3bb8c92c48fdf3d4c11d5b78a283764760aa9b4980f7900bebb90ceeb82c41ecabf4e9ed80840892b94a72b45a42edc9afb405800a1a22e63cc910227f3564e9924e0af19bc3aff0e6621b5665a0a45d3e8d4eacb60cab062ba378628147c6c727addb04c1b555ce7df1112c012a58f783ebd2d5df5ef29a80f86d06b518776f6b30837c43d3791de6ffc7457ea72467b25f41d6bfa9553a298c90e5c98858d692b01d2501836ff90fe18de9a249015e01db8a3889fb0084977364e58fa48b7c957cdf411eadb7e970616cfc8889f8d8cc3eba55c77fe3082343b4efa43bee7f89b47f1c9f974641ca57e33e2b1f3ee27af17efa583102963df4db9ed213e20afeffd47837a86651cc28c2743f7628000eab626de463a2af9536eac3fadf84ad14094661a3e6989a89a6cc91a0e6df272a59fdd4939b9565dddac7e4daa4d625d4fcd3386729680f8489730af36faaff03eb48444cfb5b184eed58c804b8a3d475382431b07ca1578b5a1d612edaadbde3f9c457d897415a31313a3cff89f1dcbe94986eb492047d9c395bc690bef00b9908e5f41ca5eb11fbc6b4395b22242150ec14e5c51b59df1c6977755b0f4a024328c30f8c52a8e8b38dc73568c99003f034a6dbbc995e704df83da1027c93a1f7ae3e4eabf3081d5e01c4b78a52615f4f2ce49cf18561dcc19f93018eea9bab97c278d79b9f35478ed53e7088d167251a635baf16aaf89889b8937dc3136f57912cd9a90c4ca9b22e8d64858f74412b5f516116aa8350b0775220943e899c84034d4ae8588dc0d8413d141df6dbfaa447ce384f2a74eb274f6639c73ca81813e6e7602c92f60fd9e89c45aa7c29ff3b25d31b3aa9de664abc2329dc85382499c60598b10630105c5779b34428ef7381f26a589f510adf684d31156bf58e111099bc66672feccf962fee7419ee2595dd9097db08580b9b66984040578855e8561f1214828d2fe184827cc3cce54d459ed38cbfb162def06cc602c002708283808898f513faf5e02499a73189fc49bb5b5b61eddba0ace5b117cd5b9e085d0210408b41d5343306cc72f92211c945c9b7582b77397c213001ad4cbba7e28184220e886e913d1e112750e2515a27f20896249d8e1c97f94d324395fb111a15481e533e5062e661616c3b9dbfacd16f95a91958aabb4029340f571b3d484139835f41861758f001254e14c90c99ea261395b69281a1b40b342384f900509e3034a840623904f74b83a44709308a19d10ecfd627960043200970e4d7d67ac98c5a16540e7fc1dc8149defab2a456c31bc124028eefe68548f6304a20e0b8eab247abbe4805b3279b2647c03830ea7daab8a627dd05ab8baa386f9e5df4452e11a221e10d97a04a307137f8b00e53c0716370b5fb854fdf95f72c8f6e0031a3027bf92e2554ba06c4a8a2410c734459ac3da3fcb76aae7b16c9889652f30241ba8e7b610b0fa6ac9874cdb9de21aac07d9d4e935e3ce87b4f42f527ef6952177d874fd0e7f123636d7ad23f9cf913c8e3a86b29261c4fd78250d3adc1be518c4988d1f45bd1be89e25ae3274e6f63ec91a8a658dd8ccaa3fe96de449cd1aad11ce9fa4a5041d0bde9c851a6a4459f8cc22e53ed6716bd4642b98ebfe532c70efe42a048ef4316ed0bb4032bb6fdebdcdc0998717894f47131fa007f0d6b50fe33009318fedef22a3759e8cfc2790927f2a1b53f7615593174b600646f6895a2c1fae5d0c36f8015f63c3f132317267940855fe8da43a67288ae1765208e41a7e17c917b64d6c9edc905589f6c98c9a09d07ef42cf305d9ab1f54aa91c60b20f0b785dd1d020f737f47d18c1483f82a542088841e71fbfbe528112a535bea0c213341b79e644dc70876a644cf81993fd6557bcd8044c716e0a8e90e56010ee315ff8ed471d17c36a24034c94a3e479fd17853c4f5f56f1293c5f384cc8a0e703a1ffdbd62fe01e99f7e2f6f5c1ab41792f384ee06d2a9052bd5296175a9ffee40e4c2e5f04c4c88de4128ba5afcfcbd635d001bdc5407747907e3deb94513770b5555c0a57b05c95a597ceac5aa7fc758ca2d30cbd1f3f3f5cad764b40c531acf1dd6e456081aaf899cb5aab9f5a9ab85692bca71d970ff83a3019d1f5bf1099fa3d1e55ef9011fb75b274ab532626aceca6bffb54bd36fac5efb52a1608cb6ebfca2414b0bfcdace371c865dd69397521c62e946cf477178c0a0a0570f767ac78254bb906d5060fd6d0c5efa6aeb8824d2bc67fb2815faba4b6c9ced052b7da7d58ec19e5506c8e3196b73bef62d51e05cfdec8ab6a144e410680d6214f6d657589d389b6e74daaa04a87ea8bada76a05771eaf99be2cceace522d0d636f07f9a54025776258bc0453798d83401a0bf33835cd0543011339384ef8db65260c5d4a7ed7db4026735bb91b14223c092997c05b4f9eedbee6568ae453327d01358b253b0b4c3f5a1d794a0b2c12ec3bfa919a4b42f053d801fc80ba3a27d6a7d78fee703a25a335f69c045ddbcca2a264d98ed34f48513fb209ace249a7f2a034b232689734d5b879a6d0e005c9b01be6deeb628f627f8dfb00d6d1cf017aa676d371f0edef4f360c6083b1e33061b6ee8b0b843758f4c15ad1599fc879184101748392d9e79cd0d6818740082474a379764849941b15b2b2f582ab4fbde12802a6cf021f5cfdae058ae93db89978d8849efb7f39a91841b4a2b4626b662dcd8d08774cfbabf7c87b113f39e4abb010ddd943ca835b9bcadf09048d15599c3d3434cdcd4e3ce1b5c241ff1d24e5f87f5891f5081e94da9f72941fe47129a3d11576a2bdfc0a5431b2d11b9f131c30536325dc6fd1985d364ae88b212d5a18234879bd1fea0c8649e0a617bcec08c047d64d1e99a8b38824678d4fb92e75cca479594f201f93594aef5032c2d09907c9aee2ab1fd00a7334afbd6064e02d49cfde1c0795210f7bd8157e3a29bd67c3394e5a26c42a5da1b6c9688284f0f0c186a760566f06df4736935eef0aeaa78e992746367dbf1b1cf14c21b65aadd7ea47840cb09e92ee0b231eb7a9eff018fb8e530a1c44148de3fb5f2a24e154484257729586caa34dad7d093e2cc4017e4f96e18b63b007db2449a736c00cd22118fda189cf1b7d83e945a0288a2913724f4417add4012770549b00f84055bca0bcf2fa9f44f7aff1a65b62d86cce137847466246e0d38c9d35ed0f73684871ebf9899721a2ccbe337e3c3e95acd37b9a58008ef2da0871634c394ad3c3183e8d5ed00f4628f4f968804ffaac6119701d0b256f741e25934738dc058e96a487ee2963ca61408fc9afd40a2fa58586e1cc69df0c653da9189a574bc67fce46da4439e9acb1396b2f1a4eb841a86f23cd74c529b05339d2eec8811e4b8934f72971a93a79ff0036241c2f3c74fce874dfa9131c049a508fb0683f46cfce9893f92193aba9a7410d592a1beaf41277a75cb54e028e1a8a4b1d73c76e1104ac53a2bed09587916d9cfdd0e8df78cadb5ab48391d472eeae48179f3511a692719f41b1c91bcb6e68b60bc04546b409df5f1c974da0dbf2d86fdf01e46a1d1d85d2bb1f7b89abf05d5a371ad9ca82b84152c4e0f50a6ba854a06a4648c699038b1548d4b708ed2a143329004a00546005e6a37fa9dfd3738ddf2408c3d131857174d219ebd1c9d1112e48d15466d9739162ccaacb4a4acf057b1ed1a785552eef19df539124f694c61442ea7620dcb9dea3398cef1fabfa404f61624b4d77519c7efa21ec9c0bc3f4265a967fd4eb9d6d86aebbd058ce2cad4b957d2d011d0b1bd3ca6559bda5037923e9aac99f5816c0f012e4d2398aa01a8d7904e91745d455b3d747365ddcc54bfad2720f9dff443ef5665f38303f1169defabab2c3813e63a9622621994e47782f1c7d732a4ed3f7a54ce38ee0aa8a1ba4d2a3e292e8bcea86f76ab824e427a77bf01579ef68476a1168f1e6fa6ab2677a500dc160124f591458b981d87f013686a7cfcbd21d21d6f2ca39b4044bf85529c98b82db541dee3ea91dadd14ed0a6b0d81143f8013746044d18da4495ddc16ea07df8e823742d675835dfc8146d9d265974b7ea92bbf330269731b81307c5b89c3897ae5da5c93f14e7e5a77ee1aeae17c23232d41df7c497fa9f0ce1aa53495a43fda0cd57fb53731dad3fe2a06ffe7006a337636aa98595eaebba26fc38538e451fa6d0f19a7d1681acf80e31fa5e8b763274e254a814ffe1530c06392ffb111d2dba6e84907a35fd6bf4d63b3b917eb4fef98d2b5d6edb91bcbc29ae45d91cb2e5c0b6e6aa6b5edd52baaac7f80eab8fc31bc43ff3d8d88bfa2ee57b4d65c3809f4e6c076bae8db66393fb674d56a5e066cb6a4d2cc16d1246ce24309530b2128e302c7cd33ece3f1186c19c8d5b4024cf7aef2aef1a1b7ddaf4ce2be3c3e7a46addeacd90f8476d18087d25b8fb2a59d0588eb64b8381e648da570abd835f39dabfac3ec5ca173b5589012ba00a55a5e6e7de0d54a368ddb067cec16e773105d2dd95ceefcc4881311008d6ecf342d07105a2fd31a4f6ff5c81107cb90a73f6a16e8056543abae33842a45f449f8adaa2c7ad0c351f7f4fca170312256857f9631b6f83794be8f76b0f307040852505f80d8ffe854376b1f7a3ddff0a741ed7503bfcd36baf69b8c958f425526632f1c2ae0a76b7aedb64a65a55e97d6e827db105b612eee91667b48b831fc596d0aef2e3305c8210f2b346db964752549239c3020685f96518b91f041e68f90b0459dd40190c009773e7e438b2cb6dd0a34aaae0db2e597efa8bb8845ae2374ea161b821193dbb377f17c36fa49c98f98d5f131fb2ddec88b1f3427f951268e226c5cf3c8dc5dbba08ea4cb3b13d23f81d8c3dda40c061e00c4bd31e16c374562682aab6caa650443c83eddb3a95f1e5e46f069b6c8c4ca558f33965101d9413f41aa522d2da1c4217e39184b0ee2d38ea78136a5323865902d3b3aeb0d9c81bd69aaa94ab6c8bd058c39463906a9bfd4fb5d2d7da95bb83b8305c595f063635fe2c72d7d1fae7a27bda68006845069e03186dc534a000ab9bd3249e8f3b51fd78124cabf0b45a674358397e1bc067f5495d499708e87b856ee32a0bfef95290176de9beac28888568fa4351719e8a46c6e6b793bbe6ea373531373b4393f302cd684575e9187e80766576e9e69d4de7bdb41fe602f8851102227a5700406f59b261a39f305a63d2e8f563235a1636987a6b730dc2ee8677c9b1730b2d4081e996024edabc83028ccd9c2e136a3235ae7c186c2ae3da3e23eaa2cf053ad54fcc524f1105209f261a3a2f1220e56d231c067631ac73cf59203d8899a99ce677c669bbcf37284e12e104f5cb70a30ecb5401de3f0332a83c90365cfff3b68ff835aae2e63362fe2c381946bf5f3a39350030b91528b70a41c1210d1a7614857cf5c201fc79a0afc8956c717c908c4d0d1fb628a2c45868a788a389affdf2705ca103b31a6b929665596feaf21f013d2d1585bdc8b2e6b290a51f8c3154d5f7f8adc1392a810b25f910ec7dda37831932c72dd3a85f001e33ac3de1e300c92661bb394c5c22a7e29890a6c4bcf732cc7de0a1d94e1cd611d282d809fa23f9b2c327fec4094c2f13ef7ef8e1bb3d500069b50371e1025eeeea5cfe9f3d625e58260f0ee0cc559b4ad615fb6f468ba4feea539f889bff602b04d004ec5c045a868bb154205e153707b9a9a976b26dc0de0f505bf22f677e12bf66ba393accda75396376cc077b27b6b5f374d6cf1ba6022c5e44eb83206cbadc8708a371ec267369392cd277b9017f8d419ef6dfd66c77f7cade52a62465270b5a0b820af38505a7575a2c961383c57672088611268c161484849bf9b2f2a1f79a5e6938389e04c70627f41e7a87c184d178783c0639f48e64be98807aa5b5662c9f71d2aa176798309a12256990cd171526bdb2b319cfac67f6036b9508c584d16aaaa0335f5cbc7a6563b19b184ea8c587be7b30616c50d00f72e8db355f4a1fb238388d04a76b424e8709637978b278325f483ebdb2ad96ccb68042295e98305689922a72e8696cbea07ce8e9925ed5d98ceacc98cc787228646ba8cb1272e8a992f932fad0d356af6a8cba5e39147e100d72e8a90c264c0d1ad2ab8a337bf5040749e661430ebd07264ce5a1a10fd2aafaa107325f3c59e813fa1ebdaaa1d73161aa12ea627feacc66a197d12b9ff16855f7a1d093adfabef29043a15828147a70f41c63c2d49a50e86bab268c075117fba10ffd0b104728270300f6f331300cb6c15af04fb6df3b88f0d6da94805b0bdc5aa1adb529c9f655c6d0cfb69ad00c9c8133fb220b082316148a8562602c28db77b1f29d341c1e50c309e168383cd97e0b93f7426b694a40ad056aad90d6d29464fb25954e9c6935a119389b6935d93ec905d72216148a8562602c28db4f295db7383c6e71423838f651485b8a6d2909b16d29c9f64728da5667b66636b335d97e38b2618dd5a01a8bd5580dcaf6456d086b2e379c8a537978445eadb8a441a3c9969252c4162b0969d676f659adc121ae35d9fb4220b0bf26ad3dc57c75654114c67322c6b28d05e59317d4a4fd2bf395377394130ae35d315dec5b99289365eb8639c37392ed7f1ed09c61dfe58ab0fef7b93e9798e3abf0cbc9b6334eabca30675bd22acbc3db3fb56a7b6bbbd166c7f11d0af3b9a88b7deb0139ca7e18e264dff180bcd55ac203479abfd1e736496f7eae154fc9dae7caf63f3088cfad7d9f00f294620eb3fa13a4b8560484fd34db0d35d2e81490fd7bc3d0064d582ec1c3dfbf5fc3eb9afaec6cc89f1b298f7d19403c8ce28f2d6493f63f960764bd7a40d9fa7f3bb883f4c09bd91365dad2d2e4c4319bce4a7bee20794292bba162e18d1feaa75ae57df7f5ef57f2d62f69e46a23d7f7ba7182f69fd46a8be5b86f7036c971a191d9a449a3b3a5deccb40a49d37890215eb946924bafc9de36ae6a535fed836b6fed7bf7de7befb655ee5ecff33ccfebf628adb7c3d5d5a4cfb8ca9ee1c64ed22e9f71244dd2e06e353900997fcf349256d1b48ad69badc93f11badb391d0c429041106e62704a021639f49a66610db2e91406f0d3730d9ed7d224fd80379a2b8f36d681cab3c9f99ed7894b32cf509d334224842d74e2168d76c85f860e540d997414c9b4a953118625d9ffc35c69109fbd5ecd2220f4a7f93b089476bb3f09bc39d2f95eb77bad96be678acb2293bafb12fe844e9e52c8b3c913acecb95011d29f266fa6f89910f6c7b2e323aa8b17a84f6e4c7da8cfc6528237bf092ea01a714a67baf7ee7d7bef4948df7ff3cd37df7cdbb8fb694f3713f8fede9bc0deb86dbbdb568ad947b276f6b927656cbe7d0585f0efdebbf7ce1defc0640feb36665ab61f2c6e9e84b3c9da39ee0d43519be068da504168df450e614966cd147afaf74d21bc83fcde7bd257f75eac914db637b7514b935c49923252f3e5134281f27b5ff620443716ae4c65fae97fe3cc9308cd1f9661f2be7f9a3cfa14cf1e63bf8d9970b280861257004971852c4f29ae88e5f95c9e525cc1244f29ae58924b724a714512a4b8e225d2d041b7d48aad103815d736fb9a28924da664589cdab00c5246ca29ca6ee4b885f07bf8dfbbed44f1846c495243417fab4ce28885db9b56f10d5aebe7ddf0958adff01505c5f7953851fe58c4c92de6aff46bed6c7dd0e2795f775f9e25a9d6af51dfcc5aa62c6008bdef2a188487a11d0cc28aac08ef6fdfdff72c5708b5bfe00e1fd615ee20a249fa751d6cccacc0639c30e9d81c832bf8c1097096248141cc14b8277936614213b97e3dca09e3d1046726dba74c5ae5d9c67c5d874d3b6aee3e889abbf97232cd3a49f3a5c5cfb760105f76172c84b3491a4dd64fb5e44abf9248cc171ac58f60aea81528cabea561985191edd442b6b039c3be0c1484dae338027e37d155366bc396f48e4eb64f9df46a5218908d2c49778dd8476850b665d8b126eb5b1c582b2c43d82a878736b2a5e123a36cc9560937497865afbf3a5fc23ca963249ab4df92cb39ab1167a1290cbd930fdd91d1a0564d2bb84007e749b65f0b996de8a94a7a4371ecd32596e2e4f0559146d93f729184d606891b25b26d91edb398212ce96b4f61968479ca51284c087f1da1ef4a9acb1fdcfb7b3fbc0f62e6f0bd0986df8d6179c308cafb3844cbdf7be38fd17f193e0a9e4258b2c8228bfca37bef7dfc11e2215af65ee47963c97df7c37bee272884877f74f84737ceeccdafc9a64e32fd8b04ae766774066fb6949b2bb76fda7bedd55a0b0ac1c13d1034db6f9ffe6995f7f35bd62a7a39b1ec9df14ef283f59732bed35cf58da3c63ac9cffbf92191bba70f7e1fee3a5ceb13cbc261eb13cb6ab27bd8824ddea9b399907e596fbabba508ba7dd4ea70200802153b5e8001164153f0c1c523061eec24c17505114037746e831d843a36484871c27603288298cc1c79a20a52004dc1862564564441064b204181820a278857d802c0832e28e141122cacc023680b6128c318aa8081cd0e783a9d1d58d1a22ae10227408315343f8082149630b284119830062098810b3d4b9002138ef040095e8409c8010b3fb059011390e4800a2870420b8c20861c48a1c4153355070859705e038e2e21093de5d964092c31247b42817c2e908c94afb839377ec8c1e3365102154ac8610203bd57188c20872ec8400b41541421073a504204275e00434cff9c2237143f557034d74b020e6a1294c8fd77873c9b2881437e916713253d1b8dd72f8488ffe4c9730a09401ee2ef1f84900ade6cd9b6de36179d39c39f858910b481030adad1cb46cc17dac2c05c51921c47159511c7610ab3df12c2fe98f94224cc96c2d8d5f7fe0a982fdabb7723c76b4e0ee78d33c6085f51ead32dff3967987a7446788c4c9d3eed183b12b93175ccde7b8c878b9820a6c3178b27f9207c4fc4711de5bec3dd734dc106cc986dbcedd6bf6e646122f43c8f1872f3fdd93d872dcd114342cfa5e44c3dfc8dc49820c6f33a76cc1d89dcbcd9ead6431bd6622c5ec2e4c363ea7b4cc5454c10e3783a6a3341b73bf1daf3812424697f6f188a622aa56197c182b024dd3c7be420a4bfd12d541af141440f915267e2cd9612c595b9f766afea7b8d26b994a3b8e75e636122dc7a686dc6597e6404e32e8b7b5292d03af7ce7dcfbd2369154dabee73ef35adea9e7be73eb46d8ff2a16ddb6e4884a3b80f8d282311dc880b39118e652e89fa299f843fe9a967bf0b6e279828eee7869d356770a14f22e5eb27414af9fa3e52be62d316c23e48effd2d9ea33a99c3454a587414e78413df97b013dd9722dd6992fbefb7d1e6067750264d7e9bcca35f098a21f8b48faf9c3551dcdfeb93396d9c63bde3ddb6df6e77051084108956d9e7befb2fcf14af3fd2e82e47719f32be32e7377306f7dde86567f90a0947717f6f188ae23f49bee60cee3d7b479ae47ca649ee3becac39cbdc3b2b7328ae0f0d5ab33031b91e264afb1a597b1a5bb8f7bfdfee5faef3ee36a6c4c9e1d49ca1556cba8f4d172fd19fef7f59bbdadfe85b23b5041112cd6b4f5aa7bbadd552ed0db47675bfb5da161db4b3ebac3d2b13d79dce5a37f7a9b9db396b7577f7e9eedbc537aab5d6e25abfd6eaeef334ebd3ead6dddd45315a6bb6da4b36c04a95bbac3dcbee32b7552df738bbbb695c68061b19eaf5bccd66eb8544d556ebb55a6b3532588d0c3d4cb2fa90eb8eec63fdcf7ad6dace5a8d0c5623039d24d75181b657ab6ddc56bd7a7dfb3ed08a3ecfb5480d3f1087f6a3ef46df14c5e54da568d4005355fbf9696110cf7d04e2d050b6af1e869a63d14fe56a35a11273456f7c7c598190ab133509437c59978c71982b1aebf1653d41ae2da83210e2cbdadaa698ab66f1f8d2bf909d0cae061d5f7ad08a11e6aa5f39be7426647fc2a3c0f1a5effcaa777ef8d26b905d090742fed29b5039c35cf5ecc6977d45ee38f8090ef0a5cf9ca0982b77d9f8b2b1907b8afe8201beec9fd2b7c36ab4119a093e7cd939221de6ca815a05b91b065d831ebeec24a3554d7d49c3e0f2253d03bd22cf204dca43b140a190f1257d42eea74988f125bda142e0e14bda03f24b8a03306ca12a987480f1e57c43eef7408b7286e1054bbf0eeefb3e406e1f72cfc8dd2ab93bc6f5e18006dc0c71d343c65f081ae3b163012c1dac1c4100f1c2f1fa4101313b3bf786a278809ed980797119c0756f288a3ec0681480003d3850ca6500ad19ad7bc3501459c83014c51855c68277c0657861e0f2b2e0f2bec0a616382e70c922a2e0524c0989b4196d8625971d8ee7f2a2e0e17837deabc36111434dc365b5c9e34a47d315a17b75aeae35b25ed6b1a063cd8046eff9b1f3cad8d953c901436c078673e25a4bb87971ae959ad3d85965ecec9580b81870331c6b06245e98197c623b2acda47369ecec8dae2b0a3837af1769766f7069eecc65958e64da793476f6c01d36c87c623ee1ab0a39383723ba241c3b831b6be3c146d3ca34860590cc079c419976b6374d603938dc971d4ec7842eb68414ae285add13d95dde10f69732e8673b76aeb8dacc15d5400630d05f5d29231a7f7f65cd158d5de0cb3a63812f2b4d05fabf6914d13bf4cc55b328f0a5ff4ce04b7722815ef9ae4144c3c099ab7e45e04bcf81c0970e7b409fbe6df86896eff79ab9ea1d077ce9491af0a5db0cd12adf377af40ba0b9ea99cc973d63c097ce12a2c5ef1f78b489c95cb96bc797cdb3802fbb474797be73e4f8b2aba0335f5abce6ca61417cd937407cd938389af4cde38776f1fd7d64ae1c48015f3692982fbb26f7e8bbc78d46a9ad047c496508f892021da0c36f1f367ac95cd51c982fa9cecb979489011afc2682ba788d2fe912e64be8fb696baeaa6cf52575a9bea42f1ffa7b1a5dc490b9b2348ef2efe1cbf984000548f57b60c21821fafe20ade2be1fc87ce95cbe9cb2017c397d66cc9595f1a58e09b301eae28ffa72ea04e04b1b02903157d6c779b4ea7e7f8ef9e27d3fc9436e1ebe7c00f45b0eb4cc178d4e1820d4c5bfb7ef17411c7de32bcfa5019c017a83811485a14d1891f20be0f2ba0570057029fad3a030f455048dec1d0d055813604900d7a030d486881a999345e00581d703b00d0a435d3e6cecf88acb71c0cece10b81469921eb8ec9ef98abb32b8bc0cc042e059f6ff81c2d01675f1e7a193fd02ed702dc0a5c33f0785a1353970d9f9c282c0e5050286c39f0785a1343fe0b2afcb51fe0ac0310e94fd7b50188ae4062e3b6f334725a08580d601b00f0a4367a88bbf8d9ae3ab6de7087591f96a7b154161288bbaf8c3e4e41820a7062eef0a975785cbd0072ca3819d65048529a2c51c456190a02efe05a021004d0f38e5824b312cc31958062edbfaf84abbe1008519425dfcb5192014268800b04f00ac0f0a972289cbe601972200700c8ccbf63c97f5f15c2e97f569621cc7711cc71e4da00c1ec796c7df827149f388c771a4791cc7f7b73ee3388ee397560ca618df12a305979549a6afd70effffff3f8ee38fe3bf0065c6ff1d7efc1dc6ffffffdf61a4f9ffffffdfd4f263cb88cbfada61ec8c3b1a9b8ea6a3e9686c60acacacacacacacfcffffafa88032bfb232fefff82b2bbfb2b2b29257565656565656564ce3fff8b8ac47642e994ce662399d4ea7d3e9b4b2b2f22b2b27119459399dfe57fe574ea73f9d4e279691e6d3e9743a9d4e27d3ffcaafe0d27d58c6ce30b89c245c0e97c3e52479a1a2a2a2a2a2a2723a9dfe74527101ca9c545456fef42b271595575151517931d2aca2a2a2a2a2a2a2625af9d3ca0997be846bb5b816d7e25a2d93288aa2288a2a2a2aafa222b600655444f1f42a7f52c125cd221645d134d22c8aa2288aa2683abdca490597decab4f38b0b5473812ed005aa69512a954aa552491445b1540265c45249e5c557114ba52f954aa552a9542a954a2593ca8b2a222e3b28c36860301a172412894422914aa5d2974a241228532291c42fbd58c22412c9c548338944229148249249fc9258c265efb8183bb7b82e24d7755dd7850465341a8d46a31189447a12699402ca9046a3d293be441a8d7e341a8d50469a47a3d168341a8d4ca5279548b8ec266633b3d96c26250cc3300cc3d168340a5140995118927ef4a41ce2300c439ac3300cc3300c4da41f9146b8ec9994b133cace919d9d9d100882200882611886e04f991004471ffe28c4200882a19166100441100441d3e8c351884bfa935fbebdb6d7f6a22cd1f77ddff77d20083e087e2128037e5ff8e087f8fb3ed148f3f77ddff77d9f297c300471e7d0d8d9db584536d6c6da584f3a8ee3388ee3beeffbefe344a0ccc771e07f0f7eb8a41cc7711cc7711cc799c0ffc00f9734490c89582c36f3eebdf7decb71dc73dc0d8132dcbddf73ffdd7b69bef7de7bef357dcf7d5ceec6ce9e7633e4e6e62648abb5d65aebbdf7561094b9b55e5cd25a6badb5d65aab89fbcb61203acff8aac3f12014c6738961a8cd6477f566adb5d6da5aabfd40996aedfdfad7da0ec73a8e35d58a81e89c9a333a9b3e9c5387f3793ed852351ac673f56ade565ddadd63a0b9bf41991fdd3e9d13b2bf4b50d8dfe1b87c273b6cf341765a1396643e7d2219bab27b2e4759bbdd1a618e08eb0e270653da4c1d66266092f43d97af1430513b4e64a7749cd9daa809cb8af36180d22468b881822b87b9dfbf26ff5329519c3d76440df366cb9761adde52d0c9f4e2eb36086910f57e74cbb487bad4a72e3da109889be76f99e2fba3312967319659b487c28cd3a57eadb4e7668bdf0528f4a7ac7bdb8ae45fcd2769b793fd2abab0b6a652ddeeb3b5c19b2de5b793697bb5da76b9cefbc090a8e6e49abd114a0aa9d4c285a8f2c2745a81c1d2ffd91bbfeaad7940b87b30d4aaf0e77fdf7b2bd4b798e4d9e271c77194f59df99ee3f050982f47c653bd70fc5a4f66417386fd6ef480e68c8ff586e9f2e57c34587c391b6c83b50a7cfb1b935e0520c6961d67835117fb381b4c347eadafc69b79414f7a8321c9f68fb8ac90edbb541196d703e256e4dbf7c80ff5a1401da8ecfd48dabe4e1c49e1e73af568ce007138675c21bbb2ebe402b5aabf1a21c9e5abb006d1197cd4f8d57c344d7e2ea0fe5c2d916887178900208af1220f0683c1442d2ff26432117e91f77abd5e229617793a3aa2f145deec991991e85fe4d9d88860bca893c9643291e945de11d1ca8b3a180c06139d5ed4fdfc885ebca87bbd5e2f918b17753a3a22951775332291a89b11892fea6c6c442d5ec4c9643299a8f4a2ee884824e260301107137130d1f722eee74794f222eef57abd44282fe274a88bd511d11771332291889b116d2f127136a20b83c1603fa2fb23f2177d90568d5ef440e64bf87abd442f47591dd1d51189ee8ce867cf885e46afee0c8f56712ffa1cf3a5bec846f43291e8bb6b23fadbab4de628fba2afad9a30f70875b12f123d0ac421da61eb005063885a6032d80e30982cdb277188c57be9782f16efe5bd74b27d1e46f0bd19cfc69b61f166bc19cf26db07008ccf24f38ec84c32997724db8fb1e29d603f30130cf693edb7bce85c742f9deee5a27b75dd4b27dbdf418513bb99ce46ec66ba6ec626dbc72d6e89937547443299ccfe48da72b9e17030ee470c31ec27dbff140d857b713a1ff77a712f4e27db67a16e082d37c3d988dc0c3723e26c3616b733e786c37e603087897e608422112e69644be3be74eeebbeeecbaf0eb752676ceecc7667ee8c5f9b6c3f0c9d4e9bec1ed9649bac6eb27bc4d45b678d7b4b8624ea138091fed4cfab4daf3618ac099892185fd62539f435f4558909b3fdb47c597990435f93cc971d3ef495d5ab6dab335ba509e1384c984d899239cce6cbd8d32b6d36fb99399905c55af5534c98ad66abe9024fe2f8250913bf6cd8884b327ff8bd939020e26ba0ccb7e37bffdebf951596b1777862304677d9348a6ea0cefea0dc3f9fe4fe236816ebe8dfcacda711877f8fcd647c31364f93dbab6c2f8edd33b64f93dbb718fba73436696c274d36509341e1d8a3b3661c89d3788db79ac4db7fa3bf1a46e9914622805ac1904b9a6b8769adb7de1009e1f6b4bb76d326ea2763bb2d9da1a169b91ad60d9b2ff65547da441413860a61ba5c61bed0ed2975010a4b1a4dfabd1e6d02c72f55f27298b8535d8242fa12c29c3142de2813f2f6d67e2e3ada6694541087a98238aa146cb60dc461ba17c471b18cc664935b53da04bda146982fdd6fef8d3d9aa44fc1c6531b891de4f6b6bdc7090992ffe22973dfeeb8b85a87d1a446af42476d7fb76dabd1e48d7fe9260c58ed84995de8bd10e779de8f34df50a8fbeb99aee3b0566deee4dbbc276fce93376792b7f716f92f8a61b8fdf4421863fccdbceec31da8a3c3443cf0a3d6c6beb4258ddaac92b64b341e9d0e972d9649de6aab3c2235732f449439efd65b6fa5613dcff37c3e16de1a4e73fb0c31fa0c7d863e439f8186f53e5026e479a20fbdc8f33c513b216b1cc976b77bc330fcbe90e3e805653c0edcbabfd1e1bd6178b5effd561bd6e84f400db4e00eefde7beffd3b6bbd9e4884e228a00e54ae364dd6d1b9ae7bad1f7cff06bfbbaeebe7fcef2deff7fdfeea3d0882cf7d1329d4e400e4d9e40959a6c9de4c0f5f1d43af8df5bde98dda7b4e914fe90541efdadede4ed3437bf2f6e25cd19e89dab8efeebf136666cf4750a6dd9d47939c880b7d73df1806a8238489d02cc2a5e8c1f75f0165fc394cc4071f401da86c6d9adcdec58ab0973499d3e446bfbb57ab45c7d6c19cb17d8f8d83e9b2bd1ddba677405db6e7c65662ce4852abd72ca00e8a7ba7eb6aadee5e6badeeddd8759dbbbb7b07c343a3fd6e89869322756cb0c26e0383b8b8ec1ec40ebe13a9d95f88c89b4d595fbfe7f33156afbac7dc2ab99b861e9469dce07fdd9a13b6c79942ce15603b34a6699aa6699aa6699aa66942a8e630c7cda4ecbe776ec87f510cc3d68db7d7301097225cd27e6ffb42a37d8f35e953be1b8550229748e8c3994099ee3bd62aaec7e705b8a3fb7eff31fa21ddba61280a814f1a7d0e73c6f629a3c761ba6c8f328ec67bc3feae031be6e2fbf66e756bce70cf7eb557dd72d4f6aeef8593b79c144c16b50ef40a0a345f3a200a4481b6efaef3ee09940171c544babfa00eefbb1fb7c9ed271884877f7c0dabb56dba3a6f283e592baceed4582d004d92b7ef9aa4c96d4e5c009f791c5c0efea4b2e8585b354975adea9146d5542479bbd3ddfd3f779abcfdf635dcb3fffd663869f8bbdfef534019bfeefd7dcfdd03effb97da3bf885a00e0d13a1d9c3d7876fcbdc38b34b16e1f6de6a95e3112873bf5b3beec5d93c6f746143389b33360a33f356b7b79bb7fab6dcfd0443debe9491a7861323cf39e7dbb9d270266afbf99a13e6ca67d53586af42dcc215a640a7531feb30639388f9f22a2aa3d1fd39a389d4dc2f4494fbbb9fa18bef8c34b3d5ea4a35d95ad6b49c25383a3fa99ebc7d4932d9bed6da73bb36add5feb5b5b60d56d8df93bb2febec6d6cbe68797bdbd3abd91aab554fc420eb246f77fb596a47f2866d2cac11b524799b4c36ad8912fc8eeb405cf6eff075a3e64493693f4dfa349933676ccf8dda139a14a88b4e93dbdb518335b9390662d2a8edb5d96585331bcdd6d278f2f6230844673acce6cb96b7b7af56f56f5f9fcc17cfe2157da5e1543a61ea8cba6cff7df7446af65e88287b366cfc2c53d94693f4fe04ad2bb623c2eea77afb5b815ac495b054a5c8db9760be6fe7aa4a3165b4eafbaff3450365381a7aee4378668e070dfd1d2bcffc20d149f893b7b2f6e4edeb6cc6fd27ea16a883c344680e61f05f73c6f6e0589b68727b0dc757f5c651330a5385305db6df60a2b80d61ced8beecedc787db05e600d2dddddddddd16c4d163b764bc6f4c84e60e7c2fc688a3ff8e1a8eb624c751dbbf2886f45df3c9db77eb037558ba83b664ced8be8e1a4e1d42ed9f7a103fba07bf5d497a6cb592541b577d912d7f1249612a07b603802f060844e31f20ee9d26b76f264d6edf35ec8e65cd6dbaffe123aa3f61b283ccf72f6ef2eeb4ca674dfa7749e149fced7de6ab54a3fa6be4ce0961e2ce6feff9f079e36873370aa1f9de1b86a258fea6e1e4dce192dc9eaba08e8bb7fb35dfcd35040cfefd069edadd3da157aba7b5cd5cf9c865167a57bb87ce58deaa150890ef29687bbe941d865c3f040ae1ce715846936d49df9dab8e4d54fdd93eb117367c40bfec98ab5fdef41fcf11eb1096f547ff109ab95ae336ad2a6b4eb6f548ab7ed0caaa3315c97c01bf7e85c17c097d0d85fe1b4b51083b2804fd92be03813ae873df3de3460ff25612a0961cc2d25bf357e1d7f7e6e8e1e83a8dea9873eddfb3bca8425bdc77a66010be44f8fda4555fab6c886353a443f86308cdd57faa0d148465a511b310ce2650c8c93fdfabaccf912741f5bdedeb0175a0b21f69b21e51a94e932dfed364f59c39a3aa9c202c2b2b0fa1ad5cdf93b4aaba5a65335f80305fbeafdff7762c41fce14a9347dc579a26eb8f401df4c7ea0287b0741acfa93447d45c768cacaf32519faf2dff6995ac913051f59d70a790eb5f21d7df424fcc8f7cd9313168f6cfd7ae56d9d0c97dcd1cad25c96b2a858d5487e6f48dd76751a9356bb5e7c907894efa27d799db6041e83aad2ab5afef4fcc17eeab7f0b11650b0a418d08a71441b3dcef8ea7c9bfac3d9adc6bc1bad2596d6bcea8475ad561194dd2faf78a55ebaf9ae790a42683341936598fa8f966b166caa2210ad1ef983e0f1efdc3a4c3dfbeffcc3c3016a186cba9d3fdd4f5f5d7882d97846b93bd0224b42fa2fd6bdfe2854808a715eb287cfadcff0fe35cf57094f63203f490b512953558902176ac130351520be6f81bdefd0d0f32a487d5acb68db57e87bbd6266bed6a576badb5667bb711a9c9ee67932778723977441fdf8db57b4debbacf1ac7dd9f2087494d6a7fa39bd41e01617d8a63c341b44b83960a0631f3f65fe9e5592910f40335f20ffa8e4bdab8cc91fbe958d6d0c2fb76a2e8773f27cacf398a31e78abe88461ee7d53ebfd1dea38ca3b10c4563682c79c81d3e828238c49fc32291a222b427982f47da6a3d96a9ac1181d3c387f526776fb5dbbd37ade2d1a4df1b86a22dc91293b034714fbf4d1c994bcacad1f2ad182b9996c0b9a22c47d5fbcd17203532fd704cc52024894d5a3b522573345d0c44a5375489833c8ce4bf6337daf041c8bd89728d77e01c4dda6f795b5ed27f0f5fb5c0a57b49f8a331bb6b93da1cfd3d9ff9665a471f0a8843d3344dd3b4da54738d7ecd137fb5f5ebe3d8b0687f82731bf5cf26394ebec74edfdafd4e73423d5262abb5d65a6b6badd65a6badb5d5dab7d6566b2d17426bdf5a6bad2543cf10c1caf58bf091e6cfede3cc74cc6992c393eb57b2be8cda5e97294aa62f32e9a8fa9492755423d799e33245587fbe8c56d1da63beccd9d4ab8f61273b2853eb04a235defc58d91fff38feffafacfce9f42a2a2f8a5f2a3d89f4a3d187e183e07fdf73dcdffbb5beb54f5bdc492acaa1c167e7c3c9a84baf3c1727a3301d0d75f117c530a4e1649e13deab9b818b3a673783ec2e5a6952ae8e86c2cc8e866eb9f4ea098aec5d8ea6f2f6daf2ce2830c96587d3aad273792e1b6f12059decdf2d89293b1dcf15ac6f23b3c93295aa5183c2a1cb306fdce4ba67dded04c8e3e604716ccfed98cf4d6c7f6c41f87e6fe87d93d01d34a4668bafc5d569a2a091b7fa7d5f9e322d5b3295914a8de048fa6aa641c31ad19ac2c12bc9bcd1ae0597e3b8fdfc0f90f9c77cfad6bf4d508dd8a659d8dee724732a7f0d5326ed5bbc9118d268d252de07e9535efb94946f818bb8f8942fd2c21b8d26edc5371283329a346c1a02e54bef03e54baf7d09177142c33e462fe2222e7ef4455a3cca1b89d9b0f82a5f8aef2b17afbdca288ea5713a0a74149054ce71a3156e3fdc6817a3496b319a347c848f16efa2f42e709122d3df484c10ff19238af8635cbc7f0b6ceae1e24d22dc8019e3024f47d198166f82008d69f134a6054e39caefcdd6bfb417c77def6a57dbec6b18486a4789892998c82494981badba392c5e22c4a9ce08d817053e270a0a61798f387104cd42bc0eac6ce7f788e778f1c602c24d3bc2ec3f962660e52137fbcfb1aca511d9ebe658d9b7d0ca04c8b389143fb90a4f76b201f2acc2139feccd1a61e86986b5aab17fd95fb9ed3e103473b6f31e089a5f36a73d2336807be7cef8eac662a9588d982bb6f9f8eadedc9bcde7de6c3ef766f3b93775c7573716faa41ce57f71c29c14ecde3014c57f924ca52850a571b1025d9f85ac99eca22c741f7a7333bb01f2771a5fd10beb9d0b5472a0f40dd4aaee9f2749b26b343c4fc865f7640f595ab0412e5bc66393cb9e39cb0a4972e93e93bdd4a13134cefc35292a117e393a4dfeb48fbb90ee6932140a8d424261e9e9d83c63c7402a1ac3b1bcb9848fa8791b85a1d1247ad297de0b471310348726380a47d308572a72f542f68484638de5098eb50b3a445f7ad2687aaf4752a7d0bf39e56f4e5b9df903e54bd18e10ff187df8255c7e2f7a2f34c110e9897826e1d2fb144cc4b387d23ff2680c127ad38ef047e87510a3534c72639bb0481ae55f7393d8d0490f5fd5da44a3fc6baaa66c8ae7f6fcec28b94b8074b27f45e24e7ac757cea451fe366292b0a61e119f8441373cb727fbeffc64ffe6e9f1e91f77248d6a9dd7129792266cc424618d1f119fdcb80e15420bb339bd44586e3e3e2954e4f1a601c2ba22bd51f8379a74f1222e5359bc69f128a75f791fa75f59f913ca89e54fe510944789d5bea95ef53cee2b77d32a25bd0a592c16abd2d880c2509f13a5f47f3c8d736a200eda4af91b4d815a559f06f5ea49774db7beefbe1fe9cc0f944f79af9553fec5d8208e17efd1d27ba5d20731730997febe911c24a37c97c6927eca7bfdf5687a12ca98c44da6436c36e16a03ea92f237fcc54895349992829738ad8cd351a113065340157c03821fc4574638aab6f8f2e2f296c42b523c5d1c47efe26fb411be225be0f266118cbfd125cd28df98e5c7510a2e6b4679d2f758d21c56a6a91a3367c2e6ce8ccd80643edd7d7a1c9bb099c3f223ee11641ce744fd384ba552a94b7f7d75fa122e6fe47e4c29e94946f1c9a36fd1a3c8b810cf28584802b2c8228bd188321afb2788d296892998f0e9e1a8faa71ab912b14c4cc1041872e31fa20fdfa3dfcde1580eb14bc038fdca09cf1e34e626ccdf58fe7844ad47caae41cd42521beb6d14fddb52c54699418bc9ab5fccb3c91550902a1faca1598af3bb07a2c35b7981a4de6bbf02dde8904c91c8b448a63f51525e03974694e452b7f7a0271aa90d7df59dde895dbe3787a5983b3084a92b280c8012a11129b851f00def1e08f7e1b06d8f8fe57d3bcef9621a62f4a19f40f8a29f74340a471f7a15842ffabed13444ca871e02a517bde7df379a4c43a47ce87d945ef4331bc90efe0704cd20ca87de478b17a17ce855d0e2456f249b8640f99487000a36818f62fa20d0029b409487c0e85170124ef840f9f09348f9d14320fc16df022781f2e1cf9812367dd834c4e849ef63f4a40ffb08513efc145cc4091f298ff2459c289740f906cc98244c8059288f824d9044f8a49fedc3f6298ff2fd36789082a7a35070cc084f2fdf519e67047dedbb2c53357239a466eecbd48f244368f6bf3fb4b7bf8d94654499fadaae817ea17b665a4042ee1e2b769cb615eeeeee54eb21b9bbbb3b6d2bb0bbbb3bed9ed475d1dddde9bbd3ee717777a7ed85ea4ebb2998e0426e1f2bdcdd9dbeb715d7dddddd69f7b8bb7b5be1ee4edfdd69fd42dbd7ba6a7508adeeeeeeb4ab204577dac4dddd9d768fbbbb3bed1ea7ee9436a5eeeeeeb4fbebb883f6fb17a80ff5c9f4bd40772a1c68e005ba6369aa608623997e4fcb9cbcc008b29e2ea7cbd182df741194d020d3efb1319ad1f9810d2aae6b37d4b52304eda6a792fc89b0e41018c1824cc91c98c87a80e0a659078c18b040075321c3850f828a20a376cc93a209ad19abf10405329f10f94401055e8859210b42ac47c63959e208b29eeb3db58ab714a8fa40062da040b21b1ba440d6736a896206400072828592e0b401d6739d0a20345262591803101a54ecf80a90e060a7876a3a42168bd585ed6e5400f9a660628a217c5588019882092e885aa851b1f395010939983bf68642e18928d89b1e20a40a933228216ba0d84441831a40521548eaea784b8525d1af5de866751562ab86d49953ab983a22164c410c53a78754001c303103898a214ebd8a0e6a9f2656d0413d48b058ac13e4eaa914844f6a202dd9df3beb38e15cf193e9d711488b6907c54368f692664a05a5dd943e118f8a26a5ee7fc4d7aa966e80375b4aeaca1ad56834a9857386f63593af91c20dcc19da1b5187f0b9a734fe3f7a827460c214415de6cb7ded4331bcf145a4c44eb1226b37b45f394ef3384ebbf6692eafeb8afa5edfdd2ff76e6f9aa41d6b92a272e8cb318f42a3b7363abb5d3845ed5b294c8418723fa7b770cb70df88c249a36e4411d4e4f479ab4e75cd19acb0ac403fb81f427d1a7c5a7acb9f5c55fdf169552773325fa61ae68be6bde6e192d61d0e1462763ba00efffa1cf71e37d658e5f1993f69d48e1c5cfe2bb0baaa4da3bcb5d51d9e56555cb9f9b23dfdda83ee595e5471aee277f07d49abba1c10b7bcf5a3ce9aa4414dba7021f49fb9ac409952a0994d775daeb324534fd224d569f2884a6d9aa45e759aa4d53567d01fdb0fa1331f8e558f5415cc97aef372e29266a02669e867aea57f05d2e1cf0535497fc3d5058bb024ddd4eaaa40ad32b9b7825ae5ae990eacba7cd56e9828fa3d879b027f81e834c894be2769b94ea6ee2d57e24d509f9f0fc4e17573fa7d439f1b298cead06da42d201691b6209c656af2af268e7bcedf959369385f66bef7de7b695cbf5faf11739b2eba8cdfebdd741fcb8985cf2c326541a65404134531c9ba6d6a8398327deedd4b9a7d4ee77e7beeb79f4dded8e67393db30299c33e66fa055261d37db07b265229e4b9a6d114670806691dbbec7c9a0b3a733da6a929a3abc83ccdd77982669f204f38542d1ac6ff4d1e49c307d435de86b4f5b3af3a5fb7bc390f416a5d45bd4fbef8649fd108744e670777fa3c13105e0173112037e1d4b23144688e9c23d6dc2c3c84d2e81cc9dfc8388e7254800feccde03e4c60d83a38f650958d9ff8673e3f613ecbf63f919ec862293ac4f2b44012bc2ba91af5bbe70a7f57eafc7b2fbe65aa7f55ea07cc23aa9cbfd8a29ab7af77661586b2d31094beded6fb824bddaa6d5e2796395f8f7febd57eff4f3fdbae95fbcca8ba7ec4f9231fa50f421f0cb09c3dc7b676303bdbabddae84637fad3f454e5e9935cb4f8d2d36f2165a45a350a5ff4a1a77fa355e0d3ff21078feed12afbf47d10d12bfc1fd540194ac2f89587b1c2a3c92356de5f7d2f4b1d6168a3f5c6fbe0e87d68045f341ac3474179d298f2a591f42dc6d2bb185746212660e57e1f85f4ebde7063f917a3e955c6172f8e2a7f1a69c6e3112760fc0a3e06ff0b944f489f36d1ade25ea797f45572fa9206fd97f40d7983396afb166101a87ca8144c180b8be029a21900000441006315000030101007442291682c0e34518e0f14000d829a4e6e629d099428c6614619621032600000000000008090c4910040d5325db31821256c9bc0c212f6d734bc1426cb686ec3c398e0e67b070b87956890058686b9cbac8a33c2d6d703116d41e57f5f5da201f25eec1276888067266476edb21f5a870ccb6e3168c79b3a361317ed071453a31151c6b4eebc6bb8fa7f8148edaf6c9a76ed8f01c691845986c3653d21f95c780bee5b2c39f2f8fb58505e59b1f9e2e560e8ad23f1d17c17888b8375e193ebdcf7de3607fac9d47cb573344cb6c039840b4f93fbf71ae3dede87af40f410b732444f0fccc183014c8bc8cf15da442753082e7a0837d5a3b19311e13c019bbaccc5a823d27e5a154ff7b3065b05cabba6190135c7b1c6f5c1fd200a347528092dce211ac5a932db5fdbec3694b7c9745230e7239ebffded8d6bf7d24fda0808b6db6855a117c44aefa98da10f7327b87f622090298656079f0b777edf9a274b883ea8821a65344eb06ce5f1870f627756855686d3adbc6a5ecb6ece22580765597cd6d4a18b1a2c4a111785f766635124bd735b2936428eddbade1056da6e6cb4d7943d63f4cac94b5850ae36921174f27df2edf574d47af5936ab074ab689de91f640ef991edffd35975e298a9adfa103a2bcea98f48c136888a681db396115db70097832f106b4bf706f5fc3c85829950e20f107ec0a2e50536d1c34ace53895b46ee9d30013d8a33df4a784d5614f5353ef4de7e503c44bd9bf726553b332ba8b3223080fd9bda9ac5eb9d038f39a9cd3cf3a1811b9b10f67607f012c43eb1edc6c055515fb56ebf26086aaee62d0bd90f3530583a458b233798c276ad65f3e53cfd741e1579ad82ecaba11b5d9a7abe71ecfb95f45bc782451808015e458089b952bf6a07ccbd2c7b6abe8e7b33fa57287a730fa50d0f58549668c1782a35ae53ba8f2407db6372f41e771a9c46cfcfc1d4c8a77403b1718d35ada1c058918a335501f13ea03cf121e9762577e8a6bf9c8546a26b3bf765288f6c26f9cbde9a07e149c4c494aabf52f29007500c70fe72b4922a9e55edb853bd5cd73b4da6b3100f719810356f1cd7f0f43782b5317db005cb6f0bdf7d907b35f6fa4ddc2ca79596b180513a691595bae44f3c2099cec9bc05d83904506d526fcf5dcbb3ae1e8c21db09e16515df2808589eb6dadd282a0c76863956ac81cff89c078693be9e3cfab53425548339ade7c8e7d6df95a385e1059d690c62fa1d5ff54ac5d41de00feec181818bd49fc626929240c42bc685d835fc5846a03283302fda2cb9828bc16c51c436dc76e82e2d32ce7873bcfceadd34a476d58772e45315c428704131802f7988a7ba07ca6e4aacfd9e6f80c3528a0c619d9cd2ac15b840813960bc17c7a103f6af642542283c793cf340bc32dd37f402c6950e9bc468f2b9b818defb58c5cdc04ab4b58e455218ea9f82e1b2601029bc08c1d1b45ac07fb75c00f69fc3c4550d49140560651be187de9d97aa778414d8ee3ce1edf0462a95872ecd910a60834815ae4f742f89fd5669803343e33a918098ce086047c1e0d83ee201365c589fe8fa8c0716733afb32ae4baedde2736e635c205ba96a4dd4486e99b2050b8513b49c2415f70fc44fd209b94a94d4608ede00f2eafe88864d6cc37ee22e7343772d828762b979a40e12f79b7406a077a14cc11421d52f2c0130a1e9d88219d9e066455eae88dcb4d3c4e196e7781b39ae6ee1ad145b38ea828836bde6226a13c275169c5942ddbd4ff765d156743a36ed6b45f6ff287eae9f59fb29bd482d9631785ff49cd8629d23fb4f5f0f69a7225047ade4a5dc3a85dd91eb125ca581f87ec02e82729dee7d6c8f79c37cd8fde2300a29cc204a59f5e916d0354f9cd1c8cd133cb2ff3ba76c748b34afb8a0a3333810425f1c6df7f5779298d3d1ff7891563e4f2cf6a43574ac68fe454a36bd5603494422f7868c98e17b99e3f438bc45489258e5f65726f637cb5581cca644bc4508e324de0cf19140a431c209a2056e90e9f750c3b15682e54ac83c308e45922b6759746f094801da41c0fd136ee239b824c50caa248fe0e1828766014bdc084c1475090feeab881d7ba30232cf8ff8a9453c8b3304a3781e7194f424b3e0f5466965a1ed2b674cabc5a03843f8dd081a96b3c3183202802b01ed1de0fc48a1b061b81b6c132c2521a265608e2ede19b73cc2ca5dc2f61e37602d695ff844a0e229ba86b447397e883b2f1997b7464705227b0f2cda5ff957d965c68a5df69b65679aabb18db9ff9c89b4cc1a986336503aeedb41da4965377e1116ada8a4d0c3e7d6fbe9c0f507d7da80a8594efa5b67aa20d5454884c1b0a841ec71ccb5fc8d333c164438ffbef2a4326122e899405c20411c1658b1dfc0e69a31901067aca816fab1bdfadd4b4b68cd5bfb4d2c0e6eb52bd1a23c1da656270671a6488eecb095820ef19243d460355034cb1e2dae27ab875c71b6220817f092da4e6600da93bce3b14cf421ac0c6f1e51f7b500b52c51c14e1e3402d7bad5f918750928da3e4b965770d55f3729a55334772c299ceaa3d3c123482b921ee7d9b300d1dd418c98a7e9b88cad8fe73de51b6238edd9f2b29d9361e41fbf21187b7422174f8320536d66b4fd368f33617f2ad79b105283fc8b4f500f2cfe5a3b2cfd94e101d05df25829cd7379935cbb3b1ac55f0567500008f03e8cc792f955eaa709086fdc8e6ec0bf1ed43a49c07e617393d0dd00f5808ccf05ceb1cee0feb493b9bb79b0bad123f486fbd5645ea9b78e2dd8e462cafb56a09501e58e2edf67461da01cd37fa868918d5215c35d24f82a6b92abc289f740b99f4661ccb6ea1b2450fe23c6f25567b91d25c1030064605e398d54281a6c9591b51a47ed3c6901d8e3d82c7e13b4bfa9249ca39a7d764fe16b5a848404abc99422fa93f6807a86400afebf2f28a4baf49ab0b87da1429794e64c2867d81f51e5f51e07ec1fc4421161cfa71041dfa00a7e27e99144493dd9f474afd15844c362cdf5ff5e30f43665db438f1132c507311dd378c816c47138e482cd01182a8daa74c16ed2179b35239033b66cb6749fbcb719ba175f0ebfe52f1849754590d4ac034d119760625a806598a3e5dce4f42938e87c3775320c37802f8f3612ba708a92db8243fd8c1406b84ce92e7399a433d47af99f3e103b35e083ef35519e40a4268853a5838572a6506588dd0f90b17ef319c52eb7ec70087dcb8a1177166a1a4631518bb1d9539fe8e7c2f678efe1867a9d048ab2ba2f4c9554b6a0d835a3dd1c7cfb794148ae56da02e9b6af33e798d0fea86eb625782845e01ff665bb99c30d7c9d6a0b54939a1cbb5fbdb39126d6c3350f930026a08327342f7162081de721dc2211c380f77c11f5f50ef2861d812dd7e0f4905cde6bb4efc0fda77894cf06d2a7ecfa032ca419f24744b0bb336805d909a5169287868522670ce83b63a675c73893f18e8e496aa5fd1b7c62ccf7332849ca0d4f044cff75f1e6e12905c6a1f209c6ab2f994bd991a66b04e9f2eca83d27f8110d96c5a840d385baddbfb08b440f4bb059fc7d1a273c0844c1117cc57d494cb3716f63f9d8f5cc0ccc62c6e84c370b39e820d669292e8b7bfd306f42ca362ae7381b8b356635f48e66abe676a51b6bb467ce26fc6957074d66ed1d7d0f14e26ede2b28dc7ec05569ba6fb4bdcf1d79d3337c7d4798c8996a97496ee05122e5ccf2afca062bcf40b7a381eef7ea4842a79a5fd96a274f1c595e86eec6711e033e73556118334f96ec0cb7e54fcce5262f03dbebaac9a590cfd65ec23cce649cb9461a2394ac84af57b9b6a709f67074e86bf8e727e4fd45105684297ebc525c2dae7605b286f6cd7e3f5cfd6193d5a73929b1d7e029755175e885fc9e78113907de191ab74c68b176bb4c1f3e340ed71ea7933592f76d7863946669fa4b70872062a76fe44444de7fd9038a15ff90fa07853ac009bd1716574a48782c03ee6665867cf9c99b1b3f845ba5d6984eace854b9f04caf3308b52122d543402efecd3f8d78e26ccb70a8120ce9e5c6523925e168597c077dcae38976aa0e4dd3e0620a8b67154690e4f85a57d978540466211343cb8654b5d4d7029be8e85713bccbba4c5a7f240cbeffaedba2a4647dc526bf00371a246c6df273a65433c098accfdd5ee145e97a93bd3a3fc27347e0825efd5e41ad6924ebe2db3dd15688528d198a336313067f6c300f6550624fa74cd421780d2084322c436886a54fb2a423cd16472e46f3479370825624d0f84251874eeb454594cb9eb98f463ee97292a5828bd918d66c6b52fd4b3d7a545248c2070fe13aa10ac8c057fc6c1698ab5dbea554a75730b18dac1ca6e1155d76e3cedf5b9548eebee830d23abd7dba32d9c59091e06bf59e553fae8ec0abd415fc0a22a6b8b4c1f25d954b2278f4b61bc9fb2d803a84eb822a920159575162b849a6d02a8acdc4aef12bb0b64764db0a7ae5056e8d01c34a2d4e5661d31aca0d390483f51687e56fd137078ba2d4c43d7f957aff8497cc311dc4e642fcb5a8e7ee406c7355c6f3f26aa9fa5d6d5f0a8c2532f8be6a80dcf8e35c470c5d7b64ed9cbdbc86c58e1e5e8cc6b35f223dd5c45b8e2e5b2c81a499bf31d4caef52048032187f3af3f2280ff050927a27f854855b438c25ae510a7d03b630fd265470116b015f80ae31a62bfa4d1bb600d5a72b3831e49b32dee6c537793c871777850cc5be9a60cd3b85b6c8ad2c8a8f9c48755442a89c44dd83d162144b7685dc8515939df7511ea62d87495a3ac9f400932b060c2aa524f6c84a3099d70b39308ccb49e7b9e42cb64e1a2e40ab64528d4db5ea046bfeb80669e2d332fddff4e9c1f5afdd593e94003da45761bd8a8a42e8e5be590002e65b3a89e5df9b7320bdbe03738920970ad8e4853202ea2ea27a22765f425486e544a605ddb9431adbe0644c11e1991e525e419ef096d24691db52ce7acf405e83c490dc3c64da2a76bb6f6e2d59a0b30ee17a054c04815e328d4de67a5af8c292c17a71c39e6912b90bae8395ceb1a97456f494959863d94e109297d1f1ee3497b9a599d0937df98659a0a78a2c960137f5c4eeb769e26a17a31437241c51da19bdb8f235f314299136b664ba416896dea1a913f33374fb4de61f181056ea3862b35c3142b9641afb3d75c17919fde4e66ae9441a6cc94bea55dc9bf60a7e917f7b252a2d3195d8757926be2b4c6c715c9d6080552f763812537e53914ccbf252ef65c1cebfafdc6b7ab1a6f0eb66ed05fba4992c8c7b039891984acb628eeca65c9a9856a67740d22466a92f50795ade3d6ac462e7ea066bffb2c4e279dccf7d8ba017a523f7e618dcea3a4f5f4a0d56588d8036910067213dbe194ae45d51d2d4082606ad182455e8fb0bbd892044ad9aa0c0507a193676d9182c01b42592d7d8a6a337c1a4c45863f585a69c16a2cfd0bfc9b1441916cba920f811e477cb5c2bedfced0834b9658b0d4ccee33acdf6ab0392c28395df20f711f7f3343725d6f16005fc3ff0e4b6e948f69974db0136339f575d649405d583a0e31e4789e9d13b814eefe837af6943ff1f5d2f9d1aa398455b2d28ce6e3501786fac5fcdf47e387ac62514df53b7f7d5b0ad853491b7eb287cac1d8aac80046df432af71988eee24ca0cbd70db8e7e64a765211551b29adfa7c845a752078768344ed777a430f5d51df11038e3f3a77d6b9b8f283e11b3f4761126a5b1b230bcb912d35b234426651f98735cbbd856f481e91ad6642ddd75a13dc0838c2f8e95ca114793d50f31ae3900be64ecea067ec88afc82f06a7589e5d2b7e99757e740705d06731f35cb86d1930a3b8ad689174e91233a64ed9371dbd814aa33f1d3ecd1224aeacf438b8d63ba795139ac1ca005063f919913c448922689437803963c78c8e02f4cca0ab9d3c28348af455bd5ef453c140699b5d1c59aa32efa6d283d10831cdcc54ce222674511610c5fbea724bb1d7cf8cf36120feae9c46fb42a4e0a7f84013ad3a676802a0120603f74b7e18c6e17c18da068054c8131a8c55b78cc169c591a5744b261b04c0a37168e91bad05003a5dec9a230dd8c3f036c5c5e91bf76e396ecfc2742a9371b159d947b7cf185cd6464d9dbb7dcc3d159ca4ae89e32d07eb4653a3f3f809fe1869f5712b52808510cd062fc52a75ba908cbbbb273c4fba4454c8947de657777ddb08d0bed4702eaf64940984ba63c4610e0684fe213e7008dc9a0d122fc70975fc4d18e12b4c8d567b235176169ed5423affce25912a8f654d72f7d6a11e8ca5c4ab30abf614088e021b964f30dacab97e9dac0f740bd10476166d509d4b273189c20d5f0011213f2ee8055350a7a0bdd73718449729afe1aabaa35edf4f948a215a7b7df743d1c71dbd34af7432e9d1179a22fe33a93032f0f673e6fbebace5d3262e335748e0905568c19b8bbaab62f20d61288780995278cf08920abba3e0b8f704f57e24908d86c7a1f05aa21f4a9acaa85f4d9454ac5d3ef58d80007a1291ae1e2b5c95712edab46cfcd682c0618fd900a264d90bf4006640800fef7f693e0e21655e96a235a0464957e1f2158029e3f242332f205c437ba002ecb9233b611689d4b1572728bc9b2a54a7ae524021b964ed9054bc0676c47b260abecbd1a495cfcee554bc034b13eef69576be2d98ac01f5467e02fa4d45293d0f14a09f84add47740630a6b05e8184c3e90ffad07cdc7af357ffabbe5441c03a8e8258613fec18a85810d0408fcc905e01343e87ff0cc282c67c857135f72179280bac2f1a634bc420e0436af101641dd7e4d4dce63a06cd4e717c19faebf72110300c763e7d9c5ecdbceffb84059de8b84925adb57cb937488817a19a7504d3bb82804d4e08640a1e5162b01d8a7da90c4d1dd18c6de83167d15bacfdf6da17f8568afd3b94105966b5d36b8263ea673640dc9e67d261546a087d446ce3fa019b8bdaca8bbfda71f27f5c8d6725d929594ee29baf8942f137ba517d416474b352ff76ddd7e7957a03a428642df331142b33accf3a4619c66089c806d848ad1389459b58fed954232cbabda45221f906f8105e4cf420e11de26fcee9c597615b3fecf3c641a1a48e97c95312387f8b4e0dd0156a51b3443e72755a1297257c7721d5a96927b297e11c96a5309eeaf273440dcc8b4b969cf56be7d6914181b107bdaa10f81b6317a79064c7555544c5044696751342d08c1b6424d210516041466dd6e3af47e4173be60b17e85c27e31da9f7328016f7c63e25c0735d0b08cc872926ade70ff02b5092709a3d08d5286d45dfabb0951902a20e400103edc1c18f51045f5a3e370583bcb120bc2ac27b99ea0f923ad39bfb78d6d2dce3f3c50c21cd1d8c053a137e870ef1ae142f9f68bd567b9eb0c162e1e00c50518351a3629005159ad466738955e2339a528c676b0b71b93f6c7ab40410e7e0c7b4442c507e7c7e9a1e3517ea3aade02dfb0ed943a8e5227e4f9ce252fb373c0f9e80f986112aa9c008a09079e1a45e506ad953a2069392543437e12352b51af941bb6daf5b26e10c4c50a9beab62b0bc5dc0ade6b944f7af61b73f4fa76b4308d1bb6e8699d20d510295f1d06d23af4113d588f62207d7bd150a754edcfbe4c0e28c540e59084e2daef7c307b01dc9549272df3b4605e183b2cb4f54331162156eaeb5ac2a2960a535454d153fa49af94194c921f140732282c0cfcfbb038b85096a19763a3f52b606ffc44bcc04f0e1084a627f4013283802f9875896c33400e81fa9950d001914e959e0f1307a216e33144894c014ee640e995ddaeeb195cb83cd3864be569ef3cede0869b06b00a7ee26ab36e90c708ba48f9ece423f9a66163f8216da57faab78fe65c23f5bcd851f1865438598ef927160009ed0558e054d30c8b185b3c8ac273a01b9a64ffe853e5531ed299fa0905788db763fe2c8ed9868d78623afd1b6600ee5d544d2be4aeb99b07f906bada903568bb214b3385f26387fd953c4e227faee6aa35fa4e8a4b610c8a2ec53fb368b9e4918db8892bb2f6e3a4a7fb06a4291cd58a26fb6d91128ce2fb8390aade555c83bb2c29a03d041e33aa65e291567fb2717275ca8213f0f7c56e2087e24ac6740069bb2e02f37882648ebc0833a6948156bb511776c67c02b5621b1df7de7c7ed966e7ade592c2b1c3616718c406357fe4a91d714712ab460f537b722f334af49524712f2d78c11dd7ffbb1cba75c0ad0e3935ea9bf42364c4118075b0268faaf5b61254514c0a6e693e89934c892b82be6b4ce3ae771a802d688fc16b1429155df7f95a33550c3a8cedfd3e6be56112b5087b66a7465982aac6c53078b3decad970633f7cfe5355ad78453f3b791d88d5116ba0b5ca7480eec68aba7642eb3466772adf514607bba38ea4fdaa36b107982602e476d769aef3757d85a455ae88ce12f105e70394dcbae6d8c6b6f79116f1433dc1fc0a37c9ed1ae62d1903ef6fabdd69c10ee354970f35ce3e9acdc2bd389d73256ffd773c55845ed82d9570da64aea83a53217bbfeb5b43458d0826e26cde0797986214c0139dd076be78377c81b7266f398b455f7560ce29fb6aca3bcb801e86ced0c24ebe47ecf41f91b404f37364064c112975f5e33ae505bec1bb2be6421a7ae2cfbd3ad5f4d28b89a7f925a8646f8edf56a0182a38f16394a4920bf426031ce30f5d7e3aeef3a13e2c411ae7cf089d36c53ed7a57cacea805d1941ca8961c0b3965bba6dbd3e71bdc1f7f91e651a9f0c8acb5f66fb8106b80daf43dc10230449e9b56eb7d4a00b838d5edf151b396ce4fdb8f9b4c3f728f379a05ea623e9877119f29c91fd13722086b1db7c7e9d63a6ca800e0590854e51b7d2319fc2695f5232d65a0200091b47a811255001c2dda1917c8a73f0f7a29772f74963b90e39a8e60b8d4018a79604c37f54ce2162b6553b2dbdc35898f288c22c25fa1730748cd9fd731d1672a683e2e816675df9dc1041c4234bc42d9fc0445f38ed6c0a8757b6bf64b32008f8c045b071d564c85fd3c8415a61348f9750fba14b829d5177e7c0e29cdfa6bd5d589fc1d14eaa526d095e12500ee30313541bab14d8ad2a1f24149eb7537883b9c37c521aaa86bae83f8e192436224b9026a0e2f6a89aa4640818766cbf8c5cb717f1ec97b056da6aa016df844a4e26be9ab62ec7607fd73c5ec994c3e1e61a40d0055924043612a950009a37febe918336e3879e90b59d703263d1eff31cb41be528afd1b523f520c49081d1fc796977714a10cdf317ff6c36a2930566b5d0ae7de20cf638d96070622e302553b05a617ef704a5444b763984f552fca11441d6a3c6bc0a39588577a6aa4fc8029a773576ff27ee990594ccd2bdd6f9d31a81f3a8e0366ac01d111f68a50313b9c9cb85193d2f95212d1481f8e41e3acc25883b7f1705c70336ab64d0631fbb85485040c200cfc8c410128c743c198a375e950768215fb3c6e99145a7027329241a10f02f72514bf4942ffb6353356c08abcbb7e156b10940e85d6265c1dd214897c06682be46647639e1861026c9acad9b63b5035d840b1023cb934e1a7377431b86959b8087ab57b08e676f8bd095e8baea2dbd0a7b520dab63db279043da3b8d5c184ef0635f554eded86dbb494f98c4f7b5b915b124e26df2d0c64dedc5456d4d35c4012cc887a0a8ecb84a8cb591d09c6407a9e90aae76db6d05850c4a7505bac74ad53a830aca5ba1f2c32d41cc5ddaae041bee068c565d766aaab05a8f6a2981381e3005d8e790e47e43baa07c7b720c8b8aae25be25a6838bf58540914d9424e49525a5ae4c198e3b149cdf4862c88c210b774f37466d63dcefdb841010c71e8823428e86015d6d346b1455fc50c51df200932283e609616dcd93d615704697b04fab3408f8360c2efcc9e0dfeeb04c2e543d40a341029c0f3e8218736bc0aa079a578e2f409990cb459824b2da708a312bedafcae64d5e202b4b2fb4f0a00b326e6326651e19207dc530e1e2d09be55e463f013ea18509ae2c014121e68d4cb194d00f86668df8ceb068f93b15acf7dddc30ce95701949508ee8d0e55c2fb49fcd1475ffe074b3cf5abdfa5b7aee7ad381d42ed45d632d98ac3ffad491285e9ae2608ecf46cf5fcd0443938ffc1a5a1c4d49f83ac95bd138ad5cdb4eaea7037345557ae1c0d255528059c10a8fa067d1049b287fcce22ebd7015f7f445ba974091fdd1da9e6f691668970467f444187e710cccc5f77f224094b48dac848b49f65506a079e98da82580606d366ce3555a5e6b90293b675271914854e4dacfb2ea2de58fea5730e216ed500f18c17f3e642f2df8e88c8ea1aad576d081c58b866beb7c7a5872f635a4ce156558a98eb8512c75ebd3612b8e377fc9dafba22c6e5c2a88035fad10eb60e8872a283c1f80c200126b01f995f4618b56b924c9139e01c6a7753c37bec63b948615444429980e505c422b4f658104e03a683eb80055898805007d79a608f7b2c15935d19b008d663289d46038b541e0396297df638afd580528dbf85e3dfda15f10c70705fb0b3f2694184109e2d5d25c9c767e247c22e5d2513a45ec02cdd21b2ebca18209aa648cdbd02b20a113f5a295f772eb30a1902245fff79142166352231994521496fd8ab1ec6a8c00421c3223290a0c562b5549a04b18e0040e472de7ecfb792ac3a1d813e9db44d39fccf4ec464425c056c99fe0dfe9d625ec81e3291039cf5323fb11b155825c6fdf561f28ebde728012fbb01ca44e6dcc81b38d66bb4342aa543ca8356eccb2b40d8461d04a600e27be1530575704a8b4e0c02635c96d13c51b10026212690d1814aa065a883d7e534265bc27ffeac1658c2634ab5f4a854ce36247158dcb8438ef84bcc70a262c77f0a3d854b1085181e098d693ee07c2802ea2f10ecdbed6ec4204a071b12c3fb10cd62d28537b5349654b04a2de67cd446527eef8456881c6f02d69f8ea3f0b180ef91e07fe1d304fc6f7e91eac1ae76e92311abe834311e596fbd08f830185aa2ba001e03431a95680531e67e880bd4d3af5144df9ded5f7a4732d23b0d24480138c47ec913d0b21bc35fda48ae107f0b1e21fb1bf02c0c788840dc64cbb2ba8850324c954bb59c2520ad25d74047d5f45772c16efec36a569c489bb058c0ac54089822b8e653eb0bedee244a5d4333d97b0f401b23d51ac03f950cdc0255994c2c6728b6402c0f03944859c5d364714333d4fb406e93dce3dee7b40feadcc10636f2add1a64c5df50c56b1d3446957d60cb2de27d9e1b8c9f921fe1f4a44402dadf969e6f2d03e434725424a10d596f2f0ab21d589d624e64be67ad6213e08aa9f23b4970920c70358ce31f3367f92a0f0dbac4534615308fb338cc977e8428d0ff7164a4b05298d6710edb4a97c4eba56d031fcdc6a0b2e91e82ede9de3ca048182795501610cf32e973ab524781a15531b04abc0a01c099a8f258bac6035857e4b384d9fdc899f6e2addb483b3fa7960d39a41ba7f0a0a809cacfcb6e88f42351cef124187bd9a279ce6633d1f33c492e5790357bdca2f4ad624aea9661add86fbb037437e9dc61bc76190c6df23f4a6e834d254824dff961eaf962085219eb5babcbeb77522a55a9af84a5f8d7ceaa05ea58b3f9f59bed095b67de356d6cb6d1fea54daf6d98109514688ba6780de41d7ed6fd04880d3d04f357e5a8d28450975c8f987d86dcbcee8c8077323ef2519d63a421461b3f6d75f5175d3438314e35951e3f6e9f1d8053ca9faf458df846480a761e2fb93bc1fe86fd1635e09954e3721e913ae1886d8d94f97f46508de682acf28e6d864040a2d6311de087c9511bdb4e3706a3eec9a909b6432495593da19019721fd98a22a2f725391e9749ca491d1b64ee46bd4dee46bdd775d602d95d490de2a809c9bce27f1f3feb754bcce8546fd08f78027e646557ef01c41ea1dee1af1392c1a0361e716357ce0cb7b696be6dd79173b60ab124f1e61386630bcd30ec2553552689dfeb12ba9d411867f5c7c4c6a445183c3f6526604ac56afb061a881b14b4a7b7dd952a1c905f175410c149bc0ab9671117a4c237b2a344ec77905823138778bdfa25eafc097e7ea618f0b6d3cd2629a020a0de4b59a604802ea744c07bfaaf1288f083b50debd4f62d66092cf88baf8677c0ada54e81c6de19dd578f0de37dfc8dfa7d1f3c851f978b5f4b42bd8c990619fbeba4e6f3498ac946b1732c72255946000dce8742730b3728837066602a7242cfab7dfd11472f369b3df46dd3d13ad19987615102180a822887f97e32912f299e9c11bbc8791fb8c4b6acacb073a2b159826589bd3dc5e8cf0eb53d844ba78130a5ece095b1f0fd94c3654dd501dbb6941280a010d776320913aa789e89db66d721ad3416239faefc11e872360c3bad5d16c3d9491fe7924dadb6a1c6f44e66f4f836ba76f897debbfd48381eb20502eb19e48fad6abacdd95747a513e8fd3cd6274c2f20cc05d2594d0396397c8c16fbae7a7123e4be67ed6aa24abeba1e1e43edafd0753f0080a4a11c469bf712f575faaddc07929d04c399f38528be6a3f4306df394fce2b6531b7f8f1c0359f7b968884fabd24d4dac5b9ee48edf24d07e4a1e921797c31952bed34065810512b3b9485ad3955af15f1e67f595d0a08525003cbf5fdf7e5d5c527137a39d92d8db140800b93aa1b20f07b4c5aa3d3df61890b1cb7e6dec166b0df6bdb0a63d5f6682a5390bc9c5af895e869b493f865eaa18e829058feacb97ac05781e439af54c41ba506c4433d6341fb24754f1a224762578c027d4997e7240b08081a5be4107e0e7592cd0500d636f6255a7b40fcc88981091a8520203443f32f46826b29e11e3246c7a82205c62235af7867b91605ed7865b2aa16d62bf38cb05a689e39d01129cb3fb92f23fcdff7283b68d80581c7b3142d0c1d56b2b29ad5093d00a5cf746e016a5be797dbfd8a01527c82f7906287fec8d91f578146063c94eb637ead774f2c8e79e024eddc5f7a5ae8c80739ac7390dd98bbcc7936290087b71a1385d13cadd2ee622993e9776b8db6e5521a7ab892b632e141359cd868b1071db241fc2dcd49259e0ef27e481bf9be00bebfde8c97353077bfa69d12e3e435cd143194adb8e1896b7edd69a68ccfba43f1e38d96b406efe85606549dbb5cb694146e9f52f1bca0e3ca3fe19d751f4baaa08292c0c76c0a17b21e6fdd04a0db12d5ae5a75478775ee4aac8cf72ed9539700d50ee1986eae2409cd7c3da12cfbf57920934b752542d8ce6f5831d9d1707d12a2d427192ace7ce0b37b2462c69b9d5633d42e3c2d54ae7b5da689b14f8e84d01b5d588b370f8a4e04f115ee51efaa69227549777ce54ed075bc3d51f139182b469764b9185f5c8883e11dda65964d01e54d8631bb3d4944abb4b16b212ba2f6455e500495dc5113d28148b46be3f5e2833992fc0bb416e441e9d28607b72d89d5a526ad2f91521384106bb1e43ff3a4a71935c139d46e94e71d7ee572c3a0508a7aeadd323d876ccd9384c1e8e102d7ca6d97da5e8e57c509094a52b3c234839047cc76c06b20861e4aefe4450a5d87311583b76bf849634c651c0b158c3cee05f6c641aaea5410d924f4bee0e3d0aff83b2e55420e2abb5ce26460f0cb261fa0530320c9cf7004ca212f4ee04486a74e493ad33a7d8cf1dff278a82c243da38a123241865fab718f4d5cdf43ec07762481d6da09c0f0c6c69464849bc2c826f190cdae0a177e80fc764312aaed274e636c1b23b67227fc6e9536a31a0f634fe196fff6029ef89dd4694886bd0965b6878b37910aedb185792ec5ce28468e95807d11c0c39adb98f87297403cf8664db0cbd55e3e00690b5a444603bd01b55a04f2149556b481f58d116f068e50da138c18f8006373b965eba8260583c22f2baa953eb86bcfc7b2d6119d91a9e0d86b94795f01d17b8c419fa50708601c0ce651cc75e1d5ea9c6340d2395f643baea0ec81f173fccebcf57c187a14ffe8774de590962334b3a9f149e320a886843c650e4ef7ca0bfcf9aa1f9acdc989acd73bbab1fe0607953b42605d9dd3b1a4ca79d10cfcda726600f7531db4442c34d2968d7003d6d98f4327deb0e4f9acd448bd83e97c8038ba7d81aad7ec13756fab171f83778ad3a38965a87c12d80d53f36f7c5e6441e52da15b79a9d397d8eb1a8a95c0565b25bdc6aa6526ca2a168bfcd34432dd20091ea9839e1419b11a1b5245e85d08af241c7e1f6d8226d79470570fc436f7d116e3b5882f7263e83d12d7bb69faef5bbd68319368bce8357302a8630a803d1fe89c4013c5125e15155f7373aefd3a788e87e1cadbaf034d7e187afa8decd62635d00928bc783c6076b309871c0bcc28896b42af533d8993034f30383abe7b8d673b2a81a02a4422975662cbcd26e7aa06a495a43ad254582bd802401259a95bc24d7684f1df7de8ffa7cb443e04e1f720d12cf384597fd2ac759376d78a2ac3bc7554de70a1ce349e620bee7a240488a720ca2719d0cd52db4888fbb38d0997e60bf11fbbcbd6976a6d796bd63b38ecfb868f3f6feeb437b7bdd8b71c9d1031a1929680ee299bccabcadc4a55a883490df389a8a0d6d6762649bcab0ba7d2d049d821dae6331a3b28d5187c5afed7c21ce99d5b01f4a2153dfaeb68823f0c147c7efb7e9dc4caa1726b5e5dfe4cacb3f9f567e64a2684f2cd2a309cfa346d96cf2d0fa0a12321217ef181313e23a6b4637980c3c56c96c865da1af3d148789e18906f30a6fd233702d4381c8fbdc312cb1b411b2d5b7f71891a943608620090a22833f232f9f5deda8710cbdf2efeaada4d113060b2a18ad26c0e7ad42ef2de048c739c5580069bd35dbc8c7234d48a4b7b66852ef3bfe9a938ea4431626219ac89f5ee0093786c957e4eed0c8599e482f297abe329156d2482fd54eeba85b7b32ec1d616ad236511056ade5079bd9edaa3585a737882ce4d839370209a01e682aacce33fc8130bb3d384c496b5342b06b86ccc4d0ce3a9d4ed13be31d1ca88b9e05e8db6e04068cbcf4328f59ed1e181865e1199d1c749ad144ef8f62135fdd994202e7dd434c464d0d3169d4ffb8a3b58d2850a79042144f39b9bf8bd8f3c3202dc43d8fd0cb9b6a1c0352bab00eb8bbf4247f63e79a382812a60276a8ec378bfd0feda9b0aa55142cfaed07cfcec1841eda73c058039c879848eb5fbeb953314e320bd1efb4e5db79833826cfc37be4942af1ba9066083838fd6403035acabb308b075bf43acd12eb0b8968d15516450e3cef4e4a276ceced81e2fff0187052431d3accfe062ec45e4676a935d7bb16b2343e92223490a68e208ac04f292e413860dbed642f4f595f93275c7199066689f37cbdea570150b6f93a44c981d2d92b918592cdb762a1bbc51f039a21bd527bb8d3dcf3d2d4637d484eeea26e491f254ce82b82f429694fdee245306df5f3399bbc2992c9c0254fd87fb754e4848590476dc70cedb32dae66fff3dd20ca57048070c8dbc14a22360bc5442d3e14cf430adaabdf0aedf75fb50ec2f02db5bb2f9d4ef3188412f4ee97150d44cc37c18f0bd5f57ee8a376cb8e768612d829a03c473dad6a9651cae9fb4bea2c4b2baf377d88d2b1496c24c25227b48ae983d3d39f6614f6e5f165445f9c892b8f602307a4044a61fac9d144c736c0ffab5a40be92ab4832efad4713b9fef024ff174e23e6cf886638652e4c09ee1d099a39f16b50af6cc8aa80c56edeb675d50563016b5d5a6c3f0d790acb48a839958216fb980c4048f4b96648f370f3ccc4bb2237d9f602c80beee95908b0715db92ad2b85c1989704bd4b41bcfe6bbb208990c73fc039c74092c3197f1434e83a92f54af67a23cb3f4d82b9aed6b33cc92f9a3a7054d42068026cfd790e3f7a0b924de1de639daa7284c6731f51e048ff65655d37532647617159a31d2939052f11d04c8ae6a5b968e655fce5178801cffe9b26ca5dc63264c3ed5cb6dd39a12ac0d88163f662f4b145c879bb90f07f787f3777ad03c37731948bcfa5bc3778884d417ba7b8b57b90940defc0b0d76d6a775807d7a3eab14c53dced5a35e0b0ec19076cf4c1a7fbe6738dc905498a54a57c9fca57ce3bea6a709d81225b2e052e9a962ee6f18ad96de70231f6bd6f0f804d9ca5b9dc7323bfcfb5752769362db2ed9747ec823a329c08baa2d3536a996b2384986ef9da0a1690bd8ca782c8a59aaa48e3b3c02985b50b780cf6208484cea56e3f29a7152427159c802046ab15ad675ae6e2b44bb9d20f5eef1013e3355363dd3a1cfa9284dd2d5990b8806c32fafe2ae2dda9c93ea8f9e85a80900d2a1e916f2ccf053f62a197a7752f4fc067b2c556c8596874147d40f3333fcbc6332c11e0c06d8eb069c2114104243be40643b934ae34aa7b1c30deeb718ae21bf254e9d2cd2c2f8bab79a5cbb0c0454de09ea03029e6b369b27290d5212301db28179f08b73a6ad5c7b3cc8494671f2bd02c2a666cf9b32ec34429c9a378d960e9188bcff999c1070c9ef8332c64e2d17f4b9b0c09dde168c4c1cfd1f73bc6e6b16c74a1a9e0ee340e2ebc2adc551e7e914c2f33c816d2100aa17cf46647f688feedb53ef5043cc1c6f8f481083d404099908e763a33205a358fa7f30bf8b105ffbf6ff249bcf1325a6fcc9846ce0e92eccddefbfc2f0db402d3db28daa4117b09cc220b429bc993ad5ace8a90b5835d9f1863785379844299165934e08486d29b78f56c3d30e0605f26bcb12a071b03df86891cee2025a09a66248d9ff2bc9e8ca8e0c20259fff63fe4aea4e2b164a696b1f1573e692d499346986e94329cffa8ed0c8d05a5f57e1de2f4e6a30c9262a4ac52fd8b97c8e9df6d3d0dc96d4418cf53a0114987409a870399e968081151510d6c4142abfbaacd1a3281d377109d3be2ec848afd7324cd0c139f6da990be2d9cd424e9ff976968b30b21eb67238aa552becb1bfe239a7b6f9a3756f3f6ce12b9f8849945488bad577bea9caa83ab531729c7d72477d799bf2b70ab7945fd75a5358c18a2a6bb80d94e38a77005e581704133f621e424fb18b9ac042042acb8da9f31d9e088088601208b715328fba07ac7241db8c2293c3a1779b3fcd5017e4a61ba374e5d72887e2a16c0f809ea013688e02a7e17af8d1cec34550f74b931d9242c5c6b68b595f1ce457abb13f66f9e2f3909b21cb230b6acd344812b3574afb5f92fa264ad5edafe37c8663f0cb6cc2e8acf4f3bc27297f14b71dc03531a35975f4e15c84c12ae9fa894c5141e97825145b2efb5fa4103c463d65034737dba107fb124bc0401ec658c762f968e6acf88a1cc2026be847cb6df4a33044758712ab71d91b87f7dc955985616e57bac91bbbf309cbd5549b429f448a6dc87dde1968c261f40e210634c134f578f344b43c3524eb9001104c2cbd8a668986ef6b3ac98334a85b13ea13135cddd01ed144811ef4516912e1e9028a8dd8843f84842be9220180cb0429b03a421aeb6228da9dc9d8d0b35d8cfd46fde9f0b1f2903684e01eddecad2843cedc2c91dafc0a69e9f573e288771407d28bf04b063ef31082fc38da84bb17c3bc6dce9400a9828fdfd937d31a4c02f612f1887463c0ff51fc53ac4bc4f75f946829bc865bf24173bc21565442fc521e22cf20904e1ea0e6423d031b049348adf609cdd761da2039a12290dfaa47d45fe2c763c96c80ddea415dea1666ef64ac2e2e8c95ddaa22fea770d36efe727cad17e61d921c960977c14cdf19262c6862dfcf09a49a766c7511d49131fae1e064fa81cd0ab2e25e3a3eaa46b260d4cb8e46ad71161879491da146a542e9b52fdfb1252c87242918e90a3671882a8fd42e6d70dfb293fd490cb8845f2821a791c438b83054f5c94dba76d46b61f65b3afa6aa5698ed4b45ff5fc10d69ab5feeab16f3e857c390cfb4351f401ac4ae4fa06a73ab3575db4a877f5cbe6c17d6990bc2569c2337665397edf2bb34e16894cf233384efb7f599220d5fe247712d013abdebb79a63baee42aa6d8eb8161b0cfb8dbade108c29dca5ff1decd7b12eb0452b6d638c59f2b10dccc95c81da4ef61fe89417a259e13e4327da78d7a255befcca91e135f5d8e3b529165cecb4635763a0c77d7a5fc7242f05d7a4f18337f756e40d1f5c725a15bd1e85f2a8e80d55403cea1d121eb41a9805fba441e789a55d26fa5989f6e29c87db7f84f88144cb014c0a7d10979c685f5d31c25ab2c45ff74a343aebd857d42aa40a8a5803c8d9bc81e1789dfc73376a6dead9dfc5c0a46e18ba1ff649a75ceb50b3ee7cd5134a2ad9f79a65c786df9e7181b52361ff3077eba7fb68b8514fa9aa98fe59924e2d0a7b3e78356915e0f5a790eff0a5acf60acb42975caafe24e28f99d0e0a967b37c493165af1305e09b3c4888fc4025f8ed10e69efbc8ebac6c21713927377ca2a4a1d1d698e4c1d64a421a8799c736209a1d1dd46bcd8a994e02781df00941b7392d91168266d1e073e430881f24e74b3142e3ad56cab59f5e4d359193f79b09b23d370214d559edcee9a00440c5d5ef18c7148f10cd0a845b864a9bad4308eda6640f99061ce856ba9c6fe55906f0ad5dc8789337c531c7b2f2d2e9349f2453df7392d98f06b2072145568fafb9e8ebc1f19ffe69ac653857370036ac7fb11f373263b64957c8e9e8552ca9d966455990d75cef59ed60e33a3b3ff55ba2546a73b8916a614f9e531f8695239bc5534fb1b5247de79a8579795f1ece42fb826abf7ec54ddc81bf7ec2c2a7c8ccacef723e3df3c7aeba1b016eb6cb6c87353adfb8a2afc8e8acf9035ceec162a1e6b76dc2c224cab1b293476e45cda02e8f1f2d35f1f6fb752ab06f1e34d786a536c2906711fcfd29de7f1449ef05c96d2a73d8d2dbbdd2deee5d0ccee2e7b2f2fd1ab95834b98669eaf039145a0d6174fe85a5c245694e9b4877b904656c6aafb8b0bca9e8bd43962dabc885680e7dfda6eef6b8cc25e2cdb17d060838645303e057dc573f15a41eefa9d0539c2ae27686c22bb986c42800fe617313f678be82981e8e0a11294503ceffe5db8dee5c17624241601ea9700c5858287ded5be24f05ae5412ce027ab24e22b5e888543849f34e42a09cce0746160159186ba5da51afdd706e625766834a628c2d2addccce983076f3f64b17fb3467f0eef18a1d58cf29e792016f71e383fcb2df61936ece69a6afcb7b3d8ecc84bfd214d79000a9725fce0ba5c6679caa3d021d7ffb50d80847dc31daf91341e1878b78caa56e207f4928413c481f38db8a1f44beda91fa632d3e7ae62fc2b36e878c5cb618b0d14af23055dba6d0a60fa40b6b1aed7bdfa6fe16d36c856b900c4350ba4c94b94a764c92b894a7df093242dd54faf731f7667c5bdda4be4005a34aa4b62cc443599d5d014e3c6fd9111a96c621acfc8ad6e308ea766d11f1773039b1dc46e981bc790bb9bb9952eebfac58a5448b6788208a86db7d3106ab2efa24735fc5cf84b6c13d31b4687dbe4da89efe6632fea2e1a341bfce6deab7f1725115f82013ae2753770fd26a8c9768b95abb889f1b2600650e12634da4d524edc699e21c39690b61452eac0bdd8508bc08c171611354ac65d17ec2fc5b6e494c7c94034e0815c4e8141ad9a61c38b06ce54d3f02d3126450d22fd5e332b1777bc276f8dda7999f6658f04f98f9939fbf343732644317fcd990842372e04d952d50d9f7cd278034f336be9398fe6f522df18e3a1a14c9414dafb01ec47c4575bfbe5deba37492c58df0dc815d88aad37353a9ee0d083e9097947d5b5cb7f2301b2674a728e1e5f10a5694ad6fac04e07fecc38e9d4a49e80df558c2ac46e91a92b8170474a2e672bb38978f72328b1c8534b552ec70a698b7744cdcab546e3c7a22dfd44f704e3512801ac4aa6e8ea2a69e53d8ce7a5e7b0cc984cbbd72d45328317e086ea09f8318a73036f58439e0ae00d930d4a04296cfe93966240e2c071b6d6e59483ed1d3429275023d5c44ed411f37a187cbefdc3c796c1f00e93d11b49fef424a89c188d057c0da2225c22c1e37fb5f41412f95b86289d4df54f28f06c1092126236821dd2817584345309a90b663832621bcd10ca1d958c266b11b12c167c041ff00efdc7280b24b809618cdc2c85fa25e3d70453d2e8703a0156cfc9fcc6d16a42b0958367c902c9d2fa3574b02b1db7bc62fac81b0d4bae744105aa6a6d651e1b91e07fdc158c3cdb94bc770187d9e5c00f9afb16246d97c02d0b15c065492557794cb53fde80ce93f3019869bab06e9eb7a48f652a90527426b71c96089bb1da2b313a308dd07615a6d285a7365b8031155b3a47aee47d417fa26676d3a8c6131f5db3ff72319af179d9dcfea3dd0be35c552dccb35e3eba6785d357f7f063a4f084ffa62ef7f093805ab2974bf74723d4df3b7c570af3e59dda1ba2bdec118398682ce292c251636dc3cd0187dfe671bbad8bfc4dd7bbbdf4f1eb5bea951306296efc03e74b673905a200462f6a555874f720136a3b15bea913ed439b80cc3fcfec2c64a8f432943f395a17e1b52b49ff1573052f12b8eeba42d7a8efc08940cf46929110123caacea6731149ef05d5a1b88e7a67bef0839b8fd260c284292fd6c197639c8befa6741d5e99bd50efcca53e32527d191f8c9e21a1291c5d3c244c60dad9f064b64175214374ca6baee9dc92967bfbfb87d89fd4b7930ed3063ed31ff74b1fc4cb05a82b382aa7ce2464aa6ee0a1a1023986ec4f8ee24d963b9cf0f830296d1dd34c33123c123b81dcdc88f077406203f09101d5a88e2e38134c708d2c7032dd41bb2c2b38e5ab28745d49967bfffd4f5c4faa2b91096b073eea77f7a30fe4d545c4299daa53e5a4302b67e0b071809d86f04f46e135207a4f49657effc6436547165b0e3827b0fb817df29572b1787d446bc5d4e8840fcc943430d40d4d65c01f3acbaacdf13a517a2f49e5bfffce4f1d4f16dd1b0ece06858170139fea8ffba60feaf502c4941cb55325328a919c628d748ddc0437afaf27811dac5d15a970f76bc00f8574e9cb147ce4d0a61fc36e9074a45f831f0e0769aaef48be184f4269578cbe384244e4f556e3644ebaa6ce5435c0dd72e1c39c7ac5ab3d17d132df78fb65c913bdd563e2bac3f7f7ebbee18b7475917865466d2a92f41fcd10306a50b0ef90f989286c15e4ee0b49b9efbef7b7ebc865a5824102c9cfc18986e022c4465f1fb958cabeaa86431b5f673009a8e732d6b45393be692063d8d2d7bc3ff3f25755e345cd020bc4574faec0f2a22bc3d1cbe6e7e02682f185d19f35dc9aa85e5882f1992622857d7f2a96998416528793756ec9584c3b88ee38cf40dfc9eb2bf79c48ca78efcd236547a8b045189faff8ca6586e9de3461dbfebd0ffb0857c1a61e69bb3002a60932c84559dc60191a06bb8882e8ea1f0cd84ee06b44f538c8ca7bf3bd4f189de8b43c862b1a740ac222ea817fb33fef1b3e28560b125372d44e9548cd545cc4636320d0bc287f4e496e7d93afc8d3c0b272f18dac8cb7de784468a476038b63301f29fe1c406c402c72b034d4d10be4040f9c914544a6bbbbf1b4ff299d636466ea1cbda8c84a0919234aacc194a9c6d79c4065bc2199507c1a6d4d09c29aff0f121d0689004a0d75e845314b1028b682111637c1cf378b42e2452ad15aa2c04f4d6727add9fd18f043214d9a925e1490acca2fcbb04a9af82c69869704ea715ef48b222fb90f33d334e97da9e5202f14da85215d723c6dffba94bca07ee543456a982ce644a15bb8b8d1e649da899b627ef08561467b5f06aaa1a8d37473d1f369e299d5d378dc480f28e53b6c3b40d9d0befb6d19153c0e3801944421609177ad99cea7c2f88e9a4ec546f9f8ab8a73a85bb178a3aa8af6ce3b90665e78d1651c66c56a16c5e79438c1d5f4c0d03393bffd607266af2bb696d78701d6888b584e390789e1a1ff1edd6d6267ad85747b1302471ce97627b5d4eb49fd7cbc49830179192fdeffc394265516e536a7c9cc20dde2ce4586ca0858bad118b19f5c24dd400341b231cc2b6906630cc2c38a18a0c1d03a4339440c01d5c0a11bd6f10d8139a320071e96e3e344ebe82cd9d1af3d95d5dddc951b7d7515bff66d5a0838600d1d725f3d81ce1f4c2c272e6f7c4846703e53dcd26d9f982a4aa15443176b1c3bdb3a9769b7dbe3905cb28da1316b4c124908ee2892d99b5ee69fdbd43ca2784ac0e32dfb1e2fb720fc9518952a5c3e8242bd5ce4805e1b594c1782ccda1a4db3f42a308fba4a0d33aa9a0a17659d840266d388331880f12a0edbe3559267a5f3e78453a35df8a10986ddfafc9b56847ebc6b47b91e1d8f9c81eab80ac698c9bc6dd8fea13a9e0c5a36d68e13daeae3fd4b60b48a655dee21fd3485a0c0a122cc328dcbf2e5d636ed7a4383260b2f79f10fc6da42c56b30b870b9e0f16b8db8fb3424a7ba9cd8f252316418b45ea1ebeaa32af6604a511cbb803be9149eee1f72e88e082aa312d7d2ec4b06d0d2375113244aac2a6b24e11a3622740d1926a842e6aaaf995a5c721c0779f8f86625ca8f42ecbe7faf610af5984bbd564432f007b1cb2081884a38d6fd724c8a803db26aa84db94aff22bb41b2cc83bf5d2de5756926bbc94c425db0deaa7b276dd2bfffb693d07d1d9c651317914cb293b0c27d6d4a4c0a9e8723b92d49a8a1a469c14719c4b2d7551e524c2b886dd86591b6b91c43a6dc1e923aabd02c341094a54079a33549ac449a7aabd3278286bbed4621e11b8ff797cc283e6daad8b517ea272571ea72b8a03d71957689036748cff24e64d8626f1e3e5451233cc413c1125392b4e62825d4b559287d69c77bd610b7eed1d6b3497f88f745e25d73f3376af7ec36327b262fca0a603b5afc07f7207df6e515e34d52440af5f0d00ff961ef8de191a16e27dc91e99214e6641771470442aa41b9c65c46ed66cc1aca6099043e0626245587ad411ace2420c7c787ce46ad3acd8653f964b9b8b16dea4738daae6d30330a514bd8180dc0081f461fcd4e59461c8bef180c2823691c7a0d40a301e8360a8d86a1d90874da400d4275165fa3550b5428ad11e834064d03a1db08f41b424336f287ce86d54850c2d7d2634b04ba5f24867a908e3998d52b08bb78e5c383dfd3e474a93673a951843b739e446119090189769b16a92543511b2766a137fff72bfd028e36c05022b3b6900c3d96f29d2b16f432e1a814a439b8af2f7a9aea3b6191fd8185ce32052eb038c346b2c00e6400d2725a4463608d7365ce6c90b7ff4de888d39aa119a2956536d2704a0e6e452413a75a744c8004dc9e7810a833f1db9807b737210445d71d87d14339c692a45bb40f02a502b3fef7dd0c38fcdcc528eb6a3ea311b66549246674fa95b1e0e1d0ed8328731be6ea8f6cd65cc2e5a9cae1db65aada2956a32e35e62200c33f8312d34f8e36d4102265b2d28c091fd371e0a09a28df4701c486bd949f721e71ddd49269ee98b999448dc9f91f6154d69fb69ad728358e861c4f84f7f54299366dd246b3be16318e514bd53ec47405f7f9ec47a7ae99fcec37f80b8a036d61949cefb997b89e953dd3bd1ef7e15916bba8a8e306aa94ea84c301d0e7dc90db4ca4b7d6d5e5aab3fd91ea1a5c54b1c6478701453f41137b60ef09a5f814824cec297881c8d792c5ae076deb3cdf51fc91a6e945144669b83c317171b6d13cf74eb4a868a587725472910bd5b8dc07b2699f623a52fd68313ff3a1addd91d47e7d03230e07bdb5ef0650801093f15f79f17f54eb5788c9afb094fcaf9cfc9f5a99f55f7cfb8f93f1bf9bf4ef7118fa2f2ee14ffeb8ffebacfb6a53d64ee1bf4892d2d29eb61fecd9efdc5dfb2fb5fcbba085ff2f22f967d3fbf746f72f5d98fe8bff3c5c6a1c84fbdf7f056dffac9ebf4ee83f89b3c4ffcacbff0bc7c8ffe2127f9e9e7f6bf4ffc280c9ffe28bff332b461ee5b70477f67e06d532c62afdff950fff351e2bff8b0bfc3ba9fea135ff2f03642b4726867769ceffb555ff637801028f2149fe8b94ffc94b77ff756cffb319ff8f927e1d3873ffcb96988bf6e4bb2e751daa96055ff437e2485172b570b8390667923c11a470f0a43138b3aa0cb6780ced84fecba669a7d5b5abba67adfecb6bc201cf35807584d9733efd695f38db7f69efea23a5ffabf143a7a20041786f138cff2a77bf0d1338e13f34ffffe612795e15d532ba3179ff45cea1da83f6538ffd656e2fdc070fbd1f3efcd7447a345a11bc7ec1c18de8886360026c16b9e8ddac8d636a8b5ac2f5198eb64102a9f73fd1d612830d738afebcb5b8b139a1b2b5763fc4850a6d3ef297f39727a25003fe10021c2640ea88fc6f8454cabb38c6a1336542f2479ce890e054982b518ae8fb0f26ad64ba94bf1bc7b9062d889e8c48fdc1d6cb4cd1ace5189aeb1f8f611548d6dd315f07b3adacce0cede3cbc7f56f51686169c8c8345ced23933772bbabc6aa8722e82567ab8e075c0cb230c1676fdbc3241a7077392b756727a04d5164f7f622a00bc5a5fbbfa3b190c2a66bab8e011a2ec6c024023cf9b2d186d8efc9b4f561b66a40b9443b3137637962959a904244a7d2da1c3e38df8d071e6f4252e7276e8ad7d38d37998461cf494fb3ecbd9d13d5d2dbd8486b6f095d430760fb6937c7e5608abcc1b3f94fdbd93f83671bf8b14c5e88d6171fa6e2693013dbe64e3ace79a7db541f8222b1c770112adfbc0669355d5f685b42415352abbacfab5cf97454d7a2b7148a981b3e866385087b15751448a5bcc2cfb2429068627825a83d62954aae6c3bd53d8ce6033baff2d9283f01ca1f45f47cf2a7edeaea2632e3178ff67fe3f8a3bdb15a77fcf745d503e50d9d0e3f89aa3089086324fad7f15173b342b6e315488d4751896202bd57f41b6980a37b808d275d3de5486217b5831f1c63c14af840c41784b5112a45ca3f7d20305154541030aa6f618f23ce28fa168185762f2e41c4df0f14523351aac2ef3ebb3de6038f0077ea60b897c420ec1f98417aac5a55b48431a3ea078d82b1109d616637e5cfc522d477bb794a95f1082f958a60c48c4175f2a95adb4c215ac1fa1981a858f1cfc3d2c8b8666da2e3b6dd5671e662ec03c7418bc6c4212722c9d1a72b7638007ce5983ce5677055f051b6a5c1735c3117377886d5ce2ad20ef98da1bba3634d638278961ec2cc45cd8a1cccb0c0ddc3fc3a32c40d9a4f676a28690412df34af2465e3468e873d0229b7773c4e7040f80dd05bdb0b0e53ce94221ed226089df6b87db5fc2170d4d822375affed37a6436d9d8a6b9046d85670f148579040b58ed67d7e5ea39b3061031298237e28a041b36abaad5e241619e82ef8a74081f6aba88b2c4460289b0abd75a09bb6d2187ef81c04b5adcd9d1b91f7917422ff18835e1e363f338fdb16146644a138c386f0aece3b082241220a35e07f0b0668ddd689c7879943a4b6d5db8598ba312a5e30d257129a22f902ad5991ebe95563be466326157a91c2d53253f3fc1976431f88a06cf8715a528d2e027d3627e576d79ed97a3f1903dd69c60efa7c528453d9518d120df88cf306fe415b5dd5c05d204378a61a9a7729042826693c5c4021002968bdd3d3e0a890cc0982486064444bf305e77770da3f3f91c10e55d958769916bea0be5b78184601f530ff2e70f022100394ba7d11c9dee6066274fb98be4f896170aeda2befd84d60ffe9399cd264d217328c574a3561396c9b04dcc2ee2586a1382828b8524548f2dea58201733217695aba9d6c64fbf0a46796172a5406659c7d91a9229d963d6240a81e4b195cd45f09d5366af3eb4f402e67edeb5fc3a6881336ee9d2a6e2a5664526a5ac804bb662c3ae41fba8369ff5918a71ec2909ada78438fca6ef91b65fc5751da6ff449142e8e6bd3101fe6585ccbb93bac737532c1569c435a675396a4f264bc3c5bf186ebc6d511e47684455550ab8f344b19a2b016181cadd35f84d7507bd0e395d158681207ecc9f03529a1ba66d94405fef13966af3583167326dc8dbfa7de8c7dc6d1840a4a900a35691cf83cc5c9dbeb01331e5393f0cd37cf0553a7c75f81505024a1f466e038fb73006e792ef439a5e35c9d43556362b44ce3035bf9e942cb8fdc9818556bb4bf6410ceb9e91e1a2b1a28d815408c45d7d8b389a96d1cd2ff3a519725a267775f2946656c684c4ab24da62064a273f561d4654e54c4c4303bb9aea87aea3e2d7953cdc719fedde514d0e3635feef7a37151ef404831055139e995d84bde32e18dd1bb4ec69a800153aad0d25ee44273a9eecc28e015b2ae539c14076522f8145f14e21a2df408d8c417bae7bf5d94ee51e2ebe10421d8351ffcb0b35694ab2b5f564bbabfa7df47ef63aa100eb72b81e6704e2090328e5547e94079dacf38a65887350944d56661ba1282a916a6225d1d555fcac2713597121dc2e8451c97c23aa49934125608267bb78fd339bc6b436e259c23d7753f511cc4d1805360bbe12bdada2b08cafb878025cc5c72365b15673ad326a0305e675557523d09812af4b54b352dc39cff278762cc33c2c200309cf3f73421274100259a1c6fbfca5707bf27ac4f1acdad89e7780c1be40703bd80224afba6c03a57fd2ad61d6a1fec9ccbfc57622fe10f189831c94c8bdcacd62b27cab7b3925ef5738a85b6b7e340b367cf9c62132b0989837b13aee8d8fa0f548eb3adc519921eca814e1bbca41952b5bde79c7b518db9c4fde2a69b64b92fe45185f34620eef22885b4a143fee4ece91da5cac444bd517d5833ba1115283b3680267ddbd659e89e02817bd5750e28cea29a4a65f32c66b01b9ac46054b3ed41c9dbd007b149269d58be88581349485832bb2b0b111ee302d110d3b1fd528070f52d822a76abf10375e19f1f2e1f59d2ff2f31c850bafa32f27242d70bea093ab7745110f397b754e267c7d2fe5f7288c5f517d46316b759d11cd902394faac175da31651ec85f96e7799134fc81c3142cdc0214e51b4a47529cccd2d4e2203da294f8d6bfca8ea497a25aa4e027f4335597109406304c2480e43a609f00ad8ec681ed3d736028402db8123b30cd79376cb79506b1852059380d48db2decc6086bb4d6345cc8d10bee2d78af20bd05f512482f41dd4b211b2407d46a7305d5094ea2bbe921e2454c0f385f2726f9e31334ac2a9a09b285bda8e4a77fd1b642417baef274b342a7686fa42718967ae3731d9d52b44b769e700c23ad9797590d43174a83bba25a29a56373ac3cc6e017f975b7897e17868b70ab8778171446bf2ad0c7502c3358f9bb3612b9074e0524f4b4a81d241415b69f7c417a9cba34f01c4896a65e2538cb4916ffcc63a9a4ab9f180efd662571999cdfba1e2fbb3a784424cf7511f9b61607bc274a88d7b02158f7d1bf058d18f251eba03fb2c7cc1934dbc61a0cdf5cb2236dbeb4f3a88c285e1d937e3e10f086db02b7e4fec9a1cea1d15df3845efb80ff64080632e31a29a27bc8d78370d82327f58b8aa04a8a9355a5c73e5544845b45e98a0bae910bc1fd25403b7418ebb0e2510e5318f7e3a8e72515f6838e93a468d3866c512c347fd765ed519a3ef123249540202096706d9225f61d43e164757d4de18ba39209bf47b1127302b72234f226a6cfab4e6d06a3ff3e2d9d4e17cc15508a836094ca90ffa3582cd2257d0358041408a1f0aeae7f520064449aea8e4e1c235ad430f98eaaea53089d878ee9b844452b7faa6d169856e911bb87028a21426a4ac7f4bf37f40d5dc62c287b0b091b77077762af0ac3810f11d9a5b290e8163ac824afef3c6ce5b13e5bd35620c28cb0ada8e9fb8d01f8e397e9a510ba53c3767c0b9bd3fd61840947d11565befebe8ee8846dfb44c5d6178ccbae4f5d2063e81958dcd42f0dfa88c985bcece7ffd9ae1b99a6fb5395a7227ed323e7af75de773c0a76848aba7523657b84d605055c43d2a8c3bcfde7d7efe8618c00e51544aa2f023f8ca90544637d2d880a699340116210f4a89b168ea387eb6405b9def464e215230c927a108b0f63ee6cf872c609b2ffa6a4230fb468fc2494817afcf27d44ebe84c4aaffa63dcba2024b86c7ef5f1435330a01a0eaf7b0c4af2f08d0068d8a157dc868dc3b1558712d16fd27d547216cb21be1a95c638252de0194b08abc1cec9e30e958489e4543d0489e599358749d936b5cd9421d5b2c54ffa7203f7fb168265be6cfe73ff03221994b9683cb4ab2615463933266ac9af614e9c320c9b464b2e7e5ecd114014c655c455dbf28f9722988d5aa12cdf2f62262667129dac08696a9f06f12d622e0f173fd2454d4ff384df033b2db8afa1b6b039bf1ce91aa835866e259a22f51546c9feba8f72d489c112e84d85f37609dccd187c279c76bcff5ec25bfb2e1b59677702503c76262c127a2d6d9699cf8c02e294ee0286556e77b42a9eef585368bf7432fabb7205b5b2bcee6ddc9adea628d3fa21ba1a3dd3b4d3223b9ff39f30cb5288c80d540fa5e110fccf08aea1041f6531e5e13a816a26c9a1bf2ba18a233fdf4a4571742d6c226fcf36a34b33da7ffa44fd4323436f65c8bcab8aa4d5e690a369b46ac791dcf1ed0b67aea91ac7666f07e164dc812a17f043894f0d45f0d9897180a53bd57772e1f3a3f9b834a60dded1051a29595e3a6b9ceb2c77dfd2c86e816ba2b4e88fa1c90a597297aec79df4e8d1fb8fe93d41b40945b0e37b33618a0714889525789298bee8b3705a67e3b065a7af8abe61c273d98bfb51f19dea9284056c1c9c534263826f7478b1461d50f13d57077d9cc4c361097cbe10e649e319a25b18d7bcde92e4290a483d074b6d985f106c2f953b1ff7d3be445587850a64ce898cebef46fe70d5e78fc96db29545ca02d93c2378446ed31811a0c29a05f8ba908f81a957427f4f2ebb7348fe155155d2e1985be0144a7c8830cc0101218079184ea7bd2489a8d687c05b45952a711c81b624f500d4a697088484e40ad28ee783b3b9b21230807c99a20ff4b8f89d8220bb25428d81d89410d4096332f072555675d9aec3ef6afc119cfc41aa312bd19c827ed44a4d26721155b8d1d97ea5eb087a2138c5201152f54ef099e4f1f562c10059c5e895e084c42941764bd58243cf708f296422687fa1eee479e02943cb7b5ab4e9e2ec47371e95da452f0244341988a3d8ffcd364d6dba8cf139818e658cad0d69e7a67326f89f197ba6801705b26e3230c3ab7f9f59bba1ac7bd56e8e6ee47ae5e48292e06d0e8a17261687a08ea99c5def24e0fe19edf7494ff01125d41b7c95df2e8305d217137845d71f3508b1ef55ef5131140cfa0a12bb9d27b1a18581fc6823b33d5d811023bd34176f6882ba28a090b1e9d52d680a35d188742ff61f5617e83eed574518a7410607dd0c35676e1f777dce5fb85c9056f5345e2718cec63fee79b1dad5a213eba8be1e2812bc74ec3ad008192587f66d636a9be1a2a7657f6eabded6fd62d7587929253c9c40b718d9f6210395b35d4aadaee2161e60a52fa9a43632e5dcd8dbbd2027532419d397145e5126ccb07f79488495b7d863338760a723fd2511ec96e803be24b74334b60ceebfbe8a7880c41602e2a2d09bc87120210a5677140f240cfa6ba085c8f55cd51cda1caf79d560c4071585107caae29939bf165e1e6caa480dba8a0bdcd84501ec9102de7f4707b78a0a04a2040ad89b6ef0433706d85408e440297b2dc33c1b463489757dc84440e92e6fd71e4e3eeff92eb0363b038a57b58cf7bfd6fb80cdd5fee1ab128ff5815295ec632b20aa849ca859123f294a604da45825f7d35e8d458768e02e07ced61cdaa14f8923c43f4b203d3b53d31f3c41c20a7954630122b0f1b7db020f7d69c32b541925d90a986b2db539ead6f48eb1ab76143a23efd1ca5e202c106a67eef7cdeb374de4ab7514e3fcbf80bed755eed0ed470404140d2bf4ef22a7a8d796ad73f90f85f8669001e473aa51a6e2fc2b9b83c9efcaeb6e80402377b2f3f0341da3836c6b81643e60634f9839c90471ee4770b839a3a97906b1098e07c466547febac1227750a0e6108042702baa2efd35a6f45a3b8d5f31f37b206be43da1bfb58f68cb78ef148a2abd7a94d0fb784cccc30e4320fafd68af20259e89ac5d63121a09586aeca020563afa6920fe980afe81dbe7cdce64efd5a26da613783d21c64de3a5c5a85c33c320b4b371996e90f2167edcbbf91aafbae8123c3c206899210e7f9895ff0f01212274fd3ffeb88547609391646685352fb9938aa31073da2ab9e8179e9f9015d30004078ead143d5acf680238ef1aff4aada5576464d0a2a6103d5f0cefadfc5737832561c1273c25c37335915a3229a7eb22a706d2ab96016a4b00c387de2bab98f2647016e27e9851e6ecaa5d7668e69c01f701b4124e301faab7300906a7bc8ab41cd35f65ebb2af014d99ee9577657e5deae4d241b56c224beca5304e45cfd49f7cda075b4969d59bfabfb74728bc1b66d686512acec72f6df102bde91c43e823f6917928dfe51591d54fad949ad74b517c957d51b950b8a8c2d81d666381501ed04034ca54b27f5aed46207c4a37bf33abce4a6ef4be59583ea5fde507b3122e00a548541b59c06195abbe13313d42ab8534c8abd54edaae2a22bdf105fc6c011a9541e3bf070d0c434a7795c7ddf5cd4a59ac2836cdc022c9ae38ff4137ede955aaa6fcf0de1540a111365a945062e02782abd83522aa397fab8e13f37bb7e40bd02fda7d8efa8e0a918c0bdd00db1dc2a2a670f194d59118aac0f0d389cfc56384767d843344d51e07c098eaed011578a5ad3d8d3e3f63d729c0fb3334a805e924c51406f46c3db17c20f97ee1f5c7c950c3b9da87677c83213889c4dd4223f3ea8e55626d06093824116d93a77f06995bc05c031c0e861708d43c381339c4a4292e12201c75e8f07c30a8e714cef8aa4f3afb14968157ff6d58ca8a6da5afe694f927885018b28c458cb01464cfc3388f665be63562e775cc7180a5125f16db9f110dd19fd1bfa213e333d12c091dd70b541b330907b72b4d4470d36b7de9b4c2f3cd48a34cf51249515635d539530e94758827bd069c2a017444b8d05063e9ff107577e7656f82e2065a3dcfe3340c1b612f239d2a39550849e90ef8f9fe6431331d65929fb1a7c88b4ed660f92ca00d309035fcad9167f895a9bfec940453dc43cc02f7fa0ea05beba7a2aef6fcebb9e3aa1f682202a10e5f4c7f7ae485040b69c7481e06a378cc34b70a031520ef6ae4043a70b1a78b11b03a99e1566c87c170e9dac2c6ba4e116c3bb2f5593fad87f5097a0365a53b20aa650da7c222a5379280e740faa6630e17d9c534d10a7a136f6ba2a674ac43ef11bf14e0f31e94b65600d6649371107e969d7ae9e3bd8c4d36fb49a1b0fc480fc0a1ef5a8115e1512aead377ff7214a0e39f324c613d05784881186ce1f2c18b95ba01cd847133595fd9230187ae79a163e722d31fe1ea51bee6929078b36582ba94e26b2e81dacf5d7f81274c62f6c47aa2194588cfa632255a7e19af219fb43bdd64661f57698d451967c12fb474e47b03f1254507bc8955a57ae18e32b1021c8ed824edf047ae5c9dc70d6e2db92240221f82069857b93d053f7620e3897441797a78e5e0713ad791e6ccdb468d50b01c3ca8625089a650bccd85c961687011822b22886fe02d6a8002e43b618daa4fd81692acb5027c673f637d2c6fab279864c823b4669e99a2e9239cf52c2e8de61214cf9cad6eddd65458a0a367c00af2abea716e82e34081dfbcf4b84aaa9804d2b5fbf42cca2e7268bc4f50ee9d2c8077c1bc84d966fe726075d70aa74c1fca613b5f04bfc5ed0fe0907febafd244542e12090e981f5beb3fb7f6e20e08a62a96c12d69c7c409e50ed8292c69d7959ae5094c3a79ea6a50a296ac3927585ee3a002b60bce09296acfd563c9d231193090e070adb6af62fca4de46a2bc5d37f7c93680fc4ec184a3593ebcc616a5afe1a4a81f643d77742ca44b44ad3433ec2c7dd82c014c19ba193c5415286ede77cb557d873bd765b71f6f06eb9e5a6d71714c137ed1c995065236a508ca25b12f99c894fa63462ff4cd9c31b5459a5de069fe9e64866296336b9446db6912aa23b4cc3957de74aa6139956d3dbc0fbfc4fc4e2581d3af5f653bd6222ec3095abf3ba38e9cc83a638b1148b20bd2d140cc729fa7aa544c478cdfb49c97c978ce845b64b6b88fa13f3d6a261a88cd10034a197c735b6d2a658181a391b8b804aec2d9e938552e70fe0ca28a583cc35776d99e07e8b221d9e85cd85fa5b996be241771e4d2cc66e7ce0efb3b8b89c462e65ca376c169b466a23f18503c6b7f55c550745b693da3d11767f67f984c93d5b58d90d87960485cf7e32d9e90697873b7cbbd0369cbcb40d515aa95a7ccd61575947c27599f1863649215be2641f30f367dda0ff14caaeff7b45033ab2d92f1b9be5f62325d5479b7c2313b38c67ca9c6af2c03b91fad138077bf7e0fe7d052311d6a37586b4f830d01d7648e20a6ec20abdbb59a66f4efec40ef047b23bd4e5be9d42b959eb7e4c7307f8a504298a4726953a49e1a3c82b1ea02a0ed407ae63d9583d0c625fe5ded12d11e623bb102fb5d0d62e2d4241a2ce30b44d4a74c155f040437796446318e4eb359fd2eb959b64559a947ae35accda2f3515e9885d83150404b146f51952ad644099592daf847e8a4aae2af8e4cb5b13b33b1817ec8a9ede5870f18f0584f23a6036c456a3cdf41622b56e6131c37db8552421167d1c49bf6b1484e08e54cdaa62dd960f1cbe84fdd2049afbbbcebf4d3f2070bf610eaeeac96f82798979aa8b85d5bc80e63282020299462ebd3da6091f08931876902c5749197e509157da47950130b28bf4228e1f4950f8c09b6befcee281b87bdf0a2f2c513eac8d39a681b81357125208fa10c04c8c90165f1fc657aa0c9245dad2032f1cd528a810137d9e9e8be90438bf86dea29482d6ad9f30af2ee122a33cd017f9e80ca918c03caf202f567569bc66a5d90461a0e8ceb13a8f0a6775d9e97115ba2b80104ca74dbfa06122c08606efa8ad56bf86b59d944f1401f8f2dc5cb89dfa3fcf628f464377bc3ab0d60f7e579f3ab6c30f14ecf7042f9802e62246be8a3bf83dfc9d635081408c8455b668cc03f8e83ac4746faff3aff9bf1ab59fbf2694c8475db4e75a983d0afb4b5dd255417858186d1d1033c06bc41fad1188920109cae256212a693368c038e52a7b881473719ddc030dac6f8f891a2d00f1440da4b84512ca0c28706197c11a3e065490376826c0a2a66d19f33a06a5928dbcda9f4d74c488a6f44daa43f536cc65a11f049b8c4292f2b1091ff12b84312037fb2987959ca47f9a5444bbb1fca29ebf3813e7004af2d59dd01f0c45bc8027c3da320c402943465acdda630936df4e62ab018030832d00f3b6f7df8ec0a249ac4b272e4a005560028d2e245c7f5c60957ffb88b5c5b78250e0e0f48a2051b8d106e7ed7534e72f91d9b7ee880a2f008acbdaad82a11770956ea8a6f7ae85148c8a77466ea0e19b1f7bd011e451133334cd9a24d42cad6468acccbbd4baa1dd7c51ce8d6ee9b4eaa438e4e1fbd084c304c55ee3478f579cf5d9148c06871b9c6acbb60f76e54ceff11e07f93233e6926093c322ddf88046a3f5a6526ede3a70401fc66d3e4ecff1c09a2a904dd70f345990584dbc6d6958c2bb7a06321e63879c4312e9fd41c903e7e6d885d5c686ef25f970762542b745972fbe09e10cf612a9e6b56d171557381d2725f800c4fe76f5560f9ff9a8bd3af3d934e65da33d2f294df80fc71f9c8bd7d537b2363ff73236e279788c872af1ee8ce2d2819573720b995ca3873e1f8249b22e88fa179a1561aefa9057f693a4fb4f22353778f5387dfcd9d5321ff2a58344dd06abec0c25ebcbe23083c75de0338554c756db432f7fedadfc7d133b96388169e23a7d1d47d7cd7b49bbc13d2b3f65fa6ac2368fb95cdc2394f934dc10708b8e3435e5438f0d618f882ee9c2aa8ffc7ebd1ce7472c93ccc007b955f0e3c0a8837021c66ac97091bb1062f8ccc574b6b78d9a939e8e8c3bc6b2288c98e131863dbff724bbca94d46378324165d87eb5d3a0862d37d5c4b35dc32c7750f922a503df8010e08d53d186d5da7ee6092b0373a06d95b2d31c297b037d0a3999ab3ac8e5d2e6181bb09e446c8abd219f7bda45f140ebe4d69706d275dc7cf83ac042b35f9218b21ea5d9557b2ae8b4b88de69b03924452fd6008cc74036a818e805c30d61da7036e2b2a8e388b3729257a04d5568413120d836f7fe6e6122a48445a24a49d2751048aaafd841265d853e57c3d7a6538c3795a10e2e521f2821550590557405ea2758c95ffb517eaa48efec57f696defb626a59452269902af072508c2073b76ecc861661089535ae50a35bd424e0e13a794524b2337abf4975eecfc69942e193fbe949d247bec72a4fbf4f86e939b33012c8346684f0f570f263d7a7a7ca087ab874b688248e86c325db387e9337dc03825d76a679445a3238892084f9df464adb7d64b2bf596c998a3567faac54d1a80013187779ed13b1afcf35aeb37e7317ecf2836a5bf6fb5ebbd0cd0b3d0184720a1fedd6def2579f66507ee506ba58ff4d94e70bddb6fb507ef97bcf8f4da7bbdab79162b5d5436c13f70a29d9e76fd767baa3df5404ae94669f4ae7f718aed52778944fbadd731c77dda4559b69d27a5d3dafab69b5d90cef3a9243995525f765546dda9532adb3f69b3ad7549a694d6ae3ea5524af94e65ad2f65935aa98c5426c589094e626cffeee4c7ae35dfe8cb26fef511bb24cf7eb943ccd12e6572879823beb70726914dba1c79c4e6ce9d9c54c88410839021342e933b3071c7416684a669cdc13429adb576cbf12dd6812c493891040f7890af0726c9f7a5732fd84cfa982547e638a9039700b9be496b800845ccd1c9b71dcc3f1aa18839e8d74f12b3f8473d66d1e4463bffc855ef88cd4e3fce1d52ac3eb13b83e28280e383465059ceccce334bc62f0c3e5705065fda6002617b463b3f850406bffcdda8b27aadbdf8e184ca17a6b779d373a04dfbcecd3f5531bb140f3889bc117224753880632b897f36588063f8e42d79ed2b39ebece9243442e5e3bd8081c1df9c0158c4c4bc8c8c8c0b172e5cfc4e8681992e60525d8b984f753b19b7887916319f2365bae91fcca35ec838150a8ac5e9d7efc60199912981e6d2bb3be7a4b4567b372bbdbde74f6e6de31a735776b777d4677a634eca52f8846cefbc6b550e70f4403ac53b1407a45fa65f86058bc8a25128ef58c4af29e55053459c56867ea0f0b46458a804f934d9328b5989f2d3c39345165271e273b4a5e686a68536891fdbbb312b517e664f165948453a39da7225068be24cbcc7288b50189d7039ba5243058eded750811bc86c1c5facbbf11078ca29a734d5c4dad22d3511a1d56f9a2e667fada60a5c6bad35cf5a35ede705533d3df002b94e981a2a708cd7b11cc28c3d3a8c0726c93ab9be0bf2e6d2c41c2f9ee4b4adc334d14701a610b824632f028dc3887ff2bfd293b8e979fe18e0f9ed369a57ea2810c16b84460503cef671b65e10ea79aeea039aba12e89bcd8baeed9aee12f2f45aaebc1b3a3791670f60e8d57cf92ec81b09f46afebc21cf77429e17c613302ac7da38e6771ac0f8aa10624aa3ec90d01c7fe84e412635893e6cb83d348e0da9cc43e38ca0a32bfa28fdfcb843e3683f83348ee64d20eda3e4c5ce517fb290a791c689795edbb7bf6abd5bbfd2ce73d3cceb9589a6e1f86207387ae094b9e6e8ddc6b1a395c19a79aa96ce7ae7f6cafd1685ba77bb3f3333ddafb515d8b7d7624b9726b048eebf292b741ce1f81bd7820a598b59729f380e88fbebb0026baa2a387a272ec84c6efff0cb09e6babf50b38cdb8b26b04c6eff3e2d0626e605c79cae8d898989a9d48529fe8c8ca8d925d19fbf7dccf3e60976a46dc328edb9f91af7820e186a481c904a7a4ddb6a1773c7bc6dcec15093e7bff88182982be95ffc408125cdb75907583be6b8b3bdeb4d6fcb52fb5e2caf5f7f7184630f6884264740deb878fa35c8246ef3d2e2e947fa2dba1fb8cb41c1c890c5c37c4c4c4cea77728b0efcdce27107d290f1a33a161d184932df1c867916dd8e8c276d1073c03caa03bd28c37c8e8ce9a67fd86bffb0e7a26b16fe93093f877ffbf69be92b0ce4c5bb3732f6eaa933755c07a6b2a6f1c0ddb67928a7af8931eed74edbb6699af63bd93bf0411ac0984daf75dc37b7932577fa1cb9798db1941f3d0a1c49928b20f968f2c023366b9e8af49e8af4f1643df04fa50c6a0fc45f3b82c43f66fcb23b3dd6809c4c5c90ed8fd86cbaa523d60699e5fd2336df0d48e9a1e0fb24396f0d7d41cc03b1ac426e1d84c0276bcd0531bd7fed4c7f3bdf4aa4fcf2a08bc62f1d07e4e5b5582a953e48e7d2efe4dbd50ea421d79fb2beec7672f529620efc39f2a59bfe693d4fdc774a06eeaf7f9b0d476ce3b826ef88cda5a7a00d32973c30e6bb1d895d3af707922297e3d70ebcf314670b342779d107fd6e5eb98a36efdfbc9dedd577e5cda9578d23feaa6d647f31462ffaf0cf6d3c4ff0cb327a2d43fde843f5c283d4eb98a3630efbd68b39e8fb178bd4808066db50a899997f1a1a2a47f9660ffc6c63c64f422277066bc01b2f3fd3320625f37d945f3c8bd7e2869fe3384e2b956660c4e88f0183e370b76c926f60bccf7c2906251323060c6f7bad3b163f13bf4906597c8b162d542a954af53bb95fbce86791ea77f1a273998b0e09cd294fee1073b0f81cd9a2ebfc6e13f32ef311bc37dbcb74b7677ff6b31e3893edc378207ef1f0a9036fc440541743bdf07bc4df8b712ae536502474c6dde904668ccc44a9b971e3fb6a6aa847287eecf8f1a37ac0ce0d35437323c71d3f426fa116f21474687ab0e647761b2a926a77a87b0e3768beefc68dea110a29a79c31bfc332c53cca24a3837a9922a637bd4906f92761fe499774b5a40c8b169145d7bd92e9686ccbc7a3782516b98dc9c358bce25f51c4d2954a1ca7c9d3e9f4413a9fbcd39b4c27ee05c604f39223711779fcabd9c1e3478ea1f430cb9732d6e24ba89794c4c2a31e8f1a49438ed46a6a626c4b9635f827e57b1f5fba94ee21f692143c2e4efdb763874d38c230fe1c897175694da37591e5f3629c4a3dd7040cf610adf6efb776a5e6edb0def43b2cd2f55407d0def443b437793a524aafe395bc21da9bdee4e968de0e8bb4752d3decabf81bea5e52adb5deca19a0ef95ae4caf7b40f43c4b03a29d825bfe898a41c50dd5c5a126c460ac980e45042bc7743adb1058318f92f9984e67039415f34b66102cd4cb743a1294302fbf93f1efe4d3ef64d3ef64ee77f2b643fa9d7c7f27dbdfc9f57732fd9d3c7f272fc13e20c142fd64027717d56dce427c98970e77584aa41b99f8d04e7ad7a78c4c8d297ce043357065a7edf402138392993232d2a38c0c95b5a059a896ec9fa2462e5ed0996aaa54d2a38acaaa4f6d529d549feaaa50eaabbaea5015aa56ea500deb951aab613daa5b2a977a546796c7b6eaccfe5828368afdb12f3bc5c2eccb16d92b168b2db2319bc5cafc731b83b1b99c34bebca16d48fdc564ab71aef84d0acb67712dbe257aaf5158100d8768ac0816140ecd8e5ad575a73895c96454ae10b692bae258b83c3d9ca293561b8661185e97cbe57291a88ccaa84c86b6719cc9743ae197f9f2223dbea0326dd11eeaf2a12ffa738bb02c65f4b3efa8862e0a73b95c2e5750384463b468e34ca7300cc310bb5c2e97eb616250281919162c5aa4662a253da662666c16c98c6647afd7ebf57ad559485b61188621eda12eea435ff42706f57abd5e2f19166118d6b0865dcac58b172ad5cc0c8c1833460ce931064c9e3f1316f4cda1d9d16cbd5eafd7eb5567335667b35b64343b72f1e2f57abd5eaa3aabb33aab330f460c1932febbce4b61a69082f498c24beb68f64c1f1b7bcdd7f7fda75218cf9f099b41339c433164bc5eafd7abf35248818666c60c1a35668d1ad2638d2d2c6a49594b1ad9d8ecfbfe53298c3da77ade755b9e8d5d5ba98d4d7f1a9adb73792e93db735bb7c975dd5396aedb92a18b933159d42d797361d76bddd66d65ff1917e6a69c236fa44baefcafcb4543baa4d4b8249374350e7f18d2255dd2e56a9cbe5b04a3a0028f3b5ad380b2255d470a70942d1e8df417acc92d8bae70e6afd8a4d55e7bad2b86337f4d5a6db5d515c319add41dac015b36e9a4d135dfdddd678d16e0f8eeee5e53a305eeee8ed3d7895643081cfbfb6b0881e3f7c7d86005b2c7e6abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb1b0808080808080808080808080808080808080808a81b08080808a81b080808a81b0808a81b0808681a4143e50675220ab42827ad765a2b3d5a49abbda44922498fa4bad54b2a69dbdc36e9712b6da58d339df0c4587ac426fc0213839a2894f4880a8784ac0c855762e1ec680b97a319e5a1ad594c4b868749f667d1d3a249f64fb95c4c172ea44717ada02954826042214c1ac92c528b349232b945cea4cce5d3c4898f0bca6b8642feb99c4372ce7925fbd319abb356e9b1ce5007140cce998c0946a9542a915af9e53f0ef3a05918866118c26030180cd68ab55aad56cb8b5ce6463ef3230b83c1603058abd56ab55a3164bcbcbcbccc40cdee91b7bcc7150ec58a6461188661e872b95c2e9794c1fc0583c16030ff719807797865188661e872b95c2e180c0683c1de633aa0e01931611c8ab158248b46611886a1cbe572b95c523693c96447def21e77b98f75b95c2e974bcaa44cca3ad50c0c183162c890f1ddec3ae9b183715d1ffc4afd3c2c7ff11acd8ec2300cc3d015ba5c2e972b0ec56291cc280cc3300c5d2e97cb25a74b868cef3acf4b21059a1973c60ce971c60b0d4773369b61747ddf7ff489aff813613128fc19a6e0a540433363060d1a35bef97dd2e3876b2e0f1ca686e6ecfbfe53a9d935c2b187be5e7366677346e76cce999c331a9a2dd257ec68704162ec6afcd4d8624781f97141e216bb15ccc6b461437ab4714daa1748397e94a75876faaa91ee5d446337f531a7313ac3949bab1145a3c179cc6d646aeb014af0a16a41e61d8da37b60ad221428734a2822c0f35f5082c8f2391f0c7e710681217cb032c5103e589182653d9510fb3bac18ca8f3df2668725df7649b464f93a6e83c3f89237cd92f1eb11c4a04c0971e0447e0d79204f221c08c91752d278e5233dc91207b90ba28888bc49d27d5424a3f7611d2fec38f69b9b61b2ac872e9031c39e7b86af8c198eb967589a726b521660464dd268e1183778c9a494f23f1aae2dca311ac98894ba21fef9023a1ce0a741663d6a4411d125bf3999b8db95339c514160f7686e1d1c81c4911766964bf2f4542dd42c5f7aee9eb8dee5451891a5076252111aed91ee5e8f0ff460d2a307d638317af400692de58e0cce9933a79c72ca504a49a994524aa9e55ca11dcd6a52980ed124a8100d6912356ed334a44374688bbc194412ffebacffef36ee411934e2412fb4641089ab5e2a12a87c5e18a948d02f8c542fd58b1423d5590691481bb34b582b76c82e811bc7ed908dd9d84bbb118a288bb0e936ed020de6c3870f1f3e7cf838d19ec6a93e7c48192bcfbd3be69d3b56a8e9157ac7e5766ea44ed01afc3cfa31beb5525a79a5955e670f3107fd29e76c927dbaa8947252f78eba28952ea564f5d722687d53d2b6e7dce7bc5e2913c9bd50a97b4fe79cf5b5faded5779f3ee78cfe5d8d10054b962c59484e72bf101ef266b2bafffafb8d006eb0820914284c00718065e3891ee8d0822b74d8d2040bfc620b68f02a6242103f5889a2ad74808916132b41a072f403fd8112563c1982478a12b0c0043989285c25b062022a5010c49322f4f840020e04c10a0978809f900f3e8e48c112a01ce123840e44705283032cb04203a10c8040e43345c7119c2c9101275e270865144e98810c7c3074031a4811d42be594d5da2357f810851c6c1142122fd8f1e13284132f2c4988c209a411dcd40b92288a519260424b12b194d08e7fe7bc59228ddd9d78d2e833d4082ac767424ea63f677f911a6ab2f48cd4ac90bb037574c7296b9612531632d82f9b7aa0e7f9b3cada42ecaf903d6b75eb4eca287b641eb2b3d958c8b303e54f9f43e07bafa7b38465bdb9a56d269594c6396fae94524aec84daf5fddb994bdc5d035af7cba65fefa473ce26b4f6fee836fba337493c8106e309f885eae409ee584904be9fb4f2a260f0c793474d07c2277e83eb81bbc30ef6a6a06031e064891d58b00408a68022959ba84a145598689aed088c55e84069877fb186087c63e7c859ea8acc2fe215914fff37afc87d6d76f26bd7c160bf345193e45d9a29bdee46879acb2ab4a6895afca36f4bb86fdfccb7d696ba684489b8d13831eb481962fa8d7bfcf23af85baeaca763fa93d7de8c7f18c3bc7c25a69f0f83779f3c25f8e5c3fc09e6f4e2a9ece96778f9d3e99570ff72fa194e9b9017afe65bae72e40fe1098008e1c19e8ea927b24c3f430696983c2535fcc9a341ae4e2d3bb55a3dfb017b2afb4ab8c79ee95bce60fad39f4c26d39bbc254ae2ebf4d83b799fb3101fbfe9c1e8327911265794fe8f1fe6cbe7c161bee971908fe79b7ec87c9337443ef654766b16d7a9ac2784b334e22ece70c702ec3e2eff287d1588c0f4a3ccf454c690eb36dbf993be88d5ba5247ba32f85b2fbeb4a0499f762da594b2a5941bc6dbdde89540c4e036f2d5382d90deb5d760e03e6eb708f751dee8481982875a1683df4456dfc428932b8781e9fcd5f90bc9dac723f99d5e7e1b89473e78fe9678e49f3b4c17a65218c4de7c79b9eb425d914905762b7ea549108ff97785f0e961de99bc1c16e7cc6772e53379237be4cabfa76726a5d05048c541f9922ff92361728aecb9f247be64cfcb474a28fe79284f1ecceb9c1ec3783a30ffe2a9eecbbfe48f7f530811b93a75a77ff919601e7b4af0c3fc8ba744ca10fc30af0463fcd85b12f3f85f4e9e8e942127ec29a979f99387598f846943081cffc5f31e67c171cc9f1ee6c1cfa9f9c2a0984eca7dad3352f3fe4916ccf4be1a2570fc96fe9237fde33c28eeddfa38f1d68c473390c0adc609d2b9ea4002831e128129058274762d8dc31f06d2d7f7d7ac944a0fce5ccaf15267ea8a90bebeb66d9d0b5181a3151948de9598514638201110c2307f7ace033d742c514635fb0a39c944bafceb99cd2cc7d90e6b9ab5cfbde6e9481902f3a7c73ffbe6f42f0ff3d65aadf370080aa65fed16b916863819b88f5e49db36c9c9509f1691ddf61389e7cb79409c8080f9ad030283b2c78d3c8b96c669bfc19e7cc178add64bde38c6d77f8a8e23b007c15e2fb7013dcca10fc99b66b5a29013ec431efae72a74006bb247ba667ed4755b8751a9196d8b1c0cd75309f150aedc7650ac0d28987a0de34aa404e9ec8f831229d4038180f91962126050a505932baf41aefc5b4b605e493c7dccbf3cd8b398172f86300ce34999de7dfc606c99007b118c2e2c5732a3582c929ccbd8800cca4f8e580a1c8b704fe3e0e4fab52b88a7942f49cc2ec262907fa61a601999f4a5219ad0410f4de0605405ab7ea94b2257f5e7cfa03dc9f366282991dfdc019ac5739ff4395e2a59efca15a94bc955353d81a3e7de2757f5733c660798be7ffd30575b6d558107833c72027a557f08611172ad4bb0d6aaeac721b7b9e2362d8b5a222cd76a25d75aeb51ae6f6757c9c14aae1fa7542251a8d301c7a118fa57ebdbe81262d99d428b41a3b902d3afafd54e355f9b3a52a637e4bee6e9707f3d25a4afaf73df7a4a94949efe0e4b6b4e7ab6e33c25dc5b255286706f5f89e9ef9b9e6093a78aaffd7dce531d408bde10edef6fdbdb597adb913c062993fe7d92a792de10fa5735a4da2984e75a1792654d0f1981e36ad5dffffe1f6b3ea123d0eafcfa028d9c01689619dce1a4e77987c6b883ac663035ebb5f7dad21030cd4d54e5cabd81a80a0f3c26640891051055c18126233751959fbce526aae2443699d0541d3dcf317b6e1aad71447f8263ee6f9cce436453b81083902129274541326b89305fd6dc8d01336adef52918bd79b3f540cff64b9a07c6af3f3fc62e5dba5cd142bc5e115b372945867f65510ca12e0809721b1bb8ec018329fba939b59fee46bedcbd111eb97e24fd9cb18fc710377fdb1cfc52c9052d027992ae68ee051e197421cf23d1ba67e4c3800f56a67822cf9f008f3c6541d327df998cc802687040cdec01e36094c3ff1341fae44b939a61023b8bc6297d300730f87df76e180a063f17f29f8e92b20929a59452ca4749773f7591f42dce95f458a703cf6a5f8146b2ffb4a42bae67c3ccd5562f94402a1c6457c221ec681c199f4e15f0e89e5703f843df41b8f51c020637d29764c906ffade70a3254010671e9b1db809f7be08c9df651924aefb1449a2d1a65972e5dba746e61beb45b8dd432730091053481c1ce42f3dd70c0dc4e5d30984a19ddbbe10c603095ba17a73cba47f72863f021ea6e4f6024d22d03a28fce310281efdb4f813fb592067ad61e9c597bb0861cbf3e4d12c6f995005dba70c9f723b893afccd7b38166e9d9e0397a45e64b3a8920422905d2d1769d4d9b521ab3382d81133f720a802212c4cc0f3598a69637d7d4d590c12996751d7fdb866b951cc77d107faeb4936dd759eb90cc5caa371ae59b23678d644c6b1a1c59d76d2896dd2757eecf83db82c12e1a722106213a84c02f8fff7e091bede04a6ea2d8952c80dc443111e4779bfbee5ad4e284e1fee9835c920ec933e74528620eed495ae9dbbf17dc5d2fca025e8302bfe191227f03afcafd7388ec76e4db351110a2644ab2d666d28d1a1d47c20aa468b1b6a7592253850a6b7b29ef4fcfeef3fe27c57e8c510ead204b4f0838870777f743dbc0fc712481c83374c03fdc467a35784e60b0674ff3dd78b066478e3f83077c7f860f26bdf69a56e22293c484a49aa84b987d08690599d40591a49923b429b9898a82b2d7e036e02711b26566d2931bc902b7c8334307ac45ee769274affceba9b010795343cb591527f9c6597c218de3a4289a04d1c8b7eb50ca4fe6eecb4e4a17720cd9da97b10552645ba5c81b192e211121fe996018ec590dd1756a822b7b4260b026dbcd9f48e3a85aa839fb2f8086c621bdbf034e3eeedfed1b9fed9e60d9adea28c5956bd0b9c255c3338b9a74ac7b4b1f4d193f556ac6bf1d35312563c7544fe39fff69769ac91b21a7d969963df6354e6317e9045ad3802e62b97fc60db08d5f7dff943c5c5128323a92531a81b1887c54d4c2c76d543db5a800742e2f6fe6fb4166be3a9880417bd4e336f7ef6de236d7e3e2369de577eb48026602660323968ddf6195522d57f8a94baefa3bda3add6bc4bd290ec5999dead882c1f8207d714f5f764a8e0fb6b0475df0f4acccb345fef5abd004df076d91db5c4c7b729b3a0a4c190566b412875252a2abe29892ae22eae35fbf00a6e850c38a28be99f3dd3d78ff412c2385ef96b62800fe82280a4c9cdaa222b751b910837577f74c3f8867da47b8210332d64c0758abdf61697e3d00ade61294bb284aee6ff0f2e4fef6eb2437bdb0dc6f6d40cf92fb5b25c43fd2938eddebee1e8b689c98fb6b38011391ab7e5309b0ca3fe777580a20c00fe08708e07304f0ee0d21c00fe007e0e910e077582629f0003ee7c17864d46f8f1a47e67e1661e3b412391ec957fec97daf0722a9f9badfeb974449d716dd6b8b6c51eee9236f5adc8b712af5d5b4f0401edde24ae368d5101b3a4b5c871e7c802565c6df781bbfc35a75aa21abc7f1383c1d157ef53b2c1d55e098fb2f4ea95ce078fa00f8d4e7f8167f53f3fdb7fffec81b1ddf7f61778a5f2a6e83fafe1bca9b007cff1d729b98efbf56fc66c7f7df2b6e03f3fdb748deb0f0fd5776b3f88dcdf75f2d77266f6ebeff1e5d2e7ec3e3fb493ca41e79d3e3fb492eb7e1be9fd4c46f70be9fe484f492373ebe9ff4f3e3fb4953dc86f434be1a2d687c1f8d1761cc4114b6d8f8d5a0d1c2a7bf85131a3468d048e233ba194fe36978e00c4ff502fd193fc303532d7e27b3f89d2cf33b19f53b39e677328ced231a0fbc3705cff3624e6709d0d9017402e8e2d17b2096e175c3f0403ce381299507fe0b0ffcf27d171e5893531d9d427ffceb6fd1d1b0a356fceb97e9685147b3f8d71fd3d1997ffd301de5d2d59eae36a92ffffa4d5d8dd2d5a02a5463158b7ffd3fba7ac557fd3eba3a2457fd385da5e2abfe1e5d85c9553f8fae42f155ff4d577de4aadfa6ab4c7cd5cf42575b72d5bfa3a35b7cd51f808e1ac955ff0a1dc5e2ab7e1d1d8dc9557f4d47857cd59fa3a300e868145ff5e3e8e84baefab5962ff455f646711bd07da83d923739662d9e2760c9fd97894bdef09c6250e8f1719be90407af0bc56d9ae80942b9fffeb88dccfd1766493486316cf1b8937e7f3914b7995472bfff7814b7a1ad163ef7b6f069e1939b524a41fa5a75cdda3ad086b95feb401bcbfdde81f146a7c3e28a076247ac25a8276c61d978153a9d6d88a5b301ca9262c393a2f26feb13c354146c8f2c177b64a37bc52254b1273251dd2e91be3fbedce667d5352b4ee982883ded2e72eb000743e4d8b247968b7ffd9d12f8ed51affafbce4e651d20f1cad103717fd3b457c4456ce50078a2af26f481508713d85f662d4af1fa41148cc2de37b0e20430d6d669472c4d8713980b422395b2d0b5cca8bb8c35b80d187992f038925944ee82b894c6182391221e313291277438a14308b87206a89c0cb1870c2e2942b62fc411c8103ffaccae715d33818420c8585ba7f28165bb22f16dfd9137ed040c46c5897cf0cace034b76eb9155b59ee5dc203d01db4a0afcbbd710113255a56682483da9fe2080074b4de368da6bded3744f7a7ee28feca13f3fd933b900c738c33bd0ee6e7953ba2175d7803d727fe7b953449e1f8dc8f7ce0b3620cfa775d29ac43610a49f77567a64092a4b5a29e560b0b96ebef2bf4cc0b32ee1a8d680061e9082082025a620814b091958028f14a02a551c310512a80e3c14dc97dc444448b04dd0486303c02ecbfffb5d9edf65faf17311fad33f235d9e125391918a483c0891482f57c2023d2ba832031f0f18c1072a5917261419010b028a9f120f2f80219d40ca9c87bf32d845e057b342152596102208212e432cb08724a0041c8640e289236cf00316d8312c3e88a0c8a2e52622da41136e8e0b0570d2c56108d212851610b119e400068f112478f2042980e8010f1f28f1e0834d7095c849ec80871e7c9a40a5080b5112587c78b281101461072738403485e827f53359f016d31146b861cbc0e6030811501384e8e18708433420063ff881080dcc6240c50e3fb0423b088154021e2bb8311039c93172132d71c5125b5247885b94316b73c6196b673d03c4e9dffc5a63a44392873aa40123a225b2e41cb98974309435ba4db7e93a80fbf2697dea9c0cd43b9224470f881e18acebd1a76f9f7b81c210a5b7614f029703062f9d53ce19141483291d8318144427925971b8b62361b701413a27fd9e1ef6cf41235bb6e0259148d6de4b64461a3178b9c611b3378e8d82358346369b83c682062c68d0dded050147f92f1cff3a8d0c3405e5e3d8f582807bce5a2914f259bc20e0862bfe75eb8861ff6e97eed1a8ba5adc90d4ac12725f47a674cea9b5a424d2c592075af7c04eba6f57f2b66077d2d39949248d448aa59fa52ee62091669c33a04ca8948df635350991a201000000200073150000201008860383a15028498359731f14800b7e9c3e6a5218ca234996a3280c8320849431c618600c1802403332432600417652408e9e5a57a18edca51e9bd4aa002a841284718d62085db891d1b4932b0d65d852b70e39fc1e61821426a5d4e52f18ecf670760c2f5e32e1e0b36f5e401530c04a3591cc82adceefa52a458e031759c681ac48b32b858f4658be4e9c56b72cee175f0fb20b4cdd458e9511461953c26b107826ee598afffdb091c1396aee88da6761bafb00c920394ebe589855e803b79a6e0389958154367bad5c7d37e3b8d2d66277d5a1f6558e764e501f2f4e8f33b7315c6e17a714e4d94054683ea6e085b8c12cfec5035e662615759c38cc6267958419faa4835fd68f9f3e8f7410bad72c8e70dfe2e6d6a3e1cf87ab83b93df800ed87bda88893430f7b7325d95f3dba511e3138aa9f03426ae700681db01bd89f6ce279f3e704ade989daf5edc51e67f931340a1ac14b3c5a745431ed30839f56c2b4a7e8c5299ec4e4663188a381f5ad03991ab6f018368cc8cf903bcf3282273ca77b9ac62d9f28cb2a5ddba7f4c08365ba97a530d7d40d6dd87cec500234777c129b326fe595c712376b1b7243aae828aac8a78a066bd261575e2b354fb083acd8949008c0106732e6315eaade8c94e1122f2c2a212d73637e5b9e4d30ee730e604b2eaea7d887903743001b5348b1a80de5a58f4332306402277ff62f0ad25a498e7b011acd2510298392a4faf167e1fb5b972eaa42bc289e07c180aeedd2784ad945c9722384d604a5f8a9a22f3533d83066f28e00af8bacc466bd3fe17eec70709f5b51e334ff947634049d1d8dec56c23f78e9d2786f175dba0a4275200e7c4c8a3bb9a9318de6493b24bddb12c386f41e97a7bce667244bc3d8dfe53a54bd4c909c6a56d8b598bc17ec63591ec9940fada5538d5f44cb83fa332338b4f63995b1cea5c0a5e52f0bccfc99e14064dc8e7918e48967353bfa1458361d401006dfa9b029a12b5ea0b616d6102c975d9187a5fcde717ea7ec96f0a815a8a7a7a6359fa7a6ed71be7470037979876a76929365c1fd618352c22a7a8f1a9dd92f2215a701f2a64991db8fa261e106ddfb8773c6610468c4755db60ccbd065268e325e1560dece671846a4a3138ec4f6f28ff3b04abfef0e438714e38a27157cb7f7e4f13edb9cf3ba768f6042072bb7a23e9c1e116be3df40f258da34c0d1f0228e637a5e728f32442150b7a9ed67b6533ecc2375bbe864da7fd27f725d47ad527710c097a6a4147ba3f92dbf2c7782d9a3de53827c9ff43b50018c056db56aec94abd8a9eb558d1af09f4fb2faccb6d2b40466154d4d1871c3e5c18d9e076a988a86a89a2602738a9072c71a3a6c05c5ad2d8f43131c2c207b8eef5f170cdfdeca27052e6342afe0738d0e858ef55590f53afa3dd8f969e005ef6e3f270d00d0e4d2a282b7664cbb7c5e57c2da651a46f822a4839636f1d381b5f970827f2564eb59cabd129042c56f4971e0dc6f64232e4b4b67f17429af83eaf36174e551d0f203471695e799f7bd420001681733d2762d9c1026c1b61a8ee5a785c05835f33b844594dee556a1822587672255607fb4c319921d3d934f144206b2d029212a4cc4fb5fc4d1c506b428c06dd4413e880255d523302ef379a156aed529fc270aa02b895192ad35980a1526485344956224f92a584b5a444ff08007b1e2ff16a7f667108cb19be2ad0f655f4185062176388604b3df9270cbccf7e75a03823654cc0b21d54b30370d11d448f6733eee6324df21f60db8b135022556beca9d0b1c685570f98bb7b5021f44de2229a1597831819249e398589b7452534d47c9696cce23b958fd3eae888810d007305ff2f2800662c0c0158a6e5ba634bbaf81a35c8c0c0c61b3e4f97e601d0dda4069f1f45a2690e29288dba3b4ba3b632dca5bb9178c390c71f8db63c846dc6b8c6b61d7c755c5d5db6120cbd19a48ff7ffab909671a941fc1c38c460395c5b0effb1b74e4e3f8d989491126ba40ab6c8c210ee723b1599fa280622248999e46cb9b43171238649648cff91fa6b6f348ac4571a8ad94f7d944975686759ae1a783435f88183c621d0d1b7d68001a470551f1b4f09fdca6f16fbafa9a02c9e28015c16b00b39212d1e917659034db367b7add3eca9489603b84837a0275a4ab5b54610b5d8e87017ee608ad68c43c09aeacbfc51943a36ce928ab1962a1ed5f2e1162624f478d1663c70d7fc405520b47057b38e47ebd5d77b9a5d39707c03a8c941f75781419b0db62d4bbbd0c37e9cfa7dfaa9d9d347ee19c39f93fc052fada4ef816a48c70141b26edd559f4e81abbba5193cca7c5bce510a9f7ba3f331954c11b58f7e10aa0ec8be79f7fe0f4acad2b7a493047bdcac0f4db7f8bca1b24c40638978b114a5fa0273c364d17d068700641ee598a2bd7a9859efb48082a712d195ae3b1d0e87b0a9b969d870ff7e2f71d8f652b74d8cddfa818bc207970227ab04ff3e00dc753bd6849e6b64bc4b4f2802903e9ac8b7a613a2f0c8a22e026cb31ec631c0ae91a99697dfae59d35be2e3f24a93760a214d261f049b3cb923436cce4094743b660990672a00002f56f1246a1b3139c817373cc50224477ff33f38bf7d56dbffad9bbac8961afc955e1ffacd59bdb85059002b908345277ed93aa150fe0867220e3c5aa5fe6f23d999ad3ed3082ffa2c89acbc0120a91d56160da4147eef7b32779773fe44323d0e0cb9318e0211d6cb5888b5c22e1a6772879b9d6beaab12179c3e11e3b380671d094424587ffa44abb5639b1d3f35cbb09692bea10de82257a622c5af8b4584865df06135600cec10980ce131ba739519ee954400a080d3f75511a4064874db82a8d1ff049f8c1243779a81121333a38e1fd508d3d84818f718d9d7145ea6c82961302360957417bdeb316d5f8a4df78fa613d9ad4fc610fc69a9543f83cb8f33c4343259b7697630d647702696e0361bb4463ce09c993a43a6a3d6586e91bf1b6f2f811d1d00aa021014a59f2a604e98ac2c9520bdcda668ea071c0185b772ef742a0d12108d5d31f5db050f499dd1691c964f8b4f0fdfd87d630ff20dd2d79381994bc554e5a0a6675c28aa3029a82026c90dc36e2e6afa9cff17157c4abd223c32d0fa851e00b3810fd41086f996f70f4210f3e866537cf5740ea5d25d1c5e046005d2ab70a59a164e51cf8734d69225017ae31dec7e22e7f89c73d4142ada1592aa9fc0af9084c278e455add419347114e9091e0cc0e78272748f0768b32314823508c5f96fa6d9ecf91eeee7f0a26fe7fa70a63d36c0e17b211c0fac8e7d99d008a11986fe8a14e71579b320c0cb69a9c18e0ce21b84b05e6602eb14947be3b8fb72dc6cb1c118c2747c9f0e1c51d74849421efa8f7847960db87ac3785627c42318dcf801052f6530e1d9f400b6d7edf0cf3ab4a79e34a88eeffb47bfcdfbfed288f33bee34e10e2ef5f3c7a1b30e13348c45a03bb57cb870746623679be7a5da388e2794798c070657c237e99c9f62baa112ad10f94452f7647efa29b2af9751319ead32d3547e547701944ea6e501972e582e028bce328cb95d44f5da06bcb03f6b01b9db305d28c8005640a44f0814ae412568facbf0f760159a8c0ddcc216b61ae80943108f214f8450ad619395229aced19e408869395626c65742846e4e01a12f1caf615903c2d5f43e447942291ece59eadd5266953ead1c56e2178c84458d33e1ec21256f6efb2b401bc34621377978287cb68adc49db328baca2b8816e3acf4151fec28862392511b3890d5924fb8ccfb80e0ffff162acc55cad68c292a3d870a8c6bdcd527e2925fec414764e50a360563df8b0e66a12110aa221602ad88158301751d2c27e7ac06b6160550040d89890108d2746bc2041ffb69e5a9e1576433a9582a44687c99d0aa6d239bc4a269f84284242435fa50ffa8470e56ac9e073470e36a1192b65c1455936cc800da6a6353461ce610a6fee36ee21a15cd65f7cb9e28b273e9a88b1e4fa16dd2b5f352744e111d00e9d73333daff45d4ec58509ab44b73fc10963dd2edf6bda237be6b41b580822d404bd04d8d82287957dc7732ac3483b9fd5ed914ff84bb1b8fd004e10095df4472dbba1ae82d6518e64e5f829a9561baebf3f20dbfb1fabc85bbcf9ad7102fe78ef6fdd6f4e1256293cbbaf77438cd1c95f34e24a9a1b5216d3b693ce83a832f056bb717c1f616f041f15721dda784d0126bacfd38b72d0a0b2285540355d5d289d97fbee0ae7fe87cf73f3fe19dffd774b206402244359847309ad893dcadc137557c157032abf2ffbd2d49ba447d7ed9d1c95921b355ddc0ef29f60269c3cf4f61cc7a42857dff44720df6630e65e965e848e8c05069d92a16e0af00efeab49a3c80c5a595980a0ec9a1cb671ffcea44fcf1cf5c155b5c3c0145c4e7b732f3b3558e05f73237061cc6fa9d9e4a11fdb9b2945e2cb254b0628b22f63d82fe098958ca8cd0e71ad6d205bfe5b48229e00b2610208f2b5064bbaee5b05dfc7f5d9788499f851fe2cf06bf539afdda5253fb956f20e7cccf752566812f368f93d38ea9ebfdcf51c1421b6ac1b3655d6ffbc0c4bfe2988d626a284eac4e54648c07ef266ed3847b4b2ad6d08c402857e52b3e73de5b60460eb95a1f18ea0aacee1d0a80ad4812c996c2a344502c3006bd7063a78a25a5b2ffa24105ceb5edc7896d0cdb3eec2c07f37b873ff1b8bc6e3cefdeb2fdf81bceed01d3bcf3cb1cd47f880a1b785060c0199fa2b92229e412c334dc875617408566cc16e3fe4fd55064d2e48d3277df0d1b93042bf344146cdfe4c87bb8ec1c8a1c347f49a804bd94b741a1b033066e439580523cf6959b88719086911546ba828ef3fe0404486fe4a5bfbb0c7e7c1893b3e3c965ad9adbb44d3d4807f6b95ae12014ce075b80521c69a01959804b47ebe70ced2f03560aed83a964ace2c7110a2a05eabd3396bff6e633636dba6de21c4c594f1c7fc0c2ca8fd2fa35dceb129f28a09cf1efdea1f8842c316a771db24c83cc97231f56482a8993ea0f8c78f168eccf2d7ec403a83b5192f95aeb93ce58096fad8478d2b551f989ca791ec7bb4dfc0b8575afd6543e6df2be80f8e5cf58c305ede52fded057223a441ae7b43e57d9db7231de7b199f562429b13bfc6cb5b9641f54d97e388d4ee6eccaa2cebced341b5492d4a5bb6998d73193e3a5c43779514f5387ec6855abb08d4d284bf37beaa7ef958f391021f8d9d0feae0ec28e6c4023d6536c4a0722fd9e1565b917bf753a428aa7ccff1d7bf18e475c17bdc5f3178872a1d01306b8c9ec7cd9e817d66e6e00fbdffbc830d8e3fb68e164638a0ec23543ce9907d42f3721f49b1f43c907388340e2ad9973a10532104fc2ecb43416ed217081e6b03e3c1fac61219e60e1594175f5221ea948db66af4100b380fccb285dba2937e7a3025a54f5b93ab3435c7385205a28ba42c430b42ed1a9c9cbc73f6d705863c414af195e90b02f7fc0a2bb865006b14a9418171c6ecaac746afe14f12a6f488eb69cc809faa3ca2cbee776e86a14b58dda1d98da73365508d3c6f94692a75e120a8b4307cb9f814bd49bdb085a1c558c2fbaa7759fddff0eb5051a70ad4e69d36abc8d804390fbdc62f92c2d4d038e3f01ba7444fad387a48def6821d6d3198caa20ee46e0a293deb85b052b4a7d369bb86f48f3ca6578050f5d1d4cc142fcbad40a11a3b28b77903ea792870e83495c7211c3d971d2a78061f21944039dd517954edb4ec30a28798c1a7d33268c2d0c678083b7e97a2ef4d7f96ad01603e878a0fe333e312eaaa28899a35667844caa0fb0e163549996c7bfc5645ed7da1edbdf2636264968e2aa6cbb097159fd75d2f80213793f2933efa008859ca07c2b8d6d167991042ecde6c16e701763f5a6f7e5ab089f2a821d621685057dd6851832cf0d97452f362b409dfe6abed123d766bffa70f4eee4aff7ad203082fa2855d90244378c7af25988aa70fa6f64a91d2d7e634aaa98720c7633201a23877bf39360a1c6233b4082ea5e8a481d6375f4b6b29a4b43684b6702dcd9e7070be0e75cf1ac0c520250d84875564121064103f0502a5e2cf4da077a1bc3fc94adf14ba8f7e4fd4caac1e9a3d4e14b42a33a98e8434337470d6d2f532482ee890dfc6fc93e8b52d27a0f600399988530f2802a66bc7b5fee6532e650b319fec4893abcc0faec5f808f694d338a2303c51cb0fa1a227405d2c4a86c81c7e53d5af93fa28c9d276179155697ca65c95422ff9667dd12a1989bfc88bb20beee03826333aee03c8854f4b46376e4c85d62b13867df088d0168cf949251bca4ddc38bffb1683b3231b7b7d00a3a71818d0ce900c9c125765e3c0ed4eb6a03b15f7d6dd28c5a6313b8c4556f62046d285c77ec1ce2c98e258172ed7f3fa45b20b3e2cbf3336cce48bfcc78122a4840630dcbd335d19ee4b9e7b5c1e8012d067190b2b719c3b690abf7b65d8411a710af49eb593c6050ef2ed397ef675c9fa294aca748ff0706862b6c1eb0ac452f60ea00eb8b5a68e7aed6e44188b232f20f14686bdba80e55b6c0689ac953400225665484d9cd87a2ebf9004926f55568a8bbededa8f2d6b7845b61196a90ef23a97bc5c80b2de4ebc179a64c7655ee3ab677aa0017286de4175b71fb1009993f58f684fcbd81b9a475fb7b7bf71d39c134ab817c9f04cc1f6d2809ad02822dfac941bed6e6df42b9fbc0c0ae5dff0b2daa86e5e6431f786b8931dad4e2c6f361766a81421d575916bb5103137afa08846175bf23ea87d2f622691e57357af0b351019dde272f0a0b3044e161f7a31d4a83491f74ddcc86493bee377ebc4380ace1a3576fb680671bd2e9c2317132a4eb0de610a01fcecff12c280a47370c351dd02cab5bbc2ff6e17b9eed5f9fe616e54bdeb177c24acc080c66140b25460a9874b3c6b38cf619358e1a3fa4f1190296d12363f09080cc38cb0dfe7b74c5b0df52336ea2b617838b6d2e02d2ad39e5547d8cb099a965117ca80824bdcbcdb3b1f9ccae390d137b871bd0426da833a139ae4a082536b7a08e2497374f3e20d0a51fcb6a1c2cbb81cfda3b903d3175293751e4b10dea549175dc146ddb30042758416c531e0cf73cfbb23eb402cccf691bea3b87a81ad990a63c1fb0e3e6862cf28411c63162aeed81d7948fa622120a34a00edcfef49178752b729a13c429af3c0bb267eaa8a0768f32adac8f24d6bb9f1282da266736409e6023176ba8bf208268fe56af81dbb2f21939f1b72d34dcf5a02ce13b6261eba66b63e8843f1d33820064a4d9393d1de4c876a7f4184b3b3204d49922b2de90149c0a860f6cb53aeaa0e0d289d84aaf30e5a8806f8acac3965e2a901229f51ed145565b0924d50163f8c8be041c6bec508c644a38c85c4a612ac016caa23bb128b11f14439f9e2cc5039f6734355459b451e71859b8242c719c45aeef229a95f91d5ce631fe2f912661f7417228140c20c2417bc37b1dc7bafe633a17b2438364c85b08f8f7502e58b06689106fea5da1571665f7b7140551030f65c9f594f42468b5e12cc38274304bdf67215c79a7d1a74b82eabc39c82765ad928a40b1946b01d55fa09a662f7a86ee857ddedb4d8a5d9ceb97366c1cd6c8a7f933e7e3b6951dbb40079457a259fade86729f2c79494dc423ddb4097cc0ad088fa3809217c2cb5f65234ed95854ae1bed397c2e1c2a13a4f47c6887f2848d8b2e4ff9b5ebc5c32a37960e3cbe38e555032584b10e2aeb53759cb5ff8fbc6befc7e281c2d8dbeb33f54fa4437956e689f4f2270c2cd0c07a175c6dbc03e532df39fab4a4a0be1dbe5e39f033e832f0b1397391419d0d9dd537d5634a6b5762263f27b355d30e87c706aafbe4485a188339d7cdd8c0071fb5dcd4e3a18a14b88dc6a3902d28af5fc8132f97dea8b888d64a2aa2b470c7b1fb8530d434edf58c5efbd8f2ae88b7e955638d599674ef29e1e04c1eb54ad482ca57d4c4547b96ff778c78ad2854a2c30f86f3dfea158ab6e87b95ebb90b7d081ddb53aa4c7140906eba4c0324374505d22dd852d04c0a61308a50a0f3b51ebdf03d67569ab199aeb328169769b2bcda2aebdf028ef42c7a3168c5597f75db7e9ee6b1f857104fc8a473e4b62ee43b70b948a0d58cbbef236a30ddeedad6a270a13a6edb5c2d80c95d91d371fd4c2409ddb7d046f9d5e0dedea3e9e2a518092ebfe3077007d8baa5f1e23574f3a0d95b3e84bad7be02ca1135a6b7d4badc45c068a9356b76a2be43edb8b4fc36346584dd2c87a8727303263dae75ddc980ca4e81f86e6c6f193061dd506f4ae41354b6c01141e2aa3a2590fdc5e87178b401f04a8625f2d53c3f5f8c7fa14932a4cc2a2bfad70b08f61e907f2ad8e12b57dcb0c7e37ccbcf832192ba82f9014e23c013cb4f57f590c5a6f73746c9bcd8142acb66a9f26f630be9550856bb5bd420b8a0dbdbf5198680b51021dd578057fd70a1161c702952d2732122d64f625e80114b071c2690431dcd09f0e1768c0b5ec123a0ede1aeda5ebb7c3d2f59251afa0940f6d0549e5d994379db58710af7c9d7bf69ca908429cf9255c25595d818b482ea28e89407398c541ad652b67bad433b7c7d9ce4826ea1e895789b785cc469158cbf608952e1953ea387df27e1bf1760107d3e547b72b2e6f3e9eb1db4888fde82f48f53eedf45752038948a6f11c7057bc27ee12358aaac7c0b3f9a43a10000a5a671d4698c82f16d619c3ca6e53ae102c25ddcea6086f87a00abd5b950547b68b6c808b426094c9e1a413741aa6bb187b103ed9c6d09b6d68c82aa2af63ac5c81c8e928356017b7ee1a6bbb788e09a8e2ee3f85b9270f4552fd07b78f2fc784b17345846934a9c469e3231ff89ecedd24f5e7dc04d0432dad2c9f816610db840aff8970e9b5b9eec701cfed59ef22c0be37cc4bcdae5760be3ed211ce0bee1843a713684fe2cc91a4421922b15ea88c7c9402f222dc03289869bdd88e19d948b13c8269713a17100047939e6ec4124749e1bee471eb7775bce0d57944cabc3b0cdaf5fe8b88172e6051813acb9a537b651bf0a3c1cb1105629ba99be314d1ff1f9c1eedcba6ff974a1c031095ac12bd9b85611a494eb357f123c64a237b902baa3fcec855072b6e8d5c20e4718a869ca188ea257d476412d00a246625168d559c68d4caf1d91103f8f3ab8a1c0c449f6a1c781986510d29d2f987fe0bf41a8c9f9f8b9d76af59047dfbad734471382d783b6cc43ff777d5165f824042eb47f86207827c74adcf6b02b832140535d778f030cfaf3a8439abbd54ab5ef34fbb675ee751cbcdd78f0cabcee7f608be0ea96af9f66de81fa46f654028a103b317e90f52174e4d0350daf2cc84be1c2e3e63c0caf96adafc05982c872e5aea860d715a6460c915140f82dc50f6f7a7dfdf34c28e48c0311bc4ca2505f3270d8e8f18655bb5665af7e9327cce5e07bfa6962fbd3e770c2d6973e8ba3959441f61614eaab45b57fa8ef10d5d72d8adc7b11a08199091556252dc4b682e3d487279613e2930cfa34c017327409c9ca1dcc17d667a8718901faa04cf48c04eff2c218b82f975e1f19f5f96a24a3b7147107a1e70e5c0383d550793936abfeaceac4eb1c6f4c8b88caad3c748068d424b39b48b6ff065a21d92ea9972846f17fa003d4ab57bcf99f962e181f88a9e007ad599906b1df13ca4ed2cf07b6ec88dcc8dcb2968b90848aea471a30f21d46745852c7545a14c33f8c4f7b277c3d0a0b41438fba80e863f4f2ed9a46528c7b2b342b6f0bafefb4dec72851e80c182caa1892d81121d8d30bcef2defca0ee2515c428280c796fb417d7b5163672e4ef734de35fbb6a6a92a27b1dde54ac691c5f9a3fbbd67a0abb74067fd3d48ade85f2dddc142d74faa40aa82524358b3d1730fb048a35b894aa07de948bb55ef0a3d01f1b5331c71a0493e4a689fed2ed775701f6cfb459bcc6974f6211edd5375147e37e36305b34498c30b28d099a91ff27582b90531a552c20d05cec952c9860f1d7f76810d4f09953a74615939919eef38b1ad8a395acd1c3a598ca07cfed4b3c4b7410f1a08b9be11be02d7a4a1bcbe8128b06374249162c5d1edcce16b984ca6b485b95ed76038057d7cb8c9d5656931d22f7978a65a141a7e22093dc78855cdddf6f09c796d71391c6250298e15519a7d1e7c8ff5e9e0abbd9e466642112ae17577df71561a1fb415e5850c13d027ce74e0a2ee1fbe5a1a8543c251ed56d1d11fb240630447a31003f60ffd62ba44fe67d074eab3bb01d9509231d83091a71909e7dd337e569b45b7f26b7224caa9382847f44deebd31b6860462ef59a8326d3e44ef973277d818f39592ed50fea00ee47eb90b5ea23a894e55cea8809e04c688b58b4355b7da7745f4ed6fd7a9001fa474c62bcba000da1f662c9a26ae86ea14a216eac16e28ddba90843e43e2e2f0bebdd19bb2461d7982c726c53b87c13223cc42d23a3f14d9e7d7c94d50a8d4b84d6dfb30139ef8ece0c8b3581dae3c612da6f1e94bc6f1745f8477caec8954ad9a81f29d16e7b9d36b1f28e42721f9d04e3375ac7ad490af27d02a59605896c433ef560c80fb0cb780e6ce7c99a19c7c979eb0b9ed052283c946e03799a7328e444433a6919225adf938ce9f4644b21f9ea94583efa0dc4f06723145f9665b9255e0f64d39f226f8c3b5132f58c286acdfd4062f7c2a9ac09a8d420a4f475e989d5a44b3f486c1d5cb978c33ad48fb1f58d6eea85a219748920c971c4e458ebc4c981c59cdb57812aa3ea5042926a1b956d905d3e654ca8d283a1de2266b70847bd551a4d6a4c3c732de7a7cf8bd53f315db87c54ff5eaed500fd82e21807cbdef89580c5c5a8b2c40e08d5f5f3d24f4a2bfcc8fdb19b5d3c195d715b92819242f525f89d0683d359fec4be40a08fcb26b7f919ed7c0cda24f6a2d978dd282bc054dba9e2399d109c024d8caee457a08b6a4f870fe842f8a433a9cce30bf5440b6232c4b4d7781b1f431a797612c58a106bc706ae46fe777424eb0c6d8c5867c61fbf2673c2831ba4e7e139ffffe12d8295e7ff717acde83395ca901feefc7f63cc26b0058df29d0d9ed7c05004436a86866d66a921043284317aa40eac444200b25cdea026ba07c647cef7f39d90c862ebf7d6d38a6b0e32fbb5097c699321ea5d2e878885cac71edcb876afa4ae3826ec053f38ead826ca75e46236742bdbed488a361cb4ea87465d4523f552ebe86e62ad4d877e21e8d33b7ba9680c7ecc981d9696af9e2b94c6de2d7df3a3190b64540666b0a2ef7a2137a5cc52ab9b2a560e1a34b66247a2f9ce4f232d948fca030ac36d7ed63180b09dbbef36d522219543fb4fb4ce23980edd810a4d5863009635c56618a3a14dd2dfb66c31a904dd9eaabab33e1d40ec225d0b574794d2d81d4517999eb94924910886ca8dd67000cb01c0d0acf0dc0651cda2c67f2ab3831b55dfaec26b53e16985d41d82b43b2d82c0e7999c441d9f59a677536fc9fc7dafbb228a48b768458facc335cb4c305e4586a6e50da71097ae7d6e8182a5ed6926749c4c0483863d132b27347c325a66943008247370dc228f304b5a3f0161b61e5d8d1955f8440902c4a414b5c75a477bc82e201c2d72e7b20be4dfe8f516e055b3965e8cec9adec1981917af33a0b66c6936c0916093ecfcb2cd3917c3baae3081724e25bf83767fc04e476ec32ae02df04603238fd014b64caaba44af5025ebe05ea2500d8065b50e243355d04724d399b4c140b700dea7c0df965c1aaccbd0bef2da4da47ab47350916b36419eec611ba1a4c74afc1b71b10b5e46de7f0f392a87d984f4b78d4d4a076ddb910fd2657cac043e2767e3d7af4b7350691eb61567ac4829747428c07839e56cadd9a173e6a515935ef44f79043384d6aafcd66e15443bb2c5623ee4504f120602735c33ee9fc34a30ff9430240781952a01124ef1a8f614fbd5ad62d614a07a3647bf40c521217c9dcc9e5d0a6efe0e0eb28005043bfd8201fdf50384d3204953c842bf0041f1fe422d4b81fa4317d2fbd24b2f0bdfd09951ef2f1cd8a49f878078b565e468e665ff8128f12fe489348046187f213dbbab3a826d8f0b695d05316afbf99ee32fdc358dabca0d5610373bd21349ce087b237e2f5f179cc108abcbd7410aad54479a69bc67386e7d943c534885421559b405dc4312595180f99169ca83e99f820b1668afd2424eef628da83ec361b47684ba600163be100ce9f11e4f8e17728fe70c38e37cd37deba2520772065575183116e23972bd99fe66afde1b3f370180721a2733d04f7108e967a0d68b15b51748e384068baca0f3c2ec38e54e3a686b0dac46e73070636a2946cec1e4021a0ece6831db88ae8a3f277a734c8ac4b1d120c029527ea4381065bb04067f8502aba6050de23278024e89ccb5a785233556af211d6152d117056b5256a8cbf99ba8b9a0360f35bbbad36936ec41a0149e25cf1e7e766bda6813d47ded9f36e5e1bcc64f9527c46a6811c32e73496ba74d734e13ba3651a65a12119fb669385ebc9db549b65bc0cd7f29cc617b49ecff94e2522a169e27e45b8f16e29770aa04ef805f39b0319b388d8fb5ee7098483041f8e21eb1d15a708d031a8c8a448a859f9b61303574a786aa4fca4b48be9596494f7e96928f2918b7da891a08210c2527885cd564bd2ef709d8b498aa7280c7afbaaa5b8fd6e31e3d812c043ae24674d2a85a54843b7e0fc7b5efdc7c472910c0e65e7a376f526c3779cd42a80545610cfbe0fb782900c57fdc007e7ea2dfd527d317e4d84fe38d9db59d5ce745e5e64b6a4ab1a8f2644f7232df46b8011510e24f1111db6e9ebc0a20d9d951b3aa53c969880d1ee5225f0ac8ae4085f75e0cb741e3af93619f3eeb4b50eb1bbde20e7c4b6178fe8b3e965dcf4662d62a1ec40f2ad8ba9fd88ecfdb5c332459da95f6eb990b543aef74b93870bad6fb80963f7817dde0b023157195c59a2f2ff7a0b73340fdca4113d1524998f17c82a8dea8f0ee556b5843611b02079c1b78f9230e2cdc48f152271c01e32286d3e7cac435948c05f18eb75e3d69c15dd093a6d1cacca2ab386bf6f8bdf77e1445f157709a337198b378a76e93d003d69405de3e0701998a35413477208559d72ffabbec879f4d570a580d40e9e93f923c22ce3538d0696407e8d30a3b897198059e4b349b80ef4270e35a9595441406c14f32c9fec1350352afdb0a66522398053d97a4d83fc1115c7bdcbd2304324f6e4eee2aabc04b84b53f71d98c573bf5a3f4042a8e600d7c679af1034892ca0df56cfd5f42f6b5f0b0c85ec0de33e25e32b0ffc2af3706963d1ee65f465002049aab6bb937e1b162c3e8e588057dfffa6f03ca9107130b2f096afd4bbefe386cf6bc98775910a0b69ad062f8160aec10bc1ba00acdb0f7938358cbc8aadd504d5f8750cf4baa8686fd3718b67fb7728cbea0e8638afb3bd67e48da91f627ebcfa491257e73350af7f949f81230cdba8d064dfaf9c6cc8d22f2d94b3ac5b2f122ba50d8e743e11e33e4eac7193c1a5de78d4d3aba13f7add0ab3a2ee84edffa88c4faaf266b12429f623ff81f49bb12df59e9410a08c37cfdc76b0042d1eb5463780dde3554f1e9ac0f70f3a15043d31198c3e860f99f8c662189f7881239528ca592c70c63485db3650be900175bf01e69b611fc519267be933c79046059226d28849046f071092150c05866fbbd48873fc2798864e2d7c6dec69d59227aa51fb91d30970d1fbf4ba4eeafbc52bea7f9b40424371fd4fed9f92fbd5b66cd92e52f397b7e16b70417e78b60ed9b08ff1b1cd9a95082d71746fb97f832dcb003af3c5162c3f1cb3869c1c5bcfb6d671a3c1944047a5135c402136812f26d4d6adcbec84a2378999dc3140fa1393a8d9a53d2e1abb15acbab4f55516c877ed571c55acd8fc1e973e88b4fe9febe76f47c6d8d8f0f68f7d75f20787b767bcdd63e90983d0c6c1810ec79a03598a1014d371ddd6093d155766a96690d1fae88c3d89f28b535bce5ac2cb3f4b2f7d979a0486d379dff476df75a89b691f74372548a1bc24cbbb66b00971b5b87f52324ddff9a29111e65f563a5558581334f7e252791d4cd7a99b96971241060c15f7f83e2ad19ecfdd9deb1cfc49cdddc79846139f65b8ae880fb20aa25592d79d99298b050aed95491b93f68e708f80931b69835e17dac3f8ddfa70a31e76b8e3814b409940ac47bb306f6b677553eb717f8bb9cbcc3a69c87f05a3b0bee070a97bbe00b8a1ec84860cdf9fa936a3dbfffe05a9820f354bed4958c75971b4a1d06f61cdb5c4c32da096eae0e73bc65bf88de8dd0241c0a05f42e678686e6639da3b249659e2ba548a49259127c534cea98c1abc9775f2a8afa9f32ffd771af90f0a9caa8860cfc677c784d1701f6c777201b2170f5f48235c254e227c52dde5eda28664676df324192bb5cba23b3dc30b6f5a84b52a9ccb1bce4802a984a171b3a33ee1766cb7bcea519a6d35033b804ad8056001aa4aa3f24cf1bad4ddc586ce118d7d1f3542bc1300e9537a4a6da8951ea14156f5ffe28b7a5d75fa680d74c905ef1cbff966c2916cb5eba2c3f46659b33251618a7884316bde3e194d1210dbcd993ea330afcf8cc9a6bdf5b8b3c837648d78f2a48e61080181696db2d3f0396c5196f5b951f62c65b8a8b645dab511bc2a8540978950e8d2070a5b3f7a24221f5ee7530ecb4e7ce492da4c60c09dca38b166421e567783f2dc02e26f9755865684065813564150ae41c6462d41e0ca65d10c6682d16eb89501b1882840a0fea27408b06db04547a419a0143983612c6a586dfc6111a42dcef1dd22cf364f98c2308b2455fd962c7e13227b8c231fa51b007f0c63c9edb571ff4ef5009ee05dc014fd68bc10e1598eddaac8f03681cfd649b8b91d2637d3c83aeb341472088256b60546e1816b585dd46d41a238b6d47698b18d406ff77a4a46ae9e1a5e622d511e208f66e1de77b1c4f3716df1e6f9428df798b75d22d7f0ee7d6df8d54bbb3757bf17a61d9fe1f34b4943e7e31a7fcb816975f015cc1d0a8976bb8debd57cd43388682faa6229d414f82d7e1e9470f37b9528bd596ac1b51f5f61756ad42a2f7fc416eb4f1757b926e19ceb3dd188040cdb2db7b109ddd20038ff35a58904147b7a0f12412db11f801716113aac8ac6bc78e0b31ab436227e4f6d6401da8c7da3d3d6a619df2c5c19f7f21bae3693cc7ffb7ce048ddec75f5256349a5132e618e29f076be92aec1522a135e430eb29eec26da64f30bfa5ae01327df0006740ac7ba62c59e113b084be0e34b9873f93b72aadc3bb24066a22e5d4d9b84232b5be09af7cdb852ae9cc7280d526627b4713f5b8f0a3e2f10be02e659b50b49f58580ef0139185ef173bb550e2d516bb8fa65773949867f5d3b6dd28c2a623d687da37e1cd2ca57e8ea149531d04822697b2d391b303185237a353cfb88b2789732306cd741d8e608c64c554ea9cf047a601923dbfd643a388e5ca8412aac314673e45d9a013c65aa5c96ea5d18e7e9427b99d063b7a556a2b251b276f8e060bfd566a955a0d167aad142fb51aade856a9a5d434dad18d525ba969c4a35ba5b652b3d14637945aa576a3856e294d00bdaae967b4a2e8ab6845e92b6845e83dfdbe53a9a89bd5aa68b7f5aa28375a735f956e055d846e1d5d84ee0f7d947ae7fd754951b7d595a2ee9415a334ef3d596e6b2b45dcd32b56e9c67a56d145e9753ea6e1f43b8bde41719f9c923f77b7caf2a028e606d203e52e461b30a7bdf260702e3c3a03cf1b5807751767d5fdc1423ee918fd72ab34a6245ad5af2c49523a5c15a34005682f98d760124c38860682506f6bfc74022adffde499d732729a8290f0555f88f85f323fd3e5dd5b8a6207b259cedd997abf961dd51512fe17ebce7b0ec1b5df65404eee2ecb55342bf1f6286905c74ffd21e56badfc1c17b8be09edaeb03427736472ef265d05b303ee1da702e9abbbd14eec7907b0ad3b8e7910edaae9c4f59c384e2b270470ca48789ddecf244bd9b96431858c34af2321a2d895dbf8ba637d2c4d624742b7b9aeff39378124f63fc0db4fbccc6041b8afab53d631087be594c5bd9a6dd28c30fd4bfca12a4f61894e5b04ec7c3ac8481c567390f9000573cd0437f1e175d9063fcc55620bffd27a522b96c98719e1ab1d3419cb6df8316fa31923b70b37a23c0a9ed3784a233950893ae6861cfb35d777fdeafb3c7b89a99a470da656020a002b8a0d57616f17b384558e0bba7b1d2d5608e0232e75df7af0da030c1e09aa075685edc60074de273658982967cce5131b829e427a72c543053c8abab656b0d857ea58ad5f7959f7eeed6ef0e716ef5b8e7785b5e5975b2ecb27ac4f93ed5df2bc4338ef644648d753be4d7cad2c63986218cbb3b24da5983ce1764d4a558105da2e545ef180f4d5b139d3a574aab0bee71f107e21309b5ed95e9bc30a22f3a1bf3e7fc4ab84063ee3968c31df8c3be10ef5e332f796f0a968bee484f0a368b6377546d6a75bf57e7037fb4b6ee27ea911ef108c08b124584838f3f8ba1ab8ba9896e99fd1d0e51b9d9dee1a4b4b2fb99c974f2eaa4bec1e9108c0ba9cf6f1a6c9c26e40310ce33a1981e031fefe40b901f8f7e3c4b53739ccb9aba5554dbc9f68a24114e28af98ab1a4558f6bd3545431ef5caee8f7df57575053e87246a8d04145b1d29d57f256940f9b3153cc43188eb5b7bbc09b5ba46f39437e30a2267db5c269717c928b992a5c546c4ea861d060a541cf3aff57ddd6bedcb72fab6ae208c11c64c169e902fe6c2ae190e6b427f2d8eb40036438b7ffa2b28ac6066a64f1e0872a12ffd4a1c7a5c43659859d7d33eb86d4188906ccdc2cfba3e700bd392bcc818a7ae9223ccec5bec551d7b35e9e9776b8b5aff1c681351a1310c1ac6d71b87ee47f5905fab7fba7f3b38984e1abb2ef32faf0a058086ce8973fdd190ce149124034bdf99c2afa2db61815126790322cf2fc3dd6df89f71cce6fdfeec957cd7af55fef163061916b2d192199ee2bcacd0ec950120db1d580fcb56835ab2fd303d27e52f4bba5dc1e53ae5e3389f23297a735ee7e6bd07dd3d8e511e65418ab871d95fda8e578d8c855e33a497ead05cde2a7d7d9608c22065960f352aa5fa33fb29d099022163216f26079593501b76e198ebcfa4d6f834f0c47ef599f9fa8248bb59e15b25e4762a4e0ba3262e1a55a8fd9a39caf0a19490f5e0635508a4d597217e263e38811f4fcbb5f027b5954240a3c4e6696d209ddff3c6be25a3229e44da0b6a30844138edbaa4456a1cf17844e4bcf7094c7f7069cce177a77f0d0d1be36021de0fcb71429b6421ce8ae356fcd2d135301f008210c1ff02aa5886ae1ec6ca2a98b298a90ba30f93e3d2694755888601f9f8c4673a67dda32a34e0431708ecc36137ecd576ba30012e4da5d95aa2561b124ee86bfb8d81f843792b1d841f902c5a858a9e5b24fc50d23c9e49961f21a5997c1bb07f5b0c20d4bd4bf102a1b0cbb146cc3d350430629f020f10f73584aeffc93822f248784abfd13ff7472e49de8a24f8a8ca30593529121dd7ed35a9c6588b3544b70bdaaf1fa04df8b638aecf39dabe21daf76ade96fcbf9dff6ac4982d6ee790cb94b6ceae5c78849e6a34adfb0fc008c77384c9aeebb9a6e269a275ac7464ebeb5cd242ba4cd9608ecc3785d5c431fa29e999e89c550b76839ae54e2940f2815b9853760da071ad82db071a3e006b6b9abde04e107c7229a42872082e5d4f0d96cd1e26f031b46d14d30974599afe2f7e3d0f224b02ecaf1e60a9159a04ec6557bebd8523990674e59895f367d4582b852d353bd3c8919b62443b9acedd308fc89a2a934c33219c02a1a998a0d46306d5357836b1ab020e2ad89c0d23120c913cc44133ba3f186fce4ec36e6296a1bba68e72735e6d5dab9b1121454cd9c5b7b55e9d28d424188633d6db025a5a19053ace4a7b1f3ef1fc1177909d5eb57e3e2f13e11f575a57dcc90b22b73453ccb0284360899863a374c04debd9ed09116d2ed458d74b5d070a810b6befeb3238c8a90d67850afcb30d41309c1454956bd0c29be4b92436899da192d4e38e72446a0f4280dcaa543e7ec70548ba8c684734613bba300a80b2b86dab4718132ecada7e95ef30b2711a4651111b2a2b402f79d115052cda7415898545100c8f0a319738277d6ba09ddfdb61e173c7da0cb2ba95db0603175e542afe458206d469cdf2c52c6d4a54fa1a8e792cad402714b511bbfdcefc7c2c95620b4550c20797ad109eb34dd343d90a65b7469c0633d5ffa5c8396c0a429d8888a00abbe4d9fed17e27263434d318dc263dc4c0a81b22360efe2c1d60f0bfb4c16c9bcb8c1851dda2a7671a3844c8421b62edc9c3501c21ccfeca4c152797d2a229a0be699679e7535ac7361cc2a4d81d746a9b2748c6673939d347890f330e02cefcac1e6d2b8dab5392897743078a0454b9a2b2ba069468c25ba9097a40cbc20e40f1d8184671a15ad4cc8320610c830efe4c3dc4e8742d3591887d278f90d9432044e5b2d81bf4a487ca4919cac3087c19ddc9293982ef62c0502a3fb5149dac781b8dd09d5f8f092b24d21ba101aac5c7a2c59da119c15351c820069ef1d1d98e8f0a5dad2871652a06dea496aef95691ec2c306adbba00f72d913453a3beada27e5934b14d0e59ba6f6b532e5dcf70fb9c833b88c54cc788b14c563ba3f6ef6ac78ad1cc7990a5a44acee76c0f729c387d7ed79a26b949d8f4cbdb5f2aafe39ec2cbfff1de747447b38abf04dcb6a926d7be6d73dd6f26972608595dc23146e8d42eab5248341216ecb37c828f13d30edf8832d328f951709d362d0a3e152e6ade20788e0817e574b7b5726391fc3192704c4ca28878686b90fc227572078f88d534c32f177ed3a1679b7bae9aa9417173efc5c7168404c94003d112bdcc535efc2dee2cc639b59d299e2ac65e8e564ae43b5bc5b2c678f3cfcf19e1d5c6526ef631871c280988ce790689cfb48f8e41658f94b13dd204481c7d4dfb5eb18bc22075a788d41515c0e472ece71909eeb08b7a38e8c1b99683e0f4f9c10142ba00c6d1a7a1f848f2b10de780a0f26feae5d876c531fa234ed83042489e5889974cf893f1543eec737b4974a428835eb71c2f88101560d1683488c8877cba7af7d123eb986a0d0db1c1680e638acfb805e40a81686ccb6302a3110829085a40ce2a11d1f3e5849994c88c528c63f26e1fee6e32faf2941cdcb4fd002bf8da92057efe0bc17aed3550bb6ea734a77478c842fccb94dfc5e52afe530ff9b2326daff2207bfc27cd8eddaaf1d983a68d1b09fb132ab4e6c7e2324d807124c8aa5fbd2f54c3476fda77f05c5c13ac7ca14b24e2f594d094bfc36b6b10c49bbb1d3febb9bb8d3b6c59bfd7821b79cb5250b72e9f74592d70f000d056c796c43b0a900ba125042ef6ebcdcb7a56d073936100fa820d16cf885d9cc1e9f21decf769effa482510680fd2b2605040ec989a05b4e695225a4ba9d3f582d7891dfc0e01ed81f4e3b9a45376ff737711f7bd90dcb3e8fa4ac55bb53e191338e668f506b9eaa1aa2bb9607f10705220b2680c083fd7eb9a29f42e87bf75a1b4d6714e971560da93ddca1da6b69e7cb364c31482679ec697593cad270d29eba0c9d54575b72373c97cca4b13c646ff33e16f9fd54673505a4d0acc5ef433d1fc25ba406fe35b7c3236a7ab7ee49935c347b3226d7ca84894c2fdae58def3b3693cd4c407ac5744682aff96e90f2564739d5d8409f2db4875f5498a2063a77ce04535d089315465c3ba39d6d6b29a32b37b8000b6dc9a50713775072268c7dedbedf88db0f9a959c302ecc56d9ee781979bbe540c4b9ad237ac13e425c1b383ed7de5733453458440ca8d6152f854efe2ed7c60e1002f8a02c6a06ee0017fc944b05a2249e6b44bfd933c9ef3988d9e2edfdfd2d3baa95f2b0ac0a03c15deb392e97a3b55e49260a79904a0ec0f5edadbd5af3d339538132735da11d99de40d27f7ac5e9dce0710aec50094efa3d7938a421ffaa1435e23880bbdead983f08b99e3c783b2c7f38954b593966db5fd5201f2c14d258044c70f82f0a46ef21ad045bcf8283eca554103a92691debd8cd8ed3d4b7b11fbbe0e0a5258e9948c976fa46056451b22b990db4152eded2a198a9020912a3fcc252a156052e9a1b4ce09225d8b0e43641257fdf68b0b67c1fe2ba0b29f01c19843bf70c524c94343fda5295db317522a309ac24c6815208c8098fcd58e2af90b5a06480a7604215c0cd0095a8b298cd4bcabc90ec948cecaf4f61f65ef0d6eec444af0888d3509662735bd515f9b248b949894daf6c4cab9bc81b130bccf3fdb16508bc09eaf9984b9fe830e11e48aa47aef5a2a2769e805f5e1e5ec1f2aeab5efd79a208ca2969f1bb9460e5579b0c3ffe9197243fe2acd38e664e0861ccb59f8e09e330104e2ca4dd7b88a356ce0d14c1ef39f0a9c390b2850cc74f306c327cd2be80d1c7b158bc18fc3d830669f096b54101ba9f8c11c7f12041306f5e15863476626091c8d52c26ecac6c2ffb9b296f814fec4cc9cff6014bbc6601871e432371e5d4b8e4c074c81ada445694e76949c4592ad5d10c4bfc78c67e8c365254a7f93c170b027221f00bce8e2d2f05c476800ae99a9082052933608c501ae946eb084fb023a4001af736bfd9253838df9b708e6f5711fcd84da2702bd15e4e2945e18d4c45d192a1c3acbb5f8f1c61e20e5ae173419696d04bd68c43acf7224a79137f0d3e4a736edec8738b384af38be5a4d258575ccb3f4f8129c876a30177359a60d13911442b6fe2afe137aa8744791b33b8dd66853e28b3cd76e5301991843110a90f82b7e054e803770f24ed75cc18ad240f1dda3bcb1df0909c13a2a3a178807c295324436d499bb7cea689d16804c2a546576ba2892270e571a5c01fe5ce42c7fd0518b32e0e5f2d2af71b0d221c4a46d5ce799391c5806b5076d44de48fd01d061078347fa4b518e5c24e3fa4e6d35d0b110f99a9f362b5368c00bda9dea5bd160fec1c6850fd14deae68d6ede7f3a3570de7e1f7040b28cc8f510c7d351aac70fa247eab4693aa0480c8d01e0e901db89532cc9bb7dab6224c23e7ab8378277bb30a04d883e667b9977248be95eefd5c3aab21d39f188518932b1d69d59f495f64017bf064231731893b3dd17596d78010b10bba3d3720dbb2b4e63ab03d32eb4fc7d3ff83e1eaab27f50de0edfea0c68532ecbab30cc70d42e88789d3f6fac4c1955333f4c619957ca2bf24bb89a408703042ef5f5820a5f970c99ccd4650a71c0e01384ed87cf791a6d7b01b7516b6c653cb3e93d6df4b2010d701ab6e7ba519442cb9f95c14fd0038c01a2c8f5a6eba63cc3962608a22a3193245e3b44c8cfce0eb74ef934f362ed382f4176894ad05397689e55d99b1a550f09f2879b6f779ffadb2273b260d22231e763995d3c3b47055fe8b9c447f8426337a8e2b09fee9a31bb09a7fd2c08343b0e6cc158e73f3f50af10e09b2cc0dbe43c09ce7cb0f6f266503a9146f8768457654ca5136d7a407977b52cb3b03c54b6c49753585455577040c3f01c47049f1b92be6858971bac97aa5179b2cf84f7219df76b819a782d321fbdc52cd2057a37ef1db3f596af4583c164d60de4381c2c9ee5f659210187ca1b9a04f116d0fbb87e884cb8ec1eb191c699970ebdc2e8016feeb51bdc3f4ca8b0199700feaccedad5cab19d657b02e1734372e3352c8b17e55770f590136d113be1426186d3b22537bdfc8b98271ba428ac49f4cf5bfc7e99c007e7aaf7bbefe7944b84f30df23835b2a6e901510143c970d68193bcfa2ce7ea9602f01ec846e80c8d93871db47d0a220e1761e001bd794c6a0e62bf9be0901b498cfc6782daae8fcc7910810d6951ff83aa219defd00f8779ae3040f7629e6be4b03710c7bae9a591dc3034298019fd105929f0dc81413d9dc05e85cc2633683d5dc95fc32593717ea4b328dce2e5f9beeaaa2e494fd1106b2f6bdbac7fc64bead3c6d1d98fc2ebdde9c8854a607b776010d42ba0fabf95e4dd8219aaba04bd85dba43001b0d50f761280788d17e9bcca6a02d7544e82a1000cc195ceb136e8f7a4229db2c1e311c616d08590890de056293f9118d21b505c87a41d5496756dad1acdf532893edfa3f9b5f2b0aeae408fff75159f0ec1c82d037e2fd4c853884316b992c127b0d091b82ed41834c5c4d49dc3ae80aaebdb7578b2e590e247f0964f86a4439667a353e04644e9988dda060d8836af62f3a44046830766002ef6bba03270b1181fb9eb6e234710592b67009ed62e89c9059369e69705df44cd5534179b9cd0faa71bb386c035c7982ccb9e10b8bb556faf93d0f1e06ae61247e7f07b2d785a86dcc80ce29582f3c385aa8034851c213bf9bc44305e8c2ce9d0426c0f52d47f898668646b514ab2e2241c1abd261b5f13baf80b1e12ad1db3ac609c9caa553f018f1c82aca475f1f16d73445b4ffc6e0af03ddbbd178f21b2ea40e8e004e68284e2e6bfdf40e06d67b2914ba1b37d7b590a626927e2284417b888da9411983677c8b9a3cdc905c1b0f3487b99b6efdf77200b432c4608c9a60e95fb78291aaeab4a716d78e4a90908b9af2bb5d41c91b1c8504dbfb375f602e0c7e6245dd736daa3842ad3728e903c9ff1639ecae40d84da513b59d4245105ce3f733ca2ecd0c2a5f5c399bfb40f26f34a5b0b0c0fc0c093fd450f1e3653cf6715ea8bbe574b90e589a4af9179555d90d84cfaa6c248e33b3861d9b0a845827b397aceaac4df3025020e3828abfc25392b1098b14a20d64996b7a540ae7140673a68a729aff5c71c43171ae7a044e987044b0e7612fec022a7365fad1d29089925a959ba125572e1aa947323cafd7e833a1240fb8c60c54bbee6cbabbf473679f808c83e5553660f47250f6b2c1d1ce08a21f350eff6a1fcde5b2f01b7b2617c1f02d9b880d795b81974970432feab7d93d76361d7d0629ed8c54868937cddccbe22850e893e16f4c141598eb6b3df9551745f932daebeafe446431c9c9b78f4934e3ff3106935bab1015176dec37e90ac3a9090e545a2c0e3eb17fbb623cdaa3128c6ca5c44c667ac244253b84cc94608b574847cc1c50b30a577d9acca6b5206965ee8d98975638255583331a1df0a69457806ac5e0c0444292309a29f7cc8598ac2a179a2cab74ce337572cec9f2dd8168794da8c1e413c58186504c28e4457d4221d47ae2f733eb66443d0f3d36a1ead85acea7582a623a8b694ebfdd8ea301bd5e218c83f7af6fccf70a93b9fd889e29a0dedf31a252af4136ea4b37daf433412f49066ac9acea577d76cec9d1f29ad450d1c09973a12efa1a9a951ae7e6cce2425d0f9f3c3101bcbe6896d7a829c81ef60dbd60fea0591ede6e2294de91b93d84d6f402995e416057b53ffddefdfad4510247a03f21c926d5560b68ed9ad467f38a93088a6b1f5da6432fa0f72c648a6a20981bfd15af212a147595d7e8d29780f6d0bf024166fa99f4c623dfa0c3db91250e9075c973d8cd3e324a8a3a68ef41981fa8ccf9ee84d02eea008c875cc9a54a8f64131f540b6b72ac2933b24ce2520bd37550f12d856023b4986fb2c8805e48727f6c2c50df9b4ebc415c97fc44aab90d2f27e2620c5005bad74a66c87cbbdf7e17a21b803adc688d05f25dd4c26832d100b630a8e3e7fd02742fc1c5448532deea520435112acafca9dc09908cb8d6e8601406902ed6714f227effea5408f1782744bc52213777e662c85cd023d21e50e4c4a00701b40877da0bad4087cd10c52681cbeb1753e7c101f07f5b2a214c693b9ff2a3dbcfd851db058a0437f05beae019f92d75f08dfc3635780c7e9f127c06bf4f0d3e43bfa7089e31fd0f0d11246d4a740b274c696f5ba905bd1acec8acffb452a4f9d5559b056b123a71160c24f3153101fe56a2c00d86999f07ce270a532e36ec57fac13096605f9ba6483d98267e4b17dc47003e233fa70dce6e57985e4b70cc253f69418488afec5f72150123b460699119e0580e8bba7af006da0855efe7eadd469015b8abc574c66a57ebd47054e920b19ba2f65fe917e4c04d4f82aa24987883437271272978b1210ad7c65cc6953d789201e3cacdfa759cd2546bb9115338fab8e3da5b008c9c5e182f753e1c119cc93286a6dc4879701acef12c0749ba33a8504181044339437279419559ac172089b1202b0b9a72b3255a3996ca5e294b3c4ed4809ebbd8b09934a4fd2bcd358378cd7bb62ba3653b70108bc256b9d77594c966ebe21aa06845d858dc500f78cd9e866f4decd8bdd2e971cc54109f68b37c050c28cbb42ea724b62ed0667715f4ee218b256ea18c4986c2fd41d36df3cf5834ae99602efa38c7ffbf7ea1319cc10535d8a23f6060bd671534b8cced698394164c6e8368a830e909f333d6c34c3855b19868db2d6f02602748867c8b908389e97b073fcb06570592bda3366e54d0009f7d9ae4a43387b880d8c90ebfb18c04ccf3e48f994674527c123fe5e6b71ec913a05f13691045c2c8f1e1a232e93bd890192216d8a7556a4629f1fa7740c957cb5032ad3d0cba3a6547d001e1011ce5059729dcf9566d2f69686f75601428ac9d2bdc090f4f454dd6424df78ce70bbf096f9d09d1e4c15a252ce778355e84cf79a4f38c807eb1feb735f410474d247af54fdbd11f3c4fd10b5e96c642c4deff93ec3a5c697cec747fe9e9325f196b8ee1c44c45890d36d024a54e7d7ec02ec49f1ae197b0c184243aa0f86c294d713198fea818dec4137cc230acb0834eecfbd557c9d01fb07f3707b1ad5b39a4f8e0ae01f872c5b6f53eecc4d058d4e2e39a5f460bfd38b3b6d0c60baad8ff8bc69e683ce2f6af3a2e4a376ee34713484f5f5aae445be9ebe88b8086fb9b7cdf56bf9ea0dac87d77c12a5d869296f125a44c880f84b1be8a09b037cb87b9397a310a31ad137d0501c18a60c094f6592f00783d3066e5c8fc8239cf0906bdd5bbae1a67fcb643c148033f5c50941acbff1955b89fa905d99f139a16de1da54b3cf4b1b7e3fcfa6135b8c6aa2c6fa123a5ff40c954057c6a81e39f834b7bf5d56bcb6fc23ff835f95c82ae9ec8859b4da1f0287939543618f5a78e7f9fc4cf044b1dbfb74bc67843006e3e0235662e2fb72d673187c1c61778fb56cffe7cf266825b6e5ad8e7fe87aba4594dce01123bcb66c6e8ca912fee0cc1d31702785323cc933919d1b7f7ef8b3da7d66add592e63e88b72b92a9177bd07c062337207e94924ebb8395df808cfb851092e96b80388a425742022f72ea09cc7de632dabcdfa8383340677a695adab58573568f6ae2a0e03a74390e8f9b992540566c46da30dad9d82c2f8839af8ec2eb0bedc2357b3720fbeed398abbadbf4d7d8787c4cae231fa0d8571fe8319942fa095aa4c23b1418bf05ca3396e70414572ef9ebae5c15feadad4e714e6d92a3096bad7888ae0e6f8c5bcdb4641579f5933f4d64329ae4d9f5582f0720657a2ad3a73f187f312cc4e70cd0eccdc18b3df4e7b067c0baf35260155f87f382eeb828725b2b69ed2968154ddeb3d23e2d3fba160f0756907a06919dd2b15dcc419a0c50dbefca6a4cb95adf26bd171e2035f05b5bcc7813d8623b63153b925ac8271fb748df44479adb98a765949f09c69001e0e0db7cea22419ee87058d34f8be4eea7270ae64d8adf13813123b077df887fbe521ac862800df1c7770f39aa9aa4c22450e7d5c9547517cbe79ddb3151e729b097bcbdb4bdbe2a9dbd8ec32f70100508d84036437501c586401463bf4c9d9b05effaab06e841dff3e4d5f116341dffb97a039bd5045048a7592875857eb901bc5209390be4d8cdab7e8d8496589fd3340f65aab528df322c722474120e0bc91bbc29c3738bf43aa8cdf8d9aa9b97357edd70c20606de3ad4657ae2ec85890c724fea20cf29171e4264adb4bad353d2d98967dbff66b2e1abc408467a945d6ebb009945e39f043a54f044205c9aa114c76ea1dea5d20e60fc638fa35df17a0e70bd37cbfb7834dab6f32ab14ff5c9e44c9b89a5c9309c21b9872e75cbf8664c2c310eea14ae1677dc549505a40185f599ef0fcc5ad7d219c45f6bdef9664086bd07931e6e751890e640f1718c42980744fad5d49ccf9dc0f3aca92ce0b3c336e64015071cb6267de3093f920d4c51a605feeb9bf6a31aa4ca475c8ed26452ac3e74e14496853b4e0f820f2572b355d23543dd0229286789c27348b3f35a92c664a963958481c49c0098bffcddfc09dbd1fef99274c895db2bfedcf61efcd4a849028a2ea1654d730344a1250e70462c9b78e9c6bcca7fa85af0dcb42ebe224a520d824f4c1a6ee271215102d9de754046faddddf65442c6b3c7107b7fc25b6de21c74119fb5bde8e721e218a786204f4a1c89ed7e2f88fa178b793834b641fc707288647c8dc891b85ba831600a1e7c84eb35281d99434abec69fd2b311b06af6458969cc002a3ff3d3a109d904395cfbc5053a37df5a9b280623d44fdf07d448e127f53fee46ecd0fff21d7b92a35120ce1ace0877826105bf893d4a4b913f5dd92a40c84af0654e2e85617c99cd39b13184f4a78926e6025333a1d61ac6fa8857f167fad90d527131c4f2da4be8c697bd9b6cda8d6b8201f1216a0058937426645b83f1462cdcd5e8ab71f6dabdb92cf2dbe5fd9dcaa456751506180db1ccc9c568011ce0b8e1373df908e161734e3b7da10c5a007a3fec510bfec0a380675b44855bb17e4654ae594934077eb0b8ed3469e39e75ef11e79cec6a6e6481324f4a7d9c27fab24c41adaeb0628da42c44bd97d0463f329bd6a53fc8e2067267aeddd6ad9e2acb3a1552c635cad2cc3bc4bd02bf2e1f42b64902592a0ebf4cbe0133ec2da8b178a9296c8879fb04e6604ccf26371ff05999b178fe8d31e5741b3a8b55186e78c099191428c21fbcfeedb85a6198be6fc830fad987a60f47f43313eb72a49aa5f51233cd49e6ab7a8700698cd48d4c13687918d11bf59ac95e3aae679a3043d2ca8d99b4222bbea6ba416b0c492ea4366a2a228f385615c06ec739619bb035ed3225b894813b0812ef202b716ee052e06514237769e61789d95a26b1b0e58970952b1b24e1f3209acf8cde8cc917f650367b4a7503376281e51f4a1a3f7a87249354a630671a230c13cbe9d57cbc632e3b722371e61862b403c9b1048637bf9aefdbf8c6529db7102cac362832b652baae02324d2b9d0401d750df8527429be5e89f5208055410c316ccab8c700ae4a06754d5fbed5b6e9b6b4c8245fb59019c550ea648319c9bd47272b7b03e01472a018384251e1cdb58f040d9c5241f28149b70c2b07ed48049ae55131ff1e306881c068a2c198906c82839d8d4d303f215d0f501de5093d7a04f495ca82d54b05141bece06d016a1a2b239565f36ac266df1461559b2d3c34c68629f62c60f481f9f325a2a6203f881143813d34e3acb5a3e9fbbdd8a4738aef0fce663db40a218522b298dfbaa8a1d102cb6cc12b64f77acfb196e38c485d627093dc889ca124033e24d1a33eec2bdb634e60b5e0395f75d8aab193b4d290769a48d94e740ab82c94adcf8604a40805bbdc52f8fbc2a49e65a789c82ebbf55ce5c68296a653cfed5be44c5016e52a20e324013153cdbe009772c9ba351b34db65c9a99edcad14eb443fa1910c4a2d802b08552741711b8dccc9324448b9fd2de9244a9a59ea7594ab516a97fa7a67480a6f52c662e3205adc9cae4d12c9eebd296224ca5d91a07aeb651192f5d794b88a45bc8bde218d94ba92748e2549e2f846fbc31c582b7bca270629a3c2e958ef9bacc5ef34ec1813689b16e59c45464b5353a6683b44597b1a8845869a04a42c37e3ae964845b0c06637067a697f4fad40ae6a4f1398030a8d048f1ac82c71fdfd08d38f4f7fa891f64396ef8e261bf51e4ee4456020ec94c15256bbf3d706ee02f0a01d5c1ef296b865fa9dda39eebed2e0d394fcd9c3458280c6e730d843c49d0fd65bdd0dfdba480afa99fd3e06e29903649b9c1a2e2413d2f781550659cd74e3713ff92037f26ed499b1c2513777b002341906c7d88859a40b739ac1de102ea56a0f17029ebdfd645ff609b272413d8ba369bb97d5765d61c4111476b4171ca8c3675907df9e4148fffe574c1b34582f138418035a2ee2745a300cda4a07cbc0ff1d299d60cdda49244798f1b69358a11b08a96cd17027c854a084fc4a6f62cd0f787207e03e73fd68570cd3f14b01e1f8b917ff2d0478bea9081e76d32bb9928bb82d052184eef8fdc9de9b6cb9a594324919060724070707b928f42f440a3398f5a4cf83b4277f1e8cf120e8629607b18828f220569790071d162281ffb917227d0e839579580b2f4f79049fdf983004992c22752d1e0f71c2e37bdd0d12992ef9ead4765effc3a94bf92de82a280889153f541882640443d6b400e5ca51e5e1bb0b99ef13dfdddd34420a9b05786bdd7479eb4688b70e8648d663a4a1a0cf4f0dc650105158042183104a8428bf3d886b856fff6e745aac81f103140f3fdcd982b245070834df413447ceac302fa8b0d65a22cff6a0068431f44d748027a755950f4768956153e5db6d586cb47cfbadcd913642f800435f893082304458202342396403238820536641c162e44836c515ae8410cbf86024009dacbbbbb91011ebeeee4a67bc2501102470e1ad09d2f440c3f3f0edf6c5112275d27dd6647d63f540e507962f22fc50c4d797198448a1c508293144409921cd51fbf4e9f4dddd7dc3f6c882488ae21a313c48560b87c301f902e6d2afb57f1e0601be3df300e49b071ebe9b072476e0e17dea483df5cf12cd7eebd9fbbdeca32e29c75039f2018200b69c1cc51c84900e9a231ea7fe412df2c16288231f2060dc51cc419071c4335ef99cfa17a35d099d07fedb389224eb0fdcb7b7fba0543fff731d3546ef7f6970142b2b69da1a2158c1ac3144299834da7bbc5d606443558bc025ab6d07e42d4713cd441f0950f1e787688d0a2605d46b1075cfd00648f3ffdba4f1f3d42d68a229c68e0c538c1d19dea816f951a560caa7b30cc1eb2985aa514ffddaee7a0bbc2aef5e5d6b68833e8cb6d94420c41c1dd0f1818f26ea320fd5ad7e60d6e800df59e79fa0a27c7cc0a30333064dea0e680d1d933ac96892a77ef3808dfb75a456660cea624ecc89b9167362ee69cbe841940a7bc51dea2a5efce98e46aa6dfd513346cd5136622ec5cb1aa83d83a28fecc76bb687f141332bc9b7ff7483edd33f5d6b183df435eea016b990229a49232c7d775b3b2b6ef5f1e999f58ddb7cb346c29e9e7647d3a75823771eed12bda3cdfd48bf72164599ccdd667baa407666b1542d6a81a9ac9aa86ca23540a09ee6a8ada7d80cce7a5a3b4735118995a4a89a8884390c97ade4a8cce5a954dbe814a62ac526ea4a568fc5623c3cde3c6ed44d33c063a526c25252392a477577dba71ad544569a88c769057be9eb8dfaa916d1ab326789b2aaea7595eb255aa39d704dfbc9e22a10ad61976e57a7a7d76da661a9760af6b8e39258eaa170b5c84655206aa5d013668c965192e0f52496c25cbac8e6b450c730cc1ad6696ad2a06aa2a7ec978693c291c246a7a7220c76cd7491575620ea596bb4b6b8babcae1cefeb29f59ba348fb949f6819aa6df8a902516cc57a3836f58e86a9c63b622b166797688d5c2d1247fb54819c688d3ae2244a41d2d3259ee7f9ee91cc71bdbb6b77e81d861f9e9a3144a78e93f0532d12472ed80aadd123866152409dba9619833a8981d4118f6d44eb524d9404535a6aa21da0f3c8384777f0109fc7634e5341d6d8080d139914500f693887976838096ec24e6da358c993c04a4f1d86ab45580a86c3230cd723296229d8134fcb8af830beded531c61763d701c2ad158ed8639eb44e4b2908b43bc9a4a1e2e7c5b7eb5a6ba551d7e7743b5629a94aeb6dafac5a556f371159d75cd5724d13cda6b5da52759d9eac66de5a4a956bd5a294525aa56ad107746ac6b02ec308b1675413512f7a7555965924a5a232dc88d455fc1d653386553181ac5bb147b262d5b1674dcb59a7f32d094d6f49daf5166c4b2dbed58ab33c1f4c80b573cddbaf0f6a11a59ad3da5a6bad36dbaa451b176ee1a0ca53085219ccb4a995dc97ec6705593533cb2056be67ed15fc9c344c8099aaa9aaa2fec455cee9c662a5719a5cb3a02115823c5a5655a05912e5ecdd50f1fde1aa7aecf993b340be9e6a393dd9565ee50837d029fa88e396887b2b0315888aa1aa9a3472066a117daa4034b745bd39b942067447192793b513eeeb40196dc48e610251e7b1a1e2f3d854330675a31e7d10c976ea2d644eb271ad346974d58c41b9868660c0a865ae9001dd116c6caa0a445dc60f2218321d9869b47ce2e1a93d0d08814c5556eee98f5b620e974b8813d75769fd7ec7efa0ef854ba9e4e28a7a5cce399793463b75aac32ca2546ac23ca54a54429e7a98234f9d3ed5a2cfe9533bc9f36da681a86ad137760d5db8ad5e2386a78d483615552da23aa8a1544bc5f7484b3662fe590a59f399fae73384803df14d558b606316656e6bdcf8834836ee0b9dfa592691794b36ae71b354b2f2d49b4b13f553a7b86eab174444a4491d531898d4f59356e26d98c1f48fce19a6ce097e2a607af6aa94a9aff95906517a1a6e307d7a8fb9bb3d7b306350cfb3dc54b588ae40e6e92c715c32a5d56b8a6b2272cebc4693284d05cf1e4c1a3dce6c60db8ce1156822183428b6da4d152b22ceb2d3254fd225eff98c3a3d1dc1c6b9a2a70121fc3822c56faab6518aab454dd554383ad6a67aeaf474449750345c3e5dd7598c33c6b9c3d76275187b8e3fcf2f2dd7a2cf2fcd83ffbc03ff8da4e7197bd8f3e87b9e7b38478964bedeb977ded98862de7906459f986787c1603c3c3c7ea369248df4f73e30f468f3431a8ffbe058992805d93f1ad95bb0dae4652bc54ef08a78bd873ecff31bd4bdd17a5dee46a1a1a18e83bf9184f164e7414d542790656a618cfd463b1e3b1a693bbfdd0428059956289ff5599b3ef1f54c6b50bf794a0cb23d103fba727d56c75883ce617ea373d868495afef19607fd3c624a41a5d7d6d9b89fb5ef7cc2f82ea869165a833e1691cc6fc920d1f6d90b82a0df6807fdc7631a69c1ef601df8de48b3500aee5faab74fc38eea6bd6773b6b40c9891d8fa407df6e542dcdd22e32be64cc913147069c1332152850a4482193a950b162c56c26a34e8b162952c8642a54ac58319bfdfcb070e1c25db6e2675e4a014dc55c8162f6d334b70dc597a28bce8e9122f83c0b33b09f678c9f1bf427d480be5dc182d622bcf1034e9a8bf046ffbc2dc09fd0860b9f36e642f47111daa86fdb86e421122d44ba127bac2f5ebce8172fe88b7144aad55e0460084900e18d0eb2d9808006308049fef83c634c1f4100d4e88b596324086f6bb510e9bb2d56bf560e1d6760c4ce2f5e843760b88c3a31e468aad9d3ccc9ac6905d7ccc9ece9de4923f5b49f36a459fadcf7ade06aa2f977f6d4368b69f8d164ae8a10c9a62157179b869cbfaac175d06f5c0747d07f7cf61cf6d17e7ccf9c500ab2f58e8647db3717058e48dd8249e33651a6301fb5c45976daee6e447fb2b5b4b4b46a0633beb29f26eaadb6792d6a2e2f4f42f4d6f26c7ddad651dd488ad78ea4185471d7ea8a0f3c26f175348faea8f97c642f389df27d7312b5db39aa19d588d46db7c5b7bbbbf1ad4e6dad55566b9d330022cc490130634ccfdd3fb3986fd63e5a411f3a2f9edd7b69a55d5bc759ad467d9b4e6b63f5daf7f24d74f77aedcc34d3222fcb68d1e7d3468b4037aa401fcd4834aa40a207804983faacce49c3ce6e23f7ec559e51a781fd3a1ad149031b19e10e4ff8a8ad183ba573c6c0180498368e12abedc027315d1e27d01dc05fa1bf0b2801ae182700a31c2891bd2be606404c3846895fdd95af0d0fcdf9de3aadb578bce3bc47767647d53a9287248832d995d5a29087274693b14148eb0155d028f0d5e7ed49ed91950147ac3ea62022d52f9bb174559d3b08a6839d2767d51c3a47f8408788cf747c28d2761a682635d07c1b45961a680a21f6fcc4a293c3bd23c67e62d1a9a243e5195d8041021a8b4e0e9bc1a1889697214f5345702978ed90bba2c3043784111b4b4f694aa0fa92247765890d25a528466c10c161a921a1898d2155433cb121442aab7462a304ad0de828617d55cd1388267e6289d1e1c9fc02969831794964f1134b0c0b188b38fb892506c96725f2fcc41293f44535470e8c9f58614830c72b97475cc7d93f1e3ad5449d53fd9cde753d775a3d0df285cd534aa7a2ac48f1f0c03c77eedc918a727a828a59d0a3643505ca294793d0a630c7ad04941826da4473494a4be09089ac1cda2c3d4dfac00ec57d411fe4f092038391628a1c16e4e4f0608bcbcb615341268817b83c7040d69a5cfba12ac10f6b1c8041d9f1b8185830adf04f4d0c13d4d0dffee61cb2019c59e1cc2da8668361b1cf8c26648280f1b476bdab160177eedcf1b13807786685332bdc8fe28b6f4ec08fbf238f3a3b6bbf693f3aea33c2376b3588b479ae931e893231e726a2b44a29a594524a672c889383eb5f06c12e022d9822d2afafea5757659995a5a55565b81171188a759bc52ce69c6f36671773eebc3ef7130e87633a9a8eb7ba28a88128eeca2975575dd56fade9ac7c7b0725a7ebdbcb8ecbb777546cbaa92672d1357549dfdee1c84075913710ed6aa596f54a09574565bfacd795ba5197eb6ae127dc84b38e48cce5dbf114b6535add252d33366ad6ad4a85b5c85c75c156df2d70144e22f5dd7ebf9ae8e77a7dfb5d33b1e6a4f0dd2ba82e549bef9be4dbaf5213c92eae8beb082a299cb46bca264271d56ea5e07cdb269bb3f3b5e5bb76355109af59dfd54ae99bac53432ca9b5df6af876d28259fe560766f826a79ca822bee79df7bc9d92813949cdd02c2cdf5b49bdd43938409a8a4b775b7579e5f3649b69af96d3ed93ac4a2f7c3f5997eaac9a57f30b3749ca45f6d37777fbac4ddfeede224472ebc25984482eeccc340b87fb16fe1322b5b0220b332c707816487879162c9028f32c8e38e259f82c4462e19ed4fff88a10e9c70ee0674750fdcc558448b3152e0b9156a8b8f32a3c4588a4e226f1752493398a104956fb14377c0a3f1122a5f8ca148153853b4281c27d422414e2f827e434fd093711229df0f13c8b42241fd184115ede84f784482672091743a412b6d64541413d697ab07a7a5e7c4f8f8b4584f03d2e2b22eb7bdc8da0f23d3ed77c8ff384483d4344118329513c238a0710c5a1177d2a79b182174517e1e4f0a2cb4e78318e9d17dde615c38b1e5482a5173d16228937f1793c0c9178ac8d1131c3c71c0c916201a153da8e9897701e1a69c279fc133dbb0e0ffc78d1e79c347a88ef39f5ea4da239dfa87ab4ec301a0973f0619ed72367f77acc32f0a7383ccf79e8e8de67ec91df1b2d68db90d1056dcb3af2c8233b1d41ec94c7c9a01d3ceac7fcc33011d36622f6bc2c3cea831ea3e188b9e8e3d867a8009f1187e719f699f0461d1d48c3811d741b6ee0efe344ce578ffa9c317c9cc72c037f98cf878d382c685bf5fcf9cb307f133be68c41471ef9b38efa264c38ecd6361d17cc18d5c8440f1ddd57306354d7719b31866030cf43278366d8619e09186d6682c232cccbc069308ff90c15001b7164cf1f236fcc2fd133f6c831d1fdc5b147ed5133c84f9d8746693bb07f238ffa7787e73aa89c08dda3c9fc044dc98f4690790a2a2948ea9f83b058fddc9fe97b2349a98fcf309a8e9a4176ea39a45191b993adbf954dec7cf5a126a216ecd04145c98f94fcc8738fca092a285c46b31955d0a3e9f0c08fa782c24fd04640e1279c869fac6df5e7a3050dddba1ecfb37f1e6de6c5f01ed5cf10e6d166302d1ecd9bf2b2fc0ca91b75e73b72ce9e471cd8f3df19752fcbcf6ce4f760786f8af6a8241e7574b3b7638ffa748610b13da87a89169fd0127dd569a80841c2979a375344e61c39539472f08a3215441234ddc25587e98a84480d304980d85e413624c704576a30019c1d56a081cd521821a40411a10b92322abc176eb82424510394262648e29405c995365a7408a3c31926745cc85c51826891320526cc2cc2083744ca2d59547040f266e6b5606f188b45b116cddc754c90f8aef349cbd8ad753b55e78b0d288cc9a1ab841f3354719284a393041ba8a3b269c8779e54dd61e6a87dd70979891d15bb2ba54d0d56253cd1ba537524d6a207dca0640b570397d299a36cc97c97e32614479c6f71e4ac9a17c8423b7c9b89e77834c2e002eba39d47be5607bd560fb2631dbbce25c9ec08652e84c7f571df1963a44e09375a623ce0c081db93b7bf3e8bd82922c48899cec372c71cb7d76264e9817e9646aa7cfe591a39a1fc668c113d21166e0dde2ee8a27c349d832e8994dff3b35c5a23fbb208098f1dc4eed86d411ded07dfd147212c5994fd2c89104144abaa6f9873ab8e48cef2eb5707698d165d106d3e6eb76c453edd01dd7a4bfb992d76511055130b619ec05046ce51f59f269212c9af851541d3d7fd5489d4ed639f61033ad0b69fafb36d20ad41bfaaf966ad6dfee336918d48fbc88e648ee6fa3aca5c10e79332b10219f54f0ddd43be598baa5f9f209bbee315ead341a0944677d55d753974a5d75f39f75eddaa6bc3bd82e2eed862cf4f392aa790a1f2534e2147e5282ac5c5d5468bd20a942ac855a05441915a414e4b08ca5a6badb5d6363535d55c6dd26aaee6ea207aab8368aee6aa545555555db854aa2eb5aaea5e4ac7faf4244485ea537daaa195eead9deb5c17b9b79794ba48e73ad7820c11ee37a77f73cc9da5a2ca3235b350613335723596d4c8d55852a3a926d444946571d26650d2a5a253744e2f23a32a9bdbb24df7da243667973c4d7a3d986be604a8072318c277e8d62a343444c3acf282c14ab8b59fb793d97962e29773f6d0bf9c4bd0643edb66e43d6e5461220f8d1caa0a8d4226111cc976cc0346f37ae8efd08d33b7de86214f8e812e446be42b4429b830999362f7e08d1c9a4143f7cead871d11bd7148c80a75a390100adfac2d71d94203f5413f6d3823e7cf9879ff698399acff3a7036118e91d845bf9de70d5a3b923c6ce865c7a106b0b7236d13deb0fef967bbcaaef7d0660532aa31ea61ee2229d7e7ea337b3bd5ca397f341c22b5504e848d5cdf47494b8a19dfaebfcf23ede89451dfeb51dff410883282882b3e1027992b499ecc95a44f00063e6deb29238838de983f9dfa6c3a11a9d7376bd57f1cd9fa659c7327f3bf30826eb9c393fe8c016e471f57b0c3461fd6dbf1e800eadee8a33af50ae49b35f24ae192620ec48f62e3950fd754892e359117133fa7ef734e1db46957fd3c74fa81340ba6889f8763677367eda7836ef039cc3fd8e7f341bf2112f881df375a3a52adb64db25a7596671e9a95aa964bdbaa7f14b4483c32a6945a1df471d88d96aa6dd5bb9e8ea4b5b2b25556aa02592e966a0791a45a5c526012eadfd7cb3363f43c422479baaed3c0fae737ac7f947a97ddab3f7e34aa1d6d875a7fe38eefb32e68918f243847245db80b5b7ea947319fad559b3a91e6315a484724bb37ba00836eab32dd59d61556e09114c1d563da8aae155c33275632c64db5a26bf684674fd746c13e180c36929e5f918a488a1c7cf6b39f1139d4a117f3783ccf796261ad2192f7d9d0adb5a10dddba98dffa17d26e3b48233fbf9de7d6fa3c56264c715edf27d02b087a6c2461220c2207356752bc437fbffccd180db434d0ba966dc23becd7ba28764771e826c2f81ba3d5300cc31f0f822208bad725a6b81b2638b3078ea4f8dda8e2279d1e3c29936d552615dcbb8e93582d19e38c4712e78931c6184b055d96ab2ca5c555a099b15c5d6ca526d12fa0a192430984c0512335074c1839658088214dd3085263605e993159c418638c31c6f331c618bb80ab4033638cb1956c7d8a6deed4174ebc34fb9d2125de2ba781260730d869204feaccdfbff7de8bedbd3e6150fa7befbd335a2881da160ee3768107bdb5a0f1b50e5a4a29b5d6eba351a893cf067ad6d01c98d1e48792346ca67409a3c58ae9071d7e4cc9a1882c31805193e54e95333ad05862822a2fe454804a530587c7b85400149c3425308571526786139e9f65952e5865edc9dae7a8fdc5b45a0c8ca874a540c7da2a5bde5a6bdd5a5badb5555e786bdd67c613db54ecbc8b9f65952455828ce0fa2c03d8539ebf23697d96c14cf3414f623cc349c4d8eddf71d610c9f2a0df5daabdd65e5ba7841dcfeb045a71b5d7da6badb5d65a3b9271300a493e003fcb146e48c10a05203aac0c6547cacd2670b8c589c0d5161276f84213250a064d26a8d13ad3c52486890b53d58b1c6e71728ec141336f15130a20d34773321154411307c893375d7006b093c4cc102d38227c3952027be408131e0e44863092a5240d982d2dca3421c20419b0e8d839ea8e647d77848d48c6037f96506d32545843f016275719610055862a89b3e9b0077f3647ead3a16c58aa7995505cbeeb6a5e36d37e96504fa094b29d3d69125a1c54dac1bcae9c856210edcf126a04ef4ef6b219d6e16bc994279c00ff2c4ff852e1a35144ea3d5e7b4eafd46b90a61178223357506a816f36d1bcf7de3bbbaffbbaaffbe677bf1147bbf56ac3fad4f63a7bb976edcf5a58adddb45a1f9d670ffcf8aaa3fe1d715487517c04881268786e71728b9fe5094c667293179fda84c544beb6368b9fe59423a6fc9026e6840213ad1eea0451e1cc950b60c6883083d451d704654c3e8331ae615c4e31f218638c31c6425ba68430831132883e3ed4a08317295e9e9c55425562accae66b6b4b997adbcf72ca95294f4f0645c1526dc55de76fe660b132048822ae54c10202a4cd10261411e7868cafad52b04891fa9642a58ba78e007e9652708fc99840734384899718de9410028d324c99aa375f5868ea94b0e3cd8132bbaeabd382d2094fe67c29296dad09e149112a4854c170c4d4c533ebdce2e4173fcba7328f7f964f644cc839f7add5d66aadcd6d445861d42c11dbac619521db6aa565d769a35eeb5427525b2304e7fa8553b335384f5c66882092414143375875af858c10b3cf0f41cfa0d37003d0b397fd0369a1675a06a0872389a3739883383a076938a87ff373e76ed438aa7bb9a38f4dd07ab234e1eb7faedf70031c5566c3eb1fce007b3792382ec6816938b05f9f21aed5418a8da8d5e4585be3c02dc7ad080edc6a8ffb38aa8360e739bbe75f9e25103b9fdf7ef3cfdc833e1d6d8ffb78c461fd8e43bc2a245544683549a208a7339e181c0b66d2dc995a1204093250e3045f4808020e141166b8ea2ae17051422748112460a0081d303ee4aeb0a074316900852e2430e9503546870be4664ca162650810282e27a047e808f9c1871d5048b345e668fac480cdd4af5fea382e38d2a6948a24504a13040c93285da264518a0235bd37b71b93120f139398953a021532e9a163b255a2b130190166a2c6d2a46b6b4741929de69bdbed4b9690490a22932232392b961061c39d38260071b3a509072170544dce68a9a14d93252e884bbc1020cc12269e25527a6c08a26acc92281898bc1113c70816921e9aa03113a5e44d4c091d15605a00d36469088e0cedd2039210b4a8b06507255b605602b0bac5c14aacc49434f9ae5302f55dfeee0b1c99122026aa6e71f2f7b38ca26462879b179ab35259426f725e3a610b105e6fde6c89e10813c25439428622565c5e0d5e95074f727237b450cac91ded70f244a72cc2c40914906fb7659420df9ea1d849a36525e6276709068a9c92830ed83c19a45579986f6eb7ae282357554ab0852987b0045348d0d08406ac21921c8125795283865b9c24251c6901bcc5393243ecc898b062b9c5c9267e96507290a334fb594279e184ef6a5e2306425f44f0670985c993321a449e9f2514a59741c165153f4b27216857ad6615a4e6450309352febd4021317b4ab565b2148cdcb3a355921d4ebaac96aef49925ae65d87976a5e5d76d029a9e6653d143fcb2770644872d7be1dee3aecc64b52171c72261852e6044d475278b3031036776ef80e490b5e979b11beb6f613366acaaeebbacee797ef9060692b69aac070f07e964fac722e6690f9fc2c9f484d71070a9ad99be611e8eb8b49649dc5db136fc3b77e6791e77962983752ea7402d119fda6b507e8eb96189678790bf441288cd9debeb22591cc426c7a86900bfef50ee664d7751decde194268776735f2feedbc1bede30bbecd3f2e0fee62a5dedde149e66e847919c4ddd449d9f58f0cc1db4c2b52bdf7e924bad3c6aadb78ef12f169c938a06557eef26824c445ec7e964e5a4fce52a88b130f4fca6cc941072efe2c9dc63c19a455d958a9e9dfb76f2edf42f4c07407cd1c252c6c8ea69692254360f84188d59da3765950772ff94aa39e0090d5643f7e2449ee49e84dd775b4d63d0a0d81812dc5cff249094eec38e59c4f1932c392395496b8b152df3edbf38f2915baea248979418d972d4f86226c90d0300488244c477309961ea43081612a923b121a22480a53998c57f7b694284c7dfd4aebd3eca497b3e78ed6a3eee0615d47f75dd7e3019c57a8048ed5553909111909000090006315000018100a89042391389406a2b0b90714800a749a426a4c30130743418ec32888612008a218038c310620640c320a19950d02043759ce9c6b0904bcf5f909cbd65c7481c2654205b7cc431d905cf7a0b1ac251e5f4c3ba8e83c6eb8c8e30831950f372c0ac03bfc9392f890f6a39d1546cb6ea70a26f746c1ce99a1a830b658290af32dc76053f5b5e173fbb0109395ca4b160026d2de9ea58d9b32e9d65c28f7f0cc2f6d20ac224b8642085c140908a60f39b8b0e97b61af82ed0f8696c6d98210e2e9d4fa865c9a159d58e6415c969cae8fb5ac8a0e7a52e1bcdc290180471e28472e33c3f82f04e57ba58a0d3ec5018801479ab7a847562863c2c2c3b188025231826125a9516128ed36181611843238f8b3b59a80d5aacffde539b05d2104127ff964a8be111385fcfe4e34065683b6f51cbd7f4f1b48100aa4e581a80021185654809e170918192093965276ac45f0dc93004792bf39d8fbfbf506d47712608edbe95243aeb20ff80cd57e49b929a848ac62d28272e50ae7b0ad1d3b39b1bd8b7cc7cc48b4a7d6f7fd3a05e453c235b4c341bf4cab861dd157bb66538d99d642305b00b899d6b39c6955c7ae76933b92b418b6be169322c74484cef2d4a3311c38d2f77ecc75d2b47e578f171a11598b5700a47d8f867b9801d0c6d5c0b54c4c89d41de34ccdd3fd25f75d09a6f1dfa95d7c4f24f7ef5c7bbcb036e29e2683b478d9ce96fe2457c7a189f55723afe846e473607a4d6ea524999fb75d75c89d4aa02e22c8328bfd7923f99bef705981fcc22d2b42725a1cae42c25cad0b83ef047666a8fa7b0bab3eeb178f246b2d684db38355e6f6726eb691628308c0a72a08e83d1c56555f78792ffff171952e7e85180ead0af2c022a80b079e3851646f2ca51fde564d26db8599be577df87cbda5bccda0a6855b6cd0c540161e1d3ecc00cd7d7cc855c4dee61e3f04ce30b9737b702d7523aad30c700d65776288e4e4eb6b6c172ce43ae534952cd9a63232ad2b65cb260c2b5fdb7ca9a5bfd7f97dfb7138ca17b6a44c6cfa6553d7cfd78ee5f38c756f1b1af0a356423615a197961c425f647afcb00649d493a87a4cc8bf683df756bc5e59dd153801af5bd9f102f1578b5cd38919f45e3bdf723132962a3f544824f1df6c68a2ae620cb821e96e004025e5484f072d52ee597f59a7f06259b30baac796fd7841d5727c4e420af9b1c2adc6d9a6497e770d762496fe6dedcf58029ee0810fdebedb47a03fcb648245cee0b863d2ea8d664be90dd624069cc701cbc883e13f728b454031085629874adb5765ca39e8b2473eccdb76fdf7fe2c1b7c9ef090dcc8714e6910b339679873198313f6692e470a2b6591d4173c1813465969d40f32a2f6303ba7a0d470f4199021103bbca1db19e9554aa71335ebe8d8f7e2c08576e9e7da245271a62efe396a6f843a642e74e8699634f72a1ff2ee5d882a6524e7554ab3755d2411add001489d7625fa78d15fd22bbf94b1e7855b80387eb2f82c529f7b43dbdc7b31c7ef8569b492bb33880135eba6df0713d60a796a0088bcdb45c38a82efe85471abe4f283f0217be852cf32ad5f62003ee16677d549602a0c294791dbd76cd6082c17241d22ed8b064521165a80e3648024a46f4dc7a4ac27889b26b3d6e50684677bb3b747d8619ce40041f0b90403998066ed893052251fdd28b8e151acd7ba16eed5b970512a363fc4ca293068cde17df822a8b024699c14364a84da38c07dd1d32efd77214c231ac22c6956d589fefa88c49304fb5b69623ba22afd4c4cab51e8c14ba39b7ba87e43941bbd445a717670ecb96ca1b27e480571cd3288abc113716a167e168a3ba28e1f86bbf1862dd873b7aab4ea423ca4697edc1dc1dfecb79b263498edbeaa0b5eee480f8cbe6bbd0649a84de084c00c47f6683306c773aa85473e80a964024c30a04c800081e83190ff402552ae8b363bd364077c1da3221b0ca23d2569ef963d3db4a380bb249640e0fce3c776f40701581db2ae8a9d8a03940b1dc43bcec7e8cbbe50a82d05ba20f09259262436c8308fbe5db0ae5cb44b94756be3d2a4a2dfd3b106e5edfc57de022b0e29c0f643588941bbafa793ade209611e5974df089fd4e8d5c165dee0d1d01038088c6e84d6f172e4886cdc605e549f0d9fb40feae386dbc7d3ad9e696663e50b5803cfdf0ff5f4d41658bed053865ba31627fd22ad11371cc44bcc8d115d1e55d79245ff584c9ffaa98a48c2a8451d198b26cdaf60cf72461dead2f2e9f9965d06bc0228f69b82763ca3b594de80204456de8d671aa9c5fc7e581f940a7fa9d03fbd30d0bc38272ff15f0d280798973429149358fc8ec78acd126a744055c34eda8da0a5940a4e70bac84e1adbff3b8a76bc2ca4a3c432b30e29a87939cace2689006dcaf9c366dacfd1fc465e55e7a5d83a8de753caeb01c198c0e9d4aabcb1aa5b4156dca9153f6275fe8410a328b98ab1dac2672a09e0f9e416a88523955f78b31862bd06aa81f641e919a5e522bc11d42af53baeba4d8a2edd90c48fcb4e2cf74e8ce4674178b89e3880bbd87012d7daa33eb5d4cef5ec396a410baaf811b1bbacebaea9053680d9c3e111ec6981a26d0f87694b23971f49212afe0859753765f69723ff60c00f4aafb4f8379d9a882f2530abf7707b09b828411c02a194bc3894cc6a14d1bf6ab3e9ded9438b58b0e703906e141e11fb98b8016aacfe0406789bf060c72b5125310c11d18b9a9b83cd8c81f4634d7e621e5609a80f3bb2e00a35bd223b04f4165fc590682ececdd57e9e5063ed4bd958fab95e9c846fe63f11a5d27290c17c4730d6736dad0a6a7dc4a1a8ca1e60f8e94467bdbce4ce9970423c1300be3d8b27a069e8b521087e3ad1c78bc25d728bb71a82949e6182e540f75025c4c46d366d9272b89c3fe06d922c484f1ec195ace85b874f30c447935b891d07bea6aee4955887df38edd77be8c4d80e44094a16de801d3177583a4d52ac29de965dadfbb35512f9b956d93938203ddc200b6a14066d4a6b07cfad3e0c7d9ed5d8a14d08f849998720c6549b027e46100549707f7bebdcddeb86f37172a7bccd9cf626f760889ed0077d0513b49fc0b21eb5ab28c6c58cfee6ce78cb638a584a2734201cdb4061dc06cd3e82c3f624e58ed53c089267441558a3e387b2f528dbaf66cd263a9a12938859034fd29066072372e49f80fc3b21e6af4ea5e063cec0658a866dcb047d9d05f615d6c10a0a8f437807b74ecd70842aabe06051960f91cdf2fdc86824cb10318af1ca034015f00a20feea6c74470c41830d4993eaa0e5cf95779cc1e1ca1a1da5664b5f62c479580ea432b468a8ba4d6f133ced7bb3f116712144335f60d35dc6a0901d04b35ff34824d34c21ca8db918ce57cd403b540edb5ebf2f24c9f40e4c5fd4785318577d418b6b7067d322f381aa54d749e01a2298e53d079f0a38499e9816a3f09d1a1a27d273fabee72c621b87e907c8d09312ebc86b7ef74df1db48ea535d032c02182b8836b187d8996fe82573c6b778036e137ce82330363110726452dff445148173f181f0387c4e8d7b39fbf5746c3ee45de773d2ceac3133776f6fcf26b0faa0430f0ce7b59d83a3fe18a4a682e4e37ae7e11891428f8c5033646c78c1a027da5b6ae9110ba2a44fade25660675add24bdcb0fa8e6652d2edd22fe5dfd9d8270ca877fe9088a5e0b46e020c3ebe01c7db0b920d0a68144f342745d36b5cfdf25d7bde689cc055e99020c22419a5431a467624beec4a6176cbbcd36083137dbbd2fb7a400c38d983262eb330d75ce34e8b3efead9f52f5ac324d138a61432b5e4163ba1704f4e487242f72354a4c78183cc463f8378a2b9dac09bb7f3393f933ed97158921be9fd59918e1f31f1acd582463e9095e0d39dbb265e81cf77e65f480af3f58bd0202f020998c52326aa2f11282a86527287582bd5bb443e4474491dd2d1232a11db9392447213d2295c201b458b739719b01f303f42b6bc968b49dd96f988418d403e497c40c5485eda287887eebfeee89881cfe44168556e76f0481fec4cb59afda220bae47ea7a7e0f1ab0bbb687a1de5f3006cf279478d19e64a8a7543a9df9ae08ee21b956e85fa203b657728802d862283e24b1d7e42f408ce47f5e832283c026dc826506352d231e357184fc7e2218ed53616481fa5d2d7017e6801c406bc26c2b025c70b2c2a6455dbc94fd693831fb99c10c5e489cd15b18cfdba45cb98bbbe38403a00569db12ff8cd65dd771c63b6775b2ac4204e8ea49ef6b1391dd7c5373299cec29c1c7e8151ff8c83871db75c865d2fe0b356afe6f218b9b0041e43f3bb0fe2118dd607a4012aaec9e357ec0bf33f00e3ca001e8ef1ffcad037e3c0cc11d7cc9d37555afe582efa51033f0ec06e0f3e9d878e34ded4a69b44d50bb4a9a2ee771cb97ea0204f9e1a72da7fd0cd6d6868cca6960353066fec53caa32e0d40af76dd5f649f623db8cea0079f34d5ec8e90b6b926d962eb91ae3befb680c6e4bba3cb416b0208c84eee5ff062e64bd3f16ada1c480c56eeb7161e4c11ff156f568e6c4876af6bc94c05e5f6589ef421b64bc2e9a0f1e0db4692a6bf1b86d1f4397df5a66dadda272babe77725deb8238701453114fd4b8da57ab027e1ccfd8f6b225f109aa90863ddbb599ac22b9105ef735369afea56ca108b7f3abc9b0772e621458a18c1f8fe7e236e29483a46888f6d0c5ef64c95fcef2247746332d862e37737004998ca914436ca3c3f06e33a1c45a8bb2e76603b38ef66a66bda3c46d530ba37c07b214ab5af90172484f4bb99f7a4ceea2243e91df91b40108cfc3a8ebcf7efd277f6dcb2773dd3c340587ce0a62137d2b079663c1129d25bb117ee701f7aa20a03c817305a9e657b5fd4a2fab7843e4f8080ffdad3dea21bd9b614075b021ecd7515c31a3f07ba4a18ec455bb9854e99c5dae071ef3bd24b7e133db7cf07ba5379d23d1436b53193974e34233dd1c60954a9660fd1eab2896bd283d20c1949b394d16daa1927008f54e7528a1e309883e0094a7b3356da35b9029faf00322c05917d3925a4ef31e8ea408fb71f558e05a6f7eb10238415f23eec31f3b159afe8bba9597026f395e9fa8532b9121df7c5d2692a319f9b21d387e22be86bcf4026eb94481e8107c56fcc050fe1c84d2c201ca6194b1dd20d60b5ed784f8f73f2f6459c35c6d82bdb8db87670aef751fce78737ceec7b5704c9a40ea544bb5ec0dbdc12593efbb3165ff129d02970855ca9377eae0592e54728b2d84292a5e6248b98e0af93a1824ab871f6b3229b4340174f7169a061b1c3f0d09426c8883d8895b1ad2917ae417748b0441cbeae3c90904d44fcdd10833633c063dd451f88e64bdc240c621b2eae22219e5381be9d26124945641c1238dcb7fca1ceb87c4e37992c3623e08f2b4c248dd33f606f0b4618331308021540ad4b8b46eb46c359b4465959f254adbeb9f72eb1f85986ad26ea75aed544fb6fafadc28e22d6e694c5d59e9cc80d74687f56ab259c7e557b5588d3cb46ca973525b794b8fd9f75550ff59a84c60915d5bc5e163832b447308a88a69a32e818dd292b8eef84d7c00f6ed2312a9240f9826dc379f157fa078a115ab2b53968559e9edd9fcdc7d44261b6035c741f908d0fb25098f8416138b15125be48156a04c095d04505740308f2110731c1fcb71e03b7ddd5ed58c676e710fd7ae82eac64b4021db6e72003bbb34e897133de8083e38db27aab76fbce78f56a855964ec8d839bd3b2f8011795d1339f79e65da31dbf3e26cb20902567610b56538b53c356bcd708a50ee32ade65cde08e436a5117cc3d119d2c8fe2b4b5b81a2b607764d34932ce5aa79e8372467bec1fb9d9541d384c1d46dedb6a00a8297b6473265b88e2046118064f440ce72d7abd33ec2a166c5671993207f812ba45af56226b26a0dae37d26567b5a9de2a149e9c1280323a9bd80ad23d73a863321134e1738249e335048b61ec33343ef95cd0589330a7452830f95d316c7367969392b84851cc622ee227d3c339348c5e315f3c60c8613d3a77207d67e6208bed3a3df2a2202e005489a455f12a5d61d25ec1267905da54193f4cf69c3910c9fdb439a768057d94b627071d38e20f089923c792e14f577e3f4f8315dd8949c1eed18e8e7437ffd34004e143c602927c7114d75c7f3fd8f3bc7a39a433eded13b051bc8eb2e2943684889b12da0d2bea00110588366733e3ff573c107e7aa5f00a96d6a222bee9648199cac2a83433500bfe899e056d5232ad3b37010908ba0af66a4b2a475a6ed198d481c1e446426b7def172888a8a43cb1a6283a199595677f97d4b0846a01550f788769e0f8c62d1b2d4bd6b8156c4cc5eb39c6f7927f84da062c536bac1c9cffdf712a8b1e65cbffac220353f8c062bed3bd6739b2a3088d1d08cf698543d6ff2daacd60e6cf53b75ef045a619f9fec2f32649faafc026b958f40087579cc6d20891cb4a663266cebfeac1f2816d6bc4a06eb8e8d28a870c9a88489ddefc255331dd6e989c556dce807345f1a872fced4b8006e2ea8f4b9257bc15b7fb2c5cdc03a82ffee35c01564ae8997a3dc27d691fb53d08f34d0fc6723d7313659f5a6d7f51cb9c6eb4a5a52ac6697bc8cdf0b7ae306bbfcef08f4d901b295dddfa9364802e3ae3a5ac146d8e043b911e03d58eb5afd161ecbbdbacfa34e0fa275af0fa7ae12619149292c2581bad58e751d8ab692e33d17e6715efca88ef939934bb806b1d48ab9c33236bfa7774d81fbe0952be568158b6f9652016cfbecd5c15405503c766dae1ab543e67e5dbf225fdfaee03876266b44d7ab89cec90f7353ded571ac76fc77a1384fa49a8300982ec5cb790deafe23c7e17bc9c13d48a933359ffe2901bd6ef28285bec2232a0d675247192667e5476abbfd13dcd0e2409cbee6c15480ce901734ac1ee2d3968d8c8cf5d334ac6f2c4d70aed8fc36a811ce7861668e4eb9fcd2be75f6b18a26de2faed46b6f5d4461c5f91b551fe253c9e5d1edb435460ed9b08610ed326fe56647776aa1c676ea9606e5faf1825e7999cc2b16fed4edd8c5800aef329c46ed5770dabd9f9b31ba24645ca1e476edede9ca034fabf7e5c3065572030c9ada125a868bfc4331cebdef0e502f06eea56e7aabe3496fbbe2afaab1dda3c5bdbd06d4f6a62e9c4a10913eb45d1da0b9e9b8e10b92405b826869ebfaf109700d6f28a07ab9f0a0e37dd806d80b26cf0923c0f5d282941212a1eff85e2e6b60285d4791188f2f1a2c4dce6a346b7879404ae8b3b9c2f31a4f3291ec09cf1b492171b795bf4687682f5625dce53dd3a4f5c3612f0ad6aa359832ae726a55552aedfdd951d52fa603403bf3cb24c37c3af7e3893e5d82c5114a54e826c96d17a1b5362e33e1c23b7900747141d3520b428538a2dd97f6480021576642e9b731a2240b0ddbe4fdd6a352fa77c80c605e750aacfc4436617a6ff3988e91b7fc87d21354de7bac0be01707451dc9233042a7e81d19b478d7e5666bdbc92967152fc30d1c74d374ed630328c0d1dbb91c23b4fe6dc3a5e496d0f5d8547c0ba07960aea2ffce7801c3ec0a811d61fa1d4697f5f0e3c79912dea37ef5bf6c26fb24d14fa8cd1d515acf8e8577c61db31df700beea112be36f2b61ada437f50949f59a1028744022a0b9a1a292181a279f62a7c481605f9cdb027860587b6388ede2d8bae854a6833832f808351e02adf5e5c7be9fa765582e535082c55d72c44ec7b2dceebaebd34a9337a5320802f9f1280e65f0d2e28c6563c99960d7400b822b2851986003443f133ccb89006767c3ffbe00a556e403afef2bdb0bc28fde91fc163123961c0aa217671089a904505cb0751a9a794719c18d101cc1ecc3ef792a37e99697b60ac816ed3ca622f9b558d4306a1511887c5c0118f751ace5fbef539f01413aadfb440d14758692ad1eda75f757fa58fc5d39097525f0eb19d2565e649423b5ded0798fd27139748c072629790968b478470667fe24b8d47444228fac13e50d54c51bda49c0dc396133acc7781425200abaf3fb61caf44bfa5894ea06d3409363c2743980d87bbe73f8e9ec6a2c73de23569274f1e367231aff91092e402968152d35ba1ba714e7a2c12408ddeeceec8d2910087450d425ea14b091c35ed245ede166af81e2b6c7c74b804109c6e564c1ab6a0f0230d142717e661cfb83226d604d79cbebad25d3695f3fa84c711bad70d69ac3df4fe7d7abff24299cb934fc5eacb2e079adf667175f524990eb50378126e4c3b3a7601b75307f632c2c7fe26cb72b42881b6f4951bddf7c82c976543a3ed70c4c9326dcf58779789c1f310ffaf06afacce9f492b7555d6885a697f3d374327018bb9065c1a9b16280adf3593a82750e5f415f9f3d61004780a96a5f28b55789d50b90449d73ad559fa0fd1d86ba80910abb774a794487061585df91e0adf3f370e8b092886aa7d88ef7b76e5ebf73913ae7291642d3a165816590aff3aac19f9b3078bf7f43ae7c25d5bcba98f0da998ac357c0c495edde25c7b2487d3842734e016ce90026cfb90d6c77ada2e51c2c8142ea1234589c054b387003e0d046565f2ac427c1aa75fc291012205d2487835c598df6eb3cb4c406852cc393c47f38d95662cea562fb45f957c382860e184fe7199e9cbec48b5e91b618efad74fe92df0c319d9efe03a9e97ba20989e4ea537511503072e6db1e8b7cbafc28f7272004295799347472fed0ea7647d3904bd2fa943248934a4d5520389a10b0e1eca0eb691f333815642c8e00f55fd4113adebe1ca12ac478ae23a511b3f5cd456bdd7c462994eecbbfbf33a8c0488d162b3f59d56a3b97c52a506eceee6688890702d421258875871f04d1c820a1c6753008fbea4d421a0dfa9ddff8d2363afe163ec536c4bef9b2ecd45cff2250c0036bb1eab847190fe09d0c332fad03bc62236b7b2c84257f544c37f532cd7b9cd6f1b5c4abd7c6fce6262099041bafb0664671a2837c9a918bfad7f14a2f5237d2e7d696a9356ef9674072a50931c1202628f8302560c3d141e7e93e6e708ab10f3fa33874481b8843b4f0c615fcfa4b738cbd51b75a55bc4949a2b3a147f6709399328e51a867d21daf9163c58fe480267b97c81e1f4b064d4256eb2870cb6f5366bf6d2a5ae864c681a9d812ac03fa943bd04945c0e88aec9b1ff4cf425e5c17365db048841026fa17e18addf22a099debdd5bd463172cd9bd3b8f672b25ab7d05aa30eae86322827f2b8b27d55bf6eaca9be58e12f9fd0ab05d45f6fc47fd5922933a3f16b25b8a05e0f0a7644821ac25c5ff8bed461c2420f4130ae85837eeb5d37d068f01f7be4c0f2baa0d5834b4314be260be0dadd8640198c9a6d1da3d1f7723a2756ff28eada35488c2879e2b9b9a60249c07f6260ac40d38fc93b257610b3aa598bff44b4a3e0a93cb6d17af8b4e2b6e7c6a1e3682b525e1e37a191af3fb7ebe5d7915c64d83b4638e00f47c25604194905e4b5fcd79f3d7c5ca01566ce1bb15ea1773b0abf4d236cfcc63fd6d3feca55e21e872e262af6d4e5276b4ea41359c54ed52f6f107b59c60359da60ba1a479afb66cd67231d8271b60b0af9a749d6ced0e896b28f08f856098e1fc8a7bb6983604bd246bfed0d6938ab65af790790c1e508b1269e4da2f9490c9356ba2e9fbfff3b4e7435e04daef74585104aaba42c36af765589db165bec2a9e61c236733bd83ded33e66702ac843b50134f97037d8a6a064a37ddd263667e94d8dcc52bbf003392d82aa26df142caa9eaf129addb45bc1392b3179982a850d0a9da1327800c9b4d285e5bec0fda36ea7c147914fc05ca1cd8e1a592e7bfe56cd74a3c286dc83be3f442831172833cde089bc164cd08e8f2228c55fbc1aaf6eca3f29f28b1f1290f4948bfe7d236979e223e36fdcb2cd4eb7508a77ddcba333d29ffcd352c1753174be53dc4f6cc2ee5b806ca9a4d9f3ab3a6c050450f99b322edcdadfcf7be1a7ef72d3e3de8ebcb15f6491b803a8945aae411cd0282f67244b4408bfd95d0d6101faa78c985f924a819e4a1eb064503aa1e3297af2e8dd69bed43e2ab149ffc94db89bf4b46e4a8ad26984c8b2293d811f62c25a7aea93726bb9de4b231034255f4b8de6b021551c164448c9a7a71506983e8cc5b88b5b1341009b103f600aa30c5ab9038189957733ff6c29bc0a743802721d4c4920334dd1f9ac8169f6fc73d806a67ef13782d307e5d5a0e06142ef5274e881a95926dfd50453bcf909a6b2ee80c28ab2571f21d249a87fb6f19b9c57f444c50aae9a47741af88d9125943c38df904e43d73622131ca7a75841c261a40274ca871a2af05ca30a722042dcaad5f2d2943c0cb74f312b96ff01df629240b4279f653957b9c5ae611537ba34c1a9f343b3e8cd1f6b4637b79579b88940bb75c56573c813673f61251abc3c1d66671255d085a7688fac38e7f0b97284fc9d23c2a357b7da65acacb252bc2ee94edb3f2826ce574b7ff85541b216d855f98596449e2256820cb250bf6b35c91914a1d6d816e0fe0ed1ea6436a8ad66169ce9687f79028d0a8118846085c6ffb5bd120d60cd5aaa13c010d099590651e59f3b11e77bfbaac4b842c7f20755c72d958c63ac9aea73f6c9ddd19cc9d0fc2fe0238b157cf707620277c0e3150f348b8ec5b86378268d39f25044619d4f032935640b82bfa431e30ca2d12767a144862818a92c1382234bb4528dffbdf9861a643e5a4ce9405d00376a699ab2a74df7bae6c6f44c64b6387da659d4f041028e1d7923cb786f94a8c603a2342c948154a38da56bed04fc0834f4fd7a2d869d0034ad5bb646e6f4141b8928c3561199f8f430949e86154997fb68b965276e5604e23a4560432dc340ba7d3057425d10d7364039bf65ad4a3a3c1871701592d627ea4182891f681f709db36dd07398d3a5419990a8dc2e2f3d02c6fdaf31a79e0939f76ba614e4470095ff0b93375a5e2f1108157de56d995f1a0a2ea346e5492b6350f644d087cd906dcf93675d287e1737269f84212fb51f67238774cb3f188508a26d9550f53fdd8a84c6bc91c53fda2d5179b71dc17f20ce4501929edca6082dc7487d850945ece3eb33902212741910ddd4efca96d943a931658e59b1273f10ce92d4442a0e64c54e841007dcf4fca029505a3b7ca51fc425e16fbd5d509d07d69850360f56070e717b3c0197ab4fc27c202c009650e6757bcab32c1a0d5ffcac162b05377a2a04594512ae8552ab1c9e59cae30787b22443b8f6c07e8497430b8bccaee901f0be41a0366da8646f4ecdb2536e393bd60507613bb4d4cb4c2b9454f64c61ac98139d42eb5d637281ad3448b8d210f4b6524d1b9533300f5f8e2161ffa2355c0d41853839ee3f13cd6c091278240f19ebea40f4ff59cf0af444a40ae7922ad7271becddfb8590455d6ecb8b13623b7d22954421b6a96f4784fe5a943174f1ef7b6a5db0669cd2ff45b68c6553e8403d53455f4e9468c1c645c1ada9ffd25de6f8785e0004826d4da8728c9987538964e4992ac3d7df50f095f0635a50b5089a778a356efddd8c1644e91582a259b109cf46ecb4f5ac189ad052edf57d0dab23558b8fe879381cee77ac7b1d57e0c23cd540cadd89c461e61aff870762c3e4de608cbe342fad0556174096b0ca30d48e2f095695b7cc95ccf386a469fe88f6774607e4c53e03131ba8f771d86015d1b9733f21e459d89569b1328605b725fc28a979bc72c7100ab81690f7cfc54fe039a195c66bf11ce736f061e313e718c1721f2d2c604ef55e6c25858d5ab9b67d1f71432f61d0f1d902a52ee7b1c07e46e07a500191aeaaa430de1ab3608a0c91561106aaad7d5df597a063d430dc9f9581a0020990d55fb35ac222ae6d0c84b3e826e110d6e8e0b5ea675d487a9f547f12d1022a5c31c19481944f1e81509be6bcd29d05677c04354c5fdf9056799e89948d8d52e030660eaf0cac281647d599e91ae04d2db15d98d71fdc437b0f650eed3eeec764a1992abad409fbb55949b39b1bfe458304deaf7154b0f13603993d9f4abac4e925fdde6d07e3f98082284582fb9931a1a5b55f36ce92d2a562ae1cb4ee65ce45dc68268479752b02045f2ddc4b380c6a149414f807d6dfb13e41f78ccebbdbbb9554bcd64be663193e630f4232605c230975e7d947731ae4b389b61fcd25ad1042f804aa63994b31c1cf25fb3224c1fabf4297d9ae4b9f5e6418b9d55255646bec6ce11ab5a50b31ce7b418086df1268e481b953fb4bea455202ce2574c991977f42d9aa3564c339308cadfa532886216d385ee017c3795445f10151f1addb4df42b0f3f36076f6164963aea5bafc18304064683346201bc1a6bead9297059915b39b2b85d4cfdb15927409bdf8e023c762526b46e2ba440085a700a90eada594095b7e521af9e7508fab63ed98f6335e4c887feb188bc7a7388a62acf1afa1114aba1d9d69e561872395ced1484fb67cdaf1c1c9bd673304d7fa8373f7412a536d17ae588a937a5efe4e05336c5da92163cae9e7b60528198490363428169b7201a28a5d37c10e66e3580feb5a612fa15396436c164e15dd3c304cadc6e4f11a5eac2ab419bb1ff088d176e74b274088faecab1bcdd24e5c7735863624d711c501013b0cdc2e306bbb529ea0e1ecf95baf320d4c9a5312f0d0cdbdb99a5235e6f9f8399b55e196fe5405d6e63f37cd1ae20dae6f0de8f62200f731f86c990221e27dd0db5c79a863946d765a96293046a83e0118f3c46e0722f15b8575be0a101cb431930657437942ebce52e2be21c664013da5d49a5e0bac222f10c72ae6fa78cd413e3707266ed7aa205f916c77d0b52b5782a4664b5918916be5baad76d6e8f4bdd9e0fa5c5ec0f9a79589b6a881550ce237525f51edfbdae6b6a249362f1a696fc18e0ab9ac8ff0a02dbcf755dfcf8488b4b70d4bcaecbe63ec226027982c23878d0735af1000ed9d8441d408e90b111a39c28b92f4875dfc3811806713d835ba18b14ba8bd567f646fab48f9b756a2eb92f9a1ff315f35bd765d902d2ac07507bdd848ce9b15a31f147511f4e666e68d4ee633219fe602611f2d7f310f2b8664ca622fd61c1a87db9aaddbd8f8a68e55e2dbb110e25d8ec192b594334c87d12ed3eb17df55b0332c2c438b67d440512ef90c0b47ed8c06143a0579f8c503f248629982890ff826b7ce9d3bdee5a1f1c5dcb7a7803bba87d09f275501717086a133d913b7ff948936a2016b6d04c6f16e460dbd6965e509f519a91bf46cbda32b82657acf5a529479a846bcdadfb00562e806daa1d24c0ab99269f2548769c6ea8809debe3366771af87b392d7cb7a6025202da8713c03ff3a6973b43419918fb2c717c91125d0c66d66a5bdd8e030dc46017567490f6bad46ff5a6743bb2ac638a3323e696baba58ee14c9476d4728002f215ee8ed2da6aaed2fe787efde22c49cf60754cefd111b6cc68f884e516b1dfd8343d31f93501c28d4fab9094f0d8a2f3d0178161801891eeb57bb4a44db051fb4cc16021d100928d0c85d2713be6a2be0fd1458d6b25fe72a30ddac7782d87abdeae4e2b33868c026b669a55af0fb8c9a34a97b278954f44167fe14ab33f6d9d7d50b60fbe0ade21ea1978e8421160bdf898f08d4fdec2289b05ac7d1cd31f7ba9a2b283bacb1f54566d97fb018b1707a480f19cc1a0c916ef8a94752ea460261877a3035da3d894c7665d2dbb7311e925ae8cd5bbed198b81c020b13ad0191a3411e0bc10342035a30ea172e78f16955579206e4e4b0189e9903bdadcbff1309059b35b6e6785aad8b1f957d6a6225d58a31ed8bdfd2716a7e9702cfec1acd91f0a594c88d7e9eec057a4c94c8469d1bbd0b8f9f435f00aa0259fe1076719d638bf9301e4d7c9b494915c10a08d1ae5b9773585f6e8426668d6ac09ee935ceb4eb9b1df29e893295f755b45c84480e38c87f6acc8b593657ec3e0f51081bb8537cedf4e16bfce18c7bfd7ae089e3c764584993b9900be61684d22959975fd7003f1161c5a3cde139771239cda9ef83419501d5065146b460259c758b29a2cf27181de23ad2903d0bde14ecafd99a4b03379756a496be0523ad417780309a5349de66d55ad23bd9d2e0fe766be8be27424a97823398bcefdaa1d09ed3008364a8a5fd5544f9698f848a3084f4f4590d00e8360a35cf1d362e56aeaa2d71696fc47c7208e7ed476a11ed0cfb5ae20b7836275845fd00c3fe30a66e6c79992542d1c44643516b3160acefe12aaeb10d214fe730de30dff46aaf1959d6f6fe48dcd3a0be50dfd0ee209dacb1f8515fb79bd38f2a6fe8aadb4a658c94b99ae904f882bedf035b30cf8e2b664465644383f99ba25ed957c7ab3a0386f4563ded78189ebd3d1a371de50df988b13e17db1c2b93c9fa0f50d637f4ed50b9af5fad54186115cddeeaad9c4c23d1153f4ddc910aba8dcdc63500f8a30b0819d7827d48a00388362661f131de545ace8f11c5a04af2942ab7866ce216b0954a1233470709c0e05e37568a10266a644fd3161de09451d831c3e7468b05c78a6551ad3cbf056b25c266d8c4c2e5a06379cfb1cfc2ed3ada60dc17e24f94b3b517a7b3474602c6d93dca86aeafcbdc5dccef235a5b3cf095d44fb3818becc68764c0e79baaed7032f8c86479413da0ccf32a1869d47c94f267decad843de4a2bc1cf17e1e51016c93cac69997e14a26cda8deab7d852b9f3ca4f7199a2411871b6335d697e3e89261360061a7d9ab7b6fe8fd395819432ae2e4c2d577c3c0b8b97326aac0568d1503aeb395a60c7979b0e3888ad01c554bb66e505b5fc563d6377fa9700ce2a39bdef9d7b09fe506da41e170fbbcc8cea4601f3ebd177ffbde73c10ebba1423a91877743ac7ec059b009b2e9f6415dd4d44ba6bc543732fbb378f311bd1b3027e34be323e0c601e31d0907bd9a12b11c5171a11af7852a4f94ac3fd2f3255308128abcdf611f5c3644b1b7281e9b41caa654865410fa376ac3beebfd7ce61de80d56a81da98d8610c53de5cebfb610d7d547b84c9ec37eba1ec413a506a5506d8e4dd35bd5abc03d4c9fceb1e94652a0f810744aae0a29f9cf831b8ebdc59ae8160485fe1778498522edd6d9a18ea54f2630bd076e885b69407527f3f10f0b494b29d25dd7e930329ae2834520f338dfaa47d3f2001887dd3d12a3acb633e2d7558d103bb4370839af49ddda6c12cd977c6aa2e2355b8b1fdf0c124ffba103415549662ac24b32a083d452b762d4ae2416c439eb92a787f7b7521046916077c5933f90ed1c0d33bf98cec56801442fcdbe65b55d2eb2310bcc59aa71721795302ef011a8a7520932566bba572175030b5bc716b370d9a198a8399880c59fcb6b55aef4d773524f36a28d7f11dd3862abf058f245d3b508452712f7d72f1cf2ca5f6c29c386995305bebc8782079fc709f1061e4b4fee4c4a9f713fcddb9db71716dc431db63b76096f354a0d56803802f8ff29905a58110297e167cd88dd8f4b0409487f9bc26510fc48829817e8e1c4890e4186a2bda35664c88e7ba12f3ce0b171b5083484c19c3ab530fa184e17dc802120950310c6bcdef24928d138069ab608a640222d7c8526011a781a1a4e718d831ff269647a1e904766dd98498b3f9d469a984ed9e725a2b92d41e2db1429b3b845d2bbc8a37a8d2032952a1f2cc4067581af6d2bcdc44f905732ccbd264dc11732a402d29d187f494a4dec60929e38dcd84f7faf8d3b64055094987def5a5fc230477eb1ad0572cc0f718985ddc8490b1e3f1bd1538f1f72a003308ac8c1df58d2a67fafc31dcd522d490f4bda9484683e4564c0e626730938427fc25461a285cd530b7f27be6baa08f223f3a5f99e1d7ec8cb8cd56e40b843b209f9e241ff1628a2f908ac3e45e41fa289c3f464c0d5f93075b0feda419ea4d36f5a59353c7a453e554aa811464ddc4b2712b06250a29506d39abb84227ce4b921655297bcffb62841d55876785527d2301bbd63883a9a501e68a2f8a601e42a8d9983d1beb00857c3b5bebe187042efab6d91b5ddb670382fcf469793759edcb40e85f6f56866815aa0d0fcba11fc55f0e363665ff546b3cf6708821c1590048dc6bd8c38089a047567079ef15e4ae1ed89450df0ddd0e6f199caf295f0306cdc2444497b2ce3c8149c39bd2c0593b492928fa2919136400e51cb8c8daac6105b0460e9c49a02b7ea3f539cca2a98ba3f0d482dc2c552ead599cf22e176cd1ce2afadcce43bc7ed4d39b9dfd257798b560a834e6bf41dd061b1319bc33e92d15398584b0615bf59c4361c1be29f892aa2413bef4145c9a62a4603ed33d06de83568bdbd4e60e15286227f1a3df7830e9b57aa0449b2d365e90a3ce8e34bd137bc0fe3385e285a144b8c83ccb312d48dcd074e2941eaea5b6ef5479f448dbb4e6f12a6cb207c8aeef1368028e6ab837ae18af4a1c71539320350ee388f1782d3c6b4beeb9e7910b56b65414ed9a8af1560387fbf494330fa6600bcaa93e2fa8800dc8030da21527a9d3ce85d67d789df4085431881d4e4bda613c7d954c4df91f3053eb4186f4a1bf7c66252a6dddfa8c34639f1a742cdea0c05ecea5d0e598c16dca108c98cade11a1be792578bec2308fea40fbbc01888c81b4110299ca7c00fb050924c2fad926eda6eda4070e1201843675ef7c279cb5b4023996fec61c0909a0f50492dc924bf0e517075337cdf4c39c719276a72776f4fd0a1b3f14a15e3d8d97be1e4d1f246f54ca4634f25220d8f9d4c2f3124a3ed55510444dbad33f32f6a6d79b5bb20e12d44ea1ca03e7c97ea510c67641ba0d0648d3efac8da609e514efcc1a89fead14f21d74bbf47b313e812799eaae41b6d37a7af18c4a99d7ce7db9893234b488e84fefb966fbbf14bdc1a4a2ec58bc5b464f716e2dd7be2b47e247b3b1c7d7295a6efc6adbd2cf87487947e8e7beb9b6dae1e1c989c850df38439fdfb19aa3c736b7c65e6772b3241b524240074ee488568485677bdbf73351e77e221e44dc57bfe265775d85bce223f6457c31c004810d1b2de45c6e24b526a3494942c0585f4b284c43db5f3bb6351297f7ed30d2948fa9798285385074a43d07b5cb32f0dace61a0208abeea856522744069899100eeb5d9be56bdff6f59030f09a8f92ae52917c5903abfd2b6d26f7c58725d0da5f69f3be3e59e067d4b3e37d2f89d6474d810ff32d1c0e7107aafd5502ce9787580d41166b9d9149ea736dd167b32d03872c81d67e95c4f3e521be86e0137f605a2d81f78998037ffd2a61e7e3a386cf25f0737dc67a211f96aea0d6438f6402cc831550958b902bd91ea7e731d71dd04fb83a120314fc0c49a0d590bfe0f45da54bc458cb940e9653d21520ff85688878c2fdab712f3fbb6fa483640d94f9c72c3991412788d0f6bfd3402d7237c2cf7d4643f1b2ed8e0c3a9a182a4a9b6a2c78e8fd5cbd1b8f401dbecbaf5872da8794e5408ec709c4c253412bc639d699b091fbb432e8041b37b1d06d11c57e4ab1ee9c780ae3ecb9449968b339fa08125e81b4f52149881db64a406010d8d29287c08f06f29e091c093d7c274822610e11a9e5b1613c0e674d6fc09074e5c9d2d0c80f1bd883cce105d8342a855eecb9d71717cd0e05e5e222b3f82c129096305de0608e46cbc6d67748cbd0cf4654fc817860700e18cb5269b1fe6559a5e6322b232585a88b222f5d61ada73e1cb3429840ff7499f615df7e95da382e1e0e4ec554df4284042b79407f480911b18b6084afcc3c8740909b73a12ecca6fe242e27a2497010c485a64f35aaf7874e26ea032da937add48655b46fae39392223f63c868377711c4ed4dcddf98cd9fb593cb9bccc72973067859a240d33a7e025ba400349038ab936066b1c58f256747a98fa42b5a2972f0ef65663055f25d48789514d0ec6cb2d1847189e836adeb2490a2fb3a3dc19ab6760095f5d425ac34a567dfaeff340123828a0152c143d7a05974478a51d8ffbbdf8d3958a6ef28370ad6bc7f3a04809a78443e9261cdb620d378c248efd98c8a8da98d8d047180679773f786a072a9813308d8b00413a56b7052578389c25c0d4a362ad4034bda4ca423f770116a230a14cd47215ec66ac383098f9e13f4d848399db9c84258533a8b7ab326cd0a41a0ead1404ddc8d7f9964d5695ce5e99f907f0ce2aa28e717f5158d0047f3b86ebcdd02ebbe5879dd8cd27e60691ad8302e913ce7cce2af28dd464cef730947a6833235981e9aa12caec70303c4e52f8c027e934fb92a8a3a55fa0488979ed9947eefb6c39ddf681d64fcc333de4fb6d247bd0c3f9fc6a9c6a4e20d988b417aa4a61cdbd62f79358fb8adfc5633f70cc5d0b05cdb756a2ca866d185d22518579501555a0e47d58660c412d98d249d46e7d270ec7ffa230e93485f88f92c189afd1a8c331a9b1ed08d30de8d7ada17c1742b9db6b488221110f9941367eb8f47105301edf43d830bf1ce932ac4a06c58b8d305850ed48f2419c74f47ba98bb2ce1ec66d5220b8273d4ff2d15dddf3fc36b809e017092b3dbf1700d95d7bb29580272bce9a1642049bc3043db088e7994a90ee6c1064562a2e957349182a96fa0006841063abfccdcaf7ac48955a31828204a193c768bc20884dbdb8334528d8dc3f5fa45206eda2250c5b446600c152c033fab6a617e387c87846ae6e39f91047c9150c86058453a4417f0e6d67aa5442f48d90c93166e94ca0cec8b324fe441c4f7410d8a8dc2848b8c63d75e97ac258a0aefa8a70468f34ae2925e0b8c30575e7ecc0c78707016d712d665fd0bc63ced1b1a7c2a8f97516dea822fb76dc448a5edf2b55ea92f5f3041706f70771a9190c69f2f66c306c1b019bb0d4fd4a78e6a9d73657bb113fc633e28fd77fea7c24c388d046efcc242f98a9a21de0dbaa88d868e2d18ce114c53ba073358495701125780f8b58d312692e601a1d97ffd6c9b9fe4d4bc19fb8b60604054538c3e6adc2d64cce23a5ab10d82f2d594d3ab0e09841b0b112efb19c2a6e3914876a7df40cb65c6e0846d2869ce542e1f25f9d06ffd882efa13e5f482d640c43c5a41e2a12945b3dcd1bac9cf64193d48aec2b63808bdafadcf67c077a9fa4bb543c4b425dc892ed699d73d067b1698b377018e293dde6b0cde9c7057a1ffb0bf4462d42f8ad9f8947739c6fdea9cc79961e1ad08047ae77cb484b07af5207ee121999236d151568cf7212d99876c313072529f7a479e4d73df7a4691d6421b09844dd1a9bf38bcb69ec2b5f8da3fadb44e7d9149de830a5056ae5f2dc1f4b719cb7b9d55fdd3043fdc61722bb8fc790a8527ba7270fc62b61888d215f186b4358a9fe0e34ce7b1608c371adde4412745000c38c844b02514a6bdeecde9c742c356f5e02c5184285f701ad782bd10586b66a07c01341b1b9186e03d5a9b0e4dc32aaea0577412e0e14846bea82a75d5a90cb4efbeb9e7d7a1e55939461df5d983e4e74a02f95b837e395053c2b08b488b82c28abbddf8cc9e98f9aebd4a70416b2734ae739a177315b1073cb46bce298c4fa8e6a82bc7d63a950a7c20074e886b24560eaffbc837d609a43155856200240e213e0c6578fb415ea778228be065bd72eca2582b174e73eb90560a62f6a2f2f38d201b3bba728a171bff676d10480f3a9f31e6dea54353ea41bb24d90e96e929e65aa93dba7e649a2141d9fcfeff3f31be30154f0271ae6b49fd5abdd97199d9d92c0994516412f15a0f6cebbf5e4783e7d739d6c2d279d83d3ae04b9752abd68eb2c6c4e0bba037be2571c57e1e01906fc13619448a0cd3577f7ce3d2a05f7530152553f9ba363e4f2c617168723738ed1e144c09736857e0ecefb1032e17981d30b81f53f3c23e16f98a39eed1f8a62ec943ade2654b91397281dae75f00079b572b29d9cd676b8358c380b384ea2422271cc2936c4eed234594eb43201bb5621938b7dc591ce2b20235f1e8543c1490ace10e414ab60157c6432d88d90be951da002e02a91dd0fe4d064255a8abd2addf55a4f71b641e3120fc4f7a831fc9b0c8a27dcd9fdd12a05c5728cd381ccddebef0296b1cf37fa83ce51af2990f20bb284845141114b7bbaac2fe8e2ea4149d49e071f4da8788ac8ccefb49f260487c81155b45799f3dd00726f74281e95969bb44f6d099fabb875e1a3e92a296efa093f2d44326464ba4652add2662727e0988afd50836e592837f57bd88f2530a20bbc29fc3d08f6c561775498a603e7ba562a06b2e7c7f257d6b21a34eec1df01d465641ce9257145833a419abdbe6b0daac71ba91b40f7f9060e06614819b1d02d3f6f2ff5c02bd0542256ca93a604142a590e812c8919278139692d638323185eafed048c51eb5e1f605e6355d281b45e9cee158081425b81389234dd5889e70a34af4b389d90019fd33ac58c1e1343934302c2ac779223c4a69eb133695f236aa8521097c46d8b43d1412b422a08498606f2bbc6ea4540d841ecba008b7b1aa5e85257d526c12a852883d2bae2d433eb97d116269cfeca700c621ca944953029f55c43547882c2bfed8750478ca257022a9f1e1315ced868237d10901a42c1dcf32b4033e58577e93f3cb2ce51d0e3274669c44f3b8c62a7cfaa00e7acf044573521b2bfdd680deacee54cca27657c5322c717180c4cf19d8e20be524918cb5149a69d6dc37c5a4d6a0039214dca5bddbded62b3e63aaa4bde6fd41626c2566fe11fe633a5694012d6b353f175c9eb19ca1ad25d02aeb9586dae999dd0dedbe4623904f94f5a4c0741e192bc4c294bb651ee3d7310d99323537924296ea8e18f8d9616b94e009a07e6e4ebe59281ed31f6f08164b9ab9ddcb9570e3423d81e3a6e4faea3520d3f461cf4767890701b72ef60b237dbe061ccce5b35555088fbcf534febf62b357809990d5db3250a471962287b122ed40c6a0d9e71ea82c98a91cd7bf0d3c4d118a8250c4d1c5987c4581a6febe334fb895be2703491befa598047a64ed0aa95d201327f8e124399f5ce00ba7739a7b90d7d9aff7c58e5036032060c970a700d851dc23706dc530f2c140dd8bbb56072a4453aed64ba043db8ac739cb987e064535c761801d30d58c77810def7bcedd4904a2252c94ec37a020f5ffe87aaf872d8f6bd0e2470af0b4594823e6a7643e291024c3978b8ad40f8963670091493a660937294efa581ca8c8c5684339a09357a69f9bc0baa4649c974234d77222c8e3b76fc2ab08b6b5c4267ab6697d120ff8a635830324149bf55fc705fc09f8e4905e1c5071af1f6a56478bf4f15001e39a1b9c128ac8f38719750e0ef800b2d8dcdcb391ea9973a0f2be11408a1e2c63b84d178b23e249289cdabc22171cf505f9d130401a12d1f87977027da0ab7316ded4b0512fae9d927e9621c5c5b61c9928a42286506869b360665fdb5d8efc1c1974d861e59600e45cadff72ccdc3a97e2ffcb918ac0f3a7d0fc0a822959b047d253e044b464ebb6ca1d0812466a784111608ab4d6ed7b6b2f62a5a948d276318a8e71323f18bf4b20481ac87081105e5b4c7ec85cb0740fbec8161feea796c045c2df71f8744501588fbd535195b5745ae74ca43058c35db926bf634f37a51a655db4b7bbe9de709eaf5b9c800e6c2a5292eec9d91562718905d7021b8fee40c2c7261cf3ab0032f811c6473d603ac2b46e26c19ee9e6a2947f674d9e409e5fe482fbf9e06a8b2cfea35fa3ea11a6ca73a1b63ec8cfafc0f27f1171dcb0df4f8b2aab849e3ac57f08cfb1b25a4ae2a9e822bf6742771c2878c8ff70abde039a7c47d69c49b2ea781efbc70eea9d7bf8eb4fd5addf64e901f3d0d43bdee6fd5182214109961f297f4e7300cd0cf52b92174227b917d23a7138cb595254849a700fab5d105b7659f40cd262441f98ea0f6dd8fd78b53f32ae6fbf04f988a568190f22597a6ffabf185e125b0a319e249a5e5c683a34acb214e1751c57af9692a1c2aa6f16227ef263ab41c9fcdb461e7ec25402bdb81d462b1ca3d4f8e2071a66a481908da9df287bb73d55e6734504a6cdd67cd22b96a4a13088a7af58abdfd4c900d1f12e11519a5a2f8b7b36e46a0c5fd34d954fa7ac0d1328d6a3bca429da0c86cfe7e1b808d3c08bdf50e964cb14bad649e8834916d583e4ede40d1203d6b8bede74e934ac8c3fa8ca1d44573e2efb031a0698bedea92f3ded65c7e5917985540e95e503e27b2b4f508f4668ea2b8e2ebb411c8845e9d34598495854f992753cde7636ea726d4c86630eb55aeeb64b834ce1bf6649f56b593bdf92337ca9c33d51533e333e18046a5e6d8935d3740d5040a17536d5c4d6c906d66fe98ccd872edb95cc836c1b79288d51ccaac765be4f6459537485f7c3862f6c40bb9aeeee91724e23357bcbbd4faba9402f4ab4b4643172c8c3c1aa6190757e8078d3235168eaf6be22f5d0c56381909b1f5b52b1c77f52423d32742dc1ee0221c1941b4840f91224bdd5a309454c274783d3887f86f6b551bb1d118742d8f015e9cc604d5499e04dd1117123e2bcdc17f7c13c493355c517a5237f7c2f7c094008da4b01c2722594451d901bfa24a3245a6a6ca254257be50dc176493dd90ca686fec55b599a4f8cdadd0411bb23628d0fabb30ccb766eb78020b42bfd53044551b9c6869654dc48ac4e8009bd931b697888fc1cd93750f63dec02f454f84d1158a958433de339a90d23d5b84f3fd845c2671d3015fc910800aae14fb8ee77ff854fd9d7b612a194ec1f837eb3e88dc524c20a483cebf9d5bce81cde1c7157f9dfc429a32c63a61ad94851a28cceebb0be32062fe9f340c9636d54f328de21bbf354ce014b9f6019ae16e361c2b36bfc2008d8e21da688bf12c56d4b8a7b0cd6b3250c35a53c8b96d05093ddf9df547466cb4a1ff6fc383f4fcc5fa081af5a79fd68749c2d56ab3900ab53c7386867815ed5ce1dc3a0555730efc0110910a21ab2ce7f5d0c5213244f0a7a2d09335e842b2650f23108d1d582e004aeec5f3fdcfe5816693de0b43746e0338215599c82c510a97d7b6d4801b76d55f7dba28f936297a3a8679707ac0c2b66cfe1cd0b3df50e4f8c6398097b78833f061557849ae3b6a723c4088eb57eee80069782d66cc158fd9b87a7328b8762adaf633aaf73f53328e6d6ce5451c37a7e4f67a215f60a6d41e973e895de8afbe1f86212dc104cebf1150912cbc05cb65b2e89e407bdd4596932f04d724bf2f326b00ba31b01cfcdb6d2ec2f060a30125dd250c7114641235ce85344d37441f421106ce88b4c1e181f2c8c9193ee968b369c40ea369baf0b48541c3852992327879cf6414b9ae9865d1ccecffb7cbacc360f693d2ce8e0827619889f73f6dc2ab629cf98d5224ad5e1f11f4cc10874a6b85c1586308a4fef486ca092c569027bc4693d76c616655f6e69c85892633df24ceb124dce334a2701859bf771091b7a9c60af8c4bf3060e120b7824e8791a3ab75063b360d307976eab29a22b027f905b2fce02069070de1ca8acb816f352c62e7ff2068e57a4106a6a5f416e6fcb6d63a8e78e0d89790f30879fc96a0b711e98b4c24210174522a076f6f4d34c0f5e87569ec9823da547063ef0bfc023690ef2de0359c5c10f72f68944baf21614c8244b6d327adc2c5dde567094a0e864d7e7f16367fa6d4450e080de2fe724010097ae5b91bba4f03b17b059d8f0b1cb8c08a36b00c11a2607c67af2c8b623c44b0738c98cfb1f49a41293c968b79a88ad3f6979adb7faeb584cbdd64ce0e2a86299c37995fd1004a477e22e112fca54a2497f5f8b53ab671a312cd250de6a9b7390463b2fc740bcc326ac29b837c8623a70a29e340dccc0a39c1c2c25af5d25d3d0537ddb4212fb55dac0c14ab0404260d60ca53e7db7bb4761ecf898078d86df602d6c816b8c8a930f0baba9b3220ad5ce418569518f45e9bae8e94c0deae6c9bdd31219649e25accc3b91ffee7c455f88af4ba88066539f968702906f74e670ef1273b74770cb47af75af202c0b1859541585036493bdd79db7ad1d0416481604b7814b3198cee24f900bb59c1e48645f117d2f2248971821950a0c75ff38cee086621c31c2d499d571ab2e947031d82e2564fac92dec4462d3dbd303aa03ae2ff1bdd91798f5d48cf68620d3c6ce7412d54a928a7daac937d70ad43063c78dd9d9f5ef1a9e60312b5f561481b021102ef371d4832082792fbc6afc37de3c954881672ff39c578d63c86cd3a4e5a841b003e65a9416f723fa8a499e56ce44df44095bd0aa738ee99675c56b98770430d16a30e9bc5f80a7581014a43f9371418d78a8f5a4c994f4ecef1a3ac7b6e7109b57eff9938895f9b203aeba58f9a4c0612c349510dd91f1854d4d8205f7c60864207ba4f9dc082f626fd18187a0935f1bcc422ddbb05ab2fa2aca7cf3645a91861010925209e6330258b33a054e3cd591871c413111dcfdee71d43faffa1e6b33b5e90de2e1e3f5d4c20b175739eea7787c667ed2800bdc8bb4633fcc4aad77b4cf031c1f2b9b790da2d2791816b2781be77ee73e2119044914e6fa934b3e9b635b5f80f28eef14f138763c76c6f7c013b7593cfa967d5ea5f1811b6c9fbadfcb5a654ec8996e0ed9a470354e2341a134a61854010f9e30412e59b5a2339e154c7f4935141a705faa58ce7a9be08633df21754b00cb7e297b919f6aa4fa704b050278c457440b187a14ce7ef5fdc83fdf0e97de1838871c18580dc0b5747214b354878a23b9729416404798453fa721817e19bf20689020a44e139cad0947866847a6369bea7b9aa5c54ddf10e668d311ab4ca94a0de82a6a6209e18c8a2e010f09be64f9276740d66e45916219e3cf5df6bcbb0e73d75aef789478cbe93cd0337656258bf3e3ec34138bce746840f77db75aa5a86701dda9c6f98b0fdee31044522a26172cb42e5f685f759f60ab90810b82c9ea7716f7ed3d8078cbf2f5878db9e16a5cd69ac240ebc99b17dc37b7b1e3bf25065fb8d735d3f4c665283189fb0a7bd373da0f1c9d70e444cf311c2ae5e032c6d63e9266710e33ad7d1724bf7fbd7df4950a0196d9b6558a1bd24aa28a4dcc41b4049ed4bb0259bd4444372d26163be1fc56f029ddd24ced93a441404fa523fa9592052ba92160366452bf55cb0e97813116ee22bc6c94bffca56f16e5559efa5cfabf4b3efa637db33f8ba01079c049b59dc1cccd1018818e49a4e6dd915850f970da4c33e81b4a15b2df6fe64fbdb6eb2112292882412d9dd3b670ae809490af897e8713b8da7daf36c8479d4475bfc6c6824b9e96d364b6f6cec8fa8e538f9c4cb1177d9e24711b2ce84ec3599bbcc379f79759a8cbab18a6ca5525765d6d6c5548c5d0a8c38c2977d747bdfe2b6594ab9544ab3b667c78f60c411ec33ac7f48c1487447ef199c37194e1eac3f248aca4698107fe4e9eeb52be276d45f77ee1c77da01e12896a5a48a0759c4ec6ff8bac11395ddb3e12b4b1afb064e36e8f24492cd6decb346d9f4892d1b68c3d7134eb6135f9cb8b25f4e98d9777ba74f4ebbaab2fb8cee3bb5b038971514f3678cd709d976e47e313831ecf489eb806097fb2a67c7b2fc0d4b27e7068ecb2ec8e6e91b8336a63f11eef5eebdf7685bddfb39e3bd79ef8ffac8dc47de371cefdb731c47e7a04f38f42782d30dfdb5b0c8d1dfb9e8684cdf680cc3f4f661ff8a44ecea230f495e782bf7617a4a25579cf14bda7bd234f6278f248fe665d858fe26d1dc5ea4f1979a99fb8b459fdc933e8db9ffb86799dedec3937152da2ef273b303b61afb7cf68fd34e2d2c7656b30c63f912b0b303ecec5fcf4f369fb11b14364dc7cc6bfde55c3e33623b944dbe8ddf0b3b1488e6bba279ec62e208d8a38edf76546702c6ba97f527c2750e8d43dfd0d1f1725e67fedb3486bdeb4c983fbafa8b5c57c4ed1967fc6603bb0df69ac389c2de615a47db28ac1c7372a4f203635352311b3b273471325ee5ec3739751bd926d7e4295d9e125445a0b9a67aec4cc00f9a2a115a97da0b82d8b2b67387fb67dba8bf9eadd2471a1d1347c062d498f6fbf8abdd93d9a3fee2d63e2b5b7463285cd53d6df547b74ab3b663cfa6882042abf5af56184fd9b5de63457eb6f7afc8cfb6d9872453b933c1fb91d5cfba226e7b8f53aed8b5c65af557e467dfa3b4f5f40783d2beef3e268e009f368d547e603be5ee4cd33151044cd71bf15464077ca48840546c65832b58b4506a832d5e36a5d4670a9b7dc1b264d30d5f58b234c92beeccf1e1d8572ce6248fc9552136bd22aee0c29dcfe26fb5213c3dcd93ea8db6270cd3aeaaddde95dde8be62a737c2c7be356def747cdab48bdabbe0e4c96ed49dbed66f3752bdfe56b1f9bda6a3938bc9a8e6d8b186b50bb3da9569976b3ba5bf37ca4edf4549bdbba7e3cbb5845d751b1dab68573d8d76a9b4cbd3aeeed57dbbd52e4c9e69647f9f695776ef291da368173d4abbbc8da9d3435c4c46d6b569abf13432afe392a66393a7a626ab1ea72af831ca939d56af9c328d5f98b670884a95529496062569ed93956d49a7c95397e0482153e6b96ba61b27d350c250528e67f20cce53ce99e4eb3061691d6b73305e59b90b1cdd5c07647b2aa5e50fc9cef947f34f3ee12e246988434caaabaecc3552bf6084e33a371ac0710e757e63001a87eb5aec6abadb78771b2ca2ee35ef5ef32fae56f9a3a9afb7389989ea178c7070985e3062dd429d84a35d3867e9e86af3d19112d658438dcd22b2f9fdbe918b7008dee423f1dec6ef6de82331758445745ff3fb1a1dc3dc47341a69eeedaded39bd5cd06244bb988c70ae733879a68e76c5a5c9630500ffa0d3ce3ff814807f71291e27eb3c66239ceb68d78ff39cec521acab9ec5829e81ce72ae8e8b310a3b0f4e150cebcd0c4c949b1ca0f1e9a62d3e49142beba95b6d4b263e1c7d170f2a8f4a73404bba2baa3feba6f3a5e484eee29948e61eeafb58f3211b6b90b200720c79d1c0130ffac91ec4f7e4751234da4f9b4271c7af2443eb13e1fcfcc1e399f974b4c668f1b9f389fef201cba91bb915d37be246bcfc9ae9c2fc9ff75b24be74bf271e064263897fa9b5bea642638ff8dcc04e73890e696cfc94c703413d5474ab052c7806daa8fb2c3262baccf39ad1cd150c290ce6f1ceaed39c7718d898e4ecaf1df18300e34bdf6927c9d7f48d34ac42b2c4f76d3ab298d3d2fafc82cfbb34712ebf0c6710e7b363c843ad739eb1fbea17b2e3479e66f5ce75f0f0f38394f9221e738f452ce718ef31b1ac3974eee52804339dd378f94b0b3e74c43094339a85e92e3377e37fd82dc71498ee7fcb3975696e4388e469a3bea18b09de91f726810e0b83cd49fc51f8eac63c0f6a7b46b8e9e37b40c337bd8c9ca70f254a27aaa8f7e609dfe36edd805f9010470f2c81ffd00829cd7d8a4731d5db35cda9766d8b459190470f244100cc9d17669e7681c9b36ec800cc1c9387a3579e269964b1ba56443e2f0e0c243f8dd451bbea0d9e3f242b3c787e44f7000f431f7c7452c53e2d676b025eca20df8eb69e2cb96efc15265cbffc811e02893502eb62a9c86c4d7acb4561a033c5fb489268268c2c9fe7a74e043056b1bbe9ab081be86acc044174c7871824b0d5b986842079f2498486209134664b4d229993001134b30c4daa52030c316b2d5aa47c0998a022c51b0ecfa0bc5880029f0079b5cc059411fd06aab5d85e66b6a8a80720840037fb00908242279481e95053060b2b4eb19007dc0d7ae2b2e76b562d72876fd8d14c334843be69fb01b4ac0f1d662bc5ab564c5b4769bf363070cc3ec4ffbfd5704864121d3df1e959eec2230cc8eda6e1a763f89e50c8b9bd48e28c58d6134c058c54261d579129a0d342b888082224206decc420413229c6c4a978288b035f0411da2467a1d2db02752508b704277aaf090c3dca9520315d82077aad4008711ec54a194d660864de94e15aa9d765785b54267f52a42e8b47067605a6c6070972b32d8362aa84154512285812b55007184173be0620713381304133960228b921eaac4c0561142aba20a94ad4a0eda18585fb9e17a012b034ea8a2c4a31408dc18d888297402a57576f1c0d894ae4ee9c459363da53488884ae910486c4a8768da94061161972063892eda933842dd3e5d28b0b3e333034a777c8cc8b20db0e16b0927fba268963f78d7ae179ad216d563fa8b5b22c92d3fa52d239729289f2370dc1185b8e313c95525b4eced4a58d905d8f09584182131ee69f3bb62fde636728eb8a3ce3df5cb659aa7328e476db151c7d992c495bd85952e7b764434c7ddf1b1ae08758e7b2a7ff4de551aca16772fe59d267faac33780758ce3b277d83d206ed6c7ddd33fa460eea9430eeb50a039ea5cfe5c9b3bd6a1903a667547e98f55a4ba873ad61fabd3442a7c4f43d982dbb5f139d531efb53b216ed651f9439d46ff9082511ae9f49e3e62bdd34738475d48aa3292dca8e3fc711a496e564a3f677642e251971d118ea6b2b5d2af47e9ef86ca960d8c02d6f7b043c1c6267f32ec8b4f947542bed4512e6c6ec60e85cf467f49fbfe6679f1539ad35ffd47650b85aad19fd2bebae6f553764472d7fce61afdc57d31d6dff30f291869e26b192902c14874df7b9f23304b48dc18a5bb47cd228a477ddbdc9748b2756cf852c2cbbe9243e523ae23da3686dcb78bb3d743fcf60ba3140c5f4950b994f33e1894b6779a1f21c94df30f49a29ea21d90237263bb5347e50fc9d6392a759d0c254f774f958fd0d0e818e2c617929edcf8b1131289f037fd71c75eec8a20d65f4465d553797644a8abf4678f83c8ed1d13a13495adee42f2887bbc9e78dbfcd90f896b437a24438a443638b2ebc167082d8286d8893e49f80031848f148c377c59e1617fd6c7072b4da658492203cc9df901892f4150e10303acdaf085041256304c48563aa58f95132cb604d8f0754419bb6ef83a22ccfede83217c1082089f247c5e7044184778e16345067144174778c9b26c5a894d3a9a75cfb2dac8e8ea14f0e9599956019fb254407d1ef5790b5f5d9c366469df5459deb29653c0561ba19ed22ed45dd9e331861b8544b77cd355da646a87ed99b5b388c8ce8ed92d83b295692212881a585ebee7320ae3eea87b94574473d53dee6a221aadd2a977345e4ea5bc73f744b677cc5a8c8f53879bc8c7a2d1442a0d652b4564639497baa9a352fa88751a7d84a3f2581cc62814eadc2f87cf79a8e373a8638f439de3388ee37e6a4061fc8b51a81ba9dfe0501c8ae3baa3bc5c94c22bd9eac1919d152a7ce06226cbb2df8fc8ae199cb1e39ee51a372b05efdd59f074a75920b2eb390de76a489195dc0117263f4a5e672c9729dbfe7279f2daa2691deade2f2ad3dccbdc95d7152a2f28535c99c2088ee6dcb1ae081fc552a13c8f46b3543a9c87facd5feaf00de8aeba90441d760f88287c8fc8461deb50f0ce1da6fee18dd22c152b751658d7390b387e83a5d2ac944e81759d1bb15437c2a18d6cee37b4918d0f3791cd1df5a58ea1bae3fce1d3e81f5230d6dd51faa8bb2a958bac36ea5cfeb02eb2da9da7bf222b8ed334cdfbf5340d750f6b9aa7691a7714d6bc739ea6e51a0d3dcdf3bc6f9aa7fd28413d6ed4397d746fb9cbe50b172bfbe8dea2eeadd743bc7de1b0ad97bfd526b2b9d3a868f48759787b1ee779455638c71037eaa8c75bfddda3b21759600185ba779d7ce3ac8ce35cfe5646ace3d0421395e5e6b244b152b8719d1bddd070eae8ee462c7d395d64b5afb687f80b225264b56358494f8b299abca6b061db0d5f5388006ff89282cc042e94372e31e5b7fff8c0efbceddaefb474f468a9aa69536a52d69e546edd8bca19b6bbba718e70c7dc9ea37540badfaedbb6d32d27dddaf45059ee2ed38c43e5e8c572282f53d9c2027f32abe4258fd67197b272d73295dc29a7e70f29da6f9472b3a754db186737d8dbe196ad17787bdcae0e15b89e62cbc10fef6dabdfb60ec8c64d1226094c3e2449c0563a9247bdc738560af3a8ab20efcda3b412794f2b6d7bede658656f14bbd0cc2cdd24fd72e18efcbfabbf9d7d8fdda3f9b7e2b4cf0eb0f6968e9e1feeaad707491edbab2ac5d1642f73dae6ef7ede723a7a528af1259bdd61be10c20e42a5265bd31e08dadf09bbd347dc8fb220967e9cfe8236f7eccbc08e447634826447d74ff679c2f79b86f01a841aea8fe3f0d6d91f52dd5387a45d401c08c105c01e3d43a09c3e5df0079b668692e7dbd9518a57145b6cd8491f2b4ea8913e64e06ec397145fa470dadfa5944fe8cc36953c62165b7bb65a33fa8870473ca45ad435c36c96ef6dfe8aac36fddd74576d6e2a49b09287cf13fee09ce77216b7cf15f883673224ae1823f7511886481123485c71e92b12b4b3d74fcc6e1d102c6a31bb5624087a4d6c0ca2d8b21f8faeec7996904965cf4701656797b7f9966d5e938fb2d75c95bf2c88d24edd9a61f4526e764084a476e1859cd024ba37375f6516857bf5d801615de7c66fc7d1bcdd38ec80dcc834e87c491cba192b7ff77abfbfab4c7f93e96fec724e8fbad39cdf6ce334e37bad2b9aa7b75d515644732db372be3adc38fa53da38a71adcd115cd7ff7343bd5ac1c1d4469a7ce12b23acd57a7b1ef72cdb5acfacddb7172761d9ab7d4bdfaf36ef3a1be5d69dbe8cf4e1e1a1894f67655feb663f5a97c84d2757ebb9025d7b8b7b3dcedced923619b7b81ebee397b35843aec8581dadfac7541b2cfdface5799a370eeea8d7f251f6a9a96cc9d7d3cfc39d7a23b7268e561eb7594c4905e61120f3d8110b6c43aafe62cc5f6c22672bf1b0e331e8058ddbfa04618b962419ba132005f2d5c313552a5b42137645557f2e561078fa2378aa8fb8f6b785a4ec8e2049328308963684ad1d3f8da041b246721dd48ec48ee362dceeddecdfd936efd97624ea7851478fdd51fbf0007ff069af668f303e42c0f0d662bc5afdad1696f6516f7bb3dbb75f9f27fcc529565056f2c4d5d55bb6d73412ddd8b38c490a63cbad8179a25327b717dc0885290a81511bc0f138e0186fc0d1a70418beb658796da9b285871dcfc933d9d2c296a5fdad342c74f0b477f69c3c6496da870ca6b16aeba305fe562b9d4f2a49d0b4dad94a27bb90cc32ec93ad745cc8322089c411e2ce6e44f2883b3b12922c2e51b9f00318be745065c3570eae6c046cf8cac10836aea134531f329862d1fa6c813fbceb577021fa80e586af1c30914f9e7cc4c0579e9ec68c799aa187bb72ab95db58cd863a1be3bed6c6303ac687cbcfcfd6341d539bb82d1d3966736adb7728d5eccc324c764815367d87521a67a492d208ab27246b0a2e9438d802c5192d62f64541c943fed0272a06d231f4893e4d2ea8f4e9a2cee9d34545a25aac5634537bf953bdd29152876cc9960db363d6034bccc8873b20f01489e2cf66914aa15c150f03b3e2122b26912d192346aba83d70ad5947b624ecaac0c25ec3ea87342fdc34eb4f8698b3f81dd9337f716f1927a5b55ab4519a89d5e70bfe7aa8de628cf160e021df4dd8a98ff712b46bf4f9d2b3c2a2bdab95a6bfbfd5d25f4f0ff65827555a01ee90ff59c23a3e5f62f5c1e92f4c00ae59562b77bd4dcb3e6b0704f58bc2284c2ba6b57a1d77374dbe46292f51713e719ce39c9e7e71dbbb715d7743a18f223b37f987141c7ff39d5d23ce611771223d464f753ddc517f936bb046e5ef1b754353b3e9716ef3d5a9fe50e71e73d60159fdae705638d4014a1b4723cd7d73db65b85737366c32ac895d10d4bd8e76403c8f5aaf3bd67595ee9b21ddb10b82bdfe2bb2136147239553ae22b6e5e86ef77231bef3b0c71c638c18e6b43bef7e76dfed990776f651466de630ad06a6a75d9c47d03a049abd46214955cb198282beb036c829ba00c397962de62be6450f99f3f6d7eea00c20823e3ce823ee2d1b3b4b483c5623f3871d6159c8ea5cd95aa85cda01f1e2cdb4ea98d601f1bee56f66348770c75693bd7fd9bdd4b78c9fe579d869c7f9f3be9d267fdeeb5540bc228dcbf2b59cd4367645d83ffb69b337848638b4e51bb91aedde85644d26f228f6f91dfb6f3ef5ae0b92bddaae68162915d95feff7c2283b1332fbed9dfdacd15ea35942ae77e7356d6a150d71689ee636179a4232eb8ab07f93fec004c203d8dc8cd29fd2468a5b7b7d2a1f798f1be7a3ee9a4db693d2d7ae08d3813ee2ccf61cf41161207261c7a8b11c678dcdadfe28dd73c38e082fc99d75416096a5a5fcc211f8b4b4b49154fb7e2271fbdec8ce7ea4842de10eed48d8de5ed88e8415b9b0f3911c474ad811eed00ebb0628ed4c43a8a31ea9ba138436843a3efbfa4bc3c52e88bdfdbca6f387833be86f07e18ecfdbb2254354a2915d167a7b9a53bf1c77f3ed9bfe90a88e9d09f71bb560c4305c0724da5f0defefb5da3bb5dfec66bf517c19b685d6467ba1696dbc08461c617bec8e5f844fa33fb9bb0b499b6d74fc76ed3f1badddf451777c1b23de365372519bc5db2f9ad95ff62d1fd9cbb023a539bb47ef89d9ba9acc7bf6fb8ffb76fdc5303b3bfed66924b93b8d243f2ed2536b6df7a85901d109fcc5a6262fa08f786b27c6371bfb77b323122d76bef015bbfebbd91f3c5302d4513fb305a08e0a9d56f01235303f4758f9159808c1f8b0060f2598810c7ec0021614c1c5c91548fcc08b2e9aecec8b0b384d8c199a80d8624509bca051c30fd4d882ca0e8c78a2cc17253bfbb26403e30339802ec8444106142808c21939f4408c332a50e3883174c0e2e60b0ea60d255b5c21054c13a62a2c40a60b33388c79a2c2e4c9268c277e86505a81134d40b0a18c13229a28cde01406184ed228a3039430a0c01a183f557e88d8d99901a53b456c4198008c2084b0860e3a685f888082167029c3ca184560a1811d2184102248881dba43a57ee1e454c6174f3f4c9913154e5ba215779e8b2f18576ee6eef74a3aad8d3c3d31acde84a961c520dc1996e98fea3fa2238d7544f1f5db96e31a1bfb16e7943048b6640f367f61fee88630d3c7fa8a75ddbd87a751739ce71d7e72f947ca3d8ffbe1017f3db0076c4291802281c9873009f81063d41adeb9cf1c77e745259cc61a4f1e78a9490dcb853b5766dfaed18c7dcb4653d346f499057a4cbbe21065a580691538dab152c01e6f84e97a267108abf59c4eb2a48421ec4ce250fdbd8cb04aea0d993cf637bb3079ace5b158cfe4b1937ed24b3c79ec65b48fda7e6a973dfd6093b4ffc1031e9a111f0ecd7f3d127b3d7c4a32bf5d88bacc2e2623ee5e69e47d6a57bc77eedd7ff020a3c693273bfda6e73f2c6f6ec3e637b7b9cce88de2297dbca68d562b3d44bb988c6c3c6a57cfe4c96ee337da46761b996ddebbcc6917938bfb764fbb6293c99345cd6997912bb5ebea26f251dec564142f5d4c46dca376b93079b273ef8e7b52874334d0490587620687b60c8750d969b24abbe2d04de2be1de72426234ec78dfb0f1e30f67a75ac3279b21f2dc03ac9c58425d54338d46538c4393971aab22f94507a3fb0866087001cc2ae421caa43536640c0df2d43d7c6a01356316e66fb592ecc6c389160977669dfb46b7be1bb623fa2e0aa8fc0212881be7f48f6cc47368de62311d3476636b3fd91eaa614ab139bc78eb2ecf4b3c66ec2212d26b970436a84cc026faf71d1850b1f367c71e1647ff6c545926d670f97bd8497fa08dece1e59b742122881967cc84e1e7aa882b6934776459bc6471fd4306da7a653c3b8c96903d4a7b463ac97f76a5d8dc13b94add915d54b7d245fa33e92bf7a45b917906c0cd8f16d57245f9f7545a8630ca5574af2488e7de43dc7f60ef78e6c6d9a5623b34dc69387ca861bee2ee2e35fd439f9786b70d024be459db3d101499d090e53a86ce181bd6fd57d28fbd8c9df6823a5f93daac67b69912dfa186514fcc5299ba6452bd14a53bc92e553ca74fc60f30fe9b52bd22ef5a7fde2fba8575d11eabb1a06a55d138fffdac2cc86af2dbebcb6d8b2b5f0a2c5d38ef7f2a87bdf7545f8567ff2f89c0e79140c4adbb5b15e15d5441f88e26bf4470345b6e8553946e9017f9149f4b2297d8f117cc8e6f276dfd35fec5ed555511d82260f3d928f932dea923c92401f30763f30c02ce013260f152234737e60803ff8a2b0e92d481e14678026c960e337b739bdd14b363493bc8dcd6dfc36329440401f92c697f4eaa37b18528f9ad32179a474769ae252f03b2dd42de63829f1b58ee81e3f5e14c517cf1e4356903c28925c6148137c2f8f332a23c97d513ca4fc0105a38ec2871d0a4b944692fae85eae648b9efb8d50248f289247fd21459e939ce4a4e00fbe86481ef4b0eb80c827b2883ceae618621179799c85cc22a8b38ec47d1f519ea73770bbfc030ae6501d10d955aefe8082f5a649cd3e6eab8f2aec50a897c71d10a92d24924f3a50e68fe4e1c6fed12419ecb76f7ac95ed349a004fa3ef9f909b7c6c41212f7b3fc1d21c93db12c8315b284542985c0e8f1a164d8a547bba27aecae4f1e7e4aae6d7f330ed77341ea512c21f4dee9bdc36df391ccdc6b3ef20eb7d0f43e24c9414942aaa2f4a7c4e443291252da052550bd2b7519a004c28712c87b95f7a83c74b1f68ae8b10dd2dd6aee35a32ef391978fb86bef324b083d777a0ecad6b4b6d6cf935505978b1beb340d013854f32c2321493e4a8ba9744da5e48d66499b24978d6692af391cf22ea526539aea577553dfdcd90552ebb3ac6219966159ed82a872964dcfc3ef3a554a95b7e30c4be54f69a7bcfcb9b6f79accc4d2f40b46355f6996a6fd46b3344da4baf6a49adbe8241bb7f9cd935c3634938a49bbf7da15a9687e6972dcaa0ce3539a46438355a97c258af3b08cc3b22cc3320cabaf4a29a93d016567368b5bfe81e6f772b3c33eb93197cb4857947d461ed885a691adebb4631c0ae5e5adc36e6932930fd11ce7cfb531ca238146bb9852773175a7b9762da5fa55e5b85358d5a57e5339ee0e759597daeebdbc9d66630d953fd746927b56bbd56b9a24d2bea5b459dc5bfe010e5dd899a02969bf5d5176ecd7eb3af97b2f5dfbb347923bfb94f6b7891d02d1b485188c1bd35f3c969b1979ecb5c5951d5f7f51174a1e94d61c7f2326257dac4a3bca599d5c282f844e4932cc63b335c33684e9a549a130da86e8675eda8628cdb15baa3a49060a05d44ba92fba88811a499696e6125e5e66b2d0b204e4a8083cf6cf2edaf07d853f1d5b5a1d23dbc6b407e07944a26dcfc3ec816dcf1884c5d251914983bc98a1517b8082c1b4b4a186934a16658ed0440e17cc19b24822f3a205405ecc641165671bbeb2584194f8ccc667750b1ceae99978f9b2af98b2e5575744d9f23a3b5b12c1635dd1a6230c2854dc046b871ad611d5798ba726a590a37a63575435d570c7981f14fce4f07382a01f124829b3f8214116990e3b3b7745921d5e42f04705599609714493306ef0e2822784c852bc10c2194d8af86228f3c289173bcbb29f12d82b3b67c3d70ebcece025e3d08d39c9c20e0242526618b37dec6088001cb287001c4ab2dd2aa9a18421fbed32c3a1ed4a9422ec22a034a45d48c6cd9ac7eeadd1f6bb7d6616e2904d127f5943e2210087e255981c9311f7ecf1469976d11087b2739a491cca22a4927af542f2e69e1c93f0d00bc99f2943f6f435cb7d638efbd609c021ed37564c6693c2683d25f1f23627d92bb1ba86128692b22bc9740d250cc9cb21b2895a895e24e9e2043f4e76764c500269450d4c76baa861c38f09b49f145831841551b42ccb320d66d0f2b8d2c8c6f06a6778dacf22514bb7f6f816910f0fd9f6d0624a56b6bdf183af0db56c7b2e1f99db03ab6df547647317925aa691e8966bfc2cc9b22ae3a7720a6314ea34b7874a43385f9da5b4b367f6579593289311bdcd93f06db4abe6aa5566455456011f7515e8575a27ad926a4e8fca49aba352a039be658adaa8e654db9c4473acb5635902b19367fbd492a4b44b69c8c5645519df6623d5b176290da9aa547166cba7308f3a0bab63cc6281e6f84635da6b346d4473ac5d718826764392a4aefab32c1b91ffe2ce5298c76761656d97a62d9f75796d016cf8eac264c70e082b05d4f18d50c72e9aa33436c2a7da5573acede4b1d9b767f2591e62e3701bdddce6ab0cf38f16dc1bd1e31bcda3fe93047f3f41bb26a96ef3544e5a3df56bb56f1e53f7544e4add86ccecb7da597c9637a619a96e430394c86599d345e4cd696a6e3493bc0d9d9454739adbe8a4d455359a6569342b53920a4159c418638c598c31c61863ccf4c4e2568a6131d68a61316211ab955ec6901d5eeaa884c5e9e5c29d1d49335aeb9c73ce39b38c6a669c70643279b6cb1371411ff45ece1bd1e6d4b2b8946924d84537a02fb0d19c3a8679dd017fb0099f7500fc8baf0f75fdf91588cc1ef99ff0853383809c7e481e3d33c76d7c5e5e813e521fc0a7003e03f09dec72fe6b7db0494e9146ecf97f41fb93518a0465d981c8c03164f34063e0bb923c6e3e7f6fe4d2b6973fa4e0082406fee20b1392587e8102d9ea8dfeac46ca76d475b3915b3579856c04896be94995e8c4dd55cf3c61f24c9e7800e4c824e76865f2c43f3e47c691e1bc91e1e4d1c92c9cecc50be7541346e7cc9ed80d5c7250c68e2f1a31acd61831cc09d7af9ef3b9820b48d0c7f6f921d8e53c91f9132c76a02ff0d7d3f31384946d31712923c12ed94a02773c912d33f14c1ab215737264b2ca677684af189665524a2925a63fa49ac636235b11491a510c50185846f9c24a36125503eb823e3017dc6126b6207bb42079e2aba5e1663348c805e8e31ede62873dadc9935d803be269160aba91ec617482ec718254aa325419c36841b66234027db8e08ef89eb3feb57870b485a0d9e3468ecee1ef0d1c5a76e46e7f2f7694dd3b96bf1332eda50ea5ea96e650f2b0719b9baf50c791a1e459e90c25cf0d8d5ea9f4a7b3ed53fadbd91cddabcfd31ca7c3ca391927cf1e4060e0afc76616462866474d245372e79d827af610d9a25e2884104208213c9d9773880b77bef88261b05a2b952abb2a4dd91586b115868161c20081c1da83c8add530518b1208120322035fb9b4698467ceec6f05f405cbc7ec36bf40b7ac083c83248b9413ac6177cc70dbd963839227864c72b1deef76398ee3907ab6952dbbb7fbead9c35bb94cca6f405e60d435d9e2ce5d1e95b9fcd9a346f270af5a6b179abf79cb5f8da663f6229522681fd14349bf1e0d95e9bc0ed983e618d61d431d8ba1ee23949d3d54c76832943cf7febab0b5f8691ad5a219a52f5bdabd2eaff28e953cda53f9e64fc7d6ec61b769da5e6862b9eaa951728631c0ce0e03e8280036ba566a02600350fd51f97a2821a4af32b376b5e2c1f22c4cdbd5048282abb69763dc961e40adb5d6facb161016acf39d56c5e41440cf0d37004109e839408b8762814d59248c52a8d2aeb168ee588fb34e48102519845e5ea2203f8f75422ab0e967fe3451ae972790829a347efeaba7b205299124a2d087d28c5207a57d2b57b1675548083a22ba8fb0df8cab99882e2d499a752870510479da0171c17257e8c35e7635c77d35aec620b785d4cb88b0cf7f714ea69d0fef8ae18eea826cd5b7205b404de00f7ad97585165cc880cc2fc4f81967a400a84b3fe8b2e10b0d34829078e215602a5b700aac7f9897536bfcd9f84486f3ad1ed99ad7215bf34055e0afd5d2d1f313f4185cb803b140b8639e205bf343646bde01b2350fd4057f50cb090e1862b4420e27326e90b871e0799b1b380953d558b65612a8a60658dedaa0862038d9f170070853367c9d99b27f367cfda0ccd63a5848c21d58c265c17c7356731e7e163fc19d9df45808fdac9562194196fdf5ec2da0961624161bbc6cf99fcdc9fe388b691dc9037718503fa9e4114f5f310502c7cb1da79c582764bebe3a65a1342801df0136dc7085633548e9672744a909788492399032dbf1b41322a1943a8201a7e4f1c59c190f4405feec96ab0887e431563b201895672eceea26a24c803b4bb1ea515b55b359f5acaa5f88f6da0686f0b10c6d83e1956274c0adc13728a574c56d202cf85bad74bed3daf1f1324639697a96dc203b1766154ec2449f844962d8b31ae07912dc1486343748dc18c30ce575181e6297c390de0d27619224c8f788526b38c6045c8b21bd7007b3b28561d88198c09fb51caef92a6e7a49e5cc703d3d16a55462398b0513c1851d10a8235b404db8b6e29697a752ff80d188e98a857083c44d5377e22024d9f0050433db001bbe809025d66395ab814052f06777bdcc2fc48dddcac61e336c5a926563fa878cc60c5f40b0b26127332d7112a6f9ec71bfa08f488fbdeeaa8f9ec68dd8bb11e2244c5102c9276182f1300152b25aaf84258f4c488c59f64e48083a229a554a543fbf68821216650f17c22c05011a7c883f10b3e3068a828178c0dff756c38e3a6e88315f01781e1eb993250f5f3ff8b2a16cc5166e06ee85f2c62f79cd9ca41cec4ca072ca5af53c1681a8987352298605524a29e5bc625aa8c10dde16e0ec14cfbb59f85da9d0ac5886552139aa8ea16e3ae7950b630ff318961b9a24ee214f65ab13328f55298fa8c638b850beced852e646208230e2c79a6008f11607108c114c910228410091c60c361a9a88e1258b27747046131a009100880452ced9544512507cb1c60d94e4009d84c8c20564845072260b1a4f303476e0e40b4870d15082860dd68c1143b8679801ca81564a2b174d3421e50614bc5eb082231278c922075d74310111676cb1e9194e0158a3045ea6988122849911ecf81b9dc057992b1bdedbf055260d1d73c0f055468c6ac357192f3bdec289833ce38a192d765f548414835a78c803e530eb9c1506610c210a1aa0714610a6984f2d788205116768c082c3d0d4d4d8734ea012c421ec2f684b26a0d0018a2c6cc0c10d86ca74d1d228638504c478e203174454f18329e8502cb3250b33c8284961811638a8b2449632d47041143b20b103304b38f1414b26843245604d6894794199271bd235d688f409374d5ae604fbeb2181106c9a6468a5b492e141a605218d322348e3876b0619648650cd98c10281cc188c8c19ed4ca5539a6186156640914412643e2e3a20e344162ac400338134a4e8818a22aa7841165efc408b12a5090d3330101bc3a10732579c6146141ecc2053e5a906642ab064054ed830e6890e6c70801252760863c3191a0c41660aab99015a32c3dbf065060accc02196e06600cd0044c3ce0e900828dd39cd992fc40d7dd00d218f17e446ca9047d594523bf79e7149562075d2ec81261e544b9f9cf86087d57e526263d84f98a7294f3ecc566bd7d34927a5b5169133ce6c770ab8e8e2cb181a6465b6b03e18c1be29e3033d410a70700310383011030e810a199cc0075de85004122f509931868c30c694d1e48b0f4498e0e9098d14f0a004a5043d40220a134e3730c11814992dc21873650c97313ad0420b2d88c8c458a15263dff0a08721166841c10d4968610196059401e50b1354a9a12989981eb0f624851562ba886245971296f081921aa450a1c4175cd4f8020c0a66f0c3097c20c649a7f9400a2bc43021c68ab4c2f6200b192f10a0b102272faec8e18a2c3d4003c7982692681201152f3af01004296c0fb4e84197d71417108295304778d1a5890765643248908145cc93234ea001a05bad073bee000a0f2d30838b993286729c44c96203334488a93214bf8a5129c6f84414514411f4050825c8304293810b11fc6083982f960801bde8b203130b72204616598061020393c5061d03e6080b86ca06a6874bc610645801c30232c8786283aa584bc60c592031060f5a30440e5cf01a83899d21e81845e4a035e95cc089918159b28169b28d41c30c4e8820892596941053c419595cd1c41332b8b0f2a5cbfde2c500b4301b182ddc172b5f9c08aa628f68d2c3184db6329cae60f94435acd259b38516a721f18584cdaccd7e94c0dc862f317e9610c306fb83af9f24c45882be86ac204611664419e5ec01f1a41c11232f44db44853c1d0c7b46d8a2b5567d29077f2795717a9f127dd2c4700156c68ab1c40a78041866be58c34a501007d0a8c2cc1445486922822651aeec50a50628510051a5ab01a7f160f52cc3a6ec2c0c2a5804550923092a8c1c6c7eaa683f47801103660b347cc4687fcc006cb823ee8cd08a22c87f19ee903d3adabf7710987b08262184624329e113d9a251a204a12804e415db22c0b4fbc3f312c9e491974df10670c7d402b6e465cc22e39538426c8a22c84b2fb6ac62886d03b843fe8b4cec11d4c0f1557ff148fc086ae020f501a240a814adb4c54d4b32e6d00c0001003314002028140a888422b1502c1c12865ded148009a1ae4266469507b31c874114830800c6184300010400024006888c580184030b8a11ee2968b940770392ae982df38a65c01af9dcbed90e116e2947167cbbff800c1621919eb31bfd26ac969e587c6c52e55b9b561170a7608b0c9f7fbbd188bc154cf992be1ab71074820ffe6694d43548f28e701dc1fc2221b790d6ed5ca39e5e5d6f315c738f9063194029d00ea9ad2e477160c049f029a127dbe8d17606dd40b052548847c14e2bab6e9ce23e9aa6b71ff45edd42388ee72c44c06a4c794a187e009e56719b253e94f64c1e34ddfd78d18e52768c547cc02058e96cbde0305b4e93d08ea1497af560109d4b74833692f903680f302cd7653f29fb6fdc01116c25ad0d1bee49e65d8fa147e899d1c5b3e8759bcdf4536553bbdad201a7b26d1c827fd9145bb5554bbc1d42a2518971f515bc077b8406d0a63a66a1b5aaf4363727b687b0c89b573a718381bd2a0c50a34ac36507727ac7cf54b670bf1dba282fcc702871e846eff600628fa8059dec2412cd4ef147e961ee53bcf144b5b27bc2c757dfa8eb7969e45e0a5a11f851fe3c1ee64cd23b4110e511660d661df61daef6e5825c968b373fa2d092c8687033818b8a2f92390e7aba30c927c55bad23d5abda633e573a684ce9c2e585edf4314e0e34f9616ec76102f2f4c400c705cdc53e61b496275633c350de0f30f31379ca6b9a10cd47a4b7aa7783c41a103ebf116ec7eb07c6fe73b734a8933a71a77d0467d3aa46fa900b3373f3e945862de0e9bc1c4322de237843e76e031b8e53583def47dc166af611c518bc089c721eaa874e8a910dbe3293f390d2dc0eb0c360e1fde1116f95b2d59d358f8e99b7ea7d137acd11630dbe4d44d39a140c97a128cdce99a3af2764ad0b780c5ce4c6beca9dba81251bfb0edd3e8abf288bbe0a795d87b39be773d558cd3475f69412886caaff2cbf7b086f8941d96176706e7aefe74a241ca2ac6dbae623ee528661ad5f8c43e61db4994cf7e08ff6e8051b2ee2f9b5c04cac0289e28ca5ec18362d05aea9cd3cbb8f2c9e1926ca6bf80eb21513216fc90ea4c6cd21178c2adb747ce8ba17f53cb64603d6a84e8dde1e385a36b2aa4bd6f6c8c8c84ade3b4e5c4e3ba13a348ebab430ad565adb61bc6c8e6f54562c2d0c9ef59c4a557eb2f044ac0771f83ba26eb8bb4cb6bd88e1f40a89ae435baafc75237f1e1b4128cf88f6222c7e471def96d7a3d7229b7e004193bd13143fb071e8d369b492741d1d45825795913039948f15ec1446c30c61b3a3d49e7e90e74f5a91304213e7a80de3e544b544aa8146356ebdfa59a6a9ee4fdc1d7ce28f174f0ea0bb42a5a87c178ebcda1048a45eec64ed02df809ff532ea65a878d2e70d75d33be7b802a7189f7c3b0ea02b65d7cb8375b2853146dd5a9878afb93f3ff3a85b047320244a16b138f79819c356098718aa71de02fea86731d99e52c1d17a15c49aaf8cf320c6d59be77c41cd682cead514cc0bfc12304d386ddf1e3397032b0e11727faf331a69b93d03a60bcf09a2bba7945c3391e9be07da9d3c423ee6d88b6f8e856e8d356b15bee320d8e940a18391f82925c0280dfe0fe44ce8099de4c62e855dd6847242370b4f533ab982ecc871f103d95a298f9d7139ddd1f26d607e8501c29baa0033ce62e30d13ac39cf8d6ffbf3a48aefab54d63127d1e5ebb0a5c8845b390430f78506b707d8025f916fa7a244b12e0989c3cb73bdfded0708443420723eee4475c082766ea487acc89bb089af58c9c743bd4f39d1d5f1071dbd3a70cb311ba6763ee45f0bfbd84607cdfae4b6dba655374a08a5fa44fc4085bc5e5b74fff40ac6864e29c218cc0f298278f306bfeec07f835e3b87e62291e8b535fc94b0d37dbd48e7eb1aac045e4f8c1eb86b0779f7829457f328ca1b5becd778c0bcc11a398834e61c47750e91d782b7d8635dde86b30df75e0bbd691c0bc19de32ea511d993ab3e27e4dc6ad18dfa227f1024562dc060fa834d8f622b67367d080d8fed2eac4814c22328da7377616dfbc936b7ec11a0c2258f3bf0a50d02af55799be906736f86e17c0c20c296aaa1a3e783191c93e22fdae58074046d65d1916d24a18587a8d8c2465038470db0ba7a074023f73749f12807833958232d3d663e697353f87677c09789c92b6b9108b847e5fdfe9e52ecba59e177cb8ca9725e067fe127be7a3929898c66ed139bedc44c67a16f0421ace5eb3fc0ec547ef1e326ccad2737225d905797bd81993982e85654b839ef330ebc8b6dca9b9fd505857b8840a5a66422858fc67c9b2cfba35ed68923e15076eccc347196921e5388342c50a01b52259bbb5987d927916cf18464a320c639a7face711016160d8652d5ad2a6518fcd8a0263680c63f09da26605c75de811c97a64cc46fb93061cfde419a5e31143d28f66b1e1ebca380c8209706e30a6805d9914a4f4efb22f1936b4201c9160cdd45d5dee30dd0adcce3c9a24a4aef1db00ddc2388427e7a2290fec4206dc7086d88615523169a53647a9714630163c3d6b3ed63a8d5bc8128ba3588b5559978f982bc1969298b72cc8a804b12720253bcde9d1def5f360eff95bb08b7102fda401707dde9b1df38ff9ff8db3e53aca767e658084c0c3e10316cc99d89a6a45332b1c4110e001e6839905fbe98d4895c034fcdefed0fb054f1483d933cb7e6fc0bc65ea3734d40054fc87afbd9201a4545e5839c84e62e7394a11fccc681062779b99d3fffb0d3d281c75f824b1aac002c7b4dee3cdb1ee9f3b2d30b7bc1b55d4339340815185ca8e6934c812e827acac5fee8aaa4f7d926c9e01311584d941101ef3387fa2e21f469eb462823d211fae463c118eb4dbcf15dca843a3d1155576d030c138a9391eda410da681f3a006a681d8cbb9dacc15259b0e07db48969cff9178c420d8cc747029f1396850bbbc7e5303e77108f6bcc0badcac0bc786a21771327cfd89a7d4b61fb021bb309ec3e84c348b2c90fb3950104123cf89abe001ec5e5fd8661a8b986d329d039cb9af93fa7dd1e193c31ddb53a5df9a16b7e4ba8e56c7cd91c974670cdc48ec0f8461014d11910b864eef43ec091404201c5227c0183f04ff59b43e86b23846840490a7a2f00fcc661b170944a1b247a228e3770e220249487007cfa9de4f841e302e343dd1016d9783cb4d01a2bd3c126f6cbed5fb8a4499e25286d0b9d66fefd6c2cfd224a0b815a1634682824f55beddc7bff24dd8a3066661b74c278225516d91a3af88cc88d4c5abc0df71cdb368ee62b886598b6f9699eeab3627081f57432dc55a6c344c68a2207624b5042647c97e9493acf6f16ab35b81bfd4201a0bf47ed666b371432d0f4e25f5a400afac97c2aebd3f1a810e365e6853164c1fcf7d10c77190bab441af549d37d5dd92ef9dac688822c4fd35815240580afd55d70588a57b911a72458b30d2634b397076fdbc85c36ecd7e0a228c6d23cf8662816eddca1f3a069e44c23a4ba871e8c800006b49d593f320c14e768c65e65bf0d88ce434ad017ea297bd2d32d13649ad9e510eb81a4d2cd1ca4bdbd4c6be72d7f230bb346a15e0620060b42f9c8633a23ad8a3c0d959f8a8aea9ca0c6cd9a61b6850e270e7cb50dd38b9c5c0d2d9232fefe9612b1a1f3f0e5fc01d63ebc372d80e52e888be164c512430566f7e3d92e62dc378e30f713691b4c2b879299695e76d3b829c592225a7b8f4cdd9cf1fff26fea0e38da1728c54eb9d79e8c652ad810c927bb64ddb759f29f9ab5fc2be7b69d50b8d0c006846bc9a4e861331a8cc944ab4aaa3434a90fa354921d6b1b89953ee5b4e1e9f48ca1cfada374f222e95ffe0299725fe20447ffc4c83d6e81e36fe75b9a48640eb42460f4321eec38fdf139e39825fd9d45c914dec32ad46e7c830de96b5198f531ce02b59bcd8c62450d9894e80b194d86aaef7b2b590475c444fc8878403e8a2814dc7b2cc9003e2a9f0a1c7afeae6684a9d334700bc0f54c0c11dd54ba0129ece5107a2ebb9d74bec8daaa6ce39eca3f23a73e55a8d3e2658718400132e7205223a9cc0949dcb2cef5ad3dd0491a52c386f8a52430eebe38cf05522a68ee64ae2f48d73a1ca94dd116ee37b027a6821fb9b8b52c8642145a411da04cf0ca92b00e92986bf784333dc1a6c44bace7da04476bf87cb4b4f7e5fee9ec9d1d890b927200e6c354e26ddcbf6332e8be4e55b46c7a006d3bc4ac417986c08f5b683378be203fa11174c0317d512acdf69f21e60dc9b84f1b13269982f8a8ab791ea139e0863c1ba3df9e244cfb7f9a77252240b7b67a635f465023a979d0fdb48e06538900c1c369a2edf1a1fb1805cebbcf7e40ce349a97f08feb352d74da6d2598cd52bb62b17165fd9faa520228aa7542feaf43ee96fda4061aa53d1a5052a9117ccba8b934fc57e4a95dc72a3df0047ddb27ea6f014fc4ad3fd35b8df696658872a2c94b9dd9f9123bd51767053388796d46b6cc2dc01c7f22b2e39865f8139ea9801625d6af8146543cac25f6912fa09fc43d716bf2a0da800e7e52a1287331a2a7f768831e217f617f9322a6d4dead7e303e4c4ac10c1bfea117028501897e1348df84123a28e4b5b8b9f1a942a89a24121d131ab11ab8671fc0afd9018f2b5b3d82c6120bbbe0d28daa1f54c744310fb0dc86cca45b38a6c0fd596788f6ca02aa0dfc65fb5ec49a455dfe81bc84ba122abdb26e0709d1f36500a9bab4a604fb5f1aeafaade5a68089b463d217405dc0b4852f17e72255bbca4e08bea2b2491c3ef4809d22c687a15790e1cbb00b7a0388cc2ed28679b2dd18878fc8fa1d478c0fe83193e6f0f7897a21d6332ce954437c0ac1f4d72e574c952af63d763e82b6e47e4db1714ecf72759d4aa778073aded9c7399a8c334036f7cd5e3e359270fd7e3364294c9770cbf1307434d285fa11835b83ff738d6f3c0be1b21442ef2bf555ae6d99f0bcb78601b2c3faff51ad0de1f656efd0ac42bc8cacc278b8ccc5b2110ab7afa32afae6c54f3a03e857a3011caeee18cf867bda6516fde0ee6d14137f26f8126f4aafc9d9aede2c4f471e11bd6ae48d2a3e12fd53837c5f0f72565163494587e116ca66c0939e8fd8b79769802e541f616cec6d958eb1b245f86fb85dcda23c663b222bdf5c81509292e5521c5ee6cb965517e6c9a07fda0b2768c84db19c2f540982be12cef661772804bb38863beeb2d1b51235e694d782975cf3008690dbe803d5234fde5975b2a2e689fdfaef1f61eadb1c4e6c16f511a9c5a4ad192366647e60c5f008069e7623001abde309642034370b33899986656b1d48e78c81e6eab3d6f95a524aa7d930c982f06436096286077688224c4cc27eb88bd953e9a72a9e9d9bf0d9655f0113cb2c6cde1e14867e1b6a4d8b88fdc9199759d7ca62bb85aa5468f6bbf83bc58aeeddaa90463787a37ff547fd43700021c822905acfc1e226dc542a60188da032ac02c7835c8cf18ebc8e791ac64d22d50e72c2353f54da7e4c4e6c59b7e91628963564a8ab8d89514c6b6437b97dececa815e38e626f8816d1e7abf5113421aca9aabb853509f49b7b81f0ec7b3b2dcd9c09af284b28a0e07a4afd8c38dd9d604f94f8e50ce06bb51e74dfb421c5eae43350874ae8a0f28de2e905d9231bb0477358b3e4de89b2d918ec623ce71979cb1301a74e0eb067801648e8b3f8d04be990187b29c38ee0c68379d828321d39dd5df856fa16ecd5fbf48f021ef6fa81de461cf443b143fb5db0b81281ba70965025a62f7b6135c908b3d29f207edf2736415b1c0f9791d17591825283a78caf262c2cfef1699e30ba4c04d005c4e0a83389c73033f0488d5263881b90d39218d64dd138af3e1396060956fb5f34a4e832fd2923bac124e27d3893f98f9071d912feb3194c8794f0986d5e475268ce15753654d7a5c42e8f3d7a4e0a398736944ef5819fc9fd126aa7af23c566e2cd8dd287a6cf4681614e7c00cb3a45fe807a37048ad83f42b50c50a47dc8db9ba25038ab56e66afb9c66bf5eb12fa616bb27b3e016e8a6a60eaeca15c1feadcab4a38f83a283ab5831e5c2844bebfd6bc15fea7b10bdad924a36cd7130532305f6e9f68b24d096c3a4f4eaac87f4674ad3028eb3143f3ac25f01c03b0a9cb0a81915ec84eb2664dfd0670a7db7055ed1d799b4609ec2d05147116ed5cb53780e34b791c06ef3736ce2d3d35be2effc33c14cc43dcfd731ad2979a2bbd3b6a4b42f6cc9e0794d5bfbd350ec7a65f3d4afe1750a901db56170fbbd9c89cc73fefa0cb6d88606b5859bfcee19d695c5d45a91117371359bc579b6bb2f0b51dfd3253ba79ed99c4885faa36a213f3fd2b7a00ee5dfebd7b225e3c6e584bc9b9cb421454084d54b0908dc8148aad8b121fcace4af0a129742c6cfc40a8be1fc523cc777515c5fc54cbefb7a90ff9081d63af5bf44bd344b431bc48a760bb242fe4b1345ee90c5303d5453d2c7bb12448fa003240bbdb8f1dd23acafc052d6f2c8a28f8dd5ce7b41a02f58e82bb912d6dffa8eaf917b2b24cdb3eab91678b0c44dfbd00453ebfc322ee3995bec5a7df83f532e90bff7a122a2525edf8f649949f8ac29a62724c615c7b918912a5a70d3683936ce96ec11474a207f54234cdfe43fa77cfeebb1666efb40ba55a18815a284c1c9d80c643c0ef51685ef3a97705e6b50e9df679d91a7f359cb56577402eafc5fb566ed393e4031db8f5dade5fab563c8404ae577f778a10106ac636d241a38615c1c93a0c9141fdd615f820c5455d256b8426b874c21e4633e1968d5c9737e82f3744c758c69267b93c2052b7e237b09664493aa78690334065d7732421c9811c44c27acf50a00087b9f803351961d6072ea0507f2745d4a88ed20ed405cc7c465abec0fd42d440a6a94eb90d698b78b6807b80046564d5bd8a38e420f71434a076a3a10fec61d973b62255cdf1485a421e795a8fb7cb8d11fa2096d61e58603c791ae24f6f63533f4dd26c410ab7d43abdcd85a883d434b5a1cbc2d0b869ccb661d833448c00a695d5ca96e63f5b1f75cd7ed4c0f77c03f632137179b63a2c858ae07089eaca1c4321cf81a30238e07ca0652a97a9de39608c6259c26df1a0210db572ef1df0506f8ff76e0a0077f86cf0b43d2861e9e2405024fd03484d0448747e9dae5a9fd8f5de2d2f5e39046b17789e65326bf0917d41f90867009898f1f72dfd8c8c6eac58cd4ddb3de8972b9040cb4f888025e13ed5c7374bbb3fd85049621304c783822b676a0cd0ff007f28446bc53a550ba3008496c345df6d78b0fd8ce89c4d1ba02b0ce41cca410d1203b0ccf5cd34d74ec61007b0f556834a2fd4c7fe436dc98c5aaa1db758bc4d15d772da40d6fd043badef544a83463271a44110484d275c8aab553528e16125f106351ad2e6fe74c50fc591ff5132291e62a058c0a45e3e219e8edc4124a6e65c7c9d5a9c6056e3f78ebbefc2dad8027fec51de0e5d8d4577bf5c27d84c18b211870c0f2f193083d272f82ce47da66fe35f7ea7378eef3c39bbc0d4e05eec9a4da4b0c26b686e5af375114c04f5b579e952b237854c81ccc6ea8fb976082ba6d122334473a995b8569725f48a8c68d2c9113a7f0a4c248639f9cf7b884e0ef61a0d0e1f59552766c67ce034209ce0da17500aaff1e320ddb55c680d1bca118c16cd18af20bd17225a393dcfaa41bbfe4095a5daf4057ef32075c5982e4e8a7073931cdc8ca58cf2cba70822ed2ddd69b9b65a2ca70baa4da579802620fcd28104e246b4c6cd526453d25af386ce19e931d44e5140921fe4989c85b92ea8b20e344a41b2573c56fc6dcd4e66f49b2edefcf39a5579fa1861f129df4b0b07c833a922fc86675b46dd549cf5f1e790bf0ef0dcdb37d773ab4914651029acfc6c045b962cf3815acebc5802765ebdd7110f36f645cc0e5868ee16c2a3734f6fdef511d34da9a187483db05880f41608c8ee58247dd89c7e38a5a9463c4b798b182148b524b69ddc1631fcadf7935082f0a3da49f589a2b3f62e5dee3a508e744122f3c3c863785adfbaa50f3c448827f2c93afe028b43d48a93d0a5b3bf83de34f0041a5a16efc89e4aae8e6a1b5c6d09ad8232d4faf475c2a02b1219e917eb1028f589f7f9c881c48b6d79bb8d9c14af6dacecfb04a4467b9c710fe5dd795e9dd8416b29211def557e3b71bf1de9d090d4c307ec8c509970359cd77cd7c546a6842fa4bc1272da3ecefaa86136eeecdaa030ac6f2537d0b4171b14e2335382321c90482cf072c1d641710e5f3390d7841bf066877a8ff7e1c894d2bba8e6bfe087801680cc76655601f870883e951fa1e088577c79e3df593869a645fc968330f599842228323ef9e6f6790856354967441631583141461834305d2dfad0131c1a32e5c2dd9b22bd8d961ebc3838bfe43826172d2fea6f94db69f090cfe5c46add8da162b029ac98628f2d6f69a93921e8c0506d7166949c8e501ba2e7b4606035c13c6df3e322b96406917f98960c921ffb6f2a1b47e17d99846ac7fe07d3a78a293a51861cdf210e70894b0a5d511d703f97d88d350bbece678e68ac0c19fb1e1b351f89561d198808ba13ef4aedd8d37355b706dc279482b753a4067fc000a7da177a74120141d9f4320bc8636837314186f8368ace17bb4064cef76144e2709718d29dac578ede55bbe11871ab15cc1f2581713afbd7c3a13b07647aef77d5d84744dd20fcfa3ff0df8eab2fc9b74b5854ac201ebda5e5268bd4963d4d2e56f59ed499b18e6bba0fc25be342afb8a9471802d0fe86c984352adf7b9e0f933aed1b558e186b2f58dc4e778e62a2408ffed59d9d9a2a5ec5a88644ef4e2adb842409c58538d4c8bc90d64e0406322fb9cf73875e00f7daf6f8d586669d272b3899eb8b43b7eaf08ddb0d8ad49e24d3a4963849bcc0bee897b5655c93068b803adb7232a6cd43e8c64d0b2aa9bf507a410b6f730079f87e936d4b338ff890f86f310db718c5aa35f2102abe258d187fcd08f63fab1e4dc3fe9d448e50d99b325e863727fe5d8dcc3dac5f34c744bbca4726dc1fa0339622d93d3f72026ab52e9493c8d652243a4c0572e5594d43633a5e62ff2dfeb51db5c6670593dcfd59862e399e88913111590391803b294d3efa55051109fdb7dffa5f1a4761910abd0a6459e07f088b2b298d7261f6c03bb8d3176a163201de35589adcfbd2c5a76c1ac27439a376ea0cec82b4f03f9b23137211f0257b82d8dc91840097aaa7bb03b5ef20415abfa35f8288abb80bffc8f50bef5c343f9657ffa5133dcc61b0f10d79aff59222a95fb31ae507050f125ba1f3b04b9b92c9feda1a70c0923a8e1829b5faaccb4efb4f70a3f05b18b63276ad04dea5f56a300de1a7a8b241ac352deb3581d460d5a2a0f63d287de6a9d7e595183d447181540a978084e3e179b3d34f65cc898ed8a5bafa08a0e903f0d4339f019825f50d6db591bdcf2626196a9d6db4139b66ca93197ecef5f13a018d5da421cf99106362984bed59e40f61f9f8b6dd7a9afb4279d21d86244026620646833433f1b9da21044519f15c93143a77e0209868bbe4cefe294398e03b73a4063597e7aae0a424f4e45eba165909284fe1527d208af3a285d23aedbb6bae6a565fbf5454b1e0dd77427e7c4b2d766873661726c8dc83f2c7da6eb941a04818e0d841691d28c517ba3f6083dec8610b7460ea7bee7c3c77c9eaa19ee25015decc06ff1d8cbf725e6369246d09c75d842b6d25e4e90291d2dcfdcd52be1b3ea7f217733cd45e81aed25dd32daf057bae6092936071fa8eef4daaf199c023438b59a313f694b5150f0629b0bc344dd71c9ca558974bb74345f88734b7a7308b0c3d1d46a4861754a82bef77c0a7c6b04ca9e213618708b1a5e3d28963b32372d0e94cdfdf20b1d706f1d6fc277039a2ef5fcf91fc9ff6439bc599e6339bfde5c48f8da216c8599c3ccdae216d06462083fee1f13c5b4f77d84e2a0fb2a55cd2f705e27bce1a6a5bd97d82d78af51b616af341838cb36012dcfdd89db0dbaa6e45683fb44d60d6e17c546fedbf57d878976c07de238ca1503b34899befb3286feda9a585dbda9150690b826b2b2299b90f48bbcdd01f2df94f2dcb54397c08556af216785770137ad409c485253fb8e1b582b33d36be967bd2e2e45e7f6e663656466d1def01905650972cf6793f8511836036c90f72bfa314fbefef7c47d56248f318a0565fd13330867dbe5563e416ae1db8189db8c872d4bed6ea4c2879e5eb67be73bc5e431aa587b7ef90075e5ecbfdc96e58e35ec4fd8108521e0865c724a0115b927bcdc869caeddbfa399ae9f96390ae63b73ad1a4c1aabeb3e16e4920ed8919acb1eadc79f20579c03cfcd62b3f84d1094d389ac2e660b172fdae8fcd9f412591dc313dd750bf586cdf2566bbb8220218134f4e488df2e94bf51270b2fa860c09f1a6c7edcdb7872d4441d3b79801cc9a139602fb24e8f491a16407152216594990bf9bbc83211c84aa5f5052407bf62ae16410a64e21b36dbe13205df83ad758dfcddc70b65a8d35fd384d3ee899409350c9dab46af1c8fc3bf7fcdec1e5d903fc60122d2027793553b5bdd7b7e4d3c227f3ad0ada9a5c74a882a45d5acab07dfbb433c752d7d357e4549f392fd8958bbb208fca220d1ec9265ede4bd1535285cc820b765667ac19763c1e8671d4a6a50fea840abb910d27856d94e0d3f7a84fcdb7b276300ab2f08be473108125bac98a8e35b4bf542b1c4bf7c713269d1f9d16164563b7b7b62b2466fd26240301f5d09ec6418ed1170ba0607a68544dd61e64115c0a67ff12fbfbd1d375e745b1961c9c07e1a58b05493ce038feec81c6524a61b0db613491cef26fb0d7d926268d7c7760ff84b41fb1cef1f4242f833ca3f6d45306f2380b67b3a8f824e9464470d542c0fdc0a0928262268a779c027979d0678fc884069847bafddd7bdbf8f534a2f132c79fd9a737af15cb35c001947b3d19569c54873f20613201c3ffc5427d7aa870a7d8a78f106d20e771c7615a1b8f38e34b86f642a770193e1564641281c428c066a7910de2dfb22820be9974adb4ac435771f80f15ff982d334776a16bbd9f6ccb1e2809a0d20e66509925e7810d92365945d987666952492f4c390ce0701168743193ded5c1e87148965fa1101e0888b90549b34d3c45c59a9d23e2384634c0375698f3648e48c4b3641a3e57a77a234f9fe555a0a227b3e094c7aa7b7075227faa1d1034a20408ae7fed648a45e1bb05c3729e98b97f286506f0973af41665b42caf690180cad8f1ebfa0421bba971cf5cb3b6493305174d47022b0df2b31cbc767d8e128f81241413d7ea9714a6bf7b3065237e2aa199fa0548741e3fd2a3a93a283497e0a6507d071ab5d29348a03847b273c5a45612ec20998bca7bd97eb1bfdbdcbd16950bb56720bb41b2ff9d8534582e056c938af0b8fcc148270894a2eac1cde5caa8f9c6eabe4c6c105212a2886543c777f38a84e499a198157113b73a0946c394fd84e3d214fc5a271ca344c91a78856afed88325563cab0b4b6a5d86f75c4ef6da011ec160e817c67c7a1fdef016bd82fc1b7eed78109c0280ec5845a7a8f52d39ef3186b90d70be1ca06ed06182e8fffbe8371fae19067d91f0a70a2127b2e4eaa55a473b8de2060679018828db38aeebf987395eab10f398504734ad86c9fe1989c14b7a130461bd3747524ee2cf5dfaacc3db5650035f8cbf3655b123d8eb6906a478d925897d55b5c56af64d6e910e0039c551b4eacc1c385ef0f55987a80aaa4423bb797f82cbfcb62723dc4f9757544a370c3c012b4a707e9c4f66de422864f5c49e53035556371f5b6df54b3d5971eb68502144cedc80a2c007075297ff24898594e4739f1ba5421b5a0e17d69161a5d2919c53e84fdd08cc9a1684a04f9daf481467186cd0d2c0582004e458a11fa8c5503e97ee9cd0670fcecbf7e38e66cc4538a4c48a7f9c7a5afb1a6f62d300b6c11ada78cf51569678a60c815bd55c95f78a1e9c095795a2cb67ae7d02ff75bfde5156c22a3f616b52cf142d313137d3d59974146642db0efe24d8a815269814668ac8245e4d0ca75214065d51da03703a3f94239a9b859c0031a22439310648ba3666eb77e83d6dc29a64ace22dbd27e4543f084e474f80cd1e2e64406743babd08ab88d51f0949dd326ab8d15c20d1c353244457cbda80a85f2336eab181ca33fd4bd3bb2cbc632e44adf19cf4e441fa3731198ba212a98ba014ac36c70596656a0e493b0ccc5ab91748c03b4ebfcb147c3529cf3a8474d9e990ccdbec7bbdfbd563d523c172eb81936840b28717d5e13ba25e3037d2c94a734e1ec34d5d96a9abcd49e1180d10418dd7b14e8634a38fb87ef98cff579a0cef21ccc1ea47afa401e42003a0203190b18c72450d829319920302609784e97968b2cdfa84c816199f06544e95b9897689965c36351270f3bf56e01b9134ba945311cc3bc2f60dad02ff86f1a40cf2c15b79416b36582a1cd0bdf31559649c0b221a5634f30cfa64eefa49e1172d885a2d4d1c7f2e092ccb17f2ab8fe1e9cbdfea4c77c3571b191f50cd2da43db9fe9c3bba583a3e5720dad1ffca478881b8b758cb554de1bc94bd0c6c2e39521a6f8588ba453b67ff2e5d607842139a76e195b33196fe2d68b23dfed52fb04ddf12da5d3ab55598524a1caa36c11e513458b72969cce3d75be1ef2a935ada9788918c425ae63b90d259dcd4c8f828bacebac9f6676954f0c0b9a2c5926ac857511127988acae14b3f7d563bdb3b3d8f273aad0cc372b01c6c1c9780d0db1c98fcbaa117473b623d4e5123a9c38a57a69451bad0d81ac8a66692fb885f860d8e4b76df7afb1b168a62bec4d4b1655dadd00ae16a5b497df0b80b6e7a1661891f8d5d07649015f40b5e6a3da8b86fdcbc7a0f5b2b8d6e786c7ca7f65c30ae75bc0fe24dcf4f74421a9c764ecdd89729acb62ddac4f7bb07ae5452790f1ed2bab926967ef8cc764ebf903fd2319ba2aed545adc4728b8f0a7d44a727c91668bb84ae649fcfef8d0a00746e68120f69079866be9b343bc27975dc5c08fe7e94bd77385abbb57e611586e3771e556b8147c104bb42fc8ed25da1b66bdcb63859c3433f5f13f163e5ad0682289914550d03a0bf0ee3c48b10d3d3621ae2531f03f89b25f539b890a609fba3167dd071013da943b61ac4091fef198dd3f6807239a85f6bb40e5845f33474a2fd2b939cf511aff9b1499eda1bf58897dde428a47d29ce648cc121a2c75000d59782f6bcd53bd30d49096d3e158691f06ba1cae4bd4c5e402f7a36336949ff8845a3a9035d2a8c9287ef7eb8b5f4a925045de0fed6b4926347a6c9fb2cb6e41e13bdaa46a9399b7f45c931cc1cf7b13839cd75e64c7a8cfa9a73bf3432e1a8a999b88e9c0165034c2ef34f64a223f618db00e02d829f12b0463759294a230da4e874d81c2b13107ba43326412565a0fae074f120deea0a97f51cc3284f0d0f302432c050a5014014d65b65d8981fb1d31f6e1bdf0e0b4f13d1cd0a21cd6387ace1ccfe0909ca49040d10d07c6d17f25ef75120072c15dd7648cf24a20c4e8362e2f6f50110de213c6d77ebaecbcf482870937ebe88a0bc49ee8349052650929cabd73f645d7f027cd67fc6837489f83e65cd732aedd7eb0167683d64b0cf11974b0e042ed67962bde41e3c9e33cdfd6c12204d854695785a176057d788234ad8f7f50f893ef9969e0a9b311e0445235148b5fa5d79126e50d7090071a41386890981f9d3301458dc8a9a87cf723a82824458e8dae4ab5e8bf590050024d4862e377da56c3f23721e8ab74c952d125b72b36533a2ab29bd3bdb6402e68abf719d80980c561b938546c1be55530a74234c7bd8dcebcc781d781f5e91e96f80062190c14575a8d006a9ca2c41968b0e996fea83ee0abf2c20b99e350299f87232caa9a41ed06864fcf3fc186af50913ea0702ec9d0f1a45f4b6635a6d2c46ac2d259d88c7ffc9e0d2905e86c279cb0ee7e409747602cdcad00b2f12660cacb919ee4d6e34b808a746a3fe4aa63dfe529d5bc4e21c8c5b464b331112241b74a79af82ae531925330e6de36254da380943dcd83a9285c14b13b9494c98fb5c914845a195ab4ad8b57088c23356d36b36046afe0ee21402f96387dbfea2907ada2939f6d360a0539aeb1e4d7b315eb22c99c19bc59758cc0bcd89f4fccdefaab882aaed802c336c0fcb9a6fc29a047a6bd3f098ed0180f335a33902f9628b7aadac11500000042be841a7b09681e08912fc7741e07458e98e2c6a74719247f3978e48f278a92c1b860cee85f0c5e1697a1235168d8bca225009e86409e75630e0d8d0b7a6cca656f8183b6c8f608b9965ac55a75ab04231d42d9f91da342566a633b346256cb53c7da49c7ab39e4d79e1c573003172f2c36600c70db9b47b6c8fbf7084746a7157bc2341e595fbde178d88694d058f76742823a024e3cabcc0b86035794ff291044780129c9ccb8a09e3496c988c540e118e9748cdefb311cac071e0b1155bb4b5d37126cf9ad1bd3c410a1939ae3b40ee47f718e918b8d65b205d3162255b923f35ecb906f0cd4efd127ca1075648e4d4ecb669e96ef870f63c26252fdd688e790971e8d1dd28387098b7e3df5707f7ee7a92e893f341f826f44a594f28cb71ad99c8a94ba95c1e3f21348901a5a8a5c4323de4b2c278d8d206d06562022eac5921c630a4105ebf061bb1bc8f4c4ca14be67c05bf089f4898c06b23a3b74f17742d64d756cade818c152eb6fe8ace8e3a446097beb51354cfb30cc49a6eb3e070271a70ed4b1ff2218e1017e97a07bd510cdcd3571256b1d4438b6c72512f9ebc34c8f4b73fb5be7dfa6569a355dab7485a1a472f8ee627b6df735dbf63be0d1bc001300e18ef2e3f0b79a945ae91b539f7536f8f3e130180315949cdde9bc8bad1c558e9e5ab698e2e80c79ec6887cbb550c6d2362172387e0dbb5aaaa94e54fe572155259046ee6d0d00e9d9e37d963b03f946ba2112d20222e25a5915b8658c2ca3dadc2ab80bbe37249be45ff7d84abba74289c00d8654d0a2a783650fd1db0e42460b8af613572d1144660bed6b1a8f683962030104809fca6b4b3bfd127ed6b902b961a46d981a7d3805111728180aab7b5dc3f863dfbbead53a84cb57a91b6eadb4e0d3f3041fb0d502d20085aa84a12c98afb51c3885d68bd0f8bb2cb3e681754b7a0692ae29a3f1a42fa5afbdcb9d65778b8bbdc3ec3697c04ed963db8b7b506ef4681ba9f1778483e346d400e43d815901a5fad8df28f600f8359e753ae5211e7c5608a80bff8bc88127a8d9445a5bb7f551cba02be0cf6b6dc152cd2baf138a3de46034036678f341c86c975bc7c31f620740f65830ce4f872e15b5e359dd80d5f021a26335e93eb2b6e9b138ee8bf5aa1f211b65e8ac80cf71b525762e136f239d2f0f356b9617fd3923e56a1e347d733d390ca44fc030af62ed8cb36a2e9c7b05f69ff7d0a69f6110190a8ceda3a0c4d143188d03d6eb4e083bc56bdf4f1fe56ed53660cae1805de188da75a9fc2be14e0236665ef3bde08e714581019f3bebfc4cd70612c625068a53a6bb38b2eec05f58a80b8a12b1a2ba94ccf6d00ddb07ec539e860750471dcceac67370697fc3b88ae1e90110e0296e1e27be597176f6d325cf2cbfd575ddfa5b4d1c31087cdbf4eb46c6f8e1c6cd666fdcf0ad19565585a307afa39a1b45c956238e4e15090a86bf6d02bad8d9ba3861641ab6462d1c2f0bb429c0227cb2fe3122c8ca25dc3aaf426e9700883d29ac74a92e472058a9b35ed1bb8bbefdefc0ce4bbf9d5ec81897e338866a71242b7895391c10983e2e82fcab90d5b90346010157d56ef8349173ee31a202ecf00649f010a33748da9f7b9764e53d4b182aea4a6a26d919c02a45bf360200e7f45493f45d374ff2bdbf0eebfe88ad74187159df6c17dc9d079e27e3d7c95bdb677b365a2c93a607ce64b04e757f3d452c6146cb544bb26d1a0c94c238628de8e325c8fe1cafae2cb4d666e0bc758a50f4d15c3eeacc5f183fb8c7474f2253e0933cd97ac5fa110b774d296fc03dc45b5b3d769dc94eab1279971d0db99b1bb58245c378e52cfbcfc15fc02b643f81388422c478da0463092f2ae429c2475dc76ee515669c28a9693c9a6b20e19a0baa1d77d1195fd63119a6c8113fa9367fab9457130f535832a65288f0257b4304e181ab5f9241a3b46b747bef26248eae3f4cae633df56e4de574dd0000de3014fcffd331d670436e31a56c057b638871aea913dbb7a5e51fe056bc33afaa81733edb6053d842eeb7c23a46504fba6d6de4cd76c02dd8f117c9803acdbabb5d8f0b37db12c5392414b0d29ea6a6eabb258dbc501d4fda3653e379a9a04fd835c0d019db02a792be1f9b0177807e0f2ee4caddc89f54cd6740f9c1507996167cb081aa3c94b482c90f36871372ac04560722019a0b5bf68ec7f46385b96d90f9932dbfbd023c26136573cf465f32dffee5b8e896409a07c7ea8889f514b906218c5f931b4900e1f82a5828d66747dd23ae77314a3bcf9366c13c945c0925143ea0621d3f559e4fa1f6ea84787d0c2d904b829bdbbdd61a22cd03756712d66b71f9e1cbb449c35de07da41c6a36d4238ce00048e85a3d2ebabd4d282606a1a039d6c0f2556336247c5002d98db040f2b558fbcd0138b1cf0937228e42df18603aa1fafc35039b4cb1e203bf542abc63e6b83a8f16f84e718b81915e42558c64dcc8db3399972db241c55a8c49926bdd3ad5c35b0b72562802eff62dc30cc6a993a5247a6df41baf86d3134f612d833810fe23ae6d4a7e89ced9c2c94d6c6e674823a2f3c09c9c0dcc5d03765a4151d1bcaaa117d5f5390181b4b6b475e571c95384d8828160d17d8d40f14d45ad2bafcd8849a242698eb32001657198676359c8679a3f48e9dee47735a2b5f58e1d82c07115a3ed6def5b6b5473df42ca3253269c0f70a84f0e3e8398c17445ab368f76951016b44a7bf7d9cfc39d2e83166b83a602907214da32bb2dcfcc62d302b3a0fd06b4b89658ab70d08df9e6bd9fcbc6cfd25bca186bb97716def62dfaaaf02a6737e27f8647ccf78c5843494428c6b21a73708e41e6fc5ad35871330294630fe81b7fda703bf24581f0428e4aa6ea0d124bed48d102dcde0df8d6b3c5bab1dffe0370d53da9aa25c1cb5a161276d4b9c939a1669b0d0bb31ce3470a17fee2b0da0f01a7f72b83414d579ad7dd4db80fe5bd07fd46bf5ad405cbdf6649ba14ed0075695da6b3b52255e5ca4159d2ab9b91d0b4057e530d8a58105778c57c887846e749ecb7688e261bbebe1e62c890879e5bdc00eb344a0196c72c0c341396252069a7c9a625ff99c29ea8a9027e7a9767e9a4fc9c06db743c3e7651ea069c82faf611a0003183268cfb319d2d3c57fef90fdad2cc2e6f3b06a9a3fc8525d4497afc49d104d7e79e3dafb2d77e1fb4dfa5808fdd336a36ef597e09a57a5fd96d4d9fd93489796f602d492eed95553fe5e13b45ce1acd4608eea5a979715273921c066455e301cdc79f8b3ef28ff9632c60fafb6567b5dc2e2a68c6bc7748d791b46347bcad1ba62b69e2d8e8800b6fed7b68033a40a66e0c6254d94e9e870d6975d20ab756f80f1834ef6cb14223d930358cb427f2b88361189e8bec09a1f34088f8ed031e40c0bc2bced026bed894f2497c59eddeb1e176d06cf8d3e75c3a4d37d0c9696a3f9c51bf83039d9798c81b52e6685d160ad317914a7b02ad45f6ba38b22fce013f823d67608eb96d4547af7a7f50c68494776556433386b9f3b1c977459d94b61f949411c4036bab0e0fd183432c1257da0a2edcae135b737fa175f063db0e5229f384e16758ba497608e150c4800482e27c7bbb2b656f3e2c6d593e0f9f6c634cf182b54e17746b449795545d10ae957557bb8b4c208aabd82a4aa9ea9581c2e9cd6703c05e84b7a3565524f4bef3ead3042508b2e90e20024a2599921fc185cd5e6c19ca0ae09e5177e35f072fdba4669099f5c20df3478861275e6313fb8e7d47cdf8d724dd9404026b2581ed44aef671a582c1792b9094b3320e3eca91e9f1096527e0809bfe3fcc810feda5de3ab3c3239728a3a0bf0c67d07c690f9cd4d2935129dc9690836dc5d5faa8e4f4e8d0eb3956a9bcf40b44a5d6b4313a73027e7f791595b48551c712253e713a8ef0c4ec56b887c53b951254e0d0b6ed85a3cf24d5b960937fad1b01dd583f4c0fc465e6242135599fe96131c9fe94bc7cff1d6a6583797c00c85d150b59183813af6499394b6df7816311c9e0b6858381e4d92c9cc43955271d1f108ab89809b61d2c9b86fb7407c69d5d09f9e0e2ee3419918ed196e9b1cf88e8f7ee66dcd27438f9b6cb1fa579918bba8d4eb961ba193eb87ea7b6a0c496994a2b85574e622278dd3bebd5c2ebdbd23d403f1a46c64c2226bd3fab38821f94f88aae56f90903f7ea9397c3c7bfe9f84b58ffd4e04aca3fab654ea57242a369e6416cd473a0a3569eb889b65c5547cb7416590f2cece1254e815bc1eed2142d638162de6368956264e8790dc44ca5012fd5178cb088a91a7767b61d73f14c4389d9e320322edddb297ee4e5370979a0daa136a1266a1a669c36e18328e680529843ea7c460ca6d748276cf29d7d9f3fbfeccc0dd0f4b89ee4bc410ee954638a55b2e85350041be4e2d4cf17930214eb179250483efd0cca98ade02e89fc7c9a2fbad9b0f50f591e09b763b88a65e21fd35a97b149ad75fc16da7d52cb806ce380897fe62fbe58076d3048bb663fc357b9e0bd74f31db5bb7af22272b0d7fbebca85969e56dc50bb5b269f72eb9ecff601a940ab92c465922dd4f2fca77ba55243879e1b9c78c47fe38c17fe4e03551ad118d4441d50c198517090c628d4ce14c0a44369413612c1300a9c37d068b11003c000f96fb33fb7366aa72b2bf5e91c50c3db626f1152b7225a3f9b4c7970ab63d96b41819f22458d144d4a3f1f33264e8303b5851b3402b5da9f548576f82c309c49d15ed502a0510b4619ed2e1502b08d05382a36ea99c64b325ac8ac96f0f27f186e0ae4e3bccf5f54a22b660c4e9490edae06241bc1daeddaf78353eba71261901c366eb6cecacd428df76c90c2a352d40c3583c1aa60c1465f0d87903bec12b75e76e92e157285cda428b1dc5e7c0734a6bfb2d060f4dbb371f5a7e35367c143c29a2354c144fb387be3a6897b8370a9eca1c1fae0325b1963191fbd4c8827cdf41da124913a3ddf0c9db20819be2f0932159b2f8fbbef875a3c722d1d22cd4b67fbabea0e2d56901c4cb710ecf8f044226b52a25a8eb6524d0f084856af29abdf94c2406c9ee08302ce28c7b631fe8ceb850904234f2b3869567cb699d63e2b8183756047828041ada118a4c5ed7516e55e63b8a4354228068d503b482ceb082f4268705a65c121a38abf3be68ab82ade2b85083908c4aa82e7cc0cc290b611d030607f0bb1309c0212a17e4026e8bcdc73f88a21dbc1ddadfce266c36d30dd38df2b6386db38d4c627b27ffa0179e43350d4a22624e15e10fdac3ef758f173572bfaafc44046f72d63a762e9236cd37c7bdfe1a058c0c89bf2d9f0a255ed1ad5af76aa1171c38295358f77c98912ee66c668f98dc64e6405ab0f6bc5bd1187b9f252e6a287a91c95a7cdb288338a5505a624024ca90512cbda6c6f94a208cbee06ca89e4a608f56d6fff704aa46985bf6b928591fc500df6f2346a40c70d8bf4b8b6c0e98d8b18b0407d8cca6971f661d4c842828f7e3d7ee215cdf89f61f7e8312c14d44b5b9ed3e0d15bffe40892830ca508005fec27cc5944555805d23b6bf5b23c563b78ca00b3abb8292d1c205db5324a6fc89206450c4aee4738e422fa14abc3ef19110e563c7d7482c4bf645ef3e4d0fac247ef6480b3ad0f5323195e4b412ca169d9d28e5838754812ae5ebd9bd1a680be32eb71343abb681042f1cfa9bcb8fa7b5fae63aa360c95ac54908b159e6dac6007f810c09b8cd560babad3bbc63dca7b3a35c2bfb4c5513d28ad34072dc7e3a794a6f03de212e12070fa0e04b217237f3caba77e21986bc7cdc7dc459cbd979d2b41a2eecd515360f79a238598ab3da495d80f64a4d1949e95e38340a86afc28eeefdffe685930aa5bbabf0d68c7abb0151580c873b2a0d7adcfb0e8a9f1455740e4de61c4ae3b6a3bd4d620a7331ddf6ed6d5a027b32afcd59fcf3fbda82ddeacacb5f5a9b505f2f38822982a6bb5299d030bbb8d916b7529b18ebe79aa5415451e285c6d6439dc0650b9d76339adcad3401f1a34bf498c38c0658b88ce61851f288b58b61cb9fd48b85277348064bc0818c416612b91749ce3e534a98f55341dfc6f6d8473196bb2499dc89ae2c98c104881995de83b2a207c9a7e9c1e4ee4c7f66995fa7143a83b4fe24ea2700312c2c6cacbfe77c3155c9ec122387436eb337edbf471eedd40788804a08ea04b21626138f89d4a0a0ba167703c9dfd0be6e7731f54c7b2ee303241f84f9c36b11a898322827b84f1d88a2f76c003600db30defddc9dc33e27b7fcdbe235789335b7e2f1396273657827028e9fd762a9f6c790744e42ef085eb35a19ccc58b6374046e08a5679aa0abe5c5df3b2f59501ed59f97c73b8cd9e73db6d729950114000217f592b68f4877fdd3b23c2b07a15754f9a3c2b2f13905d74bcbc4b04f60303a0a272cc565fb4d0fb9df143928276c688c099c745eb4bb8090484cd07cc13129c2fa3e0bc1d2084b938df09886d6f39eead6ee080fd06f7eb31985cd9bf7856d8640689da3355d1779d308a839662516f312592205ed247ed71c4ecbb0801ca8db6abe31a801cf4f339690df6d965a6f79e5645f047650047c60d1793e82795f4531ef23088626c01b2bb4a05bc79295c0a840a6d8423a9b9d14484f1ba5fe74fbccb25f17589dba51326c539f81b494aa37a5bce14f6621f5de4dd490f57e7d45437177d095fce151fb6af39636aa979dbefb870f088ccf543ae38343f70cadd7bcbae32e746cbc98ac9c64b42025859d1647b23194a695453c1ff8c2008edf85b02212c405cc60bc97f194203fb3aa6c4d76c6e02fb1d2f3c792cc3bad01c772a2ea16d263d65bff6d4ea30d872e250c7f45c39420e13c4ebaf54785ab1b66ddd032aacc11f7696536228a59cba24beaea32d0b3a0ff5d42b0321cb3d613dc8892c4f41359ac52f6edbc7006afc0ed3f9cf10ae0bfe389310b3f995953f440a2bb43972db1155931a083482d8f0c85d68ebd6c2cc832d100971dad3526b8eeb0fd3d6c2961b7968ee4c6dd22b2f0f7e097be18fb5d2e035a740a47501261ba1cc2e2b500d25175e1d166da09e5914878b003f4339acb8c1063a31f2f80812dc416066cbf0b3e20f9307116e1f0985ced773533aca5d3a812bdf265019c62c182310885c925a039f77cad41d1d390f9be2fa9b2c69db406a3e256380705be3019615e5af3f1e738b1554ee0349599313b58a8acd2051c96a4a94114052c41ea2f4ef8ff78645619ba6eabf826a5950ecc7f4b3cf866a2f07e315400d37b4faa378d28533d491b8d111f6cf3c7ee52e7672d5886e912db401837d3bad46b48864592ecad788fbdb191d248f0aa13e813eb11e88b86a29d2567ea107c64073c608b1e19926bdf1a937ab5f41e356556df1e6b6a92807d69e4c7bdfccbb4ed7f1adc39049d5eaa1169490c7bd0af960117abb430a2a289fd0ac52620447403b6a70fadb87dcc95ff8913aa58e59ecccfad187f01554bc6faee89d4d8e0ddaa2726dd932028c09c849522860e815492392834483ef5588111eead79066ba101cca8edd4f991f65f19e04fb04738005b75e8df44f04c0c8e88381afb2b61ee5c0b3643dea8d4ca43d300e55a70c46f4cdfc8165c2a76c3bcbb7728e26cadd169fcd3094e3e84d7c7d43325e7ed20c6579a334b26763588407265629fc99f24b9d936a0622cc5eee39f660aba19b720c634704d847465c8436cc1b455b27111899cf0fac8a69ef76d7f1eb64b32221ed875877ece36cc2f89b2f66f4b43fb20900e2c50efe9057daec87c7c123338618e343e7f37aa615aef7e65121e4046c30050e6284f781a3317cb053d89cab1cde7468eda6b8fc94657af7c62d3bcf8f48054d5aa2b7a5b934a208332939b457b4a956896a3c1cd666d75e0f71a16430b777f4b7f22cd024723177cbea4cdb0a5b1da2b649dba5e6dfff83ef4f3abb22d6b3bc6cbd4155956b727232a81ca9ee0a55fc89f1955ddb6b1260e935a12a3508a2b20e9f658105b9281d393b3f80e2e3f36fb42f4e154071a3601d2376c92d18b50c8b5a2d12ec6109a5877d127e168ad34fe045b29a310f116b229187116d23fa0d8fa1d2c3bcf2e4ab648794eb5eb650ea59f2cd6cefd9070e97f029530430ccf16da45fddbd4a84c778e6e226b4e2b1c6e45fef16d3d0f87bec99a02f23b0b6169bfe4b3be8439f698e8bc6895706d123dd9e298d1025c46244d260c9f4e393ba94cd28e9bd4c5878da1e519f83816679d97ffc74cb18ce61404c75b56955bd265e83c8c1350ec0245f3843ccf11b71e9c38008a6dcc78d728363648597e86705b25c5ca06801779bd8cb821a6365ef593aa442e214ed37e043b7e803abfb4ca309cc865dc42c1962e8742646104288613e01a2c707418400fb7b404bf2e0d37f0c6661961b25a648c46783a31607709974a94235e7587a61b66ae4ac846475a53ec946fc486243235e0eb24c511c4584b22abc26c43d2eaf433e21f48ec6c8793f060666a4ed1751deea314aa7c7c4ed8b9755491ca1c37fb11eba554fe08c058505d2ec8b53210e1ea744814441a85e4d5e9c0e8ba99b350bea28c47c1bc7d41045ae4ab2adf56efc8e9295a1e9fb8866f1ea599d2a9290ae28f3034d684ec22e24533c45458c216db28282e06abde31351ec9afc6aa452e601b884eddaabc50e4a9a9426e423ba074829a9a0d28e8964ff2359acfe46508e99c3b4c3de4f81d5ed16ee89b1b5d8b0144fa83d4c3e8692a058d3ab639d2ab2497b19c1aa2c11c9759adaf61b0c52ef8bab47388302f7f6c678477d23d872029cbb7f470460dfbe5e9d0677dc5c3da359596285cec6ce0109d6b35a05a702084eca8bec734597f5a1be295efbceeca630b6af3a2d4d790a83683d58c1d4f128c3bfe0fe8d6f6005283dead907225bb0dba88bc9ff124f9c00fb2eae10d10414e6ff29e66a8da221790b8b4d21312549f10f6f46c8289b4628aad84e568fad21e0a7950a9e1ec75f1bf502680da1553f80189fc0d56ac7ddea82ae50a13517ec3b75467f15e702b890855e5fa55fbb9873c713594a206d5a2260fca74add26729fe12480b4eacc31c288f3ac9aca1c4ac4becc603dd5068756aa964ab81a0ba0d2f4c5e0149e0f4182d3c79fb2082431f6010bb5acda8b41998fc8e019dcad50aba1a9166a1b5d206e77ab49362eb673c6d00d27f9c65cf9eb5e1263d50d7f1382023a5b09726c6b8c983cab88cefa4490b82e809e87d32f12ece162e5f944f1b64b02f75a5d05843b251241fea6618e5f42262107d9ea54456e33086b4cc5a7269f44b27e4b809b67c105ca0e13ac7af73138f5c42998c4efa19befcca6b6bc64f684cf43cded400ad2fdd4ceb064e19e8863526e586e2da9270aaf340768c21e2a7ae0294aed4a352cac9429294b3dbf2535a95d801528845df116f8d835a4222635ebf88372bea507e0cc02a2d3a16867b64c1a4306e87bc0ab4d33e3cf4dd83adcd2a15764ecee1221c3a0cb4663a09a90ed53e2044fb3c3111d2c767e014edca50488115269de0ddaba2c8e5c041dc5b45a8a33eebc40db75a0f4609db6df2fe445d9c0598a926125fe9908e421f4b24abf6d1f089e61556bb572c082e588680961934baeaa4c97c4e83b5c8a0daa5fc520364e7dcb3083675330bd38d507da74000df6876b8fcfe4ae815b9906c79e6ee7aefe84976da9582277ef53a241f155d8eaae5e95b1d1c5a363a95f28e81ee27d7b656ee077fbe8d8c0b7a2f0b25604e7161431e0a625d054dbafe59d3fef6fc8c347d1d946f959b70186a2c80b6534306fcca4b70fd08d18d3e397a3e78a91f3334e65d33bcd88fd75e7b804ea1d241a8bc39bd28a8eeb96cef105b23a10f4fd670fdc2a2ea8496366a2af5471f85a29fa909b3c208756472955556fd404c9bff37cd656b705615d1aab6bcc0333190879186f8eed19be5b12e2527a763c0b3bca7f3211afa4add20d660d67dab8b6a69bdc377d90de4b6f7209a92673f52462c9267514206955bd1f0f8ebfec83f0b9cc41b9fe9cda81cf0b1652fa3c2aa145171d7de367971a794a7100e388f9944c662e54584380fffc243a787a84e85d155f7086b0f0c317db07d21681a82e25f400d1f44c99aca07c9eb19ca7d4ea7dd61cf3ac526e2d399483f7d3461335df1fc2a22e94dde6f9d8024afd0957dde955999df58cbeb4ba2da26eadb7c19ea464b6f2bc3f9d849fcfb250e42d2756c260276a8dd3402fa555351bb85d09e852d00804cd1860f3b3bca96769fe9cd527a9c505b768370b66427d31250919c1388b8220d6ed94885267dd42e8cde809da3e0bbd01caa17adb95213c62e34df07cb100056d54eff8fab232c25fa8e3bde04782795df7edc585eef396db37d1f3baf8a7fb0f3e651660a90765641ab1f1362465725b1aaabe6e643f9ab48c0ecbb05e91002e65ae2e33cc9ea9655a3180407c733842c9326de9da968f5948ef345c36a04c4c68d3da0aad83c8189507508656954414223c35d541fd0179dc66a7b6d200153de9d184ba31b72afe07f40bc0ed0ed01e44f15323a47c724a8cb36ec3817d208002845887126c761fb1f2dc22ded8c61d37ca2cf501cf6074f4b196b1dfc28b081ddd588ea2f2479b9593acb390a77785f39eb238bb0c70b8c7d8a14f095f5ec21574457d40e860c07f925f1a0293cc101d8fac9a58ded7c10b1fe517aa4cd17f9b960bf698015e35974429aacde3ad9dfeb0c4825a029c866a37bffeb9284b4475bc47c77bc99b8832d57defac3ffd1c4a18ab8b5a98d9bd3ba30725a1d9f23da6228ac82f067a1ab761a48caefc5b0e04a7f230a1fa58e1bd1a239387284d6981df8b8f9756d04515ee0a35c3e959c037d548047eda7278bef0b98ca1fd7cd5f861a9467a06d5b83b0b4f99a785d9ccde960b850dbfcba75d2de4cb5955d542ad25e3dd032068423f61ff086408e0faee155ca00a38e276d31f4d402a0cebe98f2a10d3d95c92c4f255b7f4f8aa060c7953c626cf12c26c60b363d2555e4f0d3c9b4d376c1a3f35ced71680bb720b96d52b4c7b531d11ee2b3d50ff0b31ba0478ec30de017e9e4bde5571d36131f0be7efc4826e6008b2d5a19ebf9c30b1fd59545d1c97dcb757e71c512c8a05ef1e32714306ab4302979daedc16be72ff5f93209195e95a634c42c57cb1377018aa020b41f9e70ab940041877891c916a95a359176ef705a6a29892665a1a80c4c8a874db6f8eb3854c9a37472e01c18df34148a1409815ae0a7488835024b08a0f4d00f8c2803bd0b9d100129ef68538a2d98700de603d1be11ea0a6aa0abd2de7f8253be6e8ec0ccd97b81f779dbf6a0126b24b044c88e6c964c45da8384e9752d738beb870a2e7902c85011286ab7d222eea73e505a387f0f788b27a1311026d4e064b3040880b5dd9d17d4d75085ba5268f7d0a0820215caabcbae91c280197598286eac2ad7549344621d638a27f640627dc408f4fdd7bed7be9fd7172fbdcc8b35fd17329b509bfa1456c10b40890787207e3c11c61da304f76ef51e5d4c40f68a8a8c2eb01932003778a0b63b05f5430bcbb4a141a922253b04176bb0e1c01f03934a913ebc1063eda07eae6dab94a0650beacab2e27f7610cdd4216deea56adce8104aa12c9f0670d5f3eaddac38f2226b8d8f8b698bb8f5b6c1947ec885794ff30abb65c5fd4fb34727ad2a5961a1dc15e4c67a0346ca1c15fa07522b852b7298c59ab67a3a01afb3e81524552e1d563c8847b5d6057fe8e8e5f0ca98bd8807cf9b38721606975bb7cdfeb2ac608572d53fba95de6bfacbbd0728fb9d0770f9e099d25d835691e37ea4d465f96d8ef61362c4ddb58213faa2afa42e85a2ce856fc16a644b436eb59b8eb87ea26a6926208c70ddd94b27e86615256a63bbd1aa5162634bbb2dd8152ed453a24716c36fd4fb669644e154dfb0d49643ee962884ba5fc388b0e21159f4da242e36ac98897653941a782ee565ea7953ac237df2064004d178850aced5cf8b5c304aa9deff9b6d176211fdf98b4152997d6480e027ba8d3c58f710b68eef923960f704dc95dd5e5c3746f1f42cd6cf56414b801500e145abb30d875c102c6e3558fb7cf0eeb88dab4c7ebb76287d9e4497fa6a3feed72fbc751bd1fde326524f9c229bf6aefe869c7af43c74ec1d7c7c3927dda100fd8c408f1227037c0d268ff31c2384731afa71068caa39163e62c915a5514b7bca19ba132b34b78cf4f8ce4d798c5720c593c83c84788f56181cc5fb28a7f2c7be0eed65b0090f0cbb00ba7f0219dbddc4a2ca0cefbb554bf5dff23623bd87420df4cd8cc3c62965b35186de4afd0318ab851c94c6428831d7f32e910f793c199faea8afe4c5b9f719a76c0a04858007436c7a01ac436e0b2a93d2249f00ba2b5ab4403fd59fb6a0b4f612c108e28f0425a822d100592364a03be18ca37460c5cfc20cbb4a11d477c344264bc1671bd39b2373b35421335c2a78a26915a67fe71dfac52d6c3fd500aee219bd3dcfdc5685580490d5520ce00308c472a2a2cb473297210a1d01a26c86b80ec072a8b802791f855ec22cb67362a9bfc049130767dc7404d1ec4f19c6875799db87e395f82aa6c50a304447dffe52e357c419238e43154675b5022f30df355238a6b02c04707d226cd999f1ed37b419813b396d20ae120e6cca58724b5b6080aec5d162a1efd275dba2ec495c998ef65e7a470517711fcbc5cd0036c434906b67ecb7920bfc0f8a49f4794032694454fe46abe1a649602aa00085ccabe860c62edd83822a0b808dd66fda0194c0afd642a6d531f5b5ca15a72a0c46b75696d43d035b23ca016f6f8fbb3f4b8c3d8ec3a793580afd748caeed32071a0d32e7090d6bad68b4c2ac7c5507572ab2d72e10ae34a4a74b766927016b9fa45a24f9f5182cf65f5c5cc2d3937810e83405056195817cf3fb7a8efa7231e9d4a821b35443baae10f2307201dcf36081c1f92ed7656051d55153574514b401b2088e635b5b8b724c58cf27d1a90dbbf83bd0445f3675d89de1f92a51b144ce2962496b3c9d24a7b0f0678aa8699c2140ab75730dfe74f0a2180119438777271dbf97e25950905c09767cc6f0465236ff2ba22294043ed0acb970679dec3eca94d8d01071b2bdc460e2c8020146cbcb55faffaff2e51d0d291ff99716239e4d70c0ab273e53d88a3ecc3a076d438f799e2fd0117f4086e63e251c54096591a1903a9d2713ec8f34d652eaf086e06cff07986bd87adecba1b32e4981c7eed9076da42fb0a7bfa73b5e4cc65c63aef02c567d8251f1bad6614a069258907d8c9470240d0ca7811cb270411fe99760a5fde9b128347a6deb0e6a241f8aaa90bd45df19da7158ee2d25cb2f5016a62df338af0e981b85c0eef71a85d6b73d951965544d082619579eafa963fd4cef3e67ab02cc57ab9e5204494975166859c77efd058d825f18f5133cb5f0c1700fe388680f36b51201c4bae4896d6c8ee237f2eec6e43d08d8967337971a551d8dfa2e373794ed42676069eead2bbf03adb81381e8af6b36a619f2e21bfeca9693051e231c335e120733f9836fe8cdec42eadad24754022e04e87840e9a651a89ffc8575a5911823eb3a78d50229e3eaff7b7d45f4f525739a5d5fa1d088fc027404e606d069c6a88e18aff3cf859e02aa8c02624dc5fb96d560824034c652f30fdd11f2a6aff1999fe46fd0b03480e25e7213bd0e01b66d947d79f8c4fd1e38cd41bb013d2e2111e31e28c3beceb2037268b1ed681957b3dce75807f2e9524470f72ec4ca98a3905f2af272e0b07843bd650bf06f11599261f1ec619b858246302cc4aaad718fa9a6e97f7f2885e48dc251a1b0518bef347edf079a9a7984ee7fbf4331c0ebb1da80942e7ae2833bc790b52dda7927fd05d5e213d80c60eabc6bf65a0192c2a566009ce332034bd212b1ed378c1fc3f2cd32c3c841d7a38fdcf3464030775ec6ab9260fbd8b6ae609cd828a140e0b98a1b2f248f6ae6b6722dbe891b2655b2ca68b80d4140c7fcd0e910d213a2556c1da51c4a7ac3b0641048b51006c2385d78ea44ac815ce069c730be2053a272f5f6360fca1274e0da8a8939dd6824faea95ab4c9f60742097ee32968148cf2899e7a681ec7c30b200e701ad3d054343483e0e65700eaa7a91fe44d06d2d955d48564d78893d2d9d28aa3d3d0d0d7ed2c5454be9537050931a14036962892762a838d4ec5dd7da5dd41590fe84b9bf60f7e2421d1bbaa5e3b48e1f290ffff46f1850af51bad93e7e65b3e0b8485b7cb0292f1f99c9bd20e82ee8bd54a80d2ba2f3e88c398d53141a55c6ba028525f99355560a970b0cf0815734699c1d48ed9c3b361062e3ed4d424f6802d7c7ef8629f2612563bec54213bbb8296808c133855ac7816e0e42b05df6cffcf3b4f4fa7430b875340d60d51c9ed33adaa097abd744028a331e861b17c1a134b7ec3beeadea4344e517184b8ca72788da2f9ea5c52f9c9cd038cdee507753f13c7416e4b84318588e481c634f212dc27654d6d4a3b7df22e5c7bb5683ecb812abd7056c8f57e0e3d59dad44909d54ddbae456c11bb1ec78678f576472f761ede6b512ed53d4c29bab88e145499184159180bd3f015374ec24ec8903ed8e9c96fe398542e256e9668481790c4fd58004ad539a52a7aba2e962661409bd4a809db7757e2fe9154148c71af5cfa8a5b5a6087d63d2d7ffb308a07d73374cab1d799c0ccc403d6566b62c64b1825af2d661e90c2d75ee1ffea5084eb954d7ce3bf20eb9503d59afcf779f957ad2245832185846225857f7b75789cca8ba0034abf8a8af3b8c45070698c8373ab9063243137c3295353a07b1f7e6d08ebe34ab0600035a28cf2529433ff06d8acbda4d0ad7d33c9e463383ff357f09d39ddefd23ec6190955777012029e466c03db2736a697e44cd9de1c46df1cfc3303254e1a34b64d3c76ff6d80009c771b80e519c339ecd5c5520b36816e80130b4cd23ecfb18b0c15366f9e40e5aae4e3ea9effbbeaf33f3dc0d16f594f6f126d37de6e95a33404e74ab8f1807c2e2860ff36f19e47965a21f22cf79b6bd3a0544077d7f892773b92a29dc686a3ca5acaa4ebbf78e780ea1fa464fcc7a28e0bb9777b72ea95de11fa5ab736e45410c2adea685573abf0e2c3af3bcb65df0038e55c61ba6590bf707a5298c8cc9ef2a139b437c2344870d8f079260058c2b103d47d408a50683632d3e568bcb907278990cb21097596ad011324c06d5396492d1af0459409f2558f44bf060c1e1d455cc03a78c24b3bfe13667e75ad07822bb6daf1e427eeecb80a0c87310a1a053e62d091f31714dbc5f727212d1544fe46227c0764281321cb5ab3a2e76c157a8403bdff841e3fce55df2a0be63ae59a8d957421f650180d8d58031c5d131517e7743472a3b0c7713dbdc4035d330d4a11d1cc9401da6e93d3e57ec93f542d720aaf09cd90666191ad874d9ac381cebaff0b0c16fe50d3595e74d36c010f2b2928c51e3889c6e3e860c8fc9ef447bae031a83f422213dfbd859dd5b091b5f4bda85eed0bf677cea477a258f382a83a99ddbdddb08f700dd2962e35ec993a6c70eb5acb703c718460c09eaef7a16fb412ad8ae6a11fc8c04ed51443e8275bfe9f0b06bb009ad12250427543b988bbe5d215b03bea09e19d7b548c5c6709bf3764a14bd183168e208a1886a1a77f194ecec95072cf8b131368fc29e92d14b13468c332abf4a9e38be43e65728ee5e956f7b180230c2ba3b8370d3e07f0bd5507bec2cc111c92a2391068a5efebe162c049e9880cb761f50ec32c3c6907b4a494fc5dc64ed9fe43490b410d9ad7f31ffae2b2093889ecdb46b9a347de34d5331b2823081146e2f8d0db2cead6aec667032272cd4338afc00e4f390570db8b8cdbf7fc6baffe39b0d7ab390363b5a119fc9d844942be14da6ad85328a04d917845974cf83b55d0d0a39408149dcad7b3023bf4d5d72725da35a5614c7a9de131ad769e21bbc6b97806a47090273d33aaf7d2630d493d1d44af5409940d88b5d2820eb1f39d81f885f2b1cc936f6d03027f3028ca00de6839e0650566e24a7517faa048019b6c315404b616d86117179871c4f596ba02561dce55882bfc4e44a99517fd02910d7b445e257aa2c19c1c8ff37a6b51caad7aecb09fa83f7e06c7fb7d474aeb4acd2932f0c77b71e90270738f6d5322b7e3209b4291ae9114fd94076613b403f17166eb644fe3c5601aedd27b492ec57c023fd9d530a373cd8ee979ce4011a8756c2a08507758adf0162022de26116e3c01213ab203a50f6f788cfe6a7620c5be6ad45d9d5ac066c8193458e2bb34a123ac0fde420082f084dce791f23ee131ff1f19836ffb6d9969d11c302c0b51a5774472a2576469eb4008ed5480228c81f3e010b5fb00dd266c627e52bd40642693488c8cfc32a6cd2930f13f1959abe20cabbc0f06fa52f2697c164b32c0ffb6ffc38618917f28458b1b9f4e06c9af6f4365f4c19ea2671c18e607cba041cca86e69e46e5d232364d41112d9d89b3808776504eb42acf7026e36e015ca933cf472ae2a42e7fc7c5b82ab494f6a62b8548ec177a5b4338ebe4857f6ce8e52b755feb60a5b289a70fd5f63bd020add4d1aba33a56a65b7358425c3a14398e9c7d0c386eb6dbfd975060a3e8fc801137f37927cde1bb60ba5d51796184b3cc4ade3384e45ec42a089ee92a5a87570efbb455880932f98d6241f719005e8bdcd7592e27e959928228b28270dd3aaf2f5cf88c5d09631ffbd7c68573a809a26ecb6dea9cd1bfe18820d96fe34df0e2832eacbb800318ec1f17b484f3e5d1596083a6c0f0516f10301bda34747c5f4e5f102fcc50f004640ed4b5868b65d3d5074d75d47116303ae074d1ff6713f677ea4fdfb57b1df2524edefc97c3aff2dfab9925674e03c60d0d1b16f59d569536a7167b7b7ae6f82bee430c7355f07c9bbfa70344853e0c75162f75c3b24d84e55dcc78eb2f0b3baeb6a02b7ba715509680901c105b72957ec17be5e60926f5770cb0268114ee4d67d4a41a248b22c2e84962f4644e96c837176b8afacce9d40211356dd9f7e0a82831c229ca665ad4c37ff636ecf18c011d20acaf8458a9a958cc25919c8b4a9b2137c2e3032ee30408cda09a4b13892af8eba5698ffecba6365eb6de9d8379823cdfaecb54c1670427b98e3773216c1dc11dbe04faa7e1c71a153ee6f72930caabe3e43bd398b7ac3052ada62345854caa30f27ff31d49ff5770d8567071922f812731a8512a696a1b40a3039188cdc88ac00d3a99b47bdc6104882456ae061b508ea4a62519aad5aaa51b653743a6294ade4fec38f43874d6bc1c75db6046308ac20ec1f721430b509fba26b1ab49957c76b990ae8baf12eed5e3bd7e448c2cb74ed7bbbe9337b971e74309863711ff257b6a3346b48f6bbea9cb86adc80895ed34e4f8f0ef621dde0d01c2cfc009ac3ac2d525010b53a0441001058c98599a8925ba159813995851515a345a608480300000060996666b68438153db32cb422b2a80100b4a8016823c84d967b1cd020cd1dff3d30559b3121f28f19235c37cb5a29654a01c702ae02cc025438c440b625b3d4fa2bf814ffe238a6a1fab007c3108c761a690e0eda69ac9976477b1bb23f70a86b8f723c0bb85f433157a4cc8e154a0783ec850c989190a029ab119b6f31a0621f504350f36e2c623b6175d90bd44d457d8a5ffdb8d89adbdabd3a7b00baeea83b4c4fb2b3a72fec06ea2c2cb46663c09c4ccae60a4875705bd32615d29012a1d4943c257363fb94c5b033a86758ba5004354f332349a0ee393c4ac976b181345849acb0cef076b0da33a9946d6a845632c21445d5eddacab6a03e1172b6b12b7b83daae46afb6b041386a63dd53741432b29e958cee4667e56d713de50bac73299ef5a8b96c23bb1b675550f7360eea3bfba3e6d971eab841419dc6ec90556a3a3ad7266a881dac5a7b0dc76e8f6c2e66629ba8793a5c877234ced59140575b6af3bc0eeafe9274f1c5c25661732e07a0be6d10358febcd5e675d2ab3ef1126555fe95e6af6da04751d64cf35b643c5d8559d6daae7212d593dede1462d43cbe6306c335bb9bdfa10b5883ae49daa61779c27fbf59aaab3a3ec0b6a621d663b4bec355b8bc103359f5182a9ca16a026ea54fe0443cd2a2667772db90239951fa768cf08897279bba8e23a49ce548250549d46813acda6da06d4c13acc1e515bf51638eec5cf3a558fcd638cd9c53aa42ff98baed5a0ee2daa3601f50a14b6af96a096f3c4e4e013360f0b82cae574ba0dd77e2351d3b07f3998f58cd4886867ca784ea0444f3b30b9b53b8466055500742cec397b867d072ca0be45aaa6b2ec7e4601a7aaa53a0d98bb79e87808d45836efb27b0ed460f20efd04a1a172bd38ec3e7e05f7debb147655bc1910b00787af782472e0a90041e47af14e6e030ff9219623722291e31d7147f6373ec7de75fd23b1c876678d3b9aead08fa63a1841b0236a01daa0fcec4c52643b9167b7ab66d23d23e98a95a875c1926a5dac1fc74610b5b86bc50ab5b1dcf375749f752e5d1a39a802244dee2e7579b68c20dcdb1947cbf4aad27abb7a9baa7f699a5b20cd7541febae39366685640ec549c612ac8bb6e8ab7f96d3b47b55e5e1e484bc5756651f5214d5f487337c82fed98cc90ade8a438cd5290535da63796ed1ca2f5785e2db468e575a4eac79ac890e62af117e451107f3b509a99aac5bb41fd0d4a09c81d4ac8ac53c327dd7cc9f61f7976fe3919fbc78fec8fffe0f903f68061f8e4ebc3c5f883ff0f36122d84fc15cad06988bdc87901501ea891072ff24f967f57679bcfce4d8b9d247182a26ba885b6e7e9ece7713f04bd7c9fdf50a88f30e6e8e0e808c1d11182a323445c3c12ffcb01770e8e78088b803fc7ebe18e9e081f7fe15e4f1ec62fb0078b10018e833d6051e8130a3742d0208608177fef0751fefc36a9e31fbdd879704304e1db391c7b808d84dc481bc5c35f7fe1e1effde8ef558f471c43fce3ef97808b2f7c9b88c5fc3c464032f9fac712330d93ae99ce24eb1f4bd410a5fe019fdf26402638ff588c97c2fee3f1efedc1b188235cecf0db7b271ebb872cf4742a011e99fc72506417a7ef8942d27b269fb193ee093bb94f67d68945794a6d0c018d4628e3d3b9e41c3a879a42273358453c726a0bb126f6b054145f53c9d9e349896730e291539b49e64bf7145302373ebf973f580f241e3ebf9d50ddb9f4063fbf9d434e1e27cbed4cf6e08267be6657132bdf4daa500908e2f3bbc9136cb2dc4d9358d84c0e3fbf99746762df033c03637ddeccfaf904e0f39b99f5a35f465c16c8eb7e998672bb4e742c5d574694585c1e15ef8a86f15cc4afa70b6f762f59d03137b2fec1cfef65d4f7f0f9bd444162f8e59b597f7e33adbf17f9024c219ecf6f26cb3ffe98cfcfefa5dd3f0e97735ff8f9bde4ae80bd9f74f1fb165320de48965fe2463e7f87b107adff49d83f8f6f284bf21ed9ca8a3fbf47ae1e27c4740c9d6234d227716ca4519c5c25636455d434b16c7488f4ee60ca9e907db2ece1818f71421a5d398102d6841358e9137b0205e932db838ae6318351ee0a309a7dfebea231a25529be688a945ed4681947839470b487246a53129549028b9fdf3d753d6751c0f780201f1637f84f04fe3f38890f4ee267340243188dc5df4384888b7047c7eb7baf788461f87d455a8528177dc5d8e3059f889708530979cca563f9efed43b5a1e3d7d0f1df5edca1771ad878568515f87c02863de0789b55c1051ee5415605165c80a118ffae41c7e7378edb873fbf71d6feb1e37b1a65fff8210256b3e5749d77a0e633260fc36845bab5f14e2df310ce5305074997d4cd476df3cdd9360c8eeb09cb3345f998a6196d4a2f7fbc12c7375fb037d0b9eb463927d905bd45d13c8ad88a2e1959fe9e9ada20dd2465909cce8b180f658302675392c5eb899bd3b833576bc7e0b97d9e756d450e0aaf3234c7f1506768a4ca3c55c3a4aa09f1d43922b5998dcc937c6d69cdb595dad38b143eaac8e74467b91a2b0bdda471ce6e3423c81a56b679c9ccd7c9f0382ee6eb95b6eacf54881dbdf8cc3c37739b8b979a31a14966925a5ddfe66642432d3bc378313b59d023527ea89e579040ea806672b060ba644e62c890c8dcc9c0f9a6edec6286170c9f861da39a974e564a53a1ad1d3f276317932214b34491172537a7a4cd2d63655e1edbc2ae8a6652bdcf521651568f0fa3295981c234e73c1891393affb33637cb6811307fb235cfb8d680f22e9866db683692a6d8d0b4dcbd3c8f5e901ea6ce23db66a2b3192accb3e0f2715877d448d30c339d517e5c708a5e7cd605ea6bb828672522d06e7e2202df3cd6d65b696717af59aae595ae5e5b9d819ae62024a3202fb423360382b7e2a52dce9316e44cd2c57bf333b64dda59c42b46cb3bafc92a485ed3bc86343f833cd20e77662e5af14b16a7476341ce3eddf9ea6d674b27b5beeae5536831afb34d754593ef419ab9825876ae66e6b215cfb6e2045b41aae87aadb7592a3ed857c39694541acf8a6b51875dc781d961353e3fb7f82c967bc48ede0b4a9c303a64cf8ec95559a54577f3b491c87ef049efce9714aff9a62a3ea7a5b44a899ac509c342b745c9fa2a6c0a530cb65969f07029e1e484de58a852b8e7c916cc6a859424847304807bdc977f672e98744b6d5c64564eaea5c9a8e53991c9493240b0c1d35b0eaebd30174802773b7772337263765dca709542aa6371c078a6ef409484b547646872480620015b6a55f1a6bea6701b6aef51de34992673816849213d984b76774d4a86e545c590eee01930370d9e967cf99e367fc12d0c26155c1d2ccad7dd06d36b9c8ab713269c0fe423b22f1f718168dc1705167321745865d4189e32c74e56afb692941c347a4773bf33aad88434b8ec9a6b2323695ac9745bd59127221653a5180d9899e8f1f26df5606c6cb4f49f90b7918fcc7dbd2223a2da2678409d5491c918171992ab2ccbcc29f5e6c459f9dd463370b08cfe2aa27e013ebf8bf08ae88e6fef3f7804875fd883c3233ab2c4715862062dfa8d21832099545dcb96c7ceca421b32c6c396693a4134aa5a1a70ef8d8986368854bc203201002055a36c47868ab6cc415b2e5dc5a1a889109dc4442b5a22e4706ea8050117832a8a64a6945655b5a66585915d73922127f4415ce451c07b086b83839f100a211fb17b28e916a2c231b7fa645f318e637a2b8e83337690a288e17a7391c352750b495fa0595953352d4ebaf0e7b7104fa7af6e2094fb8766c687360f5741ae61508cb662926f202a6d0554f43f7c7e03e170abe82cffbfd7bf2f09c51488c7df4ff21f7ffd6d41c8ffb117611a8b087bfcde8ff8b3fd19e47f03001b1f3ef5dfe0db10d1ff909be8c52e02fe1b37f83e374143cffe1b86bf17dfa2076e82861ef267e8e06f9450ecbdd8a742fef2179a06421231018ab870d05011250d3d6145617972a1255834b7a00c1a9af522001afa237e7efb24b1c0aad46e299d534ad94a80540f469086cd335dffc8b1018a05a894895d76992998045014b319000003201c48721c86514814d10314800432ba4c5c543c44948c201286826150180c04040261000000008401804010100e85c2b1b990ee03c2817c31ce6825660ec688091fc0e3c9e6a86584e2f0e01ad0c31738fad419e1278f954b2c88c61cad542eb686b24b815a55b11098461082ce2040104b916820eddcee1f734c5f6e04acb792c1a3fd619f8cfbb7d28774032a4d3141d251c101aac70fc61e81e7df15668412021c0d2788fbe877f439a9dfe3948a160670d234c37154393c879eb5cc757183ebbb2c30db4c2d9bedd09fe4909d8a34b8df2ba821ebadf95348ddb7d954bc2861b1b5ba1a3b1cc40c6abc3357b4055a1e4316932a1671e382300fe71826960b42c08386d2892058490a7b7a2788394de48789dd22d9c995a6ef58c8e8690cb193c6dda3c88a4a1f005335cd6150941298f3e4e64a20c08eb447eb658ec1fbb98b0da8ca7fab496b25a9c633cfc61dd5f12b4ea9d70ff8920935f41272fc02404f44367e59d5cdc97af750ccab3fcfb6d63593fd9b56190c0630c0270920d5b3cd4df4b2fec4e68daaeb6b41a69b1fbdc066c19de53e3217938c61a09caa825e3d255289927872ae985806647853d596210eaf6a8d646986fbcc7dacc171318b925a2d6458b9ceed3cf26b37da35b00ceb597d8e9dbd6c6b386ad360f94248acaa8c1b7a69f8866c0b84aeb71fd9bd065b9a56c81a6be902417a2886fef4e16e7d0333fca6d3e47c7c35154a18d391c319d24cbf5ca96a3ce3d5d5d2eff8826d23e737afea3714696e5eeb1e0d65163fc9950a5318b7d00de11b50fb717d0f6b2c95ec5167e10b5afc245b782d2da696ae6cf746dd82211a5f403fbe068ae30d7a785d4a6debf4e77ae57667841a510775c41399629279c05cb05a0bd1dc3354ff1635342a028a7de030173387103b7f9ff416880d47f0de9bb08ed2412d301979143ce1cf2f3db44761e036b8f7bc18ee1e0598e3e32477803e700111e2a3f84325d86309d64de9565737035f8b7b67491136b9bc51c4ee3da86f445c947a46d094a19821e15b2a921137ed3df4add0a6c4292d1d817a10d131d0926e0788fa4fafd199d6a5621d26388bea719bf6f55d3ec943e740632abf7a24edeffc69306e866c8fee66f2c436535d1c145a8e5b05f52d2b260beb9e8f3139b2bfb8783eadeef9a5e48e4c9effc3a1c6204e329f8c3a3e67d1f8981e4b9140197e4a372be819ace7f4932d6cd81674d19ecbf3755e0124dc968ab3bb30d616e933b33c87942a3a725e5edf481475b7fd069a479bc1e3dfb583317f0dd3ffa73d96bea4d6155f7eebff2bb9fadbcb1fc0e7afa9e5f140286c41159a700b4fb948f011d6b0dcf00b6b5f3daa8424f85b3326c84ee8e61053ee95e0fa680deb3e24c1114f66977440d490a17118a11896ad0b5445b7bb1efb99047fa688f107efe469124cd8a58a14d96458a460c222d2eab5432bb1bc2c2bcc4a6c055322c997eb0aecf0f53189484c1289fef8b43904ae2cb5704737fff84955626c4615c4799ccc05fff761414b24abb442fe13eb1f047350e9744e41439b587db6323f57105053e92f5f3cce973950418c4b00e33ae7177e8144d143367a322a06dd395540a31a7f944279bb44e0eb4741df2ae36d93584e954090e8ec4b16e07cce434473cc5476c9fd2a9bfcd5566b46a6e2413e12a9f0b4cd2e1aa61a2ac19b8d6381f2811f998b0663f633b66f5ac883d784aba823587536769f7fbbb141d03871ddd7ca37e99760ed245be2cff85a5b602c4acf1504d17ada740da15107d4a5369ea15692aedf396704acb3148991d76c45629d9ed989f336efbd03ed361280249545f336433583f43552ff3b38f4e2a6743f05dc68e682531a13ad86985741b7e632edc489fa82bc0dbeef4e10cf5c98313c9266ac71c2127420d8911234421eb4b880502a6338fefad8df202e47e8b4b0258ecdb0975a1cf9c9b065b31384012bc7d75f7fa349408b28591b20b8e3c5cd0add60382a12aef7b5fcf90e1d6612eead2ba8b9271861be3e4dd16f956c036819dbcea46d462e11bad826effc220ef225a52b7cca4c2603886376b63c60406379460c11fbf49b6c56194b0407bf2d11dc65f4c8a7d60bf9f287aba75121c70832095b9eb01cb5ea9a687590185ed6b7c4d8a99656be5f7068428f92618f1488c11911489f1e976b3c96847885d082069b10231ff97d84d31c47ce85a87161bf2b12b4784ddce297ffda65f3e2b6d034488c484e9796bd201cc4da309dec1301916c6cd953f9f232f37bfc58a5b4a4ea0b6c6dec596923e820c7760f69df724c89fa09e40c7d3ed3950035d591ea8116154533ce0db7aef25317f2baa539ec377fbb7555d59bf71b569569b1b581bec7da442c5e0b4d629f9fce0478bdc40237d457c36663d9d9c3a6f60d9b80c0b7fed3a6464c03376fe36d3946e811faa1eefaea5f4e287ddca5f07b9dee2b049772e00611207630f2aceb63472289538d6bc268f54cc1d1d6a927472902f601050bc489096d13b964f224e18ec600f5412496c6344e60427ad5ab343a7a2bdc8191ea6c2adca3ed6d432b0d0d638448ee794d05cd2c9d91a2541759275a9d988268b229a3c0bc48cd4750b245b16d000c8cb9c877b73c79c91da26e7920b16b8e0dd0b43a58e1605ec4a64b28d9c410912417044bf3b6c3c90ad7f076b0acf56c313310ba08d456358110d4b7f305192e44182d93ba68e4229adddf3149f823682913830c155e08e011340f9231c02f54a5bfe2028d493c8239227521d94e28dc57345dcd096fc1fdf68b982017fa4b188c6c4e637252bb74d79280a1b97d947c06c0fea239bdbe41c85c84a612e1862747a5ece48e707c16688ffdf660c90edcd336d6b1e57636e4a14631056404da0b6904e117aa1256dbce2ea6375f244d5e77b0fb589334dc8b8356b4cfe4ec707bf36cc03e3370ddb333d14a66cac34ecd1e8e49697467077c28f60aa9022a8758428c1f4006c86e7dff622c40e3ba53e9c7c2efa8da74a2c7ced1bc6e85857107c3544ecbd4c55c9e57c3241f81236a059d7c3eef737788ab116567398af47211050b4b04e40646dbd3099824f309faff2ff095c4c4d5af00a617599cc7fc1e21fc856785dfab9162feb535e06393810c2cbf3e4faeb2db481f09bc54bf5bb392c8ba0988e4e377ca3642141ec4fed997eca94d0448c599595626abed054b9d3c5c65b6ae6ebf47ee7f7c7f1eabeb3004ddd318aef21114f46c6d02d3f98145cb42d1b78a5320285c0edf62cae572775d050ddfbfa4bbac92e1ae7fae53eb6802f813cff50c0435d0b7d25a0d9d3438574a564334589c5a523ff524a670e6aa70ea8e6aa9ed94e1f72e1977d4223b3f329526fceccd942998b575930a2ec9aea4eb5fb63984d964067cf236a458e6ea952ab547978bbc8f8012deef6640c035c8647d8c6c0062bdd840d3b99cb85267989c06da7065957a9fbd55ed8cddc88087a739c7d255add77fc023cf8faa44b9543dfd3d08bc1494d947e4e300dcd0501b3f552d85963c83879ada345c86bd7b3ef339446b9c255541014ab38d2cd0d7254a380cb2506fc31fe8d39b30b69c036debfe1a18c46b70978423bdc415536d0446679c470f40c6455eea049e3075d478c3483e71c2620f8131e2325e41fa6b2f8d44178e1702be22d9072c1b31a10b0c011dd8a56df9ce197027d705a11d4226d9c571c031fde8929b470b8090d1175337ed9d080a27db53d2a343d52dee0e50cc6fd7327245672280733938b11338bc531f02b1619a487a71a31ba0590449e6b000d8deb1749ad7246e953b978faf04ebb96582fd20c7e42734c8b2d121c4118d0aaf4d0222aa1e1a2b3f8d18d87862147f0b8332b6e034dceb66eb6817ab5ebfd5ee2335c30aadc0ca2eabf03678841d0cdea6901ca6d66ef44dfb24c82423c0cf769322c2145a554fda619ed6bc8203f90e9540897fc6905407d05f5a589a3bf0a848802b4a9cddc96ad1afc2d5f039c501b0ea64b25117db8926a03156640b1557a57daa2cdd2c26bd456b1c33b0e99319cd7aeb535a5f5ccd9a43ac6a31ea20dcc90f140ad5375fca4cc39ed0d6ef12d894140d99f6380ef01e2f237adabe544136cb244b8b2d1364f41ef61798cc3eb870b1311f1db966684f8c5315ed4e78169e6d8a28132d373f97b622a170b8a76df533eaeb5fb87730315fd008905e57e0d58b1c3e21f19967d45fd7925919688e910de589d50797a35a96773bb8f479b931d49f41b21cea9ed02508fe000c51b836676e2200bbc34d388cb43000916da90c81a429d56a0a8169272cc357b3932dd4d559b034c33dd6a7deea205ab33e3b238a16cd115e8995b7b366bd7c166c8e5089edc0acb58cecd728406778686781639c805defc796f2c12f9dd08650b0db7beea371888b973509fba2ebc93dc79f1c138d09503352c56efffca3d9a95ad4b9931963a26dd5e29f72840e3539501243fdf4cefc2823483ced29f6ffecb49a9f9cf8741bb94fde083be04beb47555112274bfda5286e68b85ad58a21cad2d853f77e455cf5de7c16b2ab0d053c9de6625629dfdcccc4b2023b47caf694d1ed9f72a98cf8276d3e0b0898f47ecfe0be02583b8e5dce3867980f7511dfb84ae2b0876b110e976e281e7e56491c78b8fde8b9f8db2a5f0c73d734d50285accc85c8995c9782e1abb59616958e56f252340ce06276b1a265baa142219fc6085ce4e7cc086d219223e079e0e252141e5eac150598cb922a82d8577d4a38c84af92f19adf1e8ccf6b24a542fdda876264fab8b55b92756dd28c91482dd6f550eeaabedb190d37a54fa20107eb737ae9421a3da2cfb092823e03a1171639a6b59368a6a6a2223d74adc88712913ad1fe81d97884ee2449f0dd88b439c33b4cf1517b8c655dead9a95bc178fdaa59c6d745a98b8231977e33bb67b63dc131ded58aa28e4f67c82f1671c638b752c6f8cab24abe77cb024404e0985ce79d10670a15954d8378b7c8ed2259a12bd95616bcb934ec89b888f2e263021917a8ade37d821888de90e28b79511e311367fcae90a0d4b6a24b3a1583205f9057dafa41450726a7ca9234ed28b126a525fc832f1bd5768c99a6691cb563e6cba23c49fb37f77b874c71fd8b9fc5c27dda1d1c9eba4afcf51e896d011ff912ad7c5bda483dfd5f9e49b0e7fa001290db205ce1bfda0e2ace96fd63e3fbf06526b40a8311400c7ce3bbc1d35bee3cf732ef583827304c6cea20e4247ed4c76bc3b6747ab68a1b7028ff4bc567d0396fcc90cf8a1cfe3abae13f49e41fbdfc099e651e8bc1d9421f488d4e68e16c101c71dc7ebe7fd7f107ddec0d179f4a819b43f4af876fb4775fd27d81c20d1262020740778395f21bd33fd8e3fca19ee0fdacf3d503b5674ec2f4b07903be3ffd999a27de0b58ed0f633cc5be850bf23c4d69910bc77b4b0c83a1b6f4d0f7d65065da37d82656d5f6fd37652bddf9937d8db7b86d70795e70b101d4e9d75a7f41d7fac33c43fe83eaf7076e474d67707089c33fb76758264e7e1fdeff27cfd860e7cc7dd415c9f0c15abd37b431262d87ac4445d76f58250a6f3ee2c3b0d1d7b87ae238ff620eddc74141d303ae2e9fc66ce7b8310f7fc2af0289ce130e8283b9ee800aacedf2f6b1dc3ad925935f20e1e949367e26d0e2fa8b382f388728e3a875e5940e5759d12491d282efb6774b49db07602e9b3df9f7ae7b77adee283f0e70a823a359d699d97ceb38eb253da4ea07d5618fc3e9dad9fac0e20e1d9fde20ebeb377b6da09043a6b1bfc09cfd15f40939f660790e0ac3dfc6e492648da327d4279921922f7b6fd41a7a7d35864b9044bc3400e190854be5a506b2721ecfc8841b0243ac2530663a2290491f6ee033a1ddfb9c7fb0c002c757cd3738081b6e13c9305bf3be7523e487a2e00adc3cb4e20fd4c0f089dbaa3a763ef00bdb46832c3434673efea07bb110424c09ef5f74827a83aff41ec784a122826272f70e641b0853b54d765c2e13987d0482c48073a72b18e7b2433883008c8daa020244fb460abfd3a7482bae73c9f41cd597b01a15c11acf05d23ae3c5d676488cc418825328c89bad0dbbe93dc73d63c384918ca0fcef90619c5b191075d90672380efe4fcf80f72cf0f7d40f2ac0804ea9e554861073ff4b3f9a09ff25c804427171ddfed5cfb699db763cdceefe5ecc407e59e1718eba875e69dcfced9597742db11bacf31fed6f12b74acdf6f4e14372e43dd88bf4de777fcfc0ae44238430c7ccfb3ba3fc89ec1f901d167bda1c7ce79d40f92cf4548d3a93b1b3a161d7a1d9176023de7cd5778e7f7757ed80759e70a6e3a3374ff473a9dd3f97d9f035fe86821b6c12addb3ae1f249f8b41d01d0ca867b83e483f3fffb9e0a40ce7e312df27009ab2245268503a8530c3eece72878d64fa19b7a78881c80221497904d28264356eeff9cc4e4c9d707684d69f1954af03087cd6168e0c9f07fa8a66e164439a3040edf7a80e25eee2baa34e37bdc2b33be20e4365d67bdf0702e18b7369a09fe01c01a5a3521d61ccb34082224591d0d9041162e0d6fcdb23fc76ce0b3fe95074ac42030a7fe1842dce0540a031feea4c75e0744e9def7400a967bd8210f25cca07149ca397974e9073c6a03ab490d8a18f190ef6bc6e4d93a59c0fdaea2f96842c47fdf14240ea3ea33258ea1a26958118d336eb56e221c903842740d2650e8abca351fd3e88daa56277e6e9fdd9656a85f4446948aa65f71546cea5f7a754004f64c45a37066b8a929d19997bd2294386507b8cb17386024358b942d0b20794e9a573200853957c575a6c3e130d3048479e2cd5db7221322643cd4d3aecece746dc2f7cac1634482c87f6b770e120f26626a9c67438039337c3a7b64e6ac1008e9dc240a20c6d237a2ec139108428f99be1ef5e1c074c6294658ab39cd788a959a3eec6b71702d8fe46c781ac364ddd6a5e092df7db83c33a966765dc6d05e53c00b3804c5f2c472c8159ae4e1ab49d2b0dc52d65c9333cb94e736913e1723c8732a45f82412dd25f882399d38471488e45d9b681cdad5811cf8402e072455659a9ffe94e92aca773531d080458e85c0739419ce1e129f8062a04611677156c3097ccb5d6912dfe2df35e6a41bdb2cbd1d182d8aa609d53a9ccd3224573b9812a8463096a41d6b2209075bd589a83ee78d4ae12ab4295fb8d36fd2379a7b68ab90c85026ad475615f40125a4d07926d422dcf43dc484799044a99eb108cb557339bedbfb77c5f1495777c9ddc827c7e57b762a0b9629d226154affb5f0b109143ed1bfbb991453a6b13166172e09f95017c18940f7b09f1f1dd2410635b6fb2f7965b4a999294017906b205f005b3c57dff4ac42ace6543487e70c48dd37fda59a64fa03ca11fdcbe1f53732e9d90fb80cfb2e5a6cbd2177b84fb49eb943c5b9f65563f2dac648d252cbfcc305bfdc2d32d95e8d5306bfa57a20764d6f4bfcc306fbc1f2f3c7de395ddaac17bc17475dfefd1305ddef77b42bc193c1778413c1e0fc87485df6f4b9781e271af05d375bfdf9361ba76bc1f9e0f2f06af9cae94a50a883d09a4578e108b4102eb499527eb05b0c61d3b7a3c09acef11830499577dff67afcbcb4fb0460a21a05bf2e9531001ce29808f42f1e04c47776fe74ef553a2b54d9bfe18d13a316dfa61446be545b438b046a27d62da40316dfa43d146316dfa51a23d9a36fd27d1e660daf483a2d5c1b4e93789f68a6891442b859d62daf463d152316d6e9d807e19714e03902a96382902c8989c57a28d62d6f4ab444bc5acb1544c1973da236beb8904b59cf8225b9bcbf0ca59d38f711896b756cec6cd53cc4d7a25379e68d3a6df250b49144fee27bd32f7bb2075bb1c39914c49148f109d9d971d3a4b997449ca1d94c91088e47e140f4a084a06828b47724c7b507ca81a230409eb6c4629a5943a80cb608dee6e2fbd74f7a6d5f05a6b58cbdd5a0d8c7344e7f98b4421d7ef7e9af322d7b1ce725ee41efffa94b9fee7d222ecf7af3e763dd3e55fab8f9527ec6a61b15cbd12e8ebff5a9c2c56d7f9b77f8f78dae018119235d38fb9912958a3e789cc19c370da4c98eeafef0ddaa8a3e8a3d8e0cb053ff73c3c1a09e0e7fe04cc7923ee46ece02bb441473a6de8376a867d9d4cc027e071c638e305bee8cfe7401b74ac1cf8fa66e6c09c98ee18313a10e7879082af2ebb47b137db3bd59f21a05bae1d4340c911e449b7640e07dfbf6c587f4e5c7fceeab59ec2473d8fd383e1a3fe84704485e0eb7573fdd95b5edd7b94524a298540aac6e6777777bf38f79e4ce7bbbb7b055f377f4747b5beaca5f3adb5177cd53bffde9773dc880325ec5e6d451c281df8f2dcef7f238d0d143e94e93f93fd4e245b2ae4bcc8f7735e643bbe788147b216b9588bf2b537d7166f5e41bdf1b5a9fc4ce0c2908226dcb7143ce18e78c45eb325a3684a98b9ff35738f377283fdf8d49f5b859797fd5f5ea65bf2045f9e978574d944284fa2263da63c899a20a9b489904ce22665a6df0496c98a6d9e443e5964fa552953a40fe7a7a788e24c1f8300867012f90c65d234897c84f224f2e9c97912f9f4e0a383099f0f916906ce5f4f509e443d434846540a37d4404bdc07223e08a93ee8f8c0033359cae2c3e9e123825a9762887b5bc81105d1101f687003951800c04654048825183081e2e3e1e4e21d5dc589206850bea0038a26464cd4a00517105144468bf97608717689129808b710dce55ae829305ee8b06006430d6234281949a2c6440983d1133a9f0e274b863c7cb4849639672205ea87104278bde252b8dcaee5f15305cc081341722c2922cb941d90d072832c98c04ece63081a440d40923c3184092c7c14e1e10644c800851652787c399e7078079c96d5d5c103804852c321c9952458c04316a01854d95102067e382ad6800c8ac00289500b2f284183081a4cf103050c33f47cb0192a4a5b7676c8420a904f8a2080b40c5142871477cb9576242b8e540413235c860072b96108cbc140831c749e8002ab02841018600287233b5c208203ec861abe2894071be76d41b10d6a98b28323db0f0e2ad3ca0b3e9e60a108901e598aa0b49881039621927414c103061304dd7c724cf9b192c41215ea992bb42d5908e19061082ba440f1e343d4630b26444c5547ff205ba285161da8e4a872031ab60022881938a1e50a2bac1b2d092242104df450c40a123510c1f2444626035a1354ccc0a78622ec65318412290a40b92c4294a80ac124c827c4954173188f18cb124c9a4009295172a4db00196650030f233dbc3034e489a1248a08a2b16024968ba076100016e8200a0d663a58986201263d9a00b282051c5070705a62b4c7951fa2c0c109901e769853b4f8d163871638b48045c90f272527c1852156627812a588942119ac006d61238276039399c9631863f18c68f9388ee3eac5583a9e2e82cb6227125a9e10d9e2881990da11b12a925002cb9392b653b10061052b907e0c69c202202670840db12850344113fafc8710f8864e922a4410d14356e589d913259ad0804410131e9ef030832b2e3044942b43362458c4b058f2821c10d1928389e6a1e7264d31a9382a12c48f155ba62c01a40543418250b223040f5180b4852b4f601886a03532eca0c20a2778a04287034531620643961e587af001009e154290013d2ee3ab5d26b787bbe42ab93cdc2477365d31b5af277567a9275599bcbd8054edabd5a7d12e731bfc061fe244bc881b711ca64d7b0e0e03d7c19170a2c7e08a2e03df61daf4a7c41e314870594e009755a2e390127d87eb3bb8cc26754dffd28e5067e70ac94d4448957db4371c1b849f0da6310c60a5e0eb4a80c532993c26e685c11ad6592b6732e11875e00841c2faf5d5ddea6647bb93bdb63ecdd52bd0e74fa290bb863325bc94c4392b7577734a4742eeeecd7324546bad61ad25294fbe9733791321e7224e1321f724ae39f471a6f46b8d118284f431f80273a64db754c849e5a472fd222a924d6547a624c618635ccb51cd69e3017d384f501ae31e76cb446977b7cbf6c78b6362ac0d438c6bb52cd6051f70efb54fe9c781ce8125cc8c82df97fd8b824007be68ee7047bb7baf8d2e05d90a8d3f0af4e7fb78697bb5f7f230fd77c209347d1cc7711c0df728dc1182843477c4cf514a2905f37d21a1ee7677f74a6badb687f6587b692c76397216155109fd6d2b53f7fbdde87df734da411bde3853f97e4e2a95cad78aadc471dc0aea8dae4d40aa6e2d7d5223b5d1fc96e2b0e9aa59fb2ea6f27ff691ac41dbabbd1ceebccf049e50a1cb0b4c4c4ab562c9cc9035ec48336da875316de80b0fa6cd0776084b09a95823a4dc4fb64810fad6ada46e298d16878a90b4b79b854d1b0ab3b7dcde8dcb41b7d4244949132632c9aab50854e02746afd769113e110a097d94743431228a1a8ddbad40e38522547050840e11235b163e3610e18328098b92167c2b648ee38a601ba6c534d70a2a24f1602b1242168bc988d56008922542372deec2f7f598516446114ca3518296104bc8e549e44344888f4f8e4c3b30e4424f10772fb6d59b46a3841e2bde86837027ec396aa22f519221a2d914578f194d84383d426e68061421396b33b00849164e0b218bab618cf10760b0a6e78ad13c897a74324d9e443d39f408b13b7352da187745bd6cd43afb065ba97b5ec517d9eb38aec39c576b8739cc5955d7ec89de10a5b74ebfe1b3ecf3fe75b5105e7f3bd6b16bdda28e95a86e373a79be6561def4b4319f3cbff64c5be70db5386cadd36271b8f82277e2e926e4324e4a5ed38f8ad5c8d3cd5a8b390b6343a7e548264f4aa79bd7348c0ce614d4b4a5a42424242312e646c2dc4c9984b9e57e981b4c128c52df58ff691d366d54d93d366da837d435ed09754ba85bde50b7ba366dfa4fb7dcfd2858df90b696fb9674523a2dd9da74d5d72afbd835d5ed1a7d3842b4e83fcb384fd06799467707beef04f4b1481d05f8fd3a1eebccc513d287c56a122fca7646a7f8686cccb7ff9afe1f8ad26e378da66ed5da737430a74e6f5a6bb7288d7dff1bf963b9779d6d6badb58db18d971549f041b6ef9dd375d20ec117aee0abbeade2b4b1e0ebd24880fbfb3cba118fd86b2ef8b2373207beea9cb8f33cefeb3acf046e9c31c36b3acf7e2cd6770277ada9ebac7defb3b556dbd58eb8035f3de7e781fd2670d4148626d004eeeffb09dc75da2058c2ccf6bd561bf6c11ccf53c1cbd5cb76f44cfeba913fd064c2604e074e0c7a273b8215d41bb5515abe0fe5600ed78f430bb9fefe9e2b61e62e04748bf7642d9a73ce39bb1e4a29a57d94fbbbbb9b1331cf91bb574ea8d66a95ec25ef910b8e9c882314e2efc00670d9f41f3ed96faf28f70b6f24bd586ed216e51ec9a25cc713d4f207ec0c7fb433e2038a5156508aba514a53312bec2c53d495d96ad451b7fafd5522eb57a28ceda9e5c64a666bc6aa4fe78d3d6267b9569791b0d61aeaa86fec91465d79127f1464bdea79c8fc6a842028d03eca84199f5953d3e06bc667b6fa532ccc97979f13ccb9f98a37af54aa5ffd09aa5ffdfdd5988218dc51e6579f02d68c0744f87dea29f8bad9ff657cd3cff4ccd05c86ccf7cf27fd5d3e25a662a9d265acbe4f3acc8cfa396f2ceaa7cb50a5626478cb0d23439f95c365b74a5e46b266da373d2394fb6b0a944cc55a994cc5f28ccf4c6dc696fb3daf4672b3ccf164abc3bc99b3696ddbf6770c28383e6250e549f4039597e96f71cc9b599fd6d92c6a1db3e50deb963f5d3a922443e231a2df525a46c7ecd3fb747c91ed0f5b5a9b8d756bded8d2da74cc1bce85d9f276aef41a7f8cc390c5f27fae63d8c6fac6e2982d177664fa8e63b668f9f647d98eb1fbdbc0d2bd7419268f2b3dae2c3dae2c3d7f972c747159fa24165268d4a8f255f15b9bcb7031ea1a7f2ad8284462257dcb51dc1d5d6cdd720f5566af494eb4e9e2de3d160363a8ef6f327a7fb09c2e12dcc9bef4492ca4d0a8f115bbd6521c078e15619d4d9707d45fc216666481c3c29359042af04313225a2c8ba0737fadec4a12b6b2d77ba2a38f61166d4ec156ea9303b94d9824f40b3838b6d09e944f703c9145c1d172b560d25d0b9e4d1b142e8ffeafbdbaadf55a6befbdf75e1192dd57dc6e34da1521e8006e621a1b6d977cb55a4ccb64302f349770da6c289c242a050bebc2c6dcbe96e535d439b046b517b3c20fdf4d17db6bed75569e79526b4f9c8bb56bfa5d65d87ebaa40af21dc3cf3aedea74d569d38139b46210e7d05c614cb56f6a9e38871d24659295058b0c6e394ca18434850eb61c0d093b4f22275038b1b2a3cb93c80916486005e89821c70d499438c243451622b454119bb2240553e97c8b12239c2cc91ee449e424c80910b2465bd637a7555ada69a3b171237fd7d513ba7a4213155d3de1a4e37c8bf44d18e4a5b4ab575b6db7d76a6d554a22eb9212aed5d6cbd9da9155286848a813c92aa5e6711cae9d672b4fdde956935548e5f9becf04f69c4c0de604d18e8c908e926ea1c965874eee7f89c190bee3e567a2a403f5e4d0ec555badf734da6d5d6ff781a89718950c8d18e3525b6d8484a49700c5088a3194e7b78c26b58c0b2cd97a47ebd87aa785dcfaa60b6a19b42c874a292595d28822e5f99446a34268940812da920d60a43089b5b423d3f2027d739fca4b610d853308846e99c790d135afe9af34994c269369b29e7a9d7d2e21ea04fad7a56841341a8d1653ee946559c2dc97d96cc96c369b59186c070c068381207a7081699bb669a3952693c96432c5a8c5c4f8dcd6a14ea07f311df60f86164473da47a3bd943ba5975f599abcd96cc96c369bcd6c0400c345984ccbb22c4bdaf77ddff75d80c42cd3b4751875024dee82b92ea405d168b48ef695e54e599665e93637729bdbdce636b711c034809a4b69341a8dd6755dd775655896256e1a079afc43711777345a108d46a3d1ca72a72ccbb22c808d4ba6b71bc618635c6badb5e28f46e36e1734f9d79d2ef6aa47a305d168341a2d86a900269c2942ced432996cacb5d65af38dde2e95795666655666c15a6f35e9abb7aede70bd71f576ebcd0026031ce0000808c1840030278404dc24e0e6c69bdecca7798e5d6bfdb3586158233126db23d2dd489a4c782463625cb8800102085d7319964a91910d29694909cc76b4351f42f2c26a300ce61a07623833273a5800948593156c2e001d013d01248513201d80860029612baf1c61a40a103f7cb25421b28203c90e33004559800c44704005902355645562e03132d402161bb86009084a04682d48f8e9b0330488098e0ed00e0e0c403a4042706af8a1fdf8fcf0f083e4c7c88f0d3f3c3f527eaaf8313a32297ee5cc1b4da9109892e55dc3311cc3b18e61201c73192095d28807499674045cca7ddad1f3e960a00face1d8c05ff5c20e56f98adf8e0fc624244f1f2cf79f76fa26d63754a7ec1b3ad66ee1578529ad111f8d0d7ffb2f77af31316d9a31dd634869e8a3306f70360c31ee582cafc13726c664b2ee6168eba3605fa08dfe9cf9feafe91c9883c1ea15f9e6bd1cf77ddcc77d5ceb82353e9c1b8424ceaacc44f88da4cf32f71c48d33d0af8bfefb08dfbbc37cd39e79c4a99a39452da42dddded962773efee5e8f32c77dadd5f6587b39ee1b71ac7c228e951338d2d8c0197c9cc191542183a7c7f934922aa0fef40eda40dda2ccfd89fbc856ba4732e745991bc9fb954c412d7fc067eedf8f9751a98b494886dda2e324bf0983fc6c4a7da3ca534995faa566ed087576582e8445647e3d5ed3f88bf209752b25de1fae0fb3a6ff8b326fae92d9ea3bebd60fd3f5e43ab940f7e736b93eb7e7faf005f48adcdf39a599a0e4fe10e35a6bed3e2cb91ff50dc9fdd493e2c96842b522db114c2693c964324fe6c93c9927f3643299a8cb819574107ea765ed8872ff89fb463ce227bb1cb95fa6c3235692318c9654468fcc7a60aa9aaaa6aaa96aaa9aaa06c3300cc3300cfb804b92fb5af991fb29875979dab824b7d65a2b375e2b370683c160b0d99dddd99ddd55cf6ac90a68d5b39aad8256b4d52a3bf1a45bb92e6c2e6c2e63babb7017eec25dd85cd872d7d13a315bcd7c06a68542acda17ad66eedf0726550a34dbd14529029090b4b072babb85b985b985792abf2881120c06b4178ee0243152865284378447c25b484b82029467f22482920494a13aa3b6f6d29b566f96ca44a02124674d041a44a021cb47deb079d9b41b95a15c800d88794133d327d2041eccc8b0fc449ee003d64a95aa2712059f8a817971b1273205a24b883a81a67b22553072277205f903c0884ff49d501a5bcfb84c65ddecaa6360d9679efe8cc074b19e3e954dd7eae9d3db74a99e7ed3a62bf5f4bd9cae7efa6ec34fbdcf04be5841852e9c28f31feb93adb28c917c45f6bfe1152e4c4fba04657f97201af0c9971fd93ff563e6f4e48b95ecaf7ab12283fa154c1256f8e427cb2eff7598e671a699e1b0a8ca32224b8cc0b4a1bf12a96cdad05789f4d6342fa7cd7cf92a7ab569e3301e6cdab866e83ee667625e441791a49388a436645a4543f118bceedad63e596dba30fe640eb03132d4b3599897e0ed9ac46da5f764d36ea6b73335a70d25674d4669a4979c3be1b669435dd0883332b26b6a9125ae44957db226a1c1f98e16e6e16061d34583b305dfd2b1cea64d43a0fee8ce3d69719c66d3c66135142c4848c6d43c58f68fa94d17f8fe1f28da1428be744fcedc6664d3c653321790909cb9a564d9ff03d9745924ac586731b099543689337f286fe696fd3b919cb121fbc7881e2ce5373e5a63df84f1b9704a63f39e7197f1cabab95de5116e08abf76cba54798e9c85e5394be55967350c834c63060dd519e58e64fa3466c00021841166908d27bc508ce4989e35173040086184dca50321d2b4da821b6e56061995dd1d28659e5074326d89a694e9879896942907c4e7dae1388ee3b898cc72d1555b6db5b533e1119b8884336323524f73cc48dec831eff2a42311410e99ba7841ba0eeb3d84bc0221d318230018404325fe18c3650060f41e1fc7e935420c98371e65b6ba040f5e8c242bbb18c9a719c99628fa12b3a6ff45af326bfabd47a86fbce651e68dd760b6fa5389377b0de68deafb7d8979c3c4bc513dcd7f18ac41c3d1d8fc357bcda917a944cf5e53899dbdd6bec49451650173268074c9f4e7440099ca7484390d40ae32fd17ee44f32a71662e8b4f33ce69937a99d4cbfcea3f0bd658a56e6a9592f98f9a4a984253680a4da16e257aad5bfd33a24be956d195f90855ef7d4c0bc98830d9e5c3f432e24b76f9005f4674e99111431acbf4512f23a2b2cb4748ce2219f1945d3ec2d4935e5b3dceab915421af5e04334b1c53313022e946a44391fb5d447266a99175068aa42d2dcddec82bab5d221f70ea3e6b40efb0c487071df6f8e60cda4d296d1a826fcec0f83b91b433941d22a3f4b169b4474e47ca1e3b64ded89fd9ea7f7ba4a7dffecc9b391f14a78fa4fdeb8d73da74f66d27dec7a2bfb53f414ba9a5d60554ca2d031568952dec816fc22057b014f5fefbba2321cd9e572937697697aeeb52a48b08b110043eb3ae2a216969a4cf704fdfa8c4fb3b54919b1c4fb26913149230355986a9e19e6ef98c96fb245bc16c163454cd3ffc170fc6f33ccff33c2421cde1f74ff0157e3d7da3c38c642bbf4c1aed329ac6cf1bb323f19efa8e4e2fb5d2580305d21c87e048b23e7ba38fd48ea49dad72f74ec3380c59acff16e934a56eec8e70aa84a4776fd3657338fb34dc937548a817ea21b5fb0c09c24994044c042e18b87270e950de2056056683157c24551c1400b2e50710cd05447240e1c1e10a154a8c96f0174e58a600fde063840f1f275b6049c2a30821555ed09f909d2237cce0882c3d3f24cc9f1dc02132638bfa03964cc6b8efd4c504c602ee57f70ad6e86e6b0397dea8e4ad9506350a9532010000a00003160000281810898442a124c8b3244fdd0714800a6e824e644e3a92c782b12c478118c3310c87100000310010438c310659552b081e3502e61e98f8688a58863c2983f088bcb7c28124f4c45ed41a8fd9dbc25478644e18d60859ac28139cb6e672293e5a7a4021f5546cb85aaeed57a3ef4fceabd58f55c4124e8fcd13b6b0ce40f77eeb5821787638f9a1d7b24df8a62b4fe5220c72a6cdf8934076fdcc656ce070103a2a659401d472f31afc94866db417d773f464d7760b882579f98473809d9a0346495f63984d1f4c83dbc171dc5f7c7eda58cd898b7f03f8e5113ddd900171bc6c2a58ff497bafc72564aa00f16e2eb881a442d49fae200a2d3c1620797e308f5f32489920a2476e05d15aa83f2817e586a1de9f153a46a5e462a04cdf74a68053b4e90bb48bbe0157bb2220c1b452d2840f86af649eebb2148e6173ca6ea0da7ffd26bf8957d1691cb88a08ca7c8e99725a75481a802b337c558044307215211b2f0be6ded7fa4c2e1e0cb8e6158169d1330845ef16b9d70557a9dbc185aa1acb85dca354d72d648b3a7510ae151b7a5fb247c66e62d513c4a3f97779c607b75da857367aa3cf573e84709955743fc77b05878e6de47190d3ed2ff204228de8ce51a75393373474fcf038de872279d4806498ff33d032c0e3e47e6ec5b2025b79209638623c9fe4cbef595a1a613eaf5fc1ea036af5e7d9962c3102ca1d1b4ecf45e75996958746d1e2611f772ba63046eff9393a19eabaaf4a265dee5a2819ad70e8ffec65d74593f3145cd41918d2529e4dbb261a157051386289a95b9202fe68cef45791dbfd78d6968005fa968e0d51bedffbc2416ff4fe739c01475d3ba359c32c065a0cb7a83356906f7d4b4817ac73bd6591a2e12f432dbd8a4362766771a83bc80e323555dd03b7b8bc20897e0acb164af3b23d4b7782b775a2bcc45723c22df40a3c64d61ee27dd24cc992325825f2f19d8927a061cb34bf44914166c9d2e52a95d45a3b333dd9608933832c3bd4406066dabb656cd1102f6d71dad651bc003669180945ff75e0bb1a65fe93426c2ad675fe3eccf93a2a32439e195d129c9e61db5ee7994709726799e05be491b1e79c0af17e3ba2bfa652938639faed79f7744bd2b8bf7f3f22ccefbf35f1719fdc49954fca9e66ea9f41c245993bb8fd7f1f6f80a0929fd76e92dc7f812619d2941ac065e847db9a329d007444a9fe0d42864ded0332c5ea6709e7582b85f97478922032219544f2b543a21af4a3566f5613ef2f7d1c28a2f5da03b0cab00248355b85688fc223b07bf5801449a19678e3e0e93e06531e37f6b64b4963f12d5a79b1a420ca8a102d4b8388e3a1c825bb43e03f97154ad4e705e29052074f5a139f677aa9f0991f8bd77e309c0d1338cabe582549a6a898d391dbf0267a083650cc40399762ef653f210c45f3cffd7be7c44656aba975e878b6b2471820bf1cf4faaceb2595d30735da39bd9d079398149eaeb9016516e4e5df5eadc73df35ab3668614e5770aa3595dec2d2b968fda6d7edb0bb7d9c160fd429d91940f80daade70045d03eaaf6e63e4671219c7ab76f86e2123433457a8fe8c41a928686cdb46b5c34bd989d18cbb7fcce83994c93009ff6381261b234093ad060816cc3f991aae0cf5796021bb4a08bdac4a14348df3cf574f5e1f637ef93586f1abe28d51d1f6ceca3a4e893fb05245767eb006c96a25cd42efe5f60037c568529d6e6881339c1f09242aa380b7463a187af986cc0e710060145c1269c0178d68a6c631dcfdd88d18760e88390b1eec3a011182d9a0d9a2c641c5bf9085373c96aafedf16a4ce31b14b1d880be7ac9c1a2007cbcf0c53cfa88cbe5121b4ec02507ec03b9c349e7491b0ffe154fea28b8d0bfd90abd04bd452498962d47016bccf66085d514e61ba596011cbd6bfa14a32dbf960a4fb5cfc25a5d18048a1a9c6296612d9483dd399d8a12d8e889d80f3918fda6a4285651cc9d44f9973ab9ebc17700db9cbeb0509f256bbae9533996681f21b17c0b53f6f3d032b4f8ec91e219d4076b00a492637e02dccfd2e6115ab8822313bbbc1ffc0f027da2aeab42b11c7a2c2ff9a1b3efa09046da4218fbc54a519f9e542e46263454a777855ea3f6e09071f27086986e3c605371d3ece83ed67072509d96ad8f39466684d4ff0cfba2c29c8ce333c749ee424a20061ca18d8a63ef6e33263344e58dbbff0ebde57831e5f346f4a73d73893dc02109dcb3a765aff460655c88694a9158233a5a65b231ce46698d5412e1d5a99f0890bd74500fbcd06d1e99b502bd6edc14717b6907ec15520c9d2daf929c80d926e45735b9e83319ddff22cc220ebf70963d32402e587ba00c83cc70c03453ee06282e26cd4b5095ec3046b08a4e96259a857d9ae90f8e074bc60bef335ab43c177a12281dfdd151016a93b1660c55cb39bad07cce8affbbf3ee151662219d03e1fd983070871668007d9daaade9263130520a601fea65b306401c02f6f1802259b168c955c842f178bbd194926a73848e10b53cc3812856ec79cdc13529926c1a104ec9984d00f2e0c6c20272c880c86e0fc7d790b6778dafcf57e82143c2f423dc48cc0fe8fd4d4e5ace262d366c4bd79ef308a11cf50afbeb50338dd0f13d33e8524e15da75c03dde7902feaf405e240dd859a9e60ade4c0951c86d39d1693203467a1a9f494a34636b091f6e23cdbd8fad841565a57f34c09302eb4b62f5a5e48ef5e298ed23982e1f023ad47517af95cd26ad19e8b66c1e792e9df53d6ea79381c58ac1e872d563a11e68cf07a538b0bb28a73251c99e0ab18840274b977eb82e935c2a97edc076ac2e60720eb06047edc0ccbcf78bb8c386761bd0e12febd57631f6b3911c2ff6ae673b6b5b5b6430bf765ab7db82755ae8bb38d72809a586acbc2019351ec6d7916d249d0d26525fa4d44e145801dc9017d4c92a2611be3e5f002e5f9ee8ccef1b2bfacd377908fb434b194214ba5e4f51bf97c9cbd4829f393011c4bc0ff36339fbe488cc9c3ab4a014b404d7da245762af4257b80b4be92e09bd066060c9a8b07eb99fd930b45d07e0d57030ca41139cce9e6a65b4b94ae135b005021a1339943ea4e18eb0a63e3517004860d92493c4207959d446959887a19f3db966fe0cc431c8602f53e3f731e91170fb8cd1f38a6c8591b4385f97d3950d0ea025ebd4d33285dedbff8bfd010daa015d241b4461ad12685c7deb3ab368b97ebdebd9ff2c1b66c4b40b21c3e90df6628fa85650a40bedcfb8959d740b618b6b12373336a879878ca3173aba58ecc9f964290b84522eb10b4a22be481d8431fc708d325004e929167059663f3972435cb947ca41d868cc1227c3cc0b4a3b6f76b8228587cd59d61a56039aad6c63c9ad6701d24905854435eda1aec0375fd734ff72a24ebd4fce3d8e6caeb4d4a388172a5cab4a0a46b23d4b9b1650f71251fb045690f2944dc9c28098a0da4631d6e465a063d866ac1e3bca334640bcfa50bc051441d2f348c22f580400a4d116a88d4acc89db1ed5c0c750aa5248ceb16ce842c99582a83fbfd12c08926357da05833740ad9dc054005d861a00ec23878f7ddd447bbd588a770e9af99dfe5ae7c24e921105abce7e3abcb9518fbd34ecee78519c35523dd1a40696fdb6dc318bb2b58578ea8cc8bd1115a88e795c0abb7f1e477e7f230fd76d786e91b57f7bcea69ddf4e4e96a2e7100a9f3472e3455fcb74604c3428754d4980193a76e0bf608345f7235371f7162b261f301375360762e6253f7dded1e4dca4d213f60f9156bc7f428c433b54b689642e016861cda44d7e2d5afea809b3bd93df268852c7607af3a6c0c41f9541a985a202e9f776736008bc7139a1979a2220ef3202b88d7402ffa2c21ed91fac7c29c30ca1ba4a01593721dec84ed10645f8e72bc77a6381364402321aba14ba6a4a3c34120d9d6f087d00b1b5b14b0bbfa9055dc3be3c1d3be88f664be17f5888189e2b6db449949e4217e74231bd936017e08a54010d6021e4f781f16345a2a4c655ec611a5e44e21cb42157cfbd9a24b846c4d9e5822ac1626ee7a3f0b436c56375d297bdd82e86b36ce822fc7843ead5c0906672cd6f1a7705cd293dd71671b1aee6d3715d968d843b3b0253c655de98b3a703f3db633cb5f8607d54dcbc6e73594285dd4c1a6600d67fb2cdd80387a48369e5c320fa2e6f5fb583bb774b93a03ba88849859397a68f3923100ac7e283793859511b6c40e5d2a3cd05bb51f0b810900e4060dbd0a030a64c2b522f4114401cccc10d6a72cf5cb57b7a3a3901c5524ab0d7eecadd4df03681694e8636c4a73476e0b7f462d9f90ae32383964c268ca375e7bf5dfd576241757aa3e3ce77fa685a77519e43b108c22e07a266fb63acbf335095514eaf292adb6de42447460946b7d1b592d0b5144cbe2be3401835cb768c002ccb791184b63234bb6435305d2ca951b34a6131a1b530309b86951099a5559b2d7a8fd02209a39fd7afb895b838a5903faa8bec3464bc2a267356e42e8cac2c214783a4b164470f5af623d9224cb7bcd165276e491396319a7745021c26c2fd987b9ca3dc53802d543b4286b89fee0552fb9c35a7a0f69eefa33065a61ecdea61631188d279a55cf33a7d90fe609b355498f681142689c9225d6bb87a8532463baf21e3d4a8b0838af62aefb0c4611a7381e25eeeabe504185e97946fa910e2308d71a812ee17957cfe916ffd127a7066851151a687402c8c8bbe3e67a2226e005accd45485a6947e590b4a6d7c7a6fb694016de4c94fb67596da94ee6d871ec415e63c962f31e2d31c485668c22e2003c04aa8b11fe27cff3864c0bf4a8d6423b690bb49762fd1a67c15c219c2ec3a9c49be3c05f97ba803e34319d817f7a827a38bbd9f56a77ecd0544d15d3e72f0179c5acc58052837726161325090eb25d43042764fbf27c07bf7c41a6444883a75bbb71a97af5dc35d4b3dbb9742416e0985be1fcb99b68ad837c2f2fdf5e020a4de476f9b52b91f474af4b1184ef31dbdf1856c887aa84ac96cb8f25f215212c0037f6eb439e72a9bfb243cc1331c1c2f0f53c78613c2d2e62908f0293113f088766fcce17cbda756325cdbb63b8742ed1737b114d3146e1f40f23ae77201fc5a7ccffe72811a8252b7c779b8c368753ab2fc2004b01521f6ff41047be2b2a8dccdeee062889b73f8c727bed6c3cacd6f271ea0973963361d116c5bca8270b8d2bf44c0b03eaea92f223088262929067f973a3365a9a712094f33ca12b7a52a9513e1009f32370adb1042410233efa5524da5f63195a2d1fd887c4cecb8ac0193b5069933f89044e288a43cc31890aa5213212e1f2ec41816c97941208cf0f78a18f64bdd1d7f543b2f093d0c198a787961c4be77bc2c0d560da218c20be8589b822712bb52314d29429b5900e8ec1409b7924750193d132ba852d138c7fbf68a9ef1eab8b554f1032a30ccedb15db82dcdd2cfa242bc3cd1ceb93c8785b8ee63c28e7281dd53bb0809a091400506adec8442bad4e8764fed922f95c085dcec005b43d631695935e5c9c55cee75eb9d1fe91b75096821d5621a0f88c5d432b310e5ae699deb41c0c71291358bbc8b3433b5f329d5a28916a80b1085497cadc46a73b924fb43a81de0bc020fb5e1a911aaa4f037b86b189bf8e08be2042dc02fd10071ebcdc3c22f089439544f8e2e93b7921969806b1c517919c2b28d40501cd664f42282da4398f220b591327f8049a5c2f7118e91d01da60fa891dc317630c7487d02c0603466a2e5bfd41d4fd7bde1c34764fde848ab0c001f00c21a083ad42b982466c2d2d04e5703c6ec1ded0468e9d367312b524f6699f6e8e69138af7500dfe5657d2518aaf2a4e25a826310416cdad76bc8dbf3ab2890e8f242cb440928aba996ecedff21f3c522ba02830aaf4fb7691109c40167f350e20c0822913aa32a2bcce10c2220d5ee1044109dd67f6fdee9f16c568f24321a4093bbdcd1b9e42b7e52d05a7021c7f12e9bdadcbfafe315802e030b5ff38da2c363e09448c37dce93fe51a11946abc4c7a18fb6785a267e5cda2c0ed83be6eabbf5cdc8ffa1ff76ec7bfcaf956a5853ec5b7bc05e4d5db4facca00e3c69628c259e6efc632da8ccd4c6aaa018af73e2e6fb80e67ef511b1691cb9f60bc9986a107dcb5d95fc897c2f98dd80129ae4d7cc3f5713f154a4548c50555497de531e456d92a32240de1c8720a6e8aba9385a69dfbae3098f44582f73a94725d28b4fd08c4c8ba84bd80900fad4283893b00608bfdb6e0af1916b6dd83a1b6a5105bc2ac9462bef77802c8d0565b0de59d95f59b61e1418f907c9aa5bc67ee5e3978c2d291b39da9712154f8470700e0758b544f735d293774daf6ae4c28a88c4fe6bd1664274e712a82be17cd92f36c27716376bd0c83f98d3731d20aac4bce0b39d839d1890557d11ff119e9b5025322f76fc1c142a75b45af25bee1c1a15291b9efb8f0e0e3fd98b197a8ee18767e5f6c2028be8eeab24dbb7dc854ee580d6fdbbd827c667bd0abcddfcb5af7cedfa4cc3dfaf94f014e6bc5ddbaddce677081349bc97d5b5442b4524bc8a33a4baf16804fa7616787761d63139193d00c1f30273ef79d182b1ab845e6b415db84aa2cb36ef8e1b561babe5f1629340947e2f081b485708189505851036f170c19ede8ce3b4f810a26a212a41f8e3ebc0cea8670e0fea8294e9b4b6caf68d0a55649496798a2dcf5a5f1d802c05413eec7c07791a22ed1e1e4ffa292ade5191e1fef608fdd06ea0a037bb75737265f7d65763abb236d94ab391a86a42d1e76bbcaab366239efb8f674440b2b482415150176ab56c1c78f91e8f38b9cf233380785d1eefcbc9d80be0aa3301edd2e3941ec4256e0287bf2586e4150d22b13d67c33d6a563c45b33c8bb7e8f255890e1e1f53be7eee4d248e8c74c8ebac7327a5ba6228d3b3e245371139a4ab285e772a3d65d6313c81ce112f3117480eb56faff35fde859c07cf485c1dda993a225e40ed269268752cc0ad820e210e05ede153ec0b39088a102ac6c211061f3511c46e517a508e86dfa79c7fa0b1dd146f03538cc5d93cb1c42291041a816a376a0867faa4026ea784bf9d1c342874d6c1cc84afd5af8b0f022930d38ad0d26f0fd0e324583c4c4d79373e4c2e5debefec099bc70427a984f858fe08119f00afe03d877ae8b3a9a09b55647003e4e3574e9c5032ad2563385197211eb15e49f2996e5b9d7b027f7b29eb18129fc89f737dce3d7fef87fa0e7f799bae7791e6ba4d290f1e0c823d66fdcb6a9922239cbcb7e2bf21aaa3d2c918f38ac848492e83e2a8a6b100d063cd69342ddee9b88971004c628ecf7d449435ac047224cf8b88d8eb1c39f147c92529ae51716b2565f1f3a80803089bd0d115230e85fe188980c0cfb890192468664b795f40a098cc3334de628a400c7a3c394da3d5be6d91b6fd0c390c5398041e3ce81a5a212d9aad90b4c1f1bbf21d068ba49876a32b0f5f0cf0466e137019304d7313d90a4a1e1447fcfb5f810f11377ef560d0e8b5979423203880578fca7580c03606b1a50ad9442e88fe6d534dec2f7e1d0c0d373996f4ed879f818d50c0536f870623359a4a918b5b58731f7d0ccb17292b8c0649f8f548ab31aabf52e41c9957f02a67e216c5ee4cee4da063fa056d353fa0e823b013acc0bf4432f0e6127e985cfbad581b4e80be9c8e1ca352b4adc61623686cddc7c8633706cbd1ce1272e3167cbf77d7e051271dbd5399e6accf2cc557494036c563b223cb751fa3da75a74be512eb76132f1952044ca699b4740e64be3072a6be324194be0800686cf23907a0e15e5da2f7c56beb49a41db80da4d268dc2764e4ec86b8b3f05d37ee42f01814e226524610cc915ec36f82402ea8334d62bbb5edfe01b8970ac741af6a16432aec1ceb3897ad3a21d93fb6784ff01207cabe726e486e29e86c48cd549349c26c86f8c363cda566c266251131409319224a5c134a139d81342b545a97e98ce350256fd4212599efedb30c8ed66dfa42cfcb0159e14bcf4c5291521f9e83165ffe2ef3cabce8be0d161343c16ba373e0d0c41b8c4add8eae825a052b73bc201ae40c3fdbbf6b36551ec4d10531ffa8d5f33b6975a79ba9825ed8ab0f338824263d82e0888b75f5117c01b40ca4c5f3811a040635e5f01a965bc6d8d93b98a547d828089eb00efe3dc1b5ce6e382e4bdd71b7c464e36e668edbe07d2cd99fc15ea3a94d6a01b4908cdfd7e710114a598ccf052d4e73863889ab55d19d659d0e6574dd5f7afa87ce4aeb6d96894f1e5643e637844fed00d7f21759bf02dced1141ff3dfbd050279c24c8156ba0042cc9b7c040324639b33f451924b90e67ec62ab3e8cb0a2ad58fa94dd31eaea3ca4d337fde809a84d91a5ae288efd29feab8be6f08dd34ff0af4e050ef65654dad99072ff07eee2ef10a8c51cf85ab8a9c727fdccae3a5815ed6f95817a118e3fcc5d26135471292559bdbe94c369eb2c8ae74d961f7044ca1f3b5e76885f82ad27039c997533480fa66feac364b26a4e06206d5820dacd83a3d56491dec32174060e14c65a825e5bb19815aff7f84c8075f83772ba0eaa064884bcaabc43f8776c0ceac65fe6d2a7097246db66c0e7859077f03ef3a6781d90489ae69b8f549301a74b46c1b9acd1fe152f5f6fc57bdc956b2fcb96507f2e8f382f40e423d4eafc081ee5a7291d8fa24de134b7a12310f043461d115f03c51f6f32623e93b7c3e64ab540021311a388819ec4118776b7352c6c19ebc64f46ddacb0145cdb2c70d682541c77166056122474a2c01a518a2178186f0d179e28a609e9584de77d11986ac94ff6730614fc9a4babc8c07a6ffcb8d5a233a085d57bfe7a0bb3388a405720ecb72522d0d184e1dab81ad49b11244163ceeee16920f05ae7c1c79a2ea827e7e6f9e40f9e51c5ca371ea7eb40e7be5d51befe797421264414f8372c10d0721e7cda223932750e7c8f6f45c87970ff9dfea057836fa746c7afe4dc2af5fc886672d92d435bf076ab9c0bd9059a6f659f2e4c5c29c75dac3702ed0fbc04f1bf10a181cbd936b95946701499f12b0601923f2f05c21cf0070a4c22c35f6ade71eba00f8a13c946a53e38ef3a09ce0271e616ee0b0857d70668a7b97a0525a096d416b70c44c97f01f553d6a08ca1a3f565209f77660c2865bb0ccc8f3ac5ae35161e084450e8635d229e5470b4cd8538bc6b59990d7618343b3ec5d12b5e8e72d66a5459f38cedbbe413f1bd3250bb48c835040d0531cc904040215a96089b9f6522692dadbe010ec3428b1d6eee43662fc7c8cc2eafb5d6bba2a89cfcc711424452eaef1899498ac6b711b097526dac0461ed5f80d061ea7b0072be99c1fe70d4323a07463738e2f2e06fb88c390a4029d754dbe58c49c625923216b17a6db6237a7ebe63cf4a4c7afa38e72406eadf7256aec8ae4d097c3c09a160ff03732ace668b412e23276e7688e805efcdd345d7042e5671874bc7bdee32af4a959281ce66a809e2ed629878f01d8584d99f996ecb8997df850975acd0fbc14ab6dc63e2b3958bd2de3e2707b5dbdcf4bd39e4feb4e62bb1e30610bf039ff38d0c722d1c7210743dde8837a5bc3cf176171fa44ac8ca9aef1c07b95703be19e7fd66fe3bcf2def6f38de52bed7699ccac0cb114de5b891bd87863da3689ea18a9348c75362726a4d382ac81c81df6df093686292fc26ebd3d3ed9e1555066831f590487b35fc4d306b0813f685bccff9a6c32f24d8698e89c5283626f4a81d3a7642e5aa7fe9df8ba0c83880f6c6266745f396c71c0b7ed2d75147752f339dd4f1461fe7a27f8e04f4863d9bd3e5959211360f3e4ccb36ecae379c12bf68b078c09739ab796cc6fb97d520da6a53b56514d939c4f5272974a3ec28116f4ae11185b1153c54053ffda291201dd6067ef5728e7f155c61ace9b23ca1326b6b3fb0d87d35609c3383260eb7266102cb0d30c83d766fdf8ecd17abd8c95f178704a44727ae99a5e2e402803c823fa0a114d2f32c3abd927cb12c13df717a93d2ce143e2d4381f6c8d67cc21c762c4e15d3f9e3a1ed57f89849ef2e6e85a7d004df5e88e2ce383b058f6f2ae9608d835b0a3ac6eeda2574f9a7f0021ce35cfff8c091229b5348ea81077df377deeedcd7f2ea2a980a15a249c67eab0033d442c58a37a471b79e150728686ea73b52f1d16595f66d5faf8f0146ddc6567c033d818a42efeebeb33461286015385af79de81d1e292dd5f40e032de0150c0f93244ab80a0f73023bfb9c4f27167abe47c96a31a731f8e4a53ace8516003f9fb7f7a93b674106df4b57e183734bf567f0848739e137519dde835858ddcfe7979e3c47dbb1851508b64ba549ae9f09105c2169405bfdb7b011cca3655c24281895e0c14cd072a304aabd2838d4cc8f6b75bf4407ae3557dde33e67d714b4988adc827c08175f9a665f0aed135b31ec340c65f75ef2b1de38ca13c586b72ca185fecb3223c0dd04690c9070e741c62068a0aaa1a91634a3c90140c23ba369544dafe6dd5e384f0af77bf0de1972f2cbba39e76e7fca62284b2aeadbc6c1a23fc43c4157677e69927f7f5e1c94c1ae9872825c1698a03a496bf5f7d7be5002e417c7cc9a58706125a956d5f7f7966392c30d943c92cc53d8444dc9f7d7705f370cfcb0fe6f6c084ec7bebf4b3e88182d902187689ed4f9fe8255b75ad41d6a84dd8642407ebad7f7f79df5e35f1bf499f11c4de58681d1a4f728ecde34d3487f665e1dcec83ffa17f0c74c0cfd23f6e21741085b6c0c428af0339fc794aa7fb6d19a6ffb2b7f46638efcce122c930dfc9924ee6cbe5e97a2855f4a64c3db51646be61a9abaa6cfb3d86d468b06b69d7baeb02c6032266e4a2c2206aa411e4d5298af650bc13497b3a521b5813d4cde61f70e15b35eee481bfa3a077021e8f0d75b6c4dbaa98ada5bf4a4c6363a9cf38029d54c2a0544d909bfbdb482fd51d5abdb6d3621d660aa19f72b150bd00383063e7fd83114f04aafd0e95a0b73689ea7718d662dc66eb76656bee957f6699ea355e213d3ae740790086a1d9e70449bad94c9fd4aeb63c4decf589e697969019c649c89c9f1af3d31c912fc734f7bf9dee9f05bfde5cebb3045918848eb2f331108444765e907265bed4bbeee363ed96568ffd879f7295e29877eff19be98bee1251cd637c3d0b3823db258c240cc33f65dd9552000577f19b24cd10d47bcb3b65aecf7851c5d0e53fa162a4d8b7b54ad7bdcbea8b1796c3a2b8b238c12663baa01e79f49e8845f2413effa29d8cbd02f38d45ec2ef562169a21c227be5ba3c407d885f25907651947970ac4dea71e2a3ea22634a28761996986397c0fc46b577ff9b30c64593e304bf4cf8dd0ad99a271dc82f57ff01ec7d8ab203164653f2536e81a1fa299340275d423a52df2ae20441aa0156009695945fe359b52756164ccc98df44867b94c3b53895c2744c9b52f2c31e4ce3e1570f991f7cc23118e7a4a12c15ca8d3a602d91f181cfd22a5cb3bc771fbcf8ba7cc430ff95fc9a63826c7a1305e158df1a8e72aa8bffd15d0ef66778109c2b6ac1800c135910b0f094838c558a734639fffc882992332f4d6c2a688e6ba388a43a42d1cfea9b0a80f83fc0d699f1bfc9cf564812f5e14b26b2537b3f5220d79890e182f9ec2d8e4d07a9f11f89e57c9262ef9a1ffe2450d72d81c724a60e067d9dca93cc5705f14f13bea3e007e22e0c9e1c1b7e9e68803acde305998d676d490fe960bf59c418399007879e9f8c20e7cc5405d16cd9fe5bf291b069be5d06ccad2ce623800281b8804758a0646d6d46897774b6068b6169594de85ce5053b86dd130b1d606b48d5c4bda9f5334a1c7a4985e2dbc844839309bea74c049d65e2e898097b9bcb1bf874ad8af8910901788a68dba1fd831a360a7a1ce2c6dc48f826a9dc7659de242512cc19804549f8e47f715c13b6f608171113496f1e513f90040b10514d59c3c34e982ce8377eba7c9ba944bc0ed14a1e503be2175824a7224989700490e1fe763ce1a183e0cc28c88ce45a4493b49a2e11995766defb8cb73dbf939ed668411b5078b717cb488f3d140d19cf0f69410eb4fcd7738894ad63c56262209e3446ca5338fbebca375b2a3f7cdb609b327ea93288312fe2d62479c25e934b66fd1d35b462d1e7dac1788c5dec96030bad13a6522ae86ad4b7c3c903a8b867bb8f8a6c34dc0feba9413f31c6554a507d94d8d5f157f0845847cf2b51ab332da3b2057718d9984424a3e374491ab14b3cd21e1913aae28b2be53f83695c9c6c5522b1e42becef728eee1fee31fdef22bfc8daefc6c7a935d1801acc1d0cc54e66086446bb1178806e7cf9bcb1bf66266c4310380f016a3fa94d37ee38c9146ea2452dae99a9f85bcc4d71f762862d0e48285d84baadcdc58aa4bf7190406cd1a4106a3cb093f58d47a11528cf0aa0590da1feb5637c54f407d8a977a2bd8f7b498d813b5f4dbaf1fd158d306be4ce8294467a283cc4041bf55f5a8a159d5ded6c1cfa9ae518be83dbf873ef4bd58d17e01b8d7f007a467ffefc37c3ce6a0124f5f909b23495313541132b7bbb1d06b20c0cda56acb1fe1bb8954da5d02fc77177ca31340536676ffcd22b577b2d3fb92c06c3caa1529198638b2a4541fac6b1759a05488207ca617782d9d7dc57f986ce290497533db1cf2584e78157f9fb6cebda12ebeb92d928ee6770a9e6244ba0708059a41bace83666f7c63ea90f7006e63e38f66f8846d710c5f99faafdb3bde142784b5c152f077938587bc32ef849d97f4b35d6157719bb5769c73be09163fb3ec5a83b9c4422c88cd8f26122d32659b51c818dd714ae4e7ce881ab52fc67de101fc678464dc4a73cd06db2af85788752861983bb134d0235dc1cb88c0b9174d9a1f8d2c13fd28dc069ade5233c1b5ccef1a17a525375d9347bff1c9e5d8f43b4f4ed7d3da6ea2fd06ec8c6f086418ec7e0091a4a704771130e569fcc5d6e521677c7211a8f6042b54538c177e8897120ce29c5424c371aa269e36c8463df31aa980c1798ec09bac1b48ddc4837b0742363cf33fa884333e625502c713bd751794691409f6ad880c0210ecc570212edcca2db1ef7f2ae00328bf198e783439ccfa4d8f737d65d614a1227da0218ce07692b6613f2963e4f508dea5909195aca8b6bf1440cbfda7bb48fc4ef7c2308988da2ef31a4ef9418dff3c1ace94fe5021182950b247d3e1776eaba3cb86f3de97fedc8d1c32db0d39aaf7707a62c2f9d6ee7920586988682200438b0c902f51b2cbba8d55b90c7ca05405458d9d7f57cc340a89ea3e3f0e99d423bf6e1b9ad5b5e6b574c82c30ff95080132446477052e7e011aead81f393682a7b22dac234294ce484e24ad203e0617f03af7682b075d3d9d320bd399d2ef86f5944d6f5fd909c68e05e99b34d6adc1a8af65a429e61e9f44e2d1049690a2134be5fcc79370b62efeb19867c55e92ef7bde37f5abac87d5f16c6697589a74eded8010ccd55bffa2109727cb561b408b1ed5f4ef11cab817b57dd3cfc1221eae71c00888e03e0209547548263069849dece4fc5de39268174f71d280cb899667f4644fc6eaaed86935ffcfee2660b08f7a05187dd596a8348f9c15003298783c950191e5eb872e9e57a8c47c3050fd07c8b32499dc241764ba1ac17079f419031b9f1a56ec285456b4138a810541e471c532cc600ca31bc6eb7cfafc52f3f4231ab87e8f067dc71f0e876a1514832820c30409bdb8537a17fc0703eed63fd7302ac8bb7e8188ed8046b41819884e3760a97c9a0ccfac50fb3a67b70ecc9ce79ba7a50ad100e6d59d33fb68fdbb381d22f7e7eb83988daea692cbd510e4a7a2de17b5957cb3e20c78ba797d7fbf3136a7912f77d76425fa3d69b8b67048ed3821cb8ea92d195a4154ab9f03cee48bc9cf817076c745115e0cf257610586fb9e7eb43a0cd65a108c3851504bab21fed3565d3486f91e9c46d5780aac4e2c9aa298a59413060b0fb32105629ca32329a44d521e7473377272f8daa2be3917a2dab3a622624ae5f7d99388b603d32b68e21bbef985f70e28f0fad7ab3136b8090bb77442aba9dba62804a2d39cc85bf01121c9233faeea984e0139583d42aedee69428755a9e3096455c171ab3281d4c98055355c22705636ac4d690996b1d577fc308757414d32ab5d67e27bb6402d9297d4339b653d206b8c9d1baed4798549a7448e9ed21e1cd05c4ea485963ad7a54ef0200bf8801b732ff552ecb70b17cda0976fb3dcee42cef965696d289c9027aab62421e5688de0e6167348625a16aad29dd09318f0df52da6eb7769caa76d35fb52b274ed3f81724e0ea3c17634c869e36eb58696165cf851d67693640e3f0625b63259a631fb3b9f80a04aa0aabae12f995792437b99ba7901fadf9fe4d9f0c6417ffe65ed25af91f770f7c18c4d2e2dc940e66f40538aad8313126bffd06244ac07bf360ce5ae60c9b6b147b93195115f0af689581d2ebbf110a0517f6a4df10620dd3169dc5fcee797371419a2565f02fdea037bd0390848e6f17a01730f9bc6d373c58349ebf2590ed0c2d87c2b25859ccdeb8266606b5f17b4fb83747f716f242c6f8c07b1fe472a74bdac516806ac4dc097f7a8346937b2dd104c4b74ed962a9adf7e01cda4978c2af433e69e37dbef5c8a6034fcd3553453401b0bbdf33cc75eb6abca36ba534b3b826af0eaf3b58dc41a1102befbbe3c9852d8339b9363851155cd0bab8280e4fdccc2ff2d058fb9fa271a041dbdf79f7501b965f90391ba6380718dfa5b51637d2b4f101fd2a0601c7e83d0adc98a139a1d5ee9744454c36ca3bc06e422b1cc22ecfdaa137dc51ae7b09a6339ce7acac52b57ccf7c67c2a2e3998b9f5d04194e412475d7bd400585fdb08baec85623dd7b71c3c305d375f22221b29f3ac24fc1c93a972eb299f734230ce8390af762f3c15356df1ca9803cdc9d1d13b415a8e4affcf3925c02a65783ffaa6e811a32e60eb62165ac7245f565d53b039d7302a0597b8778431b263a6b18fd633e8797ec54a47d9df56b6fc40b7816f5332e42fef811368c0459704db0450f7ca3ffdc55e5d80338cf1fd3303beed470d2ba5a2046a688115d91e1ca103c694016880546c477be01c67d23a1b279252a98a7959b74dd1a1c948c770412ac48fa8d8f1d92f3415b190121ee20b7940191feb711e61257bbcd2f11a84f8836707f90a04ee447186f52f670fe8c3512f3f6beeabdc9661bcde4cedd9b86374732d1f8276c702e28a56dfd95218218679947d8b54baca6d549fa93596abf08d73dc7d5028e560045ecd25dd0745c7466e015ffe08bdfd13f84abaa938eeb3b0bcb6a2338e485e4bced911c38c2f7352db289bdab1ae2a9a986f29788518d50aeb233f5f15c357499b264e66fd84c0df65e641d078942f92c265ccfb7eb2cbd6cb9370494f1c44c9c3d96aa4a54e5aad72fa7a7f6070ca9d0947658f1e467de66296750535a19201aae112197b318e8e57cdf03cb657677c4ce871b9f8b4e2e223bb50954a999f66618f71fea25174d9d7cd6e47803f127829771800a94dc1b47f93b92f26ed74226da9654d2e5ea9264f0474d537eae778a8107c3bd580a3280f9005ada51181a055545e7011431957ed7c514a265cd103e2effe3ecd0d930920b55c3b2d32f223f6467d4dec47494c29c500a6743afacef7dc23d93c6ccc4cd5867d12119a157e032535d02ffde24936adba64af2709710d81a7f4768059db1a3af21f8bcde0905ba17a90dd4c3c965f3b113d379bd3c8a32efeca074012aa90c1b66c9bdd95788abfc83b1818370569e4a7622de395add87d09b01b863df011daed74695d66706c72ff8b02ac607de56f3c005fe3b0bbf6a6938deb7210c496cad68209e9306cc48af64de7eaed44a3c8e3254846addf001bc918348a51fca426ea3d20da72c64dabd62f63f0cde8a0c26d3f5eb54042d593b7c0758f9e6c401925b3fcf367f1b93ef1cd95b5c202b4d91fc6e83965286ef6b85a6262643a031c361412ed2159881c8366a5fccbeb6037f7d82adb8a35534c82570905a40bcddb787bf64c998a8a313ea47941d10045722f28808c593255dd39279605727a00773f4a6bbd1f21bd3a83b0d3f461416c842b24bfc8c2ae206a83777280153913373d1b063b6c26964e8559ec9a19957ac35562bf9e31b84d03f37267aafea80cb7a234c2173606818c6662a14c50478066836df275f97c0b922c2a2256b2d5e0d226149c536ea00e79adff708e7938ebcf447ab3a783413e9538f534560b817706de2529d730f2c9e20a5e124fc31f3aff0a99d061ecd480b196916fb9556b504295e5aa7ac0b1a65ead5f0acc284107350fa47b29189fadb46aedcdaa254fa223fb58a06988387efb783c3d49bbc1e13bf898b63d1d83060a480ffde6b314e236495061228172543388364847af127e3cc2951b6a4ede1b29d55209a76f5e845ceaacfc94c2da4a3e6f06f65f1c7087b05412eabcd53b9924a148f1fe206646a4df44de3b8218a31a44c78e89fc0472720085e4d79df044da364fa2bc5d8f1cab38ed2e2c466d1623dfa0c071bb65b59fc8d0859a2c1700227b34def10f04a9e05e92047065b416f272d8f71292ad9bb4e6fe59b7b8f2e1419541d8b80d90f82b5aec0e44568d3bdb077178049e0da79f3a50b242aa52f92bf876a339a234753075dcef88d35a2a5d0c1c72e9dd7c4c0b885cc4726ff53c27f39ac164cfb2398b4f42cde4ba1069d7753a7a9e16aa6f43e7e8fcbc9c39ce94a9f20b20a0f9772f4a07b01b41d669341cf838f27c2012a45c5e3cd08bdc1a473cb2f485afc4b94dd3652834160e28dda50fd2bdae481fc92dede88363d52fd4306dadff99a5f76732aa0ebab8e88fac89acd94b366a334188a093a34f045be831e511a42f3733bb10b7cefb79bd3c14da0f65902326e6ea77669a2a1c89a959767fec3370953a0149840fefab36c52ff457a925cb29dbb42335c99b083f7f2509245d95b5017111533d5f43dd8e7e7328bc3b52490eeffddda87241355c4696235c2db09cc72efb2b394455b75875f1a94dda246a800da6f84716508e3f9d2f6c5baa9bafd7995ddb85e0b14fa4dad06fd2642d425dd101bd74241db113efec586978764f022389e8934a699207a43a96357cc43af63b7f4c966c90b6ac3f59ecd5a8e47f2db758f1563768b4e200f6ddbc896e735e7fce17079b39cc7dbee73e6ac75cc547798bad63974f905d336e7fdf434bcd76dd4f8ae8f297b4257097c197eff10f051b9b70a3bef23ae822be9f8fb702eb56b1c6089b3ba33eb443920bfa7efa5443c572cc7bb5ff6dcceee64b92c656ea68583d130ea19d098645f72ad84e331699c493a5da10e41b05d404f8edd2ec06ffd367bb665812f081891594cf591bdec166ccfab2e99632ac67a4adc63e4587bc22377c99bb5cd3e462307f7c867dbf76f800cc24d64e5f6508fc4bda598e94fbe854adeb21603d79afa80cf60a666409209aca231b43eb85d27f9b1f37cb020ea4432dd8461c41428774fbd45bca075cb357ef61e8e940c869e3daa278d1f521ae8e7300cd43756871cd990a9808862d763e1710c7c710575ecfaac888081899557d937c34b642183102647b6591aad3d6e589349c9913d2e904458258d8e9e42ae808c6ee0b552393f06495cd44047bce30ff510613f367b24e3296eaa9e7dd2f96eb720bf611981e4c6dfd21246339f800a08e23c4214cffe8b050862ffbeac03a4124d89ab933475bb33a3d5602f21f735491093f0fca214e398d1b216fcd58bb46060f37f3530ceaf846f840603765f451a8598594264191e68de3e2e26edcaeb217f683a91d05eb76ca8eead8c9679ad29b2562478901fa94d2923c0419b10846ef60c7b27eccdd3289f403c80dbfbd214077f21747f0877d2da931bf43200be39e977ab51c5c6a17d938155c79d04121548f96b1b78f564fce82ef17957866d2191bdf7e9f53ab60fe9309842ee58280229ec875422f055f78459d9ae70322c2785f901ab154d759634985256dde8696ee5d87204fddba16297173ca09bc4da18b6f106fee8e58e53a57cad23187de465d780f467dcdef8ce10c6fd98471712920919687445b09803f93ce4bc73a1f1a9d376520222587c435e18736b096fe519f40b544df44960963b96ab48541ac0045373dbdff7cf1c294462b5326b12dde2d2306c448f150f485c3dce4bc766e9da2043e73fcf8d2c27e0b18defd81d31c38e2105af100b101379613c51b49f3d318849e96a42dbf5b2c21c1d25ad9e212055148df3b51f05412ddf0d5e666efa0e478b8d06ba10bc80fdc4532297bd29dc5e38b754c2aa118c7a1fabb60544b087cd02587862eb1ff2c03384f478d82f89b276fa4f47c02400e6e31877fa27d7d7f52fc0933f3033fa5928fb06294e1df961a3cc332de3c24cb8f23611b565106808054344333fece06fc4085373e3abf85108d83aecd78985bb91d8774f61d43ca17a2f7e38769c5e008c102b5a5879d27050894d3315aab128315e3652b51b3c9304d258a42d7bd5dca76323f6a3d6044a4389e786064f03c38558d352c8d5ee1013f7642288d1fb152ab9274183b5d2126ff6cac7f30b199ed274d6c70c6b222606aff24feb647dbd0bb578f5fa5ad6433ec9dc86038b21712f0e4bf19af28ff1c6eeff3da2681d20357ed5468f6afa6d7e5526ca3172c9072ed177172992ea760f01161fad206473a2ab071981341f10bb91e5f84adb5767e2b984662e05b2284b7486922f49b1f20d97749775f12ba7127001cf0ca8af147d986fa5db0798a9dde355198c7b7e7360967e29e5f7a2962fad52631ce4fd2906ae1714bfedc0be07371ff4dcd9e1a5b7e627076885dd075d4fad077d08978514e39ede18fed677db5d416579f73f9f483777fb17791fe4ded4485101d39809cfd31c37ae8a29ec1491f4c47f73b29256e3e9504a9d4bf8aef37dbeb0d00cef6d62213b4b370a3b5a0436e2bc91dc85f948f6a3cd67bc02757e3981c7d2294c20b3b8c785a3b93cbe7b145317efac114c1068f09b27c547e6f04f2321f3eb4665279afcae6789c957b3f3182ad6c186d9e993c4d2e3a63724778192905e4ff6613d2b57bebfd33ca1a4fce03e723063258c60d051aed599d3326667965cc62f66f5579aafe64cd257ecba5119ea6806136fb6612f649b2f5c705908e0a44d1ff55df651ddfd0ca23290be6f04820f7df006b542bafde29db376622120c9885e16875143adb344ef6a9e06f4985c74df4bce5ed9d4ebcc82bdcdbd2a301df2dc0dff01c14f4649d5ff939212f5037e5c08018a430d02818f31974be9e5f8c887cc177a51fe69bb78e73e1a9ff4de9cc704ca4f9aa5d4e4caaa3e0e82d8d2ea91119e60b00fe2eeecbeaf9abe9b6f174491cf4cdd1b62c97cbb7fce4dd8cae668d6270e8fc9a2ac86b57f10904d966d9b1cdcfc7ae28c22ee35f463df412151044b6396427126b134f1187667e9c9b03308bd32cc88335566df43eed5bac514a4e3c175782a4bac2ee77ed8b729fc69204bb114a58f39787d862dd76f72b5f4580b5ead94836befb504d45c8bdef127baf7837d0215710f6384b86949716cc4864ada8b4f0bfb8b09d26bd7da9e3af15bbe066d9ed9cd6570b61eb08fdc4472b9f83796471138279e944fc70d0153bd0e6ecca1335c67f5380e35d4980bfdcfe41f9f58b7dccff5cb7948b20efdf5ea04fbe18219e81eaabb9fc63734bba2fa56ac00570f4e990abdd696ae2e3227b0fb3ee4e7e0a11744f9437adf603af968ec036f597ba185e9a08d3fd206ea229e564d07500a95e7381cc961315ae34657dc833fa80c08afcde7e114b0cc7274e4e78cafe5cd9a05761b9e7275892f06bde73d56db59719b6f10238e625368825137b3290e9b6f902327402bb19b913a1be57f56b86df7140aa81605246638828c32a41574b973824789eed926afa25c2dc9917426e2d6cb520fd107ea1078f54ae9213d5cfadcd3932597db9c08beaf3e889d4fa61cc1bdf303a7734c144e6334bde936e7c5e869d87c14b57e1e6af74cf5d6ea389b73c7ccba49e32b9b1e0998644047b6497bd4647b7abd1a2b7620656c39daf6ca45ac25857b215f341b57d4cae357ec80630262ac783dab7e6342f4d15a58ece676e5a30604904a6aa06f03e46857d3f631137a2503c459032a7e8e805c30d659491bd6476d36a53e30432a746064503d35ec74a6bf46fbacf987d35da8718069a337ee22846add30225dcf271b3faecdeca1ccdce77ded1b5a40c301542c60f29e511665f5ffd09b78154d6c5d476b9b88904d24b2bbbb7b076d08fc077b0744d0268e34c1441349ae0ba0407975c87b481994dad04967a7c188f1dcbe1a7a7d01d8b86fd5394010b780c295de837eec71eb174c1dd339ee635a87006064d9cce8563fd33a04d0a97dd1b144cdd8407bff76b8f2930bb6d797bdf2af029d9ebeb2c0c361890e23d18d2ac73875accc9da923adc4c933554e5a4ea0b49c14110d4929a79c52d26a2fa6184f19b1b417631263599659d6f446b76dcab845a949bd719aec3627f0b996ec3ee94476252216cac2326564913f728824f22387c8221248fa2051a98244c54ad2110b491216122743427115796290b88a3c7148f4892a098b2a3c8a52ce49698d724e4a6bb5b45a7b2fc6d8c5189665396b59d634adb78dd35adcb4c8c51dbb2f850e234b0b6d699932b6b0b4b8b8985278e95a527059e14b0127caee45e3c40e436160a68c30261c65e43269852d5c4574a00c04e1faa1b35c4180b8880001072a1c21021f29e222e1460b38e8d11606d862954ec9bad142eb88b440ec0fb69ed8396c23ec2a5b0845767087ae561116b4866c1dba5a3cd14a4e8264a2c83d120211d7a14b08435408424208eaf1b52a842227176c053a3ec10cf6aa7f314960420b26b2c8a115830584000475b1c0c2114626951b00d549e5063f64031801c2144392a0f8210a252daef8a1c8094058810e4f381285e71563dd688109284c181d651823803e6bc81a78c77ebedd0d1fd0d1d1d1c1613f532865942fdb7c1c77cb1909882af49842876fd3a7c699e953e7d0e4471a2396a8191dd2e7409f2b1011041f0a044105270061e458441012466ad00155bed162c7d7820004218d1b27187184099c24c5d7a213253de20b141184c44dd2f3b06dff70108a9840d2a30d1bf08bd1341a6d48ff686878fa57d33fcc4491fe21a03771f384d68d09af94b2284af9cf842e1fcbe8d0e5a4e524a875440c6e90c10ff90433b8f9912ac2e5848912176c27495c4e7a10a79db556d5a641132070682284da264049f98bdd8d111bde18b1e14beea3ff9c734a89d17972615aa043f92486da9a73a658b084871896a880dc69812b76b6051396903a81d14b0951144a845aa0c4c80a94106955990e5d4b585da87f2b50a49ba31d1f721f10c28fca8ff65297b44be9d221a733524a29d7115de24947ec127eb1df7b71374286ecf596a7cc1dc4a1fd781c2cc804444a33168ccc596bd6add065b2cf293becac4eeb59b7a3c950bf07eef773ec768c582376fe0d69847ebedd08708f8ffd08623af61726eab8d1120cb4c336eb208edcd5c84f0fb19e3b8cfbec225627d735d329f1b511db62bf9d76194b4a29a553ea1418e1441599bce612492c4c50263ba85b153633c2d59e60ac0996b132db9728a22d118476e9d0b5840e5b4b87ae265a34c162377995e2671625432d1a0d5ce26713131d5eb62b305a16315915325887aea1d7cc4b87aea1d3d00d34d7d0102e86820ced7453872e259200237e7e97124f946862ead0d56407319a145123d3a14b891f32627afc0c00dda16b091401582225000090511383770fe3858bd2b6d9ee0ef9e6c8773d7690fed1601528d9e9f179e4d8b33a27d6b394524668b34169d3a4079998988fa17917c4f4a044500b9478010f4cae9c78f28f069ce0832d74c0e1093f30f141832194f0003d690152528cd2ca499da8780800132a7a000093570e326eb44053446c960e5d4c98b8c164073dc458a20a6e0414744f78be0910184bb05ee8e0878b223d344b049182d3123a0499d1a207192c7862627ebe7a606eb400594d5aab9798a72c1c3141a27f9045031e17263fb430d161c572a5a79405cfae0205dc0bc8d64488bec10ab4a12154908782ec9025018b4287273b3401b28315b89a14c1e352e2870f58f840a1836b8914d512a00ed3a1cb890f2e265acc40c2c89b33a8084537434cb91982851124a9d209d0a12b49928abb2883706743883d1fced0eaf6ad087e6a50bc42c4dd0831842575ecb30b08472e200c61f9ace5e55f64def42e315ade3afdb9e3e26b40e0e9dac358d15c4950498227899d3e9f6adcb67dde3eefd29679bd65fe86d476d6affdb6bdd50ec8cf9d4aaff1ef8413af70fc3b95b88e78a56ffecdd0eadc7f33b4baf6b0cfd0da5f750bb1cbbcd67dda6b6e4388ad711e3588fee63932af790e4c4993f9cc611e7e8c8b06f3d2f2f535fe995c5cb296cf5ab2962cdb72966d2d59966d39d35832cec2f259c792715cc671f5b9c71997e1cf6e84c030579223ae242a0cc3302c3ee64aa2a8635ae9b5dfb4dfbeb4fdfc1b73c3302c6a1caddb3fb5df3af47f3528736cf7db7d35e89ba155837cc0444d4a86359f8b559771d21a67b597e298ee436a2fc6329a6553c6acda18238c3a94d68bb12cd39ca78cd9befcdc97168ccec5b2ac693d65d42c8d63d26e8c31c2086f8cd8caa7cbcf418674f99a8f26d2e56f3f1ce5b82923b7d238a65e5943499c0cb14c504c2d168e49dfcf52332599aa98ac9892a209e97bd9d13121c51f202245807e8e04fd44ec5f885ea0bc14bd10bdb4e88bd1cb8bf297968e6a8747a51364451feaf9394a3927a59896695bc31a06a34d18058a0e7fbe620c78e8f0e72a32e1071d623289123ac4b387203afcf813450cf0c63e84a71fcc2c61660819283238c4e000868abf93d24d6b0f678d19f9e9075dbe9d73ce2ca1cbaf599637ad65999b19a2cbc7b04d6b589681d2e5532e8343ecf35e2d6777c6e0a0cb97d65a6b2d0c15b1babc6082648bc562b158acd56ab55aadba7c9797cbcbe5e5f27279b9bc768985a5a5c5057f7631bdd0979729e30ba6b1b4d2b424c192050b91d2104a3f60942ebfe62c7f78e8f2698629a574b624c19245976f5988c8d2104a3f764bfe000509b1867e582c168bd5daaddddaadddda2de8a2419349832f104683d6853661ecdd06ee884d8a2d88561263d0e5d34c6b996e1bbafc89d1989867714774f912e70cf3c93729baac5acee616442bd12ba424a9a35afd98904c482624139209c984d4e5eb955ee9955ee9955e6933790979880c4adb5f9e739f7b4ec3a0c9fbfaa5c6526e58fb80f1d5f7a5d6589debaea9e3aa24e9bf6f7517f74f98c7dca754190ff61535ce8bcef990f160ed9758f297e2a67feb89de7c9ebf1e74c6061019cfc19e0b8ada01a5d7fc8b18c7f9d0ed971d717f5ffb1955577d31a86b0fd3bd741a73380ab73955c7bfdd544faa27a54af5d8d48a439236f6b38eab226decc721a57a5c7eb6843902abd3cf424628f24aea2b74e87a0d75230c29020f455debf5e37b432e2988f40474e88a824a8f0249b7e9d01545901e85939ebbe6f974dcf3a679abacc61863dc3e7a59e360f5ad10cf27d3392acc8fbd8bfc70d6a8413230a6969226adb5f97bc89cf835f4bab50bb3636bed6b12f39cba350c19fe5c3f7ecd40b00601ab0f524a5c1f6a8d3ba07ead41967f3bf22fa6d6a0ca3fda7dbbfba0a186a87d26e8f535ee11e939fa6be8b5fb668fdd85c63d1c1f736f087eabb9e039f8350c2fdeca5a5f68fc9e4f6ff9af02c1af4060fdaa82372fbe72aaf1ebff78d817b4e5271811672f2ee6f7e9691ce0427c6f2551f2ed7b41a257df72d96bb55f0ff9793e1acfb1af3f6b18b42867f6c7bcd0bc4b7e99eb671aff22a661d06a7d9afaa7fabb3e577394d91bea60741b898d5df3d7af9fbbf837e60bd85d74dad374a76e771df456d94b867f3886c3f06fbf7013ff625c780bff60b0e46a79d4c8527aa9716ccf5eb3c69ed92bab2a5149e2c7726ee9bfd243fd422cf113fefab1af660d1b8e7fd8c62666af3acd2afb557ffdecb7aeaa348ef6399a7f4f350c5ad7de66d6a840809408c207ca03ff78548d7f21f0d1ed676fe509891ef47cfa16da48b650a7967b42a20ad54413659c8821b6e494d55a6bb4b3d82d6badb5d65a6ba9ecac0a13884e20a484edc3cfa662b79e40d2c95fec32ab53ce70c19675524a3569eb97c07ea9535aa9853df69710c0e876a6db986e615e14d0338665a85e6567eab45216728fb647dae3ec02e8794a1967b6c5e71d6a1d19c59a1c40c7b20340c71de3189d77d8a4e73ca78c31c60873b6cd4fa25fad23eb184b8b558ae11653ac38d19a82c594d71429535c53964ce1c194235fccce96b2c594266011a7bce41429738a6bca121e4c39d2c3942114f3c829278898942d7adcc15e6ba5d820059814a014e0225b8a0d3f2c147edc68c1da2a39c4bcb5d65a6badb5d65a6badb5d65a18ec0fb27ad8529c4899424a51ff56d002762b4416668dece1c4d8eba8753bf97f601c3b46e6c5af7dee220eacc35d8434eecf4e421af763d7afc4a18d00cfaed471f71d5441beed62c000c01972c6a7429770865cc0bfa50f2bee28a411f3ad5dc470601c1747c65aee2c8e8a23675aee288e9cb5dc4d1c59bb5ab64117dced9eb186753771e44cd39dc491738432d38d6e2e7246d453424d37dd6538b4ac35bd652eef08e5a61bfdace76ecb2e77db516913b9ec622c1dfb504410122c3d8825237207a00e5d4ef8f4b9828e75c0b3c70955b781d2daa16b8a1521b2b77ae2f544eb0916920e5d4f14792208f984cf7c224887ae2776fad77558b445519522a322a2a221a1688b80260c341f6e0df2e1a21f5ad14e872e27aa3841c58915a61071820804f14aa730c85e8f0da400f56ce892e223650729394c293650281055b144585ca223ec4054d4a18bc849efd045d40322a11e77874440fda344437a7c8ae12b93c81e4f8c73ce39618c4872b0ba9456747a7d796447561e8cf1c517637c31c6f8628cf1c517637cf195b4da4bedc518c6b28cb59ad5ace94cab9aae9a56bb2feed4fadbce0625aa6afdbdb55abf246db5d5566c6b8dadb6625bbf7eec2149109144271f6040972f19305b883c35861b318834f2e15304a9f8f8f97cacba8c52ce49698d724e4a6bb5b45a7b2fc6d8c5189665396b59d634adb78dc3d7765f0d4696e334bb6da9a459167c6bf7ad504d26adbebc681506e38e65855ebfc5a8d77739c2dd97028e49c3dd67a3d77fa901a9cb7bbf1a5ebdd620d4e57f71a8174048d4894205baa2587111f1c42fc268071f7420c40db6d89982a7095454d1c288ec83285644b98215a54914564d9c3a58e707082b66800d38a000c8106e5022ca0b7c40c41278d0041e9ac880aa2ec6517ea23c20c39ac96f5cb0bfdaea998e8672525a390c69d0a755870d0efd12b4810f75b8481bf8dc739be7da411a50c7b083030e55eb889006cd58b33a077dfc19c3b29cb5ec6a08b03067d08799c6c93af7396b5143808bd17404740efaf9a7e6b8fddc2e614803624803af90354ed6f7674d8b1a027b6b1d013c67c0fd79d3a6ce41f77e4de370bf39ae23c3b40e9ae19bd96c2b3d0b879006fd12ff44d03787dce3ce6dfc66c8ce897f636a8975f8598775b8bb954fdeea30868f52cc82305d4480d2a16b08500ad0a16b083f7dcb107bbed98d0ffb5ed38f96d9d0e7df8e6b411ad5868aabe55c7d7e66bac2bf6a4397913e0cf2f482845d6a1da6ceb5208dc965fba745316cfff4f9f8f6cffee9851cfc9d5ce8d8e905fcd8633ce7f2aac34f2ef4fbb0736a428a22df0cd9f4e1674e86fa33ea9b1f76fd793364cb9b211a07d2188b80f1c205cd69462606e6c5e4628b5a584a9b12e136ade50cc348aead14ca191c89b28aa2ead04584a44e042510c14802743f7639020a0fa01c8192040a0cfa075b46a008a902ca0a0acf4e112b7c9e2ca17f31503c8142c81329373c69e249107080b2b3458458dacc19f91412b11f35354f7cd06f0d1146f4e019e4709f08a1b23544dcfb8405fdd610f1a4873ec10108035a98e2898e951ddba1eb8912190a127ed4103185eade7b59b062d70e5d4dbc9eb8a1892c6810b11f35354d0cd1efac21a28916f45b43040afaad21a2899d9a2c2c2c92857bcd229fd32f75a954fadc95a494527a3bd8f8f5a63fbe765d01c5750590969cab0a2b3d7b4d73dcf6bafb78549ffe692fe54bc941c9bfa8bbfd53fbc84fff7da97548d9a1a783bd6ddfb7eed39c47f5f139f2f961820b2088573aa7235ef94df10860f40a04475ef83be09cf1b4de5a55381107b640a59014924252480a492129e4803be79c73524a29a5b5d65aadb5f6de8bb190f484e4e4736a1d35481f5566377837f08e701e0e788c31c60883a9cf979452ca3be79cd3524a69ad15beb5d3c38187831d1fba203975c1f2aa43e7eb70d1290daaf13a21bf84120565d63ab23c3fd22d2bc66901762d7badbbfa0481d26fbfa3c4339e3df7f96ef66fc8fa9aaeaa9e7947ec8c7fd863b9cb78545e5590c6dcaa4f9f40fafc1aa88f4cce23f85049b1a73cb5c258d7434581523ca99e8c950d654173c67c9b25e9f36f26d42b500d80523d35c85a545a1bf22a48a2229594c3ebf9489b4b65a434d2ceca2ed6ac93557dfe0d9977faf48cb8c81a34fc781c9e907880f8f337af39acad4676a515e755918ae4f3d52155356b6c3106630d138939c87621627fb5b55bb3460d0a6a05b5825a42ec1cfc15071d91284c08092bc9109652fd645d2502f44344a2322018d04a84d65410186cfbb1fe007942ec186fc5329aa57a52ab1651500b4a51aa274ba9b254cfeb0ee3987f511593eaa94819d2fc1a2451541ee931f21839a443c01ef38d1ed0fe2ad27c8c9190bc20760ef635d716942289ca2f89aaa878c43f1ff7a1ced15bbd403bc66fcc9a44da0cd521899290566f085a87f4f85e0cb6fd2189faa217c48e56240ad3b13b12a59228ec82d6bfd83f4f485401e33ea068cdaf491549a2a00b8aa13ebf5691a858adf4f9517b42a20a913e0cb5d748ebccb7ed2a1322e288816282d83e60ab3367d8c7da638e7d604f88e71329f6843c52b7415091a8b7bf8f24ea771589c23cbe240a49a29224eab791f5841475fb5e14fbab42dd3ed44e2c7ce33c1d95deeacc1a3968ceb06ff18ed9ffc1b016a8db123ff90461d86ffec1e0f8178305a9884c479086fd132bc3e8f927ff60e4cbbf0d4fac6e4f47ddbec7846dffdbafdc7da62abafdecdbaf6e4f3e746b751e69cb61ce39affd7b398ec316637cb77b31c6b82785b19df7de7931c6f8ce0923a8c7fb75ef48948e44ddcd235138b5e292244ab55712f59c9578314ed2e37b50ecaf6e333238e96c1d7e25f4982d6bc6076a1d2f3afe1b328a126458d97f79c8a43367c4dff9bf1919b800411af1a4d3e37b59ecf81f97d4677e4c2a17199856323fe8f1084650cec10c62ce7a7fce39a727c49362c71823fd0ac3bdf70af17ca8ce71abcd6123bd9e8f44dd6bb12724466fe509c1d99027c5ae9e8f27a4bead1912ae42deabb57ea8b5fe942ad2dfb66df22f760dc3be34d6578f31474a95e2b13687a53ac78c114a944ea9523c30b592289bbb4ea960e784e60cac6dff5519748cffd5f1674f889d63bf76fc485440e9596a58a270dc3112851f738c33d49ed1b6ff65413a5e50fc1c13db652a9874fcd3da68ff45871a47bf7dc19bfbf8b7ce06101b52d1e76336cb3428a8e3c7d5676a1dfa617416bc8141599dee3469833956c79fbb6f77fa838e1f0bea98db10624f1a78b7f4aded5e9ad498e839e7dcaab49b94deca0b223d2259e334ed3729e5a66d766aa9bd0ef28478423c215628a7861eebad75abbf5d7c2d967da66d9bb67d8a273524276b5af65a7b9d452ee7d7369d7d53975e6f9afe52b7b38f87a6cd2d6bafb596c98c43aca310f3ef5ade3779896cba6ddb56e9bdb75efcdbf57c3c1f4fc86fb3446910ba7da9cbe12ee577fbd2fdd24629c7fda5a5cbb7ba9f7f21f0f1d5cdb7a7ef09e13a4f485461f38478423c215cbc3924e902e8d045854fa742d5230f1dbaa6a8421f3fcc258ee3b89c2f7b1b63bdf86ffd1eb25b9c7fc686101956f5df90a99e3bf773a512572addcfd9389612e7b8cdfdbe250e82d94b5f0d3d27c2b03df73764e97bc81eb9df746f0e4469bf4b4b867df6b95ae25f087c7c56a873bfe3873bcd5f7feb387c21f7ccbf0ce32e7ff3759136d156ae71f26631f5fc9be2b9fadeabf5d5fade8b69fdf752ad756a75b5d6586774465fec3ea6f9966efebb71ad69f9de8cf7a01d4bf5acee15a3a339edf79079ce298cba013a744d1134e77c29679f1304b34bea83861abcd594f369ec608c543e1d42239de2c728c90610db669923f917b39c61d745ab41968a154ead28c613538c53ab2cb5c218e3c71ece1a1bce19cfd26955cfcf7dbab46d97ed6319c7523da99e544faa67b5497b83144552d4583bbf23ea07e8d06544a4c7fe45fe490dd7f915de2e0704344cedc2ecb546f8d1de437ed1a74fa3203d001dbaa4a0d2f3d5ec85f7670dea19ffc9713e1f3918ffe29cf527cf8937f7cabff8747289456ac59662a5562e3fdd7ed6b487f9978ffcabfcf22f6a8d936ffc942ad5935a659de346fdf95bf45720125562c915487d71dce55243ba9ced6be811b77cc638a54af1702d2e8fff86bcd99bde857fb26f6fe2a955bff4bb37c6c8f19cecb78fdcbc57dfc7fa3e4e0de99fc6b793997e0e3ff7a534e65e63fea5565dbfccb0cca494327e3166596ab15b97ed3ee65fcc9ee327d3eb977ff9e7c2fd944aa2e4e34ff144ef3ee6dcfd2fb54aadb6c6099ee7799e943e7e8e9092f0935a61b183842c1280042324245804394589692c7d4c63a9b57cc634b1bf4a1445038c86e66580b499fb28e986b499bb481aedcb6d48f9f28cb0bfad7348ae499b1a9b6813491494f6b5a9b4919456a1d401f73ff9564e18ee7f37543e5dfe0b1afb7213cd18dd87bf1de33d91d248a264d1ab52a948336af8c58baf3ac70b1a1a1a1a3b338331a639c28c6950e3e4af12e5cfb448538d728e35319ecedf3af9b5755e7c628c18cff9d77c0da7a1e6797e55226a7afdeced7b7951f5d9f233432f3e2f435e7e5e88bca8f44fad6241a2287f4ae98d41344f3f22a1d4f492a88f0593914451131589ea7e726f52cdd726da451245f3f3f76b1bf9bc788ce7bf23c673fe316a3ec6c7e034c4f81aaa6352411a73fff7e243d3eda2dbedd736923652ebe6ada617bc913fdf74346bc4807918f3dfec2f0fa729e65f384df7f9e8349faff63438c6f842ca1793a6b341637427f9fc637c4d778200ff187f927c07ff18a71d35326864d474313ade51ce42cc5e9046c63b4ce7d845d2663ea6379111fbab4432ff6da22e331367f6cc9d99a1f94d3423fff0befc8bc12f5ee47061ed7d1a1811c68bbf2f9e76df07fa0b9677d1953ed339581e3f4df7d9747ceabeeaa392e9b4eeab4060baaffe00f9ea8fcb8bcf8b4f89e823ea26a33ee76754d3b1678c6213a39bbd17d680a71f18925f69e5a9bd27f6a7cd54a059a30a5596c4da738f83feed4d6044be09b4e7feeadffe6acffd0eed39be43ff46a3ce8131fe9ac9a00f744af10402c08afea7499a81bf9ff367195f731a3a0d9d86628d8cc75e5a128629697b69c5aefb1dff9d8cffaff99f41daa40663c6d37ffff2d9ba1876647ccd67af08f62df01fe32dd03d7f1318d951f3fc4d20839fe6c7f8ff186f821a7eb21c0231feff34f98e187fdaf1dd738e7d963651e7a82aef883da5dc7c74d96941df07baf637a696e1fdec973f99923a6df957b9fc89e854747a9d8c4c49a6a7a6a40c6c007b3c6bfc6398c4b089611f8f44242955aa07937124511f7cc9a02251988c2a12f5e229cccb88f97f693f31a5272256970fbbffdfd1fdff4bce77f0efbee33474cfb90f1c72c2fcf7d292b1823468cc7f27a27f491bfa5af7467ff454a48d8c2319482efe9b7d4686e667302c4fed4537adb516d37803d206c32437b54c44f389cdf2a616bc914fdf44346b744f399a871065878036f45d4c3bf917a3ea3a3c67e47f3b04a4f6273fddefa2fb66a7e15f0592757b68f799eeab4064baaffec474dffdeb33759f0c2a9dbe4bc7d27ddac763973a1a3a12489b287a50893c3c28f6bf1ea9d391a8f99e131bce19704812edcf02c98003fbfc4b658d7ff89f56a3fb6fc760fb5da7c919df4139237e545d0b3467d0977bc80259200b64812cd08daaa8028a3c2b890a626db4402fba8c1511f667813e18e3c4feeffedb43ff82baeb641ccd19f41fa953192b226c208992424312851fb3456c903d629158964d2251925369036570c0fc0fe32e5e14f55f328ee08d859ae61ec21a2722195c98ff7940dad0bf5f54eda14eff44249f9e88f4890846978f9d88ba05eaf4935145a71f2d9771e40d617f99c56ae59744e124434e321489ca44b948a224ef9aa5d603d286bed11195af92f456246af20d481bfa300332c4fe451db74bfe2f4883267dffeaf4ad9e4f02f954d248e9661fc64837fbf2350f5401c08a46026005af3e6d9f339f4d67ea587868b76046f45ba9ea1c2c9f69b7a3dea512f62c33e68cca98332aabce51d238db36bdcbef307117bea38543ad0304f1ca5755de115bce90d4e3b3f8e891e5659775df077ac5bed471ffd5a01cb8d3dd877bb5c9199531773680c8a88c5193f26b3da26b90b489ef19b1e3dbc71fffdefb3680a899d7cf38e595470dc262b769f6da524b82174512326b6bedb0b7134ec8eba775e841b17794add9e8943e02ae4b0c4d8d4d0a54839408cf5e3ffbcb5efdbfeca392cf0df90c91a89a89a8a691aac6ced9df788f481492d8ef5d65afcc283ba21205fb4c056fe257a222239decf3ea31fb1df9334e8391cb3f1cf937676781e864aa9f4ee45389a44d91b47949af6867f233a49e2575a9718ed85f0cea58557df4c8a3aa7a9d1ff70fbe21c31a38e4b560010fecd8400362c040063c29fbf24ff3a6ecf8106768536c39b798129bfe9450f607873c28fbfb39776c40a226a70d8841a22aec94d22e056ae177786535423ee1c89ff433fd537c1f1df21048aec305d7e1422ee15eafba0409c92e4142ba4b962cc14b2eb6042fc996689c483f6b2963d4c253e229f194784a3c25d8122e335ed1fe32211924f41c6bd6a8aa3963fe6ae5e3f353815c7c84b823eec8c5873bc25eaa8984820c6b3e2ea8cfcf9e13bbbe9dc1f66595582b0322fa01fa01ca806806d4e7474eabe48294f8a0073de8410e0f092504f1606b6d7fd6b8bcbd1682b83d24b6c6fdd634fd59dbb4bd7d49d33e8d7b486c1c76eeb52d731e9cc673f467bdf7f678e021e121e121315fa236032a41ae30a2f66bfdfcd107eb629fc17f31ff6638eaf7af8f1541cba7fec81c582c168bc562b1582c168bc562b1582c168bc562b1582c168bc562b1582c162bc9dbc1b6e228bbc2a767af5da1b20209cb6753ccc3e8e73a99d73a195f5b51a4eb7f19e2b282a788ab0a22322ff9c743bec5895734eee5732f5fdb2fd37d1b17f3325cccdf90bafbb8df5c9379fd1c274fdb6bc9bfd3f6cd70d4e57f331c75fdb0cf70d4bf2de665b80d2176cc6f9e13f3323ce7e5350fd97d5c0c87d2c6f41a7f79f8302dfac5c4f2f535ff5c5a5aa40ca105a8c4edb556b9848a46000010006316000028140c888342b120c9d23cadba0314800e7a884e5e4a998a24c22046611405710c62c6180200218410119a199a150016746f29383d0d840dc709ec8cf6838b134d1662ae6aebdac3757f84124e5012adbefda5b5c71de54d05c286ab9b3ecfb47f2327e615f4b7d6dc88bf4d14a6afbcf4e5af7fb417f7665a5fe48f5a062b9e70983c97fed46c03ddd643afbd4047f261326e27ecde9414704ac0d2283dcb3b98616210d049210816bea8a0a1882156a616c4b7f18cba3e07fbf48a50062aeba67dbedeed8493ae7b00a2ea99e4a39430300d02116bb548e5f2f0ff7610117965a0b09048557824061c4367148e4c481a18158ebdbda96bbe9cddc0fad2db5518a617674b122d490195f8765d2c227938a6f93a090cf5d220110d8a50d8c9492b3d2e0732f7665f6749e320168770424e02df44fd78f7e6bf6e64d97e2ad8cefc94ef6f79a5bf58961dcb8b5fa33efc2e7da567bdae2b756f158725c963d737c71dbf14aca4d8156e7aa2ba17fe86b48dd4cf9086b32a7edffea193a8b38b2f95a063568b29cad0b0e4ba770c1f13a2a8527e488870b7b1f1ffe46c0879ed63a7e0b110826cc8cd794daf946ddc097a15f0a6c2b0e0503c6d4429d07d84f9cb85f8f5470f83eb019a824d3ccb0758ecd01f6fccf2f077ddab530c3022563f46787999e8ff633a1b1327debe9fdad829d0a2faab44fc1c6c4ab3cac76c1aef87973a452b43812fd9d1712f7c1a983510e740f76e26f5fe90dc425ea7cf30e6eaef343327a2322db876fb62b1e4f3809f0666406981e73f27b374ef8b902a6e105efb7d8c869a8f3454a1647c64724a66f9606559e793819d87ef044cd152a4814dec59c955068c7d729aef69c87e562092e757c3cb0e28680ee3a16a90cd9b7b246c01b316e91d9a3df33d3fd248ff54808398b0ff496967f5bc6e853d12c0aa6e1681c5f38b2dd90798eda3637925d441b822d86280c186379ef000830570b9a10053a0d82d91f83b05a093df6409d501de73fee9cfcf6292d0a6a85c6f2f6f7f6c20f0fd2c7e09f436fd0a32fab4a167e32108428061bf1d7e0f30521820605a75fff368a5f8be7f94dde7bf90298969b9d7ab504048ad1a617a814fe3a1e4e24d1970a1bd9363f2e0d4a6b7db2b55e2b3c0d773bb1180809303791de6dfc0e4712f81c0278e140be549303d336a1f842cafbc1b087c4506bd0217987314f0a3fef6047de30a5348c188cb4957dd57059d34179510f5adbf74a1e26c121025f251872e8256d3dee55f187fbe247ea03eced85e72f379ede6c655353d18cc5975319ea151d34ffd24ee1483299fb71444a652a83a778099ebab75ee4d3e2061d40c99aa8228846ebc215f2174e1a7ea319e614f8dba0117f0b0d4ba559d590da289310774ff8198b561c4571c59edbe16ff5cf2ea0f5a0280b362242c9a6f93710677affc80a587970cfe175e50f6d335d158534858600a2286a29e39530b8416be068029be67f53d69ed025b440865c11085050beec8cea6c05137622435c361b482ae4d672d7d5945addc570683b0e40395ea592b28aad340b842e2b1b9f93068fd02ebcddb89e24bedb69e2372142b10c3296c918771d9d075bf5c37ea8cf0b97aceeb6fd3c97733ce8773c3c932239135099563803d683b855aa67e5e1d58184fca3544226bd5e3e9e8830dd24f4c155a8db17e30deca4cc48dd6afa301c7d95e6cab039346aa44ff06d560525bed1bd4c75c158ce9a65cee7c98220c11971ae640471531d610ed4fdcf8e1c2d8e704d449931ead47118893c3f4016264d8a134806f3010c29a3316f4c5c8ace1c06f79eb219163f9c68a477fb1acace17d170768ec8056cfd71a551791b2e5705d62fdb226de8127662e3ed264466b587846c24b2a7116f58bf6e0db4e1c99321f1958980e6d81bade5450b942c4ac969590f926bf847774771d313914a308005eab2cd6aab57dacd5f5fc0ead8a5b7a78036a0fdbeadc8acf5a884771052f19c27796dfe4c4a95b04220b03bd88d7be24a0b27711c05bbcc836ae40011392dc2e22ab0a89fbbec0d936429ecedcfcead7ca18202f3bf4c920b62d44e387bb6be47de0dd71e2eefc84124413710309570143efc45cedce0117372179a2a80f64e2d57face0223f4666a1ddaa857dc2bbfe5ffe9f59732a92bc9c7c22a04ea233c8d11f2ddef343e9f6538f70311e48ff3d1d1a81f8540e5dd4694a5cbf13dc841fb3c86342b49c2c0b0515f6b0ca6488a2d3cd8b6a23fa1e82349c0bfc137d435b57f05d2c539bfc6b27f08703ef0ea147a35c13bf4c3a7fa7ec901a667755c273c94445652b6cce154df5020ec86b2556189d42bc4df0f5f03333a3d3e2dcf2a17fabc5aee8104ba83fbcd5cc2ee539e6120cec9b36eccf876e0aa967c24880f8cd5f151ce5538effc77d6d48c2c4923fb1805603c8eeb0288081885b67b89e8b5fbf4217ab1afd4ddf1baefdd036f6c1e195d9fdab1262d34c8b255ea0bd8641a2bb5a62f631ff7aafcdfa36821cdba46f0769c7978348994f884fa4f667179568557871518c1f03f83c036fcfc3118d572c2e0f25f8c09ee212bd9dc5eb96b37ef1bcf5ba122bbf7656a7708131afa8d82ba5a6c766f3cea2070a36eb498215551be26d8f2cf851b1aebdfba68c6ca7c4fae74a83059cee8fc3d1c939f7e3a27d8732d2823a8a41eb8484142936e7c4295238706d9c32ffe589453b40abe4113e408237d41740a2ae223a8af0c526770381940fc45cc879181f6d545de69e87e0658bcff7b41d32d19a81473beeb959f80a5ddb120777e8753b4bc7b42fca70813f52cd3c01e5da42a19eab6dcc5b8dcae70b37848b804a5a5442da01a38101885cd8d00c3c78d36d2cf53c5d81b45f809a1f89ae0f2e72c3a0838ec8c0056dad64797b876244f6d3d9ebd9296d713ec88a17fe86b26dccb145ba693af40ea1951107901ce8cf93d65479ff394f8fa740cec2fadbb8eef0316b4218d0d7a8df02199834a11441e213d9df48b39654cdee648f815701b66877f99b7590ec7e73b766932078292d4ef9a7ba5e0db1bb8dcf088e48ca33680d4d5beac44062ccc316703691015f00915678911d056faa79a5b178c494d43a58e6b926ba2db91940f04a999b4b162dbf8988df8e077ee10678f6cc3bfeafe28c00f6b1d5742d997e0695f70d2f0968f72ebedf55ed870c95ba78bad2aecdec7110be19ec12e0c916bf4564540abf7848b9367a7245b67f38ca5f7f15e6aa980f11cb7256e44a08832cf488d17c9a7765f1926744c8924b8e0cd239d1cc860dd166a6db4332c2efc46f0da7d7cd89062a122235f82f4346cf55f9b138a3c2b4e9fcea88077bfb1b08f93ee1d93676af5a59f7b7b32cd9e8a94b9e01969e50494dbf33a8cfe238395ee058f9efeb02ae94524e6479022294fefed7a8f9c58f10b582eb8d57280112c30b0d6620ef761c0e4d86a4ddaf45770b0b660ff5535fa75d29ddad0d08c8a511d0d70626217072c4900fbe0191a1fcfd0d82f288cbffc656ee76f1dc67d60143acac0929229449b00b8f1478acff452d1051597a57a306a790c0c5d9405d880940da9c7594276c55df5a7aec41fe5f1f8bc9f5df679d93a8d8ad4189418a65bbbc632fd630ef78ac2aa91ae8d4b061351b94f3b1fef4f31e5052b9f2e48b7c64032d7b008670d064073f1509a2c7204a658084364ffca156da6580762318384307be631d2f8f1d28d26d37d44a1e8ab5cef2a8eb8507bc6e6080074ad4be7ecdd960614e03eb8f1781abd5d859454be80236cbe3aca9aa3c3e654e56c841f24aa756a1f0e40ef5293920cc896b2e6b96f7ce652f2bc85a6f88b3122dab401456586557aa865504975a42f13ae8ddb0ec542b4234aabdeaf8068e079e5ca6102ad401434113a9b835078490b466c4e71546814bf8dc2f1aa44c3260f9278f063e542083b7ef790ee48184e0e26977074ab612b4aef0c209fdad25a4fd14f8e0959a97953b90701d99214726c947d2a8c5ed4c1841cd99c49b82df9f586787a40157709a83db960a871ad5f04304650b3fba464e1d2dd31bcae22a3a6c58caf20c44eb8f772f8aff0b05d62e01d3d3159eff36710fecb601110fb761d5207854a3b82f70d3deee36a7b68fc43ef2f7c16656a2a95ea2265005a94c1554eab1362196743455ff3ee472faeee4afb63ab62fadeebac6b9077a9f218aa228e0be8e28edfaae5fe13a7a16cf0fae1641c45da46e33d62537b9be7dc6f8241d57f2489d38617e22c6c0aff4237956865d8c7ed1952ff8ea3cc8e3ae44b007cba3c9bf9d209b83e4e15c801d251cbac5c57bcd6b4e0f7118a21a56771264921b3826c820d17ee9abd924e7cac946515e690953e0853856d2660f52025ecd66d85c5f89e7308d37f71a97e9a6ae47cc98f922cf340576edafc4a889176df848dcdd247568ab9c84babebf072f218bf3edafdeaf8111ac3db6aa62e9ffa18109ed15052aa54dab132a1dafabbb9542b219cb40a043854953603d3de10f8e0a4ac977009936ccec845e87d586590c79702c387aa2470da9b359e9193997ce8f166c4a9d403830853b85b4ef643ae7db2ac0235f962b3583cac655662c196622b3c8e9f24932a5b36155dc4709f8779c399594d5c22ed6e3b5e43b09d6b590e666a7866e2b9bd7f1b646125c07daa5d011a7074fa5eb18979be52876be147c248ec3ae45cffd5cefb2119cf2d9b29c7e850afd7273faee26f49211afa0e87189c31df7ebcf11f92af03b5eb2044d728e084f2cad67159f067ad5d34e3108b13008a2f0c23e03e79dc37aed53eb30181435c31f6e27f7a530608ecc0eee2a0be3cd01fe2cf6df5a64a038f85a9bdf8912706ea56cdeea893fc40e4b4ded585c3aa3e5a665d9147362f23ea2848117105a485fe83c27474d3b25121bc8df972e74bb3d0d58184946e62f2c0322ae155dbd9f43e7a12e96e7790cc4ad1e2fe6db5d2c9791cdc458190579325a7224fded9e77bb099b17b7fd908563270ade1c180c4672bf94af64731434498390386d50ddf3f31a7ecf4d8fe8e34b73abbc178b07d9b5370383420e690b7a4b897269e7d6183d1180c7b65d8516edce37d2bf2b30b7036f1d111deb26daae7a805632821038e794dd4b22a81fbc84e3788f9722eccb2e81ef6847db888e1125002970572b1ad015ed08ccea71d72a141f39c41ae2d7d9341a83710d4ad3fdb4c2259c3ffee26a6c815d8dc86737d00f81c392dcb3ca2a85f5608bd5e22799e5beb40c5a8a2e140cd6f50fc8f0c73af029527a64b07b4f4cf0046f50c2331f73162ba1b592f4e3aa2cb87e62eacd8506468de3900ccccbad44eb0f009594cc43e3177d8234fa5d756167c548ef7885b38f1c1c93c3cbc6457548f7b485ab55dadd36467d3fd106d49cc335322b2ace62d7d7442ebe8a639442aa5eed34b82f5bd6ac0a85bdd0b95daf4affca92d65346962b5facfb452ea0fcfc6c7e42ecfb19e7c8b8f0a8948a264514951901e0836ac28ce1719e238e45110ac7d5c05bf6ca6d8025c31a12935efc5d1983b893c94c616615443d8ad994bbbe7db4b103c74195ae20a9f55dd62cfe1a3d9baa3a42b29f5b8acad78d217927eeff1ec31d13e8c5bfbe22df6f93fe57bb7da7b6bc4d86eb46f5982b6f251595b9133533189bbdd6dbb0c79bbfe6c144285895ee87ccdd911424c8a4d6239b51946f3addc948af972993f27d22e9fd2457c18b8cdc60b4e02dba79c7376ad74e9d4f2181a84ff0f0524b14c8d9a558c005614cba3cae1c407d90d213a8814c064e5baa8a32e6568ccb353dd57d89fbdb881038ffb24846ffea6d2b65eff1a3ad42c17d1829b14ed48cc50b966a14074e9afdbc20f21f88d957bb7909343c3077e0c4adad665a1714d0d62b4450dfad113c9b3e7b19c8fcfebb3ccea3190cb80875199a7f3bb9d50910b6ce202c316a96101258c1dd4bcafb75663ec5964eb8547e9169ba93e970b58d0ad3d07468c5afe9961717339f4b4bcc7d3d6c969340ec9a4ef3924511c9519d1542562f364b67f5ca19e39cc1995f57c2ba6e665dd37965e32dfbe08d4bb0e93210a77f65a9952781d54772764b72ad8c5290c5ed2fa19325429c8e2d4a7462cb2172ebff1fc0ee74a1a14a67bbaf9c229a9c5cc2f6343ad2e21419374eae015f0a4cbdf563a56f24308a96fbc1b75ab1f8ef7418d7d49d1a9129652cf6749679fce9d8bec3a04ee3189c451aed54db8c16a418d9de22474d9059febd075b45677f3f9558ba2c6285b8fb6087ec5a947903fc08a26d92c3e34f82eb49c74aeeb1cd79afd81ea5f58943a1c7e52edaf600918038b20dc6d6ff2cb85ba8b46b165c1366e06aa1c2be3990b5a86267068c6666c0d34ae5182deaec9892ab1d4530d02c16fcd4e3cf502b4910481aafdec353fe95e0906d83276d1c7267e6ee224d0b70beddca1f20d158b1215a42b6c1494fd5bccb05b69dbabf10b678dcf9b81a68746f42b6c7bf0350a96d1460a1b6a8963a957ddf6e0b0aaf2ef6202669a57aa6cd0342aa77f54c63c1ee8522da1d002fbe4c178e3dd32a66d917a2578a122ccf34631236b5b274c8f0b300a3b1569daf66b91982ae65743a8558e98a6182835c2aac3c7376c3a35ce9e662a3ae634048bb0285e8ffa7a811b8816d1053a868d644d80d8fac2b341daa34298e7ec31af6870b962737c52034f0a4ea867708ff132ecac6db2cff49d400fc847d68b1ec89c6809f0c293f24a4e5a717a0905afb660b135d9ec39d7b82065c739132ed04544966e7b783b2f92add39b22854ca30a734dc9ddc25988a6e172dd11611cb1e2c567a438909dfd082a770fc4a57de0cc51c5e8bff740a59dd812ede98261ffdfc2bdac12a716d3d2375133d0835eef0792b99e09b4f887919b9ee129224bc5f2929068af88deb0dc8d05d4170fba2f11be1b76873330a668558f69bcbfe0fd61dc0af3c1ac57b92022f1d345e8717e26708c6029479b13592612cf2b893fa8f5fe1e987aae703de3fdd74a2c713e39a950a32700c1220b6e9435d99fc8150698f5930034e1b0ab9f970b71347b06219ed3f07873cbec3840cc90ffd585b1539f8f8a9b7e328b16c2cc2eea16f1169311f2cd0da8b066f591c3fae37d5c5c866694e3d41bd96cb64cf83558a33cbd0e647df6f81352d3e1d4447aa3e064cd08a4337c16a5e13d2731d403855896eddc06874044855b390193dbb3be9f2db1b4232a4a1fe3750034effe6e4f9ba33db6fb157689c581d163a0975307fbe316c557d32b7d654ec995c89ae19756b2ecdff76c8a10c4f1b6a169c054af196c377c81ac281b80d150380fcbb06e8c0e07df372d9d43c3005fdbbed2c60fb46c228cd621f092ca269324dad49093e34ab1fc0e359ed9e017e596e8b46b2ed9becb978c6a3904ac896a4635003ba24893ec6999ce8c383f04ff74d1591a2d2f700f4fe3037f0a4fee8b3c8133882bebf0acd051f73ba54a4afe9b1f947bad2f7cb530a00c98386e6c65bcb3671f7f06c10c2d3c3c67e668aee1a3f4ec0fc944116bd19af5351b5311b684354b58de49d2fcf29fbca854190d6de12a280634ec90058ccb857c45cbdbbcf164752091df17523e62132650c4454f4b83ae87d0c5e78eec96c77f739b4a7517a42777d3b7d0ec2b322b2c4e9f195ab4e839afd81d326f54f09fa0665ea4e4d7d7d361bb89a88901b1b5df8ea9c1206cf39f970c7ad22344312f0644c25625fd7f7e0b37e543667684cfbb9ab18ec542106cf8620cc9fd60e599fa13a16cfd115fa0f8dc7afae05f3d8229771717be106d9aa72fdf3139619837b29d63f65a922d13097fe4c21469a68e0c511ad7f3106c473739d800e703f422640b91e41bcb4ec225cde177eab3606f793a9610d48563392cc927441ee32228a4509538c89ed172502fb32d2674bb268dc3e6ef50d641d4d3e2307abf3cac7f659f820b21327d6e64b4cc058b276d8f60fae2e9ea47ea858b9201aa10b3399820f73b29dc330128d70b0285696ae643944dafdab372525c7b2ccc21ab719316a6ee85b461b4a9f600ed42ea38bca80f5ff9939115107f73fbc7150460ad1f9bd137de19976e73527ff1359cc42edb130afc18f2efc8319cf8d1fc2c91d7c12b58b2a1ad1218cb697c182169b75c3d807bc1b7138f42354d6ec476f7ef7ea241c5c66536d8532e01d428e1ea1b81be6e70a8829d61e73418cacca4b5d8744aaeae666fb866aeecb8c609c158bac7287b009b60b0209024567a7fd7a47d431a414ac4816a3fa2b2f114c2b5fe105eee1c54d404ab9ba502a016523df30ae708ff487307e0a1d9d849f7b0534081f0f74e58ae3df57bc3bcf5ccc22d05a6ce2571b0b93d56cd4cc5761982a55d91e7b604eb2d10735b32cd2141c462745e0504a02bbe94788d18be5cda6129906ad322e5c02a78afc4091f8d50d07c71454ed7a498dba01733a091c7c9c3fa6564af770a7ab5c425566c966db22616591a36be16b931b4fbdd2adf7326ad89450160ad2b1f7cfe43625a3e729abbd6872b97ae6b9caeba459a2cceca813abcdb4b143ea058cb1327db3cf528cb6d784d93711f7c954e6093500eb591b7336021b1a93ff39bdec478f0cf357fd8f873ec522857b170b5423f94bd4d5e3e2d6b32000794518591c4820bab0df0e19e99816cd9b391937d335a70e6fd91d411d71c18861d16e7edf883c8790666093f4bc6f0e893669c84cbbc3ff64b8852d5ceabfd7e5c382879affec9e32e4e1671ab6a4c466b462ad7832a5a9c407a5fd940eb4ba7382e689b68282b458f78f1458f9e629c99b698251067d8dfb7cbb706562241d749fde02f25259dcd5f3e60050044696af814df2cd7a07e86f886e972d02c62c200ab73b15944d7bab129584ee3b6fedb7bb3617de1edcf3ababfd3bfeb44962c97f62c0b66240e55ccfc04b16746f7f59f15e4030fd50d4063a2006e34cce18518df21c05c8d8fe9861ac70879c3e52fd81b8322de0e3079aff0740527d1530873eb1988768c4304c7d9b3e37ef054bb117a017d58d64e9c589fa64c5fa89013a106461f3ac25027e53f5e8fda123df454a53f2c56984c94d1644653055a906301627a33dd53f8ad4282dd495b48eac3020060d7a802f4948ba168bbd70de3c4a15bca102dd79e59f97f4b21e978e5e60129ec524502d1aa7f1b4ba798d1b32f443e8ee6817a65eca4040204cdc43d9de625d88c039779851cf04d480a7efacee1336abf1044584975f85af493c32d37e6fc79e563543a16382eb23a3670d804e8f861ae0892090a8a098f647d78738648f210ac9c72500c6b869d8746610dbea4f7f87f5f6efa479eb003187722c1f030a7366360b8e9856ef1183b756ee565a488abc77866e87f7a967f28e4e72b5cf5264f9584c9713fb05b23624166d9322b4234d068ba464d693941b34f3d2524afea11953cf3843f6d16a01bc0745643af3013f35e1377c0169fd3374d56ac005133008404c29423b23be5df5b8c8e8eb331e6891a298a6a5ec2cee8995325715cac3ef3a8d3a1d1d5e2a3c3fab1ef27ae298ba0ab836abac101f9dc4e5047343774f9a9cb810020dfab251c58d33847d4d6ac33d13dbd39c0a26140275c3a7a0892e3b789687f3d78c24c5b5283e01e03b87634222d2b56b61555d1916f8389935468982f86798321f62e413ac608086318e21cc555e57acaa1282a7cc03ea2ef2ba7f666bc666f5e8e7c6661a0e4c562c3abaf79eb217797535caca59984fae8adea9d6b6d908cfbbc0fa32670c06e105e3e2a84f51f896622ad9e943c79d3b4e4ede9e40a30d440afef991a143ab696d018d96090d6741f3bf991d18cf6eb188cc154e1012f901ab806e3c7bdfa2c440d57c0e7ecaf12e720ffe9bff21d32ec8c51867e03eb75329ff2baeede0bae2762fe5ef6e14a6f2e5c4a4211b85122de4c31400e29f1f60353cfa9a8271961fe87c8b49327e899b82b586a70e0dfc906d4ac9a55144ec901aa69d737634b6138fc48ebf106ac246253bab965d2193a9ae6b41cba4637bfb24a3def3bead22799eb0c892f5b942e844bd9270565ea987b53eaec3712da17d8d02b122cd018b7e2ec5bf74a5645189ef94071cb72b1de6a2d4ad1cb793f472aaba35d85810d84efd0d0a0743d56420a036ea3e5e50080b0bd90b28f0d34db804302654b1bf72d0546ad662034bd05756d4041c43da69105631aa70882ca7d735fbf841dc4134cd6034204caac578f7e454f8f391e1b86a19602b9347af8dfcffd6d520b4a7e9f686dbc774492073561759add12ca4a72ec34fa6b680923ef80b6488aa15c06fc5aedefba624f371cd356488fde14dd275a752639168609bc9c599e10968344308df29f241bba255b04ae7b599c49a1a85ac672e75e41b3b8740c7bc5d3dad53a67f7c71883ff4f6b196aaa756ec2b65892a825154d42df39a088447287d2b57b153418605f88fd12fdf6dbd953e9df8e4a7eb8b6055b839dea4c2d97a7876315842aba14d442852a1e77d05cd1422249c8c37a4e171b2f4385a1bdfd4619d79d12ce3c792fb1f6981c9061ba22b64c3d2b424a194d3cfcb49fea34849709219303ba19ef500ac059cde62b99125c4c474deff3b312170cb44d8a337d05f8fab0c580e915239c7a7a453d7f4aa264fb0ad5a5683c647d69a56ce2c44648438d2291b01befc6ad122dab84704576abeb2dd9aa672b26016b832c8dc0f08385e221bb7fd3af45aa74bb3b659d77c1733b92a839ef11260a8532d11b6a8550d462d561091a02c7cd2f3840891781bf6564f7e7e5d62c206092d4d68715c6f4f498cf84fe945cb7cbc0225656a2df9f500494f8943d867175653b254a0496e227fdd85121d036feeb3396b5802b437384d3abdf5a09bbfdd41e969e1380e30d293191e4f36a8ef56458261207655718326afa2204767ef9ba1fe84a2b3f4127ed156719e89e2430d342361c579c1ac2c017ae24355952220b6d3a7a402047a758c4b666c038cde7e38056fcf7a0014ad0f01b8b399244fabd55dad16fe2c803a98d6c1377a3a56ffd3577c876e2861df25b56e6b00f5da31d67c8d5ee7e20edee3fd526b32260d0cfe63eb70478bed74236d0c86eae7e2959d09e9f075e8c7c580f91aa81fdd7c9c0547a33da6ca26ca904356ca2698d643ab6ebbdc390bf3d4a3316cf5bf17cd9907d02fccb45e346c07ae9bf0c66d2586b18021411011e8596af42ee70997fb4efbe0b1139d713cf969b972d2e121ed17f32a174e436d1410389d76c7f8859713770d27a45dadf0f67a9cfb59a4acf800986a60d02be5f65596c5c143428bf1afce8cd163b93adb2aa3d918a57d2682636cd883127c6cc50b21f6c6531bc8e6f78d23c564d643c4201044261e922ce5db476abd3faf73357ab4ffcb7f17b4ff3c17db448779bcb17bdf462df7671e906763a5884e15e34c5d2d9c7305b02193ba91c6475e3581afd7631da05cff357c042addd71db71eb3221c5a020b086f595c2e25f4c1141058ad917000684b972d33f6ed73136b292f9ab3cf6536d84a6426959bd0dc0a01fe75c44096a845f15c5d9c3dd0e9e0b4061d4251867c1cb869c478e60cdb43efe3ef597747e9e4a7c6b03b0b3a4d2dc5a21546accfb5d4970c20711bfb09b8e36b9a4569755b4c538c024585deb2b14adbbae01b208f2f93772546069c27875fbfddc39f3bee901610358d1da045080ea7bd1f29083ebb2687c8412ff9a703eb986143bcc9c6de02644ab9a4aa257fe62e265faa6cc0218341dc5d96eaaf5a628c08dc123f0646d58386e7c2794c946c2af61e4d595fc6f8f28c0e013b39c4262ee07411f0dbfdcca7ef0d7c19ceb2346c58486d1fef7827722c014eda3e10c6429f396ca2b9740053042845ecf6f78aa62866db7d767b952360306a0230de0d903158a624e24340437c6e462145fd804bc04067b69d0d80fed5810c91ed1d09039507b7d1c418c4269ecdfa61f5fc3fe1a71514d663cc4d794e6e6c0a9bce7bc42fcabae0582337d5127e523271538dbfbb0a864b1f10efc42530b409a8b3d7268fb0d24cbdd43e079c8e019ec6419b39ce3d8b5b66047c173bcbb105a8955c5e61d5948f4900489cedda2ab5a47d254fc65c0b721f9fdf322e5afa3d6c4299e090ea15cc924ed2e20c5d6624e958d3f6f86607cfcaa59be49d7a2da68195ebe40bda5895ed9b55a797bff32e02fed511ae16c6de1a37f44d91edbef43755fcd851e6cdef68e9791d0ed4cdb9ab1193c377620e50132ddac47dcb914286c4eb7045c43c5c8e6d38009a2f6e0306619b97c37606845a09018647974a49a0c78535cc17a90d6f5270a4385aa8904a826d48a220701048b54a275c67a9de7fc6537305ac3cd3aef1d57bd60925a64b05a641de708dd51221cc590b8ee1f6eadc17e128c000f6b8c1dc65f585bab50fb96eb787b51e93deb27eee2084665042d4b95bd06b326f0492f4d8a75b506dd1fe946659b893c001c966293e1190ecb5683de40c8fa39d07ea596a6612f2fc2b90da6010227635d5bf8b55f3eeedf586fd5f854313c65a3376c31491a6289927cb74c3cf45cfb6e3667fa7628a2b312e79b9a8e4867f3b7eb1317e6e9c34ac083bd577783db5424b119cc1df58948ea4dc4d79ea68e5449ad0a6d48730a75e6d018115f9541532ba4a9f9e0823ffdcd8388e9ea2e5b1634ec3970cfe76683453f7b506a31196371e91f96f9f221ad2062740ca5f2cd59e490536b6f1811baad1d24765d41385de8102e7b52f5e8a4600d4a95698d06ce2448a14e0d771b4c55908abdca3430d645d2520c7409f7aa922a603f9dc5a684b7632ffcc39266033bba05921e9d158062cac6164015f00db3868888acaae727b51ff7a961a6d88ce0b6a2dcb9d178bf1ecf4c9903b2bdee6f944bd2c2b52c15d0c8f0dced01be228103d3ebdff011dc2ddd719aaa1b0f5395db162aaa37c8c7451d7a2b1db9231374db4d544382510b4fcf811c86d2848a2c06a219e9dc21a844c65e5b1dc8378423a531722b44db503b4269a6d4381aa311801a8ff7b707738ac07e34bb243e396bce6d6b64233cfa179648fcb55689fcf2a22096056d8481083aa748e77713e2288bed4b3f51e9709233f02be3854cae2ce206acbd67f4bd9ef708c73aca894bd756240e94340164be18bb37f909c601a92725cf2c79072d9dc57871781486b9f15fbdd82289621352857e28549778869b7ddadcfacdaf96454f72d3d87cab1cd6b1b923efa100fe7416cac92cb13cf577e7484d039ec7e5d5e2af2897815ed7fa428ca2bebde590d140d529964754fadbb392d1654d556ff172afe4491f3abc4d3c3de9b2b04ec3b51053af6b562673976b0b048844ca1def81b465df6ccccf86f6b0f862b10b187c1f38e35917bb8a9671c85f9f7f91b26ec52f46e3ef00ccdccaabbff03fa07e4bace15d6d0d8ceee0504796758c61c6dfb8a765251fa62d1d9fb23e401dc755793610693e875184386f82d1b2946eaa8eec5c8907967597bad25b2bcd461ea5775e89ea5599d86b421bb0d2f00418f0b211a0e9d93671a6fc38818099a59d37451ececc9e9145b936d197203e47c242678d663498736d1895f0857e238c9c2c40db020e9b186e53d954bac50c325bb2508f558035805c1fdc8fd69e50139cdeb0235549a5980d33226d814b24da58c4b8bb6da1ac85281a0c0eaf99ee99b85fbd5aed98e6df9822cc24fb12d699ab7e81ccb200b820573947612d0fdb21fbef4e3ba0b22ecd8b61cb190e2bd69e93a6415c20ab5526ce0b58c442c1ba5f2f6abe1736cfa274e649be78f4b4db48d258d8e188ff7f123359b89914d546e7a911dd3ce1cdbcf8490982b2acdf22b05d1243208e9e958aebbc8b1a6264440584a7900c0498aba51271495702c097ab873165babfa6347ddb2ce944402cb487ce4b981fbfeb76b354c0dc9ff6f3338deb8b1ba9e827a74cf5e0a67b7b1d15f9460d9661b7bee425fd3c216f060a5a1b01cfd9db3602b985a7819bf91e2f377aba1bc9eaed819552d91a3fce60d5aff67f11c7bb67ce60635219e8dced839918f1b5b33d540a2e422fabc28898ce387e78128302ec185c965e72c9c7de6c6927ebf52fbe847113db22b93a0b37d4c78d26091381f817929a2060e9e68cb046085af62bb8f45b05a39cc989da0d9e79b8a5559f986bb2e615d457793a5d9719fb2df9319bcb13199000bd25a8da4968944af8028039de478f28a8b7c4eba8a6f66136746eed85a2c1aa01631629a0562b31618664eb282d6ab52cec54cd5bd3100ce7b8038bf8c7ba1497bf2aaac7d98426dd85f838823b394dc94acf91a83a68e819260e990c2031f318a01de1672fe1fe421a7e32f52107c8f515031b5bddb0628810107ff991efd2325bcc0ee94dcfc2912a2c51e286ac8a4619aa0b74c5c89515dd9ba74bda78d10b41088074b24b54286e32aaaa59a0ea7ca8fd2f921431f83b08afb9c874fee134793230f8b47e23e577785089a8171be57c980bae49525a0d89c917501af40a35006a6952a12d397571aedef0acdc3c052dc108562086c9337b8f0139b707280e74cbf5f93fce1b0c5f453ac6d1d64eb4e607ff57ed83028b1b3948618b6880fdbba6f12e44894a80e20ea75c672c959625f1f578234895ed87d32815c75aca312963112fdf247e87762eb38ef7d620b73f42563969956c47c8decc4e7ec6d8ec1ab22e2be61227e7a40c2503b7048ac4aa8f527aab32280c14668eb87814f5c1e6b3e6a09f0917327606ed30bf837577c5d2202102b27e9e08ce0dfe0afe109ebb85447f9761b820c9f4387d79f8dee35c4d63b07e9d57e2ec7f169c4ef37a1d865f061d6d163ed3544e19b2d2ab5d8a9e345f39a273f02e6880ee0ff4ae2a93fbf77793423aa5407dc96211fac4360007db3c5cfd2dfd44799447719dabceaa9129fb7d496ca284c2f082aa00dd6d567cfc254b9c68a3cfd6bda96059e5f4bc4ea96ff453a1f27698a302d851cf1cb48b9c523eabd5c3482bd5c2400046cf54c1275be83111b6d4a73b18324810eeea20e2278e72af314d9f1223fabc88ecbc34dd78e87d8628d8a9af7700bbcae8565774abea996a5b2632d8dc80616fc9e79a4cd5c096658567698dcb83e1b87b80ec2a0b9e47e0805998657667234407d04bfe3b48ec1dee80e30b63546edab78a43cf65875e91cb8923cd47ae47d20799df580a10582ac1f042ac23e2ca273956153f7f1e4100f69baab4b70bfa24c76eb0a7778cf75c5e252b8ab40b543e91f51569b7e24b6dec55201b9ff2f4c92aac19e08eab3136b32e8038ea1979c3ac56dfc86086707162d587ada2f6029a4048becc2ebe0913cee99adeddb323a806dcfb0715658b57a2577d758db26767bc1166c87c7a23c487c30262a2b6c3938ce39d27fc00b840224f300081d2828d8cf6390f3284b89c6c1c4ad37921598ae40b47a51d7dc3cedb2a37f3744ae7a06bf07d8cd861343063b9ba79cbedefec5b7a5ce7aca283e1e60131d26a366c2064a9479bb8a7a7f376fd7426929bcc1c8c133b4f02d1060325324c050025ffa8cf6b9880a31dde9726f77fe8503e0606ace643f1c4a0c0065f857d483d0006405270ad2ee3a31fe0a40647b37428d905b47b99aebaa17b50657cecb63dc16a2f5bf860c5d1a9b866f0b6011e53f4263d81fa9b4a2039186d1c4e405052c97b9dd2c17f5f34112cec4a312af78844896942d2c6a67e1745068a7d4fbd1a172f668f4952a480a4b0397d61c7e26d8e0e2994caf7177e2d931f91ad0833ac6c37aa96238d4b3da6373c765af79d71ca3b1376008bcb43d9763d28936d771b5deb372655f727152393e08defb500cf80e24922a24c84959558dcc5eab90047d7358ae037632498dd604fb0254a30d3842b5798abe0fd4385af61f763d1ba82da5c94f1f80d25aeaa4425eecaa02f944ca7f65d98aed94e09370d95a46fb7b6eac3f6a2f005d3eeba3f0851d59dc0bd0c7c7e612875042df57a5c1aa19079963ca53d88493af0daf0039c41cad70c8e5c81e54069a23c369c3f854c1f7d8bc0a3b2ee78be89887edd8354323d5d315b89590b8c7f154c5d168ba1309512d8490a90c82fe5baf3008b249e9d8eaa026d66e49318c946cb1eecbb666f793f279faf26d535c49b5d8f0dd972c5852027dde6ad154292e6fe9579051da73b843b181e55da0c93151e63decd5e127a5dc101940eaaf828984e906b68faf359ff15480b5fb50c4341d5fd0691cb4f487bc740ef5bae6c31fc23b20a1d0c7e799e2202c2c552570624503c2be80c14840b43a498a95dd2e831032cfb24c4e2e52decacdca18072a58024ec55d77856cd28bf4267a0ce9a53a5e60a7fcfc08c6858f043037d0b3964e0ef0e41794bbbe4a0281bd0256704d58d6ece3146b51516d6aa51bf84a95c55d698675881988241ee1361c486c96be211a0b126553015c2d25f59aa2c9e41d4cb3652ab8d6223ffde68c539c77fa443e35cd0580938ffe9a6430f42d7ac4dbdb99de900ee1e9aacc1f1f967639c4ede0b49de2d04abbf4f06439095d5688c210e746f4e243de45e3f4fcbaa9de4c4151ef4ab7d0e6d2dc13dc4fbfe2fefb7dde0740c60391f4582379ac00bad02ee35715270e30e0b02a7b60465ee98865e44698e32f77b72583e37b1d134ca3b61cfbda09facc441f1f442fee90089ce9b032dce0f38a3b47dabf2fe6993b0c9569d0ef3d6deca94d8a8f18c3549806400461f206a0bc1e6a361e8a78eedc97fcd56cd5781f03d6c15be3e6056b2b18ce69c894e2dae990fc67269db490d2fbd25a1e25e1507854ea864d6467e378039e069df6a916ff66611b9766d9ac5c36a1368f250f684916e58637caac9d174314a902984553ec47e87e867de1130f97510aba6d2524c35b237188b7c9ab62d7400c6a88edf5a502618670d030065c778b50fae6b30a498315ff264597503df77edf919adf2e95ebcecf145cc98f7a7e4d502e7c8da3101c5482415a554c0b6597d33d1ea161871ae813645296c71fd4fbd1f4fdd575030b8109496989c242078a446da84aef0bcc0e3c2538c2ae41f74c2c3713f81e91519ee235ad219873ef13d4e48a1b94775edc7ffd63ec6186f21f823c123053002f7351de8644d3a2446e43406701c440ba1e7b85f257dfcc40778e5e64175768bffb6302a29881c4c724facc2787dcd058af29cdce695daadddd235240c573989101f1c5d74bfe1970e97fd99ed3ce4767c0d4a2e10e5021da245cac22d09d3f826620435b741a7c5d8d779b2eef10690251748122f300d519edb8cdc15727375bd8309129a764607b7f97a03cf1b93e1a2fd7fd5117dec0724b0e2f2eb88b082a48fcfa12f2e29a1040b98514a9a595405e4a849135fe93a27e0e028c4e16f18eea85db8e0337b03e6ae8ad959e0248f0fccb62380b608c75e120f00e704d0a039d282b0448cc040fe1b43216c5f47c81dfbe65234f7bb6d5bcc2c43d77a8f205076816198046c46ad8bc679e3bdb36ce2c2cdf4df4453220e2fce341f83fb533686485290324645b3fcf17962d432efa4434badfadd15dec2cdc894680dd49297832d651218cdd6874ccde6a694f062e8864620f3d9ddc2910ecd245bd5baeb2e87f5f3f61644a31066a97528d9d12a5957726f66095b3d06c322b4da4bc2a678415586ab92e369dc9a9414ae7348eb1b3d28a2af9f51956990fc8accc559697b323bd253c553e058e00114c384a7f10bc3cf934f989da232721e8f5563572eba478224b281756c8f0990a01290725e68451120905be6814102590449e0023d1103e0744483e6244cfe84a2a4a24b3143b7356a42e4a5e1210101fed069a516efa98f3734729b23404442cd9af75ec127d19a1fc70f7ea15232c3cc3dd960be4f9a223069d8f6b1d9ad99694e8fdbc5f911ca89401485d42d75cd5ff4e4d249e4e4679490a21f02bfd4b34ef3bd1dd22a84acd6249ea38dc3f438725fa4e12c7138a04f17c9028ded29f886b7a25bfe8c4ec14191fe2f2af7c00fa33e45805a304197e315e0621272afc7b0615f631fc86808bff153a8b92328ae7d7444391e7b28975ff932c03fd3f5e260aba7da520df1a8d8906c52b25bc2d1eeb9500afba35a2bb8b821abf027cdaec929753b8179c529073b1b84798bc9ca9fc8cb8040876280791f32c0e361cc6417d356de37cea00f2ea84e3cb53a5cc8fbf4641e89402bfcce892af9f780f7840bd82e71a9300385255c39e9dc50089ca21651ae0809ea195e90af20f6bf7a82a43f37716f9c9797ad48774d3cc376082f8eddc18c75e447c807dd586ac8383794cf310f85513251ce4910962aafd64704d1019876391ce2d63640010c894792260328610b1100f1066f8970a35735fdb4b38f37600e476e6e17c8ac523f205c51c01fe45d823781675ad38d55e7edc9eecd24a5b229609750ef16ae67d50e05c140a4059841c74a0d3299dc29c56d22bcb0f0d950dd619af2b6fc976af2e30cbdc102494948a494dbe01f204ea73e5fa784442e5503fd638c6625a481594ecc12a42f2112455277660f7493ab0af9a65d633624f80e2bd96c6111f021ffbcc05ed33153976ab88fd6759882816b287265559c9fc9ee0d53901786d6909ea905a13b74e5e9e433389d1cf6e37d88021bc3640a25b984f133bb624eda7c3efd7fcdbd0d8079182a84a718e1a0850778b89dced7eaff80bd73b4ec6dd545aa56d40ca3e40a0814456a0171eb01b62a5706c4010870a1986287f399a5fd96366a5fedb6a75658dc14f8e3598a5050c41fae10333d97db3d83fe11aff671b5e14dcae3fdeb0b04e05de83e754138666a698bba716ea13da3d29ada19e94de623fa15085087599a917f467fa9b36ec78062ea9584400ff2135a1333c5b181c470d40e993b46210522a035e059402f51e5c306a0690ac557c5c097574b7186d6b884de17e152b922f312e5c55c4ac76fcf0fdadb6cc9523b724fae1d791e2ab8a7498844d31010042aeb259090000f7abe69faffbef695fd85ffe57e6dfd9a81ac01e9f3e8c1f7c2a28f9d8303440328d120743285afa821649239874a1cec2f000deddc6b71becf7fde123dd36291195f373601be07ef666fe45cecdf757b0f85cc8d0df359bef42b7f0f7a1c0a9198b2fd8b3ef01f2ff5278d2c3bb0c81a1602c223ebf0c59fa979b874ef30335fe27350d05bf63f2b9a4d4bf12ea862ff3336734f97f9ba36f25e89a7767beea6113e97dd12bb753b560b3ab80820e2c6ab49162d20e5bbed72fcd1a919c2562fb8950fe871e4363f488a3e46eb7f533e71a639ade40e070d844263939e4f0848d7f804ed151c4211a4a1c44a38853ecda27707e0ac11ce8343a820cae825e30c10d72ffe88e4ea7d1a0ec3062237dfd8c4b579e298a051635814d8163bcddbbdabfd117bd999285f757257764126bbeaa52538d089b3465525d81e03889c4cb87021bed6804a7bce9ab7598c6495af89f64611fb02f59dcbbd873617a4b49f7aec17fe2a470ff552df09c658aeec5b9e23b3a60aa549a46196f02441fa705279b4bf66bb2f4d641b1924d3be627b23c748386a3d869152737ca570cf76db3f5771e503b0d406fa57f794121003ab1c36d8cc1cb621bf866e3c6aef2dcb890bb8fcde4a6f3c4da17a2e30ed48d9420bd0e622539ddda82529e1f776536f2419a764392310aaff2fcc241600ef62612537652fc69d5b0d859664488d4d6df02bf32ed49ef397b410b2c1da722520532cd1006250d57ab890db0416c57706cfd88f3254880c8bfeaa37a4192238ee25dee7734c7140b300176a0a06fbbd7b37d315f0a4373e9b495a256d742df3f560bab5926465235f183332c9ad687525216b54410a513e22c76bd1ca6c9fdd39a70126324a89ebaddbb6de31851f492125b6165085a848ed7993094cbd212e7c4a3cf7dd33a5e92fa223b1f2376360923b43954d7a3b204b14732aabff09e467f8b5b42b086216db8025a4aa8c88747e4915d79bc7f8d32bcc69fc625e54b7aca89ab9f881ee1fc542bb6463e6e669439f2c5ee761aec972f102ab0503ddfb0462baa8419052d3e467ddc6657731badb93d8888fbcfab99e6f2566c4a76a53431fd9fb5a77446f3121ebedb0e003729f5680e05a651b269326b310332c496228e27b50237a36e0376aaa0b75bed0cd0733c25cfe472c033586276a901224aeca3bc082f091d3641485e9d430cf5e81f68b18e9d3e7013387a9926a54c2b84997dfa612e591760e1e615f5e0805347da66165a47c9d987b49967ef04939c99ee9bffb20020306bad6096c9b179a8ae7cc1c69894cf4bb16a6c56ac75e71721d11e2f41510c7ac1955d7f588d8e8b013635c0606991bb2b77afbf0ce245d284ef8c526738125cbc5e3b490394d63ef73258b498a1303003511ff20d2332b31a8fbf238e0164df40507f190def9cae633246a13f6ee5790bff9894f11a71764edc77202e00d006db993fbae0948642c532acb83c43328c8f511a8a88cdb076780dc8300ea36c28228661d5e51992c53864a64694b6216bb638c1c550d8df18259262904aa35659b2a702905c6ab23ebe03315bfe04344392d07a3def91b1ab51bf090ba8eb4785ff55244340401e3574cec583227071e055a8915fb38b1ca4a7686ec850c4671460de5e01a015ddcfa8ef9e26da2953eed598480e78a3ec9efdb099e9d066e73d0c3de5632a177fc84d9a4716950202eb2d2c58c57174624e05010a80f357144a7f088d3b750699d75d4f7d1d1d49885390fa566da2e285eea4939100e77edd50fc203603246d820769c5020ea1e8ba94bcd0989f374b704f1318667ea6657754b69f21863bd4492b538c86ecc7fa343fa207ba99028f05bcdbc49d5f3c823a01480ad54383cf9a16c04d5329437494fda03302b8b4f470d021c98309d8cc1fa8d3660100304be25886c4941f6820adc00d502a7b8b3bf8bdf2f0603b380b86a50aa5d2a53d0d425dc21ad434cb55365b24cbcb33d674ac7c3a8f70baf46e17b2a52f362ee21c268f9fbc1b4902b9844dd40252f194820a2e165093ee5b0b50c5530250387140efcf6f5940298f5248c1c1813ea3e42d251936a3d392c416352ca0a09c88ba4ad9e60ab8261a0185c8e275a50d5886c714a4b9de8d97d086a2d2f00029b28aad685715113decfb5331137b6fa125b9508d20e94f102f2d0ab322ace5d388972038f152dbd78f3b08cb4e15a9881f8d946999a6fde8138b59c2afe9403f9e20992f46c7a9e97fe808871a4d4b8fb9f9e2c846cf3f31689d2d70ab898d38e40d1c90494cf0cc68d83970aab8de9a6d02176214628bf361e0cfb4abbcb5580f85a4ba99bd8c218eaa70c1957b0f61058ad88bacd0ece5c62c95244443562ab438c929f5e1cf0b243c1c102f83a477b5815113fdb98a43a509a2a8acbc9548550733ffb8c13765ded4805d7cc7a125b397133b3a423f094955352c1f56d455c6659466f7e0f8d5a0395d7c29bbccc635eb6c3060a6fb26b501dbd4171803787c3f75d94fc4d1a8b5a33378edee6a1fe6bc459cdc62fd65ad2cd04ebb90e88007549a8a13a21ff2073128463096e6e614ba452bddfa394a5110a4d109ccbe04b61914868469c769e298289291f9a8caf1c7448c04bd27da70eecc1f5c5ddbf24bb5ffa8225e02bb7f1b237a2c96ae0694ba2830ad7cd7f7c3ef96e8d0a4c05ae0276b068032d11ebc9b4004f4de911f918887da54325ed2fd9308b6033398e1a29ee7c1609cfc4f057a422b4c14cdbbaaba312c1bd382711f45b0e6d0f26b8b0e6b1bded7148cc159e9709d9199252fcfbe64d507b5ffcd6991d34ec689c16b5df9295da443c61b6a398b35c7773a98c99d2f38d7df330ff63a177caea4fe9089cf6b79c6ac3ad0cfe0fe2c07aa5d4396b774c28f540e48c128089eee982dec514938187fcce2ca699d154d037161fc73c8b0682f018183fcae071990c7b401e867b25e910e833cb2e980f6d408d787995a97b76255b3f54001598dfa244765fd0dc954dda31eab1caadffb539622b3410d0b2076e415a77a32c6a5d7f7ef8e1be3eccaf3be13eb12e87bed2f56d95f8c271635fc83711f8826f0d585dca7e4b20b9d6a7609da41d4a270a95c28afa22d342625ec5965cc28589c85502e311b1e9fcfb58a51ae898981ced82501e00fc2c64708f03f24a539c54f1fb172438cfcf17d7c1fd28a90554adfbdcb7291ae62f4bc206b393d0ede23e28842759c24ad60b638721321c83d6580d5132ae8a77e7c9bfc8f68638dfdbfeb4d45fa017cb1ec9bae341fecfc995beabafaea5a3caceb65acabf3a59c96e6592dc0662d3eb2443da658803fbd9df02d2d80eff49c5575d00154fea82962cb17c3cdcc46c5fa8fcc806d1800778b5a793f6559511afe43c411420f05ca604500de2564d268a3f8059966ad2d70051774a0f50619a8ab081c1d65b28cd6307a0944d42a403c1e1425e52e2bc528f1f944d4e8910f5d7d313d38ad29388df227c06ca9656bbfa8aceb75452f57e20f4e68310b72139c7b57d7d32b85a857e26df6103e23bc1e601b73251fc68615b183d56c7918a7b5b8d88aca19cfba5e0c78e5fd53bb5aac171e4280e94f71a6397207831ac881943c57c68f871a7e3867d37559da4190a60a14b0a37a56fd50ac555b188a6093fc330b24825e7bec9be005f1c83c3057de29b675deb8981d4cbcda4402968f5087c18db8d848d3a1a0e9739fb66d4fc2a85b435994fdd8e1a11b2477a484948f3c2a3acd98184e23c9764833173ecfae8554c9c3844dd381f6fa201aa6bf31587504188be2b4f003981df20f0c96c8c07fd2b42c2f07f9ab4707671c4d880346f5700a14594f1a91b907a8e83e4d480595ff5d9d888a4ef3abfac96335582efff6c0c4f045431ab5f9a8c73f5618be91deb75f0595fcf6613142842a7bd73370105947a413df93efb0e28a5809432acba0ed872c27d0347f44c259a80fc2aa8066cca1fad7ea58baa824af49b0cc233eaf434d25bed02445fce6df1b3a03a695f3d41c070a6e77de98f63a10f154c4b9d1062f035681b30fb0aea60ec061326c84f09af0d58da6e7e6a251a31932dd4e1c994f45211528790cc3f54911dad60407e394f4ed2c78bf2dcbee98ce505e18ab020ba32f3841ed4c2506f731bb9f5dbe033c6ad8947e741b033cd616e1c0b89a66179d5017e1085d2f8e67ea88351fc9e6809fb038449f23405cc31d8f936bce9771b12d29357038caf541b355d61912509941e3436a67cf835cb7c70e8562c96ac54f3d28ccbc55bb1d0ed5b8da6cc37b1a45aeb6efb3fabe3f066b71e3ef03909d847d8e275ea1d18d74d398bf75d7b4a0783f2b56e82cf9c97fcd26e1842b82617152b288cef51232317d23beac17345208c283831468516e1f564847f497e0034c59818545a285b8156ab4fec2c7e5558848f14175acd725e566fa8e48ba931e92a6577fcf978f8b31e1a6032af8aeee3392f4f40f0a58f6cec702cbe54dfa55b77427226eecd41afacb184c82e32f84759d02ca46063eed729d4434b89130feead8944cf699e2ae3d9cc23befe27776befafe9ec4050672b2c41464e55ae903839a9cd511651ef4dcd3a16153280071c69e0dbbb236631c48b3520f0392dc9664c3141d16f0c6a13f88ade2baf264221c4ead2e05981eb9f27c8fd813a2dd7f51dad8232204d5cd8f48aec739c9623c1e325fe6acc8b52c0aa4d7481f71a16d51c52ccab6586c5bbdae539d7942940cf11826a23040e5a1f680c4f099e7c108ed92cb454071adb31be9e092811dd1c7da14ac644d98225e4b7497f2e3f8d5bd7887fd8dc7b0e4b82547bd6384000162c8f13c0b9b4c3c8e449f3c9a81ac0bdae0511ae89f6c911196d2d22d89f3daf060759517067f495adce5c2016529c8a1c257153a11d40172ca61519e56c280db00a74271366e2daa284c4edceccdab0f382a3d22b5dd85c1d742f0d1dfbe98fb124d81a7c24055af40ef2cb9cda9b217a2a08b6584a5b2b39c2e1b76e1c6ccd6813431e44b082df25efca0399f144230570902ac9098650755c2474b8abab818eb292518fa1bfd6701f1eda42ecbb7d741f9b8ae17706389c9c8c7f252da64a7266d2013bca316398d9db6c5312337836bdb604d89f6f8ee38c7f3ffc1e29f51a1fb21ab77e6861016a1bf1c4f0b5cdf22cfcf795708b82704bac6a75887112e591c72ff537e347ff16b48043b0e38535d63d061179abf9d0f61f7329dd2f540331aefd506a7d85720250280adf085c8d6934617ad5184b5b6b3a0b59b1daf4d8413b8b6e09057e3a0e225e89a63c37b23e6024eb338cb03c3a3e00d8354baa811e3027026d04644b3f9088ac26fe9b0602a3442101b1e87efb57544093092fc3182dafa6a3f70c84ae130a2484745362dfaaa231f1f74360c69dd14dec35666442c1f1479b1ad746ea3b9fc96afc88b4729bde88a7081cbac0ef23a3c0abc18b4c2858e8803b8978543de351c05e09d407194d8879127234b08c45bc6f0f448ea0dd0d1cf84c529628f8390ef3761d133b90d1e41473f098b53c43f0ea1fb5994a8d5a1768a2d4a62fe65c22253b8ef8dd0305e2fd84c12417328390583c3a097b67213769c305f24e2fccd1fd5739005700faa3a9fa25d4c5d202fc2bb06e4183183088b14637439772d58d5e65d63602996fe40f793f545a367eef19151d2943cf66425fe1116babc7d46acc9ec958f6d0a13957ee39720eefe8ad8d916cd07b25c1d16b1ab6ca91f762eb6c5ca8d16470aa88440d5cfeb07e12f209352cff84a104fede055f6fede7d9d979188396bab259d2a5c39c19b8ad0014a2c64ec92595e9ccc0c8548793436c72d11e737093b7062aaa0733aa33955b26ee936b475582a6aa74a350a31726f0d2aee2d82bc74adf08b1d1bdd7a83e8ea5872b708010cd6e15d26cf5b20ef6f12ac43a6f48fc320ac76f419ad815667e9c364c089541a750ae617f8735e5c66ed2d97d3a0b2b461a248312cd7596afb261eef2a3f960586c988ca56df8e50686211d16e3209c8d904bdb3a202d5c9b959b7e69ef5aca5d8f66feeb792676d3f01ffad7eb91da17200cf859bb234f47468823c8b64d899e9de50dec61befd9e2668030208035dce1901fc99ea1c2d7c75405cda030200462c7b348e24e85a28a688bfb0b9c0b96dedc3518defe4e10c2ea5889bce0186a819551f872a07f6b43f2a803671a3deee1903d986d32d8640879ba21e416b8bec6162607140a04177b256312daa1600a2eb5ce93918bb9c125bf2ecb9a021031ee33c08e22d27f3635d7e002e26a70edcbc781aa940f8d97e969e258a76f882223f2c26b3fdf13b7332b5c09a459a65808914ffa73ca32414ef07ef8b7e4369be8e57ca233932e91b577737740d836fd8964d1b9e024e7c831bacdece3850309d35e9d6c2a04c06c900959c123203eebacb4f5f2fa0c6940af3ec58716fea9433ea41d81a4c569a20dfd3bf094bcd327a4ebd82b68f8cb325cb07fd432232be8f8f4ac746b80fa8038059ea6709313caf9fd21d3ee569edf07bfa3cdcbe78f5f0c8184257c2cc6dfb51b6fc6a766e612d94484292303b90233fe43bbd6e8c49261de4d45caba580997fbce9d5fdf52a78673243b835c783b0031c78e406dc7ce1d329219b27696500f2fd59611c2eb2d1ea2a7d5053605043c52eb75e7c975b218e3c734b612c4142a631fffb222ac10e70b3771cccf0e36e8fa74d853c8107bd8d1915648b2be683fcf5372d215c891a0d463acc776dc994ee1f43bea1cf760a4af23483b7c44ee9d3b0d66a3f60fe7854f43ea291c9509ad901ca029c062168cafa404c5d44146c41e18c8d584900fdf33df76228fce8ec52907f77568922fc901a9520bc653d5bd4938e5bae1345325da493283053f4fc1944dc84ab9dc9871df0441c11e94a9f2e2819ecbd840c74af1642437b17981c0a9549189d324b24c9644c758901747095e0efe05e88ed7c86601b20e3686ec320299f8ec3dea99cfe70459c785ce506b4dd8bb3b585984c0ca0df4a6f675f7a74af714a83d515780f8c04e7cd64a833446ce8b5001857cd947f020c087dcfa5acc68c0c7c1e0e013d0a448d7a76eed847dd352eb0394a2b78e69123533a8ab588077468f0fea3be9878394de5e706e7ce7daf8ac43fdf8a2b4412d71748ab8c429be670a8600839b782bba5c754dccd0944e685f4ab38e9d9426f21704018ec3ccf41485e8eb57beb334b03495a0a12f9b24e11883752a296b61f59b62365223c34704e9e90aab272f442289243c5c3796cee73a78c76a62eea043e21d66671e64d7c14ff3d7430777cd5f29c961bb83f3dba395e19d2be02f4187f8882f82c46307ceb78814432e0e8400e9579a8278083f8284fe3f6049ac013658b08324b3cc661b6d2c368600e0a3a381cc54500d419a0d1c3cfae4b845cc67e87ca36e1432d77fcdd40c16ee0912adf599e53d044a3c9c7a19046509f69d97dd2d6643e1c5a5c75070340bcc303c1e6b6c3aec3a0df89e7ca320a0ebbb623f469cb4e03320d7d08bdd1dfca1ef01fec74f71125d9cf41beea28d7963a9df6faf6637e7e12a5fb317bd79ed3d985c29380879cc8cd12e966f8b29c62e1c86dabd9d613eab886a70c16140bb7e148bd9b3882b067e66cfd8dec18a2aaa80644246b615a0ee180c0602fa8c5264b4a26aa63b068c62191c8e248044a18547b40b29707a992132da486292c95ca8c28251a507e7aee2d0b8cc0b6516861d8310c9edcf9710590810562b0833a1605baf07b767e9224b9c74b5e90ae465f0291d9f181763a7027648108ae773db085dcc9c1b40054df69f803770f958b1ea68089dada617e06c4c399f800947353c4a1e3aaf036af75f5164bc6653540046007d43b669ba00935b7574b71c789ee4b25c3f1599e3bee192f774e7bd770cb1143f76acc32e8bb10918377974429cd71eaf0cdf7c0fa3eb6f9a8ef3893f94041c74f9242ad6f4678faa0ce0e8b9cf47b80f79e0e3fea328d89880475d71e58fb51326e7a3c55c85675e2a6318d7721951416bed6ac9415f0cfc050edbc5b813571058c96a3af08b8e86152e07f5536c597e30ebbac3d22721200eddf3c5d65f51ef5de2ee7b83c42d25d916d9c5976e24ed6ec9dc5a63b3eb9a6cc8163ff11c4e4d8f482cd768c79e131c03c6b909d8ffffbb096db2770a6915a012b4134286006730044ca4bee5083870c1952fac90a0ca79435ae2c910a01a68f081af1965ecd0e1b2d162f1b00392a22d280043052e288103c589200a98410b52d085073270850476843120e0dc4ae80c8433b4881106185c5c208800f4b0640725488a8808b9d0020a276cb18120aea0400080105958015f176acc40064c724f5b8e2084202ce10185135e2de862041ff0400630808512491821f6a0430e488a88528082119c8003508660f0821474c185073a2005048e20420067686941181ee8c0161568f2f424d190978c1632c6980109b8d88006b2986289023c29f6b084871c888600f1a979c9a400052468c0029668f2640f3610090d01e2648c9102149460041bd04016584cc144124d946c20a201888f0f1e393524bc40df42c6183228010946c0c5063480c5144c2c9144019ad440545a52c38e308d22182962a3460c567882c4114a72c089a20047ec9cac70d9923ac2670455113e22b4aa00a9f8a6f824e0e97c39291c2f84b949cd90a227b8f0ade0cdb05448a5e0e10051f04e7029810412522ed0464c0d978c0a3129c0acf0f27261b556ac6fb552a9529eaf9abcc0608227960bcb05acd1e261b9a458884912a208d68aad5e5a3d608c0d0f8c5989a02e25937a815704a080a7558d8fe502dac8b0d04381a797243124685343888fc78c0a07d05bfd80e0890623425e02f8c017d0488d109e6e30840c5684f0543e2f178208f1812aa46e4c8f46a69a218ca7d5ca05a32626d8002222454488013ca5e09243cc8d981b3e089960b6f44b109e7eb60b111a132e964b4a46c5041a8184102db0c68a05cfc68e103888c24506163469453951e383510579b17e80459e87294394a08abd08bdc4b05a02cac0fa318416501019820a254e70e13491198d1040044f30a76d010290d1b1e1255921058495028f102fac1fab97568d940f222229165e8482f0f4c22292416a0dcd0c117a71198a41227fb4784201b4e17959f99420b37a811902eb418aa797d6500bb5239e542997ca5bddb07a510159c9200382395d74b486562fac146484b8e4008b5c50b8b1420c2a9c20436b8622473108f1d20a593b323b3e888c5831204b05954f8ac72506d44183b05c582fe00e5511b02788a79a49d1a84ec071698da91f4380e068bd9ce0e24388a022181ead189803ccb1a2e249861aa010d50caa165c72802bd8c849b130c453b8c34bd95237604a0500580f4d7400df2b2860056aca15544080004100e1a4996c3fd27a58b283922438201919b161c8a7672716030c2f5070045c6c31830738a84106034c851458514ee08504a4b861480d3e2c981003b312c1161ab0428a0420f00028a2340087988e1de102149084b4250d346a3b9851860744fc6242096b930179614959e2890f17175c99428acf0f1e146ec0bcb4582bcc6400034b9e4452640391d0101f1e393524bc6ac8804f2461c40048c0d283035750808a207248e28307c70df00b2b3f4822a381c785b5c2a2080d54200a04347104001cf0c4134a20299201d111e3152145842f0ad442cbf8b28038f868a0c2e2bde06bc1c702973062c0f8bef852f06df16191bae2630297bc1c9e0b2d16542bc8a4f0a1f099e0dd7895a022e17b792eb0c607a37af95c5aad8fc5527da097f2bc95bbbff0b8bb0a2e189c2f6b755f9bcd1d4fe2d1de9305772f05037adefa050c776de94b142f4fb85e9eb8d3e8eddac7e9680da5e187c668f859b9eba5e67ad9c1f5c2c3f55243a3364c6b688cd227adbe966a4bb7a54076b9e68a1b69b574ef6a4a6118f2408eab1e38eeaa2430950bee728180eb1cdefc7b577347de7a0677b9541723d76efd24a51b5faa1f93b426f6633eb176095d7038797f57aa5dad28f2beab3582561614b76ff74b6ac3f4475b6a9ab16de7ba35fddbadd9dd5b7077b9e132f367deafcb6a638d00dcee3ed3aa71f798bbeb78c1d3c18f1c2aa3399fa30c3f7ef4fcf0f183e7478f1f3c7eecfcd0f991f303e7c78f9e9e1e1f3d3c3d3d7a78f4ecf4e8f4e4f4e0f4fcf0d1e3c3870f1e1f3d7cf0f0b1e343c7478e0f1c1f3f787a787cf0f0f0f4e0e1c1b3c3a3c393c383c3f3a3474f0f1f3d787af4e8c1a3c74e0f9d1e393d707afce0d1c3c3070f1e1e3d78f0e0b1c34387470e0f1c1e3f767a767cecf0ecf4d8e1b1b3b3a3b393b383b3f343a747c7870e8f4e0f1d1e3a3b3a3a3a393a383a3f727a727ce4f0e4f4c8e191b393a39393938393f303a707c7070e0f4e0f1c1e383b383a3839383838351c271cebee33ee62d9b8ce61eebc50c6702fabddb99ab452dcb63a9cb6d702b97be8ae1503dc77cf44f5b6f433b67d8d569afa56a5cea1ce4f96f6ac09b96bb582fb5f4cf5896dd367933eee4b338747ba4f8cdb56477779dfa766cd51dd5f5ae9b5b7af51bc29f9dbccfdde5509002f362fb118101488179b1700506a43014029a5d4dd63707718dc3d87bb832fa8ae501f5516d487a662c2a52a62ab78701a8dfabcd8d068d467dbbea4f48556b371379b29ddb62f6334eaa3ab7edcb86b8e061a1a6850d9b85431ee54db7dd628d5f671dbdd61342e300759a88d0e1d74574a3ed3686f35a178f071dfe7918c731ea2c339632077ffdc052ae1ee61b873b6cf7804777081482e30e635f4e787e6c4e85f5c44a317db6d2271edee3bb82bc501d739acd972675cabee26b813b9832f30550209ee2e77b7e1ee35dcc5dd6380bb2f71d76700d79746caa98f26ef896bd486e93e31c55bdcb63435a5e187ba7b8cbbc3b8fb8bce214ed823ccb9d961cbc1c1e991536b351d1dd2478e8de706db50ffae790c77796b7bfbbc03df721dc31db58cf7f8b81d3abfc6610709eefa70ee658d8282bbbe98bbdf70d777c369c8afaf55e9bc2c3a8724ceed18bfc9e5798101773fc15d5e1477bf5fd21ffa354a9627c6695a43b58d51a727afa5df84739f45b3eab4bbebb87b3cb87b0aeef2b6bbeb1c8e77a75b7f96b589d2721e42a3b7d28b29ad2666d6acbba78fcf2e4ffd47344a7e59d6d1dda9fb5773771ceef25c3a873b6763b9bbe72bcf38e7a7b752f26f3ef7e77d3ff7a599b374db9a37bd5dbb77cd656ca3350af720e0ee2ab8cb69ee2ee32e57c2dd4d7097fb407e69ab8deadfbb1aa5364c2fb6bbd25dc6b9cfe91c7e31eebe82bb1c87bbbfdce53cdc5d874b5bfe264c77db8ed5aca3cee1f84da2aee61d63d63b465c9675c72ed75cb3a5bbab52a0bb7f2408f104e3504f4f334f91d0240d18989f03e07015e115013671e2d1225a291f3ffc10808fa668003c0ad21facf8a189130aa44a72a12aeab01f8aa0402ad80f0158d11798130a94a22eb000b4604e2890475594050bc00aa682a5a8d300acbc0bf383809647e3c40a2635cc524ec0264e4098154c6af898785e0e303f4efc28c005d21a340e042343f3d1181a8fcac8a43e994f6646884599583009ec3eba82496097a235cc3c189aefa32f30d48907f4d22446e6060772691203c40bf55c60405891f4423d0a040c958909e285a6881a80521486a689130f28458ba8010804a90c4d4a7940df07b6649ad4a02d9a263034c6833d0111801485813da59a38a14032f493d94186068800a4680dd8538a7e3724a58c5e689c78402fd485c68907e44261689a145103d00b75e201c150988f46867a343337805486ee90a231b445d3c42587208060a8478178a10102867aa91a156db9d0146105931a662bea01fd447131f12db3158c4902bb8f4a6007ba8c3eeac9ccdc60045297314ad1d4b7b200cc0f14d7476154b0541029af088c83ad264308f1d10f74f7498160cabb008c7b1e93b7c4a22d26a9990b0882a00b65b540960b9862b15c582e2d18530a04413095fac1f3a80bf5886872d34755b09da22aba822dfd9052fdd052c1364d31a954302616fd5654f5518f8689f5c30ca4dfa7ca02c6534b140646e544a9990aa4aba68fae401a2638664c2bfa0263f2c3cc857e344c70cc56b09d1af266add4cc05b604520fc6e487190bc6945a02614c7ce6d4658ad8987d1f08c018087a204c4a86c6a456ab9915a5595232ab99a9621f4f6cf6e998adb60ab6b4a22d153c6999a5e86a66453d5a0316cbe0cc2ac69a69d16f15f3e80360fc1381e7ee35dd82987d9e02541fcf962733ffe208516633ccfc0b23a031db82c6163066302996475330a918157ea268cd107d94d5527d9e8c0d41cc58d465843ecaa22a1a9721aa997d54c5a29951d15891ef0818cfe1c57c2bea4259608bc2b0965474453dd053aa948aba8a36dd8298ad666656b1996a2947ca0b18cfb1a452d1964a45795855bc453f18168da702411655b158b09916f55a30cfa32e9e475d582cea59f97854d43d30664ea15a312eac15930b25728181aa97992753244559acd58aaa562baaca020250a4282b8a31662cda022345554610c14c455725dc28a104125c2b982023e441cd58e04ca582c1c0d034b16249455f2e26992229baa236648854307045552b98d0376351976fe64213832307cecf5012ad746af2840152bc2c604e8041e1938981408c4b8b7e2dbab4a25f11ef0b2380d192216ab9be98d7a251c9144911ad60e007a63cff54353c281b4fc0781f08033f2118ff56de6aa55a5198956aa54aa5220093a23029f7d920119c5c9979959943997d54458508ab551338642605a3817119a729e2cdbc20664efdc891156c29889914ac480a4cad28cd076e30055bf22828736429889994cd8c07b30153a994cdacd542e279f4a3aa09b45c38e8a78281a9a80a86aa543f4cb8be99960a46e5f281339715cc05d6e4a932a8a2b0a5184e18511ef51c9ecdc7935ad1d58a45419921abef9b61d1158bc666332cd88a49457f06e00269cb8769f5425d56d405e6023bb2a22e2cf08b28b3144d3d897a9af94c457f80840f9e6cc9624b94d9f66629eab27261d1150b06ce409818bac199c752b9a8284b15d36aa5605232475474059bf1668ac466ab214b29baa24b3345623315934757744635a3821589b15654b5fa59c2b5a23e43f4cd56b4e9b694a21e9d69d198079b69c18ac462309b8f67459760331e0c66a5f29915f556aa9567e3376e63e6f98d67f3c5bc55050c8c8d8d162c5666206d2da5542f2e34e502cad0189a92194265a8476363d604ce58748333f005b6e4d1189aa25992f1b4cc9ac019483dea4b2d3ac3c49bbdc0664090824ddecc85b6609e0b55510f64c2e54e552dea026bc1fc26b5145b026752d4b3f9620eb3f962319e2a0c213c99398ccdc7e3335eec8b398dcdc099bb0f134fcbcc832d7d7486459d7eb019162cb55a31ad28ab45615c601e136b49073103bf9922b199473d580cb6a48231f198bc0f66332445809894d06acbc808c1f80b9d9189f12331a09782155951b7f9c2804905218b598bc280d485b6102ed80c9d49c18ac462e817fb62423331b4096c0267eef48589770406d6441d968209cdb0a8477d86052b128b11ad60365fccf38f044c290ac6c82cada8cb80344d2acfc30226cb2c45c12b369654948945895c33fd511818caa2590ac08c459754747b600ac6046382a133a75982a12e4304d214fdaacc521444e38b211860a6a22a2636667ac847d3e435792d22302ed4973c242bca4a51907ab026af95a22c181348b7375bc140150c1462e2cd9878b30fd67af1400980942505a42d345a2e1c34e6b0a519a29a99cf6c66a9235ca91485c9204d5190e6278a15055b542543b4a22c50954aa552291006f20c013d19a29a9907635ad116fd689640daa22b19a21513afe96bf2662acaa482b57200e339987860eabbe13155933703b780b1e1371eb5e13e4e3d1360fcf3a6a0220b175a606185191552c081c20926dc28818497cb460d9918981797d6173e78b2811082108420b0a86a096fe5ada847554f5230400e7e3552d4035de0a76ab15a2d560c250286bec8d0189a2413034b8281bd106911b5601e90b3605354535ea8f88dcac9142a53a6b88831f3281517171a4fd5b03e150ca2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a6cc5a4881543c164859340e04040dd7920a8a0ae652c56fbe166080b0e09bf295000a1715f80114205a54aa2f0715ae58cc0687874a05e53eacd9b78217b3852b453dcf7503d7522c16530901c71950ce4062e65f6849e30b2d65681963e6d198235ac0d062458b1737b0a82045e50296475920751fb8bc190e5c00e5c16e40bda0f1e4050d951752a4489122e57bcdbe540aa6ba81e549140da4b430036137a03ee05a3241ca0aeac992095266dfd28d0fcc3e1b52fc064a0528550fa07eb856ad8fc7fb814acb929d79206dc196cc99075b62d1f8e725e17cb16f46e89bb16892cb10e1f1c580d4c0f502ca28f1d80f62be305c2f7409e6090fd7c7e3fa623318d8922b01b32ab3179a251e51ae2955587ec5794aa8a15aa95454055b4a097d9ee7f862de2cea8a0b4a9494d7b241e38a422365e69a92aaf95c09f0121830fbe84745852720cc05254597609ecc7200a181cbb301c502974797609ecc3e28972198007502901ac47832deca6379aaa854cde70416c5e50c973783f93ed5075bc211038b2202330150e5cd54556026f0f1bcc096543c507d55be2a5f95afca5765f6d1568c14284010474ae5040635035d70a1062e0f08085c4b30535c54dc27a5025d98a9b04c8101612664f19b8f0209238a77c213170f688f06081964789ee7b947c6f77dac8f0c2d5a52a9558a0c5005822a32522b2064b83c4ae4034206abf579df9016133242eeb1580cbdc1819a10e1a99aa5184a440daa7a92aa89a1435a32371831ab415546784d0c85597d9e832995ca89dfd4a03232d465808ce1aa019ba9416da8f01385cb8556a9542a4555506cd0d88889a931631159b5545358ab198b3a711f191a43dd975c68c13c06e664a5a2aa29a997147589b9a23282456158f44586c5125ad11818136f8b0a8afbb066535c683e150d9499eb050b4b014010f0e3c9941a30186a0354d12cb9662a570c8bb65a322e160bf62309168b92f00a92aae1d1158dcc0f03cc3c1ad372c5bcd0146dfd70f20389179a98161697cb8f275fec47125b159ba5a64cf991c40f273f9258bd40cb04a4b8a4605c5c680c58837e35e84b0d1919984a86880c6d5118204f802040e6bb8163f5a272690179f2b56866e062c18040f910007e54f57d2a20568a00e339965c56106961060303fbf1c4f35841803668544f3eefe54b3921e23960804071a79e0b7ef3d1964aca15ae2517b0b8e03e58b2ac66402ce0f2684bcb18365a748a1507b896b04479c1e2bd80e1535801e3ca8b942a383ef0a2c50b16b1282a900aad664c5e288be6050d9ad56c1595c68b16201648799f0bb32f6ab69ab2840defc5c91753d9983253a9e80b9417245ea0785e4b85256fcaece5e5e5e5c5c90b124f523e64a8ca658afbbc50952a86aaa8aac9c7c3522101f442534e655e625e54f485c60566b55ad980c2c05315ae9694094cc982ca165022a8e2851530ae8881658c2c3c785a0010282b20137081f45b6d0125021811577901c68c47b18c9185071f8f0b8c081a5133abcfa3711ab0d572ea7935509acc6866fe050db0ccbc1f4e603f3c8f7e535c4bacd5c762a562626258302a2729964a05f591a10232856b69855989c57d5c9801d1024816fad18f5a492901048819505872f03445044ba56a0a90200276df8a6f1d4b5bc56af36f9bc76a6abd4f739f382cebc9e475ec1eb7777f31a5b7dad45c4d5cab364c44a9288a228dfccf4234aa2d253fbfcd56aa7765cd559ff4565c564a23896aae46b48b2df95b5b9d3f776a5d492bd65c4d31cce99aab79ebf89f856848be35cf71c43463fa9f6d343f6edba79f83e8ad4d5f7ea6ba8eb42c2bbd587cdcce58eb1fc793866d97e8c83c712642a3e9f0a5d15d997013fdad314e476d9852b2b4b8713f6e637387a34fbeae354abe0f16dbaec5599b27ce374a1fb743bd9f08b5e66c67fb070a494bf7ebfd9ab4e27ebd5f8bfb356daadaeabf7f1377b6ff1af93bb4a5fad4f7f5c523d54168c810201dcdb923e11e6bfe7d2dc522f9966a7b43bf498907fa178fa2ad89d2681bd728fd5cd5f42b8eea1f2d792db5d52f2979ebc5a6aea7d6f46b368fdbaf4f6d6b5b5f5b6f7567b3f99350fd4df5cbd7e5101ad22eede8c8111b5acd8d7d0e23511a68d44644434d6df935ad3463dbd19123e2d5e1cb246efd352ddaea9762cee5b40d8b39970b7118677a74e408b5e6ec6b1508b7ad4e14c5703bfdbe78bc9696a7ed2f121ad6dc7c6e32571cd53666e9ed5a9ba5e12d7c6aa34b5b6dfbf3a89932d6edbff4715bd791ac38d7e8d7f08d9575c4a5f9e358e9d7c85bbf5643a35d5b770dc768a5196fbaa928ee9e09d3dbb519b7c97f7deacfe73e85687831fd265b526d7f57baafc526dddd4ac30f2db5d9b89bbd5dfb354a62dcfe1ca6fa6df99992f5b7ae36e37bb3547faee66a525d296de352d398eaa64446b3db89719bfa0c4942524443dd644f266f73b553d38cb5fef12ffd5cb50de36c44c37c92946eaa2ddd3d53c5bbaca3cd786b8b2bcd138fe549e2dcdfaa3ee99766ae3a3dbd336a5a9cce7f75d886a90de36cd24ab5ddb8d44e7fc31af2c77be27b2bdd953bb27e2e2bd55517a34e7fc95c6f9fe9c5f65a31c48dd8fc5dcb7008d00ce8bc595ba5b76b77db96f5667b32f94cad59294d641a1f37ea106f4a7338dbe82ed7265a965647ebaea47edcb56f0b85e8ae34cd1a6e7c69c6d752f2c9b31c2dd5ff393cee539fe5782dbd955a73960392d09a95e667ca5827da6c38a44cba6e7a6dc58d37361ba68fdb48343f591fa7775f24b46645a264b87b263a03d5d5ac6da977cff4241e5f88de3d93780b4d4a3e1d6264168444c9dff5feaee3c5e3110d35a5f5d8f649a3957e8dfcbfeea17ebb6d596ffeedf4d53651f235369d9e7ec67beb90fcd7bbb2ac58ef9ec986f3df2742c97f6d856b4892bb933008c922e40b979029ee4e69f8a1d48776bbf6da9afb4ca35b5793fe786b1379eb383e89838cd85d9e4764b29ef367fc243f0540e288a0330440801c5c414b70767ad0d1facbddad547f2e578a1b77cd19793db83b93bb80b0b8cee1f823e409776e6e687017101477279f0905a2717e1fe8edda5d793125eb97146faab7ad516bb39d9a92bf6d16a75f5f6cea3fca8d4aaebdd98ca44442ded94ee7b736bb7111d2e746a75bdd48377bc4dd7392e024b935cd94f20c87ae3a5a9675a43357902aae204e1597fb6de997b76b359db9820471f7509f355790cfddf1257576ce1ab870771cdc5583e96159be4e576a73bb76eb6c6dd486290d776579dae89396a4faa90d53991152602bf5a558df8ab58259e919f9b9df44b9df441ab76d0e41e4ffbe44b87157201a36e0eebecf9c8730211992a49364dfb7e39de524d9f773bb6a8aafeb977ff463068f9c9b6bbbb9b9b936318b3f36b6c992d4c7bdc4b8467e0e8f321e3ce78fec67fc2419db6c8a38bf0f17df9d752daba96b1e524d2420405cefaaae9cfdc5ba2ffa3163a1dbb563c642daf6d9c8963509b65da2bac799bed544ba5dcb54b7167d82fcf081875bffed766aba71379ba36fee76a5f96bb9f3d2c76d9bc7eddc79cb15b7e953dd1721fdf0c00f9cb28e389b76fc2312e976adae9fc71792eb5f6ba492f7f3db3e37e533c87d273af1e02122dfc90a65bcc76a23b1cd6f9abf3579bfb43f668c4b5b59da5a7ea39e19f78d4ba4cfd9a43f99541309796d11f25a261f502e1fdae5c3050f95966c5c6a1bf25a261e9684fa71ba969a92e46fabb1f9e4bedff4545b5c120d372e35fd9cddb466b4363bbea96fc86b9962e4ad9426bea6bd69561ca591d732d1e8fd267ba34ffeced8fc4ce9ad545bfaf97149487c80e19bbb4df5db6c98e7ec71c6aecc7d3622bf2c6d119cae1b897cf262205d626c543b91c85c757f54626c54626c445e5cbb385ca21fab8984bb9df80ed9b8e89bacbe6f7b21b76b77386ffd42c8bf6f22f95db72e3136c51cbe395bcb20f7b29ce96a2671dbe6704f6ce67090d65f929fdf4834cdeae3f3d796178b3977bf378b34dab2761af9f8fcc8e9d14327eb1d32769c765a3a6578febdad2ee96f354794ff76ad9079332a6dcd0dd9d81ed53e87b2dedec8ae96a6b64292b605b262dd55084f0f9dff8c4b2ac2d343a7e290787ae8605dd233d512486747578f727674f5086747578f7ee8e8ea518f8eae1ef9d0d1d5231e1d5d3deaa1a3ab473c7474f5684747578f747474f5084747578f7ee4e8ea514f8eae1ef9c8d1d5239e1c5d3dea91a3ab473c7274f5682747578f747274f5282747578f7ee8ea518fae1ef9d0d5231e5d3deaa1ab473c74f56847578f7474f52847578f7074f5e82f2e12da6dbbef93443942fcc848bfd57d11ce0edc8669eebc4f6934fab8241f9f6a92b7e6dbbedf34c4dd75b80b278a87e4e74efd192787e4ebb39afb7ece67d0d6d6c86cbf165d38b1907c5b9a66a5f7bf4675c5d1fd9aeeb6cd18b7cf4a755fb4e302e110778534b82bece1ae7087bbc2586b757110f9e43d315012f9d9ea17a2c346304efaadcfbc2f0ee43df1d1673c22e5df38c911af17877d4f1c74bb38e7a0b373ba7bd2fe5baedbdd918efc7db0481989289ae269e67090fb6ebc7fcb9f1bc234d65a5956211cc8bf378b746dc5055d243716c8df5462249d333e828b92cab2069d2546223fe38d848b8c98466c04c96edbd2d6acae2f54a499c6c77d16bad86a6d73b513a9c4d8e8cb5a2d6d6d9a5d1c36c669b35e24773fe23e3ef9af3df237f7bc795d71e68ddde599dbb1db3657738605b83bf1f4e0b1a3e394433ed3888d04f96fdce4e373bb76e326db74f1f81b27f98b8b7a74724e7daba550cddb694859ce466d8ffc0be93777dbc747eb8b75fd231994dc55b3c55d353f70574d0edc5583c5dd635eb6b6a58fcfd6b614f5add644535b234ede130b6d5cf4242eba55a0cf55a44bb471517edb547f68e3a2df75b4a5790aa929dd7c7d93c3a4931a166a40a778534dfeed56dc6862fae7b5fbe2d1e64d4facc3123277c5ac701d573f26b5c56d8a1b2d4e476d6eb5b4d124f2f5494fac83ff80137bd091864e8813e6340d014272948444f3dc93e2e91c7e0de7ad73429c50e7e6da327fce06a2978e9dcea135abf8bb36d96c8aa24ec823c4b9b9e1f2f3b9ebdb284ddf536c62a2e993925fd6ea7dfa35baf599a3e4adb4acfb7ecee69bdd33b93b11f721ee8a210d213b88c18826a908072441a290221c901c894262915950d09022f456dca6f945f29e38d3198f3027c4b961aae3f84c4f2defe94988a79727f7a727ff9e3cf5e4463cf91147b8bfb88bbb3b7127c093bb3c3d09f174c493fbd31301fc690877f798fbe82e1856d0097772429c9babc3b986d31597238a529cae43eeee3974e0eee3638469c425907b59b8fbbbeb852aeebec7cf198f74e3ab29b5594afe1edf747aaa2d7d5c12054213331669e403e91cea6d73b6bc0902ba5d9b379268eacfd5bbc76ae670908fcff8241e9b74764e597e232de6379d6a8e69c4652d9775e32d3ad59c53cdbdf8bad6643a3be78f53cde56e128621717fb2e69b89c3d1d6d588ddf612f960cb7aadd068f5b5793b0dd995a639cbfdd63b5d7146f98ddc3f8791fee2a2241815dcdd88bb688cb80371f71edc45c5f0dffaaf88b34d1cdfd43bc66fa2509e003c1d2fa786a772b99086eb1ce6df56ccd5cf5bdbca276d8e24da5f96d5c8e762abf3274172f71f3d6575b5c0847b18863af69736bb7ebed18d8b68a869596b798e651d6fd792f7a49f2bfd1c7cca6ffa52acfad6bc7d70bae25af871df19e334c578e374c5519fd7f99b3e53d296b5e912e91ce274c535e5d77ff49f85466d733e3efa3e13dee7ae1be9c75bcb0ae4ee64f95687fc4d48c8bf3808911548e77c7cf4dfaad67f44eb1f71de1a1b61db25da65bc2b79eb91b2e637919435977111a977d54846be69dabcb54f4e57dd8fcee18e7047e7e606a72bee9e3e3e3b63d33cb5f8b99a6d7fab196fede3f339bbef99574061051356f068d4874677a5bfebd667ce380817e9d741a98702e9397990fd88653df758f5ae1afddb44bdaba6d6b50cc28dbb9ada0ad1ba96630e1b77b3aff30ab86668709d43b2b4274e4ea873836dd7de7c59474dfebd59ad71bb1a7511850a552c208618280ea58186eed7369b2a861862a034c828795fe523893bb80107ad08e20e0e000c721008873b380379b87b91bb5268ed8cf50e170e1e3891bbef70ff50f8747077d15d389876bdd9a6ea3d0e2d81ef7be291d220a3f74b71dbbe14c912b356d89aa434d0406b62f7cb70ccfa8be2ee3cb80b051e27f1ddf19f776d4789c71d3b23ce68c3e961e3b1edece4f0203f8787ccf951499e1e3d7a7aecd47478ec8c3f5e67ec41ee18b73ef30e1d9288826646b320fa439d489bff5e9bc3eed6231827b98336f949db84b351dd5588bbdfd8b8bed8eef1cb211a9b4521f9e4ffe8f454e7dfb8897e3ef51de25f905c03b8c86df19546a28b203b9da75220042af574e42923a3261962e083af166080182a70860e9e6a6a62c6075478389884f544004828ce6a01912074a0a9c73d01e01727ae5081b38210850800e1cc16676dd9b265cb161eb80a0147f8b8c20602f7a814d73a31857f392057d8b08bb90a01a910ecc4d0c3553b5021a40747a87b0d4b86236690b9337951021cca98817b2108e24905564d9c9542152380220d240eba7cc000e2079a9c554495274b88b0c55737c0541aaa6c7167d56f6fa1c4bf1d0c8539ace087bb11a878520aa20315e541410d30b0c5593f463046f9d980ab701005cd83aa714f4a0c880997039e3a52821274707a807f547486c034587016110790e2c9c813ce32a30706702086e61e4604d2500247037c35a3c3861a51a6b8fb0cf766cbfa3e58bd4fbcab465ccb13cd593996830ed2e0392b8a47aa5e1ecb6d78aa13bc1a5e4b07f4e12903802db0be564b05a63ccf51f842ffdcbb01acc173161095db5879ee799e4ac5e383f9dcf3bcd437e4792ceff3bcd4ca87cffb3cd677c291e7ad3c967f9e4c98fa56abd80bf5c0ef039f78a0e77d2f1e103ecfc5551ef8a5def3963ecffb5a387c339ff7adc0cfdbc1e77d9ef779deeaf36ef050f0bcd4d70a812ac5aae19de07926b4525e03bccf5b7d9ef7a544ef035f5c787c319eace5a972a468f058df07e6a45ebecfabb1c1e30100cc81114a197d0efa17010f4cc540d5e7a55c7500af069e7fee72b9bccf2bc36b7d3278dea7fabe94e7ed783bbc9617f37ddfb70af2747c1ff87938a9102685560c9e0c092a7c4e78aa2f050ff4529e7f461f7df140ffecca29cd2b40ca6942e07990117c296fe5812c4fc64b41f9be964fcaeb4979df07d22fc625e6a93c8f8503e6b880e37d2fabd6e7799ec7f23c1feffb60905a792e1f986af260dfe979a91bcf05e6f35a2cef0bf2b5f0b5f0b190f2947b36bcd5f77932a02b057e297cdfe7ad3cd0e6b9e4f060549fa7f26af898f98858302e9f576bc5b03eef06afe579a9cf03bd218f85efc7b7c207f3b13e9597f23eef83f958a09752a53ccf8797f2629e8b17e3791e10af85ef25c5f25aa0f7799e8fe7ad4ef86e78e0e7a940efc707e3a5be16bc54eafbc00ff47e7c9fa7c2f7e2b9782df0f33ccfc763e17b61a53cb0057a9ee7837a386468fda466507929cfc50371782e2e3e303cccb4e0bd007adfb7fa58efb94cc08119ba71969bbe43109e161dca70aa031459ace84033060f72a835a0000e34c0e249342f702428be78a49a023f82628b061cd160243740b1a4c5a8f20862048a167e305457c0f901058e1d3f6a8d2a7cd04026870f9a178f87069e1178685b9ee0a147f0905684166e68523e1b2c52601b8a23c30c516ccc708146635ca02890e0514a50729a6a09a7a000dc53eeee32b8832fd400702e711e2f23661df5db9e1c6bf5478f0f9e1e3c767472709618b95c68e81cd65d4951c6466a26d58127b0004fa091950cd46301410897144f2a550c2b86170801429103a21003812a560478028db89cf0b2020e013cb9e800ee906a0166af625a42ad9ed54b0a088e1795099e5a549e6e2c11c2c547aa013e8899c1936acb932a4605163cc56c58079e5c70d4f85840543b54067062024c0a4e0c0122f16483c6064deae6b5a385e3b56508d50cad1ed64fca46eaa70581a7d4919c1dab97d5049e52302b15401b989c9407c680362f42aa19583aac5458bdac96626ec4dc70c1e172024b8794118658a9909a3284aaca130c4b608e8a30b000fac4d858bd402f75930a028d6250587dac1c5833ac22950935bc154c4a05c6a452a92fc54ab5522e2f2b8029160e2c126c18bdbc582b969762813cc01da0d74a213523848b0baa2a9e6a4c4088d70a9c6905da1e79008b97a5c5690f480971db7c92ee2e91130f98a1078c4234aaae0307e8801007b4b8873b74a55ac75f4ca3519b8badad29baf608ed62a06b8fd0e8bf8eed6ab627bfc49972c0021ca085b6aaa9be5d8bdbd4fc67ba5dcb44561a2671a027db6a0671c015fec9c4de9392b7e67e136d8125d44cf7c48df4d6a02da0729fc35b244165329d1d991099cecee954739624c98b6bd7e6f4ada61673e7d5b6cfe6c53531e33d569b6cdf1ff31bfd9848c812e3f467dc16afb87f59abf96f79365d0d48717751034e1a18daaf3540b5d0e255d4c28a1653eee9a45e5cd382965f8b9e9a5fd4fdd5c2259af573162a55dca944712aa6fb7e4da547fc31632aaeb2c4795f2c19a0e2eebb6a8afb7506a0e0dae70c98ee62595adc98812039bfed4971cc40cb3de3bf160359dc47cb84010ed4fc22beb27dad598ffce724205bf3cb340e4ef588ec87e99eb8201c6ed7e274dd5a86dbe60bd9f9f824511bf3d4d78a5aa6b76cfc24b29ad8cff8494c998cdafc7c3e35369d8694f5246d93252f910e5fa015d22cae78b8ef898510d18c8bb288a22d898bb2d83dd35116397cc45a578a77ce52ad635b8a35b5f9265b7e116d012dee16a86281271742f43e600b0c795873cf18354f6a810a68a9c094b28eb756c0e9d60a186d5d05647c07f99f6de4bd3597df4c727af26271c5fdb30d8b291e2ad1b078e2c37fb661616261c4b178b9a658710f6bc86fb2e514281e6e5dcb8bb1a65893ae29495352b8220c770f6b3e77ea18de7446ca64d4e68abab5f81bdf5d45525644fe6c6d8dbc75fa5b54d6fbe55f5b5ea28cc5ff7c9a148080939502d8dd7db88b023d1e3a3d79de6c2d57b525b7d5fdcda74967564485b6aaabeeceac80f2d06657eed18a28332b9cdc37bed40a23775d29196445cc277045eb7873b7f704a0ecca7d31de3675368121bf7d699b6c367fd7b28efac99ac337a88aa830b7e3ae2ab4a03c39e7d55553cb70ba0e95650daa02c7ab68854a34920a2c960a2aee4e92958a274efb264c054e4edce32f7da6c2354516a87c9abfebedcb277165fd8cd375c8c72729e39c1fdb2ed11431774d319372855e2452a854d23c694fe2d18726058a597533293329d4435d714fdad19e4812f022572f05729704b04b023cdc3d4c92312deb38648f156f2a01971450a1eeefbf6e7f8d3ea9c3652905ee964f338733ceefc3cd75490183fbe3765f44ef9ea9766ad9ee99647f9635c9ee8be47edb27f11801288fc0935089a6f7899fc46304704ed6f6d964c15d11085296962902383c54a2e9bf555334ebe3c4fd1a025ac4fd1a02563c54a241200a77a65c739f87c8924cbcb5ed8f74aebb92350ff1f1497a1207d576f588ac86dae8ec2cd5cfd5bb6fd591444565b581bc9649276ebb941fdb2e51c6d7e644f1da1c796d9137775bccd8be0f563fd368b71ef265edc442b6c6397157d1a9e672a393e854734e3527f44df6c683e794e5ad2fd1cff849749a7e72a3121f9f9d26998f4f924896180a27f7d0e96da7a6da922506423e6edc56a79596e4c7e4a9795802c590d2121e96d090099f4b1493a7a6332866307922d5c8fb48babfb2a41f1b12f364c2d80889713a270c549675a4122ad176cf24eed751a0844a345dc528b87b8a983c7594214c9e5a8b03a0ec3c7300ce2bf9db0128689bf10bd94e5f2493c9927e3216fa19f2e384818cf25f1d92dd17f1f149723de1e44f0479e286874ab4bfdd5a9ab2fc47393f999f14b2f5df2cb94f13a7ebd0be9fab29fb193f89ce4fd61fc2b64bb4c9ab84a3a3a4b3c30012e4dbb5bb6aea7f1b4f0f9d219cae38243e3e5bffaea58f4f6e74fa5c6e549271ce3d6759d611a7eb506e54425a716b3c9244a415650b704101555c010450008f0460f19056436d48229b188d3655dc4902a96702aa48409204b8879fb37b674c6d6afea6bfb76b6bcd41b1425d509c3cd4e47d283d5e8fa0f83b6102279a9ca0e659bf86b78d4dfe5d4b8a00331000055f9af84c75d3285911d072f70364f170dfd7b3031820aca1e54625e47d24da6f1b8ed1fc3aaaff850e707400d03dcc914906f8c2003f78583f1b40c900373cd4fad426de9aee9a43132c68e2c9c323414ab2cde930a564c53561c43dacd48687b89818828b8929357f33aec5e8b6e1d7eeccc584c9840a5d2ca15b42e75a22c7132b7ebf4631bed15d59ab359ccdfb4d375ad6f3d6233424df34712677fac4f96fb7923564fd7cbb96d658a8afadb9214f9a9e50770fb7b6396d67e1b694fc8c2fc6bdee4890127a32c1297103259ee88aa32e2592dccd9a37dd3fd6d27429e1ae24a84842497932b1fb6fe4e954b5b54dda924d49d4e0ee39b8ab0059dc3ddc96e26dc3745775145f5a4b6af365ade2c6faf99e54575d01a0e8b3e2b6c515c00c0bd013ea8a43428b7bdd75e3dce71f772121c577fea66b491a0d89a14dfe3de9e3928ec8b51c9fc48db7de7c0e3a828a87e43db18d923fd2c7251de1b4bb3547314e3ac228dc6d33f7f4b34e721d11730f09a0c5afcde5af691d4904a84200a7b0e64890126ac3648dee3a7e2d4680210ff3efd2da86dc65445448fefe6c7e59a3251911e5663f7f13bed5658411f770ff8f34e34dda6a7e92112d2aeeb6b336569369ac25ad549b1597df28d4f45a5a73bb769f3863f2d4da525d752e274b2ea698a458477735655963f4eaf0e392643f4e1848262bcb1a24d33b24aa2b4dfaf9c98d4ab6cd592322f9a4cece99fb6dc57f5b6e54229bed72b5a3b5fdfddad64fca6435b19fdca8c409030da0e52ae28aab8828453895bf8b18ca8d4e38dc55442c54a2b988d092e32e22a67ccde651d74e7d4f229edc9568175b7ddfd4a7167fccd8e622622854a2e9fd5a5731bf4d249f08193c54a291f7b58f0f8d07fa8fd4a39373ee7be25ccdcf34e252a69dea116a53d3a39373521ba2a0d8cff849f2e74e7d3f67b7aca6e7a4363d67ece79618d7446d65f96dfe9389287e6d5773e4eb22a6119740bbe6273f89eaa59261b5582eee3d66eeae7abd72bcf0824b8daa874ac5549a4c37b8bb0a869c1eaf1eeefe82e1e505090c50e9188153821120a20c99e0c3dcb163811577f7187ec822cadd5bac95ca25f5793eb87b0dee6242777956db93942cf15059d620d2e224f9cbb206dd6f1af2a4c5294ddca749c3c1f57400ff8b371eef496d849e905c4f323869cbbaab8d5e7ca9ee2fb5a19fc34866cdb4382de6f0d6a76edc3b279ccd9bd535912c2d4e64dabadab0cea499cb386c13b5a8b3a8372eb5aea3489e48b79ab39f8dbe5681ead69fb3494ea4677ac891e4887c974f5d3f272539d25bdbf2b7c5e5a093380e37abdd5dbfddf7c44064697141aead91fc1b2779a6baf5c561eb2ae4ee41eeb9e290888872763327c4cce5216cabb91ae9df67cdbae66ae4fb7e6e567335caf5c8ddf5c62592f68a4b528773901be54625eeee983c917cf0c1dddbb97b4e9eaec9dd3bbdadddbdccc4dd333dec3e3e2b7fbc35e8469e368cb7defc4d98487edbc545bb3b7b7397b473c2d9c8079395a9c446494884d05d35dab2858c21844058400dce51e00494123530e38a6b810016402db1445d0a2a4d88219e11e507e082ac2f1441c58b38820720282054dc29064f88210113d0bc6c620c166c6003385e8d2882d3f9423d0707883120f042891761e0052b2880060c70210cecb0e5388115af81082f9ab861450cfec3d5a4044a7c208aef041d1da5e06ae2a14c0b44a480dabca607258638a37e0e43cd0bb430d108719a5a03683ca080390b46b8c01764c8f0e42a9c9143074570798e63c78c264550200137a103608f190c40052fc1c90b4d180942c95f479440054d249181db08017d4201352a4f35623610c3043f513c86ca929d12a0ce7882c919a1596419fc25688ca72c5570c05d7ab4387aa206503cb5863200011e54e09e584c5620a288243af8ea8a920c29b648c257de1542f40122573da5e16106368280573080021397251cdc81092526104413415239986d19c00d864a09a0290a53872b9e5231300a2c5e8882fac2908114412710e0e94382840b984c5e501f51179985224488f2d5f8e1441143977a57b4d8c009657481e349a1828816505e583c2080b082024510c3c84b92c50710d0c309eaedc460880b4ac8428b77e389026e68379e78542e4091344ef0e433c039326e0568f12a41ec185e98028a67c1c20c11c02dc41c8a13d89dc40870bec40e34a4204636e44334c14303352660c54f235d1809020446dc8a30d84d718228dec31446635491c593e3e09200db0448166e845c22c30f4c7721516025945b20e13448f1b2829a26aefba8460420f7038fe740c5544338d1e236581881011381171e8b8185a817a0ace0395e37353c79c089539b9317441089c15998d9a902082568c05580810a100709a081a300448e260ef85101372105145460070174380968384006481fa880bf58424c452441e4368c989093b4012d2ec3011f8800070e311ea3c4062c8606428739e1081ab8392306ee1204a5a01b362082b77e50020d9070e2f3d64b0c3050ea0012ceeac932f3840a20f8ea07338e3481040f5c0506095c801101ebaa9f0aebc10b443898c58c01127003000efe989201287478c05326c880109680aa9ec2f50007811c74e129572823c51737f807b503f0023dfae71446168f06a27f3d4124512027c6bd2358008806549e70af045d6c192ce1718f01b02258400440b8977f761520c270cf089473478c08eec950a505174c10732fe6c6029b0f28b89fd1020caa1f10e07e45015350118203b883e0003bb4ec92b853c08b175c81048b7b0358a416167cdc0be06ab2016cc13d005ab42cf00410ee4c5820030fa480bb4d48aa898a8c3b0f32a87d67e0c0fd68003a80a1420eee36bc80cbf18026ee400960818e133fb80301c3dc32acdc7b7860888e0d5ee08e3364bd20018ffb0c221765e423b8c7108431649822e5fe827731ba0005ee2e30118111472080fb0a35d07e7c0ce09e026d479622b0e07e82093af092c2c5fd860f4c6831b8e04ec23709d1920277571154c0c00921b8d7b882c40bacd881bb4c921a1dba30dd618a5083062b31b8bf848006fa810bb8bb54e142123350e1debae207266460e1ce3ac14d800942e0cefa968405d081fbaa88192fa70d779514991620f1b9ab6e8481050740eee093033409238a3b9842173e1ae0857bea0a99d60952b8a790d840b90114eedf10608648e2c6fdab6202251c200bf7cf26c31f39c1fdd3a1c5160280e2ee99114518c4e7ee714026809905dc3d2588503406cbddbb19fddc12e0ee015569920615778f46084af40872f7c01f2a600207dc5d071e0e11057757010f54099670f72d5a004528c9dda78040154980ee0ee5731eb8c1dd9de0e055a5e6ee3e14010d2e9cdcfd02008503b8b8fb588314272cb9bb0e31025c51c4dd8d70c45c51c2dd89fc60d78475f720518021821eb8bb8f8b89273877e75193031626ee1ec2e0032307dc5d061c383890c0dd61b0c2801f9edc9d266a69053cb87b0b4d869678dc7d66c9e5450877c711c5882049b8fb092e34616ec0dd4b98411753c4dcfdf50222b2a0c0dd5d2e2f58000edcbd062e7763e6ee31676012a4c0dd61a230d1c3e5ee2f4cd048618abbbbd45498c9e2eead9c0d027004776731f1540606dc7d7582b2c708775fad401343c8dd5540e0d0438fbb83659c4466dc1dbc3350404440c1164df8304087bba778422203b8fb570327b090e2ee9f134f4ffcc0ddbf249831dcdd7972f658b15964c9b3de9bae3557f3e29cfd5aaddd9a6f4cb60da020eeee4c487777171331a5837954451837b6fcc0dd4129514c6154c206aaf87077af06267840f2c488066ce1eede113b70a126ab7eaa70770f04a920566003183ee0dcfd1bc00b371c6003184a6002774f01a1073d5460025c24a007eefe2949810aa6e09841184bb87b6a4a0c019266aed00191bbab68c8c9a270e2b3b9c2dd3f23436a20e3081d4708c1dd53262861a50c27a06ca10277f74c090000030f5062060e77f72c40819acb6c21050aeebe52c001a23c307c8e10c4ddbf3180b0c40f693861031fee0e2e80c64a00ce044b3370f7fd77ebdf373bf48e1df875358f1c0969b45d49259af6f1b1958f44e381bec6818636e45b4aa3d5fd39fab89d7150b875ed447aba2b8ef6190759fb341c1296650d0af7695e4b3f8769ff423889f66f2bcb3a86b4b2ac41b4b7d9d01bac3933fab789788be2bf0ddbae15ef97dbe9b4e6ac28d455c45bb489babf22de228dda70599becc984b46289b1298619db3e87713a6bce7ebcb828c45b0c6924d18c76f11dab356748629871a51b9f4ca8b6e64cc86e9669373bf67d5b8e346bd65dfe263c6a4bd29d7e1b3aed9ade6622e1aeb4d930792bee5a32633c9678c41b67f335b6d55d5973789f955e1b5d446db8fae8fd9ade6c98964fe29c3ec3d7373bbbf1edafcd3567b3495a5dfb1b596f74b77f5f4b6434a3a47d223b31ccb9d196dacad7db52a0300ca98dce0c40d4266706a0d8be95bc3fea2729c5dbe66da90dd07d5c0e34245f6b1c79928f1bb56975766624a468764424cc6fcbd8f6e79f3724917fbbd5249af8f9c98ce99735ab3536e9ee6bfa4b9396189bb98c6d35baab395a96ba6aab6934adffee116ffa4c35a74b5bf793f4e65ded9df6d678c828d4bbfc27139ab12d69dfafe567aa4f7c71c68d77a7abd611d65c4d1b164591469f24ce26c54228cdedda5bdd9a56aaad0de7ce9cbd368771ba70f74c2553fe1cada415f5df33ecf177ebad7159c58cf5f84e7f312dcbfad914456d6b32c3fd3ae35d4d28899128da9acc126393d27bbbf6e6b34985921851bd735b639b0ea55b631cadd96afae5aebce4db2cb5c13651bc38d4e43795587fa6e4c64d63ad519bdfa7a92d49dfac7944dad544bdad68e6324e0cb12e6b0edf201a6afd99242bd5e5df5b71a30deb7d45263932fcf34f263457ddd561925ebbad69c7cf5accfbe6d7db86ab2886afad2e3f6e631aed7318e94dfabb9296bc75a4d5b43f7e162fb6b4d9671c4422a131ed6afb85c48c6d62480bba5d7b6f168916bb287fedaa7848afdd39d905710fb5cb5f3b1c4e3a70024198b1cd673cd24ae94ce76b3fe37157271c1d16d74d2971d24b0745a7c4dd431d4e531996d2260334d9b82b2db9d8f2b08486e49f669c5bd07036a4e56109cd76ee70aab05bb0a15c1dbe4cb89b3d6bf9cd53d34dbb38ec8bb5160de5617e3bdab2e668d54fa1d6b8480fb910dd3ab17898a1f8c599c93da90d4ed7a1d8cf9f4c5ec8f849b28bc995ad9938b94c94d86c98898bbe501223f3cac64d2614770f4d236a33b6bd4c9793d80a7e52f19273e7b6c62da7dff7c7cfe2d638a7cf7b8acf84cd28d7f47a7bdc6fcb8a489235072557864a34f1f7df6a7e3223d9164826d3670db23519ed2aee2aee2a927fcf6cca8a7ef4e7fe2867f70f5e06f110e3235a6dd3ac0469c83481274e537a1faca636363cd042f2df44c25463c2e14e5322ad68b3abf1400b773b5773571bf56b498beb25289c7554dba527e412cd7d0927c4a1da2e799285b24f3686d3e2e10bf7c4dd71381ede4b77b5a0db15cf46478e86e8adf4f6a4e486e3a1b6b6a8c76d9b140fa9cdba7ba849224b7e9587e25ebf1c9f86132ad1ee9bf83d54a291564828aef58f24ce4325dab6d79247c6a890fca6fa1aa5f8683a39f6b81e5d3530f2635a7715a26b4feed78c3cd4fdad51aff55571b5a7d2aad09ed094b8d3622e6a0995682215f1491cf2dd787f6bab7535772fb1872baf1ea2844ab41ecc1e7094b2b8bf94a428392929f912a85089b6c4892f315a42b5f000e5a112ed89f340e30167872828dfc1c90e467edfc43be0c0e23a4cd1c1e9a5431071bf7ee9e00a95684aaab82b49c0dee27e5deeed5473e5dea2adc9041aef4ecf709bb43a11b7717e1ff4e3c6cf39ac7518a7457caf0e9325c623169d6a8eac8fab4c1897e2ed5afd3e58fd198fb56a736d12ff6d22796b4e6b118bb6dc84b7689e4914c039c987a41c18a7a3d15dcde6df4669a6dd341ba636b47f1b6df66f13df26fedb44d36ed90bbd4d1472d3b95dc57d5fcb7a7c7cc8ad4553231d45b9d2121b8cd3f1b08486d9525d71afa3291e9219db6cb5261d3d31d5dc5f7c64e4ae752d6f88da9556d7bfc18a1b947878d26c48fdb8bd77d5157703ce0d2e6c6474c5bddcb8c9c8c93dd4e16b3414deae110e47228393bc78acd844a2c41d090e1297874ab4571116772f82c24d4550eea97f57f5aedeb26e6bd651466d6a904cffc6497ee3243625368ac984e88b654c233652b7aed55dbdbf71925d3da2041f71b97b686b46b46821a8b29c198152564d8de0fc766d90911e0f33d67d0e465a78cf5e45aa58f22bc5bbc8114582e8dd33d1cf61b2de8acb2238f6ef6c88720f5f365c711b9edc3d5f9bfdbb970d384ea33b4462ec94445c109961dd95d888863b920867084bb6fbf3d0d3eb9bad66ae8e43b4d7b61ee9be8846af1db22102e5eed5081127770fb52532e44ec415bede2fa12bee4e858e92d0c7ed22212a7eda849cb8bb108ebd65ac752409b53cc4b9cfbd8654d1f80e6142f6f9dc15e73e37c408e73e2792641d12fb212d0f956858a0c81702458813133685e4102ad1c87b0ac982b6ae466a365bf2b5add9ac63d0134146dc431a92b616240344c543f25a4a968fa37ab4ba0df4837ba8236ded05640372e56839d0ccd758a7a38f4b9a51a129f140a3f96f179794246d4de835abcd5c1e925f9a6726ebc9843ede543f699bc80f72c53d2ccb199dbd8240710f5f41704186dc83d01aa2dc6bb8524307dc3da477e352d76054434cd4fdada15593594942220449064566ba6c4826030d57dcab99b344e8ae9a3a3e3f2e898628428868c8cd643531912cf16713a96a721c227eee1c2223af2df213c5fde7c943255a55f2135e9b1349206284b76baf92c8030d0802800c79f944b987485a47cdd53471cdb70a859a56ddcca7caf54180bfae6f24fb0409956864aeb876b198ff76b379fbb8dcbd2cabf8238b157f40b907fd781242441f97f4e347cbc39a1825eb67dceec1e2eede0315d26ad403c5ddebe360fcd5e3430fddd5529f179bf6d61c2e8bf8cb8795abc3a40f06f8c09b9a55a7730dcdaad3f4cb7137becd967407ddb8d495478bbf266fcd3d797379ac78f8241e79a4f06822dddf27f198abc9d3727792ac3daef498d2e38959753d708e4bf17bf4bc7ae0e06146fe5bbd35272b31aefdb57889fc5d370f283c682f1e74070b6db66380d70e939d15dc43ddeb50a19383ce9743829c04e40ce5cc7078f0c27902c7c9c3cf66ee4f4d146e4babbd5dbbcbfaa7d63735345a8c925fbb79e1e0e0f01d68cc5e3bb4d831ee9809d1f030c762bb6b7e5dab3638d06a697316c96a4bd2b27d85458434f7b086d694658dd15daefb9e256e2aeb2cecf1b035bbc18087341ac59bd2688931ade6a7d19b21f25e5c74432d5e32b18d96106f7c6da0be66f3a4c886e64eadcd8f87336c99610ab5c17b776b89b1f99a8166337dcd80c3dd5f3290b62646673248e0258391bb87369a31c59b2ad17c7c683cd06b6732b4428a77cd150f31ae89e2e1aed4fa4bf27546af1aa3d0de7049751d2f119dbd6a78b8bb117fc5b2843adb1a8be2545f3127d727bd3616f39c2df3697be9d0e21e9e4c9eace34b47938e1e2f1d5f0c54c26bb10dd71ea11da1e1edd2c88b6b7809efcfc5f044bb6fc4a91ef1f1a1f1f08a01c7b7ae653e75d10b862c7bac4e0f0403153f77909696181bc1b084a9e6f2d7d4170c405e39b2fcc5b8fcdaaee66898038a53cd51ada3c43887510edd5fb1ac355cdec451f757f757a6fbab4b4c5e0c54f376fa6bf192786daec4d88441b9bf60513c54a2c14caf39f1051b7ac170b8bb4893c59d06ea4513c5dde2251ad3dd69863c54a2bd687078a8f4a258442a2275628afb69cd43259a1665245189b1517e1f9f249953cdc9763d0ada358b5fee8d336ecb6a623259d20f0ed44696f4935f74aa39f15a594d7f898fbec459cb927ef2b300e5ce42149c3b0b41dc672cb4aea52b507177aa4768fab5822edcdde8feee482b92565c2196b335ea3553e535e3805089b623c9fa1b37c95e6c3e1b511bf2f5296a23375f2a68ab823b4d218a3e639496312d0523f7508fb629bfdef8e6570aa0df5e38aebc700ca1c43f97f5dcf79bcad7b9c76dfcfbd65ccf1bddf8e2c613e9666ce67e536d71e4f8f185e3e585c2c6979a1585261ebeb61485174ec8e21eee9b5dc36dc95b5f2770e0568dde078be4e393f4b76afead9a2728799db0e332d98fce27aee16ef6af48debf55a3ad6b29a336ef834532eb0ffde8ece080f725a2363da7591ff73e58245df413a4f7b5356f7df12833fa193f8953cdbd0f56d7e0741d2a8ac98e72e715cddc293ad59c282bfa193f0929abd927126a232bfa3162e6f21091993bc56b7340331cb6ad1d0919ca2f931d0919fa01fa193f09d3586b1907c98aa8cdd08f98b14dd455c4a3ace86797f71da26bae55ef13977b376113e32cd6d75a8b2253ae3a1ccc530f8965dd17cb9ec441babf75572164fd21b23e4ef65223aba136d446d4fb9190481c94442489c6266c8aa425ebe332deb52c71503e75cdbf1baf4dc4a279e24ce4cb9a354f9c6fbb8a385d87649f71d089c58b45f3940dfd64ac716ada888f8f8fcff84ddbd6a84dcf6981867eb6191424bbf8f79eedb6fd193fc96edb1bfc5543898daef977e5ab46cb3dd4a5c5e9e8cdd6d5466fd7daa02e253592974c14f7f02523e41563860e7fc554f18bcd9386b87157f27eae396aa369483492a675b82cb18dea5dd5bd629ed036beaf1825af981a3058dcc3fab917cc130fb57dc104a93f44afbde70b4626246bfe172ca112edf532e5f55284fb93ef8345aa91c9927e6ed73ad523e4cb92509b22e2c625d2c6a596511b9288dafcc64990643f41fb7e4e964412ed4722ad115971b9aba6ac06370d91d9ea1bf9193fc993b8e867fc24d87689b6c646b86d7378b5a4b887af16cd29ee6671af56ab159a486e701105f26299c02b2eef97a2d5892f96d38bd5c372bd566290efe393a4c377558167d2358949d72d7bad68af55cbc3b23c6de4fd9beeef67aa82c2b55bc7970ae7faa51af21f6ff84b15235f20969cdddbfe1148c5c1a76a1279814174ddf8e6627d82383c24aded73b58849d7242aafd493570af74af588e42b7582874ab4ddb63299918fcfe7aa7d7de6eb1b7a7d36fef2b2501ba6943e2ec9c727e9e54df1a497f78471ba97470bf7cb6bd5ec6a2fb7425f1ee5e546ee2f8fb9d7d9cb7dcb94708b93708b120fb7d02364f1f075c599478072f790e9084f47303a020e235871cf9da4119c84f67746a0b95f6c049a3bc934b2501d6940699bc693ffc5370d2569c4c2671a6b2e42d417218afe7cc3d9a4d7d24a6db8160157041e398caf0ca7eb104e575c59d69c48045a7edddbc4bf9808389e3bff469e77085157863065084f3e04a350892692f7b47d1e028e330d01a62696cbb5acfa73368969ac7997650d92c9aedd9565dd329bd98bcdae2c6b12b509a236d40609d3587352eebc5d4bdaf149d1a9e6aecdd9f1c97d3fb7c77a5fbc36776d6edfcfe123e2e76a7e36f3d674b4bb6ead2386dbb5b94cca64493f5f9a6752109e8434928816841e5bda6c185f0c84a81cc67706842be1c54540b800258764a639a3ba529208083beef56b3bd7c7dd2f458ddbe74cd4fd4503aad6e85f7c040d21704023481dab6da62d89cbbf71375bfe20ea075642259a0d8bfbe26c13af15f769fe60caadd63e377d36f39be629de6acdc7476fa7bfa5ad9bac8fab41a236496262fe265c9698247fd7cf3e802a4b7cad0fa0f800e7a10f5ca5ddf7a4f99b30d565893310a433b2787806957cd6f289a90d9ff1343b03577406f5d7e15ba91634b47ca1e529dcf7cdc76d1d2ea9b65a9478e8f4e4252a2b69452d38ccc8e20e152ad14433a298e1142ad1cc30720fcd885d33dcbd0c28f75cc6b6fca60f65e0ca5012de722da3e5feb9f6600ad575ebdb83c777f7a0c7bd07ae5089b6f1bd270fb0f0c002f7d4d9a9b95d7bbb9607d4fdbcede02ffe1cd63b98e2e10e9e76d083ef0edc756fd381955007523ccc98eae840498d6e1d97ea80963607583cac4772f064476d70510e681ed29acf3817a3bb5b7340ab2e8b95da2e0b942c41bceaf2937786031db8cbb67e9bf11671309423e280de408bff0d40e0bafe7cce26ddc7e96433a77a24e31be0d4bc3fdf405732b0e419194f9c8c21df6d3270b87b38c695319eb8ebb2ac3b8f61e43e1ba3860daaecb6ad399b77353f531bf2673690e2eeb8c8064e5c36303d24ff856c30a45d36c0d96d3337a40659dc77b7527da406556a20c53dacc19310a21ae0c21a04a135a035700fb5ae9dfae52e1a60d9318d7623d120cab65992d2c0bc3a7c533b916810730ff3398e98062cf7daa965b7cf3232563bf5bf4d74aa39a3fb194bcc3596195c99a27f064e2ccdddb73d99cca047dcaf67e0f2da29832c1e2ad172fd72a402c526039324ab0c82883258c16ba71649b2ce9cea116cbb56a665d4462f695b696bee6536d1bc991bdb4cd65e57d6db9764f95688dad4d45d85509bbaab9058dd55c816438b7b98abe3d74831a0c488a2fb22319ecaf2472b4690fc3a204b62e070874116baab516d61b085d66104032618189d78476d76358a936630c0f1f008067eb335fa822be10ba4d48c695935368b5e5056d70b70d0abc3e4be27d63a76bc7541155aba40cadb5c5084bb93567c5246446d5c80e38256ae3a2c4d389b66bd42c5b7f9e358af3cc15de9c9d6747a71777f3bfd1597bfc6e5d88231c4ddbe27067a5b7e8d4d9c4d7157f14f52cc78d77c8efffacce16cc3bb05389c0569380b2cc0029abbe3cd82180b5a619630a6b88761863561d0ad698c86e12109c61530a884e4eb2a6e17711718344b7e1e5d60e0086b32b64d15f7356d73596a2b80e261cd0a701eee6b7354d71c5e410dde314aaac00c1544d1b8a4fbb4113284ea70f935bacf9a0a9c76353a5301ad3d496d68b4b26e9cf48596b2b44c3abae30b28f79026f4c5d3ec8b2054e7ad3fd3ddb6a43d99d8f28bd637e5ff5cbd56a0ee5b7982bb591cc5785bc1b9877fdeb7d273f3d904b2d2a269b2c443e46bb2c4b822b2c4ff19c8c72729055452e0c4c533054a5210732fab68fe33a1206ad7f1b3f9a4a851602554a2a1208a5d9f1445928869c44648ab75d6b75adafeb3ed043c38019418bd27ddb62f1f9764e909701e2a2de16109bdb313e09879818517d7d2fc391a44c3bf98b4619a76aa476854575c92cb0b1c5e7526c8620229b4ea6639d87689744c80638256a844d325b82296608a6ffd4d2520c2bd29bf49deae2d01908f4fd28f1306aa65594712946515a790c049937d52d495e64e82202448c171dbeac83fb148da6a3a3de9f46425712edbc4f7c1ea2aa6bbae42436ddad16edd4506ba20bbc0f1f099eaded9ecca5addb88856dbf44a4b6c785842bb68f908b0d4cfef83d523785213a323a0b98737fab9ea18818c8b280fc937c9f2c463cd7f675c40dd6b69cd8f8b28174e98b8182282dc496a4bc98bc7d73719dbfd9ad62c821188c0741a8de276c63a4ad315278220ee8e13c10d4de25c08ca0801145af3db7cb66fda643c0b01ce3dd4f67154575c08703c2473d5659c04022baef39bf4fe787e7e0d8228198f9f29089e4090e40281cc07a8b87fc089bb7bc639fa015ad10782b88764aeba9adff58196bb3b75414d710f7518f739acbf56d60b6586b46b8f7c49773d42431d2e29c64150d46b7e1aabffa864308a26c29e754ca11901180000000033110030302c188f886432c96c4a7a553714800377c06288569d49a3288721849c31c02002000000000000002000d692dd89a2067f7b6868408e3cef9c792264d6a9893ecb9337f547eb263b8f97f54c86315561b9af52f180927b3928bb8ed6b64fc379229d0329e7ef4f1d0bd5123c5316ccd7a42fa5fe962db51f80a6364c218bef71d9802ca7ac1b03567ee934a32814b2985c97aac948a27a9fa1407cf43097be70896858a65dacccab0417aad0049f2c5032fe0865e686883715965e84b5fdcae6a94a193bdc42205a996bb002b5b2f1d2285723d172634ff5c379bbefdff2fc4396cb92b4d533f16b0a9e3cc01e14d7801312ae61756c3b234ae4f231dd9b38795e28ac6d6c4c1c4800fbf44b3ac03664957aa7100bd196d044e9494ad1a70909ee631401b06a2b5e18a463501a1781b35319b560ddeb94ee37d49dbe0c64f8f6a2a5e285a446ff43a164abeaa0ddc014b095694f76b5ce674bd987dae255e07ca50f7d361219dabc1322cc951c5435786746b9c1903d70bca4bd3de12919f4259031458abf4bb8d5d9ab27b4898a5e2df74190481f739570342e47ccfff9c25c4012d27e95939597204807e26ef580c429348e7251fdd0139bbf9def2210e8787b7768bb077004baeb212a0406bdefbdef162f660deea2f8f91f2d8da70f765e52cc5832a4756b3684bfc646930a113510e8eef12c50399c5561e11a72c361b48a5b8c5dc704e1d5e4c8ea0d2a180ce46c350434917d4eec13cecc98adb182d8005e12bf46c4cdd21ce40d37179071eb607c25857d15b3337e16155a860716f6de7743fb0e3b70320801387d5d96267d9000fdccab6b97a8bbeb8c3c5349f06c34ade9d55e478f81cf4c953b5934f568925651b39fbc1189ce152e6ee806b22c5e7228d32053171ce65b73a9a6d7123cfccf7213cbfc25588d19e4e9b461c4324d9a187277e73bf3e7c4992988101f2b10a078bf6931c8addb5a967977d9b5cd8da716e2cd1e9dcb72e9b7c27dd17d1d6ba01123c2874bd7b7880e82867db26d3e06c5c781dd9ca7658f5570345ecb37fc5a30334450e6605eb604ca95389613868a9db08d3ea8ad11be6ba665ee423b54a8f15d6141eb13e674f13062d694e73b333cda5c3675f4116ae51d0088ffc0c0d573538947e23c1a9f468da6754b3d537c844458a7774236bd371a690e1e207ba46bd6d36d6db941695cdc27f32faa4fc41d2a2407a00da6839f2a3266dfc4e9b8cdb2363a7f9051c65dc0ab40946a59774037094f5586975b7c00fca6dece8d16d04ae44c45483c7aafb3f439c2b3284b644d3e9ec26763e9731996de31b4dbd32d991aa9eaa80f8f2beddffe196a3b23b017d4448882024b0cd9a0cd46dd4fddb96292ce6f711ed2262f5289a3505557ff459f37ed732795ef85dae12c369b7300314bce1d16332b1298136fdab84903ef32f5ce1f95a2e217afc9741b96e17476853659e391c1f7860758f5c7f2b7478c1dfcf2b941d9e350476aa7fe7bad4f8309219c304495c98eeca0f59cec3a247b242b173945deb09e7cb36fa18386962351fc38dd2c9603252194de5c5d53dc0b9618c51af3f8d84e28deedc1e1ec0ed5bff7479e2dd16308e459f9f9231d684756250f0b6598ca40a318f27783ce9c19dfe45eb6b1a66323a9ed862650c7874bc3942e1c7cf22d91df652d0f529025578b4ea18a5f6877e226cc74b627dba170cf7ca4e5affe79b45a9fa38d35819d6906792f99eb1c4bad48d8e15664619dbdd46782aec32a17ac2d0ed4379ceae582bffd104a0c16e4fbae119c9706f852bde270bc1f530300aeef99bab4127a2e48124c6f9cd6df15ecbd751562aa42861b6f15b5e4ddf3c043061cfcfe2d961558b497d965eb88d77ae3de2898e0e1bc43a8955f41478aac0b73b34d04c0ad7adea8b94febfa0dcc3bf528f9d15c8ce45bb309aef887b25cd9bfc4b0f812c30c352881eb7a3a97a424031c85b5a65f162c1fa24f3917568c2d31a77f6e9ed8f333a84626584156ce6eb3a87a3ce448db525c8072669dd6b05a3a2451eeba631781cc2cc221d8d93eec7236bdcacfc99790598db92681842e4e81c9e0b8be7d75af87964f38606d640832ffa6bcf08e164962e524d60df90587e0248663bc1968e11ac416acc60ceee93536e010f638d28cd350999fc4d30a5eb05695fb7c1468b104e680d2713c00a338df5506a4a30012f38e463fc80a8c406f3dd2d73cbe528951181c316c0f79a79f5b96a7dacdfb96e9334b9db791e42618cbfd378d0f7891d46c6c0c86159163b5c94ddce1bc71634fde9c1f13b09b6fb40043071ff639d4957e714efdc8226790f68afa6c176b39a40529ed207b66d4f81c71518a4b6ca03cfdd2b1dbf8ec998581e0469abe2eacc3469de065e0cd865953059a6e24014ce1caf569ffdb333b2868abf01fbb42e2e0e512c409aead7d76d906d0280e151dd0b43f3ad491ec7903087095a3c7f1f175983020bd029e912a7f2bbb27803a37535ed42e9cad87410f0b115b864630ae285c5c7924d048c7110e0c6ea9af3ca215edf2ccb7c3995a0d3de5992671e4719eb3d9c1819eb16e9ebd8fa91f813845c532f8f0f04e76b31fb2ee172bce5c3f637f32bc75afd2a42797610a5be64427be1dd8d08a16342cdad3799aa2094a52f07a2fb0ae6a5b9593451ba1c4f84206b91af30bed3d8fedd2ad3bc2ac36655b539b07841801777e295f8b6a748744b08239b056a0e9976d4057b36385efb800317c07f726e3856156d5bb43620abf7b8f0e4a73f3a8a0edf4980ba59cb1473cd846011734061cb871cd393264deabdd6b4019d8b9bde92a56c08ded488d180e2bd1633e18ad28fdb400888bc0daf858edddd488d98100914dbb3f92f8510904172cc9731750c9d494add9b5ab7b66bae94c2ad31f9203d0d333cc64f70a9b1e244e1f347052e73406ab32298a26accd9a8b8c3250c6293e6e84cd6f24fc938a5e03503076185c4840b651bb9ae8b623100ffa6c170e818fccba81fc2f07b822e0a3383aa802231612ef81e0542ca13b2edcfd502279a07682ba47d5cf8617ff750f7e718041eeb9a6254534cdaf75ef291d72e7b8dd0fbdf57676f3734c400d159c385541fbc36bbd54572876b727fad6db4501cea8d5cbe0cecd41e6c28f3f4b19a5069832c0ba05d85f1c64d940f0785a3b294e4566ca81d66e9ae38e27e49edae11c4849fec4709cc064c8ff54d931414403b7d7ec72ebdfcee3ec27c360f091bb0e73dcc7a598c05812631a26c8d84af20a8ff0756a6e4ced63785e79c3169aa533c108f78555279cc61d7612276f8e886418efb8d951992c02aa0e131ecbb524ad68609d25663680df767aea52523482d78ef6f2da448bc51204f46c61b7c120a4c9f1e305a61c73044febc879f31ce44d8d1a173d2691ce5c6ef093aac95245405e5d63f6e5ce8c5884a75313826d3b824cdf636d496f457f4ec94e4922068f0c282209532d0b23a696f4c1c4b9fb8b1815f732ea36bdd06c1c374321214922d31233a500c9b46235f97f40f413188c717609a7e33308f296782b3a9fcaa10f27a01e7e8ae498ad90a81522d06de26c91a800c3099a615ccfe986fc466e232c15b9fb97dcbd6ffda675fadc5f21724bf1c90410aff2bfcf6d765a0475326c5ac1c599a52c79ecae4ae2595dd1aa454b56b4f332d81ad75be1bd4f651064a0ad015244d134cb321832b7ea7b48a89130aaef2e0a3765b517e05f29804f028d192519677b71c2a65ae8352cc71a311e91aaf5b480dd6c620bcb96a0582c95cf9b845331e4820ac7db96f526525f3863d4c65edec4255d6e091a039268caf0f38fcf88f4998aeb39f8d5321cabbd7060bfcc8f1580336c5cb6e8fffcfd00c5c0feea9824252e2ab5ab793c1cec4ba206e962cccefe55d55b700e5be40d0617f52cefa172d2a25474d0480a9b44036a34e1966f9ca779b374451c3ad4e05010704242a017b33e60f9ef88c277b9ccddf0c6d07adbf319c9b428c72fe21406d19b95c5ccff68aaf8e9cfc066245943bc05302e53543fcb7765fac3c5a684f29b1b3d7ea13404b7d905dcf85c126eb1b63176dc3f1ebf4d01774a9c0b95049163346d172b77bbbcbdce79338885ab3751e8a2bbd98aa40c435d2ff3e0f7b8333faecffffd5cd509d6fd852e63354d234d24a54f0d16ab89342be0bde77a0612e735c52f62ac18c1b557a850ddf85b689a682cede2c646ff4c57e2911a800e3db01a30ee5c4b20e876aafa71489c7c18e2049ab20cfd82522c210a160416fc426c50983101753bf39abf65684e1fd1da6e546652c312eeae89f6eab8d1722d4d4c5516877f522024e79a38022844bc16844fd28238a8114b11f1b6478e5d82d224ecde4ab18abdcb1584aad43f6bf0877e6ce77717655fba907edff2a6adf1672871b2833aa4f5786e468ec7fcf87f77f5a5cd0bca068ec62b7f1e50d13eb16303f1fc9ad7c1ec73439a41248a2424d0af2735298335c619b9c63b89dfa247ca3a42e8d04334cb0239258df2fe310c6a5578c4793df17447986cd41ba1a012db8edaf09f0a74e622ea01d893ff3a67249c9e0bac52b734eea15c422a8fa84004fc3bf91dd3dd004334d4f61e700a19ee9934803449b5272c8594c16a4a82fee224c2f94b09cb7a59309e29bc97d613e0fbe8876fe0b449b6e98fd207db11278c2bcac673ad076e4dcf9b4690a0318ab7c885664fc355277360d80107ac9dd00166da455c52883fe224de2c1e58639b5c72b45502c91ac2b8c5a0e37b5fb7b918f58037ead859dd27937fff82daf8fc280c6ef60eec708f3d16607292b6b566015f62c6b9347d71ce205cedcb41eeff61fbbfbd43f457ebf209761ebc1e00dd024a6a37065daf3e06aa5514d470a2b2dbcb363f4eb37b5e351595b7612f043b9f72af906cd725e5884fb73f19c201e625df7468c089242efe7e21ad677e56df6e7385ee0224e2841034398f46f58647cd33e1376a2f3dd665d6f3c20056c8c80a191bd015d21452e4e8c7c2a43decbe279af5c3d321b5b15112aa1b49882f1bb6eb604303ee11cb1c5ba5e45322d89e3b6d463028915bdf4b0155f47d127483af7238abf5ccc77ea30da2bcb5bc0688b4a028465dac7209ef3f59b18ee676930680818db0d00e9bc1f2ef747efa46fb74dc0942ac96f37993b345d1cf0ce981713f4cc0882c313139f32ba8aa0b8c79296d210d3d6b3c4f6b6e65fff1fbe9f50e1f573b4ea1d907377c9b6c87666aebdb9f0e34921dd45b509fd5224243a4ee17a6b500a69dd2b42417d2abd954754c3abb6d24668f99cfbe4ed879265bf8db384bb25bf52c9e010e7714029b9c8abb735130592a33191e8e6f739421113b8f1347d2d64fe558ef084738c390622e8fcee36875436742264c52d6749f0239dbff42c936ab380a136032f94305bf9e5b828231aa82a4ad801e0a56c1837fb3b761168e84db526247b7146e43f3bc39bcb4d956b26e78bbf770d22d8460019579af65d3432eb21e84b2ce4af85ba5ed1d4e3fdce89b7a1495c09665f5f1d8b5fc5e414ded8ddaf5d17ede24c172c633cab581d69613b550bceae86a243b65a2aa2bb5145fa1f2075d23676d84989c0ea7192d28d30e5cfb611addb1dfdcfc38247df832f02a615bffd7e9090054979df5544103d7c009eb2f0c97d36f22afb67f48a3d31b9b19372dbe81791fe6bb51e71ca9a2cf50eb859f8eb5d526fbbf8274c18db9648687b676840916c314cf498609f55db69d86cb07bd428071bd648007a187d04e2fe65c255feffe7723b17fddf02b78087df57864f36d17064976402f61c3cfbb200dc7d504f6087bd5f8742a673a54a7bedb1064543569159491cb8e66cacdadae10a4fe48aa854d3e2a3022ed38b420e42e96bafa616399106c6b0fc1ac83894b45ddb6d88d49a3f576472bc7ae1e887b0b09045ec44c14b4834fd6b865a47a64f71a853c84d1e131414a941ba5ac509cb556df5ee452a46895626506979672a023a8b3e8f718590152a7654274eeb772d68f512d7eff1a80a91399436d0d41e7d94ce31dba3131b9062a13dbf2e5be6987900e99ddde5bafa122aea35c210b77679c876c74cd111675762a84be7631a4fd9661eb0c21a4a0301cdec30275d2929116a2ffa19f50cc1e65610ea94b55114aa973255216f7b83bb7edebf092963a700b832493bb8620257cd16150b4495c90484e6d8536865f2bf3a7c06bf3ff79bf067df0c1e9e6a70bb1301a99a92bfaf9bb8bcea2b20145220cc0afc0f10cce828396e89019cf57071638856a966c84a40e9bbd741a4502b4e88acbffbf39c8e3e3d6c2458016c678fa588aeca103d20719cf82e358890b00c2654358361a021f224c680ecd596cf725e973af931e74db1e8eb73b799c3fcb91faa685a201c6a5336404d71b9afd72d2b74d5be80d6a0c52c00bce4b6d52b412deff49da6fe558563fd9a653b3f443465856d2e7a8721d6745d777ce6f2d6958af6fe91cbd489580905a639d20094916c0f171d72136874e4c3c2244c77bd5ac10cf9dc20ea6f28f818af27451907f0bf02320830ac0ea99aa58da056b01af064940a38f32507f03af3ffd773f84e270deb07a7bc0f22effd3e14fbaf7445abc51191556dae1aa1582a3897b75430bf7df88f98a5577f9cea29117be6a1d4d578768a5e0358c661d52f3b2319024546145937467224d2fbd9a6ab75764560245d533c77374316f89bc7ac32afe312e3bc0e102f7e74db087e70b7ed29110aa0e6f1b3e31ecf8699769baa465aa496ac453828b7912e36769c9ba06fe99e12f2c17990bf55ea20dd6e225aca9eb106b9300c8180461e1168cf025c28dfbe6c5d13d200b4e2aa4b81191c4a801b5783a5a46292bded8b21ede724351570094458e03dee6c9c9bb75539b86d22acd600fcd075ca71b0b6458457a8555b6413cfb2403853e81307577ab586e49842ade485779c6853fed15cc1da2a61d2e5dd2834379efde389ecc9cfd99738893b28bae06105ea9b72d9d59cc5898b91e93cfab75c208897245c7c4ddcfe864b72d39de6a96f082e2177d50f5bedec4a22d44f58517619aa814ea747434893c8a757f645ac97fe79de2085bcc9d0d9aa6fa407072c9a3cea7ca7f863792b42dc5354a7120168c5085d6069b9a1b796e59a76e1b54123654b0291f90d64ed25eafafe81673d35a682a077003904d9e5aa194d3e24649c3990ab32b176dc58381e44505ce42ee792bfddb4be6c093fd42321fd83529d9ad1b37843a7392c3029a4a07f004cf179e7b7b287cd477a5da187c20d971679efa0dd2d02a888146fca9036134eafd9236cb5ff7407e3ef1cecb60a014d5efa4a68fa0b271103d584f258a0b7a8e31f8a5d692d7f91b165a505efd59c2b63e9efb4e46e2b33426c70594b9ed7309d676c96b9ec4ce82113fda531e46852e25343d65342fecfeacace8630935b4a520f340304ad55d54d0a11915563a8705d265f404c5b399ae313be65d41de0e074a1876c7be57cac69139c0cf42855097306fc1e930467cc540ca0ce60b0bcc47d172cc3a48cb72335713399636bf6f7191f53d3bf77c2644ad2da57838256b2f94f609201e1675d1a28550421942e9e7d1bb37646b3cab793b4f10c9342105312ac5f41551b0085c4b460664139b5820041a64502fb14f75a4680e517adfa42ff7ead1de5e4754407c42cc239fbba19f41cd6e7d3ce50235f084d70e0930c03e6fbe4769637cc0996ddc9142a7be9b9077a758057a1bd9fd15ea85598a6306d574e851a003eb124d01f227ef13c6d67392d0b5c14903ee672ead8d6ca558ff0c4e78024c888763afc1f731d74194b7933f06601b91c728801837952e683bb15327e9815b74420a84edf96736c59d47fe5953b82850fea506205627d2a9862e2b9ca52d602893c6c66ab85843ac046f2835bfcf384b15309e1eadd7e40c54db38ebde76736d7a34f4cae109fd1571c488d26c1bc20ff47563241488b9700cf227db5c10585b35c76c3c1f445eeb5c9121aa8ac0bbdcd95f35a0ce8b4f353a92cdc680128de035b614b43a3b2ff2b83a4841e7bf63db986ce1a6d8a664432f238545b160c0b2442fec0a8ab389915c3ecf0606afd76f105300fc505cc00d533634a581e6803154a89b9c1b6ed49be65335b7021b4607fcbd0ee135394600f3ec68cdf530ec3f1f914c74ca5d02770bab283e297583e629040790b7a72b8ad2e5dd3cada93ed5e878baeb05215770fbccb2f250456d8c8a6cd3e54c135f5e16ce08a7407526dacc992fd8ba0b5b7d1a6ead82fadfbfb453bbaac76a14b5c5062436b6a66768589dfa7de18a75bf74f2e672828e0fe166d1a148ceae954063ef922a0c586dfe00da81a82d060437bdf7c1731a8644e552c4b3c7688e13ea891840b7f7b1171cda475d4a6291f4383ac25da4bab0802dab6a8e50ce03d3d91b280a65c6eb141c1cd32e4d625122697914e9e90665f66eb960274903719ffd75992c97b82ceb31a4ee4c0856ff2dceb058175f10743c3d8981e79521b3d1552603c440446311e3170d66fdb271015af6c983a3a6b0c69920e88b388d83789010877aafccbc8c92cb42c6bea2c359e34ba9ac82a1f49163a545bec01f6242361bcb9bafb8c6c5c482ff672d1db032361e8ea805ee1e4d949fe486054f487554fa56aeea972a015bc40eaf33804848889effa72d6117b33ba0947c13e9b06426095264a4e3914fd7764dfcde39aceb7797a0d2374db0b335be381acd50ffd64aa375d20ca56ebc59d3631fff38f0d42c93f2a9f7eaef12c9a1a7dfe11105c7a86b045beae61a9123239c2f725df263d154ed7cdd0be06a8417cef1a9af0b7c745bdae38cbe98dca6d8e0bd546c8de3ca47ab0541b7fee0eea3d0a0c096c8dcf95b6c34e4aceec201d85d25e0d9c460af3a17c9eb0d1807fc2d871c877ac8f556996ba3c80a6d43f8858187acaf866b68a143db8601c3daa06cc3bb14a73617c3c6cf6a6b77c7c152b82b587bb9a03f480b53255ca5dc86201436023d740c13c5988f7766ef155ce320ce0fe77f718f867011162c4c2ee5864ad41dfad3fdbd2c36cf56cd7c468da0f69203a912cee4608f52d9aabaf8b81d5224c5a3786c1c601fad2d7070c45973fa8448957be63bac6f50ba3685521d04891bd225e3abbbe5442cbed448e050b870e1362576ef10108ca0a70b8239f6a637a0462ce1becee6b3a9c800c37c86f8f72a549f70a0575373ea6e01170d4df75a563c1caff18268dd36d50069a13c3225d5b2988a306fb8d7d934a7b4f67f671f39e00f587fac140665b879f005e27a6b6cc6524141377d0c6191e6e534172affffc9976124701a3681a2ce395c7e1ae7aae5c147ca799df11308f16d98c186f6e0b1b59e226ab43d90731882c9a8ce361360e299a0174f7308a51798a244cdf3518329837edee9cd701631e806092a07b2cb87b321ae198250a7e3235103b136d81619e362783364a756a0f8bfb3dd1ede1afbe3be1eca091b71f2c437f1e8a56ced8479d48830488575029c59d72bd9b942be5a88ff2bb9a451bf8e8d4263e4e1c0ba85dfd0591d9ada146f9784c35748a3bd05f7104962012ff6e3aff5f65eea6963640bd6a6ccd0c09fb0c9bcdfd46d88c3cfffa16f09586fb6d6f20256ef4b2d5f88c9d4edf57f26787315e78efd0f5a808913bd3c016d753504e3f793b6ad001f624981f5704f764376d9cca78f1c60408c2b3469ca016eddb2ad279fc8c5af5de0d90c69dc19b412ef43400051f42687dacc64d0367ab8e1dbb5d3f76050ad93f0e1e9262ebd79cec9d219509ac0f21750283e57b8c0942e38af600abe4abf2132ea67c904c610da4fe76d09b104335b6d67bd34ac676335d857362e7c2c564b6e0ccbc7271f92a57372b7c9ca079143663c05500820ce372429fb448ae18d8a1d7c5b60d52418ee9c11072035b779f9158082811d473001bb0e46674dfa88d47f39301818bc15b7e9a9a731ab2d9ab621646e3ec51aa0ca1d1347aa852efb6eb3e67a2e19a9ecd364edbd4a1b6747034c536cec00ca6c90f7182ab8e8c00b719665ddeb231ab2ca31df3e830881c7ce8c1d2da603c6cdf1db1781a9cff36a1d86a798b2250f9095885816c59b7ae419e33543c35ba9533dae441fd109a46d036c88f0f468c0a6c49bea41ec18f320291d3b274dddcda7b170b7712cae036b4845e0c652078f69e7651d9bd97c7fac8fbf3e533ba4a757da34a73bfa7e2fea8f6d950bdee5df158f07174e22a6f3c9392f37275426dab6240b5e06c5671c3edcb50dfaf6246730408b89f4bbfd0971dc8d19c4ab9b7e6a9d4320bca1a77efb05083e29d040fda88883789df0281bdd46506e13c0062528648e0e0293cc40e004a4cf342df98f8d4e8286fec94d5f6bd40f804c2f2b00c687af67e214ce2e4202da16c0cfb6a68e252cd01507737c531613a94be624316b5824b089cfe043aed0c21677d251ecc05105c29d561e6fe6106be4dff004b18c6e3e7b0777a2f25f5c2ae29b1ed3825c2b8bf7ce331f5890fb64f32cf71a94cfed05cf4f76dc985f6f84e1d7d74f29d508e5ed32dcec103bbd6e1e1acf5bc23109a3116a172fe275320ad8d681214fec5ecc5ebdd0a18614e40da19d8f370743c61cb8e8911d008df0915806210942103874b4853faf938b694f09b3cf753d7918dc8efe665955f85ecfb239e0bb0efbf0b83971aca6a21fc7b5850c502b10ace0a8d9afbb069ebf844eee01c7191bae6a68f4a2224fcaf6e23eb58dad1631cc113e2c67b0ea031af5921af81cf61d4af209ccb4c70521ef61fac7a412db67a30b4014dd9da5582ccdaee4460c45eee091d93079ebf993c5efd0e4837b719abb9dde887e95aae9316bf1edf08c626bb3ab2a98e67ffe55b0ca66069460c74bcb37bca42e42d59a233b7e554bb27513e32958ebfb04280b44ead9bbc433eb160da116c30f3693b46969c572a9da5ccde2e3e36f85a5c7a8db98c072a83fb4c2904503ebb21bebab497e1f89aabde79e1db4c7d8e6e5aafe633ba773cfefbf1aa17647b4a625c36bbf0b8ea929754b67d35439b71bb345b871837698b54dc303498f4804601737296d8ff7a5ca884581741bf00b761cc0b171f50280318f6c54e31d8c648b65b07efdf28220659b05761b00edb2b3f734fdacf25888d8e194596b654cda7284b3d57110fec4d5bd05be07c1a45987272677779d7322ee6b107c292f973e786dcb092aeffa9ec191a3fb01df002e3adec76a73e6a6b3243d77a83e37bc8bc20856c1f9658fb710a6e43e417423a5c5db093972a83733a4bd4752abd7c1e4ccc97baf772f9494426aabd2988f060f70226f9ad32a7129f9322f5f03a05ef01a7764811335a4f57c47518dcc69cbfe7041c5075a8e4fed6eb510f0a7d283d1951be4c6bacce8d2927b03dde1d38070d3faa3b26e1cdba50d90dc641a78e78eca96a4979ab6faeb0e1a3837ee9a2a1bc535b0108dc8cc321601946fad79d6b6fa2c0e22aec3aa911bb208e61289c0a1528cc2a3ec824d6d17881e51745d4924ec126b607cb5ddc3ca4d33f7805bcf7f1f369c4f62fe5a7ce0fe53282fea88fc676cecac58c96641e7f22e1eea4aa9aab2a4b97b6a197a2a2370fffc791c5cc2a0b2d33718ddcea4f97c11a490d454145bf162a2117dce86672afe6cca8dc70f961bed2149f694680ec8eaeea075a3c5471dc2f0ff6d5c8fa6c31a074a0c7ca3f07d2ed3cc76fcf22e3175cb5edcd57dcd3d11a763a5d94f9b500c006094a1bf0e7a7f32a728cf9e83283636b19f9dd6419d320c1d18c3cd3066f705adb6e59ab500f0f0d992dd6647ffd76427854f61c85295c61d6f035e12e0c8a041edf77e6b4e1bf8bd58d85785de72568a32c6782de63c489c1e615cfb6feea25a16e9ecd019e9f0c416bc3f205f035ac43eb239f63b87e4394ba8b845746483d05b7744c17c6113090e64619231b7a41ede21d115cd64b8ca036b3765522fd764e55dddf925087013221353af6193e25c4171887334c8b45a86f814651b9986fa2e75d9658052058f6c1324a17fa934194a72964312562a47b4c4b36dd46b373f67f1eee87dbec414deff0707c240b3b71824ef42210ad8bb5fd282817374d07165644e4a24aa3f43d195f0c85445fd951317b265b1a5f3ec013927b5cf44aecaa24060d25a26c795953cd0ba4dce3251d529cc0076eebe6bad17d5efcb7ccd9b21c1ed9efaba2e8ef109b5cdf949aeff3444b2def3dc7eb67452d93582b9160445667703bcd5c8b2155c0b69c6c515581b4f433621eb2f3457b0964514992490de25a7d911bc2f4a534224ec69c019a8065d2ace735febf8c7db14139c890983fe19e81d8300e025e24dd9d2aeabf22bf16a183e71af5e4bdcf2fb5edd38571829d907139755c2abe51a30ce629c271063d622697f4764b7aa88cf2b11a666bef68f03c359ed4e0401bd3a117215c84d74d9fd56cdce188383c1182eacdd0149cdcc56ceee64bf45eb35ad23ed1499eb744755b8fba566312992ada51ff2dc136ef3f5cc666e36f05c8b0ad68d5767073542c63a63441016cfca9d1f821aced831a451961bc27af3bf1b22d60a6dd286adc19005e79ec5f916fb611799394a334795f41e1c27f214ab65e3ca5f8798701003711762592bc55799e1ae4a5ea0f75e9b15e4793f3957315206610e819ed558367fc55d6b6c7db97f7c676302a6425aa305658cec8b4e261f1703e096e1d6563ff049a6c4b2ef33db3779ac0bd0f435013d3c9224c445cecb3c03ccc59ba62cc46599412317ced2c212cf968759d61f339d71de3f35eaad51ba9471a98839d8fbcad2b60253f275e6528e6ec972c54edb22cfcee39cd7722fc6b3021d2e8b588e54850a72a3f620c390985f2d4ff0d171a4b674c28161884778268cb7b93f0bdd600a0a6ae84cb8d7e3c4f730c867900396c23b13249fdcf70f69e4e5043eda095216db6e5f5bbd35ebd6e88c119e320774a9c0baea5cab6d866f822ae4d79f88fb0d4567733820cf7883da7eacba9976844b323e1ff8980d0d62be90f38d3a8bb526436c5190592bf08c4d350d3a54d85069103cd0b28604f083ad047793338e8f852018571fef004a0248288f6076c84933032cacac02a879cdb3010bfc487cc6361352717c2db4b8d542cd67dc761ef3aa4ee88faa46d9bd3813d3ffb9c9ba80716c80f966525cbd60854a8992a04575f075ae0423c36ff8269e4c528bc46dd1b67c73c258431078f7af81602e76f17a045f47e32dca309fd5de35fc7d117d0d617cb84be88c28514a0f816a8d37bbf50ccc0bf7cbe0d34729b8c3740cdb4136a0b4d1b1146b041da8882a23f07dd932385122ca069bde25618ba96e867c7994bdcb774475ea1c84654bd88f07e170a0c3d3589dc4a78f0fb3c49c50c1b600ddad3eb0c90ab0b50fb86d4ff11d6e0bd155de7370eae3f9c853223f30af395e62ff84d6b63829e9a1f782f67764cc8a9429ca3c059e84c64096a7a34a7cffcefe328547c025a9f79b54fc8823b930ef078e21c8f94e0bd38fa98b5bbc14ea71c6c70e152aa6edba4d40add7ee3171072d091ca9b63be6d7b3d015fa55f1fd3ddc32dea2a70cc289f15f63d01089ff56fd4b64e72c33d2a0dc9b0426eee2762efe234ff54d89b1fd7aebc04fd783ef7c0d5d034ba8337cc3b9d251ca6e523afea8e71719cbf2bd85ad0bbc542287200aaedb865babf5e0c0d902081e6df944155baedb24e54d3436fa2fc8608cb66001ff4bfed3633ced17b38d34301206df25f1cc6a8945147eef20e0516f14564a1d9431eb6682eeb2f6fa6fe40c5174beeb90ed3499afd894b1593f2d2d7d05da7fd5e171327f5f492f8c293afd11e4d55cd7f573dc8c621d3c4ef6a0ff81fe0bc7057f3b02239ae521773558560becad8b4af2ea5ffddcaa39bcbbfc4002ecc370d3e05a6705ffe398331ebbff0a251736d848feb922f8e185132cc8998153847a18c9c119787cafda1717b5d3181a9e37b5f837240be2f6e56537d17e776db5fa263bed3b7792b3c5f1a6d510bad1fb3529eee500a51772b6c010d1624c69bfa9d5a9f766f7cc9ea397c3a156f058e2a218b78d1cf49265f11244207632a212705b8984d3afcf355ac33f71aa0b227eca9f86e27daf0e2862f885264aefa63628c187b3ea09525e40b5aa30ee504d6ed2d9ac4013eacda90411f83a4736209166da428ba68a1aa56dfab2cc1aa1493840a1cd69b6a1a51fae0402146b8aa447b52cd0024663f9a7a3082915561846b787af984ecb90c926f968cda8825e2029cb627f699aa87adf871b23fd14d97af1540d776edd03785d7aab90c4914417fb8190288c1d75de49a306a5a4b48e124feb24c75ad0a4494c3278627238926884851c6985815d184b218a32e700bf0374d538d8e4a70ae455552cadbabb450ab7d8db0d2222d559876eb8688f5c4603aa57149ba99c2b1721f96ef91f82ca4dd1b06be0d9144d2b97feaaa5a1f16365861aabc1a9c332721cb4b93fcea6aae849570d5028d36b82a1e9ca24a0ffc20f05e862cc2bfb2899becc43ea3264269bf4cb88f140b8da2296a201057558966d7c039ad296410b5659e1b55b6e33883c6bcc6724f98c106770c50dd6c555cadc4c5b07d2a3a81fbe1ed672a4c2776dc8f9ac45174b5671ad6105d0ee6938cbb2055a62244726d70952e4e2ade2dcdfff0ed176916de34570153ddcd05f762c79ed9d6dfb82391296d91002c34307747300ee38ebc92236f7d4246d96c2bbd18d9bdbe5a76363988fc0b67639199ddc7053e1ef9d338b3f658cfa32b12df363a4ad87d69c51f0c0eedb7c0522bab02bdf2dedb835adbd91f64831a582304bcbabea915b6fdaa16edf708277c1daedc343b4c932989d2e79bb340d1e3d6bc1830da8e03ef29b05e1162666505231af48249bce78a562e12201edd9dbb4062380757f76dd9f8e052165e78c7478033ab7d7d111912861f7477be502b7f839e14c02bd7c3622e09ce8381f3afcd884e26fcfb83f4752e78735813d38ef8fd9a21c0f2a0d6139c2b508ffd20c0c3f5e6526e02f25bf1e0afbc84b15ef174b227023e605b9850af33a9cb2d17a4e040defaeba4ac35ec5f3d363d822f680650ee722cb407d022fbcf524fee25114f218808d617fb676f0bf762f77e253b1ef1a5512edc394d328beebdb972f7cbd24f0c6dcc152eadd18bf56e50ee0f3ac18a95244d06b016b221d128379d41d6cc017b8ddfd195fd1c1dc9f807cb75a9872d0749b85e27d8facc5f461103825d149bfd516b32182a769e16de3673bbd8db2bed7f23dfad6f6459446e6a276531e4a161b1469bae6314bfe248617329ce6c1209bb8f902bd8d903b2278a5cd9f367e441683bb81cad4ea6778240685d79d1b88113240bf0a97172c773703f9158064ea64fe4c2f4292f3d9fd2082779dd282e7f9aec53c882b41b40c4d42e2d100a8f651e6db9820acbbadfd8286bf802b7724978b19dbf92658f46f265cb15cee4175eaa42f364eb5d66a7b2064a26c10aeb381c443a1a717942110c1cc67e0c7533a1d2d5b653ad3930f952e45eb7311acec8aacb8ac6c6eba6a4c9bafa27cc04c270cd7b9678538d5ff2302b3f99aa554fbdd15a4e209983d60e4fd4c6c12c216cd8d770acabc0551a99290b681d11dd58c6efb199091042d28c665366b4ed0ac35dbb3846c310f52f62084d82c55b0a92558893926c90d8b32fc7960b197133dc2f666a9057469db3e968e36802017fb801836fb26131b8626b3a061fc73233cbbac745d45124fbfa36445bb6f3bdd19f8ac595f1a60c20d7c872eca07b86f1fc20d2e4a55ba38d7f296d78ddd6771af10cbcb93d37d35f4af67e33a32043e1dcb1a787997069850e609b74d196da0992076b9e404739ff38af40c94bfb1b88cee9f984c993d11532826113c7344c17f2c593048883830e7bbe8750204c270b00b4dd384f93a406b5d23ce895b7243761f45517b3d46fef80b8143aa0613417c7f71a96e80cec9f7bd71b36fa3c64af1d15b0a9987fa66ca7b443d5f00f39d2ed8000e4c05c47a67c2afccc7379ae6281afb21111f57c47490fa7020fcfcc25710dd40a6b084a7399d7a7354538e7996a6b4126d4668a405834f4e3686ccf45cde00aa9cfb2618ab4a5ad8899ba07d79cd85c79c42b7ed9f3c267c50e49b61c0264b22d762314c73e77de76b5ff649825e026000c5dc68ed967c0e6d652d4c26e7fdb9a3be8c64dd27df6a7c1b55e41bf8dd9254b31640c776474d3b04094357e60901dafb573c9c6da05ee60d54e506e31c785e594ba693cf7c20138546e59e912908752284134dba3e52014dbf9b48fd39800841806dd05cedbefa3ba386276064bb1570e07728b77e9eef470a6766883ee7fecc3822c4c61761db8b373e441a102b7b1c032f667d19168840355ebe1f6ccea31a7c902c8d60c4cb015066301d5af094acc4c04c9d5b75bb52e0b5aba0da9ae5096bd81b5e325df69a7e1b0ca9c827a4902fc4b5da0733872d4d8606df5a2ecfd9d9d19b9f7e351ebf2da402aaa03a1c77cbe16e222894dcd043f730c7b89aa36a8b585eec403b69a4f62d0578bf9b3c0cbb984ca9a697a73a47c205612092b3105f6ab5d5aea6fcc7465591cdb9263dc0330351085c787ba0cf192ae0344783fbf060eba3a7bdbf15a8c68ddac44c6c5c6462e35d8be1ef4cf0ad6dbf85b75eb3581173089744257f98494d9d65b5384b717bbf8e7099a747451dbfba52c60d091f7b3d77ee41b6ddefe1b8d86d5e266ba5842a616a86b94fbc9bb757ecdf0433dc992afe08596d58da5362410974ef0a792b5f434e2d8b00052d8a8ff5f777bcd789a4ed9f619624791093ec9c86d012b09859d3276281991ed5dc5e18bcac27042bc347618daa3b48162dd2482f9a9c225b7fd4eb9a7b7d10dfe3d4270fd32cec23b776d0be60e0d2bd6f2abea0feecfbb8439386d416db73a38cc73fe648a58fdd823e27763b05955575de47d855943863bb314b611465614474834c1c4a7f87903ba6d2ffc5132599c688bafd58ec6f28b8e717b5ea0bb1653dd517c15c00420166ba130fe9e42dde84cbc6744fbaef5977bd17218032b22b0d4807a23ac5bf220925c1a7960c3468fc82383bad9020e22e491974a60869883d3db8473cd5f505b3433e858f30b213d7b77a261526704da34e2cf722e0d0a4952bdcc80c154a580e5a7a027e698015829caca8b824d551d45a082626ae71ca263de9d3927298188509f64d82b998077a587363294f0cba7bc35f12f461707c421a87947d44177356f6a13734bbb10c7e29e6bcfb0c4531f45e8fa293efd48e78fd515335d6960a5f71784460846bd6a30687163dbaaabaa410c1f581af9bf6b3b66ccafde309c4565deffdd24b6b2e6d1f2d5f9d452697b563007cedc5155f5839d6c211690e8f82f0cea22e42a09adf21ea86c6b54895584049e6617b41fca16a97dcc4ec09bfd4abab60d6e65d9403ac8836af7f68ead51456a7605a6d14b438f2bf8a2370cca0df9811a7e1d966256a4b21e90060c3bbefb1a8148fd48e4c160f8c11b58100ac11eee836f94f5ed150c6012ade08da277b3389c53f5f82ac8e24d62576da106b05e4f307bd01f208d83f4319c6a0264250fd61e5a3b58e2df578143d1a9db5f8d4ecb10cd58969975c3025183f4e701773eb7ac113f1b31befe3eb1d98e11d462bbd40ffb76e61860b0f35fd3bbb72ebeda48975df67bb60e2187e13c498c1bf81d736aeaf9e3b14582ab5a6b0e7d511f69f099cc17b1e816b0c8091a51c96b85837cc70716114d7c7dd71af98b2beb54c7157e7a8fc99307073e4ff45c09f2e1c45b6c4ebf9841aee17cc3626ce99c429f1c11e9558ec644caf42063ae5d11b760b7a724e2ab48bbd4cf1418f7edab664ac7436d65682efcbdca79101773271f673900e7eb83e6a3867481b9de99bcbc6aa854134063415821b1e04666dd18e52fbf4360ce420c4418116941a6e8fd6b114544377069ee2c419a89069e18591246af36935c52bdfac49d973757206994ec669fce20b3bface0f25a69964b02aae63648b753d8b296cdd014547d5e8d69aa8cf7179ca81f2d207481e3a47e510cdd9bb7930d5a46a5a17f2f7e7b079b07a85d15e5408576daba48656d013c45dc4cebce0f1dac1b0497d703164cb57ab687399d148dd49dc430942c759565c0b056945ee2a64c97e67e6bc4819b5dc89bb408b59a9765bfe33f3bc6a08f5da75b847579b6d533e4af9ac7f94b7620051e4b3640e095b6d3e282064aae4f97631b2388bfc9181f18274471ae9ee34490921ead08a2023bcee3e67e25f4b115f5bd7d9dcd6475dc8784e44cfb58fbb8ed660e352af9e467568aa7a6de8fa4e6de8c785cef2ee8c6e7a0b36e4eb9bb53b7b7321a037198c4d30129b0c0f638b0d5968983f0e138d1c5b45cc6b39720900f707d59feed717256cc2341cb28edb4b974ebf51fac3fec9edbbd572aa5fd96f73290f8fa39e92216a9cb06629c8d79083a5f2098a74d112c3e4016d9a2430f9529dcd0dc05789b4c517ee166fbf62b9c21f45027245fc452a3fccfca450c7117e23918d012fda738a23407fdec53882a80f03431612bd3ec1e17ae8c87c2268e7cbd18c763cf9b075399da6b9ad494102f5ef8531ecf130e81016467fc79ffd72e7d52a6c18dc275dda76278558b7e780f102138deb0dc90bd66054c7bac7037e2707bb0b972d72786cb67dd7196b30e7275255dd1127bd6d8db72532f13c753182b920b8d346c7723046036cd52dbb316f0f829aaa98595fa86edd1662835fb3294ce1c819c823948604c563241845a31d0786be7957ecfbf0e87059a34fe30a5a7344bd0d8786f5dff2f0685853401ff9dfc6667805794ed40c79394b764298df48011a521f90e346138e9f69749a98a091bd087be9f94a8a26b2a4ec9e5030f4395c0c0808398054e77179db13ffe1744d554fd437d70beb2f51268b91ee315eb7cbf96960a24f1f32d2216da8ae3dc664a929e96cad0ea00aac094da530521a7b12c52aada4ea5c2ae5acf8a3d349d2591547cf8ce2cb313ae0517c8aecab31b2fb4c755611a9f07d9c6b10d4f2799315b0b0ef8d16027b26148479eebe0f348dfeb922cb8a61ff11c03607f98a86202ba4e4f9158ba5561f9bb00759ff55a9bf5590c58b593e3ece55f5ebfed04cd9d128c381c71d72897dd94c4c6ef4f621d7bbd79267fc3332de3f05ce3b3859df76032606cf32fcfb7d68ce6597311fed4064fe87e231227dcc40185f0d7306c5b430dc83462f03b7a847019dd583a4df0c52de23336a980d84dfb2af7ba11c963ee8d9d72031ea9d818d247a2eb83a0c3e8e27856ce324cc6c78b0b3ffd4774872a2150dd301e14e473bb8248acb86904274b1987549b01cc033974353547435b51b747dabf3c348db65cd701caaf62a6b08ec29ea7ff119674acb8cd81bfd186ed1319686158684d484dfe69e628cb05d0c5ec9c1e63c6f98d67e48a296977112e2661188f8a92789170d3361f4f6f15ff930b9573e5549bb9e68a591e13968d68ded6ccba9f498735d142e2ceda1fd5fc8b9b306162dc327c3a26dae9b42f43bb79ccf419e82d0a80c701b709692881f6efa7f00ee5ef4bddf95cfa8c36432ebece689d3f113b5e513dbd8e10263d1b79a98bd5f9a1bfbb0b315e37168d248b1d4cd72b85287f864eb95c903189785c5d704cf3582360934cf29d9d775636ef971fac1545d40338771aed5c77ed2a620a262b58913335da03f148d9ccc07ca94612f8368b183de65c1395339150cf7c69a657afdd20c9f469f06633be92a7f469ebff3edc82f13e70e0dd07a0b394461b8c1991079a52df31430b873db1f5d3701790fed938c0b2888ece2df23a6f44e70e580809178025cd8947a47ad15eeb07772477eccd366c11e043f5922d9bf04fc3450d6aecb4f544cb4442fef217c2f7db6b51857e122ab6e58c39dd369989ede7f0b69440d8a6d0726e2c8328d9896d304c790ddf09aa610b9200d70ce522edabc6123a89327a60bb3e597839fcc2da3b4a6a22155ba1cee8eb6224d4f7862b86c75b3bf518d22ff061cef0f01a5ef68a7b92948a182b59d8fb32ea6e6a1da86eaa1df2c370448e9d93a1dd2bd5593daec6ef0990c06d8e0300ee93d635a0f9d53408f4b806e7e38bee12521d309ab776087cd9a37495194b6d32c3cb94a2ab9c8ea567eb6aff2edd6280ecd1d17793837a3db2da9a9c33d2fbfcd90ebdc08a5199bd0164e058a1de38d45a0ecdbd67d6700962d530cf4baf5792a499b43a2d2c383864624429d08f9a0c1ff18c1cd18bbbf4208cf8280f2083b3c1e897f3e7b8d56a96857865a0637150062bceb9c714dbb7f803cba8f1d7c26e00c4c73abff5c4442b0cefcba549dafe6f0da63d03c09ef20351cbb13c7a0237a5d83111c31b55b1382c4dabe1b10af440c4115ee23f6b3ec1c233b9943220079ef2225046dcab25e1c2836bc3226364e3da6df1f1fcb36316b3a07170a36ccdfea5f3d9c0f3d5e75dfdb32c130645f0118a245ded62cddd065052cfcf806422b7380761620c50010b97bcdc7c626fc0e93ad02dfc4a6c96292655eaff4f90532f2c24b7106b25f2e54c7e23b5dfda345b26766aebeda9ca78c93855c7e20aaccc3af00689bb1f1a427e7df7b4e9b37935c61b17c6d9004d6809fefc011f6c060d7e808c793e9de35fa3039baaea499f8c6048b0166194a1fbe42daa2c4842026bc0585f917f540fce614512aef1ec8983ea0bed647d0db83ecc27a386605ec94bb7627de399262e6fc603c374ccb0bcc2a41b767a9c32a7da16b865f4f10d9c7f611963f6f7e82d88f15290b313ee0e338ff66b722f01117947d1f0fcfd82d74124d1f7ded47f54891f10e6e59f1329d487afdf9c2584918083a7fc7d1c38901ed14df02657ad3d43f8dc38c73d50aa271547e5669af7e07989d6dd8aab1161d903a44da8f50f1cb535b42acb1aec7d429f10bfde9b20a246df8cc3cd97b2149b22e631ddd2336912044bdac180653dbdbd9e166f669fdf93b1c75f1e27d1ba010b67c51154db4c1546189c17b475449a0c69f84afbbc3786a2d5ab3e3fd6c2bb7c7744e35b6587753c4a7907ecbc8064e3349328e1a681497bc8abd956963150b6fb125ebec489b631a4d5f51703ba77ae4bbabf8f1cbf5fc248aba904813f07b1ff7193a893cfac2ad1b11b481400f0a684e518e1901b9e154d87d6949cdfb72700f0503d08b7d10025ace1c5884c3dfbb00d9ccec88c4103a2b8a0b92723ddf39950f699e66c853066486a365b10d51ec75223e92d7ad4681173b1dc79b4967293f3f413cc8521bf01ff8533b001c380a89b8e86f7f47161671b44de5b4ae1c1607246c27b71cb027eadfaad1ff84e47df05588a0fbb98720e9137f1e7cadafbee0d86c30dc175b4135b9a3a005a99a2f1b4b5053489099e7dc81d500a700bfacaf324c0a1bb373490ae3490e289508239ea7074aff54f1b9baec8d55e4b7b2627cfea5dd491243b3d34cd47dc29a711fd01f6da9e0b057c6d33f8f1e50b96a9a76c4079baa2107492c4da4b1f9d3938508345504f21a93bd4b5c4ebf75a55b0ab25f123f9f46e985f7e58490ed05d194d9d084b79c6f4a6e5c4d102964bb13cc576041158364bed5470c03c52cb60e3466c2b06a95157e9be192ad7f46b947c47cfe2aaf60da08a8befa4bd67eddaa9adaf4c8e778cb759349ff302a8d530b32fb1a5dee688fa70168cd2b90a43691403318f495b6397bd794ed5d5e3beda7004313a2986cc07d9305851e09b30a4ef2895058eb0e9ca22e80be677a15e03903818491d66d17aab5c86c207344cb01c679980eba63ff75df7a074351385bcb1b805e393ab9beefa05a746de06cf5dcb1af48cecdaee0f0c18b7071996ed7b613b6d0e850a7ab4b847352d8bacc7a9a00d65be27094f30194c93b0d2528809658412583caac8aef4add0059ff81a10d3f24dafc58739dd06db8a8c3562a9b21854a69a68dee22860fe192be791622f215cb5891ea9933aafeb9015a8400f2bf5c98a203802efb29fd9eeaa70b06ff8b3d7d1c140bde5692da0e375d7d233b02cdbc5b067ea6691dc42ac7a470db5d236d14c2bd227b171de4722db7b912ccb0f66aab8c1a44b61cb255c890ad765c4371b3396cdc86f198e3fd28b0287a2b2f35d4184b40543c871fc63a6255de5a766237d6db62b8d0845baaf9780cc3ddcc77663fada52390fe35be01154407ce2b5b98c64f0306a8ac4a84f7667bdcbbd2bf691931e51155171ee0766fc602d9d5ffdcfbc6e0a6d0b2f5585d80559309cbe1f5eceb4908c5e0530c0ca6628c480072d32259ab078951042053476c84e95bedb0d50fbb3d2c386a7c0cb6e94292dbc28376e8a2809c8302e8d6c417aac250e1f75a06c54b71ec1578147b1147b365efb77e269c370084f633453a29eb9f73b73d76d81ec6a1ce46d5b929572386799e7f7f1087ee10a4c6b7cd0a8aaf2664d367b0bfca7ce9e18c79f372fb3c037d0e97df0d68246da3ca98b3c39282c7a9994d67ee67b89709f7cb396dee1d99e7ba219de9c18eadb9c6911215b55266a7506c1950c76fccb2e41a75f80e595e4d2ab0786f15e579bf817ad3c4a6ee00b2f908270af02571c135d92a6cc59b9db66753fd32819f73da748c5c39a77235977181675e8fd2f9ee5e6d0d67786b48bcc92a448cfb4bb32d04f3f560cccc0fcab7c20ec7b4740a9db580d493a12520127e1176144cdee11427cd944725d2749c4bb88c1e6a021f6306ffcfd9082b652a274903d438e117ca7e0dbeed27adf79483382d8c3fa7a8030e4c965ce9f0a23411e76d80f020d8a97cee04311b8a7f87542b335d7098f26e70873279a3db4f6c05859c9a52c30a8b6501be30a9057249791ca95725ebe971e2c68d50cbcf1a038497a1f2e3006ece13e65c0db13931a939bf0b7eb720ebaa72a04738f019730d274a50d19a2e8f868fd5f7867f27fa384f1b509433789315b5fa54db8741ff86e1001e04a832b5e42a3f88afe7d3cdf692a9264d625f3d18516e96cc6bc050000b08334a46ce491bf38e90ec1900161cbe50ee14ca9558fe161ca327cfc46c3f35ccf0e6cff3ce613af9bfc66bf5aac736874bcb16c0b6972e669e89cd603571e026b6a05d2abd874349c1bf035cb2543eefb6c884e31641a1310adfd1c9f741655ee2e582afbac162fd7f0ab7de71f4e139f453576d54e2aba00333ef7cc1808ed4e593481e6894c75f8e0419462d348f2515c25ff81433c3fffff78bf74fb8d5757287fcb14c02cadcb46510641c549a5d34126eac0074da99deab907af5e2032ef4ea28d82a76134ef3cccf741bc4df7ce60e0e5e1b9179505a47fb439afbf9e2eea04a3a4890858a6e152e11110227184619e00d3fd1c1f3d2915e73b7dbc71a28e4d999974a03657ccd204225be76bf09407ea326f27a80dc8a8a0332a2a10e1da25a59a7caa01e455e8b0c172512b78a4565bb71723a3298f102d0eda9b3938e6ea1a9da0d43572cfdb99edb087c9e3e4e91a32e9fb2f05cf55e996325c66225fb4b234d084d164b3a57c969f294cc25cba81e259d193b9dea1a5a0eb69b19b60dcce42fa63e2b6bf596c07ce8ae596be540571f7574f3ebe464ccdbf4a9019d8f8933293b4bc32ece653297247d3f8469235d33008d10e471cd1f49bc339c923905664300b7d59856c549213ae290b35aca4321546f87ddbab92a63efae8559fbbeb1367fc5dcb3b86612a9639fa5b5e39edd37a0618d94b310406e89bb77a87a311a04ae3b91b8837f69df2a6219a45208ae8223ad5beedd0b671e2a4051e3472b1481d221a34e3475f33c02fa376ab003561b56b0b13103655f2d5203de05e30e5c041b9ab15a96be4a5c6f793e6d7707852ddf36c142d0f622ee751f6703c0a71bbd36892e9d9468818945ec12392c69a99d31b61c654a7f89fde57e8a3055c90c00c88affa4384987929b26c09443ea8e1ca038a878bece09e8d887d9080b4794ce048297c6f203221d1ff699fae68e68ce4da87460142bb85f3c3bd08c23309b5e3b5b165233c54aa5b574c3cf1ce1b16c9ede0e236fc2b1495a450caa2acc1d4620af5a1b07692498df450f9dfa36a64307d2743c5698a0c3a78143ce0be0fea427a37c0a82af743233dc1ef8290b31543bfe1efe8af43364e168ccc826c49e00cc6bdba1976e7951314be087fda3bc769c75b01c3f9c753d43eb49b416d025c23346edb31ed7882a80075e52a0506ab684bc2fd437abe907ed3fea3df2890cf544971afd75211b8c48242469a424e299016a3a728b85348feb9e28bee1972e8321fd598f764323a5ee3db28b7ece06d90ba08bd5c72ca9c804ec7fcb4ac1c6bd3bb0143ac007889e97023900f906cadd05177694d3818e08b600b40bc34a02d138345f415754f601d75c1b5ad74e500b709adfd94677d72b455e36a74c7625a5720d77d396a02eebaba8065bfa57e0b286293622e0d50c4b550e32b2f683fd806958618ed9e6dfafa2999e20aec8c0a49d3a7479b2ea153c81343bee58ac66224a57f81a036ea4eead46e7fbd9b480af637ac312bdd2ad1cd0937092efdadbf5f31ad44cf14d907fb60e3cceec5d14aefc42a7020c0f6c0f2f41e1c2bf210742b4e1664c3032b63fc2551499ec7de04d9b214de0443fd6e14d9651b4057494a16ff0b17644552ca35cd0bbea2e694112c50f011e91359783c27898aed31694a9340f49060b60d8174964c62a7e7251e109a7bac249e11a31f98a7f5a196629ee403038b80298d789858a4e8c435072d6dc79244b5435342dc0a718634900b7c2ba7cdb44305e8a005fc4e41185217109ed3a0fe4414ff91d73a58820762964cc58e01c7b16d34b391922eea680969ceb05577c48471e88d9b5fea22f421e7ed7139f138f62bba538dcdab6834b8e8857bd1d71dfa0890c73a76e6313657cbda9a87c3706739e4bb532edcb277aa6779e214fae8a2d6ca75164e210fbdf21216b160aae3a4a66fce8d69f82ca9750f7c06a55f4593e353dbff8ca140e189ea6ca3f31275dc5d7f59633657320e60fb4e29e3cd99ea6cfb80091cae9285a4f7644d0788a4bb870ce10d42d1c6ba3b06add8d5461085f379d7057d6fd51cf32ab8505bdbe6280e1262163cc1e308cb09f2706c563d9c8ca22994c08f276472ac4417c877e5b4ceadf002ded60bf26aed2d840a030d0cab904be4abe98992673b481a87a5081ddbcea8be408702190f2172f79c8f1df5163314d6ed2c40116925d52787470850d53485a429492bbda66882ce34e00d078b1c99d0d4d02f092af514ff48acc48e003e8571a9e7443741cdd0d21b4e680ac26a753db9f4f25b2172e22f86e65c00de0b91d57e772f1159d3f60fe2161b38482d08f40caaacf52fa0916eaa6f13dfc433331285c9667221865461752fbb523d5de30a75e4866001ee29af227a5976d7968a7bb2ec9eb6c9cb591afd05b52eb005b427fc5748cf98679fe30748c117a055b4df4ca591e8000626a5856a1a2ad00b2ab9bb6e4039f16cdbe9e1281c69ec73f85b1e075d9514f90fc9c164160758c440829727604392842be1db7bc719fc5d1b34784c7a408becbbfd8101832a9ab314d942d35dde56f98afb8e54eae3bae907242f66552d3da82a1710a53e27c1251f46ac44c8c4acc2663dd0cbf56ba33a705971c8321a43fbad07c8c950f5765e2b8069bc5534cf4f252abb37d15e98fd901c82000f080f46f6ce2aa043296299e04dbef6da501496e83e0926722dc4458d3169b5aaaf00703e84ec99f698dd165ff07e644870044c0af4fd2d988b842075966bc14148350874146d075e76073b3442bd477e74c286d79061205c7668da52feb339d47a331fabdb5b8aaeeb0e85655f28697a052e699c51df596a0ef7fb0da54b3d0e43359ce4785954f3b9b009183837b9cce775ac1c4c5311953f81357413bce0d76879fa0c468c8a622d181da4461c9853a002c252e599441bbc045424ec8a98b8577e765fe8c87577b3cf42af5d1db5cfc4e6fdaaabde7d4e4f52b7a15c9d9dd125f1566c6c8a7cd7264597e6690a21f176518dcd4c1c0eaa90f15cebc065b290438de01fba71e6a4bcf62f3b651b4a4252741b17ddfcd7757cdd4788ce90e9fbc09879d4bf6435b6818b915f50759f5d2d1b5d102600215b190c40c6dbf26b70d2ad95e7273dc397b091413cb91cc79d3e6d3c99fdd06f13726e03fcfa9271f76331e4abdd3e0f972b51f04d87e87145e945cf3e4b3994fc72173e89a9af42b14f717d9eb727c525cf41a0e0b3dd70ad9acbf1235813c5e452cc97ea09eb763c20021452150258356bf6920c42b65c295e6de4e5a311bed4f4c9bfdedb8b125cfad0ad4cd9e53c9b2cadb4fe3edeadb08158b49821c7edb70dc138169d7f01715239a5cb9d821159c5551526f8bbd873af240d55878516839f0a20cc924912e06bd19efc319044dd690523af2f2e4dc16bcb1142168fda488da43ca9914beab340b13a5848c6916060fdb77803d0d391d1484a3226b111aa703215c4438b4d3a79736da8910e43d02a6c3781fe94fb813adaf6a09286aa79748a82d08f614c402056bdf71d77d8ca0c24315729a407f326c433ed6b830f8b48063f1c7ff1f5e411315957b93cfff3cd52f55d3328748045b2c9068f116d9d3ef8ce6e133ce86fa3b215fbe6265b545a5f1fb924efe335da16bc34b7ccfa8c5616bccf506636da3f0f07a170d0e7af4c62aba2007f0a8aae166e0c1dab14230d2eccc601d35ecce958138485b390bee6fd9d9147888e15b8f8e0d0c1112f78ff5799072004c7f17c1d82f02ac2738b6ff7814e01590adc44680a8ce38e2cb07a3fb7bfd63de4de4fabef00f75a6d371fd88f24e0e254e87bd5a102dce3cdba1697da1487b37cd8080112e7ae0c22231190be88c996b7fba16a2232ef86924149ffa04a9ae6281bdd2839a92e3d8300679e94698c8b0914383b391b4d52fcc41d3196517f0b0d6baa84027ac13a5f63b3fc25800c97788fef1d237dbecafccc85e09b972ec41f6895ef536fe412c4ce758127574e20083d8f4b78059a6e8d66a36f07bbc047db7c125cee9b5f4ccb649e40dee6660188db9d6654275ba0113ba6d3201bcebfe2b9dd721f6555a0fd0c5d1000374ae2fefafbd293de7e3917b73b44df518c984c1059660ecaa1bd2ec0c660adbeb3dacc475ac6f0deedc0402a1278f4a5009739d028717fac6458ac0710c04770ad399fdeff3fea4e6c08ace59f8a0b3b7308c4d7b9d66881c4f8be2ee521ca6a5bb47793fa6a69c8e15180801af9b7d8e80eefa92752c3732ae82464f194eb1806ff0fff66da1ed875fc01a7c88d0be6b49da93c9fe45ca662e55302a5bd8b6be86d3ac3182f7235a33a4e4dbc932e34e8f48c0cdc37062dcfef36e0c302b7cec88625cd0eb2b270a2e1ed92f80e75ead0ebce901d50e60b3c36819b240fff97da9b8faf45d663145500ff179dbd85824f400682b60faa6d2429fc240f605bdfa0d502628cb63e07cd0a67a1c625f772dee2f0c96823da22e50a12f2e8cfe378793aeaa216ecfa353de01e30436370f9eb3960ee029ede19741b3fa5242ec4049efba96c288c4b32b24d1e6c179a018cbdd6e748f06039d95359ab91aedce0e58d9379d071758b7086611e13a54a3caf6b284d6bc96ee38807bb2dee819d36040aed1e86b8014d67f11f08b110893186383e48d19b63e4a1c375d915b005e9ee77c190e46e71dd377483e6462aaf5ffac9dfbacdbd89617e95e81248a6e5ccc76332b0a06729f460bacd2454a74ca2c9a3fe440a6bee708f2b768e9658ba66690e10d0b800b1c0fd08bf3497cd0598b56e597b54cf566a5e63429d1e00ad96c6979648ec215a38262d2c4c6a28b1a03aafe582456f9c6fcaa9fb61250da0e372079c3e97a23f152060af0aee0e8a34ce048eafd954f8fdaef7891e9f9b977889dcb430a7a3861b7cefb056231e9e34891918bf3f1ebcb97b60f6c637fa11972b528224a7a84dfc1bc3ce7f19f73112e8b85a3031e1fb782d1709307ca182deb8a09a49951f0e24aafa75da75e8e282371e0e631b7aaf6515b368ef8621d1522d3f52b8b6175a384934db47ea5c0ed049df5db3e62a177ac37d6e572606114a388cf2b62f7456f29693f2349285ae42f22c343c173edb1d555a77c065ea57f8598bf88f3dc40f55ffc11322159f25c24762ae58a25ba38977b27e4018fc4cb97d77111e72755ed7257dce66273c5fd98770c9877fe8b45fb734cb1e0b772bf7535ff784faba594ee78789f0f5d11c5a0ebc658d2020c8bcbff1d1c062af2d5bf2579b640be1735322dc2dab9cdd950a4b73c226e8a179a8e248bbd9ba4be8f9f4f33ca38db4ed8c4d192cf7074eca1f15827ffd15d17a05bb02fdbc35ba0904cf147e8238e6bf609e59bb1c298a18651134f5833f3f9af793874c3396d01de3908dc17a62fb1009f091676268e55e788d0d7e6f8a96675c04f6ec1faf00c0684f76bb1d4c83f9325b5442a1fd777aab8ce5e8aa57f4680316ec47a61631771ee7aa872d002b00fa29a2409e038a22f430c180b7c61dde3bb964183e64658b59edfcd65c8b4bcbf05fd98f6649cca26165773ff0ae96444219d4d932741efe38b40e336112accca18d24c1c4d2fc5e27f73b177655da545cfa9b74b9a5a7a60d1df66cfa3f6ecafbd14b57537c46ee461d34d30b1ff3bfa67ba5d90bdac1b26a89845cda9c3094bc69e37620aa5ab5b79858a56033d462c9ad3ec9798a231a198be66de611a1c7dcc79a21b5ee9622d0b2bd6628aaafd9f0939245c0f22e8db9cf0c1e9b87f8342e864302fe5435c18095dfe343333bb6575d9d8335de3380e9f463da6f3082c3bf962e8e09a97159bbc76e69ddd94feb261b22b960885f8cb19985dcfd53663a7fd3e2b458689b81dd32e5e3f22adf60046fca248d24eecefdac3a3930cd76fd04db592a8cb35f9e3e6341885b74babb7a406f3bf97e1a37d4c5feec7bf015dadf9fc788f9af39775499f87c5179f93dedd9df2b2cd3a917907bbdb306041f72029a9b5c23973e7618aa6300f98848309c79897a2c57016d4f2985cdea8bcf912883c5ef5b89db27fe8c5efb6eadef06129357c7cffa56c18517da52c36a23f064f5cfcc0fe59d21bcd476af86f43e03adee0bfa60f3ed6141642f31f1e101996bafc99f9cf9a7f013fa8e607339c8fd1689440b061c8a101fd04f0150ab6aa7c4ee0b1f9039b9919ccfe110b25f0d5f9c70f62312988d0c58a92747cd84c8d5c3748e4593f0547edb141a62c988f9bf4bfcf0103eac4a14f3b136a536781d73d7815f5e352b78ba9ac76465daaa577688b7ddf70eefd7d8e557bf5e8b8df7a973a18a2f503b3fae94326cec6b042bceabcc305fc6145d6afd59adf02721fead2a95f524a1eeabb0c94faeffe35ea4bcf7a13994ce92bdef7a547ffa915c3dcf43d1932924c3d2e5662e92fb440a1c69cb5657bd88cc3ca32acb861fc7189e364d78f7fbec1f977e5e93232c03783f658d0c2e56cd38fd1f4cf064e7abcdb9dcc5177ad1e260a42e9cf4fe13492c4bf4f7dce62febed15dc67a8ea971407d236042e0770c177bfba5dc8df3366b643a0826e80a60e241a6bc876117ca53b2ed6dfe8227fc4c6ae7d149eee82611046e10743f8a7c894621eedbe986ca1470b265d9430e4187fce5e4af086b7abeff2d1b0db7df703769776cfe16f90e7bd08f7c0cf0db148e532f1e4e21edfc4aeb7e90d03399bde9ad3ed470f8285533e7252fe79afced2914de96df568f01f2bcc3f440d258d7da53b1f729a47088da9ff1a5d97daba6526523fb6fc3da71b0fdcf2c3a5f933e404e60f1294da8ba52f0cf82db042376a71e55f1009d414258f663f3bcef18691d9a5f0ec607fd56fec814a55cc466754436d059f4c8a3aff605592606510bcc83ca3c67273d58babadd226b7da6c2ff34a730e77ad80011fe977373ed7f11d0bcf36fe73fd1219325f01cbeba900e9e7f8dea516a0cb4e762eec91cee36015ae35fa5164b6ac6ad2e9b56ff8b52f0ecd30b5217955f6470e669bdcf530bfcf953c606969431fcf6825d550c49c6927497f122ff0cb61acef46e7756f46e226a1a698ea7845925ffe4200ca8b4272cad857dde776ba84015fdbfb9f0370e1b0d5446cffbc5df93919396125564cd16332ad64565721e0ac67d223c0900cd67b61199a656946a0270c4c0620129b537a6334e91f5ddf1c6276d2bfaa7342b1ab796eed4d7d6c5c18ea05d45c3944b803dad3400e5401d8374487270edc3ea4222f574b02a9deeb29a6e178eb230698d2cbfae5824943575d0767625d6caae45d133317c5fd94f9f92fd881cf0b82490a72e314a9b6f31c96a9b9496ab8882780d47071f5c671cb97d2f9221b01f1681429eb70f06282d3f7e34ca06b64b7369172e77d83c0e03057c63228b70010074497ea78fbe74a8c1a922f8d2cc17e36bf6e216d355f2dc3796ab40a2ba4302b26fe0a23cc8ddf78aa27ccfbdba9356abdc9f5fb494c11718c0932c65eafda926e107640ce1dbba8a12a40bab185b05c2c5853cd1017d3984020c1db9d614dd4dc7d160ad8a484d7362c86900272d949ff550812d80e329daf8e57d7a653612b84c8be59972175beb84d6b21a8a4288dcfbcd81e03c0e192cc021ed440cdc301aa6f8b5caf2622d3cec3928c122c789f5ee1041c580acae24c485a16175a6ad1b2b5d2d6e1783578a6815645c88aa0c91d5dd218ac268b4bea0a56d562f961000344954b5c93b56d33ab9a2a8e3617118146fcf82013f34c4cd1119d3999deb28bee6422c04cd2fd6145839232a85d3ebaae4e3710017840a78ba582c132258fa4bbb27bd6ef6e207f7b738438176e62fbe746b66918c163377de5c171f4fd7537c70263244440e0c17cb3016cd3e9a222d439b3a1251acc8b3e25dd6a2d85d1ed7122ca9dac882c1172bcd02e8d694e3877472a94a0b5bf6c3d6dfbc882866287a6c61b154e99e960750bb0e37862c72e9b9a262c21ca1567bbf55c65fd0f9ecd4cbd6e28b422d5c46c27e17947a4aabe4755a289abe8ce720ea554cc256e1aec55fb03406948e6650d2d3842fe476205562590179377b73194f1d80fbc8c3f7afd2d056b05ee83dbe0cba746d116d90c5708029b9dfc2c20692606e90ee87f83d4fce2621648c0fb9a587836af1a2c6b422e26f12323552214e4ebc023ac474dd555d0ed02e913f373787b2ca03933146a91a93c130baa6156aa96bb373415a09ef16d4d6f9583eb7ac7c460e6ca1c28ba40dd8953439e012157b92c5f1b2e305e860038f8ee0208a62091b85a9fe5ad088e7530249bd04e293a548613489953f2461c132f9a12ab8261bc4535d6e7f0e150c5cd3067909b996d770985ee8808147952ac6776a46240a83f3cba900086904dc7bcfe46c5eefb8b8e8fcd914ed27b410274e82819e109759ef8a366cef94ea95ff58adfc82a2299e382e13b1415e2d4c7c395d683b5b39f245a508ce51c31057b11f9647c3e301490f10fdeb909c1c00ee162bf057ff419e60d8a0f80b359e383f8f419a57b60e219703195b20a8a6a9264edcbeac280a5b9ca87ef02723d0403d8e761faa3f0b19e9399130d347659e99d11ba0e0ff0edc9c8b7be5c983982af3dab4f6599962489a1e2f82b940a02b64f3b2cd34c51541d2372ab6b057f95490c701e07cb46733b7b71223a16ee65a24a1257f0ddd5934d0e862804698bc35c896b108ffb481dd2009ebc9db0d111aa4e9c4652e3f33d6fc967d14a0f0d4e3b7e6306884446319449019c457a6ec83abb4a7d197fe96b3b9549910799dca51362636b30469114f2db38895c3ae7dbce69410a18beda545e0a92d731f636e04abddd971d4082e48610b314cf213cf9703876fb27eb0c3ab3f70c101e0c06e8835ffce03b0190577e13ac8c011d1c9a4d31623400d3df54400a24c740a974f7d25082c1eb62099b611c4c47a7fb11c5dc94fb5ac99f5d1adc3bc6858baba74f3800a2b66f5594b9176c2affe078d4ff281baf3b4ccfffaa19839e963c770bce10b62a7aecdd2a42c3256415279c73d5062005ab5fe834e4103eb511a0f3a013c7072a6930b3252884f37aa4079d39eac52b6366d94a3b03d4567e6a47edc2f8438b2563981bea903b323c0c60c3aa4242eb7755f5a39c26dcf05317414100c57a07ac6f3d4de0d6ecd7e6cfd9cd7f2bdee68ffc4ca360fcdd236425526586b9495525fa22b34decb95b18be4b0dc272f3dfc2114bec9b9580d0b0846e57d6a4e122f00cdbe95e8feeb9030b639a48521418bda43de92b2dea0a9b5108c7df43c893727d3885160e9652da9d8bd1cda7731abf4b7675d24a624419ea82f2f98040a5f374f98648fdd6205793f77d843c744928c317921556a4ba41e897d0a7348a991b15df0f14753ea6f0ecc37ce69ce61f44ef4924b2503a0519842a366f4ac81a79e1a97c92043bdfa7403b9cee1e7e6922e53da2e60e02c6078128278806a5dc338dc200652c05094973cb152d83ffb46ac6e1ad6cebf879e28eb6b6afb13ad359581e6e7dd051c1a3b4606aa078a75e670a17ad6c956400fc55202c0e2fc29f6715fdc31524d57c0f34ed7c75ec4952aad65e75e53905bcf7e1e14360868248273ffe9b15060a5b1a977271fc80b1f4e66b5ca4852c9b8f300b7faba05a19a06b3fbdbb0cb91b79e5b7801908e98f042f0b26425f2c23541e0ce7197d11cfb90cee99dbbe45cfcb20f5f8659602d3bd7487276661a754870f746a40658bf1f00535059dbfc810eeb8f8ba6580751891f4f13fa5f7a9a2037e67c602e8986fb149e5555d1db66c0ba72d078a0bdb69a0ee32d5e4eedc8148fc8bbeab4469c04bff49c5f746632191a55ea485ee6d5c9226156c647276022e6d6963e8b03f225315cfdb4f09ec54e976a148c7e328a4328d84886df869b94298b02d21e1564c49ed8076bc1736576f317085040a4c2d4481276f187e980d4f3ca7484425087f5836ec12f34ce98e107ce0923206204a0cf6c51b43bfebd0776519c886fb21c1297637c7222dcf47a44c80d618ed7e144b44d49c7d0b5f9058cb50b37de66621fa34da89121957abb6d984e013a73edc8e971d7664cb9818e614dbe29dd692a271754f6e191a83a142099718df86db7cbfb7b41f49a62c76e0c034c0506efc9d5cc932272a2c9b59cfc803b673e8da9d3501063925886b4b56adf6511607ece176eff08b47d262965b70edf97822ebdb875839a1d100362d0103a64391c812f078199bdb3107470b533c014b85426ad548c4159c51e84dbc572f89a4eb1c6ec226901638a1848aeeb28027f9ada36685846e91efa773447403acd48a00d66b053971cdbd87380b4eca5125b25a8af2758d6951b74a35b12f677ba3e6b6b6d5c56c4a88a98426d1b9eb0da88bc057f9502562dc4fa4fe73685ef318ff32c36673fa4919f965fc3e0699216d18d60176c5a9c7310a6063cd4ea8abfd57944bf1ab3d8ba865f69fff105f6c97cd7e8c11706aca015fccbfd1327969be2aeea177a469c19844d2ac92051d0c99063de06c19c2ca7b0aca1a9edb86a3595db02f65b962807c3829e80460d72953ff07343652cb5c549040bb019e2c1aeb4d597acd6b971f15b6818abf35cdb5f2553b998491205742cddc6d47a387221418b47fbc7e1cefe919a39f8d554a058b08ee29f175a095212f2d41204b1b5acfa5de1677c6af70d6193f830e10d438fc0fb31541101244f50b38a584d30584ed4d406c7f70759faeb81377245495f019a37e2da5d89f60f8ddb537d3661a49f7b4d2cbb29a1bac547504dbc8dca5e3ff3c69de044f2f6c79a9d4106d9a46077da4e36260bb42686e84fece4e7b6a4766eccd21b03f2309f24193184bed4c6e390a8abd8a70d5417970c9290e2251a40e8979ed02034fa657817c9e3f1563fe98ecd83a6755e027204f5eb02b8b7ba9e42c132594b8f77a2dee9061001770364684238834f8543b8c1daa4a03f0022cdb508a5a217dff39afa2cc7d48cc3b97f76774bb2d6bf9c432776a5acae2d01c58b699cb023bd29851e43d0166d6d97ab030ade74aa9b73e1734909221fb24c55b957ce62971252c2130deca751e402031794ec5fdf160496c685c1baeec70769b1396122c46e1b4f7c9590831df02dc264052644eca7d0403b3aa896fbed9f5f0bdb316521732ca00b3fdee2dd26ab2b9bb9fb993035ddd2eef59ac864505cf88f8c4e4a4ca5b3b279210a75f96789f48f5ed6a1f5a403e7a749bfdfec456135435dc9eb9f638a61033641560d3e14e50c03bd55a722cfb595cbc73752bf2fb0e3943709e622b649e1bf32667633b2edf77650f7c6729a5537f03ccd3607ee9d379a6999c9b133ab0699095e1c07349abbfb0556a354ad783134aaa5ab7f2e36332385d56ff1e11c45ace283d8e58666f4c335c9530b96863dafe5ad75e5bbdcc2d681536b888b87795afb11325ae6d8c6a2ebf7de806161fe42f3a7afcc289dc9777ef9e4f8e2dc2905b6ef8358e47f85963c6746ffc57c342f26d33544d073a6e7cce35f6fed121414fc54440b02dd9c6885363df63d948a479b264a2410041b06cd44255ba6a53f6a0b427bf7f2a04ed6dcbb5901b4e3f52e9167aa193c0a446e64cfaff9d639da55d4f433245929b1c5482f05c7d9cd6bb0b049a46583c42e2a7ab1d5604420588b921d50d38bc5b2e8a3b920969e2b4e5371e09848a9e4927b5dfcd5b988de333ce4a618f6384e5b24e874b9e238278c6202c2fc47d25e4871beb84b6d308e0871c7d67118692e72fcc9671b2f7187701d44643d3dcb40f257f650dd2945f8f56fd5960b71b206d1308d66d39987da9ec6f07c36226bd01b061378a574f728ed7ca964ed385eb8c99d35cf20781cb88e5f20c91969e2f8e5bab707b0a21aa21691121f6a69e54249ed5398bfa51e4551ceb6745acce9ec984132b825c085018f072a6790a0c956d6167423710950b3e338e23773a3f2d4a93d458034323789d22f9ab954480b927254e715ee3dcd0f403b116aa31bf4d07ab5b29dda079acb2ffa7713e356eadd4709f672eb37a043a21aa7a204fbc77e6dc7d653878eb1ad34ee2bdccf38c221f5e7be14dd27424bd6a59aba34c8dd4fef108e477d88cbdac83ec55715d02eb5acd6a98d2a6235e54b33cb8bd2bdccf86cc156bfe949186423da9d9455b46eefa17285ba1f00e42e6cab008107f130ccef64322b89b0d57d3e51b856d6cb29936f4621ab1e39476cc690710b10d71771c47bea224ebfb6794c88c50e11fdb9742f634b26d0a94335678d2bdadacf6b54249d0bc12b4afb542ced06e8d658d1cda2c362895b4a37c5ae6e181d888dde24ba2b967613746b2ce966d16db1c4a51ec294cac8a8f544e1da58834731a4aa910c60ffc53b2dde83edcc0f214845c805fb2875fd4aefedab573af7d0b45c9d22072dcb508784072552ee9608bb4b425194e7209713c992d4249f73b5e947e32e139dcbbe4f8270e36b5a4f470db0e67e48d79bbc64ed26abb4890d785268ec800784e5004c6d95688214da12a315be1654a9f51a0f6360062a35097d9509bd1f1d5664a59930f9d126cf95571b44a89e00c664305ada760ac26461c3e8db02f58da859013f52c07c062a26b847b67c5ce669e91a0e80161403ffc0da2e1a522e6bb03fc68a60ed5ba1e2784340f90fa43e78132748d509b3a823571369d74dad1a52d8173e93f18e4d3943fe434a1475d0a230fc9a85f42ff41b7a80aea29bd059e81adae8a2964aa5ae525aa928b596a4280bdd864ea0abe82634ab4b2b354aa9a552e98aa2a10b742b9ad46595da4a224a459bba10e79c42477095306f7e1ef7cfcb2bff5e291717a211d0d8fd9a27bff2a438a4e9b83f8b705a128bb4591445300bf6768ecf27a7bd29422f85ba50a7247695525f2b90c86114b2ce08273166bf6d77143dda681488412970748cadd81c9a0a72b2e1abb9d58a1edbef9d3a3a355be466880a851c1873186b42d9719de885d29f4c3eeef75e725f559ff1e5064d19e7c35081d9bd02e5055b4c54fea1125aaab3045c6828850a202b9be4f07ca845e6545d65deebf39c11b459d6169380e016005dba69671957b8d6a5fb08bd4d1358a5576f301c0915ac3cb12a658760b74f112f9b1abba40d598472bc70541b1a18c65ca2c186c1968b94e385e1f5c05854ed767d0a8311e33bb62b5848d762a9e197624a7f919f2ef09c79f321ceab20205df491499b43f287afe641d233efdbf9e31c6c3ed034e14084ce2ba35e16ce075d3dfca146bb8850822585e6bda886c1eaf24b08de2758791f9d5707d5c711cc83a068c8a1020306709aadbbe5f66ec8317d08aa2c401ce030f4e5dd101878093bf57f847e2d296dc27e832ea93a1be75e1daa553aa4843a081b217e8e2b13c2f920037e7780a6da5d9e8e771c674a3a66cfce263a5f891302be59d60280a5c2d8e92c6bfd03ebacb091aa043e20536cc5493ea67f8b59094b8fde8981bbd45369835a56726a902d83af9603fc4de2cf19eae53f3642689b7e7220cb82f13a6afcc6038e038bf8542967765bb5f20f5e51a9a280975b0d89569f4c639b1bcec1083d739d0a0907ee25945982b51786d5b272b630ab65caa3222ba04192ac89446dd1ed7e440e01e92e068020d7aa59af47bc9c93ee26da6ebd1adb642dce0ab29ce9a52804c3fd89a0a141d2b2269b9e91a1e8ddb9415e99afca280a8e7e732af1ffad456e6c2ebd45f9af6669f6d0a0961246293e1491fb6bb9913d997d0342a713abfe86c9051382eddfdb6cc2b81cfaa6ac86ec98b796a57cf28993290c1a7a4e155fcd454c6b53294e154f0e6d0cb4dca7843fd9a61e5e0063871e47fa6e7c1cea8d5c83aa52325252f470c7d84e07262b046d2d97dc5ef2ef874a815a763703a627c6e00a96daee982174f6c515c28ece7828c528d498996816af9760556395a213fce64981eb15fdebcc2647659279e1f0cc9488393c3f4c0b3831b26d002dd005fb345b1ab79de85fdd79a6a9b191f3583f4dd5352965f617cc2cbbe39b6fd5405a044f7eb5a09ed65ddbbdf41f37bcf81a13b305ea1ab461a2018fc062bf310980f4609887fb46310b1713ad0f8b1d2380cdb129fcf96c13e2677a4e31724b60480375ef6818fbec2254545d6ec42a8f58293b484bf385a4cface27ccb4597f2e25b86cfeb1c20f22571c2e63e768d6fc505948c924ed631ba3451408189d092110d2283dbde5005ee4b2b6936f195f53d98c414b9723cbcb63d42688256503448488b038dc2edbf51e5d456ceb539f1b2ad507c23459ac9d6a469e39b965a465f2e86c435af6fd32172e662e579b072ff002fc32095b207646a27743dd22bc89ca6f3f2e53d5341bac2c815397e5b442edd5c231609d85dc9c151eee5399f2cd974a73181ad34c8764d90eed5382fc1dc58370d57effc630a301e39de2ee4cc0ed26451c17741c69879f6871b9dae4aba1f72c6f5a279e91d1b3e70dad9eab2111df1ae454f72cf0a5adb3991bdea66384c2d846ba3a2c31c87ed3507676cbe22fd7cdd041e427d902a4583ccc8406ed34d93e990e9b15a1cbc0c256c6dc3e166393cb47417eb88c2539d557722668c96893292113b9e08b71c0109d4f4ea5649759a7dc0378e0ec44addc948c04b395d740b6c2d1eab4b4fde135161f30aa6e762894b3d4d53a4044551cb41838e51296539fd0e697123370cadc6d832a785412f3e35072d8c9334fb57de398d3ee0569f58275286026f9993fe004fc70bac486cf1b2697a9a314f440361fc298eeea6506d8f639a9cb369573deab1336f31b0f8f7f24b49faa2fa3c9210d8e843ac39b7eced03726dceaf7a350f5d5a7973ee723852c72e2fa35db66929ea92534a975cb45991974d32c228e228a58e9ce451b9f57e9cb51ac66e943294626cc90059ce093bbb3a20705a49621ba7f455b241e27ab9289f08293fa172e06cd56aae43c75a8c2733875f1418e4f2b1498c9d26ff16e23ecc93540c700daf077737044b06b19e591e34dfa643991030d9f95f3beecd2be18e644b8255b1aff366b49792b68e4b465d7424801c974d321183b3038834e37b583f854dde9773c65258b69a0bcce989e44a3ecfad9eb52eb702b38632e873b77b56cf95a2d191680261aa9156b2fbcdb2c715f5fa065705feba0257bdaf8603dce4c646ed5350668c6bc68ccf1123abea00940702ef9046170015b00a70b68f00849e1ecc15b8f394e06528d1e92c402f6a5bbb4c1351cd88c2350d95ad31a7408fe7d58f991705668ccdfcd1a27c692bbf23c64568b129061325f1aae0a8ef7e92ef7f53fc54822bd68bc3bdf29d33426c7337e85765a4ce3246cec445b9b36f13f6b26fe77f00d88f5d6f87ef3290fe1cbd17d6ef5086450f7e4ed673375cc4f1af5427fd2dba82e31d9fb748a84c4bd7f455ae37e4f9417556acd1ac739228711546258a2b31cdb65dafb24672c9ce6c991757f9f66750e529a1833c3b6bb2c40b6a34411ddf7527940e4a5f32a645edeaead5e842a8ed58f40eff4ed33dc485aa8981970fe0c61619c99bcbf3abe750c88b10463a159482a8a531a81b0a3b254a464d5172b6148121afa6b06f1ef7db4bc173932c7946f7d42931d8f2b7eff691f852e1385c45e4c248da71cc29d1ed4ea7e4ffa8ee414c671d67b0db054db794ca98338d59bd38258b38fdf3029d823b672b0216f0902ab4ab4cf95a37f5adfafa5fc507ca32d3243192eecc30bcbb5875c9cf0005de4742802db12c757ee1132024a1685ee8e593ba771673718f441b06830ba71b5596d8d9af5e458a96da6e815056e268c0c044c9682c052cdfeedf1cc84ce4a3194a976c140919bb987b564473f81449692624c3e991b814bdc816e6071ad70d29950876479e7783edfc4d0c3324745867b857fc4048ee1da162ff9128b806876d034cb902a580a5df95b1a0cf2d650ce995145a2a5e4d2d6a1dfc6d97dda676fdaf5e79e795441012b06d3e72c0a0df6a154be4f6a3c945502186e1aa77f81f56ac79ee944fdc74212472f74ae03df733252ebcd6077dfacf443ba6e6f0f37f4686433165ad11af678d61806abd21644eaac9d85cbf16b9ec8d5ed39e099161de7791a66bf3992c369ee0099a4f6308d5c0bd5243f5ec671247b53a780f53643da83179f27cd655666f4fac175bf0ba4ec9437b5c3a485e727f2f493a5a670ebb34b0fff07246c5d00a5c3197334842a70055504abd3e54904351496e541510a1636d608f4f63c3e83440c12afdb39ae7bfe9d9422d8b365c0fc902607b13b6e82d71720d9a6fb45654553f19656e8cdf222c0b0293035172b669ea46fd8f1323b3064c7de0a8ad94a0f5b6e0bc1308c9b8774c5b4ef718fd4803c678db3291c89f56af0db989430517536ea7ae335976027a8717fc100c1936703c400d6103c5673092120712f1cd24b9f186824c4b0c42b81c4f6fd389e8099eb66d8949a0aa872c74d85c1a8eda6676eb8dcacf08667e0d82a1904810d9bafec2e6489b518e6ba35c1a5f8e7842be1a2f4779fcef99dd7fb89ee5c1a0d1dbcd3aab940eb29b84865be2a60bfb627a434f53b5b83a854e704ee0b8ee86b526980a30b36f86b01d913346c061ae6a9031f1c055dc216a26ee0920b868cb584baa46c19bc4c971d2092c9ce48614da27c15f4c77508a129a34fcd5928b12929f921f7e374f311212183d0aa09069a498b8966d297b91b0e838738fb2d662a1b6d6c190033ad69a92f58f2b8bc9397a7fc1645dc7901d9ea51d16e1220052a7803e58ec35ab0125d013ffbcfc95d303c9aa028cfdad92dac829e245ed32b94812416d4a79b2ba5587d46429b488cf2832fd3f3771f9c500bfb4699647a1f0290c5475b738cd30d449ac62f361a3096a4a5a6d901a895da1f982d690da9a63c79411bf101550e1156076dab504a7d604f73c2c2f4bda5c19ec137f4c9e8a2a240f14548e37b16ffe509c62c180132a2a38aa5093963840b32013b13eea95d1973ce4c216b4102ced405059a8e29dacd144df34cfff2df89ce61611ceb03c903ec9ec59ae6402b0bad4dfe3c829d9c1feefeae1b12c3fb692ccaf088f8f1dd4ba11a2dc3a406f72b024447b14244de142e389dd9c83a24304e6520c762da41a09ee58c19de18b3d78d89226c303c65dad3778c6a7d80320ae03d35565515c4ab27c546f1e24eee4124594dc8755951e46add99e823a449408bc803b42517215801fbfa4a4253be2b3aa1544bde71cc32f2bc5e5fe78c4c54f3c0686a47158352211717fca64713428ac6e1f66dd3ea397878d283dbdf0792d762e04f9d711a89366740a40268b1d5d13aaeb5c283af2dea84be9cabf063b06d6a7c2f02143bc4f19ba569acd3a3285ebaf051269c00e66bb44dffb89915aeedf4c251d83b09d9036865ec86f6537526c8b5448b73acd594774d2277d92d85bc2e97d106357bbea24b591ae8dfeb360ffe15f49f0c1c2cdc3209c301ddfe921014492da429c4e1555e31a783d8678e446859f9f00c74afcbe6a7f01e6fff817fd3ae0015cc804da2cafa6551048cc381d80f3b9d4d11d834457e1d7e0b20710eb5a011870b10f40ee0daa4462f51dbd4a257f4ba5d922fb5fa146d2ee26f79785fcccd446386708fcd3461013450a43b88a03f31c750572ad774df2c66d8cc8a6f209240a630a37980c3329dce0055768381c197a4ed889c243101626d0462fa1a177d874778d150cf8418b09ac34571ad30310e39b490550e042c3278a93328ae8ee965118ada4e2d3eecb28ec3e49d2fc1ac749c3f7fe482b4792ce70cd8ee3c7ee574a13e7fd71ccd65e1274f75173a5d76d01178e464ce3741a4d33823057774f698e262729c7d349a530c71c73d098c0cd90e96e77e2fea53285d97477019a9b594049b3a1122f2755e55643e241c383338575d3e8d0dd33549c3feba699d17c775f519c4bca1ddc4c0d3753e266ba9d386a27d39eee2e71dd34abeea6d25c5fe91d1e2bf196783929b1ce2b42378d0168a0d0c0b0c20c9019ef5294520b073428908302d7629a44c5d9342a394af87ab34cefc4c1a4f590ae8d79b771e68c21e4d0f87a4852291a1e0e68531c20e81966f8698e0aeb0d8fd0c33ffcfa3e531c16888304c1387684208710da10c2170d85e54df34692854d6f1a2a4a414c572ce96ed40de6953ecae84ed60c53a22824ed0a8d629d42344c8966780a61fa53fb95687d5e76e790b5aebf41f34e564f3dfa7a748bb0bd8fc338f27c15da62dde6f846387f0b7f486f513d32f251dd609e1fa3c8af9346f192b4b04fb23deee171de4f7a8c6595624c892ed1e752a4ad1f6595b65ab1a25c16e12e6581457703d1a61f04a16f8fe4b61afd08e5b2487c1a7589d7fafcdfa0bfd68725ae8494247d1ffe249a298d5eb21bd08331b24a6dbac736f5c086af579f269b77a6832485b1256b8ac434329a588b0fcb250d5f8f46b3a19829464aebf81bc1ca96f8a2c823fe91910f0f986859a5a8988d669b44eedd72fe4bce221cd5cabd1f15ccb16d1279f7490fc90b8ecb91f41f95d39bef8fca6d689fe98cc14aecdd27438a67cfcbc09b6a69b6341f6958cbdbf848743cb98cc2328aa766dbe7893395d9ec6e045662236304acace14f8a2a9d3dcc6da6f93ea9f29f6b83bed68ab25dc1ca95f81f94edaae5aa5fc5de20f1c57fb1d210bf914a346af510fd181d89f933b64319973fe2d7ac588154d8b34da2d036893c9af1df462ff2d5667a4b9b34633eed8f3bcde66c7bdcb3d46fe9f965b749c3a5bdb5ec8f5fab03597a7dd27eb566fd96f6e679bac56e452feded6b560814ff5529e87586f67e153f721bc7f9f7484ce5458a8c8070f0158f90cb8d805c3c40463538508fbb5a4744eed527edb4be724fac288c5b98dec66a4110dfeebcb9522cab769c20e818856331fbe2bcd967b907042d1e67690be02008ba0fee8e72d40e8dce5ae4246a791377771edc7fa424cd86b6822008e42ab8c599d692065168e0a3bb3d1b1bff44fa42e1d85fef6edb263522e07d6ce25cc2ea87486129da9b2bfdbe599bd410e2e1520771f2b88791423be42eceff91552ad6276d4078685e20f181be0a6de16f3d4bfca027f5ef8f381689229dac7981d038ea468385eef6663046cf59ae93dee9338ed3c7287f8bb476e57ad5fb48b73a81dca332c52708ae10e4de2c9c7f3ff63fde2dd35a5a544ecb3dd40de6fe77e639fbf1ae8dfdbc40f759f849c71eae65b2bcb0f992551a96e25f70de27ed252b05417f10f42fad8cc6aaacc654ee81af943b7110e6bda7720ff4eecbc41f021d05da5ec41f88f2944f01bd526a83dcbb51f76ec023b649049efc063c726d1078729c7c677e5ccefbf9659e55eecdc23b1ffbedce4a61e56dac799ee423392e023d461dd7470ae7bd271c9bb79f5101bf8f089c12d61bc613267ecdce7c92781ab917bb19df60d582549cc94b56e9bfa680fefdbcf37e8a662f494e9af3b8f777fa0da695e1a5a1dfeebcd9760b63160447b1ce19c65494d5795fb444392af76ef302b52c0ef59fe57c23d612ac350bba38fec9bd8c5f26a33007af75816129e2169e17089c82471f9f3355a68e55feb38c348e23d2740fa4e2284ff9bc405340bf7ff381422e16d0cfab004645ae964a08ccf7baca66f7329d79b52f3a6ade77db8bb73afd069b721b3f4f2a3e8d466f318ae7907bf7fb40bf9f64ff5a22f748f0e6e4dd9cf0ed91be0f046f636d96692df517357e2cf6d7c6fc4b7a73bb3dd20b83494f6f46e1799d0af87d33dbf206708a639b5c4b5b451b5672d25054ac9f1f63b2666f1e366b9db553befd13f75e9cb437e237e29f50600ee836c754a3bd76163e76a0209e564a4a511497632346263989dcc3377e4b1bab9d7c9c333c3f7c283e1ddb64957b7e1b6134e4714f7cb1dad07325720f5f3bce17697ea26c57781ce9cc678ab307d749b67c8ec4cad3ca76856345d6352f9058cb20bb12abedc12c9e6c57d9aeb6006d5952a97b410653ecc517d78b097811841756bc5872e398a24efeedd3c50eba10c3e7fdf9230aff2d9b4601dd4d6b531659ba6964a079f17770265f856272d2fc6b262ed8e8a611bbbb8626e482092e14c0850f5c8cd851f85ab2ba8b262ebabb66ab631f66f878f00f04bdd20f088aaab5020a02fad9428be721e13b9f24278f7f33ca1638ec785bf8f89616678ca1c5175a3871e3959edc2b75163c66e7fdffb418f23c5d8b153c8b37ea1c497cbd91a4d12c7c4c2190a1bbdfb380c005c43815ec2e2cc43416572c49c2c15992f47ddf7785077a0914eb7a22b742dff77ae1ebe5a452df87c910d8d0ddaa29d70b31a5e1e01053dacbd6a60f7cc1381867167ea6d9d08395b34c6538381f26739e799ea43311ff96a24fd307b6f0400efe218514e98e239d8562b5300ff8e86e2c49ac318ef37fe68789036e74e3ebc1521cd8e96ed5eba563aa828a55edab0e0bf8507340a19a3869e23736919e72ec873ea35b94633fe4ee5e73e0c16d227dbd4cfcd70e1e3b46d82143184d765cd051a6348333ba2609980e225324e9ee176dfdc4f72f091d4f3569e2decbc467c20126296c30806d8a020cbe1e0e3969226c618ac287c6d72327cd8a65cbe70a4dd8ec71d2dab936681ca7cf387dbeaf87482d94efebf17ddff7627d51a4b31725e78bc4f8611376edbd562c31266977f6d2f950e5a889df090c3a3a3a3a3a3a393a322c89010625495e40a2b3337776761e367b761e3661d6f57d2b389ebef31f7b25fd25bf2f0b155960598010610222762062004388f1be5a9ab517fcf26670b4160682dfe7026f30bfb6e8ef574a4381374fb32dd031080e9145b737c42c1c4248777b54082e8480a23d914e9cedaaf208418410a76a6f75e6fcb0712cc974fbb5b1283e4558ba747b58528e429a5fc3328b8285863b1f0b8fee927605cc952cfa0a11f5b1acda1be58a5f0952a78308a0c16d9231274eee571a2bdaa94da31d9bed79d5e6c52f1b0262af20bad4d21404134184ffca8f9dec840fe59d60db24c22d7c537b1c355a0b3bb90e80c401c003b8a340f778ac98d1ed59b1e2e2b5b2d30d440f80a002db64fafdb56108440b0822deb535db4a3d759b363f88f1fc872cba7f384276e70fff43906e0ff4418d54b5f5e455e50313ed89749c3ecc72e90311db8b353cf5a0821b2fcc5f4b474dd8a9071b257b58d23c8069f7afd6c4438b072448f7e181ecf678106a0f8a657995515c854c7797978a55be10aa0236ec5afc235905dbf87a295a684b7ddffd172db47d1f2db40981012122d0fea1fccb8fb1f8585c58680873bafdf3be965eff621b66fad7e66b48ee51a15440f1bf6fca0de6282ae08f227d559b678f7b54c0fb2cebc2f302d926116ca272621693ff4f289e54c059a91bf9f28d04b1421042fcb38e7271c6074fc37f0024ba1b536a7b6f1a295caca6bbd473749707400d803bc08f8c2df9334c3a0a0a2618ff688595283e2b51ac1059b9b182821522dee485f226fe42e187620a4fb6ab719c3ee27ca16c57e3580665bb7a424edaebff66d8f77932a47e48c10f53e0bffea40db1e7f0935e48f36bdff7121f697e946e1a39ba6986d04d1347378d10babb068ef6ea742b827605fa885f646355452b896cacaa90c2795d2d28a26a257459f87a38a99758e70bdfc279434b7bb18716ba8b54b12bf1b35d89cfe31ebe30bfd5787a8cc42a495af5238d95a85958bfe747d8e75bcf7dd6ccf3c7ab969234fb7fbe556a9b73d2e67dea81954e2210cfd9c7f2c474d66edef4fcb7db2c9c229e421f54fac8688fb52edb8b19267265097906ddcde505544ae481d76bbfc2b49a1dca3cb24aed0af3e8eedb78d78821861dc4c480af97ad35aa4da3fbd9bec69c5fd5c6fe8786aff77d2ca29f3992f87a2285a5ec7d1c5424ed0ad92611adacb472659b44e2db26d10babe6cc85f10fdde6f83f4996659b44b649d44d23813b855eacd959cbaeec7d1cec7d1c82ec7d1cf0f566780addc42565ba9b0799eeeea6e14110babbd291465d62e5c9f8938a9020d45c0f1534d7434a7323a8d1dc083331cdf1b0f1e8e64420a239118234b70389e67678733aa0e074e43457934597686668667600496107023a89233956142145036ac4b084088898f095617b018c154f5041c5131ce8a691a28a6e1a273610451354503185145140f144378d13dd344d74d368e9a6d140374d06ba6930c0bdc6702f303417e8a65982468917182f2ddd48a11dc379e5057b4971eee5bd84747b51dcf33c0db0836e6fe6ba397106c8a2862e2dd144c98952144d93848fa612032a6674fb2c53ffc0042e20c6a9a830a54b19e0a810a552292f895ef24517702d74392431e37218bbfd8b2591399cba48c674c87fe69ad246ff486735b2746e0a14dd0de371efc5295670ec2f3745ca14af671c0e26f059ec96290eef1e0b9c32c55154c03c6bd365773a3ec2d54e9f13c83d6c43b2a4b376b163fc21be39f9d7fc6be3e471ef2f256d99ce9acf1b0cc855b42a5118f3b8370b33ad619c6f29ce9495f9f45ad26879be406fe26496e98f6a9277e6faee2539be4fa2c4fb36dfc6e9a8da4c2c2f19c53d7c29fe1a0cb3dca35191a278dca372eb79d1fa80537cb0a5dda44e9e6def37f53f2aafe3386354b4348f59a702fe2c9c3ee014af45a17b182427d1670b2596c402e9486d25cf9c214dfa4a3de55f55f98b367c1d60652b5b283f45dc100fff6cf61ffc6407dc932c50fe656c594faef41329fd2405ce063036a080b30109ce861c3aff751467c34edb2080af42b18a0c3e529dd1ed9f6d1241d1aa2054334ee5eaf6cf05c7fe7d15da529d542570426c74b77f98ca14210f74b7cd294e8886eef62fa77aa17b17ab583c3640443c464199ce329d4e6e4e20ceb62f8baa2ac808cac366cf0ce580b80072a2dbc3b5c7e7220a4c38d315dd343574774dd35800db95f89ecdfea3f2594efd65e13c91dc9b8ea188f5285b965de129442b8d5e365962056279b711c4df874b7babb7f1a9ec47da977eab739c59fc1b8ace7219dec4ac47058c15d51fb24d22708ad3db384e6cadcc79fc65d3e6974db286e78fb091de60572f9b2cbb32ca76c5cde0a35aeae3840d5a918c38275abadba7385fe85b6077a39a73427ef9b77186ff06714e5c68f1fbc23501e361637477acb92643dddcce986e5ceef04e583b3355d0ec74f74c051a566212767874977ac031814017f11e7a70f1bd8913efa107bf3961eafddf181ec7e9f37a79781c27acc4adc38a0fecaf846f8693a18c0c626450c39bf8383f47cd88e16418298084873f69fe187e928b3f71aed4b2808efefb422a7e3d12c5bf254b7c2e06bb04067086a710e8a8e9ddc0a0e3b75b3ecdd4d36866d92651ccde9c3c77c191bc0082d7e67006d52f672ef740f1667bf2f1ecf1c447026f6260bd4d3c6f8f74f29b0df577628b63e3b7ec4aac472cbbfa1136eb51507ebd5e383772142d4d7ca3daafca964e087412d0dd2d20d61225ad168dc590dcbb652379d2ae0d5aa2046323be4449abe532f76ca2c07ebc47b221c61336f31467780ad5f2c5d19ebc0fb7e03310db588d51bf39f9e37106b9072279a104c1f2badc03dfcbd88eb89c65b715046b1ea3e00dea04e2bf311b73fc98d6390be91d537926a3a3e39092168794e471e2dad9719ddc2791834e5c3bf869e2fc8b4b1d6236954beb56e6d9569bed71ef762ded69a94a7d42b1ac58a52a2f1de58137334fd1867672f7f093932897e4382bcdd6e7588634cb2649ce9f7d23b5f771a876e5f353ed2a87d6f52aebc2e27ff864eb6973153ed9d2810517a44777e74c6086e76c95391b03dc50c4d9ecd8e0d89c6c52b0e961c3c346846ed2fa0d0b27ff6e587047b1e01f691dc5829fdc3f14762a535a28836bc18b6eff6e4eaf7973a5df4b9c3f7bd186dc0a5bb8155a708de3f4c1d436896c93c89f465dde0dccfe7530f494eb80270743b094d1184da2378734633a52fc30a01e9e1ed9bc9f34dd430ae985f1c82af5c9788ea8986d6fda8c3fa44fb3db3c5dfc9c8ae5e4e8e009826ef309e5592769fd4c6b56c83d4c478c6ab977832fccbaf8361abb9136966dad51c015a8935a815f6789025fd58237d3b34d3d553a47d26f77624bde273d46fd26366f2ef76e13c9de9755b0058239e02da37ccd519816b9070ab96a58010dbd5c2b20a21a805aaf5591cae57364e46aad8a7c5e46403fab23b0f498957deaa3b8178bd9d4fc286eb40aa787635626fe58b3152955a90d53135bf296e4ed4e7c3f499c3e6ff0f87f7d3a9e390a877446ce2bb399bcddb0d202f010d1ee67cfd7ce7039c62a95553ac26853e6d7c65c45ab32fc96abe0de915cff2bcd8a75de1c3b3ef56c4fd2192ecd84a0b59ce59904060320f1903e8c1b4286f723681a1950ff9a33c174165a1288260c4ffc1d9cc92d7f6ce21b2681a1f41875265faddf604e4b0243e978ce88e8eed91c9008701d190d663bb640c7d7e338305eaa529c31dbfab0d9934a650be52f2b96c4c2d7fb6040924a3d8c0332c4fd3001f7630aee8796eee67eacf02348b77fddfdfa5be2a72ff64c1be922ed9d4213a087b490f6d140fa4797d024748f223a4637cccc703333a49592068c8ea0610a397e0540a52454ca192b76788cac200a1bbcb91a258e4cc067a4580117194c53ce1b33538a08c04700bb1b498321a3892045388d949ec9f2c509424960b81a214490c4063e4840c90c453485992c5f74e16aaae0a000483a4447886c516688ea012a02ecb0e9191370310a6968ae860e99c9f245090c5773244408089321d880e4e940bad100cd980c14e00757e342095c8dfb1881c8ea32d15ccd092488c0a347ea4644136faea64796d902da0d0f8ccd52a15921c7278ceeee1ee20caabbc56e1c7e10a40a0dcd95c804377819a179704878680e09d91c12b13924539a43c26a0e8950734868680e890cddeda36b009c8e68ee84a5b9d36dee74d4dc8949732754770b216282079a332189e64cb8d29c0923abbb7d98482841473747421862736148692e8c55736130692e0c9ce6c260a1b93050682e0c1ecd7581a3b92e3c68ae8b0d9aeb6246735dbc34d7c58be6ba64d15c17269aebd280e6ba08d15c171e9ac32475917486593fce90d659f8ae9186b5b43e3fb39016da706ee410ab1704b568ab38edd85d5222090b5400caad0300d86d45f2f6edd0e96e2adda120a643618c0e852e1d0a18a732256dd8a150e4c806d45da280f822909a1c744418d0e38748648a5653b19bd411396affbeaf3be188eefe1212e304241081234a4604d14800020f28c201a5062880e60025069416d07d73aa2962c8986e24dd0d61a2bb1b92d30d39754352e86e17ba1332a6bb6f4e3727dbdf9c50379d90d907bafb87eeba18e0ebe18c239d25e8eeb2bbee84413c613b7426b0bb33b1d0dd9e37d25c8e3497df87d35d10a17e8c64adab4b39219d6109a7034244770744a803524207e4ceba1f23e86e9bef7bd91489eff9fd5c5a7722aac28702fafd5ae79d5130c644ee5d9b72af52599d620572140bc8036f588e529d40f74a4115c80253fe61bf819ddcc395facd3c91f771aaca3d679d30fd3e1cc3344f0cb2404f8d9407f5e5cda0f8b47b23c551af13f835292078f3729494138841bf36466b98ffc9a3598f1edbd12757fcf6077f15daca944687b08fbd8f83ab3e593423801788d1de0de6ff313abe68f30dd14908f4d4ec013de5ded76b3f10f494ff056f6e884e428eea7194387f7602fda321553afadc40e4f87ee80e816ef22684ed0da29b59015f4f4912ce48d2e88b92134bd746c0c1cd1104ec5efd71af89e78940a0bfac66c54a6bf67a36463e1def7c10a372a5d36dbedad40d968ac5ac4db5977459a5792651158b87c7ad1882d843c019020a2b30dd4d3617c70edd294e0839e8c62ce8c462761c674c08a7eef6af39d0ddb53938a248a5b27501a00c1825ca885d90a941f7b5f70229a0bb6f73647e96e08290c50e1e8cf63e8cf644a3b5acfbb5e2bf416f802127ed8df08da12fa0c84192c2a3b564ea0d21f9f5d4bb02409840372527ad7c1f63c4748fe9625228e37c232549df37c798d267fa832d1a5fefc5f9808cee15e641f282e883246a3e187d20d4ed1faa847d1fc647ed0506557a10c7e732d32856465910af1e2471f13f4ab62b3c8ed3a71efd65f58321427bb85a7fd8bc258fcc7bf790aead15c0bd3b4972fa9123b3f63deee1197076d0f3b4b9128570838999aa748cc5446b6b18471e548e597fcce31e069db87644704eff1263ca0396e17c6c51a0ed57e04867ccaf7ba328529a8fe38cd566e1e441b28e6d441e1abac30c09d3034c0ec0ace1df8d7f27f7cfa9f837050727633a64c20198ee1496cd04000738d5031b5edc60fc6283310d470fdd3dd3830d6aba7be6f9c3c6c5b566eb137cbd7c3fa9891a2400d53335106b40430d96b8cdfe7969540313d608b3c6083cc7b41641f1ee357237598f320de948da80d648a2c23c2125ad2b7f4b56e948e775d1a00d914ed6f8493f445a170d92ba6dacd24e428d2e24506388a6080b92e8eed4ede69202ba4b09e8ee9a5296d2104f6fee346669e45012a2bbeb57b17f83bc5889642dcbaeb25df51b221022828692ee4ee51958e9f6cfc6aa6a064e3a08dfc229fb212cfeebb52a52b58c80a0bc806ae0312a72b55eaf0f5fcfc66cac529c970c86fa5bc18c6e9a146210b365da0be3fbf4de092bd2dd3f9a834107835277a3d09c193968bf7f513e48d385af67c6c90c1b3352e8d6e2890b8871c7d72b03a90c270d45052e20c68bac80582c1e2137028202b4f29fa1a322a196b754422ef7fcda187867244d145fa49da91c1956705e48c2d2dd5dbad25a1dd9f092c24364ab5f4b5fe2f7b5408e16fcc08a656b2648b66289bb8a167423857684bdecda5cf95aa25855e52041f2426d1a892adb8b75963615bb4995a66237c928c808ca0c4fa1202328792cc9796b2b1800396fcd6dc69385ff836e46b71be80c22bac56876e1fa48d3f5d848b4e1dfc499ebb7c44f4199829f6e2cd6c763983e8bf38dacf7b35c837b5f1b615456cba097cd77af662b12121d6beec5acd3bee618e797dd49ef4cc24398c8478a7b37e338a9ebdc4ccb1b100625481cc923cd11f6c43d8c8faa0a29b443f83fa8956b76a8d5b22b71f6cc9e37ba9f34c2663d0a12ebb78eba69c61202faef0346d0a5bb69a1ed25d6c7277801770215e017c17023f638cf87e5db3941342db48dc00a1b3680a37480ee9eb122042e4e74dfc61c72664ca3f9ffc69afbe2f4858f2f52349a440e384a5b8ee81f61305a6340e2e5a460a20861705b58e8ee9417a417d30b17bfa5705db00185eb6288eb228748921360a03201213f4177370a615e1021071d38a0074a0dbabb878ede5103a561186341cc6c02c0d494910289110c00c42354c1c1a1224698729c7a4036808a6e1774370e4074e1c5c6853882d4506201a6fdc30204fef5e01f163ffe61e1a3dbbf2bce68ffae80c2d62b48ffae48826f8f14822f2110bb3b0e102ca01b042b7c208bfe80d0077cb47f1e30c3035ada3f0f2ce9ee0019dd8122bafbc57560a7db3f32382b462b503d72608c7f1c20e2c58115ba0a30b3e6aab055b1a4fddbc097ee0d14e1df0686dabf0d10a1e20cffa880c2c6a8d899824cfbf79a020c1d9a9b823545104e0a32fc93e2886effa458a1fbc545914547c18a4200fe7150a80145b77f64b77f1c144bbafd83a2c63ffc840afc7b0209fff013a8ee76828c7f4e2c003be1f2af09315c13593471a5dbbf1d5ab8269234a74505ddb9246dfdc4d933c71c73d4cc90e00225ba4af70e7d460690d08095eeb6ab1a0ad41fbd89132767f8aa7dad198367c6886362d65de2f17ad15624a9a737a748dc610656e8d6498dd35e9a545f84d20e0ccc98888309369830c187c4d3d1f17452e127e9d84273444da94433c30677811ebafb06dded43734bb4d15d9ae96e1e64ca3a456e899c76fb333c48284a68d1ddfeaad9ea54a6dc608e23e2f9a58ac5436ff6feda1a8ae23287f34f1ee847dcfdc35fd6b23fb6a969f330cf7702b98794a3e3797ed7e63a2ba55931272747279593a3e3b659765a893ff131acc49f7210836eeffc5ae41e9ee1ebbd5e111042b7ac84f9f48a403bf398751a1bcb98cd8e2758a9cf04a0bb939a3ba28a54aa68e627e28c40a23be349915adddda4392358dd5da48733c2877fa5c841608b96552afe87a3ad8f642facf6b59f4fbb85379cc5f96124795a3cf2b887e944d14f12e964fd07b93c6c3326291d49da04419256c2dcb6dcc318974c6e5c70261fe447dc05ff8fbd4edec4499a5ff947263724cda7709ce1ab89e71f7170282ae3f28220e89eff0d49f3c971ea36d6f2a656a0cf6aa9ecaf574bdae9363b0882d85a2324cd97261901c709fab5b14cc792c6e37f6d48d2ae90acd26ca1fc20857688c593e948b6c43bc53a738be6079a2bddbd4373315a378d0c3cc4df41c75d701d40670252df711d40b0c5620df98e53b0487c1e91f6609fb1759f857d5e56a751b6630b7bccba0a4d1cc4711074cf9bf6e6bf3a9e722639b8054be5d8e4e88814766f52b0129f46073222d677231497b694d15c5ab2ccf5ef8cb9c59ecb221128db159eacaa62054e9620d12977beefb64677fba28bd49aadf1514342111566dddd4e687cbddb23bdc6195a277a8a06d0c820d0d996d4f4e81ec7f942b2a4a53d8d8a7fff052848a0d042296518e5165a684be5cf36a987a0820ee3d05d2554349beb9cb15e2fffecf7e191cefc3f1f36d146743ba881af777f16e649434002ba1dc0ce8edd0e3cdab661cdb0665c4f0855a1855000b410daa385d020426ec2167233d4426e92747710213eb485f83869213e3c5a480f0d5a48cf095a48cf132da4a7012da4274a0be981a185f4b860e331f41019ed21ba2fa33dae719cb4d6bd41ae4a69627729ca0d4a3628b1d15daac1d3662dadd15da2811adda53468b8e0ffd804c699ebac7d12957597d098c119d7e6fb25198ce30c63509292f18bdd345b1ae7120cfa3e35a3bb5446774902dda517b8808c4e871c09d4b480041e9e351faf14e8ab5d79a5d786e284d1a1eea62e5015684d76947f347b63677477778bd0dd0c8c9915ed9f9dd51c3563402e6d3733c04cd5ed1f0a5f8f9ce27c7532b0c14a5faf3a4330a6466c8ca236fcc3e411be5f6b69c4b2fd6a26013341b060860122c696ac2ff22b89c0021874ba3fd23ff663b315bb505818c90b4740b77fc2226a65dbaf3ed31f5c4355cd0b24aa78c46a7b2a25bafd137cfb27b77f22de591371aea5c5980ed9d80c4fa15c5f28db7e8569a1143c85700fc65f3fcf21a36cfbd538d2d9ad3f84438a2bc58fe7cc368944550e7db2ed7f24e97559d7c726ae47f8f64f5478b4b6171fdb118b63ab067c6dd07df16b9685e705b2fd0aff643be2a719d3a18ce790acbbb40215a4600c31be784141185dba7482ee9209ba4b25e82e81512241abc4faa451aeb7ec7218d24d6143e5c5a8e3a7168fa2ea4745ef8b28d1e6963ddedf49629687c54fa22cb1da1e0f299c30d82d749bf1cc769569d47fb4a929ffac8a524649c1deaf92f3c4a05707417184d1fb359ce991233f2a8f5937921f1ba1b4fb1676cad87a37a83c4ffedd5a6e5bd6ba3a160edddfe702b2b1aa2ac2938e2f5a22fc1f94bf35c2e667bb420aed50be769c47375f4bbe7044da489efcadfbdfc2e548deb2752b7b846c40ff337fb4a34f7db2c8a7f62b927685de25fe1b2149fa3e5a28a51b1a3314a669a16dc83514c3900a6efb2756bafba5b3a49b4812dd3e565deaacf44c962f807ca0fb08866e214c46c441c487f6647ecc518930f2faf121e90a0006554516564638c1a5862949078a084202104400396105338ee0843b84142105183a2cc1a140091129f0a1552105093a2136ba779c7eb9c38baff0934a1b681a09744252babd70c6629dd04efb77857cd0d8743578a981033534d1dd2eab34ace5cd8ec2e338439c2036ae084aa2f1f5be2fd50551099ad219d7d21ab5a868254a5394a488c58a6acfebf562a9627f59f7937e857b8648970aa2bbc5daaf6e309b3d9cf11ce9bc798e344f54aa5a5a5fbc964ce5d88f36d323f96f47fcce98e3e937e2b527c7f326633ad6798295d8718d8d7346c7597ae2cded84c517451ecfab5e6a8e25ede6e39e97edea966b697b6097467b91781cc7e9e3e4eb4e813e1de636b9fe8fbbe02549f24f21f1bf1397962625497227297f2169c8bd6a51a2caedaa06c84905dded93bf25be85f1b48c7eba2f04dd2704f6f0389368778e3c9ef8b396955a7cdfaf8de1e0fb48b3e6b9cef20677d523ef31be5199689b441eadbc393908d24ad15f366177ca7c8e34d60a62a9fc5a2769a5e8aba2960d181cbf0f043df6e2a7608e2aeb9c13693acaaeecaa1eadec8f75c92a9d65bb9a3de3580689780a65bb22f3d3dd50d1dd5d122c5d1225deed915c7050543d7973ddfe09385fd582b747b2c17816529aad36fcf1be5829ce2b7cfb277675fb273c76d5e93869d005c6dae80e53c60badc8ea7ee3c74c00ba9c24bd442715d2fcb414897188003b1c237c3d1ccf072f7b523aefa7c799edc97d7cff40d08776281a3a14aa43a1bafd6bccf397751b478ad41d39c18f11bac728f2040a205e6cd071c3d6dd1d845aa182ee6e1e31cca0f86102102001e8dc20eace8d9eeedc48d29d1b2d74e74690eeda28d35d1b39e8ae8d32ba6b8304ddb5b181eeda58a2bb36b074d7860eddb5c10a32a401d00109e2440430a40538ca36c1f16d8263a84d70c8d026388ab4090e1f6d2ad3469bcab0a04d6544d0a6321f68531926da54c6016d2af3439bcad03695c1a14d6582da540686369551a14d654a6d22c383369129830c176d2233459bc854a04d64b0b4890cac4d6476da440605225d778400ba3ba2a63b2378d09d1132e8ce882edd1961059020270081499b80706a1310506813104468d31820b4698c0dda34e64b9bc684a04d6390e86e1357c484128876343724a6b921349a1b324173434d3437d480e6866ccd0dd1e68672686e68d5dc104f7343495228120002082085165448410718dde968a23b1d0ae84e870edde978d29d8e9cee7408e9aee68dee6a5cd05d8d16ddd528d15dcd0fddd5c0baabd9e9ae8600dd9584d05d498dee4a22e8ae444577ffe0ba20123417b44473410b682e08371704361754030f1f5958d05d96137497258beeb258d15d160d74970502dd65c1d25d96dc5d96ef2e4b01bacb724377596ae8ee127c002181041e4450e8b084a03b2c1ae80e0b04bac32244775892bac3227687e5a83b2caaeeb0c8d01d16af3b2c46bac332a4bb2b737477654c775770d0dd95167477c54b7757b6e8eeca14dd5d31a2bb2bb6eeaebcbabbb2eaee20266e881334370417cd0dd181e68660a2b9211cd0dc103f343704adbb83f87c69cea78be67cac68ce8789e67c8a68cec736820f93194bba3303a73b3356e8ce0c0074674609dd9521477765eca0bb3268d05d192ee8ae8c1274578616dd95d181eeca60a2bb328ce8ee8e840e052236438694f0230827c65177620875274693eec4b0e94e0c21dd7d79a3bb2f33e8ee0b97eebe3cd1dd9722bafb42bbfb0276f7e549775f60e8ee0b0bdd7d39a1bb2f40bafbc2a33b2f3fe8ce0b0ebaf31283eebc8cd19d172fbaf34245775e2cd09d170674e72577e745ecce8b51775e68e8ce4b9246a1738283ee9c8cd19d132d76cc5081e66608a2b919c8e666d0a1b91970686e861b9a9be1497333d0f083b3d2a33920e6680e88379a03024c7340c8a03920c628d5f0280901226689e6c424a03931b73931617362581c8f1d1ddd31f941774ccee88e0919dd31016304127a744a88ba53f2d39d1226dd29c1e94e090bdd292140774abaee94ece82e491cdd2521d35d1231dd25b9417749cee8ee2e4827654977528c742785eb4eca8eeea2e4a0bb7df8e02107cdf1b045733cacd11c0f2e688e87309ae38104dd5d0207a46b42a7bb268c74d70490ee9a98e94ecb1bdd69d941775a6ed09d961674a7654b775aa8e84e8b05bad39280eeb4f0d09d16a401fc2801480f1ea61c94d0a63062da14064c9bc2ac1166056d0a03469bc260d1a63053b4298c05da1486016d0af3439bc2e43685d1a14d61c0368591d2a630ac36857122a4041f417c943968ae7c4173a518cd955f34575ed15c2945736512cd950b68aeb4d25c999b2ba50039811b92460fda94860dda94c60bda94869736a5018236a5f1449bd288409bd208a24d69d0da94c68bc8109c9bc33934875bcd6127cde1244146e0f103003e82f8e0c185076de292469bb88cd1262e5eb489cb06dac4454b9bb848a04d5c1ad0262eb6367109dbc4e5a84d5c7cdac4e5a64d5c4e68131712dab4458e366df1419bb6acd1a62d2b68d31612b4690b166dda22459bb628d1a62d1168d31622769cd0e950d19d8e13dde950a03b9d20bad349e281460c9a432305689ca03934ba28a1336aa13ba31fddddf0467737f0a0bb1bd6e8ee86177417a9cca864d467eaa6850a91080000000000b31000303824188bc582d1805846a5f901140002609662aa624617894914a314420618038001000003000000834309920a57e5639f5b9345ffedd4cd68d6c0c19b77c8486438460023d037399665f13586fd89bafe7725a66221bb533374af2a36ca09872ee5509e6a6a6f3dffa874a5b882903e1f1321fe13f0c3740f129b618bc3c26748f93dca8515badd9a1e3c6d86e3a5d7f418c0179c25a6e37782e3ed2e430576cc74662500d984e006be07a1303b1a5e717515aba0b07ba46d37869e1302c41fbbbcb518c52dcef09e4a0f2845f392ad5da7d69a53244c7f867ac283be38fd37ad44ba7834423826600241d7c80e09c31bdbbd2cf9e81c99139ab1c0e3d260c5851617b96aa4abdd44fb71b66f204d3fc2127c534a688cb97a6c09a522833496f39ef1ce1ab887b90bbf0dabc57897a3bd2ffbdf08e3afd408a7f3f150d9c399de27a1c289a9d174e9940d4f447f4f325c471aa6a672a65d945ae4926f75a148feb47b70db0cae3980b1a162b7e172de00a9c983a65fa3b2260271925103a7e4b10e184e0aeeeecb814bcfc8c59d735940448efa8b0e52dd75e39cfb75503e7e3dc586ca985f2c3fb1dbcd965b4de979d108fc7d83dceb911da33ec72ab947640c1bb11f3712117a53f4fbf57d43406b1f6aa4905c1c294bf7bd502b784c9f303dc5f485cba169264f76cef16a86fca14030ba77e20eb58d286ab86f067a603955f8b58b130871eb21f5ea43c2f951f55b0791ee2661478fe59037932e704a0110815f51175a0a68ed83bb0c5f0e39d9d32977b42182db6437fd074590b996a7bd4c87921fe25aec31199a0f8d8ccc0f537f4f8b1324ba7b8befc89f5322e58fc2d4e425e3fc8746314399859a4bd734b822a2f547706d66e1caf1bff456aafe69aa7666c6513d186db0fd77e82b0da53044ce7ce25fb821e1c736d2c83838a2ffba7b3e86aea173bc126ea2fb0cdb42b7f01b785aeff613f75782ee11235d80dc1b137017548f673e6ce49bcd3fa16fb6c66b9afcd2c0c1ef9f7fd789ff09d91b42f6ba4c39c4fd7d37dd71705125df42db4f41c8e43350897ac2ade1e130eedd35f9e56a19605bb0db89b351e22f68482613d8228141bd8fb307f12ec1fc671a8df1940ac5ed2b35d7c362ea4aabc7c96109f066ab7673c77861eb4663bb848276c47b475bc9376a77da5718a558c82e81d5a7b9df5b915270e615eb0759abc8245d7d4ef8f8153953d3c1b0df03c598ffcabc085fb59667d66cc27e5a12d392c0d9097514f6aff3e62b21baab6a54871b1df3f32aea491704deae14880738cb28313f4de3e9fe7c6c79c2337b0265f957393f2ed6392f6848fb95ca41ce8c90bdeeb9278cfa77e3dabb152761065346e9ad6e338d1834eccf5ac79f3c753c4393e7afd7cee93e4ad1649a3511ef13e45cc947b753bffdf35d23936453d3ed1694878cb1fc0997e844565ac1a97905a4003327f25041fde0d93aed8e0cf059045d626240136ec9c12c7342e166590a2fdef08636f3854d333bd1088c90e9a2d106278b12719ed49d252d5136441a91ff58fc255f502f141bf4fc344e0942c20b22a6bfb18d8c83f67b704ca8bbf17a487a86bc878e042c67eb98ba062615506ae546087d4c6f324aae086ea46e2a1a49a40be43d32c6c21c53dc573c3b2db0f0b02cbaf7b8ff101f57830311a5b250794ff5e6cbdb181932bf1ead39dc1356aa8a3432d53b78a4079c866a3a5eaa29fad82506c0fccf832d5a77fe9332dc9bc1220d57fbfbe6b4b5bb34e23cf63836144b799c160fe9c201e9e650f7e0a936b7bca9ed598f28564460bdd06e185a20cab05027956f3e7ed54acb0e57abf5f652210a7c49259e4951c6d45faaeebd77de7d92a453a09e42d4a39a84f88282dcd24a420d9bdaa1467f6915cab897d6d3b31e18d92c8091576c845ba2a32aeb1ba9c91cd45c556a550e22970230b385cf0bc527c010809e8816b89fbccd9df7f9ae06dbd09f344c6444fec1d8535372beec2c3760ba7b19d16371d945600066b72e313405b57515eed30238642f23ada7328fee909451724371a3b62e82be51a8672c7164f0a0a1391d41d441bc69bc6c1a244187965be29f180674d19e1912f04591260008421a66f9da636bf74c600fb5cae2056c0793c192fd43dc906f1bf1f8e5e4e3c65c640599223f08cd6e2da729989bb72fd697de8c12ec21d9c92a1afe53384b4d8d3fd173931c4ccb8338f19c777cd46437cc0b501fe3e61fd20fbee96efd0c5fdd28849e2594554b125eb44268194ab3310ff3af6ef78e1ad01da6374b719471ca87a5d0d839ffff5de11c0f7cb0f2dbe1c4c021ef2047b699a02426ee774c6162eb0ee0c7aaef16d512c19dd07783561077bb59cf45a15e7217876a80df76bb804716ee097302cd7ff464a597436d73fb0443196b360d4975a9979e39be158f19f0f8f947fe178f4ce6884483e66012b1aabfabadcc6dea260f4a270e906f30702109d02d5cd85bf5fd4bd96ff6dae244dfed366316cb4f9a3e90bd259c6cc2e4b699c244a38c6e26c10fa8f3b8922c4645358e2299c72be95c33251f10dd21517dac1890b4a40546dfc42ff66ec594709a5f060ff76c3c8a07ee25c4f6610a6fe2bd547406e45062d4a746b4346acfe450bf73f00db7974b486703ed40577a8450a598b98f64467aff72f77858464fda8e8ea7e09f578e9827fb905894898b1d40fb6130cf068fa87b4bf38220e397217afaeea9bdd51c15a5af22e1920aaf0ed12572ca55aeccd73818ac8862883f3aaf7bb84845da66c9ef0925dfba00110b3940c229efa20fc6505465a12409c458a4ba22912e17e532e29fe065336542e22bc0b140198154e7421354302f0dcc841ef2d8ad31457716b063c6f8755586ed9976ea30a3ceec25b3b52b3be0ace68ee80c6e4bbbf84e677c4fe700805d3c964a67e8c27c9372827b161a5d8b9191e262a962354d1c0f7d683b6e58d671a891c1df22fa023a8a0849fa597e1036f759058e5c8ff6758b9a215fd110ad6b8458f84aea9b57ce6a480bec8f8198087ff005f2dfd751ff3de033b73279a89d30f1ff297efc2a3bc9cd49469f48114e03e7c77e7ad71bc7dcf7a80a26afff58b6f53746f02da1a28d3b337aba9ee9b0a46ee74c8302963204c8bc9ef9c05690bda8d12f127701a9380866d700d08e34e6665e2cea264801adfcdaf769fce3bac75dcfde455dcf5fb5542355cb8306ddee4f33e6a5b466042c77b06abbcaa192dc6a02d7e39d75beae96d9a1c716894e9014b65b899fc872f812d8dc392d9e7a64a89aa56ddf1f4b8e9fdbc123093a21140af728907e70b5ad054fb944c887147aac51f02eb3566e6afd790682f7e055fb30d482273e32a1fcaa6567e1cea4f0de82cfb2b8e7fe2f743faa296570e32d539cb14f92b9e0d1f6d255ea44437e7268ac0ac69286509ac56b8ae2b99a2cd80b2559a2f085c41318a1cca84a6efd19b1cb07420bb83ff4f9b36b0f34edc340ece18cc316b258efe83c2bde118f7fedf3d77e2372e1b6236edb9faf25321fbaab3a1907ab68eff2c9dd12e3d577c639c3888bb9fa231ce5979ce4f1e34adb3d443fb7530120bfd0f63acbbd2da09c95d428356b83b0cff5b3af0c9718871b9bfab2ce575b71d1838efa0c50bcb414063e60fc02b7c70b127875f47ed461cbd14dc8c9d6c7a63a2c93b0aac4168f41feebeabdfe7839e9638fb401c0989c5bbc02061ae81e6ac4b2804fa61b430f6f40724857c04443df436e0008681b87d516d6bb8dac5c9cd9fe10fbc2b87cf63587ef2d53164f4b39248403673e462b7dff7baec28718eb5657734463bf28c7564577fc9f6dd112e8d5d7e13f4f799d2653bdfe29750142bd00861a0029cd3e9b710897688c04ebe3ec1016357d9b9c6c65229c1677fbc73a2db1175cd1dd1fdc73339ad395e18e0e0c7fdb48c798d19a6d550cbd8c1fb46d1e6e3c660dbdcbcc0b88885c13b8daf943f2dbf9e5beb1646e8a84d6dfcb9f3b80fdaac7108d8101961cce1c05e2d9e4a9f30bc77414be2bf6b140dc476d346783f44b7a217cc308af379e021046da59e2973294c1d4518c0ff76b534d40938bba985c2eba946a95148129cf641b635b0594386e929201744ddfdacf2d66118fcf596f92a5ff4f00fe32226d89e6e6093bc0ef65670e00f6853b17ce1397a4c1a7fb6e80afa521f3d5538de30f9cbca0fd74842711b21c062f6d5c7f51ce866958ef6b4eb8e7cf2e035e6668ddee8a8e2e2ab0a2cddb205b49a49c54a7c9966b0df3b5f7d976d34498477b05480248ae8ac8800767cac3628d2fbd70744a3ab16e51f6a587f6798e67a949be389ce2253122e477f86885d957f94f7fb5692373346019dde302d3cc243a94495444a6f86dd9d835ff63ff2b84a24b580181b1f7bd95941638af3fc66a81b985dd04929e89369d9064fab54e320e1442c2b7581fe67ad04c2056904fdcd65816c4822cd5d8b529a278074abb23b978b7bae2b5c93be9b514416591d83ad789bbf2453b2312023c0691f5aaf55c2adbd2abac0185d9e78ad34c2722b09542c1612c5cdcc2324f770f63df1bbf775c616f26ea3cb42f8c4ad68e3c183a0f6884160f6fe5f011b09f6e04b9ce082c77fb323b542c3310ac4a89665c4b58a6f2747a71aeab644a87eee3655a56509d32ca144be292cf9a0e1e08f19028dc91aceb6fa66b6041f0880db4530e8c2bd80923a224828976ab0cbb3689402242ddc92d08374fd0bb75aff3c84bce5dd64b6215fe1353a93ce58eb9472a205e178afdc28c0c23c8431f11e789af69c47ed80e3404a71755cec1d2a43b081ace7bc3af160fe6396dd8f1072ce6399f27ba07592897fcc42ea25664e8e3a9176ce759f961413b4878d4431c97c008a21a080635d0ee98c4b4cca9eb84bed17c3fa8992b9887282528a69b479d8573cbe6c8b383f65074ba7344ad2ba06026599825119cf840b49cc30cfc6d0a8f9115ef95b3bd87c02c863c932ce4a382a3feab064839322c0affd334c05164e4758df06c883c4bd03f4c7fbb9d615d7d34751741b6d93ca8397b81d72ab52395a06e71437046f3ddb8bab175b4277375c09fcc610ad75770acfbc60a24fb72809f426f817a78e656bd4aaf1e6eeadd6756bdaf1b1d018c64972dd5182224b7307f8ca0b90305f01d4e3fba825f43eee83e0dc59c207668957caa025ec37f4b1e1100c59265d1b16e0a25634e4168cdbf3d37f063cb117a6ae893ca0b008eaaeff72e0791bc24d535979475215a2f3b16a326e5f6f895c99021e6204b31734a697ddefb0583fce0c45b6dc5b8ac0ed184b334f03ad735b12d9759f0a0c64d9a76d0dd6a827c67208861304bbba464ae923d9cfc813baad71ac471775ccea3646c92d9d6f36e729202086ff9829b3a845177ffd99b195a1fffc8a227c5729b78581e0dfe4febb39ab39f7746fdfa9b158e29c413dc2a4c13304475057bf596de5dca23c557eb607e5306df23e937b1d31daba48df9c04b800c7c8582793cca7e4103d7408bd544fb2f85515be12ccd7ddb387ba8aa1762079a2d0154ffcdbce93e989b016518a18fcc1a5409e258c28e494459d6b05386ecc019399b6c8c148c3823082bdf5df8415735ce863d6fb8df0875e316042cda95a2379620042cbb7483cb2ab79afbcf3dda6822ccdb0d1ffa0ef8ad836bd58d11badda7dc4104ea60d1b596be5ca9b21ec7caacbd60d99a4ab2adb39340fb428064f3066a03ed995fb2ca946011aea6ce84e849f517a3b340420d4324704e37f787db4e4c2c470635b321b7650c3e51ca6d23f8728d73b7a38db91d5bb4c5ea71dffe1866b3bad51fa0a64eccf7f7faba042543a0fa7c9cda84dad6ff59d9cfd0a938ab810d824e1d6b51a1355d418d34e06b03f47236579c4760af997bc7c5b2cc5e957afc055f5c01c0cda8aea679cb2f804a4ea757765ce07b6a73b2f239aa3c42b75c46d0f0c45464d3ecff48793b32363349e30f605abf19112e229aa80f55325e237a4465087179e846b12efb062e26527844f2c4f2a751c2cc1900d03f047516529509229552f4a9f7907101126b9d9391ee12c6efd716ee650bbc89635b5a795809ba0c46308c542d53f839046b37fb1406f41d29e32e852e80a0b4f1d510222391b49dc88d9f1e64c04d8baf08de9f2190a572b6c84646ba81f54beed944d7baa34d49c229adf095c21f3907f0f9fb5cc9a120ea13ff7c4131e95aaef687215bec1c22eaff2472ce9b359e97e1584ae87f7b3a2ef8b2076ca758d71f3d24cd905eac2cbbe6e61691ba36a5c48474402256272cda11cfbf4d3e4f1c404f32a8fa6e738d4e4702214e435ad19921a1dec233b04ef658e8fef41d1e5d190ca82c23a87b63f2219d9d9f46b4fc03c1ab04380404bd1253dc4c50f6241c19bb31250af81f34328f7a1faf05fa7f1c03f96d1683fdec3034c830c4f065d131d681c518f7b8f2604411fcc0b3f508fdbd12da81235384c7d9d6821394845e0590dd87ef9ac8749970170c29e080b8e5e6819309ccd39febe47ed6719c4e6ba0995a49a4668991f721029f43f6dfcdb4fe7f1408cf2d254fe170ed05ac3a601f67bf62fb43aa0b5b3ee30267477cbd87f6f6fae6f0d96d9119e1fc477b5f98ef527a3199ee3c552acf0e84b7faad82f2bba08970ef19293c8074b992932fe43d5ee869e439c6d921ceef4ee781721542f109aab0f6eaa47a963ac60d4cde780eaf981045f3310a8d96a77fdd143e8a727fb81fd150dd0bea9ac61e5eb26f0789ae33f790c190166df3185e18c1ef2f674f3af889882f1412a6526e58359721d1ea1dfe7ce5db767883b1adfce51e1206048d20be77504edf970447885ba51f966b4f2bdfe3b778b7f63f0699c70ff9fcf6967ed1e17c35e81cd2cc15afbfff1ff61fe2bd44183e7f5237e9299e45da4590ab5a04fa4c9cbc7371b3236ed30abfef3e3c0ebf77794adaaf40bc173d1f07b30e01f7cf9fca94fac0437cdaa9b77ff44e3452f4f4b2d3d9363a1c11c3c3b48e723f846ca3bb1c5cd62943bf6e67708e1512cee5dcc343c909d8d3450cf458c278e70d1e6ed1737d2285e046e53909405bf0ca730546839918ec6d97f20f821f367fb9e4cdeb562b798e39c214e7ff3fa4a59f131d6beb4dcf8f1867d21b6f8c95d4e3a2b51ac39541d431cb074e6503cc2ba1598d5ddb827860b6e72d93e9f6700d5d1157bfaaf2c0677db5c538c079c7f683d02f8932439c5fc74c88424b1fd3787e3fc6cff73147196ccb21504dafd2360755d8b14f778b0b3509e3be00eaf9352e29b166485571ef314d5f829a5f6342b177ce9c7e738aab7b0bc5ff1b95783e340198b79491bf9a224311780bf4d881cf9ef2d8cb45d1fb461fec0570adf17ffa0d9c4f5f5ec71ea09790aaaf5fc0edade74633b3aa0e67ae07dc8397390cd3e7cc59e6fc71909f9465cf89dc2316f80bbf8771bf76e1db6036d6e89c63ba634e286a52178cd4286e73100fb8359995098ae0225e3bb3c538f31f893356b1162256f70ad777c5b5676b0046b4d42a969335dcf454eff6023fbdbc9806e6c51c67964990c0ca6c8b07acbd111d714065146c70791d7ae7a651be353bf57d06b8d777384bcc8da3ec3b8bbb65546bd9b619caf0be9c04b7f3c38445ca7ebf40841f5e824f9bb90418dda334179ed9398c7a983ff837a4a0b55167ce43fea1decc079b061f43f28c9c3f3b0606e7b130f4a1c626868a970e542a1c55abb6706f729fc8a4e97b1e4ecd4703bc50d64ff285fc1851644c4e6b1fc60c6fc50f4a26bc995a64cb568242fc403649fa3992e650035f7a5b35b43aa71c64ebcb09912ea4f0f4c9cd00a857fce4a43983e14b05f44ae0cb20ac484f866650ff78d72eeb2725a56e7d1a0a830f8df9beb640805b5dbd1058e2dc380c73d58dbbffd9beafede2c53909bc617672d2ae317fc402058571343bbe872824218daa8d55817fccb95b1e4b2daf8ece6b87579c8627f005b4a228843b2470d54947fcb7c03727ae4c30a88220c664e9d33c0e9748c9a80c5a39e438dff5e0dfd08d236a130f89c326081d914c343e08e15c3254d720225f92b9eaa61253cd19903e8e1f9f8d6f0bf587a8a20e6e0f11738a06f4b355e524cea79bb7d0d2fe64e41f5952eb406c95c2911a978b8bd788c1922401e6f4abed369cb46a329eebeb64ad8976632f384254825ee941477c44f174e4ed1c70be635cf4506e17e08d3222826e5cecf5bb5880d5d20e53e778ce73259254e0525c823393e063340f75e550b883e628d133e86dde5d40c13eb38a863fbad2220870959123deea5bac66937b41a2fe3468f403502ae8b03e27ce335cb305694a5fe7895277ae249bf00f9e2db224dac0a05120182f58773899d643bb5880f74c25afd12bd9afb8603aa4fccc95f324ae5f86d2b6e3a1aa017b002cabdbe25fe0d63f8191609d048e98bd60d81fa4d66a10e287b4f33d4593ea2e326a4f516c1321d9ddc008887fef067e4dbf0c141cd4ffe5a6951e3270a09762cbb78fc6b9b58795691fea4d478e2ceac866d0f9d350844ece0c159528aa622b37a85d247beac06787f5fde7ec14b8152b942615493523f15e53457516acd6c021e7c766ff7c6ea2871b64b50151b99304fde9f456d350ff2ab93dbe6aceae9b46f27f1c705c6349acdecc1153ca7aec9c831510c7b38afdcf29a94400c2b113ce6c32a68a28ac05ba34f5cefa54658c9350fc517f1f13f96320514d0d153f299e96d1b2e35589f1b2a06fa6b3d71d29c4d6c0aa58ea1e0ae910ce1b63f9dd7a16ba1c48a02272a306570e68c3ae0f9a09c7a6de9a19ebb95a5da0642c441720c806edcada3a01d94a653d5738c720d58417adb24edd350e2cb1daac54c13ab61774581168ffdddddba71012e3be3bc3f19c03549905076ebedc98121c77709c56ce0998ee387716db907904aaa2fbdcb8cf6a853a61d32cc3bb8ffe9d775505c1a6294e52b52d0d3121ba5db7a3d354fcf05e3422563e396cd6e1f2f91934e0d1db89e7f1ffe98b4a8c8e3ba53ca743e435a74959b4d77f826d69922815f2c6bc496e094cfc839dcabed12bf49804bbf454c487b66e660956295064a367b5995a36b415724ec88c0f11116e6ca55c77aa1c36188ac200b193bfacd48f431ada94ca6c8e103552f739913a7519836bcd252dd07d4fe819711471671416947d787c874ce9a58395c14f54151f9dea9350dbb9942ccd9309037c5ab1da2b1b1089d444c21abe0baf853bc349032303d744652ae10cb26ed5ecb61425be40b271fd82ca96961a10196fe35aa3917e919b0fd949795527a3b4b90bb30d3c290c4e99eb5872deb15034cb6fd02ad823b5c2aa9fdd12c7db8bd6308e0b33819b3f875f9e3f19fce17cd0dc6d5adeb8f64745f24f6f254c5ddb901f9dd5b958169298f67e5cbff44225a3ab692dd63cd451089c83d7a4f31a08edc726a793ca51e4c03af30691cea619a9892cf2a289934cee986aaabca466b1bd2348534dcd511c177ecc411fcf85498635a9615a9023b47e50a464142598a2638c73335a62fdfecff3f4b25924e755cf846e498c15fa48615ca330d363976bee0086b4e3441615ba41a9a5ed608ae054ecbad0e26239b8e2233540fa0cc8462ef68f8eeb14df7534c774c1c8abe51a93f818b34255c61ff2a01a3c0fdcd97f0c2b10f7fc1d86169edab7c2f6fed3af0f9e633eb7c5ce3b1be0a322f0f87b1383309de0635f89df4dc9205f7d37671e07e04411af2916c633453f24ec15cc9bc544fc78f7af699ebbb10ef9e7ea8be052800b7f56bb68257c4c407bf8b5d8b7bf15c19b51e573883937e35d2449f18584b84e21780754c901bf11d728da0870d7d7a45c2ae6715b612be868f90372e5e4054dcbe93391bd55f4413030c461af6ea964412deaa07db79dd709633d366caa91b46ecf4c30e7a410f94c2b83168778cb44b6f0c41ec8e18e7d0764c6f8e503b9e77610d639d08911fdee066a384550e30ebe38bc063a4babcad8c972a56f046a751c0c3336a393cd2083c04674a6aed22f04215b22afbf58e21c31be910c7d530235658780492f5677df8a5de7945a92ca078aadaf57217ac802b8604fae60ecfa544af7067be053dc8882c967dae3b61effba521c798d6cf7eaa192671bae3715b6a7922d8490ec23d8624454c1c059051022e805acc76bfb3f1fce0bf5de4f29b927fbe021b9e0891b18229c7200c07f85d065e4bed746fa94de0a25750fb5fe4283b57d7c7208b1db46b8635e20e35cdea4942caf0b611dde31e5ea3606ef99ce22fb33e734434f256c75a650da66dd7d9586e98510051d7a12ca72056459cf1346960e7433b604aafb0006a15a2084398affceede9c93994894a86a54bafe18a350bc6596ac7c5232c964e22557bf2f207a2c6c855211245b28fa84bc232d552bfb6505f73d96133c4925063402b766fa19534a0297a83c6307f0064f5f99222facc5d925f217c5ff5444c51d4ffdbd648d11b1974d15e3ccfc860744fca6cf86565dc9bcbe1a0d29812a2f36c85f859aa45fec0636d8400f359c9b0a8f4563af9ebb516b860076ff34d156ecc3e087888bccf8d17810d6eb8e0db8fc012a12ebf25e725091515645697918b96544bae6918cd633ab6c858619b085b6bd51027fbbc944e02671d8134cf7479cb50223ffd88d6d41914d3123fd089d6a5d012059732848bc96daa4599c17ca095c46f4162acbe175058c2e7ca89481447885817bf33f7815f13565fa325a5bddb2eac79903bd8e5e295b4ee20a961730b4c1190edb6e504ecba300d8005c40edbd6962e5f2691c8cd610f20a5f5b32a194b0d22f6d8f0d99ede2bc654859d1502df1f4ec45218aba9ec0a17a30a5b91324214cf16061e77c1ed3bb11fc16bf0ea46872173e139a8eaced493d5720630834e5c5b231d7d3fa9a78232cfb9fceac7e8d0b95285c08f0e66f070a59b4fa9f13cc6c8fddf2172eb127b696de14f5352922206b88334a0db78c55632866d07ef59e9d08e602b1e8d6d836b7b1a7238d72cce7b8ca11e29059c47c171759261078caef753368f41223a56fd3b48de1bb64c9ee3b07dbfc3c4843b5f08b77a425f1bb485c55465f537e13dcd5386f74c28a67970dd11b061a91c73c4fcc2677aa3795e0159d13a6ba914d4e50ddcb1c1bc4ee493e34635855d98f06ae360baf05993d501f965177e22957ea7cd481a4a7b95afd02574151cdb8c5d85e086eed1e14d6113a7e1eb528b1facf37220742c206999b5a15fd7e6277252c33d46646736aa7061ac4628734b59f82bf4435208cb87355ad2344a046546f5429d1018239150567d9a747b60e60b1d19f2f1fff55c17f67bc8ca6d50cc02da976afee6cad5585bc8c707f7fc9259ad80f120aef231b7558cb729a6fef71447e57b6aeddd6bc13e5f4f82d58f9fcaf5c7f58efcd5102e180a1dca2cf4806505ec56f2b2b05c5aa250f2eb28e04b3120c771ffee5aac0d397b362882d03840d3db913e03ac2fcc9406e7c80d6aeb9c4ce0a08be214db9b26fa7a09846ae903221c9c62072efec4cf7d31e4e862196f2636fe45a90fa017782574af6c7dcefe0f0d706178dad3a3c494c89a4ba1dc158a8c8dfc78f4b614d80190e32d7abaf9f9a20dc40deac5431d54bd6a5b730a183228cae2718bbb13cbd973a95ea07522184bbc18530767e2d2f3af3fb6b476258c40a48d263a75ef5d81dfc2e481e49b03c2f2d3fcf83f6b78e4f712a38b42ddedad4961df0fef1f7997c2ad00900604265280a7cf35457b7901d8fa466dc1e725c3d53b37f57f748b6063581edb2f77bf5ec7fbfa05cb3d8db5bf176a0834064851d57c4fc3982d124729e044fedadd181ebf035c22697588a8e9e25483db57a80cb35045c53e1402c86d6ffaf7946d0b5ad3fc63e1cbacbb479c8309b2ee17108de40fbb666d0a3c6e01035c4dbd0a0a47f55c31d62bbf7a532c1d1aa231d258c37bb767d068f6264174c328510ed359e58760753f2e749eb74eda7c9c2b289ca154362f4e6c0c07a03b98b55adac6bb8ff19740247556840af80eb4ecbea9d54989c22d2a62df40c0fb783ff247570fb5a3c2142ec055f4952c53d45d3aef2c2f6fa2b27e76da87302ef852a63ede2e5b58469166083020c06ce1b62788ae52bcb16fc29a71b6e256941af4b3bfe37417582fa5dfec3f71e6c736c3cc8d81abfc8cd273f129c825caab7eb1b0a263b241b525995f8c40b3a3ad1c7693fab5ca9e5aa2efab194e46992d6bd5b96e25bbf2ae16289f0a2c4a23c64fe4608d05f5d9bcda17c0427e1a5fad01662abf4808ecac568c60da63c267e0dee3a1d73a54922b1ce48ec8c75afc8ab0da93ffa8bb187aae57582f65b6a41e07f4f672cf08a70f21ac6f0f94e745c6402f11ae5638ccee29c477be216a0aceb4d5b743d70867e741393a89511ea205ae82bfc604c4062464a35fa02fb946374bfa6e469262ff396f1f76a12c9a87f86c83c7ac67e9e08598483618c43f66d837386380306d69cc4f5e7f5507cbfb26f00ef8811abba53fc9565a67fe46144f70b3709a24cb0b315d64a881c77476d6a579ab5d1f665d51814b1b2234ef81834cc4dfb430f4a343b48f92b36439ae497ee3504b41d860326a827bab02c98c03f1a8d58067f7106c023ccd460abc50ff2b12366d3f69bf8ca612a411ee50dc0ed1aae9b1574634bcb0ddc8ecc1159e9a70fa473c0bd89adf435155033313ea0677733aca31c73b19da001b2f24561cfa56a37a3e751d1c4105cc0936cb0aceca14c751d1019c9bc192b555485200f934049b15cd84979754d00c02b127f90664abc82cabb8b3968d7eb44657880098f328b150bc8a939a5ccb35c07c4d8b78a2ba56125589ba39ee19fc5ec290364d82c61950d4d1de5b8773c048ee304908c0fd11147d45aed9489d6bae3a97448fdd668af511067fd291cb8b1996744f89781f091ff65fdc1c09a789ef918acb25f7f060b109c6011196ee6720a825de760f06102fcf13ec750f61b51a97ed47c6ff3263ec682375c368ff280f72c4c8575e60c4f561bde4979df268db83f32988faad10ed7dd62a173dd23bd9d13ce67d66200439e313a7731fb66148ca665cc1df2d9d6eae3164a2a88b6e546f3b889ddefd9e91e1999ef1af21ef047bd7390d61b63b82b59ce503059ac0d707b1fb8f9a3e974f107755a7297891b601c237b8563235f9d1ba5d98f369c292dabdd8b97020787d2a74f8257fe872a90f3e1a70513de0770a843ea329ad7def16577e816b86fdb0a0f853a4c6fc492f77b9859cdd4c07b1a37b83206265d5661ecf6bb2ca0a47eb19abc15d7cd18f78266a69e3ed21f409685b9dc2635a9dab028335c1952f06b8ff8934b83d064c8f876b0311084cf33b2ebc123240e7b36a463ba517a68387faa0713f518b31629e215eab8c1b86960bfa0fe63bc3d1bfe8372b207a950c05c7061f979ec312fef6efc75a856042c30acfb8a19c555415418dc734e4c390f0eea91a256952a5886b88c160aa30f6d24d9037b9c4cb328fca42d16e7d8d75bc61bdebe91c5706f8cbbe3620124efc477eaa144470d201db015520c13085d7277fd8a5f5f3022cbffbcc05ef9102a39684662f1a880953e4daeed7429e63147e364bc954c509d81271277db43b4cf45b4e896d17bfb187a390749db02bc76166243d36581722b4b65fc36b7e174831998dcf5c1a2e42f48e709d722b08e88d9a485fc978f0036f14e2ed3ea80d047e145d543626ecec4e2a6fb3d386897e787108ded6f448b10c589be0f3ee08df3888a79fc3f36046bf32cd4fe433b8cd71102da8805ccb93fd9217010d12c034eb837104b87165e5587cf953284a1d828d6b0e0da5b40721729df88a369cd12e158360084f46b00c88d3aec9ff145d1e2edc23b2966a714124e216c57aa5818e07ddeb1703650bf350cf1ab886304e93c1b9bdbdc8e1d4a012c28847c0c443dea4d789f7a0238db5c8fb9d453beafae77e3c1ad68ed7b659cd218db7f06f8014e77fd15852e99fd4d5346e9f55741a8e1761df28a0e5e38f9fbb5766aaa520b2173e31e806be87245da5b30fe921ced4214ee5e8bef370ac3c7cbdd4331b0f5aa92ded6dd9e761a50d6e49d9d5eac2af511c25dade8bac70600f9735b096610d52328d1577694ef801fa79b44c9c33daa80fe8b4e0679debf80fed7b964fe13fd3c9233dc7ef4eeb9ab65e63ab7c58b79743737f42b9d09550f35e6e407f79bdd96d10eb07d58694991afc691be9bb6eb94f6ab3d283dda89a971066c0b5f46c058e699ff6182fda03bb54875816527d6108b30b86cba205c274edc0b9eb11d3ad386ef41ba2304a22655352c8617e8b7194b8dc75277d3f25b48eafcbfda58464806bc34c1f25fcda87d515b7520c250cc827619c37e786050a277f1e77d29b2ef331dc5c9774d260a669b2e9fc13eb5f61a467f3582471a15d9067161d09a9a697cc96eeb5bd9a07897584e6350e01a646e4aa5e7daa65d63d5194d2b51b1c6e777b38d586dc01b8679b6eeb65700cfeb0cf4159cfe338d1e04c5fd670ad1f66bc581f0f8c2fb773f066ef4822e88c2fb26078e332b93d31300cc252b37466fdab5361a8a46336f3cf1a0475bc638f0ebf2b9b5ed5060ab3b7661018ed007fa2b07adcdf1a05531b3f662a110f0d5e878e5774e142d7e29e94cc73e2f008c18029e9fd85c55c611cd5222cc1fe076616be02e0f89bebf1268d5b45f41e024c2e9872db2d791a97c5935a7e6d2b66e2090dfce48699b149217ad2fd3ceacbec48280a7e7258eb7682fec4f7cf964fb84a6e7f02380b29ddedf59823e73885a0881d5b30171aaacda2decd918a8f15b7908a1f5adc46b314000807e07a6fddf4d3443a9201f4d861d1e7abc09500595c3e3b9b37acff105acca0b01c1faf4c71ded8ef1b63db154a6cfc5c6ee1ea580d45280d59e8da831497a6f73f6bfac36dd2c7f71334b0e3a6be5d9cbc3adaa5452526b532e09c3ea318298c684079beba37382b0c4373309ae664be5477fb477df65740df72b67b04d0e90590b1d7dd040f7ebd65e1b133a0d0f04051c1dd2533290493bc88b69ee3f3e2b3f591e9f04d267bd9ce2b438b611b0cf45e370c5744b6abed45f2cd512f75fad2ddc641faa2f690bfb1bb5ebb89d0496c9c9c4e5bc323a6c524686d4892dcf6c654addb8d68ddab36bfab7a9db3679eb0db8df15b98579b766c74142a217835c4679f08ef6a9fc5173e42d6bb7e438cdeda6a58229ad14d2278a81f4ad40dd65d772857e01e3f7fef6707e86fb846bc796e321b87c867641bf35505b4f6f96ee4b6ea44eda7a97f06588d96588ad6ec47744e780b75b3633aec815e9e4b8d9fefebf8b393dbba286a28eeb86054584e62cefc9b430a75c9ead330e3969831e2f7cce84dcf14f2de736b1f286825f4cb9a053d808589c30cf3c61f9a23d99e67793b1f8e8b3f5442363e9e3d245c6f913a2c0e48eb189faef662f8e0b029e88f4b6fbe0c29d03cfdfb45d73be8e4a84e3d5574e72779814c41f9424e4c0067fc084686b62a1a082a2398aba7b35cddbd3f4e18761082f9c0b9140200e0ca6e3431d734a069aa818d7b3033c84540b52563b7a67dec11c83bba033d2c76feea55bfcdd1b2dde85db74ac8d57fcc48d68be2bf5f9b854a798b61951d7a942694bb8632ebdb9fbc63e823309bb3b02c5a6321653f519dd00d4bdefeeca5fb31e6bb1a855fe7f2c72a44f2cc638f481d95bac6ab97d83a75e5ad05cfb9d1b0d7a93b46f443782edd82165c02202761b2acd9cfce60c309e95b5af8ad4c59d77f7aa9917415f15d29b56c406350124601ea01f1095d5720fbc4359cbda72c57c5b61e8bf1c5558b21a8a28a9fd814ed7410302bbe3465010b6cf0aafb8b373a95831448994a9c1d93453705d147219397ef238b69d1b2227a7551ed0be15b12609bfed7b511a8c7d1cecf8c53e5944e707e170786c8a314a6449f197cd2b4b9d7b0404f93bb2bef431c8fc1f07a74eb08462042b50174617020e331ab397be036a610e90becf0f8d0a4c66502caf73da2a9a42a153460d2013647ea86d6632d7c4ad3917925b4b30ac5715b57cda60530c5f8116a87a7a89f25fe406a8d4c7e2a1cec29d7322cdb66080aed4fe230e528501162921a10c9f0f5d93157cb0e4565619b18d71fce51890817bd39f530280b5058507f16d1cd74059ebac6b0c84725cbbd9b17f74e14e11ff2319ae89104ca36e8ee7f65f10200df7cab55371946b6aa5ee8800627e880e9fe04cbdf7ada9b6741328359590eb01af70ac0dc5aed8273c1bc90ca0181736466ef09d807b5e508debfccaa53fc1139ef1b3987551ac98382cfcd7109486e00cf8548f969981dc99b0f468ab8503d30cf541ddeaa00dd66aef254933a691dd9f43418b499e54db2634e6864d5959161faff71e460ae8a707141aef4e168b1016a693b86637e3c0924a33a93c98d7ac028f983c52f122ba6ac2befb64b5edf75482cfb7f4c7d8e6ce382d67d5ae21aec0e389a19f00e7f3bc7113e952ecfed33684859d26a5c8b17be659d0c8e6b740bac68966f3beb453bd1311e32a327165a2ceae0983df46cb130c3f9194474e7c46bf7f5f4dab79234de6e0968ba4f16d2f52a6cb92c54cd976cc84a1e2958db7a86e310db0b0ac24c10b1dcabb7e0f978462b8ef0e5cf4fac6b0c9f48273ce2d144360cf8e3ad8b1d852619b448d4bf01b84f119a53f316d1eff8917cd5c4248553125a796ee100204329c4a5c8d6b36bb857357ea2d69e52bfcc3959a01c2139fe05111f88b136fd076706a5822bbd5f6915e4209b7b885e49997c3e5b173b1f932c40a88fde3cb4bb73508ae369a23162c3c138907a750ffd2387c12e99bca952b94ba469b13e2c01cd33416485701586e5316779dac4961cef5babf706a90e1cf6461a89d939c930fbf497f1556df2f9233368b1a79b1d8ec4f29d5629803bfa6312bcdd8cba48970d92e74b1466fa47891cd1422504eca9a438d2b23854fc2971d412a1f4b84b01a06249d0aaaeed28ea1e2216cbd03f65de57ebd13bf58bfdf108a66d2a7e769015ec9b201bff10443864f6bab91639dd8315f9c9bb1aecb26032fb65bb1c5dea1b2bdb84ad0beb7cf376dc796484de51832d3a6055d30e54de8fb3c4fea44d784a92e4c33e7148b0ca8325943b9a6ddc665fe7eae2d8a49e078af8498e960cb1423d05656c6fbb7059f10288d67543f46b6fc7ea9b0e8b518bef437dcad17ffb8e2f63ee4d18939047b4a61ab49389d0bba4bebe29810daca5dcf927d09fb13f9352d638140226878a945650cf7a14d2a08b9220e45857edc16cf9c0113d1c3a1f95319e61935ce97d98ee85534f18ad52a98150920760a5eb11607c9f3167e087df8b58afe41fc524c3d32117c7654d424d3a7496f4bc07c22677b9ae3edaa3453457632746071c8b41fe1d65c51c2a29151bd2e57d12d1fa21aa6d78b49992ed759914c298e4138917e67f1a49cb425cbb94fe111db70f470021cee1ab1977e5568e9c0ecf49ded764d3852a04150e1e55cd177e8b856adeaa5b26891581caaef3284e8f838b98c2959cdb193b66ad046559d546882747081ed6d2983c9bb41050bd3639a743f98896711bceed68b35c6661c4520d4c8b704d5a45231c165036cc08f06f41b7985879d5f58daaaadb85c67897660439d68fc59ed7ca23c5e29beebddfd3416cc7011fa1f839421f0cb03c62632007f969a0bc825d1d8db4684e865338dc0ad177f70ccd1cd3023d9536e6d4846d70417fb21af881cdff073ee34a4321f4353f32646286bdf30dd5f64899d206928c4a0dd6f33d6140e4d01a21b1fd16c07d2170489dd245fee1478782aee1e00b0d84347640a0882db1de91c87b3241d2a5957efa08fc5abe909e91681e3a41966c344993ef9b89a1042076c0c9ed9e024350c3801b31ac3b6150503d673839a858af2baeafd9c3c5c388510c90784fcd708e27d50bb1767a74cfd8db58e0bd67eeaf0d00417c87cf84e0c1e97900700a5c2a03a653ec4ea05ea9c655e63cda0b2ed8ee8716e8e572b95b631333bcb43c0f0c53ae5ed43a9a9d6e4e2263eb06aced8e17f994d3f2ff644eb77b2023980a4e3fcd87eaf20e14f81626a63ccda8b4f48ed7fcbaba6fd408296a30a94b6cd2dd385508582f4b4ba86e67784f1574c2d5b6d6030d6db14e072a69d4c24503fa02d331fc8221f662eb8fb5cacc473be6b360423d459654a87bdb9225538ca5184cb5fb995580612c1faf405a9e6e41cb5a1b4a858756c355704317bc1ebe9a79d1c30dc7d4b3b59d7ceac3d607f2e9e3e271584dae1df4432036b6abe4316b9fd1f19683dbe74ea8cfdfa62856042fc5f3de9ac13307ed977319adf29974141c986a46f6893f562eeb72463397776cfc8da1273869bc7d76a83838bd6321ff53a5ffe767286f3b77375b6f747abe4055b91c11b176aed6ac29be7b91c32971bb530cd595705f6a33d96e57571b6584054f2ee9e484bc9578c24420c9cd681f79ae0f795799346b3d34fd88a95ec8b51a9b3a9e05245893a721e88090c788ca16f3824f5e6be5fe14e4d0274b058c928208dd461676ce5a1de8867b214ccae5156733a4c4b43818a0a47e8f2ee283dcc54211bc6d8b7f91dd737cf06e70efb9bffc260bec09e40976aed384e45359f871e1b92dea4b9571265fd2d2099630c57d0d322a5e86c12bd592fa007b327a1e281816a7e85f0cfda3cd24edb27f16a2c42d342a9527f0517db934e45f414fbf086720457b8f0c9e36c04438f048616b547df19f58ccff3e9b555841f90b4d17f2e8a42e80f3be03c2277593b065f4fa5cea0a1b503421d227b0ca6a19579d2eee4b9ac8233023c413d116d94f3075a7ac23c60feef13cb484011c09cdd6105d05205b4b5429dc65764bd2e47898807cab354e17b41231dd81849d0b04094416c20c7b0684c65fd65e8d3ee988b86864a7071f58e58c2ae6db80ce552288eb199f5004ab7b4e9e41e0a3911b542544fd86ad26a59528470c3774a1ce71c463dbf5e2be0f18655396bfab8820be25217b4d4219abae733b89437b544743053922e176b5bfb34870dee20b84a852ebfbf0a6de26161f9e7042ac4273ba95da9b94f365c9a70f8f046fa85c329ff69efdb06de81b8f6b459838f791a1da2b317b8e3a4fd6be33af364d3e09a167f4e4b56dbda793432d1717960321d8a23a999165f29d11d71dade6db1ddace6512ce170c352f0c87681aea09383e25f9435eaf055a6313dc9e4a15481617e82119c2b34eb496a1d7a6339872f323afe8317c05a372031663a4a41f01dda7c4267f530b9e82dea4d58de591df0ee8fba05b9b8e87ef0b8571973b5e5551a4d58f54ad7b6fd88dd73226ac4047d37f41571c26f041b18a990e8db4180169d426221d70dd2c5a10394a961074c0cf27f896376533484f2176823fbcc1b2b281c1fc52c2b368ce4905f38f09454f54aeb0ca926504392d1a4834399189332d4ee75bc64d1fb1251f8b55e4c5c616298460be291ab986ed78d99cd29b66a9a46f2c29f445d3116579c7c655c6d54c035baf0d5d6eb83be83a95f1ad286e4578d7d26c8b490d7bb2a6e4cd6a08cdaea5890ab6dc953855bc001a9ed0c7a841428a692da6ec243c32c15f3a592ee6cf072c1bfc1e671e0fad43871b98b895154827951c394a009a0bdc8080ce421ef34a2a028470f1390905f172dec18c495ae2ade9b789174a35678d3549f2e8d1c7d42f49d8d4acdb6b29be6ffb1436e7d29cb4c1cfb1fb3a76312a8899a93f0706bcdb4ab569d04bc1975d7e22b014f7e98f3786180c5e8d5c1abd91392aed7caeb3b2da20ae9c279c2bca013a1a8b75863eabfc5946aabb1df5213a95c517acd2c396e6dbde5036ff36ba826d00370a88c64bd2b9412fbaf779f0a08fd01e6cd8c18dbc3f18ff740597d8f9772df8c654d7d392d09252477f1902785e99c6e2681a56f5955a8813a084e4102e104e18cd0330980e52fe1f4035569b036027e091126e59840f999bf8206c78d0e702e2a338e4bb4d0c033d70ba51c293a490e8064ab82d01917fa4d77e0242077cac40afaab951e1f7460b5430a3edaf11d4486941e06bcc7c8c7116f6417f1e2e78650b5489aae312adf110dc36c75b0589cf9785a5ac6482e3d690e0211f244739080212ce40146b0b8c1151c018be7660b924c1523d3af4dc80c6ddf2dcf26c26cc44e74d9cdb73cb4b811bef76d760e224de938446e4cda2025b018f7065b4e487049d0051859830b9ede40f85b5fcc0375bdc196673d4f74824a1411018dad3044ea55051e5aa5eab0670e7efbae48ea51b25c570e36b85274a97699fd90219fa8602593c248166e87d2ea8c64d57c796f9770e917eb825d283b4153a5f9618b84ba50b6d070b425f4e8a77b4f158a82022f67f9b03fc1f8c38785d418ed71c92c776e475ad9a6af7a922acb38378eeb13a66e80efc89c55d8a255691a400a427d61b7e1077886b6000c0df7b62c784de243bc727b61a15b7d08051547aa3f5bb2ee021089528bd1f4db050c0d7428facf10a5428c35551ddcf23bed8ced931b86c4c3d2f4df343783448c716edcd4df67e20c11a012a0e369527963febca08acccd86fc6085877d18c7ff152604697d278fcf97cba1882c2c3e84ca4c8aeb1bee089bd770ad06877828062386bcf6ee7796b7706bbb69dd29091ebd347d7a58046b67b9cca346da21180453e8743ebbe6fd362c9a07ebfa87f2ffbad7dd960f1e5a9d22fc960633ccf3705501e5f9a9d08dc6aa8b0d072db296365f2a6f59f99097efddd715847c67be7c49bbddcf6a8dea79449c73c669a2f8fea32488cbcf35b0d803fb37d13fbead5929a3ce05a3d76cee5ec100d7b0f626188a7470fc85b0f4ff1f592e4fbec41e043f60f906bccfffb56f7ee4ecd8796a48683850fbc1d20fdbbd29299962e936270afee6fcc7f236cf0e0096f6970c8fda531408fd5e3a902380a51e07e0c9d761d05fbb1d1e9a56c14d8cefdbba2c8b5ad8f844aef138a5a9a58ca91b96816e2b3c0e62bf53e0321612c9e64420bdf37bca02cd2cdf78f60664f6f90128f5d55569e67fe1e9f5a862bfa7f6b3b069db9f1dd3d6fcdccb052d8a957aa7b00f4586f28da94da704b2c0406e0528de8cded497b0a09801ee646bbba8ace0467b82399b84d6700512e91b911f5ada6446d6cb83f61741832a390dd177a9fdb9ea47bebad105cd303bc3588180a23928af8e6df208287b03fef6da9d274b96a191e0b1bddc1136e16d3960f240f6632487101274039a5681923d406c73768bd8ec2e447ec841ed6048e37655004ab440e3284e714f4b0bf8cd8f3ffe6f3707e1fd5c95ea5d863e2074d78ef0209f2b7bf88cf2590606e8288e2abaf3c92db071c3dd78293f64b030a218b8bfafb8fa868946478e26c07d12200505948db3b00abf45dde5513d22863a14c70757ca394a15a7f558e9d03a7a05120e400e489883ff247aa5795f01042b592ca8144db5e9288c12df9bae1a43692bb45fc9f218c460ad70aac1333720179a957b42de510481c15feaff5e5c171670c2bcb7742ee01ba457f5ea4bb3d4c80cf9d13bc1aeb1f2b559fe09e68a3de0ff91ef230f12388cf31e2d4b0570f95d70ccffec5c3a69939f598174b19d16ec3dc615a5d8f5eab80b65faf3db0f5f880975a7382c9d21cf39cce6780bc842e249c50427bfc55f101a2d56c9fd5b975ab4e96e6d03620039793701263b4db0189009c2607e9f8a2b968961644c02648370938fc777e77276fe99785cdfb5152dcffee36c9f80fc4cb42d27797c7e818eb3b2201be219a067af033be32090e49044fc7db8545904a897a4f5df5c1998f4d2ac3f8c0bc3d3f82203213b4eb6346c5577370b9d6e94101ae095c78584fa272c32a4e9e33397ad273e3d22fcbcbe4d8e49c3383e0237a73c95e08be58f53b3aadd4d8f743806e064d8874c40a5691142feb021c1e889c457849121d6f4874c30fab86e83207e0cde39f9abe600f3becbf5c82bd3d91a8425c4516c284db98c9996b672ae0d50bb30e28443fa19384f799ecac3cb6bcb1f6b5fb39c47ee25bcc8db6d50aca5be5441dbef2df3f1add02462c3d1c4d8e76e7840b1b23017af18ed0c92939f3eb6dcc516336aab7d31bd37ddb837795f607d700d8d35cb5c4cab26be830d18900209e4547262e11a72f6507af8b2354785e7c8d03027df6076fbed42f1c7e577a52b5bfef7b7c9d32344d82195a5f1a2cd2c27eb7e8ed73200681ed112f1ab8547caddaf949043fb9d6ca73c86ce273529260c890f123e481738ee6f2e979bc9747f4d491bdaf032ca60ae12f163be3af029da1bbcf54b58206c4d86c72c2b544a3e842f11babc660e7472ae877bea243d366841b19cd6960333486770cef4fe4a3429803987fd0406b5061cdc7469faf7a0f8861069c2ed8241b2a79610ecb13ad1508e5ec46470f0e3cce1ac6625dde41c3cf1f0052f0af3fbdeb7bb9671240154959b6c19d61f28146b3fd3fa52e3cacf69d9536ff3dd52e3d24225a7eebbabd69bd5596e1ecfbb2a851d8b32ec22a99e682257d9067ec1127b1cd20fad809c0450f966e32dfdb61ab7b3d9f14c05361bdcf8fee2e1dc0008176dd88911bcecf8e4dbb53bf2bd162176fce29099680b7481b4cd3349dcfb6ff2c2544369b7f56f98a6a26b65e65313d17db0b2547905b238f01fe45880b1be2441333902dc15072223971452f969189a920a4a81c12ab535fa6b0d8a1e7cb470a31241ca16aba5444912295609d5137bf3275c9e04f4ede1f4f13541a18c7f7c4e6b217b3f6b7c60388e2de350f2d51ea429dd70f0ffd05be00a616dd0c65ad5f4105d5b01663bfe81599273624bf680abfa90bd33f63ce8b8b46bae95297a49803ad066e39e4f05ada03d4ac365c32463fdfc93d65bda754c251fe2771041b3bb12fc7a3ab2194205b1ea150f5348f8905bed5679516c2a3305af5737fec21cb22f8bb5056313b1e6081cc78be4956008db5331e9029edc5505f7ad15f3fade4a4a4f2de91b33cb0e6ff20fff2b7befcc82de26e7810f67ee8a437014344eee8007c76450e58ab15eb16087412b5f4809e39d0d2e7c9b1007ae3b31fde6c1aaa8e257e90762cd2000f8f766827ef114f53f2f2af9dead75606dbf3dc7c360318b71cfbc95fe1303aa172d7f97e84cb4d03abe02a74b6ed839bfaf31c343829f3c5a07099783895f735ea1bd69f321f363a4a5678331a1b4f61d6729c7e6c501905e28602b3a7149b44b105a8c4644717e3f9c325ea85af2bc311ea0ca8091a80de4c07138057c6caf9d4d80eca4f27fa355f16bedfc85df5f9dc43b53b0f058c38a633bc16d8eb8dc81b69b173143d1b15f2a1e1a1faadfd4dbd083372c8a35d042d3057310127a62a1443647e3bbfa8e82e565c11611136f880b2030e870518494363ffb20ce0eb0a6e2a7171269065b681050028193999763ed3ceb14fca79b17339c67673db3aa7ceb85881a7a27543f945f451a9ec4c9c86ace8a84a5de1a9ebfcc298cf3dce81f380ebcdcc5e5ff8b11e7335e77a7f991611727b4ba124e17fdbcb0fc97b157784b422b0bea61f8d6095c088d333bb4fc4b147fa7ee53f7f13e86e33c3b1b58618a8ef5c85bba8a632534d87385c283de451c41fdb49452d6dc284cd059d81379d84f6710f465f7f014cdbf213888e2dbd26dcfd655f31e6aec0ef5df0df71fac09a94d06afce5cd23ae732f3960b9220ce0d7349b10766a53951a02348e00bca528bc8ddbce02d81372ca11a9d751a0fb290b936bd864f19f87dd02d3b5100134fd8f86d05a53db87e684569622180e579738a80fbb8ab08dce17e2cda7449ac2ad85666c646ce0d7191bf4242c3289f25c7af22337ddb05db5010c543692ce7325ab08affea98b542eaf7d286cfd502b34ba69f40fe7d1bbf6c270183fc6f093056a0b583945d89e39be75ab40b563aee1bc48e89680e7ba4f5573e2d1de6d9b20e3ba8dd3dacc6c9d4918e3652ecf3969c6d0058eda49ce2ca830c7dd991612eb161b9f60f15656e4eb3ada95e3ae02fd88411385c16deea0baf428324d2ed17048aae1abb460e440d049a1d561910a1038e1af0be4c338342d185b6a93cb207cf7d857407a365b7c520ceb275893e28e602ba70ee1c0c27a26916ec0bcb21c4ec43f57166ae8a392606b6f295a1e7f37fa4c58136552c92e9d2306f2d4c9085041b2377c6cee603c08ce3db49b49307d9b547c0e447196a377d50375f687a41a2e385da0d03e31b1ff59c0b63568399de009b02572fd42e19dc57cfcf0811e3fca9ce8bf0e8dac16c3f3410bff6f921d2a9c0644463667bd54a7cfed38d1f725259ebc68665ba0d918052485873dcdd203678efc63332d2accf9115e885ea7eeda49a7727122c7b1cd0543f6c9b4a868bc8e14962ae140875e2baebed072283575bed8d36dbf59e1efff79ed3d27ec05d09c422daa9ce89c1bd1670822c9a9eff37f25b8554282f387a7c78223e90bda0d0619a4a9d8d339e23b8ce52d53f20777644d49578ce345e1839688cb0f3996d49dac68d75a6e7d8c966d7ff56f4f17216aa92c952bf9b0867b6c59f02b3d28cf869e317340aa7ac45c5524ddde9b87556dc29d55809ad7b4436b35dd2894dae2a6fc04d537d3665b78dd5c6b5e5cc8462500df71ccf70356a0889afbdcd9783211299b849fffe2588d0ae2d3921959156191762ff728dc9467e886f8f8c480067e0107b90d632fdf487bec9a7ffeaaa3f1db888f16d8d7ee2c2a8688af79a6ba46f4b06881291d8c6c16f8fc650e48a9ae0c7819dee75a0cc444155947001c52ed1b5cc273f9e3b5a541f629dc394bb33b40cfd52be022ceb8e6583fa3097a29468d4ff4fe8065ffb6dadb0eb37cd95ea6f639db3e0a9c19f1a589dbbf0f3a77c15edc636f8858301e01d6923cba4512769647b4a63e814186b975db8cd191014241fbc8caaa69ed739a2f166312161c750c699cbebc9ca288d8d74a29acd4ad49a7f9092dc39fd8ec373c014a762352d095bf222761387f2e956249d46aef2f9abc2719c4fc58522623ece8b1e5b11db2202c6da8cbe54ceab5a1d7df922b96d7b6410240bd3436d971045989320c74709bbc516792cfc453c0d4f84b05d0ae96eef634110d8ac2622029efadc408d41d0b118ba0c62130eef353dcf938b7e354432b322f78cc3a47b5c6110d9b02a398e469b07e33d230634404e3af65115348221079ce56c64051ae2505b95f54c9b4d0e4dea8e218deb9b38ee2be60746becc353d89cf6bc175e962f6994ed62eeec91850d5429992c2a783592c7d1b28132f8b4ee8a8cea22fcf1a99e36d5610e31fedc6d98618ef59a80d869848c6f1c8cb4a218bb328fc0f87b5fd36c1f779c7c65dc75bf0b934fb5c03b8a5b83928651bc996c36308a6c9670d2aee5400984b76a8a7b9814f04aa6ee4fae5de308395d6320985a84a83072f02403b3b8e8696c0fb70b83100eb0b4bed01eebd5ce19cbbe0c17ad47fb26bc39e1a4bb7b183c024bc7d48a423cd71fff8487514a2beecc8006f9ce0616f22292f1ba71f0009cc24fe86314dfab9c709a620b3fd116ba6d284f1a057a9eeb6bf2002a10614aee6add1eb75ff323aa784a3cb2e615ede82e344c804be57c76c4d233e7baab476e254d7ab6485cb20a7bdde13de9935c1b4e5d5acd08e558e93ff1d725494ee4fe694c6518ab125da9a04b4c2c286527364118309865c1d3dd4c5f155c882d606e6ef7555f9553b43b192836c864c038c2124144e5f632e30d5310cb6635407d6ef8027568c8df152a8ec0902de1cd2ec3383b4361c9a1bad081a88b174da7d86268d1822f2cadaadded8a688898e1fb8762b00ad48942f7f32a28e980bced32d729cba5573f0b3db44952c7a192cc56232e830f43ab4acea33b520cafb17ac4da247153c8ccfe86e4a888c956948343f45b52bf19dca8511535c09d5f1e5220fa67c1a9045f1641c6b1a1c317f3cd5fb34545ac6d405da18bd3af36c1d4c565af3efab040945abbd7b7d0af0c72290cab03d2a16367387179fd1198622b87172f3605bcf515ae42560cbc9f670f328ef0a2c52f54309369e226048af96a596c0c3abebde913dbfc0e9f02c2cfc8b7741d036d0f1bd6044bde65c86e477993dbc22203b2dfd37dbca034628e6d8ee9280c8e724b943037fd0495682108a6df4baf5056dd991831edc49719d80185a262f3b5ec04a06864cfb61d07fe3c6e24424fae25ddb6508df9772992df3a507efba4dd14b5c52d59ee2e0a302b9c6e913c4c6a4e183446c6a7a3f3d9ca325f62a86fa9347161ec83bb1cadb148da9b42896e797af176b60a7bb2cbe20cf2ca056250061d6c882c584431f1f6c08a482617bf48ec9656c3442b5671cb5a0211c6675bfe7bead6ecccf20f02cf53e05c5250c4ab96db5a67239e7f8acae55e1a47ad4858151dc07c3b2fbecca62b896d75a69b08cbce99cca1725017b1301342c617c0cba34d195333209951015d0ee682bf14141b60aeda17e6fe2997da65adf7d47e7e3858a51533d4dfc3442d3eca21a5feb752b7eeec9a0c5497ec67fd1b131c5b6f8233ecce4a887982f8ce9c35e32d95d0e565e95f7f4fe41f1aa166907a0b1b3c59a72280d6816c68bccf553600372cad8ea56142f00e9b8fdc70f1688b2491eab0ace10354a284873eed197d6ec7117c7c0fc6b83d60a4056115b96d2312eebaa7805749ee674f26062b55ff71a5198fa46db34a80cf6f59a5b4cd2aef36fbd856c010716f49914cf5689b30337694186787022f7be551110da7211da2a1607ba8be6dac2d86434b9add96715a1d62b45fe90692c8cbdf6f4d1d980061ca2241af7ca82ffad0a6361b11d65b535446f259cc5040804856b2c414d11e81ceaa26702076756bc847fd96bf9e1a96997b2742eea9219cb233be349909cb5a02f3b236c8a184654ad8475630a8ee597efd6e8aa9055b2b2ac97b4afd7ad498f59afd3be16589ed3537c13795c1899330a87f1b9fb5a046b2bf45438771eeeffb0025f232ee6d77723b9d331a56f970a3c8d958ea5444012c7c40387d4d3b6345e74a2d35f5342d919ca3d385b00734c43351202f7359a4ecf5bfc3aa7b068614525eabde95d157481fca6a20457c0fb588e54196b0f44c255f74660c57436155bc89680d859543baa8069a4eb957b901eb811660833a9d70098ffdfe3b118804be3619014cc3ba57dd7353c466ae0f5f686fe1df1fc1957e55fa1fed6e4a435f9a5647d2b4aba1316f161e996d0de1d28370bfb488407bc2cf95938e7fe0a09dbd817682a70cedaab4f03e2e3f09db1e24b707c273979d183ac0566618b867648d5efa233a9ae008fb70895c5984840d96e29160bbad83ec6724d74487e90936087b84a74a4162e04d89d04b0d5f8f0a17fda02d34a232d7aadbd68c811322a6eec025367e4839ee1aea634e4b29630488d069cd798fb5da57b873e887b2ff24056f66fa23529f00a8b989a7b69b26d9a3936977c91f9bc7c9c80141d97f2151c0ae3303e5123cd58043c5245c0e03022407d2b4146ebe0f960236df9846d1f5ab7de14e4aa48c16f8f928e20c533e6ec3a54259b6b9092581dd294f5426ca8bc43201dd722523553dd640740319b4bbe93bfbcb83430cfd3b2cc60ee6383246f4cc1bf5822a78d7310a92a0c1a497e603741f78db44a4a09a470e6ad052900c37e40db10e2d38e2c8069a6eb874e5b301023fd258507041150b17308a94fbe7cdbcab512743fc9c9df32f6472a72c8809511697209ed5f260e8bd56604b7046213f95ca9de0a9e1cb526d62a19231ff4c9e5f0c75a919c1732438c75300af9a1ed1aba3e2051a6e84136192e4cd0d4592a772b978d6352f3d8f9fa57f2c6ffa0809bf9c32452b3db5e4aec00933037aff654a4640e9d8600845006d3d928c9d530cd6752e26adecc47b0b056cf0a2bb233c3eaccafd32be1d23984003c9960e946329cd13e9d9c6376401b6ea96bc4328f7126fb090827c7ca73a92049822d6968a648336bb2d2e5a928304b1f3459cd3c11ea5d24fcc3b9acef1b5fdc57d84566d56cf8048aacbb32be748ee9b9136ef6a00b4d286fef92a2d3e408fcacdfe094321645fbf50fe48e3b720cffcfb3a17ad22c305ac078e2675038018d3fb0421226502a0e2808885bd044c2b78e9d03a946537302ab009b90df4a3c86d48b5f2644b7b563b5a1006d18131949e46c5bc590370e09802b0c36940ccdd74ca7e28eabb19b749e61d03eb62ceb7c242be6711dd18ffbf6f4852c0a7aa2c599208785078acb4d3c7e23403ce3494de7f0a0088d37954508d3993c79be9e138c9311f4ef0d79b67024ac3c6f1751e7ab21f5347b2f53d1c082b24d5958a775ea6a4c3d5a5650bf9ee0e2dfde83defe78244f4301e24f13b6722b8c5ed97f556c100b241f9c94b8b338b94407cd9e877024ac16156723243cc298704e73dfbf82d7d72283197c009b448a2f318a5b5fc75bfd5cbc5de516c039c2f2dddb06a1260eb9832456e8de7d3c0cc51da176acdee7bc4c227173690927a76c2b13ca9afdd7bc79a155d587f23ac0fe9cfb332a3ac9ee3ae7161fb558903b45921c1eaad34fe80fbda1b7680ccc064ac97e5a46046939d578e765edebe80d7a5ff8c3851d8d325a681c93e99e2ecba94c20670fb023eebfd1b591288da909e029353df1dc663eb27b32068378ad8a39f38155b0c293c5417024c46b0ec74e27746d87b431db03d4630c7d2ce6cd7b8441f7397b734c1e70c8d21c29b28d0db00b64005580310bd1b2212f4fec35fc53ca31b535efdbaa33b36b6854dd1f6db38867017de13ab688ae174db07fdc4840754e89a3a8f7f24a81807ed24869f9dd9193c03463ea3107186cd1620686fe6ce90615ec7185577fb40fc4320603a917eee798633d324e21c4cc4a8860b1d045c3e7c27067789086c6efef9d554623b40272cac7dbcd29985d52618f4189a0600c4661b15271b6d58e4de831417d7e537197f645cd81a069e81ef809fd76a304f034fbb966108ca1d24a7784f3b7b8591fe58f5c7ec86272567a8e786507f3aa67e272eb7f8d1b063354c88f0079e010db85f0b6153afd5756f2b18016eebb4852f35f9e7162ac2372862e203fc37e89b71115f1140b7a198819e6b0865400d61c4b142754f7000d5e01c95d69d1364c09a28ece3965fc389cdd2b53f0387f0fdcae6fb5eeefd9e5cc9d0482e08b06520a3cc4c60fb32f214389599e901107bcf9a47f99180e5074f009ab16d14bc311018ca03b0ccecd4f60804fffa2b9ed59297ad24278a7ada31e0ffced5b86033ea49029b8214cc14ece47072f42b1214d99076351be3fcecb10ed13368c7ca88bb6e93505c4616a38c5af7d10552e6785cf060925f0e7797aef8170f021fff4f2cc550760b71f63efb2679e6f783ff3e406bf147d6413ba260edb661c01ae6f35bc8dc45ec7f030959610ed1cc62c8874ca4fcba8b57867067509cdf167b34ef5da8523515a6c635bbfbc1b5ec1ea1d2ba241d00d5feae71bc9a4f160f1f7390d99e255d402c197d5b9d6afdfc0f546eab8768a3fe78ad66b4ab2616bb9658679aff8311d1dd7868043e2655a749e38c72d01faa29f571d1003350209caf303da7f3b19dd3d39c06ef60910fcba537180d79e6571ab0b47f16b92bcf077cf07c56a16e0d80b3f26794ad01c0a1d19927cb078fb25e5296fb909d118c933f819d743e6078727619fe5e727183fb5f14ae1f85a33fb968b96a3d7c72e063ca0e686b7ad37215049bee88fa305069740ce0fc68be65fd3e42d042ef37b79cf3fb1f81fff0ee3c7844b4341d684effd5a8fc89a026b783f092645a9e5373317ccaff2c64a67fe3497bf80b5bbd6bb1ca62eff8a19d9311d1895095cee5bd113798e497fa8f6e477eecad06e52913d8b75c921cab02fd6c263f0e42efa46bb951d47300bde06d6cd6a686748b7de24c123bfe45be025543a150e6d2fbc7c7dd3c17668c0347769f48d703f972c712af4d1324e0c8e30f4651b0567ab4fc2b3ff0ab974bb0d571b259bc226c49052c74ecbbe4b5fb37afe4b703cb5948312a79b3d8daa1d8978f4ecd0c8c2b7a74e0302db2314838cb4a0b33261c5c302c99a21c7409c6e95632e548d1f324c02b8bb0aaa6d22d9ebd3c9a5fb1e50e17a291693a4cba87379f953ff5b3c441a68629ea512576e4fa52d30d3bc1676c7da150059b4d47529848403b4cc6af5a96a90ed8ff66d88bcd41f2f32c475585391a3d90cdff0ada764694a931ef6c4a47e6a0646bcb1d9118a9c6c6714c92bf63dbc6cc6de93c257c032c614513931434b1249cee3a0114aff46c1651ba4b5017486cf10eccb579b871799bdef68869a86f3fdd04cdfed6a5f4f7c54db3a11d09ea37927a1daedac52fde9519bbd8b84fbd5af71dbe04f2e7bce25eada84130c3d1411fe3bf6f1287af50b596778dce4ded2ad4afb53803c2c5dcb3b3268bc0268293b70857fd3050d60436352381f4955af88c44413de9f00e3c6d26410218f755a255aa97727efb617d7b264e26e8599eb1a31caafca6020f067f251ee748e2fcb5f3cf18513da11a241a81cea6ffc47c594ea2dba2667c3c441f9bf113781b6f40c2930da29ef3b0ac9951809b81dbd26b0b50a0c38d0263b5e6cf7e9c8e43e994f743978b0ca0ebebb804bb8a3df37e98fae70ab2e93bcfb5fa7985328f6497011c828016254bb7189e262312c746f0bd62bdc6ec33551a40a4c245b8fed9a247abb8a4a9070642f6ae48eb2c7dcbd334828d35c86029c11637cca5cd53fb6c3a2807b2fd341860029491907d89cf804abbdb187fdc18fb9bd1ce5f67a73bc365ec6e1d7d4bd819a20d55df4f5995448ba4feef4317afced441dfbd3b45c1378b81f3cc0790fcbcc36fccfeb0df0b332badf633273d28e16623f53ded6c5bc4110e826492e240a7f4db66e8344f82815ccda13f30337f57e167122f32f371abdce9c07133435c703f79c133578024fa80ae6900b66438fca89b1779e8d635eed178b4b1772b558265b1531992da5fa16302cfa07b46a21c6ddf656605e31aa219fc4f9e9d77cad815136f6847d84e2d95f2adab6ae059c00811d5d5e1b739283178129e849d6bac04bf8a61a2c6b339c612be1b12ee987b236e4962e8b3c81473a88e9aaae51f709d6a6e5506d9b41343bbc2583639dfb153268ac2666a75d82e50fb3164638cac33a8841ec05a796424f1edddb20dcfb353e72ce23acbf16d4dc5a4f8c54e13d802cb180bca18c9183ccd1720e6b01a1e2e849bed92e69ca3149295d76e4bc3536134082fdbba727c2763af50b1b84077edeefacf984acad2d8df31dd9800c984295c544864ae74767791b4fdd4f4a16727f6458dbffd4e4bbfe51d222cd88a4035c076f9b4c4e34138b45e3e1c9b2940892cf30e599ac57228bc16d768782002ec118d0861824fb10260ea2fc5051c4c2785323448344903d2aca7b598acbdfa2a10dab8319659a0d4611ca8c482a29646b5c33feda57aa5112aa506831ec5ec410e7f69195548eaf87060c4b731aa48d633e0bad71a2b47bcf138b4663aba863aeb2f4e7d65ca36727b1b82c36566539338e1299bfce6ab018ecd724d6f00053fa2125560ca30ee1fe50e436c9de57dfae887d08d5115de756a9c514e86d2c74123f4805cbf054224cad1a28ab12a3d57b1bc9c9523421801153090a350508b5285a9462c6efe8451619e9dc01684310da8c0ee55109050c54804f037c1294843d65bc2d65d5c63591f291b315369fbd9e82422b5acc7724cf64454293012d6437d0aa4bbad1a6094ba04ac6041d904bc397778a9c5b6fccd94f1da80cf69365ecd85d881e4c8b0a2360321f74f0e2b3fccbc011c79fb28c32a988d586cfb1d73b897c113f2413e36cacbc1fe25263b7683929ba1b03d052336f12472e8bca0d4e1573806dddd4db8f5555dac6caca40297088b4612ba90e0ff0252b08428f38db33b7d75d51599de8ba087195b39906ef0b22b321ffac66172c5e91b5399023a9f88a34057b672440a098a6be91337f5e676c985922d926eaf58b9f01d4e986b52b8acba3ef4279a9be9242d1d6cbf337cf2aa30c721c0905de9c02e19056a16512ce0b3d3d579d404af11526a36eabd7a083d166b1fc626a53de85e56d4b10ba9045ba20bc7b29c6b06815368570b727b31d32ec1c4d0e743446797b32f30a8d501b5561b897775e64c453b1b7b42ff564380cd422f141800887b0698b0bb0026660f600159f92ed1a238f52dbc42b37e99ddab981390ec6faaaf167a99bbd37d87930064ab87e710138458ac01510964f8624c9dae77bb283744a3da846a876514ea7c0bf3137248ec744a6aafb6522db90222489b7eda90c0b9d6093033153e73dec541e6a2e50644627467bb6258f9a6ea6626cf81f4f7b19424694e5d9fbc31c02885502dbf233a7a93d7fc53865ccb11169bce715529f6aa0eacc0d4605713cfe8d08598c3e8a81d0fdb8a01c1c99d78e615bfa1a22834a6c6e817020998c0aebcb2c3483fd39221ee7dadab2d5952e93833b870fc7225e45284c770f888d0a963d91c2d4d15d7d39391f27098e46be68747d1d33aa417805f9d95b7d899d02140b94c81172fc3d79ae1dea3e03bac4c4571c0b72d1a420bda479613deacfa7dbf6b4843c1d5116496126c1813208356937d60e12d719bfe5648a0f7f72f07408394a1f75389b2d7fe0aa904f5c7c0fed77a28c6ade1bb1499048a8f02e223bc5fa9b2be0291496a7835e1b7b8b9a3a7759f7507557196709c2f2d51f9ec88b4482afa64195800857c909c6d006d2d7c4ab64533f26a3f27914817209420e80ebe8f09a20e27e16cd1f019ce78501b2c9c731fe9c860097b491a583032e8646925b8bc9b645369a25a093e12b3083c880a96d5c5258eb05a4608f0399be9ece916011a1a400ad420f8236fa543c26433ea938c19c4531e05252f99e440010c5d10b53cbdc2ed814ae7c251aac8f8239a9518d9b24ae7d75d16271c5a48d271040a7ac5e7be4d5c2ad130d5ecb8e710beefefb91bf2527366058e12f4fb514f8bd8f5a1c44a0857f3adc86e2f34eb29d081b1848e7ea62c513ccf8a5c0258cb20b4a6601dfcee6bfa88e493e836f35642d1e4b2d3e032c919fe0ba22021de0b089dd55b051ca9b90caf1eeeba700e345823e92c1169d97d9c75eaeee55d1e7e2d0769b9fe2f34fddcd448dc563e54552601d65cf8291accbf117ece2ad906573a52dc020f004b8daba9a62de2c21eab9d5262871529b86393de595f782ae216a330f78101c93d95514587d369c037bccfe2fcef3303c24d001f7d81ea76c68d6c28c7094e1002f82bd0a1757b979717c6306b3de3d3fc4588b1023fe8721fa3015aacb7bb83ac3ec75c53a9f55fee53ea84d50c70281f0ffd0f9592afd41f65057293100be03c9fd7fa0b28f613d52a0f98bbd6ce6c5eee477e680d037bf315e37622846c4b5173c120f7a65d23b95654f8ceb59722c8090cc2e7a7b3a1f1265f4b0027e94d925a2109970e59eabc4e54d7c86a9a998e9cc08ca121df19a94460b6045424b7583793589bab38adaecb81e42360cef437980e685b549dbd967aa89710de5b99a5469405d56a3eb7a99d0c882e7e47420b60b4a794487be2d04bf9cb986eef644e8ca0d8ed139f713075409c1a1ba08aa4ea2532ffa6c266cfa4b52f5ec45e67cb07be70d0003b49795f15bd9c708bbfbc7f4621c3475d2bb0a9c35affde49bc72b00abf023d0f64dd29a4651bfe68832a4215cc0a61a7aaef7b843ff570ff14a32fb72c9a1e6a822a89cdc098b4011a9577063269cee391c702ef67e880c312e831368a1c6e0229fb9f34d7323c1c940fb40483b852d86b5549fada64d48a91fd0b688bf437166881729154de2289931359520b4c0f7421f0e15737043c4b3712bfef60a2e83ef9772706b1ee0fe4ddba895636c195e174140b450572bde16cc4ef5322cce7c6b00c7be23f307bf424a743f7deb5d3d1e5f2b6def323a48d05eb21f411f43b45593ba4d5ee43df59299e913067ce909f62e1ff62be49d15cc8ec17c649daa50b97e2d9cd53612ace05abeab7ab8613e145cf54b01b06bd837f98193f139f1a9a80733c66c2d52297baee344040375c67f077d69978d541103565c6d0b8fa7d9fb18926d95451836478229640802386c824a9ea12ee44736a36678064614a30790964ae04718faa31a16e88e1e88ed07a58be39ba3ae2000f43008aa76d98afd564b6774e84968fdd8ebc0dcd7e7004c2ef31ee93d65871a94924db3b4c7057cee6e52b5f0e01e6615d8affa5179eb6edc994c2e05c3fc010b3a4b19ca1054d0c000389520f5ad4ebd82bb3ef34081bd7622d00fb43a481a0c08e3ee872e38b3abee215892df41023b9a36be9cf13e25e5d266c3ab183b79b345a52d8f15ab3f045ec71ae385a47bf5492907c89e349ec8b430e4a3a73a8afc4b56a4279213a3de40d080804c104c27f1057745bbfdb0a884f6efab326f032e4a1e210f8dc012c1b0d31e3ee54517431a24d76c5347e7d5140da543314c3ca2000e2f1a1c87a1455d8d96f4a7b98fd801833261197af23f50520263c11dcd5169943c906bd7254acfebc75b6f98973fe8c6894cc90dbe9689b6291c625eeec48523e910920492ab4470f61335475e59dc6ccc2bbbdf16a00092961bf488900694b8c10e19229fe60aaed92aa73a6ff5c94516490279a2f4851bf5e89e7cdb5df5e21e0f8c64b4840ed2c595307c2a7ae4b1470ae404f7736d932d2eac00a5044966ff499d8ded8699163fc90542f3ed048017d2d86956a6fbb51609b0771d4a5456282dcf63681729f0c3bb25ea5012f185b9f2074bc19fb10f4ed7be0873443d3f94a62f0ac3e2b5128970ab5cabf9971dda7d50aac41bb37532c629286ec7b496e59bea8109ec89384df70caafa5492468aa0895795e3ba226688000d35d957627d5ee0ed103a19aec243f17b52e40a08a0b4a1edabb55a555fbfa050c36dd8b1bbd89e086529869f1030e2b1ff5a8f719e3948d2dc2c36ea99b56c22c645bda35d921ee398f44131edd4c50c0217ece13935016cd67aca4cf72caafd059f8eb68ee517296bf8dbda08d7c9328e643681d8ed3590d28b18758ee76e04d35aec4cd65769fb84ff510f13ca44ad36546e8713c31b9705f115954b8c7e00914e28ef411f05d91d980afbb32b6df9e043f19ca00b4e71104dd2092f9649f3e2aaf08dfa1e347742afe613638b5e5c74bd18b1e2d91e06e9f50bb7da5657b8032661838e31f348d40ac15fcb4c80fc6074104acaaf511fd0c27da4534dd116002870dd98ba42e35073462b95efcfb378be7a9d9b09d6ba524897a7bc6b2d2f98e8f042649e7dee4ce9e57e116a86c18e0a318317c1c554e1f6607154038cab48517b0d4881b1b0da9f37d66b69a7309ba5c35782bded400af1805aa2a54045cc396e32ca6ba5af19c58215eb7fc7fe42fc72afd9e08153dab07002abf80998feb32413ff910692469551ddca9fa0932fc664ad6ebd29b702570ece51efcf6e11c2a746272e6600f2fa85d26a4afde38db90bba29bc33573056b5daa3f7644e8af305bdbacab73da49c774fa65bbfa930f0689cfa02149cce3dd61d4bd9708f85cb7ff0304afe93a41f8989a556f938155cc0a57eb5148eb004e178d500d495a05e42bb4aab714f6558c4c1f1f82642b410a1c2448f1934e715b50c2e2e57f7e08f8e87a28450028cf1c4e6775bc270a695db9f839521204dd3e7ae815a1e943381c17c957101c4c174bfce55446e2b78c268d2a6e09cd02a641378e489db6a1b8f41777a86cdcc9216f7768f7b3565392bf93fa7340196978eb978a1f3bca4b0338a4f9915113d9fa2bd525099340ac02057fdc4fc00e62c8e41db9597baf4e0a780a4ed7d2f8e995720b3f5d0f573dc45d2752c4efbb7e6d46862c2d52f133ca8d1d49fd1df85102e036a6d1a57adf40fbaaf3944ce2406679451ed402939dfdbce8eec03db1f2d71112e27dddf100807549ec04b6d711f6c8f51e6d01a6ea0c7d43f2ff14aa56b1cb1fd09239c055fbf897573d252d4a23b407bd41dffc5f9c58be3d8d2116b5ed826c209c596734cb5c56fa3490fc5449c38edabb87f56014800f8aa250eb6436bfa5f7def10813bda0e5675d729404c72572b09640cb6aca4a3d2dd6345177f3067bf971d35af725a9d20d7acb7372d0db1e07417a4506dd425bbb903bbb50f38b8a8fe4ce29b9e105cdc3484f090fd0a2b193150cb713fa534fbb809fd667897a9cd67b7e9ae64bd84956faf6b6ead09f37bd3e1612a35c21f49c5f2141ec479a7f4b60a80a90df5288f628339ece80fa577a007b180362fb4295f418fedf3f3260c5a55ec1726dc66dec961abbb86c44ecb478f19763b316c23669db2dea5d7d9a9f50a5ffaef8ad96376a633c689194680568d29dcf7c9ac627935a107617282f940ceea89b6ec4166a1454d64ef6601ff011716a42b9ced359178976b77d1a6feefe8455ea62a7e5d846d9c8a0053c7787a6a3ac8553383fcd3413af2a83b5bf4dca287f1f3e14db9803580fd0710c95560abc3a03d1158aebec3ce678b4edcc49aee94cf293a088562fbf2b6b21a727d2633d4f434d1b4430b8410dd4acb1ddeb80006ae6c5576cab65702143c4d34690350bea4c503433a774eb5eeb5f73fb9cab93845dfaf3ccd1039be89bbbe1827e69e6609ac68d596155de634b7bb3da5346e04baedaf1e3237d614c5fd7781e58dcf70f95fcc39c084f5a1ce599046fcd6d3ee84cf076054ddaf562eb7e111f824214b365f46013eabf3fad7f2df3b2757ed57bef2358b1fb3f446d904c2af059fba709665e50564e24ee219e46662ee227e0729bd8dcd478a10ff15d0725de5d0a2da23b52ea3dad919ec3e3b701ecff7a477150143187162fea8697018cbc53bb9d248e3e5523cafa335ad743379bd0316469b17fe8810ed228c228e79844e75b748274681d9bf2a30e43948c6262d12d1fe802e9d16223ad5b1d2fce7fd44a9fef10addb78b1218ee33ba80bba67b568cd4732470f9b9cc409eb67ff8a59b616a843cc556c5f7ae038e3aca221cb75024aac481c534f3205fe665919b8fd97da4dccae37f67404f3f7bc220849e3922f08b8a512495c76e0f64f136a3201571c2914f15069bf87f23ad8c14a983e97a11494ad982a7dc5bce161e02a3c50d9f9e60bbab2e42f30c4a79cbc63aa0d008ede05f84ba9ff257110d0fd1796160758fe9632703a113e8f75f7fd58f21c0fb1b94d20f341d9729983933d33fc74a3f3acd86ecb3a279ad21cc540c680d919973aaea522f7d27536e6cbc14cc1dddbcf8c2e606e7d76d875004b6068b2cb5334493d5e4112d76118b2144801396a25792c0b32de5784d4e1d58ed898651841a359bb189482c772a16e115b2fdcd688805757cb1b11ed3a9e7da98853f0454c2cf4f8ca583d356fd8ca240051e4ba115d6dd4f9ecc35f7c63e8740c586e54b7b7019d6866bf8402cf84b0bfa8ef934f9e71317f3927cfa6b8dfc960f0c1e64f22568f4c909110d8835b122be92153eaf4ce01628434956446054c31d7faa86855e578960a185de37b4b91668be4cd82b73557f9e83e22b8562a5674219a3811fb569f6125701f07027fe62c3ac1e93f905501eb867056443a46c83298bef0e868bc56f3201a356017bb109bc5ebeb73a0b288557c1cc89883d55e015bda153622e265f0e28d62ba7fd267ce26abe86daa46981f1057d4c970f76eb4a27190257f333a5e3e77b62080c2e3844a81352e089faa7841cdc234eaeba20cf2a139d1d537b5cfd4f1e7fb415437ff259930c3ef8c754415ac150ad048627dbb1a8335096f6451ea9eeaca74b853e8a17ae3a2572a78b2158377f1418a1b6dee3573c46a98df12ce719b8b852168e0dd829bc147c807bac736a302ed60f46ddfb0c8dd891cfd2d9f81e39a2ba7d6048e3d35ca6e820be106be42f6181965a7a482092f42e8d00591e482254ccea7107ca442c1c254badfb39210376c8ee223187f0a06af7e8a1e2c25c8d2c99fd03afe441ee6cdd7118796a19d0849242f27825e34c81c1b1ec642fa29d8cc687884cb16e459c963d36a3b6c34126198d00906942d1f171771bc226764d0c291063e5927eea180b1e4a52c0bb91cc482355e9d7588d035e5059fd720b202019d12a1c0e76fbfc3f6f566aaaa402844fb0043f7051275ffb3b610a1b2f6b850eb7de0f3052a501cb4623af8bfc48d7210450794655495056e7f964c1d2e5fa746ba04c0196ef03761a14e2a5f69dcd450c5e981424589404c7d76e802b66c2bb6a90e5530aa2c1c834a4ce33e0b7339224d150e130a0f2e16d40e5ca5ad857b6f23c31da77950475a5c6263aeaa5a6442ded4c28b3d861b4416be458e32e22110fea2f66c62ec21040226540c14052d58267770482eecf4e93024a402adc0a82072f2ba7533135473583fbd2c8360b79202bb36a7ef836a529a7f4061eca4f73ed603da7868564ee1441bfa5e2355433b6db1ec81ebba53884da63918a973a3fe469c2e0cea38b2dea32e5f85a49a04c0a5ed79cea49d366abccfebc4e65b9e86461c50529815e1be49ce3941126002824700a061a3e4229c0c0acb62768c1d4ef74f51a3bb23999019267c2ac33b991db4395329b4400f87bbcb0f5e35d1c95a0a68bc03cdbe16cb3d789347d83b4f32fcdeaf0eda432c522f7420e53d0b592bd4901a8ae6fde033cc093f25cb8fa4b48b334a70a8fed0b26641708b19da179ecb6cbde197d7838a104219c28039b66a7fc987949c1d940faafadbab954bd2ff57526f79b1a594a1d2286e7d2a915a0bcd1a8cc22ff3903a10bacdec4c568920efe17272bf672109cc4c5557f0f815a0ab7beb8d4909bedd12adcb0fde5ac299e6e713bcab9ccd1e21428df8de0633a61d1d9d97713acf07bb530b999633450b6bafdf16862cef5e636fa02ea326e0c63a793d155f69613545030e1acc85a380e4b2ef2d2bedc4671622ec40a3dd8cfe8a832d0c7f6be95ed15e867386f7554fe454d9ec8b6aa5b4f810cd72fae0c2b38904b91976fe335c88f79db46899057c479350864f869116e5eb3b715fa32b9f2385c23310f4725018314817ad99a075973b44a836d346342931e577deff7e1e8fc04165a4d3396064728cff9abb8fc91206d70d88209df3ee9fd74c65b5105df0611af067630181913c2c0f7d53892f9569f0494233eb7cc3b60555f27249d0a71d260970e2270fc46870647a7f3c9272502935433541a507ecb4ad091123c20a6826ea733695a3350564ff438791b401e7e5e878d31c865f032357632aa7a22eb5ecdeae41b351edabf0effcc546e176b63d734895037ab6208f01c253d19bfa79db7efd775e150df5b0ecd300a9bbb9d3f6d9337e08ad142368f5125cc79abf5a9c2a60ef52b62889e47d9dbe2a167ea7b964b7e2a38e0062cf3f4618cb847cce32c641c20ea9b26ba616e9df47ceba2f631e7dbd81f715d76c1ec3cf72c4e23b9c8c566994724b43ba155214c617262c6a7eaf0ff5c570f070c56c7ae7cba5426288a4ac1791108d2a713a2168d1611e049673da9e29009510671e01b8ea929f1c005071e34b4d2dd8c3133bdf129b7fff19b8e7e61963f0adda7f2f2af3c59929573314433425948b917a0a19351898afccca67216a5ef7e61ab9207d93265a0a2fe8f4cd1992b84fdff01e800f516c4db607d8eb5ff1afbd9d70812e6b3f9a5a371dfb7fd22aaa5a9f479cd7a8be6e7548d412ffee59620581e399ef5b1e5bcef804121d848176037220a89309ebe9aaebeebe553181e915e53e85b98314b8d10bbb2e59a75219949bced1a5bc35d08c969d1e24814e9a3b167dd1e9bc0d14d35d74a15ca9882f330908d405aa92f7f2a1da63d910df66797d0dbd453bc53d36e3763a033da54d65ba0635415b1b70434cb274f8745107ca363b0f6140a4582a5b03db02a72ba4cb5e9aaa515e861ff8aff2d97c634b08aacfb67ff40c1825dc6878105097cb399f57af40cb14ba3aa4976308f61080bf1e91d7bb5c28aaae4349fb908be57df8c4108c841e17978c9adab3910c9e6c6018e05574d9e9ee021453046eb704440e471098459336c0c040a3d6434f5818f0c9b22cf6d53a913c6e81502324a391d6da505bc995dddb293e0c03a8fac0b24088227a58fcb2bbbb7bb34999229d5dc75466e1c2ccd0b71dc910320e010d5884c136586cb6c142036c8345136c83c500d8c6481ed8c6c811d8c6880bac0b12eaa5fa1efaf6f07155538fdbe3f3b93e3a16151347b2eee3fbe158d77355d3944397139a7238f91d72b9d5dfcac618638c114208218410c2f7de7befbdf79c73ce39e79c73777777776faeb9e69a6baeb9e69a6baeb95a6badb5d65a29a594524a299d73ce39e79c534a29a59452ca18638c31c6182184104208217cefbdf7de7bcf39e79c73ce39777777f7d65a6badb5d6bcd65a6badb5564a29a594524ae79c73ce39e794524a29a59432c618638c3146082184104208df7befbdf7de73ce39e79c73cedddddddd9b573a6584cff9e75e154504e4c2814d902a1863b7fa29a400836da46001db485102952d3cf0b98fa4329227b601c2836d8038db0069826d8030806d8030c1364052d8c63a826dac20d8c6b6c136360d4c1440bffe50424249aeb7ac96cff798f9848a50477c58c654967cae0a0754b460d6912c86a86c80cddc21d40b632a4a9230c6546018639a238c31c65490a81cf90a89bd9e277fc47ab93d2c6ba238d70887268efe497f74f533f7c3f9dccf1d71a18205632a46185329d20203c6545e185321c298ca10b669d15730a6b20117b6855de282a4f3c3fe28e2e3c5763a7648921f458874b46821f2a164ae88aa44439af090b930469e090543848e7880fe7291a13e3ce8c8e543934cc85a231f19d8428587158ca954c154a8d05fdd1117560734facf45c203e6b2bec7be3f64664fd52bb92610e8c8f5413cd6c888a8489eaabf3f246499155030c67244a161ac314d11aaa00cabe8c0000bddd0049aaa6c4dbaa2420f0c0d1b2a3cd5774c3b00f0b932c8ba4e3894907bdd1f9d0f45e199cb84e251c5bdae664c452565c3c281239625b22aaaaaec0fc6545054348c59f7ba3f9a7bda454f9412c6d8ae9ab0b0ec4a49915d2981728bf7207d81723497c0188b7e6946e75a3a3f8f1c3ce848bbccecff1ef1b20e90234a74a805df1e9ad1e301034d9793c9f668e9e14a801c5162312633f1c8f1388a09cad1457d2ceba23e560e28471c1d508ea2c8408e28f9184172592f6364574a3abb6ae24c30d64a20ba22abc8660a20303661eabce3de48fa888ea8f31194d3144e50e7234d1d26a60141a5b1da10411b44c0010b58932b363b446c2433e12001b062a3e30bc63e1fe949aa378208f684554956a77ad146878e4b87033050040cc48051e74996f0c1468a2b30c6c282012818b32aa9a3ffc9b43b22ab8531d623c92687c57eb4845a36505c6103850f52eac006fb5495cdd4e7d3d2b035e4028320c2e6021b60cc021563296bcc8185f1441a9e5882554b364f0cb9a229662e3acce108c7735d11e7f3696161d9acd880a54c2234a1e2848d05d6d858c0033ed838c1c4a60913308e1c988c4ec886330536842ef0c0494105f470814d05f4c018eb3c691bcec418732eb51d3b44a024325fc9c8b430f6b9991a62c9542db7ba463e04d34704aa3c1b09786023810c5c95d67f512ffabe1027ffa2ef0bd944c0061148d940a00e10c80193fea29a58d789c7ba423610d862038125623cd6f564204a8c70eeadae68f3802d30eb4ec1360ff8820a6d1e20a4e5014b30e600351863317a32325d119a98747868cbc5059424730192b13a1f8ac2d9bd51037a6c18b0b26100130c6082310e0b753eb2ee16515efb223972ed6b858049e6c0cb912020a03c2068b99e8c5ec92f997302a264c9a40513101c79302130b201219eedb94498582d727c4097479a429258861c491205479a7991fbe1162ded0f0f8c0d223292248b249e3c2da9921c993e8913bd64511f1209c30507b4f00cc2c2d32204c9f595cce5cc60171c2ed828a00d1b051861a38032360ad0c1460134b03e32161e15e8e40005f2115c684305e070f590851a2031c6133a0e20da30d156b08735d070c3049c4530f98c7a5c4f2cc8ba593c93fcb94cf2bd9010614aae1089ae48743b2c2c1b04186283802e3608d8ec0afd14fa2115b5955c0172b91e897e1d9aaef80a486cb23140caa6004e5827f49f4e27ff13bb9b18d1a1e90a1194238f75857c280a8778f0e0e8e0a15d3a3cb48bfe7fabc383078f1c5154915f890e4d5720d1a1e90a3df1d02e2e3cdc1bf1d0555833cf44bb34094d577c0e4d4cacbf46226b09679676716fa45d84f8606109000c020001211eeb0ad900004401c61eb1516983f9610e8cb10fb08d4acc2645114c0cfa372967489b942752689a9ca454b1b9b1871c53141caca9cd8d2aa46a73e3011b94456c50d0d81b941730c6a0601b14cdc2a9a8bde32b9109d846a386a68c8d66c846b384b4615bd8b02f366cc8f5d5862d61058723e94aa481b3e32b512568c0e940255c2ad17870382c1c8e136c80a31208a844007050c2101c1d1c4a8c8112656440070e4a0891383a28a1b22761c5240cc0a25feb07a2bfa22e2492208331c6246125eb39a129128ba8de88e79291627244c1c20213c3a1ae98188e355d333baa37627dc5c212d3014e2887b55d38f7c925e2e4439dcb1a813a9c113533a2667458574f5493cba12e6ce409cc11e999782e4b743f1f39978ec8b1a6fba21e121227aabfaec74e70ac0b9a389bfac19162622e1ff737be2e14505c4e45714253b56387384f282730216a141303c3912c8e745d0e68531cd07baec9c9358db8906242534c4c14319c4de968e1485fbdc7c5125d22d657a3eb5a9c99ebb95a118e745d77e25071672a10b5454c4c07383f71b68889e9803551d7a642238a338339928e8ad271598f6338a2d1570fb2a890cc9db40e5d3b5e8f36c7cae256562644554e72114f1e11a128986be4f3190161727fe6535dab8701086293872f04a20d10e7af90953a81786be2688e0efd15132615e58449a63e80900403c41638a1175180a00120520088108b8ed88485c53561619944f76238fe50848ada327f600063d80f9a31b663478a1218630ed071b9689747a2848585593e34a2734dd88a2a461c0b33e18310c6d80464f670c6198c85ae08498b1ef4c0f250823c8c40094aaec0831d581346eea01f0625741540819444c884a626526ac0580a194b04a048455595a6184ba1016329638871461b0eb8540ebd937fe9811dd6084d1f2555b0c30d18b38311096bd4e081963a9c810896883ae8b02ae9006c5c818d19e06023056ca0808d1184a84906efe05c0e0218634eb0b159630dc6ac08fd87a343c602ade1e8b004c71a1c3a54820e85608c55c170d0c1094a8430c131872bb0221eeb0a718284f3fa72724894c5a17444c1917e46749d20a17cdcea1af9f1a1282332a149eb1fd7a8889eee1bcc8079a2556aaa6ee8b3c541c2919ca53967535e514e387f8d3e5468ba9e4ef31135c3d1d19cba704c8c355953458940530c27445d3b2c3d41019aee47445dd3547deec4b1aec96af97c6ef54d2c1e599787a3e3529bf3202adfcfd7b29a4c204a474c0c87874c753d1f8acad6a427275768c4f9cfc789075d99d0c4c47242748da8eb5217f542bdc08046a049ff8fd00f79728958dfe40ab9a817eba91f17f512ba3e422f2a5215a99e499eaeb8a8978a62624d47a81e33a1960f4519a1607a9061c3382c478a1c3962394d34d1841a6750e3098c75aa8e0e0ecb145647871a6170acb0c20a35b4608ca9b1a2c603c6a008c661094dd66505631a2eae18c31bc30e58889a44f73d1c960fc184c4258d277058d270018e344690061769146139240e4b13fa7b74ae497f07949f88010c31a4400c23e0b0e48e07b868012363084dc882198c3110c311861446857376581d2a1c10bc08a30a621802181cc0d88d00b047300d002ef0852730c6a41c390a0ec659e04217345c9064469803f25c5345c97c2e27e643931dd2a345888f167be425c9243495a82852682a1113e4c63014260ce58322330386f2416182a30b65c8c85cfc47ba702f6b1ad2851d5c58438af9cb9a86c470ae9b0547d2ffa14454e6709e63018e94afe7e870a7a339a7a2384d4f9cd0168fa3c3717478076238143ac99fb4e61c87baa66f42e1cbd1315072642ebe46a2cf272647141d6b9258582c99db6307c79a2c998b774c617da66b34458ce813c32486f3f9e4700e069e5c9094a32305678b28385658c1d9c23918e030e9408c8e3c55d783a897e15c1fa27c702cc091663e94934b874539b980e0ab450b11169618cea674e8529548e6e2175d1c3d71583a3b26d05b1d8e357174e8175594058596b9b8ba7ae2845a7435d22e97c5b13ed3bdae3caa6e0f998b6574e8be682ac4b146435e2a690a504c01005370aecb094d9d101582f91ca242a2ebc4ea40a10bd6381715b22ac99bb0b0e07802179e608624499c8adafa9f10191b85260a0454f54bd068038d32a05106c6382c3268683102612178c1189b49c2188b3896d08614ab620ce5105cecd8a1843d304b66b280848a582f738d58b7b221171f936882d9457611075c8031266238ccf0006348ac99af2820d40eb984ae0889e5b1ae90904511b13efabea7850a62fdcf5c23d68bae132414102b025542fe81fcb0ac9f2a0a0835f9b0f61f115d22967575e8655aac9f7611eb87b58bec22d58b46482a0a0845f5b0be326259fabe90d05796e812691922737b5cd63444263415b1f008494b450171f22f445001bba6d0280707031c782b6a534ea821d4934b24e43255949307425130d48b35a23e4455951d61e1841af2d7fd4151413e89f5e4120951d305841886861f74c198e882585858587eb088bfeea7a22e0d193f20007b1085446602c1571c2a1a87c7680394e1304302f7351bcc074db0911e7158867c88fa61b5507a8444cffc157ad23d6803470fce601c4a8f9058b743e911a547560f9c31c612c0832f30c672fc68e69944c152f6c018cbc170f0e0048c735d263cd082071a608cc3c2b9eecc3349d93b00c30e0250064c13280778f2c4856287282020892206808223780660884230c10227d2c0002cd08032c6aaf0c318386954a0063d9680e2107e5802021798431a9813444460103908928302e420004224216a8c31160197323c8cb18d093230882b30c6363b88c4166630c6981ca3038e605625791316961c7cc19837e15017a722e28223070588030e1ca4c1188e1b48425397435dfa521fe962c65216c058ca2605a4df9a58585e67c652703096a2000368c0067b48b991925f090e1be8c051832dd4600835f8823186e270d4c0016400828c32ac4a0a51d385830665b40089792492f52e2e42c6d8c1185d30360587e5832f68d484c22d631861d635e18e154d74588224e931461d638531c6b1a2898bc57004631c1631d610430e38c40802632c07a70a4e144d70582eeb7e4238c400430ccd3856f0e070582c3134c0981812601c16315066408819881863d60e6b87feb776a07421045d608c594da84ab27e5f4e35ca997aeb3f31f995c4bccc05fd35b570249910f5b1a6916585a8c91aed50889a3820eb73ff7a99184e4c8e28a2b8de8ab15a3892f5219840d325aa804cd515baa04742ed90cb03e93c109de48712e933dd2bf41ccbd211bab7a23c16870a711ebb48319c7f8bc332f3a9be09c599b9a0109599640af415952fcbba9f10d5e4f6b0acfbf1a18ffccb45bd84aee740f517a5e4a25e28180af444ecf5997ae8cfa7c58893d75f08e18b2f66fe8a98cfcde1840e8d769e6246e40496228a7cdda91251954814c3a9a6095794be9ecf877ad99592904b0b057a223f2898197d2dea054855444f3d2eeaa54911d1f5415130216a2a12b2211b72a92e108a82b9ac6a0242ed90f5d38e1d3b44160a8a30c6383d4e9006564953ece8585e80c19864ea93e435f5e20223737b781131e04505bc58801706f0a200321773f46fd1014e193348490063294ca41c20c5005d14813126737b70a890b99810265803634c92b998a3a990654d1f1c2678c23ed3d513e571c2c2e203a5007a28c11349238812e8608c713897046d304ba34c3848e00346e120418cc4c508fcc0186b32e118c115248c63045d541c2300526bc521821656450174ba0ca360e34797aea99b98ea1ca1060a56bb4c61a315cb871b4a77cd13e06e8f6afa1a7eedc28c3dd3a19c22d0f45c344eb037c38ae38591ba8ceb7d4db0af7fd318ddf3bfeae685311a26585de1dc744a7c5f7e0e3f346c96db3d8cb0f97651e308e512ec9c18ba6ccfcd5ca7acae042b9fbba8a3db78cab963126c951a6fa8ab768777f333acc3bc65dbf83918f17e29fd753448b04ec7ac31a65747e7da8c22519223d8f8f0ae8e33c4aee5a7f3c90ba9de0935e4031a23d8dcfeba0de3ac19cff8ead4a379405304dbaa7687ae5d1ce3eb8d93489224814621f9b2a4833544b0f969eceadbede5e6fe310ec1bed71ef7735763ffa8bf53a3460876ca361bd6cda1feece443af7a24d585c47d5697f5aa260896fe7b3047c7f7e656dd047542ff993e23a272741d472b302b388e56dc6b806065d3bbfd6db051d79b66a701cd0facacdb6dc75adf6ffb5b7ed843638675b16dec707bd7e17dba5ea0dda9fe697c609fd64f52ead7b9ceb0beebe981bdf866ec0fe576947a9d41343cb0593e18a7abeeb59bf1a45e5dd60eac944f65ceee1f218dd4559755869df5dd87a586f3669829a5280c5f9674846874606b7559679413c64df7cd9ee9a20bca81ddf766f74fe7ddd0f7b4a64f731c58bff331a538cbe92dce6dddc0ea9aff1b7ee8b5ddbc5db481bd3b53d71c7fbbedba4b610d6cf3b97dcedfd929ce5a3f322caeee379d79c28b9fa3dbd1c0eaa751c32ba53f7d69c5d16ac6b014efad1bdc1eabbb9fb367bafbd48861fd46e75b561d7fbb1bb135037b6b7e38fadb5a4a39217d61d858e77caed5fbf42774e9c0b0dadb9cdbbdf8ad4a7967a7239d0cecc6cda5d4336a7723f5e799eea8260696cadfb369287d579f128a44231858dd74cb39ca58b3be5a46cff427efe954ef84fa44d7bcc0c2bfeebed1ad5f9c147eae0b2cf59befcd98ce37e97e6f818dd7dfc3f1b508a55339a1e60bdbe473ed0fbfce279f939ee9fa8a9ea461818def390de9ccfe1c965b7ba63bcd0aacd4fb3aeae2cbd2d51aa367ba4569e8b1ba2c8d0aac7baa357d70dfb6b3db3805163f7aa3df97efefb62bd6a0c04299ef84713b7c6ece173527b04f5dad33bbf932d678568d17d66d2aefdeed494d639e2d0b4d17f6337c9f57e60d63cd7a7ba67f6e25a3afc879f517904a4d10299634d237756bf8a7673a753eea5ca04d8194c0be0863bd186fbf9eefced4f9eb90a0e5eda8ae6e3aedefb2673a7585a60e94a3ce5bd51541078d428fc8086ccb93c2fdee67a5957a135d22b48ac036faed5108f7bc717b082c8d3ac72cef7d8ce5aed5337de6993cd12542e58c759b3a296b93f9d6d974135d22f4899578e7e77cbbf9dc9e2f5ed634a43ab10dc69aa97675ea8ab3feeb296de2304f7db78675be97a3677a8419d978ab7bbd9f8452ba9a75abe2b2d7fdcefe6486ad5ead9b65e9750fdbbf4c5f432db367bab4a2b2fe5a7d18cbe65ce28da1677a44e24e2fd07e5c5818dbbb0ce55787abb7d8a2af1189ca97251d199175dff561fd36aba6b8d6e7f22b2a3bca62dcb096355e28a76cfb4732554b8d2f4b3a5a84a84bf8aeacf86795f039f8b2a41303b2d7c5afb4bae95cc77c6f752a3ebfe7ae1162f8a0bbf8c2c4e6465f83fbb986cde5083dd32d1d1a5118043662fc6a73f77f5daed83dd3a38ec9e6e88dce7c69c5184ed8746872121a51a10e75168fcd396be8d037c4f29d5611687a58c662fcdcbb2f259e54e6fc2edb3aa7f37bf355bcb784fbbabc75fe76bd7ee97e9cef84d40eb9e429748d48d29b2f4b3a5b5b97dff2acf06ead6ba453263431a92d5b8733c6d1b5b8277cb44e08b17dec1e7df7f1cdd5eba4cfa5bb763ae7f31d76e7bcfe949ee9a1ff74a48bf1478c75f4b55cefd6f8b5dc66f4cc8e3539af1dfb68a3126287bf3af9707ba64bdd79d07b44b79289eefc3595a7d0686ee003d66985f1757d1937e9f13dd3af27df9119e1d71d793dce9ad5f564b885757837f9195fdab2a64e9f7c59d2f180076cac73df7db36b10d3a7556af2654987031db0f9dbf53b23c53edf9cce335d4f9d17cd5c730526c8ca4ca8459266421746be97251dbbc44ed772d57abbf85eefb93dd3e305da17e0c09335d20af56b19fabecebd36d2c25618738c34df9fcf719e4fb4cb0acc90154992244b44757cbe2ce9e82cec6cfffeafce31bbf610075132ef53e80eb56ef373759ee92b30415668f5978b0f49d23f89a82c499624557f4d408eacb8e6d525d2fc20c9acb0d246a3934fba196b6bf5b1ab2ee68da5ef8f907aa67f0e51f573a8480888a496884202635bfdac5f95d5b9ac145614be164042e1866dd7ea6f664ab3bbcb114867f3e6a4c7595b7d9ee9ef0a2cdece8eea78b5bf87733bdf8011f95e6e5a9b7315666faf3a16452e6cf7e1db309cda79959ee99a42f252f17d5953e7d05fa3ee5a534788b88f2e1bae58ba2cdfbdcd1d19626184b9c6292bfe87bd5dcf743aafc83fb3debea58b176fdfcec16cc052072f9d7a36fcf47eb079ace9d22546095ddb58de26e95717fa26dfa140542572874488dd30dffb153e776b8c2f7aa68ba84a7b0b62db9e91eef82ed49ac68f9ee9f99b74a8eb39107b657e8831a6d54157a7f64cff10f5c99decdcad7d0e4fe9d29b7c59bb163dd3698f1f165f19e3bf5c5dffc5707aa62449d28bd525cac247a4eeafb984d1b9d92894d2b3538ab7fbc3f5e5abafa2b6cc08ff8f8f0834b9081a85de05da4e7a587774368f32b6fbea762df6a196afeefade6e34e32d5d34601b6db3359825d653be47cff4ce73979ebf49910c58795dc36f57fcbfe37eed99dec9d5752f87662cf883876df0e1db22cd544ad83acee505da4f5a619bceef9ffac38be184d533bdf96b2a04a37b4b15208aea7c74d5e908800a2bb75397eb96b1e647aff34cbf2aaa01160ba1a3fbdd74eeddd4587ba67726ad364f37f1c69aee8cf586d0339dfaa456d71bb73bf7d93996727aa63ba12a998ebb69fdab9bfe2ddfea17fe65498ba77651527cf78b2e3ef44cbf40bb268936be5be795d061941acff74cd7a14b69779303d0be97f3d5aff3b1935537cf748f3512752a6acb96151f2b302b304356f22b71728f549ff1654967c8b3d7df778bd1e5da3ec3ec995e5d4feeb4bfa62a0beb66b1d2dc68e51a8156801c59598119b2d269f16549270be72e657b51cefaa874f95c4e97404d3e1fc9e2fbd74dec5ace7aafae927481b634df757babae784bd75ee3eaa2f44c5f8109b2f22198acd10acc90954e68ea88a84a4b575da2f71595e1cb924e15ad638daa4e070253389413909e504e2e0aec6072737e263a64741c0c38f770a4d0df91d175a4c80145143aa080e202fa3379728e27dc8acc643561819967d289c289ea89261c88ca93e3be49c799c8e844f92d1d15b000053a9d2726d07972411590800cf591a13e0f88000702a007f8575b020ee0342007039c588015c5260a1c500199fa4891808aba6ea7733d790b30e14e2080ee6b3481038046210378e73d9d4eb62690040a000501ac070cc0515405010150522ca1ef57100800050070bdcc03542c09a47814379c132811d078e7769ebc0458154060c9431b340fbaa114163db8803102b035a4a00d2d713042a55346f89cb729a41d52e488028a0b3cb16201279ae05480021390400420f000073480010bc0a180043081800318a0000418800096604c40b10463028a1579a30d04606bf0a28b1646892e524cd0185ba20e84609a4168e6a0c182e60a25f8a11252616309190c618cb9b00d09604002cf86049e9c0116f4288109be08c266048df5800765b0a045053cae8a4d3d385438ce139cd07f1ac78a2638a1ffc410a4c108452461093d701006184c09339281993200408819a90a8eca1a74604c650e292750794307949f30a6220738a00ce20deca7ab7a1537b4c1491a94e024032a6c5803632c8c261eab922c35a0c1984a1a984c68fa8caceb8c2b8ccdc536a22f587c00065506aa0a95227c2ea63e68b8aa2b624ce50c4b98a10c337f450b1853990219669e099413632a6a34cd98ca184054880c151730994214916b0a25f98ce19305507ee2b1ee57948fd044e56b02421654a632754487feb23e0bc654d21003634c2a0a480cc2c0189be9c1980a18bec0988a17bac0980a17b68004267f13c6b280852b6c40a007152b30a652052a3095294821743d305f51443e4f9492cf9df2a4471bf85893cc0fb1a1241774add11533a116d784854587a62b269f2e00ca4faab746d58b46485e18538902cb9388ba18538182ca1318d353c709ec7a02ca5ae8ff5012c65484a0d2041526c830813115345496a0a284eb33b26ef0f98c5ef427a1a2aaeac960210304949f9071860a12185339c21bd45086cd85cdc6c2664f81a9c4008c22a81061088c2161ec09e13ff8ea0f25f9dcff41bd3cb1fa33a2b278d07dc92f7a623f9f1110ea458792886e05237a621f08f5a0e97e7c505528c95551a21ffa4309f90fbe2f8ca904e1f31901d11f4a929fc81000e527d5e863e4132a32f3577c9a9bf92b548000e593d721f7547ea0428619b7003185b0401023859833984a0f18735a30a6c2831d30c6184341805580ce12e694e84e013a1a06ca4f9e128b6a31b5905a442da0164f0ba7856bf1d50bf9c0123e1004cb6d81862dbe80a94f0ff7a4531ce8cf9405632a3778c6546c5003fd453e1f32f47f68b08b7c3e2d9f4f0b632a63588ca988111a6dc65466f005632a61303db980f1d7573944f9604c450621eae53faf090b8b0702d0014930a61203f76020fd05d40570e6562df882b15012cbbd262c2cee356161714d58585e136a875c2c6a875c78847e088fd00f79d07d72893ce886ee75350feb7ab44be835880a59153559224b7fa62c7854915f4968b4b5747a55d7c88ca67a5cd5156997194df5e0c1c3bd2adc4b72e9a0a32aa8f3111dcd3c132847937b231e5a8040dfafa01cb9261d968ec8cac20e9b2cd490c5167270583afbab28b220c246577e25d6246ab126d1fd884093122c2c1827940f168447921eb0ce4fd50593840e63ac022d6c73640c6c73e40bb639128431d6002aa4a840dd1c696c830520d8068b25b04d9456fd996648d8dfaded8ab58b14cfed8fb037be1a377653664765738eb050427ff351a7afdefe8db0df2494f1377e7a5f8d9d115637ff3b1d8defedfaf422acab74e3bf98527729c68ab09a467c73db386fafd85b22ec86b7bea7dbf96d3e9d4b120c501061f1dbb7cadfcf219cd445234356e408e510f6b5fdef49e922c6ef74348485bbbe9c77d4afe5cef015e24d29618d113a74f2c5e9e0ccf9697698b697b7bac484b0f452badd73f5c93c1f66cff4ea2b4686ac482b3a1c84d555c37f72ef3adf7b924e4158f77b4ffc8e27a5133b0d84dd8fe2f7d41dfaaddaa37bc79f155000615dc23ca7d4f549b71d6749aa7fb0f1c936e97bb95dfd176bc79f80e207abeb9eb2fda35afb8e353b41e983d61e6fc477be973574d5258241e1c31ee29bf545877036e8cda1bfe9dac9d8f67618ee06dbbd287ab06dea889d431827745f6b1e6c960f7f6e583fd96083120fd655399fabcf61dddfda06e2f56549e709ca1d6c7daf46279f7a743a57d886fdf9f4bfe5a71b3b9c9b1d6c6bb8be1cf16e33ceebaf0e767f9b3ad628f7c652cbc9867df79bbaa86bc4f33637d78093521addf3369fcbe7a0cbaf9bd6e7d863c639df7a050a1d2c7e30361ab7cc34370c9b244912d54c01650e4dce5a9bc72725ac4fbebadf5feda673136af7b1032872b02efede58eb8b237ef8bf76bc39744a408983d5ee395f5c5d95d121fd8b522249d27c59d211a1c0c17afb17b337e7383e7cfa0d36df88a786f13df98e9d76c374281b74d8a484b5c6d6e4bb5a567ab3be3f73be75b7c16e77f3d6991da55043cf6cb06dded70fb1c6535377db1aecd595ee07e3d42e9b6b570d764b579d3b77e862f40de30c50d260b184b15db8f5cbafd04d499224daf168453458fcbc365cb1c30f7b7d7806db1ac5b83dedaee7e79a51cc60b1a6f7a95ff9d34dfa50868152060bb17b584297df750fff254992320a19ecbc32477f7ac2ec52ef7f99d07f3ed793a31af6debdb7c6f23508df431f83950ef3def5c9fb13e20ad3bc390b4a1a96d698f7e74bb79675ead6094d328f29758ea4cad0f4a91e245f9674288a18ec75ef4df9f4779c323e9430d8df9e677d38a7d68efa9b56a080c1d6aae36df735cf9462975fb0bfe3843736ea60cc8fa9674ea2db792f4b3a128a17ec7bdde884b8e9bca36bfed97103285db0f5bdfb18ce78df5528437d1e0a17ecde17fef33def7bf0bdea992e430dd982737142dadc7cae515a6b9cede55cf5ce3f3d53ddf0cbc8504344d48b24c9509ff8aa4b84a20537298475b62963756f90ce569bbcb9e2d89e7f75d0335dd4a1f26549a705250b166e8773d6ede8942e3f2c5818abbb784ffdaf4e78dd156c8d5a4e0a6fcb0dcfead20a36d2f93484ff784aa7b3ab428db23e176bac74b658a1d7e96accde34c513d6d62e15ec67773faf732fe25c698b60a04cc1bed44d4fec326ae920aeb50c1429d8f8ea7efffa395dff522c064a14ac9b4eeaa871751b5efa7c800205bb6996377a8b94ba863ac6f9b2a4e3417982f5f7bb5ff5ebb67caf66da91018a13ec3bd8aeae77dfabf57cd9d3a336c1d2ab73c3cfb76b90ba43cf746a39973e509810619cee39950eab8b9446aa67cc9756b827adda9f675a49926a44c3560c258e50c3e9dae3c34bb0336b5ab38c373625d85677943b6e095b7bd83e09764eaa21d5edeafcef9e3cc33e95f16186b73eaadf83936a5c808204eb6a8bdb9de7f66e361c256976dcf9cb92ce0cca11acfb94f0b62cb7dbb2ba68041ba3d718efdbb2491210508a60bfbe4c658c777ad59ea3ac44b018625873846efa75af710856d38d5b9dde5ecbb4d52d043b61bd10c7363d4257e50b82f556e17409e7fcafaf518050e16d0f6e9721843fb0ade918319cf4eb7ef78f6286757a77ce1be3bffa56977d601bf6ac33ddeeed86b3a63db04dd3adf1d40dbe79a3933cb0f5e64d279c0d3fc75aefafe00a4c10941d389d6e3af86295d4a7cb09a7862dc7f75aeefbb26acf7499093fbf46a2908b73f70aa50cbbb3dffa6a8b4fb6ac67d32e1dd781cd324a27bfd17f3fdf274a0e5cba6bd04527658befb953f8983649a1f47c9dcbd3331dc89115575da2a7040507d69f7c137fa5f061fc3a72484b8f22438248e940506e60b3ab6db6c772d62669d3d51d05f95c49a2b50b141b5857abc3a6b796f33a7ec16f9600a506f6e926e1fda6b59624a979c7b33581a04c020a193656a7ef5ef937d7173dd3c0babf7cf5abd5ef7679c2316cac2dd3a8779bd8abbc62d85767731b6a4d31c4b3350ea0ccc0c69bf7f597d06de7d0c130acfc2c71c553ea28b1dc0e0c08df7b95d2189bac8f5609676e4d62edaf6e7f1419d81a27aceeeafdb729ceb0c6c04a7ce9f6187196783eded4baa65b5df20c1418d877a57fd4144ab9df73974519711507282fb078e38bdf376def6d664dc9407181a5aff98c0def7c9fba7ceb7e7cd41aa0b4c0ea1b69c5faeeed9e9ed23dd3a91db266ae11499224da718af285adb746d89eebfd1e694575ae97814e67101416d81971ceae69bb8de68b618f151f2b2dd3f5f890ad05282bb01a6f8d5b8338dff96f6e1558cfbae1595b7e75d7aaa367c20c59b1a6c71f2829b0dba99bdb5dbfaf3d8e1405f6e24627ac94b6a861845eab172827b0d23dfacc7e31d63abe2d49f46549a7098a17b6b9df37678f0fbf8475d78ed300a50babe76fb8ebd31bd7a869f64c9732039a4ad8b7bdc128e1c35bbbbbe9824434d2ff43bf68a45fbf2268286175a33f1f95534b4863d33043de246cf577133be72ff3d4f985c6d048c252d7decf08efd3325f7c6186ac4c303491b0db63ddf8dded4ed74cab04030d24eccdb5c97ba9fcfb68bcfa1136cb58bdc2d8e06c4e296ca1ae90cba57184755bbb747aba87b33f293dd3affd9f4ee7aa28ab135d236cd5eebe89bd4937ab83b1f9555116648475d939beadd6dff1bd5b84adb2c1ab5d6dae23baa0e93315a58ab0f546dc689310c2fafc3ad10c8c24d50e2d439308fb3e25d6cff13f89f3db4a24a979c72722acce716b19bf5ea7ebbed29233976b3fd01cc2bed4b039fab9bd7377f820d10c8c24cd5c92d4f199cbe9406308ebf65fbde9cc2fd677f52e84d533e68c3fcffddc3f6342d888b5c4efb5fdce3d6239085beb751ddddc2f42f9f487da90ba2e90151f2ba24b44924417e49a0bc2d6f6efda9bb966fd506f178fa00984757f5d8e32620ab58b384640d88bb3deee5b7ef9dcc33f58b96fc6f9622cb7bef83dcff42b34793a1f82c98a31fac13a591f6e0e3b87d3d1996312347db08d3ea9a57e776ee7375f3ed8bba384f9ea5aa5ce0ddfd7ccd0ecc162d864ac3ace99af8bfb453d58b79da6b54d89b7abb079f31868f260ddbddc78cf17ab4b4863c48395ba55d8f0bf99a17cef833bd4159a5e4519a1ae908b24b51368ee60ff46df7a63f7dadcd81b7425d4a10f4d1bd66386f7bec732ef19a9d694e77a2293a825d28886c60ef65539dd673765d4f3c12895a0a9839d98fe8e6fce077d6b4c914812f42692e4de71e84d1e1bd6e5d86ad5f775d331b617e51a96ce2a5df3fbe24f389d337264a51d59f1b1d2b058313264c54a92247d44a0496a78a0a183fd8df3d6cddf7b0aa38e4c3e235cab22fbfe101f2b4a567cac5cb1e2634592aa67525dd0fe23afa91749922469ba6484660e565648e776f03d75d92af64c0751d5865f19912410550191a4cba11b412307ebda94b2dde8e8bb8d21ad2f4b3a1a1568e26061cb55bf7ef567bbfbe16029d454d20cdf95f2bebcdf60af7c5db77c9e5d75cf19aec004591151d50f491251956e1a37d89bf76eae4149e57db80d36632adfa5cfeaf46bf1b2c16e4d23dc2e7f9e75e737d76031a412de87ad6d6fd3dd33bdba541590fc22249294bf096dded19ca15183d579ba0863a412bba6b469d230a34b07a38cd4e78c3e9d378d2b8538faadd263fcad4183c50d537dfda7cb7ac2d07f3a54a8533dbfa672cc1dea67b0ae758c50ba6f714247a914a2a44e87be2ce970a131836d6e6a186b94fa62d774037264e55597e895c1e6b8e763ea669cd0694906fb307e14d6f99aabefdc55c3e2561d3619238ef7e5fb3e3fbd7c13aa32224972bad5713e09cd18ecc6f5797b74fbaecf697db56eb192a47f089a346cfc5b5dfcdda6ce1a7b6e4ec5607374da6074b44aece0969ee94cf215c12849d30b4d18ec6baf35eff972bfb7eb7ba6239195e4a10183c558bbbd6573f5df749f9ee95c7ce7ca710526c80a17f641138c0f49facf85479624d197259d91e60b75d6e9e073fea43f27db742d5618a76beeeaf7d7e47e764766fa7c5eba271a2fd897718e6fbbff4ce1bfd32b345146e0104d172c75744fddf6bd7eefa6ef99feba1df1519dbaaeaf2e6baa2a8c860b56fba3b24a3cfd9dd6fa79a67f46a0f719119124ba06cd16ec9ef941ec8e1f560d31f54c67e29c50a2a885460b16fbeb57e547af51e2a953716139499224284792244992343532d064c16aec9a7cee7cefa7ffa8673a75518f10cad1fbdca97a4bead064f29fabbf4935cad25f96743aa0c182a517c6e862c38f3e89efbb82bd4ed74865d6704feca07c59d2d942630589777d34ffbe53ff8b585305dbb0c6b0667c2f84f4c646052b67c3d99da3b07e8cb569a6603de356dfc50e7abc39cb9114ac84b06e077ffe0c3b48b983cdf35e3a238cdff4dc5b7aa6d3a8e4b2284f67125d6b8c9436ec8d70ca7777bbbea55bd12549923ecf5c9324499224f95899d97baa5e09911649f2b1e29e2449529e24a9e4935f4b92c822400b52ecd0a88c59e3bb3dd3aba9e33e6152ea60f39b18378a5bd478ef0da363a362c7d5ddd590e635ac6389aba34ef574173e6d410a1dec6e57ba7ea7b47aa657175d8109b2925b7cac54a1070139b23253c1b8f8b2a4938246ca1c6c73d7b3bfd32d4edd60f44c87374891839df94109b78cfadd8deea6c4c13629379e323f74bf31de70b0dfe474aaa5cbf23d0ce71b6c94543fbcdd7ced36f8a417296eb0bf37de185e59a7f37a771b2c9c517b7498278e373e6483ddb76977e7cb4f238c4e6ed59c4bca1aec76f2b1934ee97ef90d7aa653f72a52d4606bd37257ada77bcbb865e77aa4a4c166b775d6b5550a37a4fe32cfa513844a4183ddf27fdfec4e7d42bfd59fc1e68ba1aed7f17cb8357d9ee92ff39f4904cacf45201f461e1252cc60f19d13cb4cdfbf37df9c2ef76987281f3f2489da920463829432b43cdf6d774d1dc63547eb65494783821432d858ff6dfcdecf9ce77e77922402c91435ecf69cf77b74bf7734ee36065b5fad58ca48e1c3f73e9a868dff93e67befbc7262872962b05afafcafd76d1997a484c1d6862f748ec2ebe4ce1ace9802060b6fae19bf07a5a4f0ea27540f49d2ff84c26e7ee1b584d8bd08ebcceda9673a04418a17e8c7ede8d70d9b769fcfed486e87942e40a7b9fa7eeef2d6ee28a5254992669e50956877b4be9234dd0aa655575d22943aa470c1a593d8b9765b9ef39d674a1288aab624adc00459a1710e295bb8587adcef71e24da98e5ab0ad3de9d37da4fa41bcab67ba24b91da464e1a386f4fa7d2a9dacd36141c6dd60f62a6b7e7d85fde294ee4eb9ef74dc3cd357f41cad80268f253559c173b4d2a015a0d68f237c31be46a5ce9ee9d708d4f97c6ea6d788480848c422a50af645d78fde6ff95bfdb83dd3a37b0a156ca3534e59ab3fbc5752e7991e2d4af4e2dcdfcb920e929429449f703a6caecde9edc1f8a6cbd06d1ae7f629efbb5aa448c1ea9f4ed6ec4fe7745daf67fa8b82750e71d3514bba277c170a36c6cfde5ceea7ae3eaded09d62b6e533f8f314efad279a6cb10a438c1e61c77d37e9fa637eb1782c9729ed204fbb6d3747aa4feb03d779bc2042bbf51ad61adb76e77cd1d0d1b1de6b804ebedaf7ecff0b9ada3739fcff5f15e90a2049bf1eb58617dd16712a2d709a7cbeab2b9e79ca49c61e19377bbedaaf4ab6b7d2948b015bea9dbeb8b31cc2d7e4555d5a71cc1d22d75931352e7bae997be821423d81677832d4ba7637c72d32258a8ab96f1d5e6ad627c2111ecb7bd23add0f38b3466390aa50c81462adde3a7b9e297d14a11827c50c2592195d4ebf32825f45a5b8eaeb79e75d61904ab73ded5bd86bd3989bfa500c1bab6bfc6ff47f36cf06bca0f6cc62fbfe7f08e504e7d3dc50c38ab6baf52d76a6dad3638a1847753f897ba79e1c7ea03db5a93916637357d87ef7ba6531e293db0d5259c99c2fbdea3d39207b6e5475f6cee77dcfb6998b2032bf1d3b3c2791dfdd8fea59461a1db2deb9af1cb534eadc148d181750ecff9fe1c8ecec1a6afea1aa93248c98195f8ffe148a73fb8a5ab0e0716de3863ad1a53a8bded7b034b6b76dedaadb4fe76b8dac0be7e7862e7314ed8aabf1ad83df593b149f9cecd3c9d8c7bddc6efd5877ac64d039b5fdf0d33a5f81f53776b6c2963449fb0be37e574d249fafc4df8f7797bf3c514312c762af1de305289b5ce50cec0c629b1ceb9d53db56fbd619012868554c71731863fdd4138c1b050bf99fdd2ad21ae7a3b507ed2526460a3acfa1d958e3ec6b3c5cbfaeb0e01e5278d052931b011ee2631769bb638b5969204841418d887d251df4ec65869abfbbd175838a38bf7b96b3ce1762e4992f49a77fcf96b428a0b6c9e70defbd9b16e54cb2a4992e49a4812754d9c9b91d2021b5b7c0ffa6db46ae8365441ca17563a0a6f9c1f3f43f77d535860f7dcf5b6e81aad583fd5292bb0f8de7b7d6387ef6189af4b5181c5f475ccf5c9863d4f083bde454a0aec9bf8394ce77e5c67d328b095c28d5bbc38c7b837dc27b0b4eaaaa76becafa5dcd90b4bbfe29ff365ea974e7d53bab0bab6989de30c1bc4aeb94a5848e3733bc2ba299dda514ad8f6dfe8e74b33a5726bf8ea359a84cd17b7f9343b17f7de5b5b496ac10d4958df4e57092786b5662775246c8e34d256ddc52dd3e8141214beea5e95d5fdcd1aa9d388e5d4cee59cf95b8cf73dc2d60a5ba474d288dbf3fc92249d23ac94b335979b6ed335f86823ec7dd1f37b0e697c35436784c5994e8de957ad3dd33b33b8d364d2d17bdc588475705ebdb3bbede44f473dd3afa97aab53519f997b75a20bed4e471196b64b65acf8fd4d848d5fabacb5ee0769830f23c246a861bd58df0bdb75181fc2d2f6e0efb6e3ccb0ca070d115d95b0396dee39e7b3d2eade863f5dddee6decfbbd10b6c196f56e75e64b08eb627691ea26e7c337e134c18d41d8a91bdd524a7ae7fd3c2b1041d87f92cefaa0bf8db7cbec2bb811089ba9dcae3d381fc377f812206c9e77e2e9f4ad735fdcae244952ed38bdc18d3f58b72f6cf3522add0d3f58e74dbfec74c44ee7199df6c1e687f1db5efdea5d2bad7cb0cee17d387594f2d6e96d0f56d6d72a9eb2bab99d8e520f16b65a1b94ef9ee7e74e5a37f260ebccd0e56c72bff67a3e1e6c9539bf1ca9d3b1b536e51d2cc6174efd52d2886186af0ddb1e8d7753a9df84f96a9776b07b4ef9eeb50dbdeef7eb993e733faf231537ea60e7744ddfabf59e51d2a83dd3eb37a9463ee2cdc00d36ac9ece497c5f94ffe6c4bf8695eee78db55d9ae3cd35d2c1e246dd3dc2572fae2dba3958e7323b9cde9a5f791b96836d35c2e9393a775d377c6fc4c1cee7746eb3d557f18bd16fc0c1ea396f86546eecde63279d4619dc78839d6def1d77fe2d659e53df708395946ee820761e9f74fbb5c1c2e7b9b9d72a1bc5cfdf0d36d808efdfeb33ea776fad8f0837d660db7dfcf8c5f81252ea1c1de3861a2c7e39ab74beb3fc0bef96a4daf18ebb9106fb26a5ede2ed3acf4a9b43839bb5b9da28ac4e4ed93c46f748b7f6c754d38af78efa0cd65fae78cbf7efebc78a9970c30c967eae97eaa8759418ff96244992a4da71f75906bbafcef7e5bcb061b7b52683a5fbdf761f5b93bbbad8d4b01bd7fa26a6b7652a9bdc63b09246985f6eeecafaf3b634ee9571d6e9ea6dbbfeab6278de7cba39278ccddd94d5bdbae384d8f79692bafd37c260718dfb5df48a5fa4504f305898e73f9cf3a3bedd9cee0b76df77d5a33bafcf6bf41b5eb091b6975a66da72d50ebf1b5db02fe69739eb27b7d6aebd1b5c90deaaacb2c629a96b2fc249a3638f5a468df1fbf9e28d2d5888ebbc15c32c37a4fe9e0a6e68c1d20c5f6aeaad6687299e59b0dadd3c73ad2feaafb3b91b58b0f9c61bf3beb2bd2b9fd6f4c615acde57c3f9748ef5c1989b15eca492d6d63ce7b6e9c4efb95105aba33fe759cf3831cc17afc004f93173bd2ce95437a860e1db6ed388b76bf1edb8a760ab86f3bfea7dfdc29782fd37bfd6a9e794adca1d05abf37fc453ee1963c4170a563fa7b5ccaee6f7d2d127583875fe1bf3bbecaa7682cdb256fab7c12c9b9c4db010cb0cbf6e47378d4d32c1ba19e5f307a996b565f768d877ad52f85b6378e7747a09366ad97ccf0a6bfea795609f83aee9e6a4cc3edfe72458a775c296a5bfdcda7bf80cebda5f6b95c29763dd8f04bbdbddade926bf69baf5116cd415c7eceef5fcfdf11ac1c28af37de7e43b5c1d17c142bfd25d877f43183d5622d8bff0fdbe7e8feb747a0ec1c2bbb3ab7052f7995e298560dfbcdbc1765fdbdbbd0f82bdf349bae9dd71bbfc40b097eacfb7c147b57cf8c51f58aadde559a76eb945bfd00c2b6fa5fbdd86d851edf8f9c0423aefcc7246a877a5b707f6e1ed1ed6fb23d6d2511e583ce97e7acb99378e74eec04a4cf7d4317ed30e7a7465d88c778d9ef36ed255ea5c07f6ba1aa1c7e8247df0e11cd89fb5392adb7bd9a5531cd8a99d467c639cf7d6175b096edcc0c6166b9eda69df57c76d032b6bae10e73775854ec31a58fc3abb5b699e0fd3e7645899e76dfbffe77dedb54d03eb1ac6b1c928316d4ebe1ec366d77c36ecf2fbb765cc62581ae57450c2ea1f1f3f3c03eb5c7defe93c356efaff8661a5ce3037fdb9563a271876d3483dbbf84e7e83b9cac0de989fd3d2b5eaf4e318035b63a5eea8cf786f7d8dc2c0fee787317b6d335e89e70becfedbb0c718dd731fa70bec8375467f77594e59630b2cf4bf384afaeaa4983ef985dd124aac277cdabd5c290b6c84f2ee776fd6bf7bcb15d888a99b0e63778d2f842ab0573f87fa418d65c3db630aecbbf7e618a3d30e3a5c5160e56becb1d61d7deb4c4f603d3a9cf2cdfd1c74ef7961bfbea6dde7831043e76e74615f6e504fbc5dbae7708395b02e23f6ed5fb7a41b6b4ad84b9d8357435cb196314fc2de485bcc7fdf6c7ae3f6246131940ea3ce38ba7ddd47c24689779e309eafa8f431a9859116a390310000009c981b007312003038241c8d05a3d18054b08ceb031400034e7e62aa4e3c18c963c124c7511044318c410618420021c600820c54383702793513a7c91280b511ad6d9522735c2a27fb90867409efcc57c540f375e0df93c56a4aa99b00f0b687cf290c6430a2c704aa3713da25c89357278fe7aeb8ae9831238410c75c8156fb3c0aeb32f7272093abc6ffd74562a6b1f62a2d66c855d36359165dc2bfbb1d3448f276fde3125139830313506d8b4858696eef3ec9130110aec8d2f4598463dc699a0484b7d2ac7e029f2da19398ccfd9ec5bcc6d76f7f76200eee0489a088b9365e455de0523f88925cd55faa1318fa2080be3dc0800640606e4986d574e0d20c65483a7f9d834ad9f82fc1755e186ecea81f4ec956c6a16ca63c983909a3c5785038f4517bd0eabce68274c0ed259d38b641179a77ea3a8f5c575de2402aa5f4ca0821f7404aa342ccca0685a32e7851aa47ee15a5ef917434fe4a0e1cc7926e52a154f11e639645053d2210db98da3041a373196d1cb787b14092e5ad5d14b767377b1b211f649a3282f60f27a4f066abc76a400abe786a6f05a5f69b1078d6b235ec9a03cb42483835160b5102533cfb5553b55090a90f4120246fb80868356dcbfe4fe9284069e3c54af047d54085465254f8d745299a9faf708cc33ff2d9f53a887160412750c28afc215e41ac7ae23ea32922002e1c4d81f58e14804efb764d6448b4f709be151de2d7563f715bc62c04c0582c56072f720e96770ccf051f3952080c60ac05e67c2b75d1829ae8c6dcd9744da0434cc3c54824af50d409b510196c0f9fd95e776178473d06f990e10d40c824e905316cb4582d439cd19012e60e75788d83239936be6c6493b5b3247dfa9e84d4f8ea8750b0d166d3b4bb4433f091e54b64f8d3c2d7a24e8a2118b20c8cdd7b109e3a9b3bd1e18f3f3c262278dfc5aaa71379f7b7a5cd50b25dc86233b515ccc85a0c371a7d4bf760e9872a0c36582aadaff6ec64ff6d93c1065c8a08e35b8c4100fcdba2f7cbfdfa898019fa1bbf62a15fc1a9281bf1450d7b634cb1f4ee5338e7db74bea9d551f5dd58f9487ed6dd329ca2e63853c5ba40247885a8b56e7ec513de484e2f4e4b8a36b58f2e53c91aa2485244342e87685b102a77048e605202f7a226b1ec97b1ee41e2b0a6925055cbcb0aad792f9b12aa802ad172a6b900cb17508a031dd99399d219f2cd6e609d9b9d902d970907a2ca25ec65cdc9df384c1376cdfe783be9fae93bed866a6c498ec4302f684e3b66a1a5a30ab709a72fbc6d35959bca8655ca6e4270cfd6dc7402b81e23531ca8925b869d6397cbd7f14d4c4aa6f6e6616017f45ef52fcfff988d5f6fcd68e00c4c93b88741f012db69f1c72f655f1a4e3bc14101016e0f3a653eef6ab5a23fccd4f8782f126c87b36222b94af66af6cfbcc0f813f5048447503c6428de763159bb58d1ab927a01f75338954a2f68efdc1b16a3c3787721416ae4a1b676598191c29d5b160bf1e243d16affe6b0cfb5d154ecccc1a43a857c0876cfdd497acd49ee672d57f166a2675091235ff8f6b720734cb7321ab7a3f1d2162b4857af567f3916151cc5aec76886e284bf0a471cfe129dd6659ccee403eb5854d19932322edc84b6094050f0bd35ad0c498139a16496a11f738f1b235220028fd169f4b6d6354c9c10dedd3d8d50f1605c91f48d2de10b6968c9ee5f86566b694051855b234b5c69e25f8abf930864a48e39226f312ebafbad0446fcf0f02b719a89081974f24d65eaaae7baef2914ec544dd9562f59b4102da6f4a4a0e0e34cbd7c79a8682b6582df639b14947f795f5321e3cebfcc765bbc4f5bfb1580953458ad428071dad0ffc7c5bd079134fdd3e49618450e50f7b263e0029dab5fd56d5b9a923623fb5c52024e9b0971e35a5229009a948ef4f0a3429838b5b80ee476689e07b23131a5c25067dfb62f971535bdbcc452b63cc9b5399fe475f93a12a17b9a4cdb0c131155044f5c42159b76fa99541f80b9f6a00b0f3defdf3a69abf1130be6fc6590a531e2ec6d8445dda86ca3dbabc958f62cc0890846dc3219e6b0fd0e7364bc45f3f2596aa799cdafd59081485482ce9704172900e0904f5066d2d43f1e4b0681df5dea48a39fd636747e06ead356548cf52c385109ea9419dac9e3921ef7b4034ef9aef5bd59795f48b63db86d615a12aba3051f9bdecbe1905f4f743cd2a5d84f5cc562b70bf037623d719bda0e98e96420c13509c40cafc9c81d2a1beb8d8a682b2f07cf11a1c4f26ee88492a4efd6f0e138a9d73617a1e60257e5a3f8a175d85f3cd7585b0aa8fe50ded5bd36096b4aefb2df450a5e9d814da5878d0849ecf35fc246cde79196be9053da8379dbeea9644f0ac0f9b16ffa9ee264f56893e46a38d0f3ab792142ac01c09800b0b02de543031d6329132bb9db4e465a73cd6a0e93270a7a9413b87103a4f8121d9aa1de54813ccd3a39b0912453e244b31a13361d6012472ac43c5f1f5d7d77dd0c3cd246d4d507843353e0e2ed44467ac0aa659702e9795bc23dd2920e4865af9652899f80c38d77da4aa6cd1cc0a52493e73b4b311f0f694e63e71d7d22c04d8d27948142a758eab8e285741978573f782cd1f35cc3b2be4d3b1cf5dd6219f7d927893f83b5b6f9983636d9ca34d1a8c47cd03722701419ace2de470bfc31d4cbd09c3c1f83569c66805b74577c5ae85451b3d2460be611f7a32d8a1a064ab0722ef688ebc402795973692c48925842fb8cea03eefcc7a24b38f87156cfa68f75210cbd99174b8b0e2cf728e2a342fc30ab83d3076b44c97e78afd4b366d3a2920f95e4a0d2ab9d73c93c39ddee2e53f6e62d849a3cb9d7591663e2a6bc7b83e2aa90dd88f5fde5b9cd7861041d17e8fc4cb5a6eb99241ef2e0408e9475b92f4d918bc370db807fe88e2ad34c1cbf2bdddc629761222d2b629a5bb70bdf30ea19f46a7b9da61a3007749374d4cef4937666ee8328b7a7dfb5801d4434ea57c3d44703b47b6a65d1e94c0055c10720aa1f5cad919df032d1a81e20d2d3da024febe8ceb87850832c3531d32c1e4524ce0810ae93a1f7e66b171b66b48c69ce96c0e734b6334d6f7f74a15ec7a7b8fbcdfc8164fe1c01b089c41eed818d025d410edda7fed860c3a129bd86b34a7a7309da8be3cf0814d8f9913591dbbbd1186516ac455da3fb0fbbef2cdcae681a784cc447c654ea1e1c71e1c24ba02f8ee1e3d0ae776e8ebeeaa9d9ccbc62d04266e317b87f9c789573871b3b07970b2525a943f1346c5811125ee30b3ff98430c545e914f8783b0105a6f371065e58c858e0c175544ff7e47ba0431b303f85627f63a0c76e2e68e3aadb7e6b3469932b7aecec02ad15c676e330d29d3bda62ba56d69566f36db4683f1b7a687437f6d5c6bcf7ac344833ba73b916dbca62bcadecb466136d6e7587e6d5c6baf76c346863e8ae11ee466ba3ca3bf271b3e37507661953384cf331b1c6741647ee1d2b5b659cb6fc49ab2745d6bcefc394f72a0e74f6bbabd6367652dccdbb3c9e38d0d5efe5daaa58cd6d3746d9afe3c6d3075deb6e66ad6ad7367952c49977fe3045d79bac26db67f107d70ccc4a28100e796de497ca0bb45f1a5add4c8449c737e49a5c80d4cc953a4eecffa4f2a0e987d98cc1600e26d015d22da4f5e3dacbd6fd4988d0f21d5eb8f1e2930e34f12405da45c889e441887416708c2a9d9960dc04948fcd2d05e3dd0d5930366d89a7b946463902005062ef18024557ff428d5ae50b26a0859aeee721810bfdf7118366747db528f63b64987663661e4acfef73748d955d5748581aa49f5e6dc4e440a8c2cea011909ae288396dd4d73124d34fe8ea10658a84a648c17390fdad525eeb71958a289fec1632d286704f91e99b42f17921ffccf887ff96ceae4de398e6077d18c285bdc70889613da721eafbb940f3b2a3a3f2cb8d0708a607db79a6f530b8055d8c2c94ea47d3666ddc3545568bea9f7ca609f0e19c29d51502a00a9803105d98d6c2a9aee04c7327499e9d344333b4f8a6d0e641f1ba3d7235b12c6b231aa699208001809f0299f019051e78dfc9079670099cab082d2245dda47e132701f1c4753a61b5485f2a740b65666507854fc5558941f582719ba5237dfe2c277b2551430f9b16890d596d23e2a4b4115d7b896ededcaa75991d178485f7f352cb99a0df0d80b9e25c4cfa016861053ced9048b757473f1af2f14c1bcd500af1d9dc59846552cb2320dc91e715d5190c03da1794d7965954fa157e453fb7f64e5522eb9f19bd8a2ea3573f1a437d58ccc43d5391b45b59ae3e89ea75bfc89cbcd995a5d38dd35d39ab3a0155c6bc0bfddf1209401e23aaf9728ce6d5d9d8382461a856852153602d6eda42c9fea3acb4bff512922c85c9aa0a009b2accc7d3231746c6bb179a8f2749071e2712c8402205b16dcfb2c4aeb40f186d0c608945f20f84fccf11ea4707040f5c37d25098a0870c45eb51f73d3d9d9d26472b9532ddf5851f055b2ec657ef597d38791431009fd343d4ba98f5259a659a24d588f99703d858befc39cc1c9077a0120b01c4028be575683c543c272f2a0423fb21270be2ad87020bbf5b436f638db3c28561f3a9a2b773afe6395ec3307b5ba647af7ad57351593a5b8d9012099acf38b4d4601fa16f87d335e90f29b61f82969189fa99e18645954e0905bc3bf8ffa4d32b781ae41e3b4198ba0819a1d393a85e05c98a6fd56185be131e01a2775afe6a7d76717381f88018ed8307d435f916b502209fc7268e438f65ff6551b569ab54b6b6312e59aaf670f971c11a065e26bcbcfd5d610b1326f353477900780028153d277b09fac131bd8343531d40b8b73ec074943065d27aed712e44a850f8bdb3d34b3d1018b56071a2686403a66f00abd25a711e687393034e29b502f8347ab3888258d436086b512a12be70b13346633679607626d0f953c39fc847d4c0c0c3543b0c3289b5fe5a70381e2ce6b728a38fae2a81b63c4133a6ad518a8e0f1d7ac9701612144cb4bf2e30467f9468056ef7e2abfaccdecc6a4c7e5c58fedde0b7223729bf79cb0d27979ad06012786d9366303bf430a439c558f4d53fdb9593da1e6350210e4685fa88cd547207548de6b6e20f6f0c4ac1f614e754499fe95d198725e73e180b158cb6c457ab65c578b94fb2971903bf7d602e6959f0289f2420c652728d8db4111bc541407e74c86595e14806ec684235d524ead9a8fde34ffe0c324f547de9970c6f8bfc71a6119b33dd14d155f2a4b5bc3bd0c3e9f0a90ab707083804c08edf6c1460c5778efb31d3d637529022553b1818be6b6bca62a8f351d81500fd33e003299f4a6e06cb81489d1edc68a4e164385c852a8ca44a14659d0f9dff71266cb0bb2e734f62968853a0b02504c22aa22e29bda8815c19e462181223edfbb554fec8c949f3ee6a22de385a9f13a93e1563d987cc90937662027cbc9519006e46f3cc330815f415655a4b6de936183917a778485d36abd1b639de4cfa8876c6d7635fd3f682bec27a74d39feb2ea2ee81a2362cd536265c6092cae4261f166d68eac46cf260f21a60ab5b32626bea08a8104b9ffb6ae61fca23d28d3edc1df0f38f513863cb3618ffef3944090c6da5e3d5662c090426649094612acc4d203207ddd9e2896f0170d5aee8a8e2110d7d4700e0bf354cd351089ddc4c466291601ae76683342d5f99e101d01077b0efc1d612f51f60bbe247c09fd3369f4352ed134f0837c7b913e58d41ea81de4131b2fca2072dc09465d8cf5cb05d913ac01e55ff5df6095b4d5c0da916cdaadc3c8a04f08147ad6d21016fc65013fea71612f66a845f6ba7bc2062fcab7373ac96a311ec9eca95bf76022548c6c5bd64e8246bd5be4e6954d8fff0ca8ad0318e95f1a054b92ef4b7448f96a10c0593e8c424d500fbe63bf06ba96f7dd86ca6d30d9b58996c034dde42d1f72a76d0bc313ab81496eaa656b27b3ada7003e0ef06183490627554f4c4ad4aec9480626570a321b0d26d00a8c3420123b9c64696ca95ffe1c429db52f09c07963807d3cb3950007405a75019681fb8855649cefed86225b02e805635cad7ff005548bd26c574c57f368635f606431ccdb6da59575d7753980e56e6fdaa68dfe228310d437d847ad119668c15b05d5e4b3072b125692b37dfdc604a9b060168944d6abec8220ab9f474626530f35f436cccb85d7c539b1507bb7504dabf68d916253588760bcdfe603c2bd208f429550934bced60889c119b7519ad602f42fead85a7e10923aeab41582bf2ea4347a91902b90204c94dfb1d9f9cbe25e1a5ef06aa0495f3ceac8b828ad2163ddb248f6dab047cdbd01c26ac4726e9301ed20aa80617fc5855c6821044bf2805f0fd932bc6ea9494d85007fe273cb38b36742a34b719b13d7d2a714ac073962786b319d10f9ff6877eac0b36517bbce71477b1bd6e6db658206a2573e0d2473dd5904f9847cb3166d74c3ad13ad19b36a337ddcc4817a1797f88e82c2abab80331188e2c2a9f3e9289fc88964276ddc97ea8704564fa7fee0a1b1230c3292c5f38ba4d0f065d1b898bc8bbd7300fe641b46e902240545e3f923d54583f25a2bfad1a5ce4d122ebd3bc013eb277b77d28f134dea958739a46dfc16c70870dcc0cb3b5b5873312378e0dbaffdb04a823ac499a1ab6200e82633a55488a07b6355834c7551a6352cbdcfa846689decfc447bcc6d7498ceaecf89bc452d3f6bd3c5ec97862ddf43b1460c6ec6fe3fb7828590f6386058883da9d6deba3d947be3d3955570f24e01dd4b0d0833401bbddc1bc70cef5a0ad44d5b51a925504053a6dd01781f91df85f35d8ee4c4bdf70a6ffd68202277ad18bca07a7ccce43b6fc542020d34ad20f02e6cb6af6dd370d8edc5de04d77faa771aa0416c226ad561396d67fb5d63d35aa1e0614dd2a78a0cdbc34144a925cd383b7eae179a40518540fdeb95351814482209c1838780f8ae1422cfb04524e1e0924cb03262875f9fab569c8eaf45a15605dde2b0a77e3651cdc238a314dff4aa6ea8b218ce4b3b32cbe05b967a052c7761ecaf9d04c9e6e35e5ee3969c4a17da77c9a46656122286a1ebc851274586613c385c886b3c80d3700afa7fd2d04fc647a896b7a38465ed98672de9ae674e5264f1ffb534bb5cf069e0e64154f1ecb5b9de158d0a1119b63b0e4ba071e637a942cab89c9202e068b3e0da06851dc2c631f59707963ac6e1f9dff8de90691244371b246f9c9df2dde81d6e620fe11469720e8d23bd48dce150cbcacf14f03be4849c8a605374a18d6e26ea9ce9919d4c04baad165ce00a55552eb526cacc80c9097a96a886866d7cacdc5d67abf308b543896543f5f7050199cc6aa81944115dbac64e3f36e15cfad801e37b9fef4edde1ede15d5925ec8c1db9d7e4209642a9365805b4e40f146709e1a07020a805a2ad75790d1094d246a980326fc0ad6ce19ea6a5a2e9abd46d2a62b861a07febe277820599d470293ce30b895636280443c7c2a330e0b75c0207aa3479a12c393cb01c9898a17344675e299ad7ad3b1c518d0bc8bc9ded1de1afcb31b05ac9dbefadd5f82e23320d728da96bd8fb878e956decf6488e83414128b10d353f0c83078d32f99da710f3937fd8523b935d3e7f93006a8f3c620c6cc87b254b656cd95988142ad89e253c319d417aea2df56b1c4dcb20dd046b19be8cd722ad48fbc47beac459501bc01b0547c1eb3c0c9124799ea35cdf41a304147808ddf5e3bd92d51f3fcf3a9634cb8beebd712eb05c6cc58422dcba67d0c7cf9eb49c6f6794ae7d841e469ed1f96cea13e0c13820941c9a6319bb991853aa62259d7e7f7c0a4e149117fe7c37410178558420ba01b622504a3242742e6f96e6247896de25624b0cd24e45f90b608580c79b4381e3e459315929574b707b88cb9025ac4494d54c23f69e00d43b16bd1b7fad16c8bc725d92010bfb75bd9c02952f1d67d8de2e45023b31a72ee4fcfa1e3bc2f6a4b7ac04cb2b4db983d44e50f1f37d48834b8432f79e543c29cc1b743f679b37c76b08340e7b98771fe3e1923e871b761747c6af30fd289a59c559ddb490407121ca5d1c64a73fcf85ec115748dbebabd072c01301723b0090b0227f9ffe63835d851742092c8cba1550b7c9d6b0604bcc5f0e24560c33b1ae10d03238d2752babd8baf89ba70ee978cfffb0bd1bf8efb265357487ba1272cc8902329ed88a6b234e22dec346d240dc08517bd44d663b407e8669245a25d8a4b25f8a73cc6e792bd1120b5956080ec508a180d55562660836299d460eac87968bbe258ac7a59720951b8a919562e7e20b09cc693bb45d41eec1f4f5410a5ce80a60c692b302bf829582a553e7e13f548809335e9d34a6ea3dbfc2af295099e28987fafb9447af65cac9c64cfde2f840bb5719592200cb648846eca88f045480afeef76d32e12b314cc76e058e77a24811972b05de1a2d41848b5166451c2410ca5767aec0bea4f5b334db61344d4f87b3390ed355e98102ad5c76321300456f9358c251d5341d1c36755774c36a8746aa7b7863ad13bc00d59bb33ffc24825cf6054c702ef2b2e34239532e23c349ab70b492331f13cd32a4df0ae95641e5ad5c021dceca41a517b4cc87e71f11fc9d285a7c957123547746ea78f86a00094ba4f62f3fd8841085fa4f716611c6f016674db065de55aa15a9ebec6ed1a704f7bcfc0fc57fb146ed0563040d4d037b77022c5135392246584c204bacb313ddad640a767a77b82b21fb721c0b5a8a5c8cc094269ea351058975613d4c268b147086a8025281b1b165c6e01e85fea5d8781ec8e9023a204ded0beeccdd228a2073e15a1c023be9f568193aae64aa41ee31952845ebfc6283d0f5fd0e7130243ad3d5f0f0553a062583caea3729e70e5f2891c100bbf7c7c8da64252f6a7b48b4164301abb8a984b617de792f9ed9a69033cc5490b00708e288bb402d43692ce15b1a13b22e2912b91667240808c872d2d111e4a1d2aeb5118739e5a9449d9156d871c4f50f6b33092e558145bf4a19d4e8078709d75a7f88bdd9981947f89597d5d158a81dcb265e13b3328c14d820490a6b2fd1c733ce08cbf501b12a02a0f54eca8263409966759c364690d83baa2438f1a00f24f203db8a50d7ceb1900d6161061052eba5fbaf56a8560ea377b8331fbb64d9e77c0ca4c1f22a8827a65d33d48d28408b7e26c4fc885ff6856c224c334612f51eb0ff45c056fb776bfbcb5a5e0d4fbca4fa046e0d1f7d4ecd29d281a3493c7dd24421ace52a4abfee7100676437ab565794a5ce713a6e92044937dd3dcfa39782b689b004173414bcf67b722146e3390c334ae26d699014a7362040930b9350702cde3265ca77ee9798f2bd9f696555476205dcf8533a8faebb0c04c90554cde7529e232e56cce0724147497729e8363d28aba4e9bc5d96189c52ed2cde3f4b0542177a9a7e6ea9c3072027a303d832cdfb61079f849d2351fd71a46b7e7c75f6b8caa93ba0eb9dbf76f1b96bd3383f60d90bd039fbe7fcb6f8c0cc4c5ae35e1f7b2e4cd3b35cc9ccf9cb96ca4788a33fafb1a45b95620f1686ecce9301f9de7126a8b03782e8f356d7e2df1a1ecf5aebe45d740f6b79cdb565e8e64a10284fbb2e41d958945a585791227e83b6f1161f62f0e049c9d702fcec3cecafa074aee256d07e04b196e376905cb474ded3191330a2c5d3ceca6a1eefd05729d294edac1fbdeb367b326d970596357f5b21ea5c0838334da66fef77ae6be61bf37def4846efed2f4abf51bfa498c65ed13e87c4ef3efd806c00916c66f00c08f87d907ebcc1f6eee6b9424ff8ed9334f3692387bd3a1ca22b1b0bb8c1f9af5113bf22a90bfa69021195b4f729dfb82ab856060ce321270af04dfa71619067c08a9a18f28dfcf10a9ccdc783beff6d801eaf741589f579bca49e030a5d3ee9ae809c7f0a76408eee03df83c998fa517d05322df3235a7cd6f739e16ab52687f02e83e06a4d1c1ff75a00e7a901303bbe751375137c7752560c48960cde80b3aa3492f295c9fc0b4dc98451f9650047cb54195b2c72166c33a1dffb185427e46d9e10cfe675341b8e36f9eefc29a792688211de6d6e10765435a801eacd3fc4684edf4d08ce9a38998ed3ba8669caa0376314143c08d8f682c66d464e54ea26b232caa6b1f71db09197e558c2b4b076fce8ad7e3104a3e37164c38119939616c4e400ebfcd75e0667a39aad2bb67fed0590301278eb95f285640c00706f503fa3aa3fde4c6ee4e487c7869122433fe3e12a58deb7fcaafc621ff22149314114d22466dd3f3a18ea38dc955392a140dcbe0a41cb0c07343589debe9717ebb61879450d9f6bcb5193bdbbf829ce737b3e2f77182fc5336d91a32c89bfb0f7313ef582c023353794461b6c0c599ca23e8b92624f53f9f5207c8208ff1af3e2d2e0d69754033fcc734ed20258fdad9241d76dfb498de17e8f4b19840dec658d354dc90379fc6f4b7f7053cb777879c5952c7763f2bd844f51cec05d1a8d8ff99048e42c71850316c7a14c23b265b7d0424db4bfb0304a0ae25244930886f36b304a66d68dfdd2421a4e566f4dbd4aa151b9a432d511ef1f08ab4f879b5e09217dcebc5fa6b188796e0c121eb6379145d07d2a4ec54318b515e5854f4423eeeec39a4685b754fbab2139816a0123d5cd22bd2dfeb0369060903937d0d6c0e5a9d169899f8c4b261aea3bbbac96f5e10f063dd9f2e245dea01d6eae6482d43c570e65fc53c3b7b42290e31cf30f8f520069d2ba0b146b5cb28dce0b514668a16f7dce4c24e6895b776e483a85f6f72b9183e9ea9974abb080f7fc2c19ee77fbd3936eaeccec1bacde75df40af69a71344d579ad8a84d14e8c00f8d832a7beb24672030fc2cb4b26e21a191604b398e6bfcfa33d471f240ceb27316017b31cc4394b25ad471e24eca4c394858b450d0d5ead804d9d8e50d2a3089c2fa751eea7d9157bc110098dfb1b9ff8e3084d0990a9baae0f28e87354ea0e4ccb10144926b8bf4ff0baa6768300af34900c8f3b782c76c22e4f64b1cd5a79c94c8b9a4c79873c6ee11f2c4e666debbff5acbc34b8d121ec2c363a1614cf3631f4ce2efbaa094446650a428fe5f6272b8256cb2fb2a36ebd612d34844eeddc5fe4815739bd72330a71640f62080cc08faa9c6886edcfe68971e7acdfd26074943b77dfb7b08c9ea5ffe3e1e147b7481770228dfbb042dc3cb995a29c773da4b7965c29caf050ffc4065017f92b0f4a72d6fc0cc7f2255806d7ce6f7390f93526709b23972a57aa569f635bb8ad1bfce84abc5b9001c8c30eedecbaba9b2fa034e07005297fd1377445a66cd93a31a2caa068073eabdb32461a9e3cad949bb15ddde5d8025bef1ccde57ba0671eb284db6dc3e6866007483a2c87ea286d610860bd42e335678d22b01f8cd451e110a5708cd3fbfa2d0608ee647fcf75a607b851ab7a735107ebc8c7ee739a50033612a34f0f183ce7e1ad4fe7a13e236ee5fdcc7273a32c44cdd70bfd80d697c241583275c28f6f3f001f9be8a7ff3a68c01ef29e7333fb1ae435a1bb4d7daaf570ecc7521a28d21c2bcfda89e9c630f2fcb8dbdec4eb120d814ab4cdada240d252303a2adda42b927ef64b1bfc1a71c6c89bab1b1b770b13ccde68d867b09b4916a30b04ec96fcedd8c5ce3fb8a51f7f3527cdb6eb96c9b0c43f51968656c18b5bad57b2debbad2ed732eb704087a3b830b798d0ce480807b6e00a5e9b35d76ec55bf2d9b985eb2d37d9f72dec506f95ceb5d0cc38a25a258c86b735fe3759e1ef1c22189295f53a610dba88238259e30a213d4306d93ba8131c03ee6cd51f58b8c14218ea0ac9f6d161ad936471d1f20c3be10587a34443570541b9b010dd04592d5ca196e10d46611e37caa3e841372f03b9452dd3e31fdeea79cd1b1d47b67041fc450dd99355144dd46cdada8ce484a0fb1c3e69f97ed7664be60a8a854d7fb381ba20bbadb2cfa7e19c28b022ec02e4d673fe1e00c0abd7c53d5fc9d8fa86ba56406ed8428a8590ae2a96cc6841cc1718b9cad47a5f190dbfe1fc0125198151e99d955b065833bd68650e86d951b141b28c08851a23ccf2898f88a09a0d4d03995de5e97a0ebdc5ab0ba8ba1102967924b10a78a977919a3cef5511d88eeea965a68fad121fe764ae223c6c7bbf7080493fa00d9f86c36a617b82d5e0630e249c403bb99134649a74f495e66bbc9955a7a27146e751283c73167f5c8c9061d06fcbe1d276a1958157f1b7af1363764516ae090c2f42d65c0d79888163094bce8e88145f23c987c944169623dc8ba86f10ffd053a4f969dd33dc4f12454bcd1d8080e54260499db40c4d04545d119f989a40fddf7a65d3527227901d4a24da809aad9c6a20f17014c6ac92c942b9c700e494253f12175a9e2e44eb5a836699efed6a3ed9a07cdcd2cf037545a4806853016bdc27c4916cdd7be912242328954bba8f909259e4267f4030037659e05085e6d996620e5782c7a4e663e635afcee0487668040ba91fc8ae19d8469110fea340a8a7a01df41dd594e3dbf008c1e8fc54b3e27ae0d5d3b035c1e9a7b4d5a9632fe153a3829ee267e3169f519dd60f0e259ec59b2c2f7d0b5292b726d4ed0e962da5b257f33f18d49adcfea068d1fcb6ad79011ba9fae0d0e3d839c1f2d7d4c1b97327e483a3a2ef7907c356cf5096c61f8ec2df152bac8f7e0d0c4b17bc8e589b5a7696301cb4f71a793ca5ec2af86063fc1cd8c2f3ea51e4b173c4e643aca2ec76ce9869b6eb639a44dcb6cfe62d372cd1b6b1aace612355dc55fa9ade9216414422fd5d678123822e17715353c0c1b91f885ba7a9e068f22f44e754dcf01a3117babbacea3b09109beaba8e759e8c844afd4d5f6303c02b1776a6b7b0a1a8df04bf51a9e858d4af84d553d8f0347137827ba895c69c6114250911ec29ca91ba464ca2f3418f6fbeef0f9e9b1b679a670a9afdd966218a02c9f9c08e9d8b17caf03f7d8a6482ae9eb154831d357681805ed777cbf03ee1d8aab64e93a2bebc02029955b3c7bfa7711b803380ecdc3b3cf1b5131cde100cccac7454a9f6e0301c5cac9ee63c8d06c6cd2078f21cd44ee1ba2168c3b2989ed2959d40c49d06b1e488703e84084a918fe181e6a84e4160cde5c3a18fedf9331dcf2e6cfc832720f7f672fab0ecf6304b5b3b224b7a517a0702dce6a09530cf3fd4f282a5b50842b71a45babf81b31467ae62373883649b0534a8cdc625b4348857076190aa38d14b82f5ee68fc741a361f7293ab56d9bffc7b2a9e3feb20577554e4e50417cc5d57727bdd73831473628289d9ace1148c29a67cabe3bc8514f1a40d21713036db8a05cb78ce399216b7067b55e15d698c2d36a5b0825b673ffd78ccd0aa1ca3433eef47d06d1ab4132734f6cf2ba11c783e7f75047763dea67e92d09396f66750eda688ae26ada64ddd02f19efec8dda4ecd05c1eb9f8535acb46e271e0c75e8351ab4906a3d35c3b1907a288b52a9b08744b7c16402938f669e95c0fe2631f2f2cc11caff01824213a695a6d215c221e9e89e93409d38eb2394dbf179597e48e7ceca4d0210fef76b36dbef2413b71cf430443bab29808bb97330880db7fefca0a333e19f4d857f842d5707a201d1754d4bf0e678a1ab57cbda0f8c9a6b7a9934695fe8f7b770c2eeab3f7341c46b914bfc43cbc5e362192a581b175586e3a29564df5959fdaffe044685896489e5a103edfcacabbabd4b304d551f70ee1965c4862e59d09c43756f0e0e99038fbe66d5debda60b344ac4450899e546059a7e6cf5f214ce1f8c4858baef651648185b1f93998526d14194a8e99e92ae80d45a9db24a2cc871cae5f45011b9fc01289e468b533f0779db6830fe83349c55a829b12800e9b46573c84d8ecbf5ee3ed7fd64b07622824a96e51caa3a4e01fa979455191e0d75a3494390a6f4bfe1dde47817551abbef31f587c96551b71d7307ecab4487c5e461b8a027a582139ac821ac303d40cd9fffcf1f8b2446a08b07b0e6c8f8e061ca5800fe6e058a1fc8b4e6183aab3be87a12fdd93498855c2f8a3746b7f6989dec4c0ed433ae02de0810d4d136df9638bcdcebf7d872de248b262322e06d64b5a7797bf9b38a8e249c2bc2976801f52a8d46feff8455751888ddb4098355fc18f7fd883d2ecdf10b7322c44d8ad3fd0b339251ae4ac3f021001b00c710a48db83bd32d55b9b998de7c2fa87ac93c99bb93879fb078282a82c89478072ac70813c53946fce73cff8378f6669a80236f161cf74cb507b9a29509167247a60f314fa44cba4c0c8514729919854042c14c277ec0614a5f437eb00d52d999b56a3dff72690e72b21402732eed4911092a024ee1793547ab0eb3d5b54e033587f729ec2688d1702f157cf8bb844378fe0ac3f261ca1eee5c65f3ccbafada603065cf7daf35f9e092a12195d771f49e80553c3131dbed6ce3432189eaff451dfcc71973c6f002f987f9ca10744af5d6bc43a28f1cac3050c845d9a9c0ed271781cb299604f3c5a8595c8622de59d2cc165530e8739f51df3fd815563b254ce91888b70f8f73e124751821114661c8ddb50a46be157d39ab764c4b15134ebd5e8a432588392043c3189447a2b0ccc1c4d74c5a74e1fc888fea255be9ce2857c8381555a16da5902934af36ad58de9334d17b529d6c7b23ff3f149b8c97f65ea4138cf77d78ec8b2f7e6eb1e280aecd9d30e923575de827f1909baf3f2c70857f1a8bb4c7fb35964eddd5c695bb64bb58888311835bedb47da5dc4ff515a18e7c970ba780c473c65473640e1f3e9032ed9c875c7836d15b46107c3362fac3e0b4a31d3621191ddf6b73b2f963362cbd7a67b8e687001ab34c55bd55d9e752cf1b86e8eba61a52b5bc30e8be19c3e58b9cd48c98e5fef2a639b6d947c40875f6a4acbb903c3af4e4ba8037e58e15ae31a0c26c2e87e5846076099812ed65292be56f97a988e4cb5c10fda2ad873fbd7acfca3475ae9509cd2140ceca897458cecb1f91d24a461f54bdcdf1b425a95df79f9b71280f0985f2ed1f726cbefa916f454793374f0fd3113339252071311bb436e6cf26b864b7e9e6ccfe7e84fbbdcaeffc89abbc64c4ba75a063de028752b092eb96a03642f933b742c43c5e783c2776cb26e0b0c101c624c3759c9ef05c3ce06878d415b6cc8148238f72e9e50ead0c09dd2eb50db7d68d1d9adf23ca83dd3d6bfd51c3e28c9db8344b28d098689d1bfe44bab94f3d566bfea0509010193ad62805eaef6e00988f00f28dcebd07f678101807824f8b55ae3b2e366b1a685836dadd41fa3e97e62c9d79804070ff9acbff366792a5c4f2ec7f3b24f3f3bf95021b0bb492a6061fb47827046f37097823750c9b77d8a92df4c3477dc6aa99a4a79ac2859ab165d7555aba7229a977a853f17b90eb196143cec4cc61c1d312f24c303502d1aa8970c3bf7f88bc8130dc028d8b58b338b1cbe0229ffd308416f676aec9b429443afad1c4440f092aa3eac920ae3542213264238f4a93182e35b9c2d7c9574812c6ab934ea13a3090339266f6a3c401165d4627fe56c8b6c4577c7a49bcc7608b9084dcd7025b45949bed19991c8441c1b81ba0e65f842fb721710dff1369e144eb3fa7dfaf44d6d22ba6cc774e1949ae065031f86688f19f48b33bfd4346f60f50e4b4c734909f3789f02aa60879bcadb32be2b3e83c28daad90077158ffbe79e8caddf7b627792c1cb74b21e39a41b4b015ad774d9861db09e48f519aaa6ad3dbbb7ed54c34b4297e1727433f385bb7e46bd9510dcc3fad3d56d249e0b62eec47ab02c1ef9a373ab57c0855b0092728a469fa1da0e817043244261eb85c7bbb04f0d3b22f6992c2779b19a5b750a7724f03321eae55faadbb4c818284922854da6d6cfd4368311dfac42006f58b66e39a064322708ddd1da9e5f82ac9480775cb1e16ca37372b33c2931a7b1d3d18de1237aef6a71d13f28b872e6ef50c99248fa57dfd2a7bc7040c941e0ee428effda06d36f52b54550326d67d71fad726c3aaa726e92e4ed5cac8e9d797aab19a259ed415c2ddadc463f55eef98f2728bfe24726ee179edd1727f2bac6f3a274663835ac2497d1e42211ae8c90044f350a4bf97355a8be49bfbcdcb38229cc26850fc297f991b924d3df5b8115ca3be2220187780a1a9ccacad0af65efeee5433108360bdae2458e9093a8ffa8b0896760f02b7c4c4d6dd1acf43aee5c26f3056a43cfb56d4981388f2d3ab47b8518df5f7a55265f283daa7304474bb3de1a6265d7ea7e3bddcbe07b84f1e31cb3aa9c32c126829e137c6aab0f6f658634719a2e96a5f438891f78cd6afab39212059f23b0bb15c722177a04bc1339b89a41fc1503d27bf7bccf1fe7db41fef7364f98386ac6d4e0f1d63d93923c9f585da88ab7eca1dbc954a52b5fd564ce28c4d7bc3a7486dac979d6bcb9a97a35a3b47add727f443498cb8998a6923e6dc78f3face0f09c4e819916cadc8b9bc5812e13fbd161897167ac7f85d1334f683eed2fc2e731fe4b71c377b63ac8abb4f33a541c4098faab7a4773abda227f65cf8fd10d57d295413629ea9008357742206b28d4c6100a6c1450072ac52766d580d16c253e16e57e4418490f5559039d92108d053d86d231e92910185de0c6522db9393fdfd62aee0a5973371ba7637e44f741a722ab53813b428dd7f2372e62ec9523dea8765624ea65e698c45bd8a0abad8a669d191f5737aa6409f96eef7d7f95b3250e5ef745f04f8f44a4fdfcb9e0cdda5ca1d4c9e23cb372b07f808b422f274235410d0c5c25a03adba156f18e722edbc5b26ef4988ce90ae4f965cc195ba0e32dcfd3e9855ee03136ad8f7fb25dc48aceda482787c5bc1cbc2e314a8d72b042e571aca7d4bfd04979ead16098420047db8e32394aa6754c56c47d0a549967cdcea5af1c46c35169ee121436724f976b4ec83dd3fd8c237b5a7e91024652c4e4cf1a2fae336f5bc5276cc6a350bf0cdf0fae07c5102f85f3dcf0dd6d312143eda9aa5ee49a960a53af4d34dab428b918f0eeecea0edcf452216ccc44613400d6c22cea339d49639321d67416aabae6734db0a0ba0438b7e391340602491e1f541b01afe6c35a00177ef898d5f8c703085e346e05df4406896f18cade50852fc130eeed0589dc9eca9671c136172332e07241842be47aeee33daf3754017338c7518f75b7373981e99c78aeb353059af6b030e4c607a31f86c88bbf0133cdd0b42086d0076cee68985c74fec1d93359fc3252189f9c34c9630d04867afbf6e846e389ba3fdb6377d6243eb995bbee3f09de1a406021606f1306caa852734f18795adbfcd7a9e2355f3093966ac0718b812c105f0d0eb987caed73ea55e035c11d37ef2c48f09c840c359c294e82a0808089cfbeef400d87bbcfaf433769af38fc19d1eb7ab439c15a9afb4e3becdcc861ee5b9475a458027f1a17da5fb17eac2d66708f237c758c47d5edcea2170b9e4f74cad5a8f6eafd93eb61e0a81349f15cfe8f10b1f42c873baefb0726a09818e53079c6f27e63117c20c2ab06ea1795f2df802eaeb3b1536d3cfb7397a963ae1d71e127fcfb5b6fb3ba2ca12a840a492032de8cd698b21534fd50dea84fb812afe95008914112d29f278b9fe7cb83044de9a358144cae3537c00ce605d1429e9bef7ba3b2229c2ccd59e5173e9b620eaf603bf661ed45dab2d7e6825af00e910a8a3990b22e73b94c12807ca0716b5de89d4402712f666a846a3aff3ad01628f522d7815178115ed2d1035a8d4f702fa4ca0d3253e4f2426e8b4e8f703259ab51e60347e5ff746f06f42eb51474b4d6bc93ea6d4064d046682961bc3932a0e47d8159762addd97fd1ac06c08e5c2bf594db641e9061860f14a02ec1e5d35154ce49d9197488004ee354ef3d0c8f092a19dd7cad02fd0ca7f33cdec684972a339ba694010b14ddc1ffd5f7717d298daacde5de4bc4c006fe878d531d78d696b90a14599c10978036ab1648cbcde6911a78dfa0e4dfdbab8dcdae56fca06f556dd75b3b2be043ef855595f20b09618e206be2a4987cdd34be7fedcd9411db2506d83eee0b9b7b5b43ff19459027b899f7ca6a3b4a1dc08f78e8a897111acc455f8281f80f296e82b8650ec1e2d625f43c0143ae40c15f8bd3c9eb063dcfdaec189b21f95c6f1f0bac31a5c49704205c7795bda28396fa7baee3579232262a1214facc07b5698bbadd447a7c714dd82bc863754672afe722683ac8e0fc7a95babb2c288e8ac45d61f53707f13f3ae3812102b0fb7c11879871d6a8310c0470a34febea19c72e12fa2f2accbee2dce7da5c05810064800d3900a2ee3810b62758a43f8b91df5fed942413f5e6e231a733b254cbc8ef2b96623f75a0c52d5b54d6733df6f14f0abd374f75a7d8adf810b77f7805067d472130fb921a70dc504409300b4aa0eea154392c503b6721b56147a4ca6f234bcc2376be27cfb09fa27721f58878d66ac2231e36114baa0506da92412f3a8c5f8f4549a8a99f2ef3245c316868660645bfc578285766392577244f2c4d7735d37e1bb6e879b3183cefdd047b1ad25aed046b80f2eb7f8d01a2070004758501ca245c3c1361dd166f6b255d7218720e4aa7793293946d17106641b15feeec1dd108c5ff616352306ac4e82f6463eeb81d14ef50eaad9e4941176ee900ae50170a0b6c54d4a7fbab1b42c5af16b8316864ebccac868063a4dc27d83c3c36a4274d45ba2fdb9eeafea711421fb1440c8a63429c5c255f56db7e58a33893f91f3fd9cd2664fd413aab7b948f3910c2ea2c7991481a5763b700316508342db76f8bef3d24116674c721ba6f6c9b9aea7d71b82927b9a137a7969ea5ff70ac10bb5f302114c36fd50e96ee5049ef42b879825d73df1035a93b1e179c288060b26878101725c1094852e7dae06db36ce0f805ffaad192916ab077f6ad6c443c82db1f7501338e43aa7733b061dc739c970cf1145d32030781cba955999049613489acb3536ef03dd5a90179e8ef78e23161e779c175bbecafbb2ee627a77ad512c00ec668eed1fe6092156c404dc78823c5d62460a7c01f16f25e3094e59f511a017694b95e2294740dfd9603005d16b120c6160950b3d6a90f4110b7b1ba949991ae56741935771b024a8397ab3e9c23e8d77618670273fa99666b6d3d851ab3223782685de7aa3c617a92ada5a9dc98d10b78a7969cffb0961ead3b3eb0ed5ad2600a19965c568d55643e0d35759e1ed21d152a44cbf0c569fd6cc9f9ca5a2f81866318e40aaab5124ef7caeca3fe7f7df413736e4d96d8b0c1f4c654e4239c2c9906b47ac868671c39fdbec5292fe9b7c90234001983527fe752798d311efb6fa769fffd3355c37344a98826f3e9c4d6fd57a09e750f2478e022885b48cd300059c67f60eb297b2ca49088f2714398cbbbe106c64de7dd7fb7c1bfb032ad3303b16991e7b6c30aa890923ae3081f172e5de0644fe4c528728cc94b90b90550644943ea34e9e7c78c3a888240da26524dc5f67fc460242f4f79ca10bfc0d9222bc58b528fecd19cf09e48f364484cf837592eb3c8fe2443c1ed2c80bdff153737efef0f6a0f8f684ff5212faaee3f0a3dd2ea8e8bfa80f36d70bf9ce786bef909a2d94c37c47fdddd0e3bf32c4e92f85dc3607b39c4fe8b76e7b72527f6928ef0139f9f8404b283fe55dacf40b6f192a5e1ecec1e672c2cd2f7fa932452c46743a5672a1bba62af3c1a892d8b2363cf2719965f7d0106c8694421e5cfe4dae0f294f71d02230e80eb3aebf84ac8a7da52433ea05e66774efd602a0e5c4d804bbd7d776f485f176331931efb1bad80f6aee3c6b4277a8ba9f4f1cc7da4148a7f826e876301c4e927a8644e32843e70e7560bcdd29e24cf3eca24499bb71da440ccc831288db73b8fa73f33956a0eb3228e77024f772fbd227f8c818e8ee0a1cb05ee37ab3f37e002881fc7799d1995221cd4dceae4e10df738ea815040c10f16a116362065cb159e1cd019f8f3d12c717c3db15f4f0667929d4c4e97fdac5036f9ee3101752792fc00e6ff1f391663cb881878e7cf0eac818ce4bde908439431962c79bf3c3c9fd018ce55c63e7d0254619b3028f27e49cd3285eff08f2086c85b18974646c3967e0d32588801383f9510a0d7d89a46e138bb501a03ea8be809a013128190082e256e9330ef90381c60161bec78efe7b8da1c62c8c7fb250bd71988e9cfb28600a48ccfc80c33839a4832f3b8c330ecf3da44861b6a3a37468ec1116fd800d06ac8758dfd7c5b4aa5170869386e06c68eeb86cc45358ee8ea798a873efe05310a3f2b470a0eb544b6e26cc831fe11eb3de2067dbe212d686868a328367b8eaccbd24aeb4e5d45deb5f3f36107db7da07fa4f1cc030930b9c0922f72f339b897c505a3efb284066a74b6c3e6f45c5cf88eb87a0ddf31fa1b8e36926aaec98ddec110c764cef07c715b216a48d9a61e41103d7de33542201ad6299d7c53a19ebfabcf5d45d85e73e089b2bf493504de334324160b4c012224ce77a3c5e394aea91d00a1d7bccc4a9be8923e3d7568e994d04fc5b876e1b60251d931ae44cfa28d374f1ee1f3fd4dc2a0e95314465980bb1151d6bd3cb26715018915c022a853dd11ef5dd9db953eb6f9480aa3458f1effb3661eed29d9b24fcf9baa19398fd9fbb07e3717e5e17432161fb278d0a8d464c5eca1e1310efaf420a9769cb819470976946edd6cca63e2b38a7a4ea06c63be22e45227cb3075efa6fdc24f62ca7425d8c0d2191ffb0f30ddf3ecacf97d472bfb2be7dd47f20ccc90387fef311d8153a79e32daf8b2684913137e777f64dcfbcdb91cd136071c50e714511a3efd3e731aed09456fa13fa9961dc093d722527c6f61cfc59c0a665079b08f34bb1edc3ba1d91041682cbbe4656d408c9843150bbc04e34fd7ccee36e4f8b80dc7cd5e5510187db841d80677a023155ec042b9464933aa804eb93b580cfca62890b368425bad0104a64f73cdac6b07c6d00c36bd919cbf743fd0e42a91af98743512352313b0d275cdc8bda81b44ca5724c4f7f6e19e63c3feae7d8ed9d1530eeab32ff1f041fa96ddecd7118d3bfaf5e8aafc3545a7e627a968d528b1281f2152f0c1687bf9e56ca7e23d77b11bfa113e69c3f2997a6eec1aa5812b90698c6cc7f00c8057982bc51115379d22d4085f10465b03747d0b9b0b47b834a580f4ba97dcdd22a2c1a3017150d32204275117fd7e2c75ed356923292ea34386d5fdec8edf89c19116c31d560006a1493847f22119c66085bdced7c85fc9b604e955d6fb9d99828cab25ad723088a71d1f64e064bb0837dec016b1e9028c997013e27ad4a3cdfb872ec387175b4f0c62cd651a396078d9d8b5d9d873600928e7d75aaa462f23bb546376090701801d6a7a3c4c1c1a7b76a6240914658d661b1c293925327e0bd10d5666532a47fbf599e65ca86943319e740dfb503599149581d0f0d19548aca10561a3ccba8ea6722e260eae1ca79e4fd398f8424e6464a912c270c836fba4ed438cddefbe23c92e272996d0521273036272fd5f21a2c3c1bf32170d94096aae6dbf7d0003f039fcc0b8e7cfe104066b71693e4057d366954f5134abd0b48c25ffd36dfee0601b72d921d0915892222a4ead1bd63c605c790f414259ec9ea7ee883c83579358134563511a0e570e95b97046f8df4bb002d9092e1f4d703516dcde7084a3cdcdc3d4345470549eb8e72ae9046b3d30c3ea471d915bb9f673fd1a7bc23b09f9e2c1080047a3c306e20bbd2a7314e4a38fc8cb393cbdb824d4a7e411525c230c9abdc2d7ca6ca73b216a554148c0ae5445424b84971bad083322997fb0da8fc29bca9dd20c8f642ef825a7de880248bfe94e960803381e0cde36dc5a71fd9b053f6c8d7a19e54ab327104658a430b018426344f1dddb23f8fe64d60031ce0cf4a1019d2907cfb3d4198c207a9af60bba635aa4caec049fba537dafc2c098410761f1a0fec9165dd119e9f6d96f3ae6b3eee46daa66d7995f7f7213ac826966c33aaf261c73b20a40b91d0517bb9afc1fc6a3a74faceef666a4074aaee104c135c7f45a44083c0d35fd13c7a2842dd37389a069454793eb79b5823a25202a9a8bacfd1e6d5cd1d71a7fd61775b426d1b0c17ac6d0730b8107c248edb807bde1000419cbf75348140c26b8dc31693765e35cc251599510cba8c5415ca9deb3d5d4c512d4b2c18d7eb1dbcd790ef68f716675703bc785a395971d77c3a5bf81d804c36d7bcfc5eb372127b30c90a6b38edbae78a3f53322d55aadfd0f837e849f90b2ec8eda5ee519b8be19a1aaaecfd1e98c635a0d5038bd234c58eb10b3ee5ce23123f8454c586bc1f19b2121f30e3bf15932d6068bb52cc0691f24bbedb566ba458cc5b95ffd4187edaa63d8b1667085e06d4a5d22e3a5cfa2c162586d05a63620a8b080e8eb61243c0d771a80e17cdba05f39a81359ee619f6f3cd42ed566b087c1c1a11a02871a1324e47680c8e59ae17705ad5b1ecf505c191d3561877317f7b44d255ae952e81f6bcc0e59e0c401ec738ac01a159d42dfc7066b0de86951e2b77317719c3d88177bda7ac7159a65c62742ff582e91664d130352e48ef13c927fefdece2c41192bda9173887749e2ee26efb68d69213a3bdbbc87eafb47fa723d806d6b45333ce1a2348cf566a6d975e9d52c664ad93c4b050325f868a364fd2e8c89f0db9a3347c7c53b4edaa71268e70a28caa492ae34ec4d9570454b596592de9641d3f7b624b0f45dddde2222dfe8485d22ebbd65583c17211246951fd3f809f1f9a3ff0acf1e7fb9ea6b759e7ea31bcde103afd35f290e48b944c380a4dd3ef669aa49c98a2f01629c1b2919427345e27fc0b29b36f24c7212208fbe36c531e73bf2313ad5544a76e1812adb662ff95486a6e73d8e02f5bd52bbca26ef0b499ee09c8d7741a1fd0b92865e27e73dd4f8bcc541b1d6e9ce821dbd8e1cf2c47e31d4fc81df97a3df7268f4bad1f56db0509190359f71c7fe70ee313d49be16f6976ca636d801a52c9a1288a5d7fae906e29460ccd6d86b196a65c5cf1c6356870a897ba030ff73fa03688bcff69c1fa04393ca81ba3263b117a99a84ddfef97b0023ab0fa7d928b5dadd813dadf27087691897a9edf70740f81d881e306f118f9b9fa445f7d108954e9b39df5e5630128d7be14805b8f5c817a2be64484bb12f07f08b1c7cd06557959a4b7b12063b6b0b5647c1964aa074a86ca043c5d20f1a272bde11fd475ff133ab2c3e561fb69f2a38322ba33a150ea6fff6efe2e80d51754946e5abcd4a17da47a2e026046d578268862e78b5891fa442ac16a2081f232631e00227acabb25e9630159d7b16e613d5f86d1827f9d195e6fd7b883de7f388efcfc60b3ece61873acf55bd35a5154df1067f367f8b553d8cc8a34be71431a571abe6499629721625f72def0423920baa11fc414a6a0a60668e5e90f0a37fbb33635c7e4613ea9da64a8598448b464727c549e4665069c6354f33bdb8857c1721c78834313b1a79e2609c57ec3b80b90827fdf66be60b7a42fe56bbb94e486cfc3c1f437029ee582864b5b713d5493d2f326fbc595d38e523bd9dd412adbc27bdd8c05631188242261c01b3adddbd7d9400d5fa3e6fe6287b0c6ea7ebaf72a70b29d18ceab746d200b1ba8ec6669f6c12669c240e70372d8339743859fe111c5d2fb710ec8f934d15e14c909030fc719ae936bcb71df5970e805e3097fa2bae76cd4878edc6a0f62d1df707190892e330b9688b82e5964b469c2330b30f1243229591c64553f5c35b91aa33f694eb44cd370f13a3368e92954d04a0c3811da854cbd88f580f0be567d37e35292919e197b0df2497af534ab9dbd24150732e7a6797290d7d82b6f700c4053bf62ab7963c19329cbaa398ba0c63367cf3b88ef06b30214ec3b5a77b1de8d858330b58c6e5e6549a9200d252bf092e38c252b918b7d30eb96a727ffa6c503cfcabcedf12c4150b3b74c9da6f2faa908490d73815bdc31d2102f6241ff1f8071bf237ad2de32d775d0937147c5b8e4db2277743728af110b8e198a18d347edbba689dec3b9c4ad7415d1336d672d7d57b2ed2686bf73e45958e31c85780b853b2c35f156102b219bb72f91b253ff69405fd51da95c45c00a15fcc512779ab51f6606b33f895467449ab5bcd4987f54d7b94f320b056eab033cc912f2b72e8ec588b9a69433cd6378d39cceee621d5e9e65b6674db1998b7808c3cf7e7184c84a232d0a2bb2fbcad0b69a5155c4983057a34bf802e7162cafecd5501a4aa699bef00c1427102362a8ee0c939405437052eb7795d6ab6afe7028aec2a30fdba416552a6707aacb7170f34a704e472407cc21c99b3910df1e83e901f143a2cb403010532b56578dc00a89670cd9fe0b32947cb546dcd472f35ee47153379b2d6d1afb0dab18901a51ab339a1547bf0bd6e7a8f453b0773335b20ddcc30450c38d856b29546c784195e017bbb6ebd2f18fb980f95cb2e17f34427923b66f8eac1c0a5e00628e4f0e6ceaf7f2223133c751626e0cad48b1ebbd40a9d3f8451a6286c2484d96312d33c9c663f96ec3bc7d7a4b874b3a6114493d07447e55465f87a686d76414c9f8d41fca0110b8f41a67488a849df41a09a2870190032e6beda58ed15b395f94b5eb991b750453ca8fe1ac1c45d8d762c1af8399df75c9eaa13ed143ae5fa1d50720dbef1e25249f13f9df9355e3f7b7f01c80145469a51dce75cadb9b971beb1f7cb8b4bfdfe93d8f4d000e773dd876dc3f21f83ac0694dcc7d3415804ae94ee2974ee5030ef561611cea9e37af168c2d5cc82a5ddbcacafd8414eed8424e1db5bb90130706e3a66979f7487431ccd5d77dcfddc9973c9f4914bc1a69a00e89d42b42cffcb51c5b0dfa7544f3076912f0c86694cf3f9ddd9d9a2dba554e3a0dba44d7ce7adcbb544155e9b4755d63ba3a9a15e231ea305b8f9f8e96b737dd5d54202409e9fb9078c462f6264ece361a9100cda0a13b30a2ebb3678113e69b658dc0dac79d1775edf30ed69459652c508cbfe026694699caafe0be51f9258a294506d5a87f4a6a0e7b92dfc1cd7c588971fe782c896e8574b8a08d873384581d40d46ce9b2f60042788cb456813c5492396e0ddecae8c3082a661baf294b357372b5e9a2a598e248f68d28868d479f22c65cb7beab16e5cc7dc0f862bf332c582639b2e0e7220d2292710e8834e88066c56c97a5aad6d6eeb052a217ea88203a01d501bfa1d2508f6641783d99cb3050470fb18a2e3578684d8dcd87b85b17f67e6d6ea4540b40895686107cb4ce002aedb8f8d4c973525f032f98d9c982254bb6ad5e736987e45e351d934785faf8bf4aab7613ecb62b9afb418e4cee10f02025870f4074d205ecd2525c82cd35990cf449e342c82295ab8629dee5af22aba75085a2489096754bb4816a3f1d9432a5ad13a4dd391c3e23e499521c4b8195b70c7176ad7ecd9c583aae92cee1f829af01fb2217fd7eef27e64d8e99818164b80375ced2c3ed0fffba81703610afe19034cb4131074e3db8752672c2cab30468bf1427817623dc7071d01eb47bd2164051828ff43a77f5714a2c4d352757fd4939057bc8c035c51986116857f914bcaea17b378cdae49032c5702927370d8bc3599bdc931e397a88c3f3df5cb95ee34d52891c3af724f7be0de3219b189860a0898122d5f7eab63258a0d221feeafedd7bab567c0978141fb340c0d4c34ae803564d95c7c2cdadc6cd336c0f72fcc303e813d4a08252312a9db9bb90606c139608ba8a135cbddab032710a7cf22066fd6cce4f502c4e15766e446ccbfdcd7d251f64c8a559a21aa4473458fd25fb93a0302cf94d205af18d0c780f3a6dbc993e0b023ce889cea9971532a2020ba1d38c6990f1920360bf2f5a29db2c3b8fa0942fe78ac3d5b0f55ff1cf3ed0ecc5a05f147d84712a8a38109581b464024b738ef21217f610922fa399c9d2886135430420a711ca9da665d81ce954bbb3f4b7720100c233c2582cc4001b12b5af189f8980a3c5f85ba9de928918c1a3853e09fd00de8163cef1484e0a97fc82ae7e2fe1b4d4ff8b2e308c20c8d3358eafa7eff2ec5f2ddb6187a9f9cb18b9666d27819ac8f527a24e0916a69dbdbfc0335d29878ccff9bdbe0cc3e30aaa16d48fe5c12002a726609bef45fcf3cc4e1cc7fa57bb37d575b2fb9d6a4a920b346992c9fb3d840f6c3ffb087786288fe2e0168f1fe2040e2c30f54b10dd92aa1968e723d5231aa469e5faf0107d6f5f8ed0dcbd6897774d785e5566b381ecfb99ee9e92dd40a7dcfd958bf02277b2ddc9cc8dcb66c3e7406fed30e2288a68b2dcad8fa7833110bd5f9ccdabd5305ef01009e9f682a4b75f7298dedeb0701de6e23b816823121be786a364dbb354f323637348b56ff0001cc35b6a880e3254d05750c57ce50a77bf9a3150cd7a7ab1a6b591bfd6914baad0d97fe7fddb750cb3a3c43f91163710eb1b229d5b98bb5b2fbe48a368146c181325d16d018a54a8ec7b1f7e82c6ee76355d4ed5db9502805f4482a2c2945887773230d599dd2af2b7cdd24cf5ac719b2a88c2147882455a23f903128f8147533cd9c0ac57e64432557de6f43b34450e2d93372881097f53513374f009071de6ad8e74091e5d8ebbb23aa49f85626e2f5136199c8acd9fc2d29e45677f4d575361aa760f38afbc3b6f46acba5f83f2afa6e1fbbdef2ac707a241700d9e3e5eb7942be4db30ee7befaefa715d413fbdcda29f39b66d4f528ef49c507fc63e2bae63b07485d9a733ec50b3c557f6a6f1e7b21fc6ec3b0a46d704f059e09b77952c69f882af484d1df1f3ab0d61ec0dadc8ff3c9aa5a7549e5bc9a1cc9403268736fb5b880c2ff7441e0d642b1fb7cc1ceb62bbc215118d0035e80fbbd06bdb1e5c807e504721701d413cf90c93fef433d3cfdf90b971ad8ca5602da2a158523553cc8103e451a4d4481e366fb694bee73c512d11b0c1466fd032640c1dcc8ffe367ef10510f6f56cd9ac461af82c84aa9c29ffbaa08772b0834dc7e3c8c94992ccf3d85f9c30eb055897945fe3f8f5c20d075412270200ede7ee04431cffd05a3bd6030c6519b2e9354274183d3bebe84df615bb801f5323b56866fe6724c71389c59ab9c8e5659067289db9309a50b67e00dffdf9491214e75fb039732712c8713509ced0131c0b4a70195ba4c8ec0b283a22c7b0dbf1fc6412ed2c4de82859e6abd3f51180d0d804e499c2ee45002613fb99f968590283d52786289f149ece6461deed0759ac6e676a0a9b298aa163f0e24c96a0a98e0c0852029aa42dfdab437bda93efb23edea1a740ce383cb35d81744fcd9a6be9ef47e2542f680061bbbe4975e70d160ea6f6f4e737178e789e45eade59957130d30e5aae84a40444da2b6f30c458054be0608abc611221c80d3a194cc2d2566904c4acd1ee8382caef84095c2e80f4ddbd836fab2a862108b2adef33ac9471aec7765a995367a901fb3af6b40309937d205777154a7262d31cb03b48f16438a9187b742410d097dcdf4807278fc11c35304fc9fc868ef696323aa5f91aad21ece631fb45dbcca5d43f5c9ff259d530c306e5110677aa345a0c11dfb973ddd11a4c50198f826ee3306febb5c547a17816183f00237edc975b99e3288bedb4c40a9b91b95e65be7e1241ec820eea49089dcd61019cbe44f6869f6be914b33965f82e20c672815c3e932e2bbd637e38034572e01b9c38a9b80a30eb4af10d4152773487211da08141226a60f818a1cab2304ce7f435e03154bb801491fffeccff1f8c0c02236c5a52201e7731cb094fb0061d569e5066e1c28f37f6ddbf859dfa58b4588a93b1213981e291efc2995fe312611349407e3c6c9e27cda85453f72c75f611bb62bd1d664df72a04d9f7c54434f64561a4279373b901dcbdcd9e56a20630d838b2d76ccd5112389233ec95006d87476c9e3ccd6c376f4035fa58e8ab9e1d60959172e6626dce3ff6db707678e462f99579557d2dd4b71b353fe48e9ec7ad04c55576a4971e8b2bba9426f56db399d1d25923a3c71e7c0655904af4139a1aaeef2453ef0d6420becbce48d6e5041e0410423e9742403c92db38e65cdb7dc290c2a92f63380f85ec463a07b28960fc40456909ba96e5d40816b2e1cff8ce8898871de7feb394c739a1520437ebe9694e7119a08d221019cf12846dc58c1451bea34307450277311b01e2898618102670acc8a388819becff18ae7b9e900fe53e894b380a0a6e60a897e910d81229d525a9180029147f02cbb0366ebc698d4d80fd0a18697e69f883d6dac496a5b6ffc42aa9763a417db0a37d9941c116c311e04fdcd96f01b84ed058c7cfbb2a07763927e43b936d3e312eab38e802890736be6303755f5fdd8ac8be41842987d5d9f6d0afd87006448cd51b90ffb430c93e5e0692007b82dba29bc0c138c6ef8343fad430a23146ed6df1bbf1b36df6dd18056e31bf29040782cebda4e26c9ade103a369bf8acc57d07034460f170b6b80a0d90708f9e0d6ddfaf7996944715304ad3e42a5cc88825806eb276aee9df8e20e1b533cfc0b9f9269caf8c0b32d8056057ef670b96af062537027cac8cc8bba92c5a0da4eea4880358eb62bbeb5d76e792e1c586d6837de51e42629d15a493a874c9feb8b7d84ff5864a8fadc03367c980e5d98e6c5bd3383c677e485f0c0c9dc9f49fdf2edff43e8ee514e5000fe2423f1c65e437c4426aecebb0bca3022b6324f219bd824d42c8b981564c802993bf3844ac9703b2d2fe32d6f0d555575df68cf1ac49bb28cc564aeab893942a2acb4810ec39017f7df1fa9f276a25ae59dadc7e298ad6e40b82c1f79e1fbcd49e01de4e754a311f4df428b31bb2aa251bc4fe3cd0be4a23c32b1eaed0c29e930a1161cdaf51e8439452bf928e0a600a4b3048c52c00d1927fbdffcbda35015ba1c93e1331c7612243b4998151df56e69c169678da810eb54cd7b4566dec78d3089be15016fe9bd9e0784c7e94e44039aa2941a77afd746fe51e152fd4963479267a3a1c4299ae0ee2346dc75eb9c3d06210e7b4394370c59cc613e0af1a5f316228fc035ef1027b34b8cef22fe179a3c345799fee6932d953abedf3e53653c76157beed0c142f8a1d8ed079b03be5fb7bc6a60c62cac42e0a905e0193c4a9a74a128772542d53327a93b30e8001efe9abf705c9410bac1d760b56c387476481c1d0df996ee4147c48026e7f455f0d2e70b4940e000c30c000030c30c040701bc4752559a950a60e5ccede00be297353923649d64684c6c584c6c544d897280dfb0c000d05ed5499fcb1d38535a554176aa11e68870b6b94665a4a39d336ce16762063e1289e77485a0bcbf763b06a394a867016f6a0a124ba41ca560c16f6d090524cc4fd87e60a8b98c74dbe98f1038d1536adbe3c9141f0303155d87a6433da870e2467a8b0e583f45929b443be4c618b616258f0c813d3228575f3665075932be31a851da4bfb42133c5e42814f68e8aa1b19369a7e813b654f21bf2857558d4097b4a9bf667724e8b6a135675f0e3d739440a95093bca9cd191ee66ca74095bc8e819c4ce500fa712b634d1247476ce679984451c3df4f0649f538984e53af94dbc494f361e6133ef1053e6ede41434c2de536279e1a25a148bb0aa7fbef88cfb954388b07ee78be915ab264d86b0dee76c469d84b0c8a6e431ea2058fa82b0dc7f8e270f4b293d20ec3874ff7aa63187fe602de99c29a59136c407fb477218728c959ab31eac394ff97ccc5be9613cd8ce2cf5aad442a2b5832dc7a4b75329a399a583e533234bff9841c8930172b0fd4f55e8983efd840c8083e5bc1ae5905421f171b7582d47c621ce655c639c2d164bc761364cc7f25aecddd38f52861ab369b145e98f161be57cc82cf6f848ac742ea31f2f8b75eb51f288561d8d8fc55a53e2d7a1428aecb05852089509e5601ee4bc62739072d347470fbf2b76b019e628c374f5add8bee4f45148f2b164c5e679b36773c4375cc51a3e690c2162e287a862d190d97cfe7399eb54ec91a4bc7388d628d4a8d87e3b83c9b08f297d8ad573778d468d1d6a36c5ea481dd7a50e530f2ec5a6910ea4322d334f8a1d260797d636a35852f22e4e44bbca1851ec69aae1c86842b1a79cead7b1b73e2fa0583c273d7f4a939d2b9fd8734da806a2e7a12b9ed8530e15fdf42af32a9dd8434f07cda9441dad9c5827751fc7910faad14dec2867fcb02ecf63a99a58b3a763ced80f39a29958d671b2f0305486a59858249a477e9834b29397d8c637d287c6a88aa625d61cdd0f5134c334c94a6c15b1751acb623f92124bae7cf995a1e67f9cc412d2776a5487710d94c41a5592f92627843889c45e9f699e515d5e4c0c123b88aae0e7e9d693c41eb1559ee4e0323d4c133147ec99e38ce48a9ebe41ac119b8f748639724a393f63c4f6d9695e9363657eb6882d8e87cd8b8b569f3345ac23b1573967ee5bcf12b167383a13f3731ecd0c119b55eaabd4cb0eb18fa7147975693aa7cc107bd57d946b9c9d725921d61ab5e429555c8a6584d81a555cc9f7d13a5636882de7adcf61bcba246482d8f741b050f952c77a58203689fcb70e33cc3f0d03c416ca3f47fe154ff1ffc3fafb4173e77da4fcfdb04ff0d987d1326bfd3eeca82e4b4394f59fcf871d6c4e0e298a8778fe1e96dbda33e9cc49e1eb618d670f3523cb98f29e873df49283941482e4381e36cf0f8bf92476def81db69493c5d5c6edb0d5a6bb543db9abf13a2c9727258960f137753aec18259f8c7ce28788cf613d9fc69b1646d473e4b0f85ddeff10711c3d71d82e35e2ee79180b1e38ac99127426de7a65e70d5b8cdbf399d9fbd071c376df602d754637d269c39663dc0f590e1dc6cd86cd6a3338ee8c61d45ec3963a1e8349b61a36cba01bb7eb81c5390deb48e78d52c93c9bd1b0683786a9216fa283cfb0f8fa5eb81c331cd99861dbb416222733dfcd9461d57a64319d5694a892618f9bb89624fc9ea56358524d4a8f66f1d24f31ec3f2529634ebb1acf30ec3977c671a25c4828c1b0a7e47e102155731abff044cc5d3ea917d65bbd9c3982e897d885a52c83c88e8f9d4ae4c256b93bc7875e1346dcc2f255617ba2c4ca19a8853dd4c49dd5ca8cf3240bcb967aa43507192b040b6bbc4751663999577285fd73520f19828578c40a6b380a31e2a7d5ff5558cbe2e4763263f4478535c66a2a5c5e68ce4d61f5600ee3b2f23a7d52d853e3c38771738aeaa2b0c69b0ffb413c8dc4a0b06dac987346b292883d61998699a28350966b39618925317743c918aad5843d775d7549c4ea9298b0a39d108337c8891a2d6169904c3a2c342eaa843dc5466fa41c27c624ac9df341485d740c1c85842585c6186a6ed411b6f99fbd14339ea9514658344c741ea4f7638c2ac296d2e15d296e34cf136199b48c3a0f618d31fe31ba861d2c21ec3096c3cb39e37482b0566d74c5ac0fe17820ecd92176e56b3cd73fd82ec64f396348c3a0fb60ad7ea41227e142da83451ea61033720ac08325544d8a0c96a2d95d801d2c1b3fdc4f28a94ebb003ad8318e9956536c0cba2e400e960a1126963bd267740170b0c6e4b43197f76a946fb176c67b96b9f63ac6b6d84c2576a73e5f655c8b653d4ee70c2ff9584c8b1de30a911752a46ef02c96480fd9fcec8cf6228b3df23988c6f7917389c516e3db30733e896481c58e7f27f67e87ba4d5fb139d0941f5f3e134b5db103d3f91424667492b662ef0caf4769a51b0759b1fad4a4b58c1ae7435ac5e2e11beb40a28a6d2e39c83417835f24156b4d458a7ab15b61546c19761e9cf9a71c3dc5969eca27363b5c77a6d8636eea2abba641578a1df959663c8f437c1c29f6f975247b711df2a3d87335c7ca7361cd12c5162b678e21fe61842c14ebda4efe74112c3f0c147b4d9cde7851970efbc4be13e42e478f647979624b0eb3663a74fcae4e2cb97445d3993a2a8b137b9e079b06b1bc6edac45a963c4ffde4a6106962c7f74124a47a5c7c502616bddcbfd21dadf44198582c4f77dca43d870ebac412ac3776f9c3ee14b7c4d2a5e163b2bc128bc967eeaff59bc829b18ccce44d886b933a93582b6e58debb25b185d0919a364e4c3947620da39bf5a71d6c65482c9db1a278ccfe8dfc88b5c43f5d64c6114b839fb0fa7c639569c47a295a44bdb39c7761c496f158c81fd52ee28bd82c34869a8e50b95645ecd1cc37755c7314d6446cd9207cf0d40fe5828865662a4b743ed3cb432cfa71fb61868f9bd210aba7b85f617305b30bb1a6f8a3f59fba4626c4123b2af56fed1a3c883568aac7f315991205b17f657c1a92a8fe858158a3efc983ca30860c882dcae391bf8bd5dc1f765ce791cc42e50e113f6c96e366eee5f4d4207d58fbe1c5f015dabff2619dbc6a3b261e72c6ed61cf30e7b8f126421ca787c5566206b1b63c2c957cc77c754d420c0f7bf8943b6c712cf6c5eb7c55db61f35ecd9206331eaec3f655b934e3dbdfade8b04c9a989b6993327e0e9be7443bff9afc1539ecd034348d58e59d71d8b2e124d1b81c3ac261adac06761d7fddd11bb68ef09533885bde1983006ed87322d89ddc46cd8d418036ac1e1b3b88444a960408c0862d4543481995976d0ca1600513d01f10600d8b0319db2093fb779a1ab63ce65b36aa21fbcc34ecf126c48498baa48d44c372a725b6197d4f86e619d6bce099a387ceeee3cdb046ccfdb9a365ad46cbb0a5f8112cd53ee5873964581b79c74893fc316ce3fd3de952df474f11c31653ca19f129457c180dc31e72c8c1319c8e866a83614d0f7d43c31cf2c6a45f58a42c536723672e632face93398d81c3b6d90ac0b7bc89342c4703184d91c2eac9ae26ae5aa0c4254b6b07aedddf5a418e3fad2c2163545cb0d926f8810cac252576ab9d16a83890c16b6cbab1fbde63979feafb0848a948408f99cda5961f59433c80df3868fe1afc28e7392182af67174f354d852bd26658a2893616a0a6bc67ac31a3bc7791f52d81b843bab8a5d0e421d853d2393fa0fba97620e0afb6e7a1c62901cc7317bc2de20868df9d2f56f8613969ad114a22ba514626cc292fbd51399e7e36764c27e1ab1bb3db621265ec21a628c68a59537295f4a58aa73ef61a93a46399f84f556d622747e3ed33c12b6b492379d6e78ae0e1f619bfdc87916834e679411b6d3bca4258d2a348c14617b942a67982274c8771161b14fab9bc299e6c9c710364db964b13f3444c684b05f8e6de6cc254d2b08cbfc8649588c81b0d7f8846af4e330eee6074b8a21a709d98d514ff4c1323956bec6ff1121a4f460abea502a9eae31e88e077b8c4b55191deb1ce4110602ec601ded4855172202e860499b9252e13fc25c740402e46079a81229b9763f790a0170b074d894d21ac78f1acf5bac71a4bea76763ca61b4c51a35e432de18a29bacc5a6fe95c36426dda7a88b2477010b8a1ae0010934400603a0c59641e2c579873497fe2e924810031658bd008906cb05174918c02cd65f8fae39914f196764b184de182533ccb8514563b179ce04d10d916e7463b0d87724c5bc21f2c6c8f52b763cea492b65fe3421870424a852c107b8e0e2b8627b60bfbf73bf5569d245b66000ad58ea42ca182b4e5acc1d566c92e2a6484448367bbd20cb0a0358c5de51d9931d43e3b3e3820b2ee8c83000556c1a2fdc54c678c31fa5048901a4629033a34da83cfdd56100a858ca833eca959df92e499e18c02976508f3e44c84dfcf1060330c5a28d43ce5f49cfd5ad60052e381b0ca014dbf5a410cff5ab0fbd0b1898600624a8810b60408aad73c2e4f8a35e1922198511e14ea52b1c7cb84571c73aac0c0e73582d4331ccc04348040b8f195d2491402b0e0300c516631d9f69b0e0d5e84fac132a95536708d2bf27b698fc2936bed00d1d547430804e6c8d62431bad4611ba2e41924b209513abe9570a9f112d838cef22899c02ac30804dec1f35ca490899722caf8b24a30201176cd8d93026c01603d0c426391e43caf829a730779194d48047a59c647a25aec5527fe9b36be5f77e5aec31ff8659cff711f3b3d846bdfb2667fb4697c59af3e86cf0c46219cbc956ab33241b168b87c6385191af635eb1748c1b1f4d39e6e9b862b58c8268ce70b5fb56ac5a77feb0f1831066c53a1f63e83ecad4995ec55ed538a7c9fdf31b55ec13f395d12f5f2653b16846a978d428a6a8d852af466654351be1536c51bb73944e9e6636c55a9f1fc6bb20569f29c5b6f7b53966902bf3a4583b37a7fea38e4f65145bfe89fb979b2aafa2585546f6470dc512bd9dfb3705c51e12e18d24c61c1ffa897de24f9aa54b590ff5c45a91b1ebc852cae9ecc46a5ebf2144c65f8de4c4fe513dc4c6c84d6c7e214a324cf98851138bf674c8f834124cccc412f949f22e6cfa0431b177348cdd19677a849758d5373b36840ccb134b6ca124a6fafc71f34e2ab14c6ac817663c544d28b1ca58aa58df603b9149ec3978be4ef96a6296c472393b7fc3d91d4b24f61836ce94cdc89790d81e6720df971632788f584dd4d374ee1cb148ce31e3f14979d93562d5b4fa59131969ea18b15d48c6304dbe936d8bd8624e3925ad4d114baa0e87b192e6299588354a82c6fc78b43e64109147d499c6c0436cb9f25583f1943cc4107bc7cae8e4d1603f2ac4e63f9e7191552127c4fa38e61446a3366dc30c62b15ac9e9c19c9e2c882d797566673e106b5a70b8a13e20b693b5cb7c12bee5ff61499e522ac731ac96ef87a542cab519a4f8efbd0ffbcdcce8898a65c7f9b0398e347dd040d53f7bd8cf51aeaef9e861cd31628d67d967f0c9c376174efe73f00762e361ef0eb6295ef4dad577d83c37cea441fd9285edb06eacbe6062bf31721dd658d1a38427cdc9603a6c17c33236389fcdcd1cd66816b476ac268e460e4ba5c638492a5fbc4b1cb6df87979b1f776b81c39e3592b78df2864d42ae7c9b232319ea862d9949a4998841fb6cc31a196ec4e6c986c5c3a5feeaf7e99e6bd86fc5d1399028614935ec1bf3b73e1766d54cc38ec2c7eac73b4234d1b0a8c77f18d723c68f67d83a95a743f918d38666d8d16dec9fa95f0d6119f64915334c32068e2464d892e7dc17a28a448e8c618fd0f827aa7374fc8961cb1837ba685b09c3baf115352b0d65b602861d68482fadc6d50f2b5f583f2cdf84a34891afe2853d069bffaba9ed5c952eacb7c972f0cdb5dea970618d2126db88ff9f9dca16961c336abda6a2852dc5eee77f0c246ea86461e9eb0e15a1bc3ba382854543c7a1a7ebfd6e942bac9e32d3c7186a217f2bec21dee57421ade6f3555844c23fa4b39826fd545873755aafb89515fd29ac93529a87be558d7c292cd9992b792384c53e0a5b10930e26b936a70e14f6d56d90b1c3c7dce927ec316d0ed9ef9dcfb413b60c3484495d3761afba88a9193bae6a26ec388ec95aa545a37a096b8ce5ba5b0d6f1456c2de1f2db2c2ae3a9093b0a35ac993cbf9338891b0361ec9cb1834bca839c216ab2992e28f34678cb04c76a68fc690f12fc29e365a8f873f693c11b6aa10266cc6b13e9721ec733392734873131721ac5fa7e297813548294158aba2c9e7a3924a1520acbf93243da3b7a7f283a59269ca41e24bf33e58ec41a6a9dceb9bd3832dda4f883c2977f0280278b0ff5ee5f4c8e7955104ec608b19c97c5d97851045800e56cf4ebe9d6299738a801c6c716386b312bb41a508c0c16af17a93a64f92ff164b94ebbb9aa8e39fb4c556a16725324acd256bb1a4eae30fbd239a91b4d8828626fdedcbd9c859ec38424cbfa375a991b25822ee57aa3439e283b158d334d07218586cbf123c743da45a5eb139cef738cd53da892b960ed9cb21e63851c3562caa3943d39aa3641356ec239ea7a366154b9e942a1fe454b147cf1c5224f865ea52b194cce5b89ba262cffd382bc2764cfd145b2ae98d8e7ae41753ec5024efa224e52ec55af1657bd9513384146b7408212d1bc51ad2591ccf9728d6aeacd47741bcf7506c8e73e5301a3bf9028abda2846838213dc027f6888bbc1e32ee8b911ec0136b08d188121bc132d2037462bf0e1f8e7727ff3ff4009c586a37a6f90c73307fe80136b167146df4a327a131f4009a58834e0cda251d1eff3c4026d618a283ce31430dd1cf0360e25cd1559a3c7e8965746f1f68f949d82db14666485cf1c9a03f95d874e74183f8bff93e94d87a2a04e90de5a13e9358e6335dc836b0981d49ac12194d324b0fad3a9158435fca641907124b06d950a695c236ce23b692b80e0fcb31fded88cdf6e2679038315cba117b1a7b1cc239128d3523f6bb48760f6c337a5ec4be92515e6786e96256c41abd32ce4bf14146e1442c5d95923be39d451811dbe5efca9faa0a0e3ec4e62973621b9ec96e0cb19f7ea67edc2863630ab147d1185b497d326208b1effe4c1895988e5306b1d876678a91d151b40862ed2889bf890c2a5902b1d874434d21d7e7ad006247d3402d7c472b2a7fd841046918aa3622ed87d547e3428ed13e2c65b63983101e68a87c587eea91e7b29b1ca27bd853a6f054b929744ef5b065fe6dd00b396ea779d8f3d443234de261c95ddde06294e79a77582ac3a6b046da289976d8b74e4d1dc38e3dc93aacc11ce859ac250d493aac79266ef525dda8720ecbd823094b87e96694c38eaf2e73c957d605e3b0a9eae71c9dd2540ac2614917464ced33e804dfb09e45eda05b13e34337eca1a9438947a8aab00d7baad439847a663c13362c929ea2e73cd0d39035ec9df283454ffa713e35ec9dce2a04f5d2b0c34819f94af09c3e8686ad4fe28e5e3890a89d618721671425e59d5eccb079f8e47839c6f0c132ac19aed9655a473f293260f1315ccc498d61ab5249763a6993a4c4b05c9fc5ad1022564d61582e05bf702b816139cbd9d028cfd1a32f6cea31392af7331a79618d379636560caf88b20b4b8889c550f2bdfb482e2cb13fac9ae5871b1fb985cd3ec3c38ffbf8f3482d2cb3ab318ce6308f31320b5b4e9744c98f852df25fc887edb4a9bfc2aafb99992167f5f456587a52491cd3d4cd7b15f65a4775eb337351a7c276e2b03b4e3e85354c8f79752c8db814961c42dc466cd0e88cc2f628eed677070f00852567e5a4691e91b7830778c21ec74b6e5732e6d8c103386149bd13ca77ea31b4f1004dd86f3b1b6a3fb45c361e8009cba39fdd94da388f8d0758c252398f5264733e8ff1004ad86e3a2e9cd458c8311e2009fbda4ed0289ab2418c0740c2a6152ec4048d1554830738c2de207de7c349d5bfe0018cb0e61c9af267301f6ac1031461091f62ea376f70b4e20188b0e6da8cc23d1a8d18c5030c61d194ceb7a50a61b1b5ad0b750661fb8e6143c7bbce198130a8cc907e55fcc1127d3f9e7bf28508fa60f5b5681b927e4851d2832568df65b47121ec3cb81fc79bfb77b0a58c69213d67c3e860cfdb151b7b3d400e96b2c94857e223e51d0007dbfd48a89e3ca972768b7dd5f38ee39afdce6cb1a31cfcf427858f19568bed72ea39e9ad897ad16293f39861ca29eb4ccd62cdd395e4d46b2e4e288b3d6f9e60d2e9cc73c2582c75e63166bc19250d212c96185fcb9cbf9626e12bb69c733226e9bc5284ae58e26a1fcd442b9d085bb179080f7a67e26667202b96f8d15297c370153b0a8d5453ce43890355b1fc8fa40df3eb1119988a6ded53658e1e3a1a0351b1c84f0872c1d2b7f2a7d8422ad86759340edf14cb8547319f8dec69bc148b65eaa6648cc7d049b1894dc67b2144f0948f62ebee4ca331a95fe5a2d86c1d79fe78a9817f42b18e7db24671506ca9f6fd199fbf95f38975c34f94a67e8c7a3cb1060f693431e6957827f61cb5e3f943d6d6e6c4a6d73d12eec46ced4d2c55e52867aac760d5c4269fda8de44c2c5965d921c3fc39c29858536c5fd2de5c624b522b49631bc56f2cb154ce5f1a3661276d2ab1afcd9de7891c7e6228b1a7cd172773ca24360d2158fedddf6d8a24f68ce9f651c550566b24f65a554d9b3435c50a89a5ca1c84e4fdf853f4118b6f04d1305147acea20a9545a6a546923f65c16733ac598349a8cd8c41f36383dbd1ccc45ecf5b86625a78c7ea522d6d47fb19f730c5a6522b6896e9842c768dd48442ca9f364975f66f8e3219692d5a0a325653168886d4f52646da7a8060bb18f8664cc2135981c12628933990e574bb627835852c56ab89489a113416c97cf5278a393fa9040ac9b32c6a02f425f0e01c46adb9b72da8f19a3e40f7b9e67caff5ffa7d7e583e2744b4b895337d7d5822ca91c64bd78d727c583544da4e92f27ef4f6b0aee4c438793e76e4881ed6d0933d92e7618d25294dd9a7fd0f1ef6483e216dcc11efb0f7895dfc9439d8d861eb7e541ab623545a8735a409f61ff61b11a3c36a974e3b836a0a7f0eeb6a8f8488b127c672d853488efe99b451340edbe795e4d168f37170d8c17e5c6b942759f5863534ca75e4b1ac23372c3f52792449a87e541b3687314f88bb7131cd86fde397e4dfacb0f21af68b5137aa4a3b64d4b05cacdf8c9deb23250dcbad57081d1f693e090d7b681c2c6784b49e9f618bb6beda2985ca7410332c5d8e3393c668ee65d81f6a6f49e8c9b05d74905e8e1fc30e76337b636cf8ed625837e6a4214489162d1e861de647a99f622b630e8625d337befc112b5dfe854d73de65c4950ef3f1c2623901c730620ff37c514aef2a52cc2296c8ce69424e1a45ec0f75a2f67188bc9f49c492f930fce9a790c36610b16cd09eb234de4062e610fb7f86863146ca81650cb149059d8d9de12ff885d832c9e70a652a51e2845873566d79dac66cf8205629499fa17daeccb720d687299c4d705c9f7609c41a9226c79f9769765200b1f4e41b1d89e89192f287256dbeb55011ffce2a7ed8614446a15921e51c953eecb8e1a4d1eff061e9895c29ead54e83f7b04684e80e25b1f662f4b07a074b41336c8798c9c3bef317d3e799583178581e654926b3e81d96067b3fd1f234c8286a87f5a2247892681df6b878d31d3e3cff74d8b3635a0c63153f3e873d23f198f1e54c43510ecb74f9e6cd30e16c8bc396d3a598b995983282c3a68f71bc9442649cbe619d7895f2cec3ba1aea86d5be472337693dfedbb0558a0731cdace4e4b061b91cbdad98699f2a6bd8d226cbbb7fa61af69c274a3d4ea661fd1f8719ce4e37b81d0deb679cdbdbf29ee8f20c8b7d4ec6e1b0ce264b33ecd133c6299f858a69651916f9cc49d27888c1a724c36293a17fe5f5cd9d8f61ed5a9b50e56863c3c5b0e6a4e1624d8361d8a266d81d7167be3782618bb5db195ace68f1c62fec33293936a485301bbdb0a379dce9c2966b4246611d1bb5825cd8ce2ba3989437132a6e61e9b8dca892a8af03510b4bb060d62103adbd0966619ba8d28ee9195906211696ca46c922437dacff0a6b55c6f9f92c6f4e6d8575d3fb1e63183dc67015968c4b2337373f480ca1c2268ed4f174660afb7f0a957bd24c8f2a85ede344d3c8eab1948cc262de9db224a7be0d42614f3391affcec234dc813769c7bfbe96a423b77c20ef3520acb9c262c66bf377d1f1396f9e495750d3245be25ec713a1983efc7b2ca296149df101d79f549d841fe10294a277d4591b006497d65124d37e38eb077659857e1c633a233c252a9518458a8149a22ecb5b35e0f744ea226c21aea828d444a589587b003c98c34ca6e798c11c2525f31c3f40d323a084ba6de182e7f8647251096f1cf30c2564c57f783351b6ad4985259a1f960db8956a9bb95aa523dd853f014a258cea011321e6ce291a324cd6b013bd83e47cda386ac289e5a800eb60cb18e838447fbe15a400ed694f24fb49c28f6a85a000eb64c6191c1d37e77fe16ab434d66162ee2b3b7c5d2b30e3dc40549294eb5d86fb3fac3e595be9c68b19ffd87895439a2159ac5ea67612b394e165b9a4b1f2c796c4a9158ec69995327e386c620b058638a9e2b2d6af27eafd8f7611a95d017519e2bd6f38e07174a6d3c45ad582758c7cef9493e11b1623b8fbeb291779bff556c2156c8884893d8b32af689dca99a3aca844b2ad675bce17188a062d174c171943dbbbc4eb1df57c50e490d66a4d12e83421150802996fd30d628fea5d83b86c50a29b3c3e749b1a5b90af97248079b1fc5d2f854373c66c8e78b6271e0b981ffc79c1d0fc596267542ed038ac5e3d1388a948629e7133b0e1b37c73a584d8e27961c711afd934a659c4eac1b620c9d2a3ace7173622f930c2d641e4fda9b5847524cbb18e283ae35b19437b8ae8f924bea4cac65f753d561b177c6c416f3e463b4ff481bbec492d342dca45a62b5d0fce515e162ae12dbea86f558bdba8d12db77debe70d560229ec49ad24863604953081a492cd28f91a5ab901f47622d4d1548ac3dbbbf31541eb1464694aed9d06177c466b11d526f32394b23b60f9b62a60a893732621d0733e2218b58e6d641aa862a62af4c964ea74fc416613e469024229647d2b0435e6708e221d69426ebfc426a730cb1aa4daef1e4fd13420ab1a6899f642f78c39e106ba874755e6e109b7ab83a890b6253db911c2fe6759c0ac4e629efc60815c2d70588c543e51ce3868320d71f765039f9cf4442aae587358d993acc70629dd5873de683bcdd49e2a9e2c35a25bb8eeeb2fa517b582dc7545efb180bd3eb613d0d4f07abba51a2cfc3a6c96ecdfac482831e0f3b8cbbd05fe7c13ee3efb038bc48692e7658f62794c50c09ffd5614b13aa33ea6d108df174584aba629c9677a7e7b087f8142ba3fc992c72583de6ca8d466e373e0ecb8d239d95780fd370503c2d74a58abf615bd10a731437acd152def4dfbf29da86352aaef4639832946c58e6ef2b88e79c10f2356c41d7f72b6546891a96d4e5f3fd795d7d1ab6afd06192a4c9ced1b0e449de9cf4af7a67d834e7d5e0316dde479b61a994277aee285b269761898a0e1573ec1827c372314e63d8558de231acb71dab3f3a859411c3aa62b1715f09c3925386292b3560d8b6f4f3e53c29a3a0f9c22a992c839a8b3fa2f1c2d68dbc1e89478dae74616b50df1fac324c58b9b0f8c48bfdcbc9e1935bd8917aea14c4fa2f9d5a5826340852a1324c9a59d8fcd2c68dc91cc5142c6c1333ba88d7711f8257d82b2d8d4e9ad30da11596d3a825e93a27c9c02aec97fa3987daefdf0915f6bf58eb4e21c2563285bdf303d51c97c2f20dc38f66ca30c75c14f6c97b287ef2d59983c2d6f0aee2ab7bc21ebab327373e98ec841dc4c5ba3039358e69c2d2158f626ec6841da3281d92c954a86c098be6aad4133257c28e265c474c51cdd3e549d8b781657f389f102b47c23639237385a89b6cf223ac327be629a60d31496e84e5520c8fbc242fc25622b99d36a529464e8435c771ae49da1cebf021ecf511aef38370937a212c61eb8d56ebab3a1e846d73e2d759e38b290e8425c5143267dce391fa0f169bd0e05198d59e8f0ff672e4f13cc5c5c7b3027ab04699afdae827d559013c58f326cb191d6d928615b0832d265377e3ccb8152b4007cb360e8f10436ee6aa801c2cfd38537d46954ca20ac0c15e3a1e739dc7c993de62cdad3189e5a91b9fb6d8bec2a74e6a195f2a6bb15fe49832ef75860d69b167bafa9b4e31ea338b1d754c8988f37d18228b4d5384b514b527a42416db49ece7111f8d232149c7c94477e4153bb85d47121f99c7882bd614cd8b7e0c3f3d482bb608b5bbd98f157beaf570a531375e6f155b86abe937f5559e9c2ab6d8d4798f5652ba4bc572d531b9277e4e5da1628f9571c7f4e19286ea9c629f90142b32ca147b0c1593a71c078d97623b1d8dfaa9d2e69c144bc830e7c60ddad7a1516c71e3a428bfcb4d9328968f1c954f426caae88462d59c63a894122a6fc601c5e6492e45868fbfd2389f583d4cc641abc38def8955f536cdfbefc472f1626c459c649ae7c45299bbe2fda938ee37b17c4afb9c51ae893dfcc606417272203999d822c64fce9c422cef6062fb92780cad73892d244f51e38f4ce6b425f6198bdb09c9f6c25d89fdd2362c4544cc375362952947d99dd1250d4f62ef18613ec7b1f41396c49e4b42ba18e28ec41239e7bc9b361b48dc9058ce3ce78ca99b9cc1fd882dcca61ce5724434b81db1449a8fc89d6ed26fba116bd84c4d89916357d38c583a864544ce48f3677a115b08291ec66684cb29ad8845baf4538670ff38a513b1038f7c8ef26394cbd2885852081f5b29967358fa104bc6ca3039b633ce4a1b62078e343772daa8a37621682039729da784d82f454aef8839c44d07b1e6af3efebd9cd25410fbc44aa7cae9df280dc41a6c52fee6b28c7902620f5a9bb3db319d27ffb0740ef59989609a4e3f2cd16023a9858894d987a561a75fcc0fee82c987fde63ad7694d5724f7b0449e9033a9f4e62cf5b0c60c3f458b49cba4ccc3aa17938a6674f778c4c37e133ea56eccb972b0efb0c389f5892926cb19db0eab4857494abe394fec3aac5269438ee9b0c4e694abb9215e6c0eab7c2ecd8e97e22f94c3e2683e38c8eb1036240e6b663091f13027c361cda41ad3b86a92e70dcb8590a16e5969c4b8614792172ff7cd4dd8866d33aa935fe9fc216c5825a64b521a7aea352cb99f41bcde6d903b35ace1a257f4f81f3a9786fd6156686479fe618686bd2a8ccf9adfe7adceb048fe34d950ff936d86fd4b3c87d775984c65d8420edac82e4fee9d0cab44c83d09f99b312c6329c73cc2579262d8723dccd049a395637418d618f7dff5201efa60583d9437a81c72c2fd8525c5c7991f529c49df0bcb8587f1fbbc0b5bdc3c1361762eacd13f39ec324f2cbe85fd626398464c86e1a3852565d31c83b0deef6461ff181ecaae465d76b0b05fa8b2991c827c4a5f61cd949163c6cab3acadb07de658cc89d069741516c92f3fb3a9b0e4e568f138e3633d857d7767a62fdcdcca52582afef7764ff62e8ec296163d8ef72e86104361cf981a2cc661448e79c216a7123756e5ccbf386129698c27c6eda7579ab0caa649394c4f532a4c582cec7f3cda941f5fc2da25219eb9e1495c95b0c4e954417334093b8a29fe420a364155242c65e5252a61a38749028eb0384c162a4886a1159200236c7ed2d31bc276d59780222ca923fc24f756e3e3041061154f21aea5ced2cc710286b05752cba87e552e739c0021ec0fa4e2ee324a548e1310843d1fd9454e991dea710280b08414adc9387f773a4ec00fd60a5a3231a74f998e13e0833537869a71875f0a1a27a0079b75ee88996d0c43e304f060f3907108870ec23fe304ec609ff9aeda061956be38013ad86634e7f43974647c710272b07d59ec7c879c3ea5380138d8bec3f6c7073a3927bec5e2113233c47c1063886db1f76a01add89238fcf81cd2a39dbe00600a2f58b163f08fc7539d5c881329bc58c516d71d33e791a8624991c2c31c134242f924206178918a4dcc53dda48e8f1f47e98517a8583d736292c6a1a1c6f8293655efcf94185277a74db13fcecc9052b0b0cd5729f6a8d1232944bfb263a4d8a32b422efc8f7dce20c12816c73087bb8c57626e85043570010ccafe8528d6b3549231acfbcf60d4e217a158258790e1743e49f00214cb5f4ccf547a86fd2110bcf8c492fc235a841ce73e5078e1893da4df4ed468a3d2e82e926250a4f0a2135b7d6a282977c61b22e582725d78c1897552a8186b9f3a7d215d249d2319b8c01d056c0e0b2f36b136bce84a2911628e9226b689167e3ac3908b6a3951145e64624b973c85cda4626219079e5743e4fd9ed5451209000e5e5c62b9d09b3187e069a2c7241885841796d8e72fde36865136fcd645920a4c9723bca8c466a93f766caeb8b0124a2cbebb579d396359407831892d9957ca9c3ad245d22001f0c10b492c993663cf077f3495466291f4d131b936191e975c7801893d4aa4a4f3ca186bd7472c8dd2586cce2d8bbdaae18523f6699c61ce8c3cee71f20c2f1ab1d576e40839e646fe388c58620a932c262783ab7e117b9c18d7334a8459add3f04211ebf9862bbdb449c47e397dc8609365a4e15210b17a6510657cf37d69fc105b4eae8de5302feaa986582bc46c824546131e2cc492616708562a19a4d458a001426c5a13bff142b8a4497a41991783d8720631e4575e8be4591749242082186dc63087a549aac12e925e70825e415141c18b40acaae1d693e72b38a38af002104583177fd873f4b3953899469d7ae1873d324c35890611e6b33e2c613425c93036f3dd860fdb3786e17266906190bf8b2412ace0a4105eec61b3cbcf61d2ea6d8eea610b9f0b639b1c85f54e1749e71812ace060105ee46187db30e307cf94e0380a4810033cecc11c64a03313e793683e787187e5c349ae9fe87a1cf50c5c0524a8810b605088082fecb047069736e2790ee509fc057a012eb8185475d87118090f7aa2a3137791448215c0400526a04ac20b3a2c9954477be2c40ebbf8094c0ae6b07f8e7c716b71efec413c7821872da7a3e7788afec6413376f0220ecb4a04e98ed520ea3e5d249140052b18c405172f40010a62100108a840035c70e1ad02ff5f01175ccce1051cf628be212be7d18ef9ee22a9b8e1c51b76d420a73f43927b90d2459209000d2fdce0dcc3fbfc1479b1c28b362cfe295658c5909b50cfc055c086f5bfba2f6c6688fdbce0054ece021380195eac61cde82ce34e3157aa8a6af1420d9b95747430e9b0348c5d241d2f46bc48c3f2f062d48f8dd58d9f857881861767c0426692eecc2078ba48fa1590c08c62811996fc79c2a414f57b37c661b0021990a0042a204436881765d86ac3e2a71ad50ce32a861764583c3f1cc4f0f74943ed22897ce1c518d6bc35cb104bef942c5d241d4f36bc10c31a2fc42ae5073f270a5c008326410d98046158bc4159f4faa7b30c0d0c5b659c3279d0989c097791645c40f4854dea33280b1d63cf372fbcb05d488dca3f1ee4adb58b2415ac40b9e0e2052758c10aba502eb8501970c1450ab8e0e2008087175d584cd2f6f24fba3ce3b1a3820f18135e70610b67f79d1aa44fc9e82e921c059c14e0820b2e4635e1c516b6d310a164733c3afaba48520ca00bf23ff0420b9f58cec0e2a701d8c18b2cac1e2a7ea7b047673a0a6000037f0162e00516b6a435938385751edbbb487281090c0bce022faeb0fc5428875ffa1721e7820b3222c20b2bac69424d9a2a999099430297010a56a035680dc18b2aecd01fe4db70631749480df0c0051ae06568784185e5ae3a055deb8731a6acc0054f822e5e1003160caa4091c08b29ac6129e586f4518b1927853d5987bc39329c63e8e8c28b28ec18874d953cb42b99264d1abc33c9a6f6842d8aee49e465b4f2d50b8a69e18513f6bdcc394622ec2289a80110680041a00103052c8881071ab0810b34805e3461ab5fdfe0eb0fe278a68b2412bcc0044cd8a224ffd754886078b184fdac34cf726aee06a12e926a800214c4e08b9020f042098a75a5acd095a156d2486ada7934e4460e63a4035c70c1823cbc48c292563a2be6dd5d2471c185111256c9a0b31ae1f4d1668eb0686d860e73a3c4616060108314e03b073c062830442ff0c2084bee78d2e9a234a7ff226cde5bead1a6cb40032040667841844d2b92e778c86826f5212c976a1a3f1ad56ff20861d5281d2b66488bbf38087ba5ff10162468ac1d08eb4fcca7f6d9153b8527c00213783b07b8e082c8800b2e880caabcf8c1ea18fa654e0ef4c112bef32348ec66b0b8073bfc1829aa5229ae7a78b05f8a85b27cfec5e0c50ef6186308f590318d4d8a0ef6338b39f3a7fb1c07f1052860410a4cf082173958bb1fa45c9e3b3eaef3841738587fc256f8fbf81d7331e0168b244f31194a08ffdb6db154cc343122d35d2491801ae0010934200529a000962e8201b558755267e9c65d000312101dbd00008d60002d36ddbab92819b398f259ec912f622da98653ffc8629d349662869863baa8c4628dbfd1a27c4a9f690a8b55fb61c55784cb07e92bd69473a24f34dabc7974c5fadfc143f4a41e520cb66251fbb815a1222b960dbe39a7a5345d9956b1a6a53d7e307529a7d145920c4ea0380906a8628fbd9f3a3b6a6f739f8a254226dbc655411a7550b1ae75557ceae831fa29b61869fec6b4c2d366536c39ce678c7fab1bff528a1daae50809b98c272a29f60d19ef73b84c6739a3587d2269c69582a25822844916dd5928160dc9931983b8dda8ba4892c10948f0c68f406001520128040340b159cc104c63084b79f29f58d2a5ac1cb9d51043de137bcf5834ceda10bdd29d5866afbb1b464ed88ce6c4e6f3703f7c2791ec79135b7cf8594aff68f36f34b176daed1e073963a49a4cac7d9bfab152e6a8c860624f36e97208cf25d69bb8306ab395528a25f674f16383588651334a2596082595419edbdb0d526253dd08192dc3d998dd24d6cb18c9c69bc3068d2496589f54c5310cd131060322b1e59cce51c6b36477372416dd4fadb8a27695c28f5843de0cb43b928e5835f4a50c53c69b4e93466c13ce6635678c581d3f8a1b42855634c82296c99f19eef9d6fca614b1a30c71120d53a30429114b0c2b13e75513f3f288d881c5c9fd9f64f1727e88fdc327ad1895342c850db1a7fe4ecf28e768c9510ab1e70b0f378689c3092221d6c69f39194d5ca9944150da9d628c0c15c4922bcd675cd8240fb140ecc1b7af6c33e8380948ad800180586387a812cf32c3e3f41f96b9b0a1f633d64cbef861d978f9f3a147b9cbf461cd1d2e46cee70ba1221fd686b92af626526f7c7b58e2585f8eb5e9306d7ad8c363cc9b6dd09955e561199b3f4751442e4e78d8d13d08a376375df1bfc33631c43a2bc964c4c70e8b8ee68c227ea9c39629ea31058b871e4d3a2ce697d9694fee53c739102c4f3a327ca01c5647291da242cc518c83123f4a9ac3a8e0b0c69e9061fc9ede6afc1bb65aef4ffa3192eced6e58735e77e7c7c8a16c6d58bd7ee341c64b15d161c33e9b3bcca488d7b0766ee3dffdd5b07e880db5535506319386756432c44ea2924c336858e6fbcce358ece59433ac75b521e5ddafce1334c372712232580b29c3621beb62bf716458257756a48668dad0c6b0c7ad3c917037922a13c3bab1ff1c5fa7c2b06aee10e65265581d074393249e2735f90b8b7fa806990cf52cf4c2be62e141a3415d58d28710c7ba739c89a38ba412a8c0042f70410cda5d4002a4129c9dc0202a30800b6b79ce0dbd6a2ee474749184f40cd8c272392a95c687601e1517780a48b082a3236080164e0fe1f555350a8cc3e0052438010a62c08420f080527e56a082634016760c6dbd43deaea8cd5901098e0a0c096860822783410c30400313bc0500080cc0c212c252ce290e4b36af2403222081081870852daf6e860a9ed67f7334062403222041081860857d3e9a8ee6acfc9bf22aecf073fa509f3bc7ca5b790003a8b04c06217dc5e0abea2853d81f67b4dcd9717a7d94c2fef5382544bbc9182451d83a626c183a74c8d98603180085ad3ee3854b9a538ad43f61077f7b9bfe710626b11316bbeaba9c223cfc7c69c25ea931f2a829ca578c3261cf172dcbe7c2252ce1ff743c592cab8e28618d51965358924bff86041890842deee690bd0f3673b091b0a4a39875e93b837b94236ce981a94e88f1de4c8db0a30bd921ac995cae598455c43f74fd451b4d95084bbc990df96c7b42cc86b096e58784b0a7687e79ca736557461036b5082162f2e9067120ec20e332cfdeb578f80fb64e1e0fc626c689b40fb6080e6ba363701b323d584b2e4f0e21dfda5478b04ceef0a82bddc15e1716ad73c8d570ea6053f1b11453e8c48be5e0a918561fc3c73300076b6d3eaa9a90b18cf1b7d8f3a4f0f90d7463c76db1a43c0a2134ea9474bc16ab8f75facdbca7233b2d360d52f7fb17ed2ff6596c71f229ea850c25322e8b65bcbcce41fef44ff158ecb3a9e4c4237e7574582c758db7a256bfd7f357ac8f61e54d39b92b160fa6714b4cc54ef256ec79316568ff3879aa59b1deae6d8ae44689bc8af574673c1da8a8c4a862c7b7f321c5907661928a1da5aa4a75944a3a878a753d23d9982be568df2976e418a79d1497d4a3638a2d53d3c70d19a7149b9a68a6b0cc99cc27c592529cdc4a8e6289f86b192693a9ea8862071e345dee878c3a1d8abd52ca7d94c9d16750ec93be6193570a71e4fec43af9d5e13ffc66baf7c46a395c5fec8f144f3bb16ea4cf29864e6d3827f66b7495ab1f3219b689a53a348c9a2fa5fd686275dc38c6ca8ccec40e45d567729e31b1560a193c367cbcbdf325f68c7e42da99e81e0f5b6253ff1434f3757478732576188d2c7326cb7a664a6c92b344ff24fb3ac293d8cf6b2e56d4d2183c96c49241ce4146277caa184762d158954422df86943124168d9211f9e02b558a1fb14c54c6a6d0e91c8b1db1e359556d94aa4ee3a6116b574c6333a3dee88881a844c7c3c22161010141c1e0e0a04060606074ed3a43180800000463419023511244e9031480023c1e163c16120e1010080a0c0a080a08060a040004060604080006060800040400060608e01193106e00025a44a1e8520a8b62820fa961834b99f6b917f1673915de9f0ab5a9bedae966be543a985d1e661d2fe43cf5c0b7316041545642de9af1178bcd3a801c76354a272f2985d2145e964ebfc952cdcac9ca79ea314e497b453aab2ed292cdc6f4d16a59f8699717ff12978530f93597ba65aa6292af40611dd1e312c801988d1f10f392e8e73a92ef9bae42d8298dffc9ebe3098265d241e826f965f620249a02f5cf29669951bb3333030e16f9ecb2710fdc5bf616ffe4187b1d1230ede51c38ed24864727d44c2c60229aa9d42be91db70fab943e4065116c1135ac7f3a270ec9274c123125e2b8cafe454da6c05397b2f88224f516799844975a5a704559d38320c907c5677b31e95caa838515ad02fd1a81665f2030f0fb8c0775cfcb50ec89090e7a50f6410b6908d9ee0f50dab9cc6cc9169abd4e86bb2410d4f2995c8b151bc64739aed26d05569bb6a8c7040d03a81bee8bbe3770e90ddf517a9e72436171ee02fb223bd84dd046a6a1d790c65ff27081acd5e3555781030b011751e6c067d0822a32907841844345b29e5eb04ad13609b5760159546b76b43dc6b264c7f25920a5e974f0d6589c1b0828a08ea77eb25f4b7ddc46f159205cfe2c4e0babf8ec4dd9045d83f25b91544aa74694a654201b5af50b73f860fd926a576dd4235eff7719e0926b97d7a4c52b667540efcf0e611bba3fb3f24e7ae24776e775ffdc15f818dafda524faf0590ac466fc69118caffedea723b5c701b802856118596fe05b83c97ca796223e14727b99730e6e5c0e1225235c55a5cca7710904e75f0ef9a65d2e0f71c55cb5156ca5eb1aa6ca12a57acbd594b9aa7bf62abab3ec99043fd20e89ff796852be7c554c637d8e37099aae14be69500467e674b03cedddc67ab4948b1a7a087fe014450669da4a1fa39666a21c0c4ec60974f58d6313346731c5fa8d8bce872f2716d2baa53851f265bc155a8be2b14ca02594493294026ecc7986b0bca5f33466acee0d8f0e6e1bb38ac2fe2ae9001c1614a320b4a6df7bab1c176bc400c313e9a9f91d1376eb8d4fa9dd80721a8b6043f1601a42916ad2776d1be8a16e2e29a4ab0b6de6d4a08f66f6a07a04efff2abf12c635db7b5ea277f3370207f599577a79ddcee44a71132325ce1b28b01894c200f733990b6b7de23aeaa7df7b37268d8d67b0e2f56a7c26dac1d6ef36bcef370ce5b3ff2d8d245fe8a15662afbb8edd0e2b839fd467bc8d5d2799acf3ac25d00d7ed8660d8ea7d8fb5fbd20bba166424ed0e82d8af1b156d682519c32cb61ce161f46bc9a14a7cdca9e0c7f4cf6be2d0346a3f8a6afc8c992e582832729160f1d883c5743ab756903b850c174a98005b85c4674c64d680927758fb36f8affd40785114ca363f7515250997d7d47053c808bdd1dcc091b38e119ff75cb7b87a4336ed92c4174f3f3eb4854fdb2dd16460d8c0004fb28064e56481e89b16c08e715e78a93304dfe2373a8ec4d63afe24f06ee9325bf18657d85b8b0d4ce50c43da93e04cd58014e4d6ef70769c9466873e496cf5154e080e85b86888622a3ce1715022b5f28b40a0a7d7a172ef0b93756dfd66247e4bedc84c7941a8c1b07fe0f4548e696463d6e5c86c8ade04de1b8d25fcb6e10009f0421304f3751a47fe2133bcee87f89b063451a4b58a6609286b6092f1d639eedb5a06bd1dfb996831bc4276d691535a5c2403f22dc0157f16db7eedc77a050c5a9dc37e389f5bcd28b1e7c0d6c442a7c4d8a09ff83dc51f899aa4db139344599916ca6a99b5df28fb157161db5095393e7deeebb85782958fc748016b340150b73e9e80a88d0d0aea3e43183e74d9c9808233cc3e28227acbd538319e3085a73786bdf5899337f23c1370287a3fafbeda6f94e90af02dd6da13060e8e06425976a8e60c9b4792df36ddaa4a9f2ed55135d84cac94bd496ddbb36162c0cacc026af234ee9b28aece305e1866102a48eee0fd41fdf2c030aac4cbdb3d40f84acbef731830dc11a388aa5a979e649a5db2ed3a9cd080bddb3b537b9bf29f89b3b6dfa4377144929449c214ff4999adeaeb23e8a2c86329e13af36db025dcd1d35543f47614d2e459190b6254fce4d2a7511ac1a857aa98ba49469fdf7855e152d6a775f6eba0c9cc50ae4c18a191d67a7b783e905a2c72d79d2e7ea7f96bd11f40dbe97c667dd7477f8cf343ec3fc526a6f848345fd45e8860627f294220ef0d02fc659c1468ec1b13fa527290b29920682ed980a75585b23e416e7657fe9c241ebade4978b6763d9874a9f92ab3745e29660438934a555ba91af89c5dda7f9e1e1e2608c4ff0a2f2d8cd93ddeb3337f8af9dabc0c218022ab52f63f074e6e4d5f3fcb0fb0eb5e4ccc3be0574c23c2fde1ac44e6e44483d6256d34225e1878e9fa09f26ba86f001aa38df59fe061792185a7157ba18859355eed26409bc5259ee2958f9da64323abc85fdaa02e2e406319018e96d587127345377c92bfcd7a568a511c598af1d3b3b2122d15d5329791fa9e4ec68391ffe94e42b9a64f1da904d428f6d2a2a2eedf952731e43071f2b234d14a35fd310677bb516d4e13ce22395ef116b42ceebfb24813f29494cf25587ca70c04384b5b2c1152ea2b171cf78c702675011ec7b9fd0e82209b1a43ba06cc80ea044cf82f302e6dfc797c208f29af17692230194209bf5c5742a9713b95df8aa8b90d677ff893f70eab7efeb9a94a5863a3ef936e4dfd877391f2fbd5d25cdba7d008ad1ad4e208965f69204a1698dcc21098b1bd3ef48e5e4593a92edca3193643b95bcbad13858a767992945497c4a5750da47a97b4a6f94025a5b299869fb2b85299d0e57128bf4350f7475b19baa2a8101b05d042b9b8e3253aac3539f273d3d4be2e986e72d0fa0b987ea53dc771fa1f050a18a540d4b209e878a54274eb56dde3b4e38924b3a4e441af9fc7b73e46a8f4a55a69caffc41995206af8c5306aa2c34b2fc9d47bf7998366798c195f7ca83cba6c4dc9f54f2830e562c9cba3b8452670012d692b51d110fc57230352c6d69c707c75a0764341e8dbfceaff16ce3d7cf1ec39c856fdcccc6ae77e10a2bed316f24876153e50f8ed122e95e00ab6d643e9d516723b3e98c32f389c5b3148b9a31704ecc0e2d46a36861083199db4c73b8ca1e82897fa8f5df60b51b150ded2d60e1fd42740c5ebf16461c67e250e58685d1486bd89db70ccb62e0bef01ec344f185112a728e53902e8c05753dfe0ab540cf02a08f852156888cc1ef0ba7048621c64c91d62303b93cc3c8fd68f01950f70b2378bfa181cdf419ca53cd822348033acbf3a25289468de8c19be0bf0269ff3269c4794f4c5fb2549b64f00da4df843ee2219713f3ea986b5ce444b5aee9d462577ce0c2e1d72909347dab80f2c5a67ffefb1987427568d3a3adb6209c21fe07c946dd8bcd853828195edd6e78c2fdc51324ca9a0003744638943fe40985dcd21c8f8640e6b074cc02caa2cf5228f667d92f633bb49d14c7f10b08a9028a1eea4b7d9af2ad99b38aff75d4cba87954e9eab9f47228c676410dd16635288de73e3c34664cd4c8e364ca0a2f184825385507a6a296ab5e9f267035961b363627f566522382ef2ea365ea33d14e3f27570f1d07366e5caceda4341262641e2bd17e6775b758bdca8a09e86c424a70bb25dfd6b8eeaf6bf045cba9b7abcb9b49f8343c916100dbd89a90a221c581811f03172d44f4c8a8911019009e9c877c681078f05216f69b35e1e2c7c6c84f38175816e0dbbb1d9d3157e889fac3c443a44923f7ef548ba9bfbcabc99eb7d9ae2f8373d3c65e814b92ce29522ab05123a226ae99ef893bd5720952049872ec668a3e16e884c8830e15775c9cd4dd368d92b95172d23636db4bb4b3de94c1108375b57a2d19d539120d418f01047d56fd99dbaa866a5f589afabfc53ddb1a9ec8ec62abb85f39bb6eb1a31436a6207633ac37499bcbad1d9a2839361b0bad04e6596a1b5f99b7719c87513f13c31041a69d380d4a9c9aea006e8e2ed7f7966c20976079797d787fb9399c8f0bae5524fc0fb1f8134fe6512ab9be4824bc3152dcdc87f3e33eab111ddf13157edcd93a4b2accdd3a2041a66cbbb78312332bd07c6ebd30cab5aba02277e2018fdcd5c40b1b4924ee17572707d7f6e969532d8abec0fbc50903641a768e014445c20d86b2f53cf77f92aee3a3e4480b2d146ece0d1bab5062a6c2ac14aa7ae5598ac9371f1dae6f2ef7372cf917a7ebcbf0b0612e4fa572c7d0c3f2297479db9026461484c5bbcbf699557ffc3dd1404e9b545182c05bf0a71122941d5eae5f6f53a3a54e4991e6707bd3483f3e99f07f70ece8fe70ad2d504aaaf787d76776df2528196055bc319273c724192add06b1844782e6f2de3fac77276be3c231d55ad7dd78f74747d7eb7b8539a01dd8939410151102525a0464884993458ed83921b2e3cbe9c9f1f4eceafae472df449838a3c8f4abc0cc51e53c29062a1c2a0a405932251eb16758f93c7e7c3a42e68a29cb383aa60ddb611a0d56787774bfbc3f5e4e1a445ff2930a36f1fe747373bfb3cc1819d61eca90fdf1953ec5823133d3e74f9c333f6376e284b1f95454a58793a0368717bbf9a0b1534c7859664496b2644a34c95a0d50c18bffef6f9999bf2884d0f1e5f526779e7310cc1ddd3ddf1d9e2fef0f361878c2c33e748ba58483fb59e4b05ddf7b0007ac5db52ef6171d8d4ab08fa73aa682c1483120d78af89d42f5e2e0ee70263888dcf58a87ef9707c7dbabd3f9c9b9e4b0cc6e0a4337075683689a600b39b83d9e1dafa7c7a77321a35f1acd29449b92300922c53e797e308e0e46984859793cbbb85fdfdd5de916909cdd9ff4a3cbddede5fcce1a957a383f381aa024d1a04d91ae927f71beb9b890cc2cf2f80330323ef7ee748a7c3b3ae43d23d09b88f91de7992dfdfc7a432483afb74d4566db1f9d72d86e6b89b131363095522637614306a68c0c1f1fb15cce67699f5d8ececf8b7d43ca830e5efaf1f5e0e0bc3d3a1f9f9f9d9d5ceeaeee76fc69e622aa081ede2f6ffb8d052ff23ec5cde1f5eef678f7fa3a39391f9d236b5d078f3b1fbc3023a4cc6183b1a3925f5c26b6a36d9a3de2c5a91d7a12a4b450325b2c083e3a3a3e131eb66cfb2668880d45a5709319e2a8a17946c60e1a99333f7ad6fc900307e921b6b48886e154c22bb88f43789e8307a7762523cd56a46834ea7faaec173b629484dbcbf13196f4296952a0a2e828886c77d40d09ede4c5f3ede9e4fc1c35faed60c3c279ae0c29378b4146144205727c48b68fd58f16dae1d19d313357cbe683f3cbeb4d6ed685d3156be7e4beb8399e5eddefdfdfae6e2e7757e7eb5b16ae645d6dbfc315d1a772a3d22d302878f0a85403321a35c9089dfa35155449cfa24753c984d175e1f0f87674757d797fbdb9b85e9e5defaf0f6707a7a3b3f3f1e5edcefde5f61eda92b5686782dbc91a9bfee8767b83ae0a050d0f33f7fc7269422121a3d38f4c522fac0a196912442a9dd92ee76a881376878757a7c7dbebd3cdc57976f6747c6e9408291d4aca34e9d1154a17d27b1025212a3214542951a50f4f27c787d383eb6e570742d9a6babe3c5e5edc8f6787f3138677f5358179f1f1f4fa3a39389e9d3d1d7523331c10009d7b71beb8badfbbb4659fdc6f2ee7d3a3f3737304643f7d500db616a24d58c98e3e6ceea46488047b639963c04b458a6dae5c10a6234c8e78767e3e3dbb5ddfb9dd5fb077c93a2a8ff3d37716e48cbb6b0b2afde4707c723abec19e02b5ce1ad63bd52bfca3036c1f91b848029d5fde59ceba0d0f866f793db6cf5dd9c88e8c90e8890713f4cc4d9bec1df5229ec093e6cffc26c838a1e9b7a6b8a8711386183c4a4a650acc788fa12c194a3a99affce85400d5731292bc635e8cf69a0621dc5147107430b453cf96492615c038c0e460bcc3a431a16414ab18d55b4086ed4ac120c0c060f2618260746104c02c3153649a3015613a60ce60fe618e605e6132603c60d23121326c0c40192d8c7018258c441806182a187e9833985f9803300f987ccc7d40866c67e2f8f08479c260c37c803912067640cfaf531d411a110aaf7e341e0f01326f6e22ec1f72426ecd688f689e63a3b95b82595a38fbc86bd2a8d7148208b80a7fed0ef236490be9ca14c22c2a363c4ce1c1491be2d9f95f404b98c1595cfe6eb3416c82a2d581f86048e6585dc72c19387f7636098c888170688adea8fddf93300f993b61bfcb3824270b386fe942b62549293f22855e09e987abfa351d5d3c58a86ac2f5301dbab4b50c6c8f0e0d25d4e0a873621631c3440359b2048f50f4aecf00d6ba37a0e637a02523d2b356ca9e9d39918c4b88a2e768dd1478ee317523441b64a184b9956f59bdc9ef867dd6fae5143914d1d809f143ba9260fa0ab2049eb60a2c40be8a866426f45964011b45767b5d84679c417083cd5b47373236c48b928756f04d6b814cfffca634c85ea25c4ea1f49101889019331249ac308e4bd7e14a62d9715b1b0a9bcaf804bdc33006ab1bac6e9f1dfc1f57a7b9143024272a27ed3ecb1a03d95af2a8c0523c49dbe0166cb4f8a5b066681fb88489772673010dd63f2dd1dc5e1b2ba1e79fb49b44cce40813884b99a0b76719c5168673ee51dc2e507c9e182a4bc598cee1850aa3e9e3e8639e4297e1142e645962877e8037efb0c198ffe9a05a63fbccc3b2658638885227721f513946b5f5b58ef6711f36b6cbe301b68b07b00f42f906509e66001c4b19eb2299209aed6e7f26f9951637611c330addc80b4c8d7b15a0f1399ec56ae51a7c4da69f83170a465357194bfd6f97c182998f3717aa4cbde6ec15825561a4ef1d83b6939b0764b31e04973a510bc5918558068813a386010a428e5e7190813bbdca99e0477b7f5395f49f024e6884460b3660b46439330f0f0f0f0f0f0f8f1c341352db36863bc82465aacbd52715719a644a49a614096726bef47466e2834248b2daea90200c107d0bbf0a3f0b04d31b445d26c4400413828757c9ea501fcf4330c93e87b4a2be84d29212420c43706b79fef77bf739941f885108d6479a65ddfd1434330bc420049be95992fbdef45278b6821883e0ee73c5cf2482902af687d6aa20062a48cf134deec701238c3f14c408049f84d2f8938f1880e0449bdf76486dadba3eb4c23801d020c61fd8cbd85fa3f7f3456d90cb8183eb6211c30f58a66b4a2155a55c9a2b98ed4589a723efed05001bc4e803dfa5946aaece27d7b4e60bde8005e01031f8c0684aebddca1e91ae97ffe2020030458c3d702674c90841490f6c0915cb245c447574f0f3c076f4ecba9c60cab5475f7491ded1175d38a0468df731c4c0039b722e19d3dd5a0789c6b8037ba537940eb9b603a3b57fbb9a99eab4ac1875e047c99c2a59f0ea9cc4187440d7a9d7d4687de478cd3322c498031b73fa4e1eb445e618fb62053852d0457b7162c881bf58316853192252fd94408c387031a909696732a7519a1a350811030e4c8e2125b99952d0374bb2b13570548c37309276554458b2eb9cfed02a165c210662b8811339b424bd177234951c428c36b0265ba2f5a78e3f42ffa165c35780436d0b0e3c20023288c186b486a474d8e5465e808b2d2ad0616c2047170c28f6458781a3dcc811c64120c61ad2daa136691efd5b63e00531d4c086b81e62f54bca3d211f5afa91238c479986537ef1d14df79c835c10030d8c484127d5e952a97cc1f002c7b9b100ab03c43803e7624a521e51cdc0594e29377f78f89615a30c2955b1d664f1636f67b6e0c0034cf085066ad4f81b606880587f70d11f5cd4a8d11fa76f680c3270294c44cecc6f18fd1f26c618f410cf62ec1cead71862e043545de5ec90b4a39ac6458c30b01e428c947a3fccb2e1457791e34687610a880186d247244b2eed3eb470d84041ff0d2f1ae30b6c4a6ad2a41e59e771f702af9da285794abede5217b8d371d2684d2217d8a8db6d766a7273fd2d3039c964d59e41923c951698745bb9b4049511cbcf02ab5f252a05d398d1372cf056393a4ba5068b94bf023b16263387c9cf685b812b1122a28e968e575215b85135e1bba2aa3187a8c058660e6921a6da4841536093a992a331660eea7529b097c44c5bb3a490e189021fc3e34ed4496a69d450e0eccbee74dace13d88fc954c8d7c1d55a27b049b3797c1b3b1d546c022779c553a635abcb2913b8f3be9cd3c9509d447409fcd7d5e41c39727b25b0412fa8aefb4af2c926810df1d24f969062882b12b849e11a27a5c449b23a022bea43c5cd9da5439246e04e9908112fc44e3a761178ed2d95f4f847ce151281ed9874c86fe5f1f44543e05253e7b7f3a4a3bf10b8f2b4ffd3247350c90f021f22c974d7510102a394ae6edd248f11253f60ff5782b611161fb0d9732651f2c77214951ef017723adf10bd35d58807ac6e8acc6699e6f9bf183be05424f5d59363f2ce493174c056249184dfe5911dba183960d42ee656656acad91203077c90317a34af98fef28b7103de52fa76532d3a6cbd1836e022689e2c32a5f6d2b11835e03da878973e67468ac5a00193524e6a447c1d9f189b051f2985f2ed2f65c107bf2c31ac8f055b6ee32122c5359931b060939a48114998eb8ff50ac6c72599bc1d6b5129aee8379ed457ff5bc1a52913d5d198163ac20abe528be8a6d12a185331bc82bed839a9d7a10a2e2bf533ea9ff649928e541cd95247af6497640645c0058fbee8a2468d2d3840811c2970000c5ed002fd41072a4cddcf4bcdcaff6fe408a3e314fbe6601bf5324c654cc1a7c73f152274edc47cc1404729dc283992e5b8a15df7400729388d75e92f9508cf3e8d821fc9bb9a5387a524f3a260d3d8a6da1c1e3cfbed0805dfa3ad5d418d6e24610728788fa2b39436bf361903015a4004366c747c82c91d849c9882550e161d9ee044ea8a20440a32c8cbb3a3134c1ad5f92124a8f86b8e2e1c2f90a30b06d4a891a30baf5103471860acc0021d9c60eda45dc4d1412c947043d1b109ee5214f541f4cd4ea63fb4ce0460131d9ae0ad7c47092deaf67767828fe17ad639c5bcb136356ad4a8416aebd08109d6d6467256f2282af71f5a557ce8b80423dc5462aa0cc28172bc09b45882f7dd30d35529c875cb875617613429956072be7c3aecc60f2d7294e0325f3368cad23927ef43cbc61760d8c84970ff9e525257fe29e9ca246150e2dd16163da7f1838b2d120d02356a4482bfa46d3d871053bbf61f5a37f2025be75970690111d848400724b8f033774f3b49a4463eb470982c743c828ba739b3a95e8c76a62318e997363a66379d4ffaa1d5885346d36d1677b4093fb46c2023381de3c8097696d38d4e7f5166118c1c097e26648ab82e32448722b88e51a485c4a042ea89036983e848043fa2134d95a7effa2822b8dc99e2e4ac3a4a85e4e5a1e310fc87bafaf68d7cd16d3c741882af4f5253c52075ef3a1f1d8560db62a6cc18c2d25660648d1ae802356aec133a08718b10bddab123a60f83ff1b29c78d67c1df3070e8180477b1ae4c7de4fd9319213a04c14955c96a499d644d203a02c19f320d5516bb362d0f086e2d6b59865231e44bdda1e30f5c6e34addba6d13e0801c00e1d7ee06488189ec94e525f4c1fd8d0e5a3ba2d6ee4bf3bf8c0a9654f7fa5e627cb1c8d6ce4b801468e3e5d7871872ae8d803fb1ad2d3d8954af57b81bab0f105186a81f70245c0b0a1430f9c9bdcaf131b3f4a391074e4818ba9933acddef0c08da44a1d63f9c7b1a4bd03d7a2692b8a10ae1df80a66dda24af3a5d775e06a3484f46c39a85f45075e3f645091a78642c71cd8aa1132e5d5aa5a47ed0d1d72604589de98c272960e2283868e3870ba3ca914254f77f30f0746cb3d698e99dba3c6e8193adec07a76ce489ae75763ba818da927bea15fb2cbc23630e69b354df48e58fa2a6ce0437554d71fb58f555b1b3ad6c0bb999498b32f439250193ad4c0d9a55f4ef70e3abc948635d7a7d2da776dfd436b0b0e3c206fe40863061d68d074943c1d96d2d3c5fca1e30cdca80c59cd3658d66b06de53d0b78e23f7ee8365e082cacf4aba77c12d880c8c6e0ad716e57f5dda18b8084208f51ed5e89a1103a7134f926f454c3555030df8424718b80cb9a2e489deb9ae57173ac0c07b9ef426625db0b7f20b8c8c3e322584a4c73762bcd0e105eeba438c49bf46bb9ec9858e2eb01d3fbea5f47e153ac22d7470814f9f837b7a3b9d31695be07386ebe56c29dbd36d58e8d00227c146865d08e964be6e80c1454716b890cf539259b592c820a2850e2cb077a94984944ee468fa2bb056f1f428175159626a054664f537e549e47dc6add05105fe4f7abaf44f8391c8143aa8c0c48ab8d93a482f1dc40a43c714b8ff1a69b92dc4ac9e4a81d31d246b0a4a59c53b21d01105fe2686c81c3f26494906053e746c1d4d2241759e743c81d1c9fff57749abaac7098cb0d5f3589362aebc37811139a6605e3967c9f732818b1a1a3d44769ec7940314d88280a163095c56f10aaab4365ba4046ebffd54bcfcb2ef4a125833cbd7b5f2fc681b12f8d5dd683a73aae3088c105a9234dd9a3f49aac308fca41482b79d6e3dff011d456025c914fa37e930e0c6d04104fe627b321173ffe9895da16308ec96d2df24924708dcd876decc14496f073140858e20343169bc09faa27e1d404873c99442744fd1a4297ee8f801377a5b2eba42be604aa1c307bc7fc7f024457750627280025b180f64d928c3025bb55874f4802fdb8b13b57f47d308012543070fb8a4238ad29126b32d85828e1df0bf15fc2e548f1e059d6303d745870e18db4c61b513b535a673c0c6939f792786365e7023c761600b2eea705499a003076cf48e9ecd5489630b2ecab43a6ec065351791f81afb42018ece61e3adbce82fba30c18dae80a90974d880d76413db7c4be65fb5065c08a2c793654e6a33892ca183069cd0a583fa175b4d3f1a25146016dca80bb2bdb44416dcb57e1d0b5ef775344b50124d5558b0219f8e17f354438fe915bcbfe44cf5e61a4fa45dc1e75edad4f4ef2afd5bc1fea8265193af5e93598502b0829127bd52a80ee1080ab00a46694d0b96217812f368a000aae03d2ba78bfebc2145c81601e6a878f164cc33c1c6317dda930e9245e598e0f57ced2be774d5a74b7097f574ecd35b82b1ee8941f763d2ec5782af0d7ee31b3a25f694e03d72d64b1541758b26c168a43832898f69ee48821d753159e505fd7723c1c8faae20c294c66b21c1ef4b88ba1da64d691ec14a52916afe9ffca48e60440c1a94a9da083ec52d2f5756cd3219c128df5cbee1b13fa9f422d8d4229e928688242dad08eef5ac35868ca823441f365af05f1c130c9008f625f6c6906123821fe59eee1a82ce54e94370d5ab3fe9df4e74922198b4a66bf5f773f72f049774b449ab6a9264040310821f15d15f8d6a396510dcf65ec5a4c6fbbf23084ea346941cb9b4da81e0577faf444de2a41c105c8cab162c8aca49f7073efae91025d2e4aada0feca98f8994d93ef09b162992d4f081edf2ee90ef6f9edf03a35349cfe0a3bfe2aa077e3c44c516bddb319a074647e4b4a2430a42a878e03c477753f1256512de81b7cf49f3438e25ddb303a3b953453ca14c9d6cebc087607d794dff636b4b072626ef31110be5f1da39b0e527a29d4892036366415850b2d674290e4c3c597ec1cc5573090eecc68eb973ff377025d2940a9153481372037f3a3d249b90eb1dd4063ea96c67e2a672d60e1b986cb72754dcb417d93570d1b2c53c35f03189121de2c4badaa481bd131293e9e9ca1d0dbcc555cd1938dda1aac64c6306de42fcb41873b61fcf9481b718c7cd926b0cca3264e0b38b789d5fdacd791d03db697abc4527d3e9ab18f8a03545c9ea583a8e340c8c7e4b48137d3345a460e07cbbb368ac8949d5fa055e628b8a9344c5de56bdc09f75327353119469b40bdc64cfb395d4126d552e30c2f26dfaef8f9852dd026f71b546639aac9b542d301ad15c545cfb1047cd022bc184b6242ab75d50b1c0a6e8e7254345afc0061f4f5a82c8daa5a915f8adf74f5fe2b56baa0227d26d3ffbf79999a8c0e58f57b0b25c4af353e0ead376c414ba7abe147897241a3fe507534f14184b551acb3a6e58070a9c4cf73e3183e510ea27707947f28e260b2d5a27703ac64ebbd72efa671378fdae28c9433a3b9309dca85548cdef341f97c0c8188296f81a279645095c14535a4f7d49e064e9d41dd1a0af3724709742b552fc088ce9674f9e35d3ab32022b5a32a98f481621e9fad6218e9e08fc6584d49522f2a1b53930c010584d9e56e3d952e62021f0af2939e7b248b5a320f012276daa241544040181d7b40b21d5e7078c12d3e731ebe76c393e602475d25fca8248a2f780bd78fac2ecf387cc79c09fc54a6679ef80499944a69cab93ceb50e783f53e36d750eb8fc92e4a91444bb5a1cb0316da58ee419d773033e93f237fd5cbfd7d8800f226dbf45bd28b9ac01674997ceeb8d393f67001a70e126b34adad287565512b498059bd362361d72b2604cc44e95ebfcd72eb1e02de8e96cd9251f5a36120bc2b091e3f4176f0a169cfaca93e4b5b8980c5fc1660fca7288952bdf86aee023db4ff2cffd2b2a6c057796af2a5a0459c147f7687aa9eb459e56c1aa45ab2433472de555c18ec69c2d949ee89a54b0961a5425a5a4238850c127d5289625a5e4163a052792a7ce21454fd26e53b4ef9eadfdb4a5e0eb7308a9229252ba2505a792f75fb39a85acd0875618675a8c829ff45992455562881105e3976df94eb388a8a1e0b2d3dde34791ae8282d1163bd31552a849f9043749976dfaf81f774fb09154bcd510f53aa613fce54d1284d29d524a71824f21493c84a46c82fd4f293397af092675dae892f299e0fc740e3a6da48f108409ce2a8250fb25187335dfdfbdf1505b821f553a2be75b093ee968d5f15e478a28c1498ac1ac3bf4f23e093ec554d5186324c1c9ceff29fe1a09feec738ea4bc7b4f90e0762d24a193a5bf2b1fc1d7644d16e48852330495de6904bf7a6ae3ed8c6082b632d5f922681317c15dbfa608f6574d4ad63fb7950a5a24821fb718728352a1a73a4430da22d6c591b816876093de8aa94292f7137286e0c7e366c5c89f744c8560f5630769b1358f2511228716836023aaa890f51704235abe9744b3ae8702c18f440f22eebfa91f40b0fbb152d095e4f875fec0a9318f1e927ee094875c49b78e0ccffbc07548b5137f4bc598e583da9b1bdbc321e51c2cc6a492693a045ae8818fd9af7a2d4ed23998075e741491c654decc100f9c9a34653b52e385e01d3865b9fa4c594515153bf0b1b3c8fc21259df55207cecc3af669f7986a313a702e9a9a93de1ecf98cd81512e2aa8d35e1599991c78c9f9f3ca92d234497160afc39489e910ea3a70e0a39da5a8d137b0297abaf8e72f1d750327f3eb65f698d49ddd06f6db3d57acd9c07d9260e9bb4fbdb9064e45331552a26a40811669e03b85dc63b14ae49e85064ed88fb6d7926b1aac33b0e5e957937d881938a9a17cebf6ea23576560fc23d6e898a6648914193811ab0e55419decfc18981462d61cedd22678c4c0559031689d4821a54e18b83411bd2a930eeae3606063b0c8922ff5bbf62f70418d593ccfd376dd0b5c4cbf1b323772c77817388b234d921e5b372d17f8dec91272faf4e8e916d80ca252f792527f530b7cbef5e815611638d71493b64987948258e04dc97cd954635ea75c8151aabe6b79f72d58acc0e498f527a355e0fa3d44f28b51ed3a2a70dade7b2cc8ef60da14b8f34f22e4a0720c5a29f0c92d96c7aba2c075eccf5ed741bdf691a38b166479d107054e5b9a52f1d34f60ef3a694cef7addb972029fd6a38dd09126b0fab993a4d86b22e6cd045e63b64ff1d496c06826e9397e0ca141b44a6045f27fdbb99f04f6935a8aeb223f942791c08f461babea1083d6f5087cc5105cebc33eedc98cc05a5dcec9955dd172faa1b50fd0a208eca6de60264d746948118153ddd39b2a6e4e33915d408b2170ba6a64daadcc4d3126d042088cdb76b29822cb34a81f5a7f63056198175a0481913124d539846801042ebf2849ca3ae507fc697fcbd12da95b8b82163ee0a4c78b9944599ce89e1e30218d8c98a2e59f74192768c103b63fe73f9dbd73fa10b40336f97745d04207bc26a1935e0b1e226891033e7bc4a0a4989d088be2800b719d94d495d670c914b4b8011364101d3c8d84686103b63545ce39a6f4a1950dd0a2069c2719727a309121aa03022d68c07bd87da45097c474e5434b67c1a9bcd51f21e9023164c175f0d4542a84d48e4d20462c18193ae295cd62b5e71f5a8f0316dc6accd11253ffa155afe033fa5daa0c12af332688e10a3ed3a458eaad7713df0f2d1cc55a118315bca5dc904aa612a59ab120c62af88c96ad52cd847c0d7e6899a28118aa60d74eec47448b05e6458c5470e942a50cee3978ed3150c1fb66041dd51d740aee09629c82133a4b483accbd54670aaef3d45bd456ce60a55230a653482985e0bfa63e2938d32429b88dd8af3d0a3e849cc7cc93d0912a48148c7bca964a6bc938c2130ac6b4e60829c74c9d4a07051b21ae898afb9929da4ff09ea3e3a931b3daa027b8efab14bb903ae9443bc1ee788a05fd2fed6be104a32267a864e229c45236c15f1ea554cc9682658c26d25bb6a3fe920cc6c804a7bd2c05b529bf9fa99860345fd2397758aeabea430b7d189d021ba81819625ca21c23a914f2a706d5170e1c7fb6e0c0034ac5b004f7aeab57b264ba51a20fad4730c4a804279e5b553c424e13744ab06af71b62e53d09c64b6dc4a4795983144982534f7133fb7f6ff03312dcc93c794d5f82f7a60e12ac794e9f3e95f608364c8d778ef9b385987704971b3435a7dfc70b7623f89cab4e7345df45ef19c1a818b362f97f9b4c2a8be0e2e6a819939260999d22b8d3a045bf9ef011ed13c15801e994b9c3b380113c16c1c65cdad1ae2d9e9dc543116c2e8fe796dae291cc148051812db8f8824722d8cc39bcdd7f63dd8f08b635724e91dc82a6f7108c4aaaa3a95dc94e22670826a61275c22c79f068290467ad1d3176cc3c6a1282bb4f3a597d8212d99fcf01060b8a0d33780c822f135e1523f5e66b26084682ce1632b49e0c3194038cf40804eb9ea97d848912952d4030f2cd64496f55fbbdacc0e30f8c7f8bb6e8bf39a71f79f881512a37c7b2b5d4dbd0078e86c0165cdc78d4ff91ead1075674722ba5739c0f8c59bde80e1142a491ee81d5bc10b4e46cea818ba2b294f6e9f3aa0a53e09107d6ccbd538cf69bcd843cf0c025fdfea54ad4d9c78a0d1c78dca1d42c12f3a460c1c30ec50e4d1d9e29b44ce9521ea20bfe8b2978d48195a06fdb6dc77ddffb020c0482071d4c1d83be0499e43cf098031f2d7b04e5515c0f397091e2c690511c60a42fba401c7fce024a297777777777666666666655555555556b0111d818002b3ce2c0e7935c3539a5943d6f8788584a29a59452cadddddddd9d999999995955555555add1c472c605016ce00107c66d74c40d225b79d0be81d1218b52f24252e929f370035715337d88071dbdea36b026294733ed49da95e078137481960d7c4a6dd12228fd937d8d81c71ad8245f635996eea1062e33a7dde82a168f34702145cfd221367034291c0bb080086cc8c0030d4c50ef41e420c16f63cec0c752fddd9baae2f67f689981cda582a6d8f9542999b61761a4e0b5051e65e0b67db4e7100b3cc8c067ced71cb2651aef13021f381a021f38fac30113d8828b2d2080c3630c7c0e2bcd6d21a877bd376eb0054460e386871818913aeaea7da38bf91e6128a648c2444c3a33040f30787c81db187935585ea3461838b66cc0c30b55c30b8f2e7027712f293db8c0266d7af3640d1b13da17fc043e703404b86040036ad47064230559377284e1078f2df04982aa0ed354abd975408d1a356ae0e8818716d8ec2c3d62a2cb834cc902db56d1b325396ede211e58e0d365882245fa25f5ca8796a60be0594004360470038f2bf0a25e1a42557712325bf0b00263d972ca66419a54fd3eb4aac047b54893844abaa236bee8a35af0a042298fa898315a58089902d7d5a1ea6288177f1b5260bfafb44dae09959dce8247141811e61694a80f0d7d17f380021fb5327f091dd2f7877c68d9b86156058f2770fe1ba3051f1596f9a2df8b63a36f74181e4e30ff8794256ffe3bff432b25306ca0e0748102b3068f26f0eb5fa7e11f9ab1ec0fadf4376c28143c98c0554e173a223a6e5970091e4be03e47580ef6d9a64dad044ef98945be9df4c12309dc066ff56c1b93ea344202a339f4844874cd273b7af03802ebfad14ec7d1b0d14d8dc06e6acf5359a392834711b8f24a0ddda4525ccbd5387810810f6d27e468a79231533c86c08b0cb2bc7644cedaa30f2d05c343086c4a309d838cbe961b02e3989dc023087cb2d8a53ca432536f910710b8a89ad27d693395bb8484c70f98949d3629d58c0fd81c4292ecad9e36c2a3078cd656883967da2bbd192584070f18a13b24efcbb8762ac563077c29d7947553ea0b0f1dd81d456f341a004178e480517639896422eb5fc4160f1e386062ef46b07c6ed5c1e3068c6a66ee3ca9216a49efe06103469e8fb41293bfb5690d386d77bbb416f248be8a0e1e34e0278b594c1bf4e820f159f0e9175b447fd498265a830c59b09b41075d766927c8520d3262c1a68b119389dcd3033260c1890e2964a9c49c40c62bd834dd3a1949dd325cc1a68c5e4982b68d6a221f5ad60a36768d4ab7d458c19f1279641c954766dfaa206315e96c262f97481a2ac850051f54ac501ba4a9e06386e829760551c19b925e962a654a66b15370a15534f2a6460c324cc19659ce24936ba560738c37395e34c9162cfd820c52b0ae112b692b2df6b9b1a360b5ffdf6c2c7fbbd72590210a3e624b4e7d1f4ba7a0f30119a1e03a47ed1a797254243f1f21031464915c2e7192e50e055facc08609bee8e2868d17a0fe1b5a9fe04d8fa5a4d0195154f4a115c67f0b7054056ad4c0610150840c4f7032e48b9f04991e7deb045f979e1355e4b6c97f192083139c6de974b35751d15d199b60d3c49c227834c15bd7dd4bbc904162454626d81d611d72ca4947d09cc4049bb946dbfd6d4edfa3bc04afaef7f93bc492e9271f5a5b708002a907199660625e4dcc29d5460bd14a703e964cf89feeecb212e32083125c5a0dbe296be8e80899049f74c85c5141a4041992e032bf6afda424093222c17f4a3a658d8d57c40e093220c1fe569bd68e7916eb3c823b0dfe49b74a168baa23b8ccbee96934ace4688d6054eee4bca21af4540e23d8339db542ba3c41d43a05198b603b5688e575f1ffe4f6a1e585176d6323204311dcc9de6f4fe9eb767a4c046f7b7a2be96f0c9e768e760ac840046fbe673a5dcad433559140c621f810238527e1915a4c936108466565cb1123662ca310bc6d0e52a377974e8b2c136410824b5a2cb87fa8a047eb41309a440effcf923b952708f664acdaf68ec9080497dedd42f22c32820c407017a4a7b8276a635c8d1064fc8109e9d3de7966164f1e1064f881356549eea4353bd116197d60846d8ae4f676191864f08115cfee792c6fdc8c49d805197be054a9dcf9734dd497857a602bad8ad075091764e481abd2797eb983e83d0f1ed857134fae1ddb32cd92051989767e134b0b32ecc0c51ff1ba4e42c60a32eac0895c2a2619720aa14edd820b3a705b2acdd5da44529986828c39707e1a5cd734be5a7fd6041972e0d547fde69d9c64ddd1403d41461cb8cea27b530e95b1b5c76181e2041970e0afe3996975d31c826c0b2eb6e0620b2eaec87803779d82d488b15479de0dec28bbfca9a48fe5b4816f8dd77d2f2906391b58179d93a879aacdd01ab828de1d324cc68c1c35702a9578f260daeef334b0a1b73b4a369b988306269e06a525620cabfa0cfc8ff2cdeb9dd3a4d70cbc8be7491a8465e083d4f494ee918137d188391a82ba680c5c6eade039bf572f62e06b93b85ea691266261e0329bbe9cb3fb8350253030aa3607bff1bfc08b70eba032c80b5c52da4759deda309d2e706fa61a29bd2ce40e1738d31872ec982ca68cff1678cdf335ddf77f417f2df01a3d585969b3c0a4f0d6cbef0a5f150b9c8c9a46c85379f7ce2bf026d4f9ebe5bb4fa61538cfceb4203aba2e590526070b11cc829abf4705b6a4e9111d9ddc253705de6bd56e82c8c9bb49810b39a490e29ae797581458ff8f904e459896eaa1c0e84e9e7bc15310abfe096c9d9d4a7fb5f15bf24ee0767b7265ea975ec93781b52c3992498826a2338193591a7563fbe9c497909e145c2530d2d5e3a7be69bc98243049e5893caf20494f24f03909cdd39e75948947e0bcaa73480c8dc004ededf9ab3456a61481df2e1fcbd8ad712c44e02be6cb93c52749d00d818f64228a090f09814f955fbe6f393ae45010584f6f166c7b842a19020227f33dd6a6e0416d433fe0b652be536bf980d333dd1126e46711413d60a3db55bcee9165f2800931fb5494dcf1b3b403fe33ab2b59f624261db05f13fc3f8559b99f032ed25f8ebb796e4138e0440e326e1a492ad47303467dfa4ca12ea96cb50db80a4a7b968d4ea15403469fb4d8bb2ef97e1934605b3f7494c7cc82d1904b7b26b7adac9105ef21c44a9e31867c31b1e0ed7458a5cff51115166c1a13159de72bf8fb9c73ff4b869c82ae6074b438714daa886c6905dfe97ef4ee54a74861051fc95c0517b384a02594ce1a9e2af8b0187289a43aa9e054c6f466691d5430f233d3988c3f399d4ec15fde3ef548aa524ca6e07dcf825076edba9d52f0f13cbdf89e5f659a14ace811667563aaa4eda360749a0575b628989c639bb0d016a96442c186caa1a12c6a5279e3a06094d4e4edff962b6efc13ec862acfabe399b4e99e608370aff6f47b428ade09b6f5ff4a47c70f0d9d13fcad6615dd8f99e2c537c1e5cf1ba23d082bddb926d8cb90123402c198ae18720a410d08d62e555dfa9341e7ba7f60628abe971354fa11cf0f9ceefe4925c53ef0d9d74679c7ec0ef181cd71434413727793957b607594f77e0879eb31470f7c86a452de88631e909d6fef62877860cbdc4582cc19218feec08a6e9a5b471e3b91b303632aa9cfe9636a7c6375e0458fd229fa8a08ca5374e04b9366520f0f53ca39b049273d316651d39003a75c37ed2c84d08803a79dfaa5b9b342ab080e6c5dcc39efe45bf57534dec0c9f4e9638812531242ee06fe26e6ac643a525bbe68b44151fdb14d24a55d5727d060031bb2c7ff8a2a925334a540630d8b8c222adfb0a0a1067e93ca194787fc3a4a9b038d3470fe2987949fa9076ad450a58106ae641addbff6c281c619b8d1dc1d26326d50495fca24689881eb8e24cf83280d992d607491c3062a031b3a4b42d07bb71c6c32f09d2d6af2a01b2e493806be3f4b2fb3a47422420f460e3d0d010d31b095ae335ed2b10c34c2708c95a379549e95b6468d74830537d20d16dc8d33d00003eb973feb44481d3bfefadefa05466f23c46f33652e711a5ee03daa9976515a97a2e443ab58036874614b26258ed89746521a6870413d791b7c27869ed816105e7a4a77a8795006400a34b4c0c6113245543ce420ef00d4804616d8147eda563f29dd22d556d0c0026791ea4ab374b6e0620b2eb6400058058d2b30297bbede4c1e2b7017e397eed17c7b5977028d2ab0f563215ca04105c64b68ef1193c614d852be9d95f4a510b1f5c0161ca0c016526094feec27926ff29324f4c50a1ce085170be800fa6201356ae0481a51e07c83ef77b85785e61ffa5e78d15c7ce4e8e20336684081ad929b4474d2255d251a4fe0bc336e9dded0c13549c309ec9fb9f9a6642a814613388bec7ae9f626059d3e1a4c60745fa3b504258d253016fc5554f654095c69dd14f5784269cb6181461258cda22bfe4b8ee636228149da6ef5b46f4fe355051a47e0ee4646fb9114925462046e528f055bb5f4a6361f5a29b071030c1be8bf304117388acd804611b8d6e4d9c3f257fbc60534885069aa4b14ed9492867ea03104ce2e242926833ebf8a87818610b8d87dbaf92e78051a416093768cd63d1a3765030cb4f5021a40e0f347ef2a0bd50f18a56ce377d22d1368f88089e16974c5d7bb98e4a8011a3de07feb22e9bdcb537ef3804997a21593bd7de8b903367704d5a9dbf49bcce1e822dd4043077c3a7ded79422607fcd9e9ab10326adea938e0ba368230a1466492e40db80b1a342b45fbd0ba0146187fa3a0b181860db85c179692decc165c7061822e70bc81460df894630e691de94c3b1a346084c848e9ae828994cf9805af29fb7259ccf298643364c17efa744a7d5e67c4824d6acff24b8cb13d68062c18350f325d53e5158c2ebd6ceaa2549094e40aaecdde84867a9138d919ade07fd36cad3cc60a4e8520ba3e7956e9cb601666ac829316ccd49b50934d8e3666a882911f39978590fc37ad54702ab2ebfc6d9f810ac69446cc340b4b9517e4f86205572d98710abe36e8add48e23820a3fb4aa0f334c418e685ee1a271e0c8f14517608461e3061a624629d8b0a4224308cab3a6f5a1f59fcebeb840072ec0010aa4e0051d86e11f6690828f9424c58ba61a9a320a6ebfda52b5883344c1a83ca23747b1d82544c28c5070263d2fad94d9e6e5d80c50b04985dc4ddd1235c493cdf8047f9f624e74fb4ca6923cc1ea49bbaeeda04ea051b3051de45a3338c1885ed0a99e3aae6e76c626989cba2b72cc925a7ecdd0042f32a5b49b6189604626b8bd55915dee39488ec1046ff2aed3d896fead9d81199760337f5ce3a80a8be0f9d0524bb0298e8a1c159d4cb50106322c98510926ed5eb8e9fcdcb0f743553083128c90629541c6f4a1a5d436cebbe00bc198310936b7a8750f9a5a29671f5a36106a62409821092ed6a9904c7afda185c50133010b02b0831991e063aa0921a94376f1941990e02e4f4ffa7e6ed64faad261c623f84c2af366b6103289a02ac30c47b023b36e7fa7688561462338b190435031062543adc8083e9b8a52cd4e178448c101062e82b75c25dbef93ffc88961618622b820a93eeade29a1b24f047732f5ef4e9b5b6b66ae30031128987108b652efb3a978a4c298610836e75c1d7fc31c1ba0c08c42303aaaa5ea87cc1e51f01f4679c18d5f4106ae70ec0c4270ee61fa6fdad24a6c808131983108ae247ee4d1397474c9ff0b30ca243043108ce63da1a15de27f84dc308196b180086c2060462078f136a56434fd432b53f03922b0003eef811a350e10fce937a5ef69f1acd63c38ccf8037779622e1df378b258316898e1074e575a4b2693d9074e63ce996e29a253b06ad4d8c20c3ee06d392d4735f7c0957bea694b59d2a4d0f4c0459710f4a7548d1a17851979603ce5fe967249fb1e84073ec5c817b3a9fc0efc5f789a9e4c71534eedc08fee2c298dbed48191577a5bb6a1031b2c0611838f2c259436074e43fe2492d96d7a901cd80a69d94ac51c925ac8066ad4a851a30933e2c0e776a5e89dc6dcf40f07aeac946f86f44a0bd137b07e27fa64d647fde8bb81f110a4a765fd0df6c936f09174882725641f69aa05c30c363039688b23b3ba3ed7690d5ccca91964e4513289781766a88111f7bc937eff3430d93be7d6490b5144101a98e09b54b2a89cc72327c7e3c091e371e068525a98710626f578c55c328d7d9ca819f8fc1e47e78df6505a512bcc28039b279a92a9a9d44ede050e30304006ce334f74ca0c9683ca47c0046f811a3570d4a831630c279821063eea696bbbb7242b34230c9c55e5dfaadc26f4da1b5d34066a9412668081dbfea4e6bb6f95b4fa05764f249be8be5d5b671b6678c10ea1361253cc7781ab102fe4303dfae31e3135cce0026fa3b6e4a61ca480195be0bfa3e897e72c26d2a3052e69b1accbb0189e6cd4050bdaeaac0433b2c0e6ac71737b98fe901b3bccc002bb92840e9e33bba65ebf00a30b626e987105367dd5bac72039283d5981ab204f7b664c1518bf13cbb8b15e84a8c077b4322d5d229b58d014186527a69e213753480adc25ef28b69373486f1478d3365ea91b63c4140aac88b85b59c9f3043e4427652ab3e304467829b53d794af9c79bc0de5a5aa47ce59334ce043e88d024645497c0f5e59d6d59b49c4525f0a694f9e4d75c5b9627818d9ccc94aa18b525d54860bdb29b6b57fefcd111b81bf5973ca946e0dc46c7f16411169d74b89590086c5f92dc69164a2bb83386c007f5699e237d454cd91942608245ba9843fcf879db1941607312cbba9359253b660610b8983fea7afa5c79bb99f1034e7ac4f7329997a69b193ee025073d32855ad1989b193de07334ab246f3463bcd31766f080dba4843e1163362ba9d9019b9245b0909374c0e58b46b3f6e480c9563a42ea1c074cba8a183d96f2d518bd01df51f399a8e02b221b30bee12f7af3d4ac643a478d1a2c30336ac09a567b724bcfa0013bc2337eec1cd94ab3e04226d74ccd64aa29b2e06490496bbdeb7790652c38a95127ed7710165ce8145b24085522eafe0ab65c7365be64df96ba82cbb8ef1d622a6c2dad6072ce27428c9b929a8a15fc6ad2bc69ea3d720457c18550ba2d795d25758b2a58db6c97b285647b7f2a9890f3b8ae97870adecbe458e64e6bac8d5282c729b851d79842f48be8eb4cc15f2991d3268b1b538e9582330d22e6bed348c1c44ad72443b4b8288d822d1d5293d02551b09b2539a9f0ea607d28d8344b3b93dbdd220705a3ea72ad289ddeaefc04bf397a4f8aba59c1e2093ea99074c671ed64b9ea04af75e6b15a249ce0d2f47fab7d69135ca76589be9bbf93724d70fe399e946dec902ce891095ea36849cae48f08e9c5045f39df9952a14d786d2ec1ee2895f304cb3b3ae6e90c3c2cc1b69daae78b5ccdf9af0497429a9df00d3278e45082dd572ba12b7766c8492eb6e0c2bcc063127c04ff4d5f5f9204df31245f59cecd5ac21a356c54223c22c1276dbaa23f7520c1694d297edace11697d041b3d47595987183cd28ee0c32cd8ed4a3209aa6d047beb96c482e8cc0f414630f95e4bb5018dfaa87470c6e25028100744c16010c3307c625d01f313080010302a0dc782d160a448d23c148003502a244a282812262a181830160746824018100805046150201408854481504820516b9db90671c09953c3ff104d00fea586362b38cecec9445044aa0a4ca281ddce3e486c0678d35135c259486dc1bb74c8fed6d5ff3bd463c4a6c50a504555056e23c2e9c7192efeaa0a612982df0e8b904d972f162419298ac77027d99444e53f450ed1135dcc41cba0d9619d326d012b65833afb40ec70e01baa7a120186a375aabfcf87a7a23f3eba2603196357ac0f389d023232e5f8b66a49778c742b00cd5aba133b897241428069ed9b4b1bbd2e1317b5b9341ea880a2a32905b70b39d21f98b07136f58c70e9519fb82151005ab1a277142ddb283ddbec027c22ceb100242e780f78fa174b4f35008b97855e708c0eddb90fd3f91709a481dea4d6d52aafb7040a31e85c8eb153ac29e84c923722efe921a6211336db4df45b6a43b8fbe1a34d1e87a5a5415a76fda794a08cee2e4c2816dd070b881679812e3f47444fbb0f02a6c221058b1effbe44a2106fff080044615dc9d963569084c9b3f443f5ed648248c752577d994b8b638af5134417471c8cf4fab456e27efa89a3421eadf740d12f384b1ed83ec46960881238a0939d7446ed081f83879228a65c93285d3b44c7cf25502600a128303e884b14ac43a5b9a356105862fbabc62658c92581bda1011044592eec764c341840db3e845ce61f51f0dbdaeefc575fb6f15d5e96df9f9bdc14c32e3405caf196a8e5fff0fb092d3532eead586ddf67dd359741a18f356b4a54c7c271e0f6c004b2af0e5b2294d1b98053c8cc10611eb1a6fb73bceccc9300eb2a55b77a0b3555304b1913ac5877f58720078bec2c7de64e98f6c3b90c089ee25d7b4de69b9f6a9d9443099f77f96aa5c15a6ffa08155d4b7b44be316f233810756f9793ad41eb39bb5dbd4cb803cb84db18f2b4f3e9c41f6d1252a4bb8d783ed61e3b2765666daf4c528f2d48bc2dc80254490f8b18c04c7cfda26c0c6d4ea9bcd0a2ec5fc75c9e868ecb0ce614aa098e4e6046ed61d49a9d78fb3fee61cbc4e24f0ea590ba18dae721d933fa054edda17cb664c62fa9622d2ea6f1ad4638af0423e64ef35dc4db046f451a90262b46c1e263b6ceed43472720a8103589da56455e4f7c175c9b8b9d5ebbb3d7d6c49097c338a68ae8f607cc0ed9fb547c892f550cdde50409f4503b8d1a5e19aad4dd03d4637aa4055f5defe022ba2967389a4d138e64d07989661306df9c4c5818346bb6b633dd99065e0cc92d8642ef6519dc1b6b2f6cd841b6a2bfa3d66e0c695d8e1603484b71000af7d81da913ff2e147142c4c2e7c642cf1be37c0061b3fd8a584eda3f3a104c0a60f2cb14b5293226056a8593014b4950e3bf1eca4a7cc00c2e38a284800fc1a32b387dc20a8d97b2bd49091a7a3d80c5f9f4475ffd13b74515abe0d53a41cf9ce88d3df9e9e2d8512c8e1735675d7eec7edc107270057a63af1b49b0aedfabbe2dfc8c5a9dbccb7475a902dc3c06efc57e2531f4c01a619f2988b8314719404a9bb119e35966ce8a28b1f06baf70c24778ece25004df46d380e78452ca7856958f1b65fd7f047c27a5a62a126c1f14053e25fdae7b43287cab1f90c8c7eb60b19519673bf735e60fb2ec49a60575f4f268258278204908eeacaeec5da3f24a9fb7e007975710690750abba1739531228416cd633d0522a515847ffc5a4b89285b0d6f57c182cf9f1bc62d2aed976cc91bc7940f30e336168a8a3d18ec33a859ac0678d866eb6674c7354b85a0dd78156a01a93cd094d2b429deee6bf07108e4e5c87ec2409ada82cb4a14f2eb4cacd7ad71dc1471b25c4903935386089370108d7a9f2503580724093b98ff2778353f635d72a5aa0f39db6f1785d30329578ae80b49968d31941774ae6052a42bc5c0461aefd76238ad4028c9ab003f5acda9a7d97f90011b2c3326ffe97b7f8f5a4e675a2d0eef6d94a3e4fbe25ba24b65d769edb2b02b4389d12c329ad3e52e8587d135355f048e498cc171bcb8a3d79a78995a7820be964ea809f65fb0c7b830eb4a410ceed29828dca0f17dc081c17521c1145d995484ab5cc44654faaab8191985c43c5ee37bde7ab1237f4726758c876e2cd56c2b65828e94a65c7428654a159e273790fb1d1c37272ef8158d7a647b55ec8e3fd42e07b08b91961573286ef5686fa610ff29fafb8f64e7cdf37adc40fe15080115162f8e93d3d950003811d4adba50b3268cb34422b65394ff059c064ccd4a2158543b335b9688426310273a3f29f7d07b295f3d4fc0481a391661f1d135900c9266df5f36e529a66163d89b6818f467cfa796e273f5af0f2e58ca8ef2f6cdb7c89e405e6ebcac1b501d62a65d542cb1e8682d552945ed1b34834548b938a3dbb387b127d80d8beda4e130bd9ea48f391556bacdee887b736768391cf985df19906d42dc2c99dac3642384e95595b3688c969c57c95171945aa5bbabc883e8b0aa6f2693a9a14185a540e1adc4a40d382c753f3ac6b43e0f34105e6b5c7770bed5e6becc3df970a2ccc958cf24369f26db8e112f8f317102cce41d84ac03d8ff9f2425f6f12ca1c093a70fdf94c998991f4498035afee45682c884c871575395f9d045481e31a0504fe6a802e48c2cb45a2da057f53a952369ac1ccc9db427dafafaf37a0900595d8ff332302314023b6f52280be63f258e1feb3155500337e70cef42c77a555dc56a5679cb371e61fd3d0927b00fd7656769ecc78876ef20ff2c28a41c42258a88d7536c3bb2503a4b63600bcc1c4faaad06b12c042284ae2c21057088b51345965fae18ee323339b08c7f7921b82f0a2198bf565cec607202c8d8e0da17c57398adf9e4012fdbfc1ba420e6ef17270d7bcdc1e685636e61056630a86e2c76a05a07d563cf731b944caf91412711c2eb17a26004932a0b9eeb6b4a32504a1b1ab50a31b14a5a2142175728ef190c816a2c76f5c47980de15c8400d97a2927907b086d581003be50825f111227010abcd950c237b80234c92debe9425b33ba6c38971661bdba0976e4b1c172ae82b5b7a563bbdc4860aacdfeb499b1109660a87026c7cd40d2d64f696ec0c08a3bd9f1c4c49902b9bdba128222c4ab189a2aad5e19b8ac08505cfd1ef8ea8ca1ceb708aeaea2e3c3895bd88bbb8d7d64e8c0d48bda453aca201abc5327d5e0e205c7115364564a214ceb80cc0090636b6c5240cca98699fa3606c9ccbee99f7164d4e3f5bd4a535d43a92d3724a6de1269a8efd5c9fd0d92b95b41ac95fe66db8dabc01f4fa9f8ede3451fe27e9f3eda4bba9c56218b9a62387d0ff377a36a87505142ffe3d8b15eefd67a582860fe9baf165a70be8412edd110f5595068a5de4757df737a9673ddc6fadaadfcd7741b10965b68a001e15f35975c8e31379ba1f9fe818bf7f17d92e0e24aaa10403e4e423cc01743787b1a4b55141a19b44a2185212faad04a1f1d24cc0f6980129ce2f02684f8eb45a1711ea7c10696822128cdf9fbd07c56a86a638191a67c6dd89acd5219f783c51bc3a29ea2c619903dd01d507c3485f540c08c3ab752a36a800a397515a90cde3437366b2bc3ee43e182ff2b0354ac7fb28265fcf02cf16ce759eb99ebecfd59d219f76f37e25c27a090b81dbec97cd20b905b94413938023a5665582f60469cab15985afccb232aaaa00c68607f1e6a7a179678bf03258ad56c9ad06103416e9b18754798cf9a33a7bc49ee2f97ceb2c5428deb5386f3812a086c343dd597e763a4c1422502c4772b10ccb439a18a5825cc7aa5ae5fb3be72ad168636214d6656192f486b85fdd1d716268adf6b289681ace3b317d5208ea7165e58adaa80ef59dd53985def1ae8247a6da51508d2b169e714185873ba6236d98c5695ad0c5b6b6dcda9e69b94f12cabcfc5c1a8a38483129b03bb9498e36c9e45ac698ac58b11e0fbd306d4e3523dd91cadedfc62ee2f3dce3739febfa5c9d863434060548266286590b2e943091ab81b28c323cec734f7a532ac56646677ae1a49eb2808718221acc6be168d087980de3034b26802a10cd861c034c5614414218b92cfe0bec11551df9833e6ff001acb508906b4cd08873a6932dc6ded81c94d13092ba10ad9d0f8d08ebdd490181efbdc169f5d79663ffa454ea593942248d6cf5ab8c86afdf4f9a7485053b318683cf0587038b98c039790213de8c46e659f4c115b8bed15c7de0791b79a183645ab0407b8e2419f6e0fe2abacd5b07854eefc94b63fab9cd7d0af28e16899a527f7a790807b7271ad89397bcbb0de85ecfc31d3d4ecb1330351a16fb005f26082d4d9258cf2b5c4fc0439d328b78ced27ea1bc82fc3c0d8df6cfbf9c3573dfbc5ba9f6a91e9e0acfe0f04ae569b5b2e3dcc5769c178c1c69586f1d34dee7f0fd29454f3b749edb0bdfd5870a7ac05829b9410a0e50909fbce0f7039e988247c1b42a839549c63cff5f8b4d796fc3b3f7fa03073824c707e51f46849eb276244b9eefee56a2f5ec27a28012f402f21f6e13eb4ab305c93949ac782911f96efc2d68b0339ba816cb4461de7043419afd9d004c8d616c8080aad947c6e6dfa1d3118510bab20614eadf51945b0d10a503151ce5af6236a7011145829c4d7e9809515f63eb18d8c55986cd2b21ef23df30d007439b121ecce08954f32616f6e5d4a7283209c47b48766ad29f3d036a8e80bab92748e6df41b2d08110baa2c612edaf6174190910a5631895cdbf52188ad43b1806c3581aa57a4408a1ab7d3174cf09586be2af3574aa76752ea9b643d241bd935f4202de6dfb70be87b8c9a8e47ad4a985887fdf63b2325d21707d850364348cf80271cb8bd357079ac1404e6be451909c809322a4d316c255b4c9051fff2925b7e3e3f3a3b684dafb14f4259188ea01a870cfb626daa30e1bcfd88fb48e0a570b0551c5731e9051bf11c126b2fe6ecf7ed8ba83b6b7f23102934ada797279ab4aba50accd2afad3da38165405a28f26382ad0e615a572c7b9b660ce6d585cc3f114be7619f48c31ff0ba3ad1d8a533497e79c81ddbbf035a34148b708f8650f3cd04207496df515497cc0ca3cb5dba4dd52185b95e05e10f5fa2ec6b09725b038744e72abf6667fdc243d71b042f8a620d7da8ee0f7a4438f8557ad7dd5376b8d8028120a2a964e15b3cb1d4d7489b56097a134b3552eee43b302dd7ba1e2557e57d077af7c6e5dd2390ba2da1df7a82b93895a9657d9283b4dead1f4260a1380f9681103bd80b0c8486fcb36580a087cc6d83c9b4834477c3738b5d51faacc947d58281d8aad7fe0ba097dda19e6528ee76f8a28d144caca626488638a5b53432d15bfe621bdafb68eafef5732614bb89fb5008ec5f284e20b04f70ec4a23f477effccd106c558d4b7528961ed1efb8d4260cb52d3fbf7b5b464492a59b1b6b89ecb2b89821738c5f1f4346ea65da82dd1bb2b6c4cc1aaab6dbd29931f4aa28d640b3c75c8715ce4b27740b1d21d91fb18353d829da93991ae9401c52b7cc10efa66074ca0c55c9adab29182eaba080fd268d5aa27a606a8c8044810bfede14391b0159f6fe2f1b5e3ad614fb840d11a1337edcc3b14e14e334eb5ae9e906a7a3f727324c7903d69e5a06d6f66834dc55e002eafe4b62e102bf0d00190785bbb50666223394995cf037a9fd6a4d8e252587113952dcb53168e7afdce3b4391422051405721324461c147173ebcdb2da312ee322724eea8a93c0c60bd01c5b2ad24b730ce4194dec0c4887642d330e65d8d5ce3a9001cf57676cf8410030cda597dc28a5db60ff1f8d362ceac28453e2f7d29142ef9ce2114f91995ab4ab90e489c395e324de814952a3fc5d42037cb81ef4559090e02cd96fa1661275d15b90b0ca8ac6767265b25d3d73fe7e4d3906890801aa08ddb45a01f3e9117bbf3e60ab358efd2c0f1d1d5e2eb954f70756415da71eba7e204bc54a5809b4729720e2ecb74291bdb02d647d2279b06f05f1fc62c1df57c8af658b2e788744dc72995bccca4a843a00245b21bbf3387649a3614a904465a19c02d6bad4239222943e5b74418243ebf135975a51cf7019453a60ddd1eb68313a266720f3fb6ca69dcb875a26f464fbb0ba77c640f822f7e85076bd5892c2107f9108181efd56e152d7da11e2135ef215183e04e0340e8e30734c49c00c093c688b0106d8d2c50e049ee6c51b4eae27f71fac90af2a67dc89e6d18dc48ed617666d190d3e1a942501ac53d4c6e0001b894aa2fe0fc67e15158513dc63e931bfc13594cc1078a515a97aa2973c6fff1bb38e0b1c9b3c0d195ee7ff0e93d8ca57ee1390661031d1b2b6e1ec99a250ebf336fc4c68f1af6eb48d4f7c9caee76638c351585287e0cfe2b52e27801d845759866dc9cfb444235bb482ef72dc0db2a7594aba409d403c86a99d7c95fda41ff85e7b8177144e2b72a1d395dda85a06ce9836df559f8558ab3421d084351adf4177208f867faf6136c55f9f6dba67821bd954eeb526abf45bf84d29819f628336995f4e2ae3011537654913f478cddeb70b6f664452f1e58962c16f6b1b9918912e87ba6b53c9c7cbf2e587f065c7be079524a075bcbaebe38e21b0c3c48b9db5960256e430f5a38eca08740f362fa4500ff6665613aeb4bbac517cd4da283983ee8f399e652385899fcf0e043cdc6e1c9d4811b56baae5d1c13ceffe7e3bda7a184e1d815537a4bc7d87d7c0f60455f0dfcb502453186945564c1141b93b233133671d1822738901afbcb8e1d3b0eae4dbdb6372e07787e874b019088196aa785b4383d0bdb7ce1aa0d86422b834d5b37e3d4901f8af6d14d397ac8069404b98a873443b1bf53f248744791af015bf4081571549b992fa65048dbe96fd209b4e228729fe1f6eb07c938c32b53b33423838c19c17c044453767577888f52e72486ab91075da28a86047aa355d74c1bc4c5dab3af560ee7c937b78df01d828f166480a6937950b21750705552d1e85c2df02aa6168c8282353bf98f752d25e3f99f92f17c844df8c40d1c650a5becc793f3af4683c28efd6078cfce002b12f5bc0f0355e003214122850d29320c32af2003b5355a5e49ad767110b0a77203522fb01254baf2eb290f6a45688cec6accaec892a0536d96724d9ca274942f66c8c603ab316b695092c70d74aa0961a6421763092bd024014ff860bb0932156b32a39815275fcc78b4ef9a47d334161b9cbf31372273998bda3985bc1f0d6d44d66b369addcfa4712b2ed5495f453c020484c1d9b4f8a46d570a923d4d17d48812c3faebd77090799cce6d6611cec7e5b8cd0fc3640f8bad70b1447a69bd50a21cdb4d5602fe5d4f6128b9f6c10eacaa281609eb3d0e844f758a6d2a0063d42baa5a0df7c09c801bde26f48d1acbce83d33883235562505ac6c520fba8b7ac99df2820bd35e1c687f00c1ce601558ba14275342b69184b188a0c3abbf9faa2a1dd283d62112cd6f5fcd5681b23c3cc5c4e5969f6e360e83e0f25eb476d67a6347ef718b465750602b10dbe5ce3ab07004173777cdde3056aead6436acb193eeacf78f7abc3b621fc7b77a70d968806924e36627d6f7f6ca3da787ed373d48cff077faaa1feab2bdb76fcc4b883a3454d39730a7dead11ebd8c64b89fd948dc8ceb49db458c44b8e94eb82ac02e8acfe439b58d785085233d86af7c561292f06bb781dda017be72ad39554d901c4be5bf4007d087909a7d3abebd4f39ab0ce5a9f1e2857f8d0f312ee016457f09d3b4dc6fb0e2b732ade2cd0da510bdc72ac37763b8c863d147a9f61e8da4f236a486da979b549bbd7241b8db85cc7fc0c91bc036879ed593b6bafb5a2b6b935462f35c05ad67d2dc55c68ae7a2e27a1ca3bb0cce318921275caedb677d5e572df213f179ad691e04072eef01fc0170e411fe891e12b7f06cff8ffa0ab624e5ace757d93d5dba2b1e61e7f5e8fd7cf7ab77ecd1f759d5e77cd5768ed316d0fd2c0883bf7c64410cf8eac8fedcb7d389c2fb44cd27d434b7948af36e3dc521bb47123b0d1283eb88eb2fea9cc9a4b9b6b474da875b5488dd6425a5d436a448d58ec1f71e2d3bf5072d6429bd7d2dab796ad456ac7ad4fbb6d6f5a49abc327ca9d097f7d3b803e190d393df97f665d65b29a6a955b89c8d9b594a1055bdbe494a1d76e5e2dd9b376a06c6d43a169f76414376a3b1bedf5b05a5b34f41e34bde4110f38d96c53fb4a7f463e396e606f657d43ee1cabcfd2f0dcb1d8672fb705b92bb0514161d9cfe029827c900af52d3c7f4a2117f6bb86622d4e59e81d64365390d09a14208101f21c158fee11e64518ca1a8c01d14f063850b07a8636884cdbad5a7a4d1e437bde1ab646a44f9dd36386d536b9cd4c4b3a08b8b3890a4e20a48e184d46882c9b14738920f06e9b07a2de3ea79a1c96fbfeb01417e3dd0257c3ef2bf7bab55ac6014d55ce2776605e486bd3f0e97c176c90459f4ccebfad0eb5225ee0f8cf2bab27824cd99c7f88a3290372a62f4baed68ff3ea7b0095199c0e44c37e7a2e01a99c1fcf7785a3dbc9060a84a45ce7733f6c71c7c36f58e597a0a66e17b0794cf0878a10deb8c087aa31e32f17112bd030f2961169fe15d92f497de993a850b3f573a6fcd287529a40c4b113b5cf2d8df3649d101b8ebf562bb22b8128ccb9ddbd39a87632a105bf04607f23ebfeac015205115c5ced692b693bda08cb57636a73456bb1e93ca0395021aff1a5974fc4b49ecc87ecc65fd5d11849517185ac84fd99cb73cc7d13a10dea53b42d34c2bf4df4064d69e5b420eda2856809ed41bb6bcf5a59bb6e5e7ac80be090b4303db7cf4d5ec20e74620a7f9ca6c219e32b33438b40cb5ca15e0f399c8bcc54d3230ea58db7edc5c7466c1a47a5698824270b0400e9cfb1b3fad1ba4892d7ef11bb3578f8d71a266739b324295096769ad2d94affae31a4b83507974544f29768935a3e09f0932e0d817ed744f735379f04f8aa47a814690392b38729d9c2ef6adbc6d5a9c63f5018db5217b6da72b0816210b3ae8253de93ce7992fe99c8bec06559045333f394ed4200227b18ae668809cfc576f4e4843ab26eb54618285e21b52365625fb9168096a5f01aefc3bb020de372da8580a4e9e6c5eaecd99a415a7f107cea08217cc2b08c440831707036383ae0a936c800dc3c345260c616932a9797e079784efcf222147223f8584775ff098d1375a0ce0efd7a27956e64568995456120910ae00781893fe35a97a3092e6ed116ff994e136175cb24e3ca16765f6a7cc5795f1acd95dfe7504c320807483c395077f530aef978f66c2b06cb9cd506af04f49155baf74636ccf0e2167d73f2b82e26693eb4a1720ed0286043c2ee753111929098a21205254009d09f8a21140fd0137ddedd3ad4e567969e0cc0bb03e7f36abf8f58cdcffa2a4f2ecc6649917d28123d4d64b654191026aab6fe056445cbd1475dc994e39e1f043be3a88598b3a0556dbf894e0f42878031a66be13601fc75d561cdafd6b43de415056f50e21c38c80e95d805722f7da4e73fbc11883df950ef7c789b0f7bf843bd0222b7f9b861bdf421037afca39b40b0d71fd13fc8923d13c012670421a732100769fc29b3f139cd166ae3d5b0d4c409bb2c7fdc5437af4bece30226eb454b8752052bf91abb1f45c51ee744ad21297779cc27a3c8b2cabbb58859d51f424d9c58627a5b6b06ff419f1f28050da7db27722fb5931aa4e865952c681422137d31f0211f1a1164372dbb84f0e19746184ed30bf362a57efd2f9c1f0437d1028a7a1e343566049640d3178015080a544f3313d0912a0b7e84149b453d6cafc14e1a2547e167320f7b27987443022bc8f2f049a9e8c8433bd07605b3c61974092abb440480d2e3e408c83365a50acb3e12fb86b77b0c329725726d56b882d199d82814c995539acf380a117523d9d18bd7b5c2f361e09192ede1a3573f486bfa625daa49a65221517548b60e6dbf237dc6e833162bbbc3be59df41aed2ab573cb4aba8dd20c908b29e30961546fea57a2f57cb687070fc26d48686f973a1b31108bf7b5a89bcaf05ab75ad721119aa3829ae44cca0fd639a6fe64e9d849992f3f1a5b9eb20c8f31fe37c78561302de5844dab0220fc6caea10e8e82a7b087643c8eec32ac173aa428dedc0e24c7138a01bf9f57383ab7f9fdf77fbbf79f2d3347b27cc9635f29f8005c750bfa46e9e1e8f0e726551afc822f669e45c6b4c97a743b30ae54e447552ac3dd06d9bfb66fcf619f0d8f28373c87e2a605db71633ae50fddebc98154964fe9d06804d3abc5277a3cb6a4c8a87065940e347db0d622bb567a266252c8c7833970fc1842ae10b00cbc00e4ca6a0c77864530ced6ce1be5ff0da1f88826fc183314c4e954d90dcd8aa7c7e54203ac1b8af22a7f541f4426b8cf32ad48bb27a5caee1b306a3a0e431c04ff4c286ef60464b1964b1a123cc6653dbe3954e771e6a017dfa4a554048058a3c2d140104e07985b8eb3c485dfabf245a842340729470a7f8c4de2b95ddb90b3fc3e1b95cb3be2af561ae66b622807600fe357a06277cdeb8c16a8392d29cde82152e4e8e40848fb8704bbc7432b1ebd2de0c543ad8792d17f5d764fdeb1c8981cf913a93431bff7d5d090c59f49cef89cd4a518568a192a3c1abb6fdc825054bd4c573cfcc033189254eaf3ca651c46560adb0a86287f471b2edf1a462d0288f73450acba92af8f657568f896e6d705126d0d205162b31eba5372cf27245dab01e0d23e11eb8422fa6dc3608aae30bc49778a46d603dc2be5b0b1065557b84e48ca2afd2153a2b56d58e290557bfea3f4b5e2581e95642b635797fb6657792c66b16cefcb6cf31e768681fed91bc454b704edfcbc3069e817f65e52861c4128ce22bd2b7dee7dd20d96f1482497ef54cbfc610d503ba68cd6fe4809678be64e9f215a91057ca0dc12a0db24e28dbaefb3d5e140380022c3ef403f3fffbc6e817a82bf5458da690abb680d3f7103ecb65bacbb51a86d6e38f289a2edb763e1315067a2e2eb041cc64ca72538680a09e90ad10a87231f6ddb3076af7794135422118ab0beeb22f3c6820cf88dbbe89d91fba72d92b476df60060e4b5f43e20a59ab30038b0c15b6b7545477fb76f8ad20af2930d96e6418411644b9b655300e1916e39a5d95a8658a97890958a07616955c4620d205b1708b37631f86c79bfe4b3d133c1c5911234a3dc82ce29d5d01ca40e9a538ea043e5193a1fa1057cf04d412400bb13c00b00d2a4642f2db48610d29ce82e5cf9e9da9f252f9fb81aae537fff496497b80ef963265c802c432434a6990b1f9f2afb5105a56af86c619d0670f8dcf7b443e6d3502bdfa3fa86241c4dfca4b6da7463234df8ba2f6aa8bd5825b8e0aba27d8ff1dd16ba820e7206db36fdb11c8780568129077ad8c41d6f92f2dc92036a1b2d6bae6092179eb5950f93a6f32f7387c0414e87e82cffcbb925d63fe8ad58e6f164c704e442f515810852d3b64ffa2f18bc825995686d884b8995407a1afadee0c90b6dacc15ff944152faf00b465f4383dabc6d51bba8771497ca61edaa267134215427fca7935a3b69b8623945c121f31c2791e134bd2f27f4655eb04a9f2e612f5bbf18589760eb9bdfa31aba5e1c77d277b96783fbb1369a089b169812be5872e7e664930316976ec9f5ce23466130cc9ffb2e73728a81decbfedeb6309a6cce6d9c329b4f554b6bce7827e9ed715f8beccd19027034aec642873ec274ca2a0f3ad83c480193a82e9b64f93c60cea05ae0570ef36ec6b28654da989c2ba23d92a8e4d33dd0e342593529103975cc045cb1a2487a0986a964ae4f0e296ecf28f648709371b1f6cef56749dffe25f9a30795aebad5b5c9010f1bc047ee18ed33a27252bfce0d450fd47f2c6ab8aa639825e49dc60262fa4e687ff22512340a6f9ee55eb554f1005da0101d5169120f400020a8db9d4fc29ccc027da88d6e1823de7e0737c00863d560eb8647c253093ddfb325895ffe98bb85257d46f134edd9093c98c5a86c47985db8a87531ef088416fe913f23af53aa04ff36ce9ba4b5e875d07f7329d48be80516c8fcdb9d969c6be203f1bca5828bd9e3c15d693a0d1d818e164f6a5a8e92135b1e6ab8facdefefd226a481d2fda8f85393f95ac26eff5e58f6a8890742320283ab06ef742484aa991bb8518adc3d737183209f99736feb9b6af47c38580566a323c7dc83c86493e4fe59464adc57fe921ade491764a505a19dcf8f3bddc5d62616382333b2bb6912738804de0403817ee5f489ecdf736794a44f369148f6ee5ff86b55fbd811cf927d64d5da56ba0166962526b9b965c768d78783e007dbd16da08a9a2c40ea536295094b628413f91735b8722f624894d9826fb2707a7a96a08409e0d038b191ed390466fe2960c37939aaa965857e6a98d16d62b0e9e4f6124a0668f56921b27c3f56d153649e83800d9642fefad6dc3ce75839539cad08c8aa7e9a1eb5656529fa2e5056f2d6df57ebc9e6203f01c243c66c1254dfaf390c061c2396db5cc370b8b6f36716d557fcfb1d3157a20a0edda966d4aa8608ae9022f92993b630a9833942354cb008e5835318d1b3d5481248c231c96a170883d759495308914badb17ab570b9cf99258def046fddbc207c5cfa1a0ae27998f1ba9467ace564ec90dcd2802bb6c229fedea82fd1566fe33f0292a8845d1fa8108eac63475ffe698d790df940296d02c2867083f4acbfd8946725101bd724cf3d1abe2605dbe12f7a11e47d5f9e336f8125f6e079b8e2dd21063da7401316879d14156f55bbc69c957a1e1555c61a1a4309120ae8f45992f724d513d63a076c8ed11e65467df206ebfd5c458d0789b67a3df0cd0f58b4df1d390d8a274a2ad7a3bd2f7e8333217b023b8960a70beb9f28eca20b5fbbc91e14b7809bb49b7220fab838671f26c4314e971aba26e2f1ae632d8d68b8927908c14a09695e26d4649eb0b31888d140bc83c3bfb8bebf3b5e31ffc515bd01f7fa15aa8eec78c13490c25bdb24b51c7ab537e172ef932d598b162efd652e5cf40ff66c732b67b7015f2da70da6219841bb6c1680407f1cbbc9a87e8911ca490b15cf24d594c7ea0020acd3f7f5d8d4ecf1d2a2fed909576ff5235a024472940d377c635dec7ac03e201de21f7536d29535bde74cf35947d418036644f22563265cdaf8400a32591cd99225554030935af495df074ea6b0ff0d79991e98647a169ea43d2489dc9cf0862b78beecd57bf044039b6da167d7a6a74336246d413084b45159f37dabb61eac9da9234f225492eaadcbdc46dff2e404ea372f6cea4294c88369d4f5bd4e4641cea28d70ef6f0d050eb3d7910b461733cc6921bdf241313e441b1ddc07b44065b9593a5456e3cb587af1568f738f552f3aeceb1aae1e11857ec9e7fb0a4f956cdb16d9bba8b303afb84b546a9ac28f2bf091db83ee0691e62a348b71d8be86c3d3ca1f5e3860cab25be3b25262b7e17db076308de42f392d3e55601ae3f8854462c343310e4573ed5940710462e20fc8b351514a1e7058214db871ac7ac484750f98d8711fe14e8a4a2cc477ab2d2775dc15f6f8ac36c559a678313d97e74bb93e8b8816af731eade6eb485111b0157c2b254bb3c6729758596a3e244fb43a754706edffa34d9eb04a8743aeeef20d012a8bc24afffa0442b43e240b6091b0db882186798f699a07ab6dc5fec039e8b6802e05526c66b59c4c1697c0410cd89141cdccd8b989c279f0bb53182421e3f246a4df53176df5304b510190354654388a3d629e34263c890b8248a35fcf248ee12c554290b25a794d8e4ca046c0990ac8e6ea41e0dc85648912b9a952e13a3b7d29623ddf4d47ce21f7574ee80f24d56f4c554c7e2b2e9991037641c9220288c65edd16e0d2f2cc14cd956c613a77df4db331e96e5e84258b0bddd03c7e00d4c2a0faebc317006c7e487799f31ea0a047467022e1c44ab0a3540b0a6d0013c0c3c0c3c0c3c0c2c25e2b73e4d7af9da90524a8bd43b3c1de2c04529934c49a6c879c3ac66d452f21bdc77f0139c0b870aee0a310a854aa762d20892003207dd73acbe34c2e4ebd741e4a088d92d319f6495c491c741f7f2923a9d7cec32e1a0c834cac5ee3ec917f10d9ae456f2fcf725694b03e206c5bbd3bdd5bd69fb7806206dd042e652a68389bb8f61f0300dc2063d3e434b6d8d89a7533f7a74d90d40d6c0688955a3ff6b6bc39115c6c80d1ec90d7ca43ad6041035b401240d6ad222fc4eb824a8fc972c40122263e950c93ca6c617366c60dd4005c819f4ed7c5b328e92a43433090a6b006206dd5468083dda366efe560990328090a18c41cf9592ccdd1fb7416cf400220645d6ec8d5d856bb0510e1e61308f202c80844133cb7f5f96e498e27cc30718fe23c78dbb3e808041edf13af96df367d0231c593a74e4781fee3dfe0b9a5c397d0ef9985f5d738e0e63a418102fa8a779f20591c13783205d502f3ec5da33613ac50947d60d8306102e28a3f9bf5a1ff971c3ce00b205ddc2b505fd6515b7651c59c540b4a0c9e5b2a0e84bc29fe6d62432806041bf6c6964e85b6ce966844718233d6670ca868d30805c41bbac3896d9db4e92e11e09078815d4780d0f72378c23ab0afa7ff925a5e48cc8970a9a9787b757e63b49467a4718470390292832427e3ed19795c4947b20191029289b24bd212b2eff940847d68d1b3e121c685150debd47787c2c592ee2c8f21e090f3046f8c68d3e5b010814cac608409e5036409ca0279917795e1fe34e0e47968f19243a3690f5029026a8276bda52a54d05bdf0880d6ef84870201026684a496afe734a154c6084d163248c1a1c9025b00044096dfe3e7e8ce8d091ec0def51cc4e0e2049d072c36f65e5b9ce998223eb86ef08a3078f2a5c1024e8259cf09777dcf652ca7170f2858f3317801c41513e173abdec49e1bc94048811f4f36c9739e8f854693644801441319de4b4ca4d7e002182daa62f7cf25879b32d0e41afaffca9a41c1e5377425063952499a49312574a5977000982ba9f93ee75be98f01004089cd262d927a3a503c80ff4aa5266fae3652be6417ca09e2a693f77a86ca57d2fd45282de2ac1e3ef97ce0b3ddfd989f2b877a149e5efd77b4a9235dbba504ec5978e27732e349fed8ac144a6379b71a129794f85d2a5ae4f12be85266b2c081da19795846da1a7fc4fee3ae7192eae85ae497c0c9ae7d242773fb9f9cb7251d6ce42532a46c57de7e43926b2d02b55dd78be66e82b89855ea663491e0fb14987856e494e2513377deaec2bf4fc7d9663c49de833b94237edfb315f4a501ba65668b9742c654a09b142d1e13c8312511b4e08ad42b17cea243f13a942ada052ced1a6046d73a642d9ad0f319bf5a7c3890a4553790ede5e9e427dff9a93afc2e8fca6d036655399f76b47d9a550b4648a29db6de7a60e29743129732f957ca8d38d4233d1ed1b71a1377651a83966f8937193c53f149aec5c17ea3ce54e6a50e841e6bf5f4c56dee8139a5ef99e8efb29f1f3842627fc6ce3640db37542df6cd3aa5c194e282a29fb96d7d826d43c935d297da6092de6c82044c62561de4c686e1b2be4c905994e4ce8a5f34f85ddd28c99ba849e355c4abacd74b96f094533097a1793c92ca64ae8290973b127587064ddb861490945886f8ca9e4e42e773209c53ea86427eb2409f52d539b244bf75d9e988811095d2c7e50e1ce83849674a6e46ee1f55df22314fbce976d634ac229b523b4d1f3ac2594a811da66998a4f5a67849a33ff4edf8e129be38b504c4fc99833c594795184fa7992a753594b63a612a16997df697193252549661103118a9097192c7699958871082df46696d898ef8e3d9311310ca1c9aa79fd8d27a57cf91b310aa15edc984de69210eae6b0f04b8297709af71163107a5cdcce509e3adbcf59871882d062c85142b7fcc39529108a6f968c6162731266b3430c4068bffd9ac2de27b1242a36c4f8831ae72f96e017630751f231fca0cbccc84bbe190c62f4414bd6a137a8cad7797f1c5923bfa31021061fcc6e61f468923df438b26e90f4b8e1fe670c31f6a0e9cd202fc4c8a0bcc4c821861e14fd49febdd1d121461e142df5db69bbc2e9330b0e31f0a07dd055496c07257d6dc3c6d91df40afdb6a0bae352109f638a1876d04eb7443ba5aa734d8da8801331eaa0e7584acc9adde1f359e38be48b48c4a083be2789927dd93389883107bd832ce94a4962fae4d7430c39689a723c2567cf1bd796e2438c3868c162d649ee41377e0947d6485f1562c041cdbc93415dedeee78f72c47883ba23f325bda61ccb466588186ed0ff642bc6d80b6132598388d106358bdcfc88cbef2986d8a0689127736b0e7262726b50c3bcafa42c5342570a11430dbad9999843bd8c85c5d3a0a67adb0ddd2941c4408396bdfde249ca5492b8cfa0d798591293e6511e629841930df2e4922ff55d123cc42883769e5be393f0f457a58e1864d03f84be8c7349ecf01ec7188316e63efd8566fe14ceb1375c47ef4870941862503b75f68c121f619c63c020000c214618b454fa75bf259afcd4d6871860d0775f4eb8f686764cd71c627c4193b2c71474578e79410b11a74c70cf5cb2635dd0cf24f9ab45bfa90b31b8a0d56dbec60cda29a81c17626c41133ea9aef23c354a92b286185ad024fdb8a7d29c681c3f636441ff0e26ac861858d036680aca33bce43c7d05b53d56c99b51f9253231aca0e860d946553a121ce7811855d04cc924425c5e7e90a51b31a8a02929a7b798bb6497950a624c414f2a73c885b6d3374d0c2928b22e6f443bc6135eddc8418f1fa5cc8011230a9a8a4997ca27c3f5a9180acac62433e992bcbdf4c4868d3c448c2768233b9da73659c4b6ec043d85ec1376b931956413d41c3e4e924a774cd0e653095399742ac558821a3c5da6d0f6f01f39ce470e10b4ffe831e23e5260c34628622841ff30f661d225193a49d0ff5e4fb864fb61498f044d7909d1b9374bcd47d08476b93ff9ffb7736e043d2eeb248b93929ce32e82a694f68dddf21f934d04cdb3c7ab3ec443d0fee38d7fee08419b131774bcd0de650541933be1535cbf9b5c18085ad89f16d34157a8fa07ea499b77a3421bc3076a78e9747a96fee4d8f642998d41999e6b79a17d988939446b4edf26bbd063d6b895e4555941892ef4fedaecff36a12e95e44213ae662bc9ac27f49f70a14931bbbf9297a0e6f65b683a9f788bec6da1c91d6ef704192cfc9e6aa1664dd94f58124eb624d1420bfff93e252963a6529a85163bc5c9b15292ec5e92857a423457d60f2f56c742db54f632a35399bd0816ea7b2a2985b6e8156a49f992cc418968d22157e86ea2def292f86f06b5420fa6e193492ae497ce0aed7484b90a45e3c77d0cc25325a95485eeadf993aee01597ca54285faf5963de830af583984ee26e5fb0d23985229a3a5bd29d5726774ca1e79cfacdb94e29f4123cf6937462182588145ab06ebb581e4a8ee32894bf3f6daa928f0a1e8a422d5946874aff71627ea1d03ffc28b9dfb66684a0d024a5b2895d927892d8994f281647872939782649f784a6b4e4d16f7eb7b9c64ea89b426e68dea446979ca84c64689bd035639ca066fee682a609f524415805d764424b42c8c73b1f73bb1826b492ec94b09cf53d9c5c42d97039db2b7512c42b4b28ee9e63492f42c95a57424b196c2e3bc87639a18426e6cb13ee4b85acd22434c124933da71c49683a4ae4ec4824c9fc152b73ae4042cd549b3926f9c40ea17f84fe5ff5a6324999c4ab1d81926d11ee17021aa1f559ccc8fbd1fce96584f6d59f5e3eb34d6a8dfb0823f98247a54c427dec924f2c6313cae7d71d1584af09f36e6c3b26994ec7d94e0e6464c224496e5b4a5c0707323081d0712f07f997c421e312c6860e19969051093df35e9924891b541e1513c8a0c4495899f65839c9e96e063226a15858d22e3188a7934b7064f1e091fcc8480f12f8f831a2c3c7156b850c49940d37c8884442a61cf4bb32a50c3e8e2c0f2026c88084625a5ccbb6b249c2c7c8788426f75e0e77ab3cfa6f309805a57afce0a104198e281b25a3116523063218a1cb9c7815c3de245d2e42d1d029bfb28fbe5b5011ea8953f1429669785f3212a1ed094ace2bede460930c44a859692c957decedd2ca38847a61179e3ac9106a9fa04cc6a432320aa1be7fc9d99784109a123af7e710e56e9b3308e524d9ff4a7b0a17640842f913132659388fcb3c105a9c11a2275e1240e85f4a369be5301efef983f66e7d5756e149939705197ed0aa33bc99ce7bb14bf7412f31f7cdf01f3e203a93668cb933197b50dc2d3be68d952a147cd106197ab09468529654a88c1b84a60bf64987699091074de87676504a6df00ae14191d9957b4c93244931e601114441c61df4d1295f7938ff4eb71db49ffdac33425fda741db498cd5fb7ea467e121d3425e5d225ef680eeaa76022d47c5be82907cd2c74ac2d6937651c1479d29f9cc634547770d09364e289ea246c56ec0db95529b1f5f26e383cd69fa05b6e83266eac8cfb41bbc5c4064d25bd7d929cb5925c5a83665bb5259aa43d41a706e5d49b92299934baba34a8a1b24a9727b1910d1ad41c3669bef039b3c6f81994372df9beb65499cccda0e524dcf6cd2469b3f232285a4266781d3de92f4e06b583f26aeb0a77221e833e4aece0ad49e7681331685b614b3e53183431b9c4d47573314b60d05c6c838b9d942a73fa0b8a6e3b6529dedf79d75ed0474e32f94b7517b48a25b246cb8f884a73412bd939f5bcb988ce6f412be966d375da119fb5a06c68ce5c820967414b72777df6eb91f3612ca0ce37aee237be82de29796a11a2ccc4fc5a41f9182b7fee2b49967daba0ebf829d9bca482a2a32be67de4959ced14d424937c79620959722f05fda4509225e9bb6477a3a0668ba3a7d932fe29a1a0cb9f2c3bba4d643c414f3165c78a39bcc43191e104459d4abaa53541b7dc8d1daf4a98a0658c9f4689755a82664227e92beb42e7392941376d573979de943c494a829aa4fc5e2a49262468250865679752ffc9a42368c9b4e7abb7a4476c23a895eb839ccb65655c04dd6b339d8a39e4e88988a009eae5723666f724918c216879f3f6ed9ef86c492443088ad6c82b553229133c484610d4203e2595a7af6f2392010435285131c7db860b1692f103357fac2a994d92aa4629c3078ae713945e78fe98b2ec852247f8869cc9e185a6c3b89b263f49dcd2d985b21d7bf51f3ce9af8e2e3475274c94384adcb29c5ca8d9af63e84afa35e61117da07752f2797ee1473bc85224c16b14126415be81ee4cf49792a4fd6602d744f31279c9eaa581ad242339b51be751b7ef29b8526931ee557329c7293938526c444fb09a658a8d94c2ad9e4da931882859a67628971f9717bbe421375299868670c973c57e859c372f06b7f17e1b5420de3c9538925c751e1b142d3e6e39a3e5bd056b955e8616286ca9e2cdc6aa30a4d4e922a19aee3f5cf26159a6d8bcc1bce73f2924185223b37974a9d5d548c3985963f57f2ce639beca6d0664c5b3a39e860e92e853e722577a8203e889614ea87e5a0938e173a996c145a9724bc779844a18789d9741c6527fe69a1d08328ddcb95639f7c1928d42c268c12bfec92e7d427d4f8383a7fb4f284e673f2f535f394387742dd2e4f62cecc094dfb34c9b9c74de8a3c41ccc338658864513da888d31c4b44c6871e63ae7dc944eac144ca897e7e393b8dffe4e5f422b490675a725cf7e344be8a595736967395382be128afefa64a1eae2564aa18416468385f8be749ae94968bdb1c42bfdd141fd48126af80997e2999cfa3b13094d3a4d6d6382362569112434bfcda6c35ce611faf5954a88234c1742c5aa342a25a4112584113a74245f2c421113084944044210716301218770de31b2e386488821141052081f39701a302084103e3c470e1e186040c82030441035420251801040fc2101217eb87180903e4420840f28640f21217ab8918307066884e4c1870fd7211282070384dcc10e0708a90318211840081d1e1032871322871feee3461c6e2020040e3f7c8ce878030342dca08090362020840d387c24381a10b2060784a841022169981182861cec8d3398a10c3ec020c3c8cb08198318b8022161f01e619110307c0102215e78404817768ce4e01181102e846c814688167cf418f9e163a403202159b8410e1e18e8e134e831b202068460c1291072851c3c668458a10a3542a8e023070f0c3420640a52f0b18090282420040a399811f2041f32429c3023a409040861020d7844c7014296e005085182004292a0ec0837fb947749522624a89d4b10f3163f4750636c3f29269d72e56cbcc71508428ca008ade7d9db4614f43049042db8f1454811b4fe3031094a9874a72c2144d02b864ba7c424c6bb7221435053880ca6c438b252082142b82ae74f76334111b4e0460f4282a0a7ec0c32da0481a0c8ad6dcba23d8f92319fb88ec483901fe8169ff4b7999d9ca10ff181da9b4909dfa0ee85fe1aac64f39452cb563a7673ecb8918c00c20b353f637adfcc17e7cc5d28bf6757e9f45d07cfb92080e842134cdcfc4fa2930bad5a94971c93aa0d73d9710307ef487a6c550d147020478f1f3778f8f001082e948f29bca60a9d7763fe169a870813c284c7246cb7855679fb1f766c949cf45a682af4a9aa4d7a2fd34d0bfde4c81c7a631273c7f42cb436d17c52d92b0bcd3a93cc2243b5273b8985fedf713b097e9f354e60a1998eefedf8a33509df2bf4927ae45d083deac473857a49e5944975d989d8b44211facd24b1db58a1e67f73b3985cc4696d15da9bfacc1bada942efb6af1b1574dc184a85a2174f2cc9456997e951a1c9cc31c8cf5f82de703985221f37e265769f2336856e6eb2c8c8709de2684ba16752fa8269119fed5b5268212e7897fef6dc95c5cdd36871f9a444149a987b92ec736ed74d120a2d5b0a1f42971028b4a0e3c9cbb8be85ff094d8c9554cc524a558c7b42afcf8a59e64b5f077542b90db7259b1c73722839a15696f925f9839fd272137a92ad245c529a9729a9097dc455c89994529cf16442d70df32763f7557e0713fa5e7c3993bd3f5fe8127aac5026f97789418b67092d466b0e5bc1249584570945571e7dadb3d849779450540621468f2c7d7e32935084b02a5355b2e2e94d128ae9b07757c2c228218b84dea9c2684eb290504cca9d739217da96aa47e895ff317492fc74ec38424f5adac7e37fbd5605a4117ae6247e5bee0b27c6082d7d5ef0cd7862ef912fbc474490d87b844b6ddff01c3d7ea4085a70e302208b50e4ba9389951fff4a0cb3a077f8098c0d52116523116aced667ffbb51b9734468594e9665d13dcae37d08f54d2a294bbb089d1d0da1081372f129e3e62c2984b6592deae492648aaf0e211425efe7a67c131c800c4293bfdb2a36fee960158820f4f4268fffc504428d6f71728e38296605d9b081c347820310fae934275e09ee49c7fb07cde3d3ec75ee1b31fa41ef124a8ab1c4756bde3e2489d212258e1f3b0c1f4a4a7fb860a5754f6838b272b0374ac900640f7a99b4d9e289d224b72f12e500440fc7cd31d4ff7c32258eac1a2400418d1c02481ef6132d154a5ee57064f9e831b2011ce4f81f1cb861c3068f14e8486c3002869b60c7084860822f4e00d60082072df5c86cf2e71277211f207730103b249790ed41c60152072d499933b38df8c9325900840e5aac2fb92a1d698c95ed8e34da31a312fa99c536fba083501a836c9841094d8965f5659ba48310c291c563e4878feb61d63063129a24c74a928f093349d4304312fa5f92d118c3e47023c69135831d397eb0f3c081a3f2861991d04bb2114206a56297e6cd496640a26c1c13cc7844096638c21f0b42e67f3ddd98d188198c281d3316a16df8e9e624844a1d4f7064f5b8b16344115a5c8ad5a77465f37d80c138b88186198950c62d477f78539d7d4684f659cf928a2ffab74f0ea185373967ec5e5efa922134693c9dc526332f259e422816a7f93e7d5092d6f908a18689bb7af37751791c842685ad2ba94799c9bddd3304a1cda8f6cdef49c593c47768126604421d1f3d42eff38c550c106a752c19de22df6e7bfb832e724176d55c75f61c8eace30146f2c3878391c39c30c30f7a0e26ffde44449b3cf5413bb71b4f928e30830f9ab9c98f1d25bfee896fa4cdd8c331068d9952652a0bd6413c430f5a188f2987af1c0fccc8837e55f264c778d9b267061ef44cfff69bd96325569971072de5932544f676d0bf5d2f8941d8898bd7410bea3f67b3183463a6837ef2b493f0497a93da39e8a6217ec47e2f07cde2a974b22a1f074d49a9840f11257f901d0e5ab565d17163c34cf437a861fc7afc43878ccfdda0496249496ad3bc0dea98b527398c901f8bb3418b318c78977b78d0f41a347dd38a2bd10b66b51af4b2fecd1afa171e4f8316a676b33ebb68d04a9f9cb9b47dfb09da3368b2155bb69d19f4dfb025277d1f3ea9b60cfaa994e13bece5d12f193419971b2ed78e38e5183471e37f98d34a661e31682a7858270f933068e26f54b9a8246050f3257562684a9f4696bea0c97c25530a2b7941933663d5bfb9b799a80beaa63eabb13a416889b8a0684e6209febe6dc9425bd0449be0f6be4950ad6b412dcf1f3c8307cba4ce2ca841a58d21f245c68f492ca86153ce3361dcb4c4f20a5a329dfd35894f4abea41534dba045e9511b5e7a15d42bd93022d40913642a68e299ca29c98e915b3a53d0ba5cc4e7ba6a2c74a4a007a1a537dbc84441cbcacea5842ec96376a0a067adb11226c70977e3137493359db21a11154cd009ca8b6a934a99b0095a2a6d39f9e8e9b91099a0de99e778c2a7ccdbe01294374d1b4eb2787a62a704bdf43f6cdc14b3687549d03cb79cc76fcb265c0e09fa96e4c9f63376de6f8ea0c89361495f4ed7a61b23e8c1bcfda47011143b8fcb6d82f0bd96087ad09c7385097f4bca21e82264aeb11cbf84d0118226059b1d79b90fb7982068bb5f234ad05def0341d1b145654b79de74fa077a922f6c4efa7929c5337ca0982849d6a912eb853e72949d249dec9d4dc60be563124c4e72ea54776c17dabf85e5fafe8b3d9a2e34b9840a2d71366f36960b4dd0c9eaad93185449315ce8b5a97cb369cca14bea169a6455a764d7166ace774e165c2e4f580b4d8e38d9cc0493cb52450b4d9e6ebd3a9d472f3d0b65ccf7bf4653fc14cb42bdcc23375bcab261120b4dfc2e07ad8ce1e207165a8a99bab4c39db0ec15ba9ec69c6496fc217785a235a762b8ca1012d00afd62948e999b8395b88b90005668257c7ef68a9fddfa2ab42c63baee9224890422b061830448042db8f12301aad04d89b3394c38155ac793249d41b3466850a1c9ef687e0bb714db9f421384c688d6daccfa9ae2ca702756c7949742fd0da7f4f387149a97aebdac31c6f7d7a3d0a4204c7fe598b29d49149a6cf3b4593a9edc16874293e1bac49dbbf8d94940a15e12374f09a22d63d22774b35c428e9ed331959ed044846e9edbf2f0c1d4092dc3cb292194f8fd9a137a87ef78e2e41832bb9b5093e42698590ae2bf26d424348fac3862428632a1c91cbfebed4dd0a7c484a667fb4edc903d1b73095d932629748caf4d62c812eae8abd39354291dcb4a28b293d0b12ca7b8cf0e2574d161a595db9b8496414c94969124d49493c6a046cf98de98486c7db2894142d13e717b8496afa418324b786ddb119a8e3031c9f8cd1bb711caa864736727fc2fa518a156ba1894a758d52e971d0958842698a5b0a94f8ad03c5e4ac224e194dfe889d08498949f9d468426c89279bc663e849a239bd4e5e6cbcf0da157dc8c31cb6ed2a9c34228974cebfb7674aada08a1de59577e53ed3907550206a15bca783ff63196ac5e109a29b1b537c5986413f64068b245954ac265125ed380d0ff7f932cfd41718d493ef95e9377c90f8a8e9bd2ee39585e0cf7413b99fb6327311ff4373b4f9bc23d68a5c4245d923fc5b14e0fe6ec77f69dcae641cf9952f6a4743ce8a553f0eb1753f9e13b68f9ba52669d9ef4257650b3694ac2ff7f66c7923a184a8e25897739871fe1f891634ddd48001db49cc44ca61c47dcc473d02c276da52f891c344b4927d73a495292a01407e5ff94e0a026716c7f2f36492627bd41dd39b919f12508d9233728174f7ecfbd501b14b5a53fc33e095adf6483a2e5e4fa9c8227a14db906ed64c7cbec9cb3767dd4a06c9ee6934ad025a720a641d730c1e4913188064d36d9d8a0a772c7cc9d41b30bcf0b9a54d2261b3328f2ec72ff32883b212b837e498e79f30f611726832684be6066a71dd4a8c6a0e96062fa6352c11d233abeb0612337016250b67ce7820eed79e40e83362774dc7c379df2de0480417f8ba1ad4efc0b5a0effcf1ce4cb9cb85ed0c4bd7069948dce25ef82fa3d2aa73fedb275422e68a12d660c262b85cdde1634f74b9b93b6926465b4a0e909153fa524b2c93231484016f44d15c455ba3c16d40d5ddd2332c3c4daafa0c91bb48fcf5fa7206e2b2832b2c1ccdac2c9555093bcaa198b2f6993980aca2529a93c69be36944c41cba5b64e127b4f5097958226293965a65c924441eff132135d92a0806eda11b921db2768df9682de305ec27cc9098abe503a7b2aa59117d404e5332bc70695e674944cd03a336b77bc7f4dca5c821aab92149abe5482f619ba24931f93f49f044d9b684d39c8aa127f24a8d94d3bf7fd7f4a82ce11b4249f97ce255db992c7087ae56c3a164fbe7dee14411383face110b1693e61041139358b28c9f5071461c8296c13a3c650b85a057e78e977c5b39c8170445775d503949a7ba6420a8b136b7e4f67c5207fd009539967c429d26013ed0ed2441a5e4415bee8ae985764a8f0621175e282707b74c25c5b4a9b35d68a72d5c073bb55592ba5073c96e494ee28f8f4c2ef4da5ccadc2b949658c9c085e2b22979f2fe4d95958c5b285a5f744a952c7cf08b0c5b28b23905937f628f901735c8a88516ac24e1bb5c941021740119b4506e940cfab66983e93d0b4dccf8c83ecf14eb3e5968a66f4e89a50d8eac630119b15047334b598c5fd22c2cf4d14966ce9a3d6bcf7e85a2e775bf4eb8309a94aed04aa8bbd441e79875a715fa49d9a4bdb0b1b5c302c68d1d39921fdea314b242d9986fcfe4f555e85aa6e6ddae4d2cb755a17e86cdb4f14ff7254985f6ae95b3092d41859adbe4a04d104fa1bd7e1eb1db30696c630ac504537ae4550ae59352e22461430a35ec9496effbc9b65a2063148acca5831a954c5909a215c81085e2e6a6735e12a6849e8442cf7143a83f75cacc64648042dfa4a438e13447e8cf2712c332999262b8f0a799270a157c4c448b230bcf0e647402d1400627b4f8023974f88f1c386e30073dc5b4298986f8e8a493837682de9d8e7cb951c72193dfb32363787050de3da96465311a6f50b38406792989dca0b8c5b6923cfb9879b20dbac5e592c3a72094f86c507393cafcebbcd9caed1dc90d543aa0b1062da70fcb63b93f1f94d4a0f5f66fde24deddbe491af41b39569e3288d80c1ab47793cfebca847bb1cfa06ef0f1dcc1524aa3c49841dd7fd72c11d1942cc98d3a8ba051062db3e23f633f932ecb87839188c0d48d0a040d32684a9c3331c6207325d363d0f7cf84ef28f5f01ba6470fbfc1230123eb0234c4a0e7fb92c27826d1c1ac30e839fe8cced26252ce1318b44d822633415f8dcaf9b9028d2f68a2fcb7a4515692e0392bd0f082265be5b5c9f0ca40a30bba6ed6745662e4eecc3b5cd04b8ef915e3ec87c616b42bb55fc292a44fa8b1c240430b6ace494425d9f3c4895216d46457165e5ac7d2854e40030b8a5d50c2f8c6cf545dc681c6158c8615d40ce11e26d0a842e9c4dc7d19c6913e72a423d0a0822654787ec7fc7064dd701d33d891c347190f684c4193e9c4bc15962d576e181168484193b56ff1fe047541230aeafd8c987c2dca53071a50d03447afc297491b667a82268cf8ff64a3366f0ae9487c8cf0e0020d27e8364af2d29e2429e5679ba0de9e0eb541bc62de4cd045db6989490c9fe458db1b682c41b78d5f328bce589d9494a05e7bd89c6e5bc692fccc814612b493e56385d83ff165773ad04082324ae87810fa11b4fc939412b46d030d2368d556aa24993c455063b2aacefdd9a674308c113030d01e3062a04104cd4d8ebb2054d0bd7169c0232018829e2975886d10e3ef498560a0210465b4fbc59445739624bb1a01168d2068ba55c1fa24a51dfa03c113443e46433c347e90bca9fadb3a5ebc638707bea871a3060a4050a368f840f3924678295197356370649df7f01d366cf858fc83482fd4ea707a7eb2c70a4ae485b64169124df983c7be204264178a9631cb79fe614cc9db1e4474a1c61d13339f9d3ae4960b4db8ff70620a9627639ff820820b45868961a9c44b723fdee293213a496ee2c81f232326f01f232228c303115b68412bce2cd5c8ef367164e5a0878751835a684afb644a92f6b528d960a4070f1c81082d1479794ddf39c746a5e0c8ca1c22b3d0f4fffee5fad8264fb7cad1030533d851acac4044168ac921cc93944f495fb3482cb4a033f65472cd16fb24020bb553a514c3dfc7d75c0a22afd0ea4aec2c49363942f33088b842392574d55ffaf3ac39d60a4dcf7f6326995366f722acd09418df545275d7179bc82af4934cde2a0f23f25e52857ec9c5d49724c9a5ffd3059154682925cf6d9795843b2954689a83fdd8e8d315a2738a94d4b59df4c92b49396e60cc98640a4d5d0a8f2f6d954bff31a2021dc98f1114d4f8a2868f1ce8d0919442f1a04a744cf2a4d08250b22589dd6d6f3f0abda4ace17305d7b39645a1e80e22348b1b0a3dc6f7987ab4575d0a02855e26c43c2e66372b3fa1d94942d9dbc88de31d4f283fb715cf53d7092f3f6486ccdf38a17fdc701d2bde832ce94d68a7c424cfe8eb5c357a4d68724adcf426e68fbb3a138aeacace999d6315c498d0ce669387123fc6c7b8c825b41c3c67c5d767bf8f2da105132ad397d8754ab812baa8cc8bd3ba4ae13d9450b4537fd2935fe4c571129a077db1d2bfa8d3614928eac44cb15f772a099a4868d9f143ffc8f064260512493a8c98bbfaea72f0830718da3b727ca0c617353c0c1d890a6a7c512307fe8146a8bc20f208ed925516d7e42dbe7147e825979426b1d00e228dd094991cf1e7e92ab4ce08fd622cab2d290917165e841a94a62a9341c91c4414a157dcc6d820dcc418df4722b4fca43d65b16041891f115a79d073ae733984baed77a77233ffb7c8108a264ba599820aa19e26cd745a9384d082924bfcc87110ba65ffdddb174f721205a1ec27a5c3c92313087dc3644ee94bee181d40e8c9c42c95836566d7ec0feaa6fa12762fdcee587ed05476cbee1a2b227d504f25db50d93b6609973588f0418df3b02e11e149d01d8e2cac81020e341a44f6a09c4e4a86e59631f144e731821a5fd4e031d243033b4650e38b1a19b061430f5a6ecd22724ff897e57164f918493550c0011b360c1944f2a09cb79e0c3bbbf1438607adb2e4741a44eea0ff5f882729a72d13da413ddd72d7bdf184cbba0e7ae7cd24349e69e7c574d0468f2ed5a9626f3ca139e8314e6f0efbf3d9cd23074d535e8a3b715c0dd4f8a20609dc06236058a941240eea9879e7dbde24efac1b3a92ee24ad0c2270d0339f66bb8c77b2f8e70d8a2a25336ed0c3a8ec254e4bc6fc961644daa0271584d55e26ab9c713668e29a2c267163d757bd06b5aa34a6b34de14cb646440d7a7e0db29318f3cb09118eacdee1c69840240d8a52594b7bccd0b129354283e48795590b44d0a0987caa54976e88926bd5800d1b366c5c9d415359dc32094a689635d261f818e9307c9841f77caf5562f09c41a40c8af789b9254ac4850ff7c061c3860819b424f9b999b88c75e2c70a22635053d2b1b92fe5cc41867064f918e161b820220645e6a42e4e949c3d69587b0b2261d064ebbc9719b3fe6512e7f0b123c7221b44c0a0e59f580c2ae87ba592c817b4136e43a8fc4ac40beab69b92245562c8ac1dc408912e28ef1abfa4ca301c44b8a05dd2399e8c28d11ab41fc710225b504f763139c85538ddb17c94d28236e697b4286d725031dd238c1fc98d248864c13f51a344369f902082052da9ac983fc959534e572a82c815b47416540ab2f26be671c70c729c20044610b1826e25b9c711bfef29b68410a982729784b608cb53414b499d943c743261df0a84c814f4ccf2d85e2e16df495677109182e295b9597ef2547c3f0a9a9864386933f42971311414cfbaa727681df43a4686ffdc25e904bd3d67d9044d8f0e9eb5043d35b2668222536cae3ccab2681b97a058da13222fcb4c259b0a224a50bb933621771ff3f44624095a097ecae2e5bcdba20409da88c5c5af125ef425891c41711346948aa134840c1231823e7ea6a12a43bfe90de540a408ea57779c2496799ff644d0458c903fa2a487a076fd9d78d85908ea9f12de20a81d3f89db9c4d2953920041f9fc71caf265c473d00f3449653149dca0880f94cb7faa82c6ceac782ff494bf7b5fcf753f7bbcd0629f2468cacf4a5a5246464880bbd0435c12aec43179e92fbad0d39bd020535d9b52995c28d6994b86fe1617ea770942278b99339df88c5b68d226e1b4492fca3f9966d842b3f22f0b2d622df4785d735bfe9b213c38b28c47d26305366cd04213af4b7b94269985163f552e9d64076161862cd44da3824a9ff4b69e5b8519b1d06eb38df7cdb6553ec1424f2e3a7ee4c9afd0e382ef88f5e90a335ca15ce9cb964cc6d7aea45668a5e95a4fb6850a3358a1872f492f09d9599e3b1466ac427993b594ec3e69c20c55284a0795bb94797d70532a342f255d7c0925c9c89425cc4085163ad678978af313944a98710a4d186bdbbcd211334ca1cc5c296d76b1ade2efe1355000821add6384dd04c804624629741394c8519dc3eb9c1029b41bdd24f39b280d3346a1e906ad11323a6698210addf2c40eeac6467672c13023147a923166bb0cd3f1abe4c20c5068aaf4840cbb4f08667c42d3174b5912764c5737cc6aa080036dc346f200a3cd0c4f6829976eb1badeebf3744253b1c1736e903562c43338a1c66cf71a36df6383199b5093126d23229b478ef07eccd084a23aeef69618c6919583cff1636487598f199950f355fd58e8cc6d9e2f42f0458d15b4610626124f50713f9d97ca30e312bab9f8a7f6f976b31c1c39624aad4e30c3127a028c02a8b44f6542692c108983c150401000902967020314000018141a14064391501487b1b27d1480035024264834321a241e16141a140bc6815020100807c26050200c0a8401a140483c160514c51f2a0b03b21b2907336d4d86b1b3357316ff74bc3a89be68d11babb4fc3139e8b6ed9afb5377d70b290a7d1e78112f0e279636b09344c54a306e772c682751ffae301657f81a6bc2f6c02dad2a7252aa095e98c3de30d44d65925d58dc8144291b9ee629cb93487625f78486ad8e3d968c5e31ceb8dd112247166988216601ab2caee188600e0f956ece9ebc32a66054eb9a2be905dea7190e1b30a907f7713755db4fae420700e5638065575554f83958b3cac301847e96937039688df8d0e8ea6c2601f9da195a50c0d5c9bc39e422460dac55f1ed92207e79da3df21f25489c60cf81a3c4aac0e235bb965a04ca965bdc2833078a6b52ca50dc4c5ca93ab323b68b56732c0909d2846fe41be491912a5a31dd8607241d4eebcac1fae35caf6cfdaa917a83ba46d67036aa7f082f3647e101b3995a35b0d4408e51287333e97c85a87a23e86dfd3c5daee58d18985b6a013094eaee226b1b015c798bd426f1c5802047ff5827a5476ca9f178553fcbaae770fb30a8c1eff39edbe39b4eb3e2e4cd856ef2032dcbce2db8f1c1c751f369a6e66535ad76561a0d8d58388669d5b7de337cd92d3df4a80f4304e0a98dfa5ce08e58ef71d997a3e25eaf86b7ada391e9ba8b5211c3667ca574a8e4511d0238adb3e12c491970e0e32e0c0ef91f81de1d04a9031c79c3016ece8eb604c5141cc101861c2f0721a526d81b5cdc6c31ed2f54f251f27d6008bedee7decb2ef955908ae1cb0422a7465c2fbcb1214452a75a1c46e76d3c894818b12c4940620e50d5cef6710073566e967fedcb580304eb438e61d6c2e5609cc72968da8a0414aa072dd63632d97c25a462f8c4cf0a0cb68d67e0df97981b2208d983a105635546f26b2411aeb96b4387975bb0fa4b0b0ba0925dfc1cb8e4f871c8aa3cba8c78b0bcd906b41ea0b1b09472edd069933016070e3a7cce5835facdef99f242997e75c422344765b2fa015f9807cbac0d14c4e80b456a0bed96057194da3129d4b1d6e81b59f32347933b4cf1fc927098fda6a62ad940db909df320758e2c2cce080f2f434b3ab662a87a53d8e5fc48355967070115952a5391576774bfd5d64219e0dc8071a40abe439731ff72cd60b3710ab439079d8f5bfd57e5f2cd2cbed9036c3112f2c7e004fe2418b55ddb66e8afe54d69d97adcf343f4e61b6ede9e402925f9e1af7e1f5ee5e50c9c1852e653077e4694f73846600fb4f3ffd195fbc486fe395c8631a888c5dc862b449f2762c4aed3e6c496974ce265474f66231cf55780acae835de591e5c9c987d87a12c43703fd71e5ff03ce9c86ffa10cc0870da0184f4f1e93f2a252852d16bcd986eda0cbd6ffc0ac60ec0d32488a01712b2905ac750047e67a486dce893dcc7088d1c77624c955413b009caa123fb2168877d6970298743958d22edfea9626b0884450f260f1440b3d2d076bf91fea0c99d9bb8b53f7360d9ccb1dfc10627452edd4a94fe9840ca0716bc0421973945b8fd3c1a2557413a29db4c68d8963b45a3b251026d98fdec07499f72c4ede494bca1af83d7ab51e2f4301015102c4625390624da23ffa962c9e7d68c268394cc38d88020290487ffea81c3aaab90a2def116c0cfcea0bac18c213a0b1986d56f751750576cb468ea5017896fa9622954e285c476cfe3c3c51cae0c7a0fb6e81c166a061eff73283947493eea8877b3b21540959c9261fcbee5651797a68d78805913787cd19d84adba56a87aa6c9a1af312675670a838ff30e109e109be67db8a8041300b52e995173950849e12d9043d9c180724348da84052c746520dd18e455c6a9a21ca69c3f5393cc45ce943fc99fea851e49748e989c47027260374a81194493c885479a41d4932455279e32bbce50994f219373859fd4f781d36843fe59e7f4b52fef4f9d24d54921fc5e7dc9adb5ad502bc6e0eb284e9d88fe61c04e03901353d87e46bea3f27c71232847efaf58102c357ef42e6c2f907be8706c9ec44e295387f4951624656f773d064103fd9da6240269489245dae4e4f1723c6acca88aa3ac183bbada800a35d037c0d6eb334e2c57bb0855a7c699109495b5eac8b25dbfe98990f89178037f53e5ae8bbc596fa7f083c1a00105f84335264afa8774c3786615e5e1de817c44438e9f5f48e2ba203a4960f9f2e2a22347932c78d1675553b3f22a7d3a50cf7de2b39d1a345c3246c52b31518b12f00435ae20e65f80d13b44dcdb6837c6dcf97615c6106f793f549defaa95ef4f2d58b474b499bdc36e55f06cbb8f3996b0e560fb93bb777adf6ccb3f5af9bc8a2980ebd136064e51433b0af367a6b90ed417f5164c836ffcbf865c4c804e26f6108ce03237a924f9402f78cf8bd60d5e43a32e35e88d3d4263c9b9c36e9be996559a6b5bc7a6abf496280a38804dbf933312b64ad1791889b3396c25e3b81490ccc0550a9c190b5dc4e7eb20ba6764f9fad973d7e91ecc631b41bcb21f85b8cd01ebb60a3e9b285c5883e427da7db511eecf1d8d28466a635e28916614062901385fcb0ae8a9854d91b24d2cc701b86aadb80daef95da16f6abd15809733cee3378082e11d8b222f9351ef98a0ed9c1ed5e3e550c6cc201d8d80c2b6e304ec3a6de951cf812623f59b935160785de776820e35e363a8b22c3b2b6f08a7e6f56e8f2c2230f9b70553723ba693761d03273605da017bf9b41a21e68bf89886504cb77471404d6bfc046e752506fc16c57d0a433612496883504ca5da0c81514a2d6fff03ca0569e92bdca28390710af7e80a7e21602c8002bcf8f2a038511b708f80cf1e6be3f80ded122e082ce4e3ac0874da9e981b0a8eab198ee8938318f3e354006291945009a5e7a0b03a70ceef3ff9cf96aee7b5d937b84ac0e4f63891442ec4e5dae090804cc930a6d65943b72015cbeda2e47f8845c07277d1154dd4db9024fcfed6bb3860061272e21e1b2042745694c6f344d5309f204e006191207bc5693415b9ab550828e43fb586f1d42e0eda887f86eb07db352f56223a0896b2749ab01b5ea6920052f39132ed15ca4cea1a343a1f2170da464437b1b819620cb398589a6c352667954007f135b8a18f8fa1593cd0b2601a3e1a8a5048375a8082eb1d7a93c706e069f25533a1621d46953735cfd8e4f456ddffa0474ea1eb534df3eb1816168b2aaed4f8d625842d40af1e5e17b1a4b43b44e099e2d71502b480727b70c4711dc41aae6a2f143039415868ab8c3d8d7b46f542643b0cb30e4cfb04add480d9454e025e401bdfd26883a78776480d78ee463789022240248b7c84b3d5dff9ddfb939d13833fc8899746145fa8d9d405fe924ebd867ff8ad6cfc6dabd12c9e8606aab6f84348e3d76c8c790b9b004b390fd0483c3bbf1ed8c98058b1a9a05533083e45db01012a135f6cf1cf16d3e1fe92cc4f38b9e05524f4cbb2cb370fe42252f6c00980dae4d8831f0f754e68eb8c27377b00cee4e6683736259a94f2b06fc95761397ed241820995c236f7cba120170aad24e8b3da4958f3ca56d87274f075367221bba823c7cc95f15a672d8049040e1ea4c45b8c6731010b5cf47f0830059862af2f7a93bac90e61a12e0ac42c76bdfe9b79941645d7126a8531e3b96521bce1452a1f409130105072cf7a94f081579154704b5489a0679b382daf6b79630b495a156a82ae61413e22bdf7bc86822b30e1facf24670bb4de644fb48e14d9d8094ed02cf689334d73601ac498aeb4aca97131a7846ecd02b11a0301b0bb2d7556162a2cd728a721dad32f55135df965955f1ca31e99682093cbe56870c66666f3144247571c3e8f8142fed0ef74fc021fe823f88c7c3a29762be820917a41519ae5baa25c21ec369546af336ca86caf177a86c24003b02cb983df6484c6bdc1c32fac341856c1882318ea3d835813e2a47fe4eb93346090536889c11f616682154343420a9180de08f5abe4bb8d1ee23c8f3a358a53f6006655a24acbcbba2fc546a66f0649a8c5a0cb49a33b7504ce804c343a6bf715871b8096144bb7aa3a4328b9424bc0dc869b271defcd0f451d411c50c07ec40fe686761a00b9521e198556f2258a65191590e1fb8be71625330032b56d7815561768c89ad6aae70ad74aadbdc7c3c8251c85abe1970ce8db9eb7e382231e6100f542cf545f49315c6461000df04ad13f8ceada5a60b3e0b0b383ef6d9e38669201d2dd43d8f25f180c7e801327dd3d46b029abb31fe2fe72187740f2689eca214d6849a4721a1fde78c44373124d0bbd7fe0734a734f3ccfc1d68bedd02e943cad8200d12808ddbed06fece959c12ffbd6e54fdf5525d10f1702dccecbf72d8e8b617c4a1b227419b853342cb4384e2d89331de7b3a113c2b82a9fb025f7289cbeced3df00e122073a54bb962eeeb96a335c74e6b6b7f912a390fd6f8c0865b1071eccd433ae65e97e6eebfa75fe93df789cd2e43d9ca38a42dc50a6ee6d963b243d563be9fc931b34db65b845cfc4f6e28147878d41aa8a19420a8fa1f0fe31f93787acdd7d3178e4c224e0fb32d16d2c8e1827a864bc26961482a234b2165a87bf250fb7ae53522c3743436c8981c20a87a8c0ee8e4394e2efc614070417a5860956c64ecf698692f0f26d587768fe6b2cbcddf13b7576b8b7e175e23654a90a2aba0398066d860aeb8d4cd100bdf8efe8b66deec63044db3b73fad48ac600c506a40e3e59bb8c0a39ab92800e6818597086141f19b534190d0d8e48d4a0872fd6ff29b8ad3464038e8d6e1eec39054de17c9a277b046644f46241b4eec20e19e97847b6585b673ac30d71c1f52915adca14f9179c0ec6fb8b91ce717701bf9b20a41bf741e84aa49b78fcffb48734356c29266b80f6b12044c6790456d981712c64ae1db4c8a24e6abeb55b6ccd5010651b4a2bee6b286ceca4820de3397193d31a38a46fc500de80c7a415358e30e2e1e33ea807e41a7a617d95aa194be2668a096348535d8b68a24aaeed299ebe8d6ab50190ed4192060d28a0391e1a318a5be28be4e82a84267f401e582464d43aaf56879341b4d20cd738d5643a0d11c7aa202041e61142c5a8dc6e4e812b334027a8f5aa264d16c3481e646a3d18a065dc28b42381bec1595b5973559ed011aa52def1a5d28030a56994151b3e835ba88fe4597d1fb04a5260466da51b58b2aefd2986be9d62a6bba6bbf6a09b68cbe0346411fd7e9df0571062125a99ef58d55aabe5a947cac81e0097e2cd4ebd01ea38c3bf4da0ca0af06ce497f1707db8916d0b8074545e6a305c19f27b3d20e726e88d58644473427b1a697918704b587091ff7a994081a455909dd246e4b8e971d918e6c8ed81d191d811e613b923b521db939221d64324d2c8390a8255c9e731d09ba46b54ccff29fd28102025cdef1b1c023b2eb9d5b3e3247604ba8831cd384a0b202beb88171ea474ad20319130e9827c3d7a3608835a2da67539b48ff269f018071c1f7d2847187382ea21ed918c39000890025339079a430cb97ab45580c50b5a3ac04eb11cd484c30d63e9c53a008e0e004b50a327b797a78110c95192725d0980325dfca16078412d295adf8737a586f98bf7550d680259a96b10db6083a11647c3a3e7c88a7dd16045802dc0c817fbff28d8213ec03fe079f803b3284e15f0920228254c0f2e099c37be2380714583a85558790c11a32861ecd314883415367431565fcdaa07859f4b84406af7d276fa2efc5539b6f59936164676267dac8e2d80d0fdd4b0ac1903e98d8a69b8d4d3629eb74e8a61a4e96dbcc3656151c35b89945020c3e16bf993beae5e457b241dbc6c7c6b68d1bef66304017b5a5a21b62cfc98553cf26459bae36d56c727933cc82357ec5ae0d41de74939306ce2c35f4abf05163426d86311a28811b6728371bcd390ece2cad9a7f35356b3203d3c611ade3071122985c9127ae0900538819d1906208608b491ec169a610984b8e2b9fe53a551c2b01027d957c543a496904df4b42638faca11de353f304f16f3a17a288bd2c903cfb16fe2243d0346f5a483041f0b593bd60cc27882f6cf6d8cbafb9e634684ad6a72efb69beafb781601c28772c11a7cbfbf194aa3f8950ad731d04b3021ec20284b720b312994f52a1d4f8985674d5b82c6cab4fdc084938d1e642e861418074bf1c5f552fa02a97e42bb0df345b6888225888318b756788d28b1f8b6a4be3ea530c3dc928eb45eac923fd079ea4f56cb424068d3c349a30a5cb3d20f9e158f1c11aede25b180c7e428a6bb58195e94a1fc7ef0492015948ab5d1596bb63076b8ebb0a7d67a9ab35350d850f002552f19bbcf180f11053cbea22158215772abe736529cb7b3ada6f04764ef0b815401168a1466bef78baf7cd7ace53b5982e95faae0a5180ff08003bc880f4610c63ea595497e38a2ce30c68ba7d1c90dccf0e51882b992430e1165604bcacf7793cc191b5cdef43b4e27730a323c106116d4581e7fd292abe7b82623d912a631e60ff5a00ba69017e379f9ab6a4f23b98e92f8b0a89a7c93df06dd606da70349c0bddd32000060897aef56ad683e4e88368d3a8e3acec301b22900a6eec7c984e62d943bfea7bc240d2e82e374a9fd216fc5fcd35846d4e170ac181842208f10ff99b141948219a4b16a20c765890a813ac1f13f5c0a12553ec4374e6d09237c282aec5c8b0626989f3f4d4284666fa3cbb1ac358b230a105fcb5511347fa44ad5d6a0c4158b7d496510535f3c9c20618c729bb1050a0addee42dc74a1c8ce7a995b38f301570583765499aa65912cfeb0ec332028ded76c942901b3542788651b9c3d536a5c148adf45e94e39ed638863ff98a1a19252bd1b5f84c08be303ce0a51af96ba388504c7097f14978e5f010b1481debb23723645b3fe367d8dfd06d5842187b980aacf31abdac5e9caf7b617484138e5eede8620c71c0242f7cf0263c41db3db3f7ece3200cb270814069d036f0d6f91e37cf49cccd63f6f46aef92d195326005597d9d0ba384c10b1e72c547b5d54de510560f0221030fda7441b9adf7980229f9f59ea347b17b1b01f69b34e52dfbf119c682e5bd9a2ebdebd3a5777dbaa8ae267a1b21daf3300b0fd22bebd579895e7c7f972e8b8d6b28deab77cd5bf4d6f7fbdd38620e25f5104dd8038cf14a79755e8ad7cb4bf37a7a51beb28455c264301e78134abf9600ca209b57c50be9de023da2a9e2e90367c3034ecc43f3e0c21518c88681a0c0f7e810bdd8d6418817b57758b537a7d9d8eac51b9d8d28bc3b82fed86bba3947b432fe8d4e7c08b1137548e50edec1fc66abc029bab4eecb9e056ee3f8c33b24c90a782254d3e050cdbcd479170e7d901a93e7164e07970cb325016104261c7d72ec8e134e0c8d7a0b88a3b233deedd18ed13a3a7d458d08633f06ed19fbd7ecd397d9b4ef6bc8fc06822596db0e396c582009a9a33cc19b77e4d0ada023b2602bfa6a12eddc9d837614693a137acebbb1cdf3e1d7cfdbe2d18ea99c644b93f46613aab4dd5e445c34f72620c5a5b97a6c5f3e91f7e910623bd7c69d0bad5509da3b7dcd2fdfd7defa5c8fa940e53e483d0a1707ee21ac053ac32706dfee3fa3d6b9b52430423c0c2e626718aab7db6271cfc7156e24dc3116c01606e27593e49daccfd337470fc7f5f5dbde4a91ab67df6c199661c8632646106c6f093feedb0e6af37bf7ed664004f5346e6ccd8e411cde467317c945e5834aa499ca716b6f9583f26b81f8937be724a2977d4f341e097cb84a54f37dd87cd25d43a3f73b4a544b3c880c8dacf858eca3bc303661f5f881774bfb46185d787c1cf2709e91799b61d6c5b300ee18c0b64a9a6b6192f15a3405017deeb2ba3986f33e02f848b7a5b1a2555dc7e7011ca2aa4aba0970d6139071e0671a8e9e665a43431c345428ad2f6be9248d720fafe77ac636617c1410980664a0181fe9aa0aea86d804a3cb191e3e2b9411ac8f02cfd9e3607333cb48776261f90278bc63513de7926c2e6c1542ba9278ffe52637e78161b219b091146222f28996ac7d40d693ea8d49716de5b444be99b5ac47be0d58d0a7ab5d5c03a8167e401199f5b39214a01fc665d150d5a89bd5bcb63d62b25dfa97492ca7d9447b4c2f09a600352573fae1dbbe7c20c068d0969c76975839e09b0088eb3506bd3713079d1f3238d418040556640ad8109cb4a0e532861f4ea376ad4164d1153ac40e6cb50fd57903e0a1fb0049099bdbcc308f3cdfa4c28a037930627175f643486f472bf70e5fb5c062a2864dd23c058104a50a262a1f4c10f0bc6a1d30d519725e29abe4b15a461c91afc81cb29026d9b03207ce3e12898845aa911224b693faeaabc4ea807c88b8b1215d195123a151424278f543da2051f02bc009a480e21f1bba1780398cddf1363d207af8b398a53845dc098bc25f614df8799827ce0857c282f0475812eec7a16cb114a1d20835c3fd893c3004e197530804869c735561020ee78ad890500ad81e62664638dc634cbaa25bd3daa63e4b2892d802d07fcc6acecfd2d12a69fdde7a75ef78218447a39d9386bb067a11c352389f6224f00e8826490b709777967a0abc9cc19078446423d598e54b0564080b9169a157cc2c025a4f6079b21876ffcad7c9e811c522cb8723209b43fb1dce718882b649398ce1220e1a4b7445e76d4373990a04986e76897bb8fe10d44cd7c7def0e014a20112bd323b0bb5893132180b105d812d8f634a43d068d86e886f78f284b843748f57ff17184852627f5d402adc092ad5f34016904dcf928bb9845f4de6b4c8344912c3c84b18e25d6222d2cba12d302a032c0c4b4f8bfea1461b26b19b503cce78714784c9b036df4a79561c334e10372f08a8e744820e7a21b90351da009eb44a7d95bb47dcaa7ff643da2592fc89445f43f8b5e30501d2d74ee4226d225b2fe799ce13dee84eb810bff4f4d4bf0a8441e694d6278123dc85f1bdb757ad96b4739d91221125090f8dc87ad8b6db19f77bf3787cf695a3a0f5dbe4d384a547d6086b364466c87c24ef118c04a4b475f55305d5f6591d685c167e3cf14e74410668d025da5c3cd1f5a458f4b5ae676fc9d395356fd0400883b4c00e4b8b12612ee18b94ba03f8d37e229384ad250821e93d825d9fd40b904628e5812e8611e83d6065b077abc7246df0f814da40d2e9626e4fdb4692a6babd72906eb5ecdb6b80b8d74392ad90d825bb64a5a8cdfe4ff2e470075dff711c781e05065d1f43aad017fa74974b2a72b0621e52e2d70db693a0cf3cfe59e2f3a6b0379e0dd50c592f1eaa4087c4d29a04d0f2ac20a56ddcee1b694a66580bf246c3b6ea4e3d6546a85c668c3da391ad888d4030f78df098fbfd09d6d76e5c3c4adf646ffe866f1d7bfb1e8a5050a6f8338f6583741b5454388e6f40311001bf510693f294bacf2a58a5188b22ebf827a95d5b2413bf4f320d3f12aacf676d8a3a21c21824d6464860f8120948b7717c49cfd65d3831485d939da9bb9b799d206fdc85ab497590c467ee71ab401fc74154477a0f33e2d9d89a0ef734584b1a04029c476b02e1cf48a881d87837b607b1e81a2e5a50400ef37eeef2df4bdf5fb513b42100687b529948b9cba8676a795acb347a25881913d35bff2505a26ac4b6fdbcc7f48ddff25b83056eb688e197de7d37368f655579074bfe228b98aad3c8087391e0aa0c60eb6892cd5cd7cf95ab90be7a99675e524c3f2d54b5cdb31473374af901c361a5ccc01334b5ffde371079eda096ed64f0b02cbe7afc993613a2a25ad8cbbc6cf06c1985b193f9ce4ff43ad02c4cb54c5aa406798290170e11c12fa81cc844da4e22d45b27e16fa93c41c64ba525e9318d6bda0f2eb679043555479b5553216d0eb40588836f79a04351454db044174d514f2befebc1b2456fed8a413852727d852fe8f1c93223976eded7fd13e34709802c08736d6dae109bbcf789240e6f156873c3212cb9feb1d1ae45f0f131658e607f6dd93d3ecebc08da3d001e20658e3199ffe3eeef560af2f118b50203da4e53208f3962554d0ccb47563e54113e0216097f28f431ee6f7ed49938a952d403671a60d44dd015a162076df240136b22a9fa6fda13fa39440680ca19539bccb8fe64f8ad5a696326047093130c2a0013ea282c1ec457a8b6137afb957785fe4269b950642449ded816325ce8b3d00da1a5fcd06c66e7ff6b9bd00721ca422c1c226b1177223a42a8b4d0dbde28f94393d0dd295482735482e510d22d74c818aa5ec72d470d870859e495a8be502a0d754a54bda3768aca50d4b1a2742acaad8e1401f83a944b79d089381b3a0266ba90fe6ea835454211653314bfa2e89128b74e961e4fb52000fec40379d6803516704dcdb8faf870459a1156fd61d50d8605c94ec018ec4a1e2be885ef55e3298c92e1698f983c2282c2f8090b77dabbd260815c03d92741e61f1996ffaf859c5cb227d0e96014e68cc9dc94bf8dcc3f256d478783d7bffb8178fb73c1dcb046f42cab28264fad098154a90c33f43c54c1961bb6206e7f818650f3ec6bbc778cc6cf964586a45af6de16f5208504c80375c5179ee9d24edfc0c5a8f139a3ebb064e588bfac2e980935b4b92e266ff1a3f44b108eac472bf6aa232c31d4658259e9e098ae22cb3b47708e6c3649b483334bfc00c731f264fb9280b515a1d55759a922e3716560ea4eb315c755d49a122a3199a0d7c457311fb0b02a05d5a07256f03aea6e12d0c4f1a563a5cc213a51f74e32d0fb9d2add8af5d45672c4639284c1c7c3e0eac0740f73948be82091cd8de125f55ceb550069d9f20e1705d70355322c880511c2a8177bd84e6f385fbaf9ab2cd096b3469c91b87180595a0c621b3561613a388cf265c51cb14b10824e6cc1ea331dcf8c07852644d06cea3ed078146229e01a721ecf62f6da953c4d05fee4d76b300bd235439e57041fc4dae95b0c52ce8c52677cd6da8a30fdd302d7ba4ff6b7ead59a05dd1fc52d726cd3701c4f4f395c8771085b8408a64ac526bbb49c71f03fa3a6e2225ac462d58dcda60be753000cd9b2b0949a17af0b50b9dfe083917431477764e18436fe3195a115fa701b50b1884c6e698b9be83469417c17affd3959e0e064367c74fa746b44d335c56cc6952773d4925051b3f90e979361d28eb55438dbc3eceadb60c0e01da1ada008117099a9b81527492554db9c3d1bba00207e94eb1328f59b2d61acd4a2e157f8cb440ae3dbba744cdc38435eae167df2da716308abf45742f809cdedf32d386d0bc9fea5bf723404efa203851768c60766bdd1acf9afbea9805f3182524a84c82af9cfa28032dfd49a5d553da6a75cb5b8dbdb26421877b35b3d75374258ddb1d2411bb113c3dd6e5592d2e40566c35c8aee449e1e1e1648a86d01ba135fcd4362bcfede1387f5f5c40400c76f3fbac0e76004c1dd15913797764d795dfcbe3377c15c8067b78e16625d582c27eb49c5dcb5c1b64699e3ebe49538cbc51751c3091ccdb936c81d94ce666ba609004d62a9a90d246efa0175afae4f6500e8f8801bb0eb738fc09f181235a362c4bca2a2a64f364f3ec486645dd802170b2939613efc1ed21a326a34f83306fd0afde51ea7fb5b769f05d30a0fea525bbd4cab691858caf236924f8c1130b04f7a62218b2edb12f3496fe11bb4355e4adff858a8568e1eb8b645a532d084beac38d253fce7f1547debb5c4b3fe22c2b4614fce0d7de157cf93344ba82d0b3af8c08a05916958b3c5116bf2d47ece637339b35108b50f4e5a8383b20f1e81f00f06f9a56b05be0b1c8eed162ac3d515d24edccedde11f36bf6b3a0b8a631f7259f2eb5fa25fa558d2bf74d9952aae8e8261138d2b6276c22a94f95daf66f9917d1af074940025fd080dc710d94390a5e61811b23f2cc2f903e62673932377eea58baf5434f10949f7f1284865da1cae6e4b91d065e204a0e1b32d60cf54420b650bead31b41b47249cd9fa3b9c5051d6d913bce6ca23c426e9bbf1caea335807ed66f98b8ab889db91733399da871f5dbddcdc5a0162e6d07381e72a08eb505cee45c9697cb002335f5524297f292c6854fe4a64ad1c2c7a27fd4a49130279efec07b34235b922bfd158d435fbe92e6db700fbd359a8154620c66da75148dc17d00f24e282dbd2cfe05a4bddbd2170bb3f4c9d64fb3704f90694577618ede99d55d1cb38f360a6bc3a5d2295049f105a2be2f29786cc4b870f551ac38c9dfead41a6922bd2298632c207d48ac4902aaf8b3a13860b46439330f0f0f0f0f0f0f8f1c1ddd08a98d7c02a932c924c9921e4b2db325c994529229929ece70b1db6e7b98f87466e2d39989182801570a080a280a17f8303474e4809166a26e2953144d29d9c08d2e68a8a02390451635be2a081d38e0e2b7588aa7534c210483c60d5233504001a3808e1ba0bc374a0e2946f3aac8205d74d880b5329dcfd2422899ba7340470d8c1d94a753daf7633ed180cbd11de1173972f067c0f78996c8abb6f823197032c6f2bced343aa463c09f8a9713bc279592c180df9ccf36d70675bce0d815ad72f24f50d976b8802f99225570512bd80c9ed39584104735c30ade2fe6a9fd2b1399e52a98a86d6d61b5c93eaa6043ca2a9a49c454a4226a568d4a4a79083850c1f6a6978b5bffc4ef148c2acd0e4956670a5ef285124169a560d47b98caa7eda96291821d153f988d7dbe9846c1bdad6a5e2a192dc78882b5bbcabd8e9f739da1605762cceda9645249231ca060a36d7a499a193260d4a091bae0c260c0460d1a0bb05183c61618b8097c004600eec0f10936fdf77fd0bce9a17749048727f8f8dde29a8400b8034727b89c16d4c4ada0b2bb3c46707082cda9abf27ecf543c15ec04c726d8aa9821249def1945706882916ab23e4b73cd049f7fbcb2a4c5f4acc9220b0e4c307afd3de531cb2dd3f3392ec1e6c7184ccbb625a64d2dc159eedc9543568db89bb2858c1b5d7880a3125ce8cc7a42588b7fdc500b1c94e0947a9c1c6a524f3312438b548163125ca54922b783d28edb9004eb163bb3edefa88d158e48f0a9f5b63e4c24758cd210c18d2e68c0a8f13752c34083020724f878a5be15477f3fe378049bb9d61fd404339d238ee042a307193a6769897a0cad1b5dd06804fb93640427a6ff5490da496457091c8ba83cdf95adc99db41b4570e9e2394410c5d0328e4470d953fceeba1a1d6318434b91c0810846e46bc81aa24f57d0cf98d185081c8760dc93a8c81579b4c645096ac42041e03004a35450d513c1dc7d5365078e42b0d71eb207b51b4465afe6c04108ae64dccb5b7d1a049fa74a87be5504c16e578974fa59154d1308f6c2f3f37e4d23c9910310ecb725939d6df9037b3a44d39e34d7a75f86c30f6cdfe9cd1aa28e068e3ef09d22f4248d9f4608720e3eb06d3adaa592b68bba468cdb03bf9de9441051a52c1394006d0f38f4c0e56071dc425f478fa12f85230f4cf0a473da1b2162d03c2a78e0bb94da0a7529875111f8d0e2e3021f5a7c183b04c71d38ff18f3e5c8bb61a27124e3de97dc8ec05107768428193c530e071d38213c83c83a5f060c159835c71c185d13dd89571939701ed2f8e65ae7ac93230e65652eebce9b02074e7889c6985a938a5831b462dc9081827c031b5abd4ae888ea2538dcc04591f94e780afaa795bd4a70b481cba05793965c95bb3f071bf837afcd50429d8813df82630d9ce9ac5b5adb520c9d188d34438601800b1c6ae052c8a64b94085a3770a4810b7a54a6ceff914e9d0a00c2c081063e8fae47bcfab51c2d05709c81c9e693f479997cd30b963a7098810d2962869d5ab19c3d1fe02803d79bd38a05e5a71d1978d5094945edd06cb9bf81630cec9a6a939c83921838695bc254ed29256eb181230c5c85baaad20ea5e612303041866eb06a0bc172b4c1f10546994aba3d66bcc0485272ff745c4f9dfa337074812f1d6b3b948edc1282830bbc46494166beb6cb169d2038b6c0654b9a18e358b01399f4038716783f699f357c93f0af141b37686c6103f9c0910576f3e64cafd71659afc38a040716d8d7fbcff5bdae9dd457d8f35269f00e8db102a3f4c9bdfb8db4215e4c1558d72dfd112de94dd24205b693fa50b5496bc8949f029b528a5e5eb514f8ea50952d88769ed24781093945f95824ad9322287031888892495350efe92774193a98040e2770ca835a3da1fb44594a1318252d620a0ff5d7baa98183098c8fc85d00c1c0b10426779f9b6a532b7d1d871218c99a9abd429f042ea9bd9cea2ca68d7f41021f3ba61c8356ce14f4b6058e23b099293fe7efbf0b5293050e2370327a8a9147d2c5d46963058e22f05b23714dc7dd98f12702ab6642ee8f09e521a22130d93ce6bdd15a086cf2fd72ebecaec011042606ffece64135e8242070a6624bb677d30fb8d54ea50f586b8ba555e47fdcba078c08f9dfa45eefa9c703ce6c4b56b2242a16eb1db031cdbd5c2c6796a80e585b0da52b5a1e8f174281a402470e1865a783899c3236050e1cf0bfd9a244cf9c2570dc8091b5f6966310396cc09dfb5bb999f2b353e1a801a324866c9a216920e0a0016bc28350d125399f8763066ce92416294d76d4f148e034b6e800870cb8bba4d72353c8fe8b60860c0e70c480b1ee58a51ff9185ac80103367f7448f6ab2692462cc0f102ce3c77904909379d825025c0e1022e7b65d10e328790658aa1653cf0a2155cd24f4a7f0e1d224c419700062bf8bd90a35dec12321378b10a2ea395ca654a5d446b94a9820f79fe39e6f63cbafb185aa9e035e99a6a8b674617da8117a86023a860a6ef239e82b54d75ed1b5b9474510c2d1b68461709069a41e3c050418d1ba78117a6e034bf830e7943c60bda185afea82ef0a2145c56330f51ef2e2f48c17b58564b927e4f9df15cd080f107068d1b304840a3ec28385d2ae9cd1f4c7454ad17a2e0f363caec5d3a4b726aca5e84822d1dbf5474074191a634b937bf983ec1fd9f7bfe68a3af49e409be84d4f893532df2ef9d6023d5b9e87cadabb1c30936f8d88d4693be92de4d70234bc7e8d17f847257139cc864a9ff9dabc4a399e052f49893420531c14ab2fc99d6c4f7f4e512fcc62821bea6490cadb304779a3c6216e9c1844e4ac38b4a304295be982e4a1691934507b4f8d8c087161f1840c0ff02d0c320f689179438ef7b6825517a4fe8b560f06212dca87e04bf1064f4a08ba105a3b048824d7aa7d378a8e98ad987161f1f1988c087068ccde14524383da64255b07613df8941825fbf0a2aa748316ae93c823ffdd274a63a82bd9c34055d7d1d6a9d4670c2355e0cda67694ae70c2f18c17b5a64f7d4680c55418d1b551f5a68a1821a1938412d82cf2e31e7a07e62aa184570d92d77dc90f375f2cebb8c2d6cf89f00c60d194713c1c934d16b6236a152362182499ec294c823c754820ec1e6cdfb1fb4c9d4c17343f05152d24acb9596fe168297543ad72b8e84e0acf6525ceba0244507c1452aa13daa2b077821082e5f554e7a82a851eb03c15edeff4d6326bd3f258b2c00c1276b8b596a343f349545165b022c3c81d1e1c51ff8f10dd139ba882431c70fbc7b4e27515392d9d5ee036bbaee44b2cdf281913ff9fe94c7ec81bd49fee94a947a60fbfb4496e0ee1d1ae6819f9819fea3bf1778e0ee945e13b523ffea7b71072659b42439a4989eb731e0851d146dd749e5ea59cc8b29195b60e0868c0494c578510746857c36b6fe1943ed1774e0ce53740826f574f2620e5c9956ce21b7c9f01255c0092fe4c0a94b6fcb599b23bb1307f6775cd3d6c53c2aaf2fe0c05ba86c931f4bef4632092fdec0d89ad00a65e757229d1bc8228bcc176ee0924a49a974e9c545a5112fdac08d6a88dc3152a86b644005fea1c5c7ff023e3210810fa388176c60efce32c81695bc41478b176bf053e84692d47af27b2d3eb58d191ac87aa106aeb7b725772e59a7160c1b32ac108d09948ac08b34f03169537d2b4a77a930032fd080d2d41673a9cf258d1767e02dcb83dd051d748766e0e379f88e0c6a4706a132f0aa9adc75e2646062e5e849fae578277d697b0d7b0b0d2e607499e20a5170fa9fe139726ec836cea001c3466fb1058d0a5429ae0805dbd59162fe741af3bf4b842b40c18656980955b24424c9159f60d3d9a7f3b3d6ced35ee1093e0625b3e48c1d561902852b3ac1c4ca2023c69894d0ae73829f5461c22f879790e0159be093a84baa6c52aed004132c3f9e3e15bd086a74c1050c1aa898e08a4c98fdf783a5a6941a7339b80213dc983c0da121b546306368d950c1938006bae2128c720b4968548f2598541a6d6c929b0c3247aea8041f4276eb85549af27994e037ee8598dc4e05b86212ace7f6ead4c963c82349f07e2aa56aaddfe47b24d80e4aeaaddf0912bc05f97b225cb5ab4d8fe053799adc6772041b53cce9463da90ba9d408fe3d05ebb32d9d17498c606257e55b532675b28be07368c8e135123fa68ae0dfc582d29062267d2582f1743177a89986783d22b88d2523e4b38b69933f04fb1273aecc0ca54cf286e0e4afc91cea540856fdca74909bc4439e10bce44bdd35d24e643e086e73745aee08828f64ea52558accd10e04abb1a387582b39f405045f39299612eef9039742df3ec50f5ca508aed9f2f729ad0f6c886b1674121af388203eb09bb729a92e6dcffa1eb80e094147f6f4163d7ae093ce1d4dc5be3cf09f95d3932e1d0f9c7acb8a647f7a21f33b709d93b4b899d2d74adc0e4c90e631e55778b6531df8ac184c9549074665cba5155210dfce1cb8148b5e1aabbf4c39f0137442ec1db597db38b016e4b649ffe0c0c8fbdc79035f32c818e9f3b4a8cb710323422c11b3a4b481c9994743b65ca27236f0551a93e6ff77d3c8590393cd44ac15a564502635f0a576927df654b97f1a981083ad48cb49881c4703935a3c3d861c9243e70c7c474ab51918cd0f1dedc32e035fc97bcdb3fcbec22603933d548912cd92aaef18d81472ae1cc9e4e6cf5931b0567a225ff0b318ec0d031793599dbcb08b965e30f0eae321779c9c64b8fb0524d7a7c9ce7881bd2043f3744c9b57e90217227345996ed0967181efb3ed94bc82b0a0da2d304ad4a7bb9bdb7a9216b8daa49daf5f22d79f056e736a13b699d53c58e03fa9ec371621a778aec0a768aa9c533f8e69adc06d784c15b8ec98d62c260d1538ada46576ba29f01dc44de96e568f9914d8785e9bd4ed73d494890227ba2cdb53fcac3c3150e042f30999d784d6d23f81cb417d3ed18cd7277782e6d7ae368137a979934fba9ce32a13b83c3375099cd059ee2992f4c55502ff9eeed2d7abaadb24705b5a16722599b36a90c07769e48c5fe611783bcd7efa4383cc5c1a81b78b5c71641ab178c922f09354104d2a7b3c694922f0315a929d42ca10d80b25a647f57be92b85c04dd4ce6ea173570ac920b063675b79132b651f089cfc4ac97523498a3f60bda4a94dba2ab11c1f702b29c6c8abb9ae3a3d607465b5dd85c803563be490d9941c1109ee80d56ce7bbf9a403f62568d2185f4f257d0e583da162568a1fd47a1cf019736392ddb1c6736ec07daadc9f57214751db80cbb7ba11ad32d7c6d4808919c1b72a86067cf6e7fea4435cc9e20c38ed39a2a2464f4b940123938ea93a2f065c106e395b7d4ee3c2804b0b26376becae7801efdf566b694ba86a77850bb8949299885a933edab582f5dc0f4134a495ce8e1524ff1f95dead5b05db21778c12723295d4a982ebaa0d9a6f2d3c28a58293982b9bc4ce21c4132af88e1f256f6dd87ed229b8ca5641a525eb137a5330495bcaf6d43b32eaa5604ba7ae936ea7846749c1897e5bef4a12bda3a3e0b4938e41435544c1d9c59226e4a8c9d92aa1605483eadecc75112705147c59d0d7c97d82af20920e3f7929a93dc18b5e6477b7745af54e709deda292aa34125c4eb09d217c2b2da49d36c1259926961764aadf6882bbcdb425f96299605452dad339c730c1c4929eff64b25392b24b705a4c8876893c39059525f00ad78ea5ef4ab0fa9bc74c3c457b0c2598e0a24b548c981dd4493022a74f4a8dbfc71325c155c57affefbcb9ba48b09fc4ba7df27548554182fb8829b3ba72e7f9084e254f929f3ba6912547703769e37bdaeb8adf084e86cebb5c91a47b9a118c27cba0d29dd2695416c1a9d3d2e5a7228ae04d5b68d9e6fbba3c13c19788ef113d29bf1a5d2944b0a516479f521942ce762955a7e310a89c3f6d26ad37c18371c37f8b2fa6353a0cc1c8989bfc2b8a285d5b472188d51553ae98992d5a34af83108c14bb203762121d34a804fd356e60a0c6ff16ed1d8360cdccd5cab2998c95a41d82e03475f88dd5e7dfb004823d4d32e68a9a4ee507299047e80004a7f536c1239866670d091d7fe02769d28a9fb6634fcc0fdcc98a51db93e49844ca09ac0fac660dd53b1183bb09f9c06ee648692f84001d7be02a646cfdd1594a8dbe081d7ae037246fbaf76ecd9d5542471e18cd49be564ad7930836a1030f7c57ec8b216dbf52ba63e8b803b7f7e19ea38f9e6ec60e5cc6f16c5522e6a99246e8a8032792a8d251fda703177eb5495f4aca1ed473d037b5e7484fb1430e9ce7ac26a49655b84688d01107ee73c6ca2dd5bf1872e0c0c7586a495bd03a15d42e4cf00af0df401659bc8109f2534e92d3c4edeb7003dfeb39344fbd8898343506b2c80246093c05a5b2d0d1067e52e70675f69dabd10e36b05d2997b04cffded56dc71ab8ba2ef37c4993e518a21d6a60ddb2e58a9535a550ab230d8cc9ce1623648d79226da3cdd9053ad0c0887493a405bf4b4f39039fae527e09c99dba7733303a66ae7ca2338578fa04ce7494814b615a991e5bf25afa67a080060c1a37b008023ac8c0a9202db774794dca76dd1838f5710bda84b288a6150357c2a3897888c433191a37b850410dcf8e30581639820e3070d9adbc349fdcdf3f7d814deeb132774c27e9e35ee054d5a9b5f7a8219ada05464834b720ac639650ce9001e3468db761a353021d5c60b7af6b8324e9a34afb3568dcb06166746cc1d21a59594d08e96f0103358d2df423030c405bbc8cd4a105de6ef472c77893535a6368d1b821e3c0e0c206b6a0230be898d2a69ed6bf788da0b1d0e694b33d23336494e00add7699a68a6c21a36f5841bdf60da9efe26dea3baa6059165193b119339a0b1b860aa5ac912db43f8724e3a2048f0a283643469780064a10744c81bd3cf7d551efe3698471c3460d1909061736c8b1b1c5c9f0e7c2c695144a3d3652bf17c18caeb1828e2870e61252e54f2f14f8d3ff79747aaae45c660a3a9ec0c50e1e439dc8e9b4b74ee0b2c474a5496a3a9ac0291d247ac8a629d9c9fd025964e1353a98c0b68daeed3896534236860d195b9c2e96c02511bb10b2ab8848994445871258959822774aaae367d1231927602d2af0a1c5c7054af0376ca8000338810fc01080293a92c0da26e9292108099c92994734e7e8c9d51e8153114cab44a932f0a1c587063e2a10810e2370228db4ac103adfbd45e0f3455eede744e0f2928ee9cce347909b8e21b07f42e8a0efc6b4b60e215c1616840e2074fce054229b3c4b9db51e1b30b8b071dee183ca420f1d3db00e1e70d1ffaa72e5654b5ec70ef8be4951b3c4a4189a75b00b52a6d0a103745ae94d10030b08089941e44c21b85eb151594aae69c71082f5f3772bfd6339773a0826df5def9afd498a141e602108ce724d909793fa839653c022107c09655563dfadf92f2058d341db7fd0a163f2fb0f6c86a04fa8f02b9de5f98131939ca7c2b38230953e706d39d9b727496397e703bf9bd934c6b49493577be037fb4dd00efa19935e0f7cc8dd3af194fe7ac9f2c0ff4608baea43a37a89072ec748d22477cebcdfefc0283da12327a92c2aff76e0b3a7d98ffa5607d63ac9764f4176955de8c0678e2357d25fa648e21c580922a6102986c881eb984c569e8a395fb271e0277a3ea1467b10b11f0efce7a6b03135327a0a7a03ab957547fba75c5a5b37701bff2b25db531bb8ca6e5ba9e31f399b6ce026c620b455af6be0dca36b16a1d496696a7280851ad86fb5cb4cd31d9ea6810ba2e5754ac354751c0ddce6e9de3c31ff46edcec057c5b81938a5f58204193b62faa50c7c7e0d16344c53de7892810ffdcc154cf787f2c7c0a549d9fae6a546db2906762c4710416990a53d250c8c8d8e683a49f2df8d8281d1b912bfb4bd5eb9e40b9cada47d0b9e7981578ff93a396834937617f8d2fd1972375f26ad7081d1ad52e963ce6d01911973d56dd6b4c0589910faef192a31cf02ab9a8248d1b3b0c06aca513fc7ac89972357e0438520667bba2e4f6805fe3ab3084b52444b24abc09e77ff0919a454e05237d3d5cdf45afedc80c5147891ef21a4cedf5f7d41010b2970d9f3c9abbccc12da5b2ca2c0664d2b1b3f3d144ca521a5570b583c81496ff79e5ff357c57702afd1d6cb7bac2b60d1043e83bc98844ab6ae923281cd31636a4a7ab48b0a9760b29831585aca992955724f356925da41870558288153f913438f4e99619104d6ac9212c964ceeb8d085820816f3b114f31e890db2a45c0e2089cd614f7fd63c4cd31c908ec8f5ecaf51dfd6ed92270b9275d2c2f75501625029772e8750d323904ee4c57ff5a704fcb50084c74915ab9f9633ecf30086c48ae8c19520ce941580081499aa65468baca77610219333410e3860c0e7441a34601018b1ff09f4b259768d36f16f201ef7d69e2bd254fa5dc1e709d29e6d51c736d0c010b1ef0766a379eb2b403366b68d60cbe7f3af63a60bbeaab1c70218368ad166d49671c077c0e22ec93c9d40d7877ddd441c41855c4b60197ff2caa4a36cb2a3135e032db778a5731550a190df86c222283fa6c065c3c59b974694c6b6ec9807b1349e9b064c580cfe6a5c337b7e7aa30e0369d0cfa5308b194456080c50bb8d19f374dccb921ee63e1024e851cd32d9a23e718f36845ea57fedf780658b981072b3859adc12f3776d0cd7bac8211f5ba1e4baa7877ae8a5473788bed910a26c91494dd77e5810a544cc9f2ee15434b055cd0f09de1718a75842e75167259ce14edfee9312584849482cf8d9593ccd476b2871448fc5297acbfb2c551f0e9d7418bab77f08fed073c44c189bc976f7edbd16df30805ef5954f6156dcf9a030aae459fdb8518327daa57011e9f502b9648c8c172f0fdadac2710315f5afbd223bfd289fe56bf37c595061e9c285b099dd6d26a46b0041e9bf0d0047a6482cd29a851d142dc132ee0021f66000f4cf0a3552a098929e814ff127c4ffe64379edc94cc2dc17a122a6b5e51594d5e8963faba0cb1ee43092e8f6aeaaa20e396951e93e044a7b47c7b4188e8100a9c068c2db6904102f5808724d84ba22a7869c40e6524780bda216c5377fb4a23860d2e7a868d19324aa066c303124c0cc24d6e88cef6e6c5d05a8f47304a5f503a9fc960fadd185a30b46448e01cc16afe10ff1c4f0cad3f41db281af06804b76f2a552dcccc3788114c764927496ee69e1c5b0497ad777784167bcbf901c243115b8fa6fe6bc77c6851fce091084ed55a2a3d2948070f44b0bb3a7e1e2a72886b37622e0311f8d8337818827b53e369b1a25a083e664d32e6903323011f19884021047b3b62164c4a8ab92f41780c82d3b5b765c96214b315e9e021882e3c02c1a57fa46bd5fc1fa365e4e001083e95df25efeedf08528f3ff0b124a87aff3d117537c2c30f8cae131509d9d6c2cf220b8f3eb016a947877e6f03c6165bb40ac1830f7c458bed92fd74f218327b60d3e51482b688e9da84dae0a10736e50e22e790224b25cb1b3cf2c086286ad4861a6d89371ed8ae2aa544648c0d1e77e05f734c3d326976604c24e74c2fdd6b9bc43178d4814d0d2a58b54b1a9592c4e0410746fe9d0ee1f5234f486478cc81bfccd0d2194fccf427075e63dccecadfca0b82d1970a8f38f0a1995e5bd22d3e86d5200500a7f080035b19da2799509d949d6fe0d463a41054f333438e1bd80b2294346db5f95d03058f36301a3177be2c965254fb050f36b041729c98b2e6a0237d960b1e6b609412b947e7566a60d2e8d22b31e3af28791a38ab71d11b42ed050f343021e90ade2984604a68cec077bac82b315132f82a630b1e66e04ac57cf5e22996eea4f62803afa2e79f43349efc4c073cc8c0e94839a65a2c51175b1510a381c71858db893a71abd42ad819788881fff829c46a2c58e7340c5cb2cee295d524a7918381cf9ce0a2169e2ff0d979f9659af4029bba27d4259b5d6092b20d15fbf2c532e9001e5ce053f6db0cba390615bb2db0214f4c66114134a38b05649145c1284d7868810f22638c589ddf2f52b2c008dbf23f594ac6a08405f66d3f6d2c4d497550795c81dddc217fe7a474e8146405f5e8ee641ecbb792f4a00293b35987b0507f8d4504784c8151adacab0f9922fc6582871438d7abf6da0bf1b48aa2c0656d49771b7d99b74982071448fa3f9308315bb4f82014f8d0e2834ce0438b0f22810f2d3e48043eb4f82010f8d0e2833ce0a30211f84038783c81b74cb52649250b13490f3c9cc0dde90b2248fbbdbb206d0257428fbcd3715f4bed2cb240820713f84d3b4ae764bec712f81b13f184b66ac8537b28818d90e4785dacacfaf148021752f5e4d1be1d12989c2625966d527d263a29781c813b915d52ddd26e92ae11b83d69eef5dd16810b2aab7bae591e4458a3ed66cf69538f2130425e4a96827e45fc140f2170fe49d389085a6b32c42308fc6435e191e2e8999918f000025b5e963534a495547d0cad46335e040d838b2eee041e3f609259d613f2be734e4231b44860a36f901a1e3e6052927ea9a162ddc7b4075c27199205b770091e3c604cc8984dd74495e0b1037e3dbb7fb0ae7895643ae0346445885aaab26a6d3c7280fa87c89ff63ec60a1e38e0c44de7516dff98d972036ec54ca6a436670376847f753e93b7f7162c78d4800f965da2868896ee9b068c5453515284ca63065c8f9815020f19b0eba3b3d58807b7ba39e011034e87c79cf2e8580c2d187f83c68c6231f080019b34976b16a5c70b38113b2449b5a33b3f1943ebc6dbd81b1e2e6093fb2469f5bb69c115ade0b353c5646a7249af6005a32949c5e0914cf85d31b468a02a1b57ac824d972f42fa1f8f952a31b468a0e5e20a55707e95238d8a575992c4d0423066c810c19fbb2215dcda899979ede48a0aae40059f4306a13e7b506a3fe714ac5a89ce8ddda0710215d4b8a15798821141927749cb943ea5af28051f4787b6b7cc49c1d56fce12156c1825a8d15bf40aae18059b0d79e073e76b51bf232a95f0c006993a92a7a40bd2bf03eba1af6208db0edce5c574f1d25d07ce2ce9ef1dff1024b87460e37664f30efa83773b07bef259bee5bca15b5b397067a16f44c4b32cd5c6810d13d56b8d1cabad85039b52d2b3afd3d1b4316fe0c47e7783bc5e3bd5b8814be9f91144c6d44949dbc02955e5a174bfaa69940d6c5ff018cd43d093e95c03a742dc738a199e8226d5c055864cb2eb539b8f99062e6ed09e22a79cb4298906f64e2b338a8a67e0cbf454a46bdaf210cdc048ba6c9d39f3457fcac0fe77a51b2b61a25932304944feb4e8e9413537063e8af868268ff99b3931706de56dd1be0b03934a7f9a976a056d3930b06d31afb46ba8b8e5bec088a023e7d039b5798e17188f9f763f49d5e0ab7581fb53d984ca271718fd9e2a4cb7afe95be04fe6756eada0a3a44c0bec07d179962c45104f16588d316dbe1aedadb11b5d94000bdc55d05bf27ffffdfe2b3076d9b28d0995dd64c80a2418401518196429aba0f399be3815380fa97256acd45cb19e0263a9b347fb4a6308610052e0d247d446518b99a451e0d276c54f6f295a47870277e9b33e94081694a89fc0a964a372923e3142cc3a814bb66ff9552a791ab309bcfbb59acedcef51c304b6328a9ed610f9b404dfc612d8149194c9ac2b814931fb7fc4ed24a8edd984a61b91c0b7a7a495b4e83c41d7476083cea6d3b4e99f7dd6085c9e20da23a77b3591290297736fac98fa2a8d9212814df13ef628a58352691b02f7b14595ceab58a511022347db4f866ce6494160938838329708da64b2bae002088c8768d2bb797ff5f23f6093deb3d01ef29936910f18a1546ec4ad3c3a47de032e770ef6956b2a09f5f280b3dc14ca4305214adc1d70ca628e17216e74c08552394ae9cd39684de6804d29d45a893ce28011bf1095c27303367b907d4a9e0edbb3d8806d0dfba0ad6b74da3560dc7a2b2506eb0703a001ff1352e575d3a0d7f60cb8504927f9514d984618f05d6cd127c01d800cf8f6acc172aa517ab431e0b46753bd2e21f2ce84015762424a2e59d9c2c62d603cfa2eac1a30801730512c85770a52826f5c04351010e3860c0eec0706e002bef3adc47c71f2d10a266fe614b407510cb5ea02c60c1aa564ec083e58c14a9069e365cc91c9f2555416e943157518f84805139404d1498fdcd229523e50c1fac7d19899f2970499b2027c9c82f76426a25b4952cc66df6805d028a6c0d4894a173fc330c2f0510a4e0711f4df69ac48e183149c505a47638c96b393e8071fa3607fec5d3f74e2e48f8b820929b3291dcda6e3e942c18812ea5577d55e3a0814ac9b7a6a095993bd7f3ec195768922749a56d0b82718952cbe28cf124385d9095e4d37e207fd6cadb11d7c7082db7135d5d9442e31d5c726f80f919cf746f7c1872658d3f608b1bbb353ce67821092c8bb172bc5061f98e04446f40a52fb2a89d02578cd1ff474ad64094eed9b99eefdbd5eb7127c08da3ea55de91ca2e683124cb44e275a2dc6838f4970f26f928824d53dd49404232d8f5aafbb23c1ad7d0ab1ee36e60bb5103e20c1a90fc256bba4650d2139f87804a74e997a1e193926e539828b62f655ae57426fdf087e4524041d254670265c943e4b9d4570bd591f6fc7a208fead73ec34759b6c2d119c049d43d2eb8d082e54c7e837eea1b7ec43705a342695ebd26bb49221b89422c73f4f2a87e4ab106ca69ab08fc1738d6409c14fcefc1cbaa345d1f72078f1985bef7346106caccad79c64a7533a24105cc8197940f0ea15a286d8fb1fb82be9a729fc22a8e4fdc0d8a492e895b6496ed60776fc4f09d951628f88f381fff11033d3b949f4dc031f399ea63655e9b9463db0f1b22d6249ce11f39507cedd3e63698dfc963f1e78d53c2ae418730726c8bfb64fb264072e2ff7c53c5d06f99d3a707b497b97a8470726474f11b93be90e1634072e5a485be54175a44b0e6c7489f959cd1207464773ade9e8c181b1916a9ea37d033f59946dcef92a7e8e1b38cf18335f636766e4b481ab202d458dbab9d5216ce045861835bf275d56fb1af86c31a769b5a01a78f7b498be924c4969350dac7d94f4225a77c9333430a6f49598d2330fc13370b24d84a8f135035f3282d68e25722f5a65e0bd35e9ecf14b7fe84a062e64c6aed17b595fdf18189923b6e40d1203675fc2644ce139f461e0644e39a91271639dbd60e04229a52ed742bec06fe61c82fe94d62152bcc07e9c20316fea3c2ae5bac007116c33e89c74cc762eb0a9459dda4f132184942db02164e98598a916389dfe7792560c49044916f8641a3725ff94d2670d16d8f461b2b4c4d1619db9021766aa64b434ad595356e06b4c349569ad0a7c069534f6a6a5d15da102dfed962c7bbca7c0a5ebcd4da9475aa490149854425aae52f9826e370a6ce82e6191440a14b8dad2117472b2cc9b3d81c95eb5a6a7544fbecc09dcedc60b397375bb3d4de0be3a8b54bf99c077e4245334dd2cc97c094c48695d35a994a03b51025b3a064d22a734e2499204aeee3e9ae605d30e151278b38a9a297fc811d86c49930c3aa71cf48f8cc0e8e991e3f9626a6aa5089c0aa63939e72e917c1381911163f7e660c2c7ab21b02a2289bc2f3361a710388d185a82106910d8ca48a7828c1cc2f207089c88e74e0aaaf699dc1f30528369ce67a9a3f7e7032ea6124aef88b29c35a407ec9b10adf5764bd2823c602da4f13449a6ec8057fd0edf538bff1dd1017ba956ff64e9e449c2470eb8fbbbda943d2f287b71c02465712c5a9e99f8770356e4475691fca7c57d1b3029fa6b05cd7aa577d78031f5d1ef4d9f96c7140df8dddd12daa4a5a887cc8089b1e28e74d71c554d19f031758359e527066c9de80a06dc8a48d041f9a70c217c01273f7f76b1353f5cc0e6b825738654bda7b782ebcfdef696f5528a145630b69293298bd62a1c2b6d5a446dbba2a96094ecf7ca3d13156c0a7549c4763b05bf7549f76db5a660df7593870eba52b0a73a650a8fd9a1244a0a4e44d717211a44c5108e82b314754be95acc9d2da26057636b49dcfba42e1a0a4eaf266dce2e41c1644dd64987c84906937f82c939481029d9dd3b7f9ee03fb2a7d0a72753077d2798205bb332ffa720630a2738d1ebb89bf29a9278d9043f499ad03129fdbd26d104a7dc5643b3ffd8e99e092ec48af95444523d398f0926a7d1d2796385975a976094dcac7b135f4b309a214f361dda64969c4ab0b153e6355922079d1f25f8516e6a41de65a89c3c031693604496972691dd92ce6212b09004e39df43da4ba9160d4b4d2266d1016b46627304830a2566b2a65fa083ebae8ac4fef9872a53882c9bd7b3a3fe5b74ea6ed01168d602ced4a2e7d59a2df89118cbc189645f069c992d9249322f888e6c1d32389b6f789e03d7e5af1151d118cade82bd91937c71ca3032c0ec1870ed31bcb93d2d1d48660f73529bd078b59a8746f650259341288040251381088e162de2300031408001034288e4622c1601e69baf401148003512e1e3a2e2a141c221614161a148983e16018180c86c1a04018180a0703c100421e44399c1ecf3621fc13368bb43dc1793085894982986a70654067d06650cea0cfa0da206a506e90df20dba0c120c5a0863c981c2b691ad606df1ce099d0809062504334d8177b704c3b836f06980e1a11aadcc19884946d6f1d614d8133076594b099bd88c9b1323d63f7555b15d2800e423bf476563f2859891c3ddeacc1fc30d8bc0a504638208ba2fa7748601cc025c015c09380b3027ff78da2310714e50e4c0160c82694e5c42aeb259d0236e423d941001c401d401d403340e901fd0b54c5b0437737c0690600640002002c00600dd0c293ab513e02f0de00cc29520997b10184c326db53ab40ac005f0f60aa140307735f66cac75da310a07adfd85652bbb31e09124a594099c2384cd9801f7a8b048e3b7eb92fd889a6355a1b593928302fe8b983b113363fae1da64f4db427f1a6dd835458f880cdef902c4d9b5e415673361a887305290c1a94da7c0b14b46f6dcf1bf5d7330f1076c8b0646ff8b1801c68d10a5ba43048b39da9da42bbf6e890f567b4fd539d02ee507fbf2c83ce9185583072a16426b80802833941ba8fc67bf7a2f568d019eda3772a8f4ebf57125648eeb22a351747bfecb3ad58da24850709176281e0a873161770c2fa6b08415dd32926b91b7a219e2d995c4173e450c80257c1a2de2f537cf5166323ed1ab3060f16b7790d949ea5a1b6a9b5394db763335d74142bb413304c40245ebe683fb298cf5faf90ea6ab216c17857f66600669b1743c7e2aa7fb1c7e8ad0f3043e6e4600363158aff284e29e0d95885a2b5c8f05b0f28b6150bd09d47d7d7c7801d2ef3e2a7835a9780c6b7c3aaeadce158f5fb8011791151e1e753f39e32ad6123466247541c44a6528dec5f4e1dd55643a5d26fa238a3e0123d1378904c073106eb25326731055f4b7e247b6e38a455729beb20a1686b5075c999d07cfcec75bb8b30a61bd32458bb59a3a4ec541fa0f03f0a0822511ca5f2511cb4cb7c46ac48914d9430a902aa1c849a625284845982223f052cdf40cd439540850b545fab3bcd61110a1ea180ef44284579fb437bd47a184e62a71b4a18d427d415d415d42bd451a86b8b02a2c1a8908403aa2f1d6743d4826a6fa274a8e4ad890fd57194334f017b4674c4071430f8d5f5d61e8a769221e4511fd529350df500759a46dd9b1f4706e081fa037575d45598ba371457534d43d543494339c0a29abbf5d2d651ea3fd53551c0b2972edb10a026a0a0a112a03ae628da52f33eac81ea85b280aa868a41541f029c6299076a0187d2018744497da178416565d4bd6f3df4bd0a3505050785e251b409ee8033a55641f141d540950a541cbe953d94289413d43aa81fa82b51a7ec541cd98bc19838141b546aa82e543d54f4a3fab0a0624531a839505473ad9353544e4115ea0c6a1bd41ed41bd431d49fa38017121c701e8deacb78f970f2df14b301e54228e013f304a6734290aa4ba380e4105c894a1410af53be5995026d8102c6ce7f5c17b20802454301a1179d23e943019b9315d786a180e4b4f102d3a368c3b206ec0b8a3f940d5415542404aa6c85d6b32ea84528b050b550b550b1504150bd50b550d183d2d1bb67903002e508f50a14f00f4f5a2ea4c8eaa43e5dd3aa06b3dec11e269bf23c7bdf7f40183c34c630feaef1c0a724bfb12354a0e9fe1fb3f29628ec9bb754af1ceef1b02a47668ca534e370a78f886b610f65000c7704d3826ecf14172aa857c8bba7e076ef6c59d9296cfa0ed97befe9d67646c33db5e4b1b3296cc32e56883afeb013f98a145940e48624b6b249d119e5aff5f39528acd9517c24aec86ee12bd2018e4e46a7f1705947251e9acb299446846e5e7e5e745e745ecc5e845ef45ef45ed05e08f0ebc99cf85ffcbd78bdb4efd7f56f9ecfa37b4178c578257925e2afc5c7a924b6e9a5cebed0521d6bc6972582b3effe8c32ab1ac211c477f5ab0ca58a246da2bcb2e548fe9332ec54558a86160cb057c65f31608d5e895e3dbd6ab9d7936cf80bbd7a517989f2eba8f33143ffbf1621cc030d5caf35fb3511da187c30f362f02ae295caafe37652f297e8756cb2ff38c29c8abeb26fa0a096bfb810ba02919eb461e6639a95c2875d3fc6acf7122c1bda691862a591b05db357fa66d6d16117dace9a1236aeb3338223513ca8c2ef889320fb8681d180e1e5c35fae0c01d99ccb581d545ab7afd2d281f0e5b8d682b1c6379149fc701614157e1751158ab67c5edbd4e7ed9e782b7e4499a2657348c211c510f5103b9f38b8a5e8c4ea5273f9f725992346d7e63feea1fb81e99f2cbac0ba8202a304ae5be0c5fbd02ce420009d40e69c56138265692f72978717ba72a743389246019663f117cb39f9fc8743176f0387c1d5b25ce26903aa4f676cab19bd526aa8ce400aebcc18904b84bd39880c52953a44004b04ce5909f480d6d5840a17acf033a254c0e58d0a6bc6977d15faef13986630b26fc9986a108d99f16104ecf5b3f35dcb4d44fa1b377c498ca0443445b18a51bbf880fbfad91b8b3fdef860b0afbe061c173399c00ae791c9008a039f5094fe1207a6c08f36bfd9308b8ea376a1ad16768e2c7f0155b6c897a220741a13a496ba139edeeda09130f8da532091ed6d3806e3c2a0623f800677adf312143ac81e0762d3cc2864befba4b14629011335d81fead98c69a744547948f10d977500f515fd3d08badc967aaf793276e10dc199f78ddbc1d811af15c164dad86a708fd4ee7e42d6af1bb82e5d5cb724003113fd360aad505a9eb86c0ce861f94c113d87aa5a148569c455a53f252b7d35404a0ca69af4ac6e5a31c616eb730582b18be988440ce1501e795c0d0a16020f77517e6f84745c09d9816b3b6d63ac4e5050f0f0bc077188ecded05fc790e7926382ca3af3b2ddac2f52c02b92f82124b39956203e88333eda90f3e907137d00bcad1f943f48cf21b14ae75bc1bb9a78e39e9b34e51c866c6df82ee33a68069f1404cf46d41c22cb507b7fc46542799ae7e1bdf453f8197f81031a69725f06688384434e656d70817f1d6fc7eac7c619fb43421384f27278b3b8204eef7295968e0939fbe58711456eb9038b27d3719afd864427cd6c2b7766d9944e4736339f2db39c7e39a51946c04aa995430bbe0e74380b189881029652e2ac5d66a81e508afed713a76d6e9baa2ca14eedcd17481e1769cd23a146e4958ea06e6ab4aab00e90d31c47dda97b135a14fd67e993636af726f47e1967f828c7e265d9f98b7e5e1d374de306bc8c245504de9fbe8924ce83373ca3c1653e6fe8f0ee2e2a54780ec7d992b0d43605fa8c39d1c5f15414776263a3040636919635fbadf36ebde856d8c2c0e02cebc9ad68019631e9cc6894ea7c4da0c9245acce6012841c9d004947a389d7240177597499d9e6e1700a19a381edf59556101e5b8597d981369af489487d700fa9f9e2da10ab5f44efc86d16a34853f2fbaa7cabbaa3f084552a46d349af8abcc804260f6ce43ba17cbec89f3d528c4ed9e17ec57375c8ee42e72a4184a9bfbbb48e76bcb65916ccbc6838d8cde355e0e8be1459058195976d4e400f695c561c271b72fafdfb76e065423e492688b78b5a2d05671674700b7c8096dbc0e88d2ae4db209ffeb99eb4b9e41a1d71f4cfce865e0297ef39c2dddca0ea9f050879cab4af6212ff2c3bbd751b6e4549d959f2694ee4f252db9192ea0f13cafbddf1b0999103eb1e7d12401c480defce89cc7edb1ac00ffc1d11d55e04ac55594e7d7a5064c805e5a65337dbd495bba123c56d06ae304afdf320106ed5e21b6e8a23ed1eab0388a172afcc1dfbdf3a0aaabf17eb4fe0c7649210398b3947c7df6bce0be3a19affeabb5706282367b163403b1a6a58dbfa60447b300bb718635db3c6990e03db0ff36107f0d42b9fca17b40e499c8659f9b25238f187db8c41760684af5a5383412757422cadab5da0f59c80e9b8a1e03b6a822f4d179c0a6cf1e0204d63dadbd94628ec5688c7f1564ea7b9e8047122784151f7081328082804fc7d1a8b20887999f3cae091c54ac35b609d1afbd728cd256b56d5e6ed3efea8a66d66d8a3faca705da88435c1895ab0f5cd4e57c4160470f51b36a68647004de59d20abfe9685cc5ff3d0344964c6d1ddd9c01c404603ad2eb909d485a115dc1ae7c292b15455feb70af5ce3a6b469407efa09e1e52d591a3ec37d7de609ea64ad73f1d40f1f028531ad123c115fdc20b2ba5eb7ac14c16e36c8168e6946232139ce7b4406fa0a3c8542444b98499bc1fbfbacc8b45cc102935473ec3afd721d1ec74ed393453e0e984786eeefb6ad288e622d0d668f44b2d92f309f2c49ca5c22d7a7891ae448ed102b7b5e20ad5448eb65371f0e2b914cf9543e1c76bac6ab484d5f4a2868e87ecc97d5f102e3360659a935f1e040d82c33ab2aea48019506aa3d6ef953c5add18476f563d60072967426d756f1b4f1803e263e2d072377988fbc2dba89d3f58689b30146a084dab04eb41e98f3b4bcb2d22abd314cf41ffc169ecd2fb9b7c86a188bdaaa11df6694e594f6cb3ea8365eb28d31c95517f9518343badb38d06b050c1b9e007ee837fb1e000eb8e303780f161f5237312af388b2969fd2db42916e6744fa8104f54fb0d1146346cab827c8d6d7628492fa79101ede960211bf72390958809e8139d7bd4386a606a7e69059a9ab232f52e6c253eee181c6425dabfea8d79a75927d826b2f06ef10fd4628e809ad3914568d35629c7c656e2013fb06ca91497c458ec120d9585cdbf18035120101d761c0a038005d5cc1666ad98f1745400a4175d048838ede6c2fe7df6945d6456839dac46e6d9714cd5a8ba729f9e9f2272e1cbf0412a3782ce9a2bd965dcaa68f72e42cad2e9eec0040d6f9580148a14746a32946695d9bd14ff6910d81901b2aac38dee6c2b51a7ed474f0d020aa075c30920d5c1837693f27be836992b929b9f7ea21beccdf7eadae08251ade11a35f965fc8c17a7ab2007bbd46aac9fc672f299523e33bb845c678e317b93f4d2d868e44afa9663965eddc13e0ee06105f5b22439f82f998e31cf529588ec8749d98332133f90abdea3da267fb3008ba2802e90af13284385d9bff0cd166f5dc77a5e5fa4d7b6a0c84eee8780f3cb37e5c3f1ed022ad0039947e2aba2c9a87153efcfbbd87497ff1ddb1e06dbf5ca243c5545aac95b6098637246cd193a408eba48f021eb528a2cc895dfd53747d592dfdf5d0a8488f62a48c5827a3227e099d737bdf8d0adec1a2b7f446edd8b8bd99ce0bd966ffd8dced97ae45ceb4707c560f43dd2cbd6a890aefeaa4df71a01bff8897d432d45049448e51cc613a4aea029f28a01866016f9f747728acc2ea797688c747e2657d774cc819780f916b7d01db878d0f24d712b5f967397e5aafa90fec3891078e4f3f988643ac2ea713fe85e0bccb544ae490c2e2da36b64babe21628b32bc9e02af4179d76896f219ae65edea0bfeb0547ec2f38be18f7aec050d3954e86ad7c2db481d943419417eaad29bcbe0e5a43fae0ecfdf27c3f213f9aedd169e0947796552e2783e0c182bd13f59efeea5c6b607b2a8de9db3731b89d850981d2b444e45e7b9a68a8fd61448077190d2c1a8503acfc9de694211cf10b562958b501bd57cef3a453b7025ace16a001a3413a5cdbe815d1f9ba5af4dd91235cdbc459b9caaffcea6ec51027b88a71e9a1254d020cc8f29d409e6745030de01edd4a6ad08560831a42067087eeb86d901493d51787eaef6daf77f2073fb99eedd8da562e88d95e8d694869b36d79352ffad39e21eef1c29b8c128151209587aa4d2057569ac644ee884220cfb32280fe7fce417095ea64dd6deba87787579d5d7ad7ecc1fa09dafbb865e3a0a2a122b37fea3e2b322063f3c05cf9cce9277774c8b91743567ff195552827687d5321ead36aa8ded6fdb7d7f6ba87a6345e00b9eefad53e138f51f0625a3e16fa00d3207fb67f1906f5ea53a4d22a192df25ac1d038f4ead5b594291da548da0982c3b310283365e26540c7c9604351b5b4451b66e1315d9d7faed4930e44c759096811f19a305a2826e36e7b2448b6e477b86b9a48dc3cce6e4100279fd4c64c7693c5e05a2c9e2eaf85caaa88bc0d1ff4dd74d9d261416d3ec89f8b780a9c6e522219fed7fde73a831ac8b1a199ee9d411ff4c17524450bd84fa4405283e5c39c8348ad24d02778b7ea799f4998bc8b568308c00c01cd74a1114c59c583dc98c68bee81656303d231d0a67ee86554c1563177476c065d12500e10ad7072e898fd309b6960d405d91acbc7a31fabcafc4593f717439ae7fad7390b1e25363c87496cce9b4931eabc92121ce8ed653881634c47156c41c48aff5510e8da5378810c32c64aefd160c12340ce4455b6c29bf604133c9460a1092463467a37cd64b4033dc0ecf039686cd577cfc67b45ac58a25f560592bbe2990883261b7b39fc03c9c2c247418c11e18b72e25233261c7b1dc4b20938c0dc1827c33c6d99b40d581681d1158472e364bd8b3c9649891971f181dbb89c59679036e5c3f11f578f7b462a6cf8aa24582b632ac90a70d41a8caade04789b6dde3e4b276536af0f7ab6cd6587ed9707d335210646c958b9c8bd28c326da35f1fbd21d138860f3487ec9dce0ad3ca85b8ab872cbf183fa5118861939bbf4cd6d291ee3c353619b1ba9d256b87e40bbd10b142be2d3ed6400fc9dc4aba3cf40a056c5e6b021b9281da68905cb8b9e371d30f57a4528e960fa3e43bc86fc437b1c3ce824d53067e9c5141b399d2951ce852f0146cf3ecd96a4e22b6ec08dc0790b63890c5a0e6142190b683078b164579bb1d29f820c88c0dcc7bc8a5bc3aef229a8484fc7082370dfa46348c156192943a1f8b2412e0a0403e85d5c98e0a1eb75b7f4ad0422c5b7eabbb16c1ce89c71591a19c42425626b5d8066449dbddea5b3522b4f79e31c3ef25ff2f5330fbb696a2a0204bc9a7b7df46de83fc8d577da88c59b385ae6762a6675f2c9de66d9ddb7ce8b9505ce0727814cb06b7052832237c6c33c5c2207100ecd7bf26ae292d11d0184e170cfa154b3327b9360e0326bdf8460519a48cca6dee901a92df33b7913e5d0360a2e94dc175a04fc5b39e052f4945909a670185f7957a6964c28c35e6b3a250403ca2d0c5d61007c50ae191f8041149035dd831e6116001b51631575ddcc851b66680a9872a757a8305e988506062bcf6241678a6b0af071db832bdc174886441e1a40a6fc9177b8042c79d93409b17f15372670dc395b93184ef642924b13f4de5e0a05c798702d549b42a883da678ceaf96b0afb188301f9509e60fa8c4020665d553493c04bb496eab290e270b3cff361fb031e138a92a87714f9bf4dd6838a814c77be55fd7660c9318883fdea12da67f6aa768927f4babfc35d99395438cb7edd1caa1de02cdb40fd19eb29561b3e65b2e910b83a146ab3ad507c454ee67167ad8fe81a2e838796ba6ce2d78a382052b9e9b5a218d549798d6b5747d107e8a419a46276ad3b8bcdebb37c08d05ce48faf42dacb43e01b7637952c129ae596364879fca85bee6d8c7f5628332024f9c921060f8d3f5398f4d5d5d55b68fd263b4fdaf9f223881a0d0e9e574939dfa179a5987be194c7fbff03c7c9c7d7655f668f69e3cf1b3f0df59ed40e7b2d1567627503970225c4eb0d75f3ecc09dddb41fe5266139a3cda8978b33c3fc488d4d7eb3fda7af694c762044a016a8de25207edd8fd1c571e661061003b0a2618e0dffe08a3515d54f2103e593b2d8ad6c34eb136877476e6c0ca167cf2ed3ea0bb103e6e898361fa5805674e60d10b83ede3685b9c92d7c1ecc65e04b7a64dc046e954ecbb57512424a08f0f12d203bc7ba52e0a646c058fcd3b52fb202f01aea8ce0df99cc9eaa292ee68c4ef5dc408fa66c6e06c658eba4ee2dbc52953157be27e987ccc1056e75adbfd5c075112ee9e5c6ea844b02db43e78b835464df8b26c7207f334bdf8a23845ae6cb3ae0c4b8ea8963f0d0fd48311606571c4770b22ee910147cc825d4a204244d770120f796d2e7346c590f2cb9b28112094637d7a417d401505cfba9f0190a8aed59bbd67b4dc7b4b4e0fcdfc0c5153480976c12bd84985f43b99d13a28b989bab1d6016ab02461b453ccf8a05ad8173f62806fb50ab689f91ef66ea1f9b4f75f1ca6389f6c4f6e9480e312359a29b00a7a1a41fea454b47555b2d28464efe0ed77a0efe53b309ca6481914ec0adafe115c026c72213b20be76b16947beace79ff90e5c848e8c5c2152cf148c9b22bba3ee3a556c87005d49f268c7349306317a8f3f9077720340b5489b27e52274db89e6d0ba570591b5ff17ae0101e93fa9f1a6024137d3b079c0b312d3a403941348075daaabfb8c35043de75949844e2ba3b1529023bea5d8ce0f7f6dd11bdc6c1abd3a7af574709c256cf61904be356c73bd026a053f24079d474440c39339e2f0b02efba80a636bae759bacf9f9948181c0d60895ff870425cbe59e5ae710de2534bb8f3f7de68cdca134a7d94ff4469dea045fbac79cb7cf1b522ee2f90337454a386c4b097830210ea31f762daccc424cf4ac822548a98210ef61e3bd42f730da373f834f32b0f362784a85520dc49fa82c15bd266f30544ccaa66cb13ed2e3898a8a3f3aac826e94c608b497827786aa8c25da2f5600ca1b01901452fa8d87d3c587af771832e3b4ef3ee866bca201875a44cf54a0912a0dbb5d6a56d9d270e482528cd8ec243dcd1b23b53a3b11b1a2dc49aefa08095df45fdbde26ebb751415405de50a10d5791d10efd8dbe5bc9906f24a7394061ddce5513251bad244455199f503d81484ff11fbfe4e3c9bfe93dea10bb5812dab028f37ae5c39d5a645d72c32c53006881ee69555524bd178af4d4c512d283ad51c7f0a4c26deaa6660c93189d0cd4f35ae130abe0bc974ea36b919401409bfa28505a9eb5a3365dd45cf3ea9411f3fe74113a7bf9b5159893b0bc1e4a366245824eb8c02ed05f676baa6717df6aa8781263018af539d7b23320252e1b25b6e33509fdd9a34d67ab0f45f9d66c6e149a11941cf887528616f5a721e60e2397fd1f407b7bf21893d505c0b82a3bff0837489e74d2e56cb8481119bd8f5aa7cc167191d70e6718f1a97d8f629b42de93fa2284c33f42d5e44d20d39e2e147544592a469a8bc394511ee813ddd2887348ed23479973b1b62b58133f05f487fa6cab6382711cb3a351aa40f562b165bb17aaa5fb931897d0157ce76a9a2941a41ba08a36b816486e9724615223ef4fbdc49cd4eff68ecca28814eb2780a3293bd355b3a6c9c67a16c9634dcd73b8dd5207bebb8c4a8c95b02dccdf09e3b9aa39c5d5d6b494d3f8b2d1dc1b45e4dc0ecdd9eaf74f8fc4593222e68c56ebf8389963c41dc8d8165d325aaa11f0036808347ebd155fd324aedcd2f0a63e705526a15efd59e331ed97fb4cedd5796db744e2ee51e4a28f614ccba63e6b71ce4a105bc23269148c15f1727dacda23aa466f515231d4f26e1351da4e70c9376e4c261326334a614108941cebd7dace8341751ab44553d449251bc56da73f3b102d5adcc409da1b8b0dd6ba9ed841c4cd44e59b91d1f0744ae0befebb853dd42d4509df12f8417e1543d15c8d206420101fea167d28aa49f19174b435ba50f193615fc7763c0f60609af547b9e1b8fa85b2d1cb7124531a8ad8a3b8f1b8096ffa57bb1679096dbd13de15b1222467945f80427cdf351c4a6ef384e6840610cb0d22e88e420edf3629640c4021f3b3ec7b0e94c7904d150685164ab1069ee6f7ccd7ddb6b7397714b51e63dbd05dff21047de3356c3d85bc01ae33fda37790a28d2ebe69a7b1dae7a7bb940db87e4165840b3a0da3a65845ca72b802e6d6820b1dc2ced750bb970f98ce1534857a2950dc876d1e53fb5134d271cda1d4a2268820d14fd798d00950210dabf2cb29f1aabde0bd2f7a7ea71c36db020b54baa037406a10f290c58c6c04c9dbcd969c7c0c89ccadeaad5efc4bbf26f181f77384b0b8bca61227fa5aaeaa29f17e3cd7c56846cd964797fe50596d607d58894d37b603e7d2a6fd8c1cd9d0da9284e752a972c55e0cd6a5522bee50e2565819f3a0e5b9ad1482842bd978cb8a079e71f2b631f8c8ea789136797b119dbae23d92e4c7436cac1cb5caef920892d51a095cb5e301c6970a106cf76ec668ba006878daac83a3fed4be9db17cda7599232116638674b8ed64a2031a1ce04d744874fdf9cf4775eea52ee93ceef9e8eb998ad1a6986a243ec27055cf499da0739fc70f235bfdfce9fed2eb7527a03dd161dc23d89afdd25d77cade96a78be6d9201632ee86729864614166e49c1f0d047ab206ffbe8c7d918a13da212f2f810942b8cb8f775dc8994389037873c4651ead3cec31d010460548bcc127cf63f56b9eef8a7e8f9c0743a82f0599bc14cbcf1b190795a18b500915bb0201d8d7536e763fe40dabee31924ed85e8f09e5741e8b46c3c8b42865ab7c74b1d6a07fcd12690d557ecd6b9cdfa7e9a1075135e39eef400a23c83ffdf779d6b9ff68925c69e7584075813393fd6376e47b275752bfc9b76c364614358e98b9f93067b131b129b818d8ccd171bfbb00b50ff4da42d6cefc5262846a3f7287ceaa602b98f7e6c4b10083927625a49717256f1ca9e92d2b63c0ee50b6d757f742e04cd39294b608a5fd8f486bf0313f69af348d1a5761b6915a212918092d166e3246df3f6ae405618b686e6aa184ba5ff15e9dff8a908c31d05a053020067f3d479534507aa048e55ed86340c87627dcc015d7332a37c984e723ac0624ada9d6805f6eef8f25441d8117f0845be44339beec49fa8c735aa212892479c89ce9979839006d014ffc9a7d4cce3584692187e1795042265cb4f9672ddc25a01b849088efc664a42e635400899db12e577a29ccb3e1220b89fce1732d066acec716e60d0140356509e19f7edf6624cc8decbe0b157f3331774fb5bf45d1a63ed833c5e0ed712dde89e952924e96cbbe3d6743887c3b2350b86c8f663f9f7f9eb54d019641243279d0710eb3ebb255056a21b963dfa0ab51b25bce9a738c7e3ba5cc2b957d3a80b1db3525da812aefefd0ed5734899aa18147e8b740cb2301eb58166e969b5420d371e1fb1275851361b764f7196fe34e9f537ab6e6d835c2a1665035aa54d14d11851c138a2b3ec708a21e22e3b10761d60526639189b080fdade1ee52e8d87122e0ca3f37e8c32f6b5f1a99b91021bda98dbe51d38532f548d09a1cdb5dcd46658fb76dd36334ab15be947ba424c43386c7a0080c5f44b23da6fcb963a75dd223ea12209ad779f61a72246ea9ccc4540fea6feeffca7b9df83493d502feda7518226a359829ce0477311dcb99164b4ff958869cbab0d2df5260117797ac97d6d4410dbf8bf6ff38f2485fe9e821d6944542f52794dfb212b43cf865f85e844e750f6090589d6cc7e7da68cce627ca1ce9e7b7832e2ac89f700aca7cf31dff3736a601a9dcbc1ac5a4160ff4229701e7d163d2013e924964a424cf54ea74c4028f9813375fbc48419fe392862ad47a6fdea7e526753a0fd3424e84d57f0e259a74ada54a5abcc1b3131161d65f33b15265d35b173154e42d251119436b5816a8f5b3e924b36cd4ea3a85eeaa86cfd1cabc94b9b6fc4b7d0004cdc9c9ac7cd6c51d7639b5a01fc35027ace779b3a70eea4900330fcf0c30f3ffcf0c30f3ffcf0eb6c867010f2899864a5f53bbfbaaa12e07c5352529225e3ffcd9afdb58192b58192b58192977301c809ba09ec09dc041a51388a318368881355a537281ce4a489a975abba2a963ad078c281678a68e7042bcde128d070c2417f458ced1fe9734836e1c8ab8348975dd896cb84a3ac90528610dc251cbef9db55aa38f9bf42071a4a3870dded8e3192241cffc5ce5c59e3c4949b31300e349070ac72b1ba3fbdc43e4738f6b40c2ec13724ebd3084771ad315fd7ed5e4d07071a4538f64f997f626c79df9408079f534c44f53895e319c241bea8bbaed1df95c34238baf6fcb2399b37d008c29187aa30297badc518ad25d000c251b6da75d2aca41df70f0e52d9a494c2fe56bb436da0e183e324599e54e37c47cf3d3836fb0e99fa3c8fbf448307c753b177a272bccfadbb50fac298c0ef0a0c2cc08b118401460abe088302ee0ad8224301347670f86d5bf93a03c38b2eb8e8a20c2fea28023474705829931db1ba39f864061a3938cece1d53a64e1a2345c6400307c7dbdb2dd9624b688959e00b2f4c199e002559a07183e3a01f793a3d675a521dd0b0c1f10613cbdee9bf539a6b7098fd5cacfd3f06d194185e74b1c51662d0a0c1e169b0b9b0b9cfe028e594ffd2b6cdd52eed820cc380531a3238d28d17374e3a06c7a12fdaabec2777ec6170185c3527c99025636d1a2f384a5fa9544f1b267dd170c181e7be8d8a752b8e3cc22c44c6d3bd25597110fd838618e65a12fb2a8e3f86e8c857a9e2a8bb66c3654e638e792a8e5b3a6fa7246e39114105111db7d5f33ea720759c8b7c9574531cbf67f7596a67cbf629c5417c38d11cbbda181e521c4bdca60d369fa4da328a8318d5d6c3edde778e8be2c8e32f3f227abb2a1b8ae38c5e19aa5b06c5815fae10be2dc987463f7118249ea7f5ac1a91a3278e37c7e43421afe710cc4e1cec25ed4d2bc9737a72e2e82eddce972689eddc260e531a89ea2995ba99451387496e2fa4a46ca163cbc4b15d566baee9f1d462e2e853dc2cd3fd1247e672b7258e4454b2598ab1119daac4f1ba5ec9685429713cf152fb74e50d5fd1248ea742d6185583258d39ca82199238ca27392c67a9eb98934dc18c481cc5f7f017d65f636c0e240eb4b354ca39da8f38eacca056b69d230ebddf2f86d68c097d69c4a1d4d5665c488c388eed39e782c5b7757ec6220ea2c5f55d2eaba4bf228e3cd6aac54c7d333b1371bc99c973ecbbb890d50c441cd444d59290b3bbb855d630e31047ab9a1635db45fed28b2e4ec005d5126618e2f8e5365b6e25de7e5d8863adb4292d4fbeb9d210e230ddab425acd64b71c185e708134c61661c6208e63e608b2192e7d292988e390c4726d4d6c2e99e00e6604e2d03f3b62f3b576a42c200ce311b5152efdc3917dafdcade58f34d6e960861f0e3edc55c879b3a7caedc3819edae585ca4ec93d3e1c455275cb6ed916550d66ece1683a68dfcd4868598f1e8ef4ce3ccfa2bf66b9f37090824e7ccbabf07020dde942fcd94b293e7738d87c9353feca4acd6887c3f01424746d5787c39a4da12d4987430f579b3df5ae25324283197338928fc934989a64282113c40c391c5caa6fd7e8751ddf8a718130cc88c3b1ba94a5bed411fb61381cee5b878c99adb255e50d4726c9fad7226279e686a374e93ca5da6a660adb70a4417246c4325b8cb2e12865c4f210e56ae73b0117472e460a3030868b218619630334630d4712e2a41599520d475b71c368a8cd01c3c510c300291803032a989186e3b2b5dbd8de561a73c0988186e3eb584936c53c3191679cc1142554f2915630c30c4761d63c429e18f2e614045ca181321c65f78c776e91cbe292e1d826c56cedcc6c259d318643ef0c3df71417c3a15eb8301552864929010ca4c08c3186c111cc08c371fa178d27613fbb2c60388823a5913fdf2f1c680a121e36c69c1a572ffc51ff428ad9b40b076fe2997261316a835c380afb13ed1566b3764280195b389e8938a9dea5fd3205ccd042cec8c20c2cd88c2b1c6f4fa70b1953dbeda60bb2c10c2b582f155656d1aec271cc10cba3946dc5149d4185a3e825d3eeb7a156df195338d0df8a613d74e2c8b9c5dcf84b148e2a7bd6226c9414c30b858332db18aee17dc271fc93919c75b256ad4e380ecf3dcf39984d38f2983bbf8fcd84e392d9d4f8d12c395bc2a145da1295e99470901de653d6c4241cef8b5c05fdcd4a63876006120ea345b38aa142473892987c2e963e4f9ec80c231ccd5f4ce599725cb26d46118e474743a8a89d9d4184a3f87962c4d3b7bd75c80d3386701ce5d7c724cd62fe344308c7122a9475e4db88a6705146185f9041e802338270b4712d2e9746cf1872188514338070d0957feab33b7fc6bfa398f183a310affb21a7dcb1ac77a154a898e183e310d54a9164f454564bc58c1e1c6e52d51cffc22ef4b6d8826a060f0e3ca7cb4e69a3329c0c320c174766eca048b699543fba020157943106152ee43043074793bbfa1fa7cfc1919dec06c9d291c2a57170e89da583fbcb0598718363a92ef7fabd9552cf06872135723aead1276d336a7094aa43dce68f1049f2193438cafe5292c3b6e97bc88c191c7647f35bcf4b020e0203c6d20c191cefe5b0d9a12e5ceda70a4303553362709cbff22de494cf192b46980183831ec9e7912e58678b21c28c171c74b6b8aeeca40c672b1f238cbe805960860b8e55fa3e5c5ebe1469314a508693518b452b0ec62ed2737e168bfc75a144069162c18aa388b963ac74a5efd757715ca1f19db9557e2d57c5e1dae6f53f3b1507b1a5b37fb060db72a3e240c73eec36068dcf9de270e6d3f6aee62b09b1298edb737df4518933c9528a63f7d47d1a1245d75b52b0baf5f12b257514873abd19c2ce298a0399a929cfcd122262a138ae781d425bcefec983e2305d4a9a3a68bac5479f3876971855be725ac83d7110cebe724821fed175e22885a4decc9359246d06c83d60c1092653fc7787486ee2c0be3d9f6b7bcebe7b12b0d0c451d8478ecbd9629189a3f71d71a9aef50a5860e228dbba4328d5bc948c52018b4b1c458dfd69928f9ce62c712c12161b3fbc953848de9d327f904c11c90a5850e2a826a564b521c60d396712c7c92564fb5871bdabe014b090c471987c7943e62f4e2a2eb088c481f489a58b9ab579251690389098375de73fb7a78dc5238e937e5e0ca55f712a8485230e76be3ff67c52b77c5834e2d0276626a509352a592c1871b41b3fdf8589394d368b38c89ba2640caba08085220e364fb21039ccfa053711c7156ccceeb25820e228a78f1cfd439ef05ab138c471e7a78ae43a626188a38d1fa298449aca21195814e278ce25c49a24b1c6a21a032c0861b49887ebfa8e3977a19474050616a080086cb1051765f8180d46180820a3015b6c01868b208c32b820c3bbe832cc08b6d8e20a81c520120b41d41669012c02715c316d45962f9f109518b000c44168c4f88dc4d8e4fd0f873b3a93f93256dce97e387ea908093926e193f7e160bca67aee2d72b60e1f0eece4a34cb26f9ba6f7709c51b36c3e8d583bb51e0e3dda2464edb902c5d4f14206c7a592d2535e772306d85aaa7e083617611c33c2060c0e82c445996dfbe4f2e161e305c716e4c286a0125762aceab0e182e3ac96d3e3c4e83db915c73329b769635b3b3dac38c8d1f6e34ae73307e90e3556a14f879aeafca22a8e4362f8df963c99ea151d6aa4e2482384a94e91e36b8ea3e2207dca4f9f776ba8718ae371cb496fb255438de74cc52adb7e32821aa53870b98b14e3fdb4e69453831447fe1ff3e65fd653737ca8318a4379f34df1696bdf3936871aa238b4d777d9d149a6a146280e255b0e5972f5559e078a438d9d577721dd48ced5f8c4f1e430e551328e4ccb353cf1c712579b19895577a164c5458d4e1c6b44ce4c13529ac389c3fb2f359fa96c7241f2428d4d1c564e1f7b36d6f9e5790935347114dadccc2bcf52cc452a13c713baa13b84ad6a60e228f47b9cea9a4eed145f3441d5b8c4d199c4601236a68ebd5be2b8eb35fee323e5264f258e2b58985429ceaefd4289a3f0d3112a9ac673b44ce2b0b2a7dfa04be228947fa409fdd12c66240e434cfbbd1e41481cada59c923d498d471cc745ced92ff78e38e891e90d9f2fbce802015b6c61871a8d388ae8d192a43a31d5c8888370933d048831823118506311c7512521de8615711c23630a8bed127118b5e22df27888e620e2e863b4354971da620b2e680ca23ed43844b16fd925e8668638b890d917b9558d421c65b6cc38e177421ce967399becf40e151fc461d4bc2e2e3171fec3823875ccb10ac4e178acf06c31b946a580b8cfceea65d63f1c65b110abf553629e9c1f8edf2b59fccd55a30fc73efea163eaf0e1c87cae636e088f22fb1e0efbbc6298dfb49fd37a380a973fa887b314e39787430b1727630ef36b13e3e1b023844fbe361252e70e873bd39233bcfd556a8763b9ce29576c5e52d0d4e1207bf96910eb4c532d1d8e7385dad8bd8a7f967338d23cc1c77d2363ba723854977ab5ef8ae12c8cc341be9bab5a7e7038dedbe8d099e80dc7394d75c4c76bb8e1a04d3d9687aa0e95ff361c88f77bfa059f0d871b345b48ef49a6d5d770a8d97a13cb72351caaff8724a9b6663d69383a598b1e47f744d368389e9c2f597e7cce70109521449b6ff7189d828d6d88fb4d97e1f02396e7df6c93e128c532472a87cfb163c67010529658fa8d21846dc4701055634d8b59c2701cf9630e995a51fa5c301c556d76c6fbf40b079e5f2155bea07179f4c271ce8edf3bf18d1463178e92776b97c620178e63929e84e0aff6f56de138e9869c5a96da9c4e0b073962ea31454ebc3a0b471d3fe7ce28dff063e150f7ef6388664c86af7018f3fca4a5f859216c5638923cea9fc43f5d3aabc2d1cba46c39db4585c30b979496f348b6650a87f17ccfea3629901937d5870945e1305665bd4908b9734628e0841a50387c996c91ba50952dfe84a3b4f927ac63f4387e3be1205d45f65fda84a398653ae387dc92ae64c2519a14d613a992a6c8985063093594709c26bd496cdef06076128e572c5744434d420d241cdb2593e91021644ccc165b24a2c6110e63ec7d798e560d775da00b320c03b6c4302368400d231c77089751e4bb752fa70ba5d43dd4284241f466f334590a11ea820cd3450b6a10e15872bbf94f495d1c2ec8f0130618554609260001f7620c0c44a2c6108e72689e48aa1217ca2c01185c7c185c143ac0058c12033584701442a4d8926123d49b65841a413888b8f296d3e7f89714a10610acc60f0eff33f5e674d91a3e386c0b9749222da446df838308d1a7245894681b91e145a3c07970fc9f4683a7a9608451480335767064725927fad96a7661400d1d1cba46f0b8f96fc64732bc80805aa0460e8eafbb64ffd3f8565d5c5081c01c206be0e070b4c76cc257ba02030b2003015720e08a11340746d0284841d4b8c181ce861863746517ff9830be98000afc9049a0860d8e7ace3dbc2f6ae6e732d4a8c161ef7eeeff7618dfc5508306879a73d8c8fb35166acce0306454d1ef490acaa0420d19d488c1d1fbff7a923ca163c8156ac0e030e7c64af91023776a6bbce02845d98a7ad9a328d470c15143889938217a1446185d70e15f1c2faae440a31587dba6e19b4429d060c561be3e310f32792d3a739153a0b18a2389902d87c47fb8185d8181055c81802fbe30c01508b8e20a045c81802baec00004ae3842484315871f646d5435dead30cae0a2d8a1918a833edb7429345071d449ed3f367ec4c5fa1487973729f227e7943c6c8ae38db1a93ba5e9d130699482a49f3727cd9e1447616f8fe2e8628adc985b3e8b4514071be347a59a90fca2a1388c1cd594f2098a0309414270a9a0f1beffc461e7f348df515e040d4f1cda9bcd67080ffa7e278e34e588bf5f9d1307994322cb8d9b380e9e36e25dcaf915714d1c74b6f20ddbcf1cda4c1cc4ac9c72a2b59321260e2fce7aa60e5eea1f305c0c329006342e719031db8ab4b744eb8e250e5c35f7c7958694a3af5860d0a8c4816feb670c6f97824d8963d50916ae159244a52771acc973db54a42571bc7677a3295f6c951c89e30abf7142b6061247a7bf633753f5ee96471c6807b949a92cd5cae28803cb9f53af4efa5fcf36e2302f3674b46b95ad282390eff8f198451cf465b01c62bbac2355c451468a41b3dde59a9f88e32f8bca2013f31f4e44f02f751bf25e52e81007d933cbdde6d21007f9537db3536fd9948538ca29499d7e4a65a424c461ca8c7fd13e9645080771bcb7ba6a84be4b3182388cba4bb9914b547302711c1b2e4be53be2d20788c38d512da79918d467fbc3b1f8f7e68743b50ae9672e75f0b90f59fda64cf3e148e2be47ce3a27c37b388cf163a755ce33cdea81d2ac88112d9bc46e694b6331735a5a1a7938e84acb1ce2e3e6070f87dfd17edfbcc35168ff878ef1d9e1287f287dddb0ea70909eb94bf32ceee5890e8733229f65253a334c7338885e4992c3a1c65deafafbbb55e370e85ffbea9e63789e70388a8be1a266fd247fc38124af0ad3792923e786c36ce956a3d47957aa0dc777933b9494ae278974a1f485e93020a054be30ce001a6c3890dc2ab5b85149048d351c656edbee564abf9e622168a8e150a26f5f6ba64d1992693894ba370f99fdeb316d1034d070947749c25dd4dc8c92331c5a081312566e62de6a82a06186630b952a345f321a65384ae9589241da527fa0418623cdb952c8e0395b9e681f688ce1d83ebce4fcb3c470e861f795c9a3bb028d301c7a4c92942bfe5c555f78a00186c3b8e43b9dd2e6b8b5d41d687ce1502e877cfa59a33d5776a0e18523e9918ff943a206ff74e128c7f1e95c325924d24fa0c185e3d6c9dd1472660b473ddbb17b1a936c8c160e7cc2aa9b84350dab6864e1e0ab53be0db92c5838b2948a663d10685ce1e07273ecbcf217e5fa20d0b0c251d4df6b5ee9b0b81df5018d2a1cbef8a7b55cb129c5910a47e923319bc685b4eb7e40630a07e36eedb26d569a39522858bcceba3505ff08b562ec237b1c717cbf9f5dd45463da4e2308d1e5b2456eef9411c739c4cd9c38d71666594466771b36deefa688239b75b9fa34713c3e8697e105f9175cc89045228e275d67c7bd8f88e3b0103153f38738f0778fc1ae2b346537c4b1e7f1b8a7655196ff421c76ca7c9fe28734e631843898f73c57cbd11f561bc4a1ca464e2e270ae2b86b375eccf96ee90fc441dc1ce2a5a496cb920b88c309f1b7f442f787030d2512266462852cfc70f4912466f1cd92b61c15b2e8c36148e4d13c9bda436f3e1c5584cb6f2bf71e8e538c9242f9c5cb1c347a3872ebb1903bce29649187e3ca95cb37a7c6c9f778f823733e79cf8fef70d019b723a2f67638d45039bfa4742e3ca70e66d188fa91f2fad0e13866afc55c58f988f21cb424933fe74a9d28a21c7e491d267b860e9a389839ef62957e957587c371c886247759bee1307b947c51f5fce9a31b8e6d4279e6a9fbce93e342c590451b8e534cb66dbaa1e3f767c351fd5d48db4b9765fb1a8edc83a678baf799425c0d0753c942cadc95341c7bad680e9573f6b48986a3902168b29043fb3b7886c3087957aa524e91309917b230c3f1b807c949e2a9bde4cb7094f62fc242eb64384e1b2d660d97c770f479727ebb8488b7ba9085180e4ca34506cdffba97301c780833daf9413786070cc731c678ddf3ec41f2fec2e159dbe518c3b5536cc43023302164e1858378b1226972dcc686d403ae40c015b4852cba508aca87fe5c20ad66e58466cb6f61119da4160e5a15c92f5976784e16e8ce326bf5c1c293653d7c0849f2397d857334a70f5f5b414b8b1b1727882797ab80578e4125eba496af50c1168f199ffad2296471e42fcb7bfe4b2a852ca290c6c4f821426558a0c09d4b2e9f1c29f3e6134ad2154542e54caba1135eb58939b2c7bc3781e8f3e830010b51c29c47ffc92de1e80fb7c430114812c38c8050164ae824f24892a425d62661496732f3a97a4124f0ffd2b9ad53ecacfe0887fe9e7e26669d560c3302338215c483844a112a8dcde9adf3d7f94984e3d7dc1de2f93f0462ded3fa678cb13f11c2d1e5e4312dfa6948bf8390471ccfffe7934b201c44ee78994359d0989a1f1c73ce88ef1f363e8961a8c43023301464e183c310c627e374787e64c53011200ac8a2078719dca6d345b4f5869590050f0e4b935f8e1ae39f67548e90c50e8ec542dd7da4570707215d57ec85c866b63938b4ac97cc36d13f5f2c0b1c1c59eead8cadac7fa96f5017c8c206c712f383060db211b2a8c171f0bd903b711331d9343890dccc9a1dc52c66703819a12e6ad6d07b1bf1200b191ce5a8a9623e5fd765450259c4e0f03b3e46d2e225ef1f06ae86883291f80b8ef642c6780b1ed43d59b8e0f8e24588776561333cad38ae38ee12be66561c78da7b8fd7cd2a0e3504ef934d2b1131ab8a239df8254923a74fd6a6e238438694ac3e7bbf43541c8a465df5cf8bf7f24e71641df675ab62a638a8ce52d1f98feb77290e5a3a4e72d25ef20d290e4354e7d2ae7f6a1cc5f17f8d864b88f4ae4a14c7929b5fdb326e6ea62b104043d808c5d1e67b08dd8f31467fdc8b313000840d501c7af6e99308fb13c75eb9c23544d0f27cf1c451862db795b93a71f439447db0c189a35c21438b474ac815c1266c6c02f7ae1a8d6d399a3848371fb7fc6236327174ff5e1f721213ce6d7fcc921aa15ee2482da3dadd9f77e66f894f633d4f5e34a5b21207617b5248ad6da93713c30625485e492ba668967a0e9527c58e21ae863cd898c471cd848b6e1ddea96a0afc0144071b9238cee4f312237754c4c546240e424e9d57622c4d529e0bac2b30b000481cc4ec9273bc30dd73b90b2511981194016c3ce23842497fc4297dadc9011b8e403c6d46cc2a13361a4165c48edc9a236c30828a9418c26511476ddbd44abf4cd8091b8a38482d757f9d92a55bab0b2531cc09c8e0028c30d49ce002604ca0c060231187a9233605cf253f1e93820d441c7988202924d94fdfb00ba5431c7587b7f31cdd5b47c086218ee3b6db59f478751eed08360a716463a92b5134edbc49888388c1628fe5fafc772645606310074136e78a5e9f0c22f7628c32ca86200ebba2b337ed53cfe60371a8fa7d15af9b2ecd03c4f1988fe69c6270bb0f75a104c6182528c3c7e0028ce7620464981370e165981d60e30fc779bb32e49430ca00c38b30ca00a30108d8f0c3d16bafc686cc9a3378fa709cd672fcbcb27615637c38d670ad1f5d2112427e0fc75145fa639818212bb8a18783f81a215351fa537784aa4660ee6394c19887a3b0d7f119c52585d137f0702813c532e7247a33dee1e8ca5228f95f759d330a1b7638bcad482100034fd8a8c361e5930b66b7d77e5117ba4107fd62630e1b72b88d387c71f597434cb30b2513b805b6d8c2043e86d118658831870d381c24bbd408db1095dfe5c2bb68d36294800b118411c6175a812b10700504aec000040a895102118411c61769828d371ca54edf99899812c3e586a3149952c7b451fdd8461b8ea5e29378c4deb8e0b3e1f0fd6f3eba2ba6edb2b186a388d6f1acd06d194236d4709cb62a67cf29de85d2175f983e301450b491860d341c6fca1db5245ffd59772c60812db6d8620b0bb802ba28c38b0d7c613c015b6c7126810e701180196c9ce15036a6cbeb36792a8e8a880d331cd5879e44fb66888d3294a2aba628eb621c36c870541937e4c771af6c5a17aa63d818c351dffac776486c9ca87a61430c59a7541eb9c984c446188e2bbfe65ec9e1dad906c371089735c49bd8ecb869c4c617dcfa9a49d974ae0d2f1cd9495db089af8b3407b1d185032bff9c75593136b87014d264ab562b4d39c7c3b0b18503f7aa0c1bd2ffe306b9d0f262430b47be53ad1acfe25accc828c3c9e0820c343248b09185839c5233d3354264cbf062045e7ca1b58185a36c17f364fdcd9ec7eb42e9ca031b5738d2368d3ea562bb096cc30a47551e652d2419f53a27b05185e3cd1bff2a567bac3418a60c2f38c130c00615a83eb031850d6c48e120ccbd6fc4f5603617ae022fcaf0020c4346148ee44c3ba4a5c66ab31cd880c2517a5bcbb0b5cb30031b4f38b6904a8be2fe86138ed3e4911d17b509c729a55e464846974d02b1c184a30ed1b9db431e09f737967070c15add50027935b5f744e1b09184a394db35a5a26a3e6d01400b1b48380c2106d9cd6f4f175d0f1b4738ec2f0f71f1b42b1070870d231c7d8ed48cd9381d368a706cd123b5a37bae4bd15d010db0336c10e1f82f8c26877094e34f2cf9ae741aa7f2c186100eea3359ca60317c8535061b4138f61ad3c8da6631edd38552d960030847d52126fc4d48b0bc651a367e7078d9d61f924dd20c1b3e38966a5f95b6b4d29ceac1f1a66c716f721e1c58f0105541f42be244c3c60e0ee4b54227fee965fd75908464912ebb3595612307075ddf9b2df9e85dcce3e0705266518f7a51bb3b17366e70a4f9e4375feb4362b4850d1b1c746af0d9beb58b8d1a1c7b4feadf90950d1a1ce4dad98e91f62b8278c0702eaa9eb03183a3241772eac91ba38538b61c430e218e3a4eda85dc64d43f8823cf9f5ff553942cad200efd23547bee609d6a208e73b28fb9b2dc144a401ce67b8e78baf51fb60f1ef5c371bd9b59a69cb69ee43e1caaac078d9af626850f473219d2e9bc8847b8ece1b057eb5752c6ca13d74376bdf1e38770f3706c1722a9a7440c77119455bb09480402c0c391590af183d84e5c8adee120445cf675b57bf4b4c3e16699d998d4cf8dc13a1c764af5a73e221d0eef35cd4d67a227787338ced9fec37c7c64b9981c8e45fd4dba2bc7b0d4e270686a2eed49f33eda0587a3b594ce2ac525b8586f38c81432bf3dab7ffcca0d47fa90192278d586434fbfbe9afe39569c0dc72e6979d3b3e9fabc868348b969530c799962d4701c63f927a6cfaba7a4e128a864eb56b4447b091a8e3dc5599c8de8caea194a618200cc709452a9955de7feb05418045086a36097e29ee5dc8200c870902eab64ffa8ceadfe188eb3b52d623369cee28b21552080301c4b444fda49ddd6b407c3e17ef6982a13c2bec7bf7014736b887d1173b4b8170e2e3fdbef8b65cf90d385a30da51d3af675a4ce85239d57179fec9683d85b3808d1abfa43b01444d7c2412e0f1ffae349eb270b07e313536889aeeb2c168e3fa5ed226b854ce6150e83dc869853c888b9d80a073b315b16cf3199dda60a07eff37eb91e2a1c47ae98e52a2667a9a770a096b25ca572799d8b14b82080281c581e0bf36351a2d20285a3b4ae0cba339fb5ef138e3cd7d4bf7deb84839a3bcf8aec1b35679b70e89a1e524f3e091a2f130e3fb56a5287bc90769770147453ecb3144ff351251ccccf47b08e7d351d4dc2b17e65bdd67c22e120633d44ccf811729347384e9ea142e6b3f89da6110e425fc6a9cf482f5f16e16832b28789cb20d913e1783a582c8b380ee1b862e8aca9b2536c8b42384c563a9ef3e618a1c4201ced5e8b450c9a5e3404c271ae54f937e33f380c1d3359be5c2652fbe038e44ebf8926f3d7ac0747e79a576f26e647cd8343b7b218521ae66da91d1cc56e468b3f9621d93a38eef0f09250520e8e2d86692f7bedc9530802c0c171a57ccb46caf9463b3738d4ed4c7e29c4c9b2c906c7b1eabd6b9f1a1c8418fb556f7e1317018200687090c7375dda28ff1f9dc191c549cb46f60290c1c16a6cac4a91211db900627050377a393f2adbd72d00181c9ccb84dc506962620be00587924e3d7d9ad5bc712300171cdf7ec76b780a6e49d38a83db942e0679d9bcf9c28a834f51c6e723969ba9ab384e1b61f935a93b7a5015c72a16eeb2bfef44c3541cd8ece578e3f23bf9a1e228cb974c083dfa397ca73814d1f87eb14d71ac11733e64d5965aad1447e173ae7c0f51f972487190ab6343c59da5d0d5288ec7c2697e96f0db5e140731c753ca1fde452d148a43df9c8e6f313ae74d7ea005288e3b494599b8b93b4bffc4714cfb9669d1633eed78e2d8436593ebb5c65ba71307314c9af5b49f2288cd89c398dfa6fcf4673ed59b388ea32986060f6d31c89a3848b1c2f52c993892d048a9f9ee7dcf60e2d852fa2037f21337572e712c9f247464a994435c4b1c7affbb9ea85ad48a56e2404265980c7195a6494a1c86d49e2c6e0527716ce13d224e779238f233d3ca5e31a2a72271181b2ad9c7ce49b3a4207160dda2f351723add896af188438b5d5dc9fa71c4f1a48a714f377948921b712439cc6f366fa6998611471a3ec2f2878b38cc15e1a288a3a82771c3b78938cc39d23a37aca6fb1511871d72df6396aaaf8c1ee2a083df5bddd746c8a4218e6225c9187a8285380adda89dd112296f23c4c1e888c479ea920dd9204892ece7553b411c7d0e1ab59348758654200eec36dcec66251f0f01e238b537766eaecdceffe1c077f32685ad9b684925410b3f1c49dd5897c7b40beff7e120236c48be7af3e13866b1b1d6eddff89c3d1c8a4d8c9c7252b2183b7a38e89832417bdf2e375ae4e17066433a06cbe0e1208799feb9206935297738ae12ed942f2475623b1c76b0fe984919cb531d8e2356d60afa6d5d214987a32449e72a559cc3d159b4f42159ab679a1c8a4921c76474ffd20f681187a370c12f66cb294fc6eb085ac0416fd79435ff063bb3d747f4dda0c8c67aeadbb0b998f8ffce865bcfc42d89e5ec6b3827a64aa49025e36a30f676ae054f73f134e8298c7464d2d1e04f84fc92f3331ca8e56fe690c1a26d6e062a4f7542fd46b14f194e77cdec519d31870cc6892198e668318627b45ecbcc7f3d5a88c1aa30b739a7b71661b0bc27c6b9c140dabb87103e97fd052605cf0f963eadec0566f4829ee4cc1d2b77817c5b1d4fe6029e2c34f4cae9c95b28f684c7c9d0295f8cb5405cb5eb94fa353fe12c50626146cbe5eb7d838533478c61b3588ef93657387cd287b9e8b2adc08ae4f5b61039e4520564d52ee9bcd5cd4b05de6c3eee8674b92964b6f9d22fd8064b490199a45b9a3da4c46811854c23c7be58f8bf40a134edda3d398dfd5c9eb0db6d8cd709e734217872abe8952d4d30a56fab182ca6a58509e6644d87a73c31e525102635f59d9c89ac0422866db647db3c4d12f8efd8bfe322e1b0b3657a8ece123a5c2eb438c2eea17eed3ba56e8df077f028d94263cad416c1149235fee7ddd410216dcbee901cc1552d8670b23c8f3abef1a91642e8642e5ebeec99522d8270e65f8a9de4b6974e2d8070be8f97e47e67ffc0ac9a318ace87257d60e467448f75d7f6608921578ca6163cb073a76a3f679a9fa9c50eeebc4e99f37265a9850e9cb9b09d69e6c0eae41d9752750707c565d590df6dbcc11943cab72c0bdaa0d457e639895ad4e0304b56aa129e29895ad0e04b3a3197e6fe0effb498c19d431aed8976f5692183f29cba8ee5dfbdcd6911032ab746c829e197725ac0e014726589ea1452ac9c162f58626e4ebca22e8ba7850b4aaf3fbf89edd7b5c2d515950a413656d86f397a8cda2a0e5fb319b3dcfc97a68a46bcd24f07cd525108a94c156e6f2164a83035697a0c6143f7750a6fbcdb93ca8598e3650a33c74497938aea54299a1416e3666bcd96278569b329fd37add38fc264f1bcd24417855f975dc59365fa864291ffe4cbaccde58282cad50c5149f737fc04122b5d33ed7d4e21e4896f7ddf6b346f759d784fbd53facee939e1a4fbaed59ba0ef43dbc284945813c6e011424c33b1578a9b4298206d8c692e52c72f4188398748cf1294b44bba50258c6351f53743c948082576fbf288eafbb1f2320926ce44952d092ae73dcf712b7fa81909e37e3edda56472fe42e23869ae46c81334b41a8aeda8440ea70279301689c4e16040144861e171015314000000001414862281402c4b637df601148003512a1a3e2c2a141e1c0e121414121e100643616060201083c26050200c0803c221713132c5d503dd27811ecad464223f1da44db89bf03621e5089b6644bbc918837db09376505c91b648237866b0a960e9e03461135bf52dc29a34c8014f20171c2cd79c5ccfd2fae43303c2d22332e29cfc75303abdf04a0dfe0cce518315c246d807d380646066d0c120dfa09a816707fbe20c1521e18165091c27e449f0baf91168056a14d2ef517f27a0c79e37a55664b352fdc6d127d02f59c5518aa7142605b74ea93a9d3401dbc3a3a68ec89cb6701a9b91d74909bc2f4a48bc0987828112a23767da76e06fd2a7546fdf58bb21efd36fcde3bc95476f1039b82a685916490e045c656d3d0cbc2438340037aef9a856cb303e38336f70f6c02f827d071f08181ca824bc9a1043421a42aa81871f6c1e2ac9190d4d98414658e1a0bca6b7ec2143dbc6257b83f95a7c03c57530b909e26029379031b83760668035b822837d26a180c1cf8096c1d9e0f541924f1873b2f397c4da80cdc06070f50d263b4ed6c18c09ec08ea1f182108767023844dcc776b50fdc1a6657847ec5c2701200864ba64d9933823c844ca4194f186f100c68ee863fffc59ffdbfe0aff8b7e220009c078806e805e808c00fe131038aef12ea9080416d017b8362046a02d004ca31c3207bbaa1e6c1e101ee41ec11efefd637b5e887a8d21d2eaa1e861e4e1c9e34e1fa7c849c626351fef106447e9f18318e4018fd33f332b9accf120492569abe8e407bfcffe64973a8ad51448a510093770dba2162209f766cdfa23ee46a8f893f2919ef2518450f853b8d90a7079ca195b5294539c73c59f901455dbb120a5185c61660b117e70cc28702436153e0c200e619f05076eeae11744a488f0aa62f769ca46756f8b10a28db869e25302a61120856db1af0c02a48e8801f457dac41adc94b3b6529de4c17ea1b9922c3710101ef9938520e9e8eeefb9841465976d8d91eef24c1384a6645ef5e7a8a3a3f0e3cf48a4d16d703fa5a2c2f9d0e187dbe7e6decbfa714f730115f7686937cf4d8ae307a63e4d6dd0fd6cd9811f2d5264f634cd52d59f71e58d9313f3c2d5ba10bd9b0cb294671ad99be1aef8f27b3b2b83b1cf21ca9fd5f776054bf9b7dab508ea3bf45a5326092e26ee24fcd06109126b777f7552e9edbc5ea5e615aab5afb0badf3b5908219e4c8ee04b24e84e7ed72d7a1802b9996490dc2f59e5052ecb93494c12ec2e7700a5d558493808b37596dea30a74b96219bd170ab05aad9ae07590fef17c49d99567a4b6eaeadbc46bb4b58f2ee3e7fda11372933b515ac8be15cbec3e565bd3ce2507c481dffb5dc9758e82f74946f2c34863162375eceaf1fc5dc9e127b49fbc3fee17b9cf4c72bd0b26f01aa2c02f6931fc0899594f1aa9721cc124a941dcbb87faddc8e8d34bdd9b274ee9ea31aa473e81dfb0bbfd76ee1d16cee38e6325008a9c6ca2c74a1795139abe71e0d6890753c9cf6d92f6ecd8d4c46f4c5381737779c208cd219a422e6e2a5769c9a72b84c46da759c2a180e3fd4774756038896e236ab5637843f323f97df09fdf032fddfa2beffa4d248d7e37e3994de1f657a92aff65d7dbd7f34befd7bbc89b16ee79259fda4ac9e883d41d2e5b19f7e58e7ac9b10eefd041cf6719cf67b68d1332ee51439d87c0527a3fb8ca37801ecff1c6d6f2b9fb64cc641535cbc3c0a8fb49a111b74a2cc7a24cf60ecf3a68b795e96cf6915b41d621fa3d893f8e5f001da51187b51101f7808a581d22946a6f2901bd16804f4a1368529766b6f5a15779df16f7fa1c50578bde9910ae397ee16afeb241bcbca2f340bbaad7571c07262dc598c1b1e712cae5a5f593c8ff390e9071a7365e7a60ced4cb0b3f8b5d240d9757857fb4cc8cad00dd5a589dd7849a6ea10b28559bcf44331885d550587a71245c29d293b740303a31ade02ba5e367bbd73dce3aca60e4be3a3ab6c4b3b601c0113913e37ae7799e4fdad080d37488a0d1ddb8f69cd2e11ae160b42b65fc77e9726b1c82f1c327dbc7b7f87cc8742940203ef93e56fbdc8e0bba11465855168a01401edbde726381707b6c90098a27421e5bb004a18989f43fd18c481deb706837e1300d1dce765fd09b1e97262aabfd470ee12edc4c4bd091bd44618e9fc083a1cc5cb4c64a487085516390502d0aa0b0c35b2b975f331d52fd635f1a3a0d28954812a48bbd6c1b790919c6d0be6110fde1ae09a8bf42fcbf0e6745cc200a00878f0d4f85c6d82a983e24db7bac0ce8cc20f7f1a6413f5c353ccd22e6e0bb1b093326f2b76c1abb0cf2d077841abd0a11622305c3e88ae2afac8681940fc8d1e3b93428d0f860b6730ae026fb2508d3292a279edf4c3432a3379e4de856262cbe5a92386e499bd0dd9346c5e5013d41c20a54dc3edd423afa6c6b3f90b5e14517ab172bd6b7ce0c92740c41ce09a2661d64238542c267de48c5d8e16fd6c77dd4c434b644b08d0ca41f2f6160a99f84a9173aad59328ed18a7865b67b4093a49f93c776e758e6681024d4c196d13d4bf478d459b1546d6d3eb97fd3f84cccd55878c55c445b956918bdf068403366118861740197a832f57338d8c719bc188d596c009d5635871418c704a5a6ca00622ce610c90f4a213a01e848fdaa8991443e6f69dccbce6afa502f3d9271da449127959b25c6b161db1fd9ad4d2026f776aef0a5ef5a2a32a1c579af3ad85c6069109cd5cbe4c7f0ef15ea8bf4b8bcad720df2ac2eab5a43973fbdf9accb66d8bf6c7543c56c63420dcac4a58761815382cc091c8d5f156bf3048069f8177c1581146110c10fe1992600d4610ada589560c0c2370bfa0aaf113831d3f31890f290398f58ba0bc92b33de29035700cb5fb4d0ef66dac0252c2ed8e1a84e62d8df365dc0da7fc8fe85e7c6f64fe97f01fdd58b41fc6e59ff0302704a7e6d91048032a95028e81a4e9085959b03b74a6fc81f0ef4f460f388980d17149c4f99612f8a00cb10bc50898d9da235c07a5131242e6ca3e0b08f2e6166704f1fc477d463210fa4c909231d62c92c5821c57c157504e3d5436e2b9de0f1889cf68eb6c2a225cc6699044fab221f8fa75e219d545199aaf515c6d4a5ee12447f7aaa80143b51d4f01ff622a2d4af5bf20eb2fda988e25d2260d8314e5eae625e8d331b5bf7383a5135b6e09cf45c9835a3f3bccdf59fe9b35eadb13e1dcdb6653b4d9a94008842458d40f8e0dcf8446f4ff51ed07490bc047df61f5af3834ad28c6001afa4f62cc2169f8f053dd088da2760099cb9a1daa11c1f299f386725c2821b538aa02c657a346e259c60851d08a7c59d739e098ab8813a1436de750253df2ca4381cd60812eebf332cb7afccaa3e1284aa36c2a3b34f06614fda8fae05f568795a9378e31e9b274d40257c66d91d44a1f3e474bc00a0dcca899795c165f0921842a63937cc355526c78b06b10f5f2c17828acbd8296ff346606ac3808de57b8b3b62920c1cb71fbe8433f4a5a51976f78efa109a645fcdd04bffd17c04c31e638095295dd80457144c3adb9104ace2400896e813f27a2352f4c809fa5f458e4edd0601ea1dca3226202f0d628410774d66c426f217a48033d7a858bbc408319c35889f963acd33c6a7d1f213c079fdda76d30bb0e60e2691640b2ea708006413252dfb896b17403ca512394b0054d43f60254605e547e1e33f35d5edcee8f2eb4a81b7c45007872ea944eb5ebb12ae238dd0bac42380372b0cb59edefa12878cb091ad584a54bc12420778d9dd5836c8c485eca46f917c110a57760c3a1627aa1dda02d67d604972cf1ab771123311e80a1803b5de1b6b67536cf7ff4cba8ab4150254255c52cff51475aa03ec11b232cc686e7aba3cf5889e91f0ff29d847d52968fe6f81650828da4590d5bad1c9dddeadb786529d6108ae2f28e51284a1c0675b52994cc2e6987f014bf22fd5200655713e267463a371af5c5e7c8594f4b251c431a0cec258aa1dc4e63ec2d281693fc76973229837cab2aebe61d0cfcb322e65238cda88278322a67c954f06ec413c256ddef1e928ebf0319f046d1b0c43ee3c256f16d0822a1291880c8098b8f735895aa42c4badc0004aae48ca6b45f2001bd5e4cc3f7ea130f42d8f243502c62b02396ce759e272ee60c189c0c1aed80a36ae3c21778e3f07c8ab3db012538a894d5332b3ade3f7735415a5827afb2063b79624ea56e56e925cf7f80abe5bdeca6333bbdb10cdc9056d588b15e85490c869be8ea12a38ec0fd1cb87eb98b8ef3e76c15733a144326fbc8d412efe284b91c1b86f217627d2d0d9865606cda32bed71abb95d41e8992ee4e44d5c754b90a671701c00a45be2cb36c24a4eb95e48976332a56f1401f59a1b90712e9d9c039bb1a9805e1151dc55b97806f127c1092566578c3fd82b5f4578c1eaacfb52807a42836e86f38728abe82c2e3ed576d62a29908a353492b1c4c98e599b08e8d80b02c7e538e73e6319fe593f02975677870e5665485595ca0f6ee48d14c9e2333a6d50c989daeba0e80e03ccae9d2d0cc6a45aacea04a0982c485af5aa40a42c2f1e1758606694d39cdc504404064185fc16157b5326eb911827f41d7d9c6078c3b8723c657c9b5580c13169798871dc8cab07026dd65dcdafa36920192c5941c4507ac977d5a062e147b5a39fc502defe9fb049ca45e749d3093b95a4c14f6a3984da382fc0b7ee5c3c605f0bc4760aa80e3f9661e02700892590c5948106357d5fef4d55fc3830e518f1a6c7ad8c95953567e49fd23ab61e4e1f2c3e6b424c2404f95dcc39be5b25f81e0db5cdfe6aea333891b71a79f26956d5d83ef12fd04733829084258ac78f533c28ee80a0e2b3d4b94cd940e319292f1e06596feee67bfdd49cfa855428f6831ff20e4553a7f58d446b6c5d9cfdeb219428b4455196ecc5620142fcde9aa518316dc458256768e6e099e988ec075eec9f88bb8f3cef5bc594614cd6247b52c1b82b4fc25aac7479430fab9f8bccf0337582730510b7b8b469a54d1b6b347593acb391dc0fc493feedec1033482544641db7f7092a678a8469ce2895e8e0a352ae6eb81b23c51ee3f9ecc10b2f224bb8215d78bd799b2274a693d377da9fc52a1f80200ba6928c8fde84b4152ac792688d833b5a5052ebfaa15deca2a67a270881adb166a30ade07bb55239ebe5f0eb0c1e4a3f02a598dfb58f74b09f8399689346b9fb5c18b4bfa96756925c2665d35de089713dc7018adc788881a7c4b1033a2d24b6bee7ecf91fe829a0410e80bb48f2230d1b801b6360ca4eb88721918c8d0251dc8cb99b4ad27055fac3e872d634cafbf87afbb13e3e0923df632f968f58b60d8c9fc0c3707c6942123c0f87e3f4c8bc54760ab694701bb85a914334cb9a368b8baa9bd047af4c36a5ff154020f966688fed0d7a65737993553b59c0947edcb880374c9e1b972870efd733e65dce1329ce9a262a90307c592b0f2ebed554bbeabb3d994ac6f6c506b368b4b0c564a307d85a829231624060300f53ea0cdbc12ac53c7412cfdf08e07c4f367ed79f59f8f5e86a8c6d3f6e64b1641defa0e7854a42e8064fddcade5ac89ce6be386decdb7f790d823569fa985493be0ab5c122a4490eb249b5abef836a6440db64b82cb478e2747da7694ddd7209c7810fcf214ad4a74bb1fe939d4d18fd846c604f65674bba2a32d4a04129b1f87f6dd734ec393dfaf717ba9f97e403363d7736d7dd4640ac4fba7c0ccf2ec2c81ead9326e3ddb7a943a0bb0288cd9dcdac90d11efb4e177b4bba2d27b6d4f415708961a6242b703696b73587eff434b7fa373be9f5f724a90aca72b4390ca8829478b40e54aa0c0bca18abf53f99acb17bc8009c3218f8fd28856c7cebf337a5033928201315652a7eeb88be8e72e59b582959b3d218b61a3416fe7d39e6e69c24a8f4538f512bdea90b45ad8361a341c6548936bc380e98e8a2f452bc5d2a1e2d60748dea2d155b0267a3c1e15c8682d08a601646bd9f32b4f7e37c35e5bfe8cda7b019b9037e9319944e1bbd12cf7ff62ddb100693d708f77ee7ad8e217d8bcdb21749ec41d15475602d06af27fdc50ce04a643d2411a9189b8a1b28cb4467c9b87de20362c820b567cb38dd8d49e57e9d8d2996b0be992096d604cf2e3c306667a97eb69d46b807466876ad96fa26cc3e3f7f7b2fb9c9339aaf00a85d6d1a6b7c98cb7bb621831216afeb009eb9cde0d5a447c6c797e4e3e0493121abf9ee60b0826a14bfc39d164e259c9a6137a133cc93549eb2511261540a23af6346e62275181240f35432c51d92fdef7a195e09eaada63496efac97af7d8d7c7fbccf89bdee203e1909411b28487d3294d8fe43121c54e12457abcefae9c087c1e561e3fc46389544a4e308f63f67847d6d49b20e3f1eb31cc1e23b9d4573324f4f0f6d8e58362109c917b90668fc6f26309f9575210f2111b2127334fa90290933a008be2db1ea73be36091dd406b34e9c27198cb486729d6b88a8da31d4735bda1412f8dd2b479ac882b4fa379736b1e9accce1c1dea27902944685554e1191811f70475497f26ddc7816395e388996b7f80279d651f836a68c9024a0983bb08f4309761ad01682ae2ba614b8683bf1ce9623f97516600d282d36db8d20f91b0940e1a4d6eb365c29824bb20ee6f2077f20f7e89f84304858c26d01ac57265cf3504467aa693081dd0c5fa9b557aa03a411c3eafc41b138a0c956ee4f188ae38c8816ffe2023a59d5c547165c3f2187ad35f845695a95a3b76374bb15ea26336bb40524c506d3a8e091405736f1b4f6f3ff7ce871071d7435711b78572e0524a9fec1dc469261e9cc54276a5466c3571fa14f6bbd1679d819dacd2db3fbc178e916dd07074b3fb23afcec126313973568d944fb152fe8d5d17760dcf0fa296af4f33eb4d676ade974e103192ce317c1aea447f165543be8ebc11179ccd222565ff03c2decf3a2725d62e1f0c529838d9a0bed5b1e79e9cf316a23b4b39c5f71110b41a568f45246d6393f70419725757a480319703f587707a1838754c22c258021605f19d8b8f39f171cf93caefc00d3cd249644959ed0f9af9408ab6d210ad5e543ba8a2c0777ce23f3b1898d0f20eaa8f7cd742dc46c0e91842963eb78ab9abfb972e36c9a3e58cd10a4ef9f96c3f88f4229b0ea1538e40f934c8a28ed66ad1423a1e01cc703686f8325823abc051cb364eb4cc192dfdc89bc8a6c365d1fe3f1efa61f2cd1f981802107d39fd912c234f056808a688eebad2002e85dfba2fb545b093c3a1284c15efc8d0d576d770b7a003eaab3683b77e2e8e8ba3811f85e1a560b41c59f1ac035090534284975bcb65a8fb2132f7ce6e02bf2ddf482fe4cd25611d60adae3fc78d8df4620b5ec14d3e07cdc0336da2b108154d91ac94c4678d0e23c1d399d32f73159f06137d6135fbd14b93788a2e1fb37266fadf75bff099a65da7231078b4c86f79994281578788a00902c215f467d7dc280e877cccc92f3378111fe64a46fbfcfb9ffa4eb5d2c8eebf3b646bbfe4f04feb2415eda1ee0a2590f81c6cf3b7e71d84c61cbe2341ce681d854cc68582b14df18bf789fb4eb175ecc1bc2305c6c076ac53bbb9ef6b531589dd6505b0ed2044af82e049bb04036875e93b90b5bacead9ab1ba79c69992d3c9804a8c7b9cd17a561a4c35fb106b160b8bcf0d5b9e4a872e0326ac41e636af0695d8f6a52ede33b312479a6876003e84ca706c677438172ec0f5c5f71eaca8e7b4ca0e3ee6ca6b51546b953effe36de0f6553a9cb20a9c2aaaa6d8252801f3efb884ff75c494ff40a9587bf0e9c177142a3b9c78044cd79370e8c4f8a21f26e72c70c8f9153adcc0ee988e2a0d3c985c7c0c3fd41cc589a52e36d8cf0ccc69602f3ad49f791f1381b29782dfc11d227771ce62b9cc468136be8eea633bc1b92e9c890541772b7a97de07c91c35c5100910ce52fbdd6900314a516b561c878ed1a222b890178c1a1d23525baf61aa14358aed1b695988ba955087ed67c55e5b672603b6399e8e2d5cf7247b065ac99d0ef64d441ab5c7c6cdaf2ca9f5e09a0410f2826056b1243b504798443d4916795395dca3d61d06fd0c72677f412b560560d8c0b870aa05e41fb43f4e79a4c59e2e50381be8e0cc42d95f564c9544fbe55ac43041da584e009dec7acd8cac89abc76971cbbdfbd858ee5d84f159087dfa00b44b6810d4bd1d119738bb8f00ee28cae74693b9e187a3b4cc9ad7442645992222dac213ccdba62a11171c95992f53e5048e30a9d18b74bcadbc1bca24906b62b08a9b0ba87cdd5ac1d8f6ad414a0ca6e6059456ad6909198cd020f06b0fc83963fa88cfd4c9fb01307f10e0837de74787db13b62fab28ea3021396f2f0787e2f5246dda4328c01f7cc2795541a79f02f971a40d89e68897a092487072eca84b46d251a510524b186b3c5e46d64a9bbee03fbb7df308613424c577dad82d9ad600720555b4d3b3629e2d4d7327ed0bb8e3d8e0470eb8666703cc42841de9191f2f6ab237d8471d028bcc76fc195400095e1b453c2a399c4599ad299497f2c96e172075916f40453300159a7d53a401908ae4c4bd14f1f21484a29afd9388cdadb8a75729ba110a89ed641ac02363fc533d59f1d31f242028e3b964d3395ff0400d294a53d9a95265e79c9a141ae695483bad43710dc15bc19a1139c08e17fbc3bbcfb29adc241414daf705230b8a592ae295225ab0a8c085b20181c8a4d0910c6752dd06b012a89728cf3564ea2fdf094fb8b289b2c61d93b504f5bb67c050fab294ef779d33e473d489ae462a0488c0bc0c8015afd5f707b7022359588547f5adea36c432542a9b29b3a52da93dff8264e58200bf9435d888e532adcbe000cb4e1af7e7c821bf53ac63e9e2728892747621f833c328ac884e4504e25a69da60c85428307709e38eefeb6e97023765955437acd6234c5db9d179fd28339154dc6be1cdce57c6e9109061c4a41cdd4d3f8355a40d87e63e5d0a52408fc07176b0d90e10a37473a258076d858120a77f8a4c59cca0182b475bb1f5573d463fae8ebe634e3e2c02d3c55983dde1100d13540f3dc31290bb31ecd2ceb1d8440730ccc8d9082bc7a7b1dcf441c0974b5811404c080a05122b0f1c8bd60f0094c2b6c41a6b1807a0cee304fe13a8dc6f8a90383805bd5fab9944d8178f9d9a8e743f68074560328ec8008182ad324a1cd4801ab844e0c4acf421c7120b013f9a1ac188643063e7a46081d5c6e8e61abd5a0daeb82975345bd959837375907dd1efb5bf955a57d3210f2c65688dc15321872aefac90733dd4a394b6de8008a082a50629cc343a77eff84ce76389e46dcf5cacc629dd4cb28fbfea41ff8553f7e481017c423c91e052d8f1c9833c93874e15223ab32eca3abe39292b34691203f87f3332f5fadd98876428b242b682305224cb43e438be0aeb6a43e59d7bc35bd6c6f1bd090b9cdb282e42ae0dced789384aa2d01dfcca3e1134fb09af7908a4f655492cd59800f4b319d627f2dc007717837654a985319fbe59063f8cc1a696ebddfc94f71d797332dc919a53e186471caf2ab80a4b1a1a534c23eddf6d9ebf2eede844eb809ba6391da3de765e41ba888ee68ec55e603dd554d4150ba54880903ef9a602512abc2ae1acb2ce81bd6cf983004cebdd15425734b987ba603593449a4d9e6c136593f2ca3f92692ae0a3a737798680255d264f3c5a9349204062298d59e7005ed49881eb7a8fdb225551910630b00993e8a2dbbdf98d5dd98f5127f36c25a03a9d38e83a8c0b6045b3aefb95b8fd87a80b84486e38d44a0752635734c6b50608220daef1cef4bc9a3c4ca66ea83475e79b1d367a1f05ea417000d8a97428bcde4758e3a21050500a3855a003330d44279eaf051753b6d823c57d3d62011ada10b144c8a1977181242b2cb5ccc92566ebbe8b36471283dc47239f0933b272354e1a818f903a4e7e663ac8b72a911da030a4fcf75cb641dbe8b7285dbc5b55fbec626432fab2f512d15dd99fd0f5fa6dca6f6610a4642fc4591cef01bb9dd3cd976d4092c4db8621776e9ceac44315f38ef006dd47580454c068546ae7a01ab5f9486a58b81eaad422ee97739db1bd02c97e6daef4c4a11f5b228382cab1adacecc4bc11d85e1a399d03f209ef056c37953429a70fb81192ac00ff0f24d40d282239e119d6c06cb14bb0eee758a341ebc4236593cc74e0a1e115d634e56b606c056049897b94afeb0b010eb3a53cdeebc0e08cf5765b111970f493b019432f94ffe7318266fa1adc147138385d52c137194f76f3fcbee4e160c691ba8f0734931a38800fab6ad12bbba14aa516fc03246682a85c095f1e24e51cccb9ca453a602cfb5b26df07796a8c7b1701f791da2011ee4c3b6a2650b266501d9ac389e300db9e19374319ad726a84f9ca885ddfea18e38cf18e6b306202ee1da226de0e9c38a507631125ddc76da756c86f14437cd7bab541514abee48d2868046ac3a3c1c28bd889aebc37e9420a8d59a64b635440ceb44cd19c7cb8bc47b0abcf30939085a2248e5a900d8bcae36c93d9db60fe1826a120ab81887faa9243069e65c4e7a1cb70ac245f714562448aab28c6d8ec9e17ed82675f00cef408094df71563ee1106f5132f55cb6e40cbee7cb3ee61b6171f5da1ea69a2ef7cb08ec70c7d78295578004ef0bb989107ae0c4746d36c2c6913d259393a7ed3b8c1f183119f5135a37791750a279c8c4ac46a7970958c631bbd3866d5d89d476f3964f375156de041987b68a08290834aa40c7b8a74efba82e416b0d653c9b0a034d8fc14881480d61a9fbeeca975831c345346475315395d3a0c8e2bfef4c2bfeb1512390d0fb7a80e7adbeb2904b20d2f64f89102e81749b80491a1ecdd0f1e46042dc23de92a80b0ac8bb4ec6bb11c1ab20c086ab5b4e7036f282b65028749c6915c6bb778811fbeb9295d5e17c24b94d8f32a14ce251456db1c0a865c52b8a6db97e70ee76b2eafd42cabf3fbf3dc30802cf75335b100151cc14dc0cbb97f26a05b24b40033027d8b17d1d088dccc4cc62642a7605d3dc87a9ea9dfd23b276e96a60fcd73ddaca3861f77a85bf4447b25c7d94752468ab2f23b9871e3044c826680d5abc3b4fc7d72019ced1881b61b4e5cd2123bd7fe384409e2dd99ded6f6b6ff6a141c7704773c03cf017130e3b7db3df62487024ab02d3d683ca1ba6b865a9f4368d6e52cf63e7d1fa0372ef6f9655499b227a7414f0ca3288612562450eadb4b5f73ba1e265796183113d2129a9f5186b2e12a1add7c94414a39a0208046b6022657ea9bcf666d20119b086dc4cbabaf53c9d24aa2fd0855b0157f6ff036f359505a4920187c472e284e2568db416d08a32ba4411f0c2a0d6c06530665060dd06019e76d4291c8db544c1c47027ab3c7953870b0a490033ccff33ccff33ccff33ccff3a4c3f82cfba2012ddd95528a4db68fe4787b654acacc94767bdbc079d6064ad6064ad6a2972c173709e90906096fe0c326d323471ee89987165e7091812e8c1714d0d098e105175f94175c7ce106b6534ef5c12aa859693092944ce9a21a6d604d2cb8a7450e11181c34d8c0a69021c71392639cd4a0b106bee3fc8e27a923a93466a0a80d1a6a60eabb3dd6b81e4f48510d0d4368a4818d93a38f8310a9e921ed002cb4c0a2015868810503b0d0028b056881850282a08106c6cf53a447f5719c7f730626c70fd94166df0c7c6aba8a761fe4fbc82e031329a525c494d52003935352ccf7749ec12391a03106cea326777f485911aa1134c46059842af15055c2d4ac2255cacd62843c41230cfc5a8ced71a74ad9dd6980818f2d66dfbb0857e30bbc57cce97b3babc5cf16687881cb39770e0f935b5dca7581cf7bbbd61c6a25cb6a704135b6c0648ad261ed66010d2d1440230ba581056e23e55b09d3c8a0710576928795be9f1558bdf590277fd4dfe4abc05fce1d586a123bcb9c0adc45d85360b3f92611894b810d096bcd9fa28792de28f039cc2a2cacda4e5b28701d713aac723c674f9fc07814f247e6b40fd45227709bde92fb2c47d13f4713b89052eccf2815426b90099c9f5fc668ab9cb20397c0789c2c91337d68a239257015e3aa98c476d1ac2581cbbe9f36b790c06ef4b05c9a5287f67df162bd660f6254b402533ed03002df31460e42ac4e4d993f81171ed0280223212de264ae602499e782043370031a44e03aaaa895e81c4a8e1c20d0180253961a429d94da4b15070d217063f91266fa0902d36b19b1376288521d2030b93b0e2d6a073d99343f60cf527f9e6ce271a5dd077ac78de91e7039aed4ae46b7acee20b325692fdbfe76c0474cf3131ff1d8a3e88049dd2959c8154cabb31c301e07cfaf29830356efa3f4216add80d58fa38af162fa38ac0919346cc0791c4808cf8e7f739c1ab041cc3d4ab569c077f6facd0f351d3e03b2a48f21dd960167395a58e8e6a89cc6800fdf835a87390d18301243446b115fc187fca11fc4ebb882cd4c3e2989755ac18a7a9c232973c7c80c2bc87457992fb7ba23cb2ab868d93e2a91510513246285942226154c5eda8fc33899f5f247051fe5ab3009effe08fd29d8d50f5d93987e4c216f0a7e3c2da492a6145c966c63712f6a5a4f0a2ee6b4a6a0e3be9a8f82c91dae1ba17151b0ed5178fee4f9f1a91e0abe3b4ae594afbca27450b079ee152d1dc729e4f04ff0d51bc72b6bee09deb5725b8365bae674825bebcb99030fb2959c60fb737bbcf3d1eecc2618ebeff8d2de45cfa109fe53c8d1744727afcf04275231bade8b09ce5f6332ddcfaa8a964b70191a52e2c7d3bd61b144eddbc927755482cfa167caea37091d4ab09fa9c3f1c9ae14b23209734eabe017edd62209c6efd6362ce47ec44482dbe49fb14a8704b9b732f75e86b6d21ec17e18729cdd2e7a67ba1dc146cccf412a9fa021ec4694cb03db3082cbb9357e54ef4bdb16c1a7e8bb2966f2b073d58ae0529969f568bd552611ec8464493a36bd8f5310c146aa37dd1c07a631b287e0aeec33846e0825a64f885721d89a541ea79a7d9021041fc79acf23c7ab299641f061ff5175fcf45d1541709f226f8e730776b90e049b5e242b8ab9f77800c28b1eff7ba8903bceffc0ea76da6f0f2f62877e60cd7255faf4d79a26da073e8a389aee23ca07c632794e31276b857e0f4cd77a1c5c4b3db09731628c7439b47f79e0d424ef8744120fe46fbefe0e7cea28e8e6702547530f3b70ebe1244bbf60d3c1a30e4c59e705f12cd281f1524ff9d133376a9c03e71de640cbbb445244b453485aaf9b348d033b113dbbc34ba96c85036f9562471dfb062e749821ba7854896ee062ce1135d7f48510b6810f2c6c22a2a71c438e0dfcf988f649c8ad8135ddd448193570a6db21b35a2cc24f0363532639da4c13394703ab99d3e4ffe50c7cb486142d5accc05df6b1fc8fcf2b65cbc07ecedb710739b41e910c5ca6945bc8098d971b03bf9252c8f6415f6a4f0cfcdf5977af785c71b230f0d92dc5ac223130f071d420751e4fa5b37c810b413fdac3d60b7cc6cca9c374163c8e76818ff2a6a5df8f3a8a8e0b4c87529ee3fa6d0b59effd65ebd8a6ed43165ae052a5fedbe4120d992c302107962262d6ebcb63814bf13abb459fdecd15184d592dfee6a59da4151889e943ad60f5517b5015f8968ec96d3799b9870adc6578dc2b8931477f0adc9dfe6aaccbdf624a81bff4f1a7f40995d19128b0b1a3a421fae4654879220b2870b93ab61452d6b37842b9bb52fe68cb7702d37963ca802c9ac0fa76fa7f4e31223aca043ed2a94e6fcd8a1d57592c815d095539deecc98ea612d8e492dd512445fdaf64649104ce76d52e4eec0f621324b0f93bb847c8e208dc5f55be4c6963842c8cc0df079ec314bf3645acad0a5914814dad51ee5ad61e1137296441042684fc5635d92395854360bb628a9cd683c7e88a10988c979d25a7b3eeffb20802539792393615089c76b9b9bba959fc80fd1ca68ff4e69162c7b3f0011f5d4e6cbd98b42fdd59f4808978c1a7efdd93651546d217db45163c602bcd3a6d6cff20f20641163be0ce52c78ab9b7534ae21647163ae03fafb62e65bde6f08491e4458ccc22074ce69f0ecfe3d8573d471638e07fdf626d86e4a5d11059dc808f7b16d473678a13a92c6c4054b4ffa05248481259d480d33ad7cecf9b43c762b5210b1af016a2071d07dd6731033e3c560bfe6927642103366acce1eaed7928a1958108862c62c0c791985bee847538b1222d64010377daacd202015ec1c7ef1f378b672a893e1908e00a2e7614c7243b8e6c528700ad209a076a79a304600513a1afd3add722301700030156c1c791d346adcd174d827d0201547179fb985ac81f74ba54b89b5a7cd3dfc355543c659ff2f8e7b39c428f5323e8e6ca080f9ac2fac83b996847da4b81c4dfeb0e2d52fcd61d27744671d03595923253145b08939cfc21e9070142f178243629766619014041d6209635887ef009f08973a8f92dfb3a9c009ee036af064d1fe49de04bafe3d4714e1781009ce0573d2f63ca994db0eeae5193c55f139c87a196c2c31c9607954cf0ad1695d42b68ed07135c8885b4a9332774d825f80f4d3c94456d096e3cfac8fe104d21a42bc17ef697d841524ab04943529446ec8bef31093e8e6e24244991041f4b08e581e46f971c4682f1142121da7290e0f5aea3b60949bb9c47b06192c3d3e28ee06388901ed75f62d01bc1c53efdd46d31fde3388ce03a8ace91379245709aff93dfa733d18d5104e7ad5f153dff89e0adae537e88b839fa23822f0d2179ff73dc9bfe0ad87d18b3638bc101ebf1df927f9c2db676033e69645d9e728d48d980094956398888c888718e08901af0ed71d8dd92e6620b2206101a64e5716c9e69191625a1d5313deccae36e4180cc80499f4b721ca88664c1560191019772b6e4a569315cc8c8c01724067c90f437d2f6ff91ad5d1018b0c1637806cb1c09621819f90ac42f4ef96f7eae735dc16d4a6f3ac61614d868c5e9b96e4368c998333092cc062bf86076b92ba5420a9d55e829aec65455f52055b091b276f49f1453f79f0a267ddecd96089a72082a38f33c0d9fca39051ff7b5d84a889c2998643b9e9fd293869c2635818d52f021eb7778e8231ef331ba40076c90828d34c9f996a38de9dd517029512f744ac1df1005d7977ddd1ff6c12d9711630bd3051724e8828b6bc0462898289b7897d5eb3fca4b1043c689c0023640c1c590a31ee975ccf11a01116c7c828df61d42c5ab2bf4054692118162910106601181126c7882c9d17bc4f3e9956fbbd1093e4aee61fbfd6f7082cb083fcfe9b1a224cf26f85ebfff2855721869a309d6735a0d2d95c17dcf04bf6d6621c92d5f678f09d63b2484e46b2fc14d8476983d5c2dc1c6ae8c394f5909fefe2a5fc78e1f2c623092483728416563122b898d48f05148d1723c966024998fd1c5160686b76b6820ba610312b8f108b6628896db6fa37d593547602945f4844a6321193166cc30ee2798a1a131c388806670a1c5db09b0c0a2d409ba58c17fd1245880864617e68b2f9a0487d00074b0d1083eae643ee9283bdad812206c30829fb2f47bafd7d7546e2c828ddf1b35f95fb08c16860b1919204590462298d4396bf2b8330779d31b8860438e5d24f190ad51da3804a2eb3975903bd61c2ed830041da5f56eb2bbacaf22700209244001860b191950da426c1082c996a349fe21e6d0df0c82d194bc7c697cf7c34bdd862098143f3454dade3f991b8160c34ae2f925cf6a4152850d40307ac9a34aabac9bbc0346d2a1a131e30b2e62880001303c86176cbaa0400962c858c005343434344cb923003dd8f80357514435e74c1da60fb561c30f6cafb77e678bc8d63f8ca453c1461fb2db8e4c91c2879154ac88a560830f4ceac89124ff405fa7930058c4c61e38f58faeb4fe96f4b9406ce881dbf8bd1da5102500017bd8c803174347297ee8f883d537026f1919d8c00397a33a24fb24a13a856670617407267f684dbf8bed512723c6162da3bd283b3092d7e369cad9e6d92d62fc151836eac087cad19ee39a426a195fc4882163b7b04107be434b478e6adbc717f35bc478abc2c61cb8ced6105457425e955fcce04204b421073e694fb68f42a4b011073e45a5f562a59aeabc1e62030eac77b01aefa8a284d878036f593f26e96e491e39dcc0a97e5a4d9bd8461b180b6a6945731cdb6003a31a39954f471fb6ea83d85803d359faaa3a871e5954037b2bf55d1ef77f760937d2c07674c83144f3f2b081065e542be4be1472f0c8fc62e30c4cb5875e8b7d8e60c30cdc2531b78b0ab96c7306067d9b06a4c04ca00b0a6868cc681965e0fdd38bbd78a051e22336c8904f7eff30cd0e46d23962630c4ceee817f3aa7d65de328c08ba8b2e810c456c8881296bb728355922453f0c7cac13928859469dfcda1536c0c06e8966ccbdee9672c70a1b5f603d98d64ef6d3f1b47a81b1249dfa2adb326c74818bb8314ae68e5f3a54480c1b5ce0e3dde4c7efd4ebfbd8021f49f5eae724f639da68815fbfb41b3c859fda071936b2c0b6684e08416c3c7f1e0b7cd83aeec9450fc3c61558577fd1f4493bec5f34dfb0025777a9a7c9537288b0031b556033bbc37c1d5a46c60b36a8c05a08b1ddc94f818f42b0c82147bdffcd772105265d787e60daaaf98e023be671981a3fac1834bd0105462f871d78dae6474fe0fbf2e8fe7a188ca4de1c6c3881cb1ee7acfaca8191d404be3b9eb416cb41dd5bc478acc10613b8cd4c192c7ecc3cd571022f96c04b7a45d718d3836cb0a104eecc43750f546b435ebe3022a0dac14612f81cb9474f9a238b1f76c048da220612f8e81fa7183bfb47963730928c8ec055e4f31c2bfbe7a7b8451731340318e8820216803a6c1881cdb4cbea619c72061b4560f5ed5f2d5b4cf6f16b5c8b2d627c31430b0d8d2fcc8c197d28e30b2f0410870d22d4c610f8da2f8fbcdae2a8df10026731599846d9871add185ecc3032b2021b41b00d20f0dbb5298690efc2cc38096cfc806fdd501d45b51b3eb08d1ef0fd1e065df5305275a3801b2f3090010c6cf080d31c6991e57e606307ec454afb30de8729c109669413cc381d3039f4a3686a5357d2c148ca015b1f35a6e6f8fa5b53b888f1050e38a910e36be7899e2b3732086560e30666fecbc6f0628b2fba7ac3065cde5ef39c4306234941b05103c6247d7986fca17b98ad0d6cd0804b131dbafd7e42c81e5e703182193360dfe31cbb674b8191340263bc2094a5810d193071f2887bbeea70425e0f8f3cf2db850d18b039fe4bfd6ffd5141e315bc4e30fb4074a2ccc32968b88273ed8e3efcd257ed38165a606158c00506ba0250d068059723092b984e116f246eada0d05805275a9ff93a97879f3fa85ca1a10a76d27eeab84bea643434283552c176349ebfca3b54f01a31dd3354d0dcf147151aa76062448a6ec922962b33858629d8e8d0157d9b552c2224550a8d52b09f7353edd5be7d55aaa0410a3eceabb6927112068d5130593a0afdc1c3cc9e155170962468e84e87826d5d5f41c1861cc749894c7e82cffb358ff891c4c9be274866d92ab9e77acba14e706d213d457ffa40a5c2000d4e70b7e6414f4c2e6ab1b3096e42ec4b9df36d57520d4d18a7f2a69d0ed7c8441ed579420e8385164a1a98e0325f740f22728d4bf0913eb29af4b91c342c711a95300d4a70a3c1a2eb473c34ed4930923f858ee5a995b205237325c1ee4e7425df5c2e4023124c67d6bde0f1fb5d2630338a14011a90e042878bb507d6713a341ec1abfe4ba88e1dc7070d47f0c9722b25af1ac125df646693c5ef427730828fda3395e7dc3516c1b9a4b68bdbd1b1050d45301d7dfa452d7920f52a163412c17bde143c79fc89ed1a22b88fcf3dfc737308f683eed036ba87189ad7828621b8b158ed13b35d084e35a87f68e9d42004eb6ea6fa91466f4e798d41f0b13f487973491a82e03b538721e528072349045d8c8c2d62bc462018b7bbcd10530523493140072a3400c1afa70e3f8f04a9b8e11f98183ce8eca8530cfb9c1f98ced9e17d347a9192260a8d3ef039b42fb57d6021b80ac0420b2c52a1c1073e8c75da6194eae8b2c3038d3d705e672144bdbcba21898516584c008b0c44008b2668e8811bcd0ee3c721c13b8422e8820b181990612aa00fd0c80353639ebace727a477ed185713c709a3447b5531309b8281a1a8434eec076cc62a17f2f2a40c30e7c94a45e8277e7f4c84d83461d986831666e32cf1ea544830e6c7ecb71f54b688b188f7534e6c087ffc9c30f91aaa22107de63c8f9d284783abb68c481abcaecffa3954d42a40107264344a8dc4b5b12aa12f8060c09d664154612161960c020a8126cf4bdb5865676db4705a204f7217f98cb352f3387c3487aa3059a047b75925325b7c8b106072409f6aa6af3e5b7782221e76246978a0413b37d64676223081276e5bafa047a04db9f73cc1cb2b3725439c8116c889683643a21c4185ac3f0620b3a1ac185d0932ea9dc27530c122398e415762be9032d826fbdeb8fe95fb254ae0826c5bbaefdcd6c1828115ca4a4aa9fd2e6204508768110c185a60a39ba2479274e0ec1daa4785b52d519e961000a6408ce7b729c3ca72e8186c6175d981210d24230f16a34b7ba49081a4308de2f43c5f0ca6fab318106c15bece8125abb20f8f036bd05cd716416b740705f1ff880e053a5b479b40dfd81cb51a7ee5845223ff0d9eda3607dc93b2f657ea03e709239d99f239b0f4c5d4cb17e4b5afae3a03db01772b2f58f1ca407ae72182edab107e581cda17b300f2285d402080f6cee9876561e861c7e312bd01db8ca5b49b73e7790a3253bf029c68c2b96592dd581d1aeb28a5739dacbb61733b6aeb8203af01fed25b47b8ce6c06777e4f074729c8cbdd101480ea76039b6e514d59213e3f5021407d65306530d562e412b2203101cb86f0dfe1766bf81e91ced9dc8b46d1b9303b9810f1a1e8767394a09b8c800a281dac07de4da3142f0fff8fe19880dbc4884ecc177fa3fce5903efe6e52979583de5ab813fcd2d5e177214d5e3d3c07e89dda4e6942c391af8149196733b3d036fa5ab1fa9c6cdc0b47510b5b1d6831ccb32f0a271cdc6373742f420031753de38064e6fdbb5dc72ca8f1103a31bc93c92328d395461e043b87706d3af541e0303e337f1356bb06a0dd12fb06e1e6e46e51615cf626980bcc074ea252f0fd9247fb60b7cce22116d4d4223ab051c077ce12de30b15307181affc93317424a212fb2ad01698489f6b3d8ac8a142ad054e22e6f4e039d87fa859e0a7fcfb5643720b9fec17080b9c89667eaacc0f335957e0cfb4828690e3c72d215e202b3051272572471d35e7982af049bba773da1646125181cbde6ead8af9030a0b05b5059a02921498fab795b6caaa19341405367e8eac82d87dfa8d5619080a7cd414636e36c9a178f813168f71dc63eedc09dce6ce95721cd216b3a509ac0756bb1dc5e49e9d66026fa11ea1a72d2fba5c02af2e759bc3e2a8888712b8acd192a54bfb417d30097cec2ab669dbf2a78b04ae23e5cfc89bd53e6947e0525aba8cc00689a9515a5d3af668085404d6a3dcf73ac4cc39bd0cf385037c4044e072730739c412dda0608b2fb82809041a021b56b7f93b640fe347086cfcfad461c8b105cf05814f73cdd9de464b420c084cc89105bf8cff807f29ed8e7208d29d2c27900ff87409e96f17af3fd41ef0a92dd94fc3ab343d78c0e4b11ce610a364c60a0c0476c0c792f387398ef17733ae03de827ac7ec38ce1cb39203ae925d264fd36d173f1cf0d17244b3c73b594d764037e0f2fdc4f420741bb0796e69a123bd065c879419c13a9418834a03366a8e83befcb1860e429a01933bf6a88398b70cb8b0c9ea75d2bd69121728068c5f8ca9a3068b60c04a6df8c7397ccf71947b055f9635788e95524a7063405cc1fe7938213fe87a5dddc8c84002b8405ac17ee84a3a799233b82801ea80b082cbd2db6dd7b166f1cf2a989c2ab7f3a474dc9d57051fc46334a95495c8a4e2a83aeeb8e1b7a382dbe89837a7e3b8ae7e0a264ea5d8595542cab74d71f428c64f9363e6a458a5d8df6a2ca60c0984142403328a14053b62ee5639aba1e0fa92e5a0627c0b21a8a0c04a2bd7231b8fbe136d817c82eb94932677f38f90739e6082bdbe670e2b27dbad137cd6e6987a226a24ad38c1a5a9f6ffbca14db02946f1a0593139209a609358cee9e328472f9a93094ea53dbb4287470b3d269814db2ee468f51492e5126cf20fd2c7faee761e4bf0e1992da587a13287e94ab01235c3f52c071d7728c1c5d7ab099d4e02c97b1f8bebb892e03f6709e9307987461b093e489734b84814127c9ea85c5fc9adf77d04dbc1a33a67241dc17e9aac93af2467df4723f8d05b438accf04df160045d7b9a3d658b242ff0880359046b39c7328f2afc3f3a456c193950130f42f542b25d748a0824117c99e5eeefa86fba1722f88f3eced1a3b01c82bbfedc5dff5183e98660cc72d0da91466a400ac158471d897bab79b81e42f015e92445c871f4d39d4130de933427858e20b809d2765eff397a318160bc2ff4ff23ad50391e10bc76aca123ddfa07a6a53692546b5e44eb07263a8f78fccf999acf3e309ed9396d47aa18c4e4039b37e53aa6e71ef83843578cb13a3bca0f3d301eeba6ea0b1d218f471ed849269a25d4e5403ac403f781a5fc81475f9b72eec04e6ad4e02731a5aed881d1e4f133796a75e03b7a6cef2a9bb887d1a1adcab55f97b239701e927b11c39388587260d7e2fe44d0a4515ac5819b4a697375648eec8703d3e935dab93624fbdfc0a6bb1b49aef71915377096e6faa1a36e8eb00dec65ad5d30fb78c5233630fea1c774712f3bea680dac4844889ac34c1b2f6ae0554f72693a0d7c1029f1930e926934f0b17f7965ab8eea2242c5017206beac2b34eaa57c621f33b0121e976fbca9bc8d2903179a6df3c67ffba41b32f015bce2a4a4f2d8ca31b0aef7c1f72c96e4efc5c077f4d921e5ab680ef930b09725dd67c941ce610e18d81c126a1525f4c4f205be25236fb0885ebf8b171849fa696b24ba3357bac088e85b9547e5d3172eb09d5f42ccb14ee4b4f616981c62de1d0b397248b616d8c98db9fd773d472959e0bffa72faa7edf68ec502933bf2cde3df16438ebd029f636c6d8b1ebd3b45adc0c4c89e9022744871d32ab0b9d62cc7202515989ceb63c77cb5be2da7c0e7649a28214b29b0135288f49fe6fec8230a5c656e59bda79f7e0714d8ddf0e491997da80f9fc06ece3b81b7adb5e4bf9a3eca36812fcfc9a388fc69553d26b0266da9b652fc8fc55b02d3aa91d1b6b3d332a6042e48ca1e25f645f0cc92c087e3351d7aa890c08e4fe445e53c393a7504ce34b354f6d83202a31e58b97b300bf5a822b07ebd390c75eaafc1220297a472c6b897a287590d81adee5bcb9da5d73f0981730b8fd4427db25928088c7e106d2b59fee86204043e4e1aed393c2e55fd077ca6b10f35afae07711f30b9625afd383d6072fe9843ce69924fcb033ecc3de9a963cd79e70ed8dc716488e0b16f3a74c07db4dc71d23f55793960da0607b64347a6799fd717ec0d5ca5ff2c792cd8a769e6067e2f072155f5a598279583b581b5bb6c9303b3c869b6b1810f7e53133c5ef6a8ec05d6c07a4e79299ac5ba4d7dcbe80aa881bdbd0a71cbbcd353c6d2c0e49c223937672f4fd5d0c0a598b356b2beec0c7c87f6b1e3bf471d3f970033031bf3669a1ce68c905919d81c76f83132701ee24f6d5fec487fc2011b031f420af983fcf3906b17836252e90b037ffb418cf5b137c7d08161898ec3ae96d8914a8479b7bde7d0edfa025b9f3be5fc0d9a17d8b8db39af8354b7a176812dc979a382778e23ebb9c09e6b8e830a8fb242dab7c08aaef956eeb5a7e85ae02a8b5ffc30ef9ae64b1658899f7ba3c79f1823050bfc068f2f5aa8abd6745f818b8ec2229d8715f820dad2255e8c91c4a30adcc76158b6caa151819da01d2af2274deb9f4d814b165cc5f753f6d4a40093029ba274e728d6e4ef2c11c1a2c0586a53c99f1f94752814f8e0977773ca1e95c6d69ec06659d84ebc8934c1e2043ec8f464c9bb530e6d6b0237c163ce0bdd3381cd219dc51cfaa310f55802aff539d0948e94709c6e1eb8b6c761b6aaa7b687b4392f2b596e49602c7c2cf6a651bd5aaf6048e03ed774e4df7158d6e4da11d88a1d37c724ba29416246607220ea21fa9ddb33e5820b9fd15bc4f8a288ad08aca587794fdad3c7d685c188c0c7904aa3ff524ef13a8d86c08418cf624e71eb367721f096f91bb2050b029bba2985e5282d32994060e2a9ea4dc574efb0c37ec0c41cfde65297b0201df3017fe741e6ba18faa32911c1603d60739c25e4cfb7798387f180eb6439f2b55def38d11ad80eb8eb69e98fba9ce9804f59fd63771ded1c8e96838389e520451cecd7c943f4ec0dc89ce3b4cb91794808cc0649cc9167e7f0bc950f580d923a499390d180148b6ce95a216d1ea61d48bcb475761de26fc666c064aacef1338b5b45330726032e7fc7fcfeb8bfd67532580cd8fce19feb8726e56f210603ce83b879b173cc4835d42b78c9da19252f347baca4a15cc19b65dab1542e7aa7562bd8c9da71dc6aabf2f68a156cecb842d417d70b2ffabe50abe03d332333cb5f2f942af8514ff9514bb22a15acfdefa60e721433df4705531562c4b4ea9d72903c05bbf79a63a48ba66033a3693291d49d1fa44ac176e569d9644b396287144c7ec5b5d3eab6942ea360ad2648485fab28b820e26a7e7b71a042c1dfa44eb12daee8550e144c48e93fa305c9270889e9418e52cb924ccd16735ad66ad7137c94e891abdf5483ea04af5162dc4f95e6445e9deb2e44886d826bed382457ee9ee690d2043b31e40e428e9d4cf0df313305c9794cb011ed3b7d56e219f14bf0e18636dd8e2b5156b104a341f3a4abd7a7d46825d8942ce657d987f92b4a7076ed418aaaddac21a7a58f82052fc98824b80a31dc3222ad365b1509d672189da34f6dd341c78d0a1812fc4753176e1a4f53c247f0215aa76095d287acb412ca11ec4796f6524a5289508de07245cbcbd17a18c15b9288d28cf2e4a1bd50a845301de88be998ba7b7b10a008de62da5823798301265422f8fb4d6d1b4c63f2c85a150a11bc7d65e6d68b9f9b3653a843f06186851ceb86e063f4e8641dd5b35db9842a04b711f726ca2b6aaa1c02b408c14dfeb753cf4a6a1fba46a84130adc9235279a0174345105c48880e3faa0f2a76f431840a04973a6ed671be0aad1023238616422840f0fb6184accbfcd184fa03133b6f75ea4829b11541175c7421022e662061e00b6f2f4a10438be302e507aef7b6849134a31e507de03fe67a8c929572947580e2039736453f77f6e64c933d3031c5cdca8f2b3df01f4a76e532b1913611055579e0c2ca7a2ae55af9a7ef048507c6d5e385d0c922e409cda83b70531d87ea1511428d47d981bd4e698face3a896993a30e225da1b6a752ca7d081f1f03c5448e6e8ddef39f01322ddb56e85b9bb72e0fb27783a49691cf8bb24314a6a120ebc6fd20e6348d91b188f7eef501a5ab3a3dcc04f587ae574543127d5063e568485b0c9d2dc101bd8095937eb725a0317377f6cb1c2ed63aa06b6eec3fda41e5140a581f11cfb77544e9e9a0f834203f7a75521647eca95fa0ca4680df138f2f4282a06dfcfedffc9393330d522551f5e249d94920d55063eebdf674e9af227cd2d188a0cace6797abe5bacd059d518d82c2152a24555db5062602c3f88cc6d37b679f9360d20541506de2ee4cb61473e59db0203dbaf2a39fbdaf5055e720e4c63d4aabcc06a6ad4ec944aed3d2709d505363f0e9359b63c8c242e70bb9373ce1eba72ca879194b9053e9ee6b1cc315ad9335280a487d2021f7ac77bbfdca134ef2cf016573a77c7103554120b5cfec0cd74732821abe40aac787e475449b963cfb1023f1e27b65bfa9c737daac0874c3142b742052ef57853e03be838ceee289202372a2a11ea3a6349260aec5bd4f18b1c50e0d6b3c34deb717a079fc095c7af129af73fcc9cc0648bd1722cbf31957413f870a5e28771254877cb04ceed235967bd943dca1278cdb5b1e3f84395c04fca1572591493c07eb0315f0b93c89203097cf4b1f27d7497e33875476072afeebf685999a78cc0e43046f309a12a027ff13f56b43411813b314f579ac78d410d010f72acba3e42a83c0e839084ec1b53ee5d0723e90433b6b02b1410f81a9f56c93942b444eb076c44f2acedee0fb5bfb7987122e8e28bc6005941f9808f73aa8a1f73f4caabea81f93bbdf54716e701272ed2ab1ded0ef8b83a74b490a00eb89decb01c709b135df92f5bff3738607af2fb454f6c3cfc1bf0a12db6e40eda80b5d35d8fea9e9bbb5203c637b333feda6e7e2c0db8dc0dd123de6f6466c0c4a90e22cc3c6bc8910117b77348ffd1c52f47c6809198c226060b96275a30606a3bbca81b8f5770992a738578314db6cf154ce5edb8f3a51b9b94b4828befa669f2f346318d15844a40ab2055f0bf21e27d5c16a2a40211c042c6175e3880527118205454a1531899828f5e42f03e099ab9a314dca5be3deff28c2958a4e0442c04fb48fba21a8c028d828f6e9ed365845c59d287a440a2606bbdbfeed7fa0385824ffd29e8c79d452b3c8e0f040a3e3bf26065962aa4f43f419e6042d4d66ecf39ecc5dd037582eb919cd27bfb69cc4138c176e7d4c72aa14db0b1d3efdb875c2a129126584b1b528ad95fa44cb03f962247ff1f3d5b5a036182af5429657fbebda04bb0ef933b92c3bce3e593257893ce928bd0a8a45085f27824100904e2601c46414c721f73130800182c288d46428168248e747d1e140004522c1a3c342a1a1e2616101812140f44016128140c8602c13020140c0343c1b0404020eaa07cf2cdb1e36203f813c3de3996eb261d7ba03988f65daba5415ac7309a1cbbb53e1ccf24af984892e28bd2bd81a83c43302efb50865e1eb751907b48fba9e3ccbf0c1fec0cedd9d0ea3054529cf2272947349fa71ecfa108761e8529f6ebf5913097f7f1b444a1d8ac5847961408c3650bcc215cf4de721271cbdbe93a5bc45fc6bcc99b9bbb01e190ac368f6e7f6cab7143178dcf8da5346537719bb90264e98677dbcc8989fab2b497185428977fdcbcc88d178cc54921f2ce43419817aec350de4b6d6b639e4f49a4de4ac901bc4b67b62223cfc758a4aa373646cccd3f60a310dd8d5b141b73910921c45a8ace8f152ff4e396a3469228f46dc289b8dc1559aa9a29a413b4a394d50e98599d3e67d91039a5b80baddcf86c19a1825be8b4b2a4341b18859a913bee7a0bc82435eb4719a1710873a86ce199adc9792669ad9c035d5cc9019c0eb73eb95850d899e4d7123e81aadb6e9967016a91fbaa14e677602a06f05ee5e7dd75be5ab12b6a1b9fd76ce6184d4dc6acb1503b41754d661ad73b9af2965b00efc9d71f4a1913b3f065d9bdc77cd6c651dc5b3a146288d0698f8c7748ec4eaf68e7c0af30f71a97f02903231d93170b9f212f2477f87c9f017fce5e2293d1e264062e1b5664a62d63dcdb42d97eda47d44fe32ae8408130c866779cd82a46e32b1420722f62234775666d2ed0647e405614db73d0541f0a5f4c273f71f34407eb0e4046570629aa912ce4321f29a10b6121a86790d1215b4cd538542fb3255a76e4ab35f5273f052e1383222464eb435e407d0f88e90988a2ac0a65cec823f398592cdaddb2055ef7c21268fdcdef345257f112977fa513314faac5dd27636fda71a142d9e9409e54b04ea3c9a6b3231d8ad7a4a9c6ed3f1e672cc05422274df5e1fbd5da0e5c16978bdf5ab0715b11b58d2a3d41d20d3f3ac3b9d191ce62889065d22ce1ba3ce8e63af6a3be36d5ac6702c84b675f4ba56a77690fef264cb038cb9374d92908b0d5a1305048c5a28fc0689b744af10385dded6cefdef0c17455f5a45b351fe6a2df7ecfcdc0f831079e40e9433b69e7661a23dbd0fca9df94caa9a4b72f2d63ed343ad702ae261f6b2a10c69e322961f487fbf815553cc289ea51d441b67571a9b87201d869e7a823d8acdea099a00aafd8b40d925604c99794576d5e1e7483ea7dd07f93114ee0beb189164433d3930151b82fc9dd428397816d6c8fa39262399e2457b5cbd98b1bcb6ecfacc3423695d5cbc7570ad4a4b933967f8421cd53328d1ff908420e3c32dca474a8b432b6ad59d8e4d0600ad5e009e1d080c2c6cc5c5e399f3789f26df418c0c3263cd5bcf1f7acfe8d08a2f00e9ea5a35212f38227d1cea24f511f14a0440a0356ced3b2ea2c09ad4084c0e70d55ec642ba70edc676af39deb1f7868891de5c68d1e55f79d84b0fc1413cab8581a8f13ef8f16476d4145ef5636e31aea6cec40434b9bb331ed941bf04dcec8613868eac753c316b0db302918665808e6e1959533dc9e0a51fa0a6456a58a4a4fc5c6e70c9cd7e345eb819e113068e8d97d04a71c21d7e1d6eca26ea7de34e4672e929007b80ec184ac447668a4236c94afd068a6690ca66eceba90e62519af45bb513a4bd7f1c8e0632c8063c0f9138d8d111acc94e2bd4c06db4dcafbd844480c1f89e18b2bb70b8f1178f870b82a1099f0a3fa3b082e80af977b4c914d0d511e99442c3dcb14fb30f64d9119d852f8e8a9923834663082864ff4bfd8a38063d9dd2cd06cad1a050d7432d8b1558343059d99014d66e40b4795d54fabd8faafe7fd138ae98ff1b62bad29de7f5e2cb0dbae3ec5559e1b470cd5312ea59dcc331af14815127a397eb5641e36f4d38e441151ca2b458fb657e16629d0f4663eada38fffd37d144049d8f417a1bd157139c9c5b1aaf03f53a899575f029218e3b696911e0697ee9a58c335089e3e6a923f2a26a8805cf6daf014ea2672a234aa8b325855aa1ef4e274637ad7483643efbbe2dac0fdc1d02390e62a1acc238e83da6589b02fb1a0d3487563314970c1776efa4b271192fca7ce2e8791024673db82223765a51313f0a918f91a51a77706704b51931006d140fdc744c3262b00c1209eaafca20851b54778daee61cc88461ee872963cba644c537304502f80bc8c301a60a7af9a331c3c63765864ae0824740bade94452d706fe60bc3ead9bf595617812fd99b66f570ec37f62b8cd6885ee70e6cd09a42d54bda930ff9f765a0fb5398baf1aa92b00f63fd011c3b1374d618e64bfbf297692fcc29325ada3215968bea2ff591aa1d347afacc6da39238a15897490371125e40051ea7c524718062f0f02abb97a24fb923c818b1cdd865f22b2ea0b4fa36d1a823722b2f722e0acaad0e03236048216526a777e6c994db330e4610e8613a9b1c2e35e22f9414e0a536c44c860f03df8999021aa5ea8374c7c198392074a085e1dc9b55e1174ef144d8e06225c14dda309db2014591291cdc2240f06854ca5c955651d2c42f0392705d2d45d989f966d2583053c420658ce4a77cd320fb83f2d421664763968af5f36adfd0e05c54b8b15a80d3b072dc368b1105dbb8119d7a880813caa8930416a4f85db91bd927a9a31e6ce8d74f88bf4496cee8fcf351d066395b0464403cfd394274e3b192f08f781b89647b40b8303bdcc0122c7caf2423f32873e7d0fed35ba36939cb903a6d47795a88836104735f2d42b26c50a71e1baf2886ba2b388b8e1618058cbf64630de7d9fb910676afc24dd5ff3728422b1d96a44ce935569431b2ef5f0d674314cbcd9961d998ff6f697d7aa959cd02406012866b63a81ef468a420a050d13b21e2b98935db6697f1592ed3f511668eaf98abd09fa16471392c29f85a823a7743a7be343c1b6d0630c0ca730a99b54221df5fc758aca2979454a75846b20e9c6aee9cdd53a68c3381718a7e47488964125c87727c907dd1fc2a4ce5f20ef2af931f1d15a002461a3de26c92dcb3dc26e3971b4cdead392378be78b4498c941d864cfcff481e7c7d5f491093d9092cb0b00eef777070c1d3927a738c17a280af858ad63a8827fc7a7fd4b7a3e5a2a0dfcc4e8fbc7a57d8ca433d4f7305791205d13572177b01e3c1e738ab25d30ec1140db57c3a1e49a06d7718a5b193402feb69b5dda5159d22c2012e3a10f21d39ce6000acc3b7ddbad10779b32338f92789c790c868b03429ed1bf8fa14cb1f5f62d6296b5013d4b6d9f96118ee4a9afc2295a8980385ce5fbfe72541e06e3de475025dfc73b3cd11281ab2bb237cbde884b76f84183d823c927920238724d8d048803adb332eb8ce4a3f6404ac1c52fc1bbfcb933bfc31c0bb3cf81fc30079873a3475bf518de50411633ff94675c93d794ecd4289846f96f5fb7422c8dee710834b65f89f45a6895a565b8358244dabacfad2019d90f3e4a237d63e1bfc21ec358e11c1345c744913dde614a4e052d3316727ccbdba821a4ce84dd1f1981eacb1444fd8d6692ac3e2e5602ab6e50b081432f30ac86dac657e6b98da6e4002e830db0e111cfc7b9f22a04cfc19b70012928d186c517bf5f355d21ccd28872fa6ab05a204fb74b8f967a962f464deb2609b1b6e0c651e822c7cf9d0f91815a2a03f07a6f425e36e72a5a8585955685a26bba577e4c2fac1d38a0d6223cdcc5a17126f92679e7a47d6797c44b8a9c99531abd5561cc947c0db1b8141c0b89ca5d22a1acfc442cbd2c3d2363ed1d703f443281c96c96dbc0e8628f2b489adda89a3609d401161472a94c1810cfe82634723cb744a063ce9c0b66c9a0d7ef4e26dcae0a8e9aefff143db0e149b5dfdb6b248603e6ae0aee57e47720a77a3f28de98ed19992a28faeacc5919c3edba34a2f0b58202d2e6986c0c8d567b4149d61d2ef5cfb68e213c5d306f6fb4619cf87d5f99ace70fd28026c52c79fa8d6148c8eaa8314ad9ae46ec6611cc40ac6ec3023fddc932c3d387c191f7eaec2a61174bca56083d980191f4bdde1edb86ddc4891c2c2154632e1862df5a4b3f19629dd5344b0d2e0b14a6ca106473870d87017e5c21e791cef5bb82b101669e2b3c0a92440c41701ee378c2ede02b5dc120c799c6b35612a174379538bc7513953933ed7baacf77db4c88b0ced3c9c6cb06e6dd8c811f2310f08d761bdedc1acecc4c1b1c4056830a24337dd9b46dfc89c51e52c8c69cbf26efd2a000486bb4a40e9fb40ccdbae4ce94e168aab512a0f578ff421ad60a08e2ca775fe598f582d4e727a1cf6233a5bb8b20ffecf601220f7e3cb6a394a0b526e0202a44eecf8c31c7ead4e0ebe7ba719b21716fe0b20755f3706df507ec8360baaaa1459f55d34c87a60f0b2cf08bb3f906e9dbc393c1966d33873aff1a74c521d77513fcdfd1518dd31097dd25e00ce21b83dba07b48d2edcfd8c3d38f4979b61a68e89936527088d10cb4a7970450d9f80009729cd0434f56b08b47ddeb128c4068025704fb0978127063411483df10682060ae27da27bf4e4ea0017de723506a4152ebdabfb8af13853725dbacc062e84b137f707aba58f24e1c62fd9de9f4ae4ab81654f82121d5f1c581f96d943c39ac13ce513ab81b5a69bc6015a0a0acdd109de4651d45b220130fbd93253bf3ff90c52bdbba1853ee4569906916a325bbdfaa90a43a527cea257a187e7ca7565eb1601bf79a41251882377c8956e653048a0580352fd9bcc553da0ffe070d854771d306c81854e6e4736240724a6c7e3e82b14f1b9cfd87ccb4965f3ff8eb4bc0e8a3bf1d829087360343167cd9946391d02e8baec3f4adb718754a4155c520360fcca4f9db1c5a38393144cce0d8cefdb3ca1823fce4311433a48d849a533fc11d6dc1bd0d93ed1445cef95b1fa390f1f4c50da9f909499483e61ef0932ae1abd4966cf805597fb284ab487791528e0f41f0655f8519df190c4db9b56326ac62feeed85764400b0f40332be62e3499cfb60b33ead6f06a929b4362d8e7e32ca48cd75d860d6b6d9dabba9121f45808034cb8a02f0b767ceced61d4d5fe69199d78c9c1130f676f3044e95b0238511832996e2a32ca75a0a4d4827a8a1e44ca8cca42693c54194a593a2b865c2bbe59448369e82893569023bd89e44f15ef9531a1e5ac841e65d04f545bc114a7a84a031bc2e79edaa029865a3c797a778f79b5d13fcb93dd761624b39cbd684e2186b37684b4eb59baff1536c60e25258194aafe28bfe48d80063b156d2420593674d0343507598ef19c47584d2b68a5f07594f4105e19543d1e8c8e7289115acbc30279974b7d1785dec06ea33bcfc1cadd9c26afe23d670ec7a21bacde8ae0d9036bb896dd782efbde10c61729dc791de48586eed16e33755409e419b70b37a2b76691f1911d47be20f91d8bde308b3b62cf7a5089a01fca6ffa8f222c24bb2800cd9dc81095498c22f62ef6ec1dd22ee5f6f786b19fb877b567efd0bb14db8d9bc40eb19b608b0ad56c4b9549c8ea553fcb60a7223df41bcd38455fd5ed454af26d103526f0d4337488ba283dba862b0415ede40db42c29658ee73d0a5509536217518d604590a3072b6d3addd6f6f89345172098547b64e141d231a663837dab537b4f8a6d0184491e8f7b3e8a74e7da549a63f7a0f0e0253245e355ef891fa7d9212302d41bf8c75a8011eceb5a827e53c0ff6b782134c5d47d3f35c7d4b6a778aae084cca96732b5fadd5b3408801de52cf02a0057c5619ab06111df936484afbd192a537f4d03d87f0f03a83dba5cf490460da26d88b3b9fcde68da611f5f8deeddca0870bf0fbc73b884849c29f86514bab08064ed54f24df57c43289d4dd9101ba72a47aba7a3fbab705023e857bc6a445567d2de401e56e880d2e33171c0be7b55a12b6773d86394873dbd32850c584ccbcfb4309d247f50e400849306150b4dc58336c436c98d2b07f8bf78df1996998015fa3181ba6c40c38ad9f61ab4c08eec5a0e210090f9a23259a4cea55ef4c397b6679cb689b73ba8040690b473f2461bc218d582afa6b8b884d0783c0970bf524b4767e09e240ea49f3124aa82bfe232ad98cb655dc201346165344e27f12e5cf60cbc856913213bd44e51a3e950e6b5daec25edfcf42ce8de22a18754fec05efcee94f026bb8bf8b9fbe2911e568586d73d6fba54dc2ee490f60c0dd466785ac31a947c86d21552e96a496eb8a21eea37780a0f3da57230e5505d7639db0065a33c811b33ba3cf55fe3346d7dd240b3669a3dd92076c180926dd860689ebc87cee31021746fa2e2beafa94d2dd9a0197da10e4ddd6f39945ca4ec6c579166b67cc97c90a37dc2a5027bf477f061851a9832b9e1782253f213e14da4a8b2d19eb564374ecce2983af85bc1f17a9c58f05ec7c6de59c8ff5d1aef7ec120b1400d13b66b71082e138e2a13b311cf49161758515a6c5c5ad085a1b2509f92288134edabdb01758368847f33c97cda82b0dbbabe691569d1386b13fc95a95517a3d33d482914e1a87e8e971fb3ed20090951e97bb02f25114d2747c33a4b5e33af88995e7d458d8d66259ad8e4697b098679cc368eec48bee8e08838ab342edf69a15791c115a140060c673d4df3dda44c59f905b83b46b72d7b6845f37f82fc52e921a21abae8875f824142f455e21d3693636964c2b6451b620c43e47fc11451b4772159786a9c4109d39059f35e2d3974b5a7602e1fd25bf33090b928104e5307b5786d3ab73f3dd4a3efb377bc64898062ad8f15c4c31a3f30a374e5ba8911bd8a0ce429e4ca73aeb9876cc366292f74035189d8982c920a04e415975e8f6365f5196204d1096ed8355e0a3f4bec795e961a5429ed47beb70f6327fbd5be641f8e162f87c31e3a29ebaf1098243786e5940ce2776cb83006911aae69fe807bdfcac108f6dd0e4bccd37b95d614ab8740694c0968d0ad9a3c6917c2a26b6ab58d0e271cb04c23adae4bb58745057620c9fbd8f69cbe997397f9607241f2cfe3eef7902690c6fa77e1efa2c5d1d00fcde4f5a6fa57bac54a2b6d9054081e48763a84a14132531ee747949f95568d304ab5d234b0d1351849506d5c445ae589384d2a832437e11d7b129154578117339edebf9c8b0abd1b999d5bfcb742265683b4b9cde02bcb82c2d9560d6792d99d5bcf1a1c24d9f2f2a524631b02a8e23ec2d570b2031d5c3f5e2b6813cf0e2610b0cb67d17954f5845730cbdbc2aacc933a430854c5ee4d04a259ea36983d3c8c531c8049f3107040969c3f99db999082db86b7e45f5ceba337393569b6de994bd1128ef3d3d3589c2c15bbba7e2602d5057d9e70075438dacfd203b3ecf235a8518416eedb7946cd3f2c1773f4792264e7fab9139e9f5b7b609b68d53b6b637831506beb659824140154eece0efd13fa02f790a19f4b2fa65612859d3c213d50e2e8eb2db839811e20d9dcd1e5ce2489c7177f265943d2c15d3987542424e1da8a45606a86ecc0441670011712630fdc3fa7cca2a951cc6819da05916236c78c5772c29539c2010865a7468eb63bdba52904c065c0edd9adefaf65f15e755ffeae3a12e706d42d0e371874c1fef8d21942e7a0d37ed8553976821b6668fa5b4136caf77036f30856b9615b7571677d8dcf1ac3008a605d5b4e8cd0f9153400c3e31551ba0414a7d49d0997bdb9df4413ca586a89b673a6327f55cc6b5848289b7297d21aed8849564e8ff8544992779541b89599fc287fffec52364f0dd8f927693d0748b25a74a0c034410038977008e8401cd369967755a747713ea8ba26368cd8e223602378755757ec3bb1b9f56a933edd130c73f4f11dee373ae82409c1cef937c1cd080e1c326e4c4838a0ce49bcc71c879098e90d2e95ac39be8129ddbc0ac0f1aa31822dd8bdcc8632cc9d9943b2d8e8624c4008ffe565d0d584e378e637602ec268cadc8d6408515ad15240cec75e253ff53c8e7a5c70e092b0113d788628d66d0136fb430470c943835e061b4e865c1677d9e1ad2be0ac7354487e2e7eec5070e3778988315ca02590762c814db2b273742a84638a73f0459fe24ab289321a05c93eac64d65ff3536cbff66899ec43a81e080c0acbbc114d7414953db6083c1b0b006fca3453c03d03b89a0af33f0fc28436b3b4e33977e23fbf3322c6a939bce512e555e4821ef61aaa946bb33cd1a746e64ce9b910e594bb0b578e0571d5c63db0e8503e5efffe102221bc219cfc4be2f25411d3ecbcaeccf8c34a0c97111d89ca1c2c2b865aa18e71ea04957f7caed9fc4a92c9324568bc27707444153fba68bfe981d4311be0254185ffade440d93dcb2a50168859bc3365258fb31e275f8ff99257d5e903a822b12c963ab6b2148232fafff35ac7e3acff2192e95d8aa4c144c606f2862884f7fc6123a2b997794df98f1608335a5cf635e9a669b6aab023242e5f432b709822b542e6028347de19383f6b1098dd6a0071005715886abe00bf319abfc79b87e4474c69c35041d58815105e477483aae9a49d37714a8fdf81e2d463e0419a9df50211a7499126bad287c23e80c4003f120ac41d8f21ac17dd94990234de1e43ad28899a8837da3638eb1c937b0e35cd618c2808ff702d2319879ad980dbfdf110e4df37ceb257b99a769b5e5629ac52347ac0632353d25adcd0ead6fe9077a390af20a5e451b1f4916a0f51e328f8573ca98b63c86d9cd88141f8efacc78dc4c77e5aecf799f8e39c3e41e0489be672a7d5ae585ed0148759c89b8cec9c222910a1c44a0dea3dd0495fc7cf765b9c28d102d0719dafb6079ba36d1867882b8c899c6749e0afc9542f4d4656fab531974c7867bad5d6adfa250ecd4d826a0058c73e1276f6ff023c094ea2e1e47f0cbcc54e81a2f304d75bde5d2d5fefaa928a06faa114cdb7253307c503c50cde8bbf9eed8141d1058c0a936bfa2171d149eaab0fbca2f371c9a0d241b928d541a684baf8c3e2d4a942bb5c2f3fa5ac620e9725088130c7ac4315a05ea82c36aac1f64d65112427b5b8a21fb233d12aaf82d4459288c354601d5fdf38ec72c3d602736e0235a273da40c67aa371e3411c388bd6714fd18c3b97066b851fcf90b566b64f1ca976b725b903f0ef0063127f281f24f07050703d682d901474b5411e8564953c4082507d427ddf2df8e3d2bcf8c552a383c22e4061f083a5a983bf9fa1ee8a777d6579f4659b17762c2844ad140528ba27a737bf393c1a755c931c111ab34384943dbec967a282a19fb5e3f4f5cd5454e4db7cee10d9a491952a18f3a4c3cafaead3d52449ebb34039b45a0f04ff76ae4e0295b31e01da7ba4e1a748bce51618c3e349f4e96d64a641910bf845955f92d231d331cf229d7c0a84a05f12346ee5966c85aec3bbbce73888d145e65e4c7a5756a34ecbac3972de26fa4254b0f9d08e2850337a3cb5e77c1fb5c6728005cc87653462a5996ed93d6462f84d36315579d98d1bbafae3c09af6b2b8805891e3c832eb964188734610eb59ceec1266efab71623954c294e569d358d7d91e587611e4ffe7cb7d948521eedf7113b361f3283c75cfcfdc1dd2194c322a5c213c816cba08d5657c6992ffa6faeacecf0d7ecb437104fd08b655305c0d5a17c69870de6d2b1c526f05583e4d01580e176abc43fbe453c454c511310605d42670dd439180210130c6077f8e0ccf3251fc82ba3418222362360b7fd7049e09683dba978f45dce7844a885d555e46494b989639eec82ac2951cc165d55411ed37c78178233397ba3649f175e955e6c3315add70708445e430adde5cd5fb1acbdd12dc73e5e1f8f628361f749ac2bc729e9a26c229720e3dd927554613d48c5c7b0d7a83f9d6013c77cc799f145f1220a6eee63349409534d015ddcc8a793f67621176f575881c98f540b23d1387bcd10df0923250e53836a542bd287a7cb1d722da55a5e479e93645dbd9d7e9ddd131bd2788d658b405b887ff177a1f82ef3d3277e141bb05d7b5314e7eb13bd235732eed451a5c84217fb759c8c8c1a54d659b98c1f218100b5d68c403631cb05dc4db536802450a48b5770b0031a19551b2fba68ba105a897b9572a75e15178703d406f56552f99c288f68c43883bfcb00efd55938af14c65baa02d040c8214586081adc69821ce2d55181c068a82ca0f456e101ab3e931cd72ab6d93b9397a30d72fccd54855d9f83a1b422f0c01385e6c832bb87a8a9a8a74179ea5ad4ee4a01f1178dedf808360f0b4c6b6028ea1a423dcc42f599a27421837fa83b04551c29602c4e244a15a12540c13758df4c1f6f7f127457f932e771b26c345fd882a9e631f977ab28ef399d34fd796151de28da83a02b5d51e473514e8a2503c67ac4d72a276cee39ad6d650ea52410c48ea561ec1a547ce1d73bedaa2a58fc627ebd83b9f254ded057a5d497843f77349fcbdf608722fe2bee7ee4a2c5a9e19eeec49a44374ba3ddb632e677a1e50ed2fdee202c945e36b75178da7b73ba6e585bc42fc01a1a97675bf241f718f1e6e21242fd25ffa7a0c4098ca86c7c89506465d9dfcbe078d59da708439c619dbc0d1116e0574fa2e2fb39d4f18d79916115b9907bf009e8f811fdcf0462a6111850e18f84c9d50c3c80a1e8c73fe39224106fee133b2e1d59e9c051ae667d500fe2fd22534e5dc6d2a27f25069db667eeffa98b45f11a3c7194169a9e314dbfab7c245ee33e1fca3eab3d6dfd3d7bfbe5bfd29f479f599d20f02f54f980798ea9ded4ff757708a946da14f5753304f9f29c0ce3d5e0f38565b7270943e43e7bf81569a080caf10ac1669ed9642e75a4c19da6358de05f1e7f2f878d9309555fdf30d64b0cccf2a92ed49e623003e9505bad11e6eecd33e7f047fde970972dd3b90ea065ac07441e2a119174be267148c0e314acbe87b47736dfb59ea29f289608d72234d3e71c3dc910fd2a471b4d36ec5cc4043da117ccbd314dc68770c215d95b75fc56f5ac5e446c77b1ec9c6c5ddc86bcadff35fa1052025bf5fd55bf722334cb8a46eb616eadb01eaad5342d0dc290bbbaa378954f22dba6fbbfbc1d76ce44a491a7c556074973bf01c51c2e8caa80b541754ef66e33a8d66b67aa3b95759f62e778d5b9c45dd0975317507a8ee6b8d8bcec58c5deff0a2f9f796bc32ff2a6ade7a364ddbc49457352375e5769f05692be8f396d132c28544dfba9dbbc69eee0c6a3ceb235ccc845775bc8afb6d5e1b4d9e46b8a4d4dd8b5dc7d4a77d6bada3b91eff11a079bc41dc9daade7abc77ecd621f6e31fd2fb5ed57128d2e00e90d3aaac30720cf6bf19f74e50bde7e8f666b317d77014a4579f73e4d16ef38a87eeb9ef029563d7dd6cc57b5e3640b5feeef5c2de3adb878ea18bbad98a1a7ed922c668765d1774a1d76eb6c0d407fbd4bd5137d07d8dd0ed67411f811608e25d9efeaa5b4ad297a8bff0eb0507de7a613348f33abbd9b84012a170efddb6def8f0d6f5037c05efd3fb9a15dcc7e091de55f58eb757b3ded5797199ee79784b4a7bcdaee1e9cd163ac61b915cafab7ad1bbb76e8708a7dbd4955b1b844a4517b5ff14e1e8e070a0273f1dd011409233af1aaddd18f96da35edf5b23f5ce893798bf2b796fb68b672f05f5d6651b6f2b9e1c0db8320448386a820f65792efe5f06acb70214e00ae84595304a788a0f27b742551243b580d02331d1ad25b0dca62c7e2d10e5045bb31835e83346d716a4aee499f812c0173cf1e048461e1c71459e4d286ac2b424ae15a8830f7c27b3a90437fc3c4331f13b62bcc82b37c472bd19be8e507d2f34344089327c2a9e1289503b48368a4b55fe803a9b681666d953497fdf6f685a830e063459490d16e0521ae6699e4d3f06706f77bdfcf34055c5c37f3482b29367b1d17ca3add2d0c42b3ababac2822d49077e79323d866bb148695f0815319182540c326cbb40019c23553a683b44936c172f2742b84da9d32ec753cc6c776bca3487007b190edd78b026380b949b04b544c1cda4bf707b1a542a3919fb68d33d21cdb5b718d1ef935af6564b3a313f0c7dd1723a4ef034ba107465f92bbe1fda2ae0378e8e46ac1fde2789bb4d5823eca0a4421275d962d117633450c85531bea80b4b7c9f108024f42c1e9199240366b629a03202400c2426baf08b30474cdac186c933c6c616bcb05288e74cadd1981233a28a28c9ae611d7df6447748d284d0ede01f3599c23ceb0526189fb2c326fa65d1ef6d3a11ad483d656ae5b1e1180270e0bcd1cbca1acd1925cc565deee5b574bccb1dab4deebd6a448e3f6a33bbe722afb835da29133e39e61b84634dfdd02d982fd671510f3c097fca68a58aa4fd58b3e8dc461be3feaaf51de3879b2dfa4c2cd0181c089c45769d8c2466aa7bf8c051a2d8bb1bda9ee5892c9087b340f9e69832845f81e7440a37ab43c640e23c5e0a007e90514427e54323bb8669bbf0f9a3c013ad0180519145d2914001080ee63a54a530fb8dc9d9235657656451257842cebf8f3e86fd8694df5f4b8621717cdd51e70d5f5335c7637fb2e0aec8644de0404e36e64909b19d32d903dc4dae934278a6c198c89a10ea93c1dd07694bf83d0833ad42c428560c206f83195805ac96d0c0970e66842e06720502d426e349cf00b8b4e943d790a5aa8bbe84e5577e5288b0842708b04f831988ae2bf918503d83651779be604042039ddafc086fadb602fec504090c2f40b46439f003fc003fc00f00d546486ca4f5dea2943291872a34966a7ba794524a29258d9a5f618fdab7563758d31f050c890ba50bdaa5970c368472a4cc816347f14098ca938cd31910c42432c733f75031eb3469e8e817e81ffa0e21f3e5991f48d2acd4264f59ddb974f481a459713f43c9d0967f363e1074a99176f641b79cf88aee81a0324744c38bd012ab1e0af5b1b464b0e9b871d629d03c90745af114f91a97944e062b1e0842956af16016196c9d76a46163c7197720771062f27f2e470daa33ec40ecb6a05f6baf037945cae8a59bd66c99ad054a8774c996d12c22d5310762cc6d672a1a54a73ee540cafb5a3aa50bb31d1a196c757120aa78cecc2d2a3dfe4c3810ac931261e2de5425cd8e1fa033fa0d041de3971026be2dbb8944dd404a42b8c825cba715d536903a5b4ccb531e196c37cc6003e9b26fee10f6fa6f1b0a740d24399d4d1debf255d540f4f6a4e43be775d8d8a10307193f407b21d03410cf45c5d654a532cab841821a3570dcc071c3061a3a140d0cd0333472d49d6a4d15161d65201b3970d840a39861a6335033e4217aae745c1ee10e1d367494f51d66a4116819f6dab1bad5d3fdd8d82e8d21d62947a78d5132b86b1f3bfa1e72f1e6418c089d8e8118c46f08afa07245fd37524a117ce00366e8283ea851a30b2a06e2c78eaab0e09673ba61e0be633f45cc52af5e3bd3a3c6a4b3acbbb56020e934a1928d96fa05923e1994bee6e097d2a65e206dd820c3fc6dfccb942f03da05924c5e315367cf0572e6f914a6f4ac5b20266d8b265bb1d4eaa95a20accc55e768e65dffcf41b34010a641a8db942e6b778a05c2f59850531b446faef50a4419958416617aae4aa45620c59c938719b13908eb5401dd1bc4af54383c4fdb5e633bd5cbe54429df6cab383729ea1488731523e635533a4b3b41a54078df909ff2254fa3401c594ae62b7a5ca05020f66f7af2687f1eb38f0a037d0249f8860835f11dc425c960eb4ea3d509445db18ca26f25833681a0275bd1e349d19e7f3e823281a4652ab355e6740904551ea6479766e693512510a4e6c6286d49492088d85213d2458a0472697fe6ed6496693632d023106d456a846bb75c443bf57464cfb45406d51fab4184a618c4cd1a7af43abee824dfc0f18235238941ccba39ec37da68ff6f021fe430c815ed3fc7a42f6110df2a3dcde6d4bb9c0c06d1b29a8cff99524cf10706d94d8c145153e6d0f10b82a7f424b43bd6be206aba13ad27568d1a28472f8863e21734ba7f12b9b708c90be2aff6e5cd31f56ad63a84dc0549fdf49a3a999e6006367690ba208d75e62f9dfe2ad6290d3476a48102cc5cb8a24dd6c5f77141b48fd1ca6e4f6e32fd16e49c63ca417c3b5b90b37c686a4b41e7a0d4b52086986a326161faee4e5a1c35f8eefdcd3c8b62ce61a6ea74f32e8bede56bd6d5755ccbd43d6f3e68e64fc682d829998ca73bf4b452b0205958f65bd22b88c953e756343f5d41f88f31767839a599df6c05413b948d921ebaf3476bd45042b282f8e9978329a5bd348f70ece809b40004385a50a3860f70e4c8b10a82503166ad9ea40a62e5143206b1e184f4945490ffb2cfeba92c17640c2a08c275d5dd4f94a88fc390a720c7fa243fa5a6759694b990a62005fd362a8858b9b47c66214b41ce7ef3659bebc3e38714c4fe70fb2848a79deeb377e69aec884271ad2e75bb2ecdca34ad1dfb5d913114a4caa1d5b2e7808220e593b4501bfa57e4999f20e5a4f72d7b05336d9a43437a82a0f39da95741e6ebbe13c4bdbd38a17644e860d6a851a3061012420c848450738258f731fb687e6c46cd264863d5b677ba2ffec6344190f5a173b2ca4c902d74d43ff9204c103de9670ab364b2d6b904f9f2c7a07aa5640982c6783f7f67a9192d9520eaadbdbfa94bab16a684ab7b6d2936db21eaa5f3e92c934e234323c84910bc36054df94979e5daa69092205667f8e938daba8219099218194329d12a32fd270909095252ad499ab8171f4114151e548c8a5962b53b829431eebea74adae7fb834236821cb64e6d3ba8cdb859a34615921104259f6f4de5466ef8f8224849d5c83b5323db534f4510b48d46d3eda3a5211341361d3b67b8a86b0d890852f670b5f87f7974776dc84310de82de20f425cb908660ef63cf6ae4562dee435b3499a65ccd3d6721c8369a9dd7922c0941f4cea59ac74646093b8d1c04e1c37ff80eb58c95194110e5346f762ce15e0a044966d1cacbba39655f4090cfbf46aaee698d46f30fa4522f723ebde5b9a0fb812052e3c68ac80b5b671fc816c446e68f4c1defe303b1db34c57baff84178f640ee94a9f3c68eae1de3d403492651b27193f2f0981815335e8ee381987d76498fb8ef403ed3df9b92b8ef3c253b1066d447dfb01dfeb9ab4396a2baf795dd9917a759d51aa6b2d281346e2553326161addb39701f3776eef965395696ad927220e8471fdd70617ec405d1d071977120a9755caab89d45d130e331070a6e05356ac081982e624ff54030368050061a8933c8375c296a90634a293de07d763ccd0dc45479ee9450ff78db408cb54adbda7d8d1a356a241b089a8492269742caf81e835c03c1ec949870ab133f911a48a7f4adca8476f6f8c61969d8e8c18dd31bc834103e5abb5e5b4ca6530e1a083a5d5fccb4a92eb52683cda4a1e30ca4936b42d6564c11eae39166209a4af16ebef36f68dcc8b1434719bbe38c1b2ec8c10d0e306720cb40b0a4c26baac7b49a3c4192816836efa22aa82043bd1d67dc48830764e41808a284ca4cf7991103d96459ca202e571e3d4f8681e8ade3e999c49b868b196020e720e6c5733e9333970b520bf90592ef664ccbc9644afbf92da41748e9d4a6f352dbd339b30b244d5984322f9da593960bc44db72989921d349e9a5b20250b3a5b5032a65bab520be460b2632619c733ed260be4aaa062874e97c402514ff505ada42d6474f30a441f55eda5de6655565a8160267b3f6ac7d0157f568124347b92af8b97296f5460b3c43ec52a3566e53a4b4e88f6bc7d42cb9c02f92f55c905fd859402c1920cdb1f64d6ac3956a38617320ae498ca3eace949065b0f6e1c1b69e0c851972b82b6152414c8a54de624d355d2fba2b6b1847c02417754c8523b4bc2fb7402e964cc9d797f9a60b09fcf9cc4e74c20a5119d74f4ad7c749c4b20b727ff186aee5402f1ed636f688bcff7d10e641288b5bae18e347000a194914820ee75d4949dfb3c02b97565449385d30884cd31b798fe9d8f226423070e1b682260cb81ba1824f1a04ae9c89a12f71f3156fbecdee22976c320ba58dc6c4a16534b5a4647096ad4c8814e0e1c9c041406f13edc466586898341d20ee73ffa473676e4b8916347e7d8a1031804f5b24c2a5fb6a472f35f104d76eda68b73c9d3bae10b8272abba4f7ac254f47b41d0ce3998e5c5ef9e3c9717e4f4a44f689cd7d66dbb204991c9b53ac76845cf7207a80b8290c1dd2be5cfe7b9823ac05c9433f98d12dab265e0c061ca405c90ec842caddae06edb271d69e4b0a1e3469b8e326eec48e316885b13b62dc89d5a1e573389bea0af0529ccbe27759eeee37fb42026edeae2e16b19f7096660a3e02c08f79b72d0d739a65596461928071adc280ba2bff9b6ce8dcedd07e88cb558903cc38a9814259b53908c1d250549c68e82460261c725c282a0f2d354efa85a9ce7152725fa3e9fbe8f80aef0d2ce643ef5146cc51d2d3372e0f8801939702420ec4056ecf1a965ad73f65a318fd214fee44fc68e92821a35c8d851d0a85103083b6ad438347015248d1fb5edb327bf18a12ac8fbb1a131a754103669d337fb410549dcc46e86cd9c82b06a5f632384d40cb229c8a9a4974a571afe93b614dc99ec7db9a56e5a7dd6c659af8514a4b54f1e5b4c681404cbffbe2c3597621705a9b7c3e383e8aecf78763a8d96d4e442501054122aa7ef9ece2ccb27889d22fda456083d4192bf5277f59d139ec14e10cdcbd3aabf6a3419cb0139414a95e6734cb39ade933641d474b229a9769a20a86439c78c10d6fa5526087315839d7e7b519d8309f2ca8fb8f50a1fa62bbc44666e792e73969a15e484e798f4af328b96207cbe24fcb7f208f57425d0d8b91cd18ebbcb3a3a36479c8a174a10346e9cf0e4e224482767e572527af62c896d984ae2cc7b522ec5284682b4bda1c3ddee7a21a851030608095267fd7d0e13a765001f4152295c43e8ce5d1a7e32d80e0e336cf4e0c6e1d573412a3a821c74ca1c84144f9549acd80882f00f4f0f42870764042966d2a3ab821061a7838b2026d3e3a645a7db075404495b96b20b3d424c0459ed8452d2dddb032282984cc9a54d2b196c27c04310c632cc461bb18ba62683ad8e056d256b0e9ee9acc38cb3206f8c1fbe2b5cbe4559105cc4a5751363419e97ff182d68b4d2c1829869bda34e9ddaff5e418c319d1ccf5629beb98220ef3389d4cd2063ac15a4d3cb58414eed2997297d41855641b84c4ad367d1dc29aa205aa59196d5e43d9c0a726fcca8280ba182f4962eaac742a720e668d234d8fb5c523205513efe53366d4a359582a453a687b689535b2205c1437c5768d02888972ffe8fd2f75d17511035a935f50d2fd50905295e4c2fa92b32950e0a92959c4dedf527485a4d6cb4c69e20e776f8d5f67cfaa29d20beba9690bb4bad1c27083227eaefd904412df7e652314d105c3429f54da5396999205fa878ad973a9820263d1e536c8c49ab752e41badc9fbd848c25c8c9d572c9d5660a329520574cd522ca4e996528418a27db83be5e2861990431d9691765154990f6e2894bda8352a28e04c1a348cbe0516bd40d09d2969213279eacc4fc0882dddce989bbc691df11045de9831637cb7fda6f0429f7df722cf71941708f39f557fef7d45f0441cf7e88f051e3d97c45904545abd5b0495e8f9f0862acdb14e3daeda1840852e9e46341f9e88be910e4906f320431cee7e41b4f58f07c2108a357fff6c3299d79429044e64d1bbf7932fe83205f774a3957ae50b31104a9dc36ed69261044b998a73626ddb60104b142f7fe27fd67fa033975257129ee072f65b9784cf7816049df94de9b0f2499c64c07bd29dc650fe41fcd0b4ace7a20a93d370f861297725493e2816c3986f43842cb7a66ee409af5a0977f3b90d4f5b8a639d58124a3e379b14407f2aec630db0ce6999f0339c7b9a8a1e32835bf1c48c22f677378beba1863e97cf587dcc081f01d83d20b973710335569d695ccc5c50dc4f63a695f9b2c064b1bc8f37331ee92d87d9d0dc4cfa3fe548fa6737d0da4ef6b93d9aa1a8839b7c12ec7301bec3410f3560e95eb294bb56820eab847134a7a78909e8120c469d7e5b598819875e4a664c14efe57ca407ab9d24ee12b64208b985b870e96d35e650ca4984f51a76fa4b655c4400c2a4267514b3d161406f29526d3de4af92fea3e97d20f4289be40ce5f428eb787b7d37b81184c9f9fff794a4ad305922e95c5ff4f9d073117c8f977e64ea7ff52e12d90d7c63ca9b09be35f2d90b2adcef73a69cc6d16c8996fc4e37a0665522c90827fd063bd6d3a46af407cf5d94fe623f3aa5620c8e8f88ffefb23d42a9052dea4695029943ca940caf09a57f79c02e1bc2fcdc25a769b5220ff797acf6929c38f1e05c255f43b2d7b8bf1732810b3b25a786511cbcd9f403639b97a3aaf9bcedc09a43ebb98c6466f0261ec5ceed774909f092459d671f1dec4529640121d457918b957655202399b98ee11d77131ea53fef02081245c2fc6147304d2bac5b01108eb15f36c4c2906b992ac4eba6a71de420cd269f5344ab4cbc6f430084ac3e26c270c622cad619f95ab310e0649e4e2fefa0b0cac42f429f52fc855a5820ef63adfad2fc8e133abc8cda0e3aabd2059aa32a57a362f08a3936aad127b17a478d25c6430517abcd605f96e7365178f317dac7341ec2837f7a793f2f3e08268326387926a5bc2bf0539c8aa68a67a5b10ed4cd7bfcfcd94a616c4179391e1839552a205316809773d4d376dce82a0e9d932a3e96541d2a0c4792719c782944fe78b41ae8b186141f03b195b43b5fb0a62d7eab87509a91bb4eb8a3679aa309d7e2b484a7e8e9bf37ff68b6105d1f3269331b47898cb2a481593c95d564a5b155590e2e8f3d0a9f4c9d153411ecdae29cb4605c93e875032daa3997c0a82cd555bacac29c899642e194f952a9196822c1ab272deae4f5a2505792cc958323457267514a48dada4535cd0b6e48a826095537eb89aac186e2888d739f462e5161424137bf1544c6349bcfd0431d489ce14dd43cec91364bbccba8b712f9ea813a4932fbd65329c20587e5e55f82c1f6336410eb253a7feabbab26882b49a2ac4e75e6f2b9920683b33dba0425def9820fa584eee79c4e5e79720c6c50b15ffb60441cf5b67858f94712b416abf52a61eb3a7922a25c8696bf4de4f25b1a64e82742e6327835b8e6ba924aa341b3cc9b33412c4d6bf4c1b8388d7132488f39d2c5338354a7f1e41d2a3d47ffcec9fad7104b2ac77353ede088206f70af1d123bb2723c899f4c4b3d68b20c851df39a65db55a459044bdb68ba7d76e8920dfa6381627db7f2922c869b2eefbe2865df210e4cd9c62ba3c6af7350439de8f7c10195e55652108d264b55650b2661d42904526bdaad20f82f851b7f4658d20889b646faa58024112a64eaec7fced8f038224933653297cde3eff03f9ab94526b613f9035dccbc6a99231c57d20ebaa7f4aa53bbb593e903367d7a42ea8bc9c7b2006dbf89e643d90948ceeefb3add4661e885194ee381663951d1ec8311fa36536ec738677205c080b2ae9d881984bc6f486a60e64fbf0111bf1ef23a703e14f7e4edf06798bcf8194c462909ff4df4d2d077207f16aba6c3cb41c07727f901be99f729fe040ccd98446b3209a83f60d24bf10623d643cbc75034169aa8fcd987f94da40509596f6dfe19bc30662caa5f3a792b249bd0692f6b7f9b8d92b9fa906824a5aebd62fdf6b990692a6e67a10292a6f4403c9d433952af1ce406ecd98454689ab8d9d1988e71b833e93958198199ff3be2d9b1b19489a5fa6d3f87ee8756320a90f1fd9b97350729d18089b625c8d135739ac0b0349e42769f5f184fa2430903cc6ddcf31285d26f405e2787b9e0f425e20cf9cb0f3d4d1ca5117c8499f7dab67b9e0395c20fa9d572afbb740124df7d142c669d66881604a09cbe9f12c10adf42c5d732c9043f75e73b6de5ff90aa4bcbe19b7a0f2a6a815c85b1f5681dcad26c42553294d5420afc811693987b5a56c0ae47b791d4f9314881e2eb6a8d2890241c6b051cc35c62003058257fc98838ae8a69827105eb35ebacb9fccc609e49c4f8d5213481b4caf7e3d69bc0b13487ef91d57354b20b67db5e7ab537fdf73e2a724902b69dabda60799470279adf793fa7f86cd11086a7492799dd708e44d967490ba79b4520c728625ebda1c3c7a490cb2f5ce9ed0e33008ee2fcabb3d6110c32bf496824130e5315faa97028324638575b42d7f41b892d1a2a5dcf54cc928a82f482a05a5e35c455d737d7bd15cccb8f2826849d7736a53919f71dd057bd71e7b9b63e6032bd0c5a94489f272ec402317868bc263db4cefe4cbd66c4dffffe618bd0541ecc7f8f2259f6ab5da8214efb4e70cfd5e416b41bc94f547b3c5e8fe3c2d527396e459983157b173b1ad177b973d3aa58be1634f5c64b1856b95a959a988957ec6edb88a4c6341ca499c698a413fa8496141ead5242e87783f191b3b500e72e0488776fe0af2563ccb9b94d6a5ae20878bd97d2c08fdca9f6a2b4839a38cd00e9de332489515e45139ff294f5f259e6522741564cd244aa51d154625ad07356aac400438727c20ad0798e3860d3474a4a92ac8b9373b9d584a1d94a6a9209f9fc7f8d8747d99840a62d62d59eb9d34e6844e41fed038ffab9ac53ba6205c766e92d5510e5a0aa2e8bf5cd1b020ce6511282948b132e5912722578f827c26d426f5f3c1f26664b08982f06126324241929f3f86ac650c0a720ed1d9a77142a8ac92c186876ee4d8919f2089bddf749e2d3a3c41d00b23d4c4e63a410c7ac294527f49dee89c20955cd21a73b4c711ca32d228091dbb4d904b657c8a97452b658c26c839f6d9c3c2b7678a6a26887e1d378993c12a7f4931410e9e27729e847a0962d62ebbee58822422376ca80595635a2b41da0f955184ee1cb486942099d8caafd1d286dae4e0868e3b814e8218f24e9accf3bf4b8214642bd6c4856c073f12c450dab4546b4619d3b191781412c49341ca85f250eac67b04b164ce39e7583932d8d21104a5a2358772ad11d7e5d1ffe799716584ad5523662127f316eebe2e826c614b9556a7ec6da21ea03214410a9d3f65d3d8397a84a38c1c9c71230739c05146228896928ea652a6f67cd91fa073c60d1b6926504490535bc7d021df5f9774085297253932658d214c29c9e41943ae85208839dd99f67c44e79010e4b8ae146ed388b0940e822cdaf5847988af65c60a826ceaefda1a93017f68d978548120adc76c73ce983cc650812862639cde8e61a3dc1e53209b36153f684ee59d9692ca99f27a4421cdbccf1915fdd0b11c3da040124ae52cd67208956e8f279054c56b3295dd0944dbe42b9f723c9a40529e2983fd688d32e51c3c9840b8647aa6b205bf64992510a396b053151d5fe3ae04e2d5e8171de4ed91048236a59dcd62ca5979f3400231c920b6dcbd944cf03802d9e295e94eeaa9335369781881ac23dabda39ae44ef56290b265e83771f22da8d507eeff57b4d0518741d28f2f5bab68da4e9b2e0cf2d85a507f353a668748f0603430147f914e9f23772dba3c4d705ff49b2e674273017082f7c26ae0c07961356e17a4da37e1f5fa93c1375c178caee65d99d6ae89bc7b4ee963dda7f8792e482a9adfd66d2abdf9202ec82175e457fdccc468b7207d96514ff24188f8b8db82f8967b2184cc5f972c1d580b62c6a0aa2bfbfc8c5075064e0bf2573c9ddd4bf674e7dccd8274a64344a998bd9305416b1c13eafec1ba4e77b12086f9a80a7a3dfac3098be26d4ad3dd2b08aabd9731464f217c73e70a72b9968789c8ee5a41dc4e953c3c68ee58413cf5d173c7d8574110d777b1e4c54cf3f7f5a0460d55902afb63ca183578a9207ca6d92833f15041de0a17eff7e19d82982b2b2fe6309e2948a15c378c1ecfa6c388570ab2798ae756b7fd3ed639298849e6ab5c956459a9f10c1c67d8d071432f053e0ab2458f27377d9faeca4e14040f4fd22a7575a12085b00f55d14aa970f9030539e710fa1b534e8988c9603bfe09623c254ebce6d19ec13d41ead4902237293d26bb2c837782207432d1bef24ecae504c94fc82b15543bdc5661f04d903d89dd76136dfd5479c13541acd8121953c9f34c56f24c90bd4acc5aae909ac131412af7f0aa4f71cf63e60cbf044168664fa94a8515598ac12d41cc9b5375ae18273f9b64f04aa45382a0692c46a78df9fcfb240842c8e5666bf9bc7a3925844b82d8d59d99428e4c0f51dac1234118addaa3f3f86a798d69704890b7a3f6a57b6966adc344e08f20a9a826177b3263071533d6e18e2056ce6bc1849039f6834e9f0ebb813782148458cdd9a7789b3d7b704690544c2ded39ebce231d86be0892597eb8be9c4b051ba9c11581d649cf7162fee924024f046134fc49ed641592c01141780d3a5a7ab3752d49077e08925ecbeb68feacb636dd10c9bf564e23636ce085209dd4de2e5de153bcfb9d1055bddc79c89197b43976a041041fc45bbaaf3f9bd2665073ec40c30541700ffbb1d33589fcda0c0f443999a7fc1602046e5fa9326ae7da69eab27a1019447afc0fc4af7effbcf5f881a0738c5dab10fb40beb2ec8e11b11e543e10637d9c4ed329a74d7e19be07d2a9cbd822de4456ae5c0fc4f4aef240f0bdb5b4793c5367f0404aeb24c36626dd81e86fe3a7740addb4273b1045f34b3be4d9f99dea4012b75c8be1a203d93395ecd0d19a97990349d6abe6d02de23f2e07b25c75ccca1b83cf1d0752d46d8fe29fa1d3860329584e9d84c86f20a9d4adabb4ed16ad1b48793466d968a5e449db40b0e0e619326503a94c4ebcb5764635d740acf472c24c5403417fc924dd3c94862e06693a8e06e2cb7c979a8f0f3ae8fd0c2971196433be66207fca315fd920747a6520bc69553c15c5faaf2703b95743a8aa2037ba3f066265b58d49566f8ab71808969dcb6a4b13067210975fd5010371bb3f75f3abb28cbe408aee5ba23ae405f205d198be5665ed77817c5266ad820c17c81d5498cc9bf4a9a0d9023145f5f67c580bc40b9d4c8f56f317390b24ed30333a69b1408c9992c9a44aad97ae40ccffd0d2cbf8e361b182212c9c5dbc8b5781b461cb5cfc762a907b37e798694ea5a64f81a04dfc678fbdaa2f92025153c55c4afdc8dd942890848a31e7641528904fe59615556a4f459f40b8b09be2e2677c994e20a5f8398ce95c2c2d7613c86e9d367754261054caaa945f0251c5c2265d8fb2494a20e8092581d4f1fa3fee3112c8d735d2adb3eb47208cfc665d5279b45feb46207bbda9cf894c793b5a0cc2ed8d522a5f07b5192506b1332b3f6c59592e0d83bc59db2a6cd68be1220cd209952153fb6cf2381864ad34aabc030ca22597d139c810afdc2f08be612cbca678cff105297f4ad18467d90b92e9cbffb959f282dcf147a91cca3e9c7641109b44f6c8cdbea6d105c1ea93ce1594e5b44b2e487fa382b820fb26692ae5740b82cc4b9bb5185293c916c43816c32a3e2bcba8164437cd6831644cc1a3b420c6d45232a6a710b5de2cc849db94bce9280b82da4dda634aa7e962170ba255b0202f740c173e2c92ef2943bde8af20ee25eddd90c993d7ae20998c12d6b7c2a46445d6ac20cb96d0187dda2ac8c1f2bdfc25135a3d5305499c5ed22965a9209ca77bc614d4c7b951414ad77cf9a544b6cca720d8788e39a7cfd3b3a6206bba5e4e3257a520c8bf956d8df13da98a14243df22dea92d997aa4641f4eed3105aa2f7ed228af2b6acdadc5eace3433d9bcaa1a55090d365483f5913963cdda020e68c9e39dcec6c92fe274896a9b5d19e20c6264fdbfc3cd96745a13b410c0b29971e7f7382949f49fcaf6e7cd09b2085d2a1b7f671bd5ad604b3592f2faf9b2e2aaf6f898cd4ac726682b4a16abd52cef1934927c004514e45f524ffbe04312e6c4d98702d414a312f7c0e35e2d6ae4a90c46793b86bf19cc4f3018e0e34258e2a2ae78afd397a12ab97c6e688dd78c7184fd4cb2d0952bb09b19231afa7a78bd09120a89cf36f7fbef841a74082e4a9b275764cea47107407e11ed3cd74acb423c816f52abd424f295d3682b01fedb4debc997f8611e437f75251b2baa01741d2dd5839784ca92248192fef29fd9cd7edb8910306356a9c4e04d9e2aafbdbd998ce4d238258b91eb7b6b38720a6df28f770f22ab99f230d1c2c6843102be998f9a57d0f5d08c2854d53393c8bf00b2204b173f014f73feb315bead083207710cd38b2648ffc0f3ae3865941905b4f78aacb4d8e1d3a26f081154ce004d181206ec9146da25d0e52a346c9816e34208e13336b2adbe2a6ff40907e1d5ee1d3089da91441fb81e01a5fed93ae080dbb0f6dcd5c7797657dcdd8669ccf88695b9a0f84bf8b716d2eedf8a8f64098bbf0fe63592eb41ec8997fe921ddf258280d1ea092060f48e581a899e6db3fd56ee4e67820299d61d4c594d2d17720ac86d6aba03734b968056e0772ffc970a6da29e5f31884ae03395426f99af62342d3a1b6605a4554ba760e7c502a670eab17125a0ec4bebee4a2555f695b1cc8ff63e16732e74029848603d1a4881f8bf55811bdc10d849141b7a5e8fbb42143ef361047efdcdb890faa7ba341b3e192a541274fa2a45cd21ab8f33457d793cb96f93465fed9eee9154438683590cec2945eccf1a23e990682f2a45f2363644c6f2cc8c1193772ecd09146a381a493d8b4fbf3995b7dd467205aee60e97aff360341c74bf2e7531853e7e128410eba0c84194d7e229ff97aa6264363dade7167e53632cea69c76b374ea942007ca410e7094d163207f0e63973786b74a4d8b81a0ad964c4e3f1d0682f6125fd7f4f8643718482553faadf0cd17c89db7c17f562f103dec586fa59915d5cda1bb408c97b2ac4f27ada5c2058252b2bf6490a32d10355dd42066bfc9d25a20d6eb9c32137156a5d459488610c202e95c64573688cfad9fbe0279b4688ad3fa512af3b60229889a6b4e198f7b7557812c3ffb75bb518120933c19ddbe140f734f81a8ba1a3f6bd229bf342924b7612d7255a61d3a53fa7c263633bd1d0552c89f4c2a26d54d27289044b54c9697f4d2a12790d42fdb680e9f749c40d013bd29bd6f02416c2ae56926107fc7b2657e9f497d09e4bc37627b5d25903d9dd411b7ec717712c89b3129bd13238164edd69bf3db4720e9faec70aada8458db0844fbb34e333391578b41161563aa69e877391331c8a7e29cc558a6611034dd955229ba299784516eaed798350a0631465febb424b75b040c82a6cae5f9cfda72ce2f889ad2e99cc673d8cffb82f041bba6d3b1518fe90539bf776cb19893bc0c2f48f1c942bafd78ecf82e882766d183cbba2087951f13f95332da5ca4e46e161784f1a0c472465e737b0b8246d3ddd6fd0d4a690ba22621bc2ea5c6e8662d08326797b71cef61262d08673b7e803070460e6ee84080057460009561ca8e34cc58800504a003e548634741c9c6022e00800aa061c00b03787512487c47f77a282001ab715cc322f03b74a491a300010082c1800200b043471a2b204000500e4a1a3938a394810001e8c8813e8d8323071a09300100000000000000023bd0193b74a01d18481c484741c3010b581d37d0280a08c02700a04387a9410001941de5d4284000ca8e7270e4a87100000ce00109c8816cece0cb0e1d366c2c00000168c0018080c6a3016fec3838cc3803ded85106193a8a0d1b0918c30c68c30602c628c301c62003ded891830c1d65d8b0918031c6b003e91003ded8d1831b270d1b36123046188816d69470118d0ceeb334cecc40396cf4e0c6b17190db8e336ea8600c30a061c69b81cc30c0185f20e8dc79d39fc89cceaf1708a73ee5dc271bef63ba0b64994b1f362753157d339019bc63708124c49a6fd64040e3cd4066ac19c80cd471c68d1d65dc48c3d8c881cab8818619389ec7d802397768c6b31cacecc007563001c5c4185ab8f262b372f46ac618592067326d9bf4be6ccef1118c8105763edfda4d4f3635afb5f35cd7cdac538e8b79c1185720c95c5a4ec65cd9308615484a0579425dfa649c51ca48018e510582d4747aa49ece630c2a74a3f9b9b6adaf2aa6962534c69802d93cee8acab11cbba61d40e0c01852283eceeac4e958ba41860ed781060e1b65638c110582aa1653e62f1a030ac45b339533bee51e43378cf104f288fc494f3a7bae99c8602ba31c1a610c2790dcf49ab9566b9a678cd1047218d9d3b1b9a45926c76002f93f7488ddc564b099eb608c251094e672f5afd678f300dda317c0610c2590c49cdf46f7510f6ce8480249a5d116645f38c6400249e84bf5fa0ee618e308e44bda7749b6770ca192c1b6630c239052533369c88c63070b4e50a386192ac3981b677c148394794e5ddc26fd0f70181b3bf852867154c60e93868e3dc10c6c50e0831804cb731bfb339ca71171063e8641cca3f36ea53cdf76b63048a5f3bb3ba6390bf50583ac633a837a348f5a32609037e9b8b1262fe7f8d52fd017d88be305d12b988c9f847eaedceea2b40ef90c1b59af58d37541dad4fc7f49a598a222c881768800e5200732015e0eb4e3060d3e7241ce5a9b5a2cac54460817e458fbef93717fdc22f18e1bad4fd92dcf1fcb243aa9ceb388e0c31624ad171fa488ef9c4e5bb5f8e4586b78eaf8f1410be2596b4aca46d6d73d356a04e16316a856edbfb834c70e34caf89005ebb194de0df12c5ac1472c3e6071bcc85a958bcced9a5e4134d963c2f3c5ae2065f52fcd9cb5561065d49eba911b35b7fa6005af829d1bf990d335af9456e16545679ca361c67fa8821c1e64c95aaa956f2c15e40d5e6a541037c8d1b9449c3e4e9152aa53e5d914a98ebc9ddb7ab97b9669f7eb67cb9a72e8efe6868f5290720e6a6327a1270531ee737c17a5320a82d6f4399fdc5bdd7b51104b27f1a4dbd4342ea1205655f8133eb796930e0a62ca4d7932e73f3e41eebdecb94f79e9670c196c4038f30439c7a3e9649d4ba993a11c3e3a41f8caa1b4f4e864edc90f4e90d3bd46cb87aaecd8c72608fb991d5bf5624e7bf9d004c1f63e8648afd218692648e1a3417b4eda287c60822033df8cca89eef9a44b9092e9cc3949179d4e5996c0dd52fea6f94e06db1937ccc00d7c54826cd94bc89327b4107c5002a143b33c5968b4d828a3dcc72448a7bcfaa266d78b9f5b093e244132a9174bf55d1ef8880471fcb545456b360f2232d8b8031f9020ff952c21fc633a56c70e127c3ce2686e296f9a551b2a7c38e2a311c788fd580429e5735d4df946d59f1f8a305667aeea544d2dc5650ba11d94167c248224fabe4fb73268563e104132b7945f4944a814ef8f439c21ae1056e38310a4946336a535bda74aa7419074109de149853eb59d2048a59982f8abe823101f80209da650b927ad8f3f580d3f10fc84921dc4a898f4693efa6035f860353ef64092a139ede9257de88168f5f9c645e9cc29ab7948eb2e5de33ccfaaf24eaf9227fda50f3c10f76aab4aa86f131f77207c4cc2faa2ad1f76209dcaa4b25d78cd9fd50dc5471d08b316e29376d5581f4407f27ebe2d71154fe2630e049713337e3517a39ee440929babd5152ddd2d1f075296b1b958a247a5a7e1404e62ecf6e2b51f6f2026fbd7f87e354277ee06528aa2efb25b2e9552f7d106821ea135cdd7ca0692c8f4b71e9ac2f1b106625d2e5329cf5c8a22aa819c5310ba54b5f343bf4d0341c7abd3adfa95660e1a489fcf627dac31ab289e8198f4b48dddb6a8ac9d19180b99655c7efb280349a98aafeb759dacd46420c7d9ac9a4176a90cf23110542d699805216ae1430cc4f81f5e4259a792d13bc3471808bac13d4ded52a54a26163ec0407cfdda2edbb1b0bd7f7c81b8bea34bf8756ee80b6ee1c30be43c0b23a3a6743edd1c7e748161f0c1858f2db0e0430b1f59e00f2ca4f17105e2e69ab63b6d41ee345650def3b3eada3e3f5545db543e65f09c8f2a9063877c4ae541bc6d863ea840d41474780a441721c75e3c8408157d488118bf735fa768e2e7b52810454d9f7291660b3ea04050bf79a699f9780277aefbb26297651ee2d6da1783f2ccfc1de1c309e44d152c77877afb68f968024183bd09d7768c0f26240e3e96401e91d916febc73685f09c41621d52ff7ac83d03f9240d4a0e362ce8e955e412410ee4ce858f44e0f1f472067bda074eea9ebe87f101f462027d9f9b1ef4d448f6290ae754f7452970da5e2f02006f1d33586e9cba4b3591968987143479fc06318a4b469423e93c8d0a582d04039d03816780883203298d82453f2e4a943069b8d73460946fdc98312977f040c721ab57e49bbff054145b6b66c967d412acbbf5eb1429856e7d10bab8135f0e04542e325d11d2fc9c8f9051ebbb8e793bcfb67557f051eba40f374c642d4dddee1910b52a5ffe0a9da3705d1e082186ffbaeca32e5bc7b0b7267d18c97f79d3e856c412c7751cffd7baa345f0ba228f1ea13fde915a4b4207b101d57419cd0b1c6591063949d89ac7e859179c88220bad4e5c8a0c74f055d2c882995ced46ee2312a2c08caa4098b13d2f22ebe57103cd3cb3d43e73c5c415e8dd760314aa9cc6f16c1a31504959358f04f6e496ff36045fbbfc92bc5b96f156455d9b6bf96cfd8d5a982a0b1f4f5a83ef152417ed7b09f53389fb71415c4dfec9c63f46cb1a69d8218eefa4e68ec9c41a83c4cc17b65a6a59db8553625aef4681c4d7729c8a63d8aa97ad2a3a6f22005495c37e5d84dd19b725682c72848663d1ef62a03050f519036b869e7a02f8d672714c4142d544ca5336d52a507288817d73475cc6f63e16d82c72788415564cca42ad883f3f004496b924978a7453d3a41f6b9d2246ed473820727eafc2b9e6db4f6d804313745bdd3fedf53b28726cadefdb153d2a426830f356f7864c24ab9a284125f690b3c30a1566cce3bfb27f081154ca0f3e07109b2c655f1b9d936e119a804c88c72821f1413a481050f4bf4c0a3125b94207caef8f3a72fc594a1499c2ffe4207d714ae732441d0a699eee7f247d31f09fdb4fb3bd38951810449e7d89129c8683258ca2388f79d19c5e64437c81dd1c5daabdd65765b7d79e7ec7f41de3e66c68b4723482aea76c8ad476d9311445ddb4ec972fc46d78b207caf7bbaf476f192154116a554543a3bf5bd6c2248dafd728c9fca7d224490e257cee6858fa9510f411e8b99c383ec39a193862068cff0979385356aac39223c0a4154cdfc23bd5c47c61221484287f8242b5e09130f8268e6e94da5c84bf01004496b8ea174ca8dcaa9f408045145548cdc478bd7900720c8415ea520e393b65f0b1e7f2028dde953bf525e0dab871f8841c9a6e0493ece275d0c1e7d20e9a7a926ed0d1964d8830fc4f8f3b9298392b99dd380c71e8816542cfb7dca95cd71e0a10782ee5d586d0a3f56ca03e1c4858b3796c60329b553959081c1e30e045535a5e5fb82ccf9bab303794fc9b0f7acd37b9a3a10740a9584e9fce05ff2143ce840eceaffa43da7c8a0931e73202595732d737c10791d399046779d1ab9216c94e240d698bdf675720f381034a68c59cb2f99b4ebf106b25cca3699a2a653baf3700331ae840ee257b781189a733983ee3cd840ac2e95af4dc7bf51298f3590477e9af533a9871ab4f1ac5ab9d2b890bdefd129f622f74803414ffcf5c85c36163f0f349082ad9cacabee710682852ead7ecf3ccc402a4f1a32ca82d2e92c1e6520a7e7ff36152cc728150f321cc1630ce4a09256ca3978cc24f3d3808718c81f7f514ec8ce120d59083cc24010dae453a8fc98ff520c8e327260e38c1b64ecb8e1033074f3b1f7b2f99d9a21a3997764980c96f43370e44836d874f817481ab43b7aa5663125cd5176d8b88123f0f00231fdcb092927fb2ba7e82865d8d871a30c34747874012f612eda1c3bd0f0e00229e998f94d5a0a3db6401221e641aaa6cd6dc6430b2415af4559b01a3bf0c002b12c7335d9598cfcf415089f1ad393c554e2ceb50249937ec7c5038d29a8e4adc5620269381a8903e290200c6234cb0300531308001048260d85a2c158a0278a2e0f14800265281c32322622221c12101a20120cc6816018100c84c2803018100c0643a1504044932bce07066fef0303c11ff11f9d3889dfb30c4b3f4a3e670479edbc0733577a0c4bd77d5ee85e670879e8d0b770cf16223f7bc053f6b195abc85909870887b294a546888bf39175b7cd72b90f667cdae405902051c8365f4dadb69eea9403f2776c5704abb428ffd6f1c626b596a5e4bcb8b17aa8632e22fe70c1521dfbcfbc3af061037b4f467a220da0af29db8ba8f4a7564c1fa6d9cd901aee26bdd1ba2cc481814c302857da5619568ad7f1e4a2e75a200902f84281f602c92e46d636ebaac596af63c0c1da93cdd924970a4480c6655546a94a93b46a025740141e40b539b62065d686510d178f736baf1a7fc99fda72b77557b9224f50f4af7809f9e9e8d0d28da65a1da0c48cb64829495fbad0e112e280557e307863af2f410bc34b8ba5d2b5d0b1f5e3a96dd9121660e9f24fdfc59752d1424cb5ba63203741de0839dca5ac9a0972342732ab0967eb82b3fdf6e9687ac8dd4ec0276114ba00c5bc11c4327572bb3bd97d8c1a9f692d2698c68d37f4104bbe61c9a7303b3ad6a8f3914a2f25d0f105fa6f664028a01840da1bc71814327c36e8aca0dc813e082e1b555a3af6631a06d16202de3446ad3834666c0acc54128c381352d1cc02bf28a777740e82b418d1270261295e5eafb445276b01c269191be79b82c588e6e6c030f8c1da4cb67e54b6c0b6d07b368beb2d9ee1c1864e2e519d4de8d7ac2043c3604a861fdbdcc18c7b7f5180c9b936bf7df1911822993700952889173d61aa8f1a4d404fe5ab316d8473cd2e058dfe1fb231bd8b52810c976e243bb6cc4ad072988c91762f490cfc361a9958ca01ca611292c5fb5c115b41046422dfc4a246d3d57ff53bdca185b3044c0e7a20dec3d84dd971b2385e9916b654d8c334f8111b270c4bd58d4356fea8202b91709de3c81ef38721cee1730fc4b70daf121d56434cc08452a634c82720616516d2af59247ceebd3e4121b2c2f70d2c190cde740453be5b687183949117cbb135ae85e873aa98d7943107a7184caa1468a68c2df6be6b692823cf98267ea235d193751dc526e9804798dcc1c69b63c9206d5f068714ed37312519bd4771721a00b2215177291abbe7265b850f4d685e6f5042049f1e11846413c40edddb9923a4afcfb0ad89d9112a54fbfdb505339abefffc0bb7bd50c43e4719521aa8b2c2d0f3a2ae370eb8dcf76a1b78108122a1f994b610710cc1d5be206024814541c471b857a16ac0c00ae6baa4160b1f32593cdef769e5d642d45ae76adbb12e788e922062e5826bb7b798d89531413dd1d7b2c08305745560d4f3731d10609a1f60f24b5a140565355b8f38e0945ea476e91b71c99ec9aaa6868b74476f49054c7689ee155d5898f679f7ef830e44fec494928e6d9647069c7cc1f05e340d7adfaab8cf8d38f0f1a812853ee99c88af0f453d57e05d47ecbe2d8daef170d53ad76d8a772e8dcd905215ab7844ebf7d5a8a49d420548221ff69c4bb916be28dda17117588e456d17c22a35038c6eb3cbd9d257fc9f9038785ec4bd7df15d6a3fceab0310cb33ae23e760e52c66027ab73865a902a01134cdbe39b6ad2776e555b489d44f46f387f51efcca928070cac0d6dbfb85136369e55949746fcc81bcdd7cfc69b04616f54bd0edb985a158f181038680dfdd581619aa18acd7d84a91ad6e50bc7d8663b42a72b8c8ce419c624d386fab6a3a944431afa3ae1fbb24c027db5bab92767c6eced47d3e73a5a03adda3e99085286bc7b273f1760be7272ff2fdcc9a9b94ec28df682d6b517d0146767cd8e7d93021729d9777945fe2027fba39de8a91b146370c98d2ccb5bef184d85cf3ff24e3f171ac240a4594cdd65c9c443dd0d77dd53896fe0134160f9547134422bcc3287fdddb4481cecad4a51cf45feec3eff4c4564cc6b29fe867c7705c4907c86cce85a5e6ae79c799a569c5121848da4545ecb8ddc4cfd16ba30830ba32ebe2e497f034234b08990edf62351b8d700b2394347d16268c0a23777783ddec430992b2eaa9f40bf4b5d50f611dbe912799d0904186c284669b6912672ac389229047fb954d5057994670a618da7e872381f8bcaae9bffbe3d3ae97f06225fadd479e2965fdb4e35bb578809721ceda25a68c5756440baeeebd75a4d604db0ab993737ec22e5976b075c7581a7eabbdf34108ab28fae0a75a27feb878d4bf340013581fc9550915f98a3d2d6ecbeebe04c3975905267c75b7a9fb078e8d274383225346eec3ae5bc34e6042b9bf8dcb507f11e3c2b487e758ffd2583b9b2cda72a4cef0a55fe15d935a35abc3acbda8b7121c6dc054c465ae62a8addb82cc8c823489440196ef960297a90400b38d4c2b2193dc082cfecce00e4d3e9ba91fcb4a4920a63c0dc3ef60a72cb31806f0e410c99b4987165926b760035c66ccefbdbe094a59986be3aee4be51c4d16e53b2b5c08797a08746d5c000a7afc0c906e960896299cb0d4bbc3adfb6842c67dea212c7236527f5ca00f2ed44ebd53d27bcd44da15b6a18fa45496aafa8fa42ba0de9b87070fba47e2a87082c118cff77c54c71892dba80a573851e51700110d28c18c3bf81611729d7b7deb34badc3a1f1b4f358c0c2e6df6867cb9d752824dbb9f647ed1f5ee74f5e00961e77630012ae8360ee681524988902cf8239356bc613b9adc5d11f1b6f5da2c45e8b5da092d082fdc1a98ed5b88107a315e6390c596204f17b9533688c91899092b1ec76b11c11f27188b6dbea77f675b64f6e43217248769b8126141db2d7af336181c2f0c8bde9b2710b971e07445845712144f58b81a7fe47549e2b10c6fdfd0d545ebe7e21f5ca831bd0fad8f3d26b0ea4831af98076ddf6472ee2a532f044426a1b90978a5069af43084d048ee86a309e02aa5a26c228d2711461468d8b2bf25bc2464a2962500df72c6d300108d9ecaf9ca596b6029e5152742c9e2dc0abb22ee890b98a1294a5eb69444e46760c11ff2dcbb2190939ff2c003001b1946f7b6a82bdb7dab6a68d1415a822a8e6f5514b64e3e8489edeaf0cd5932f12675e1515631bf22911f4cef040f7e915da379964e66e9a383933015bf39cf4edd8f217debe0d1fd2411c83a5fb0c61df22c43c481ec2b7df0956b86e597117d26c75a4a3f934bb6494f978e8be525f4aac458011292dfc5a85db1b11b1fcf68ad2fdbbeff640bb60bdd4c881220c82d140ec3deea901a6db38d540a3063a92e2e3d8d29316417c39bb87d8a1f2e41d3702a1f0fd27d36d940bc6acfd16634febca0caef30234c79f295520f4eddf7a68be4195f569440f4e62e3a9d26212d88f4c73242c072a29e94f5b36520c9a9944173433b44e97961a5babb38a76d8f67aeb1ea973434770f7190d65bb56bed3786ad40eb248daf410e57488492d8ac7ed9050b846e6e6c5b99df6ea3e155110e218943b99df6a1e78a70ecdca200bcaf0e47b4fdc5eaa4f85fcb648890e6fc51301db6d4e6f71255ad2aed3eab5932d47d66bdd7a98965fb868aa0c27f811310efe56cc8e6ec7dc087a9b52584d5288c28d14aca24c3341259a4a4a68c86ca276f855539c81e9b65e22f67273d1c7e1eea0f7d609b5c1f420a5648493a7c82191e8fae335643f2ad93455dc95113c506ebe6186525c978724c5d8b0a4c6d9fce0abff4fcf0f3379461c111473a9f36ef9a5ac616368303861d648005d9b8e7845454ed5d4903e1ddaf502ee6aeacb4bc14fc2e19651355e6cd365239c7fd3af535a2ea9ded3c2b1a3fc10ce0580e9d99ed881fd647479120fc36462618259ec23dee403869393efb9f2a903f83902608212ce32daf72e7d6b011e23ead7895e7c90327e60d3b1a5da8323f2a5f6dc7de754373be5f383bcce2fbcac543dd3bb8dd9a8b777807868eec247d2ec7b01d517c9e33e3354e3445e5a85a16ca6bf8485aba962420868b8fe5dd17af3790127e95136d51b15ab0ffde8fc186d56a0a354d21882f3d086426e03711c695b5d5b2aae89422d9230b0c464b5c1e35753db9c7e6768f31a2eda8b1ecdd7b6277dbd10c7830f332e982867bd9191b10c758cadfc5f868e074bdd7902432def49522a0e442cd3e00fe22a3e9c008a8882cdb4bcc001e074802af8cd022338cb60b45064424c5667de87414d37fde9f97b656cf8a9e42b6e660709875103767455fb1ad31f899f706fd729c0c0c3d69b703087c6a43367989fd3b74a7c1807fa3dcdc677f89e650cd8ec4061ae2dfcad7183d00becbca6e94d0dffe5410eeec75335fafef9958a92cb51785cdbf60deb086023123968e0b642be2c0412f45e4021055db6a5966ce57a7db88a068776ad98e4818ad39c21ab84342d1ac04504b94ad97006f07200786c5c7022135f190ca6d7ab13b1de7bee3d47510d73cc42cbd487ba677d6556105368e74b02171d61a766b9e54ddd720edb2dc9eda484f6fedb874c0dca7b0b0809ade5d88598fa15a74a1c7246570586f2450e966109be4dcdcf6a7543143b3f1b6d942bf12adcfddd5e927c88f9dcea2b65f4a8796484d912f6e4b090a016b1025fa518c092a52ac34e9721b020f5892620c1be5abafe8593374e5892b3dbac34369326b47e02e88dcd10ae2f46168f814e5da8c567781582384cf57326c48e81730488de44b9a8ec879b3b1d0898409582e7f4c63597944009f5d4de3389097644aea4a8195bcf04f287d6d2496065902990b0ca9a3c21976421210ad5b2f3baad261a9912a359722935686a265a417d65c1340e41bd86cb8fa8b0e195769952739517549f02018406edd99564336f0d68e1114d3a1f49d023ecfbb056e13c886f3ace5985c1dbdfecb59bbdd33fcd6aeac504a4176fa0db62c29302885734a1d333c44b6a751c3a301be55ccba68b38b0b7d23d65635a1f11c7c1cbcc296b8ec0d864c1b158e36821b187ac835fc6b29b4461819eaade0b47415f181d9e576ae8946d35ef7c93ca80d6c619156a274d5099eb3d0b8ec06e473f2973f8dd74a398489da11b2622c776c753c7f96b14584c9f0f44def1e32a03a52e3fe4cc481ee7c19a0d0427b5b7ddf10df767c68210c7dc1f1786cf99bc7ad45e99404d1e4ac79d40cf56a93a83ed4d23c746a97bde8408f0dcc0566d3a2ae39e2eece61cbd32864bd79cca0d2491cb67c71f8ada966227e0b5e48627f6ffd45f9900d60519e3d3ebfb7d7e8364c91328dc963924aab88801ca24b4a56dc74a093dea79e319bce210f7cdbe7e91da78645369d5eae03da6bb17a2d3b0a5b2e6a26d55ddbe843902c2960683353a97d3bbfb2e41d668a7203f6549f4e205c6ec908a5da04b5eb93b93891f74973cb58ebc320f8d572090a287f5531d576fb3a95856105aed3f62873dba5ff7b8004d895be3a4dcf0aa128bd4eeb3911724c2746357b02dcb4c05901652e8a9b5fa03de23ecac378f438785171f3743a4d1f7e23d91c6bf2e94197665c28b574d226bc136e19674e9ee66b8ba29274997e12dfd0eb7a4d0218c5c6342261c2463e0e0c45fe1c50b8c64ead59f263f528fe46a765b1077a8580ea9c496a290b6da44b7cf415ca60e624db40cda020ff6258e2906a9c53988a1175a776667369348808b230b52bacde0ddc4901b6a98810ed765dc4ff669b2d3be0b5ac0061a707d5563ca126cb0cc8d44c0c04154d113f8649504e890306fcbcfdb1a631234d0d3d7113fac59b62a90d0ab8c071da718b97d8412c64a17833334a905b15d37e1b9e10d2e9a4b83840ae8490a338af30e7d6b3fb372a9f69ada74bc4d1579f478dc6612e6d9cc956a0843060bfd91c14f74b29ee532d805b4f483c0be262f471ac3ce8d11940bbd912694d94a6e3ef7357f0baee5f9322cd7568f1ae26c276d1631eda438101f0e53bc04aeabf6dc94f10c85ac3b522c60803279298c060740d402ec4cf4661bebb273669a2243b67ebca222d676bc2b99dc71112ebc4022bee6b70873774f23ba665a84631592ca7d1934ec8d083092ba95a16aa5d04cc250ec923ecc6173b05288d2e132791f66385212f80b7b66966252e064e01384b748cb4130b45513980ac92589a71914f94f549ac9e06dfc496926eb41be6378ca4841b9bf0cf7cdc39ec5ebf4024969ce9acae021c27a5c1f3a5d993727c471b29dd499d23f42a700d78855c86ce0cf2a6272c6ddff3331d3a3b0fdb340704258b95b4449af0bb474da3bc5818d2de2574548d208911e8ff18574347faf4f3b217d4492ccf1fc65ff75387d36ecd3a2498221822418cd149b06624deb422473d8dc30186eb066080f602511d444b410473b57889ff92cc84b3617a93669bd329ee5814df7facc80358392b3f149787eb8bb9cd68d4bac2f8ad8b9626df7a90c1de6ee575172aa93dc460227c6f2e01c7284c6c2840b93f3ded9d2b37ac3ed340dcee67f8fbb43e3aca7095c44861b888bce7cae1ca96374e8e993fcd4cfbf5ef482b804106d017d582723473b12adebdc136fbd40b00d642e7fd0c88e5b3ad36b39b807fc5c4f40178318ba682d5b26c9dce39e4672406b1c92b80b390f0326656444aa616c663ac39a5a82215ae784a3f0a01c05146140098a98d5c9cf0015ad791dfd48857b25f082eb6b22d89ad3a434ceb1e3f44d6b7d81ec12e2a8753559ea13de01cf7ada2699596203d14d2323c3015ac0bb948f1d6158922358d5bb3392f0a0842d79f2320eae6003616c63806bde99a45fad3d6cc0e4965469cf5118256b1ee049d7d398e863959878bc6d1adee5456c8a0135e0ff118f8f192d84e93e42ae9d3831522d5fa1fa65467a84c207b06fb66df9e7ce9d6d058c8e426705370d1564159a9ff8008ac239a73a9313feadbdae2a54bbd9ea472b5ca9f033c46c46119440bffeeca85081f94b214b69a3f64d1cbd82e2d44c58a9f4f031343743430dd4eaff2908067ef30b12fe24d5fcfe798577cb590afdfe632167973848ea10986f63cdeb2914d9cab14434c2400320083ea62ab529ea8a3bdcb48fe741c4ee6013029d7ab0e6ef3b2018b5255c5802805e9ee1e1ba40551eb7abf5126794a0cf87c9bfa7bc6a4b247ed3a7d266ca7eb731af1c98d9e0f4b365f03225a8182a6843d23f2b139aba2fd3bd7a72b6210d64484e7a2a23b5df30a7cb605de1c941f778f80961853e00bef583ee941f79c6ed1468c742662ec7ce6237096ae3a1e8cc3bc42ac31ec427e3af4ea5a355594792fcb693410b0a9f4b5467f683b90cbc35af7b601f60c6e1acd003fcf1841514dd57c6628c6c093b61f460fa755ea7ccf4c10c8ed88872dcc697fb3971e4c0f683d6a9758ea0eaff74d06c71647e59a418fce529d916a236d006011a4a7ed5ac5d494093c66ed378b230abcbc7a805d45aec33a552a0118313ef189c1d2a97572ec7516465c660bac060f45cc21d2b2882edbf36c83a22579ab2f958ab1a4b510b6dc9a478b3ca95e6fdb1138a57fcb0b3a387894072cab46753fa3543e28b2720b544c2a6fcf737ca0978963768f7fa498eb3dd6f177976c8b465438a0c9bcaf1af2789ca1787d623018a461f70687cafc4dfbba97778989012ebe1e7dea188c274bb454001da9be987c1b651c56f01bf50b7e49abca086c5770ff586b927291b67eca844ad9f523f1008b05d14542206646da77204b999afe2351a6cdcb08b50fbe4fa2d8b5d84ffae31e495d694bf248763c6a7a028e9883cc2a53cf62a2bf22fba16d3d6a8466f707107bbb9e1e7ff0c5c94f7e46ab148971b8ee2b8cb36faad4b63100bb6ed1ecf93e49e5ce419beb13533ef161913b8e657ff3c9c43da07cee679e77d190535267805851c9522f86f09aaf4919dab212a520fcef3ac621f7159cb1c6157689b536a4da9f766a71030ec5a513bbf1fe3656b53cadc6233e519e1e1d79a6257138b0c1c068680a9d6fa881dcf740f37e60a2fa95eebfd090a2afa929c930b69232a23b5c6bf93d419a8135d27cde5e5bcf8ccf24aac8fde9ab1e0663920277876366c179e60609615301dbcca487778607a4a179700c31e1749372b0a6f8dfc5309ccec1bf0d29425075f28e6cae7e32251bc89ac1ca3572901cac1c5915151c6b67e06ed234725c98b3df4e7e0c27b9e3ea59461664d0ad6e67fc5df815379a6b88f1889fb8b5f8bf338af5e846fad7510404928b335e8fd00a50464552eef3381984f0ee65d9ee6f37696dffb937116e7eacd39c133d46b8d9ac9e6e4c3301f098d5c90d19159204b8e33524954924e6e35c5af4b3d8f47284c143185debc63cf543251f6d0a45cfef84e2916fe7788f91cbd685c040e864427754cf1856dd7d6312c715584d036cbc9e529df8f78b58dccf47848a64a72813295df735af681bb848d2ee7ac85a5a6790af921d6fd8a3839702aec6958a838a2e5d830db461fc27970f56a9ff6cbff5e61913bf2b5e0fd30e35a17a49d52baebfdc09f8ab9274f461f497bb6eba4d225f1b27aad56b34d17e28b709217cb7db9029f59d4d15bce65983f52243fd1471061030e591eaff0a96c9107120d808f81aa76ec746d70c49d35d626254fca62c34f5558a688487d3cf19a529b96cce46c58261c1a58b16b726ebe0fb44e571cf49c5ffd53867d2c446d200c92f1d2b2f792627f3c1991012b2f5a01fec87c244ec5393d3f7699bace9b59872f514a9a9c2b37f22282083a6708e497137d0a5619ea7cf3a64b56645fad3f0134acf724dade07e42cf2222b390c05720d6d0a46ed6c0ae5b09f556bd1fb713dc70c4b7c075e49eb5c54da3622d652bc72d99871631fe10b2e4c6e2396cacff617e91d8c3eb05bf9a73c65069bf2293ccce31ab2d9c57ac028b246dfa1323de5b0f35429d313a760f956a5929c82600b02ab660acee4a0968f55a822b6967ac6253f0d880fe23dc5d243bcf3ec2dd112716798c51065c67ef965d46e9bb79806134e1aff2a839dd02fbe86d58bf46e245884b52ed46378c1c058a897e4bfdc8336814033dabc5bb8af792ec057b52fba2bdf05027bd0489daeaeeaa9ac11aa0aed9f390dfac6761eef6fcafcc18c3df59d70e6e1b975e3974d1af22920d3deab24f731ba74c199382151ffb9fc03de46fb77a68fa88e797f301bc5ff6fffb8c748ef15dc2aa02afbc66818e6eeb14c37e114cbae2476962dbcd66a9d8807f71f60297e8ca89eb7e23e0157da7f697d426a748adafab0bab1637dd3b0b553f732bdbd274aea1dc9a2cc2b8eae52495801df6ebfb4b63f271b9d309f3242c4900e494c1ed3d141f612713bff316c6968a0199a43549b2deb429de218ef86902048e1b5cc2944abc4c63eef25c2ba1bcb680ee3b7d78c7d6168973a32df5a428bd499244199c66a91ee9ee068263fa6462ba799a8752e723be31c45b23e6e40c73dad314f2eea937a635769f7da508f1ba00367264748cb68a5a203afbf3b4469045b9640a119101e242e7db682e77c367093b39bd9aa0f3be17eb095d4f45400a552141777ba522caddeb0a8aadda77a41b920c3561f9a28f043436c2dde10745e4ce00b74741aa44c94b67e612a8423305440bb54a067c35820066773643bd2655442e291b085d97f65e1ea864aff1093a7eec6eeba2242f82d0b9653f84179b6b6cfa1efab84eb403d83e2a261d31db7dadb0814fbe4fa64664eb38519571639ef2ebca2041324471a6e631105f338c23497cea88cc4426d64709ba2ead38d69931b32da2ab74139855d33247d9dbaa8c9d351ce3ad857bd1458e44583759d3fb50d16da62d508dbec3072dc92154a91be59096668a4102f6f09dd9199e4bd9dd15f04b2d9258737602584f3b7cfaf4fa1ffef459870e874aa52436ef1bec08e6fe2566b07c191fab2c019eff5191b3acd8813ee3551fea999863db20b5f894b5553b34f85fc9bbba1a8a490e39201073584fc4eed9873fb85c011fb80ffd5d7d6ece18d878ac0d565db715f84a809e2ffc574a750e650bfe2b26f855cdfb3b6fdc08952732e78e64c697cf041bd00d50bba26751106cf581dbb1bf9f69f05e9fcaedbcff378cd13ebfc4818fa10a86fb1b7112a15ddd19a5d1f765f764259f44ec92df0d18ca9463deb4bc5694f070c39d3e581b853f4ada70a353c00d697b684a177ec7e4254f2a497e45aca8eae11e706c763a5d2010bc9c53b02466dd6ea4df68bb03608aa6328703d0caf9b4232f089bd76c188fbe4421396082f4230c183af89e58981954432d444c8a345a463ba21cdfd067542e8ca671587e422431a2d581a7432f711acdc9250ca406da4e20ba8b560e4d2b50ff7942a5a2f17aeb66ccecbc51bd599efbf63605fb8df38300914f811c2fb519a1a182427bf9a1492974d22189edce32ac2fcd8e08572b78401b46753f0af5a9501342b5f73f4bda4ad8d6a1b8bb1c76c798206519de02b5c3593dcd1faf046be35d54ea47e6914d01bd4aa4bdaed451d0894a8c2ebb99ad0b174eec3e194e55dc2e5b2b464bcd05662c9040b61316feb3f00203879d90b06e05373433e85ab8a3abde181b123c625b2a7db1f2e496cd6d8d5d7173ed37ab833d4d8172d29efe1e8226f6907327f6f4b51876438bff7d61a121706f78954cb01adbdb3308a6b6c3db41282f70c9d3892c869dff43a3e91561b199344068f7385b08917c67a42653dee7510fd4ed25954ac82c1f2261f9e484dc70b525392a64dd4c3e260b8490f8207f7939e45d42ab8764e78a8d222e9d8f9dbe78ea57921b51b3e1828f66a1c6cae8393a159a7c705b43f3b479bfbc0884d432a45d7c158f2c59c40b1584b7e85bc2b42e0cad3467c784a57038c89cfe69d7bc99fe1b6be71e024844a2ca4016e73150a25f67861ca23d2c92ba0b6112218e9f5fdebb547c933843b9c7b6a9899179d2291850a40e7e88076d9f7be3ad8e75b42da4411831fc8c7c786b4c0076c26082a44b9bdf4683ea83d88ddf48028b1888ce18c9e1bccb9bd483c4a07b3f311788f3c7812111e63bd498cc0bc4dbc588a32ab218d8074c54fdbc6e9cfaf07aeaa0032864f0bb7c101424c504bdc332838642edb67b2d04a1c9ee10ea60a3e32fe51b5b7376de42f078b1ffebfa48378b70407d808a54b835f188f4be8d90cb962a2efc34855d66b93167828576ee54cf895a068edba81f8b46281250e09e462a7c4565e315590ec525ccf8df45cf9e55c6b1befeb1de10117408c3222ecc6508150b7ff4207b90e3d67ef0719749ece71440eca79f2af172738795e0267d199f76e81daaa19569323b64d86b4aedac7be78771576c429fd2ce8c86f338234c371499603bb4605a808783acbc38c4929eb5adc22ec3e177b3e4ad89f6bbf9b63e6e2d9d1f10b9eb0432fe428672ca5db0d7948d9dd36314ed7e0645c9e05d0e7940f2a748061bbd0ebb23d0c032aa9e01b13a099752d9973c9209963112a999e2830abc2bfcf2e9448a2618218cb9cb9694add0cb852fa13e86f7748519bea86fb98f7e8714224220a0d8cc7632c299ad6e1518037a6bd83266560f546d43c14a98e59d332dcc409f0d131f679c8708ec41eee3a57621bc9b1e78a5daf1e4f1816f7ce68692581b3accb433bbac62b18c6d92ee31c5b904db62af3dfe1ecb541eeebd1fb06e500e3db1e03901df7514447eaebac56bf0577039e89807594c0bb5879e2d87a82502101c4c46043582343149bf9d062c35fe190f329be6fca881252623f451d823f7e6dc6d96cc2f202a5e17bf8147584e82ec11acde393672cec0c7b60a9a0b86e99fafce1d61e8f3c6e11cdf109ce4b06e2491861feee21e71143c9a82730c81ebf045ac5aefb6d1748112203af1aeed4c06a289381feafcbea4a602f65371d4bb91920baaf105b10ec1d8aed012e98554783fb24f8c3e757b89da60de9cde2500841ba217fdf43d39ec66041097b6e84c20090c23860bb284a02660f080c53c13b9584bd1cac9e3dcae9d051a92704dd5adafe089d0b40bb7fbc537f892dfef6f22df8200a55a9278a93339c448adb0fcd6f190ebb814353b471d6293abdc31dd88d6008a53d69347ee9e411a10ed2a5b322aa0322e0cc1acd92d0c6d5a5bd3e5208cc1fa0b487b6fa2225ba454b2dfcb8e83dac4183f5b6311c96f53341d237bb9e993b212bb6170712ef5a21cbd1990c4fb675aa1c5077808ffc518f2a16dc253e1ca65a96c3170f338939c54f5c65728e7a608e41fc74c75c8c44ddf06cfe3dd4844a2b3a9610c765145810f5fde301700b690fbc4a9c122c01308925bb33a89dc237fb8e985d19370aa7fb5f54e94c5220f4fa297a8e8824aa41ee8bb675e9be80a66d3451db297ab9b4d0b6e973ecae4b014662e60df65b5ebe3e565af545a01b282d41e803b828a7bba8f4202a18c22531fbdd153ef08e99f8a24f433d0b024ad59213bf1d5150c849004b3aff41df0ec4a10f9a3cf213476a81456fc0031a8d37219d185878a6f90d0ce7b0193aa3e2d12074e16f4a214d70ca2142c85f50059f41a30e04fb1e7b988b2fa871969d3197dce3d0d47773d179d3a5f856f795c0d61cde365c34699bb2c673c346b2d75296b2e471edb09164e42a25e7a4b3564691669a6c585bfffba718796d0f64033488ee54b7c902375c7044aeb3d77e58f50e46faab5ee720acc6c60107529bc08117d48207f40773b5553e56959e34fe01640854564bd6da721ec652e9888ee24af81bbd4d0ca6d5278bffb798cd0c97e6bfdf922dde75de337275437f5f1bd733ceb98374e4f83900bdef5cd09063cbd02bcd4c0c385174b05058a116a2a0bc155ded02123a647b9f21044cecd3960424d70a66aef4b36bf408b37f49d00a728f5f7f4350b71272e36667130e789d27aba6b66f6e37a940b4d5a683a6a9716a123262f55d2116da74309515f25766a5473ea08ed1b8b1e417c80be254402a8e44d8ce8a96cac1207408847c547aa74114b483a682771ce5f9330424dbf8da2e2b306a8a31ceb4428272aa93df0b069bbb8cc7aa8cc2b6d94877624c59096111581540161b73998ebcad9ed0935bb06f2927a337dd763a1d95c2c4bb8c656a86460705594c6c0fc523c6a7829dd432e5e82dfbc1b0c4c858da77bd50833afe2bace65ea28dd3b5cf70f3a63dd2003201082f40323e9d103b85542266ce1da1bca00413820bbb0182599af116e00896a8a7cdf36d15c997fc03a1ab301ee2b6daf3c0a55e5f88df85989a1ec298442fdbd03157689aa7df5b29cc5b32c4502d890387c900888887d23af1e1b07dc0d4e8ef180158fff5f89d8b4d093a6aed2f3992a63e820fd3657f500b403dc7fe7b4a48bb97bd7e142d6dcd614cd67fea9b120fc57beee57136c945991c931ca9c4ec57c30affa2bce01f02e6fc34cb83c945b01faebb4c929a0cf519b441fc1870997b319ffbf6cd2884e2dd3734bae1e3ab2937b1a2a43c36c5e24850e446d3447df4d214d2aa8a29e3cf55f71e4d2742bd0919d0dcd4ee9af06b717bd71aa657e8075e0fc440706846812e0a0bc351bbac3fbce92331d37c2bb230825e880282a5aeff880f213dd821db2b37b6fb485e16872ea5190e1548df147de29c9d72f0c178944cdf86ff14908109fb300b1d57f78487dabeb62137c3fb6b254b2f1bbb1ba60bb8e5462c3d0e0f68c16fde1f807b0800f254b42a9d47b9e1be9f32912d149cc3320f6e6e69d52b19fb0d92d23c9fce91f1699a603e82497f530c2a5255e351154e91ad07bd0a683558ca681b9b874512c12ec7b3d2be9d75746a14376f59b6df816bb8dfc7fd7bec4ef79f8ee93b1bd2890fc6fc8da7dc6fe8fbf737735ac834ba99798bbc838e25506c79d048f9f656c7ca29496e7b6a816054a0ae46858ac9dbc89d97f967311540ad71508954d4d90e49c529d4cb3065f08000e747c88ed9db4a4ede9f042b2640698e3d2ec24b13", + "0x3a636f6465": "0x52bc537646db8e0528b52ffd0058540d061e1308c819541050e938e9005f822fc197252fa2ed5713e0ba46c46f094c0fe2307326fb057d60bbce8bdfcd175f1feb2bc7604d3068f26910799b2f5ebdfc5148bbff93d098c88f368b8ee8661e889a5a2344ad1142f6de72cbb41dfe175118bdb120c23497929fdc3aea901fbf5f6ec5254f62bd59323b7e76fc0f30180c36cf35e4cfafa724fca1e767a5f2247e3bd43a5ec62ddc3617c3ae39462e821cf3b8db150a63cb17f9629075e3b88be0ec6e85aac4324d45eb2b34294e761194ed5dc9ba317da543ad5095086b2a483a9ebf1e0d13163d251face33b2511d6f11c590cb7bd712be4d16c7cfcf8507e99b5e724e8c2c797dfaf4703ff89ac1bb3f6437c14e300b805fae263731bf4f1f773e356fc9ec166e38be7cb73ec2591317a0538ea782e76128cbd5eac1bb3e28798c16556fc11472adcee845f6b99156ecd0fb7de2f032df7f0f7dc027dd963731bf4711b91be8d0591456f2c88a40e53cd6fa13706041acd5e92a22f7e63b0a4e62fb7b6f327b7b2f3376e6df339f6926c75a34a0145c098b59d9d87028ee8b23edbd12bc05167e7629781db1b189b8b5e762e038b62678f1e95249359f34abece1e3d19b6b3af97649915761d3fc41cb71eade151d36f60b8f5f60c0cb32fb3b60fc90d0c6787f345b2b5af97847e88396e3d59c357cb6f60f8de9e81e1326b7bb8cc0a7fe8ede1322b9cd2dbc3eccbac9087de3e2425a8552aaf96cfc0ed0d0c717a9e8b9d066e67e0f67a34cc961fe221a62015d9f2135c66c5af9744d6c9acf8211ee2211ee2211ee2dde1fae4ebd539d2a23482db0b7c1b03c28b0ebfe6b3b81576cde7afb815b29acf07c2add8fc8f5b1b03c28ae6db708b9bbf0c145d214ef3b98d59be8d01a1456f875ff359dc0abbe6f357dc0a59cde703e1566cfec7ad8d016145f36db8c5cd5f068aae10a7f9eb29e00810accff61077dcdaf37787eb9321bd3a4731c61170bb13bef9dc067ddc027dcbacf06bbefc7a49e4a307c30f30180c8896e7b85b6ec9ef27b7e2f7eb6dcce20fb1043b66f15b5f68d3fcf8f9706b3e67c3ad65d69ebfe2566c3e0eb7e4f95cec56dcdae6af273fc4433cc12b396de5a863bd59b275fcd6b1de2c899f8f9fb528b6ac9359fbf592c41ac68e1fe221de1dae4fdeab7324630cc3ed05be8dc1c674b8f5fee3d6c660b0de6fdcda184c4cef3b6ec5de2f03ad2bb4e9fdee6c7df25e9da31891702be4dbc7af895b6ec5de4f6e6defb9d4722bf67eb9c50fb7de4f6e6defb9d4722bf67eb9c50fb7de4f6ec5de73a9e5d6f67eb9c50fb7de4f6ec5efb9e8258975a34a0145c098b5cde7a18023baaccf3617bb08c65e8fcf71b7ab5200116698b52dcfa3d9f8e6e5e3a59724d68d2a05140163d6365f82cb2c794e76118ccd1e7fbd599759e1d6f243bc5e92c92cf9211ee2211ee2e815e0a8f9eb25891fe2213e8a312281db9df02dcf6dd0c72dd0b7cc0abf968f5f2f497cf460f801068301d1f11c77cbadf8fde4167fbfdec62cf9218e60c72cf9d617dab43c7f3edc5a9eb3e1d6326b2fbfe2566c791c6ec5cb73b15b716b5b7ebdf8211ee2095ec9692b47cdf566c9d6fcadb9de2ce1cff3672d8a1deb64d67e3d25f15c437efc100ff1eec8fae4bd3a471ce30db8dd541db39659e1aae3b9de186c4bdb302b7e71b8dd541db39659e1aae3696f0c066b1b66c52f0ec7edaa14408419666d2fb3f6eb2599ccda0ff1e270cb5cd117bf315852efd7e356e88bdfafa9631216860f4edd83ce41abd13a68327a072da6cbe8a5f6a2c7e82c9a8aaea245d04a7d45b3a09f68271a8a5e419ba051d056b414ad82fed253f408da4b9fa053d04d74145d8266a2b168120c1d61480a4346186ac1d00986a0188a62688a2129869e184a1a126208690836c4652888a12c43450c6d1902620889212286b40c393134041235903c61c805487c80240b243d40f2c41009864680440b245420790112162099029215201933c4c51013434b2081028919244e2079634889a118209102923343251892c1501743490c79602836d4012450401285a12c5e38c10b2678a18a17ac7881042f3cf102140190c20b1d7821891738f0c2085ed8f2c2102f20bd20c40b5c0270e6052d2f30f142065e38e2852502308517b2bc1002a12d2f80c0881823608cc8c0481746b83012331203235b18818191258c28612404464060a48b910f18f180910e1849c2080e8cdcc0081246928cb4c048164658606405465460040b23293042032333307285112b8c90c04815464660a40446bc18a1c2c81446a430128511288c3c61c409234a469a30c284111118d18091238c64c04811468c30328411242129084141e80da129084541880d21324241105282d018a11e080d4148084263082141e8084261087d21048690184246106a430808425d08c940088c9009845820c485d017a19810164572504409459650240945d628f28322658a04a188108a00a108128ab051640845d428924611342217518c226414b141911914b9a248098a3051a489221a28e244101482a280248a201b048d0962232808414a415d044111a44610164132080a139482201f04d12068064160826a105446d00e82a20872220809413a08ca411018414b41610489116482a02f415904b1200809a02858420049517900a4045406680aa0250041f11346eb07401c58150174c40f095400c12a06ab180f183c39e02102cf10789cf003e907179e1ef0d48007053c38e0718327093c25e07901cf183c51e0c182c74c4b07ad25211bf0344128072d295046402101750454191796c0c21a3f63fce8c00525fcc8e2c21178622eb481da82c81b2e3c41c409750d17602e34f1d1c085215c08c20511b82044f5810b4bb8805481e08216224aa8648834a10ac10522883cc185225c30c28524178e28f2c505257e3ef04345902578967852306bd03242cb0700d0004bcb4f976f8c1f2454434841e1a7053f4a081102644388078418d1e241ab8d1c6bc82708f940d0195349c816211a10da819023841421444b3542103041dc087283204a0862031e292062e6071541ba207284202fe081823ac6852d41b8f831c50f2f404c0419230810829011b425480c809a20b20422653e190429838514e48b54526a0341c0d049d221426708f0043a45e820059142903782e800080aac20829c2088143f26e0e1e2c7169e323e4af041c20f08843ec003854f0e7cc4f0012386e1f3850f0e7c6ea093e54710d38a59029f297ca41842021f0e0029021022107183e7083fb4b0b0041694d0eac124830519f820a31261853456f8c1102b2a1b1f1741b4f8baf8624162f5072ba011248b6f065f0d5618c3e2c21a6285355cf8000e332b90b1c2182c2282b4aca0031c4ec0e1068e25e068c20a2c5841052b60b1c209523be811420f1b3d6bf4008195a5e707ab2d5a68f08029728691345a40c287831f23f8d1c68f2a3e227466a012824e0c8428810a03480f524008d9006a0c9d1da890e573e2a7043a63a07290030837425c042e2041478c33f121015103e5858e18214ba496a013068f1428307472a0f3058a0b14193a660849ea59d29141ca8c1e3474702064089d1a0871e21b42870b214a3a6ae4f0018f255065f48891a30729188f171e12a49c90638c8e0d3e2656374829e1a70a211950b1117482225d8474d14123a5861134844cc183c42304cf123c43f06c8027032b9cc163c40a3950010c0f123f849848b49cd032f393c50f172a28a1420882941094041e29a800fb89e2a7091e312a00a113849d2738072fa080c9603478d0688520880c74cef0193a3dd04943e70c1d1ee894a143868e0e749674c0d0f142e7063a617468a0f3021d30421ed081818e163a2ea8661089029129d424fc90c2052e3a42d42054346a11826c51d3f001061124b890258819d5093a5b884881c8997a847a860e103969d428e49c51cfe488a96fe4f0a04a21c70c1d580e1a750a150a393dc8514327091d0ee87840a7034156509f20c484da84ca843a835a4695811441ce167507f104750c1f2da82ea82da85dea07aa076a524522080caa0682b8808e01ae01022107991c43e05923958194065e30e22ae019806f0cbd0146017c0268066402e8469035c02404498347093c4cc821424e16392ff021c60520829ce1c38cda868f307c90f1d1031f3bf021860f1df858f2a1860f367cac71b3e5468b0f20f818828f347c10c1c70f7c8ca1f162b6e18307467c80030a3e5cc0e30394192d245011f858c1b7850f27703081c70644924084093dcce078428f347892702841880b585b70a0c079c247042a984027081e58f058010f2b8a3cb182014f153c3c3064044143f06c2124043c2ce03963c71ab3055c021e2b8034b083044259fca000350321483a2dd0d902e502941642885829b10a010b3d70818c1026503400fa802a08a830281c0829037503d45210255a6c14f900aa8b16115035f844e04211827c40052826098480691d61c7085a53b4d270c1084536d073860e135488828519804140f1a025a615041e18fc8015f1800b43f09941cf98a012b4cc407db1a30a1f6078be1072049e32563af8b050418a4f0539d40032a2550625c6f7448f194538803a83052f78beb48a10c4033c34d089f98801cf0a7c6cd11a426b8d9f255a40a061680d641142a490460ca922c719201200788206073436a0a901cd0c50b11f1c4c333c4e309ff0c1802788ef053c598814a18a09c2021e26e0016285c42a690531448cf06941a48d1578b0eac203e3f1658532563063c5019512aa107c6204694155a30e8105848f1c1081828f327c8ce1e38b0f4ced4145c21783cf8c8f8ccf8bef8c0f8d2f8c55078a8cf9be586d60e5812245f896561f5825f1811157f025a1880f8af480474b1129725890b3822152f0bcc1d3021514aa27544ea894544da89810c225f584202f3c64f068d11ad31202cf09789ef0a382203482c4f084d152a3452648972244f894f840f07529e2a59679210531073eda78a10924357081083d6050628286904283c708423f0842c19fd013868fa4a0275464503040bde0052780422089288244d099941b2b2e90d840a806b209159c10c281540f68085a67f04c1174c52a8c1ba41f227862b09a0190078a74e0e70b214de4c47eae6829c132008b8002934a4308172a3ca142132a28adba102ac20c41cb891f1b08492375868f2c7ec2f08c19c2e2678a1f2b86ae98492f7409ea814ce24acc0cb85086b9985c5a55ac64c0c3c62a8c0a5d540862688bd5172b1b04a9c0c80f8468f0a3082148a06c303520991082042439e03933a484a4053c64864c502489151873073c66e8fce0c7892240f020e167053f60a06200d401214fbc80444e0d82aa081a411011281df0986045831f303c9410d281a0229048114404432f08ca80500764077e66200404487080dac150133a63e6153f2ce091411130b80beac40f0d907c21048a1734304df033022149acc07c8919048329a22444cc0b53acbc102202236adc3c010eb1c2811023ac8cf879c190164243042d41052682489033032430f8d1e2c70b940c7e4280440c242e688901444c91a4202e48a658d5e06749880c90a031e4e587091d200cc160471143219847f478a1e38322301f12f85ce163854f1540ca40e245881a42a210d2864e179405130a730a14080a9b676810f38d1905fa654a8166092286e60bca058dd118b89084173e20848a1c19d02d80940002c18f0c78a2f851e2e9426805422ef8f122d482940e84d0f801c48f144265f8504a8991c3450e0d72c0e4d8a08523f050f1d3c5cf133f2910d2418b8c1f43f08c8047053f30f8d9e227c623841f46acb4f889c18f0b7ebef064f153039e19fc40c1a3c68f087e4ef07383203378c204112148084145085a419011a9314156041d91d345912a5e4812f2414e18540b788a60042674c6678b10117eb0e0e1c10b5f5059a888b03ab303881d30006c00001c08a2250897205b88c4887441840b16c06061898530762cedc8c10e318884217203223658018a15a458218aaa4415415da29aa07ef94cf09de0fbb283cc0e22ec18820a6178005104043c82f834f025ad0066051aac30838f042a7ca102182b7460850face00115622a74a10217428620a4084288507150bfa85e0c616288d290265a00a385a516c200bf00c300c1b801c18d123721103286901d0821a34709134ccf125a88410b5cb4104b4de183a5ce083933048821b016d66821082db041640744c41029c327083e647c84e0f3049f28f840017442a2007c0284c18c02dc6226e1d3850f181f19ec18b3c3c54e173b31d00ab004e0152d1461d5849513401e80678066ec88c18ed88e2d7686d8296287081e3178aee099024f10785cc0b3031e28f0fc8007083c3ae0b9014f1a3f88e0698367093c5e0885119a22688d2032738b900a261bd306431b9861cca529c60c33bb784189223a28e2c5ac628e607a1112420804930373035388893487104a825019d904e903e98664824443fe400a412a410641f64002418a91652412e419d20cd98654433a4192916bc82448366411a41939461a411e41f2400e41a621972089506449880c59027985549220904ec8276408e412720a49858c427a911f9052c82e120aa984f440113124103288d8059fe129b014f80d36023381dd6036f8082c045e0297e12230190e02278189c0436025701b8c84173cc038601d7018fc057bc13760308a84c151700c380601f6c22a602a9805bc82225ff009f8056c02c682afe02a780b4e014fc1246017b0146c05b78047c02828b283175410651081107f108310c1884b4422e210718ca883095cc921aac0872844e4126115f8a189952a0f70000e1250800d09880106041ca0061a6658420025321069e167c80b0100400cc20b828bbd710719134b222b4d26c0644a13221e9830e1a2d4210a111326271f9f1c15e800c5890f13b8c9c162898a9a1055c921869d9c140cc9410114284d728002a589004ec0431105a21041f9b244443b309152c4a488070a3821aa404f8e09d60a14274d8a8874604244b4c38f9c12ac941da2386162812a443e5061e2644a174521eac1024d6a78ce154b94431429519834c9218a1403e4d03d72acd0414a0e443ae4e49060ad5880e889150b102d4900985385152703683067042ee478d9c9a1626972a6582996871ca44c21bac98962abf84001273b449192031327539a68024d5c3872a0f891f344508e13ab831326454456aaf890c38e1ca5b542e484024c9c4ce926537488d2a44a0c3e729a28fa8128ca4e0e1316287252450a0f453e34c9c911c15ab14204a547ce1235394a84608ba2f8202507263b342182822307045ba5871e7a982195d345859c0fac0e445128e0c44a141f98e420e58a131fa810b5723cb0454456987460ad38b162811d888826c0c4c994b6c00e4444ae1c39496c911529509854913285a80a0776072952aa4c6132c5090f56aa1045a980142616207a02e66c60ad10f160a509142b4c7420720265ca9426517cc8c94162ad1015f930c58a131d72e424d51c0d2c5195294c88aa38f1810a5115265676b832452727035b1485a807a2a226455676b832859573c45a212a9272850993285270728c581eac34e181890526c0a4082b5088a24469c2430e4ca6f4508507263a44f141ca04a23821a2288a941c9850e9418721d64a0f4daa3499c2a48a14a495b2038f1c21d64a5114a22b44395c96a8a8890e4e7488d2240720395b9648072939f4c8d1b255a4ec6085c84a96b552e4c394221fa4f0e404d1830f56ac34512107886d52d444072651a438d181a8c88a132b5588a6fcc881ad15a22a52726062812227509c4ca00914274dae34b98f336b658a0f52aa5080890f1f53582b55a41459912243932a4d6a58f990c256c0c994ae42f4a48a1406d000fa786389887c20f2e10036e84044010a34b181051f51d82645527ca0c2c44a14264ea63405a23879d2a42801383ea0b0528a983c61897288d2044a9309309152c4c44a51142752767c98592b3a54695201a22a4c2810c5890e5188a6307132a5ad409122a58628529c143589e2a4072906a83e9cb0569ee440c4430e4d582b455188aa10f110c5c97d30218a931d6e7cb8b13938b112c54995a21f88985800ca94294d985421d2018a131f5c4f729002c585e363096b85870f256c1326383e92b03d306952d4448726454433a87c94b98013284d9c143989e204870f24ec931ca440990013a2221f7220fa8188c6c711b68a1401741b89a04ca04951141f76605294035115273a4471c284c98e19704ca17d21e388287a8fb88dc1601d8fe48ecccbe2658953a2367570361c9c6dd3c1d9b66ddb984e9a6db67c62668e94329572aec44199299dcc534a69c3ccbc316e8cbbbc91ee4e9632ca8d4b296566a6bb9b9cbb94e7d2dd38ee4d5c6daea489579bd9e4ac9392661acb93c652ca9394cc268d4eb9f4a3cb1f4fc99327dd9d3c79eeee049a508e73755667714829e54637ba946e31524a4d1ba554a6b4a5da04b5b92a497160c964aea675734e4955544a2a699492b2a473ca18bb9daa8ed967b7ca65cdc98c9200b310639494258d546ab31bc272638e41987723c0922c519297e54e6d71d089929287e70a524aa6a66566a9c91865a7824077dab873eea472b59573abdbf2d87d00b3a4da7633959adcf1d464c7546adaa63ac99407903959f260964c1d207969ec32a9c98d9b940db8214ac92ba92771ec763491294b962629358d6bb4c9cbccbb5152aaa2ecc93853cc92535288fc4e3b94019399b56e4ff3f49d16703a514fdbe44ee66e519379521959c3c13fd68615b0492997999929534a29cf9e7657ceb9bbcd39b739a3d4b4a9f1b6bb4d29e5f4a494524a1a66d624a56b434d0d955ac709c0a194fea09e4480644f4e8d0fb03f9ec73548a621ca3925ff0c611b96924e969272dc9d9e67e35596d5c6f33c03c49a1a49d7666d3c0f07087a2849a5dc4d63c92c375bdee49c34d249299d9c511a59f20c426847a746299d944a0aeef6581e91ce2092f2ae941a53a65372645e9e3c234f9edb4a161740d2c8949952ca319d94d21825dd6c728aeea4cc9a94332ea13c270e3997f2eedc9697256f6a575bde5d4925a574521c934e9f3929a59c993893b4d3648a67c63b774a6dca5d6d93bc0460254c2365197fa4a6e4c98b8399e6b8d965cea2b632f2025919a39492e2c8c19429cd6edc3565d9eeae2639721677974dcb6c32994c6bda35adc9c4cb2b354dfb989775ac8ed52179994a2a7923f326996764a64b99255d96a8ddb18317c53b98e58e1dbc2c70e48f23c78f25cfb81c254f2965a452d33466c9434aa94d9eda5cd6e69474ae9492512bc3c648a5ec2265d6a4a4b4eb54cc1d65eeb8e378a2db89c66da3943b1983a6ed524a25a592eeae64de9dcbddeef2ee6429e5943c254f2979ae9c92275366661824cb6d37c6dd8e57ca8e478c34d24d8b7105f9ff1b9d52ce69a3699ac633351b4d4ba269da00a63605a031338d52b28cbbbb32ee4ac9947799eed24957ee6e94bcccd44577761c378ecc94a9dc58ca4925f3e4c99ab6946edc297929a573cedd49174597eef25c66deb97432f3dc5d9e9a26792eed96e9b26439996564e6aedb9d93796ea4cbbcbbcc74e9b6514a7729a5bbcc734e6669c351ca96cc1c99797799254b5e39a5a4cc2ca5643a44a5ccd1344a376da3cc2a243d2fe4a8664e007a7226ddb64d45e9ea083522448bc46d53c52d6e5145378d529992945219a35c5e4a996e94d2549026a726e9dca4a4bb94f2524ae9c73393737653ce49b7f3a49452d28d9939e32929dda5dcb1ee42e658b64015293d10f59001e940548548872a5298b45c000091932a452bc41e767f88aaec90831428509ae0dc4ce9a1499115a21e9c14ddc7047cb0d2436485490f527488d2a44a455961b5582ee810a50913a22a4da6f42085c807a5490e52a0ac609252d4440727509a5c616245a7072745517ca85275ec50e4430f221e9814457152650a931cac38e90187c80f1580a8f0a34607284e78c8814911141f9aec40a443cf0f443a4471c2248a93263ce4b0c30e5288b47afc20a502453e9e34c92107a2272d1700f01c4100f8610200a2274da04c800905a238e1c14a131e5ea54991951f35567600408a942a3c9ee4d0640250a048a9b2420b4d88aaf0f041f48449d10f44517cf82822a2c08e1c45569cfc4084934971529443132b519a54296252e4a4871ea4b0545800fce06407a22a53aa8efd69420425879b9afd894234654a132644146852454a91151c00e800200a059c580169f6c78a05889e30c9c1099409f830855545051d88ac5821aa32a507293900710b802a4457a4e44054e5e749cb05004cc981c80a901d0bece0a40ad1159d223f2eb800001d54e84154d4a40a5114220a30696245871c889848299a62812a39e898a0105d2182c2e487263de45c89422451fbc3248a142756aa105d61420129509850208a130a50400a1426453f10edb81285480e49b980579b46d2c8c8c8e884c366a6a3686474b47538ec91917674448d4cd2c3e1281e9952464653e270148f5e47f275344d381cc523a3232323898351343a3a9246464646469ad1d1d1d1e4705823a3a3cdc8c8e848ce2323a323a3a368148f1887353232323aa23818c5c5618d8c38e270148d220e6b64746464b446468c83513c8a381c45a3233ed2240e6b6494e160148fe2913ca238ece270148f8ef868e2b047474712873d3a621c8ee251c4618f220e39643888bb1b7bb0d2640352203595545fd9b268645b86dc6166f4c573d1c3f27171c16030d14b4265c2603b666641d1c8d6d868249241a3f71c81d6b53f452313d60bc3a2d1f1dc6adbb66d196f9b8cdb4659a8f86c8c1c32465ef075534411cfc789466223e1f602df3eec3a7e875ba1101b6e853b1defa3e33b301dffed4e9c010505d71b93a24bdbe0702b81728cf0a50d24b634c18b0eb896d8c0075e6030c517d80c5cfbfd67d1f7718ba58035c7b6a9f2c56f6c29e0766c2e7a4926b3e29935cfd5f0ad7de31a16c98e37dd865b5cc665a7530db3d3e5b90c8ccd9dc06556f462e8d19359a66745b24fe7226f94a80cdce68084381d2f8ff375aca1c6c91835be09dc9014519be6ec1194de52d5f4765375ccda6196bcf6334bab0b2e18c6a61257f69e3b5abfcee68dc3adb73c90157ab8d3a748a3e0f66015163666f16d8c044b7484f5ee4e1861cde78094f89e4818f7c6aa78a363c7b3b724c27a6b18612d93cc2f7865f67e824abede4b303c6a397fa3e4d4b35249f57ecbe8211dee990ee579c1f8c5b20bfbe85179cbcb8ac3acf80803838b67fede8659f12b6e85ace687409acfab70d5f140b8c5c31ea7f95184696eaead9b0ebb0e57cddf1d8e1968a36b982c3aa7370645978602893e82b3975bf321abe97770cebdde7cc2970fbf87d9f95c59d627df9442d3059ff0e7a53703cb86e28d0ebfa636210ed727f294be5bc51ae5a3116e39c390def589f20a4ece2f98e619f8f2092c9a2f9f18a3f932353b09862b74bae9d32b00e5cb4f700ee993fce904fe34473bead1204f97dd3bf9d3e99203e34fa17c7ce62d603b96ad62d99e5d3b9775d25bb275d15bc0e6002ed1b51d0b97e8cab263c9aefd86ed58b61bb66b334a1074142dffcabc27926879497f7aa67aacaa676096edd929687472d1ef0a69cfaad1c995d580643aa5607499c02ba8204949f94ee7a2fd0aeeb0938b7e3ef5ed27b480514ecdbd03959c509f1e0cf134dd43f00914eeddbdcb0e8cc2ddfb908ce0360d181eb52a3e95fa07765faf00b1a6aa0f5f8713068b479d036f38cf092f1aec8d2969e18416bdd31b73a28b1348b4e3c01b94e7799ed671e00d87026f38cffb10534ff3967877424c6bde9253cbaf8780756535cb565f47ae53e5a23dba2815485a57b90f35adaf8c5e3365d588d2677f196dcf2ad25dd991ee3255a3ad22dd452b172424ae2d472e7a7a2d935c7da28023fa1b6e1a7ddbebfb81d61bee943ec67ac3d17aa4d44c695dd9b31965bcf580dba8e5dbd8115d9a635398e9ad3bc2ed4e38a4e7394edde806d7b700ef4636bcf2be5ad5dccbbede0dde5760778ebb194ddfdff0c2e2aa36b0eb9bdd7a3660e1f1ee58bef3f87a36f078575fa87ff4ebddf02919d1f24dbcd1f2927a36d45cf50ef48e02b1d41c75ea3960c7bd67a00d3557d517ea38be2b748e76cff90bb5e35e7dd5bc4381eba201b174dff11c10cb8ed71c0788e5d5bde61a181ef552efe88d3531a6b9f59268a7609453d36ba09253d3e338c745cf01a8e3780ac4827aea2af085baea1ff842fdfb7a0ea8f98e0f310ef0957ace3bf0a8b99fce691d5dcf012fd5bdbf52a7f97a36d47c477d7df772402caf97f7ef3767170d88e5f5ead695025fafdfc7552016f0587e2c3efe32c282739ae3f86bf5578ebf6cfe51cf06d455a7de0ddf6beaebc8953a0ff0155da94a411b50f4aa8ae5bbea9cd6496f01dd77542cde51d7bcacda50732cdd535f812fefa86a8445c7736ee86a961def9eaa462797ea1e6874727dbb42b486439a0a24d4bb6a747275cfae0253ff402fba6288dfbede02befaeaee552ea85f91d52750e8b567577d88b91a0e69eda7ca1e0cf1f21a18853efb908c59765c555fd1552b969bfae2f197ea393781db3b30cb8ee75c07f85a5dc7b9ec08a55e3092b223943a3bedd94cc47ac5e8d8c317aec2afe73cc288304dc49a486a02a6b48492154c9461620c134b5d47c19bad2e3175f6d8a7d6c8f4aea191e9ecb2b9d753b26b7416002db1f9832b609d5debac395447c11bfa0dbca155496737f5d6eb2911524617b0ce5312002d31250b4612ea39f5155d2fb0e67c7f3dc7c7578d70eebd4735c2b942fb771c4474bc0a44ba4bf51b10e9aeee29d028751ce7518dbae35055a3d453f7be037c1db96a2a1754bd820a927e126e14126e2c09a48ea7df8eae158874d76b7bfd8574975177ef35d701be7254a3ae3b8ebf8c688ea322dd85ba4d45baaba61ad154a4bbbaca4555b32071a9deddab595267ce9c39e30a379604ac632df26858313ad61e3e303aa4547c6928bbd43f526a53d71df5f995409fdfbc05a09efa2781b2a48eaa48aa7a45f623d7bce615e0a8b34aeb13ad2ae0c8f5b10bf569f46daf08837d3f6cacded0a3cee52ed4b387533aab37b4daf0c58738747cfc4aa054c5e2bdfbfc168dc4facc73c06130180ce5d9d01d555fa87728af33d271b4f1d7020e7c69f953166b7a52ea258ab2cbde5d7a34c89f923a7a06f0e1eb18c1a2afb573270f8638a47f7abdd3b3f0a8e3f6cca3e1f430f693f8d376e2c7f074fef496645e122a617cac9997844a9278ee9cd6450f86989d8bde4df7ce4b349a28534ae9a5e93b641d07c6e6d68be1f41de7c052cb9f7ee37120d6cb6d5df76e33f180c1f3505d8c3d52a91b1f57f2ddf8e8e1c52f9ed7c3f3c01e28eaf5b8a9072a557373d383c7b8b9a19937cff19bd4d7c5689323cde4dcdc809e4ade78d771ef9c07de58c1ad9bd3735fcd131e3f7d48d77830f0f8e9d45b227f3a47d331773d703caf8a92cec7bbef4a52dfdef9e8de7d35a492f2c01e958a51df3c8261d781b1716e6e3ec43d62b397e4e6d2a3813b3b0eb8dd35e56e4e6fbcafee2db366c7e8c110bf7d48ee00c3d83cbcf30077ec00b7573a40da8559f43940e906b33c100c5fad02c3170d18be5060f88ab40bb7a234d3f451bac163343d977527709bf34ef54689fc93cf6b5f4f86cc0a6e79a7e73ccf3b7b8f20d14d6fac8b3038308603653800db40990d8cf990ee7af79f5e5fef9a7ade8798f33a6f49eccd74d44d47a14ecfa11a753aa5fe755d1525a713f7eda7d3e95b0db90e4c65e009dcfe50a813b89d42a14cde1296f778af462b9845ff83a71fb018fdf5b5c27e1004c175c5ed041ebc227bcc3c1aa89764634778e922ee534fc79a69c12c7af66630f5a98615e0170e462f4977a31f3afe603c97bb5ebfd10b8a24da80b9c0c3241a454802e67ad5c2e5ae7fabe1e49e85f1db43ba448ca0e927509e03c31fdaf4f56660b5e94a4e27503ed3825ba6d3902ed1f42690979845c3ae69fcd2d4093d2fc52f3462d1d4a6e9bf7f9b3e5a71fa8e5ff8e9f46c00ff4bcf86ff55ff0a9ed62759ac57c7481eddf00f37c255cbbd1161d615762dff188e5a7bd1bcac6111b792f055e5ca76f9a2f9ece1a481d5a151d3f0870ef71b4825a7b7af4703e52e50d07207daed87787a4964bdb2550da4975c6ae90cf945dc374b9aff0952c9e9f8ea8c3e0b6546f7a0f496b0ae501a4eb08e9764421ebdb12eba34b744b19352cff3f87e8b46b42f2dcf030cb96fbe2b34a453e7d143f86e3ea4c3d8371fe25da19fe61ec2977d4887dcdba3b7e4e621b7173bd630f65643cf83a148effa6c47fce8c170d41978d411bc790a0c799c716678ea3838383812c707ce39d9e1e07c88bfcf870f1fe762f7813e7c9cbd243e5229140a752e762910858452cbcb887a8fe7e8706c8f21ab5b5d666d3fea086e60181fef3d25dbd0b2c767951ad1738b9c9704f514b8fd81db9c8e2e7af12b2e7a06a0b1ca40cf6d6fd01eefd1a3470fda23e7393ddea3474e8f1e38471d0785e2b818050f308c7d73e9e3a8fb088734cec39bf3330f06f99bd83847ede0c17128d9060ac5a150600fd906a3eacd121def711def11720f1e37351c8aeb4193503d7a703d7a805cd7e33deacd92d5b9afce9dc301abccc6f90d8e4d8645eaebcd103b3bad4743cf8ea7bee3a97329903b0eb87554ea70a0dec3ca51a33431b17ba062af97845b82036ec79b6e401e1f923cc0ed1da03606b3e65760f68559f33a406a0418be4e60f8ea30c3e2b5399e7a8eba9961d67caae2009d7cb2434d4ccf6f66343136a0938f5b1b835b31c3a2e72358033af962675fb8950a6952cf73920cb754aad3e9de921f7d22c3add327c7cc614e672fc9e90c865bf1f3dcf77de76217c1efeb0df1776e773049d985318afe2ebd24df23b8ad02b7bfca6098351f953893299a3e4de0b9a3ce2238c4dcb9254222a97d7a634963901aa83786c4d20696bad51bdb4097a5c6c122298aeed11b4bd2d21a70a3717a631a28a3399a4e3bb71cd7adb7d5f0d5db23187e1d0f824877815d04eb0bbcd1c9b5ecc2b69de33a105c5704e7b32d9ace759dc92403e532f0267efba86f966ccf1ec35663186bf8eaf8a38e5e92218e601177bce6d1d0d3b1de2ce19e3af754a546306b729fe7346fc98fe6bc73dc454f49fcf6e829c91ebdc7ac2eb3622de2ee50e7d64bd5d0a853cfc070ebace33db08a12efdb95d0dcfb726bab611525db5557f27dbb57c3ec347599e5dd5b6ea96a4825b6f7ad86cbacec5f0aec2a15a3ed0d0fdc4681546273dc3d70bb0339eede125673f566c9e9a69faa24c3ac79d34f4b70cbf4791ee1f646cf9bc0a8c4ac798e870e5f761d74d0e18bfd44fbf6bcc6c3c9f764fb7628dbe8792e761c8687932f7e6e356aa00f5f177d9d552a90ee7a31164a5f380173194d325c84a00b17160bbc8e5c3b1762888bcb10d715f1d4086ec936b8157b89de0e6599e6303dcf5ee848323a1c865fd00963143d1fbd5406536314fc42fd3c8f1ec217bbf476b8f5fce4d64d762a90ee02bf3759b5e183c160301778eed8f3cb2f809f8fe02a0a89a7208f05124fbd193c1beac14bcf868367ef00ebd2a99fec898fde01d655eb267be2d73bc0bac0bab2277ed627a779d4838520c1c9ef4e1895b865baaccdc617957a7eb778d363353d82db290e49b92d4fe77607790269873a4d7e77e22cfbb6fd74daa84703ab4ff127b07b066a59f613486b763ad77ddb3c18bc9f2ec1ed1dc87d486edc9e03b73dfa3d0a0c63113773cb6d1741ef26f06689bce9d2744e825a0ce9b94ac547cfadd7d5f0d5dde90391eefabefcc2575f46a92fbb70fa6bd905f9eff2f1eb296175fc23c8bd03f91fb8ae147845772e825e0d67c777e076024d1e0cd9e325783a076e1f92a7edf343d2b4cd6011776cfacc4b429f816111478f869ea6a713ec217c51a9b7a352c7d9105390611324c1a9fd8d168cea8d65404b841d41a6776353cc7950e0e3ded8114bbdff38d6ce60383510e92eadd3b4fad26e24a48c2e602e4ddba6b2edc06a2e7651a9885b2ef9d1f2546c1f625ac3a3a60c26a15d2035abe5ace1abe719dc40a4bbb66e43ba2b7b56c3f9c2565fdb8db42fbbc0e70fc9f946cf73dc31b8dfc075ad47836cfe1053908aec59a9c83367ce9c690d5c7621dc9813687411b712b756d2d0d3b24625ae5434233ea29c1d815be696396ebb23c9c3e3f69c16d1041dcff10824ed8d1911c5342289ce7a63461cd15e6fcc883052ad3756c4133a9ecbba05a3139fa9375644998ee7b46ec118c5b7b122c674b8355ff6c68a30a34fbdb122963a7ee3169f5b2da35372dc0db5ebb367265031a9dde1dbd810647a7b7b7ba77c30180c881d62f9e8256135af50ec7d1148fda36516e339dacd15a2bd9735943eacba68958155a8ec99e617699d9d93de92545389675a7e7d42f9ed5cd6cd15e25e25f43306794d86b143f9790dbc5942cfe7fa649ebe336fc98fa675d7477e322b5e825c6aa3b05ac6cfe9d1b0665a9ec128ac9635ae0f3ff392c8478f0656cbbaebc375d3e22401b7cca5e67727dc58338b5b1b4332b361a247837cf6e825d967e0cd3e8b4f7cf141dc0a7158dce21f01c240d1c53fb736568416cd746b187bc52c3e9f822c66cdbad3716b574474e98d1181d46150f3bdded810663a4802ed7012f692ec308bcf2c66f2bdf936dcda4b707b63bd3a4734ca28b8087cf1cb71510660c0883508f3c520be48e54de56bae38e0518c46c2adc30d68720f7fe54fcf4d109c0f670c1bb3f8dc7a0bc85ec3a3963772492f89fc905cef06ed597d1db95ef2ec921af83a72c58e5b93bf12287b58247bd68e597c5a27b3f8377cdbe16cb6c1c8c5e50a2a489a3eba26286b187b9e82a1ecf90cd43e2427b8ad81ec5acf06e98a1f62096e67e0ba26036d2e36e2a3c90c0306ccd3e370ab48b6dc9a8245b295c4f80916c99e31ee80db73935bdbdc6df492c87391ced3cb4b2f89ac145c66499039d949303ec91147e6c26df86d0c894cd3de181252afaa8d38eb567fe00cf0312bc5c5057ceb11e0a8e7ac310ee0052b05c044a2b1bc7caedbb9d8ed9f6ca562d4fb901fb72eb3f68b3121dae88d09e1c51442a9e5e4b838bd021cf5322b9c7dd491935e12ca5e018e5a76193d38bd1862671fe28d5b3730c4ce9e6dc75330f6ee4c19590ace00313e01bb8d18178605faba9ecda576550a20c20cb3b6b3f3b96ecc92979f34e3cb0ff172eb0606bebccc1e3fb915d23f91e7967cf68d5bdc59d7433cc44731c630dc2ee0e35242cc021ccdcf73a9025031eaf9f9f88d3deb04b86529a53c7f4e29a724e2965b1be3f246cbef7e412e3af171bd312e4974fca2b8dde246cf67400b060673ed61bd9f5c6a722bfb3e4befb95d21d439eea83b0adcf5e96ac825a1c21d0357436eeef11d181a75171b155a316aeef3a7ef0a998e5a1f540db9cb7b3781bb3e5e0dbbcbed0852d1ce9c3933859eefc0f0a8b3af0743ecf921e6c0ed13f864d62bb1b32be1ce2ebd25f35cc3a3e66f738f60183b5e7a3064479d036f9474f462e8beeb133f2487b803a9c433bd1d05de78316837d530b6e927308c7d3a978161ececdea747c37cf6f592741f626e89be8d71d1d2f3d9b9e5b85b2f06ef5c0d677392af6c8d3aaa464fc9ecec1cb71eaa86478d7a075ed9ba3b0786b1b9ef0a7535ec0e45a8777db8731f921c28cfc58e03e5d743552ab2b327c9ee8161b6643e5b30b0209a3b7d1fbd18e299eeeaae4fc8552af14c73a75f4f866daeee02315d9ec06d13189f81db1b1861eb43af81213f03b7d7e30ff1912966417097405b52ff57fb961ccfeec3dfb2e3a80791df3244fcea42be1268cb8efaa2b9e4f12d39e7bedf24d096215b747cc7f72b09b425477de5782af51daaa7c0e7f82b477da976bee3af1cef9efa16595fdbf6afbebe6bf22f595f5b1d22feb5ddc755f7beff24d096537d9dce7c6e477dd56b3aff71d6bee32ffe8efaa2f45f7d9dfefdb5e3dcb7ccf3a82f1b9bd3d497bca9be4c07b9bfc073f525e55fdc6f5ef37defea8bd29fea4bd6578f9ffec97f7f75ffea8bd6fc45df7315fefaae427dd9d81c477de1b8a6dd477dd9f8380e0dc755f84bbb0af5e5c3247dfc65daa943b06aeaabe63870fca606078e7a9dd3e93e7a9c4edcc904e2d89143253f8d4da71c72e370d014483bde76a852bc81da27bf938683a35d8e1d393630a5a2279349729f8643d57d394cdd0e95c61b9f5220c521298e1d72fbb1c2c7710ae4ce824a85d38f1f353b3869eae9f98bdfd363ca91a39ef56d67752bacf0d7e92bac006a5aeaf53ddde93d94c5fa0b3ceb076b4be1f85ebfc20ed557380b7fe5380b3d3d7f71ef51a1e72cfc65a23f7e70e0eb5914c759fce3c78f15fed2be427dfdf8a192674105157afcd5bd87e9f53d3ede4327070beff96bc77b582c9eab505faceba8f09ebf4ed7f9ebab3bafa9e11dafffd1a3c7757cf8f8cd7be8bcc75faaf7d8fee340fe4a1d481c827316fe4a9d85b8731d160ee4af1cd7f96b7b0f0b07a71ea7e7387f81c7897f71c7b90a3737d7f1515f3deaabc7757ee3e3377f75bf897fd1ab70d65fdf5991e7c75fda7fd417cf8fd7fae2b90f1f67fd255fff321d487dadb0c277eaeba6be6e5e53739cfae2c1e337f585f39a9adffc85e337f5c5e3e63cfeeaead0f6c4ef9c059d8ee63dea8b477df1788f77ddeb5fa7d7faa2d179fd8bab53b6279ee62cfc75aa3f6c4f3c0bf5b5421d22f67057a1be4ea7dbd497cd3915cefd65aa3770f575e28e535fa777a6777f81b586ed89c7f90b4795617be259f5f503c779eaabd657bd0ae077ea4be7aff5b544d0f2042fce8860091b407281620891441850404208b688e1da027e6847ddb6271eacafd7e7a8bb3df1397565cfde478f1dc7a9af9bd579d4d7d10b5250842531535401c60b5c3b8e9408c20c24bae0c113c434e1daf197aaaeb6277e477dadeaca1e213564f5fe95aadff6c4eba89becd9e7f84b561eb6273e477de1a84344d716d56dea2bf59a3ac410d716799afa52a19eaaaf2329a8900215529871c3084970c93da34c0aa0404209335d3cc1b5453ef5d75783b6273e555fa8bab227480d837affd2ea90ed89f7ea4af6ec5f5c71d89ef8aebeb83a04cbb525fba9be8eb0408b2398c00d1d942181cb5487b86b4b0e174d50410ccce0028522b8b6f0b5bfb6cada9e78adbeb2bab2c7a786437affa2f5db133feb277bf6fb2369d4c33e67b6c096b44471818f7f6eb1b815ce8f5bf23bdc8a2d6359c6b47cc72dd9b20fc4aeb0eb706bf9e5d67e4756e136227d1bcba2d41b0bc28d0e532dcf42cfa265d1a60d5dcab96a69264df0d2fbeb0a1b6e0ef20d8f7373751eb1661d5fc19a7f1dfc98f4ea7572e03c07bf86916c5cf34bb32c23bf34ef388acf3af5e5bdd683f5f5ddd4d710c79d477dcdd8aabea68efa92af32f65a1b9bd3d4d75c924b3975269d56dfea6bfaa8738cec31fd33f5c0a9af2b6cf0749c4f57299dc6347f934acd386ee2f36feacb3b8f8a83ece1e7e0d7d4d710d9c3d7ea8b0b1097f6aebee61759467e913dfc6547e5325fca569e8e9ad57c7f71ea2f3e4a2635272dc174d457f61c35d2d4d714d9c34ab287c7c81e56923dac84a3f2981d534689da643595e62fd35f48445c273e8dd1d497e9aa1a635f7dc52fa9fa8aa8fa9249b287bbca4bb0f86575008327244dce545f5ab874ae539d7c2422ae595fa61ac7c81efe7cc6a7f5c55be558cca030032780a1021c00119762172048218432a8c8a219710db038c106c4784309d7acb14c19a5b994b434631316934af38b2cf3a58cfc9255da658c541aa3442b4d9249f3212f6967ce4c810765dc4089a4592912c7e211766006109a0863866b5d9273f0811db440882f8ce05a97ac14267b981fcdf8e2430a6ba64bbd3a46e051e41c55701b1197961dd7c2a573cdec5c562e7a792e362ed68284e3d282ba162e412ed3a93b17968b3b971dd7a6694162b9b47009727dd7c265888be65c582ed5b9ac5c5ab4a02a97952b752e9f0b754f0b1796abe65ab86c2e9b6bd1c2e573e5b8162e77adce65e5d2711c5ab8dc35f7275ecbae0bf15ab2683b2a9721ae6c7fe2b9b05c74bf027dec4fbc0eb0c7fec4e70071f6271e07c8637fe26dc09bfd89af0173f6279e0644ed4fbc0af4f627fe0353fb139f026bf6271e05d2ec4fbc077edb427c07e6d89f780ec4b13ff127d0667fe24da08efd89dfc0d5b610af81dafec46760b73ff114e4f6277e82a7fd8997e0b63ff10c9af627de6815e506b86c9ecb74d12191e5a4da663ad1ef3b2aa594ac9d1d5c57b207a7878f9c2967e43132e9f5cdd3bc84b58cc15a1eb6ff7cc75fb8d8b7fa92b018c352f5b55d1bd3923e2bd3f2f3afaebeb80c7f9932e433a1945a6ee7bed3524b8f935a761f2fbd38e915975e1b936a78b112a7e23119adaf58e6f14b6a639d8499621ac326ec8b75434c65b42ff32be37d315de3b963beaf8b7bc84aded7b57d95e8c30ff570bf543daa6e4b5e57575cfd38c954bfadae968c6a5c5add5c597d4c5ad7ac2b7b58a68e229fbee076278c65b66b19fdfe12687e4fc14f705e035f38be812f1d37aa71699f7f5d614377edaf8c7bf657d29ca7a8cfd8e67dfb6b658f1235fdf4d715367c37ed189a3a8d4b3fd51cc75f57d840f39a3127d54f7f6d191cf5a5858b97e33ab4202171e13897e87a5d6183cd71fca5a3be72542ed18543dea64698ec91afa9af3846f6c8d3542ed175aaaf21aa3a45f6c89bea0b07d923ff552ed145eb2b2ec91ef954fd0b55b9449757a3d2abab5ca22babaf1a648f3c576392eca14b33a9a992e9618ccda5ec3b6688b6654e0fe39813fdbab2871b3355a31ad7fcbab48711e6daa40666200557b2277e82973d31da1c45d696b8dd09b5322dbf49a0f00b57e14356c7be2af4d9f727d4a5c974aa583cee7c3a71158ba9eb2a9653eab1ebd84725b616667de4d7b5a5fb0ba90557aa62e150154ba76566323312c8ab5b3caf6666648f7c57b7749df61846abe1b9ba85e3eac6620d57a7bae5f42cdbb79cea66217be4670dbf0e3333a6bac5f42c5bc592259e715d31bfae2da61abfc81e0dd6b1af4a767e14f37909e365633608d359b614bd7891405b7d6d5f2490b75258ca4202755b4c02719f7c0c23814ea68a659545562c973df148cab8e429d8b16f9edb1a462af31dfb60d8f5097d427ea88589918c2f92f1694a999950531213bd70875a182d8c048a5e648ffcb67ddf6ad5a116a6a5ec09df61f402d394b4309ad2aed161b69499c99600d06106cbbc6430fa858aa15f6c3a9c6328d21c73ea7026cd2c9268873226c9c82e32c66564973065580916c60b2b99f112463343222cdc7ac50c09b79651cc17cbf862f3c3e845db8e28a38ce076087165916366ce1e4102e2ca22dfe0b14790a68b8f2075387b04c9a6c71e41c2f1b1479076b49a3da2050900ae2ccce7126df6089286638f20a572ec11241d3af60852ce6a8f20fd70c9faca8ed4826b8f6841f27165916660f25c326f8f20316a8f209d527b0489e6db23483c547b04a9d2ec11a4155c5c5ff4482d647b444b16cd8534c4c5e7225d7b0489bae41124cfb4479070b8e411a41e2e7904490597ac2f79a416e21e41da788f207d728f20ad5c7c04e9ae23482c171fe1fa9a37aa71691a67995657947eb2be905a70c953a4165caf1ad7acaf2c521ad5b8905a7071fd8db238635c152977e72eb3180728051cb9f858e4b7c82a0412179fab11c773f18b9bad0438eafd2bbaacb072d4fb2b6a6c61bfa8fd99f34787477433ea0502b89d820b10520617675cfb0952798232aeadc2168ca4f9d0d4f2c77e3d07fce8a80320a00ccc4f701fbd2537d9570bd2e6da6eba06226d2eed0b86463630ccb5dd0472097269374aa252539ad51ab46fdbf0b9b62cab54524d2b15db51cb2109eb4d0357f6845b53e62420262688345df312b4e173cdef4a59a9a49a8a39c1953dcb1c9b5042ca1996104114c204d012df5c29026ef74c022b3a7684b55c1ab8e7655d303ceaa5125b7e376e808b1fe8c01757b2b067904aec573c8a1c008e5702690fbf4cab2b7be457f64cedd9e5794b72e300d3b3dbb05ddb4eab0da667f57505bda95ef6c82a3664dfeaeb933df2f30858d74af6d04c9e5cebd2d69581eb9a60c86ab025cb27b8d8219453cbcfcb1aced34a4536bd18b6a940baeb0a79e92d31f5ac610d2da5a9b98647a1027a63121c95d87cce4a6c537d126fba0ac6e890abc4f5a12fda9aca9ee97e3a71f491c3ecf42670cf2dd17cf735bae34cbc1e87a625f3dda3b7a4bbe9dceca2178376ce04769fde92edf1b1eefac490abe91c18b2e9cad654e4146061513cca221361c6f09b4a69b40a96e8ec72eb6ec021056166d3f81a6e20238f866471eb26fb90a6af815be1ce0ddc0a5938f0c0ad70b5610a30845b1bc3c24bd39d0ebb0e378c4e878bc672eb1d6e1b40d4159ec61863163766dff8a2ac37f60eb768957d51b68449f333fe462031c993cc2bc0573e460c74f617162c469b11c3c097a6bad5507e1f663064974bb28a0564ceea9278fe8a5bdae9713e9b580a943a03697fdcdaec540a539842af776681ec33309ee9f5600b64190644c7b32c63aa823b92fafee396f7bdcd0ae73b2c6e653fea13e85d038f38ee74f90ebcd16eba97d43305b09e379d5ba2d8826f632818d3d9977ade4341999ee7ce9dba0c9ce7b8a3286f3281948b1de5a8c8e652dc9fdc2cd94ebf6da6eacd70da4ca84d3b673a7ddb74aa306837ddd45d036367118613bde974a261fe549fd087f3568e3abbe6d1f024fbfc56c3ec5d353d8b9da540c95c767f5c5a52e7b2fb933dba8664079e2a95977c0a5c17c77529705d27703b3c6a0d5c62aa4fbccf244de6f31e7297537a4ab86735f4ced51beedbe9d953e2d51bae2ed19e9dbe69db136d06a96da61a7260d8fdc6f4bd315518e8b973f50476dc4dd993d3b91ac60e331ab8baeb53a9c433a64b2ef6c904c68e447ac3235b178d3010bd470d09e296348a4727ee86e038198327c3d210b9149096d3b55c2139ce046e8c99e34e762670c77805981e0d725dccd1e5960438d2406f340cd9320618ea2dd5dab94851a15112cb30d4dbd98b214a533152d2754e6b1d7b04386aa2b773b29b1e0c474a4d0587c88c4285464abb40c2ed994780a318648fd7b6eeb4d530ae8b337527705d26705dec29e167353c8546fde54b67ffc0cb2a6b283d1a2615d94fa456b31ae593f89b25a1059abfbd5e12ce989af9d2458629811bf159b69a96659a96655566670c149532b0cad6746620ad38e066311e818b54d504b7e06caf435da14979ddadd0eeeed230614abe2455b6652f3f7af2ae4fa63ddb9f5648abe1a63d5eabf1a78ef4cbd935eefd8906ae9ed68723acf9bb3ecc4034addf0f2d32175c6ce28be7b88d30f8e6a7d411ac4d2317d92b407706e33bf08917033f3e721b53f06dac045ffac485983e75778e935df462d86efa13e618bcf1d63b83dc9ed6cd5d9fd4e919443d0586fc10063eaa86913d18f8df87e407de2ce1a3ce4755f066c9a9f9a7e6ca637da4769640eb9267307594e6d170eaee3c56288cddddac8f7c5779a03ec411d6ebc5106154a83ec49f0452d599aae1d6d99ff04f0ff95b80a3189c4e43cc4d969e12fee9fb04b53ef2a9f591b09eeb235fdf8401d1de3913e7d5d4777d4c5eea281318cae68ee25e9435ca03aba052dd8738d51de5753781616c0e08574d3ff52e5569f56356566bd36a73146b4a70b1891280a09b284112bd3e2ab091edf5428423cca0144f90526e8c746bf8436f3c423e9c0bc6aa01cff3bceedc3dcff3bcef8ec75a388ee3388ee3be3b9c144c269369bb7693c964327d774c4dd0344dd334ed8b4800b9e831189bbfdefad08d59f4f1886f36a5f7562809af1019d30a25915f22dcc62d9fd6dab994bc94524a29378b6163c6ad37ab04c36dc158c4b7b11268697a5696813164533b53914aa550f7de9d7b2a954aa57ac071dce9a66fd7ce711cc78dc1ed0261bd0563586f7db46e7db4473016f165a7e1d69af66e85f8dabd15dad6cee08e66036ee50ea9d91d1cfdb31a76cb272318c2ad25e53ee5d45c8d72e2900f2536a1698d72e2cf1a9ebe9dfa8a359af6e9dcec38eb535d6651309c9d51e99dc04869a56c3a831c8564e766cc98cf25e18d35d770a8f99ca9a320fdc67a635744d1cb45739dccca321318ce0d0cb596d7c0104ad7f45992594d9112c129d13ecfcd6bf39ae9a6f0a8b38dd3a6a681db13d4ce317f7df6c936c3f6eca61a1eb569e3b618e3fc16638cdf267f8b317e63136fbcf1237fa618bc998f9f67f0667e82320c698f1c7771b207039578a64a513745090e0458200b0606dbce6d60387b5e9a6a152a5b0606eb229ade2e6551d7db4f5f8f862927b00a1524cdeb335f94f5e96195d38bb2254cdaf41c2a483aaecffc069aea2e90e9c1409fd5096e1bd8fb2ab36e9a1b929e93e7a247c108c6c5c973dc516f31e46f1294ac5d72a5f25a4945768cdaa9e068e736666da66b60a84d666d37691a95f846cf67a64b3094bff235940903a2c3ed54b4378038d35736fa649ecf5dab1bd8690fb7ad6a0fb753d9b404014457b99275fc15adad1cb5f621a981d937707bc1908a56a6e9b30ff156c43d6fba51126349f8191843edd919bcc2dbb9e9b7af07039557cf2434945103afed23f824560d94bcd5307eaf5d89ecd0ca51c3b0b33e91f5c99e1f332370455fbc92ecd933d37e4d5fd34fdf054fe067caaec4ceb258bf3ecbc54e7b778acd9d3ea44d8f5e9258c3989d3b759ab6243b5753a6472f32b17deb8a597b538dbdd94d3fd5f0a84ff1a60fc9c87127d3b675bc09d4b205c2a77381300cb363a5f26a7913287981f043021cc5a0e302e113e02806bd0b8467cb53912dbf26308ae053f231b8217dfc06dec44a25b65671c0c060cd4273b1db409fde675b7034a9e729fd8f0ee9f636d10a5519d254ec8f36088fdfad59d5c2c5c7951da9c66504b76c7a1e4ad30fe9d0a6b5648f5dbe301ad1f3e1b2d1f3affd277b98b0040b9a60c1973130982b7be432d8244106a008e20918b8b470f171c523d5b8f65ab8b04b3bd20baef80c447ac1f5bac286f8570db2673e2ebdaeb06148f6cc6bf575850dd9637d5df6cc6775c8ca9e582c3edca56d821036a0c2086654e1831cbce73b08dfba8e3ae0050c9860820b312858826b5dfb70880d87c15cd1086efde8199398a7135c0cc1277b725c9184154d68a1509e7f9a9fd3a8e5e31a3d2b0db379ad46c96a981d8a3c4199a635ca4359c35776098291d4197d68d461bca48772b4446b51168ca4a60f2fd09956a3ec1a4db32801d0126b5a97bc6c21a48c2e6af843cbc7fd74c01053f1da1ba5ce9c39b384cb4405acc7115224a154450525138481154dd9359a0a989032ba805d098096d80fb00523c9b50f2fd034885dc365aab05dc3b50f5f4d6b182f5748b8311294a08b625301cba1c609b20c0106736d7ded8da230e1040f80780306736d1516cec7704a4b1b543e50638a296881c15c33d43497809bdf9db94b35d870c3a2d1f29c9628cf0227b3e46f8842e46d60967c0dccaa41029d7be425b3e42ef1b04b2d2fa17caa70c7b47cf82e031f8d70cbefc8b33819576e8f3df94684b9993501ebda3e2f7bce5f6085e6e5719040a7cbf32081a24b7e8a04f22eff4334a25d9ea3bf991e78334fe0cd3c9da26b823570d7e834fd26eb7e93d51aba4d3677aa3774db4cdb844184d12c384a3f6e6ddf7f85342514e4be5e0cf4f334f272b35b2f49bc2909ade151531b908261ec2bb1c37825f69ce778e3f2449dfe245e5ebea867d881374a768de64ecddd70eb29d1c83457370a09b969e495a88d42e8e504a174327b569ff47edc1ad27b1338a47fb2e9281895bef815b336fb697bf4a86c8db33efb0dfc98b5d7c0afcfd6adb3cebadf7c83a3df9d702be3812fdc62316e699fdfc0708b3fbf85e1d6f6f970f3a2e7b928beb40abdb128909a935108117b9e5270ca37e5b76f60625b01e8b7ba858941b69804da92b8677e6eb1271ffd16db9298351fe5a3f54609c3602d6f0483f5931ae2e82b7389869542ac6985427f23c336dd4ebba8978846b6e7a6d4f3dc9cecb4cc4e03b7a49e5b5234c23dbf25c1ad8d450144cf6f5db8257b9e8b92a3443178c3a7020989cbf4d8148cad8137095897e937fc8d4501eb8d4171a695c8a6974d432322ac10fdfcb62481b8cf6f62245074cd6f632410eaf31b996864fb3c97fd86be025e740570d028f0863ebad8c58137b41e603b97e8e2fee8a2600ddd3730acc0179d7d362739ae9ca8f0a86f6a38fd46fb6cd93707f07ea3d51abc9a65cf98317d3677ec2196dd8159a28bab3759a5923ad354201171999e2575660a2e2ed165ba96ed54b36867ce9ce15eaf497c046110bf95e9d531ca8e286b64b4d1e1d6f15b156b747cc73fd89aad0fc7fae4d90acd31554451c51249324ae7e5334a29373beacd249964d682dbce5628da78b111a0d136dd08b068f9ef5ed89d5b1248bb3c101c1c66d9acd6bbd3f2dace3884c95e12a619cdd6474ac1450e7cf3f271031f9f86db22b40db3621fc5e8829c13a48f51a354a3944e9c3884c9f4138c3d9f559c39c1d893990c2e7e5ba158a3a841db304b7e5b21f97d2413d76725d765d626639442c66d85e8f71c95516e97d96576a95d7ecbe465762925a78138ccca380893824bb3c7ec37f3b1b96f261bd9a956719895d5b9adcfc69ee08ee41c70fcb9420cc6bcfca06d628d40e8e3ac4f8c6738866d2af10c8de05c9f98855bfab942d9f7748522488459f4637a08b3e8d70b719a5e86dd105d87c340a7d31f59a1afcf767a20dcfa98b59d3ea867fb0e036ddcb385371d03d970cf76fad5a243224d7fb342b12967027198b55da631c4df2eefc54baf98b5456ffb8d76a369df981731bd312f67b40db3b66754669b2a0eb3b6ba1a48d7677d36f6c623cad204dc52553c0ca766b99239d2c065e7b2e82539bdf34ecf71200c471d763f695fedfb0e0cb5c773b137e6658c5d1f0f0690ef81da4d606c8edbc0edde76dc1a6edf40de1a9ee3ae034ffd132739ef5bc3ad7fd0120410b0aed275958a95a3363dac4014a470a6adec929724886853ed6ab8dc72b4db40e63eb9acdbc0f3a5b7c4abe10fcd7dbbbaa71e0ca7eea7ed27a7ef562aaf0e659fbe9e92adb9fa643b87dbe312fe762af14c9b3e2487d8046e6fdb19b8e0e4e27c01b717f8e6c355d3efdca227c2ad95172b9a1e674853facf664fe937a6852bfa766bb8a5aa8e593657b8d14c05d291cb508317a459c798a7d7e85ba3bfc0973d486b859b0cdc0a3f1cb8a5fd08b7deda6f18a3b543805bb2b515d31a18d4da975b6087ebc510e3700c6a4bc410b4c6bde2d6c6a8e8c271258a4a3cd3fb714b5ebb0db7e635edda7747ab4fb68cf422a5e4cbe3acd0b67c8f1582815b1427e5630d5ab6605163cb52d2961aa8b1866b65cf961603051cf082cc139cc0856b650f7f59352bb358f1e1d621ab977bc2afa750eab9157df4933f845bfc49f146f36d98d57ca4597edd6eaaac76cca2b5eb8d4d9144cf6ac3acc851e06213df64d6c6afea6b09e42bd3f23612a7e51fc0c52d3e36416f6c0b2e9a9be88d69f144b37a632fc88192a22c653a3cd222ebd0072468d0fb54dce2935df4c6b248ea08a637b6022f1a486f4c056e442f86d317639a0a0ebd8fe1548689a66284eab0e8076aac9ae8b0c84b13dc0f3a2c2ae207b4432b3c50010b2e58ead04a164bbddf60b00983f59ee94dbd973be0a569195462cf7313a4b386317a4be4b9863228d3733e3605c3a39b25b48c9ea7657468e5a8e7294865cf9c39c347cdb44c73d1a5c1069be38e82b48c6630a7a317c39b3fc12b38622de2256ffe12f00983618c219e9154d61b25f1f231c4cb5ac475176d741137ad21ff4a6cf9a396604b106c2ee27a7f44233b81138ab03ead90d0a9f7bb46360a994fadd04a01288b3df1b3865b87d9101763accfbc377b8d0c71918315b23d6bc83534ea2bdc14a8123bde6473fab8aedb58fcf560e0e6c7c0cd1cc7651457fef2d5f4c6805044c7313e50c20719f001d298316e488e3b51374a642f2771cb447ce08330bd311f64d1eb0312b4a937e683273e36164c6f6fd017256002892ec840a28c335c14075720e14510471821082e1a7c7143726580db15279725551bb31eeeb0ea63d60dfdc6a6f046cb1b5a61889d49a2e7831848c6f84305b841072c9a596d4c181a415abc3230180ce6ba62636abcd1e12ea9e145878b4673cbde320a386209500143a0012a608831db4533171c0c47b1e5157084d2d1562a321c66f1571268e3ac2ef7ac54298088a43438353ab0a375b85cf4001688480d2bb57b01b4b9f6dc1e7d1b1333a655bdb132dae8f5c27c382194a0fe67f3312bda302bc665630b1b7cc006660c5103194c0604a14b14699c40074e08c335270f54d0f31db7b49e447aceb962c50f68734520dcc3925ca406dfaa01ebad37a6c6111dae3afc9624156662d0d1ab8034cca441268d35d8d41b4be38cde581a63a4116693698049c305d34c1a58f4c6d288a2c3d89b4608daeb8da571c4a46848010d24a03104347a804619687811ebd0f88246157d52f2d048a2eb8da181943273861bfdf5c6ce58438a30bdb133b2e870f69e61459f7a63673cd191a33963899a3392b0a160646fb8604616d101527c1b33a38dad3766c61abd3133c6e870ebfda2696fcc8c301ddf7dabc58c373a068de0dbd81963747c17e3eb3a805ba0efbdb50ea648ea556ca1570753c03a1e08b7b823918e5b871b106b5958a0550d1268ba76998fb4c2163a7e005c838f358408b742283b6d533b0ec1377b6366a4a0571288c53df1317b53130045187d07b42eca5ad6d668460900d0f29bfc3b49111c7c592fb7b2e6adcdf0926d6c8ee176871bb3466f6ccc181db2172dcff5c6c6d0a0d98ba69224462f3e34eae8842fbab13ef26154da317a633cd0a2c3f8844f1e6881a219d343243248b9da0a999140b18ced916779fa90d5d94321bd2b2444b281918af591d7c008c5b2e4658846a25214222fcf2b149f581ff9dd9e504856c345028bd6f05f7d5ce510be9b254ff8cb45286f46521771cf2bf301e8f596d09e55c28428a287c16045746bfe8683205e62074a104d1436e04050b70433efb43cb3c4c922096b40d991629ad019155c964d6e65d1867132117cd951154a3421082cb00111c29c717d1288a703496ce186185e9ac0c2956de716003a3b4b024d57f621b476cc9a67e2db9eef7667b20886e69c2b431007828ec42c0392d5d5764414c2f7d559ad0583e08b603a666d9c530cab8ff47e6885a6ac4f4c2d9121adfa3c94064405c6e68d66fbbe267c41aa8ebbd3f9c49db62f457342bde589e6f22c1aefc4ad040a5ac2172e98961f00b7ba6d6edb2e0f64bbfc10ee92935082fa74e92da189c2061be8d3a98d316b908100b3e29752ac15d17de9cd8e704f967537300b02ccca768dce5ec4055f18912212b7342fdcd2c0d81c05b7b866db4b44257a83045a6d9d984d4c11305f761924d08b7bb2675df6e84696fd066e75cf0e011cb8d5254332825ba7678f49d96312193ffb8b5bf15d7da3b36e677df8145c318b4df1095f7c78a4b3af186873657ff6ad66d7a89ccfbe3b597d12bfd0703fe79c73f78b4e90a19739f0c54309ea5dc934d8ec5aa869d9b5ec4fb48c6e966559986559f669e0c601c9d627d3b4a865f5f1e1bc4689accf3ecbaed52cab5bcd90f8bc70cb2dde5a2720ef7bce047212be09066606429d9347813bdcd2c0706b69bad6312b7aa00df7c45393f9b29b625866c9ba52eb6cd34e6770e7d65df8f5ace12a76df8e111f713c91c0edb6e6946c84f94995d845cc112ce2163fdccfddf0b3ee7a4a260c1665944270374a8a6687db43edd963a57d7aac8ff54609f7f858c31b25b168367dc83f6aaede28e9ce71efbacddbbca50decc00de4328e7bc6d56596090ccf69db766dab32cc01306bb3ec34639e40684f8e54f20ef7911727144edfb7002bf45a9f687ac8eaddc0c48744e404c24010e09e08b4dacf6d15c47d606cd662415a3d224d4df8584086706bbb96ba345d1b3ac2adedc8f6763a55283b4b70b2480210daf45903b3e253208ae5993a30b67703b74caacbd720af0237d9d36d120808f7c86b219196b18d2fdc303214a0061bc8f4e9a6cbdf7002e335309e879d63b21570360c04847be2379e5b9bfc46277ce1ce0908b74cf452d2cbeda6cb533d7df67aa65385d2f566dace52700bf4799f1dc7b4696f030315b10cdcc3f5a237888178e01ed351a96f00dca22b06ba41f6988064903da61731e3dbcec198a58559a60f71111e985ec405eb797cd3390f8c2a8e4bb4e9b1f9218649a01dee317ddbbef0abcb3b6d52c1a2e0469b1ebde01ac3c630dcbae16f8c81e196155a684338b008982fdc2902e6d31e8df0f16456c6d574bab5e906316e702a668e935e12ceab45b3190766f155e01acdffc2a486a0c02260768a80f9f81d08657df83248a03806f7986eda4c8f4c30991e61dc8a373d6a8948310b6e853b6df26205b7be700b0b6e85455a7ba7373d8ae19677d3e318dcea6e7a1ca34d5d8c15ca2eb5576773e0ec133899e5321d481003adcb04a64d5f718bdef44d02c99bfe49a019c79c4ad0f542e96e3029a57477f7457cf16210b13c05224098b505581f1b3a29a51f0476360065e7ccda775b125f7c18d473a565800b17640d7cb18a7cd16cfaad374a369c1d6ead321b4a50f3a12c989e3feaa33e6a2ed29ecba736c1b458cf9a358361d72b969366e16c1a190966c658ea78f6a2e3105fd8f56a1523912140e239a8e3975d42daa5950c412dcf5abeb06b0eea3965aacb17768d624f7693a3a7cca475365dcb6af3e1c469b37528df31cba667cffe98359bdbcd865bb37bb7a5c32cc0510d67cbca427cf147cda912ecb61d4bf9294f1f9f5039ebc6290c616d5a87524a29b3169a8febd3e17c71d29c5b6b5e763f915356c91a0476b240d9e9f92c725b87c383a03daf7949e6b729a59cc2407cf145b3e70e0eb3966a9aa669332c624e0b6ed66de92efd3efec9f225fdc4a1a7200e4d3f410ee28b9f45b4299db34eb9c53e7bdc6c9fed9f649359f24372ca6da50d77e05b1a2311b838679cbbf16836e8978242f8fe84d6d91b13814607e98d95614573a970c5e7d6c603c76b60b89aefc068f2c0e51e6aaadae9cde94f4cdf7e327dbba96e2cad863c347d56b7860ef75056cdff4e2c9562290ef6c58e33c48e5fca5862074b3bd0d21c173bba24360e1df7c21cdff1158ff36836be27aad3702a107513f8c5abdc68d5bb35c818438ee146cb31da6829b36e3d25a87f57729afbf7cca381e62ad54d60b86a93c994aa04386a4ef57d99f5fdbbea26d5516068f33d01723ca502555fcf00b1737c8855377d3ef366409de62a30e4a155cfbc194cffbe0c64f370b379f8b54dbd21c051dbfc4bfd3b8d95a34e551f72daa6de707f92bacdb9d46d9e4adda6b25235e4816553431e9ae634577d48aac0d0e699ead49321768ed38021cd51b5d9f8682a0d39aeaa4f723c559f74577d886d4028ab56fd03439a0a65d539aaa98650563cc070f6ead25be229e9ea6496574fafaae18ebacc5a7d326bf5cdf0d530b68ed77cbd1974bc4607181a758d0a945d738e7626b0e6d19b21a4b90a87b691b2a33743f89de62ad93a6a88a56b4e4fbd19b86b6e9ae13b4db5a9217d91ec9a87935926138e1a1e754dc5718ae3436c0271a8541554351b5f9154fd04164515c3be9b25b1691e9be65ff5cef54649fcf7f8af86a7fec027dcb773d153726a9aad0c237630a6b9e8cdc07ddb408e0644c56f6480e9eddcb9e5689702c355a76e03863cb48d4d7f5f6ee9f84aa5faf79b550dbffe9ee33ac065568e1a7ead7a0da8fa32d0cd73e4a8e1d6aa1ae2b8cd6b7e032ef7d4d470ebaf86dd531f9229706f0366c7018650564d732536cda7a7849be6d15b124ff3ad86dc69684054f40870440372b307034d0d63d31cf51458f30fb4b90a0c57adba4cfdc9779ba7bedbfcbb4d5d66a99e7a4d557d48d280dc2a30ecae35ea3420f70dcd9fa8fe9da63699ef4689eadf557599f555efa9ea29e146d527dc63d85528abe6eaf6fd3a24c243120a6b28abbe123b1e47c7af38c5846c630d08cc33cb27720a0e8da663628c310ee91a9845c5c418a31766ca340f5acff315ad9ff063dd3a49030e29c639c323de18679669ad6519a5fbf821e6ef8ce71863e448797e829c446a91720007b461d68a594af8429c1f191a800cdc0aa1ac5a7e2651b03bf3c96f18cde7ee03cb7ce1cf0c052e6ada762a5a6b463db32ed9e9cc42074be420a9a5a7647a200aa1976790ce24a2107ef492d0ca1e8842f812dc76c54b3e389915199c493f343d83f21c7734bb30eb888274562a5a4b593b666d31fecc8a59166e7722123e286ba63766c569c3ad048c11a659e88de9c08ae652bca56876f679fea1b7d5da744e1a9254b4de8c5aa39fda331085ecb34f709f816c441432b73292066e739cc4acade927c849e090a4a235a5d97c0652baf121d7ed5b9453e07627bcdcb4ae77a2a20d0e17c32f5c3557ab4c28ddbdc81e58d2344dd31e14b4f37daa237b1b9008f7f06b401af0a4ed2ca97842c149e1e353ba4b1d9332f06949f18ddee42e691915a60ff19e977bc2ad358e4908fa3e551130615704ccc7ddf40fdcb4a845ed282d6af7b477da230a0c5721ab8774ac55669fc0f05d34bfc848e08ec6d0bc686dd36078a27dbb06864b669ed36ab8645a7bf8a11eda780f57bd38340fdfbb4305d25d381eb26a1e02e97d38a4379cfd5181e3aa1ab00898cf74d291c34655736669e7d380b306c4398141ccd2ae0364e51842036e9f584324908dea7c9604c271be0a5c314bfb070e314b7b0a1c00b3b4a3401b98a5dd036f6096f60e8400b3b473200eeba31d07b8718fb6124806eed1cebbd41a0cdca72d8452350a4e1aa66870bb33bf44b37a634b6eb41863f48fde981856740e6062acd19cec681453492ec15d5ad33aab4974e5246669217d16cece1e72cdb44b0f86d8279b673544d13cdcbeaa67af5906aa79769b1a16cd4d02d55c16c936ddfbf71a70b927751a70b9e70b8b6473cf52a6d3371b1faa16cdd6ba83cdc6b7d522d95a77afc6f0247eb6569f74559b61d16c6ee32b921dbffcad61fc42db549ff035255c9fc4d3d62857e9a54703ed582bb7765a434eea2c034f52324bee304bb2ba556b8fdcdab34f1e1a4acf2984ef55873c6854626735caaa677d123fabfcfc102fadd1ef8d851184e62c3ef33c1a62493d9b5ba08f633d1b28ab61d16ced337ee91e462c3a6a9f9a119a764e9a5e122d3624a79784665996d4935b948d884616895b19e0246e694966ac359006ae93ccca485c6b153c723a3dab27d0548318892f0cda3616ebd5da275829277106a290f9a9512ab2292d83e2cf0b778408103091e5a46f7c479815631d29ddb64ddbe8b3ce8e749c603a7e88713ac618671db25bc3eff4ee16adfacca2aca659a6d5f0a8837af787ddbad0a6975959982028bbd43316e10b67470d15fed0cce5488ba737e677b8aee3c255d31a9e866a58d43284d2323d9cb29ddbc08e085f10b33ea0e553cd8e308b33133899a56535360581f404c35587abd5fcbe55c755f8eeeb59b7db704bfbf68d8156dbb6d5b941dfece8cd59a1ac7a0532aa20a3a5fc6c596fe48ffaabf9705504cc17ae1956fc0f122875d4c3cd7bf8759fd54c0d1228258f1ab37964be4eee929979b94b605d375ad6100aab23cc4bb2b2fec0ac08852f2ad123139bc4d0b8a465710a9f04c335c3140c174c371bdf82e93022f107beede1c7ffdc685395da4d6095d99c4623b8bd2d180d08d18599a6b3b9cff33b70b9c74c5c323197c165a6efb7cf3967d1909e6094a0e63ec130a86750cf4730dce9c87d192848f670500c0ae223beeefc30a853e76eb3f970563b3d51447ade9ba7a9bacc9aa7a3400f0c8734ad5566f3393094a1e9a597e454b98eab5076fa95451280e065b6095cd60c1ad3ab73848a530c7785f693ecdabbcec47135dc38300c6aee2f6e4a7345cd716038a43766993653f7ee0aed27f4f340d315bf31d0c660309816577c73e7be699d77fe32503cc2170269be693bea7c22dcdace50823af5ed747e0afc98c57d43811db3b87fe08a7bb87be06416770ada308b7bf4927075fb90dcbe659582b376f30afa1997f0853bf4bc4da99db36f91d20a25c8c80867ac60c98b61f21765c934ad4f66dd6a973103c1f159cc9a4066a6adb6ceeba8683584b2d3d969993599456b58447b7bb655d329b89324fb46c5a8e94625369ddde439e352d4aecf3cce3f66cd1a4209ea24eb456a83380429a1ac3ace56b2021955ec00ca6a367f8ae1b6865f6f77fa30fb66a010bef8ec3c39e9ebea3c0d8b66c7ef65c0452cbef85595e96cb8150e1912c4401f62a0bbf8321460856ae016377f7a914aae25d3928fc3e7f385acd0fc69ee7035a4c3aeb9095691af7265eb3d95f715aef79c04630cd69cec26e8a3b9d94d904773b49be0aab9ac9ba08e1ccd6ddd04713467ea2668d3dca99b604d735c37419ae6ba6e823fcd79dd04873487ea26d83597ea26786aeeeb26686a4ed54d306b8ea69b206daea69be06ccea6c3d14d909bcb61538323878e5aa5e86b21fb58ef969940848114d708b7f339dc1818b1ee76b825048c249a83f0faecf912e4f5d973d264667a45a02934328d0ccde4c586dac2e49de0947a82f33ad0c90d781b11a82f60a95313bea90495ca0734338d9a4e0c9b530d70c43a9ecbe1450b72e0b0a9a1517d2994d77127d3a6033524045c5ca3e3f6898f1f7d9abf5e7c69fee4ae11c0c5ef4e945f336474ecdbc7ba20958d1d591d953e1fc584bb346bc8923564f51823be984edfb69b4e9563646ccad0b0991a55163695bbc4ceadbc2ff59540ac146b662fb287b3903d5e207518cbec524fa42454974ee9a45446f59951fd3046a582915121a990caa892544966be2eb2879f458713298c14c270e1502da95435626a6a4ef31c39eab218078eba32d8d8d0d4a149435353836a9ee5ab79548a4a613892d9e298d3b7a8a218edebdaa28a4b1ad8b12f56ed31cc6743b314c38859da92e5bbaabee6355b54cff2d9542c352a558d64ac1c35c7d6875335fcbc1aae3eae863fd57065aa216bab4a1c5b336674d0022742c04612a0782d11b6641143461a3070bdf8096e4c0113023602d74b8291451044007bc20b5c2f0ec3a50d27106184332e19546490a4a04599a42e133695a6171920c1c5ba41970e27120e929a63ac301268c892040a12238186788c048a359f151b237b964c56c3ad0cade1870333b2677bc80b98ecd9fe82629a2db8dd0967acf746352ed55559d0d864f1c5e65f640fd3dca6a66251d5d985662ac52533a90fa9059a313bae834ccd55aa8a85c771dc2655b764497dcb0ae7aaabcedf0d8ec683c7cdf6954035b5e63c4c5f09b4a3c6af6b55b1a86a2a96efa7c7a4d87d2275de27cc435d9a913d66d84c1996316dbbcc62335d7e319d2ebd9c3e64db988c9463e6e9a598ef5952dcd7b5e5ab2b7bb2cbec1c38bbc4a52e4bcde71ec625321d32accc901bc4d88c04622fb287bfa16ab87d5e0dbf5557cd5433cc55f612615ea298ed5b5a4533cd0fd3252e7551ead2a54c98e632cd4bbb2481be94989ad3acc6d05c870e32aae7c8f12c925f58bee3c0f12cb26249dda6be6cce9f5d24d06b556b903df135e74f2509f4d251e369feca515f386afcf7974d8d4f9d3f4402d55496a419126e2a530dbf2f5c694322ade1b7ea708b97357c875ba38e3c56b1c1ed0ec79929135de18ae9fd0646d78ee9dda50ea598e945f6c036a42f693f6112e84aab2e5f12d2b67d52cc94528af9b22481d62566cc24d33cbd7c19239124d0962cf45bb46b5e4c9c1725cea4d4e55b5e482db84ce7ba2449a02dd9b350ad4aa48a257b566592440a2552f3a5981db391f9caaca41909c425ba2e7bf8524c128f21d325298be673920492cc5d9a3f2966c5493bf842f925aeebabf0eb188b64c89489b12fd14c982c604b3146121346f6f06338bd347f7a91406c050dd460230419b8818b3fbf4820d90128bc4145154f74e1e2cf2c2450e452040fce9839b303177fc624d04f0d40f083335cc0b8f8338c048a2c7802079098b001988bbb343fec22815ed1753a9bceac9d1fb398c2f17f9040a72a83ec61315cfc089340a65a83ec617e449240f586f36352c5a2551cb0d0ca83ece1f386e1924831ebc397629a2fa564c9dfa42bfb3257b78aae9abb903ad76a7be2797be22ca31d49fedcf881ea380a96e329a45e8ae3df529bab685ef33076e9fd72cfe66acbcec91a24d00b751da9d473d4e845ccf71d478d5fc6a82a97e852dda6d6542ed115c9d0ec55601cf381510c189740560d3fc81e79ee546f903df21b913626f1207be4c62e739796c2c487512964356bca0f99f334a8bb37743af72cf2f23268db4df7e2bbba7d12e8c5c59feab792402f53fc565797402f2d3eabb4b2644fbcdc5cc0ed4ec8b178f970cd7c3edca559c3ad21abb7866ba6654ce3cbe8b178470de1de05997e7a1895b46fcf22b1d0670f374c8751a94c876bc64c87514902bd5095257bf8de8b7b9deaca1e53fd640fbfb6bab247ab2bd9c3afac86693ead973d652450993264c88c690ea3d28a690ea312c77a758c4e47cc9305dcee8474a9f991075f48bd741809f44add8b04429d4ff9340b09949d4f6312883e05d2ec95792f0f05a2be9e02a47c5d6183fcfc4b96693e928dcb742190b84e2f308b760fcca26560168d8259b4fada2acd7c7c9b4a730cbdacaf59860b10578f0b81c4f5d5d791155d0891aa2f0b6c7101d2096e80c519d74b08242e547d1909610933ba7811c30157162da84ca8542a7335da584f928e318466684613001a63146030482c188dc843025dd4d4071400109fb0545a9f0ac42408720821438c2186800100001800184dc000a600c2c1d94553a2a299258205e45a745b0e0a532622fffa44a7ca3ce7ba36d808847f4fb94025b5514101edfd170a477ddf9bec2a9ff3b0fb520c84ba0a6e9bded66401f6c18b9d2397e62af84663026c1f3971e67e95202bca8faee684fd786305aa7e964d2a75de25ba76109d00aedebd41023dcf25a542524f8ef8db11074156b3eaaf18e433f3703ecd49e04812a5afd8875919c4c18881dd4dd11675ba62bb8b0576cb434b2d679d9430265fe522365e9741fb8f2b283296ae624a086caeb04516e678deefbc62774bd101702884cb21a23d6bfc2236d491e0716f800a0d8ddf2f1f87b9951b81df69dcc02e18c22c5a8afe2f6d62779aa69b59fafac618d224bbfffbc0760baec0050ddf8b682d452c2fce5d1c374e3a38c4f0fe7b94ea55ff4e3f5a69fe5677b39a40bc2b62adc6ec64f2994ec5bd6a646e78d89b02be14389d75a5d8504a423de9e4bb7f230c5d8534dcb9d4bb26214b4481a62aa2567ef300376766e7092216b97eda8b5c1d94cead18e171eacce317f79ab331bc56bf09855e49b85b458a2d938599e876c0d831ca0d289b69e5094a8d887c839b7df2cb755abdd2218c00a84f9699d528b75d481aebd03ba745b1321357205e85964dcce3f32e129e3306e5f0c46b2cef83ace0094861f777a27ed1205cdb559cc43fc404a260cb709773f47e730857001a8c0772b17e40d17ae373ec51f004da9f3f08ca4dbacf07090e2674d08731c485b6f5fb722feaa48248e6a581b194d8e1413f7d145ef591533b1efeeaf7a9eab9aefa69b615223a60f0b961e90206a0b20a1717db7bcdd458871e5b370da4adf45fe95527cfdce421129217fd7534f00b5047bbe274a617d345f81fe991e51d4c2ff500432c36ce02765cc706d28f1339ca8c2fc26b96a99b7bd50bd6679f15ffdecb87154da3e63878ac8963613baf92d77971ce201ad0f435de65b0cf6887558328e437dbf177acc9a1d894e7b39a5838d1a96e6e4393d77aa7cfc0ca570bfe220a4a8d84daa76b56f760748e6c80a734de07bea4fc44ed10cd97cb83783f4c442f5fb3d2e63cd8f93b03849139c5f40929c5c8f8fa8e8c3599b4fbfee879bbec1dcef2631f7843598056e5523c43c8a20f664c64c66eaabda986a8f6ce67fb2c7ffa6f4ee293266f56096292b981958c46c72fdc31af6a595ce4e75c813736124a5cc9840d877480e3b6d2b6b390b84e90ea6cf12e26740d102f8dc75ddef18b4171a7479d6580e7d9758efe5ae5b1a5c25c6e2eb19c680fb003583232b11c45b406ddfb1e2c28d5294862c8ffdfd336425cb80c348f7e34a012b41879f69cb0cbb8856ae9508019bca9af8e2041b6aa5445dd1d8d4ef9b9aa6b4620ef94c61851d4f9d55e866746257ce17729ee96303ea2bdd9bada05f9ca5bba1e707349ab2a14b4369e7aa7b19528a1aae7b3c46aad879ddb4817bf3468d6d395a96f159a23bbde48b47eb6a1c3aaf6280398e27363bdbe45fff1b96f053d687bde70b5a5bf2e4b643ec27dcd1c6558eaf4f982ddf1e0272cc2e7eb1b0af278e421a6676059b102c0ce9f8a36a88ab855dfaf808a4d5623b87be742ed2450cf6b5f707a644d4f4bc8407efafab476d81fed165a8510b251011097deaaedde8ef827dc37250074950b15920df1a8f28908afc9fea0163895a3761ce54d28385d384627ade7f781d819affbb9e3fc0124810da67fa5677500f5fbc7704c145abbd66a047d306e5af07c25847b3e2ac763897d77cac82b8b645c12364aa54e2baa71939cd679faa3130fdefcaf54e53394118f5763c95d7f5d167f8dbbd01c16aaf5543de4c563da6d769a69930ae1a34056dcf8901072ba252d51225cb1a76de7920c2052d00b07c46c31885ab01e8cfb31897ad8132248444b6444a5b62b69b98dc1bab790717917fbb5566789f062ab8eca12945d48ae1e719dc59ce3871cd6b0a72c64da714889571ca5c3c7e8a9e166978394315b7a3674af2c5483441e4834bb2fdadd570c4e71452d9969d502a1c1a3a5271f6ec07ac4d5b4739332d4098efd5cdbebc9c746136295494278ae60cf07768e725a22444e6d523e96f707feb14d76dcc9201cb66049c2b94e0fdf32a57057faeb2f2895aa9c5473ab22814395df5f31166c603b5e3de54c259891e1b283396f1ab1e140cf0439968e66fb364cf33e8328ea7eb97bcf1cdd13f058d750cd5c6e4149f5bd516927805a5700f995a35a3a3189d1a3a0c4c4f667cc4254cf2e0ddcc044cad15397fed22f81e4f7dc2bc1a9295ad0346ae5fc45c5a5fd9b184ee277fa462d59852bc661e5213d11c74e65d2bfd3627ba4c909a50da87690c499d495dc4fd7341c9c0b8bb3e27d0c3b6e70cbd3095259cbed550dfb406133a988a31bea2cbe5c20104d4b922b33719c36fa8c7164c5c65959d4eee0d69b1da935988c77b95e212df9ee886178d68bfa31e59e3f01c3b87d819a1c1210dd4facd2da5d425c48e3423b7b8bf50d9a87d8aeed26ccd27775812e335829d39080a3eb4dcb6fce58a4510e4e81340049ac22576c42a0562f164c880b9f32f6608424768d4491552cef8c307ee04ce8f81362ea88de1595cc00c8d9540006774816f9d7715d1ed55022d8ae809ce2a18c027b23b33b99957af39a766320af27903dd45244e56f12e4eb10dc0a56181a1bd967577b4a32313b23c1e1a030500a77d390e4c29721ebfeac3fe744a2bb91c6b34b328f010f2e2bc82a20ab75623d8e1d7b14018ea3dc0aad1b55a0235a8b1b1891b4343ff1971f9d6062075489b43c4085a4d1466bc4bd599def928bcfa5882a200549945d52a89cb84e6c0410184efd3a525ecaacc1248499fbe7bac0733fdbeded0628bbdde65a44e0f3d2e40a34fe80ee2091c9845b6e06828f9c86239d3c2207bd9ac2c7be7b49061ad5a47f9954c666505ffd4f1b1c7b27f4981790fea1886f4387096be84adce097ba850e9ad0eadff267fc1295cd43ad1464034aec41594704a559a2493324e33cc0df1b47238a81b698c012bd6e8578f6c20685cc6dc80c8598daa46f8da266b4897015ac9cfb8d33317bf8a564d539f8ea87daaf0bf7df89cb5b145f0f431c949429422e6317e586604505cde881aa913e77ad13017daede493cb069361105d735a49cd85ab782425dca4ccc2c500756ba4c1a17e7d763cbc0dd6c00366c6aff0e0c7aa8d4f51131c7372a6466bf93338c76531fb1535b787da4d3440db369302d4c31ddbf3d556558d5cbd91ab91a409b5d5492972fece24cf02e3c9b4fc154a2b9680f6b31e6c47cebcf41bc539efacf12a86a208eb386aa6b3ee2b2b786b09d88f7a542c15fbd6dc250c13730378cbb1be4e3206e5177fe0d43efa29d8eb407177190951563d5e0d67102548b0e9ad5b730a192eea74905d0e79cadbd3a479f8aab92d2709b1ae4b66bdb5ae27081c0fc858200458bff002db2269fb244e088768bca09eb693068363e2e3d3e2621d571b08c85b05e76dd54004099c979af73b74446c1ce5bf634bc144bcabc3887090722f36eff26dbe869fa016fffed61c2c13018a90695cc406e8f5f249b3cb07aacfa922ee5ce86c205239bcbe2b6616f82509a551f0d2b17832c7548531581f3adba1890312874136b7172c823fd47427d794407105c9441bc37e39002fb8196de88795870147e198fcd99d50e32d660b49b5fce164421c98285b55a21ede77da919df09563048614ef921960f4cd94f4b71d350b164ee2c9ebb5fe0484641fcbb87579a764a2c78a30ad507ed1053d5bf083cf206729a5420199cc0663fde2caaa4217fec92eb740038455cc29703794a3717880899667b02e8f5e82f40df4aa07a898ee484ddecad3ecee20f0fff3999327d85e5e2d91da284ff16f0a4eaa5c92cc48c5363e4dd90d2dc80cffa8577447ca50c70c7e2cac82ef7d0868bc3694476d07079b6ec58c93aa81b14f4c1fe8537997ff652dee6a79321c23f44e7f19f663656bd01accf39e0563f2c0bd16709fe01004c314dff27c85f9ee6de1a2a0778ccb566a0cd10b1c77e0b59ff901a8142d2a2273f1a8d24cd2d39b59b3b4d488b24dca424197569d81def01ac52d83986b9a2d4e028c0d507657be5f4942901483ab9cfa883ff864256bc3e7c452fd0b526ac4870b974982251c111412464622d576c3b2e497fb3e9fa4b4b4fe928d02f73f4e16b839bb091ca092159b6f625081fb2f38b7a89f98725cef1884b29b17fb06546441c5fa3aca4f9eee1858e2fe4974e4b5030d22c729b544817db2c9e287ec556a5e310977e1ee6a001c2f49801203be1ea7af19cdb50d920d4599d8096bd974f1dab3d1869d5a1a2b027dd0506700d07e21062b6c8700fab0b2e81d5242d40064e4c85344ea656b0a08f0843aee614d541d49b4d9eccdb6a2e553443a65c1072325e9f2037cd5f61bd4c352e93688348f5b47fb9a1567703f32ce309dd5267824fd7a402eafb8ff297257889f1b626d7449c0365cb3e9841ec278a52817672b9bc8521f859c74ef003fde958d7810516d1a14fad4d69db290d1d3bde7f394f057998148de17c9b704cf18506c187f6d610e6da0a1ac1aab4c1129c53c3e2bfb40fb493fc564ad38b2192ee7f58ffe3a79b17500571087cdf01b18633460f084b54cc30bbdbec173ed94517ccef4873cebc47aa3d02b99e6e190e2617de04f4aa93ba673474444321a7e6af1d474bad78be47abd5addd8dc5137050d353c123934d0926de88000266f9df0e8f5553b6a5e55970ead7d4c10a9bbd6f7930a95467fce07729b1ead57c07a8a5384f8fb58460295701705845c47646d6a7bf7dfb66ab5f3b1cd349a04c78f653e12b3e4692c5ac18526b9d586f27251f09b0ccb1689406b3f69dc52c372be8f53ef41a3d067d8dde47afd1e3d1d7a977c366f05b23b35684fa60de37479658adf4723896505832a0857973d286ed8ef3f536eac6c5f14a064a9a0772b4e323647687f978237d59aef8cdb8085407ddf3af149a5504d6f7bf663ba802dec1a5235403cf043c8c3d8b93101e88511d88b7480bd94e05217d4ad95adde27ca12eee5efecb42c25d8084585b96ba76117919d7864da9604ef33387f1ed823336fc118b210838f4f8ce4f98e172f48070aca1f277078d6a3558a2e8def2eba321bbb94af7908f62f0027650c893adf7761953cea5c4d34c8cb261c889fd7a8fa45358a2596b64e20c6f930433499b67c3f988521cd65bb29a5d9b380aef2aa0554176b211c4edf28bc403154de89b17e219864c17ccaf098c4cf59270ef9535c1bfe21e059be34ed89bf1c13622eae8c07a3d8514ff7f82373adfccd49c2c5c1a2ac85301e41b12a3c871461476979b278b294e4874ac86e70161b8b8daf838727c93d54660031d3b5686361f4be57955604dbf97c400d1452734755cb22f75542e2e8ee6ccc5fdb307476d8e1d7c9a4e0c1a87d79163cbd233ef349fd984fa682e5507b568de97ebae673721a7c87d8ac61787bd039001483d63c1ee981bf8e837a49e3f3f86a5dfd5cd639def58d81093c0f0f1e5efb0ccf9443754a8fae795f2517be6ce86736097e2e505b76b1d6703cf13d0ff360db06d890c3ea7232bc7394f45f446cf4c9d9c4780ec3ff3d6eb18220576d14353456a30eff2fe189ce334c3357ad2d6c7e652dde9e164661267e96435ab984a9c8d5d72732591d31e97f76029c6f7200e02925e975a95459d7dc901a56a53bb783ce74d140e66fd590e0c4843407826829f2dda521fbb9ab5e9d82f0720a542988435eb2607069a2616da17c4d8b5af302383ebf35593266232d222b40f47a7a2200fff6ef8f2ee3ac3a7e02b7d5dba90e13fde67999d916ffb95d111d10ddd853d8b0bb2d72becaf860a167a9d02ae0a66839e67c67ae097b43de063c1e7c0e838234c3b3004660aa6782df3e93561f4930bff0d810e1fc4675c0313f3a131defe4a844efb55e67f24f2cdcb15b16da0e8d569b8d310b26f89e678a2b82c60c7f5a803e742b77e5a30a5a95d9617042071c18a4e10f0f49ff5586cf6312ced0e58cb139f53b1c43878821d96b18395228adaa2241aed62964f5824012fd83a0044941c56a5eb56752121576d5fff7d8c87c82eb326da356af689d0087cd97d3509a7a16a81cea6af8001cff1dc01b565116f04b97535775dea4c607d2b6e24e594f624416bca0f19dbcc85aebdf0a3344400cd6ccf3751e69c236139abcd570515eba886f5ee3b602b1b6a98352366076c83fc72c13fa1c6ca9ca43abb684def62e785863db795ea95e855e11e9d6eb43c421cc51e807dde4a03e3030019b23664bfadcf4ef308c965a0f5b37f134ff449d10eabf6594e3f04814eea9428319087322c62633d093f72955234d19207b564906afc674beed9b0dff19078b17823bb20dc751b220f96329fe6aac63371c47bebd271a58a758aecd988ab4544392d0aac6bd9bc334891197f55b8fab42b401bcd2034e83c8334c35689ad256cf8ad7d7675024f372797e10a48a8f621d840f83247e8bff480b4bb613d0a391ae8573b3276430500cc1dadddff5d8fc584dd13804599f67f2a5cb1a2055000e8a4411afcdf785c109c3f6e3f9c7505f95232f63b2d7a1a748eeca382cc1a2a549d1393c440d5fd78c76513946297838b9a442c8b529c1baf587b6d21feb031d7cc91e863c8ccf86c61477de99b22870b500412de9a07037822327f37ab80aec8255d3725054796cbb537694d4356d81cf8c074f27caef892eb7a39ebbc5f52f8a4038f237aa90a596de82e2b1a2bd8c56cbe6bc8a926066725ada42b0cd139bd1acfebb46a45389902673297a9aded4c2a28fd3035b67115c9d67ee93834f03baa9d35dec59a73abd88de4fdd9619b0624c7971b7e62d32ab56a93a3698c6d25caa9c3b6538b17a13883cf861ecc74169b0a392991084fd74db5155736ba21454303d76e412a5962075c123fd8b94dafb32e30ebcc358084721a1541944c183b1b4e15e910bf4bb40eb395d3956e042cebd5be1ef91b3b626edcf7a2b11167c2c03af0050829686cd38942234a65fc0ea11db8f4f2bd9b80948b66c80e42d2f09d04f16fa2c49d3e154b125fa90d65216550df6a4f738b0b94c75734d54b2e60a1175efa7fc224244221c15334095f5b5d8909f876f8b31d5e6de6571d4301bd4387b3ecbeb2c495a0638db2e8eba1bf8e5eca4a23410387d46406cfeb07ff9aae02179dee9bd2cd8ec925fc04a21bf5d7c34a2fbee2b640a88c191355a0475153d894d65bd49b6d1a6b012551e51689f81862329cba2c47644a538a2dff90a768e9bf1b5b372cc5609af46a883473a1560189a62c284bf9631f78a52a02c0c8f2ff0334ea73e2ab3594f49a05dab44af17a4715bab152edbd1ba97cc33e404a639e53f9d4af792c2347926fc165543c1438453a982ca0c9b828a051efe27bc0df9d17a1602a39860d8b9b677efd588cf7b28ef71657f4e72507af59fcf84221f9e4131bec7a12b0a1cbd8833dce1874dcfde89200cb534c6ef2974a34306ac8668090f98ae4f37064bd1f919f4929fe882e4b13ecdb0a6e362c63aa100f15bc471f52bea26c820f4cbef3a187cce388354acf5cc243f6ab0a584ac796e4cded40a42d595ac4eeaf44b9b787c108359bbe0ed8adf96ef780c4ffc592ad4547db9263b724dd6aa8859f921cdb4545467ef16528fa8c960c15524046709dbe01d5a2b6915ab51943f9e95bc3ceaa4c51d6fe667d783b55bec041e0ef5944a2eec42f75a4da4e130e2dd9ae71de824e6d16bf87ab092506aab4b6537f6f59f3735e17c80271c173d520b12bc96838430e9d74063ae248d576b66c58d0e5479b210d1a3097f4e75be43977f8bd9b984bdb68edf60e76f5a9664a74fcb2d8bccbba37d7b13f1ea4007cb4a24314a5db55204f4c5b1c2f836ebfdf37af0feda52cbae3e4c4627ba5974489de886c4a44db9bafac964041712011cb28af0e5c5f8d25edcf170c8534e56923ce4e061c3556bebdf5ad0d8458843f55ad1fc19a0f4205ac592ba1be3fffdf6b1be62e745ee044e840a15cd80938369c6c7e5dad9e62639da26e60d206880f44d841fdc2abad2c2b576f4caafc1a6d8e8ccfc70dc005bd861d74a1dd0fd26c2294535114f7518ad18df70cff1d34f313408fb8e20875903bf271cfe99236b8a1e9b75931b8974c483c16eb91069e2d14f8e41fca9da15f6dc15868a0df2079f5197623c7758d7ca361026d7275cbb3a0d8046226d72ba19e63f1690d618046eeaf6e0721a9c20d0c39ede1ae2e45ffa28db7628cd5c665525a1969e85f86dca9a93335d1303e4b1a836b6484e4f20e351568738a109311f9e9b35a4cf1c61d2ef2ce22be58e1627b9330c1baa6d02d4e342ecd95c267b94a1ada8c80c3355b9080760bd49ad58c4806d92000792314f1a265412c50b6d411a292684230b94fd00f16af9c69bc5c7907f1055988b9ec9aaae4c6a5c063c016370ca982732ae4bbac36c96c0df0acf0ac2d9e6ec81cc2c13541ebc7f327b625fc38c4318985dc79502d60a4a6510ae81abb9d82332ae16d7a3b2f9238dd5ae29a5662c8d9b7789548c17b6a81ac47a391517370960e51a9ec7d524dd86b2752648aaaf57c305c7ae6e1e695d0455489ea8e28aa59e1ea6dd423f0e4048d4d3d740ef1f2e7942ffaa3046796c5c5ae3abac74cd75884a75bdd6da6e16c69b50cafe85a5a1688d12fae8ea5d3dc6a762cce599c9fa5ae9d4eb15da26127affabab60e7d1f765fb200333989d36786097f086708e35b2ba837ae8d8d20c7827a51143ad73c0203a005bf5819ac9ff8c18e949876d40791193cfe365441a0e6346ca7f0962bd208c3d16566091fb6af22d0a79ba4f81816bbe3d47498c195dfd0a8f4860bd032d6556379026fa53f71037d29918d20595f3a6826872efbf69fffeffffaff2f96461e27a7e7fa5b50205a06d92cfd66dc0b654fccb16f5b871c06467cec8ed5c359e9a63ea40a67924987b63bc777dc68600f2dbc3933b0076307bac4b5c2e58e2ccde36ceba948c6491030a489fe0b149ef9d303f9f72f9b7d409c959bf602fd6756ac5a34fee2b3541b12c97cd64005de2cb87021bdd88653e68b4564c0bd34f9085e120411f43cd6e6d708d6e238e44dfe24450dfdea6273ccd841bc111cdf390d1dab2ee1c2abd392e04f98491a462b3b56a1697ee050efd4c15759b7ae7a43b365a6051bfb173b739c3ac89b9a8df8c47e65ee8f22d258920981de746e780cb8c60d44890882cf30e4fff9019d11f4e482728f96fbfaf2ad2b320a3a43e02ebd04e1d72301f56f402371f86d5a13fe2f5e20446e5af2d0af6978da1e86deeca4253540d8dd3a31ad99e9cc778005fbd55f3183850c2a2262bf39efd2e6ea6fbc8fff02c7a6a342287f3a320897d332a200551b423063ce7f81c75185165d4ac0de2aa6c1bf5bcd6788d1a9961d385b6112880e4e9e334e9139465bd6ff313c42963797178dc1a25df12cd0fec6cb620ba262d81cb278568ace04ed2d63a022272b1565c33aa14c503e5e061032bb25014684eb652c9befd37920852c385a937552af17c79796c809ad25ca21edeefbeb3caec19be603736fa08b827eba1907199d28838706c42d6b34a9a75e28d97cda5a3cada69f78e3ae1c9866a4d060f84fdfb6719d48f0698e1d2d08160d60a3993698df5eb2e260280b5c31e2f010003255fb72e711152f19694db11ee11e522f2c444c280b651d01fead712fe2e0f49c6c467d843713b5dac5e6bdcdba7e09eb592b287b5163e1e80ccddf78d337960bce5c443953270bdaeaa3c03be5e616a87d2b6e5cf649e35e208d829f07100bb79c8545d53910f32cdf6f514c02c21aec8434c4a00ae43adda04e38ee4af64c89a0015db5a8cfcc8563cbc1fdf7945d23483505994698f90969fa8ec2e0d4852c5779ce720d084dcdbb4704a208dbf9025e886cf17e101de33148af81701a36f0eec0b66fb8929ff9909b9c1205c5d4a888674134068ec013c32158238d782ebbba488f2a39bf7b0566b3744e2a40b7b42d0c63ff3d14b5f70100c5686afaf4ebf4406099e42bcde4821b0623023ff06a78db3b4dbf0cab54a149c1d88d6b71fd20a499834ea207e66e5de6bbba14460a287d9a4e3814a1128e8e72988c81f05fbc3718bf90ccb33bc7d3e56bbced2ae6c1c7ab1ca20d572fb5af1320a74dcaecb797d78d035850d1d83c304b85dba4feb6b2172e5fa5722f05855a2841e745b5dd98511c4e44d82381a1658a0e0687c00206ee75ca97c37175a6e117d6a74258848c2b8b061caadc6906cf4e59c82b0c51fc4f06b296943be900401dface811e24cf35eb5d78d338bb1ca94c6328de0f5b0e1c1936a7152aa8b028b4a1071398f912b7f8b55c3612885b07decd99e05faf3eda82462010234cceb44239ffa22371b0ed8a5afdb416c1f136e8b7f4a18d3c9e000ded99c0873bae3e1d0251f1c301d6b1b0d69dc44ca61dccc19589b093a7901bc918ad44c508adc2426010a029df19aeaa4bdc5168e5a511dc7e08e11f74a1569a27ce4aaf1215eba3401020ca6f88c421246f7d7dcf71fe02a0f53ceecaf492f56b874883e299dd4e0f1f220acbebb95f6e97fa5b588e9338a73772750fa88bf7d0d5337440b87e693f8010c0f735d290b4987d66ad4b62bcd36c570f6a411ddd8e3fb5ed99caf5b42e765bd2373143face32ba12078bfd8aec4e07377b560afb020f4f7d983fbc2a083988ddfa978d34b11e774bca35b07cb7deacfa5eafac3625f90e8c766d50c12f83989302dee826d7a0649f1a2f73148d89f134c2443535cb35ce443200eb473708acaf1c10cf70ec87d816c53d7ee26eb123861d834e33e9713e0e42997f14555ff63aba33744820a5209da942470d46c47d782b693e0efd0c1e8aa1619637bf4f41f3e317e6f148000dff08dd4f1339e937e9688f65659eb16f243a0c3339cd47de46e7961cc92677927028121b37059b16bdb25a72878a8147938d9581b8f0b782d297eeabe20a0b3df0bdfa4455f729ae75c8e7a8edb34d14dfa09d4689ec6f9e0d2dd6cd658c20dc70e69ed2e27127ef8ec7e367b0c20ae618239fdc14f91115658e8d866fda06a15dba1ae7613bed668dba260355d15078cf22dd63490cdb8bfdb803d09fec65011c817c910f9a380c7cb8241e99d066efd42eda93c8636033adf8ec2e4e44ac067b80c9eade933bbe42537d33e71e193a908f2729fbaebb48d740ff23cac85d243ca59add0fe6c97b264a1ad3f44a0528eeea81a4e32c81217287800b893f7a1ddaccc19bdc07093cdd7a327beab932eb1962abe27bcf908d4ee3e4178ca2cfe45f5334f2e909d7ba5fc616c8974e0de790268f1a091f6ee9760660b5697efc305935e45e90a1540838b63b4bb05369f4656ab1ebaa388506a153dad0dec203db50d0fdbd49db126e1a5de5fcabfb894aac824f56e58038ab8a0eef60f0238f93f0ab3de711247aae2e5b1001a54690bfec2667b9dde76070ee80d82e9d471cc53a478c6083a5e5fc91219769ed87bc1afc5a417bb3f7d015136c4f997a0501d0da90e5130d8e00ea651c6dcf31198c3202c9bc61548f89d6d91f1ed300767b5811882108dddb0bfb1eb808e0371aa4c6fd3e19be1e4ddc1c266430b62e7e677ea8d27865313848e36ac68423b966bca5468c9126983e933659e4ad83f332caa278b4d42c05a34b3f26f4046cac49c1b47dedfab970dc013c53f2d59145304d108380764712c038cb05155750df8481e7989cd32c22596f3348672254890ab84634918f63347eb09fc3b39d82050a655090f5ce0bac673b5b529be849784f28e907ecaa5085f86f2e85eb212327f50641185ec13b5f43a035cf832e12896d63f24fac224754426230dd10fd69788c4f1c031b26f6e7a789013439f6d9be14ea8946302b0e0694f5c0ffeba4719ede724cafaf606543b07afd028075a08ac631a3921bbca44a824af5270221c024063f201fe1a8df9ef4769023d96b27f14f8b77e604ae5cd2be65e36b24753f0079277aa84b8a87e995349c1f2372a01b5ebe25b284ee825a0df6d4e4134688c58087f6ad9a7365679925c73dcf9c2457a9b1f363ff962f3fbf53ff5552ded9a1d63da4acd5693a853e6b089c2e487f9d7464ff18fe8af1f0ed5753c916853dfe7ac890b659b5ae5aa4874ab1c2349877b6781208eada1da28f75b6fae1dfd8d5605b67d100de821981a0a1047401eb67de32514974e11a12567e9da9c0ab3154e751ff87972d9677d06c73b21dc696d80fb5e0fde12dfe9ea47d816d18335eed2f041e680120e8848515dca74c69f637331cb9f0db443aa3deb69e981f1064618cdb770889eee9a25beb9e0428b9bb1e0194c62a5845944b01ea08e7ce1026a17f0cdc9ebad2a050b3306b4bb3065004f11ff6399307efeaeef2ea77f612c91c9e6b1392aad4205d0f7666b4b24373e5ca910bda3aec141dcfcf60d514efe01016d2f828c1cc01c3a61487c23673907f71bea8965ebd8d140cde667426707228a94d0171a71002b1dc212de099d6908e8c8878a4d8372fa48685d3aa11d785d931edaa552dd7c3687ee8d2c3965562981aab8c2b81258f207c1c8e459c6c65b4da6b8a6be13991a427f5b826f9993d2d9152c7c91b2e9e9475d52da8c2cbc1d40c2d0ff5c0012ae8db1fb9436c74c92f09fa99ff5c105e727fd20532b8dfe91180935558ea44d26b597a6c7c52ab1cb9bc4d47d9344e205471f4e0d46fadd966b2a3132967cd9cdab071e78feb8ef04471642c2761ad032664c0b53400d2caaa050b75e23b3c6391f1e8b82c78c0cc38d3d6374f613d1fea5ff3a98d46da12ff0aecdb5853249ad77ac54944d39ccc087888d8dde2a099c40350cffd0935ba2e1b49dd0290e57f3d6c79df1eecc77a3517745a8a4fca4c6652996406d700d7d17117d755f96e41925811770376c29c1c36de2894e59ea4c38f3e16234cf144a80ae24e11339de07208bbc02b38236b842b131338430eeab02ff9a6fb79deb51d8ea10a48cf5136a3960c2b46e3cdd1aea4f9d202cb82f138addd254505f741a503a8f221166072ee0a214165d6a99024e0b5aa6c3f3f10aa60caf7368950b5be5e9b9cc8c6a8925c6f8b7f30c8ea37aee239aaa248870af9f0c3d9153c08668aba2448754dc003bfded164a207110d23940fa826aece5b39af807b7a4339a3e2052bbb61cf9c61404c93f7a9d10a539dc1f40de3cafc38bae2ad5dcdb30b40abb61c36ac17409deea5da74676bf00c25c45acb9621219abe297451c325fe43f1418b3d26d52134ab0ba3baefbae8afb3b28bed09457b59651a51484e29c8f2fecb57d1bf2072565455e3f899c2df4d09a05d25c3f89f3c9ce75bfc30b1155332abe234987f882d469c0ff1a2d50d622f4154619e825f4491c876985307ad101c496a63767328cdadfe8f34c300f6fa077f41cba7ab69fc95dacfdbdf7b24b61ce901fd5eabbad1ff051cd3573695cad87dc65eab1712032ae72d74457df78771b114e1a61f84394b287594e7eee59fa49ca2f39823df67ad3d4444727ecf6ef509314969a2f14389c1105a5e1451a0f24b37d08ec8d738f89b50d6787400382a4325e5952911147028ce9cc5990691306b8b9c233727a841ddf9f5c82a3a965310d56f833d8d7963be6f292c41d7122930c30b041c16439620380b848d724356c21253100bb1c3292126196f2e0ae81801c8f9d10970e1669dbcc304b0744d8d12145021b4c1fac23620234063944a1a285f40e03c16b66a39913c8d5421983ad2eca9020a2b6594262c92652d4f9833986a4471b6a485f653328b547d105b4bca6e273d2ab7a3877494f24663982252992b5ce69258f836b0b09ceff610a350cce368d824a0fe4294f542b34aebc24ead0fc18ebb68d632b40d9028eccc2466872dabb6d2ac939552b7c296ef2473d3a9dc3bfda868324d6beab3d29e1892ad36c5ff5940e304e798c7ba554a2f1665d1454ac5368d4598f0984ae368327a277b2d010088e375df15a7b965be9413b63ad8a0dd7c42e1a6e1d107b21c2f39ab5005f380f4a3cd8f33019b26cb11b00bc8cd4ffeb87bbe82b524eb3c6509141f797b3d8c280fa26746c7e4b21071ce75bb170150f4fdaccd10e680b4a23ff1e38b565c2c0789a013cf8ad20bd0e1b4e353bb08893e1124150d1dec00943214c11eeb9c520bd4f66daa3ff7cf714c9080a3ff68846ac382793c474a5c57e359556c5f73e9e44c20fba63a236beff3c368ef8e0bba82161d9f59759490275c54ecffd58bbdb8d8afa833b8892af33ecad8e3b663c028b150d4971ee5e2a15c34ceef6f5dedcfa881ac772e38e5820b1bfa9b733bd0241a8a26bc7634512568326d78d8d81f9fbf3a6cfd302a2a6cd0b730032345db65c9e5f5715ad5a049e4f9809103474327b65b91e40fd3a9013f46be2cd0511af37f1a91ec7f1a2066c546644d77b8c520dac1a800075f4d7dc990262dd19aa7abd07333c5a7c50b62ec15c75e92d62848ebedfe91fec81812d937e0874e30fa0feeffa7cafadcf3a840c33599dde4e27bd065a0208b05dca334c85266a50696927164e058db26035fc37b43208254c537dc9d075049fc9192a7707600e05627ceda09d2797f5d9e7853c4e3c1ff0b1b04dfacf1547be49e80734aeb4e9955296d480592140b4b2625195a903f4a6ff37c21650d8172214a54b87c33436d9ba814cc8a7f92b7e00d6cd9dca21ae2f27bae9f784627f1420c1546739e4ead602e3aa183ffaadb26077c54495b4648c0170c2c933fa605e4b31b8c193ee043fde5001e74ff2ceeadb797d8b99b493c31192dc49b6f3a65bca627ef95ee0e80d2004a1f987809bb8043bb9fa293a8809ba369b03a2bfa2854d1624e48bc50f89ed899b79322fbf652a82335e418445588006a690c8310fd413d249f242a3fabb912d47b80f1275ed897ee6b1549bd6b3009483e983c6f2240197a8133d355fe98d552036e6e332f1878190bd9f0c55960bf9a8461efcf50ab6ab075ea82b306a3a726482ec30717cdba75c34827ea2cfd72738deceead79fe0d8e9090baa34e30412cc56786bf20ed46c45ebc5a677932b926fd01bf1372208b7d9068f5d3df0d36448852ecb1ceadab3ced064a6231587856b47e334400413683873187861652d58ed20adcd3b7bfbc199a64c60aa36119f8811a371d00481874ced6c2e5748c8bf3885f863f748e1deb44534125badf88ea4a6eb7e3155b641f4a5a4d38c0d54eff58739eebf32bce4783c0a9d39e2fd2b708c01ab34496f23c1aaa0c8c8c999d355896a22b84365402ac3ae28b4e4bb897fe72133bea0723a7a102523a3052b0780e6f62551fd3c66b45cf05cf6cb9e707fa4c4426974a6db97c355b9857b9b763383c5164644fb91767fb4a6fa731f5a056e8f9948ef5d91e56fdf6a610daa6e5aab976572004530d5b575c111270f9ccad17bec6a5f6bf4c0ccefe3ce79728115c788578ba521af714a923b841e99d08fd48b3570c217a8fa8c922b6c1ab8c05a754a17022de53d3467c5a75c2da6575e48bc3997415e5c307be9a83e5c2530e5080fe8eef911c25e8adb4735fd043fbda5cc3a45e239f8bee642ece317fcb68e7ef7c2d59df1ccc85c66b3b2b8131c37b441d97e4b7d6163348fc0bd0e72f515ae7c7888ec7d08a4bc87145135f58f47566c0d47e2b33b09c566aac2681028c20f6c735b0d0c442c40c1e1ed25a707ab2936dc52911ba73cc1f042e9c31860de16bade416710e36548ad37ba67df0d265b008802d13351f8a82602b6912625324b2e0744702d84f3f96bd4edc419ef00544e8bd49833db5152203e2314c91aefb701e3affee534af229ce0379f9f510d1602869a13dc218f1a87927439eb149efb53ff67ea6c53d8621fd6270c8f6c1bc00fb611a509bcce134f19498c8f985226f2015593f5e2c73c64da9872a822b2a079dcc6822662452c2ad5407bcd1a6c84381f60e9ea7a36b03da2a969d04732cbcbdf7d396d8b682ed66ce325f03f1f5e66be9449db8e0937c8058e4087b89947df90b0215d842f8ae70ed0426431bee794be4a118ea8a794ffbb3ae40528f9ea0431742898e4e9af8e1c48ea4a12c4acdf174d7245d3b9089b842251e7be0a0134ddff33f42d93820828e7d6f8613a8fe5520762d43f69af7d397c14bc55d1a8ad03d89e8774f895eb074cf3560b7758f261354e8cb6db1c3edf237a89c7ee3a515c50d76a1664ae862cbd45d241f066547ba7f962c2a7d62736481ae7630dc454fc4d950c7732e1be44c15d6001e24ba123c165ea4831d929dc65bb4a912afa333b59c229ad44de6fc7c1075d01851d9189ba22383e00675c1812b59f2b1ad540c6c5aeeb9ae9335fd5ca0b8e58b182bf1d458e11911ddb1f04552943483ec2cdc0b4493abc96b4c0db91fece11f4bf6a42231ce68b918386ae8020ca2f19753edde28905a0192c80ea727033af785dbbce56a71560f79277311daa1df66a9dc47825ca4c5100699f9b4aebb438705752600fcafc1a8c08c0004ec1201e2a48f0df19ff5d839abdc53f131a021762c5550ceddb59c5fc49dba4a363414e3b0f6d83c91c7e86f368d2303ae287c1c6e0c969b8ca7287ece25a0dc84f4bbeeb51f7e1073a534f1db5220c36cafc9f0d32b2f396d6359e6102b5c1f7e460d60108632cace2e802cf14eb10c826d081da150792b349560f4989c6b48ac00db2ad048a093bd122a82422a4b57f3be7249cde9dee199493831f0f888393b5671eae062438390dcdd970ea079b4806d175c1110fdb41cb88f217598dac84f624e01d1a2b1ad6400ee2095dabd5631beeca399115cae986fa5640e8e79803533fb9d3279d8ff8c63a5c99998d1ebf844e209960e6041638371fba1746df7778a1e0e513e6bdf0641680cf5c330b55e96377860d1527efda7697c37611a5be64935f0591933d96fa53b313fa43bfd10800a1f09806dab9f5f4db1448c7233b04e0f29d6cbf4f0de9e713cbcd46405702bdbe442758a934f28259899b5e28a0588eef5843f1f0414be0ce49f46b7594a6e030daa7b2c4fb226c057b8e0db8cd284c497670814c129397a39db7058a8f8e187b7f23548f90d1fc93398993807f69958de7d8c07fc7d6c47709e23c84131d33b03c80dd5e480685e55632a96d1136383ef88c2e44b39286fa1e3320c7223a004f9fcf1088037c51ac1058d6c873cca07bf9e0f29ba09609fac47921d4065a24946157a87c7f56b3416fa34fa1a6a1342a0860d13ccaaff835799cd3414cba1f07daaad66cb9dc6ea7dd9ae932389dbbec13ab7ac612855dbf8579e53e3b8dae13f5811c5865e05e772117ab6e9316f1a8e8e348c5ff74b465af58a8ce23bc35802a1ec64acad61f3132627058e68235062f65278d6dac7159bfa0980db6ba32a7066f9181312e4e0f85ed4c6c0c5c418d43cb0a5b8fd4232121e282d3a5ccff670949ddc03bda198dc608c7c87fe0dcc86dac8cada140244443bdc52322b75210488be01e52b7c530337cccb0f89c53d418d00ee3ab4e34be97ae8df7420abe02096e3b319779a982fd716741ab8ceb31b39a7083d3887f38257ef8b18a9020065fe8f3bdcab0bbabad8a07d643546070c34da088523ec9915427aa1a2f0dedd4930df81d1f3851404c5c8e8fb4f1b7139cf599a5e44a930dda8f4894e926e10be980020bca06de2affe6ad35e836526bb274a1c34d434d24dd5e94272100882363d3ca1b6039c530f8fda7ffaa46ca44887482d609e938d1c186929f0dd2c35d1ff8e0184af21996f8ca4cc3b11a04fa4d0581f8a989dafcd51311c29b7872fa708b37f9c2128f4063e433738dc80850cdfa01ce9a1ec87d211cb484f0ae49a0b8c1249d42201d4b1b3653351b25be4f0bbf941b84ff10c8908c85716e6c8af17296be3a892d1c87b2cda242d469a622f78771b962b09c08042b87a506247c19722b9042585375de00ef3f9f6fbe073be1916cd064384eba032a6abd9a087ce8464d25f67e09399e3051cabf10c869e54dca3e774c28e0e580e0d7697db4fbe61c769b8327d28cd595750695d729c5eb86466964c0e5a51c7d6830cff4a5bb2c0db40d37e9a9825b57d1e5a4e68008bb29b06df4e451c3cb608f07278c948c7d059ffc47f4ddd2212248182f6a0909b70e1cb8e157387bdfce15bb532189015256a0a53acc85269792539438872a5c5e992e3eb92b4ea95e45f99ff665cce546b66f08c4013a9575175a2089fe115c50032db4f7629e038b541e4b8af56955dd047b79abf2db6bf6d441ce208a616e7c8b9e44028f893dbaeab6ee88bf11eb5051889f50b5442472eb6e4a1b484f24c5b7b6a250c399d5c0a82c58e6b7942dc0041ca85685f103ef646591f8e249b6750ab6fb9a3cac145e484215285ebe4b1ea11da0a05235f148d646166cd17a534f419ba10f509a48a51b0066b92006da95be120f753a8a2bb606413c688992d78972a3e31e32aba0cb8e672255c0dca33fa158179cfe29e355c62813542474e683274c38debfa5b4146182544961e17f2ddedd491c49aefc53abf61f6a3483a50dc724a602e57b30d781750c8921b5cb2ed844b9e42afe90bc411da21f3154d3214878fb5616db361e74a0519456c927dc6e28e431485e3dd8895dbf86f7923447791d2368199009005cc515a1a1f0b1b81e956144a28fd647d38369c21b7843ebcfc00d05f9474be87aa63a086b3fbf3f931407711944ed1036a72428635e1d03e5f80b9e4145f5d0a73319205cbd5e431824b0e05a55f75fb11b673068e82c245794d5e96c6378d597b9eb0a8778086b20b06853c23433bc610c0ec51785edcbd7001866ffd70b40a31062b0a706215dd32fa97567052fc956dbc13395b57d7fa0d51b2ed35cb1e45fa701150af8c0da68f83123b957e94de757bfe4bc5a92efe21b195ce41dba784817c1ae2a3a1483a464a86c105c2d9047e8168b226b1646718d0d7599bcbe96c795a5496d45e48e150fc6182a4d819eb72080c9200b76525d65aab2bb4d84809342f274db4ccc00bcce8904017201ffedcba0c62611f072718e184411ac999f246b14c0a97cb6c9f4e4845e822e1d1d1d266743908b0263bc69d839e5a48ce9bc679abe5b85f3c10d7fc05a48a0ba0a462e336b79fb2d043a3cf3f22491d5ffa1889befbd3bd0b6046586f00f95a2b502bb9100a6cd6b4e65600fee17991b625087afc690ac27410a359d8d602f0c8c4899b8c872968944414db7a12e719c36f2e9c5f75615e4599946201aecafa34de5030dc70c7e74f67182eddd0b2d64ce5d7b72557e491350c17a8791e99d96710ef1aa1315d3aae40e68f5a4444c556c0d1cb7f3c446d8f9115ff875b7725eb70f689acd81f32d819849f834e3a30ddc55f292bda2f98293ba01bb6e73bd480e53aefb698d2f76507d33a6ead61dee820d181db9c85610c91a826c01dbe73929a0183bd695ec26883151f6017d776b108e26f32b8c14389eade1f9890861510485412e3a684999dc5c058bc09b4e06eaee0b4a24803047226a9b437761902067304c6032fd5555b621ca3990fc894de5aa3348c67d2843766abec6984d207819f890100a12c23ab34002aec42b1fcab63c252a701cdf08c119e3ff9827dbb6414229860ddb1366d02527de8691df076ec49ccbd0efc6005e48dcc79855c0c6c070d92b79cbbaed10cb5d5b5b1fc2c798908ef54f273bb8ee303a3ae7f45afe80a038c1776be59f7c331d3b0d7deaa45302b0e4f6956f4c4134a83a7267807253d72225f117feedc8c5747b4aa6fb1e3d022c2c357a773178f1462274c1d151d2d3a6b28ee80c381719445ab092779b34a9df97daa8857809dd663eb6e84078a023bf2890087fa9934a904a25e377b7ac4c4966c68dd645fa969f30cdca309bff1bb2c6fcd4977709c843d7da6d49ae7610d24823c381b0f512c42211a434a8256882b34575adc93c6634bdbc3ae20640240ad7e7c19e107f3162e671c7a8d4db51979e7a87dd9bac4dff8a7533cac3ca7ec9121a1de6702640171c1c395acfc64a7491ed81812d53460e4d31cc286f24c841442d82711488bbfe9bae6005213442a0424ff8de878c2098b42407c8cddcc583723d81c0a7e24cb558f38c5b88fa1cf2502ae22dff9c32d86b9c70b88839f73d8ccd63d3bca8ecabb8455cf53dd888f73944bdc361e12b6583d954dd12a14f43d71f654d119e4d3fcc2fee2fbfda27e8a30471d388b656b209ac6eb88aa025e9aa7e9e02cbd83a87ba1793e8e3d6cbc99e08f74794a63e94aae0ad927ffd060ae66e27a1a21b1994e19a15038c566cdd303954711ddc5f6cf609e4bd0d86602f1418120d3f213aa6761032b14d9eb242ba1108b33b65c75653236a77e6271161c3e158fa243085f8d61a26618822be0065c89baaecbbb8d122b206956197251738cffb6258beb53448d969b60f6b7dbe3329dc840bc4c578b87209fa33bd24c1991a2f24be0d191b80158b4bac41264a2cfd9e846fb510e6b37fae82fc9d560fabda67e2b29c9bb2a5c6b44afb3c0d28e0efceb9cdb58ebb24196a40aad70433e4b068cc695467ecc1c43d6637532a55f7b42f784a34792bde57242b4ddb72b801efe2663db5aa011de1cafe4332d37c7ee7f5fa4e526fc3fb6b1cd8e6b5c3cb0c7ac7aa789ea9d4a4d0b45c62ff16ed24695a760108496248beefc5f7d2f0513595f5541e0fc402592c93ea6c9a160e38d5009f43417460254670978d967c55e600640dd3568620306f5800b0265510c358c935d68b3a50805be39aeb44fe74bfd7249065c9331d914c6fc0213be24cd549d7769ed68f8ecdaa5c18b9d42559a1290512163b0b414cfc26d08096d582abb8846a191c7035281df2edfb85899594e29af2e28f269f6034783e939aa91ff560a71ddec47e43f2246191344b596edf24c5b3c96135cee105d1b63fe00d5ca88a3e19e49ef0c21e0e5b1477d966a9d3ab09e26b77c1cc2d79602643b4876a493d86e6fe61211e969755461ef008e1814c222b65ee5a6f64959fc5a5271e9871e5cea808fd05bf931791483a2cc86db3d31ef57505e5d293f73fccaf956204d88050023a37215f9fdf7a723bcc3530002b93a323d7c511593685e806a6a82c41758742f132ec3fe46f9b633844bb99531871516213b80f0cd8faab289a228dc90955464056ffae3fda62d7e78261d323c0d010320f84dc8fbcd82a1d66e77002a3b6b04e8652776febcfd400444fefeee08b11a5f691e2a0f10a99b11596c619a6b8799302c1d20fd27f3613d39b029832dbfa83f6e987606d5efa6ff05437d54088ed5e12eb85d190e6fb7491452136c53b39b8f3494bc111be9caeb9043bafa076b6401ea33beafe05e9373337e20cb60365e4644a4dc8fce41851c58a9027afa91e12bc9bd29cb4825c89990e9ef3e3e3c2a0f8875c921d7450736e449b3a4dcf1c6d85b477d5f90605e96f972aaebc740f57c5368b0326ba63a60330be901db8702c496dd55073b3556b49874b95054af146347fb7b0fcfecc5e560dec0a0dad267f82db0abaa2602cc90107972f25bc44d8336e340f68666aa720a2b3473a032642bc565066ed69e48fbb4f1b7b6b25a9209791d014fdab9f644d4929408271fbd39fd6ea8014ce9f8c73cc0d3eed727b58ebd6aeedaeba1439bcbaa1ba087b76ad83152feea531ef7fc04154d52e7ffb84688b8a32703b49497ba8fd3ca0fd935a40e80b4656e8caab2a661f8bf7f64060ea94eb8a017601b6b65a86fbdcaedf9f88cacf6c5664f6cf3d327ef7970da3a408dde698c9adcbf288c758baa3bec6c56e039ebda448a5a740d702cb51c124484e5fe067c9d84ffd7d74fd2c35a1ef9f85bf1ef0b19e9c4e0b7b56a5b0efeff1b811b8184c906e630cd6e3b0cd0803253dc6df6720cd826bc7968a4fcae01ac728500993a9bf43dfab089b957612a60946ff0a054ce8b8a22574a3a2a3741bc15cee76afb1a7c4d2fb30188f19a5a5c97523980a8b036fb7a73ca9a4135da9010e7687344a5696667371872d9dc7b0f573e83a306b25835acbd6d82fd4655f557350a5926aea9bc9001602bfef78219144125ed36dc718d8f68dfbc221e32826e75ff202b509f05d46223414473a1cef9c62401fa050253d9dc427d080e83f6d1043795b280659e014320041c1009b445757de9ae7d05094c793c065b272812ac2882be5d8ef2b24ec7cf5b0b19ebf457f7cccfcc78cf4717036c0c320f1aaf8329e4a1cc9918f774269529194a7c61d4b98b57817c633689d2dafa74b16e83152a5a4710af737b2a8bb218b88b217057be1b99f665954e916a95aa147f1aca11a7bf2a53981c16209a42e43f74ca03f2cd51a5ec3f096fb91637786edf52030715f8bb05e42ab005737f669343987424198f180c1b1d091f19ccb01e1be2c2f4dd81ff156da744c982db9572d101760b06ce8ec117932187011b2870b698e4741a255065a1a7bef743d7851f25ef7af88b05389afb74e4623af4060f27328c60862dd03f60e2e092e75ac8718b39ae3825d8886970e69b46040951da1ba18df44ab2e9184a5d11505e02b57af3764ad41d8baf6a09b418cbf1469c05a3dd4094628866246fc2acd9d71d11b63429fb630121f97290fcddac3ca5a7759b883a6f688c7738120e4b703000d0e90e6d01369fe4d4df01918d5c7ed70ec13e03403569106defd3e231153223bcd86911c860ac127f8c517a5d5e6ffce32ca921e301d2d93c30eb4ecc70dd42f42603ff65f32fa9d0bab10815d7781f08984d94665375e4afd2da520cffdbf405c6ee79e35054cbe1a3055e5d438d5cf0bb58ed416af6180618e2bf6961976a46ecd6767911180faa2fcff356cb86349b2cfb0678aedea53a41da34d30fc3ef8c6aa9a0b3482fc9dbb365d14e1350ffcb6260f91b6e18bbb4a7fd8d5bd2c1976a3e9d88254290ca81d90430be1dfe007c03982d30ca8231433003cdb5e674d10f0ee6df4c22daa4b8f059094114b940b509960be05ca97decc9959e3b919e28140d0770f62becc7820f6f955a57e6a2b222323eb77a54bf3074c0137e8fc4fdcfe08b02caa60af9c5aebcae6a4646b7d19ec3fb6b7a5e4c01935079c8372e129d66ee4f6e6160e04a940e3076cdc3ac60a1be15c7809ce9e1652829c6356d54ebf0481279d654cf244cdf22ec81295709c27b612b382e6a447b2ce161f648ec7a0333da72c7af7446b199c5b78182b65628a394ce4293b6e4b7decb9502ade7a252ffb378d4a9997044ceae7f16600e2a27e5f4cf317bf782698f5e8ab93b5909407df7724b8503ee1412a0eb6f9d54d1db721192f8d547cb61aa55b0473670133b46b2e07450761850c48d3ab4d40a9d43bf9a9100c3b424a3c5dad9900b02c94506a46b95685bb615a42d7de25c5e80f3ee5d2153cbcf14527fc0c603b60107c7a80adf718eb1cc96405113b8d02f07f16796434e9eec4aa4230f17aee99f3f88ac3171217c1d2c5def9b5e5d7a567c314fc5641cde8265150c2d8c396f836540a9631ebeb65158619a6cde19bc7a0917ee95d623130fb4aaefd70e24a0718c88e75711dc19983d1d00a9a1f8a7429072f2575901a56c195d4ae9afc1a0ac0bab0807938b2432fd4d26630230bbcc105aef1ea5c5965e0e537dc385da2381efe09da1cdfe872b3e57ea74be5f811a55a4369038dcb16a1e87122a60b3a5e8a219a6dc41a66abf6af59e4697bf3f7d924ba11bc8dc03614d14bfe6c70f5763f6a9779444a3f196646c1139f1c4b21a813cc9a80d39e61599850d54c002cfab648c852e85f893799f78d5e420e8f2cb312834dd4c82146dc456cb5d07662934dab65dcb35ebf1ca2b567c4940ee564854f6ea25912706d42094d6a23dad6a150ac1884c810154fc633670fa3852988fd6d563440cae2feb35047ba409286e2af782b69c1159c08d523acca4582e26f29043decbde4876a9c3f0c4ee5847bb9bc80d70171717f3a4bf8d005cff35c7080387ec65ec81a24e0edc11b2306a396e93e6441b15697d910d9c20203a970848a938a2ed07c67fb82dc2c4e37312de59aa691eeb67a5a87f531f74dcb516fb3e12c0063ed69b80da8b07a0c61e89e84ff62d2999f32bfab192b4a21b81ce097ee6f0541ac82a0db6cac0e6f0a5e2c8527fb7455af2a2d94f0e53f553ef4ee635f7cded4c485e0657df83ddb26112fd1499e08d8e1337572f972007b30ce47136430653802b04a1868b24be6f07351e026970b573bfcbe4e706c0071073a40add46ba4db1019dba8c3689ef64d6a401cb2a8099acc084652fb0eca614064bf2e79a1148e4bf7c79ba4f47c047f4a704ebd81b954cc8a17caebefc4d765c7febcd1418fcae664eb5b3840acbd326940cf0c1415bf79aaa02b7840a8dd9e18ab513a1cb48a006b46cd79b3ee72a0b3fac7715a40cae505f226d691e5f4b5e12aafc9440c46afa2ea1e0a9ae73c30f32063d42100130b147954068942d49ddf77b3397ded414bf3a1df622a8e4b0fc77dfb4b16f01f52f91e506b30c93433e0b5af633be49c9f40dbbc35bc7887797a99dfebf252680e2b3050277964f1a93a4ff8d35561f84aee99dd92d4087884d8ad1c6ab936c0ffe7cf4a939f736b42efb8186f97fd09a3dc15c02422ce3450ea4c5c191a9f6c4f856487a396b4b6dbb0d18cb939803fb918a757863b972be452cdb00066593b5b55190e52babc19e490cc40b635c5800798abf3a8c8896bac372face9ae3f40857ea06deca74542ec9afc8de3ae3a38e869704671437a91099995d0591845674ab567a6295a78abd8908ae2577df728ca7fa37e12017a474cf94bc48145ea3e87b24b7609101264fbecefa12dec3a92f63425ae08e70fd92063b04490b2dd1b421af24fefe4dbc6c9e0dbf452bbd94744a81d2c4ed63b18a2fbf9c9ee112e5025f13704959f5587398239b3c11f70ae52780d4de66820a366b14af60e580c75b4791b17a737fb79a008c418a93839abbacb3f2198052a2f2e86bb176136274e37291f48aae218477176a085739a15b92faba87af389986eda11e49600205a7219aaedcbd116d1ab4f4883460f344c3d28749520fde3c55ed5936b8e557cd12e74155af547b7f8101a0efa15e43ab49261055766b4d6e090e6709e41b4d62d72470f69456159b5af0b49b6182388fb8cd4337167da559e5e003b1974b01c8a88824f1c8fa4439c0199489878cbcc12147f38a9b9036bc53702c5c32bb3405a4e246433a279b67278ea2d1294b0402692e3ec064f7b24f2019e461e87d315692f0cd7a80ebb3d74f51757d9afc843dff3f4c8168c3edfa1c8b890d305b70a992cd91eb43e630dd916cf8c1ebfb0f1205740be7a7ae3d79d6ce6e6cc5f239ded7a8d1ae01f0517b10d4d7cfb41f5e10b08d88edfb7379cb8b14784d98ba399e24d8500b1eae6afe3b53fb43413624744a163239e73fce9ad202a3d965386babd0b602286480b07e59da8c0d6bcc71a1b0e0271bb596fa5340ee4758d6be1c2791f9d6d6fe169277f74e0af066a59ce4325365cc90df8c6ab7e8bc9300fcf3b9d33334edc38f224723d79a85867081c4ccea2ce550a5eb973c01bf037a69622970c357ead46b867bce4832d39e8344976b0f2902a3b06bd8cea4981ade0c8bded2e235a641a786557648f0b0c4da389a04c8281a556827949d03987eff4c32ba24ea2fd70bae8476580724035d024a5311b54de73e459528d88f20e70a473a38cd48a8fa96b826bd079a2c5910e3c6292cb937bb9804b59b836bcea6cee87cf35d74216702a3c5a90604d971c939da147fbab620600bf43f6336e6260218bbaf62d320ef1ea360df02a7d7df3b0f8c3e472b2912ea52fce0eda3208dcc5256a6dba43664c23abadce7cf6e906d2550c29700ca43d6b23b36845f148051c1e0adf85a178f3c87d6b9d01cdacd7aa8dea001ea9d1ce24591321ab518883b1e107a7b6d55b6bcec93356dd084e879ff22d55866397751386bd01393237825d30d0ce005c230e87e4fd0fe22a6a89eebd7a10d04d780a5624bb2657b5642155789bfdf0397372433b1c519532db2d2046552a13c574e64234e51e14ea2206122a069a638694d0013db363d50e6c5f0858ceaa9fa5797bce9e2fe96b3b07b30d7fc66fedade73a70717977f06947be0c539404daaaf7bf9fc07b24e7654da4951f24ce57a614c23c19f1b5cfcfc62ecae6a7f7b1db80963e802c7754f1532e53b4338ea1eda9e8975684fc2b7b5ef693c0afe49813b5803ff894acc7ac503fced347624653376263120f57147801c487b7d06b4a489c7cbe8a4d257b80e493c7581e0abdd1502d9665f6f7669a059a27fcef78771b153a3ef2cc2aa3c86d8422f9869962c2bfef8d8c35a9ac14fbf08ebb2bd74a1f0e58efd97aef23a37185f048fce21294de9dee17f1844dc39423e8e80fa46ba88aa9031c4b0dc05ae7810e12acac4399974fbcbc870eee1bb8c9737c5bd820c1d627e8152d1dc7c53f2e4305073246a70e38fd52c5dcd7e888a9ce29b4ea6591308ab30bcd61c905873b6ad781edb7c69fc33679f23cacd96f4d93fb543dff517a83452b78d366973dada30c3ef667f3b77180112a03a47ac61aa7561cbb1377b35f944cecbb70f2adf2090fbf36b355a4ba110ecbe8612680c2c4ce1c3b3d108c927a1295739eed73ad2344854d694aae5e3da8616514189cc45afb38c5c9614163f4d3b49381df57c06b1bd6d3b78f95c596eb5b4592e336d87a76d84b365547463bab704720a8b54bdce188f7a350468f80dae2bb90a4f693c8e59c3a06926611a94e6f7caa5c66de3cd960db34a7d250683e3ffb4210f0906b8a05ec02303377e66c236eb2649ce14e95cbf510a951fed229fb1acc07b2cdc8524da3b2c4da53c51c573d340062cf759aaa10af3eec1950d07f86f839e68fe1f2b7b1c4393dbd848c180ea8906036acf219931c7ab4b9537ded65be86525e3f6a2725e17f4d2ce1aca6388905240387c13b69b51c938800743a3328d6842bcb4ebb7694a5f67ad7a103ef716e18229cf8ef83ed27ff1ae9f5246285de14df42e59e92e19b543e9709a00cfcc068ceb545b0c3fe29c4e940e70e0b7c438e7f33c36d0496feea47897890fbfab388102efc713f78388cbdb800c07b27c7b3a4b250565407866b1ee8c1030d7807f21d00226fed60bb6107c39240c6daf0bda5737b0d050dd313c47d6ab2e418f9a858e2aec06d88c17176de2215065eda699bd94f15305e1aac8c737a89c682dad746e7719f6abab762eea51a7738ebc1b0595438f00065c7c0a6e428f3b42b6d5061e80953cc3ea3aba27849349b4d3c59542d59cd099122bfff13ee2fb9a512ac2e1d43c84cfeb06953960e735177d77ae84b3502645e06debd2c75a51fc6f27f0a060ddb6593a6566730f23ef57ccfe40dc8905d40104542a10ae070cf45a44ba4b20a3849f6bd550d52604359c9baa2720ee237bcdb6d8ad87ed0c53615555f5eaee554485dc77716eb237587d8d1624ddf7487e9f21512e5489a024092d35af1d9101f213dd1f4b8349ebcecf88fa8a51c095384b84cb8f951e445c55e931a169de92491f730ff5d7cade355c54ad03d5153e90575a3b9e18760ed877d247458c8395536316a5265b34e475f9fa0d8a3851eea3acf47b322b92d54c66cf7d1da10bc7d1b1b790e68c45cc1cce1f2c66667c603a40aea4d82b6ec9a8eee4f6ab49982971ae5a0d3d51b037102ec1e65cc9fe2d57efa3adefd13858ad4706f0e057abd0292a901850940faba635b0f4601c3cc47b17808fad9531972cc0098aa4159f7cfb67a240b216fa94d404b40624a77f75426cd7838c14c1c660374317245dcff8748cc5504f2082585bdb41e748fcc890100b5b5295a711f1a43b961a3bc63e59ed222f5586313ce2acc0957845b82464d2c530e18e05c0c80d37c83f014a5d2f52d7b3aeadebd85a392a8c47161d255c207447460724d8a09042f2463bfac902c4bb555a0679344246c2a38c06e4c62b3b184338fa586a91cdcfd195f8406ff99378ce481bd3846dec65a3d8984a2152070511d844e1d620d1a2e213822826a5585bc21d0f750e4236f3933616248930e35f2a7f7b5228c440ca9d14ac90aea63eb03259a4085e7944d571b3cf2dca973a37b3514db73b85fa396db54034c93dc7953708368586bf8c995c86cf867411a200bcf0460f7187d1bf4a433bc5bc3f41f1f84d1fe7790f2dda83d32a6ef038253f81fae9a1e1dd5da916ebb7086592ccbc00fe06046d9bcd6da41a414121c1cf135daeb76f1437adba486f9b559f36c90b30fd2cff0e41215c70cc5272d027e429e7bc70399efb7f638e3e91105462e3325afe56ed24f6f80709e5b7312cbc81b1a848d22325b8041185945e6aa623d0c5291d8cebf4197b5c6df2b8d3578dfcbb1b36e40ed9519107cefc1f59977b1a61484d64468935f94086f47638eccf9b8d1a43ea942caccbaf9ad423ff5630d0ebf8a5af5448895653d1b538032a12724b1e05046be492ecf54c70e1347fcceeb7bdfe9d40ffb5062cb125d42e3c8d11d16110b0b3ad5dd90e94e65526ee708da8a5ea4c6e89b52c6736ff2e83572e2679ca786fe1f47bff89e9e089c73d30102783aa15bac1a0c4f7d197ca09d4a03e29995fb331c4a58bc765bd021f5da7004922f647b01599fdcc1295e792d1a49c133dbf3022f4e44c9c460f20c5064fbb843667e2fe8c41a08f1f0c9946cedcbd0983401f7f0c09ed9c993b130681be3f18725a391377260c923efe3164347226eecc0c027dff614868e44cdc4f1864fafac190d0c899dd993348f4f18321a1cd99b89702402176d8ed08cd393737160602b58efb9ea424064588aeb9dcd3d0acefbccf9d2d1cb897efd089b92a9de53513bea44653dbc598a426124a1d28a60a849adcfd98e0eb8723f90948a50bc8a9ca635611b22a08924f28e82fddac4822b1bf4859f3114bc9a3006eb83edac35a1588056d143f7a2b1f182d75337f044500b34ca75f79a9d80aba43dd49789987b8113cbcb6871f8de7647a4dd6632d4c5431d84fef0d755a907ac06a2f112af8a9e4a81327d9a0fbd68d6045bd21bc680b381392c810224615e9cda5fbcdb9e5187ce45553770611a53ebc66adeb05a889cf2bafa71804bcaf0c98025ea2cc2e1ed4c8581f8b5d6cf7b1bf2da2f12f598af4650f1cf73c658f2b3fb0386b39a5b42ff5c7bbb50fd9fdf307f50f9a6fd33b6e3f7ae1a217b6dd2bf422ed17efa92a6014fb251f69c175cfac92c1779a5b878d1290ac9470412f1c197e74e92ba1075ba5cbd8da2a02aa52ab5fc21742d8b78ff2a0ac98b7aac7f0af54c5ebccd96b43c8b8a6e2e8e9c5aaf8cf1a0b47339a5bf4b1b24891607c0efb4af46289dd51fef4f8aa47a29ff177a5975c89270a0e7d3fe61bdc35b535a44d48873fc88ac5e422bfbaeaf32018bc96254e3acf82c67c3b0e86db87d5c868305eba5480a880b39fdb19377727ffdd30a2891ca8d00af120cce40432170e9be084f244cb31749b6e6c2ee08a23521cb7b3103f489afb54c10e0fddcc6e14abc47652331ba3e3fb99426e8c03c7a1f340e8ff2bf29bac21900dc21882e26243d8cb4522af1ca8297a44a715b50a3c6a8a44be3c0ca08a6e1a2c31c50524ac848e7ceed9cb9663a002e8a8316b85dbc59d6bdacd066e42d88b2c87f2dcf0e46d27f659b3b5d9a1f361eef4b6c6ad71d53d1812024e0c3ef54acab21bcffcf3f254962720e36b709857929ea4ecdb0bc2a3337d7abaac8ec8a0bf3b8a969d59c9f60efe919754387c3ec241921f30fb72909846500975bcd80325a73ae87cf35c298bad1899ec899901cd1c43dccecd122f6cd71d01d34b39f59f683b394d536cb5e63944fac5ecea6f48a6963cc43775fa1553b7990de54181f581e897d6fae6259a1af9c50359a0049543c7e8f80e964e0e674214f35978804ed669d8039de79f6b2430d83e91cc6d30555ae1e8b6413454a7d297964c8f32da9780b27f2610a8583dc79492cf66bcc85dca6567376f429f4c7430c64daa2bad8d3e601738c6f970995b126368eedc6500832f164cdc76febc7c70a9cfe1b22d37a9be0d22eba8e30de33f6759006c4049628df72c479c13c301182c9be1ffc85f8cee92d471f20d15e8e58cba5b887b271c2f74d8702a40d5dec0509cee3b491bc689733310fd38caac13b1d46328656a70d50ea709de4eb143e28000130b69b30c081bf6864766003a2149d3443d6a23059a5858e0933a6e2c7301c0b917e6af1eadbd919595e616726f5e3ce2d2ae38973ba31347227db324df30e3d9de8a69fee0c0a4e7c0d30538ead3398ed480636ddfb1f11cdcee240e9eb522a29c7da30140ebbcfe2a80978f048508506848778112bab9e3beee0bb45d8f9240628d74c0cab9a62afc18b87b1070c6473fd4cf7215c64371b1c51367b7fab733e287e634e7e8417514d61dd433334329da8c9f23908647fb819220b9845c2c9310009953f8bd440869605f4278e1fade0fb337a8f90fa5ca5c385efc069f09eaf672b492a3753d9103f7dfa81522a7138a9462dbe4a8493b1d85b0fa7e6576cbe37daac420624159a8280937b6ffd261db124ebb9db61df40f72be264e9697ecfd51a910e8601a0dc9eecf88b8228870f39a6f3262a4668b4774987fbac3111219cc38d179d9e2dc97ccd7d103cc5b9a82a2aac398041a47cc0134242fa21e7145aba04e312ac1673a17a44aa883625155dfd99c7775f06202c5bbb84db5a5126356c7aab42d88e424b1e10212b3c65c09558a1f7617809938f641f8865923d66db25127875d525ae08018351287aef06d30b8f1716242951b520a41e3c3674d78602599d137b6fe08407cc01679dde82bb8a229035c00c1f251c988bd5e26f52438901c9e95c1935a01845e2d030c21553be875b9389710830df98c21ea60a638e290fd674d208a9e08eccfb5953a69fcc31a4a334a142e3d56d6c47cc7ea08aae7a644484b8e7c7486cdbd54417dae90c8bb0cf056b36dd3dffa75d8c6c04690c2b381a7630254882a787ea7ef3fd6c094ec220f28f5a23777aa0f5b52940610ac604483b65d1d51ff78a03e06d2b265b7f422bddc8dc664dbe8bded1dca79075765a342021dd040f115ddd990b11e8ee06782788dea5f2de027116625947b120af12a513e0a71c7a6ac621c905d42e32ea8394e04f9c124bf325325841750f6b2c8c4eb0d46497e899d24f050bbc32648cc5022487dc70cfa02f314afd400dfa4752608c18eb261fa07950aa06457c50f73f072b122b9b4c435f3700b1d475d740b97ec925267e5268e479ec31962ae50780ca522dd44ffc8c58555f729bbe38b920c4c8f12be398773f06692549a0499c82356739b9097f50c4aa6835f2b312e243ddd3c44a7c7bafe1ee8f2e6d7c3202296288a533254bfa68d920d6148a65e7a24147c5e6dd22cf42a1f5809e764d5c699e83b23bec8d26162be87ad2ada6fd18f8dbfb53edb0b20a1dd16c66f713d5a09e1150a4fbcbd09703a511870916832ae9c7450d43cdd0403978f2a22fc0ae4b1f7f0b024bc1a2465708409398407f1bd3f8162b5de7aa20eff2022c0e653166c52b2f056f41ff0d9139e13666e9df3c0a7eaafd9366d3ac8d0f7a3b9cfe3139c710edb9e3843c153105c0669aab3fe5f416ab2e34b486d833c8d0b1b35d3ce46d9192fde2da681d6c866a23ce9341d9658795909ac9f5cba0e09e90420c9425348bbff2a22a1f3fc967e0b1e7de4fd0accaa1338c49e8dcffdab5504c870a6c224b2d9b0f5fab9c8541c836431100b4b9ddadc856ccf0086cf0b4afff256705bcf5f3d780893e6b5408708247cd1108977fbb03891655c26263182cc3672b47f235ee6326440431e347fd06fa85929c805bd7a867a9425e3d3144b4a48231a8db5a23a2c9a46c185ded954a911b2da6fd78e4e3b10a1f6905ede3bca93c9d5f5359ff6fec822102a2c3c00af0bd936e8a8fe16cee6ac8722b1356aa57b52cc2799ab7f8fbfc3dc8b5a4af253a45a077f857e36fb11540c3a58669cbeb476162ae49c54d67626be844425c540b1e83eacc14235201ab2f70404ef53eaafaa7ffa09c398eb8cb2ec6b408c5d19a74033c2d8187fe3ae1757aa901af08b6c62e36d4c6c600c466342462c14af56ecb965b5e8962933c44f84cf5eb54c7c66c367744d25acda01d3851d5884589cb8862d5f55daede91dbcd2108195f54e6c055d47719acf669a1b4c4ca3c92cbc1e573c93edfeb937350ef4605a02b486b3ba1e7a9e0fb6e01a4bce23c3cb434785793a8861019e06fcb200d07f329125265f5f7290bd654ff08a542fcba2255eb82539114f316f03f21bc938530837e75b631a02d6ead629cdbc20bdabfd24a0e174a4f6244f1a9c271e0284f49a6d970790347d8cb7109d1a846ab50d536fd463a47fb991e094680d6912d612afc50da4e360a3a360f53c559bc2d7e54b41602ec08927aed604e736ba57dfa84c1e898583d4aafe1598d809bec11d8fd83bf41f22dd054f928e1e2429f6bc25a5f90d66ed52c692aa7d130becff018460037a55bfb258200c5a351d575f5c2af1d94a6492bf661e4408bcab4105bead218333e18482f41d74f4c30b0a1c865e9bca379d7b5e66a3574ca74b6aad4657b30c276a74be5ebbeb03116804cae3131a55a089b6ddc86e6b7b6f29a59429c80a8e0a3d0c349f5236be42ec4ea45b6f72123d88e653cac6977527f27a10cd9b9c80dcc79b9c7ebcf426a71e977993d3cc476f728a79e84ea4bfbcc909f4a07c03ca252dfb4fa6f964a29e5c2b736d9689649916cb377722b38fb984e57e6552ccb776403dcc62eda07a931398c9da01759cbb7650dde966b176d041f576271b7d618def2b99da975ebe24fa52e84ba02f615fbabe74c302ded28d30ebabdf68df5b0781a99da9f3e92f677ae593487426319fb5d099c27cd2406702f359565c33e17c92d7996e3355b7b56bcd39829ee65bc5d13763571937425fba715d747a03f4a51bd85f7e03e64b37ac8f7e2346bacc6f9433efb9f1e3e334bf1104e42a6e08f912f51b35bf41e44b342fb1b861f31537a82fd5acc4b4b41442084b365faaf952902ff9f8d2cc97ac932ec2cb45a0de7f83fa12a5f95ca279f2e4f96a5b914f225b3ebd36944fa15a3e898054e43308289f351f5a3e7f7cf2499bf5e4b39ce5b32465f98c91f924ade718fa319f309bcf1c438f3dc7d0bff2d9be3f730cbd98cf90870f9758e4c993e7713e730cfdcd67873cbcf51307f836e708fa9a6f44f402192b7dc1c8d74c4462c2017ee84b05f81c34a146625a9a7d8ea0c7484c4bf673d49ae65bc55130846522d82bd762a2b7cb448b94c3e743a41cb00f9172d40f9172900f22e5b80f22e5903d46ca013e46ca513e520eb1916e769f46b2b9ac388af4a2b6cad855061313130e1c82306f1745d84be6ede448ca62de5ef6943ea3793b4df63e34c701fe8fe308faa5d9abf0f61af940ce94e3be0ac7713fc871d07ea9006ff376a22a830987cfd79c0907ec859c2907f836c7517f88890947f91638130ef1573891e3a8fd927d16de7eab329870c89e29477d16b9ba05a6ed22ea2bccdb45d4894cdc45d46de6ab8ba80f996217511732c32ea25e33c12ea2aec224bb887a90397611752013d645d469e6ac8ba8ff98b22ea2ee63c6ba887a8f597611f5d2ece922ea33d3a78ba8cbcca02ea21e3381ba883a69d2ba88fa68fe741175982964d6ba88ba68aae822eaa149d445d441d3d64545d4afb9a28ba85bd30293451751d76101b55de3ad324e1bf6fefab00a0594bf0eeef8f671a427b880eba2c884e38e641eed251d36fa68631dab5183f4a5d9bfbcc642089df45be471c44450f1349f2678582c9f3674487172d6189d2e00d451c3d70cf325fa95dea7394bbe7a38a7ca5797fafaf59fc0f3c4d66ff0ed7b628627f373bc7c75108c89501dac0fd2b7277d30749babd3dfb717dbc69982d5c1bfde4bb307fd0c9d08f339ee8335acfd91c6b08722cc1ea4ed01e5ba7a26fa38ec9918ba2317b41c01c31263c634d169b0d8c069024c1074aa705d7971c920e483eee85edb82940f3cb02902e4863348be00f1c40d35fc600506ae212497929f1eac966871b7a06fdf0aa22e109f889b5e5b2bef08a9652d0657136fa576d65a6bf319c39a49d6cf18474915c7864b882f5fe09aab07aeac3f7141683101c1355c520f825a537cf081a8e6367e98b2810b8614de5c99e2a5c9105a5b58729e184209130d9bd7191859064f19cff6b3c74f1f3f493f657ecefc2cfd8cf909f3f3e5e7e867e87e827e9e95e63f3ed26834ae6ffa306ce554106c744f8f564f4f4f564ffbf9e33e3efebcee8f597d3f26fdd107867d4c6b835ef998f47b4cfabc2eaaa992d755d21f63a4cb7c2449522693c19199f4c9126c0e068bbd5eaf11e6a48f3018cc4792e41169d287e9425ccde65eaf570983c1e8bfdae6428c310882346c0e5b6babeebd5436d74e69bb520efb515000e4afefd80897c71b81f2783b20af23b46f0744df89eaa0d552c912536163d0826649d27cb8c1ea2a8e103e298ee8806272c5051a8e0c61e60913752c135100f1a045c3ddc66fdf124ec8ea66ec5bcd2a4ff1b958a317f551ac97568582f30229970bb41c9147af3a767b77019babd608e6d5cbda0833e92e346dd8dc0b279a5a361242175dcc268c1eee7a37521206820df10573317498188a2efa68365921813201830ebc9d094e4d4e2f87f9989d5e6e82132cf4b05655ab97d9b503ac65839ad5aae26895a76ea3ba8d04d15f2e86a68d84d05f1efacb86db5cad553657eb15ae609c516c24fa75d73eceac68deb12512605efe748c6632d17cfc4533997e68f48a72a13e9f17cdeaaa3c8af5d4cbacf594723d751f38729efacf6b46565026731bcd4b1fd1631ecb26ecac93602321e63dde934dd8dd9e6b7b7209b503ea369acb5cc7aef431997c448f551763a2c7b20dc8676095d503651b3c4af5d469a60da8a72e33a7987e9c698904985bf771a62512b0bfdc271fa03fca75a6129a42071de625348d3f26d38fc7bcc6bc562ab36d8ee631d3e6e3a4ebd88d3f2613902f31916023013bcc61d904d0b1f72ee624d84880f9cc67d9849dcc1bd0bb998f346643e6346623e63f26fd8ed998e5ae1dd09859da7cbcba8e1dcd3c6b95cdfdf8086436a0774fd8613ed21809a0630732fb613ee60a6573d461d946f397d3b0395aa7b26cb46ce3ead8fd983ea60d9dd5719dca2aaa19bb8e1714ed11363f982146bcf7dedb0498bff7de7befb5f736c1c2dfebe2d55f27b3fede7befbd14dfdbc490bfb2bf4d30f97befbdd68295f6ed5b62cacfbe7d4b80017b3873e64c0688bc1004073748d880c399af5d3b4c0933870918282a2c4c905982cd93e2d107a049e41547dbbc6a6e2dde1a67d0b0b6664bcb84116f9928c2ad3dc25ab5183547fd9a3aeb349fb75afd568deeee8512469478aa815446caeb1f2f5788f065413dc96933df47d6e8ee0800049233bf213b8c59b36bef0edc1ad1ad5673d47f48224f1277d6d49288228939be24e6bc0e0fe814db915ed16fe8a059c9ba78620f4fea30ea98b4b6d62e7ae5939eb4e9411fcd137b08559388f36ef5d56b555fbfda1a5b80fd933acc5cb21484eb230e4db2ae50bebc17f072d05ff9c4d8a99275d1c31808972c69610c843037ed7900101ee8f43b06d6c00ee653c93a761ab38e4da5eb57242b8d99ba91824a1c1d5589e2d1086e21b839b97704374bb7aca567e8a2571fbafe7a99f74979b1f8620efa34df1cf8c14c86502b90f1882824a27a37ccae444e9eb746352b670e10394b9c20e785283966fee7db27878610899c2950725c7892d3e36374667c40433c72c91c3529c568f1f98822a20e981436308512375b45a264f8c126492c8aac0c8db25696466865a9287fda28067ed94a228adb8700d03fa02258a475c6cd9d29769ca1a18bf03286891b869022c51a3642c423974c596b52d2ca078c01f2c44490362edc3941159a24a03cbd918202cfe30f777cf8e10e474dca32f4e6fad06f18866158bec0d568255159d6fc881fc01564b1a8b5d10cf1e52d6ab71a91d788fc56731ea26acd6b44332c9803901934daf741cde7a356abc5d039514db446b717ed9b41a3e6b823b4fca9132b9e62fc213b478d74946251a782fc9226a62ca9d7ce30eb2a8e7a086e7f14a3e2e8093cc230745dc51d857eea62541cd5592332d485473c7dd8631786614892d8bcd14a93acd51cca53a73b2269442abe7d472cf9fbed3b82eacfdb9582ea00a4ca57a76960cecf44d1ecf72ae39ea108d7c3bce8a1115b1e2445b37a29afc3407cfd450b1d966d35c05726dd8a4aa1d7588dd0c5df2ac3ac143ae8275971b1048039ccee9928ca087aadca20a2b51171fe3a7623d47c8b6f5f114c7eb4b52ae3ac8e495acd4338ed933d0ff851956b5909f4eaa2d3b07962279d66259cad91a9f2bc4374089616c148c560f1b83d0ff8c1e5c9daf38028ae8aeb1f2b79abed5e6bf4e2d1da47049e9bf524f3c591f2e72dced39fba0bfe8cf194bed9fab37d1de6a95f5b471b0ff801e70765e471d43fdbb7f5d52df501bc8d2d007c6af38c7e51bd4651f69f942a0cd58c8aabce426936edae55e12db7204762dfd0deb993c5420f60b44e406277d6becc834d8466698d54b0c40c912966861831c48e96ad1011f6fdf37143c5c2d77d48f592f8f0f5b80df1e1e5e9e6a34b8f9b1d727db0f0e1f7cfc7923f75595b468ac954354f6dfd3df97e3d76e80991b236df6c73d47f3d1e7fefbd17ac8160d7df1fbb7befc5d9fa8831362a6b8efafd3d35f1f73a79efcd5197567341dfbf2736fffafe3d65fd59faddb2503ca4c0eea2504c8307c403f6d8f261a9d31d61a781dd460f288f9dc4f8621ceaf0edd86f78ebcf178f1d742e6b71fdae9539c4bf16044110f479aa4317b5878de4f31804bb8b28982b985b3473dd56bdf5211fe1f7af4798cff251070300749c0924b69c28e2de3ab54e175947ba6155e822eb95cecd548d74a9bacc7717f9496fb654f6a9f4794b659334525b07a9d1ef38d3392ae377ac695cff297b1bc3f79f3ebf634ce3a807bfa34c6fd0556497ad366d7b94fde311e7cf1f8f257f865f7f3cda7cfdf1b68c7a57fdb703cf53acb73e5a1fef2f8062fd8d8940c52cdd9e7e89e6a158d483feae77cadab7d3288d74833e3d8dd245ed48375ce8a27617dee6259ae7b11ae9ba15f3631ae9a46f33c5eaa2f6315dd44e756824eaed8d64f392db3cd66bff786dfe14af00c6aea0d7ebb69a43a82f7ae84715f412749ae82b6baec11a98abfe32c59aab2e86f7c723f3ddc3770c841b682b3de2fb8e5da7d8fafe7dfba698f2e36c6c6b34b4c2db6990c01a5d6f22efd1db67582398b77fc01a65e0cdc2db56faccdb2f608db277ccdb7420eff612acd18f3706de3dde8eb346427ef3f613582315de2d62b466deb20b87e12f3cc86c917bb7167e81fb9818e45e0e03c7b9ccccb907a3c8839c34cddcbb20bbe92f3383dc2b0af2172e9a2c720f97816be0a169cbbd6cfacd417345eed940b72651ee65a081e77cc8ecad70d059500762e1440ee6a8173b0bbc347b2c72948b5d4fe62c88dce6a3d923ca5140bb9ee844394ab6735f917b435eba90d91b6d0ee434b36703f2153e337b4039caee7a352f57f8cc61331f7d458e2ac2666f96a306b0eb91ce62cc51446ecb51b45d0fbbcd7fcc9e0b18394a68d77b399117e52871d7b36ef36bf6827294cfae17e4e58b1c45773d205fe12e7214b8ebcd7a1ae428dbae07ba2d97a36ebb9e90978e81c770396ab6ebc59c856790a38e76bdd0891c831c05dbf57e7c85f798bd16dea3a76377418eca60d7bb6ef39ca372bb9e0a2f732f7b94d9ebd1d3b1bbe52802ec7aa5b360e1b0dc03f21ecd7b3fdeeb711f6f91a38c763d9813e5dc03bd87bd77bde7668eaabb5e8faf70377bd56db9c722f746efc1bcf7f25ee8a247d19dcd2d307b345f917b517447947ba5f766de93798ff498db726fc87b42deab792fc8550899389b6baf99366cae5d8579029b6b0f326995cdb5039934536773ed3f660936d7ee63529ecd59d95c7b69cecc239b6b979917b0b9f69849a16cae9d34471366ceb0b9f697f9019b9ba25c6cae1d34dde6dab149c3e6daaf49029b6bb726ad26f5d99c4f8aac3fdb8767ea71d857a25efb5a111583f8bd79563084f6751781331b5d44fbf7ad6b233a6573651b512d3b6ba4ea35ac29df510dd75a5ae67d3a658d48d039eb5c535c535c4bf47b67a373d66bb5b2ec1c9d1ad24516f7154e7952adac2afce60e97ef1f2969a391dae6acfabe72a9e6d4af0f59997588f2a45d55c60daf443df4ea144f95712ac9fcc583f9f8443a0d2ae64052330f9a2a5d85911e17f2ea95aaca383b8f8f0f79f5baa4caf8719b535efb6c665d5271d587cc4a558dd429b34a5528b33e999547f1d02e2b213f7df4b326fa98148b8631a9cf8c99940b69c24c9949ab50ab8aa33c154f415233233e4b685e9d42295dcca7d8f3e338d7bc1fc86d2ea13a05aa6f007bf510e0a8de64bd7a87f914c77c85724d451010edc7a7a79cc964a76915cb94ac581557c3545cf5555cf51266caa5e2aa2f81995ed12d15573d04ea22504aa6e2aad737e5ad5da55bb27695ef2bfb2f5ea253bcbd51747dfba290f3e3b72f8a1f9e5a5519f6adad55431466678446a2be347319056aceaa91dac74ff12ccf9a10f6deac14f3985bf0ba0dc3ac04826e45312b85a1db976725d1dbc26059e9f5723b8e590906734b9259691cddd69a954827ddfa8b1bd563b96a559cfdc94a34573c1567dd3a69d62eb3ce31ebcbac5b558e59e354381557db08519ef5eaaaca962b2e5bbc70f17909e3c30a43062b8b925153652cd1ac24cb54cdd32c3ae5496995b74e9fc48a83aa46e45435a2515523a2aa6a74ab39ebf645c559a780ccad5f20e6d63f10ba7512c0dcfa0948b74e79a08bee934ff1e53df92c7df4329fb55916e10c797859a65a15d76689cc74ce9829172cd3ad57a6722acefa9298299c4ce3549cf5eb215010cc14cf5febd7a45d6f4aea27d5fa1e2a123162bf8efd7c113ae8a19fbad7196374988f7e1ec5ce2499cffc6c9f9f1de6c71beb6fd01c488507b90a6fbf425e136d3ee4366f27573891aff0f692855be02cbc9d66babbe9edb5169ebd85b7135de037bfc0db3d03c7c033f0f69bdfc839ce73379668565aa2796eb868b7d14837a82f29d96ca38ba8dbe8a2eaed2e34307115573d67e2cc5bc555cfc0c4c0bcc0bcb530b369d2589816982b4c229b39640a9935156690096456ff31bbe27accd2ac3e336531d2ac3e9e475fc5ea61751d388add707d755c97cd556f6b52d29760337b1d04a6d98c52a5ea7350a97a21c24a954b8d84b952dd428329d5ab5254aa552aaef6a954ad2aae3259bf52e0b1f9ec3c7984f0f2a752adaab83acb6798270f9e57aa612a4ea942a9b8ea5d3deee3e28f877e6d4f0f2d8ba437cc0a1443f2c2ec0b763d7cf1592f10933031bca08bde57ebb3445829d6b0125162c1f2298af92ccb309f354cbe782b2b2b2b2bcab3a23cca6bad1244b06cb1b588284fddd75a15d65a73d52ba8d3e9743a9d8fde2f11c49788927ae9d56b1556e9b22e8fe5b346d2ae7a73d75c8815a7ac5eabac51d75a556b55ad55b55671c92a52d61d2c779edc597247ea0ed4959d2b3b55769614b1236587d785a7cb89abaead6f5f579b2e345d3874c1d015a56b852e245d43ba9eea74d5915367883a5a75c8d4f1d5b9aa53e5eb2ca9c3fbf6d1e1a21387ce0f74d4d02143070a9d25df3e3a52747a747837843153474a0276e4d869f3edb343c60e183b30d0f142670b145c76f0d8e992402dab95552bc52e56abb5b7d62976a4f576bf36d7dce4043ae833ebf5627722aa57b4eef4725cab5edd07dd39817e4317ad3bc168d7ab3b890e8e8e9d606ebdbdf4995dd1ab3b61077b5e1ebad3e8533a8d3eba53473125f301dbc562a537f980ed64590673a7d7d8631dbcee84bd3ad4d06ee64dfe72a7d143f701db95b9070b596e7a855e4577b27e1d6a681723f398bb5eec4ea18bdee4841dc35e59f42627eba23b857ec55ca3bb26a7106c72c2d7c9d6ecb5831e1ace305e4b80269b430ded6c6ebadee403dcd925164e210f3b9b9b7cc876d6a1827677895edc6396076a6867bda9070b1f307c730f164e310ff21eb3a1dd10cda7948d3de48c8816e453cac69f594f2dc88756ca46a09e1f5a10adf4112ff80354ae08a2f9c8dc89f492687c85d89d40b75156d884bc86bd56cb3d58343989309b90903739c1aed8514ced888682683ee5e84ea4cbbcc9076c67cb3d5808d57213d4d04e456e7282795035d387a3fe2b5beb45600fcd8ee15a31687477b5d21878ff6663796434dc2c386ae5f1783c1c954d962c47544b8ffb425689457a69ccf800cb11406e5ab00411a5058b0f505ab090f98105873f7531fcb0f070ac00e4ea8715863c5169c113450c3bac8499618a3ae11437cb04be049162e6496f0923742764edf8d1238a0f688c807185f44a26834f8ca13bfa804914449e88228919b24c3da9818a891b3543523802c48845770485453324ee8645ca12a8ac52fc60068ddf55d7efeac9851b28b8e4b1e2049a205220656b090b555255581883cdc4e815197c9488472f5851229a79216761e19725c8dfd7dfa317c4daf7ef8a1c2c431e0d58c503f7ea77250e9caba5bcf286f6f85d61c3468adac2ad85a32a6dfe88f28af88af867e5ea31c64e9618e32857ae6aaeaf7011cbbfe28b5d29433ec6186317ac58bdaaaca0cad68d72b4c28d8c719434054b4a0f970915377749d9f3fda342e6cf185709152f7f96974a0b54aafec43d19ad3541b7d5c84e5646d06ead5c9dadbddfda4bc95b3b6807c1dc64a501bd33017431f3c26c853a98dd52a56bad8b40fd8e95b420180504b309b8180851dcf451f69b50136dae6957ea6475dc0a9619a9a71182ea85bed3c0c9fa9192d4b435a077369b0dec0de81dce24cca2e08cd4426312b0d398037a877313cee63a1605c94749bfc966e3c9e6aa779e3c361b0fd0b103bdf3e49981d4426973a2cd75efac503f4b6ada44a034db44b04e9d66255b1428df61b7d570db468928383b5919c175ead83b68a73110a8d9151785e7043a0671d417a166a5364a18e17df536c58a1b692cf3ac5c77b262ab71dd319d234244d14ae943b3840f74d004a1050226c4dcb91345431d39547820151fa2958b04ee46015a3a4f5826b0833d0fc3300c432fab8c30bc4fca30a495157793b8f7e22230ee827eaf5ff0a2406a485617569a2937697678d2d56bc38605345ae06f4a1021ade2250b657f79cbdb4479c12a4a596235e527a50cd4cd64c5a9509ea45873d4a5c420a5ca9fb7ac41ba5a4a4529ad4ea9c541bfe65b716dc3510a2dcc698147edef8bd5860b5cae93a2ebee8b2365cfa9b98e82a7e644ef6af4564f35d77f962088b50f410c9c51c20a95540c2870d99db537222ee92d59e20914aaae8ed85171dd11f653f73174f85a49fdfd6b35f5974e6924653fbd2e5cbd5c8872e1e9cf9acf4df540f983b2038a114ba0f83005250c11285ca4a0344922fbfe4199da32f6edd48de0f6e7ed9b5e1a0b4f08b949dd90c5f78f851f0b587fde667842c38ffa64cb094fae9e9e44818259e0f204872747509841e38327bc232b2471e44aad1084d4950a557cff5610b302973f7538c905729321d2171983c9bc7d9499743733e96eec1803cc9883a6e83393ee640710a919336326cc6766ef6426dd55b36d4e141d3b688a7e4d985f30f46bbe9c344ba21387c561df6b15677d88b7c2556d046d2088fd453512e2b21e63067eeb254cbdbdc1b13c282fde925c555e6c15956dd371de3a88ef2dcadb9f512350568f40598dc4dd5841ad1388df4eb8708c5d473a46073423c873c5449d2d4b84817265860d982d3250698102081f175e3538f1d204eb91d4a98108106c9e5838810a4308993a73c40c91ddfd392133a32ddca75f93a7c7e0fbd7448507bf7f4d8aa0b93d6c6b35b5379cf9a2e2e80834aad1dd65057dff986c3191f367abc9a11cddfc9974eb52bed4fca94bfa528266fe4cd2d1508e37fc594be202e64f5d2d862e7f26e970d8f2a72ee90c963f93745aa6fca94be2a1853f93745baafed4258951f26792ee86237fea92c0a0f06792ce27f5a72e49cd097f26e9d030e1f1a72e294b8a3f937463bafed425a579e2cf245d0d4cfca94b2a83c49f493a1de2fca94b82e1cd9f49ba353ffca94bb2a1873f93745825cefa5397e403993f93743360fda94bf212e6cf24ddcff7a72ec98c973f937461b8fca94bea61cb9f493a32577fea9276a8f26792956189d59f6252159740794a4aaa7066a04f7fbcdd702fa807ad03add5889c7ad0583bbef2ca937cb2a4510f3a871d5f79a529520f3acc8ebf7ee9e3fc4894d6ad63ec6fdddfba49bd1d8c81407f6c5066e7f57ac1eebd972ed11748a9122ebf2546b4b2aaadd6caac14943a1803e1d545203e40ffcd9dc54e61a388e216c5916f86016c3e6b6f71eff0d431eea90a5a54447cc76cbe555c9ef26cdf8c0f6820c0563346c551194e085f00a108c14e7a57c993ad49a87ae86a919eeec8ceb8d926917a6badb5fd2a40f1b1ef9f0a72febcc17aa8b824901dfa01a9cc45a1928845cdb622689cd15e21b620d6e303ebfa23885121bdde134f5ea0909381143d5049a105861f5fa03081cd7181073759acb2802409da8245052fbc28587260e1a219d3e48e1a3b4bc6a8c922048a206ea2f0443d099a6d1962840d5a9290d2c47767083a4594990a026a85263e3f346419d224895f90169a006235866a880a3df450d50408e9881454b44c4981b22601509858f383889824779c10dd4033258716be440184120f5678e3c4af0715dedcd08456c28f1e5a5548f020314502d24091022f081d7c1461c2c4023030bcd0c389154cd191c3011b9e9a227200e286c99226a00e2b728cc852044a0a893c3854f12184922b2778c9418993320f0b6cc63c05e1f1b3220122a898045901c91457123b64a42a8c0d73b26c5d71e0c312274a9ce061ce122a5413e01003d49621904ce130048e8fa8272431ac5982aac98c7493268b192e3c3c4ac48ab05242448a26e20481e4678c982c687e49ece092d268d2701541018e0a461c71c4c9e8e44413659eb4f1610a991756000495d6173b56745218ca008829425841e2491e2592f870b3460e9316de5c19b9208a45ad052b3c6a7867a4927852a6c7112867a058028522f8491220c45cd11066892998946922830f45d060040a112742750b92284f576c9454a1800c528e1841650c11572830215b4b4c0b6d8638a2cc8f2c0cb0e242922d2c4b4429990a8bae5000e24a1169aea4f4d8a27acac22407224c0c51d171a85c4922093148764c21ad2912c788ad3149c608e9090286168638e3048a136084aaaef88268c2862e4e82d4747cc963030c2eb0c1610d9808fcba7c3864c5604607a9262da2c4fe7581238313407ee015d16f58e841cad31c1bf01401c4498e7afdb89945e461e287993b4986ac11418b0d46144125874a4914ed3561aab8a9d383480f282228e3cb8282c277e7085413a7400f364ba2d810a78834318c10a9a3250b112b302730914521d9c10a8fad3339b8e9803703151588bc61e3820fdb093c7a5841959625a23cc94eace07bc383181690e8355961f1a8a00c0f5e0d539288c346070b62c844e941091e6ef8ede893bdd3e408560a6a6c5419b6d63627d416afd8556ff5f6f447e5838feb18e31b365f603f6f8fc3f90596cd55a1c27abaa3f2595babb89aa34ed57bfcfda3e2fd59d3adc18285f6f813d7642b0729a286484115d99dfecee369963344b4e441e18cd4133b03e850a5840f4d1a1e4a762fda2421d2840a1148b4b0a3ba17bf6354f76276749541c2871d98aca049a53b7252a0e0a2f543618528bbf6f6808c97835eebab127c24af171a2c776e3093c50e103ba807c11f92141e74d1c38e3556a2d8c2866b07f26c966a84942329b71bc298f919416344cccfc817235ba0fc49fe8c18c1469e605d45e414815324eb69111c7e45b61481f22bc264fc153152048a4817d753274799acbbc856d1174aea422e94fd230227d4c2fa53f47dff885cfd49f68f88d58fdf3f224c3490c2530a556cede4876febdc5dd43f2370be7f465ae0092dd713e0cf881dcb65a9fc84e07051809a32e225ea6e81b2d9662155a0a60871c1c714214e4e980206b9f3b3ef5f90ad3f7557c66e9fef5f10adae7dff82f8dafcfe05711284d744df3fa938a51a502a8c5495506a498b3afefd03b286c5f70f081721250d42ad4aa1ef1f90210f1b654b34cfdb6a8cf66d3e992c0ab6fe50a0a24a7e3fbc70f9fdd8028191ae500ec1f3ed5350df1947fdee229c9a1e71a88a212850994aeb62addc159efe86a4407f437e50934a29fb07c4eabf7f42a678ea24fdf9fe095182fe8400f11261325b0dd8b7d7ef7ca2e07f80d4d629a0cdb58a2bf20427b95144a0ec8ca81f503507a37840cdc050559e6ab6c80d6fdf3fa820a0d4884f63dfa88d9aebfd0125ea85efd8cdb5da87f1f2dafca2e61b91b2fff682a43d7501e4505af0fd83daf2f7fb0775f567adc280020a74abcab046d5c7e3f18cecdabbad111de98c6a04aba231c205420055794d9efc9d70e5cfd63a61eaff6c39cde543cd9f3de76bd7574ae901ee53a392f6f4698cd2de1ad1aece55dad7c69db5273ea3d6a200410a86a2188254cd0e9452300cc1300485e05b1f854b6234d5c208c09192fa2d12c5b22cc273f18b1a0d1874a10343f1f5124310bb4003861af41badd6124a138a391c088a2980d106a25896b59a7b0c32e20ba7810b2f58064e6023d98d84c1b71bd53870516bce3236d205df3493b1db0625072e84b66232f25619b60551ade2c694d4cf9a6c969bd8da1abf7675d1908f59692231d2c2488942d943e9e8f343762359f0eda2589617f0f04323c52a83aed0008592fa59d28088e8100af51774010ab2ed80d5022bfcba65032282540c85d98c8a9a908eaa2654dbe088032ca121156bb2b2c0af642acecc6188c43cd888808e8003870141bb3a65731b94212271d60a0b7ef274757d97290b58f8d4e1e2fa92070befd9c08e9be5195d2ecdef7083071cb8f8af5315c7811b33cfc460fc66cd9916031bfd9cfde9636e5b96b59a3b0ea7d31d1d7d0c36b905d98d24b37e1ee90080fbd37318f79a3ed3620af02f58db576e6bdd71baa2205adcc86ea4581a2a5f6f45746e179046350f9e0317a25896b59ad9e4020c48aef7194aeaa76390016c4d5611960c70641ff1a7b7cde0abc3f9db0cb89cd862ccbec1030e443f5d88b96b59d66aee385c949c06a4188bd21c20516ae0822cc083615896b55acd8e8b175d006fd14618e73a75c37b505d84b92bae7917559f51cd342f487ca62c22db7ecdcf9777066d0482b9623f8d1ef47383b7fa9aab9dba0529a9c320bb91ba08c60b182e34c8c1c06580c105306e2d606418304c87c1c2821544306c432a846a308260c0800144fb81e1d353ce60c862248c11665f30441821886fa595278311ebf7fd1758c8d4fe44de5da68dbbf8af0abe1d7f7b8d7517dddbf9da9d8d2d40d6ddd59a4ce014d003fd2932e1109ff606d871d41e3b9eb246b773422d7c15f9d8b105fed6306953fbf82e39f1de3e554cc580ba0b339eb2b9ea2e7828dbb1f51a0b21006f7dec0a82180441aa42667377d1f55b6554dd30f6ed84f1befa482b14edf2ed974b6751ba3bf3e72bcb5ffd8aa7f975d545d501c0654da820295619b571d81b4c416877d4634ff3c5bb9192a1597754e6d4573f85fec43d46986773f5f26cad86799f2bf0d6f35b6a6537d3b64678d73e86f85619ed22ec8d5fb10334112104e0411fdbbe603058552103737711a630de14235d214c95a6d5db03260a36128e7a4990a93e689e5746750b92167452c495c420e07bad5d6267f6e2b2cab058845b033b76259ccb2cd246ea5ba3467a0394bfbdf0ea97cb574ac3bd8e95b08b37f7ede98a237bd736168215cb8b7337eec40e8aa0025ce3be12e68075ecbf304b1ebb8e1f2f596335ee873e5e7c7102689ebf99886e501d8cdd24cafa2751bf9f2f6ce6a81f2ed9bf0e62b721dc5c823c46031841bfc6aa572a068bc715428823215eb75a4b8a179b295f4fcd94e2533365f8d44c095e33257e33e57d6aa6b44fcd94f5699992fafdb3e7dbaf99923e35538af776db7befb5e6bdf7de7bf1bdf75e7aefbdf78234dacf91bdf786f74d8b5d54efb5a7ee4d49bded12cdd3481463fcba17062bc10c899778b5bafb14c18bbbbbbb1bb4dd4855c4185f9ccf7bef751b8aa01886a6881dc418bfeecd23c6d8cd7bc33ae3dd61879af5eded2188af3d753bec9055713687106431e166ce1b25a4a8080d51658a246bb0a234f9cd99cdd0c50a4e133bbc3077a648b11aa720e22bc20992264776a00d3dba50190206227468e0fc59f365fd29d688fc66b3217aff0895b783f9081bcd271f473bd1a1c4ddeb35ba689a309304b606f4eee54deddba1407414bc9c83bf3cde08d4d6d9c62bdf6e24ea41ad94cab07e99eae3a81f45f4b16f9ab27f3e5f0d5c3e2e35f06c6c84cadbbd1c7c9950e24ef46ea428a29fd6436b5f3ec3becc36cf13fc0541500947fd9b45ff117d4615f34d42abb529c5ef1f982e20c040f10207a60921bf383eae1e92d84cc30d98df839982e436049822486e435c9fef1f981d0ff4fdfbb2f5a72eabccc5572f8a6595715eeb621947bc59a9661291299ddaaeebf88bf301facb2daead395c5b78babcf8bc84f1792183c5c5de8b310886e215455ba98841300c45f10592a128be5e30d8c885bc24692b2545f205838d2349c6e4c8ae4c662b9569c1c891246331996c4683947991f9665ebe7a5992f9ea3d583ed7c7c756ea53a7aa54353255a5ea920a05a74d1c386db6e4685dd12a5bae68152f5f1d73796a457978e813e5e1a15214ea04368b4cb7a16169add6de8bc10b82b65290566befc5180c6f18da4ac38b41300c45f105bb3098ad14168aaf170c368e64ecc662b6d2186c24c9584c269b95b72c6da5654c369b95654f8fcfcffdf9b195fef8fcd068404041412a6ab756b395d67a461fd29a3f443fa435694e23adc9741f680422ad79bef8ea41b103d0c7b58aab4285ad54857873ade2aa038932db8daf722aaefa0130949aab5e83a0644a5ce53aae82a1545cfd1343f9ea953eae5265742c84157fb1a05419d80b00045623017d754cd5487dc50199366daedbdf17327807322af859bb8a47940318c56e51ac06182f1cf1bc57dfde1edaa96f07dbf1b5f5c269b12afdbdeaa2f6ea9817f270a9bd6bbabbdb5c43b1ec45c51a5af0627cdffe5d53c92ba7bc770dbd724a4abf526bc3cb068b7ebbe876cbe8c6d1683f340b9257ebea5056f3ac91b55a59b6d37c5a7f6bfd2d8b57b2e6d5a1a46eadb52279d79cf725b3d578bdf5a69ff1c6eeeeb3bbbbed69adbd6bfa524b7677378f1fbee0f0e53744f9c507c35b17bfccf065862f535e967ce192c3971878e4f0c5ea4b150e3718b4b6142186245956bf35cc50c49fbafba5840161cd04b1e5912ec611172e56678d441b03551575a5489672aa6867b01242f1e84517e348862bf1fa66a0fa5314cb19a6ac87e6ad0b9b2e6afe749f0d23f5919a21c8dee6db45717e3454796bde4a4199ad06fecbf4f637c39cbf1405be2b8229edf7af8b993fef9da1acdfbf2e64ae6b39716f755d74b0ee3bbc75d1b6af4b982e612c132516c11786c130c63886ac570c5a332ac618638c3116b348c600e7318ea188c7b84ba35d22d84a015c3aec5095c5fb614b972bb04b0c3212c2e47027c9133e50d9da4229a59452fa7a1509c3300c43afc160611728252c555851400569626a77d63e8c41ce87610c4b7c08002e6dd25cb103440f2a3b2d40fc7e4794524a29a5e12b9ff47a29c3b0cfa4106788224bb6e8b0a4e90b798428c3a34c992670e0611782385f1aa82e88b7586981610b113cc4f2fa295aa32d3f44e1b894fd341aad36f4fddbe27bf1fbb785cb9fb429196eb37a2f894372528241aa7eb4b7bf46642d9212dc0ba426e5897b8b7b61d5f4ecf0789404bd3d46c559fba2e6ace32ace3a5255d9358b339293b22bae2bae2bae1ad5b2783cde0e4fa23182558c58d60b58b817aefe6c325a2f3c59c797d29b9194945c219cb16f484acaa3b276cfa8b125fab15ae5650ed26a1d69496954e69e34446c7d8f5ffffba535ba80c7cc1bf4635ed22e9091989467f9d5faf878758bddda0c822f1f1f43b2b48ec4a4ac17b04665024aef2973575ce93df99a3de679fdc74bf3bc6e4f1d3ff3d9cff51f9f398dd59865a599fb789995aef7388ed5b8dee334463b40e936cf6633d367a40135fd8c76c5d91fef07b23d3e3371d4efa1f5e4dacf8d2dd1bf31bbd6b18f24098b810096e609661cf57d1ce72841ded628287bae33efab8be1d66846cdd51e25e58d410d1c27e5cccff6a98183d4a43c75d75a70044dec1663876198eb62d4c7b5eaea88c4a414ddf7f2ea1fa8325e3027fd5e985ff33ecc613111689efbd6145be6336c2c9d17a8b4ea570c4339c91845330218000000a316003030140e0a85a22409f354dc0314000e7fa64c504c184a034910a5288ac120c6186008008410608c21c620e6e63a00d02f0f6628fe4f663dcb2cf2b3af3207de05498e9699d9778001f217d830512723901306cb869636d449e149918a201820ed51f456529aff2f2e9f5290f00dbce8b22352950d0786f903d637abb0fff842641e736cf9c0541f3338872af34e0eff60de850722d4407335f3b33cdb2c3bc778338c67c935eb776c5db453fb35d000016ce95456fdffd5d93cfc502cc194e54c3c99fe462027bb2deb6c7778a1a2828144e7e372dc3f9e89d95337101e8989e5f8e6528ef8772343058c6b631c4939edb9cd82f69e977c16860b601860fdf191bc19150a36e6b4b426b8134e0770d4a105ba131dc63560daf28803f90861c106da250a5eba2e71dbbad49c7045176598534010eff2bcbb7e2457df702f5290735373c666246780b6f847b9c22d1902daba78b0bea6bc1402fcafa03c5685da295fc53f958bc41ac7270f4c4d60a6f6e9b4beebba544a86beffb3212d9b9f4c6a3059f1373356c0a4f9f4cd59275e052eae2dc9efb3e76d8a5760797f62d14a3da3372d66a2406b4a7164b917017353b10122efb7aff10b9e8eae5cd6ee2e7961ba5dedb7bc0de788f3581d8defe947f45f453cfeeaf944a6b18e47f92a2ad001837c08db32e49ce746823214562a69a7f0b47f81388fdd242ec3a448ae6d92acea08f7618387d2568bbaa492a74c02eb2415e8561a311e696ba6bf6147481e8c299032e2531f5c3b5aadb86c2991c16c84f0340724c1dfbf5316ee5115341b5779d02b32126e380551b81ce828efb1d3c17f7fa723ea7bd00f7bf02faaea34d4f703026ee48f9be7ccf34bd8b7323a1363c32fe568341c484011bf76e3ea541c44218de3e015976bde6b5fdf43ca3065df4fbd4363c05dbf722fd706ae7555207a36c4a4719bd54e6b33ca7eb6b7df49b7340381fb77542b9a227787d3f02550b5f92df7af1c03104e024394d619844349b440466ea55649444c65a3251f4183153a3212c2b2f131094da15f565078b64dbe27325f840e2057d7809e7d6b6bad08ccd46c5345bc20acf29c701d59c518d84eaa3715136d9dbcd9346dab9ad850d40f04e176fcb0733acaa1d6ceadbbd2f8dea50e52c0e87a6248d233d7ed859cbd6527e0fa25fa0a90dee602e9315036e4590d0253506e33fafcf88c7a2c52be7f6c56d03159464453939c803663336784822c2b88c3ece3b063edbde959a2bc7e5d527288cbeb04e8b44c156681d07b86d708835e24440dd542a403e84becc903d377534d90988224a114b298919ac8660a14a7e9a47ecd4754193268bcb0185db0b4d7bb998973d5841fa1da9316876de27abeb10b4ebc8e9d5e5f582817bebca353a117d3f7f433ef3898ff47be9fbfc6791225a9484f2a0eab9f30b1e7368114a1bec66952054ae7b885ebad8865c4c50557687351ffd59c74f52e93416129ca9805a6746f1690d7ab6e75db42cc610a0660d7032426e0613e6cc1841cd82ba7ef8871d0131649bda1c5e663dbd3123ba9bbb679b3d192875f6d20136e80d4484f04918217ef409f93668b182693e7e068e35e0d5f9ca84fd10355b85182baa184db53465a53241716625bdf3aa5d5955a8627601cdcc7584d56535d4b7b3501e70db0c27977ce1784bab5b7bed15d5253ea77281642d8277940ae65c8f463a5b9e904a7930904fa08dc6015a304c04ad58b37ec9c972dd1f1be3ebf56d21f38b75a3fc5c2a90bf0a509750bd79a99922757362eb8dc1846e52a112520e30882411ed235874bce2b62e1983b39167073a3b7027f9194e0258ef4d8f97779eaa0c097a1c7de58228203031405a5c1c46ddb021469246244f61b7cc41dfb26731496f25b3803ec342d3f0a76ba8457f61cd7aae1ede0e10d61f071ca11c979c76397b7b644081afc3c2a4ffac03cb071a246728972eac0448b8c5b1ce28398461dafae565f67051cb39a73832f6534d3e20632de4a382cd13959bd7579beec91525bbf980fa2fe8ff20021602ce7c9ee03697a4043b6feab85431300c27d7f3b6122ff8ee83365dd25b08849c70a3eaaa350312286a96a1059e358e38346de5e8b3d12350ded24cd734a53b5a207275b0793efa1acfd86db90f9fc890c57aba3b03bcc8f5c019cc5f6bc584230376448292c4a1578735a7bf7dee671bfd1e6a123134823c14064f41c288ff1c360e40e37f54b09f4974ac0652b81635e07c8bf03d7b0a6af84cf07973085d3ba607509e5c6a9b4f3e329f54796efe99a3e3aa1532dfe72676918f4ad1cb688e2d0b51845db1b242137534104326726cebd6cbb9417d1060eab48f952a6dd64a03fe8ae68366a590cbfd810c062678413f97f7899788ef8235396f46a6e10ca01f7ef7f5d05256a7da26317b3dcb258d4b7651c58d6aca61c35d64f163ba9184d95759f57e398d93158683238bcf93a47bc03b08d485baef1688c2c1ecf65ff80a7b65c83bdd0cc83ce9ab8ff2bbc4aad31a7d5639e96165f13e5ec2b0d67d3465cd421b0495d0705dd8921402d0df71332122c71456e819b597409dab82048e684d97820b7299a5971525731726b751263935db05974c933e2828a2ab0b586ce17545df097abb3205dfd6f7408396a6b368a5ca130538465c97d784853686cedd0fdac5cce05a21a16ec0e65545a7a15ba637821fd415cd0fde1045e455a99587c08f3d0600d1ce919f803a243eab7ee18abb58356f4e6c2f76e3ccf8fb9f0442ee1d0ed3df7972fe2d57e6a0862b5bfbad58fe6f90a6a08c3097182a020e2e45ed1f3cd4b40db5bc0fe5264fa938985945db47a243f7cda9015f61e8684ed6cd4a7009dcf2d50583355406a9700790b2b0e8a6ed4ebe8f168570bfb182da75d732e2b7b95b40c21a603012e4595aed63b0821f79dbbe580c7e7f8b1410c8c2a88cb87e1e2f5d5d79eb730834a98892cb9337b4196123cafe97ace8946b06059833815e6029f964304c923f409b51889debe643db117d7e0d073627ad84ef712fe5e6999f54af81cf193371778730027c419117168acd3c691e0ca6f2b42bf651e8ade93a45485d9f378a4cb70eb7219387f9d9c18e2f4eba14b116e603174e3f50e6f19065a2d09aa86583639e3ef842f7c3e41d8500c35fe38ee6c5bad57dc9ad6ba01dee4b0298fcff38feecaff2432d6503e2c5462d1ac0c9d5e34b7ad5810160474034c4f90c163e68ebfea8731f07c9d682ba832250f8798cafd97733b5edd55e3285fed213f50030824ea5329409b9cfa6c2c070c6cad777bfe1f367c89b1855e32896d3a72f731e380bfb598148e9368a0ccf8b871198b3dbe60842d40b2a6d2a181ab8dd9f1faa76516a0eb0ebd06103319f387dff194dfd91743003241bb99f4706feae2826e5f6030b402b3f757c42f2be352566503cef6a0349d9ed96cbd27b02400c5c750e2f9ca8e4727018aff35136c46f07247ecb0ee432d370743d37ee83e37feee20ccd1103231dcf9c313ccc584736bb99bf34f476be212f218041276838459e39d2f87cdcada9d713791d30e83025d761fb882264ec9ca025821d680e6058679540ac983f705230d84a806dddf70d29a6e729c4b9c2dcb6a9cffaa05a8a5961ff85c47736417390f063ecf80721973261e3bc37dcbd5a172f63de00fc10996fac801518eceed60db19a9c9410c0d9a5b544d701d3480dca6bc4be3fc6dd02e866451d89f38eb5f81fd9a1ba3a3ee8d10c03c5c8b8451a0a6a990150cf5b437733549d1a92f3009bba491ac7b05374426b8310809fbbe3b149cb2167f80370f477c3ca4faaa0691ec79b05630d20ef4e0764a81454729a5592881d59b9f9bff01cb169a3203017f7a27ecc9eba90eb82efcd40a134dd521c33c089e76c3afdf057e410286d976df5d78aad852f07ac016c67ca12576bef27d90d7446b38fb424a8953df2fef9e9ac0c6d91f7622fbf6aedf7af15ef859f6eff430b1b8fb6733a0ea40775faae26c48dc9dfa0c89ee0135f99ee290b10043a23e22453ba0a980f9bad6a6995b3a2945ee110b9b1495843f2c5697c61f463e2788e071269c57a7019b31ef05e368e1dceed0ced29d9b561f1ff82043798f6a09c2a4b3f4d7dd94800ff257007bac758f10bd777697632ec439e20fbc6d89b5a7974472964f3f507dc6ac955e58ebdbc6e4bbf1802348fccb1161584fbf5870b38ba403aa5b3fd04e0d753da47d592bc0585c718b968c54b1955bea62ffea0532364677631106dfd7ec4a763069195830cb15a9c991970f37d9a3c9e5c182300500a44f39834512548d64132f40e6dd53840b25552bd7e8d584c6fe2d10b79dbb8935f2833d03f8bf04cac2429e2b8b7346dd5daccd0be49c07cc602fe1d928b2b83f4e44fb310df47d49cd2d6f5da5859cb3aa92812bfa8d3a20bc592a93b6d94b15ff6636026abc06b8184a80bd860efe8561fe79c0044aa92f4605d85ecb82043f5103f707be2b342597de812f54494d5978fff4ef08a00ca78c6f1dac0cd67a58411fa581049a84f78ecbfd24e8be91f92008d90fa0dde13d736cf2b20b14889401cd539b201303366c0145cdbba0d9fdcc936c970f683eaf96ea7acda96cfbf5c74b83c0b7d78420f52b882cf3076a74d653d2be95dbce127607569052c3e7f2ed168a5ed26d80137f5217197688566387fad3db31519ff325aee39e1377865f097edfa63d62e4ee67401abe0fbd05bae4e867434e3e9fae0d3e65c467401abe0fbd05bae4e867434e3e9fae0d3e65c467401abe0fbd05bae4e83d57f49a2e09e47806674424a613d9010ac4d4cabdc100f59428eed12dd8017de607c81373e13d77f1e31d9b390ac7f4ffdcaa5de600d65bb21a4d2d0dd772f9f0a27b1dfefffc9ace53437a82960d734d33385bd2fa6f905a8bc15064879ae49ec1ee127c04fb0e486bf9c13771129be16c24f5801cad86072f972a515ebade409e425b723929a2ed21223e2c37e031fbc319b79b8de88fb6c25fb5f5ff87c1ca18e5a35018d0bbdef2d928a14de10fd1dfef7e5e0d8fece61e6623264bcfcfd2c9591573b9aa931bdabfc9de3c6a5aa847b7a83de9a81d0b50015bf748bbbda368529882df5d67212b4e230cbc00d357a7a3ecb47c6ba2f30a74ea6031da2b10f68e65e8fdb18829a6282bcc18c39d292a0e3145694da2b004662ab84c5cf58accde0f6cfafce3ef3c67bf1dad7c233a324bf0cb1db4900b6fe4b72449c4e537021adb266f08b1f99b42b7313bbcce2a7c0a440d07aa14fbce95ce84c812161a3a66dba03a5b70790a233d5c7691794298eb24927d51d81d91871d7188ca1f429f64fde5ffd97e99e75f8ae98b769966ffbd89e05fdee1fa59da9d9fb12ec319cb784bc109102d72b6fee325aa48c58079227909055aec4f48e0f435590b427be9f4b0ca14ab114047e789ef900ab7adbe2c7163e9f7048de74e2f11cc95b9f7935c1d681da932702d78d2fab830b4152afe907fefd397330ebde9bc15406e9ba140b16ba3212e663b8154dfb49e73fd7ad98089973240ff37a7ce52d2fba9c0b5cbd4bb634bed5da3d44665300df1cfa0c30cbbb0b566df748bcd724655f8810f4ef0f15ef420f9d6b7c2e17b52c00c3acdd424d099374faa549116440bb2d699217d4c703ed4ef74a407b86a60ea9c9c50da93f68fb6dd9144d35fa40ef546ddda07aa4833215a2e062a9d501baf3831bcde74124c4363964caa29cac6f4ead083b2fa88cc9d0f1866f18fe6f134b059d78335684c8057b4251f6ed23cff0a7499c40dc707b165430e7d41a716f2d799095b36e015d5d9fe825cd5682a19a8c4445a13c1441286315c6760a87f8c1bfa590ce53a41c4fd8fcce51eeafbee8ef6fe32bbe4f0219091e7166d961cd4db631bacd8de6e2ff5c8374e2685bc5731db9b81adfa778239d68286db92c7cfa5bb8b3bde5443d9e64113d79a5a823a843f4fed45398c49c2819774497c0f456c7f4151141be7a5e9b6c8bb9c2d8dea9569373cd5b8af20012ec4babcadd72f7f2f4f29646ea2c728b58b792fd769d5f423bfb5afba62aede71a9d6fa20beed612da3e087124006b00638f1fd4b336b0aa87bc234e580b84cbd8d6d8fba5ff7632a933c38d49d6199650c9b0aa42b022fe10a54388bf561ee9b97e727e1aee0eaecb13481aab3394892a997b4d6b5d8ee3c062507522042bae6c18b931eaa330b823b1023d3364c48d054f279bbe9d933e4e705b21e110e005b68e07e610b7881148d6a76ba42d2ddfceaa681b55a80a6b7f70940b6ec0b25d062dcf142dd7a36c909fed63b684801e142a9af3555b7ce585af98cfafe1c77e4bede7db4d342a15e1739b57fc962164d1b1956010392d1e2a10456ae4e7e48f7ab1599dafce20a9ec01da48499a89e12792613e69dc2727993524a1bef2ab81f012fb3107643d51422c202610a1a1cb023730d56b31a37f8c46b6b6129519b45f7cff5c00616e477d0c377f81d55453773e9757635cb90ce52d80a76c39c9553138d7244387db211f55e868fc79da5d6f6fa80d09f561538b1397257216f71a5183621dbea5e35800b0082199a36d1b831f900ae78b61ccbfb32ffd1e6127a992187b2749a38de088f46886e3dfae6b25fc0397361fb16cb2a23df8f30a2c1b07183c4f2d805149de0b8813e41dd59e60b257c927fe5291b0a4d6c0dcbaf1edbcfa2a5e25fa02e9e36fc4f3a85a0b5a3dd73398854c424eb81f62354ebd15e58c381d499c9e533acd86005413568cfa39440e9327fcb797715e2571c2207f362b16c69cd556240e12a8a37a24e94752764fe693d98337c7567fe9c642197e4a37b231fe00fcc4eba8007ad5a3acf4f72634e5c13e6d9240afe1d725e1d2b9ed79f9fbe3170a77b637e0dc54887db3f2e595a30a1b4d7d661c0a89731a7469fc88bf1def88e7eee9761ade6d199233d4f41f609e6f67b96136a652c448f9dce358cbff35369745e6ce6578bffd92168f2ab91e735d2503cfc7b8e1ef3293e0f09d9d2e4c5588987d868031836d2d63e39e8f16a7248c5ac599c9528686a97cf19069f0da8586a9dd02f6e9374f486e4dddac473b3f3231cb1904c89288f366fd3d101108db469ac5b6f64b653079a7c5cfe9bc4b9e5b9fb4f577488048589313e7b94f0f073f452bc9cc93662cd2d7eeaba488d400bfdb09bc2e499779e2bfb2dc35b77ed272b82af7d8412d93aefaaf40d811a01a5ebde3ee68e3adc55c3fe997b9b2f19d87351005449ef769d2874463be90ca73444d45b61f71299742aa18fbd6be8f586554db3cd14668e0fbe2ed54e40635d59c0b99331bacb0aad34073b7eff0601c87b8daeb87bbbf457bfa432c6f4e5e8f0073196d05e89b760a8d46bc554cb547268e7948e5a00196e6588130f6b940093d5fd9c757afa824541e58057e306049da16956e811ee3cf72033d6a7629088571c77d8c629d29ad47a790a230f6106d08c081c76ea46eb692404a17623ddbf264e051056115015591eefae1795176f62afdffe6581c18886d12b014df7de7dbd1a7f88e1fdfb559a04213ce6869b724a3b313752aa012e91cccc02f4575118a4eec20c37ac2cc6752f874374b1c64bac3d0f6e6069f2d3b2a0ecbec6bf2a6da08cbd48f41a6874ba87ed35bdb2aa7c656b3155c8cf28f1265110a34042c3681d0a5ec5d918912f43e5a5a7e9c1a5a6b1956c7818b3fe59404d60038f498b6fdbf084b4681814a4b57b88c6114b9595699582324a12c4dbbc4ede92fbf28fde15cd54b646d68839f9ddd6aa689bb4e1182c335915175830be7f1bc5a1f5e5452d5360c496e75eed11873c54e740f55d479c5a3241dd74840d31f427945b7a2451d57086e70b7a701cc4382b332eeb1ef3b8a90e5149d310a52128e37c0439602a3511368fd84da029b779e455ae05a3343cc5a7b7fbfc2c5f3e4bb95bf13da8bc432306adfd12d56bd60c749c84f1796f472b5c6d106a119136b3f6742e0c90e7b1e1827c9367195b2bcaa83a1573d11dd4c0097d0de395a901f3892aa830589a8ea827f24cb094c5559afb57f3ea626f7f239c500b10d09a777522ee1ed1f7ccaf6930692abd3a349d50f819df1cc7958f406ae5fb3738ce0390cd763e1f79d4d251b0d851386becbf156508ac3da591b5a9a58c176a518694db4d3c5780f2b98afedb95a87565a37813a19523efb56510838f2c14d9ffb444c07a06211e5cfe7024199e2337a0a6cdc54c42d3a2a666646cb05a0745cd4747225bb60e87e6c6b773eed0591cd3ee82b09309a218f37f2834b6e119e2a55b561c880a251af4b759d7602b420a8662a8257c74adb2937022f1c69bd45ade89c480511389549cdb7d053e14e0afaa5576b5266f939cb607c6374e7fe839ae069cc3c669a8ae589d040c74cffac629f6c55467282e349107ee50678af8b3558e930248b591f9348ad7db9e56f059a4e0639920f80d90004e2860deb94ec34f09d3ba343dadd2f99c5861e654e147624ff9d6d844e444b6656dfb12f14ef52fa429224171b918c8ef2f6011775002b316ee4b9997f330355231ec4f73bed5b502c2743603b108be41f27000f874c79a3d784b819e8351c7fba711f6fbddc54aea1c4f53d063ceb6e21c4966e5cd86f125bc2273aae138f812b9d1f36dcd8b72382404e03c18fa0a400e00a9691a2a66e16a38e8ee063887afbcd4b22623124f3a8aedff1bfbc6527c50dc24f0042963d789e085a6c579107f646958f94e4c74ac76fa56a5969f7b415d157afa3dd653bc1d01f91f142c8157c666caef210f84adfa3dd9bda8cf807741aba9489bae6af0a91efe7c4911df6132b9284f863acc4bb22adfe1a433e6a685b2e6de274c25f97b031b5c15697bca983c24d9a913f6103b907f635b1a98d425c4e81f8cf10acc7814800eb17b408d44ad89e36d9ee144e5f000d62f1bce6dfcfb4ceff98b07cdf4c287f4d0d9cf08a34771c5f9b1d8dfca3804bbf58665370ce801dbf78abac39a19947a45ffbb6dd4ba30df162878d27212970c9281115211996b755f37581177bcec14d4735eeea98f55c3392b4cff31428d6732872717b365ad1b58d94a9fb9745768d10a435755090d5e14e82e08e3c72ce8b29be8b626a1b8b87aabe309f77eb23a76794bfec276ebc8176e6631ad018f1f2faca2712de2bc9ed099ee66ccc622f0f6f51a0919db2ca45aa1a390a78562a3d8d8904d37a45b22afcec0e66db00a95d934d7d2a562e438308892e8b45eafb1e2e6f9648291c3072f3b93d6f8cd2f13479f835057e36b4d9d11ca4273624528c69ae125a8e6630507b4ce2f2fb3d729eb23eeccf86ed201671c6b729424bf2e57b07b9df97cf6215a688ed4bda14424cf3c2048144aca11fb0e59d113e5887e9f271ac949cc6db545d7ba5726d6119ad5409f3db2c0ec4edadc68b6239a576afdeb8cc1a72cd40d971b74e4667e8c6a2b0072f9a90c1581f7f8e2e4b2894a2fa046d9fa83b01c932aa74469133912351be123e01a1f0494e4fad5a390a48c3fd3680e9f5e4f95ea7cd1669ed305020012bb9d18c22bd8a57aba0de504627a981452b1966962486ed70f9544214dd7c7a23c837f44aa3d9ed0f876b75a45bc04af9b803e3bf2e5ef7eb7dbdbacdb079461afcef21db2b2df3350959e04b8e7d8bc24f22797029581ee414cf6d94bb8a1b814fd162d2726bc519e91931b7fc61a2fd5b3226b88aa8f40e6888ee0b7d20736b156a4d44274e33e2df66f02025e35c8b6d109abb67ee256d2f0a5e2ca6304a1f236135a66fedb098c13a04b7b781bced541b00ce90ed63b709fc6639ce69e96181c5d9b0d592fa1d10ed14487b4763684802e9eac833ad59e3c3718afcb0fadb13b5afe657b355032c900e7d8cee78e62e1834d98d59ac63476d7afe1f8d4eeda2ffde00997aa45e0c0f2190be32840203913b6e44c9270a2d54fa00fb980dbc1e5658a53ca289e79b9c09d22b74ec16f5d3337220a867a5a0d01a14f068cb110abf3455b4bf62f464c3c15d1e183e161077ca7c8dcb27f80f209204582ebc22c948ea1a55c92e21fdff9dea713d3688dc42253ba8b06bb24d39e70cfaffda9b1a7a6efebb39f6d09a9fbefbfd01c787e751afb75566deaf26101daffa5061735f7fda381836484cf4853f511172d071ee874594bf113cac49474cbb76d20db4aa5a658a4fac38aaaa142ade4f6a2b0a03e5d6cd0400cb65e3af497761e4caf434c259c509115f4bf26ba7c7096b72af27f5bdec0021fd36a8c4ffaff9a3c74a35bba24c826fe9ad61b60402ef010787c82f8f19903508153e9c142a053601c859a491a3722e362dd9798263a1c4422dfe7186c4cbd1b3a63083ca0075f04bd6317a310f00957701bd1978607f627c6fd5d4beb7b1514956448a602a6e0f576209692753b0c06407613b7cf5df9447ea90665b091ed93741d0ee345662aea3762aa64bc07a64aafdccd96641cf50b2ebb23389dc1c801672ec7c3f6e292eee072750a2d968e40aa20c5bcb93e10d6857b0b17301bb1613d61576e67beabd5aee4258242045ecf4a9afd956b1b97e11f1e6485e0bf5e575f5d5b48884566f7b5236a649e0ecbd5a433c3576d8583e1129f22f6eecd77b2e3c4db57eb59608ddb5e4a507bffdf214a566e8986667a59db08596c4d036d95ed29542c642442e231a7a0a709f557f760e48116d436c6206e396e4df8848e3005faed501f3d0f262d3bcc9802bf03fef9eafc4d6e0352542d473bc87990d21403466978ffd29e800e8479e2886790df18f5d37f60b3c36b747b7312b7c44247ca05968e155c400211662cf3fc7c19dc27f9e15fdd65d19a991c319b4ae4ed7e918c0c6a100c328cadb2627a7944318bf732c4fd410fbd93d8a084d2066148b5a22cd466d0f0815412ac986bc89feebb14c80bb7aa06bc33c82a76e1a76ce00f3bc1b213c14b86ef367f0a92940e0aa61ee18615316faa81205278291bb828dd2933a0681d2fcfee6bad02e612013620cc6b289d7977f387e70ee5834e11de3029dfa50d4df6f0f185e70e69740f2477e5a0ee247ab7164e04e0d59651edd9a68d6e6c9def6e94285dd651d120feaeec55925f93bf0137f77bdd126efc9da4a21415497f87a317a202f7f7f14ba642ba1c0900cffefe7f351087d4d4cd0fe4536620f48a55bb535a7411873cf4d6cf40e24a81a616448127bb69d0d46748c6274420a89ed01013281152e09b8997ec2dacef645edaa6ae8ca360dc7fbf1002e60fc6b944b6d0e24df45ffb975250390ab53a412928a714f40601b53ccf4ba4beeecbbaca0d31f39dd8dbe5fc4d1f078644e3f9d6a7a612868ee1460746b4f69192a18abb1f7403efb1fc9db91d373f2f516335612e91a05bc563f51b8afd11c88b6d22fc668e62bd107abfb1144761b2348cae47c0042803bba41538d1907db12166888414c214a03d9f00679fe527ad525fbc0b7710d18ab696afc4d6ef95bba2af4a57f1f563e5aed0d6b2556cfd5e7157346b69155f7dae9c15cd5a5a89ad5f2b7745bb2aade2d7cfca5d29ba64591efa1bcabf3de860641f7a9727a973f674048a1b8abcb2863883358e096e5a3e602bbde2d2bdb8018c0eaab4720bb6908d7cff3835c98951863d3585d6f88da1447c0a8926a5cd559720a8d4b169755d8722f0d7e5762a21ef3b1d19508a6da712f1b5f38652346f8f730379f1d1406efd209cc55ea1d3e406ecd22beb1214122b0e564d04127df2722aaab0fadb96fc2313a5ed5189fc3eced08c787b5c8d11cc5b193210b615a0872362f17175622347c2e64806c6b20abaedb0df33b11bb71dcac47f28051913ca378612f929709453da5c71095eac0cdfc195566f07078a229babae5c68d56563d5dda4c572485ddba984e48f48a4650f2bb12cfa2d0d64059591981c3a1c7b2652abee88236fdad16d11d62afd1c7a4af1898df4eaba341c81655db7530979df4191f1536a3b9510dfe980a0787365dd1c618b4d89b60a89a4392b1ae1909c0457ee2da23beb94eace459aa024ee5745acecd1485ee23e6c3e6a91a2c9602a979a1b502d3fe369cc2c8dc8a0368a9a1f947de0ccd9bae7b73ce6bc7f34dd943c46d45fa6786ff485c6ea76e90b436ce4e0dcb620b28c8ed6de48fa7421a4b4296c54284b9f9a9c537827211a334ff7503ce126f236405ea0cec6d5f1d333504e656cee795a7a6b7c521834c2badf2af2ec144d7b86c81aa9ea262455d7cbadeab1195618725048cb61b03a9329866eac87a40c557918937326fa353ae4882cb5ad7a2eb81f83d7d992c0301fa97baab9c008b54e8abcdb539e16525d864f0960f133d4cba702f0ff0cd00981105e93ec297d80ed17125fd55f9b7fb16dc99a2515e8d0b47349896f103d238ae7a6e5fd1ec7a3dc43239afc0dd3347c5ca643aad40c5d07e46fa6722cc172a127d2ea60afeb235d7982b9a94509db7899b9a96ce9d66dfa0209b23f892945f598776d116ce2af837851cc595ce1f70bdba864289c44d6d3838baa063c874d5e43b64c77e59cf4b55205713827825b5846fb8cb75e803d5eae7da2f882bed0831180fe0d8380c01dc0df8819d665338ede81a086a0a8f94241682560ce5dc4a0ae044fe037ff188688d182a9b725091828ff9b1fa7993d8dc2551080a1492ccbf434cffe0ca1986012ebd2df789a02d0b2d8ec912171beff3fd73290697f020b2b6e7229d52fbd473939d3170d1012edf1d9ef16e92c6826b4362867c1355d4e94749055673302ac4fc53e8fa368bc5f42e24cf503f6c6734bb0054678f3334fbb4ad60672328601f22689550675f06f512df89aa1d18537e014886fd4f1a5aed69ec39d3010f39475875f8d2c63129928fb272befe097e71fc33738c775fba03fc096cc1c3a8021ddb1e3cb2d5e982b433fb03b99d8786f5b6a0ac3015999250cadfa87c3efdd16b4a55b945faffe7a98af6663c3e50052c4670e514e3ed404e77a333d9c6027d82436b854b113eca247d6e313c1402dbc54820329b881d88b9b32880f68810a6247be716b9ffdee1b9516bb0cf4b6b85f81d80e6898f6e4a61f9ee0db6810a9d8bad70065b699b07bf7600fadb4ddd2dec7721634f8015033cc1dae7c8683a72dd8d331d9debe06a2043efdf6372e272b6bd730bd37c8285a17b11be044affdf5904fd3bb3b8dc358a05bf53bbb3674f28a67c503d5e1c352a3a1a8d210708ecb4e1f3a982687bb92adff297c5092306b2187deb2381a6f5d47f061a91127e12473e3b415da5b5a0a1065cd332bd9904d60ce4e13778b1d096cd1747ee0abd474136e9bc967df0cbdb7c513f1f6c6123111a13eb79071ed03fd960be57b8a403e8eb11a8261cefd89b240663907364d866a1082cf08c7ab5f707f6d82a0fdcad678a7ce7ef174987d50cb80158c2ba2f06d2ddd68c040ce935ef855d33b661269949cc2ea799e47265eb389953d1ebe2e692075a57ecd26f13230d165419f077b3a88cf442494122c9929d1ed4b2aeb42846865977d7b13ef4cc917429f95155b6135e0692afee83000804c6e81d75881e52d9c0c482f165ef7b898373306f4fd4f6608ffc84c148592540065d4d6e3d5edc6a7ca2da8690db0f2921e827713b4ed4ad3e178d0b76db942d1474f366ef04c91b4cf8be26ff565dd66a18abbe31b9582a7c69466dbac961aada20979b3183139e7e43329219eb3575fde2f87d87148dcf188053315188556808f62f286795f0e0174dfb2c214282483111874a90368210fd62e16aad17db3ee0333fc420659e8afec3c0d10a45c212f424a5e47408892b7bf1c58d1831427276a0c9ea3dab34ec1542938c7a2badd253910b2218038ac46a00a23d97004e2a14d91c72af1c9671a2f39e31bb08f6465af25253d968522a5c645a4d121e120b1c057eab4e9dfd0e805d77b3aec96912526bc4677687e516522d353a56a62b768c650f6105b4925de0cfaff9143f17b2e4afb767147b8ba3a45c8469c23af2060bf212dba1a011be12f3eb161ad4180fb85686f55964284ea52a2ff03fb40c81953cf434ac082764f16d2570e3ed71aefcd423f7edc4d76c4615005b6e8f62fa9290cb054624bf58e0206a83b60231e6807e39eb65809f9f0a2e965587f8a61d1cb4043e7ce9458873a02635457daad1ef2cb99cb0c5935492fe90e25cafcd8b60a3116f0d5dae8ee56a02a9b76a9da32c702a60b78ba33809a2b56fad85b90b5966db8b17e7d86c7c3878b8ef450052aaa4c94c144074556a347b0d93c7f59e36b802c4976b04087a75759b55393a92c080c06be5e7ead132b56efa8385ab76a97e818d15275a8fa713a847b152bfb78a9aa149559e051dd417409e8f04c708c1d682b315aef29dfb38fb37ebe48653bf42dd0e55c708b6f415f8e9b43e74b7862fc7f03accd0c499c9e90d60ca24b1a8ded878157ea9d416e0abafd3c31568da53f979ced027e97d4be281958bb2acf534c4e95741b4330e93e86c2f272adb1d46ec9eb4eb2b064cc67cff0aecfab24ac7c9330fcc8abc26156340fd22d31cc8e34d4d68fa5ed306752af2a6c47c4df21e0fd370ae202336f331d9b3da7773699e68ab9bb28fb5d652ea603307625147875eeace403d1a4178b8971e7989a6e375d565eb0b27dfe9125f004e149955966fd4406cc2c50931564120cc8ec20a850d78ce8ffb8bde1d520d17820018e8d788fed8ebe72ac857a489f5fc022564736ceecfb5f9b0a47f2d9fca4c0101ac3bdfeccb3912dd2e3d9f0d4ec4003181029fd92892fc24c0961a8227c19c33ece671533f74e7d805337801b1078604535833f2bd7dc214e9d112a8544c644a786482365d1faee01a0d870357a4aaa92b757c2c2850358d3f34365eea5185de32b4c4fa8f35c7aede0a8e2828b51fd1085e21d8266569cccdd030ac50fe51240a7ab04e0aa4231fa1c0ea6a69dc4de1ac7b654e829117d250074395e0245fc5000f3f6ac2a02610ad9e21cfbde0cbd7aaa0725316cebcd9ee380123bb7e8d3a2bc6606c8fd03133f9af47cbe5b4af5fc1bdf7cfb4f41b9fe1f56fac7882d75dead1d1d60f7d65943f27bcb54fe22514b2654400e45a5009fd43fbcfc9c18be576e485e9fe9878ad6ab0c84147c3f919e2150f56fa10439fe736a48b0129f7a23e56c0eb70434b10e2980998fcf4e921a3f702482af9b3189efb8d740554290df97f12b444ffb7ee3533149197e8b741b82011fa6f73de4a15613de757890003e58d16dab096d4ae19e1d9535ba9bab4a94c6fd42d3a1d840e6c69cccf6d342c10c90ba2bba71566b91f4fcdc1f9c687d3ee922d11f0449ed25027206b215d0d4c8dbc32d255bf93148d3cb6f05da190d22df8cacb3dc1a0473bf33b968bf46f507ba71e76be282f15078159db617d11126b0f002bb268fb617f6fd18a535b5925c1a82b0da5818bf025aa2592ea2af10e29afcde00df0f82eff30566e916469772a27572231571d4574b330434fa1c01dfaa5d737b8719ed9693a5c03196e1f24219982664139bc8898961b80d4e8690a1f9ac52e24a62c9e1b4b096f65c3483dcdb5d9aeac09f1cfb52aea76bdfca0d491dd80e84f37bfb7dfb0464e6cec380b028f50379fd7300df4fa3786a96d564b39659b9b1955bd14eb785716196cf05c6dc93ed5f7712a73a2d14f8fc19a1e28ac475cf9829c3b4bae78e980b71f6d9e2167b1f2fbaa8cb7a507551730298c34caa1850cd9607c45055922ed0354663efd8be50aaa3989d49fbc90b93a31d78466b3d1cc18bfac9a771e9cc9d529df4348f875e86ece6cb21ae772e1d15f62d52316fa7f49ad89817401939582c771a461d888297ed76f78e33eeec23d03e049760023bf747a2010d9fb1a295c4328019093c478662446bafed8895449215e39824a570e11119c02d4bc1cdcc9de08c4951f27392453e0709a52e2e424bfa0b452626f9114830a176652a4ad445d6a776d0629113135050c34a89c35fa0f3d858daa9fbae007ddaa558217f262a633eed0298dae1b054f8f76e162f8fd7f84d98256c9f272052189475e1137253bef54105e5b268492d61fb2abfac306c870df004ef9e40e5b6c8f10716e1be7f0285a399c0c24f3684b44617933afb6615bf7f55ba30c2568d18309c2464b66a372b19053e0577eb502d83c15451bc7e2e5ac4825b7943eabfb5af27b428a35a8ced9414b65e53c10b6d6621949951bce2692323950811262d0632e4ad82391d92eb0da73062efb7a2e092790dc7a3bd2c07174554a279770a1ab819e81401069d60142d2d2ca6ec3623d92d354781e59636bbb0cfd39b5443dc51428b0136faa94530200f62683788f170d16ae765735a1af3373dddeee6e3f42f91528040ac8c7dcd3ffd06147a16d6bf877f01d50066ae04bc11a0251982b49aa31533bed7821f8ac87f0794c0fcb792d4ec086c21e9bd4cf69365f6c5c5acb825d12f3db7c7ef5ef22ff5c448642e8ed6e3faf2a51eacc5c7f788e1750659ac83ae9bc1605b7d3024800286d6d88f66950b0cf6eb9f9de8c63060cf804065fd4b0c76c1c59a7dd16cea365525244148a34688341c42f795ce19fa39ef8488d946caf8209a75b01a86277cd82c0b2b1c8d8bfb821a0622a68b892a16074f0ce0f0d80bb8b0c0cc00673e18bdd807e42767c3cb768e34ba5d283c0b1c53f46299d31af341d3e6b68258dd803e30c5fc24b7fafd0b706fe5c556e48a59dcc7e21711b37fcec6999e1b500a4e9f9b1b7e8bfa6c6cbe2d1a1b36c2525eb672af9f20e1aca8a7021649a4c6e78b4b859123021c570a921a4a9698d21d364ba2eb1b74e298c4458added19a2b1414e1d68d7d8c202d419e5367c25c736e038a9848bac0c94110e6b5cdac3430a5d6b3db1c8d917dba6897c233fcfba7e7bce0059c6239caf5bd34f3b0fa5cab70497d27ee137bfcf44c2799c7507867621b31abd02d4e45529d1e5dcfb3854569c22d4bbe0b678380608c5224b19b55f2a23db995a763886ee2a8abc4444f79401ede8a3dd3fc9fba246de4bd13f01499dbab03e568e3fbfd3d7ab55bb1458a7b9df77a1bccacbf27617d1d3c83531f0c2e9e50438dfc61e5352abecadfb111549c2e46778baf3f38621226ba56a12192cc72aa52a187c1f07c89c4192a5b778e33ac8729632d93350490e30179bf78eb12e8c75e6fac119dfddf827c86f00a4bd1c70fdd3d118929d07b192f10395240400e6d19381fe87a67b59a7143c768669f376fbf7b136e11db8861e904690f009cd8a2bcab93ef3479b0246a5f8b3ca5eebd079d962b29527965c66df3d6e085b69b55f3c25a56ebcbaf7fc97892719be30ef30caf0c4fe7c4dba4a0182c76175329ec66fc0b186e24deb63adae5f3c914c8dc032bab5cff962cc41024f26a7a58bc6202fe0fff9f88c21d576660c53499531744bd522e3ab84f95973abe4e392209f344a660397cc8817ed76bdc79c277374560a5c9c88a87143a3414e66a332d9986b17ad0828e62f6717288ef88e036ef075061e147be73e829ccf4abe3120e4089a6ea46dc15fee2d291e463bb3e035ade4a5057256e6bfc274595da425b3b1946b228f5bbac30fd3019cda9411d5086c967c24331300d7f3beddbb5527433540c2b83d7f834a85d7840362e61641687b06ecc7ffeef6216e7c18c92bf36b347463951265963f00657a8ffdc688306dfca6784699df62d5581dc9cc461027b20d0a4d3e1dc8b8a5bfc5b20351fd2c066a8f4785ace1a6e57a76c1e26bbda1fd9d2a19ab0f313f9027356c942e48b90eb1bdfdadc58b6bfdeaad6bb76bd72ef89c2a0750105a97ae7b358a117a3a397934947519a296b272e271d24a7962fcaf06f35cf8939655c6376a6dce9ed4a7399348b4dc669fdb21a499aeb8550e898322d0729339f831f325944c60945d4efc78764f74837c4ee21a43787cac3922a8b3f0018c2d952dc3c30dbb7499c6cdd1667d5c398798ea55bcd339e20c35ab615791009b15ea92966547c4a5cb1ea2967b097a8af72c2896d8bccb4376f9420fbeb16d854a48b4647a19908f812d5a33ae8499d35c1a1628c06f5130675bfdada9f46dc28491ee9fcf1db17056ce9a1742fda3ab481a038bd4c8242ffc1f7fe05ffe512eb16c0382a997485a8372f5faeed5e2e0ab3501f93d309d1c698105706af0acc9ec8e0a053964fbb62857860f2d7e7c2efd074b60e812baa4662287438a1fe78ff70f65ad3fb4a7cd4bce398e661354946f9379cd0a3d8ae0b88aa3ecd2137d57dd2007474334813bdfb6fe6ab7fb33e029790dff61c8bd6ba338ad0240084fc0b7e5f28c8278f02da342ac7daeab0b7f5e2a17052def621378d635d0647d8a84d6634c68ea7c25435a6067ece4c1c3d84a90e43a5fbd85bcec8a9a4d633d7d9d0ad1bbf2d0f6095051ecc6d28b3a333711ca9121b491986743e693196547abd14dfca34588776f21f198f63f0d1c6c23be0f2bc4ae733dac024c330713cfa39e0855192f00919d0129a95a766485258bfe1e9f276adf26ca9b359ac62d2b328ed60fb34cc6b92450e5d5a0fab559c131f081d6c8cd8315c9a75797e06edeee4c9e0a46e07444401151bf4ecd13e6da91c4553ec2265f2a917b69ffef2f1e641fbfbf018f6a1b8583300359c5e9e90755d3ebff63ce37289cc11738cf26249328887186eba367416141fbb4f11308b849b51e630710328f526d363f6183c20a2f4a67abbe170e343b08961cd9ad8f32be54942a3b0aaaf44bdd4ae5b0b179706f4d3242112436fd7022ba8a50997ccb191aa2a87b09f9ec01e341084a3c50ac81b1a1a583508b5ed344326622a6ec07be86d97b0c75e49788efc6b163cd6c851d9de03e2544401ef05a29416e75649c018b5074758ece25bf659d86110f72692f989763bee194b7f456cbdaf00652e4c089b02c0215b205507ef9c9771a6a5db52cbec5734c60fc4f957496297d663afc4941def1293cb36f29c517cf3be6795d3cf18d15f4676190aca741ca97972c0f22c24a44190bf11ca06bba843aaa9222ea9ee235b696dd28f380f07a0ef945502f9d898c21cc25689b8d6b524acd5e5ab1d4abd05941a3a6ffd217279a1ce48a6581d684924826a3b5d82696fc59b10c37c7d96376e7e37b6dd09db9472c086ea53bcc63681bb7beb931c37f350773a22ce4b6d68c80177ce51f8449aa88e69cef2d5e0bf83a1703ea6b617fcedb70018370a0833029779d0f33ca0221ec69b43a53139ffb865c0e5d6cff2783247446616274ee44989e4cc4be241631efd54aae245feeaec2213cb0f489e6724b8149db159a0918820dec1233149b7f5499b78eb1aeca4bdf1d971f798ba50445add82123267a85a1d6d2923fde7f6cb15e077c42fe2881de0ad6922a78412aeaf04e9e464e4a185e1ada36cabbfe391eee12d4969cca96284500a6c9a9cd7f2e45e6f20439a6a4acf7e97f240b5a686547cb80066e8e5997c5d586069e56a187356430706f0770c0d870a2eb25944351904ad6e2918022199d8243e3c89cdc168ce433d91814ca9c319efbaca316365d6a06dc250d6f9f49103ed158491243a4d0a60d8063c46265a14c061bd70051c97ac6373812b08f7f924797be2b72e1530d1c8e5621d1645489e06cbff39c42abec17181ee3f329124cfa9265887c99e44f06a119be82c9673b56f8a8208fba88de8a0876cc673e04dd825cab732c1f3ab9fd9e8d5a9192c7e28f6b99ecca73af7c9829f2b50b20a4325253f0463e415b7e3ec8c49f7368e308f46869ca416ca3541af0b506b76d64ea8fe0bd329b23838bfb44a66404726d4ac2666da55f2c09c3c8a1680577e80a2ca7fe2fea21c960adae0a4ac02413e4bbef228c3d6e6e69bd673f2979305bc0c554932e224758870671448811def58d9c57679043ae142fd859b824f96de33ffacee2a8f17931455a0b7ff98e50e8fc8e2e124d427460a60a877e441cbc837cff803fc2e5ead135a34af7c6063a16f356b35407ca40e1e4acdf2b0050ea3cb1b7a144c814c72e81b86255b2762e031aa60d5ad39af9aeed75869476735a999e9ad0ddd87cc342f783cee2ab638a54c1fc2d575b4f5fa1811eed27d71426bbb592a5a986c384ea591e6b2f294890081b38890f4e34ffadd19eb69b313f43d8a615860963d8852a21162d10b99fdca8c895eb510608c4b7e7673866e9a5960bc9620471f0708b5c328f2da64b0631322c6d14eec3584bd3162cc3309fd4c1de9964d53b64ab6b7d1e16418e438348466f62845b75c687662fb18ff5cb0d57b0fa7dee88e14c62f629315159a393c0810c7f2867cd25de292e34879818bb1db0538491579f658e887d6598ee6d2d31cf7d78d2707bb052036f1eb6c5a34f56c0b7a263723a5438abe766601fad94a2519e3ebd84c0b4e84df4cab5402edfcf6dc92bde6d123b18da1117622bbc3f4d8edd586709bb26146b740569944591c6b67b14d92712aaca06594faa0f17ee2bd5ba41e75bc062771cdfb85f5d068a9d9cb519c2320d35fe6ff5d392c07d64adbf5cccf499a5d1de7df9df42c8e39efc19d0389b94f4b0c74a87e81adcd4231f295639c518f9b9a08793bbd7634b5c0416fc027cc612e7d96bead6f2700bbde923eb5d4326b2fcfa807512faa44012646872377c16e977fe4c32516a11ec7ac66a77659993a43a871d7a6125c7cc29e4a650872903f0e703afb0f10c04b05a9cf60765eb4a6c0cb6876247c13c9ccd10b10a18d68f16c0584b94568c687cc0cd4ee6c92e7cc661d0e2fcd32f6d6d0f39086023da07ad58baabfd465143549e2f8f0c1a998410182cd21db5c63a77dd34cb9bc2f43c8b63d11f642377af89f2e662d76772ce121b112b8244285feb47d931c845d1ebc5cb6134dfc17e8bf38574c4436364508eb1ca9dcf81e62328ea0c11c24c97f0210e0086fa32f12b8fcab7fd11d44e890bfa8dbae9cbca7f1aec51aae008b2344ebe861ab0ec222388975dd5832be4e6ed03544f1ed43245789777d595dc3015044a09587c57d3425201a0990c0807e13828e350a1b27a22aa452e05eaa1b8d0e4aefb52b8c8a169896666ab22ccc96b967738b18d6557644ab1cf0c5519056f87b8dc7fc009b0e3949b41606fab20fc3b4e67844a7d6b35c782d6c515dafd12967a9dce80a8409c3eecd55036079a4d1795cb414a737cd8be5f4baa7700b7d405e5c488bc133c50326c66478ee288ef1b7d31779684d7861cfbe625dbe690ae06428627cf907209b35abe88e09b556f9dcfb1dbcc5b3439420aade1d0c0ddbb4e60795815a647486464af4a2356690e50406c1aad249f7120e785a631637759fe115f9db290079398d73e40eefd33a0b9d2e169b731516a19a65d35e15f31b4f6761c366ea0f077c2997980beeabab4f9dfc5d0421b79e1777340460990473b9f4084923008ff4932dabfe1838eafb806b6d5b01ce51bd57fa60fb374537e83fa1f597f05c118c67f946454d8d6fab9084e12f93d93900b191f8b34b0c38b34ac9a2b4e417726da23a2f1346fb192b9309a293ef30dd2c278fb85fb1ac5d1251fec9e5eb84fe14a404930d0ea6b473bf3ffb5af1e3ddd32a0230e90fc245c30130b9303d2b96b7c4663473d88073aa8b3dcd5b21a96115042779250274ce4f9f9cc08dddbfda35326130d16bfaa88dfb9aab99cd4f8673ad69ca7cf43eddc9978d06ef16307ef1c2f61334a80c7e9877a33401daac6b89479a0a99d6444de12a9fcb58fde7c702de0682c796a57731a26ecae483b4d890bbc8850bca88aea65bcaa20ef6cb93f8c05f28549022153c488cac41ef27ce575ad1048b543d14b086ffcc2ec798168b662c9e99c917d99128da979e88697f915550d5e142b61a651b1bee501ce9327a7fb0231dc1663fd11554f4521e3f05c2efcdc3d10462e94ec1d688be57f2719e689efad090f4f00a2189c40a6d4bc8296d1b50f1d6815a7e90f1d183fc119e054a8aa82e0f18d8cb30b7efd2f9b2f2d4de2147ae004f9834837066712e4feb4deaf485fb1390b7cb3b2fa703049c391a2b4b4b9165e9d687c211edc94f1899a498c4a9d9a63e86c808bbb99fbc5a66c08ea83945434b464f482a2044d45533b20ae4a3904c82544cb9f226a16ed72c78c7df5d7b1cc8214a840bac78218768fbe0a89520357880b0208d0d9072439960c52bb06d26c7e6b8380e8f28faf38d5ea7794f83858f81c878680096b1f074c3195d9b824122f89fa981b9d826a35db6d01cd22bb29600d51ef231b4c87f43c2d683ac059cc50918a28350310cf65c891310d6f67586540104aacd341c0ed299b52ea12dced2ba63f552b914e9c1878d0d348af31d5d2d38869ee621c327ba93284bf1b1ca2bd62f614f4688576144aa53f3f756de315f563676d80eb36d22b75d233da7cf556fb58c01adeddfa0bbb263be29b4d8a295e4919af0f9567191048cb57920e06011df526bf12a64e8154a4565dee2fe8cf23db4a694972697e9da1c3b5ebfbad712e5bba2e3ca54ccc96ccdc77bd9d57ffaf8d5bfc7eca9579e8ef92f8f7d88282f3ebf30c9c0dca655643589c015369ac5cbcb25a14b19666349dda0e5bf484a777cd48d8901701056d3ae90662914073a26471d0139790d3cc6a86dc59069a92139ad874832bde631b6af2d167060cffe11f4372c27364445527ce3fa04eaecb83007681e56935ca8d9d3f6b8fb9cb206524f187849747ec117e6740937f80f91b6659b0aec951259a28373af2e4c1b76616204d9b845c97496621266071212f7fd4a9b44ccf1cca7938f98e4478289cfc8f66e626aa2b7bdbb54fa94cbdc551938419e28272600a26ef42729623c87c5363294daeff93339e7a3f15cea2e5f11aa4b904de41457812501038e36e3a00a07167ad8106a071e75a8196c65deecb6ccc3257c3b8cbdd65b79af50731b5c145c58416cb91327acd49c96a7814f8d9db8de8c644fe6cff7b3b8afef4e093f6b7dd6eb4c9de5b4ab9035e113812a6131fe857bb7ef388dc0bce9ceb93e39dfee94eb7d3ed743be14e3d77ba668f684e2802fdd3d30254888b833ad1faeff6b1eb290d3a52c8c9716745121e2fd603b7e6c60ab2869e125e0e75ee4ce1e1e24c09592f067bf3a28d881917093e6f56e365e7012960011904d6ce41cec46a8f0591e465e79d79582c6aec393d5305188da746859e3da256ad79b21ca42c08d574329db0630fa84e9cc0aee984dd359d54ddba476037cde1f69d2c88fcb9b7477c8e05d1b6400ac8fde90b30775bf7d8b7ee74de9ab1a9f64e16c429547726de657da2694eddf5504f51e10ea360048a533318d6b4661990aa322d65191064595695c99abc0781e9a71436851ba8f5d6be53ad3fe8533b6fa7aea2a708d6cff936bb3ceaf879e8e938b79e5e9442dd50e3dcad4e7f32c869f1b9ae2c367306304560a2b6602061566f05628fa69b017d79d479ed9abadb3cebb5fa5cd9e3e9ff5b73503a613552710eadad8c936855fa025c77d1aaf4c46825ae398f562189e05ab4416185c2a222ad2e960aeb118fd357a299eaae4c73842bd413d50286df6e924e1fd54ada6cf3a6a9e679172efc567afce7929a3f75d24ae4a32aad50cb0b6b54dad2eab45487a617a6d5e5716258cd2c95a5f679d4e6319b3535cf6b1af13ac3ea04a5694d7aa2ba439c569d7ba45de73cdfb9475ac8c1ab13561973510f4fd7b9bc2ae0ab73ded3badbbd59d5be3a776239c8bdc9029dce874bec0596b802fba8c5d50da3612d6a726e58935eb49ecb4148dcf19489856950f0d42cb573ce7bf83944d3b0a86cb39b2aa6c1f3526e71e53057d027845b08660766312a69080cae1a0d8f679e61b4373d9fe5b4375da3e5b3b565f144d35c9a5375aa6ec73377bc9535dd5cd91ed0dad3ea71bde9405b5a6f7a6bba3b7bbdd9f566ceca43d3dfe0defac0bd652aa4db9818d998d8991918c6bac239b0f015d67ad3d1acc7c9bca6d9d6dedc4288f210651671ce5a9f278ab3d6e789a2e989a6692ea7aaba9caad3ed762c088cd71d6f5d59b6d70372b78a57dc629f6fc53fd374b76a4e308786569308bb4179885d21ecee1304c66d1e9ad8f1670a4f3d78542d90c703c2599f680a946341a4e7ab6bab638f27e69979b76341e45e359d4c732db2c4d5a278050a0d81b4d94d7607a69576bde94b4d5ad33f68734551b45928a4e65254886d85565d9587da4a6b9d85a9934044aab590edb1479ebb851ccd189b67cf349db2a3d84f3f75f11c7ad15c16a372ce49f94d09465513d5eb118805b1f4aae9d4b6c75f50109ad143948976143a9b3061682825222242894e9d3191b9f6b6083ef7b736659cf367d76d107b14ca39e70d50144dd19ac209194f30cf9ce7170499ae9010d0501e1ac2262ad4b6137ca6bb5b20500381198827b0470f6a1e169dbda3ec43434444c3a66311447ea1a2d6c8e8e828ca584442d219eb88888040e3d1431e9a5ec482c01f659a46461b1c3bc843d38f5810f983410aec71e8a3cdc2b4259af2cbcbc4c249b7b710661d3425991939c4d166e1c67dde1456608fc03c743597a2a76b77f78ccd1ed09bf35d4e28020b5ea051903d2e2169338a4d4c34dd748b8c888e82199bee5e4d779b6d47ec00cef628040207d902e0230a2e1285d5741d45d19ac2b642057737bbd875cdb4425b81e501adb147d39d0581dd56686d9376b459a8959494b2921256327dbddd825785162c242529295db8609a4b4ba98524ac942404c4c26190360b93580e942eb047736969099f6793c69031e025f6083414ee10eb635e7e37e7ac3dffe996007f0662628f39da2cdc2b860c4decd1893db64f3807d41e872c88fcbefe897637e9f33776c163e15190890212135305676262728d98988a9898746a2e45999a46782c7c49c825c07f9a4e3ca004e857f3d0ccf2994d40eeb1b1ca04f2359b8023adc20df9279b00a2c742f5b1708f809ee799d1f33c518da6e7899ee7b077b22086804cc081ee85a0ee80de8b1e4b80f3fc0ef1da07e034c200ce87c06be7edb22ef7f608de336fd519c5a69a4b575dcf74fc3956658f2ac61c985e02ec59e717fc693a611f52753e9bf04cad31d6da4d1745ae6606a7e90404638d3106a14108fd888cf1162ffd83e1fc79cc260008bb8ec3f3994d40fb5878f401d07bfaee3766c1ab9a4ed82f3c0f88c29cb83f7fb209307a2c544d27ddaba6d3f3726c02d0df23bc8ad901e85e553111bf6a3a9d2ff893031e900938c83d760ef263a1da53d95e2fedf572bd3597cba1bd1cefcced74cee57239accbe572b95ecf6452df14ee1184f2d0fc17e01139e73b3c6cfacc3cd4a7d01c71f6c13678f8cd93172604c6d19b61569652a6177bf42d2ea01c596d320f3f64aade0afafc42b8e54258d37d90876a0766312a28692884271c5156b8bad65a6bcda2beb225c0ad109887a7b9cb4d104bb3a7bd2ce4f3f97cbd2bedce66d92ca3e5b42015f8d3193f685785db58cc09f7f027ec097dc25618e46e0f592845955e26101858bb3118d81899188516e8c3a2072385e09191fb109b197fe7a488b48b6bcbe79aad0728edc65d5dca2fafa131aa7814f53b27d7e62c3d6865e5e8b9a6cfb89c6367b95caa69b3d4c8e5524abb71d6169756f0c8c88751c5e091511136dbd82c83adcd69e363c4ba4afe5cb31715a5dd3899c4ca7175e4c3e25153539415aec254e6af18d66e8cc4485c85a98e5aa0370df3501f6923ddb640a03b5cb1cf0a724d53da6dba916005228800027d689e1e040aad6ad26f6ca6f672b2e9cd2d84a99379785e81485d0bd1aa76e7248f1f49cd74f2783d374d1ebb735fce640fb04355b4598629a5cd610d732a6732b1b90b7738c1886fd7292093e9e7ebefc7fe4e1d17f7adf003269e1ee484f904f2d668acdde6f24f0fc1032134f0c1c88b7cabc0244cd4be733d0fb591f034b3eebcfa9387e9aa3be7f8d379eece44420cd9d642c8aea96ae14e7acdb22ccbb22ccbb22ccbb22ccbb22ccbb22ccbb22ccbb22ccbb22ccbb26c199028e998a89da7834442afcd74f0774e86484a43fbc0ccbe5c2e97cb255cbbf3d26de7245bdb5a9113f95683c03c648b8880ace72d11a819590741ae6906f3b4407315b2bedb55e8373633831e825cd39380bbf5011e0095ad1090a7a77cb70e87bb35e7d379e0434f1aea7cee0946bccf8335b7aeab4f888489a7bb9ef2c0f4e2070f3ec8439d04d3c9a11c5327b119910b6122eb3cb7cd439648686626d685acab6608a031aa689a424843304848ea6bb7c544d6f5145078662676ab392b8450d33069826bb6c3377acacc4cea8ad3449c1e040e0d0dcfd379bc74c52c8820d681c00c16b01893e9f49d7bed4741e614506bad7db74240de07cd747ecb73206f7bfe33d903f03ae860dd44af739e939ae9f401cd303184f0b57fd06e07695aeddb04db40d881cf8139a9a7804c268d3bacc2ad6aed3ed761b3138c38fa7cc295e540c7c5fd4ee89878fa073d85cd7ec29e10a881420f74f24f3fcf3cd05379295c32b968735696da45cf91a54ecf5779e94556bb2a26b2a8af6e12c835f71ae4ee10ab1e72859f3ff5092e565dfcba9f8bbf87df39507d1502b3d4cef06cebdbc2b3314ca7d377ef593f32a7f2d2814ca99eb31e644e1139eb2beea09398783aeb2ccbb24565cbb242605eea296ca6994e3fbd156ed5e4399070f9a7eb183e3f5d2771070f30f104f29d755cdceb5d01e840e654d16fca8875ed466ece6462fde76a73c87a910b6432b10e74596f590762855b04faac037906f2cc1e807520e19a875ae801ee00e427d05bf70977fe6d9af99c47e4ab603a015d2093493b906b9a44ed446eced4e630e7acbb0a75ed6f6ca69eae33b5ce0aca58858e104d7fbaea66f174ac37e0154de08877b7235ed379b915f2f4b76eb6ee069949473d2adc22d23f5de7bcfc28fa6183475fb04db7d7792e013eae6ccff770174df5c39fabcf47bc2cebad7fc0aeee138ae0f3d547bc3b9eb7cef3d67bee11f69f8f7881dc232c3c520090f7fc0320ef39f69e50842258f841cf7f42118eb0b79a29f5116fcf4538c242137cf0735533a5de7a90500411823ce837e205f211af9bafcf77fba94f68be2dea29eb9aefea9a2fcf1df1eedc112fea6e17917e1676e022bd68b52adc200f732e928ca29eda2033a5befe873c4c3d4f8bfae9a6d999d5ad2214386e03d689c79ce2d6447dad6538535314064b54d91886ad2bc21e567a70527c5a33003b374d0d7e9ba669f2d622391e7e6bd77af57d7a7a9ee7e9a85063605465ecadedd52905b42a7951a1da15438c73a8f4b5445c3b958256618a24a1b57843bd306185ca221c5add3124a2f568c74901b812b9048b56263723a315ca895a74b4b654c0e04aec43446185423301698d2aa30a55589dd482dcf858638d35d6588305e041604a0d79bdbaab9bc513aeee97cec2ea142597b4267da9a8d2ea6e9d17560b46b4d65a2fad403e6a1eae2e981dd35ae4e4c418d6a4a4d65aeb0c1884ae5718e129dce08d5dbd885c54954f3f857b1dc099334e7e36c1f9a759e74fa18833ab23fe09799e290ff866d09bbe375bdef3526c6674cd6e06b42231b935bb515cdaadaac05335a66b769372297ae6741cdadd9ad7ec865d697b11f9cff5793da0e7f9d4df1aa4e6527500bacfaeb6bde9277b0afd08bc4314b56b1c22bf3dce28ea1915669c5da5b376abd93de240ff9987ed9a5d2528be35bb4436ed3e7fbb3d7af4794226385f443ac1839c6b78fcbca13567f3343178a3d49b1c9e67eaf3e803f44d3dffc62f24158af082a8e705add92de268b79a5d282ded56b3eb6669b79adda3ac769fd9257669b79a5d263aed56b30bf5d56ef5c8349db686379de94d2cbf4f73cd9bfbc80353336179a53fe220ebde34d59e879a4e476f2d364da1065eed6ef7081f5cb30ba5d6eef3b31b65a6dd6a769dd2b45bcdaed9e579ae3e0f57795318621d91872e5ef3669bcb82c05f021cf73ce19a5d2729ed56b39b04258987d1a301e4ec4e781ece6b7637faeef34e9df1b9d7c7d9cc4a6b764190faee95d0576253cfae7ea554a8e6e75d58b30bf4d36e35bb1bbb26385f049a5bb4b747fc395cb35bf4a4dd6776f739807d9a239e9743ac26383f04f04473a6dadb235ee561a6e1d205a5240b1884ae57c88044c1348347464540222727f54fe11e017decaa3f94254ddd4c324d8df3f1d9daa407f4d49b4cdab1b87531c7b8cff6c174d22cd0f2d0cc56583b5f0358b0e5a1f9db04337d6757d5b6cdc847851d98ee870df2503bd00c292277da7da618f59dce59b0739f7abaf93a4f8f54a1073b13ecd50981c3f3ad3d85bbf866978e88b15577a7eeb13c20b4d5f972bf20a1094344c022a3a32005a40aee0a2db8c91e93fc435e3fe42fb138abefdc10a698ba0af101a5a0a2e6658a7d7d30c1e858f09d2b224db27003684b44dab5818736d783d0d5eaf9aac47ce4f0a0d58191ad82d12be86093468c6580a83914a29028f85883d0b6a4f9d4315514b6f2a0f8e6a8183c1ac29b400218125650fc54f841939ca226a322203b221f6f464234bd81307c231a028970e88ac8d824f9d4430831649b40129b09494313b6d4d43f0891941682bb05f9ce15810a05fdc87590871a08d6c9f92e4d859e873908ccd1ee24dff9ce933230e8abb30548c21243ebd39debc10d11fc54e801a01b1df07d3e1f5055733a9fa3ec5157f4e97cc610427650dfb92184be6d5ba14fcfb3fd9d70a7aef356e8db24281b646cbe45db2612b47078e70301a9eb8af2805cb3479e88f38180dcf345ac6d7abe7343accfb2ecce55dc41c5c4b4a7037e2a24b4d7dfa9bf7aaf042fc8ecb117627d96e7ae6c014c30248021e1d91e2b5cc1ef3c4db2725a18d2ee356903f599aeaaa3ebef7cafbf3a6fc544e14e1dfd546b2d14a15f5bc0422a3cd3711ee2b54e6bfa6fd14f698ad35c2ec5c00d33f4e9049c22aa6adb3af939d126cfe554b56d814027689aa1985375bb9d4ecda55fced25c2ecda93a9d9a4bb5a4699a53753bdebaf2763a35970ad991a6692e979690a6384d711c8cb3a48891e672692e979a698d934ed2e454dd8ec7dbe9d45c3a254dd39caadbf1d695b7d3a9b9b4469aa63955b7e3ad2b6fa75373e9952b655ac8942672aa6ec7e3ed746a2edd93a638cda9badd4ea7e6527c264dd39caadbf1d695b7d3a9b9940c4e539c53753b1e6fa75373299a3b698ad35c2ec53a9e2c6992a638cda93a9d9a4b7190347d93e672694e5573690e3bc54aa93e7b8a647bb4cf67479154cbdf28169adfe899dfa8197a86a6a16b4ddc7ea36cb9df68db0acef8dcb86a132546dbbbd12a1031ded020b3268e899877a3492024570e2d778e4489f16e34cba8ca8b2873a6b8e082f56e74cb032d6043926895f892e2dda8f2b3bb28a17401848a1a9777a3609f6798443c23ee731f8c7562ac13639de77972719d7df61399a5ccb026794e33bf4facae734cc4151a293ac0d8a851f6eeb32a45e364cc16728e8e2aef3e9321d071b4268176d36e703a6dedb36b329ca990e76f4d95f65b636d5de6a9a88173040f932158efd66692a0d02211e7c58dcbbb75d20340d6cc5021e39aacbd5b2bf7993d2aa0b069c146f96e0dd60093e8c70413f739f70121b3a165ec14d7c1c92462dfd8cfe72266c36e54bf331599df196bccef8ca6bed9339ace5f6470254984bdc96af2ee5cf55c6cc869eba28c11c5cabbb308686b3b966530b0c87877ced282026d3944451d1fefce5b0e4803260f1017795468bd3b2b7f042a8e582569d18602c6bb33d8e33e1b9ffdd4061b7d9c7d04b3036a12b1636933c66b050d275578bc1b2731b88073a27c7246ebeddd42c44f14b7253d62087937d663a7c656122d3d7f72bc1b6781e551f3b2a704197d8ebc1b83bd73bdf9520349d6931eefc64a31bff1589807f3bf3195d56717a17553b77936957d021a087332e3733efbf2d84b131f33246825d5e0c4bd398c5b111d52be9099a97877fbc6214e7e20290b23f6e2cd42fa8461d901870515b3d7a8a7462861c18385932279aca8bc1690418584305d7f926479817f5ad74dab8146bbd9b1c7be0106ebf5d83724dbcd663d7636c926c572eb97df429f73d3c4e7ae7c8e8b90b5cf51c9f2395fd9630ed8a4cf638f52b67bcd20d6ee95cbd7632fdad2ee75ebb1475d59af1e7b949576afc8c78ec147db63c7d0a3dd3cb4c72ebcc1137bec421bede6753d76a19176f3908fbd35d2ee1ddb632f5ad3ee9dd9632f6ad3ee9df2b1177bb47b97f5d895aeb47b57f5188b5a9247db4751f7a4a61ca5a2edd48837e73cf6985377ec11351a21dbad737bec4637daad437becad8b76ebc01e3b062aedd66d3df6968476eb928f5d89acdd2adce3f55333bf839f76e971e5d3339fba0ae453b339699f76f181e4983a0617668f1d438b76abcac7aec444d57aec4a4bdaad261f3b51adcf63272edb9d637becc2b1b3c72ebcd2ee1cd8632fd2c81567b43b0765250725d6eeb44ada634fcd52333fa88eb3573aa24733d2afc77e74d5ee74ebb113b3a4578f9d3886f7fc793ad093cef374f5dcf2c50cc3a39e9332c190f0a9a7ec313d9e462826688f1d4a49bb51b0c70ee407e57aec407dda8d623d76b44a4fb1cb13adc2d143b2cb3ca5b250696f8f1daa4cbbcfe5638f0243f6d8a3ceda7d2acd9c5a8fdd29addda79d769fefe4d66e6df6d8dd1cfaebb1bb38daadb71ebbdba4751a83dd6948bb4fec4e5bedcecac7eecec15e84a6b57aec4567daa425ed3eb12729d1f35a3b50bb27e5a9acbd294ba1aebda855ededeb2d8f6678d44ff6889a53f98d4a1933e47bba6a4e99ef6904d353ec123b919576b3cbc7be59b8c7cec2b17df2544b3683eb85ec92658362e37aec506d5ae09d769fd8816fadeba52d5ad2eef38b94b44926da7d7e528976ab8f01f27bc9372d8cd000673179d434158ef082346796c8b908454480be909c30e72f30d11ca142be2a7e4dcfb9684e7f54c030daa0277de820557ba1265d219322c785451a0506ca24854eba526445063a3008f4c122e94a98170f1349574edd9b615e347e17cd766a3c33ec8a92573f0b81cc26eca6ab7b1d9416ff067aec26909981d9009e99b4b105e126c15be009e0fd0d1f3404a0a80111004f1e9c3a34659001030c46a6a9a1d41207cf9e01ee7021cab36bf001778032c5bcbec2daecabf24b00efbba7bab642c0a0270d9b8a51460f1b34f47e434648c20b189e36486570fe070d1a00c043870c30304e49718892327ac01d36f8565793363bcbd5f51f02e09eeada0a01839e346c2a461935147bbf212324e1050c4f1ba47a01d00000450fc40e4f193861d064cc3085418a89c3306a09cab36fc01d2e6cf0ec5246dc4183871c5a5cdaec40595a01d00000eea9aead1030e849c3a6625491d8fb0d1921092f6078dae0a200f040cc0003e394148728a80d4fc528dca1684c624969b3b7575800f0d0c13dd5b51502063d69d854243ef57e434648c20b189eb2ebeb9001064f46a7a926a90c1c304431410d372c69f0ec1870870b45cffe54c41d88519f1d53593d69b3ffaaac3a6480817baa6b2b040c7ad2b0e9c9a9f71b3242125ec09062607492e21005b541439198f4e4d9578091696df62011301203e3947baa6b2b040c7ad2d0a9a9f71b3242125e109a92e2d01495010ac306260dc3e212d1b30b71870b4f9efdc21077701a6a1179a8279040c4941407f754d7560818f4a4a60cbddf90119230c7212ac3060d45e2139227650fc103c52e6df6210d78804314947baa6b2b040c7a060cbddf9011527689a03668c05064220e9f969c3cbb11ee70a1c9b323e10e4a193e9800f14a9b1d08c204a03668704f756d8580410c4cbddf905176550d45a627a7a62160307b071e6469b31719c0030d45a27baa6b2b04641af67e4346c427a761d35206cffe131a02e20e4cc1cfee6183296df6a30f1b109f9cdc535d5ba1e152efa7736a5ac2d06b858ac6246df66094d1a929837baa6bbb74a1975d0a1930787675873b5c187af65e8b3b2c8510faec494d606d76a46153060c4ceea9ae1776ec9add9d9bb8039367cfb8c3d0d51577b800f4256df60a41671a2eb9a79abe2f79f61477b8a024e4a4cdeeb6424bae8989d92fb81913b32bb9272666a1d0b39fb8839267612e4269b367a1aae49a9868a2295a8dfcf97a165ca1ebba5bc1457229b841f7c835727791bbcd312277c89da05dc8dde6db6b0f727fda7d3a99e3b5f7b4b3eec65baf7dd5ced31a58ec2084152433deca53e7dbeeb611cc44cb09b7c9e6960a3746566d8c750a37eec2cac74d633c338929461bd447ad3e85e7e968c6214c31ecf5e5a12822ddeaf2f0a7a7450fafb556d4d081218c388995e6284c9587b8d894e4c5156064d39f9e0e9b42e8b724608861086c491fe8341d54cd827f7a9a5bf50c117ced28f0755643008bc610422b78114942adf67cba88758bf07acdc3f5b5d0791a1979694d4cdc4329cc9256332908cc435cf490a5b6f1b5632f4609915d688069f33a1b0285069c8a2c2f6eca0841abe395eb52802b55945084845511a3c345cb49091f584aea14c999996e41e03861134be304c6082c3a2948dc11c34126a74e901c972fcaaeb01d53ec64a1657169513b03c4ca15313c725b8c4ab4d83242040d8c4f1bae132e5ca83111454f1dac5c590c3733625e5a902a7ef876e489ab31040d9f248c1c199a5686cdc91732435a14608f1f3b2bc8901c695167454e0b87195e71ca00b91a4b0209230b53478d8f11b43a5459be989c2dc9a2a3849517126ccc117b6202caf20f960f9298a86a41830e95da0677bc689cd07203881bb8b1203194907d9dd9b242c7d4a26cbba186c5151f66ea5c19d1040905c68e2c269c6819d1a46a0912155b5472badaac4c515bc1c72a8d8bb72156865cf9b3b5a5146353252bc04d0c6bcbcd881a31261f1c27aed658e1d155652996dbb2068e103960f60819c164cc59982f6caec66890a1f25aeb2cece1f5c6ac871cac2a291d59632f54a49153c2abcf5ebf3aecb5d6c53ca59a6a3be1cd30213f7e0385d2903c39c295c48894183c58250f56098f558cc7fac6637dc283cde2a182871a1ecbcffa44d39caadbf156b607e43380071e08415595a5f59587bc7ca6ea6eedb5269045638f382d0ff35b1e62b8b13c85dfde5e7fe5298c9699b2a3897d76dd95a7f097569ec25958790a5359e5290f648dcc5306c84cd9bf94595c98ea8a4c2b4b6d65ae2ca5cc62594a97a5789e3da765b6fc96bd54dff96bc87706dbf9ce6344be33d9e77c06f49dd13ee765916f53ec739b91efecf63bf29dfb7cf6252fedd967cf39c4f05512e7ace1d6977667e4db67cf56596a8b483508e62aac9cd36ebc86e6e6816dad0d280a9f76e3b1af3323d998590f6cd96ecc95a58ceae2f26299ec6937a6faec3847115fe124c65a9dacb5db7c6b5a81081178c3af76494cbb430c5f4895bfc109640d829e1d8410b25490670feed86310abd59e3bd61472e4167a1ce9d8a3cf4fbb0a31720b3d8c54f608b492d00ab53d574851518e3db24cda608e05ba42806ea1079037a34d4ad99d2b84c82df42042d9a38e2dd1360d850c0dad5f6d5451c88409ac93d683d11522e4c6d7ca4c396729db0d7ce72c0b3d5abd7cd3c20b74237ed937863f40cfc2fe84f961fba5ab3609187460d0f310b3958193e345989cab38644c6039a184a3e2a14647074ac7c786292b94e461f1c5cd8a341e392a30b13228b0ccf071d312e4762600606af4c829f103cb8f2c6e7c54a890b9519239d9e18604727be1418a04808091518e596d112bc01c1d5d5e36ecf4f19ae3a43767cc18c5429c2b2eecfcf0b224cc065a8444012386c714397d74734e708002743933b64ce993474cd90a33ce11232d94c0d8fab1bc726ad1f065ec2097cba93937ad3e97b32247c6477d0923314c5d8a01047a95b367df0cd3b2ba4286f787a793399904539319d8c2b901972d5a8bc7dc345376a05bce7552277512ec05606e6e6e6e38eb054b33333324b2ebec6acde588ac3387dd50ece6a66be3a6f65bddd2b82b89e5810e861809e7b644332b1a8140cdb6443353e3e13e8f273c77a7db1b9e69aa5abbaf1d83407b2e87e2ac9c737aa6a9a340a0aaa2e6f2cd344d4dd3d4a88f48711a8767e2676bb567f6d9daecf8c46606e7b3b56606fad95a3383fc6cad990106619a692e97cb9d39d46c7b5b84cfeea75d2a6bf19b53c498e4e1faa2dd182c4c88159d36480f863c2dd09bacb69776a3fdc2a2dd1e1664ecb79a21471b1684ecb7faabc9e735f14b2371dbc7492412d9e3c518638c3146c34b074b5d2d30de6c7529b3d02a9c6c3116f26473830b418fa966681ff2392c1a1dafeb893cd7e292166bacb13e7750d43ee56130531d6bcdfcd13498442249a8a58eb3305632758cc4c8104c93cd7413c27d6a9a996340635b1caabbfd6082b929ccb1c714ab7d969ed254398b994cd981cab64b536d01b1da645e53d95ab10f7a2a6b994c39539978cd64ca0e446bcd80c6b6b887409d70035b55b8dbcf685aebb3a74966644903a695a53497ca1ed18caa5a5a60bc51fbada9307ba6ae838ddaefbca6638125cced771653a19071f33b6be5d8236a8524a2222ccdef4c759ecbd4ce89b536bff1da67cfda51d600233e4575175018cf4cc2261846eabc6ae3a42e04d5b37fc8797687537d9b6c40b4d66c9b6343e3a3a703816d0b06b6cb7ac1128934978f8ec8423f9ceedafd35063be3a89eaa69eaa9eb5c751172aecb7ebe92c8d77342144d3361d44c336134a7407866d246978fe320df0483c3652edf046bcb7a9d3433d060a6135e407eb5327769b7aac419bee439d3857cb117f5d40829d85780d45319dcc8b205867cf18817e317f7ac78c99244be5d7aca24036286ccca8cac826212844fd78b3d636533cc86f35263c76ed8f15617567a404f69a566c2ae7ceca1058db1173b08217cd0531aa999b08f3d76a39ec24a33532735524f99afd9a2ca53a88559882e5df0cca4ad9147baecd81d70ba168e201c61849e67e1799e193d8b9ae9ddc09e500b4510a1c89107da1760be1a887507a47034e0e8d0318644be2300093f68a6b4272481e9cf060bb3f0147a7b3a906f172277d97711157a00260c41339dc2118a7c80baf611902f1c01156236d8738a855988939a293d5da7cecb4059d9518f40765478a473d4cd3702d88184473ae191024e47dd01a7a3c2118a7c805dfb08d94f77801ee1fd9095780c2b7b6ebe63471fb08e3a2a14a1c98b88da9315235e56847e07b0ac10a369a6d4311b46c3277498d042b2e349ab35d1d8c484e1b942919a294de1f230f50cb45afc1bc31d29e0f4ec0ed01e50b1ee0839a99952df79f9c882d3c68544be996b2b2b79f4818797750f2feb3b677deabc9e9bbb3453ea0fa735dfb14f3d8369a6d477eeca600e30ef5c818302f9a2ac5b91304e9e2491c897c7ba457667bbaabb00405cdefca07339dfe9783b9d4e98d3d0f29075cdd76473c27cb601acf5d4773efbd4b39b9e1281153715627dcada58bca908a658b1b31ce25582c49b6a305ac47c41165153f6a69ed9f4d4882bb3336331962471e2e5d5c8158d977a707afe23f5338b56e7aa9ba6699aa689d903e49eedb1e918638c716641e43ea7b11efbd639e7ac5910e9e7fc88b2c0a66badf5c982403f07f7d8cff3445910e7e7d823ca023b8aa62c08fdb96da2a5391644fe9cf3d435c71e5116edea3c5779bad57577c20d823f95a9ab9f9e4a10d8abde83eca80bc9cecb790842359dcc73d56c4ee75217820a4da190537814c7df99c59845323197cbfbb498799f3b7bf4a96f6ef039a144e9448bccbbdb575531afaa612bc1caca0e9f207d5e550b9857bd027b548dbe75b3c8b78ec41e5b35789405caa35a5864f1f1a853608fa8bf0dc173a7cb94386752d2304ff4c28a903d60618c6079d329596c7cea41f698fa117bcc2dc11ee8078a077223f60894e5e47beaf7c07ccf7dd8ebf950f1bda05e3eebf57c02bdb59e8f3bdff3f5f2979ee3a49e52df9e63ac2c657e0fed159148a4899eab7aca449b8125eec41f206a60de5e1b384a966b548c69827c77fbbd3278762cb9b30409901e1e2641aeac9589238145cfcd9ef7cc1c8fa2a8b73eaa3cea45ec11056acdf4da5b38afc37a70bdce0a7bed40f6a83d4dd3d45327628fa985cf65d5f89c0fb1c7dc04f6d833c79ee5a1e55917628fecdaafab975fddf594faaeab3765a9d4572f6a230faa5f3dac0e4296d2befa04cc35b65f9d043c9efcbabab971d5af8e93abe3ab15e7c059bf36e972560687ebaa04dbbb1a695164cd09581a20ef6ec15637b709f72b46fe1ac603c6abaa07b14775c40c307ed888093344cfdabbdb47d12e8fc68051e64d0a9a2661d05ef4ca9a47fdc71ed1a2cfc1f99cfbd8634ed5a3c3ca8d071821a7d96b192bd494e420014b9adbab9d4cca6b6fa5923edd01f6a903b1c7b4c71ed72df6782e5db68f7b7a07dbf841ef5c9d50d743e621d60959b83cc42e15d7de31de31c6fd36b2704616cec8c2198d4afa18b9fc366ef96d2cfb6d341a8dcb0c2471d165cc971f5761de6d6c3206cdd5112a5c92bcdbb82487d80e2d232f4860bddb78012f67cca4ade1b2e5c6bb8d137041470b8e14b73950de6d2c22c3890a275a64a0bddb6866cacc14cef27b6a39c536b59c82fbec53c8a9a9a9a9a9a9a92a5255a4b0fc9662c7a4d83129764c4a6a899514155697949494d4528749d411f799039bc581cde2c06671e0c08103599e62bd582f0e1c5e7020fbcd018b8396df1cc64694b0268d913125d09480f66e0e40f0c8b142430d161d5adecd21f9e78819202a98a8197b3707a5dad9128ec98f353defe6c0b688625b44adf91dc526a3dc3e47c17df6286454cef23b2a63f91d95c77e474545452db9fa1dc516d596b4b241c7445b111639de1d55658466c72d88893b3aaabc3b2a096587891f59ac7419797754d61bb82e7660c0c9e2f2eea82d1c65695bb29c68b2e6dd51ca3d6e2eb660787d817b77d41aa835506bbfa1d8a0d8a0d8a0a0a0a0faf4813afbec504b74405df90d65e5375495df50634df894f0e1b5b5e6c9bba17a79a8f09825c58455bd1b6a859e3b39943ad0e859f36ea82c5646520b8796650b2aef86da22010f9c1336555890bc1b4a7925c7878f3d2d5ca8bd1baa6c43d90635bf37986d30db60b601eeb36f18b241ecf7062abf3784fddeb061c3862587222adac0b42cfd1c79f786aa2d6dacd4d49accc91af2ee0d490b98b8b3a5c6c8599e1fefde9055420c956411146a3cdebd616b6dc5d50e1b5e7e2cdfbd61ce1d175556dec0d1f1ee0d2b1093b80289fbac61ddd2b06e6958b73468d0a0812c4fad5feb97060d256898f25b03d86f0d527e6b18ebd204cd94235762c478b7861e5baa9c8179e9ea7ab786e408f22c51c459ab62f6e3dd1ab23a50c34d5a1b347edadadead616bc50db12329809c71f26e0dca3765255879c930637bb7063f453fc5b1dfc515595c91c515592c168b64796abd5aaf8ac527c528bf8b507e17bf7e17c730d861832d39c3099f16ef2e569d58a621221686c657dabb8bc9f6091f367ee4b03823e6ddc5acfca38e161a5866d0e4797771ab4617136e41d04ca4787771c411382e4b5270ad2821cbbb8b604c2298b8cf4434221a118d482412dbda88679f9db8840af1c96fa293dfc426bf89639fc17cc9621624cc1334ef2656ed21e1860a2a1540acde4d4c52f95263e72ccc16ac77132ff0756487d51c2079dcbc9b3800146f66bcbec43da1f36ee29b316fccd87a542961c7bb89459e8a3c59f9fdc4eb7ae2753df1ba9e9e9e9ec8f2144f8c27f6f4b464ec49f9fb89c9efa725bf9fc6dcde28c981c3c81be5bb9faa3858d3838e551bdc9bd7bb9f922576f850210547189f773fbd6c4c7839e1a1054894773f1dc08513a4da9d196adedefdb4858d46f28d9d253262bcfb8967c324f26cc47d06fbedc4cbe1d4f6bc1c4e6f9fdda9cbd06f272aac2e2727a7a5139b1f93e827ee73130fd9c44336f1904d4d6d9a94fc6e6af2faddd4d4d44445d56645cf9f363ed8bcbba9ca03578b1779500069d3f6eea62490b41245b2909133e6dd4d4a3b4a963d5a78f940f2eea63526714ddce70c3bb60c3bb60c3bb60c1996bc6548f23b4309bf3320f99d2143860c567c6cb418c246824bf2dd19d8d8f073450c450d14efce00e58a4e9a145f5e5829f2ee0c59187c2d6133068a98225befceb09580b715253801334386cbbb332885cc6833c3f2058c8a7767002bc350860100bf31986130c3608601eeb36360420203060c183060c0a0c4242a89fbcca46452322999e03e3b13932453d76fa623bf998cfc666262625a5ac1b9d3c4879d3f6aef66328183c5902f5a65cc20dfcd5425cccd1909aa2246e8bc9b8958b6359694422287897733996e7c669c25bdf0f06af16ea6186e5f5fe2caa290e07a3713d84ec770a76398c1efe12e6bb8cb1aeeb286c3ab21d570381c0e97bb1326717722eef3d2ae6a6957b5b4ab5a5aaab154e4f7d2d2d2d2d2d2920232c2e2e0a9837687eddd4b5560b26eb40842840cc5bb9792714c629cb8cf17dc2eb85d70bb00f7d92f30f17181ebf70522bf2f90f0fbc2d867bf6076e142da85b55f2691209532c628bfde7da1ca4c0d1424eeb0513363e7dd17925bb4e2d468ab8387cfbb2f643db92ae3032b2bc9d6bb2f6c6da06dc50e356282fc81f3ee0bba38ca3a7161ec8cdd78f705304a6094a67e2ba129a129a129c17d7625264294a8b0ba9494949496504c2294b8cf49604960496049495e9f9386fc4e4adafa9d949494b4a461c2cbc48e323375de9d54b5470a1b3e38565ccd797752f2f3579d396a5daa78517977d286137ad66c59058181e2dd49402c00b1c0e1b7852d0b5b16b62cc07d760b4cc02c08f96d21c86f0b407e5bb060c1c252049858193a606af448bedb4255da18db962d30c844b1f16e0bc927634adc108223c78a3cefb69005656d0c4d97d80eb2775bd8fa123882d0f852e3c5bb2d2811804589999610e51c23efb600a66b21d4b51046fd16ea92425d52a84b0ae13ebbb04ca8f55b88d525140a85cb11379ea4a9f19ab2a68b89770be1f82023b2162606981cef1626ef98c43b719f5d3817ce8573e13ebbcb04cdfdf1dbc572b38fdfaeebbacbb2b2a49698382a59deed5691019b9104a245898977bb491467fa2ca9111763cfbb5d655b92ac2536a0cc38f16e174ccdd57cc36feff3ea5a6e7b75cddf3ebb3359e354585dee6859ca7c4ffbece6f6a5b3a94a2aa84a2aec0aaab282aaaca02a2b54a850812c4fa966aa59850a4ceef07e57e8f1bb42d6ef0a3c7e5760c01a267046ba945e24bcbb8201a49c81b1870c0d3a6cde5d818b9693b63437616aefae904565060d9a8e1f634ade5d814415ade5913d26d2b4bcbbc20f2b355c34d0fc20a27c7705b50592da026923a949a424929a4442424222cb53aad6463afbec4848a0df483b7e23e9f88d94e337d298903a29e4d4a87273e2dd4855cb352a4e9c6023c7eddd48c93b61ca12fa20a77c7937d288ae3137d494fd21f3d5f56ea4173fc82cd9b1670c9b9b77238da0ec08133e6152ca28df8db4c624ae89fb4c818d021b05360a142850e893eb43e1ecb3534052c0f19bc28ddf146cfca6306707d8d90aaa1443b4de4da12a831a2c303360a0b921f26e0a2d701343e6cb189b3c5dde4d418d989a373b5c288152f66e0a28dabca1b193a74b0e907753d8000b09144468ac0951f36e0a504c2294b8cfc11c58300716cc810583c1e0d95930a8c2e87710eb77b0c6ef208ddfc1b6b2146368725e14117b77b0ea8e8a651cd59430b57707934f040a181c3a30eef87977300b0f17b41650ca66c8787770cbacc2a64a18395fec907877500964b365c9e3c90d3159de1dcccd3089b919719f8f72584739aca31cd6d1d1d111599eca71e5b88e8e943fd8df4779c6efa37cf5fb28cbf87de4228a9d256653e67821f1eea3ad3870662eb8cc4c59f2eea364708c891c36c4800511f3eea3ac0d48d4dec468f225c7eadd475b31d6d67e5475a18d79f791f22a0755123f6124c8de7d14c724c689fb6ce466e466e4666464b4f6d98dc8f2548e2a476564d46514e3b7118cdf46c9df466344ceb0d4d24c904979b75195084e2489f3670e9a182cef364a6270828d4d58094b5cb8bddb284beb0d0aae3c7fa85ebcdbe845580f1e59d4d4497bb791f2023ec698114266c713b2771b9599c4b2b8cf45a9599119dc17a515ed22b6b6cf5e94965654a42c7af1bbc8c5efa216bf8bc66898a5415251c951e2f6eea22a0274894341024e9d296ade5d941ca1420c16166971b06e58bdbb28eb4fda893a5ec4bc64bdbba8c893b82376e6bce500f2ee22351751e2ca6c4499e2f5eea21488494c81c47d066e01b7805b402010f8f5050482a1bf8156bf812c7e0357fc068ea500881c18666d905adcde0dacfabca38433234fc8aec87937d00cdbf1828d96b7256bf6bc1b78c5c55595236b26a4c47837300e181e267aead0a991e6dd40250b2a3d5ec9333ba0bc1be8c724fa89fb4c9422895224518a2422222222cb53e9557a4544a4ac42a4e23751d56fa214bf89d2deeae820fb91d6f56ea2aa2a1245ce3c9901e4c8bb89929fd3b8e08c51e58081e7dd44599fa9d6ecb03376660a8c77136d7d7e83a6051925494ebc9b48f919c98c25666d64baf8bc9bc88d497413f779086d1b6afbb6a1b7cf3e543684e2f7d089df4354bf87c63efb90d9d050dad01a9d2524d27461f3c647cfbb87f6121d2548f8919c23e3dd433f2a2cb4f21c712b8184770f1129e2a2ac4e1a9a9f2aef1e5a81111b7d84e8d9b365f5ee213e6ec6789918f34325ebdd436028940928940945bf27a0601350b00928d88409132690e529140d459b30417936219bf83d2197f83d2193f83de16843c5933c6247aacade3d21051a3f4e58b85993e5cabb27246fa4d811a60a991695774f48337685c5978c2b3d72bc7b029aac2142e82c8b5c9d79f70405a81053640a9bb0af32ef9e30c324ce88fb2c846209a15842289690909010599e42b9b6d0d967174296f9ec4219f95b28eeb7d09fdf4264525ec0b833a6c34f9b770b554d51f9402ba3452dabccbb859247154ed2b4e29ce13af16e21245a676f5cb0b8a0b9f16ea1addd1d3841eac8b9520295770b29bdec8003054a1c1d2cef1602434f04f9f913719f83d0aaa01d04f7d983907e7e07f5f91dc4e777505050d052a9f63b882da8adcbb28a932b3f926d38de1d84c1922b78e2b2dc19e3e4dd4120e8326749458d09d7977707b939f2e6cd969c2b5669de1df40124708e587ded90f2f6ee202c33e47cede0f1860bdabb83cc984433719f7fe7f2f73b97bfdfef4796a7ceb7f3edf753d2f9edf9fdd3f3fb07f7fb7767ed2b081f2f5d5633defd83000940622cd798ddc8f1eedf08ac272cceb8bde1a952f3ee5f1293225f56c07003a27cf70f03a41de59fad2fb418effe69e15ab2a6c6441c1a5ceffe2931894ae23efb943ea54fe9f3f97c64643e9f728f2fcf6f1f9edfbe3bbf7d639fbdaaacd1da4182489e77fb4e40a70d123a7a528cf178b72f4bd89cb73e507c3c897bb72f8b429a3831b6a0d07245e4ddbead26557ece44994135e6dd3e73cada1b1a6edcd4f048f36edfd9c2249e2de23eb767b26dcf64dbb62d599e3ab54eadb65dd1befd6eedfc6eebfc6ec7248085042467705c7e66bcbbad223249d145d985230bd5bbdb640867c4c879f3c20a9533ef6ed98021b388294de0f9f1ee76ab03111c493a5fc2aa687977bbe683471adb9eb8175adedd6a200db4813450dbf7017afbec40318080808080808096406c6526b12cee73cfac67d633ebf57a3d36b6ded967ef69f5e8fceeb9fdeecdf9dd1bfb1431b8c8f4b9ba62f5eede8b124aa873c5cacd32f9ee9e0d2c7e9428911104489b77f7b2726481b186e32d0856d8bb7bc82a1438e2c250f9f3e2dd3de51e374d76a801c391f6ee1e10930824ee33bbc56eb15b2c9bc6ae7d76962c4fe9afcc845d7fb1679f9de562e5fc66e3fc66db7eb3639f7dd850d2d002e3aaebdd6cd5672e2c2bbce8c9c1f6e3dd6cb20538d8c68018f10292e2dd6cd6671663b2a898b4483e79379b876a4814384f867089f16e9645d7971a619adcf0827c37ab5b9844dd22eed7bdb2fdbac27df6b5499215ceeff5cdefd5cdef755d572956c4906c0943333bf1eeb52a4a143145be909044acc4bbd7e4e7ae3d389225ecc191e4dd6bd602667cc5b982a4aa079e77af5bca3c5957e62851d2c4eaddabf28d15b3b12835c40079f7aac724ea89fbec9bd7a74f66c2be796ddf87f7f6d9794e7854583c1e8fb7e4b199318966e23eef96bbe56eb9db89eda8d87eef76bbdd6e99254689064d4e172037debdcb425ad1852d2d8db20bdcbb775b4a4ca292b8d76d1d9b5297953a9d4e474676f6d975481d559bdf3a2c36bf756b7eebc61eecca8a85962b2ee0bc5b579585c8d7da95126469dead4b7e56c1a50d45dc902c2fdead03c18d961f3564e840e2e7dd3a07f020434369620f9a12deadf32165c68a226162dcd0f16e1d58d66112b38eb8cf2adc6757c976bf552aacaecfaababc93f37327ce7fe7fac0e5e0727070b9b4b5cf9e23cb53d92a3361dfb9b3b2b42cf563969aa566709f3d5d4bcfcc8024dfe04923e184c8bb735b9fddd69475b4c84811f2ee5cd6daef5c57eef73b970bf3699f74edd334287150503f60e846e1fe09d90e3f35486cc5887b775aa5b45c3384c4993044c4de9da2658921c18a4c942f26de9dda3112864c8f903327c2bc3bdd42011991396b685a56dade9d2a392831a3081824da9a14ef4ec1d4fc4ec7d2fc4ed53e24deaddba07ceb370df7a79fc8377f9e547f9e55565739fef4334bc89fa7d69fe7d6c9f5a7d79f5f7ffa09d6d6e6cf53eccf73ec24fbf33c43fbd34daf4742724c027b446253f2c1603018247a30e8c6ad0fae411c4107214b69f0a04f208814743318f4a08d3b5780488141624a7087561a9b3a24d4e013448283e406a9cdef28b62d8fe42792ab6e863cd29bab477ae3e79190e04c7924c743d82392f17c0d1aa6b065d96afb2db4fc1d7ca29f44221b374f6c03e6896c823cd1b110f6480cc21e8339c0fccf7fbf9f2735652929ff79511b61fc6fe887f5fbf9047e1d7ebfdfef47bca2f0fbfd7e3f0dfefb59f8fd7ebfdfeff7fbe968f316dc826320ecd182060d1a346870acc51e3550d021e42938fec11e29a8442291e8c3e93bac7d07c73dd8630724242424242424242424c759ec11492a87899772cc833d4a05836a7e5489d5592ea7a40583fac6078316f852a3b3c44a8c1d3f6f30ee838e77b0c7a00ef6f8c3a1e37d3e6de37deef3249f476529f37d6e14f23e9f0fcbe72064a9a0fb7c02bed3e7731244f1215de5d07a9fe32d31ef0bf2f91c8f6529a2fb1c93f9cede9786d462461a1f6ae0b0a8c1c5c99012249655d0d4787d5f57f88c91b9d146c2279547cd4419931c5dbcf87c3e9f9b3e2776792291e840a2e31cec918884e4aa124730180c0683c16030e8f8067b0cea1f8e1bfffb85e1a8f13fc736d8e30f8b3dfaa0b04e3c94e31aec114a7572a0933b7952767272d5c9e1de69acf83fc734d8e3cfe79bf23ec733d8a3ef8a3d3a199bdc04fb26c732d8639315af7727bcebbaeb49ae17dd2b45de756bb80e42964272d727e0fe5cd775d7c49c1324e08ed80013e39a91f18227c518d79531ee94d9ff7eaeb678fee738067bfc019190683c92ab4856b03c926318ec11290ca829eaf4b94ae27dde52799f037de8b3827cdfd7fb1c27d9a3cf189cf141578362ca0f3a7ec11e83ee823dba67c17ff2334b5198a1e59f1cc8e39f8cfc93e316ecf1c9fdeadddde7ee6faebe941183c682cccdeb674dac70a9696952e7dded8c1fef6e9a799f9f3ec756ecd1c7823d3a13993acfe478057b64dac244c6a3eeeea817b5f1cea3a89b2084e084ba4982268f9a6f5549ac271475bca5e3d11daa63a0a86333241475bc4483280c649672a97c28ea190b7542d1a0a3e80f454df8e8a166cb09103938de1047c0f859834384eb883966c259a1c2468c093c6d666f18182dd420b7d0507b37567ee8b1024cab4509384f5053ec4916da924a5814258aa2fefbfd7ebfdfeff7fbfd7ebfdfefe758057bfc0575f2b7f183242ce3a04902c5cb8d77b71fe483e64d588f2c7d9ed479f7f0836dde2469d322636122187cf1c1a0922d4be5972548dcdede60d071157b0c8251a1221646c517e68bf2f97c667adee7f3f9be50bdcfe7f30f675adee71da4789f7b80e57d8e53b047df97d589286464b8ba5e9cda05524a191638deb69ef4795d3785bceb18057b74d747427215c523597924c727d82352fbc4a5ea9f1c53b1c7270d5bc4dec9b109f6e8b4e5eadd7109f6e82a09f68866b3a2d1187ce36e7ae3efa8371a036034ba0a43cc1bbd05a678a37b0cb237faf0c51bbd28a3cc1bdd680cbbba7aa39b35dee818c91e8d4918df7adbe2dbb66d97ad1b5ba7164eeb206429a3b73e015fdb3a09dad6aa75bd757cd5b66d180c15dff65a6ca255dfd6b1599632bf0db6adabadb7ad970e2d566a98f028d31239d2a556030957d5bad9626552cd850ffa08f6180c4b6abdc075731bdf75dddcc3775dd7755dd747608faecfe7f3f97c3e9fcfe7f3f97c3e9f8bc01e7d409a0509da50014911c50c1e2f3f2d3ec080bda11225460594ac9db9e9dacae2c668d42bde68c4aa9325e52a47923e715ea3d15dc01e8d577eaffffbb9b983ffe3d2f43fffb9fae77f3f07baa8f23ff718ff0b7ba116d6e2c8ffbc054516b0c776c9cd37bfe42b608f4b450716dd87c522162f5f041655148bc5b65874b5380114784ca0c1c17903576c83c22b0f15b32865452c2dbe8825ebdbd655c01e5b742ccca39e02f688a2803d164d37f795d2f0782517c11e95ca64f934adfa344d5d4fd334e4a7bd34459aa6c1343563e6d35eaa51a4be2b639a2eb57cbaa63f4fd3629a3a5eaaf874283df11999a9d236dd3999010b295db880a541f9f3a64a4f82f001e2c64b9e3da90a301746c274ac9dbd306a7ca9922cd345459a162547d61b332ac4de6dc22d2551335206450f3457efc6c854c8981a39697cc01832e7dd98edd31e40be14f101c625f66eecf669dbfe0e367d1bb52cf36debaa1a8e6fd5c87cebdefab0f562eb27608f6daa4da0e80944dd51ff80ba3a6529e65137c1162af33ba8562c9e49f24507a279f14577346fbe6806cf17bd04ecb138443595e9e709d80917407ce8e19282c9132d5e574dc448f95d79439219533623cccdef67e27fee74fe77268c89305a5c606f30de9f1938ff7312b0c7dffa46a319ab37ba59c21b7d04ecd148a44bb0f94b1a8ef7b9ea4b73f13e17017bf495f1f2410f017b0caa6e1921ef7a08f6e882803da619cb5fb8e6cf0b7db347a19a14afaabac4abaafa73555513f62aab6a12aaea211455d54950e55527155725b3547b954305e3e7d5a0aa91aacf5515557117128964a16ae00758da18166a6c8479d52c7073b2d4b021117353a5a498f11137e5480e1f6a9f3a3666bcb519a35255d5e8a50459d698376486a84cd610794282102e3578de8d95afa2c124b1280f554c2aa87966c6e4513f5157d58079d4dbaf471dd8c5cfa3ee5ec01ef5e1dad5a35efc62e551379e553dea1fd6d01ef50e743cea1eac593eea1f608f28da8aa7f87c6644dee753c3e57dee01f6e8d3e0f71a80df42c19d14f6bb29ea530f6a983f9f7afbf5a903c5f0f9d41dedeb531f92f874ca182a9f92a1fad4c9a7de01f69802c5e130338282c61b197ca8acb87144c58934ed081d2e5068d9914c9695b5bf6fff7ceb4ee4db13a059a1d24406922f4bde764a181bdf3a07d8635b5473f545df007b2cfed258f99f6b803dfed40cb04715832aaec7c031c01e318803e3b5f6f35a1f19e720739fdf208410d453dcb84c3ebf31c6147ce31c386bab4e98a2df986bcfe3319d6a241279423b5edea981f520331592ef6c95b1f6e41caf9168cfca2cd5baf6fc55c17716cb6625f259667bedd92dc36529f3dd77eea3abe4bcd6567a2b4bf95cbbe6f2adbd5ebba95d83e9d1a1c5e07e6bb4d7ae97594ae8daf59a05dfba4dbbbd760d87e2b5ee93e7c43ab3f6a9756ebdf6b36b8f9c138ff6d32c4b29b9f6f3ec82ef334dfbc9c6270fd2efb3edce6f145995a596ac86bed12b14ebb5a35960a8d6db6f54f9da51b02cc5e4da51310cbe5132d4ecb5a3687e7ebc46d3ecfc46dd5e3b0a9722b394f9197ca754dad36492f4aa8e6e72eda95776f29d7e694fc7ac52b2d7e9f2b5a76c59eae7dad3b627dfe95b0ee9274745477b2e2bb795a5cc27face7175bdf69c126927f7e5f63b67f6da736859aae8da73691a7ce7d6726caf3de74602c8ebdcdb9cdf6ad56b57932ad606df6a8eacd7ae6e916023faad72c9f9ad82bd76752c4b41b976952ccab77aa6a2bd76756902c76b752d8e761d525795a5cce7e05b67957ced3a2c5d8eb6dfbaaed7ae53662929d7aefb9af2ad13d38dbd769d19d5db6bdd199cdf3ab6d7ae73cb5246d7ae7bc3c0b7aecfeb1db2cac584df3babd75bb4efb6765d19f8de79bdde295ffb0e0c8599d73bb137bf7768af7db7cc521d5cfb6ecd83ef5ddbceedb5efe0525c11fabdebe3e6372f89950500df3cadadd7ceebb2f19ae7f59aed374f3b0f4d03dfbcb4d75a8f0e145b42f8608933e5d54cb2a2f01823c184d5bb5b2a624ab47113848e1822ef36d9f0b0a3f6440a999b8d779ba6951b6060b8f8c95286e6dd269c9196da8d3148382a66bc1b1b60085b9a263c944ab377e3ae2b35155d6968e0e053e2dd784b959f382c207ae2eebc1bb3bdae1164481f27684594bc792ba3e5a54e6aacd75e3bb0b419437691f26e3df63a4d91316bc01c5111e6dddaec75559f3229967825f3bcfb44be0ef183aa4b8c136b5b62e833509a6c9992a4488b779f76e2fab859bb82c46dcebb4fb0d75b636eb4889b530289779f6e4954e0d972d6674e5c9c779f70232852c2d24e16b82247de8d6ea526be662873a0d5817b37daa5a48dcd2c4d151a1e6ade8d2e5f6310a38d0e9d332c9466de8db2bd365156c4080b257edabc3bc57a6d9665cbca1f2e593cde9d66bd4ecd5ea768af1370434d091f52ae3c81f3ee5cd56b1b3de2f498c5e9d1f3ee5c7284b2b7125925a81841f1ee1cd86b23ca3657702ce15202f9aaf598c1640dc98d3f64de9d837bed315abc91f9596ba37ab78a7c4d45cc941676dabc797bb7daf5fa4b169d1f79be94bd5b55565892c46a471435b6b477ab6cafe39ac41992a78e8cab33ef56dd5e57b9b2634e96e59e34efd665bdce52a5c41a38246dcedeaddb7a0d825615b03eca12c8b4bd5b87f6fa05132743ae9cb8f0f26eddf2b59b7b977ced3b2c13e4593333a35366cfbb77634f6061aaaa2455acc1f2ee9d590466e821010d173c70babc9b877cad33e1e6678d95951927efe655bdae9396640b7945c69977f394af8dd4a8a3a44a0e153eefe681a528f31af899a534780dfc02ec5103f504008a390f000f813d0240c5e03d68bd07b7007bf4a076e8d0a143188a1fdfc12bc01e3b38007eaf9f0195cfc0d52e9fc109179f8153803d66d056c000033f317015cc63e02d0661542c1e039f007bc420c8d8e6b7d1c9c6123025b4b84832e3dded1bcbc8a8a9c127489994770fdf28268f1c3034780881d2e5ddc5371ad9bcd11dce1b159034c1a2889d2c6e5d5ee39f37ba04d8a371cacf2c05f55353ca9ff208b0c7a9b67d2929074ab92a156682eba51c02ec516a7d0e7e72082b01e639c43d0707813d72689f3ecac947f903d863940a05050555e4a1dc01ec110ae937849148f11bbc01ec7183aa4183da6bd0a0210c19e335f807ec5103b0582c168bc562b1587406b0c72291482412e3823cd117c01e894f4ffef4f4f487ec9f5c01ecf109a8fbadf96dfc5f89226d6be43829f1eb61768686972d4ac83079f7f07f6a304ef4c0fab106e7ddc5fffdd6fef787caff866ed069c3c26dc7932aefef8f94ffb907ecf1e7e434f54e4edec679a725efe409608f4ec0a6a6a6263f48bec911c01e9bc83e431f319fc10fc01e33a84b8fc1c6637003b0470c5b4e097ce06459e531f3eef69998d43c93ab7a4a045a9890807351a4edc4cbd467c6337901d823930f877cbefc70cf0f9d00ec71d82e2d2d2d2df900d8e3d2853d3cfe8277c01e2fa84a4a4a4a7b68bc9283608f4aaee593f43cf924e7803d2661b120e62dc09dbd853c7ede821fd9a305a1502814fa06ec51e80abd1b7cd775d5f5d675a09bc7c6bb2e00f6e8bacfe7f3f97c3e9fcfe72f7bf4b97b78776ff16c79c713e3dd9f3d3ab0ca57b8a3e42bf807f65841cdb569da766c992306093b597ade3dfcd64d3d05a5e6070c1f6b5aa4a1b56d7bc7c8b76deb01608fed9ae5b7d02321b96ac7ed91bc7d2b7b243b341ec935608f48c025f58caa0e5f5555b5ce9c5755d501c01e55a92e177e277d4a86ce904fbd4de9a0f9b44e971b974fdd037b4c87bbacf636c17283c4923353bec0a8e4c811850b1bda5b5619143c546eac968fba57298195294d4e98585375e445dda23cea1dd8236aeef529380557cd3c852973924fc133608f145ab4dfeb07a37e0b7dd0cfa0ab6f1f9443f6c138383ee818b0c760f08fdaeefc911bd9e3d1147bd46c46635c6fe452ecd1482d72605151515191ab45ed988e2f82f3e886479d037b44b5d65a7b147bd4ce1e8b8c402b0ff40dec11a80a3d5146f3444ee44944445ed4533889442255bc44aeeaa90e596eea2c6ba85953f112695952d1d2244b169b77b74fe466094fe463ba54f8ddf4dbc3339530529143c59d15565efbd0c66b2fb250bef60f562a5ebb06f6a8c7720e839ef96da235800d9989342b4cb8d29ca199f32626264f1d36ef36c73420068f0c2b3442dcd478b76996663355781ef50f55708fe2205c32b8d8a0eae1f6a2556e8f7a913da24b227b24f2fdd0d0d0d0d0d0508d203f94e6513f517f628f288d32afdd893d6a6f628f431afc841b577e8267608f136c50bd90900385843c290b09b9d1869917f22024e4131012bab1e4858484841c5f09090909090909e5f900bccf31b0479f0e4551144551398fa2a833b1471400dfba99f5ad0fd9639b9a73f6f08bafb576bff13acc868cd7bea43279d52fb04755556321c30d182e1a7dc4741a148fad297cc27ce401f2a6585e9fba127b4c3d893d0aa57d909b5c1fe416d863d08e329f734f6a2aea9c1baf7cae9723937310f494fae63c8436977312707d2e2897c7e454cfe57cb99ce3ad2c654e29d3f1b91c5acef11826fbe5d03e77a4cc8a0c102625743979737ac020011245d9a5c49bf35c87ad3827bc547cd92acb8d808b131a62d6cc70e1c8958591c94203cf94322cefc65d9f9b42d6450915321849726e8efdcfcf9f0bd9a61da854289b89a37916635231c86600800273170030281c0e8a666194045914e30714001666a65a60561b08c469940331638c21861103000000000046461b0220aaed11c9557d1adeab008fc3230558a7a4264c52638115267ff94379473bd865f49c0f6976ac4eb694c7408077ac2ec38d8356af4b5499c5de89d6e9885e320458ea9010ca51b79f439e442d39881526d8a8a45b03a202b927ed25934db68358e7b6e585775113b7a76d40d5a1ebbd88111c226fcea58435b183911804b450ec404054ef77ac76b53a4ae27f442b933d95f57dec830328128d993ae997b1314926fab47417b3507d7941211642c8cdb21f87bdf9b825cbb8d7030d9551d956f9723c5e10c2969073f383f12ae64200d226271a138c435dc02d9c4cd0f58732071c13f1b3486e776291599d71d545cc021a7e2584d24ca3c60b814dd268a084801abc2fbac9136db0c66676a67038c37421bc72053566811a31a0c1afd21871b1fcdb985ffefdd7204ecd821f9b534fbda8d79d422e46e1a4c88e942ec7cc974878f7f2f98500269d50b023d0f471aadd00935249d27ed3260acef98362f68058fae17160241621c5baa04c6199f3218e33ba2b76ad5e5374a4b1766b36040201d0f2cc989f27b4579b07d7f70e08010ce04a7845bc11180c4a0eb1f131deed03c90fee1198d2f3179ab88163aa0502982330135c7204be17f3fc73e30394bb7562a7da9aa1fde770bc2b45e2e73bd056c0e080b804761b01940240bbffbe21788006ab2a9f74504dffbfe46c4239b2585f5760161c4ae71d9ec637c4bf36694a08ac7ee8e9129abe06c21514538f3b82bb4f3878992ae1e24e5c471db1cc2edf9c993bdce6488cac5b92918fef94ca7986855e8323e2e4c183d6f676a928df1b813d35f6729e9ae5a1fe1a36edde2bd6cbcebf5b0bd95469b83c09d92a5c8634baf15a8ebcbb1d2342c331249e70f8584f20ea459dd84125e83d21265e0e468d0f514e79e9e056d763947b1fa21c75609350a017785c7f5389ccecd3110bb637d8e7bb69576411233177644b859a97419e645d867ebf13d1cc4bfc54d16c81f635aa276a09572946de08af912fb76af9c940f086077caf0096e5e026acdb9c50fd9504c292d811bffe6fdb45ccbc96db4d584cce9c7b17331a792d297438c044636742a7723e7cfeb0591b473c6db8a630ac5ff95ccf364bcadb78528603d46e59d542a077e0dbf483cd69b1e8261502c845107e55e8d5c872a4b551ba8589698b805aba57c1bd5008bd13f78f43fd432f5cafb021859489057593183cd8d9e8bc3c306f0c35662312925cb0ceb44305072b0be7ac8db6b261154268a3338ac2662e2d95446770126e436777347571a710fe1987afd6d89deb436e7d04a186b23b2fd24637436431b491b515d90a816e33eb04d64be007243bc209ff012f1a0bb9830e4360fd1be2e311ba47463335a5730382adb522dc8df38ac4dde8962e6e1ab7859eb203fea233956dac77d8df2a09aa7ab184fff3554f611d69cdaa98012a1c26b13202bb627a11d8152ed204606e13a2d48f2e9d7b3eaa2da05b4b0a5ca0f5a5ee15f46a36c8fd5d4a2e6988511f160d50b31d0881766125f4e550594e2a0e4ad7a0e66b49db982bed80300058caff381c6086b7bce60135508037d567ca87b30154de23b55ec714cccaa4602af577fe3905800bddbb74208888fc0c0569114bc51977bc9b60c6c992457bad9c280a6d4ebc8ba14453d97c66b6629d0ba917cce8f045e5aa65f109502a91f1a4a9cf68d656ca9528c5b4448f7d17793c9d078c21fa21567175ef0f8cdcc8f1acf7cc2547488be12b70ff645d9f5321e4b32865a05169497150b73eba763a0df89c1e5aac82948bb741a360c97bd5a1034a39fc276fdc25c0807edcec56b32f4f81c8ec82c5a3812115de8d3387269abcf81be9cddf3fbd2475a43f3025625244fafd85d577ed18b6d2cb42249bd7965de60efe7ad4c77ec85a50079b6cb1cb7e5644154b28e6964ae5fc1f4c618cb332823bbe6bbccfab6f800fb50f867752b8324b72e819a8d4e675025c864faace2a0b0adc45b0c697df0b3ab6d28ce1faf1dc9805e476d2f9acd5431e4141fc5a5905a58c1649b35803b8984aa9070c697d638acd865a97610275ac57d1c4c5feb602fd085dd2e9cc57d1e32cff3c5c015ca958563c7fc3c53dda808bf0bdbc2ed47865e6d8463abc392c40b62cff8b738dc978f58161b028ce172b06bd24cd40901ac80ec4212fedd031fb1a80f010891a27cb30349ec1641ddf04067a62e06ab3a21d78cc364f1bc2a69c3a9f009968949c30fa6ac06714919fac86014f260bf1fdd28e3e6d09f981f9b3a61b3f12f9c34b905d9434237860a857d35ba985f6fb6bafff05b19899e34192aa3202b4cac692add4c488f14c1cf26cc9c58480f2a4dbf125e2537e8c98b05ee6b8a37881ba4d2d7976e10185211e7596c0ea87df736364fd38b8e89d7945859a91ecef9ead1981b50b359d743db1e51072104071302aab3aa6a6f9530b609dea9541cb1e53fe07bf42a7650e654eda5f11db55e891bb580e31e891e469f3a182633c06954a60aa8cefd85aff8156aee406e4137077f38509c96f608337e2a45d5084c589a839eb881f5b277ecca7ee9986c6085b357a23d07cff492702c8c9d1685cb8fb7d50b7d152199b548da2fd3475dbbce8d2140c73d41cd66816933a75f65664d21f6086f031de4fe5a70632c18fc6fcc5a7f9e09e600b072a6598c1d9ac22707323389a8d6b2143cd4cd00114de82904c18c9cea36bd52daee9a09a3858c16a545199f3e3bfc5392b87cd9ee1aa4587ec81d6dd240985068a0c6b9b5318ccd2a8316d3b08eb2dd0f85cb2b9f9510aa9b0cba8ba4566ff9a9e8950e9ee1d5b223f849979205dcb7c32dc469d8fa8737538574be9a648d1e60e41f7e77b5a02d28f160b97eec5c623d1d009cfdc82a91e8a8f66ef336d9c2fca84887a4b8d790d44b4343e52070f4279f82c48e84fea5ddc1618bf8d4971e189ac45d8cc6aa09a3b6410ed0fd54378ec14182b179fc4070048c9d3a6c022dc3d99bdceec148b83421353f40e909320b250fa00c567728d507700e0510b4daa1f325ce96f44bf77d1f332a721db8f51b358a5e51c7fd623ef3ce1ff89711dcf034902fe948c8eab202091f596c6444d77926e6b302cdddfd223097d9ea83502faf1a863de7c075efead3092db949d104ee633b5fab0021d05152e8161a0a5f40ffd47b66c8d70d2944a4e1867e3128c9d58f0a027750394a251c0d273a27d279bef8db0d9c9408967126a00774c0cb5bf905edc88f2998018108b688a52a722e37542717ec26a4a5f17fa3bba68f2a3e8b6be86bf8b58599f38b54e8e453a99c7b7b062d29f424b27893819c047294b196b3d4842c06cf6617b1498b48eeadefa58049f2d0430155bf22542f9aeeaa3e4ce8995d185c8b46b2a5693963c664ea98127d79aea5bf72e4e7779f1583442ef9bd3b901cac0e6965f8e0e35d02817503af74137581e4df7e157f6cfaff6f86c72712a9ddc8fc0e47d06e9bc85119ca963a37863e0882c32a7c7c5b29b54516f5c8447b490f8c2419ca13e6f329f742f853b802e0b7a2d3d3e6882be60a6fc161fcce9fde0fad4c581296805d69f7ada3e08a2f4fdf3fd855e5718dd08935d25b39e871ef5a7a097e1e6c6d2cbd1a2f9bf0dde18ac71a39c5494a6e86dece599fb5be2386205edf527ca7806811416fa19165aeeed14ff569f7a75613373ad34f9f45aa935d24ffddf39f86d92c608be41df6ee3b695db40b3579d88bd5c741eb4779bf7da2ea60ba450d8cc931a162ca5e9a87aa587254a5fc4b392252130c14407366209c65d0918879e086574abf0777dc04a0bdad365966a0011d87acc4280b3e57b9aef02f8f835f074018e9045882a7192139920764a58e5003a11fac16af287c1f0d6d0e38eba3885034fe1e676c57be1f71e8742c077630c098177cf66185c4226068a96e8b64bef8f07d7f81adf4d115f4deafb710060481e2a397ee0fb2a82649da69a820dd154b9fb182f0dcd4d9b811e53191317328bbfc50c0ea0162cd272743221be3009bfef7e76e588c6f58053d3443b55c375e391a280af63c3694044ae91fcc6fd20b5637361c6b5fc759cc2112d3ca118fdae049c8dfea75bd7103c705618d868ad1d04ad6607d1740b36580b2063d9820ed0651ddfc309714cc16b6c1cdfdae7f72063b767477863ce0a931fcfd236309f708fe7759034135c58fb7c0163f411dec380c506dd9652c181b65a0e42c5665c10cf8b4f3d50051714cccc46d637d514c3038ebfb4e543af61dff41c743b12e1144d1865664a8f89d5807e2c7bef4c64bf3aea8b6143f56e793ae6f11a0e1b472c3c278443f731402d65585d450953490a97878c1c02cc4e3afbea16b5d89ddcf0e63b5209a6086dc20624149317c3020f8f2f475c1d6b4e8d88fa681af6a8940acbf73d91d85b70d189e7abf1a5c7821c3407a2a5d4da5b4f9815a425eceda52d23510d081ee826fad14bd639e1b77a0f45658a8e640f4879a38bcce71c5fc664cb837d9c4c2ebdba479ef90ffbdd33a71de2ccb35387de76fa586f8b8a6379ed86dc116724274e026ac292d83e8e47e9773bd4b7e42619c900f5659c98922d041a7247c90732ba2724715abcbd80695964cab26b681ea91516df9fc823e227917f9b95c403ab15906a25284e1471c7adee8920fbfe06770c14f093733f3f8206d3983cc885724c084d514ff38453403120d18b9ef25a9207f29bc9727451f9bec1a838949eb2058f722f643416833515200b96a2bf7b87f57cec5e2c1ef4e10783a90e5886c0b9a2ab4404621d1e0c5e424ca488d705304a5d11692ff10f39936a71aba390aa7474e1e7f47c67744eb4356f65696a96cfa14a97e45e846309e9f0450c7004d5e6b2bb9b3c59dd3bbf5df08288818c0f9c55dd05063702e71e03c9d60964ae1820558d020830a664a1aa99271ec6d46e70dfa9781399d14aaa1c6d772752f65723e216a2e605e523f9473f3a92f304daa1e2f10e78e91457f167bdeb2be15c386f9dd3930bbc4e923850ce3d49c577a0e7b8f5c0831416607d93ed1c0d3c4a417e010de9e08823bead6943e2b4bb311c983cd737857044665f2dbf38389aff0f713665244b43c82be774bbc450a5da28f82601772378f0ca922d4480112e61d998dece437efcb38d9328610183c688410abea5e4dccc574db467c6741f62fcd8d37d0da2d6482c80c94a029d2822c5232535906511182b566e46a7874c8a55443f46e09dc36e1d59cd3cc35f61278907f430eb3fb2097a31306c637a5805495abc4f4c71245a368b3def961e1c70304a88530635e83bf276ae2024b5b3268b52c826a5710fe16f087ac69b3d44cccc8ab98595943156a7a86c8188ca5e5716c47b8d42c2868aa0103e2e10755b06450ec7e56e79ea9e11dc25c078df8a8b071f92faa292590012329d13794daf84249a2b553a462ea27ed1311122f8cd977d19943e92d9956c64ace13e67891b830042542a50e981f9ba6d3ad6862d514609bf21e3e5445f343c688b40b15e001d1588a7b3a92dc8348d196b5b7d79f561123bd130279dc9ab4fcb3493e597ad7528919e915a59a840eb600cf0818eb7d36ae55f9066f015f027e20799f081573ba2a8532740d11a7c6ad0bddb0dfc22b90db046a7173ba223a01cff8ee832f828e938eeb6629be25684ce8ef0fe82c0535bba2380c3c1f3736d41cc56b14df69cb04cd97737d17eb813d2f32dd5bdd57942aa90436e54a9191c25d8d27bbabcd6d2ec873ffbf67bf16b79d3e3f57d026fc1e0fb2850a14355585cfe67899b3991466bb68c1fc723c10b205f50323dfa86132e5955cbb183b54c582d2117d98551a05c3bf91feedf69411f9bad53c348c51b5fc0c7896b4d76701a0356b910b3958f6867896dec68a5483de32abeaf4630b43c6b05f49caed7bb4b308fb72399905aa478ce1a1338dc1e6e6fb80d12f1386d9904f4c39ad36686006aa25a01617edcb598794ac15dbaea2f12eb809560c1ba4bb382db68ae2bea91cc28283771c6c81ed4f86a42d9c7549466f571de7f2c6faea2b702608ad8308175ac2ed4de24e3893e612c8566cdb4fb99423206dfd303f83212df12df15004d1a3ac04be53fc92c96f592942754092ea2e9576a0c349c1c1481e341b16ad2bc4525ef4029dcf9b444a3e9283ba4ea0ca4196e3b396afd4391ecf221286c56fa6da07f6411c48dcd6ca9b37d9d2c5c59ec6502de29cc5a661806f4cfe0dfb4197b21b7ddada80de407ad6106181c61734136e75de6bc718eadd425685eb22d77234e1f5914ee5acac9d607f0325726818d2289e2c87bc1a368993af2c0b3909349c8cb96d5ba613cfaf9adab44551a4671a0d94ca426f539525e3314e58cc570bf7fbd3ae20c29a10fb1fa1ab3e52a5a2e475c1e3ac558ddca62ca40689f1941ee64a02478a17387ec47e8ea1c249a84f4f727fbc9534a7903e213e25cc489f138c49d6edff297116dc5d5927c9bf3e7f3b1fee180b5062762ee25fac08b3cb2a88e7e0ec36de7674e757f4f33eef5d38a6490ef61c354e86a143566cb41b09ccaebba22304690d50cf0d2b2dc943d750ac86e631209e005943c16907ca4ced6a6ff3c5e481e8a2e97b3177c48160b89989615f325abf4f1387b0de0cbbc0225d76eaa1d70d2b2a8a175592300fb0856101eed9673f7c0cc5d771613970ef203620f4b82577292419673e897ea31cbab1c721825bef367fe80490208c6c0931359f10ce9b6daa3e68ead12946262396bda0414c453e71621438d2c120b92427038f60aa74a53fa2e0bba59efabad83345ac8516cae55c2aeceb4c2aea7afadc86356eb5dda22c1e7970d6097bbe3feba034d934e985eb9a48b2109fab92b314d978e4c68f6d08796479f40821152dcf9bb7c7f5ad0db8c81dfbed7509eb04136c3c62914696a12e311c592572564f856e7629445a3cd773c91f857855d52688a3045ec7e53c2d0c4ecf4a4c4f8ccb9024017860e3e8e15f8934b4a93fa8c4a96bf081ca684d285f254ff4fb3bb337162d26aa5fbd7372fa01a0839a3d052332f15e7412c56651d9e91b897c124af94bb8803319d71e3bbcd711051de0ba36249ce892b2ac30caf7248076e5c67f18cc976942149fa8f7151196229be224b2a8ff32988271855dcb8a892c89b694428980de8a6c95609268ec4c03d6a10bd46943fc940433bdd67886219d1578aa9587e7c1d3710558cf252d8900b923dca7fc215c1ba006c3a98eddc5ba46240b374a1b6cf8a7129cddda826707388fea06e0642652f399325f49f0f4613e4c7e032c28e55e7c339386ad82c2abaaa007c2b19ba7b8516db3011e5340fca5666c7add659d2910af25349c51dbd6d322f1faa3be927b1057df640527af940c4362fd46d6d2452eaea02dce8e89ee925657234565acdbc236e032be56012b422204930f184d1f3c78251bed095b66014b1d40c56efe8816c78a3dc185ce5ddae8490c1093c9c4e6596d7f0fcd03045c430479047c1b849409e7677195f84d1041ccb821f7472db7edcb0509e1b6a1a7e138239c69230add20bd19f2f2b2069dcf8a07ea63bd85265b391b819d9bd55c4eb4aad4d587395802c6f5a5ad6e2a044500aadf6d59908a302aef926d9e0bce28c10f1c52087359d79fbe6cfd4cda070eff0e796650616f5f886a3160243b5bb0655ccf21e241e6aae9234f024f8f0923852672880b096ba468d194fc68853ab6b9157b74b5b4c528612b9cb8365d25fb5008ca963c8c1a09d953777e07d46b870456b6c8e3960933e75882de6485547024dadcfbe6ad0b9ec94f9df90b59e72276ec2a89576a2b822ef65d2a1e08a6e32ebe0a4c6b6082e0cd0014b1de9ac9e7b83874903b41436f33f15e31f592c0cd53e05ca364b35443975d8ef55ed84d04df5469823a81dc2bf64b681ca50df1f543c109ee2ed5d89c43d03067e2b949c802820a3e4f41e9c597f341a7d1c723085c0b144b84e1089000e2d2405c430421bfaaae8338f5ab49a7be6829df9d0405478b484bc7f916b0914342c2d039ed86690e2d55316e921e88f97723a582a0ec809be9bf47627e1af6723da952eb513afa1a27c4c44576aa12e50293a12255a72d54037797d87a4c6c0f008ea349e5034207ba4d9a0d79d243894eb4f30866ee8a39fee985a686e0db3a7327a91eba588bbc45767d59bbda81f8a8738d16ac024142fae4ea6c9612fe0b1ac0de951e19799d42478ac8dcc787abe05854a43320e2b2bb67fefd6553f33c87712322d7d2276efc313da859d134766790d6d832c398ff764d6db617a28d07e1b58e8a4f8b073cf331aa87e2cb01aaee4e4bef5f47791e08f0b118735059a7f453f2fb9679eb6bdf1d7e380a4f4d67bd44f20a368257beacb6a8e420aa2c01f080b133e124680ed7885a0ff0619b140a7a98320e4e4b5b8f62f292441eec41a6e9a41940fe57c518444a14e131162879c9ba27e250adf8c81c61c2a4bb888cc1f6e1e0363e91e035d761bd104b4ac64860663a4654938cfd53fb8513c5270393eca75b08d2b985e9a6bd06b71d9433dc9b5f531d099356026c05ab20a84fb7b6fc4d84d8a45df89f62a3198d70d8ac9d0452b2d91df5ba7b68d18ae429f3dba08f3aa8d00f8563578fe01380b00f08366a601c24cce7aa5dca2b49829a21b65cce9156789e41dd735ef99f4fa1f149eefb32f9e94c9987aac5fe442c708fc0c152e56d8a51f56cc44009aad093a36dcb44b092fb98b68a52bd9fc778d821003cad0c3f55770495b78aa2ca7f605cf5b77c39f1d12f1e36033445c00c081dfabba904a9cceaa3616249cc5876ebe660eab27f1f51864018d8ba41f3411a324c168c739848ad475a4637fb5464fac78f8496bb0a9402bf7ec4c5342312bd9922b2550df3826b7648cb480cbe21ea763fc3d48bdc3ce9963c308e037d8e4660dd95433e94ffb5b017a974aa246f217afe19f241b7f001ee7692d8e1e99b648cb8c7c3ed9a7106094c03db9298217213883bad9928f1eaa7d58e44d6df4e06bd521f1db4324df63c33c7ab8a8ac00add8d3ab1940594189f142391e51d0cf169324e000e3c93ec20dfa637f135dc63967f44268ad5655a37ff9ad9811b4373ed81c4899ff09f8319209caf2be0d10b36c8403e5305bd3d6aca075ea511f3ac2d68c2d91693ae81a12586e9e3b5075ea5f5c9c6b8917459f6703c774fc7620165776d7708505d46e1577cec9a6935963895b7637907fdb5f1b29abd952803e59ae9cd3761d402f759492f425df8d28d0db700016b99f22284ac81b3b3234ed0fd2d626501e7bf552e11f71bd43e0e38964c4c0801cc7616cc4c1a093c49bcda61bf7713a25be6bc17c299b22fb12b8a28c5c85b6b9cc88cf605921252e9c815c85a47ccee784679573b245da23c0f9e982ad417aed7f023d8c05251988f239d44b108598df12022cd8de8efb8dd824731697e0af87fd8fc3d2c9c3252526cc41230d9a70583c630d02fcf239109c795c02728525d4ab6d63339d605877f4990e1d657a6a9ca9fa62bc17d6421f59b1b8f2842f00aa7e99ba608a8b02e7c7710f9d2823b25a7a5d5c7e3eb0defdddaf87ebdb8d2f9fe01239041eb43ab99491020a0fcb60da7ac902c7cb58e43b8ed6075c07c1f2281ccd6bd074c2b733ffb73678d45b779b94adb93d7f7750e28cfbd20abe5e8d71691369367e72f4718b04353698582d1d612a091d3fa53da3f12a24aca13312dbe46e1612cdeff3d51d3ef48549dc425655e22a5a3bbf4b1c843ecaa6277c8e648ec2cf88726a0f98bd293e0c6add597bcd79aee9cf57a9758fe39d8727bb3ba4d807d091a25c0ae47584f3ab48e1c05157d0712f8b424e23d5067910371dd29c683b10e948e9c776f1e1dc0e0518a91001a30a0c80d5895492360786b138bbd2bc0169a9aa884989837cc8e6fc4ccc946851405def2851a4dc11fd7fdcf455086ea84a97ebe0dd467f260ecad51da8b5de3f149a7b5defe8aaa1822553dc25bd85d754c3017b04e2ec06687511a0fccf4242ad872e1d2078545ff97a78ae5440bdf36c0eb481f11cfc843f51d30d664f69727ef530e246e036a224e7277c54d32b2f7b01e61b74f24deaff65437d12fe3c7ce57241c1a6e458f0effb72d7a697363b256831fc2ee0aa11dca8b0b6d6e08d2cce5c44766d8cb1890a7174d82b1e2dcd9ac2d31df0e0d928b6f062ae2e693ed2a6ba8f20210304fb615fa5b12de22951a5e6b94ff2521760b85f31a65d863689720572f26b94fc2f4d78a88567df6eb6e0bec6b76d72ca35660bbe17dfe2502cacf6358b5debea86cc2f4c5aa8d650d3ef919d8ec24557e1b65cede2c4798d08552238dae89bc7cb494f08b7121d7dac0ab159ed6605a433f9d176084f9c364bef6960086b9c647e121485381e9feb4c77636059e4e07e835ee0a25d9610225a4b5c403575207408e4c65cc6ea6eb43dd319c76a80e7d639a3aed75a596cd49b9ea4d3e85e85ecb115876601f5503c48eed6937fd63e2d4b0f1c2a7762911e238cb96d2c5624bed1c78a3ac4d313df2ee46f9ce4bf0954851e4e41f193531b1e67937131b0c0a7daf115dcb5c1cf9de10af1edbe905afd94dc63c31beeaaa00796dee01393f8f0b977f3a8e83fd8e2131a705d5c6d53ca8542605b93221e291ab46dd3fed7c7e500ee4651aad014965b0127176dcf6471e08cea59f0dfc72465a28fdce6f8112f8632c20a7b7ef6f28ded4a0fb458649e90b8852414b0341de418011a289d345425c5b4da2f77ea5bf2cc2395f242edca700b212550ac5051c429876ccd5a8834b1ccbc6b50ca2774b33b063a6df9eb7e48149626c4edaf85b8474012f4903a6ce4a0379004f256fab66decc9637e52dc53512b5831f382751fccb0f4016771753cfe8a14b0609821016369acd263a9ae3f05dc3ac447b65cc1f914dc0d8bb724deab16666b6c6b329c956de5c4ab6bc4d0c646ad24b2c0f4d43d399c59529c88c8673cc77286558ff2e3371c37a2c5251f7a592bf350fa1a3d05a71baa86df1275a22718567fd05683199635719980e44d27932a3cd255cb535cf45ab36d9e09e24d3a22e2fb946097d91f052dc6e717346372ff0fa7cb590ac1a0d8c45bc1de6731c4110b1be8ca7e52b99f38271b99da0074f6607ee9ef9010fd2a60be785f595e38e59f0d88722543c2a410819b5168c96dd9785c11906f518b253c600a112447d436b4a82de12d7e2dd9fee34647955e482d664b7366d76e692c97aff602eaa50bea40c75b5d3e9eb93f7570f0d75112872cca092b46852185c73a8f3f121b56b6d1042bc67ff06ea6bb9c874bbf725ab6de1580863f05201ab48a0055f7616596423e66cb4319d323334fa6b56352090fd1dff001c45939a280a875fe65f45c0d0c4b2b0e099f4b6bc73d1791c161b7de5fa9c8cede97c837b9caeb318d404a0fbc8606e0d29fdb9b7819dac810aeea4317feb87066561d526812a05f59162871818b1af1db96453a22cdb7786e82dc98627fa0dbe41137abf012ee78436fa3d9a478aa00577c71a719380a13262d4e9d177d5efdb8bb952abf55346068a791bfe7793a839c08ff879ade277085d31db1db1b2cf93d72c35447c91318e110fb0d2d030ee339d60dc7f8107100aa40e47ccb8fe33160cc71ef38802e6c1e2c7984b41ded5ba4bb49523acc7ec3dba1a2956e5a3425fc4670d0007f6f25abd680592326089adfe0dc4f8a56a2a294dd519e4a17a68f141125944f0a041610236d81120cf81e47a5e3398c64096230b49ef19fd8d894d7065a68ed1855224cf540f5e3689955686db3d507cebe413554212dabbfd0517d4a6920605708a0b483af84261c7436388f63553c3f261c73ff6d1400f318d3203dda378b01ecd9b3c367eab7dc75da75342305af508a0665de0e1ff420859252bf9dbd6cd8177e6ffdd7864e215e3de7ef847890c1186be03161d8c63cfb02e9da888a6032bfffb14adce3bcc4cdcc632a22522cde4d98704008347c1e6750a829199212aea8b78689ec803e9291528302b708305fb3d790d9d852c8cc5e25a114e9c7e4a48224a6b0506a3ec08233fda0969ac9e64f75ed9495a50ef71e92c0104db383a12bb6fc3bc8b6c5275081c664c96eb83e33a112e9c09c7a972b747790449bf8e9494a910e56e6653422637dd339aee8b01114d8f9083f7f4b63a0552ff5998b0b4d40176a411326421a21c7a807095dd1dca12b1cb2ff44cd45bdc67266eb2782e0d26768eec38cb4d303cad38ff48e388e0ee3cf7a0fbd43efd03bf4827a7f6c9934f0cc92c9cc9769e6cb24f36792f933cddc7f66eeff676c9a919b6c11cc8550f2a21d4895e61df66a3ba1515a9b6e620d44a50dbb44ae20739ff075ecf4434a12806ad338e006176d273c550a66688cd096c21a6cac494b52f0a37a7e47c99ec5b76d7be04ac9a2dd0b2a897376f750305706815a08bbf31c11cb5f8afbcf72898a93e1ed2bbcec2962d5e6fa22258bde9aa4238ddc4562983ec8cbe2ab7a50339d8184d09137ce67076e913f45a31124b10357113095b7c460a5a1575837e348ef9362ab5e5bc83fd09764f2017f33da2f1d17d9e8e58dc669d09b3ebeca276e97502ae6f085bedab9306ea2ecd4cb0627552ff71d8c86bc30d04d618ea47e665cb8c4d0677a0d8c07ae96c9eba995e84ebdcd57f90440b2a4b4c54729b050874b6236c8c290df153b8790e1150f8cb938eb885de75e8a4c3423882e5a87df0a024b35abeeefe0e0aac5e8bacc29f5dcdea8f99c959c6e55a7ff2492a352313e5dc314debd485441c2219c3398cb27d53c78c9db093b0e36a21de32cd2115c17709bed8ce9b14b1ecb8684cf29533bbf901775ba5062a811a81cc054392590bf3bc6ef06f56794f9a46ad035084706e3db436e9a4319b11ff3ce3e3a0de45fb45c62b6169c89a2b711a79c5b3a6c0b8ee02ccdd5484df38ee090712f8390a033b5c622ecfa1f5e89dea9b40b03f4508bd37f8fd9e0486b3bc6c0dc4609cb3f17130d56767612190f9dc5ad740da851012212d08613f2239380dad0d55e458944a3ea4b5e161a245f71d83cd5f223d382535dff81644a11c117da4e6916953fec4ccc8c292294e93b51ba166412075fb6293df585c753c9f4fae18510c1041c0155614e5cca6ea673e364c2b4671fe8b3749be23e6ec339f9b579198b8c9e348b8caf8020686676803a0f73252ba23e14db12473d53976623953ae5c8dd18aa89d1614d8a5d39b9f4ef0c9b5e1896e04a58d25df73f5f25619dd0dff3d69cbb4b83647bde837f5195514b9dd42486028ac8807cd005e9cb1e4ad9f2498317a2acbf14e63563ff4d31521e6a2fa7c0b3836f2fd60624e6162954124523a3e9a97bf856df747079e00595fa88f14679a16462c960a1684c1a25634064347ba7aeb7f2156ebc0b4f4e9c425548c28c6e853ab0363fe23042d6f540637f2f17ac90fbd675efd5c09a926490f909499d45ebf4fbe817f53e63c638d1527cf04142c442e1d5dd5c5e0cb9b77274739b9827fa6520b438693ff2fc6a441f64821bb1ed444ace94a932ebe81e0e63897d2d2f91969280263ea207847af03adf85121c4075de74d60866ae467d9cb194b8307ebb20c1bbaf0b62950f40ce8070f375d62832f16f01c709ef8d29bfc3f8efee9affc1b9feb4a4ef323ac05b2771fddeb61f3d40d264453b64c5e27d4679f0100a8b0a38d0819f27c471a03d819abe7d2f0d86523d644b858b1b61eba899ab6506ce2678a4cae056ef3c7adbdfa72bc39a671dd63ed376468b63ab2653388d19794b6928bc4ce7c55cb3503d67c26b0b06593705d86254eac1a8ce892c8a56363a71ed4137d823230a13e64e13e015753f80d64de27f8717648e7742e9c046f426c1e477778c6d40e691eb70bc88eeb7168e44123f74ca238791eae40dc3ccd88691146d0864e57241847d4f5967936aae19202c752594886d2c501f43ab92a0336cf6e3a1cb2b72c381c650c12fcb65a5ea899bc0d2a7b1c3b41465a57908a4f06071d0677499a55e59f0a66b3ca0e37cf13136a9afc6c1805cba610ff10bf1e50b29c9386b4b11a5bb8e92260cd2b1defec783893e7fa59af4d325c1523dad75b4193461c500afff8238c60d76d52e93a6e34d94341e41b7fbd1c84628dde500c969e682ce428c99c3d4ac674375eee73e780eda124ee6b1f246a0ab6f1456f5ae075ca7de0a27717d3ff46b35a3c8b922bb866d79fbb0424796044b2586800f5d2bec6b3e7ab859eb46881cb6574470798c0a1dc616247644b597f14a1c3fb68a20755e57689360a3ae732742e48789d8cab27fea48c99c0c565b655c3d7e92be7bda3c12fdf1af302bd20cf779de2fe369399e47ef46aa89dd98902d0ce6282c585e7e81557218371abaeb038c3d8c841131acea719a819498155cd740795642c4a33fdeca6d3dcf4220639846eca18a214d2e31a5c6a34f5130529d19b1481481ca323c68e8f8b3eea3b05487f25065da5cdd84d8e873320db517a35eaa3337ec53bbfa3a7802360d653714e3fa721dc97595b7de1dc97feebf2c02e842b776e2ea9581b72076f87add3671e00f2bd6af8c348c2b5be1dd64a4de0b23ebc7c494636cd49148fde0cbc0ec11db419166ec43fd903fb8f3a578ba0941886ad9ad7eabdc4a92d63d1aea547d02d9796c44649f0b5fbb9758483fb4c2f043b12446cfc065c1f43071455c7e6d434aa6578a4f2ca618b096aa2f86f5d3f068336bcd5f8ce8651e28c89dbe147f0b1c1fb0faa003fe873734a027ee86cf873b5375245b9f42ee790279ad0bc7e152d0848fd3669bafdedee9c3f2a5cdb838aa0c357ba787dda554ec8e20342cd4fa19608a4864b2e949c3aab9ab0ad617d71bef6c5554ccfc03b38cb69a2bf7d605d95c46a4558fe6510d44db28a8cca0f5fc6f21f4099b5d38ed84935a74cf009b3c49a35bc91247cfed0c9cc4648f7d29658900fb36ef846fecd46f178bb95a64834e6d84f7a5785cf741263c13a016ebf49afbc90d0dd586643590c795190e2212707c785fa417144dbc9fa510174845d1c5eff259fca49f7eb7ac3bb774ee8357155dc35f067ae31feb51fda0f6363ef2cd922d016addceb1185d4bc138e92dc99a2f25a9f2cc04e990096a15317655c0cb7b4d8a167ccabd598e243f306d66161fd4baed11d47031cd6b4634342c9bca1150953ce702dadd083f115e809f84a6fba02c8ada3683493909a79b4209da91bb12cb441aaf14c366f8e2f6628b81b1b4e44a824311a7b87722323c723be5d874e13ba47a70e5568dabba48c81f4e67e3b4290e9461b809e9c4cb980f484757ddf454efa86a97d96f379967911e6a904115266c28da27fd471afc2941ecbf440f89975ed38eeb81a43f6874d52b63973a19f8dc0709e13468c819bc46c7ae45906c49dc4a8cbfad2a663395a0f71abd4c3a289c239d1f4643c7c28d6b34be4df8df18aa7d63b358c931328a4e338bbaa99434ea05d29db25853a69c5b1160b9c03ffebfa484290312d747c932ed129f6b39e83b9661015c742266ebfa61fab639cc4ddcc80ba8eec34db93c07d99e58388ac81036970324c22e31e20fc2dedbee00a7d7ca00b7ce7105482e01eeea6ec60ff3ce5883f1311871254f3a91304eb5ffb621363b9ac55af773cb59e14ef54ec0ee82927ba1df5c242cd7453012b03a9b60b915e36ec319ec83f65caea46d7001156c6c7f86eeb271653544c43658bdbdcae623c02faf8c788a1051b7ee0c30a3e21ee2cd1465be31d491c68d8a30a6f62196347be4acb356d8ecae73967136c1b258d275a01f954508ad58c5cd741a6b1c896c1334468e7dbcfaef757c3ea314a81c818026a084d1a2b76b9f5e2346f3ff11911788f83c71b98e59db85c3c3b62da6742d72f13fc4f9c1339ec745540f16cea6416a2ab45f37ceb971a607f6e22f87a18c1479f2063d1cd8c63ab7c1e0bf98e68b59356f42c5c4b4ba4bc41722cee69a4c66756f4f2df6c915c6c24c50472385d13515660c3eee88fb162039eec0445741c423ac616b0364fb942374ee7b866fa7edc4ad31b91a25e51e0e8b08dd36f456e807a9dd7ca0ca3f23398f14b4e979eecbec72d73ecbe3fc5157abb1c3ab514ed4b8ecd4b708e62bd4020028976ef425fbabdd8d2bee63b36b319d4437753a952b684e0f129c09acb1238e9ccfcfa2acb575d4be3d78bfda28c25d408a0deef4bd3491ef476c1a6a70b4cdb1ef5f941c3e6301ec95243eee3122c0e5b9007d86d503cdf4e693b2555e9b6e40d1b3cbd3f0291b402a482fcfb0a8a2d421f213b3e5e436bedce40cb1afed6cebde59e30803c84d35a08f1c70b3817d147de168089c0b8bc02184271d990c580f781c0a8f41580462272a13dbc623435f9eec665a7814502c0809bf0ab29f9607af6be7cecb9496506ef3101064e46b0c527f06c4d86583732b0649523b172ab2069f6e696bfc3de24b0ab2627eb67aae9d8f4462653dc943974154f7fc646c836efcefd9413a7d655195930de3604fab02a2eca1bfc45c9a6e48c97b8b8de289525c2326e8c3d0a483e5b15487f2da2e2b99f62fa752f7b6ad3e4bf3bb4e63346a2b4d4e6ac773a15352b803d9196f72332a1ea9cd4a98b4b26ecc56ec998b3a9b1a79f79ed1cb9a4e2ac6f780b41e6cabe870dc0ace0ef71358078a58dafe7fd8e851159de221c0ff02fcd6f65b85c10345c18d30970478d9d55faab1bdd569d1855077d7e94a7f37550ebe3fac23afbcb63177455bb56f57022dc148d2607ab64ee116bd45857f17ca94d08bfa27572e0433aa8704d99813ce07bb681f02ec350d5c254d01ec989d9f095dd7f8821a245421c82448738a7f10aa5d374d1a081c1d13f48686212aae66a767fb83165f278258ac6782813eb18980138ca3f0e2fe3bfd43b997c21bde9369bd53ac8dcad7c18498a9d7c024ad5d760c59d164a3d07a6df33df891b12b24d1d52716ffe3909057a7e734a62e1326268c38d96a4b162d16e9078b4298376f13819471d421b8ea9283ae96923966db1ca226e0353d4f4100ef9eb907a65f8ded0a353de0c157b105e248c26dd818e2196e05192cfb309ea58e7a8098333f93ca9eeb392543cd215f8c12a49619461d83b4e29c9194dc41a8e47622d045b67bb7d5d5d5cbf425cfd2d55c03fe607470321c19ddd21c11b0783bc38316c0f7bc11fd6f7307b671d04cf2b5a3f517079d28c6eff84742439c629f4f42e575cfb7a267fc2f9d16ad903029205aa06ee2fcce0f768a2017cede35e944416351e2e9e14850510cb473a1ba1619015404daccdce24a3404bdd2441ac8f5e1c0b8c1846bdecd682356531b72ea24a6c20430f28301060834d79ce098f1a150e5ae2c28c7f819db1db5c1e555f6c67fa7da3922e45c10b1b1d52ec700be3a4fc6503ec9d2b96814281ca4c4c79dbb88910613f324593d7f365447885a51687e2462ba4dac516d646d188d0936ae12cb8453c176228d239bdf152c223ca54a87231aec3e7311114d7bb0ebd6f501fb61afa1e52fa1d322312112f1b904b1d732bbaf77b8137d95953dbbf4e844e8e1d05b1cd34a297557a48692a855236edfd75eae9ef9bd8af414c1eb1d2e6f79aebaa6ac7144728eeb8f84a4deab89f4e76a21c52e29a972d9d9ea7852891570facd3fc33bf0ff28fdde5f61d50788c2b1e429e95b3efa2cbe1ea98f2ff692d82fb299bc08cfbe1ef2301da15f3d241a156945c676a8a71bf1436c0b090d143717b627186934b995e6ffdf6db86937a79a5a5557eb5ebb455e9930aa2e76b8050814803d349df5734957249448f12109110a88958a5e4dc5677525ec0340ee75e7a210c4d9a277efa725f07f6b70c0a0323354849193f85dfa39f930e3947395ea7fa042a3f97050730181d00631a753d3b26d73a4dc066f0d78ff9a2701ba36cffd7edd3cc50b80f3ec558fbb1dd2811320d384425b12ede53ad42a6b11d3bfc33d1ab589e65a34cf365626df5c5083158b802980305f731266b5292ac8989f06be43910f1a40978e2e0b28996e39be5ac602d55bf0c12f754e346881e680b63e528f31e836ba016af1e23e482b849c3c39993a9b039961bc675483184b5525e3f0f10d3cedf7e3cbc09b2fc26500d9050061597f73ca553711667ec9df5c4abb519c5605e0d896061743e0d6c0b9eceb8cf52fa833e35a9d52363425e30eaad62f5a81e1bebfffb8bc648e144302a60d4553a81124e7e003b8f63d0430a8472c86279d8eea8fe9b80fb00567e153ad6e8a6b623b85218340e8033802e766a87b53d0a52001133d4350eeb91d3041efc1d4dab7f09427605545f68bb48238783da2a7c0ed752cea01f94d558b47dc51010215a158c934b95359ec1da07c18ea002c8fad2903562202a82a78ac6579d9f54007b9fb82d6161753b4a70ce162a1142b0238385ae957f6bc0482f9a72efac0b7268b82e6f2c7aa019a847c3c5e21d3656a5e82853c420f7ec58b14f3f7f254650b30721da0f4c70b58674fad814a98a089ba7372f575ee77cacb618e1795a19bdf537a8ffd6ccc047da690167f841161890ae8a44232d767eccf538d8949e253ac26d8f2dd03712e62be88a117667401975bc29e03e5cab2fb9f42c5eac607723acfc644426ea09ed67a71e505457ada4cc4ec315c2fa880b5bef5a18cceb33d5197204ccd50a819d2ad5a746c1dd1c30cfa2cf66b34864836cf48b303564521a8be50091a0b15a198bb237118ba199e230b6d1056d2a1f2f4aa5b3940b68d18be643cc5cce552a6afef9a9e5c06f1f6fba97d93323b567e6a44058c8738839748d0fbcf338fa85c0d378cddf31b9093e07e5c43a46f830138f8398a93dfbfdcf49fd136726b1271884f5ca13983db6815313e6de49928608ab23967558ee2129914e037b7f42202b8ffa03b72b0abb3ca9755d177da097706c6631143f6e00dd82d7f4e3bb633c7c824269e0b2ded6f4d46f521b828ed3b0a10ac31ad26aba789743e9f2b8174b2299c3992b2225acba2424e3ff5d79a4d9f8590992911425a2c7b6c7df8f050bdab7c233a5a79f212e8c4da866cad637e6c341276750725f4a38fb80b9c3dbca2c0769467c036204c9d326167a3f354e388e29ad847e5fddb338c5d7bc9473873aad5aa1b2a1b6f49ab723600fbf0a8944ea1cda3de29f21acc4c109667fda338deb46a8f885b5fe6de90f02d9f1c5c44414807618fba68ff3da1603a0b025360558aa0566a9d03e0241b0a993f904dcc3aaca145670a03ac42ba5e70e423eae93940156393d5e3f4459101d1303464f9d28d05704fb4d0bcfa4b95ee90cd50186ad0fba0a767e04b4a4da214997200829ff7758c93e28fd9b71518a4a64f98104dc21dbd887b6c04565647dc5a6cad0b6587ce117a1e105edbd1aaeeb7ff091ba02bbd4e11040a5505bbd319235ff5056ed74e2b4543ca1ad7f9b94578cfdc8163b8f8322833971a56118d22c29ae796d354167a9a2263632a607b367de66324730deb3cb0af01316909f0dc7fe9c8c2ce78dd4de75370b392469acd17acfa462f5b887e4dedd24ce787e35c50bd6ca51d303836e5a39d9f470897ea19998b9e6b8a1d320807038f65598f45e329c3e2b719b14c62545d7072be81bec79f232631b5d7944d7699ce7d12fa6f93676e5e609a9c0926d37f638d0b891e3ff884fc0b72fb70e4a5865db8f8c3abbf238cb0e0bff6e5b84e0449c957a484ad3899e419558cf162ea76da74de927e6f4aec994bffabb1c5d43a249c91c19acd7071defaa3b45df9e6e6a4e26084cc8331cfeefe06dd7c8bc4f5f75c443350d7c5a2b5ca0877c5ba74489583888e5a3289d064bf61fe134444a08990f19d954ad072309ed1a0b6172b28cbea90960a75cc48892cf895ae5548e056a19ebb3d0fd53180e941eb4f3cdf087030de653a930b04551acdb9785d431748f7f62bbfc43adf982a55d51f4ae57cffadbf1249a545db35550f73033248ae6763d7151f8c510ba45daf0921f56e35e7367eff6278f0a80b59b5be4f1f1cffdcab5bb39d15e9a34110b884a978305bd80a4889fcbed67303e6f4d4b51a04ae15d7a901252e429d4f4830b39675773bb85d9a29ba0b9d79240c4b2590bf6d1d12b3fc4394f94d1f29eb47fb2bcfde00256f75da6f82d876d51c7713ad6357cf4815037f6bdf78e476893f076eb60a1eec66c833de97c0096a1f456633100555a789f17bb38e7ecf0b3cce2b480004faf23a18283d3a19b8ccf4ffd4011f0b9919038f618f1b1859b78ec6db4a3075fa0d9f84da750aaee3498584cecdc3da5fd6e6df52a7866630a4137f28ecb4c136f3885462acdd0a517fa3f30126a6dbbc28c3fc7f92b375dfe3c5bd0525d285656012aab798d00050236d476f571d5a6a2f74e6dbb37606a936dec68c87ebde78bc015d89bc9cd1e0f496319002c3a76d5f56250cc23b9514fe225747739a0e76cb628d71daf5019f1b1dad99df682e04b0819f3cdeb5217a8ebdb51e302b8a78b6cf1ba8f7a5523fdb276d45db631fa941af7e49bfe31e74b25501dd8045eeb901c84c59d106fdef78616d8774ee0623aa4e69a78702b903d9dbf00dacee17d244dc22c90102f61a8715722bcfcd64e3303956b50b4ddc8f756c0a4511d8cb206a331dabc82d6810e81a7c2d75ccd6f51414050fab644dca41fc50a1d7be7d33f4b4ceefa882b21cb6e6a62268acb2082f7c3cd5c6de7357fce74acf07bff79fd8f9d88e267ca3257ae06493afd7ab8bb5238422dc4d55fb64791f547f17407fb1e190703abcab819f12da3ef1a8924295763fb08432011bba42a983be6518bf266ba7ed2ac55b443c968161301b160c93d1a962bc84ca4ad98b7e3031320f84979586cdef9546808df05a5096f8e3072fa8a7ec53697488bb638c70f823ab7cf7c89742f65941cfcf07cb8d657cae622e744e1bbe16930d0f732e2a7ef26fda0acc668146e0d6bd5c4c7365772be7e850f8b63fe7c8e627d5c43bd1230b471f3337f97200b6fa4ef239a237762276867eb01fd04ffada2ed19acbffd6275892875e3ee87d74d826cc31656b84d83b53f1037135d33eb28849feda35ba86a3c05ec1899ae20b2e5cc6a6e9636a11f22bcd2a0135482130d9fa2e648b6a93e4e7b0d0eea204db28c432cdd11d0370eec50ad6799ed83241719aa8b2adb4e29d861dcf041b814a92fdb91885bc316c679a5f4271cf1dab3456085e95e8add95d3224cb689744b1cfd8c6ae9a3784d400c199ffc125a3863ea659e705aea822ccf914a6d305daa9d613077b9f17128c1f277771c86e5d41c526e0bf07d8312906ae0d3c8245537dc61f1c9c5972d9e66d5ef964537b88580fe445ef641b497371b6c589f5ce2b33c3c1b7bea9f8a136b17f231763c4cc5b433a00ac7f01e40dd14d78c5838c293941d2fcb98b69b6691bc46d9c628ff7047626bf472019a63ed4611ddfc4d94c7a2045cb43688375f548fcb9c86dc44e5a4edf7f058bb7b5c31120ee99d79e58ffe8fcc2e413d035fc9508feb3811f9b33d7c24d07022131fcdd0f3b90af9eea506f2388eec402e02ea5e69318bd8cfe28cd6a7d514e3c93c187302086375b203a430df22b6f139e66bd9620ee056881fdddbf7a01ebee2f9cdeb8c84223d463d46209ad264a44daf757d2eb3496d0ce8a1adaba4aed5ce0d8ad282daec30c28ded196f0c6be9e787f33bd1899d165f6809c71e3f19673b33461bd018524014f79de637ec6f1cd0a1798fe3e25b4bbdc3a08cebd12ebe31ff43ab9f01d64141a6bfc30159470f37a576184ed5e9b65612a799d0a5cc63ff8bf48d035c78226b70ea3a85b2769cd72ff96106e5da42f73abc159c0d6eff79659e7dde1267f6a4555fa22f3b3c562a0a538951d26cfac031a487dc8804f90e370561638b8fce47309d067bec80236ca2aae4b1a02380e3757a31b49057488e4528d6e34788f8caf7116d45ade38bac93039bd484ab10cc6d680cd111138543960feec38e2876e3e20efb3e7d63f4f44bdd269241780e608df3a06b392c81416c45e8f16c9b7c389920fbe9044b5e505172a45884a203f7d812064d191bb20bd78d98ff7615b99817a79457f7fb6ef5f2621b72cfe546aecc8fd2a0b8802e4d110e2a5852d60ef15a64f6443e1882ad3c721d93be1c8f6b400bd47dcc57f27350205aa9f4e689f2b1e33c66ed481f472e033c48860f52a15b145ffcf0027cda9848472a8543c15580c6f912dcdaf977e27a2c2bdc013fd25a7c3eeef874b0e0df4706c0b0cc3be4927dfb7ed3dc25e795eeb0d87bcb7dd620c264ed45c09b8b569d26b38c2d3bbaca1d7506a161fadda2dac7ef8c4d3ae1a2ca1bdeecc1f105b019f3a8b2c380408ce6f4a1f6eab5b290a17375b40bff549713cb6333bb873226759c16c67b98f66e4883b8586f3101e22eeb21401fe13c2cc657a553051ff7c741bb4eb73cd0f5dafcb732d062f0f3f6973a98fa895ddf983f3a726cb1763ed2975d3b58bc83bac937f96032c73f4904b16a395989a036c8ccb44570beac0863828ee35dbb7b83c433d111ac8da7e3eea41d960fc5fd826247bad5fbf241313e643891f4bc9883e3ec309964ccfd12c221bd4dd530ec333e401087392a2eb04ba93a675e25b5ba82bb3dec875b6e3439d146c51f4df859c47092b905cf05cfa474fa598fb163d66e360954adf3147ca1340d3ba412f34ae9e8a0c495fcd31386989b2c35b40e72026b149c04f267473648d39728b49ed54dda17bda716106f9fa949257e824d6558a52bc4f6a8cc987698c1ee9dff02cc9356d5c1f66a37b0a05fe535ed30557a8b9ca304386ca606e887b53b792b7347a26f69407f51ef9fddc6ec8452ac7316762e78ded474849c5e265ecb8890414da9132aa58c3196f24d81d770feae1d8d02d74e368af7783f79409e79a05998f598fad93eec867181601aee4025504ca776eff79489eeba834f590b53b11cf9cf241b8443e351b51eff901dae346d90dec496c1c5bb4201f32090815b61184b510e2bc0e07aa8baa8f0cd222436ff4aff7792c0c96a62ab658dc41d399b804c65a403954aab10025112bc1997534f3c3a3ebe6a3b673949a7237c8981b5872ef00f5f5366c7c6a05e97fae62487531e06d2245ff8d221f8e229052a4ac36c22f4e14a53a917308c31d7076d2a849bd6fd486c1d1fdb574f42748606da2492b4703617d5d9d7653222e525d81b4b5dbaaf812b05121535fd83d4ed0b1a8f11ba3ff22eaf4e93be37faa8d560ecfbff2af973770279f6ad8c2e97f5dd83833141b9b07f22fa657eacb365b94329b73522639d98bda31b514ab174994bf906bb7aa79e0b6e3ba45fc2ba1bc1029255a04ec87d18094e28f2e119d89add3c8df9f3d92e1118470e7049d08ac573d84be47e07d13ce9cfa091755110e434d83a2c61a3637e52ffe9ebd53e6e609760e211eb0c8be42f57f572f795004fe1b75ec11dcb09633377aa77420aa05435aae574c9cddecc08470f22d25e447c25b77e93409723157cdb35239a825ccbeea699e3b7f7c40a4c61b5175f819f99a8ac0b82140b9d4c9508898d3197c54720d8ce097f2f8850d81d28424819c0c3bee386dee91e24acc61b261f91c54566bcbd63f833d0786539dcf4e51c1c6042eaba0fe51681ada03247e61ca2614abf2cd74c2d38cce4dd0b78d162fb40029e033b8b8fd33cd8a7a6816558745c7630baa4c255982dfd20052ea05006cbe74c3d018000ae0597f567679d8665905842f307664a83862b39d6bdd9a51b906f71d9bae9c06650e9b52d688ab8101d9703077632017dc702e737a7c2fa9bf012dba320e73c34b9cba13fbf8078292b2882fee0014b84b0c0a3aa04b88f8cc7e7016b1217798c35c8bff9422c423d703b996d13ab934d74b6816f09d71a168f860ae7114905a98bef343cbd46fdb4b8698e1ad1d76ecd19cd658c7fac99586fe4ccbbc7d1cc36f606878d1be673dcd70a460830cd949b9a5ac9143697cd987659133305b0963c36dda85d726e4c1e5e8dca64044aa1d6da3eb0e0dacca948f6c52322e6704a1c00ff8baa6e9b63a4984a2d5be1468fd26c2aaa6c0b0913e131b02e8c56ff2fff94c5c4236f9bb725b347841fe6d71d97be95d5d9072dac89ae92e68cd1e3602ba9d109dc40cb9667c682f547b026dc8c720f365ba6eaa1d4f5f5d271c5b4d50b3b7a344dafceed8a68783f6fe6e564e04699cb44ad607cf80fd8215125bfe511070919a07111f20e69094c508686104e380c12766c1acc924202d253e7348bd9896425c96deb25343f826e2fa7f666f2ebaa9169759f76d0d7d7b82eddb865257a747fa479cefd8619c7d6d2c4dca9827509832574c11c6ff347384af967c4bd522a2b7a7ff06b081022c51886d3a732a2056550b99d8167256c285a48bfb8ac90ba22b96ea08f87a7b147c4ab2b5db9767ac8e57a88a222e29c0e8bc1a81914b978eaa9cf77148e66b8ca0e8ae671f057a25ca3c01568558d279cbd9602725a354e5338a5999fa3f57abca0900530bf1ecf59efceb7763838286440e02e930ddde321d812fe97969ed38de228dc6865241764afe740ca0b59fc5b1e09c3101e132b80ca66621d3df6982d6f0e364375002fe945ae6f886d98e20da9fa7003795d70efa0ace45606a56846861b6c2ce66868f533875db4a2c8bd99a7db3b7fcc1d1a6ab9269ff4815279f3ba0072ee595d4ae9a7827e22b32f72fa47adc4b21631d7008e853d01bbc338b36e080d7223ca09c40dfa5d6e216db0c230328d215fed59da0f574bb53707b12c769940994210f36cba7e4e16589616956a7388bd840b2db541c54fc5609a9905dca554966d4ef9a931fd400cd2c3bf9a02ff85da6e20cea059f95935fc402cdd6ac7e6bb96d54e3666167c18906f101cd6cbb81d0d647402271378d20de5adc0d20a0b5e918c3b028967fcceda928555850c8c5282d44dd0eaf853786b678ca0a9c15a66ce55ad86763f7f82bfc4dc011795e1393e388f87038880f9fe7e78cdf4ffe5cdf2d16fa2dc27d17fa30e2fee973b7046d11f0d602ecbe8a71516481f6c56bf92653ae50682cedc3b8d001849f17f5923b3aeeb0aa5ec81ace7224bbe204137319e897a429849347ef17ed3320d2d0fba33b2d0f326dcda1d3f80f5e151f6a5687453fc6457a06cf0a716618dda400cda78cc7f0932664756d02a444509ad83d4844adca85b787d7e7aa7b48ee05dc894e163ebef76b82d4a0ec177137d412b7b38b2974d74847c467c431d94f7e9f04f9c47e0b1480df63a3045a8c7560d0d2152b0a820acf4700e8ca884526b51540dc5f9ac21722e5e76d0e50a35e603d66ef6a5507a8d06a077e50051f7fa417fcd527ae91e69ea45f8b85fddd941ec049a597b54932f7eafb6281d472b9802230d47f1a4ead80372f68875e60005bd967062b44f6164fb3d6a201a33594ce037243005533946894678f0969e5223fe7f07147c2e249d58e39671ac56bc553a0a3ba3530e7311afe3e0da38fa4735c118a0ed8ec1a0b8186c4128dd9f26440fbedb1412e976ad51e55915bf4056c9f642c85942c7d1942c38c31175b86adbdb0b3213754ee015089dd53573597e5958cbbc4e60b60647174bd11ce6620fe9f51bac81d193daed36145e3b034930e02771a19a0c4434a088f3338796b25e3cb389a104b07a4c0f3d096f3476bd198407ed08ace09a645f30eee1ac53bfefb3f388f0ae00a54f2008087b95b6e4f176a8edb8c49623293b010196c4ffc4c7ef8cb0e9de83c7d4401b8649992bcc1b89eaf82c86eda14517bf7a5a1585f603f1f4681026c0b3260ce95fb88daf287ccea9a36394acc10b3984dc9a78f789d79355b65121df43a87646e806afb54cb642930e8b1c784c39a12a0ae7c417ab17decbe4cecb019f457a86cb896a5d062685bf112e01da507880620d60b81df79c053ed4e656c4c52e13d0beaccf2f30b44f91f447d9c395370cb1e6b9fa11e27424b0a66c4bc51841b0ff2024eeb093177a01816226662e85699d74c7200469e15d533a23d390754515715f603902cce8265fe4970c33cf8b25ccfc5b95d4b17eb778c355bd07d32b3386786f336b03bc2cecea2609559c0ba38d2d3ff7d79046bfb3c4f10f55844691c49fa4175c57df3b49b4a37d329e8642c52c8c19871c8deba48db52f8caff1b3a06cfc027f72045a13b85db6d1858c6b10cfc59a0d9fe9a8579f81a379d01d214c80a2770fd4185c4f22bfdfb64a709cc6a41cb639f3a91a3b3eddde0b5b0bffd59991fd5d34d1f11099c9aee4e60731b89ce799a1551651d4ae222469eb8459f13495ce90d652342222b78427e4c562488e328c437bffc65053a1bdd01eaaeafda8fc395dca9435cdbc6dbfb17ff391b2e304687ed336aeb09f0af4c5e2682a57ee75b8039f4d6e79b4b0c39bd04df84723a92d37941742e20f8083fcfc77939be849ff1a37c9c3fe14978323e0af9e62225f3f7453fbd7ec25590091ef055fda0d578ea3205482f253112fd07425621905b9f9469d69515e4c868296e41b8166d921850b79d4c2c52e83c34ef3dd3cb62805b93bb6ed5fca37b0deb0a624237804c12f4ee5a31512858a2b6ad958a01e216cb35b8f651ccbee816aacd558589076e8b7bd047ff1a6efcf00c6018220989036e9b7b5259752057dbd60bc5007123a871cb43c930c0e51cc9987810976382315c31511327bb016f2d7d5231406e810f41d52856945da800d159985f85dd069043080ef992a5341be64a51859a1afe21ed41c6b7497e9a367b75be6a725c11df1f2051fbb6f0d4a3fbebc46f633c75232ca0e117f5f6f3cd6b0e1fa9febb37b182a7fdf22068f7ad0f32889b95e06b48f8b7e7f5f6e0b04147a64cf3ebcc227df5b9883feed8960dec068c4e259fb1fda3ef81fc0813a26c188d3e495ce5394f073bd1a5d0822031491d7ea87c15321a4386cc2615e90616194092b82e63057c0104972b7641702a7eff811277b8f74da2449d323336b0400eb7812fadf98654fb63add759ca35937e4a541c6019f14a3ddfd429183759e40b180a75d007f22ae09bb8a638055343712ce5bc244fefe616c2a1ebe3527319e6d320cf73e7858638c678f249136896cf663a1557ce1fa82a06a51c7c252efe7721815d554e5b1982d1d39a66046283757cee56cb045725b49ae4a584dad68791041e4f10115fb5c8775bef0af5159496a1695541b5f645ccb43e93b160bff3215ffbac485e8bb11d028e373af25186707c684a2dff8d00dfc6ce3166905f5940979180a38c98cda9d4147aede7d82b83c89df1ca0be5351596990fb602b657b20416c5f3a016a5ea21ab4cda265738ac357f1244e5755f1bddf75552e858ee1beff11714b2bb98701b80d28c3f3177f346ff4dfb47c6116c3c5f7a87489fd3d3c14dc88bb622998cc6498d1d2b2d1fd7fbfd4c198a7927ae775c6923f2d382787aaabb1816402a697c4c49e303e5e37a742d3000893482f263091e93f55550eac408bd26759f0790ef3094cf7693d1bd143c21245ac802e98f597c9873279655db9565daed00e69973d41c796122965ea717b61249bc4dad0c011d34f4131a1f2b3dfcb4100ebe7a93c60548251a1e286bfc00f9bb130c7ee526080b50251b1d55d2e881f2395d02d45514bec96eacac4c8a64532163501302cae4c68e959edf45b76dfcaa7df1d537765049c30384bf3ac1f42f3683b080efb892c6c74a1a61fee027dd188acb990a08d90f52b6d04f68f048e9f92cbd4c0d85316513ba89e78fd2eb067e03416d9f7d2babf375a8acf181e7990e73f07f298802b0eee048a5fd13f809cc68022a05ae745630cc249e03330468f56fa1ddf1259837991668d6e5f97785bdfb627b2ecd7fd70fcdbc332ee493aa38ab8a30490b183eba4e35046755c42bd110f40d7266339020aea480d1da4f891fb160eafa2db2b79432a594640adb06d50677072dae3fd389e07abdbd36747d130b5c4f36c1f5654f0aae2f42747d5ad4c5f566d00aae87b22faecfb383713d92bd0b5c2f16e37a39dc5a703d94a38d1d8e777ddd2ebade88d1f54eec645cff7cceb8fe093bf45f9b0bae871b4261a233721aec30b8fe2e7f68695c8fe7a347d7cfcdc8d5b8fe6de79ad4bbcf2114263a23f775eab56f31703d5b7f6d3e77ea1b76555e10d6f2203dfb9c9ee6bd37f1e5a7abfff5dbc881f08617086824f71ac98d21ac0099daa555a66ca4d65aeb9c73d659679d39d02f6bce55ce55cef7de5be7af89daf4e3fddf3a07faa94fa77822bb24165d3efe699db1d8ede69ca6693af30a917ec678e8a7a9cf0de0481559e1857b8ee41e5961efdff106ee71b000123ec61b3ce8e92f70e867bf81431844fbb3071ae50febdccf9ec589414cf8d90b87bc0799eb4820152211ed8d733662239b8f3963228be460ff75d32ab79f77aa91da2c0076ef9ceed4a835d5d71d8348fdde2a8c5a21074a53ed4be065554e361a39d04ed32b15e45218989d5d56dfccb021b1c7c9491285bcf98a3184646faee09ec856abc597d518427a87c2ec0aa91279f10502dcab5e3586b08bb2903c2d6366d9217971176521719044cea08f08d741c9ead674b1aeeff1db7a732ef4902ba0a044057b7d215790f611bfbea9015a0242b62ef14de6ae13e358d8220658fd852166df0a5d19bcab8f975ecf9f1a4190a1e260a2bf5b82eb777038d1a185cc100537c649079d0c4f1cbcbdb82c7c195cb9c0edf0d3e1cc424b11a7c6d4157e42e5a4cc32ba8e92485eace942d251f53c91bc38b33de419b94a61aca01285e445163664bb36c582cc90bcf8a2876c59774bc1624242f222cc0eb2fdfd58de125f8e4b49149217db22c8d6bb58df8480f9d49c3c902bc8435548ee43903036865c415009921b71e6001761b6ea542861768716634a646b74b1be7949bd9eea53f244663826232fa931ad8187ac6166809a71659eba8c0c0c2ed6370448cab547328d8b91d8f7340021a8267ac4984b2257003c1b42ea78410076fb2987b36512d91e5d991944a6feee8f818b41a9779d833cb28a71aab5d6e9fbc1d0c8e6232641fa63e27a93cecf39bf198ad537331aba271261f211ab9919d8213714c9f281fbd663dffed8969fba00714dea81be4ddd0f0402811adc28be007df6f9bbc517a00ffa8d64d1d9bb3e457aa06f62f0ae4cf09ce871820a32bd6bc223e9a987935b1732f5363840a06f5b230b90d5fe57dc3e074770e307c7144765bffa7711db68b0e592e178d225e5f4ac5da06209da911b932a2782451c5d937ab76853f4006f48bd0650baff695b524f0b28d024676a48100cf151914591bab144a649d69438a488d11921d32b393a92927cce6a0421dbccedd83617d7e27bfcab36fe8a5e9358bdea3dd06c91fbf5f8b7d6b99880125f1b3f387eee35ff38a835b0d1871f2fa9ecfebb7b166bb1c73369dac1c5506bad3108ecb377c11fdc7ccc427ccc43114503e00dd90b9907c0c7ec626308bc8106adbffe67aebfdf0fdcaf436010c05d45ddbedd5eafec0fe8dfe36f8d694eaed91e08737c71d4633aa30672cdf6db5883d4df740928dd2910f73bd57eb73a3822fd18d0376eaed97ec8c274a206cd3ba4bc84ce9b459b6e20a3f6695104c577f8b99a163568d16f5e989fb3cf2c51fa8b93375dea695af4d463a59fa61e0683c039fbb7ff14df9c6049b706858c098909327b22bec155cc7c9ec8284a319502993dc6370a8806cd152599c49ec8ec597c630266d19a143ca70e0f64ce416d1ec65a07db352023b1aef12087201f53bd37c6183bf5ec658eba703ccb1271ecbc12da9f9fb5b1c3891ef310f46f96ed9d65896c67bb678947622be7fcfb47591c36fa59cffab4e9b3d1bf89ecde29ce670190fc2c0bd04f7d9a020720b88739a41e07a9acf84825969234b97043db295ac2454a15424f50aad0a890597b58ca34352662d829d5a3c712a69426c8377d267d8d4484f1141f4260496b70744e1a1084891d123bb81452382a994cbe40e6d5f8d8638c86c6b4842485244a847cfdc7744a3d407445763a9a486c63021c32c8c0c0a0f0889006d037b82ded6ecfe90614a92f244e3bd156e40991125f091f203faece945544f690d8a7391fc0f91ef885808210e40f9ac6c7471f7b0fb0d1c7be639fcaad40c964120bfb17dbbbdb0ff387a0df9e987dd73e6af8db8307623a7f3b319804ffe2320ca4f8dbed6fff62393a0bfc8bf3f66458a53913fc9bea08fd6d34e3ef250afe4d9ba088fe4da9b64f95cdbf53a68b7f5327d8df15b64fa36d7f07f78e4a269345c837a5fb3bcd4bd2f87bebf418fd9b8586947fefa60c75c1bf992a1369b17306a3f16f2edb3e47c9805b2ed3fd9df3debcf7b7d749a4bf75cedf5ae7d53d35fed542db6ba4185d7ae96fdd04f577067bb7db09a6c1bfba0cfab7d7d11b7f0bb796fb3bf998b6d90d5babcb123f2384e53976110a7409b2d39c6756e49c73aee0d28529059ff6cb09f541c8df680810a71c8b3738d5d4fc8dec2fa7586780c283900c49facbf2230eff720b769583969cd31403591cf6afbe8048ecbfff8f0e9a1ad558fe41c8bf0311a8040dc6df0ef7d0655f38f1b7fcebfdbd60509ce11b33b1f3b7adebba41966030e8b6c05f905d555ef33b74007eecf597c1b8033ff0bf2040ffec5f4864a2fad5dfea9d62bc7556598838e786b87d10f2cf7e4290040b933925bf75d95f04e91511fc6cc45d980966e4c2feb682de5cfddd2f1d7eb129f3d35f560219c9a6e90765d62aff6d8af7d67be7546bdc5936defac5bfe1ad8f80ff0ff0b3f85b969711f0ffc1f1f361fa31fc0b3fcbfcacc138e33f56fe565ddd5cdcfff8db9f9b8b361f6fe8f07191dfb66e2ecefcd66d81c01fbbba2ae7dc6d61c15b6faafdd20721b37c6c7ca9f92c5659b6f04dff8c8e85980b605e2e547099215290e94263d16348310dcbcb0168b2f11dc12f91d39636e090970bb607b8d7fbc74babb7174cca54fcf7f8cc693be47535ddc5ed99b2ea57555d55954db9067eef09c20f35fede4c7b0539ef26298ce8ae520e3f4d27a06fb6d1461f3db1ab2ccbfbb681423ffb6d50f434e7654e4ef8411c8dc2c0cb514f932618fe344db7cf46af895d6b9f72a87992e6d4488b673af89973ce7ff6af0be71cd7e41bad8d1edc68ffe26c4fec2ccbaebdab4422f77e1f8f78cf0317ab9e46a55963b5420c8275530c82e5479bcecf5bb6fd45f8d8eb2d3837c520f857ddfee57aeadb3558805f041442e1c79ea79e25480283087eb9ded246f835614800a67ea842876a30f56eea23a418c4faf19b15918eaebb2048e69038d8008c27057502c7d363b90489fbda7bdb79effd87d30c5485db2cc34e7fb7db161c0677fdd5500cf0faab819a403078d2df0d6d5ec5e0534d821f6f89596540d15f2cd75cca60a3bf5bd93c6ad15fad1462c24a7f798a268ca2081f2f9b28aa60cff251c959f36e74277decdb8bfeeabdcf5cd3d9ce1aeac74b2674afb14e09cd171958e981019854fa89b9f407f0f192cbedbf4820014eb49c27766aa5aa1ab9b3d58085918ad8d6df6d618ee5aebf6c06b822517f3771aebfecc74776c518ab18638c31d619638c719a3d5b3b0210d8bfa37eea5b0d74fabb53e3547ff7c720145fb01fc10b369260f9d8afbffd20b0ff8584fed888dd6f41ae3b002337926b1628fd657786b7fe6e0e827af6fa261be59a6ca4a3fdb1457f5d239c937f1b65547496070b90ff7aa52ff9b39128a7f2755df79aae352caf0a7efe396a9f68d5d234e56a4a33ce384d35f62f5adfa45c33fba7de00543ff5387d2ef5acd164329944eac04b1b62fff5b0647e0ca67331023b6ac508ec443fa436820548af609c66af7fee92c9d1dfb6938fb7dec7dbcac75edf74a8bbe48f2076466498ef4eea47d18f281d71298822113a8b6212a995301acbb2b29b2ba2e3844cdd3a60dfee125a543e57ce1b575d6d2282aa7ef8403d71f83051597295fd385908b97441213b6bdbc3bb824cbdd6421f0319a1a0f988bd0b507f37d8c77e26a9cb40e91d90f4772b3ff6c29cfe6ae5c75e835c7f798a8fbd87b7fe6eaa8f7d06293dea63bf9bdafabb8d3ef6febffd7b7d83f7e084c4b9452da5b140a6fea86f42a8533225ebab2c4d0959626789f2e7c69a8b2b360b00b9b165649582eb0532f51694f477037dec35cc75ec65eafabb733ece79a8ecdafb994ef440e7a22eeabd7cc4792789665dc6adbf9aee635505b085eb0364871a112454666c35f654a9ce9554a1ee52dda595b9b81bfb69d241939e92cabd3d1a6a1d0b208ece2eb146a054b0be4e0177949d957395fa6af71362db7e4c4531829092fd27c4c275f7c8cc49dde93f29608ece6601933ae7e250facff9f6abeb1b8833e95c49d5a104faf8215991d8a675a7a5a13ab567eddaeab4fe00c698fa1d01f27062d77ecebe42ad4bfa3ab5ea7496690398d6237cbcc432fb4df9b56895542220d5d24be957c483871b7c2362c5045089ab070f1d74a25f3f5e7aed78dae52e81dd1cabbafd6f57d7f78399df81d1c38facaa0a30abf39ff54b88e9e3c88a139814968f2c57860f2ef5c987332bee848f3c1c7d03f1bddeca688d3a57628574976a05a26c7fc25c145650aed6db8f97567a6c544ad8acfe4bb6f50a3bc0283d68c3adb3516bd8d56f46e74aa1ce36815a7d0d637ffa5ab622e96adaead57f4a78aea0a8517aff787945a56d7696c5cad3d9a5150b0baafdd1d7a9df92ca4a5dd1593936acaf1f2fb1ec5aa9fefb78892505a8a3efe40f4767ffdb5b2afdf75fac647774f5e3e50f18ffed9f55f9d61f8c342e965d7cff3d7ee098b2cdc7f9245ecd47f147a07ec9cbb2abebf7c29e3fd733f15256216deb7abaa1470bbade8e8ec5f5635be8fa28b4bbdead0d6470604f1aa95053cb5bfeba62326d55b5f055555555762b8598c879ba4a2372e2e24a8ea5233c24cf153e655f1d73c223c29ecd31b709b6d4f388202892b687b3e922d9b6aa95d6227e9e4ca1a490450ff1da11e492953f162e7e4a751dfaeb1ad5b597f35527a91222de6b9a0d51ebb1c4c6095cb60364c59415a4675d9218560f9e1c2a253c787c7c0da564930a0d233cda30af918418239251450b8fcbf973922494dcc1c3680b8cc4909caa925d690b7e74cc886a7a2b92e25382e22b8fac876f2fc78aafea2f4c62f9cc31c683e8ed6e6c0a5757934eb8c7ae40f9188a1aadd0445c9a634cca829a46564af8d041778346598c2a492467ee25837ac8504fc9247888ea6a386f40796d6d3931825326b19155e5cad9b48690223705698a532ba5341c3123a9aa6a8acd5c55c522904411bf0a1b7f5d7df7d2572c9af2afccb8236f483f5288a879691075a5141d02c3cb62a2a56061a65d65e862c2e0b1eb5aba7ac539fb39e79c8b50b1e7b21bada719e5042487847c89ff4452d5d1f13994831f764bbc1c4796d468b65055555555d589c46ece1cd2f40680165d3a38e79c73ce7908194e97e503c239e751fae4104786ac381f6e1fce394fa1e3b22a9c5a058f11ef31b222cae7dee59c0f87c3fd83e1852f07900e95d692a0b6383a232e3c2b396c435a290b9aba5a78354531d98b3bca18a231a5b4d493e39363f5f97175555555555555114aa2ece69ae1e4e9abdeed790da94d00337097978ee657b355cf4e459f216b0ce8af2eb992a0ba226a25f99c37935a9f73dff39bd5eeb2ca6ac96e7d5dd7352ad760e494ab5c83ffcb762b438055e4dcbc8c997c57d86595156b8a82f3486596374de268006183eaac81e0450fa0b13214534cae1f99e55f99cba5bfae5f51ecf814aacff9ce1a4388e305454721e76062e1560100414d1637d2bcd29804c0a90f8fca468c154026fcfcd5bb5b3c4c1138e79c730cd4889166250c2c4c6b03a72c1e153e685e5fc89ea0600f8d16ad446b39e7bcf905c49eb59c7d4ba2e333b60605fd7a52327d9e9c3ca1d4d85c3e4012be16c2133681ac2ebfbc4909b5cdf0d1f32a8183a9432fab8e0d2a88abc7aee591026ad109470c3da35b3b537850986ae8205f6291dd57d5222a5f2d56f57d2d1541029484d44170082bb908f888d2f10971f2e502f912bfd69a569b85cfd7849edc9c4f2c21231ac8052a2aae8a7c15f938510157d199dd93b1233a905c3dfa5c9d85471c191af6755dd7755d4b31e7c6c664c9c98c11594d781481564187348f20f5e6734ff8834b858ee61aa2e166b5ce91b4882b251794ca3a4588908985f4390f72ce57960b3fe79cf311543abfdf5a6497624b954bea08e79cb39d757b92260cb7cf391ffaddc3fed6097f5dd7750d51d5c4395bc43c3e96885e3083902ff107116309a2c6723169d38b4665fd406a7d83c9cf5a0f0ebd173c902f310a5d320a5f16989c9025a190a9962bb5073121412868a539d0d556b537b4f343a985138a243eccce52e40809922014b4d20203a102cecad1dd971e5e0917615128526cd1274c419c44087265820ff68e056bef868a3c262234b439235f2f54e080c252a2975676acba6ae5d94bbeb220168ea825b135253423a0bab3a58d3126cca5be255996cac5c2ab6b2abf9ddcdda5b1154941ba3253a737e3ac89ab68acd9f3424c4f4169585e31c16aa9a3d70ed357ed7252533a5634c5270849919c0847509aaacc50265bf4384a45404199507d2d53c78e8e8a1b6107c4acf2f3d5267346cfd1b7259859cb619941f267dfa61e5c76d2ac370002015e7d9d8109bd35d0d03b91e8fdf168638383c37a08b27907e19210c9c38fd1c11b32a071d5c605aaae1cc09cced5c373cd1f32f8d8973db313fbdb6054fa01357429609bc1a5aecd1f308099612ba0708335598dcd470cbc822c478ec10b16210d9d9c5ccb052d61c79039c4f5960baaf1d9126186edc0c3cc0cccf0d73b91e8fdf168638383b36d8c224890112af3114cc2d058a5f835b4a0e8c63f8b37bcc8b06082172af10623132858184af82cde50934721062b3bb0f170d43003e3c644f58d360a951efecb6af82f3bf35f16e6bfecf0bfacfb5f560b63a25a1813d5c298a816c644b530266a63f3f30641882e1780f5142e1560d4a050a152d770b052e1e5c52d1aed5fa84c396cb0e4a5c2c77e2b7b5883b94078f5d97c6cbb2086481ce219bc618394180addb5b1b14f85745fb4c0c3e4e3062445e3b06583d2887d8598688c5b162af58d3676ed5f0b463ffbd782d0cf167a54ff5ac8f939c6bf31793fc7d0099d2c2b540a9542256785ca97152a5f9cff0a95acae71512283f387081f7b3528d4822f54ce68cd5c68c9c7a70b9d3dcee01b16ebe21906617789430f739cb1d1e0a1039605d2530e58c062b6501913152a6784ca9ff3070e8e34182eccd0c8188b9ec6024d0c0d0dcd0534343034341368541a0b86126822b474210d4bd0a561571abe696858bc217b9a20fe38c862103883649a3f00b52e2c760627de3c68b965486b73411be2a188f3c968a34f0dc0c5ae830a3938cecd010642c70b426838c85d301699b139c2b041186b2c82c5bc0ced0b3890b130357abbc2065a2e5d0591431f850dda580b3cb00ddd7aea256cd8738b4bbbe55c1f53af8dd9983d0ef968ca3a345ad5da10584e9ce492203d494b50a20811538594ad2d39573c6b7a6c616cba6f5dd7755df594ba06b31b376310116a362faac90cbc2e27998da4b6271e54583672f0924d3c167fb5a2b73434ca51be7833ca5b10ef070645c91c6e4a36a4d5ba21d1cc993566371b95f64eec4322e744f5d9d2442e4719b122bc9c1e500ee92cb574b08010d9012472f583af2aa9e307c78e94aec2b99044da15129fe65ac63361148f5647e936c51a23eba8be32a12939e79c7b71ce3906241617f28b0b802a68e077e4b4cb5795428153ea523ad519e9d9925992e2fb7962f2a449f3d1c0f024774188c98b49c857e51c60a72407457464a52fc31b19d4d294269f1395a3f5a84918e20e4965459848aebf169330a1a3769900fa6aabaaaaaa8eb062c76e5555d5561c31eb510666a5fcbaab9b32b28516036e6bcadb0eb9e2109f996ab906bbd1fd4830f388bea5eb9ddff317c147a975b04910331532964cdd95a8f2993284c9d68c2515abf7d6755db78442d6301fba75dd54a99d9be4a77f743366b06956c45d57b424a111b9156a0763421d7659f9d14ba56875d947f753d323960c31c0079228a3a2a11d566a06585cd591f1e89194a3a53c81847832fbb0ebfca47efe2ae684ca6a3fcf7770a0e46226df089e456df0a2a3c1236585d21021695c4894a5c885d65752db1226407414eeda907811a5b560a4507012bc8eae2ca4772e3f4efd0b8a8a0892eb2774ac423f5bcb2899d6755d63ece91acc027f1540f2646c4d2bc60e2b1b08c264c95c185a5edc0d21d58c5e84bc1b55a8f4709ca81353a263a4026d48df1eb4452bab256f840a1745a07665307040493a5740b102857536f78bd21a6f35abc039e77a8b734e5c9f0ce51a8cf4a4a913b5d4a2d035a1b1e24a4e9c06aad47eb49892452cf45d920e61638808468b9efa82ba2d77185901079d6ad2493d7774884e008fa21a0ad152d02f55855d2b0f904ea6aaaaaa467f3f16362b39224ba743e77e7aebd0ee00492ca21cac2234641cc08a330c480aaea792a88e8bbf6abbcea13689264fee0ec958bb9c446118f668556f31a9467b739f73ce39e7219e20fd7e6a5d8bb44b1679d188cba1b4421504f912bf5af6d724123b1e70625d65c83a352bf75adbd2900acb27bdf3356489e47e4774c11954d8a191bb534582dee2d941b175cbef26137e552c1bf3ec4eb6c2cd820fa70dc4ea274c47af242f995ca35555b5695dd775ddee66dd2969694b533774ec9031f5465454c3a70c4a3b39ca3b4a167c411aab6e9ec8fba551cdbb1fba870f0ee116e57048c0ac3d34caaedafa33528b49e79a2ca8ea63432ddac674f2fad292cc8006d9d0ad552a8a17909113cf3c253aad6c9a58fe38e711deb20836e8baaeeb5af64d409901d1e36164eec7f53472050a2a055da6d22497d08152650a89cfd29914852a623f4f369660cb287433a36a314c68e0353bb5c5534e18d56c016da36c3c3726d175d5a50c767585b5e8b9b4078d0d78ae5af0883e25e72d1d98817579c56863526a3732aa2044694355b4dc2a46e61a896a9b5c96888d1e329e7cae98a0827c899fab458c15779f7bd7c934c4b682a7e9461cdf493134fa49434093ab05205c534878910512668134ec3555c08e98181224ce0af225fe3bde6421c753438f2d67c86c091c1d930cbb2a9e24455ea44559477a7c62e84c81295151f444d06feb2a952fc068d2a9881aca8dd71f52544a43d9124f4ea6b0a51c3c33921c4063aac80e8ed9c64246f2c794b103e3c64492fba92272aeb8cee22c51214c20ad3472189928c2c4bb71a285f0c4515491b32a2baec85b0cdd66021ae1869c067a0926d12339e7b254f657bff52fafeb5aa53ec9017f57cc9d1b3d6b1e50fab09a5cb4704271a00d8d4d2809f484a9e3f3d389a1a4074ed5c96189cac261f34f4caaaaaa3c3b538e4ca0b0cbe103f729d17252b239b7aaa235ba94c35ec249c76a0998912410d195ce24f2534287d511ac1e41ceb7156c48cfa825689126460f484bdb1b7bce4d9960aab15d6b9948a80f8f2e08b46a860a5ef8480b7ad1e42468089a508e42120a4cc5e9c1d5f0da49f204e36a4ccfca8d3a3eb51c620a2b254bef6b68d446799ec4817326e3685a8138541e50555535441fbb79ebe502c593ab98a7270b2d26fc8a00fd70b88e8435c3645c794282a82448663479d4fa984da6d993d5696c67dd7ea6a36b301faeab50c310664884c9c70d4d723e8449bec4786a8dc3451285dd13d8724995c4886965e588099cdc5aa5860548f41b7001508ce4780a515a3d594a325376f53cb15a8611b156a22cdabaa5e6b4e40ee088d2978625e3ccaa04d0195971369c9a74ccb827130ec85fa1ebbaaeee1913352df183ae07c555901d38a910e0008a33e265a4f404c9741a02c452e2ea33a91440aaaaaaaa17dc006cd090e961a28fabe9a96345280a05a6ba68482df17a426126a28993ab2d2eab7457744a358ab38d4bbd6ef0f9d42c15117d72ce5b968c2d79229bab73022d11c447550b2a4a446e43f4913d44a8aaaa1a426988ca57552e9d265f557559700d8ab1380312a1808263c9cb0595285328ae10617b63a103b9ae6b27fbe0a5e4a32637a5cf24073ef2a09c6e705154205fe25ff1038271058656d0d6d99214521b31a1dcfafaf12605047481246f506761513f448a4cf56611ae1b5da48f6baf62ed1131f68c3ef79b7331bf7952b1ad1d78673df0f87e6ca802f684892f965da8846c776639aa0cb248200020800083170000280c0c88235118a6599ad91e14800e43a85260542610c96381401486310c02311000010000200040210cc3801c8a74ee099c04023e740ba02d7477029d8106b027b3d2c49c1a08ef0c828d294f402640101423ce81da13b2e24dd0f7ac279f7660aeb7cc62cf4c1769484e3f435b0317489b959c4f3a65ac74feca770db9ec1e40de4e1e7c8ba1e05148dec5263105e540600e8316ffdbec659f3c04cc4b4184c3687e1b9100646f638608dffc4ac0e98196bc21c97ca1c8035de0907dcfcdf0ce192e3f0e439324e7f1ec54bfe88e76b5c4e99cb850793cb6ac3c719d9f48d0c78b405a4a56ca4d772927ca75f26a8ce2455ef3e2edf74591c87a19f05902ff5404be8cb991c51b5d281644e9f86769b1599123d504ba3daf8b3a153fb37763f60cba5a0a24cdebd29890040af1571a92737404b3a36eac8ef82921e49c4ef4df427004c48d643b5af78680de0e68f7008a0e0052a3029b7688c9258422c6392238bdfa8c4e9091dbb938600c10a647b40695a011e0bd9431b8e967cd7c0a256583583b37576d08ed9203e316c929784b213ae0d528382400b9970035576535095fd01caf711fd8403610288ab5994f5591de604a9dd8ef491892b83bf31964c91302ef064ef0d4269fe954e4903f27f8e3f070369223803ab40ff5429e156b953d2fce96b85c1352d2b1d550cf1b4a4398d29a60413f1593f5ba2febd8dfc8badbc517df90adc3f5a0c6dbb448f1d3d50ce15c41db4a15f11e38115e5534b636e93b211aedec2fd83030a153cf2f7fa9273bdc97118142074252115828fc65c5287e1d2bf97105ccf2e23667dbd23b9067913b89314dd35a77b2074cd38c57ec99f183a17d4d75ffb9436a4b3aea963a478a3b32bd08da87c66422c75ddc24331e38170232ba4c9a09e028e4e93063e033cb095b64742901131771798eb7ce16f25d7b57389b54428b1981e0b516787854f284357e4b0351609b34d0717f84a3a1e22a393308cfbec2d337f032095c3c9a99ddaca693bbb3c8295e127af5dfeefc43cca13a6ca28628a50ee5b20da45fe90a1020d8670bd750782d46a08135e98e3851863f898bf16d1ed7afdfb0b2b27126a5dc4dbe9c75a6704b449df76918cbce615fec354b342f9480cd10fd108c21be0f3df42f7d49069bc594e2ddc66ce37538539ec783af3f6543627683154fb445041cc03b987525eb7885539696e6e8306f0593c5e262d98d465d0576639617518225d34406d1d29624528ee3f99388592c58e1042035a0c4390c5f1c49ce04395f5612511d0271d6167fd7f92ff1c6cd4445705264a38c7bfb13eb58a79fc455a5cca6ab3ef207821f9014b58ee3e39716e76c4342b86e956a1dc3165e54dd5e982628bf6ed4859d579ee387032255588717465a0757dd46769e959415c0e9c9aa3350b06ea1c70a63a021e0f4a47a22319cda3b0505884e4b2a763846bd0c429be5199ff92bc0d5a78be9c9b599fe045897e6e53a145d65e989418c3378be13ddf909c3b40a40c041f80b79a53d32f2c8137b98bd4e48b3dd74004a086b6c7432e8d04d6e2a885f2fad0b02a070a680b8abf52d9daee51dacd9bc6970fb1719a2df53d7b4437970e65c95253afeb82e568ce7b1e761ff4fe4bd3b7a5f14e58c629ab3a22479a0714d05904101489eba24a0398d116c8a6d7bf7de68931014a42038c804114aa97ac3840edd9ea3448a389ef29bbadbe0d6895e3ad7c982838eac638620f8cdf41bec9ba2e2668c0ef72022ca1da33914fb5817eb8780794e3b069bdd289f10370fd63b90c38ad752ee6e1cda39b4b39320cbc5fd7e8636b3c292ed435dba68ca0567727701251d33f2bfeadc843737a7b1b339b7d75f58729297099a334b3697f41762dd3816ae5cc817affab829c6df686394fa6fe7fee0fc085d9828f8ce9dd0560c0cfeeb4ce0a7dca1919016b4a6178829b8f3d7658b1d3c33832b07fe001f5e147ff2441ff9f79ef53140b1c2c039d9fe091c33809c4d962481a2253c1b00f2f32b8d94130852c4aaa1156a2c93d21ffc6e5e3c77df90688d75ec2f320331f0b044b65c9901d419c7c7f73baafa180d6fbf9225c2a506e6a230919a5be9576e809afc03adc15d833976c70b713c21f51ddaaa659c53df6d842a84855ae61f2c7038937557cdc49f83cecea08ae288d0cae750a00c22832f7be3974b971c4814f570ea006f370227dc550d60d2ed35c4b8bf361b1f9411a53696ab70b7449f13721e2bbd3fe5f0f66caf628dfd28d1103be3e227608c7f378c2b0da0e66f8b5d8e8543bd31aac3f34c20eb6c6283644f9cc64a97392b5aaf77146882811c6944c8f1bc98d96108ce719a551236a24f30d7c8aec3dc6d503ccf322fff67a64d86432995c15e6ed6c8031b59a1d689ac9a0416cd1d51274a2742de84428e6465880fc3d0c91fe79157b7ba54f99c852adffd15ac30d08a4fbd98b3cfe4c57d5e43ddc8b00fbf1d1fd3ba13b3065e55d948da094018f2a509e9b68b86eddbe206940b4a93d96cb005913882491ea0c69510b78332e8d44fb304e3c7fd7686d340a1fcdf0c145fc4c0cea3f3239a660ab8405c83506bf5637bbcf7ea80cef058968c6ca4cea422f2a3df95e2b76c546e120a1b95f67a28c9ec3a4d0afd363ae28b86451d3a13383407ac0c647b48f4a855c3b53e3f8607e9614ca87288cc78a9ee4406b0e47f5bd05864fc33d94279221a753ca209d000f8d924d40b67f02346540c821631a1b9ec38f654bb35594ab0339bcf2a77508be235ec77a6eacd25629b36c94c377417b46530973d362f47b9ef6303c5a91430442c52fc6fc533fb52479a7c1ecbabbc334bee909b4ff901463c794bf4f689d1374424cf0562de6cb05994c655ce41117a519461a26050f24cc40596118d35e94fe12e81cba4afa000d8bfa26aeb8b07792b8ae14278294121c742809930969aac872aa977b2aa2092d4861e737a20cffd5a6338e79db4a949ea615cd4292c3226e9b4ffa8c086d2df4d4c43892c6826baa890c697fab37a0ed853e45bbcbd046bf94546b1f906a40eb3a8b5bf1f9453ac0fab3799d39c7c830859e7361fef2cc0e678b6c6b94a9ae50b67ae59695f0517ac55b60a2e5f1662a58603b0cb23fdb06df44dfde859e6b7556aef7732d2cece5ea31e0941409afc2509005774fcbbe08ad888c89c50fab922566651e4ab2b07664b7bfb1f057cba0049b419b6b4ea8e7cafebb8943393de443685924cc72425389a7e6a86bf5af3727521f8465c0c5d065ac4694a5ca1fc4dc06d999d1d14a72c97faa5e353ced3a00eafc7cd40be28e4cb2bd600c38a7568280a811b4ffade3d6c87dac54d9e7144c080e01e4dc3a066cc368052b1423be208bffe3bc701a12338eef7dc496f051f8a90aa413e1121d26f81165b62e8ab066bc8260d9ce5aa9938c190dae2c2f9f31d70665a20c60dfb41bb85254360f84a09d20f3f1c2e86fa34bf46c30c2c9b8d1bea8df76ec37744e9c230382988b6207e8962f22bbd84d25cf898017c51620c97c516f225cb54d0a13b9c610b70ec101747d5907546d9a2a2f46a12d8b411c77e43cc8c162540cfe91a6397b68fab1116b7c7c173314c3cd8af051fe6d5f1f3e0cb2cb6b91f4730417df311013a600646b35b322210d3f7e262ff9b635dfc587bf7ea17c839f66cd05164a2ea3bbf1c809ad3000c63fb3bc35b8ae4310372f18e07d05d4deef8c807fc59408635cc6322b2b911f72a76d4574968c20f5ac3e2729150e889739435497dfe004e5282a1344bbd0e47119fb671200b6ee08978ae7c0b1e3a0ef728d383f6a441ad5a64b3adfe60e22639defd82a8e6c57d87ff16429eddcce5fb0b4c263d179c4728166bf83ce7244e23a76a28c2bd98cb1bde56c001c5bc4ac32f3065ee1364f6511b69f0fd7d7982e0fd69139ac429d46b1a86fed3bdf6c403de2c41674656e9b8f694e77d92a503cc86c36f5bd9298bbff1c08a9ff49801643449a8f27dc87879fa96298f4556568463621aa1e829e7968e1417552f5f79e0bbc191480eb1ed1126ab5209b56b7c2f8a3de6e5c5938c7584a0f35477a1ed498d38bc377d20211d159209d861fc971a63c5f53342ce81eef79462ccdd12d802cd956cd1df881ef86493a82776585b8ea160a68e94171f79c13898fd843aa48c73ed4448e285a30e67866982c7fd2cc6e2130d2ae12dac7b1282c06f538bae2553ef4c4844dad9bc73f2374cb6e876c191804aec326e7f487ce59e9d90ac00b8cea24945dd05a83a89eb66ff25ceb483ea340db81d988337c45ff0206480d95b2e1c79d9d533acbcd0cac6073490f6373b5b5dba6406da5f4e04bd41fb10106a2f81973ff4b2a0bf4f9639e1c72ea02cde605bc5ef5fd7f6355b5aac77bfd7b4f7b6c2d3a443bd5c99d88b04e2a23d5cfaea32fda8ea7cd54ce4ce32d95f010ccb47fbc0dfbcdf51ece489d1214d767a2251c481f0f1955263b44032be5448b1dda3d70a923762a64422b54d2ddd3004aec9986cdb28697ebf0a7c504e692eff9792932a30b9f319e4f413a98d508f233e55c93d7d1179aa91081098a41e66c4da0cfd878516b1ade6cf41641eb4e75d0c3f3147153376180081588ccc7bb11b44e7f0b9ff99de76a9ab2d7211fde321a7c6d332165334d3c83f8d8876c63e7aac7e71ea299ad58a3569cdc31b72f0a30abe2ed31a5997b5b4369bac38d18823e0f121d91bfb1fb1d5c4a05c377971f333e207c6475bcc22ed2609435df72303e9a323c97d5683883bf4caddb32105afb2d111459dc602ad2e26e32e3269bd8cc01b2a33c972da6d14f497e303859c4e9775bf9e07f20da772f449fd8080cd1e4b1c87a416eba517d01beb062b0c8c02d35124724525f0774acb3e0086079fd922afee1ec3355187725d4ae9122506dcb8efe4cae810fe1d9015f1e26101fefda08c9d9cea1dce89f87349eef6a92841ecdc5d6054a7522a24d667ef96ddaafdad2461e39e9326c912ce6fd35198fee79e30e97efd4ceb455409b135bd0934e173a056ebc8515a623d8b149819758725c2e5e474fffcba471737f1c616d0f4be1d4c6fce5f4405cd4bb327bf9073282b0f515156c050eb81a43f59318e98aa3a9d13b681d4742e7f0e3dfdaaff0e8262c731b4ef3daaa34342d45c83d2cd4478a11315538cbecbf01dcb2348a69941f5a76597164efbdfcfbb7188d7c1a01e60ce453258918b2419ed295ec09c0dc8886d00c9057651c284314e26beb8dfadad2e15b5973b9b4a97f6866fdc6fb46930cd3c82cd6403429ce9758d12d2ebf780ab872a0e3f86fe49d3f26c7a21dbb17b2b8b5b6575d09538d33fa146a93faf3bfcabae8d6304bf67d23fbc3cf6ed2fa0ccd826be035e352b3e71241290e6f2e894b8519cabb38e35c2f110a2f2e3f5647c62c476bb78f935034751cf6e96acea5e2e6d914482bf6c7c27d66e3c7c0cb527edb8d476280ec1725fbca6187339104240a535901e0b7a5e95b2fd901923ceb19cb7e381b43aefcebfe0c3add476a00aebcc447b1b84ace25321d85f688e0ff50dd6b40dc2fa77ff281ecf52cb35735204a643a970e2babb69c19e0ea92a4b0427660ec632e46d3a24515cbfa87784b69b846f4e0be4ebbc11c9b3e49bc99a4e8b94ee48e859817797c695e82cde551c3901789a1b6c0cead10621c73ad2869d9568710ab109856113ac2e74b63a00cb1c31bb6614c52ba9652876c98a6e60385bb1cb5e4663477093a6c5e6777d9631a5381c6a9da5f528566277dd3a2f1d40402cadd2f7444d87047477a6ddf8a37034914f7899c832e861383813ab7053caa06bdeeed2873e7a5ae87d1625bc0f49eb24ebb6e776eb3c07f3196ea4f1495163a3bd702716e87bad465fa02477ce4000e92f16343ab18402daf8dbda70ad1aeafc05bc185b8af7089fa4e04e296ab93000acef50e6c52c2a25af13b5aa2414a9c8da4e5da0346825ef9dfc24b15fd6f9da04dfbab52daca8f681d0730df784d6b3ed0eb3b281626c966a8d9ca09667dfb5dd172bea0fd872fde9131d896277e24e81728d2c551e328c99728038bee1400af2114b9eef0134cd67a081b3c81295c763b5a87069db37cefa83893c86a4eced2c8b3b464351828e1562f5cd19f26f9099c7bacb415fc4245260abd7a83758a37208e305d20b6baabae7b1bfdd3d918db915185cc066f68ef3f037d790c61f2716487637f8e1bc465b7bdef133c191dbe6cfad55da8d0467e4268ddb5775a5015ba65556b6d2a03a7b1366f52da5a4ecdecfd7e11c5ea72a4f4c1c69359bb25bd0f2efc97bb4f77c87189bde59697965c62567f726d68b3bfca034abbeaf2f544e36f8051c41a1509e0c8ae69bcd01410266065b36a4b1462324b976a170010351175351cae0438877ce207e518b96d752c5d9aa81c20114b232a642c804653a9b14149574dd5742c5e2631fb987334404fa842044ac91130ce12dbd39377311e2964f32798d44155188c2720d480107643d7c0f41055008d228fc149426b8a4fbc414825c4cced3799c49c036b136236f9c67ad8cfbab537c4528b236ddabf457e54698cc81208f4ea0aeba1a4af172e0ed918c98ac045be0cd7cc2a1058b65c782ac117ab63db596e400948ce63f604ed2667805430fc00b5877a0f671ab3ad98149e2af8c8e173a31eebb7d7248a24e1895f0f689e8bd04a959e0ff11b0355c0460a6ede5b344f98d0610cb44a89ac1dbfa02aad4486fb371d2926b84d99afeee58f0a5e36ee104ec1374c0056e0f5709408dad78a613f336158e54c0685b875d8caefdf9e2409eb49dd742c7047402e5d74b2152be20007b9a80489fd2370306214825ce6b96c0127cb744eb7184dea121091de869c35f2f2600eacdaf20af049ef5cb7ae1f4b467023b9cdefb520b09fae1bd1009df78a272ca58ca6419303a803806650c57a79a998ee9cd50d6820bfa3047cc8c83b77414fcd865a8f2782c94ceb6d6c68f5449e4835f9ea7450221dd03263bdd266dd7b76362e0ebb0e8c894a89fe82eb259ba0bbbd0fce9fd8b3817353f938a9bcbf4b3f22bb3b64a4d7fe18d63ab9e41f20a0449d5441545a623c6f093222de8d455c60b70e39de811e9e90ba0b9e00b385abbd6b1ee597208d0eeb94b44c1061eceb528260ece507d41ebe087f08ec8a935ac443651bfef5deab2044d09f9bf74de4ca273abd7149198550fbbe5563ce471fc86fb7c13ebfc08e30ec454915aa777bb27907786ec86300316febc93fa07c86e64f21a78042cdcb2941bd85af3df1cc9d4eb0daef49f006347904925318621dc021a7f570d92abc99926db61cd434d00186b888f1263fc1f84e693587063363210b37550e0db9b72af461822a2ba80daadff3525148afeb5f1d1ae30c6b710ce90603284ad4433d65c47fe469431461a838dda6197688beda6155231bebc8d08b7f38d334dbb14ec44364142c54e06697680efefc177c12af44e6c775af32c200e68e0cfa14b0871e7ad7a121f45041e8fa63a198550175ada68521a640cbfe6cc0d1f5940ba262dca7ba188806705b9d9eded26905f1f62053b24a9b2b50d3c2f910806f9b2380c5d70a2c99b5a2c1a41805f788f9499f6c9412b88e9be32eb0ed0aa22e8080bdb45945ca22c5369b36a0acfd64cf33f4c2e54ea4a5dde32c5b9cdb3f2fe9a3f6f3b04be7587670ec6bed52740a15943c885b9cd2701db582af88560a7853861615221108793d878315fd49d00c47afbebc453807a316e5aff08417f3ba6141911fe323567036c37488296006d0ff943199c92e42fd7423845a6bffb80adf92a7856d490038e516e9c992933dd7fb80bdc4249725e40f16422fd38d4f9374adb927918166b21c74913eca3c74118e662160e8347da56b9fd6fe99dac63a96a0a01ae6a1e381a73bb124869710279ba323503676bcc1251e1e6703ec9e85c3635beb7bcdc045ee51c0e001181ae700d668647b6772ca0fc950bde1b962190cc935143a145fd755054bc1231edd493643126580b08a2fb2b781cdb8b9b954e31d1ce1c7ddf6c30b665a4685f187f814055395df50122db27e4ecc482f40bf1a58f5a8d5cdcbaca3be12b637b71e41037ad6a6ca7edbfca626d742e8891ad6d1c3c2c7e9a2c98349921753e78288e20def0576a1888ffd59fc6c4e57364c43020bb373d0445fe14d9322e23a1de58080f9632ceb57d1e0beda4898317d5db2d6641dc9af10da1e78c2751ba5b51b0aa7ee1496dc41fa1a268a481980aa251bde11cd56fa14ff24ced58ddbe33e54674333b69a4b2e23c9db94018dd14a61d055c4b683c155337ee6fc380db507178b5febed59a05be00c3ffd0148b69e2d69d8c99dd9acdafa878c2cd000370d09efceefab9f075ede159485d3536658ada15ad3ad11ee57f01c96709ee02b26accd699faa321889d175a6741a58020bd8a9cb7a63ed6ae125965ffc97057efad0e551107f492356cb8ab212e7d3084b295525c3a8692ce943171eba2855e50b981e9cb30ce8ab22b072f62df474aff1a5b44e928fea3cfddbc292dc91a996867a23a6e3cff947668978535062b33701771aa87aca7bb1b6e7280b90822f361366ceae45c504d8d30e2198193b01f5ebea5abd19d7d79c4dfc1b7369fcb14f1ae05f5ac14b0cbb3d8dd5add0f506bf0bd0d60bde5ab470a871c717046500a45f4fc885017434a2c42708306e6b7d14e92af65a11e257da280bb41fd154793c903b392a3ce12a9853c02c3b5cad3019d32d71093197da5b6653bb7dba90d9e27f62ff4ff58c790dbe8f8e8e68c90b260c670a2bc01b19cab53f94a91f0556d832f5a7454c5cd5d1e401feece89ea046c5fc2b20865c1b6a32d9000a286db83bf8ca4a35189f2b3c2af2b31aa24fe5d71f3506817ee551475c9cabe668fde3e28e1c91d910ea08163a3276bf5591fe294f9a36206d8ed6a123378bf7228a79c05b1448b286856025f091183f41507208abaebac280e6edf828429a1fe7e5a889c1930481571678b82e15ba1554f71a25151591aa8a0578f06e732a7f20982598c06cfd2958252b9025174ac986cfaf35c193c74dc3ba523cd0e72a4d36def94af62d42da754996a823ebd6aa22bbf47927c2a6e60a98144eb23797c0073d9e9ea5ac1ddc2d0a2838b8912e82242e77450a8b79aba01394dc4126e948bc808aff7c07c081ff28bf821fd19a660db88e2ac754b755dece8cd67330c6e0a1d634f2445d3f4b078ad614d960796ea12ccbf8d752a89e9777bfbeaab41fade4fa759601712182c4d10a2a9031ea36a57ac146b2272cba79857ab8a0e032ef2c0d52a8633304ce5905dacdf0a18bfea83e781e6ee1a1328103aeaa2645dd8a4016283cb1227bc5fc958ea68a874e90089218de4db70f60931d8a2c46c79f5bf04d217a766cef59f59c29f8c3ec3527cc80c71a2ea7f2c133e297d5b440fce28d7fe66393f9e0611a32203e194a8cfb2b19c16fbc024e58f9b8971a26b36074ab85c1053b686756eda4cae85874b9df112160f640571cc8341fc96c3c501c9fb1c280ffb0fa3ec36286916aedf61b3dae4b459a978e510127898157b3c86d209fa6d12f73c88ec06f07c8391c2f1879263d09c7ec6b2caec6f04edce1edd409e0dc594e730ac6776da81d4b054abc94b5beea8d751336caa5b636ab8e40d31176b2f2f85012efc47e20baeffca04c62decc2698e1dac14f3e0a58d7456e3d2db393efb2f6ae286c0bbe089cccbd0c7499d6a49345e7cdae5f466423a497c3c3d7212c68603d5213fd255be703fac3119d031f7bcb70eaa3278c7a3cec8e4cdff640d0a5212feb5d175a7aca6c3042e71ed4d0d219e6570a901226c1b0a3bc25de01886cfa482cf470879557fd06700c1f3d4e90b0f8984eea3d9127d6f9e7d5f783068d7108296329b04914a537eb6f428c8b599e19387f919c79f0f8e01661eed53fa799f75dc99bfe64d5908b689bb3daa03bdc5f1dab4b9baf80d0d22ede36b4be93e1e724d56cb0a185045484bd0a351d80e4e400c40195d287a5bc368b1eea1622f43836d4029f31ca23ce27567b4d82605da006220026456352a1304663036517514eff8d336a3530f25bd5a3a532ad61c32ba8639eaa93e6eaef8973ef3b20583c9dec9bbbe4cac2379095b36c79d55f964f7c1a2928e9982fb5dfd21d5ddc649e55d79323889609ce3ab9892269de9a0c922782c7b13d032f406202b124b2d0a1bd694169f1971725787a658713f1d5d0c4d52df66a5f0901af0603f49013c4b1cf69e4c8db9c06cfc0664b3bd01812d76f10a98f0c144525b11784c9cf9a0cd243606fbc68ef5f5a285da380460d8f4787bcd9cd82df8f539cc574356c4b1ebefda246e3425d58b29872ba4c9ba1a0b602c16d63a75cb84a88605a05f5cf3a9b21119596031a4e24bc7e33021295ba6c022222f31a0d48646c3cddf27be23a9c89c994d03021d0016372dd6998b260a636ba4ac6eb1ef2fd6329da4ddef69ab42c3c5de12f2a5a35c801dceaa38ac8a18065c49f7dafd29bc1e2d49441c54f4fc0258249c250eed3acee1958b8abeee64573738e96d0b7b69c2a7c0fcc89bb69538277c9ec76c7c723c7cf1c90075f3578a25e20bfc4c27593ce4687554189806accbfe8669dcaafa871072ac84d3918dfee84b878a79aef7ee9210c565d62ad48a0e4dafae0136abbdf4a48b083d82e9ab4245e1136eb026cc1e9e784dad0933340b75ee4579fe94432fd659107acae17337454e1488ef103defd2eb4ca4e6ff9d6165eba53d3c48415fe2c6a2d5d507fa53beded45dc65585a74ef4b6451a051d8cad7d865192064d289743083a224bff64702fcc323ad716257328b172861b9ec86e1eaa20f212e31def9be0e79757ed75609add853775890b62b3f89f8ac55a7b254462e26bd293b00549f7c5c4fb360cad29da9d2ba39b6bef041bcb224cb923638f7a9bcb0048225382bdda6d4fa8fd0238e6ba2a7561d273b12aa770d5b930c7e7ea549524bcc899af2aa243232df7608e712ff6ec7121645272618d67bf99ab7f26f2459e1e017fa96c46c4669ddbe6153e72d56d4f6a4bfa81b7b20954f9f1fcdfd588e5bc75ecd35c70398ddc5fa0352041d48dba7580d711fcc9024cd1d9db7a8dc997ab2a054fab250fbdc115bafb5f43a10beb16657dd69efec0d760a3dc82656bc361aaacbfb7c5325bd078eb09d7bacbbba8f472dff24999f73ef32d5f8f61b67ef0fd000bc525f38d22f2571a7780bb7c37d5781d65204a8757e9b6ec05f95aecc5abc9beaf948af45909a9a3858be4e5e67f198b995a03cce8163f55708856ffa4f64cb19f698d1adcd7b315831bc9ba17bd7a4ca5801b982e679b905026b4dda98fcf8f657a44e5fff7ad33b6a985d6c96a6f0c25aea32200ea1722eb116cda658a1f5a3e325b4fc65c08984b886f5d822d60b3bb17dfe97309842c97c6d5f388ee7f8c9b2768a997855c73bfc1b3f570447a9536339fd2ee17946e6bef6d430989a04125232a5ff3b7ffc648953122a1d64d7c0964263e26845c36080b7b8a5c8bc9ef1ed30b0d2992100e34530c690459381c67dd0504a2abded4c39703796568179fed1d35f8e1ec067a61a7f58ec0c69d172795fc2259fba7da4509503a0fc83b07f96c7b8b564b5d9505a152eef1ccb9f1e9ac56a4e2935306cdce0d0e18ea00fe86b952f5cf262111cf2412b5e430a27fc63722544f32147dfdc8b4c1144db3d82fe6abf7384bdd829599c3db011690420ea4d947b602593907a0761cb886c3f4b232790aaa274d18205f9a4a2ebe7ebea660715320a126d1a78244ec9c8b486b9d73209bd863fefa91ea32b85a9650ba3347ef040bedc277e6bc5a63fcea24df3d6c96fbe613da1ad6cf77765d162cda83a989820b18a39435ac11190a04fbf683294fe06fe7b450daa622d8180e67e7d7ee09f52d2754e2866f091488560b7b404eefa9b32da9f23b3e7d151b22a01832363623100f7d043f2a0ac418d84dc0a7cd450622ddca11f9bfba739309b1ca97c935badd37fa7c1de84ab752dcdda39a37cef2c8c9c625b1481ba7d5666526ffbaf08344cc43a26273b4a92bd1392af9bc42cd14efd58dc91d6cc6f5514dff15c5a2a93bbe59cd003a4bb0c267b8a11e1ed8561f16cf1f94057296f811501c6707e690afa07b61a3201210a7afcd0db04777338635c84679e262169563dc14bcfc4b7205731e98287b1d0053df4a54a1b94c3e5abc5adb881a61e8296aaed357feeab82a44ac596f521b06e8fb52425528672e08e9bd9cc1aa8f534106335b15687b853fc15cad6cfe6271379f952f41059773330e8b1884439ea519ea0323007bcde51d36b70b94c910f1705277d2acd16ec81cf3717d247d842a48929df91ddf3c925ec5fcd57555d0af1c22e787dffc7ff6f0235c4ec496e9d4c859d88911bf8c1322f5c29aa3ce851d3ad7d850437c16fd65747e2fed6285aa68519b9bd3d070e3dc48ebb4bbd11faf191bac8501789088f27541bb7958d004775f5f6e4b2f507082915b0812fb71a8342f5332daf767033a75d3e10a7c8939c77266757c8f22fb33903658e03833f5cd51fac21b51ba1429d32a4d1f1aaa9d28d54c3ad5b5172e06b150312d3a8efb5b228877a30f7f76a965f28f39ba8f1472a14c4d0b9a6fbd0bc4226c7f864da90e51ef1a285bb0de66b30bdee34e89021b905ab48d26590fa6fa532215779c5a45710596e9f09feae82796c505ecc3c30b6f8fdfbc73ee568c01d201a2a2384ac2a35fb32c5696f97ae08cbf5e77a0fa1a5dafab6950649990555e33159e25a5739ea086b324d39eec76f24ad4c6068c684feebc15e2791df19521b02cdd59408e59e8a35956d063a295bbb200c448d6371ec41777a80cda9874dd4c74e9dd01dda875a53789f78733852936c33f689be47692b805b8698241fea8c4d00abb14150a212a2b687a4c0ad46a8efc4880a54fbb8480a9f06dd6dbb01cefdabe1e508b1e3b898451225c49819cbce815c42451f83e503784e83d678ab777214661d26a279927acfc9988d2e18fd95d1aee8c573d417ae262a20f2327ca127968489ee581818e493f93903d7c7112af476c1d48f673359deb96288b0642d841f8ae88406b2a2e05b8de44733fe56125e26cdeb9550e2f5089b64a7be809032684304d85c6aa6bd0a19511cac79a0a5d96ce006c0c044f16dff59fc8e19f5cf559f0248d031c0b5e47a499a41fc8ce33b131b78500cb9358ba7b5de3af38955ec3cea2836887ed673732ac16f007d13568d9e9332e75b0913185a6f4ad48d0aa4814b60df013eae193509cd60fa783426785b4255e98d7e928cd8b41e189fd5f2c4bb8cc7b497ec286ebb8f7832274086bdeea8d66990ae9de9cee2b3e0769ff8762ff32796bc96b2fcd41a2f5a49068684fd530bb0a08b428b11c4e3addabdfd5b258a04bfe51eb2336a9f3030ae47714bcac26c20480d97915a705673c46de58ce8783a6a4c38510d6bab4957e434ec350ecc1763710e8be460351c4049bfe876e9a9408515f8347c8e39351f5575e00e13ed4f893925c65064fe260710066dc1bf020f74597a1e2de87648896db04bb969a5a9fcfbaa9b62cf8879bc56c9a30d96ba7950fc940ec9914260291eac414a0d5b8b5176a7ae2efaf7be17f065649ef6823c8a6cd5233515bb4c474d3ec4add7cd42c22870f5537a190a660328b13bd83f93ebc9f379b25c3f743a5cfd557930d45e03f8f426833a85c36cd5e2cd37dbab582d08b8b8f28648310b4b47c084128f4e2f30168b27485813f69798482ceb9e08c2a62511a88cecd8aeecc1b06115b2d1293d609319e511a99ec607fa256a2c9206114075d82b3f10a2ba4c0dbbb24c95a415e986c383b5a608f15494222a8550e5656fd36c2fdc69822469e154bc4a565d5c39a3f3868b89f0cda1172982e1784144651a7439ae32a3ba980def3d86d57452f112484a6c860cfc147de5d2932923792fc19bd464b0ae89021d9b8c9c1b95381e8d2f35c8ec5a53c82c60573f93f64cf51957f029f3d99ff94f7393a1225e482082ea1cfeef3aae9938a23ded96953eee4f3868c33f06ff8813c495158a6b5385c9999e06a9d391ca392bd20e1e88d689a634491a7151956064cd7cee4bf9ecefe6f8f5648ae7cec2278ae76ad6a1492fc222e0077412459d18d6533f9281970cc220b150ca0e6547ac85cdc133296c35e624d114a8f1f44d9ff47905e1164aa6fb54b2827dc9d40b18b84d0b4dd9a56cd6bd0f012f462a9f76e78f16a29840db216d268314088496a4f4890885423c88224d47cccfc7e43a7317a5b2abae7d603bdffe232fbfbfef23c24d6a9143203628537a75123e908011a53239e1942189434342882eaa8165e753cbfee86c63208d7eae1003a2ac323154dd735ac83391fe2aed361bfe591227ce8629cdf2b0722df0b00afe8df8ad7098e31438dd3e7b9a6169302a3e728b62273abb8d202d690f8cb2900d9e59f85588ed4294e1e32e98479f14138be9b61ded19e57ceca597e67735619dc0550e6fef43d2b9354f99ab88c0a6ff93daa29731247a9474b08ac7be663f74b7725fc352a7ed757e8c9d1f867fdc248aa3952eddc547318ed2d6449587ffadd8bb2fa42d7875ddcd3750f120f0cd43e0d6a85472a7504083a310b2f6e9c65af60d66bcb7ab17ab2edacda3a2ed8c405b30c8591845baf43f01ffb10f9f7bd745c3ed8aa7ee140232bdbe48fc4af335413d93e649b14963c532de96c03e1a79dad415ca8907eefcefa7ab15740104b78b538ec5bb2243b7ed26b9889f08e535492d1aaf5aa7b2a90baca078fcb84557a539edf5e54c7141e0312736237c151653897ee481fce1f548e5e580879377abecc18c882697a5775c0e914b55e9f7b9e6f75b46dca64070d75e83a31a83e11b5bef63a0fd59e81b56cc80038ffa18747ce862cc3e532953196d233642f0f296f0574d83b4b4ae39404321df1f47325e84f7444ce86b222dd1da34c1b2a6fe83e48508f58a3f5e14675be470a2deb4db01553c84688bd904b4e74ed59b33f8bfa9ca6c267080286c306720f3c1f3e92288b3faaf171af1701b323e28565e26df74a42301a8be568dc0d0166a47704fd78f6d5eb64b85a0655d6f1cdfed809f56ff91496fe3d19fba1195f1d05d2e15d09af310f5c849c9f4c06c2d42c84c9f0f3f08ee073007bf370b13e1b432f3884e55a254c980b06f0ffc1545414b907c3ba80c1adc2305dc4d13ee8c51c509807630e695a02bac5093d13d69757fb99f8dd7c48ef108d550f6e757c6bf67b28be7f518c1b201334712a85aec3471e08fbca221372186c7945f0561d1a32f6e3d34b5dd4e79f9e9d52607d24b6b0833589c7c2087178b226224642908c4691b3da4efeb56b16c586154e5a1a95ea1958c0da2ad59a77349eafbd1c317e280a43f35eb07186eb86024c31ddcd162e364f373b8cfa0136538fc436149166b8bd504805dd7967840063871072f8f258301e4f3cd6fd3b8c9588fee3fbb91c008d27c536c9cb7052cd26fdf018274452e490d7149446fe9a58ccdf39d49cc2a16756370bda5cd3d9c3e0228c2e17c948dc5f49b3d81f1ffdc159985c45d7debc7970f1a00fb20073ce364333167dcce5f3a8c2ee5a1d7e94e69798d47851a7e5b197c32ad35be097168be7b946fe03d30b3990abf889c101e17055d8c6374809bf16b45a0f7678729dcc5ee905004cfd4d860c9a954d4f20a53a8959f8b3c44866b858e6c5881386b94a53ef33b432ce82955a355ea0b36b329ae2e731ed6cd5f9cd74973fe4bcc76fe94a542d006e0ffc993bf76c49ca199ab855e2c450e9b5a88090645fb5cb5ee130a3e82ff523266ea88267a8249a933f025f010b0780ea7f4bbae8ac62fa6eb94455879b3436674c2a0dc69962c71de2f0e5c742ec903d73d15e312880cd03da7890b7c68c2e6395011c48c70e07fa2193230d3a565f663f18caa574625fe8a7e3a584133e04cfd1dbf49b4a1dfe6666f3348e0172d852f97c66583b1ab8fdf9a38f1297eb0d393ba52bfa0107f7f58a59524acb4cb240f4d3f5c8393946e94e0402e2c4b92593b2fbaa0a427cb56b56d10af30d6f094eed88f727ea13249d0465711664810a60e01a5e7d62a6d7baa0e465e157a7d82f46c650ab85ea5b11242dad7a674e871aaaa0983530700a6356b7fa636404109292e6db063900df1d2d308de276ce8516db2c8602305c587f8f89678127e7f8be4e01f8a1b09217b958d9d6b6035a25bc5579d090d4c62516f0b66f39888909b325b9213c3dd113b917617c5aa81f0d68e47251dbfcc0021bbbb910686861063e9469d3ec11ea5cd27fe82750129a30c2cf74e48e96f3313ba9a95a9374be37a95edaff2c4ba3fb7ebf1022f734f5b5f4830b292a748ca41266e9ab0e90cb4ea4c0fee740bb44b9f27772115cd7398b3e10c52bd8c392ff15f5677ad1ded937d9be5d4118323a9b7549498c585ed84c7979db0d4657235afd23b412754c3f4d1b15c50a7ef978318dd64945db8a194b73115fa7e6df7866954600b279d588b771e0dc68358a03bc5460130211ee2ed69c92f23e9c3e0488e3d11dc52d5ecb23687a8a6cca36e9b3503c1266bf8d42189a2f9c84dc14078544f86873ed86c3c0d31cb233cfa80e6a54ec32399d33fbfb7b9aceb7bf0ddc94bf80556dccd23b423ea33d62e31a429879699de2c61eb4074b3332207653a9a8804329e150890987fe373fd0e01b2de141483fdf1a1e3e11bf0147e7a87fa0df77035fa8eddc524d3a451ea013345a02a055c406ad1a324634cf1486218218bd4b3c4886f49c15a8402e1c1a5a0a2a6f67d46c9ce42317c6f3f1ea7c08b552862a149d9d5917ca64ff13c66e85704fe6de7e07932fe6d4fa6d58c8cfcb00c52b9cea4c73334730420e05001d7e4ccccec47dcb10baf928765d17ab1c4a806a0450cc3b194cd391c739bc4bc0f19782f47f29622cd9f95b2da1e3ad8a0052bf4f0013cc3cb99eb21716007abb27327ac665889f3eb124692c802c64e9f10f293d9c620fe01e4af983183dbd55817d2cdf7ec5137dcc115939b3ccac642f0a0af5b4f41e02e3b4c419196464da4142b1193f057d59d13750742d1436be8053882651abe9d8ba68f3149330c0482e743dcb23972de8644a83d736cc15f99f8da99f2379d8648f2b53c719610178479f0bf0c5829e67e22350d153bd7b1c1e6e6cfb6d4a5b81cf3cf7492ddb9ab11ad0ed3aa8b6adc4bebc7608eb3cb79f4c03cc36012ac6602d546010a9e9e73900081921e3ffa53201989880240a83e913ca1520fcbb611a191018a10010fe6fdb4040a40cde4efc41b22b6db85186bf68179d6e8752f887e4c1741f87a6ea27b4c9a6a5399fe786b69449eeed16bb02ca02c102dd0ac43e19cad8ec2c84c645a1870eec8cf6303a1083ddc09509c1b94a99db9b32aa43dcc5c163508fe958120799fbffeff4ca2036f06dc419fe908233498ef1586fdb9c7327eb3f39feff4f6245b8a66f9c73cd763ceecb0fd5b5edb787ecbd7739aba3c3d721f1e5165f7b1761dd5e03645459708c2d57cf2aafb4caab4b85eb6c39c42cdac004526cb76d9ebbd1db916bf6aecbcd1ecb4fc922b1f42d2746ca21ac24675eb2ca1c12265a615c5e30b9c85b0877a9e33669e306f4ad3fe79c732d65105b9386ea8e3e5c3fb032c8f3b8b2138020dfe8de7bef1fdb6b13291226e53d1d0ad31eaa6a6134e2342c8714bddd73f7ef8099af0861f7250966ed71def1ff0f04bedd649c35d5e8e4e008811e19f16c108ded2022e79c97dd80315039a35fa127e0af8594a69ac95368269479ea64a4db846b7b18690fe31130ba9c467db84c8d21c5cd18584fd410262b7138c46067101b4d3dfa74fe5984907db933173b3e23a611933555935adb1ba92710ca9fe7746d49f853b54adbd8263ef07f7e95438642e9f0b89f1a75e9ff316b650f261f31da023f5051b26f18f2b9245a7174220a7f4abbe4f8ff3f223d92a1fab21225529631a73334556a16553581c26cc6a9c6db24ff7c3be28476816b350ecd7059512602d712ca8aee11699232f46ab8edcbfeff7f97b76ff3f6857ea8e9ffff8f857268f0dcedbd77edd10c62a3754bf557a7627979948bf053b7280cecc54f082a414c35c1b29619bbc48ce51c4b002b4e55071373185106b18d842169dd9887109fa3e98e73ce39a777103a161344554336030dca9345b80c1b9b41b4e3bbc769b6cd8d9919a3222388cbbb13e56524a55d6a614c01258b7049ebc6bcb69b1ebadcccd63a50d57e775d0e216689db1d17a1414e6bbab802d69c680731b187cc14033119003cefd5439785fb769fdc35b757881d4b7bdb7bef3da4b8fd97b97137f4de67310d02325b6606df6e26fea3fedd01e630ff7e02873c85b3c78d7ab8e3bf9b9ae8facf4310bfb6e69dbc8a1d7bef3d3b83d87aef58c994df1dd93b27ebf36df39a0878d487563e549a456840be8d4f9d8c7bef5d0c4a06b18959d1ba11c084a25f51d3a8b037ab41f245d3c422b48d244e5b9a003e4203db09f0d5789dc8c8de7bef50539b29ec889dda2316d39a8fea8b2f9518261d6db530f1ba136bd79d5db862d5f99493e7979f9517740cebf312ad951e108027ab864c926525280684b5074326c191738e3678cc938b050ca5ac284b48d88576672bec2df1ac31383b05ceab08956b35c0aa3e9717469f1d1f4277fca3c6e96a0d4f88905036b1f7843153c29d15e61496d2ecc5d6d50f698b6cc7374c9c150014d90ef3290a9545a521b9ebcb27d3891331c0fd32361ca48c5dde7e78d472d2ba3c5fdf162e3125358e8c50c5330e54a7148eedb26dd8bfd6563612407ceb8170467e323bfc8067189aaaa185c74a5a48cd2cbc20665031a5d4955552bb74768b4c0405f667a1e26114d9126e1b53b42b8a880a4a43c760a6901595d2ba596162c3db2c25b2bbde37c400e7743d8654ddf49a89b819d8ff3fd8f676fbfff898e48a19e09c734e026b7bbbd5b0e09e852d558f5c5be38e57b73adba79427211ca6a5bd92a4ceb30f0aa1f12834b4b17d9a6ca93009e37d9faae8f2d8c3e34c5c63f9ffdf849d416c34ff4de227bb9535d4945c4e665a0cd611ab066b46f70575cce7c016763022e75bade01514ab157a8145b9d07a368f532654aead625b0983f43e80627cc5bf7c414e602b54ca70172b065153ce958a8d828673ce7d7a223e401deeffff6b5aaae685ac71e2fac698923afd7e4968bf1fefa75ad0ddefa00d4c83da04c3d0b07d999e55777d58c27559b39a32b9bc9d325bd93483804ca8c05793a0aaa26b0a9f13c1b59f7d62c48c89183ee215f68584a798cc928f876c660c66a3c8f5128e11c55a124e540821a18bd9114c8f0e2f5cc9434657c60c1c95d763d93a02be110bfdffff0067101b3d74d3c1d351d5e2c805cb128a198322ad52e33e19317572c8af19d3c06d6ed79bae434e96ba5967c7246400516002b14133195e972951c95fa5d810c3ca6ab1120dd93037ceffff9f00630cec598442339a1ad282b1e29712a55a3ed22db794bbdd76cd7228887c6989c27281a9c9f21aac7ad815541c8a028b12db4a9e69950601d9c939e755d9db4dd6729d386bd12a22cedab0b98e30232f2c49eb6afaffff49e862cc831c61053e777c4c3e2ad73e62b0052c33265634dfa9aa0b788aaa50907c952cf78dadd9b19447ca809cf3ad98012e5895d70b48a5528267870b2b48050a74db1c6d59c8ace58e4fb372f8ffffff8767101bdd3fc7c3144096c65664c75c25d4a648b52e1dae947afe79928cdd7fee2c443c837b39feff85353df59afd8fc5b19a92e6cfaa1931deb65c69447befbd932d9f47d6d4e59a20e4162386cb598b73edd4d1da4b3245d4c98385d3a43ed14adc48ede4d39d73cebb5a64109b31a9e3c3a293e89af6be819754ada3699aa6b91c7665629242caa2c1f4fc14457f9e485ecc5c5df5ea55b763d7e8bec606586fcd8aab24d85480d6040a78689d91c6e352e78e6f9aae02acab37f1f725e2909fbdd97bdf39b9a6f73322626e06295f309c911f624b4f4e463b62f74f4b5da5b0fc4b9072369580cda445b5e677e924054544eab055fca8348a385ed99573126f0357dfa49ea41419a813e03e49015123a160c9d9aa2ad1b4914fb34c2e698ea7ab6d04092aedbd07771d00b294183516a592e9c609a8c6d7e3ee9c18410d6b2899dc9ef9a02faf51f6850c3c2bf9008148624316651b77dcf85ebfe69a03ec3af7a8ebad8bec2ebbcd2eb47bd4bd76b174efbd772f052ac239acc63786ea6a4739add8a7c79504388a852899bd840b2410bc4086e9dc1aa893e7e66e6645a2c81ac686507a3e1e27a4ee620015487e6a30365c9dcb9d44d23378dbc07ac789b1c99a39063d535690667f325a4889cb4d1403151dedf8c7e6263439498dbe5fca359907eac66946486d6a0f7d301cca3f1e50b25a572b8b5636e243a3ac820cca6bdc2a3962a5a39b16d5b0e1bffed8311e5f40dc83b33db66ac61cac9f55b59ed2c692552a74f94cf4d0444956783b4c670b6de4703ee122205c3316116efbffd756b14c0e6a29a2652b77756ac051aa649520d9b9f628739e5cd0823c36f416838c802aa55d79aaa1a9b2e2f42b533712925c67dd24199e951df47151194f55c7a0bce22ace39e7f4ae8ac3ce5e7d4111aa85f4ff9fade9ffff3fe1b0d9889f6b2b4e06588ac150dcfcffbfac6914a654890c16d78e782b7cd8852d77d895530f56f14c39c3db405fd6c4950d6d6812819c73ce390f7981d2bb82a8710acda854f185016fc0a4d5625adc9cf00b116aff6485676f4701c21def92531a48e2b2114bef61562e6e0da085227befdc2662d48c6aeafd0049a95c6d4970005391cbd912571a0c6ffb346baccbb319af700827b0693b5098b52523a8519480fd9c91b82d18284e843a69e1133fc3ef0ae32bcd9835b1c3fb2504db199440d8fcff9760f7ffff5ffdffffdf374321e218ca6138b7f49fdb5b7ac300ddd2a19ab24123d20c954bb26030480310ba6d4942a22e69a8d6d5d4241fde7be75b9cc80f392f6531a1d8762ad6dd787a9de687dd12e7a39b7d2f21b8ced68308b7a3b46137e32dd2ffd3ffff5c2adc1c45af9245d48428fc43031bd3dee781856f761b85a1d104b35770dd9cd3f9ffffff08145bd32b33b83ac5a636f4927191c8bc6a5ec8b2524a757550bc78704ec0790538cb0f04c3fca3eef9e63f6e9ea5e49aeebd6efa62e8103b1fc0326b7140b32682c7e683e27c6a1c34cfa917369a345b74578a26bd37450553b44d45dfbdc39c6d337f8a82d83a5539b96f9501760e876d03608e76ddd3c652eff1776fef018d21a8d48f767d3300c403b318080400c44018c9034d11e50314800315b488ac843438303c3c240e06c36040101006080001200008060340814020082687c88a81d9004b7892e68b6ff32ca233a401574e9cbdb25ac87aac7a9c21a03282d12944e83a9fc24a51229be433fce0d89646b189f012a5a2f16c622ced673669784eec24c0b1b6bde025317cf510454dcaaae98b684312a9dd408b9448143ca452b3c0d2f4c66ce153d40b5b1c328c1544349f36478e889c4f550b351d0b1d0506b1a2a0a23b5bf079f7294e80ab1375bff77fead98d6fbbd32785fb34c1c88cb1d7832b8586bced0411dd7c0cca60a1c6e007e8a5a22e5a6326973b47854815fd6a5de5202ffc56c363f431f6ab1ce4895a02312848138f9362e39ccda71b15bee048c3a8130b3c73c5d84656ebec0431ad06dc409fca8aba16e5ce3c68dd0466ff8d747c1c2f243b9b423401a6e5290728d174f7d1a1bebd90a06fe82816f2dd31a5be458573851e407c60da24a33a3921cfd8d621fd7efb74b1fbfb2f6a2f092f0b2229e14831a9d21d242f8b4805b17e2767a9de813c8d1c4910c5692f9fe23a189f24a22a913bd8837d59ba9f8f9b97e5b955bf9c9734442ea94c84a79b9045b8d82da8a0dd2d820905a278f718bf2c3f0a985df55480699d0401a021b34ba93e84f7c06ab3faba66f71835b81cf6b2b30ccb6183712b44998c9f6b4233a114c2cc7acc9d4150d413abf13900808a8ad5b86a946040ad26a9974db9c26500a338fd221a9f1e2243a35ea4a56645642638ce0e4721f55c5e648e3e05289f5844c926364a16dca6ac4b1900e253e5cdb1c854c4dca11bbc85bcc20b82f9bb3a3778714cacbedd8dc8420d747e034b44f60f2a72e61079bf7e1da036530929299839dc8dff3f4f84b572fb991bf5dc2a5b146bdfc4d8523e418256ca1ee5f1862950e6c896a609e2f9a36f31b9448433e0fe43e83a20b709f0d8e37a57de666a4c73a3419a0319e6499ddcd5590c2bfa8bc7ee611046539002dc5ef79911baa20a8e2770fd54897d4413bba0112bba4cf35a652fb31c8594d8f068df5c64bea435cabee2dd01ce1cc825e2a1c1ca1a2bca4073db489e81231fd43fe4449f74fc1974b8806dca4c153541741bc2edaa5081fd46d1a1431950d2011d54cef26693fbac08ecb4d310f14e09f73cbd26adb9f28d5172ddec7614d23cd22da423a8ad1eb1aaa62c21bc1528eff46bb4fd396a3d3f53e70922844c025322d57d3b4a14859812c9651148e2d2b0133df606ee2f3ed7c48046816aef724632035e48f3f25823ff1068664881129da7009641796fe6edc395bb75289c2643ea60e2d501fd66b91198b8b14771e2cd93e5e493c4f8a7fa00299259edaf1c1062a37d234d00d2f5a6164610175eed87d9b5e157b306b8da8badc48f10032805e6970bc654692380a23719e4d2acaebed1fb8c9b0cb236d4a8365ad56072d74ab6baa84183d17455ebe50dbf2e111fab084ddb7968cd8a5a0699070748f4372493366f2b3aeab52aa514c04807fd32cd11294c3e42c43b03640ad413bd0aa6a21bf8a3a6feb2cbe05458a57aae6eaaa26c962aaf62d273122f5a18a2ef8be1988174de4ddf22c6bbe7919ee1a07b3361a4f2c64b22d972659b2d30750de21e2f47870b818a0db48d6b892fb7aa8db9b17019b72ae2f9869558c1ab69f1fa868d44a5c227e4e3a07cd8c191c85f5a264c9c41694f4b36a69ef0264e818c62e5c5ce4bdcc27b2e60e67aff0d17d17649fa2a7af302ac8f15afc9cc14009ee9a649ea5b81e2b8fe2410c8fbd71c03102491ad601e17b02b658e19e1ec11e0a822d1aca9d5d3fdbf94c5bb0e4f92b8132208fbada6b6ace7bb449f1aea33989aece1f28e521df3fd4a44f4595fc34dd82e17f54f27c7b291aa8f82b4bc9fec76db016ce9a6e4869b5a566c900c45953ac70e8b87791e413632cd8762fa5adda1862f78c35c8a3d33936845825454be6a77fafb61a922ac44f0b4eac4333962705fa1932ab3e6929b3659d95c127582d703fe67218d371981cf59b6f162c18ba2be9fe18797081de33e5e9cea3cc7c4f600f9b9710174368fbd3762d3bcf5596c10cf45ffa128887041c16273274a8745bb6fe8a9e6cc44fa70091629e3104ac06a3ea93d52833e358594ee14b4b82ee07d5f4cf963c5bb397c655e132c3ac6c30f792e999962bdc10c684862d560f43eae627b43d9245645a77fd48c98256d0cac7ee83d5e2a2b1d10653763c9f6c82cb807845ceb8bb9d4ac4f5753d2a6ed4111b1bf1dc871403868fc66e13041f2324114e33c8b5913a109b2872ca18a6c17018bbebf6bdf94b3cd73ee83c2427ac3adcedd417bcda125b429465ca2e63b8a81e8f97ec8bd84327768883e1187ff9dfcb55e04c66333407868cd9ced33b215ca31af490eec94634c8e5b8ebdbba5f573fc5fb5727334938cf78850c60a2c3312f7b9a9ddb0cd75c836d077a5b172890ca787a88dbdaee82e60b0c332ae8333df9259ff8819a61990ac8e716f5c2804d814815b7667b8ed87173e77fca0e1748907e272ba3236f21fd1c093ce7b9e351c4054bffc1c90ffbce9a85dd6062af5c75c67cff1a77d9a1db7ccb8aa048463a75c7b89469a4c62cbd82c3c4752d0b1b1826955801c56d2459996982720df1b1d75eb9d267753d23ede4789f04b2b9af258021a1ad2184ca83755186364fdaf9b2ebbcb52d40b4d10608be2327c2a34a6e04e862f7ac2ac6944e9b9e66d42e1564a4d615179f5d04adae7d27760b8cbf23b9b65648e162aa3b76db3cb72f9d9ead24c42dd61763f86f399e5413d4405a2c82c6383e823f1cc4f5f2c9cff3e3c51dec51a9da30adbfe5c19b208d0700ca87b2ec40377493826d42d858e274d3886136e9195636ab26b822e2015ed57c25a791ec35fb66d588ebb6c3df99693065e1e8430929ad402fe22ef6643f1d1890f628d152d7a3e0c655ca6a6e1e28e6adaefe6601e7a373dc2624ed5d9441d7d4a4a6a891dec10177150413c33e21739b4d75458134df60d1c453064988d065dd5fc4fb83cb0f59d3d024f33c01cb50b26980963212aec4a9208b0103e0c6630f81aae9937a8a6dda8a2a18cdb3b04913fa97f0f9358c0719ff577650a563026120629ebc5b679239b8e6bffebd45cea0ff9a0cf7edb56366e5e52eebec1e4e3c3456d4a91bbb617d5fd21f71354b409f9e00a0954249793522e7742f168b73bc0ad4a346eaf573086a56de51369c51f2b090d466d5cc0ad8d40c0487e8743238ed29830e4f30581a863a2e0843d160d04ed0c5fe7d5540879d1ce333a6d509dd9b944d6cad4e4f7557323642287a78a27f621ac3a105f4e8f052743923345f6281e41400a5240b6340a8724e1af63824044501854d62c560d6f842d03c630cebf84633c63f451d8fc3d1c7bb8db5d033c93e2863a22792c800116dc1efa1449d6e952f18a0ccd9c2e62b21dc2c70d03379d00f0cfd64f37eaf765ddec429d031dc5cb0272043a9b9b872f76d8b226028d069520495a64e08aa87a922a86bc299df72e7befe7eb6165217e2d3ff1da59fd13e16005889234a480ba3a83cf241b49a219a308bf7a790100e6a4eb72a41b779aad4699bea290f5e6e4a1f60f270f4feb4c8f54b353b807bc551a99600b28e648c7f41c8167afc60b2bcc99b5337f5f538dea09f239c2a25257a0a9410371bdb522f57d7882e91ee74ad01d6d1d0a13d552c7d111ce38878bd86156c674592d5c14aecf1cc6a99af9064ee5c448a49242fc27047e3ae0998518ea66c6ecb25827f3e3d7befdc64b016c1f1f4488cf1c93515513a78c7cf0c23139cb482da0b9f8ae47cdc188801f031fd857eac932da7dcd7715cbb562493554a7282c711cfc3a6de89994c58ec948222d1d9bfae130f4df8c0ae96509099db8e881aa3489d392fe1578acf5e7d7cebf6f5263d85203fd072a352e8cb2fe8995b56e65598b54eb20d55a25e699f86d0cb3313d04105c54208020ed87e2d8c35240913628f8b206046924def1c11683c69b5ac7203871a9c6602a9da37f7790365cbcf13bb5c583277a23ac39a849bdef6b1b9499842b2d116e8348e38b961d2ed4fb3871cf4292b8a43d3da9df6a6293ab8cb46f24e05f988ea42aff9aa350b27e9b978b04fb173f8a44cef6c1749fb26a3a18e98d27e1c1b798db7926e643dbdd8e31f7e74bb3296e8a435cb81d1a036cca8d04eeb0d62af2426e64543586409037a53888af18f69b50e0d41ae959b9d2f098fcfb5a6502454112890d078baa5d68bb4a8b8227d0f6aa32f6245725380722065413f7318089c8c7f5ca16d0a4dffec0a88b65e203cc72940d2ad5e7c016b6061ee0731e10e34d27cd85e31f4784ffd6450b839fca3cc5a79b1c29ebcb0427655c074a7912d30b153959a311a1ed3f1d71d321d781ba911c865c7fae6427b5e38b0b70204538d439f22476080aa8c0a5c1e4b7e73fa085ccbc639294ec49123e529592d8b291a09022f003f7c85d9185e407ea848fc87501aa13ca4261d832f4052c4f9fe3d9296adc931660964580978fcc17e8c7f492de27c668ec7e9192f11eb47fe239be02a7e185ad48a30254954e0203c39a06028ffae69a0065c8109cb04d90dbf94564917efd4f249c687d37f321bb8e89cf04727c33580ff74c394e6616e7ae9890c918dea8e7c250b2c85ef70d443f24cc630bb4b9061a8fbf2a19cb89911ad099d6ed4555926ed648b5d117592b2246ac6b0833b14840b763b6d2e49ec5641cbd58f6e63de7dbcd55fa49322df99798b66811dbd3f39b80089bdc4f7ed898c120300cd65b098cc361b4a28f8595ed45e88cc0b882b4601848dfca70fa7c7d9249d689a7177b3fe20a00cabe1c2ceef3c2b0261b30d620b79adc79dcfa48866bd521890f83cc1a5a0cb0d4319bdfe854bb12efa800cced284419099dff07840a2e6e584f11c1ffc6220a59bb9dc3e1a2b4ebd9de3e922c2e7147776f1af89929c9a88fdf896fae040063b1f3c6c3e4702e31b32e64b078282084e6ccfce4d904299089c532db8c7740119a459aed7870e9f5f3f2558c8e5c0b80b47838cf848ece8971347e651193a7d446c4b879deedfdf6ec08f25677a304724ae3e3d5c6ff94af6e562bb2dc4eb2ad6e1028d3442d93aaceb4a523061a8751c49f81f06caa1986a78d50039adf280218213d2850bc6f363d21c0cbfb70cd4db61ae90a7ea263e3694ed65b92d977c7355be5f4064c82ce2f42f103aded94785ee716bc3eca69b86ce396f045465bb5783019ef9dd325e18bb61271b0517368ad976953199a9cce42fe5e220761d0046eb2e7f877b9515250ebb8c7d558d929dc3c60ca0fa4d58cc4cbf58d785beb2780c97622b0040d565fea91fb14e70e380740219360c0680779f25a8fc139c298425dc96ebc3336476818252df5a72648f4ca153c40ce2ba749ecb0a2bef726250149327e6439f221b69a32ba1c33b24281c6f4413766da06907243a4df10ec811700aa5fb2bbed239a13557555f9ee168950821658bf84502d1a9c6d7c597b2d510bd35c7a2eb2b2cd19461ee13a176fb133400b4dfc6bb13d4443886c0222081f168048d3e3a3a2fd2da4374bf2c63a550725227c0409ff68451562a289aa812fbc310969460486254d68649c08f40befe4faff6c22c18464ed5d20b84534da15ad70be6c810b6dee0afbedd72ca9589c6c5f1074a3809b6f828f5ee50f5f89a8a14014c44d91b186d097c6f6db83d2884b07ebf6615b60ce4a7da53c2aa173867c3f01fcadb3698db343681b765f58fbd2e7d635270a6431cb893c7fdc01b824c38586090ac14da51ae45548979b92494190604b6a2a60d2ca31985c6c7fe39791a04fae0205ed93582369ba480b9c030ca53fd38e53b02dbd5438cca5b221578f949921a2adf3f0401c4ef34cca520c2925cf1fb52e22e737f426ce5488c4cc4d88671485455d03ddca93e0d947598a29e34af95b1ffa59909ab00819292c4222e2ef84726d815e410a50bfc2af8ec196a026c1012b16397a74498df975c013d956550f45c6d481f311db6140b0173b4b24ad46638f4c381871c7cbaa179b8b161baecc81774a8e726660e57977cf1d9889dae0ee14a772add93c4bf4188c76f88c5c6d0244602831308ccb708dcd177c5064c1e758126dc211484ce6bc7d66464b1c51afbfacb297ec8b4819fa768db583a72c32d678bd5c71fbcf191325fde71ba728c795634695ee646a7a366270d0aece502c23c9fee9b3412487d0b6ad7791a9819a2ce6da9233a20b44919346f7de9a0326a41e2b44ab6b53006b3deaae56db9d5126dd428f42e23f4429c3a17cadc115216761e240b0eb1eaa8200e93e7e8d4aae4e2cb92776c27f07b541b4fb8be97baf4f770e9e28b298ff02540d74aacb5bcbf4310094996e864bd69d919f86c90f9b39a3aac584b3202474846769378b85918b2d3c9b963c75197cfd9c94a4899d2ab54c61b58102c0455d02ae14de2df6a4b16157b3ef040355fc2176f86c2568a96b406696f308e8ac4b81e9d247b3f1d02a5df64dcbae3fbae5c022aac8892290dde1813ab4f74f196ff5f242243690f05d0c09223f03491c0bf391339f695e23981140fb157a4b7a4de2db870e3038f87b8e3da61ce1877c4aad8b52d8b031cd628909eaaca0a575ba97011f8d2e8581b5f626ad5a333da22a3d56c7a4985e7bc4748c548f2c6e70823f617c6400b9329a87df522a1eb71f6a3542e3cb71b233af4d3cf2749702a7ff40d64b21ba55b2850af5a649f67f6df8c93efa433092b421f2cedf4afd141dd21a445ac9ce2244d5e31303b556c1a02e646b0812f0db173ba4824865c695fd67b478c73c0acd5689ec03ac538448b36856e077b848740d72bf8edacb3d73d79fec4839d1998bf656e923ab70088318af0aec1672a5baece9738e830df0f7d8b2294dc7780234916b76992386d8e65e1e8be7314b8f2efde416b322241b59a3bbea4e8ba3da242c7df30aa876a5163029e0f227122723897344b1912e6dd3072c07ed0e624e23cf83f478ff02d81632b1c4ccceec7d05d0dd8d9fd3782cd9c599aaee6664ac4d2b2c9155a5c4280f518e7427b7b9b99e4b1deaab88ff4ea48658ee24f3bea8ec01683ca8e78456901ab1c746fea953cf46e1442b553359d21f97bce1e4de7efe00ab363751c7480ed133c67b43303f8d10e0c744f3758e345ad9e81a7339df68124a9e183242420372e30841d64687745b4934e1df6260a9184d2ebbe2561940da2c08c16cec52024a7937e5372d996a4729095c01f2fc0c02a10328bfbeb2c2150e44fc8bd8f683778704ddcd0481bd5dd33b745b0bea50cd07510483671b2c6a7b1cb2f8d6e201609d4879fc305b7c9bd869fc80b190c7df345c767907e6bc165bbe38473b8cc326d32d0df37172b50dcdff74377cd7d462e8e36b520dd00d9fd7303f3bf398e422318ae80635c98461488b727080cb42376a55270e61285e7cbc1a6e458b9e3bd4198b30f124a939113216c53d7765b298b70563c9de88b37adc6c24f1a186b5f1acde3dc673bd27e644689961226b0c2148a94404b2cf4d9dcd9db2dc26d15273dab6969a16d5acb99b019d56e90b1d3c0f6597cf4c39ee39ac71bb62eada7d56093636bc0a2f4eed30d8d6b5e33f37b93a7ff29fec7c0f66257ab170011f5f4913b5818dcafaa85da21ff1c3e4bc0e9c0282619e662007b26af72dc8faee68b64574fab6b57094d540896b51ce1193adbe85688938a36f56b48b2e49f496481668051e391635981dfdee04c39320262e0e76186783e5e4cd8771cad996a20e2f529fdcc94886820d4397bdf72ec43e28a842472385c0e3ebec541b7a716d3f2d66b760872ec9278814620360ad8211b7c43ea1ca6e480c8e62000d9252667d05e0ca53e3a8e05832025849e193b3194dbca864f66828fac6d182151a40a4cab1a25ae48c327f3795d115c001c3175856ef1260b9c8f7d24927f72ddceca2190b4b055a2c11390bb4ce8a34a91b6e63e364dbe65e6331d810edecba1ce1047cfd3ba5a05f18a7e40ff0296627b64cfddb1550e76857fe675f97e738a0d3cdb27c746c08a8342885c0bcf324126465bab36dd92e8341c091b4c5371e0341a3d98e561e47a4bce390c7130f0bd812687042874551f1593a4604c31d1311c093f19b59c4e7f4a49b010a163652c73492ebc02f30b5969bc3948e9120061d3ee4e69cb3a2e3895f0052820449408019de71f33b9000d2604398859d7db099ce0a8f96d44065af280ee09a0e8a49d9956bef3f49b9b1675cd77611806d655400a3b9f46c0fd75a00efe2f1cf5498067d550a2c88b716e96700f504bf6ed8494317fe28af0daab259e4cf13a30605c8d1dd04b2c1019d8461c4fa537f1d1e5c65d77f30d9be688d11375dd9a552fbf32b51a485248b84e28ad4723488c5ad945be417721d1531294cc4d7cc17de8db8c344f522b7d0fe3cb15ffb9f988a908724a39b35277394355c70ba937bd116e39e89932e352763135b427145a20ab2f0d882a0e5f349c70e99428a5e2c00ba48730c23b08e80404b145e696e41796262c1b3afb05372df764eb110df660c8ad1a3a54f9338eea2aac89c7cfca7a635842c9c7c692be25e0ebf56dbf8df28cd90a4b84a866a183ba5c556c07b4c623a6613b47274866ae12d62023d472dee8158907a47199f1391c1dd7ccff60281ecd88b5e8eb8e8234b2e4cae2c52d2ebdddbfd14bb5293903c63b5541f6c864ba5214b52ba97555980d4430cac5b4c4672a28e8cb0baaaf8766187b0a92b04029df9c076ad25b338e0a80f18a9792f1713b8e81d75e7f85a60faf267837baa2c61a981bcad46dd2f3d85893f57133130089f64c1437e398357a6a01436b74712a2e0843b9af0636a41901b02177045019ebb954fa5bb4848e1ad4db26c15fa84b2de35c454d279ada346aea4dacf10c3809a4d5a1124b09f762e235c6868aa500e2b35f5fe7d4aab3df3ec343907c659a10c22cb743830d9137fde32842102674991424187d59b2eff88d175e4d9d9136e48f5583ea5f0584917e30f4bbaef97b98e5601996589b74e49a687d90acd469a198f136f4b511e79bb292fde23aeb86b98f4cbe404776b5760894c8e61e112d22bbd9c901958306829af23ccba61090509489f9f74fcbd24d5d35b988cd3ffae2663fb7a2d8c8754ba491850fe46432eeac7600b254e93e26591a0217d95c8ab08f923c91e8e1b614fde1cadd45e1fa98fe1e4fcde0a321721ff55ff36556a1ca017a5c67e464bac34f1ad414968f847eda2f793d6d4c8e0a60558a2854acd31d9da0063824210bea28ce0ab7492a86feed13d6f56eca94814c6cac92d1d2ef25422703eb715d9db18ac7d4274906b0fa1d5f96888422118bb78d48a3585857ea51631122620d74290d969040980a42b11a5e75378ca1cbd88b3a71237583a6486488d6791246526638a904cedc548bd90f3f9cdc38be18fa617b5797bd6ad3291f92676f74dc098d39d15b74b823f3ddbe86c9d567ee1b52cdc45d48a9369c2a2eeb933046c30e705c872c3921b11685021aff86fa4ac98b0e8f12e1eea081edc51e6a53c345c9b3e4045c00ea3361ab0058031996935e20ba25627338f69c5cd3438a291e9901a4cc2ff939942fa8fece8d2bdcafdd537cbb5e9250fa85c31b5c0e48bd0fd34d28091d1082e59d89392438910b85e1ceda1c867c5930a48e832abd31c8af87738b1cc55af8df9ee173595d2b69ed76e26ea7c75e528886f6aed2403d86017e6cc023babd01eb30eff338a4e237ad03aba699e400aa9eb55d1411d93f270c2d2070c974e50c8509b398835c74a6235379c3849cc5cbfb89039922847c72c721068d6ce2c9500580a4320d181910912ae0b7a1fa36121ad21359c2101daa37c2552704c6d751430a4432c81e1274c3608672689c4270a4214e175bf05579c84f441749b403b290023511bd8b5640185c594a80d964c3c363c5e5a74507693df5c4f943cd2b928927bc69d65fe76cb94a4dc7b074e024a02330227482cb09f251ccd97a67a5d8ec3bdf756669116d6f1f4f5de7193b064e91967a020ff934926b2a498a8532cae495128792ec2944aa329238aa89e82feffff3fac816c2d18562a1db2520a8a555d13992630ab2ae755960de42d2d06c8c9f2a553e870e038dddf83a4ee0a3d04cd683ab1431842ebe7bde05a931315224a3145d0a1a10371dcc212a1f41b728246e1e69593fc9ab430a5c85db128b312ab45151c17665b28304678b440f4090e0e2fcb4c818409fa21dc2d8ebf3c259ac00805c388a5cb057577af2263d8ddddf3cbe10cf9c6bca6b4cc38a794a823600f1f19e4c1cca0bca9b0aa93ca2554e27f2669dac8eeee4b310cf55b825270ffbd1bdc9029dc9470eba1b8d9f6de9b4ccfcb52845b4ef1d697c6677c1105a84a6103418007aa3da4fbff0fb451666baec0476a802377772b3686bd497724a13856429cfa43c2ee6dc20abf7b3bc0d386c0db7300518c0372de9168b1c22759d86982b030db116b910d4a8e492dc471098308e343c7c61789bd3714442713fa8975181e82fbbd83d9a36ab916e2de7ddc61b519f28dc5d6504c8c3a09c66870879d8c80110fbc344249c8ffff7797d1ad5b181b9886355d0b0b0be3bab300c5cad972a41a4bca3211674509a34a30b2341d81b022fd6000756375aa613a99666d7164adb80015f38157800779672b7b73307bd7d68595151e1a3107e9830fac9f5cb4ec5efb0e62bcd970dd81e94445b3c5b0102f5e9473c0360ff74db8af5eed9012987413d0ffff7fa26c1151a8c153da58105d1ccc882912d5a9b0a7251214cbc6e96d8b55aab923b9e3dc5c360b160444231f4cd010b451e7d4b013c0e24a7125e38461624477edfdea90872ab0d2816de3650124f982ba37fdf86bc528695a9e8cdaf5c94caa7a9dbd58d1bd68ae9d973ad0dd3d1ac35238cf41c1b4bbb751322c8509511f6e8c8ce7ff0350d541c9431ce4645e9525d521d58a7856c6ba3e584be8a815e84300502812e55541f4b2b7935c865821da9c4595e85c04b15c4d477ecf67436f15fdffaf80fa7c667a0db956c2ddb57223500434c0ab6440e6859816cecd8f870f8ccb4c510b1b21ad950374e6d566f35789dea6f28150dd5270e362ca921247edb0e2e1408f5d1d53da55d26a242ba9d391130532f394cd27e7d616d2c48954a2c179d5916039fa32b40a1d9b20905ad5a605e7a7aeadcb59d5c633d3d61494cccaf8d058754b2804dc17cb7f1f559d943b1dca46a3bb09ef53aefaebeeee7145df48dc5188b73bfcb64c33bbca8d0e6d04e8ab1c64081cccf3fcfffffffd5fcab648f46d506aea9639f5fd8640a4c6debf3723b59709a975223ad1fc5ca4285331f86eb062ff7f811a22d587a611c4ed221212934f5f73753bf5a4cb5228ff3a83e8594d8c5e15f3274cc58a4a686af584d664f53339a226bd198f592b7c9c80645ac4489d6ab04d153494c90cfa70f7feff7d1b88c09703dc21cc160d6114dd2697facd7adc1cb3b5edaa92745dabbea8d02702284585372b934caf2861324040c714a314ea14a6aa69d8d11995261b333b9c115df52f0a6ad587788f4bf223d4ed8f5047ce3e563907ea826be5d0eeee2561c8185e0ac394c3268cbfbb7b490d3686d95c76ec882750acaa4e684cc9cc990478c6b51752371ec6f074d99b464472a4158579efed91e2e2e592e3150804eebd7716f98ab4ca91ec12a923e7a510a2bc1ff2d2b743b8f7f0e568aaba06667702b33e1a74a14a6a422d6a50d020a1931c1dad00c114dd8f1816bac4a4c9bcc405fbcd4c4107ecc2dbe1ff5feaf3810dbfd64ef470efbdb77e5e91b668eb483841b7315298855af1fbd5105f4b87f0840f08dcdbd7625f9fc6a4e6294602c9650dcfa8fd3a5aab47b83916bdb968063d561454ea0c1b1c518d27a2f2764098b8a804f554c94dc5103b52d052571acc2e06a87ccdfcfba6ba84eeea1a5e72303b435f36503acaab3717d91d63cb887bd4438be5a87b0b77bf90db600e4acbe6507fd518e2eefe5ad257a4ad5e9f440bfbef83e3bdf7dedea4b69544fa90e584a6f7c9ac3b2789272c1f1dc6e1568da7ffff5fa7353384d22664d7c15a98de4b51cd0b9906cbf1f93cdf78614785cb079298f7964060cb5bf3fffff215699bce6788f446cb6465fcd537bb9796827b67bcd720d01de8127df90de18ce689864dc76a0409475b62331f52ec14f184889515f417480d345b59aa46993d652c025f777726640c67ff8d687d657dae12f8ee0ddcfed36f03758f83ec1270d57e90af938ec6026960d79c59ea8e1667b1b684e9e8fe5a41bea310354d59314069332649a5f2e6c9a84dda53651366c10b76515ca1d4b901d8ffff95a0af481b9556af254ad8dd80317677f7bdb770a76cbc2d3fbbb8f305aa6691160ebaa930ccfe4d1c40eeae3db21f978cfb11eab6e8c179f722d9e2166d04b9fac90c10380c6f11263afeffdaba23328e6d963bc09c24cba8307682e33473cb03b35085a54375c4bca9c91982936c1fdabaf74be67c39d4d5218b2be43a987150a40a81e512a7b4a3b26878ad26a47bb2bea51790d798a18cc40d4c1596aa6204bd9b131c1dae3c2a9d96f31efff7da976072a8a747a716352b981db9f770ef8cdf684407264129fcdcdd4540635844d69da6eb48aa4bce9984e7a537745606b6c774ae8db8160163eb152f7a593774aa33eb893b8b4bd9208d57ca13cb94d3224bbbc624878eeca6655c1a5ea331ddeb34e8ee4077f766166961f796a88203f12b465da6224e7f99834aafc7a55bbe8937e381ee06b9e6feffffb7c39582e6b71ac365174b24c1168570fbec292ac82eca6d4f6fd39430c22c9d00a1ab3c4cdf9138f451dd51dddc16332532d1b702a2c6c2975b6595fb90ee49b44657ad160d6331c7281e4b1c2a5e54622c9541a11b58763d2c2a467366672f86a87ee6fccd29dbd234fc3b958927ae1a5e479de1cab1dcc130e74dcaace1cf2cd2920193be0960925834435e8e109db585782eb86bcb5f100e06774c8f6c497f5dd70d6dd692e77ee68a60610cbbbbbbbb270518b2c21093b1b04b4c057492b7cbb468c8dd9dae18fffeffbf18ccedd43309ffff3f9cf793579477d421d47b19be47c1517777777f3ba38f807a089fbf35a7c206cb523c9b52c731ae1aaf180f78d434b1144dddf900a126cbe335364555d92b827bb366ad30987316f47479008ff24d2eddab6ee3ec58edd370f303c615ec8f044f269bab5499d74b8c96a8c41038450291ec08a31808040d0451248ef348d0391400020eb478bc78302c383c342c200a87816020300c1480c10000000c068383813020440e9556223b03e123c2daa70b1357dea207bfe8115cf0e05991b2004ddef243c08ad2566aa0e788c1f03c23a59aac2edca5a0dc78b97d6331b10a4e8970aab7ed65fc71db12d4ad783a1177eb172f2f3a1e190ec404a9e64dd93443ddb31370aa378502ec08da93a43309ee4e829b753b90d439241ad13ccd7139a4c6fe50796352db5ef47c17f262d26b3ec7f588fbfbf51e869224184b2496a014ef86b5209e3b56e844b1f74b7910ad4f1881d15469959588e43458aa58b3d5a40d3d7557d32767c070f751af6188e7c7505a1f0cd48c1dabad8fe2ab776ea515de522483434491468eca381bccda871885913f280f3cadf518ed0d23d0759197a9b213542b6eede1a8c3b6000d569758c848370bf64812bb5908203149205cfdb95fd98535dd86f18c19d779674397868b8c5aeccd78f6ba70eda73293a95e689430af3b447a0276a9fe124ee6b83f44cee75490fe5f425a194e196a6a580d2d4b654105649481a81f5843b6e656e719b8f018774c50d3476cd5672a0a1bf56b75f3d68867d69f989368f483fa26caaa4189ed9c7396e4070e8568c513b5785ccf0f1c0c14816c9099fbe10bc9767acb0e530b89728f4b2c2270d8078786042bf03a759dfe20fa311a2b6eb001f63ea618e47262f84a75b71cbb471fa8ecd0982bb8452f3c2b90139dfd51bdf0f18e3232c4be073c6a5693f3364a1d332abf5a902d0ba3e070302592c6326c7ac60baa671b169f9db2dac29f60131ca109d58e4ea2013e3b335ecc9acbeb084a938cc5230e33075b9229bd699948cb85c7f4821710868fa1e42d6744dd743e32e9c97f2dcabb4e02f663853045019284c9e160fbae6fad8c8b3e8c97b432ebe34e5652e659ed8f907d4fd623306edb507890245a46acce424cdc8f32bfd768c84ebc23a8e6c692465e69fefd74c170e5ee35f2f8d433a00870ba6ca2818312c604c6824b3e48004cd96b18d1b6a359c8d793559a3829e032a2c59d55e25a8514531c9cf9d69202c682a9d591126f1e5106f80a77fbd9c0fe9c89e5dabeec70e587f2a5e41f90918b92c831d0b22e56675ffecd713ec4a0061d16ed84bd00906e2368b219b2eee5bfa990b8f4f81565122e566811f1d747f3d00c20a6fc2d08786e70c1a71388069453738439f125461db5321bb2216448fc29c9e7fd284956171c3e408b4007a9bb9a4ed9c8084df003c028a9115c1b39ad79411364bd8c053e82db0de4c522491bf9eb6e92f32a8295e7d0cde0d24db5c734c16e6c8e42180ef4da844a72d4ab717fd90f820dfc305349f94a5fd2e7223d303f7ed023fc8f562082c6c3af042b628935add9d3e4b938dd2c11468cc0adc423cbf05b253d6e70f6b1771157785f929e2682873114a1bb6b52e3b810c360026f310b5db744dd3c16367d4015448bfc216db1375bc4e283171d082c90a64664042d5dcd5e442489820c0cce1065771d573e6f135cb23f5ff43f769803285488385edeff25319fce93b4e6b28e78ebc630370079ef10a68e498b236efe1f11843e4e4310b91c16a73b9a7b6d0479fe11eb11b2b420ccadaf5302359542fc6c62cd248bf34c936a0d6ea3c952638b63227f4facb8ef2ebb23fbc5a1158eb5944932992382f0129a9af435459d296f2b484c915956eb8f5497a3f765bbf3c1a64fe97bf93dafab974a82978ac5f0464a14c6dd3e2302fcf4bd79b985172bcfb94d0fc50f05adf5c66cead0127c2c2287e3ee063ae23b36900afffb9b2c46207d2916ef72ded728f6bd68e04f2ace81c518d6f44cbeebe4319840d3724a258fcfd398b056c85a420b617b252eb00bc55525e650aaa7c361ee6729f9cb0b992c096f62916b838ce1c7272c4ac6913aa9d32140cfc4999626f5ce2116c1a4898881619635013afb274d1ea535be1484e60e9648515282960bdee6d648cbac63f6ba05ba8b372e95329f8c5884c6103546528c4f1eb45e62048d1845684c4b7aa0b259c6fc53a619ecc11fe1003d2204a38c79f71e08aecb0c18b886fdc3d4420f02741d6cc7db41475eb5e67aa8dde1444c2301e60d14d7e0c04ca524010a582e9351e416a19315fa79f3c20b71e96a02094989ed832caad3be3382e6b5071a2f344359cdc0e65e6b3ce991407d099d1070dd1b5ad2c755e0a0bcac5a1c18eb5f2af84c5d39959e057c997a4d1a18f490155d55f2a55f793e64c30e1f88842db2d5fb32f3f6fc282d80b405fff9a1428961dbb302c940f774d6f03431f575fc8dfb5876c4840dfb46e4f5627bc3f9b8ee9ff1a06f814d00d1c4ef757850e40519d68e5639022ed609f024059be167b5e909fb978099c7f2613f8883e7a02cd466fbc443dc9ab4fb3bc7982644f6660e960895fc505274f579ad05b8b9107c2a10c6c11ee824aeea0629bd244ffc77bed7367ef435534c5c578923db279d0878be2493f9398839df092123df615ab25d6ac34a3efff966e9ec0e498a0dc71899594f7ab9613d8449ee63120fd38d0b25a7496fa5fa9334b3e418e581150eb0c74c1cdb918b782a04b34cbf9c54bf8866f84f01e9f427ff319c52944b6f3455d8501bdc3cc52cda05cea0255ce2c0c74e4000e4ed402aba001220bf3ae1adbe9d086ff6aa6698441e593789170bfb5990a2116a1a4ec732f7bb89a9f3304c086e4ef70953830870dce8da135fa79121d19e4cb100a601723f0b0e5d88fa5752b5133fed1006440fbb87fea2d33d8403fcd8512a863120dde812edc6eb1afa50fc4e941581a9d9fb323e77557baef5ecb7142a87ad8073c7c23e52240e256389303870ebd9e7a21a2faf44004927b9c898f8d063fa1e14b72b3fd081fd076ace5b665abeb5a0b1df67071f3ba4dd1877ade381015e8570fba5088a99ca75ba85239af944fe27e844608565b81093d41828d448ba2c3d768f85aa2cd31d1b30a26f22884770e47fb2c44067c28c4b6c1be924be2cce91bf6294df2aa7a2c8979969a69b7c8f24347b783dc8bb62a7a1ceaa980b485254506fb6357f3b59eaa6519466b3bd240e0b367ce4eaa0897f7464f9b60c084c6b43b43bb0dd3a190d5a72d95481855784c730ce63ee508344b4395b5759eeec4811a24b05ba535ba6f0b44389f1a93b8d95a6ee87a7fa479aa74e37557d6d60bbcfcc516e003039922ad670d58a9e6d2306c20ac8567f0cf863dd0dcd0981b98658724e72b51e6e21204c6ba91f77dc6b8f30adb4969adef72583dc85aeba0c25546df9e36be311ad5f474300cf3a1426d183f8bdc45662408571747818552c1710bd695f20506c18a312616fb6c7a2b6746fbf52b9b6eaf6199c277c934118e820c8317d0a1ac3d17891b360364ee7a8b4349e80713876ead37276449e9c8e30bbe4c5a8844ab83957e42ca7b9a2290c4d697c61b4be4e8ac47befaba086f6551e371cf2b6bdb02cd267c544e67b9a35bde49d3cdc4208e78e06ce07ce543d31adbf44247b3743269c8ffefb392b40509d768588051474d13803bf907382ca91aae5fe6b55b2960f4b49c820899494e8163fca3fb7531186991c47943a55fdc34c5612f9da7fc3f338c7777dcb503541219c78c9ba3e3fc840205e8447bd1ed10feccd2334d4ab5e901ee5631c24fc7f37264ff92c87239018fb30a47e26f70aeefda49bba9ba951faee6020b65cd297dcbc209dde1e908180279d38d962a4d3382a4d58afef8dc8dcacc597bc7d2227da090a6df5481c5d2d1b01cd9ba4c54647c41254e469a31ebd96f99c1e78296c2f6df4f19951571a070c2dbdd1377f67e863b178cff157cf0e03beefaa4ce04628a515d35bc631e36f54f4a0482cf3f3fa13f905c1f585f2bc4d2a8c37a660e3f3707e389480b456d3759ce253e9708946e44a0ea62a89c9469ad999f1f5cd48feb6be225e73dd4c12c8347e3548a095573bf961f5f3054b6c5818417fb21d337451cab517a20665aa7795d0537d07ec738dc8e97e7ce38af1b3cd7f524af8b2c8a62b2703e2c2a2b562190f368b5d68d4f60780e628496c219f6bbdea6ef71f1125350c4356c70fde225a73817e8e99a10d3d190b6abebeb5068cbcc3682cfdff96416dab13305641c5a0377960ed32a836b1500029c7368a5149e741b5b19683e5798aa0306ecf251a6394d5459d97a3e8601b4580d74c3755e0d3231718a99c6c4c5cab58d1b7bd50765726e0937e6355db47b487aa7076e4994e00c30f94bc33108f03b0e85d619fa6909a18f273f172ec2f22b0eacb50444b4163e629b988fe236a38ea11135a525c3802736dfc24890e68774a80014f7795bde717ca12b07c0d382278f858f64fd384fe652db83a5809004a6b5ecaca0ec85a26143fa3f24ea6af618fce73dfb62dee545ef25bc087c40fb5c337633df50b006b2c91921b8c411e4fd4b6be920f94c6c86ab331271bd6316dee1f8bad07e4c1aab89fa666724191d8611fbe27ba5aadc7398533a8c8e587d20596fca24043222ce4a9b1c68a91164ea433e8c9f690300c7b769935b0310439d67ba44e52e0fa8f15f69861a717ad7c50071762f67db6abe82cc2efb3a9da702609070922d13c309022faf3f2e4e601aeb38c2e34e86f3da2c682646421e167a0f990a27e67c7799c103338457fac27f8eb5171c574ab3d3989c3514b1b885f10c221acb742e7cba15a005432383653675501d4e5c0f05b2b78dc94e06d911d791c3597a4878f1f2466bd36144e5e3188cc3ec3c9570f81a8b962c73e94e6a764ecb0323e4c090e09612de265e9b9e395d86fbc824cb25576f46f444a1033be12e9594cfc76c132a82ec6e98ceb821e611291bb3dd645b367449d29bfa85dee45389881a02efb454e8c9869b0c5ce49512b24ac3f73a6a612104d4193950ca81c46d46553bc043697c59665b185ec23be33e72f1b6820f6e1d895e0289cf2c7771136c122021c51b59ce9431a35c074a23026edff523d82c9bb19c5544e9c77ac13741adc452a5b7b23e0d4b6171faf60ca252b1ecf5d728ffa7e707bc78d4760175fc270b07dd41b88e237a10ca041e299d4879072596f7c3a78e1c12688085e66b93e2d3cc89619c99b28a65a46933d0e62e9fe422b70530a67086bfd4451f6a898212296b666ccecd5500f4106daa05e3e7c1f4ca40dac84c8c5fb65a3116803a651a2b761113e24608411a33e221681464a6b72225aca4876de70cad5f671588a0e32a87b1195c9d9af7cdb5346394616aab76b201e5b33402660ad77a6e00f20392aa46500d12933bcd96df3448b7fec3d08004b231d2ea70ae863340e2140829176564ad2ea98054236e6c8acca6e1500014903b137ccd06e3e8a710fbf9abebb274bc69daac9a39b32faa2ee64e5733bc04df589e286b84bc8d1b87a1274d60e132390080ba37dcb0b8f1f67654b345429d84b136055310a565a2a6af2daab7b4a3bdd6264232a969ccaf63b7b7e2d61c70e166b7acc79fa4c380cc8dd2f97abd212ed20262a849652fc6519493791998cb3941ad710ca8c584279966e508c3dc210ca55e7d052eb07ed9856e49b403a2641708f2b3863565c514a98bb9af7902171c55a405772ee57dbccddc7c22e99cc940f8308a178c055718db6adca7dd24eec6875e42417a4190d914d119555941270da2bd320c1a54c090bb23269396b4af4c2c3183330c5b51469840126444952b7f873532769a116ce7ddda9d47356167235e6834ef76a8a509b5612b951eb98dce0107db6414ac723c383fe8c6d9633afde69af1c639d2c6f257061d92b0f0e820b78e8b70fd01f3b561e3536e776e4abc26ef97690366724996c113a24858aae813a2a130bd44fbebe8f9819dd0c1245c872d682096a4cdb644dbcea80aa29d8586bfd494c7cc6aa0d97c16d681faba15cb5d7c12a7feb82e236fc8ffcb335928cbff6ec9e9170e3a239f95478430fe52fe3bb3d8248b1a74432274f3e667447e9e234ae1e014863c5a21f6cf4e6cace9aff2f36aec04b407b1214943147fe0631a93c58d7329c7f145862eeb663bab852019a4e67979c0aaa63eed49178549c3c21b45c306bc215b000b977255f8a422e1b1e01316a4249bfa844267133f6a2d296285f1f1666c93a45edec20bb76a944516b5785b1543e49083a84570606b258bd36b7f1d1da64fda5a456538d69920c9078775422822898db91a42c7b8aeb0eb9232257871472796ff42cbd10bd25410929841e43a26bb8c6e977c54b6c41c2ce025269c3ed734d6505e1e1761820bb0859f1117d29718216a26d3ead6add9b5bc25024b5f63dfc10baca5ada81598b100343b3caa751a24879216646069b5e742b1692292c3e8de194ca6deccbc1581abe04003d916bae9376d1df6f81757ae077fb11a866fc60b9e80a0f3d6589de3efda9606636f893b39beb0d0e5dd615aef66db70d91ca71c2c219c2886e6838c0c80cdb46d4df12bd481af94a7ec0d3c2294cc80a4778263fde87ce7baac11fc37ae724d6344f7e55148f35c3414654de8e06e80c29ff94eb821177347932ea528bad86f0157c3c1e9fb6e1da16be60425abba56e730bfa2504a62c42001272d80f10e158851c416cc8695121014a71eb6c1313758549da66121071193c9d8431c00929726f82d846ca4e0c4f7608bd39e38e65fb2ff379a36307b7cce32c9e2ec61ab10782e53228ef99f9c520a5b016cff2c0701083455c977b8057557507cd4fd508a077ec0651f5dd527548e2939be298e6233bfb26376c89a96e6f22a8ea659c682347a0c96edc9244a2249a3c341be432346a66f68f93dcc3e3cb8f408780f11d6b8b5e9866ce75770bac55a49f4fbbe6106c76679409f273890655ec021735f93ea7c525b924924470d2a380b0ac35e8b02823271a863f97b38fd1068b6a8cd67e6655c8cfdb4b5e55bc5755c39a482af539c64814363a1b0804e0c77ea50206a3b1ad62960232bec88a22f24e83a116c05f04dd6dc0aaf554980244e92392e9abf9f76620215cd42c8f2430a4879a539f3ca08b7fae4debc3fbe51e5fe9cefb25373e785072b0d86da235cb28e161c2eed08deaea9c7a7576b11d6731113ceab7be9aacc82efaeeb0c0a71b334c8c2360a0cb99b0e4266c7edcc8c361ac529562e89be663820500e3ce64bce23cc3bd425f13b0507759a439631d011d6cc21eceb002c7e148db683e692b90d0651c89ca6fd153836a0604656a8dc0084eb3ac39742d7c121c39b373ab569bcabbcbb2a7511045c8504188e31f1fc20b72090e8e8b73d2afa4f49a2c7118d6650ab6c3b104c5d9b0975c04e1e774e61eba0cff4ca81370e99bba06adc66e81dac3862e66fe4be49282805dc655be0cc71b9f828723bf0c9c656297ad67ffc75084b9c1192e7d086dbbf9eea58f1f513409734909009091c97843223ae655a0b72132c5f7bcd136024a12e1bb8974de4b0160ea4a6f6cf5bd5bbf8f713089cbb082d1041b9d0aa836636938ff239debd63173ffc0d89f6b2520b175456b3a71130c704e6155e00962ef8bcc39c729576120945970ca367056e64595534e0c821d84c5ed7e3bfd26b4d3461b5ebc8cfe7fc3db5e164c0baf26d86eb5fd9b8dcb4d4f25b3b968478f03af5392f801af3723a608762ea7482b09d3a56552beb117c52aaef5b944f6383e5650af8ee68ba0a4f934ba1113c22fecb2769220576e05bffc5131ddc9e854c699eae93eb6208451fdb82713783a72eac079aea6977b035961f3641c07386646f98bfadc789822138beaf44531fdef08ab2a55cf5d03258e0853f7c4c49e260026fb95a41ed4c5a2f85fc7679eb46312132d6aab80b6163e141f6a2b48b319ba83d08aaf8888ff874e87e9691f18760ebf156e403a031a425978603e046d829c12c06b445c6499bec40f0f88c859b334d0db2686230790f8d94bb080f417583e51172a77aee6cc33e2ff002c6d0422e4fdbd62c22a32ff7da4087026154d884dbbceadad2603df4197e74c912921db3f4d90277ab4783ce71108c0671167e5c93eab4be09030802a336db68dd5c26fcb183d157fd8d5c0ed2d14388b8ac2872482ef5ecc7cb36105a1cc645109837d91872b0b8ce364dece481888fd1b7213f702ccbc17e91497e8ae268679ccd640d5b509bf1a030c9128802c66da888b7a46b1708e605591909321386185b2184361a60fea2ed267491289dae241a5216559cd5eba06085b5cb61f684a231bd1bf8ff352892224b93224a104f16fc221e9fb2c2ed5fd9938349bb5a2e929a47f3614033d5a392e331653cb253343c652eca24bf503822ead70e33e6ffcbccbc608dcdca64bdc0d025031959b2cd132cf049d672b2fbef33117522fbd938b482af261f52c5e72ce54f4fbfb2d6b2669ac223109f4bca1c2ba614eab7c080a57635dfc64bab893d1cfc3ac94b7597f1bcae2859f82a8b632b10aea13b787089a1995c635a015d0186cfa10f1369f7a7feed0228a4d208aa23171877d24b77ffb440bfc87995c631b461bcb1f9bc42228c00d0c40fcac7db415cefb58152c20ab941a765eec0302444b526da15121e6ccbf526d64ad5d88af0ea7c299ff98d9139df7371cc1ceda08b6c125c851b494b2aed0d28f8c1ed0a432c1c24d2a60481196eb8fc6df315828fa7bb3089d0359aff30a28012b83c44dc5ade7010a501fe8b3b81b8d079099a71070b594693ae2f8dd6cb41c19a0d1154dbeff1468f22d84e7a55d94528bf6089cfaa447667e3f4e3634ffb45c39a4d54793273b36d4ae76cde828207e3a3677b0b6d68a6cb7d95aefc2ed8d426772d4d96ba346896c343ee4dea8e87d8972d6877bf6c3c08f018eb0bd9b02e729500efb742915aead894974820d0c5d32e3f18fab0846f4f91f9e938a76b0e80621772c6ac849eeb7d0078c932f03f0142256433c1a53d213dba6354f7e749aef9119337ab0bd279d39814fdd2a2505e2a4ddd75955e419132534930ce026fbbf88c25d9bc68f200a45fe7f4c1a314e6d9276574f552badc3164cd8004a4b8aed5f36993f15b3ebe4d12ca36f3c31acb9b6fe1674db82708bf3363400a1d16010e8068f4bd0337dec292d383ff7e481e9e34859bcb144ca6c39c974ba0921061d88312120e125321fe7643740d3e2a1a665e8d461db94a28e1a05e67a6e73424c50102e034d0de3be44d7fc09cf991426a8ff3942e408193f367b63833e15aa98f5ce0aeadf7557d6da4989d42f42260870a3c546aa35346bc4e53b30e0e7187e491fc9288b8df562cdd01e2022f8453e6e5dcbdeb8d0791378f3af4bcc2b7759815e95c4b6fca31733ee7283d6d8202dbc3abd05ab5fa0782f45ec962b0ced590d2822504ecf18f232a36c1d86ae97a46b345f935269dc7671097d4e42e3b145db93ffd2ae132e447c6383f97978a5ca6e13b00f25ae1326fd00ab536215427dc5c5aa4902b80f8512841df5630394543acbadc8ee1547e9261949db0ef008dbb145f442da081518cbceab58fbfbe5ab2281a340bc43657cc42b2947d8940495c86f06a290e3dec6393e41fd0ae6c99f052d4a5df0e4bc8686d419ca198e62e5d9dded41dcf345560a6f0d3f081192683c46d0dbd88e9eea0642136a15757985133286fe988d4435966be4b82b17ee90ff08549b48d815e9dc01f4d4024a023e61594056daa4030568cf1d9112804cf091fa34b5b068932f27d255b900e20fc1b61beb49f6349b1e72e3030f1719bc40d73374cf46a2b9a10cb4f42a523587f6dbe20529d77037d0b25c2dca989da7d14a42b6dc7b4b99529252ca7606ea05b00548d8739e758ffe80aa29de92ed0a698c89998df163308a8db00669a828e5142aa7971646e1a4a8ed88c9223de5782b5b96a8c73cc8eb8a320f6be6f3975c7835d633134199ad0a42f7dc39e79ce3f80e8bd70379b19960a187897da3e1afb012116afd8bc01c1f85091346dae0f2b913d2e353799f91a2b17caef4b060969991cbf6f3f4613999190bbeae2a503aaef05b93add18e13632c343635bded220f086c9633f65b6db9324c42fa104bd863f9f9e99042dbcb1a0601a158864f288639bc335e0df479602a34027b7c007d1a4208a14fa9041f9813ac57d8a48520509803ec7951a638813d3ee450a27ba173e012363c2147609830b6067f1efc49e457ae067f7eb6865461c2d88b64c912cb95fd2b77af41516b3c9865bd4a22365fd6dc1c0636a9aeeb753fe7ee76efd9df8da48ccc2ad4f4f1e554ab9ef1e5b37523a16496d6cd7bd87a30db8a848d750c41f999ef63c26ea8076e0c2616fe6b3ce0f318d4d5e0ac1be219a8a0897443b6c1a12c3e329699e57c35193fcbec65315be1066d440603323e4343ed5b427f96405852938ff4e1e6934563393439380439a564ee6766e6e6414eca5f5e115c47e6265472c18f38ebdfd8f5321adef68d57666666669679999466c7ccfdae2f924c73c91a51359c5dbfbfb9172692edbe42fbbb351a7f0af30c4c586b3de2b325845a89c921ee65eec9d9b11a2159f7e824cb353ef8a4c976d98e3b63bb5db7a43105db31b627db6a6c6c281b528e1cf6da58af570f1b305683b372d21999238a208419e93a0c212925a5b731c1b61f2ca914725221d8f8d7b1cb92a12d5752e3017ff4822b62b7ad9c94bf4c61fc3044fb225483217f422517fc1d762489ed775d32327d71cd431af8ed959152970b3c1a2090063e9c2cf032f8330a755006d431f62deb7ea600dbf840207d2e620979ebb535065c249952a90d391719e363e1df7cd1c1ba6f3ad63548e3e69c57ec2f59498832146a40c7106b858541dddf2aa8b907e4c5e0ac1bc7e0d30bbbf16aa4bfb89e16dc60bbfb4b8c8f0ffd8ef432c8ddff6e1cc6ffb02349acb347dc25d3d72523c3af26c9f4fdb3deab3199fe7b5992cccba010b75fbe7b8ed27e413bbab50025aeeb64167a68597fbf1c7ed61018c284096303bf93aa9fabec49141e004aaf9348f7ac9756f592a87a7faa40b2de52df0644a368ec5e7688e4e6e8f800b65d570c1cc0ea3018d0cdd6a4578234f0bdde9cce6037ff9bea933596667e401bcbb6a181600fd5c8c6f2fc5bb29c597e12fdc6f2b95e230d083241f99b15848eeab52995f8ad7a33fe59ef57157aed2b56891214d65ae65fc109ad1b6676b66be397c1d9ac2337d3cbf0ac2130b075aa5e24b6df75e51fe84bec87261f03ad127834f001016842dd0381dd806617525cc038843cae186840965f26643d8fc2423d8e04194190c1240464ec40869306c6086ad0030e5da000e8084c04fe616e5c8064163b58022125d18bd4fd32064f081fd865441df1857ac26405a74513e10b04ba6289185da4cc408a1fa4502268b2a1620a25784045075f5cc1627012767aa62980785d8ee86e4f810b0971e7e0eeee3b474876bfa1bbfb67ca257ab096c841774b1a3b0d27fb544fbe8c62acbe7d28c2105e125c05a5a57b727777c99e8319d3093de9ee6e1d749964d00117f4044da028610b21aed8210b332c1268e1431155c4a8010f1cc67309bc988e80af0750670aea490320a2602c4480450e4d2fea63c4cea275627abe70a482070731880098285828e14a0aa6a8428c2abc38420a9c0822038c18b8149f254c469c800026d8c1871b489d1bc824ae274c0508312a09a63d11aa557ac240020e54ec7006efe0c99719214f84d982090f3e2461081e42777777379992cf78c2518085055488f0ae544834594118b1db251943287046e8c2c104e0b0e47a620306602295ca9b881d22fc528d485480464ec8bdbb1b0848d324cf1451c830234a16504411ad38c3860376a8006db1224c9428f88b6e17d48472720d35092239f1815edc508c311e0169da45db083176cc8b4af468e131c6b804d234e7eea28c2ecef04204a48b32e10535acb0a87725c61843f0b18c2d95114a10c6155ec0507152f2928326549084162652f0206710f325c618c798f2689a12318c7185c40d325551e76283fe9080255fcc20020ebe5801451548dc2084df20e5d241145caa0ced4c414116f622c618b5e0e91b2d80a04488f9a131ced643131b53254690439fd2d34e069e206f3e3a30c1142e3ce862045660f0138130450f152a50bc6009260083bbe8eeee36e5c7ddfa6fb9f6b384b9f010750a53c0dd554ad9ddddddcdcc2ca8e0e24b4eca7d6551a3a3d1b0161d37be571a00175096936160e73c23265a507e968fc91d59f84f07d6f00ea4812f9989c6037eef3031f10eeff08eb39799783a4c60139684714c2cf3b0c0c7012cbb607987e5bfd0f2cfb4d99a0ff92f3cc0f5f8d962f300f7027c34f069bc6d8ba46750e81ccb206e91341ef0cfb0f0278c02455e06bd4aa0f180a51004715103cbe97e6cec7b1bfbdee5bbaf66d53de52789aad44e5f9aa5aa1481408eef51d6463d16a3ba2e67f6e7cfbd3266ddc8382d118ba23b24d0f932684d7f595153b3ec2e928c9c5eb44193517a932c851acb29ce67d21664993967ceeb5a0011d886e655c4a4d705200da4b982c2e616545151658716e47ca014cc5d45776c010cea8953d5f3673b5ecd649692f2bfc0a3c97618b19961014243948750fb8d26938094f2b9114bbabbbb9b999999a96867b6b18c820a5a37ddddd463eab1bbbbbbbb9b7dfa79faaa557176733777bb47ef89a393005a37efab6639b981221331324602a78e20e865cb7cdd81dc657e05dee3788080f09e09831ed25104e57f172ee1570616949fe51b824b465e3496af8c2714c408f895d1434d22bf6392fda954d3d432ff6669c0764f1b702d73ff80edb6fd1537f744536d8d87747666eb30b8ff7baf36c02db2a822e5373619c20cd658dfdf24ac29d9f42895a6ca31fe0a73315818d668fa270d89d4020df46215108259fe8a6368e951bed611927236a0b6c53514b2ceb9243ad625d1c1ba2349ba61f2b1d77b4d497b2027bd177d19b45e1d989927a86b0560ae81ed7572521622e3ba785eccdfdddd8cd9d0b17de26e4b085bc76c78b15e25925fd6cebae1fee9b9716666253d96954cb1fc38467e05bc9a2290063e84b09a3efc210d8482ca592403b086b690a4246a88662539344464d402dab648a0e6e9e2121e6666ee2e979c94b95dc9c21c9b99f95f6466666666eeeeee1e4034cf10f1780750b316099778d690185f3eeb86618396eb0d4e369f16b3924d8e03814a797d562ad9d8c4d0a84104d65cb5068944a3d6699ae2bb295ee923b75c45afc99033d9cccc8e1df2e9f3d1333baecc643bb067fdc0420d4833d90e49faa26a336fbeeabdf71e5f231710d8d527ebdae533e9c8f6c8de8c0527575f72527e97e51e2a08d9bae7b70485dfd57e41a17b10c710f8fc5c711872a9d7c8b7fbec7bf79a1b7f63b225ad2227e9e272c94999a77d11543ca79224acc727ec254df2c7c6b885c618493fb329026b622ab5b119954aa22cb3fe27895451da2e10ebefd3594360b01e9b93ee5dc6cf7cdbfa63f7039627bd7e444f8e28c28fe5ca135f0b61f6bc2bf51a7940de3cd63d087aac03c194f75a07db6f5e2f83eeeaaf91cf95f480b8e0d745620de5d1c06f9e2d949ba75db1b779ba5818bf79b8799aa7791e94c805d56407eafe524d2c8ccd33e9f580200e39ecf0039a3d4c989ef925ac8939fdc48449ec590d8831c44a8234f04b2260399d5bc2ef71b1a143f90871292a289f1faa098a8ad20e1d289fb6c51338503e3674d8411dc9133edd01114803ab499660f35b82128132ba642ff2b6959e2aa258ffdb562cfc16ba7b2c33618509cbcd3d961a1202c7e96fb4f1afdbf8b76d7c6e3fac8fb0fda01fdfb51fa38fffda0fd1c718b111e61a8f7e11f61a8f7e0be3c6a37f6215d64fb96d1e37daf6ee697b8a1c3311ca6c322a5ebfeeb7d9d125cb2c8e218c836348bf3f89ab8c652fa46cd079bbf742dd7b6deeb9da2fa8fbc6ae9fab39611c176666666666e6eed6c14333932c123a87b534b611093b07a2e7709368894634a6923ee6d3e8041543ebfd6c546f26a256bd3693086c94d60bc4fa4ff54a29296dad46fe636f45ac4becbe7b741e39bfc192c62dca89aae60c364dccd07eda4ffb693fed676a3169636943030a144e6c96b013efab9110ef1be4aecd61ef9bfdf75c85ce3da123bb27aa9a16911cf767c9cccccc369679d0cc3a15104f25192bdd0d05b74c172e9ab9bb703c724a495b9776c5892b5d2ea08b74e58ae361d2584eca2d45c78e9cecdd3d432e27aa02b2e30839bbbbbb7bd03b5098411939d8c134702cde9c67440cf0bbbb1b7ab7373fb8850ff898beecb90af9212491de7bd2be27851f14ede57c159bfd5776a52f931632cb4983b8eff9bbce775c2f08216ce6e607c1cc18089c80e7ddc7fa167e3ff7f365d773f5a2ee65ab91ef9abdd31a32b2ee1f5b3771c224d620bd5cbd976d35852ce89312c257711e08f7c386d1debcb96e6fefcb6e09de5cab934ec7f14aa3c705287197394cd5a463f362db7cc89faefb5da52f6bcd820de89d9649a4bf72a075bf8037065565c0c25613bf2bc571ad71bb7fecc57b8637ec5858df37ec5666e66feb86ffb9f658c44a1c063123ed8b755b6efcedee97b5fbc2b5756f491b540512009e475600eb10605dbdfdd44b4cbed72f3ffe9d9e7ac9f21f0eeb5af3d13f6192e3bfcad175cd62dbbbf2b2f6a31dfafe0109c16facb91eef7141ed75df5ef6d8ba7f38eef9658f051b482c66acfc4a3e1faede29b68ae34ca8acee63956fdd7beec9971969571e0d84b21999cba6bbbb999979c70f3a2bc306d6888890d223e0c2c420aa2d349a36401069e3a8bd5dbeb6f24bda3bebf83d57f80e0a61f82369ccf78db6dd76dbf90d473f44dfffda0febfb49fdc48b4cb167ddb08bfc2dffe95ff95393bf8ccc0169bd73b4f5b80a33337337172ce7735e989999594eba855e2c67bbf023c4cccc2c677777fba0594ea0eeee66660e02cbf90333f3b39c4098b4b1e4e25c73ce39d78d3b4adadc84107657503d2c5bc7f2573aa450156c8f9df3760f43587eeef125ad63450f16d2252bd8aeecd378c04701073a84bdb459078b1721d0a17a9ea0735eb2d136311bdf1d9b59904824ac02d8a4971bc16c87608809cc7205426376f7be5d1521f81bbfbffbe68fcddf6c377333d7799164dce342ce6454352d51cf8a12593f64006828034343436644eae48ff8fedc7c341f7356b16478363e63d637563db46ea6af22864306db53e34159f5da609b8d3df1062cbf831f68d7b6b96ad79e487a4f2c5e56a0a308dadfdddd73c4c5f4d95872bf32da097e7a3d7ed7ddd33e53a1f40494e5642c26aa9a33b890849cf4875e2ca70b3582b404c18515382092c452aa2845275a2a954a2ca70332c33d3ec2ec830a6335c43f32f9e4ee2e7bbc24ef07a1eeee6666662f8de5135353dddddd56b25053777777b793d2df795b49810c000f94257377773777773777777777b733bbbbb396eeaede91b9c1781302c3c02b90cccc3cdf647e5f2cf3401a58e10f6cc205f248c84372c8635fd0b31a0063a0ea1b8234f07958417a609a68897243132adddddd72522ef4623979c71035d498999959ce2d266d2cbb2483c81200b6df7bedc7f434c618e5aca8f67732b09dfe5943a46dd9eecd4c03283dae59c80959fe796d31e92564619003e3c0f8c0304ea8fb0b8360100c82611c98492f27c4503881e284dae705ddefb9bbbbfb1d49c28dbb4652a1a44703ff7a01cbd956d89b64794563497a6198999999bbbbbbbbab682f424d46395136fe2cbbbbb334960ce6e57b4b24b38a32c6e891a3fb835154cdb9eeeeeeeeeeeeeeee66669f6eddc8772dad85bd230b7a7a8f11632dd424a5fc67b5c6839da5fb3f2b880ee912daa047961e59584ee61f2b1cc85d26874f3098e11231b22381ce9004ddcc3cfb5d4bf3d0dddddd2d279d825e2c2763e00054a094040066099631b3edee9eeda0a2da5dbac7ae0c952099a9ffd1f797a474d9484622f6b6b0d6deedae243e4602ca14ef4142e66ff0619f192b58cee746504103586fc13a8d75cc7a0ad663ac4fad86481614fe45f5d21a55bd998d9fea2d9df05ceb093636366a6b34b7d468b562a726e4cb6870fcd49ba618fb45d9ac581f0935c10403cd8a8ef5bc898a7d6666878c5389bba710f128ad1f2496d580480472a006ca66b21d4a30bb5fb91c78afcdc7002dd0448abd4dbbe2840dacf12ab2e8a415a5a8dba902793db248a005fd19cf72f23811eeef9d75436af759abda45acbba95eb6cdde39bab99211e444b53b3333cf04f10c8442b66099c63256cda9d5d01f4e849c5a0f7ba985d6eb0c8d077c1f415017d3b713e8bb6ae37abdceb28288bc7d047a3eac1ba7ac9b5855a3776cf41426820f637c20298afa988592188ef54968707c29df291a35a216f702f54e23cab7e255a3de67adbf2290f577ce558b72f2ad778f42944f551cd6c31fb9bf3ba4dc7aa1112802595690eb7d5ed07ff4707455b152315e237f389ae412d96e2dac59e9eed05d0992183f66f18fb18bbd23d127b1d0521f7f9e8fd1c38f593c6eb1771481bc277a1781461fb58ca818eb8d4076f4145571fc451f5b14b6ff4eed478d8ff51a19895e747346ee6f89461fb3b81efe6e05113df523cc9df03530fa2860369e06367a91086bf187a3702ce61d8da930ca7af735c6596ec8f431e6632a0ef9a377e70327b63f6a712f60d60b1dbdff84d68d5ef4d787ecfc1ed5eb78b2a3bfce07b69f62b2f118fd09588334231ba2a825be55a316d7e3276a8111889ab066a35bd9013b7d0567d4028d5c230dc8883f252bcef4f1a9970c881456e5cb7cc84e1fb1698a51cbe3a1cfc61730f66dc332441096882fb6641b16228eb0433cb102b00d4b14285decb439fec732145b00dbb04c09c3629992c556d6fd8ee5aa3d1fd4a2be7d35e1cff97ac9137a4f64d150596759416e3c7c9e9a9d7476a675f37ea07533d522d0468dcfaa0abbf1a4136c7c8d8f79eee1f4ac03a70702a79c87e18c2699afa68975a6af1e94a97e7a20efb91c5fd5fbece87354de81f0afab1527fee8497fe5097fa90d068cea745fc5317a5804d2f4f70a9fc23f0ad88b70fc45830142dcbf770f7faa1e08b45ee8f40effc6b482e4f857e5805f4485bfd2e2f8f66a7060f5bffe0af5cab7029ca8a9e6f8a934bdcfb1820a5584424d41fec4b623b7a3db01f7defbaa55eda6c94dcf3a4c0cc55eff9c8773e393d8875d685da5f37ce8e05e700f2f3fb1d727208f2d1aeeb337deedd80b9fadfe4e53bdbc63bdc6e47eaa38ef6ffc346b4c35268710362137fedd9c86d875f606db88bd77160d6ddf8f6ee8e07ab88f56901b5fbd63ef65b0f90e73eb6f60cde2c0ee63cdeabcac3f857a25a95e8942bdf4847aa9a85e928d7a4957bd5fa3de2ca6de8cd24855d439e762c5a17e7a473d6d817be1ba7bf08dde1b559ceae34fef5f41e71cf5137d87d18a437efcfafd45604dad7d5b1000fe7efd4b62e12f9df9ae4ee767aa6c166a83b53638bdb375d5c76abdd0eb3f3f5a41dce354a3778b8669dd3b9deadbd53baa42d6bfabca5f875be05e68767ef30ec6cd4e58bb95b70396fa8ae7344d0ca86ae51e7ca2fb014bfd845154956c25e6b805ae8777c5713c2a3c15a218c1c57bfd9e115b8c08c288277600b661a1f2e5bdf7a2f7a27ed17bd11375fc07ada82d1994587b5bbdeca2f62217895cd45ee4a2f6221cb6ee0efda1a81e81f3afe8db379148d43deb15bdc8fafe9bd376f48ee32fbd8657d2a5fb5f3ebd1c5551a53c06a358836d67bd4aa86b8a9e8401123e3600b6614142075b8ddaf3317dc5cc38d71f7e12f667aef8ab23d05e675dd47fe18e7df2813ce8e4a7afaaf9b1aa6255dde92786437db30ebbd0c6b72e5b37fd8c3f2f7c62eff439efa6aa47a0ad6e128b339ffa2bed1dbdcb69e76abc73f5beea6f4ebbbfb01a188534ee69383789eecb71b4e63c5b517f4735e7d9e85e54af55739e9d554eff9ec2a013d763aab15e776f93b092d18b3b5482a8f4d86ac44060c3dee73ceb2a874d51c266b66199a203151eabc3362c4580b155bbb46129e28b25806d588a9862ab56f1bcae5e254892bcef0b02ebbe6b831662384790d8fe661b76211421d0f2bb23d0769b6acebb70c75a51fe55effcea658cf16b60edaaf16d6014d244d15b396ff4570dac411a1a58830d62ed65a36ad587592f74d6fe5751ef4160c5a1ea757d73daba7f93fc9bf31e17d6897b21561cf7fefd5eeba4615aaed73dacd089ebf1ea95a4958c9efb5041eed2e213db2fad2c68b316161487bfeda5d33a713da2cb9f813bf6e63c1ba47980362c5280480d8b941febec834e5c8f7e67c50a75b027c4962b7402ed0c2a1ced71c8e0acab479c75f6d991d5ea03aa3673a96b3f468dc77b3743d59a8ff70c5f90aab51af85a6bef5e92e726f0de7b0f42c8950ae62e0d039d73a8146d5aa872a788060000080083160000180808844282f19024a931e37d14000d6a8a465c4830140c23922409521845318c32c6104000208610631052101d07f36a525cda76f6833a87e4ff3e405b5ca6ff8147162a2ae847b235868e6c6b3c1e80bd0d9551084c2dfc1cadb00faa9cbc93a82e8d640c66d260e544d97e88ba32fee1282b491ed31a7046de09a098fa83c3d12b752b570acdced491e333bba026762e6b2d7cc199d0f835432041aab2dddb6363fa72796451bdebb1a5b6dd99421f5c81c9636cb3b2ef44df5ca0652487ff57872bcdae7df5cdebf645e8e456c4bde46234b1e3098df2a7b80b88b5013bd1e1de6c4e50ec8b913c809b53d2ce29be28072133f8d08c4f9381335253108b05f15a8ce01b5c53210b41334c2be26b4e194f268c7694b1b04e986496e801b1a53d705ed47eda370ee10c71a7fe1234089ea5c849768a1fed418288c2f352a4bd4c52710b2b97625d8195b0732fd8dbb166f82a700867f721b89b14d988d9c5c5c5a07a15abffe51b5153ff64fd98f5183e04bfdf77ac527b3497e25ec2eaff51f851c07f52f42124e4546358f5f2fb62aca0133a0028ce3cebd5f063b21d5b9377d62ea89f1d8a01a5e8b66cf205fb605cf4323b3247a157d3eeff2b698bb8f7da9fd1748d56ee0d435042eb1089e57f57030d9ed5d86551c1b2e1dd7bc1d3cc5966748f4d4cfe51fcb078adae8b757d0d8c5a32a547454467a1b45443bb7216172eceb8645e7d6f7d51c438553cc978cc33b6468a5240e9fb55cee34a6153e8c689eed18ecff4298cbc7073644fc32febb0fbdeafa4cf9883b5c1d4c01022561aefab1057dce53a1b69d97d8393e86a48e6fc444562a4deb8e14ddb078f08964e5ffd37629c75c25dd98838d50f075ab48cf2bcfcb5e5cc7380e0d818f132b1471fff63b8c86373cf3a74851801d82c0f2f8a9bb7268d18b5645861edf1425a3758b934bbc483ed352c89fff1426f117be779b1c8cd30e863912b36b0ee17ca5d483198eca501289f0438649ca5a12431aed0a1e8abc1a4c72b997c2b3cb2ef7336459568d36d77c7fd8036713dbcaddd545657c227b23730011fc3c5b2d8e094fbf3931b086aca6901670218a0884968dcf3fd3d70bf4e6a481de3a57cd411ba3b08693f42f169a2104da1a807e1714aadfe6f295c9adbb118a7fbd294f7d0de8fb6d771af8e04753dd1d5f375539a41ec4d0729044912f432caab7a5ee893024806b72dd43bd7e14b40aebc2b1906d2e0e14ee06a6156690062be4733c6ef525e632f8030435f2b0a49bc91e46a8100448a180a3166a708d664f5b0a6e824b2c351e5d8e70f07ff9e0fcb9667d41647d92bf3d528eabc4ef69b80a1b855bfa367fb765e50985d29d6a9f640c50a7e3b0d503c9aacbeac4e17cc85c5659821ccdafd12de63abf43397b70dfce2595f1dec2854a1ff1651ecf8bada494025434ac41922ea61584c37d134951cc715f96b56b8f8693abb8976080701043ba38f5a1094ea8b9771b1b9bc0496db7d6fdb16464bfbba1c80c6ad1db4e263d6a8d60c6fe1169a3dd716df6654ab6c194315148984b25f9358211c9676ec116f873d0a2c153502dc2a162d1e0f0482a796db49ef52d8069d6970a1b543c9bcf8cbf90dc20b6a3f3307a98ed7058a1e9ac1820be1821171e0c6fcd0772362aa8a8276f9d6b3da1901bb9328918202511b8fd59f51a7b6a7f38bc6b02fa3c69c3a8fea3eef508c7a5eb86e343a4c463d9042eaaccc493a872648bc18ec398e5614ad811f0db7e87cb574548787b29c6743045900694e24c117884bfa6fddd5de2e0378fa7d88d028a87be7e228ecf6b2a39aa5c12e911020589db15f8be64db95b71bf0c4756775b239b787ecba9a8371518442569b505e63227f5db5eb0cd2916573ddcd7de0afd6902ad3ae8aad4dc33350eca218be0a4a30bed231e88011a2c3e0f35c53276818983c688d2a8ba498f3c1d11b93a237ff16d955edc002526428031d2f689c4e22cb8533c929dbeee9f5b88101c6331f2cb986b24ced3444dc88a24677661af615482a63d6917951b1c08c0ac9120fa09b32e161ba8eaed15c7e3d4b3521b02476805a23df89bb8b703cf47f0e5d0a8a417f76c41191ec6f8a340a2e4df51084aebe051df8d9d4bb5f44e2eb3518e7c2030ede6c2ecc3a0da05eb38b467f22d1f9b381a237c37243b7b1dc0759f317588e823e994e5151a7c0768fbe8870ff0ba8e0fe6961ddbaf1f118332fb5a9de6e5671f87c24cc1bc73dacbc0bab5b6a1dc2bda45759de22e5d10cd6fa78e2ded304e2a288387ed3b51c95178cc0a06fde85150036640200007c581cfcc9eb68f704155bfc832a3b462e94076370f2799d75a8c917700194dc5b184644aa8f091c417e7d0771758b685621f7e516596840e8b836e8453e8f8c376d660c0bdd0d2706085750a2229e966b3f36ea71afe0f6eaf2b50a6564b9397e4cd32a8ded66d89a17b9e601fe43568b1faee63f128281c49c6b3caaaab67b276173d058e578ccbfb3384b9597123aeea4592d85e3fb83b7d5b54f614a89d279fbeee00ab515c144b08e29337cf51f3e275641363bb1c4738919945332bfaf2ed61a03186876713f02ada1df41d798ab793bd2541b52d41a009862b53767ead53e6eee18535c65d01d1422f6509a599a72ac7ed80c94dbbd73930859e187aa9c1a4403a338064758b76241388eb7619c352fa0ce999c0d62db27fe2961c306c1422f17eaf7166e91355e2aab9e68fe25d04bfc9591db373fc12e55ed99dbd6c433dc7c170db92f65c8ed2c540438f26b6a77b7f7a049b63366d78af1bff0f418f1105bdae699ae84c2254af9d81671595f23bf62a54eff2b40075df837807c8f428705332427c5cad915d60e03c1b9f0d57f6a5ade305add67c02116bcc39be75e09fa0a8fdee7a050901c1bdc18b5c188236a3fad2656926754e89ae2d4077f54faadd29b25eca800052d284a8b314e652c4fb0fc130c7c8c8603edc9d5790aceb3f64ccab86772401a019b7de4524e654aa8289d77a9faa0e2a0873b4d1bb6d4bb2b152b6e963f24e0700fa62743fcda5a5742ec6407ec4614a292bce57b02a4608ecb483ec38d243d99e095872297af9e19a9046da6ef416c6d4ce7ab494e18eec3b8f9360e1bd4062a343dfda09445323dd4cea18b92f462b38fb3f039b8a6494e040c83cbf6229be1d58148c641ae960f18969dfa0dd463e8f44716af7642d7d72b1f3423aee8faeb671e61d49e57c084e3035b3ecb05be6077cd748b4eb0273c0b04140d10ee120ea5ee235f21c01f84b89a1a18baead5d875f149d70484f01257dc522cc7172d185341dc5205c9150efd1154f529166b8a2b5363a8ee430cf8bc509116a26d0a1171d157488e6d58a43e3101a948f37469414839154473bd45c251b1e7f6b7b64534aa41f34a01d11e36f7c9531a11445511cb41481ab85bd6e9ca83cd0aeb5502aa2ca0c80b4adc38a4ebff4d33d827824b84a58c606f62dd38aeee14bf1f452449a6e3d8ef3f321f141af4c26332964d95daa64d2c82ca8ff919aae5527ab394ddfbd455806614a91c37727f0f35a34880d8e0c11c54a6e72bdfb68b975fe241cc40a704c4dfbe11784d0812837666a8fcca22e63e9068e9034f7651a7fcbac79782e3ed79ac6e0715e4fbc1dbc67e81c94e6522692dfedbd3dd3ba712e5aa7501957891354df1bb7a045ec0463dccc23f8086bf46b08f12fe18b9b5b042b2989bf4a37e5c73a25ff4e59e91a66a665573506196329ae472bc9cc032dcb2eaab4d1994a8308520c535fcd2a345285bb25c184fc6aed6824b1a0bee446b92ad25f44c055c38f74e486c32f83bca7c809710575fd882517cf70c09c9ff99b017e50a2670c84e068fda2b5bb93e89875a53411e2254d940d074ee1ea507cd6ec02fc0e073bbaa4fc32f4e8eb25bd5bf394b671d8aa74094c505e686bbfaf9bd0f13ccaffd1c1e7c8cb08ebcdde8663882675c9a9c2434b48fe43914c702f800ae8cb50fe13f107ec9901bb23b3182e8842fda6b972b8ae095e89bec34b68d7a0588a98e26f2b2cbd536449e9af0debec463133e1f1f90d800758a32618a339b081bf973ec0e908aaa2633ec07092d9baf9875fcd019aaf7052e79ae491d25ad053dc1a959ba65e2b928d30e5c6207d0cfd496eb76297a6b216d4980d119d6ce31306015006521ba7a108b068811db4c1de0f1bee778780a3b80a270e8e28cfdf2c8c827ff9a2b6f52b9ebab5a8be4af5c45460da3cba7e25002123cfb522631a8dc828b39790f0019d7169d90a1cf1caa7b853a0e880337172bb00598977e1a13180e7f418336b5cb7eae541e8cf1b7540d7686139a439793c57400703fa3fb3238bd7302b6e4b52d55e3f0416edc6faa116f2beb75d17aa7e694fdeddbd7e3c16ea6385f50d08894783faae27826e84e076e43c858d8643740dab7f70f00bbd608d8aae40121e81e34840f40067a244b4d2712848d039275771790f17fb2330e80522c1a1ab40ee43762c4c2ba387da1ea2146dcfe5e091902af75988bc1907596c8b59363b848cbd999f7ffc7a7d98bdb46e87d54467c6615f1078e4a2b78a151c420d727c7e6579c2fecfd9990e04c106c60dac3cfde03a27036d0d1858715fc6ea581c75c53ed7105d4a6ff260d88f21a03b4a9ed3361549ec19d0cccb93e87a04ad0fb188a35b3ba25d8b700b4861a6af32f6f7bf2015d5fdb506eb9484aef16f08a1f3bc881ed03122968fa130dc73ac4fd123457307e84c94346c5c6e18947c14924a1a6e5ef303bfee0634445e08d90acb6de5e5f5412284f568653e46fad9449c64fcd295a166fbda95d7eb408653ae6ee44b1ade89a0279bbef752eb7fd47d2568a19c2ebdeaf4c0fa8f6b76edbee727df7818a4a743a7649d8ffb7b9fa33fd3de70499c9da35250315bac60c9a298e76069b9c2e928b2d875e6122400323df30533f432077ce8db4e502b560c1e46b36a7604a180817918294a9c7515915326a77be53932edeedb8157cf08b53b60c5f8d27701e6fa53eb3d98e4c17640db818d0606e5a2725a7f56b19428151f6c9a51b8531e9088f38329973c6e6dc3a8115ea8860ce2e6523403e6335985de31dfc63957c57743372f0de9826906f1c94dbc3d9642d8e06cecebc6436622a50fdb5bde07c00025f03b98f00aaa16e8f153839caced58e9a28bdb6271cbe0c2ae33454b7992c8ac11f88bb91959077d6f3bfa863fcbe86ca6467585024322802b461aa671adc8e159f88c0d622b12a40a0c2d540abb620e7dc5fd8b11405af21ae7d065d1ae8f8f7ac64d9d3bc28a96174d34db59709d6f17b260d0e0a0ea2259cb9a8f91f396caefaaa1c12337b30a66c7b30fdacde1a3e2f859e2c452b8e860935a2da7bebd0323074ffda3a0a28a270e760d9312a0a0e6e19bef5681ae3cec3d24e33641896e08283e6eb707d2bd33f128cefd6730020cc68f9e47b405f2a282df012bd10fcbc67066d5ec9076d9b507371f63f52395cc6577c4cfeeac4c8adddfcb6c10bffa85989a2de370c9ecf2f8a1228d750da8fd45b1666c0311ec948245154b5185d6e8d8f18c68cbecaec76613e670db01854518e7476f1505cd128d5b44d811c5613a80c16ad1af20fdb098e94466b47e414f0fa7d486a7d183cc49b53e66e9a06dc7d2098ab08506626a19c027f076fe4cf5bab14556b56011ff18ffbf0489cae60323a49ae2ff2a4a5672f2f2c04d72e7a45ec30fc9523cbac4e57459668e870f9a91d0c3e6fced15f5e9dcd843a4d32cf0790497df230c05ff39b1ffe8662cdd9376644d3a18c272c13682d06af5b4e6922b9e97fe16a39efd120fc285da3602a7cc2d4cad7e186c829ad945452c859b9940a805fd6d63d8e592508e35c14a55f3dc10fd44659488f972ba50ca4d4fd99a1b55bff427c8c9fdc8c805f9048b15fe79c79054e54de102f1808a8e0c2139ac649b38505acecb79903d1b327573d2ec021f1f7c4e78c4dd8174315e9a8d00ef90ce45a8b014a5899a782b7be3857f07cd7fb6994a3987c0d452fb63a3d42b71dfbbbebbe394b90dd8ddba65c3f0b00a2ae8ab4a9ede564f69ee85c300e68b92d3313d5af261be446292850446af2ad9f2418ffb6ba3a268fccb564efec9bf416b74d0f49a42a355b200567bb0e7af7b0341a888813fd30a44d183a7b5afb869652de574777a52147fe3254592f4b5424f9015992612ab933a447964a71a897c1677a0d5c030d10481b210ddf1f5790f88fdea3fe1b1c964d8642d90ac67ffd613075b0511bfa65ad0ac8572578e706aab1d0a85802de6f2bd181d55d91abeaa54e6009f1129f2123f530111ebc4f0cfee23bc5d1e9b85ca335a024687db174893d47a3ab5cbc63ba63de460983b0c72a09e937d8603ec2034827caeb01ada8bf68e0fb7552b5c1f4966fba2c4c7aa4016fb50ac7eae5a59453d7296e75e9054ae38f3755110c338d109434d86c36dc8dfea4029488ebd06b6ee7a7cf40a53b06fd785ee7109059a31ace039ccf5e440b7b52a5695f006c8c95c5362788e62adbb8f105205ad8703ab3debe163f4a8a61902be91354a625124cffcb22ca090593f5e08c937c1ecc4e1bdb78f5504cc4f934bb7b78195ef0aa07c19287e8435a56bfcc115f78692e4bc790275fbdee1c8ab3ec7b98861b6700a0591899ece597fad9b871cc252dea684d7a6801b81d1100c091e2330a9e2b0d460dbc0cc612cd519c0ed87ee541e29a5c3ddb142903909d36c172d85268fcbaf1468fd27db30b882ddfb06a50d75c1e351ac13282a97d2b8d258f28cd7d1c0a202a1716578c1516b25050eba36d163f6a3590f01533856e5a66fe4a208571d2dc2c774711be4557608746e56a105571409ec112c0f13608010d7ba789695e795196959968c8cfeac8efff2625bc3a17ca33b0811511aaf213ae513a2a554496b9918a76c13d9dcf93fbc30ad034e4073138e84870fe6cc5372e485cf9e7c8c5c0ef114e28b0959e206df908cb2f69166ca623a540092220e9354a886e2cb0440208abf8c4be2791cc491fd8785c622832daa6cb207c6f520d05dc82c1a92ad18af803c1530e3bb0d65488e2f2ab3b9a33868a82e949bbe378a939ccd7f16289127b5a81c29ff88cda2d91b8f923f8c908042faccdbf3efd7f13adbe18e505b945d2d23a6b0bc23a63020c920d5ce36e6609190151a7c901ff7f8ad3ed744ebb8118a353378b959f025cf7abab48e81f83c2971b97d418672df9ba9f0b6c8d8d34fd285f8d256d22ac660e0081639c1133ea7e7d3d76ddbe8c36ef3684165b4db8674b7698a376a20795401e2e141f884712a7102fa553545c73bba36991a7afe5012d858d6a7fcee55c7068161de500f384f6330abf0de7adcf927ea3bf090978e55045eb9e5bc1fe394ec0af31788c1f210d880a94abf08dbf30de99aff1e6d10f2153cda92741af7cd0c569a2c1d7c7556b42496c5a543751dcfa8c2f8d801436380769fd453b084882ed318562347717072c68e0434dd84e42776d8424bb3a83a49831c26b0f65a32d13f1030ce083f557447735f572825476ab04d48bc55ead286d3279521079879d2e5f29d8196c0a97014fad16ee4940fe71afb19f11e01a8df3ea2265fe42dbfdda51c7d08e48c0bcefd3f42405f8e087875e40738c1a6ce6b95648a120e03b21cd5944d019e1032d915cb838a58c7ae0e41d6201c1593d8be5fcd1a07d043b763b0493d22108f758c8540d2f7a3d04cd151fc51b281a3a1cf57a7080947f10773351ca0aa6818a48a40cb1d9db4c20e0413b10c4a7bc3b31f218d5ee0c4e8764c7ed3d0623f8e6542d0d92b4c27e67e043d0a143c8b0fdc1260b6b9f4631c39ad48eec35318ba0781321cc449bbef00db253054e397e62961c03b03a619a8c27a4147219e0c19b359f1ed06457f20e7e57e1e881bc47a289163ae63053d1aa1f624bc094238b96dfe4a6a36ebef02cc51a4412480b81718d48466d44c1d284166bbe35803581a449696b1db2eb05ac1adb83a41e1d4c475b7b2e9e4600d366166568cfe21072d2c9ea363555395913ddb9c2f63aa5e2321aaf71db0db4afc5c71402cd29d4ff7ac5d959848ea858090a0b72c70702a37727d6573d6960c928ed0135146f6c1da3b1aa7bde82235db7125d4114efd6dff72ef9b560940cf2f6711666a8614ef573572ab3c90d2d7ff761156bbaa6cb1820f03baa2bfdbb9d33ab69c8d3eb7a50c862b88d1de38ecda5c06de0e75cc40adbc9d47140fff4c6b7594c2aa7a89a4e207fa4c1eaa7b2934d68ecbf17d17bd0cb51bea793ae65af27a97c01b5a45ef8cd7e0160b56dc6a7f7b9d5b29b72d06c54eb0fababa570fd7f41aa60174f6d6350239240216488dc756124b6f2ae1e0729e3aa9726943b286b6a6331763865a54e11c48ccf85a26002070a90b4f506c16851cb76a30cc969a4088c1d2a63378a1f7155b2d2cadd1b559780e70a3f4fa1a170d828edbd6661db80e17524eb5d253cb800dbbcc021b1ad225c30a6ccce4e2ae72cb3088b686741f0dc3d673131336d45d172c161722b0a3dd0aa912bf8cb009ba09ea1a81087f25a796db497c6d6ba2d91de941c2b9992d07f274a36d5c40fa2f0e0e69d34520ebe13d27b2a17d9de424493e4edc8a2e4767a78a4e227c0c99679b0f9d63926f6090732ac2c7edddb4ca8d3f5b41d9489884cf79d059ee24001c5860810da49194d8879c047bd606c3c7f70813c9333f3a618c74cee5078e250586b6933da4377f0b51704ae16762ddf52592dbca0f62ff6b80a03b0ffebd7a39472af8af96feeb628e56f2a6ab2bf71f9c750a6b96b49f410d043d9da4d596ea6ac0592f076a5c5181d7443c39ef045476f74d07960593883107f036c09f10383b98b3cb00fff8bd0b6ef8343d3581dcc45f56b1ea1e54f5aef1073995a02a69de1820e06494bcc9f9554ba5a469b3fa1c00b77b0839ac08cf52c3de4a4ef9844bdd7a342d27854cb20d67b03041c7f79fc649b0f5a81c083ff586cac7237e3ebe035f5a9103ef10bfa09e81ff832b80a4479056154e8cc9b06493cec914cdb66833028fff31de86b834545fef14271e3d30c489cc83218381c5d672fa4714a8b1e380e947d1cd64814238ec862a870bd8de5223979bba0c74e485d9d11a3752fc5b9e1fe207b41eccc46acd9f97cb0561896df97217ec8bf8d750a38e7f1ab0057a5c62eaf2e1eb4103d90608ff34945ed7a6e941516d277e278bfaff87f41646ec0cabb95257980cded5ebbb8cfe734da1cd8bfa5999c43352248933eb67368d690655091c746ea757305f6c764e01f5b6788661c58400ef3597952438547f91576800f023289fe0985e874c7900356b7984d26f21e11e11f47352d564c806d75cde7beab408554de0f29b9acea0dd9cb71609c986d3807e87eaacfcb4ede9e7d737e24b6e03c1b0f754e041f5629500f40e5cddc836c4af7383540c7d29eae388b721b15d490e72cf9464c90cb353f3eea0f8fb8c1bb52affdff3e06f10dacd014a78c8fac9cb862899edf663514e8106f001fd3ef92a58c6c09564c15a07e3e0cfc896659f04656014760d787547446bc56ba2930145b1779611fe55588ca1414db3c828936004fc4884788685694b6e745b89bb499f8e4042b7f9d5827a547614efbbabcb51bbc26008fadd7507d3a35ad6bec76b246796795a0715ecd445207c29233d25fce3aa8f51bd706fb728180980d8f017028b2e8b1cad70c5d1f1782e3605cd88476736e168a04e1be77268116d10e7b2f53c1157bbe4b7829ef86f21f916942e98fce7d12f7afbc80fb515d7acaf775cba1bddb5c4e951e9bd8a8e9afcb618a379e19ae92359088cb7236955cd20cab06c41f8cc46d79fb0fb3371b1daf7a70f9785921d31b11e50331eb2444afa7740e0211c22c67fb48421eb5a319f301a152825eb06a0ad218d460c27bdd93a7776297afe638cd49200244cd10faca433ead78af044a6047e2f6ee9c876b25a041b84f5c0e288fadc99417fbf536af3394bd6f325d6d9901b16dde08d732efe5e174b018729fdcbab7253fb7fbe09b0090f6c9f8739a21e0f42b4a5af8a11c52306c8304657bd7312a4cdfc46eb2ab0f5f6bf761556bbfda86837fc3bb999ab89f10a8be58dbcc8e794d37ca91a8008093c4efc05f8fe491cdbfd7a1b62dcfddbaecc291661a4576f1cf4e97d700c1161e9ab2e310a570bdee4ddeca2954a58ee905bfeb8a90fe1f6b639e1bbff8cdacb0f8155a1b9fca187e44416df75f94879f8f27e9d9a308d2d06f08a41cb15ec96ca623ab094c2e9531802d101c4d48f63033f347ca3656c0129bb3ea60850dfa9e411ad362284f75225f881794bb663beb9bfa4b980019963dee1d6efa6fdf1cd86f136d80dea0c9d0a939518a330c60461e590186c57b51fde283d30f9b42b1b14146d11eba2846f6d3fe13e32585260f10ad3ef77dbe51d947696f3721df4f52f34b930defe50347d6f16f8131445a92091389aaf760e1811d94e43617c298fccf263580dd5de2ca5cfdf2f52a2b6330626e63afaaa8bd1a38cfc5070678e6e4a8dcf776de8ae6c9e7a053b5710ba205c37a1d1209d492868048541da5b7d6b5595720bf7147106931a4b8e818215010976bf6f52c3a65e83e1258601b9dc19692188537683066c410b2243a0ccbea0953b7866df2341fdc2edb6028d14d1fcc29270a1f5ac2f15ea40c20f33013681f75f05a5bf86c902570c2a2acba15de8875fcd397a0c7011cf5ebae802fbc59f278b7f27b96e5126af88c5914806d579075b4f61a2315ef495d46c496379e4486e07686554bac8c5efcf7eafaeb32d09efa08ac83adf7fdac520ff349e235f2de6ed6d3650f6390e74de79130a562fa53ef23e771d3b3249ab51cad6832c45a04ec50f362dc00ae353d4722e06d8f86b258b7b0f8c48a523f60185eea692386209d523e64c50a2bd6389c00a695d37c94fbea37c194e8c646e65c87721ca64ab01209b0404df438f2b535b972e4b2bee3233e4800db3f690a5af630861578a96ade9ff28e5e0379348bf1b78e7963cf9badc6082215c9b90ea2c4dd65e7c146cc0020990102b4df4c28e2e903a87c94573914e4c1542624b08b9850c24f4fa04dba9921a48876592800db17bfc93328636ba95934e96545b528a099dbd90d98aad25b7d55d0027cb12c3fd860514096bd3fd10924eb9ebc76c5a0d89a0c3a59573a74a9358d311f3fc18e794220762e197edf43f5b55a08502945e22bb64a04677abab6742ec045c7eef5d9f023f79b0ed73dc6a3eb3c9ee994fd582f0c0e2cda8af7780901bf89460109c957527d60321bd690a7aa6866b6a7d17a74f2737fd4b3e4b9be6593278b6087d8c5991dc35f0430d49ca006534c2de610eccec78a00f0dfea3121cc91fc8833bad69e9ea11bca6e281e4b00fb28f3e386cf9e4ca9bc324d5976d732d068662525f1a59d467ad62401a65a9d6033bf29d6fb57f67b19003a03f815f95ba582890589ea5c2603b13d5610857a66031fd12c2c16f405e5c22e16f669d98827ee5df5bd474f5ccaf70f0abc1aeb55b1b5cbb231a0ba8f4662b6d622d30de2eb3919d6e8a542276444b674b14da542d9f0ca5fda484c1d65479a001317a0667d130d74ad35f1a6994d96ee4f6521b408b858d7faa33479272b9a7bfe952da009e55fb246f4ccc91a290f824e4007d71906885e7767c918983dc9121a8c7fa31df3fe7134f4c574769aa92a228a161c47f52977cf7d43a0f4b8418c20b2057dad18f7e73a6006e45f95ee8f0668294a9ac8b71d51a1f2d8763bb89029996c0baf47e1df20e67034bb1358088ab0b2af0e816229fd51d36f64bd11f2810d8182b7c59137329efeffdf268cf93bf5c7563a0227a1fd2a141e332a78d58d18a71ef8308dfb8e04a14d4c768602f50171e2631bc000bd3fc0dd56c5b7c879e0c4c0c14d7b928417dc87e0be4aa34a4461f0294cbb70d9e75a26c8bbabfe9cb745522c7355f0672b9067c8789f66048b88e13e5d833869e07c18f837057228c1f121dd22ef7f9cee33c6d52ddf13133c82ef89fbe658706d071f612cfeac7993de28053d9aedb529d1c54342374be4506fb4a9f1b7d3af71425206860023a3a6e3f280aba3fec42b5ab10831205d0246c1cf0ebf039d4d1aee40c6802ba34f558627d6af9ff5bbca31014e0b48183f1ac40feb5fcd63b6e6ef70ca8c4ec4ab6a7d28432237fdca6e426b5a787eb9069dd98440bec27d06f0cde99ead295ed872659e3fe3db8b3a8fc425fc557b112b11552728481c52603aed4929d2b1706144d0028b73d0cd98183c09b6e31b027aba0f1c6a593a4e4d46ade17b664c0d826094cefbe99276ec92ff721524ae689a933ac4014a2fb89c4f92f3f633d4e5bf4238a7bea76622778a97638d08009020152c0e290819d42118310030090b1f8c8eac8476f445f72c09b82043eec9b81b4faa8d838c06ab9c4e7578513f7c203781edccb25d8948dcc5d5165c9ee90cf4b0c6a8238f4efd5711392a97fa1125aee05909f80c5ed94c251052c816334e58a5cdbf67d8118f49c15bfb00803a0694cde08d29931797dcfa09d4cbcc393011af2c37ce134bbe8daef2096d19a8002f86e823720fb200b69af40735f588b42c85d2cecd4dbacd25a124f166f142d94cb2a600b9202c1686e8c28e57884d268209f185d9134ab9a1afce693dbc58585abf5d6435c98b4c4a172ae313dc3f5dd606a927ce3091f08530b0da68c5f1b8de90c0c5fe1c27fec2722a5d546511b394232e74046a0500b31aa438ba4f23cb48df590d388c2ab0e2d1fbedbad31d29d5e77a832f6bb222dee79fbe81e8c95c3ea6f917f0429e611dd89aa4c0735673bb68b264fdf180f2f0bdf50100956d3d071c4f0ebd9d68157ade97eb697244020570248b2c48f0ef9077715302f8014f85c8614b8fb3a5edc56d1aaf05064e69b43eab7138241103cd529800e9909c12ffc1fa82af7bd764964d2376acc8eb4b3e32ce0c35d714aa28c09535c527aea7581514cb2120719af385e7654061b365c796df8e714d4b8b8d79c9db5e2701478f4fad6580d5843ca7429d652e753d0c1344fb19e5b422bf7adfebf626a9c65e800ad1e6548284b096c692ed1144c5d88c663143a320dfb59856559a9384b2fcfac1b73dcf580082cbbe7917c5b935ded98d45404c4a0c6ee93f470fc2a2bb9c6248e0509771798cdd023b1ed2bfe02dfa31ad1e70881f3c7209404f6d4245f2f39dd71c95b9a11e659e4d7bd6984021b21c950a8f4890cf689dfcad99257750be0b9fbb6a127d001493cb959c7ecd80f54e37484f0da000bb3cda89f59bc80968339b3015fb23413abe72b6747c87d86c98d9a995b438796d1e0ba4992bfd887397032c81ebb3eb4fa977ac3bd06f60586e1c8ffba0e933835603f91d326264ecd35c0a1f591a0743b88e08be2944e8231139f0ef65b962a50a59138453be4a73e945b53cf63140f4918fe81f15bdf352446d9628f81ec80a66755f45df32e2ae8684f190762ca45f2b9b001570f6854696d8b9955775003fd619c3698a3fc30418e0271868c4d34456aabe2bbad348f3ca38009687130149990e3477c27c97c93dae522f911f6a674cf7de3f8bb635b7d2b76b0a78537e86a03aae1705ffcfdccc562b2ff9fc47a7962ca24a328dee7c49076a34fc36bb7348bed581a94bf29896d018a39979905590a69cc57bdc8ea2d3dbb027670f35237267aab63e664fd09945b4353985c7731177eb5cf7357296c3e10e6f2120cfd9307ae06344c53fb8d1209a91c81a60fc591c4bf211d23c80f6cb22099a92ec438cb41f36bbdb0b1a8001576f66aee22a0e69ad535001456eff2d07f846bdccb5158dede05067eba8505bddc7cff614b321398e7f1570e9f27bb65594de3bc238596893d962b7e1d5dc9ff3d47833bd8d8a274c82243e089817176f177737788c61dd6be2c774b2d3beded655f6e9a91dfed754e44cd285c8a0a2722abfd3908440742a04d79ebbeb09271dc9c30be8c55819d972d0c1a4a60d424e1836229241adc7ab572149ac2e5a01417c68168f66b5d254dc9bdc44c75504b0cfdc8c6d188fb8f70b4b0b32a8c54f13a7a5cb30404c84f5e67ed80b615d9fbd27c7e4b0d55ee08d5556ae0cb8502030fb101a7eb7280a5b263c2d94806ed0414b823f2de1ae964a52633303909240520befa1336433b46a24c82b249f5f657785225fed5c35a8b17bde8fa07d6bef2b1c2945aeffcbe25c6516ae10353f8fe54b1289d1634146a856623b894616873fefc5a0a42b167d1148eee22acc72927cd98d2278eda90a8e57f62e407b47d3f2866f2c57e93a9682f575c2924803470eeb19a36f71bd9d6caeb174b9bfd31afba3343adf594f9145b5dd00448820924ecbe6a6cd90a78bdc26776aeb90a7669416725d00593ec32e5d12fec7e6003939543f870ea04efd4ce321eb2bf65b1a5e4c19090c00e3d24c5d4ce3262df2d6668bd69177a0d6764ddaf371c6fdfd1ffc914890e2b62eca127b5d57c45bf323012c544e6886a40bf425c04b344f9146711ce120d7859f6d3e7bf95ac2c0a269ac69a7b0fb7f81aed86cf0d154d43690fa8b56a5a322b0041bf7f5b62fb239c4279f211fb54a868f3e7c825b383bfe660b4fcd73fea5428913133853fe98363006651a1f3f7d1b1ae30ee909f8c3e71b26add2174f164046257f19c3cd1011b60135dd615e39712ebaefccc04254ce28c7ac189d493ccd4c708554b9fd1c04885abdbf29f6c101ea21f44f3594082fdd2af92707611c01a32f443b07fe90552143124364482fdb696311198c20286d3c843eda4e55109f81cde1f4b87ba51e1d53b19f0a84081d2743c364c1044b7f175116f825d9ec4d0ecae0b58f3f255c439d0358415150b5c0c250b8f641433454dd9b801255512a1b550b5fb24cd38f430b6c58709759209003025236ee189a4cab30ccfd93f9d1616717505e6cc9ede3a4605f3426c426b07a38924dc526b56b38923fc4c74a4a0b6708c9d5d6be24d46430cd3e5a0350266b4fdb5947939c48bdf495f1b7f09f315d1040e2f0b7e974c027267cb40b8d97b7a9b36948424c01fec371b081e82b4fc2510c1c78c70167a6cde6fe5edb12ac03ec2825bcab0165680ec59136270e26b7f4b91c5f88b45dfaf97fc54d23b6bd615969acb7483eee8464afc294f24815301eaae2b10d035268379d61051ea874201c9d9c513a7c9651bd72d8851e68fe2b17941eacd8ce7bc56ccda8554f546c56d2bf6bc84bea7c338322d2dc316eba31e1e92ffbe8be45491f51e08dded902d9489ca8ada5f49086493d5b92484b1384682f69982bf14976eaa953c884c39498c7bf43bea8fbcdc77841a487948c56d7a90cbea9914b701a5b36db9cde1d5593ff6ceb3606da2c05ff6d6973f545c911796f2c8944cd5b34a9fbbdb411d40d1944f8e01ad03e31291de9f2324c7896b064fc143f7e1e4611216d3416a28708d9091891dcf1001f76754416944b532ef29e6df27aecdd6e517f1884286afa80f3f23d8618801acf14987152a1238583ac0c48b2fc974703c7a4267e815bf7479c635b54c66be3b4d0a2912ac4f961107e062d41f31ac1b6530b907bbd7da073b011c93702575e0916bd3d6219fa7645dbe442e8e47b00b55c6bb1c63c5e9427c2cfede5987237138030316f884840c05be7cc558624051009af289e0ecaa5f7ec70b925d80296b05112d71b124669eb27252cbc2ce2a1fedb8b7ea6b966c9a5399dfc44afb646f4cca1862d6f2eafa9b2d4ece360275b7fa38296f6156aceb2341ef33cef30ec6f0ccf260e7d2ccd5c22066611a2b74bb11298094f8825ed71c8cabcbeebee0b52cabb821de6259736e977b4200e6ddafb6be61653375bd0ef6c52267186e62a65d2952887db5728ccf0ff98d2a9fd05742164ed1278ad4daacde6e4e1d9dfdf68fefac97dcf6322fa2dd34b6ceaeceb4180454434d775dbd54f3fa0737ed0ec846194938a01f1edfe282b66d05a019cd293f1f685df5c851258fedeae110bf0d11ee0750fd38466501536bbd7a07184b77d508f81d9383fa1b37d190e7937887aadc603a4432186c5f389b700d548a519b9c39fc3ad97b2e9482f987359a54360bb15e70ed0b7c39b464d5e4a453dcf3f2524710e85339a72404b4880c448ee7fb5ebcecca579ec0920fc3f82480c95712038c23a87961b86fc4659df9ffea56f473089ece0fd572449c045c6e9244a56e6a4ebee5915767b1e41d78e10ea52d67d44177e7957b50d344cbe0302a1c754c348bf5eb8f2ae0ba731cc0a56c7b3d361b017d0f730a841a4d4542eb317c2f690b5e39e5bc2c6ca02b177b0db58d3834c0a8321537eefad0777ffebec418bdb78bc06d0633b1bd7fc01f9eb7d14392a0f144ac4f43cbd8b5dc50dafbc7081fae504813150fdc98b474d5ffc727f3ea8f61b3b0dfee6739b07d3270a0edd59e8ae523c42a92f1032ebbd481017549db0b26976a2d0e55174f8707bf9b71e23d7815c4af21a8aea08bf516365c7e28fb4ce2c34166470a0b7512a2e053c5fa043dfb7eeb3c0fbdcce71a561d3c36996c170f6ad9fa0be23cd1ed24252da3891119f5d85200d5edb2bf208c95d454b48575278c3241b25a04cdee9b16451c585d5c3492510799821e25e1d6730eef477192c5dd9b382c914080050e0ca0ce9043b742e61a7b7960874571e5fd0fb82ac8cb4aebe992e6c33c30250888bcdc0fce2c8ca9f096ff188f12d77dd290ab044242f03e4a0df9cbc5972e2539ccd60395faa26300ecce36a6dc8d7fb86326a57322b99fe28fbe20e566d1eba2af5a162809a2ec6b18521d9291b68e48c59575b9b617aff88e5b239462e0c0e4b6133085e4317dcfe231b1917d462b9a4412d427869e1617f69848d3b1c34ab35df6ea2eb0af028ec9c0936ca0de6be0e6c1021cab7a764ae64ecc48b52b0d5460d707b8fec7fccc05600e3b4b0a8689e8b73bf61c300b062d3aa232e0839aa49be154363c34470b5c90d5c58c711447d8d4f726b401146e39e06ea3eb27021634058e450648f98ef730f2e4fc89340718a7a4d2b4a25149755429f8cd7e9593cd47b78870359ce6a6608578d927b5e5c10734be9165ec4457d11d19719eca517fdd7795490effdd5891d35221390f649adf71741b37751422ecbe4ed1696bd2900535f08783953b9bf0c16e6d4813c2bae02543b105888686f8a48c81f8625c61d07b783126262fde875f00ea00245e13561ef031d41f12440ebeebb0e4fb4f560a03074701d490ef3d5b662c639edef8375ea5269e328c3e9f4c0928aef10f8139a792aac67ce1817d6e26ab00371101884d75e4f60146f1ab45a9500a2e470ec6ae39c01ac441c52040b1414999b555477617fd9d01c92b2a48d2a479ccfe38e27d0ac191c1c07c326a09c0c05bf0dbf6b1898ce37f0472c45b6f8232d03c4cb2838dcdc4d6e413d3b6780528cfb0ddcb18d9789bdfed9e79fd2c3059bb82967be020e566c68f80acf212396da752e0599230e4de788249eee0f098696881e887dcc9bd8acf9fcb1cb90fb361e839b84ea38b793263e797a2051732dde839658438d6f326a9c86e9ab13209775415fef87de7d3e95b9ec5cc14aa3638a249031411380251feb66526664fa35c06e212dd3ff0c007a5d1655589ec010459a86d33d4517df62604b025b9e1d4e3bac73a74251ae617bc23f4bb482f79c65c355e220fa45f4b737ad5584c44f1f4f347d75000b738e631474f079d98f904b1f3fc44f5ebd1e7242e095402ad59348b5b6e4a818f222a7aa91b312155dd8f4c369f874137ed0e26331d3b064918ba427d3a4f6b5a7805e76f395b1f8fe6ed0c9b7e895c251bfdf98fe024a1941ae6664adc18a7e15be210d4898eade7c6162c5016ac51e31f93f2c3c660fcfca0c351ee88221237589def032be44b25489830aefc2107c7e57e81af3fd4c42fc064fc0d1bacc4fdbf9ebdad5afd80b35ba97e67e370f60d83b73e6453834fced0fdbaf26f497c9eb9c5f202e0840de55ddaaea95070d8753cf3f71c13d76a7f21d609234319579d97d9d37b9683ff5f147fd5e1aa96bb4a00c64f8f909b246c3bbc1427ce5633bb9c676b37d212673b5e94da0790c3e379a031bed2bccc8dc4fcdc33fad9ab7308034394df9dbc860db70ae5a909dbd858f5b3ca58617620f6d46823783903068574bce3859c3f5ecde8c856b0ac16e1125ca25feeffcb8ce75196489363c6fa810019ddac7a22a3cb74af4ada7b070998d1a1b2142b47c220315974b21bc0b698c5fcbf792b6351762638be3e631ec2079dc29689d1c590f0e37fd2606c69a216c96376aa6074e376a63911f723115f657186f330ff1f2bbd3002a288a7192dd698bf1d7f63c0d02cfb67dffd2b764ce387a5be7633314ba9b97dc5aa53c28f83e81341f1bfbb1a49de69225f0b5339e1775494b5c62072d0116a4f495de4cb77d256740fb60f69a8338776958b9b1b1d3a8c4f842632c25e063bc826012f73d7acee438d42f9d8b26823e5e9a043bea0380a186de162ef8cf4774d46a9987f9e18c5c31fe204fbbee3f89338c555c89cfcd2e074224c00f4de4fcbeeab83297565b4410b6907974fd08a4ca440fd8e639263d60faf48b793c82fce79d78f6b4294d7cc1c6cd817facfa1b0957ecd84baa2211d6d067bf80772ff5ee0918def767e0e81a603f65f79c340f39d2846aaef8e004a9425d16aef38d1790b3a1bce27ecd0c59dab8118caaadcfcb12ec1fe222dca5f967bf164ae70134f1a73e35fc14226af4599b72f028d6a105c02e8c6bc27311581dc5668cf6e8de3d9ee0431385458450396aae22de8e16cff61cca3d9282aba7af182a9c60134e7653dbaa5cc00e387e82c6dc0a560601cfa921851fd11d4fb58495dfc2b9c1e9f5dd4f618aa274ebb113f4239e1bc55ba616910ba45b9a90a3dddaf686f874b1d29dc4398bbf860da27e8c2d92bbb505ac70bcbba7038fdee22733e925f4bc04187dc9020151ce20a9b849e981804b0c41ec9d0295adeafe159ccf52147aac1f8c508bcf278b14ae74cf3a2bdca28d38dd904b14acfeca6df13867f0c5267ae008f2dbc2346b63108c4222c2828c923f3fb98efeb487025a2fd9f6d10b7e47ede102c12041565d5ad8497c19c4467f9ceba4ed1c28d562bd353c3a63e702ecb94e2dc2f3a952ea5306f346dd00f21e3c488a567e7af2d01aa6c25e4bace1088ab416bdd2d3c30ec34c4c1bac4346f31ec0327bc2873a68cfbcc2edecea080f57b8491fc3e9213a0b692dc065f18f593e0519c9cda90bbae747db901b7c600f954da873fdae73391e3528ed4473fceafc557e5e8080a51d43f1d63c862aa0b9f322f4f1f100238c157d3fad8a5ca0046a3359f7ecafb1751e5af381c106f42298452fe9b8a07e34bba11fa2f8658c45f14a4ef7f5f3b9c33a236c7d4592a82a85fe4f6940f08fe19454b4026cf79d34ced981ca3a3d297dc95e599492e27b7eda4460f26d6a98c9536fbbe3c8b9d1f3f6221d2d39f2e573a83640de541df1ed3a0ca6557bd4b591541058a409d2fd80ce2078a9b1f261c68ce16342596cb66407881087d53ff3e39e27b5cbed175f2da66114e5cf4c7b507cc23d14a7c6945076dea6e78eb16cde15652099c572b903f45d1197735057978ee58d45b566b4cf94c42b21c97ab94c9f2afc56238f9ac9b4cc82027b6c58a39e5593dc829351bb04a508614072d727229ae15cd1813497270822b834f6a6eeb3286e6edc250f382600c69751cbc7b6b4194bda9afcba18a752aa70407fc26fc6a803b92f380f0baa4f0479f244f044e68b96d882b7cf9b9ab01f35d2b5f227b1907ce3e0d422dbb1ca5eff2ce16325bc348a18ccad2699a1557b79b5479d40c876fffee443cec9551012bd18abfed611d47e4e1428df0179a933ff275e625a76911312ca07f0ba9723c3561916a7f108d7356366f3e9469fa95859a94dd58f649620a7749533697a0e32e0d39ba182b4630295c190269e3a6972dfd9c04b92878a73a5c8580c6fcddde569b8b122f7b45c968eda2729f0fc878744490876e76922e284b1b4d30b83d0d017688cba93e06c0513d203577aac687b4d05ce454b6e7a5db41251680865a75fcf6ea4ae54270fff9481f052ade9ff2b20ec22bef9f363c4d0e1b54fa4e8defc3537b3b8844ea37195222bdd0dda06389b477f5ac815cbf9af74ac44ab40b38604e55fd40ab41c4094869ed5f2dfafc2e398402b2e48519732d01e46f15391f947d8601960d1ba4a2c85bc9429be9d96c8e79aa0e5e1d685fcb7c20571a505e5ac49ec63d75c1b628eb53489cf188e293db78d160d3538626c2aad4cb8596366bd36d9bf84753f138ac50d0d2dd09e850bebed26d97fec199850f847356cfa4d1b5e7117b8b4eb33027c621cccd68ceb94b92e12203c9b99591b71fa12bbe87541aec143e56c763dc3aa65503552e70b83f0c01b77406f536914a48610c0d62d182c045637486ab9c884d3db11646b7601112892d3a04c04bbc33b755d6b6faba642d8c5f11a680c7be8ced62d8534d18e83e97c998f9f0e8aad6dfcf4a9a2320d4149d6f5892bad5d4361cc2b0f442666a06766ea7646a6dcb234ddf0d5a4a620d6749be239cc26e6a198d9caf873b9ca46c95f66cd8c032c10e71caeae035581964d7754f160fcdcca0166f439f9877b7b99095c19054e8349a8d46cf20b3ab92b36286e34b9520065c3ad670abeed8784c262b37a5d809282abe4cca798c5d4d4bb45aa7a95079f4c771f0c0546cbe86b63b39a333b38a2942654ab03c9134385bd83412ce3455a311aa41dcea6fb6341fcfc29b53f4bae30d4fcaa085a254a0d007770be24cca00eb702d28fa0545cd8214cbd7c328c4bc6360b6b293954fd9dea9517fa61be42a3bd61330d5cd20ae716f21392f3cc2f2733e1aa59b3d5403afc04c26ecbb43e89442282540508f0c92770def2056ff8b8c873863f525bcfb21324f6c31787756a891583fadb0f52581b454fc20603dc3c91e74a9903cffa48b50441e209a9ae399eb997ace63a0952e140978f6ce0621f75e9a190f6bac6c05d5a79a272bd273bc27f178cf1a958f572f3cf929e2debeb5ba7d65fa6d05950210dd1f33a21b3b59c41c8a69ee42343e31998af52ed399528a2d911edf1cf3c93745818c398f5ea4d825c42c24cc87e2a3d147b7f12750a8d6673938f1e3061700f019c68872b8836409e97df44f4784e6a4e71d3c222505c64692b1718fab92eace095038516b7bf326a17266ad5dd85e5910d11c485a7011f344ac838942c5074cf33de88981930a22a70f0b817c40bce24f133569851c54034fc8fafaafa2ee96cf47e736c6af5ad883c1fe2856e2238d05f9ac6c88c16e4bd2af9a2c2cdef2011133dfa0c224c62e854df310ab939e8ba4dfc0c84def387d043e65fe1058c53e3d5752227cfe460dd2b228d4ee226f1f8f84423121fee033162c2da585a819dbda929f0226c532c4fbc391a6e19a18a1cb609374aa794bb1fbb741c8b64aed42d5cd8948b6c712aa13d10fdfe34aac230d53c1c054dfed1c8526aef10ea93de0a8b7d2e8c32e02e571db7b8b3490168a60b89e67091aa6775788d5b6babce36478c522f15bee6e69de2dc9abbbd369e47f02718241124a6193eec11385093e5eea094b2049f015c9b837583d413b922dc43892b9818f2307c1e700c7f203f9b1c05f775979890e958d7c7503037ac91e2df6cc776bfa27de419a8ff61e064cb235cbe4976822fee08db83100380ed9b4daed97184f1ee484852c68ff07b3d327e261183eb158c82315903f2516d5b1ca19798d4c4bbb3cf37038e85c68645d3fc18e9858a9fb0739f2496cd7cac4b4a50e2d8dbe8144370cdee3c4cae6996373410d26e5f503ad2b4a3498799eb91a256045086cd2d15db9008b08d5f3146d5cd1235b5e595e7a1435e7fafaa17ac2f18830426f3822c83a201c500408b25e024e1e98f10f20486b89a6f861c2e4d26c3b6fd488caa2ac8620a3bcb44c21cbbe72a14067b74603454a2d5d184449b35b20fa31bdb1dd240dd2bb7b522584db49b943096a6440e4fe63d23abbeffbe08ab278d376f30054744c55fd7a75aa2e132346249506ed15c2e6d2e2fd35b9b1e844dcbd11472cb2470b5644ef7290295a4090d358653d70d8e1e50b569b05f13b1b67378ca21fe2419bb8ab7f78bbae92b5ab56722f332d888031776e94fd8b0115876288669021a23bb9cc0ac118652f1117c60e0b04ca59b8514c32f3194788d0ddc4feedc5ea0715e6f85bd663a56e681904a976398b8369cd8052baa1caa8b2ed5dd8933ac15cc6a010252614ffc5c3f155a6966c22fedccfa83ff9891e16bf0edbde97148dbc1002891220e63c7f1f994574fefb20108fe46fcde97d7365e631f1f8037aecbef1288571acb04d67b336057ddb9a4971dc3e28ebbf7d2e59c95355f07f8831887174d8b0445b835533de205285f0082fe8054c200e8acb6645806f3d8c0b46ac93e37459a974654d2e19171d7efd6185ae4ba459678fc025ec8d3f26d884ac32ce8cfa5f2aee3e107306d9abc502664c3878924ca720decdca8ff531ac0b8dbee3107844e5ee0dae52dd243e21900b0db14157bdf01730f28208ccd4bb68304cd052c030a53c97a5bbedc69c857cf213f1677e74aa0c0e727ce57a2c959521b104fb3300c801e75902caa34f0ee3293c9bde3a49c407f85cdf1c59a2827364d7d6ea56609cb9842f8451f4b44d2acc1d2523f4201b0a81ee1dd91d63caaa59be02cd30c6951148f4003cccaca01369e52a8559b137cffdb6cc3428d9db8c8a9de9cd34205858f9335b847cdecf36708f4a991d41b0b33a825eccb0e2dd6b6adb43a4948a68751860fc0973eca05b6824e07a9aef0f6f184566ff532316b399f0caa21808ba50383f28098026e20f57149722c03055cfead293c94a93fd55774004a9cd32f795fbb2590fd749d9ecca7c2c5c27d2465d2114023d899a39bd0ef10d9e28d2e31612c7ca599e5bc56370e109f9279e32ccceb5c002177c11c9c545a4ef667ae411b3a44e7db4f26372a8ddcc7098d30b3b41c5eef0c9d8855bf9529a58230d64553dc28d772e9410d5d711d4c2d0683e04629a2198b24d43a8055f819202838ea1834d8be1f259e5cccfc90d548a3e39e39083bcb158189a9a23c9fd463586e2a4778e9d319c98c6e88d462eb797fe32a6244d402f204c459e751a2f241af609c3621dc34059065fef41159d2d1e58682641a450e1209a72fc990df9c9d874367f2e4d9d211527515282ec23b306b29148a0486fbdd92607e2d6bed4676871aeb9a37f29fe55838fafdb95c7960add5c1c57936d3e2761a056ae92bf2ef076191d160d9da16d9b683d3e851c4cdc5806c23cc6ba89455f3fe5fd0d229f3d8ad7cd0a571cf40a5285ef51820c56cb86a23ff5c2dd85f5e0f6c7279199805bcb9d59c007983f2ff929139507d42ad5bdf7dcbb938fe6bdd18828e98de5c67686cfdb204f4d3762101f3b939f6ce2a0e0fe507ec583931823885d42b0969eaca1409eddd9a37f34bcd742098171bbabc998e2150b6b89bfc390b4839ad2ed4772d314b5679347ce93cc6f31ce6631714ad41045a95e48f0c002cb1bf3db8c6d487964d93cbc016ea2048fc55179ec031f8f83eca2bb42a2c287d99078fecb9ecdcb001f5dd40d1bb355c8c29304a73e8d7a9e73cfc980e8f8f4d09d9ea930f87fd153c8054e883f24634af512fd97d4e11f9494f744f4c33c52edb604682036f117ff9160334e624ebbfd019744cddee1dd440708c7c8e6126d2211cd668c79e68d4d27707a9f33b2e92272061ee2d23fbd2de3ca407bcf941d454bb712434c5cfe037af10a895a801615251dc336f09eaeffb4f0b08228e0b4134705dce0a6b58445a34df8bfb045ca637248d771504b8192450097d3e628ce85bce37c0cf8fb5103276f55c17f62baf2c10837330f2dfae2ffd65a2384ecbdc9de9b6c22a524010f0852088e084de3ae711ff728dbbcce735eaf73349b495558a3a11ff7f876894559caa698dee6e52b288b745eafbfa855d19e3245a6eca42142e752364505d58d97b482b2168a14be2e85d3a3607b9fb0f768e6269ab2264db31a25306c1dd96bc9deab30cea365897c3c8723b4ae0cd47c5e6213f00c03bb854df0641aa3c43490ca9516f0b361c0cfd0f50c6430a2e6847019e43be552bf2094175c0821a493fa75f83a2fda2c140855e18c5afab93eff264b19f237af919af966f9c4b2265cf5dd0aa12accbc0199dc2c6c59ad83ba286a32370c7e9f58482349e6f7391f4b8de610cebf47eaccefd6b75254f2d467ca9acd3487d4a569189acb3f4bc246de30914fd8e71d6c73a8d1702aec433dcae4e470260dc55ff391e5dbbdfa44fb52f95edf8c879f8f93433fd4f70ffe7db1ebef96e61f6c7e049b17c1a22e99f7cc8de6da5b6718ebcd6259dd22231527fc5613ab2a2c5f8c147c177754d0cd319c4c889cee7372873fd83a77d3da9a5f67dabd0266458c8ce3e60a1b79e4798cbd7cd7eb425597e85ed1a7b604b5310088e712675a8256e6ad914cac6e586666e6f8eadcccccccfc806adf74e9a784c863605e01435132ee23558f6c4c0c17d90e806b666666e67e88721d801868bb3939462c51028bb21857c0f057c0ac80d920788429e615309b8267b65926db186a3ff2be013eaf53d6af0d867723f1dad9dfdfc800d8fbbd1c4b6c0ccc9a7005006c9e629b6398e6c7ae93c0400e03667d04f6711158e71f8c73c626f991911557c8bad3107e314c513ca9578a233c877cb190bdd8bd5adaf3201f9d3d275a96a2016c0dee216aba8c9410b329dcc6acc9f4f9151bb3329219c2e884004e45ca10728c00b0a844a3ec11c28eed1d55bb4ceef1710a42c6110f323e40465280cd2b0a767222a5e593766113eb781904db7cc2aa3bcc723c3527625a3d9f4fb49280ce390e7514e21c04090939bb6fff0302211fb4324f520642dd344d137c7f2299a6a999de807d421e5fbd6b7ea21dcc7bd03ad7afe22d6cca090199713c0e7d2202235484d6cb43a7571f692ab27d0f5f8389c4a69b531160fed464fc8ba9e7fd6453c5d83b604808166510c6951619c0610f5a1fb25a4d7c9ee2419b821f7b46b5cee2d776c96af5934d115fedd2da2e59f5fe56e86868baf5e58f490445b6a08dac4f8ca164150cb140d6dd2b8cafa241b0cd3f30cd3b58bc8545d9c4ac28025dd27ef52b89ac92488ab37904fa040e5dd2ce21e1a968f55caeba38bbbb5b55bbfbe8644d38af6845c938f4f370e813245dd2eef15c1749fb49757d625975dd7a45c9e23758140d60afa91eba6075926e1410ab28d5c99a4c8f8f4e62bb38aa4a13c4324843af3760d93ec618e3de80a16427213bc2040c776671802ad777cbdb90b37bc344336cb88d711e691ea364e6bec44233ea30cb90377768487e76f6c6a6e0f7079410a4034217c7bea2c896f6b976641f865015fecc3f4208bfae99abe7d339988c13022e7ef3bd5141aa878684c087d8823f83447acbb99d91534ab7d2225a1bbb8310476268637f989a399cc3c5b13736858baff37d5a97df4d2b281bc97a70deef1b18c0f972aaa47272d6c69a40cefb937b07b30e1babcee1605176ad3cf56be5ee0d48eb0d5266f1fc7996f3716f48d97583bc418232376fcd4b5036ed8d49f7c69a4cdf1bce2d8e2a9a506252b637da4a673f3e00f2f71886e43cce392fcd9368ddead2a5d7a28c61b089264d1a1a8d62f5181f8d2b2ddea57d975eb491a47d94e07e34786bf3b7a19533788691eddfa355a525abbe7d4fa22178ebefeddbc7ba74f7defb4a2dc60623935e817c2308563793f6b58348af4912bd300d06f21e6ad2a315036fd2302184ef9d5df00e8f79c8aabbb4efb9e01d36c5bbc4ae451a5ae9e34fba6baebdc5b746db5ba735e941ebddcd1aaac73b6cff81cd7730cd37d875c642de98e70f7357d1ec82493812fbc04759ac96b35b715e6c8a6d4bdd23a41ad210f527c5e67c310efdc06feece2f36c5bba3d6dde73dd8e62158d4259b77775d83494c72d1aad2aee217efd5e7a461c63665f0f5897efc039ee49e94c147f9c59a385eb9b8f3811d9a1d97a24d3155a6499abe6362e6697a556577777ad235df487dda14dbf81a5a1fcf69d269da214992248364a9044bf9e951a6a1bddc14d69f54d9ec493a49d3a6d8e59ddeb4fbf64dd3f46953eceebea95a0b08b8bd838e914919c3c42ae4f1129be08e111d16f268e423c2c5c8b15d5c4307ef46c4503b91f6380fad9cb56704ad2adc2cf3c1d1f40747d32cddf1d355f4897749fbc8474474aec2f0293e44ce1ae039c228e29b9183300c74448423a008faa12334f60eba3b324cc3689750886398f88611bb05867c3b867bed8e914d97878e42da5485655c48480ce910da2c0d23e44e0493f760d5179be6434218e620f45d1e66966916669669967676af2c9359f06e722a9ac5d166619866e918149cdac88adc40b648062593b2ea9d922aca7ece1ae437ca5edb3d8a9c34eca421928f71626656e1090bc5c8629e367f6cd1d0f468bd9dff9ac6a2953346c9de86aa7055e1484fd6e47d7af4134f5d6a5f5a19f0a57835975e5132ea1293b306f9a52189d5cda89f540d55e14c7329a959437b4843ebc2d6f6f24d1a28992c4ed3eeb7adb478efa047bb4a57e1104fad51076d8a760ac59b6868cf0d25ab2a279b82fa7b7c959f93c6d09eff5e2555a16aa42acc72f791e9c99a80d6a4ddcd1ae4df256c7f61d42d6cbe61d255b4c4f66453540cb1cff4288b74abaca29fe9d4a54b2cca4e3645bbf4912a65d42726693c9fb4c7533fd914118592496dcaa6934d51975b03daf749ee0d738e6653a3276b22596bf9a0f372c89edb913d763ad8041002efbd5e12ad9eec6dae397f70fed1c12226e1d0ca321bcd6555521f8662bf599aa5595e38ec6248af77575abcf7b5f1a4d76d7a6d5a1c59bff73af5418955149ccb47eb0d32bb5e69bec1a4aba21c2ce2928a56b44a93ca9044eb0d0ddbfb7b63533cf7c94d928627104430d234b198a41e908a60a6b7e51e69056555251d6d4923a4deaf14a4292929296d05124a14a3685a69214520a9b3698a5e26944699a669faa31e2fb1884922ad5246d12a4d34d4f4e4e484e34a3ade88f7b2a98a968481c42bcf7453524a8aa274c8ddc7795848c423e5bb1443e2619e3d2c428d78221f31972f45e48499e645a849844a28a0e647e4f2ee2ae429ea3d271ff246848c903c425252d2aa225192fa444d54d3112a2dcb9a22225894cd29298aa25cc8a328e9a81712e271f2ee5194a37aa27aa27aa2288aa26ea2266aa2284a4e11f7ee30be07b3de180c9730125fcc556e9aa6698a2fca4743f1948c4e4a4e4e475114255d3c75d10ffcbcc7735dd4dd47dea45913dcad93c06078f47d929fe424279526a1e0d62518120aa4aa6590c2a60c5214453da9d21c85c5484928983c3cd243253b0633d386ddce757777bb6676aeefdc748eeb88b7e3f6c4c89abae866dd6619d1f413efba1d64ee118e5d8fa0aebbbbbbbb439e81ac596a18dddd6037d80df2f6430edf215f3963f327acadaab7b5d69a6546ddda6e6bf2d674d339e7e6ce0846643d308801b621b227af237bd9a68abb38b2218268ebd225edbdb4497047dc938686bb282289119f91190212a3831072e88a0ea193f03424ddf77c710c31341dd976bf5fe1b157778c5686e918e4aff7a36ba45b0f24a45be23088d08878403addef86dd0d4f2286770cd093595766d59759b700704aab871ea3f52ad19f6278e59778659818282f8d41c532ecc7a011510286470d004ae96234c636449bb2abbbe179078d0821129263de4103028973d3f1a6239bdeecb9b4ba6fb7edb6dd56d2509fc4f73552953677ee6498f72e0690784312e7eeee5f2468ebc10e48dc1d8b67125894599b03092c6a2f4c02031d41eb1341af8c8ed0eac99a8786d04a92813e5e50649b8d3bbf6e3c926e5a0f9a87c898caf3eb6cd9f46cbe587574433fef120588e49b439fd8972e6947016a92cc73bdacf5c0bdee2d49b3265c8eaab42cb4328a5a0f36856c3d5893e9d1e36977abada3eda5f520c6a6a39a2b69eb01b6580e627c40bc1ef8c5aab60508183b60e7c0259099d9e361e629468e5c1e8dd7039b8871d210a9b42f1cec82ed226fe48e7f2edcf1234af18ef247bc0a7e34ba690f19f76643bf1cd91836c366d80c9b61336c86bd3424cf79c7eeeeee0770f8f1f3f17d0003facc1b5f0ff5c959433c4f08d8107948bc5e11bca6a2a8a04a525f3d10320c1b1d356b880f393821e0d2671712148611f036348cb4624004ad286805f547682511a1f51e5a3dcdb0aa1e101f1fccbc2b2de2737933e2b311ba2418e3737933f6f900fbf59055157e34faf590c56f3651a545860c193264c89021ff7af40071eda84d471637f1bdebf2787e12920a82af28e27556995a7e5cf4d17833d6a4391dac7a73b0f9ab462fd10bbc983e1798cc7214b4266a3d1acdcc557b99a25ceb11abd6c2f489685f3c2d170c76810205084442f27b3cd7d5fe688cc8d7f692393a4dd57b1e496b7bc9a6dbf068bc428102048a1106e6d3737934aaeaf5302347d1892823a4e0a3f19245005811b125b6d0d299899b4abeaa3cf3fefa84c81fadf241f978864dc816f8e49bdc841042159e58e88a3810cacf29218786acc93c7627cf64921bce57f84e474eebd72b94927eaec3bb3faa82a19cb3f3e0f7bd7ebc0369d684ebd58f0921adf0e38356d8999007acaa16cbb2a09430f22c2385806f4af9f7dce1596653bc43eee1e47ccc434a5a792673737ed23adf79adde91af15959772c267b1cc9a3c0e569d319037167209f33cccc16c22f681affb6b63ed37afbb791bfa81bf345894318fcf8b664db8a4ec229eebee219ad93c26651e6be25e9d619847f59d34543392c7a638f7abfc94b210e51e157958cc4981067e767c5c46663e2d6da487d3c07523331f191f1736e1dad5a683e6ae7bfbe38373ce7d5a3685036976eee3926decee8f0c07d2b48ef4e8761f978f4bd6d7388da6379b35e16a9043b89acf8e766e64e6d3e23e3eb4732333fd4f4b7f66b88f4b6b5ee3c7259b3eef2eef689d9cc6715cbb911efc6971b2baa64ad906d38cccfc20a95f9571e0701f191d3fff9131d2837f683a64ed1f198c83718cf4c8a48338b27a03e3f8c8f8c81899f9c8f8b88cf4c828f891b126138ddc0b8d96cbb5ec0c75e72877d5889961131445a34861974461f5caa87594a3dc791d5551ee517f9d2f573454bd5357c11cf766e51c5d577dde3d60561dcaf3fcc93947bdd7f9c4bf30396bb8be3474617533e6cef5b136dc75e82e29b9a5aa2a5e21424f923bf5a05edd99798aa275672877bd53afd35114f5aa9acf5da738987c631f973010775d9a578da6aa621ec7769a2f5669e8c75515c87551573937e320a4163a8b3922e0c854f20b431b420eccd68090575ac0ad0143f119c8e05c9890afe39e8fcd71c6edf9787817a7a39f08b235984338cc811b07391cce2104a19c39a7ebde1a3041b084706bc0cc4866991890d7386583448979da9811466e8d98bda683cc713ec65d8bc9296fd97031315b6363a494591b319ced7881332d8c4c92348f110b286513289b68caa680d3c4024e2936b009d8139c24c8dddd2acc58c845b39b99d3136ea6e99a1755c1f01c5a3987af9c7fbcf63f6087ef5e3994a6ac09fc04a769fa3e6201bbfb2d420e07ab9bc5a76c8aadfdce445598f38ffe47d3cef410a7a7a76c8a89560cbc494384c18e7982109e058c7a64b55d050b49699ae2596c8ae9319269b02398acaa70caa6e8b71829b46ed10a6fb1bbe05b9413e4c68ec59a4cf00ddb6fb0f80bb33eb1ea13d6022f41348e1c2f3f348e1c2f41e4681c393cd8e6d025240878dd4d752380a4bb9b860878badb43a32e897abb5f72bce478c9f192e325c7077f0c21ba96ad076946bb300f992449b463c8f8fd34d10632894cbfae4f22467ce4ef9ff779e72244bcf31198a7f3771158e71f2c8a46b00ead2a4d048ba29237ddd3b907c47378a96520af2acd499732101a7a076149a16b99ebd0d03b08031e05597a5592a3cd32f5547daad27777ab1d642c13817454340bc3348c8e21758c666118e99b437bdeb04150b8371665f57aafcf9e0b2ee9bf47146ce28174dab60ec8bb8a76b7e1d4a3ae7e7db37f5c1b05c075d125fdbe28f5501730f41ecf75b9809feebe2edebb2e4f7d4651ccc0247d817dcd02c21f4a9066260424e9e8dc98adb1305b636376a6656b6c8cb4353666675a36666b48ef715ee10a4d6f59bf472692453eb0c18e96c95e598fe4eeb057337630eb1cec7a8c6224a55983fcbcc320a783ed54c0e3dcc23a57d11e0e167507036570f36b730ea6e9682a40d2b90870af9bbe2e8187d8fbe5711076f5824aca47730523a07a47ef257a639a84458e53902c796acf05ef10a7e31d56ca85352a39636549ca5955e417fc825ff00bc993163e2d5d4d1aea7d5a28d7de9a57296935d2432ef58f8c8f8c8f8c8f8c17a7c365cbb699a4a464bb545252d4ac34575f4b7187396cf1c64dcd9234724aeacdd22ccd62044d6b6f8626724dd3ae71d3d368de0ccdb3319f86be477dbe4f6a6a36b3331fe5a676c99c8d4676e7ccd950d7b84fedbd1b8d55c9ce7ca498c31bd6f0c5164ffa2ee9a3f1683c1a8f069cc1c2f06c578c0b7349077f5d516ae24c9322e7f17a5591e226ceb51135f01bb3311bb331d31522080402bd440974721223287a89668ca038e725ad28d5c9c949d6ba202549622125295dcef6565ab0887a8428179db3dcad95d75570dc285f2d9857d0379a6fac6fce9187b5a25313af9d935699929292b226bcc2d94907936cad85aabb14b96e979ca921754af387d56ba585fcdaa03458283e8a9ee65d43ed8c7f9beb92b2646b5e67c8e4ad4716651415e5c84b4eeafd88ed8d35a1e0a3b54ff0d6da14a9f7f1da31565a304cc3b03496d530dca5b92aa763b410908de54236b47a3eb6635d1b0d5561cbbd599aa5599aa559fe7a900f3ab7d2626decc06afb03c8ebe2581c7b63a545b436e2dab0362fdb4cb73aaf3364d637b48232eb92d450107a636fec8dbdd138eb9c60b9e01d98074bb2a44641078361f00b7e5159960bde8179a8b0d0a4288a7a355d15ecbe4a2e5a43b3d968f805bfe017fc825f3820348c9e21350f9ad72c16c3340ceba2a85bd665715e5fb669699d0f9090ea21201f1d4bb39ccaa23497d52ccdd22ccdd22c1990562e764f883aedd9e8ec9bd1793dbc1e1e8dabb3ef74ae8f4ee7b95c7f33aee7c3d509b90afeb8426ab3a616f26a518bd6f632d21664d7932d49b6a06ca7b790eb55f4de87e4c9a4952a2893f6b2e8a3f1683c1a8fc6a311bd503934f99c73eed3e21e1bf5e919c8dcc6d8eeb0fa99110f5f3f2e1f19779d9516d63f2dd5bd8385aa78c5e8de89ef38ab73157c55d5e307e79683af6d7aeb58b73e6249f0d56a1f5485b30ead1858a91c7ab9f891b1266eca482b056987493e32a6266dd61981aaa494124a591d442359514e4ed6445e5a2b2daa83e4a3abaaac8364754dd7452b4a2635933c591319a9e9e4e4e4da4908d64a0b163dac0852947516562413c1443029299b9516967516d686aa2ceb14457d63559546f30afa47e775f30f4b433bad4a4949494959e9ac8b61031db27d91453ea8811059ed7ff87f8cd54a8be994349d122254e15b04294a9a5c0fcafd7a92dc98c74855d5c2549c285b8ba2389c692bce55b0b59fd5add7cd2b5855b4aa7040fe515daf95c4b92caa524d8f818f76be316bc26def74cc6e76ce39762b2d9cfbf8c0cccc9f19ce398692efce705e7146aeced31ae911619242d791ce0aeda268b389369b68b399369bcd4a8bcd66e3e222b7d9dcb986d1d266b3e9189bcd66a2dee69be9b539df402755960b712097fbb05ca772d6ab0e6773bd83455109cb58d36d18be90a80886df4a0bea2ca847453da8179da2288aa21e45bd57b9a9f7defb837456af904638271a23f54a4aca2929292973dd8b76dd2b2d26e8e07bd14a8b686decc045ce39c95d6a895a1c0e4228a977fd72af34bf5e1de5a83527ad8e35f492a6db447a83cc28ba373af262d7857f9aeedce42637b9c9496e7a8fdefbdbbddddbbda35f45bf2f61a1f62873fffbd22fb514457777ee6fe71cf5a81f92f8f4f7e38bc4f9d85ca0ccd1aae9589c8fcd554d08b2b99c7320ee9d8dbbe6729a5fbfdb74403c23227e182e803f007f0c7f891f003ffd69254155a6c34047d08ac28d50115a493c21b4de75369a8b56f5728dea48123c9dc0efb5f7de7b2e7887c73cbcc73b708cf7de7bef8f9a9ad7786d3493dc4cd754d2252d8aaa7406695de1179dc92e093c679b73aeb498738598937bcc3ae7cecc39e3946faf5c7dbef86855e9e9d7545d98452385ad8d37d18de9c8c90e09cc5253d1b53d8a2250048a4030828f32d810468710be400842012b840c217c87f15052af32d2bad2aa949d9c74e454035723731f9791994fcbc787f79e3544e6dca3fc9239ec7d8a6f29bdfb1f191f191f191f19ae46db42b7eb7dec9e565a3c97a8a347d973d1ebe151d7dd2f9b8fc81d9037adf2ee83d68b3bcca1d5a3a1f55c3187702c1bba6903221aa2ed888278b446333f76cea3e804fdc7a3e804004a5ce4517422867f1e452702f011ef241e002c8a4a62c0a2a8a404f60e002c8a4a2886615154722c8a4a1ead2ad3495018684531825610e8434568fd432808ad9e0fdaa1f5e2d08bf6b99a91721da25a7b91f6d178341e0d1838428161b00bde817958d7ce39f96769a8cbcd4777cd34fd72b1a4aafc45aa2265ed0c1fe517fc825f486e4c68cded4c738e9d5b219a9be99da175b264755db4aa486b63cab8362489633666637ac5e206334daff624bdd730daa563c4f75ec8eb03f978f5c843c969fea8e43d4443eb0b4cd22dddd20e3dd9b66992a4f648147c550c2eee9631b8f82dbd26751462c4120a0a8a949d804e669c74504e50665c4f5e3fc4a844057164830d0d24710a8b12222a1774522e175cfc2a65d293120f7e517486e118866b4cebf223f30b17bc03bf2061926eaa6a01bfe0170ca305173f925efb418c4ad4e6f2c226963deee3f271a1ed6e1a99f9b47c640051e5f591213f326070f123e9b5b6ad87189568925a8843b74c2b3c31bf761851d49f631889bd4ba433263d562cb8a437cb12602f5d327fdbd666f7885970496f471b2236ef71688b639aa47349a3fb6c6fd85893a5a0869d49ba049734777ea2450572c821871c10e08123606f2c01305a573269ca405d69342e6ed7f4901023d536203dceb4a34f76c151cd2d09aeba2e2e374483f2d46e7b33d6c46d738f46af93680b26c9617568c126a4b83a4492eb82e4dcf3c179605d7041073631c4b52e1a0cb83cac0b9b888fc69ac0d78383aec7bad81d36c6a3f166b4f85c5c4407752c1094f4ba3059b7695df47bd7e5f1fc24241534b9c8685054885189ea5e64a4238b2e42561a5838a304576d30b2e0e23c6c3b3f2d23331f1994816012253010ac0436216122a9551a8832a97d6674e09a0e044c16ae8dd15a1a0fcda5cd683db009f991f171a90e08198c16a3f1f091f1699123330d061059c368ad32101f19233ddc0e58044952e2728d465295438b10c655a3976c5ae10ab9371390ba1e772fd8b65d45af8562612866b06688aa1c2ea9b5105cd32bca0c56b836539180053820021458d1316aa404150960130b04ca5956bbaea9d23c50878867ed8c7bb1858df0c515ae908b77909a3162f37061d664b786c43c52c406aee9d116a416d363039494f00a949431338d6a2f21430c8c935ba3c92649d59336666dbcc01cc4a884ec5c273cfd28e985853f5c1d115c1574097185a8b7ac82320862132ecea022010ba80ee9daa0e8de982548649832445e8832a9ed0f20b8a6d7123208712d0845450216b0381ad580004299616facc9ae0d17cd1932da286e9072c715247c3b7c61a5cc0e55b8422fe551540a8b94aaaa3850555f905a6b1cb826d75e55515570ed55d553d6e4d197f2288baa4a717f92a650efd0d1870309db08b4b012451b0115ae108471ca865d52d09a2ca427dda8899a1cd5282d480d45d3e19a5c8b1405a9e05aa4a8933589b49d80d644a2a44d511b3d91b20743c4b34b3e7ac7131960772e8da4e6c2524b2b96abbe679461e10abdb7ac3ea3ce261caca8f741282828ac728f264579075e3a318f88ce14c1265a244d5c90a689634c2eaef6ed889858b836ab25486400a1b00969be695d79d633bc840cfc6287375d482f5628452b95aca93d29e8906159e4031ac0d4f0a145d1b328f26cb730c9d2a5d23cc2b5547a6c42468ea612a24c72fbea03d7f4cdea252712aea5e2fb2f4112e94a77bf331fcd481bc69a4812045a6681fec21724a9c226767549bbc4fe9e44214f4b7839793979397939b1765f4eac5b8fe65a56cb4b8f5ea401e6f42a65271236814c4852925e2c0974a22208e9c4b2ac130b6c26dd77c21c664d8d08d28e0602ebd8aca79e3e725d7a577ed3260fa30134ad28599d7f7c8f957adfc2a247da688b5ae593e9278e86dac99a48d494eeda49afc3a48671b6b386f7e913b60d93a2014c1357350319759f307815fd9e8b06f072f272f272f272f27252adbc91ad588b56955ef1152bda8a15323156440dc6f99c3ad28a152b56ac58d1de3587914c3641687d6a958e91d6a944d2780c3e823434ef260dedad6d8e924f522b22a3163e36095f612fd86c3a99fb355f705d05b73901cd39ef4983bbc65d45c7495ad874179d9cd8744a029a732e6c1329cc7de74435924ba83b78ea13ab9d8c3af5893d26a1bec124438e0e3290c17b345f70dd6134444cb222287871c94ed6dc207613420aa27bf0ccd46f3fbd4fb44a779fd1a757a0ecd4f9b0f2a747d8235a418eaa701645547ebebe4fd7955c4a55389b769a5a94b1987ab6364dedd324b5d63a4ab1a1a7d6d19be48c7f7cd8f49def8ad8be3d62fb87b5b7af06977d625885cd9e0a707f190dcfb5a052775dd9683649ee04c24be38234440bc141c8cc3c78cd6c5e1f888c3a7fff7807c1a2a8e471b00d959a936888a2926bd7a5c30b46c83c669887c433dbc2332d140ae6c132cc83675a9807cb300f9e69611ecc03459fc0215228ae78450688b2f678ae85d4151960b3fe290edd8f8ffa849cdfeb51d4e0540075771caccf47f04e63555301d4271cfa443f3944a43dbd47452f7a4c69b2feb63033f30cf3d8ccb4b4b4b4b496d65a6b992c3a5a7474668b8e96161d5884009610ab2448d860110220f61ecf6bb0c725cea9689e0a781a8cf30a03f9857d9070b08f974847b4a38379cec1460e8285fc031389cd730d36f20a0bf985892c0246443c214cc2c11e97c07f601e2e8120d8e62a1804abfe815dd76c1e0805c50c4cb2d76017f62ed95b2d2d2d2c7325f28c11a53684244407c13d5686499c35b7be5fa736d13b07d3bc4fa7aa45f7d461559168083e421a726f59a41bf322b8321b333549461e11d2eb90dc50014f27ab3774245a424646464646c63d42402606739489212313039361aa326420cd1057bfca9035f32f13c1586c25e1a4b5d640281bc99c804e50da49039da09cb4d61c15a4934b7a50b2ce3b9955a455aa41d2ea2a3a9a559aef11eebcbc8a9ed87b282727d0876885ba39ec0e6cc2440cf76e591d9a45a3d16834dbe16c56c5e4d63596d168341acd898f7fd00f904fb4be2e5910caa19b6f0e0f5225b2386ba87eddc2368b1eaca21c38932bd12eb39643bbe29bf50bab5ec3a6ac5b58844924328050f6d76ed8ea41ac424a8b6026c992a248b2a2261966214a4d082988eec1999336a33654f2edeeb5baa3358299f6f1b95b8f4ed11af1427e5ccaa291a22148cfafd599abaa3d1aaa2e2b4953aa943591a87bca9a383751555555d5231611cca6442c526ca852aa884594624355a52840debd611ddfeb92ae01dbac61138d98c4bd5f70d1884b2605bc68bbabf80953d1f249114cca353df05e2382b4a381c03a7e681c3fbc745ae7e55d8f11c38ec8f4918a29a4233a9d0e841d08691475206c1dd8ddb9ee2e2a7540dea9ae815c029168e7903a47bfe4782f395e72bce478c9f19243922f5995bebb2bfba583f054951c8de387c69123ac38a8e4ec198a6aa968400000000033160000400c0a060363d14494043983071480147a9e50685298c7142589410a19840821040c1010000100121200000db1da1b6931cf903fc47be11cf835387576b7ac08ffeee476bd26af6401ed9f15bc28a363a326ca55347ce4bf19c54351c88dd2cbcdfcb1adfc022a5e35bf64fcb1998a91447fb9bc3c943705b2c06a15cc963626b6325392fc1b62409129c8f4a37ef507dd4086cb28876bc38e875f653ef9b39c3febefdc876815249d5e4f52bb64d4517bc9afdd006a6330c02b136548463291d4951f9126ce87a2662fac01f0fc812a370b94268685396f934703239b70d785d4ca92dbc6f2320e909e3447ff3eeed5ca276ee4ad780d8a447751815527468cb0edfe99ab51a14c68cd5a843600a1c5841970109a9d45843c3206ab4d7e3fe61a1429e731945940d99249294407e1e6d25c1a7e8551e5a1c00705107a36b1a87d14af5d1be2e1cc0210d55c1b446c83e61476edacd7291d609b2f90162f47967465d36b399f77d5219f88c17ee28d07d1fecc578e1ca8729550bc8d1a15272fb26405669e321927ac869d77e16443897755427f5c5a6c0a8461517bb06d0ced04dfb456c5cdeb7c3518bf01b39cc1d26ba5b00e4391843de8d8019f0546ddfb1572194d6310e0fb7ad988cac250ba7cd384f612ef67a40fb21c1c659f39587dee1732a735f28f89dc4274bec86097d90af6f8c3930c0ad9382378b4d273c34d370c7ce3a77198bc268bf3cc053293c0e4a98cb89799f42724d6adeb0651b7273b8d3de36e163d282b1e56987a8331d2b4067059088b1c60004eddec21f5f1eb586a9dc1da8cb06d37b4e686d69bcdeb1e49ff685efccfd64ba74c9a1e2b5ddeec4844ed499667ba016cbdd2dc31558edbe681a577c28066546560a3983b77aad7ef7f9f47b75ec6195377a73e7e6168f63601a5f83c44f81f946f770b663875683fe5f4682025ce60f61159c5cd1c5b891069fc5361375d8d2606d357be57c062ea4cacc3986c98f939dbf1030c2ea0bcd6a7a94548e2b90be6a26725b1b90110ccc49cc6cc4fdc6dee4a2f98f6bca912937412e5f2606858eee0d44ef21fd0b6a885fdab10db6ec3ffc27f4b5afad5e25bfa924fe1d1eeb25ad6fd3785666d12626d93d6f3ef2ae12abc9bb2c9a0036b7731ca82b7c93e4e0489273c1a69fffcb41a2bbb9769bc9ecbf96c3d1b96e76db1982cf7a6df66efa6c4ed9a4049b1930d1fd307e74729f621711e7b0e44e329a77318a1f77cbea58361b6e2740eaecc1c419bfd5ba474aa6bf0a354d32e53c8b94083b0a2e23be2c0ac5b7686269730bedeed8123474aed9a732c87af4230fab334a7747618b3fd3803e8c72a827a93e803617f5353a83df5be1f14e6d7756b1f3a40f4fe0f283ca3fc61a5e468ba54fcaccf29a4a4364bd3417a813bf869cdb61b627a448378b4e517f0b2a12e461b6912067928433c51d60ee8f58728c6e241b99e634bae4c87cc946fb8e5dd9db83cb14878a889236e2ecb4cd78d669ee25fa3583774ed41239805c4b3f683793a0af6d44f6dff93d9f38f9009192cecca2fa680c6362aa92a9502e1a2f22ece5806f2026093c2e5640102ec0a1a3cca301307130497a3cd3ec8207861efa00739d99474a72613014fc23c199d44411426cb1d839273f023dc5c52bfc84ffa94f800146a11c4172683270df33cba893d842a6e3f5c76ac309fee12eb5778d59133985ddc7ceccac7738e6a5885f19647c8834b382d98292cc0d220efd235b0c9e5eed18448a71ad4387a39c5330a8b4b60fabe7ee5bf571dc58f7e834c409525ce4f42667da7c27221a762cf92a24297a361b9a1c08984916838e2b90ae59912e0328bd177f85cc6c0105fa358b5cc3c3ec7c7ca4cf5594470de845c2a0bda887bf06b102ab4d4546bad86e73ea22a8ad22bffec041e056ab617c9d38c9b9ba05f25711c0113483b8499465f30afd72d24562e5f06ff0c18076db2e1625fd1dcb10c47150ded4e39c172dd361629b7fb8eca0b11d6ff5f753a012b07c06140bb8a494fba4189c3b5554bee81c706bc071d086eb3a708003e46027566d7a510c4a32d350a32c38d2f46aa1d0d2f1bc643b728d2625b8e8dbf60560289cce997c8ce84195fde66df9c44e86bf465662a5e14dc459dc4bfe52e5e35a71acb79068789269c5d9a6bd569b9e2fa954bbfda3b7e92a3c0a0888860d87e82ab987299250a6ed9f803c918914ee886524999c89e62111445b8d7faab19c590f20924bb086bcc8fc13925662c0885885f90b4e01d46d27e9b9728930040804427acdc74a4a000565c9b193cd6016b1c729c20253a5604fa73414a179d42a910790d78f302bd44c1c0cb72dc1dd8735cdafcc34ed50086570360f1d14a8e1a859fff88a7a67277153464311d8a2e75c261d5dfae5a72f5c5ad1a1d43f59606ed283536914ecf2db04056886f6e4bd3408392849fb84901059003a3b4459ba2c28939b05730cc5f4436058318e6172691532d400f6b62feaf20f6a76d4f2f3845f8210b41a24c9ab3a482590bc51a84a846114b3c18152967b7270101bbbf62f1971d416f15e3ac9d585c195d5087ed86ac123629cfbc3ca195da92d4994a981dc975b6d16e641b2f571d5b54e4c1a6173eacab89b6092256cb950aaed94828a0c12ec871fde102cf8e24691eb3b8d0ca0eee9928a4a15c1367c289b0c113d447a10e2ee0e174d840631d8f7e397af6e3474cd373ea18c56549b6b02339a2ff69a0a75ec7d4d6ace58921fb64d6edf0cdcedc22e1b83b591c4b07921c0a8fa5461b164ad7fc22b7a5ae0544a41ce377521e928572437ca884d9b81cf2b3ff2f30666aa34d8a5950ed879546469101a2802699c83c203327e98a64aaed438903521d9fd124d025592c9f489c1b5e4c747e2443adfe1250764ee434648c0229ac589924c9f210d2464df0499c20c2834095e9163e1108858b7d31591892b7099f16914ecc032871a3032640934c2eba4a3edc7cc1dc220ab421e3ad17b432ed53cbd0e2fdbac8dbaa3e7a2cf272c99c8e07143c01fe0701551001935d972a825781d8785c1d1346a9aad4bcb500d13ba40c456624b6c004107b369808193e73f39c78c46dd03938e0059787ba87e86b6d564c748e68c2f56f14343c6280b8f1f0f27ac36be9810d3f0d58457020c8dfef9eb903865bec315982a9d5b46f6b8e4925da7a352aa46743e069e32d3c411bd5047814482e8fde00644d8d760ed233383ae1917581a52b07dc8a6580d3209abcedb4f17c283a1624acba2be17705ff70708b9529187591b4a322bbc550a84255a1221dae3a5b04d1b96638a8ce029b034b28b178fef4e96d81b1fbbd76f382d6e44afb3d61183f5ffb4234462a1f8cebcd048ae1440d04cdb6839b90fe044a95adb1cec8cf40c57e6bd84988c2eacec3d2704744f756958a6d09ad633787d7c0ff13f3848b33feea64d5008f18df8d9390dc066319800e12aa7bd3e7ba36dc7551d27f98f6245b9c9ed67278d90083ca3f84a37c100bc90261720e2137a575eddcae26cc9d63d26c31b75235e251eadd563e185f179270e2c18d9a0e8a8a79b03f30f52d51dc338456cd42609998c83eb81ee6476438ff07be23f18e6254cf8ce2a6c5cccf15f0822815983d2ba8f2224715e930ce1a20afb895a5f6b02f1a792c4e93e2bff8c1630255bd8792d24e16b35470c5980919a716af5ad41678280d5dc21345bd0a12d072f99022275f5d0aecc225e592a7be5269d3bc4545fac58c2c61aafa205a0feb619aba5f6885dae185ec3929bce58376b47e323d290e2fd4a16bb5b6fa341986ae029465dd54a1b9a6da4435c59f6dba278f1c8318c7aa3dab486e8bde7e6d5aa62aba853d1cfce5769103ff4bed30b55017e65fc350b60f9180091a9b0422cabc2b89d1e77ad433a05f82dd98e331327455878ba59b60c0fc29df10b2b9024beb82fe4c47769ba127d6477dc5f0e8e7580da6a7d61419224b441f12ecc69a907371e27f55df27a1e81a6f077fdb99ae83221c297ba593c5e6c175e98359ca45c18fa911d3c898a14bbd217c094512a437616ee7c77711cab3b778609bdedd1b4bfca9f4d6e33d9a4b01e05e9826289872ea41f9ddf3d6aa5be24a94e0cb1b4a2efcb9b298fbcd9f1b249ddde7507897253fc4607f593d0fb20ebe865f04a4b417a9b7609480e6e2582fba781871718ef5fccd0d5b18811f2b8746f55080ad0e246a06c8a9ac496bcf994da5369d84ceb95cb9c6bad4dc07d33d2126a90e47b8c015fc0dcce634985574f7c7078ac611c52343ecc1657b53a4449e2c8c3bc55153d0b320f2730214b20a3e7531d4a965dc101eb0ee38142c69af2ac49db06938061b9a8707df3d95123c3645b126a170452a013ae318a164a835b904248d66a1d856048807d598c67f4046736356df1632c45421fa8907b2205a9173f6b684427325ca9aba322b8e45700d98c1465fe81aa5d22834cd799c463f9c2cb7f11ec7035aa591d2b01d0ed9bb4741a1508fcdd3e649cfbb94cc6a4603231dd2cd87552aff2e8ed4ff0554948be6e9bf892c5afad1f81144e0192d50de89ef12172f3dad380d4c36643769470c167b20110d0490ddfcb68b37ad2e0cc7be9ea015b2ab10cdfde5f918568704cf6420c3be754030a82e44e9cd779b5143ce2b7b97df0ef24a39dacdbbe5db7e2875c0a63f4a0e9246c19b5e2a0bc1e6768d902f5ac759b94b8bc628aa2fd7a0e27f91e7e28a3f57472c42568174ed3aea5466ba0d141aafbd08fb97f33d1c1bd6b078906206371e73945e973074b2ec2e704d9636e939e86f59d4786d70b95d3aa7a717601a646954c6fa29bccbaf461c871374295fa69b0fc6481dc209fc97e95c3123d8efbc529411260adc03e0795102dcc59d4c42a968fc44cecf7b10b7d4b6108e859658964e79c271d4e80d8c44d085fe2b412ebd911effa28dfc176a3480f946030c1a1130db3060e8a5cbd6ccb77dc180a599aec5c42fb9e79ff8f55f96aa54d10fbf64301c9785bfe05cca19cf6777d5cc024f66a4e0ae9ebffbfee8ac50b0e26286f757f2cd690f8bccdd9dbf1a521152cc37844cbdaa379e2eeb9219d5bd12aedf7de2f1628f7817ddf5e5ebf7652c45ca59c35c1523ef77b56c5a63316364c4cee28639c0855f32e25af5b01726e83062019fd603bea6dc99461ef15fbf0042c2a20fcafb254dac172d63c07af42277d31de8a50580c0a9477252910b4fc2dd04a4f40d1a97d7ca346097c75e63cb9e22110b482ce3532d7f065fe78c6b0d9f3aedc819a82ce65ac62c305d66f884908de6b50be2bb6c4529a7a7e1c60c29872d564b1f4bb973e00512f7b5187d604e74f5fd2172cd3b614995e3ab85131f7d4a730b5ccda2aceda7616dd062fef87526df3efff82233a8034702f07a86206dc521052016d236353802e0f5110bb16d345cc04aa921731c7d4302c098b30568419a273fd769a2d2c3b2e00eb671605c735ceaba0609f9e2920c37a366d4c2913673d03ecdc010ada0f9fa9210625d90bbe4d6bf514c50eec9c764287f1d6cb51c125ee247720402991129e918ee5df442e6ed64bb0480977a6a481fbcb865eb8b22066406ee7993fc4b9cb55c88e597d64358dce73541d5c60598c7ebc0f66deb1adbcaaffe3413001a5b8cd927140646e0305940e87a98a96195ae6a0cf2d2f1cdffef1a5c5853e4472731e67e129078b778fa65a362a443369bec1e28286e1c1497a69159243b5c8431079357e5eef6529c85009648ee58bb520caab9a10ff5977df54d995ca6fbdf9c98525781428c597510856bc8a7f4854634d97064a8ab2635e1996ec44258963c6a2d061bed92d12d0a75e8b705e8a45686a377212fc1d0d81f4c0568e3076326f02cde46b1197130a175395c4af1ded2685c9227043a3742132ef9edd7a893f7eaea605048b086f37e296080984a44de5c391766cca99e37cb05be387647c007ac8cb2d8b78a5083b947aaab72986680c42b41a5fa4fe9aeb04d37cfd9cfd216f005e2aeeba6a5a5b72acb98cd66293f03ab5effc98f70d5a114348f58499d4e99fd4485e2d20df0204e0a85ab63366f5a2026bcf5fe6a0bad5e00ac2b49aed0350c9900faf24452c6fab8034e71a54a430614428b080f70528346456c4d3e22663907e95c2f8dd76f1038bcfa802fd109d202d1aade0e3ab940f488830c97e17524d568c782c0956259a7b84ead1d1c6698b67fc001b1b721409fe8c35ed5b21f8d9cb71ed728f68ef883bb7f225aef60a343aa106c73acc476722a20e371b85c4ee4b360ba5731a559108a58f2d1cd2c51d7f9e493708981fc2bf29fb70518c63c133dabd53630dc7695d4fe5e13ce216789f2055cb33e43a335a6e624ca2de3c8700ea9463922eab08590c281c517d9e73e37cedf497776e0dfc8b2374decca073d9f5da7bbad3c8b45a6b21b65b79b35a8d15c7b06c961e822639200c2925dfdb168b5813bc251f22e6ee530eb95cd10a295ce5c9cfedc1f2e2c718f3b45d82b97cf9db3e9751817977466ba131e37112964c00f6cda91ff4cad0cf45a813d412c2d39c128380683a40f2761dfdf5fadc4e17e47f876bdbffd1a92764051bf0d32c0f4f1f635eaf3caf0d2e81ccc26780b9e57443b8914360ba4c9700d7788b67d8c346ac2940e3413162cf431837799e80f2a61d141c2fc41beced29b0f3dfa8337e18e7a74b89f809ba6336df23a650a539cd9c41f8269461a3633c1bb15a8d9d05775aec94441586b7ab41e34d51cd809ee39547ace9e502a2d298e79583d984bcbc871fb9a15e797bdf081ee3ffca60e816d07f52478df161781dfa57db579425c161dda48f37a824b9c841b1eed767f0f72d81c8b5155a68c26abddd9a3eb59deb68aef2564120887b70a276d0abca538efdf01bd10b2df9359b47c8188f4743b35264202fab56348ddfb6e53023d254120fa423bac7b932233e04d81987ecd36240f8b13a51ba5a5cf90ca1da5d2db859b72b5851a69d5cfc796b5f3128a35dc5eeb4f7cdaffdcaa2a67753cc62a724499c7f30bdb5998531235a2f28fb7e82aca7cf2f6f66963663a0f0bb3aa6967da43284be65bb67b459355d505361b56f5de41c00d6bc2c0e1e50f2e69670c6c2510b0b78fb2de505d14552718de048154e25f15ca5d601d4e5709152f21442aea85d9f9ecce541070319701486a5b6de6d0b68fc019f4dacc341944d01881f35dfbb5db15d5d536407cdd48a00a08f6722fee9da62ca7d5eb1f79c062e37e6043ebb0d574a2157687047865819842d0d54b9755ae819d94f21a74d49a11cbf1fa7f46c7e4a240790f0838fa277ee526300a42bd639211c5c897e27d3dd62f0810419ff91d736c7705543aa6970b5906a00f0528c331ecac3d65132252e5cfb738d22fd61abe59fdecb47f2625f71c0a7a4e8f2a35ba319710dc1bbc91e45b3cae514b1d35e92474ba5d00cbf58ac81b316c92c046a737ab77ba3ae5f427f5f99875a840600f3a251889c06c69b35dd93c0748abf2d840221e175b59575e24a0502379a81d1993f2385c22cac6ad0352c5a7b4569fbc8f24e33068b2069ca51260bc83841394c7c70377419c695a2ea57830fa920697fe773af9386104b5867e9fe77dc031d23d324f5f103f8918cc0870c65745e92450363c9441d1358a09e980a3406b492e8b56034ea45fbda1d2a39d662d89f30571482774596511a76a216c787d110b0818357eba85301346025a85bc7510f7db4326f8147dd948b7990e1c4d0f417a6773cac38759b84ba54571363b141db3f00ef6391e817846440e79bf8b6a345e785ee7c1c970c7faa7a83f169aaae45778fa236f0910ff93a9ab30b450b06b17c4be38edf28ed54beb2dd8b400f4d7149ed2491f086bc73bf4ca10b2ddbc54e0bb7a7da0f31ac09ef3918709c175e124808a3999ae3068595913533fc596adc97de00fb0fc60127713d3a753a259385247cbb4a8d2f3e9e6337e04743302cdc05c3ff120ba03b172d83bad44bf9d0c43e2d14fa5a4dbe26bf3edb7567b46439cd5af8941ca941c9f00a78bcf12fa5ab04a7155f7362bb1d7d70881aa6c49d6a84fd9d7bed25cf12b675a580188b96d7f4148c032769bafcf4488e9a22aee8cb2b223aaecb817e44fcc186499da0b442d045c74977a0182d657194a57d0b79022689d5c4032cef9c47233adf373a370598b61083019a09061e14381e7b5de3675e5951c2284b91f1d536c01a96b80d681bfe1c7342cf8aa13c58f7ee588261cc62d72ba03608d4a6c09017ce6178f4d71cabe992f1720ca6ad41ee7e1a471fec179d122fe70ffb2ae91b7122f7fd03a19eaf171852a7f2f01df6aec0cd211715f88ee26c417bd4f755bcdeb6bb8af7f11e21d23cf1412cc40fdc5249fb91718c328fd791f021a569ef2a0b4360acf1cb30477152e6e8960e73215062a5469d683e17d5c9dd428e7617b9f2b286d873a30aefa9616cd6a50eb45923b145d2dc181656dddf352848363793099ae70b7d148f076f75c876e663d67aec832da9acf9cc4e6ff47042b61f25341ea74cf75e05d6f6fe1b2c57714a9ebc86bb83785c384ba7f6ffa5ad508bd56d661077b46b122abee5efc7d17928b7d8c9f438d6348344fa852fc7c6ebeae4ecf73f21ceeace43f09bded1111ab9310ea9d110c7aac70c95693cbd679cccf2f415fa98a68d0b977d788049a2acd1b32492184c7307c59bd4ceb7938e29e7f2228d807cffe217e8f8425042bb7b8fd1d39c1d104835810ee85d9d002691ba31b315d7dbda83c2488e9998caebb5a8214d5686eb343db95293110d5a403316c62a68805c4b110a7a0f58cfefa6795f55139d461eed8380395a36a9a81b5bca5745838ad69c8a773c5dc9bbe52cf4f41d968357383b1011eb6c8dc9206c453698d6eee7df569dd3062ed606c41201d3c9cd5fafac35c041c6dba1639f679ada6a6050387bf92da6283f65a6b492ad7352d0d6e60c07e82d965416bc58bc7fb9c28b32c53c142aeb741583ad852539fc8ca7dcb6aab73d58048aa53799b4e7347ab61b99437722dd893b3ab8537192d0b48e1a6ea5ce035161fbae233b09ab3d7709a692144b7afc5c086b75c710890652102ea8103c36832e3af03f1a3675ed4f468492037845fe500071d6c78854ce9e5035cf0908d42af389a7279857025223b5129734086d9fa073e819f579612be1088baa747ad6a04482593c80d7b9c94fb329b6b584a22124b0b8f4c8f1754d2b62787c9d45b2802e99503ece3524a8e6c937ec76378eeb205aef19180759a8ca585e650cbcad607ecd1e2852e64c944555872de4aa72ecdcf75e070ad5036d5869c2dd6e69d3a49d1abea0ad63e0da33874de31e7be95287e8445ca7d1f21662b2b521284aaea96060b7dffb2c53d4b48169ec0ad79ae4ab61903c4b52ca4c9e5b780fdc11436f720199391c2a152609634fddad933ba2e5d9a5cb2b9c74df4bb3d24614c10bedf6054156908f7ce0a1734fbe00f94669951cedd5f840e9e35115584c20f83b5c32564db895c83cec66dba0e755ddf7a879263f8217269f8d9b6dc7a5eb9e036e4aeaf81376c352c9d856b0bd0c5da5e16f240c5397997d0f91c03a11e58117ecc94183a38bd9c499d58963f1ab53ab527f205f30b5a470e9071a60c26fc104025d13332bce4fa8612fa0c08a5ca40c59a974a74b6cf415cdd1ef4d8ad139d6a698140381a0773843e1ee27100ee834271691c36f6e5fbd533154a520ddfcff167b6814c80cdba2a4d5c8046605504caa6e68f58fe91331c6da7f049c47fe801a059685b37a7538468e141101b320aca9b15574e3cb40c0c4f63186251d7b357c14b3939ecee0e8b06093368e31ac552c4beb14926cc567df061d557efffbc3d23fb9252ac00d98eb3e859f0ca224b49332b42e8ad5f48ed5e310018deb12e138838782634b0fc921322584eab99d2b0f64e812cc434af76ee9183d7ae3b2277bd1c3c8e40e38eb47153666be33b598cf20a631cbf7d4a33cae5df5ad6663932200ad1ef8810274f2f74158630f448c0f2784b9ed7d4dbc9246b781d75431ea1d5b24b9d4f06c705e26ac1564fec6a56dbdd40971d2ac5bcb312d1e7df8afc194644949f963aadb5179e486bae8bec3d3cd9e7f34eb9265ba15ba2d418d127d10f5bf6932d9dd772127c402861284d219b9a02b5604c5a63d229ed490d86cb65284701284d66da7ce01061ce67e57c776ace4eae8ce73b12d28c8cb3fe8c68b93760a9f0ddcc57576150cb9f1be5500aa1ff19e29d507c00d304cc259f8f78ca64e2d2a3a6affad81ae29f5e02a4a75b63452a6986e01f6008e8bcd2425c572da25d6c9de12654d30925c0211cce093df0e3306b306cfdee61b9b6563457dc5f544e8a4313ff58442117d00f965f4caa82632ff86ecb928b91b573b826a911268bef3e737767a7481fab72c8847e0c91990a1be7f9026d23a8648d4b292ff5d91fbc31b16447092b4cbdc11869da3331c873b4162775b7bbf8a364dec86255a0898a939af086951ec2de9179b13fb1d9be0c61a2c27f443c80bdc5932ac03d8e54f63a32251a1feb1ec64926eefee9c00047723f0405248aa58a009480bd4fe0789d28fd2c20f73ed4cbabd2cedf5bd6a01b5d872ecd8ee3af1c63aa598aab739ac6aef0a472fc30db39c414fa2dff521daaa0a0f16ce0791eeeb11c2691c14613f13c0ca4180ca0e36a6a241c6bbbe6aebd2969cc32b67a761b52387151423981010abd9a5cd265b48a891ef8d669b78365aa825b50ef0e939e701b2480eb5cdce5d3f6a0dcc5b40b850db4a85734abdd0a9c84638e01c9abbb63ff533bc2c1795616ee0a794693855013eb5267093c36e1a8bb6de3fad2f23a9a4293348659ff86d3485c371d42514626ed59909cd260175163186752c5c96065ff548d6872f9ffd3938d5920a0f9f62478ee723664e3e991c8214584d5497ec0680e9a1054c7202dd8a91af6639130b17b8287509d6b2b67a4d58a99e06ca0f8477d7a58369728625b20a2d9b34c75e2325199e5a1c95832279ac6afaa0b26fdda0a29c844e387a124e0bed0ff36ca615e4c943b08dac0b6d81b052e61c2689410f71d1a73aa3a0d98b497f3ed46398ab9e50f117f6c14186930c421faa6c36e027e783b7eda66e846c1c1bee85bf4bbecec6edde2252172e9eec97e1a0b4778979e17bd7cf3bd7ebd22c7cf6f141bebc51966ca6b614356e2fb14f68344ae0b2c0a6b63567476ae568ca02352381a4215cceb990014507c8d8fdb72395c10c2241fbea96ee2217e1c84d02ec5b9ddc3152516ee5e8d5db8f8912d878d80be9dd40996af48fce439c096b3f8a98243e3fc5a4dc5e3de5e395c07dc97f1aac471e035d8e2bfe15050b82d429cfb94ba3159fab44c6ab47da0a85250d7c744106e30173eb1999f7c0aa744e1be5c48bf9a7382252131af09378e7b30fa0dc931b038640ec914560898d67672be58016b46744c26f40665f62e3df19d8e06c678596e003970d8ef27d4ccad6606c604fa35ecc1307b6636dbb9e2e0bb2655a2e6853da0405b46b34c8dd896efbfa8232fb76ca72e9f81019cd43b90f17258c83b2647cff6edc9482c2bc70f8f06f2b5f85ada409645c2004e4467e860bcb94d9515ad6ec6bdc5805e64fb61d370c78d8521810c4beeace647044caaa1029895bc081fbf144429cd43511a617168e6c284f59637c3e5a4f5a54026c7b41fc033ccc4a5eb3f5e755bc53c78a242485470f89a75d3886e3f3084e2fac63687d909301750909e50ce1d032517ff3cf74394c0745fb55b9eacaea755b49ce3d7ffbb5006e772e44c515634ed070b3d17b12ed00bc804250795e366acff3eaca9fd10253d85a7e42d910a04aa4a9f6e4b444ae9ad38e8aa55c7c98cd28a62d77d6c2f8e11e68256145969a64064afba6907db245101e8697c7ea13ddf006ba009a0317ad1f13b9a8b8e093295b36c4c60b63f69201bc9f80b0dee545dd1424b7b5c6b9e86083d9944eb657fb52d3f31b9cd685fd0dc7b1569d32363972af0fa79f9eb594703db03666a0df809543a49cbafff6ecca44f7c4b060b0b0e26e82a14450e01fdf93721f4426f6f0112b8c6f142a8e2b36ce592b71722872b97f087e6cd032de86d7d37ad077239ab59cc4fc1fab31b1707b3475dae35a549257f9ffca6c91803436d1578ea785983009fb0ed6a260a78a369808e7d9443ea72db6064ad3d81dc570c439a75053eca6c2818f64de745bd038118e9f8ad1d9c121d6b9692ec03c357664a032601f14977cd42fdf80b1ad8acaa4c2fef26abdcdfce576767b43392acc642596ac0d703a36f703bce422a882f09530e5fabe2fab72e532ef80b191d9544c2ba5945c7e64bfef8380d8443a19134da2ec89c6ae51d0a3958137b6d373281bf2f32295307b6910cca3cd1a5017e41c3aaf4c86755dfbdaa48c63bdfe69c9cc5c19f4f2ffa2ca9a13e0bdbf65915d7dc51f9915df6cbaa72f5b7ac449d7631252862e91acc5bdd8e2c06a45add2858dd1ffaf624fc6dd02a309d596aee95761ae64656e98223cdc0a058aed3088c39825e494cf737f1b08f64eb123768dc17677965a736bf361b6db278bb5b94670f5b80ae3d92b718fb3257f07746ec6b973a5b9ac9145aa4865755890c7341e800c2bef8f1e94018eac82ecf5714797b646ac8d641daab482d364b1d96ec139d535fbafe990274e5c0906d33fe54891fc8dbb44ff5c0a70d115368b53bbb8bd97ca1526e082e4ff9fb2ebd9c69453fac817ad70a4e6d6fe3695678bba6b33bcd554e122530a042b391a5aaa8b7d842fd5cf6c85a3a48b8079ea9f921872464c996acf9291a799a62506181441d713e408fc49afa6cf72a5bea0bbeb9eb092994f6f2b7b61258c81d96b3a88e3c40159c42fdaafe69f74a1270464f13b9895f265ea42135da7d756f5a0192c29fe74a98f30f14e80a86f84bde46224561f4c6376010a0921e57ba5ae7bd5cc91f91e90255533da8b6ce3e53c0f6b61d4f8e97b29f10f32940890ab7eba61b8d8e5e9750c7ac36c947e5a273d44911cca3855801e7aa0587fd597e32fd84d509a7263cd8b877eab153a602ee62862bb0bfe81df77db6c25b1af86906910bcaefd65e5910a14f04694e004afb59283cacf30e35b8502fa462c6332bf3b8eacca80600a2f5c6f9816f267960f456a5cab919703c4a90e7fb79fb246cbb2d306a94705fa00caf84fe8f9027d24656a9ab0f5f9a8d4964c463a0af65943ad80b8da53136f2ba348a1e889df41c6192d228c1f7dd862d0cd2a88314ffbb0b4883176391d5db90df09755b1a78ee44e57cd62778b075977a671130c4c442f075c55d3d4661056262633fa54aef6e3211cddddd7f3c05d1dd7ecb42eb713723133807f5f0ccd79c24e2a83f2c61c42e6887d4ede88567403e3fde8731127c1c40114013f38e7471b56192a7351237a6cb9a8f5bed52c507d1dec36719f7610f6cece89b34b29b29f469a8bc064aed9028b4edbaf02cd3f67e8df868b0128c721e38a1b878f6c111a2fbd5c6eb20860ccac7d72f6e4522f76c5b03d5f83a46813febad77e7fd3a03f24791edef1224963674b91729ef6fa7a65f234048f206f0f5efbd71e447881bb6ccfe1a153f2cbfee84d7c0e48bd015f95d2a8faf1f31fe37e185c6ba7ea31488f8cc2c5339f144c02c5c022462aca0aae16111cf4ab7bc363f25a16829483a562f48d811320305c2a702bd85d25e0ed2ca9e55adf352f9c45bc1eec570d7ab822484329406b8cd544ba7f6164b817e25f476c54206218db376b3d2a59bf7ca72bfb6223900c96ed8f764596fba8e3b03ded4f924626b7a8612765af59ecaad4d60e18766a408d48b8750e0012f6f5a9b552063dd685012b59fe8d470a059b4755db3f25634f0a438082a947cb09b1ca8005ef53bfb2c673ff9c91bccb6071c4d96b1aee2b2f8fcb05e6223520844b0f92114109b492f0f798345417a3ba0108b57da5f5025de22c44c2bd53e8fa66e8776e99aae07ab72fddf0aed78381e39dd60b3d5bc140022629c4150e5f2c9bd4f5e0ff5d308a36b6bf91809741b0b246f9784768b071ae995f18f5524645ce2e3388b61d7f457f1ea0fbb74b829aca1c82f47e2c1b746477f3752e24a1ec4d3fec588af7437cbc54e4f15d30e55df5344fc100bd01c72e47b383c16e097bcb6974ca55f163dac2fca4689e64d06002e64c3b85ca910541237653b1681805065f43c77ec9d58fdc3e328057a4e098a932877650c890c6537aa2d8ae022dfc7075232a87fb999dfca52d325340f62a9f6825cf2f57634eb1f2484b10d72f200cfae32bf77c95d2940048b5f89dbf461c5b0ab32b936dc8e76c354cb8bff1bb089dd4cb71fc78a25afd5c9b8194ba7cfd68af8251276571fab9a2a214409fbf8c4f9f62a87e9925e049594af8c21e92509189156c2628074bd2d516c83bd4c4329754cd0487cdf6ed75498e7bdbedc859a730a7f7f132646d50e024c7ce42491062123ab455145f3900626a70c65e9f71e78104c5177332a35d4abc3d3ca7dbb9c09967e326be7bf06889699c3f08f2cc57f313398a37072d7b775f3f907158cd7cf26e81020d1b79d8931741a107853033d403217af97f8643a4472b41e4124d7d2dd66e9257952e44c62b97a6a0a41018fc15bebe7eb92c6253f3d10249e4e8762d80598af47692f0408d88142698d222e0b7ea34fb48c73e554814f7f9c305f996d5cc9d74b3a5c7d221f468a5f6b427b9cd618792a25f97e520e6c2d9ff1ce8c6028991b3cf79ed0daaf5880af5a54ce2b5180b596cb2cb56703e5b373c704b8f813a66db036a332e2da00d2fe3965a789789704436f755ee31a2f7245368a0bee5a17a62a02b325cc0b1ae6119998d9a3fdb2804e401333484e4b67c64758bdcb45c67a1111226e1959b538b30b3e0a87a12d72e35d59f0893901dc1826385fdc6ba7b628137397a6a9304c74784252a6b688a83c70841333358e07454d202f735bef6d4ceb38b744aa22a1ae634dd144bb1d448d325aa4260ea681735afc3ca06d71ea5d44c9ed22f3dcfd797fff7441fb8a2bf7740d2a5b6dc4c7f31a5f0d12cf13095cba14982e2cdddfd3f2a76b98816ca788465737183214acb6ea27dcb421ae7fe3bcee22c953cde01c3f3bb3f2e6033c7fd4eb766c9248758cbe7446d78d7e73a10f144f84c6611104e4401f4414f9c2857da3acad1be29c1d53ff1c1532d70f60401f0c0be24f0ab2790dc11e4ddc24b34add8a1c1e4435f90db714d19fa5a09aae39b1ce2572d09e836e3f5461383abb633e12b9d2ef7b41688c59065e8503409531f40d1796593b6285dfc8eb796c680427881a3d1f3e708efba17a82072f08e100029d60020c1959abf46fec4d1e09e81266c34bf2c7a868e188b43e06cd1d00ba7b0445830c5a7157084772bd1d78a6f288d50770585704e608884f81084a1314006c7065e25d5115e19b2aa477f93c7c3556741b841365718e0fdf95a9e359d1815586752d249d50de19d180262c0586532741a2a5b23157cf22ec71f6434f390cc0bd9b964d5c4d4f771cfabba4835700121850592177247c56f333d78eb708a9ee8ef1db354817688a2d390f8174d9b25c900ed96c4bad95ad289899722f83f0c67524c93728eb379dda7975af1556017c247e5aad3200ed05abdb845cdb14b5c8d8a3209ac3abacf3b876c4330fcad27f6e38047284c8d36e442e887a87bc6e043cb40bfab7e74585fb28096dd12c42b09eb6f9aedb0656e75d33f5adaae573fe2df8af5b8ee3f9aa74358729ac1ef63b31217e589b53f3fd86c6e8280ca412ae09278de009871e1241406138638e82558efcc1aa810a86489bb6a459d46e00e8b47c182b42d536489f049499daab56448c33c9454c05a9bde55a23da8b2b27c01a900588b311db1f700106c6f33d05e8eaf5ca2af61efc0270ccb94882fbeacfdfce810ea082adedadc1468baac0b6bb1a08108ba165bc4f71707edd2448e3bf2c011fdcfa770dfc9851a347f8367394d507ba466f6b64562178af752408f4081c3a998c9c8f810caaba2cba021783b75df7237ad0bb4abb161403e741e6259ca8c83d6106b0f007ad5818999b45377e258539507504c0f8454a97748e950b6fba1e08626aa0d79286d44831da1d0c7c12cca8ce0dfe0fd426922c3df06a6d6aef202480f38aee00a8a8eaf49d5cf199a59b5cd3f9e30e23e806c22bbfc339c64e1f2790eb708f49720d0af75c330751384268f5b6e712ba0ee2a4288beef7aab2469336714dd765193976ae746218e6fc2b75a3f413b2468a60238c6929a5e9b1fe24813f95b8f03de93c79a522d4d9261830ca04cb7e35317044af356e6940161decf98494cd3d82a23cd4113e7c98b8cb6c9ed36c24546c1eadac466c83010cca630e1fda2b81446e70ea1effb5c114c93e88151203f4bb4e4c2e895dd47310c80b93c7d8db44d9d41860072c71219e7651bbf5d3f5cf6ca4d9c871de7e50431f87fb2380690704fd11ddefa4ce3044a57ab90e9bfd7426c61195644ff724004b871ea141c721a6ee154744cfcc36c85774c32196df919d473c7e6ae145ca47f29c38519553efc5386ed7139d30d2765a2d691b5e7e932ee224d24d1abf58ec8154682e1a245a3adb6fb24ab5c453385a4e3a63d46a2ed7eb923a86df782c74e3716f87812da472c91e04852b7f7002ada2cfa32c61561112ab25b45918a7c6c161ed42dc5625ff670e765e643267aa1e886971a9bf70c18df3c73bc1cf864321685047cbb565659a208c7a349398edb48dc8844d9f5088fb1d0a60a51e1d9596980b9fdb1ff807c03ec024c387bbf8d8c86d4ff4f51dce3507172c8b1c4244e16340560465bc75062ad451ee64ec290abb8919c0e257c886e8500b0d3042eb4bf07b45bcd25d5d79691469522728064267fb81d18eeebc05e5a0df6712cd2c6881f9f23ca4ef946af89882cd13daa03f30e669e1bb2d48289bc3796271efc21dec004c2984b3e3deb33a0b4a3bb574b7d300f7bd89b6d01c72e7d2d3b391f0c9209bc6ed207eb8fb03323d9777f1b4228e95d9572670ce165e71363d040bcea587ed209270461dc84439a8f8d6225f1eedaf5222f5e4d4342aae9993f1aa64e6c30dce482aa7582278cfb66b4eb071f258f163371561a1693b0866fd0a6bd1a87699bdeb4b3787d7fc76fb09b6608fde1886d0a35488b0866f224f6f1a694d48b3d2281f42700f1d847070f1186bc846c1e737d99d51b3a38778b9f417e2823f1702806fbb555899912ed4e5a0ca8147efe442f4dac3832683da8d87230e33e095f21d6265894f002272419cdd48d6c9809b48bdab975f0a93382c230d244cce03181b63590d6cb61785ecd01b5b3487db762865583ea813c696a0ef9718e79c7c0caaa6488a45a2ac8032c179d105fbe85606ddf96e8861a48f57866b6ac0ee0b04729811c33220a6a4e25f28209a2e29ab4aeb30172de98395774f1556ac756b86348dcd37f86915b4bc7ced33bcb30b56066ef96e6f4bd650775617a210074fce1adb1b480bfcfa40b680a2ce011676e4f34c15c16d205affc2115b4a1e58ab201264870a5fea983eababbe62e5818b757e1c7c03b3e71ac8d702ea4a24f4952b485f8f6bf21be08e3a3ce0ab90daa8bca833cf1eaf140880c6543daebb9fca75efba097f7cc69e12480f2f02b195a438f412ad4a64ce71f623c5c7fd4bef74793e3c43c7768101dfcbc81eebbddb6da996b69332edf90b2c20c1a8486145951f07e3572d1d749e93c3df877168d646ba15e596dc0b0a5b3b29052cfe8bc97130370f225706c6e634f56d78f48aa84be647ffb24a8884e7ef32f5e4362b2a12a173507a6c0d8e7a5d13a654ed687836489320109aa34fb96b7895f7cfd36845c1d4d740d7f7e3d97e383f6d1662ceb9fc3040d9657dc39ec6ba8e5865bc4902ec3ea1ded22f605687fda7c41a9d32c64d34c739fe709ff85b4c6c29209199951d6938d3accda101d5b98ef332a495f532ea1f948cfac70b35a97c6a9de484ca9f88753bbb6330ce796402b7963c2a0e19d89265961644fac5c4657a9c5a2f57f1bb797caf7fb8a93cbf7c36fabd577281f04aca21bb4fa03f3fb44b6df8f0d073835c4b9c00e783e61b27d983d1c0e3d98b69f0ccb3435403a4672fa9815626259326b14e2196b9be3c9f065c5e541bfc5baf006694fdbc4471707e5cf9563e8811c800f0fe1826b0edb1dffd7eac5c01d223446aaa790dfc1de8fa379035e60a7a4d3f0eafb19cf858ae34fbfa7c6c177a45bdd92df8b8aa1365c1602a810c60592ec3ca42cede09233a03bb118ed1dc8ada103b7c11ce0f5bc52d965a67cdbf566e8e48bfc5d5309ffa6c2b445d401fde66abcc420e08b80842014a027348ec764003d4d5a571f92859aa2d3cd92c24852ec8d30268466950ee32de8676981d1cd7f10306381c17facf004a75e3c8f990af28d3c93949e026bd5d0e106a5e22d300c300d6ba3341fce88aba86545339c439533635d23d7f366bb3c30dedd3c5a9f0cf582af0dd58ae02d42020efbc573e56566b5082dc896301f0865891a696e940a5290ca8ae72a64ef7b2f68e0579afb751e86b8081081d6572704ca4547c142950c4b799671e9406b26aa120593df506b429097b17bd14f69d4237c239106aa023271626005749a69420b8c1e4d1a64af4586598243e364acf86a96f2dc482727ec959787ef8709c37f67fe7047a27f03f821ce91c4ae6f8a9e976eda6481d3d334a65f214b12565f4e44b4dd9ca99e61c592ee194b5bdccbea48990d4a3e6b1812c7f4db00c7d220d7488276622a16c4bb8959d211efae018b3b150145347b0076c2bc2d440aee2643401e32175e1ac0d46e03c180288a30cf18d084c5ea95568eab186fd3076156fcfcda8c61d95a6fd5fa5158c4f468c4bdd8a0cdf3f5dadd98fd4cf56d990882ac2659adc70eea0a515b8352e55cbc216cfd3f2f3d07ed9d3961a8d2514c12d8424da5f64ec041ecfa86314066f4cb822b84038bb6e9c1e8e981cc9c32d80201f60b395fd2adfb9a4e7c865729f8aa2e945982d83d76699ac55674f7a45de18a48f7f79f02c95d05ed06a880df3aafef0096336ec9a4023fc518486b068ef272345e24b35a85bf250ebfb159a51a66c975b6cf99f146b54480cbb642ccb54be44fda05c0db63a35f5d1da873dd522cbdeaf0e8110cfd342fe6a04f76d9b94ca8e12525f52fafca091fb3baabe52364f19f4d8238de9aa34453cd16c97b9e6a85e295b0df0bdde411765423e54f4923462bc4b91cf6d5e746d19cd23fc15d63fd5becf50072c54214f43817e6e4d957f054cac0e9c24b93b665820ac208c3fd388df7ec19510e4884fe23f319ee2265aacd350ee937c72b738741737cc06ccc86eedbd1c77ea9679f9342a259a07934a5e0053225f32400cc6bc542b21965957e4edd091a5a2d3275be4da9cd22b0794ef65f2ab0fb34fec7a42f5da64f2540a3f0baf66f29fbdda4293ffe0bbdd6999689edc50911d3dac3c03950230fbf34d84d98e9e8f00b849bf9f7f9a78cba53988f3cfa3c97ffe0dafb10ff114d6a7e53f9fe64b370a7d690e4c5ff90eb69f72332aeb73e0018dbf32d7ec1e453867fa810dd089d743714388d1cdbf555dd6a7be07461baa16fb81914dd4a7a629c65fcc69623701afa5151ced1eb80420a903921115c9111adcac37c4d8940052bb647f4228481cadd93b6b7f226c9008a1a0139403b5a84a4c89076782c4468b19dd9fc87d9bf411fb3562d5429d18e0fc3a46a58629ed7870008e8a277d306fe67528c16dc4111aeb0f67130652e430cd5c9a035fa86f94a5a7c8b4e6401f1cfe55acc9219bcf9be5a485521116897f5cf538d06983ac1ca10008b0c457824952aad8e46e923e1b183d83890eaa5fe593930de4f9219cb9a84997d39e04ae69a126436918823a273b8b4293eaf1e7cc31ec558e252c7bae682db0a0e0a8d4aed7f591e90937a22dc916850669e32686883f18b0ebe11dd21a988200ca1d3bd2f2d4d805e35e61534ecb9338cc45df7fe7f1a7872d1400121f4405505bf9f1ba2deccae23f2015de635718b8ec8d9af27e676178fce24d6343f8020903380c11db623b88179a723e3df799ce186e592f7ab0154b063e27533a2ffce79a4d3bb7f044956152a0277ee2deacbd5786c55cc0cafa4e0c21b6b3ae23cc57b30a6ad04a9c78f733d4236c846ea0e5d34be3798a345fb23ed6ce9c41509f1fb2c130fe383cd424d21289cdb16f7543b0fddfbf68e4f619f70b6db8d77d89c83e547503d00ec32d0b5f74a2f127cf14280451e6c2f3d50330ef64c18ab92e5d4dc3a05dea43d2a172b1a6d369f9249f0dc34b890eefe7a89e50de7c1337e7dab467d0c7c4e7eb519234bdc7c39e30c573e0c2fc9e2497ed1e4c5a608f571d9b49d02f223e75b0614a352e3e8500821c15e2a8169d75aa17f7d49e3dcf5c6cd99ed7dbe311efb4f823baf60c463f6541d0c1cb0cc3992ed7b35cb3bd9b8eea10efb54baab041c39c95f533198792ff53f161f970d9e7d763272c9768f3ea7dc3fdaf6700b93ca58d107ac1224e2d71982e6a7d4f3bff62055321b123cb052d433e0ed0682e3d6935688a344f6e987156fe23d46968f26e32fd3164afbe1747852442f85e41358fc41f07651d6b8dd4714adc6ba495ca2a8086c375ea30ffa28c0a4cf8fd0c70b8affbc42e88d82f8a9eb559644b097c876cf11f01b472b9dca501f4dc945f82806ac739b8df1a3ade6ad9bfe9258e92fcc5aad4899ae86b495646f40259a0a1c890968fc9ec0442fe42b466297790252475725727b13587f50ed0d85c28641f235aa690641b15e9aff757eeaeec12eca4bf33e753fb84e3046dde71ee355074ee7bcbf3c8a9bd6af28d6f446b80b7abb929e4b800a261f399beb1eb5a1cbd44767a2914798755fd611f5afc6667d0b9a20b6b7739bd46dbcadc42719b5a72861a5a041f50584eb392151473dad296466827eb8d00f970c7185c426b5bcf52033fecf822b0dad49b93b04baa7fe58514108c2c0fdf49880dc0200e584061337111a2b37e70cb6981605a2ed8e898c4ebc2455832df02ae46c161c74086feb8faa3f87010b6f696fc375b88b6b72c63da9edcd2aee19d29c70ca5a4bab65d7343c6ad3c6be93ba709cf8a45bc393b5801e12a188e23bc74f71cc7dd25414758ef11791771781f8f3b08f20a84d32d76d6e9035626f2b2fe85ca921f9b489eb1e3b72df0133c002dea815fd4ba796172967230242c8c6ea20575b3c31de623cd6a279a8905ea59cbf00314298a72623f4c4ab7cb9119dfd96dff7f8545e75f6aa99e835022a6c01706a8a306e6b18776d5963de4bd0c61785b84cc160bea3562c2e0360960bad3689d5981db4c93de2cddead492351a3aa84ff36dac7583580527e9de4b58d89bd8664b5a7247a0c44ed38cef910576e4a10c1abe656755221cde6565d20a819d6067a1cc81b4c8762c70d2646039005f96bea7e60988d99f96a7e771a0eb411b9cc5c4037bce68d08c476f8fcb5d67c71124854b27742304a5177e9ecb39851fc845a956572b7ab20e141888e60e8a9051bb5af1fb8ac8a855a1509b9b84edc3ca8a3147d67ec02e6c42aab232a9156a78ab16f50999e079246c2aea5f63e31ece0438eb2f86ad7d62d8ce13ef3c9bbe56ecda1f151a56427e93baf301beb005450451ac890f5547f80de79a715d514ac155ccc2822869c8f5aa1bc40948551143745907b6f508090100b041d5ba2385be169dfdc9e1714cf5247cb76c506e7ff9a4c5feb6f88b8c1f8de44feb61d495871cc08d821219127d1164e3846c56f7fb89fe37952f3729ba436612eb3d43378662a05a3e7c65dc1cbc44acb179e667a99c850b94e65779db71f2c2b64649408d686b50b8e0d85700db58d1e8a7711638aa3f9e12a89dc61408dc52f128c09bf390a7b47c42423b3a839a23be96e606c959f6b849dcb95324612fa29dae4227b259131cca219bc82e8a6f83bd1270293e5591dbc1ca8e727350f51d361bfa9bcdde56792c1812a62f0eab2dd069cfbe9e480a02cbc52806b08e4d216ea057eca2cd43ea0e99838a982f65030a81b4610a2687bb66f959d5382646228089231892973019ac82e42de86d414879004f5f2382f2cbbf922341f41f1e28ac565bc455c9ac9603df8b93b670b0506576f4e5aea5f499d8b0ac7da7c46cea32b2383042e4290d1f6e7d900502f960188dbedde27027a0d2a88332c2b8451ef2545f003aa03c3f865b28703509fd6f5da87fb022d2db44cfa5bf2132a8fbed6588259607ddea8d401d667fa0ff16a8969fa16b527c796921935e11ab9cdc276c417762a655b66a1a24915089733a5db389b41adeb15549cb13ed77446cc81433fc56dc7b8d7215841981a8304434423e2b09855c11db6d85c0ee20ab8939ecafcbfcfd617d64236ebe561efd5ef17c91b10b72a06952a9ec717dc88c488b5426477f32e2a7b13321f49df60d61a016c42691f6815adce1bd0f3b42f3f71ce07cae44bc143fcafbf13656a9f299e358fe82df016496877e3bcd459de7b87815e9f88d5f7c09acf6cfe8ac973fae0530e45dee997a80af2fd0418bd9fab6e0279c16cfa4e0faa9db0bbd585f58a6042ffc700f9a77c8e0c3520f5e6c073bb426a195e67721be4dc61ace8ced95d7be69d4bd59f445788b4012c2aa77e3c76c1878b6624b40ea7ca969ca0570d5f528798182a2c5543025d91a5f91865556561898358e7b3b567242d5718f4a4b836afb11ea1b05981d463015f96e293e846038cec21a893b2e0535b18473627161e53c67378614ba7e0c16a2d434840c2a1db315d94100b552253249cfa9d944899004160db803aa86c92bc07670fd0f82f6ef2f855ebd9eb287042758827c8390a8739091dc50c28f401c686435ba6324294feca9249e75f2f86c09e77c12daf6c9d0cb8c625108408794d8230a2d7ed032694b4536c4c0954603d1f22a8af9232277573d12f29898aec7584c8c7dd574b7e6c5f32556c56db57f5f54ebda419ec1d00e4b38de54403e71cd560754ceb93a98f7f0f8e1e355f9798e7f5c75627b0f4342a660584255e7175061d637dc185e0520559d8ba899311c521dfa0cd338e50bf32ec50015b9fe7aaaf26fdfe03c20442e77de90398dbf8a9152990968cc23ab344012bdae77346a6ddd4c2c4342c3f759ee8b22910dcfb713a9f978d7a8a8896dee51464762cba19456ecd68e42cf0e3f9edb34be38d098e1c6ae79a90cda1cc35df06ca4a777b4f99d88fb062a7d29079f803a241ea39e86d67d7847e21b4eb2f28e04f075fdf8c60f4ac20dcb69770911f17a27ee66089f5b9ce5848b28139cd60ace09a77a5e1345542a4b08b8c9f8af41354c806155e7c49e98255733f24f0c039b24a4af454984788548e74d8c45850aa5d3ab8c4681f3f3e1f074032580f9164288f160c069a356276e74e122aa4bdc9a5a353480582c02485a86fee9852802d02c4e3f9a92620b65ec9d6c88225276642cf6501dd69bd898ddec94671976d740b448cb9a68184de73d71eeac2ff1d2c2bd03a3513199738095f404a5de21a9314ab2b5b56def8ffad7136c56e6e0ade750cb7019ca7c38f8e12024c179599d01497d6b683a1ea029b381cb905033b3b741a6536bfacd648e91956139d4fc3c3e162d75b087de5111936ecc2721efb2e1018b577fbf6d340793efff042f45e564aa71415dcc43dd8eea0a603f9549372b77ea3f37e8bae26ee8cd1b03a8111e7eee398553df187740d7800e2387eabd03bd56d422d1cc5640a854d2d09c33c5b41484d1f92c00974c79c871c74b26e98d944df014215ee8b0c368d7fcbc6eb1b38e97e46cb715d12ec6320e021c95c2d11a51ad287c2f5335027138b18c635c1d05815585bbd900a95134321588bb6334912f3c61a079bcbdd57876fbe74b98577cb89ac88764586d4f27aad7286cb061d95c358048e37d54cfe004dd7872551003dfb87bc498db894834cf6f984bcc2c1c3c10a5fb833339be96e880ae009ba05160a4715cb58bb690333a4107ccf11f1d169bb3518c523e8704634ee4d97b8e54376b5d9430029ca07b81bf2fd41d0afe6883461ac54117ee313bec9a588c2c76c168f6b125183088f880f91cf212f87b52634a0a8a4f4df997657d283e1356c8953a6e14326d5568a5b7beb61aaae162c335165b84fd43da6286a558c33082df6d6473209e5bec819141e17a16a7ede2f5a16931c74189f0937b164ead88f6c5a23153184a26605eeea3b522b1a2612bd867b90582577844b518ad65d3fe7737094ae6ccdbac1312b920139c7d2f8ab687643ddc2f1c1e40363e0eb613273a4548318ae8d46b08d06ddc5014a3f6344dd608d724135d8a473b806114e7c7927568358473dab37d3a01711a6d2af82cc7f4cf4bed760c16500bd56201cf6b956d773f586b28be21f1e3cbdff8847efcbb587f1d5dfd62e6ed8ada613cd0a6b9bb1d3d77fefc81de6030c6e7fac164815a6f46535dc3d35ea8f52c88d7fb124c4d6028327576b976c186c725762fe3da8ff3c5b4edca130546b6b90205265641b514b7c8720cb3d393a799a41b7a7df37178dae1e5583c3ed9dd419823a1c3b015e7d969dfa87e90dd06f8a00e343bcd969a3250780e8ed2d0cb11cfc5d079fe169aefe60bbb1c258a33aa77dcab34070e49c5a3b6ba5d793703b2be43971a9b5ef1d21f0bf18c48a4104838d1e009a9803c2e84a9a7c6f3cf9efe86c773b5e60681b6f430552ec87c1fd5ab34ce44e3bb23cdeac0c44b0fb0155aa649f50f604c10f924d4b4b99230af81aef428c125ec0f593ab73415b1e1ea9e080501f21eda37715a05231e15b94f3a75ec9e7d9103d23fc5889bac737b1fc8e25ddf475ac05fd388d189846a14eaf18b3eb7f64a98dc0e47305b2660c91763e290e43f9487e4c2a6916348c2b1ea16ea2665d411938e399b63ac8b7f98f7ac2047928096d8f47af9dd0808b7564f161a33bed9672b09739294508e9805673fcbb3075cc48ce410b9588f1cb181b047fbfbb94f6704853dc6ea969d495333ce9e549b297c900b8248027def361ddb84f318b5484a2713cc4c2ef3aeb792c7c0274e69e8b42b68d8a479fd0433931702bf5493f35d20482b6a950822dccaaa0bdce7d44dc9bcb501e1c87885642bb69de76abd57978c7fae1ce09792103ebd972739700ef805c29f45b92ccbf22f42a9a260d96398825e70c0c2a9798acbf548e6333438a0c4220696058ab0329d9498a019f79544ab79d46a1aff90f8ab7da8d1230bcf1d01c844e63459f2a14244b531bd37987b87d065a3fdefffba6cf010cc5cd5daf91211a03df5f0d58f3c8e47e34ad9b34d0ae2598c3583cf0aced862bb3a6bb022337f6503049b38ac1d8805eb75d5fd2f81439b00c4a6078d2829313306ecbeafd4e34c5a0cb4c0a33c64da91845883a36fa0c3da5b9909585f84ead6c60390789f9e160b09a469091fde228f07cb7ba48a59d4e1e6b202c6868b0a8998bf021e0e433cb2209c9a325c012b9545cfab8da300b51d771f35360d06b0efc8e41baa5c9931a4f1ae7710177516a491f566dc7f6ea6197740547968c56ffa4dc65b4fa594b8cdc8a39b72216a33ed41c0c6011976808f3e52a59ecb0e49aeb220a6f70094a4b80738fa0005cdc47ba6fe53cffa942c03be48884014a480361cdf9bab9dd21e948c600208a59e1a98796af00c22b12461afc11bac615f70f87b3909773b87938eb6e650ed8b553436acb8e39552aad97ce25e97e937f6fcf60f1217425556031c875e4333c9a79279aa67f246fd9afeb9b1bbd2af95dd18be074bc008feaf1ffa298bfd7280889819769f20a8fb19a65f096042a0ad2f7c7cbded4eadc018b67328d1a994e16e8fe56e651806e586ae5b6956eaf646205b2b798370387ce4b6af691490f287992ad4b65f6fe03d09c087a8e0d4e74aa324b5bd45992315b018376eef09a8805836a53491542b2dd96635c56441a70ef476ee0d598e550925353a565459ede45ea52698dce3dbb4344b24509dc9c6f9317c8317b2b7aa41e33ef3c4ba05d278dd41726278e4dd0c5c0aeac6490efe4454954904871d508b27869545ae56fffe3ac1d6c201b2b40c48de0465fffad239ea660f40d0c3fbcd732399c28840d3da4e0ebd65d8f3f1560cffbe65eabb539dc19aa9743dbafb1aade030fc5a39ef220c439194d8f42b9be8be8f3a241b8db5d9509b579911b34f7110d8d33cf98598fbd09cb3341cd9cc3a6647ba8185929d7dcd34321af605eb8eda08ceb006550c30b9f6aefceae20d61c27622c3dedb861b3ecc50119b34bc50f8bfb3c178a0c2b91df8bca15f269e8562f1388d0d4384c5e03af3f011ec666e540af879f11080a9cb540500e7472a639464d9b14ec360f6b7542c700da44ffdd20d07a62e107123031fb56cc3edcd0da6bdeaa4e5342bda43b1d9b032db54b0b6ed68eb6c4f75986bbf7e5bd6183a65c49233ea6f76a5ffa68fdc9181dbf558ba158a9c74bf9555be17b47085dd4c8424db1fcf060c0c6b3211eb993015a635bbe1a40690ca4a216c0df8641c76e62a093644309222942edcabc3b4b9b6558d29d23d8f7b260b0435d31dc7efbf9585316119f8b2bf7410ddd37aeaffa137628786f492572748221b102de55610a09d194191948f32beed9ef51e28e0e8428d558b7ff630e604ae34501150609b3fc26b3d8fb9567d447dfb4ad5aab6c3f949c898591db5f95b7cb41e6565a53273d054a8ac6fb57724740b9090c37da165c7047b1e57b67f4c04e708198cd7ab052b5107f95ad8572b4934459dc58d589393a2a600af0c810ef03dcaaf15dbc7cd32685c01c2105430611cb92466291caba299b3acb998589342f1de55ab04ebce3ac9b88ed6ef1e22c3e373b50a19babb773c6eb0d0abe4c00a974f8ebeb8c8256121a9703ed659ffc759c8bd1423d6460881f84156095e3672db55e58052622951ac52cf24c141fda3e45c9b2f71e0afe8951bba2ba0b74fd6f135efbe28c9114594afb84a678d10dc33c45ad3c8f112ec3ab791df744d0f79a64cc3b05b38ce84053b35873bb73546f6691f0a72d2748c3ecef9118247f88ee8f7e868c7195c1bb14190bcd96cecabcea97c9dacec40212ee0a6505a4fbb62bd2ad6e24017a863b40ef3d5404bf189880d74ca42aef662769403ceb83bb409cb4f08b7234828b93dfb19cce424b7a8e0528fe6f65e8a4f74f78665a11fd57c4d9e4f13f38bae878a1887aa43fc41b190ce9a7822520747abe2531666e9d40d85b8efb3191d9d10c375f729c619a331e4caa851a197b860efdbc64db3048f58d796d97e82389e40f3b8a8d4367196e3d4e8a614075e87afdae2738c13dc35e807efd3b2a27b2106158828765cb50fed8eb3f49deb0d22dea1298f69a8733299976dfb8804226c794551d72cdd5af8bd4b93d3dc2e85079d4729a01763ca2e5a3b291f7c87810e0e57f32e4f0afd7338f130d45cfbc2f8c670674f79f30e7689d798e3d80a44254aebc81372bd4794c67a0ae6cda1c808b2e8deaf5dbfe1c18ba77cf0d111a86b0e4eb5d0599376f94adcd64a0e9c6218f87b750f8628813813c82eaf4aaf9b826ec39f0b4d34435a1aafa73c343376fc66234df4e4d7e8dd9e9b132498e36384d2d87ae08894027be94521de3b174f99fa98728377c49a5e201134470481fd411b72b5ce56533c43e005840d4300d8305715b27497195c23020ae989445360b88a2f135758df50793faf77edbc2aa35b007e2536eea3699f593787e9a740cf72b24fd0cb37a1d10220c7227e96df98a325ae2a65d705ab449682c1520e516326a88349caa752bac30d81892fe490833294df5b22692952bae30c3110a5832f7792ed010f39b90ca85a86cabc316ef5844ab3f06311a8d17dfbb340650c26b89124c90c5ff85a612e265b0f570224ed07daa685156ae5f782fc81f18221360cbe47dcd2b221ad969fa16756dc953aaed0d51742b30f6d3e8a5d13edcea43182f926f97cc5465d6ece253381011d75f476c4feeac01a6eb39b1cb4c75b505539d3947a2d2074c8ee08365f49e29faa7eb6c3435235c7383753ba320efc81a3bd8f29f17b824ec40cf11ad090e66f700d2eb21b6ee947f6206fc038e89bd20d871fcc7909d9a93d7fec29bd82bcc5f75a806fb849deaab841fe8beee9a6907850c0e18656aed070d34e98e6dcd432966259a4684eb46a7f90e70f457eba8a04521c3862629e8e46896e920c658e721a2e8beca34c8820c391fbb764b980811b553c6f8fb90a1927151bd4e0b63bf970c2751834e9f415411d8845291c3ec6f4fd643bc4c164105a2826269c07c4a86b5872d51d4294202ebad99ef7fb7b61ac2e8a4d6801107f0e249570e698556277c9b732f1faa90af9d913e539525728f8a919ea6a6efca00cb1ad9a7bf819052d8a730fd6609d63c3ab44633145ca2a86eeef57ac0ffa96b4e1a7e154ddd80c2a2fd0e9276160c0b0156c28345a462b30b27c169cca40f52f67f1b6f704fbce0bac49a910c30acd6762dd0254e960109d47ce79eedb45f9317bb8420b86efff6d85ef82e66f1dc7e62afe91461fd9cc3a300624307c5894e2dccd1d7c6be045f94df96b23ce022f60ece3311d022f4f405fee02af5baaf7a536ff67299d151696eca5d7d1b55d6f01090ee691b7c33c1f36bf12948e5f64fc7f3a5925b26f2b802dc6dc48d4a62b9f85ade5ca1808d320bb95703b845480a6bc13e46f48b15429e9bc97540e96d68d8594a3f03740d9386e0ced4a346370ea7409ff0ab1233b38e677daaa31c16c573176c2e0c57a1e1bb522d8d80e00b6fd82790b608885720e1937d0c3d275c235b48c50ad2ddc8c70a753212b49d1bedc487f86993185e0d00b85dc5920a62ec9d8591e7c9b770ac9d540aaff0cd83156c9f448cde38efe9e5ee6b356c485622f1c2058e8bf1bbb7f6c30ce29fb1d608c833d883ac017e764d5f5072dcc53ffcc338e90c2cb5769e5510bd99b16a99a6725b43644aa37324cf60df7ff07fa3e90feed231369d7c9c7b4a74eae3cbc2e269aad4c5198f6a1de7cf9b3267467e3526454bf95dfeffda0298c97d3a28852f9b04fa74554957aea3200a84363ae645b3f5a28f17f1993fb007df79cecdfb0a1319255ebccd5115e9b4ab85fce317e912f9cc4d618aa558ec3d4d26cb9003ed008a99feda309629af55440a26d320701589c3ab4c9ccd9fa01d763c21c1cccc25a5e7aaac806a2ffb5091b0549796075148ac7edfc9588cdec24dba4b93daf6b2919119fd74a0def7f1693b58157f4ebdec4035d6e28172772af1d19a18f9cb8cad148c223fe9749519dc78c9d1f03bbad70c8a97fe79d8cc888fc898a9ceb0e5527bb8a2311d06cd4afa51f6c16efa3cb1386a150355e7178964dc3238d21fe9bb2df2f8034308b85e549fb741c74496fefb4d936a3ff6e161eff437676f8e39ad449311edc5310c9cbe66e63ad804d201a4fb20a5c74b3f4933c29a30adb127c46d6f0f8bf3ed7b17a0a0a67111c3e1d5f4fd495ee85e1d201d257519dd721727ed424623f1fff1e0835b910657d30c778feae630eecd1250b31838a329d5d476d9227cf92c4979668f351799792616d904620058f579cb9f81391adda937600f4a58c8cfae428a4e22d076d0f467f8d22df75800767d240f063868a1a0f02f9550a76ede5fa3d874108626ac07e3f6e87bf0b616f99a9fee2765b50bd4c069e477db200de6fdde3c9e6d88c4a3a738c880bc3b448d3445f3a797dcf0f7f28251186da79018d9c70c62d8b102a3d9281bd578d4d398e18ac4857be3307ed37592837a21491649fe500732ee5c720c513e0f30a8ed0d11c4501009fa74cc14c5aa9de24c9522b20ac139e004b176576cc5ebe8153cfc1a215cd6920415822e97cd7e36609ccb795e98547d458eb19f5dfe7429cc698993de36d030eaceeff63ec1be8af7b415bde80106c968e0095cb57f61be53374b770b63696fa376de105977a411fa39f450b02c78967ed4e4de52e4919d64d188458cda89c3a7adac8f3870b394138656497fcc52d88356ee3735324ef6afca03d405ed87162fabaeea47e5dea5a8b4075888a6c89da771af5bb8f53cc8fa8f5e7e8551757fedf58777d6f7bff89fffea055a0d27d6f8d341e314a9b48b832e2516dc069a6e57672bd110a0dc1648b7cd42dc710ce1a82dc0a6704e93edc6d5df686aa2bb14f011d6f22d94c6fab38800eed191cafb554afe73769fcd14bdfad2f907fca4a80dc6d1412612f083003a670c16376e183cac597b774b05fa4182e6c4f489afe77ad0c6b599d852d9f22feaf4e5e91eb8d452dc5956b7fc5b1874dd15659fae186debf29ce5d1a204125aea3d806e48053f8b3807048e36ceee745883549953f06411d3c363578d1bbbff77027e31fd74a3df182b31392334ba8f2d3248818d8d1e55272a63194850e30a6ac44adc3c83874033e52782227e9f516cb53e2ab5b47816ffa5d7c7cf001ef6a150ee43ed03d17c5e489a9e8bd9ea088074d07ea197b648835fa8f8fbe6c03375a0ac9506f4c920e91bcdbe96c312b94bca04856a1e72a289d1261a5647a18ef91dcd7de532a3172ad468941ec2a7170812fab196547f10c8e3923b4fc13658903600c57fa59ace928f6cd9a5e1ab4c342699739a087e79874ab29d16aca42cba088a0233cb264551416a0b2f3019a75c525840a8f0511a060932679801e34e646938af8ae2d99162517e301bceed4c31b1475ac1f2aaa84843c3fa69d58d1ae7f3806594020d6b5ceba95219dd4c3448c4dce336d561fa5d10c097d1ab0bf43eac41365dcf3e460ad1b3e5131a61270fc2154ea455c94806f2b7e64ea1d8bc4b772c82434ef0a4f4341ca1131926824fdbffa90d634b096d02c2e8a959a5c8675dde1b08426ef988d3a6a495d3cfff33913bab086cdd0b152b96b6cca79559e9a59a936f69933acfef18bd016d7a8030c0800c8c6f6841f7c3040a16e42b19db86c854e41cd844527ad2a1256cafbd7fca4fbae8aeffd3b4050bd568bc38c45cf7a3ee903c2356a71f4156329074399c28a1cdf6652beccd40ad19016091c64ccbba18be77644ddf3fe2992a49365f78dce2b0e9de3cc393e454dccc4a4b6ec4ec5942f2f3001249fd8f68e8ac966be8453d804dc994f347b23598a76dbc9ac43f372376220497bb0d9e8ce3d8ce4e5aadf27e121ab693e581c3a34269b25a3ae2155346c5ec4e4f901b4090b2379b0491be512238dfe0505475ea7250023fd6ee73a11a236684721646f86367bc1cd91a8bf1aa1ecd5d919dae8348a285873d6841d3b297fd95c0f8139ee38577399f07829d77660b0f7d2a037604c38d271617974e4066540a382ead132105f0d50c69e7e4c16746ed2cf0f570dca216fdd7e9b1edcfddf432ba3cec537392df37f0dd464189974efa5bd74fd77848725b0ca992e6886ce6a718e1ed00c040050c9eca061c09996a066151b541b2fcdf63ecee7ee06748a6e4c190c5d2ee038dfcd7ecd08d93732f4879a33c322830bdbc8255d5d22643b61205c95e29cfc18d90a270c6774b51c1a68b64c52f5d39adb85c277805d97ad3ca94f3aa14f9b71357e2f459eb1359cf2d6b0cc2f0ecbc041dd9efdeeea63850ef7ff77d7b08dfee9205fcc0f67f1e159ad114d83dcaa4b922ae4f661be14f51360339432fa2c0bbf80d47e3ebf4756408971c0c1d6076189e8f3f3ede59075a50d9d1089b1e1eff662694eaa492756a71005a883c3363d4c594c13a8669593373fe011745028758f5e4795b5bde3c7a90487d4b6e1d860be6ab62e32c90b4a6814cd55dc73c2c29557dd2f06c954715c1128f1333bf9dc7dfa3dae02a4cc6872f19c2d21a6a1c62bff49fe195f6a369e17292289bdfe4372e82da2efbb6169d64b844cf2954a2ee9661beb8129a5a7a43133e8c03eb04c12e17999ab4308a4db67787c464550d4fe2e4dba0c0cd6db66270a62ef9230eff0dcc66299c11abe69d8fe3a50e29749a1f992bf3f7e8e9e25559b1597d2ba5dc32167e000c905ecc6671b561b241beb9bb003b57dd00cdd41c724776b70e57b995dd5bc7c26d1040e9ad221ac096cb6e75b414f72e4f6cab26d0d695cd7862bfb0d976a097f5f1009a12b0d6944d5a4f33c6b472c90087baa37b2502aaf8dd26f5405722e546ae01f2d12278c54b6bfe8ae2392953e61fd39faea2adef2cb7c3de965adeeebd9c83db8061adc851e706186c8f7767be31ed4147f910993ce7efa51fc164209b4306ed7b2722f30292abe3fd2c86540f6ed78d8168586510dc14088c7eac6b9bb5a863736b112f55dff93116812a4477a4b33526606e58d68c1e232c79a540175b8f021905a1616030008fda52ef258a7a3efdcb5fa014f66481b4afaf445c4f588c28eb62fef15c4c92e00b0807abe92edf500b99613d34664808f1efd162845b00a2357bec38a28aecefab7c7614423fefedbe6f125788c2421c1074b1ef6a680b9edab258f8a17503a4c40364d3bf40820d4746a3704f0615180f9e49d77dc37f8807b83dd1121f095300f0463d2e55d680fd49cc390ba09f667a0b1e3c4b5456f157972012ccec0db1705dc46e9d2da009261fedb9a173864dee2b63f009c253c25fa4038affedd379a16bc5e0e4c020e66e6d1b849a0ee3bcdc58426146b025ac21cc98648eb19983792fbbf6a6420cefc0b32f065d763c05ace59d13e6fd02f09c2becd2d78ea1f919650e90155719190377755148bd81ca1f8806d7b71b51afbd388a881b4a1913b873f2db80ae5ca065a76ce55d045a61ce15bd4a438a210fc29707318ac4cd2c27c3074f0a3dd237fc1573d03c65e08d7cc510e47eebe326501ce9bc7153250baf15eb5d9fc6ee4e6ac816af919ba262cde569c950214e76c82742082704f8741a45ef2a42097d0c0de681e9d57b86212e5751045c87522551f7361169418efc47a807c1238611c9f530000210b2ac7e68786355ab34c390681dfed5f7edae65e11b0a08a84784c51ea26379dd8e7516c1961d530049a20300373266cc04048ea964699170f18a2c424e50daa7079d30b0c6455e8671f201cc4ef60ccf5a193d61d8015a2d58b536d1b417f0439eae7601277dfc8f75116754949fc439d6fb2640d4813cc8922a314230aadac86c2c8181e72f5e5bce4ef04de320d35b59457060324de8000862aa6dcb9e59a86f7488b010953abaf7133cef51eb55cd054dea86615fcb1e65d76b0393343263fb4264d2693d6ddeff27659c9b6c7123cf61efeac10f4665eecb9ec1918a20c1d055a175ce015e2a58621be766d0756ccfd080943909ec5d87b748dd37e8f26560c642c3fed849929cb2a1dac6ba1a0feefbd6cce3e51fb5cc3ab352d8efb5466fd4591038ebd238dd7b7bea6116ada4d7a3f40abefbd6e7f7816f898195189797343edf92c1ce3fb2e1ee3de904919f191cd66fda77b0d7a37bebdf3ca88f871d90058cbb8cf047f8011669de472c64e786f196265281bd2e71426c4ce531ccd4e1e206d787cbf424df1d3bb76f118fe862fca54f7b9f7e77c1313955b6bf922c79e5985e0b33487f8ad5b2881fba4452a25764064d5ab6c6bdd9d9d3c761213dc231c79d70afef8dd6aa86b502b3cfd10693dd595acfdece9b1dad5c386a6c3b38d5cf55e6ffd1853680c8d046ad69bc55620103b4a0702359eda575e176e9f6709e78c135b30b4e2883dcd189c9d2fe0b9ede1fa6afcf521a19531eab28aed581093d93647ca3764780f4c79ca08e451839c6a34ab7b691f160ef6ba4942b6dc7bcb2da59449ca380caf0c4f0c0d5e2da956a72eb7482e5af5cc35dbee9b90cfdcc2e24f7c81e1a1c76b4d34c5557c8d4312260bf9ac8bf2c153c97cfb64ac412bb960d728cfa4a16bf8ae67fbacccef249706ad5f4f6e69d0bae6859d63471226770ee443b9e5ed9406ed0b1068f0ba6bc66cd08dc555fc139e80211491c47f0206505c4520afd39907c4afa7f9e645d1fefa149bf90b0d4e69d03a0b29671068d06e5e0caca752657bedebc0f63714f2617da15df2657d4a11fb519149170d178bdcecdb3c95f493c717e7a150d84f1f0d27c7d9e38b06ce3b5fc0e6f9a301e59b779bd360e39cc7577c71d8bb4f25bdf39e170f76d4c739ca5de01ce5d2519b771f0fca37e7b171ce7b5e3dafd8f18839d755d27b5ed853c92f0617b0e79b6a21be4e9e3f1e9e987b5e361ecf17176c1cf5f17c09bba07c01520d27ae80bd501f0ce5a8af061ab0e7e9d8bf3857f77a984bb8ce2ae3f4dd47a373ec9dd7f3c25ecfcba5178228d39330ecddc7c3336586b99a1e430bf165a5850e7b286171a9933226816241b17e893d94731cf5e41702f9d435cf4a3f7594278bba185c40f9e9e3e139f9e9934353129d501e4a94ce44c996afd8c90f27e6dccc331575b9d54f45bf2874cad15d22cfe999f530cbb28cc812d4299125eecd6d836a6ba594d26a29b5153f51049d5448d22f4ce13ca39db95b1267b6e2f48d119c734e6be328a2592c16ab084a299d55055c2cc2554f291d2549892e2973d59e3262b198e4c88a2e168eae3cbd196468b01d2365da60337dbb949ed43a6b66b67a1f2562442a9573dca1e7ecaef25dc95570be22252949a196f330955d6b3d1c5bc4b1e224b2c4c6598f11e765902238aea41f79e9ddb2737a159cafdf7dcfcec8134eb08fdeb149bb3c217e74b0e2574e05f1092ee1925b91cc1472fc4f57a15d60ec3c1bd7ee95b2e699aba84732512c7d2453f4c4d7ee89e62afa917929e62b67b1ec76b2d83de9bfe192c35804c618c19d1d493904c1d8c4faf4556c527dba91e89a4afe7ad8e45d609427dcef6aedd9678106dbaf37a81373a65bef63cef4eabd62ce7470ba081057d3e3163fbd4c1c459a091dd99f5e716df03407afb86a39e33c853d37888b21faf5582464bdfcebd4d2eeae5570f4b878ad6bf1a3a5b3c198736223478faec612d7566d460790e520577e59fb11470a60e06588312c388342153368e2287683d10daf36c11b15f6dd9def96ad3e9221d3c5134f918c7c246374c39f969041cc645cf098881366b54d534c50d021839453f9c84732465ea230c1876014edb1488f5a6b8c35463a298d39e2e8c5673eadcb9f5d141f6fbf50049f519f5cbd3de69c37b3746adaa6695e7c75eee288a9c7da757a2aedaa3295ed5a29ad5405653a9d1e9d4ee7accd029d597b8247f79899adb3efd5ee7512e7784aa72d720a45f0f4cbe0681abb7bae6c2dac94b46f7738333bfb5eed5e54fcdb2d948c3d3d4eda73564ae7a45305e59bce399d91d218959c4211484d561bb40cad4feaa92c9d9e6ad36e2629f5a27d2a67f418bde716ea5546da196756da9e1751214afad453d9aed6869e6ad3ae6c6badd796cf49e7384cd570715e99f46a890972640f97fc7423b72360be4e62e4f8b18b277942c35059e67efa49294737418ddc2e3d36d97e3ab7033c02d636ee97d5e4305037a920293276b8b1830d9bdc2ddc753bb4bd193dbc9d93a8e6a95b354f274ee724fb54516cf624bad1371d0bd633df2e3e8512f8ccb9585b4eea9dc29e243ea36e2beda480cddc2ea7a7eada2de5cd2c9db3e79c737e9785adca4945c4b5568f2d7b7a19f5785599749595aea2b2c6e94d3ca58c229db2305d7a6ca7d265678018aff75c3995accf8be5898c9c41d9faa35ee684faeca73dc2777bec6fc69fde1d6d6b9db6a79a6de7a435f6f4acf0b4152f5a9ca3d3e8338aa6336d7c4a5d7606b853eb58984f58df9fd7cbc2fca8262d15dce98b7b9f4432de8e99d6a3fd3c5231404f95120cc7717d3b0fcb0c6280bd5fb16b407c751f0dd8e3eb155f9d979aabe95a3421354598f93446e6322cbb273cd4afac2dc4ce05ec3629c3ce69586a639c8c70bf50ca6b1d0bd5ad632f94f2d7b90d53b72afa7642d158f88b73966556ce38382d09e36cdd4efe22c78faf76e9dd2b668e73ed76b22ca31927559aaa3357ddcc5ae1792adfd24cd228d5a9731dc6de9776ce53b5f7c060f1c5cdce4924238ef9ccbb7332c26f99f5ac7bc2aa747b833b25652a6f14ed6ff52cf3ec37024e8590537d4005ead60b103f30030886bcfda8707e4f65e4ea5236bfce79a1e655b6e79c935d94ec492ca30921ccac10cb68428832cf6ddb6b9b5b16ae77d36e3a6dedc6f9e6a938bb499ee734155555d5556576e33cf95b2633b799ebf4cd5cfae44229f68b2cdc6e65b1e53c850af84c665cb8ca99a06a1f023bf648dd524f3e3733b719956d3d49a9ab2e95d28b99b432e35bfa7515e85912027f47748ee90e95d54d80234b0f2ff690e81c5311c2b84598a69761be6d306090e809c34c1b58d0308aa580bdbb02f45020c6980276e23f603f100c79ea3406d36391cad33d595335d871eed6c9b13d792a8b4f217decd50b3bd73aec2a0ebb6ac3aed2f0e9ab159eb727ebd8da479dc2c854b0532a377621e6b922eb787da95ec3f68a525b10e2353d762ffc30534613429829a389a017ea94740223e348c6c7e0df51c14e9d43fdc971a6ddcea97a2aa9d513cf9fb017721e6e1e5aaf9e8a536d2aabb94a6af5e4c9af9dfcd8759f0a0af6eb18bbf58bb73fd52f94f29dfc10fb46c53a75f93d9bb471709b73b85b9c6f1bb785163e1cd530c7d5df7c763b1be7859d73aa6e5accf39d4f8f8b1e520f4faa5645159dae3a4ddb795678de5a99d7332b5f769cd72e0aca9dc498d339e7a1be50869c57b79cd37047eb9c70de8e372af75345c99cba5baf93350d773fd38f5dacaeaaef5ea8796855dae764fbe8aafb39d954d9e7647b95fd9c6c217d9d29ab5b5141a94ee5f75cd934a0756ca08b34b420c3670c184478600ea135ca98a2680a19b841c787b18887337438020526282222871dd62c051104c5134a3485b1e80da7830cccc477d7e143a5638d1d5f4c6aea018d177c01050eb017a02022882ac42cf102a63aae9ce00862089d23051556901733fdac5112871b5bbe6cd14112610861b18231ac38e328a9891ec422560fe4f830164dc102209a804881183578854c84a0c3cc17305003cc165ebca4df8f9d7c434abf52b248d04ec0299c71584b983428e1c84116a20794c584b504378b0539a698e3092c583831a6670bd10c9ad62893c31b4d3449b13559d1230e8cc122381947d6122628744b5e8c83f347325a78f161ea430c068e68c5098ab0388104130db0e880e6a80a2e5600461725b6203383684ae1064a3c71c49a1d90f0e2d5832586268e2622bcc08e5ed253d2a3f42b3d5585bc22a55fe929232a86d10e8cdee8410a3396c2a84087184c213d4abf6e248139c28833d0e8208bf902328d86f640288d0e55a441861a46f8d00507242272b8d2841839309a56488f577aaa93aa986d503746c8010837d6a891861a59f4b0800746a851820ea216f0a093820a0d4a37e0f8763acd7cfff8b64276b78c356831744cd15da463b3b44023fb8b325d7cfbd562ccfbf848c6065e6857f418d22f655ac096164c916608a51e76b8c01452c8e0cc173f24bda0bf901eaff4140fc8b4c0c31847cce1461354ecd0460a311042820932603ac43040d9d0c670c30d1fbc70b1820b5a82971ea5dfb803a39f1f6cc00108c6032358193556ac81454c163e6b3c2009130c41041d68ac01224b8fd26f155d80a0838b3137f8f9c9d274090a175e9ac8c227082176f08a1e3380a2d54ecfbcbbb36806893276a083a12c68de7855b96105455a9811a444183fd8204b799072385029a4322a603f5bcabb334fc3b6f1dd73e8bbbbbbbbafecf6c8c537f74d833ac2f8eeeece6ec6f22057629d145458a15bb20893ef322c9928d4e8b44ea73c4526066b5ef5914c1465de7e2413c5984f2ac39da0c8d163b6f342831202ce43fe74a4ab209d259d89741007593afe9ec423337eea25c51195c5efceb9f8a954887ffd49fcfbf57c9649eab5877e12e8a95292894c8280dbd432b560ae3b95dab414cc4899ae22d914c2d1a2458b162d5a84b6b64953ba253d7e335ca106b32bd4605669ad538eee0fbfd47b64d4534ddda74be5b3e7864f99e2ecf83c9e3cf6173289947a7bf5bcbe5c2412ead2250b570989847e12d63e3f5d1e4dd7945ecde9f25e4d07f285b12995fac2b8d4633a8ff9dd7ca9d317fa077ac8ba9f4ba0155c2570ae122bb8386b678a529c9254da362ac14831e4904c92626c6017298652175b69edfa6536a765175b1b7186eb6196413a95732b6f46cd552c92df8e183395a76ef3f517defc65d4b703c2b7e3c4f293f85646b19f4e8ff9e6389c3232fdb04bcfd395b51ed690c1184d00c9c1f492fec27445a31ad471821fa22b7840c58b0b3092484a020d3264b0a507054234a9a1c1115f986e7849e7a8f5f627f60be9670f490251d4c783f2fcf1648f47414f3d297bf145bdf6ccbe70d2c828cfae33c34c5fa8d9f2d4da1047a9f5d88452b75f343a22f645624eec6ea7dfec12c4569474ea3418e9a45ec1cb6292722a29fde2edfe6eed6d882cda5c238eabcd35ac0002f5918c153e6c2469379e89455c1a5ab7866626751453836ed6773c325ad21a8d8e111c49698c90ee12178b843f927e3a041a9cb5be50450330743b28e7783a7af24950e2dd8a45b0d329db525fbe10a7b5763bd9162d68e9c9cd98603e945ed8fd7170ba90b86421f96427759a9f309fec3a0d648d0ca48e0d26a5b6466b6a7066e992c32c68c6a62b894797d1e5d04f07e2d153d15241ba75728f2a8fceb557796807e2d1873d434f453aca69ed197a94a3aa0c51da97a9bc6ca903aca7729d7ea1f4b00aeb35bfda1776f5aa0cf23b10931ee5d50b973cebc39ed85b91df01d6a3be50cad78f8a74cd4329afa19cf552fa75edbb59ec7e5950d6e30a0aa40b1339bc423fb464290024b215ca30bfcb8fbf90b8f4a90489b771e9d5b779318245a63070881c5ed8756db1c53c0d164299108e72b8c5c22c4c4861af01a51f43c8ef49fcea2e3f55fdae50835e1a9c242891fd7fa42067433f82c886a8caa7f6851b989fce79192a93430dc605c8123954b9a47d42e9f4855dd810fbd8656c658edf0099d30669f8e884f6a365d180342d5a90b6a5c1b9c309a32b84d228fd5083d12db60935788532237ab3d04c7919a2f2ea5529f241be0d48e56d3f5ae4cf6d7cfbc0dd2913ea1a1287cdea1ff8100f7b39aea4c719648b4dd517b2c02de6110422d3069d5417e24ebb9d0f847c55e2d1bb676afc03ff3226232dc8dfa7e26c3c53f3020724896c8fc840cec8d4a53402030f332ef77b125faae4a735d56f8335f803881c6eb11f0d662687599812ea08a9979f99fcb2a031d93f7a21441b434c52f810c41a6c5c01630634b89245186abca6f7b08117b06661504e9f876b1f9789c9c4047989633457558a3498892113e667511600334c4b1bd28fd560b189f4e99a0dda0df284ab65e91bd7e92c4c51833353235bca98bcaca957537e991a2f4b23e64ce7e2102143ee10a7a793e7443e108a00c0b334621335b1c9e9a7733d98c8e1f643c59841cc8626493ea4cf24892cbb871199ba01a8e3f430410ef37b2a484df9705fe621f22624bf2df6136a30a6ed4788991c6e3f4d5f2505d29a7ca69a42a82278ec76565e2b087a1b503d528fd46fcb72e4db7e402ffecabb47d74c83d35b4864f0a6e9d574d0d3981a9c2ee9cbf8f5bb6bac68f279113e1a9d71e2b9c66a94d21485210233e4e5c88c918646436a503ba36dd182342d1a909645fbe982c4b9d8b5108bae0c200986e4db6273158489ac39f2d3372eb1c998d8e403393e42f0e1c363f4e160b551c00e3f0dc60777f8903526946b30d027e8391fe8216830d035a66eb54faff55e4ffba9f5d6fb693e0d4e0db67169359137210d06248d4caf9aacdbb94a6990d0983922023314e61af9eed2656af0c70fdb505193fc8d8b90976e5127dac2748b7e97c90be7cb207f8bc59ce935899833ddc81459f584aaa0f4873d2a2731474c51984bd4e0bc601a1c1202264be7a4a73deda27460be2a8a74eaf20bc17ceaf17310d78b616713da912e8250ae397cc5e173fcbafc362e5b0c05997acdf96ed10d1344a6df057387ec1b29ca424cd4ab12958c4491a5c37e6eb14d75bf2d264489e80f19f4106eb76878ffc71502f23357d37f08cdd57420ce5d6f2bfad9b274cb884fdf80bab5ca39e222f874b99d20d7d9edac7c7656768cb813f937478423ee447e8eebb42a8af4117c848fca57afdec625c7db848e88708dacbc2d0ceadb62d247296b3f9f066b7047960c7e217ecd43f8c223af79a8c136a406a703c0dbb834381d046ff3e26d431b9806a713f136a206a77fdee6799b9806a7bbb71935387d88b71d09f1b63342c0e4fe3053f3c117cbed455cc32acdf17d556664f4d3815c18ce8717261443fd1e5e789f8774cdb3a26e85c9c474cb5351af1ed33ceaab52e4b924e9dd8da7a2be29fd0e4f453f27fd5b4c8369405a9006d37e342f0b924f3f744fe24d869a29cc90aafb87d5ed3a5d6ba54d60692a25614912bf41d392c45351b93424a74b094b4a29599e4a52cf85258ec4b94befad26c0681b3eb7d22cd6e0cd8c342d9422f17848f0251f3857499cf5f128f1241feb4b5642a45a00126739eba3810447f28149bc251e3d2126d9d02246846e853408088b166a5a7eba9452762f207196c76e014b3e3af4d1a0b9f228516cae2609dea6c48b7395054dd716bb468832259aabd58449a5b99a1a6c22792acee5739e3d15e7f17109b0edcc6f48a725f9c25f241e5e968739898729252490e0c55e29f19278acd8ab29471aac1ebaf0557e457d52a9c19ebfae2cf216f3e45252b7ac926d9aae243e3dfb99aea8c443bb84c4c30cf61910eca3128f1acdd5f4241e3df22812cf88975cf4f7362d015a2368f322251dfa49345d5f86a64b83cdd5346a707a51130cd6ad3033f2e956f5a194d2cca85be11d7a170883c16c5882c15ed3ef50b78a60b02701895e2f1acc434cb4f43325a7f4d343cc35fd749a4493daa50446992aac6601f3d36358b5fc54439baa4f0dd260dd0a31914fb7ea950a94c32d4695681a255fe41c0431a44412b138a21a441c91830e40ace162044d43951a4c52c2c59431b4f802c98927704064c21a686039c2044068b8206ac1c5193b24f150c70db4c42b7af0218e1c52e0458c8bd774eaa55baaf82834383f8916262b1bbc800131268b1338b14501451731639cf18312d0d24f12160b766613035bd260d34542bd61ba90f8f40b9b2e257ec3879489064d170febe361790d9a0acaac61f4fa723d894fa7b1e952f2f120f97890b81227e15321896724065989f7a50ed52f8c627e8b19d1816b3c43833c0d522aa5f6cdf049989aab25b196d0eb55f3ac4825b99492414192c8fda12bf1e44b76d7b758b724de62dd524191b77ffafda634e8bdd0e0a41489a77201c916eb12a623f19e170a8d1c6e3196a77281e5493cc9c793c459def3da62dd4ae2f1e02e6480b8e255438f1e72c0f48a5d42d8699278b28b0d329843e8a5339178f1c5f2e24b7a1a6870b2bcf842423f18380a9b2e9ad4ab7961ddf269577c252971230d20242fde48438c1444a8408e13fca4f1821b7ee8e2d5f373c4450e673c61822b4dcc90868e1bd830e38727aebca6f7e022cba60fb7981a2f5b6cbaa450afa60f5db911659cd2cfa5a69934cfb40b174d9f683ad6ada02906cb6bfa34d347d345c5f8027b2975d274ddd7f4e9a5c1b44b13030683c1c2b44ba6595a82bd310419b0d7f409d442334bb7d4b42bbe846233c8a888680e31fd5c337d7efa168b2f5d091a9ed1152596f0d20333c0f46059430d9fa3352710428828460a30571869f146181c72e0c0e18730aa9001134bbca6534f8a0b0d4edf86a66f44d3b7581b398c4d5bac69ba3623e26afa123017fc9c1b143fbd69bae20bf6d3405b8c2e4d170758f082344d148105961d5ed369d374ad90450fca3405e1c68f0c5ed369d274aa349dab5b6c8bb5cbc8d2f44d084cb722fde99b191ec07e86db999fb8c8875bece766f433dc8e7ece57b821fdf44a93ba155e21aad4ad5083fd52b7c20eeaa06ec9a506d3744b760b06fc214dc2f9904a4a95a62be5b109bff023e9484b1e7e5a4aaeaff4109cc816ef745f74d612ac751c87bdda9b6912a8814e4c99d3a2bd20e37b3333324ece524b83411268892c53d50a2e1609e912d0c7403feca1a11eeaa31e6a1708404dd462221935621da68dfa4c2375520cd6b476a5a442d3c57a62882c3028811534587a692a4dbb4e8dba25d42e000cb5d3a130dd3a436368ea501a653ea460be5164a870e385d218fa909a0159f215d2a46fd7aea4b16e8553e9b974abdaf02d5551faa53fe99753e9de9c5329f71e9a4a2df46b1acd55fba90e494474d116c4557bd315c8557b3b5d816cb140bab6eb2185410e1f765dfbe635981873861a6ca7238753e97b2a754b4d3883c4338da669fe24bee63d7fbdea85f8eb0caf94c9a899e3a7f4c4f08638930234ffe3e3991414dd4091e5091b6c471a597e5d542ea0eaa8a38e3a5ed3759a873a0d374ca79f6a01d5a9d38f06eaf5fba2baffa25f9e2be9b29332bf3b572fa925fb87398ba32f6d30c1030e57dcf06a07a7abc7cb14368062091b8c41dae2d54127f082cb1a659c51e225821d04f1c311195000ad79b5b364770e5c3c3232a2e9e527c3b48928fce0305f5fe417f65ccfb1c9132eba329097d6a38c2e949f3cd4320f6d3e7e29c4953d7d666e04c378e685e99244c495751bca2da1dce2c4d20c62d3181816f2066fb794602972094cafc25c31d94ceacc4b342f3dac60882a50afb25c2d79cb4b2e2f6198ae8eb03b8f905e1ebd741a4487664c88e8e52b898c97f22553a9d71cfa5738895ebaa43f2f815efa646a977cc9352f612fbd12b52bbec0800153b5b42b4a2984c1e98a2f59372cb89934d3203518d11c7577d3331fcd34185d32d1222ac6cc51d114038648c8cb161a036a30522d47dd1dbbbbbbbd5a91df718506a3ebf834487f9ad67c9c430f66aa6930cea59e2a2d8fa4b7ce65de49294bef9c93a9286577dab4ec5e89ba281bd4457d594bc949e1acd05c5997351a0093966b8a0f6fc30a1989b14836c9e6b40011867cb0a305154e0f1e37794b9a2e0660d70b4b9aa6ab014a5c0e60014dd70346d8d22e08ac20345d1110e29200ca052db33ab04b3f52f71a05010225052ffaf9f9f9f9f93132d2604b9c9a6c6340ff09bfb0a401668ede3a18cbe10e336f8b1ab43bc4287100eb013e02047c8508ec18d24502ce7599808348641f539cd368b773e39c8683add174f188f5359aae9ba36b748daed135ba46466ffdfe4cd70ea5fb737feecffdb93ff7e7e7ad14eb5607b175c34444ead68d508336c90a4d178fa299f4f2f022c0c37c002bf4d699b43ca61d03f8f01a3c8aa6eb001f8fa246c0d7af2fbc79073d6e72b5c1998ff3e1a5bca6e6e7a48d7d0a6b7817be4cc614dbf970cdf3e1239fc123c6834bbb26f0c55e9d2e66c2d205719dee8adea67e781d11100f33290d3161c26281a07b2a9583f490c2c50fc575fae668ba70d35c59ef84582c10744fdd20bd85f2e1a51248c0e334ee632744f38408f130131043bcd8e0fd3ef076c0de7a0b3e6f5de5c506c3eea713e1c34b396cdae33562ceb6134704b8155cf3565821bf01019c7dec46709d1e61842b0fc04d6f9debecc78ee53acd7558f3581ebb1d162c3be0a404cd898794e2c2110faf4c506072a3c20a46dacd0d8fac75d549a148bbc959db01dbe123c5eb32ee8b291648a2e011f0050820b196b01ab0a40b1e12061996f24c054969a08a52f622fe828691784898f48161d7fa399bcd8b6fe784bbd5cec0a3752e5feba2f8f8ee0b45f0786a5d330ed7690bb00971beb3e1a668ba30d39ab71ede18bd75db39a18fca321e52fe64e93c9315673bd9e97463b7736383eaee3de5939f9cbb38778bf322b55ef40dbddee9c4692e9fd3ba9c73c631d33c73dbed64f79e344f75d2b44c3b69a7d377d24e278ece586b9f18e061a65ed9fecec34c9b8d17376072183992c182e9a3512ccdc7e7bc7eda65526a9a952925aa3b6d9e159e5741916e65ba76513bdc7bb3c3bebbfe363a882e2dc9a6a95bd7ef186fd1781bdab4f1d6b310b1b5c11accd2aeed27c7ba15c5ad672edd5ac1ad67a1e962805bcfd633986e05c0ad67a26ea1e0d673986e9de0d67351b79ab8f52ca65b26b8f56cd4ad216e3d9be99613b79e8fba5500b79ecf740b8a5bcf48dd52805bcf68ba85e3d67352b74470eb394db78cb8f5acd42d1e6e3dabe9d6086e3d2f752bc7ad67a66ef570ebb9a95b2cb79ed7740b895b1f6eddc6a75b4adcbacd4fb792b8759b2c3640dd5ae2d66db4748b04b76e13d4ad1f6edd664bb75a6edd26d6ad12dcba0d976e0171eb3642dd62e2d66dbc74cbe5d66d86ba15c4addb80e9d600dcba0d51b704e0d66dc2d814d988b131ea9610b76e63a65b29b8759ba36e11c0addb9c894d566edde606f10415dcba0d0e62eb895bcf41b1c90eb79e61dd02dd7af6e9d68e5bcf3fdd3ad2daae44d07a98b7bc96b7be0529bdf55352b712e0d64f69bae5825b3fc5a68b1b3a0575eb03b77edad2ad772648ac23d068a868e84434741a1a127aeb5dd274714b46d38585d0744bc7ad7748ddc2ce020af1929f60a9a6bca4f46177f4d6f152b7f27461a2d90d75cb006ebd03d3ad03b8f58ea85b2cb8f52e4cb7a4b8f5aea85b0870eb9d986eb5d0398b09c87230e539b57442234b4fc526a1ec3ccd7144c0f10fc8ecf5c72e868ec7083cfc03f2513d583dfc0332cbaa4839719e655996659914e74e0638c093feecd787121ffe0119bb187652fe01f9f8470bfc0184c9ca9fc407e23acdc56ea789ab027044156400ef4fe2c76ec7052b4e3ccbb29bcc9fc4d8edecb877b743c45b73bca3715eb839b7c5d738ce6f76efb65d6d43656210010367c3381c2782173f7622b84e779e13fa1e650547793b3c7878ec7646701b6fa7470f8fdd0ecba578ec7610e09cb7e3c3a5548949bfb914d4f685fd9b77ce79a12c7a0ecc735c447a8edba478d7420baed39b15f92db460008fddce015ca77d2871eceda4dc63b7b3c4416fe787732d5f793b409cdb61e2dc9113bcbb9d267ec48bffde4e100f80eb343700e712e0ddedb8e04e766e5e0e229177bc1d21ce09f1d8eda4e01c0f5c00280af03a588fcf5d48c00e180c0683c176c08838e703ef50e252f8a6c8f3bc05af13d3a07504785d5183d6a5785d9806adb3e075440d5a3f80d7813180d70d1d11b93922727344e4e688c8cd11919b2322cefdc00cf0ba15bc2e4b83d6a378dd0f130070eb374220b8f59ba1225ec48de0a00f717ede7ab805bd756e68ba76c0e6cafabd39a752ee20c862ed80bde586de3ab7345d3b9426169aae9ba2b943895bdaa1c42ded50e2967628714b3b94b8a51d4adcd20d167aebdb951b261e134dd7cdd15cc1ba75231441ebd6b79fe9ba196a2d69ba78c46e4e397873f41613dd0cbdf57b734a4b0281c3b8008ebb1d284ec48bb27bc271274ca338615fead68d5bc74cdd7ab975dcd4ad935be750f8c6068c2728ee0607719dbee97654cef9c03df0102f7fe01ff887c76e27a5e254d887c7ee7e4efaf307d8c693ff01965d0f4779f26df0ea773218fe09e976709c0b01731f5e79788d98635d050fb32191587fc22346c46d2ae56126c223f6f623c2a3082fc51ca510564694e9d58e134ffe7bf28f78f2579e7cd0938f51f23727de0d7192e73062141b1ae23abde344c09d5cc64a048d0660119c82a7390144c0428464348e60215e3c624414425ca73910f00b37d56ee7c606871c7ce3c59cfa914c1754dc7cf09ebb27fd1f3cd0089d0b3e709dd67154f7a41f67c9e190a03e1a197104f601789a0b00090e12440b0b07616991431095178de000789aa3708287d734f13a98d7f934683fbcf493939099420e95916e47fbb21c2178f259ddaab3223f04e744c04c3ccd5d4a301020b90908093b3e1a19f103e4086e799a974002fef1c3e7c7122323603f72f0124f731296e054ca86540945a8498d8095789a272901fbf081a6857db4e0f0d1c2d9ebce7d744ffaf3d77998e90b97518c8dccc22c4f73242edca347173d98dc8f646e00eb91048fe0699ec304f3e0816402e6e14513b28f648ed6f0709de64cc03b3cfa448ef88fee09fd1d5f078b39d68f787829074de847a322da78224e280958044f73231e6e6a8271703ebc64e345ceb813308e179f8827ff644696aed3dc0958f3b218d97e348aa979221ececd89c71431c73ac8238b71e15658f3788869904751ccb15eafc8b8c9431e456f7dd539e9c74b98a941eb26c8917d098733627383b604754ee21c4ebee79cdb3ccd35cdb76e475b610f6d1cdba03ad035075720f7a9a0701a8742a15cfab471f9d38643390a9583f36b3bdaf524909528e732ceb911f0e669ce69db46679c93fbb2f82baf1ff4fab1d71f82179f02c00be783f0859d0d6fbd88170325e22dbdf5cf0bbba2b7ee796167f4d6dd1be2f50bf1e27fe0f507f1e2abbcf840bcf8b18717bf7f0714cd37e9dcc9b7546c723a75ce79fd98f3ce65b7d36d7e551ba77956e6f3fce68536aeb24175270ebbe6d835d79edbb20acae69a6b7743a1b0c78e05cdb1ebf4e661bf19b2d2732a3649c51c1c1c9d566e5e4e9357d3501e761e9e3cdc9c73e9a950dd69cba4d60377de6907391a1511fb900718b47312e5f84d07775ed7fc7ea1945ebc9ea671dc178ae0b7eb996b1ffd78703d525aeaf005a7886638611adcbcd42ca3db903d218738611e45d4adae3a7c3b6a48e9db5160ba25bf5161be1d270c188cda21f7b05e2855d55e8a3de43c547598abf6d24bbdfadd503be44a838c2bd037ddb634b587156813dabc6c431b182d9db9fceea2f8f8ec0b4560b72c0df606f46d5a42d426f48d42e990dbc32dcbb787a8a16fdfb26c409b9676e5867d6ff9768aa325f60cc528db63b7f7bc2a0af8ed2d8768908bf227472314acf9688402352fc247231dd2fc4d7aff681454f4dccca44f6e62bf2a28d46386abe717d72c73eab81e207e3be9d84159bd94609476fb8159b858c42828f6361f8d82b23c277167d6ab7399a779cd2147e7248eaeaa4e5d551dfccc6dfda2809fe94c2f7329dd3af8f4a36bb8a270c8d56328ab8720033d1f5f7650eea5623f95f5ea5465bf28e05707bf7e6083311609e5d0cb09460cb18b3448acc0082213d8200b971258a1822e6de04046092aae2852b3b0987467403f4e5cb921290d2f4cfc840182074f98c00b2d5a9ca14676861bad5a82254c5000f2918c165bb4e0e1816068d0e808498632ac5be2f4c9d23d15140a83c1b0f868e6630745c2607fc5112b7eca2c8b17191d2155a121a22d5ee5bb9265ad48494a52faca182d4b1eb1d9adb5d60cdf9b69b5fa90a48f9e752745acd382896c0ecef842c59ce1023a7900f3edb4c374517714636c0eb208e37b4716519ce13854f44a51a7ce4326f1d334edb331327d8c696c98658c6b3cede981d3e2694f0b7ee62461017b20c415a53909f809c2037e7a04c3fa94043f5dd29f1ec37033906ccce83166769b51070f7925856e491556c842a847163ebccd473259c0d0483a9bce59bdaffe546acd497a98924adddbb3d2eed94f701d593863a6657b6da6b6518371ce8bd44773ce39abfd12c5a452da3a6f967977aee49cb251e0f86b29a753fa517c69c5b3d3542963adb54a4a294d7537f8b160f6d79eb5571ccf1aec0eab809d71c0b9c3911966ba94525a6b9b075c8c2594c1328b34d618e7cf8c69add44bdd3461b7dbe9c984098b0582dd03a0f48752a323a4a44c68a8bba8b3ac6b96654a46f2288946876920ae2cd06043e08adcdd32ce53a4e16f7ae028c69194a398d654663db670a5e1e421de0cd30ca76badf5cb33e3746bd669bbd2254f6f984de7ec76e8a4f35e2abf3b69537ab34a7d6a6165a419b5b6d2ccdacc567abbe532e644ea74465aadacb5ab6c154c195053e8cc1abdd4fc22d8aaa0d8fa55c1b3082ebaf28e974fa76d239c54e304411206270641f2210e52def121ce7bc76030d80faf7982b83243444412cd9820aee2eff81a3f9c20f40b32abc4100789474ea968d974c545c69ff5a67c66b1a595524c69cfeb34a59489a462f3b1ab36762a0bba108b5ef7f3b992b98da034a3d7d6fbdd23567a2d12579ae6deb3e9165cb76d3aa3adb5566bad4bbfd37677ada8cb4d3a27fdb20696e6fcc25a5f48fa486d5561aea267de0c0d46d78278f91a87e9f4a61fd2a792468cbb453bced3749da6b2e2eba2bd923e35ede5773735d9b1e9697e3dc5d4d8444a9a533127bab59987d6663d1fd2d983569a691aeca40671183166ced82d5cbcd09a65599665a9d4bd2864d99af6e92cad8566dd68d2a861f221613eba986be6cc47efceb22d718df49159a8ec8e34ab99cd325b977c741a64ce944d0f95d6196348e367a404b96b77cf2ffcc254df2cbba9eda66d69a5edd9a75a4a2995a19b055e69189d46b79e6b74ff2f080ece4fc7b552cf7f4b0dfb696d2991489c18e7a45efea6f58ba1c7cb507fce9e2fa8d6668c837132c6c1c138631c8c536666367af6e1dca0f530c6186908627266b3cc33cbfa6bd31eadac3293d2d66ce2b6dabd338bf5d35aa3f87ead699bf751ad6a56d3b2fa31cd4be974bae2110c06f3e1155b521a942e43cc913e6db2228a32a66f85e72f7d1cfb5501ff0bcd34bab744871cd210aac8b87ea12ad259269323c09129f6b075e9619659cfac53c93e9c06a9758a735be1d568e6797a997c51b75eef871b5c82ade70627cecc387a094839c45d1539c46f371ce73cbcb7fabd1f4ec655f0636b8b7c54156930fa1115141bd20f65c0a1def3b58b4b37f770f330a5dbe6746edbb67da1f661cfa8a535bae86bd28e437e9657de0fd3d487f4335ba374cdf9c5336836457eb4d65ab194d265a8b8521d709c27514239c619299d9f9c7376e1ba5cb7de025ed785d8182594a97352b69cb3ce8f31c7528f42d0c8d9cd19dfdccaf7e757c7ddbab9c6a01b93434ce91453c6cf868f5e5190716aa52eabb40ddf4a747e58ca0fa7c19e6d6b566fa532469b1b67ce8943299d4e854edcad5a67d3302a9df299d75abfb849a933334f86fab2bea4d3059d14e3c4534a4a29a5544ada30fc516a60aeda979462d39284c5a52897967e989ae4921b59f6eace7035a0a4a494a4032efacb4c8232294a59ab9452ca1865edf0db7962cad14309e4dd8a2f7c07a5d65b6fbdf58b111604173d19eac7482fa5f3de54ca87f8b044872ca991121c993670b75b32ce8c02e72ad2e90ddecf67ed56419329634c399125cd6a66b3cc566a468ca9318e0518b240f8424a69cb3aedd82476bb9c5e3799ddb2e79c937ac7e3834a2b75d9d309fdfe32a9ede003476393fa418c5ea32b82105d278f391fc312360f03c07918c7600f23169d87f1061fbdf8e827afc3e0f5c2509e385dee5cf44f023f7f7632e6549f14cb8e05af1d144ba3a4f494d5b9432516d9ae5966b4318bb15614a898b984b21a8c4eab9dd99c5fadd72dcd94b6aacbae421a8d317e617bf79d3326344454ad56b5feb90d94a3df8fce3953a939bf5073fa454dd3b448354dd39294969ae69c3395a2b118a3cb59358dd27b959672fda6fce8446d91cde563df2e93baa5ce238e1863ec21f821eb1bcba2798dbee5918cc1a65ca121a21fa0a01c97523a0d9b11e9c82815d440dd5c421a4e1edb87863532cd965884bf507241f3934a9faf6bb64431618a7c8540d6c9ec081d1d9d6e49d796b9d53c6e9e05a6b020969a79aeda897461cd55bbf5ba8044ba64e93a5dd433c82ee3ab697ba43dbbc418bf9ed6e56d0f73a594c65863ad2d1b6bf59942aba98e95b4886b189e8a0c1453b13edd0af57086cfbe10fcf87befbdf7b35281d88c91a3c7ee5a91d6fbba157a8e84d967857a68a54f9742ddfaf4ec7eec5a882ff9c5b80aed67a57e5da4f758399be0c2ed0b6fecbcc3e95628037d2ad8e3dfd377e574aa7e1bb4f8b3a279c89dfc7a3d9d4e1f96ec1df759d11c86cfbe2e28cfa84f8753d7755dd7755d773a9d4ea7d3e9e4359cbaaeebbaaeebaad3ee743a9d4ea753d7755dad5e6f6dd1902ebfbba5ec9eddb34a99654ea775dc6037cfe334d85a72c8faf6cd711a6c4781355d77aedabbdb639742019143563b4bd64a83d0a86599a69a4eb5ec88c69a5e9110a41e16f93088cba52ab314ec23cf22cad2290070c8451a948dd3411a0a2e4f17fd4218be37a79aadb5d65a6bdd3eceb50f06549e2e5b6badb5d69a655996655956c3193ec412e34a7a6c1d7cbcb0d450b32ccb28a58e039317752c3cd3adb5d65a6be987857af6ddb98ad65a5b6b751c98bcea87a5bae54a2821e4f9507ac7488274eac59824ecf9f08b318974d93d91df5ee3ab86882434079633696e40c79717cf97f63c57d17b640db7372f38ea5ea87de195dd8e148ee7c3c845dcd8797a5ed8b9eedb60fda8c8407ffbac84607e9711e0d849c1df951ed8eb0a4f0d754801060fde90638e3a5ed8b1f4bcf087a5f3f859096178fbe12ed83baf06092b8a01fdf8dc60c3eb4aace3f5f3e3853f1db063a9014bcf8b73cce485bd06fce5b99ace711ce7281f72fb7461a921470a3296a0010fb43802e9c5b94fcf8f8f8fcf0f9317f61ff085dde727f5c2fe735fd82b11ae3e30d80b7b10b017fe6ae8e982831be26022052bb0c1062f9f4b85101d5c5e3e3e38dcd71554130c967a5dc9d6c0602fce7d70005f57261db017e7383079711f962245e4408b9f2f597cd8e2f5a56fb28831262905539421e635b9f8d4c0e708336bc6387a4d1444b1050f6240d1850eaf944d818f7c135d3ab248d5ad25e5ee231933b025717c777777dfa08c3f9229a3c6b7cef8f679b5639c6e7d61952259b3954e9d14545841e654e63e922903c68720124958a42d7c7a90e50d159c404889962b5e008b030e2038b8c091a948420755dda1e22acbe4a0a84c99bff948a60c9126c4419975d0f1566697301f9deb80182058f3ed610864d02da554720a7b843431d14c16df552c7dbb93c88619b18c30df0e46115fc851860c221a2fdf5d7c116b00c53716597c7b175e7c1187956804b3e16598517062f085096098b043add9298432c68888450d90d40064e3db3f31862801ae01044b0d6c503024893805cc8697a171bebfd0036728eb14c220854d10ad2fbe65f78453f3eddd39b95ccced1482e0281be3db67e7c49ee0fb491d621215c06ab937f87e32b9503a29a5744e4ae9a4b206a7f0e713df0540157d37f9febe67ca2c0917503be43012f6f2c99411fa48e68a18928f64ac58fa0aa65bddcdaf82a9b5123558adc815a85b6105f312f69212f63aadd341d3c59a49d2a99ad824be7c8553493a3dd32efa6a2e625e7a0cdbcc4b8f619f79e9316c342fc356f392e9e7254543d3c8353edd59b4bcdcf23294a1540aa823a8840c7765490c32a5660680000008002315002020100c09c582e178344d04c5e20714800e7a984a74529989a44910c4384a19638c31c000000110019a196d132af7690120ee74a835c5af9be19f9ab647821b03d6d8c93d81b6b8d0a8cd4c6d06fbb4a2119cfa6c52f9e3813a1271ef9647d05724ca87f16151dbd7b7c4223a63696629984669c69f38f53d083e156d27c07cf1ba6b95d53be78e79bee2b2fbba80362f430d4e537a93c49ff6ce71cdf2536ddd8d394465f905d88135bc4899a30b62666d361b0df7d8b20c7d9d7e604d99835e21fef701cf4592a79c7a7550aa12ecaf725b5b1990148dbf1cd14390033ad09bfbfbd3845c951a6de8939708953ba52750998ae77c3363dbf1f2a8e9b66abc81b66fc3f89c35b100a504cdaf22490b2fd49351382b9d2a5367e802e0c204041202076b0d740aa0a2427b25af83b6616c3e0f5ed9fe3831f54627a60243204bbe87c0b96b56764e66d2f5bb34fa6f199040c28764f7f35fb2836d16bfe4d90d8aa8682b59a6fb6ec007d3ec2a81d310fd79fa72f3705c7873489e78e4feb410b23e32c4dd5fa10089d3aef07e9bfbdf0af5d2cfcb3d3156276e003b36d60ffc1790ec48f603a3027b4fc10dab4a76ce11c6196c33d258730f982c59f912161f982796517da77221cd58bdc4959def13f8238f2d17af2d758491137dc458c6b324471f094a206f0d69d77c890248013431e872a448c9fce8daa3f103a58925a134a39f51046fe4c9809b1834512982b978f672d4002d18ca576fb014df75328b1c05a223ec62997637942820fd612f9c846968ffe8b9970c908da5a001d724df66ee82f6ae9c349d888dba18850782f2765aa6b694decb1444d696d17ac1f1778b0463bc48c0dc70d48a7179c8452401994164ff82c706441e495ca8f72df575626788b30e498145e43d762eaa2b9d8e24a90176dd16053c79ebd0e64eda77f59cd6f8b7e415d8fd4411c73da76d92def7dd538f09a508dbd347eb7a36da4ba1283f9eca17db05bfda0ca83f5174ec83a1478df6911c362d5a111468a3630229dcca4d6439c8d08649723aaf82c8c5ef77bb7737ec56615eaff9d081144bbad34f46f1a510131f9dd6dd54ade4098d53914e03868f61e4e657a8df940c9dcc7aa1097d2e492afe783402047f3fcd7bb37120492503a0eede32aa9a231809318e137624638a56f4c3d8486df41840852b7ce6d135e143d6d03a4841c8debd347441dcc0c3a01c62f3f41a110ccac370cefc95c89580f1868c07aa9d59ea58e15b456db3e37bdf3929415e405f0a0e26ceda283044a413c53c2f6d8c33de84e25ba8a0b4658d3d7a0cab6f8d82b20983404bd71a76ce813686217ba15196a01c82b9a517a1d1ebc6151fff856ed13595e67117d2bc05afd12a8724f18b7ee874f61bf95741cbfc8f22bdeafed76f2d672521cf64932d487c223badf4e917dad47c5d0844b767d9f6a2628a55577938f18928038828a4a28609adb24ab8a320780015226a798eafb9a769d6bdd287cfac5e0cdbc431e8d116cacc8d3011759d48a46802d84c16fa4570ec5c55e47c61d600f1a7521b5288dd70aece09ef11532042cc9de3fe71af8403dc4381aa0e1c3ce178a9e760d3f9b5ae0f9c6d267593cd9c87ea02414d9c62d09f0b20a073c9cfbc6fe0529743d3f40ccb5421d78e80b49242f4d8cd1698f520e7066ed94a4dad0e44e998afba4542eca6586511c070b32ea39b8f82c55128fc020dfb36d0887fe60a796c88f39c0eaa8ae0124c4dd2255239b84d6922ea65ac4f0f1daa3554d73b4954cf6706f789c5d7aca6a3d2e3ad26432e829ff14176ffeb3913eac1784025a7033c49d027485b7c0e6a83f8a493286164037c23f3e35913c36db6ca084c8430692f9f950eb9930cf0e66096cdc1cdfd68d999b6dd11bf18191dbca081647486c6c41d74ef55c0086abe80e3cf7af17dcd01c8c48a04d2c77117dd4ae09358099789c9a319b24d816e4a55cc9de288879e492d11870b4679b7c71710953c23fbf628dce27a4d67f851ad6ab9273149f52f78f90f4892f9a72958fb1432fc4400e62f589f21e8547cdde573a5273921281a46f2fe2cc9e132445f6553f0daaef0508d4595feb40256447cfca5ca7f608ba108acb9ae03d39face9da4c84bf32fdefbc3fe47e63ba65ce96f839c3b25f55102f805d039a9389d3cf117061cb2bc9fe5678226b5f70a0183cf10f0eef96de4b81b74ed21f46d359b9acd31780b8c9a9f879abd7d2e67569a48a7f1c36443518dc02cc5e05920082a2a8df06c9cc5610abc080fe1dbcd110ac256824b67358ce09a130734c831bdb4ee9815b9b4881d8bb29b06666bdf9c5cb0a999354841eec7d1cdb09777560b07cfee03435c606041af95363db905b029c620a2e1e0720edabb218b9eb3773494605217ca8ec544ab0ed64a9d7d5fbf161f1ab0bad61ff1409b8686439e4d678a866dcf5c95e6520087ca106873465e682d1d45527122dd0c2ee1116b23e1e7b7d60c5a9a7605f5815fb1879a0e1dcbc61327008fb634d82c80a3fcedec5aa513384e66cbc0fdd1bfc804acf24c1852037e66fc080ea22481ad1708272fd01962ff051d6557fc3d2f1a6226d133410c5a903a03f6ec4ee542b27054b4bd2b041a98d81c09b6f0017b0651e328ba7333a32de00de561cfce4eb75e407699ce0aee926f6758bf491b3580ba16dc4bab64fdf641d2a18e68e3aea0d5068f1177c159fdc0ad58f2d0330f133a1793c1c100cd4b1589f172d2d1827b2144d26fa79e8a7a2d33de7702a11427efb04cc60d309743dc7882f1d26049d73fe9b57b39f5dfc9bd503804ce207e29948c464bc469853cdbb5d5aee8991657bee88bce4d3773b33d0faf8c576af72908e1e80b50dc1261c86bb731815f4eb1db39c57850f3f243ee525da8dd5804920ec4efdb35453b01db8b5477c61377a59dfdfc49e377c4590dd024190ac6ffe24727c677023f7f507990843ad5a936440c2874db33c4f90b084806ed1904896bb6b384795ff54fbb186767cb4073b5be25b6c9305902a8d8fc1f90e4a1f29782af8df46815621c486686b8caf86791bf76b036fc484ad0f3f3fff31b9303775e2b6465d9471084a295fe4058d7fd0937fb02ac775e58f28b44fb2af86670bc6447a392d6527492b83ca34affffac9f49a4949c673354605f5beab7b13fb30cae04e4d4c67f80bd0b8b6396d03cb4243cad90a5d76ec6ed8f3e0401fd1bb16140c7bff5d1843047e0d0fb727c8b15f48b768f356abd6767d3a958148198ed40917290359595d8ae6ad1b73eb444fd36f92156f7735f29bb337efae7103b209e690fad4c51ae294ac66032d014498ec7fba5684219b6e4b1df0bb6db8785dbbedec9b3193c8d12baeae4f06e720dae026e61b6d5b68cf282021b50f8685e0a3228f2e6a63c0ad0cbc7bcd705704af91c88393246aa81896955f8e431bf45b2aadd7d40dc188d942c1e7734dcaebcc34e922b1d13edd3d674bcb6f27db6efafbcb088f9b374f329702a38de9a5d68379e9b2c1dd964e7f932604acf72de1cc1bd0b8d5c29d6b7853cd380bf6d4727cf94643f9a44bbd2137b747ca42b71c255ce0f2f483f4506d4c76a5539b905dc8fde9cc5a3a2d40c3e4418013bc935f36a24a50ea45c626d2ee3c399986d10420eae5a5de6c92af31f2366242c9d8e2fb1c600912477390722dd556183be27327492b5befd4744d5a0d8bb5a9aa3466e8c32a418f6aae1bdcbb01a8caae310743c2773badb0ce818808af43da320c1fe1aab66304dd0e2298cc2c43a0070ccb04eb265db263a195f18af9dc2f87411972c4de59d871db004d6c234e0674683a7592ec67c564b04909f6c4bae702dfa7d656ed4ace9b3a0795785f9c45a311c9853020b82d909bdca360d4541f634fe55ec01e0282c11f971c4ff69603370f8f84c2118adbde8ca943595eb69ea300d1388bfd485979623a3846189d8f41447190e5007f86d2f3f9cd9ca7da0ceef708def0bcd75763ae857b560934d3aae1a3d5e5d25546eb4691431961e61fbc670727cc04db429a1cf320e8a2ed92f6caefe2e3b4c8a09be85dd7720d472cb8f935f6b3ccb1e1665efd1333c635a1c56ea545513348ceb295bdeb66288dfccb26c4d714266d9fc883bdf888f22d8bea17c3a5c74106d8a413645827e313463ddc29742216f2d2276dd41eeb6dde40b38353d8ff78216e11acd9c3e290d54bc13034a5e39c9c08fa7669f369b4e39c80d985bc02770bea5f1167ef7da95cc0c175c49a1b6a912c3df508f7ab7c931a4df52570b7bb2dd3abb79b3bcc3a44709459e9cf8fe2129ffc21cfa19216b1a33deb94be114bc67f34011ca99dadcb0c1cf411f10b647003557b382ee99ce1f3b2cc19fba595a24f33bca5e12bdb78bb361f1e1a9607b6acf2d71a1f10b6205211b9724ae51a1b07889606b18e4dd312f3cf6b58c26482286915bfc9041c6f94d7ccfc019134a0644c5c6df12a7728e8c85c24df6aa038f9efa566201f891d0554c78963d3ae0e674145f47d19ca1f9f42ad0ebd529873fd38b240b295400ed2c49b4d22d3a589cf8fbb841aa7caeb0b9c0fd74e30f398d15205d31deaf11f0fbc62fdd326d22da8837c0d8eda902ab2c84b8b75f8bbe9cfca0d5766d534c422158d35a2ebbe16fc49f690289f467a478aba93258b5354d5754b4dbc9b54bee2034b6a0e9f2b24f1ca016d018403415c8c6bb5ba0dcab708fa588a86247e63545fac311529fdd6dbdf769a6fef1426ea94b7d9d8e153c3f0371358b364b4141a7dec54303badd439d3112cfb9bc067adabff78974b4cd78a6176f42415b338c1e7a8513bded7ad95fdd612352dcf06e8b54dabe7fbcab8c1801728c6641f132f1a3f102c936d80b2c488d45b8212a2d20f2f74e3672251b0fbc9d464edf4452692aaa47b1d227ad14823d7a936feb014b9cbc3ef6225467039b4b82f52895ab09734393003a266bf137eb7989c83091f4d9978cc9228fdc959dbee22e3ae038280c960b20887067d3bb6d6dda33ba56e455ccceb4fbf62dd7b0f7bd3bdf5c2739e94e79754b0cb8b68282a30b2ed11b60fd1cb00f2f92b4001a96f170498089cf61e090abc3313325253c3bbd7a71a672d2ddf28493d2d923cea6018c5fb31e756852e285f0e99249ad138e66d23fe102b0296b46eb315a4c5bb3ed4defddcbfcd1b1b2716f525141fe4e405aff7ce31dc60a3aa489bbdb337ae54bc2657b6ca74134cde0277efd6a5278e26cf53fa0227f05840b48070eb8ada4c6e6bb2f0ad5c5f04a8689df889b81517f35668b9fe339bc0bd7db90e8a4b7de8f832d79506fb98a1499b80bbc3613f444920ee2b5125147f0418d2c6c81e5d58322726b39aeba30a3eebe2b13e5b7b741b84926db838402f68998e06e91de091fb037cd66cca79fdf28e70d186b7b04204559a065ea74ac227565cac8d94519649a1f3e794719ec2e5a35687f2c3c28d1b825e9fcbe7c7476b61da07c3c08bc735f4ca199f3f88327bc53a1276eb8ab9902a8cfd166c2e650872b619b70f14b87aaf13efb09c8300127e92178fe41f54a94c24b61fd4ba8aa6889022575d117853126fb0c0918e7223a677652a7b3822159f45924b4209b5d7e52e8448e8bbfafc3485d50e2b4faaafd4d7a44920ac55474bd2043c9dacbbf817dac2ccc008c8c9ca9a07b1ef57afc3a19678d2ac74572e6fc46a5d6c769a3f36a7af78a039d0020ab4e63a0b137d7b3099fec9135247d4424e316afef94f40fc6c331d0a3cafe121c50034d2584028d24dea06e7ab8f4f9fba1f855548b6838f11080832aa540311b90ee97a3204eae1f6c8edae815ef312091a8e3126d88e6bc7d449102a6ece9283800d8310dc789d99282b9e63df4167371d23d27c0089de42a3a398bb44442277aef469420d1fa4b22912247edca2994a0efc8fe4a285e61daa0088a24855ffd75cd38a46e265490d3e2a1fad8fd597a291b8d3dafa7c30f72ef6bdc9e6d64beae386a713bb5f8686dfb9956c5052f04b17990b70ade29c60d88da320915c5d2cb0879765258b5ec304f84cfadd3892da0e8eac99997163964d7c55e9b83a793415638b7391002441b7baaf3a5fa59e188b72856f481e73fb400e50c73e8b5abbcf984932cd4a2bbaed26b24465f9020c3188274fe2afdea5c6dcbd5d351c1e7ccab740e963a3e51e9515b0b55cedb81308ddd46c26245a978a9fc3912f02632526c022e3e4ffcd1ece6fd6d4b1d5207e11fb73b3e428ed7fcdcaba102ed7322f214d6a83da6ca50089546ac1f5effd806b79c542529c93de198298bff596fe3a722bc435f950f3ebcc110e561dadc97ed9a486b18a138d38a848d65256f5fba945b63456b706634a76e9286006829eb4cb11067d0826686b3fab5795d27b102f3faee22c1d169c172dd2f5f85cd75f8041f6a92555941ed226d38be3864297320f9239f77d31fccba73d0c27b870e1660fbebb85c7e47b66cde7a5a5a66937b8bd671c7f362e88973cf62596031a5ff30677202ca1bd00038b97ae1b77d15d22c1851257937c256f41c87f633806c2b9153df6464320987ae2786055320d224f5855741aa3c4cacbc6eb8c15b0a108dd3848ebec19197d3e576c6c78238fa0a3919493f9246ae7e54c43c81024211884c098625ada13addbbb381792a8352c1b4073c8cf0492e1cefaf136999ff7dbfd3d58a6c1ae3bd83c0d2aea164cba0f6ea3161dc97283fde0123967f3f8205d727b2d908b148a3f043b0842b3aab7250c4c90f33726da76180c10114e1030efba4fd58a10adb92e68265e9c13ed1f5e842af22b3ee2efc4f5e0a990f260248169b70a2b99c4d883c8e7c122177a3b85bfb4e7a703f68eb5c77230b38207b826bb9e9bb11047088db8e2d588ebf093c51c82d82783d008c5cb11dc309e7f6e394f3bc98ec192e394f23769ad60e1bf1370dc0787c3692d9080b63ae4bdc36350b64c6738b5a6d53278a928a619cdc9aa8a2e2a465b8dedc2605188a616a6596a537c02c31486348889869f90000e0457bb66c2715fefe69a057681708f4060296fcae9de18c9fd10b1cfd2dc5d16e8fbf3a98718e51070136d283a847aeea6ca7a30c70772cab0407062f1b6480ced76d58eeed07d038c1d72a4b57eea93ec11e47d1b78c46796cfdae727a688e9808f1d32fa10b93934f0e18c54c874956d0f9985b05493069e8145377567e429db321a933cda4df80b80e6c66bf72ff21c3b57da471004e49866c5a195ba8336a239919616f9259dbefeee94a16ffc078b5c6e37764d6c2cd659bc67e2568f36190585bedbc4170acdd50ae05d5a44a0be9dd801b8a2854960839e67da4c8e665ec69e17f9ed54b5b762d03881bb25ee483ff5e0723436a516a65092a18c928b26267e5c86f91807bbb7cf13bdb92b3e47b2ffb424da660644770e6527ed7386752328b29aca840016a929bcc1e2b1b2675ba001627f61863b99a7452d3f83383143807c85375026b93e52ca050c3bdc151f944ff874c400aeba31c43cfc851fe1f3c0d72fb65409b5b95e4619d9fc95fe1c17191fa86411b38b1b246465435499f36d67b415674aebd38d6d41554662ea6fcd88d9f96838df0dc68208af894c8117f46607ca25aa07ecb9be2f208f2e21a3bcad2fbe0fcc11694f89c8c0393722db074b8ec0db63c6719f6f2ab788b2a82288492742ceb883d4f1d425ccfd61d9917d6b3b62d3aab62fb78cd96c8b1a6d2e5a9bf4059196e4941186b7b4674e335139b95c6497ffd885d7eb52b7596d44088a86a066a013b958108a251b98bd636ad100a0cf04540b398d8731a24d2a7629b35e91deb6bd09127214d47806d1695221db08583c55abb7dbeb84a8804cc1b8c606884fba5dcb783e9461e6c49581a24d263070e4ee29c2129da460fe876614748aa9963193cc04c29551825253fcb710b8079c802431eda07ec559a4b9071a5362838c0bc7c2c350430322c4877dfab0a2a1b0462684b4040f5ea1d3fa1aab374927ca5b74d8e9a287ec700d7e2f3a610fc2b83b3dc3e8a090390b25de9ee9633b16c146b11b1129a20801fc78892f2462b3809166c4b38470b320450b7fd7b178e5c83da2d6ac8cb84b34fc2b3c40c858f842b2bb9efdbc8ca2c250b290b5c222a3d47f056d8297f44e1949205685c238c6a984240c7d18b2a8731bc0200a0f46ecf4fba91f499cfff2fd83a82ed2d83b66148e2bb2cbbaf0bbebecf15f85810af2c53f9a37265d60cb11984132b8c9352925f24bf51e7134eb1d39df76dc10fd0c661502cbe42e90f56ec22d68f796a0771caf230a306f1d27712647d0478b156ce46509354663d4cca6aa4659acae27b2a2a78e45de4ca732fd7177f4749dd3e45e2d435dc29100357ed8a07db50a7d8501441b8808558dd29165ca95c8ef7805a8951241f0d5a898f4c6400093ba8e34e5724f515d58aa83a21a8019dac09c94f24254b483ae71445a8e6f8f886cf04383ab3058419217eebc495fc21f5e101f994f512b7c36cdf91b3ee4e7f7877efc5bb3ba610172f419344390f850d87eceab5e14b67bac4542619b30084198d724e589d2d12685aff460a7d3dd08232bf227544aa536021f0bef6e8cb584c1b0b0e35f45f3ff117fe5d00e6ad1cb487f0fe289615e262bb74eb64d1d2f6081e15e4e6b875c2bf8af94f2a68a20e238959fe052eaf8ef5eb98bf607010abcc757b32ef3811fde6fe207f918e88ef397af786ed769b0cb24e94eff0a0e73c97a3540a886b9ea896ec190f5e2f605db1615408ed911b6ad7353919cb3dd99838d4ef22bc4ae30d3aa5bf6bf0a9ad2219fe8ab3b085f29ea23696e27d42f92102007554ac752de923a302822e932b915960e9a6c1403bae166596e724aafedcce84fcfa06ad1f94da719815fab4cc516ad7131fc75508f403ef0d78cb8875c640c433180d6cb43ec011e0fe5bbfcc87ddc6763c067c5664dc773478ab976247eb2a3e0d55bd1da4c1a424927b860f93ff5e66aa4e194bb65c676418e2929280aed8b4a811c8cd1090ebdb6012fc406c0486683434dde646660f5e4b20852c54ba9e7a3dff1e2c499c05b8201219a0dc2e104a63b998a87abbae24b6b76da59a4da5fb2c1c6f62f585f67bf6810f54b245c4db5ab912a494e95205414451bbd5f3f901fe9887529f126618173b04b962227fca7163a09b5401c545eed4b408ae5af042ef8f0c083447c4d7475bfef383a558c212aaeb2a6e816653236bef7f6516e22fbf9a788b89e979544e0ef1b42ddb773c1819e592ba7c06297e22de4a9d9c03fc5d8f52924175fd752b0b726e4d4dc063d371c879759c35e66cdcab8dd38dbc61f83bdadd49a6b563087f38c79260684e5966453fec96d84782fba5829a4b3be0e69ea5b0a1a282be5042268e923b6963d03d6f70bd04e8c08493d7ef03762997adea07a1680f4155c2870ef58600a1c314841f28ba9590f345a15c0b0de396b9630a6426d65ba63920d8d9b774bba71b7445d987d3ab2369e767e8d3a9eb013568358e403834065abd2956de49a18ff0d42200144aef7ca9f59f3bf84eaf7c20f3818d1faac373c99bc9588946df384595109522a2f14b61b65ce12063062b5fa1789237fb10ba4212d0c365ecc227a7c744715d77073ba8438938f6209421a80a1e72580f93fdf6ea55b8ce91111665c65c3f20cb3df35c29511508592360e0cdfba0de6d40967a6e92931841eb65ffcd53e471042251fdba03222f77fbe46030db11434e287b4483a1fa1aff58bc46d79381d59964fa48120720f453eedb3ff7df18aa8ced48fc7087a092f0481df97093a637e978b3eef81d165cbbdd4fbb6aaf48ce45d6f9b2402349d66084ad9063b7372c6f33819c51329f55a13a4b1898776a697d090a4ce4b37e6a62500ddd2c0b103aaf48cf4a15675319f91fe748fda8bb10456278b0d4f6b03ff351b31b6bfbf68adda1ad090057744acc58c2cded3a9c86ca3e4300f590c6f4f373e047278e3c8a2fd195d51a4d6884af682ee289ab2a37dd5df5ae758ca418fdc10d78891798fcf982c75ca8400cc49175bf32eb95e354667a70ae588a4bce6f06c711a01f9495d6319e3e4ecfeb94cdd88b228ad70b79260ac971a6eb571e5c6061fac598090a9b5af2952ce8dc21f7403c10487045c33a55fde73fcdb5a55567addc5281fda44d9b2d819fdffe692cc576a17f83309376d4a79a60499e7771d28588f90b70ce9e2ccceab27110385707fd3a637dab289b593a8ba147480ad7d8ac0664db15f4d68e99d26457fbb31e512ca84a70cf9322c7f0026e12bc3b43e7c36440c22f5bc895153bc6aae9ea360465885af52bbdf92a3c83986867f8e66537a2240bfaf665fae04ef1cc49b70660f5197290dde1ed33255caceb0194905c829566907cd21e6360bf2d681ef45fadc7b4535f65ea3c563b4a726b76e911a87e4576a8a21e2d562b660d6a4e7ed6e5d11238f8e050148df5b4b0760967f3a8911e69183213b87b82eddfd97c14855d8cac0e186a36f83461f6ec09e7d556966e54344b679f92cd8d64e304989e3739ddd13a36468e0cb1493cbb8aabebeef8d070420d5c318542210b35abfce11a119d6add007a778fc998c7cd06ea0e1098ad7cc0ecfb79df8d1e1fc356f17d433e31a886f809f7258a779dda564be03fd9c0f5480dbe3e43719a31ee4bb2f7a775a4fdef485b7e54524d539dada14fffbea9cae32e0237ef987185b6afdc027be89729da8034a4132f31201883360526fb645c68e1ad04ed15da738889832bc4983da197ff7b1e351b503fa61a319eeb348d8bce23478dacd82a1c78673412517de7c6dbecea802430c52fac4ba3a6ccbb755b3258788da50cf139c22abc413448abcaf0977bba9ba3649fa0bd5ac8beb76853dc30b5197a0b5b1943757f8514095eaad7b1ea65af98b4c93c039a69bf44c801ca72d45a1563dc0edffaa956abf126b25432002574334272e4332e637b2cf9e2b150fec429639ee52328de55b6a206b85eeef673ee497e71972b5370326a166a0792d3c7c294a801a8533f89cc5ed0bc9401465c0790c6d9ed358398fec184242339a9d2e6b5f5c15e86bca0c4a482bf894a174d8445abbbd14bd62c1b3eef0ca51a0001536a5c9a596181810c39c4ee0a2e1236d1ed57b809d02370a904fed10bf7dcb63a4dd5701433c9a6ba7ddce5386087feb1931833c23b1fe5146c7a9d1be6ddca03593be8ade5e7747395f8063996feb66cf369e3a1431f77b2442ce82ca6d6edebe0173f67109c73e044118b93e77faa95fa89bee0d16e5000ec26e65e255c454a107e44f950943947aa455d2d48c6fd1e17df870eba241ffca0559e4497d82448b31783aed47b8f5f5581ea45ec75f4cc80905230bcec9b973f3081a5f60e815b328c9327163c380183eb2ed921827e11deac5588655ca22307207a2817cae4bbfdb24f4dbce02a2374e641bcdcc00a2a0aa3521b170a582f510bfa3a949b265ea67c321b276a4e71cc33c5f24e6797ee665fab51e6783a0de5fbba61984bae89a57931abb558b990daaa48d8e5e1f5f7f22fc3ca5492551b3361b31cce1e931e61d9cd755402ae62b5618308b343ad2ee81e305a87d1f255c1fb502d2222f2920853cc54530de502e33a084cb4dc66f4eac7f7eeeeff81efa57c196aa6c514c5d9bb10034e09fa9e4853292ff718724c7c7e43bce5116e0c7d27be3640407e9152a5790ce9dfa90a7de80b26851bae693c91bda9a93189953167543b802d3aead4f771b83d72e902ebb117c04570a47f07033b1726d92cb7625b017513e32312d03e9cf2d211a0e8c0e1e5169f92303c1191cd9872b698ab010229f3f944a97de7f66ff3ebe00da58c5152866d2e46730136fc9896399ae3b48a4d00bdc2f289dcf070d42987658ba4b34b1eb2dbb474aef0098a8a98a7d5e35d0aefa1a6033406627201c0112bc1d49562953de61e06bd6b4b6312e00d6b831863ced0035b6170e081c4c5bcfd76059d76f9a61945aba429ec60cb8101bddaf2b301e449ef452e43e15e2a5ed93667027cf683be5d181811ab9bdf8a20e55203a1ca404de2ac8596d845100c2db1d07ddb955e8c5be5acccfb8bdb0ae6fa73d3669ee7a4877e88cb2bad19446d7c1623574cdde4281cc35fb24a1560558e6009c17a60cb1cddb2c70a167021b8e45a9ec57e8c93a8a7116f58f02f7e4c7c02e8dcd1d7f1bcff997943ea2ab5ec447747ac271632bc1ef16e84797bde38f6ad08faabba60d61057fcf4273ae7dec10457cc47c40eb07e62d0176efce8822914b654f43691300ed12d42d1339d59eb9bf3e0dd620db388482cba7469fc02f016eca6e5d294d4cb5304499f2340ccad60509ceda35aafc66ec2466fa4ff3ff66ef2144b1e0119d73e8e2f18e6dbd74f4eb54548ddc00e545ff20e34204319bfbdd8a497a1925d7f2da10f6da8cfb63d7919f364d24ba5ae6bfbed356ed3eba081f815e57a0bde9991d8df466839b8dfada5822d1102b7c6e17f08f57a677db14894ef3585d8aeb0a2201647228eae687d7240a891faaad596aab5cc4ba1f352e9022e87d17f14001f0c8dd9bde3957d451192323bfe110733e3fd4bc59364817dbd23480eab0ff2d5d485cacb2535e4e110f96134e1136a54df0196c866413ebcf544ae065a4eeb2e1cffcff5f7d18794a2cf4d2c3de22458f3461a9af3dd6e0b4a0da85af499caa9812d45570ec307e6d3c4b0b4b82ae35a28a42a30dfa7342c3c5a6e8770c328f296ee085394925b968d1441c6b8489652921fae941037a5ce843ad351f25db8512ff2d0939696722f35906392af997dec2e67df027e0c9c20189021de533a2d5a65abb80c0ae86e8dc6c258f34030150c273dc4e28a986e93e9907934a1006abfe0409f930de4fa480535403504adc14fdf08989aa0a65ed7e464978f15a3292d7a0a4e3dc392cebd7a83214511a3921916e8ca29c754c01d174ddc5d24faf236007527b1a1657e5aed19a6030ee42f936dcaa54e78652df7256a5f0503a01a6e8306872aa8f4adc8048a2f8c1feb25eea77a29feb6f3d036320711d470bcfc0b522265c2b0e27f4912e53b357ccec905feb266c1478dc7467d85ac233312a917e9586108ae7db9686e60b1179547191e17e9e5b2565e96c3613dcef67b18cbeb734506436c52761ea586a816306de4895c1d72d1fd6a08d31ea50fcb05d4d28924d66beac6727ee8fd5b69fbba12294e87e82296224919e2c2aab48364bc4458e9c32a2a2baa318e0c1fe909dd33f074aae47d55b5820f42a408c6da1d08ecb5929b634d9cb19a3cd4663c1870a56cf4efb86f764e753ee9c97eea38db806b6996aeb8bc375bf8f9dfffdb55b041ea254ca94b771cdbb88ad9b23c3604d0e67c455a9c7b9e6c856a0d71290f008736b428e5ec90bd2efe8aadd5dc035bb49b6166197c8a81f66e66834f1d45cf757d626ab116b075442472c1cd1e4985e4d7b474e5f39a87b2bbf2fa99344f562adc9ea126d0b6b53ee6c92d6a214e33fe483d7a772190e4fcfed5de7058fcbb778b536f2c1dc7150ce7380fc590284f20f5c0529489552d0636e210cf7024bd0563101f9b8e43ca31c1654b53ab0123384d0647302ed108bdc14988a5805d513c6cd2be02b9949fac87064655e6e2d5a907a8b4cf5a013e69e4a324e12d275c96dc8fff666d5ccea35e6519b0790a64dfe7ff07395172b724a04bdb6b1b75f85c3229bf28b7a69044ba08493158131f42b96f2c0c3c63c6218ba52d0408aad955fba0a05afb0ae5140735002050ce28b3a830c2634a3b819e5bcd3d699f13ccec6118f0be5f4a8e04462a46cc1a4b2d284b4bc3b3dc2957e7eacc9ece4328060750b818fb0ee09072a94114731f3c5238a2b96fb5f9c4f78b9139cb21f89216fb5ae556f19d3b32818c3b46f561cc932e75fb30aa314245de5a8cf51bf2ede6f10bc6c0a165717337904dca8fe5d38e865491b2ebe4987735879711d929b83afbfef9154954840364bfc88eb7cdee9da3fe618825e2038af73bcaa3c6d6531842d0508a1e8317ff7003b6a07eedfcc014c50778c0997d06795dfb2a3d19eae0d34c2ebabb707b47722b43a9d15e164efda5e19eb20b441275b9fa5c79b3774419f4f7051a0baf8fada50e224baf6219dd2784276cefced07983838c30b31b2215de615c2b1e41dd8a575f0c4587f4addec0c1127775159303403c2e328415494055a38c860c25bb49f5173924a229a37facd6a813b9b271a96f0388a773654e8822471e4846d02571e10070cc9bf1a3aa32c8f92d318ade89688b0d380002bdbc7dff1dd7dd61c547493388e215a51076233b6946cd235b8ad40d0da77adc288332cac6d8f7816d5f1b00782efed00e973710fcac08ee99b0f90e10e7bd55cf02574538947be99f0d4d084a72d00ad1c99237c59155851d90dc62cd82daedfb261354334610d546f9ab1e83370900f6e8054241f6026d47224372dc60e544e0ef81ad94afef1ab86712d0cf524141a38c109500657b1dfdfc4a2b1c424409870b58817bd7f850cdd68948144142ea037cd37b080aa474d5fe2c268f5bfdeb580335c87725a367ecc5610192c88fd42e36aa56ee091ed6305c6c5dfd1848b0a52a2a051d644e706f644880d3df89e265e080a4f8cf3dbf64ea4790f84238ca73581e8ee206fdb6ee8361d44590a76e67d10cafb6cb0807df95b27d7ed8bd79b23ab56ad5c0d5196a45eaa5487d20e59fe3895afc05f8c78be87c40a68022b5c155f4643d51f67a54095370d12e5522deb08b426d900e91c9b408a529017411c6ed3d62f2d26614ea2077e0584f6e761a92d8ef3b56eac0850686d83912d5d60c4bd0bcdbbbd425656419060c4106a0fc145ac74071bcbde85f0a6ba08fe161abf6906a06c6afc1614e06bf952302b9457077dc9c1033e76780266d3f5788b805404277e585588ea20cec846a99c9fb20fb82329dcdee0c57778cd3b3cebee5f407762682c5c867eb9885b4a0ad441eb48043feb3cc1c04bfde32ec0096525306cef688fc37511c1b16a8bd1df78a8da68467913779aa0f8d9ee0452b06dcb62c8d25e7624262f9b40bdbb4c04e6ed5730a526c060b881dca532088dfba3b43aa12216193cb9afd58a964ab646ecee9998342420ff8886d893c02f648560fe6758e57ee6cf9024117d859ba3e9d7766bbdcc4385347ec0d607d3623a15a1d1739454b4469349daa11b993f8f922f12e3b1b70d14f6e0f974896d5a83aa00e5b8cf48eb6b7b3a961ce71768c711558aa0152e7a405925d625b44146582c2615a739a3546fbeb3128b034ace74127901b2eb69f335a9ae21eea5d660e89c105bc43e172c5b4b741178fde63fce44a78004c29e106fba7dee054fcc8d556e3809227e381633928d1ffb12ceee68c4e0aa403618bd2ac25932dd1c9921ddc6dc16211c558560beede0fb900ab122c2bf65067b4a19923858b3e6c32f64f70c402b2c421eb60b5c02a048114b4005dc28278fdf5db38ab2066debd4ffafcfe59cccdd24f9d5d59ef0e17efd7de66afc62c070c2d2aab84df66311f1a01fddd47a27d9b043fd86bba4ab8787ac6d8dca13399c36f36aa8b7f551d9cd752b81e8df5ca42c42c7becc0198dd97f71868859c7b1fd4a908e3f95c43bc24358077cfca76ba724dafb765f8ba350ad872d6bb81f3712d70324b51a92ca7457f9185aabd35d0c9cd0561516972afc7317c34cbe865eb011dd81c002e8aa4bb6701e4e8888c9d4e1b303d03703874f5406fe1f924e8dbf39dc1de98329f8450e1fa552671b9dbb8e216139f4c3502881dcf352eaf38b014f458c8a7dedf05f9e371bc0d9a280863023d8234805e211df73e70faf1faa6aaa081a7783f7a782fb4e8a55cd7f6951f17ea456471a15a934e765896e0d1707d35c84747c094129ee2109c420e2288c90d19052b4090147462bc845219f9e8194f9da2820cc51f6ca9218ea9a2026b2538b0803c16665f48a8c23c55ba8a43178e2f22deacaf6334199caa8f72c83ead47f7bd9c85efb2873db88e1bec855ac3b1dcccb8fddec90091ee8464cda2dc392c352751290fa830b5a033bd6da14dc894227889591bcaca45ffeef989f5657d1b76721a7372328105b9eec537ab5be8a6d11ea66ac8eb20786e3ee34bf530a33e0152662bdeb1264c5b967d8e05117752797f1f1f5a02518d074a53fd726dedd6346bf8a502d8abe64a2c34c752884cf203c3769b136b356e831530923e9f668879b8dc91887fa614ec4eb15f0eb578739a762da68e17a9bd58c663fdad82b5452566a821aca8d4d64f82ddd98cb62a14068558c7e4426eb973d728d8e89300313bd3b57f87eda94bc7ade937c7f8aa1882927f73fc1d4f1edcca1ea3970903c46162114ce772c2ee4f51c81311d8018138869af3313bf3f9defb11dee59d993dfdb4d2c42f029170425ce101a53c9730c98d83bcb37f261b48e836462fde36a6fd40901db6aafed91c9eed1e347294b80e92a19356397cf56417f2065a9451b4d87d5b1eaf0bf33dd36bfa828ea5a8bcf4d8c6fcec7bfdb2e7c2722ba57cb1f536bcf46c9548ee4621886cab0327148ce22336ad7b9389558c47d16b479128687cc1f1449192a516625c2ba2f884a58a296cdb252a24c7acf62665dc22d5b5d2e10f1f76a7fcfb697c7dcd02012d2b526002a1af26fcb972e70e51103719830fa9e6e41832cfc1d3461dd9d7a720dfee77905448d4d0ce45fe70bbaae09334f855e859c7f5c5b07bc75ea2c12ce11c7f8de0c8729e99fb813d0f7aa98402f23921a31f06e8890f03ec13fa6f09f85e53b0076130abf08f5796fc6468f60ec343ea9044c99f023aeed78e1be38d9d8cbd2286dbe6ce7e59e0f6bcea60af2d3888af0ebc5306adad12b868af7208917e1a2110e891a3fa9d80957423f2845440b908d4bf9db3a49b7d60d78092c5bcddd88d3e2ba58cbd1993fc116360c52a6b97d3527c4365e81a7c94adb92680c3fcab470908b951ab76435726cab7c37eab48e72259f3c2b91a77100756af2b57730454f6c704da5f3350503567d3b8f2a17f1c4b45be8499802fbf06e9c2e442efae4bafc000546290705edb09708047504687de1467c0240e64b7bf24a050e775d062a969bae4ae57c3644e1804ddba60d7afa59e16a4d998054f5b55d167d274a66a95f6fac44f09f6502a1d1ec1139489f98ce1b853b4a705c49b4dfdd8a38e485560412bc87c69ec48a47f59651408d96692df9043b6aff030a39e9b3bdb8708a234d80f698afe0d7fda71531311ff32b533b107d84dca8b1181d3348d1af006337cb49fac0786646b51389bdaf8ba2d8dfc4ebca4dcc7546136b907e034c8076060c30d4db4ee6c3fc663507ffebb939f7a3be06bf7da45bd9cc514c87ca13660c9c59edee3101fa6cfeed58208e23069eab1f64654311b786e58116e9fe9c8f5c42698bdc6dfd8d4dac751018b2ec418df31a109338c1c8fd0f7b8efb125c59c42889a8793b0fbc678101f8db89c753a04caf3370b014f2de400237309c542abfa3412ad89035c1c1bbc687d4f22b38fd2daf44d07b469b1d3502818f4919e675bcf501b71120e807c9a9e2f540a486f8140c7cd15a4c68b7524fbaa92c84912a1878e87a32824dec1e9af3e1c8f70df6058629bf10d9a47be50ca3fe588a02c5c84f06a21048c4e23e0415250a0be221b7e23cd2b0635640639b6970b8d2bc02062b040cd3273c8604f37c203745f8ceef81afd24effd90daacf954013350e504f29a66a0831463e28fe684cf17c38501fabdb8df1f0d8548028d0ddfcd2617d1703ac2d73da00ebdc1c843d18ac355b286702ceaa13a795449cef25373bc4770d100951129390a58ff558d12b31e74a7c66ee07ff9545aa5b9be6706489b03e18b337914da1f91b4c570931755dd0be6411878bdbd8fac7357864d6bddac8832d2056ad5b53eed93d0ae048ddd2b664544617ad267f8c04795fbc1ebdb0e3929a8912e53c216445b00884f21d891bd194d2d22251ac7ef11a6d9376607840682f9a3fd9e6c990b78af8fbca90329d6ef999dc8a1d092487701f553ebbbd2b2423c478e11e0879eb26b82a01fb3eb2735a16fad00484fd0aa25b1386309ef3d73cad932890f445740261a2fb65a6bcfdf2b3f12c571726f2e573f5637deb9a2f14a23f0cbeb53c261f54fc6fcbefadce97ceb29095cc08648b4bbcd2578a9a65b692eca395a48a50395d5fa9167eedb48f7d7b31ea4b183e87e1958b8eac9af05347c4c6b67a73303f15956ad07fab6537f98153a2accddce3270d2c6fbf4db2de80661cac088a708bbf80a39b0297d0eccdf920a7d5cf4ac1fed2a894c49dde6a02377e183c382a61010f3cd96307c0c7ab5b6b70850fb07682da3be1f6201f4b223367ec0b6b46a48d2d98cd425c615515c1e8a114d00e96af0225f214f2786b2a524a49767b0bcb5aacef0d1e296b6e90869e5c89dc949e913c7d5058dc971e78d10cd681522a880cec6a41b28574322b9c9e2a714ae20159c0d57ddd8c4c0c74580fea153c2b085dd264da72755d3c33186bcb2bee2af021ee3957a85c23f77f7b862f278c05699757f739c05ecd78d5739b58c37e61b2c915087924a563d352db8f25a5c4c85700565508363e97ec3c25e152693df70d7979a32a2bb09def87bc9406821d8e53893405f24cfcacbd296b60414463213660dbb201314f439c5501acef7c0e72307d08cfbda5388e9ad1c6818169ff2e0bdf1133c2cc59e7c2f112acab35daed1a22f103ff3c4a49b0a1a2b7fa0544983588d9df3518aef363a6e7791b717c69f679c5d7008e6436f0864f9210f78f4155c0308712a41df17498a96a45a64f172189acb840c606a91ac09b51791b617a60df676523f699fd56ba0fdae09273ee6fdd0a21e32b57f2164a2c547f8b9c612fd1314548d143c3516b074b57919c025a037389b0c949ba970e7d6042ca8266b8993df4132abc031cadd2ee6a6f5a11b703d074172581e2ba3b2e72448ac8044e3106dc17b90d134e0995348935c922535307c581012dc7beb8ad0193d640d5211af1a52d19b26fd7b072116a1013b8758e1793566a409dfb7cce9acf0e92065274ee90db7e7499f67226141822a31673eff2803138e357c33ed93a7bbc1959655ccfe3e2914352a993415c6b86ea76ce9650afdf411911ba6b757364c91225f52a75326f29bab8491aaeed06f6d7b2b380981aed75bbf416183c40cb10cdd518f7338fc0094f77f26a515c155949c7a80e8fca0f62db6e328317c02c78f9b87de0d75ac0bd1d6644161613c1014c96f8881d3999a8faad6ac1598a145ecb96cf3bbe125f54c617a3e32971f098bd8309aba2ada623632828435c1b86a745b08ef787daf349c54262b6d0747b5517cbcd962c9d9fb6cebd485eff458807496368f16507e4e714b2bb1d35ee831ff255c99d23c770aa62869ff003517632fa552cec3105ea04a7b0c01919cb73554739e8ec18ee444e6d639c4723b121b42ccdff9118edf520a26c6cffa10e25dc11ba0550302ebad8eff1218df35b3cf1c10d4fa2e45f0840c32949b49654c94b921d36a4159cb1727b68613d0a73eb18cbea3c13d6863c96583943d94386d5221219943ad6ab8a95c846fbc3c2363e0d3fe14101232d236963b54247a8619806a3fbd1b12e5390cf40bb45c32892811a0f88b617ea52c16a445056a21a1d0d1131c01080d09afed6870779a8d152c0ed0629552e4be780f7612b451b05c8177b6534ac99709f541b081168a9a94db448ced9c8e823b03e77410be720683f3ac40ff39920914f12b80341091d41ad53956d9e4b69d39837c11394f66a7fdc65e31bc184d18d06c958259cc5455390c80fde3b9a7bb743c40458556d6db5695ec26b7d351dc1761965c7433113ea5b3b7b1007ad8a341d41bf223e95ada871e28030d8870d83e8a0a8eb3b9e491bfb6bce3c0cdc78a23bd4c7c7138765cc2ff3c00335184fca90568f975d432c0058db419816ee5d6caf4d7f71ccab01aa5f2b5320bef6c66acd525dd0b0a0f9230f755788bad30d77b13bf2871b26535c02a88130323d469b12ea9b7f9fcc3e4a47d03e19bba35078f37c57004fff591bdf86f6d7b990f5dde86d16eaf89380e0fc75fd50cc5ffa140200b42cdb20204e32bd0894653ed5438c2b9db023359f1b3b84227c660e3d12321f74e23c45d0860f9a33352340455ad70354c638197ce0e2b07b9b348ad190e7ef670fb1cf11c2aaaf73b573b180db98e65949e6009f6d944c8e623005d168e4c34d5b00b6cfe48d4840896862e9dda1fff7236fcf9c207936539b9834a0c602a1d19148faa4a42d6d7e7ccd20805ac611803d2b3de0d6ed03176be670f75e88ffcdde348714d803ec1e8d93395f9a3837d18b26cd40cf813e96d9d670c210ed67d617382423f0a60dcf7eb0bd0ff7814527bb25a905c16c4a097d37c01c81fb16ab4e586eb82350217eb9aeb7056102d8755643148e0f7d46a291004c7df1f466f68dc45eafdbcbead8c680c99309be6a380045101ae0c0fe8759454e77cb930b5c3950ae85daa46c0f022c21493740c89b68505b781eee929862d916025a137ecfc8ae438c035aab9f9a84af1de8c3a8f0c55334d156f250b9d3944c62b39b4df81b0c4f7671c7a4149cc512cc9a0972975d936ae5f7f9566a0156661230d80a4e296e1737af851eeb7d495aef218ab755bbf7a41225265b0ce6ed20b4632f391f8e42e1ad0ac93f107426452d9344e0ba8d4970a1d5631aece246e35851027673577818b6284c905ba159507f0a14eb8d3570051fbdf00cb6383214561cef8c928e8582b27dd8b059a750df9e568740cc5a9caa77b249b00a5ef0eeda6134507b1a9f19c6fe2709a5d49c5a5a484e549f1c2e1bf166cca16437112dea3696936f4490c6b6bd9a7d6b1f53b6c1bb6a52328113e2a86681678b5ad640d3452dc4d2232fd10e7d27235e876634d13528817a21cbf84aca3d91634276a31b1aa01c467141ed38562c47f516cdc6bdccfbeea2bd0fc8886911163575ab7a5ea555b8878c9a4c6f91639c0cc94764a3200990cc5614f77d165c09b21150ecf1b0d819b534ec0d25522bed28cb2d5618cf8220887d7ea420a90f906843d1068391eb3c5982666b03a236300dc604ce9618ad774a54ab55d17355d05bb3123c460c261f390b864f8f3ab0f206e8afa40615e5904f0d4719c0e9a628f8cb83fde4690b751e609939a519c17cee0f8c39939305f5e83ac9d220385a9ac4e72ecf9fc50696250d625481218c5f55130769c83d85c3dc83e0ac0b445ee7bb589238696dc2fc2a12f41dac991dc1b0693001ec0250e2a192aba0a93e0d1e77bcde246a915bff1bfbefc05a511fab98d425055073c3b8b337211d83564b8fbb93351ea3dfe77f70d67426070611f470965d9f3c21ba1255de19e07c3b90ff04f34aa5468ed81573eee83ef05b6e45461783df38691a17ad01ea8703050a007b89e350d920bc796702ad234965c6cd3f04e01ef5872a1cb3a070964ebc4f9fd810c80cfe2b1414aa63c21f0ce4dfa5a36a195cbb3497db7ba6cd162463601b170a3a103259114871224fc078f746941ab0b5060d155e9946684a95ccba37d4f9e9a8c8dd3a8ea8f08b4928bfc0ebac520ec199e5193cf524a533b6acfc83b38400af369c815d3831a941f38b8a86480999b97159df94cc4a8e3579a0a3603d78513c919d6df2d059167597a24633a00214a115fad22b16a03cb5780bacbd4023960c431fadd214ad3d63152a76ac98315147ae924775087c7332f85564a2dd6c4a0c9b8e5a4ea04b5f68271634900eed27170e78496eb2d2ccaf22148200de507de429bf94fa9cef36485a11c8f9e33ef447b309447b1d889c5836e06127a688e6575688e47778a5ee38e1664725ac2d243d0d4f2015e2d31b6f2e9c7a442f73f887e9f7ccfc19ffb1b10df047815a791ad1caf6a957cc892a397e63b9f1ec2427167f2370a7b51d877eb3e18b9481a6fa7278c348bd88bc99f5f07303cbcba09d06a8ccfcedb94c23e5632d63d683a9d72e973684828efceb0fc9040fb7568eb1aea4719d93feed0b02b54652efa696ef98d8560135e5c5324cf641da60e06f1b2e29bd04f97bb4810eab6d2a8c2a0d0664ec5e81e6e95000fe4e894d805fccf811503e6097d5169360cd9b3d8f266d77fc04cf7bc35a355534b1c543331c0d9d99704fdbcf8b7c17440a60f7842a5f62a731aca2dd8682d2ebecc492ff74fd040f09261e6c69eaea70a01cde5b53fce31a9bf2a7380e80713be8989b7bd0c69c3089b68218b570b026191948b6e5cba7feffe0707f8e4723542771dd54c2509a1f0ee916f2b43a52e06faa0edc396a17bc7a0981526f5c58e9a03221ace1ae4e302a33c47e33d72658a96e2591d0c63c10ec0879ff0b2545e446ccfc7e460398b672c077f9c60b5e937f3d0eab5ec8b9114a7f740614f40c59089d19b17e400f2ff59120c17c335be6ce36ce6d701142b8786cba3e5745ac796aa167eaa1ac9e855678c9f856a41e440b9b2fdc671c53c382e3edea8afd10bc5d7c4b679c2f0267e47447a60709f26d53eabc2d9c7413b2ac4795d92660f3edc8db2587814ebf653f337621b71d48a438a1a52f16083fa9446351b2445cae8f949085edcd8304d90a628ea2b17372823d75fa57554d68d3737abae054a55f13d79191f6f25973dd4c092d9dfdf6cacf932879804c50a373db106c09138778db4d70455fd3c7fc5a5032d8105cb40b0cb2bdb5cdfb18b36d0a026ed81d5963d5b7e1ce820c6845c128d7862835d123370867ffc444eae70d695ddb3b5db7ba4ae1463868cd922818098401ddfa7c4537c6bc5d3c5aefd8a8c569c85a3eea46548f71723c1efe67f517a007bfd3da97f04e3c04def39cfc20f781d7c8d0305a322c2820b4af80d13047f9af446dc5e6dd4503edd4c76b09a310f6ac3f281aa3f089237603f2bf13265fcb1f9f2a93e1f7ce57782bada895ebb5ab77edabcf047229910fa9460d6a7a3656df94dafe927496a84429b4da25d03a52f733cb16f962c0bf5536257113dba2621f962bb661f23415ec5cf22031d0dd8cc6498853425bcfb1545d3e3b6a7076a15fd4a4495aa94fdd94219f152ad12ab3546b355fd2469535c41547a4c05998892120ade3c5bba7ea2ec26473cc948d0117a372b0f6bc59a58ad8713413b5c7fd86b909551422537f53de655c9941f4fc800e3fd9f9b062ac3f5677758362623b7e2778b83a9e59182f9c23a5473320f638d2bb320ff329b1d1b828897753a8540c035c9bbfc001fc322c6324f98c2792f620e3f0315a20c7c11e58346b810527c706eb6057b38ba4d419ca2473cb42b69f80242f3092c2f84392e9bdba2c017176ad5206ab0a9990a58e0d02c6e5ea50edc02c1994721e626f4e06cd03f8f2db59f3cba2f40e1e95df322aaef720535fbf51362862556d3cf4cc3d0e2690d8892da54e52a37ac5aefb91c76eacfec7b69354d92bbb51249a111ca58dbd1600a0ce5f9fd86efc7e3766ec221a03fa36e2cb6de37ea4d076b225151e36f7a9536553a1b6428d43361591175b572eef8de9eae8d927f806e5465acd0e168898dba18199909a91c96b23613eb79d96c90371be42dd74bb36f06831c53975f8967831ceb1654641d1e293dc49b6c8b0e679dcee7ee52c1b644944e4f087dec8c5c5cd27997dbf37f7643940454c81847b5ad193572166434727c6aceeb3dca2a001898c691f0065774597567ee2b39bb8686fdd2887c627614cb5ce9df2e7f43f1bad50de049bb5d290df9544a956c290769a5ad94ac1ef1db1ee0714bec59c3ad99f8295952e30a61db3508cbe8d2a7292e7186cdeee52c9d2996bea163290dc11614bfa216d58ca3ce6853f1772f378caae8565cc14b7f7e3221808f8316417a1c84010fe113814acaa516651944fddea5221742ba0816eec085d41ad4b1e9fbfd671f83bcc47a9b78a19a9749cc22d7e4bf6e619a0d5e040614fdf752743480ab25a13bee50011ee1e035462a19223ef3c521276323e1efff70a9f37acc69c1ec18e1ba74e63e8ac56f1a2c5f6cc2287add9c11ef4f7e0c35247d46428c3b23b3211fd8df7154a8d1d09bd050bf6e85722c2217e84e9d75940cce86b4d6d62511ae6b2a9af7e752cb04113979bdf2003f6cb575eb9cdcd4d2e6b5c98768cd2aa2321dbb7b508dac3f1a9af362211298e54108ca5dbc7447388cc268a1010d2cbae40640fb937d65557927fcba4ce2388376d95b93ff7d988d34c69531c1ddc969af4227e0187a142b0817851ecb4a9decf7a69b04fcf9f5a7ed40a0e738c2b45a196dc678a60923cc20f444a10f77715632c74373c5c7c0566948802dbad78995ae372bb9909efb615cad2e1ef140f2c1a15afb616a17db3e64ef07f06776cab33b57362f2853102d758c2d25d051bc990470edf97c8869cb2886e860cc73596130e1905e82168088e54f1450e65a125abe5a7afd6489296711c9c3e35b7a0831a23dde5c787e2c101b9e66bc734c71d1551eff6c298f151c91a23ee4c78838e48012ea19c5627ca4adb5ef0b98307f0436677eb1d0ebd96fbd15ec7989144ebdd4d258221d8ebb8edddeac278a62954034386529c8e2f80d752a30c53e1e63d120ef23b079cc519b3e02916a50a37e1292618b47ac7b0e81983a9f200aa7773ac502ef48598e41fd8bb0982a2bf7907c903ad765aa752f2dbbad340c0ed2f2612495586698066b7211b95314dc6adba2f3fe6e48c829d19658a83ed2cca0abfe5881fcb352c2d5a4020b888b4a9e18fbdb449c2f40b32356e6fcd4c92ef744f3bc0f95cfdab2a54e454f63a41100ae67f753b39843252cd8ff221e9980942a75d966946a2b7a268ddf69153abdcd0ae66082bffb5399cb666b7535c67d4c2809530a39b51b1d7a761cefc9ba19200061de2bb429da1b1d7fce6f1b0b4cf2aae421137f3a79791aa41fa93c70f75ec8c327779c394cce6c0493c65590b3ab55ea40b7963006139a8404e3a3c47ec7ea41d274705647d198024fb1c874e650e67e77194f2a537aea28d96d1d5f4eafd7677d6f4ab031380773be4842d550445050784c1d910a8a45ca5e3ec817ea6beedceaa14ca0cfd923370e297114cb1054b2a82352f412c9ad5e8ecc59f846797f5bd6a16dec1eaf39cae3470dfbc371191852396875c2d33b8f143d8ae1778985012fd66973313d08cd319f2f8233029cf4b5dc1b6088d705ecc45f0a0879763108032ca54a99f7e9b6eb56158b924fca7414adf9dd00d91015c789d5c29f35e00d6ead7efa074a74fc0333fcc0c2bf4f3ba3d60afcc0a2b2d755472813dd4af55d5337c417132d75d7616657dca7efa7913f1d89c141f797a4e31300c647acf9a0446864a5f868a67fa87a90f2b73237adccf6f544a7b9308af27960173efadc1df4dc134199bfc7bb359fc37d4397ebae6404d8b272c6af0f9bc9459b5a2b0dae9bc93db1b330d57dcbf421f28402dfa08e0a456c64672f9e713fdfd46905e4faa09877b1863043136626327a5e742cc0b29093919f9159b5a383d2d8c515055b75563bd49b281ce9ac8675ea8a36e07a542903ad1e4239083ce369f778ffe63a0357c455449c397e135025874f6c099aada8d754363af018779f072f9b23b1dda55579e5d16efdceab115f03c054b34e588c6376d53048763f96b21e24c1515c8cb0a72c09637c85ac805b06287881050aea752e527594c75788707907af7a8b9c3925c888a08773c00c832a96df730c69c771420cd54dbc83e7fbb1069ba86ca625e279df695c3ea214f9ae1a8dd8414b8e9e42692df84fb5c3aa4de428c9a60e9f8efc38c27af1cd39e3284c3ac70f07701c66789bad3b075173c3ba994451c83e3761d8d6936f48e7c2aa6f05609a060525ac94bd73158793efbadd086e8e98c0808dffca1422537c70688c84525590021ec60876239673354931f60b5a8eae939821f69b9289c3fca093edcf7fd55b060e57b2b02cdef3e0236d5a2fde2f6cbd104a148c449d292b99cc1afbcb70f77c2524b97ed5a304db10a1eddfe935048e8449fdb2210bf87dd69bf5a63a0eb53d533062bfccfba79dfce0f222b6a1ceffc617fef2880334f1f3241a09223ee3012e32b1f6ffcef1ad568ece0fce3569f6cf9696d1078d01a3bedc4bf7420243b703faeb87528160e5462b17075db83f53bacead30442bd2fb149b77ff816e4fd3e96674c085e5d790f160371aa1be38cab229d7a10822e15e32d2c476e2e8e832507876ba2f4e0436b4bc9e79b5896deace208f21239b7e67dc653ebda0d107ad8f0a3132e0753411e1a073c030da578a1447cc8ec212447c5d8256c405b2f1755d6103c175b6469bbd0bd32d0dd7c7e17c24216cb752c337bd9a13b67ba181009a72d5d20651b121769d457e470e751a572575c4a5297f9d60cad0b4f408e6c1487e2ddc1f31aa24729fd68a7cdfb9ad269d1caf818a6b11023b7adbfe05e225f468d58ee4600c11ea40fa1220818396de33d24bbde5d09a340c8e1c276230a74cde00e218c81335fe25fc6aec38bc0a27a539196226527ae8530be67cfeeb8c365c7f5c2685005ee3a95208e0e25aa6ec15358a4cbc7ec17d337b1a893f30e9c85286eff545e9c40afe815abaa788e4c3fd11dd917fecbd69813b2a00d991ef52883f17f79161e5617f346868a67a9e53a7dc8eb5df303b22d1b149f1af2aee0344228e46628787d2cbaf699499907c5eda0019664a133cd27b9268e41ffb94cabec9e1fea5a3d603f968a6573783fc6564ea4528db127670e01717212443b50da4e37b5de4511acc403ca60282ef5636b816436cb607d4f2dda2688b0f9dc2cebd3521c206936ba8e69e3732151e5d331bbbf75d029f8a9113da7cbff4988ddc29292afbcc3fcd3c733c480bae4b0432aca9923a295df83f14941570ce9d579b083f0c0b6cb98c7a3d0da7e012e9d7cba9d333cd794c82c04716794a43a4ffb11efb93283a85e903cc9a8f6f547313b56922b5e0613b4a480a0e4ef27bf2432844fd058504b424ffea5fa7f4bc10b9ab909d14edbb999981c3b71e854295745979465e068a06af28fe6c21fbf6bc35b5f6473b076e5e203a57b3fd6be2ef0dd22d9697146a4aad422c9f6d6134852ad8464f32331f8db8dc0dce839f93648f6e7621e775064cc7adf394033b78751388d7d1739cc13accb3f87b03a8b15ced82bbd6339485f20a6b8a3eb629ebc32a96341e52643cb456a41308c8691df54e358b41f1becc6b4c1c94d979253b382ac2a32f3529539acdbc950694b01039446228995b9db4d888221ef6847e05714fe892a99595eba13f599fa2bd26fe7709e15cc51f3c5460cb3ea68a01fa4be64cc627f04c520384dcc7f3d489544231e8e1b93ea12d537ef1acd010ed3fa880f6bcd0cba26e5b44ba6d8644a718a48d914a6b5984754a6a4782723cbde442e37f563a7f48ea0e96acba7aa4a880010f01c258488f6faeda1ea47f0cd196d619e2432f1241b82b77b6839b81c6518092e41e1a8ae0e403843d02151c3ba5c80085931a93786704cad33942350b1d0670109ec03bd5551cc9767ecff7dc055716965db6cbfbd0fc10d094241b98680ba2c305271b0a27bc0569dbf733ac98a20d9e7e42835bc4da05603b3aa5dcffdbea0ccf0eadcfd7abf0e0cf42795f5d2f6c2546f0a2c12141fe19775cdb1c8b758d59cb764e011f3d0e365610fef3b3effa7755c34b854905e1b1c6d98389138ea56e2244d779eea56b124030edcebbd197ebdf145a4f4bd819630236cfd5a0b2e7779770a981d2076040329b23291379630683dbda37b4c88df4393ef27dbadd1affdf0ce7ec9a088c49799c3080b200429570430e4cc358bb22b0aa96549e5b37d1fdad1af4b473c673ae30dba5d83da653212bd82c24fae307c5147a15817498b2cbf5ccc8501c329df3728142032c8240687f471ce4f5b0a7652402bcd363c27302c0f0e71e34300cd0d3c3f7c52086cbef27305919cecd3a8e63604bde1683dc8366ae0800a90c9334edf58d44d5fca73aefa22222434d0e54b42322d0ff4b7cb349633fca6bf657f5d447ad8d88e2f69625bfbfcbdadfdd16a9f84bba1fb1f2eeee378bf75f823793ed1524003ec4981571149e14f0002c9540b82315617416d3d3c753ffcbf16d1201c6dd4e2aee044f56de03ad5a1a323a16f98207703b620d7a2e833dc71b2a3e8d85966c69fb00d5c2ea9f85970ee7360bc2f72141d3bc375478deac24f500779006e7e9b68493b2fe7ccc8c1f68503f11c0901a75f0b535282a8924ad306f1029740e205c80f9a061a21d572216702abe1d10105d1704adad3d96b09722182c25952b48a963c6bdb522da53030008409d644607eac373c126a3cc0868500b41d2c5136fb147ee86e9e18504f803b3df5bd74944b527c581c003eaebff36bdb9e029e4b78aeaba9830a486a4c23464f5de075f504aa749444902e62be651a8c8c70ee5d53e0a24181c7b9f8740863d0018c0212a8c034e4e538706be532bf0988013a4390a076ae87662af24a649a94a4099086cba361288bffa6a5196f2dd2150a0f8c15da5cc36cd8a4b68482785cacace7330f1bf310f4e2a5c09daad65e3c9e8a8a07d80702724aa59fdf66583b4546374bd533b714dfa5d325d24b18af6a09516686cf9a24585ba5334c7b0c894290e6b998a55065a701e32eeff77a477c5c39f9d9d18284b3cd7d0431e068006ba39c21faadcb0a6c1979bfb65839d4fc33730e8694259161b9f520d094032204c61766bce3edc00948d5a06c21e6c4d1ce55660dc630f2bf27aee3468404222d9744a9ba8b915b1c16505fe704135e1b661e6b45f4706f68049a798cf6972dc068671b6d68d9a11ee5e6a2b2da6a2d8195710413544c74abab9b2d47ea0db00ed566262cbefecc40bdf40fdf81c874f933c3b5147d088611c8518e97daeb8d5e98cee6e2da9147befcfdb6d5da8696f34e86e2e425ccdf3b25445bc808ae796f34693bdf98ae2fc55dd92c9adb4994785ddf52001d0f781ad170b33fd871a286c261d77eb1f1f5f9eb4ecd80a4d7e6b2621c16e9c1e0d1f888f0ae37be9c15fb8c1f7e25f1b5707119b0c715c50814145cc3f0a5cf85b98d59d79cbc6b3621d7db2735af2d6b7912722aaabe5c5abff19eac601261663d4f1d461d5a09aa3c069e3985e07870de7f13812e885bb062857219d3196b12ead18921612ac9d729da6f3944bc847b82a92e645e4be94c0c73191de9fe0730d835a68487fae920ea71172263ec0f299c5ecc67dc45575c681d871a0c1d6c59ceae77bb0c9812fd33e9060eb7639934a9e3c8be70590c24e853d73a0aaf9cc8cc2cbab38374859f807dd50509b01b0317f75185aa08ca6b86df69e2dfbc46e0a76483698d650ef3c3d2ea2e6c6d69cda49a2e07d84e5e0f3e4828ff5d2d4b3ed42039e554f702649ab0dca9c223d8c35df2b4e89fcf27cc67e6ee598ffee9f2341e99cc07433eee4db3e56a44c639baef00a6a84850ecf0aa783ffce73d0ce7c06c061299f929d02151e9f8d63d26bbf2c7f542165ef19e876e37f0d8d1f167c531c95e5c9285847b5d0b3894d75d6a378859b9f2596139b824ad6d5add27ff539969c92a3d24fdc1481113d7627a90f9c6bd2591703c0ecd1f15ab631e5a53b15848ea0b3d087584e3551a4ea58d0b08eff90df50fa6ca5560762fed23130aea97fea5be9885c00d2e7a1dd34e0ccc882f75d85d8a664556e56269b18365ad5e6091450fd3e43aadcd4c0d8af4e459c01f47badf539a0699777e7ab802e31788bb3b08ab97c8450f05a50f977969a276dc31c63ba0eb91fe3558327225967f0293f05054bc0ab1c4c92669941e372c0d5662697433351ffcdf2d1317476aedc296a72aa496645c5cb08eaf81e05cee8aa3f4df8b1a6cb0023fb35f77b4d9a6cf2e72221585c11b027b27b87a0bcae4ba87337f5a5604d0b4d5cbff6c5a1db325ed3fb143eae6ce194954a64e3c0f33adc2848cc3c4e212031bb95526dfb1422f5257b49c95c5f93cf923aef5e0cf09aab1487f9ce742139f99601efeb696112a7417f301c390cfa82be550aba7727f303a93bab1d73f2341a920fc89721aad8e9fad099ae10afd624ac55141a58d5d8011157b5ba92ad56807a4b93d88c9af9230ecaef20734cfb67214bf2181fdbb2406f46c4f159fe2d6bb1a1912bec2a6f2ddd6858ada309b65ee30ff7757e5459d35862a5ccea086320c1fa80fed0a1ca75f5eb57f299d449bfcd3067f4a9655f25db0f35f10040ca00e73338006f1842ec1c26cd279866f0f64aaadc8f30b3abe01a585e8202ba2c5f0a14b8c410b36c6273026f8429ffe97a53e7ef3c3fda92c42bc698e09089d97d8e35dbb5095883c7ff0cb55bdc83b14e54f40628f9eb06ea94d21d2c8b686a3ac031967c845e4eceedf7fa2fb40a8e4fcf48c98bc40440f239494ccb3859ead4685e6802819812fbc2c99d5e1689d09fe77d08c8c1990b39e1cad52685dabbb5c5aad63d3386cab88fd6b90bb7bac587c4fabb1f6719db715a8776e48d36a58f07041cf2e024384dc3e9e8dc2f8a3c306d955a9897fd7c11e68dfb3d85b823dd074cc627f239cd31e6a17c7c1b74be7eaf265d718e35baf5dd4069d2049083b396828c6cbb830f7167da17be2528b4f45612aae4d55fa1d1107807647a658d4896d6159237d6a4276569509c4ef3b525d03b5e46ccc3b639db21314860f5c7206e37a840f98802284892209ce430ccef53b2c56143172bfaf0b1a68e808908585ea5d53c25c2072e27e1bf64b145cc46bcb4e43a43184bf8dfd98c443f8539c932024da3f704d874d0a872b81cfd70009be24367598a659a33fe58963af77af1b47bbe4e8de703dcab2a8e968c15eacb08d08c2d860d32f3b7174b418062c8a8f6e5ca4fe2367a175f21051733fad91ea7c19b1f324db54b5dec80b3e7b23eb84ab71088177dfc4ecf5730e78f8406c928d0085ffe06e8447cfdd832cd1078f0d19b8f608344c789908fe2c2ad5bb609b0af29b87f8713004bbc07d090f25fa6ba75cdcf490712520dd9ec99f6d7ae79b1a4ab54e69fb61003c8a58dae8ed3366c841e1dd0a08fdfe47423538e1a32c62d4d5f03da59b6a11d7f6dbacf956e9b50792ce6fae8ff9204a26bcab53d87d36fab885031d8df8ebbd8107e4cfa1cc3a4e615bea2a2e6d0ec503c326480620847e10edd83883a4e16b064e2e2dda1d420d37dca596f989fe058e7a9a16c49dfe4ab02c109b2f1f6c36926c178dd728d87a41be66de0e1c0c1095e90aec3e802a0cbdee2d59b0207409493c1a96694ec15d359ac8e2e032859d4319e41553758286368dc06153fc5380f9c95731e9669bee586e0a2a415cd5496ea211593216aaa70d8a0942a968668e08319335cf4512a430e4c75c185cb294787f81088ef350e00b407dbf77415d493b2e1a2ed429a68596523c186152ed276fb92f010b9f4e788c82401954a9040ac74f989172095c3a8836a1d93be7f2f0c367dac2753d47bd1ea63289b2a528e3b8bdc80a277ee4f6d58c4c8a99de4521761e6c97478edb0ae8db06295231b3c37375fb471c565a301f1ca77083a69b4755ce67282fe639aef7db2e7edbf1293a4f2f8578b289c0bf931c37954d826ecc7e1df63fadd8a6f25851739963f120cb28890c0cbc56086285f8368c8b646bdbd4ba4ccd6ffa0d13394c2451f4e04181ba2be68da784bcb2d50a2353001438ca235407d6a9201e1a9887be296d44d26363d70eb7643ed4f7920f956e6950e869401210c2789b9eb3353f4e2f67e1069ce516a2cda111bc9b3ea9b60af809d932c5622103ede20f634ed0dd6db3be47f38856277a74c457da030357313f218e1ee4becb38747b3d1fa4135520280a7e5df2b34d43695179a485820116559b8209c953eaa7d07b9136e597d5ced59be0d83d6afed47eca031c157f220d6febceca633a7973ec322752988fb6db084bf36e0a025f9aef5d24beaeda03cb9954299450f3e7e1d04592f1957f5740ce940ddeeadbf114ec54ff569baabb2d12169fb8a852315c68f0d7f40f983534c623ac3ce0bb9262d40e4ad120f61b122f686e2851c10d59c59a2bd485ff1dd872b172bd736f974861b3e842ea994792b05a3b70240f56efda2e686774f4640a694630ba149a5d8b5daa3eeafe985ed999faa9880315ef5ba15b90880596bd7395b1a9d7f3e1f428f6feef9ff605e9abf69e48d452b1c18a1b73d0b8cf0cc6b2cf267f9fc7fb0d7e4110a6c8f656a68d1b713b40d8606f5057029b6cecf9949df2991e800450a02ee229700fd93adf38ea44c793cd2929247b2f92e2f4d0136f930313d521b6fa3185a6b2e12cdab53d6eda9febde50a1ebe92278b8a0326cf9efaa2047bbbc72e14438c3af0377b2cbd5b36e9da69624e8a146f8add316a467f1079d83a3d44ac0868cfe1375bcb588fa46658c28bd81569173dc46c7e28a9ef1ab6dd00c521e83a029823e779f14ef818d6b45ae6adf3cf04401d5a8c52345fa29cb8b1706d3c14c81598e9db1f2dfc90cd237673a9e02e59a3211150e25b5bad75a1f30b17637f6e38b23de6af1158ee2e3a317c67090cad7f3313c3aa2c1516dfa556573a2e04c64598809ea67bb54050a21ea6440e31c6cd028facd78147a2d7fb01fe05dda560dad74b1128aaacc875b1a9d37e3811e53401459ce997b10888e551c8cb88a1d04919d645d1dc6433a7cd18847bf551ab68679f5542957993bf2ab8bca4313b19d8a4860b54b2b93e68274d79fdebb9d9e32e35bae079eaaafeb05425a5071ad77c47cca4fd9ca4d4c72f0c6ca4f2765d452c930a230e40aa97a42a2acb8492c02226417715209034bb4092dafb1f206502adf63c02f08df51f83083f131befad2138b60469f3ad3510e2864bd5643a2427bc3f783c844ddbacebd73427430c00251b5c9ead75239454626d7db4addbf0e136acb28201aaf3f8731448ad59c262576c82659e8676a5107bab09cf0da6b0a536280387546f6a0526132e0db582286f9a83076322608f4d8bec94546b203fc0581f94379dcdeb42a9384eef8708861c503285024a728b132ecf7b6ae47cd1e866322c42f1e08b037a49c30a8bfc1f7af4ed87aef77ca050825d36c3391d4794dab2179271b3975ce6098c54069c74fa35814246a943e13024e84b1a1b68e1f422b109cab7b29af34b189be4b0be2649052d701ed7440d32a9e59b339eeb8ac2ec4cbfcc1a2564af823d17cc482af8d7097382459cd14678cbc9c2d51fba31d8d57983c6161949d5d66ed439389813a9d6ad1aea06a2ca7d1d637f5db0e22b825324ccc59dece9c5d752e1257a6634ba8ff0de94dc7df0f05be5ec3bf940d458c3ad56dcfa1110fd2d508c651d9db6d45aaaa7662a292258cfb277d40ca46cb44cf93e10a07ee2d66babe552365762b0b419213679ca101a7b0824d9890423878b5f8c94b65a8e3dbf7e755ae092f7e4b74bae28221197a333c937a203856f15c8037d61730f3519d856a339d001dc311d08fdc83a8f0fb170df6e5951c8061b74594ad238d2161b3ccd71df512c19b493c9c71e66d2e1412e55486b6fabd6a5c9577b352851a4f363230e0547e6c4d59597af8167349cd8e05dab48146b3334609c70dd6debaaf53cfa3cd5232de0003dcc53ce31e12a8ea74366d8c16f1326c886ac69664386d1d0868c64561bb2e22be481127e3a14c6569ea92a1f32af52be0217d52f4661b8465f0841cdd16e611b5482f2d6d2f7e4d00caa1b0168a501db551084af4b9f372c10b741f4a2cc7b52a5636fcbf134a6d1fc2984a0881e536fc2cf90bfa576001a398f7037f1c522721b6444c206b4f74e24732b8018ca4982065591046ad21b61738263c837bd4f307e27a82c04f1f9c941c47151ac8267a8023dfc452cc92e8e61f5034b5ccb5c310a402b910bcc16b9515bf0cc423c482ca8985d7d82f582bd192d64c8c3f36c61ec98d1400dd1d8f2004e25f2a56690bb30324445fcd41e03e6d2bded16b5e90476c66df8bcad67945827d89165513ccfe637a2f2f6ec5f6689f39b3d4517459f4b2ea114267dcbaf8e78a622101ae0a88a939f19dadd7a4807d32c3210df383a25bebaa2583e957792a2e4fcda564e86b7ed3979a808f2bb0f28694f478def40346988dbbfaeedf94439305c3e8e9642b2b6bbefc4e63b58805783ad9b580555f6cc5fc87c31c83caa7adc63a79c9253064dae4b2c3264b13be455ba3adc91352f32b2554d4418f94689ab9ffa3397536cc50f284e80833ccc6b0b1329ef2ad0384bf3e7e1fbaf3dfcfddb157f745b4e0a9357803e7ebae1cb932b8c1dedeaae5d460317f03da0a9389fac945ead1d36e691b5e689870d615bc3224e35e601f7abd7a9b34319995bc8a4f1cacb7bf0843bbc395eb751697a96b34478831b45e3ba6c70e747940d397170d4885850371ee779119ebca1f777d66d8acce3da7d9c700c62de872b3dbd11084b6b0fd3f28701d9e85969fa743c2577fc55486793455b8f83b3200cf38bd5f767ab931bcd3f2bf62c9207e214c5fa31ef4f712f53cc057e84d3bcbae733fa635a47cf593e56237fe9843faa980af2c6df976daa3f6c731f761f985203597cee09b7ce86b0ec42a98d2a01d41e935a9469c257e80a318bac2edfeaa1fe314905f4a8030ad10a925820d03a4e070f470cf6441978ce0061291d62cd0b043060d28d7c9ef31c7c60edb7977f581038dbfa6954361b179d4f83602e40bbee081b180254438fac6147c6685a1eec214f2e64bd1385dea390b6f7de5bca2da54c52065a0910091809dafffae7057ba0a3152978d2f17316502c63dc31022cf4fcf9f3044b628440cf3356f204bd5525ccf49c7fc14043cfc74a2c8561869e33ce982043cf3792421217607057de4d2ede82a787ddf18e2f58a5439f3aaae36f1948e185f9fa3e86f94abdc35a2b772eb4ff9eaf70ee7890241208888118d6bfe1b5370cc33338443776905f4869613114f249eed7df5156b5b87fa1eb09e2a4a82df7983059b1f624f940954739af899e2f2593c874fdf6e004c58a08bac061cd125794c01c808828485b867802c10be0d0a2cc60a71e39a5a69a7a45133c441a4429a3b360d8a0fd7f9c56bf6a3af304573569e9862d87a6898cde5166b96842c38d26186c4d5edc702f873b8ca3e35213154d4be81d6a317a01a9eb47262db1addb053205f6af4b38cfba735927d35b6dd56ab5a17eadb022307d49209ac6dd739cc194220345db1813c66a1923c6d620368c024ec88438020a24941003b45b5f860842fbff17233070822a6040c41059408183ac878e4b5ee2f0420649d232d39b9ce9086f67d25ffc15c500f98f6f97e9ed21c192a04ee5cbbcc888c98f1c698c5f29e34a4f30df914f65e62bbafca23133e6f4c4c43446ba7b931b35791193c7664e57c62895c1227f32bda21b311dcd8c64b248e9e94a19a431464d723b9374a632364a17599fd0f6376fbfed87ac967f2938249fb4cc5bfacfdcd9d1522f49f1e5de7ce08e59f4f72ad5c7ef5adf0153f050d3f11beff87d6b4e928d78a8ed683b9280107f77b071f4dc9f9e5fe36bf533e75e0e30e6915739c69d379282bb76b4f634fa483d59b00220acd0f3e9cef71c8031eecc25f827f729d950b4678fa0fe533dce4f90be056f1eb29a67be0a1cb29a6e19a6fe84b81f926b74f733c78fdf446d03de14f84d621607e3a1fc2dc3d4f767f6314356df21abefa76ea064fef609cd9761ea1b99bc286bba8c316dced8620415359841e5061b8517322eb6a0220d1a65d8acb10110a110372842aac19a2f40c052b003154c4ba801476cca86456c08560d811735a67b09b3c38e5b354b6d7704972d0cb6d171c98b1448e0948e4b5e96d01b6b7aad911492ecc0c20b117a877426bbf152f1d5bab2b5c3be674f65bd0c6183a75af2705679b8059ea23916e0abe392971cf4f628d0ed8109f154dea6e3929798de2c59181a23adb5d2e9458f8773b624a5d3526badf5c6b09032a3153f2d2b0fc368697475338bc5fa11b23c2f58c5c298b6c6e86274554dbb744a3136d9762d32ed2ea34ed4893ac9e6ebde1966726ad27e2fbeff6188449d9034a4f9baf70a56d21ec3a2fdb5d87c59ab55a7f9b2b656d97cd93bab48f36535a71516ac64eb953b069759694fd7e93a5da7eb74efdd2ed26f17090929cae2dcf20ef71683ddd88ddd98939393934c2693215d24a4b7b1f9da6e2ca61f48cbfbd26d8b79e8aeb8c95254743e72884c2c10b3e80da58a26318bf66de72091399ff634e7734e9f5e355da766fda6afb69a553b58f5138beda6af05b52119d7acd1feba067937bd5ae5cc9a5fff4ed3a6e7fd646e4f42fbdf2f683e12775342bbfd905489744589ce26a731655c36639251cb53acc86842b1f2fb40cf6ccdfb7bce2f0b2cefbdf7d6392916d98ca922d54b29a573de8f1ede2b2fecde278fcddbe434a68c53199d51264ae9f7b8f75e2a69934d518c59b66b8379acc0df09493861dbaecfa6ddf952bbfe99da6e322975310e4606587e1ae9cdd975f2fb1e77d118a2502f3dfc7ddd36a5f7f1fbd4cbcce4fbef53a8efb70fe565cafaab9970c4ff0f6f63f31f186103b2fe8d00a38bacbd71de6315f7def7cde9cdf73c4f6219f1dce93c6efbb48f0ec69d5c8cbb521f7f686792fe05f4260fdd994ae04c5a496f67aa3680b153805609b44bd8272c162bb35aeccc72f190c943b75fc034d92b6396b0e14a1057b4ffc6244517598f557152eab436d1669cf3c9c74e937ac803db46bf20ef7ba6acf6765e6ae3e2b7501f5f7edfa7c01f1e3aea0b3dec68b4823b9697c5748f9187fb1f8fdcf6940be232f510b4825ffbae604d6a59e5a115fcf383223018fa4e14acace3e107456047b915d345d64639a181b793f1f7320ec69dc464307a3b19ed5f67dacc070e954b65f2d0ebcc43ff50092cdfc9b84b8bbbb4ffa277f53217a987d3e23bfe5c98be34817112f31c9970a6399987fea933b0fccdc990b4bf87345f28997745bba7a4dd9f93cd172a76f1eac3964cfb7bb1f9fa9c9cbc2227cfc8e948fb774ef3f5c9e68eff1da3fda37f27f3bf33cca4fd3ba4f9faee15ac8445fb7bb2d87c795e93c774a4dd48bbd715c91c2543c5260cd66a85e1ff44c5262a365131fa439d9c9c9c6432990c090909e98b7db12ff6c5b47f03de73f29c3ca7e939698f15e584bf4c5adfddbe7f434117356d8d61f71b1187fa328d3c5dc701bdbd0cbb8edbb64c2b783d1f45fe74a4e34f305525c678efbdf78787f12fbdf56af7e66d6d0fd5d5a1de1ad6743aade6394a9f30f22891e608944cd39433d33891a68d1b3520d0e166166046044f2f400204559238401b4a3138c81c2131454d951b354c50c9824b136d5861a3045bce6862cc0c62d006136fa49122a5258d16a8fac3ea572aa59553c705cdd30d4ac8d65041c59738c6a0c2ca137144d902884bc4aafa0045972b96e0a1862fac8040031770c18229045fc2a0a1e2d184d1be54a7a8c01b812d63c2b18ac3871b9b3360c0f183ea0d1bb8c301850d1c59c0218607471a374c9d84c30a4aa281061642e440022baed0208a119b128233669c40093364a05283538d5677d5ea02c6c700a30b1b2b27d419ae32c325470c66b07d2146cc8c0b18333801105078c1218d356f446941980d099bda039a0f785f6e1c2af080ce35d674c1c28a33639af0024a15613851023262d4d00414606e1c62b82a258e373ce96e8da49064f5ab1b1d564ea915442d2d1eba2395193d30219ecaf3424b14676eb089801b656851461955886164c21561a401c6165600610232506cac2a0f0ec576a6053a54cb8f91948e4b5d54f0c4024d62a0f40194d22942162c7fb7f40fd64f6bad55516aa7101ded4b4b3f7e4ab817541d4e357b39cf7aa8b75f90951d71e5e18bfa68a5e88176fc8f8351bf6491cb1377a96268f3f1a34d0c5ad56159e18f568fbf767c7c181ae0ae1c1fff01eebaf9f831b84bf5f19bb80be7e35b400806b1aa090ee8610ba8c0eb3db901a94d0674ea6568833fdae054ea05df417d81bdfb791e0bb0aabeb081307c411608ac4375802f7c3c60d6fc8fe1fd7861b0f7de7d2f0c1ebe065f70c17b4154f9b64537bd68a5e881b88f8feb476b7fbbf6a3be4f9caad081ae915ba95a80e7df7fe215e0148286d90cf407da2d8ac270427ba006060d5a98d14afe2e79bbc26ba01525790609a4e42b6865499ec181943c05adac906798404a66de7e45c74f9241f040d14df0bc7d091d3de6a1db4051c14160818d008eb31dc0c889a46ba703699b9ac6e5ed7f5fe6edef6fb7b9e948026f07e32e035d8b87977b47e32ed7f51d8cbf3a592de3ae222cd4c88bde3e4657544c0b35cce8ed66b45043a6f78c4d1b749d5174dd12c7137552d1f52f3741c9715ccc3ef3b0fa875ef0762ebad61c24fe928bbbb4afef5fbcc95df6ef77f153019c010a90e759fb9eb3f2650f24e1245cdf7bff9ff94402f265b6e2fdcff4c024cb7150f4e6b87750d3f2b79550b483da6f2093a10cd821abad7d3d6dcc5d5ee4477ea5e6fa29f0c61d7f1488e38e073a185d1fa41a0caebfaa1febdffa3a96f044a2375ecaeeb060c85261cf53e968698385458c7f29e512a64b0890c082044c18e0065684f8c01645557801aa24404117346cf980374bcbd8d272e98c10da4d034519377429cd174f5e687f8a552afa29f898277cd0c19a1f288185b84b3e8c15fa8b02fd78a3072bf4d21b66dc484f45b3ca1535cdb981194fc5351d89da498c74e8c990d5fe5286a93d6ffa50a674d2f9778e81c0df122633cf9f40600ac5e50addc7958e50709c519942ebe838a382a4af87573aab4b3f29c170e80250344bfe0f2dc19f1fb549ef04736e8c3feb103a7e747bed3b2731c6af31339179c8e6cdfdcc4372cd548132994f33d5f170d3d7b28e06640a6c9f32a1afcdd79e097d4bb92dcf3c64f5cabaf64ce84b7f979321abe7123af3ede105d8e59c32473ed552c6d471c04117589a80066f8a753cbc3947d5f287f9bf7fb8e58997c5d331c650b2e2f8183b392b65b98bdb3e7aef2d8129dd401ac3aedbba958e6aaee08da5285780b1850e67d020c011465da678230b188c918226b4287a5423cc701d1724f0b84801126ab248810eab6ef52b379a564e9c957ba7b8d14523811b4b493abcc29f8e4b6eb040ef906905115c451e475a451fa7de6a157d9c6eaa95906b51cb2f484aa1f9f32f93f9dacdd1ce396bfd822aadde25338537eba41b26f098b4263cac6af5c0a2745b0d2a38214595347ec892c31917a4a011031dbe8461021a82f88e5855a41e99755c72a3c8cc13140048026f4b291d97be60f15106c7f7745cfab2441c68663ddc60d6c31a65cc7a689acd7ac07265d64395590f51663dd83093626626458d9994a69994d94c8a1533292b9849499ae9c0349372058592728a60524aa92a877ed59bfef198d7744ee43bb1177ba81afa11863aee1a62f1fc8c5c48e1a42b381423cff65ac83337d6f2d659591bedb7d72cddb4fa3f5e87e8365073b2fd93a9b7cca4fed5b4cec7aa7bad0c536b79d73c64b50a5c3efd4ede8f9167ebec094470fa9e408c3c92462da5bce04a65b19cc0508c3c3c9e877ea3b5916f2398538bd095081dbf9aeb0874ad72b504ab08741dc282f59dc87f327590cc43566726f46f7d19661eb2324c33548cfcfdc3d90f487adf4d312b94cd7c50f22be7474d295d22bf66a7fb48d4324c2d9f06aafa61068dcf6babef4ae9cff92ca4ccc0f2672ba506963f5bb0b0f5e1eaf1ea524a351f3bec4969e4ae08cdb80d1ec5dd5d8c3badd3dddd9f05adfdb7247d4787216807c1a97b6de2d3571d88a2e9bb7f6a0d37e33e72c0f23d07899cf16e51722aba0a4a0d8e3c5349042fefe3bf4fef65f5fec7b5098a00059eda7373ce79bf9343f293461b28a95c0f05d8bb964aa3cf04586584b1a4e23b958dcfeb988b6920a5b660ad1ed8184b460b24289346068b0f1309a992294208171a818c9289f2832cc248a88823c76bb42184101157ba355baab0b618d1d2d4a3c197d62081f6a77e2fa5d2094d9750a3e91b71d7d5f45348e2c20b346a4a29a519fb441986472ff4fe1144fb5facc692f65f19a1fddf9764c0a44614eddea2f2dec614cf35a63b6da9717efddafda9d31be3a512cf977c98874e427a3c9c9f52ea49925ecd2adff157c92b6230d148e948430d182be44c97e30b8a859a996a02b1b53c89fdae745c22830a324ce0441759db46e90334b88cbb3a982ffbd3cdb85359e960d58c8baf74669e4f391f470a2c1f4709cbc7c982e577c0fefd0e24d5ef80f6db772004a6ba0321b0ebf864c4745c1aa3cc4bc7a531a2d0b2cc7cc9d92cf2cc2f81091c3d3c42fab0f52367393487e69c0178cfbd0bde734f9fcb3324d1ec02f75d9e81cb39b4fb193cefb9870274bd19674869cea829010c8c6000259604126a38f104155888019a506ee8218d152c29cd5c60051010142c81822cbc60e28b2320960f587bfbd5eb4008ace958020a300b120d2c5f7e3903d07e7b17b4df720efd0854b30bf66fcea13906200b3895f1d87c2f9a2fcbcc974ff32da88135c0f26d94a6774a9dfc8b29fd5062d54d4bc7ef71a655e386610bfe600fe74b16aa22d0cf7d99ec47f6137de22cd3b30496bf7770aaea86ca48e79c6428bd59aad2c05286dad3ac812aacbad93ee59d2fe3ef39b34499663267adb55aa1239dd1eb2229ad9abd1bd7a540d3760eb07d4854da1ed9db513de9474d9b5fd0dc26cd94bafec008efa3c2deab7d484094d761c18407e2c41dfa1d48b55a2bce7c1c3dbf43a5eec65933342502362777330c6400a8676a73540d5229faf8f8f8f8449f0b024e7cdd596d2e09276469daa2e3efc0228fff8f9ba0c455a99aa8886887c520f175dfb33cf21d8f5768bff2e88322bc285f51ee6c498403e44b1ec91dfff87208f99245534a1268bfeed193b07c58962aec872466b99452242f57cf145b9cde3974fc707a7cc9a3994d60016c5aed6103702013a200b105b03c9af248d54453242a0d14a3165ee818c3c8a229ad42d397300d5667d462e4a1124a964a9aa3b848737dacc1dbd34b5d7000400d38fe9547a6981a036c755c1a438935c6954b63bc38cc1abd574f17a76ec7d91b2dff5e57b560647ea85fca4c7bc1d0278d29caf4e2e43b515f2f637c27ea8a6f4be6175f0f7b31a3ad79958c640be619f6c5e8de2dccd1d17df7ed15f405cbdc89fabe7f41374769e37ff50d63642d07b21e7bf7e255d57109cc18bd5fe65191d9195f2ddf99b1e02ecd3b2ddfa7e0d043d984a56d6679d8748591969b9a2b74b820c09e8e4b56a8d1f2b51f30a7e39215663817584183788f80719a3b516b17d4f11df9958ae003f678f1155c68aae3121820f45ed129a4ebf88f1e774530465588f223488bdc888c2eb24ee001ffffff4a156ffdaddbde73e4781c7048ea1c9d0d7a7a4ca22963a69b6378f880f7535dc1f953f7c0721c070c0ae7998e668e929b5ffdeaa6de804e82c21b473dcedf645645e526f9c5c39aa38732bc5f4339d25df45105ded3e8042938e7b5aa71e6ebe7cefc09c2cdaf6e7e957384a8bec931f2e4448df3db8f74ba1b5007549d20027c73138493551ee264549338a6fa6801a6bfa79151cecf9f54dc75f3f3e7d1ea77bc24d20cfd2595e6cec4517ad60d381d8babfea6db015224d46fa9e45f908ef76f2868c70b49bdcae1c1c10b499df33fee47ee8a3bc01f0fe74b247984bb76fc7c79c55d3a7ebe944fb88b839f3c1e498e0a9989c6c1015921073c563b401828a4e6d714c7ab389287f371b21fbd2a6fefc70b2cf047ab0726043492c23db259cb3f3ca4a0d7827da8e32e241d55bac8eaee7cd990443ff5d13506933acc7514d45e769a77bd97e0d4184c427d77352d9f7027bf2393071c787ba2bfafe0f6b65401f554a39f0af62df800b0011fd8c4d3d7fb54a81986fb50f44d1534c72095c8345a03370251300713bede97c0ad0bf5860cdedf8d3c1e0c30ad4a5ef0507ad3eb07bac8da93e620b19a3ed51f084a40039629aaf00014c3f5a3ded6d071f601244d6591bb6addb2280619658646d7298ba4a7699e4a180b8e1ef67848bfe521fdcdb23a917e4825177d7074ccf1ef893e724be4a95fdf845886ae61e85abdd0f5f7102d6791a7fe0d1b78fe963369c35dc21d8ba63f5ff6c46c0aea51fc15231997b359e6883155a8e86a83ae9b34732fb7619aea38e56cce970748b1588c0630ee1a23cdb82b7641a3a4ab129aa9a4d4869c7d485e3421456fe9a4ebc7275d6bfd29e9c4e2ae3d9ba692bb6297345874fdf984bbea11ba96602a514aff88d4d4a38f0e2c9a72a015ef5347e0ddf30077755f1fe62fefeb1bf11e77b9ecd78f61bebe2c8be64e0583ea49f5a4ee45619c8bbaedef5fddb9379bdc1567344cd1f5271877e9d060cef6dd472d8beadf5b6f6880f76cd25506e66c59c8b5742a1e826ee461fd00ac81bfcaeacf59fda9547f62a93f9b6c7d7965be2417dfa9ffe582c1524cf4c13aca592b094c269495d2af4691be53ea4c1759db22598dd6fbf51b0ada3e1fe32ea959ff7263e8cd7a5cf643c9db4f74e2cb956607522ff43aafe3b87c2b4553634ff489a2f9429f3c747f57525262516991b218f18497b89143582d8a7270340b2d5040a1d8e80eab5fc5dc28630bd31630378b2daa10749cc5bad01ce8388b4131b749a54ffc7bc1c8e3946e795e77a7ddbc52834127bd48a54f39371d796830fac8fc91c7dfa9ffff4fea72a23ef2f8ffabd1d6604dc7598c09ef8722f46e69ad068c759cc57a680d71bbeb7998c6872b7be264f08395c936317ad05aa0745b774b6dc15a3db02aaed83551f84085b260d15ad78ad16cc15a3d30596ceec4bb468a104256c4d4bc118450154d6ac218c198c7debd8e6fbcd436af1a2e523c4fa805314f65eda6398f8e4b5918c9a81b343e718526a50bac40410b9a3569b8a89182468a20ff1ae20be2a0d2258e33347d5aa4b51041d3bf35d03446e1c841d3a7946228b2d878b23366b47f3cf384f68f568bcedf63eef2e934bab6ff8dae3dc3d1b54d70a95eab13a2eca36b83208216d64ea801f043fbb7a26bfb3cecb5f4f83cccb57df29e0fcb7bfe9632ff9fe08ff6d87c2df9105ebe477117de2a1494e419e46f1692a3d0c20af9778f2b390a2fdfaf380a2ba050f4402be4192890926c83924ff2217ce843c763d8635abe94492d72e6ae0f5ebee4e22ed6cb974cd269abc0df2b9dcdf2e077f8a3072644c2f032012f5f78e9c2cb974af3c5c223e0f7aa003236808f3dcce7e5b3e0ae204fe4e55bc05d455ec86fd50a85df3a3fbf59fa7708f4fb8791df3d41bf6142bf85e81d65f27bf9362fffdd85f33c5eaeb48e96bf593b7e6899f31ba6e52b01fd8a9b107f09e857e64ef491b73f113d0cf376a50fe70d45e37790c8c2c02020911d02120189ac068b8044360824b2134c401640de45be43923791670d09f233a58714817dd2cc7bfa804416f649fe30e414f2eefe834c748fe44de439050cf9f2f67957807f12accbdb7b4e01dc0f51401724ef21ef09f02e135d2eefee5909f83e894be5ed4d4fc0fc2447e59dc27f09f8a42eef23cf2580fba4cecb1bf533f5eef3309801880cf9203e79077998cf27c104f0453211d6792379229908b33ee9831f92b7f73e9f1424efef612f044440ee2921b7a089bc109bbc857c909ebc4bf820dd82f64979c82709c95b080a20911df24941fe851c80bc733c98892e097917f9ed00fa266f143ec801827cd2901c79dffc7680edf5017210c95b7f1003dcdcede6b01112c400423e29c84dde01f81c06f8a42d6f125e33c0f6499a4dde395e88cd0741e1871029216fd5e74c7483eecd2715d14f0485bc89fc10143e69882aef9ebf09cabb842f923f097c9db7ea51f8242279dffc90ff010b905b5feb7f26ba403e294847de45fe2713793879ff3c91155a79ff03c944b8e60d6485fa492dbdc20f104864f527117917f208ef4126ba3c79ebf89b89aeb64280bbda9137d0132100914fd223e4bdfa4b804fd291f7fd2002ecf8241d45f20e7a222daceebd3a707ef22ef2445af8f92422abbc47781d2d7cd2cd9be76b0bf9061b922a4ede3afe07e78900bd2ef2dfca441748deabebc127ed0802cabbc8eb4ce4017d92febc5bef4126c2abbc81fc8e4c84ff933cf8a0bc570ff44945f2def1fa8d8003c83ebe470f235fa4878fbc7b3ca59fe443c92719c9db88104864833ea9c8b390c3bc55b04b2c0fa12fb2a4880a7987f03613619d14e66dbf83251d7c52cee7ad63798470edcde191893c2399c833f249453e84bcc3cfc9445827d9bc4378ba42bec17e127d0ef2e6e08d7c128fbc797c914f12ca5be8833291cdb93dae8f9c4ce40965224fe89382fe47de3fde4726c23aa947de3ebe834c84b51525f9861e9fd441924ede3a2ff4493979e77cd0ff4c0180007824e0cea7008af04740105e083ce183c0d71b015d5f0434e181c0135e83afff015d4f0434e151004ff821e0eb8580ae0f029af03ee009393e0c7c65573621f78000f812c09d0f0028c2930082f02d10000f04dcf91140119e0704e17f8000f001ee84a0085574fc1040108e747c16d8e33f003bf80cf67810ece01fec91a3871e801de4e8a14e0e073c563b74e0a86e72d86090d32950db214b9933342a5d96ba7cc08ba227a1168accc430ea18433a02a327ea2e2e41f00e5f98b59a60087ff8a72083dcf717800c2237eea5fcdd81d4e1fd46bdbedf5af7f7b7d32f877787dea1dea1f7d5e0d4b2f280eb4b163cf4aa69da6f22dabfd3fe6a6f25775fdeb71c98c443e76e76f25277dae42a275fa3ded59685bf3f3cbcf287879ec4437fed65b7ef6b6ae09b37145bb7965b39d461ab82add043ffc1b90da36b79f8fec2b3fe76ca1381f84f10480cfd7109dadfb7088368ff1179fcf10df0fca8fc6e7a32d721ba0a0ea1a4770fed2f1fcfef6a8d41734eedce39a7f676ce59516474b4e1ab2925053b2977cdd9ee9cd372dd47ede39cd35aab596ba596e766023318072145baf8d849765ff86ccea64841675396a853aaac749c4da9c1b533d6a84c9440e24b2fd16510ff18444cdf97413497415c172007311248d45ded60a503a5e3c7ad59fa9ba678c0daac71c71d47fbeea1a7bf056504e24fc1198178de364620f76304b2fd2b137ffafae631b06727afedd7a1e84776d6f6a99321b9e6c9d54344f4cd32f2d49f9e0b008ac6c04a3f5955d79ebee61e18b5f701cd0ac27116022d9a7eedba6d69c61148fbd24518a6af69339e1087064a55d4a4a649a9792250d23ba5fd13e2416bb0e2b17dc41ac4870442dd453b6c03bce3171d9d32e0ae1d9f6670575542fb8c0a17e9cf8274b564e8ef32dc01d0fef7028940fc7d35821c413b0923e0323886df23686fc00368e024a5fcd6e8226bd3396b8d1295c6044f98018eb32e8c26b5e27d57ad37411c06c7252a9ef47c79efbfa22ee058aca332d289b2d23d9e0398405d3603472d5f9313533aeba4b5fe9cdbb553abb5524a41406da269bb9012b381dddd316766153da62c9d5e2af53f6e41141916ce9fba362814ca5a6b8352a07dff52788d2e05da8e76ddbd369fc7711b8e9d97023119de87b76fc9177e413865e3c57e29ef46c725309474fc82b0f7e1c7e9eef7e1df42858aeefc46c72d3fa8b9d11d8742d95cacc2dc8d8d4d8edf3ec74d0ecff3beaff33c2f8767f3e1bfdb97a3fbc08802b7a731185339529f4aa5501eca4bfdf6a99b9b8e4b7deaefea0b4a712b2f258b1cf0a0f352ada192525ecf5d3dbcc763738fdd3078febef183d03cd4e76d1c65904ef3d9ba8fd65a8bfa716a93427d9cb49112f51d977a0e0cba8f028fc4d57cbdd0d474af7d1428356a0dcebd14eab34b32305f120b96272c4f118c08447b1b508a1143ed259615c6a9df574b7c032cfff36c9a6653939681a6f8a4b5dfbeaf434d1d9798ae78754b8c8abe6fa78e5b6253b4d5374b270f35d90406bf86bd14eab3f9ed6db00d0a65ad27c9d0da63707b4bb6e7de7edfc671dc97fa0b1e89da3e0a04b95fcd170cb6b2f63bce43a1c8489d0265e79ed4b0f99247b7fb302410ed3950dbb204431ec926f9b45acda6c883ddc058e414d1476a2fe5a80fe5752350d25bae991eb481bded6d0fd4db03bfb8e35ee419e3c92034d69a759115252ae57d1eb7a1502914ca7ea7799fdc40d41af3deaef352286f43f2dd0d7aa9516c78183c75f442a98c9677069afb0be67c1f75a7dd975abbdb6bdf922e771f4a0d6c0646e9b8c485167d41d75f775316c44e58ead805162efaa274ec020b15287d7328e43a856decf5eec5db17646d529f427ddef6d806775de779b7ebba1bef82aeefedeefd4e755da740ba6ddb16b47d41db831645c6756797b878d2516f0fe89bdf438d7506ef95f63ad1476aed579ae7d92a4e7a0391dc5b9487fa3c4e4a19676bc71029cf438ad1c0f4e9c471aad1a1a9adad740ecda07354e85e9b60f53934b5cd41625f7b5b6de5b8aeeb3acaf91744238f9c3ee7b4765a6ba7967317f3aaf667fd6cf5d01f23484069e953bd4dcf46fbc71fdfe675ac9adadf638c3400a8f97d3ce2a7020f5d9f1e69929f905b1da998e950c7a529b2e80eeff85b4e9c3446ee77fded2798d371d5b75ca796df91184714a3e3c08e7b50e52e07631463f336ef3bea7160fdaebed77df71d8969f4f673fb2e4ee979526e997a2a0f23f71e146de379673b9bcf334a0c8edfd195ce4a731d5575bffd37d5f6fe1a13cfaa0ec7a35f90e713d6609c9b4212ed138a6740b169c30336646ccac03a389810989667a8dc50d9e0660365f80945329a83f2947b0fe8f8843ce7a850df7ffbf838df92fabec3a3607dbdb7cd52ab81a80d0589ed6a575e79af9420500ecccaa446ad510f274a0dccc1142947528a78e0e22b3d67cf8a872b7abe4e122d3d3dac2a8a2d776d184c88bb260f4557424a2925a329fdc8a261f0fde18f9fd0ae0381f8e0096ce0e841d383e018c1a8a71ca3f38f2c3c06de57e3f8d38a9eb32854f44c62035dd4d75ddb3d28ca67326394f2e76c4212a6db810cd5b4961951b5b2963a69d2d2353bb9ba8933e9ebe1964863a434461a69a431464a694881f183564df3136fe2e5e829f06ebc31c6e8495974851ec6a714ece2c47463e8f07cc54802a8e51849d8914b6a0abe9f63ffeb9c7326dd9c637fd2777777f707010cc8ff8619b8228c8042881054b10512d00c34e3b92329a594524a3ddf101d6b9aa6699aa6cd1d39e79c73c28066be21face84e21ea8ce05d1e9ac9ac4f88a3ab31b9a7440d271a68391de445cabd4eaf810d37136a58c7ddd017c8f323f7c279402fb0dade0faabe979185d7417074a4de508eda3bbefe8f7f4a3f7fba8e739a59e97f7b7a1e82ba3e6985c24ae75f77589cf9737f7f3e9139abe4b6e9683961cbe68fadef77d20121fcdfdedfed24fe83ecd51e1ef7779fbd7edeebb7d7fdeef3a9049131fcd650af868eeb9ec84fb2757c7c833ffdec8b3fddd40f9bb9ee962173d07a96bafcdf9f96f8deaaf8c962147ca7370ea4f064dbb13e9a465f0b4ff7c943f8e69d732502f556cd04329a92516d352c10d640a4c3fe608008af687a29f487d5993e2e0051b61a4133c3e6cb57492e833b57b5e8668424287c524ab34e0bbc84156e9131b0c51460a96e9adc381e401c7990e4a7aaf3c79a5025dd4917a6963a643472f53a2c8a1a31765de50e9b8044695c8d2c1c405a068fa51d3fe21b937fed6c202e88287f203b0464cbe17c9974f01a142a8142dd784414b15aa1901000240015315000020100c0704e270503424cbd2921e14000d789e486c569e89a34910a3280842c418640c01c010000c18333343b40eaca079ea411373a8415f7a3158055e81c354fe32567f3b997988169e0b612d73fb3b113bf5c122e364fe9743ed6869712f133c18b206823d7a961684d70f9af15f6352d16855c7ff404a7f6921179e806cd671a4c4972cac046321dcafc5b6538a0c3c97f03abc9540d84dafee2866aeeffcf2f159b74fbb4e3b47d19e304d68762a2b94a3415013303cd9c4465387e715bcc4e330bd514014ceb02a80c48c35acdced215636be8ac880cb0ef25963253f0d6bd9b95e6386050438a80eeb426d0775acd1fc8a71002360d1be1a1865a9a5d953ae243df2bf537f3dde6a7cd91993c7a2cc203cf68bd0b50cff10492b61a02df9d5e8e3af1ea9ab1a9d57a6a404347ab0b83e8a3f4a6d255817814e8ba8f1716fad58e0d5dce9174a1b0eb6175b4cdc2138b69918ce3db1d40033487861c215547c9ea5721aa4e39f681666abe3b875376b62b73c0e0607dac702b360e2e1f6d992e67906582360714b0209572c0b91a8f64aa5ae7069cbb5f119ec5d31ad498c93477fe80dceabec21893a24e192fda31b53aa5e2808334d983001c97f25b1a0adad898555b4e2fe8b5e53423276ac10ce2e06787324c55ff7533281c67d7f0111b2f3f596fb5ceb4eb1e326e1ee2f4345073800bb1cc63a8450558c1e46417865bc92d9aadc7e453a4f6895380466be0ef71656f611efb69647032cf4fe2d467f002d6824369cebdbb72ae5cd929486d752c73c417d7890d6682518127a38abe43176a3902b5f8849b1e21555c756c8c443e4b28ac7ae042362677e841d459a3de3506a854b3b211b0ea8bc728f22b65b55f44140c252c0d3bb6e9e4f6d97ae2301fdf8ae81646ed40356ef9ae54c6c9f0dd8530b5ad14c6e7a388a31c8e1f2f1ecf1fb68e7bc5e1a1dc67f162af5c5f5dda8629ec4abb37bde1dc43d18608c29b91edee03d195204f88c5b21fae113da21ec3e6423f6313823073cdc4686eb92d542c5730c8d7a8e8da72808200870641f9b706662ca4d0d5bf3649bfd52e8b51c2b669dc96511c66cc25b1535128d730ef694132d47261fcd35f2aa7a8172c2e937c84bed2c6f639235bc673c0193e0f8fc2e6fbb3b5ff9fa5a84c966bc237932abbd5fc10429b51985037c54237189a71cbe77bfb9e4b1a7d3670c96e5666288786f204ad76e2dfdd1aa393ce047f47c40134c479765a9fa66c867c0d3dd62411ad34b521ad6f511a9c6ae84d64260fd78a718f1937fe9d19b184cafecdf9bb30b8f80c52c432f63427fb97fdefe17310953f4f0055321a9229992de80c6445385d31ef7f3012504a8125b2ff83ebe37beca00a25d5a64ccecfd97ce621d05a25a62eab2e92f01bcbb6be972302eb3bfd71b44ff4698e94ba8030b5505735be55a70959bdbb59600c793ba996ca2d7e294fdb426a2ce41dcf7ab36aee388d2dc6857e904c0a0bd2ad313a90daae5a545763e4dcbe4703c996a06285992e712a6b1f2ba0853af30942350705222f5757a8fa19149209832f6c9e7bd23e73a06499404f7e861af9b72f99770d12c30b839a6306a08ddcc1ae6181a039fb3fd4333f768e3a221a2c565a58fd35afe72142b561fd09dfd232f33b7e842b9dba04c534091b742dd95bb2a576bfe55a4665018f8b198ce6f2431dc35ffb8555522c6a84fa3092169671a0a88328a979394302fca1541a5e760e7fd46b839236e59b207d1b6644284a4312171c9e2c2d524ba38d0ad6baacb4939d05b3b6e060b5d17ebd611daba431b0f4377f126fe76fe1c8eea6a9f7db18a805e2985b409538bfe26c94ac7c850f93f90eecfb6b4cfe0fa6b25d31c838a8a75e2c27fc2700a753ddfe57c76c2cff1fff10329d7ac26280443d67fb0adbfbdefe3f40e3f7ebaee1238a21aa60b7e1494e98a3cc6b0f45b8287eda556d2389db39a51fd2d5ddc830954821c88822b7aaf82ff6ce923c9c0367e40ec4e57387f3936f3f888cfe1b778a3e70fef45a3607be1a801a1e361290c15e9e3a90a7a1c5cc4e84e78b7004da6b8cbcdd89fbdb6e3c2179458003fc5ca2c701f387a173ca5edaed9c0402126ab16796d4b7c22f985ddc1aacec113af10a0fcbec5dc0c610f2b66f4517d3bbdc6598a4cb3ef90917d95f124ac5c6e32144a6fee24fad77211eec626af2b32bc83a7d72bad75286be15dfa4bcfead4fc12becd1e51316fc2bc2c5d4a49af69f42d24e1680ee12e0546a01fc22dd4a3c2b6f9ca4721a17783f446a6d71ac68c2204635482ceba36e95e83b3c11693c2af7f499e9be762817c1f9b08dd065cb2c8dc3618a5ff81ea7f7adb4cf7b9ac54917b8d3201b41e3ed181d9457428c9c16cdb61307dca0e34b7388610bf92d5bad65a1827f840350fdf9a391a32d0ff28f69c04e5dc1bde6e1d799c37fef297f2440ee6093c3382b0a9b38d24f83fc8d0162708b712f9593eb9963e142d784e3ee6c397fb5029b2935022dc3bf53ad74881e7881cf357f7648da321325311e4cd52ad7a40b1dff98e0acda01e450040446fc875314c74d7f502c06d6935f616070e4b96b1bb53e67633bbad763a6b950232de14746ca55684c724cfd5b9d1677075645881f5c344b8f1d4101d5e81c830876218c38af6c17c91fc3ce64964ad588a88e158e541e1387306c99fedceb0d4734221b8caa90a19968740db4deb8b6d6853f123a28dddea9c16c434d0477a0b3f789c3b6b1a1eba64fdac4812488ba88ea2730e1b4b0d62f9405618ead415066c188807809fd21cbe55a2c6a46e37595803e2fd5e4db387c04cd8c77d5053569b8bb374380eb9043a633d632f88158d7216c6e9df7165285e4790f6dab0578a8af8d736e42ec354f0a435535252cbd95dff79220a144925ea8a103ee40b0c8bf84f723dc9acb0859770a3763710aba80695fa2a33255433efaa0a02ded7f5153c7c5e24eaa947622fd18901bb819c5506ee9f7200631ddbdc38e0e83dfed239b8143904721892d49cfb104ed392d141977a073c02e53c19e3cd4b83ee32272eca79d9a1a763c925328e05e7d5914c08883e5625eee4607c567bb336ca26953345f3077a86326271226167843b2055f10cfbc7cc71725b9be7752d6851721ad7ed0523ec5af9c2bfbd848b06884c5442147716dc63cf30096ebc1007108449f4c6988452b087db4a60c81aea93efd6d11eaeee81ea2bb94ec0214e46f150f39199a4f5f28c6abf14198defa338e7b36d29578254d603fa3c9fe289c54fe61e0a833f73d322709b7f30e6871246799c31d39d733e4e108dd3a10a2db989f34684aca9ea90fd9caf51da571c4c528aae66241e0129d3acd15e9da4e164bc378b0f6415b091bd4b17064cc6e91a787aea7fb947e83c75a5fa9b3ae1b34f3abb3ff9592714d134d769aef68050a44a88b2b6521691b9ebef47823b29801654208562299d8295134a017b26210ae481627ace698744ca6e664510260c8b0aac63ccf3f85863ce1f3466131e581446b191fddb4a1052fc08f824298deb67f07e768c2bb7e9b2ad1459ba95a646ef1c64e321982eae620b55fec80c46322eb786546cc5d0653a9f32901abd9cd9adb10dae342cf772a0849d73cb475488991ac5736a2bc669edc4dc8c9440e91a86a4ff52e670655af8efffe8b7405180019f2227508f5b188283a21c778f6f757f5738028ae51adb643da9d5cffd94439e07fcedb0a3b385475fef4764eecd820d68932660fdd6cce3a203e7af54e616938e3b0f6c9a9b62a5081146f0b0cf2ffe952adf3330c608398ec1f0e13e8b5ad890e3c265183a58d8b99daf4324692179ff0f1f3117d8c91a62259d5d67ebc34a5badb33659f1960d472733399da66320447b7f7bd200d12e09e05d409418053f5d91d00d8ea8f9f4674d2d4362a8a45a4c40c31798c2d4097220e3c68226062f145c7f16de80748ffbde87fb8cac7b90208f69181be632ec856ab1d77d06ca0fded02f2d3e9a7288d3dc6e3c6ecb2a6e993b972c193892a83c281a951b3940be08e2609df2290f8a227fabebe47de3415b07887cf6a0c16e3bf0048f8795ed14fa534256be747aa6e2b33247564c8b01f7aedd0dfa4b1f76733f25eb5293f72c40978831e93fe15476dcc8157538828c9e63d76b623f3fdff3c19c793ff1fe86806ec662ab907729b03ce48a8bf44187c905ca59737e8f188fe73acb40c3279123dcc7bc3e3417192710ec71f1a0a79a33c2b037d0ae58622397fc398ba539a5a2125d21884ef3ec4373a95bb1241df5ebf89bb85a8e4091a5766ce9a8d0aa2d4295909f58a2fc9e0cfed8feedaea896534f22c430abf0e0d8c83c5d9e58127aa9aa4b803a22c729049ca98f94f84dc0266a4db194976659ea9985cbdcab1e9313f00784bfea99281c41d5e0874c326d76e30f41dc7f11d72983bbb5a5083618cf036137cdf30c4fdbfd1db0c03407b86188407e7c12b62feadf018bb8fc4b186a91309648ee97604f483f528d4900ad31c59751d3caf4e6aa070715eca56cdc5ec5728e405d2f8529c9354c0c3177232d64e174a43ab9d4b9e1045a5fca2baaf4ab535f5a5b0c077cd5c950b20d07ca8d916a98c296274a2c46cc6dc51d93985acae89a24682c5f2858e4c052bf7c9f2c5f6133e3dff6c51e499686f1092859e474345093482d4611b1cce760dfad2e93c2d186563e1ca6b57d06dc7927161918cc86a8942460700daf1319d1585011792b703b125067f598ade0ce686db2a31855612ae02073d891aa313b97ee34e5d842e05b70e545f600691c87fc864b04c8703f5e5d18ed78f25b85399c505a3ff119588e615f63ebb19ca80df2917cd24e1c88ba3b4dfdc4056fc1d995880ef807a9e2f8b1a39f39d5dce2ea46a5868bb8dd88bfb01aa2b41572b30ff05059b367e8cb37172502edf6b42a8d6c650bb0272ddd16e62f17a8f506080c30247c672461e15e710d1919d9a171e692e1ae67416d2ab0af03e2fdbc9e630030164bb3322dfd39adf5e254e69104fc0a88f7bdbc9cfc926659def2f9d65ca2ec3e47a1312ec03f6148d8515d32f22ec7743fc652f77e006a116c2a5d0981226fddc6f82caa94a301fd35349a8b6c4e6254508da5f20d08de3a883fd100bcbd85607e662e8e63311308ee288950d751e091457652d1307432e81d6e843b02197bf72483e27127b019371b344650dc67b30744035bbccaadbfe11a2da1eadc147c8d42d63fa3becfee78e43ba69a8fbca6437acadb90fb15bf09c6f6deebb2926f2162892091a60ed10d05f9853175027db03e234bb6372a421c24a83b4983d27da6559e8a3604ae5e9c92a7b23b76edcac62e65b2829e0981021e35376af9e09a8ff62966576cc49fea9282185747e538b53f069b4f8d2db3a49aecf95818ad4d02083766680c734aa6071b4fc0d1f971ff3f038da21d415919d41bdd4a1d0659bf515846f11099687aba822f52970fc53e407ff333b5e1ede3a5ec99a548138ae028e0d050be137577b97621018a7a53f0122b9c7ea47a045c04c946327fad53a660d416e913c62bf7bc58ec93f6bdaeb0f068f20200f751dc3812ca003afd185ecce5260a64d7b438704f72e25d2cf2f54858a70b4d1e0eada3480944c772cb25bcd449dcc1a941320f4ab1557e7cd47cb60f9016d49f47529fbe06874e9e6b84d8286804f1dceda9ce718539c09cb654009c70ab09352e15e54532081fdbaba52510c7edfd4915bb61f52cad058320734d24c9534024f6da2beb53c7132ab05204f1aea45ca5f1bade3870f5f950d40f26ebb868d88489f4e340aac51be09423e5de01dea2aa4b1fae23ec0d91ba032352fb179112d6530d22edfa1e8b29cfc46721b3956296f489532f626ace3d138c3dba10f9f6aa2efd2d0766b0c6e59a49a285d9c03217edad4f07ca229f43ea38009228e2c5e3340766990e7de0b22b927eacc100b1e6305a2a34035aaeb286001e2b52500a6e9b8bc24a8a9faa1b8f4946c67e1c3342a9eb08d912fdd9dfec9221a744860553eaa661f25759361fec2210878f2c429969357068328fc918eced434e64cb1cd7ec56bf06d753a369340147eb90647ca9aa4498525da14daa649635ac31ac8f883dbc6611b2e99d1176259b1327d4b6f4e2845a31e52caf2eccc0cb82b5b1d8994203ee28a77116d46e5abd585a47e4dc0a45efd7d070f4754e81a16a66fcd84b1c19e6e6720e8474f7e85b4013932bf0d849e67f13415ef28ed4e7b337957cdfdac8d8dc4c70d0e16fc6800e24a7359f47517f06a8811263264c722b4ccff38c0ea58cb6eec2f1a057552655ef9d01574252a48d33e2d8e8173a2868b2a5e9a7dc8cd469478e30652263b27d0570d91a37d21f06ac3ade92f64efca46daad3e26c4e978316bf23727687257bf49732719408ae446f45f738176e3097908b96f37f779da0e54f4c52483e4276ada9abdb965883ea2acb53edb061efdcd1e5b1f35a07bcdb843d5cf5c61e3dc646c2201927bda56492f0db2e4d0d720d1633691f8b0b1acd1e810963fc954a6fba5e98e7c2e7e1839f75434cf25a5ed46b0e6b70797a0b371c2388663b27061fa2fce8c60c0aed457d668d907707d776193483cff2e143cbb4dceccd85b573ef16e1c4af47abd9e84669e1b7a113cee4191198edd3dba1da8a661539c037eead686a051f2e9310a7bde4eb6170c06165b5304c85dba59d75f8da23736c475239f6c9ae4a2859d8b5afd0909f207b732ec62b9878b62732a8c1e2699ed3c016eb65511fb0abd5bfe6b55d3071f30433d1850e101bc5bf37d8acd90421bfa5d5d74e0b80a1603fc0d30770dd1c5e077e206c93d8af83b351ce878ae241c39cb70f2ac25ac70559fd7cb690e093d67c782ab40ab0f02fc4b67aa406871b95d5175f43ca0c1ed827d9eacdffa715c8053607ec6c42d82bac1d23eff30f39ae21f1074001d3620bc1f8951794f3541c308d6b0bfd8ad841427b9385e6d60da97177ec8ba81320bdf463c2e8a8fa43818b913d7a3132a6c2a116574cff87d7363f935e1fd397157a3f38d06c5725bd20f54cd4375743445997132a22271b083af3da3d500110085d92550dfcc7aa5a2f561573baeb49229c4c16f194e4d52a9516f04041eff082af02a077fad4776091490b9864e0c8be244ebb54fd5de61acda4964a1b0a981b430565346a500df23c133c7a3e25f510b22afcca279dd1ea62d2221377a6c0f3a227b877ebd71da10920df4734d1a8022c6ba266f3e34631e5ac8c24ca3338e706f646f24b3614e5113a1c51595182087d2c905d8e70c5f21ccecb649a5b46965e5557fe42dfe5b2dc3f090ece67d5bc63cc2b3d9408ee5e1ebbe2930d196d65e708e333e9ad06dcbdd1c4889a36b8225b4df7f20223393e26a2b7be90d00e26e078f6692478798fed0c0e400e751c32a56d0a602b948162c165f6f63e5f6fc61b19c41225c8ee1829ca954c2158d3e23c69b2f8b0697863bd898f23e336431f2fdead1473fe0c4c64804619b614a4cd447b9ea41611a7a217fae260457ca33822f0973e60853834d300d238ce205aac40d87cc97570a1a25490644c21201384240a4b8fac43f0a21cd930a11b1886616315fb81df6f0f127709c83acbe4266fe30ed0b7ae1d11b9bdca33e634fabaa4fe34491a5583bb72144e05fd06561cd05cdd3223f95bde352b1d8b37519db577536a4a2eb9722c7b305df63f69213e57c19c413f814cac103f94b970c509ce81de1f55a29edb529dd91c14a83c0d57f683acf620ab6820f5c1a450fc387d5c27d47bf5bc066aba5ab2723d9d6cbde1a713deb0778967e48896ca2333ccba1f026a1a69c09d7ff0db5604be753d2375a4e77d0fe5f7bd2cae027d341beccb9439333eb04314d6b4218708ebddb8ffce4e5121a4055716ad0595b0231cbf7f0081fecb53d99a1ceb9e5201134b43ccd4bfc9fbe88230319fa0d8f65bce32961efe8b910f414042220df178cf1ff27144b6736080d513982a774c5b3b644f28e8a4d06a1831b7aac18c5ff06b04f87a4c68181eb83a164cce65b2649867d9619d15deecdf9eba6dbd9e6794fcd7c18ae69f3a1e57738da40379a56d77ec4856c284294cb677e1c55f988a20d236e2de787d5e5a03d05b2003327ca0116314299dc66c798bc4c817d84b13c65cd6a98c4f67966ab97ae1bcaf136de59cbc9f4a8b63607cb268d29df134e2d7891fa94a65521b6095b26adca8a89281d265b83ec51ccdb0d2149b71a868c587121815538f4a16fb4a58856ffb2260bf0b81611f9fe6427cbbca665dfac3eb4031dce115a0297c69b621814758b5ce0d3495b6f5bdeafa8a9cae189d66831b8c2b4d285e176b0bc02f8184f7ebb9280dea8b64dc1a6edfad3e7a0e930fd6634a1eff84264bd47c1e050d0e7c6ab7a145253e29bf9f42878f0f7186d2ac2e9d919403c85c71411c13db3e9c2a03d34190385a1a636c85f5183f31d9a2455a7ce725cd931c7ea2df366194123f09e3f6b445b320582b2925f5f3c77d9ad941aa63dc8defae621d68729ad9f0d3446d70feccf7baeef73972f39f5ee81519eb089915985d08ea60a166f9aa972c5bea2baea722f4468e85c725b3c20631748adcc1b678fd824cc586e9255173476a642664adff449fdde1516f5c0a8691c4f2d27c695fb262e51b7699d9bd38073bb3e54bb3b7b300bfb15cb4d3ef387ca69b9fea6631f0e0bb4823b5897737eb15f9a799976023cb70cc5cc1419396fad7e6f618bad3fd9d216e8e3c8db18fffb2264c4a7404f1d7d0d1fe3e3b315951972825dbda6e97e26900c9910e2d23c5bcd4f22490e8b7d90444bfd73e9d0f470806a123e947d36553b11b7a2963dbbedaafcce3ed019211760553aa3f06b29e25435afa89f5263233b76b019edfa964a041bffa487cfd1438dfbc92daae2ba88e8a3ec7a413d7422b8240d0819bc3f31f69bbd8715baca7629ff8afb1abc24362c5b50cce633d6ac193ba0de3cd649fc1f981a760b1893b135dd756e9ba404c5d179560f004e27c9c2011ae3c7f08370a55028c43a83ba9057a57de5a5348d326f27d9cd67d40904d3e40f85d9923422049e1b114834464a5411427575d94735912e458099f419b945f8cbd6d0bf827b978f4c2d25f31a42334b92a5ab0e544f0787351120361070a3b1077216381a1a3ce5cd3f8357326fa1322773d81b31696f98b3ed700c5df1f72cb4ec094e4522c65e54b5907023455d6615d67c1b106fdf4946d28e926f031c9cbde98db1e17cab70bffdefdf67f56f6295b45942ab9990ea7439df2d9c181051747c03d4ae6481751f7324e2614a0eed3a7225aaf4db4eced89b453c06e6603075774aee161003fd2b361edc64c566f1159079b09cb426c895f3d685d0400c240f40ba8ad7c95287ac3b8bea0743fd6183d8ca087206f1209a204d574182f9281dfd8a78660f14988f23f85c8bc208bc9480ec46105c06fa1eb27bc6331ac7951aa94aef9ba5adab7e5aecc6e02ec01c54a84f8884072867a8c27ca76b52b18df1b3a1e7fe91430c303b29d75ae9fc07395c3c50d879d286bb585151e3d52676039b2ec1ab1cce3f8900fa89b78ad51f99eeb9cd45c1f9ea625886654a167f7d5b57e14b6bb685186f40438b6a1382def289e9fd2e877033ccc6982233c7087f1471c29046dd26a5f8db2e9ee07fd8bd0668346b003f98bbe9b8d0e2d3f3452d060e9ed77d2b22d1e719d36acbc817675f16cbe8afc3a5f617a9314fdbb504ccac592e671b2f72284078753cda99cd560d55846b89952bb48a969d2d614695dc704d9109f56c6aafdd62eda573e269f713eebdd544f0f015b60e07893b7fa634758fa57c088aa34f4224cc408ffb28fb1ede1dcbba7ba88e740ca186a2d541d11f0f69c27a73536c8c1bf404da8dc875a2e0469166bf25971fbe0aa63aa2781aa37bc0ac26eeee0ba7f7aa88efeaf22b533b0fb59025cdefd2f3d94cf87e7061e90542967f594eb455c14d24350062d6a7a720bb353790227b710d9442d9e2f40da77ad47d844b10f7da5cded178569ceedbd6346197c942ca7c3d6f8f3fc71499bd883980af57edf8381e37a876065d278006b4e4016ba821dc012ced5d88904605a2867a51d82bde3683f91df1135a9ab33e536fd6c8b1208cfd7dc2a86da952849a0bc5b1470af143b60f9dd905ec114263e4086fd5be5df3c5acc49e978b40307725d289242ff52410fca6cbaee4b02eb9b90b50909a84fbdb63661a0e057ad8fa5472a7d08082609d47c285d0986efc639601925379525faab53243550c2d6a5c780f10ccf66354062c7988aa07c092eb7528fa9404cf8fa542cab18fcd4800388219006c29679d2c7f202e581e44acefe237e68c2b5b7157cff28a56bec38035457a09c635bf3896030b27ecb66cc4973de2a93113f56ab71e3afb815d12cc095e777fee1d30aba518fe4f5485186d623d128ac1f2d3e50abd9e178d2f233c9648dfba742c674739522ce5c68822e012cf419c7e73ae2e69b0549915cfe5b478849c2e24057f71f11e5b50e56606ee50fc7ea0a40c8c140c9dc3efb250d2b25615057d9dcb4bed7ec33a3a88dbc013d8433e231735103e9adb319826b6a36c943a2773ece080ec2a0de6618c3cae96d0fc80a9a7ec09e6a3a2bae7718a3d7ebce40ae448590763d13464ec5fb63fe95881fe655cfba41f8b6c8d41dd1a8070d068548555167b8cc80062cba75416e7e6bda10d8ce21dad4d4961cf24110266ddb630a4905e4835bd2b0c049031b92d96acdd7b68fd9576d67f662b60b0399e9d28f1abf07e7d6e801aaaf3f86b2ce5c8290ab14a8af5016032b831ffd3a8c40611b51ef3bc1e02dd4e1f4f240104bf94ee2889307a1fb3f30ce6f36dc4ce9a712630d6a012cd9b835b644d8dbab899f48e1b083e124173ebabe2df4258db5a2a70b3c63d439430224a30ee9073cef827b474b0e08aa7b402098e402414110d1e9585c9ae047f9697bafd00f584d6e0fd899167e5a2951e782d1946f94fc6ce6d01f572581161b08d8c53f6d8a6f2716446baec573d3a4871dd2e5f0632189f8ec73988c7617fa2cac9a80b617fa042c4c4c6abb606b5011b43118a36b9f4aa36ec555849027939d15175ab5082ab118edf1e485a1f671f3aa64ac3b695e82d2137936137d844fc1deb58aecb54f55485bd1417e39ac68a518fa610e99ae440f9099806bd0e21990e1f7b5accbafc76920d0684ce16bdd336665060b7662c929734f706b2a7742cc92499056de43c40ae7d2a64fb5a8d086c72ee43ad4b5d16670d92906de02c0aef00be57cb2f1d99622c3adc41e7c5f33c76813093db2e55e04231d4129adac69b36ccfaad26c49764c854e37833eb5304edd7e37545b30c101777c8e93f4ddc00480b563a4f466e6e2ce3170a46d3dad4e916ac80f1b1aac22b58ec1c8e28f3d20c3cdc173aacea127ff085c942e283c7dba50d56175d5e3eb2acc04fb73cadc500a33e85ff510baa5cf6521596f8036d133e9635788b94dca11b398263ab0f348d1f32a9eab8a38f5b894d537d1d1d917377582bdb1102177a5615f2fc4963bd700f80d533179be02a5b691e8888dcc8d37e8e588336a3fc989bfe892492af2a92e901a093402a2092c21bb919de6d103c1ecf6e109ac5c4ac7a8e66b4a3054db091d41b1ecd4119dc9f40f0d7d00343343035e72b44b100c54878d519abf57ca9154bbdfdaa037921e9eb8e7a8f8512482609dd0ae5a99050017652697a77529e53c334a71f040921bf293600375ecbe547a13c29088e4d4bbd5c53731732801b7298036e30ec4ac7211b36c7f36323cf0ba201f6d4ac50c837e6299bbbaa384f17e3b469fbed4e2425ccc4c7dbed85943256f9d30106d6c99389894aacbb87e184ed9ae612a38f24b6fc8574444ad897b3e98718ed54e5245dd2b14dfb49dc772a73a3e4477a74bc15efd4e88c46608cdfea157c318975b08b499a3c2ec4bb2afb6e2acc09e454b5abdbbf3274c3d8e22d3e5a880bca0cc1975965461bdd513e7d179536408197c10b9be6b4c7cb3fb39c6821bef344b31fec7bb42dfe78bde77e36256f2a14cc744809a6875dd8998e6db09510036c362c2c24e277d1acf0ab72d328158a6f34b9bdbb8a500184cd4614deeac719dccf416838a75dbffa29ee45bd05c505569d8d96cb1c74f866c96da4b0f344c2d71f251104c684cf930b7101a6b10ff6776cfacc49bddde43edb65607d971257bb36d00a67e4cbfe756fbc10c08ffc07cbfb51c56132fb0862b94c37cf1fb4282e36453d1d4ee38a0b94cdab63e0bb9a08956b8cf03da543ad9f866a59b590053a53af404ee35a26683a8811792b5d76df945fec8690100aca8d2d0015b618f9e6c17849fae1b13fa2b7dd1ddf101f3ca6127e647c2bfde3d96121dba7936e031c01c70e64b8cb0c2b04fb4503ff0fb0c58cc0f6579fc5129368156a6bc534624c5d07e4eb20a4ac1e15c468c0eafd8d544d55bd20483b36096c8be5bfb62b03c07e1d5f98ae384a29f60af25893ff80b8814c573ad61ea9f6b8f557294713f0fb22143554280ed3a3b34d4f5d84474939b7d2c9f7cf205c4ef228cd34a68768c3b5017dff75560a52b948d85be50d4978db3323269617e53554e369ebac14441a683f059f055744b6e6170171fbdec7755f7705c79fd7ede47f67a9da7d4c6dffad7ec4f64b83192273203479e42b3bb004c91c19df282f5c57944a3a5cd2722699ca258a156c84a58acbf890ac0fec296328a1d65e18f92229a2ded082312eb29d1dbada4af3f07d66e7670f679d2a2d71a45968611b3d2ee4a530c76ed29d3c5bbb42199fc83824d11ed71f89c38a8e699ca745684628aa2f1980d547d20fb4beaadea7fd83ade331aada570d9201cdf8ff4ccbb7b2cb518fff63b69ea76b957d9bcccae01cf6f64fc403ad6fe91e9b8a766ead699a21694f077cab4e231424be8022c6ba1b2ff52fdee1452ddf9cf9a4fb42483895dc8fbefbf6db171bb9bf4eb0270eabc66b1262495209736bcc67be467896dbc27e95712d9f3f209695271cf6166e3c8b03a6a6ec3e84265098769f1b1bc5c23d67a4b3d34d781ec71f60fd599531c520269cdf52401ebb17499cf9d965af15c4636b260ff6c120b0ba951f82e0b25c94a8088fe014418044b8c8992c757bfb20406d43be1f6707e6bea83a7883cddc26628d136989b63922f3cb25916ec701ab671eb1e91f708bc9f28e2806c8085f4c9c82428f8859900fd55e5af851f9a8a12753942f3f13a07d704f4c5bf0da5ba5a0e523d6c046233762632605fceb6a320c9839714d0a1c950dd31f39dcb8f71d1bdc1041a100ec28a38563492f69399c26fe632b03fd381b7aa2830b6c1210b24d0ad34154b22082b82696a025ef2a14a8784b9cb050d62b1c4bb44e7ae09cd029b7822f792449a2cab0ad0eb80a91ccc70245545de9e2ceba97c50102264833de3498694d931e6628ff6edfdc900f89740aa6c919f8bfa439fd21bdbe73165433673a6616a4d6b40b56688d0e3a0c6e0d4f4070a9337c99b6506e1e46242bb5171fdb6f310415a5bf466f480ea6cc8bb7b220308a472c999b829285afaf33e0ce46d073bd9e3ea117c5e07a70ec21ed7611ef58769c298cb6c2df9b23282b427fcb91fc62fe82e97cf3d7846e5abe50f3c30beed49de6a66c45e42c013168e2743b85d99184f982a2677c699f99f8f9d2704b1324c1b2308cc5b288f86672b1b67c8dfd969ac24c64efd82600158c6b50a4fbeba067f29270155aa2c03fff3e8ddbaa72bd5dc9dbdf634ad63a0ac04daf04dba889a2ff327eeff1d4f697b2a86942736522c03b3238aeedef5f42c6f32010d4d412644c36a8257fa3750322729fd01b6c79a25560443d349934bfe7f44da4a0576929464bb5e356f7f16d113001b9b90ffd61574d0a643197a36d7467afd8a20ac223d0db41dcc1e8991d6f0c969207b593ac3749f89ee61f07a7752fad7962a1a26d622d501cfaf6f29db67244294643cdf47a190d4198279aa348a07c07a1683022c5948488e957244752264b46a813ad6daea984d13ff12948dadb173b2cc3a34012a03d0aa321e420cb1cd8f030e4308583411b885dd73b10ba4e03910db10198f94191d2dd1919c5e9128a800cf050fd8d82db0b3d8d7f7911db4445b3654867a3ab0d0bb2126ba3235e5c1d3a73c32794a999d2c90131c2ef344a23e52ff8ebd7165e2c62214a127fff6d28dcd52c214a33934062114ab12cd7d2a7fa2ff983e900cd626e9f9f15f0127307b02205e4614f9bb7c3bba2b2989e452b53277b1d0b86ee62038cd4c83eaf1f6d1c4ced27dd351db6e0c808c8af606b44c54c6368a627ca0263a700a3dedf47b499d2a99b20d88521039e2ee0eed524eceed01d66814b20cd43612cfe6a388fe223af18c0c28d253ab7cbc109e39acd70ae146145863af286137aa820853277cbc056c75bba48b8fcd64f901027d799d0d52c833c66388acb9e65b2dfa8956599119c064fe85fbd9cf651dd0bd3a5827c5d2f88fa54eec05a2b93f2ce6d547b8c21e916fd7f6512c8eb63b907c37fb9c288e13e604df58aca8628f47b1783c1ea85b8a6887a651810130bd8ed65c1b53bd705e9bd6b72ea7d55938e78fc5d215b9a7d1c9b3237876e4a8551953e636c4e8e94e92d39c2834142eb927a186305977217f157c750fbbbb8fe054d3146d99f883e8695874916cc3b41139ef74fe2e11be3ee34cc9ec7eefb1d19ab4c0e6741e3ad4a0d642e12679b48f907de26af3d160f0c925dbcbca36f8c62425f7bd7ddd549d2b7745582eb00e9c60ec2e5c1b6d44457c6a0e340934f4c90a26df0c179933dfacfe0e8c94aa1cdd7e220324d24bc7439e078efb5b1d529cb6c2d48756e52243bf11d1401c782a1b4d3c100872b32088285c751d8e31588a6b0e48dc91b46efb78324418ed83fc543db089616b6a701d2cfb16c8626997c1662f9c745c1816300822183475551921547609e0d07ad1273886d8b34160a031e628426b1b8cdd96a0349baa81b728e80302d985519847901409e47c6f4470509cdecbd93e3a1bb491ae52685bd0a482a26cf441d4546a7203854c74c5af04e9fbdcb81bed3370b1425bfd5d8addc2b72fda93f08b6681540d71b3265715f88c6a588a12ba0f9681de65fa550007715ce561582271248088c25d1b1428908c61ee7c46118e9524a4a5649c38faf27622ab018812940b57de913bb9c88e070cc3ed39cd7dbd873f6a5b8d39edfaea46033d2538947c9307c0270d4ba493d830cd84112d05a38c1dcf2e231b9d0ac427bb27b16dddf0065b48db0079df21f72559bd0a7379cc6224f35510c1129eebca4e5874e2702323a8a1686f304ec843017de5b44fc6413385847f7b9f162473cd17446583333abb9b946b9779aea95c250fa5dd78174af9c3b8fdb4d18412e09ea4fd29959d16e4e8f240d541d2de6df3153d91abb55af8c8251c212ccc882cb8928096b85fbf4a7412a1d8af7cdf9701e677a28a68ad138313453544ae1335fb34cc9e89a0d9bcf800fd62eb49283127be0c9bd059ad64985f337260d54530576d41131565ce9e8db98f666f5e46553c2b2104ed52901194da3866b3019c968d664a0b8ad064e51614a8089e15759bc81541a6a347216e2a36814260fa66bbf731c4f79521af134d15f01a8e45327eedf90f27f8758969f94a573ead6dbfd69e4906c8309248ba802577c5ab5726ddfce1620ef52230f2b5693b54d28d237f9046f57420dadb5f111f37aced30c4b4d52808af318afa7d29d4de2b0784803760659202c8b378b59efaf5385434199b82fa7609248f7873c51b573ddd96f1b87488fa0cc24ebf2e65dc53a3650ece109045aa9edb601f2b0fbec20bdfd2df163834b11030d2f9e91cb4569485f18be27bab2085f17ab70afea8af990b63118a5483b8614986c0e149502ed955fb858e49407f2681caf6291ad107173e18678b668a3bae97f1e52aab8a6919aaf532c6ae88109114338914c083692a0a71561dbd1414a7cbea0a3f17c7e797f7c215197a3e6d6abf5eb240a2e0565eef22256b13b9b5db65eefce834388e7ff9dca30bbde1880e390d533be1a3044a9d74db52ceaff674841a3be82882c48c6ce3b95c7a5181caabef92b3ef482c1d03ecc70b852a550e621b2bfcee9100485701e9df4b901cb8c0114e0a2d50ea52272fbb2c2694be36d59634aae07ec42afa2acb26624787251d315b8d1aeb5c16662a1629bdae25bbac9cb943eeb27f84bad5636f9bca3aadf8401bf97afedb26315632c9640dae2ca2c88ef20839b8bd7c83efa31a749988ea897842c35ba66a1c4d40a551d652dc900ecefdfe749d033ec45727c9fe52d083403140dc507e7ef8482f1fbbe791004f435edb045d627f86de1bc4474ec18a331b7f18c76554e724fa10ff20a47a3495898be773f4bcdbd5ab44e50bcbaa4aeff036a048a3057936e14f96040656221dab8dddc468b4fb1c2a5689a7fed6466e01ed1f273c92b08d9d8c5efd84a11c5f7d51da960e5466d9abe1ec4988962abd9e5140c966574d7030ab8f68f832a18836c5da52589a493dccd0ac38b77fd806ebf0858db00d891640c4ff201da001147d67ab1cbe8d7a7b9ec8f5f8b66ab857bb7e13d4fb758fef4af9ee804255bafdf7a687b582e278c2061f320a17d4abdb89708169bfd9ad1a45c432fcefa0610e8eccfaab288d2c9153530471e7c350eba6a0c0f807d85deb847df09704b000ee855a52bdb09365999cfdd5631e4c7c329324e29b98c0fc7cb2f45b666b1322789f024a0913a4a98f646c5da44740c8e6d7859701f47a2582c77011fc424c2d5a1a615e2887e4f8792452910143a7eb43dea250436e1d533140da7297a291913135db96a81e7544c7c6ef6ae18cf92c301db82f6fdc52cb19ea77128e237ed22365fceaed0a7256c33c6d4389b51c1158b73bce170cd561876319854e170d4b376e9c5b8c4c2def56c1d8753d053d1ede3a8546c58158c8b47b117c7e02dd151322dcf9d5b2825cb20ebc729d2fdd680152f91d451441775da9174bc8e45924237a28a3426547e028ccf6f7abe9a8c674af3a2b60ea89c77f4e5b7046d567e1a2ce19fe09759b6aa2f0bb4737a6cb1af6a478a9206402bb42ae9907a053bca24c340c26a846143968af0a6085048304d63391ab8c38b18eaf60e5c8690713d0e88f7f32822819fe08242609afb159031d0eee7bc61be5354e3a962f44df86ec5c3374eb336cd8427c831db65084792ba95aab226531d0856f2447f54e1db354ce46f168a69c598d797701a45ee476dc55949c372600ee40cb7634ab61da140b010e61743c81ebcb8bef82d4867110d545f43c584de29c19e53c87f477f22ee2c02b10def93b5bfdc76d3f869c2967ff304f1ca7079184fbe1bf765643e6833e4f2c6e04d8fe2c2c59194e2f18543a8c90aaf3f8a0c747e905b79b7a8f70912b905a5a68c1eff23e8f219bd949d81e67bcf0e1582e5a80ecdcf278c6c878127d7b1cf14fc90fb4a3ed817e1c7b45a6ba452b0d2cf3dc9b6919a4ba206027823abe65887bf5606b814d49848e28f4470322844535be84eebec4803c8c92c49d562858ad9c410330d8fcec67df637c75fbe6efc5625022c008275dbe62e3c6552f33c6b05038451486b1989ac2467c4f13b8261b1dde81c36674dbd5220bdadf24cf619f3601bfc3951d869d505dab7006edd41909ac3996ecbccbf1a0248c8da4d140fd1103ab88732624470b0977d6353b2814e09b97c4020054215dc73ab4d90c9fcec30bfee714e0a7a5e365108deeb9792e09ce8d5fe763495ae979d3140e974003d8f26118f9796a42c4e3da94fe9cd7db82b484f640d315dc1fc21013e9bc3ac2715de4a077d689b73b4236fff681b73d05c25cc1aac5b99bcfa0dd5acc5a4188d91a114e6c01d29628528528ba803b148bfda183df75be8fa81a7bed21d6b6bd9bb9576f4e3de1c423acc4f043e0c4adace62689c0624be3c93d40dd3336c7eb799739a46f1205a954d2dc7589b5b8d3b54559cdeec2d55e70a003f6c0cf02a65452a51162a055e08e87d444cda692e2cc26ef29a94f49fd622a690f3a1b0110cacde6383d76280cb00940a942a76ba10bcb53da2696ee7f0bcbf1ace8a03c743725c2ec7e8e7ed34e926514485ffd4248dce08fa9fb8cf3a4e200d1097b20e1d73c5f817042653f00f39eca6b0eaa4bd69ab7804d59ee0e9a9f76598a010adfc910411b05cc9b8196a1b142433bd6b164a91566ecf1b087eeb6ebd0236b6bf57f3f9d250cbd118888e763c276c3368b040fe9f3fb0f60dc178b36a7f624dc0ff00cdaec6a165563b0278d497d508f94fd697a0ae6f35b7388d30de2d2149967427e123a82804d1d78f6172e7abe0756835893b935c6e81943aaac79a3c800731eb22658096b3d8be5bb8bc987deb3cb24b99609a30766ecc524eeeca67c28a5f95767a8062ee660005fad0f973714cd1cb0f261b62ab7b8f08bd93e05cbdfeba2f2f268d9f14a850335fbad7929b80212cc079867286004039b5d64c066bcf1af5909f46b76d05c2bcf21aa14bc2e034c55e096fb36c101228659ee8c656474ed2516393de250ccb55821ec72759eaaee5e50a0d5c459e653118c07c6728e9cecabb9360b5ffbb0b66dc2fc235925656ec6bfab6a87137ccd4ff36d0ef7bc8880171bb935d22959b9de72da8eea9473c6734cbf236734ec9d3dddf204387186fc0c5b816a423676c8b578c248e492045de3a687508d894c8f4421c75677e9337f94766d7bb26063c6bff59890666afdfc8cfb82b58ba241dd15b6e0b685517984a5ce762c439cde5c2327636385b27ec203039b084f5a30ab0f9512a2f0442639d1f5a258b0225bc3937fc34e32bba3338b270b0539c3341213f3b195bf4ea8bf12e45e77882ef16b1e9983b7051231bb4569a06e9f3528a231b46e59b966ed961262030105c5dad3724e6f4afc030d05867399c6448a1c9c7de5abffbb942c9b4ddaddec2e8f341aa1c539baab28e2e960973db409c1275ed80341266c038096e1bd2c34d34d620c0d4e2e9246ac066ace3a29f684d293bb396009cc0700af0b2cd6068705512bf2d4b80d76264b0e514dd388e5c04f726bccff16157ec35364a093297ec7e56e3dc55d86cda3e44cbc54ee9ff3a8b94b20753678f47f2817f9f0f1578d52cae6850387138774ed190c313ca90acf60d260e2f710fcca03c568ea2ee3560d741500c7ba9ba66c68b171ecfb24ebf98b0e6c845cc22eb4c0805445bb1b8a1895f4f7b8c37086a55c34749edadbcf0d472b57a037dcbe0717b2e99c246a4bcaf4bee1eaab70c72109fcbe9cc67bcda582bd1f5d65583c32b3da4751bc1c3f04d6eb278837b3d066f5d80f4d3866172e4d7993f6a45ee15b55d116b034fa83daaeb57352d52a8e1f43776a060d10b6359b70b691137178fd89ee9e7582d9c44c583540aa82832d09cc096e00949f8e1ce35edbdf95331604d64110d61f46dc9f52c841a07b10e33dc67dd9a25faef39c9eeb701c39cae38f9ccd84ec7d2d897794a6e172907e64b287e0b7cef9a57ac4deda862cc215213acd21d0dd7259ee634232869506b0db9bea7345de9eb90b280b53cc23d57cf3eabe0432d5062253c9db53e11d5317187c86f725f4478368e2f41d4dd54c3e72135e86f33914a1ab23cdd57396ed553028350a3950add4bc30019754b47250744c6e6962453d06865cd854cceaa9ca7eb26709dd24a4e2a118f9680da6f1a7fc4c920a348cdbc43a21161f256dda7fa0fbff9e27f56f806403823a8de2bdcd6a77d08e74f3c1843fe11c9d958ca25d9521ac4ad461b3b3be22106973d0d439f0aae4e3800908d279395dc1ad9008b138962dacce90572cdcc41ccdbd96a5b5de3382ddaf951c7720f34d1e3b792d9054ac1d0da4df0158a3e5b3aaa277d4e3de52de7319778d72471d1b379a83e514e5b7ebc01d349d70029955f958a303506605a5d7d6bcdfd0389a6425db9beccafae4ae7eac5e27ff5756d9158d6b72b828081efb41dc7a71477a8cef2454c133ab2bc469a039d47159a941ef1573ebcb7f04666c0f734cd01b21f2ec9d4e4cce8e02c58c1a6e07f608a76dd11dd7803b3fa6bf3ed0c9402f32d8120c3f6646ceb454293b484a51d5deaacf79722f7a7eedb8cf9ee8264d4f97241261ec3ccf4a7e64fa070afee7dd2663b567455084c13d58be4e7cdeceb658ab5eb7e4575e4e5434e83d098afc293fcad89f2cc6a1ba53c441e48f940cb6211e8604915b5e6d869a41462418a7b2869314fb16f2cd2405291d45845f9fa9666070ca79a6c377418d4a36ed8cd23ba5aa5602c7d1fb7e060d8608200ddb1ee1a1d0b4748a9cc684ccb2450dbdf3c50c1946f4286184d8bb99c6b2e6f6df21ca4aaa4369d48fc1cc58cb7a997d1703f29a1e9a3a4c77e9999d84ef63f12eec118427e0ee572489cc7a851af96b540591fa3548b6a64cd2761df07f870bd60c51e3249cf327be22938778df74498fc9ea5ab39d3a997cd78ae13ae3976e52d63aafb04c0b89f556222d8f38f938ca856e0e23aca78ec01fa148cd0bd91702301679634b2a0fb64a64eabe2437fbc4aba08e8c9d2f839f4b10372b911aba1840bab2d7afdc6f80852b450948f234d0f80c0d62876caf69e730dfa8a46b49c94a92db439642f2aa9209bb82194b39a11b57750da38215f2eb48a7598ae9e0d5bf690865d8b692bc109b07e5104c7a98ae8982d5c8a6e887790ab2ba67201175c3524cf500b5a2d906b7d8a4085497773516ab50a0000df7838f81a6c9814dac5859efd087f0c94bd2c8791e7056641caf72eab597a600fb4eb15c3f85a679b8e370227633873ba1f1b96b040aaa3730ae1e3e76cd46ea3415e03b8c23b7c124bed0116f3e6a16364a051d5553b9bede0726b5b352177c71a59b99f7090dff2850a2eb5263d292b7c96683bf0e244231ac42e6c1dc61729c4f4c2be8647f8fd0a455cdf63d6a00276f002434b110d1218bac2f310ebedac12d9a12e6310dc21b2ed4e6ad60104fcc517091274e952c9ea83858fbd48cce056b22450d347da1cb4c80ec50869abf3a892b4a2344c209e374fbfa7e6e217f6ae43cd05c2cc1351751d8bd92a6b43d8975acb7e323d6a791638e6dd32f864d1b46bf5ea48207c6b1c981e5b1e9d2c1ffa9b1d90c21d57af296537ea2fb2aa096da8fd09772982b25d0967abb1feda34d09c504cba510876f50377e12cadb574ba501447b701a0dcf944455612984dc5c26142a439daa2ee43f70528c55e1c3462f428491280e34d7859ff103f3fd43a5110ad5b5c4f6e732c4929500a4e0204e4e387a095bfb506e616be9502eadd3811f9ab3547284a778f9d630d58324cebeb2128a945fab89f5b88203175ad0bf6e193d1827402c0ba48560b24405f42581132a5e499b2411d8dc17a151275520d0a28a61c5d86abd2ceb0eb70377e953c340d616c183cec0e944624a06b3759e86550c6717fb10dc5dbca1188e4689edd35ba6e17c97fe6a6a496ec8b810d186f8cbec7ffeab18f58fa161e62ca4e1ebc5b8c73e611bc625f95c51af0c88ae3bb5a037c907ea0c4160ead7cbb0e56e741637ea934940f46637686e18aa9afeb06c145f608cdfdf164329ad1f3109e0bcf0cda9a8c1565068d2c3a16e6607a6e9120b88a656b3759599a0a267e658d55470315ec2656e9063d554c84248440e751fe3f070a139f433953d9bef24e11f5753c8765d8c2a08ea4677c75dcab8a5bd12554de8f3c6cc1c1efb96d00f5c3bb09816854b4f648ff2192e05ea2c4a2bae4615efcb12ed67b93493d11a21f4ea9db8b287730e1284f8763a61fd8ad34e711e1c7776283ee49acc11049680308b237dd4d54e7267025a34e42aeaeebabe8b3957eb796de2808a9cc37cdcd6846b7aaf2e4d035697da175c28cb3039e5c6c19c86e93996bee421a2a67d0d6a767b90ae1eacf0905d5e6c8e7c7131732425d080aaa5a10a00480b0d75517c6ad1588938c6aa2d229ac377dfb1074b0c596100b59c3195e55e49f9c833518df9a533269f2b2eb4d3d88fec5b36802828088b650ce4a8d4410626b6d2e96b4a3e7e0ccaa1fd11966a975df49d9ccb11be1d695277051c1f54c14dbf7dd2fcdbbb60d26fde9609b78862e1d9dbb577a2d6931bc6a213492f9b4a0130946b4901396a58cdcd633930c9f88ac1cc7855c86ee235ade11bd9c5fbaa05b2cbd12a1d5dd3fed496793b325bf2fa48118f032e98ca7d3548fc90f3544635d5766c4657be37a2bc41d3e6817257f1ecc1823ab8b9e22808756434788cb186c5c3b64abc2fc197211c97460894f3aeec1eef0cc1054290359d98a001637a3f2cdc25cf7ac5f05fae5a11878bf4f4717b3bebdecacd925e95857764de181f95108d52dcf7ae3c8309669020734eb2c372ef21df6ec68eed31db6f8a351ab7f95c84398dc7f8b3626c4f004cb3a1d4e2b254e4a807ef0610ec8b099c8a27545e57146022206291df2210474339ad58a0101532d1757d16a8a7ded9760370a7b2888fce5f70d94a44da477a07d86556568277e01d1c24a458ff563418b6a5faf59d7c9e62f9b245c167beb5a2a8a16626aec453124a09f9e6ce0993ced93d08f7757394198938a00cb7c3299e75c46880820e23351e3395109644e1304c46c5d661ad105e2954cb7462ea00ddc76a9101dd748cbf3590ad4e6aff51545c4b6eea9e6e684cf3021ad42eb265a620fcd593aeab2988614827774b37c89b24ee5f97e5a103029cb7ebd4b94a085c138b20a031e1f08dc6581ccea4db9750d24751d092a00881a5b4caf770c5181c4110fbe4c5240841794c8c0cef2493e4b9bbe4be35d8f64ca4fc9bb839204ee44b7058035eb6584742dc47bf36b9de1625e4cf170147c768ff73ed0e26026865cee0675a9a7b70353000df263df23a08f1273db4b31df8a95e459bdc1481b181930b2c0243aeb0dbab931048f9166b82f4f2c0264b5a40bc6312c5a7fd4ad75aff1cb25568f62bddcb2aea60bcaf42a67856266e7d4725ef9ffa9463cf8578ef9d4baec3c4431d1d9fe82733a580a479068351c4af2966d4066d3d6b03a8cb5350073f96ed1a3f2a16468195018cdfd4b2b8e07cefbcadc6d7c52c9a44f69df2e7b985b423073bb02f051534923a081940b1b3ebc025c3671ac1a5f9227773e4878cf8d20b0d028a94ad0595e42d57235e19af81ef512855fb6d3409502ec25ba0ac302985daee5592dfe953c32da885ae07e0d0ec7efb4329279a4b25a244999085b8eb4409c8ef6fa552211de46f17edf326fef5344b28022a8c2110390bd9519fec17430271df681dc811dd118f48a8134c22bcc6effe49e1965a39465c8641641b2df61966352f2e3e74f883955d078ef65e2a908d350c3a7ebc7d4124fb79deeaf9784dcf27a2276c84a11789724f76a66f956a89239b9017bc71683ca2a593b6028bbab3c37bf2e11cbc5d5b32a084e16e6035ae6739eb74f365d9abaab9b98ddb4ea8c24976ed6df7e635929ded8ea2d82af3f8a36258820771f4592ce10e1e6cb9a44223c06a99cac9a4307ceb9a3cef5806da1fb9c575f5ca751f3dfb30f7aa445710bc3f22abff51e66ce1ac5ba6a981055bb542066af9a3618317dcb2776da3f4911d41bdedde3658d68791fd1d4a9801d04a81f644b8991bdbf0f9955c861f2b6c58b1e9fb39c6e35b318165722436ac9c68be56240b474e26b9ff8cfcdfefa48be864e23c0adb7782916757cd05881ab73f1f650df36018901838073806e07c708420334467f04710eda98ecc16d0eb027ec2ddacb4c1a5477ccb907f0b2c719bcdc6323b180e9eb2e39bbf19d2fef8167a00306800a652a3b01080369c2546d79e06fe028e4b97b45149d3c18153b649d43df9cfc74b4200c4f5ecdce2596e9c25c53885966cf637fbd2c1f19870b22ccc443a1f0efc7188748ec61092a8c8db62779ff70b24277ade38676e8a3a5c30859a293d4a5be2fd9363970b35268a232c3dc6992bd55b7ad5f4390bdde56516e1355ef179fe15da6162b98c4a6a6aedf5ac8c44d0902cf203332c704af1fb3ee41be46820227fe015831c3d22e484fcf6d38bfa9575e36412b84d4d82a8cdc4209f8a1a0f1c401ac47d83a65a1fe6b0bd8752491c61a2726918d8b02ce0dc053cb396b3c7e4dfa8c9b2b99827f0289a7fc0ad44c49045a7910b7b3e03df6e420ecf9544cb034da3a3cbf7008280a65fcdcefd04a632e80b93b4fe35a429e5ad791f5373e43af9beaf011a588175cf409d91be14b369409476dc604df51d2585839eba3832fc98f663929e0100d59edf24c1acec5ad046ec80946fdd1617d768822f2c8a655c678e338a761f85f29dd5ae340793e9f8d003ff9fa2f5cba2072aef710820a7baea8e0a7560030c701e12510982c7cf0590c6380607542953a9b1d4856c26cd7224e4f9c19c0ebd227aefb02ee66db545841b9e46a3607b912229c791b9a41ae17ec073d5c592188fc82af09aae5d7d3e487714a85ccf6ae4daa405aa60301161171ac209521b2aa123a4e59feb3f23abc461fd0ef02a10da412c76b7998e8cec5e8abd08a07071bc44eec0c53c68aae23b7c289c180ac9c697ca9f9135cc5887f0f9ba8893145467d7aec832bbc741fb02c5589014c38a4feebb769d8c5730e5268845242802d77594bb2bb6557ba362b6eef648137d51b40214ed5d5ead733c99c0bf6f0c7b312c02d0d62ce79e88dacb8a8cbd5e64de26c6325dcdb0ca42f71c17bf41d70a7f0cd33267b49ca7a48bc40bff81f6a2985e6587af0b86f9640348fd21d157a7a10f1fb8a6b7542bd03b719c8ecf4f57ba35507065ef9bb1d707729b3b0a46d782d391f47156603d7f08aa3cf3883d09b4e311881fad1e06ce3a150ccfc47227395ff80214378d681bf2c1511b4f7a01a9f5c2fd7389ab1caff10ae424507b75e7e95a83b07b0b4b93fa051e48ce8312507a47d3d62f3ff8612dbaa529682cac115af1874a193533ad22a60f3126ee6c17e4a505d26f07fef7f1acd6815360ab38f4f7a374b95afc09ba80aed58617476b0a535b845c13f298105216edb737d870c6a7f37ff0c9f13701251924c10272663d0283978d00634da9ebbd4acc1d8a20bd1a324ea623ae25afa2d52406833c67e9734910cb0bf3882ecbdfc86c81ce47c0366fc3ede48c88114ba6c76fc439c5485aecae365c9de62d77d61fd40116e0112636e540c90c031822035c47e104613caa34285c90e3d0b14652e390ecd38e1a94af82ddc07d13f4c860a44385953bebe51643806a16ace5d43b3d72e24f6c126fe9031136ccb3017831892067a53e0e88e8c6bdae0da06a06f13c59b4704a85628973a9b16522cf884647100278f678b9d0679fececcd576d61391f04bf78839e6042859271837e2e4455825cf1b9c105ac8d43d3d165bbf239e0107dec7fd6e9ecd20ad1557d3452a85568d2968fa3491a4030f0ef90eb8a34e17c4df69fca9a7060795cffcf5a5cfc5b4617c4bd79514eadff1871aa8474e266953fb53817bcee77682514852facb589019906b8e870bbc62b8c350a4d46ed550614a43625b02c40ebb14066ed30cca9f651fbeac760338821bb7a977b7c54f97a9e32b57d2ad0feac00b707e298fda3e6650149fd65119b33036dc718ceaf23566aff6904c3551d3fc193bbf4082c9314c8429c023ca19e2043867529a1f2e98e9b244e150b8568e1ae45f6cf5b144325b72a6861111ab3903e37879e060f5f67f88b5c4484dd162fc2a05182370ad0d094463fedf3f51702e2cf9a2379cf8f1103357098b3e984e92c42f813a9acb5f2140076c84314c5b383783bc17553417cee2a700456a0b66770c501ab4a63427cb7e99f296c09a8322fd1a7d7cc7fa34df50ff034f549ba5372e6ea76408f542b354eb0f0e2689f24f96bbdeff96d8508244b41d0fa6a62446dd2b7e1ec75a22371a6167ca6b66c3b5890eccbbeb255b4c554660b897b1a98686db84fd271baac2a14ccfefff541c66819a3a4686889cef325221f99c7ea59e7c36b92018716702fe2f909c98617acc3c18e9b8106120c652985e00acc36fa946d8703b0f4aba35ce2caa56709c09257efc925eaed6cc9c283ac3c712369bd338cc58ba08fb328bb46b67e74e0878dfe5ac82cd9e54cc30306c7258925aa7f5237d3d729b303d4afa799d717c22a09d9d1efe2cb78273d276f91d8cfba78d38fcb4ebf8d52c20811188daf8ff1c79a576190da947d736e0d6160e87102db4c8aa016ccff392b0df52ab645a15f6cc210465286bd4a5f9029b50cbb686f79fa5545ea7f3d18e61e594a5aa56203d99cbe86c57edaa20fae69af552d8a67b6fff4accfcf1dad99b0203808677a682406c5331e4ed916862856151023b93acbcc8867cf333e5ce8f2a52b1cb0e380e646b1cff78f5447cd003b0a0f01fc334d0523a82e40b543eae08a1a8a1ac836f9b53a9d3caef151cb6b35dcad4c686cc994bc641c237f09cb04e33d276b79a5c982e3726438c43b6c4b857161afd452f7853556bacaecdbec355f10f570286dcc730876798d59f138cd05920f680b5feed60757d50e25b435edc80ef485836ff6337037d09540a3757af938f12f140e086eb42041331465ac59be6fe5e5b2367d396f692270d0996c193296b87b268ae5623f28f6c79b4045afd9e8d54d5cd3496a529dc8f05f33f1e4858099b2e854e24a4311341cc10f5d190f2149f265a7891a3f2725096d8363504383677a5036ea8d2f31da5952a4dbb29b8923d76cd830c4fad417fd25b4de8ac12c6bc6d85320f1040501a5535eb772706c6dd6d631dc867caac94fbb9343aa1492df7bb237680d9880bdb01d99b128b8e449a73baa4cfc1b45f0ce75517aebdb31358eb0c6e778c94639f9102670878548e6fabafc29932ca5f928455ec5d7bf467dc45d5762464538e1ab27aff825a4ffa07df6716e2c0a320d5e5448d95a35c43b2b5d7ccd1192d8a1835605b8a1e3306e83b2c115a155e3842d23fe74830f928acb1e98b61cefcdcb783a1120553b675050d11750cfadd13bf5b601ea0f71f84a542d7d6b337502acddd7b4a33b05bf6724fec9af92a5ebf56d89291d99622b071394bc05c9d1ea4a0c61eff09441ae2a43ccbf4382dc9f8166529abf7c91d5e756252c541ea699fe1e0bd70b3bb938c9ad2ee86d50256b6effa9e221c75736b8c86772eb0fdc67d48dcddeb8ccd6bfd0d210bc2d9dd7552b8c75b30b201dcf5d60ba4ffe920cd4385087c91f1733df89339ae313f91847123919b36f45b4c1b817f789112940397852096ebcbe45f801a0c7b0bd718413e4fd2e200525658c5bf49b1404530cf3037efdbd43b70343f6585ac80e0d9bf12c26d4250852d21e3d27db342164dab1c90967b7f584d34c464fe9e2e911da123ad11386a4210246e189a4010c46af321488a1bdbc78d8484ab7bc7e03d5849fc726790482df13ba7b7222cba280ce2b5fd2068f2eb4e6933012ee4d6b3902153b874acb0a46def82344c360dfc72a5a1e0fb09b6c252c397353bc08fb6f3ad1ce43f323c9f25f69dfcdf4f00c06a64fbe38e94fe2ad6a32c0166d0830c59e40ac178dd863667c8d593da4c804d9c7e8d37d21126681becbe56de1cc15013a1e502b0c63deb08278cc38daf1dd1bf70078e2d3e59bef461cf21f3fc84a6effd4a1cdfc5fd9b6866bafe0f48afa90a1b09388cc169ca6e5ffdc050f195c812b03780ba101734003c409e992857be0c9cbcbc2ae575480e6d9ad334e847f6641ada5506fdeb9e225e10ee23fcdc572e6864e0161eef8f5248a29a801af8729639813b12f8a6d04785532144c3daac17eae820476c6100e9ea60eea31b373fd5e894f01b80defc24b6c45a37b9b81eb9aa918ac1b0f83266909a21bf6713ab982e6583c8362e2e853dbb5c7d2d8242f48ec6041f84f97614f0173424090fd0b24ba3a1acb49bac01b3627fa6dcc0c30c5e012ad424d8e6e07eed847cb99cb0f23652e350d105f7716a59450668c1893046e847b403f19d98ff5346207d13843ab27bcfa331c5acc6e174f52e960e664067779bbcaa8b47699336bb514d72f048dc04ab132b536bbe181fea7cb7cdc30e8c4c377b17c8baaaa85ffcc9ccb9d7792e7d06e3ddc484a0230dcbb5c4f89bf212b4eb721e694f17b8f6b43c5b81f815b6036e6d36b8487e30232e5e77f580f989d9988b8843ffb9fb8aab71e98829c26d2808b38993ed66802858636f4e13af5615367ea0c7312003a1af03e093780bf5f511e5719df5bb319cb5debee0278272c1460b069c115bf1b7c1412af8c5dd9d02bcecddf5d68c0401282ef71ca056f23d79c9dc354770194ac709a4a3346cdbb5e3dee121c6b95016a5a2ebdaadc7afd0f519b04027133b3ed3a258425cffd3d93b8f965c610b182a89a87efc04a2360f3e8e915d088442bfbd88977db16612eee5400f3ac543c28460813d2b3f98c7ccfd9565f7c11d29f10151916523039766eebcc19f3f59f5be411027183a1aa9431a4e0f88f8054a198355f1becc27b02ad6b599bb6ee66776b81807368edb64e3038f68da68b4ead5cf2e565fda4b13b91003af528f62ac10c34ee57bb803133ce04a3091a02c3c84cc0e0b915d1127207e29f1cf1634bde55452b2088311bca41c306f53eb608b919ca510ac3b1a909dbb8b3fff5745556e51750579b30ce90eabee6497ff402d641d2e40becc2856bd2b9937528f235c275ef147520c670e400d069243d91fd589d732d0405dc9554ead18b49ba7ec944f87629c486e0616c681e028a03efb2ba762a76eef2d4e67d17abf15bbd03d6fa25cd2a9b13bda55fb4cff8d69cd6b32d82067f19da5d3641072bf83c18da031c3cc0778f8e2aee0970a77f2cf7e0ce5721f25a44479db6e820baf11c384a35760fcba5f25d791d49e94073a75a610e431904b88f00f50882fbd8bf06e51277be9873063430890486d48c39f9c1d02de6538908bada4d5ae7e56eca04740b2ae4c84cbfbe2b4b668c1a01341f024a80ed4a5b505a15a50d39c504355366f199948ea23885f50c9668723cbbd251e5cc8584ceabe00a25cf6c362eab2e6e0d5a55b67b608062089c64c6423555970abadf88a838f5ae51c30362b14516510ffd2a43fb4d6493c84d24b2bb7bd30e6c049e04c20436b896f92f68728c8854234452915a0ccd45ce9f9c5dd49ff3ab7ddbe489b49339d11b8a7632a159c96d2feaa28ae8f3c117bee15a1c976ec0cf5bd0b5175b283f396b179a26c827e55ba4bc8e9759f12c344d90fc3a5e46f42a9a46c7cba8c8e2d3eedbac3eed66363c6d7eaca075bd9d411909cb6af8e793b2822261c876dbbaec0e5e37281ff31af06450321264996fcf176915da3d1a9f0c03226fce880d3740173bedcd1d790e7b3080f0b497a14883a31124c1cc2343086f0fa39c7f0fc6f75e7c8f44720d39ab38f8ae6d031b6622cc88bcc5fd36ba698683cb788c4e678cd5cd56774824820ce373e9c13a27df424dff06f431af445f6882dc7711deab6f408f1f8e94f4589e0dc209cdef49096b9554d62a2b29abf516a5d37dcb04441cd0b25e4a6b2ba6d75f5f7dd259ab6a5e4bab29a7b6de927252907503ca488f4593228c7a624c69bdf0521a4bd8b510466c5575ce29e7b4b2d6789abd41c818669713190a49028f5d3976684d2ed4d8477dbd3531c125ecf311872da6af6c060989ac3f27528b01bfc235d02d7392874bd75b78cbac463643939ef6dec4b0f94f7bb4caff7c26a552425861098adeabf4edc99d5aca39a1db1fed6635eb8510c20927fe146c5515d229259d77ce89b17d6bbf82e6db0987c8d9db8cfd5e7e2f4e7043b5ee70777fdbc35a6b2da85a90b515044ac282acb5d6da7fb8e292ad1ea4dd067d6a0581fe56f75a7bef5b9d92b10b5b6b2f080402555012950402a8aa2a0afaeac7a65f6d37841e3e76f5b4c2409fefc56f2fabaaeeb5aa7b2d7661cbaadee4abafac1bd238e0be6f62dd6befc5185fd7c51883beaa2a7a2f06b29f4fce39672266548c70910b95301deefe26183204f58319d8f8c18d0f144693ef1588101922c48f2142a8605b1f29ac87517d777deebdaecf65bd756ce5e7fa5c178cb884c37e0d6ee380df82f8635e65dfd2fe2caeb2dc44f64f72c4de5b21c1c8c6ae52ccc397e2d2fbbe17ab187e9886cfad744e8284276cac3d7fc52e092aecd85512537862576f73a5f6f9bb360c6cadfa80b04a4f2e2e55d775522bb56acd6e7faadb9662fa57576f7d573b3fb6db17cd2eed36e643c56e1dc24614c51345f15f78957e3276fe96dc84def54de87128a4571ecb7ceb184a0565b3a7b5314fce0fc5256bafe352fe7cdec60bf1e76d57e4083451fb36c11f89ddbf8e987cd529d8bbcada6b551ffcf9bb9d10546a63176b0dbb54e7cfa88c69b6db0951a2091b6f25d96e57c40850d8f42d7633fe9cccbb5d11233cb1e9db4b0317c8e482f087d65b41d704c34c40f7deebb2f7de14f7047f7ed614f7edcf473bebdeb729a5d785d116f0b02841bef7b15c2f5c38841551e728cdce519d726d55297598629c5280e9bed2af9afea42e535da5acc17921046fad9faba7150eb8b34ec9f8da6955d7d7bfbe7eb53eae3ead324f76db2971d3c416d1f0767e9a7c8a12f3223600caf5d7d7e7725d55b6e5d835bb9664f75357f7dd475caab47533916b71ef78b6abaa3cadd729753a5aab55bdf5ae9ace55576bb5eadbea2c2bab545e45d8dc05e84cd80b6b856e9f28b203ae6bfcaae357f2e54b2d0add87f960f9928c6449deeecd56522a2584904629212542b33717fb59e83f5dfa7b8ca116d83c9989218490cab7733a3a350f4f7b3e03596e4096f723e937b8014f06673b6024b8015f38e02cef7dd84f04fbbdf42032920db896b7c3147ae4b4d99b13512bfb4e7830ca272c5eecf7db8a9c85eb6eac5bd97c465f4a64b4ec36abea134af8f213e528bfbed4a59861ad22d7f45a92614662df08be40c059de27d9ef371214b6b3c27eff00d7f2fe866b91414672e369ef311fb20d1b5ee460287c3884676f2e8afd1ed2e7ea7b2fc405218ed8f3dfdbbe3f6b0128c90e9758829043ae11216f23a723041222a89f7b95952c060c13b27b98cde9b28799e8e1743038b89cadc3db381c8a1d71d0a16ef939ccfdf684710cbe6f1c2e8a2876cc82c84113e5d304f1d7a1c177ef2285dd1d88cdb1ddcc2c003f3e8f80a5d13192d1f1321ff0286482a4d050778e9d63bb8f6e01d67daef8723055e790811c3b071c34c776b814c56e61e30abb425f46a41a310320c001eec3fa06c8c2e369b092318f3aeac283d3f88ea78968763fcef7de93fe64b6c392bccd4851c004f93e0dba6d460c84fecfb3f8b419a416c4b7e3f178a6bbe10d39ecf05c248cd5e501b7d7a326d63ccdc7a301dfba94d5d65a6bad94c65adf5a6badb556167b3c0d42b13914faf1781a7cf854bf9e07059c793b3e2fc89b4301c57371f16843737e64a763e231edecba9d8e0965ca79808ee96402b29d8ea9663b13101b6fa763a2d98e8047dfb3ec2fa967af5691e83d0743add9082c243f67d91ad430fcc576f0dfcc8c3f25acbf00a4909fc3253f226f2a2a2ba4183566bcb84c10eb48f0c58180663b1d1d40f6cb763a3fa4b6755cb2f15c66c418f1448ffdbe864391f63bc9c5e5580109978385d47e5fe321b17fbf8fe160e0b6f2785e129450a2081326ba5d11267450224e3dae18a37b8c1e9fe83d27d3f7ec629f33c619618c32468ff2212e418ff4cedf28a55f69e51801a3bd8576b2dba34d33774397cf1963ac2a95d1b2ac65552fbb8aa841581d358561a12cdf39d4761cc83231f881d66893a025c0019e4b7e571fe793c1878ee338f46db6917ae83b7d8aeaa13487441a8d34ed5f34034d94866674e6ed4b3f17b6dc1d6bfab6d22d2a8128facef1475cc2d8e7c2f1c82681a0f69c3990657ead398b44ff9a361a45257bfec47e9340e890a7cd0f69aa93536bce22d1bfa68d46dbccd9f37186049ce48817f6491561222c58b2e963222c70c2c634a33bf00ca5b1ae0a9770cdf8ad09f6b9b0090863ec628cf1a4a04bcfc7f446af7e17c6d87d17068152a0a05cdc8299bd552ac09ad24b48ddba4d10c294e505f8ee8b4a26866fa312ecb6c30ec82173a38d430ae35c22c24a90e16f195a5978d6a9b4a8d9ba2aece2dd5b2f819ed2bf35484fa381827c7f239dc87a6b0d3567faf97c3e1ff328a59f9c93cceaa2689761a13225ca5b2b89b522ed322e740bed3029887a234aa16b5c92c5d4a1bd4ba43090f67dc17741185ba26757fd40263530c9e40221210454df0a973e353ef6b9db8c629a5d4e647a820f5d6267577f3056d6bc18e05ae6a35caa53bc057ad0bb5d99bcc9bb6d750a0dcf6dbb628926ec7a23ba4179abe29ec4d18c94894b01ae09d96fee09a1dfbc07ed37ffc0ca6f0e8293dfdc0530fce630d8307ef39bdfdc072e7e7320fce6426eb88ea37678760072c2c9a9f11ddb4d355e53e3364a70526e8627efeccc9e9a9ad90359660d64c93a86cffae1e03c9c87f37620cb7c3d9065c29a9d9db7e35e53536b6a7626dc812c13f64096f9383b383b383b3bbae618b4db5105cea5fe5481d366cddb812f15b553513bf35f0f7ca1a9d4eb49bd9ed4eba9812f1485aa41d5a06a70e00b3d9d704e38279c3d1feec017ba03e1cece9e0f7be0cb4c411f0e26fffc87d333610fecd95906172fe4b6706ec73168dff461475454ce343b81018676216d85a660b8e0452d8626c3c7bcb8610ee593b7d5fc95af2a19429614a9884e422193d7d5044e9b9fe913ecf9a819ecc943d260229f585a8d5826263aa7f8ac2d3d813c6d3e48cfd7b3e669f3353d61d0d3859e2fb40ea2ad3d0cad8368c3f815ad8368af3ca67510edff4ceb20dad9575a07d106d9d8ecd8449b1e9c8bf6f3e78c8361897586c0b9c0f8f973878389e167ac354f209ccbcacf9f2607f3e2e74f153897ece74f1738977f39236d204b8d8381e1e74f1b07a35fb463338b702ea09fd2c1b8f8297920cb7c51ce39def36a6af6ac3adb7b5f126aad24d626b13789c549ec95c4cecb42fba6299f21732e4345fba6945294542a95aaae088542a154dc0b97688b98035f280a9583ca41e5ecf9a719f8424dd206becc13cf89e7c43333434d33534e69939a2f73e0cb44491ef8324f2edac0179a722cf35139a81c54ce16e52c796acd34355f9e5268df2643a87c4a906233b3a2d2024abc8248d36460d1301ca0a05818876f160fe3619cc40063c58aabf236c5e4a668dfa1172ba7d3e904c3052917c6e13be5adcaa5ba9a5668df263064ba9a9ee69ee8b79c0190b9c7830613d955f28ab72c2ed5ff573f0b16208cc337cadb16976ad05b5cb20ffab800b56811c2387c8bdea6b82ab4ef79822f1525caf994d5d3a5ba456da1b315f20c79ab50a4f6fcaac773d9ea129ae7b2553daa9af9d58e0ac55ce22eb504aecd9f9504ae9ac0b5cc6fa1abc9b5f915957f8a72ce6fb38ae6699347a966158a6a860ad9e7bb50c1387cab785be9556be05ae6db4a753da29e50906b8f141ed5948287ad9f900986d94a5d0a8be1b6187571be054948aa413a39c93e781ac9d3886b99a1b72737d7fa1597aaadbf8d422626b5d66ca29e36ff444ff434ff8230df16f4b6630cbba1266aa2266adaf33f5695822ff5345333b5ffd2156bd326a289ed36095ccb49881a2406f534c49e4fb37adaf33fb8f29842de26aaf2d8f3276a1a91a26b73f6561151bd781008c2ba95ad3a79f59bfb0112560001d86e63d1121b55db39293c1dc8421a59d936d2b47fd1c8079741e95b4f25fc410a3bf2401f7c06e9c365bc278d469af6af13757c90d4f2c8035f68cd5487ea501daa4391b817a94375e08b0402a50f9f4122e132de8bb690abb01f75628bb204a2b39f44626fd2875cf2c312f6469558b287e06414c1e9b89619509d132852a9b6a23287b2a45a0ccdb2e40fd7e2b8247fb89657dfe29b1dfd299dd24cda3cedbd0e7ca1447096f74650024dc2e3da396da349ecf7124891fdfe5122b88cf74ee420a149a043844087407564903749757ee0cff5246cf656d12063eeeed5491309d54a77762d32d4bc4aeb0f36ba751d8eea631e0ef6e9c7bcb90b283e303d762def4550679761882cbaa1d0cbd95b4889ec60721332dcae084e137b3e7cd485f115803304c6874b49885b49aeb62b8283da95ac605555959cd7c6c39e598d8ece3985b0ef3d0d8c60c7856f071fb529ff7e942564f7f006fe25172d58a8a4ac4011a9487112320161f973e16badaad229237c37f063de7b306382cd7492557d5b225186d57b3bf8c0a384488f144da4522929638b91f6953e199dbb6c2d52b8ab34e67db6d50de1abf41d8b9307e38a9311b37f1a8c99b6e98721aeac38edff35389d8200072719b3f25eb06b847348355aa5f896045f6cd8802f375c8b83f5524bbf3a5e3de95fdac948eddb7232766aefe1b675f3f9aabdc78e97b91ed3343a5ee6fa07dd4d19d7d43893efa00ec6a5696f4e032b454fca9aa59c9287685da758f92996fc944a4af7397dc619e743e8526aef61ef68c6c8aa2eab0a7eaa164373298358c10b2c106e32597ce5909cbdf9f5b14fcfe07a9f81282050a8f836274b581b2f94a3a2b382723c970dcf657e7cf9bc7b6ae303389628851a7c01fdfbd7b411696343de8b9b4059356272264c4d60b566a953eae44738ce63813b066089377328432861bd55feca57559da2593fda7120cbfb4bbf93dbc059de9b88e040dc06aebdbf2107fb3d911d42d88f07560d4e27223ff6e654a0369e0b8cf6b1f801424a2db450baffa63e4cb6aae857d423a5558a252774a7d42975890d20afe0811c4b3a50d50a4935ab2aca5955b172277820c713f13db0290db54e1e3ab0e97b759f6bcbb1825d1f42f798122b8fd1b2248455156315530ae02fdf2b5adfab62b6d19945602b0c22b2c826db53c69d9c5a2bbf567c7f817b166e06b77119efddbbaf6af98ee7bb4b49a65fbd755195363e83077132de7b90fdbcb20ebe256e63fd2a1dffdec7a513023cfa281ccdc0de5639630650611faf24a90609b3277e43023b6e7d34d243e79452da702859ab472d86267d56afe3651ce7d128ddca55e002f2e9ac247ed737a0bf380a99d2f8d661f9b69dff6cf419a729803fc631cb4d64cb5e0cfb98274b9685a120bfd84e2705355bea7ce488dbf6e4620dd23441b0fcf288c396d7c0b5f8e9942da812f2b17e22311d3f6bc779daa341ae70cc2e2732b5212f596fb1102342767cabe2c4dac16347c65b498e6f3107310cc3b047df618f3a2771894bd563782bc951668ea3c2e9a36076071e52a4c97b98fc3d951ce35f5c8a37e4f0b4f7aeb32caee304f8c78f568cd5db5535010a5c17684efaf46d8a1bebc3ca666fde8308ed73f8de8c260ee445fdb6ad3456af799a0fafa966742097e2527d3a758a4fe94ee9cf38a7fb74777fea3e8f78da734933fff1b49891f8e9693b2fc99bfff01fcf055af7d621a7bd790f54541ccc8b70bb008a8074a5e460e65f98c6d602512f9c0d29df8fa08df73b5ccaa1523484c88f4a3921a4da4f4f7b514e29b5185a554157f1055ccb2b5da05654cae955cd625681a7bd1d3630e960bf1fd83c41b1768fc6e63bb08115f06470e6181037c01737c206ae6dae83192fc2b53c07e244de9c920a5c007fae3c626faec48a8359713070db97f281cae6de19aec7bfc18f8bf51b0cdbc5707f733e00fde68080fde68498fce690fc6f0194d3a8240d5a286a0a158d0400401100e3154000200c0c08c4c170482810865d6f0714800f6d8c4a6c409b0a64c22487811004419031c6180308018018008c4186868a373ceb889ab62e7f4f3c07477f6c2aeeb7cc32c893a3036b6c62352f04bca0129a77ef0cabf71f802d43394a46efbe749a06bc366826e0d686f21a2b406e1d3ccab70c3c151127c916cf3b8d4825ca1bb11fae046b392cf563366a5db455117661bac5b6ac3ddd3dbaa744009e24489406c87c29e2229b1283235c40cdbf00d95df90ed7001157b4011121876bfabc3ad98811cbb04d5bb0065327b485d198a4e5a9b24185871c0f8e930ae5a2097228a321e8c082d6436a0d0bfdfa0e041b1a9bce2e31462c78e425bfe1e8bd545aa1144f1db7159ff8983626d49337a1a090b2813bad3483e8002fcf8ce3b4e833705d26778b037caa71058d0e33118419c4fd922cf84dfa1163cbc9ad25ce4a15f6780033b133f4f598c2067db1f9358004f6a8e88f09b2627092807604c9e7fc7fc67b5d3be0b696a4d9d708ffef83bda27b3df82a9f60fd03c83a7cb24fabea80e999ce1502998adf61969f98fa9459011cc4a9e24c66cd572f4da29975db435cc4a94422e732b3c0b12ae69ee21d98e5e50ecc14082ff08c3328410ca6f2ed42d7c1dcd650dd25c2dc64e40de4605d86681afaa6fa0bed45f1ed026b274ab37a169c80a1a1049e50f189e75dea84ba4487e2f31a21e07bc0e19281957bcb20d71117ab83c644eea086d73e3f4e754a04f037c5c425a20a4158405e43660eb97e3d5c0149f24554e2b379c70a88a4ff0370a9f5b9e4b1529ec921b0551f503a485a0db694e2ac3d20fe91bb6239be4fb544204ed92f204dc0997b80b4e569a689cef3a963931a7f4efd3ea0d9d83a019314f9011081135c9348bfcf6d907a7b05a2b37cfed4074d5ae5ddb3e1f49c4a0ccb2bdff94bf1c40c1a82b6841f53ff7f4eb138b8a07054ae1b9d829f1db0329764bec06c334e1b5f6dfb9a9446877912a220cf18a8ee39eddeab0fbd1c37547dadaf80fe88652ec950e42aa20d7a72c4e70b65295c9e86f8fed0f0a20534203608a21fe93ffbd436e35b150e431ced0ba822272813d595b30dcf3bd3e9f6ed4381c6149421bf37dd148cace07d2c66a5a82cd51eb5ecd07216a4d85187bb23be0c6926cabe8f80caffd7a7af5af062e93b237901e02b60fb756c5addbe3bea6d8c1e2e95d5f1a1e08ef0824fc40e140d13e78c4a7329ac513e017adc30904607ea2dc5ad315f11daa130e4faaa7fee644c59a19ca17151a913a49d9fe7de933822722d90848c38bab2475bb50941c661c08f6d0bbf73831697b0636339951060e22625d483ea343ce33868322d16ce53d9d596e100c661b8a51923d8236aa45f2688d8d4ec98a8b7d8689caaf837f8781e36fa7387c79099991bf66138d5ee4018a73b6ec84280ebb08f8d6a5f17eca4d1b1f08ce44368e829bde845eac8bdc7497bcb20af037bea923a621aedb60d95c19deae399e99cd6c1dd64997d661c0706b43b1433dbc4cd0cdbff3c2173ae084b7b010bc7118aa108b039dd5b61b695779873c0b8e5319b8ed65c36a00637f123246314e5cedff23659eb47dc9684c868164733f1f96e84d211a88a66d6c7d5ac6db0972be4cf9384dc20b43e738a3e769d0764304be9db95861325240ffe5a01e73c0416d8b53580cd977fd010ea93c427776f1a71fe86c7f9db27d6ba38e30b449c376a3814a5cc0a9f72d1d322f8c9a1439cc418b2d34ada2ea969ff29fd3ec6a84556caa597862a43a5b47439d1b15745715baed5a4708aa1dd5f62532ecf8e22f05bbaac09279982720ca2252fd4d3777ae479ce1da694fe75fb8f98c07a8c3528566869df9b9e5f8c6197d42293a3c305b1da24ba1ab842516b480016070d0c2296024bf3c60b52fc377766c088959cf70db764a2bef9cab5ebd663ab646cc73c8d18cfa647029ad8a7bb51439f039f50efbe1db43533f01f9fe09bf60e90fdcb795e7f3e9bb1743c023d47e9f4afe8c8f12a0cbb5a2c556f3f09dba8370d18c83db9eebcd8b881de6acd0c1c6c3453a7cac00c3526b138e9683351f00d87c1f71ec4b967c181686348bc346138dad3f27edb4d1bd2f267748e6acc96514c5a61743e926e1e933a62bc438ff37dec10bd15c15c6f0d92238c3b3ddf412474c4f1042b6b55e6a0e111f58a8ee214bd96ebcd93f191b5b0fb4ab3a2fd0e813e28a1e94ae822131f2dd48ff6b017579a49405d9d2d4309f845e161714b681b1c37b507cb1332bf3113588fa8f71999fa8b903fb3aedaf66dc0d845f27114eada909106ace09a6b1579704ba14f3c326d9f2e33a0277b2c9ed0755db45635fba783c1c9d432356bf11bd80136c0ae7ed67e1f95f0c4530179ab126305c3957b51b16fa70afe4786c066465f59ff707396e508aeb5b30493036b031f7f125ac0f9658b86e0019cf34078e109192a2a43f3bc64e00db846f7f41651f1a582273625c643f6f292d0a70091c0b923f81ffa14cf83d3187e52bc5419cf8922b2fb1fd72363eebb7486aba1f1fd9b2f7a7d3f96ee746df7486f4b73bce5664083b7df7bd396cd44989559293f2a0734cc3d77804613c828180efbac6f0e69b2adf797fe64710043ce58535fba064bd5c980a5f2c7a750b155149322c6eb361d2957486aa1e94b7044d5510898881ad0228519ba8b808057b38f9e2aa09af7e3d82140ff7de5a56f3379c91a62b5c3e53512a1d3ca26e2aabc21dcee7c2b68935e993b2500784ab651afccdc1ef90f31b3a7c400058e5bf36be02d07f250dcf102bc52fa921377338c048420e4416e40a7f83a7373a2f2ec0205bef7d0b663ececdcd9ae78db71f886757c41f482152d0fb1ce383961c435e035101b998782a7fb2b310463894b02315f1dcce191795f026986d08ff9d415d2dea7223dcedfc0a27dbeabdb9310fb8bb11f9788b43b5c187889705c826a34d8be826b99c1f0ad1b05c155fa63d4d853185abfa69f7907f0979954a83d2d589d73f8980379058cc71be7c2ebbc5f6a183eb5565646e0453dd386a6bcb74ccbfa7b052355b9077e1ed2b58f156fb455691b3693923fe31f365b932b2d847449a3d89c0f589a16eb476cc9de8c09311fc40f988ac8c6d7a87aa8319d41734106a6921bb207363fd9f77057f39052e9f0846ba34e5d37a455e836fa964c1894aa3043ca2041b2f71523648b7283c7ad32a1ca1d010630db6d975063bfbafcae09261544cd3bcb0a4314cce609f42060c052c2141e1d6ac7b1da40549fbf893d07ef27a3285b355e2e1c78cfb8cfc1fbf368538e0d73b567a0e9f1323f244178dcfe53c46a906ed029758c18a6bc5343829c7b354af4cdc9c212e76cb19f871a3432a06f4d47e3406ad98299b60a238ccca64829a9aa0a66d0fd834112a00c0bfdd4efab4c10112121832f79cbf614886e9ff5df8435598dfadb3c551ab4122cb80f250ec86b7517b445fc5b4945eae6ba08a64545370a6e25ee7aa4c51c19c68340ab610cfaca404b4f5b4aa0a032e46d641f36a6233eb0df827c1b9d78904b482096cd9c747ea3a03a1e1ec89613b572fb8ef406704971250f932b8a89d32fe8834f89c22a31745a177ff0eab68eb8154f77a23f4d8f492b5194685237f3e8824cdb901ccb0ca719e40d849ab0bca44c9a8aedbea6b0d480fa23e5d0e8101cd7b06cac46c3e47899dcb8c5d48ea10a9716ec876b848b53615aedbb78caac651c5c80713b9597ec14911516a5701741f2f3a541a8e6799e13f931c159a382fcc914fd5b3160f31ff247d4989a20b8517f0ba342744c17cb4f567e68138e736d0462a1979cb8744398e2f5d81b1a28555847e2661a030e4afe5691480f364ff3992abf4bfcc3fde743c11e35a4cb241f721257437ccd0f05d151bc1becffb0e372723d358448a4ab4eddedd598048aa5893143a122b00a28a6deca0de8023bba1ec1184fd846448ade72335bd62e80f4ffd6f28cc1cb3740fb5a199238ddc09ee424d6b10f4fa9a2f5ab923c927f9c3b3b2b32ffc45418c04b41c5733c61e67b3549697bf3591c67a73e03c6361045427f4acb495433e06889880086e4baf9b44f54b6775a574f68b3ab9189091da6ce59843b416c636e15ce52eedc79afa5778723158ab00447b2d9011bab3a4ceb2e156420c85b7cca6a804640b318f8ca5bedc2ea9959de2710bb67f37dd36078e7be747ff41cd143273818c4d9004b6e15a8d8f513e2621a8729177eb58dd03bcfe7eeb346cc3f20c9cf762df8afcc32afcbfd38aeac00d6bac789f9745d7a12b79bc2cd0986cc6e5a2e8db8a9b143b2898fcced2b83e3c5ca4fad55098207553c0b1f6cce25b0d102fb3ab1b1d339062b090b36a46b22aa1082902057474d86f398c9e49cbff79037a39d398b2d290db17b27911bd880e047eeb49c6f3bddce26510e50a76647b4ce67b897082fb8964ad672455198e9e0ff9a147e29fb1aa88c825e88c40d3a5646d49dae200a24f9e1c37523349d592e7ed226dd8cce74b31a4420d410af150471a475ccb1e244b3767e0d28d0b1f5db56ef34847ba9bb1b035cd6b607e518dfac5fc84160369bfab727af3b19d3a2055959f4271b11f464570441f28c09907862ff087556d78e49758c0fa663c008f9707dfb250c00c3c8c47c77fd920885b1b3638a036e6700e4db1320de0f5aab859c5cbf18b4b77d90c7c53c31760b7c142ef9f1c08768f3768c3520cb8659b7c82734881a73d7e2db4ecd6f611e21196d6098fee9584065e8a3dacd81a0a95ba39553f2ba99aa5e111ab7975a30a066d2203a08076863ec77e65792f2dbcb713185a718bde649c107d178d777d3d78230eb6fcdb205a56cc0b4ea91f86d4361790d13ee3ee647cbe113f23f6083c20cbc6df6a2a464a4b868e469f313d1b4793874903ccb86ae5e593cc6744f5f6920b20f71d6834997eb1d9cc2c89c11444bec61de0c7dc710cefe6909369de4a8a0e43d578d08e7b628c84ddeaf99884b682ed9607d6ea5267e92f1c4273af73ebf119c3cc4d4f010de64180962566c1663ef7c322eb567da0f96e1ed12f243110e1f90bddd08c0d8750e45d37613718171b7e7061730f66c10b64798e1055422065e3a33b791e7d624e5432d21601fc81f61791f027bc0fd10a0a4ac6b3a8809d196261188039169cf35eaaab489284d7cd1ecb14cc19806345c308f532022dc237ad8a7b3bc126f06fb25f2b99348e6aec6183a2c97ceefaa6496d971fff5f2b1940939bf46b06a2cc455e255f9c48ecffeaa87c67480d42541db3c4fcb47136e9c179263c5d0980be2283993c928a05a495c44cc6449338592b5741f6f3c9344156ff1c791d55d766c4724d807e591cb6da38d1cb70366dc4733c91231fbe08a134dd97b36b706d204d45e3252930d636f0789a5148a1679f80a8dbc4f08d926c58964c7e5385c0ac95b63c9196ef1b1c65724aa7bf9e983012a64ee8816cd2f4ab27e5926f392bdf145eac6b5f9d1a8751647f927ea7b2bcb79bd3071b71c13347eb1c09ad8693d6c865dd88fe8cee09d6dd88b7ce38ff246f870049b7c5b5af8c8251b62370088fd8f208d15f9e090ed8e2e822fe037087709c320d8003ffabfed9fa68c2cec12302df112af47fa053844034d811add844d2f7c6614cca61e303173b59eb96dadc3db381bd824464569a69398fe99537bb57f4320fcb6e00747c2c752860fc251701398d37b3e9e8b2918f06583f6bb7bf43d9644706bbb794d40c675b1d6e73e6bd347fd4489ff9c844154c8a63748e0db0b177aededf5629e7b7a6b984ddb319eb83f852c1328ff80bba0fbfa40ce2ecd0d10f80c0c3f31484386b08fe0893d1ffe7dfde3440ea44df8caf88f99fc802349caaa7863b7f11031042a930c0cc11f244de108c559033e0c8cfc4ec7a3af8209f305cb2144ab66316f8e8595659002040b2fd6eb629b051d86645666ccac6866f2bebaefd86a096b3edad27012bda6ebc5d62a6afbfd6540ada5e235721510e34825a8f72d67e23836cf36f505c011f3b5747e1ffb4ed4846d230ec82ec6ba274a11e7180ae1ac3f0173efabb17b86105c52f05ae0e2470208b9ccf444e0e952791956e3ee33ebf1032fd13bf7fba73b0cf57a7757f1f84133f004d229bfdc63b81996839f5b302492eda84056a01a8649f81400d0070e618dd062ca6e15e15b425c2ed0f6b7c16a1c35e8f45f529b0b65695c7a781131d86357924f45ad0ba70c89f5bf9c9de118258d112775b87601a33294b007a28dc4d10f217ce233a90230f72ee3661766054eb2b4e891a13f1e211bea653749d67e4e1e916cabbb19f21961c2c8b77aae9172b0df7e2c96e59cbc70579e442cef270b281a34ea4dcc4d7a6e6dfe7f694c447b6c777e8fddae9f45da0ee07068835c4bba751e8dff57e3c399c13d7f7099fb3f1f6c9f49ee71f6df7279381f9c48822e72d8a11e3930fbc05a27e1c2a9aa952853e5ce6cc520bd4bc1071e821274803a3ff7e04823ba2bd4ef97c4119ced7a551522f3cf6ebe918bea12d05f80763e16e18e15b1812f2348637dd3e6cbb5551d8e43ab12507967561afa534d5093263f612ee03c1ca0ccdca97331b45bfa78135669d4f56efb6d6c5d1f0a2b28437888225c2f2e8d3f320aae8f1715cb7d8957cf8a6f0ccbe7accea70b08f4f3b86a23ccbbd29cbb34086fdbabd0b1c5159a515972c3ed1b33f8557e21a89bc15a9cf05f4a86b47892d7ef40316ed426cde968e788caed2171098f8eccd818b7fffff4644ba0005dcecf0c1c0b19eeac201b42283ba00b1b0e11cbc58b1d462b0f82c92098ae6b1c0025bd567c878eb6d627b7c0182002149e173561d0fd346103366ed8d2442f8229a16f9978876537c37588efb28a4f35bc355a5241bdc69c0699caea0a793227b41548c91d697a505cc29cc258fe06e79713483a94b7579e9606607faac010654efa8b2638f65af4a0af25c43d556e61eccb60f4b775d5e6f2836d6c4e23d33919cdb31270d50792c2de48a4e49cd60c376031e6d3214935f1654e2d48bdc3c05a1f19fbe7656cd4538a944a6651c5f03944de7034f76d96046119e5afcc09373ad7bff9c5bea1e7413c26cb86db3113c048f40f3383c695020950a7394d8276c917157e14db269b110bca63b2a53f16d46efc227ea41706b1115b9766058cdc8fcda5907b1ecc03cd85fc15ad95c357d33cd4a6c9ba7da08d37c280e2d73831e9d75b233cff7071e6e17dbb8a6999029f861aae3bad64edc143a553f8a93e2521d547faa2e1a3a4ec0050dbd03129b27e8216e2170f92acab6f40d55e578916230449616379ff48e93ba2097755bb7ecb55168d02df29ead7dc46400c48673598654c4c2ddffc8be8017b83a264cef84a9e36eb874dd59ccd28ccbe4d8382a8840add548c9ed8464c2a78a82d1d8de07fd504282bf02539faf085d487ea7668085aea8e97342809c3c7dc3d974b4d1eff23cccefa5d06d13eed781079a79581273f7d4b7ac3936cf5e6c66cf005586924c86b43f6f25c7e947a13e154625a11051ad6a0df5f87275cc406b502a5a73d7042258c72f1a105136e51c99d9c2d99a2370298263188f1724406d380a6c12dcb1454785cd67cb5804f07d407f0b66e46f99145eaf37ef114600df27abf703f0ddf2249190f1240b81c48bd7bc7f1da8c82368d810d70e3d5ff3778e21b4711ee3fa5403b214235a663cbb43d0d4581f06b8137aa2029433a3a2483101a708d5922c10b7941755fc67073d64c7f0ad49a4fb42af9242d05196651cd0c8020ec54c40a19330400a199bf867665a14e8bf3c903fe1c672920dcd3c08bb512ac50fe6b0174bf0ff0abe4e3201ac707c619f344d652db3104f3206ab99d6f9cb3808b840651e233381ba9915a7e560f09cdbbc2ec153453ecae8ddd478c2f3166db0facfe5c5511a5956445e756046504bad261c07571cc2e221f50660f84c15193000dd4ce367953668d453aa385921966293a14dc63e0d5a7be42fa549df57f7680c1d110ca435c558b48702dbc83ba44096ead39b5c2f2b77df54d29f88e214b1b3eadbe3dad7cff04af8d0ec031ce0e24bee0132f8e31c1c2d3bc86801180d474c45a4acadfd8e828b17ce6ccb95d8590cfc2b5cca6abc290b2b895776ecae31d9f082a440380a8589dbf21c53498c97ca4409cd8a780a1460244aec8d4215a42a8db6242a80af43b9944db18b5074f11ac54da65824a8f4d16a59605e5c90e1e4ce393fec98dc6ee49f214d638c6147c7c181803fe1c4382526bd6582734d264434176f0fbbd67d22f5f76b74300794ee4fbb0b506f0439aa65bf83b4d0abea59f338d22f809658ab12af9587e4a765e34692f79234c64223806e0e95ef42e149eb15de4ba434cef498b8dd3babe925120fb4f1812a27342c9f76d192885a29bfd164230eca0ec6c04b4dbd6d43857d6a0255b5009201abaf38c09e2b6cb05c4c0ad4a87f53587fac32f19a66ef9841e42d195ed08ca0d931c4c563ecfbf5ba0396a35123b4f29f6c6bcf92deb17813b6011dbc21567eccfba2a53c5e378bd42f33149d69dfaaee0384377726aa0bd87f266dd41b5c9322db62842d30780021855e9638255d979579c765efc104254a9bbf04d1a8a2d1249aca311e20aaef558e5dc6ecdf249988e94a8c8a15827f012151d940a2f1723f16fc6394cca2f420fb27c004a10d129c659e82910058b8ca75cf65863df217665e00ee3052af4effed34ce36a3638d7b7d45b1c1c0a8063f0bdd196194589957147fdcaf41e19b704e88bbca560e6c0a2c3c51148c184a49ac0d87a1229be9bf5801bd57aee47289e78751391f40b74689b9b8e7064d2c4bc337d43f316a9069d5ae65d35041f3d4918845a048c4226182d38dbe05d9c0db78a6772e9cfbcc4b946f578acdbcaaeac0c8bf89e8e355f68c9230610be001b5ebb082eb6e1890e350d45d5868c047a52a6bd338bdec1a41b0f30da0f29087730e8dc2b35c17436d4c97660bbb41481f6aaf0845be71b1603a702c1a7fcb2706ec3a5bd2cb7ad85b7eb7eb39f280d2e7fc1ca497e03a0b63cc9f81a6aea6569128bdc2969a99db0d06a5900ef05b5ec459ef5f3b3d9b5395f219c051fe28b09a86ea992341c7e7d0ce58938ab091ee37b2dc331029b5567bc4ad1e9d139ba1b43ee93fa011650a0a0363d2c6cd0a989bb2056f44bb5de4ead30875a6eec662ec85fb93020d82cdc135a00775769fe8e0e6365234a9c7e37a1df9891a7d838802dffb3c4bcef6e6167ce93e4280ae49cc9bef5983d9bffa43037ebca40b072ffa50810a7814e473b2b6f63d151736a9f9b63f856109e74fd254b863361db2b279437b7c9f5be594f3458eeb9bc3308b66b98f8539cc3a801bc88cc54863eee84a847b046dcfb5ddfb04f76c206eac0ab0cf071e01e9d049433fa23f4c5667d2cae9304763d52063eab41df92b04618a48e957b291a72d21e793eb282a32c32f416242c606ff3046d1a3603ab3c5db715fdb138f1172936e7970a240e4562c1ba186ebf677f35b0214b1b96ef5d7a0f940b38efe025b7bce767c89fa45e05f04b80fa594b2817a7c4dc636fb709cf59b1ab8e1b6068264b5019b21dc76a4c34d0d28be8ec949c5d7824e46f7812ea852a9463618b380c4262949c689afd35dbc5d48949a3a6d72dc98d258181e2347a4822d93b2ce23c944c99ce26a8ec2d23ca25590717809ce943fd4d8a164e19d739492782b6f80a31bccd0bbe3f88b22cf108c5922ee1ee087c057c55725088fe3e73fb6fb152d3581100c14f32614554c69a6a47cc326c11c902149ead27c98dbdadf14ee513345a00849b93181c9a7a7bc4178dab5a97607d962f8cbb0b8b848b270e53b84cd3f1ade4f13daf224361464988de08430cbf16bcf48f0428deb12d31fadd5a3a80904c874a910e4a302550e2b22cb4dd8439f7998470a58b07d758b56c6f25122521f32433370c74b5fd402844279ea1149a65aa5e4cfd8c55048a4b91cbbede05e60aad7fb5a2e069def983afc61467b55e15051c237a1e41b207a618eeecb8f26c248449c56fbbcc871811975aaac57554645eea191b82df5991b5441c4c4234ba4e6a4604fb682b127b49ed46228ff78dc8035c3a71ba87c7947947780630dcd5b59641e3ba4a67803d1191463dd7346f518a077a8c290b9c7821b49233f83ec48f5fe1816c31d839c32bd37ca906945c443c0b358647f9abb779a8d0439eadc95f8cf2e1ee772671c814f0714e9c04ddae173fc7326e9797c42c93eb0f7b7603b6688fabad86680770a4ddd154de100f8dd7d005a361f64f590e671e1e1e6c1a5eb06778970c0da71acbae2c32960f4850fc361027a2a1683fd8c0342682aaf7354af709717b97411089268a0224285ad14eb60b3e2575ebaae0e6b39eef47cf0867c2f3bb1f288a417e722c102a6ee69bdc612cc34ae50444546a1feb8d4312feb81f5170067da4c0a6602e0ca1d3a32a91ca9e4247403242a14718e19b9531410a961f52ea04283ef1312f7116ef3769c5622ee205917b74e963dc75ef34db98db726b3d0c292a50388083174ef261c8ad0ce7ce702fa8f9cc6596b810df4c5e7c198c867312ebedbb4c07c22a4e8f565607328c20fc8b00533e2af267e93cc0d8d6de3a639961d2a7d0a0f5201a3dd578378c0985a6dd73228bc2ee09d46a2dca9def2bdfb12d414488deecc65e9c27eab1da3f8254c31c1004091d900ec3057c3153765c1c43590247cebda1c4717b009343e32da4c89c134f8153ab3df8ed10f030acc4d56e961532a8470b55400cf20068bbaaf5632c4114245aca0ce203a9b416e427575c0b6c8183c4209846c486f63d3f7900090443afb6dbfd5355c48764fd07176e7f0d8dce8636f03fa03ab04ac0a054bc26850497c4b601e1d889ad76bbd27630a914a4c43f7742fefc2f0ad5cb1e93caf1cd324d2a17135cfe360b57411036b6f6c9054951680941a90d95c070caa52ea64556e8324bfda4970c5de48604acbd1f3c73b6975002e3fb757847cc4507d8888df3eb24755bd64ca9c755be3dfce99bb8f7ef5c1cd5af786a163ca146be3b154ce807e77db1e43fb09da87653f11fc5dca7974807a819bd7bd6d45f5caf602b84f82a7b233a3b5739040e3d5cfb5090914661db9a96eb4d584c9bf2f570e1098abd7cc142b679d8790be0277d4d2c9b26bcc4f30ed516750e180cb6279b919b75cf56122a74dfc98a6b1de782ec3856baae08452ba7d23cba17937fd58b7d40039346f83927ed802150757c245e5f25ac167f7ea239c06896c7ab5b11121b06c4c4d32803094d4d5ca4ed57a5684c3119e5ee7ffa48c2c4734274cc116344d90edaf5a3c07fd48d808c1a61f9650429121796b049e735e16385375597df28fc4c54726137af58d2ad9bc403669ef0d55bca87952f13821f71f5853b8ea2d984b76b6d9d4610e8571e40a66493daf581e78c7c21c4e6bb1755cfc51bb1f6345b4833c10d127aaa897b8ed7ca349e79821074ba88700ab49ccc59c76d38d4845af4d5d4ca084c58146535846e96ed071d208c8871161ec4089abd1f27ab48fadb2df76683d79b564822a2b04d162b39de19dafef42a12475d43ca72c42821c10712dc788643d73bcee71175191d01eac2551dc0f52b489278ad8de395be3f6b9d78e68ada487e6b14a1ee143528d44edc57eb881b79c2a7691bfaac26f6825630449058b52571b8a599eaa9ac34b1ac412f8d351423856a89f723ade9dab383c1de5bf93a7f2508cd1d9adece7cde64ef179d759ceb4a5301d7e22e06c205ec89f945dbc5d0b9aee8cc48344a43dcbe2386a6ad65fb01d9f6c964ddacde71356fcb745412a58b2c5f2253607fc53bca00727906b17a3771f879cbc6db2e150e08620aa4d02b5723a3dcb213da62c55da46226a1128373ae7b391ade590b2486cc308bbeae73593e6e63d6d6dad535126aaa1d268838b44f2cb2780c4b2f54ed6db7c1c9457d0da34c2059cb2e2729586c1fbeedb34c234eb7559614aa215c2a63cda0fc99bc275d39590dc6e8c2670554e49c861166c9b99361a0d358456569f8efa10ff52a72c229e5ceffd4c51577bd1b30f994c9c3023c2c2fb620389e7ae1264c758cdb0573a7d8200526e37381e016092314a88f6ec188220d380c32577e1ac499f945f8194097212d800c060328194ec6ed66ebf27f910af889ac4d6fe492b6cafe95305c430d85d29c38c1f65f7ee8941673f39981cf6a27455780a811045e7ce44a029f8c93bbd5b2cd26975995df3382ed2717c993ca1c7854a703fd74d937b0b38028740bb54e273690c9f90311147aad506f3b9d182ef3270563139422246ef70daba1d93358ac50f4aca492adc11e09abd0a48be88e249e946b54c3919636be54241229b08e18917a33868f42f405c7c5e10c7fbe00837ea73ce3705f94b0189d9d280f580838c14746e28a0d350210dfda3f7eda8d395099798df9c38b5df9554a5174bbbf3ae036a01c2c892fe5d59f1f72da94f8f5b717b8fd4b3c98c1ab5869421f7a573f2bf145e6ccdff0465770d236ea52e2bf671b6d59f2668231567a037bd425bfe8003fee661efef2a41c247891688a43071be4ca0c44388a8eecd78eacb8eaba49bca04e05dd0f41df8e7393612033588ae338d13e4f80536b0559236b7606db3022e552f5d4afe27f24cd4103a1849f3f3fdc81ea90ee012a218c9ff49d72a3691cc4bcd1f8e1dfae4770b9b0a25eeead38cf3b7261a9af44b89c6f7b11b8d9a5ee08c50c8cc940dce6dca0b56f0091ea60c091a88e8bbe4bbab21221a8f1981f1641ee216cd361e7ddd8f60bd5a2e503a3d0086d583bd76177d0d4a3404b35e6604793b71a2ba924e543782d64e6853455e1be6253ae9caa71490f98e96bc9e8b1bab77460602a3067fa907c182d6d54104881f5c68b203b2f2082301863169bc1b60f88e945a721a255803d4ecd13411af58c45f377c0b7da53e578ee1f9c6a2af820da589d15eeab56ed20f3a4acfb43547ea8f0fb5acfbb01cefdc417e0dfdbe91839786a760b0035065e4a31c730ecf766f455bbf666ec2a083fd8b10c3500856306ad916d64cb365cf8c1dcdf70fcf4e3f887ffdbccb15e6d45b02c22de847ae2b69878c31c7cb8cffcd8f44852bf397bd00859a83e47e8bd440247a72a7484e02fe9f1494fe2cf1ca021c3c4e01f7f1da4e11e014a22bc849194dee1a8bf64f9f41bfba24cd7926be60253096d699f9829c858a1c886b12925e649607b2758d9af60a412083498b4ffb0b3a751228a10c0ff48d3413653751d1eab6e4f51030c4796e404ddbf6c227432359a4f8b58506d239f8f843955429e82ce6d69fe123ffbd7e9996a598c2358e1b503e0051fd024ee86ce78bcdc84f80f0d82b8b96cdf44968662095f7091579d0a99710c10e5e42d1e3d51f031b343504f6e8bab35273c7c2425ae8b596a899238e062fcc1d993508f4ad5a87a6c439ffe836b7bb1bebc797c91a3ffbc06c30e8f5caa221bcf866731f3aa1d6f96ef8b070c358d69e308c6d0b8525f418b7ecc60c36d06396b07d7c205919cb33084cf95931d222ed7aca250a6c768a7cb0324de10b2a0c44908f4d7412d1882b4ed00695b1d36ce56c52b301377f967f29aef7690af7c6be74402d670f96cd37fb526cc9656340007db472f8d8478c388250c5bdf9f7ec3665d23eab78acbe8b9c812d18b4243866ba0286d6012288f1f7885a43791d206874648aaa78189c16ff07148341e12c7fe6a962736835090b849569b1019a0a7d569d978f304a1d0909b2359eb15fb91cf27932d2bc8da820db5da6f374b0c2d349584a3857bb90671a6fd01f0d77f673442493b54651e53f5c0470559cd7764b827dbe8f2c0776356262986c46ce2756a58e646e71e9736ef344803f304d026807d412c583026e4a7cddfa33f6a58dcd527f130502726d2add09f53ec20bfc4afed58f4af89ec685182ce7b581dd6500d36ab8817d066e81ff87e205382c6337a67254aa45efb9a626317144735f8ae110f493446d1353265e8699c821c73bfcbf41a6d2dbcbfb3c5bb9fe9c1ecb745e20be2a9c074159f43f1ff10edf9b98c5ae58abf1bc87b6f9d1e0130774dc484231d68dc8135246e11111e1541da664c9b0e92c2d7095b9a0e6830ce630416792dc87fee6f8e125771dd2b4998fa1c773eed2cc0ffa15798eb0ef59fedd8a51efa85188d22b22ecd9c5503c51dd7913d4424d2bf82d9037ae83962be5a6c588155796366e0f07f085221e57134fd591c6ffd7234c07493adcadb793e5470197c0d167ce0757664040374f65e34298716ec78c71042654260239bb2152562e21e70fe917397db20c65c23944989d8aacc9812a168b457cd40cfe718b7e45fa6984f277ab739e8c241a2c698228a8f1f2b73c1ab111267be65cbb10fa2dcd073594896c96239e26041610e1559d8ea070944073099580d9de4e18ed705928d5a1e49db585f04acac3c75b8540fe93ce46d8065fb4d92562a8cfef54c4652a44f988149e82452351645f245e610758e00664d1856ccf10af5baaae296ee6e68c8ecdcb8a56a2102fc18fa8a5d77144381925084c8065bddf1df12667d5dd8ff157c61c722117179d748cc341228c96b94a85cf280742e398707d34a812cf093171e25900e6d57881cd90b20b080e09161581c35c4bf57f5deaf3d02867f296809a211c76d9142d43dd297eb33012e59741a3386f7dea106162dcc5d8a641534e6ecce5f98ae889d1c4fa5db53883862342f40cb207a3a7701f9956ad16d2490c91ab1fb45024e12de63e9a9b96105d49ee109aa9f6de0aa006fd12909203b8396ca3eab13aaa6416c3d24fa77dee93126ced603220eb08ec83ab9bf5f05b1f4f177084a86085a8fa109f381af96f5d45371ec77d5a113b63b9165c9b123da4309c8565115b5ae002f61ec9e7e1f6698204a8204d74d90ed26c7be4544bd766150f061f816adca1ef95b158ee1ac3ea372c62be6993db0c5a33d104741308c09e3c33c5ba4e958f7878f3790ab435b3016e4f66a64b2f54c2552c05b22e848ec4341e078b4c3c473e6cbd587e28adbd5565cf4bdd57dcb3356f0660c958c61a330baf02a193733afc4c50862bc8ada4cce5dcfc23d8274421006593f8b308ae64323f6a3acb9f8ab229b1ae64dc24c5c0805dd46a8c7eee3534db20e780e7bcd1d00c570399038b0c933c04f0360242bd5f7057b4da878b6913eb417139275e8267a013e1568ea21ccba77f7003de3b6aba80a49b98a63961b2831a81624103ce20b6b985bed2875424abc416c0c9f0ef06c85222bfe478eadd6f1fbc6c4f23a824861108f589b9765b26500e87db1e4fd7137c794d76f86c14201b194da9cc73cfb1ccd2c9775ddfd5299560134a191cc0fb2603a13d1aa350b2c11e90d3c49b6015e40bde0e0cd296efe443bf097efe1204080aef5fc675e89b0447a647f113086d92c368e1763eaebff1a0d26e346f1f5900f25b8425ba3fdadb1c3bbb252bae8694742c014dd4e7bedb09dfb748a6611a1181b3890a1f11ff1524491c928d5c02fa6e9bd17c34c80e1b0043d13d02f869f74956884bebbe4031a7dd362dfe4448a64de96c8c7844961cfae80f1e52b3e1d930da057763a1eb46b3421fb35e4f09d4958fc111ffd368bfa63e10d59761ee94272dd1878075020b017ec52d5b017ec351a303e2a167d15d486348f72c2d1087683e7d7e52402cd8b046a43d0532e7f9f9ae5503daaa70fd9758e6b8b0147d17bebb8014f880f1dd6f41560c422a327bd10636edc096da433247d26117f0599e86b020962d2c35fcac47c374bc5a0ae06300cc610b2db265013d932bf194cf64a0cbde1a7a9653d5f985395d16b96db8f1cb5b3b6cbb3d9f335f5ebfa2170a057c6476903c3953312a8499e65e6bf55e7c2a75e9ded541bdcf19653ce9935a7a9f9972e55f11d3e71eba664cf8d9bc3f768295293f7e7143ef2eb24e6656eaaf53ad2484407fe974a3356d3382b0d68aaddb437b23338573425e3c94dca8b47c242ba9dd402c22f712c08c54674feece5dea7421a498e9ec4937f54d60ac7126efac65b7284bd297549dadd1839123e474c29d4424b1052cad4baaa4cbaa2971fbda19d3d18b5eb6c23cd93a9cfab399d0ab039dae39c3f7f299602a93b25d9230c2eee7aa39d91a76bf80a572e661f3b87be529ded59fb32483ee97c6edccf44918c477ccebd5e31dd72f308f211b6e21685b0a1ea560a1391ed2127b3c4f63583c17c1eddbc3cda3451a48002fa0cb2e32a3f30d58309afd375d298548b30da6801250cdb82bc926c69b6994b50e50f1054191efb1843a54e32df24ce9d7afa2fc4bd6ff015fc671677b4fe77f4113b5a988f870eb6650401050a847bb7efe4330a2ec7e5973aeaa632d21b11474921a2bd4f836a6f474607a3fcdbc20bde9fe6c509d28f8ad21f73b1e0a9ef6dd6643ddc84bdcc0c3017163892f91eaa7d37217a02f040a27e0e594436b541b516cfa434566e1075d988016b958ccacb70adcd6514babcefd5880e4aa20c291dbdf6f603b247995f524b66aae22d467b7793cd504ca1415fd04428d9e3f6d001522b0d9b761815e393fcda1463693999622e8ac9002370f2155d472f617612524b8b3929c0dd21b551c777719f6c7c9fd241ac91ed2906f29f825fa45874a8801b524ee428960d59225b5599a2c725bce4810a8ea28b41fbb66d13eb7a32420648091d41d14f4d62d90da44324973dbeee1e1abebb4f0d00a10d3af309fdd3fe4e4847f68cdb4644ec86e10f7f2dc64a7ee4445856752a1c43d6daac15a7fb130246a827261606381b43c81bf2a7a389bf837839527f9922ef5f39899b6e907618b780a6277d4bee5b3e0e8b1ec62b1761e2cf6814f6385ec53ebc915e34e40f8d5ac4d1a1a58766361fb37220fc24a4f1a1a5c9f75a4de6a39e56925ebd7f3da6a38300d9b313dad5ade83dd2ae6ec02adf835e85483b5224f66b7c25efac203b062d115e2eaa70abb4e2a0f991dbe1dc7a91880c08a243c699eebb47c7fcb8846f00ef031a2c99d0e87b90737675c4ebc9e27560bf3819602f619d06cf693ff24fa92639298ecf537f51032a8053a1611a651e2af074ddd8fab9f0191f64cce9409ae808033414da46da62389fc708bf15f0a066e8390379d2e21786328695587c2899f1858bce7781adcd77940a19091944850474404a1415940f3d8108f8face2ef22b42bbc7b7a9b46415f888e2aac114d4d79fa317a5e6020bb4004944312191279ffd935ff5a86aeae40ef191e91176bae7993fa10e0dc6898585a71024f0c9168f80d9ae940bc54ec89163a5ce02f19491860bbbd8843769a8bde3861190487da85f41f8abf5625bfea049c4207d553ae114a575f97341ba9b7be28658ce7c1351b238588cf82c70b4e1fd1742c3c9d0684130619c81e93ba34f34b42763dcb1957765c3e03369bb8764c2a6ae64ab9f472b6e66636f1240c3307963acf819dc91173cce1c45350ebc21e2e8aec2550edace2102f6f9ba8dc5a4a240538c0badce0514581aa308d6d4aabd4400662f29e3740bf4db90f81559722ec0588ae7b8c7968851df89bcc0569ec6e3e7ada8c1b734929e7fe0467711513636a9fe9567516712ccf17a7c9fe2216d35b2f69ddc879e9ededab83706402c5acd1d190323fa3cd2dff3a889d5b0f02d9b950a801954bb02ea5bc2795820d855693af915f34d0ed387460acb8585a001dc3f6488f7934dae9c26dfb2d230f044011e3590fbe29f0d4030fafaed3adcfefe9b2ea64d4dd702237cf98d548267b313fa7b79befc65eb6fabb68d9a88d052237bef2d03d9075d07ce0739c8da97ca4003b9848a4ca2d2d9ae03dfada440d4095cf9f695d40a0882281408822b9eb59d65016bf66e675592265e5e969bd7dd9ae5077edf497bcd1fb8d28228f9258190d1b80213985045169678e890052074ab7377cb325d71c5115660748422c4d082229aeee04150fe17085281cee65b5d4686cefb420b983a218a2546cbfb4353c7e57d59248da6cecb1be61df3de797feadcf765125307bf67de34ef9af72593a933e32dbe5b6f1aefcb28a6cecd7b86b78d37ce9b863700de97574c9d1ade97594c9d9cf78df765d3d4015f17f2be8bee0079bf9b936e59e8af7b773bffb4629e764f330057e65316008cf25c99438d2743551baecc286b83b6857655b09e2bb3cad663539900aecc294e009b4a1bc09559d50d80c3472bf6862bf38a7743c7a201cbe6736566f97cbc185cbe32c700f3b705d88d57e6d57d38e53482de17274fbb2d1e01aecc2d21014e2edfcf95d905f513d214c17a775732b35470a05250a8f470e5f8ba400f2a29095c390a092281d40beac3b192f96525876a0bedc2a8e8b832c3b0e85861f1b41b9302ba32c7c4006281812286a7dd1d150e57e69d55a7f274eea728e3f293c13244b06a82ccea2c05b832dfa5002d37c6876325f365e5e0821201cbd32e5e055d99f14b106b85c44ccb876325f30c8c0e2fd8091a17035c9969620c0053f3eeae64ae39408c8cd7dd8ef12670e508e49eae902620f315cdf8876325f38cbbe3d7d3ae18c3e3ca2c621e177bda6dc9ec70656ecdec80691c0157661a340898f1b4ebe3423e6868b6e86209b9bb2da45d9bfbe9983901d8e6460a6abceef615125023c3d3eecd4c8f2bf3cd8c1e325a42ccf0b43b038d02aecc33880a98f1d540f4b46ba3660157661bad0588610f5a9e76716430e0ca8c4383012d1723d0f0b44bc38c065c9969a8d1001a6117353ced0240e4e1ca0c001b1e6ad878daada1e5802b730d370eb00187b8f1b49b43e30157e69c191e7073246ed480c095f9860d086c61c3d32e68f3e188c00b8ed76911b0d11444c6f1baaba3b34c37c3c675de079e42944a4ab5c21263d5e2c27a818991f9c53334353266882d1a356c6e6a8037fa26bb173dc81d8fa75d1fb742174ac013f0083cf7145e2091fb83801743e4fe1eb0fa42eecf016117b93f1e5c8c90fb6b40d883dc1f03be1ae4fe16d0ca3d851584c8fd29000443eeaf87ea07b93f04b4c820f7b7c36a88dc1f8f2e8a727f3b322eb690fb439a400b16b9bf036027727f06582191fbd3610a5c8820f7179455c10a790c597e05b83645159a90fb9381c93d45157098c93d45155600c492a2638b2ae48ea50a096c0105520f5b1481a3850c3fde17045045615c0d21b368e083734f4185a31b68b818408c2a04b0f2440f8b1236b854724fd18424773c0178e7f2b0ac73e50821bcebf2af950d99f5accdf96f7cfb8d9c1b356a0c40f36ee822703b43e0767ec0edec80db9987dbb901b733036ee705dcce0ab89d7bdcce09b8330bdd9911703bef703bf3b89d77dcce13b83d1ee07636c0edacc3ed1c743be7703b17e076c6e17606ba9d75dcce396e6709dc1e71dcce3fb733016ee7f176ceb7b3cfed7cc3ed3c80db5900b773cfed6cc3edcc733b07e07696b99d7bb83dbaaecc6dbac9c8d840a41eb45be33b57bb8c8bd4c3757a38906b9424c302af80052a7021d72889a3c08350e0473efef8eb1a256d13f80526f0231f25402383f7462b47ac6106006464140868a8c1a1b1313303beb91905820edb3c8761a82193c330d088c92810b460c497192c242007af8c7bd725039161810adc9e376a5a68563984321303b3dc957cfca2ca3ddc54ee4f4646e6864025774d513121cce905e4380afc38ea045fee9af6709fb9bd07bca2ad6c0212b83d6f70f865e3b1badc3575e172d7b4e553dcf21796c1c6d058b2154c55535465a264e885148b5727fc0282f09593b31c57f3c88c35c9af1b346bed66b5cc23e26122400912452f2bd58b76d50b0e54ab17d54afc0dc29777d58b17365181054d5f2815e5d70da167c3a963431b53582fb4deaa562c031b765d3a220aadd73c950ebc50e5852f3ae69257ad49342dc114e508a52d3425c9af9e82cb86143b905f301d910455e8a9848859bba7228217aabcf02b1d84afbaf254d5326d4ad08e28c2344dd3340da394524a69d530ad0bd6064644d142925a6ba55c300226842b180981054cb5663f59599b82480b986358f3e9ba688b1e3544f812b97e911e779f49e9edb1cb9c615cb515b514cadbaf4e8d522cf4407ff68952ee6cb5dc69d826b1ec5bb5db0776dc96dd6edccd9e5d1a3e9cbea14749114a10048360973a60cfb06f57669be6e91c85239ad82cd3589acd3aace3366b33db988661dfbfef62c7ada4ac5995a0e536cd76588759387c9fd5b4d76aadb4dca65969b59417a7c6acb5d6269124bf4e799b5996d9ce6131e6819c1da0d267dd6d1bc35e2ffaac6a745a6b3bbfb2fe9c147fde8766f75583d4776eb93151fb2a115e47fb90bd36e885fc76e9e757bf5dedf4aba2a8bdfb4c2ff3e6372390b85c3838a2f8af5661a8dd20d434f1db618edefbe321470f9fb69c085f9e0fe53e10254a7e35175e56aa7622801b99de644a6b9aa679b4d3534caf9a224e13423a5f54ab7b206bf827297c7997c915bf7a08769cb2ced9248a1d2c8f86d2240a5580818820340561ec6e5926b216624ba660d282cada3405d315b9d64f8796b38f4767a10927241d4142055c308dddad7a6a73866db5b3cb1457aaa4eba9c3a24d710e62a6a75894d2a60586248aa4248922050539070f294760db462b0e54c4719d865ebd86bd0e7b7debc86efbd617c9b2fb2a92e5acab41341d341ca4e2202c1cb063580cb64e62143c358f3d9d40b07940f0fb9ae7fb3caf793cafeb9aa7eb38ae79386edb9a67dbac6d1e6b9bc7f2685a96354f966158f36058df64afd5d2e6f94e3f9bc7e36e7968df503a9b677a77f64dd63754f60d9df22a52b7cdc33fb464efd672c71e84db2c6abbe5501c26e28561188661185a7b4ff73524d377f70544a638c8066edbb66d28d4c6a15028d4c66dddf5b31f460ac3300cc3303c9d408628140a8542a1c2300ce9e9cd5e43f23deccba1293ecd18473a9a2048d83d9dfebe63180dafd2615372f8ced29cd139b97bde766c37fbef2584e8f61235c9fde59053b2ddba13a91bfea1256f14079b89d46720883df620f6b4ab5d67ffce62229b15a13c4ae5a22ef6cea7a2dc7d13e21786e93bdafdd4bd9bdea78328d7bb64281de661efbacd039966465d862963ea411c69a31044630a376d514a84665957bbe1a039eab977dcbd0c10657a6b33fc438ba559ea767b766b5f94a6408c1482944aa552a9542a952a954aa552a9d48fb8c6d43d90c2216d49954aa552a9543fe21a555b28a4993e29a57c7f409dbb6d5edcbd1751931c4437c83c0b872dd30bd1f20f94285f21dc2ea764fae1e0b2036853a64c09030e1b7e59a4d4e78774f431853dd04c63120b875cb352177090e5ed174b594a89012d6727caf3c301cc19ce302b87ecf4990e3fc85c2f44cb19c59e54992a35e830d94c197c678e3b91ed2f2032f8dd12e9291949e52ff0dc3bf0725b7c180672e7301083dc7610bc2fece07d2886023111fa1d7ac43e1c20f6d309c33cfc7d860f77a07010a80f118d28ec5d9f751241fcda91c1dbd3e927ac9b64300891edafef3fd08cc401d43b733b50a80b4134a2b010a9871813a1f788c829b98a2364d91afe95a8a754f051cb88c221405d88300cc3300cc3108542a15028d48fb846d43d80c2216dcd300cc3300c7fc4358638b422a43b73ce09ce2e041289e769ed042f6864fc256691026d0303da4676159817944c0ceb7d1f3f9ae754f3196fbd3f334a629af66d1b0c14a47b9fa9ddbf139384afe6e2bb2e67ddef3017e9fee5b6ae511258e3628d1ff948e333ae5152872f03ffc8c799c75ca4a38fadd75ca3242b9e46fc918f33b8fc5b44fb75c145b61c8601fc766d9601d6a071d40964fcb7fb76b52c030fa12d998bd47a87676ecf1b1a0632775acffe63eaa09ebd75c3c8ae17b0560b0fa12df10ecd56f6194675c8143d1c4269392194d6d2dc651c076edbcb8ad3ecbd45b40f6584d06a9ff5f1c5c4645f77edeb0f07f6e19039b86d208eee1a9885bb1d467e38640e8231a8cb097a27103caa2b456029a50cb4427bb285c115f95657e64f7e38b695a45e4b91212db7593899571ecc07838552d6aebcc75d560b6bf5ce5d8643e6daed8ce2503131f7993666d3de8591e587343305164f727f4858a3026ac3c28aac21dd18ef322e9c82cab72258ee33b3eeb86ddb4299235a40cffb50dfb61dad3e73e5d81243951833ef420f5f39ce90814ab5e0772a0f2573b7c75c20a6988560d478dc8c4605d4bd72acb940d056769afb0d41c1cab7bddc77a92e86b75d8f25e6cb21738c772aef7489f44df6956f48df646f3921860cfbc409e66e7fb940a8a8e021458c1072b755b619de8605135965bbacb3b05c99592df8cb2133cb3b176fbbe1637c3886ac1471a1f972c8bcf2aeb9cd7620056256b01d42db0532c51f7d937d061684406457195224f5a332a0209617cf637d3beb85e5e23263b90bd2742d33cfdc1ef1ed31465d79c9a21e53f7e565151438593e3bf367571f8e15c86d2b9f0f161f2ef6fb34cb6d9b64610253eedc31645350c343425ca4b5c80ef02133837d608df7bd00497e49137444acc9f384a6138a27b39a651acdc2dbd88783b336b01f13942a9050a4431561a8dde994e258b815efe48228f965a3080b2c8ed00226412d40aa2e40d28442f5e4cd06a0c58ce3388e8322a90465b697524d7616ab9b05b7efd2d971d672d6a2ec59271668cffa6cb769dbf65dfb770b1434c5ec9b0f11aa8042c18fe3388ee3be5d8e66d409fc3ebbc5269bd80b5eeb4e53e84f07cdb53b8137f74d96bd450a952b47d505a2adeca9bb83b6b2cbcf870856e59d4a154d647b69965f6e2a888842d6be1c5d85dca1b66f7ef2035f4419e8cb31732bb178f6524ef45027f00bbdefa35de7626956b93d56edfbe8ecba8f66ca945f3d053be7e6cdcb85942714089e4ea8d3c9cb49a7d76831eb70f5826a9d7ebdf6524e02f8002d41f16e7891587e6239015aa7032d663a4000751d90380062eac82624132368eebe4bb39d3adbbf0fecc0203ad873ff30fae1f8386e0575fa40ee6167b10fc767b5bac2711cd77520e6bccfda6db3a8d307721cc7711cc7711ccb954a662b7b27514b082bf6811225e5776d9779dfb5ef2facb9cb6ab40e0fe2d459c014b3c6b1d76ecdd7bb3577dfd540d409a45d065a478ea0874c1d7934c54c22914846d034a79e758ba967aa67aa7f2b5836e99b4c264141c1ae5c0a3ee4a175241148a60fba2393d0bb724944eb782167cf8600312ff540f0d34e5b58395db9c4143d2c9348254b487d57fbe77da90c4ab90b4a2c1ee1bd0b85f7be87e043fcf0de5f218b87f7ef49dc87c27b58e83b8887bc7f38f4c29c85b0476efc0ebeb2b89fb6ea89c6f0b238ec8350780f0f85b45589c6f7080621a78c491c9e7d63bfb02a6daf3b7035a161895692fb6ac92f54e6b473df7a671edd364f77ae489683cc572be5b09100042979fbdcbefd5524eb70a284f2892872fda69dbc1bbefbf71eb9ee7b8ff01f3662c4e3be83b8694bbe47162739cce2111e7c8ff020369264c4881ee0432c91d0563d884f5bf33d4eaac94f2589701e8576151e1dd117cea703cc128cbea98f59124e3a2f97eba7039dd247383b6835b959d23a8370b9c3ac1cba6bd7d1e12c73f835ff83b69dbbdb278e0d2b3d31451d5a964ffaa67e5e2ccfa3a227e88e1deba5927cd23c5aae97452f5984c4089924d7375d62462122ccf0d014ab58af72040645d20285ec2708e827a8d63a2bad35abb36dc8548cc0cb2d850e5a905fed248a13356caab5d65ae79c73ce39e704aad507a8e27c4ca19de2ccf9c97985d55a252596dc4b544041a909e342a74416842f9a9c14810a3c50a29282706a35cb5eb3ac52ade7a747abb5d65a6bd5324ce585248ac014250b2665f80cabf5263683052a6900235c4d71aae457731576a6396032ad27caf5062884f206a6509e8a393d3d3f3d3e383f3d3f3d343fb37f7a7c7a7c7a7ee8ecf9712109e72715214dd602480b2d1c30757a7a7a8a10444f0fb60295dc4b54206972e28326273e403f3e4073ce39e794724a2a65954a3f800861526aca09cac9f15152b25628bd5a290b29817e82e69c73ce29817e82e8fc09f2c9f911f404c8b5e3c9132054247d3138d24433715c52bae47b6cea33978feb72d219d2d965d7c7d5e3922e9f29a974f9c81cd1f52ce4a4227cb932b5f1c9a2c7e6278b2c286d572af752116890ab93298b706acfcffccfbc41f3af76320f34451af467aff5b3d65a6bad73ce5ae79c13c814e9a30c993aaf76520488a1a9f39258c815e1abda44e16207e5e2c7121542a832c0e9f9f9097202e40afa0972621314f403e404e827a8d29f209f1c573f79b515289df9d556a880b4a1a58d79fa974d7605ed88e2fa71e2a408443809c5905fed249b828d6bc7a762977fd58abdfe27e7f23e1748ca4c66a7545249e5059a22bdac3407995c7d1a87ac3e40b807e3f850fa3175800861529a732afd4c1d20fa201aa5fa748bbe69f7967b890a2872c54f3c9103da447772666be6d44083509e4ec144965b0a2854802101401898f44113ac9837602288237c10050998204a41156068220a4af8c209162cb0718515b658f204282c5002510e6030022bb6d0228825444939618913453041c1020dc626e4a00627d081133c504209511421b0020eb0e8a288055890d244144b2862a2044c4f2021ca8747e048810c4bc0200462a841113c04c1648215dce003433861ce9b01397db0215f64956de81b99cd39b10f35007d2367005a949f5f569fcd895557dfc849a79497a73b338270c4122a5c30061e1c511c84010517bc78d2832d8cf20a5e30297af282112021490eba60833208f1052ea68cf24d23595091ace45e22c20c686e29769082fcca691aa2698818b99786c0045718a5525969cd04c02aad35579c6bc52af5a918f66cc55c3f53aca712fbd0cb79a0a983ad807c7e5aa9522a7d8070cf14a9085be90710215387a9befa7418bec225224021531ca50b2a71a8d8fd61fa6063bec82aeff40d2542a748c9724a2642a74cc912fba0bd88cd37fa66beabf846e38065792bb3ced8fd61ce2abb8239324b5cbb33965f39d9956d5c53942fec14e760d85f39193bc5b0105b6118ee8942f660393d1447ac79f615425347e6746b7ee6d89c55eea51f4891297e85399ad44430bdba6d9d56b55f0b67cb72cfde53a74eb15ed6532d5b4fdb9e5d95eb69bdf6752fdf7a82a4bf421fed54bb210d4d94d21d7a349a97679a477434808115f54d7fce9f66b0408bfa664e1d30b936e99b29a70930d9042b6a1e9a84fcaa4d34911be72ec2da04264b5c69d842286172ee22a4472b7932cd682194af4db0a22f388079016b324469f082cabee18850b4993bae1c2861bd2a87e10d4d84af9c1d9b2932412795147c11e59cbe99a7776699336d9041615ba2598145a7a4c40aadc31fa0a025254b5db8baccbda48449ee722f2949227bb9979444915f22d3096cb05841a701254e29d3d7d08253cafda61b60ca9a64faab206b92fb9f0e702a865f5e2c8e4f11be6cb0204153b6993aaf1a353e5f46104e99676e9af981ae693a53791dfd7274ed0a2dd5688a529ad26c778c5ea94352eddfc524b6344555941ee05295f2a1da272f65633f6a400aeb31074cd15a6b5f856461ca455b99901f407ecc1d397e394420cbff002244c33ea07e3d4bece3c79190a9f3924640a6cee9d987482317ddf9d1adec7605bf6c9872e54c8a1ca01220822753eccc852a53ad6cf7b6abf272164e31cb56b00aa7f0eb2ab7b73b9683fb77f9e9a057b964e5803a3deab4dafb0af3abb978d58c7aaab99a7f4969b3bc968348edf57a59a6ddfa225816ca300944f342f57828a9b1d03cc543f443f3444d3ad6c86acce2518fbdc79c98d546e62b66f57baceff166791c7997fce545daeacfcbd444f8036e099c4eb84196e5b87ab6c98439953042f684252594a0441319cbbda40413295a5c51832dbed0021464e0852e884ee00525902085214409c210c67e03e93134a1a088221d0c118512c6453642092ee8c20790306232f6db02f26d8940c5d048c822f7d848c8f28d3384bb1dbbd9b1eef56ab7692a541802a6a3332625ae2eed18ce9962cdd18e69b856ad6a5a4f4eadaeaa6119566badb5d65a5f6bc52a56979454e045a70412ec5c6677ba2b3f1c56d629626f0d958010486c73e19c29f6c85b4cb3d6f64c11bbbd3918e6c26ca665d87b300cc3300cc3b00ccbb02b744db365d8e9d06c61ef23681909f034fa761d28b6a1bec1ded57045f89a46598ace349a3a47c78832ed1e7ed9305cad7e511c6a1e3063efa617b3249cb9ebaabd8ef983ccda3fe93df319d237d87de88e1db1030d691e2d63b7985d1d2763328cbec1de7d9b4753c430179969cf82641fca241269238a103332c21ec3b095b5525e8904cbb24b4c66364332772496467892608ad85bca6b1681cd226611d28679ec5484466019966559964924ddc28e61198665f8157e6050fa01648a4aabb92486dd5e3f934c2553e784ba977159b530c9e2e5af69c4ed7115b324c41eb324cc668e4fef4386780657aa7224c3905296d09e8862e2207515251ce4fab9a4752492d94cb25192ea473edebcd2bc7e2ed13c2b97f1eabdb2c82635f8e6af79b444ae46349159a8344d0952e90b02f879d76e912cabde498fe6d278cdb5f9cc6d1ddf1a5fb9da279277913066cdcf9c68545da41997317321fc199a63ecc9a8b98c138d98358f4f341e7d9c81c5e30b891f9ab9f88985661c1fe321fc192752a930d28cd7d49c68a4a1719a0bd138cde7696c5e83876a6ef3219ad338d1882f6b7e86138d33ade30bb58e3f8ff150d2c442353e031ec2aff1a199b74e348af7e61a1d7d9c712953946153e3d2b0ad5bbf72b52cde19771611c1642e41d284eec8a6244bd8ac65d5ed2c9bc8f052589240054b23a5d028579a9ad70fa9a19939fecbe69acbd22ed25c96867d10a279cd6bf0d08a0687e63de2bf441aed3dcefcb50a8f67be7fe6c61cdffbfe40eddaa62df95f991b766b3e861ee6764f5ca728956418539422a314dda9648af52e5736512497b36e53ee22c5f87671cb3dfad8f2550b6e7af43106964c4c96b496c1dbf9b3b91b5d6e8f1d43c37309a515516aaa0821fb084152ca9f29ae40993adc549a3a53cbaa161d2d1e01861b597a6cb93dc2dc1eb5517e3ac01cd437558b5912da9cbacb7bb4fce5a9bb9ce526bdfbc42c1c6612963251ee1eb4a35e86512f9beae593fa956bb47a8cabae11eb2c87b946aaaf787289d55d9d7593b6b3dc231f61be7255f7e823cc3b23a638b398629d5ae079c514ebbf7bd781227cc9a62b5a473671a2c5fa9984eea85e2f9fd01dfcfab9d43ce1eb67135a59c0d7cf24a60eea5532d15613ddb9af32af471f557fcd25725d527a22ff749f2cbb1aac085fb2285f36350f1199a9d03c2ab78b64f9243f918e8eb4824f1769e63567b92bef41f315961b89390d7e12f81e2ca7390d36b27296f7588359f3aaf778f47106d35cf51e3457cddc48ea34ef3173d555d848cb67de63374f278c34f3bbf21e317ecc7bdcaf60232bbf8fc1469284567e6f24e6f83dca5cd6bc8cf7f8cb9a98c5e3af790f99cbc0465a2ef31a6c2449a8e5323792fadf23cd3dfa4873d5353af291e6471f67661220cbbd32d8fe366999e6cedc9984b6eaa5134d9ea03bb38964a23b5309df1eefedf1b74799db234a934c4cb99e45e61a88083f2c9960a984be5e1ed19d98d7cba296942cba96b6ea636ed3d45b58f362cc654dec8390ea2b5fc1432b158eea3db6fc25aa70927d8fd62869ca03a13baad7fba03ba9d70fa9d9bee5ca26da7a22a14c236612534c72535746a1adfa953b8b68ab1ea6fee5ce26b455afba4deb596ed316eb32c9f52eb7692bc60d62c80a4f1dadd35d5fa968b16a99bbdde36a64b93daedc1e55b7c76db4782ef54db5d9c7259b5c2aa2d34481a19fd1f6714db16708217cb9b087452af6bd7b07dfc33b887b7c07ff1d9c180747dac01d47dca8fc0e30bfdd8543e422ec2504a129f7129cc8a2ab6f6903c3afd99f4256f22281d8355bf47473d9ccdd9636aafde69a224d0ab567363d33c32e1f17e683613eaebe3dae79978fec76f934c699e2ccc2aee67401c10679d6fb794d5c0b3a7a6bedc67def4b8efba6b5567bc67df3b1c336b17868cf2ea461234915c3421c16b191ba35cdceeb3aef1a0eedf6ec46be6befedd985b667f8bb867fba63443bf7fe777de896dd5e28bb15b5732b1c6c2449de883ddd31629f5dc83ec3120b6dd770485bb3db6edbcb92d80721fb6d685a8b93e47bcca2d4a9858122c91588293e31451c6d73bdfc42f368afefa695604ca529ce287d534f8da658ef33e911a64553ac3a50d0a28cc9a2790446f380b99e8649c8cadfc1cfbf6811dd69307610852aac308621ba1065ac57698194a9c3bdd6cfa34ea42d7910b3b4a10fb3344d231a59f2e189c6efb224e81df4b40b36f8cf7effb076ef4423a761ed43a2878742a229c1ee1bf9cdaeebfe099e8e3a0985ffc20f7be7b010f8fdbb3e68ab1e7577dcd3e9df392ce43dc443a87ba71f8b07087add245614f8f9fe0e5ee83bf8590f0fe1d056fd879bb666f7d39dd3673deafa049872a8a46b1a277b20640ad10082001040005316002018100e068322a1400e4589acce0e1480137f9a4a5e3c9709e3418e433108a230631021801800003006c0d0c8ccd44a002c65dd041ea83b2308c735f364618eb383e89ce1e02d893be5d87992424f33ec1546d066acc7fdc730d63f792b07f19e486d8f8975b7da02038610a81a703bbbb4b4d09a41550ca7b0f45d61668531d8bd72f1185b83291795d18171c4feacb5519f8307d99d8d003b1a63a47f180f481928379017936a5dd81dddca24755933f9f7cc0084083255281776248218ec974ffbbc99f69bd0b24260dc94ae21e62309a1332ac2a8756b0e2d8957cfc7fb4e0b6e3bd4baa95a77ddec4395a552afb66f7b1d6ce6ba3ae6a93e476eefee046ee93b516f4971ef5bb0eed8e4100f3a2a9469f30b0c0711ff8a482586d9192b6b3101cdc50c8ec2920c2ac8fb7eef6f2a5940491e0fefc1cbf2d753ff08516e6a7b81f112b39980a83a5f10481418d5cdcfef5e34ce826fabba8bfdccbbe20e79d3f1c8a8ae0db05b74b69976ddea87349214b4cdf0887f45342d79998b234b2dec0cc8210d9996d4b6d10fa62502a580c8e7148b259afc9f099669ec19d7d506d137e44a987d455af236bf1bbfec23667169b994bb0b254a228f66422b95ed16658e23ddbf65747592ec2b61895e8ddbaa110410459f827995c8950e025c04175d1ec8ad316ee9b164f9006c81924c91834bc99bd554f0fd5a432d8c76e8ee3091ce99ca0348f15586ecf377c4670c01dec36ca0e8cc0d786db1d34243a1fe854239ad8f5a5d3b32ce656ff466aeac9640081e7b2fb22ba06f3492be069cd984f3e501a26db07f156c8630fe8d9366b6b6debc38add52eee4c3f172765eaeeae385a860b92959483c69beef86170b13402f15219df76f6bc8c8326bd1e6d9fb65a82e05f25e97c2a9691da5408bfa53299f4ba1999765e4e2e3cb339679f2f16587d014a1959249ae3e462072b7148f3ea1b6591de41ee8ebdd282c9ead76c0d6af053ae0a314be355ed18314133fd2af0b9bd4a94fb54b2e9dc7939db1ac95b4fb5106db533859c8699ff9e10dae3d199590877f451dc69411bbbed3b957d8e16f0025d54c1c66b6a36ca200c3c8799ee46c7e337a1101f5e007c49ea6b5d4ccef4d2c51c62bf47e40d00f1be94db968259363f1f7ad6def77808f11a64a888a60c288cf18701aefd8d014e69c8752843a5227b154d27ab9a3a1b70733474562dacc8f76af56b9b15d25f05df375a6d01646adb497dc18c55fe5b5c5f4b4825d1bc0a5b461b7d2a2e2e539cd10d277fe32f3cc81c9fb9e9d77a49d870fa666a1a1c11b43ea520475b3b092a37269c1bfef91159d97c9838e50cf38a56489e7035d48104445702eece41d12e30408be05528da907933717341ccca5af76955d67a67f4077ad30d5250bd5e9583b68b46ec106700ddd5190f8106c5631347a001a02500d225d1db5c4a71f1ad35f8ad939f06d4e6080bd9a8d2bfd92bee95b6e6f20a9299f29dae41256df1544835d85fe6a131c85aae022dd5ccb7a25d026dd393d1a89124a92ba51b560346520a29253fb2c0c224258b1d747a0cc741933ef0d02b3624139907ab89b90baadc0b609364b8ca7c55d7ec80f590948cbb4fe8cccbed92a25ad4f74f520e805b50912847bd324dc882610b25fe772e15e293427e967eac6a27cef7f1f62de1ed22499941e072a50900f6d43dfd210e5c1c91f63593e7c37ecbc03b6b7faeee63ff87d39d44c4c61df86fdde6a71fb2b8e36b150ddfd3d31873fc10e3845c6b4a08c606a8c248a08a67bc3b1995c284665793929aa9ac17deb02a071cbaed4996f12856913475b29072856e168c9dd56f20d2bbcfba1441089afc097deda5e16d4f2cb029338872099b77a099daa446909fbab09f61f9ad0555fc987a7424e669810d633f07ebbf3eaedf83dd92b36588b442786176e45a04ecec797739d0e6e0798c1f825a3d56b2983b590e42d31da286e17ee71324427199f4fe41f938236634ebb96b394398001204a998546eb5eefe6f825a4d5828999c1196889542bc9c30bf1cf224bf5f8a1fc4768f7d6214843d189b918c6e0f7840c5866ef3eadae8317c60afa1fcd82afccee472b2747a2dc4adb666ba92e490c2d50a05823feb90d2e2491c12646bd6579c91162582070123a811078c262839a8603285e2f9d0871218175d13d19c108c9168c6613202f2f52e50545ccfa416c8e27f717b087edcd84ddedca25e4a92e451a1b856f72d15bcf1b0e76a4404017b0fcfbbae4d844e77093b0f807803bd9435bb1346b429e761417f9b21dd4bf916ebebe788684a75190d8ded16b1f6b7ba456455fae21dfc1d355be2ef69db9c3101d9b1a5c29979bb9c7f7e8de125fea325c66ce689555ce6f936f04ed3cc699fd32c432939e4209bc3f3347512c1a7ebcd87e3ef5249fbcd8edcac926425e6a845386f5424ac6426d5b047a62945e4a17a72271fb0f7215e43613840c254a73b974ef3c205eee795ff454085907167e711e8a6dda2fbb9a1b0d6ea24748d8ea0e6c987e730288c81c21f511c0977b4ae550db7915bbf252a7b0de300bc602afc7d5c7a2494dbc39c64486a64a412efedcb37c8f703acf3eb848d5ca42bc13feeb302e5029047bc7a2ba426479c1e1a6fc65fcdf90b8deb9cc199cbdbd98f4c95b861fde76b99eedd482ea3b407acb3dac9c4ca027771729c12ff14699ea8f827dd504bf99e592981916c39b10c7755130f0c5dff71c7420e1a0160d39b7da0b90db76d19315640b40945d4ceb0ecfa8733cf2b18a4d81f20df4bda6411f8cec0c5ef891149aff4327f52dd0d2b44c77932119a38482683cbe195db621e75eca548db2315fc8761643819127b238c5e95957471d9f692e715abf7e66ab77346f1de3c2b28a2224a22695911c1ac251f099519c42ecd6e87136d19d9cc377b0c0a38c3c8d736bd6ebd16f509c4c5e1d6fc12a75ff8a1534a0a717425f53a7161b4700651981349cdfb4383e475bc393e9eedf705b2b5608c261ffeecdd70681b91e146cd890e3d55307e4f48f6fe5de0167cdb49bb87f1a4768838a71a6943e79994f3e6b68f9752194e19c0910e2bd74bbc134abb556a90e12b2abcf962db5f9bca26aaee0233858b52363465525eee825da2dcaaf1ae96b39e6f58d3274f351221e966ec2015ae0cce0341e68efe50037385ce872451ced3bf05dcc647121b5abbb982c51886213acbd1ed18cd8a7b5e64f4246c2e2e63ccaa8a2dcb7e096bf540431547dce602887f3b060d762638e968ab5f294a191e847b16403d7f8abe42e22380f205b1f42aa690dac486aef47090c4ea2e34e82751d970115f7c98e420ac404b2cac3809317f79524286acb16ab6882a7ea00dbfc5b757b98661b20ba6a764636221937d16097eb4907178f532254b19b0df7e981aaa2c364975b579182aaf425f418e9c6a3f9fcb00d7b80186ab3238a61b020cb9937e2a68953007d4a5f249a4fac27669c1742d6ab54c6aadd248462109f7cfd2b6c91f47e9c7dfa04fd644cfd00e0f72cf51440106e3838235d017e33210270c58c6727c1ea419cd7b593aafe7fc224b60d92ce6dbefd4467c14b46445606c6d350d03db69f2ecb133e5e0e75e675249ed783c09bf0f7ec7114c36b6d5010418d07b4c61561078403d05d1172354dbcb54709c2ca970bced56a429def50a3579668743ff8a657509d4d2772a3d2dca74a6f2c090869a09769cf34d6cc9b1f42a2bce795fca00070a600b29d711253f362ac9b491b8bb14c3935851914f9276060fdff20ed1bbe6677655c1e8c25df96149df6cbfa427d108a9cbefa1e0650d56d431d1dcea367df3de3a46e688208054645fa264ffbd5ce4f1e93d324147aeb597e3600988115a6fafdefd3041976ff62248e814edc07e61256251f1a59203abd2f47ca9a305b0a6fbddeb062ed1b48f1f2d688c8782cf6da61065dee82f132fa143f0e499d9825468f0e27aa70c08aefec8fb6eb481166e7de376d16a507820d06545dc62655af9fc2db498522f6a07a33e3200bde0eb94d48ac74b582e5c76891241775aa1b80375d3490ae526ac7fe0d03d4428a6c07f4764507ce14b8da1f00f7763afe0b3f61600eec22b9e75c175efc2d7a9a27401b36faa4026a289014819a61dd847531f95f53d8fd856b4a206db52ec134ad8c7a1872a6dbcde2d8d67631b5ab94331d63f036191402274763449b8b4dd5037d142d838d6207aabbf68546bb9b4d9b42e96311e06627ea809343f6790f6bb2afe5544142b149efb0f1821dc546fca108af61ac02f205206813d6adb1265c6270a78ddfa19fcbf55a84875adb7be3ae07874dc49b386b2fb9c859e6420492709346dc90bc161fa1c5e04aefb4410501427cdfe9f1c1cf530af46cfa7c50d4303bf3f60e57c51efc207dda0dd690f71d30533e8299a1f3fa8d2d9c82eef3b8cb4c9ae130fc21a436b0ecbb02355f16bc250ece3f0d8199f5b5ae1226ac71223c2fb7d24edc80d108027757068e4fc5e4e9c62220ecd0d2365feaafd47568bac33d742a161da93749bb3cc0b8e0dbc6a97876c3412f7774469a3d0067cd91a04c5d935f4f63606b738f854a61ce6308e9f6f0ab4cfb91cff06af9062889b12e8481705c4b1309036e88064188a9235571760d3d9100a2e7496290d2de215909a59d915c80a52d37bb6f5ddb3d206e7bb42d07673559ddd5a8e02c5f955bd035e4eea3d6b6e61b8a9376d3db6d6218d4e8a0c9df1498c0a0bfbf26171c4cfa01de43143549f4865b05aa479ca3a2537390dd465d8fe6a4507c57e08f6ec09dda52460097b008d94e0e5604d48f1ad4a8803d4ff81ab2e560efb95d95c3a5e717bed480591c335a4fbc01e68027c25bc1b80cfe2956e3ccbb8053862e647e8eca6e9048799449bcb356d9538f1e3808d0e4a9d7df6e55a2c6d573e7c68e95968b01e4ff93b100b96745b344610a1d3f1a33e0ea402ff2484a8e42ec66f014502343b4ecd7ea91309dce5d36d1cf00954e3b21218884cefb38e165f8792cf589c8c9eacc9eec721e5f4423506a02f99ea9f3382f2c57799cef7d715270e568276a0f968b6b82b1871acd05a4ef5f4a94598811083bb26b1c7766f08530c496e90635dd045d9b46083636e9d714033bae491840638e5a9f814bb82a807d403daf6939c810d350845e2dd7d3dcdac3a8881af1b9b55e6eb3bbfb941be4fef7b02488a4f21c40179eabcdafcb9d60dcb1492385970f03a03cdf2761a94618e81c633188c6d2c33f88ee94087d874d005c41d3f969747767b0d214c05cf96ab141c2f8fa6e6440f1e0b0c1e4652bde956f88bebc699ded1bc98c03ca9e259b5baf92d96a82557880863928e9292b8c564f586296094396c2f1696c92cf317e9a6171bed72d258eb9a447d636ecd532580693ab721513a37e6ef3a13322e32c88166cbddd36623095f85ff2baa6dc22b3028211b112069323a29a011fada08194fb2bd78fa5be631afc50b8ab33df24796d3a778ce1e9ec99651c515ba2714c16d61b79a2d7cecbf7a7fa4b8ddd243fcad7bbec56ef897308ad3054459ab906adaddcf3696746a52851ace3081c3b136cb7ed7e54708b867a1f72d32b87507a70e1010a4039305b3a196b74a5a079a13af8c7fc2fe48f848fc0464a045c982ea50fcd0248b85087419d6f14bcffbe02c5815612fcd2cf1e586312c33681b2f18576e6b7a28015daf6d5acfa8e44775c97464a633c779f28f9e0fc135d0d15ba63bfa65f1b72f526a690c87d9a1ba47c9f905255b6a022c4268a0ff887979aa2279516d6b7d03a30796d2c89b1d9e8df9b914417932b170499566cb98f3353ea723ebe69b8453e94b802980e2ad5dfe543b24b11d8515e441991b28aa02f6c87ebc52d520709e8cde6ac8b5b5dc70f44a9a9b969488f45a8d3459e4aee655c6cd96a5c023453b24a15e73e85455d1681a6d2ebef04149a6e65000592ef8f06c3f0f1b8a3be05f12653348b44a49f925cb44825bc318e3c9076e544347747899eb2aa23ee7a58b191ad73f03a9e3f25f61190d1746c658d59acd44dc6ef32926f4902290589ca18867c15c217df504d750028638aac618fc8e629571908804f7eba38bf53b5ff21f8d5f1743cfae96127a4d8d249c54811827671d0e00121b11d199b328c53c822169b80d2a3b490579063dab849c70b28b50d8a53cb9185ad547828fcf146e27ec1b3e999f39c8134d67287f930b37f976080379d5c88f3c1bf84465b569013472f600d8ffa9c3dd69923d192fcfd1053e59e7b590cf08a3256f01e4e5e9070fdb329f5b6618c53841e4ce87b15120b1cea3eb7c67668f74e5c96a04d44c5b74b9f9edc434b8dc1d714ba6d358e7ff48a950c1332123e5b2e846571a2faa6d42c3a8edd9609897a04e88d4a19640b746c155e77a332996a08888008b06ad988870fca59fc1802247b25c09b20ffc0fdbce4b13fb548a6c14a68a590be0bc606eecaeeb7bd3cf989fe8d38a5d769b40e8b1ce201c23f17f057c01ea6cb747e2c655d2ae7fe891403d71a68124da323785c4a2a321f9db972200dc5890dfc566b4e83455635abe4f13823f71c80e45dd091aad9341dd899e959c74b9f6d3d3263c251ff92827b6ee24c14ebee653331368d961f0af052183b1354910b7d31ffb76d2897569cab6921599fbc54bc980bb3c7f8420fa4541726621b144660611df8b828e8ced6540c487b0275699e37054c52b3f12be37ee2a825b36f5627c0aa99958a63b7073c49d5da1e0572127c3abd97003e8864465a051fdabb8c38254751bed478073192bde038597b32582874d5aa5935831e1e8a2b561586ca61da547e9f1f35194a7414ad90d34bba681d5e671e1c066fa034a8957d40398b99759657fe67a53c2370fe86cd23448c81a98dc1192a7ea06a9483d9681de3f5a43a10e68307a0b77588fc140aef301c59babf091e21708db785fed0d99363ff69dd4aad16e4494d5696a2eab7910ec70546f75e548990bd769f725339b81e2eb408a0b166601a4f302e25e82db5a831070484e8e9b35df61587561439f8949feb5809ada43b0bf5622052d8068be5cdd20fe0e052f08dc2a37f6a25701ee978364547223b95a87594ba341913162ec5682f874153f43ee93bcdfd5dff376323944de596b6c67cb2c01675c53f2cdf89529af70fa52e171f80b34c7b138f210ff262feebed9a356b55bed9f83d5ce8ad2478345ca8b761bb392d02765f82494406d4d2060ed066072c04f55cac673935c500c49c49bb1ae4423fd9425df2f36105474b7a4c862cfad34246cd299987cab93e61767a2510636ba9e4f59d249bc0dac773f37b04fc14f244e0bd4f30cf314ebbf8e5768ed92c1c7b9e2e42288446e3791bdf2c1fc031a0e3e4ed5b3001fb2211c0f02c399087878eb0c4bbc52bf770034684c002c58caaf75af70564a8b7988299cff2eee35607647fe4d1f087dccd82b2b003f3b38c20ebd11f8f4be5691c9282a2e4a762385a3482b33195d35de78f42bd8c9b2851c5f05e67f305053454f762758727de1cb3f610540efda703328e0013139fb55a01d2912b2bac3ec85b9edddebccc5e5c81d1244ca3d0a0f303988efc28d5a045069ada140ef6065fce485c3a57b2a7ef624b91493ce65b77bb83f1df79e6922541ff6931fb8dccc8c52778b73e2c556829caa5ef396a273670ec86ecf0141aa49eb2eba6338cf56ce53bbc59cdc81dccea8d1236d39696bf8cd781484a7e325cb39b60f4fd81734a3680621aec0f3ec25c337bb34a31ed516c7fc437edd92ae9c2d67ae9cf25fc99f251d7450d7c0d283e9828427d0bdfafa64a8d2bf094907253b64132b77a4e9cda3dadf089e70675da35bff0757578f3349b748d6fb8b61b36ee3e662ab98cbd12792d623f444d9a2e04aad858288b8fdb6527b26e55a2483bfe7fbb0b37f62a83699eedf5703dfac6d0821d878878c2efff78ca4f35aa23a3d5f39ef3ba80fffe756d1b784b667e2a2d72312f5f885a1027b45316902844a45dee201dcdf64285658baa847a25751f3bbb465de7c817064369e36629a9e0203d2bf92e937c05487357c07e5de84dfd50140f3c97e1ab891d33016a6d3ab675f30283498ec5be96e3b73d408c25de19d136b95736b565980081f0b94309f0913707e2339f0148be8634f7dfbb4bdeebb3a08dd88adb915bd26eccde393ef53a758f45424fa172656ca40445f4598fa52f22ad76259dc60b84ae6ddeeee3a3a391e588f8fa7977d702b9dc6e84377b38de729c81a52703db177b5233795fe4047c6cbf0e472641f5a3af955a4362c44226fee3a94b34d9865d1a06d68f0962216d4551759bdc2a3727a50451e87e81fb624cc2792ba6adf222f07cad9e327a549fd03986a069aa16b10cb6cf2d635a137259460f17730f1b8d495c9af34dfc0c00841fd1fc12179a8c3788782ef19b2b0a08dd84b673dabc8d887d9dc0bf1f812a9d979884ae836640ebbf5bf286142555d31ef225cbd0f7ee8007575022a707f6d5e509367d62f1ea34d92a65075dbfb3368705d09d34c9063b9519afa331f02c00ebd6ceda9ed1c92ec490988d26508fc13153f3a9084a593c55ca5135032cebed1beabd130572bb286d33538a0d748dc0a44ed023cf5cb8798f33353f670e72dd6f415c830fadedfd91418180ce0fb931996901cfbfc409192ffd888e78ba20e3efdf681e4399bf5d86fc0fd34be9e06a478f7dc4d3afa58e1ad463bccde5603e992ac5e08161679645f060038188514b73419d8893d31265a3ed0d2499fb5328902b6c2406562a43dc552ec020854e3843303c7299fd70c543b9eedf779452bd89ec89f38e9dfe41fc2b47b1e1108e32a002556523f083362491e47324087422c0c0ae3b90e81d2f683e0e08197117ea7dd5ec9e90e2a1504c9771867f8fdfbaf222162d38ba60ee7f282458cdad3f56e8f9c482629167e64bb4113e970a8568fd1122911d4960c3d08d91216420f72a0348942c986a7c4d24b35f053a53457708854bb95cd10348c5a529a2d53df38dd440a08d57b52c2e67de1aef2d231c0d1fe9bc0d0143b88540a3d3251283e59f3298815d2e1588a90335568a50f85cb51e5c95856702a609fbd417aa311db7b5e50b0d8c8387f4828af2f7d7c92670629e808989a1db3ea050626536b4efa83572dc5c21bdf57fd58a509f845b3193fc9a7756b0816c9ca0422cd58d4050eaee09260f66d46923ebaf037433da44d6a5566ab849fb7c225157bd487aabab056c972cdf787b0c6f6707bc74e66d7e60c0140f7f2b164fb3269bd7b798337a1343c533f0231ae3a105e587b65541831456bc95b5710798ba23f069a25ac08ba7a998312262a70ccccdbe0bb3076ca7d6ac58571aa182e6228769102d8aa1bc2814718130a62648c68bf8a1bf308fa8664fc490eca53742cad34fd8b017b8d0920821ffc94cafae3493158046113810c058058d117c8ccd918d0bd180416fb7f3836ab92282de3f9014847bc53c35e08ace2973d45ed5f219cb8297d2922540090593b3f1165365a47a61505a939e88da5e8ede378ad366fc5906ea395fb72edbdb5315c543f820002ec4016d45fbe48dd6e0f954b408ce48d432799041878b9c0cc0b84f2882c2a720d502b0bedb8310b0bbb8c4c5111f93a299cd433c74945232ca5fc6d666146429cd938487cb6f662d037c8fce78d229be7a0b7de41b2c87e69922614934fbe2841e837691d1198cac4be85e47a27f514746f4387087d9be411b32493a1046e876a57db8a9f4ba7bf2a21b321943a07a000eca90d5ad6e4bd905d428690648596499ac91174e24ced6f4e03948a9a0cf20dea612e664e9c6c7300d4cc680951f3af1adf087b1f0badd715c674e33b90cb48d57565a3626aa905992b7de543d1fbc2d700d519f00374cb9f24f9bc465fbc2f125e4a143f1b5462b934ba3b4eb4c71897eb57990bc4348936069f643bb993c495eb4464e9ce4d307d80b43c3544b6863f384adab62b5a0ce23fe7316934b42e39c7028a469bf09cbe4942ebeecc0e70f8783e85b062681dae7656622e8a415530a57edcbc90610cb8ba51f6c00925972e75087362a78243382a90ceb91ac0defb19bcf033f7404d08cde7638d749877c0dc6e8123c8256953c5b93a083ac074664a3aef32cffc780b154f638b3e4522b05c36e5e3894f20e9b4a8f42f1d95640501cc6a6d18b02f88fb422fdcca874792b3e990289b870c9acdc359a6cd835a531ec32e9ca9a07d6ea8244ba30e32c41978fd11709be308d18efbe258290d1bd1bec064569bd43c233e22f3e7c652c4e38adefa9ef6c3462fea67d40aa19d7c381416d7efc6c68810c291143a4ff17f98c2a88bb64fc881921a3aa982f2048b4adf6bf689114680240665c0b44d9cec6ff97c8f2dfff50cede76026ebd3fde72e46dee52ef645fee00bd00aa239bb6586b6641d44376190b6df1d4e8d324f6bb427e4a7d1a07ac90ded3e4f9d5e4656ee8972560f8c3cd59abdc5d089686dc5124ff3eb58119320642bf7eb3858fdb8906fe222f8190f8c0c1a3605f8892c60e454d79624e7bc0dffcda84b20a3fe0ee56936f2deca16e7bcd081bf3c9e8cd35f54ac4b754d3dd2c8d521fe49611340115b42a3665a4e8d246c4d937918cce40298712df09077432fac8a6b747c89798b0e3fea5efc368f46b43d83413b019c62cb8319c8b4f56d7a0bf3ffa7cebd6202b1da2ca6bdc296a1dcd758f841ce58a04d76049362542e4d21b83093823adb341df7c13ea334cb0177c591a81fb0b0219bc108bfcdf3163fb14834f19c597151f1fc749e17dbe2a2ba38232cce656c4dabc50505c6c1ce8ecbc772eaccb8491f80054bce0ec627de084c7c1d8eed2e027d60b715c567ea37a064a1a219d2af8f55fe6a7664cedc11c25ca8c13690f6d4a62593163cc25de10b3b32a5150e0ee0c628b07649a820eab595003a58ba4ec0c1f73aa4278c79fff99839a59e96e7e6d2f6bb9be2ba2d72075693a99a59555b488778777f5855c8268d4f783155b31224054bef0b3f1c0bcedd00a2caeff16995915465b3d22ce318c367e1ec185ea7faca9ca9326a95999fd967a98a53dc60b8f3be27e2128a947abc3fe1b1b29eae227c18f70c6394290678531a26b0e7328e5292dc471906fba4e2bbbad6e9d831efb5e91968084822334e0e28c63cbd007d66846c72cb6b1d70d4d3e9e69ee70acc37cf0829c5f22f08060145b81c811dad918d3ed001eade417ffbc88f3a9695c068736b6425fdfbcce410e4482c3450945ff9923e5af661ddd249cebfc76426d95e2cc22e3453f14d88954172c731a8347db10cf08f077d81a5a0301224c09f060945b223172c61be93a7a8f503c1ebab3085260e37cf7ab574f821b058b03cd90372bdff837e9b016d9040a037ca1a0d3b51c9a4683e09bb28682b2e2acaf865e413c570aa5d70bef9358f7ee455418111cda2bf3ce15f4037420942d6c0fea3ee610149e426a43c421356430d280d190b6fcf101e1af650aa84ce49d108b6e31a7160bf43f680853460904aff046c9712c642646055e85dbc2ee9099f48db546a600e8cfea1d8f95e3f15c35652f41e261aafd41053abe55f96a2ff901aaa2f733be85eff895ac15c8dbe23f08abad1e3d2b1c535c4d9edc06956ba63aff0533d1f6347927738837d70a83c713b82fd9749ff990d0e72dbbb2f0817bf6dd5019a6daea339ab585aa4b666965f4ca29d416d685278d1d653edf9cf38d2be134c4d5eb3692c1caa167c3f3a591084ac4d352f1c602ec2351b6184953b255b9049ce7bc6bdd44355e042ab8952d503896c0011a3f61012a7b83fbfcc2dc597db5c60b5c3702dca0a4f5d52ab6474e559adf0e1cdd6f74cffc4ff4808432b8868f40c7bb0319b6e622da1d67ddcfee8f88a5c250ce6595eb9aa15e46d8af82c0a7d12ccb4961f71683aad2a1ab1aa5a09e6a3710b0a555870acb2c251cd292bd2ecb35b46e10515996d0783fabb82db59ed2fc45a5f69820087e5d9b2c1d39644121735ebc166ebb09ff7719eec1827a6bf518d62930965c4a66274b79c59624b14d54049a2e5a21c07c777ee968c2d298f0f2dabc43657bf0f4125b610d52e3e5e503156f2875789ed8ee4035b832c9203f78681445895853bf7ba3a506f8605081148d4b0fe803d87ffd669f877cff51036da7273d1ad6df22ea8d13a4408bc20098b7615eeeb968688aa7922f3921367d2288a47db71f694666f361ff40023931d40ab835eaefe46fef5180c9ce155ce39e4aada4115ce7ed23591706b159887b040d60228c0ef718be24af9417ab8a91df82a15083196f93f3a0a9da0159abd7e6b7c56479c9ba0bef692465fdce925124da45e67fe5d19a2860b23478b9b476de912089fceb0b5d39f9c68d85c15fb1b36829801be101156eaef7b2eb1f9ec48ed078475cb0ab92852a0e1f70fd2da22959d571ef4094ed29c319a2833aa1101bd03292d827d62ac80288c2777b9ec3341ed1f001f9c0901aed5ade0629834bfa060d2536c1d84c77ec49fb6517925a8aac57e793cd3b30412e3f38715907d5644ca1a7d617e702a5ff7082a6ac3970938d02c5c3b141ac92a2aee7a7abb4197be4264193042ce5c541391223af08adedcd08463d6b5f39b1065b0dd167d24b62128c966f5fb74b01709d647893bf128116c20a2c077da435317a0359d4127a730c1e93e14ebe5cb9076be301c6ab90cdd6c0a91c8124e385868333884c160734a63944a9e6dc64b7fe2eb71fced92dc9c990809bb052756a66af59248c75a61b28c85bc7e90e78457135a53336271188030d150a239e67e74b3be160a901a73dc564d6ad6146459acbc05e6a13bac9a577347e35fbd484ba3d49805b4e4a1e67b2c897159cd484fefaafd1d136d8e4c9d1ef2c5f0178e9e4ba2e54f0f801cef0a8659ed8c4a269ac3180db58b949249cc523340283ab083458cea6a050ff2c8bd7b5a16507e2fc0d8ed868bb0d8417aea05f65899c92881b3f22a8714e9f317d0aafc01f7d0147c76452c6240fb55de87f147997f64d32809edef7a24eb9b5017d941d8f57216c4d7449800672b2f63fd7f6f2d8aaab0edac40e05a3ce7e2bd6bef3d6cf5c12a6c32c067cd20e7457d884bee1fc63615bf7653c12d1387ae02763146d0a953f71c7bc0bee12ff9f308ae1aa101cbb1a36016ac927811c8be7a7a93ae6866a426d0942ae66a99c023db1b806d424866ad91881bfe29f016701f536f3b2955fc35be3dde5a9e2f0fdb3402cdd7795b0fd09bd2e5b13f24c9674173dd77fc86dc233ff0e97de3e603d2192db3dbc577e03363111c1ea3c08b5147ac37da7ab8d33f3401abc72a77bed5cd48f95322290c4038bac2ecf1afa143b918aa294324d9396e2cb89d13df16474cbb40a23b8eeac420f9e1e23ebe9ba4ca083dc3427099b266a77cac8d21497b5b81a0495aedaab8708c0dde96a0b9ca2dd539320ee96d7fedaba23acc805f12db1fad9083906fb8e2cd0a081bed71fa9d285fcb8d599c5e82a9554fd366936f1036042fab798d2bb2decaf863b8d23672d6802b31da34e502796d153a183b40bededf2de388c844ac415236e01100a98608f0cda0aebecfd68b33841716105b120729df3d0109fefd8ef4d26311b1df4f63fb37927bd0dd191494400c4cc56d18d475a1f201a4e1f6a6c80b0c411cc172e3005732305fc460d333d680d1ea43416aa56d8c13c8edd613af0caa9946fdbec410361ca699faca9a1768ab65ed021d905cdd8f284a1dafba0095e32a100946fcba3903699c1f3b1010e327726aa5971af683ee49b089bee4c5a6a0f786ab52bd8a3b29d559e78a82463af18ada2ac1a29d1fe1d670d476d1473c4404e31bf412eab96340730d2be7392c29ac557050d011ae9976b3e17fad18bcbea0bfdaf36062a36af88e56114f5108cebef2ff71b47f0cde528428cd170237b95d52f3649417eda00d198a9ab376ca406bf8e1802ccdd2f6d799bea21a5161cdc4c0fa880d16fa274eaec35593e780396438d62633a909f91396900b16d4a1610fad1cde0ad2794ee9e2b724e933144cb3e746d0be377e3689e15b0cf22a5cd3b1f102b44946483987bf022c60b61858f08dced8887d28f05569caefe1457e673a77685710c75d0a05ed8073933d8808d61fce4dfb609a08b3c9935a8799c746379e352c07481f3e7aacf401a06d5edb84b6f0660b0da68baf5c0e38942ec217da7e46881bad5ba5caae2aac0555f3b17365b21cfa0315a89d858d8f1b426b2bfb8303d7c18b8f97dd20160916114ed8c5fb36af1b19b9b8f50980a1ff270ac8a15e2f5856afe94845a27243a9be93b471c4f4e4aa1eec402c7e26afc7de69328db10cd9d618e2aeee6336ebedb49bee284cb3a4a91fd248f03b4145f8b667ea705c0f0ddaa0d1af8c2c2ced6a2cfcb77b595b675c5f5b1bbeab2dcda0ef69d20bc4e34e30d367bb6cc4b6456ee23b22fb54fb664b49458589c83d730e2af1f9350ec119c04029152aa698953d4abc94e2b3c2c47899aec4d17e77c86f0a3f8089b675dd60a13f963feb0a4e9a946e3e4fedeff365d3e478c9449fdfc5eb72a5dc936ebca4cdb85b5ec6c0217bb9ed4f21ea778933b573a52e8f7ba97805bb205f632677b75ca19c95256b67149a72e113492f533f721eda144ec1b2be49fd97e79aa12c7706ccb4f74ac6c99667088352a5f98350dfc48d623506bacd18367a2ab973d06e586c7d5eabe61540c9091f1c591cb689da4dc723f75db4e8a9207c83d7f8e05c487b02689a1fc867197a0d1d38ea1ec5f277033961f3658962aa51401b3b2790d2be9d117879431097bec3efd6df76cf06928b000834528a6dbe574e532bb80c8eab0db8e04a8cb9ca3f81dde363d7f20613c7983bd5b6e06c105ae6e2f535624a1f89bfce4312ccf1334e4798e15f0aee3752b9a052bb4e72ab5ce629db24b82fb7da0c7f085606936f01aec1c103bcc7c4043ebb606e55c5d874867b32beded14f55b25fd6b6a9057d16d1e5a02f349962ab256499bd09bab584a4037d6fda634c16add62758c95b9db7d851d8aa4d4ade0bd8760bec1c94e017f49e46e5b8a363c7ae71a9a3ccbf30b6f0542cf8029e94566f42817eb3a0bf06db76c7da1a5c62af838ecf67b6fc6232a378f61856d65bf8aa0acbd6750eacc0044d14b85c831b1b4367a05448790b96cf98f4478398782e1fde71837c881a047e0e872c5e57b3b337c5204e42ef3d2cdafca83de825caa66981b1df67c1b7191524fd0b0d5159a0608baaf5cc5c9b938439cdc8e20d3607b3005f677b01d74023e5aa25df19034eb4d914095ee1a468db53e23dcee53b53ee136425c9332c7cfeef3781078d03fed60596cb77664e4cb7c6279a7fd137fab0406887efeccc08d419825620c30d0d1e0d30c58f8ccfd0c42e6712a82ee6d3dc1cd83162b7769f6a3c9e2161e83901df99befcc576aa2a6e0e17c4ef6fde84d5bc27f03e8f0ca905e09937c95cfd21bc6ed7e43af57c93d08371f8f3f402970414ae23e9cd5d33c5598a6104daf9fa3c262225e4a68bbd30c652560d1a4c5255e3ee9cf5f3095005ceb76c9aaa76ca83652bc4adf0bfe1e64804e11626e1ad83e43e6643546c4f9a95a5ea3c05b029c2f8d9585a86b51dccfb66285a28f06acf295191b4da83beaa53b43221ecc30cc60bd1553aece2e8fca800d01844387c9b98853229eb329a175eb2e6076962f058ca597a85a934e941ca824a7aac76c718163e76c86970a6a938956ac028c270c9dea4088b5a6fb2c65ec14651e330797276e760f009dcc0ffd116982130f566470d44996db7b03dae756d4f341c3c12fea3893aa724ba3d7671758540c8e85082d4c00886fe482ad065d42b1b9560f59e1552eb3b2b800a1c211cc8b9d197a8668d398db495807f5b28db4afcafa29c91be82f269e43167cb1d987c93be826a758ef807387f73e4d279d4de01d87002427d7dea254c1574baa7a2841c99d1ba370112c75ecf29be4edf44627de91265d5237abc86c3585328af84b0211d642efc2c6db71db8addcfba4dd665831de3fa975edb36a28bcf4f73b9b6f1951c2845421aac15e779b1f3be5c60d3391e36c8e827efd5d1c6858caeb93b0f1d14db4262934d14068b31250c3ba83379ff38e29f1834bd458f8639dd8fb3f192503a858689b614d824f647b370d7ef1cc54061ff07d850422a4e5c76854c5dfeb0c9dc1a84415f609085af95d25e905ad9a4cfdd435ae6cde60e67bfcda5eca213be2c327a2dea209eda6b6b64ba99ebd9d85b1accdfa2545f88dcc673ee2fe16dd32a32a4c6f534194b8f24f3dc47f74cf29fa6245cd73ca3d6e9962bb889bcff371209f8a1f3068af50ca672e2e2ba354aeb74ec7415739d16d23ec4f9cf7231ea751e0b583a39edf92f447b3fd90cbf2ff6a3fbb1996bfc71968ecc8bda32559406dfaaa3ac03ad425efa67cfd884007a34723d272ae08af7ec4cbcc94de0064eb6737340be22c3af54bdd3dfd5f8affdbbd94c7add21dd89ab90d3d8fb6a054de0fdfaa844adef5917594c3df70ba36b1e1684751fc97e1ea2a4affe2bdba832b09c0eb90db33a1361e58b390f167fc31a79d46e0ca41348b7dfe23c00bfd8c51af14b12b2cbaabd28f6233e7c2d62a0a48470f746ded6875b18b5f74c33517843d964408a6209d9d1f9af302cc01531a963d29b24cffd7b94937a4255084bc740cc58bee5043832e36152ce41b8ced19a4e08312cdeff31b3d1ab088036ef5a1edcb7187cc6c554c04042cc1588cdd43c6aa77d16b285d8e55b86c6c09bf020d1061cb951a479f2758189a2ba1b3ac30f1332dcdf411bd94630af229c03596fcbbbcdca34514474ddd33a68e3dedbe35a58d95f82a24cd0a5e0fd140ea49f56a2b34fdd50face58aae05d8e2d98088a5b3c934bca3edae61aa93856d4962df7467483793d0f3bd517a2084ad383e3e31faa23861dea44d1a8ab4d6cf56f791721e19cce53a5ded0e272d354dc8d0ca6a17b47414a9dfaebe5c57ced0c60a9d27a9c2962ebfc7c958e3adb3a355c843c401df74fd683061d6aa402f7b5db008d917cac217142f588583aff9cd76b957ff322c839154930c471ad0c907357163cc5641033457ea7e08f90bc6e44e5958fc539be9ab1339ff3f381db7d037e224a601c61c1a41325bf04b1ab0099dd15e224de0cc584ac081c611f333353d68af554ebb394c7322a4875977958f779e709e01ba22f675d2a7a7c8fbe1fb9b5398cee09b3e2a19e55052334ec048df70dbcef2e4a3dbb318388ec47e560bad15b8c1871380e12a74fd73d9e4fd687de7061a4ccd181f7477ce2d052196b72d3008afa94e3114115d01331501e715b9f15726cc42071368649e8751c122303da9a2ca7a8f6f262508adda9647d5275b5c0b11e22e16da65f48fd3859e5029706cdffdb7ca110b8f82727afd3e6b7e579ac39a5fab3c9a838ee61ddb206336be51320fa018b2b9d248b5a0d7417b41e43d3bb4e18a5581f9de36891b45e696d897763efc6b6f12f0fc26b9b66cf0cc9a9f9b12b9bcea97bdd920e02362fc7840549b99052ec9367f5b955d578583e37f414edaaa0493b6635b1491ebed388b1dab61aaebb9235d4c5439a23e7cc4d5dd2a4ad02784c50ccff74fee83fced5710f83e5569dfc905cfecada97ea1acd41fb2818252921e43dc751f99a0c8d0d8fa8638be4cdb2108e812eac6362af6f2f0af4b48f9f29c01161a1f96bd9e52168baf475aff4972794e41e0991b8b1bc974575c6e607661c607983f445e3b93b22fc4479ebb0d17c1385b987a80e86b0e1e64e0eb16d8e07db89f34aac4d7a2c122bea1662bbbab82f5333add2fb1847a41b9e944a0669038d1c136d72461d4d8fe02d9e7930170cfa64ccd22d6c3c465fa419649215069da16fc25ce18a78ff47fd2ab8ecb47baebc96122bd7da2a64826f9319037f566955f2b8b586d363312aeb129464fa2d4f729410da20f5cf64fbd68cf54d3458060e3e0f4d5c88446e0d605756b10975d585fa3b8d8c5fa1af9b80d680914a1e86b24c435e8705a59da6d11259fb17742a60aa3c4520b7e65b7d13e52e2fcdcf7bb8288f528e1cdb4f1265fda6d5dae6bb67d6f6a71c36e6e4b725465c4571a725cf082d5a8b9e07952f4c34d43529322faaa623ad76b673d7b23bd78b7de2547d1175c9359fe2f68786ad351a39d1e78807bbd05d1fb82d3d487ccc57548e2a9520f20034a5d0c408afd21f7fc1c3dfce8a393ed11a38129837bad0c99340a24e744ae27deffe76620b4954192605b2a5f218020eebfdae55ad9198a4fe91fe08e7aaf06b74bca749a5528c568966cd13e092ba2dbe1c02d21464d12f4318c17192884ac2277bfaa5db5b91b60c17ceca47af58e1acd01ba9263518f1f78ac7b3de8f6b2033d095dbde378f30ea86571f6b0958453fbabc95d4b941bd66035813bb9107f23853ab7a73a16c64ec85f2e9aa81bf47e4c53c9c8ceeca4458c45aeb9264f46cec4f47ca7a36d07d871c0115a784a2101f4a338d54d4cee20cbd78a07a7cec348f802bd0b05b3b873e0a08e79f55e873cc3ad1ef47b45253e454568b08428338abcac7dcb19a1c2d62f4988426a64bf82f2830ef428b3308c6a13e96361f369b391eaab083e25cb8304af3ee1d039abf49e748f00a582c29e46804a5aad60421da2c8e9ad21b5c5d6a8caa988f9b050096a24241b6d8eacb41c916563ecae0581693a7662016b929e087b8ddf9d067ad05c0361330c424e0bae5625a42335ff5e417f5537d99a3b0db616d23e657fb6bf842896c3731562364c6849891eed795510875a4b08905a333996475d4d35e89539909d1c49402f1c74febac3055183c59c2d1459e98a8c320e82c0488f496d7536237fcc31d913395bc49fdb0718c0fb00c688a777ae222e4deb7b30f60ecabb2ee6ac41e6113cab25205b186ee94d7568c4b2d17480e1b94a7c0ad9d2774df1543306540e6e00134a42f83f711b283e8661da0ced3038fc2cb9297a25b3ff083c0f4ea2a8276ffb5d6a33dba50573d7d2cc9061cd1fddf895c9f7ade25516667099cdeaafce4827b00f81776c68c2d9f9a57c2e38fca382255b78e116f56142778222aae5efc11ad026963aae8380736688c19a98c0d2f80af9a58682649094e6a12b13fdde99ac447df5e2b3851eea35142fece8f192c3fb226f6d0e0b79874eed9c125a002a34cd8bfbe33261284d3ba119cb34f99b0058a4d28bbab4f3b21d85d0687ee8ad65c3348d28c3413b8264e9f0d31106069fd63511c90682cc75e5d3b6b0a43602f2c6c499796e54e6b0e54417a6a5014ba38e848b9c68be11a323efa403b82a1a652222e63dbf37bd0c889ff8384d05476042a4ed217cb0121f1367df956e509cd93dc76f693dec52abb4680e8dd82089ea47cecf3cbf0c2b4e2a9ff2008dd0ebe706074f69d0d0ef7b1b9046a6c059902385931bc62d30f2e1d42657ac4509d8f568a93431520f773103e86d909a4bf109a11f34f45cc335b82bbed95a81d038c916b745dd76c6712bb78a1601ddb8108cee60f7215de7d1a09caaa8c319005cf8b155988b1d68fbeb0d4f3a63b1eda07b63b07c37aefbe58fdce13e933178958d6b54c43ca0e477c5b5f037a0fefa10dfacb8c3b1342defdb04596c86d027bd457ee61395ce9963039ede4633d158796f64dd3e4751d8acfe38e1323ea8f61661b66d84621920afe1b055d0aeae63f6e96d4b49705e986b74a20b1e8d5db0adbcf3103df7a9bba87bd954848eb0317a1bddcf1b87364555e1c5235b7e26c02812feab65b0de35e2586fa2044561b62a9be3c35713bb1647e0c6ab194527460b5986f56d024ea83fc1729ee2eae0b6aa7337e35c74000159b2e73e100ca0d12bdd04a80998b8142e6146179d49cd1054ba0a2a41fa2abbb0aa73f72f6e36225221f81c4a727ff90a173468b2c236d5049493f5bf5d313a63b292a66721d12007ed8b2951cd0322d1425c2aa3aafa3f09ab0c1b68176aea8a92e615e43463275a88d94033c85b07f9c81d5e7dcee5a0f2cf76199be3405d79de8fc316f54c35ffee6868e221989a9268bee91537487b3fed06e04ea66c90858e7660036f5a3d070a60a1b19d62af2647c6e57594d2e42aaee152234528a87ed620cefd225b1e1d8f1a70642b3814609b14c7da4a132803f76532ec57992843fe7a90662259ef7733e5298de50d64e2698292d8a38515ae08cf2f237fc1f520c4ba20630841a2b2a3f0b8319786375eacaeb60723373e007487c2a3fa422e04c472ee2830480cccca2342f5936851cfe7ca4004a4e5c7d154a5232875610611a813c4a98ce9b698cb50bc2721f08680863d6935b8bc45962b2268f0204ce4c058364797b0a70f60b716d011e4abe7ef60d65cd49d49a77724f279715a0530ac621106aa63d425b3f3936462df70ba25b0dc6ebe4f0d0bce762c70cb2dd84a3111ddbafb631422d7898a097d768c5cdd7c6b4235cef0d6d119fae78300df0c0c03aa1d566a5a4058555107f92bb2e84a0782b915db14579374935010c6388b820e7d29bc8b1e7dc1fc761f3ef00c4431ad76d790bdf2e2e276c2b89875c61be0af819a1e80f691472a8342094f5960a4ddbc8e81e83f05c4ee12cab6a54c29a572067c06a206dc8bb8ee392e94a9dda1666ad350559906fd5d9ef29f28e6efd47f9af5d4cf3d330e0fc6611d28540b90cdaaf8db2c0ba8facb5bd5fde577d2b24143b6594583ee8c3211819a3407ca5ceb1656e5c0cdbf16085b28aa9696abd592e26ab95aef72b55a5a2d2d57cbb502ba5a525c525aad95f3e53308dc8ff2dbddddb8bb312e689793bc32899eb147e886b6eba541f798184364603162620c69896132994c31626486c8c4c4888901e3d2fe1ebf6dd4187222454cafbc86c95931434ca621439ae342e736d480969c20c2f6c4e5a0fbbea830febeeb832d0a663f5cdafe4b4b830de3bbff028381c174183f97af058389d99f93d25d5a7de955d83368b4a5a5a05bdff9dadf5f51046d29d181b613a0e5a0e84e2dbaf22f0b07a2697aabe2bf8209aaa2c4051fe0dfd8a8503e770b143fd0094ab7af73be41b05591fd4c83ad8afc46b5b8d096859483e23a3bbd9a1fe7772f12c97775c772683327b64820a2a3b62c7ede0431685ff2c47da717dd7b0f23f4a2af4654f32386fb7dd584befb4edc475f35dc683f57c44783f241fc758e2c07f4cc3719176b594efbc869103f37620162c93932e69e06a5ac5761f7e0a357e16c9d5e85b287ee216753f267e7744e4e08d5eda4e4d2389238a2266ac512946bb9c5a6488874c78e1dce6e726766c93b5625eea8718aaaa257a7c511ab2108474c36a3c9765cfec5a54dcaa66d907d9d1486a0f2675a95f8279ea92695b02105493cb16212abf1651efb917da47f6a1f71f5022aef2ccb2e0ba932cf35792a7ea76c81c2644bf3d967a0c11935e4f09adb7ce6b4fe390379cd83862334f41957e33b2da2f04045dfbdf771f633fbd9d4be16f49913a97148e5a8c043149ea8fe71dd51281a9ef2024dc5f82832e6469ecf96a59d87d3bce6ce233441a0191a85f3c7dd791a8c338f1ffda656a3e3681e9e6ab5bf69c6de3b734610efc8ea9e471ad51eea33678d7e6e08f47da459a3cf797c38bad06f58f13b12ef88748efaadc607452ef492771a8272fd69c1c3594ea3a0190fe751e3f3a80557fdf3d9aac42f91cd6a7c92110b1cb8b62dca3080eaf19964c422ae8a7f17e2b8a148e2e78f77e4c73aecc324c6f589743528254d7e943722456eee3e357a649a9434dab22cb12c8602132142631a6c59ba4ad04bff9808ca0fa884f1a0ef0403fa53ccb3c4600839b3d80bf4d9f811f6b0e117e8611020088fd7081c8063f60a010182f07881be1a93a7e4f4942440101e2f9115441fe829e9001cb3d77a0af4cdf9e23e1cb86e2485372af7c15c5a9ad6c23edd9c0d1c1423df3a15a5ef4459e59d5eadcea98512ea4e51a38bb8801a472e40b75589a85542ad61597353feaa65e15f623764408d38a0079de6697e31df7d367ecc2fa6fbee3bcddf17eddefb4ef39baff835dc28f7205b83b1082ae43e343f0c3de8e35b08c53db7d08386f4c43929e550a8876f0d16693072219028c4b7663188fb984883311609238e4dc5979978a0a8a02fa4dd4fc0fbd653df04683c155ff4ada7dc7d25497e2a14550e28385c8b5ed0dc9d121908dd8fdfc21c3946ffb8c2bcf139b6377fcb323333f3333f3323c153fb0ba9addbbd110b5b75556e830d687cb6f1e2a49371d039e90c9dd3a393f20d8e8e5eede8da9c71d6f4de9b11474c2e88c8956a55be7cdbc7b51fd2a07c122228db7a551b52a476d7296a1cb920d58ead8afc13113454c38ea9962596f8a0cec5b8d39e6e981ffad017e5d79083fe801ec47d1d6b50da3c274583a0dad6400fb26930860271b3592cc3128f2cf154f9618956e597783625a5113b6838423bc75912a5fd4bef3ba42f7d8ca7f932de8b5e66f45d09758a4ff2fb22a14ef14bbfaf0e2506742b85c1419f747a21fad2c3107da9fb1afa9de28bde7bd1c3e89ee4ab217da7f89de6f742f4dec7743ffa47f981aea75887a724dbc8121e505617a8948f8418c0a076f5975308b1038a1e3528420b3e2ff93b82c04a83f24fa6aaa26355368efcd621bf63f2db46be775e53b94fb52af297a0fca5a03428bf240794524df6c805a61fd425865f26c64caffa6bf54ab732a25b95f82a76a08c8a953bd7b274ad020843c7cf7334e87d1cb120b5f4aa8504041a82f4e3d3f48ae4e3db009765324ad68ee3baef74c3e8e5bb18bdfc1bd5462dfd8e986bfa8d7a102a9ca9f45d4e51de93a0485fab41ff462e7655f6e5a845d7ee73ad4a7c46795d41848a40dc216e7e2e0dc6c8450e55a3c1f82f31ca28638c31461028c61863f497a86a30ae14ae58e39517296031d670893246f985747938e60541da5104ad2526df8d57e90ba7fcc239e917523abfd0a46ab025042a9700d686851caba4394da10f26b180830551c45edc772a42b9861e04b648951face45bf7890a55fc025dcaac90d1485f6030307352fa0e7b81890e833981cd6266b0239a409b49d47069319226b1a05b16ada1726576f7f89cfcf8bfacee638c3554b9af16a1f6e5f2d03a13d46b382bb82df7d9fc7cc63d4b191e37e99ca1b3d9ccc51c4bf6a14163c486bfd7323067b3399bf346d58ab139ee5d4efadd8821e86e27ba65556e5295391ff3f81e98a021f360752c168bd55ebe02c955e8bd565beeac49e9f6d6fe876f6aff04e1e02adbd476516fc9dad2d1d272b572b8dadba78bbbd5e2f296ab7734d8396ab77ac8f1d1b5e6c8d114bbbd5b2dd917840879e9fcbb1313942b9f98a0f2c315f282f203253dc9777bb2d2d2b2663cd4fd084557c59f04a5f2947b9f6b554aeb05545d1f0fc00a287044e662b7db6d865236b98dc3c8d4f866c65994dee82d8891ae413a9aa30d3dd024800409c20654567b51b1daf0f3d26354ac23a7e108e59b04d4c681e3acd2f3ccddeb4e4428a9d2a04455002fa0b1a4464195a657d185fc4c0d9e34c834ab52517e404bbe9a1ffb35f449bef4d59cbcc96f831ec9c7d06f9de4db2fe6e49bae2e79216006943678a3556537c15a324985be4451de0bd37f073a8bbebff7f68b684253f7853e17beefdf4fafc196683618c62f3688faedbe55bd8f280cf8632014fa4b0f6542851db811fa1ec5972889fa2868fcd06320f4fb1808c55104422ffa4cfdbd3f2667cd10cd0b28e839978d705fd234d8b1065b7e4bcc643299686cfc48771a8eb9fcc48c308e7d9a6e6f5a901354effd918d9a440774561d74c3363fd0f9b347e94bd4858623d49f7bc0ac651d1855ddab3fab6059bc3729117210aa3303c1a41ab20d9d41f55fce41e94b3e34b9b4cbb2f0fbb35816a360531e0ad57079f843f901adf95163f2251f7fa0353f624a7ff235256ff235a53ff9f827fe18932ff998932ff1f7c2e44b1e86c967e3474cc9977e3d05e3e4537d36a4133f8660137b957c9465f0a308b1d7be7ec4ef04d53f3c40e537c036804bb00af0e792138f6d4a5019002b06e28b9e3f0423103f7ef75da77a8272157df8282eabe27f5a0195b56b66e98d2250802a7ab6f137996217a50bd2f3f3f70dd28700c9f3bb2079fe505685fe8d136c2bbf8badfcd51efaf4037f43f44fd32bd2b7cbe27de0cb387f2f42f5287d87ea51f2efac928f6ddce69d155ffea0cd113a1ff432f4dcc7360d32ceb373df9c94721fe3b00ddf30c736cd628f363978649cc5369bf25ff724865023dbd05909122c159be25f9495f8940a28bd9af1c4c9dda896a5794e934945e55e81292a15bdea979f424f50c0e92050fc628352862726cfe48386b1ca0fbdb9208732a84f0c6bd4b6e19e3b8b8473f2292d138a6aa565c6136b2024e69c3070829260089cf91d776c240f510879ab52bea36057209b0689b45c96cee166c771c88ee4b4143cda888e2e329b47f0a043de70bc5b0e39aa4daf463cecce911c1d38b319ce461c6779453c233a3c297878477270c86e767066de0d6723fb5015e0cac9a7a85a2d4c823373f4b856bbfb85ba0912b93a358ad1fe96fa81aefd1c6dd47ed912f878410982da2f2510bd76ff1ab35bd2ddbf5d99ad064128130d39a0e14a48d3d2d22c128e0b4c8c19b421b722467ca400adb4ba39d9158a2099555992834a1b892375b8e4f12d0836285fa6f4989e9af547a61bd01004439a1a7fc61eb9b32ca0972f8f884e703855ca77217605da6950be04cd3cb5b32c364438902a7b746e3e40b51d9ea3234551e587f2889c1a8e681cce68b62af21935c22162473c75a757a32316942fbfc7878d3680ca7d249f55915f830f6800153473d62847a7e4efa0f550f361ab72a47303d25365ad86a2994f0b192a7f231a9ca022546955b8a4f24b0dd4e69e6ea005f5937bab59d4c7f52223c3a7653c9e6cd644911a6e6d36859f1a2e1123eb53e57beceeee26017be52f4966de211fde56a8cbbed59d9959f2102768e61218507e67b9ebf2fd02a855d877c178b652eeee7ea174b9ccfc5d5cbb1edd05f3964fe5cecece0e5658bb77777bd7963958af4eeccecea327bbc05e6affe7a6065bac8ca0fb735166a7dcf48386a6ea02a8d139dde4041a9a5a856fe566f56459fc5710a15b435ebfcbcecf05dbd9bacc981f9a18e0ef1932994ca650a8bbdd05db8fc647bbbbbbbb27df6c0fdfdc5477cd4ca639eb68d07974f7f6c6f11107473cf58b3ef453f4d1f0947fe87b4ff977dfcf8d39faf930ba0ffd7aaa6d8cbe53524ab9c33bd179c7e78793a34637e8298973526a32fd833ca30e9007ea5bdb9a487fd7fec29adadf44eda6d71f8c111f40117ba9745e21d18f8eed4707068717fa3c29dbf6d9746c3f3a3622fdc3a2e8fdf2ef18eba08819c85e215531c42b345596a5a18bf4384b04d336ae8ee6a3f040bd1f3dff388bf43efd678bb8cbe929ffc93edce3a9fecef3f8c7597b22828ab8f3189b3992fc1d99ec40bb6156c57f6b4788d86eb69b73cec1dc6c446c362237db2d66fbb9f52aec211efa109db6d96a355bcd660335f5318d405dc029321a74d088458d067dae66fdcd0a3dc74967bdb4fc5ca1f73ac57b84ded51ef23ed4de871e86f721cff3be137de7496f5f3b92b4c1d30a68d730451ee008b41f8492316359e6fb5f60571e58d0bf86284423fad48834687c686868d4f0a1518386064d911a5b84c6c616a1e1e3d3e990040f537662fe7accdf17c85b60ecf2b23cad1793c9647a6917d88bcb8b0bacbf5d7878bc976787129c68b3d96c211b0db1ba8f7fe76e53dd2405547e08a771402f23fad1cb784f3a750875ea2f39cdd30c7d353f62e887be86e4bb87117a9337f96ae8877e5f3e24dfbd0cfdd0e9c5e84b1ec6e84bbe9a1f31a1277d4df79dfa472ffad1c3207de9ab097da7fe4ea0efc5e8451fe33de94d5040e553131b2a3f9ca1ef3e641f12aa6b253ca0ac2eb0adabcdff04a6f4ad7b647c9cce1c91acf2ec4484ad3a086aebf490aeb5ad6b2d6b9b732a55dbbad643bac6c5aeb5ad777c48aff8bbb629ffb95deb5a09d75e918eb198c335a609c9a121d316c522a89ce3d150670dbd877c0bb927649f7effa571d6e8fda4f687a1124aed0f0350736ab15aa36fe85ab34d54cf5915ff8e080a1ac5741f23fa913c0141630d3947a693594f8144df0c4f71557e29f0c1f1635a8d99c63187db1a8d8534e8342ed63c76a4358b5946635aaf8008990d01e20364377e4c8b813ac4c4a92e2d8814dd8d1bb87a0d396801ead2788e509b6dc081967d3d55c38d4a29db69ce30b0179a6486c1b08bc3c4f8f4b8bbbbb36034dad2624b9f968b0bb602a39452988b5d2e306f17ac65013ad09056531025ea7e396ab8b3a8c388279034319d2069e247d0fa81081d422411c40f52e038ae9b5684175020842042f4048104422841841140685c7fd692339abedbf4b329fcdf1c2185eeee96b31f44756941d082b3bbbb33373337eb08d90891820e777bb77bbbbb77777785a0b9a0d5faa1882882e082266e20057fb0bd511a8cdf52ada87ca5d551d5f22f05a657fc31d8fd552a55123ea8a40e1e1dac9c7cbf2a7a7bfb8845af9c749f3cf32cf13f7327d0847023052472b013eb55820722ddcedcfbb55cddea4d71b598f7a3b75cdcaadcc2ccdd6af104a1455237884e13356cfd70abeeeedc7152f5cbda9714c91df2bb3fc9eff462f4dd57d38dbe93ac19bdf779bf2f7f1a343333481e0d9a991923148a8c76ef3dcb422c5b16d1fbb30fcb72c94a3fb32afe1e8a63a18f715ccea2c08d09d5c3bddd6891071777cc70ce39e7bcbd8706fd7907d75cbc36c3b7ee26461300803e19484f325a77932fa133944c9f0ca86df0e433416d83255f0050eb43bfd6f444f30b7f7b22003ab444175bb865556aa8614b334bcfd5356234ee7f59a7f925bfafd3fc4e2f48bef430e8977c35254fbff4d5d49454cbb295e34a95633962c195a0685a96854403c507ca95e4c404ed1ab6d4d824184f45ce7b128aa2485071f4a9568537dc2bd4580aad80224a3925b74c10a8063337ca1941dcce28e3d3e855ac7103e351761d6a41c74372afe86d1fe162f2a7c120e87c297fe4112ed6abf8827bc9174ba186a1464dd1c845d77ea9036551d7d9f48abf3003b2a7f2a3e48fa75cbe767403e334c8bff5847f9ef8f32815e87c297d4202901ff7464ee48ffcf1b813e99dc8b0cb413b1d61974347f5ef72fc38cbf3e4cfcf0b86b8a9a1d7f108e5e852525264cccc2877e3e68cb3e6e7f294ff9c9e02e32cd1fbda5c292929393af50175693d72c414ae614a4aaf28b00af0af61aa15581567c908f7c68d5a906afc66ba177d4de8e7d7742f7a29faee6be687be46f4ddcb17f3430f43d4f17c18a19f9f8d1f31f3450fa37bd1fc4c9ef2ef219e108357878279c910eae682b970f97accffd6dd73c1606c333aa0dc4d25839b99714b99e19a3123e3362363c6cc0c77ce91c1393332e072d6cc4d0855b6d7c3a22c890e0557f653b5548bd2f26da9561a6cd54a6b57d5da1a4a8c4c0cd5eeae6aa685ea5f485542c4aa730c9d1e253067fd9c347f1a53eaad8a77f7b2845810589ab79e07b32ca0ca3caa87ace3257d1249a8f4277fe3e5df6a9d4adf6902257ff231262fc3975e86cfc68fd21753f2279f8d9247f46081d95f858fb8d3f965ce8d73e7c690e39c5b7f62094b80c10da0312114052d488b58571881f6efeff3f42008ffe8ecbf82896e71b97477b75a72880d31a04d114488219c0ec02477f765462fc23c1004412fce18a717bd18bd49631231b6125fd0820d39463e3f9630041138b1d88a1e4ab117d47cc115554bc5324a299da5bb3b0341f973fca0e64ff8200733234fe0cc15466294eeae069bf3158e7294a31ce5382eb6cb05165db0789b7116a514891800b1439c02072b92d048ec90851fa2c5e5e2ad96d4814e1226f8cfa13b53392723b3777b777b93c0bdba2b8e70840f32c7bad8da5e9a0df6e0be87fc960699bfe52cee9b9de297dff69c3d9598824cc28de77edc8d2bbe40bd6166f4e28b078220e8c519e3f4a21713e3f4e83ee8ac18410985b8e38821ac2c41fabe226d77c9e8f2f66e978b373b3b3b278393931545281898883cd8590c34ca96f21b2699d991a04be341edaf3fba6005102c8a52b9ae680245a9fefbbfab5d30be7b7b7ba3fcaf1137d58310410d3f881e31c6821ef0ca4aab65a585b6b2b2049e952b4361552d2d57cb39c535e79cddea96b1dbdb536a7fcbd662734def6eef6e1a10d55b5330dbb0039dedc0ae524d5513343e3333333333d3b450bd85f883e00b09896389442231fb832a6701e01d87ee6bccfa120121b1e6e467f81a19def4a697e16b66f893af31bd0c2f4f7e868f91e14d273fc3c338f9190230c3034086377da7f8fb02c00c9fc90b3b702304abc99b7cc7b5a4d4a4a4c4c444e4795f52a2a865e1e15083569ef466bafd4d01abe21f512aab007f0fa542a5ac8abf0ca813948b50e091104c016fb7db8c0a142244c8cced769b959a0485cec77dbf1510743f3ed713ff8dca1f2a2e8b951be2b7cd0b4f58c1ce11729c607bf5cb38cb460c083728f464e1043a2c14559801e147144d38e10baf0310a1044cfc20d18348175efd3cea01fa9aae9002dd6fe96ad92b3098abbbc505737971cd66feed2c64832e4a998cbfcaa1b521dcc845376a21bba1a672a8294341c83c1cf75cbd61a9108be1a080f8354c957fb02ad1b98f7d5830fefce6deb90f87f93566651f5625be73737e2c3b414056ee656519ebd41b3c18212748167258810940dcb81162930228ec00082e94e0e5322346e8600b498ce106af48811ccca004b49b4c0a9c570c55ee452c3b1121fad0cb88bef54f8686a7e2b38e37e964198d31d2e9cde9d149673de2c57884a7c6e723bde2c9f29b1debbac0370901ee438f001cb26bab57cb32bf189f55a0c829ae3927bb38e98a1edd5d2ed13dba3ba703f7a568848d486ea765ad23931cc7011e031b5916ee8965b50f46506c4446431555eea7a96d306779274e7eacfedd87be43c996cd68b21da6394bf4dcd732d1271275229148f4d588befba8f75d37db71236ca4e2c051d658bbed5d8ea4b0a6caae339fd691edc88cecb876643a3299ce8e6c877380f42a74f7ef1e5ad6b296b5ac65251f507b4ef0edb8a5bb68214b70c3c9691e4105b11aee1154507b686863da4a66318f754fdfbac8caef9c65e9387b9822e7711cc86311e4b5ac45a1d08b3eec5bdfda6f7c87e7901e8dbb8b5ab605ba9e8279ca1f45167d34e81e731cd7e1b11fa8cc53fedfed06f2d8b2748ec7fa4336259ceaa1eb60e1ce9138a788e3f098db78ec6693939393e3361e731c1ef358afb8e831b7e969d0df63319bdb380e9bdfd83c16cbc9f1588ec73c36a35168204080d86a436ab521dd92810001c238a827bda2cb22ab7f8d994d35a7f99c9486be292b8f4c76a0dbe08a2a9d71a41c81986ab55a2d728c9102a9ce3f2cc5890a29a460297ac55ea3b150103a9ceaf2ff1fe51fa64e4f52fd632c0b57ff191901d0a1a28e6b44916a8c3146067de7916ef52571088002da35fc9f2316a467d4c6186344ad93808652472879a851e2ac4afcf842bee8cbd10bf922798aeae9550cbd27039a39c59fa7f8c9c4007df731bc4f865ddd6f078a31039299911112e374c136d8806270323336d0cc8c4c0c9b4c8c1999199b4c8c1919cccc32369b8de2d01e243e0820c18e5c7415fdac4b81931a326d131609bbc4748246517d8aea23166984daef0b67b8c8a81ab2762fbb6f8a6ce081b6b4386e398e736f71de52a95c2e31c618497ca6cefb542fa9cbe21d4e79a5e108a9faf36c59222f517d91507d9b50dd3b76733af8f4b8692e7a8c31c69728528d28ae3c63204c00f1e79d15ccc75dc5e77eae37472cb85768fa741c8a6b8fe2fb88050e1df7a7aee39d66710e35e7ea4bcb81109ef5a821f354f76838f8410db986031dfedc533d6423d53b11c71cc7712199180dfebe6c402537838134e891771af4998cd90e90086416b91d9e6540f28e7c199daba8b46d1815b5546a46000000400043150000280c08064302b17044a06b6a520f14800e7c9c40664a99caa3419643398e72c61863080000000000c0080c0dc0021c90f1e8fee07974591ee883225ee799f6579c7b8f4cdd3b26b935214ff30cebdbd31096a56a1aa7349eac75beaee90146392ff22aebaa3fb1d195164bf88860526aa4538d73c3e63873c6d43e7f12288a3237a1deb9106b0751a21bcbd23d8d87a81b3f2094232001ebc36d346e4a4e9c77334c3748a862a4e81d71c3e7981acfe211d620acf10ea9a9eccb93009a9e9b8b494500a128c366fac18cf497e64e1c983eae22553197621edcb45437d14af7c09b8ea869c0f63a0c0fb35f98b9f5d4f994fbd7f1cf54abd8b108e02f6da714b7be8fb7a74c298c3089c295ad24516212ae91811311d2962245d3803855d56de92ca55a9045ca0990953229b98c0b90823959e4e8512709eb0d762edea7fccb36870d4bb8f8bb82227a8900eb859ab02fbac8c39ac52cea57a805e2c1d6f85ac1d579705649a229141d600b8714e056114968937e1b5335855f43373d883e01390c0cad7b67cf49b9eee7e6cf43017253fce404b22fcc4e4714139424a29097d6db32bcd6739b05a14592b58cc53f56f4e8e259c3a8218b6984b39e813e441972c5b2403ba12c58c7001c9d78b55a9f4090bfcfed6fbc11e023b839fd9622fc8b5985ad396b846e71890acff747859f71ebf20f10592bcf81863c04322f76add098960a5d987e4a165afa31727cd0d23ba617f55699cf01af4bd8f72e15de2aed42b8bb3a464295a5ab0a9659e184f2ba07c8a8820093b0e64a55c25da4d59c84b02a6feaf245c5a67c812735ff179ea41c31a1bc8b3dd3f5d2fdff9a8f9c9239b37f3c0bb32401f04071baf142a03cabf31c97c625d79734c0f682a6294bf45c18b2d40dc478b876d452f55b8e1197929dda8323e66c6f6ecd2181a9adb09b5fb4999a7664c4a1be44300ba51d39a12bc0f862bc7e9288edf3f4cf39f5faa56ef6bd1b47de47e707ea0d3a04aad543444862c18beea801fd19befeb571e64ae0a3695e0ebb8f33f5975493fa35e49ed5bb1726f5890c96e5a25b4aee7115d10a832a36a331e1f961a10bfd7260986cf2d65fa96d21478bbb8c20c74979e300950c3d880c4afb9b214b9e1994bbd09934d01ffcb6c86adc53637058d346bfb1322528647d91ebc32088bbc57e53d28a650af6403b19f5cbd8dc93b637da5317dbecbf3b6d0e657f4063f794726826185a13050c95ed3945be82303b39a304380aef8176751172b05197b633ecf06e49490e82185a2e2101e4a3a97911185366c67ea1388147afbe82db0792a7a9d59416f772532a4dff91ee3715001f3343aba8ea618fda22024fd81002941c5f2fcfd350a64e3a703c8ebe5d435414b8ec405826a13baff13b27e630ee1cca1ec243e59a7f0e617370851348b46f88e4c85e0c82812091443a61727794ec719b1dd01485dbcc39a6d63bc33b17af7a3482bd745bcfa890e4328d6140d01d49398c5cbe41e74ec261789f7a913cd1a0f539e0f2668971006e8d06f8a63a132c965e1e12cf5d0b287faec8ff7a018ee6a2f8185cc6c653a19bc4454bd09ecce347c722aaf54b44feb4afabb8eba769d2fb2bcdff49e65d5400929a7b5b9ca2216edb7c04fd1b6218a948378bd47dcbf8ac447a986720a7916bf120438a57a360db1978318b75eb71dbd94439c8246dad54ad19be49e84bacc038617a21a175a86392e42332e1bd98f7da43ce607ac58801e29fcbddefa926041179abb5343271f5dd9bb590db070e870aef63832b971ddcbef8b82ede2dc74c1afc502e78cb043848560d70abd35af4669e00a4d207c0d69555a714be21c0416e63d2e00876c760611846b0b067085da947e7bf4b9cb3e700134611a8c7f95d5cfe8c019ce36f265a23fa4b18162b9ba37c33ecf9215b91432334c1c5879446144a7b6a6860d2738784423e7eec5b340ba7802ef7d52b57f73f6951a3015dc442dee060770840244805a2764bceafd0c3bb8b78a95882d8287b90fcba423192af9612f52b8731e9ebb2653e7829a067bc0bfa6235194936f36c85d565f81a6a4d15889af37e70c1995b74372c57d881ec7188283068f763cebfbb7744cb5eb87c8c22bc6056e310d4d4b39f5f368a42a0dfb4ff983a793cd6b56147c9dada78a235f7e0e773611ae56ed3dda247a8f1c3932e9497fd3cb4b68e41e1a3b070025a214479da89da37e2f9f762762cbcd8904d6a4dc6c994e7d2a362b1bb2270a1d29f5190d9ab67b06d51f45962bb2da5f1e411b70caa7cc727cbd1c74ff7dc079987d20e07552c75c0878d63a0c32336c313df77f70a48c4b7ad30920a196d4731d368ac6732121be1d58860b650ca905a886245f905e71fbe03e58312f31cb48b235be8fa570b4ef1863e094738419b10e660ec7c0ac6dd508f5a6317a475a5a7a082ede93938377b4f6a4f21e559b01c074243dd802ce9eac4c514438d627b78836068f2c7c61f4bfd82e6c89e19636ba7653463ce79e6f9c645947a71ccde1424666a314ef1fd28157fe9a437c58399e514e7643cf5b2cd8b0647d6e5fdb241756d3c75c2de27eef9300d8385eac217b8e0f1a10b76847dc0560af83f53381962a3fa4996a3294c30d43ddb887da330137b940dcb14bcb1d2145e73e771f101620b9d1eaf2982a918727c1444c555bc7c850e0118bcd0496d1d011df771c2bc65592b5277abaef71cb768d1aee8486a538bcc7146e8c1cce9364d13d58c860514eb7e041e9dd1496b3929daedefea8bb950231387d5eda9cf8d38c916751d7d2ff7d608051728ffc0df8b8801ed507edb02304d5b746d032f69fbfb2cf73b18ea8c6f8b21aecd7a341b76c423d88ab25b596d2a47d4749f155b834d07b7533c039608314c79b9e2edabf1f21432945190c1e0d381123485baf7614a5e97363b9da925a408f0109591c7ece4e721c3331b08891c03caa6bfe5aac7b88b684ce7dc010ba56f647a19f6f3acf8ffbc164b9208a0f0550371ad286a08bc5e93c4ce3fc910927a6820851a09993e480596297132493058c56ebd8f0066298e2d545a5cbe9bdd86371ec1f08337f79047f8c46010acc50769268a7c93d2ce158360465a1c026115aed6a939b0caee1c6f33d6cecfbd0d545a1aa23ba10d2329a4f8d7eb32875f2f19458af25eadc548b3cd170d62035ee17ee30bec2b31dec48a8604de49cccd56fc29cd99e20dcc6620361fc08008c957aaeb27bf9a7f957ca8557bf936329dbae16c52b661c0f1321cdb2b3bc4c4d553d26b7e1390908a9be35e7d248de215eb44ff1a1993c1f7af1b638ff320d4877895e8419bd0e516f383297c4a17147d3a641ce89798738c7b1a75ce5f819cb73ae4ce87022a58d2a6e845843c03e4b49d1512cd5faf8db14ea7137c1521989694fee10e76f070fa9a55186093226045e70ec84064e4eaabc1785272c2a220b34bff4d4babef354b4bf23f8ce4ff87f55d9f53e14621f1d5319309a460257d89d680a5625fa9d4de003b74d6353b05b5d3dd869a8a9dce1f129831c98275d32f170a89d7e43d5fb815b492b0984fd92877b231c5d342ecb37a3df31714d63f187e2ba7f0519729c038ede486528e08c4876d0928cc09e9e60a0b08ae23e3a316575aa56e35f6b2e0ba0e242305f222a834416a8bae459f3cb30607cd6da56d3b8d86a4455f14454d7a5306380892756d142feb31bd934ce097a39485bfc1d6ff64d6c9951d9b16e7b9ce38ded2b9c94de697e69c119940410256b00d1caebaab90eb47fa979035e3c151e15f4b00c91e91eb979f8c06563e1ffcd6a5f4b84af9105f092648ffc6fd0e5acba03408826af7ae784659e1b7c288fac7f2fcdeb809882c5b57e9e58a58792bb421d9db27e95d35631a9f0df826922489f27060df65f74ac80dd62f78dd0f7def8943c12a526af1d23e09e6d44745ae618b4467c749c6673c85dd04ce543455e0c193462248350fa9b669ae16314a3b4f21e0988beb86dc787340d9e4fd0c51dc2c0c5522562e735fbd033d5d917f7bfd371804425a8cf789dea45002fd2c21bb6160a4d0cca9f36d5780dbf29b97555f398c6c730a59de9e0aae89c7e805fd939d0a58ce3935c7dc9c9ed763d570d3dfe42fb679c603011be00c5795343e350b554b061a2034c86cb86edf7ba76d400e460037f2f1653325efb4b2d01b8212dade81b1a2acd3241455efd6691375a91f2efafc527ffe7eab659d861e22f3e614bee6f4338550552fadbfd2939f9610c4430667f721bdfb3a91074b13d1bcbde9a53172eb8e24cf155e643ba3dc04491e42cbca31ddaee51e2454f7313e965b3b60d9e40c574651989765f2e480240ccf9d18e28975a75a0b456e54c13ab16d78f67ae97e519551c5d30d23cb13e92031e37a5530289b610ec811ae9748dea94581cf50022d38d501388915bed35a588dad6e424c4346e19c15d655ccc0e5104d9424815aa0191d75ac1c42c3ab415e884991fa750d61fe836f94dd92d4f83294f41e5874faae3a46fa4df4be90930cf9d10c1685bb3a20cefaf575b18a15e20fa73388d1c22317a99fe3db20f7e508160b369fa0f1f70581823ef13a5f84f15991279d6a591033bb586fa06e8365b94ab9d08942d6e454cefe5d2470d3a07a6f2d19a0aa8e495a8d29ebb4d20b0728af596721011c7cb2d94697bd8e2f09de010ab6021b5802a182a7e8b647e7615c0664f5795ada322b56bac7af01717a47a74b62acf5a0dfb1945e3d68a08445fd04b8c0caac26fc32e518d93d65a6a18315120bccc62b965931ab11cdbeca02135a329c08425e4027411ae65960cee98554a53fe5c1541e8dccd47eeab5f902d54960313bf0c99140e76a0ee668616a1534ff9df555246c10762ec1ccf828dc202ddbae41b248e97a028718bd4ed0e72b3cf9356bb4f7899d0916e3470affd5e924d6b346e4fe88fb512631f4545654263d91cce8677d26b2f54f923a5db8f2093a521da1a108fa569f79e173601128c2646576d835395cee8620cda02a4e2019d38f202e7da348350e5481fd3c3ea3d72bfa18fd37851e07d72980e64de8678a715b38ebe9b747bd236e1019ce3e860b3a2a18375137ff17f921067c4b45225606261ba0e6ce37c6f144e3b306886ce5506f0d2074b029aa1601d08e3f66efb240a56e02ac8484f3d7763ee9047a749ab3ceb6c9033226e392bd01574a0c51cb92cfdedb0f205b46df13f5086c29d8eb623482373f497408f9f35522dd72997e2ecf930cbfab184d49adf211fe5c6496e23a8b0bf12974d231e2015a52d9bf038878a8c22bdac75e71d2c302de5f04e141a6b095dc16264261145ea27a07f6975dc197d17e48d2a6386867679600257a83ce1e50c63d572cb8ef8295e21a0046c45991c9ec13e1bde69a89ad0e584778d7c9825fcf25f1f7bf0d1425607903ab9585bb806bec74e41b790057579ef6067177ce910532364978884f9aecd5aba3035fad31ddc6de8a0304c2f814428128a880a4cf8362a3351b4c48935df994f99cdfc0b476203398c09b7e6c765c3b5852b6bd4660f3ae7f460049be6c8835b962d09972813efdef2c9df450851f57889b5f94d1380dab47a2092d7b3609c386451900f28a376fea555e622292d17d1e4f7ee6c909a7a8c5b2452b165fb634602d0d26fea17516b2c19868d3e12f6f6a98e980e49147d67912bc749858d5745440b899794d178103052c92715f65c3e70459523d118df43f1d3560f511f81e25747bbc5ebca841a479f9826de86285ddf1707e058c8ecc63bed6ba30bf9182f9998399abb5c4ea45b3d2ab3417092df78dc93ff9a574a62fc59056cda0164616870ed6afd2bf2b384e390a7e0f222bef784fa2082e1b4751ac9ba5fb1291db08f2c4052b029ee4fe77d300817571cd067e968c81621f8dbabf4c2a0c7a6a99a71cc74ae64673041891b7ca384cb0aed28c4bb34e69e0c109fb34f2509b371569282b1c1a3812e009c2f33ae5c7aabce350381f15adb4e91e18eb786d1f7fe86f2c5bf059c1fc96930f99b8822fd493c37dfff636474f6ee04e1f63c858614a4332dbfb8522761d0106799ba457570983611bb213cfe71f520428f443cbc750838ae8c5af6ccd41f15ef07fda4591feb7a2bb772a335b627fc5ea94cd674cae8afc06d2c420fefaf9ea5b5dc8902f9370c4f62d90442413473e07e792a0ff6639bb292092a5db3dfb4de2f1050bab78865c9899b3ebc38b8f526aa7460d55765287b72cc8139b3c67ee595a004c769f073575c52eb61236b6c9a3df5149348ca97425011c2db014c7fa91be3e588f801a8900edd4c9f1f8b0c903774f86ff6d29c42d7d37c2edad7bf8ce0827b4fc9c154ce43439ddf95f235b220ef500f345a234c158602732aabc611d396b4dce134b731ec90bdb33bbd17de45724998ae91872ca7311be87b9f33839639ca73626f44c86e4c5bd048b03935ea58a6d6d78a83bf5195d888d77e79e1ef9dbf2ccf89d650135defc71799c324aaa9d84d34c87811478ff157ec001e5070c174ca1aeb58b526bc116df0a27772d810348e8f18f080adce23e6c5c624ad4c37a3b8259f112481ae8bdb92a12c344753ff06cc7087d0e859488b19f060df93260e96ef0103ab392510171e77187d1ab450064e262d9482d5619fd003006a5d874545425c2720206a970b92715fc33a13ad33257e5303eb85a89c62adb16da62ce15ef5a7bbbf122eb76be6a81e0c568df064062310a14db2d2872ccf8380d53fa32d3ca11baf15678d9aa2c4d30717a12dfe811437e315d6490b58219a2dd1e9e07367b46757d9992d7d3ae8c00586a8a188e98a366c242bd3673e2fdb75bd7b636622b8e608b77d09f1407782446696b1529a8b5966adee225d26611ce5fd4ee4527adbcc58f79930ce17ac5f68bf5e6d229f8594177f85954eb520dc9a1c2ab23e1eac89af85a1bdd836f1367e78ea065d22d2c246f5b2abc90f00f326c38a642048928840e6651e1ac2d7cfd3bf1a12a616de8ccd24ab632878df21811454f964c0abb3b321b37326c32f3378ee05ce23af6a19a4ef76324c4b26d6c8c59e9e63e6886896a45dbb8346094fb19a21012e7ec7f115ce8f522b32529cc52517e0ef5f1eda8a9f0f8b83aaed64d4c100484756e0a9785ee5cba47d23574ce6dbcfff9f4e78a7b0d8a4cf45222a3c9b4236a057db11930650634f3f7516a2a1b7f7c81985f6c7357032a38dbc19e3778d1c0e42a84831e48e763e7146e1ef7d188fd29fa01043d52003fb14d5e0acee4c1881cc495c5dfdd85bda79d4f993c4e5a662cb352990adb151761c26e76ed1c7117260b4d77dd607d95ad1f0112d6688d07f2417f015afafd6666e3382b4524c464f0dcc3b35d368d0b86f1027b139b7ae6e2182d84d80d15d7e12cec3bbd8ff3569791efb1ee31c366e5886232ae3e3b907e131cfb60d0b6e23470f67140ca406431194c8526cbcf3ca1b8c14d790f26636bc8c16117fa40b17d732c1d0ccc70e5c29214ede0995e43e61eb9579ab034f135c09a62c3219b6d8db0b1893cb7c8428a117e51f839c6f3a1e3b02de240ca387a556754023c6fad64e920acc285989ff92caa73592fce5ff296d9664a29743680de770d46c9d0d6237b7549d329a938986c9ed5c71d1f9126cea5a3908802c69b676863b9fcc64a72f30882adaf35001e7e379b5d3d7293a0df64529eb16a4ec1fdfbb9d5cf07ff0dfb1943903001d4b4232f623d500f7a9bb7e4a4b73d856c3ceb9a17a2acdf78b564062d6b5b8d9531d1bc68c220f3e56c46a2168fb70be437daebaca75109f277f1de69e39fc3ba0bcbe2d3bc9cbeafa88003db8d870144e049ec05b40679382a1fc206d6d6686806464ccb917d219f131bf63008237ba481692c90129a055e925c62bb932e9cb60b7c5a6542e1e62de9d2c4190e9da70bb112edbb737d6a644be00f1ec90476c2b2a239d0dd66dac72dd29fd607154b06039b59f5598cc4b9f7f175d0f34d6af9a58a0dc00e542aece3870c0b72c05e7e144df84839e7f0077a566f6d05b48f39eb63615ff245114d3f980a2590f3dc0810754a92412dfc8d99a2ef59eadc85c6e59d8ad3b3a498dc2c974392f072a4489d5a03c13f4e1f03f17887aec9200974cafe05d91a1cb3f2aa4cc8af4e22b13f9b233eafdb82c31c33fa34971e8ec0b94355197db40ed217bd1c77419c1bdb772e5a104764f4f63ef011c484ac0792f0fb9399dc48efc9f84ec242c290b8a188bbd66aa0c9407967bdf0660e360dcae56fc88a4d835dc21b5f8c548c80a222b66d518a10e9694368e01f8ae65b7d611afb2d4dd0c1b1af0cb9624031146b7d4694564ccee23104aea52c03727a2821ef78ac153e9914330aaa49887b11be2b74c3c80525dfb3292ba8152ca77e933971698b0ab42f118500eb5c9484cd45722c4cb52d32839fab815a9e4c2465b002c8ee76faaa221a838f5e07c88768f80bfa4f00bae766053c43480682ee1835414721ab6c7bb113e3f58f3d7d779ffceac9c52860f0399bee76aa5f373b52487c97caa98153418fdbdfbf29d80db47789c5caf85a1404d91e8b17ac6fdfa754b3594ab693a9d9ffdb735d59aa2a7907cf2c4eced31cc7d35ba27627e58d4a9e69a01dd5c8ad292dd4d6f84aef7922aad97b4d1771d80fe710b806fb2f957fb2b33f7cf02f109550e6729d5048f318991cae3aa1c5e045fd48a1197b780c87d5a0d94e1fcb15604a74fc31011e1c643da09ed356708f44fbbe4d138c31e03e0796c3b13c77e7b3cbb9aac86eff3eab58b2f203ad6ebb405432abe84a6c075a36018ba0b75d58a3ceb41ffbcc99c191dd575525f2195a6f38c95224560898101d7fbea8cd0ebf844d03d19c49139893c6df14419c3c10a4983511b5c12dee3f5abd0e9527fc8d4f45bb526016624c160ac63861b7123b5965d08b2c4143634307d639eb0d23b093875f1284e0f51e8c8636681492d3e11b6598038cfc2da167e4e8afd61b379127772b71b7714b97a78dcd34fe7d6a107ec05efd3f2d8ee531354780ee2a72afbcf1cb0c93872ccc6b160751e43c2ed4c0e110dd2059352b3ee04b7a57f16a4b7ea86676f5211aeddbb387b018d80c60a17d26d9ed067efe108033e7d0fbc3e0a13ee3deaed2fa9be486043a37431d7e2073e2d8e256ee5ea9bcbcf495a5aecb371faa94f40b96beb7f648fc9245fa7dc3aa7d4100960a25b9b040e2c4224c89e08f5883c41130f3a8de52447e1846470db03b1508bb43ce1862e4ef835f17e3aa87aad222aa01a624cc271af1f8e1171d7414ced8afe5be04a40967b6539ebcc2bd9c10b37fd109a57cc44823dd99529a4affdb545e73c6d2f3f423f123e8533d894e52c27c4738f6c0c845cf4c19ecc15b6f9fabb851e7ed5b83f5ee54b06406d926557c721970dfb0146768df6a083fd7f57e152f887eb3941ec873c14cd6536c09209de6a1241d3783c7af9215f05e811f751c50651b7baef491cf6a73d6a5cd870778f945288d467ff8fd2af42ee0da397fcab7a9ae01909e846cb9545b7a89386b90063878a66ad786a5a9838903e03c359fd7139b4fde5393935a11193a379fa08cbd4b06d44aa3348e769e3456758df8ff24a6f07914a734787f3ab8c7abdc4bdf24dddc58925ed72a322573cac38468718faf90e435ef1107b2eb44355dc31bbbb67a75439ba963202e31c88b76a95d47714210b1e0c9281c9a4afb104e584cae58fa9a5417c83fa23ce084311ff2a540938a10d31711e1289182c20e58448dc97f87499c4184b8fd72e16cf95628bab0998cb938c234f1e55c506cb5e966ee897302dc7b7229629a3ead39c540f4fc103d7b45a2b3f5d4634ddc730277ca6404eeb5da7925769d6e3d36c2e8b972257ae5a48d5b32ad027ee7656f11e8632b9d1d1f378faf5d3aa5e236bcb96c25b0ea400f9d12046a95b67b50a5e0e63dc85d2c234e6fdb147a1e3eaf8bfa5427fdcbfdad59303e4b3560768a3639d91a7f7df901ab13a2c9dfeacfbb34ad953bd144a851a9b45b38e54d1c308ca75bc5afd3a31579c4d945598b8635725397d7d92f2f208a566f1d4ba092aa5eff9635041a31184f1084c010498126fa6a66943ce64f24e603e95663684e3b4df87d0772793a6300d10f7672a0d1f0c386243bc495722d9005401c89fda6711f824437c786b345d8149818ee3fe63df89f50dcd943cb750058dd8ac75378f49b6ef76f6f9acfa36263a0bb529f1f2b76d029216e219b0d8328b14acaad2da8daf8664a8eb63796d6191173fb5f06f51670c6a4614b91aca0cc0fce69078cea471da2c4cb0f04140888426834202d56c4c51e57d94170408629e6db1876027804c2f9de2b6114b1970fe84a1a7459624230b077ccc063f188f6d798d7afb9b9f5ca0e37ba54b8d9bdca9e5ca93d21493301daccdde07d1327073f6acab331593dd29353d8301a076a7a7f6515044590455a50686ac28a9b916faa266dffb2f7d326a0c09621cb22cefa13f9b91e051167b847b1d07d6e7753537a02814b995f3dc69c88b3fb1bb0395d7a6930cebdce54af6f310cbe3b4933579354ff0883c32dbcf9e9c174ec9aa907214e8ff7a61f8549886f32f2edc1546ade10c0317a5019e34d98e3ff247c917f67c1d148ccdfb6246370fab64dc677581485a2f9d2bbc53b73b484e865a1c8914eba1a5ae58a0167cf840e809d12bddc610685b453edd83236b4fab33ebc046ae8ca9475b2162a393ab2b15fa271462f2151cd1a65d8c462ed5114832cd10499301b86e138f7e6ff59f7d786224de16537b3ea87c50cbd6a0f74c1890a503bf82fae9d28d905e8a3580070ddc548e973baee6e244b433e40616b38ed93cb2bfefe65e2e354346c502b47640769022633d38ac59e49b7088df7d62d715ad538c6f6ebd0a32cf2903d04a3ca01fca37d77c9325136f0aca385df6e4f043a3ce0b9c5f1158b38b95ba9556e2482f28d76d3b20e908f948eb915b605eb57ccb6a65f311b5144ff400c767c477c761ca876c6398ac51489b177f0e5f5140f732607e55bf832e93dc7474867964dc788d85d3506342ea9a9cc0abf8476042e17754450ed80af37969a7c390b4bc5cb84a629eba4ce661f7d3b07590f34a85d146d363e717e80cf3740bbbf88483eea031a81da28d2384b152a55b3230b8b44b60bb90cf392157cb3552a999fd1c3ce04252639e831ce98a64cece0233f75c7e80e8f5b09f786c2219c3d997cf34713223f808ba1b84fce73f2b37eb396497872bce81c7ce47dc353700cb4f0b8babc3d758485b9bb96aeb0f094c262392550e52d269c18a9c54355864a9c7f39275f9cd37bbe04f3f7c511ac7b08e81b24ac236404468888e799e3d3efaa301798380a21e60a7eb1822082ba0a3d522781cdc39fb6ab0f3c8167b48a4f146bd187d5cf79bc025d09cab4f5732eafbb9bc235664829b6aed467a7608b1345bd600b9553cc2bfc5f6dc057a56fb0bf60540f3f4e5157fc5b269863399de0ab0073c914d24acbdec43e49bbcf829933593c07e7c475d486965defb71198973a46875a414e643de4b47d76d0922748c5c66d71e7a5c390205aef2a9346e5cf3a32db01a3816a462fe35896cc86ba92869a3a4529461410dc0efa8306e9c421029d63d703a565f1fd42a02dc26959961715d2adac1bad478a10445639d92e8e67e778868ea9b931076827bb3f6f984926b439ba695b6180a59d6584d45dc025692f24c0915af498ef213e6295bad86095a72249c2ef3ac8afa8a1c5b4da62f8f3abc1013da360b06e9085578d758251a4032d461ea717b8b27fadf3410fbf5a8b7834c656805cd3d1cab0b0f72fb37176381927cbfb006571323b5afff363437d62fb8f56477ab7942fe5b4284a048c7d8b42faed9f834c20cbc2dfc794dc10026f7e6b5a551578a5ba310eadfc8dce52eca544e7e76c4b2a4f00d21e42ef516efb0d14a3c6ace7ecc52257444390550daeff823058291a9116b3494c2a03065fb32cac431946e8d1e2ff86258c44a5dae9354eb3503ab8b1882d19c50432b6c7faf466850bc23daef631d9e249afec64767a67c556a6918f17271fe01432444d560a65b07446c1af7cae3c4ee23707ec07fa238bb8e4ec46b69c2e8eb1b205077b2c449e4bd5a340ae6a2ba8ba9e44e72f1c64d38b846ef074cea723326b8ed75ff0a09c9882b725cd3e6e0a8ee265e2fe77de86f9ce8e3268869a00b50e021d2701639a24d64d55bbf45d40088e1a5a8138264a4b7cd0abb773c75cf09e26ccd79682535edcb345669391c58a8ccf34bc37054cfd6a27ca15725023efb3c369bd93262dff7e3438b7158a365c68da10f45bde9c929ae4a60a8c25abc4231f5820858406eaa3f75da66fc58bd2802062bc828ac2b6d8e49cc02c65012c7c449e3d505b14136f624bc83e761f0637b7f429dc97622a48cb91b6ae91b6a485179b9633333ed505e4709ef13f9978991cc6b11318eca9813b750a9c2eade76fdb3d9bacca1b0bbf38ce40ed9ed6a32026271d36afbae4d31fd42856e2437d848ffb85e2f2958b311d6f08cfa56c368cd0d37317cadc65a4601053468f80f00c5e574f66394770cc542c6c38ba53e9945ac4f7ff47f0be05dbf2bdfe1e3ba91ed002b2aa6cdeb86d7eab910fd32d891cb3389d2b4ef7a30ba956ab3fc6fc9bf874a1a916d68987c206a31ee626487c8363be5e08f7b53654eae4421a0695dbd8a5740bb028952f6bc37aef7212154f52ef866e2f13a6057a8f8870c3609a00a4c1fe261be988d6e3d3dc8e1d14051c111af9c1f8c4783e87c85d0f45e146a3a81a0d50145ea87cd0be49748b82c5731097ef17eeddd0e900572cee64a2b1e5976583a2ead3814336326052c460f5f1e617e5e3926cded1548b6d76376f226102e4f1befcf860269d3e3217cb7a9cd5d2176bd84b98e0deadefdcab62c58448ae183784b703bb67012691e526cd1f28d2e8b7ac2eec8816872185a735031b5433fe7d93bae4a00132c561f687c60e311bbfac831e9157281b411bb567e78a38db08294b2bcd6b9645a779e06db5fd361d06978f0c8623e56c5de3a9ffab652774134c0ba6e0675478fbf78e482efddee7c9dc5ba02ac4efb0c49735cc0781af6b9343173cf07745dc591a213fd10b3eaf179be30d023b89c1638b5880064b510922e7300dbb947fd6e90802dfd65194dcbef30bd62a90a9e3c95f86ba13509d5858f8fa12145cca8c6c8429c20b502a29690936c34b1b522071a1bde25c6ce7f563799efeb0623705676d31e54989e6396c0f63984f04b548ddbdb6e62c7df17b8c01fab4b51fc330b756bd8bf9e0e2780090df621f0034a5986ba8ae48ff53069c766febc5a2d80c04567e42b2a37f98439286b02e43e4c43b6823d0997fb0f941376af0157e3e659c1bc364bdaa26f38af055cfde83d20aa3653963fe116108e028f983217198f670f832111196c25884cc5cb153de0d4307e354a4aa0c5cd5460e7075e1971193a63e03e92f8e718905fd93a2c557ba7506eaa91b0d50cb0379708c85a944dc910ef3099a600c08f5ec04a3804bf3092cc0a6176a96612d83de621fbf06b8175861fe81121e41b8bd11c81312f3a720ccd17926ee463d8328d8a9bd131cf22c441090cac99aa101770a93403c4a7aee939fce5cbf9580ba3ca11517259367b1b75a675b8b284c15404b5118473551a89de3bd775b34d8748073878bbc70f47fb07d45f5dedb9833dcaa323b34a8b90436c0392b1e83821f6f13265b86b435e495376683be1f1e357772e4c4935471d5fb5539b39ea38129f095307d679ca6d593105c4ffca1cc532381172ad40756202cffc86405079c452b46c75099b01a4b6711e75edfbd2f3004de3b73668e801057b07c58e24ac347c30db4e59b9d2fffcb84c85a2bb6020f9894c9ec6fa450ce401c0247e1de475f8e91208a86f878239ee948c253cb586aaf9a70b15ab8bc503b40c07c887d9cbf4a8b4373f50e7a85433baf4cba34e439e8d57aba48a3062d1a9fab4ad6d583b10049a0efe2614a858c34b23f233b61b21a62f09d91019cde5a0f9b38fda0689afc7144c54c7e2ec683e27e6008fb4631e202f9e3a02e513568c15500e292014a41c3c48107ad6396e80ac9207323142710cece4a57efb103c2431712c352ea5bf26954769eb61d109a0b48a36d7b1179c57845442923d0f53886845545b03692437cb5b1046b5e0c2b782405ca801ab9ac9d3c879ba5c4a28ce072bc7b29ac22f5e470f6f186fc3af4b1144391fd15b29b9564e4026d01dbd66eec5d241d0b512b4b6feadc25a938f1f37d2b0278f30bc2a4b6ba60d21d6e794c553f736e2a292378490e2e344e38dc7849dfedc1fc41a1658053c795d922e22e8b4c267cbb4f68d4da7baf878c3a404162a6188feb1a4c38bbbf3be855e41e28f9f4745e94c9fc6f8f4f3305d745cf9c06bf8eb6a8c796e8fe98c49f6cd3fd7154cd0a83af3a072b0d9731e78f630761cdd921446145a2b2b0952c90930eb80e2a538f7b9519fed2327c829731067878fd11f6a507f0f1310094a1d13cbfc33007b50c4f357e0daf584227fa366c0024d5be72598f1bcec8fa89f4eacede7e6c6d1a8c157065088ca27c72f197be5e5bec5289989c7465088fa634e2a42b814c52cbf019f7a7998ca31347d42e3e308d06fe8cc5cb958b2def8092ad13febc73b4d62e28d338d4320805be494f927b18037c0c488d587cbb259e852e9d880ce01fa3b0f4797c50be65a07378c5f4ab97897b97a5bf47e71b2d467a68c456dbd4c5caee3c2776d7da4968a1e26db8f7bbd4bd1b5622110fc4e32d9d974a12f91a52c373278e5b1dc68a6dfad218a17c23ad922cfb7f157df28924797e439726b8695482195ce211057a9dd966a48b5fb88b785f400233799f48e1097c76ad773f1e6e118708f3663528a0b7304e11bce009021a806d2fd3f11afa2da6dd167c834b55da3136fc83ccb858eb93ec46a5c2a5183ee0770bc02a4a316d18a05d9c1c2a9e00ff6d00937f54f2a6346255eaae9123a8ab33442558606b17d54656a125da03dd539b8e042301d5150ef32b4df0e99d70b781d697469cc3c878500d8192fb07125750928432d121b02ed2409247553492a74ac7c906ebcb971ee4870fcdfe84497e8fe34c16942c7de52f9e890d8c5e8004aaff43a0496b87b29e95f893aea82542f055fa015238c48bdaeba5453c0c18d9da4b34570a17a0dd9f2e9075f90dbf551a8d98b764e86699542e94b4de2580c7edd29d801164138eeea66f5c82c804b4996b0402892b1701c1abd23d207a345fe11c2a431055b4c2f9c3e69dc64745fc00940ec40e68db90d56e2f1447124a907b5cf5e99ea2cd6aa079904962201a6275961601c5fbd22712d265097fd072f216acd4456947cb15e2a2383477a68391e79f3368208ecb9bf1f1dc2d36dc6316a190dc7e4437861f9449f737de6d7c5caa6a68e5e813179564e1d97beaa94cada7de34d595378e5747940c52120e323e5149c1a5310dcb9e19023b5c2e51b9c35667705bdeaf688029d4a9843797d09bd97277bd7e483fca6d2b0002f71f2c114e6c1b7ab8b544bf8f1479e013bfd762f942013e933955a4ff7a6689bd2b07aef097a77724a3a537a1c23e7ddea4942ad47f9b4474682a2a83685ea095b71a552df8b6b899021aa7921e89484687809f434441d6d8ddb3b159f55707e6259dd621fd38b7a3b0321be76970a281e293e39ee8746c1e56e0c393fb6cba15de64c76e97fa77f68ee47a321456deafb4ae25ea6c1344825d6e5aa2b8553707ca32729c02afa0b432263ee72ea3d8c86c6b596cff4d081db8b85d238262d298404a440bcbf296814c072ded228a1dc762853152c1b7bb83c01a83407c2ce122dfa9f6d2916f0f2bcbfdf1ca5eecb9e8565d10ca096f0fd1d2595d8af1d9befef31ccd0567dc1f8a20141fde7b84124a42d6e6bf83e02b53f9556ec3c086231733ee9edc462e0b412dd42b73658ed290721b4910b9a0764b90fc7b4c584e0a3d3e50319d531e0702b1c5ea7ab0bbb6bad806328c9394048b9468461c24c78f6d562453aef6e121a47e0a416b637d966f57fc42ce0c1be6e9aff70ed7661c0b2b8ac5fe846765b81f9289e58c2b6170b09d6675f19d01db87ff1eb0be4b5c6bde49fe118b6dc13f6e766e72466cf9ce94eef3315b46bc6bfcdf4c59bfbe3da455b901ee00879f13428ba34fb5968a7823af38e7fec2a9925c0c09c96cd6532c211dc9eb0ad455474223bcf2e2dfac8817937003a9eddacab5a215b1f457c9b4ce1370d0b6afca5c1a14eb35fe5ccf399af6921819f304bf35f04dbb65e1a5504e42b6ab38e912852fbcfe92d8097a248cdc67e90629456a9563cd342486f1dd48e52be159bde34ce3a623770c19fcebae6027058e3ad610f11cd1ba04bc919b190b2d7ebd2af4bd64d2306faf0715b918766e6d798f74a9650a035fc0bc9fa892f528650d7df0a47c985906a17b773d20bdee9c69288a1e69389f66a93f74ec1ef8e2269e386556a2c7fba7b5e70f78ca2c18f15a709043ab7f418f5193b4ce6676379b2a59b241606cc57e9bd0c49e68156f04af8a80e5fa32162b3c73f6340eec9d7d54b5c54c54722bce84ce4de6fa1375fc5dbbb988dc7cf31beebbe29d7249cf23ee160d4f526c844295119fc053e6943425c956d30f9ab80e916f641fc4ebbaad676ff829fceb0660fe47feaa4c2d0e8b38bbb99134dabc296ce19c5da630b5afe58969f791a87ef38420518fa8e2223ecd18d0d2cf5374a0becf646b899e60f4e49b78366692130cd5cfecc1dc8870c933bd0b37d8ab4ae7fe6cb2c0414a9d44a41cbd9b347d67197c3467bdc8da351ba0ef19c7d315e732197f0e8e78491b53121fb0d6c4f1407b05c7e9b9b6fbc47c2bbe1d8803048de943c9e45d1514131387a847cfe6d915aa1e098c58891e28dff4eff314f7ec516485bc6b7111be681a085a929817299a8f2dc735fdc18eef7de0befd43096aa504f3b919c47180b9158901bf4a2d91ecc87bfb6f42897141570f4d014908c326beaad8f919b192eb72f1f87ec8c7cf0231932c24a1a94b8012521db5adf04eff27269fa19939c6be411dc23e50513b8fae7b7da464e81b8385b52080fec931fee04c010f489a5de4dd1f329c8a1f4a1dbb0ca41fee8d77f95855fd73040e42692f189378ec816595debb0a10986abeb056cadc819d531e72d51397aee59579fc9e3f0df13760e4c073d93d818d1ddac1cb90b769fa9157586bc249cd0935930132a3faf91daabbb2a8d01181f32ef073e732c6fcfc3020933a86f4082b964beab248c7883644b45263fa61b618a57e2775745812715b1fdd15e6932ad02b2c0a90302482ca2c38ebf15f06de22013918b159198bb2c9311bd22930fe6510f2c3a173c35673b0bf1f06d8ecf6a873f05c5b860b85b55069da36b1206e750ef5a7c06b4d399f871ae70be66b7fc2508dab98c631e66ed37582f35e4eed2ab6242cee46203f35a17171ba09817efe88693dc15980f0e38eaa48332bd0b2eea763b3d5349c58835b826ca37c22ef0084e0d7161eba50a1fe19bf87157fb803cb81c7f8bffe53115e40f4fc19daa81ea10ae58d73672f6052f2191881f41e3f82dce1a2ae1871c32f909a6e7c8cf4e53bce22e38ba5b9b123c56d3737b853497799f60eceefec083cca118a27554c04736acc157f8eff18c41345f956e16bb5b07779952a32fcece1ac76cc90def49afef160b026cbcd34b4f57b9914ecafa8c2b9494395861b164dc27dab3e892526f3305f8f9ecae458cc75179dbb7134006c21f1a829f758624d27bdf96d88342e6500306d26b004c2cc9467e29eddaee7804618927037605bffac69cda460690318e1683cfec2e5a44b3158c539fdc79fc64c23f696bb3bc9a81282f76a70d4da94804cb2b623b19630e61f5317a6d975f2cf9429bcb6d3074c68a9f32cb1588aa46181ba56833dd8b5ff792d428e8cada61fd1796ff407b7d7cb870a37b881a4309867f03dcb1f89523b75e5b6a1374d168a042943b74f6438e0d220b0dae13073914002e049b984b70250cf8a2aed9c9432cee6d520038da9e7d9c74a18d8615a8e72dfd8b54c32645a6acb09c2a02f6768b2f56fc175ad0fc6e41d082514acc334f8fad1a3092a22478f780d3fc3cbc9974407c8cfe65554624abaa02dbe23b3e80295154257ebe0efa91090714bd120464c9d52efffa3421192804e510c2373267d7f7338661bfc125adcc77dfd66f09092f82f43010d88d89fb9ebec18c021aa3bf9688ead928ec17c78132256bb88ca3ff7a2547c1964e73af730cb69ee116937e3a211bd72c91f2c57d8b864fa1aaba099a90368a64d92567a5c35cfaf06e4b9b81812c95a4973cff165cd4f2dfce100dc0ad3f3625532aab05cb29899968cccdd5dcb4782d2d8e3b20295abaaab3776336e8c476a54bf4a32534a118bb25c55b26db6b9b08cde401960cb4b28ae9c0b3da2bc75418e95072332096a0f8a94f146db2121132c82d842c3a1290fceb500d8548218e9875cc98864e261a9f1143812302386030a6f8b6ea8487669f455d0dedd3aab153c169d3b35fdc861dee24cb993e6bff13adcec278db4a390717b997d4db6bb2ddfbc33595989c9f44f49013e49eb093c03100000f47e90901ecb1fc64ab2c9e20f812ccea1f6f3c709729a0a67c3ee061d62e27f4a73a0ec6e624ac1f466b851e3c9b049fbb7c13827f036563d4e991362486581e96f03485209a1d4f2611c77be257a4f8a527d278dde663e8aac5889f919a2d8ed342fca86ace62e41f72f3be6196ce18eb7046682fbfb6e50dda64b916ff71f45a60e498cccc4e63945b5a27a429dadd1ddc9b8494ae9822cda57b8c7996a22a2b7d0f7bcfe3c324706597b087023cf705ab9e38fab9e30c6f3c40f802c421d3a5e2a0a995a40bea71704aec7cf485cbab745b426c1d6056c4299acb50ae409275cc6b368273a38e0fd859f6d54f616bdabab70ec60181ed4634c12065c3d234aaf93b20b03fac204f19998de12bdb52cc00b2bba006572c23889e518f7764c3ad8f97c37647c295f77776fba74745571bfaed8f738385b790d2fba634a30c2b8501cb9869288cd072bfc6a042824b72af9fdb0bb5b9e6f8e62fb4607362665c91680815880300b9f6033043ae76427d878cc994699ddf000e433d069366193673d2d029ed6ef2df3248df53865abbaa76d19ff4b0eb05df444d58f79a89f69b288961463f05aa23932c8782682a02677c9811c04d1419fd475ccde64cb120510ce3310ccacdf6e589da10bea368f02709988015f432e489b802845474b4140cfe4c521fda86ba7f55119162ac1cd46909a65bb9a3890f189438a5fbd5690d6f7d02763b5969c2c52f20591d066b216ca16459be1438f2c87d6b4d7c27198105373d7bff656427742374e9555f59bd5e59853e067ce05ebdb503834e2e61ee4d7bfbdd597ec466c343fb4b75791fb2735d5cae446e27d9509e049e2ddb30a04581d6e4fe01602bebce9aea7d0284e58e714dead15396da25d0d042527c0d1a3445b67f8170848046c7adda93846b866670280a24523933014baf83b5d827d03d40189e3c21ec30a8cd02caa84e108cbc1e6aa18ab53f43fc03cf9c20867482652d2be6004d30305adb7c2b66d949830bbbc762f2692254d0bc754fd477c1fd1c41cb3b87985364b8f401d761fd8e2377069468f19861d91b9ba8c19d8513dccb936e84fad23e84fe5efc7ddb5ac517d0d53bf6d0e3d123855dd2221d6ea0192d33e0c9163c012b03dd174dfbdc691100f7e150f548ed892633ce4337e6dcf8a7fb906e7ed1cdc03c96e8004d784cfbb1bedce2ea08f4de9bee27dc56a1353c72d9cfd2ac513a708c93b1e458cad2971c7e4f545b99f6c6dcd38243a14829940d264eabeeb1481b61625e25b087ecd136513aaab326e82c9edc45fc3c70dc670feb69f5b56fd979bebfc4c27ff8da39c0eec7c8c0aea74ce47944abe3d8a3502a7479c448f30ec87e2c954a3fc5ca9abc2da005fd68518019732cf8e6cced8520a5486b0b98db262886d29e2da08d8937594a565b05016f31b66961972b6a73bc82a0a8ff6e45953ee701000421da47fcd4139e39381bc9edb5152d3ad4384d411a7477fb4101f14ef0b9a5485e7c34b9965faca824c99298a04fff132b885660d76d6affb474ba33c9eea8d9a89068fdb29549e2d401994af6e97e1a47f1c66ff2bac848d642597a02b3dd757107091061bdb2562b3ccca0fd5adad56dee5e74587c553fd35432aad4ddadb4cd554f29ab10980f201e4f2133018dcbd72c86c15711e20077ba25d349d233b18bdb0d8cac531eef12dfa851fcae46bfc54124e17833cf3923048e9e385e53c5fde45cf1b8a04b2530676cb62abc745f17e4b10cc3110c8ea049604ead62be6b98b8ef7dc5931ee391e4cc2b14e616d85d9284a5755348bbbf1e68b8899864ef07ef7957a8af1b1573c607e2a957b0a969b89be352d6848042bd33e7409803af668474164b7b7fd46dd8b6e385d1bebd92cf2d537ee9b4f6d322bde67d4e01c9f34fadb3947da22610e2524045ef245a56a3805271f18779f700d6089020c3cbd027a6119f6853b5c95c58ad0d552802f86509598499b328cad70d4422c414e41db30dc49841e8d0012e0def2ad4233471be412845077bcb522592f249056258ee43c147bec5e56424413dc4f5ea133b16c474c8c6b699db1d3c8134adcd24f6e518854689a5138f71c1732a2696c8ffb09e87ba24aec05a4f149d37b0348bf71d377ddcefff8b3170f1c693e2954ea65d875ae1abcf0c415069e6a508ae78b0dc6aefa5c4100724d49f5bea4039b0a98cffc926cba78df361bd19fb95e650393645279a713d89c604513bce14ce24c08b06570a9e98731174dc2bc52e791faed47fcb9b3c529f07d06e8e3d687aff4120dc0f637b8fab4bde341b58e4dfc70c0b8f634cd22f4b600607a199d1ce1e06ad6771208be15810b75a39dc02ce93e9b6ecc9b3ba00eabb2a60a6de8828d0e758a02a02210d56f9957a5ed573cfd246efe1c2d1e868b5838747c33195758590645fb4a1f6ed056db9d1f2be3f3309113eb667cf571365553f243a5812d47a58396356b69c9f2ea8e7058fd1d926a990ff37d9cc486fc1d8fafe2865c6f0e018da7d8677c6fe87d14fa434b1b59f246e0f289641212f6ee0527913828186c00e1582c52438e447c1457fe5ac8b166ceee0070c58d786c1111410213d5b4858afc5a496c78b8e8bb91566a1ac4029c3a2087709c9178df19da41a958cc003334396d527b9d85bd4721e2bf396209353b2fca04ba124b886b0c70f3eef2ae441479ac1c821ab16f30d4f0f3752341d61f196dc2e4b5d94478fe7bf0cc95ed1b7f5d3281518c1088d42aecb4eba2801c80622fac520f4a556c353b107ab2a27a581f1c2194cd9b0f405dc689b55be4d46f79e82e85d5aa812b11f5d09e4e3a215fbb560f6de903da5ed4436c6ca76800194c0340ba0fdaff4eea81c332a093481f9beb338eeef970ce600bedb83ce11f39f9204ac9a9ee9ef3fa926a3d67d32bde059fd9d120308601d405e9eadb5167fc0bc42e4d10f3ce99f5fa84a0463d5412e7f44c57b63c95b08d2149daf056f3953f02969bfc2bc80dad0bb1d3604dabc03fe5c655ca52ef552939f07df4e1f097824aee755bd1f2f08e67565eba22f12b0fd9d71a5c7aa82b2cb671d0ae6c535303e2cfc9d58b5f1aa144789b295c8ee212f1129a6537f84a8e7ce5400a25e221698c2e06ae4d934ea08b3a1a2dccb5e517b8432e25bfc2f5a5b377ad9397f4d91a4250279da3ac19ed6fb26cdbdbcb635144abe11cb6635fbaaff39bff9b0a818d5e1c98584c6343ea5090e49af7a49a730931b9fa644796195bb013ec8d9a99bbf5384784b91b4a6f65355797a27f7b8856eb8e1855f3c1807a4a9528c0f48f49986b1fc31c01f68f5e4ee268234efa30c2bf7191fe49b8cc80c9c936b4aa04f75b654e1d1ba549f63e6874daa948449cb443e4369cf2eb566495448fa52d3da8468eb0a2974fa99b9345d327bc39dec6fd25450dc24efdc1389246410173735ca780829b2fe88ce06ffc04f423ad607fee7870bc303b64ec0fa828b6cf3d1829903c7382c83b5e04878c34d215db34a83ab2b4619bbdad94313fce97bb62a1864324710515ba2f1a792bf06154d68eae99b0ecc66b1ff55f68ab241381580bc19a73ca1cded0354e42220edec584152b12b098c82128a428ee7885e68f6d66ff73c834971523b6793be2b86cd60ff3a66cea0284d8b972b32db9888887ae444fc396a55cbc259c9cc380c37641fad6dcd9f14310d1d265d37d08eb176e36518b5fbadf63960e2abd901cead710ae5e680e253dce0ce0833475eac3bf67ee2c84e07f5c9972e05a4a82d278d3e288362778e9a730120d19c6180136201b5a83eba1ab4566e96f2e6e18a15ab8e7f443ccee43d462e9f1a9ab190ce9668c5f45f70b2543aaaf00f88a9c61aac89d1519fb4d22a4937909e070da6278a24e23c4e36a12aba73ae110f0273a289b6cef5d380762dac269694964488eed71386e7fae4301ded599bb2f45b828ee40a2245c652f2a7c96d51ef93da0aa48b47180236e30b2d4606e7f155345d8a211a62cd4d275188171adeee6c6bcfeccb092cb8a597cfece96a1d4ec07ce12424887804bf1fd56a285a474384caa322a7c1a7a54bff48a7920a8b179d14022dc8f395971920ca2c35b40b65204d83867f7b38c8a0e842dd16b39743dfd2752657bb08b0af965fff00442b120cd81d3f4d5d2e6d9a14cb8b29c97226188d10332bea092a4174ac7bc2e75fabf87b4be1ad22a2fc76608c1c1fe8d7875822930c407fda01e1bbd1e1022e4c9fbc229c0375d238b08e883854bcf3a64e5c868365dd67199852d579d64816144c83cc5a35a9b91c55b1c634fa4d1a05fa826f4e63440d90dbfe733b41922bcbbd892e999076149b73bc1401388671dd5a2b194f12f9b259f4c27913191c3fbf483f26d26cd20bb40e4f10f120dcb0bf1613a29d665242d314fd71d64252be28681d9752545748ba7806f8459bf04445f1259204d7cd7cc724b1f029c5ea135f3b7dd7f7dfa4cc66fe843bb6216b5163534252d3fdb2ef0a290754d545eb68b69a373cc950aac6617845b7c3892c0d7e6bc2bc09192c4ab2a646f3700edc4dff31921e76e74ef58448a2e3d0826d63b8af95db75704664f97f6a54486df5d8a971724757aa3fcaf3a38aaea79e02a6e2ab28288ccab5c7950cc315e396408dbf1cc338c95faa86620394876d0210f06c70a5d7593087632908c4bae40d80e889f20e7584545394223834ae253327aa8b157f39fd403d2271eba3cf1d9869af49ff7c888d638d09af319e07d698d27c6447563a01c5ca55b61b256d1f54f523a2f7d443397de84dcd3e97d678e95787874a682bca9bbbf127d2df4716e892ceff2656201315c3393b7aac4c9580184d542c7f1ae4802503d8d41ed44009c9585b99eedd18a4b5a8050d4bf9f6f19f00e718949bd53105f651e51b6831b40c92319fb2b8283825c3ae215be1bdcb0500424247e0cc65ae34b677eee7d79e2b640fc14bb731936c6dd34f7fcdc6b0d02ddc3cd7f57aedfcccd58ac9201305b9a0a02e3117e7338cd2d4fd63f1089cbe5b364211a9ae5e236f137fa4fbe5562e22c05a41158a98b7bda158c0785a9499de69cb6518e305d1833c8f07d5bf9f2c845d89f2e167a0aa40ce474b5053647516e2c7c353990fc6611d882b4bb4eaec189e8d2a4473d73bcf588054105c59d67f5a61249efc49ac92831c1531cee0b4ba1e957e036a3e24dd5d54b50188ffa8c214f8f093291b89207a24e8ad72421f85e1bd50811a758c4489a29388ed4f136a65f0ec1f9c1a0a6f7f0e9c4974d0575fb3a70a6f5d6254cba74e92e486edd9c7738db622a6975c3fc0ffd48baafe90e814fd622adf7894500344e046cb7fe5806904a7b0632d4e84be99e826e5272be06213c6cd8dd88ac0cb2587275f6c29da42706850190bd29ffba32ceab097768dd72cc3bda0541dd221cee1609b9ea160525cbc189b220ec8d9b22d2b20b2ab745f4692601758efc6f2b30b99d376f47825b3c7f5b1c5fff064222f0856c9d27d29669d70c1d0b706522982a9b42a7532984b6481e02f204e80db79921e72f4728460c7ac79e5acdeaf2f4e9239760d602a299aa3b592aa97615da8b82a59c2c06232dd12dd7b45a7e03f685f22f17cffd2099a0600eca92967d4a0c6bc81404b1118b1f7abacc5794197ed61ff830cb6864a216d313525c1bbf18526d2de58e3c024bbb501b7804198f168be6ec2094ee02325f9b00179725c4c3f3ad4a0e34dc0fa2f417a4060c26553358114e764c95a78c818559e2481693982a9829d7b5891d7992450b41aa6e7a57d05298571b6e812fe4d5d85a3d10a18437e68517015666d399c576ab8563455ebee5b2339009620a5808b6ea290da2b28b437aaf084e0fb8eb2f289eed640690f2b1867d4d3dff2dcfb0d8676e0134d3ef49a4e2e524582db88cb440f53877cdbb36752e2cd3adf0c3126f889e21f50e0ddedba1550d7df95e2ab5ac55ce1389e9fea99fe5176a587e736603a540fc1d4f8d6b9ea582c7917062454a3febf650412a95149db2d5ba10d4bb475041f3c4f4e3ff684b0c14b1cb51c50b2ac459cc08a788afc17325f79e3aeea46839fbc44809c877e3058acc81bf2b831d164b94d3fc3ef00d8ef76af870f3ebe1d14b0fcd5ffb32e4ef370c499827c76082cb174c3033f3734b5605990b1477e1d9f99ee9e7ce2ba9e906f55736a216904860f3fd2025b01b133bc51d835d0042fec47a0e5e89830d20dc1efeac4fc09eb86bb46828ff809e7d4dbdd8d8d809ab2381290dc6b50a6ddb1dffcc43e00b026401e871c801bbb39278d8e457525f88866d5f50e60a05781ee25f09fbb26a8b75e27ff1d37dcf6fe24191d3c780ff2d5f1b7ee8295f9ba1e0aab96f064c1df0967d43c570622f39a092d256613f69ecac4f0cffa453aec8e0964684c5a369cfd0a3495b1389791a81583e9702abf1082d701b242267f4bee335de714c8b7c1ae3f3f29c6fbf29effb4827ff69d92eee3afd207896125f35527073e11bb8e08390b4469b763aefe12f1b7590bd0ea22191d7d51173764fa8f14e6274f58e13506a0741a6dd8d61e49b40a9ba83795615dd95ade32c80d12158df6964c76eb8e37e2ec52f29f178179c333017ce4f69c06d469a728383c7fbe1b6cdf9d4bd40f0956edb7d0e16c4955686bd60f1c0ff77aaec3c725292797f9d37a4eb0047021c5de3593cc64c761f10d56d12d4c7156077d159e837abfe058b14fbc36c5aba46e8685182633da287167aa288a852e2f2768e9e6e6bc06ac01f1cd382d1298451bf2a539d7c4da6a9881c69bf3a99680ae77f31ffde16ddf74f11481465b0bba76c2d43a5f2fdf329f7a81d076e10c2bad04ffd60a72b5958a0d17074c4c5bf31d02b651bae51d14d18aca473019acc3c2650709c51d2017e29d1b6921804d4f73200ded029d99aaa0528da31ab5a116ecff63735ab51f14e7e2404cf1b7bee886100e1e5657ce7a771aeef51009dd6175bdcc6842f321952901ff6166d42884c5ba693023f0217023abdd44ca14cdca31a19a974dc38803a30db888adcca20a807918f2b908f930f08897af820f271a21e3e7efc273ab55a58ab496c69d775dde944a433cdebaf72afebdeb59df777a889f252a8cbaeeb5ee0598245de7bccc8865f5ebea2f905cdeb603167cbf33ccfb34eca53792a9597f2bc94ca53755caa5b711ce779adab89106575f10ee4aebfa2628c11a654abd4aa634dd55c01cd1b1963645d08152c8c55c27583116f3bba68316adacd0d3c7133d0a433a5539ff2a7979aa913cae35428146a5e50bcf5fdf6c897ef9043c518e3f5bd0ae87ac59bab39638cbef226432ec5c2f30e44a53c957752459597f2bc946aefa938cfeb54abcef33ccfb341490806de81a694a7f254aabc949752fdf25410a23cae9b1e8487107a9e0ae1d9614a92eb0679c995abfb5c2eb6596a59ee569b6db6d92ab5ab5aedf52bb5ba7677777777cbd0414d6be32c76dfaf5fa6582cb35a35b071c067242b58d0b0945c1f58f196244934d52b4df09830250a105a6248c2c41dd51d75959d3b0e85ea4e4143184025140ac54a824245d9218466faf4aaaaaabf4eb1b1a9a2c9bd7e696aa31db2dec4f5708021355cb09c6212ed74f289559a38394175ce4583e5c449eb0c6fe179a10a0a2bb851813a20c38e2c5c98b2101e4278588447090b09033e578152c29627a8805103d6b02cf502e42548c88b096904237d695d3a21840233c5e01ce73293fef91624233119770c61324913156cc88dfec45593884586081e2253da9022e6ac84346848af1a9252caa12021a520a1a1a021a032b42025290f9576ca9c4b9860f105163360d76969751d193bc428729465092fb03461247bb264c507c5a19742851a8410424d91ba703c00ad82a67451814704cdfb4278ecc2300c5b6c775b443e7a2c292d0d5999e23b4c0285a5b76fdb77c17495c76cdbb6c9246ddba4847c94979931fbf7ff9b8a09737201611761be635b03651de6270c7629e6f1e9677f2f3722dafaf0f881f23c364618e79c930af82dbcad8327d82345dbad0807827f973812c0ef52877c0ac2672e1f8e04f02b0af2ad0fefa8b4fdfbff1d4614b6ed1c6f69b5a4759c6a75dd932b05205de9c284866ddb1393cee9b86f759dd4af0c834abbbbcb7128e0a0d530b9ecb2275f6c6c3efbb76ddb66ad5116adc8f499ceb06ddbb6fd833f6bf10149cc10b6c4eefbbeafa3d23ef23aeaa35d47e542d8799d4f5ef420e430130cb8a299d6a839618a858b9d3a6e72d885619e1cc4b0bd969b8be2b8a5a9d42d794a4b124a6e9d384d9266f91dbaaeb561b87cfa1de3a22cfb28cb3a2a919094945a2d28509a6813b5c13e2ac43af621b28eca252b29e793923e97cf98fdfbfffd39670c930edaf80186a88bd0d22ac9832716d7994efe15479aca035cb06d779d942c69dbb66dbb859934da08e3c492521321989ca0464a82972f5d3e5f31cb944386ffff7f0d725d23b661415369ae887669a9b66ddb4ba9fecb79f550b344337d96119ed4b66ddb595e13452f086d92a020dd3384dab66ddbb6b568b66ddb3afcaa2aaf57fdf5ff557f40abd55a86c64867315b0f18b7cc86a80dec62615f5aaa693737520e3dd1ea6cb0a839b191524a4ba57fce29e5e9c4713ebc03e7bc96148c3a09c83a40de81495c109050124a28c618858082928082848084bc99529a4049495aa02439290b04f3f9cd09460b13982cea15370f31d2a5af68fef00e3c5d7f35053ea61f6882e2f365adebf56dd5566cedbfd8ffe7acbc6a72d02e69f90dbbe63c02872e74ded8ae90d2b68989a2454e4e01f6b921c03d36f07469f2399931fbf7ff5b8c19018e6834101cb0503e4b018e6834dafa01530e785d7a5d00e048f07d2bb2e0ffeef76bca4113418e6f8bb5a942530552502bd90a18d1f4feedff121efe3fd4abeddfff6f2946b48883c532f8ccc1e7ef230245d8b68d25e90182bb7ffbb67d39ad00297d30810f34b56ddbb60ddd08a086eb0677f9d4cba72d0d2208f2e910a5d0f51337201efcdc5d0eb7c0185a11aa5bdbdb0264681deaf26dfbc64898e94a98292634cd99fa6f33d05acd667fdba2f81d7e0c9694ea936499b668391be857841394491edff5e14c78d28cc777f3ffa7e1216896b7824791d979dd5ad7ebdbaa554005aed0541a2a5c5deb7a7d5b555555f50ac2f47863f6efff373cbce968618f11da25b4aade6a55952a67acf00a151ac2efbf48944e1d37cfddf397cd4ddf1c77ecb76fdb8f21ac5cc9fdfe7dffffbf5c37b9c9cdfff2dfff0398f1edade03963cb299ebeefeeaee5ee97c49a27046c41a4c7098460d8323bf6dbb7ed5ff9d7c63b31eab84c1c3c6da8dda3b80ed519f5c4b3b8ee818f5766dbf67e77776ddb46038123e662befdfbfffd8502456cdbb66df714b160a24863f6effff7ff1fbad02344969a1d6402449653b8de5dddffff7ff9c4b74d45c6ecf874a4a05a7de3990d4766dbf1e9d0f16d0ee71d95d9b1072ba020f855900dd28a05c40a12621d6a3370a60b29a52a4d9216e129ca296d9a07fc1835ade8e601db11812d9e38fa316b66523555c76cc3545555215408a1c20dc156abaaac91a215715fd4bed130a1c33a970c442adf91f8fedf7049b82f53abda6adb9ac6ec9c8607260c07b0470bf88ca4258c4f39cd60d0b8e1ac0cf213e5b30d67b72022be1a359044f30f0e2981f02094c91899c92885646236990d88cc80384affc848b9d98683c9191c6cc3b9204a192994c94c629b5094325266030200122df0e0c60f488408f98932c88f7f62369c5ded4072041e75004fd75ed7d1d5e83a7264747421ff890c5d46f898e9c278845d100321cd214f27296f4e10c26d8756ad6a21ab23064556095555f5590c361345a24a942059a35476ed36a7a6d1e856248b668e8c9cf78967dd68f246cefbf8878544817f667e66d4e8105e5ac96d24b66d2646488c8e1cc960db323933a7490665bcf8cae818b56dc7c82b47030fae817e5b638416b36dd47270449a08e2860376f7d9d8f1d8a05d62d9b11fc9a2688ee3f837d7b0e6544293a499f5d9b66ddb36a1e5845cef7777d70abc20bce4552f6d4971d0b06848608800470ad062dbde42b07df81fc24308e75c4283039519ee2a1a942a55555555b55aefc11f4a10420855c110c19386a7503201e462fbf7ff3b82d1658d36d3c9e97c2a3b2611e7c28952bf1c1def7847e58561d8628bedb5dbe55a923a4f68453335bd0c82e778474a89a4fb56cad3cadded8ced6a6b23e5fe0b29a1eddfff6feb605a2d0acd644b850a55b3394d39280595097b6cdbb66d4b99d921261b9b8e0b079169a8544e1e758a0600488204331808201008825018c90239d133b107140012455a3852422c342a8d04e44069281a4d411c07510c00210060200618a510724caa3d3b743183769bb60ceeb26d0534b614e6ca17022556e12c87d9abb9a859603f2c4621fd23cdda98cf2b80e0061b73904f2263353836308114d1efd6cdda80f3651cd9aebae0b26375be7fdbbdc7f78c105f917fe8209398c5609d4b273cad4b2ced0aa1fc6fa55748813f872f731939ff76850a762ecab0e0a436cffa1925ee152b521347224774edcf18fbfdabb9f2e7bdbde26728a0168329a5d6af17e96337b1294293f39b456b01d4d281e9b3b8cebd03690b3e6eb93f8fa60195aa2707d881f255387860a9799bfc93650945f25c3adee06393046580c56dc46035365336b49d68f37a8d3e7a56b8311367500494a149a925bd1566d8c0bfad1d69d9c950d8d913b9a4e9b77a781f2a99b4ef35d640c7c8af5c002ddad0bce233ba1a67213ba3622c080147a10cc0c28eaa81ac207bc59ece3a8a80806527b6a750075e65dbc37aa67a25e178dc0211ae40a5ea6aebf8445d53c9fbe961318fb601d831953db8a44104a24ddba21b63dcd2f5f54d7f6800168e744511b8d8f8bdbe2403a157ffee18801cee1ddcf578d3850d7fbe5a0fe8979c25a14fca4635289cca689472d2fe4b0a773467f283600e9924f18c49c1734fcc3683ad723279393d133b2ef01b343599048cdf09fc78da121babf6757a202dfcde9185c1e09806a5d2e782ca3463483f12b43913abcdbf4d82f977917886deb650f50732bd2599e0a5094f449d4a08eab119ca5f3af50b43a90a7b0d535b22709bd693a2db2dccf0011b5d37b2fac0ff1ad644d8d0d2a27b45023f8f28898ee5203a514c629aee94a235c48e81b33c09f17501881ac3cf40ba5ab0a83141b33fb4a237d6ac5f21d5076ccab886c68fd144786fe270356f1c36c4a040be477c27554ee5f7b26cca82baae92f204324419b0b1a909dfdab97591b2ec4f269039111b98f5f57009243175c2a311d2168086f70f132428a72ee0c7a141cc727c236771b14557bd211c800475c8454160cecee0e278311bbb42f2b78971c11158ca0496b433660972bd85ba5080b0983c53cc5239662128c83e48d88d40ce6e139ecd943357fa0743114e7e25321b67d2c23875b4dc5df2a1aee952f35297401614154376d9fc8042a62b47dd86460348b40e00aa9fb058a2bf06a31b27accaab32149789a91e77cbbef41a7b11eb8f3eec4321903764133ab2bc782d43182e2eb5773248ce420498bb7ba003d6069bb820e553d93029ac9bae1ccaf4ed218b8eadd0a34308248b85b88b64cd93d55f65faf8d4ed9369d5a7dfdc7ab1b7935701588d6df8d7c63038dbe138a5acc77ac430e041eecc236118bb20abd6de11a466ce36186cb4a352a65fa33a88bcbe056a371e7a0a9a6a284ab28ab0a402d5fdb08670cd17a5c9d94fbea404755db5c4b49fd78d6b554fdb03b4bdbe0cb494aad3f7d29da7e7cca9713d1de2997c499628ba59b6c29fd20dc2a9e53011b9218e30eac35543d811e65866abb37e178af99a8b5e2b650b4f2b9d9dcccdbf20ada11a7084420d4745418b0285d38dab5abf884affc7893e2415a1dd0424d3499b50f32c47e3a3b03f65a9124311cd74d0defc293d36f70f7b93d4032ca8def9539ced1a33d9ae1c608df450c7b781dfeb698fb275e0fbae56af03bb394d791bef25d7d93f15669a06caca4aca109d1ebba42375da71dc41e1d7214b290093bb6cd49de04582c43a8d7df9d8cabd9964dd8d6f74fb32e5e9487e31c1a5f8531576a7a38330980ff4ea9b5dd330982b094655f67c4cb582b79bd3163350983886c072feedec4f1230575588732ae1602a5179c6cf117713a008f3200fa6bfd35e6b2ccd958edb28e02f7ce1868394cf95808b8fc18e85427afa9920c0cfa41330ed4c7e78fda9d8511e772cf72ca96158ae3d3b28dd2a64d0fc10ebffceb4730d969c425ae140b25a467d865eb2b10c99cb6b998fae8f7930227584332ef1dc085d0f64c63e8ae6f34321f9e1006c264fd877fca626be00032f5ecc40f8505e5e8cdf740badebd5117033a075f6bf0b9994ab21024c1e251b37cbaf31da0c8f05cbc46783a70aff1fdbee7f483c24b973acedc9ddbb7681d71c8fa9326085b4855988375114b292d34263ba087e96e22e4c3d994504212c2d50400ae6e9ab6c3edd115d4232255c881722b911ccc422fa1b0416b148415702a1beb3c4a7e7b6a938cfdf2d869420cc91947d847ce3d5656f6c458bfb74cc77ca247fb10174531dc431c81a2ee112f3e7af059b6103e8dbb73ad494c6cc50ae6fc35bd43163ae8e8b7e9efa9d2f4d1597fc22152d87250645d99843589eda17fb60b4ef935e8cf9e0406dbb7cb31198a41234868a7f7b28816d7783d52ecbb30d041032f3451c8d8ec1976c02d08fc3c958e05ef6c5ad4e9206ab15dc81bb8ec76712f69d3e7b25bc7abc9eecd8ff7f2646985cd1d7327119ae6c6a35fac5bc892ad3c0cb1bc1c6f9a11b3ff9f3fb2fe92ca16dd803aa9c477cc8fbef0e04fbab11133cec3bf71500786d7777e04e61ca4335635afe12d3f2611df379e11fb203a930f3466abd27056a88a5e8b442ac3eaf2044e243118d7a7192919a930c4c294522c90c097659ac5e324e05d5a173a02a2347bfa8befa4419aec6780c7919fdff1d255644ca80cc1c0ee4b569595ba56ccf4ee4fcac6dea1c7cc4567ce7c028607ff81b1df21516a7c83adbc30c03c6c941fb4cd598bd9b88c6442f3dfbbe277116476a60eecb5d0d593cf18edf9bedce6fc91a99c42fbe6e4c58c7afeeef01b41addaa2f569ae1b35aa4d110d2ab04688ac9eaa941dd84dac451c122cbeee5c1a4901714e20c40fcacb0fc34bd8601e5d7ab5f061e8a7833a43eb71198c61e759e2d85eea2b35a79e9257046a8cb47dfb4926c985a2451bdbc6d0f574cb79712685fea63d5393cae17e5ef054e1901876fafb1d444fad7071a369c0a3a6008f96556f50c4c35ecb29de3f0b93ed25e36aad52733f20d64266c11b7e278c4dc4b197b7ddf91c9d476ba9c48b74664e8175f9cca64e59e784b661544f9f5f2bcd8244c44c15b9061988b01f01d81f48ef7523ac4a6c2e6081b03aaa6ad3252e6af69c7b50dfc3da48934ff4b3602051b45b84cf0b66c4d07f93f96db354500470a8584a56f6354c9cb02bb9f1bdd0a2486779e60a576723971e8cfa5ba8b2ff0ea51a454210a1bc73173b130e8442a22939d18ef5595bfc3d7676cd6541f7d0e2ab82342153ac428c80230d2ccd83781acacba99954c63a72b517733b014058dc404321abe93c976e8b98be8c175931d65791cab8109dffaed6963ba3c1b751d51caac761cb660396c7c08b0dc6c30df4dbb60b516045c0ac6ad4de50c57e521b0f5157f44e30e6bf9df49161f1670bd3bd415c260176a3b2a2bf72242b9d5ac06fceffddae16ba49b0237155fcf6d8185143252afc50f8d08de8df4e44b770e6e14894631611b899f039fb8c7907584b06527456c1e69160ff517f85d847d61bab35830900f98568db6593e10f2474f060f034a411942a93441b9221e8bb3a3a5f3c5530bc56191816a47a8865b67a2d3caf5ca611d0f2d271e1cb23ed10b760feeef7960f530b83c2995f9ce870c4bbf8ad980190893f3b163efa746dc5f84e7281153f8667db8d881044d609c901d1ac02b1bb353cdbeecfc2bec312a0cd0940f52b4274ff97d83824cc4340dc26ad01200a90a8cb1b74f70c9f5e3febd05da3440b45484b87e9b48fef10504a44d43a441d4e5b7c848ae48966cd7a81d5325e3bd3c4bf0dbe5b64060cb0c4161054b67dab965e0978067479ae7aeae6b7c5dcabdcc6dddb554a158dda0d6de51e7aac45e8cc171e314067183b5b5956da4c6056983ba3d4824c503d52fb304b30de9eee2a1de79883c8e6132a8989318f538968fb6855bec8e1a85c109b087c43d1bc0699ae5760ee8ce7cf9a3356955c3efee02d4412d0b4929044cf09c41eb407b7c878a527afc37baed98cd04815e314439e587656f19b7903ecc7d813892c05f38b8b820030f785d66449a6ead43401d2961a2b13a4252e1858a58f907ed9a6001b6c9c325f4753dfdb966f4e4edf304ca61605993993b83a2652d9d36914fa763a01d11ea53d4d519b74bbc8f50b3034091558b0cf3724f1b5d5188643177ad6667231c4faacff0e2ac54e4591ac6e9df68820fd575d316f72a7e7a2231eea589c0c5db733171f65f0067a68387d42cab69e63750ee62104c4be27c6b8d297921710990d3ca9dee5239ce6e2351f1210d88c6caef9c9b38582422d70c73b342f30c8aeb94d4325c80344789e837ea337811f131e4cd9a0b3880becf6b1a5df2ac188fc29a04934dec44223aa4c8289a5bd7e051f012112009a128f19d7ec48a6e881ba4a65bc95a19ab647cc8a246be503d7b5b0896f4def0140825fcbb792e6c6a3706d8bc173f6a5c7bc901d002313ca9712a901c1f746261ce549594e6292f87231366da1cf29381a6feaa5d30a7d13ce7c06a65ea790511aa82a617646c99bb0e073489ce6dd6331f14e250b03943b4effdd840c867a32d28ac851ff4c997173608d0f0215169a7a902917081a83175b3d55000421db8f40d75e078d0303cca5a7783673e8450693647f6f213d540a741811b7580ff2188e615645d96fd6f20cf315e4035eeaf8e15afe0557dfba0c1275012b3c993b26456e0134def41f02c42af7e63634d7fd0c4022e030b34bd6c2eba47e1910d451c3e00699b794b05ba1c93f46e174d2371b201f9f99a4ac20b79934391a751c577e531d0c40fdb6292b442fd9d90fabba974242479900a4e7c9feb27aee6df095608d70ea286cffa02f7d902eeb3e401381d13f2f5b55be11a6baacf1aed75c1b47e2e94c7454c290bbdcbef35a977f3a299005a2ed6e292d5f7aeea0ff2f79a8abb7ad4d6d13e72abbcec3ad9f0690369cda670bb43a353c847cdae36a494614e4d6c8023f05362365835207fecfb51b53a47464ccdd2aae0b0aa106ecf33c370ff111ca1710f3b3293c33ce83a9dc8d7f38b549334ebefa4877dc97f7cee578092c1b4faca8932338495805f177b4f1d2c40cf61cd4c5e7e3f5e06d4ec444bdfaec351a98a4ca21ac4194695710eebe0fcb6291aaaeae00d1768186be6f34815047c2e3b9abae599194c6621fe48f25bd2c9d7e5366ebe0360d547543fb96c2680c9fa07d3470efc9b6dcfe2c29c2f4cd78cd846615654e6180cbebb1864f1f60113cf934cde3f2e2551da8214fb664d2900e845e28e9d67fa49f98417674b6527b96959d9c4ba5631c471454b21d598686b2d0407d2927590b27ec88beb57e96065133517e2cd6300f63495ab509070d41dcef79d8a97329a469bfc25cdd2aeee30e24ee4dcff53238717309036bfb869c933a1fd93cb49cf4dcf0e1e464db639ef6ba38ffcc157887d1f2beda59896d79b81e86bdb76b86ae7dae3b12a910b875ba10eef53360988110cbaf0319d0fb03ef2e578143d6c29974b797b4605475020033e46abbd89aaa22d232f65aac96240a6aabdc829bcee4e8167ba70207cd7017645580e06b23391c9b12d3917a8996cd020576e013930038a046a155cd7196e981adcb81d9e623224fd850e6a8339494db256a51740f360966f4c4712981827c7b50780ad356d6df0ee1a1a87eb92c601d1e1090aaa82a0b70bc22fefdd92a80175a47cf7310688a1447ca212ebe508496d854a8886a0d3e518dab2ef7331ec191c29873d5ea9bb36afe05df3705c329b9341c8098a186b9d619c0827104a21084d72b9a4126e51d3c3fd273c127c06366e770f47a4a8fdb9b19b38ca4762ebca8970c819d1f8f3437e4adf64774081db8b3c1e9ebc1ef26b60cd48ba1e56c6eb9055ff03e8751a8701bc6a43e7733eac5a2cffe3824dbf37726178496da00c5235fa1ae27201037aac3548552263e73b342f6eb34a709d5e1f6e1c8f3afbd1e84c4acfced9349978029f677704340bd60a6cdfa7053715c5879d0896af55b36f364f7e244be3359e766d13ba5bebfe9b6ffd9cc12f9a0d6d0c42b851ab735170b97f9ddc1f68acde38c6a542b19ee711d7de94aa003ad27cf7d8b3af9644109b8fda4c21dba6f595ff53ef494bb973780481aa12ade54defcdee97e55c094eb0c9d80d526bb52bf2bf06345091d730ee7aeaeeae7e1e543bd6524081466347b08ea98bd7cf9a26b0989358f520be685e2e93df711ee9a75183bd7b596b0ddf85956197efc9c04122fc80374fc9abb8f96f734feb1c9e05c73c1612127646ee8c6fe4e6c799e2a695161353f8195222675d9896c9dd00d09e738da2a085caa856d5aa52cf047dc38f4f0c95dcd721931ec4e56d98b02c6e1940e78d050596f603ab22c0ce19d015c55cf1a6a9b3c7848cf17b63bd8c1f4bfb438af47d15a3f35785acdc0d771c0bc7ee28b1d6df80040962253ffb3e7a4669b63b2b6f2f7c4a26b13a4dfb30fee23dca204449022227fd81099e3bc70a489809301cce27015e6c33a9dc5678a1f393bdf9f2933db396ee68cf38b3d5714cd39c45b37dac783f14dff0f2bc54adcc546d617bb631aba6b787743e1007c561476cc4e9f735d4af50c031edc5078bc498eae92cd40449145158b9eef94d6ff93eb299ebdb35877341f67ba002c458b5b129466b6382691f55c181f927ba57e910707355210e546c01e8fa93510bc523787b34b65ac0a63a406b1b2feeef7880721c5fcecacbcee3574ea1eab81e10a87cfe1639b156cf40c9815cd71eb0abdfe7739151d960d7b398ca821931a582641ef074bbe961cc7ad07da493b57be7619736b2904c8054c44739090e72ee34be5116277e24d00a86087adadae8449394c4d743b459f38de05cebf0c0c9db6d1925c30bbf95a9d09225560d9c31cb8524f014522abdd64ff6b6c226542e597f8767fc2b266a4d13f4965d920b75b7bbe29b23a5f17268e5149a202c8800f1baa96feeaf186d0ab4390e4bbc97e5c6ac3a4fffe587dca6f56aaa6928db30e8675b03e238872693bf3899ba6da8ca1967df35968e0f2373e19c39b6fb8d0c946fa9fbd172db13f4f90ee9d68850fc8797e7a582931ab4c5ec628d285dc06457bdea10318110e88200445eb794d81374fdf79060c97400770468fc283ca4ed7d3b891281015ba2c3a45ce76463c9cd83324367ac70e36a52ef4ed6ec2d7aa14a5e317ca3b00d99dd6914afe0eb956414937f093549f18598f21560a75c23a7178ae6c4ece7954399ace62ff51b22933c10228d9b32da1a2352283953c731577f1d384d53d9f92b3e85229d33356baa62b813e37f509d1127959c605b0dc4eaa121dc243052144151b6e6225abee6c8be9d277546daf3bc8a8dd7ca442fff8ef355e2a6e7a1fb78124b4e4d0e9ea35a5dfc4824a633eaa405bb13044875c912730e6887e647433cc82459f41b03281fcc627b640d73aa04ba5b46e15892fbde12217fbe835df90ff3a32be3355e993207f82c1c114cd0bc978a161529c5043137787d4b7da193ff87b6eba2b55e1e32e31423e936af5519e7f07a5ffd77ad31cc8b10411ce0bf92a54cc2652e9f410a35a18aa299babefc14613a3f107fbca52c6a76c8be54c4e25b9d7069fca4a7bbd2fea6a01d1cb0ed2f8461559d164c5b9b5ad733fc0a7dbe6778d8a2ebac5eee54ab23f7f0db36b614a3adca4372c08e4c68d9a1ec7b5602572558c9b9de9bd96f0552c5bdfdb8103c83cabaa467e433dfccd83d053ce6ff42d818a4eec2cd89bd57fd4913fe9fc83fdd1b6f90ca244f5062a556e3f37da13b6d396edfe28a1a1722127beb7e10b229649b7ae6b6d7e502e86633882709f09e2d4250efbe4fb42a03e765dccd06094ae7003762e9620c4113d8725522a86847254bac28913d397a8f1f03e2572b0a3dce8703a2ef3b5be8beb930c4266070226b2f83fcdda6b0ba5b31b062f8d7178f12140aacf24c297e51d20f4ad8d08b62164c76a96f3b0a2c3089b057d6820769ff68505acf3a6d0514a16ad91bd43e5a11c312c04e1e11ca2a7494ffc5dd3754afeab1035b2809ede17d0fc4c35296ef70377a91283a17371e4c9c308cc9f130236c8fa647085f14ad0a96016a1fb6cc211b3ca3885cb83f791170767c39e48b5a3ee6f308aceb90a1b4b338140e81e258083a098c1a13352361ddb735191a72cc05ce6dd42eec4589e176dd5330fc140e750b4fe1ebc5b5a7b355e91a519b90865d3aad1fbb34db8c410011122195205e92e58858c8670c4bd75cc9865e756c53163ac70d5863b85dc85c6e9a2385714f7635add098d3a80203fa047d0d5b0f360ca89da93a2f42c8a46585c5eca73e145364327649c97f20bb3d5fe40fec7bdb0be5223b535e1284da0794665663027f7f6400a270b9e0e5d52311b02f8aaa941efd44f7d119d9183cdd0231e27a6cc1dd0879facfae89e45ffa70b64615438aaa8085c2e8d2afe9d6f775fa3ea4d5c6c4accbbd326cd038072b15244d8f2b41ffa025306b675a88d473df8c020a2636a490c58590558aa2358fb03cd53afdc9df51ad5c9fb51e39d4d6dbc25dcc4b46e0a67f3beee0816a078db00c20f64c04eb2a88088b83e4a376b9b693a2633297d309b63d230d464bd35f07897a746917d36598c2b8e8de1df4999209ed2a3a9937b38b24ba52d9a24598b248f55e2e5b4d9bb69dc44a704cb6a92ef5e0399e90528c377e45ff6fa5ec0837414ee335437e12f84cdf879a90686feafbbcebda632d85ef11b5a4505fd6e21333f9bcf9dfb55a4a30d5bf3d9540578b569bc7c077737faaaf622990a21c1c5589f8d5e574ac1188705d10c75841b3e9a8beb8227c68f27b454ea4b3385b780699b68a40f445f89a83a228f5161875ea1e709c24087f541894e8f70e5996ec38ad7ba9407b6633a9978042731cfb9bad3b4b3f4d2812a7ef205b74c3556e6d2e5f5ece6916c9de230c5cb5706ddef4de0d7d18c7ce71f198f8d070482e89c77c82ef50a20e0db637f91a2249cd707e18bb4ef7630a61441f4ae05788d773eb7d55099e07000783dcc6663ceaa24a16a4c0ebfd9995702e45d9504d885ff599848a731a91eb1c4728bb824e6f15201d1b21351b2cbdac2eecae29acb5e04b7da20bbc6d6fb0a61112b742452c17846ed0961ecff2d0507b724a0860fc5d11a1866a5c1a2a2c5372304c7c2d161b14bec99cea6cf4ecae8340ca26611439b6e6cd4d340f70b67fdd78b30af2b87dc080faee824f9e3735aca389ddab03238a51693dcb26198a6281a6ef3d8350052ed549509b4cb8ef11fc776fe0cb2c78dc7683c321f233ef5a53c65a54eafae4133c020b181195dafc7b9a790c2cca1b9c6482a0d9d5cababab85c184490c11504b8221f3e4b55f9aaf062b3315a8cc6642983b4898f64b523166dd766e5fa1309a0ba4d5b1ba4b1c259a52bd8c6eb2bf9854ce6937106b3848d8d776bbd60ffb1801d97576c71fdd5901fb5af0f48bc97755052f498d30aa03bc46e0c8e81c582c8c1dc93209cfe067372472af83fd74d42731eed17ceb0e9ae7de0cba8698eea619416b32b44a1dcda5c0f36ae46fec7704027f08532bc4da5fa9ce003ae93867be82fa5b7fe3869b6bee779ad6ec8507f6ac6488926de9010ed72f9329e1573cf42eb720ce2c55502be1ba15c8510d863e2cdf1b95c111002cfaed922424b99b67e1b8f3fa215580b9b6a85724bf6045924af3e7a1ebd18f233439886ce60d44a2732f95f67a1919aa1751b37378eb64485cfbf6214f70374d88480d3ae7ae39d0560f31fd23e0de2512c2c468114b7403077fad700f59f7a9d932b4cd1a3468bd8bf97741ce528f2310a426f9f344ac9dba51132163d4065a29082e68a822241110ec7053eacbed56cc8a85c885efda8e316a0e31cebd767a1150b6bbdcc8ff400750c9bada2066eb1fd9753b61aa3eeaed879b6c1b99627678042146395dfb022c55fc072f7eb27bf607a69acf4d58f3c09eceeab4ce05a9d799f3725b8f3a5b7425a208b4256bdfa64ca3cf16c7c09eea0ab3c7af9db178b1d6ba879dbd624b1a796ceab66f98e632208a80d5decd66f7b7e234ec85b842d496a23285707ebb6cffcd0ab479555e1298b72432aad10d708ff5ad575a9917b0e251ce5dbad4f9b31e43e953911d747e20cea7455a5dff4beb57b8a1a3f63a7256f62fe7b777c34c19a985a25e2837eb3984449e79beb013a41e4aee965223c925e59b6357df5d8dbce34f012dfe75f87fffd8648bda66e01fca9983cd0f38971dd9b79eda3c253732d8b786e5e094c7da47f57e2b72b3cd3c93140877ddbc99e7837c3959d9bdc2595f0b77062802c071c317953a17e293d72fdf925d5cd442592d8cb7412390c428df0bfb3693ad6ae6ce7bcf69c0d56798e20cf8e95df8be95165baea88c73c62811d41f4ea19dbd16f74729a428b465c255ddc56d03e543d5af4923925041222df388bd882139c47fa5ed4736f0eee2b74c5e7986995e809caafd761d7c39302b215024dd66f7ec060a78e4f77ecfad22eddc54c6a16753a37f3550d920c140c2c4ef42d4a5cce8a1889f4ad9d30925d6791731d4f36d87719944981028c6cde5a0876c316b85d3c7f1daa223ee6c0ad5d8dc60f203a7a3dd35ca52cf3efb6c37751db6f37854436aedea3aabbaedf4949aee82b7984d354a5d7f9b0daa37cd62be4f9431cc75be154a1d4a538b780e28a5549a32ffab9eb4ebf008a331c4f917c16f95d7efac6e633e4b993c0b8607888ca43801d6cfacddcb11bbdf33419243011299021426844cdcb4bbe936655dd342e12eb339c0b508053bd875b36fe494a7f09cad5c9183000478d4bf5639c0b0b04c04c1b003903f993be019e86384999c7a7f9f203a6e31db6cadadf4acf80c3a20e400d7fa596cb6dda90447fe8d721405f8adb5ebbe80da39124a137ef10844ac2e488cd1f6f9a1616b10f08c444ab513f6a6f85c9c23e68ef6ce3efe9078d7830e1a3caafe73b54c17e5aa0a6778c420fc9b26ca3aa6106bdf6eadcdb3e7fb53fa2d7c66a6c60b2fa0b93dcabe532620fc137dbf97438f70c68e4923640b5d647244464e613f30bac374e9fa7fd9a996c10849887da6f952f7b4a09d0861265eed0e6646764d1fdddb532151f105b89b1eb160751465ba8e65a920a5393fb91fbcfa3da53c10fb4460e14c9cb0c08da1bb40298c6dda9521572f0e1d1e1c1e2b05e99a4e0425c72624e538553e20c8ec66bf2f06ab84aceb84875f8b7a13020e144fe4c1f95e09c7240a8191551fc17ca49873709760cf8bc18ad6bf613cf2260f14c60f262b917969ad08267ddc0eebab180538e79257d33a79a48746ecad0799c327af213187e4d189779ca117470d2d7a3b7163936bcf6e6c3e8ce6377ed696b017fbb548d8c464f760aceb231afc54fcbbdf77577294e66b1a82e9722942f677c0c39a3c974509925b183752b7f74c824c48d52b6c26ab11e6bac3cf4407086e703fb84efce4d13b26a64c2fe015c664f77845573b7cd91f3d4deec393d224005d56f47b9b428e09deff4fc842a30bf60bf8e2d16d9ccc72791f42576b025f0ff568eed1c40857e8ef4d55a1e669ce8ba0ad7674774c15e59e802e197ce7f1f0a9402838cda76888b6de83cb2ff6aa43354e50a38dea12ae84593e6e28fe388b495eda62d21850902dae53bfe3b666c57d9dd75399fa64ba3e22233c05bd85dccb6cce610f3e52c2cd8826199f00f6e7ea9e6c4dbc13d8d49eb3b0aefecc094c94fcbd2e554ba41f2143efaaf7f2bb33f3cfbaea76ca696312122e46a80d8f2a42c09e89454aeacbdf30cbb1e5ea4d0eac1a8d8a8cd1cc5d71012f7c0b2c0ef974a71cf15b6675bc62302bf8f4e6671ef019e5fbce26a5c73cfff307f40e1a7980cc4abf3de5aec3cc86100252934cca592620a87f3cb2c7531dd2adb42a5c6ca5062d5c8505167baf4bc1194068f8a12c6f0a60c4831fae4208a2c3e8f0c5b847bd6d94196a613d6101cf9502231cd05dbbfab5812185cdc37abd51407c26a766ace8c46dbc43d287ab0f178e208dd3975853bea23223bf742c49388d42cfff2e7177d28e260cf8378cbad06a75c68e9df0ea6479333f35bb397820ac12047e9394382e89644c51433c921b4bf48e4528dc47f175658402bc3b26c0a553a5a615d6f25970af7debf14b717831479e399a25d88280a2bc9db5693cd1ce74332357af2c2237c6ec71557990cf984b3c76971cfa9919beaaece107140e912c54272fb710e9b81c7e37efd27d4099f15d1ac28d3b69325e40cba3d4a3a19ded3fd2e31dee35fa2c87517f118b8f587fbac4f772092c641183dc1e6d2296de8f9a8736af014ff4bddb1527172af22e564eaeb113534302a9b7c0ea3672d8bbf53f12ad4eb8a4153b7e289ca91a66298d10209dbb47dae92718079103eb5cce02eb53e208b4d3902cefc9f2780f5e85cdd00754dadeaf9df18275c8703a602ef68827b521063381087213eb3b63a77906db67ca0fa797376650e9fa1e66f7802d7529a154de8ba28d7100cdc427b01830401640c63176486803b593a7ca2e95d98cd9b8c3a7f50800de9cf10e1303765e971cab953737ee9457766dc8c98e694fdce30bdf0abec527c21813a065e0fe5f2ad4ff4030bb1a6ef485921cfbac8f109ef58b2d284e97c9872e0606c5319e78a0c8eb2f675b19172b604cfe52f70ddf3a3aa323d1f0438b6475ba88559ad0f26db4f83c318728331d2c0d0f095ec078a498d5e082a34a008e7a47ff515d4d7e88f4270dfb66bf45d013ea6019e1f29ada4893d213aba1fb48c863c5be505e1ab19e29a81bfb9eebe281ac778b41d53019964cf0c8f735a6fc634caf8ce4940dd767ac860a14915b4a4ead0a9d5777742bc2588d92c1a4b0cdfb9bd12e1faf9c3068be90fe3ad020233901a1bbc29f952773838560f78c5dcc6eff2d914aca5cdf0d336444ec1cca5656dad7e2833326098041b28becfa9a6b3c9c9e99191630e885b68cef7dc003db52a670831e20f106e8090a6c942eb6b30eaaeb9ad0f2212d2d4c5fb53f05a1df46968303b8acf55a92b4bb33d4cec275d9ded49f53d76e813dde1d13d861372a85316a2c8f5c791a8a0eb2e04e044a4b35265c7a09a7f3266c05813f8d4162490d1d47c55d3a708120d36001572fabd8c2040043de8de891e086787ed11d7e3118432b81248501c611d2f91c63967a0ff48f96d765449f43c41dc70c2eda1e03796c04aa47b1dd7c64905bd036124b73a1a01942f034a2816705a20cec24a079d0e0e5889797419f8857ce0caafe084fe7efef64f02aeb1a0fcb1bbfb9785e5cc2173fd6bea4a6868dc4970b01b3991e4630360a1a68aa26781203857e92328f8b2732fbfc2892e5a9384ecbd09917b6f99524a017606c6069f07301ab087ed0b3d8c46c71c06fb5dd8af9922ab4648fb2c0c464545a689f0b6e73cee7b37f76eebf6411d2deebd754792adf73850476b2bf2afcc3db3bec592d2ffb503ed64dd386e999979b9df4dcc31eb5e72b3e3e4b6bcedca5d76264f8ad7e2b28cf7babbdb84ca7d3310ae1c87f35c50c7bcf541ff481abf5e823a80d0fa7ddff77d6394202b2716bd1e7ca06e105e0fbefe0b9d66d8fc8d38ca7af3deac7d5a149a821eb984b8c7d98ad439e158cbd97e07f79bc838af818cae55192152570dc41bdc01ce03794e4fe388b38ed1b709698b2042c7663c6fa1bc234884116684b379a8283bd6e2eb15b6de157eebfb40d60ec1016bd6aca9df4356a18ec140e77793e3faa863fe43ce718f21a1a1203ed4c3879b99c160b01b1572daa5a66da0db2d116159a8382e95081d5be25f5187c728c57106b50f6f36e2bc7931c88af66e906f06acfe2c8ddf95f002b8f274c1c2dd19e1ee06ea60673292f1bdc10f073fe3c759c1dfe620dc209c35cfab47a4c609c75ac529ea5e46b8cf20ce7b2cea046de57ec7564eac57b5da85a390da79ce6b50ab5d38a4a96a10226d354774258ee3e277137ec76c1e66f36df3b2b109b7a76108641da72b1c259871c768a7fa6f22344f7f10ebe78f3b25c214833a26d475b0eeb8eb1e369bb339e7d2d2d2d24e69d0e0557bad321fe9139c68121284d5faa2edd71be2faeffb8ffce77b90e82988ffb87ec68ed683398b92415d19f7ca674b73893a42e0f731336f9be46dacd55d21495c24240297f6dbdbdbdb3b7e902b9811d1d155ced1ffff1191d11591d111d1118d2b222b23ab23242ba2abffabbfba6a6af567b5becdbdbf90ff74ef2f9d26cf28deebdedeebeeee2ed4834aa8c790d090081d731ad5ddbdddbdfdfd3bce7b73e2511775ea15cda9fcbf8d5f7120a11b55e576524a29e5a6c43a956ce2254c78093731efaee4f52e2ec5853ee552de45caa7a438eaf0e8ca75abbeb436fa39b19f78894f6dbc2ec5719ce4bc4bc7fab940718182e202d53e7e2309da715f4f61bed257bafb4a3ff5d6577ab7aff4e852cea5a7b47371a9daef5cfae98979093bb804a8b31fea2c8b65ebd741e9b3715f7a72cbb1c96df36df3ddb027078e179879977777993f6e4de16f3b96cbd97706008202712c5227974b8c798cfb421dcec7d61bc12ac76df5852e1708be5cf45f0f12515104855cffbd055e50d5ffdbda2190bdfa00ac8e44d508567d3a4febbfb025c63a26c17069b2f691f102b8f77751d631e9524a96df8345dcb7604644232f77e0bd371ac1aa7f91ffe621b77307fedcc1b666cd237f630be6e8a494524a29e54c4a8ee38d67409c434ac9cc2ca54b295d82e1eec8d9aea71bb57778f5df9ddee716c0b5fbdd9ded8dcaeda2cc9dfd061054b2dcb93d3467b3d96ce5dcf9a3632c472661c666aaa4ff8edcf9bb33da34d55e1b26364c5d68b3c48689894df75db7dc87a92b72d5d77eb67af06b34ae13bb8fdb3eb146db7e47095de7e96c986c98fa6d96b44fd77e1b26edc33d94f378d0e07fcfedf7180079691d03deaf8d5e9b250c6645aeea585bcd5f1b26ab662bae623055555673bbadb2b26152c554bbbbd4fee72aae62ae6271a45572da34b18e903110bd632bf752284804111aa0f4b4d980c93eeb9352e729b57989eb00d4d0a11fdcdcdc1d827d399f96fce8ad92b40db776770f394fe380c201f918faf1a3bd7d0ef5d00fdf6d877eac94de54f287aaaaaab68a2e7933786b02ba4b4421b50498244b44b1c493845aaf4381a5b5742cc04bce2354c4799a4a0f5108159d0974db0bd98488cf494bebbed3d9664645384989938894ba93b6efcda527291d2529b54f7f23b50f735292f75c62ee095bff35eb6b75c3d66f61eb39dc9698c35b16b78502b87f76ace7fc5100cd975efed6221dd67ad0628eb5d1d3c2fe30c08789238ea8e35a8d33080630604e3a6f4441f7d9a348d81f44cb85c8d7776eef4a4d79b7b856f388d971bbbbbb1b14192ccb56e8a945fc2c1652d75638bba05c59217011cb4d8787fff63bfc37f14be0ba7d8be5e489b2f6d39eee39668e7997777799d959dd7d315708e7dd7b55dde7362d74dbe64686762fb9d9719213e3ecb22ad589f540cd12c511c963dc0e94fbc989cde3ef5372322b590fc7a26cb63377776eb9979739e6ee2198a8edde1d7925d5e6bc59adefffd13e45435e0ffe102a3d5eaf44e442eb01abbeb8a9d1ec0d2f5147c8f53a3dc0e787f5ae580436570fc76710f6bfd043d6ef58ab9b8f1f2404394fbf2b5c5f5a2bdcba81e1d2380c8184f56cdae250f71c9d1db7df064d7cb0e14c165b48e0e9f06f875f980026ba28740143112c44b658d17ea491210a244e50c1428ab6bbbf3b2b16c946e06dfdf96744421d5f013150231288c410c8fa5550f0a32cca62ef14e6d36538509721e054ff8fe61dc3f232ffa9c1b0c4952f2be06206633cd1e1b483982564b0c882865653a18a2f4fb0e041064fd4508226222f3c3922451854b419e5a7088d245ad338819600261a441762b6e080e50c184cd0988b344c38a104102b64c181c64ff9616728fdb9aa5123e66ddf7ae0aa2da41631ebbdfe9e7edf16bad5f1ab2d2b8f0c02f7db3196c8485db79caed20cfd3acafc67042838810aa8a622a0b8d0fa480bb8d0c10827507ca8422380132f0051e5042c663852c322832d686090064a6a0b5ab7b02db1de262c42c5c56158cf51fd7ddf5b3575233f75cb49df7fdffff8be0fcd4ec16654169b7317e8fbdfef0b53e78ffe048a5dd09738caea4f234bf94f6c7958cf2c66fd93047538d9bcc7415201a66a807a7dfc7ddf074ae0eb3854c16729a6627e96ea1fd7127701c3cf55e0547e36c3cf67180d3fab797ddff77d3bbe020942bfd7a1f4f59ea57f8a482b1ac8742153f9fda97f6066fc0a97f689ea9f22ae27a929aaf62940e57726da67a768a8a2f27b9583e177ab7e7ddff741d19daa21aa82ff1564a9b5c1ef74f3389628283a293ded46e4a9e6e167d5fe1677e1a98eb1c8524bf83d2f64fde7f1671336d1bef06babd562ed0f50982a4f5078028a48175aeb61fb53c3a2e5892b544e38796181d6faddd6cf6db5586a4a3e5556aa1c35fad3d26b8bb73b5c1b88104e87e33677777777afdc26a5949243e81a0a0f067bd9a572a1f8046842c7492985edc872d088f5cca2a4a6e6d756f8b382ca2fa14770c94356094a406ee309bc4ad9dcebbdb1ec96524a29d5c8624b3ce9aa8eb09a43b7d0f1cbc9c9a111eb01fa1ab31c75939aeaf2514f3ab2b31ef4ce7ad073c2601f4257c55d1b1c182d98923aae733f41959dc81b73d93d1776389a50e57bb79d2f27a5d34f203afc41e0da75864edc3327d203284924a49414f6b3a9e3caa9a0fdc736362908b10a44486d1a474971773717326da9b8ddca92e4a8e6cc82facb2072673b5277777372e5dc29a463dd494a3f545463ae6c6826b01e4c0007116e9aedcc646e1b9a202ba7f49d1cc599c9fcdcfd83a0e91fda37b6aae3a4b02db3937c93ce56a2575757bd396feedefdb3997298ba99e655fbdc52904bce5445d5239bc9a44b297b65cbf6ee656e666ea6f22ec6e872d8a0389396dd76767777e666e66626306a7b671f0005f1524aa95ca00f82a40771d36eee6eee4f06e62a66287a7aa89a28a52abb6c25c7706f5fdf755f77f75d1f327777676e666e9641c6d42d5f61dadddd999b999bc5f897deddcccdcccd4a5d0e5b0e1b949c9c28e666e6e628ff6226c70c6856038837de80663ccb0104d4fc838804a3a195ddddcd4ae29b8cf5b43337b73333333373f3f7ccdc67de2ceb61667ee666e666217677767c7e999b999ba19ab9a7999bff6689282c6f000ba827a530d8733f1537594a4a4a3a41842112a42c555e30959939acc011abd0dd7d3b1c6b9595462f9ccecccccc5eb8340a7b1a2f6d99e536039ab5ec6eef5eb940ebfcdcc44fed33c5dddd9bf9caecefe99939b8e1856ebf1c333333333333d32a951b0c0598e1967f179662f1144b318b99ac074d888efce7fb21b8c0b4502a95a5384b49b94230fcc239596a3d70d120436db8c5023d10f4c0f97d727aa0f716a0a2055e22fdef87bcde0543c3521cc4feb013555cc553e4aad70ff99efe10d7bfdee63dc926943af37bbcde254e80d2773d2854c4c81b112a62848a6fe4e5a22b240a127356622e43688151a92e859754ff25d63318a3b19fc53a62f91fb9b39ce52c3f2232ba22323a6239d191134919491d214931d1552c7615bbba32d3d566db9cf18de2a66fc7519ad6eac9c7faae8be2908feead1a5a1bfdcb8469a8aa4989a9a9a9a949a98ababbbbbb2b24a9c8900f1264b1a029dfb068f367f0c6dbc25148dd18673f676b759b7227168bc5aa96ccaaa9e289a2ac6f89e34769b38f2ae67e4355357c0cfda88afd686a6a6afa31e4a36ac8c78fa11fb2aa213a54d5d454d5545515f56406a7e22691f3152ecb1533504657b25c11b50fd7c9f302dafa11666333250ad1150765cc983ce3f5325e49aa06dfde0fec6703193fceea72fd06212c9c394fff109521ca9b1ba9c1cdb66d9bfb2671723c1cd2547342a4ade2c88e754c36a3fbcd689853c7bde2244cc4f302da12c79c3ac258221b9d214222c7eeaecf7aebadb72322a32b22a3a38de808e88ae8a80a8634447475c5af5c5df12baa9c76f5e01dd7c3711c3767cc0705fe31ffe997f5ca8de5ca1613ee9a767b3d90dfb4eeee0470de0e0e30a107c3190830d8149b2abe48b2aecc49be6c1f9fbc62c454af3fde9f3d62a93ff091a4f657c1576ea8e39734039719a254b0d2967d3622b5dddd7f47573faa4872c78aa9fccbe2ec41c5c5759e6f9e5e2c7e36f00517637e50a3c44c0e34116490020c9e9c5c184384d6dd09e12d6884bfe4c26f19946119ea5cf77d044f32b7cfbdb75dd7dd89e37e095cabfe5be8bf4ef3739a879f73243fed580f3925f0f085081b64a0449627de8a0c284c31b28408121affc73aea0af0882da4d3e34046163a7eed35b46d3ca1f259efcded37bcd0af1068e2c80a585600420b18224823a587291da86471c56374023fd1041a95222307a3055392d12691821924d5297ec00324af45ad47f2cacf7a8f7545b9b65aecbd7b21ccdfdfb13f45afde6cfd0c29d2563b9146c76037b2f4cf723a27e784102011f58220d094b1020a9a16a0b4900393941453b4feef8fcd1cbc0d8810657444924febb675635ffefdd83edd87790e9efc1cf187068df6019db675031441c7a7f265fc33fe8fb511bb321668095e8bbc56e76ddb37df6bb55ce1f69ef75f0886e128a4ee17b63644eafaad19ce1008af59b3664d1d6b3b1dc3d1847e182cb268ea210c2d5b90d0d806344d5e70e5e58a16a0a0f1efe46d89fbf847e0d1cbe258a08ec971dc823f591c377fe4388e9b1cab9bb3637120f8afdf401d2f8f2b70a47a0fba7a74ccfb97f7ae1f5f2fef5fe2f8d5f5bcfb7abdc4d1f5e07b2e7e90ff63ef85da07fcef5de02734f48922748cff8c0f24fe39619b6fe1c8bfa20e6ce44aa363fc3ffca7264586a51f5e4015059927b415e3258a20c8a4a08b2d41d0f885865e4c7df076677bfe20fe23dfdb429dfded972643eee55b60fe268a4358defe707025d603a48495a5de238e5279876c3f5f864358dfc9e71e03db4f2a3db69fa2cefe76e110f99ca8b3535362a254198105c90c34580c473811c55496a81a36c949de720b6f777eb2a0bf6458e92888d1a46f01f92e69cfdfefd109a30fdb96a580fe02a1448b105da6f8f2250bad5df01286852d56d8220634b4fe9d1cd2b5c1b28bc7bf3bbbcb01c93d776d34a089d7d33ef29b71fbd7d33e630ed9e21c620c949835582001838d160c41e405342f8471268c8ef1bb4f9ffdb0feed9f34785e2d567999fbf34f4a969b4b87476571bc409d5478511d7b4971cb6ddb36261cd74d281db08d4b8df23c5007ec0375c068c7c23147060d74a4513aa4ead60c10857654ddb1355597353b6e73b9d8b5325c634d87c78c6a535b1c3950abeafeb6ed6fe08e29ab368f95a3a3037774386e97ba3fbdf975c88271abd5dacdb5dcc148ebb2be1b26ca0277b0f65bddddddb52cbcd4f23e0ffcc0d981dc06822028416f061700aeb104d5c558cf00ced0b1a767264ed5fd761850314945ea6a05161fa05861460c1a922e5005981f946c40e108171afff24f0f8a97b5254fb43843268c24aa2fec059429767082a48b134dde4b4b2dcbd392192c6a564822a586e40a27a92d62c084164c30e1f18eae085042796e800a628a181958e9729454a5062952383c01066f1391ed7fe5d8fe6cdc921953a5091d9a7ce142191d4d5990542951418c20ba703acf3eb785b15026b4dc0d03f7961c6cebe742202600e169002eecc50644f0c3f8772af175abbc3efc5ceb6cc8957a3713c6a16652e69f302b74a4d5a351a37d3a07b6f3de0642d0ad11896246490a5f9cc810d1419a224ab64461815314099e92445d7183164c98a868d2e187309ea4989cf4f69649c117464ca1e0490d5d9ea460050baa389284122b767b9c2a5d1d21f960dd2a501b4183126084704a228ca9143ce143154638a912a6055778bb138018c48821c45598316ad4d440eb2d549254493ac207275a3fed87d552f0c518576bb48872a18b23be70a2a4431659ce3cc10fe35ffe6924db38e92648d92c003228a022713765b438524493224ba664d4d3121ec43c4963c57700013f8c7f8d02a0258c142a294d588185c65310d14107196e1023860f788f04172829446145ca1315377021a50b162c5439020a8d7ff927087a7bc5f840d5a62c59e2062d5fbc508184c62f043106922bae4c4186c64f74b1a4872d569044191a077420a287215cd04313332378dd401ff0d48255172546a831c2a40523a42873050e52a468698a8c918598334f10e1c50601c6952d479ac8600505bc827eeed25dc3c59d5814dd8979b7387906cc56fb3ce55cf6596aca3e6799edee9252dddd5d3064ea7e10333533e128fb41fc6769483dba59a7dbe7fb20f863fcf4841a961fbc3c89210517525c416315d668e1c40718be709ab2544b42010a199ac4acc0c8921d226298a1082d509e589841c50853154d46b48c792985c1a40417279eecc74a388a326286c9551229581ce550068a1454584a03f52f3069a18b28b640edd06450a024c91054656e48a28b24351c69c2821ec2c812e64bd31543249d01631f26822f42f0b0f4e504235fca487169b282253088a85014802c505145a001e2c9548d19b84085252e3481839a08745133450ba817c028e322d2650b31495ee02109c9db9d1b52e853d8cecf80b73bdd228d3acf972d49a89c547171a40c8dc348618111399051d3021affc754b03e6441424a150d2ee49006cb155140d952658c9125bb33fd0067c600428c92169840e28126b808838553920d6368cc636dcb115f5459192106aa0c6d9fa47ce143176028a939523bf2c58814667488a2660a087c214673b382b739e79ccf2cba5027ee079c92a8c9e2b6fd722a7fcf4fe37bfffd3c4f6c85e3c60a61a10ef7b272393424751d763bc9fdb6cfedae381a75dceeee6eb76d5be77289305fd1fbc0b592f56cdf77ad70e6748c5920277e40b62b8ecbbd64715dd775ddfc6eeb58531c856a2716ed17c9e7b871c551268ebb2f7138912bbf9617dd73674ef798b3c3fecdf9b27f766e658923eb65f040a728c59f322350fb6c536edb16823feb77f8d7ea9cf2b9ef6ab5b995a253eb5b22ac73b834deb6beca45f337ecde2bda9145832b771d0b74a4b0f6e11c1a53dc7eacd52eecc465fe183fe587455dada0440b2f62e800c50c3440260a891a94c240620cd7b1621caca1deb736f8773d0e8ca0523fb27938f042f74129b21ea8d78614747c56a8d3fdac21ee58b975415f62bb2d38d5c6c1843907b908f44154f7412c96330deee06954ff01721abc80c951fe63d79146e54dfcb5116b1e5e40b81dccdaaa31b9899db8e2071deb9f951381b06c6278b11a3bfe3dd6aa8f22a8cd108a582c166b6eb5eacfb933bb151878e26824abadefede9f75a40b87adf216f6fc7de09fa39727c3ccef13f719be7f33acf6bbdb3def33ccf33a1cef7c271639a0f84eb94eb846a200aa613d24c09838688da0c2c5cb1548507335167683c0417303b80015565cc0f566871a40a19682073048b9d408045d07f193dc810f6638b300f8b7a47fff61ef6cb20374e375228ffd651264324affddb07997256242fea9723b408ebd8263ee730a6b1b34edbc1b1c28e0c2251377421051a158cd927beec30a6ca1a29860043e397cd2467b5edf7e8d80e756cbfc7898ecbc450057969bb3a1368dac837ce00ed07edcbf667564c54a1a1d3cfb57f96a6d3e20d2ff667fbb117a868480fc012f9fec926fb407f7660bccca596048929c67c21c30a338c41e3c00b0c2a38a1050a0c5068fcb2a82bc87891821037d0f809258c40c19234505a20a1f1376f5b3c6e652c507e0eb61b08fcdb6fdcf672ebba35ddfc1dd299dbbe0bb76d73626db2eec4d1e4fb63dd4df41052eb21a4b6925ba2dbcbbaedcbae464ceee060429f99e592c7624fc7589664ec798e2b92dfcf8d30cad2c89652226dd2fbe5f6344bffe1e62bde7f826401f5c316458e58c1131aa350c4911c7870c116595841e3a7b01bbcddd9b6ffba3ecebf6d329c2f9bedff7e2c0787d2bae0440a1469b834c9c2a5823552699cc68c1185b6fffbb16d2d56e07e7580424340419e8321703be35e5c1407f6047d5e8bee7bae3008e883200ed4b11b080205f1ecb69f9fc85a66392b1c69e8a1bf0cb75f50023a3c44252a340414d48de1bdc422718bf8a558b4bd73918b5fc7645b282bf1605668cf2cc707e2a42c67f375b8dfbab1bef55ef87d32de1bb9e6fcfcdac7f594fecdd3df7a238e37efddc8e081be7ec11df4666c7923787f234e2aee8da820d90cc92b9511ee84b1cfc1019148aa5f77cacb14551cb9de6cf0a3f3b0dffee1e05daf136ef3b87e9b07268eb3e2fc06e1368f4bd4e161f32e7194f1fb337e411034b879ef46748523ad2efa74a76335e88f3b35d631c6619c671c9b1f6f5ec6dbfc5617cebb705ec6cf9811da841a843a2180bf0ffe3e0ef8f4bdfe70c21a3838e288e4b21d282f28819b1b1d1e383feebb1ee7b7d2d75397b81dcb11c76fa76bb86edef52008e2e0e0fc0eef71be566938ca4a5f37378f436f429b5f706b756d5c4f5dff0ac7a3eaea1e0ce7b7c29182f040eeb1c211492e272e8e16d0070a92cd26ac41229ec7157122d2c6ed76ee2292ac45dcbb3fc771727fecdf8ab697dbcbe7c4227f1edbff2616f5fbfbfb161b07b3425f06e4bd42da31df699fad3de04bfcb6822028e6746c0321e83873e6e751013e17debcbe7d3c195c28cef7b76470a1e0f7b7fef3e9e6013ece4d48c3d9b1d74b1cc16f89db311b719c558e00bef75e2a17e654cf1b5f620876b45ec73e0dc7a34adfe33e2e348013753d0d912475895bc4a247bfdfc2223a02158b582cd2e1f15b59dc8e7defbd0e4fdcca5578e158abde37a8c3a3ef51512784d6d3dfd17afab50a86a3aca07c815cab1c3215dcc1f76028c3f1a87eac6f85932580ba853b758452951df316d0af32ff993b950db39c6c5d8d36fdc748cd488d266b349d0970df7d0fce5f480ff99b28a4a1bcdf80887e6eb6473989be2c8aca0a4890ea3f9ad055174711d4ba6e72ca6240b777b1c8fff37fcff9b21591eb3372d5f1ef97bf5289929084152f447a28e2c51ada4e91840bcc34e1248c1c54a05dc089195018430542a4608596e5cb093a8c41c60b2824fedcef906d08951edbcb1fd23d063ab131b089e2d2a89a072f3d90a99dfc8d7b3f43b9f27b7b91fceebb0fe1bd7b2e44f2dac91a1d63b9d301276bb8585305c50a62a0c4b0a58c0d2e88c1520cb2c98e86db3fdcf981b6079dbfa8848ef7a31a2a44368380200123150000180c080503029168401c85a2b47d140010818c426c48164aa46116a3414a39030c01048008008800c8400dd20e70b7cac5c7e5b16ac6b9267016a00a732f52813eefd2ef8b07dc574cb4c391fa7652eb888e1dccee86410de60448850f8985175dd12e9b92d863c5d74038a3537bdc79bbdc00175d456b5c962c5c740fc256a98a8c15a78e5a0a6665e438152e18b110b22dfe9f7bdcc36c5e191552ba22a4f8e30abc8ccc2b929aa3a52e93c5221815b61968575457534155ce6b6986c7e24d483580e06dba886dae928b07cb85c73e17c351cb68c6bff58ab1b177fb2da9041292fee536f41b52e9192b80bd4ba93669322afb5f086f60bdc512aa568180445959940cbeac1d75f984b415832cb4a318b1a36a1b8950aa385262d5133d247f0978febc704a15d5e750a876941850159381d1d3efef4ad6a0d9b3aada1e61c42c6262b6a54c00bfd406bced2630a009b344d20ff4d1b76a5d7abd7c66746458c9da96985dc445f7c5705f19f2c8360586ad890bc0dc93caeb6ca77f42a1ac90502452da5d4a38acc5f32f344dabdd573a257235856139fc90c093ff93e04756c587d817c2fbf52ed2e082360f78336c6379cea95c1338d2e152cef68c081ca9a836cc405fd2dfb2eac3698ed46e448ba232c1aeb372fbb0945f2f3d672f768c49f90715b2abc4a69c621321689bfe6e368c200d6ce8aa0155ffa746137b99a6c356064c46b6d27a7bdf077bd86497bfaaca51adbcbca73713174fdaf03ffe8b164966916c5996cc68e5e5de61fc10237467f7276f4ad2aa7712a703253398c8d8bea52beac0a95ff9f6aab53e49b749453681a2b26f4831510b7104d376c1ce986383287d53ce7228877e5b20365516e53c1ddd56d9ee6e1b9c515276befaaad007a4395968d79b4f8b50cf2ad7c5d522cc45296baf521ec46059c7de6e47bc6fa10cc761e4495df9ae38a6ace7ce312455cdbce62abffc65433b4a28bdd87947bed0a59c53dd9851efdd1e1d2a334af18597d06e1359d32cdd11e5a3c2575f21344b824e0b11fc6cfa45f30a4625a79fd92f7afffba66267c2be148448f11ed9bd0d5095aa7d4f7984e6b306e603a7a6f8048a2ca83152e2eb403418384b089d62889bac320a9401efc6be5f7c693db4687f320ebc3eb40d0bcf5c56c0aca02ca19170e760af31d1d7458db45f22cd77d1d362d4fe459cacaa0a574cb71a82d982ef0174cfe9a43b0b8771947ac4614b9b79d41fb74b50a99fe86189269943ba98cc225de0cd912eca26df426ee1983e10f6cd831a22533f28f9f8249ae60e1cdc843f9252fd484ad406555ada5160c010acd2bba7d030dd784755ca7cf23eda3d704b70e438626cbfbad6b0d216fb5b3da91730bcd37b5d5509c6f209857ef1e3c65cb6e89596efa05633fb585646c731607a6ab107eca266df8e41e27fd113756d124f9984c973ce19247b35c204390d149b5f23b5731e5ae0352a8d466934d519ebe5fd2bdda131d8f7372dc352b6d2bd86d8ca154a2e065a63ca08b72c03a90bb83405ecf71d7b39b78a2b42f92741feec1aa467b597985e28fae05aa84ce1da125d0975a6fc941aa8a9edc4db05c9845655d79e5fbb5fb2ad13086d5d4d412d896e92daf3d735032223365175e7d369fef150c72482f1396ed8231eee73b0d1886d50c8ac8374d9c98e260d85268ab2fe0c5c30ad85d60591f44fbe1232f95308eef0e3882bb8b2926073bffafabc1ac482eb9ab5c44df81a1fd5c16fa649150dfb6e25e00c22d026e284aa7e0d847a4f5db78c0140819539403ce8045b472b61f50557cec9746fdda4b4bf67f09fb61b1f6de43955f3fe58217135a06022dea638442694237e270d3c35f2289f34db282f8a020faafb761e9c10e3717956677eb5ebc5c0d2aca1d100257a471d8ea3c3d2a4164433dc20086eabe5b7aaa751a032c0eacd0167555489c6ca510b8626d40a9a2c143f026de9fa1d71371dfb2ee113894558d828173e6e6fa117cde76fc845a887ea0305f9fe523fda27790da90daafaa911b0d43628bf0381963f446b2a09f21cf819c8c62b79784e4ccea26500c73617f3a2c585f3b52af09a28ae9b21ed24614c2c2093a0b31953cf8c06ab51c6d338bb70313e00a61597d9014f837e464f75efbae1a2167a58cf9f6a92eea369d46483d6fff3f547c611154a7efa8fd4c26a036524175692f533ce980a95b28bc6318d81311de5c116d7d590be9e3337ee3cd2715de86f8426c20b3abe31d5fb47935f2c1dd57a47ff197c0c88fb87340193d564997ebf892bde409615084d87bb88114daed9081e124f70bd316ad41782856e0de16a24296570d33fd016bfd23924cc1a31ffbc8c1f0cadd586ed1ae3e1b8cd7ea1b7bdceca7b886cc8f87fd4f8a9b79ea5d8098e6c5e8f8cae319d92c7ef6e5d9372ce86110f3c63e8a744f1059ab6354eb55cf9456aa01ced2e36303872b00eaa6559472f4f2a6412bb98b75561be11da102114990c1c59356de57ebb9966b22bdbd63ec618d816270de5dbc089a66a859ebf6a65fdac6460f70fc1c6dbdb3cebbc74b42eac7462f6b5ac79769db233923471134404189578cd762e53228d37c11f54e3a51eaf9ed584c7d0fab13e8362e60347544ade70ad32410597e6ad4cef6f9b2d6b9495b6bc51c5bc7d16c0755f0ecef11b5e5a83ae20f39a42f2c61457f8b66037b8695c74b1cb1061b454aa5d0c3f71d1e3d24524a06aad67afd4f1011ef831e7152a1a9342c20ce652fbc1d182d1dca6f6d120af82f115fafdaedad776abcc8e9ae7fdb3fe47778f838ff225166aeeb7b5cdece2b383542a4745e7d9985431270123aba27416bf0c68f4b017e5958cc9597f991f914307bae7624339e87d54d5052634743cee9475694a8b8230fca6b2ac5498c75cd07cf6d7a5e54c3ef528c4687c78dfcce05d0d64abeb4e7aa0b9d60e0c5af20c3b3fdd8be7a73d8cfba11259a6e8748d0333764c0fac2a8e1a7135abd16a4a8b652d10e52c52017fbe5727f86ed8c329f76458943dbc5c6fff8e85e008befc058ce8ebbd56774dd1b8368d11eb05b0b5f70ca0495117b9d34467812b92ba20edda4558ce6b06c94af168c816ae2554c426fe1b7aaa53a7b771a687d2da74351f76a2b712ca6a0065d6c6cbb0674b0b53628877da827c23bb3b48339123d715efc8b062262f76fa70d6d4744ddc82b7d50b3d527b4399d6bbe98689ea7133e2857dd6e97e80e6ab9ad27715e6a9f579d7ecbc848a5b4182d7a2f5aaba71bab1a914846c2ff1a831418b21a9a8e786a8d7e81fef410aeafef6769cde4e9b11b89a68c19c09dc25a1c4a98eb57ecae9addcc2313bcfa9f0dcbb5aef84780074ff76ae6e19a2a79eb75294463ade551610897dac7046a4e36905ae49d9f6d27160becafc1a957a2e2969121b5e4db194003dcbacae428ee4f1cea94768c566e2404ccd188e85f7c9037f71ad0590da8fb0839bbc6c385e77e930cbac9f0ecc026b6db8eb0860fd6ee07f18be63b9001f8e884f1448cded68309de0c507a631bdfa4875b305937b77524dce4b59dbaf827146f0f5b826fe07244a3f9df9e468e531b075c81cd95052d602a958911978c45dfb5d44455bbe02cd1507d49bbf280402e1539cd5ab958f3f006c6115a548d2ad9bc07431d2b518829531afa589f2bb05562e261250ac3432ff0b31db06fe40d4a7172b4383d17ab44f096a5bb6c5523ec5901e2af79a441bba5d4d78a3738c6ba447d60954e00c93ccda65199630675c03100f62ef9c3aa4fb75efff123c0d8194712d8e916d4b1b5367655180661b20770017ccb7658d65054fc6ca689663f19b6bb49ff363e0824db998db9407ecbd8280a710e6dc2efdb4d349dce54c7f36b0425f9b2d3e785e881e6b5007ac28ff51285646b1fa8d53beda19f562cb9a831f1ab567c9e683dec90551e0d0d512039705ab70f7951a15a388bcd552dd94c884ed3ac60bfab60231c6db1dca227d67a2d65624f4db647c2d871dcc6aff4c7a1d9bd71da2d48033dd614912f1a045acf97c6e667414ae7284d7ce5ba9e86965ba83cb25895638c8a56cf823c0581bcbaabea49b7ec5996e2ac9815c8e1a0cc7a8c03f554ec9b28c35697a8036cff7e24b874a973c7970fcc7cf2c0380c9f2be9e5c42a0934945eab2f4479d438c8cf2545b165afa1289943c05a2944d30e65100323ff7f2f355e25fdd2415c0b9a2a13a5e43f83d3302b0fde4d2de851d89e04daa38f4e3b8bc45cb486d1093ea4553bb1d1a7a839bb65dcd7490144120126a4f30f9cb3b270c8ec7aef71630a7138f856cfedf19780983ba585a8e813faa331c91e0adca3a68107d09383b8029684102dd96cae36adbe33af974a0872534088418f979bdecfabec46500ce62d10e50b16da148cf0d4d91220e78b77a5b3ba4e33eec261a8f4a009a1719e468bb4ae27b7ae758d8aa18c8eeb64d379191c50141b16aee47d5b6b7073da95ccda5532e2b6ca5c22c150da580b5a552ab2f7192213c531362e1c57ca6830754f95aea5343456e1f0d629360744917462057bf25b13ff1c404867941de5413dc8d1ec74bb6d152037dbeb07f7a3e0c6e1661edf102a8dbbc1fa28eac2c832f2539831485857be5ced2f6b768cb4e35c70a2553f934c084cedcfec1b396a8fa6d616a0e7360360e4bfdf7f814dc3cf99ebec478a180503098b9c2f6c9117bdff0431f29677db0377b13397b765565044f60da13883f59b15c37f856bd166e8bec06112e2fac46f6c81f89a666fe8835ecee5746950eabfff856c1f98b67ae7d76d0325c637c77f4aeb5078505c5be4e9035c725f64997187c3705e17058a9d4cb2b3f2a759d1e2920a1c6f1e15d052d1c40c25dff5b27198ea7948950f14e648634c7b714c26d0c4dd057091caaab282c1329b63f49d92dcd855ef60fdb4f90321feda8fa9969f5e709e1aabcbc1f898593f64334bb04bc712c55093f749d5df0f5028d0513704cb8091d5b70ff676384108decbf64824792299e97a71915438b889bfa0ed85ba558d9f8142f3dcd0b2506486ba83c8a7c288d2e49b63194ef7ee77f35efc44412c4ce451f29ef0f50c94b95ab1f62a2b4fa35a629a29e070d21b2f4812a7f6db8a1465731e62ccb589eb803b3df9b15f29d684e5cedcb48bec4701c320b776036bbcdbc02b22687bad9585995c396327c62c59968127d6d8b6ea01cb690ad9121842c105cb2747d016fb3c8af87369678bafc0411cdd6af9174153078417ca9ea4ca0fc8d59545d41cec6ccdda3a3a74578c63c192423e8543785c845f1184078685f09888889d7ef351ac70688e539f24bd570202d7fc48851b64e87844959f72b533188211ab6c4478b4a83f8e61c60e1d136f6631acd51c8734dccd221eea809bb3dace0aa92621aef8e459b76d2f2956c63719a60490bae9702f5be136b45e7a42e978cbad7d803a50d53ab66f030bf0903700eaf1c58ed2c7ef6912b0d12ff1b426a8a603dadd78518cd6ffed315171f2bcc054c7cd32f991fd1d8b4973ed1dbcb71992048a51d805c3a32415517c1e1f1c74b0ef3c15772850419ad3ed1f47c28cff7b3800d5799ae91468fa8905b4f4324a78f1a588eb182f3256b80cb92bffb8b8cdd3ade8df81dee1704f56096c765f9dad9c4e0bf5bd37524d71e12d3da133a93fee2064813a05429acc4324390eca194725c3a5e9fe16b3a0c4254c50a74b624c82bc6c944d5d2a13731041ab7e35115910c94658820fe6ab31c25973a6503cedda15710cec55506843213fd3e1f28b3342806dc42415401abd064dd01f6641b62473d60a91c846c39f42333d1560d662a84886a8655a34283ecd6018f106b5219590db645e4c682d694d87c5cf941ff6302cba872506715379c5ce4861dba3767ea247bc6d634ef416d65550c6cc0d90378706d7861a9b73829d7b1574665ef7600a757d2172aecaafc51ef78dddad6e5da2071b32b5f40396eba9ece6700e301d0d01641221746e8f334c6f253f8fc8c0b688cd7c5f73279d7cd22e05264ed424e443f0ceb1f4112fc8db91078ca37194b6224cbb849e38081d58bd221340e06d5912df301ebf093e3057b594477ae9751c4c61358bf526e3fa6126b95549f2d757cba9e257ff83fd9452ad0ae5d05ba2674881443d29bc5ded2070c60e6586e6bc2a7dc5ad437bcc191434eee60e93ad4251f5bafe090c3a1178e1f4f6b4b1368f331ec6cec8e7b6519f57c0f0a585f2e5ddb703fff10bc98a460f8853d08d2f8329cb845d8b9ee58e80f075d6009f9a1f7556522b4049878f52b7ebecaa1c74c1e199d46c91cd98c0f2733c2d6d30cc5713830f70017b77ebc3b078b2b1e53e98f1f157402301b9795d06085f7f77c876bc4316180f6a7189f4611669726b008ee84f197b5c71e7837e62e776e0d86e8999d0f8d3f97ecf91b9712d13a74d90a60eb713ea60a60743f600e2aca1172d0b2910e7091aa494e7ade4fdd6e62eea62576882a702be0b7812873381f9a7359d143bc91dc6ef1e95e89941e2356f1ccfc71c7d67a0b8609d9a247cbb4c1173debfe734b331ee66b5e8095f588dcc7494b61e8f5cfa47bf630cf2ecbc12e12cae4b944b25168214dc248912fde1b204b07c1d54cbfe01d303f5aa4c321cfeb98301334d1e5bedf12cb628f2fdcde27a6778d33f2709c66605bd070343ec047fef84018580692fd814b0c6765760e09f230303ee88996cfa8700a5dc61993e84d554272c676648355b946f3e3c6221213f65d54de6580bbe1163633ed82e20d845a8f90de17084bf1025141e4c0361a41ff0c9b7d463c6610738f2a9d8327dbfee74f2517da95309bf30f184b8e96ef7b835a6d9385d5c7176263a8c7a791d024d3817ac53578ece2d99ca449cad90696231d918d23822eb8310004e28d61eb2186168be09122f981ba491ae97536a7990e4a2c68ad9c9569144fbed18e41ff4fd625e298a4450dbbee6f342b6e9629d442c3214560fabb931cdc51e00f3ecd6ec8326b025439597d0ef4fa15f113af074d81496d96295ce9c6edcc251aeca9ed4e2077be0d215e2cf1370783f8c6edaf0652ff0efc9b7dbeb7e1312a780a35323f76478b676b626fa6f3302aba5222adacf41820f7f01c44e98d42a40ee12b52b475106948c14da5db3b3d591316b1963a4f1e75b00f8e28419263d7de38ca248cca1ee95d8b4775b459029589201a1bbe72402ea84ca1728b623732af6fe01fa0711abde6a650f912d5b0a871bb5f9376b74650937897c944bbc5e6b161ebd869c053a0043e81c755a9b317ddf2b9cb608b67aed16dfc508aa4e9d45e9f97d38c881d82e581534e6ddc6468f3220d75e10e7cfd0f428ca5f0a79ccc7229eac404dc3982eace869197e7540be81743bfc37881555c81e6a11591196a83e2c122d7d197dd0779a9775c45d15d79118c921171f27d4e35aa3333845a31149ca0eca5d11f2a22327863d922e53b3c20cd233fd18f558ca8b25304c4aa801728568d582fb511d6002c418878127010516c7eadc3b49d4e984a9b48557966ef8ef2046af07a7c725c431561812e02912cd09595d9eb11f827fcc0000ca1677fade4f04d8e98c3dc380c3cf32a86394cbed4b94618f6397062e9ef05878183046370fc757967dbc93593024095e2f25c933b0bed52be69cad5080bc2b194415dabd1ae60c5acb1878991f43c5b102c778e7ccb11b9bce5027c319f8851b8e5d7475d7c6ac90a29db7aa19c3efc13114296e3681e9d5a7f1f105caf2cd83ab360f0425520b729212f8c87d70d1c319662e3e74029f21671c6ec55794afbd1a7572cd07b1f7475ade1e927bc0cf402b9b3c09e6ad8b67677891a4533e136ed5303cef7c4a2b82ef00504215d66b1eded2b7365cd81a81a4cf3a7664222a7d51124000b781354d2c9c125c6a919c4ffbc6f0886d3e177327fdcaf59a1c8ecc9b6487f3c0612637b1db608552b169e36b171c09637dcf9729bfd1dc5608806d60a5fed581ef53967d759eaf4289e1779b1b2c111c41d2329dffb52ac6edc516f3af40a935c9fdaf121d8e3d283f6408927c6077ead2b261bb86547122ae558caf4be9575e2d28b35c4114bf8ebc3f9b9f90932f629a095a5989c00e65c3982cbeb1a9337802f4c5844e2990579fcee5224f506f7f02e042a9381a197da2a5799b54dc185d8e882c7a99ec919d0fb4a8b0ed999beba5860c2b335ed909b17b8fcea3fa0bc2278a30c1d96a04bda0d447d198e1a2fc10dd80d327f271564a3382a927bffd2ffae149a5a487dcf38ed3801568382db9867ee564d759abd61d774e77dc2bdd0b25599ec6332a52e53514ef62d304bbf7739d1e66d35f7bd62f444266e98ea5d42819b2d1701f32f138a1a8284c0841d7394d13fcfbd09702aac414c3b396935f8390ce1ed84c357f1820e9f3d7038c5ee8ab2f8367546bb5fc72e0574f43166a16237bda7d322e93fdcb76f995421cb90f8be3459849279e417b68b84bcb2ff2cad267f8dc55c53aae47f4fe0129de12b4e7aad63635d1413d4a121e2a73a2b00674a79a98145be392b625473c7aae2035340c6be23c8f2124e4919bc3c089d924e871fc2754a9abb505f982ff5c974e8a40993c0715f8f8393bfded8bd306640af082a495bef6d0a95e547fbe72ec21a09c60b7df5f1e20c19870337df580474c415b1859964a41d7e607f665c3a68f48a30f530aa06f60a0b32134770b7d95a98def19780656b97df90ee93e837455fb8d530e10632d0ec881a0a4ba143268e8161031a48adbede191824e0fc1676c45e3cd89f04d43b90b21594ec23315fce42d459077bcac9410b8b0a87e4f3399e0294d971a4f67102865f78575ead19425d0780d8651325028fdcc4ee812c9215f8b04868a0d79098d6f6bdb8990a7dec3e2d2416f86dd2b187aff013822effa1f577a041996946002895ffdb07eed1004f8ef247135d33e80794cf1763106c160b3e7411e023bd97ba12ae982c703868dea717ed0281ac8fba7a515fa9e8106450f41bda049d04779be995dfc9057425834bd71b8d15f7afcb47151b569fba9d5ead7f41003097b61ad7742590e4edb6a29f7aaa90f59f6bbdadfa4b74a9bcfcf19082eff6b209f8c7d28cd0a6ccaac2a8ac6915a382421365b642b4ccb53cb04838d8a2d8dfc20c573a56d14cdabfef4820515b830a6616cd29fdb65a711db1ee6710ed441b7cfd0987810d68f3760020fe7e009139ff19240b63c1515f84eeed439cdce057e077ec797a6673c4de376a70a61fe144a3188999f17812a803fdc009f544d7d8a43592bf643eddab61f9a586bca94102e146006b79b8f831856a1c46ddce831faa86210228ef717fdcf8e0693802a9c6eb49977018cb46e4b87cd33f7b740caa5811239b2e5087f20cb649ad30a909218b59d5d9b119e03515269f60ecba28fffef959959f40407b6a8d5a39c2e6875b80d00d85969ada0992ec76f459719e29e4f35e8a83d31409bd811fc9ececd13ee0a31e14535c3b2234d0743b3a331561f8d08a36e45ae81a804be4d75930df7373090744d6952f817146b7ba3a070c9f73baefc6c1d2110ef300529d771c9effff0b1b3b9b7390c1fde8c415d958469dd8d691aaa708dab674d960c88242e23ae9a5884449cb474c54d821ffbd8beba00614d9dfc1e05d2e40d3778b358eb05624ce8adc33a2235ce324503dc74d6bda9bc1ae5b0419639de6e69e86d1d076cd97be88c119e6eca22b4194a25b9c9602002a0d51013b4e64807ef2b90b3d80e84936888fdaeb28a07e8430a6fb97af3b10ea27456ab43c2321d5eb4668129ea7633ff827c4afab6b687e83e82cb361df559588fdecba713ba0a928b2806488641f5eaaa10bfc2ba139bdad94be64a837de738c3839ab14b0a906b67c95741a9ca1d9235d674edfe1d82f150ac0c775e38f53559a6ffdd4375865e47f8933a3fafa58b9e5726388507e21a52a85943e93e68c299d28cc484f07949ee2e06f7b38a8f976100ed65b3290825c1cd041415451dcf26178e3b51acc0bc147ad7a03e4b721bb1e2f47e5943a6ae25a5301c29f494d4ee3d691d0f7673db6235f58c85555f42cece7ed95a926e28c93b6e2524b851c515a11e432f1c4e30d223687a3ff3ef64c46222c5eb20f2a51dfcbc654ec7d7e7a24fd20ce8312429cc10e1fb05ad0fc319ef9afc48b63067c09d8974f0f0ac177088fbcf75092dc222370f2fad0f5fa94504eb69701fd91a0cd43ade34118931daa0da13ba5c05de9ae61165af698cae050011cfe0ef6dd31386d02258963d9e05dca44a765d66990b9f4020f1e63cc6e6b198c0c25f41e43bf99d9ec9e8b7e7353bda953b11de734c6938014d8aa9b9b43f581b2fb7508287f73551d0181a70313526b454a483fa5b1c4d89801b58c791821dfc5e61e6a9494f0fa4e5201b5f5dcd4fce114340ef2c4c31c655c3568d5a4d5560765d5c4c734f81d0f69c744683bb22f9043b12a14a7177dfddb4523f5aa189d43840e9ab39662e35eb6231728cc828374cb6506dcc01497347d7c792c4d17d8337b44661541b03054d002f91d35ae90d04cfbd56aacc9090ca19d597b97da773b9c114baf24ae96c8357b1edbe2a9f4b2a2d6fd0e105887ef6e04557a791ee4fc8dbef76de1a21e3f0d4079b195a33c1210734f04dcfb8d0977a4f02bb5fbc117d4f34a5ae03cc9fc6268aae26946ad51d38401a3c2c45c44219feacfacaea3372860968ec46efae4af8018e7b2417c59d79f62e37bdf2f234ba82b36c6ee974844eac9b3fe0a6c33c7294af51856a6e8b7d7589085dbf345714fcc70bff94300b2c90859270f58c6514f4c51bb630843a978aa7f311b4d007873099675fad1fc02d4d936b4fc5b8716e445ed42fddfe1202326e8809dd553d7945f10c26051660128a6b40be6b032efa012fba4b342eb384156ca83166105b17cab6d617965363321672a46593988864ac6a942d68a04e85a2aa71e8f4a0795609842868dfb97e21282d013445a27580df6e4514a419a8503d68cf466b00998d9abef06f6e742555d88eddb624e4dac7ddd15b8f2d7dd2ff20672d7c00c6956789bf07165d5a76981418775a1da5260b1f5c0be3a2da01a1aa942809beac2c06a7696bc10fb4f438002896b11660d2bff4882b92249bc88d9e012b2c726c972346b79374c79be54fb1da94d1f06f62989a2f22b6fd81f33c9e1877c49adc9f81056dbdc5793fb0a902caf055ea01004d3f563a122dedb44cba1de9577023da1d55456e8ca4a109b063739e9e2eb3ae16859f2b5ca95076c957dc356c94d87d82a4960ec07b6456785a55ca0faef5484be05d88a4a2446f4463c019030f1c2b9836bbc91954e1394cf405a423719600e197d62fc17da0732ee978f577257a0279c4be37e4d098a3c6f4f0bda14244935d709d2670aad028939d70c47fbba12ab0616b42b9d065274270fa225d8dcc671fcf08e80bdb0b548e4cda1f245f59df8d07ba5bb6b38e01a5e2b627b0437f0700cdb89d50b20bc2f3390d1b92dcf0c6bd7dbf2607f28840391d2a306b71bd42ea89b779baccfb93810fbfd7f19585722c613abfce35b4e3b557dee7b5f93c98cfbb6826b183cfa44fbd101bf0d8a563896355b652eff81e7aa2a811ee8de30fed9411b44b887f05a3e91633c29e29dc1cb9bf203c1a2372cef8baabbb98903ddc3c1c25a25c44ea1c2e6fe982ea1a324f0f22256200f5272823198f2ea1cf7378513ef16ac5a2f4ea7b28be02736c1820063071bbe8a109d5b98cf9d81b55ca83d4b45e7cfb194c5af2102103d1ac7113cea1fc6fc47906385d168f1abc346dba98c6d77bd12fbb151991c643ff235508abb4be8c74b91d3fc9f8692dddd72c30114d61e8273353ecaed08383a70dde745b14cf48ba29817b029f4e51cc8ca3aafdbe807a20319c4ee61d05805f6e14e09f1a1d765419f13d4a6a75093e7da94e364f124d534502c0893410b5852779c1c8163c1e2488c0529c7b6cdbdccdb490512f23436d1d8577ba1cb81ab47eb22fc46a69dabf3ae795e1fe4ef344c9bbb4fab17d8d730e59fbb31cb70eb2005ad64b79efb703f69d71a67d1cb63583f101d13aa1b4ac20849a44c663595dcd6877841a40e839705b5cb041c65cecf5222f543b9dbedd4568791535a5a6eccf7c245c706b42e7d2a3dc0396a739a45e870443c9ce0c6abaa52a960ce1598254ead37c6493d30abade5d9425ff810738443e9b5e623d3076ed4cde42ebb96f0af75626b87de20af1d8f59dd2176d4a8a5a752d0c5b4c17754bd7c8df0ff14f57a0c956ec3a54a20517e11ebbc6787d8cc00cf0fed904b25400cb35ef52535af4165fcb61d8aac8567ebdb8e26cc0c376bcf7ce9e1de7ddab3009251d966cf886f192dd229c39c8c669d781a41dea7123f93dae416a24d5bde950ac44d79ca171b5166754edbb9775cae3a605cbe5c4c6479b99fccc5dbe265065c94e1356dfc97f96c718dd584bb433b78a4ee5bfd53e072baeedca7a7b7e858739aef7d5eb0a758f0c5351628ae11235c7023cc5e30567f454bfa35390ea3386833d6a6814f05bb4f02259751ae01252eba0b41b7f6f5ddac1266ef6a4c947e73766506d8ceaf34edeff75353800b071f1afe84aa90f2c2328076e763b5199ed6d44c10e5fc03e094995122257f9f5206d08f81e06b0072358b882dfe958b59aed0582ebbc793cd95983843eda693c7fe37e4037acf3d8544f02c9743f36796e97191d86f0f6b2fdc627311bfb85b62922c6ea815e59a14fcc151fe03f4724e96758902f47670703a651885590b8fe616fbd355a5cd306beaf183214f6170255a1921713541eb436b11514531640d1920bfcee682d6fdc1d681a72e83d2d203b787b16c209cc10ef7ed7663af9315536e0859b0816cd0f9be2f0940f2a5d57bacf7d75505316332aae896f826a10646c6eaad36e6af7f70e548d1fd4944dd6fa77d9c618e6396ed23f59de5bb2805120a0f87907335b5e58674e56e50dd7a7d6688160e2177498f2cf189388566b9ddfc2ec31ea84b612a00e24ddb2c3761e8e216ee72657e6525f88cdfd0b284e9f9b05ae832f8fb4532861b486229d84908dfef4664499692a720517f21b2a91f0f6501442f7224849a9603960f0969f7645267056dcd1ce74172a6b60999bda6cb22383e65ab2ab22b804c1833a89147229b879754094f09d7dd3a88581b4e6d26505627f3356391db00b73ab49ea7722fb8800203f497c1fe9e4211eaaaa0bf5ed88943328ea4a4ecb08ffa77c708016407e8f185283d4b2751dade4d08b6b9943786abb2d1e801d4a0fa851d74c0b87719cf6dc3602e20d2b469776a33fabbdb1368c431596bf8a08ad0c4926c1407bb6146d7cbc908f2099dc9d40d009f982f81a04bc781facd793265a92e8bf95313cc39dd438302621b0a2436ca1ee9cab2d4e36bd8520a13bf0fab1698b29ca1fa0dc031274c9ffb3e1f8302668dac477cc59850ae1828d2be1afd13fda4432c353fc6e189d02bec1d948cba98a391025f9523e269c837ab48ae97c356ac809c41e6400615d778918152da96745cde8d81940817918378a7006653c0bca82d948c0218434aee5f9716da8ff8c3e290681473dc6059c1a485b5b3d08a7a87076aba8365d6ed068e29a761d1380a26695a7b8706994e79e20b32e2f5d1a0fcf71fa2a7d29f3a4a7d27f509d47bb63a8e8eb3f57c69e00f4c47941b17d4483accec9c0fbcb9bee1c860cff582af199587c7b6053dea41799a19e82a4beaf00612b09320a66660776a861613d120a7079b3315737c94dd6b99216dd2c8cc42cd7d3008a2a27ecb0bf2c36c6724e4e0b29a89d1d6c228771c132a94b1bc30e8d585533871d3cba38c1b31639969e584455b5e746409b99b251919ff52380f62939a55d272fee34079ee7fa42fe6bf1313b31853dc0d56747a0b35bd2376880f36a710441b9d0ee1f51b119ca78556b1dbd0c3e1c86be2bf464577d0ecb8294405e9fca377eac589c89b874611415a8a97052b1a2dd0caa360cb2f818ed9749b8469c0d3a78e0d963fea781b8462e51f88de4cd79017268a8e1f63b650e6e262c34727a767a036a995b7adbacaafbf671371b7487c6c09904b52541daad10e43c9a973242dd5205120a07dc509d72d254a42728b0a6621e364c22abb1af64711677e9c51a8b999a4cf4a90419c8a0e5381f9b25be886aa7cdd325313006cb28b78b5fcb879119ccefe5ec75ea80da6f20403d17850fbc2c42e1183b008ad7a39a792c7c852d911f88560b254e0b377bf0eff5c2ab29e567fba6ce5bb2430ba68b23bc63879d649c67677fd866939e6812e8fb96a23a6e293a9f070836b7264f6e0fd746326af849fad800067eb378aadb8a0ad800f8224e7267a5043e6d931126a2de27be6421830d06c4fdf70cd6b6dca018f2b8310b5279d2bff58e320827205bf93abadce2544ef0821accec66667cf30b7470ad23f1aa8b6636cac137d67dd08ebf4175ae4bd05bbd688046975391017b89e8a94fc60a061306621f44d564e8b673330d7a5e0855d77d2e09022e02896b50f99229d3a7a35ea381ee38d5378631383c2e9cc8d95409f21ea287d79c4302aa3ae77320267b20b4e089689c9f1696d45a4b679b4109794ad177421e6da072a6d23ff3cdd93516294ad236ea04af1be64ac7f48354e75827d633ca65be5542d5429c99d66f3f734ce123f03d4f9c62d98de0ce4fd46c0b965e2438f741f59b982f76f84343b0b739be82e9a58c06e89041e179918e811585ba8a34fd63a77850bebb1007d8c713bb183322e066bce3d2a2d2ba06712148fe9452bf040b3c8e03af212ad075e78763131410ed85b5f2e76e5c769165207e1ad9fb4781c10074ec2184446b93f49fcced99593f6cc27cf598d538331c9cba4c76a8117079e90469863e01bc2ca667acc313b46dc8b5acce21d1befa5cf91b86d8b5082ca0017e90c491f0da0c9a3a26b8fdd30e6190a5838e15d0c50788a618100157652a4f11ca0ea48089356a7dfe6044d9ed6b241372a13c0686aa74101134c04c7d4414c9c985d5228f5782d4f871b6605633f0d66486496672eb886d88bc521dc27f66551b70ffd77e0b0a531c36fe532a593840e82d0b71418b31aab8841a7a7e17250af9a908c36923e65ecf10b4734852994b2fc71be0f3d42e5d854de57e8c0b9cfc0ccbf53db059b51df6b6a61fb8b7f5e23a9f91c3340ef66e3f51f8584b16fd526796837da741e7799138d573cc52d4c2e8629dc056ec6dee9301e6422b5f20b45c6cd90dc690b7f13f793799e603aea3a8d05d743ee9a510db0740aaf8cf4b93f10a9606199c175ab1352cb5ed258477c541e8b76fc3d8743706960d12ef6a7dad04ab03cd217bfdd3358072a017465c0fbbd3af26a7abef5befe511056d3ad97e99290e9497e919982615a4f7695f4df53cd02e975395a37a2fae0e2a0bea78458904961dab2b52a8dd2e3f3f9bf9b9ba403ef0a1629d1c1758a0e8061411a71e42e0c5725550b07bafbec1e4b0ffd2eea64de54070e3b9f854de9f5c3b51eb1e050327100c84005117c0a2f3eabfb58e35636a89b4a653075273dc967e031d4db9fb6ef81345825e5a3b399928343d061b5a1981b577cc13af025057d81aa79eb1eadbaf58c3c7323caae34c8fdcf8d6c70260aac18c84d31cf99b9700b755ff945e42bf6af31fd61fa0d727a30552c19d0dbcace52172bc01ccb3074bc79d622656ecdc61dd5958fe7dc366bd06ce46e374a959d9370401b524608b7c8b9fe726c03e07102b72cfd1c8e9e4e34cd405dee8a873364131c80d908b9b5df3c31512786cbd922c31d6d56982b1670415e4a20bbbf755452b62e507f822aa41b5fcebc8272563d3df22dcfd54629d8e2c13b9237fde6e785d84d9d2a2bdcf137459aa6f978706892a3eb59fb4769bf7048c6880382d8134a9edf4367cd8024525f64c69302c7c792609521983cc38dba57b4abe3d01e111f80fa641ab2b9ce20a1cd667b7ed00a4ebc1cc2b2c91866d77e38a4631cd8fe3611e28e015865a71a150acb2c2acc17bc2742f146f8b9714e1629a1e7943997211998f00e7a17fd8593ab901bd01707f68e64326abd67a63d263917b4c62f507c470628afbb66e0f1d4a735b6ef5bea2495c12f6590c8e660a351f80223694405036e4508f88e9301f69d524c9acf6a998513d57b470201fe03dfe2f2279525cab36e5522e44d2fc1cf5eeb8147be1924d6b03ab41a2c97e83bc4415cea546253c62579cfaf0961b80b1be285f92ffbc99027e77054eccb291b8a86b4b28be20436b5fe4be7dcee85a2885784ec68379edeebdf11403220454361228241b2ae957f5fa120c1a2e2f51eff88249a27ac72ed712b7fda64aab4080351b1c19bbbdd1e471bbe6b44767625bce519292bd1f4384b6bb31afeed7fa5161f2fdc47457854aaf1c06b2db1343abb0639dea8cabf6a4c97fc6067b8615fc5eeb765102d03242f412ff0f29d79154888050e7622fc234ecefaf1da232d75b1060f94479ecd7d26d2068aa895a027429d904a82f68252619d4d42ff4f5b4c14b5ef414bc13b5aff3870f8caf723af789d44e1a237250efa0c81fbde9b7459ac58004048bc79abfe86c05b95a2aad4f38a56f7babea0fa02592959b15ce1563a9377f5e61e8ee27a889ba13f63d0d115d34ea13f414d5c6bab253bfdff039eae43292ea240cdb08655b30e78fdc59e868de700ba9461a96076074868880afde3762c91d5ebac59c7f9f73df366cbfc7e2ed43b0521e71fd76e488eac76ad3ca2d5b67cd09adc42f298ab91faed878b4536ab9b5b2265e5fb5a1ba04f4aec3108c09086e50e97fe53eb9a4e73830dd930c26e734816ddbb004570e3293d958131421adaf0672182a90af76130849dcbee5ed4819db49883605fae220057953543c3258530fa85ec6ecf3bbf4352c94055837288ba2d3a650aa560fde30385cfc1bc8501e6990f2d0694ff4605b81acd11542f4fa911d0d629d41a8e0883ef46bcde64409ceaf45be9ab9b1767b8f928bb09f16684dbb57e7f30c76f6257209e577b00fd20f4ef08ca76009e9793c8321c4f45ea407bf0949445b8e73442ca1ef9ab4ad70a81a5c5b2d9e29475d9a7a9b8653f98910ffe8e62d2937f948eb34065f4d7bf7c0a2286d22bc219926d86fb9ee7fa88af7286f803bc90136fd1bc99776f550d501be79400816999e1e3f159ebe022f63f4ad8f8066b073c297e4de78f0d1440488dde5d387dfb3b053042f40d5e2f3c603f733d71296c98d793b9bb479ede71f4816d497e3ba75a4e52560ea5d174dd4b2d9e0229d0caecceace270f22a4335a62dcd3fbeb46137952e8af2fa9b801e2024c31332ec61fcba79e88a2919f8a10650fec1066ddc951e15736d3488a500960769361b3a990c50559f75bbf4dfc4c21379dd6df7e6409919c128aab0185230841627ba188175d3a668d4c2d131b5b2688f06f9621bcab650dcc350066b2b12a9c39f9d8a724ccb320959a0ec978c43c303d91924328c031d67cb3800b03bcee78adb2d01fe180f82593830679c7194f77877cb93033a7821cbf098eae16a01afff29fc2cd7e6326216bed6ed8d76d0278f3f5e78d87d091fa320c9e2f5e10d0c3ffa053993351231cb5a05f795d63e998645c219eca142a7b8bcafec5e7372774d5f8192680305fb4e847f07522ba1bed2a5407409008fad1165b8654b562b41d9e9958a49aa9ac393a6ff74fda4ae1a0b2f07ff662283e1814532a7b87654eea9cefb08b0a01a1ffa09c3f2e190eb235583fa3af816b1a43d1ae1323783ee68588e23323faab1a1e374a9620d2886f3c1d2dace6aee833e8c389f102db71a7c01d86f85f2fd478b0b3fa5996580c632940889c2daab7d051bcece605288e0616c9f0e4fad067e7da55874936bfa5c2bf22602980074ee13a4d3827b3809060fb61a838dbd11b92d1864a6a4bac180d42729c83319a38f716a404241c88802818679bccad1ef2ba024e69e0b5e7dfea0227aa2e068446347fa017c000a3047a71b0dac3ae263b9a7aa6f68b240c84e2ba74d8551813e9820c023fcb4fd8469fd3c91467803724b451d621b919e4a9c1673b5b15e243a63bc9954f64bc3157c9eb8e36cf9b6add7bef3b77332bdab69295138ce68c6ec0a277fb0405d5053c608ba8f81d84f3f43c382b4d71dd4966cbeb0bde3fdc5b9f26486c0e287cc15e6a546c9c612fb4a11c511f34a42dd162b14483a5299bedc465969e4cfdeb2fdae3604e181878d8cf19dfcc6bfb5d32a7b83572d630b45a1e3732fc89d8fd4176ad3d0d34e3db4f330afce9a536289af8151aa2ad6e4cfa4efc62d65f3edcbb031856604d753cae2dadb5ff0d8c313510ae4ea2532cd31b0502a03c2f5d430c91dcef8379cd0af46223e50773b28de32cf08eda958577204a517c829b10ca6643f0a07e225aa5ee4774bdbe8a6ccff95eadfb4c3e27e47a39b2ab7e277616b2e4a92ac0ff165bb6d211d3950d94160595ad65b41c15fbc7bda859511eff2edcbe737ee15900510ac3be64d2ef92856f7c5033776ab9109501421ba255d3854309cb95640daae4da8c8f9762ec5f7f0809aa7faabda4e9d4b86cdfe6ddc4e6c94c9d6b1b85d41f6ca208b71c08704a70c2629ad90c2eb3f207859bec92947ff592f8778d711c346441969c294ba477022d6a4ac9d981183bd2e4a8f01166185b410ff70ae1443464c415613bbe70748197798e21dde6d13e549de481630dc0cb50fec79c1a6eb28aa85ac21a8a842ccb3fa204226b28f89d2d8e9b788a91ef9d842062441e78affb52068214278ad4cd4a9c01d5b1f687dd958c7453bc46d3cc49dec2c0b75e403d75a3d0c1bd3413e687ce1d3e4823ea24d62cc94a419fec122c2d2821e103dcfbc94e42add44e1d905003c0281d7b459ad2384657d2d59e34be1fb6568d203d079778a5cd38ece6fe5fea3f8c0072943b37b638f4c203a2a702e67ff80085d604da00769ca6d029e3cca1dfb09370be3f596efae809c83603cabad359b05f9a1217703de523a75f05fa0490e99220037d90150c1aa78037a9f8da89a74ecba363b8b9c77977066eca2d62b30cb63a83dede5af33d0bd21af7b617af21f6db5e04ac545a3d40168ca63fd3ab5c26e9f02f2ed48898b79348341be501f9c317202dc3d2385319e240dabdf9726a99fa501a1a42312d496b89b0c287562eb7f16eb128b11449f6e83a41e3c07f67437aae25d0871502a0be477abbd34651d7ce76ab7089f7591706c5e215230751bf01bcc36294bf899131d55ed94aab32da5ae1fa41c2b3ca2df5babbe227c62ab48ec8aace8255f1a6616d16cf0ca988f8cd2a53b713cdd73177ba693f15253395d64979e8fa91901fb2cd72b7a534e6fabc1e08cb048f3c789a46e2f6578442a5067cdbc85f7d692f7b8e293a6f88773ef44fd267befc30956c686c60382093687e12b2b15e5b408bc5a389ca7be42a775f46003ddc737d1a266410bd8d0c8e9792dff60c2b5e36e6ee2b315a8405a72ec74be332bce234420cb3a3bae8389affba5be1f93ef9b45668acea1cb6bd12a02e99685625a27e60435ac6cfe6a19afd0f473fb5d9b015face7c11e29f6ddfaaecda8e7db92b7c32dd988af0c1f6c2791b094b3ffb4a289f1e84d2acf685af24ad2a0cb123465c180886ecfa1cf00fe330fbf952c28b176dfdd63e0f21c89a4366bd2fbf10208ff0b2b11f4f2afb4092c54b48a083ba1d0999c9e0baf176de4ca06f07910ba08fce73a13eea8ba653f331636993eeef516e1bd2d69f45c201bbc8f46aa70421e1099d7218b09f7aa7fba1c04af17d1aba5a4b6b19a633c3b547edd0b7151b60a886180edda3757b59db336b9c1ef3c50745b53eb454221dbb49612cc2e5c9df8098ad9e559e966f081a0581b5cd30735e6976dacedaea2d42ba7c8b4d4dced584b6b14b7e457aa453edab51472adf693c0310675b4d52106e0283cfa090d3096d63b52d124133a7e1ff80b442ac64745f6a96270d31b15deef859d126e5c5a22091f0104737edcb6b25dc4354c19400d44b7494108dafcde3d743af8875fae8227ce9161fd5d0e3987054be149c69fe648879bb137c157ca4ca180cdd0b641c0e67aae99fb461b530456e2dc64ef81ef95e29d148a7d7f4dceead5ce1c320e7855a152a1452541a80e5c2ac758d1d6a1d6532be6aca5b642fc15e210c79a9aa5f5c238bb514a3d40b0843da12cc728c89b74b5cee1fa41473d430ff09cbeec2d378014ad5f326741f1a3c1698531b0cf04c8757b45a2022dd9d03d72e494aa702e5cc4184c91b49b4e9cec8cab394531a5d45033a5726442882326c8c75da0aaaa061b4dc1e7ecb5d32a011f19dd260d8b34607163343f53e67705893931c4837465efdf48cc195d60a703a45b88798406d1f4c9d8226751f5e7167a262b1b819cc1bbb3e129dab0fae5c21a9a91c39c71aa52b695955fffaf53fad3e5d9f981b8274b7c951be57c6afc2efaf052a9e52051057f9977840fb1c9963f23a207106cff1b6b0bd01fe910d873703d517d0bf7a0df7d321b1fd046750d1bb39861fe1467186f4596119559fa57c651ffc718c6e063b3434857c0f2ed482cc35a5c1108480efe74bceab60e2550d90906cb53c38ba6178dd6383931d61f4caf440c66ba118f6bb8ce6e1e1982bf2ac85e0aefdfd4756d2de89e87946675d4a5e539072a93a0eddd0b4086a75651e4472e994a31e5cb4179e714ed31547e9007c3dcfddc775bb945978c6b16000d261242f46533d2c50a091c3436a84bcffa683037ba10750ad2d9c65b551fb8d093391e27f84d249af23cea278efb55802342f880365d44484d7d9a1fc8261bd2400de81843d8bda241856cd2952b644acc4c064e64132a7a4ade7571029d3dd39513187451d7c6857d784a0f407bff64abe9041ac81b28756c15ad9b4710ccf57760bafbba11910e52544aa763b530376fdac83733393a9c3c9dde4c4d3839166f36a5bcdea627eaa06b4d4029d1cf5f102598ab6165d41336a1e796dc905d18a2175b89768724ed962f8810a38cd8a9ffd4bac092e2053f69f42357438ef04f85a94196c195e461996fde2a9d00b8347adb3cc97efb27feda1ab28d7178119cdf47c78f863946368f9f0b0f221b378b100bd833a6863eec267763eccb79f65b804cc2395e693c0dff4c41908250299d676731cc554b1bffdefd07a93e30aad0e6827339c210a51202348bd0388afea15fbf62c82a7cd9594617d501e5c0b398861b2d8811da950d7b8521570e4afd4287d2071cbdcfb5caa98e1d892c0a12220535a5f61142f9c1ccb71c525e378bd9ad60d68e90c302d69b42baae3a42271866dc990cb3a0a1488f1d76c3cbffd40e67a20b406704bb34ec9413f124d80037906af94ec16992aff9ea68bc8f1a19a878bfa03f511f9b39423d1d898f858277bb0e1c5517da404ce02f28b9088b9f7e6ca525012bee2baedcb3d802e10fc240361856092a1c49ba0e86ec9d06549ed17b26bb81e8b55939c3408aa8bad846542fb2bd1420093541e99f317a405ba5c109da80c3ae22e2e603552b88223a68a6be7e2f290389a78d66b19d57539ac960c5ee4f27d88afa5a5664bde325ebacbdcb32a20e22cbf30f45148f9b08567e8456f9b3b5668bceba53e45fc9d8e17989de90a976e70a7dde904d6951a7107478dcf2870315bc906344b0962e33f971f119b895aecec8673bc3d0e06d9fc0cd2aee106bbe61a62050a87f1bc4b596eb18b2a50dc2b6b0fd038401bc236ab864e250cbf137d4960ac3e8a6064908939f632b916aabac854a3a0fdf44bb390b96ead9c1d9d204d71fbb8279e523297d07c26ee5f5bfe02f4e229b2c5f08fe8035b6bd12b10a9d2b83e3841007d8b8528aa8e95c6844464ca9f482a6ee00438b509bd3b762a3444279dcde278f4c4e30edec3acb28f8e8a70c6524e1f8f8114b3066958b2d3ede32599f01599340d8c38dc7880f0f81795fc46bb241ff11edbc25bd13ff3cf0da4dcd3e9ec0f1b2e9e1c3ddbfd54e48b7dc11c910377230e6d1438d79f1852c840c7d6a546b3b968eed1b24ffea6f235409f34bbe0dd30e9d62fb9e112820898d288d2addf7c66b47b2659b753316744a215384abed31f08b54b4560d6fb4716be4c331f69f81e55521eb3541df8fa1e5344c90f88e1cbc14106d0a0115930aeb97743821a2be361898a02d8a2904cd52349e5ca2762f537582beb4138231e380bd1e0b2886b7e14581fa5bbab094195e1ddfa87d089cbf558da5494e9e6f07f17af2073593a5cd5f7e827842301a90a5b45dd81dcd1302d926b57347eed2169a3105b995e8f47c9b96181e1b1ebc5b612eea9106155c1b64b09517370d2d9c1ff854869f243f59d678194c0e1500e7087690de76117e947acc999fb4bc91d080a1989f20a78a2383e86e8ad3a7ffee8bfc7ad1118d76796aecc96651b90b045c2c312767a3ba681a63e7a8bedc9f7dd6b2fdfe037ad0b56cb8fcad7cad08eab61a5380b6dcbb56905264707949a6ac10db63fce3d7fba9ce85135c19a017138bf7c90744979c36ebbba72cd7a00237005c919e9a2439b7cf38bdcbf06db4e32793d950b30f45d91fbc796f7c5cb13258d60033cf1b93b643a800c551c57e65e4eb8a059a8ec17f96e15dc637510b0ea4af6cb372920c5776639508f319896d644d316f15a1991a07ae33051031e1438eef593e7b32c4e558d269f0355616407b72b30f9e4fa81250a10cf23d57c20880736345f72a357c191cae15ddc88fa05c70254eae07690830b1d9029393fb2d4e91450260cdb9527000d15879f023758a4dcc294db0a4928878f7c062f7d934de64a9ef568c86f5940cf977e61b53b2a38448a690fd41484626b4e3a34d4284f519cfb3200a82908c54b8b36bb13d80cf4d22b2488fe75484828162ac59a51b198fadc3d5894fe10883f06c8304a32e9b4e39511a65a1caa597ddba78c6110f3ca20c697e8d184122e0d983fbf8bfde2c701f6074cf9b18bdc2d8b6481aeea117e19b8a1ca3423e7aceaceb6d007c3bd7d54968ab990cf25c38b74a194c1b833d014b5165d4b524b30051b9698f51dfe014b27913002b0e207c1cefad5b2638456f0a23c0fc5f25147869b4ec6e3012d9b28d995bbca00e555ea82d198cdccf1c30dc2adb413651366ec5da661ed30b48e0aaa7835379523c0645bd96458eedf255b3b703ab85851e8fccad70add31e431b80087047b85de7e982d8bfb00b249d0fffade98b9315e0883d913b234595ae1696426a869406a0589a34ffe525115da3eff13fe1c586e3924a5dbdbf18b8075bc5b775753a8ef79dcf815524547e52e39529b212079dfbb5eb8592bb7174771cfc3ddca07fca9076ef149a7872511bee599f3028eb08bfbd7668b762030ef45fc21ea73a1bbe55cf8578e0085176c987fd99befc4ec42674fdb3b5ec12f12fd553663740d95a7b0099b228e129b56fee2708048b4c6ada085ad3eff5567502877966bc75620b2fb5c27cf60b56add8d9127b1b0b05f6220c3d749f3671a0fc431eb534423189b3530cc43fd1d10843ac925d8e1128d93328b35f60b5e5cca21fe15ce21f98ec4423e4b9081454fb20d897964ec2fdc5df7787412d75d156f130e56af9580a5e6d5a18dba0abde020bcb5a64cf767022ed865162379976e68bff59dd97711383e38e1c197ffacbbd50e5af407bb921628815d654a377388b58e8990b418a52d94e17036a55f6759545ec8677e10c5bcb7d9af8cb1d4481ec5599205ba8b0ed720be0d965bfedbe6be864ad3ce306248bc1e872c938a158b42c2a65ee666f2c1c405abe2709005156dd707d7a9f5d3a1ac319602eb989b9330a8dc7ef8b5b3ce1bd068ae7e850bef3dbd886544e4ef88258a54d2f1926e1dbdfd60fb10df356092c13f47efeae4139f72b4fc48d306f4708d61c695034c3e15d2683c9fac20b12268d699fdc313099819e35c00ce78933923e674018ce26cc4cc50c52cf0909b38069c08a02aa55e30068d1c0af4b63d069e1320f658a9e7d06fa6fc4e9cb3d9f3a6483c726e0de7c9949ca991e0501c528519ac2be422eaeb7767272d5c4f29c9caeeacfa163a5c774bef65d798ec2af9600d33a31271f7d3a9b4e435ce995184da4b4b0c7e9c1d636fbb610f6e3720637cf8d83430a6d9c11becdb9e01bc681ad5219cfa8eacf46dc1d8cec9ba6ad58f7642610117ec5bf62d5ad6ed1240af0380dc09a93ab7078cae8921f86f5fc01bc9ddc6ae48340c3705f556dc414fab792e0dc5d9df7ac614bf01843e8b20bf68f0b88bc5705ab0d8c5bd9925b71730fa9e3aab241232825ed8c1c52aa5ef6ccaa46cd324fb22e788f8fbc732e0d6cc389a82468204cc867ce9e816a8494d26ac329ad77475519c2ea1ba95acea301c107eb4eb0d7f78217e3ae409e27ccfc88551982de028a601989a9b470fe7181fe4223ba6037260f2fc2ed2f34659a2f5a7b39816e2da3b942c08a8f07c14f85c8a251f09daef01290def624ab9e44a3145d824d4df9a451ea6188715d18bc26618eff3f0f7b0e402cb4635023b01a9724ce5592823c250fcd0aa627721105a1b72d1460b9b25105419abe527ab8b18f5cc9642ead1d3d8b47084d5942b0aba48a030b0531ca537b056caa7e8693d5bdd88a4a2338c02522c16f1bf3be07688df98f5a58562c180d8bd66c2cb4b8b06ce96c8fb92c4122390cb158a5928cfe79ff07e49b91b4bb3a24d09485eb4c7c15c06d94d104ba04179ebd13a3b9385e1710eaa4139e95a0d96a978f9acebbb7953ca4290e07ec08d7b3f08956393d6a29776bd7d07ba4e2d0076e517feba13af3ac9440961ccfb515d64cab4a527cba88b668682d468a5ec612e4c8d76f91a8b6f159244d9dc61a9e3260610c107df1b214f178b77f102740ce050f391ecd24252f371bd5167d6ee1f6b8053130c06d6660d1520f52582c8f2f498a35d1259c5fde573b5f2f64107237940b3edd45950683e022fec38bf210c309d2ff2de25bf1c9e4ab9243f383d82c175b1f49dcd9dd381ca36fb1b14ae078b61347c0502f716702fc4f42e2f8a077c38bd6481978fa8e6bcfd41182e08a8936a1766066e21ccf1ff3893156461be608ce85931bd80fdd1b8200895d5c90400f792f5131d3e8116ad4fc3efc77c8e23457190c3dcc6b53293fffb5b7a794f08de6c0608515667bcc08d481704577aae225034912e9f3b10c047f309a43d7bf8d2f218ef92aece286dbcec82d23e3767a959635038d74188ca9416fbe41c46457c88f87633cdf1375c505101e6a78e075cbd40e170e4d958e0a44bd8e579baef1a9da61dfead9059012d6b9da7ceca8baa4e00adeb1448f3e602e76f8e920893443227e2f2edeed5971163ef0a68a7f6e9fa7fb61e1229f32c341ba6213704ae4f67e7e719e4b9042c50d8d9046735f4db92058328206d2d3737f1bd9602aae85a4afc561a404a95789371bd8571579c0948b09db8283de6d368750b1333979dcf6cfbfe1545c338846b6a51c762e7f2d9a5d7a7bb2416045a9449983f1436f69fa4cc85fb19229cd273ebb645682404780b0090630ca1bc0fe976d5eb09f729394401c2282f5650d058e5cbb0fc6fad1ec8cfc5eb0bd26b25e16df12ea4be23ff7007500b63eb1e0272bba354a5a794286e0e97650c7d9577b9233196e61fbdfb27496f89f0e69745fc2addd12003798e0e0b9377ca19f0ed45a371001bb25201a0ba46993e1ef792b1307cac970c8ea0b6fa51fa611855961bd5d11ca1d8743df4b19fd82ec2e886900b4ad639c2e43b8fecdd0916a25781ed387affd998b95e8fd228129137163786e20346f942462cd9174c4ab6500104c73da75199892eecfd1ced5fc8aa195f8f23258d6cfc465042b3ce75ec423a49ad0562fcc86f2f5bc7a96371ab1ac3a77636c72f75acbdc6bb487e9b9c20ed5353b3d41e1d01e6de48c36046eb0efc907e2fedd4416fc7d5b7608da81c80c332c5e95c070a7c73d118b67e4fb8d30b0d555a96b07babb9f5b838d2b5654a834634afea92d278dd029abe8e13b58c16bb7327e07484e4bd969862dfe2a3666287a9e65998b7bf775b47dc2cd7f105dd0eadedc7c4dc8d20e6d358755b5e7aa38c812f56899dee7d8fc18538b44d83b2aeb968d332c40f7928a1233b5eb2152625a6e02f51ae39b9887d2128a6e668ec99888876e51206d7be608762b0d97ddacb59366d085eedccc38eec938ad44b84286dd479e6675c56c34a11133ef11116f23666800ca9bec9514bcf65851054f7c0c32d6e5ce0ab95873cf35f9dba1d1d26de085cc4ae31b6df5fb99ca45e2e4fb3c5c983b6b524572d3fc406d47d2e67b47a7c55bf41c11fddee634cb88df09cf317e26621aa2699108b6c26a55b7584b98bd19f143db1d7900886e303d56cad45a4db04ce459c58d261c88880104b0de021b60c60b6f18dfa554b0b146d866507815e43434614f935402a8129d0f3534dbf5026942f7711417e28ae5b262d66f101e15425f4f5c53848b9dc32e8a7253426b975ef4ca8fcbe37338981babb66d628180240d249e68d512912097c68bc761d820bd6e65a43c6125d08feef85f4186da636b8c5c410b78c3e09d25a18856c8d2e0d65a0c2c4c03ea63de8456200a693a1d6f4f3550a4a3ab45f9ece5db33d8fb680cf6db769deab9587e43b89582bdba62305cb792d97b8e80800355315cf575d7ebb64518ddcf954e52d52ef46ac7ac9af6dd438f80b3b4192242f6de5b4a99a40cfa075008c108404a68332047ae4be2e7856b6d87dfa63f0ec498c822e1f7f97ce799fa3cf8f1155924fc1df0bd1211f9044979c70ce63469e04b6b1210eb9c4b7ce69c4decf26ff9ff1fdcfd547ba45f57c1d69ff36370808d4bd5b1bdce7383d697f2898dffda265fbac09e01d8da7f8f1eb975df61bfce632d92bec612092a67ce315263afc08d4471eef69aeac818a1daab35ead28ca028b3dc9b2275525f5a844c912d7b05de59bb433e6591ecdf0db4ef9be5395045d63acbedacb56b5c01284baffddd1dbed9e36ab41deb0ca0487f4953e97d4cc21edd5d6a5b3b34eebda8ae3ccd5d4fcae3e9f95cb0f03bcfe5a9e39dbd10427ffa9475729e67d927200bb463978f1a013b7c60ed93b04e3e6f9f8412be34b44e16de5e78fbf3f62f9775027afbb74be86d76a96b74a68bbcc725f6e9ba5d0ffe187ebd4d2cd3705bb21adbfecde14c97787dd8bf33ecd3e5e2ba34b67d139cc984b92bd4807dba56566d46b081609f4a5c0f71964a1353133eb26a143c89703db8fb9494de5c0a6e5ba7da6eef9b5010d28dc8a7aee97adf30a8ab03a9f7ed7661a6fb668fdcef5bd7f60078fd5cf85d3822d79410a2b6d0e331e15acd7df6497b4694e0e9a9ab0bd730cf3c9b44686adaa9f7cd22f98dbb700ef4393ff2bb4675611b8be4bffa7d0b32f4f89714950efd2e9c1ff99b9c14fff5b68d37f4487d98b2696312e180b4e1c2e50b08d90e1368b08061cd7952e462a4c7ca2bd57134b45c1e82b3748f8ccf7cd172634d8b355235a0a48cd9f2a1044a728913b12f263a2a700a702c82a6f37d91259b37c497ece931c7a70dd1a7cd0ec6af3967b3cc399fe745f326cd871a568c1b55618abc819a8a92726683cb13241c15d6f38ccb71ed4603231492295eb6a00d1993854bd23e1679e166fbff038a6fe25e7ce108e3fdf08a44f190659da435f210e29606cc8e9514b24829a610922205102f2e2699a4860b4b0b2bcf3819343c7a2ec993c6c60aaf236d7344c25cada1c1a2ce1b0a395d9acc974eb3c1666595538c8c16d3b671e37ffade8761db23112f44902c76f68388b05b8454a892808fa446283f7fced9be098bb34dee8cd9d458990d40d521ab36a07c531dc11111ce377f04e897df007304e79b7604aa0ee1b7c1b2fc11a73a84d5a460d3904da69f0445c34ef1c794141c583918b0e2c71d326d7c78f570a00812fc4df01fc1ff02fe3abbe733b4e747ea8f3e1e7a10be3b512c7faba3d8e59db3cbf2efdb2edbf67a93bb544bf38136ef85d41568af1fa4fad4f5f4f9d2d49e679aaebedde3f36d1f1f1f9fd5b72ffcfc9aee0b172e00010161f8d5b781327c1666611652d7f34c539f0a026bfc197eeb672faed6b85a6372b549a1cf6fbdc26291275b638edafea66fe026f77aa2b8772a899bdc3aad739d6e4898a56ea4fa57cf4210b801caec59ef320fef4a9eee5d0bd43d10f4d6aff597e5bb99bde84b6eaa3953ed326569de20435242cce4451f52bd38f462d89c276c66aec4702483e8092cbad0e824f15223e86fcf6490f78456bab876def668458b0147f25b9ece4748e8278880d004232698608209845c4808cd0ec40074c1f720fc16910cbdef8d3448f0f7af962f7d08fb3e2eb449d7bcefffd6ffdfef01f01dfcab0fc2ffd5df590ba4d64cae6dffdb1f007e83ba9e7bc3ef3c06b27846e68d07795c239eb5759d95a142adda9a7bef0bfddd3ceb3e5608b5426a07be85ee7d14e08a473433163ccf110d7985507bbe596b6dcae3e9f1763a146a73f80a82d0a0a00acea320f3fa380f879eded1dfe1a3dffb1d879fdd1c903a704042e280d48103070e1c3870f8590b44dff68e5410f216528378b8c29edfa5bc2b74e07d134cf852875f9b8456a0b57604873f52d75348eda0aebe8da21cd4f5ace08f844448ba6f79475f41051fa4aebe0dc277a845ff56a0aebe9d1ea96bba5321753d777a3fbdc5bf197e8e5ee879d01fe250c15f355f4a1d38c441edc03707d428140a515c2359dcb8baaea84dddb1a32fa5fb4a16e3be98c983f73954808ef0a5db665fad26cdbe288abe0777a312ec2335888707e1423f2507dea17be0dd212abc52be84fe915210fa41e81ffd1a1414f408403f03412f941474040ca2206882a01141ea7af4f677475608b542473fbb712f8415ca2024a4167bdea26f1f68d728f809ee8607817f6f7b1e00407d0dbfe141f86ec34fc317fdaed12f3b50c3ff7e1950ffdf7b20a8ff7f7b08f8b7ea0ab555f4effefffdecbe3df577ce537f42aaaa4115fadf61de3f083792e0fe86df8aaa25f07ef56aa957c8865fdeeacfee9fdd451a7e7917a943ef0b22fa5d1f434241bf0a0909fd7a8b4241bf110e1452d7df414240424242ea1af4bedf05fdae90df2d16d1a0103ef4d15e01a036fa4c172ddee25def70f7d7c2dfe209a4fedcd3826a1de8f317bf053f291e2415afdd7387bcbdde61cafb0d57f444cf94778bf76fd7150287e44971c62e280ebc503b0a94e88cbaae343e5d972ea2ba2ecc84aabbd497eec6495952bbad52a8ada7b6f9c6b80ddc564d4a97bdcb3c0b56e9aa8b5bc3469116e9be4e48d100ab6f877b1c94696188db0bc18158bafa6ecf034cb448f785db7cec01fda7ea4191967b50948f24f4d07f41d0d775615dd7b985ecebba1c78df2bfdfbabae6bdfd75a982948ab0a9027288c8c4d09ded75d988902c413be6899b2150349f0be2e6a213d46b3b8735c96f93dff32b02bcbcf0050953484b3b44055d2fbfa774d75bd3d03e83fdf96b683852bcbb22cc3c01139f02621e9c083efe9206028fd19e87de95f26ed9c73068640b0bfb38a2185f4960fc27b97a7ae4d4940f4d43f9ecf77cb1b8c73ff83fb494df9843b5d600f39e77cbe2e9fa735739975ce59c629e745d4e59c73be0f04125924ff0e6fe44cee44d6c9cbed70f595c30e15952ee115189752cf304b221543ed8ab8541604cefd02a8b84026ad68ed98cb70650d303034c9cd0e1dad373bb0eed81f7326e5100c418285c894284c989c56605b6911e2188e3164577c80f11549f31bf98ca67e9a84a0648a1531868d619a920658d68fee82230c941b5c6b4eb2c0e1a2829a15ce8c39520566a3a473ac8186a402e72c8e0ee31c0bc816766638d131943344c336e30d1311266cac9c49ad10e639d272bb85eb419e0922c7828a161a2754b1e5d0f85642e9786104489537534bb458b04a579dd6c681833d48b66c1ca3cb80acb4188af18b56523ad2925ed87e2f58a7aba8206ac86094b541a1820ce3d90f54d559933750b89445bd8d7d01dc5049f912d5560687f19d184626161099f3866ac8962f2419117398020c2770626f725b2c60fcf05941ce2b899d2a29607dc56d9fdb0e67915cbf083aa8551327126006b59ae223ada09b434cbc1a02586866cebc99f34ab32595b146cad6a66d47d6989c565b30e79cb34eff8608331e91b3a548f10d2e5f1853407979c1b1250173a638259873ce39c4080ecce0ce0870be8d9f643552ea980f2c3728d61012a74c9c2d54b67421c58c0665642cc2bec2c84df9945ca4364e595c01e478a3ca1014650b2f3b0c6c4549f1c6880c3a39c2e40c5590d1c21246c6d4f1c204a4cea4a474419372270c123165d28827541953b29002669ec61365c22f618ac4e902b3facef4937c6d1183c71b22374e182b574881a2edf18a8c1833516669b29290bd173ac8ceb478ed8071aad83963f0821627ca97953039c19c73ce5a2ce32c29392e2c65b4acac15e0ec7852272ab8fa4a296793627556006ceaeb881a1634ac385d4d440ad507f9d4d934262965aa525fdd22148478895971720c3bc944ed9c7308881ce370c11882f9ea60f437b3b836ed730831b3664a853227a8c16c658a371590226405982e3749d81045a52b6fbfbba56dda783c7f1a4ca9e40b264a17dd522ec7f01327689964d3dac0d952bcaaa2d675264b14517efc0465f05003244e05acb160ce39e70e160945bafef8f6c718bbf06d3037c9554f7e2099cfd96c5a94a80b62d7e7388d9b9d9f001ff812cea73b14be3e76af06c62f5b389eb06989719ef8c57ca149731a2a589aac708c01e5c7cf9259bafedc34e7c981ad06167df3bd3f6bde4dedfbe69a48abbc9ffbeb4fd254bae6bca6f8ed38d0f5fdbb81ee43ed134b99ee3e60bcdf418b4e3fc67fefbf8d526b63adf5d66f55124359b55a5b67dc5875a3d28bb2d65a4973e661ad15e3e160bb8cc7302fcd8efb76f665adcd6fadb5d65a6badb5f8ed3b12eb2954cb5a1369edb51a6e973f5b5cfedde5afc98ff0557213c2cfc6ef4718e824e5b9f5b21db6faef25da5aa8927be56d1f1ab24ef6c879d9813c3fb1b32fd93fa313662deb24bcd822eb581476e9ce092962273a763626cf6f1fabbb4ceac8730a790aedcf44de3aa7140a216fc429ada25a6fe52dda2e649b6a1bbe11cd38d38e16ba96554de499d4feaadde6ac13ce484bd34a4b4b4b4b1bee58efdc852bd27053a0bfef52be24bc76eebdf7de7befbdf7de8be1afd0dfa1bf70d77783ae4fdd0cf7bd8565ba6b336eecdb75bb6ed755bb6af74db8f7357045b820d8a76b753d34318d48ed930897ca8ffccf332d52d794485d7d6bbab7b5d65a6baded49793c3d9f0b3f16848630bcd9f3a0dc434270f7c96104c1395506d5a730784073185e63fb7bd61a5000c0dcca870e74a29a90ed5d6a16dcca874e74185c3e6ee543273a0c21733cb7f2a1131d46dccead7ce8448721a4c96558247feb5416c9834820c17b404bed4bf0f3a17f8f2a752aab55e4e99ac538d5586b3fb2bfe23b474f39d0be7b0abb0be272d63f346efb69ceaa9f70f736605ccb3a69f8f3aa53d6a97cffdce5eebee6eeeeeeeeeeeeeeae93bda1fbfb77d91142cc2a8fd122f95f00577ad1f67f236e777777771f1ab76b959774a0ff0634e9f5ef1996479d316720199135af3950926b82ab6f6725585fc813c298a489135c9376ce397f3e3dfffb5229dcf9cece5f613be74f31d307ce68115e3161b2c5cd003a946051c58093634b30e79cb3968ecb8255ba46045db34e1ab28a8ddfb1ae05142d29921b0f2d6a3ec834c95a018a140cc02c4329722eacde7035c1d293c93c31f2dc4d55e39cf5e75f0927cc07e1a6699aa6a9ad887e6aa4ebcf8f8e91db34f9b241152f852246b92a4970d45081c95c062a6c564c117e0df101c682b7302ba927708831ec2c89db4121890b82e62b07171b50d25762e7245242142e5e459c34f131a44e12373e16cd0d181629fc4042a69836c4180915171d7147ac1c31c674363edb98b7f18fcc56968db82e2c2e8218638cdd5dcac82c5d5dcaa57cca996e5ad0dfdeb40dc2e1fcef9c2f25b19a33f18c97ed8e31a522ae5b900b8aff1d3a930dba1f8d4d76a4b48b01e8107b826507961460a669ea39db04dbe61320471c1972c2315da438d0ad593ecea6697eb0cd026cb33471ceefc062ba85875b6cf5f8d9f84fde65ba46a1504815b43899218896b129727265c839147894503bbe20e162c3cc9db81c2651cc47d69a2c3bd804d7749b3f82699a72b689246b9b3cdbaca282551c92264474c8099aa6699a9f4dd334cd131de85d002521a9deb82c8903765aa83946c1b2c143cb99e9413129b3238a9c1634295246461484a5ab9111f0931e3d1dcc91a16ee31fd4fc715baaeb6983d889e47d3194176f48a215194582237ca0e318316efc04f025a08d8d71d8b498231775054c10630f67a90739e8deb563b32e42bd972cbd578403adba92d0d65a6bad75fdb784eb2a093e5be9ca5383f88978a97067f4f970917db2d6437eff214a9a92ba5b4b74550d2c124682b100887829132fe23cf6fd9584f3d8ee5452f75bc4480c6216e4187fc199f4bec5d62b83135277777777771f3bb7cd42c449db8f2dbc6d16246774db1e8d30f92a66235988689dbe64ed953e4d3478fe2c3efa597c14b4bd10169f28a1dcafdcce62a6e0f96bd2b6492276f957ffdb775f897fa7f9f6546d26c1aa3708854f64bf5ebe4c3da6b9ba341526fa7defa3b275a2208b3b6292c1b1d2494d1a50a9a2c282cb8d0e30a3c3ca8f386eeec4c151a6c0f9bd19c9c0c238891293ca6817a7b95b53c45878a08e822b34b8ac15665c65955f53903e16582553309ec9e195257fdd0c8c74a46a1cd95c542d29a14c2172c15641c1666c455714b0325d599e928307fb1931695832d820d940115611b2bd99b90263bd9e4ca1f1430a1c154db654a85d5160869a28308a7356b2dc84a52726582773bb2eca8d9a12e55da7bb1bff62e72ccb49e4fb51e291ae3cf7eb1050c1432a2e0c132c686ce9e44c2c851bbee1906306a78d7f6045a7ced61a5b10336557ae937e6087962a517260e25411ed0e1aa8a9cfc8635a6c758624e5dca40df1c0480e35566e70518d9d4d983c41a990081946f1aa82834630276b0ce3921c6181268203fd4564a5abcf976c4e3a9b1e3b5f63c125858218323fcc96e438430b4aec8c4959a203cbf04a052bd2353d9bdcca93ec86142fc06ea040a1460d990a3518441764567429b9949cbde8313403a284c5dc1a152c182672ce9044905ca1f142859fd959a37503adadbff7e3d9bdcddb1af62edb7c3de36cf2f0fcac037b28dbd65a0bc832a51508f7b9779ac784df621545666ba9c89923e279fff16ca535076ab6ad937af888f4abc4ad7f552a6efd4a43252b25a22fa1af5f29872ff1be676bcdc3f3dacd81facd817ad7b583b233eec6eeda5e5395a8e640fde80f5dfba16cbd0ef0eea9122053fd44fa3fccf830b44ee73781edf26d58b5a5222ba32bbc5f8d90bbf745595bff6a8462f78c84bb57f4a6b67bab04c8ddb3b1f5af121c776f2801d86ef7feb44f13c8d8bdcf5d1e766faf3c73bb87dcbd07125a270cbbf74052bb0734b57b2f81d13ae5dd7b251a96c9e747faf56e73404c5714892251356d3ede10b7b5966e6deb74c9adfbb56788dcbad719ad13efb8756fadd31008fb96bff624b7aec8d6f96ef4ccd83ab7b6ad7bef85d001097b15087f4844faa96ae2067b1e2aa85fa9a8244469a844fc61eb51874e076afd4ac3fb64e54b44c3ad5f89789f2690b1f507e2d67b6d3a6ebd0a00b937286efd1750a5bf83b6e2f038c30685478aa07ed40d3341fdf082ec827421a60290a07e14a91f5d1b3a8f7d81c6063ef62a802b5b5f70dbfad70fc5adbfc8ca978e8e0e2cb261978ea858305694e52b7a3ba2f2e1e8e84b1f9ac094869a078505631dc828b2d741d146551ee19794f7631a5aa4a28daaf799864d46eb54b4f57310965edefd1bdb6d6b104ea43873f67a74eca068fbee57a4384d468b84fe6ac1d8d6cff3531a3a50bff73c00da1f8616493f075ad21503b81503380ce0ac9305a09d8191832c6906c60d9cc323b44efe46f67a744491bec4f31a888722d7bcb557a5610732faeb4006dab677faacd3cfd6ff59fb2eb8ad1be2b6fe5337b5fd2fd0b0541786ba164e6e6cfde5a35c7b551a22b736cbae554fad4ac3bd811610d5ce73c6e5eb371a0a416ffc431d6057873a006ebcdd5abf99c76e0e5de990d3068ba4d4933b6da492bc316d5b59d5962a38608eac389edec6b3944bb19c764ca1b7948f614015b86171244ece1492305b47263c5ba7878f355165beb8789059a82ea10a51418e5829b3460e8d2340a2e074b12ab58e45761d9888a922b2266e0d051e951b6788a4a0030d94343f822057a8da96c4d88a43e19823ad4d4a95b63847be9cac2240eefc6b36ee4ca444c38f5c956eb8d010831b52ec2a911c59f45040ea8eb0eaec0d5c9731364166499e309d24064b1595112c35407079c44ccd96592bd3a6c59b2e36362323a516e96ab4dd48073deb8cbc70a7081c206bd8cc61193133eb79211246eb8bd7132c2268723cc93a6de0ba8055b515310b9b33a3a1359c7332b5269873ce59e79cdfac4b775dace8cb961d6fc48808f1824b2266545a59c7e49419526ee0c6d2d51aafc294d1b15036c6a7cb6494792983d860a9c271870d6deb1c92d683ba818c11258e8835034d09861aa43532c94245650e07181642cabc746d6186a898525206488d744db974389776e4b61bbedcb183a6cd6c0bae8d75b932204044927857665d5fde905153074e19a42c47dc0b3a5d6441aa98d508e69c73d6528049227e002942260b96266580944da154544d6688c9f106c50c599c0b2ee642cdc71829b22546d27e90ad918a71e58d155c922c453df668c0b8b10109620687e5e38a768b43e64b0a3314925867e7f62223ac9d59612385c6160b7b02d91417725f900c7db85961692c07b5dc7d8a8b12272d880b2662650f32283c0ac08226ca17911658aec26c5085afaa3239d4de72f020db2a8fb4c048a25d09b3c845e0021649c9ea02d6c91dbfc071642b764a3b4a9ccc2ec850a019637301034e74c4a9985206c7c4164d0bc7b314451c452e532ad0d676816c83c0a3e7aa312b122ce5b1b3f2966339c3cd9dbd48cbe103e92467cc56ee188bf253a56247199d3228bc84cad2d58e89f0e1a31ea90deae4bdf86b0c493a3636448fe53063337ce998d027c582d45a6badb5d65a8f59d24f005fd2b0f5a7fa815a6bfde98fe7ece3a566feb384fb19acd0e36addf861a30952c68f3358a70e312f960c052130846b7351327ebea4ab6fbbcf22292149579fbd5a63e04cd152539124498643cc5886a31e73922dc280a9e0f5363f114301488958a241fada92a5460b285574648901412d1006470b878db9b2a9eb2e2686c5d3629e1493b10b8b1b5aca9ae7640b2c4a49173a67658eb77389d78a3637729c41c172b12435276cbda82aa230c6a5c411c6a6d545c441e58c591c32df91710a782dac0c83ff05cc585119434af1888115b6c4e7490daa2c73595d5d5658d2372a4c4b97613b7caed6b0d5597bdb7242bc01c3020f39315d82a851f380d02a0b0f38584e48814b62180ba82859dae6b418d2c5ce132816732ad4e0e6e6cca84a1b701940e021650487d98c23584e5793b1e5316ee75b64ced8c180850917ec2c66c2ff6b5a423406fd47d42688d1cc7c990afaef022541acb6462413f45f063a8226ee8f1f3fea044fa05758fc80044ff464a52f1853c113570d931e557a6382277e849870925194113ce151c8be6804f1fbcf040f9de0090ac004b19a4b9eff4c10ad099ef8911134e1264a284426c3a0891317c6540f34c1acf23c00f74f5caa38301541574f0fc0fd9de9c6095cd07f1ada04af18211b31e8bfa215823b54c7b3426d0f52354418f49f86a9e0097c3decf24e7ba32ccea5d096d9d7665de5302635beeff7718b70630b1b4305dd480e6ab5a5fe213aa0d3e9be84126bf925987bf73ebc75aa75a0ee6dcf003b95c867b1d0af0f08dcab6f63219614fdfb740acb379fe7c0a635e97a85a5ce58602bb658a77c73f8b5ba5bc49f1f381083497f97bf10cad7ea90ddf9022be1c698e908c517210d322b3596e0bf42fc774a6f7d6267f50216c9ff26d1b89dc54cc124f07d9998afb2551ee62b8c71d63ae78c71d0cc4e4ff9aa87fb438c0f535ad649c3872921bbb92677b7edc9ed6ff55197e9316dd46549daa88ffae846b796706fbb636ef26ad9ab75db4eb875c25869af3983c8399b468c01c64918600c02638cf10e449d9d8934d34c34f368a6a5d6c9cda3893491593b5752c220090325254b42e84d0f610d1185dc4e5fb2426e43451cbbd3f7dfdbfd4ae8f3cc1957e1a1273d89ad4e64a8d3cb333b7b9fee937e7b3d64de79a2399f6b09d7e5f5f0bb1efc7af0d7af73ceba2ccbf75ff90e74cf596ea78531c658ebb73dfc74753de8dfd3d3d081ee9f31c6daf594752a9f9e303eb3fb2d51e81e0c5929061ae99aba181fdbc5e4d8febba4dbb6c7e4da5e39d8fe766cdb63d26adf1e08250c1efb3e10b8ff6d06eefbbd1082d0cc28d9f76f8f77be3f30f5954ca9cfbe5b0bdc2edaf6a805b9773d3ec0d4e76aea4b21b5bc6def8429e2240a0531a499e0f561c7c9141a65aec4a811bc9fdef75d773f628c31d65a6b5d426f2bc281f68fb6a4173fc669979d0811f803cca47bfb9f04c26238672281041edc451f552f0946a3f15a0953cbc367a6404abd01487a9f047bd574a0c2203d1c770aa19ca55992529306676906472c7e792d81c2864c16d9714b982a1f69d47ef891ed17b63daa55a949a51e3c79193dadcfe77cde0f03a8b42c932fe1bf976780f37939e79cf308f4ed900f03a8d61c688fee9fab897d9e2a91aadef5c71aa1e4dd110fdc7d5d9766d38a74b5c702e07f9cfa8eeb276d1109c04c66d04f147d133d81deedbb92964322e8a0ee7daf7707d1a9264e0475af9ea5a6080e54cf5237833a55054bd774631e55d759eed881a8d2f084832a1a8a5a6d831c07549ae0e8284b5a30b4359df179d393c5c74d6adf9a52644c197b98d1b22565bcb2e1c5a5c40757591914d50d322e78b419526709932c2b31266a6538b290956338eb09d79db413904a56a880c54acd97569c189b768c830667cec60daeacaf1b295820b760594992460c5f8cd9cc563032040d2d6ecc15a8e1952e3fd08c4c79890bcb6ff28bb335498997fa988c9a34d8a67ddaf729b04a575ed35e49346dfdf87fb670600d3906c16233125c7d48beae92a80d21c32308b671fe71e07d5e586a7753921fdda7a00afbb6650cb514582d6ae33ff1b957f763163adb4826f4b6d7123e4414721bc2d2a1a4a4b48284d62acb9c9f884f564fc3a7ab27e2131187feb2ccb92c4becdc1fff92ae878bc1f570df04100ebc2056a89db399f1fbd26716493e82042260253082037d04205a7b25a1a73e0c3f507d98fa10e3c3f0c3508ad63affce7c74f76b7e9daacb59f72ba1b7f9ba6cb5de69adcd37558dadb495f3f4256d9ad7d4da44511445d1f3bc497ddb4ecdc468ccdb4ae88dfef9fe3aab33cfb73d54ddd9377f596713131da8b369dedb2bf3cae3a545b93939533dd697fc72e9df5a229be6a9b53eb5763704e0cfd9df4dc88eb3cebd9ba3f8b64eabaef6cbf267ad7dd35afba5fa3bb3f56da5abefb1ea81ddf687ece79f6fd560fb6387db3dfee5ab95cb526b5d9ee763476fdb79a2f887cbfcde0b917399b37efd3bacb5ce658b240a58051021c28fd7910a38bbddcef6cac7bff24cce6d73d8f6a8b664e39ebfc5f857969ff5f07ac0183f93cd7f96bd1037ffcdbc6c5ebd0657e2ec3b92ae497b63bf1934c8192c92ff051a11f5d6990c19ac93f564d6d8eade8bad1c57f90f0f3d89abfe6737b659dfbc06eede22f75eab111118a102463bffadbad84a0749d7a4db66bbcf81f9e805dcc5fe2b5409e676b6fd84cddeb69cf36dcbfa8eb91eac4d5e0f778403af6ebb6d59dfb82b5493bf6f48d28e07e4d99196247ef68831f729b76da6af744dda61b6bf068a49d7240ddce23bbbb3b9e12e819d0a94a0fdf43e01f18fefdd77d7813258a48b811ca8d7c1ddd8f38f45ba3e35da64ca46a150e823e8d8c8df9a1fe4ac6dd6596b6db56bad33564b0861687b60b70f9d32ac17c21f3bc63e80c2524bf742941763a7802d5d7d3b235f5a49e88cdb37d09f43d89d69ea8c3ece9c4b2d27765bff24cd63678d560cb7f19de2f986a4aed3e9743a1ed32d9a40eff06aa9e8799ea5d6ba9d4ea7d3e9743e2129516b4be129075e2cb43b30a6c252f85ae5c070e771678d76bab76fc2697928ba966f5ed2b939309fe7799eda546f7247da76dd920ee48993e227e14bfe4e72ce39a7256dfc249248249148e27277f726242e893bdc9ac41d92b1b1b33bcbb3bfacf496655996abd65a6b9df6ca377ff8b3f9e5af03fbe6979f7ff6f50f3f5ecd6ca6088186a32d3681c45adb069d6fa854ae341631e80cb26800000000a3150000180c08068462b150305023350f3f14800b68903c6c502a134863b1480e23218a82180842180618620842c428e51c232b00d2f69bda36e9e97ff6aa83ef1534edbd7ea56fcff4133fbe27404419cf8bf8e5edef787fe7a78feaf14ed4a25be9dfa51044a9279cfe19240b24f5d4dec95babe6980d56cbb18e46f7922846003927f9dd388836ffa46e5f59b22f019b01d801fc8aa65e39b522097b95f318ed3c7d205725e5a351bd15024ddab68ba475d1513044e9a6ebd55dfd86c085ec5a8ee5548515957a97d12be50233930ecd6ad592cea4c37a7027a2408f012599f7704c6b04ce309512a08816f638e3f007fc9118006b48f6104352f5bfc82945a19cbb8f5d1f3de297f7639076785b2053abbf5ba4fbbd5a1d5e5ba185f89f9ba3b873f11b9e2c9f7c34aa3311fb10f6b334bf2fd59447a337f601f20bf16fb045cbfffa6ed80851b1e70797537a616abfe016c3ba2d5c053c0c1fa1411c470de0404515f212415dc275c6847cc1b8b81bb133b25e1cb2940580be013a8e566c23940c5638a7e2307da34ace83e8bc97127f90656bc9ecaf946b856f2ababc75b177bb7e8976412e02845b5790257e4d5de1b732a84e829141e195fc0ca64b9b6f2a6998133e90298cb2a634f62ab2a432c26188ba4962dd0c8b3631b313e01130aa13d66b0618d98447411530f22e242a23880aae1b9249944f26b84d16608284216df64eda96eee26a064c3166ed845cfcedeba69c6fb77383b2e65fd6c87a89e700e0ff658ad8fdec1a9c3d9235d5ac50d539aa103f0afcb160910050d95500b837550820df01fe604673e5cb2ce7e297cb03a445a30113e0cd7831d769207eac47c5b5bf9ad121d4b263efbd241d6998fd3c5a3741cbb177efc0229ed038486102e42e26dbe3946c9dc6a41b815883a5040e100ea8e46a5d90f2d9280aa5de6bab5eb0713c17221709bb9688669faa42347088b97f95c554696055216067157b84757e7103a37d1d1e98a6ef7785272dc7721ac654a6be96979d26c92c42b170551152263f087f82303df4ab6bfe63c16008b20ff2b7251d64f2cc94e277265b5bbe623ffd9ce7cde5db88963398aaab15748769bd3e7daa178899a72a91da03fdc522e1133a905987b62cfc1c53356b35284ff9334ab314f401c2202f02b8e2a606278076760abc2b32efd2d440e6318cd3d52ed858421ce3674542591405d5704b80686786491ecf9a1ea7bd780bb1abde746e3fe6b904ca3e56604f7eb68371a17c05e40349ae700566b22b3326bbf404438a30ff66b4151b44641aa3bb5248893027a447b64294141caa4d1a7e4f99f4187539c275dc1968bc33f49134ccfa325520bc81ce040de9e5150d02fccdc603fb7d26e4b107b980ec2a79e14d69421898db8f69f0c7cc928ae9e958748ab0e59099a6b7695e71fc9805c6ac81224247d3fb5a25cb58ccbc670eb5e15fabbfd5cf8e261868c2141872970c73f5dba57ebd48f884268259d6fbaaa9567e72fa9fa1384be2f7043b5f87ac7e41f688e60e36979a9774a9d90a8236f09ecdca20776886f8df4da5ca1d11a5af9230794a77d7be06c368549f4b4e58e28b7ec94f1820a2da872cb0278887bb1e42ca61363554cf23a69e0c5591ec0c8ad040ab27f0a4026e9d6c7d5cea7c2d508aac5f460a48d370d1d71fc260a330046b0238680baff0321d9787fa832c50b350fb6eb7d35727a6069ea62330209055adc5ee9d4316cc260df81e5c082d6fad794053edf7d04bb532134f4cf389e5e0906b3146e9ecde89d8d071ff258c44bb72087ca74e6317bef249690509f8807c5b40829592d16d8b29778b8d86f7907f6d4b1e317e121b3e8a714261f59dab011958919d88c3a31e1a86c94b339c5dbacfd4081b60d58e5b9d6aa7a9ad937c5759755af9c931067b622abc0bba016e86c8088dae14ee54dd76fd38b57876ab102a502fc87917d2f5e331eec59964c3791dae1a4e8c5b05951b9b5fe6cf393aeb8c0e70dcfb39d6515070ef5d9dd299372538445c558e80dbf27fa172fdb86b0c57c9869cbd22a4bc15f079dd8024e26c90c229bfcb6b3a4f4b82489ad34e8a43417ec714c5d39445bcb43a725fdb0021e7501bc94155aaf177ad490059c1794e17cdaeef798e902feea89100c39f4c977a8333e1c861675b373ecd135c4992168fdbd51bda4e4d17bf03837ba9ba3a26c7385f674e740b602e79fc2e22f3f5c85b8973ebd8d94bbda7f51f552b658b7f7a31fbb68149c77451c127bc3fdf3ca5ccecfc07576c48f105f9116e824ba4e300d39197c8d009c1d9386e85903f6ec2010805ab997c05272748101e7042e923592725c7228609a33bb213bef9163d4c5e0eb64590d1433b09aa8a9073f921b265bf8a53a2112a36a0250f421d10ae80fe7fd714f33829620038b51c2f16b21241f402ae106dd676302978141818f894c81f4239e0e8c0b038ba5e9f8b95908d4386208c0001547279fa39cc7f654dcaadcd21fe2c69aa9fa3a28b299f23d6ced0afd38d70ad699aead0c361bdbabbfefa1371b6fed32ea051b199b0a7f22e4363d0777e58f5c2d8ff5781ee04ae50e486b15870712de8db36e6ce854a1015c461ece0d8508955bd399e6c441ed6a36016b3710a2fa42bc944f4bbc015c0669e54b4f6c1e57c669e5f1f0901ea14a66656923021e3ecb30ceba4eea20ac5f95616c70e9046b9785f97363b6d7546a4ec459ba5a78f2e07c66cef9e6b2e064f084e019bb00adcb7e43b6928fe6213825e413224a1f31deea484de7a0cccc11f98589e66723df2a345084f014175b045795c25bbec6c3d68a76d9f31bd4fdf9e569762a3e1da9c150ae15ef4eefe29354261177a643de3fb761c7dcb60dbc809f66d9f43a4e2e44c4f4589af5291f584a29bd8db276b51f291fe405112881a159746f8e03901c709fea6499c5dc619295b8e96cbaa180f40b06ec06d8c21638724997aa1fd74c9f1571da51771a0e881285979d55f9e952daaf24ad6033c4f8ac6154dffaec4608b0e3b7226af5cc223feb6a34096c6fffe2cd7fa56eae7934637397347ea22452ec83b3098c7c71e1a98085a689d220a6257e652df1ea95bb0304e9c3a6ec8e80b21f70d0d0fd9131afbb552f0ced40eb37e257c5427fa94912596b100a847a58167e6d18b2aac9fd1388ba7d42eee645badb6d9aa09a0dcc5c956008d8a62bbf5543f6ff1599fbfe2d7cc8b03a3c6be9d68578174f72dd8cf4f66ec078cedd1eb99c5d0798fc71921113f50ae0c4434970698bc52c89e493ae516253181f9c23efec9835d450d717b2ac8be48f49a98c8da3ee7ea48c9b518ab93492053b88c273b0611fd59decd7aa961381edee7cc481c4b9cdea78ecf5d4332c8cd2bcf1544be7ce008de97a87b37db6f08928fafcc8f693e0ceccc61ca68f3fac7e0d2436941b9d3e0fc6c3a3607fdf255cb4b55bba38e43e7545051a0a9e8cd2800339bd747a02c23bcb5ea51cf2c0ba7e19dc50bb3998c7e5d9b11df4f30bd884ff0d7ccb3e7e9cde62d44976abee45a3453c4df47e5e3198536b5b98928c48bccccc259c532fc7342959ad3306174c6ac904b444279de4a5a59a99b7595a4d36d7363c41a2ec4feaae4cc3aa2e6a5665ca0d7e3c4ccc0df2fbd74cb61e901281fcbda60d5e326b69565b32af5afcf6c02974574d0d6ee8c15aba6cd5480c7ccba9a36aff3c31d5b2c28a8944001c40f8da464593c9e0dcd29e394def83993db81de0607ac82752b62daf02b8064439d32ee202884b5081b3de35c50fcd72e70d6d9d86daf46b0192c9bc677cb7a1f322e82d4c1a4135bccd2ee4abd7d7acc5587632aa541c9f142f186e91738d3420aa11283409e347dddd671015daf7d2e187c528e1d980fd3a611f2e0f2e503fdae16abde6df9527b440e373de5b2578c1abdac08c8b81bb6280d7bead2514c7d3caa0d598bbd82da1c7e74102bcc09cd69f944527a30f09b3ab52697a5118cb45db67a897f9fc90afd741ae58c97cca1a06a33eb6aa4b0e2bac05dd7cfe103d215dbfb40a83d11e0ac30a64643eaa000b8476eda0c2dacb1340682ef425b64c12ea9c2998cff586940985518b4fbd9899ec1aabf2e6bd966bbe312b43eb3b5eaae0140bad8b4264a324798259e28ac6599bdd6ac84fe30751dafb53143ff924e14f546788650ab90772846497a650f5e4433f98994e6c2be785eb03f409cc7b80efe5cbce3e3e7935d68f98d8efa689f18eb6ef9453e8516dc5667fbcc07c8c4a58249edca72cb6a2b35b4406206853d2cf8f081d3a9f3a04a5be70e36eee75a0cc179e0341ecadc26984f3eaaec908209268d6c01e81684581030b99b649caecf7c8c71ea62362361081f7e6dc424902b40d9df681c1cf6a0de32aa2347b44301b7f9df5dc07d89c5253444f4a60b98cb2c4a701f010295df27b15214ef975dc851861f4b8389e697e3366a1cf8f05517d67741b923db57ef4f45623f359a359a9b4592a4a0422bdd15673c857225ad41190566b1536a57ad4bb21b4d7b287615a04fedfc85a4a70ece7cec8616d76e9b9a185a5cae0c17081e5d24647601533949f6de5e99ede6839db96017ff20b2b1bed0af5bad5d32348a22c03be8a6f561b5e12ca4d200b57fe4a5ffe866561be26d3ca54c983a48823e2fd90539cbf9310bcafb29631843ec9c0fe9226f19e51bc1b6961ff922412f1acf5812e717d301283707d11a4360b983c5fe0faa73c9ada6ef7b4d8d63bb9fecf81558974b75657928612c6b6d6e0d82737cc55475fc66b1a9550e7a384e9c041423b07de743b0ff94304c45f77e6a91b6bc25e6590ddd4e311747505d73dffe9eb131878a5e568928633a03c240cf0f49fdf6e4258505a24337f650f367a9e8d17fae567fd436ebe3afbc893f0a1086c45c4d48f8588380178d060ef4ee8cabf70aee0e1a2830cae8dffe8fbff830a1831287dffe60a68161c394846ccd499806d8ddf79681f527660655027fdbba9cf01cbf18c6b2ade0e4e88070a465130e72d901d279196787c6e5d68a15286a71bf16586fd8ffe1abcf8f37295c484a77d531548aace9f7541200424dbbe50aeb39e56f819f099d53f17f10a0034c849502f0dda532b4d47c974d2684aed50aeaf2906fa45839d8b8e7c19c27227bcd7456ea5aa636956c5306394408b2e83ffed42a1e63f1265064eb6d740f243f5bc5bf988c23ca2e3eef078ddeb7b17580e74f58ff035275278e05de501e55a03dd67c9dc4b2778ded8296fc14445913a8e35232dec0d5dc48ef9b1dbb537630c9ab05cd90f0d569c25ca5faa9d3fff1d36d9a1a1a755255fee582c2c9df0d4000c1ea813489204d5087fa04ba94607faf297a14ab01e0408851046a94dd9cabe1e5ccf0ed56e749aa4b4517cb67e0defd92f8bcfa897c3835ea8891acfa6320c110fd0b773109f851258518aa96023249719ce01ba2ba1e27f30fe4e655897e2f151306eac37a576c776e011b727d6844cbe9a59820cd044c365c3d7d28040639c869cb77a1d8cd1bc1f9f03e60d317591ff1348677c356fcf5b131663bcd1fe9303bbdad57fcfad84460f3836e35635e2f965b8c6353afcda6128857f5d604ad7b17689cdc74800f819e95a3949f9bfa86849035d72fa3a6ae2b31e9996a4ebfb813d795fcdb42eefca08fc9f9489133514f9a9940ccb9b0d336ec8b98481a21f7d6dfaf1ac45d60eb8fc666353f8c7d5e0c11b00c57f3bbe03bf8902b5ff9e5e64643cdc994cf0eade6375cb31df1ea5ff2ec4bd33e0f4c2cf759cb911f75bf601ec968872e4bdc3f0675cf432b66540d841814eec6e47d638dc89156a9c20afeecb74dfde02f1bc0cdc4e277c74e239055faeb4aa59430c4d709371fc07ee09bfb5c65afa97dc1fa48431426efa2a1b648741df92a87f550e2380f671e89030ef2b9212ccb63bb4380416f1a22f449d12c8232035fb4c03d98fdbc77b285beb6d400b1417d4ca6e9754c92f948d00f61f34ed562f38bdacf438a9c9a4bea63e786290e1841cea579991a9a40c146ef7a2e35487afcb8f8159af90128d75399c271ec6e4c01b55a215cff61b6db91e1496ddbd4da786d2b1b1ec11888e929c52241d394927a122bf20832d9933a62a64f057480e77160d882095ebb334c81ac4fc669ebc96080e77d4efed75e83f2bceb26de1aaa08136b5a5ebd36b26b1622a843377e930c25823fcf065a925d5f11606797105657f9ffe906fb08d58daf6843943755b4e5d60bcceb648ed04d2d9b15be30b7312dc0d3d4d433a9328cbb7552cf8b451f7f50ad4b657c71c47fa704b4b5382621fc6fa0611decaa8e88a64a173e65aca640f60b12032a068808dc7c4b83d009dd7bbe8a0dd8360b819e8f39446c215a580ff1ecbdaa4d0179a0f31cc3e16db203e9e33e8a10353e2142929372ac40320d2b284bdf501ee9c6d294c6845a8b0133f83230c30a50c465256aec27187481c5ab8a1166b746b82c413b83f400dcba44c0733cf9c3acd3946e37a6936c9ac1d9ca4985d7ab6b108220587f19ab7519fbcdba85b459aac42ee355aa62f25b7f4f1ed92ca3bfb48176a9ff5b49df9b8c570683d2366bd3021850844eeecd95f1507c6dd58ec6bd37330bad04efb52a7defaefe4a6b0949d534e7090b334e732ec510ed7d9b7f00b8a86153e4a9298bffe6a5e320abd21084eb01222230e86514b1d8ea4a7c93ebab263c483549e0f63992d2a72604be87379769ea9744493136fd23251b1cd81dbf2a0f3ea51700862a5271af583b2935b30b0ede4b2dd442b68d1065594b2f62183218e6436a94abdf4b0f2b4401121468ff3af6e7ed92377f0edbef340432ae11c63fc11ae266551e2f3615a7c7688a13a7b445fa74e79cdf8b31f6725d200b7dc66980a1c445c6823e2b5c2aa5b535919ba916ca10e1e27fb863955eefbf7a7340d2c7bc7262fb811d7f71646eb656684bcc8e3c87fcffd9661e7a887a8f5f9f1b1328ffc36d9f9f8255774663387f4e6902419a4a1ab3e5f50012b007d8cd4be38d6695c2224ceefe73f6b2121a1c1a5518c39de93360b4c0812d553601cef05a89c64c2b4deb24b0d27f96464f7e31e4074302f95b4a5c2b9c0e485c6bda7d018c93a426fb0e36b81d91c21cfc7b51a42c12098e8ebb802279447c02f66faef14ae0caea5293da252e2f287780fa612b8f6698964f89b88015fc264ad5bed91ecaacd579d039b207c35622ea4de467f4049da37118057e05100406ad9a6bc0e24820434edc94301e245f229211faf5db5356588bc310171090a17a9911e1aa5a20e2110467c36abce85f7ec96aa850abe9f0638fe9ea19d5429beaf250154a546b90ee98e1a9e99d6835618ce0e05e16447438817562ecbde555d3fa0509dff55e6baab98e29c9bb8324aa588fb421145cb2c84482f46e893f8b23825f089c9ec2f382d986e1c4ceb10102325c6191e416541b724d3d7f39de4603e0d0f8830490968341dc3b6d065fbb20bea814c8c7cb152517a0c213e78d26ca967a61a6287d53cc67eb8e7cce51f2a5c794ed5a750243a06d1723e3d11ba2ed6093f3b623d4322464a54be29f7255bd7c2052f5bf6b12c9817e6a48b9c254409d95fd8c653f23a69898ed89a4fa0161beb18a6481bee7fd8c9fa121000303d5e30c76a0503bd8593411cc8ed6ed0881ace465bf30af430c9710c35b3dc790c68ba49031a0c6bcc51ba97d7bfa5f3440fe39b2b2fbff8709d798cce4a9e79ad515bcde669e92c90ba2a06621195d520b16d87177044a32ed703d66b11f1b8b37a84b8505026a49365e40b0bc68c5a4cce0e2ae407f51a4de4d28e8f76bb6ef68497d9c621d2558976b004d66f8e4a5f0d2480ce74b71d14719a5133f553e0aa3e29851143f298412251db983e3a8756063594193472595ee2dcf6d0b8c2a55c7184074fdf070c911ce420245c7f98514fc94ed4794a766827375b8a0c43cc798301ccf982780ad17410e1605cd677e52f23576988317ead5f71327ca7da549b99ef61e83ce1d6029135a38b6ebd3f91ef73617e2f2978a9c75e8b0c530e42e519e9d2791ed4622e8adcb80d17ddb6b94cf870cd27aa80458a86acb944cf00a7a84414814912a58f5d485e4cfc1883ea798352267ff639cb3636b2e4aa2be6c8c63663ae4621c7e3d884b312e6dded3755573aac3c397dd40128c3ace7fd8c1658ef67e7825cd0aec802a471d4cd49463127cea865020737e0a5496bd010648c05c29415964869be13b01e0e15fb5516e0b86bb130a31f5a44762833c705d2b3244e9398741252ac9673e9da982307c4ac45bdefeafc6d544ccf7bec4063f886b44e8fa7069253db855bb1d1d935507c625357118a0e73690533073585686b995da2ae8844d30ce14032a3fff2cd09fadb82d07039207d1554e894099d4caa81d0f49eede612b89665d0854dbf21d08c2243e30b0a2acec7a904722c59c19176f97b55c6fbbfee33c34c298d11a56c341e6d99031c00ff826d1f7029968db2beaa0f17279a65d70b57d991e0d28ea9754b2a7cf79b78bd2c817be699636adbc72163cffd17b48a81a5ef65c55150527fc412c6f6ebf3b60d3970810f3e2f49adf626d0dcd985fd6f283ec28659c55738dbd3eb7ed3ada002cc232fad04855bf8fe6595b71421fc5df6917884f4296f083f0e7401e3c6897ee45fa877d383309013378dcb9f41ed1e5e4b792f5b0cbf2734f753e543b52710b406ede855f4a55e8330fd0d3ab562d71b9da4a104f07aff30423dbc4a23a6dc9aee33b97b84e73c2bfa585e3b5ee154e516a20f78a2d1fe64a7b5c95573033c0206065d5298957aa3931c3ec89f65b651296158f95e2a0ae60aab5f2d410f44943312ebd8b75cab82d1aaf961ca670b48bf82907cbccf909f83279c7fa8de25792fde8222cb0f4b2fc306f77939cdc96375217a543a811581a6d87b9f89f0950abc29d84974f70c2f96fc22962d3927e447a4a30e53a25a53a9c74a2aa8b5aeb585d8ae1daacdfd9299d99c30b224111df1a2280a11417319bc27876db2a26a88b0e3e0c52c88ca66143f4db7933dd1d7a63ecfe705f15ad26c8cd936d3d7e5bed5bf88e7756ecaea205347a877cc2e5c8f255a3578f45315f9b74785859ac74f8f6e49ef692b3ecd49f30d790e3edbbbd206207234b1e35d297323119357608d94315a6894f2cc3460c82908412796fb2b0d071201593a96ba01c07a5c7ea21d11510c9c954df28ee8b3409c019cd5e56ed094b79cfd4051cee78a7a90f3518a7e5785391852797e27bc5c358a4e109dc464f9fe3b712d929df990c4c93b8289ca9c27950355428f3706801f3e43a41d2e0787653fbfd5a7d7cf8d88992694802a522d0ca5cfaf99efa405c93748de63723833c6b18e92c83f7eb3eb87c08b60048f1bd959e611ea75ea18cf2941c995c33927efeac4aa08fca284f7bdcee8c0b6b7f25a7baca1909d56eae6a1b5a135793adb1dd11fcb8f9cf728ac3124722d102673e79ed7eeb43871fa14bcefd00d95252e7fcc4b357c781d643efae1e9a655510a1bf26d1f103dbbb28306e82e9b517dbe21d24da95e048801a180d5dccddc4e7fb31ad0525926da69de554b49901f47605066b8beb16bccc7dc83ac3cc75a12bc8de55df017e9993cc309d9268599132c8cb2c8aa7f3b4936edb2b0c873b5655cdb56d2195efca381d5379c596f6c7509f8c6d2786f7787eb86fc64a23cbd78ec14aacaa056ca3af4782dd533b955afde331b36906bec0984b008c121055993b65a31c5ce4972022d67546d21759c2edb20471f5c79f0b0bbbb3a7be0c48fce7a9edb838fad3697fe1a7ff5a8c45917e6c1bfca202030fa6cdd5844d9c1cae2d699b22eb81b71afb3f20db2498a0751fa4d123e6a30f9192041debbffb427c210d9cb90d290b354a71b19e306ad6f1b1d25837db2f55ea714271a14ae420213bb00b20c9ec04a2281856c6926a236b50971df014a6e9823fd183f817dae50578adae19d0cf7ba66f8e5d5728aad8816d79173640140c35bb636ead955106d81f9b7dd8f8239aedf1524205371f4e34a3f7d391ab14e1dfe7c8c03f7d28f1437c749ad8146f9f899f19445c86e458160fe29c57ad32327ca8d77343168ab8e03aea68bacd803056a7cafcdd4296bd8c5d8c329baaf852502ec5d206a1d6d5207af6c54427f907fbc0b12cce82adb00e278e313abcf4092e4c03b06c8ce93a169cf10a753668407c47e0bc7b83949a52b991ea115fedb74a0984a918b96a9712bc9bd124781db705d3c18d9046cb9924cf9258a8464ce9c77346d763f01f36022685dc386d3bd85757433640c88537ba003ea02453a0f1683fbfee47079ff366835f480789584b01378ef9f07bc7f5e382cf107af825fe9452eb18bdf0fc2dc0b027ef00d9b49bc6e5712c60a2ba74b669a917744a894298c8f9d3f79c59929ac297a22f8719e0b32cede5e8b46e5e6d0a803b497fb7e22e2ec5a1ae74370ca7dde14eda18450b5c7d157cdf03fa0688fdaa6750c3d9eef2d048a39cd98fc6b8e8268b510058cd0800b3f0f158c959dcd68e1d0c53bfedfef9e1d2e487550bdad2191ab4bf424a21b551be3ac5b868d899f58360aea70120ba374007e32f680944aa52049c4e65ade61f1c4584673acbc12bd0494f7e7f7709e03414c66da54d7cc3efb529ba7965f6c4da75554918595f03eba481e0b72865ba3d52d72230cb67b5590c0dbf0ea2060dd351c5ad3bd7d431c971434f1df7a132a4e284f1b52e2565eeff67de9faa7d93c56792ac2b69da92311ff9c76238c868e39df5d1ff61c94ca0e5d64e8fd6f19ed1d9a502a5e64865b18feda86791b6093003f07759b81c4ae371554464175f2be47b9a90760a1bf69736f5ad6d5f433380d56e630a9e46e113fb9b1dd1ba651cb41d3ac969f9f167399712be667dc404f296f2ac4843e3efe03bf4627c74f9ebb8bb1b656168c20bfabe5838771a4822272f68cd5b989fccd26af896c2d5aef0e447522ebc63e63f43c2d950ac83246d6bdc50ddd9a9aa06cf6054b4d11d1c9aba49859d5566d57b9eab81cfc6adedad6e19397c1c7fcaf4087e77d320b632d1c1d43af31bad522a0e6accee80ec35b531f2d7664cd48a2210108cb894d80aae626dc063ec40b45df8eb166f490b3f0293557cd94decddda0b44c92a872370c65c9331ef388b54b47403db17ee30a0b6f97681398e76790dfb2d4d1f6d256b34ee4ee7b6f4094c411cbdc70d18c8df25a1cec1fc4372535df2a69d555c43c32cff64dff5dc2d00b6b84922aeccbe979a1bb22661ec88a93facbc237fd35c1d13795cd5d3ebba6a5a8ca8c9d6f8f62b3e5faae76aa4211242f20f424d8eae014a11215263de1bd14e04d46f1e862e5e48ba355c843082f002c7820e6ebd834a0f33cf627872599eb081914b0ec26b666b7dface2ad41f8824ee36328b1002460ecbf6f4d5affe08b849b48307746913c67e058a655a9aa5b848089d3dcb9e9b644acd038dad392fe89acc480379b1bdc6ae154869b3b4ea47ea5648eed70a86355101928f9292478ff34fd06b33762e2a6de651d6794271f142eba5c8c16e6feb411df35e64de23bd251ca009d0c6bcee8e750388a3603d4743c3c998a11891ddfce1b1974c9a8aae280b1b2a96cc98d471c8a53b83e5370095d1c5ec73e456e427d695646e2754ef807834ede1f16921549e2675b43ebe5919c24b3088c0887ba89fe0f19c8ec4e416de2384cf7c57be610edcded406cc6d337070bbd746c7654095bc04ada2d7679940e439bffd9dc0edba590ed68fcf06ee328baaed9852d19820211f9b278422216672204bfcde7539bb9f31698b959a79854adc2737cb553924f900820bedb22c604f71a02193dc821bc4bb8e00b381757d4520a4247fff15944b16c6e2fb5fa100b7e879566423ca6d55dc272c0e218644d78fe443d4be92b8d765275000366949fa5ec6b682052739274f16e19700d3fef394edcd0a2e3dc506761f68e6439cc9176c62eebabfec879f64557561dba00d6c8b4c163971842aa0e7f5003498a8043a26bd29a4e87661525f5318d80f08e97afc6ca239f2d484f3faea2ed10dd744f07c51354082cedfc2c1f99c8480a940bde7f883c9e053d2986962d6c5aae7e8dded4ba0a52aa5926c54356202020d4f97ed7bfbcbadd84606ff46039dcd8781d5da741926e4439bcaf54a5f3d35816affeca86d8645744e573d30b863bbead3ac28f7a927c87a6b5dff7a0d723d601ed37551fadc30db9d27318a51cd1992ce02e26982cb77153181d7029992aab6139a15ffc3eec640a972a24502f07d931311b3589daca8ddaca8ca53ff8150ad99cc80df1de4fe594e0c76921b1c11e543b96ca7d107e2a5061b76fcef96f47c4a726acbe81ff262357078b0a634fc8b6f62802140f90b44f5be0336cf67a1a20e4db9583b9a41bac7071135005f6cb851be4d2ce173b0669d710802f43780939119ff6dfc469296c7dec8a4f827077f12bd724847d2c366f6d50f3583be400fa514a83f35ab4edd5c239565ddfb9a877e87bac002306d5771a2ea9df8d0a5ab4a1bca523185db467641c8c0fd2f4be1b1c0425a2d8c80963f514f17950c6541fcd9b78c0aa4a9955ae701d6ac16fa665f6571f36a98ceb8390da545c8846121cfa0360348e5c4fced1556e501a41c651dc1fbc7090d5e98ed1fe2fb4e74833c1a9d1c10eec9318c133d293fe44f9d83ff7eebdabc8426fa1e23889594aff6ede906046342078f2b07638c382507515e008ff02dd7afc1d3d53434cf9454030308af7ef48d152bd14032a16f5d3f4d2fc2cd3a5d0a16a902b7fe767926ebe97372b1b93c97acaaa10f4de1c9a4fdc47dab741dd740017bc223c9b1168a809369b129720241741d27edb8de20b4bc07f3102d62cee1ffc664a10d29a50fe4b70b80a34a9f5611cfb013e6eab85fd9ce6fe6bd7f39d2cf8f016f121a993e5ed9072fc9ba7dd66395cf720be830587ab4ba63686b3d29241821729c277a8b2345aac068c11ae7f611c9b4c87f21d9508b9cf6ce3fd3879aef59e3eeeba222c1a5594c0b5174b536d144e67c507f3ce978bc8c4ef000494ca69ab72af3541b68310da348bf09e3f3edd60c81978e9d52a91546d6b49c2c58dac7a204456cea9c64d26049e0af81f6eb2448b5b8e90e455efa434ee71056f0394adc8c1e470e677f56cd4959d4d9e9aa84e0d88e15182d744b6f64662ed6c4e4a7cdde0f1392ef5c9ddb7c0f4a69298d69e1c7cde1d4b744d4ef299ab990c2eb1b1a7ff93e16fbb146941012173f03f988d4fe3481d6022a410131f798ac94448f86280c6e284f14cce178afa1dc5d089d739e64f11077125b496128434345bc195a7e64b2ad17621f32c9f397365fdb274d5a33ff56aa51f9fa24eae98074263adc8d9608bf4498f97ab7fb83198fe298cf8e7fc64367c93b51899f30cfa86c2d415ed8b3e9f41a3af31ff649667656f0ba3ba1248f6406866d669a4cc6cf390a8c937eb255a546f37e5a7318ffd36963ee64e7fdaa2fb999ecf0142b3d5975856e54177ad9c9b042208ff0fb9075cc24374d5b732444ecf8e96b3505d8f6301a4237a08f260819d0ac4afdbb97e873d5f42b4e5284dd71758a642792722592d63634c663cbc23c9688cdc37a13a26af9500cc24627dc06c7959a13fd847fdcdb3890d967399061224ea19a3203304b52d16ba0ae932c30183a089ef350fda86733f958b24f3bb4b847126006b70cf1dfd000a0ef743d3bdec348b5d6f92646511b62b0e5068b8739f7d01b278d080b7b421b064e84343f1e67ae23a2c0fd179271740cebde4464ed0f420d664e99c984dcc47f89f1d55e74fc44746da0ec77703ad06dfc08016a92bc7f228892ff87152731fad85200b2bea75cd3d1f5b1dd420bda641c2f305e0dc0be25b1013d2c9e328f9e0b08064dec0215410c6b52db1553a5ad99c330f41ddb2a3e9d09e533ce1c90da1422c636da61c8e77abc7905f51c77cb5101745a052f6a97b7fb5913346ab7245eae4f3242fed9d61735bc26f5318e7b04dab2640c56af74ccbefdc4c2240580a6a959b25e8b1d74a770e6d1ee0ec941efe0d595c3fd26cb61e55dfde2ab9d86ef190e36b5e7dc7476c22a4d0c342891ffe74678cf1b8f3dd808d1acc42a54e76ccc2233a54b2cbd0b6636621dad79360058fe92f593324442301cbd062a46b036769af911e44371f7d8cf8e168d14021699c7fcb68153c18267a0b144cebd5ac0db96281897dd262e0e4d9d40f891d1fcabf1da1a5ece7b9070c224681b3b99c539ca2aca60d1f55ba8d8d2d1528c83a64bed8a592bd51f6b76391367324686b4eddea1661d070f9546cd69d1af8ee764b5fdd58dc5c4dce097d440b70b0b02f6b5591bfc138785b914ab8cc8235229fba7ac292e27db40b03f9c249ea2d5ecf3201d09364b4b6f55549c93dab4edbcf509fd5a9ad4e1592e9972b3448ea88a6945336559566220bb11828a785e982e80b8a61946c39acb91a9d6454dc37925ebb047e53ffe354b34ba6220b3df753b81fa0af558b3587915d993848b0256cf0a90f64e614a8088122fb59497a39002f463da93dcbb769b4b0720df287d7db3e22f2910b3bb29359e62b8df7f93827a6532833bc5e5c859b403df66531422028976299aa4b71a797dd8edb31bcc599a8e369ff530f4ac7769e24d7e452147cd55835f52155d8fd4fb75620baa3e04a3a049011141fa9e75af926ecca48d6913a152a309926f6a3a06c4778a849c9afddb9223f446ffa161718bf0237126a0ba4be80dac1c93b1a25f1c7b88e99517924457e04aa7c0a59386ab53a13f63e1feacb6edcd41e2b22d54b39dd3f584f57e33bb7ba7bc42aaf0017dbc0567315c86ac28ffe6e798c91b0c6bd8ee9820884432d927dfc2acd87ba20c39322aaff8b11aef8054e4b9eb89d5078a0da13e52c271d31b10d077e3c5ae0a2845754d654b4c8ed6f0168649972f119be129e36d22008319d36b8b081c80a3941dfe08cc876a40bb29f2658006ca1bc70734e04db3aedbdfa3b86a12e4bba870e9a3dc4ae8b7caadebb69b442f8e183e9c19fd35b5bd04e77d8a1a0d8a38b3a20ec2162fd530a8811b15abfbe8363d24bbaa31359c5e251d261a0bf0714cfa3c6039f9aa33059506f11d60b7420e3aa0faa5db2bed6ae00bc17934e95e0d2ae08fe2fddb990bc9d2bd687f558027ac5898e74fc2afb2c204ad1b01345d74fa2a7d0a84343b5bc8555668fea312ed57b8cd3f376e06cbb372df601836bd7c2636d2435432390c5b308ee17613fb5784518025506711449e7c69c72c7e2b46ddf2b35505492d4c007f1ebc0730313ddf08da9e5a18bfa936046530f5a701a16ed068a52c4a6ab23695c5b9a1a2f5118515bfa45c7a93c1646695d4970ed04fb0999f1cda7d7d923790c0e2a0702a8f87d39a5eda9eba61a4dce03aacf8f9b16c1eea53a58bcb9c8a6da0d1b0506d938999badc73f04f6104bc8a70e29cb7811d125608b8f867ed264730ac1ffa4a5798075088092348a08ee948ae1a84b68aa66700f7e4be528dad6b5d912dbc2ca33411b23ef921901e640c75fa12ac4093be2fe4acc254736fa7dbbee92ac9244a01e258f127c8a61d92e2abe4032bf9e292ca76b0f0cb854aa52045b756f442253c03547fc9b2908fe205c344b3009a7495777d365544751fd753c6e7967e16bb4e77fbebb22e403a709d6a348f46a7c2227793a15ea648a3f1b15e1623af0911d8d9f093d3694db00e1cd4a573df5933e27d22f9bb450be8dfe5ae209448eea4186b4b15e91e4dca8d5f25c01a991f086bf2c7c393e3283895445487bccb51b42f83a4c65dcc3a01e50258e3084506faf29b75ac5370ce1c7ed114cff5f69f705961d671c1516261931c6c8afd804b594d242ab1ace0d260582926ad954e45ecb9299fdce92b3df8d40f85cc8c46e2dc0a0a7a29324615075cc01e1d479d60721ec50f217f46e62e15066cba26fcccae93209b5328557a3b8de006dbe7f7f75b530437d86f7ca7c46a247879972b624448698931211c6354117c82cc29251c828017e803382955229243f0a6b5659bca27fdee10a9314e96aaa29efd945ca220d7801e245baf26b234b08b90eebfe66db1378cca05f1221564273f03262375499a9107566bb7f3970d2f2a295067910b8168fb0bb6bd382ee92ffed8065a0d25ba4a6218be9b0a1e362fbea3390c9bb301d66dda3ca33bc0f585e3f6ae1ea09c69378774e0cf4625448a2c08805ff13b2543da75237133d28d0dd53d8a15582bae7542a8c2b7717801bbec88fda388521ad87a61f5ccc8e8fd550865eaa0323c5eaeeb63ee7950e70bd541f97b6dd3f7bbf64e33da629bbf9a4fbe0a32fa5048d12ae9336b3a5a7c302e72408c80eed5d26984ab1dbcc5f90f4e474d2e1f284b9302cdcb377ddb1037497e617bcaba838d9ca96e2970286a2a0f67f58e36f638e03c21fb8f93769cd5cad40d7fd41e2b2ecd64f2a3044f74ae41e3ca0f2cbcb2396f64b1c7dc5e76e2e60b9f8e8d891a2f2b1ceb54b83218989d695effb604cb46a85f1e74125fa3f909fb3e428708c508f60a7f6259391dcfba1cb84bf04cf93c4021402df6e9679194476a6206e0c3e115728fba5ff681ba79e7078113b27313802367b01dd40a69fb334e0898183369c110a38ce50610619646d8a66a51936fa09a3b21115ba041214dff0dc7831940e4aa80f0cd71b6fe2703d75d72d314471661d5de5425039d72c194fc6f847824a031616b4464eec74a624526dbb48439e07e68356c4137fe66414f2c767f6a7824bddb5860e0f6299271b35d5d8ad5f005f3d4fa18919fac4b2adb5e682de0365268345304b2b35eded08c8f31643dca6028b200a5290a2bbede788caaae4c8d84e94d6e81ae23263614188b503c3fc6b1d795b332c14b7c63c218c7dd174b35d6ca3abab323b6e69f7c7d0449d0e554ba00b515b8b8f1c557a3f1721b814ea5e2523f7afdf0b31b6d882bf049d6196d3045ba66255f427a79a3c64546df59854b1fe87d88a8481752b70f847db5da9e125c31a2acfe584d5df41e2b3db2cc9e88a8e4dc1b741eccb872d502e6f238ddd3c19da158628e3a92a222bd66e948259408a4f0b548f87c05393b6b6ddf75b7c82e07838862425c52daa026fe5656cc13cfa7f7c5922df6c9e896d3a4c15e9fed1ecbe39e5b3c607f19dc8eb5170bfaa3ed63714b894a39f372660881f1a195e1b3c8e47e8e41b1d4072429313aa0c8d0900539fe830018161e66cdc79eb74eb6d740a52ab230f71cc0fe1a80be1ac6d27de6e214a2554cb5900efd9fc7b2e576801b0df2c32e014d9307940b551cd988f3fb348e309a70d504e4d49d568347bac6e20e15caad55d037b067c8b5e029d587de825ccd8dce8b96ee9e8d82c7a1dc77ba277a75d93c3c551da74dbac62110d3c151d873bdd926299e56a6b5c06622f2390143e2eb28496227f796551edae14e45b7d07755c7f6ae0f9fa735646ead498d424a46bc89c16814f765a3327ccd2416266aea9a305f9d8c9292a8fd26c9e23a6214a373eb82549a4de269e5fdf0fea2b4cc9bd884b57e68c3d655893fe36b5a57125906145ab940428859e1d275acc38ad2982ebbc93ad7ca571473aff5295bb063cf90f120551423dda82a84a2dc0eb0f10f45b1b58754d53769685f14e538a3e8bc50ae8f92f92e4a2c24f485ad5c7250b9a1cd856dbf57c72ccc4ff145d9cb69f8b5a46c4c7549d0b509f69b847a7d6002f1db9985025d13b26d6340550da09c8bf3e9086eb5b87092e9047923f6b0b44b2b3e24a61f81f1ad3084c3d2bf6658bac443c09c37e31102227ed2bb35a14175818ba73fe90886d21efd49d7cbce2e9dc3ad460abe63c3b6863fa1c0f8ac231c65c6b916e886505490bc39cf33836f3c4f6d9120c93c8da2cb30c3c704acb4106f7d14d306a585d449ec32340c6277dbe5f04d08d0032e073568725e210690963b114c7c5f846157d0a4b3e7f7afc6769f34c1cab04bac40b98a941b9cfe8ede51e7712f06026ea38758196a702107cad2d58fb2c5218f7e7bac0cc16807ac817ca1bfaa86845c12656e2899ca9726fee4efde3a3f875a5947943497da40316465046afa8318430e06500c4f6cf830b46b858521324d133d931e29325c4d132f0f8a618774affb9f752986b155ed2296c999869119b73516eabe1a3b91396ef89d50810e3a27c27b9566833e1717ec08ee8c745812c5ed4f42d519c563a9a8f152a00c9581b788ca3897a8048831628255fab1ae079e9525b594d2ad5f3edefeec8fe8a6684751d7e8244d007ec4d9bb69bb48c2b09a19b2ca28cb15524f57598427f83f8147bf547d871a8f3590f8f9bce7e76708729dfad14e03344fccb0ceb694bda3e010ff86a7a5d01f6637944444452ecd9e7029702fd97141cc4af01c648236987f72a3f29bdee8172b0ab8cba548b104aa3f7dde2253df8e0b8e0293300b8cd0f2c0710a0f99e9172f9f6f0065ad8a8c83633bdfc0f0cebe9e4c8a45778a719624131ee3a6994f71f6fa0ee8a0c7671af1c89773a859d008f5bf1c0a19ea30f55351101e60575fb1c3c9a60756f1a7582bbe3fb82a27b0805add050d2e6056978e72ff0a396f20b253f8558aadb0561bac25118de01e09e96ddf68d05b8fbb884ad24e42070da39539e44225209112096d395bd3bc9cba647f30910c2e3150065adbcebb97164dc219fc23e099e4944134e029477469c29d32e85fa2c523fe5d32f9afbffb0c16c13bfe845e8314c94fd5a76a1f7d9985d09774051f2945fb1f47b5848c26a2f416841ad79bdd821166b8ee63a106859741f60b162fdd12722ffcdd7e089ec182fff838d7d6132b7baee8255c847ee79822c7a43b7bc0cd5edde3dd7ae0263a327416162d448712743618f8ebf0b034cd59b213e71d26d01b2fbd5901d3bff513e09b9d003a9f00b180d9086f043ab79d419d5e9eeea753dca1c4640c5112ca7e955c4c3ce0cc9096a9b1c41893922d60e826fd4aac3ba5a7b49b212a8186914136f98e4c0d3fdfad389745d884c4e0d8452a509f2a39254c683da7718a4b31972283fe22571f8c0b7d857b43f050b7da0d96d72658c2245769d48272b4e3b05d199f67bda37daea8d1af79f1e8d27617db01836b6441c28168342ace009a7b2c190219523b15e1b951d6bdc930a8726605a8528793e36ba6ec3b04ac9c068a37b3cea9e465137bbd1c7ff23b07ff92832a64b37ebd6c4ebc87595f3acb9d21ffdee8ac5ec83d4befc042de62cf37d3e9e1215dbefb95bff57175007b4566ebccf0d4b3011fa4578f0ace77143611c857721a84f6e1ac6697c8c97a2162ab142f4478d7c4988e1f24bedf8e4ba4e62c68d7f6814f96768642ffd9aa5465bfcfd890c48d3cdde8dc9ba215fed86e52adf9fd540fc5aa624e998b8f67a09fd8e77247b26c84bc2e31bb613201c13c0b2de9ef55bdce787ad1a42335b08ccc4bef58935ec6d978bde83395014a2b0359225ea3cbe1e7f325e53519d76474c181d2e505c64c8293d38c105c2d08660462c7ba9f4d518188b9ee105d5a602ab68eda64354022d264b8dadd649927c676703309013bc58056282951c700eb3da1f73de6fea824cae9100fe2a059f2d3c912f0a31f8f3068c372bc9b2fe94c1f05cc6bf834769af7622f0dfb8543d3e0b01da2a7393ede3eab65f5dcb1c2d7b5e4de0eb4735869f7029485d8c7c93fe78ba16f080c40e48f81f781c043609ff4448cac8260df5bebec17a5de992570de7d2bb14fbcf07e8bcd4e48bdc66f03cbf8138ec314b04ebe069d950d34154418347b3706bd45f642d290f02aa430635c8e3b91c8e3c200e4a38536d6d6236446b7f51346f27df28ecbb2937e647482f91e07db04fa3735797413e97b732b4e3d69ece834be42a681922ed406c5f67b5950e2e40b3237b09a827b6af0d4ce51dbbaa4671b3d586ab0f1f4635dd4bbb0bd61966d1bdb9eb25e36a9413f7df3990047c81b3736e049cde12e79dcdc9dacc14b38ef6832a2f8cf9a82b3b66b9f9800fcc95fd3b5e047a09979c76a0cb691dfe9c4dd4bb0cc0f6846bf54f9b666252874bce69fcb99ad7d7a1b8018e18757f91b5be7d50986bbf452b938e119a4189b665c1ecc759afe06e46846550b07add9440e698357188857796734a15838c7430123193ac2374707c3b1b7b61992022c333a4de4b3ca0182195a48da7d57a08a4e6f1524a015723c34b8f07ed21c7e18a547d230083fdcffb0d09a054fe441d475331f1d9e068070db0cd5c6b54b20d81c823bb33ce19477b2d19650f30a1782770ff33c5cd448265da07e0a8c8a007bd5a4f2a227188d382ff1b467346d780634cf0c3087ad0c9f5b14627e044071a41c71aa150d82586fd7e62caf30da62058c1dcff576096f890023a3c7d4716b3eddc368f941b30d6c2fed0719f452e3a09a5134ed0c087f6c4222ebddd115ce668847f107c4d42b1b0e3b22ff60bb86e1d3bd3d13b07771b8d3f7a33dc5459dfd4d77293a66ac3a1248e4a87a19872816b36ecc7a5a6e63630b43f2fc1dab3cffa2ea858de67985214f213644b40728007666851882145803ed21defb3126c5c0de5492deccd2b34be44f1a3a0d0fd04f67c6435bb41d33e8622f4439a088331c91bcc456886cf5d1a544b7bfca7cca52927d43c0f72599e41bcccaf7702bdff63102f6085ca12571e658e51b57cc078df03c67fdf03ec67c5609baf4bc7ce5a27542620bfc650d4d9492bc5b485d533d87f70a9dd174127c94f4eedb1ddb3df7ed5ac5cf6f528ebbddda04102628c7d6004163befd5f9211063c5c1813af98549f8cb475ac18e32aa62417862d385735af4bd843f435ee739317e5c0adffad88e5fa787418dd33ca4b545e75b75cfceb30bf11545381504ef651150e606e00ec210fe0227b45fc4669aee42f51f5a011c28602c5208adc5109587bda4966b19b97d64f91a76b08a366b8d357d9bb44a115862ea810ce4bf5282230842fde044d1879550b8ac0cfb5dca4627947b6d2c0415a56d01d6534834ff923fb7a04d949d358d463a5f0cec78c9f44ad870e92293a707619bc43176e4448facbc47a65b54d4c7eceb97319e0574ffcfc79ff811a41db9669bf2d63b2627bd5cdae254ec317409f9773382c3706725bb2a489daac5cf10a00180a661ebc02dd337f85a9810316c0075c173d0d50bf81b6c3d5ff0a1159077c5a56a50a2c1ec88581c1cb7d3b5591fd6ccb4f5455480bc59dd58a5d3e1df557a910934775307b6f9f0c785b062f774659470dd1ce2d059b87337d26d4a78811193b974296ed8b031f05e3a819fd8d0f7836b84a02f1986d2805f918e56a9119df0b6cc0b0c75c1322beee6d0ceb5a15115f080c29ddbbf027b49702d7b35a2971f87d75b4cac10ddb8da0687b5ea9bd68326c30f901264453ce8bd241c7dbf4f8967fd25f58b9d94037e67b5d09bbe6b64367b99d9ce7298d1e532f03b4301f89ba095c452439b24aa8baf0dcfd3478c6a2d30b6cbb3ce6db65db5fc487fb1d47e951db5617506e092abf06734d244f4266a52472adc07f0de2e58581493cfd5f5854c33ffccfda601a1a9ce10b3d8605e0303fb4448715820eeb24d999f2ceb82dbbf5bf0c57be6f2a00f20597e38f715b7762126b630d4a747b2b55d778d6b34e956d4e9dca1252bc3e5cdb17635c67381b7329ee1c22e2f98599a86531f2d0339ddac19cdd024eb63a4c37a5fe403099245bf3473f4167430b4163943f4b93bef6082e4f2a1906b06e778f383a7d4da702b1c1871db52d3e6c389decc373963a53153dd4bdbae5c4bf2f5e87883669840662d10134477ec1ae32c9a45d00cf7c34bd2c258016a1d4b8c647e0024cc4ec018e115d975f95da706149f7ad3d4549fc0f5c275fc9e7afe6ff39bac0489693a1bd4a57ba7e566d557f8496cc895b4efe234261fe1f519a5c8904543f4db6657b6af538295ba6bf761d600dc409fcbfc12f2f7ed5e83ce21a15512228068fb8454539fc10d80956049649b4a4a34ebe39f15c49200556640798bb1725cc355c61a4e19b47880e4080eac53658046b457278c2a50fc844118ec355c3794cb9134ae19aa2846168847707bbc8049f23ec269d79389d62c19356d93790c090e873090d145350b3725f30474298b8e13df8cce75fe736fdd9ff597e525083d5f10e89ff03fc7b0500abfb8ec40e717972385081b869d1f6503f23883c4e4e81b799648349dbb5936093c8e91970269fbef7c25a06a4c65992cc62f0b6f6b37539dfa193d41f069f55d24b959a8171032f11d4e80c524c171f72baf8428116075e8b0ecfdf19941f4968e339a73c51e6bdb5d8a1769753e04fb990bdea69bac48f99dd06b86413b811c89bd9fae304f57bb2524819e38911f20706ebe9609bb5db4903547edda05802816b72adf97052991fd02ee8a52c2af5833bdc90635bf6cbc473016461bcfbd8d23c5ea91f7c845094d2caed964dd81fa84c0365bf45cd1a4fb310d2baf92d7de3fb6ad67b3d0ac76841fc2657e75c2220520672c7fdd7b07c283cd8c6b4ce4efff751ffb616749a5c572bd77b956e0cfc37f38d3ffb8bbe3b845981af0431b5d62aa84f2b156d01912f465c538624fc401d7dea24f4d82538b8f17b6cce1642d8c9a409f81b526080035e0cc5a9a0e633f5610620f30183d80e9bcff9119e003a6954340dffede51cd032d5ee06769832fc4493af0e97d4361b1ff60fe962701d57e3495ed0e57ba453e3a08b2623c7bac66832cf0ad8ce9a56e359b547ca35d74098ac89541219aa447d368b3136a572cb7bb1a425a3d0e245b5319be360cf88bba0db18249f8be1c1e8cb62a523dfb8c49b5e6f883af47b0d72f540d61cfcd8ccc96667ae1f91c474cef8236a389a9d100d48ccc95ee0b823645f0c5c2456a98b34e351da828f7dc51d535695f4c5a8f201ff3c7796e968257806b2307d35c1b0235ca8c52c55ba3de6b0ba58668fef800999484f7516c7e1c41c69644e3acb772445510e96f9a21ad8d593179c5d3cbbb3c779ca5552c29ab3aa510a9fa295135e880749a391ca36c8d9875031f76db684ccb21601ffbeb8fc4b64d94681f912a09d0080ae50c3a8a08cdc99ceda4d5f5ff431a203fb80c3a0742da698bd663a4b9f757622d3eb6d0b66e3487fc0cb3479f12d1932cc62c7c2ed9cac3339cf1e56152b07513241d7207f8562c7d7ae33758f3185cf870b62232c763a93ec7e14d2e8314a611d947c04df369932ccc4869fb341d452dcd1f618e75b4b5251875ac9a70b19b7e31b6459eee263f81651d9c782cee8215268190da4e59b970d89c56205188266e37f099f9b6d427b78cab992c1d50f78c3eaef8566fca228e6c16a7fbbdce2b8e3c12449153b49db843ae5ea8bd45ff5e4eb876f492a0e15f6a0787b49f170c4717cb92988e42bece588e4ac24485f63e0b3e67acab7b7a1bc5ceb863d3267d05d0606430b280179c580ef468cd002d54c08d189a6c3e74019a299b7b30f81df90fc132fe5051bf1cc1704769dd980d4739e0d09243ee576771d7c90b10fbb26d004076a6d80535e90bc7ba484a7564c08acbf0eaabe6b9d85441f8146c0ee927116c33417f03f480c68d9cb500764ed62a1078d44109b59cc43596e1224a3bfe105223214a381a810c6c7bf5ebeb1b8d1ef008dd71dddc84f416a539d3f0f56c26c91598e3ed1d00d47db09cf0dd9ca4f1d2767864b4e72956d357e485cd0a6ea6923d928adad258cb8fca79c4e63415c6942ac2721a6de4811f7eb6c45990820ff7b9a3f4b1d0118f5d30ea73545ad2c7f8e97cd93fcd8e8572ce9c65371472b2d85ed9eb930b59eae0dd5d6ded3daf8909c759b73714bdddec987dcb9a181c354449503c165a8605db25c519a6a07b19c4f4be80fccc9b96fd00b1442d5493b925725d0e406380f5a61f47a4cc33bf77081a8536d635f6ed380827c292b9ef20fb2e486473709eb975f11522920759692bd42b35cd17a08278c076cf5646664de9a01650d225bdd8ac1a44103b40fe1ee82f5ad01c14919694ab60320512ad2a909995282e18486b031539f50e6081655e634be7e5506351396351448389cc8e8b32dec3dd40c8a6f67d2a710fa9b24bbe6e211790a6ffb60e8c41aa1fb8404e520af1d7f40bc2e0e374c6c98d959c1458357f3ff8d4fb7fcc1cf5cbb4bb0299706d1b3205320a1c2a283207b8776a11b900c16535375bc6e28cddb475f89afd25dd3c159ccfce8593b6fdb3989f5615f60a3054d8cbf863fbba8307ca1aabb7f469baff061141a6be60798c764be691b076d33dd6e3f6e2bb26d5373d4be3196f4bf12cb4104edfdd5b7e166c599c30b56c2322fbbb66c21e9161d143353aae68654d5605f87ba886d1ad7d39e9c48564cc98f4fe86854f81bbb232b996a332048e1fa3b6e6aafb4b740396ca46bfa38f675d6cda008b1d9e23f50031ecdf8e9d427887f8e419798931beb3df7a771963a5511c52f6f439e91048cb9345cc3a47cde3959aa837f98f022f78eaa726d1089b8026fcf2e42b267be6246af8d721fb986618693a15fcd0d4270a91b738f8e540f22221d622778bf20a99184cf6ddf264702c19d46fea7ad6225a18ff6c169344046ef932b062459fa9419a70af58ad979565759a6fe59dc63864ddc5d811b9e93b18c2c7f8549dfc0ba8c18a0ffc093386b8589743c223416473cbb3be5d16b90c8a9f170d1848df34b650747e5802227089052a02e11dbee52bcf723999e1ddc2a4ed7762d42321de8e067d3055e836877e1e8b768b58be8d0f1f6113f5fe434c4f12d39989f37b456a1f71fda15e6f6768b128c8ef4e6d548ccdaba6733ff6f97ad3b4677abd77684a0377c6fce86de6f08289dbd8475768ec2ec0e9ff5782f03ee945ae40f71e4f1ff03838f980cc6ea091e13a7905c269e8bdd7341725ad0ad9cdffa57564f235e391610b6f4c0b85cd3042ae39f17a6ddb2c34991461c56f98900db119e474710ddd7e1bd4cd7f4bf51503849f939c203d5c4926a8a49a9b27db98ff0af7cfc08251470c8e1cfca16490ef644cfcf3ac63bb3ba27ebeb2e9adfb48b24b3bcd0846f302762ef3212f33f59afe1dabae57a3925317237fe34b501526e451d5b8838cebd1b98b9919cacf9782288a1c3c9ed9c015237244bc855e9b9acf9d8dc9dcab85aed56dd71d3e15be15f12f3ebfb3878638a75e422411834fa08ca6a66e6aec30bccd0bf8e13bfa25303134b16cdc0c04ad579f8d7420728131efb0ac977aee8a4e8a2a79d68281f7d38dc4d13a37a8e1f60ca03b68cf98bcfa6d2e693b349ee2e735385cc0f57e9c0ed023aa8bdd9b4fc12671dfcb50484e8abef81f8f16d1a8c8b5c22ae6e3de1f84e4fca2299995a8d4df7b1d584745d7c48b6a8e153be6c533add27a3231e30fd33e3e6b9c0d52e07320a3085fd89c1f63c031014fc5394e83a08f3bc708f917d780b2ce929f8863a88d1f3f31118ea734bcb166944b7a5541dda35e256fca538b3869d6812819bd8001c3e7e01b0b79181b3d2afd45ecb6707921a0187e407574b7cf6e4ba8875bfc4d5a70b113b7a2143c3fb1afca6c92a5c8e522fbf389f0d0b4aff2c8c0536ee8481a59dc981e78706a2cd1e0831ab5ea239c109fe1f33800d13819e18d6650b494f503831f2b5c6157cc8d5546199b3e6ce12c31771928b550245f4da6f021e905273a3b5ddda9989ba9ce6cd4a45dc904e2965ee6c153412ec0a8923ecc66c320cd9b545af167ed88970e644222a2b13e185c9d4b3ee8cb1fd7430576ab1e1b85c6bee88fb2383d26e0d98d9a7e11faf90593594553d6e3189c6c2bc2d914f83dfa78254425542c421d622fcbae093f537d92c988a95098d41a99eddb7a1d32c987e208dfe7b00b43a400ab217e25ae8045776e34b7dbce26b514ecaf8f257680ae2198e20c72850ad6c28465204fcb80e257ff57beae6f33634afebb99b17fee3f062c240fe35bdbaca5a69bcf6e2465291becb26f0c14184df1ac6e5a26f8a2933b080eb4ea55b6043c820289803d0d304419944ed030afb369c5920ccc29a0e7ffb4b1139f89f88ec9015ac404149f11fcdc0f3a9f715ffaa9b8ef6436d01283ab8013b9f0ebe3e786f60f8c522375dc0782bf7bbf858388392bfc74db7a4f0cc8e75df010a55711f21d1dade7bcbbda59449caef099e098a09b4030e4c0dba0318f4b86fc7204d98a8a334b88b27e7fcf482cc0e4ca1cebbd187d6f05dd218847ccf351ec63eb6f6b1f9f868d094a9631ea45cbdc2a60f99ba6ac6e143a65ee6eaa9403fc8fca1ca966ad68ab38015db26d7e4e9f5e74d56040b2e93bf9f37190c209a376d7cc8556fa8b09d13e73750d8ded46240c00b543ede3cb57218b67ac828f22550b794bb9fb63744bc17328d9f36376a389a1b3452307982f3116c6c68bf4879aa36b438a4a21b281b420196f4751ea447757c85508f1eec1e2d589bcea34bad0b203841db6d6951e5c4920bcbdb5dd7757325572d8ffbd54b6cc5d2fbda5388e58cff66538e72524010b6a6290eab2ae378c96dc0e943bbf62c6a8ed39991814f47036d3f0f7cd692364893765695ada760adb5d6ca39b6ad300ca997214e5f8e56967f477076d13167fee868e0ab5358ce06bed209bbdc07bc492bb5b76fade40fb93a668dd124d2ab607bc8b4d6a69535eb9480211fa1e6a7cd4d106038a0210e9abc610989c9e64676c3d41413028430d9da4031210089ca471b1b277a0eb1ad8d12ef082826ebfcb4b5a1e16b927b7edad88461858d14900f3651d84e3a8d7c4fccc588b73f6d6c66f8d255cb015b8c2d959a27784a7d2af5a118727e9a0026c89a25585fce34dc12ed6d6c5a784a9d3cc55ef0a4d336abb4bce7b68306f056f676fc6e0d920e7332d03efcdbfadbf3b7f4c148fe7a2643eaafbb94fc752fa38bbf3eb178320054318babedcbd6d73a46ed6bd50b63b86c548c0795fa238f7665d06cb17d0b8531a52521b3ad1d31b46cd1f196030fc2b0f2f689b76994dea6694a93464d1362d02086156fbd67c5c33e65c17911411146121855bedd463e6a920317525096988c589961986c01c3182688a8e185d8ed0825e0e4a1de6330a27ca3f9f2edae158fce4aa4c135ecc4724547105513aa32689cde9e41f3d677563c2cd995cc770fdf13aabbbb69e02b0515938a2acca4c04c1829c0724687b73ecde0f0d65b2b1e96ac5f0c91c30c9a5b33a8135f86ad76fcc59b6fcf65b07c91c4b7ebac78b45f11e389165964b092056725cb96334852b0c316313b395b11523890e5928c512703854c18df9e6382312978ebd67ac64eb4702693c986e0d8b2e782d98107f38a0562b24cb93085c50bd21499314cc68811d3f4d6592b1eb64609c4ccdeba5dad78586c8414182ac539d65ec30b32be5a314874e1b236b76894174abedd6b77f5936fffa0ad10a5c9643222b1f6136e2b1ef5cb568f8f01bed431afbcc5e203f0d6711ef2d6ba2fbd75b28b29de3a500dd306262acc0e6f6d6150f0d63b2a3b801c174e7cbf517261092d573871a22156e6b77506293e80d800362e7a00f3ed35efb8c965a883dedebea54294d0f2611a5b44f10903de72053be7f82a613a763c1a8d60f600ee5806f9b27d051ef782d7762000998e9d84e97805fb1ea04f9da49e5d4d06ac3e7de8ca01ed75ce497dc6daa7ef9852e4a345b2c2a66fa05434ddc774a02128236c5e4d389f31ec835267c188ae4828f1bb44921c1287ba2d206eaafdc0df0e44fd46c5a697f969fb6943c192072910100a967e9ef969cb82c983363ca10af9a830ea18d3fa260461e1040cac40a85f347fa8ef4033ab42139830ea6398d0d0c8784a29a54d27cc1f4a9dce28d35293b32682b00084f38e930ac202f5133030c28cddd188c62875a9202c9c8001eca56ac0467d0a0540aa7d09362ac68d4634f62a811b41e7dcf86a52c50220d57eae8070def5c4d93991f40adb31c5891b4054698a4d2abe42a04d5e40dc8e23f0a377c01d5f408c6c54ccfa516c4abb09d7ed68f4f2185961b6dd84a10aebeef6221ff50a70fa800250d30dfafc7c66db00af1246f0ba4e634674c626ce952a226a8c40e28490d83ce2c1882190a2c02287375562d3295d89784e994ea98b06b63061d387fcf4223f8fcc2a34674cf367beaee3d7f5797b36bd99a67773b3919a68d974ffd07fb82a6c86e26b041ac33ba6642f6a5a2bc7711cc79529b440914e55d8f44fa45415369dce3c912655580aad150935864758552e5b06820d624809ac3cd7897f53687d259bf46972c6fc89ec3f9b4e99a653194d22c1c6a8acc26ca0b22a6892b400c40c653029e36b0853c585162da4886ada4a4a1732493fcbf8d965cc0c25d82a62af12ae57afe30838bfe3ab89f5195b01b9ce8d46afeca2362a3663affe41dd04273750fa41dd04ea453e80c41d53f20fea26502ff2e13293ab0eeef9bdedd44f4deb262826496d1dd4454c0ac58cb2438b132aa7a9258e28812186202d84ccc8281903cd2c877a636fcfdbed8a474b7962d3443ab6d3fc41937d727152a6cc1151415491431762da1cb181091cbe0c0115e52283fd5e7f62339a5061d729926ebaa49d7494bf48bae99276d2519a664c3b4de49d1d32155590da18e74c674da5a672cce415f0bfda8fbec71e69ec3afd32df42456db014a40a512e1376dd8a317fefbd146afedc59bf89a24b155074a1854decf6586209544d4d981faccc98a4907ede7edab450c288c630b043432e291447a3b0f320efc540855ddf39229714ea557d7e57e713329b164d7f6d5a3885321b172c487f6b779380fd8563af1f9e7ba3118de50d947009cc341559b3c6898b270ac870860c97304eb520305085f028b351b157092378b96218ef98e23a4a42cc932fcc243931839879051432a020328312b42f5624812aecfa8e29a405c090d1a1062eca04f14206a4b8b2660b106eb08cb9de891ed41168b9c28b530b5b940123769d4e55a10b2081c35412a726c86013bb4ea1aed3a8eb265cbf6067bbae76d57e3982ca5a6b81582d45e2072d72423b332acaa68326940dc9426571f313ca56e6465125ca9734cd771ceda64d1b165afe3a5dbade4b2e9d3dfa20b9d47a94ec0891a91117905a782cb035b9a783286d89e06a92d28795917bdaada5e0b4755656b533dabb4b9fef3105f24370d6f1fef4a15c378dd642369c5a8b1569eaec8a215fb693d5153d7cd94f56b0f9b2afacbe6cdb2cdb4b5b0ad690ce0e3c405e276f1d36832e1093b47e671566b91a6ab1e99dd84db35a2fd6d25a9aa4750a579ab45a92c84db38dad858aa50bc796adb75fa6be4f79bc4f55c8424d226ab34286b747319bbf022fb2f5a2be6dc919387b6ba57269a55e69a595565a69a595565a6975ab855ccad42fa55e69a595565a69a595565a69f58ed24a2b6d06f40a07700ae54ee2645f8117b93da90a35adad8b09d35afc30f9caaadf79a994f3cedaaf54ad46fd2269a1d934eace28d49dd1a73ba34e4d43482da53ba34b570bb994dbed0fa82a04649d4c4d49494545d96cb5a156aba7c7a7b4b496551895aab0f6a929aa1e6dd9b4a7499c8c13038b824b2dd6e2c441860f34aa588b946a0c07554011156bf1c524e6519e20a24d911809b292186eb1c748ac8a465489101c9c8891c06373b390614d9418663112d0d0218667ac8a46193c6d998a91c04b32b17013aba2514745c1a485180974c2c4f0ecb86bb175fc257d7be50b13c8622d7ab045ec8892a0bb8bd49e8ca4a7636c9cd27cdd690acbec407d5c7af8ea447cd9a3044d0f6dfe54bfa26da9f6d4546f40cd9fea1b5075778756855e691a56fdc5a609c949e9e9eb52edab23b17de5a1417d75fba26ab2f28839f387e32ebd33731bf8bef49b7dcfb0cd9f06a3020cc54d06a52c5fb2f7f326839030640303e9d3cae10c73191db6c2f0e74d29055ffad4aa28ed31cdf20029272a4e4db208c309342e484106111fd0d4f0a60c236a704fd42f5e130886016b191c55d840dcbe2b4074b9941652a4ef6e3a4ba28a6f77137c7b5114df7ec215df4e6561682011ab336abe582b5dae008b7073bb7946cd19ecf597a20f7c24a093784d383f6f4ab3c73f6f4a49989c445dfff486a21d746353510dac5b6f2926511d1b8cf9631d29770e4eda818e0c8c5ce9ad5d27b91bcb29055561d6738ba9c886aa426d73a67a97f5c6cac4db2aea30936d94266dd7d5da75e375ba4f38aa72799d5ac8761095b57e9deeb55da7fb842f188be7bdf7de3be7b5a30dc725f2f40040c973d4d1443b2b8c6c7b249728ad9aceb324bf9e3a493dbb7c641904544f1dbb7a6e4ab2a7e18b004e1f1c45e4e9e0ce955c9b44fd9c774e39f10584c315463bf105a4738fce55ce6cb2c7578fb9a549f72ae19b5ee25f61d52da250c7570844df4ef4ed28dcded2446645a2afe2dcc284e95b0c42f4d33ff1554238820f8baf12b8199b2b1340efc61790cef38e1b4df83ee7c617100e3b178eed5461d45def409f17eb620280a1d650d43b8a7ad7dad652b72d199eaca41de08495d6a4a8065626111d9b8bf9d3de4438b5c93cbd342c357fb6a022e27bccb7535cabb0663d0e37b9bcb41a6ec297869fe60fe7458d6bd8463ba8de8ecb9844504ca1fe62c2aa680775eca9f10302ca20031c385eaf19335a4a079a5cbdc44d4ecb4ddfdeca21e79f5b9c74f91277ad72decef425f9b88673ae71639555d8cf985ce237df493d3acc789d16e01cca9fd36e6badadd576df0f07193b52447571818b1921ded88022250725901051a2862f3b5772d95ffacb24ea7f0101c1f1052474d0c1d18885e347e30b08e82b5f8d462b07a9e388fd34cb25d0c415461d0794acc37154651d62ae30eaa0083341aeea3438c25035550b1540298818d5d30661fe4fe5fbafea56961cf9b902e17e019482f896fab2bf7c91e5fe52c4162e5c94b2343942060b381c0993030d424ca1c5a8030d51c7b1267b1502ea14c0a1a17e2ed34984bd558031babf0f8b5fb93a817aa8cabef66c204223c2649d9e7a8bca909aa4159c9e83989629b48aea87472a6b92264da82160867cf1af2906272f4364ff8d25d03711a92f2d139128bb24871779de862cf9d232292d3df5f996a983e62de9e9a95b241d44b34b5a68f230c9a5556a0310c92aac90dfea307fa8db2378e99394946ed8b2770582e75f5df1e038da752050bcfaa95cbd498c572090abeaa0adee7935002d7bdf58e676abd4413dd2a4a50ec2a3cba7059e2539741f59fe3e1ea41ead819af428b4770e7e636954f51fce5b1e1f726999aad0caa914c839967d478d5063abb1a32a2872582c5d65957aea9f98424df250f4608526670c526f999e3a759ad441b718a29e52cbd441f5732c7e58fc92be41ff7a889f7dce8baa1d833439cb70c6945c1ec98e5e074f0f795aa7b059618079a230c309740803056cbe300181335f348961e2a48b2ebe3863848c10244d4c64a4a727a2928af082456cfa340098a7d09cba21298a2ca40c518a21832a4c58c1c5ac4c952936b22c31051a318c5cd962819b35557811449a2732e601c03c8bc03ca7bbe6f439f1fc1b48a976dc2d960db96c91d513e0f2f1a3d54f4bd00ee6778d88eb9dbbf097ae0a8e650ae4473db716ea49a072d656d6e6385c811ff5f6dcfcd0973eded2eb2288e088162f9adeced7c0d0dbeb24c5648b985a0000055255d4ca43fb841647a0a5ace432dfdbd47a9fecc18b6bb95aa762e7dc01debc61f3d8cb233c7ae03ef5403f1d51b05e6f1f51abf5b45c3d3a38f0dbd372eda885b75cfa1f00e3307fc9dd5e7c147d4081da7d586bb313398aefb00553ca611ba9d0621cb20d2d8e809413b9ccd6f3fb1d2dfe9c86b3a97b8e02d7f5e0466cab1038b368b9cce50af53dfffc3ff7404f0bddb217e4d61329f53ea72b1edf58afed61c7b142014435d8616be50009b035459edf8222b76cd9628c318d1772254dd8e036062a561d08d7d96a5d9129934ca9ab8500798ce41e0f481fa010891b0c696e309479fa27f014c9a525eb0d02bc3836abe7d94e2877ed1dfdae1367ac1b5763b2ab0a85de6a2e746eb55a3908edab951fbd27862bb1ecc250fc1cf4a23efa6e85fe6ff46c9374d5d129ee9e40a431dc41378b2eba9a74b1a0b87868c8399e1dfbe79d6f364cfdbc2d95f902fcbc2d397997e77849821e3ab52b1e20cef812013b8e83801dc78fde134ba3f79c25aefce83d7185fed548b9a01a805e412faaa1689bfcc610fc7193dfe8238d92a953ceace6868033366badac21f2bc5ce77d843dec796e048e1e38aefc288647ccf2a398e7b30ab11c08e8acd168886ffc4620a0b39c351a81e351cc0bc559419086a018760b63aeeb3a0e7b1e65003931b6b8c72649ce894dc019febc5fac6c7e4965162997f9c3495455a05f65654df3ed354dad92512ba36e386861d16582214edd90cec9bf7ede64b75ffdbcc9a87801821c38c9d44bfa2f22ac8a02b069cf6943e7a777dcb5239d44768573c5096b9c6fafaf21eef3a759dff5415bab1a106c9b26fb3439451a0fd24b45f1c17959d5bb667b6bd33b2fcbbe42e8923a68ad9d1546af7bf68eb8c226753599265b5bc7b906042f11b0bbbb5e2f6abb33d65b1fc499f58e19a4e012c9d45d44ea1160ad5583202cc5f124e3789229b66a32755793d4aaa9b5d66aeb9c3f15066bb9ea9db5d6a78e41340082eb30c6def77918880e7bd85b92a92fa11db73a733def068238b4ab5df1e8b0e77d383838383820d8dd79ce89f3d5964c07c0943c6d48c76e1a34582c9c9c73727466b45a3468ececd4a8e1e25c2ed775b97466b45a04e1aad5e4eceeee6edad5ba5c5c721d694c4aa3eb462312b05a3834728dae5123d3c8d9d199c1c35379789a6787c7468d1a2e170f8f0d1baf574f8f8d232cb0c0e5e2e1b161e3f5eae9b971a3027751aca2288a3caf1b53b4312758e3e5ea693ac5320ca758ce3274e29e5508f6f1fcc93c5a20d01b2b117a2ab8114ed102718a2d529167883cdd330f71ec918ee3e458e5118eaf78547babdfee7e6f52145720d01f71b47c702e58f1e021f75831c00083c600038a010e1f9f0b2ec000030000802461301f9ff0020c2806175c70c1aa477ddc2406380058f198244952cb2a8fdeeac8a03320613932d0f113000d6030b771448e1b15e4c86181e7c89143b49123074f8e1c3910f0049c3e6537d563ceb9ea7342614067073e968a85157c024a28556295dc6b6f951f943851a244c90f4abc900b9076b597ebb0f781d97b8ae2643b88d29ca9439fe85393e410d608671582d599c119e1ac58a84262ada84ca6449398e8aa099d5127b46946ebae1084aa4523b4368526fde9538555e004fa54b9d14ff09d59e3031f50d3ca97f4a9898f264510306146d5982eaa0368a84b2ad2a7096b9ff469feb40f4fbf5a611cf6ac70136ff2603594c4655746ee19265f027d5d78c8210c1e96b00d3cb8e041c91e1b1d3288a66bca393f6f3b48755576884a92c19fb71db65c2b72f7f3b683137709679e1da6c8b003c6c9b543132334a26aec704391d6174d3b3b1099c145158d3274b8806a4d19c931f38587193033b21534642f643a5b9470c8d072aacc5864aa32122356535138487e500ac3d458488ec8008ea9ad9008b9e11b330bab62f0cad0c02f64c065aabe2a96ba2fa83c1da248e26878892297c798aa8e08cba38b280e08df74c081a9020d01713024a65661938d0e5e4c7571248c4c8a225349196797fb04c1d4420e37844866f8a0b2fd796b81880e2d94200d1bcf4cc63f6fb2344964ba3799fb799381c9f1f386a4830cdb4c3fd3263698db427de8c81a0f7a66021c17b63ca9820a9a11555ac43e31d4acd12266e9a685ca7220f29ddb0e2200eea6ac055f86f8cb11a44c935062662af3bcad8e99baf8ea9389962718247670e2c5912a605035fd5147af54a635294678ada724565d0e31be9db34864796b2d0dbaf6891b0e4ebedde205d850440f3330863c01e648072e45543de1c54c0b266254cb116f306daa42ae3a96f85d549e522b6463d4863718f99ee27683986f6fa32444603123c614267a8895407f5402284c9c481195248d1431eb449b2a8c42612d0740e4d2f5d4862a1b5ffb9ac210739cb7ac58027d99c2f7e4791e7ec2584a14294f2ad815ec6d9380f943adcb8531e71dc7715d11c7dd3b663cb1d892e8eecef3075b970b634a622c86b8c3d8e5c263d8e4ed2a57dd76d71f4d7626ad65e1337eef3bccd7aa30b71c3d1b10637a2fe62847db67938dc50df2d4a7c8f3445ec1da2d6c6da7d57a75b9aeb52bdcc775cec979d789a11583a3fc3def9bdefc3c07c1e779dff8dee8d7ef987b2fcf13fb1a81c66685e1eb9605f5d15e2194f46fcedff115c2ebfad101b0c8657ef1ad78002c72b64edf73ed909b73dc643ff7b9b5e1fd3e90e33eeea3d6fa5c71debd4e29a597f3aeabb6e370bd9e42ebef1820772fa594de79c5ae78cc2fa7014a4c2cec7db5fe24bb56ad17f54f215a5368bd4bd934e074ea2cccc98685eeb6b68ecd8325cf7176e5fc35028d814e82377ab649908566aab0aeaa304a65733611965c52aa3473a6d0c2d55961e4f6924e551a9e3af54a25f593ce3a2e67720f793afd79abe10bf8c5f54d864cf51c5bafe23ae6d872e7ab3ab2c64cd724eadcce4934bfdd7610eb4bb6dcb59575e7cf12997a6941e0f9cee7aaa8b995083c970148e09c44b98b5c7fde6ab8f2f4f5f356c30f4f6f46da3c9d44f4a7f28d9fb71a863c75d04edb3aac72856f4702169169fcbc19990a9b6e46aef8c2c8383f6f46a0f812e8399b9126b82319ffbc199992c5885308469a3a2336b860ab2a6c3ebe191162e44b54191c6660923ec8e2a98897d9075f6638bb3e29c21872eba7adcb097ca8613039fc69eb12054ceaf203909bccfdb475a1dd3519ffb47561eac2051408ded6a16300b64cb9cbd93b76d3f9a50169577bb9ee76d872433a3de9bd5c7fb5d1cd9a0745385f347f9432f6a2cc793769b3ee5874e924b245f6032957648b662d6872459368aec6507fb092187c00fe08e357f8ac3ffa1b08d6db6edb2da8a48e9f6a32755c0730459e60141014490cbe7ce9f070f90adffb79391a05ed7a391c2ee7e4ac42cf177b393a2c0f8360f819e5f0e4501d5bf18c165edd7beff57eb0d6c85aeb27f88c704ea256888d66a536ccad496367de11a8d67a2779af570faf4210c15bafb61bc91b827d9006a79382aef676d803c3152be7e8cc6871393a198705b6685cf05bb13e70b56285e02a041aa1fd39a708eef8941c08b9b5e08a87b53dfa819e64ea259038497bc103ec775d2520d6c01d74bd0fd049895a1d040484b1cf0e14bf495a67a9f089dc24ade35508f63f0f80dea4cdf2c63b7fac736f43f0d5297939ced2f20d24d5e39f4075a8d6fad28326bd61d469d862f53b6ab8af970dbef4f99b82bf6eef9d1df177a6c4df2234fc2577bc156ae9a00187afd5290ec0522e7d92f516c994eaed09a82b7938d7384245cfd731bed67a9be1881b0d31bc94255fbb7c75d5315febd7f925c6b14bc34a6e35e532db5a6bad53cf29a0b35e8680e9abdb9ea1459e5e5ada385e6ce87f3e055b4c64cfa9635c2f96efa6ac03fcbeeff3a20eaa53dfae03482a5faf07d04f77555c5723dc18e89ef83deebaaeeb3aaeefd48ebf23074dced022d7f9d362604bbb30c41dc518638c31c63e5b52e48e661d878e3d8c31c61893d83db143d2248e558b31c618638c31b621c61863c718638cb167b177d829c68e31c6187b517352185f7c1dfb2d3389e863171edf368fd53c2eaf99c71f0631873bdce10e77b8eb3a6adf7a8e3dec55d0ab18638cdbd2ae2bae5804ef55fde8abe747ef618c1d638cb1cf954e984cbda871a8c30eedbb16027d724e5403912bd3b08ef538d87061a61037ab30309388ce999aef9c93eaa0dcd4e63b8ee93b2f3933ef2d297247eb40e79c3a24524e524e524e52ddec85fde83b9ad821a1617550a4312cd2073ba62ac44d7533927477b9a46295841a03474e6ac6986c43742173b3ce3b9cbbd9779cd4fc996ab2f31639304eb4036e8ba941e75d17f3a7f33a969c11df2935d9cd9aec92e64fc74935d93927726048b831aeaac23a2f6a28b924af540795e1649844d6bb0be6bb3bf5ddf5fb54712ddb2e140876d851a0cc9feea9619d73454c0d3a9fe5517fb5b80a618c39ceb92823e7347f3aaf6048ae5f724edf757b1ffdabe3a4bef3ce39aaceb9aace39a9ceb9a9cea74f37a2b04303afbc2a1892bbe3bcc4f9673786b38c43da5b07977830bd0d95265152380b973a68f244c9ed208da95c825358defa16aa29de7a0956bd05b378eb65287bf0cafcb1427275100467f3c7963d5c5215ba9cac0a716e5df44255a13b5561d6f385d2f2654f395585423a96b88e535c8c1bc3a40ab351d93a277bebe0ac83acdb57ed2a81d30714c9e490c729f7bd4e238020061b5718b5549a9cd6e23c35393d478c4cb3b5384d15268034158c1ab6468ddab4c6a481591df8866a727ad300dc5001ed30e4192297df0c841bcf39c679c2a182d3548540c771faf9cd683fdd46c7686c257a1396b301cfc18ae74f38c93b59c0f61abdb538b349d4792d594d7c75560b4d91af1e8269a19dd56c124975d0ddb129d556484f014a3a4c2d206c058724394043401c0c5555d86ca24d0e4f4d7c5122d54492a9267ad0a1879359132f20e91144546d8a87592d8b921e4c6a352190f47822abd1d003081a1354497a08c1c48417244e4c3cf9e1072626663cf46892b484191f7a34412d21450f2292968092a4c710b52578b0a1c70f511690f1995962081a2c5730b18291a812442dd7f879ab5203b843ce3f6f55844c41050dc84576fdbc51b1020c93777edea89800f6f326a40440736ac80534b33502e420c242c7942f572421e596c419258e7cbb2bd1e5db492a4fbe9d0544c42cc3a7a80ca608e1d1b9bb6d7737162abe6b7c7777dbaeb466f252a85a6bf5a15a5d5f6badf556ebde8a87a5378b071554504e305192445923fb763fc2f6ed24100ddf3ea4c3b7174939f3ed3e64f8f61f48487dbb0948b8795ae2dbf154186043b1e1e7db718662e5db3b9719b0c70818bedb7136e2886f774121f2ed9c47d1f2ddf3dddd5dfb5a2fa8db5a3d3e3a3870eaa20826be3dbb9c041a9a028b299e54f17d23028ad30cdf5e5d59a868c289986f2b86c84f6ea0c20b21664c641922848c7aa6c9962db60083e444ac3bd60e81eee6f22dc5f6ddddddb5db2e4181401d423cd1e0dbf14d882cbedde544866f274cdf3b687c9e76d4291b30ac6095d42485f2d2dde83ab1a1a29c9a6c6d914bdb64422593cba64175108ef338f59ad4412ea75e953aa88653afb30eda71ead5cbf662c3cb7e6aa15e6a18f5eab9a3b0e92f9fbf9c7aee4ad3653aa586cd1b5eda3975dcda3aa82d15ebc546b135abc542d928fb45e7c68d1b376e2c9165707ec397540025b7acca396fd5903ba90a59a60af5d371ecebb88d1cef115f3a3e7f86bfc4dcf3f9aba7e7250446d14269eddbd6506ba27ccfebfbbeefebe1b9a5916e72699b9c74d08d717ce1f87c9673ce8d2f1cb78fe3158c2fec368a6d7212456c21db70f1153afdd03bf115fafc9583363cdc71def9c7b9df70cee7c4d3e7c60ddb5485ba671ccbdcb429af690ce0fcba0df125c24b041bceb90d9f8fbd3cc51d2796aee7388ee344ec9fc80112ccef460ad524f52bb6272aefa449eaf474c90c5a9e5f36ed8e1b2c61aa2dc04c619104161bde1c1d51c4cb9b29aa195c78a1913ac8c668937844faa549eaae1a228da24e43a4524db644ebd4a4689b66e8e4ecb065d13ac16189d649004956a275b24da0689d8469a27e65c8b752d95af5d37150e54c6697a5fa89e3a8025a011617b64048b4806343288268b3c157e7ac0842c9971eb27738b3f8b2d5748287358d01c495af6e5d6148254cadb5d69a66e08c5475f10515628eb450012b7c8061cd0b61242113731f55080aca542b57a11e1c40e8e8a01ec7e2cbf0bbf58316dfed38e9bb4917df9d1820cd62cbb4049972c09f03f97bece100fb0f54beb44e6523f9a70ea25c144fdd3a75d0bcfde0e4a9db281dd4964a8d7350fc5ae5e53c6793789e73ca83a3ff812429688640419b3350130b1750506dda2c29a18109893775906d2591973c5d8239cec5f992a55698bce472e55ca92a8c7ac7d4a4a565c91c12b7b419497269975e7883256c31b57df7dc73cf7de765d33a913e155500c6e4924ecdf879fb41cb736e83eaa967a6550c2f64fca5fdd9654a565d10c3e64beb54855ed46d53157a753e63d9b5815276d22b5515b2454c18f529b5aa0999bd3da5aa423356ab6449d57730e97eeaee26ff2abeb0d36ccddaac937d1a81c63ca72b8ec3188f25779b9aa46ead9397911b49cf3c20cfa9d353b7316fb43658279d2ba0b5d65a2027c5d43acec338bbfcfaf5f943eb12e844d6f97963f284cff8d34685915a657fc84e239ed28692c96436c4a8630ad69e027ce953ea809a57707c39bdd0a83ed3cc3a4bba84842ee1a1ca572a6a48c2c357f72468be3a497bf2d5815af8ea43455cbeba0fa9af372527d4f0d531d0f4b587325f9d851dbe3a952529f93a6f3ea8f9db122d4c96d04aef121d237a906244c89212c862659e630081e6c815538ac8a214e39098c2aab576d84161fe1a5544919cd0da1c05524f5345357f5c9cc861f1871b55108b9cf86af7fc286634e2d167e5381e3f5c3c5fb21df3fca977c63a2bb618eba80a7392a9d7cacd86aa10c9625d299637618d97ea4a35c8e17e7a2ce60ac3ce89779657e227be383f8ad55a85610fc51737fe40727a18f6a318fe516138f43c6cf7da71518561ffbed108a8c2b0371af568e41586cbeae43e63d8df8b1cfbb0c78d27348931506118fb8e25d95faada2a764f64a1c2308b8cfc02e27963afb50e7a9130634898265518f6d7ebc7e724ccd83796f079936fa44c1586bd5ea11dd0c76e67d5a93e592b846c01159b315a0417225c28d354d2a46bab305c9d1e4f6e46bb636381e69c33db19446bd580cabdd67b1b020f54e6cffc1078883261b6b2013b077b462711920e6aaf0e04f4dd532b0890d4b1d3247be20e917c456fd64526ac03c1731d4d768f9dcec4fed50972d9b39ec9963aa60e2a3b29a98356de792b7550e81d8d6a59077d47b1ee7581e0788e5fcf198d86b823909c1c37b28ee347b1ce7d96e49ecd1f1f213247bbf3ea0b4a9e0124432ed341ed9dd3a91cef9c4a51aacebb889129d413369b442c2722dab123478e71b4614347e7fbebe5c7ead9fce97c2536ad6715d67928760e8a9de3889de74ce99cd2a80efa1ce72794bcf3f339b8923d24589783875cd2272135f9ebfc13a95293484d76ae817923237b6f7cb97a5ce0cf263b7f79939d774eeba69e351394fc7d093963d5778ee32504a9b0ce338e7f7e4215cade395dbd46c8711cf7dcc8fa3796de0804c7f368541d673ca1c23a27754c22cfbb185e85fb2adc177b69fe7478ec2f72775032fe699d049a8412336272a0c02f6aadb596b5d64a6b596bf5a1b83b8b2d67efad96abb5eb5cab75d55abd6abf4c8476b59ebd1f8729f6728e934eb69debbaae92ee6eb177bdcf7e6005c3feaa0bc8dded6857acc9fa3e967b0d24c2416afb030a369d2358dbc75a7beff5796e7cae0ba968829f569e3e948f0f7048451c3f3e7d0ad4e4f49256b7966c4781bb0e02ce73ac4c667dfeab8e3f7ea88e14f470e7794f14cfd25a6bdbed74ebd65afba5b5d6da6bedad160710d65a6bf1dbd9852f25ec64a3575e3164ea969614480710a4d31010955acb82bad5bae5c6177f7e0351afada3ac1cc78de4cc2126cf2d4d66efeaa0b98589ecdb7bb6d0927ec67e7c3e1f48cb62603bcec162128e7dee89482f0cc5be117b128e81a3d77d62d250cc7350447a01c73c4f1a8a7d380cc32b965d8ed3cacf267e7e69bd7ef0bdd7711e08769cce0cb1b438de8e5927b43a1e86f2f101d80a3dc79d7b22de0919d07d07709e738b0237e2ce41803ddb6e058476ab971d7fe0b11b872a8cfb5b7d9aac628d708ae57d1e3ccb23a0db3948559490552e8e13254a94fca084d2a2daa8144d96a5412b95b2190000080315003028100c088522814024ce54552c1f14000c7a98407852990b846912c328c818638c318010620c010680d010d94428106a4a255c887fb0cf0ebb545c860ad3a8aa843968fa20f1c07730e5307ae4c04f208fee18cb16180ab9287cbcdf58368678dc43e092447eaed8450afddc60d9ae429f5825d5c02589fcfcb12b209d4f3da258e9fa248c5ca1e8cf12eb6d81965c97448ae4f513eac9fd837b04daf30a2e0974dbc548562469e446e390fab418ce0aa6e4e1553b57c4f6aa696926836b6d05ba2a33eb6c617115c6026c29bb15ff0e979a982d7e90c9f70af49303c2586a564ef49c551cfa9fe68dafc222f1b76181cff814823d4c30516847349e3dae3255344535835ebae7b68af8d41b277808e440ff283af4ef2455d34415bbfbcc453fe8f69d50e21c255577038dd0bef298b7816359a68e2f486a03e8fa97070bdc93e4fabc1fa5ae26cfa9b4605ce446bd51d58d9439484a8029b1a2cdf7a23574d7b9f09a194cd8fd14de73357d9a3abc9bbd46026a184474d88f156467b4b4cbb4d2224259e1388fb1f12b151a14d200aa1caacddec06b719ed16bf8a2f75bf8e0b386a27ae21cb13ceec03365bc6a4a9fab2ebb64e9473c069bca99472ce7057d7631d517e61191c6a3362ae0cf42b32a327f188025f22b5e716d11397b16e2bfa5798db03c2405ae5afabd44a732ed2849e42ead75e5579ff675edfe7abdbc80c8190919972d1b52ab098451968cd5fa6812f19a9596dc2f2c420853140a8b942203eed13bfb58399636335d215c79aa020c2d8efb35596918a2367aeb896ee71f492bf491d5ec67ba1ff0a6758883e41cf7be05ccc2eeb1a7e185b395127d1996c1cbeff998f0a1c0fa5617d9782cf1de44cda76c7e75bc586f650f2638e10ac9aa5a2b026328cd0f7729b0b84e356a644688d01b3ab8fbc3201bacd876aee39cf1c23211bcfd7e06677cd1bd35f038b11ed42144254fed530378b117c14d05a65b070f15a8c0872bdbd1b8d8a0412fb4fcf6d75d855c92b1d51f72110d854e16e426d05a7bfd878dc8ceced32ffd39c4aca5bad1774880213f695bd0aa8a2f2f9ce8e2e0500ded7e1d0580de6d619a8fd25102550876156951909e3c2e40c4f072d3a2842dac57bcb833e36e87d1bd95e2544df25c25b2e0ea02b38abe31fa0933758d2ec5959c0136b658ea36ee29498b7d266976f92e7d0c00636e2835672f36b2bbb190a2fac46be3289d22fcd4b251a5218845eb8f9388e69c9a20c439de733711c3587d194884902e5ad0af330e52132c7df40548fd36e294cece5932a939daebbee47f2ac3d26cc925182afa87de7385813daa1a5047cf070726ab44e009263b42c11cf98bf443e7e418574b65d1c4e77cedfa31fd3bd706149c4d951559aba57b591a3cda9de7e8205e89c31ca4a3b3c4952e844b43a8e0f3f7f8bf1700efb599e150a786cdba8e72ced6d3e7816e5a614536c0a1eec344cfe6d6000a8a313c7beed013a2f8d7209a1d0dd5d1b98f5c9fd8943ed5bf602533cdee82c44408d1e8ec5bc2ea3084ef613e9f743aa6bb2bfbfd1acb3f5493b8b308b22ec721392a83ed725186a06c66f3b451dd3558a8c81016e18275a4405883f432b5726b32d20af3f43e93915f9220c81ba258ddf7c5eaa38a7ba26a549cc643f6034674d7b3323693d265a83a885a6f784b5eb801ebd8db6f4cce00c8281af8f81a5f6f2f0984b15fb695053bd6d8a205e3b02c138fee08db5035f1627302d60962008252daa3274bba62890f0be715705d9da047d9471a3375a005930e7a303425e608bd6557dc41270295a06882fb0eb2a03c68b658ac4a3bda0814c1f54d85e59b84b6e2e08874e126a54ce0cc887405f13da70aa9e4895ab8be435a5d83a39695dde913c5ea226304b591f75d1b00fd1380e3585fc6d201e072e724ea1cd4c1c6435d9c7a86e02b13c0f08ec1141e277b3cb4b4cb50f832c2c439e975f408f33afa9afde3d528ef3ab631859997ca145220de0c3ed0060c4afd6c97ce9f491ffe3adeed84b99fdc4ed02773c55265a36a09536ae808d6f55053211357bc01bd3887bb215617f9dd6eb0cfd96ec8f6e216abdd10308a9664fd54d6d0f29c6bbeb34bc24074f72494a9cdb3620be99d176d073ddf79c78202058b11303a8743573c365ca5af9bd0c85e4ecfe19dab1aca1d11ef8cc7c43bc79de29de5f51406c51b5daf096c26264e7f32a311bcd00cbc6183b25a8c2de26059438ad2d09e0f4399279877525d37d5b6009f66c16b6804c2e2db79c4e8dd2052d48a4b2a4b5646a44ac5179d045f3f7572dd0b7641d7626e18c4ada231762935923b64759287f892aa5895066d6b63045547c531b0b923268f6d9847c78166c4a6cb8f529ea6804ed1cd960207072d850a13424eb0845526573cca80cbb68e18d0175ccbf3ff8cc980f857b4ac754ff9f200e081526538991d42c42adeb26cb71aa8ba76d1de9288412c6b99d48e305ddfce5af619d60a074b19697dfd50dccb86f245032c01257a31ffe4088a84611f2ef6063a5f5a9b4b5720b8910ab7de49a0989f46dd4396c95101db1401175b8ae5658551b9f295a1f8ad298fcaef33f4e63ead7e1c6232baf748be38632bc32379af2393910ed96aef68e7eb62de52f8d67ba086388dbbbabe5c385827604b3b81e9845f396365c28c75aa6bfac805826fae06ee67fd87e356607db2f90e5eb562997fb0c24a63913a6d8b32f2dc1ce135640d024e2a5aad6d96191f1110f9cc3fc6cfa832b2f3a44a206bb8e14dd8a03065e5e444c673c3ba8eeccb9e6a712ed8c0a105690f344077dddc4dc55f7bb09360d13fe63e833c995b588e756e0121099e0703bd1b3038d3e0d7507bdd2bb86541d9bedbf33f773be7a18f12a6030d5a07a2fbd9d923df4ccdfc740e5cc27341f539803a8ed52a93278ca02f98a3420b825adeaaa6e1935c1685690c2c5bbc88076fa66813d970e4512f10f3b7dc4f3f0e76c2dcafc8d5c20de3ec9a3e118dc76192c0350b4a600f1f8fd5116ef14ddc52fd38db8c6b744bf3bcc9533a6f76064497e960c715f1c841edec7b15d82f641e1dc4ea3a0771a1d4023fd016aac9f189a5ca8829d15eb4f2529fb59784b31b8cca083a249ae1e4a304d41570cd47852a375f1e4185d08d9a6f392ba1922fa9a78a330fd44fd2b53b25c0c1918fd85268da0011b74239650694d3e401770bde933349642206bc41ae7ed5501549d6af20887f301b3c9d9bf0f978f0da9bfe4b0329a4f4a6164eaa1ec7d2f340ef50cd5b0c56057f89d1f38092c8c40bb848d3722ca1dc4414bf21932872906824161ef65019a30c2ddbe1236cce7c9541c409508efb9bf1131b065643314212e9595a04c0f720e603c3153e6df4ae8dfbb1c0f7e61edcafdbd7def87547118eb414fb341ceb68ea6572b907c9f8753400454e4bec2a4a89a72dd70825eedc4062f3a990187785189b291c24de61c4a52894268c0a286a166851ea96cf602c6697e72c3a42d0413a459e2bf90d7bfd35a8654fc87c724eb17f5a8331e02c92c45ced17092deb6a757d1b7e172ad71ffb87c62a5f2193099900369c2e78db59c1b870a8f818d16600091a66b0e28d612bc32b2d710a23b8d744df730a8e5038acaa235b9b74678e7ae364d7202c2acc8d2cf7da5f0af7aa498d2bd04c618e621683a3c3a9be0893414e94c9e065326979776fb81f36e0c3af613951e8225c34e2011fd3c411d4c3b4ce9776579b1a9788a24b095a5712548246d7a0c13a722e0a4a80efab9e0d5d23560c43e7c3c1869cc14217c555c10603ba826b026dd8cc66faa70828b5318371213e878b4f49cb291641251a7083f2d855690852c8b8c905ea85dd1514e31bcf12848135c36e69a111dd3f6fd8f776678563ceb71367a8bbfde26685ba1ce83177ea59dc76b535c82a1e93d4e7dd2f576945b5224fa4f22912d79eb94189c628b71cd96169890734507ac7e865eb0392374d2eaff0070dadf123cec2dcef5334c9c16eb9fc161f53f92a2bebe2ff27749c06e27da799aa9a18061ff02ea69e1c0ead2cded3f700467ce2e49894eaf995112668d988ebceb20435101faf5329be16def2516afdbfc3b0f9a1d7cb393b1a1067aec5585b00a070ed91084bda79ec10812d7dba19a924761c08b3d748422dc961d53268f4bd887a17bc6f9fd572836bc2c54b58e4eae4b1b0feb996a0ed1f7691e1a0ebfd5c9139ccaf7f185033d382715b0dd66f5240c3a751da1dcfbe12da78be81c00b86f0b89f73f2f5dc9a04c25f830553eb8e0954e6aada2dc1b8ad7cab2e157d71b46171eb3b664657519c67dd2196aff766caab64fc788e580606b93ebc5832ef29076d78223f9ba48e0b519e4187f3a4ac124b5c4b7631a9238f7731901799e7e56dcf1317e48cd826307ae254b3d9aafce3ec7e6b3b13f75e110e302a43a6ed8b9992ef1844f34e5c42d7df322dd7af62c439fea08592753218b22a2b5173bac46956aacdfa14efea3f14bb8df39bd1a814ceef2d3bf4cd0f5b05b672f39f90212a2780e754eb6cac5eeddbf46f21bdb75f46c1905ebea3c3084c5fe41802dbe1897bbcc17c1113fe4e351be7069b19074c8da9943768530259f8c21e2f31c23fdc3c09a5d380654a7c00a5624976f62309efe2e1903188b2a30cc19f9a8c7803ca4cd2314b89435022f750701bb4dc010d4189645af599b9980a80c87f4a83fe230c98b5d6c4ba02a92ed6dbd51abea17c0f8dba0b78fbb11befc3d0b18b2dff8b995d7ea498cf398e1c002a80d148977b0612e4a0cf76671404a05ff0bbf1dacb2453eebec041662b2671d3dd19d15220633149699a0dc78ad5c78594d3d5638eab996734f358060099eac2890d8daad07e92a96599548da104bade2f92a62efab17c4d802ecec47a50d74f056d3fc7e48ed4b217c878624df6ba54985829ef36ecfe2811296d8c29dc806193b52cdbd6e675d4cc6fcff5f1f62e244dc70767e1fea5559c57ea569b4fc168f051e0ff7856beb4c4dce5ffa8ec33b10312d4e14c1b57120768336719e4d9376e055cba2405dc9909d2c2fede9749bbe4c6cd551c622533cdc47afd68761451cffd612521ae5668e56c7cd326efd367499667899b4202c5e0d48859a9a8d358cdb7bb6140427f1443e4c60beaa5e58ba1e6c67e6546ebd7ea9191710f4c8ed47eadde5f954cebf83a2dd8942d0573823c2a2f8c364741cb848eeb6876d6c787a5ef3fbe849a5d4f1647e8f1de6e5e4359382ccec07d1a98799a6e4aacc8bad91d9ecc60a6f0b543139c74dc8e272495118bffdb6371dd6c256859b092b282609ebbdeb18ae3044e593a9fd2d29b278c8569536999f6f0e8d6c45c46433d906daa315d7d30be1692d0a2f81ae0d18cd8d7937adc8c4d62a35eb32a665564f73f3b99bd803f88ba497ea4e1354194eac2241617b82de3c7d948898e106bfef06b91410db91162806291c080f215c99d4ba5007f5c6d4d9d6ccf2bc16cc465607b51acdd096b8abfe654df22568daf7e29486c8aa80eae8c6e13b809b6df11311f4cfdd282640ad37baea58fcafbd106e20990b2497888892d34f6808382f11b465bc013b025d16f7f6e65942c80bc8e98d744bfe9e547fa996038dc5aced740b0b6c7004550c494f104779c1c116e59688724231acce1d5bb87042847faa62deac005ecaa153e815c263946bf6a22c41c98d4ed0e34b0f731b08557cd315d8950d62f933da82c95a46fb67bd95d43723ed792c21c0fc09b584d05a2f629dc8a54bd279af7057ad5e3bef80d86ce24091431b9a51707d639509632958ec9368dd93ffc562e00dc9a0f30e0246041f85311d28848fd41c162f3b93ec8c1f473b42991c8e117b87cc259725422a7e84eef33bce0f213ded72ee3e8df65723f163f277f52f4afe03c7773149058d73600d2b279c9f0a1b5866a70ae7f1740f363e5567aec7ca74d83f85ac3216cfadd6b73f4e53533354148831dbdf724be86d1bb2c8c49d59d8d4036b7f61c6ff42578220ac589abf7876bd32025d0e2ac0bfd2406c8ec8c5a81a9d13fcc296d7b1d55ccc5b671eaa9f4eff66b0ed6049055c736f5ffed5e0800b1e7728a96fa2d7db19885972a43d8ee17280d6d531308640f2ea35a66e66895494ff719f03b676a5d406ad3017d177c253c576cd16784a475e7f84e74fddd17414758195a1e75e21c0ff0ff0f1c41bb83f1b0ebacc36ddcc1725353a062a1efb093d862ef035a6d56f2f489280f86932dc81dfd4670c35612eb263c97351f1b8a9b50be7b293f8adf07e3c4549b8fcba031d9060b966c52d6ef19f126509e587f808d6bdf208f2444cf0553605097420a70f3def96992806eebf3e4bd063d5c4ff88aa58be2be3598dbb20fb88a4b7650aeec982a6dde9554d08542522989a450c4ae002047d78ceb4ffcdb10b930ad76746b0d105838e44bfc167fcc757285d29b0d41af7b117705c5bc0ac18f79a0cc03081616df43d80ddefbcddca7a29fc0f382c6988cb9f91e28821044f456ddb18755f3b6946a7f5b485ed6e01f4365983c3c8331d3970be5897b52f8c56490c4a3f313013e71338f9b82d56e58246b69dc8a1e76b36d4387769ce01f5bce79c5e3991c4550051dafbc81773cfec5f17a5b10b6a2e467cd51ab72954bffc76194bc896a40449a6dd018d0e7b94ac835233a3f222b9ae0827140e66ccdd723b19b0d0c1a69885549d2543a424e017c63227121fc5fc82bcf42451f0828fb44a59e25fdd4164779b76d41b997ebb4c4f526b84a34b7bf36aaba28be9cf529dde84fc85a6e0c8251830ae74b903579368182263d446a1cab81e33a45c585651b90d6e77c5526679ff4adba319535d53327b667fc272f2d5442100357da7d6ebf2a3623f059b0812abd98b4d3c713085e6bab22759ac683af9789b8cef20dc7d0ae9d457dde0c667b6e093570899c1b58498e8924751074b7002f2c04f8aa6974c427f2af4f2b6c5ef2aaaaf7d2f04cfa1b6cce7f8f834d7a694a6c3be7372171f18fb973778ec37c2673613adec0811a74fdfd78bd683c81148b1728d6e040a6b909921d36a2420a45402d62fe372bc2eda5527a5e661115d93f8f88527fe6f0ca5a2eba062c1d5d1aff6f001cb05493dafbca39ad28ab50f96442645d424afa3e6ca88af0e5ae403351ab36fa1c1d4413810b491479bd2d28d4e5cd31ac3b1ecb9c6269704af944c8e198f26b336d9108b7edd904c26fca24a908f16f068308dd884261835805ddea978e68adb3850b3c66e5cab0fb422ff3035dbb12ffa1b88480aeae302fecb7d2f92847389a90cf1f6b27af76aa09ebe627ed56633273b9feb945ab049c8004214658834c0e6dfbdb15b55242b7822da763083025fe30bf92709873df1139e1d4579206539ac0a9a9b9eb9905688346f18804c57aa5773ca4ebefe8a996e9a0a6bbe84c83d013e4fbd2d1109a30bf9000fcca8a258a707c7ab56c24698fedb5a12e0cea2db19ac57f9af9e3f5eb2e269a5d5a813913b922a3426312cee950bff0b8a0109e7f3656f315c3711581f0bd2723888e55a9ba6a0c599c184bf89af7685270e72b3ab38b8ce5508a85d4f78fca8558c4718ede1e6e797648dbeca36c68f6824d5c0a01430201bbd570dbeade7a3ed7c61f1ce384aa39997369aa1d769338b88e05fdd0fca7f87598b85141eaf38eb76607d9635c79b0432eb45ed969b5042e5c4e325c7543e7ee24ff5498c40a1f8b43a9baa87c9a9c67667c731b8958f606a98dd6c2b687c980f3341a74cb85de1711ae954e2cbf26bacb09b7e6270e0ee1d183107e55c436a689f62f6f5c8987c944d0ae53b7ec722e93c4aeb3ca0c7c504ba7c9947c2ccb3a5a5819983281d0ec5626f3b854bb94fdeee735915a3b6d36cc4d27a78e9e896926e8b29396397d22b78e7726e699a8d38e5a36664daf76076a264c4d21b74e9e894926e8b0d36562d2446aecb0b93037a59c3addb99805d3cd62ed28815ed2c4dce469d1590a5e2922dfa74d9d4805797754679a269157ac82e97af9b9f638332813ebd5adeac45864963a27eca43bfbd99c2674104be95f1bc7b985c1942297171cfd37757b13b30bd846a186fe146e11d96f86689e41731ea129ec05f28ca8fefaf02b422b41c7b63ff212d4e8f035d715d0c067ae18a74584eb641840dc0786553319404d7165b00423f939adc327a2d3a10249237b7c446e45db03cc0b43d486eb4494c908f7114151163655185ba17f446e652e44d21968f5d02b6142ce2b3e0a29a2a3c926cf65d0190123737ca396a22f30e2bfc7e1ba8c8cc4eda0962e6c9550b0219d14d52ce07a6f583d71e72cdf5ac84c37a205ec55d4ec27d62257786844bea5ce5a2a5ae1b94dfba998a9474fe838e547aefcd2098d03437c8ffc9dfdd2f64fda2495cd86a060e9b17fc3ccee8ade3e52e8c0557811eaef249fa9e8e0d8bed2511eacbcdad200e9cd71bf05f195f7514d8cc2114b53f02d570928f841e2592ee460c5280f4631ab7fd689854eebe8c38cc452421e6466ece862671b2d1e6f5119858adb634993f9186239934897845bcd08ecf4030e771c411a9d22a74119a27c032bfbe0fb0158ec5fec85e6ada58fca550f042b4427238728309ce25280e840b2ee27a478b215ff297d30403d9bc3fbc883a5a17342b1eb1439b2f7f45fde105660a77a7a577b6b579d97aace681b06f6538882823022b2e0cf822855ff5aa2d806e3f3342d118e3a8bc96b65728ef655605fdba1428e8638dd63839c9d08940fb4daeee52c2833773aac613c1a9cf1642f663d6898ba258ae45a0764b8aa91db971da8fff79fe80517698cffb11d90b18b00a70de10585f5d604dcca4eb000db4f8d5f06ad0c4545bb099e154d452ebc9f59e9396c2a3b0d074a67f03d8fc18ab21aa4722f58e0dc10d34f3390639cfe0df26cda27acfddf772e6df4fc241386148437efc4d9623767565d93280ec3d946043e4e8807a8dc207a6c913a323780521163117274a0ba22556f213b183f1d71170734d1456425d1521b4c0ed8a447bdbfa82c5eaa98c188c8c07b7886adaea4dedb1d217c3305e5984d2a5c11846d6faedf58094302cc593011ed9d7b9952f57758d5d778dea0b90bef10084bd16c6a363aefbe0b669bade302c24f8e1d1a367a34d13f00fb7f8a0ab1c67891a8ad359470ac3760350eea386d5e7360ca5023f72d703c3329595664e6fbae6ece273bee67e50e2257e691f82b69d71f67366130e9c41a664440cbcb5e444b59e9c553f8fd5c72ad2bb7145a37237cd9a48e576cb589f8e240d61cef0a303562c493ef0b3a3fb1daf1af2b43b5dfbc545b40ac3423a967df43907ac66c59508bcbae8cb199505200becfb88360105fdb449f2d651c0e6a165f6f53bf9b6001aec9e323550b87c9d31beee459e72ccd09c2f134188d90c5cd1bf41bc66fcc721050990818dfade288b62ff14faee854abe3dd997d9e8836f23cf4b79837e42cfa78613ec29d5c19e9dda41bfa8ff609fd3da1bf999bc511cf6e6eaa2c70dc0d20a3c254c7438cc80bba4e81ca45be86e2922f5758704068913c31c9648b9e3d11ba2e78ad06aa9af6478974ce4b83ad3c1e9195c70a16387d7759c39bc6869b9385ca0cdb8aeb0c8ee509f3c71a2996a7911fac45aa5892cb0de5aeb3d937e4b55724ae29cc3b654f163deb9cd6c3724794506e644bd6ff223a0985f1e25427ef586393de000141d5d4b293192b2338d52f23ee2b9c5067eb5de4f2977a078b9d712e6a210625c8809755bf13462bd0a7a69b812d42ebc50913619c22af86a5e94fb5e00a5491a63e8e2183706b30fc68939ddd123caf387ccd53a758300b4de2ec89fc97a91abf44aba456dd7309be74594d5bb6004bc82272f8dc2219c522039e822e4fe54bc1fe89588b4fae7825aeed4dfd790aa73bc2ef0e90562571c6f9b351d8f17784894692c0e812634f4381fd143f800bf71d45570474de61549bda0f2e22e792d8a3779807fe7f1aaad9ec22310ca23805b45dcf62f43ca8f0cbb3d539daca4d8539deefd97e22ca93296dc9d4d9ff268fb43395216192a8eb8716bf82f0a24686561600e5df760f6a967218a9b4129be9a2775896a20a312dc59cb69d9d0b2048939ad3214692990cf41ffb4d0ee0b61d00b71980592403a0cc647201aee8b12a18d96eff421373432a35b9d820555dcd2a60f8ab1ec4ce8039b5e8f5f0c9ee9bb8eced84e260c02f1fedf6920e51431a30ace0f911a09c04e5fd2fb567eaf9014dde998bd4666e6b08fbd406667a7fe852a3b4218ee7524ac5889ae18bc5d8fe219c554f826a117222617fbc5e81c2663643a0381a58510847e75adb7e4b95a2acde6bacb72793af71ec60022b68a68f062fe42a834451c97b5351548088a2b7c8cb9ac40455321977d4a2c1720bddeaa32f05bb07828755acd6f75aca2fce53704a8d12ef6c171d8f9fab2b09fefb2f6c868f3999f199a3dcb55350bb6400a6e2b9f744a944479f8a6783f6bda75553c6d50a104780180e4826bfd1ed3e52abf172dd10bd4b0d90b90771f331f0904c682a41cb06625c721156b9f3746b56ccb01d78bf62a0e92b696ea0f4e0f9344aa8535aeebcb9dc23f9884bbc01e9b9c2a1e01e1ff2cd2628a21f3cdeaae31feec357d78d00127e2b275b74856dfdf22debad18a625d8599aa5fd8578584d4388722f3a16fcbff1f432037acf75600fca25e2df587032ce9246c85cbaf0c976a5605db108463339275ada0be1aa98c8e59c57d513b3e22903a0664b33d58298546353d9ea058b4bdf4a24d8f152986703727fcf4c9e6ff17108c3af1eb4bdd6309f13afc2035331b242d01c22ceecb33c7d323875271b945cb29aa037231aac0c563c162f57685d894c4be16502bcd99605f54c86eb0c20c9c2ff2a3802797f811e38e4baf451c58ba106a1235118f31ea60ccf0c8a2513a0d52596969b5195ddc574dd13b91e88a3883902491c850a1f5121f5b5858c93379563fdcea0fe4826b73ae8bd69a3c37af0029034df87ff1155b5960967d1b4c53ea6c51c9db23b1a70dbd3d18e6910aa80b26867e5328129b0bfc0ef86d9815ce756c0a6f766f6296fb1ec6c730411b6c74076ff6cb7ef416814f49aff67fd956efe8ba4a65f4222dd4d4588e91a6b61ebfc83870b801b5cbb6cb65c57606c8352dfba89865dd82b6a11afce1c1f6353324919766163a90f6137d71521dd6000c28854069c4cd8b6302e438abb0dcfed13d2781e45c2b411c00a5905b939273d291c2b2f3389b9571f0f54ebebb0349e32032c4737a7c9bbf23d5c43e802c272dcca9c1e382396fd682b77916dc2d2c8aa6d9a941e769b60981ecba0a76ae15c6fa56e01abd322e0abe1d894593decef5f23a61cca2d8d80570b18a5dbbdabd663cd726649d9dee089cf52e0b563838e61b588dddce8553b6a473ce1279476fc18f9fa680c583dc4c3407c14ee3bcd27de1ec5c7b4ec093c24b36ed0495e281554f0a2934c3e301f3f7fcb841960949fa88fd5449f228d303831f6d6dcfd9d94ae035adc03ecc726a2cc12b2376696728f50a5cebe99cd15fea6323800a0613b1f186494dee0d1d4dbdb0e464896de94ca58a6e44d6fdf503a3c5b0c83ea67448a80098ce35809b4efe4436bc64c15c5cc1565100610e8750442265dba381b38a29af159bb5f3bd4a2a268c01881a921daa84181796f7cd2531e46b802969534274dbd134ae91a0f13358d43b97a3c3e7a3c59a07dc2f60f6d72bef582dc602f932bed13d5fe00d9d52763550cb9c474336b53fc287517e81aa8d1a10c9f91f5a53dfd947a89bbec837a811eee62ac7c89206a8edc4721ffb4049f5d03469781050372610f1a337709799bd6fdc5f5c015e86ddcc7fc618f2cb222dc7fab5fd4225417b5d797eb2b891f143909870282f2e8613122aab2ed1bd36c0a17742eebf4e0734fab6b09f93f5b06438b8dc74e24b0581cb9a78ab19bb068a8e5c7d4fa7937dde898f586db2bd425e001aa91058c3d5cd49d3db05867df997655d8176e37286b0247eed9855f39d8ebd26a0463f5694afd68ca8f955a409fa22c96f8c3b77184d9b6eb09ed204b6d59eb5762d48bad9dde94b89875983af96cebfb2041f0ccac300c78146ead2d462ceda09c235187c4be385f6bf716f1762d60bbd4e81a625f7611b5e2a7a7abfca0ad36e61c17158ee47d7198201cd49dbebe1419653d21709ec01dda4a69ea78780c56c8ca6c24e10993bd04a0d258e42ecb0a280b83d8b74e315130aba6a4a064cca4dfe373612c15e4befac27472324a2ab2ea59152162182b17f627ceef0a84dd13ce2fa11b8c0996858d50bb048b28156290654eabf39fa2af7eaada34180755914f20c6d5f0fda33909cf19e177fb54ff32803ea1bf6e928181b884f7395ebec20943ef1da1be1824079da31f82ce92109a201b674de763845cce53bd52706ac1bc3b08df0abf7eb2b5ed1c6689b587bc59db0e827cf64fee229afbbbe13600373a424b42f84eb405590f5a009f1ff59eff78347aaa43db7139093bb58329327d29c073f27630a3d32010e558201654dc2be09009286a4f0c1efa46727f9e33bc8e1e298d3873c3fa6bb3d44824dae5b18bce37de0a4e17dfa2767aaf182079712005702470817efd0f8baa75ba5debdc40d940dc0c99dfbae0e8da094e4b02fea6245cd7027140df3ff0c0b40196a2a0f429d27eeb5eb6c5fd00a2a2e28f22fdce21daa63e61f6392a5c2a285c53a2d28c2d1d6ebb89d8330be4b82dcce9f5abacadb320b6c0d18060d39d68a59b12cf74a9c99f62d8034700c91de30dfc5b73f0c526f70d09e259bf705302db20eb9c57e6b9823ca14de5898bcd7d08d77246670cf2860719669243d2e170d411567b9e8e99e60540152fc280eb8488def9733a7da4226ae1f0e74976f8381b47c49c9bda764f3fc077a138bdbb648a1d6785a28c75debb9e5384636ab330babe919b058e0d05eceb707b860bd116954ef7275dca6406935690dc6557e4501b485d3e09aed589cc15c18b8e1d6067e288af2d8c0e230f6431e1d0dd43f544e0de39cd221f0cd8d247c52de9768c266f2fcc8587c7d12beadad04a4fa58298462d85355a12d6c775f1187b88960609789e40da5fb00956cefb3fb3d0de3b720f9de840837184a0248249579eb6d9c0b387ca3772b6544a96e799129a8c0602421707c856439fd3bb15de65cfcaf161b6445059a6323593bec418a15166aa95d74e29930aa9322463a88fe533b524614517ba14b7da9ed89b76f832232d5f189a4d18563eb1f63182f6b463ccbc35f12c01849ff1865d78f3a2f1532f84bea41637e826c4ab223c7a60b9ef4efcce2435899581d17c4f699b85dbe845f10f97c06923ff5d19e5fc7b6d2e1a1e7f60ed35f81b175a351045473a201636f1c2396c534f884d62b33bdbe28c77646d44d6944e894a49cf07bfcca12fc09b2af606c7cc4f095737d0fbfaf3261ec89a6d64954ee0cb4f2fd739514fd4858da06cbd2ea665027e143412702a5799fe4c20b524ceb45a2154c8309b92a719286dd016f3510e8c470a8fef2fceceb615b3b44fac133ece4fa3a5b8e8b13e1b65984c1b0f432d5b456ab05fa13da06814c1654e904d9c3c32da8acbc712d18bbd2852c5e310c8debb7ab8619a117b55665454e50cd7f5a43331ffd10ef317538abe9a54f3bd44287bc550adfda80f368254b7ec50ab9a234103ad1c5f461857ecc04fc4428c18c35fb5306f39f74018391c8ef12230f8ba20926e9d6db8699da12f8d477f00a4e265b1dcc5ad60e8fb8d519bd6345996c3ddb15d4cf8db07f8f7e3c198135a5518b76c58d4d56bc5bc41a0c926505242d51ca75b2f9b102429bd32e0e91d8f886daf9caafcd9121d1d46a141b6d195a13e1830ba027e22851efce464e77e4350e4da9cc7be0e08f5548fd42c58ef566b8c6a352264ee359a53719e98bec0b2397f60c855e676b58a27dd8d44f4060960fa2fc1544d0784ec5447871681b011e530a44f148a312a41964a0981de7794b9eef78d8fe2ebc90676d1309e85e51c14dcc331743be931c955bd9c14e7fd92df93d3b839e623060de8bd056ed1db03e2fdab36b7415443fa70e6909492c6c0ceaa9325df2e464fa9821805761e7652313b2ba748b5550ee03c80e8ae96f0a6989ab7a49271efa6c0e5f7c44b0c9b3154d2447c55aa4114da4bcc4a2ebf1b2be40ecc4c1de5ea22a1504b24beef086661043441443e4244417bd33aef415c22f2914ccb1905d729b4280e1e7e14eeb278f29cddb68d9f094dfec97d1dc1f327dee87a7f9f4c698d5e91def675a7243ee0f33bb79bbb9d637d4ec5d8c1a72588643ea8e469ccbd6f047bde415fd9f1ff63cb473544ece9ad910b97e793a89e1ef1dae04e7de97b72de7cce341d6229875b00dda95b9a5f7ab8131872478f3322137c7393a3cd6f27b4a3c7cd3c1227a36b435b33033b10f45bed505b54686572cd335612394ca863f9107a593b2ad1efe64394af5eed725ae00b50b45da4ce328ca2ff3ff1a1435a7f17e15bb17c59b6c5521153912300214355504456a0f0481671d745cc79ebfbe2ac3553d66444786202db8106d4d1129cb2861d87aef057ddb1758195f3a82d1bdc061e4c9e8cc5c8d0d63201bceb5dd0f151f018d9c8d2daaf20b11be6f27a8ef7e36cce28c1a60723015a65f07eb716b2fadec22589b5fce71c208175cd65b1eee8479418a5265ec33508eb88e85869490fa9e28fee1975ee5274ab61035494f593e246dbcfa4cfb4aa742a1751f8408a487df19b68d5e07971d312d7783539564911fad73ee47a81e0533efd3d23be0e1ba494829757206d1dc5726f4e443c935ee39991b7b144933fe8543a62f201bfec2df0a1816048f16481a409b05a9b1244901ec84e4fd7fddd53ad2b8851b0616212e7343b17d56d9adc4400c8b2b53d2305e28418ee48d79eff3398e7861ddfb245b40ce34f3bf4ed9c619dfa03ddcf2e74819b71c3b47714c7cf371fc3322fe314dbd8811a722b8e51f1150bdbe172c067327485869fef4814fcb5543e0ac46343fd730002f23b2853233a39d417b35dfdec6ff8148f38d5b35adb736f326118021ac351123a171b06c7379618bbe0f23ea02a34b1a1c8041f31d0b759ba2dc659fd7b0cd5103126562fb5c5856174325d2c7b57c192d1b047e5769648e0948bb9420f68c2a8201c1f064ff6875be55238e402ef08a7c808203ab2aa1bb24dcfb53469ea61abebf8738604d0689b1bcccd98ff42fcaae8b773193015b9c305dc9c8d6ae6ebee154a8a2ed593aa803baa41275a28e787c52e842c49331ca0c10331a478d2a849c3e740b3e5663c6c7ce995ab07865c324a123aaa247d1e435e357c5ce702c6cf37a3edde0e1e92088f5f054c926f47b4c5b3c2a29198aead413922196db265787c6ffdafcc359b0d95dd902b4632b5929f93103edb60906c6e5cd782c841e42a20f441328cafd62d41dd483c83549881dde36d96c7eea9b5152d559d52406768e5c2f758ff9a19b8e32fe0de90cee25246917157fd3e2f863b3109221b0550a6b59e30f9e46d3846702f2c359725cf64b5f9c973531abf692134eb7eeff0020c699d6891316d2be60f0d1a44838342eff6e7f7060f54684f2854472247c97a5308cc3e6d416a1083b16214d93894172c6a2b95339791ed30e7b41ad6d33dd40e1b964f6fbc2a2c439a686f986847e771cd09a46feca3de94e1b39ed2d3f535473fa97b97abb2c56fe2f77d27083dc1142d66c9ecb0ea28c80eea9bdf266736393983590fba2e5354e0d2e2f4b540de0c12fb8d2fffa259791e21023c2988726116b4c9748063a204d91958e3d2c33334a477aa2cbf1b7e27c2dd295d551f127e37b8e765cec48a2b615f0952aa99f8ebc859c68bd32aa19640285c3d9e56015b9b36dc1deb484b28e886afd7a40108f75742451e75c862e92ff213501445f19d15d4802f242a4b9de70f048f504f79d0b6df5ca5899b7bdef16d2f1fae8dd2a5e24418e75d7d0e1517e34eba985c2e03e7649da7f38dabd442f9b94ba06093340428e7f02c6d95ee2ecc503fae5bce11f91281e7c1bacfd9f1500b6688a1bcbf918a73bb10b04add32738971ca65d344dc32229677c2d8958e9d9b79cae63f270e607a1a560e8963efcf5a8dd7cc1f3a28a85d7aa3fb1ac4b5f163348abcd3432cf554d1ef65f085911a9b05d2b1498f2ce1a1ac91f11cb513b4fc5f98c3eb353d627682a0a4a5101061fe39f30861df3b1addcb07299afa13b565e598a1da1ba522794895a841038268b645e2e0c2433560fb2d12af7e69be03e7fe5117cb94888a7d269de1bf03afd688bd0fc99c2f03638bd5f548ec671f4ea185614213f121f3c1e30135e6d8a5967c56ad41b67519e3604de5aa10878b8774de3de2a14bf4c51d10112434128c7b68471338895006474ebdcd4b99604977aaf7cd54742bafc32e1ec5269605a149a0d6461d454949ea1b632d44d5446e12417a16c08b2c2a8a42d447c71a1b9b32d2a10892e4e35024f301cca654fe75e6dde06e71eb3b1a32add011da1fb11f576de5d9a5b3f95dcda833e98a72a03f2947acb9568ffa190d429d233be1ad125a13eba89d619d418fc8440a79648ac6fd0cc63b997e4a0d27ef35e72149ddd8e02f8e518f95733081566da0bfffbbcf91ae0d28c611fc3b7e1981a73b9c8f232843240d2e5b665d8bae85e19d95d615ddda8e8a1e5d4bcfe0a625b4dfce24590be7771a9961e3205260c9ee48346d31f6a0353f5c5b984565d9f4fa73d6cf15b9a2e438d1cbe880155dd4ab20281713a972028fbd2cadb37abc3bbdc9ec01aff65991dc8a2d8fd8d49a04632c4be5a50f50a823671232e7008503fb4dc0377813000e8f74695381e54d154da5b891b48d135b3d5b61a0d106605dd0e21fa405a0ff208bf84eae2d6bf4132bf4445473a9473a3091ff86d3c1ba734c4b7890eec7d134c4a83e87f818a457b88b8da374ad110d1ec80236fec0c087d5e9a8a2fbc4221ec8f898314626387ff65837fe2727dd37f1b1f755951c66fda0207600698d467546f4071741e809d67542d3edba8c238911a73f64590f1e1e4e4dc642093cff7568fad0df9128544c2ccb9653ffc64011ced45ed855b0ae2852e3d3181c1c1e2cb58d2cd7af7d4f00a993b7587eff48500be3af179e7844e969e123aab3a32b5799b48f99c0ca6a7510dfce9e0679ebeedfc164bd1a56024564c14f81c81855bd519b42cf04e11255802780cb6258d87248ee3ee5c5f197218cd07b7af7618afcbba080acafadf492c70fad2e682d7f23135fae38ab6ead387d2ac8f7fbf1e5e71ad6b5fbd4f272a2af1e7378e25a99a2f343baf4637ac9333aed06b470b998ba9c8adf7904c6e71745700bcae8031ff18ef0c53c830235927eaa3a6bdf07b86166e8f5c01f8ff2cc7eb88efb50f28d7313b8be0931cfb940a0c5515635b55346882233ddd254bbac7000aee373c3b773ca50fb68c8dc4294f1741fa3f2bfa6e4beaf803f7b4550e5938f17c25538bbca0f21bc885995146a5564ba560d050e7b6bb1bb40d1fbc549328b47016938de00148792d6f48be26d2f8801e53533c338e0ce93be3427c6213a4507bd01bb9203ad6c2d97662e65bdeb76a89f1a0598c4880af365f50d53a651bf2c531fe009ff6f480aad76f4d58abb8f022c1676b5da7825e908136d288fbc0c954197f2cdb268b414a9bc1f34082b7af971b5999291149cd6318be0af50950a9dd35ba7f85c63430e227d0222c84301480be87fdbc57c247f62e3b5ce21e3ccc5f8d5d32e205d46ce81cd275cc9e4f082bd132954f0c94591fa280e26fbd0beb73ef6ff37aae5acad41efa4c643ff790e0b56d05c277011acccfe0839270c015bf9cf80f946863afda32688243a206c4f698e5c88f428afcad7ec9f60ad17ef711f6d61fd048c25eead90fcef1f4789c4bc4863eb97e0bb9833cadd9ea2ffcc5a45a49b760f4803a6a0e0107ac9a7cf23370a0658832db0ddb914f339a1caeb9cd516bcd75462d2d96ecaae431547affdf04dfccf003f6c784e87ae6ee31884ecc99d6ea332fe4488f72659e0db23868ba8e1901ac37f88b10cbb1fb5b51767a402e40950dc387c9f701dadc89d3505ebc70a994ff1fe5c1e7be076f884239b7883235d87c9778655190aed1a4c14e8b939bcd5890ffd44e2ab0ed8c9cc01dca669448f6588eb790696ad3e397ea6426e8aead32d7da32e54ab572531fbd7f39fd48617ada910f1c7122d21f9c952acbb51387c13c2f522b629968261be8497136feed2973feda54b5e1fb9a571378147b824ec7451c999d3f2ac582553e0492d9fcad5a12591bf6c40e40e843023a982d63faaa91a5e5553cc05ef9b517732df5179e7810c10eeba1269fcb2acd55f78c8d703491f5e2342ccc1b58e42a775a7bee40a706423243304b45794eb5ccf550f4cfdf06bdd5e1e41f98e8911f81adbdd68b3569831a30dfdd072851775f0f7031e14f89ed083f8a8334c2c6fb34b17f6f9b7c6c67835a12e5aa890c4e6826993d067428a8f60ef8ad030739b7fd865d023f21415f3a741c8b42cf3ee80670df9b3f00c3b6d83e86f48d0af8f43ef249b38674a024e7a5f74733be4d2c62a6c43204c0ade1ba9a5fa5b3ff3975ab7c4823a74d8c7ad3c2d08bd19391b520316d15c456a76a9285bb3c867a481a0aec898b4735f3b70dc550aee9cca19fa94269c9b898667ec22ba47a850993257b4a94bc5ec3d895dddbb44e2c74a167d6ba707d7af18ffd264c15589bb31751400a2da30d92239001b7b651616324b37ab99c16a4c00060481bc16e290e6f967854712e5c69c28640d7f0d8c5b5184b4a04f9cc2071a9014e9829a9116b63a389888fe582ccbc706f2280119ebe199a0242126bc056ff78c0e6807ebec489b86a5afb8347bd4442ecbe4efedeabdd2766b4d5295796f9bb5799988beab20e8015d3912460d7465a512a812e47effc090efb6a22b09b10d20b2123b256921dcc0a0f9932ace570d138bb5295e583fc35ed817e99f20af681d73d379f56a1a01489e53bcd3b1bcc63d43d152f0b8605448585a63650bdd5a3852a2972b29fda1271fdbe22323b05508bb3f6d5c272c1815385c8e94a0b1a0f2f69aebb44c1e60c854877200de81d079c0527dff955503b51af4c19ade9bc8f006efda7cc5e3ccd7b4b1c6a305ba08db9f0bcadbef3df2cf0506dade03e719cfdf317e5652c05e9e2b4cf78512a557e71f4684a450a21e41078218e081ec618a74a48018f784eaa873935c95a512bde959f22ef4f11e97afec5fcb40c3bd621a503f6918d17095b10ef285411acacaae7284a5a3b69fdd18a77758549f0a1c63a1f2c63372eeb7124868fb4df1922f65ef3d2b43f411bac190518f3b7010cc901dc114ffc94f92995411d731ae758a48e6649a06a8aa6692ad9289bc659715b56dc024a1c613f2f853b20580dd722c07391fc9adb72a7229345b5e754bf2cb03545b654ab908e28ed8b72eaf69d9c9418e99dbac1bcef06c1558536fb92a52cd139578ba4b1b141c8870f5c8cf212a1abb8329090292fba60abbc4cd85b5a5d412556daa84d74090dfdefd26bb056eb3a4084e57c6dbf4dae0610b1be1e0a79831e700be0d58ae96b6c27645b8aab24fe3caab30a03b6af17267c9970b3c2b1fa16a1b3b6d0485467c6e6b75755658dc9805302c4d9c8438a8418d7fd0f18e1c21c51885101cd549f6d61d410368ab68a005db029d3e70159d0b4f4b0f258b083e5d0e0486184f58be182d8832cf145d14fd842b697034e5b2c12709dcd22514faa9b014aca4246557ebfa73c02c92102b67d96bdd3a986bd5df291661487123ed12054f5a2c3c7064a116e2b8c907edfa4981ac4286a1dbae2d504273e51d3c41205baad4ba98217364701e085bdfeb59325ab68bbb5fd6173a6b80b4114ad63d188f04a1069089c12c092c6dea5abd028d266c8ddd9cb59d32679ddb5024de70b10a772a4c32e789013a766af3b8fa7e7b62af719641a3ca52c16872a8372319ae0cfdf01f521b184c0ba1c35f9710531d7b610c27db4bf710c469a5191121236dd4a5e39c4d584abbeb947ec263510cad32808aef67cb904e77eae7652ce5fa65f6c5bd30450677c20c5b7d181e16a044ed7a712f4b82a1c44363ab587a049ac7a6aaddb37b51baa89b5347b7403050fa90f3ef211415157bec92a385209c8a3a5b28ab7331c1266cbe00dc1b24c68df04d4a6d95c2c080ef4ec2a5f24207b6e55e8b4dbbb8b40c61d9d9b63b16603fdf49bdf564e27866340cf30af5b3626369612250c37b51f0037fd72cd12dbd7f9d106e91f16af44df18e767e25b9c8d23aceefc8355a96740abd36c644dfb3faa0983cc5d3ec52bbe629301af5dfd15390514c0e5eff2080daaa22dbb6b04bef24105819eaef5280ca9de7d88488d9f7394e92073f0203a002771268c666735c1956525dc1a12b7c50751167a5ec9cc6a4cf25a5060ca579822ccec1259518a67821ae35ede1ace053427c1302e0cb5f4dd13171561d0296290d91712d857f1902aa84e311ad5070e806245e217c5fd5a1aa95cfa42f58accaba5a48c61440ba3daecf44afb2b15522b41314d82a4a1dc860213d6fa05bb00096808795b9ac55ce7188f8514001f6d0d6431d88160b7daa963fc4a1be434613ad1d6ed1ea484b1a2c6aa38a72b24b0165042d4f12b881577eb91f5a34818f09f8f7fc225902e058e5cf89906f844cb44a84e26dac457d74410210623693c1dc9dd5d6dfae01b41f9789705940b37f27531aac89b7a014bbac4b4e1b48f0af99a2dc513dae6d13549754fdb9334fe10669330fc78669b9597823f04354c4b366c0eb93658292f88d034fbac561b9547f9e8b5915a08c95dfd3edec9d598be98288f6ac92cc2554c3a3059c547a5d095e4792c28e045c8e91da716501e4a28aa1e124ba03439f6af9f31a096a88aa63a7630468f8ba05191ee9aafb5449eb4be19091fbfbf5a5dc42ff62c203fd8b5387caccdea246b2fccaf97fe51c232984013681ec0108f5bd00463e1b21ab052fd9d5dcb1759e44be8b280c283a76811a08ab146ea5561a76f9cec0cc369715ed86598e3da81e772d70d2cae6880428256082d67342e1653eb4a95d352ae2f7240f97738eff9747c2822ef19a40f4f0922118d9d9b8689f6d20d104b2034a968efa0fc9a58a00d0d421b008e0ac3737c4a07c1ad0c138135013030c17fc4390fbb2cffb9b8dd2de25ecdb89c24d5b94c39b2f16164b145e6583aa76616b79b085b6effc249ab7f7126cef4c06f841f982a2113c2e370bb024d39a90587c52e7b9c8bec24ce8c2a3cda14e556c3ed16c9f1219b225a27e45e19ffc098c12142104e38accd84d0273bec6a74ce111777245e35871aafdca1be13941a86b9e45349e0ea1580ccbfdb3f3bbd9e68e7712c063b6a4e79ca7fdff420610e1fc8e854625026d4cfb23f2621bf54bba2219ba31d0fec9ef41712042c39d62bfe42c95e0ce25fefa69ea5ebf4dddcfef837f7bc0e4dc850cbfdaaad3b45c121ca4edf7cb82bc98e056b0d0d1822200d252e88384d11ea307c62b6b30c54d5180cefcf9732775d59ded3dab09f8ec81825002e15416abce455078453d08d7753901c185cdf8eaf6f8d7466d0beaf83424c7a79bd28b78633b458175564715b1126c5af8b2eb6fe99d60067cbb03298b66562ee06e82a8f8798a2ee662a72df7cde0bb94dd408fd90443097321ee49a6f8920be8a884843ac702abb8c690a85ba4a891b80a4a463036e15f50ade8a47be6019f438c083b5e631cd67c150b784580a02bcba9d442569da632db2a1a02176bd2d42102f15243eb57f5ef2dcd4d15641836ca8751f159e5e3f8befa49340cc27e66c7d53053c005d70df3317e5760a0a195f823d4163a984503493b64327300edc29abd3037e63656a9a7e70834c8cc92fabe1b3e0a9f908210181c448e3824cc649043941bdaa3de1c595ab14587f587899fc8d008216df2c10e979759322dd7f5c14220e97d4f2e5bf3c54955ac8517aa80d340acbb8318f82714681d68e17cb51d2e08548638a5c84ed4388d582a196e93a7fe9bfd1d23bc7f9a752407508cc485b1febdfe487c5856e7170f41b36c817f6c922fc7dbc0b5aa03192eba184a01a159acf7fe2affdbbe1c0c183d536ed1485bfeb8d14f1cfd81c139b7a3f6d5821944da9ac9d62664cec9d8486be8d7794480b4def418694d65f0d1a85176eb19ff0cc1b3a5f4695a7b54e34ab8da7e2b3422b35b19d5b074868e601e14b0ced83093a15cd1d1d7290c8bc0d8b8c52360b715592e34f403f5236e9ccc219ef9a85ceb6637e8aa889e024aa60f9f853c963ff396f20475bde40f3eb82d7b9561cc1a899920705903943828a1373131837d2fed5dcab43e26fa89531f8c3fec33d0618769ccd556f027432bdf375e037df1e709ce3fdcadb905434333601d4e77153f19dfa8018f9dc13a28f033b0f938b4cf4cf10b21e5a92d8bca7b1d73aa6aeb8e7919cb7af258a8c0d7a0a9127b4af4c350b2619d79bebf28b0101448d74fc179edbe2c9ea21d318b436e789f4e658c16a03e52a53accf60b7c8913fd8211fdbc3164c4811c80e1677a18784be18ac962dcc79c3aa2db9bea074c243d4c5d7724d4cbc5e44587e7ea772394aa01140a2191be695aa05e0d6cccc15a3ee1def279fe2fd3d5ef7dca774ffd112e4a1abb315e5b3cfedf32073b9c78e63ad400014429064bd274272fd91c675526ae1b46839e5b3425805e3a61921e6c8fa90de46229f03414b8607204f679751bab5a7e944a7aadf0bc5d35f9546daa029af1cc4135e1e60a0149bdad4527a8d0e97524f430f6e287212abb69f952ba15807811db36313ab00e7d28960b6fade2c417ee1fb8022cf381a358057ee010fa205e095a9f06e26dac320e2b83321a2ae228e979f5b72a0c533d352c5ca83daf3ac9a2cf494fe2c602a6dce0bbba40c23e98abc2e7d3ac628b9302877349a129a440bc18754e0517b80bc52208d09c85075dff8c2e34eadf60701b58d7670c793b35db5b26ea3ca0b5d6bc3443ade064571c33f472b48dcba79f634ff2d9a78c937a7f3eece0c9c44418e826b6d4f793485cd79ba51198b886070d7ac7b6f3290251a5912baf62b426d5a4f323b3213969b77bcfe7bda87dfb878f88bc16dea05e7253fa4a4c1e14a759d2680353cbba0e8e50be696e4018e203b998138400b70f909395742af9911c248e309a03149fd932f6c6f0f2d6ae16eca2f23802a45ff429e7e922f0dc4ea3db9f2f73ddd975507334b46670cb02d3eace0cf93542e3afd40f8969867705e681f6968ef314c2d6e389300abd6ff82e0310e1c993dce44ffe5ec3ca1b0b5447fa71853d60e7a56e033f44a150cac479e57b2482fc274b0a05544b582297c2a527a75e42b4543168e75ad96cb6b824d9a19cf22d1468c61b72b049e2df8178bd3e833799974fdae30bbcc9a7d5ea2558d8d5cb815f3b04e4f35e0b6591a889a2c494a1122846ea9692836d0eeacd25a905b0d68afcb7d444eff1cfb81bd7782a8c20611ab7a4e41ce1b9f30d2390cffb21d75eee24e6f8bcee82412e52f61b21fc569b7f3a17dbb97b4c5daa59f3590d34f1597ced42a41c24b90ddcab646dd1ad759c650571225ad89d69d763870dba1307ad75058721a0a871ecb04cddd0105b96abbd4e53440258351871a41cc9254ba79ac81d436b0af77b468038629808a963f5582dab8ef55d1c60c78ad3223646f2be49eae40c196e6e64adca43dfb16a3ffdbb659663d34c4651c51c335990a5a78d1bd9df15d90053ecdd1d8346d1bb00225f3cc531d4181bc8362f0f8be2b2935259fae58dd972665ceeab8dd3ac6153b41e21ad1f2e22a84fafbe3ec9efb593ceaa959b13039acfc33c38438953a67b78647c75824cedd84a346c53734caec92bc81c304d063aa16abd99b47bd1b394a0e5f64b321cdecd663c70b7572f0e0f5dcf3cebbf927ddc4cbb10abbad8033db244e0fa26a36c3e8665181b63361df2c23cb473fdcec0df7e0baeb0c46e426220a2b638b9e4a1633628c0dacfa641172ed80db384751ce3d0aa8064f840d64a4d16cf185066fe07c46e56d992db667272f69ce5a9f253ddcbc51aabf20296247e9d6f7778584de4a94d3d1971d4f0499545bc7d770f5ed6109e3851a74b56551c1cc5e66f4391355a7c46894acd00aed1449d4a44e60b7ce737034e819ea5e3a2d00318340a0f21600e7a0da0800382186e204e311623d81b99c9cf0f67e3fc107c79d758355fbc0f79240f451fb1e3b2c7b89a4ee7106766ce45184dd1f468d81ea69f5742c7ebe69931e99f2f79ea461108df091fd2c5ff942b0efff41177eee511fe9b146b30e4ad64385400a4e8e34f70075fcdb411459fdf2f61f6bda7d88ab057c974febcafdfda88793b14d53f0b7da900d70fa83a58d430580b6d35e8fb9966ee09d2ecc0f4df50bdd55dd406389674c2c8979a17527a70746ae185669ef224cd3aa17adbd576741634f185e695ad4933d6f8019b937bcc00d1126a5148482b646c3be54feef6565b929b8338eb54358027476b643c29236033a00676740b3debbbaa14dcdb40f75267e24dba38cca4650e9522e96cd146388ecec6f0e9cade0ca26abf4e943f0acfe4700ee38c0c292cedac00a84c4d78da0a1cb5da92b9ce09983b9bc83eac518dd7e29f23968e0b2afd924ebfa8774fdce314634fb830692443eee4b2e727474e4e65008e0b12b4d1f307ff135d8f31df277c6796e85f3bc4057dab665588f94d94c01c3d2278ce0fe31c7226c2e06fd5ea4edb008b897938a3b8a70313e16c215de2638857468c76d4804b4e3aadd00b8755f826b5a3aac350a7fbc8138e0673e0e76cde780ee05a4b837a16b4071b0870d83ec197721af033c7007ba2a48f89ed981dadfbda795a6a8d736e2aac4804070a42cf1e6bd6eb303053a5e0f0a8e276d45902252d5cd4dae7c5dbf39e9221249de65ddfa247a4aa1f69c167b4a3f141d6f6fa73fc19b08a3480956bac18790c35cad0e8e92e4fb72a405a8342ebd079eb6a7edd87dd4754f7be06cff00c182aaddd26611f12282be58fab716f759f8eb410bdb88c68edaec07460a58ede0a3aeaca53454c13a6850c938eec2faa02a5704428a00c3a1671e5d7d42d0e4f6b2e67e36b931574988d9e432c908b1861c8c86a79d86eaf5b7cff15c4abe0cf670d30b7272b112cf3f964ad4b07830d8a1dca0aff227d05053412c371b435ba7af31c2126476579a5d141b5f23a79e36a0207f2e5995292da2921433737b74426b1cd1ad37f142de1e48475075049acd98cdc9df8c3dce187d501c0e7fb22d57656cc33c6858538a156dbf24ceda25ae0b6af456bd7d964bccd1dcd9e5b7ad70b0f4adc02e378c2202f435ed3a3f7da6a3c5b12f8245bf6f0ff634fe0f1956cf00941fd94825a9305441168b7d1f1f1d30e0d1e0b76e0d6a7fb400b89138e8aa8304440090f590b63400e2bde94289e8f8e8a5828a1123f8340db955632ef7bdf4c3dbad4a3380096026c897c3b15fc67e58320f307e518bdc70f29371399c6e441c71b65b074257139ce2b6983f0bd1007a9c74545fdae44eb3fc24a41301f75d48eb3912feb3cc9d033f11e2749fcae8e91e3ed05e7fb0d9669fd23a01569ab54b884edc07fb88a03a0ec1580f2712590929eb7789eda91c49b57e64d3e54fbd778c74a371657e38cf4aeffe579c7e32b31b04d4fa7e994a0eed280df53033ef8d21d60b8517d40ba09097afd3057146a9a7108d1d5494f96b387a6a76bc1944b23f2773df55c07916c3f4710892a2a0ebf0ce4719bcc373d31a0e068013552b0f7075b6eff5980bba3ac25d2c5f98d12948bf63be280cf14dc01d58d22885c2b8abc51cae97c064bcf84350b52dbabfd6471048f54307114f2c98d33230446726b8850aff7ce2b0b41447c757d196f20ca0d3a9139fcc2e2d536c8f8c5101226c288dcfe4b5108e8611f06e2165d1b512e02746426ca8d66c87fd2dc44a1d96ddec9c4c4a2d6ac790ea77c1ed6ea2f481c37b70d6d27bd7fa298e0a8e73343183f85a416e0ee44d9675ec6c1283db0447527a9383ca2629a05498ff913899569edfee763ebcc5b2ac8def28665e63a936176cb1876e640e58d66a312aff98b6af460f91cd739eafa53e825c87720814d9c8d0eabeef12feafd29e6f12a28c43f39f5632890790924871da6f0b2603a7ecb6d49e01865a7e6e37a639be29bf4a428f46aea3934bb82f6aa0d2f4524aa8b5d1f5111bd4fc2f6a34dd494e7a19cac0bbb6beada9be9cff25c92036b9c865cc252c72c2c68fe2a340fd0b3bd02408babd7f2e043496c4c1e157bbf1c7bb0f904c5759fe8b1e34bd65a24dd6c53a2c0fe2114dac0a6598195292c0ce49249a108611c052205c417100612a6d418224723222617267cc752288880a406872a7b019009369a20a2959f842008bc6e7701fc5b81365384ac018ee8990918ec208adf14f0430816555437ffb809bc2d337f6d5657e586fabb953603fcd0b507f57c94bd14ea7fde7966994258ed4a84d20a5102ef51a39afcb7bfa0bc2143db8e2f7913b2b21a2b1c985c63522b16786ce2f03651f35d79087349025683f6f4b9eff3a170902994176ea1eee284d124ec7e0a710d51342bdd1c181a56a0cbdd165f9c3eb2572c83425b343f701207b68b883de259c869ccc601d0e73ef56d76f7496ee073410690c23b6ed325694c1eb437e90424af4586a84a87be4910fe0eed07c4794b50b0ed3927d68d74501b2d1162a6483416bcb14f6f85d393647633d0f3c6c8ea1c5f17376cebd1e762076c97bd43d66e1bf9d212705a5149854232202a3d9a38bd1d3952e9e2400be1d96f7cedb2af84b2f2a9e971daaf63a38b96865cfe04a75296ffa7a6a5d8aaebdb0dc550145b93814459964264c83eb58aa3a58b676ecc2e38bb3e30f02c15dce879363fa86d8faf69b52af83ab70a4ae4a31d3abdaadaeb0f9da450121d19fdf3509027f47e01d33a66ea1f970bedea7294113228a907cef206068fa48a6e00f89f795cd1f1879114b1137effb28788799073198b4179ec84db5880870bc5d36da34fd8e7420821ba73da895a8136a017b35235ee5253afe3b1b638819cc67965bab711d7d2e70a5bc0e45bf0d4ba96b6e7d42a99407f2a4215edd164ae38e2358902c6eb0572b69be44d855f168e19d84a56eb7706fc5a1bfd3a6f340dc40c3d00450b36a8e21174343e58252de017884f1ea8406e45ed71c5216a65a5f2393a9615b1529fbab84f8d794cea865d3998a8aeafe06929eaee487ac2ed2f6944f066fbffa186a328cb8a26f146a2fb10144cb7965ad010116d0b09520148c1da454d9119474165715187f467f0832b8443147d52a4ec45b27596cf67a596c79c121760b866ab989a6a873aea307136a3f6dfcc61e0eb120299373711a5e1f9bab6573a4c7c4ef0b39372642ef768b2d8280c52c68ad0a5b6a0c386c86852ec5f749a12c53b516b107b61d69e5551048ac9b921113d10b7200fa1ee3afdaa8fe750639b60ee2b62c37483dd90f0486ff0856c38493c281cc08a28de2ecd58b09f3b30a91e402173bddbc955523a499927e3a73af96d2a20ba95fc53cd8d40ddb060ea055e04319de4200f323339f14127c687d003cfc1747686bfd39452a1ffea23bfe53341ae754e5bd9d8a1828b2ea146377b0952b2b89ac54aeb3b9da4d29bba3f8e6704aa67c365cc1f1339bfad5edf819eccba82cb7bec6ffcc21f695de61f2160d9c0d9b98f44dde66811dcf51b4cd8fc52c7e27a54d3043aa43ddf784dd0cbdeb873fb053518371ef0221a0d7c97501640ade7c0382aa6f9a4c18af7870368be120d719f65adcf6447f06ff04b2bbb167e8402b2cf5666705b2555cb2943a346b72865f32583700fe72057facbd092f6625363e0e4faa0fde8fe7769bbefa8767f59a142973ce1b610ae2f227884cd2f5e289a07d93019d71789d681b175afcff7f2100fc92e6b953a06adc826a9a893dc2c50521fd73c4f0a65a81298918b1287e73e2aa69ced155ab1269f352867465402ea8c36361358598d03011768b6825f32909eb25327b1f542974e28aed5c5699cd00bdf4bf8c1897afc217f335eb9e46deb5a5445dddb645a1f4c05ae338588c693b6ae74094aab9b0dbb1c75fbaff94be9b214164343384ab6246dac508158a906c8768961a6478465134e05708c8bc9353a1599d4c6952314fd011b35f1750a6a3f0eb849754cc133443d478026f8e1708d9a07d64d6aeb2248cd93ba4e21451f8aeb13488e9736593ba074cd11c27060e51959b5dcdbed415a8b28ee08355587b1ee80d7918df41081c62a754b379000dc610d8d79552e4136586e56e6abf6a0e5f8afed73fdfc1d4d57f58073c051a871daeadc140e70a44dab8f7a0278ada4235331b849c229d94573d5d44e521fe6a712c25bca8ef25baaba6271672f51278ee5756aaddc8716b0a3ac8d7577863851356a9c5b5762c077fab9a7705a1d3bf8c5c742c28360be5d1488efe441b04f48cce67cf44bfa556a1cb15da43cb06a54a1fd15ad464446edca21a3eb280845a642493b4e0841406c1725229438ffe89f1e42293047e27709b916dd16b6d4928c2ef2f960bf411074f1cbead8a65023985ff11eb840760848427bca7b12d4f4c2bb9ff57abaa7e901d46899b57ccfc86fc0b7eda3bd2aa253025c276c41007f081e962faa032319e7ff5c1d45af7b1942f0258fa09587c14e41a5bacf8247d93cc2c93f8bd7c36ba89aa080f088e8d8de75fe183ae603603a183841f8e7a3f28901c1911b1a2ea885f140e4afc1957290a38118aa1188faa7733ac22a40b21f89e3e5b8e2fd263af3eef8a605695014effc9001a6f0de07462052a601b920c0272b7414928626a2bffd7bfec1415eca889d820b7f4f882a0d688fa894720a61fbb51daa8f05127eefad29f8cb55edd1abd14f3c1d1e44577e9b5ca149c3908fa81a3331e7e734e2b770b8bfe1b23401695fdaddc1686ee6672d2ebf43e2742ed03af0a280945de2880a13b3307a2f34603dea01fa530000901a6f29dd3d5793f7d9b14118a9422a855cbe972c9b726adc0d754c0f8259c04922cd771c2d0c6d6ffcca65ffc67ff61dcdaef19fd1c519e323e493a586b22838c20d0ade18d66201a1ca1b34f966770b893b113b45a3578f7f037aaa859d65d1528a4a4a44563b1348f5a015220eaa319936f7601ba1750c09d348292cf3ef8448c20acdb25345ec970d3ef3d7caaff6f30b7c522022f4a848019a9fa16375ce5f0996139579cd984bc38fc541e889d70f7a2f7ecc52314e00f40461c4db3e49b9fa34af3e5568ea067df5e80268016a8f7b27e9e11dcdcf629d027b48fa04ea579c2c246f5f5e4801e0d4a8ab87ce45d6a539797fd87c889293cdb6050e1dfa8a8ca66e5ac38bc73af490febba73f392000777485f67afc9fedec3bd94f1353b5b665510de716e19e27c25964c2a938320956d54c4fab34628b7d42f9fbef8a344d7ccb2cd3e35855e8d2d5aeabffd1e1ddaa19d97796816a9d07c655b555e09696161c7adcffa7afb90bd213d793930984fc83ce5f6ddf9b6c2f7345546a42f4e225b0fa9f3b7ba54fa523a9ac7733dea907649310b5b21ffd258dfafef57f30b3a84ab9ec4654ffda0fd5d1a2b92ececa265335863d402b8cb153634d726341d3b31a2dc1db7e5538cce76031da5af8f01244db702084a7ca68777353c9fc47dc1b5680d1de0365e7b2af7fa43caa4cf8d50225da7a2d9288528581327774e4d49633fb1fb9b5d5b543f0d7e154f00c48050ceb7cbccfacdcf617c63a38f5202dfd11d7b7b27f6234e14bdf92e746fecf3bc5ea437db2a4a612216f09854065b11868fa3b82f691c5709de3e8d7c1225c313d7f1847b3c2abbc2768a08b93443619d3ef88f96c54e0dc53d8382bd84899cfd2ebbd32b95a42b434d2af43b0bdb7cf8c5655b5f9658c1535cb62ce6dd061749ca5055a6b24436687b7b04929d85f06f8af70d25b309ba0e3f608883ea538c41079d61fdae854b26c8a167c99b56bae587b22efefe7bf456478c0a90c4deba949bceb38671d8e77d071f64acfddb870842fd9daa043c120d41d7a91a67953584c0eb0a980e3f8a2e0bbe74fe8c5ae1a09a50acc1473c990d4513b9657203c0635a77bad0c91fbee9c07a56d64c4da129c2fa105110c422c75925fc31c66516f023e71df978db5d7db1328a2ef257aa1920b3a676f7f725ca4fcac5d9214276bb767e1c5fd690e907591711e0ee8fbec8e28ac87e8d877c5a72fa57b7403f8c38276c02d1b4a0e6d73b0e9c2ccc4f5712255b601bcdb1a69f5ebd9f9ba01438f1e2658faffd3cf1f1782c256bc207aa236ef07e09669865078534464536f02d932ea2b9f17b35aaa6912348d74e65614b1a6c70f62203d6ff04e6444070592ed5b78fa0a39f4a4a064a26cf8554f6ea585b5f9756d012e4701746aa7d76a01eeea6b8a30cd37783251d5d35f7016acb7bc138ae77f63a1f1e09357fae0e4ecd05f0b14604e84622bde5cf710f425f22caabc29420c2b1b9e0f4fae5aba3211c0bcf04840fb9f1047c1eb8093810809b521cc69daf9f02c1229258aa410e774b038b52ba88962b6e6bc77c1cee02da87268e88c046c664e210e103db4f59d7cac68cd562ab1462a379f4a076961fcd16d1cccfbaddb58165c6be1790dc29b14c414f3466c5b8b2a58cbad63125b838a4319e077493134b931aef34e2ea66f61247a0705be162f342f893d95b9aba95ffa3e2d6ca582b6eb939972f55997c4c946e5617fb46f92a6605272667fa28559343f9ce21d195920421904c138a6c904e006d52372d69ed4c1ba67ac0278cfb4d936412529c76a9802d86099de8824ae6288282178ff6afe37327c06669c71ca0c91cecdf9b5a3931abad1667c6575cc276c04ab4eccea7f7396d63e1fe5b77616329e406ff6863153823eeed606ad9389aeb73143bacef9175a00ce7e39fc2b5d0ad6125fd95f2584a2c74618b3660489e759956e5af7669853dc3ac6a28429a44aee246f2fd2f78b5cd3514fa257ca9d9c9a11fb75e34c56cfc7714707add3f881eb31dee8f22da9b847cbddaf691892bfb6debed9b75962b92d336e0389dd4ecf2325d4fa035adb2a935920d032930ec46212175c0749db10bd930a96f85e5e0f981f3e1f3085191e325ea7b301be97c42f7a4b11cdcd29ef05e07250805073aaee09c74707f04725166ea4a9630aabadd9b733f0bdc4828f24217b6fb2e5de52ca9464380918093a099af676be8f74b396bee526a77255d79df7ffd6ae5ee2341996653b557778a5a5043a744f94be3892a85c90e4a1b103f7c45257c5c82a29a99810626b013706103d682d58b263d490a43e31b54de5409f28d2ce60fae2c8084f9c7018c189a217e030421353ad296bc410839a86143190a82206241b0d628861090c6af00003152650c8000312150f3ae5800ddab728a8f62a3105e629099ba72a620881fd6910c696d717e44a3c2b601c19c17082fdc13072c0feaebf77ac312ab158b2c1ab355f90e7f882687fd144059e0fda24289a2fc8a98f075d7c91039e7fc12c4ee0f9335fd07c777757e207ec17bbbbbbcb7c414e3d9054b224f1620a3cbfe5d44117319c6f747a40b1f284e5ca1357ae60ffc78204f61fbdb801fbe7c48062be206fc2abbcae4ca1f2822faa1c5539a28ba2a62ebe80c11330e8c20a1254a460f50202152578fefd82e616f04a942d9690b2c51b5c7481fd59f74e0dc134ebc10c365450c414614a8724533cb57184946ccea97191c464ce39e7ea0b9ad96bdd603104167160ff9bc515ec0f3a2161ff9701fb8f5a8091c3056c4da9827d8b2b8ab8a20deca04c19292081110b8cce285ae30a2b5af0030b66479c685b90c0eb971e3aa740c1f32fc805cfbf57c6a81629a4b076b8b9d56ad933df5e2b96b082060c4af104fb6f56a42019b17bd114033b850537a8a86aca89a54988a62398aac0f3e7fb20441c6d38c5d186ab10731928e102ec8f80d8e469b562fd2790d0026d80e7d3f96d8568bda2485501c4184711d4c51c6c5cf932545c39fddba9383a4cca2b1f94728ba7d76b528a375677e2ea6fcb7ec70122b8fb0bb22d76df3d2bfcb19df4cfa70609d335a0d5fb73e210eec2ece1ee57abe7bcff1ec887f0170e71ec89eda47570df85d906730f34554a98e2fe5a4927d6838b7d8b60ebfa187365d5da16ad5ba0ef43b876325037a6c8ed8f99e1b6d014e5094b2c58364529829ba8e882a927f3c3b53237dc6f31b930dfff36e3f7df9797b2961debaf749e8f01447ef8d8268c00e710cc6957b259620babb005be42f9c3c7165fe2f4c764b95942750671cb28dc2f37bf30310df2a3051194dc87d274e85c32a2ccb7be250ac93ccd0f7d63c8845aa22b85d6ca06208021a3f9219a1f92c92266fefe26e66043860a7a67c41f0ea30fe4620b39deb82d13ba6644999f81f921592b74a5d0fa988f11855e68c8845a0f233671bdcc884d642f70bc84f6869e1bd73fc70d36995068c7490f32901111e18836a26434df0a853e1f644462cc90d1884da6f57e25668b5731456e67a3c485f998db6af98faff532392f39aefb99504ad8b63d953bacdf582fa1f45126cc09250a2d1c6ecea11a270e716dbb415862ceecb2fede3027f6c3c797d0568dfbedef775ba8ca89e5e8c47ca4f73731b7e02375c1dd7e04e9fee56f98a910defe4504f29ee1b3c255f8859f7d2f1ce278aac22139b11682f88e0cd2c20f1fe98ba0040ad307a24f02fd1cfa31fa31218cd4cabf1674620e36ae8e0cb59fa1d60d998d6b684b706cc5cd07c09a3876b93fc4b18c99baad229934ae7c192657ead0499b4e7a4f50ce2a6bfd82b6eea9b5dddec77c31fb7f407bc588d1c2944ab976deeac43fa0add5faabfe95a63dc5dd071f7ccbcad7b39aae86dc4bdfb1bf7a6a57331fdb68f52bb1ce7cc70a739ab8ac9f79f9f96089d29b0933f5617fc8488cfb7d6b56594f5a4c6eeea49f57eb98aff2e06dc29fdf4fbe23fefc86923efd2f25005eca57f772be9ce06cbdf41d98673db9b9937e8260cf3c6fbe5703be9c5f1697f5abcf2d64baa43a780b7e370c73e3f0c530b35efc97ca46cc8e55ef81981d8b611876966e3e88bbef40cc2000440ec4e9b2a0982f8d98ef8c9841993cbe8879c45dafd8501267b6ac8fbefac24c716e51e918649f61f6c1265a4f5a624a529a89eeadd1491bafb25fa374524f08ed9cce7a3eb4a759081e42a71c1beb9860c51b31c7c41c1f1df790383506885b5f2c817e8b417ceca7172cdf46895ba42826b34f8395152942c3cbead39023d37efb2232741cb226f369b0324b1fe4ee0c3783f4675e200375f305250e7d1be690304a801c62ba23832045c81f60fa302e7bae6212873e9ddfee6474d2a69345dd4248d8acde8bcbedb05455ea0dc5d2d2142b50547081e294e5298a8b149e4f5dab4a4c949e58dc33899b1b57252bd2e78adc017d7442f2bc2b39b93c0b3d923873723fdf97a63b75cb09dbc41f13c672dabccf558c59ba2f6edcece1f94a4b4a3ece239a4495e8114552fafa5d59f07cbf22c7f9ee848449279e263c5d0a3c3f8f78b6e85624cefcdbea25ee8b96c1e1ca97d9e1ca972fd374e577364a5cfa9aadd4ad1297a88bba67d2ed386172c268b8c20c577e1e8f9c8d4eb5b444cef9203871db874eda647a84eb77b27b71b934a86a1486ee092c9ba058c18d8f9c0aa41c5d687ab8f5ddc895d4235c5b02b911d78b7c1cebb7c0b8d98deafcda4748ae247d24ae2d246e6e6922b559e2dadfeaefa0e1b6ec84892e94c072c22d1feb97c072c2fe391356819cc3d2c8c59829de0b1c37b7b62940dcec452c96af1493e5662f52c4bd48c3f21bc9c7fae2722b2d617e2b99583e88ab918f9512d11935a244b48808ce21906d579ae846b81a71b98d9b1ed91fbafe996509db99f334d3914c7a724467505880b32bc1734524c311383b12142457c27342c19309cf77d9d3bfa01c145cd708d282b0e4f6d3f470b3cf90f88e1be1e94b73ce1f2ff8b7d57d34df91e6bbd17c1ab6aab8d9c620dd6c59188ac499dfe10ac700f1bdf4b4a74edae4b62da99b6387bb0a85fa061b2029c958dfc9cfc12c42520b0f5c1cc958437c9c9fff27d32463e46609f5d482ba56f010874dd985e8a3ee6ea9f8018a3f11887d6d041bbbd3f3fcfdf7fd270e117272ed67d9e5051fb9979f099e485dfaf8c3a90d9287d01f3e0e1112e2638c969ba515895558210556b8f2b0e5e32a945792a08892c9ef82596ece4961eab8e8e35d2edac89ce0bebed0fa7ab6fa753ae99cd4ab47676b7775a57dd2aa6d965375dea77dabea75aa158b5615372deb4e6aefcb7c698f839994568e5a6fdb569e67b574926b6bfd4edfa667dddddda7d74a35cf52ec3edddddd77a6e65313e974ebeeeeeeee4da9ba33303a15777de7a3f5eb00f6ca51eb59ab39557195eb2a0296e73bdcd701ecdff5819c0341270295ec5422e5b4d070e56f5fdc1a2c9b5eb0450f97a60596883b33846acb0b8c9278c10c4b6e0c964d53aca1b1e0b6b06c9a020c0a86b533534ca124e649660a27d803fd04d7231fa5704fa9dabafe54a5ea547daa51f5e9bf3e9d418968d1949a539b931597f49086b23f27f623e7ad9626a59473ce6c3f88f6dbcf1226d6826c6f35cdf3a4940aa05f4d682c02b15f29f6f7a762ab05be5661a6ddfba77a0ff36dead68589e5e7ff805c06d99e03930342787b2efc4c105bd60a5f4f5cd932944082d4d7347103f5299d49c1cd8fc719275a746d40943e46f9286160b82a2c664e20b9bce73b79a83fe923c52ad1fac8899e8f9c9855ef59cf7256256eaf0a7d7b0fb9df8238778edbace52cc75465c969f577798b221021c13a45f742b6774a55e106ec7328d4b6a0777da3a1c46d05e7c0b2a98b14f6cf8aade4e3fc4d9c51136a3e31dd77229168be17cd6fa9f93d355bcab6d47cc2f3a5e6d47c9a5194080a44e2fa4bdad54e4a1fc71df73d170ea1d8ebc22c04e74f94cf8540ba211477df710fa4ff13817022105709dd7362277e12e30d6af3feef8770988570f7b6b96e6863220bc155f49744f84b4bdde8a44d9e52d5dd7fbce0d0d10ce2f8414a065548a144e6370d282ea482b881890964fe4e24e54a50293529a59452d6de4c6805501e4cf7f1fb8fee5aecb8f542955d9bd2ad583675394387db62adea5ef076f102e7d1a9e6a0c57cc1104b9a8accc462cb1b559a6801850f6ac4105eb1822850da408242a20c55942c335069571a77c1827d005836155103abb06ceae284655c8a1581657e0289379ce0b04618455099e2e4040aaa1cddf084ec5a6101910a44e2e61bdc3dfebdc82f19f0d0850cca685a03ca0c7c98011bd8f39d2560f0c27ad9dce8a481843d8d26d43082fd4735a0b07f8ed413d85fa28105f677376ea6394e39767ab0cc3dcc30d28519639c3144d3196c7039c2050a9737b07f7b1e5417505c71a5a80434500200368ab0a031c31860ec209bfd3048220616082eb8f9e6c679a8acf5bad1d952e4c5162d5150444d29238832a6b053c704fde123a533236a667736121fe957d105828ee43337a28f29cda11fa3fff447fa8a72bd9ffdbf384d2268d55699484e5799efd835845b58ee7c457945b92dbbbbdbb7551ec23f5eb0bbe8fa04d7b16ca2410db85faea81799b9912d44d85fbabbfb949d83d645b70098e8b8f8e8523e7ad4749a30ff7e8180911b346e9653d469f2b84c5403bbc0e585198be5e48ce3bf9cb24b97c9e98942b184fd9b29c7c8dd7ece1348f0d1bb2ebf0ffaf2971c2999de487cf49ed51cf14d25a55271ef33df719e7eb24444440483f477248efd5d8913f6258ea4e2e609be93b9969a301f4fa8e2cef721a5b50c91b562067fc4aceffae8e81671bdcf3d43e23b8eadd84818b07a6b99f07176b7a53cf6d2b760b859b6de5562b63053cc604b8cb1164614bba7205eca2c608a80133671489d0fe44c6143f1d1bf4e5cf2c27e27145a8a68d66ecceed2535ec48b1a8af7d2e411e2dd4cd2ccd2554105145468e2fa48e166268b9b737a96e323502ce76c6f6f66de92a1c6c8cd2d65edbdaa2589f346124b4eb47092c9e2bae1239154971c3adc5e7298bfb5bdd456248e7f4c979b6f96bc89c0721ddf21ee8d8f73b6a4d4b7d58637f1e6c677a4cae7093c54865fca89bca8a5fc7b2ae6069716f9e84b325b644d0eaec444fbea4ca40f0a2a0091d0627ab97d6b87ab34ad28717ebda25c7fe1d7119542a489f642449ad0efb8ff4ce8d7c49cedaba2cffcc8917ce646349c71723325ea50154ad7744fa17b4f14bab6135d0e90b297d1d0b2eeb39532a19675e20c969b7dd6df81213ca7d84f46ec4e67fed4c89f12f9d3a2a31b5e51eec42c4c581437cb252019125c97c4e210de81e4beff0bfe2cf8fff06f61b98e3bf786f0949e7baf28f735d7fc1e52710fd7147570cc47e741060afe2af80391708132b0bb3f0b12c7bd27275c4c14242959c7c529064ed4b1f62f384f9b21a57d1361ad890c2358d3344dd3261d1aa13d0dc9174fa46476c2b4afc9e1d2d780a6f6c2b460100d489ffada0bf19dd790192a7e08f9d60d6e4bcbeda680422079dec280b56b03d674c0dac710e1305a6e7e61edb5a58d8bb7842bf79d90eabd97ef855d3461f5bbb08926ac3aa1e03bb9a51b2a541b212e6c3496983a92cd4c71fd97206d4abc5b824bfffd0bd23cad3e0d55f0b152147880516d086ee10b0c62490b24990cd1b5b66aaf7958c813973a15c13d90a350dfda9bafdbe41f472bd4371c0d4124abc1a1716e226d6bf958bfbbbbfb0b8249c3668beb9f6dda884ba7fa5de43b8ee485731369978bd2491a0aad5ec8fbee55a1d0f7aaef565fb4ba5c896bb0441c4cb22f943299248c9081724121d57f52f37f93354644297182b868594ef3486b8ad9138a79996f8542332f24b9d0608b1559ebbb99b091de28c34876c47d6d14c786b81dd6f19dd6f7affe763356dcdc6a8d71b5cf2dbc69af7a4dccdec3fcf62d983a13fe3081a348860437bfb6ed7720b91bd0f6246c9fb37d6cfb971b37671a21db3ef6225e31dffb891904652865ad50ca3cba7d67095d263896d886398887e94f1965794ecd10aa595c6144920cd3ed9779e236086454316a25b9937a7ec4c9dd63407de54e67a3d1dd2d65b56b97be9a620c94db3f77e8a44df70611540ba95a4bdcdc4e0e83dce9263912499f9ef47574acbd1704dba999382d4894643169dc4e25e3e4e6d6a40d87544b35412561fa459852da7445a9d6b391f84e23d1aacc39e713283b14c9bc0a289e5892cd64715b4937cb284c9f529129bab98b90b44562c31f326ae6889bdb8936154c7bb410207bb4986e0c113eb4105605988ab993682389433fe68ddc464e64b4b8b935bf454fe9e656abca9d475e0a984870988bd2159ca7951cb52044a88ba0a860e7827313893e352a24c41871f338c6f01643c4ed16746638014dcfaccd6aee558a198b81a0b5e214e3f66f729bdaa4da9c734e5ae79c73cea694ceea5bdb39a737e79c533ecb0d5aaba6695563c15b686f6a4635b491a390c9b37a51f585de883515f0f672d46c56c0dbb79cdbb77d69674b9cfab8d3344dab745220f3a9a67d37e714bd6f55aba7fded392bebcefbe2491551455067df96ae7dd2faa37f489f8e85167e3ce1582c27671cff41f0dac891be90c9d3b3d95401d797237db902eed96cf69a46297dcdb66cf9633e8feae5ff50852d8789d7da1f5e55799ef63fa44f77e98f2d047d6c0551b17eabb9f27e481f57899e83c4b1bc1fd2a7eba4151a214c5df915cb26244ae4e6ee1bb574d649e974ad4ba076d50dd13e69dd6a4f3d9856ac6d7b56b80abfd0a1fc69eea00af30bd3bf79b98d7fe29530afff3d5d7dd6c1540c7f22d1348d0489bdf721b1277a9b2acc8f552afb9caa622b86d99fb4da4435082a7d67d3b4ed73b4dc4c31a5dd77f46fad9ee7bd0f7f4f1cc25ad885b971c7bd6a48c5d5ba512a5dfdaed2705a6bdfc7b4da10d61c8a05283afa43ddfc21bc8551d75f9b3bb40e7fc22e5ba90c373a292a2aaa9fbaa39e3a2a0a0f608fd1240c2b9e8c8c8cdaa8710d949b299d12c7dfb3f961546105165a186d5edfba72f338aa71411f7d6ce3e6f1ce29f5c6c80556822836b01cb180e096d302397a77fae85a4617b83bb52074b979c4de63b4d267250611a61e8552ad87793e61f72e431a6f603e615fc22e85b3d011de404b61959889cc302702a10fa4be96a9985b0abf70c77dd775620ed252b87beac3e654b9ddfbe7a325829e1433fdeaf1d8f99eefd0365a82c0e4563147915bdffb828a3009a97f419ad74ffa583f6db32a8f864c8290248eaaf3be1678bf4f5382673daa46128786f7ce240e7dc1689af612f38d3494f98242a5279386a669e83ba6629600963eca78312d1a4fe6036540106cc5c080e0cb05419005ae3e10044140eabacec5dbed3e370d57d14a9caa537368d5b4ad568d6a967a9bb7b55a177cbad65a0fe9f5023f8a9b46787e279b5ccffa38e95c652078669bc37ade77bf567d109cb817fc9597a3c9ed2ccb86354e1f2f7c5f5e606aad55d3aa56b53ae2189898971bc35a7d5e4c4c4c4c8e9596c401189df42e47910b76d2a54030d43748e1ec7948dcb62e708025176760c1c5194434f160460749d8572f57d0a44b1632221d84162f8290b1fe0b8542b0414664662663d11a5e0fdc6029e5607f2004e85071bf5f7df7de6f3c8e2e3859525abf277754df1dde29d7e952f111c97e419525e5a33fc792c29b164a0915c1128b5e76c51e2e8b2dc5bafec6d747d625f2bf45fe2c297fd6940bf3810469e1296e802a11056e11c8fc8f25c543c9cd2ca90e9236d893cece4b6fe63b948adae7c891e3b53074b97ebeab046d7e0be96aaffe829e030e5e402e33c5d55e7e0b682c86ff7960f372f2f47cfd0f8178f012d3305b243e56a3ea12c3dc58f47ac22aab0973e39aaef2abc9c0954d3820c11cfc374093f5f0f89e509b4dd8cc36df6e7a4b7cec90fabffbb0f35eecbeefa847b3c4471a241f9de2ec587d08ab17c006e3688e244e68599f61660da8322fc71237d794d00f7b98986fb2363bc2b0f7bf09bb29b0b071e7747323764eb08ec98d9a255fe35f33c577fefd6b9ca40fec6f3eb3b08af598e60520e6a7d9687e87982fa6d9baa909f357bd7f23b076ecd8f106f81e3b441464f0f9d8ffbc8ee84a41e77bdee58a00cff7e83c90775393076686d34ddd889985736780b0000f0b6b8a248e3fcc6cfafdccc2196606338b8505789f30f606086b60e10afb8839a262618e028439a69edc5c73348bc2fed5c9cd51c4c74714ea21c2a4d0f3b18f89423f224c8fe842c13542c3de6d584a92799126a66443329d4c3385bd2794fe0e82f7d61cbdbc7c070b1d77edc1c2c63547d3da996b8e60a16714ba604f4436d404263a40ca62cfe363bea35e4fe8d47760a2d613b6cc9b4d9e29f3f78ca40f4ca4b3ce95662471bce7a7ac471422228b89da4c2774c1be0726ba10019e8f7d0408f03d1ffb1dd84b99d0eb892cf63da290abebe9115d1d1589e3dfd323ba601f135b459c00fe3a5238c3d04cc1c4165158e4ca7c8699ade0cebcfc8276cce47cb67846c7cc003e837806fcdc019ef98e8af4c179ff8ec9776edebf9be23c3eefdf2df19d10debf43f21d01bc7f77247d60e367f0c784f97ff0d9f1f752fe7491d6e1ff3d8fb0489b4d9e9e0f01f6a9c0f33f2f3ff9a5b0f33fa2908bc70bbd9ec87e5eca765e7eae9b8fbd503b91c1248b89ae1d381f7b21d81638acc862afc3236c1c0b3b8e2225d94fd84484efda1321dcf13c428963a190a4410f7044c988783390fd884d767cb7230ce1bb96de7305db1116a0c812069a3c950a09ab7f05ae9f23665b05d7d751bfc0f5070085eb838f53df27ac4f1c56ffa6be00c21a425861619d499caac46145faa7ea2075d4f7ca04ae3f8a1964826b5400c4ca03aeff8198dd46cc16d7aeb107626eecc1d39e07f2fa3de2ffebfb630007ffa2fc46e82004f2df18685289c3f0261cc21d841900af13baeac75ee7774257155dd08982451832219d271243828aac09fd9edf791e15dc5c730480af39f29d0c0b0300e1983362c18168f39238268cab7d7e4580e77bdebf0810e063ef5f0a3bdf230ab9154f643bef5f0a3a1f1385a4942620aa90e93cd0ac19c260983b0877c24e0b198d36ebaae43a6b85d9c67830da8cc3d99bd5d8e0cd72855275f0b459ee98e41a2daa910d73d5016798990b75d61cd5244d1e0c4cf1031cb32c69bc5103997f8dd2e4f1c00c2827888cd020e58accbfe668f2c829359488824a0c6e48c30532ff1aa4c913022ccc4041142d5a445de9b490f1ec8452d6b5b73301118ee0424a4600d1ea84525675b8d9c2ba211ede01101d929b5fddc4cd3635adc39559d89094850d47b89bb832e69249778d8e5553c4ed9f41c1cd39d84e3be382dba3cbc9c989494a424ecce783af077b68f5c1d79863336dc6a26beb9ceeb286b044b1ff0a4bb043d12736a4e01980c05932b1c1046749c58a7caa74da60a178be0f8aa78d8d0391901323b20222712518c61582e76b21902114bb4ae8ef9f435cece1a3fc29c67c04c3b8ed53f4f1f29d1c9d988ff2fbe682d22eddc02a018890fa2e1380489ca5cbbfb194924926f948646f8b12d1a22935a7a4947c2792ef4595467171239b60a1dd022b58a059b842b6522bb59366a25b899ed1f992ea2b134afe648653e5488ab835445ceea7b8050c94600160b9c5164cb3b18c16df489e6c49792c1d21752db22045104c829111b37c9907721ff4472a7e17dec984d2c7980bf5f2f761606062c27c710c7ea12f61ebe52787b06cdda7f763c22c84af7d98b0aa24cce71bec3b3916e81f09b3e5b67efe0b7df9243384652865440bb48e97965c82f412c2c0c084ac971f9d491c0f7d899111937b7f267d247e79f91be6217cdfbfa0eb32617ebf8ff92d524a5f6062e4108e09db49eb7861fd0d59ac59e8488e543dc586367f23021137c9958e906060b8eea1bb9f9113fbd1c2105d9ae93c34a2cf1ce63f23d21913e20c16373fce74867d47da16133540e11e2c998a889c70bf277d4a98af79f6a2f9c4c77e2042e6bb4cd0dec5bdc456137303d17e729ef52221dabb4c70201b56f231d682a0e87487d289001fad0541d12758a526664b31085a1bb6ffc8b7d31b0399b2d6d112678a58bebb67c7f27bce39e79c73ce39a5c491cfcad2b54f5a35aa6d53d69e1d58c2b0fc50f2bce4e4f4264ce5152c1f4b1ea07f51cca0682bd7b2fee9077d2e8f10b07cab0245076a1d2ed6a3db6fed573aa95daa73d4f15836f51481802ad0d63749e966a9bd4824295fd2c76f20c23d9b3c2f876dbfbdf04bfaacf0f6364adcd5b366db03396bfbd5f6ad2337e7ccea17e498f36eefe65e12677bd70e5773f17073cfb20b6f9fa554fdcd5240a2d03ff2017247f5db4b40cacf69cb8db374f25ed2879312c6799ef779dee77deddecb0f05570a53b6fa295b89424464abf744ed0502372cf92871cf1a0eec899e7463b05cd242021cd69995382ee9337ffb4eaae1afa5234b9271d588d580bb87cfaaa89254fdf484fb73431571236e3404ee7725aee44f1c4a7626dc6f3baaa5fce8fbbe9735c0dff7fdd58109fe1edc2106fcbdf7057dfe8417262092795ed7c25df7b4eb6634e06e660577393c81bb577d411df705a92e1786ed81b7233ed8c7f40a2b05d0816b353204ae46d6c0550a0e52683002d1e40a22197d2be75d9a6fe7df2ff094420220f0bcc1073c9fa796d1444ad175c25f7b625ca5a533d2e82c314829d2428a0d4460b7d771e8a4cd7ce9ffe9913f614c5a3145112c7ffc2b79a4cc913a72677b26099309a8c149c224c618c8431a52293544592f8f17f4356a9892020a14073636d287c3f25d3bdc89a5cd2871320858fee7c8f1923929756c0f9330faf2833a9fdd540173f30bf2dea98a523afe7c2075f4081cee441b89d3af0ab930c8c8612bca2b30b5314338c5518392144b6660ea62446e6f5540df77efdae5ea67ed67bdab1924c7a974c2167f5ecc819c0837647d36499181084a0a26fc8a4e8a3929a5f4007336a507a8417476d610cfef3929a5b4e7fc0ea4f4f9a4f43a95949c757777dfb44a3d1f65fb4c77efbbfabc8e568d9b2e8ce0788a2cdc2db258da665b5cadf04afac8954fbfc42d7edf57bb764a254ebbb77cbb5a751d35c10dd6a8437cbd841127049d6bf4b8584a0c4ad6c0fed655584a8961079c034b2930b4b144b734c670cb4721a602dc2f2b604491130e35d8125582a6389060d9148794155836c5b104ffa6e1dcc274c98a28a0e0290e71ec0110e717888888f01010b356520bee7c549f3f2c5d706188c52ed4df24ce9ccf85744e4cb710678b359cedcfb5b871b6138b4e3a0f77b91c58c70a86bca6fd37b1316d5336a60d8bcd89ce26ad4b5d70ad4c354a8552976a94caa403954b40404a452fd2c72afa6ce647dc48d1119ff98c09d8a426aa412daeebc8650297514d94cb04aea319d01935a248e88c26a1463fd8238b64912c1176893db24448d81b72681e1df1c8e1318fe6d193a9b4b4e4e35c5a9a50cc275369423197e6925492f23b20b7b4da8e29293961423a51b2ad55d153d433eb31d253d473a46746839fd98fd10f929fd94f921fa39a195cef756345eaa317f12827c3a19c0c8ff228992457a607efde2963aa48cd1997fae876041da8ef63fdb6d977ef569ea554455fdbea043c714c47a5038555685c4e07dbd7ece64da3e82837ea3156abca71ef514adf7eb92bb54f3f1f34a9c7a0e0eb2b0b267c019aabfad9f76c8fe175523ff54f9411ee42cf72e9286aed10ef31fa87b355ab3d46eb481a630aa91f236c7f98b0fd49d2fdb75269bf127357edb9ef7e663f493455948a0b149eb92679dfe51f25f8c708d79f24dcfc9fd90f12a35a7f983077c6b53f460ce8923cfc49d23aea7707a9493f491afbfc49f233fb49f263e4b566c45e4ed4b48751bd7cb6bffa9ef53caaa59f0ffbde77d282af7f118233f7f737204c1f687257cc12ca5d8559276f39d2bf7aa167cdb81d100f1efd935b157e3f4719501f685a1d5c5f93a19336597584e777b1336c7b96b0c548dbc7809e23ada331eb57abbf5f43abbd676f7bcbd5209f26e6ae3d475a07f7b43bb205b9ca4885e4862a25af428267ad1ff734cc3d487a6698fb8d76f37b8a7a8c70dcf72ce12e2ce3aa7a663eaabc086f6f27cfcb7362aef673905a84b9d7eac3843264408739b1e7c8f6b3937a8eb48e7e20ef29ea39d23fb26786fd55476774d226ffcb114ed830b9a0adae148456475896649a6843570a55a8434356c55b995c6ae924c05311566435ecd090790f33e963ceef292c168ba6bd6f5ad544397dba947b31b7d7de7b0b5bd6ed39d1d484c9747871e5e79ec22d61f5053ba5987492e7d6226c71287a2187eeef0f4c7912a40ff79e63ceb3facda5b7f781a0776f9757c0aa970ae8800a61958a7bc9d9489cfa1fe0fa5225e50a585239859ec0df25951aeae4a4554e3a6b9d5aad4995cea426f8a3e1e176104b7a3f55e3ca6f15b488b013899a9c445a984ea9260a854c98908ffdc20e1257be13612087c10bf4d93da597f8cb3b3969ad9ab66dd6725c93de5a85d4759ef77d5f5077f792debabb59fdacfb051519c1dd0261c96d10cbeeaea6852dad5fa65fa667667aa6bee3daad968ccc8cf705753432343434ad18181a9a974b4343c3a2597d343434ad5abaa16b4cabe851f5a9d6ae7508fddbaadc77773767fb35fa1bf02e18888b56f448d0b27b57ac7f9a46bffa2f5c2971f3060501249cb0bdcd54f934a2ab8992473ed28e42e24c6702679f8227d30a6ef628963c0a6bdfbb56777feb4e6741b0fa5dea58fbab246e9e2d1d55d81dfdee7dd0efc421ac0aed7343b8436981266dc772465763a5a629d570a9897299a07f5a7bcd8c6ddb680d174bb3a66926681d2360da5c479aeb680ea1d9fe6931ecf3d0f0b226f55fa5125d266caf7a1fdbab44d93ab2c52afa2acb59516b1db3ffe994ee70743f436b7fd57d278232b8b576b5ab2478d5a39ed735f6669067a4ddbc6c9cc7de1eabaf955559210d2f6be26ddbf67d9fb51dd7bd8a136992d6a139614dabce24795ce3b0145fb0cca1cd6d73296b3dcff37eeec0026541471fcba752a461dcecfeb8bebfa669fddcfbe8e7ea6687301de255874ee98e86b669dd6fe10dce1bcdd9229facd534ed7df86ba2c6fdb069f5becc5fb5aefbdb691ce7cf7157e344326ea678dbb696efac7e7b6a552a158bc57a1ffe2c11ea398e7bfb792ce0fe389b43819848e4a67a95083edd0c62cac2b436a367a8b46d7585450b15a201000000006314002020140c8884229148242293a54df614800c8096407456980a644912a42888318610830c210401008c01404343430400aee422c25c1b409928e20eb26c467c0e4ca6384123f21e09cd806d82881b4866fd25a7fed23b9acb61b290098e018edfb5bb9e21cd89d7ac1df6bd3347ae61c535983411fec740e701ce8e3fb0cfa34e2b99204ac9a302c38dd8a259c5e964b56bdfb10125041e50fabeea92cb5638f112d6e3873caeaba44b0e20b41cd7ef3330bfc28c4fa9cd75cf28fc109b8af91b5cb5907c933d9978380b3b4887e7d2728d18e87772fb6530beab6a59d51a72f8d93591281cb80ce7fc0a04dacf3a727aafc3f0399edb85b204f5c549a310d11a1c9b5dd6e7c60e4d358cad0d4a5f5a3cd33fda48c25def7975aeecebaf42ab3e8d50893749ea7ffe0b6cc64e05019fadf336e93d7cbbcee33491f23c0c47ed758366db341aac0d641b4fb8ba15db828c9412bbda9a1cf476b9ce011151c04ba06f966686a4a6ebe61f2b2940bba816e0b097b35df63cd01b5643b928bf454e46a412eeb093caef3b0b66afcc3c5643cea24a06056b9fcdfa2e200206261a8c55cef1fd4a8083e73ecb08876a1b0e590d302d5a8055cca152aa29dbcea115a317475ebd858c22c0f8e0101d20063efd3b15bf4189c1d696647d9dc16b69e31ee078410370651cdfd79bd7eaba7f00e486eb46568d16c967f040c54f215c588c8b9141876e9b2161733610158167465636dffc128422d80b4d1ad0baf3e46974ab82c18954d0b278e48754ad93f5606e8c3df45fbf7e732995d52053e372620a9fb041067f5b490c2905ed1766eaccc06f07868c18663c104ef83dd33a60e59aa4673b1d18a572c508d0aa2fb983a8db685abb247cbedcac8c08d53bd2187d30fef67432bd233578fc9645ff8651428ac1ef08711adec4267c74c76b42c9a28e40bb44c4ca21145826192e130cbacd09015225b02f4dff4fa4def57f6a3ae14a1498beaf29cd5c58ba9ddda438c7e0a8b18b6e23f43bf8be5fac35a57db64802cdf2cc494353b67b446a2d2af8b9dead45b252671aa86feccff95a8d56a6fd35b1915c9163a8c9851cb712e8b01bff87e93019fe2bcbb3aa243c6195e46bbe92dc8208896d3229ef1bc1a7369c056c73b272cc95544a8d001ccc0ff817a96916e667ebb3b1f5cbdd8e06a90b81c6a022225a07e826f3425aadbd8a36436f78e96077010642e9cf226d2db07f5a48d42c895d4669c2c494603b60f6115fd290434849859b3d1056181cb7778d112c23fc04aef0e956f05ed8f565773d2294992e504c1bc53209c0d928843211838d22a03a85041b11196ed8675977bfd12d56a5e538d96882458d6a4bae466d354830b3b634e47dce4a20d15c55154b21ea646ff767a085cdff7cf92086f4c8e943fe67d4974b10ba6c28d49a3fc88d11aa48dd3ac3654ef6a51b5834629eb151865858997f3074d67b7795c2b78f7fcba31abaf0893225bad87539905d63908c9d79f3520ca655b4cd85a4715ac94542c407c3315295104458f2ba05c4f8d4d08e4429def6b97f83ef2844abf2a67d29744dbce9f04dcd950e150ebfeb325ddb6606dcc98f16203f30687c5fa05936aab142fedd9e42dffcf696505a4a8c76e24652903024fc2ce065ba9d7560f0984a920434378dd8d28e783bf809d85ddf2633d67d914eca9218ed7d2b580f3b714536074b1481e0743c42bfd2dc6741380e01d697c2b5415bf2578114c3bbe1d544b0a7d0911cad4ccf1ab60f2d3892632851816d678f036f7e57967a52b43c4934ded092c80ef12fe14513f0458894230aca858b3c194b35f23fa1ba520523f0a49d6816553c27ccd25537c2586d8cb8eaeb1cd68320f5db5528fd1b055d11e892134d331ca5c985967ad7cbc70b5b5567516d119206b66638cd70be91a4875b7af422ae1add03662c3d9d6c849ece67f94a56b8300a3d882df4f45899ea7566a67a6216a74df40ec019040830582e17cd7275a5668c2871e0936baff0d52fa4e8e69f58d9a75deea2116157237ddf1d7365d682a056db73cd62550d54290b809d1525597a9fb037fbfd4cd1b80f1956a692acd31f89d0ec09e158ca6aa665ecf4c968b47e85221a705932383c4a37869b088b4fea63ae5ad8deb1097823e019b55b9d4daa4beb1e6514c12575db7c674fb4dd2afe1a472dc3c002fa6c2540c0eb92b8d91cf00bfc5869260cadc32373791e8902814afc644cd34ed76df5f8b1b644fee5e142e4d6bfbc0fe5f265a52f3aaf0b7c5fb0dc41775bd0e958503c2f151743dc6f82c4fdc409e6ed5fe69d519c434461d0bf58010665ca1f9dd567c9819cfba2b90e40932dec918212672edbd42d1112db18c64021298cd19a7c92f55a569b3b32478087576e2b630f48311f526cb00b5a91cfa94e7546672a84806c030c3da0e07d1d82dc5c9861ea6f9b1c59caaab286728b20a831034988d6702d03c6f17c8d2870fc0ad1df150dc470232a558bdc887a744a94919be00153ef2d02f02834a7446f198e42e201e921bbb00252fedbe01347ef0b2cb68248b713642484827323a85fe07739d929ecc90094de9b45de055d651e4f97c4caa2e2468dd42da3a803a24e4ff3573aa4f050f6043f551d4265647bd0d8b1e9481375274c1464a1ea1d3fe4b13ec3a574fe9df3b53fb48980cc2ebcc06956fed453d3fb16d743fd4da5082e1afaeb3e817422912ed3bba69834bc1539217d583b874ff4c8d1eff11133568ebddbadc7ad813a9ae5978064bcdf79f4ce3a971666696d907754148d3fd07c6dad2fb831f2f8ce83ae009b00ffe8763fbf180823d755784853e96450dfaa90518b1b6fff2053c4d3e987786e25cfc962a941ecaca1e8ca51e65ee51037cf8b31e3c4514581a3f2065b3d174aa4d9952551d83aa06c220adbbca35ae8039840bfb0044ab0ba205e1f4c15b107343242f34adb20bb7662a1aef18fd68975e9e62310082c51c093c34465412a7f73ead062e09b90f3a6e08a0e8e980cea89a662ab1571400caf84b803b1028eaf2ce6eae6fcac5cfd624cec951b634f8d3d997a2eabefddcd302c7e3995f76fee067faf772534f42f199161e940aadf538cecb8eddfa562feb9d7fcc3ad3b0848943c2414bb7896d9c44d6828c1e07131f229093924dc94785a1e6a6ad4950642c23dd6510e9751ea8703dad3b5a9c9f84b7cf50093acf6cced5a7ae14ecb4d61f2df8ff48003f171d62eddbb3f8dc27de10f5a13388f23ff4880f7b55ecf0ea045b79be4c886a9e070afbc84b2bbcc63d8b821079578561ad2180ada681d20603b1caa2bfc11d4bd68e05b40b363fcb86e5ab7913fcd82d94952c7d6a6892d806ea8ad1bfa2059b55f26521635fa55eeb034a9e2e915c4e64e9f07826570d51d6aebc25f2377ea937a705042fe0565018f7e98624fa6b2c71e958cd3f587a2b74b64056288f8492a2426cd04e093ca272cb7743b7760b71cc17ecb1b7092bdb604278725e47cf74776b05928854b1615cd5586c668acc1aa4bc4b50e3608c6e73768f81f7d9571a9135c18646c2ffc514cc139bbb90f72d73aeae6ef34c252bcca1b1cff21ac1866e7daf216636f489eb34150c73579b6270a8dfdbf5abba30dd237a85bd71aa448d7a0acf0e7d7d398d14d5314f169c6e5062ceb6987180f30883590d8c9741ec84eea12fa4ff6640d791d999a948d6ef5beec0a82415d8689c55e0933f5004c327ab367527dda88912b607c5a15eff1a7bd4b854ecce72f51db5452de780e4b978344dc2b381c44ff774a80454710d161431e74c3d6d7702e850e167c0debb2e45bf9f7fb7d163fbd895dc86771af436641f81b981bfabf82c5d960736d798b9c7983f6e8d910d86ded5d11aa409f503313cb0db36ef6f32925aa9171c5740b9804b53d9eff0ce06571de0c3f4b5b8fdc8c88688daf418731bbf6335183e399d655f4217c994669ad17e52a76f2c586492c12e51baa4b3b3fe6bc4e90963305e3c9a989db7762d0fc0fa47f51e85ee0f752b1e2cc3970b5477aea6e236661567a1dd698c456b4bd78cac06f6fe2b6f32cf2eb1ffa02f420094950fabbc3b4a69383f49fe2172d5287d4031f99a9e38c805c40ee340ae710be2a85634dd7683dbe9c15d82204e46ecf5d9d490c6d1e182ca6377ed318b693798ac8d81d7a1d3bb8a83980e658f059776dc90aee5b725cc80668fda78c249db8a8c0841b3bf12005f9dfa3598bd9dc81ce940459e25187078bdf572154986990022df610ef65daa4c63b967c0f43e0ceaf00d2df3631215830e39a020cea26936834524a697d39a942d8948fd8940cd9b4403d6d7e7c736dd50e4ea9bf357d2e849cfbb77ed13c956aed8ba06a4d32e8ce4e03a14443d0010cf8fb1ed113b29da6bd1f6b5e533692db9d789085ed0f148305232a9831d89899b16fa2af2046f8b03ca4547aff8ebbd528083941383c979986cbadae1776afcf08a15ac6042845f5606dbbb695dbfca71ab2707c0ed69372c87cb3e41a94a1c90dfc8675bbc544c4df9ff119a6499d0b9904f4f42fa8ab957b855d4594cd4e5d0a6ad61bcae75da6926238e1b8ec604b7a7cd2d180830bd0cb19ad4b1512cecb9aad4c572f1353b22cbdd14947be4a5c33a0a8ca9858eab510520fc0dc34d12438bc542bf1acacd5ea413832a1940d3a03a8e6bfecec3c509d5d99caab6f51b4ae2d274bc1371ac6de195dfb49c82a44088f01ef443c060b270f5090c59b1f4010c7c002b89b10818b1a06a6842824797aa02db251def55a77b253fb3e4726d80726ed7f7c731dfd28de177b51a2da2ad053c6bac834b5a0d0a77faf3ace420908064b81473e4dcb245d9ce5cdff85528101d5507c00634b9e16c9bc75378c34e87e4adf8645d93f469a6a23894c6d733b4217db0e9df422e12275acf9ee458f468244af767a94eafad59aba631e4cac2b075c7571ed58d456c4489ec37a9890ae283eb948e1a3b89d5862be3f4e8c862aa99cddef698b82c33c15417ae0a255a1ceb96330b392799802d8a46e7c8b261ee042e3ad724e164788d76040acd0603da03294e2508071c3e530fcb8b58da6753788aec434c12d3419ad3ff8f8c0e170968b1ba7138b5cc1545845839b332e50871575ce10bf160e0f41fdbbc0384eb43928e557d38707c0c13d6b7a6c099905a8acfc2b0e89b642e805554a922460cfba5f068ca9e023d75fd5a85c3388ae01c747417386102a2e7af995c6f57076f9a5f9bb44b4c6cb3e151bd707c867ad32a9fa8402496625b0ed9228d4bd25c12cd1b7b1791139b069858b4af5d9cdd123f08f104372830946c62d6c5a005e66a71c8ca03f4eec93b5490d9a4a1c182a4216024b3b6c727e3ed8dc95811074b93ecfd9b974ccdcf468057b21fd6408aeaba20377614c9b51db9e9f8cc89e9d0f436b332ae5f26db18d9cf42f5159ff5945416a5e2417958d6163b5271a96f80ef40236be2ab7db8e437a2d2ac96ab5ac70bf393edc568d822b927b42486289edc42d159d5fca889949060c3d7efa6e0c400dad4466a2741bd18d90ad59a96f062dc3e79c0a39dfe53ad5d984bb41aabba11bac60942c21f34519942f3398ce1e7aa20283ac9858c0521b0fd663d13819fa3375f57187899b0167b0689ffe8a5c361a55344e0f2e1bc05c3bb22d6af9b8eba2981502fe6699debb8da6cccc85e825ec7402ecd86df099debaaff7fa142046869cf2d1152dd1efdac35e1b001a0518bb88567e1ca99d428e288a57e0b5a6b841fafff2ad2c108fa2fc2523dd871aa26c94c98e16524120fca53badf3e8b00e968a4cb587200bf61283ffadcfa5ff56ee806088458029716b83942e042aad8227ddd662b456cb15b00cf1cbda8ef12a5eb17cc7fa6f9246c4ba2a04bf2fc249c179b025898d31b98011443ecaa253e1cf1a7d0e0d55ff67e3d1ae2227235c541fdc5fb31a46df1b8c46b521cbb95f6f5d0ba830a81b18ab2610a8ee82499512540f17a40b22386f581cbe0bf23e611c72c78aad9f3abf7d85c019d4bb62c868bf2806a99ba86ea292ecfd3c6cfde936a26f9fb950f90f5288764957696109423796ce9963d9ab4e19802bb65d48cdd56152e5f643f380c4894aff50895998446461dd5301e1d7f5e6f58bb334401878284220a76f03daa3171ca1ddf8db8290cbf05ba75b3b3041283382c86471e6c5182c0dc7b9465cce2e561e91999a7c51b72c5304ed27f8a1640d0d5c8d9d9ed983bd7790559c1e42db2b2619e78b9d76a3de1e13296d5de8519f53518902b672424412815e61fd93b6ca60bd698c20016c22df33fdf9c31c3c52e5a662206e13056b9e3a4183fd5d4976946aa3a3cb6657ab094217ea73550b60d192d47ed7cd074fc21b6bd422e3abcfc3962dcc5d3d646dada5dd159813f73574754881af605dbe640ae465338528e8666020eddd7bf68ff618ae46df250c4f3e95614a7b031b61ce2a08821c461a786cdceb411bbc8d04a8f07ec238175fbac1eaa1afd6fabb9a3f1002f44aaf1b124da9732abc350a639f4b99388f98f44e5a2696c7040cf5b7d506c19016c77c52c97c7a3c20fbc95b37f7336ceeb3674255157c051b3b2d8914ec9f6d149f2f6fe733b2b3eff5bbb7ae9d546f4719689d2f878a812a465c02f0141735506285c2157f1be28365e64e16bb71548a903e6da6529df38d3ad94ef406a251b8493df64327cd325aa5726ea516347ec64fb68f216393c283f37c87150fb657197202e92137395a4b09e8cbda36b43bcc692b979879d2e3b8e42c96844299792da3ab1fcfc37bde9c39f6dc171f4a33e65a1983f428c670535d15f77ac26f7fbfe829330826ed5bbcda628a4c19dd179d128e144e91e1bf86748723666f33d06a59ed409f2440f29ed62394d3bd0826cf724f3f411a8e0843387904aac233643eb07e2fcbc5374f7fa3f242a69d1b861e4c4eeef8d3bc11ceb00d62eceb32a20aae06662fc05a56d85926e842b2b0e289b03661725bf5325679685df8e452780458fa1939b48ab338a43fed970976b0366ecdd566c6b7dff6ebbc0209b8e6a8026ade51a46604a35556b0242887b5adec84049ff69675593c3b7a9f9ea57f4543bc50dce5f8eecdc7f8ca263b4f513193111cb95d4eda3e43e63c63a63e018d9dde38d1e0b839aec759e98b6fd0aa665ca5b4e9b31314f3a86f5a4939d6bd237f7c71fc4df9187e7ea9136f81d6349f7e8811ecf9364b1cc32d6d1dce58d580dcffed40912f6e77f6d38edf1966062515fdbc90e90e0d589cae75202c7f71df37eda04a1ebdfa75db33b06730d5f2d15ac19082d50745f0af0e379fad9f45e50de691fe3c97ddf2921897fcecdafdafe0f28376e8e7ac0898abb3b4025896b504a87e758fbba6e94e088e825aeb08569cc191b3c76ff25168f9ce5064c4c7d4b4262a2c25a6dc483a910316781e43d102a9b46c89c3e45570f11fe32cb44bfdccee25e729092819a5db246a6983e2693391236edc243fa36c41f09ac49f17e54779e5e6c59fba3ca25fbaf76deef123278e164c2a2e512181de16ec1e13df15cad237c8050bb325080019f6bf9503fef2c349447b0a219740b9dd0dfd59ec9cc703ebe1766c30e7c5b9c5f698684202a4f810c175c8a82a8315e4b943032488f1c271d4e38b991b26593e4213819ba9b6a8a94fd910191dadbc9909dd208840a8f86748dbc38525a045b34feb36119da90c53867c842a125a75315f7f72cf03caef6a8403a3ac80f7b13435028280989b1b27bcad96a69fc0ac1ad7144e6c933636ee03cd3c2406e18084ba14f35100b8b93f764ced4421dd39b19e8537adb5b92b4bcdc12ec2510a0175414b949dd184ecf8f5a51bea43e01ad83642eca4450700fa2ae5439389dd2087a879e6437e211429cb8d793ba0c7a8343f66271b962ddc50df45a5f4abafb0a4e422bcf343dab330db7cc8956b3339c573108a3dc22084931bd78f30c499bfb4cb83a08b634831b82bf1c289f0a6353516839e5699a11785dcd86432ad2eb5583b1844c1394ae606f037f63962b6fa4fc111bfc2fcc1a4addf1b160143758a482826b368f0a170676b5d6af0e99e9abdee8127156fc8ae5127d4635ce9aad329a073ad3415dd3ad5cfbe3f78ba0fc466a8e65e1e0ced4191f5efe946bdd44690803352a126fe0517bf702fc8fb27be6929ba301b743df3a248dce1ba2a7ecb0e3f28dac4790a9930a856b5088117658161be4be3faf0c3717e5364ab5e985c65360c50b3e0e5faea08977cb9e0ef5edaa9e0e6a66395fed0aa99c1c1282ec66695e26edc6b1ba3e5541c3fe8be6564bd99a2194d8294b9e47c1c8de121e466b93eb01f7e95bed99b92ed747ffb574914d383ac2dd9fdb35baa30c80dfcb109b841855a592e904926d42505f2b32d0bcb2b197ef12be832202f3a252e4ba7a00062c876b0e8a04d248d392c7519cab561646b742a2b4d82a441c59471facef7b7c9646baef74c462645f3f3b9a2a75c0f4c337205baaa817411190715325bc2d487c70721d1cca2ae134521bd37aa4d798bba709594a653b0e520907838ce9b1e92dcf5f46a965fc34d14816fc2b030f2107c41014928710974e5626d9f6750a1e3ca7e187c99018424f2085f012bcf7cc8407146af5b71c924709513a1a39f5db4c52d034386f5b88ebeec20660f507275003791a873f0bc1930c9969a71fec72c5126d315220e185cb8c0a98d4f122f3098b863b3dea7c210735e182ca33c3b904320a831521f490bd039520ba8af33cd7723b1fc87eac7f388f9db5765d446d479611e9ca6fb8071215708a1045cd8da5aa543084f2d151b0fa174aa9227005f32028306a813fce3642c56a29c53cd81a14acd56bfb72a744e471784c7cf6feb345f98c4d7d4c328ddab567f041d92a739be86a59a10c308777d229b4761224579f0f9f07fd64ec6cdef699e7454adcf10c6c75e2073b49404ba2b599b40f45460adc241144876d5009a7a49617acd5e7537852622ce2d245bdfa6a4243f4a69722852c764ea56204b2484c0c0b74dc2eaeac48c848b2271d90ddacbfb335379fbd9cbebe8cde3ecc51f46249a1b4a8f28bb73535d9aed296a4d3f7a56d712f9566d8eef059b3116e2f9a7ff71c81890a776f38769b5241103a93f1738d0b036516bd2d8a9c23b702e4c8765400a516ee70b01b1db4c04eba50ab336d5abef645a6e1a6fb1c402ef812358c3b2ab86c4981b140046337f47b12c51438da2292b7e8e80a22db7528eb425191c9e3f47c1b245d51334962d66a7c1a3d49c9afa955b2f983f00c3f8abb975d8981b85473c7b70ea2cc9bf639176d5a8faa700226f5039a806ba157a71c6a21cd1af0d2fd40c4f03c3a3404f98b1d66b1f7f68dcedae8912ef4961e13a0a596484fc30331e2b48c77d22c5fe401f30c4316d412f6dfed24d31ab1210c6bf62ddbfdafa516c17b51cc8fa9f6949fc53f7a87ada6432210334038243b8a43d5128ef6cf12c84fcf483cd2497ccd6da4551eff76c3e3a5b2980286cf94523d43cc294adce323eaac5b73819125449fda5a315c002b0f528a4f714f0daa2d217d6024654ebde0c37bbeee862e24220cd43e3040fe850614c216a43db89a13daa22bc2daea7e4abfd96568dafdf958568e2bb636ab03157debb5a2ba74820a8ead9736c2b2edcda111922103505063e746963aff20235dc77d513b4422ad97bb8a373cb93dc3b8ad720fb70897bff6a518cc3a05bedbcedd82db38f7fb61e7636f6aa549f4ec7bea03a6ff8e42c862af9cdd34246a9e46bc2ae4cb75f1c6845c4b014358927bafe774add24f5e307b85dc649ddb78d37804abd3f39463bd6b66717e44050fa24cb7d2794deeef228ada03cf70d3c6bfc0d2175550ee119c021fc1d70c79a5fcd675cc19d1a2a9e509809c884099c780a0c354c13b581816727c9f45f2e9f4613f33c62f2e1dd3971f7e30e7cec9a730afed221dfbf73d4b6b02bf6bf40150cb5730ed31541bba4a8bc782c3f567ce0d094882eb2d6b1f7cc62bc71e47b70751e25c01dcb8ab49a0616967bc702b18e31c3bdfea34a9d3a3085b8dfd7bff0f141d7f1bf0160fd6248d94168c7aa45faab5d9108af951b764daab5cfed09719b05e8559dc2b0bd2f915c94fd8de1f9be9daffebd1df301dbb1c67601ed83e2df9b15d33549dd4907d7c86187735b3d20823e787558e14e66f5fd4da8c327155bc181c57227c6a60b3d2169994ce20925e003ee08e2afcdf41285ea8c75a12973093f65d4a9cc62027917bde4b4deb5bcb1363fe016251a4730039ee42302a5416bf3e532d3ac3e3d39bea68a513feabe57f3d61741a45ae02437941b33331ff400646acdf7cc1584fe709a3eaec3761af5d8b9772384f7a5b4f1b24f25b5f60d8a7677f0650a28aaf15ab90387f12e1e12e5c7b44fa5c97bab3224a5320e2c9f2f57f45b3b5527c9710f742e2eb62abc9beac2623227052ee8d459adfd83a6dd2d7c19405359ad6aef4be51335229d7c49b1ad27b5ae751a105451ed0d06f0e74ffe6459907bd29df06a433b86d2f235d4b36c1dbc2ae3f8b8adc1b91e4dba5038106397575f086093f8a06d50ab3b17c563f791e9d8ca79bb3415b5a9ec4cd082ef87735dc5a9681ac20055bb03e6347a1e33ff0116872a83da120114ce9851eff87b038ef558e1bc35cede5c7ae5c48d00bf5a66dece425a0c8229d3296dc57b7adccdcf8672b936a02c07a8e5fdc7ba1e864bb9aebf215cf825a1b5864c8688f0461eb4b412a192c475209ef984f2a9112db1457501d6d78739db2d59464eed9d1c1752963d7d46da2047d243bd7da79906a1d7c9178e6320eecfb4909940ad4f25c54d398b69f004e018dfb95a8a57d34245d9312990da20ba377e4631840280de6e8514c23f5b12d2e0b721a7e6750cd11da8f4a4a1654aa27f7ad5538fdc4d2198d63a8552cd7355e4ba8076fbf4e70d0b865ef3bf86f3c598fa2544dd51a2e390b676dee7eb106cde76634c3e6429411c60ae414c8df480fad4d951808e6a8415119187f4d551a9230c9ef9bf8be08199c6c8d143e0bdeebfe81199966607e7ea44e15632b06857188dfce1b5faa4abaac8962d54db08bca769d49b0b016a9036595ea2127dcfb24abf8b5a2dcdfbd42780a9aed3b8b24dc850427079d493aa4a18a21a7b177c58b41e1592dea920c1f7d94ef5e596ad5229f9f8ef63baae55b8b309d58d00eebc9e33819b990d9cc66a7a05cbb93df6b584952db14489b28300ee6306c633b78313ba23feb4d5efff64f61090ffbea52c957d132bc21b35d0c06b290c7478b3359f5eb30687d2b9f4942bc4c029876ee70e418b5ee639bc2aa1439b28adcc0429b794e851c62a7d7a89deb2dec1d18b07b245544c6f4ef7da660a880120b71c2efcdddf48381b793b2a4cfd757efb0d2505de7812420fed223180aac2eb3a11dbb8b77329f392a78aefa4e462b4207ab417d76d19fa52b8969a2641105139371cc99cbb095776237fae3b5a5bd45ff4e8733dbb350a5ce12d09cce8ce5866e7021e40a1f19ac2c62a18265ab52a4018ad825f0850aa5b419ce3048b1c017fe22b0409557c2908692b49819a56ed92947f0c714a000ed6ce05ed055a54170649f4e89cb8db4f7d40a8be6c9805bc5bc43c0c53462ad3d16ecbba7f8b71334e667eb37b5fc3e425ef157e80d0bffe5adcd0f9a40304e703a1c5322d1b26f78a6c973322d93fdd9f15f4569680ebe64b350ef44065ebf5a5bee72e695f4d666c9409a9f76653fada14c18213b94b43d5126b47d8597b3071f1fb81cadad7339f50556fab0e5e96043417adea35e2c36df8ded1ccde8596ef0c3f2841aa0eee481da7e4f506bd1338790b8bb6b7c8cb63c461804ad72ceea2e753670e6172e96990c8def287183e7ede12f904133ce74389e9cceb8032b31add1c0b1b0c81523d3a4103ba9c551f67877597ea9c72680383a158fe3f46dfbf53c468f1d2ab480e74b685d1a75f725a01c71bdb7b85d5e0935979e5f0aaf37d46aed0d6c60ca92275c7588f2e5f5d85b5c44e291417945fce417d641ab04adb30afb3186d3e39372e56caf2dac77443ec2eee7854ee226ade8c564c9f38b5371d035f4772ed7b6483adabb008afa78cb7b14a27857e2fb2f7deba0c45d36f9063c2ccc83eeb7eb2d3fd9ea678993142b1bae166b457179d99f9526744cd663746aa949975200ff8550c825b3be630c91dcc7aba7b7bc23ddedbe6ba2d8ced831e2f697377e315bd7a1cb20d484a33ae78016196570f51c728e73e5b958d76b2ce32bb1bc6a42c406f71f777ecb03212de2c6fe107f5ab56ec65e1c067331292f29a6bd37013ab47b9c1e08a68bdca526a2f6aa91a88ab4bbed003675564ceb2645bf7122885006760a60ad29efae8b15df6afaff56cc289996b2bcb8916c4874594215b9ff6363332ce1aa7a41dbb08403893d472644d095a0b8c07042b1a985b265a6709727432c9aee8646052d896119356666ae01de1a3fdbfb56dd31bb761b3a496503313fa5349ef7690feb4706a374a706a200aab6aa9b3250b482799f4f61cb30b7a93f969c5581ede95a75fb7c03bdea398e8a1d3d7f158d46fa82a6261188c5ef45ffc86c9aa92699423d72f037168632f21984cd2cabd2cc3b7d73513653e672ec6bfa756a4369db82ae083a1cb0be5338f0d77ef1f9a2fba5b6c5d2daf2c70dd686ab97bb9692c22071944ef011a9c1809869dc625d463d3afcce057a9d81ac53904549a9a0f81e2083b59c79767ec759aa1806729cab141e0f956c90aa31af875e608502ac8d30e1f0a3a2ff5c1ee736e9016557fa52b2de80774c41a6806c33ed42e2206712fc1ab5c5cfc1ec15ece64796cdec08d0a4930ba79f3884b195588a3f6fe5fe9f3832a1b7af239389136f9a4f13b7e4a39ae151e806ecb88d0789a56087b1b57918673c0042867d8e165c14e771525022744272dabbb0b690a54cf75a5866b1b99aff5b9171cee724000267e8fd5641baca9b165be0e0ec89be773148de447e396c457926fecc0d268097017c6309d21fb2d9ba4c51a223871e051d67b5027efbfc1b6072cc49b2aff51bf45ee43790c13d4b3200e4b4c63e567ab068a97f8c2e104e60aad32cc26765e6736c1bcc5dbfa4c7b7d06dd3df1f0b2273a17d1c2ed4efe319af4c1c75b3e9db9fa091f16916adee9dc904b7d098442f04f6903be6f57c0ff02b64f2dc64fde3847867f67b926a49e216b0b8a7f95dd06889afeceafb764ec59893afafebfd29a729bb316342132edda6b82acc900931afd7ca5961a71d8f257fd2146cd008be2cbe058f0eb8c0ed7412d5717129d5ab894f36ce8d88f5ad98d88f38801776ece2f5409b6e6a60080714d758246adac7616fa07442e474759af259b90e6130c8d74173f51b38b7acf4d7304dd4a73b5ceb962ba1c0eeefafd8004d16944b764ebed35a661736bfcf126391696ded0ddc1cbaceee7341b3ac93a92ca0e0389a9133383eba74b48ff92f661efa6009e1ad7f577d85a364ebe7dafb4ace97eb81f40a0b550fae8e7d8bf2f82db0f5718ecaf267d2fdfae7bff409de286136e5984b6ed9bddf1dd90c82bc51b60fc677c4452406cd17c22136c6bbfb988b124f59a8ed28b2afea11fb1019d989a803ba6933ae28b46291e31ed4497c26e75446c536ad57e8010b931056c3db399fe1629bfa70ef5ce9798e41a096dd92266bc322c03024a8e7fe08730ea24731447a8bd6fdaba5860534e1a5a653db0fb267cf727ec22727489c6f0f928cbc293d9eba86f9494a0e75c3513fa3417a2b1e73cc517b4c8795660b849d2affe8c5dd6643bee971f460e06597392ee3d7a3f55cfc0f0bb47549026ff7e7d2d71277f1c37febcc8a970f6bf97e575c59187fa440131fa061922660248c870adfdb904cac8087fc31555c23e0e2b28cb5dda57f664d9bcee46ae9cdbaf4f6720dd332a973e3dc4b4bc24ec00a2dd2a104112f8cdab0c4751bf6db2d04074f3bc172a45bc672769d6734814beb915069308133d3d54e197c0c3beb90d63eecd7f07de539d63c038a12c2fda6d190fe1b366b35c755584ceb151943debdacad43cfb8b7aede5fbd6df0944cc3de024459dc12ab7d8f725d9652d64155ea037b32c516a1150a92ba052c780cae29d00b38a3f14ca4a9d5fe5dc13c8310b7ac7b5d43d6e5922eae8545e663157aba0a7a3967965c4620207d9ec45ec5866bf85c4d5b327692435dd744519b1249d3e6cf478705f009edd59b0725110ff521c35c0a274059e64b3183fe2dbf596a9e08c92404852448920e351a0772a0f9d6ba74d9cf80dd0b3042f2bdb267463afb99b28e48695ef3f6519d63559950b9784cd299c671252ab59af537158baff82d7bd37498d82818d71e20d95e851dc4f639460c85160ac8d2adae230f4dccc3d1ee79e197d0a56cd2216d1b7bb8f0869e02bd2ee6770ef5d4653227795f6291421174e83ee423635ba6f80f4da4cd8f940580688807cfc7c08199ec62af11d52e0743b2bb07aafa493b36c7ce06efc63812abf34ceb58cca7a356fd977895a3701ac29e3456a970b47c133f147d3d24cfbf44082563aa93bb200d61e1d64baa996a5d44f0699a4771962028e1f48ac572ceb8728184eb501e26295c1602e05509b28583913a8595361a165f83d6c30e3f24ea5bd533ac28b9aeeb49443291409b772dfdc9489ae4ebb91be91d7775c2531eda23dd1a2b16c0a3768f692ea8ce0eeca52381266fb0611124e5f995cc1cdd3f6d28c627d4db13e7d4dafdb8cae1c72c18427e317e17fe5f9cc82f8cd2c7639c288344d8cddfd44e5558636f806edbf028b0adef454ce98cad30c6137af05be0b26eea1984ee84547aca7cc72ef5ee810d90ed4fb7f20e5fe439f96b4643d2ddd22f4dc2315e2bbeca7c45273f0b2b88977d8f521814b6af96199f1be19d6f24c55730953202de688361c21a1be0281e358fcc696bda63d5bac35404fac77acfb5ac9caf2a5d4dfd8e17ed5649243177792fdb129061c9bc0bb9ffd63f8cdf76d007f482cefa996b42dcfab15ad84d2fe4ab3048a044948eaaa3c23769982df4596016a9414a30379b3a4721a6963167184573d1c63a629137ed4137d81cfe34666f6e8a0430ce1401e5481a5b1c135ef99dd37a739196c45fae34ac4d52b8065a9d2206543db42ddeaec865650bc8706b1eba88685209b8177241691c9861d87d5fd98fe4222a9212379d247bd6963779b0d76fa983a5cdd26a077fd60ac2e930b8a6b651cd7028fa5e543cece6b7673fc2d26b2bab24381489522f9a5d0871b8226c91a6ea944ea13992e41b1ce7194fcd41b60ed84cc73f0b1028c9ccd41cf7a6198a8eda1a8932b6f86436fee8a1b3302cb29625f1d4733ad61b47422f2110e56ba29396309b73f8b8b57303cd740dbe149178ae6fc773d355de3c96f594f027f93608f751b2e6b9d0b9b4926738b93dbfd9ade89bc4752c02227939a8ed400f8d6a5547dc2adeedc65638689da9ec4c7d474237da31da7f4d3d5215af72e68bd44437a96aa3dca961004bc3c5aa73c6b37cea15f9878e4294442db40a33315cae36aabe4033672fe140dc5606212e01830c69f5f9420c7ce2695673e362fa6d0dfff07082bb13c660f8c89b90f9ff5866ca1cac1e05ddbefd3cdb9a45d24660949156a8ccc11296981325f33081e9dc904b891f9615542430b1d4a8ed5c48c8ace2ecb400f7778d3201093fbdf95c4a0b69e68b5355632c599d14c38def030e9da40e300859f391a2b243a6decff44b7d4f4c5f919d539d9aefb8d99e546e5b5de2f8a3890f36574d8370977a7068bd05559001ec588d2454d9e5ae3313813db28ce4dd862e61d48f6c22592b460111b9dc22d1b2065894e216d2b1a4ad7f9b0218dbefd9e92dc1da75644e2f371822c4366604ad21dbde36d00273c6e822667d3718489974a4b612f731fa42d1b65955e936bb709bb518250ce3bc9e1904c3ff28fbe4ec73c6388645c3ccc9021bbc7625356033a63305d99ff977ecaefef92c7fbc3f8ddf9ce41c79aba760c8ac6c80768705730024491c455de47537910ef656a4f42ee0948424b4d4b85777a46beb431bfb50b884e81743fcda2020d5cec1d8386718eaf2fc6e13bf07b3604d8920250e8f007bf5c0d5cbdd60e37647d110747578bd82ea60debf69acc45e19482d1fc3554d4b28b9d038d49ba255f2adb8311f0960d2afda5a51644442b4e40aa1e41b69e6c7dcc5179b0f95826153a6ca9486bd128aad58b0a336568773eaeb86a2bd692ca20e62cd5e038c39f3c94a3c9801288a1a88dd18834250f6d15c9435c1c79e84edffe718de1f35667d6f2a3ae7f8a4b92d8ea5bd6ab04b8050c9f96beec53e651b8a7b93c6488b9cf54a042da39288c16cd509b0a432ea019b5e86f000f254526f8b0b5cfd81baab0557f1d3e9cd23b66a632f6d7469a468b7b706d54e07b6de02dc7ee18e8bd010581db5fe1f704ef8f096e472ad6eb6ded8ca5cfec652469d5b3ceb6a0cb082f8047fe02c4f07130be996a5ae60ff6c26907e953133483d3add1ff4b225db144c1399a04f92c3f6a898e7c7419d635a49b9ed30360d9e28331780c7bce7d4e144c5514cb0c3d585388c49bab4dd60c004e6c356d189f7e4c050128bc1f68ba4dc7c38cdc448fff26dd1a5fde07a2e944895c691d93ad751cb3791ea7f2a765ed4aa0a80a86cc0ad60ac8f991206527ed17744d3455179be059593a08763bc60d897d52601e8749648c5ccf154c9ce391fd0d471d14dada550208a7a13c7b494c1be566e9ecd80e2712b299b8ec4fbf8f3d9051266fed96ce7daa366b8018ed4a4ba912be0a4a836e97c5331eb903875a6c0769430ee39a64e41f86978df19fd6db13d5d2a4b2315c2760daa13af07352597978e644984326de9a1c9cea04b1ee64723de5f04cc04bc64f9e9c8f68ca2cc0ed0f9fb183bb24f3a52bf287b9fd15abce51e10b41a63044a7a96957b5233689fcf7fd81d134f799272b7721acfa8ce57b4124c2ba32fb6fac6b4ed6e8640cb28c7cb983cb0cf914c6439decdd0089e3f2ac97cf3d8b3031cec86ab8fd2ccf2d88a7cee8c0e4439ff04c46784be4150842405f53208cd8566db055e3414c39707ae11d11eaf7937aa40835bc5440d71826f6d6a8901e122b307452c94525d009aa8e34a5a2d6726b39f1383970d20545b3959638a33cd9898677cc55f51e90ab63da394f67420942aac0c90309e28870d286770927707582295f4328a24ebed40758d469cbf79449524862cd6e882ad193330e91f1fc4d3b55893a67deb7038d2617b389d5d01178946ccc02d0303c2428028150837a573ea31bae26d1b4ccc0f493d427f819ee9a27914fc2af7e1327d90bf8f55fb62784123778c7e217abeb49b5d7c9855013476c801277f1b940192910d03acde165dde32d5566d05d23a27c29bc470cacf6a624a3b053d1376541630ca3d26eb83d69e586e52f1c36e3c77288d503ceed7683d3c03426574e7d6fa6ab93176cb4583ef3fb7ee8b78d2ff003dfe8657e66cca04dd07c47732727c2a8d9e86a0e97d17d96b1d500631462391992257f5a338231d8b53d2cb8b60ac28eec36e09e4d118431d6560d4ff682878cbce4136304b90389c70732078f16ea0f2fd0a5b95f565d6316834f5a804a846233f4e050ca770cfb73c7e2804ad15c882440dca6726d567b0560111d47b16e9a55e56274d0d3d83150ca198263c5eec61b6b0b74632f6594faa551ea6da3d4eb11a88d03289dec191b23a24693e8dc5e001813a1fd839eaa00dbad5f63f6da85c911606eed051a21b09c1f5aa01ca12e3498cc9276842a090e1dbc3658be82093d029cf22849edb709734d7db1c3a335d9d31012b732644a4c9e8be9cc761865f870e2c783b0ce64d81f967b5c81aa742f75cefd9aa1d4f49eec1725f31227178d28d7185ff74c7c68d125d908b50844d1f661bd09f01339c01f6c7c4b7b6777cc4b34e0f3c0c131fb1ba7185807aafbef88e5ca30065b937ff51a12cd70d840401cd6f8cab4b4da2894e2f3c565d83b969a7978fe1ffc340774eff05c8234da9fbaa15d74840f0c5b186d6dafca047f579b72c86541a1e5ea8addbfc87270df54c0f846ae655d1f3374a75f03e32d60390bd61b4b5db276f31ce9cfcdffb989943f9e2c50d896ad37e8e65b47d5cd2c1fa2b8ea33a0e53223f97b93ed240601f243b18245ca9a612ed240e778fd746b1bd5f27122954c880106c68501289f13412c21fbdcaec8b67d46be201949d9e7d5ee716d1fc54e63dc32c40e2f980202ba52c458a0658b8a79244ed32d88738bd652e588b88b71b2189481e45f1bd89b0160872706092373208ecd8dbda5863d6dfbcd8a779c592d94618d6d19b6059d0257c6156301a17b658e1db3f9ec11a7b436d4059e1e7bf6ffa0c4edc58fdaab92da6d1ab2534edad60459f8abe3fb40866c327109cd30ed0c4d96c2e4b89a91cff62a1e6d9d7d95d0b41b205c02552a166d90d5a5f398818388fe0d19f0b49f19c5f1080fb22020de7236d1a7175e7f498aab30bd917af5b797f4c3952fe777153c6eccf15bfea8bb3e61956b18d2c8afd529071aa5948322845506dfd53553c8d8919950bdd8eac5a7c3a6bb82404566825b031d78441980c0eef0456c5d3f94a3876bcbc9b3083c7f4f510fbd9cbc1429ae5e8090b24fc688e022c42630873b17c3a05028c6e651bef0250676619d0f4f215e9aa28171a194e41729de1d980b9c5ba441f6828615728955f1a25b5f68a1f835dd58e65a8c7427aa85361b96b39d9e29d1f2f27fe30a820e59f90ab5827215dc4eb43a3f9686ec064cb73917a807bf656bada45812abb657a124a3f2747305a03ea86ef6fea4878885557de88a12139ad50cac4c021953aa55bc2514b000735dcb1745162a8fa94b4c849ba76728b34ce6a2aa72d5fd5195fa2e62613f03df02d2356068751636617dd0fe2118e32052a6313e821d36b79c0b0dc33bd5048a27a1645e6530f6dfe1c8fb74fa4ed2e8288cd50b02aa518b1895c4be43c415d8e226ebb0d3bbd21614d74df1891cd0c803e3c98330fe3f630202081e58279f139fd9c24bde714f84a7804ce3433894eaa3ca9064aa876f853025672c5836a82756556e143dd921508bd012efaf2c2f7d80d5505e830dd535d8eae2f6314fc961d294395035b9e8c07e095d7a208437a53d919079607900f1471be98a0109a4b4299267ba204de47f1aaa9729dd0b59fa1c28118c7c1d73b6ce368624df781d44435eb681e673690a8c0ec88745403e768c801aeb55a2b5f36790ce318548afc4131c03c8aa78d027060018e3fa7e0857db8342ffd1fe4ce84d01a46923d845dec3888cc1e1f1fa0f41548b637ac6575ae2201d2329baa1ea6a12874116a7f3789b91a5d9fbb33e15ad7e8cc38904ffb936442a4dbc4a787202b1fb2a6e1b5dbaafdc8837687f839089c4a00e1c86b97a3f1efa4c84deed87be85473da9e2b6922f1cdca59a6905088eb492aee2b5246fabe8df73078138640e3433b07bed3ed6fefb616a45355009df14d561dcf25c7797e6dd5a214722523428a54cc0924c766458b4f10b2fe9704d1d01ad4892162d4b190edcce66125e964b062306a451fee02dea0b0b3c84a0dd6a96f632bc7c84482cbc3cef598c62bcc0fbe0a497e5344a45627f177ef6e03f74539d8abd2dde11be08296b5ac9217040b20dc2b5c747b789353b695c5feebc25b351e4c7738cca4612bbfa9b94e8c4839d5d445c6cb210e46f7d447e13335667812add207737e42a93d729f0607884910279696f4e0c1a3e8e72943c1a0a62e8239543a9710892d1b6debad168a91406479bd84613ba414a73f43cb8be1c7b381e75f67404ee2a29eecadd95e8bd1373e8c5ec037f74e413ea16afaa197daca028aa2190f959543091cd346dab85aba379a58c7461aa9926fa2ce062909d48ab1185a5fac3e850811acb981cf6902e278ecb0568a8771a7725658a19bf5185adb46ff2c4941e1397c65ea126502ec45e59449e526eca96586f9dbc5c2f998a40e54cda1584d9f9a47c6d3b023a8adb019ee77298df9e86e8d1f8e19b69e5f4cfd0d662deb057662275fdf147b57d7d66658d2fd2102e3eaf3106ecae7c4acaa93f2a09bd6054e7a42c03f95b37d48be90e571f383553125e4c00525dfc130795d02b1a640e02eae025ed952e3a39883f3f3507499856c13e45c5d73d1a4d7390d7b8ae1ee1b82aecafe446f0a71abddca9f188cad6ca24bf1f6c598a38185f21e63e632a11ea2dd5181c76246d75c68788748f2848115d9220452cdf5b78fa873960586d32b1dbd380b89c0a2361dbac06b46dbf8350f19958dad72db492a0157453c2700a34ba5f71359e880ee34a347c2d3919f7802fde0331850fc87a7c40d7970fb013bf931b788531334d5921cd15dc6a15364da84684dcebbf5f77d3b30aaf460a737aaea3c6a03cfc96c23be01105abea4d8b195a00ca5957e93de26d65006bed80071646dc92399c45f107a701cdd63be1b791420054f18ae27079eab21595ee6fbce7d15a322b7823fa1d49a36a3b8e6a0a8402b614091b24bfd49e595b08169a84197d59a1b4a52f5aa2f4f62e07eafbb0ce727db23ed86335c035e1bf402735534d5159f39ee3048a4065bb3c2d9ad72b01db78a52fed5162244ae10e642cbc8dbae10da7dbf47b49c77781941db7a1569b0e8f2f831cda4c35228080d6cfa588760b9a599632ea579992a3a228ef485c96a5e82252ab38f0205bd526c1af1342d73f491db7d79fe1e8b4609ec7fc61820230b175b01893e04b1456d97d6420261e10bf59c60ddc8bc5b52e61480663592c7b35c66c4ff8c0f84e289a301be70e4938d85891393ba12710d823014c911e134ebba88bc42febe7b01c42d1288354e33e60d29d20bdd45b81f6dcfbe0a15dc57a0736fe4f898219ac5f042a30a9be4001863e79e741360f9408e17373b759b37cd2259b0e90ef23680f069a1fc45fc0b83d100a756d241f444aaa2ce751f8139cc7f96713996aa7a4cd091c16bbf09742725bb78ea190c12b188f0d85a4dd1cd8ea91f07a79e4f412ea2729b0b1e2d3b4cf16b0b95899499eba2333e3f20543b546844d58782e0811bcc62ffd31310afd184dc223b698fe37017b8f9695c14ef710710dfcd948d62cf47ee878508af643ec0d111af5c3fc8fe5de6c2c6d62a9cc55b67bb5a728180fc0b4d45380a480074a27879e5531d2b38e1ad3509bb9acae8e21355f93147ae979281a5b940e41e641b27d79d7c63e5c1a88de36dca4bc1316ea1a52c4139efaa8dd2cd93ee1d33bc84b33a72ddd88384eceef5a154cc445221d509bc0f507cea22a77be90818b2f4ed0c437f2c8972fba1dc56b2e9c2371546b2e453cb17e8336c5a0e9ff5f945646b83aeeac41e527209332c884e94f651b1494c101b827dff1c1961ae2429e045e28a3c3c72e44c3ae2732eb2b6ec8270192b5f48b51b12c8561f37dd2ed3437075302a8865c82903f38ef3dfff9e69db5d43707a5402495147bd43e36f1893c3e904c8f8611a3e6909ae71bbee8b8da57104eae2cdde453e97803d54926982abbd664a8018b4b71541a7b47d33257657e262e1adde9632e1ae2bc02cda831d081fd128b72a2c073e5c34778e2a830cc7d515adceab2331bd440ad5b6e6348fcb64e377c4895ecd1a58a28f435e99caf809002e336f5e91f5ea9bebbade3491a9ebea2161ef9873bc2fc9d9171248003dc832f0cb538c28c5d5e45fcb43a4c566b610a1742b0b558e5545fe61e4da34bc49033876c5d1a0678a47204bd1f1c7df98ace9830252ceae23c8754998c2e921aa80f727dff3651b1914adb4826869230662998ea3ec3886e1e27280128c0b752e214e9abca1f801e694f4aa2bfc447421f04ea965715423291f85a114f3d9e6c54e112be13e84e07181c87259ef427215d18932aa0051fc9fe4e393a5d5c3a7129f42ad73e7a922692e2c408f8d07bcd25e43a498a85c43cb26a8f8c682954638365b1e98257fef9a31d76011c7681d296808667f104379a61938c12a5b4147976ddd721578dec961ff6d13135ffe97941e26b3b934b853127aa46c931f6d16520a2bb8a2ec0a2080c7c2598c154a694f727d3d8da49a44daf78b70b46db05e2089d8b93589c2f8edee8605599f0448c6c58731c243c9db33b52cca9448521be3c3cbe0d1ee736db881b390eb6a0e98403e9a1e2fb86b43e6a9cfe957582662beddb9e8dd3b9957776e632199523a58c4a602da3faad1897be8da9ce4cb346a4e08c5e4c7884c947f0f2ef0ae453d1356d9dd1634812223c6aa0ce04d9be5686b0d0c7f95867489a5e2a7f1c46780ea07b92531db73afe93268658a4a4d83adb71b4936c490242c938dddd25d31927ab8031b85d4db02acfe644e1f1407e5ded7a84d419d3aef2ddada878d43edaa86f35e07266655f9f17c9c92c4f34e44060b5a16ad16c57abf238c944c9939b3fc9004274e33b031ebb8b853230720da5ab7206ef1a966fd1fb26bcda339bca3b193fc419c925a9ab417dea124578343db4dcfd62125552b66ee9212150c956913752ce0b8c442b7edc0ec616fe4ebfb0bf7fec41cd5855d466edfc94d5315287e4f947a64ab3ac42e2cb305a872af516fb32c8150815b4ec0c6237d9d43e129412365732dc86f41020bff4b60a5eda911ba91346e47bf96aad9221f54fa7e92ff467f5756a98e6a87e4dc2504414dc51e55899f0e6b4510e747193fd2a259137a9a5a28d8927c1c92c27d8b723d5cd7bb2bac9afc487b1fc5c1f2a82c79c062a5c0b64ef0fcc36a05be9df004b148f12ab9a1ef65a7e133954a1beaad6c5371ebbf14e5bb6e7f3ffcc783ea1b01d22d3acfcaedd70edef3314794943446a9f0729112306021d01e88aa8c67ce008145f9c9ce22248d79df4f7fd5bbed62c398aca0a93941c241e91434f1f1f26422025dc965b550ce2078011b1e36a113b4e1e2b44cdec0d5af558637cb76086dda1e89e8c8882a7d061b4e562e605dfaf8286643c460dad8fa855d73f91e01d33ae4405c8ae20b993b7d66238abfc5664454988e0fd5f21e3f414494447533dd8a012ece713b3a7cd7c0e3decf35919ee2941912b3df93c92e971e14366649427b7683e21122d31c2797cecb18e39b74a1f29a3572e93c73c625db802b03451f5d52b4debc90b5295eb2a31650a67fbdab8d27d97e13183e201d3a8e141f69309344c66a0dc003c7fbbaa4ec6fd721098edb3e63206f074edc801eb23e1b799e5e6c242c782060660063622ad49b509d089cfbfa923da2df5dd632e465f91539df4e2c5d1f0d5a210d13091312f642790b355b3b7fb1aaa866cda9b9e019f60a30bc9f3b8677a18c103e676bcdda0b5ee2453955aa5120bed97e8fafba11351cccf1999c1810a9cdb91be032e486ec4dba1ec0a60a77ad63d3cd0099302a966a345c6fdd2af3231bbc06f8e44deb7d14c5e9daa2e701a8e2201384570b740377d1e60fa7919f5871de87c850de6a6c5f2ba74f9b3b3662c876fd7600a24878458acaa3cbd53ee9f6a90beef21c0f20095f349ff192c1c9fb54ad566e08648e029354536e68931ac1bd4ecdf6f907a870ffe3d4fd0ce9a8608d06c0481fd63af169df45e6253469ff237c551c7b057e9c16cb05b52de7f2c9267d1126b7b834b086e390e08e9ee2f72311fa86378ad34ef223006093db18efcbd94b445ec0ec0f2ebdffce8d82bf17b3010a89bc1c3607cac5dc831bf99bd409e03cf4615664dd6c7d6f663ce6b21671065d5507ffc6396ca6e78e5cca1a75cafe1d483dd5d78c36ab28413a7fbc97cbec1921f5086a2fe1672d3a8794df6b9ed50031b561bb219e652801f645b2b39a35aac3f0b99a99bb5b8c3830f7799a2ba93c83bcdd2e0584fb8e2c650b78b40e3aa1a478503c044a8fb13ff231a39166591ba09431170f46d058b7b245549427a8049a90a3945817d32f73b26c34a70d25e40b9dd65116317f89549c8e5d0bbc0a078f05d853925aec2523785b328ea93701c3c6e2f7603cc62d0880180c7518a86c0d4459f0ee9d14df19ef1189ac07a430205d9eab7710b799bf6b2ebbfc593e007f9348d1bb2bf496a11bd3bde1053d1f83479c77f599cacd37ee8d06a8b4d9d98a9ff8083c02f6446d50d67854b1bced55ae8517458f17c160e71465ddeaa0bdb3c1c0dbcc4d1187b2786f5ada2f3e1290f120b236fa99d3413e523e07667d9724e503e59decd38e0f0f2208edd2e0a6cb46037ae0f608848fc9e4e026095e80056682a1b4941b70b8fa2e2de689b31aa331af99a02ea6f4a961ddd9d8f4ba04a1c109e353b6e366f7c8206bcf2d2edde14a3b7a553efc74cbdeb4493828c08d860b8c0203d7a5f0ac375d7723b0fbb1b97d4405ce2d1e1a9deb70fa4bc0e4a52b9243f9af69d9b03436f0536997d436a4cc9f8fe04ccbaf3494a8e5836ffabcd35df5983aa9cd0e82a1674751c095fa0b1eadf3fff8972e2f26dfce3779f5f28a8f1d736e26242c0839c87303f89e10c4dca456f4db1c4411aec5d6f77a07013b8c94c50b716b6ee346364d19d42fb2c2fcf0d74db3af0bab81b69c40453bb2bb4346463792e7f8beb0caf2b53e692fb3cc2443e10c45d031fe7bcba3e7b9ab9547a730d4f15fd957f8ffeb2ad49ff2ab787d328e9da40a370ded07245cb9d60d9388b8349527d40a23edb7991d0524cc9bcd95ace7fd91d23055974af3ab98eb98743f3ec23a5b748d6d7434346ea80c2478763d060c3df89043c901162b384fdcc699caa4ad95e82f2e7533cdfa78b1d13be18d42cf4399922ac91d53e4ead67e36218038c6031592a817f568fd13ef0f3f17ebdb933797c79478ae7f75ae371ceadcb9cfdbff8de4b06dcb866f8b8ed2bc5f4324909ef2cbe6883db806eac29ba2c97139ea72591d8bbf83a54e11dd46535785d32c3c2a7fd18086c6db8cb06793d3ae79b5bd79f617c5580847ed3b403ee97c2abd8e1c531ead760651095afb9f633a42521b7170fd4e3153a8a4a289aee06b9b8fad26058845315f5a550aa29843c9af698e084f7f3a2f341b9d05738a15893a87a80bd5702be85749fbc493912e242532740865e6b5e9636c979f66549f4e76e1c48ffec6e2d46ab2a4e7e1f9e11fbf54375f34f4a23f731ca0e950890e5a44e0997e029899f9f7a782ff7d41f075c5f965d1f419830fa3876ba15e9131bde0954ba108490190b5876d78eac45d8a605481f9267592554afc5a42df4c0b823c1d4300c2b38b99045d80d89b455d887fa1c737e10e30e40365521c5a51ea9dc61a222eb7c22e2ce0191458642f0989b5e72761928afc169fae55dafd4a41cbe4463e438b6058ff668c9b76d3fc5bad449faa756af57b817892ef631a29818771b7cc79687a91a19108e00ce916c195386c0862749190ab7808e23093215b640863f79328581180b6f5264a86d02283c922043f508f09c9290616110e81ce03cffb7b5156af6c56d5b03f4cd49eaa2a48956e8d46af19cc7486ccefad04eaf22cdf58550d02c4e7bd5cc856c413f667ebacf5b1e79535480ef0c6537c2474f7a9a8d8ca1f0ce40d9649c0c499a14b23382a5df2e9564073992f73a632fb97c933a761f56b057fa4503bb9146a7a34369bdb8495614f44c0fd8dc075341e10cbced916d530405db3ef8d43499364ea36b1767ab25bfbeb1e6d3eda65b2355e3a4d9e09d3eb9c2ddedd72aa154ef00cb3d5602b3060668a468f30059b01ab1e5a4ba96cf937ec927ec9ea678da72142331f820b3d2d2c01789eaccf9d6b49b0d8fdc29ddf4d6b795fe1c2e05fc710c6378312fb422692a0723b71001314ffccb20f89105755a6ca91e6365dde2ae21186bf0973a2c745e016684859f0cfffd9ea4f0713d456919a658fe0737fcc17ee47b386c5f29da1c0ba94496b5d86f63261bb80d755fdffd19fec7ce550356813aaeb56999f989b74d82a29c257e2a169fdd2014ab53ea4e04c4b3542acf2ce0ed8185d5275dcda4eaf82c91a613570ead10d18e9ec78b078e9501dd011b22b8b528200c57095b2915d80865adbf2ae6dc2482993def0f1d596960e7e06ef535eabf34f0ef2dd18c08eedade7aa5d284c94f9af90b60695e7917acf1a79286baddcd5d396ce35cd8fe52c6a12ba7609ad77f9a4ae5871a74fa4a878becd33586b492af82a09ebb561cf2458c257ac1001ae25379ea8ab0fe7c639ab216040393dcc64efdc04e7a7fbc31db518cce84db20369585d46b1655a95b8f1750a98217417ac2c63ca6bcb84a56f55b58d84536b59e387d2e18c96df18339aa59f65c94b43ef67d9d8b72613a181fd8e096f0506d434679cf2eaac9758297cefb58355db95c484c2c16b8c98192ca39db549bd6f60b7a19848cc1326c34c72de374b43bc034c3dbfdc9e6c5471ebf7668d657aff301395416e326e510c14e8abc6f4a8ac12380a17ca1e0b54aa972fafebdd79ae8337a813630006138e6f57f07693ed179bb1478f61555e80cfa8b82a8cf25c78b89ec5529fa7d1df547b32bc647e4b936c821ad50412adaa015949933cab5224a7c128125050bd2b78afce7756a2908f5a627b5302a25ebad2611c817828f21161c41ad80ca669b6410ef983926e5ef9b7729786169a07327929054122ce05acef4fdf6c1aad90a307c0abbbf45967856987e7ecabf7016129297b10006c245fc2cee18b1c0afbc1af1426218df677bc51aa98e5b514850f4ed0c1a0a2288726c08211dc6169a6ae27f7c65aa10470630a0d44f828282fd1b9da4f80b8df22a664b70036534e0e3c934aca71dde5ac130c7e685fc752775ac05b0eb2b82ce7e1260a4c84cd16f4e26c0ffa15bf093bfd15b9b54d330cba17a78f7d64d89b6fd86df26ea086477bc04326b5e0013b8cf7ea8ec731ebd32056fbbe76d52eb05d4afa9cd8417c9fcd3ab1d1b18d7977128c10155d0d44c4f9ab3f6f7ad732976b363d54355732417d7c1a09b4239a1313756c22b668267e46febb93719125c453c253f2503c525733e45f9f7dbf3c7ec7039541452ae04f1bbc0d5734d8746853dc8eabf7979f0fa0c8c7edc1462ba0ca35e5db90f15073c5e5cbb82a260158a54a7a9dce719bf51d7a900489ee970ab546fbdfecee29a04f645daba667cbacb9e72e1b0b724d2467c900d92453e43a804da9530c6a3398ba3a3f021caffbb21637e5070ed270fad5c3e548c30203f878545be7a638e37c7261c90e790f4c2a657abb11d533e082d63131970636c66ed04efe0f539a4fb53f7663f6ab61f1e9740b7372f56651f749148fcf9dea59c113cc0cfc02147b1463e4442244bebf82267b1a8fbfa79305cd21a9ab40633e261cb6711944880543f82e18878e11a6313232249fc1e84d56110c3e1f8cfeeed4731754e03f73c1d41c4f4034b2c865be2ef21967653aab2d4ef7920cf2301b01b4d43f4dcfa157f6e85915303bae4a8c3ef47bba44e6297440d5cf2d94dcf25ca6190aa1b5c9c9703692d4127397d110bea856bcf3430b6579d21626b0bdba52c1d224570365a22e5846aa8e45549629593c9d27ab91279af1a6c22ba1ab61653366b5c51cefc06784b24b9e922552599a74a5e8bb22a11f89b924f7ea90b27ac3599e42472b62731203b50c539dbd8ce0133d96569ee84d9ff887b56852e6e2ab6b8f682f9cd1ed9cb582b9446f62ee765cb3878f6e86dd5107f225ee0463bed900ae57293365f83f2f09eebbe54a44364c5f1203ef3eab19882fffd8e82ee8a5606a9258c0b82350e350d81d3ec9215a79b920aa7f6a31773aed191cb7d789802cc17db4373e029c2c42c1630f47eefceb6ad5c821420f2b65b47ce6bdef77688cf07b721c4d6a323c0c6f5e86bed55a6521efd25c241e6ea5dc6943703c69998efdbfa06fdb039a08c824d7f0361f874c671003c986c1d374f04bd830ad753d54b3e5a87f502cde3fc2c9e14b9465faa30a74b19413b44cfafa35efd7adc95a7f4ba1edad324984a00ceda21b7e3786219c8e1887e9ca51e1f22b25e687a61028e4930fa5e8d07af39807a68ff9fff246c5e470525ea967bd228a23448ffd94279a7904d07e18c899109a1c3939962fa3a2a2679e82054c5cbf28d19be861179cd3f5d1a8bf33299a6cf1c54a74b53b17ac314e57aa250789f24a7535744a2c156d8743f0a2bd6947d9a64f1fda6b92470a46806eded6a35a7509f4949df79b737c93ddaa199a5d7d1659767fcb8743342aacb8f57fef73de58418be820a4b16861014a438e770de85ae000c14498ab2cc7e0e0282f73f33a36d8d87f73e2b3a9960e628ff5a953e47cc9470f3033b858d2d4688fa83cb0f903a076a99ec46fecdf6b072a3f6e4b81c66adc255c7c5bdaa2ff992abaa9c6ee964d441b7c38639bfc971c2a11b4611ccaa186bfda4772caf9ca60e804b1ea491e7405f62ab1bc25ebd83b462eea1f76a695617f84da648c90f4c6f69dc4427efe1a12b10f1f6e70affdc70aacc1c809c4c8c050af9123b2f6ca3d3b187164384fbb72b2f4d911d3268c74d98c547124207893f54a39f4cb64181133f04bdc185da4d3247ec66bd6d6838b03704ea86d6beb3e7ddd1c43dd6de18bb7e69da7c3209c20e7cbdc9d05dbaa6ce7487b43f4089f32f0a0925b6120f10f4ba818a899c079135476bbf4955350a5dbb33d262491cde7e4e7f2e1b4a6d2af1ff39a1b7584a91fca0deab246afd411da56cf30dc1776622b5d207aee1e8e863d957f11981ef1694625c47294d7c7031d06eaedf4d56c697483c822c00824316a5d631a72491657c9d8e6161555ba53646e99413a2a34d3356fc391207be6b206e02a0fa0989789fe7c3c4caa65b98895e2f70e18a7ef3e92d42e91736465a10279c874ec4e6ce66941629921ffa64f2115312661e4e4c9c24811aa0933ca1570b2670823bbd68c3280b8a7aaf3fa8cc927495e9f9cc07e21058dcf590ff8362bb216a31c212f026083a9aeb61f9fa82b21c34b77a9fd409fe44d4bf134c6e721c5b038c9a5e4c2c1051d38bb5cfe8286a053739c78d98e2a71036441b79aeecc8185382eb79201b3186f05e81ffb5b07d3678cb8d039fe8ee07e01c1bf1e3b4dd6a6154543d6829ae7551142df8b6ad96660294a3259bd8787bc59042798d99ea84bba863590cf635df7416225ee8ebaaec3ab546c074261508817074b90cc64afbf7f58960f723729ab6ccf28df458d9a6ef7fbd41d32a361cd63a45c3e4d3e50267fdf830277308c36801e25ce42fb911f098af53e7b640ea42a0e67d9b36389531ea644ae57071260442027e1ba6fce909b1a7542348acc07240ae31e7f51951e12b410f1fc692fa463694ee1d9d28a0fbc7e849c0915c79cb61dd6a994b1f578e1cfbd72958480ff629ae15cb1846148c29d42000ccbc9331ce58571220f553b4ecf598e218f93bbe9499c50f539eb9a2fd69e14536ebe3ab672a2cd52abdd182a444e31bf00290eb380d632c995b77ce1b9d36953371f34ac2cf6b8e190fdf7c122bc8e4381114fe7508946d408d529a2296293726a90b572771b70ce9d3d5eb715132fe4829807db98e5a4d3bd7b62900f7b0efc56d2de7fcc36281927a182f2df492b66b6c93af8bbfb5275aea97ff9842309369d2f05bb10c8c31f16d02b7defa38d8e4f0b284c5da31f2d020bc8dfcf232528b383a41a6c3c94c88aee81419e1fc87be4dca58b411395a78d95ca9e25311a5cb0ba727bd5361149d058d70044050821b927f41484354aa7f54486596ab417889d948bb278cd5a2f39b2a9f1e876a012c4ce4718eae9ff6d57485a44fabe08c6c8f9293f5a863ffc990ac1ee55854c86cf3911fc4620456e31ec2892ae7fb154b3abcc57c5e7e4631f4f4b4aa801ed8b78cd73e43486556c18e6592b5adbe1cbb939aab698883421d638b2526752aa10ee724a80ae04fb21e1316a03aa3f5c09dc61aae6fc7919b156012081d0951ba23f549c0c092fea4cc52c73d05db53214e3f083c59dc76b2e8d7c9a2f6543dc0c704eb076b26308c66aff75ab799ee2e5910f4d941e5ab638a231927d90df446db478ba68a9f41a005a7bbcdd26699a903e16d8159c870b7f695b5f4f6ebb8ce3e8b3902080ea6c0d6f72b908ca8e60a3aaedbb897eda80b45f8fe7916d3ead00612e46fbf6232eb36814d0445b960f115b627b1640eb42ecaa201360ef60b2c5c0426cf30a5871d1a11ab0b3516fb48e7d97c71e4b549ab361fdc18477c1b2b080580814303f638a2eae917d54530aa5f2c295103ba488ad898e778002318f34426223c99dd65179d6e4b8d43221404c2f11a08ec3c0d4b36fc491e74aa40f93b362a82bb81cdb71bd622a95083c12537cbdbfa972c1e3947ffefa7d0680ce882d44a8811b47c5d8624f472829a25d18a8d3d1adc4aa7047bb2691c3eb6eb0e0b1dd5ead171dbf3f423562888dd0f66be52ec055514e4c745b06d0f37358052e1abef712f4a26bcc7a8b0b895220d14faa8ae7a35042daef35d910fbf74bedfd47c0e51cd76840ea24b9770646865e0e4c3c415cd7541cd123ea765e6f5c98a40997f8a4ada5b36f9a8f705642f71c8518483f43c0fd2052ff5d7036d412b2cdefc02b3b6bd99778cdcd62228f2ec8a746b7ce18dd52d09000bc99cae49b85a9e3515a45a4a04d1d72deea9077b70709d9918885b291091fc4c51cc30e29f63f382d3a1ddc34d7e5c1f11128d1f2c5c8406809d63411e3126175e522f08c3e7465da6579290ed584e8af1f89f6fff8106e70e84847b09fa22a359965846afc0e94df61182e2bb8361b1d22740e8ae4560541a5a6744af97bf8bcd29d24961a8c59af70b5d2cf072c074cc0ff37d43a09163bf28a94cd4ad81d671821d4467cc34ed69742a1e255ee63281cd9b34f11d9a679a82595f8ccdd895d80fad4f538c18d36ee7eba11cbb80f5098a1dec42eb3dd885d6494486920351499e0afc5a1dc2cc6a5b160ed4be1827633d8336244680291c84b182c1824860b00acace00b3455f717bc95b01ce12cb5578fde310717c6b029325eb20cb94e13f5d64991c39fe6ad890248611e52feb74c979a201f202d927b0bbc0defc74e69ee18e950844e312a7a52839430bcdd0891607a1a7c0d3f6d6b9d01c6087f6f6406ddca4122b0c6618e10d552301ab75f3be8490d5ab4787234a66d2042116487cb3e025290405e4e4c994f28b6e082aecdb0eb015862e49d343d45bd7d48d6507a2dc06ad5984093d46410cbdae2923982c2a4bdd489516b23daa8e77c509ae8ffd785f10349bacfcf8ad60d6358f503ad980506fa045334e7e72a5b5124f9804b45952040b562d1471f07c007f7110a1cfa104c2b6fcbf53913376c538884731926f46f8d46319767d3f9110f1e4021790c317166a1f9d8b8ad0524711d210b3f8ff3de3750edca8cfc560c0da978d05e7132483d755ab8792bd9bdc019e194f3061d5e9f77143d2f4ac3aca93f0432958439da2d155937d261ba5e9e8ec85e4a9816ccc15a0181226b5607b00ab8f73f6df0442a66ff98650dd633c88cc2eb935acab12c4b63b7ddfe6cb2d29c73f027b3dda52c8b6155dcead499d4912a56773d1425a781b0725fd2d37148f6292f50fef0be74156a51f0dcc738babc3dc131a46ec33464404386b5c8302c5c62e15e0522dabf4d1c034f13e15803b0a3e6ed083876bf84c35f63898cc7deb988b878394eec03456230abbc66eccc680cc7c3adac8ffd84d61b5279fdea0b7bf9c0ec6d87d87ce496d4c12de7be0948d0ac59aed02af5d03f7a1df9dfaf99587160b7f07d9765f6cfa51905bb0d190cf5e0ccb007ca646fddf3a9945da5b211d9a15d7b7c1987d709a09f5ff02698bcbc81cddf1e8b25bccb0576f4f6c55c1068eac460ae6ff3294635a68d78e78b458fed54049fec6ae5a22e508ad9365f268fc3c518f6e0ee62ba7d5a89ade4eb2872899ac0e61854fc6874b650712a0eeef1c03c07efbc28d37e794c022900edc4e2242683c416dd61bc484eba0e7bd9e4f2990baf692b3042227cc146318d43918df95b75785cda6dc4a400e14969be52a42e72fc39de61f3f985d5ef89f26ca3a2da8e1c2164719ecc3786939901eb74c5dc49c41256a23931f0cda3c286f073e97f36f0acb318fb6c34e23e947115007229472f1828d47982b16c7a5e8cf4a6bdaac8562f17399405ec7bbdcf4a7ff0dcd4f07e6eb3e9b99cd49aadcec877b56c7063f23c36c5dd69f083a9b66bf866b8fa89166e1252bb92707581907e25be8402919becf3463e901c6b97773ab1a08f52c0dc1a0a7efce4c27291889490d8f7d477f808d3d5ef09d374d94569ef0aacf6cf091098e2ae693f99f2b131c5029006ae9ef630827a06071b4716fe2fda213079478b6f2a40f0def79320152d6d86e5aea6b7fed30cac9b3a45d61bc19001aefb92bf63a93a6d7d53426515884ab75ebaea4a37131d744cc17f309309f785a02d4f0f192c0d19f69b80447f840cb5e0e44e515d81c9528187dda1510746f1bcea23ee1f5739fcf4282026c811aabc47612b5b4d646bfc8d715c476eae62b671ff2c58f93fb4c289b04edcce9b1b32352b4cb2bb5ab54afff6be133e2190cc6afbd3ab8e6f77e8c335b57273da05bf64fbb6d0c8b4d2090893773c0ef3c5c034a4cfa765a3016daa80309f7fefdae7bb055ba7f3f47ba44b63af77f502ea15665c62c51694d0500c7624cdb0ac1b20cd867f318e6e75de71b635e8de37676ea238b1a1abdbf43ecfa8f32f77393b9266d1e1f3fc492bdf7de524a296592293d072e079307b9698ccef9cad40aa98550968f6bb48d83352c5883004de0f8ed2629b023dc146337741605881814ab294bec0b2cfb0b11d963ede545d9b75bc69a57f36a88f404df89021799d6a919ff2b1c651b3ac0a4183c35730cca02dbf880c387c1e264044ca5b3001a6981adc8ee57b2bfc362dc14ef699d2f1ae50179aab58eebfb1def5f3ef416fe1bb2372104ee40f9815ff43ba17f0149f1f38f1b2e227a542911b8237a0c9222488a01e65ffe070c0825be0ddee387011bb4e1fb17520cdee3ffe13d065b9e5ec05893bfb291c396dd8e2ba905908d1cc69e924f91b9498ac7dce40e7353ad6d5cfe21898a5f74f11b7013484f70f41e2c701857abd6895fb44d91222b4f8118d9e3ca4a6d476cd2a8d27f185f0dde8b3dd65a6dadd6bd56ffaaf732e5813b42525e9dd16b2f30ce7193914cb7c7c9a078f18bdf1ff546a421b0d7f0c13403ec600d0f81717c00b4604011311e1e6891422523cac474c97e2fc6a3d1bfc9d4b2d7cd8dc9e492e404cbf87996bc7dca24eaca3b6553769f82169c60388a60f85d89f4e3236549755e89f4e3eb2027528848ed7c2029bef7dd0bf9ae486d3f24d5518fe4043748e71cfdd7c30729823178eafbefbd47810baef1138299e54aaee4cd8dc9e4b31196abecb227bbf4f16e8747d39fde0087324bf62c2515e09922703865a329f315fc290b648ed399d404cf1c9a60b2c8248a87057bca3acf1e70caa6b8299cb2d9e338b239a5dbbe1ef33f080c995bb0c421060ea7ac25a32dd994394e96517daeaeb7284b9ebbe9cd792f06b6b85a4432ecb0e367477661070358681c1a77c4b62277e4a65186eced7bc170cbd137ee5e4eca7b6ff732d5b5785c54ca8e1d2a6bc5abc0b0443b69fd5074ef0d6eab02cb422db7dd18c1d073a5338c9e3d87377757999393234fafee99235173da0057598f29397ab7951d726166b084050e73b2b390e3afa319d03834e6d09e1c1de4d44a6d8fa68dc6b9ef1f5d124393e4f8a14fdf2ef7dd9cde9cde9cf37b99fab612e591d236323ac54d31476e45d423d6d608c83c1f86d8f493436a8c46a80fa6b9ab20c9c26ec19ebb0a922baa2099a2b391bb0a122332931c9a66496cf2de2b3d2677cea88289f1daa8e2d1800b4323c87b791e7ee7fd8e53fc24e3014fd0db89aa163089e29312225b10ab2205f34ffce4df4dd9cb3b11987ff918e57bd307c70df7251a2214f92f7307dca997c791b7ef5efceb21fa2159966e104c7f8a80660cca25dc4754da521b18dffbae24839b9c03232cbeb8e06d6ee10c38fc659825262f5f99bcbc7c0179f460c9fe29d4701855dd7be738d11557339b94601cb9be377db2bc91d2fb62fdbe1e3832fdee899b62a9e3138c599e0ce190588ee11024194b23592461d8defef6f631ac076fd6bed7849f37969c9265147d2c6df6ebb16d943e7def43a570061cf9035f00792801875d0381087c012fef5fc38ee38433340d7503cc7d17462fb27fe4894bdce49fc21538ec9f97529cf9498a8f0b8830252895c9cb6fa517304ef1530090e0f046de8c84c03c242af2a41084c3280b5b369b531c87f648e138db7f815ff7fd08fbac1a3392e00d0c67c871589620e9da18c9b264073aec80a787942ba1a2074c088308294029ff28a39982c3f8f28f3d282236d58018e316638c31366d8b607cd920c16194bde4d706d8b8008751166535005ce942d03dc7656e8c2e27ad934a8f76bacb51b4b9ab34c79f2418e20fc9325b2c59273331a74471cc39e73b0e4d0ce59cf34682352e2d9d73ce9177bbbbe577774ff9a416b6900694b2939e3f27a3c805778bd7eda4d5be8ded25f62987934aa74e7b544a1dee31e67829a50cbbbbdbba94f367947286b1c214399cb4941c7e32a9bd75b3b74eaac506ae30e55a5b5f745a8cc3f3bc6ffbbe2f7467ddec7dd559e766afc7754d29a5b36ef6be2e9d777610b173cc69839cf3be6ae7a0df35789b683d41cd723e4c8c9c3535e67dc5cc8f91326e639b6afdf893d6ef795f95caad52a9c536e7bcf3a3bddb9db4ca174752d29dcb49abbd1bf7f5a0cf598fbb37372af4e4708cc3a675bc80a375ece33a71482e832bc4f076f3c2cdcd6667c53836306c2010e3f03cefdbbeef0bdd502824aa691d69abece6c6f7edcdf04a5d69464bc12bf244221c3dea98d7f4fd232cf3795be7d87e4e6badb5965ee9f2fb85d0584159e584818991356c8c171323657cd6b0218e94d2992738e9b437524646867b594dc3f4cbd4efdf5a078e392e0d2c70fc8e1bc1288b6094e54712c8975e6409c6b6d9b69742b27dd96db3fd967b88fde1267f207e8a3a22d039fc8124d0361c049ae0f8dcacd588810ff672574102041798cb5d05c991cb83b7dc5590ac381d7097bb4a0f63541ff097bb4a0f352099942bbb5c151d544cd08c30a2c4c7499197121d5808c20583150b3288f1b062c1355bad726041c92a76f188e6aed2032b871fda625b6e555e77cb84136890c39b1a0427b02c0e628c9105be61fca02509b8cb4d450c2d6ea888f183160071371fb0979b8a1851d822e02d371531967001a6b9a9889184037a5859bb552764725379c20cf2cc5dc5054be47024f33f9856cbfef2c399a0ec4f03032cc3048c672b7f23368224cafb07402dbb0d57f6678013f845830b318da3c90177b13b1402bf091c5618154564b3eb9e87c724ea0349dc0f4985401207fa2082679806f3507e841effce0e941f2ffffd0e941fa2870177448fc11d0c86c09d96a79d0be2d9157d68fabe53f8c351e83b85411ff224f2c94c5e98e1b0ca2c886761956d205d65cbb1726822816d0e2391a7200986fb348850f1f0ee96a69b28489a611ad0cca7abc8e18c1225539fd6a913966305a3cb0341e0012238c4b31ce99478967d36fb3b64f3d010baae096f72dc56b5e3d18d2e787e389a738e7a7ce7dfd7435ab035ba71226326c30a8bc5922f5b9b25518da71437511adfe6b2fc842feb22699d769c52f6bfad248db241f2e42f7360595a9044592d6c2d071b6465a14ca5e0f0b264fea244e0f8a1bf2eebb25a27d2238d1ac593ff4d620aa29bb800dbecb008de234847f065657f5b6f08e004d36f5bef779decf2b8c925e5b22e4bc6aca32263c2137d737e736a11795b6a5990e045169157040e2bcba7b61c456b2e47d1203fbda02bebe5a8aaca12754c9022a11e691cdac5ac2ca0ca9a4059806240b11ec464b13963b1586ce290e7e343639e4763b14ff4bd44b1582cf689704c147a79997ac11f6df1b896bc44b2cf4540885ee7ee34667870a446709a6791fdbbd7dcee064e3ff9cf58cce1a47403d3a685414043a362eb477d43b728f530c6bc8a2a7208620e636c1459ad58eb24e98e41746c921d0c3939e7ac776b9b188bb1dbd3754d68ed767dd536be5ac156b0150cbb0c870e5bf96ad53a455a27be731c0c8cc3425f7994b681b58dbfafb655f6cd9be80be25602123c3b2ce18baf71be0c847fc8fe9e2477f1ebd14dc80e7b997429250c6667db6cb2260ff2f290a7573b0f594c124b3b4aef25c168b210b2c72064ff1e2d7f79ec058b32727f30f80d99febf9c732e99ac8ba5eb9ab073754af244852497fd43996fcc510ac9a863c61cfe977a1ef53c29dac63f447d2896ec33ea88ef60778e90f620fb532b9e494e3085f9e9097db9c96bd039fc99cc5ae992b68982435aa34b821c4567ae7214f5f15318adc37b7f5a6b9d95a328cd4f1e487bd039be681c4aa54dfe6e6b1e487ba664ff484b38e0b0b6eead50b27f786a55576dc9d34b2601cede87312dda2512dc5c1d3bbe33f43d0fa7318ec6fab349016e398efc2a1382f9b1f3484e7004e91237d51a2a79aab66aabb666f2a51fcee4f07a200db14b0e95a04ca053a2da0a9566aaf3d8d467e803f1cb14067784a44460cb53acb4457996d02044952be8c9d4ed5c9e8b6ea032cac47dbd676b7c29df4d2e33c3e1e714ee2a32909269ddeedd6e0b170c672e6d381c42236bc133b44f0e3fe0fdf6a252034a107053fcafe4e3c1d00379c8213b5104ff04db1c7ab9034d3b39b4cf8122f00cc3f3c39fa176d30686d3beac4f30c531060e3fffcbb6264b7aa260ed200bd8c3e383253e8009007bc14484c1226cce396788b16b2df94a543cd291e512c5ec12f5f1e45f5393e329bc81f9e867e3423051701861eeb2b463bf7778efcfc307ca03232b8cec5fbdb73d73560d6781cd8594031c36500c3625186130304c8047a153681cfa08987fbb39bb299b73d2168f0b06c39c7f58d4bce8afb941a174ede6a9deb95cb608ba9212d23db35510a9ef77280f3fa484d0f841eafbfeff4a39be827f4729e5b81554b82815dac65f052df00b3d366470ae953232054e66768d32b4bd731cc7711cc771dcb5b1e785daa57516b1e4b9736f7aad5a1500c50e043c812d9018631c8d9018624a952754b8c4d898d9b0722fc6a3115699b0ea656a1d77acba17e3d1e81fdb97610296a9ba5a2b0909d21038e468d931171c62152692046e49bbe50cd110bb641941d882d9bf09c494fd58ea7217bf0788b8b8c9ed631566e11656e195fdf838c85885575f0c326541ac8a3102abbad648a9b42f53d6b6b00aab6ccc3a2b4259e51e0fa52f37359601008cc3007df24f0096c9883492e048bb06d804b79be4cd121d5881658ada14489acacb8bbbd7788dd7b84ba26a6abce646eb44240820a26212e2c95f47c71c210c0c97c3a825aca9b552afc6889a2d77d4abb9c9ad43f38c11b87d64d4c2c2a1bbdc6ffd584b31b2e4290412208d7a8030d9004846b058ebc8f7f6f1256de3ae9c4929fdeabdee72d70b8dc3d275dda53b55bd3259cb64375f0c9eb29901bead3ec5a7206cb66c1e4082584a2dbd31524aefcbd4a591c7a501951f7bab9cd48a946c7d20255b9006cb258bc03309868fc9160c517053b0b3caa7d6dabf17ac74da79c150d2c85d60097e40beccfd00cfe12de19b6dfe807ce9a62f0c65c73563e0d0da4983185a4c16aee9f2e9ba9e6bce2953c8fef3d53a5ff69f4f1c27e2e8804397655acc966564c6a66bbebcd6413914ecfe04860390437f1201fb71e5268f443c7e192849f6164dce8d2282434e6afbd54d71bd835bc0ea5251c0ca94da3eec1eec9099aed284cd7881a5576bafb5d73e0fb7d66e2f53db666fdbb4785cae181ae7444d4867d9638c52461965680ab8c5ea0267c115060cc95d2b2527f36bef95ee5e655382787155b68bf7b81597b9cc652efb524edb789ffc7ba8134a7da4038b85c5a2755a7e927e94524a297599cb6e9499c9587975ef0d812338e7e380495bf64838e0ce336509124086438ebe7fa8266aa27168cc45b2d6994f9568937f289a92fda913a12fc27a84ba94689cee98e3d0207a5ef96f9b982638318910f51cc90ec604b58d3f03aec0e1f7489704e7ecff5f660646ccc28b2a57b090e477952b61e4ae72254b6e09b610b9fc92a3e8b1f264b665266507332b2f77e18a9eb451376a664d8dd609fde444fa618ecd398ef3f9f89828a35aa5a452d29c99edc96ed3f7ab5b5b6bbd60e9b6cdcca183754444498368175a9327a735896a5af320a29c5a8e44d12ffae44db8550553040ebb5548578e2341079f8211a38e0892e05225346a64b3aa2ac84ec80e8246511a061a95237f64e81db49f1c2d8274abeb5de4442183807e56124569ad5b4590d6b69c9c514eac31568992e147f64fffcce0e8825544b8647f5aa34134b410f3d35ae79ea23589eaeec5b4d6adba55b7ea56d9493ebe1f921292a235afe44cbe4a59485a3c355aeb60212b2e25597e3f6ec0911be4608727331bec9092dff2af942d7bd428c1d29b66d13da303a61fc1766fcd5c2aeb7944c016c4a3d1bfc914defcff9cde9cf6fbf3617978582535b21a265e534e191b05f30d03de1995c8ae6c110ffd382af2b82ab40abaa12cd9b5f06cc9212a8daa301f867cb287ae20c9317263e2f2aa19fce4aa504f9e69cda86656aba8a333c292a80f47a20f3ff4a1e99b51792a2bf0b4aa19b0199544d1947f4131877f9df9c77af06ce4137eb4ecefbd1439f4b4783337793ede8f476b9b2c406e3a228796c7aedac66d913ef957207ba402654fc18056a5b22ab72b1818181870a6070e27e0d68c2abb4a5523c3feb1a08b5ec4185f1e6ff7918d9403ecd9e3129e6ddbb60d6582eda6071c76d0f7f1efbbf44a343e60d2f643f206ce2cd9c01a6a84c09d5b9e24d841b42b3f24c9fe3b2bc0f27321b298601ba0c4cfb3f7a177f06385c06e1fd412b08fd740b38bde73c088c44ffe219efb2d79e2136f90d41c601981ce92637868495202c7fff85d25b0ccfeb124f30dd99ddbb66edbba7bb7ed7edb7697b058b56f8a16ed092c4f004d175642a3057905a179b41fe099438f526a6df7352a034dcb2f281e316ee172c12056e00cf4001e2cb8f003e9756e5f4a29ef9cb369415c56f8006146162870e15c8e638051ad7506408de49022e1428a2453249c8b7b15c1e1e55cb246090e39176d39e7e25c33bce8bae676bc6f77c0be03f6ef1ca50d1538942a1c6b6d0862fc1ead5a27fef6f2a58ffc719cd9d336520a2c59ca0c20933819a0749a517a912a227e922f5552155525180e5d4b8f6bf1da160f7254bbf7384e15a2881343c8f28796fd474ac0db73cf813648c9c30f9004f6211f887c1ef25f903be038630a9649d4a66ad9b24b49c0e1c87de52c378244d25e52d5257b772a55cec35b890621f93e0dd15a6badb5d64e4f4aaedb46dc6f922ee1728320486edb77dbb66d9b94dbec400ea4ad6dce6d4e54c93f927c8051f569d847e5340a95bd398ebee4699b735bd23e4044b2eee4cb0e58e143163700197179c1a50a2b74b08205136eb0722943edb500fb01a3053ab07e6022c8185a544065084e7228522b07ae5c8905a18758159869095ab2588293245d10c1da39db41105114d1042798e4200564f645cf154a68222528e58f47f70e40268513ba50828b275452fe9364892f546005079e6ddb46577e602d01450f530891f2278ac0250c16912955ec90f2c723514886da4d5c177aeffb3ccfe35e4fdc24fbbe28220b0f58557e666eb06ef090fdbb17cd7788c3518679c13460cb1910cbd30cd85de1029645123533ebee9733b30e0c7f66662998230c4c676a3333c72165ff199fa82366ff991fc7a14914fe0a86a61ccecc2418dec870661667b0e0ae71a56d10e08345ff7d38338b3839b0a8c3db81844c92d611a5883978645c32add6b93e0022ce423289aa41f2e4219a44e10fc542b150ac445304d7a090128d82016d0d92600d8a6090fc7a70241e606f147eff0fc5240a83333ae8933f3dd1506c060a39339b91d236fe3546e017588e8fc1298e78e508ce605b52b2bf6dd99628c797128698239ca18368ae250f9c81954439865b8ee3cc14114d334864ff7087911cca2cc9322e191e99961451874c121920a214af1c866a1d92c9937f68e6a11e4f613014932757e53014e3810c7ffc2a305318637066264f0e846766d9dfa59418cfcc666637582187d981c543f692a750e012ba3ea7ecdbcb8c2e658f5f0bd5da3a67a5a54ec592e7990f9610a4e4f0e60891f56f02aa02d452dbfb8c18e0b83d123f37d072811d19b114f71b5805074b72aae040851d5ba9fde569248122f860d1c40b09134a8628f10418b87a8083289a20d25fd3170ab060852a41113688c2e3a2010d944839ac5523e20a26c4c821e57f2351fdc5117c48820b1030c8a1966a40911911ae2574b1c511523eef9cd7c692676b37f0e6c60b9195394319fb8f6496c0b76ef1ebe6e45ea6b8edda8ab1e807114d94050a5c70f804485929bbe56bde3b4ba71bf94ac0f6def82fa25571bd92d9ec1567121589109911f948a46dc2f8722721812b2b5f22ad13494360e786b01f4622443016c73e3815f44efa0c712f53a357cf775b7757f214ed91a8d188f68c68cf88f68c46a3d1d679930399785b4ba646dcacfbe95e2aafcb9128af8b2f7cca893c9073ce7ba7947b848645c343adf5e57526513535b39ad956aba9d26a609589ca8f909bca185872ac3d8e1356268e13565996394e53e132c5715e4f1cc75fd9b1c8a197c32a933132691db73838e0b0ce6473c659673f5635ab3e5187df6057b5d6708ae05aab5b1c67562b97eca0040225904f9de580635eb97003bc8104df60619f93df2a5d90fd7d148151b28b4e02c1e45002d5c82e1a55af0001ddc001bb116de36f0120bd83935a6cc145fbd4993cb98f042b2d827506f3f3e5b086db2ebe75566915a8ceaa0fa5b4ceea8c86f53225465595357aa25010891285704480113ba6571a1e8d5e60800276b4ceb6835433a37574e798604893d4e0deab91c3d1684e17bc73617edf37e7d7719b6c0a8d16648bb2c844500d3e673745333fe09694f825af2b47e6c4992d709863adad350bc63c1188053ac86734f2d8e209a2b3d2597fde6dbb96c75543a7c2e19d9be469ce1a1b26b8ddf42d6bbd773665c24fb775ed096822a703735480e2c3518c3196bcebba8e7b8d5af0327bb1e22c16cbc462fd777172397cfaf5f8deb276e40b6460858b9410a9830d5250480f0073e80e059454e8af96b3bc0533ddbb6854f42fbc0972e5efa1238d22cdb8476992637e2381b1fb91a8b1b75aa7b64ef7fe1ea5513126631a83314fa28968852533b3c1d8f08b5f9ef529e4d0592990ecff90f134ff63f4314fa344f3334a311f49a4df91f91a323ea6347a99d20e941f313f03eecc7cccefc83ccd5f89b201ca8f9827bd0d34a00d507eccf8d1db40e3657c8db72106b461c68f9ef4118c41c6d33c10194f03ee40f941fa98dfa1f1354020a38f0177663c89a644e3634a335e4a1a33420fe490fea824a3d4f244533fa6d4a908248160700a3fc2581481640e90bc21fbdb985cc5f22c8b9b68fee326c7e2ac92cbe4c95fa6e4475288f24da4b39c0504c46a9d2eba811a28da18ad8d79998a017784a4626c4c696b9b96b39cf532e386bc38cd1318c360b0d78c31681e9e39a7b5132651d3f18c47b44e18918a1b342a27f41db287be82ec611c638404bc45bee2fb30ea207b1f462878aac0e1080544721ea74429b0fc548f5a688a602012951331b8816164c5558eaaecdd411f185e0f8ca70e8ce104ef08e6feb5b0453b8329889b5c861ae0308707954a29a54b0a0c560bf96008b4aaa25d62087ac88f4fca8094946ea06db604c90193e88724da5c40d5a12805f4c97fdbc21da4ec240aeef05cba82c3a6d198529e8254156562cb1656dc1cf7e2dc1b1fa068e9c195430ec6480d59c28a105c7001820ac02092f26fffeb7ffb6edbbdf7b64dcb4d3c42749287ea75b2966e24ca036536461965ade3ee9d9451b6e326d8116e72e130d268d82cd1e2389116b3d0dc80b7681d241865f961e422cb5a470a1d518bf844eb4422c4fe6078c9b20363163f398d86123874200f729c30c2f216c701d2e23835c7a130898a341a2cc67680c39b189afb9335f9eb01862ea594b2dba6d2dab57685a13e4d0a702cd1e45a9289304cc0e14d053cb0a53c5520d2684037ad533f47d976d3ddc892c8e291cefe059239449ac768add3c4a572f3956ac170f4b9b0d5ca551b5badad6f3f6b294bb6786834160c96ee87e613937244b758b52d33f40b5dd336b755918bac42860c79b1cbe5baaed7cd8dc9f43f1a614caae159fa1a6621c93c5a078646dd20fae4df24bbc3a84ba26e6e4cd7e532c514059fa0810c9c480941a10860a43e2a9c90922693cb715eadd3a45141e4c9ff46c149646f22fbcb20c1e175455587df86eebd173dfe0a881ef3f0eb6a1bf7ba0f9b865ff440bcef3eaabc5254853e3e06af2bac2a520d4730bc526ecc85e42ba3140c5f4a2925f8811a8a954a4e7292fb80fdd835140eb960289f860aabdfc592addeeeee5d23cc4d6e2b18cb32155712d57de81ddca16050418518aac86acd2ca1520c0eb07f2094f8a94e9ab2602f77048904a149da28779dcb55876c0e0386d7bc10b58dd1466e8b92b3f6f6b5d6dacbc35dca6bbb678c44a3156480c39ee518cdb0f8749ee32687aca78bd147edc5881635a9020d879e0c4f1453136d4829e59c1d3bca392795e2f29c00c382e141fa4ca29aa652a98448fdf8152b683e63838771dffdf65cc94717e5cf7ce62687b9cc7b645813dd64adb5763683d9ac04e1ff5fa23cefbdf76450fa42ce9b22283f49e93f115d177ba56d197ee1ca645890c7152cabd6d6dbc2aa25c42e1dd4411d54430dd2a54b972e5dfe67598344794abaf491474aa9055a0ba01b206de36f0304ee1fe57e82bbfb5b8cf5c438cbd131c127e0484e0a7262ac602016a0bf6d2f2fa4488a209408aa6e5c8135f0701821bc5a4ba5a4f276500daa0ecb9c4d2b2dd305d3afe993ffbd18cb1a5a12705893e5bcdbb710fba3a147f72dc41c7d071c76adbefcb026d36a1ee43407a2d1dce4b5a88a2b10d4403323050e6b5ee07cfc00e2a6201bf00018d64ca95e9c31ce393db045a3cdd90d820d7e7a884421a5462422f48540cf85d67129a594b6a99594058760a8c0918806be3d03ada33b47a4df3ab5678b2b9f28a8a44dd725b5758a4600000248b3140000200c0a8603229158482e9645393d14800c718046745c3816c883490ec4280832c4180089010400408000293335b40100d688641f47bcd21826546ccab075a1a8e40fc70265770515f872359336cdd01c4069ec4512aa6d6c9d8a2fe940a17ecef8b88f24e73888aa5a24f84882a84692489607e5a92705158de306af000358f94eef8037ac760cdab4ea0b9303d82c68f994a4ce9026c3534814d8bcd0632394651a9c15e4773e0e76d863a1f6fa40427bd0feb955288e2f4129ced8f99e55c654e8cea9c3398e9b7175b3f08741a35947f3319cb03ced39d26209ed2a5373b90fb47f46eb79c4f82b89c48813c0534200edbb5ba0e354d505de83213eb90dd4381dfeca8a305a3185f5ae37a595780d4f5976c372c2a5e85c33f717a3e530ed7ef514a9b609c56f270f4cb9c909b6630b6a148c5b17a8e9989a410fd3072bf135e8919a5490a075acb95b41803674ce6c29f1097ea3299df8217baf260d87221483947083f36f59bb5f56072ad780b5ce0286bc067481e51777e485f89d3b9885231b70b51eb28b43e9f1d73460707f2375cb40432d07381b5e500b9004ab20e41c4bbd71c3b5f0948eb01c487d173e0d3590c167b31d09612f3a41ce37bada04dd82a7cf26359fa1b354931b378e347efd7770f0464f03d0f36a14a64951b529c4af6b7dad80d7db2fe51db636beafdb62f23bc9dc58e542ed9601f626f9cab44b23e7004178f7ed87652e4840a779c03b41e4e5862d89de647cace1fc23993553b173047092be24b4b4c3b88072c01923c3c540fa1108566432f9d0b540843de44a893d818027dbe44deb39f69164761e45529b4c327cdb905b3e711aafcd5b6d2298110b73002d6cb15fc5b40a766eb8c3c2a1ec811faf3127fe9d8a4e7e7cda067b28d7f8d28cc78167a5f84ef4b913112abddee2184b99a5cf4cbcff73290ce1206fc2b399af441e77e709dcc439584153b32ea0f24b655b7c579958836853d8ea4bb5728a60842b7a9044de041169f0e11d0c81669841f9302cc31473090855cc9a0a94d149aff24f0dc1be3ce582e1e8bb0c2fb02499bf19a7a48ca794f4195fa1923f36e90f817fe442c45e5cf8b14a4b69618358cb6688c960d0a18f7a9d088c968e059208b570dbb8cc56f4d6d1894a712ad1d54a6f1ba29f99e7aa63d28a2cdb754d180815996955d1add38e291f516c7980c60bba490c015d1e52af1082a356b92f228264128bf33698b00251ded7fa4b78cbfb1bf0007ca1339b82e0812655b7ba5a01c7f14d8033570ac20f25ec59e6bd8f41390d5c918321796590de27a2f72ce0ba2183c06ac310c80a4d2c1363b3c69783c741299b4b2a32e6b3d27f77a7e9879a9aefebe08e445f4b6fd3c326e342f8b4e7436afc39e60a2502416567f3f0d922cd12afbca64fce52e2eedbef851746e24cd2d92367bd2018a3f8eb4177caf23a3685ecca07179b0e2779baa8d05f6a0026a9e19395732ae811b3eae3df3086d3662e90ff19a58c44114850a6f2b7917e91ff5c763244d983af00750dd93c582de5161a933ad14f44cb54c73f8934d3bfab484625573e191d85c2a78a7d3a7cc991ead503853329dc6c60777db4ff2176019f95f01bb1865994cf0ebbc889c5ef2a079db676b1d857e736bb810ef929ad607ba33c48539d9f9b4915b9d5d4e7b424c2549afae460f37938e8245189d8881f4a1b7e3f7ab34efe09f94e403fa02139b6dafdb5a03d3e19535a8d6afa4f588e45b780acf615fde77a73c4e93a9fd2fe6fb802bbec759cca4859609bd035c15556deeb8c60d9e7600b3b9903d0693be27d5356d1a11bc9d1468d559c843fbd53e00c04033c253ea97d2bb6473a50685a3e91a8145347e0e5a6368ac60b1a1abbb278108d6d2453b84496235b07ab788a64bcefd24071189b312019618b18711b93e0a9064c327c2af049ddb845282695ba6344e612744af88e12d2b520fb388e0c44f26066fa81b8a410077849baa3836b7165a248090819069fa37f0eb0df6bf39fc94d311a8fe35c40e9943d108c39b1f8d90e753219ef567662e5c138164d935c5740b81b15a15e8d0f934cfc7bb347b39cd40f4305603d10e143b023009ce32eeb8779a68b7c28795a4422af7c2ade61d02c48777f8908876b6af88d36beef580d523978b34ad114a205846595704d75112cf03cc91267038ba05b0323208fcc83481834bccc8320e69e62d27e6381c5c9b5230c6188f2ca811ca1e262edf937485928ecd556bec2f6e2aa213298efb33ae2b93f64166bf2ca8612882308bcec3a56702292353c471bb9aecd489361f250816446782e3e344a5fdc39b38b58cf54405eb0e40f814714de57c124bfaa5b22c81ebb0110b084469d0f374b8f7c84d3f3c4cc7375c143a98a0ad931278f6b984233b517539d15bf992e21068f8b94488aa924030161355afde2b2f778181ac49652a4a49ce191d188708efb3435d71215a95995e51eaff4ee420071a8ec4b3fff48e95617483f45a921a6718e9a83d5b0084e14a403141f92f86244ae54b064eb2fdfacacacf5a7227c9522446ccf6b13969c0344e4b208d322cdb0d8fbbcb64af2ba80f430f64708a344e342a9bb7b884a81618fcc60dde9b2514943659de373f426c75ec4d337a9b68a1ce57bd3d1381b835774a0ad92d061d12531a4a90f19229f1443bdd01a03e923e85decaccec5bead33dcfd6b8b3a0a09cebd5ade4fb1f5ea80d68916617df67f94ad1a9e6f8fde251208f4e788e3c82c184b88d08a75fd1ed2580708fc4581d7be87e2ff9fc66436be4573a51301a14234c05c6a827f05d010d43b506215e6b8e2f472c35a58792489892602a16782380b268b45fc9f71d4ed213bf295e3e1660f6bbda389614f4d72ddbf1aaf7a3b2ed65b53498e575d2cb17db6d3f04a445e597beb4c5663c3ed2b2ab18173ce831d7f9995f4616239f8b113a61d13cab45b6b6e6f042ba0efdb4dd315f49f072260e0aa6b46717b3984594e181409d48e06ead633a2cba1800ab734c1208a798fb2c853d5266e6d02c621e62f58bebe25470d68feee9b7bbd89a29fd465713042c223061fc736acc6d329886a6e06e4c9f556033f17220821bdf3597ac639c83a6162d5225ab6402e71ecac874221040269c0bce25666786d29801576a52cd3bab5c19006b716d166ce9cf92e30740d82d49b0d8aea731738553b97afaa97ef39fb14ba08824eec8b3d88e9f21b39ad3780c875c3ff654b64d60fbf87431a80db87fff918b60bfa709080cd22c771df7a985d96b2e5025c04699e8959d91d5fdee18241bcc7d548d8699452f74e8314b062ec8ca50fbdc77b6b9c47ec642461587aacc2c912dca872cd4ccd1c7a91cccc5be0c8c9826c9dac57545685b7922e8bc33d590ca145234b45a6522d3f60e369c0295508a84e5e09dfd169cc56dbaad13ce5d09c80628c78ddeeb964111178e9920b8975612ddb44dc8b9bef9e4f7abb357e66ddcf7336c963390c189ab5ccddc86a8612cc97bf1acb91745c3b41976e8268751ad627f33947ba0ac2d1c08c77d65f72a28ec45c82615bd5f9aa339937aad951ce0c0903d4e0e94a0022f3c24536c49041036603e2316f2e80635eaf6edc8d7a5853b8b8a121180cd97acb32ce297425f7a3823a39ffb52c8ec5120a6f8f1bad907491fcabdcac761b99340d167351636888c5967f2ac38235cddee24923e1b8f1b08e4b58102f4dc2dee028c10390c92f6c022fe130d7d78d500dfce71ca001506167a0f48f0b8a21502c3b0038933bac40ac41fe2b90e8ed15944e7ec1adf1e50250368882bec25c835b0ee69675f7837786774ae0790c0f36f08326112fdc5dc4e77e252310d776de7f6f1f7b039b644b9f2e30bb4159cf2bab1418e05800eea487f146611611527d285f0b1bc11585d4c90625bfbc9f85ecbf05fbf50cd069bfd319202bedd47e5fae40abf2b3d8d52eb9e5935e5419fff92b7afa98de79a3b8adf422c05764d4f3682799caa088b820de2457742812656994556fe6131c50b4c7743cc30d0c0ab657314f0aa65e1ce1cb27707cce9e6411860b3a6393bdfcac561736c1e201d300b3c734972e7fc037afde6ce4358b793e91d58782900a258172f22b058cc4b2d0aa4b68688d7449477435b6603e2b57003a210688024c2bd3d85847c5b70b6a7835a6117787c75cfe2d74a4fde455dcccab22c0985f3c63432ca8c5559c975c336512dd2d209683c2707cd0cf16b726306accc60ef6fde709b265806b53435f894e224a8855c970c32b011c97605fbc24a21cab18f0263c6b14de5a0ae5262211021072d4c956e1fcf3745ec577f6a94367c2e51c49a4ba49b5ffc266d1c28f2522602aaa6197ccd41f637c356038f5ab036ce4525107d5bdb5dd39b7851e7ec21a804fe359120f958aec48d8b7bce57f145c33975b7c45471c9a4c3844f6acd9a8aacac254d6367ad6827dd29bc8fce414fe40d193df5171046ce82f8ac06cf62e489c99685410efa245e8cbeeed9021441e6fc322fa77559f567d01b3957f436764c75ca6f5334b151860421c6c5c23ebc1c6ecbe0aedd1c9fcc692c0e3b26107e654c49b983c1dae1c358a83b48d346e22dd9626c44d659da1bb5d30ded39d2f4adff0c4417073c9c2701096302ca5d87a0928caa05fbed61e3770a3ea5f8771b19e09c90d27211ddd494cb89474de065412fb280487665e911c85a014766239b96d104429d9e269cb163ed1ea6a6bf6e049cecf20f0cf3318fa1fdc34136ad1d7edf41f1246ed512c29c476a6bc5b4e57e32a160d13e16191c7455dcae89be16327750939918bb4889c297a71d74202b59ddf4107cc08088898db44471757681c1b811aa9b10d1c31677c5f799681ba26449f77a8b62c9ac04f7234d3e9fd12f341ab7ee46ba2aae34ba28c0888a30016f00870d63c6dd866560d5f2b48f1b99477daa210573e64a16334c7edba8fd4ed8c8acfaf056250e0578f51d049f206909e128fb3bc6d6d64d33711dfea90a717fe79c8d80e1e5fe97802f50e9745c3bf6d04b5252bbcc2bf72672bbec909cf52f8a008003136f07cc273f51fb55a0a870f4be318aa72bb18ede2ef44f68cd4b876f8d63ea0d8baed012c0972396a6862c87b04a9982b7d5e3bf48e10f4dc0a8d6fc07ece1c690a0f66c23b6fcbba495845654bbcee635dc96b41c66028c327c290575de131145b240b4ab7557dd3a54590f3b9343cc3ce84009873188908fa4b43caf240d2f3d204c155fc10021ef72265e613607bf8507b529c13b14dd7fd52f2e5853cf27a314384030bec71985069820b5216355ec061a3aa884a85ca454dfbf55c925d61840bda63e294314910085d677c14f933d922397e999d98c6315224ad8ee47a16ad0cfebff0bd74706299187546e9c4aec563123c5d69e357e7a9671b38d2c654f428b6a6a148b0e93c285759b1dacb88e5972b4d0a9a635bde211de3ebe444eb0a2763e7c8d19ccdbce9a7577a4b53fc1be40433ec23d481da24c6d3369ab34be671ab7f86ffb246b6810abaf6d6872729199b1213f21fdba4854c4fffb38507124851437f82b2d58f7a4d1ad0412621e5377d420ad070d853d823508beef3b1ea9f4730ceff30da57dacdfc3c4c3030fc5a9b9983da21f48890a9cc9b21866a482763fbb0bcdeae7ae3bbba26a5259ca6c08870ebc4f3d71721dba03ee07995f3fc6c6e4e06636de8e79866fb8af308a7f1b445396894354f23c34690878f52ca5c41bcdcddddc81a7f57651f2c161c8964484b37a26de3354ccbc02d0e0960714fdd3064f9626cce14dd8a23cb03f76e9d729cdc2d164c93e4c8631de5b008ea536b96e465e0a721a22e8528daaa2f1f3da0b353425f59f8c779a09781da84539eb642bf446e9fc27043366a73fa9b1f0586ba991283c0d43f36d8510445cef97438fa26c2f606954f231e63d93ecb06648e3eade6108a4834b13c047a1d5b8367f268d7db14381f9ac55aadfa95ef728d4292b7ce17ed3140ffe1132177e59cde7d05f67bc3750ecfcd85bf22509de4fe6fd346ee2d3cfa99d196a2c768e6e9a62f13c225ebe0aefffe26bb3e6f06ab0eade9a1fbbd6688aebfde0cb7f3b48b094431c03a75fdce5f4cd5dc61c76b963dce72fc16ba82cf69659d30302a32705450d7e4c3710a0e756c816bdec780dbb56e747cece6f9ab7bfbf4cde77dba4eca72cd4ab94c2cf278ff05454a900317799f23d0e97dcb2a26f6bc21aee204b23977db649a4d6730a9e720bbe103491126ee72868893262ac452d236c1cd53af46d267faafd5811d2a004a4f51e3e2203d5998921c59badaa7e865dfb6f50e8ff5e613a6f1eb000b06a7b0e282afb0136315fb54284dbe72d4c0091f3e239c49451a9884afc2d7b792480e9478b075bf21f76654f752918c01d9a5b9255bffaee98264f22eb7d15b91e2262e5ceb941bbba6ecc76e8d580a04a1434a95827ad2e00dba321d7829b6b42be123213a4c651e3484b5a701b29d84110cd20512a9de8e0e6516b192f9302f460b6e609d1fed27b90d20ec8c3412a2d1534fcbc41a0c764626472ef9468b8ccee473c11571a3feeb54d43665dff34433e375d6337e605b9762d5200025d4e5afbe76108d361167d5f830320ca325d8c89de0e101e588a4f21dbdf35a61afacaf2c8fe0f020b131f4fb28a88697d52679800c64f34761e5cc3b03c30fe0da15b12379cff15dc7a602a912e1952a1dc154299c53258294a0cb824fcb494be923522db6849990998b220c8832997feb10aa0da6cca0bdf369db6d0c674e8c7bb90a89411cb2217ff8fc6e44c13e31941762d6cb1e58c80e549ccfe3662569e2b729b58235abc658747bd9cdb6cc5cf27e21871ad63f4917f75b7d050265c54904a17cf4b337c06ea40f1767d3168246f131b0dfdd88a79cddfe09cbb1ee7ed428fea281b8d9c7ea2016d988ca008b273e3882a8bafaaa3c8843304f315021a2d423c386bd4144efb823862325774fc60426c10633f78f7aff77b278258838431fe020583a823d13465aef2cfb3aec561e4e2c21364cad9639a316eccfbc75f3d46f8a7b0613d367eede409c9cf4ad06f1d20e5a757f827527db092d255b3811ea240feb85e63abb86264325d34a7786a613cc13335ba40e8b86934e95f78a4f949fbdcdfdae182f225ab3c5787e45f7d1b46c347dc866bf237061d5c105816612e5ea3ed07840ff11368c28ee80b340b17429aef3ca64e647245a97c889cf51f60519449fc617cea311fb92e5cd64a6bff03d70785bb6efca340c6f15d912b93f2a5c960a7a5362cef8464569277bb1134efd42fa67e0e0c4e3e36272f79879e520a7663b03e1758ce4d1938bd17003af7725fe2b21315d242a543fa2d29232b5383ca260e78e5714cc4c43837ddd35e71175c9cecfe38c037d74e4cff607ddc1e4362f879a5069c2ab147e8888c4c8c25318bd76b24d1b5839b484c1a4b446d08ff3290292029e56f114a4a6f00cc5bca94ce4b162e8051bc71d37c8e6f67516b5f19214deac66d24402e59e33619a8cbece4b81651ebf2feda1e2ff9df438d93c0a41945120c536de9bce40b186c9c59af855ea8c7f1860f86f627cf6e8ac5d702ef34653bd30edb2f4017dfb3f0aa5e3983729652b159ba9c406d9cf5a1194047bbf62a795b9ce36bf68d336f005b6c9343c7947c1f31b7bbf3db979b1dac18a2a833e84ed431265fbe4ce7e94894257b5363d90a08afe8237ead1656c7fe28e7756f2812db4f39598f66a5a6842247fe00e14262c72194106f9f4714bb4f0367b9e9a06e99521921866e126c10498ba6a0992be6c42539b5bab5d462273b96aebf0c424d8bd839f8346e677c9597d2f15b5f342e9a3e371d4e4a27b559ba69a1ad12e797c52a8d06a79a89ce3c45ec3218b75679bb16ce1a201478cac04038839d4d9a18fa3996deef98a0eca2f042c9fc6c3eb18ae752aca0c7a57d7a9a849f4528e73d95e0979ee8eede93d6885aa5fca332392df00983d1a3c34281a005bb229246dc776b6d0cf5be9bdca415a16a48b4ff5e32520f4bc08c045e0777ad675f4cabc32b279181dc196330d570f8d534bc2cb242cb9c4ced58947dc957125b93de256f88b15103262fbcf71968aed0b29d7a684090fdb68382c1d4ee37ce8f0fbcf1e46d56c9948f287452477e2744d002522008147643465d08f55f8f0b835ff173d51a73bdbd8fdd9398a4b38151b566ade04a4ad25ad3e407579fd6372cd1d52c6368389c9671aff9ad7b459b8c3c226ceb36a0d313f04eda0e303452fd70667f3c31991fd10f0f0cb139e59c7c33d222209ee5583e920844e0b093177f642cc38c1fc8622f7ccc86d37b41e1ad9c73778f393220cca5371eb690116c6a995bbe65711b0c278f0a20497cfebd34719a903d64947b7edfe717b04b92d88f98fbf97ae58c35760ff88faa918cb922e3d2f64d3db176f9cbfba579fde38df7cf5aa6675c8e039e0b51d01a5f0d508649e2d3c01f7e4fa61ea049f70a053a6891ce88281a5b9ca3c5d0e2aa8d6dae142cdd121dfbfed072dd0ad92354c162c7d95634ea3f24081c9723124dbc9b40278a475117503c88c9297045e76d5b374b4cf3e8d2449c6e5bfb136ccf60723ebaac162c1f60583239e48ce08af76b34fd0979906f3cb4c645a8b042392fb7986003d1dfdc331252ed3f7b8e27bd5944e4c1a0093d7a84b8c8e600c8fb7381d009d06374770a930d55c7fc38b4a7138f9153dc801458c26fe0c30e91ba400839484f73f53ce391401fb44a880e6a8a681d0d225040307da4fd27bcaa72d47d9edbed3bf5e66332e8e411b104df2ca0de895eef674a8c05125e8e51ac6c8c485881a6ec0180a78ed0aa8d16fe1014de8ca714c52d424cba4c0b3b279845e10c0867d14cd742ee89ac125b078ece78c6dc62f3f8fe16688d5ef77a20e33fdc21a5effb6fd181798ffb285d20b19af70091507e23dbbce4bb92339fad8ec89743539576603da2bfc84be05097b18b7517d45b5e9ac027a1e12ddb4bba17639762ecc91998fb088418184fd875752e1dbe33416c73d003fe75723a4b64e069d46d57e9f46b4a81ec7c4c8ed19f017bad320407306f74e78177fee8a78490ca4c66cbe598d28fd75821ac341ef9a0b20413a787d2dc9abcf103370894ad864ca610ec3f3fd312ed91435f6999416430db1f24201416e26c9c1568b62daade78588856e3ce2fe3de78e3a6931539e3a894b91849d3b819f821f230f0a6a72972775adaa36818fa040129549ed502448e3e5fd2b644d8041e7fefa2ef3e769102f5cbe20fe84605d452a970c19fb9f161c349b4caed4db91944308c1eb84e31ed9e59fd9df479b91fb1b100ff6ab71a978ca5597fbe29f41377ec4098b5e6d9fbe753badb0ddadc6251722c4a56c761175ce582a2beb707c14986cb61abd1752f5339dcd3b32be5eb4bd4498f124439a77e333093168c64a26981d7da72ed46ad6c60b3ccd190d9db5f264e315630e2c0f1b11e811f31d442564139b1479e8d7eec48c70902b5784f423d59e03fcd963606549bbb7d2bd065550dadfa28565326bb01bbded3c44db465232c525be3210eec93ae4864dd36ca24e837c89e12bf0441ff828574358dcef536c7bf8f0f77529abc90deced0a46310e1507b80fd22e0d46ae25f8773a66361d036f3a686ef4d1b79f246746306707776e6f30a19151670a206c7b1dd99bc074f55310e4b25ef963a711bfc546ca5c8d949ca93c045cac4eaeb8fb619a8360e91bd5550462dd5621e3af1871af40250f93055aa3b064fe56c9ae25324e4128dbd30d1bd24b4ad1efd2e90bc53ba90dbe0e894e77026af546ed5d7de087f0fcf39710ca9c51ff8990a9567532ecc905622abf75e2a44494b7d788d3614ba0360da9a7313fb06c9202d5f36a2697bb628c1dc280d0a5f90125a9dc6ef7b1dc954b05691e33e020fd643bf8332bbafd4f602eaf68815ec7acb37728627d339c478346cc7bd53d9ad1cc487e27ccf11c24a6ba398219ae5ed3bf7da3c8f68c0040184b46f8ba20b41277e0592c37037075202b37bdaa3ca0886f3688c0c89e7e14c77253bd5c7ba380283a43e51d6184615120196527c9cfaba4b97c5f789bb5f143d19a15ca97e253b98ab70a2f616d7cf84692a73396e8762a3360c291a087f254d6015b48b225973123caeaac90f22161a8f006d43207c03c2b1e8f3361c4d6457004296c1cf89a474d7a271dd856b28beb449b040331aaa8e337ddc922716e7c51ea177015d56bc6d2ae3cac952b025464acc398cb0701ceb4d5e4f1a5a0a0f4ef51686034ed49ced0270b66865c9742f8d30e3154a1817e2f30b6e9266e6f39db00e0955e8e282db33a3ce3681d908f52734a9df924234020fd87faa8ef81789649400e16e82a993e7d983fe4397e4f0e8207f64a3fd4bb12cb11f9451a884b7ac548450ab0c34c6c859a64392c0e4728b48f4c8626fd307e3005c4c28f4d80e518bb17f64f4e4fdce48804299b066a5eb12dd15c959ac93f8983c6bec5d07116d642469edf16d401ada8ad015604ef8520966551edc4108e1f91c3115632edf254213e732c31f992e01b4b2f55d4f3c3f8d92cb1298bef25861038b1a7fd117aad5686df02c88b6a46bf51e23f42bc66d58300bb13dcc934cc04182cb20f505b737c1c2d0fb73df0c795a1d9260bf88739937405d5ed460e051da10ad722a98fb9821f6022c5d8b94f49f46e5eec0c8e82e9ee244e35fdd957d9eba441efa6e978cfaeb7e51f350bf15c4cb1aa57eeddfebb46bb5e156496f681b273312f20e7754bb5bc9e5d1caa74da630515461673a834ae574ce96f7615462651be17e026788d1f9129dc4711dccd849fd107b24835c5d6ca1c67154ea6f7e5137e3a8ce2a7e1deec25e6f82dab9a84970536f2b54fec906eb8d408362f5e0674c3dc2c7b7abd993e032fbcf3d096f9cfbb0e3a2844065aa1ad76071f5d04dcbb7fc983917871ec58e5632e01fcb6b4a3049d2d51d89fec00eccf177841d90b72e9e0fd9efb8dc7147df70419d9fbb0002518714144add4950a9040532ab222fcf903c0e70f2f7e7f91a55d0d6c99f441077561c67dedc5d27c5404cdc439c4da3b4544a042a04c91c145914b2ddba8861c25ac3027639af181edb7a5e96cbdba507392f439134ba936ea1f77173244126aa0586aaa8d6e05ad1551a769c7f9ec09e6f8687eb4a469f6675ead15d8a1df2ce8b44d43b5d4dc6c3abcf061b1c68e1368794b39d396f2e26d731092062a1e01830771e46d4b718a72bcb18aa6bab13689438db2f49fc95f90c1e4c6c63b17c01237b660b9405f241ef2a03dba96318b4e9608b0fd78b2c9955f2cf61b69594739e39e8dc904daa6264453bdbc2864728c3b7cf705fae7aff4eae31be7e2f6a8c52bc25218b533a0ac58f3d175baef3c23fd88acc030a3e20f91f8b2a7f85a1bd620540e40261ab5e2f4337a2e9c30e1a861876cf0c14e1c110b0b5ecddbe4d38552e90800cd65a9eda4c0f3aed860866c6a59dce7753372108d88ee75e99f4c70bcc1a746bd82cddd6d74fe985d78aaea4a01c1824847ec7bd49ae93e89e35b618ffd89371bc3585cc97f3fc90a69a4237efc4c2d44bd5d08c12291a7867374b129e340fa79e6f7a94cabeca2aa98e16a7bdfdaa251b337b9acca3dbb375c2f6a41088288e8b26d56beca5c309bb4ce9e95c8952995f996688f6cbc037535d80249b3b3b73afe245d71351bcbec1360dd0b524ca9941023f82037ce005d1fd098bc4b03a26bdbf4d0bc0a00761d9e220b033bd40023e29656bc2f828e4546d94c105321967479c7b41203cbd07a5fbb67f06c4b160a5a1b4835e0f96c6ee72d8bddf8ba459e630d0eaa3994c035537a35f41ce20b1e099053c1c24ae6480115e5f9397996a3d5f6325c0300c1cfc7f633427a01c1d8f0db0f0d614096c168af85bac6ff6f5aa68cb83009225139c1a199d62fe6fb19a06d016c7b030f3e60e3f0ccf5d947a133d1d731a7c1f63735b22c67085744dfc9223d5e705eac1df42346e2ecc495ee249572c821e3de0bab4f551ca7e92297e7eac6ea4f9e6820a82122d053d7ff068ac6e88254ca4db6f68b088fbe30dc843a45bddd3e5732a4d4288efed41422a347fcf4e8576818ef1c91001cdcd0bc4c03ea47061f7411c39c169b8f239e129457b7a095ab0daef12f791264a8ef44cafe967633b9eb1f6618e0a22fc725fba4addbffe71032bd333ba130e89d19b95a2a2efaba7d5797897f5507f13f0c421843017095829e8407170621dcc85d4927fd5041f69c0e0832896c6f08b41a14b1fcc199b37729115026d41426b2aac2a9045d4eebd3aadb9cee94c86d44ad4466afc881060d9e0d91c02ca953f52564c94b81070c10bb94cc4825a672ed7bf4ce899e850f37d6ed82e03f2645d01c04897ded0e271b2f72426d474b90b5f150c57158d162699c33020146f0e4604514ddef7daa0d28918c80d301889c4d70a979a15c51612283b8de293ef5283bbdc1a311d92f5beadb7b302a4aab1bfdda8e8ac8370337dde7c7ba17cd224cedd7e63107b3777bf704b94974ee710c4701d107bfc503496e0ed125ef8f81440cdc0f16786ed165a7b9baf59f5e3a0295e4403e1cfa620ccb23329bb764cc7efe4578bb0157f568792b876f7080b1a0e792b2b34904807a453c7c61d2bd6f117da1a2c7545bf9ffeddeba528289e1ec21c2c991c9b81def7cdd69130d44c092874022a67614a0774cca98b671b1fe5b6430654c03cf54110ca75efbf08ca75ffabd6831120bbc2dfd46b74e8b0e4276a4d8a12753734bd29c027d038211aa1ca8f96907ab33131da0bee071f8b07b8e68d7f41dabc615ab8362da4b77078c65a3e41386bade378936706a0b0cad2d9560de02030ae6f01a5a715b60b00628de7314a0ec3841c83d5ca4b550aa85bd1eca8011498c0ff4b87871a60872b07e521484bc34335546c23a529e6c8f1c2f274c9a9847bff3a8059271248a39a0cd8a0e6f6fd409b8bee15489dcf02c126bd1622545b1389efca129fef2525f201d3b33e3d86b51513608d08e9d2897ed685296eb71697ce8bd18a4eb1da98f94e03417d4d815f3e0d83f6eb35aa7976aa9a08945cfaa21cca2f20184a434bdd1999405d83a6d9254f915cd6e47be0f587259342678d29987966501b3f3e89951fbcb2d83a90d90cbfd54daf3840b29c791e1833300c6402d5bc44db09620e750bb5eaf64320a768c3701904a1a09eb0a9ca34e8eca3d455314097895a86967e8b3b701a859843b4404f76900ba411bba07f33a2b8798bc15b0340c4dd3086bcef9189abe8891e338c462a1673e940880ba2dddf9daa3851bb85262b4c06efef9aee82198592f24dcd25111f07512b05d27ba4f8822e3ce4d1fffabe84f75fc5de1756e7a0ccd0146f42c824925a47343ff86e8ca65c793725f18adf1d7226852ed156702715bdb6470ace08869a07a9c085a605139af7f98012bb94f2d0d5f27ce0436ba8e3dd3205d711b3140e38434e8649e28f0e8c38831f20813ab5309ebb50a6e1b45b6807a0753697881180c189dd15751974f0b581f631e15e7bf38427305ba7002fb79fe912298e4129203c047d2b7033f8def1158a77b355d87245f8fcea097252cdfe7eeffbb84d58e4785cc4fcbada26ecb44bcc5d51b626a9b0094a37aa4ce32ff2c5f2ad5d56764670cf40894db010360d32b898912a4d541a71c42b6903a8d370b17e7ca12913a3f9a06ff930681d54b718213fde900c5f871071f92e4b96f6a9fe76f9f6630e56c01086e3e0e3d1365f65f9fc99287cb855092a8e061af0719106ca03dc0232e491803a89f2ac1c8d0f40308bfd125e70099754b5f0685b8461987913c4ec9d1844eff61c7c2a96e0aa4143998dcd132d0adb0a3a440e60a1ad40e44efffb01601fbcdeb532cb48d71d66e0706d064820c4b2218567863272a3268dbeb3888143d94ee6ffc5ad648456205bce5aee57ca39dae8fd48003423380a83b2508658281534596e045c08900a7676c42c6a556b08338b8a9511225de887410f61b0b49e3c6d6a987d92ecdeb04160f6a1aff25b62cdf2fd9fe91579d65e36d19b3fcd3b641a907b20e7026c686a0a71776f0e105dc13e927e2396d725422b3a29b6e21147246194befcdabf90dddc51acbfbcc889bf02f53fc434bc0820b8511211fe5c5d8d1b2afc82bae611dc830a09526baccdaa7b0d274cce381ae1f4f245d356b596e74719d7aded4473105a34b9353483be76db5bd2897b091d20d98cc9d9c227fda2b0862fd23d2db863ad6011980e89ce7aaa0395dd4643a7bc600e054db8f9ea98c6529210b9097731cd8843c1166dc4bf4470e625150718b38eba91dd4e335d095658dc13a7480186b8760c98ed78d6037f8383abb8b2c39fde64cb2d2318ffc8c8c8c9257043056cd9865381f8c59594843dcd916021c9740787636e0275c015278044e92e5f3fa4ae2e710d66a382461ccbbb817faadc2e706c3be9117fe2602a7f840723b9f419ca809e71157ef7e0a7136b5f8308a07c4663f59f64bf075fe0f5ba60b861733820aaed563ef935a4d1c3cb473ba3243553c7bc588a6b3cc6a3275186b39a24b1c9fd3723ff9b5a0b20b208e99b915888579cef5eead5e4da33f1b26e826fddd5adcad7128352b1697fe3df7097b3dd5992b4e6552d9a979091fe84b7f2bdb2331db362afdebce4985fa14470ea29f00df639d19f8372dc0d3b298c7512b1677815e2096a5d05d4df9672bb13b96dda81e2033f2a9d14eb8820e88610bda15b2a9e1d1be360939b0747ca10ceb725d46bc83c04c523103ddccbd04d81b9e68829d04d70851a2f83c2f2dac16e04f29b0254d93bfd93bd79be4922075a1c338bb16e2d326353f0fb90915da7adcd8238abff15b9657706b6a110ce66f0b41c41eb140716f279764813bea80c11b56908c8f2d05f274a26b1e5a9a2c16ee130a832a7cfe4d7fb56a522e905a25c18f2c4e46f3d259fd8758d71d8f81c771ca906381a00873fad7126e9fa51e6bebe22b6c6a59f0e040323e3f7858b40e2a587dffbe3b944d50beabde1f77977126c37e42952a3929442941d82dda8050a03541ab980e516c55382556ed0157c462112a74a0d52d184a2f3a3ad21c29ddc2ddf4e1fa088d60a10ea4a912d52065457dd4e9aa3bc0eaee8f9c59b52bb1e75b37f0d16d7b993c0d26a1c4b3198c3d94dbb91a3cfb672f5c830796161422422900103afe34379d159b35c1efc11e6ed54a122b0278bbe532a5a4ed269dfbbd2b8fd489047066417775e521210bb260dd2c2c6fba9f11762ecc0c3e7c0f80f8aa9418b0f1b9bcbbb4e0289b57a606cc10cc23b46c1bf1783513ad73c2d97e809bac4d00e341b296df1405b2a84b9bf09885e0b5277c40e8ecb3f20ab23dd240b60a641b893f298466c8513ca7a7f7e17e4a63096ca28604c55febffe6324473a9788a1475723b0172ff81ab0ad220d4714749ac844e4383e177525156338736881d83fa76a56263ce655dce8408beb78f63d2832f80a1c5c5c2f00db5dd03745ae10604037e5bc3b23586ee8553636599e743d28a33ac8ecc03a071af5cfb35de8aed732134395bfa6264d97a2fd418b15f139a955baa38b42fca0c27ce158b19feb308995d2610d752aa603e582b8d80cd65545eb90e69813694edc5c96c6e0698bd57169fc0508772628e31d1f73451d2112df135e093f8e46439d6bdf5421080c1424e7963353ea1d6dd882ca51179b6a05064b5b51681d5d349bb27eb4571d12aba2257594bd8f0a9d894f08d344b8d2a24a4a69c2a6b716074381d4c715a18969f819b0627f3626d0e330db9165b952e6fd644107a7fd8d422b75f7df9ed67f5035485ca617c482d514fedfe72da520ccce30651851412baf11f83c6670e9935d1f4d2f563a3896a6bac0c5171206dc4a15c66801c8e546fe11f56009cda2d2b4553947c082800af5ca23a7f6c162777511be088a4921c003da18bcc3c5977e44caab915a9a1009351d7b9ad43fa58b6dd17153952ad4d14911d248e9e6a383d2cf2c3cd584d188edd097b8ab5f985709e79864965f7f28e7033354e11e851829fb69c06d460ad2b0618b30cbb2893c1915ac3fc5210ce91ae004f0628f18307559037272453bf4a86b7e698e6a7cabbf2fd47d111253ef061182b5b7a598de0303c6a6a56a750ac56b03558688ef65495a6652fc3839a65139cc71c8c428f279cb5109181f3b888483d76c944ffd18640a5a748cd601e36831344ded045791c6457e5be7b0175f4b94d21e2b93100da21e43c5f9c807e98ca7555d6cecadcd5d7511f0fc30e60b4029fb416f8ce3aa69095ec1ca996ab0f432e8bd6c988c078953fa19c2e9b30d5d942e6518d7779d5e42929bbf4cbb985b923086928fb2577d1ac2aed47834770e4286632edf825c7f1a4995f84abdfec43c1a16c7b2370a62e33871e018a1dfe91c2410bdf28419ad05c8e0cef4a1ec91cc19c53fcc2bedc5e8fc5eb196c5d5a83c14b0f3163553499510150f7c73f0778646f046a6507774c32b70e5c921c448aa24f8188940bc7bbc3111590b9200aa969ffbffb67e81f61c2037e25b4b55ff2927af8e21ff380382ad309881afdf57a02575a240ad3116af3ce7af927f256d51552831356e650f93ffe177f4dfa6790f2ebde9fd60634942c606c78a233696d33fe481ec1e688051d45e6c708ea24ee2a30fdb3a70ff3eb6e7da9ba1110f07b9ad929f22904ba8b6157bf476be0e6012afc6edeccc80f0111b5099d123431a39637be9478300f62f1a3652262d588e610753a9b386d5073114f2c75a03609ef1d77c8c6b428081cd39ddd3385329b358179237d13ab2f2f76a4b4d05a7cd9c5d39774cac0e35e36badd0baac2a011a18e18e473da38c7c21980185e39432ac11d930beda0068fdacb35f06fee7d8c239bff62552c3923c02f2f029cbd682b7e8341f772efe0c12056eefa017fabfecefffd942ad144ccf51f753a99995dbeab1fef220bd34340fd83e6f1f7e696d2166ec24440d9561b18a91f76d4fdc0329aad13e685267c4066b153f3c02255f0d0a5803d3d6133440246a56f092b9a9831b25b213bd426cbe20353616b6a8cb3ec4351cf176613365c29d74fba8ff53129a81113ced1ac7a9e1f2ce08a163837194c70fb4487d8a6f27f22bd2ef43ce7dd56d2316ff719b2ad6c30f928cc1343e53eff34db527a41b1c8e64e99cfedd9e9b56476fd2bc8f924c2bd80fb860cb43d12fc7f961e5058a792aa3eb4be472c23c23cefd8bd8bce633321d9ec9c9dbb3bf151e3d4528da096ddce403ffe32d4c9d71d6b465feef1e2e1d75da70359513f16c95480cb64e3fa15f8f91278fda44e9e60f8c5242a84e4cb13144480955a28c8c69472aa71aef9a502f8f6d8d27ba928fc1ccf7f7bfc0405b233aa337620fc4f6760de018510e5bd58208b59f846c0a6418d05ca47f17e9cd609ff63efc2305065d7bbc4c31e5000aca1c915398b211f2e260adbb80a231184d94508a0e97404bc228f38ba2a5bf6788519912b06eddfe5ee325a800ab2cbffe338f4107d663f5b4c2f02036c6a782c7389f91b199cb5a28ea28dbe121cd022ac2cfb7b2a8dca633220cc7b5964c91f6e25b82373da1431a1091489f6e6f372f200211c86c4c5c5c29d7e2bc017379ea7d2c49170f30b466f7dbef2c7bac77af5f099a518c4eea7b204746c14dfdd7a288e92a7e13b63e2677038470c45de6b3015f725e3eaffbf8e1a0061c59f15b720b180fccc817784f58370dd0d8916359dd9868f8523c9372318a1bc7cc5d1a63a60d1c7108a0933d901ec4a8acffe79a5cd1cf8334846f9f9b2284a815e9be0200ebec58ec0104422810f33a23121e5344a603d87c5012d40c1e59243431d57c6583e39d6e0536ee92c4e3e7c289755ab13c84271efe74fb5358d1cf6202cb1ca6ff206fe0002c8445d9dc1e2b306543776a61a70fc3936d72ecc69ae4310122f94a939b3512f427f1774f5c90445c15aad436e7d5096e99e674418ed6e98b7e0885b4a7318c88523821e5fe8a22348420cc3167f0a7c3689fb7fb889a602eeba42fb19db0e28302cd086389b2052f769e8a76dd417029bd976711097c686cd587982259b9db509a1bcae272c806f7349abc70746934ee5328f25583860948afb098641ab6328d7a63d4bb40508c63af89228c06a69d8018bdc9f00d4b256cd26b06de55ac39ce6a82384fb410bc3a3bb151c82baa607703eba5394b97b82954284df5a3294e9e7e5287dc7096b510a009332f8899001508c3160166ac5e41cffc1a0d10144f24e92beae5178bbcb75e4184c0ae90e0176d544abb14afac751caa5d2588d97719f9b1e694e245957a404321cf18b4c8dc9b66083ab3408b8fa07480c25e75d5e0c5bd4429bf17740978b3bea30100dcff1fdb0e173a1ce5155045699ff08119ff6349c69177c5067b4422aee0932d85b0ae60422bb8ea9e423d5f8245bb304a79648571a5c5e2011a2092f7298e6845e311dc6590377bc562b7b82adb7ee2e4047304f27b7a723af3a0e90fe8d5f039fb2b731ac0f4c3c457f370954e620e994c7f8cbf5262e7eccca5de199ba3817912f4aa7ae1e067981e7c4ad81f2cb3a644e367a43278b09e923106efab35b9af5811697a5eb4799a58e2c064448c6a8435d23fc65e49831f3cb8159431cc55be89201cf51b7459f44cc25a1cca1319ad75786db0caaac92ca2cc9b647fb0f0b3058e946763ed7aa2822f6463f11433ce16b5158940ed1bc65f98dea47dd75b63119d4b2ea7d4df67cc160b304a44d079bd8e45612aef6c435292b32ca15dfaebe06173e67c7b41e4829fe63ac05c11732172fda5bde6960e0d9a3fc9845fcf8d405cb7b1d37a928f0329a7ea1d70333973eda8b5572da2bde8172324c62eb553580b0c75f2341aa83d672831fa3ccad0b2f9b513681cc72ff75f8f5f4d8777590f51f6fc0c76badd80c664e35d0f64fc3f6c461bf0dedc9d802f2eb73513c38dae582bd716872e9d5f2866cdecf628adb30c3e7432ec36be6c70fdb4df2b5c7a954d001e913dfa7382abc451837bbc2680185b6a76920de227f2be8d5ce5ccabdfc57aff50084010655c64a8c6d94ab199bb45b9e13fb33970fe6dbfd2cf34f3da8713cf2663f4b7796e0a02558106f4321a39d5ddfe42c9ac5fceb909ea33f870169700057704e005ea59ee7c3f3987ca74c85dba3ef9f78a6e752df0d9b0c5b26ece3fc2c1303ecf01021a1fedb5c25f9b6e0ee810675c625dfdb5304977deb26d401cdd0a15211235f0946aa8e3ea1fb8296b98a1a1aa62d2460598d96f87a4bbd29707497b0d4a3ce891e6f3ef5b5f5d627c1bf37106c3ba0e4fbef9ac93bd4a1d1263ad37e5e6b341ab92daba0c840741567a1e8aaecb3a30961e2aa6ff24a375f33215190840837d6f1a879a71ccaa187954e6f4965673890e1a49b7b3a5148a36447eebf8dbd302b1f5e54869676ac174af1f17967b4374010347e31fd9a0b33de35f2906ac3e9af21656b3363ed7e07d7589f166e3f7922c9acd47bcc603f9eb6b0f7217d9df23dd59451f49b6de9b18536baa3ecb2cea8eab09e589be833b308390f09d4959445aad5971793aa7b721060cd47f40b2384cef1226ac8b6354f5804a0c77c946c41684199cc05afec6c9b3d6258c0a893cb83863fb7f5e848bae93baa31b2af8af538b25e2a74270e1f1ed9def763ac8b6ceb6702c8f7653e3a8296885f6864ef8b575e7c028cd14eba96146e85fd4523c8dfe8ee3705c3bd858e454b2f9f2a7360b628a0375a094614c4a0871433db1ffca5de7965b16c9af62e0e089891309a664edd81316d815cdc145df9e80457b3ea205cd656c2b244b4e000e45207f8b2c702b33213624862e92ff447df3563b4b8b3e6085a0bc5c1eb461d005583f9adb486e648c155e74ad9bd7d525af5995ea01b9476a0188471c9b59d101aaabeec00ce5ac54c861d5dcaa1cac7f300bdb964f89dfe49846073089fbe91b50c6194a98640406817be8f55261f7654e230dc9d3f4ed626ac32542c7b8316f5d488d3f4f14558df9987e1cf8663b6f2e2ec2180fa1404bc2895f04a10bb43a10808532aee42c45bb885b03ea6b5011d9d766c0891f4059322d50d660b4daa89d49d3f4abc87e6d6712084f2e38bc412d70c1a4bed0bc618d1b667afda510a89e741af9026d2140f852ebc4830fc455331a92f1b728077138007c298c6c9c94af079835f96b1c8ac4118ed597e7e8610c63e59cf01b919d98ce0f60134171221706d1733ec449a24ce58e63cd4604857710415ff19ddf0292d8d635fe2c6f7e3ee51d48939c76cfb79658623a2d56c7682556661d1e411f4d9e16b0e396e79011c5881423fd5c673611344d3e13e2576e765ff0ef8eba1027fa33a63043b2ec6f7c655646d386b27a59201a908fa361644994b2778c56e196b2dc80816dcadaf1f26d1f6cf6a3c2ea9731332e3fda0fb55c47227bbe97d551697c2a8596b9bc15d8df54e3e3393986743d2b64f4426a6eec3bdaff83e5c8e57739d401bf2dedce296c4ba089d4f83034e3fc97a198ae62c1c83724f4cac58b5fe1e57607b218a6d51d8e980dc30d4b06bb94a8ce4a309fd995c423137453016777dda588844c7daec20d600b2f0bb4783f8a58ff37845c0e0da958f53812f5a19431dc80ada2fa244febbb93e0a0317f904b9917d594f16d4832cf1d36ae72edc70a7177e68c8d45be1448fa0aac7d53284da693d9546448fb9fc8d86539a9b43191679b6aa9e3425d5f366a17ae63cf5246e699a15013913174013e657d2bfa64e0194c94371e21e499d0e7a17491513bd255cdae6a4b3aaf9c503aecb099f76e47906128c1ae286539aa1e383f520759fe8b507c69a2817fb803fdc5308fd38d17fde3e4aadd2d02394e54deeb434e981e981708180894af274006efc79baa0f1eb988303623af8b6c7c3529eee506e2f60c34339e29edd83551f78d3f8a94a6f3f6cf7702c4bf7150ca45dc2769e8359f7e54a87e75ff5e7ecfc6937f02b1352912bb3a75b07646089a01cb1ada9bff4a8a7cbce50495365746914938de43ce40df78d6c9a8ea7b6c56cf68ea0f107666d7977d97f58e7b49ea0d8510fb01c53f2e0883a45c7f1bb48b5227233859fc342b562d0c9e017e4171f4f990cb84ae8183ba071b9be6155c540f409c4652d31764896be1fc60173a1506912a16ed55f2053677529761a6552fc3fd2750645ee9f9c1cbc07d1ff2ad47c9d76cb411e52950c8b3e48b5bbcfdd45a4b4c3603c86cbcf4777f092d8456cd24781ee7b76141a7a5f9816c8e471502502766331709ca89453d9d7c647c7c25870b79d86a850a356daa341c95569d7a7829e9d841954b5a2933302f5a1aec5b1e505c735e29deb228a41e7139f80bd9a5a06904a0a2150e411dd8f7b277cc25f26724ab15bb2e1695695f0466760569463856f9b5581dfdc3cac46a1a680c2224bcd9679ab3fc26e41ed5f9a9ec00f1c38a754b6975b58a4671cb17b31cde36469232be39e716d8e516292584415a8169dfe1a139d7c7b443b4c19e953275f71d0808b21da44b14c997c8c26dda8234693139a34237c036c41030abbd380664b9015c23c13052146963c1ec209aaf3df54ccacadbeb536e73bd272ead969ef53c1c0669f8f97b41ddc030d23af660ba9cdf40fba3ff4fee588541dcab63fe4528f3cd9c7cdbf0cb171103ea92516159a11a426f2c35f5ba9e0e6e7b95e7bbf0e80d69833468b062b8696a380cd96fe9e825d1abdbe04adf7aa49d03b6ed4b3134e494d0022a48b205ec3bfbe80e0167d4f28b3a9af1b7ca5fe5daef1343edee89e1419a5a15d6842b0d0e8f5bbe0280e6eb420028bf80bc396012361aa18283882dba30e3d31e0dbc2f3aee7cbc63619657beb2d6f606a1205ff069f5944ee075684ee78e6fa239158ec93e574c00303901efbb89014b66cf755b717dc97304eccbf61fdcf849146750169b326aa342c078c52c5fca30c8cab8642918a453c0b3131f072f57cf030044830e1cb05f3e1754127aec31155be62dba239dd8acfd218eed046b6c7e8b730eff745cd9a91f6f3efd798c9a017a3302819153d394711b740da18950ae6deafe6d5271269e6485e7dcd5ff8b0e9c0bbc6452f868e7bc36d157123c6bc30a92d79a4541190fbb1f2f3a93e936070131e06d78f6f9630a63968809dc0bc0f8a63394438148da2c3cff385f1c3146f13672414d0888d6cd6e78fd384cd366b88233c1ab551917df64a43f9d533d7a7431b7d0e2c6b2ce18a25e7cf9fed4a4db79f4977923832bb222f6261ee4b37b733bef30f18f58d64c2989aeaf32c618baafc0f3fa5e6cf9a69f4676dd5dffb1035a48a34cc824cea29265c89b320025bf5e90dd1f8623497815d48faf5410beef9419856f9b4de99db63748b438294f331d26d7ac4cdd2e70fd5ddbd6b6b77bddc89bd61a7347cb74f8d06205ac7f8a43ade1f7b506cdf095355bf0e0368b83f82182bc2220100737ad1d1748f83df27e35f6c0a5500641b28c0ce953ac0972ed45652a12a7fd4d6577247126a56f1ec00a11874ed580f3c0b43b8b87dfb6a53e94a018765e685e2fe4b95525dbe9c121a006cd1295c063550ae9015383ab0b9e59dfb170b6768f89c4bbcd372ae866cda6646bd66e86fdd0045fb98dba0762eb8c7a687a550ed8167430e8cce6f3c1338e00d841ae449b1e6728372b525c43729750c83c404b6adf61bde2cecf2ecee863d0936ad0a3df61a322c1fd840a5e2761234033f2d8ae01c40b64d6122cdec28a864958e007522326c14c4a96c203485a2bf9ca347ac7386f015a20ffbfb87a7c30114b813e339a6c275cc0a01d7d771c00bcc69ea178754ff90b19522de6574af3d9575134d6baf34e7ac4a4cbbe4b88a9a39a1c90df37c4ad75734fccccd5c865926a87ae2f98a0133a02a716d8be849e741e37d81373fd846d47962468a515af689928cac866d96278ce189aaf40dd95d768d82340477a339d2516445daa8c30de2796bde548cf212ea06cb53abe29ccbb817cd18147e10b99b2e39a489193f2c7096ce7cd6a4c2600b5f6597ed28e4d61328c8478a9ac8a40dc84fd491547dc72f83189b7a9a50d8c88ec172a5a19afc195de790c7846b517fdd99e9c2ef160411b3a27c9b32ea893ff2fc78a53e105e1a7599e8ccb0cd2a4ba7bd1e8429f812f1c7392b8d79431a1f3127f78aebc1daa8951b6f3c9247a9f6023055520b301ded9f8b3dec31bc389451f35032f0ed274ff5543709dda4eb2170c567f6514bd278ef8ce12d1a3fa7b410167184e76f033f3fe118e771de2327570fde39936a28db0b8740bdb1a00d20765ff65c5cba1ce13a410bde982a33aa186d99036fd8af60964c29d81beb4650fb946087b032b6e636c0917f76365dcb7ca92942934da4ad7574468801f0d21c93cdbf78648e6be8bc9347217b78dbb0277192bf9bf88de1a2e6aa6146aed44ec6c1e5b51f65b413e39bcc26be9deb02dfb68538d3b72c615bdd5a1124e5be0d5404bfb790b35532cde77997568e43b434907034dcf67df178ebe672f7fde44c0436fdfc1aa6849466273a1907d269204abb82a7b740d9904434fe39ab255f34322a07a7aceeb7767a5987b7ca812c62b0ca7be231db6d2fc7733b531435da5c40f268f81053226cd18c2c07b500e6573d48682b83b2d231e67db5d8bb8d62c788e63b4c4a86bbfc55b19293852603a4c301a9dc134acb8dc5e848251b894ca7bdcd62a13c548a591a8c7411d26f46ea1ebfbe9d2b13302e66187be23dbbc52b9d9639f216bb3223110d7c8b25a2a769e9a2ad7813e269e19cf82cd69980af979617b9cd9c23e21912903da137f1c7cfbb4524b071efba69ac2e5a5474da937b514bd6c73c130743f751f06081fd67e16d3fadfe25c6479d222edfedbc10c0d727e5ea4128c57c8dabc11fe0efddab0fc1ae3422c33fad0a01cb1a49bab454fd4ec40d00942a34e5f2175b4252ead4c724cb4b5b4f15dbfd17f263ee229fcc6e007dc196af3f101126d512c3387f87541c37ef67b6353c4b319d890ea7e08eec8ba299ad0ad118c21ff772890836514916e006a0cb68eb236bd7222eb89b0f96d5a87d17c19e5483d94dc43abce1f87315256cba2a398df75df6a79c1c54a02c0a7c15dec7122c70cb6733c532e02495f7842aa858898dee72a8aa8f2730b8ad915c6552bec0b189e3d10a2355739eca4d52f0916b96cafad63c2ba29d0d65bc14654a8264f22c9aabe88216fc1cb85826e540a5170dd723e1e58a52cbd8217a7a1fc76f0f09b6b51f384561d142c66cac9729f0678b6657dba4c69286a768ad74bbf46ee6164e6e28df6efd445cb4897a02bec7b71a415007903d799a87cce321cf1df2a4328de371af7b233351c6f64410d170ba38391cf3afd6339b79d55154f7c9a0461a0e5938b60cb75ce8c96c35e0ba677f8a50114c4e3d1b75250e199827d9a7e874420582ec0d07b708f5b9d67483f1d7c3be26371a02c4ce7d52957e1a888d33dabd77692da05f86dd1c6ef1072f96e43ee3683d4240412836a9b1b132c27ec07611ff8edf033f97b9e237b0f7a33d50847ffff02fd2319b58d1cefe511d3b890d56070195cc67b9928520f9619fe6f0ca2ea9ef2b4df61123265741e527ee29f7801196fd7030acaa53ca291e5c1e02ef5a0f3ae6b0f88dc4e89383f8251895f9711160dd19a21e89a7547ccfb0fec334917389ca69c9f716518771ae2e5491ca47c75015c3360012fa8390594e584214a9f81560b070e11ef643a075c14fd77444174dc27f442e2e208a755c9ed85bc43f3dc008d22ab88ed9063e4e6d90448ba513b95858561470455b4892ef31b9a5770e96559685f65c288aa8f2cc7674e064a81c8ca08f0f7198f3d6be0092d8e084471411c85dc3c9c1b21e64bbea575e846230ea321b09e5c7da89e22b48366359d1df800136c9ceabce4f0371ba1b25f1672d1835787762a81212c2f9abf5a7542bf3dd8519a9d905c05edbe77d7a7ac3d4c4fcd74a0c68cc86ff4fce05beb02b918784b23332ef3605adbb6171cd1f0ee005cc0e59ea8098a5badc84837428606df50d994b3e5621b24a19c1dfd21783d74654f4f1bc310d358f1d3e13beadec64e3c22f42ecc8f766defc315a463102186d835021ad20e7a4b376913999d594099575ee1ce9ff33583522ee319f535507d629edb32f4865d5ab397e820c61e4c6cb144a0fa7e88c49d09416193a18dcd07979574559608432237e5ccb0a3cc598509e8277bc414d76a324aea7535042ec64389b6ed1140c2ebe5fac75dd2188e23e8fe8ad90f5fabd0f62de721e602e716f1be5f4696b8d67b1852e505b1a72c4ec58348b6c7817a1c66c61f36b18104768a31344412a3c4fadfb0a6a3ee6e56eef23f2fc799676c57c5fc018bc4caa5fe2502a3b489800d049dbe21b41da7047eba7156c3626d873c08d4318d2d164ba0c80bc81d3243c5c87daa071e2a2abda153462bd4c7c605d8d4966204e26e470783af856c10bb4d02032fc9db8f1ea53f4f8a8c97093318cd7ac864693774926430855344841f604e8e1c54cfa76bb702c06c8c5b1312e1a926e4affb3237a6de238d1c391251be8c4f79cfc89e9872ab213c83314e7ef7131bb707747a663a08bad44690442f495cd14fc307e51ff4a75f60b850032905a55267705fa23c3081d67031f710dde288553655248308d519dc6b4504390dd89b17f073d8041cc529c7965dfdd347ef8f4155e749f9f8210eae7413516a1100a85fdba766011a045aef4a2f1f3875c9c89e09af7acca042a2239f50c0934a42951842f5979fb76e07eab1c201a10eb47275b0843042d9f331a276fd7e085a08450a25f284eb4a4160f6e4852ecddc57785004cd2122767eceaf9f83862f2716934b5861d9d1f8492ba7edb0a05a996e8cf95fb7c06537524f1795cd61c642bfd9d03fc2c4eadbbd7ab136a34b1a7e6dde2913adfbdcc63b5e154def1c694ac40f7ca196cacc22ca23b18eef385ccbd4ee0de28bd1ac78abb450c6a0390fbee387db63746bc178718a4cf60cce4640c706db6173616bcd83c848b226270173e224172284980ab5925f76027cd6682fd266e3d2bcd6a2bb57753572afd7c8c8da92d94662f852ccf7fd667dc23b84197a61b4b087d7f50413ab0323e173297f449e0eb76a0ad406f497b57bd0d6a9af93602ba51910dfbdcf8cf71406ffca6087aa97fcfd4e15875e59095439bd3a02b5fb1fa7a4510515d9f93d4ce3280c72d7420c51d8ff1ac021a29f0af1b75c4061a8ee95b2d090b4ef6bd579d12f9b495335d1c15f42c77f6930cd68f348356560c14ad603f696d1672772ed8fd984b439107bae355f7ac8d9e3b20b80e872393aa088a3ef2d045118a0a2210a07301c12229d76e686434f6c87673632ae931680c8240e5964543f5f7db7586d75a7f78e8a208456c405842ac667be2bd4ae27e7f70904f09edba3d936f311ac4ffbb6ba1b0943a6eb8a46320ae66cd5cc62c627393e59210ac8cb739a4298732f90ee20bd9a9bad00bf14d68c96f565ca62b18832d13d08801ba6d95da52e56d1336bdc39c87e128d84a1df0325b035f086997048b2fec79dd271edce0c07033c17e4059a86f1c054c5e0bc33265026f9b9a2e8d4a6321feea8332e0a8c0455c12b3e4cc21883086ab86db9a311dc2520deca68994482ff12e1faead46569926cd2a9bff1631833dd53c49bb7a0a8cd8ccb81a57fceb2ddb3763b7ba8992247d1795751aa219d200c641b3ab350e03b5f1e5a044816073cc4bbe8b79fddfe3207fdd0acccb24eff3b0c4771a34c480bcdc4d87e3b2baced9b89f59083293918fc14d75c049368486e9e035193c5a3b90b339e8b9a33f87a1c48f7f642bd5934d1c49889e9aa55bd46f09f1540cc68d9981e17f076610cec0dcd1770714271c09bd720454aceda4d51a3ea1042a0fe91cea0851d36ec888b7c33a40e1a7e18d356a4953aa3ab5ae6a23246701a42a41792a01e442bd9c44aa328089d48b02c9230f924ed47d30efb5aa5adebda889234c955895714820b1ebcd5dc29674f58ccc74614fafde12708c0e04bf9919c69413fb4e9062a8e69caac937c09e5c4345e6f53167c4ace8f56d0485c0451a7d9bbc5255f5fdd005ce5bf062fa967ff305dd287ef56cc966055dc840d19eb11046c65b8b5ef9ed8d625933f0d619b38cf5b2791800f26c81b0d9df414691b4bf286e3c7fc8078d598e790f8ede22a35a66966847ba728dc5e1633c340da7f4bb5f76910f1201c39084d01ddca78c1c86415874c675dde890bed30782d811882ff5574a9f2feab062179bbefe0cdc9d562e85086f54d5161ee320e54d82b418a605479e33129cd4efefe8ee3045968a812f6ec30b8e58df3b3aae725d5f6df1bdb47092c56a5bedc83cbd38c5895d824d2bf19696d07d274ad573653077c3d7ad3d62f8331e79f7dfbaafd55af420fb73180cbdcadf898b1e786fad8d87cb67e304ff5c2d6fe549f4656b6c2b5ffb5b6e784592dbbc166260b330c1ebf51b0d6e164838e8b5385c73b54b4d96577123a4b177ff9cc3205223f33f46cc601b18a0ab69d0130647d29745682a9637336ecd9a6eec8d98afea7163ddb0aa4a8e70cf3b949523e0c5bce5a79cdf65898996f3289ef9d299077e8512446c30bb4c698b52037ce2013f6146861473f600b759db576940c9fed516e8fce7abb76a14e2033e91f51792014b2845cc5ec80734a134319b421a9084d2c696a189753270f8503c522c8985267329d2d61505f9343e372ccc7c316f820f446913f423969fd79949da5020f51a7f69311ebcf43b8667dc53b738e6e5e0a2d53512646386a6549c152ef4fc1f0e858645f75823a0428374d7b15a460c8678b2c71ffe92b7878556cc68cff2722d94712515a77cb3e1cf6e41d2e3102d96d7da0937411bc01c4d7798270dfc9eb7aa4077fc6f3f578235a52d0c8ae86ad1ce0457ce600a2cc303ba92c6865a2094aa710e392e36aa27a35b8535dc60d64c5f6d820d5d21de671df986511ae59f7ac0d7a5067d29afe88c290614608c113031003a3037bd7c45014cce4d1d05a1b881705dcda730408e3a3bb32ada5e7a6969932449a44c52065d085008600854242dd16665b2ad7513a5661652b25f51b5bb6f9fb427efeddba7b127f0edd7e079ed4d113dd9d2e2fd96a43fd113752772f53352fdeee04169ad9c2851519b8ad2010b91590d3a43604268c907a5d68cbbff6ecfd3a2bf6d1f8ab3eb09b038a356282b272c4f0ed893254b16d64a898a4938f341e1b880899224fb068c01319132c44db5a9a88ae4f311826d81868d0e58eae995eaa90391d995ee8504175c69e20a911a74641c115256c3902a99874b2b0869541922e4b5692f21331407bbf09032049399928f188d8868005fc8e08b98120ff2e5b6d4ea62c1177002b264c9b2b5bc60b7dfb61a1a8fa00c344b962c59f6762fae1ed511a28871450c1e23d56849cc3bbd983de5e024bf02430e14c6be97c310b5da545c711def69b138e926cc3ebbe3a4bf8ecc8a9baeb869c74d58dca48df6ca2ce6a4d5b9285412ade5e0a614ea0f363e20a84c10badba0a921dcccc1c3f637018b36f38ed7728436b995c9ad3c19191acb69e3260de5a68b24a5ddbc4a1842c7193aef38e9c59c0c439b79c7f476dc54b9959b38272c282d37716310baa3b8dc84e3a61c5c4567fbef6881c65a0ce1b2a76eb43127f9df3163978db9de8b9920ec8e17e5a47b310ae3df838f5d28147659b1e2a48b119af2409b9e59bd98e98a366d2c897631429b7575a34d2f66639eac965ecc8b7da9eddf8d954fc93e42d31d9bb3d6a2bcf3dae8421afafece7699cc49ab59cd967cb8697b2d265ad90db4f63a0fc4b5d1ba8c74d2673b3199b5e2a66d1c957cd8989bb28e9b46756677ae8d9be88e619163712bcec90f5605bef779a5592d9b63613b2e4934b74d7ac5c5085d352d89aedba4572e0865fb6b9a7635ae251aa1efc8b19cdcc270ac295272abef4665a4f43cb1133fd4a87ef846cad4a8d63aee78324bf2ce9b6def5ea0f16296e4f8d50585718c5d6f7ab1f29bd026b7c25eec888ead38969bb61abaee6cd38bf9832f37d519a8b2a7eab224fb3939d69a5eccf462d6a5086ddff462db77c61d68ed5d92e86d9bf4ca101a0b56a0fb2cb44973681879a0fdb91678e0561ca03049c4bc993d89608e1cccc0850221dc10a3f4f776ecc9e4011249d4c0066fe49ca04405a9dba6610de3b18bc554e2cafe278a41055f00c97ef740e9577f38ca5a8cb18dc1f400d521a27d0788501d229a1d3d407576fd3a6e76a3e20d2138098349185112b022c9ffbca2afba57a512498a4479c5b2a306be007ef4d317dc1f48efa5f77aff85f5d3b7de5037485219af244a6ab904a1f1a66068d359a7aa43ed48290c3ed5952539f71ecb4b4b624a2c90a417653673e0bfe580fd7e654f9ec395379a222c75b584369dc9f6af58ee2a673f2d16b1ce2cc98a932b0ab3c41231b6bf5932eb2a25a5a2ea15a229b6bff51759faf4504838d71e1d86a11e434dc564e584e52494d66a157a888d1354272cf1c0ae07664ff77a601e0fcc53a56a9ae6b7b6b86d5e1a1e6a79df7a6214c66d79470f0c0a4d9df4c03cb0ed1e9807a64ab9acd8fe22d35631ef2cd6921369a9f78d96d2ee9fe90e26b47b93fcdb6b9beac0c916b4d65bebbd177bac8054a025b1582c168bc556d66563f6148b51200ea4e45c7534452e8e867014074fcafc464aef7e55ac1450a4ccca2c521c3c291d84211673073f27b5bd75e33197792ba6c568e9263834feeec1372b6c8b022042770f3e11eda44944b4eb681ad915e6e448b44d97ed72a41347d8b31f101a4633e8f77c0540e2efe0419b9f013fc9dc14862158419058127720d1660984459bde02c5f1194798c6a347cf08be094ab38234e168e38d23fccdd0bdf743c0ffbcee034203b763deb2a77a73d3d2f541b1e67c75e63794e4bf737323476d79ccc3f0588cc2b4cad04850771cea36e9ce8d0e14c6653ef318ddd19139e9b11c1dc4b155592676659dd896c766b87a93b97167672708088259b5a38abbcbb57297cb55efe85e979f5238b7a96ea742c6b9dd75c5b2966c1748a54a0db6596137bbee4820054aaa8812f8216632fd8dedef944a1533b6f9bd609bee45add5df98d2022d62b6bf47a4e50d6d56d78fa0365e8711d0535569ffaa1f8e6c2e37c3f5ae1b0ae32a252895d1685d82d62748d87634bb5d2ddeb65a3f25249c596470c2f6df70a80c68fb6f399b147a7256ab0b2b602249a224ff182b5808a9d0e6e6dafe9bcb5979c378c31bc61bc659b39b6b739d98ca28350ac2ebbc5164abe8e5d512e8c99b8292fc8ab6d54b7b658c5d227bc24e70140c45e4bdbc9707ab3fc6f0aab8e97e16fd8ee6de0bd402e18072ea8fedfd415340a91bd56ab3b6ff67c79e40e7f6ffc0ec498b59927f8e69d96776f371d993d7b2a427e80f51fd40f9b43e2c2ac3bd7e6d1741f1a1b67f0b63e8510cf8bf26e7c66fe4a3729286e3a46bfbf65e1e39843635252d7ba2b695a3b15c49ecfaa609dbb59b5dcd123e424f78094a65b8c72ecff8c42beae5003f0c7673235e893e68ef4565bc7013de1e1b69de4b8444952c4e9401a5892ca5bf27b32702dc208a39c65083871b304a7fef654f1bd051b1808d23676c1144e9efc1ec898231440eb8c051c112265a94fe5a8c097aea46acc5f218c3a7bda3d7a2a589cfd39e34978b361104eadac31f8515e82528c9f113dba770e12b5c78a5d266e9b5f25ea96d47efe5bd46a9ec202a6a4d81bedb6ffd5099fbf5397adabe7ea5270795fc63fcdf9fa4d9349086267b1ec61853984a35df8a8899718a443179d476bc30d45ab91b5815312db4d0428774e941e36d7edff28ff17f7fd2771de96b71bebcbcb83bc6be7a797979799142db6dbebcf0a0fed8b8a0a6fbe0c75630eb940d26d12d4d36953dd172856dd2170fe06a8f10b0a15a80a6fef8464a234029404df7a910c08d3fd338b60f411403bd8d2c977fcdf1af2ccf5f5eb48b5fc00f7f0213081f880bb127f0c35104da68b3b2b0766dc743f7eab878154422b718bf9031ce5150a141f1685086d0a608caf6d7726e2872eefa9e28456c7fce3b627b485092b7528213bdecc4d190cec7911e72ca4eec62a3215c17b324501c8d9d6c549f1ba925914c2fe5bd37763ba377763bafb5ec9bdd13daf4581eab0015855d47bbe22fd11fdad70fd153a7e90ffc2e4aa2db013cc8a1245a7b685acb49fe39772ead55df268f2c279d654f5dce2cd6c52107bd420f93b4fdbb9d2bbc14cc743bde89c510e4110df7c2dafeee25164390f7256bc98d2c8fa55f9f163a6df8884224c21b683f4b0184d53def9d1151273920a45a4ac998b2512ef6bd97e3369cdf5a6bdd5aeba23040664e9e965ab07683dd77a43758b0de5a6fce4c7cbc61844e59e44611a5e4bee50640802ce0003309d8f18536e96b48deb6bce16dc31b2712c9cca0329ffd36fd8fca6c4f4d107c33dcf4a73cf1fa27795019cfc4dabeb8a57e0b95a94f3e8f142885a12e50f300228cb3159f886d3ae50936f61547f6bb1dda95f3bab03e58ffb3a28e6b69f97147979616af32d4d1e503756c71320cfdbbe41fe3fffea43f0f50119a8ee2a9b58bb77bb77a6bbd1ecfdbd273f36ca89688b43de5ac75cebf901a3880b74965d66b0e804519acb2a6080a66a0caef8b80c6e8a27c52ed68adc8a8ccddf53c6367ccc6dc05d3ba6e94ed5084d430432dbfcf62deb5e5a63afaf69de77941bc0d77af891dae2a1c2a5371404fb887982aa68aa962aa188509d1409bf5b4e769efbddf4d2c16522da1e23297e9bdf4de5a39ee81389745d287d830d765babd5a6bf54626dd8b860bc3b3c10b7eb0445984e620086e0851e64c5d266cff05bca0838dbd07db5df05aadde915b6a8f053829c449f5c7a63d5c0ad801a4051a012204d41ff7ad365a1127aa3000714dbba93d5c40687004385ce54b3e8f86c2388d7dcd5e3454c6dd9dc69e5e9ce4ef03d484f6cf34987b0f87d4af570a6bbe6d9be7511b549c6f59c4665e38f102c76459d689d96c369bcdce3aabb3599dd559e53ecf03f1ea02035d35f0058c93a3e05cf2e185a1e78521f670e879a1e781337819f2df56f49d31f6bc307c2f0cc3300c4593484bde4adbfbaec64a3ebcf0f3f13cd0b3578f40c49899050861809343f2fd883438a9fa1b9b6a6d33a6be2cc93fcca2e568cf870f7e87e4f37ee34aec8cbe927c61458a1459a881064d430d22a268410517aa98874020d0bd20516b694f087e4e58515c38b399657dbc04d8533959df142c387bd32946a0c1aeff794e4508d736331521a0d8f5fd3d6a8e60dba7db24c1b6634ac7b4192ad54a654fa21b6d52570af507960195a142657638d1fe3554c66eb7410710c862b0d7dd210e7ac17d6f4cc99b41c984464dd55a6b8c0bf4473d6b8f3306ee895dbf9ef5c74bede1ef2ff58701680fff1fe2b751e0aa5478c04979a1b9769881ca939a62042cb6595b3bdcb8595f1566c267e0d8363f55adb5d67ada930f254aaa510cd5099d7da9c95b46b6261ad9140ca26d4d61bcdd4212aaaaaa2bf0c508baaa54b1300c1d35d0da533084a3b5dff096f3b66dfa1a1162859cb033fbb9afb416894852a3c1ee59a94492ffb3d968a66b0c909acac40a30d2286b8a7c33e0a2a4ee826959dbbfa60835e33584f249f745c82da20042f9047fcc5319b7a1ded39e409f6fb4eaaeb03d79fe3e0f213e3f1bedec85e522d96eb35dfbc44c3f7168124b871ef4356cab430d5d1f87c49a2743c2f06b42e158e3f9cfd784afdfb3d566fb9cf785c41ad05803fad038029f9635a11f81348cee587e3548edbe04d554558950a84cb551d0132e8292fc9f3c89228a6dd6949d45d9664df90becccce280c4e4261b0dc53b4334bba36a2dd62a34b60c307a808a8e9ce2a0f5f0c0d0dda7dc1a2fdadb5b310ebf34f6ce5df7fef9f100ae39ab6c2a3a0424cfdeebd77c3410058017062369b9d36cff26c664fd9da3ccb337c3a19d3a94261fc5d2d0a93294c389b85b3cd8513859af90caf50191aee8472d053080bd7406d0f5d307b0aafa024fff015ae2125b65d65495174a8da6e7228fb26870a6754a6e2d3490f83404f7825473de2f9015ba1308ea2302120b419cec2999b4cbc0aaf5cd97193b925f19c6e32316a6371531237edf841fb9f30459bf8bcb2fdb1159c634f6ed01f9e22680fb7273c8633ed2a6c13e7e49c399488436dffea492284c6361466b6fd3114b2550f98708208a2d9ca8c15ae12f3c744e06caf3fa60082e220a8a96a5d88b408d9603b46d9aab5288c47a99a56efbdf86d8965b35900581a07002e005811ae6a78da534b4bd812d6162a1a6fb3a5254412a2ea0f70fb87aa30654f9e0f37d2caf6ffcea03f6a94da43e7937d312aa3d52abe343621e809c72c69436d29963d793625b66f4898db4967a0bff73c9539424ffacded87ed9b101f2a414f7ac4b1cf88654b6c1f71f8c4f611c75169adb46e7f6ffe11156f3bd8240c471bfd3ee4327c0adee0c2ce6134442bc1a7bb0958f70ec5f07d2895e07f926c336425240be984a2d41fa133680f0fa551ab506d33747dc4f007ea82bf16431465c1ff8a618ac2f87bc4906549de8219a6805216cc3008336ced5088551c4acc4224647b2bbd310cb7330e6d86e1199e007071de4db153839b4da7d400056ead860708a592b72ab924f1b5b0fd63c0510712da3e599a694c8c5c232bceeffcbed39ebeef3b3fed3b3f1495f1b6ff87c44df6cf6ff5b176cc49e766dc6c937db4525a29b5df91effcce20741d4dad85face6df69da318c61175d2ef38aafff9ed10fab7f99da9effc509a8895609425712bd8fe38b3fb79617830d30b63fb9bdecc9b7957bc2a1ecc9b79617ce7959db11dff404f363ec494dba372995174266f323759218a30f111a87d5a927f9a4c93691ac61efeeec5e0bd39df798e5c3b4ea587d8a6546cbcf0b129159b253bdbed73d6fa867aa0f1d337c1842f34fecc715c10fc96764fc15a3b77b99e4952bdb5b722b65b8dd9d80791187749e1d5a73cf0ae1b869a65d38f560fa98795c28041ec6e7132a31942eff9212dfff996ffbc595b2940a14522b52900a1452b73f21bc951f588a35afff3f9d058a3e33f5fd3f29e375282c411f8a137526a71043ee88d949e0eb164da9918fa7b4f8708c4923c8523b48e6f79ed39eebbaedb64c0bf7d10fcdb1bd9396f36d7907bcbdd244e7ee31dcda01ff443f483748cfaed3804f4a1b1a665a40fc47a445a7e8638a9bf9146f484362f4cb673abc474ae952b6ecaa3b6372d89938ef136ebc971f6b41df921a6dd1ddb322c8ed42354663b426176a8a76d874af26d88213614541420d1f823428fb4ed4c21058eb233bb130395aa0a29c26d5ed576a2a1abe751b9d43fa3dfb7ea79d38fd8fe266ca716b7d3b39ddb55c5dce426f3c22eec56d9cecd663b2f4c55b771abdbb9a5b69beddc50b04f2da39e5ac60d6549fe177661a18703247ae3a3af0ff4b809609b8ef28c04d8a6a3f6e6449bb555b1686d36a0266c0535692d2a5b0df6444b7a636facdd72ce51ce7307e83c80d05aaf740b9571d75a7b13daec725aa4d0de4265428fdca43dd5721b3d338f8c043934b5a49725e57864e7b99b6c5f456e3bd800fb1e9d6d3b188367b390443f5922693e4216ab392edb930628c93f032a6dd223509c225bf6800504a1325013c6a161e0e0e7b8e79efbba5db507ce02b637622a279bb8573363c32d14a605b7ac1045932db464e2a425c998f6c45f8c311c78d3948056b8f695aa6a4d65fc5e8e033dd1cfddc736d8158a2a83ed6665a23a412d8c067bd29fb51eab2726537bba80159d2a1586abb3aaaaaa8a8a4422914ae5a15e5cac8408c9c9114265424dd384f4a09de82a0e20087dc2a653ac78b22f38032d6d9a684bf27741479396e4a61ec80b5092675b45545fbb6f456d67bc6d993661a8a3452171f22452dad5a6c649234839a033b449abdc7d010c5035dcd070f06d522cdb4fb771d23de5249222f554af1015b1ab0aaa0ff5aa5494844ed583c60b70b2c2baee6b9d614ca3f0a414c7d54a53273f1d317ddfc35fce3953197a02804bbe6fe6aeeb4c980511c3ed6d18e311f4d22d4f34c5a1277a1b412f6d9676c94df72975a92312d18c08f4018d9f7f46ac6d5cfe406b631466db7ea030fe2944a1753e0f4a5281a83f9cb1eb7b4559b161031aa0a3a951b1896480ca743a0c5000f8fe1b7cef41eaf278acb5568711740db516b18dfbbde0e7752e9c1885d191398942b13a2cffef5e7815d721439bffff78db70ce1a0a0f569280e1e40b2c31396b2d12fd97ec6a9bf4e5af4b8373d65a24fa27c91291524c3d82a4885859aa6d90512a69b3e670855e513dbd42721c07d68ab74b0e74eeb6a721d1c2aff94a3126a84057999329b8a9497ec70f3b54a0cdaad2426baf430bade995c62187baf36882c0deb0edfcdeafb0840e81dd9fc7517e70d4c1c93c76633dbdcedb267d79b57a09cd7d850125f9f3e0010000e8d0016e935ed1deacaa92102a43376703079c8c71e3e421692485d11e30f20065e062e046adb29084ac29e30af66cdbb6791e887b94a854211ee6f06d7e9aa67d0350c11cd1d8ba987871914aadf40b95714f79f57b7192b3a5d46690d217d0365f464e7409db7cd9a69f7eba8dcc4a95f9b98329596c2721fb0f9ea03f429077367154a676e7f7b58b2aa55e11abacd153758afc668502bf8be67d2b6e6f5eccbd46b1ec5a6fb0d40b2e07dff91b6065a05ed40ed8d134e2648f23fb1b88a770fec61137e2c6511e5b9e835da2dfd0d40eb4495db782201f50cfdb86f1d675f5a39050b41c6bcc8090b7ad04400a2f9b586375d0022b9a288b6c53881145b97d166b5880a22c52db28b747010b7589d6e612e0d0d4c9928d43bbd050182f4355b4d9f2623d376849b5040186ac973d81a3b39ce4af3deaacd965e202412f0aa84c67841c1116da0650885ea093a0f912b1fd2d1bf5545752a8fd0195c154a6fe0eb60842109da1c5b05d581b7b06819e3c1fbee972bc114a65f8a66779bd6054460c2ab3bdff2d82960495b9277fcd414f3eb363d4938ba0684e5e4c44446820e84ce321a3a1d9a85708db7d664f3567ad45a2fbb2a7ba5abd56afd5cb59f6547372b6ba2b0ac5e19a7fb5d59c6cefb328adb6566b64c4d29ac6c244e46ca995266005b7eca52da1ad3af1e5b3993d77c0034a461a75343f99d7755e8771f714a4f17c6213f03b51634344e5327c8f6853833d612028c95f6b83927c6584178892d044601e5073a08680d2a468532ac95f5b8292fcd1108db1832b4453e43cb1fd6b0ece36afce7d85da7863de6bc4210885002dc38e13b532280c7e2ff4aa9759bcf4864db03d69665052fd3844405803d4a8a0305a159554df594049f5c1106da1651185e8c9ae30d8f5beeeab25e5b94beac5c5cccccccccccccc0cb5e00bde68064ff89ebd335535035a4a2da8c5ac33b7838bc807adc5f790bfe3d42691fa3214e08e772452c7d103a30f275f2801a1cdd2c889d6b1cd990a82b08806a030feb40063c40ce1e1062d8822832653e880fe6007702c59c3060ed715578e0b278b38b459555b09fabd495d9e42c2896e735e213be28cd96c36bb51d50f1f8f2afa518563a5e078df8e260934f6f47a7ddfbd1f08ba5cfe0ac7c042efc52921bf087bdf077eded77dddd77de0e78d4dc04ec9e7b5c03b3327e2c61dedef08c8f75e2cc41731333acf136bb93dd15b9dcbfc9d37f3f35398227cc7ddcb2951fd6337ac68292da5a5b49496eab6a9b13c6a5570a2474a2fa5f7daab8213ad891acba6b4950fda6e0e1f00457af00cd63e27699f0a4fb4de428535b4a9a5546e32bb2d947c122de5a69b6e0beae415b393ee935084be6f764decdb5a810a6d6a292db53da5a5b24eb8ad05b1d8d2a6c8b7a6699aa63d7ee1c065c5799ea73ddd7bdef39ee7e9f7c8bde789a232dff61a73e3e43d535081488894016284dc5c81831cdc98f3bca95a6b5572cf7bba5605f03c2f2e36bd9e08be584b57e2a41669c989b4bcb582ff89dd73defb502ab9ff3c21a80bfe57f46ebc4ffde479f3db38f88743b873c89aa6bd4290b71deb0a79fba7e2deb796e68d7bcfebecf5bacf8513fbcf44b8aabbe4f35a14e61bbdd5f2688bcaf8e76a5a0b4608ddc2acfb590a2ab75aad1cd35bd65a267f556b783c2b6a9f2b71dbffa5e5561d3b6f4e9a77fb9b83efd1e6520057cc939f4de9b7f19a66bbcf6d7c209715dbdf5bcd9c1bde729e3967662c7e8bb9d79e87103a3ff737e78dd69df5eb3689b59fc1172c97b7915a92595755654926d4196c7f1a7b1e41dbf33c41aeeb1b0fa8dfeb59a2497e8e4a153f08430e1808e18bb213429322aa280308296b94fe346bdcbd4b745a5f38f162c6c5c5c5c5c5c5c5c58a0e6dd3c5a58439749d43868f1cdabeaeeb3d1d375a7bfc399f2d2a1d245404dd62459b1a4c83d913d65a0c6b30bc82d1e0d39280d0f84d7cbaa149de2423d1f8bdbb39df9cbb9710219aeb4d727f23104bc2288caa2004b4b27b2fe5a6246ecabfc63649bbbe49c227ca9eb8d17c52f475b316a370b5265e8d55094aaabfc4ae8f4f7c7a6f78d4f370303a1af209fd67d4de48f9f95a6bf54a9bda534be8037afab90b1f6653026c6af6d8a68b052559a0a6eb72524361d3bf5850d346497f0e567f686358ed187230b19620d1875c823e24d6781471cccad0db80bee543e2bdadcbba4e9c5c5d308aedff49b1fdbd4b83227e441584f289593d5067bb03f6efb7885709ea823f48b4f1e14b4a5b302f0cb6bf0fa5925216ccdb64fb6f378a76a7a0a4ff121e7ea25f9eece65c57f664f6c4c138984cc6c1381807f3ed9eccd2ead62e9179324fe626dbfdb64d4f963d99b64d4f86bbaf035fa0bf814cd89df8ed2cd2294b76b60a4f68bbbd973f2b7a61ad32999b4eb0021a0d09e9f888a3219f96ffd899ddb1241de268c88ed7f13b463b1be5d7f13bbe451c0d21e15b9e84d1ee8ca3fc3bc67b634924cfe30c5e821e345e1de34d695468d393ed5c973773934c832f6490e8e575d99397939373bc9ceb12fde3680ffb5e4ea84387d6610ea8e5c3176fd9d9b2cb14a665a49634e360a69f9f7dd232b4bd1639f3db401c82d00d015abe27e250c50fd44c2275b6b52fadcd1d17829fe862907b71ee7551181373d7c571a359c79b735dd765eb174c6ced3d5e170addb2b365170ac10d7567e76077b639770884c1b9f17ea0f28ad97e1fd14b6b4b99cce326ec407b2c0a7b3389ec694bf957e846db374b3125181b6dd988c3ff5ffc7d743a94d0f8fda9a5d494bf86907077ef3a777b6dd8bdf679f77aeeddeb390903cd80c062b1582cd64ae4d68a4a3197654f22114bc412b1442c114bc4e2b2e8e911b878e19435454229382575178a9054b492289f6c229188e5a61695a93432f04dfacadc0602813a90a8811e1c6bc0078d23fcb4ac01fd085f8d96a00d021a6da867122a53043d695fb0e88c45672c3a63d1194bf3e2655dac6c71b6ffc662bd686a7dd5577d5d8c354d0341160be4b94707206badb5d6da5289245dfabaacab5eecca2e9b75028c50a206654d1102a8008e12fc2234e78c1394611ead2b0788b52e97cba5c3b92996dff60a6aa5b0605fd6a5f2549e6afbeecb7f6d8eeae2fbfdfd3e7f90efe66ac355b929af48ceda5099da033d696ab868cc45632e1a73c5b64bd3a18175e5d75e188738f8000ed00cd8ad06ebb22f0bb32e9b03040495b85c2708e511800046308145112cf8c1143908a37437c8d8218a2fd26041a62559cc8cf36a9ad644df3763684c4c4c0c1ad9071a5a2412514ba9a51cd7e56ec32ba83d70129fb77ddef6799f77efbd1709ac6db012444a62e1debb6dee39e7d699f39773ce2f5d8666063dfd095a19f484ed5a1927686550193f81c2f88b5de8aa83d5f97d1f57da5b94162a6ca7f9e7fd847958a2a845bfa9efce72a2a241088bc5c3c9501bfaadd7755ee7759dd76db8736e0665cc801002855c2097f775dac579e33016b712d087dea4302d5cdc35537bb8f77df7da25efba84051a132a635302ff1170dc4e0844030c36ca605f1404f5408320f8e007043f20b6c1281004416f4bd0db9366c54915bc824593e97433f5877606ed9163d3d8f54d04c05ebbbef6ef084f90488922a54a973349fed76a4f5ee62c837d424417595e6cb23d052ba0c7f349fd22b64a1b49cef289f6d788d883ae77db31e4231f103274def6debb713f884422d1b52227a55b6b1511a108a150a94492ffee34c4d0702fd8cec301ed181a58ce99e3a6ac5c1e572ad5d02ca8108306654d11179a6095e017f914e1086518a3c44df786ca68355549fe358b52375553ac1c77c11facd5e524fe2ee7a69ed074d7f1f367c780f74608dab49ed7dd4e86edb90fb2e5a0ed8e7b23f86b755167cddc34c3970423b4379a550b2fbea0271cc54db16e8a7553ac9b62dd544da536b86517a4a196c4715fff820fa8adea0580227553e00cb5aca92488a0a623a8697bd790f017ec54ce9b0db36ddb6eeaa642af100e4e489d10c4e7e379795e1576af73f9b561f9654fb8f4c83c2f0fcc33dbeef545615a7ed0b60530b4e969797fcf03a3329ae78a13676cb7b25de511c3b3b3412d80615bc45a7a60f644c4a4af17f8e1a71e378dea8f2fa61f1c4dd1379a7f3d2f8fadaf577de5df3ea7f6f0dacaa89cca3759955799b5ed9cdb9fc69eb6284ef2c7c95a682f44555e87dcac94b62b2d168571b2da54300ab363495ec5a0f5555b9fb1be3caf0afb7c3e9fd1f38239b5e979bd5eaf94c70a591076b87658b1adea59830e292d89b69df6542a91b18f89b6d39e59b8674216ba060ea4a0a46ab19655a45bac3b85261ab59db58abe6a2aed8a9b049044d7bda14c4755a15e6cd35174e352b5ee10a1038ab6b9aced5f738d983d29a32c12aa42555277e1bee904bac2380c33a1409bdbb99daeed486363f336fa53182257d8b6a136246eaa3b4e7a6d55d2a66ab9b4711bb7d38ef7841a68f3c278ac40d3ed1947f643eff9cf38b263a299a8a46d562641ea10890000000400b314002028100c85c2e180583c269a77353e14800c738a48745a3695c7b32447621c8490318438430080800080cc08cd38000a8b4525eaeb47e8f032129f8262d28602a1b1d6e8d68c5d56aa8de1a39cedd5a79988ee64d8591aeeb0cdc4147ef14ec4a68dc9f8af72a004680f2a2e8aab5aa6772736adee8506497ae966b6703627ad85ce7f5a8d575d1ef8acc33176e7d5d762b2c778f9cc43e03b42fcf6253df08b319c5ab8529b57b97960968d772b173a6b49209c12bcc94e42907b624e295d2fe93941c8e5569d022731f74a106cd57229713a763c73b59ec2da2408aa214b329eb0e0764fa1cba1b1dcee5cbfa06c412c25a273db4ce972030676995368a210d27ba79d6f2aee3258d47d99755c8394cab454a2682da31f2bad7510c11f134b8099612b549918fbcdc4a71173a4e517dc533325580d18722626c6725d7be107e15d9405a711339fdd60d15cb55e41aca131423a6fa7620085ac586feadb908960c50cb6dea0f178a29f9a22013ed6627262d719a941b105f5cd43637efb08d23686c0639b806afb5f80c8cca9d2b397097035f25281c0b50716159c50224a8c775d309db3c63242876474e86ee73f8151048b8221761c8b54558b0476d1622964aa86c5d058b30b23840f86882392a5f06090f8810de482604350746cf22c824d257f7141dcff94190503c01a38156f61067388eb28674a68952da4f1c1c8b33da925a8069010d80acef82baa0e085fb20d53ac17dc03b8c09cf01b56920b477518b687184159a4fa423e22cb393b0ac0d288ab5c55d357bb5c6a30fe2aa37678d0970b3b08548c6aa74efce3f24a4bc42ca64051e96b1e78a6f9972d6dc65deb11e06bc84725333cb3ca717bca6c04f4e2a68be6e07bbd77314b4772d5754c81f0aaa26da5f332b9b2595a590b2275e7c2c004396b2780bc58420545bd1932375ab5f6ae6ea45999e4268e05d55287e1e6c5d00163fc6ebfc3350712979aeeadac5a50f9decfaa4c8df88929bab2001c12cb378c567dbe4b672be54f3bbedc20f357208ca534e8b1fec52debdec085c6d84fbf907b5a26ea2eb6a7108e819547024048dd545e65d7eba73d7d4b00fe30018adb4ea75b48a5585fb1f22953a07955aa709f2d8ff1468c9463a2ff80d728ffcd76cfce577cd4f8c4462eee1ae8e7c2dcdd59ec90014c1c215a3c2d51e91aebb2ae0957fb88dfa2a000804339df7f744d2952bb5d7ed40f9c005cfeb14c3787e9449c8555eb86203de32f12dac1a2d4ce29a026a54ba9d866e484c4257adbd79ddd21c2a9c161e793b40cccb6db3d2fdcbbdefe337ea26a26e4ef6cacde8d63183c12dba0114d64b3b0135f453732aa7d6f2e3aa21e3cb7a69358c0f319f4ed2c6dc0cca99c8fa8b6ad9d8820cffeb4abf0ce824606618c2aaeaa7649b8552501a219718c62bbf41613134659b022f09306eb0cae28dba061dbd09b753828e1166f52c9b27ce060b06a9901116fd9e647c46bbcbdc5787b833397885914a9d10145297f5633cbaf87026f92b664afa5127a191d2c2d2d091a71677801ac6aa301f5c7567c096aa597f1312e83ffc18be44ae3ec0cac0c7e133f4f3eef9f5df07970713222c945c7d43153f130eba49a79d90456b975b66f98d6158bacfc0c8edd3e6383400d2e1eb669104e5552945c93136d4f04cfc9289dba0ee1d949e4a2d49f1950015be8dda18338780c24ce4bd3ebda1c1155630385a454df2ad695bf6301ea14132b10629d911acd42775fff6368a1a0e685207b116a91524e6514b272498d5f43a195b932772d1c02983d2688199ef76de83da4fca5828699f5ce373f983ee57b841c3458ffa7d65fe6b64f2bd2542c8594aebc80f8bc853d0f487f27b48c2b8b8f07b4c081c1920478febf4523a8bfeace61e3ea8e66747e2f82048405702dafa3354dcc7f359bfd8b73cca41c4eb7447880c5cb482456668cadd77c90c8af77fbb4c49f8eaaf354860b71b26bbb67f0918798dae66afcd34d60bfb707b65261251a339e6dea829d1bfbfc86289b0057f59680f5231ac7487d1d9f105892fd4c3ad05ce99541f4f43d859b66c05dbd2288a743a325c072160087eda2419bccc43d97d7484861e95dc03f952a686c086202b8be0db8bb3d706fbeb323e995007bc1eed01e09639f978afd1072e19db8e930e0cb07a2a13e08d4d9e702400cb2b10cf0dc83acec01de2feabab5a0c23725a8785807d28a8dce74a8886f20961c6df241c090787f0af50289698aec7e850d6bc22f7cdb1e045d89ad9f39413b5811a0519e2e165f5afd92b0f5932c6cba1c0cfc708b40ab4e3914c92fa8059db5081381a34e854b1220c14d80067ba7eda8f4264d1cd3efb92c7a020c513976c93fc7c7702f608a7580a9d6596098fd17acfccda529b7e2295ca9b8bed23ac751c588a59e2849ae69e5537ee30ff7a1643ac6f4972492d74da0c178cce36157110a57c065b2c03944fafdf3bbd9bb9e0ae8b0cdde4b1b664e2aca6f0c74049aaffd612cdcfd17121af82fb1151261e99e7bcfa171f4f3bdaaba77cc768055ce9e50f6aa94064a59fc25fbf1388316b9ecd6262281e8da9840cc4a5af8a5b70dc46710a0c4f291d8a157a06181ebc3ec9df61d9bf35600fdf34250f46e33e73924bff6b3c44eb0f47b4bd83133d9fbf8b90ae112e262a4c8cc45fc8179ede1b9f33edf9c8b4d96ade11839992fea9066f9012be3f744d624ce78a3c71a7922f0ad3af6f2369dd4fc5629d06b63a390dc3d11145a543e34fe6e659f58063d93e8b04c0de63b2782800e27825292d517ed9a08acda092c153fd395022b2de8261ef7cfa6818a780ca6543e24945a219741112806de9222052db63be6447fa345b339462ffe8296c5a8d084a0b4fd1ca6cfd3892b72d069d3ede803bb88265a4e360834efd52aa518608b7c51f019b4b04e440ea8d94e8ba7876a9e2041006a97fa51500c58bfa2cdd1e370f0e5dc49cd8870ac2685e61f99220630acd9626737d6f6ae6cccf7880d84f4886b24ac00123c707cc9077bbc4296247cded7fbf4dab0d217ad0a5ce12d5f47a90152090c93f810fc53585c6cf9059f236f5c21074e72e7543b444608a2308b7decf6f54e65f4a47561fff5c0efb58976e113bd7be8aef436871b1f2b8d99beb6b588c01b24218453a2e8eac59ce85d3ab9595c61f099cb2c819a0eecaaaf8785fc05d612a897533c02a6a4fa9a3ae34d313e8d26fefceeea4e52c6f66782d5c1f866e7b4a90bcd98eab6c077479dc789db3aa862077fad4151c72f9230db7bb32413296877940a84d759b45b6c510265b195a5f426da87707c26bfe47214e9faae25e8db70aefd1dfac0eba3d3420d3be62924fd4f00002182b41a2e031e4fe901c66dd93e3c253e03a7460384beae8129ca5c8ffba7e1a87ab48fa71c2bbd61cb1622984b518f72e3331245f9f178216cf77037cbbdd7f47255f2448993bfc03ef50a0d541d49a76ddff7b3591d6ede8f96f7b75cfddd72ffce7f0e3b109e84897c5c6e134dd00524af49e14fca82d32669ff0176c6537bd81ea4df573c1830e7c4861bbd28f41fd89e66d318a2b03dfaf8029989f0d7b0d6bd86fc05db1389eeb1bdec390c686f29be49f764d9a15ee3a2891621dfb03dd29cadbcd81e66638d21333c99a8cf801a8dd33a1a5b07c40811f9cc92edf173d61db2087bc449655bd8647b7c59d59d320b1684a12e02656b29998c97a6f0ea3162e9a001035a12a3f288dc4240fc5777b56af541a8ed969a76f792f089e8408d3d73c1d996c3555cd6fc5847139508f3188df179efbd139e4770dbb3687df87759301d3032b23a589b5d697f76696a4a43b5028491721621a18379b89e37db9fcb62ab4699073558730994c0b994601e664c2f7154028184956bbb09127b8b464c803205f2c4c6ada9095f0c31ffcb79d47cceab82bfa4c44d68ed5a987490a34fd5b94258e0885189b74d73b2a5830dc7e71c709a00f8ff09ae543db9704a735e303c5a50392bc6e1b3314a148a312a94e15f681dd10ded96f7a5310ccf66ddd510b8c19b1ae93ac00504f361fcfa478250ae28e5c7983a8ad38ac8203b28204db16f4078605603cb17d3213cd404f274f1c8fed346d851c6202349e2f496910429c96cafdae5c78655988ba004c2b052003de9ddf40e58419ec0f9674f3474de01c17d27f1c10d4972127f644e130b078961d782f9ba9ba416f1a24e557279a417767ac112a0e65d7b1bc9d38fd12113641bee88d1aa7931be1fb8e4929a69d5083fd347de14ed56064776e5e1dcfb5f44642d90a1bf7c0aa3df60a99ecb943a3d42bc23cf6341d7b695e4953027e47ccd3395b0e88049ae8f16c1506391a59b64f19c28d021be8488d3240992724b752711c45250f480b1b89abf0162904f8418cc439fd63692ccba73ec37ab8bc0427f867b628040afefd705ffc6cc30c17300c6c2bceacc2b972b83fa31ea5554abc238d416fbb7ff672c9aeba59ce04861a1756d4521521d41f4a2a3749a5414a6cefb19495947da748e23e921ddde5f477119297b09b000c3c5d24c72a6da932cb3ac93c64643174a7cb21eaa1a533c1cce791d62d3da741a0ba96c5451dd096bb7700e1ea99f79b335988bcd1ba88fc2b554e805596ea68c8806238370589ef991ee3bdee04697f10788899bd5a36322471433f5111f013bc5a83a056734ff81c6d26628a1e8ae9c4f03d7d2b35391e436d38ba639901f38bc2961d6029a6c1aafa628f8c3975ea647d856c60bf90030e02304158ef4f6da275379c474a0120932c9a1c3a49864c91f885224e7901eb565b877d9ec48bad0dbcab5c8d6c11491c8540fed4f2fa70c6d9f00a20a8a074814edb1cc7426f0edb4847c528ebc953bd2e4e06ca7c5fa9fb5ceef6f00632946d75c86cf4b89575244146e657087535a6c15cd9d1e5d86cc2561f0d021f982d8a74472c3a2ba4949934e4243eef53cfbf3bc2314261f63a94b091f21bd20ba9e2a020e92f63a7a58f5bdfc8835c64dd6d8715714a6561cd884ec6d1dd1aedf81fe819380126456899dcd8578c8a1b5ff7813c8a48ecf4266135caefd255ce95c3abeb59c6e6c5508169646b09747521ce8f24fdff328ffc68f9b56db1c7b818b87a3e3b40ca50a09eebc136f391ba85c939f507079f8da52381494e1f978090560254447ab5408ede163597fa208c4b1fbbc73b14e920b2ecc7df6c11f959948a7b280feacaee5fe7ee8b93e9019b1c875a1effa6a29212a62151b36d924b6b3a923691d55f26903f3f6488470bbeeaa87f5aa928cf355ac116f9de752ac3e28744624fbe2b814faae3460319023465b33016379d63b957196655816ba21775e9ce088d7b589a2ea1f1005180e7ceed609a744a34fa820eee916fbf16573a997d8ca2b2bbec6990296f1ba036e3fa53bff763f31a7d4910f20b8f0b558e78092693193198a9d3d49ca4106d834a3c24a05320254b87ed1e85d3cfd025efd948c4c1a9ff210ff21b83efd8af846afdb8aac168084a05571aa9c156a19cf7005988642c7bd7bcec260fb75ca8be00c41bcbcc4185a928a85e06422b61ef8d153f8783fcfa23112244e1008771dea0198e3b03ad939f4cb0b5c56a85ec8e29a0b0ee1cdbd29e0b35271e7a122a01f840fde62f8fc6a5080b15e8ef6ba2786643bdd99fb8be2878fc35251b017613047b1d2273b0473a453897ac52f964ae8e693485cff825be993c662649851429a982f33f680a9dadd48ca2783e21b10a7ae67b284868702ed75dd1da50c3a6dbb0714bcfd83d3a3e752b100c031701ef0e1e78f2bb24fbebf344676ccbfd4eb2a8b74080f29157cb831c01a498b476dbf2c7d9087da7d2556ed5e9357628271e30b30517a3caf77d6f4da3b2189b05c24dd8704e13f1f84077c42f8de3f446eeb12b469971882ec92da2e386f4512c37b74cd822e6138abc261c809306bc684e7867cbb2ca8cec0706efa1ccef811e3ee5c19c6881336d2c6af1d42611b456d4d5af83d725f03869200b36094cc249e39eae82a30c09df4021a7ac5054100dd6a39940294d54c822f0f6784087bc528d9d89a6f8bbd68cba51a22ace54cb860ac15275bd86e5e3bb83e3c01abd20d0e9bf89534e2925aa5efad3710e320f9f8ba700b133d3b091cb86c34cd80b1b2a2a0088f1153a28d9368d4aa870110667f20fcd04d9871ee0b0ba68676f326b8b656ebf834a0949848a73acff723a2aaf086525dbc21fb9c0ac018d5edda965d332c7e7ab1d6da50541e2095402a99dc94aa321605a79b9a7f56f67557aeac4fe89ddf102d91f9120dcea59565975c32668439f166b66f99a5b137f949198678cb3a8066d0726774793a8d5d336b45e919f91739e7ab66cf802b43e638ccb6c29e0055f2c4b5c04cc82a97b8b9fc7e0abeb53517708ff009803e7f6c3e0f441e68d20afd2d93c198db9c09a9d9ba7302727517bceaa43ad32d24eb0214dfa099ae751bee13f2cedb893382938aaad6529f0d882e672ca8b967d233a1a6964c24720aefe6d4e3e11bc8020bb044e66e45e2adbc0189400558230df19ea983d3557de422e467918a698142439bccc9945a358640c807eec9d7c118c57231e35050c304c6ef74fdfea1b4de5f0ffb11c6f0cdc106acb374d9ce23a3e8d87518d3e51c8f22d1f72fe8d3c6098fbb151cd8dee0816990a57d3eee4b31e93ae7de71ed91d5223ac2cada623454c369fdad3eb87c1a3cf1db7f78a5c18170a656f4042ea5ddb1292366bbfe86d4e55e03ce6bdad59bcab0e6eb346be8315d2829780488ba7b396ab87c0344de50657fb2d01966c32dbf2864f22ded43aab6b75907e7fec0102211e2207f5e1a2abb9aa7eb07d9e73cb173fdc7414c47ebe1bb8f43e88f849b72a18c8f008eeb6fd286dbc56a462a630e3bb91e686151579a8359c55287b68cc6e5c3c388f8f8719db4c38890df3022b444dbecd96930860463357cb1704db5a96df5685b5d06775edd7080fe92dafc0aefdd464244f051d5884433133025ecd16cc49f5f2f15ba3f21df7c0d45df5cd6cb6a957c58f84a29dc7b16e62bffae958d511e39dc13885e5c0c76a92f2e7a943520372495bdbfb4444c380fb0270f879f61cf754d0a0e8405ac72055c8e8683c49c60fd36207e0b7bb262c831f2e678e21c623ec7e0997dcba3826e6f9183521c8a9ecf925fc52a3502e49667ca7e26f883ca22f080f889a5160db9dbef2000f381992f43ebc6a78646ba416f5669c98fb55c4089b586e34c8005ab2fb8dea7006133e5bebe8ab6db1dd23d2cf61f7a61bf0491e3d7dd3a38b7435c2ead79eda483d2e5b63e0e6ecef724f3f18e2b25dd31d1b3348592ac4763e00a628feddef4898004af2f93fd622ac1c2f0b01c6a638d50a391920d7122c42aeb290256b0241fd94755e151cb65af5002041626c2bf205d83ac52e1e1b435de33a63185ac57e151f5e5bb38edeb53cc9f931e7f0a003e49b8f04072b41d365c7f7d4732a332331f6031da484c8053f6f665c133fb7295769a39795508e0f11ea0baa2b7a60a959443c9b3c9a2283ed90a8251b79ee2cd836b7b7ee3740662cd13c0c25c560d0a28693bd8de717f06be8fa333640eac6c438265f3a02f2452945236fac092693b645526c354faf4359dc7a478dd5fd3d27f327e57fb2efae7aff4f83896f3c1261d812e9ae7349794b5835caae21baf0bb5cd53b40a0d2f2da4222467e2e31438c2aed97277ecec529fbb487b5da5d106eecab0ab856d29336b2c1fb1d4b48003cb245c1548c129be12b4a71b689c7a315c9c32a10ff5e6679d0a5b8018dde18b217e72f0475b5135a11062352e8842bbd5d12e0ec66176c7f4ffcc8c6e6c10551a1e1c9d3d3d2d3b3ca7a30cdb0b876da1cf2c924f17a60b6328bbfbf2a543c04046871f87aadc0f540190a9a42f117d4124087812267ea6abe3217a9dc9490ca1c957fdbce3a7c01ed1810db43e00d779c26046e0b43f816045d4d4aef0a8852b22d1323d8022114ae89d0ae51ef461af8aa304b79d56aa45cd77ef72dfbe000886caa001288e4930899d89ec9f83cc88a6869e7e7ca220f19af792ea7104b8690e8c2aea7cf8c860cd2f5acce672e1e208889dc172975cc575a9d3724fc2d92075bbf6337ef14be43ca648f7523da2544a4861776fcd0c4aa715786aaae79c467bd97785080fc817fcc586c7a27482973d54014a93ef3da843470fb0347bc9e8cdede180545ea9671a393006529736a24bad61446e8ff9a1474b65476c04f5947ae94a8d43cf861fbcaa894f9d3ec51f95d6151a1044bc1f8aeb090c593d0b7e57399d4956ac977b019fe077d291b78806d3d4a67df8b7fcdbcf25a4b040b56cff6c65e6ff4acf4cb081e78863ac0573218bff8625b3b4fef5e714fc8e6e7b7700153130e27c6a6a3581744c5de88538bb408b3ebaa2974913d2d4278475b394248179b0ac78831d90ec3792b2139de4d69b3844bb61e3103268ff6e308591d1ee5eeb122e672993a1f3c3d18a639c831b33a3d17ec804bac91589df0b545fde721e04261120ff8e9953845635d38fc6cda562d36dca09f412be04c337f792fd5dc2fbdc90d5e03af88fab2cc325598d4bb80e1099d8be1a507478935268f51b2be2fc8cfea130a72d0cdceacb7db274587d9070606e896fde04263180caa01f4cdcdaf5c5303c8d06f5eb6d525588aff6d8a39acb135f59b79ea8e1c5946e190a5382e4c544dbe066881b5e57cf225be4b9e182f87c34b34b88c535ad483e981f8dfaf98dcd3ddfcd33e20d7c4f8cd78c6b9bf7fb5efb663c88b6a8e25767407eeb56c5859c284de0c67e507022e2f6688c7886bc0649772bdcda1a2953f396d57528c78015ed3c246408947b348ea49ccb8326be6380a8395fa7220ad1f24c9dbcc1708e3d4c3b7ef36c6eff074feec4eb45317adcd0f94db203820aab3402d7bbab4001741e2419d87166b7308016bb05e968c783822e549ea739c0d8bdec0629e8a6180ec50514326b8e1c53872cf034ad11f9136d474c4a231218b88f7fd1ca29a3942558ca859b53b311ba1f60f36ddf0607042d8115819de7509889c85a55636b2e93742b4d6f35e9b58e5ade70ff195c8c86ba4110127627567ca80fdd452a54e2b3ba809e929e9aeadbc1c5cce766fd48b7a04f70b36417e80baf317a1906698975506155d484bca9d2b5b168006fdf53e4fc18f4ba4b073045b25765311a90b84d35d0fd6e7a744937ff7b5d661e754faaa261b98d85811bde5c1c5ee4d3147d6e66b6af4a3074e87c2408c6bfdd89c78a7181832bb5da0762814b2e93a35382692bcd7118d625c955f57a604e20857ae03f3e3fbf3a9b214c825528cf743240023c89820ba7d67bd5d8a44f86bf06d522deccb3bcdcde75ff915bfb5f669436f5c47365b99fd24a106fb1ba6ac4ff6541b1ae0a0f30d22699eaaaacdadb534986b87bf6180502d61b469d6f45b4c93789782e062e0b10528a92874e5915c657955f28d21274cc5e7ee840e75532743d6f2aadd27b795f0e3d22055a53460aa358e8a340389f56c68f5418c147d5ea8612dbe89cbf88a85ab66a88cdc750bc6b4daefc30114e594bb5781ad23aec4bb0c23edef2a7235498ae6dc2434361a1a791cee111c161808b40bba571b791716719b15bdc449373d6f4733221efc0f45dd7f733534146d98c00f5fa1264ec69a349212ec5e37d7fdf4097af98b7b5726b8515f9c466efad9df2a060904e00db2c8975abdcedcc4c3da9014795753615d79b62de1534b448cbd0b98af27d043e619f103b4c22769526d9af2b70e29514eb1eb9a13300961f7bbf7be07e50b0a16d5eeccb7b9dfc5601d8917e63513e07e18b278a1673c07c819a04b886177cb73f9fa76b15eec6de26e8b383044c5dc257972ec1f03a4943d54d2968da7ac10aba300c6fd7c87541152b5fc365abbe4521cb69766639ffa131090f2e1716f65bdfdc1a2cabb38d0df3739f5f34454638562d3a4352173806f055449aa426b4435bdc28dc62ddb0a7da28948944b1ea2f0ada05d94e92f5d868a290a416c397c82ed5315133e764b7cffc5693322630a854a3fac7816150e7993f49b0ad81f27be568f043e2a11c54f04b1194775e8a31d148368f19375ed564ca3e8b5b33e8860ff30bf12d4b575fdfd2cadb0e2acb096ecf50a41814bb0967eb0fe2905c3ffab6223a2aeee537d7339fea1db1ee5e45ff5d2c4f5bcadbe0a0ed5e58da223c5335cb6a113f62c72f60b5503d0a56a418d7e24a6fec4befee67c9ab6364e16808a7dc679d2b862a5c26e86c74482ca18f89c6458355a649bb3a90886e3b0ab79b354c13a2a51684de8559433dff6e53a234d9e0095827eb10049a8125a477cf8fc0c1070eb1491bf3bf8b787881c104bc19030975afca95e9567475498c003c6c09efde7128aeb2985567c9ccc52bccc6ed26425e704deec189ab1185fd52a125dda2f7475a584cfc57186544107715d27fb89b69514a218230fb5a87fd8747538cb0fd4cfc92682d90247bba4faed5ec872c90ef1f8cbaa950047b8b6d53a3e762fad4d75564d464ef0a2e684e43d63e7fdc0309431b6881771fcc032b30438c92a41fe4112be8923ec9080b270fd93ec0483e805137fa5f849111eae65f94d60f68cfc89cf006cd2033213747d0739641229063a4e9cb01ec47b88492f3469f66e11de40da0c2045bc6ff952e4ebfc130a186ec5b539f4e33a00ea4819ca5a9ae2027ec7a734e3ae9823a541750ee7d909a4c48ceff62844f12798c621478bd353fe04ac48a013acf20eeea8bd1b3c3f15ef59ba7563a5150c553652c7bd1e06571f72d8f9639cb1c6465052709aeb8213966dffe83d5fb4e4b978ed8b4d1b3aedb82c1e9db89cf2e8bf8092da318d85d97a7c58270b20fc4a2de71f9982501fc48599db76a52eb92935e392bfdfad1355e018647bb4b41a527a74c52e1895feb704069b61210697806f32fecdef802205a50a79601c9e8232dff0052d2d61ceb6775de81aaaaf015e04ced6ed1c97e8b1ae53cd716835c9d1f424a8dc6e03268f8b026e472b561ad500d02cd0cedc571eaa3ef1f040ff4becc0a554ed464aa283566d6293925303723fbe7b266799f9a2139f6a5f86a39368f3c2e126ed1d19ceee3c9c2f27dca5215a3a5758b5a5417241030f1537502aa426651adfe8d4f00f6867865e41c4c39367e4a242c2a254030bb03ead777eddbbd393472fe972e736761e8f8ae2b891a239359fca35b83302555289aca9a6b59c2544a3dfc896c98438464d7c123e503d50dd13b952badc182a59d085ec2af9c257dd8cbe8725aeab26a0712716989c5fd4ee5aeadc606a192085f263ae14f50bbb83c5c1d5689bf812f2c986e011085016f8ad71cae6a5c57efdbb47398b8a14207da32606979e898e1c96f6fc593760491e4a091019bf576b7ed07bd411ec641e4d3caf259bee758d144644f3e6e9a943da71402e3c6176f5ff7cc0b02974a84d9cbcc645009461f49c35424f115fa125d5e29403ce9be9441dd37343f05c6d8a8f56cbe59fcaedb96f3306e97491fa69291acdd5a80d016220010566a1ac1b59da677af3cbf33362ef681d259694614f842efc15065e6a50d93aaba93c1fb3eaa406c27132327707790070cf8a1405cedd80d5178c325f2f9061f4bd9dc501e42a1f43d92f1ff1d41eebcc18f26885e288a035fb0d8ebebaf32127caa264499f0bd22ba565033068d8654fb0030b28949dd0181be9e05ee28a9efe351ac06ddd854e0fd4a3c63730f99bb234f0a721004a9c443512fbcac416aeb704a96c70648bc54da1ea91e6022255b9a848979e27582844bc96fefc239e1ff72869ccf0adc1979e2797f82a495df90c078b49048fd73af50409f1c94657f36280b6dde3dbf442dcfbaed2749ccf82a624c4bc450405d6fff7e747e1fcffdac69cc3630bb258e9ec3a61574fef4128cfc848c11c7f852221cf91b344736890722d5ac2dd591861c020e2e951ae71b91538a6283e43cf0b0eeff658b4a7c8954b8436dabefdcd2cb6758aa485cc7eb9fb941e6fcb0a6273f23a4ea95b355c0ab035ff5e54230bd35ddbe9d6e074637ce90535871219a8c21062c6fe8827527011c02275794042e7dbc1f31d3e2494877c5f48f2aa6e42044d3272307519ead9acc7957ba030d2da91229d80dbfef18c77eddbe133e4b4fa54883df2167ca20d67d87951be187d96a8979f39d6f8e3b4c82ba1afb3dd308b4a8f75c5944732992908879bb84d98a108058903989b73cdf8ffdfcaad7ef2231cdfb5e46f7f53491c5373339fbd48bff4459f51545b70d1f4f486b5894bb9b8255fe609e2473009d70de4fa3276acb8fa28234bd3ec00e886127d3c9973ae11435d30e1b0bb0451eeca81a28f41333b84614e980af898533249b9aa75534129dc92c6736d5159e9ebfba6e5202e9b15315ddf09822eb4f229fec984d6b4b17aebdd31e789256fe4c7e1d8ad664f390879ba17501f0152863763df641544c7c74c09950c89ac08a02e837f28d69e7b147822f500a3b5d3e0cddc673e039f8754577e62336943aaf49380316923fbd7880837fa5a7fd3a2a41f83f2658e0998061dfca634ac3eef0e839a64df2e5fff05c41b1c43e8d022a5c142e48352e65a3d919d50cde624446043a89d714e2b1b6b472a423e9505e9de0edaa82e002c800c93b618b7fb91be4e9e95839855f0deb34e863f2e3772bcb01b96dbab0609955db9c1bfd9773b891cb154fa338f55316d7c1e4b358f071dc0123678930234fd289c8bcb4b97e2ffa53c1427388ac297025258b756aa53a103ab1dce7344c649c421b90c24693c06f97f224cca6a7007ad7a7241a4f242b766561f05b7112e31cc7b085e35db0135d40a13f1666ab114dd7cf807963c5d0cb3dcda3701208e6c0b4c309fabedc28b458e45d9329e678bfc8910c98b78a699a6a60dcb024743afc1737c8c0742cb9d737ea1a8ac4ba95d972a97bc855f6c95316f77c1be3bff036a50e1408ffc2cff7fc172aac49e13c9cc3e62cdd50330eaad62e72ca63fdd0bdf04956c9762f70b6c4ebe09cb4a036fce190c93844778e0232dde4ba061e97ad60e5d9d48cf14a936a42f5d90a82f44c80e451a60766691034a781d4d86fa0e90501a40c0065a1e793379f876d2575051da6da9c3fafd551309fdaf2bb851ecba63ae0b774c4b0c5079b7be7ae46fd7eae4b3e53a9534aeeb14da95c7b7ed54bc2715d811b3547952a41641c17788c54519519b86a86ab1c8ac8081822b75bdfdc6e99075e33b7e0b799faf2b0a6798324f2c202f69f40862593b66a6eaf6a46c2b9b879b509b4398f12e156bdcf138b2666c5cebd973f21249e5684c974489ce803d56b592babf0f46d031e1ff65ea02b33f10b035959aea39c588774a28743f8410deaab34e799e34b1d380ef43e221ab40513a246d9ef060a2c8a8ee733147f9291288e2a507a03a2767416d37e992ac8730d315b005609ca90395d11a44edf0bfe378001a6932655c7b0cedf47b166f456d462ad46523df05346a098a617c5519fd93ce8a02ac7aee9d2318f72793623d579f6d9f4542e6ff84e674f0c78d1192a784fef960161b7442ffcfb523a7acbb5ee5e9023bddcc8be8d7ac308a96516892cfef267be4fe8675deb5f48e7b0f25cb048088d61cbe7cc2bee32dce5deff28b1bbc52ad481bd4f07314853dad43f6090f53d9f5fcdb355f3a3093c51909cd617aa4558ea67cd2a59e83861892a6b7ddf0ca24cd9f3280ce56d65e509fdb25d7d7892cc423131f18e9f42e79b09d026eb946e34411020a129f3e51836f9dccdf9f15b21636cfb620f2b5422944d24fb589d498377c528b05e88d1f17922c69ff54e3dd1ae8787ddd5fa01fd52d8fcb3827bd529092c58c2c7ed0c28bc56fee8a7e97856dcc65d25ecef8e4881574b5dea26040cfb2490ec1990068882ad608c935c2e1e94bb481830af925c4f99a74880a8762a125f9285be843c4ebf84999513cfe0d9c7491c741e0069f613cfa10a23cf8db69e66f8ae46aee89795cd8b566f4073b2b37f52bca4579e00da3b42d03c71ebb0462e4756608e18d1d38c1d42e67e490c07532b9923f55d33ed87dbc2513421915d5907f18ddef17671c140ddf11a596791ccbd74c96bbd03f9ed597434f728274ef06bed38129e176b4a9825b9bb5c03e14f5176922f58ef8f84a3a6b575ade0a3b67a9de098f9a41bb93b3d4a1cb9088c81a7c811e8f409550bf329e418b5d5cfaa2ff001347e839b3e0bb9ea30c687999e36f3b7cc54e6ec2de021e78a1833d1cfb46619817a5ebe5483058adceb47ecb89d2a30a98fdd4ba5dc47ce290ac3335ee4371f3858a16c02653463c6642ea7752f9cbee4dcd4d1a6381cc957c3b10c5bc53e4506b8e474befd02e514830ef8c4b1975f64c413f514406a98f42d991505e12b567b4401f5d944d434fcc0d1cbe3c53a8d958214667c6104619d330ee8e1c1a8c10379f120d2cb1cab07e060e0361a48c66f5a794c141a3ca92c9afb255eb1d4e301047892fabcb8c60e051f3c240bbde9965bb9fb360078126581e0a9e98d77e5090421bea07b82477a22ba6b30eec3917ea049636e1149eb2223679a5a8a2dbfd7aced3064609b46b37b0db25898b361738f44886c486ec392feb5c6263c32344f6e6e008c3212cb99ea88bb703850f04e02870c00129291e84a37812cd8c0f0311a03b6e2d67230632b7a0db409a20b187c1212b7af95e6f6cc2df0bb64c4e2f8460400ebdd6e461d22c08cadd35378402355a291a8a55c1ab9e67deb50c541ea32348b626f7b29dd89390140b324e39b5d61891326c871688f51456eb677c86736a40e75a442cccd9b70033f224dd43c6162cd217960e350a43cc443f53221e50b4d3e6e5db1326da4f2b9a9e5ef89240fe3b9a1fe2d71249c5ed36bfa5746c4a3677c725533dff358363305ebc45c89682f68bb4ebb38765cb80dcad025daa8073c5a8232e50998de9a87b24b8d0e297bcde9625b3b023cc098b8ac3d7c30505272e1ee9c06db6864dc804dec00b39ea1b65c48dbb76ebcfe64d8be30be11eb5c67d25bddf07fced703ee5fa60f7568b2972b8172d629fb2bbc50d323156e9483f2f55cb2af2965a6403221d45c4c7452c372f437e046069a309593b3ed9de10ccce36ab6bd33d019b77e5d17c9f632bf4390a4db51f043c62ab0cae15bdd7ccf345cc8c6cc4ad4c225a02fe35be338bc8332460909cfbeb0f731d6ca873d04765f51ee11c0f1c6d09d1d166aa1312bd900ae4e58a9acc81f8c3618d1c73e6299887fd1753589f23e2ad6b987ab1e85d0cb5bf9fad4c71efc6f2056552beef494cee7cc8a8391bf35087e1f19a7a318c27508836f4ed57cc144e5ce7c5e1765bc0b5734dbe5b25ea3003308b863df36349445cc07b6d955d0b03db123b597183d8e429c463c2191af5974c2a327a383d59324df32502c0ae674eb489bb0f85ffa27cd03c51d0771f0292662a78c37ba782571c37dede63935bd87357d29a23f72882180f2765cadf7e650ef9ee7462ecf427e5de3578304406c846ea1450a9449e9017a5c2f8881a487fb9f50a741b092c88400d3f909a026b70920bd253d54bd6a13ddb6725c9c0a49c9d0c0eabd2a3bae4d48726dd5f78d2d4f8da4ae343f976a20c57690612579a84bdf244c70ece9605a050869ef88250e96e0d1b651447ed0b05ace5311aa4d9ea16e6051cce3910448aaa176c28b323bc84aeb7ee12d4103ed260713a13d6fe4f099d5d7c75fc8edd33989844f826f0c96cbcbd908cfcf4e50afbb4dc0ffa8965a87bd41ac97bd3370194e871f7c25e61d1cbadbfafc6ea9af8993af84bb11bb07c8c53a904661d759b00b6c2c35e42301a8c9e7266fb6bed8e449d6cf9e96809f7ee0a1d1248a37f7dc5f5a1c22f0292ba9cb5813618d2233ba60edcde041aa5740e9143177e7a80b294f3fb8ce2365b1fba46f7db5e0e0716f175a26834bbab9e65b081c932409fa31b7655adbc59a8761f23fd461b85298b597af60e55c8f8a74c5f6239460e65679959a46458f9a1d0eceafd9217a9059f8d02156621dad1675b5ca6a89334dd0eb00bba5a3a918ed0c99c83dc9eb28642a71406dbf1bd951ff0a5c68493997f705691318e361be28db8ef1760f4495573e046bfdacb177d8bba57080ce71fadbd56fa83a3548f48e369fbf0957f9cfeaefac252150146db31109b3bec386ce0ef85e923b6d0b65fe622b1fbe7888133665cd4f6f745b2d1e7937df6440117cc6bf4c92b59fb7fb186fa2bfe96e0bc08e02b8d553576d1e29740d81321af4ad1d19503cbac5e9a303239e0c96fa0e6d41bb94d86a88006f13a7abbb5b76aad09296c90fd33ca1814c9d50a76c4713656edf25f6a495167cb4e9f0289336cb6b696df2d78677f6f26c91010321efeb0b5e77e73bc6d449667f03a47bbc9b35ba8c190fb2f958f4305967999be859216354a1c47eefc73390c72921a708529514621c0b647381cb090b015b3a6953188a342bec16b22364e1724e4176dc95137ad43717b19cfd161e4b22d0842ae3239dd5232b24ded9fd0d1d84acdced0c7b41d5625552959611e5a930b637f0b1108a52ee1d920a87ddec2fc180d55adfe8146b5a22d9d60a2ec8a9de2fe8c1412e2cef3dd6602cf1da9ec5dd190ebff6efb0c2753bd6260191dde8b04cedb932ace8856c6ce1123da802f424b840953a420c89f71bc37f8f213a1b41f6570ba84ff344565667ab499bde75592c8f377934ff6df4a2c4d3685465475f5d19bbb0bf8c907c2b05a68b3aadf6722794b10844fc6d1a3d9398a04872fbbe9e8a9c2457bedf0793e8b9a6bd6210296676f48e289d88ee88b8a875692088bf8bbd5d20497c0dfdf3577bf93ed6b933293db90ceba7d59da45abcc97319ab5dd53e8d9355746edb5ef391678c900dd62eb77429144cf7ba788d81c98a476428b12273535689448dd8daa6589d04e196da8a60b931b8d2b61efe1a637d884a96ba67b716627fa09bafeae308b5aa700e4eb86a1aeb8b72f07c9035546aa6e5e6a7cd39a67c0a907d02459c2f2d3139f0d606cc41b77b52b51b09e375086a05a2b1fc92219c6d6f22d56193900d4524c96a15eb341b7a7a5f2a8c97a71761a62cdc8197c59a77743fad27e339aff9371dd02afa7497ccdaec03cb9dd2324f3195e0f5b7355d60731b4095efce2a854074b0b6136bcca0fbdd5d8d3afb86c34c402b73981be51f0a522ba9060032869228256e6689a70ded1644079dd165f6d43f516c3d58e20b91acea86af7ed4bc538c154f7875b2ecae03b9db0392119b2c5430d409239d4c130dfc4fa713b2a6ffc0cf33aca05767c062c67768428ee18206d1512f8fc5de4c426130defc0a3fd080be2caae2a9af6c471a0053a40d254abed0ba8c5d64397500a6b4aabc4ce534eea950c09f25ba4668e95f9760a0c3bb402ca5a7aa0b3ed10b7e62f97082722102652b3c2db324519b03c016fb6e6835d29ef813d1e1e19f855b72344b61afe04ed05ebfdc467bba2aadcb9d60cef71575e621fe984517acf85c480471fdd1b55a30be90248aa488c2785ea1ee7fe22a083e24ddff4c80d48341253600375f72f80e966001a3579fef16d33b667abe86547a59df6febfeb5566c7fb950a7db46abd26480c2fbcea41fe30e64a19ea97576684c9411acab0d11dea17be570630179aa4a510338f62d82d8d6d31b881c61fbda0e6ca8414a21b53f905618d47e63131cbf234e2a2a51fbfb3d7cc51baeaf81330a3b67ed6ff86fad2cf3cadb27aada5bfbb3bdb75092bc6eed0f31fea677f236d08319085c7e8d01d332b45c50eb8d8e64b0c3608277e86dc4abb5836025174081ad7c5b1f1b3ba352ff8d09a991defe931adb7b0d6063742c848c80f4bc91611094e268cc7649be95a223b6856edc05e30d3b8389930be65189a22d189191b338a24e4c11ac84a1b6304ed18c6953f8054c2b5e26dd0f68684d062b06ae7e1c5bd00c1b5a2b28066f187bfa0d1b7b2a42fb467d0047c4e889326a846f8e96333a22a6074afe04100ec5dd700c0df2128aa0e4bd7df719241d794a138f6d9cebb7cd959703451488e8e0cd07b02be9e000c4b484f9a29b0b18c7c79fdc6a9c3e0154ee9717344b7a00752f41cc80505d193dcb5d6a61b12b7dedddc3bb4eb7c0a79988f4ae33577da0ad11b8a33db4ad19516d2690beb015b3a0515e336ee552a800b605e0ea451ff382a57ee1c6964f93fe58bb9332b4423f48045cbf6cbd544408465177ea8068caab8014d4e531bbf19235c5a6d6d25c6371db453ca5a4816ed725dec9012b678d37be1b273ea55a5eec141fb4d0453d106a3e8d2767541ad1e6c697c3eb205571f37ceac7e1b90d399a8c9567091dc463dc232f74e752d71e1fe3c9ee22c084beec6c9c0141c07cbd3eef41da0e4981be88c6d230d8e380a450324e20bb052b8801a783a241e34494ecf2fc66a203c7ba4db2aaa15e708511975c9271f994f78e98c86c498548724eb21bd9e601c00a56ba45c51e0c4a554cf47a7c8a19440e07ab89706f9f5c0b8e0d30a4ab4100b80dc1d8e5c04615ed96d582c769a0e1b88c3ff3e7caac4e9d377489c24644e371d5176f6007085acab6f575710fff0dcf09bed0b0bc31546f073d56d102d3345ab7f89cc0f232ebd80a279ce3ef02cc94133d428a2cee016efb30fbafefe0552e7eda8e46f7f0f24c4e387e10ee0fa43d7818e870b600365596798013071bc4a5802e06aa87240ef6c19731edb252f6d812533f81322c08f13bca256ced7c968c6e63f7456a4b4879b642fe6fceb8bbbfde65e0c8358ed70c7ef5f3a3a540b34c75012e8eeedc4cc7b412d8f2424c1815fdf2386ba4013240845b08970ca06e0b61e2de1096375dba9b8e4f0ac8ecc199354fe37cd269d8dc038fb43b737c4712bc930b30505d05a7efc57efe2a3dbebb712eb66a5458413ac2714a7c0f261360bda749ac4a3f305332cb75961c3ee7f5c0cc0e99e9fd42c5470e5959fd9e287db6c95cd2ced1db06ac3de71b5899b7225c506cf17c51403a6652580143e63b08813a1e84ee9cbf590c69226589006528124980a50c4ec2362004d9aad05cfff3bade72004ff900ee1b49c0dd6bc52b90d08798274b0ec144df2d01dfc488772fd701a9ae9011d7fd7ad5f538358c1a2d452b473fcc8172023d3f8ba347c48f90ed2637231b6650dd77514ee031f0943a3c90add59907adcae2182dcbf2518d5f1bbd41818238a864cf9c70d0d63c3ddd86222cf410ac5fc9b3d3a3694ba032f649f8527ac823253c2193928788c21c31a6a3195bf1448eca1c54f7725c04670034117868aed0a87cc1a806803f15dc9b1b0be3a8f21672299b514bf559f8f282c27dda1d5498f3a39823f0ac8b9632bbf163944a4155701d1e2b61013999cac463c0faa74c2360ca9df26ec8a02b1a117bba02e41306c52cae6e90b5a561fe4d7d66c221c94442a2d3117f2a4d73994cb374348edc18d6d66b91b2415f3018175499ba6a2041102158964b5e529c103af0f6a189a7d169c92bbb08b4006c6d86593280df41d893030c1ade3f3511a55f996c00a1478030ea60593cb17bcb417b589d18776f49c660f1ee3cd94dffc2d4c3a8381a272cac82940091e5efb083e4b942b90c430d966cb0f05a746522b5827becf98b724b2b0fecaa72a042af4692ddc1a884b5ec80e1c3fd14c5f34929c98694e3ef3943552907d90e8c47913d89ab0914f9f2c2e5c059c410d40c3a30f170b969e1752bc5c2b6a8646014e45c85806a56c3c09c30f3386069917c69410009c957267f7f498e5cd88e4a5cbd4a2464ca3f8717f4c2db1ea3abc3609b6c4773ba65f0c4c7c77df42b4a35e66179b52fd97a13f5fa01c65f29a317820c6b183a9ec2558021bd7d4d8403ff8239b7791bee6e2c9051454d11fc2c6532cd973cfb0c60cc23c0f1801520aeed6bd94f109a72be6afc9225e656cedbc27492662503c71451c63fe71100eaddb63f4fe69be4e55b2dd661c1c1d7810e1d6a33ef60ca8ed8d10a0bce5ae0e685a6c216b962aac687815128ed6d353b511da6d722dc9bed419ccfeafea861248b46f83dd5fc6e4ffd5d24e1f99f0fdf19ab339d2bed9b9fde07929de34f096c3a8857e95fb14cacaed37b2aa986febf6d964769a3633b37cd661404576cf772639e1c5517ad0da147c5b83c371a1bb129344735feb8fee4a0e48e7246943551aeb21c85fc6237c255eb7928d3e1b282ce77898494bd62caff71c098d08112680fd2f5c41a6e455c541e2436d56f613221b2b1c023a7f88ea7ef335a5507e260b7a4ee1e1debd9f1fadb1fc194fc16360b4b808cd28aa53b5a78b314b53c051c0e9ad92ecdc8bf48ad8f61db13d27bf395c4809d7274b5fd987fada5ede07fa875f63729feb1168cb76caa687c2a3771bda6143af57da4dfe3205b205bd2dd04b977960d03117ae20ac2e6b93dd94a819c6ef19a41f257102bcbde036257d05ee450884f6929a0c8db55621d4513214a4d7f2c19dfec70cfa27fdfd7dbfffbafced8d8a61a25b1ff9314327bbd37afab976d3e7ed9cb908fee2c33f21d21166bf95f39afc7a7a6fbf30b04abcb8065d8f8f5f643d5e80cd0a581b6ff5b3e82e8d4c4b6f7d8a065f81eb23ae69078e4c46c40524c5c12fa1e0c5bb9c01b4806c3d9854898a5fa93067823a088d9813f4de91492826b557d2405f53fe4859963f76864131f3dca2af917215eac07cf82224eec45ff4e03ace03879952d921129521156459230d6d58222570e066aa62929dc3739da080d45be41694ae8522c5e043b365062e5efe3da39d8e0bdbed3108e533e629af25846989dfa0a8b5ae4cd012cdf1c2f8d56ec2bd52603370f146c7a591ff98cca777acb7ceb8d47e0c40645bed63b1f97bfbe7b86d35056b38fa00b0207d4f2849521c3879b7935ec887f5a3d23ecccdd721d95532a3e80f924e40ee36f98bce7f3fd052449cf81f29e6bc2990a5e9c38901d69a456ab45e4a0e0382e6d3ee83ba4188103a33b62cb014560a7be500b347143ba965b670ce60aab16aab3a319ca5e0278927355b224d315238e56b742c5e7b7194733663e298d0798f896d1ac7c15f53df53e47e374ebaa2be94aca9b81ea9b1034db84889310ffde8003f697244a518073d875fc77b4fdedb48cc3654e66f9ca48bc155a52d632d4c94c14b1383ed5a781db47818582e984ee3b725813d5bbc609ad217fe7493b1fc8a0090deb67d9b67da0ffffa6fc031399e2be29035eb721f9298004c4c05efcfc3d04b1f79abb23aaa05bcec24250d03c470c74e20923801e10215deeb93e0cc819c6a698cd04f92a84cb70561457681816ab64b8a0d1b94ee43ada447fa1fad3380565d40f553090be8e130c38530479435d5c21e71623a18fb1f6dfb603c1cee8942bc212c8f155622d4a513380ae526ace371c74bc639599c86e1fb69514056b548c2bfd01472be1b32f8d55145746137ec212cf35b5dd86b733185ed392020e408701db55e259fe459c6cfdb1785dcff245010cc8282b6a95ec61387df5ac4389aa06976aad34e80f46f84d359e5299dea960291d3d2cf5fa8ced9ff6d178d8a1fedd5b3855dcb3d7c76efc88ea2ab503af5cedaf3040ea8f230b97c14496f7fbe5a1336597b154d367ab063028b17ff52b020512e1b920f3041c72d8947006c914633f506f14cd231b60a24428844a420c3e92d233e63d98924b4933a31d253348c3be477d8cb1ddeb35a829e25444c86c5a086135b3e16b8bfb17c6404858f899f1ce1d067634b78e4e12160e8189788a0f9bdedcceb965ab52a88fdcd087634eef8893b910c48daa02b50e655aad6429d34536c6d843b9f43c972880f381916aac7331750558d1371d0978514519a25f5a7e3a9c203ef1ec3f75fcb45bf69ab30f06e6f250c1858769bc0c1c5b7888eeaa69e98a3c742f97d6ad90da4b96832fe3a1c764285fd755f0f73c465b6d489d62b26fd1516c2db65ef292128f85350b70857be437e1d9fc85501f19ddd1a3671a107244672575042f52c34fda088a779a641da7fa9c801a2948d2bcb7f6dabc88063e9f34792c227b6c405403464df0b706091599c20dde2bdbc730c745aad03409486c9df099da4cc11c3176e89f8b880d7fb22244f40e5c24c6763964bc399180b7b6d7c2e419f021fd4cea3930245b9f6773ecf78e647b102efa5c2a6220db6a66b823781a7e4d8f4f6dc56c0161fb3a579c1fce6d9bfd4ef4434ee6cdcd30a3ba097e468f5ac5a7f0102864e31aa8a98d1153c4f892fb39909b55510940c43dcc98828f2bd097caaa7b8f9cbcc70c88917041e64ee753a70b00909346764d88ae5932f9ef41af0a4a91af00ed60a61963c2503feb4b298e33680b7f752fd6b2989fcbb0fe4cd655e8e9a6043e1645cce28598d454d058f6b1719e434755ed730a1f5af865ccec639db24243b5bb0e0cc86338386519a7f2abe5b958004126f903b3cc3adfc18a0c5c326f4c9908d13fdc46e448b1f230aa889e9e54ef7cec56f271b9746b53c96b1ed2258a96db84958986c83ab80f25833113851abfbdd3aa4982f82069a010718dcb7e9c6a02432759e17bb76dd19ade07866e3fe7be251531357950ada085840391731bc7338c3340912cb441ee128c6eba3e2ec444f8499698692e626f5b3ebbaba3a123b8e065806ebc388153a5693fde82a823ba5a9ac802342cbc8c2a38ccfd64749750b0b4f560f4418b0234c8d63ed0ba121085311c0191f88b668f79f63b644323429e7f1b67112e52d1918452b04c41acf1a88973142dd24da4d767275c78551d5c4fdf07e8d10eac582f618b7a15b899cb02debd005df96ce5a379f895b3792b204b9d51dd34121cb0d410263aa3f86d8d0e470c6e9d473a75bad3dd8901d939781b72e7378094e1544572bdc50b7547cbed1b73b4ad10a5dcaa860501a74d5082e019bf78c8be41b8d3ec38dc8b23fb0b5870ff8c7e925e0ac8d10f8a782e5a7ce72fe99c2e2011df1365c51ec1bcad618cab67ccdc72ec6c81cd830a3f8856c7eac5a15be7ed1c1a41b2e89498a5f13a38d108c28c75ee5125670c39f2db485c8f168090e40872319437e09e423d7b2508b38506b5613f2112cff75c2d18e019f297189134853879051291dd96bdcaf29da9eb6791e55b05018e415e03d0cc3da1b83784cac16b75f69fe0d913c37ceea456710d668e23b616e3aea022f4d9359f4bd592ae83de73297116ef4f8f447af911f9ef201010a21a178c1fb956cf9e548b23d6d1e5e1f1482840d0bec8ecdec88f8342b483c566881395a5e8f357664e85a9766f6a1f92dae615d009f0b6bf92d1c584c8dbfbf08fc08db87f2bfdb7798bc2d1722b1e21701154b109f705175f245f52ac467de4d43f59309dd457582f0120f9d1b5c5e65b2cb6dc0e20a52bf9b043e9073c2030adaae3c64ff00ffb5ac16d2ea0332eab40ac9b19ffbe68b2fe816ec9bb6b0a89fb3f93364ee03354f8e6d1ae4acf9e0cc7b2e22d10a8243bae41d3fbcee19b4e7ca87f5a730b1d8f393e158b7cfb3ff6ca28ee70fb8ce0ffdb0d388c60fc9dcb1e8d8a96bf139231e95945f1d2bd94a498eed689894d9c293c88cc5f8044a16c0c7cb334624143beea6f74924eb8664c4bd49769a415564fc4c2cb3849604895099f12fdfea6156264e8fca1bd4fdb8ebb5de7bc990225cd7a269b9dd8ea299cb70abe2c93bc90b355b3c99b60a40364064ba4e6d9383e6780a07ad0a4ba123b01673ace8f4ab4dabdd85ec6caa2977e8087f626b59258edeba40937a00f43280acdb9fe28a2ae53361f41856a01628584e02cad10ac745fce64506398a7b4241035168c6d7918e4883c7a47c8b0f0ea48f0fe6ae1a74ebee8069516f40c66772952e2dee3807c07528b49544a5a0aac4166bbd48257d29b9c6151da45501e852bc0289946bf49b4bc0c5ff15092aa806096539133ed3e05fb763a1968334cb8d52de7de04d17b33c26147860822b033aefa7df99d18d9b1ad61d30b6d2dfd27eab4c20c5cde4c583320d562dc0c37e29a74d6dc2d2143829e5803adf99c410e0202eb38f82831339d6cbee8046c9ad0d4668ffc368f2f417c30db18e2507a937c014024eb930d82f74f8abe096fb84b2724abf59dd9632e4c6ce056790876120af347f03d6dc2ade35dd573e7e6f301e849990f80071abf5ce9e5d0a0d17338be01c581cf37cb758d6cf525b5dfe18382f48ea7fad37825abd0cbb61cef9374901945f48ac2087b13d6fd8b2702a528a3f64e69a9bb83abd9f162d1c2dabcfee73dab21668e8dde52bd2d376a6b8c8083db605d703814033722d3203cc4956250f1b09fd1f7f2dfd3b2627b2ddba7af8c61d6f3f71f7be3abd5e7082127ad300ef1750e382bd8ba25458671cf4314306420a7ff204aca42dffec34cb1e0d9c27cac6168d664ca651bbd68125ccbb19a86ab58c641130cbb967d33888959adc594fb00fb27d1044cb0b0a16477e36892223374ffe480697e50a47311e0888e0a5048b6b65e68788740d3168d7e5600400aceab072058e69f5d318d965cb2dbe84b24d660197ad2d94926dd5053a00908c655031876d5988dcc5b025c40390f2072bfe0d7ccc520290943b23e045c51a2a5201782d46901d1962f0829732b2978c5670d79d1280a7f5cee7756b45fbd7c55ab581f13902e6e2df1f9280b1eab4edbcf7e09ec2fe0476cad96437921374ff352554f8ec6a41f7d63548497cba38d4fdce8c25df524abe8c521203259eb51fc6b5b55356dc4115ebe09aa962fe78a699c005525cf33137b3d4409e85be537f67c13ef82697f1bfabfee76da875b47b3db89cce289cb791f6287f30807b54e4e83f642910d4f67aaa0b188069f7b6d781414ce6529a2fd7144fcb7e43769a7f2e67bb8f5ebea63ffb1413424c9c9e23971b177e6bc74e4260099611809a291aa797fbe2e34d125f5c1d51d934cf1d8f5f9e92bac338abc48181d8ff1983857b3ac2a50fe24d2997c0e096e8244e34a9692393d6d068650ab4ba3bd37a39f6b6c0c215f39353f82c70269202abec1a19164deeb2f03d4f0fa9d10abb01b8921d3e76f5c0a7d7e915fa3b9d8afad555ac0c94d7c7b4c094aa55cd1c633d869b5fdfe0f7cbb40d214d47c881865b4626088d2816fb9e838d60515dc88d09f299d83784735ca8338f1997eef4a0a0db1c081323080f6d3f99baa3564a56b2e701b64a0ee5bf8a7579eaf30a670a11fc79df565025608807f721282440179e282c21d5c40f05a3f89939655c25e387e179f7fcefe94c996c34c1ae5b4d435a6ad59000dfa508291a8a7b6a4e5032ecbd6a391ff5411ec93d09f0b6bedc40e97c4d53c16b682764e6a7aab058ae55cc9f5de1c1188e291abc7b61ce7246b758e4a8b00b0dc94ca1e6e685220b8202e00dc8bd42d995855a4d1a3c9a55e7ecc89aca9994c68a2650caf7fe19d0f50749879b95de07f15a3fd3b4f380f4be2abf5ce8f5e00aaaffbd312b770ac2c0a4445ea7e5bf509c8b70f9eb3778582c769866637c5e64cec57fa4444c06327415560e7356d86c4fba446f5c23b645d3671a5c65e27158c6cd8eaf23e7da6de8af49b0760b0c41540cd271d68c761861beb5f05a022a3e28cdedd6c7166d296b4c9eb224b5c67ad1ef5290f75467bcc62b72f51113b0d74f9ed7eb2270d476faeeb492352b1bafceed3a3820a82e1ab0b6863ab34401b5cc21c69fb8fcf70db0813356042be382b3c41328b0fc3eb23fb4051f9401b0452ffe439dcbecd929058d1e6bd5e74c86f50ac0cf1a6eddf3098fef1d09f95d755abe75e83595c11f18143db2fb265aa4681f4707027c671a04ead6c016cfef1a3a98841b6ff48f4de90383a2aa2b2bb077f95bb300dc08ac725c7b4bccf7045ab535f98884350920cf84ec6d54aaee5005aea38cdef9c268af504549695f9484e65c89931cb47e2dd54fd21f948f4da250ea66e6a45eddaa64b84586467dbb5b2b6dbcd7e5bbf8b30f0fbb308f6c62c5d7ab9672a00e4c84ef292087a83de48ad29048353ada8449e7e3e80b4ba7186737b8e8d379b13944a1ee80fc27147ab509d2e79a455bf7fe6132fae14cff4af994a53a8dc93608a24b608edb8a5561047cef37643486c1d4bdbc495ef58175013146f893cdf94b756446e0a7906aa09c8330a09853f71be04c8cde8f6e715673ad18492222aba28c65a10a4e862b8fc998d37663e9522eb07211d2ee7807ab8f4fd89cc182fa98b0c6fb87e35da6b6af8dc9899c32e9417d332473a85c526a790be20189c2760e76ab79a6bb199a088fb82406415eac19f9bd9cdce712d4eeb6f8e2b0cdf9b44b817672729b4edabbaff34bc020fd03579effd6225b88b6323e43b8b22978cdba79169bf008ba0140a16c3eab599ca5358a678821731053fc5b9d7c45a38bdc6959ae270dd8dc09f2d28299131cbb347d37f38dd3507eaad2d7e8079c4cc89120736f5181d16a1c9066735164db6044b6882de0ff4070486dbcc3f9fdc3ce70fef4551b9f2205036beb8b1dfe3a7031cd94fdda66417410269687303fff3e8ca12ce50ae31bbcfaac92f391c157953471d537a0fab81280c61290e4130184d8e612ee609ea477ecdd055b80c9136d9de5da9e04153b5a5c044969c9610e5ee84bd48f735994f1b51a7366a5d3a649337337f14ef5d6779887e4f4909531673ee038b479832c22b145100b14e8710364bb6a15b5e0f55ba3f13b70fc19b572ccd89328d8f0f7477c327cd4a8dbcb784929be789bf425f8afbc2b89b44982d71301f39151dc7101bde94b13526f439fcfae20709a3d49624e81cc16ed487921fb6ec0228473aae5af9bdf2a997da9d705bb57bb3483f50386123dc809c33b5b5589a5af02f6c53ae3b5f5deec43a1a5385fb9409f26a69f65cecee7c790810b6da4066c9da9a33ed40f1dba7951b1a6bff9cb6bf5bb19227bfb727022f65a0046366385f278c88b30efb514bb6c2d4064b5e7df8124a1de99d8d94951fa4781c377892c7e0cb596e2af1fec729f713e6171970373a0fe8b2a07cc15a95e70fbcfa8a924a05889709be1743dbc89d445997b71af710fef22f122141aaa5c4fc9cdd219f74bd6cab9ed95eee4e68afb19765c67235100fb59cb6faa7e71d1595158b3f06d166dab9167b519f7dcd18c75ff4b123308040a9e71bf128a55a4b6dadaac9bd1765a705e404c0bb25a7ad42df4488adedd9c2102264c9d1fb3e99e54428a89816b90deb51fc42aae52328dfbbb0a3a23232af757f3fa981a43d3de05d0c0d3b367e7476c422979cedb0948143e61327fbdae48baeeddf9817c440231ccf7f94b5113c783a9a6100e120ade1474b286b25233dccac40e96763493e6abc38fd1b199bf21ed191655fae7e881c5d01b96e1439dd2eed9eb44d78d908b4534750840d7e0552a775fd9d3cda3603b1e46e1b863c9c257b3b1cd6491390e5a2bbe9e488e18f83556d01f658e1f359c6849ea1a9b2c21bae829243392fa52e4c355c0d9065a978e49d521e93869efae57d11972c26195798a9f1e96abc7a87ca3482a78d9a9d328bb51ef9fb24004d2844904de246916810db876d3757baea0774be0905a13726235135195461a13af57b7b9ff6b2f85ce18aa9a37eb27a54a20039f931f7c3c4ca928119e5c21efbe0ace3d19216a8706e1d6d180ae169a3c24e4ed1d3fc2236e207aef5bf611cfc79910d482451a83b5c4c74c8ddc8cde3299c37d089ab0cd86ccaa4fa214d15102245b5e81e202044358dc06b2344b7bac05ba3588d14635353e59c6a7c4be1209b282405a62c0dbf36053a4883ad704f2db3ed103782dc4cddc0da565de13c490fa190ecfddf623854fe84e668e9fb757345b448b2db6eec6d337a0689005063dc89754c1c937f88fa3fd2db0f1836efabb297bb66b48bdfb74f59710ab34599e151632fd4d1ef68243c488c27c49b42ecf346f33b7d05f6d79633d3264add091a1ff9cf8e5313581b582353b5737730a2652ee49a83609052d4a36ca30a70f4cd457049dc13a677ec7ceb03fcc93d2a0cd480a247c5967d9b3a22be882ef0a2d2c5efb1b061a51e3850ed127c442bb93e3e9e4baafa2298a70049f41b668b93ff5e711dd21f7f4f9c24d6db84fdf29e6dd22822a3881f68c1dcb00da4b64433183997accf134a66b2c63b04e9e52cf576a8fe9109644d4cab14ed0d60812058503a09953b25773579355e17f03e7a5708f2e0b968c980f033cd3c828987468f84d8a09046a74fe3d6c8e5449837c47917b9d3419bed6de4290841aea7bbdfb70718e6131914e7e62e5ab065f78968c44a06162595321c1ae3692642ef7acb2030b52d6571a7d3f8f0399ac2f13cd4018ac9c780b93b1e8fd24493ed16948847c92c681e34a0bd34ed41baedcd00b35ccad2efe797bb1eb2df05fb3a8a8a746c4a6d38b3209407d1c0a19bed56d1d1d3b93bc8fcb88c6f4a3ecfa43dc7c6686ccac0a585531f42af400feb2542b989bdf276bf27f0efd69e5773385661e6046efeb0e671f581a994ca74a7053b003845990e4dafac24d31ced919b28184b247a4c17f89cf798433596b81b380c9d52fa5cade950909461672d1488a733bf64c1114c65914dd1d60b28ef9536dd78482b9837a31fab3e3a0543897c7632cdf5ba266c70a7859a314e2d76729b33705f9b63bd581531d493c8d9bfe63caa589d5953c1cfb197ef4f648bf228fe70111921330aed6abc7ce0955a0eed505735b3abf2361d3c18905c80a28e8ea256b65e0157d0e35431c312266739b56bf501192c73d54bf3979836562ff2c0d230882996a5b2bc037d4c0b13846b13f68a003a5968491275ac270e048f9de9c614fff66d3b27cbc7b373880cc8dfaaee65d0fcc4e0856460cbd316763e78cb5eb26c48d341d8e66b9cbfac30297690fd1602f61c6d887b0e0d047c0e6558a295dc6217d82d131c9cf100abc7ed6a5f773471e2b0e44886c533d2468d3636c0729745f463735a9674352c01d8610c34c905b1a9bc2377e65c0a7f7379e5559a5227849a82472dabd997aa1799ca10e60dc0a334ced2700e22688456171c92c83def194293ec651eb5ca7fe504b842155cc0a4c96a4701f694c2bf31913e61797058068cd89bfcd92b01c3247f9e6e14c855ac390be838dc13552b2d7ff664e457595cd5c375d0a951d1fbbd56c3aaab317e8b89e8acebec4a047b8e002bebbe514b3b35c99c105b75cef2a1f0cb7ec64327c5321ad8a27e65b116d8482a0f28efc41e0c1184cb24cae00548e4c2b390c8854ec0495665dbdb3165abb1c98ce30e018be0f70bab4efaa590070a17e3116873518eecf0e565fd2543f9c4f7602a038d4484323cac5e2c985b1d2fe0369b228d454b1e48c1402b93cd5276e20449d4930634edc43883aa5418af20cb0bc4c1a9a3021f9a160ca6f95ecb23c95e28ecc962e134a845c7c015d919d2f1be249cf35615943a64c0eb841da8c7bc97456fe125e5383988a3b1613aee4a0c0fecc379ed2ab36bec83d5bf506b42b764c263208c1d190e64cec73dcadcab74363d3e55594546d3c4ce80349e650559a95a4ac872a8ae1d7d4b941cc0bcd8e3911ed3c463aee6e08915914562274850e762d14e839c15a24d0f22584dd7065a471b09fc012a5f88db1a8fc64154fcf6f2c0a5a21e28d336fb94cd4430037fa515522e3ab880208ebf3c28ca5abf1111d674ad3acf741e18dd0155f7b6dd151f20389b6692881bd7f651d6db1e47a91a801e01af2a4bd7e607a3815e19f7d3913c3f13967a7a94f0d8d6771a42fa3927145192111969ea8eb0a08e5cf81acf09d3c748c85201c2423ad4ac5761a885b221d3dc3d3c268548d318012f5062e0c7f2fe9bba66416a6e8b54d89443b2d5b19d8da814a3c317594fe90bc32a90503447d97e2bcdc249b54ff24f3f9c22db59823146098e402ef5e25eee64d7bd7e1ea190515ec222cfa22686160ee37c052d79fb5db7a7c73ceca70ba7c57b9ff9df2f916cdb27326e8b4d830260e4143b3b2464f96aa5697c4c22ce78b685f7eedb6e35d5c61645f993891846192c855baf1f27a17d1654cab6f0cdf5a92c42e94717057d590aadcce33d70adce701b4a85a8855a9ea43d9ab13ac64ac0f02ac2be9d2454ef3e0c0b388a0505eb5bece0340f6b5f99462c20098315c4c4f0b359ce20142dfd0a8f52a4b28d6385f6408bec024fda35c3e6222c1c21119029e689b8b120eaf31cd5db975e214287b1c0d77d6b14d87dcccb2add383ba2ef9c9db10cd1c7e6205c424d183d06e8d0815e708ecee1a0a57a883a2f11c59b2fa46f07006b2eac16c0424cc7ed99259a3f02a49d217e1c2923a3dfdb49974964979427210f63cb5c187742201feec3c552e02f3ba753ac5657eace354f08725688e2cd6fe08a29f9cff35ce91d77b9a6569f4c57ba99178b90e78639e809fa9ffb8468e2dc8781b2ee9783e79b2e90e69fddb1d26041926d9b6bf701230477f00555c85febb3204aebf09154ae221683bdfeca112f270f15f8a446179591f61f37e0ae23b9f6cf0d80d9ae3b30755ffb73e384593215b874570f1bb53fe6fe5de38255ec08aa23aa025a91045e8f0dffc70498e7427882a1b268a2598cc5239322ca469c44008b39cff9dbfe04ea4c05f4071eb34d6acea10c0543f953dceb5ee6b3bd5fa71f4efa256859e3f414f6c98455e43a8ef8f5689096043acd19b0c32aae369fee88a203eaa869d72578a3b26a21c910234493ea5d1f8fa6e90bb374e7b85634201aae0451d462caa17009f1509a057ae60c772601f82ff5d3c02e54da05a93ec09a6b2889bc38f543503ac9e173212121dcf3484d0ad4e40b2d1a5ba290806aff3653e2ad27aa47aa3df12d5432915994349f69bcbb6a9d608f35bbba38fee8af081763bf22ca45d4130c32a8c80aea4405f43f4f59e03437fd2a8770a122ad00e0eb49e31500a71d507e98711475c02216e5e37076acdc216a98746e7ca6eb12d1f19e741124efa4aa501601e8811a94636b1313df1166cae179fd12fe69e62182f80b3c14dd0beb731589ed532341a1cb8a355931aa65ac9c3e13dc9e34111716f19c0e69d2867216d9841b5ead5e1fa742721d437c26cda8aef364e7295e4a9a6123424ee6527fe166a14aa8dca3c9c4f95b3998b3a49b99f66d50109f9b244e9762f2084f4d7bf24ecdef7e1f1b726a72376a3f4ef58c8e745683065e1d0bab5f401ef311b701ec01a40a031adbe9615d3052325207693177d5b624c9ace738100869c0fb925a8154d2415847acecd17903a6bf52e4750042c726cc718e0e2062568b8261b8000431c68c55373ee4daad9f7b800f7ed4ec2bf7c3609355473d1146c41742f6de7b4b29a59449cafe05f305270639a8340885be1cb9408efa1de6a1d0d3061da7ff7d356d40fe6c0dd0ffbe096b4b2a4d5637b6a4e233dbdea21d8fef29e8258cc159c4431ef99d20af98f98b40316f600667de7298b7bcd53295423f5f15dcc077eaac9049ac51c4f6229e9a2a17f3df1d12162d5831dc21ba106e59a5940b9029c7419efc7968c27dbc05f3425c5ebf909787f9d2a6526663082d04ca7c21fa5d5e08ccbf3c0d7d14f4bb3c0a30fff2f3a3a1e003f4bbfc0ffd2e3f41297bfe7c584acc4387e119107d71ceb2f63179eb89a43057744b859dc5dc58582fd4f12d4ad9b843e948473ae66dcbd85bde1a432ed31384a0a0209f1a445395ee9c99c29131a23dc109933645489b69c43cf9a4229b49c4cc946ca0c51974e3c3437762a65288800ca7dfc0add8ae65cf94646248b32664ce1812ff264514c9cb4b398269b144a2697106ed4993277f39693287d2f89aa201d280b4293361d2c67720233003141d8c1993c34c2e63f298c97f3c947968c5431f439efc47a0b8c180627e018334e838bcf3e04da68fbf66103b68266d1c260763489b221c09ac06186cd2640934de7f4a215333a5492bd16a5e2cd1006bc09aa7aa1a30c80baa9aaf090a9ae12226a8608932aa9973ce49452460f9711b6943751ccfbfb3829e3d9f3f481fa794967449843174f568c4a0c5525e0dd99352dc2a958e90499860a92bede05bf1a9b5d5e4db6b80f4b0ce971874ce94f6b844585cc1f223d45732d9f662032d4e99cca9ee2670b1c1d85b66add49bbf6503eddaaee99715912991363d5a9459382031a0017963b388712dd689c5dce3388f721ce5a4cddb23e48df92824643256e71e12188fc3a10b1072f154f5a1a72ecbcd96ed412c164041aa202effc2dc8115245041b1dfd1a8d828151666144392523f1acd6fe887ba8ee675b4afa36dde0e41de8ccdd5fc7d9bb7812190a1697e5343e6e8efe55f5e462f2490028d9ef42f2f1ff3421ad16adf8db450e8be5eb017ecbe3e98480413c144b04ff4e27d7da2cba6e39b97bd50f7dfeb3857f3766f94cfebeeebc2eeebbe6e14bbf9dc683496cc1814887a91e334489b94d7e03c9ef378d6da90f3d8f083edfd5f96b0bd9b209ef28b36f945110f0feb82dea23cb5569a81b00ef4cec98f923fa948801b5b26b19d8b9a1c3af286bffc1979034486feb28815096405bf991f273be9494ba50815bb8b616959db7e721cdbc7221ed38929a259b095048c319e3be7eee3dea3a6997fcb5f75481b7fcf76974eb04706757474cc58066243760059a27df8a7e0afe3df83524aef9c79db728f0bc86783680485eea93d94ba7b16b97f18678c33c6b86464c4f2ffffffffbe49d838303cd053357f7e3031e8698bac6aadf4e3412b0dd5061380f853405dcdd2134eec8964826e02d2a4933aa5b487d548da389e9bcbf71d76ab451ece37685f1f7af21a4de9510a0e03c9300d327565dfd7bf62e000d5f0aac4830c87fb9ee8b58579e9401718f0fa70a0185a89a1950b124c424fbfbb1ec537c4bd8e4f183607ba6c2013176aea76c6b7b6c5509036856af414020ac942317ae2f1a1a99012f2e4a1267ca4f09123121b26045093da2843b2fd03432b7a9a9d7b47843edb98d960428b482ccf33476344d388a2bccc80c91c496ac91cd84c6d460c00403562356435acf80d0b2de4ddbd7f8d5908a3cc018038828550850d5b14d5b8b2fd6b4ca9f103801dfc94ed3fa30879639d48d95a8ad8fe241c644a34c43cf91bc1c316254162915e222144af0124a1c5118cf59a8144dec08c33583227c35c3123c9f6d73265fbcfc4640e7e124be66c30bff9a48c62211313d17726263c5925eabad81649ff3d016a5a9ccf855e8776a610a80217dbde9f20ab888191cf03dde9b7a308cfd6aa24004500a1a5044253239afbb8efb0273289a2f8c9452f0fa3789e120d8a60a2d708266d569a348291449fc44d10915a304e0433c25c216f44302da2d70c3923c90c2472660a894562491b237a1bc144afbf18ac3312abc492363436066762d2867b85469963ed10db9f7b0d795997b471273ee2db2186bcb4b1609d8d308616492c126b3b894562d998b940e16eadb5d65a6badb5b6565babb5dbb66d395b6badcd39676badb536677cb72c3f8ee3b80f4c2114147a19457cf4d07196ed7886b1cbc4d2bf6bce36b6bd9aaec53db8273f7e0a6410e3d975e1d90dc2b3fbf3310ffd6bbd413485310bcf6bba5692e0191e710dcf30cdd6fc660b9ed91abee2788667d6e65c63b3990ccb7ae88bd5f4c939833e6849730d45f5e005cd5bde72ce79c31bde72ce79c3db26b72de76c6dce395bf9edc8afe33a743e3e340da487f55e4cf1f7d01dcfdb199368fadeef009939f22a476e09c81c1fc815b82ffbcf51ec90f2de7b71a514bf9d98524a29a6b6ab9fad4f4da14d29a5f4dea79b10d2851d2072bcae0ecacb0fda7efd5a4b25d4113a983bd06829f8d41468aa565aa58966d0a2a4956416dfb2c2913974023429692e84a1c552a954aab5a6e0f2813bfdd28f74a075400c4e3048109af9ef81414220cdfcecee9de9e26ecb79cb5bce5bee9494b20c3a29e93451fa939a686cf8a0e9cf9f94deeb5e9db3fac81a0aebe3b1acb56ea7bc11ebd3d8979b624b4a62021bec20032a98d04082f1c5e803145b58118413242c5858800297163c3c146822082b341d6a28830995e37c587fa83bfdfa97d27dbf821b53da938db058a49452a2e82ae71c0b513024d0f36bad3c644c676475def44c72d3792fc6b6f3bcd649e7a4f35a2c6995ec2a273ad3e7b6a462c4caa65275719db3ce59e7acf98f48593d8e27a694730420d49c006aa2899f52ad0664e59e47299d3470a05802f2e47fe5ca2569a1c5c7e00f3d830652a3a1f284165b528102884d3f1e19a4a13f69b0e7cf9e26bb61946851d680c899813d79a004902db4f8b424f1bd78ce5903b245f1f1b0562aa33355b69af0bdff8ffbffcf6d195ffba2278997e8235a26874077cd9c755a5c6bad359760022dca9aed3e8072006abe9a1b35d4877ef14057e98eabb43113119820cc273e383825fbc4da27ee3686d0f8f3e328a1c7e8881671688d310e8e748c29258d44a31baae3f0123c87e1ec8b9cff51ee212a0cc3102655f4c90b7b52c671dfbcbe0099e303efc92b3cf4e499a6ea0620aa0852bd20c74802883054a11a542fcc2354a117650c25e5072dcaf1c9cb9524690599aa907ed1a3f0bd07cafb9487ae3cec5ea4ab94c77efb76ccce4433f5bb88fe0514e2bde88790efb54abf54694d4f795cd9508116e90aa44556df7bbf022af2bdb783d29cb5f6189d6daf43c5e9a2b4dd7dde0972502255f3cb201d9d68d193d028574899397ca544741ffaedb073529887f495c47b5253e53f33859e3f1e26113d0905668b9e4efbc5891627d09432611efa4fd0e399401efef9a22f0adbc1007d8d51404aefdda8f7022433e546d1b79463321c59ad562b9aca7995577995b3ae9141d32de69c39849ba919c32aafb81a19b4ad91417b8d0c7a6e31c73ccfcb5e9639730e59e5551e418d3cf9d7e001d4ae0d6b0e19c0165aaca9bfdd1a9489013a53470925acaad062cdf6afa12182cd8b384a983313666a0e2941e1b2c44ace80262f207736566bc5c3597c2fbe5d776ff75dee72db6ab5857ee9208056f019ad4084c673d393e338d26884da416270d0dc8f8ed0a2049a337c45aa62402ecca009346ba56d2249a2082c5c2b9518ce201dd1a2044a85a181ece06278f406fea03d590f99d30179f22f955c5caaab0b5102bd0469b9800d747f6934a0c2f6dcd39cb0bdf71e28c2b0377b4227ebf69886084d1bc081b20049f2d6715cc76d1bc76d1ff729f1d1afd00fa37184c55aeb53adcb65b758ad7dd19a4df34b8a43206389556259608fb050b6ffc8099adfd4df68aa64a97065d0baec68a9a8956e96e6a187e1bfcb25f7065ad748077c5fb268d1ba682a5b57c9ba00b0ab8c8816adcbe5728d5b13742502b5e8c125fa47f4faa74a7c2f77cffde67ddfffbf95ae564f140a856a0d5998140f633f320fadccae3c2e238a6e35827971d1a2900b34350261727ed1daa554d2ff4141a62474a6a75a08f2101a906c19ffbcc1e1e4fcdef7795dd77df7e4e582e7dc0a0e20a5960818395151385bb246852ba8e0a15b93aa9ae60e1f0254754194163d7f8649063ddfa250a82c6ccc22b1f24c44bc1d3676a034da6ab55aad56cb5b396bfd7fbff396b7bce52d1a96c23044914005ac2654415e2081890aac50e97f219c72031c5450b81771b6931ea85e1805db9736d29364b66e243d0c0c284ed20b49e63d1289f4544592f9b67f1d8f3149d5e8c524765b9b58b05fc8e712d1752393548d6246a351cce8a96a341a9146a398a7aa18d2e8c567940079b17d7e120504a50525934152a677576b9d4cbcff613f53d625d383a1e78b25397e53648e1ef3fbf9645f4ce6783f56216d9c03c5cfcaf6ff64dfcf179b39be29f2867f319054681b83630d8ff4540bd568aa93d8d66ea8e6daa21dc7d48de2960a4b454dcd16433520d22603f2e46ffd01aeeac01d3cca9176128ff4e48fc731040a1a6bf85e7c2fa5f776e34802f2010e0e0e0ec5c1c1995bb43d1907c4c1c1b9257baf9d73562ca67071d921f48d3535b3a646e6d0a7518346103aefc83e4fb0f0efb30e74186544013a0f4df9f098c3a8bf324bb7e60b640caac89729da85271e72df6d5b7e3c5af070050fbddbd3c76ff0f8c453dee327ff2ae40f0a753a994c33332e2e40de7a814d29278c8a88d0e21cb7cc1cdc344dd133e3fb359546107aca79637b1d474d9f3ec7d115eac2677eadb556d408a8191e5c8cb3873d5fc6bd1763db79b59248ab4e87871eeb21d61200162af13729072de78dfa9d87f2c675de17023f14ca0bc3ee9f2b9542336b6dd6343308245c40aa09a56502984afc91142d8642339ad2e1a1d9bc6149704287ae8466215a141a7f68160a856837a8a3748b210c1b08045c95411c9add9c730643169c6d175528e59d046bb7e0db037ab7a0f7dcd1c34390011e8e9ac854f140992fc918128320222af93920843d41396fdc9b7368169a91c6ef08574d065b66b09af1e1e383a6729ecd5c32d95e6fe00f3ff9e723f4cc59e684baaebb0f98e5d9f6ed60c17e0b795f1286d09b0f1284d0e25cedd47ef878693082d0fb954e9a25206ff85f5314dbf7a40ffa50428baf80ffdf388e9b5c4f192f2f8f628e6043801bdbbfcb1eb47910e741b57a90932173241d22533b9486ed056bbbb459803cd1641361141e85cc91d34433bd1663801bf1bda048c19d39863cf90fc934202905fd0a2eec00c939db7a6f2b28e8e5e58da0bbb702992ea3a3444f5034db342788667bbe6886c53780131a86189600618c2858f440441720f8c1902a5a7c51a489162a989a174f64f16305184838913501230748acb89841e5ffff8fb10e500a6c9db981961bb43d1ed24ae79ca552ac3561bed033063deb159fd2aa542a95acb52514d8160e8d26a104fa53e3143b3c21810f230d06188801c492297cd0e28a142a7f148ebdd7a25062f4f0e941dd6949da03e0d870c864329963eb1f8f4c832fb6b29752b6006973a54dc807ba6644a98f6de17c6fbef9de7cf185c97a70394c19b1a0668840571b61a5a6528d0fb6cd676e31cc7675b7c94d2838d080a50ab281f885cf07d5f6307034aaa88270a0a4a703d8ed4610ae595a0dd770ed074d770a999eeaf740c1d2ac145d6c167cb3e05bb434ac45da34b145b4959accc12f716d33d9be7aa1453cbbe2ab15b6e226f0bc6a0375e8f4a05fc674b6d123a265f28723b827f327c8d987cea7ae3a0eac5c2d4d12a2e8104ab6a2068083d78a71758cb10b2ff9a188124564560090b30f8ae6075d6529b77aa66ff1937b1637c91c5ec515d2c65f0c61cb79cc4f3f6de5f4f922ca7b9186f7620d13bc973b44f323f41f13d14b9d1ef424c5f60ff1649508f4414fde9ba62a44f3c39305b954ac5851bd808d5041b9efe974208e8f403840c040903973038d596aee592e25428b3e6e1103614b2b3944d9d43d8be8351fa5cd148ddf6ef1b36c1a41e86aaded712919c7ec1225b1d1c2dcf469e6772f55a1ef1e859010d18b40219d88b33d4702e40ce374261a1fded3d0a0d0cd9587413cf42f12c231d1e4ff5eaa3c134d06691ed07de851f8defbc01e29d0938352f5e244fb8b389b6f2c10494a25a52924be58a2882c4a54e0854a0334e0c0e48a2f9e3c91c1bf9bdf8ebc7978244fec8828b60d0f77f8e0e1e111b30f1d7a7aa23dcb9e3225b14c810410141c60314209d5fc924c491d70b14a62072d56662d5091b660420921ca606141453597206309d0163aac7e604435bffebda00ed78239ef3522c3923982673e9b4d9c03eec13dbf6d1b49e650fc6e1a51e137409826731c3f0ab57150e6295d174dd1d0ad7b14b8f74021a010285748fe4daa28e8628416f10ccffce65af17089ce8fbf9b14c457f02c3f7e29dea0ebe2233341d05512d1433acf6f5ee8ec5cb684fe0bfdf75c6811b585f8a3424f33ff07cd045108819fb5b6561e26940bb4f81ffae83c10ec329e93e68df6b8a46c4f6c0ca16b6a027093318e49b72de38dab95e36ae57a5c526a6ae408c67167671c7fe5795e08b5710664ea817c3f93572cfdd6bd9bb77d14e9de7b0fecc0d72147f976d480df76e3b82de79c77b830b64933b7238010420fdaa2076d33d4799f94d7274d3a9073e137f029a2b8d7e26da3dbf6dcf6f4f3ba8d524a73deb82de76d0b6171eda044d39cb52e95fe293de2a3c37f7e3a1d728f9868717e2efb3c57fe14e8c989d84ef2ec61ae6032a1cd15abb5e2b1d6ea68815e3dcd8fa7a120d8fdd318c19224ae95aa03834011a99199aafb0d8c36930e14f2f4e42b6a64a6fadefb509b0b10a5a3d6fa1171ed40bf445fd2cf2394a1631a091f6fe579dea42981b65bf4be19cc1c72de9041e6f830a7915982816ec1f609c45ce91bb6ebb0fd4751e898afec1654c48062a5cad967685d3d1efecb16bd9a37feebadf55a6b75ac9ba14e5a27a594667d8dbcac5c823063b1582c16f3c9f93f0c51a8d116bfc6621e933127aa699fde7b572699f229d29538d9d8cc17fa825336a578e8311c9c982bf682b97c4e1031b1a50344b3fcb2eaa4c0af20b67fe7b3b2d582a33a0a4797c743bf366cffd14c195ab4612cb4313bb3341bb3b219830ec310bf5c70001800a106d50b9309263590b5e98396555849b6e8c92aac21db96558e046d59e5486dcba7ed6fcb2a47aafc9616880168cb2a318c3270065a561902dbe2c3a06595213ebb0a11d6965588acb46c5965050320ee5a04e48563fcc993bffc648d08bbae8c903953c4b4e69c739a6297e6def9f2940c6394948a1878642dcbeb4ab8c5f9c2c137e27ca242165bf40a9044f682d13d9f07dd33a439cb50d6644dd6646d2a8142fb4b050f81bc60356a555630d8aee56e1867bce1edf6b8b4888771dcd919474a69ceab5a79bc2ca14539fa0d0678644eaa8479f2fff112c612e8bcc55a018bef74f79d1b5e41c438d1b685b344d3afb44afbf22bc220e5c00ee1d9eb2c8cc26074aa808a625ffe0270629fd80ca93e2e3117247cf5cea3e716b5afb6fc982bd9aece59572b97d8125b24d1b242460d7ab22c91c2d481426fd2a6053ad393e76da394629dbcc4eaa083cc99ff03319e98610558585554d2891eac50a20452c08acc308974368ba4b5a0c9055ab43dbeabb5b5d6190d80bca03e967a54baf73a0e8344c73e11e8b1ce03fdc74f6ead9452d6ea811ef3f07e3c421ba55ecc43ffe942daf807200b2dce18bed34a173247ee9c3aff5d34b8866c8ea5e6af55885d41912b556babadd552ebe204c7076b2da594d65a5d3838d24847730e496fdeb8b921053e2a6b6dce261c334701e40d7f12a48d7ba82b5a943f285de512d66aad35a554ca8f0e12199abe0ab200c202592fea07a05429a5292b853cf9db2a6a15b43423a2a5ddcf826586b62863b1274cbe33761aa2db4681b6e4bcb9b5ba46cc4e4d4df4533a46107ab8a8aee427313b8cd7e78e4a28d582f22cde0836db081c37e347cfa77969fae208dbb970d5fc66cc32d2dac41863172782137d5db435572ed77585d7e63c8e738bdffc589339d46ef1b045533b1e4d7d9f5de21d293862960848b4dd027e22dc405fcc40b7464f435c9adf88d46fc43bbbb37be5baae92ebc2e2ce78467a1a434b95dde2e11d6750d1a2ddb2459041cfa735a092fed287359eed348875472bf474595ad342591e7aee759d54d117a1061d02290bdce23713b4218186e18e3f6891b6a8de27fcf3c5f2d045a0e175658bb4f6b252fce4ff43afb4a8c83223c88bdc3175ab00ba63132dca51033b37d78e5f70de08dbb6b9ee78c718d74c136b4ba539a70f70e0c08103070e1c38240d0e4fe2ef38644e48e6e030e3c19bd8f0a079c86db3022d4a19c6987e0e981fb3d2d424b3a7c9cb64e8ef04fb230f75573f9a0774e04461d4bff79449996c0a216b72678d3583b3a44d4d269b4a34b15d0a59ce39739bb4f16a10f323701472e93a4a7a21913e7b5e1763a2f15e867bd2c3a0eae676d92f2d65eabee4c95fe79c7fc4bc0c1844e6639e33fd188dbe9321c998687e945ee64b2005a2d95ee64b3ff3a49f0169509d0ea8648e97f5b45433120040100003150000180c088542c17050402698ce7b7714800b62723e74663c950764a1248a711805310c8348c600430030c410a39431aa0e20ebd607bd3235d551718c8aa92791d51b3be54e6dcab6564a94dce821a7fd53f80f076c7b64e10cf5019dd0de5573a176599c504a9dfeb5e62ca62e5f482ce307270933d5a21da101be627c56a7097015b953d3b49e8b140a4b2f20fce815ac2a70b1768bdcc4fc3e2853787c02fc7d0c86095c0e08877db08fe49f514f3aaec8fe9527b0b7fd8039db8c7adc4f075130ba2498ebc974c84c419a375a9fb8dfd9cf810c8dd635acfcba116e4e5737d3dd91b5e0ee93d7f1287e8d0199fee1f4bc3c0dc859b7ea4f7b40a118874aca4affb8b1082405d538f8f8e3118c9c4dc4a8a68564bb1445f0e877fd462829de885fb331b978d4011e56004a6fb6ff83f62814f70f77c5c04b50c5b54b7b2c5022962ab0aab4b2e6436bf7a2c0c250216fe5fa7f6bfb7bec15a934d021c11fe504aff1ca980e2c48208c5f8f88682879ecd26c3c9e28812444950071b1a334f32b9d4af095ed5c48f10131906dfeee4c30263a8ef9c803021367974c2ab3a7c89ac385d0a15182e7f1845ae7b05616251f6c4c1a2c32f2b032bacfaebb303a880e585badb46e0a5e8588211c46e4960a9569266f637fce04de89f95b574408d220c455718753177579f7e7531dd11a32a9f21879d99fb3e883f0c878108375246f11b79282210defef477cd7f68faf62b44f35623175db00766b193962464925bed1b3b671e386fddbf61eb64fcd921c57dfefb3ec213539f080fd8bcf2478841cb2b608035a07c6093b04e3d348330c6a2f6062f70c368621fbf78fc5257b00dc5463dfca2ac217f7708614cc68f671f6fa42cb4fe73057baa550cea9fd025ac4445c2430f623a487584376a99a181bb3b0c0225700a982f82eb30942e048f5c21a33511819add08b0fc284c89de26c4c813a293250888b8cb62f72f79049a85dbbc1136d83a7b26f10ed02d20045702de0228540c7af483db3a66b96b70462d93e94b59d6515444258d55a91b5b68145febf38d648e5388b16bef71fd0087d9f48091ef50cbef7c46cd3b3a275df9d29e52706b03bbf86ef3e88f734918bf5f6e4566bf81ec28fe4e8337f2ccc23b0a6d89052f4fff70f67aaa70d961f3f2e80f4b750160dabff91b840e6fcb0df681f77b37c1c32f8ceaadedde4481f85d3ccf80d54d137ad73935c232d5bef58b9d394d2bc874ec81925b0fdc000b2e1ada860988523eedddc00212c38281f60621662cc65aadaa735ce838b98e5181f5c391b54e3dbc2e562d24b498d428237469aba32eb847636938424a4404bb5b792f6eadfad80caa812710ade98b86019c7383966d8d3e47e2673d28c660aa09176c1acf454e2f38752b98907e3570709f4bb08058eeb56bfcc811f182e8dac7cda147e41330df2971509024ccbcf7f720505757436bca927bf17c5c4c4e89a34b0b052101fc96289fc1919ba1364f29aad3ff318caca9b79bd3137a602136b91d974f5546f728bd28ffbef288ea384ee50368d9e978a32eb651cb45f6504e21454ba75c548c65af592d8e19b1c44535000d52ba2929c344c70701c26410b44c84cb683f9c245cf8a2c58a110e83d2c8f4084d52216f58420b76b915851feab3772352e3aee768a20d199c1ecba30527810f1dc9a6fba7484646b4d553872bc34c39da78550f7d02a4f29b14ec12e52c1c5d147664bd990698cde7300b939ab862c663dee5d280d5b55f7b87912a992e3aa2548394a97a76104af37ab5f79348375d55c6773e3ae4b137b4ab6df723af12231b6f4c12635c4e7116e630be909de6fe7291d1db0c9583ea06275f5909af14200ea3c3d0e50b8b6646967355aeb2c01a491216bb99622d52c6ae1fdfc606043c0aeee245130df87ba135f8a82f3101e77cda6801c39d77325cdd9034ba5d01a5688461700d61631b2047c913539a92b7bcc243997d52c04baa49b70e84f2d85d9bda04879c441e995fef27eada4cfd8ab6dbb18fbb94869e55e621fe0b50134de830b11519c864d028492b785d75c0727373be8a8cc3722cbf4fe11f8a25b69dfdd82c42d9f80a2359ba2725aad1ce8e67805f8e5139efedcd78c3cd3150dcf1f0d9dc020b3a27367002f87569fff25521a7afff2594765716420856e1e4807fc9be1fdb6cb3ba666d1fc6c4240de60dbffe4cf342c6a2e289e0002c45d6abce4e44d01c0e2f621025ba97ec0505e4c9c876056f66254517d32d15f73955d69492ee1b19b893905add9dcc1bdfa72f114d3fe1931f812ef302199099f04dc4ae97b0298f8bb29b94ef8588e29f7dbc424305d1d9ba51e4bd1d3facd03a05d71d02576a463d86f71843bc517e8e4b820ec393ee44c51f5b17f4aa9116ac356180665945f84108fbf7aceeeff3c8ed342041c0995d06418015b83328c8d8b051edaed4d14d9750c02e44bc833ad6f2f307e7545fde814218c97ece6d6d2c6fcbd5a6d29f09db828cbd138128f655db813a3572de2663c6bfaccdcbb506a81899815e8563d2282a4468ec7f7a57d669b527f84bdd08902c60f390c31e671be7202ff5c808bba44f562e011d77bb83799e0c249501ecc54ee11e2897a96351ee994f54da41c51595d081d3f08ec53b962578d3fa355e722a0f233d1f383f9d6f227bc3d29623615fcc95c4d29f0e9aed8fc14dc7e00cc88fe4f63fec7e5d438ef18c77225dc1176f068bc9611a4cc703a1252e79a0b961f80cac8d000a99734c48ade9f4f82ac7b53047ee78da5f4f1a1828b7175dd7f27d7a71866143393c2f594b20ef443d7527378057de297279589a96e7b342e46c74de6a926092a1098cc022321e9f64a902800d66a1d50e19b333ab0df76f80acc828c3ca2a0f641449f959548c08c6aa81d6f4ff4fd28707a3cb8deeeccdb837b957dc9b4a6b73c7eb012ec1073d6220a5c9e829605d79f1e4046d8e58b08cbf2995198657a409202616a824c9d9c7dd900758bbd11e000e615ec3935d26b363b4eb814de83769c9acf8e5759eb75639532055315c73fdd47ccd241a5ccf3a712d350717b52b2a76869621e6f9532955c1e0a3b8ea44cd5a3ccc49b0ed7ce4e36381d92d113653664a86eab7ce2c19d97312dc5e88402f1a64699fde244d076b15512724e9d6e212b903225ba2ab1e06a2b9af1c9f623f4d86fe619ee03c893e3448311f6a601df9ebf3c90de6c663bdeda79e1093df0748162417d416645e20e4782844555e1e222af083478eca10809699b4c62e3226ff1ed8e642efd05af8df5884c34e63471898e2a04de251d2933e74734b9c47a66da294f3c92cb8b38d40367619a891ae8c0c07f1537815af3c0294238f0719f2d1f9eaf40d9e91f1b7122f645b0163826bfe31defc41d55412a3ed99753678560af0903cdf7c573c98caeb8d03d2425bb36b67295a6f359003bc635df091277d086fb123e4cc5da0037af61a95039c8ebc8ab653b3f3f5cb19b02984dc3549590971ffb09cf85a6e6e61144b550375cf74058325fe7492bc161b50932176f9f1aeab5c3c3763ccc2249e86ed3509e1c23ff5d1f1318ddd8e1f951a7acac710e9ae4bc2542833f376560c299a23d686bdc5d46809b1e3d161faeb1dd8170d1497cc4cd790ab63f1c77aa8af94dbc178133760726a7ec12d80ed42aecf0dcba234c878636ada9d910ad00992e98c7de892ef1d9a1a61afedcd4fe1a5ac6813fb86860040e41b809a4a6ac27a1b2375e1b1902a12a9f90396b26ad1c0602f1efb10ca28ef200d5a6cc6a5f1c46abd07a46fa32194ecef2af3ccc4e9c34b6cbd4b6b7caddc52632a40626f9e1eeaa4b6f3566dc60c28d0a51e7ef14d7b842584750a1336835345849845aebb13502bce76e90d255f007375cdcfef003fa4bff59757f89f9381c2d97518dbd440773f53b58d1ae6f08873cac8638a0f35c177cb124dcfa94d28d344dbdbe2d0134bc6da537db839f2220a9110575d734d2c3d2d76f9ee3fd7d13cee580c3164e1ddad4213a8bf746caa350ef31918a7c20a4906076e7221570591d1c5b40ac12d0a34792e61fb6c97c17074317d3b2f67cee3cecb69fa71fe87aca961c96359b00a6e8933cba4c0c42315ac6c857973a3cc79f7720ed68c0b19afcf5012b56b0bac5cfe6d197da2bf26fdfe385412cee4f1439c58da44dda452a757ee88b45aea1347e483864610efc352cd36baa6864f98a857470be1c09d7c8a217ea76aa0d92b351ac0d6c1bb5b22d38177e728543f8d170807b6ad0ebc9a8212f30460179d2fdbea56ba86647a48cf38c18feb5806959c8234611ef6e16b14b03af73659f6ccd02e29818f672d722090cfb944ef65891ca00fe0b2f4adff2ae227471ac727d139895c3622bf89a805a79f2a1ad6c83158ba0ca680d500116c80b32944b658c2c24d1903330e85c2e68a81235a99e83c34f075ddcbdbf262d89ea76dc642442ae62f639006fd86260121efa9628da89a04c9cd77955b259cea8041f5a73097151e82492d425d89362a7ee4a7d520ef2da71283df44faa5bbcd54198f5398f23c33ad461a484734f2910d6fbc1de9e3bc2d496970265770f46232b303abe4950e921789a8108ce67f260643046fee6e7429a9177b8adebc064ecd5cd46c2c3cfb7ad0e24b87da4566980b0ee4e828ba85bc6f7bd0870394a05b12f64d01ebbe3fd8b1e4100d60fb91eab520f829da82506e03d9b25164c910288541581e76c43e017f80163e55f39a6c60a32fbf8053862b5860706c9d6bbb426b34d3fbb9ffa64fe79a5f80da6b84dc707f9d525277e297913170237d5d42de2805b2c65f3a21c4a92b3341811eff7eb0c9f7321a4d5b21e6706c55c7e0407f4f103bcc37a34dc326a2027d6f7b5605765e138365310b00be8781a566aa93e3def246300502b9eb93c5dd9ccd572344c8bc8052d1c00cfa8de02b56e45fce2a10f8f39f12e302fdc93881d6fc766fc342551f737c9ac546ab6dbe9958040854531e165c153b9154d2a7ea827fc0fc33215de9af328069f891173d07173749a30aab5f31e2533a98248f0e0a1b99f5af29edb3eb70bd44186e5f53ea2d78a51fe611dd64e91eb17117439b8360d33f7f5005b8df42ed13097e1aad34adcb02f66c22d199018758cfd447eed3414bbdf68eaa96c0cd5520085a49e525160f98abf81d0edee5299f3dc6ac0a6d492bd9761ff7ba86145179eb869c80004f582d4df9d29ba41bd4d9b7dd3c7dbb098d6c5527faa3ce14621627da5f140b196876b65f6e1c6391866ef62dcdca14f10975bcec823bfa5e9c9c247efd2a22fa5a31b04f569e318b6ce64f7c134eb63fff8eaa4c153e30857c5973e49b21e3c883592c8afaecebd62cb85f065053e16d64c3cf97271bef7a9157038f4c669d7c7d794245f49b8adf82c4da7b21bda4ceeed1189561cf19b3ca6b9a3c60cd84d6560610eec72036120f7adc1b84986bb16a6003a265f9f25813c3af63fe0cc7cb48f29c6168b7e26a0b172a4235593e893879a90a8c567b5676569b414be3d8da9ec1c108228ad50779d0e233704a9e1135a40e563b280906726126b216ec3e8bda48a2b622eb69cbd01db3b742be3b49c05ec6d775a94eb5952488b08539773d44c7985bbc41eeedcef77c9b842a5e6eaa6c8175c8569926cb75b4a03502e82bcdadc387fe8c43d6884f3ead40d218fcaf4fb7bbfc8f5b8db261f327b521d365b6166eb0e2e55d0cdad3681f32a942c2e1bfc0d22e46f19c23783a2bcee008855c94cfa13fe543ccdf21cc0fe10450b20fb3ff079c1214ead10746c73fc1e10b345dd051348e3a2ca5c1507181b7a0c2db5e97d813bd6b6c3a178a6b0a51a9bdcd24b1772366c1f10544bd0da28c15f50bd6d09ce0171b72022cb8bb5af78a3c7e9b1b074135e1bb4e5e2b67fd0f01eb7500d0b9eb4486219e3033d66741319fe0bd4484e54f845a8155c7e8f1c87bda8cfa958c8d39ba4a4de062c3a17f4375c07801b682de69d8286acbc91ac943dd1edefa53466e6050dcbf50a1622de008614707086078b6d7d7927c798892fba63cedf29f27a9801e578433e1da7705295baae73673330d3af02d8bd37f678305bdfecb53747bf545b165165b92b42c5669c3ee3b4301e795c73722aa8f95c2012ec285d32ea34ee1e934fe76bd9a0112b56c36cd8d0a7a08485fa4cc8f6c2dbb495472150b5d154be1882fb25cc32e8adeb96a5c8a39098dab6aa613eb89d839afc9ad739ad6e92e897d5607445362288c2a1af27ef21b87debea04aac61c862ea3a7dfd6867d0669bf4d42b04363bb6313db7548e99f693cd9061e9fa55edbc96115148c57d3331819e2368c0eae571a4c0f82de830f6f60172ee9ffdd8b0b344a1693c5e8da02d755b581e43897598755aba64c48b49eb1984bdca17eaf94ee430236f636ae764811dc3aed210a782b20b3b91f4366d9eb648256c342a205c1e9ffad9e2d1e64cf7dbbfb1e76cf385c2c3fc095919979fb89317ab23d60da4f2468e141a0a1bff7de8a0e1571dcb401ca63a7c7b267c795942b6e8b38c88e37fe10f33c94cc5ac055df7ca5aaaf82f68a80ddc0378c49d12c088847baa8827413a1a245d5ea8d712a4a5258c54a38384441cd230302e3805ad928a9664f4c5bf527426560f2009a6b21ec914daa411e33a86397987a199dd07952e5f283f57726984e0e30e524a38bf4a6b5787835016677a185b673efe12b6c658e6f045409392356b118835450c72bd8729cffd9ac257cbfc5a584f1b44a1565e43bd7df63483da1dd2f2e482834bc38fad800be39ff127048af019812ffb853e5cab853f680d18f5a2833f92df0b4b95217b97c659a2c030b1429acafc793bbb884376c17d22f23409f0f08cf609f0b7bb232aa0466233c16a14fa5aaa8765b8b73188f51bb6a519e87d0cc06822e760c0c21552a95c85e22d8f7ec0bec1f11b93cf2c1e61c6f1a2396761383d8188ef0e8f66a1ac3ab6fb0e4400652e2e56630bc4c0df68df2929c8502dae36492705075f3181611a071dc9b58b37cf1806f0bd0ce7892f7da028c0c8c274b6f969a63bd654e7daa59bd5a1c21dad54262a013941b311ef0823eee6f1fce7f2e2e24a2dda5d7058a7c5ebc6c735c0c6354806329774411617227f683267bbf96741853cf6d4e5112520b30becd88c74c5e1f9420191a7f5f1d99f28f07e58803f0ad2a816fc9c4e4c155bdb74dcc6a93de49d47e7273d83b994720ac55f11cdc1e211118f9645d259dfd9e0ede232492d233ce49710da6d392708e8105a1a53065658a92ab8a21e4468638b5861932054aa35130307e05579eb392b8907e1b9b7c8122598067e61fd96e04964672be3c3bea0dc3e25a8233f757b8b1af88562362fd103b47d77e47824a05469ecf7057b1a68e6ccbc16d1a9563cd419988e4b0a8b4a1e6fe61e82cf6120b13966159b1017e61e19d2f81be5bc9bddfd12fa10078e0412a676356c57252fa0bc4583c458aec0e199a1ccd452cc7d294dea9dcb0c8d50cad4aeea71b7e0757856df4206b2fd80a956e73cb5e45679fad8be5541b16298f9723f16718e8b27473395cf253add944e5401ee3c02af10abf3615399e8674672117b3559e20805e95535ae1ee1ca6b9b45894035352e4f92fd9637afd907b868c4e48fa9ea87c2357cfd1dce2256f348605b41409a4bb2a0c0c744d8e04b18b4fbbc9a973c11d7ebc9c3478b4101459cc5f88f728cd4f4468bb189f5b41a4935248a9b54d96ff7b88c2a63ce119f6e54b6ccfcfc52ea121b5565df062c30a44dab26be53e2bbb89c4cbf67e6341e013b5ac8c268d626a2895bb87af4a45db5d143daf45019e7bb9bab3915ec79af5d48261bc6b2f07154048e51da648108c464b2973c031dd1efa4b17126a11959f65348ae60ffa57aaf9885520cc53ec5cd45bd24a4fcd9559cc7124ebddbae4c4a47ca895569bab687f2ce08e963f76d0a2bb8a290e681a9941b5d95da0546d50a220ac5c58330497fba3e055c92ac5e9ea47ba6fe2588620184a60c64586565f2656df23990cb2aa6905e416d6f03264f53630606c96f843d1be4bc16c70caa95e01f17bd609bd7c1c7134072ae9c9bb98c2f480afdf8704d4287a5578eb83fc54a75e4c9bd02072127df62f7a4abda593dab59a68201c9c62e7ca55c71583c6eac5f387a6798ef9ef4ffe20ed0fdbe9db2969e1238aa73c6572e5a041286db9b76a0840c06e0c9daecf7121ec0a7133564fc03978751ae4395fb536ad7a48a8dcad775c8931b7b514fe6d37b5877b04a2ad9b3bda5c65194b4f550abd733c0f03e7dacb3957702c3f135470ee7bf55398747d31f55ae875b8bcf00c885b23d92f090cc17d7bd65afe77e8a41a6832a824e41233cab1d1119165a00fc5494556d5ed03b80fe9ad0ffbef2ea0b830a430f9f8664c60c995401ae684e18f9851c7444566bc5b88e5b34dd4e581aff5cf93f4a11bcc9fa43ba0a4bbd111bb9624a582373b23cefb1a0507b779a4088afc2a630fbb9361161303c74a75af79b541ac8ec0ea6b88ff14aede1277ec8fdb44b26380b209b42e200da83d7da344ddda3f7388561df297138a0f6714c6f5e13d858f6313bfa7f9ce9ab111eefb31e6a776668f45eca2dfcaaec43df9aa9f003036a6fbf8aa54fa5d6b52d13d75d01d9bb8ea144c29cbacb304392177ec9231a0fddd5c66ea173decd092c6525a486f5ba6a219b7bf79f497d3deaca34c3d20d6c83e050dd829c01a66469f469822efd349d7fcbc4f920bb1683083dd8519d02da3135bd56134f49f07b9ea5f0bd04dbd065955fe9bbb329b971a4008089c523ee7bc5e7c7bbba9ee3c159d90156655ed1f69c8fe3f06430dfab5bac2216e2a9b091be77e89ffad8b7e1274c2eafeb8d78a4e05c75806a5df8918912af742ac66856092a41d398234506afedbfd92cbbd282ee1acc85ae53226c68a8498c894c479e4ec9d010b80124257636cf34caf56a16b5af5bceb3b7c6e189637c24f709847cb5dde39eb26287e590d0cb4a19f6ed4490502654a6179d1f24bf6f30d1c7b1a67d149cddaede626d1876a93b2d43988944615626eb4934181844bde9ea671970fd9f16df6d9d2ec7c4b0ec1e27c6acb830824f2b43bb8aaa416ce43142dce440eae2fe176a2aaf5e49ad2fd948a686471879614ddf4eef62e841d3bd030517d9db15532f0c4bd7257f88a81c18da23c17781d528a8cbbe1a40472195235fe950c8a1a3b65aa1b1ec8229565bacf9504b3ef32c69cfec62bed0712b9085919754c8d65535552a1a002c5eb8f180efcbb5051d855312e40f2eed3cdd68176629bd39aeb823b034f07d08004e8c482316f0be7dea02c5134dfc6779831026a0d324820a360c6b89bb4fd9bc8e2c70d4fe4bc9176a2e9f70508b91a6cbfd018697608c403334e3b8a05bd3cb0bdca30de368c07e79d9501804d9f996e3ecf9c78291980ab3808553c798da3ad71e0697eb0c9d6cbeaf3aa33f7c539ed989fd4a8e02e7229141849ee3015cfaf765961b16ca7ac7d73735dbd171bea3f94a601e375f5c1fd681436a2e76b065eba1fb22d655e484dd145d73337ca35f0fc2540bae22bad92d0dce064b1f9b268c72883819254d16c6ee533af5561d7b133e878d05a6d4ff9427d8d2033a65ab1472da760cb5e2fbf761a56470ca9e491aa955716cc59376947e1d3e177a3b422d1081c66fbfe0a09a2600e33f4d43af7b1e9fdf2174e2a681b374d2b94fbc7731999f06ca9a055bbabf71f5085279ae7e273dd343e3c2fc3979b19f9dbdd6ba5d5b1dc002b97007a7f63255a44afc920787053643bed61c86fc21c3d7ea9a215c23e9407001766204723f17a614137e6ac4c49efac158a9b6937a8b3b8349c96814c7cd3a423faa141127a4959493d9c4ac0afdbf36728d25546256f9ad12a9c86cf57f4cfb5419a699a84677e2d289102eb0eacc75272b47cbf61ff02f7aae149ac02eee6e486b90b2c913f7df049744f36ee9633495168325b0e38d2b14b69205d98dd70016bce1436cc1aacd2fd3a245c508718246d4df4d63223fed6b5c204554442d84282d50e7d3cf5d75bb55a09c73c03581c95b3e036faf30d1116a8693fcda75c77db6f49102e09acf88eb764da48fa67ecf4b3fa2c0aa5bb42da05375f7c3724eb718c330c8373743ccea1a5e27490d90f8b0c98203984e9055cb449c7398d3a4fcd95049413b4812762517e0e7f7a248e9bb62be08a45d4839d98b6429e8e05ed80db08bf80f4cf178b3cd01ec5f385aef04b3cfd8f3a651aa97551a5c68b5f22646cda147cb05491924084490d65aa62a8d40f35356a510258223431035b84639098d08ab7a32d1a64f3d3f52092c72a6bef56f3d77ef9f974a6fe2bbcb9074f7eb85f48a400d2f5f4990c440ac84dbc9d2014ff760de64e7c537d1db240e99dd1e8850eafd7147a3d0228379b92154b4626faa607c7aa83b57d89f2bf39a1b5379a49525b3e3f88f2afe5dd5e52e2fa340302ed00329d58d807262587da641b9d586dae10f1dadb6da2f45c5b29b2377c0f74f3482b1de578deace421fafdc45aea21b0fde70aaa371cb9206406d78316699c549048848a47136d36ef17bd47ba3cd2b7470b1c231d4183d61bb8f5a1fd203e38fa84a152265616809425a54b335495063a809e33284068535af81dc9ceb19afae83359442079cf5bbbcb74ac6680516451b0ed8f891a8dc1724fa5b7f950915592278385d4e5cb828f92920ba0097e7938034a2b3caee57c16e6d6bfcbc1a87209e03967e440fa185ee85fdde4a083354f164c427edd2274279f80e150f9b370401a960478c8381ff13bf157f10bc94f62815ca70c7c0ffd59e39ac7bb7ca572464396e186338ce88e8907f2792320585fd68afd45a8d8fea29aed3b9c81c1ffdecd6339ebdd2e159d4d727f3130fb68ec8bee94c70ecfe53813e60f591a9ae2ac2fee5aff1cb84824ce10778d903cc3de37725f36afcf9eccfba08701334310159ae0442ed416dfd51be8613bec3e8fcfe2912be7bffee29a0942418612d7f50675c2d24f30a14ae9c0eacd62a3911994ef8dd32cbfedd04de9e26b2b733025a4dd937fa2989ac36878bf5b4a72c150998393392390b1502e1a29a8ec8f5ff0c6366805bad9e3c0d97c80064eb2d5b955dee05a956605e0173d094fc591515398c146d42ad0f29f2647ba7873247cb4ba50316339c02a4a30325962e447ad2a512cd0473a367a79de61f4ec0a152c4b50b429a6f4f2aed888b04010330bf2511011fa451a25bd6c731cd2841d801175a5e8de3caa24f50cc43ddc862f83d33fcce8d4008ee055d2c3357562e4e1cf6ab38506d7232c66a293a1f4025f8e9a615856548824c69436bba9d79c14859f96f7d2c2d26b7aeef8efb4db4c55f2fd89ea498fe0aa9c10a61a1282ba54c10ccb46c144a714aeecd69e1f9ebec041e4e18fccce4bf02b4089fc67864fc18ee108231cb855aa524683d04e7c73b069427cf258fe7acd5196c32d2a6f66dd55151c3bd80b0549c0fdd462710b7abc45d141cc910782c10c855f590136ecacf1530fd635a70ae6943376f461a4fc2c786346d8a96f7a4ed1fc49515fb8ad65be388bc81f75d1bc07dfaec0c041d0def06bbce1a22d1f1df40b6680f0dc9c0e064c69b78b8ea2445362b4caabfa4ab12bbca983b8d83ecae9f5b76d5df3b21685b75df3efad88fd29d094daf9e63d237819353e0b2054e86bc1be658df7ab5099f2bbd607e7071e9c328f09ba43c8ff942b352ec013fb82961a465fc43025629e6f0154ea8025a8f9281b5e561dfa9b9873e517cb1153ac49e820eeed498036f71f35380e80162230085e427aecce36614951d6d8d56267acad80d6965d9f82c9932137e88d5cf7f6f67b0e197246172f91f4909a43f136e28fd49d942961ca30009c1a20e8abd753bdad12908454c4bde0efef65b12dc4e368b856e742c4b2979ea021f12d1d2e4cfe44d8822352f9367113ffe25fd6601e8b06b4985bae07bb69ce2c1b49eba02106f26408f1cc4a4164df582fd48a555d5245cf2a7fe5724577b6806bed037dfd6ed2750f06aed9d784e5a62c5390862f8847dd4eaf728f1bddaaf549dc8309b392c4eef8331d0e814642e73d22fc35c117cef534077d2b9477447f11e6ea6f8960f47196d07246f5e478623e3945d48e7853f688570fb016d4bb954094dd0982720c455b174a66303d41afce39ca28d5aba0e3e96d256e9f28baed12d9d16a47cdd9eb8787eeb95cd4c22d7b4fe9a6d3331d645304fc8038034b53585d2216617366d8275fd9aa7279012f45beb92812624430779cda7b7a0f333fa915045c2acf254de1f4906b17932716956ee6c032e47b61125edac4aff728789f439e58c08a2639645382799615d3ce2a66e5cd2a0d157e60d7d5260b6d992a69b3ff3d416f3fcc1a07199013431e092374dac69eaffea68c714137c1f802ae1dfcd2dd8a461b25b4fd409c2c49bad85f15792e5a012822dfe3e97ac41717f19ec810ea44d13195ed3fa393a9598483108a3a9fd1d59d16829ce669aac768ecfbb3e2097a4bb6fc869c5f02045d2b84f746a44cbd6a9180692e5575d0608ae6155e8736fad000a0dedaae147b8322d4ffe60e9e3f101daa7eb53b27fb03c0bd6571e53d06456797821e7e9cf3ee0f74e11d7ea0403fbc5c2a81a24f274f20be08143f356079a5234ec5f3e574785a1f0a1b20bd498e3546dfa2a30fc114cab7f92d1b68182c06f4cb05b7135e1e4d22e65d02c0b7f4413947f9330d5ae711edc7103213599e3fd86b8d9564fe63de4d0e5aa33b83a58806ce247c45ebdc43482435221f4b19e6eb94ef6a75e01a0ead12368b609a79c5da8c5bcd36988c5d461b8c93532888ba705e257d80c72aa8a7346c5a4a3a70eeae2f5144183d35dd6cf1b98560da315c26da50b40b1733aa005df964563af47af1a339fba68b08c2a79c6b3fe72e24ae3701111216b9109bd69602092b26509eb4e11671ee128b2c8d7a110f452303f6f8bed62ed2fbaeeb4702765ad25303f8223ace36fda70f87ec45719ee6fb14ebf6bf9546e4302a044500b24322999f2a1ef94141b1baa50aae07a02d66a4f3eb3bb5d63d771c205ffb6516e46e7380db0eb4ae4936101d538add84d5e71cf47dd097a9b7389cff9fa7302dc8b9637c8948a0c27e02f9dc08e8c3279c2dd5c6772af2e564c3c6359aea30cc4b4f870920d9e8cd0ec82544fef86eef5eda043aa1454c1d95bf5523af4c83c3540a44cde031cba846a7f039a7a568e546dd4a3b765a5eeacbfc2c8825ab437033c0c7224114db24a101180806442094a0a972aaba04b01f5220d154f5e0072222f2f0475fe228b2d6122c516443af8634ba505c9b87882efc79ca4644d7180fa08b78f6d2ea5d1dffd507f3074cefbe7c2b9f1209d436db68ed711a1c228ec4c47865adb8e91d12c85569518bc95db2a93fa548d49ea8aeada5be1ef5db2bf26ee41a9ce020a506c14d4ac54270217389c8150924efa1a621b278712074897e2fbfd427f9e24f9740640dc4591e490117fe0812a38c9c212ab3295d17cb0a22d2b1940c3915049225090e62beb7f0653a6b4c254bc1bdbdbc9e3bcc2c67395f444c72f1134ff75e7820ec5de3383a84822067a9b17428ae30dc2964854b2208e842c68f0bb5f35b88c2420bcd72f5b126fed5e54fb31445c6b4ea2854ca49864fb887cb932ca296a7b828be4a4918093709272e44166027eee670dfa2e24b9dd197f5da311b7ae1e0e44fb0548a19fa98504f53a3040a7526bc2211cc6c99ca4f176105d5e938efac5a561bda53e6132e2fea8bf854d858c54c43c1bfb3c89b3233e123b33a80982f3b1a5d028f868b122675d2ad163c180afdf34a5ace7c870c8c3071aee4271914792fc1798a6b53d4242da40e82e22170a326abe8ea05a4d55085466d052d2f35d0ffc8dc1af14a998ecb8a015dda554c9cfad2a4c821d2a6b888821a7011e9491e56af4e7e7167b1a2df1e016f46439386aae0cc36192b3dbf2ca953c07c99e89b7546f77ffd128f0247775f28f69667a86438e85e438780bfb08512a08c417165641fd2a06602b850518325d00425e5c6216ee448e0c9786d258424a9979ef6119046aa2ff3d2deffe899f616b1112485a5784f595932c4dd4989a327d55f712b2f98dc4ea2da8e3298946baa8356873a22d57e282003fbbfa556a4d96e4c2c46814e1a58833aed0939acffdc9017bf8434e3b1d01055de9845be3c13417fa9e4619c4c96e6b8d1a262756c1a54dd9a43d5e7a46a2064e144e192354939bf50607deae599356c6af7154b619ce06e525c57b7a9f2d21f9d018e300b5b5fba2eb31bfbb04a32114a83a27fe58219ddabd79046903006166090124dc13c7ec4e1e2a69e70f81002b1e03462bbd73f79d231199ebdf3d9dedf3c236494043fa87023c6506f6e42f5a714f7768c5baf6ef2937513871554ea05f19106bc34b0bf4b03c21f3737fa36dc88c1a66df506252fcd4dd61e4d4f182e5e76bd20fa3bfb2ef8e75c206905a8ad874f4b6e4bf40e9fbb41b785572e308193404e20b764f72699032fd9d06930d4fc49d2b6510bbefee9cf056ef290c835bde607910d8c216e11386f67d26314487c22660487355233c6c42e0213c5c45ce6e8679be87a7ea4e6ee0cc343b1603de47ee0b7c24a11e63129bf82d5fc8c30afbc41341002b687cbf854e45135698723626f6a6d8b6c36159e07a90051fca4d8aa3bf7fc4fd79df284c23cc478893b57a89e74aec937b00bad88d9b10525db93edd330152392e1e1fdb1aa83841b9c212b3754e87b38e8b4b8bbdd93200f854011f4e8ce8a2e2acc44009ad4172df784ea0c6cc298f24ad52df0230ee46cf984581cc8b1b365e90709f193b2e5797b0ee67676f3e09c8cc4ce163c5e4a8b0984215236b490d0440e0c140cc72fb652555f7a360ec882f8f0b7687daae36b1fb2f6fc4911e1bee4af5fb291660b640af442342053a1148bc7eba71bd6f0709b7f6db17392f5a0fe6df1f15470b21f761f13394b6484f9ce0fa2b948cd7da4e3b0605a8ab60b388bee517f2899d513d596dfad290a7c20d49cd43149ab14c70ab7fae7b8096d2934919b9a23a073ecb61a074e51332edc3cdb6371c3a0effa9463380ba486e113ccfe40a52fee338b123ce4f4a906bfd041704ff987306da8021479e2ececa9e2a132182f5b269d1cd4103ef1c40375583d2967a5918cf7036fa61cc04035da83327210d6bfdce687e3c020e8842c81287ba0666943404f73955f09fba46032fece186a2b8bea3e09ad5e3bb37ae59f940dd66bf4de5ef7f48aa98b8ec48ea62407ad87be29d45df0af873888fac2821bb47a2cd6d58bf4909a3a3974959af21fe1691a0ec011db276a979e7a0d224358788e559d5be6496596ad2df7efff83ff00b13c973e1b10e150cce6c1075fce00687a66b2923a012e8a550a7bc155b6820466a415dbff6fa964134406045b170ba4651bab35635c1db4777c05ca49b67224446a7046a43190c2347e3d7122544b68299e1caf638d509749d1686614ae6c80b37f8032aef12f6ee584fb8c25bdb3d6da6fec0e86b38c0b4450e57f6e6f9fe7baadd658ea168b78b3c14ad897bd9a2d561a02125f4a6f8b99d9352f4fd39ef6a8ba4e79695cb557e74f9de05c2a02a40489e18c0770ab38e56539be8a3f58cd0eb3e75bf71547661ab7c31d8ab4890d0efa0db58552a183ad3393f375f294134f735cd6f02d4a294a9f9eb8a4dd9ae2e368d689a69aadea891a1061f19370d0e4e34f4c59587e5db33767b46708f9e6359b4f4375f4bfa6830b49754c4833630d98a5d93d2b76f515176584bce76ad3a02bdbfff06e1813d881d7bab54c9ff04783841d480527fca1bb9336e407f4e6c7629406226dc426ada32f2071a6de80e7aa7fd45f926d24512dd930c1f83297324cacbcba139738cc9fb7ad030eabbf1c658298e3bfe3b83c3e1e30a94c3086fb4ecc83f954f0690102670e268489f7b8bb1acf0c04cda7ba025d0bbbcff294f72fbe9ea72b4ec6cc8ca3eed92a9f1eb438de1462758e046d60e79abb3038eac89b919bd20aa3c600d430f03335f8add1fb9ec0fa6a348fde3197c7464a17e8956eb3c647085af686c950d91e3852cda94c30d532a0a1d607e8e2df5e393bfec5203f457d0ed3faf7ac80b150ade4b0d9a071dbbe6e5b4008902112f0ad4f5660562f7cf259af996a984812825f89017d44373ee7e2bc92a614e102b170f5bbf23344b28f7fb1c4b53de845466d74852dca9ca0eb434935682c788916c8d84cf5197fa148b50fecc360089ec9c5b3d799eb0fdf459ad723b4b09b26102986797850fd6e5c5ff134f86a8da02be7024841c1d3e90ad3f9cf57db77375e2660e4888c554bcc287359202b7c092cbd0b7756d0aef29ab35ac0733106b3d9833bbe9d609a177a21e0e919acc2d954277a0b5d610868720a4787dc08808aa389da7818cf3337c629eab05059cf2798c557230bcdb1aee62d3d7787533e4bd30d2816adf984aa5a9ed9ec3676f5231c5ab31cce14a74fbf40fb28e1855cea16096d489c2276bab8af3e41e6589f354d88b1fff617fb4abd2e890516662a56d0e99539be48a3926d3e131ced9e8a5e69ab9b520a0a643e6c89e4bb5c7ea8157f2636637b996d7b128328c880d5964906d8076f1372c86faf3736e67884cc67dfa42ab0cd65ab343229744fe807b3d8ccae5d7b5fee044b4b2acbe613c17dfbf7edc7c2ae6200cb275d2d8786a91cbe3d27e1c9a3704a474bde95d8adcfd8bd22a59c21cc453fea6a7456af6c2cfc7f28574da7b99f81d9a0e259f4d65a147ecdf7e891c0fe282a2581bf4ca909c9740e6704f8921c9f19b4e236384eb7d5ea26e44d70738956c58c11c6042b9b108d25188978b12dcc816fd0d3ec3459e2d846721f226da00085c2508fcd0ebdd38c01a3befc08c261511c0179d47ef70fdf60cf169cde401ad3d6b30ac7d4feddd46aa54d0912683e417e29a9eeab31604264c9708aedfe53964609454e57080eaaf5646f8c0ce31c1815747ea2bf752d2ac1c7353f351d80d1d843dad213eded5a0114163c08ea0fe95826dd69811b02462dab1d37d9b134d8e61a8b4fcb679cb2a65f634459238c71f5b86b401f32ce4ab4f81a372328984cb48b677c380657d3d3a92515d8a2af0b3bb98bb57e8ad9af79fab0055cd98965767ce1d639bb624f89b4c9bc0b62517248df93ff9a782dc31a40aa133618693386aeb84461e5dc2d2361eebdf764ffbf9ce0dfd9bacb81cffc7fed089f96ef323d3a718ae876926052eb9e9cda42cef5451eae41d216a1f1d91bc6391a76928986f711cc3280b68afe1f4392bdd8a1a0db7b33ae3622346e6530739fb2499cb24daad5e4d871ef148df5215706f8709e489b1c1759e100dd2a0c275959e7509b024b0716d6fe5f33fb5d4ea687945f837feee7d7e688c2ab35c10bff7dd9d34ed1e14fad98694c8daf1456213c48ff9f16f5de12d0883478c783159f82128a839279f3c0809b98e12be76a54cb0c96c0d5189adccabd075c14c8d045d43a454fcaf60707703c8d4b7ebe5f84e8ac1029df4a2aa966bcf7112b2253d1316db8322450b352ba3f78e164bf16b94f9fdb598a0b01d5eff52d05d54df53c90bf6c720de73c50455a03d62d65326c62832dfb6e6a1434be9e32aca355b605245837d0a8e5f63781e7b3f75debd8cc5905a90d4f4b59f8a7439bc071b5d5aceaf9bc625797d76aec43fe67fabc95a5e7080d5d58d998317f68ff5800c14b766d3376c2a22520ee727a8ad8143dba789399fc494d1dcaca6f1af047092231ee5095dc6b1d528677cccf0fac6fddcd7e7b09505ca129608b01feb98125bdd7d31136283a76ada0bc6f06bb5f1b8ad1971610b41cc625d0f636138435f399a1f49d75378dfe0ca7b29101443476b872c48181fbc918c632470ef9531781a932a0a28770dd6f4140b3bbe6beef3f35558a6128976d16ad2bc474ec508e26d6976ecc59c1fbf3f45f456e2a1bb7701c8342397c34a9b9da4bfb740eb0bf318b9e57f6a9b5a5ecc059306adf4640de7fc94c237b77bd67a02d47904fe45ded690f99531154a68cb2f13b180e0d134f99ffd2d5a182c16938a4ba0f78f67c937ec722087b52ced58a37c0b803f42a9a9d0f8a922336166ab61096a2f73fdba32d369dd764f6ae798f76bfae663bb06a52ce0cb83c3137e87c1a5b3ea70ed5a4ef804daffe90283ee3622217b965cb0895cb6f8b0d38fedb8004f84a04140f59c3967aa563003f05f7a983f8c860b27a8781e5f19658ec125c6087937bf25e72ef32549a6843828b94f68c7068a084ab2fa8ddcf7792ed25a97ae64f71a1b999f66a8b81b7e6c4e8579e1d9f7a72c066800977a6059a210c684d1e3a1e368e8e83ccf8dc434c70637758c46288c9bed244e09e62531a50b227d7f2637049dbd6b26296d5c88ed5ed86007723c25c11e4b206bf9bbb1978d1dd5807d9acbb698611f66478ff28312f03246e270ec40898a26ef9ddb828c9de127f374a50044880c1b6960eca6c2de6c416b339e329ec63f022c0b5d629e8ba71acf77e7b716cacb6d691c97407934555ed8bc21b4be8f5cb90c9678837a7fff06a458380f1069a877d9aaa800d287c879199c3507274ed694e34aaf803b1d65b22bf43166dc37fa7e021c56b66399f675cd4442eb04af3d551dab581cddb768cec944f0087f94c996b06c2a8d32815389dbf532ccd50a5206a3cce97eb86c8ce8ed2104c4654ab1db3b3b2b8c688e1129594b169d195f0279e38540fe92dd203d5e919268045ebd5301940cdf8a0bb3e3dd9fbc7d45628ef6dfa7b1fb370513ca588cd10f2483e66f33a2f3fa25eae63e8e97b70bce0db2efaa5ded6857841e7965b4fc221dd8ecd2d6b3fe0625ba0cb8ff45119fc8bc7997ebbae51be2fbd61de4d2deaacc4ed868e5d29943afe2cae6ead5aabb3211efcd5261e7336b6f50e5c8ee09f6a5fd312c108429c151da96ebb4d4bcc8f7125e8565709666372c85b26e59a085c87558357a1c80344faa06c4b081631416dc64e1f219ce7dc588fb653c1ae4bb20da2ace4826cd120990058903ae0bd6ca84de534ab96eb2cbab2498fc11bc1cda4d31968a325c9c56e444ba20e171be37d5374b300d17e4b09f9cda8aff3651e43451d9b69e44aaaba50d47f21c225437040fc76988c4b69c3cf051b44678aa5d3300ee2c8084a3b0c47a6fbfdac8ed808f7d7494c55c241d3fbf945e79cdec1c1313b1a69e4c088dafe9540dfcfda9184ec8eedbc892e3b89b2401e3d54008d6887239a9626f886c95f3f8436769e1329ecac033b24e1a1936f28c37f3e7dc538c19268b761e60df6439c6d7a6eebffc0572d992e46e1febb82126149c15416ca1a11904366109102f293e2e65e7f0579de10f9940f0d8a3014e2d5713ad12270d5ee6b9317c6373e561d27eae04deea4bf3654f82dd3725f6764acf42bb7e0ebc30eec7c9007377f970f22d113b14cd58ea92f452603cafaff8e4c69a319137d6fa331ff8b238f9065a10ea35da0ea4bf3037c8372dec0252964dd6029bdc339ceb5805f3d08dd529cd68e5641c203187188f73e14585b9864de54718c3d5a7a3d7bf11e9c7714e463a444bec9d3c37879acbf953fa74162201b4cf128dadbb23e35907cc30b77217af6f2cccf1c5bc61573328cc314a3583dae9d994c23d7e06345572cb86bbacc0e1896f662a108dc19f7eaae4605603df340e01db59418d91a52a9a882b4aa6ba84d50d15cc46363ddd1ec1968818289dd03314b517c65a56efcb906858c2c565176d5a266d09403fde40d7ba8dbc221f0dc8890ca247dd53705e8d6973f580ab4abcbe5407fa5f891a99d05a9fd4480653e9beab2ff5c581f1c76d26115dc7c52699bede9a84f0e6afb897175313ede3c4753a6bff6a21fcd44c7eba37ee9235a7722bad4ef86790fb53a4950386deb399d9bb3b1376a0d94fd40bc2df0c8f8abc446f78bcf2f85655cdd64fa7d4d195572a61db5fbad944c4ecc48b57014e167afd4dd9703d2d0c152d5714e818fbb97e69400d3bb1853515575cf6bb88ea954a903125f41e27b393cd531bb21e76e4cad82744935ee46990c328ea9173e816d5034e32414eca8231cc2cfea3ebdbb01562c982d52265cfdd764dde54884a42817a58ce683f0cacc1410c2523121ca28f20df29477b33aa00556af5d4ebaa4db8a98edf82aac26f7d45fe78f641c25c49c8eae831e22d5cdeee0e8c852351c92dac8453072863a9ae280b005a1020e9b9d547d1077d0d69683868ed8af520499f291f1c9174b7b6ecc69e9ef594b24825e499684b247fad9e1b7a3a7d221bc399da37825c95c41956f7784030ee13db9e1ae02c8b752151504de7906234c74bc184c913e700ba922bbf3d28d8ee234d2455ac1eaa270d14001368e558229916c0e0eea8e41f6adeb81c7af267d63736507f090521dd2565f68542aa86d0f3d84f045a178f83e4a52f11cbbdca8376a43e48ccca8f4a154decf1c425fcb9ce5bb1e16afe09c5871bfe3ce72099e86495143f8abe4e7b333cc191f13cb17675aac42eaa0a9f06b49f056f5f15c3d419b3d57f68b81b53132a9cf4475bcc145e3a403910f1e5d27ee3aa34567c2e86cd3a3fe60fd6a6cdf74bf7b58ac0ca6ea34b53a801fb11a400e3d4539ba7c7ae808e3dbce0a180fa644336e79d262690b404fc537f170eb2742d4291aa9d7cf8d549fa7da8cb0dc9b922570da1f7e3fc6763688810ebb203d675b9cd0cb570d5abc930c4f5299ce294732ef213820666e327349432d8084feeb9cf37541dfe09728b9d641a2de0def843ea87b5be8be64ebb99f75db72b1126f38d452f85898e0e4f79dfa58897891064ad2d4811401482998295c4da114bb95b1b08baa72cca35888e6950c16d8140d13da9c5da8d1158b1005dfd043b6513855371a13a2aeb02bc6ebe50e0ccc2797a3e51c5d227ab135c78753dcfcec24ebfd8a6a0b0f8ab104e3c1f107523b064a2e566704a741874738d5bb2dd292354e208ef20792f71bda3ec05bd6c64de40255aaa89e3644eb85bd2e1b30f035ed1042a3085d25ec2fad5f256e11167a31385b4a70ea1203cc4b5562b8eeafb4cb87dbb0d13574aad2eee5dc8965c29559b38ce53cae58949054502d2384084fdea675e56f73ec6209a993715603866062f5dec4903ceb38c5127633d9aee5d4e6caa0f1e8c7de72583316428fae38592618f2727199a36aaea3c2f3b0139316cb86c64af7ac226600ff63d71b8767f811d2bc86ea498a389a3a41422a0e75550e12c47dff341c3d23431a5c56a188cf3cbb9110411bf4442a04e0619f0cec11d6410b6d13e1b33163422b80e10a651e772688da8af68576f73a9e0ad3e14e537b1e1f0f5dfd84f8c03d9a75aa8932107fefee5a6f8e422a1790627b81b2fb3c03d3e9849c793359e408e09bdd0360fa3f55812f801b0d35fe839de120e0b76576818f5514b4aad745865369e49b3c8d2d212a211ada4ff96f98bc74475d4f408912d412c5906341e4a0111fd7bafa648b3f097ef7dda6d7151deaa0136cd335de7e49183cd283aa330eeb09cd01757ffd28781290c5f92d9132fa73bf42d7ae09ba32b30431a5d00afd85775b4228102c90d5d71d08509e370b37eb879bde7dee30d1863a1df493339c3f41f11a6d0a775afda29bdab2683b39a1c4c4b4fc3cbbbfac7aaf9ed601fd7c408a0d9f018b9e9e5c7b2b6f61e9b0bcf91d5aca4e0df3ac6f869a46a5537743b050c7eecf43c13d6342ebcf2fd69070ddbd0a94a5abb7ae9794850bd7b35ff822c3d9d15a146d66c27b5c996c36b4ab3363f67c43c9850813be3a3c4337fbb7e51c2259830eb14bf758c66bcf81ac2df8ae6dc86165845f6176f782e8c9aef91e1eecb01235f025442210318bbadc05a2b47b8146e012f7753fdcd9f20920f6e86a740c6d2b6bb960107b4301970db1756bee7c36dbc9f90f01d29a88ebcb33f520afd390a0503d2d3c3c92808ff9cef03d3fdeeae84a08a2b94f33564470b3a5e334635692b621ef170b3a1c105656e6fdb128ba70977c699b38e09cda2b7a348737bcb2fdcf733b083da653be2c34802650c647f909638a45654517d7b20ac2e8e914214d438f100828bd34017975721157d9ebffa545e18d474c5b52a33ec758c4d2661a46e99174064522cb242f65682ea9162c4c933cb8faab73ae869ccb2c92bf6882a1698a04245ba59a449fff117831bae4ea280fd39c458ac8f3452dc288ccaecd4a5c04fdf375c4ac0e828460838c7ce416e08741660bdb20773a481170b138e2e3eb1c8c02117cdedc5b96b64d80f77631ab1b5962e5f02cfcf9a4fd72066079a4a0cc6419bf115b1ca3a5a6f674da7ead0eba9a49e22d5ba537bd52090e47e529e2d66318b120d9ab158f3735839be1c58b5eda40a078b8545617fe254172cbbeedb117b5e757738e0e62f2a022e8fa3b97010ea1272782371b9f86fe4791736bb642b55e9d4ddb96fbd16c23a37532acf2af85c6302bec0d72ef8c2433067b6bbeb7bc83112fb4914c194f119780074e568f7048366fab700addb8b72d9364998cd7c735103a540ae543387a791653c81f598b287e302ebe204bf4aaf4dd6d76bd485ab822ac5e46abe2ea8006640284ae648e939fe8aac161fb360524fe827093644de162808d9fd14f72fd522f0ada81f694a798b4c57242de1e4df4c85036815bd78c0f58882faf4041837614faaf270f20971bcca4d652024b6a15586218803d18e15391d921113087e452eb079642214df6992922db3729dbdd5234255e9be0e11a99ea04f66dfb25159a12895ed1549c70d48cc8aa9ea4290ff7dd552049c67b81b0f28ac6df1a375cdfd411c6e834a146cf6279c80af7920cd22e5c3c1bfe55500294b2fe71f275c5732d37fe5b95b2faaccd1b4519b0785338886631bd45c28978175daf953cb8bc6008013f536f330c66524392afabb6438b6cbb1d2ee6bfc84e87a50882c483bec8d0546720f10bf8d0a030a2e9bd53a851b167582b1f2e3e5248f13975300dfa2873402dd56fece6e82746b78bafcc364145c871953510f36bf4a2f250ccf96da2f297dad300637a2687cb929a6bb00c6d7fdee7e9d93725babb9752c38ecc69de9d02722f131314ade9b7ca86b838e4feafcdb052440b61e5a524d636b37511a38b0213d783289835ecc4e1f86d6524e5c893ec0904cc7f59915b2e45670cb29c11f675fd0983f894ecfbee05df59943259f160f2036dc5d10ae4225b896181b110af612bc2463fc2b1c77d9763c56cda92fae19990ff8c6168a8f6d4cb03413a4a286c6518ea34c44e672385f845c3689843e229ee2141e4e62fc69cc084c55fba55a7434e0c01cc163aab7ed0ced3ee1b9882a75fa61faa6b8400b65bea8104300775344c08874b919405598aad29e7a63f8db532402a99e1cbf73f58009303fbcf6c4dc4094be7d8eaa2e45a7385343fcd37d4a16d09605cf4c733730ad1e17be26120149b8780ac058d9ffd452a91737d033029cd17237fd42985edabc89097857e9c1a22daf8b53a7c639c280912e0651f766cb0bfe424026b78fd9631431eb5b2bf142356e7f9af199d512c7372fccb793d7abf747eafb287d2b73c45f8fa5919d5be9f0e8f7437a9f5e7c24a443da67b49c180c3a1bfcd772e15c300281d33dddb252e0ca61ea6362fbdde01158dbcd41c7d2a1f07f0f7a4d1ac42bd83af7c30d16cef0679a847019934a7899a2c3e44081150dfa66cb7829772a51d22e1f0e2895f2b7811d4f6d97a83d60814f7467c09cf20cfe18a5c5ce83626b571c0d501a1145fc1f0c4d587755f424976423df5881f7a840112c1e17e70aca484b4cd3b8140af7b48bf9c088299340b0edf2c5dd2d2d2f891c636c12807b56c5a1868b3869b46684c1f61232549357c471496d09bd809549146fae886287f61039c973862bdaac4b0d3292c585e2a0a267acf757220ec4f069bcc95088a070a74812398b476f99ce514d42fb377391055e2f5f9562513a645e81ea234965cb1549bef3b1c006d815649e0759c8d17268ec20e4760a54c0281259f05a834308425a77726ed5e94bd877ad517121269a0ebddf51b4e4481600f6c4edce636b22dcc1ca201520b5192c26897269b2d35ca837856a93da56f40585fcbe5d147bdc1730ca10b7aea24b7aa42791eefdcd81f4b61c58281d21590ad9347a60836623b784cb13f82838142cee91e630b487912ea717fa84c9012467dc5ac7b9389f7ef467e9c9f5c4d18f60298c3b9a5017523989c3dbed73cb0b74402cd92e98cea67556f52cf4403dedb412a45acaa1159b33a52f38b646b2364112769110eabf2705056ab6d24d2d6eb41584ce73569c83971335b65554178ab4e5f2d2ce1e91901126314312debcec1bc826f7bff39caa18e65af03227217cf69ce01e5941e64db04212712d18fc7cbb8eec7c98905271042d1a50618b1b2cdc44cc581a40c6c2b77a0f6cfbef19a1fe81a75a2ee2a890123240acbe06f527505a3f7003602fd6d77014d8759b2eea6edaeb451772075e0bb7b3704915216a68423fc35b208ba8cade1e54f579bb33182ce357e6cae01dd2ad886794fc8d9ac6321361d62e320468ed70dbc502c3934b4ba06cf7ee8c1227e690ea69922cb32c2d71aec1a13f91c1b6c237307c84dbb3d98ee108c8565a225114ca87e5dd101d7d090991a0eba9fcdc107a9e4a9d89a01d8c8d2c43dab804d02e21f74abc90345a729bf98278b0d2e347eb712129af33907d9fdfa44cdf4063c9c58c96b12e99c37aba1b6fdb016e45062eb40739b8e1f12194e2f3317813481e6ab3bd8eb4d230ecdb22a9ab3a53bdcebeda612c05bebb53d6702879d93ded5cc6fba1b59a0c755cc830d969b8c9abb821ab6f4349e6911deb577670f8a8b063b589746092bddceebc79e94bccc548ad8b8cb4c76b08f1074b1c5ae1f97c9a06be85be71251460ef758cba438c0d01db0467e505e9adfaf7f9d7e18d7123fc96c001011689a8f2c0389c15f573134db0ecc36509e832afc99578148019b872c1568420c77c301846431abac7ca217e009356d79a31383476bd43c53b20e9f9a2cc0640782988470e64073699871401e65233d50c703d7469a7fb0c220cdcc2586ec6a6107b7d127fef1b8bf6a65e4cf225cca96330d712d13950964840bbb5687cf4a2bec9027e04c929bc8e01a19b8e53f4e0c43fc6a761193b42e780467bbc2fa9ca0365a696ec2db3bc41697c722b817075a2a073ca567b503c33d728d2e9a557549b31a2069f98e3027e332573e0a8d0a38aa6d0d0f1d6b3d5435ccdaba2f88cccc5866fcad3396fa85895270aeade9d7c8842d603e1660102ef1c6cfb9690d01b6604c96285ec2ddae5896b0cf10fb9426fc13c8842cbd7c73adb130df7b60294e4af7395b08539bca9b92995043b3905582af7e44d9858757950f1941c1bda5dce6eed838f6c65ee6a16280a95380fcbb726e3eb6141112247b7fec17e45d1b5bceeeb9e843222fede24a209b9aeeba2590beb884220538c06ee61a263ce277a024f2a27f68053f4980ff49ef93e76cc3aaa75ca35983a780a3f5346ac772b606f071d20a4aeb9456e9f1570058a0b430acd1b3796d71ebd6422da3221e15bdf508e2fc20069fcb120ae2511dd44161c5f917961106e76a2e4a48613d80c6c0c45862838388c7c95d224a88a863fc0bfecbe768c98bfa45fabc814ad80a79a92f6de4b51c99c6ca47710f24c05b10bc841b4eee2193a0df2fefcb54559b6cb9e0d44a6c5c7e9e3e9bb82935a1e2a09be458ad04c1fa83473fbbb66ed9a6c832de9cea211b6a0281b45d1ac0d89b67a02624ac83e17f1cc2baef319a36fbe6f82e3147c2ba4678fc503d2c25570ce43cc72d87851f436f33e212f199dafce14f473ffabecd10e549a587d8667cf472644dc421b540c0198268744b88abb06acea4e2815152f407557c79bfc3675584c3c15e70727d069d5191abba359af0438bdca519cee58f9cafeb83497fb2568ea58f1137d0637f68acae3e79a4ccac94924f76834e8c260e54b5836705f328e298c0aebef05c99dff94dfe8d38a318c709365e05a324f5bb7eab9e3efb41787e52c2c536095f32c3592737570ae4538c41f88a4d1a6398e54b6a090f7d88723299d2e5716a54db126b42a97bc6da366dc75d90a5c88097394c01b95c3e013cad048a70c3b05b30d2a29a0e4a3aafa070053e978671bcec4694b50d395beae8fa1cf56dd4990cdf3a2c24b84081e259e5c92cd1f63a5b61a208100c83e8f5999f9a2be1b6bcf2f15a1cac82aac5dff7668165a01a2b624837b2db9adc524a99a40cd90aae0a0e0ba947bdf7305f65ed5969e552aaefbe9c424bc951f955ca21223af9fd2ec55b91b40e7998dec764f2bc0f870eb14a8a4a8aca0a8b8d254c6c7aa231a74f578babc5dbe5367d89ee3dd91dcbd321cbef273fbecb0d971beee297c794d99e7ed91f0fb43a964807d7a60c8737d7799de7811ecba328708858e3bd256289388b0d5f51f114ec28698d7828cfd33befb3463a246289bc69624164459459202024dfaeb23a58c9ea746ca6f8743e73783a36411c3abcc14b293b5d0a11b993722425a8f39c2224738c7cbbd449214a014a198a9f6dac59f59f92620d8bcb0d67b95a5acebde77d5527e6988e83a7facc9d9feec34f9fbe52017ef9c1e1a2004fee02f25fbe2c7f5833bac46eb8ecb8dc68b9787cd68c4a412e976769a4c55b2e108c1de2a58ff5ddf013cb0a5652ca293bb14393cb7c4c6fa6a4245171a292d4a1dbb48f29933fadf2c40393727854967454a0ecacf47ca358983f41acd4e14e87ed799d5b1eab83e30867aba3633b5367f293cabbee744d9ee77376d3519e2d915bb799f633a156e85426533fea2ac557fbbd8ed58832abd3af769d1dbe3d013e3847a5efc1d91259a28e998ed492142df1bc35addb9cb4109ffccd42513265bbf46d13049c2d914dd232aca4b4e47ee7320646b9c933567aac447309037b3e2751c7e209cc79bcd461bb47fb7693ceb7a3527652b0528afaacf1dcf4cdf7fa4d9e0bd157b7691c620db5ee7dd964f2cf334b8a184e221d4e1c306e1d8729fb40ac341d54b2d6b1d2bbd2e3f860fca2c01873786ed3263fb9cdf438afa8152cf803f58264410b58f0dcfd0a461e224b34e46a0040a12ebdfbd1b3f7113712ad37274a9d4f8d8706a7a5a3531a349d269efa09c8b3a5552855a92e75cc76772335714105e24cb63692ed496dcaeaad22d120c6aa0eba5a3d9938ab837a43aa4a9e57e4c32fbba65e7f2b2fbbeaf5d76af9b51ab45e77bb6c10487f0d42cc41bdbb9dc9ebcf7bd99dbcfe545e769dd7dfeae5774249b0f3bcfe4e2f3b94273feabd1453a90a25e6a08e7ad9de21a52da99221e55eca55a99453ea26d870bdcd17f5d477eb90f660f9fdfa57aca96608c0fa906b9e12e1a9e713bede62505ebd9d804e3777a94a393c3f46806c50bd591ddb637d2c506d09d4b1ec3141401ed3b1c64caa9529741e39d3c97ab516c4b2e34c276b2b779b520f4f26ea1700724fdec9fb5631078b2ca1bda797dba15aea9582243c95340dcb7597568fde716e3f12c2e740bc62b938eeb2ac779d1058d7c32b97c778d34d6eafdb56f50ceba4b436e9828cf2953ee92a659ee9ed87faac4ecb57cf058d18a57bacb6564b931fbf7d3ad63fd2635e7d94d99c7e51cfde2a529f3687463b8179753f9a85d90177763ea9b70ae5ab4afdea01674bd38935d56927f4f568be80c0883b4f6d9d0f1dcfd3cec7f3698aa77e1a8a35ed94baa5756c67ca5ad5518e33931e51ee813d529ea7409c99b4491d7b42a58c38f0a2460eb511295dca97920598bf4146f1a47a6b599cd5a5a7965693ba821163758c930433c5010e80de04669a0fddfb2eea1b83baa5d524e9d4724dfe16bdfc56df99acf8ee6484d0cb6fe5bbce935f4b7a091ffb1be61ab49f1e991481e4239322cef8ce83810f0c76505e75f282235fc11b6e3e032c0f247586ca072f3fef3bcce453bccaa4a7d6b2be7ace3b150f052fbfd3772b9eb4de456f1553a976263d50bcfc506f755015f46c4e8749a0dc00d4003506786a004b5bb5c41afb019708660b82a74e5bbea5e5ed1029f687a72d4f693107f5ef088c3ba4a8cfab9656ab9f863a5aad5e3f8fabdee238eb3ab23a3d16c812b1462ccdfad8204a2dd153e76c1dea18a5d48a2193d44fba8757b757b4a87d7b2a45c5a3af3aa60a512cbc48782cbe512c842f83e85bfa993e88ba54441d1e29522de9b01e4155a19390cf49a8fa4c59dda93bd5a7f3ac1ae26c4fc77275e9dc4ef5fe5c77beabd79d8e4df9a61bf37c36a59d0d10b8f314b72b36210f1cfd4c21c59a14c79e1a4a01576c9de72a3463f02a96219fe30eea50ace9cac34b1e6238f13724f4edb20ad529aad037d29b9c76e0e853ee82c69c472aefe4877da6a478c7771102ceaaa18e495751a98c101de9581255916a49c724a8226a954f754465e44bf95449544528d5100d778e7295c953df8f3af49ea3bed6c9e37bf64fa6bb62b93a540da986ba257a4d55b450afb6eb3833ea35c9a5739cc9afdf586ba2f48806ea3ea8f3a03e43f8913a1733042cd63020e6a04edd04ea61147dd182a73978ead9004fbf23b0cc0a700e5f565aacb1f1d4eb51ac894fad447ae9d25bb536eb4629ad4fb7d879ea91d2a35a0fadd55440b500b241a9ce53af0da34e7da663568ca7617cf6621002547dd015599f145927095f3f9395b238318ef2cd9ad3fec41c2288397a091b857de2dbb3cb0fd6a104adcf4b542a8a35479608cbd3615b9f23d40dbf7341035beba3fa30120e3c4474332a7545f808095bcf5d510f58c75a0838c360a816605ec5f202675863a4eaf3e5f927306319442638e902f27357945955461ed6e411d6c4265b12bfcf0f91a06fafd6c7fa589fefa2a2149a6906247c742a533059f96a8f2916d7df8f15083c5dae684270bd81eaa8e85b75b44a00133c3dd7a25b51ac599994620d8bea68be3a76328a61c74e451d76f55621d19391eac84f456006a5eda9fd766b8476d1c5981a59df56ca6ef8b439c1284b6052c6ce472640e0f9027c64424693cf928784efe867faa194c69843b2f0fae94d847317e59b665abf3432050b465982f58c59087f823664008a0682d0c435503ec021502e2e4438bea56e72ebf9c7046042c0d4fb7f9c6a4f1d023470f1ed51666bd12289321e7daa59a3ef04f8e0dcdd5e39ddede790c927e8a3c30efcd161bce990490d885c024a1b21e0e9d9d66252ac59c51a966ff7b1e25ef22861f952e72fe7f30354ab79b435905842b29484c774440dc90f12a24e1a2d4122062f28c1d04c0713183d44b3222d05e00091d16f4779b4ecc433c37ce9373d4839d4a6c5672f55c4185f0491cf118a6ca48ec92cf3d2656ea26e2293de841e4c830f1e33c8eeb6d1d0b23a7e2d81adc3fa255d4a09837d6d9b2b34d2007e80a58c33d912ff8c22c55977a7f0987551cef828a10a0c9bd1d131f983970e4377cb70c6105668b102152831213b61c81746d0e0a1490f473776ace5316e42cf110dd27d48e731038d89b9d203a66dad9c1cc782d13ada298acd66b3c56c5346ad4b152970aed4549dc41aae08ad2463819e6b0f72fdc1d313aad361f3f9e968a61c84f1e2ae876f73dc1d82b3f3521a6b11cca63ed57aa25f159a7306fd00f5e907e3956b0401cce69cf109d00f93aa0408547c9644c6f8510284a094943b3f35982c31c03bf2a7431d9ce50ef501abb1485815ed88aff67b31b6110467b9e3f31d5b790257cf247cbbe5388ebb3e3f384a9cd169f211fec69858628d990e750b20bf9452da4d3dd337bf15ee10154b68575d6f0d4c135df5812e6c00d960628437a974f4e05c6db7da620de7240d0c25de27d27305e3dbab17310509ead0a1c38644b5504fb713948e99bc9f7c52c7b84fbedb4ed53665b556abd96ab65aabb55aabb593ad87785d8f8fcd6603c814d42d431973d898c336c7dd304c43e8cee963ca6e775212f53b512bc80ea0c344fdeaf0919414c618c650ae0485aff00c137cc4b85c93e326c755221f506a4a42a95bad9d8a33834b43e77e80e7fae0c3dd648c4c7e7574faf6b852bac59b924c21f572ca68543e3f06cea9db8d8686c5a29ead592223d4778d52094d393b35c4e41125839c996e9926d3b04eefe4b30b8953d2b971b71ba5b439a536799b4c263095bacd396b85194a0921e4b9e79ef7018ef2c0ce7f788f4281b94fa7d3e974728b3aa94c28e7509ee23a581a18e59ce7d50250600d9cb3ea1723d3e18e8f470fd081b0e7d9236a0157d5aa1e5bd35d9debfac98154571af8fabdf282524806a9e4af3a6672e92c170acc27b78e02f3c94d2e512c9cbce34c40ea731e7f4a2129dd65410954001e240a6704a1054c00b1032a8066b22646104f4c7044e4036acca4571a0fa5d348f77008b747d4b90cc1249081735cf252ca1e40e84f67e2a5c75a8ac0b565c396057fc4b7417e8c4b2fa5b72881b34b107611c2adeaf25d4680bd6574263df4aeccb9e99076820de292a43b5d8c716102b7a6cb5fc9e82cae8a605e8732fc61e58958f30316800b3c2a5796c0b99ffc93d87d9357745d6c8e75432d6769647528047b1dae7c045d10a1c1120d96ac6ee044064d64a05464bbeefa0de738b3ce2425cf743bdd9abc47e726976e026f8a9cc01b13d89227bfe93a3739ce4c7a9d4b9fd347c3d9306573f29853fecca09f92abb5e996675249a9e469198b2da8fe7479e428c94f8f530afd9444b5a29f4ea3e431bdc79c3b60b043261faad71eab8b7c5a3aab6536805e051b85e98835d9d48621c68d14888c8e7a8ce9a49437c004e12b89e2b4f58309c217944af3153d9c2fe972a96531de9cde79c9eae99f9754f6bcf46e201e6c6c48e6ababd0b1a82409edeb92aee598af49743ec3901c7dd681bfc836ee5805c257fad15ba01f25184f29318420a943d80fb85667cd70cabc1944273b55094db0741a1b10ef6dcf73cea9246f20d3f8e9d27f649024e214a434b765cf204738b74c519070517780b98f4a90d46c54d801036661efa31224463e8747b196050b7e08c30749ca98e286880f33a8e248075fec24e13da162c9906992c4104b98008c309c08019116216041953be4f01b86848a1d284172826fbfdd352548964022240d7a4513eacd273fdd156be277372bc678a334416cfd80e9929e07d22f7ba82fbd9652d6a674d68f764879f8d59c93fb8cf3d187599b7e656573deaa633dbbfbfa9c1c67e76571d65eea93ae565efcecbd9c35be355f0ae2bc4703e9cde913eceb9efca427636c59eff76a8b036f68a15da05fc11bb20e3fddbe7c9bc61d84ce75c41c52a6f8e0b06530fdd20ea78f00059e9ebd5ca9b55e1b08cbaa76ce39adcb0e25a53d813081971e3f182470fce17a69abad3f5c2f5d9ad1b9e43c82b7434c690bcd571cfae898a3b70e6d4926a48f3eaddf28bb6ec1cc7195e3bc82b5f98a7ebf883bc68193bb9cc33edf8f5a3c077b7a068e9e579646a3d1a0cce6e461e950a5e851d63ef3159d7ab4699d2eba949c4b2b4d2e4fd28f7ca6b50e5d46e02c6b3fa3e9569fb6466c03ce9336693f387a9e343ab1e822a55e673a8c968215d661750ec4b5ead03d47b0f32c4de35187f32cb1632d60a62d2d3c3a64b180f9c70d971bd2da8fc20c81ab5327a1086759c653bfc1c2625b5afce62526bd75f716b0078cbff80b78d3d26225ce9d3277e9d23bff70662fdfcbe75f44add04213b2ee0d25087e20ece7bbf6fc8bb397d802667cc3596eb41783e2a42e4117afac2fcba1fca3fa912518347172c5128a6281f3956561b9e12c2c6e33652c0e63c3c55b2d073dcedcb3bf74b5c016035efc7319dc4130071877ffc01c8af48071f71c70f88bc7d90bcc06f87d36f365dd3fd87c5987f960f3c5e22f9fcd34cfc272d9e22c2d2c2c5f75d697f15b1bd6e2fc44b1bc637d15b5028b7fcb8bcb0d67913e2abee2feb5acea06d8b2200bb2860458e53430fe3975697b71ea92baa4ee381cf40c2b02bf148f287a3bb4200eebd65f3e0df4dff068d3281650dee22c5f8bdff8f2cddf68f9327d16972cf076c8d2b5907216ab05cc3842dfe2d6fa6571e944be931e8bb758dc1d67f6e22e37401e39381cc65b2c608b012feedee3c51dbc2972c36f70802d16c799b9dc001dbc81015b2c208f1c1f29345fd45f1c67f602b280354e60d03f8f71bf29d2c31d87df14e9f1e2ee37457a8ce02f1ee70b06bc29d203c663c09b191c077853a4070e87016f7c80fe02deec982fea2380493e9e6391e798e4710603b21c84cd17cbe3ec05f4f962798e4a0c88b31cf09338cff296ef47447ac9627dd5593e93df387917512c60be7916d84b7ff1ee45eacc17c5fe63601c0ee31fceec8a23dfb587e3c399c17c3833ffe44ce28001e37cbd7c7136adc959bece575f966f035cfdca97a593a7aef26579047ff9beea4b7de075d497bf7cf31d088255de5afa4608bcf77a6b7762a13a975364d6e84aa572aaf26ef5f97ce51f342f3da216703b44b9c43dd8555454babbdf44c34befef935256b06337e1942f7baf026d7ae5b90af4d487571d6801993f813554e7bc5e535f6f99cf3d668316907909d670fd8231a1d721f51715587af5ead9955961c7ae530ad20e61b8ed52173980a3122b6edf450fe0a8e4086d4aafa78c5e0fec2a0caa1c3b066f30ca4b812d06ace4e08132e4a002a6c4190573b43dcecbd3a922c905705126e726ca5ae1f00c5cc1fc23223d0563f37054d3278240512b620c23762006921a0b8488a090628c2044927cb0417c760c9d9a24694815cb5062051353ca4725562079eea3122b8ec498574cf1e9311d4b016193d216645e767e0259f759a1b7773bebbbf1b17c36beae3f5687d46146707bc6f45b39ce2c656515fec953c07c7dbe4ebed211e9309c8e0202ceb14806cd39a3f4f1b13d75980c4be995929eae6ce0305fb52d640af229751bdfea8b71e58bb1e82f0e53c606159f0e43cb6c388baf348b8d6fc77cadc0d9a8dcb5cfd451ed1295f24ea6a4a4a4a4b84bea285739c6790aa4be0aac5bc81250b00e512850a6c31dd3947fc0e2bd5f8e4b56ff24ff9091999147b84f5af44cf4c053679a51a27cc7b227bf5fa743478752a660bd7a94329bf9fa017ef52f70f53c43b3820559334a94990ab4a1c6098c72cf559ee245522957798f94abc09b22359592c27196721c25ca2cce543278ae027dbeaedb7494286f3365edf182d34fdf0fd8d3eb204c103d20790ab3d65ab799d77fc0de9a6aa8ef9e3ce538b3ee3a0a6c9dc0937be04d911378e3f9cd7594e3cc6ca689fa9df4bd057447f8bdd54375ddf5529ddfebb09087b37080be73d529eb70fbce55dc8d1cba26b0e5c2f56e8795d6e460eade7d19766d66077bceef674da7fbc1886886bebb3a7f5df575b7f3ccfadb5dbf9d5f577d9df4fbfdc904847e5fec44bf778a6fed9b3c4720e31c799b71927819c4e4363d5b2e608f0b3ca344f9bc9ace72750cc7f465f04dfe030472ddf465a9f3f7ad75f2368bb72613f51a6f98547ef747547bfd808c93d21863a4534a4a9974d25afbc5a65ec3c0d461c079ea281728e84d193b25ab6c1b1839324a628424a9ecce6e856225c1322a3e5809911e3ec77c4b29654c8e897987b5561a5aa6b2f90032697a7e2a2d154d1a6966502c2aa9edfca48931464943236d38bf2e43bb047390de7ec11c8af490de397026d86c314a601edc0c53d68aa0758ecbc15e8fb3f899be70bee6b44de73e9bf99a289a0ea7bb9c6d991881912d8ef454c943a3d186cca4db948eeb7285e901abf88adb9830aa2fd7c3712b475c0071aa5896b92ac0304fdfc10459b98acbb0721550061b2e69f3c52347c5573ca25a0c507116efa1e22c2ebf1c722822411a549c058cf375af09ccd7e4281083f3a640f91d98f1d753d2143a20928216d8f85a66ab2fce246a458527d6d868da292dd36d994016fa4d2d4bb9f9f811652932c706cf9baebcdfbeeafe887215602ee5ab5f754a6faaf66a43df9ec360de173ecdca601c06f47fbde752c0c0803c260a15ac8f1e92e7753e915a269b36259a53f4d4e3ce533a979e52da534a799ebaf7c520e4e78bc18726b658cc0ae1e9bdf70ad10ba81cc87d15ef8074ce9deebdf7769e5fceb3a1431c688ecb08dc49419eec3c4f305a4021cf6508ee514ff5d631937b2ea174ccbae752492e75ac730fcc55a84506034e21d90bd8492da25e72d5725c8a9eb680473d1348082ab88af07ce8339e4d4ef79ba19364803de73c1baeeb9c3ca53db83a4d6cd7790bf4bbeb9d5fcfa977ded7b25133709e4815fc1a3cb0c579e79e9fc01607be209168b4e79c02381ae940f9eb9d0467134ae5d0199509dc7975ef400dc8cfa7ebf5e321b94e5df7bace73ae7a9e9fbcebbaaeeb3cce4f9e73d7797438c30268a0e1ebf1eb0f1f8d7a68f2f49172b475f2a9910f3f4fa7f79ee7790bf4bd4eea58eb85ce3df7c01aae77e00c1d52a72e98fcbac96fe702dee490d40324662e37375a60940417b31b188f28195afed27d5d44f568f1229ddfd60b26effc012d7fe9fcf479eefa8a74ce7d8d62e17ad749940b2617ae5f9728173cbfde59d40c32b4b8cb7bb4fcc523ea012dee026f5c5c873e22891fcc5c6ed31a9857becb7773038b1344b1cd60401d547a20c6cce52fdf8de7c5ec46070ac5cc05163179903a2b62bde56b6d81a99b3e1c3aa4ce83074f651329bea84f1d44598bd309460bd849f3824927209473bcd2e1afeca4be9df4f42710096be00944f3e48733719d73ef3d367127ce4fa7dad42f28511ef6ebdd07d6c0f975aeebc0ec79fe21f327e764f7755dc511face6dfa640199af60eef11d5883e779eef1553a9865debb1763164be8c20081f3443add7b6ff49ccbf4f344ca15fc21f3d7397002c94e80b314ea72bc75d7e5b506ce13a86329a73e835ae6e2b163f4652d38c7c55b52a7947b2bf92b4f29d279cb17c467455cac16bfacaee5b258ce9970be39ee5ef73aaff703527f82730893dbcc667db1c31b5e4d36dd62c32578dd66def8327d16b06dac3ce52e5f4ecb0ab480ccaf8035a43cc555be0b48e7fc54bd937b9e075a40e62f58c3c92f386dd349ca1773027c3de5294a533865f2940ab79623709e48b349a5913c53c60304ff497ee79ebb2cc19de78994c3894487b8e06f488b9b3e2924691dcb710163dfe897c267805842cc413df5d9744869a66c36e997ac05012da014ea17f53c9f3c75126ef807751a26d0b119e4a3337941dde9c45170da93df7befbd5647cb728a9c3c88cf8adc1308cb323dbe73981d1c3b8c1dde6f8252e8fb21f326500ee500f3e8d1086871ea3340405605f69cf389d4b15c63f074024da12e70cb37937a3ae9907a4369f1ebf422a0059c4dfa756ba589d42195b4db24620375202a7f28094a387bd46792938ee51f348f349b746c4787b68e5d50053a919e529a89481a0ac1d23b1ccff33c10ccd7399726fb6d397b64a30de003893fbaadd6d276b52c0040ae6387170317ca054b62f4123a6590185d8cc934d65b6fa5392693775de7b17a2e23701775b87c1dd42fcee56739eab64a9a8d0af7729ecb75e1bc3b9d6ebdf5ba4d535f5529e5aa5beee22d3758408f20487324389528cd05b8813af68269e8af73de412d9b368f071687711e6ef80bcbdd7968711c1e7970f1cf7968b9cb6ddae6298e111c67e6eea0f77007bd3a88c347006f46701c7e03ba3e07bd87cb47f0cf417ff95ad5631c6706f3b52ad862008cbf78f597178f016f5ad5f17c7137ad0a5ea0478c03c01b10673977be38071d00e0cd0d001c00609c2fce637066308e33f3381b610430ce4010e6fb70e4971d88fdfbe1c0b83e9c59445dced621e73041e00972be023ba8438ef3950fd5403044601e4aa3391d3b798a7776e53a5df9fc32f8179c6e3357a0ca57befc037cea534505cfd5caa2565899b3da58d958f95cb9cd5c7d9eaf7c32afe22a608adbcc94cfe4f8537da9af7bdcd41db8f35c873994d621e77408b879a68cd2fac571d26509a6ceb9fcfa5e8ea643e5ad3ba77552ae23707c5973faa97ea7438efbba04cee1739c7cd221572fa551de94963ae4501ffdbad29479fda2ceb94ca2ce717ea5b73e59fbe27cb548677d71be6ec4f9aa511e8ca36a98d8f506297b5a0bdee830b30ce0661664f9c8c3e7ab8f563d40797dcbb02da41f8d820031a9439f65be55464176f09926465b8c75298d65d8e4fc7e4cd007904859328c83db205301ce3288170d67162ba4e12648dd06e1e900a4a8c227670925487082d904f20117459624497a810aa6df3827d0bdb54f0b49f07b80dbe767e26b2a0dd18e084da529ebbc9daabc33a5519e0bd0cf4d448dfc6d4e97608641f5797cc517f55c46e017268c07eac31eab566ee8bb87aa12587ae686bcd6d9edf2766d361c1943a0c962e218d143133288a01c1931c2250a2fd41041101143a4600b5b0bbc2dc8f8c286859204044584706504d199a66e4293c59dd1c6c51c91623ccae1f936ca39a26d321f8d7670f2f8a3d10e4c4e48c418639c9f0a88a6a3e8624b5a777cf5fe32cd3715c0d3d753991c19f6349fa7d34f664ae9f4894365379192b375d2eaddb04c1853065e75d878842666123aa2d319bff8735a1db1a676281db0199a8e4dc7f42508336101a9cfe3eb3aeaa33cbcee641a62f2f90d617957e095734dad388221a1e2080ec91430484d284e78d105120b7ec862073857404146115ca421c60f0a2c1a2968d064714d9e3d230741ac21c50c867e08426896c3ef3098a0608b275bc8008bda2cc3525002ef68071beb63582e67b9fc8ca218c4c00659202d1d8133451557b4a00a1aeca084892e38272a4a021c2b5a4002237888b400028d9803244c20460dcac841171f9022050130a3730c4d11809ee5218a882eac8d745a6a4bb2965e8ed28e72d5a957b0063acfc0713a994e2f7732791df8c1626867dec2f0d4bb90c2789c3e1c5fd64d9fa5818bb2a6c4a475175d17e3ed58a6f68bd3ed9c94eaf00dce213d95064433c22089153915091485a2bb78a28b28beddf359d3647177c0e0003282e8e0054d82708312cc322ec210f6f6383b22e0e2a70647d4d01941f75149117ae09ab83e2a591ae306934ed2faa86449682ef9ac3e2a59ca8952088e4ad480825d3e2a51030731560009e9ac9689d66be708ed31363bb84f850ee5910fd366470b2a803b7080ef47256a143d974590182e402c7688e182aad031f9ed4ad4b8a259cefaa8440d24be7e54a246cf67d810643e59fda22bb0564234705efd8a83df2bb1267678fde4b565d775de42fcce71fe661aec69fae9c3f93987689b373fd0c926702bfae9bd6f45301a0d01848f46434001422aba011314ca38ba01cd064d1050a7cbea3c3aac0e4b034f9735481fa2e333138f1d2d43e01d33d0e8e8b07ef261e041c3043ad696da7458dd4e6be91d1aa3c7d239e79c6ed3611938a6baab63acbfad7b7dec00526badb5c6a5cb755b90857609c2c22f308d8e5a1d8535b0053390969ebde7e1a6441a9a6930a12195451c1cc174ec50428439eb8d084284aa2333a4f81c6b6940f91c97bcfc53a9923fcbf81cd34294527e0330f2d3e104ea704eda7c4db7e9962103e7f0e76c3a2c8c350178f93dc0942ef958b323e6901e3387a680a9a48e0e27cd844da129243bcedb8412639c13a8c3391304eb700255a1ca537d826093086c0acd29f4d32bcc66078cc68bdb4f289634f13322a163466fd84c944921e20b96bd0e36fdde0c06d147253d203d013e2a01c3873fd5ae7e35e9667d9c15939e34a5599faa549f5428a99a948b96fca44af549bf66d25caa499446693589b37149dc118734974c9fecf9e9b0c3e971926765099c6b524df2e9d7f4599bacd993474e6f5510ae8e7b5aa60a7da8c3698dfcb4c99e7ecd68ab493c1dcee93cd487d2a80e0d733e913c2630df3b39b02681b70e674a0c3f0c4dafd33deea6e2a28b31ae2c3fe5285ff954be942f461a1e0566ce81d44f79ea93284e86850cb46740c328b0f0e8db65666831eac30638df7bef2da3eb1cafa4e74170f67a3c2f3e7f38778ee4bc3d7bbd63c50fe792a7773aecde894ce0ecac1896eb26050b36c35a812b26cec88aa201a689208c089c59b22a7d8521d5d5af66c160713543e7172b108a018c144243580c0d19112262a466029ce950fb0011e908e1949f9a1115d46e4e88906e3c984ce087116699719329c03419bdac33b014c2354a816793a443082cc1b504f8032788418307273f78d7c1c232e1bd3cdf2ea58c5f7c0cc84781089cab9252c7a257ef9b93b9259f29cba94a0860e9ec54a5ba6473ac4bd40aab0e33ab2ef5ab9598c0b977a8ce4c5a1ad569409c55a5dc3ba6bc81f88a77f60ecfeaf258185c14f8d0e2550367576d892a88c4099b2a174081b317c4c8112f42ed04459c07f159115a2d06ef8d36f37af29b325c342b3a3ae10520c039d68608d2b14d800a24b963028f0c22610832fff0de358f626e8a0886e80d5c3e4d199946c70e2a7ebe1d062654d25813c1160ba7975508e7583491fa863bb8bd291891073de21449df4ebd98b05d32ea50c107dc43b08ed6118600e799191a5b0f4c31ae298c7cbb87924746596b489d2613765c77099c5d352764aecc50d1b7c368661a8576f0079c2f0a9d23beface64628e367dfb094e603f41099cef1f0581d2cb92c2c9b7bb622e653965d14b592e168bb2288ba2006796ecc6970c7d862583635e3706527c670a4483a29114404d443e81330592350f4b66864787881013c59295cc142708c1f9fa60bd8893389d9bdc178f628cfac4a83a5b15bc21877a82162166d19e6449f483c8d2b7dba0647b1652bfa20267b984a846d4592e398a4e56b23370f63c6c23c24be2612772e4db63642cf6dcf33cc6555ae0ec55e0ec1e23d333adc389e45cca2a82b337f3d325c1988a83cc5bbf710bdc4e82109cbd34702865b49ee74d5f0a62021f1b7ed4925427408002cdcc10387b302238c312009b2981916fc72e5c0185126cc707305b1138944c749e71549ab488c4809882fc8925b4c7964b7c7b1e002ac4f7b27e82a211143a42df493f917c963f9289280be38bba07663984464af14d53f08dc50038798398cff30c311ffd2378c3cd4f2965fc9aba7c3abfaead0e8464e88d36d84c9d78b2e89d537ae52ad04fcf61e8670e4664edf9ca594b3d2c02675a80251cd64ed3ee616d92000fb38634f976574c0d7b2cf7564ac062b1d27019020a2d2f6859633504e8db5949c8d0fb44135939ea7845595ae08ca98f004528e38401e725e8910067fc981a3d2134e373b5c0656419c44b9a5d5996872d0dcc9394dbc1f9c690e78b95134491a3dde4a75d2265a7bac90e9b300bfc1252b3699af6eea2cf0620383af522d7237d6b28414b2c670b3bc42bea784531ad74d222e04cbf1e9135947eb57bde6ae572c5c4c8c8cccca024d8b71b9e353a0b5252eb2cc89f3d478ae2b7ea7009dc9e2967bb5f9490afb70ad2f97696cf89427b01b4c0f9c29800e79594a00e749080c9f4e9e191cd070db66f9fc0132c057084a5872c1a6030c9ea155626f0e049a027a2784810180110be74061a78c099c20ce119a6cc756b2166c966a542134eb0276792c0d98399024f1a73543a832682ac4f223fbdc6395f681c2292862d880267fa8e032023dc9cb45a9f93ba9521120c530467fa3e91c8143351f4ed2b97fd300b09ec853c18b284ad56c677c76a4b1049b284b33799991829ab65cdcab2699a59ebb06807b708e16c8b2612f1d5dec41114738aef8cbfe9d349f345002f70bef86f32a0de97906342eff6ed98156b30fc52d2d60baab70113e14ac00c8d2bacc90b002a70d846ac2819af5822a07dbb2b064700fc803d8cf1259d7a2fc1d5e71b9a102a8456e9a7e8db63649a26bd355b131c6225bef3355ff3058022a0ff4ca2cba99eec69147e80c0d9ab4138378fd4699f9e1e1e267e787a7a7e7a7c8048003484aed24829250aed0420024f100a3cc20f9e09a70cf30c0db67dfbca05055ef5abbd5d982584f3cc0ccd6caa4484a2845011ea417c763dc6b4941cc7711cc7494981dc340aedaa17e04c5b9ec099d282e87282f8f46914801a8576500767dc42023c654a5dc189a78f0d9a40d3e767067d7b370023b89b76eba48cfbd63730b7d24f1a2d4f9f7acb41039c6d9067877c304a42e7db2f0ee2b3fa794d6780fb46a3d1809821e142e2474aefca992370be66dca4cf2c560a4cb0f418eae8d09c7cb74ea3d05ed3853423851a900067499b4d04080e73c618638c52c6e9d52b0884b68c00e70825d208453a5919f9d064ad94dacc3a25edc3e1064887ede4caa4ebc99b139d9cc598962670963a4b9fbdef20bebd958c8e903d1e6b0498e65f4b3a50173b80a3d1113b7149343222c9cb1585c9486983427b4c0ecef7852286fa1589708d55e4472152bfe20b45d8fa65d3310801ea982925861014755d0c427e6627bd6e62fbf831f860eb5864a253f4f1533108f9e958eb049576b28a25a210824c167d860e1a2c98d18c28702002ea4dda1a2a1032a39f4c16b3ae224089a423b3ce62f18310b38ef3748461c60766ddf56000e5895977bd8864872b9a9855fac57c114f0760c10e7698d12fe68bee16a3a763ab550096c0392669cf9e0a71464727835934d650d54393911039dfb567b231ab558738dfc7a11d0ad1c92670fc485d2c21381a8500cae75514e2a18b1e6324907ca263364fc7f264e2a54b9f3d1db343f014ec0a677c66088afc30c41b8500e8b30c8ce6a58e8f73f6fda5a629c51cb26df3755fca6e9df98a2efdc61adb3cedf39108dd05daabd3be3fcf9359daf7d7f2bc3fb6775dd7755dcd3f56363352aebbf7defc63c5c5d8f909753dc618633cf94549153570eea321232400e12c87961c69590c02110e863c61021fb2f86216cb30d2c58e114a5cf1248c5996495ec6d8349942e7d2bbd6495a26a12c894b1a045226a9902fe9a7938a8a8b0b080a4000279cf0b969349fe5d0cba6c9215982fc8670efb42ce5d24320706e9d3e7dadf3c53645bf3f5aeec42c7d7222573ffad9d0210e1dde504bc49eb3e5a3cb185b4e2f210a2ca7aca293fe2f65a2c0511aa06354d801c38e29f34eb1430fbc37a4a1f387fd6cf8804cd9bd76de6779057d9c7005962a47e01c06499de9324a389cf14edb958707bd1763162b4687d944f7648d26647928e951865e155846d9612a05eebc7569c5cfa75c48b11c67967a800d67f11e379c750394600f1b2bb761897c44ad2017703d45b5e22ac718a752296f217eca71dea6a4f8ca8a0ace4b1595a7a8dc66e28f76684aa5be4fc211b23f22129e27cf4d5726a52f149d44920406d2e8071e263b3821f2d173c0e42308a45f7a0c832841104c9c3e1a055144103b4ba827b5c9e9c81a01510382880342e802210253ca472320764e42968c8408e1a57b9d173f2321647842bc403579e9de54a283b1819025498c7cbe443e1a09f9f9ecbd91909eef3e1a09f1618ac07bce2808a28f46412479e971842efa8d1b481b59f42a8a39026a44f53e1a0df1c34bef6cf7d14808ce4808345eae7c3412c2f6d23b939110472d174e1c781e3a181960e9d1730918ede43cf7d18888a18f4644007d74fbd18888249ee5a3111139df804e3681551f8d840892f1a95d5773c6cc09baab89f0f4e4e8a86081775a366f4bb126526ff1b95125990e512b5039e78c3748fa03c443959672d80053a73add4b5289e65328d6dca66c3ae9179d3ed8a713aad321dde9900798ea7c431141ac699d2993ddb5f7722e1aa5d11e22ce4ed9bce9dc564c80a353aaf3d17053afca9f2d43e02c956a8d712551edb6656d75aa4379e80f1532723bf2947a54fa4c89d0214a446b4fddd656ea589e4e6ed427143a6f2dc8ef08e5b3cc71d2b1a8c40c1e5e004f3d4bda53181138cfc8cac0d9f5140535a4924ec71a6ca1d5de390dc6eed941210ddc394a4b1dabf488a58c941876843a8cff7af9b33567af78628cf107dac718638c31cece2e57ec24a531d1a78b312779a21d06a88aa8444d3576492c95a281000000000314002020100a064442c1703c20d3335df80114000b8aa64e78549809a4248831858c2180103040000040000004b37103ee0050510152ab06c334aa23d61a64546111202742d4ca06e3cfaac7a90344a8f04212d80820e20f4ee00126ae7d959e5b2c0de2c4a99fcfc2af6d2bc199a74a112ceba66362a4c6692c2b464700285e1b5d2c5caa73e1fc2b447c31b8025c30e11d78ce88e77b68876188c5100625c7b37dc2f8ef21c7b3d7efe63ce4144600e2c0b128f45960a10407781735813125f7025d69533fd841f2141cdcf403707a14ecaa26f3673b7ca4a6f1738fdce599ff8b605585e5d5bbf431e98375945d3a542a263c214840501aa76284d45218498c0bbc25d9db88a4c4e42e7370c88cb609ab9e6425353864c51d250e9f711dd3909008baae98d7747ad8a04073f0770f6d39c05b8359b9e6c009b0808199b5a985fe988c654613e662fab8053de07659379d31e6c20a8f5995c9da06e6ce0449decb1e5cb54d17c5c6a5be6da08cd6f42a841a772ac762bd011b71b5f2c0603d4bf2489691e9613efe665c634c2afc9c7f3e1fbdff9471c6d2947208ec35e9bf1823b90e219e0dd02c9a9d4832958a43d9be00395ef5fd04966a95bf49ea63fad357b79f70bf321723e288f375997f529275131e00e5641c4a8a9de59b9c1a758231767b99146eae5daa0980e58b3e391cb6800a733d64413222feee04385e70245bae7cf54816d1faa02de8dd4d5896f9403a9f37b3dfcaa95402b7035ee707782a509db6e11c7ee08d83174bb352bab9a11683b6bbb8aebe31e80aab5367a98126c254ae02a270cf934ca401ec686f36048802fdae075a3ccf348506364c44b653ff0f5f4546796b62006f312f2d7ee830e8721bea310344b6652d41ca221d55ea873cdfcacb83c81d3d064d6c4ddb0101414105aacb80321737ed9033c41d1f1fe8cf78cd50e3c0ec886d0d7861c992a081d7e6e44dfad88d61f5c350675e09d56695a11ba3730ee49567a11fc720c6af0273dd7f3d67cea5397c254e1dc4eeb130906ee318b18320f86497313056909acec7cbec1a65d69c9e5e184db9108108281efa1b2d7a2d2ee12b9e56ceaf387a72b0d45d2df7a8b08c01f9cb92f5668101ca2852ba657ad9c2adad6e93601f029592caa243d8823776602c36c391773654c78148246ccd12f73f9bda6a597cf2780602082520f7b5b7ac401aa68714b0c69862d6f16fd67a92610eee9520c9829aa9f69e94ec0c1563c849e6d68d42530a068e62fb8a6326406183055f5dded58f34f077e81171fad04315da01065b5c7ce11ae9988b11d760593cef453239f68651b203ff84ce05062cee38463a0438ce4b93c6752aba5b9c0c023e42cce57014ced8c187d6d670de8edde4a00ae52ca037d4a8edb868a01b26bcc6fcd7c8306db2e9036e938ce735c7327bb934c1ce4b8595c441ede18bf79cb7881dcb074ff53b8319538201accefed1ab546aa3073084d3c7a227da1a396297438cc3365cc338e3bf5213699c109569d419ff32fc462ea1c311fd4bd645d45e4a5f3973e447f242c0767439b8c4684132471bd6cd64cb5696d4e590b356d8e2a08c6449362b68d4441fdf3da16c093cfc225f724b13d4158d570980de17060bbf7cad3f8c67437b47ccc0e72802ccaf54326c048a442092f5628b768933c6d18605b1f6d273e112ff9a9c73be1de17e188fe293965a0a245568b9a01b4b76c0c0fa5427591ab9526e5dc1d62f2cf9c0ca205359f1b1768092db1eb9d3588c76d1080b46064721661790c5d09428a0eca93b977b7b145fe4889eaf4031117ac0b453f97a8114a5da362f1025fb04d1cd3503c491d1c6be2baa44617d60c729dee4cfed16f131d669f5dcf5bbc4d00c652961e3db9a8ceba2b399869fd52f159ae6a4a1047338cc02b153feb0904d14a73571ac440580d03097c2702982a04028420bc91037b26891d1084cb7539653767994e457bcae6e0ea63a00c65acc52628557b223384a6c4b2034c3bb17878f65e0d187df755e2b6460ce0ccc2e00cccecd938911a8516e994e8c3b0920bb24c7e2ffca4c6075ffc904db334e92f3482902010bbd6201fd48503c558ac4bb12d0e9d2538534124655ecb5b6e8a72c7df1bd7f87fbe98873035415235cc78805fb255c6d28fbca8f0f5b40080f1e9a127c855b34f9aa6fc46b2a6e697988bdf0ce7428d7a6bb9966df413cce8facea4a444000f7696c23e694213f082544587069820387238f2c894804beb80b7e908713bd85bdcd89aa5e91202e167b8eb94adc07c1eea8910e16051ca767e823c44eedf40eabafab2cf84b137ccf5af3538a3958ecd5463e4ee1417ee285ff7175cdb4532cc83625c734bffd7bb3be61e3f285efab380eaac44d0f49a331d0641bcd72bb3e87dc444717de83c2109a5af02c712bf80abc1a2086ad3418a9dd0979098163102caf714fadf2711b271cab8f375368715fcedc1a9d079934f2007d2fa51de76e577ee5b8cf05a8be1f325856dd0dd339d471e32f7472eec9cd84eb2d52ccde5f7cc7b2e033bec4a3ae5c96dde8cee2eef8090ed2e24b3f71e45d2908e3a1056d92d6329bb354cca141eb09c1796a336246b7c0e73fb1f29328267246dd18b2c5f75011d25e5ed878d847b7b3305cc593cdf880c6fb49dc17013c1b36890e9c0d94ba0e80d1c2a92ea9f6603bac5f690ca49eb9e58473c0e76f8d06ca4d8d25b311e3b6ff5ab519a796058ee7571795f7d8e1a54130fb45b72b3cd18bc1562b56d2814ad3af199a129a4e4dd90a337c3f341ad1041f60220acc8c89e17ca2863d2f6c39c4183883a8335881b0308b09fdab82fcf72190173b211eeeb19091f2c45c5a6ac3c68e7593ebf2b54dffd25c4d30a38d31466eb081417f577e31d4586e7f51c5d09a5c76d9c7516b95a077ea4642e113a68935ee7e033dd886609219db905d7ac9cecaef18260ad6acb097e18c89833c4c1250d6262bd868240ee9187afbefc3d0040210c233181857fd9095989b0405472839ab7b8cc19c55615a7f43167b1f8c4ec31957964f958ba4e1a1eb520d891e1875f408f45e551b148a3d580ab017be918a9d6d17250de757c37613ca89d487aa5d1b9b766d6b15e776caae3fd9f997a67532ef9fa13db9782450388d4f83d609c72f2d30c4ed8da171dd920b66b44a1998c7e2d2970c2e3706e447e1dfd20b2017ae68dfa0e55a52f8e857322258e918cc08df4465f61c36e439bfb974f9ba97018a9f10aa0b28475524b6f23e4a293579df6cb49294863ee02f480aca2e94c19caec88c582db19d02f1530bb042659a4c55094496527cee65650e01b35838aab7f9661220c06ca8eb47c7b442e53cc74b79ebbdcecc23a39128f46485365d984bb5b5c3787cf5f850be7255aff7eeeac9f8f486618ea1161394aa4cc05d65f4203c0fc4a96f69e29270a612ee863c4cb5e9311e975433fe6323c87001d15d960caf02f091a49f3e92ae46ea07761e501acf07abd378431fcbb1d59a1557ed3a8d5bba8b12bc973a466871a4f276b889577f64198d924c48db0d8cc9228727cfbccd64067035c66b98af946cf6f70157bbca51038b0b3a1bac9a08cc4e28e791420ecdec0c1f9eecb1f6d838addc70a46c50c9d776bcab2003254338e31cc43a71ff4f0ef3ab47c64560829bff6561ca4ba26764eb1134ae41aa8cae0d43098522c22d8aa26daa3de11546bce548cece909984ae6091cbea58a942207a6c1115093e023e15f9a7b751844c08c63ea5c156235161b9981b3f269864441002772b2c0aee031803484531f8730ab97a9e0208e4464b7df5a719a8a494da4797af23092b30aad71b47b0113ec2d5e259cc66bcd3df2d0478db57d4325c85aace7bb6c7eec74a2f3c0813efcc425e04460b2f18a20e44d2f3512b2159c750364463763335648ffd7151a2cdbcb203ac46a7b3037fdbe2dc6d47cf62a83fc0fa83eb0804d0a05f2e15467ff1a7d509004b78825e573106ef4bfb3af903b1965bb0f1f596898d22217e2465893ac3be1c1bcdae629fcdf952750368a32dcdeabda2675a3f4c4a8502464b33514cffe2ac59ddc1e97cada82f4ee2da428b51390916be4ea29aceafc2940626e9b7e4955a0deec73ed1db44e36d4fbce1fc4b69c99355e79a9a18f9e8f058db6498f84193e9617dfb1f552b2e78c75256000eb1c4c678e3d8848eb658483a652eb1c76a0f73554ba2d6ac637023d56b3dfadae89204bfb5b52945b5d4571e0c08e06db55700cbe9e8f2e8d175d8e915c821e56d2252411583cc4830491e6955733804abb6514739529b653dc673916416f2d4c468d31831e1236b78d53c5c69fa2c52809784892984983ef5e864fb0f3c09359a8380d80bb7b881efe345a42b6a0896cc84c1ce435ab9da8aea5c67eb78bbd3fb19884c10011cfe723fa460b342c13e9a588a43d7fb4858cb45be62253bb128d773bc1da5a379bc701258f54998781b3aec931e989b0dc7ad654f0e65c179d560928a845f8678134d7b5180c90a2a977bf49e7bd5f4380bfe294d177ccc8bc1c737bb2a81571b9da00fb5f239f4738b9cf21cd7df2664a89c17e933905784afe36d9d2f118266dd3594ecd272917a1850fab9f98bdaca79a58889a4907dd26f854b036bbb6e9e390bd091778b398a46de5e56cf23c1f432c0c7302fb9f8350b1afe567fc755ac097649b63695be4692298252f80b5066ea769c4b4dc1b1bf44527fca7181206b89a64239fc1ec9d61d3d9f921c1e63c8e5c14d77ead88d5e692d8403edb2794c836017e9cbda4b58678c7462451392072e63adeb2f0f62ac3c5d7f8ec695299871e1eabb6157d5d2c784260332d2e43a25333a309ef325e83e58ca0b887dd38e7c99056f20afb85a88d2a3c44c225fe14ed11f3984caf237a2b6438131715e4605454345426984b1a973da1e067c807fcb0289715af2c2216d9b9e954017a5c44318a518e9fa1ab14ae7eb4b850ecf55ed0bfed1ad3011222340e6335c22459e1c275bc3d7ba9d481b23920cc854812872c176b3c4e802884c8787ac493eeac6b13ac65770cf3b3dce24e20bbc1cc9465daaf5662a61420f5ceb9aa552bad3f5c5ecfa1b90f1ae163a043532f09577a1b9dfa26301a225382b0fdf62ba372c30fd94396e072b0d8658e66859cb1c11ef884a6ebdbc8b252e9b2d8a93582b52e5c3a0cc42cfb1a27f3971d257750b955793f3fb1dc4183fd789b8b98f2b3db5806dc3738cfec46e1a60a7d046aab4ee6d18089213a3ac84b7b38073d490d44e9e50bb16da1db4801e9f86de6700eecee467a40e69d5977ab238a4c5e02c643d5ef99821d3059e4fef732e78ff704f0b78ac2ee9b8e91e7d01c3e7278f0788366b82e479cab3444c365053937605e15a1a3a8fe424ce16466d1de658630229131f91ffc2418691c636071ac3be53bc5ed47a26578b313c5c45b9e0ca4a2ed4069dc0b6791041cd9e33ad45cfb400dcb8a8daaa32952a7eb9781b75f8d5f3b7b23274ffc1b040d77f469aadcd4a3122ab14554e2f97cee01aa3f057e23d65b92a96135e9a86e2430e8d25236de6b62cd50530d472c983735ed2423bafa68b5e93ade1e940fe34e149c9a3163201f5203a696192a9dd5c02864e8e446099c1f80a77020c29eb971be91c6ead19cb56c4b820f8ddb653418531df01d473b52e81141600e820702fe04e775daa18eed528e96aa7ce3ccff2c8869d33d9a6d5e946bfb90a1054bc738e247a987b74e6edde584a314e4fd3e53a8b781c1b530f70c954b668647c45dca929783dde731169104f2b27652d050a09ece1c355bff97d541a14492bb318c4498d0a8398e2dcf0590dff460b9dd1c99952bcabbab0b6f472f045bbf578b599552af5d0c981a72f5d0eb76c640ae5f1f6b9920377c0705d66e629dbc95deb6fcecc10df296f8c0bf807ed6370c403dc18bcb9bc7def68c02d7b9bd45ce79cd250b5a52c782a58238dd8b6ebc502bf6fe30cad61e0c53f8a9625ff34dab8def1f4f73e0f4db2affdb8e201901df9201a174a9ec27282c6702ed58477d88036d0b13dc4f482e7708f7e1ddc47527ee1757de3966291e5fde9d8e5600ffc825d8b40e9e6a8873b4cb3a24214be02edf12dc2b5a7fd8f7a096f99ac2e75b09604fb4c79714efaa28e2c4755883caaa182353154626f11528ddddcf1e948e643a754c8a7b24ef3fa3c95497e9fe4130009c3f50e8214a554656647747b93855879e7472d4c1b220af5d4b1babea26b2db3819f7fa5fe5843bb6796f75b3b854d5021e737b5da6a9125908836a8cf9e48987cb502d47230cf9385ca6f2a53eb13f8081024c605a0482c7f324bd9842ebbfd58d2d1c3595e393caa1673dc72a801392a43751f8494c3b5f0608bc61e209afb6c2d5430cbf37e0558c2789ed2b5daa481f02ea09403facaab28255dc5177bde700d5b061aa18e5cdb3db53938742b531a933f187b366cc0779b100d8a88b31b92b48bacf02c33fef02af021829eccca2512b8a4eee502943a6c9f8f1e2bd3ce0c22d24b3b1a03e957072c109e8f4fae7ae602e1408454f159256e0ffca1734d3c381c01c149742b59505f221234ee667d0d7032f8612370e02a019ba30e7c05666425e1053dab3a442dbbef54797f44855968db2cfec550bb78f656ec342f0965f04f43a889457424d50c3209fbc491851837a8070648b4999c7e7b47677d10c37ed2c5a368fe2fea7deb7aebe712b3b3a7b2eb4ce23cba1d28c9e5e0a35892bd05455bb63e0cd0ac3863dc3625bb944460ddcf6cff6312af02ba738e9d234ee32256ed904b3372dd1be6814cc9880cbe24bb06ae6f3923f86097688557e8b8ec44f371275abea94b8a24fc353f243a402ad6219f29784f9b5e73063e441579143941fb28940cdf34df8b65f656a1b5d5a4b19350b1685b9897595f6a44116e617beb2fb8ed51af594508bdda9bc5c0b396073460124bfc01837ec7d3f992439b8202caf4741ec692fb1b48361300b8d8878b49effeee0e5841162ac3978c71af18ec29690751da2c81ac0c359e28c6c47101cbe743d95fd735e332e9cebb849c5f0fca6c8a5b2febc401e4972f23ae3bcc94b489e562f7d7da62711d240c80d0594bdb77c13570386e329c03731c4c633f9a7a5758dfe49350b3b4cb5a930250f49700d470a36db27a0900f745b126c73c445e8bb82eb88dfd89f4ca959718138096c04494304601d28807fbc47c9930d6bd0143a541f1161312a8c70338272dc20523011bbc6df74d3f9448f0134fca516ebc466076322c3c48ad9f1763f0a729e52f1e9a4dcf02a6e0da28de962193168bb5a2da89d9daebb4fc6f2bd590008b34685f2b79c1fa2112680f6079b92c0d81bda8f136d4d68f0346cf594c4fa916e2077cd63c6b43ae37d3fc7715028340896535ec42ffed9c6e7ee02765e6f48ec3a55250564df0194cb089e6cc49ffa38735618bc5d219cc07be041b3493d95f0af0a8f860cc3360538861a5f04ba46ea1cacb8c14f5c9af6036c4458bf58a6701f3b59e04911996b81e49f71d37a27b8cf00316ff9a47458cb2bc45e133871a15aa91a0e61cd3214d650ef4c39614f48d72d2e479f08b3ceb4756920d54103490b09b57650c680db320009961528ac23b9043da40bb283c2d110bda4f9c0e85abcf6abae02e1872036616055d511273b3710174af3bafae5144787c642583b506c1f5c171484e02cdd2e23779274e770ae4cace91ec3f0468cde38676ce1d8f94a1982bedb5f9afd02415bbf0efa5f1daae01ed8b4436de4a5a2479f7a7c63d171b9075bcfa241877882383879d07d58cafdae8f3193e4b45ce0e7dcd098f0cb46950ca25d6d7b959a312b66e1010e895d1e43d7380951a96dc866aff51f03958ede9795a42d3b672491c91107c6bf38b65301913911042be013c89018b498d2b8008b53a81b1dab98ce75cde7ba199072defe5fbf8dc0035524d6121888cb54d8ac4aeb9fbc7a23a45da08fe1870cf284d60382cbc3f85ed8d3cd4bc5e8da013611fa3b06624eea6e9a4b472e012b6f5f7e045b0e4319ab1d3ba17d497bf1111622c49a5e6e1a511b0604b83981d1efca746df8df15a39d67f32fda5d0b9f17a4f4a933d793ae81f42c2137c7893831b02f3993e2f54c94db7ec75842606a9aa289e1dacc8809dbe40b4379ccc5109f895251fdaa7f8e64baba8265dcf9090710ef063d6883024f8e8c597af5dd2ad58dbef63c84dfdb54aabacf77f52129b90c98a6be76b7bb76607c18f38c29952c9e5b6ae2e6f3b59c7265103ad299ef7d280ab3b116db06078bfa3b4fc83088c155113f220abd8278ab24a0b6a412809d48d9d80bcea6c1f4e16a66f98b6ff0cf056d99bd63f66fbb41e524e4f92e22dbf96cc17185333027c728046c5748f40da6a6e64c8fdb8df1234ce8658c28cd6e2cc0a5ba4bbd836e292a8eff93e834d79e1b9855b3d764f292f7c54ee80f4e3eabe06359eda7370398e5155d0050c71ecf27049aa36715b30c1b7a43d0175c7b6421724b2b083435e189a3127e92b5882b71339a7ef047947e287b6c3058083365ab9f0620a053b77604051f825f80669b3baa2b3218ba59208702583bea93f18a790287ddca2e7fbe82d086281700abd6914e4df5796f38bbd5a1216fb789dd94a55e9412280583ced63f477f4f7709ce4019e9dad6868aceb02a1601459e93f656b15178756cda9d782a6eeb379fdcbbd4b31eb1314c839a051a6f8179acad58feb33c3488a60da715b7263479f46a14254616cc2732ee28f926e4c5f561928d9817e49f834a71577d21601b0e2263178ff4b067c1df21ceeb28d60bb24ca61f9defb9081000c8acafbd0ecc7300e32dbf7e8c52662639e486f3c4854c04ece8cb4a0ef26fb97b8c53215de44f63f8360e61ec23450fb099870d098bf9bf177d225aa82b42182adabb53d2f880c9b03a21498c56c2af0ecc964d3468fc5f14d4356793c3c44dd43a83429e58ea51e5b62674b4486525a0e2cddef739483e5159c9220efa9b0749a13892105d895ae709c026727a828b0eaee1f6b66f182908024270406000c7bb9840c0396e1852576418cbbe3e40161d29a541804301b29ff444a858d9c3168c9d320629e166c20b5ad50365c45200f03103ac12d62f884817084c2995f136d6bdba131417aab20e4a68ae8314ae72b61108d89a3e821f14c15941cbbcd939c7a53646c7bfaab4ad6cefcfe8da4efefffec02b52c2385cad634f8b11608cdca510c2e47d737fa785ee927cf8f07750dd325219fb4411002fb082e8760bd4879489a3a2d8135fb7686ab0b8a96a633d29d7951a6015810123a82115f061c0010e504e46f99435b809a55534582aa019773519dda85db9843151f9bce7631b94662e433d726bbae06c6e1f60bb7f3c436a3fa43811a5da0a925ffca6487ce748d4402a8b42aedab7877f40d5fd45cc00abb18db97ea372acd307545cca183ecbe7a9297e765c29bc119749a0499f9c3a5dade010dbc06e3b4892ec8ffa2761be6c8074fb804da63de3f9e9914feebfbb06ba629c87a193a6df4beda7097d23f379f5be6dade792b79e61a5f4fc5004df95c07c2cdca3fed2f7f26bad6dec8051fa5e499aa69947fcb02e90f23b0ebbeeed329713a3dffd4725e7b540375b031c2f2839d05bde442a004ed7cac8d93d89c160f2035c3d06af0567b2158e5fa0df20b4ea5b1ec196443ac6ecf852512f8665c311009c3f428ba520a2b16a18bf5a1076f49bc8b3604b9e7debee2b1697936b0ae0142824f8475c8e99b062c9ef3e1d6351b738444966b4840f8fe78c03f94442e4f24494ce0eb6d8c49c2f56b53d4ac82c734a2d5eb3a325216feef5b5c9f4c44b0d168b32f0d691d8a82bb2d98f5313df9619ecad8238eedfe7aa206256fa36b952cdaaed548bece4c0a7d6fff029c5b165c4074d0486aa739336b6583cd0ba62c20917e19ef7d118936195bca71bd37c32ca12ec861a4626a31e8bf86c87a40857697f4778ef4855dec802f44414f9019ffbbdc18733d4999f0ae561f26f5debcaa14ac5211b5c661f1f233c29aae603e0df3862fc5af0cd30991593f4ec39416f4b09083ca0745c07064c9acb924cb716bcbdd31aeb1714d1839b42c37693f830f09bb23e0d415ac2fd796dacaa276b4272bd7cd52c15de93563046c0b02a39e982d03d74065d17de7f2dfa7bee4d6da88b032ae958369a12595bcbc8a5aa3ddb93de9209f1bb60dc0bdbddaab98ea6610d6c05c891ad5582e9b94dcdb3552f031b0ae69fc48c4c574715ffe6e890a8d623e1222d45b2ac03240f98fdde005e25a38f43c99fe21140facfe2ba817b42777eeb51295c1a8eec5dc16d070f61b62b58ca2a4904152deb76e9937465d596232a2948a9ee36b919571125e4520a16274e11c5f2d5947593eb52804c3b5c801d6c698b85beb33f24e0f1c5c201723d0c7415c8c0f3c98a0d89711f089e6c6377dd7e3f90bc101078a46565d46d1e394cafae32ad1b20275a44aa0e7225ef7574f0f2ae4004eb5d89217b7ec7b17414febf6e4748ae328324d77a5488973592e538fa62067d8586df9d487dedbfc781ac65ff5952134cd730946d7183901a26bf21a828be2384e2b0c1dc8565b6a30a1cec0abda730c0a3c26bbc037560c786772d7bbf17941e15e67fb31fa9290be891f8677f6411bd6b224bd57061263ef083856c9cbc1d310ca43458daf4ed2a85d807ac4003e55ed4f49d6d0136c985132e8da9a099398bd51c860db1b9d06357c16f577efa798f00426d81bbabeef053988721568a9eaa95afb31acad7a8de06c5f10d90697ee1e364293639243447c669f9be4f4b2a83be33b1620ad1a551e34c7baf85b0cff3107c77b6a8d6239171dfa8fce0a878bd139a1419d5705f7160b18733bd8c49a015e827dbb531140fcaa12a94cca75009693007edfb9d1c2b152449c0dc8529bca6421e58b2e31e7064453cdd7de8e7d959c701406c0cd03f8a9dccc21e5e5e39f5d9c06ad90240a44b2d9ff30749c829c20d9cd8d20999333fe2cb4620f8664a4c5fe1007df659d3787752778103dbe45e812fc2939266a8f70a63fbe188307ec79b2ac9fe8373054a4098229249597cda0cd5f31044fe176d25b575a1948990ff6c21b6204f520fd7d504083c39ddc319f60a6d40c09eb18257418271107998925120e988b395a036f42f8b7e729661b8cfdf0fff811903cdfe118aa73af7a2a219c1d1ec131c041ceefd93dc82be0eb230311cc70032e71bdaebb5375cb463b35c1f0bf29e72aadbb829223d50e103c07392c1d10d3c607fbd0db7e95472abc8e03a5dc9956861ca3237c3dd75fa3ecc644a6865652f63224915fbee0e08aa8eb09dabe0eb3cdc5768127fd2f2a15924e7767bd5f7b2aa822a6f59bbb07547fe728fe9e94cbce232b18685e1789df02fcd1d7107be82f6f5e7f12ef17a74e058f1f00aed4e3cb7ec68354ea579200859831b8dbd269a56246804cf9d6fecbb6b611e8660bb2d58331abe646fa4b10fa7b89f1dc5c5951c77f9a7b4a058e146e63e29f47a63d92f69da1c773ed518f297cf6e95fd000ff73481e74e5fd3dc55562beff884cff13705ccb825b77cf748c6958b3e75d00c3336ab4dacedd0244372e50c0c454d38a94f9491b615b89205632f78ef76fe212aed33ba4c692aed1f980fc57b2ef7fca402e1cc0ef75a49cd9df92c3af916865d31dd7051696c94f7292293ca65ca73f9a6a1f14e5664f8e9f44e829c879709880f74f268f19d9276e36fac9a2d823aca9ca62d259704cac128172856fb23886681fb460812c457e388596020114e66aee282f0dbcc6d25fbdc6838253ee8ca3cb54b9d36628f3d41eb6af9629f5ed27dcb513b2d22dc0b52b00c254c3d39c19aedb5b14586bbc54ea825b035c5923069cbf059aae93563779e4bed2ba0ddce3a2452b075cfd99b441ffaf83f46b7841369d99439c711aec3efd64a0181b30fb88486982104b13846525fc87132cd201f4e4948f3e5f86c2982f0312d1ed7c99b71c12af5da2be0bd614f347d1f28662c20e5948388bed461378ad1602a88fca31469175bea8b608dfa3b31b6110c6576188c8751f9600f72073f8c40716390bc14d0be344b8ac1971b7e1f698155a21e48afcf5f817608e74143471987a4a3b3a9f45cb59545214e2bcfcc56a8fc89ba02f8b8c009beb8a04ba392340c8d1734db982fef3b3f887c3b85fb85b848c33e16127fbf015a897225198bd01b744d59a9598837f90e0447f103e1b83f62b7a2c95fa9a7379fc29ff94c2aca436cb796cc038b490aedadbd6e4a7a003e817819708cb378e7913b1533f5744ab22796a6f95c6864a49dc12c260dc1956014335f51ea84024296d80217ad083789170877e04e2071b12ff2c5e13605041acfba96253c463e6b427df16eda6fe2bcb161af2ed99c72909e6437153a4a633304583f10f9609e62669a7f2c8f84abf164f1f97234b526299453dccd1930f6e532ab3f636ec48e1d75163ed1b16a89ff83a8e112fe9c7e64019cc8279aaeb0cc496558b1cb95d5b4b74c5b767338cc58c76e537c7fb607be9d27d73086290843ed4751caf00162a08c342c327fabc44448cf7a81e20ed6b08292baceed0d728165c6a299e01caba5ed54a3963a49990a0e0950d0fafac2e10a4085615dc07108220c75eb60cf001fc00285ed75d804415f50f20439f0c2dfdd6dcecdea95d0a8a8618d802562f616a48e82f761ef0e96a7042cace0f08fdc6f9a27c4ac58d7678fa537ea60060839a6af0c45e57c1c41339a89e19f63ab52b15eea97d4a7a15f2edd37001bf8feff3449b662849d4441897882855b2764734483196990d9ef5e3ef7589e6a94e00a8811871d90b1b784d5bca59663cd0477486476b8ead193501c5062b761c4bb57079d16756bc8866ac1ae1b6c1c358d885bb80f3aa84672eb43a68aec5e1b003ed7925cd2d201cf3acc1501ac099245df859d3de19ece73de2bf6c38e09e4fdf6aec516134919012c575e88a3cb5cd1f47d59d0b33802b2f4769de57dc5424a8120807ac76cf625759dc0a83485994724bb81c1e4f0d92043cc4fd803752975cbf14cd70133e08996741d5757b3ba99f9bed4d77ea58e91da5c28aed608c60358024c465a9ad04046e27199c22e402494ee13658873d350fb23956422d5d812f04dcd98f3e9b8d2bc4c69cdf2112ca9cdcc95d5eac7bcb240c975d8a5a650fc1b0480314b5471495429b68728fa1bd71fa2ab9ddc65c53ee81a34914952878167eaed24f455c81730d4f41ebef9b3614f19b160c9b1bfb95ab6bb00cf461d786ac24e31153d4da49715d814e66eab10035ad257701704314b305f745ac4725a086efca6cbc14c08d25de4084228109ce68e402d5c9a896a708b98839e5a0d12561c1e1603e068f54deadbd6082d179437a22824f458ed212a5ca93714d21a763b537cce97130eff27b89174d9ef18b9284c795a9cb8b33d0840cbfcd92cd34a370ba967089acda6f2b2656e3a6dd4e8ef91ce8f62fdaa58a108f62d7abf4a7aee4b7f04b6f5d6ba9da347ec125066f19d20d3b42dc11e1bf549bc5ef6b363bcc573e91c4fd0582b0a181b7e4dd4fde85d716765592a66deb055d4a8e05608f36e67a1f1acf64e2034dc07a9f7c0a146c0768c421d47d1ed69a3210929e7ec61b990d770bda57af71b1ae98f8dcebdabe235096476014263d05b5aa113069e784097b4d5031c828f39840fc38f0d7a69b51109e0f8c441c7e33ac675aabd62c7e23a662cf55c15bec234356ef6bdaf64a854e75393e97b08fd6ad8f9ba57f79282396a229a1ec68fa906f0b5a6f49c182a94f081d02392cccc1ec755a4d103342caff80fa1898ec62a0171c0790e84ef5f39af825a0db528bdf97d4b2540a5eab0cc9c67002830db4f8c18456d1d79823a56a93f988b9ee943eb3941c889551e63888f4c264a9af31194f72e579b4e66b7cce12f0137b81be33e5b16bafcab0d1f635bda31e4867192aec1dccf6f17dbc2dcf282054e145cfeb5e93949dda5a98222a849dc0563328c3f6ca36a9801094b8e88633dc1eb7527f8178fe08b1c08de287bcf16c8a56906f40c13cb2a73248ade48879ee77c3099823db9a8ad4030ffe9612463b1e9e0a8e53e9b8d905d2b3c8be8ab321aea62e327a7310e24491e6773c66aa92262a940b2fa1bb2c626be2b05346a15d73a32c8947fc1e71c5870bc7a48364b99417b5594641727e5eca9880eb3c568ae4d9c437d1e36fb90b0e4cbb44035b8a602240eca9223661df00508dec3212e0e9f0a529b70cc1041c454d5252e1893edf46af65d7a647baf089ef73e3b6244e1cde6c9eb156086c26de767b123e75b40ceebabc1ba81d287782027c69e5a807e7840c0d692200d236b72061be52ccf89838fbf2eb5dcd7082ca0a130765d9d114727950346c1e0bf198d290b5db1d5132fcb9dc596891bbabdeb0ba45528a529dbcb4f66909179b11742e9feb6cc03208a35a767f02711cb5a02ef1bff0366223d155a65b9a03da55d9dd92b5e56dd242a4e9346c4158a98dcbe55ae41a524177e241ecb402e226d9447b36320ebc1072660f90932f0536e0e75c11bec8340b59c16e6f68a9265a41ac018d0f75b896cc45b883257bfebb5661ac85ee4db40a5aa1258e209540bf4d24bf68a9d80bb64d0d9a6f5a15235f81196e38c11cbc0d90fb3e8053a13a90b3438285eee6bbd741271ca1b733215e941b1d46b9e9cb9b308f1d867a254ac35e6e84cee3cb8d12404c12bbab5de6ceb924c3967ca420b31818ff29539b0c6181818037cb7557844758895337e5da12aab8f21b690c352a9de50e2fbccf5be993b4c05a3a700655926e33139ba042f06003c981c189a56325bb76af9324b469bcb309db3539b5c37f67e7aabe3644bd152f0137199967fdd7c2bc2285b0e60a3913a923e530e267e142c2f6724b4336b7baf2a9217f4417d588296fbe981a565fc9d591cf85fa728cc4d0fecc9c537bc3d5d8799548be41718feb39e28f21aecf3063b7e3dd562bf4aeaf5336651da86a7b02ed4b760c6033fae903045dd6bb7621d3963f3843d6584c24a4c6afac14454362949a3a05d137f91129e3d12cbb6d61dd3e491486c2234b2d8e9d0ffb99d9420fe88a8344fb6ac71df9e8660e0ae8fe3efb9c4ec3d1076b988bd9cd57bf03ec6973dfe987b0e21a1ef1433c00d17c6b1dbaffb1213ed87e49b2ee04e6d9aa9e55b8ff12b09d24ada6168b550cc38641829bee8f0da276688e82a91742af49d39123b65c08d7d29206b0c907d41d8557cb06fc5056f7a2e149ada448818e29e95a69be5a79753d3e1c310c700b69e95f4f9e0f107d4f742e742b69a8a3b7bf3cc5611646ef8cd74ea514a30ce08d980c88747609bc8bdd5663e1f6992444daf2ce3a435a5476955352ebcc843f8b677510cd9f8fe7c86b2bbe5daea3fe845b5b5ca0fc80e0e857559f9b4a7acf740bad3f73c0c19def9a165793fd44898e040406a800a58cab0d09469452e655b417759b3c746abb48461384ac95b41973c41a034620bc004dc4ddc8bb60278baf7e0374cb893a2deb86c1479674ffc41e33b8d2775fb637ca6bdaf22a807a33b9a0084a45bb631494c8ccb3e9b9d641812f35e1c1411623b02f639f4dc94cc3adb094a99f7175fac687d1a8f2b1c54f27a46ca8fcef8a88b997a84253bea8144c5552b7a43eb4905b15650f06d4ed12a8ab8fa2264c9ec840e1c4a8481b24de699da72e160ae9157cecc5fbb5e8f6ae6d17ae8488649bf6fa8114f0ce9604bff4dcd5d8b33d984d1d87dba02b09396a561177c278061f9092e857789a2b00282b8265c105cc463bfa67d2fe7cec0a5867bc6bf18dfe99a64aeb0cd41f784a96497244cb3339960a61d0f5065f8632b90b7dae69bae6268cc299d4fa3e3414150c5d39a9e9b8b0087a8af49801beae2e92f69b4504b339bc25850cc521bc2f8700fbba9d7935c6a515f8ef27d9f6d6028a55f08bf1b736c4c40610bb9b149b63533c27ab8cf2d821c6dd33bad3d0460458ad6ca024e2b06b458cc23e5cf041e46c4dcf9954caf885f1b9e7111f94b9a8873318837c53321d54fa5395e0f8d4e8fbd11a5701e370e486cec7477c69f68148ea40aec6aa1e09aa3083b09fdac2967129f02f92f582ba2696a958c42ee394caf0139945dfc083d4ca900ab58442095bc30dd483b926c9337192e1c09bd0e8e386389e896e803b5df5c64f662a4a43d251ac83d578b92c1a863b5b3d23e2f6b705b8bcf05de701e9f1c6f9e021211965f654226fcd5cadcbb393a28edfae34252c74ca4e4be46181116e2f9f53b4ff77f1cc108c0d6919fda26743573b0f82f1671a2ca0fb960ccd64ddc15c152b8232527f6b60311afe55cc130b01c31b85f30b585f69410c9b17e77c08ac005f16873cf3ae6d00706da08da07525eba672e8b6860e58488860148ffdca0112cdd9519db09f83a3935044d8216cc55c3d1603bed2f14735413e5a43438937e9ecfe1833bf28eb4d20979308826593f12eca48ad6a0271a821bd2dc21735330daf9a9be4a5a57606413755f0d1f8e9b11e9b7fe47ef0c252f83d5ad0c7f05434c582ff5d90ef192d8148632e437bece15a40546829b4b11aad6de0a2ea67b61405a2d88242c357bad8ca2cc388ce18acdb974ef3b4bdc17f3e9e76d55b2095661f13c2b94387663c0af76e8f45e79451d865e6dc8ec8ef0a3ce8441a46bad96628d2b60d614cb5523bc2fed4d111ab1a1d41ac7fe45d3412591458733a3c7d23b65cbd1fedcd0bb40d6b4a02ba891c71a147143d50028fabb05bf1d906bb5980daccc401eadb35915221660647c3ae8fa23928d7758b7ec530aed51462ee49bb8ee672dc25e78ee2a08474bd8621cf28d022abd4ba9fdc137d0fb14d867c9eae2f2fbb554eeccc9685dae37b5e93d73ff667b14f09b4b435b2aa294162a3078c8d7097d4153bb1dc17cfa65e575af652b341f11e2d6fd954d03745937c35a7f363e650990a447a15f39619f257a5ccb9a19160edb73540f58f4b06461bf96ea3e28d49c38549adfb74682d60c1fdcde7c05e363bc0d6a5458c96ec62b4e90ef8460483f33725c047c35eaadb0849461880238bd6bb12587d0fab39ee58e51eb0eeff26342354afe03079a6091de7b3896e8e0f42bb10798af7a2d388d581a98a5bc42c35fe986d971da53ef0be7a81128a97c9615d0b1b99403a6109b1c0b12fb356bdcaac5cc30152ad4ff1739e56d8c508171abc20eae9c17dbf36d72bda3c945439beded8c631beee7193077006c6d339e35d09c407619d796daec5cea3b1d1af56174befa69a123af057550a1f488c0c9bae4dce3726ab727e83a174cfc5fdc0b3456cc06f3eebdeba42526676a14aeeb1807be8f25e91b906157d7d01dda5be09f893dc066a554f865cbdb16bf37f420bfccbb6db6274a82383c6cc0c2663bd200108c7344862f4690e92d7c8f00aecd31d8c46172a1e64af0e72fe9f3185a57a71e814ad5f0f0d33f7db151018ff3cf7144c6ca1b5654757449ea57d9c165b0386105717498d620839c39f85b1d01228153bbf43bfe1b51ba9718f0108fe1c0a09aa23f9c3d83326a65d280bc6843c6dc4a3aff32e86443923dee6e21340fee839a99425694bf86d3f27490e877f87981767acb3c407200097ac2108093e6069b0c65fc1c8a4622d8f3553ab77213a68b8a65a53b2ddccd69111dfba6c35ad64f6bccc50c664f65cf77bb85edaee120162497022348d864873679d7c6991f3ae7d445e467dbd9dcb45927c7dbe358b5c31449ecfe0d217f7913e83497fe781e4d22554577eb1ad0b4162bbb27f8a32cc61b50d4822e493bb0749448100c30102a10e50ad2c2b443654187f34322280292a444e6a4f5e38a8809d0f470cf721e29b46be30e59018c863b9e33826395679098ac5285882fcd6b6e90d0e3b1cf3b47cab42aaf44111c25885dcf068e4311efc18606a885415c9befd0ce3144f427825a8937a257d529819a237434fd7b7593e567778962b9a195e0b489f86417d63e2987306850dc0b81511acb5b605d5831861dd6685b79b09fcd3286e7a4124989648dec074f41cdefc3b6e6ba2037b3956ba12766682f2663696e53680b2ad68ef66a182d721e95d4055372b9b6f0cb20a5e0a93a9fdc03e8ea9d90c36e5c1de148378cc8441a175f7fa2b132c8739abd90976904982db0164cbb92ad3d2a8311ef7d7a02df5096383aa043cfdadfa0decbefd8b1aa7be45cb49a9b43228dd31f45018d17bdbaa7baee1d7fb6a403536d288a72aa4a277ccc6c24cc44ec1ee699e7ed54bf465bcbf458b6b5abdcf988adbbe7e7c0781b7e277908ff99487d74ddad48ad6b0f1fa8b4084ae851ef522b40ddff9c43f01b1d2a7247ef8706f58e397a444afa382ffd05cfa4a6c3d3d3e089d9e9430b8666ea02c5e458deb0c974441e73008e08f45f678334fde926d4bfa5c3e981e392949b415e67eeed162ec6e9c23a1767f33f74b7518a1868f7988f142417575a151096074d80a48b25014be6e60344fac207ef408a087a7f76c08de33b718128de5a81cdd11a3428eec14926c49d82c32a9198c5d237e62ef6ab5ff73d6e746cb6e5dd22873f7e6cfdd19a9c3175975da1abd0f50319c20b78fc1134084460c4aea6e687913fe590d1af85329a8a8c48aecfbe909287b1c46efe93b820c2da46d2d77c6c39ef309a2248c219771cf8a138e448ec2a5efcb89f01044d6bf12da439636671931b1cd53eab02d753ce8604850a5999f4bd05547d5cb57e1081290865be4854ad50098b2c8214c04a39b7ef77d64fd9c83ceca301058494a1722bda91dbcb1ef063588686c82c4de671fb2e2b093e40ec616f5e640423f96bd3fab2b15f9dbee2000294b473e099de8447d0eb5309ea2af755c6023379bf91c3151593bfecd2c0cb34a8a97754f398b8b0a4bbd231c0bf30bbffa1b0c908dcf5a4696a517d7f144fd8ef82ec784225466c9b15f57c69449cb13652fe15378ac979f895e06751ab406b440662ef70db2132facc6ffbe80542c9059bbac35650ebd7b222d98effb024a8f0eaa81f52dbe2a46282a76159e1c2bcf1a6cdf3511bf8967aabab2e6ceba80b5cf16aae7f813c02d7b6890bd403533c24ab3841425c05bd644f4372934c403d37ce0cabc570eebd8c4db011860c4212206ba48589d8dcb418827efa7f311d2076566114f665f5cbc00fa1085af326100154a905e6fc322015ebb4d4e0529c7a703834e7eb6485dbb687dbe2249e701a27bcf42b49d7e92ec7e7cac3c53d4f82e89ddd5273f96d4ae5264923e4c91c049e46d16dde1ff21466b22012f1359b3d69000d99dab16ba700c752888dd6d8db2304ea5aa4994a52aaed08e5a6682f390e6154ad1bcba83d5688e90f8d41da8a872d9c077a97247a435b358773246143daa68098731c5d8137714b22e2454a9e8c27bf21a4680e65f1474134a99c1242c5b66e81dcfd3d2f451213f6b0897d09ee24f6b4d5bb0913467002d682a11170fb622f36b00406181636404ef7748afbfed6e0e3433125871840ab13386f79c6f3080600cd35bb9468eceddc7d8862506245c511ec4951a96193b4714357307cd78b18c99d4663fb0747c57cf23cff60c8149f56544503002668dbc16f59f2d7d01928626f95b92a35ad016c0a075cba16a5ef86e24ca79f634d3a9e41159c39747a101be661771895b37f7bb58b171488000035e427f5b24bbf7dfbb69d13685029e18a0ba5222d2994a44521865221ca64cc482e3911c8f2fb71184964c22cb39d80e638253fee90ecc7a0ba82215e32f395ed11b2dd140f393e53040ceac2a926feb8311a00f26777ec2df32f3670e486c421167a83381934a6d17848171a72f3feff6c20b339bf3764e2cbe20d2a801b3da4aae68e4994523288bd17f29113b1f26ea448002e71e267b94b2739583a05d7fc32e4c33d1121d2e10d2dd344af1e97602dd85f7215cc3132602c5540eb16d8738bf8b3bc80ebe629f16a887ffab31b03d06a7d0c05d876aed78e7a344c7058f51034e099ee11ad5c3ac1fba0e2300978701e420143426046c26309d6905509ccd0eb99257c728ab8aa077ec3656ac00b6707252557dd9fb5b4684d6cffe08fd1c3c7f5df976125d71fef7918826dc7f94daac9dac60841e078217407afaed550acf9acd21a8621087b5686134d04bffb6ee1a3979dd1fd0080b2c0c4d84af73d03f23d1538c82d064627e329d4b3b33c1763b2a8cc4bd944d4ae0921cdf0245382bd5d3b1c3b18bac82cb3c12fb9980eab3b4c4c928597dd39ad744586c2330d7daa3a51eece85c80a38db0c4d47f71d1d24474b33909a6d13d33944ee01303536e3d3e0580077c71d72ee432b3170355bd10808b4ede9999bd2871f99e38bb5c818a2a2959442d12cd031984288b41aa1474d06ea129a55a5dace3b6319195f370d31d9a3099e123e2be5b4ba7e123863f99615a26b9b128f7511dc4f8591c8db1142bf5bbc325848201de112bfadd85365c70ce7d2c96ecd8334c2c6efd6e3543fd153434e9815a138c1243d61432069b95d1372611802edee333960c0ad5e301285d1a02c19cf4255aa48f20ccbf0b7386f0221761e3463f442ce99a6d9ea3ded47bb8e9d246b5bf0a86eb161b233ebbe35414cc6cc56f6f912b47027e02caef7cc296df0dd66c0c3c83ba075149a27840610100fbb6e45273e6b6fef9a5f5158bca3a796b7c2d1ca67d021db0d13f13f5e07825aa682520db76db087efdf9ffb36f05686f3da6cf9ad6fd4689fcabe50ca238777b19f973219e5870eaad83824cbd7ef103ce2f584341e7f7838402d5d572cef013f2a6af1b4b52cca8d062d4d87509d2dec2857ce2653b5847a3eab69aa0d0daa24783982f44934cd93c1d56a2746f95859f493ca11f0e3f2cfa36e72315c7f1cfb46d40bd5ed9075b87814f1ba4da58936c95be06dcb2f1c3d547431da185f6d9615057215424b1b562642d47248563259222613ff2bf6da4d1e1dd2073dd0fd4834c4d6eba97da2f30115dbf022107afece16ca59efeded5fc8e981159c98c4f4ca87c1b04358e4c50d9469b2ebf56e15d2d2fc31c34213dfc4e2c26b516c3daa0a4f9ad8e5a5c8b4a9e951e35d3ed9c93cbe743c8ab1a773c80e5ce7142a20a88a3fcb4789d6ef10bce4071263b815777f61adca04903c39c886cdd3cf7c536c81ba01b9e39e2eea62d6c85ad44233fe821c5d9839a321bdbb0c6da9b40308e7dbb65ad02d9a64c29df75d5e9dd2c75615da99d7f6a41564d1c41d4a6ee973c68dfb255659b500f4c6b31961e60c4be5903cfedfaf316045eea71061fb05685456e8edaf4ae6bdce5e2bfd276d6096229136e53f3d003c89aa71150557b39b1d0607851f268a42e83a700117b927966166688129284d107f9a8c54911232e573ede366ad571171ba30952a23cc2c6aa86fa167c8c7917cd6e9441a50b3efce0c3badf71bf17460bf019b4e658efd9da783b58b0fa85213adb7f294795bf6fe87614a6e59656c061ab81525528b223f063c15866c1639126c0ce60424401591fa2718e3bbd620b8ec0c1a3506cdcd800a017dd9f5631cca001103f42a98ded3ff7b635544983da1cc032b8b2ff8aeae8f950aed424472c6922a141b004033ae6a66e80d3a560f3b25ca02c4395f739ee76b65d5f5dc1bd76f449fd6d31a71cd8d27500fe35496e7c90d543d1641a5bac190cea8933429f83d10d86c8b297ee817a4f625667ad0a4df32ae5f7d7e42f0fc388ac1c83a7a55ab5377a34e74335305145308f017baa3f01e0d7da9f2a3c4c645194612a7939ffc06d4e6b633204dfece4b7feff7a3302d7067d57f0dc2f1c92d22f7371f21387146649d900e79430e61201ea26f16190e7b23a084cda3ef38710568716e1828c30bc6ab5497a0fed01fc18f9673bca9c423ec9d12662062c835eb1d295a61b86b0699bb7696158a55077e55edba4606bbad29cb3dad7d61ac94c7b4b8d68218c25766c0162021720a8711dad58e3137ec5c787e71e39824a626e6b5bc45b44767752935bfaffb5ff2041c77d7350daaea85dc0d5b3e559a1f595be77cf275035c57050cd282c2289ac3ecd780e13c9634237d14452ff5980f1c8cd59129ec947b7bcfc14c833a69404a4fefb810c3c90d49cf4d7a20fb057912857511917c0076d9b114921823166dcb73cbdc6a9793162531fe61d22b95a7f51d7bcf4c16ce15c57c0c080974d1273551ea4b3a20f32627fb02630eb71ad75c0caa82a952eadfb7432025c3e1eaa235baf75936ab341448a3639a22e1d7478307b7609befc96bff395b134ed46e84c444311e7e59b21b858e73c6a189187eb0c587732c1dfd1424b07a64825a2e849ec95f052602e40d3db65a74bac04d965d9dbe1a7d69b9a713596d0b9109589d303cfd13734c518cb136cf0b745780824c063abe94da2030f8318648862b5e8ed633947c9c413f6494473566420ebc0b2851f8ca80d4849867d36ab36921bca6577421e43f5bcbfe9426a4584baee3e862df16efddc897c74923d583534587178b592c1d5808c9a340fa8f6455a0de05d79901244ba71234e5a8c54bb55978e16760702e67090975c35c281c1f91af2226f13ac16c8551672e342c68841dc1ad687073233d4c43c57706b0b21375371cc4d1c070d0564babe6b5fca49ff301fc9e834b0aada02c5e56244e30ce6bb97d0f999036ab8cae735cba4b7cfbfe695c454ca258ce2168121d214fd00a51e7c2ea987c6b50d8337f9505710b001a12c7af0da7537d692e74e93f984b364b26771cd2c5f586a43b03951c7a3c074a9b113b38f3b1c09cf367a9f0945eb4e641a8c1a7c3d91d2bbaa0a3088a6badf5b3f71e4c768408a3073dc809c123e01d1c325669c57ee0d35485e395ee628e60c60b5a4549c7729c7e7ffde6cd19dff46669b3c3e99be0282ab2b1126c352a98c262e608190926434e8ee7fedaa0507d6e72bc2314c5e5743591693ce3f4c128c19887e0ad31fb8b653771b2b54abd47e03d135baf6db7feecce315f493cce2f0c2364b90612c2e1123230b26caf1a1e76a20bf05b775b001329be18ebf5341db6e1fec7ded1911594c16711c6edc8822b9927e9cfeb10e16b64bf250e255e82404010108c5e79c0e9208a22c85886b9eb653cb14a3db395401e0be09ed8e63051b761c0127d8f741379f0add898ebc84370fd77c1ecf153a20a1055f3c353f558116e9d464869e05398a5362741780eea0a2e25330a2c97836a46adede1015a60a3450d62a5493db240d0e9074ceb3d61a08017981db12c3268eed50dd489e3ed72b49cd2ff8915689f1a4c43c5a12544eb17b03681696a7280c39f19b4f67d111000fd343fc8c63c290b694f3c1fbe9b54aaccc1043dd86910a603464d4573ceb4b1f3ce15146e0c2b3baa9cd910b42f56204286cd931b834d2b43373a29b6fa5eb80d092b32f53983ea42e744c3adec58303a2e79b29954c1ea11c17be2c3c2b4577fca347e2e3ae94860b77d092767a0d7ac56a40d88552ac658d19ed00f31bc1c1b9905530746242587a3a9fd8015f48312294e9494d88fa27620a22f81bc294197384804db688b3c1bff37854eabb9110808715006ad7a9c8e4aae3cbb66f10dc454800692265c7f449d6312be4222b5e3e2628b6deb2f68131b3145b81c6b95434d698715eb1226459cac362b2af7445f630f2a93ec480cc3b81bfa24bc6f177c2e60174f4a74d8c445e5151b4499a03d40171277243d06dbc5d1d064c4e327d158cec2c5cc2880d3e4389849ac5663b94ca976b991d68fb600651e62534aee4244a8044d87015611e28ae08211b144fa8662ecc0ae7d05e42302492884c645b76bacc07d3f141b56d9782b6d0d47ce7865cdd603c305bbbea7fcafcec2efbc4c0a8a8145cc0a2aea91a8717ca74b0e789c3e6347100126175a3b3962cca3ab08c7d3e13c051ed3f77bc39e0eb6781b7c0468c020274f56052787cad6b00b6309838122e651aa6082fa78f5f468ad81c27cc1fba672b8b5e51da18de77271fd3f276a31a2735d61b50d978684df9b33ab023e83b36c35223761199c003113ea1e456369640814952ea99c279c9373721cb49491741a612bdd113225c66c05fad8d01e00902bb56690da46344ced576d81e5322509537fea6c36048a7a0c2447ef63679f23ba7b8233bb02e00489150a6f3b3dbecba976c05479ea8b949d4865a1910f978bf82e3da0c2486a14a3aa69d8d86188783e7bcfb26c621f07f35cf1480e540cf0f63a1a2c56db2fff45bd8511588b4aece27319d97b0e429e5aa5d3ee7fe13d5c2ff69d54289805a1612a30c35e6ac093c1237e25bb5627296d052acb2c9e1ceb04c2562dbbdb544dc79ccb13249ea9dd6652117e67bc88c530e0241a293e0577cf91975c0cd3159366eea876346c3c730482448f43c9c085ba5ee3e4487a0e3481ef8acef9ecf26c21074c2669619c975dac97033d0e8f41156e2dc136c7111a653b210f7aaec3a00eb8d4f768e595c087163366704bf9da2d901c2e59d011d7247362edba989a0c0fee103ee00a9a7979d5de433565c2977d641ef2b53c8679bfa79ad2f7e93910137945c7dacb1691d3a504ebe826d6568cd18dbd4b09a4393aa5eef59da91aa777518664094a302d7def6d91eae00068861a6a4e0dadb30cccc2b6921121fc8c4af60febc7dd2c6e435ec19c446a6deef9c5000a2464c6ce96c0ee46dc9c7cdb05464e2ce575a19496753ecd83987b3c93f51919962b3195e33585c8c4df5389ce16b26a0c210fb9cc004569f456689c09d28daba2bf045168269b2d9da4cbe6f6a47573b14130adac730623ea4d19b05080c5f665e8173a1a310cdcba25fc66e7b84c7082611a2848bde9a8285162b82033fa96ccc8dadf3413feb59a44d235a4594eb0d26675684592f6f2028c9f2ba5e66810aad6c25ec8408cd7a2a981e215d525938d5aeb6b5b3b390e2faa109d16935e9cc44e818494830eda4f2aadc7819cc94b2afc2745ca9b3e105e4e8ac29e8ff1aaa16be53bbbc1e94d0ce4b68c388b88adba45ebcddf65d30ab90a7d89151d400772300446a21523aac9cf318f7ad6553d49cc61e494ffbaa46bdc42a249386ced8b53d2fc537222340333fb5e16b23defe96b0aa20110602acfb6b42e1886d89c33e8d33818c7028f41ed034d356dbe8f77db12c2ba6d55b6f2fb0ec2ad868e61e1a9abea62e7e9ed55923a5420baecbee8be368087c1cc8714b93e1124c5666d63952dca5f4657f249baddc6af4f5b8e740979505c15a236815524c0e973b553146ce727bcdfe149d499459e8869021ecaa96c09d0767cc2142792e8ef683885b39139424cee3205fe62bc6f0aa2c4c73a0ec3fe6b6df201a4b743af57ddfe377fd59268a14b1f7799f126adfc03095638fefe06716c10b5258e67bef3d268070d14916d1d7c66e7eed9cd7d7a64edbec588494aac36b1eab42e2c3bcfaa6e09174e4f87307b38e9db98fbecabb42db94bf635f1832de016132fec5c5f40fef4e56d4bea65ccb7c04ebc47404c6cd995ef6a54adb43e4af6793c798513c10c490cfbe88c68e0c4f127e9263cd1a92ccf2693313a061c908ed68b503527c96c0d94bbc689fc60f12a529f3f672616891b5dcc6f489033a571b8e88f589c6383f546adca452bbfd3d3527a4fdf14fed1c175169559964f8ac27aa2257fa5644f32e72175cde91fd69fc6132aeb8a14b831ba2da7deaf159442509f34ac8b84422a2c122176238c235549e07103d30537197c5bd55d928f52ca147aac94c7eec846cf6985239310c6a5c17200d2422f6b724858ad113c50adfacb1c6709d05229ca8ae630c4a585729919ff50c7362ad0620e94e1996f04ea2713a6761fef95c83d4b7cbf7c4d26a4721e10715c8423366befc4f01a2349da7a1fec9215640023e1519a57b988c60fe4c11dce5e99f12922385b5743ad71d14123b469f7409e0fe8f957942412ab48e4a9c1d6f381861083afc932b710ff069cff1e470a96abc4d2271a9b23ff22a585c4abf20a824574d2eedfb5fa1c3a56e3cc826fd7cf0f9f9094a74a083ebeda3b95641e78b72428a89fc7785043afddc3421a690066d27452ff9aeff9e4ca57d56509acd34a9160ac70c1156a2682b30e6904100ac1365e107bed84b6c0d7feb747822ca2fcf3efcc48f43972ae5c2261d01a24e62301b37069e3afa387c0384643eef2fe0b9e239b4ab44c68869e30ed057bf7859c08b8b9ff420a0a6d6b2f1996b18ac003da9d0cf18acdd44a5e6f08c2579a124a1fef977fb2efbf435ca65fbf069abf4691649eeb72f02d7c12976a1daeaf00f0b30d0c5dc084fb4553d8b28056db895b51dce5194d1d676c7dc46566b4152ad6d5c513ccf8fe19277da874d71e58cae2a3fe25a3218e478744bdb2fd7cbd4334fe8f29667ff9a61eec9456eb24b0fc25d808b22e7f6d6849d9adacc4d6c8fbc89dbaf02c06a61f783e8eadb1ea39eba30e5040dac0ec555e13ed8dfe590c008c0745a83b7b32d9a46f9773bdbe2a95fb96a8d5d2edea94015512393708f7b99f04ac04c31c4c2a4f51aa51ac3844930d3766cd8079ad56d7de2fcf61a2b1c84594b6b91c58bd37380d2e4abbfd10c82cfc6731811eb93e253d26d86db58fb9eec2ff3675e951321b7ac64d428f5985d6421b6d3618d159e379893543bd88946de334823e3af6f525c4972dd56f1f4b658c04ca5b7b3108eb379742d2c52a342362124968efb18e627671b6114e9de5c89a3584190a9a547612c8bb2ef7a8424757d1abd2bb3fb70a537d44b1cb1e666475e93f6b443677fe669351e8830fc56622092e0460e9c7b0345e45513abed7763655d44dbe0da934233a353b438eb70aa291739753fec79b17b7af67a5b2e978f6d42aaaab0d794bdf8338c6dd82bd2440a4a07b9a7e5cd4657c34130149c428664607c8eac8560b2faf9ae910c8aec63a623d93d9266a7dafb11d5067c0f40f2976a8582afa64924fb6e3b5e38baf2b80c496f4e3e9f7d8258fd1fa4d817ac9910835181b7b31afc0af2233d1db89a80b13cf28be3c6db99f65e7469e3fa7a5c451682f6fb7e74e5a0f7a4e8e2a63b477bf9d7f9c1a8eece86fed094c97a443a418d8b93ce92db44a6ddb71101a9effafd61f1e4a5ecb6b76b76388cb58d782c268256737396cd41c7f5981938282ba7eba85888307134284465c8dd257a9294e188e36a635c7bf9d08c10d26355f368084edb0111d6337755943ac840ae3a1f64b465b69e87891ce9c340ca183960fac0f3fd697e5ae11f28746c60f5f96622bad9f2547491087b6ea2186f75d66070d519ab8fc92f30ce1560fa3ce69c77d32f20ddffbc1121dffa66506037f688edf92431ab869689610a99239491bfcee6822f1bdb50be68d3a92d94b62683f371069700c9339a9f243c28c41e70e6a2d2bbb9cc2f96b52e889429189086eca5539f072669870c7c7f1831217df0105c59d558aa8c805e216bd97a978f67fd225931e7af767f4d922edb2e2ff0a651ab8eb2028675473cb13be984f8908f35511717c9e8d1e0bfb5861afbf94ffb06c94ae3e179a87ac2c56b6cb248d7045dcaf1fef53908051193d597c25d72fca44255029c166a51a2d3e772f6d5cd0e08225c3fe6569d01ef7c00c93fb9867e21f7d5341501a297247a44517fcffc8ef3ec27fff0cbd1423dcaadbe8d80549712271e7d668cfa11f694184bd0dfea86834a37fc0247b37a07f83982e884baa6627d2535068b5798b49fcc2462bed110590b26a4ca37e8a6a5438c8b6b0694b6e49b564778af8402aeedc72f554282223cd98ad21ea5261d33f78a20ef408a9310e6500ed6116c0ed32c8aa558becc934466061589791203e17651d9c0d0556e948b98b95664149306ee82e3662a5758bc79a6948f8ad90d0bdb843cd2f1f211cd426147478ef968b628cfeca24c483b9baec9069261d2aa5fe35443d67318af3d4135ad6781d2f94558eb5ed60ed21e0c52ad871871301f1daa6c4968b00b5d3ce97002287de9a1d10f9104a20a0f82f0c54f6268c70a0dc0716f73fbc75b5c9162671dc2a4052099ed2278fc9b147837089afc35f2efa973321b712f973494c1d6629995a8cd475c42f3b4034a57432c1b21a372b83a58602fb132c3edc154df331e580972a88923dcb73604123331855d4b669569739b6726609ed87e666f48f5e171359e60b8531621f6f76ef349023bfbe57b96b2a0a334c5af08469bb750bbf56424ba53e9fa006b2a212c0cb58b4f3abcb9c5c345ddfafd92f1a9d4242d3ed849eb0347fd64b2ea68b13bbb3e7bd98eaf03e0f539b2a5cd2aff144a53d39beb3004bfd54dd92a4ef71edd02e1f92e496f21435b9afe2406b058f79fd95626c16a9d483edfe2030233767ba2911fb73bdd535995af3f65088f73967d3896c7f23d31d518a292a2a0918a24fea0260d455aa5108ccb8ce48f28d7198d255eb33ac5be87cede16f3f1fc0aaaecb7dfc14ca53cb8a1826e0bf5d2dcd9c397e98cdf8711a8b78155a09995fdfd909248b779fa92874b15bad5824eaf650535afa1f72c127bb87489958834a4111bde5548004fb2b2c907a89858db4fbdf5504474baf96b3f81cb1da6facfd2673e74ac735cce461215dfd358372661f306d89f90a6018fad487662ac08a48cbd52ce3e726804092bab24accfce86e0065cddff1789c532cdfc535ee8b486ef845dfd8598e68c08086125888189334e60b64c5acdf117abcefb7f48626eb797faa96194221d98886b8a2948308d0ae6e4fc6926791a32194335d7e99f782595088c3fd2ac255d7ba820fa7f97361b63736c6ec9092e58ec815c756ccd4ad9cdf271213ef314fe2139b8c2ab8ab301a88f83d649fd6d5317d48f0fd6c13833ec0063b9a3d2122c1c0c73927e4e9b82e616ea41cd275ef6edc513ac495034c6453a9136b2a64167fa7e1208c5b79f085405698d84b31eaa2e9f35f2ae700ac2fe0c60d866eeec321ec709636fbfac26e11faa83c830fca5f8516938b7a198afa14dcc4cb1320f27a97cd2864c494241942d35ae298a5070b12094e51db52088fd06df52138b691f4125d7696189f306778d5f69eb378d888f76c76378c0ba211c98816826732584b4e36ccd712b54948a2c9d0a6923e4a3dd6d939bfdd15a29d576028e002ea5b2f5da4718a2a1b6c936871bede6f97c01f6f882aa959b00b322c9be72b4119f283043a31d11682af482eba698d382b92dda710bfddb41d3e5a0a5a6110d58ea595f03c46f604985645c7fb00c7a0e268cedb855e2a83c44bcc1de0445a82888d5fa07ca27cdc529b8a522e5d0869edb4f8f0f0bc5bc66b18e785c9b805085ac4866579c3d353ef3aef1c493b17d194c4c93c11971091f84eb2cf5e12b5edc3933738321043cdad85e25e559c628d18c884f015a4e31e83371aec50d68a52e82d8f7ffd5a30597805ed201fd2a268d231af0360cfe7ee77b8ebc801e8f3d9069088cc620740d39a9ec66c3d55de44ba3311f0c0c3d997f836e650adaeaad4c0d636222d9af3ac383b39841043044ae7a6ebc21ac4727fc58806776d48ea6115462ffcc47aa27d9f7615923ffbfbf266bd7e348454859bd16abe15f360d8f1baa9954447b9f72d3a342b2bdcf052044ae1e5ca403c011a1bb2164a1115e02afc985ed3d48596723fb5a905fc7394864f02b88341d5980da87865c7f415407dd24cead9fdb014799b763653b404e2b4719ad164426e7523473deaae54481c7183c25ca6852fe34897033d11afeda232f86d8dfce04cc22b0803b64fac10ae0cc2d49fa13651dc972101d11a52f732375a7c4d73ff8cd88e7e85546fc9720819ab37bf0379bdb37eeb433b8b5962f6e73d8a355b766944d2b7d1c9c4b444b17f3f6d80001949d5dda926244af2a41de1930a02375476c37954211ff04a3f91dcb362ba78f5d001cead8ed8f75e7666b723d91cf90d8cd470ca123fd776b80410c4f309f6c110df84ee160ecba8dad3b7557e9edb303475af673d0275df206ce474e7823abafde1540a16655fc0cd0211728ecc972ab0d242fb0717763d291d58b647fb5aad98d9f9a95037c128954f54eea2bdf81ae066f8538216edd0d78d2c218e924dfc631cce150f3ce0d9aa4634abb68c2d4be2554e80025f300fa03d9581e6b0df005bd6d5d8e010981f4ebde5e8053f3ef0c326278c777a9c3f5dcfbe3edf76c8bd7a6bb05d9f2ae10f46eb7e2311100135927db154942055d90b8739a5df17314956243afa91112a01d7283a2942cc394db810973806efadbb63d6b9003e91ccad622ee6d41a91ad659445fc3f44bba9b5f48646ec48782b14936d02f02cbb3397f70ee2d9dc0bdc23be46bbdcee4ec0f3c064fbc5940b197315e7b11376e9fbf5922c1d6b927afa24e61e6e4b59d5868644612bfd231a8d673089050996e1839aa9c7a1ca9f0f01295c7dec2cab7654ff4243bd6575d22aacf0517ca09c5748c85273a99db50232e370f8a3163508e267ab5866ad6452f85a8487c235c08aeb7e5c72d92d57b70ada96d249046a14634a1ff27e1741a1c96a7a932890b544f115fd355527dc7caf3111dfa478bff6630637a73790ac382596002ceb8fa0c6188f4f011527be1d880410b0751c94bb00f577c16eeff0f5738939610c7ed38fa77078e879480d2a8f6715212161cca47675deaae401c75132766f1edd2709fa7fcb8be1284e40a4996ff4d13d9eba4fe3b3a4fde1b48f5477c4f0124617a835e53f9ff27e646039029ff77afd80fcd81851c97d4561a06a0e021775943338c7766847598f329a6a0f45c42c6ef99621c5af1e96ffe70d40f3a0b80a5e083faf7e04fd4f0b08f568acebde7b0572b891635e0cd1ee98b53ba15f24c5edf152aaab9700eae722896f5703b20e011a4f2738ccf969d4f5694d762b925a8b93a1d57a8a91c41419bd23c62d9a28c48b3ff13746079354f17d6815fec59bd07099116240335a79d0ba74f6f7c1998326b2225188c849f3af89ade934f763ee2e9ca6bbddc81c3a1510840df3b24d1624d6354ed10318418e4c1d12420a91c76c68bdf46baf1ee459618524ab4de9a41296e791e49915248a4bc9c2454f8223be9a11743c36276b25cf4faac3d74d2171f63e4dcae9d48098e24c9904ce446b64b4339225f523537526700f85b66e391e04a8756333586cf419be957ac7e5ec6e16669427189ca21ca34fcf70cdc7c2a8f31c94a6e05b60673f0ca6395cad71185936e064dfa746a037a7c567407ce27128d35a87c36acdf996373ce02a04fe71b19e7d45e88d1f4450b8d25d98021029ecf0dfc41dc352dd5377ce488ea2721b4a66cf1ec55189d50a972667f0ee4cb3504a7711a0cb5d4c4908780bb341dfb767b002e6ec7ac289e10dde285c59a054e82916229d798134f058808f5f9c6116e895a676c30d5c41731874bf0c93c1201af554aee52231471e887cfdc18f317dfb908fd2cade1cc5e81cacaa0a457fdd8968a42b30b9d934434bcc61db27c386fe8bc7c00ea36c56965b9f96c0dee97879a67ea3e73a216b2f568cc8ac7732b03141376fb6cea78e03eba834939cc38aa3a0c84bffe6739716b79589e0f6122284b96baeb02452b5b76c8ffe4200943a10297755e8595a68facb63959a281d9f44d16f4cbfcbfbb70218544205d49c29e0df3e302621b9fdd8efcb1a741fd46d30dc6de233d602577b616b49b709ecdb3954cdf9032a59346b0239320d58ffa20d2e013d6b0ef4f8a41264e6af851ee7f46fe5b62ec9f2df684edc0555d8cc01113896231748e61541992158f735af641ef7296d6979229444e4c0815d5358a94826281944f0659a688264da578b18003f7ca2ddcf9f47c4a89e61f117146545901ee419aa9e1c78427b9fc35c5dccf93b28def3d268f609dae10fc4ea57ff91a81c0ce6480fdc2e612e4109d7977ee33d763a7767f937f2c79d80cc00ddf2f3df077315c876af8a29e6f103ab791a18914d2cf60970c2968c184d38a998ab1dc9c091ec95c5a71ff89884efc42f85f6c8558e5bd3b67d008f4cb3fde01953b2652fe16b682205d700d249e6859655e742b620354631aa6ea5c9d88d6f6728a55a41e8ebbb997b9d6b622dcacf0a7665c285978107fbece7af277881b73e82b0e90beb2a28c445b789ae742181a06aef6051e28f4cfe5d05c79a88e6d310cc019ef3d227b126bd8f6765e7de0de30da94f91dfdac3917a2e3fa2a19d13b6a9da74a7e9dcee3ed4b7daa794e0eb91f126ee9433081430ccc898d31067a4080f0ca7fcb6f598514d6a94718318fa46577eaa0f45a605f14231173840a3919e4e1d3f7cc6812d6d6596b702738f76d75bab6084a2faeb1ed8630d41071ccc705dac06150d7c262a6705ebdf71e5a5b8abc76ea2d53330d345220e19994da782c2cef5444a08a409236ad95e14a08b39ff1333b9a26feca38d76f953e480e87f7f7bc2c93323863dd2ea6169109824cbb3b009128f2df133940beea3c061a6648b2944d9f30a6cb5a81bb212260aeb59435aac706f3ff96dae0ebeb125a0261b0a123e412a44897c469b840a55ac51b4603147315ec1957e3563c679073d6edab1cb98de8fc254c9204c8dc18e618eab0ad37c4faba786296acf91e4babbda2df8cf3bc8d1ec547b0ebbacd1e49584630683391c714466ff7574a4900f5f9d51b4f8006347733de1596b7b6df8ac885ba64858e8d9412699abd7487cec8a41d83b2df2e8e81e40f1fd436625baaeeb50cbd8f1fa265e87e3c489cc0b66b8216cf38128eade0a9e00bf7c68bfff3b712bf832085420bd5c122a1fc175a06253948f72171221aa2dffcc2b58f28a50df2e55840441b211e50abee33ffed238a682328a7f6cd4012a51ea050f5b242591a466d70974762f0fc101747cd0987949848e30ea59d93570f3221491084c127682d18cf98c4b8e3f110eb1f51057505728ceae23ab5040abab6ad51eb68a14c4dc2326a1c5a575459f436baa1265a90d9c00a58d322b99c519359b764f3db6358fa8c8a9b310bcb2bc8ff8f959fa6e3748a931e6dd7294ec5db657528ab0a999503855e1e20b98302a654261e0bc1dd2407a9695b278c19234a3cba31fe60ec8c4474cdfd0a0707e3a95d0ad1d128157cfc1c1ffbea222f52cd0f34b4fde233a175a3cd82aac86083cc140510fb1e1c8f01bb21cb57c01e18ac8f00530eb871f52b55ae7649d64e5fff0f88926e740d0c993d1540a2bfb4e8c9758b4ef6cdf7567811970b4e94faaef192000200b49888c862fc9d65bad7c6c0f8d87be27f50b4be119042ed4784a4317845436190388670b22a93c645fc0657d2f350ea1c239a2a90443f74ef3550904e785cb362855850c9969bc34c858224bebc1bffe5242f04ec11ebfd534bc5f88c316c591004f1875f2bc596889ef0942c06d852537a9570ead758f490ca779c5988463902df32a49454cc655ea44d586b321a1251b42e1a02816808b58edc664ced8187b5876a73217108fa93c4357a00f689542be9bfe050e5ddb10e5e40669b90f7b5c8c86a0ef56f0cadd6374040906d7048b6012145b3bc6419a02f30ea8ece23e602c03b1daa205fec288fcbd07e88dbb8be62293ada3189e2a807cc3ebe94e2ec9d3818a8d4976016738815d960e783a10d00ff426b815b541ce0b2b34400c4c501471cdc8eeea03662ac4a7b9f5a6a32ba6a21580c258e1c5b76778970a2c897b767e31073b9fb804a268e9f18a3f23d1a8c91fc606991c664b14206c74acc62c7cbc5375d9a0c59fb9e42f1c4455fca840e2f653f0cfa45ae86931b4e3fcbb387e74e2548143033ee232f28717167a03ceaaec66557faf5eb19205770738e286a51f2b76a90aedbbbef373c06b09cb3a6f7689af6661a824153ee5a8ce3b067d6bde74de6a9d5e9474ef6ef39adf409ea131dd2681a7abbc493a400ca1c41530ced1e398ea860cd337ccdfc357ab9fa53f29f0bf1abbaf02e6158d9cddf7e211dcc4179e5f1ef000567bad7fed739d7c074384173f3d69dfd14be459d8f00ee81d1009d568f10e0702eb1009faccebabd0def3077dffd13ffc61af309aa88734d869767b867632668e8a14cf8c9acba869bacf9a37b5c822b685df296d8cd49f16f252f3eb06a758f882ef5d599e84d2d23e8025367afc4dd6075e6f59dbd1120defa08088f618051ce1756100ddd5e71622b27b3aac1789dc1bd4963b3b54596cef50f64795346f39448c5a47bbf8bfbe0c01c33b31ca57aa8bdf3b98505f5eaead16c0927f23bf99b983a3d84212da3d3e8571967849b95636df66103e9f0a7c6056def0d952c2d53a302f41ab2ce398176e8f0837a3d3351f8c3f218e7da70db13b34611077651bdaa666f426f767413d67f64cf675950af654987ae977f8198c131c67b56ac155c0e34d24818464092c39eeb9356a0b6a5f1864fe07582d3d0f46972f9a0ec46cfff0f11db38cb3f3ea594fdc50ba944ffdb0daee7477414e7588490be668d7f229fcb6fd50ece42fe3733e3f1f7f59c2e7e5101f3f60f8e2a750da42efb978b083f1551321e91877adddd203004f4397c8c3c5f4e97935f40e8fd9c63b5364d589d3cbff9c97ebad6b8af2f19de8d46978f817fa3860dc069805e1ad075f56274db8817641c0d6f3c045a877bcd037a0b9ff61f593c6fa2214e97c2dcf5ec292b215b9faef226a0962e4d4ae7e9bd026de1e034786dfee8b444773c74c94fcf4955561355472ee184871bb0d42dc7e962384375b913b6a8655c41397ff87e46f49b1a4c518d739749ab5d3f641085cfd85a2515073cc30c7ee354a980b7010f04acf742226d45cbcbf52cd785fb69f199e9c5bf243e151efc081b3dc57d58d9dc2722940c771586f0640cb3ef9e20ec5ffadcdd9fb7700a99787c5a08e00de42de32a821562a733dc6fa8bafb8021ad91030fa5bcabcc6b526fbcd50bb9641e0ce6b5aa15cd5c0a9a1424903e7dbed9ea411769a1a338981c2c6f7784dafc972acd76e3a1170bb058b989cca2ca66d9d2a074c606ea56522771e84070438bdba07749a4559cc04bd3b6a9262167a6ea67eb3e61a855c61c4a66d76814ba91747b24cd87199770b8a027c08f777df95c432ed4a556b7e99ed9101c287df42651e99371968a58eda98b75853af3d2970f0c31c0ace6eff6890a5433b5c928744b0adb1feb2bd0246fcb330f26727112c63cb88494a83622835a6f0921a087bc7c163df72b7c270a86f5b059bb43c8ef3f391744c64131e2d3cde0fa4b95c89cf8d91114c117801cfb835c2b9b066ceb7b3efff04943f17d17f7a0055991ebfcc6cb4960451adc70cd6d8199b4dce6c5d2d4c00921c4ef7f56def575acd0ae18627423ed4d2ee8711767ac17159a05c010dc32a429ffa67665aeb3592e63e1d519239471dc678af4ef3b926c30489b3bcfb7df57d061cd75c6ee72399fdca263404ff1feefd9ecad79521ef88397f7dcc2810a2ecedb1d6daad80614c53a80aee3aa9251027e7690dac000b9b8af8705823bbe5cc754c6df5e93dee6f3a0f4934c99be72e64eecc623d8b4a38b5812896e521e6076a59b0ec755a16ecb4b47371c2b247e5160e37afedc2d5450df0a649d8fd76205c45bec967ebdd4df13e3fa89600e6d16bd503e44eca8a1aab90e2981bd846d4a684a5b21a92d4f0fbd2e22bd9a63c336f9148f26568b77d71db85115255be86688ed72fc074d7eff109f0e8fb46132b916ac0aa2da24e372a959676dc6393b5dcac6f12dc0933c637eb223af65b6bb1393f149a78f904396477a76007bb43a16eb655194a48ef810e5961edec68978f5524fdf79367679bde5607fd4090c365fda7a64526fbdaf9ecdb3816f793b871edcf2cc656c72c2b76f7c1e05dbab30071b2dd2f2c9827239625cc4da1ce4e32c474b3ce900e1a4ad99fbde4e1266f0650cac1dffb0f81d83cbb4c7c997232a489089e2d81aa9de5ef42b7d5b04526e9f33d1a242d663714bab14464a8457756144c2d7e4f94ae0c7c2ecdf41a2ba949d756fad1710b0b341497f924133a6a75abf19bcb09430c38df327968b4a96617c2c88aef61b06d4342d1d4088f1a931644fa1e3da6ad3529e5fd332684c8ebc8093c7ac4a88d04666418bc03fd305a54a15fce8eb2bfab80b24c840453a409e419a2dec1b808a82c74d701e155229792a7d3cdae09f7fa766b63b140403f3c674a0bf84c8de726ac6613b4453e53159ecce2e7bdde49ae9b2a441954320364a252da4e8b87a1b1c7c1b3f886fa94dcf119fad71f4ba1af2cdd85fd68adcc9eab6bc3e12689383749ed611ecf2ba2821ed58657eb7b76d85ebd84b3aeaeaaddd31eb59f28d06d86662b8cacf759693f7ffc3eb36721a6fcc3d02e4f301b2a12c6f85f205b57e1e6d80aa5e2c9471157d5d4864b70057c1b134f301fd300fa1ab09f7e8e2056f240564a72276e08c8aad82068c3c670d97aaa626cf16ac1296efa217423580200eb643f972df16d382d217ef0452428823a965e2ab22653faffa73c9c25a5671ed8da07d4c3f7b1d69608c946a17d0049ba97af303da6b82d581a8b0ca62a8157287eededd1e63829209499d20959da9d9d0072926d0e42c3fa41d4250db4ed50241323bbe78d15b20460b998f136e07ad676c8f9b48b76c924c48f199ffbfdc836a1248418c49a38158940a49523ed52b9d02cca6110a42663f1ab9877e54b25c9e0145a0be81c7e65010489501de75b8ff76454ac3daecaa2176662e3023678bc47916db0c6b8a6d510223543e77246a87fb67f6645d9f625435039eedf9352ec56187cee28fb37ad07590de40a67616201d82da5a24dbffd31d051e9e2a960c44b6a406a0cb753718b412f0dee75fb6a3ff8d09ca56fc2c0095726eddff1c610265cf8058d20ae7f517495a0190302a27280b1192d96c133cc064bb797ea5eaec7fe9ceeb3f861e87610a0e0d9030662423d2359e5aa8fa3340808392961ce28f922d3dce0212066619665d462461a885fc6a04b2575a905cdc45a55f8a3cd31957229eb152bcab7675867d4eafd2fce0f1c6ff7e8a3c20c9db430362c0f8c788b7cf93f0d890a608013f4588e55ad39a3dfe07e75f3830f71dcaa4012bfa2d19fc1388dc008b47766718a5c17a8681a02cf52a918706190cd7fb0f5af7db3644021dfd0d74d8f2eddd6f21f250f375969b97e2e93a4530d22c82519aaa3860e914b0571b22cf35ff6622a506565a1a7991f64311724d6971652f92d4a570a90bb4db8011825b77d441984829ddbb6c9e378b4aa26808de7c0f7e2265d7b334ac7b30371a0d7bc7e0c72b94cc9c491f264f2675b38c59010d36a2ca7125a99e6e9ef31cc682900e4ebcaae3172a8119cba582e2962dc58b9b2cd9afa2af651284ef3803be0a0ee45f36cec000016ea3ff19e43c4a0ef8045f20f54abd40905b5d24400ae7f18fb3c2d5e3fd4557394d12f68b1379aeca5faffab70e12ee103c99c71dd40bee6deea798c9d7ddbddb5bc7835dfe3ce56a12415bdcdb43d500412b053b4acf23901cc264cfe3a0e5a569c7744666600de1632c4060b5a8015439b3cc42f87f683846025ce26d3d3b4c9ee597affb28705bd8c34ef0fd3893a65f255d807afcfc1efb14794072dfd3d111b1d3fda6638220f56763cb3b39d6139deee05b6e4b7190a730cc500ac365bc749ffe48abd7cdbb6b0f3c03208f5a37d33d29ca0facdbf5070c7307055e7bfc804152eb8622d286d2fe49de4e73f48ba5f5c6d6b5279d52e5c459cc934c2bc3445682bf5ca03c9bc0a7a85634d801798acb4708cf747d7b5bbe8f660e8fcafa4d10ac723f52993a0b060a9a5aaa1d168dce390e7fe54812ef87d7ce451be865ccaed774db9f48b26ae3e30d10b255f9cc2c10b2f979e12f6a0882682e1260d91044245df4c91ccdf2341d19c84bb73768a2130eef084520af779906ab9aa9594088f2ebda13f63ed0f4ee6fb1166174877d1f94c8b5f30af25ad8849c676d584789b04625cd272056bc63021a9429555f3010baa20e1bdf5bd0abfcfdc9e5f1d65cc0ab06e8d24e259fe54a60cdec343aa26c349795f33733b74af14506910f850b5481b8a66339000c4041c16a0a616b74e8a2b27b34a853b7fb166b06584b07d79b01e95c40ac6cb2f79652262965720529051e0516e7ba524b3b6773605ca99db549e9681cb1c954527f9e4ab229f313a1a705c73f7e62b80b2e420f0c2777cfd19d161d734ca33bd40797dd452ba54b9d2d1dac7f0bae7a9be74b80c0f0984f45df2686c3c4b84258022486c370fa3630ae106a0a250001eb8971c1e8b0dfeaec68ed1097db5698d2d9564a67716aad3029add3bad44eedbd374c8b05af61d740edb2f2f175cb3f0b989e92cd81d131ba63399167b66e49775842becf0689805d51a3c25c2490d6eb685b4ed22941523a6af3295dca45aa7aea466c939e9952f24c8b43a3c400a83ff7743647ebdee6b9f54ccbf50a078c60c6613a8d920315ad7badd3f5afe1223b60ebe4e51738d9ba6591c3827feb5a1bbb772e96425a43ea59b73c1461d03221ba734d24d62a25254d99727b525732583188bfc99153535333c526738d29c90cb7d81d52012db9204158d8995b9ef14cc95784afd4d4541d39ba73317e614805b49841821cd964ae79220b3e5611f62f073daf47aef9a0483c746bd3f1745eb0c95c3be9cecde11d6d56d2ca4ed25cb7d53a96e9590f7b050c75f2513adbe407cf2b8b266479461bbdc1c64938879794bc2742d8cf0a0a4c88a802556b8748550666b4d30a9140ce42aa58832944f7406aadf507cbca0f9abb41b8c6c355aada26738d56cb65aed56a5e4d68e32c8e577b9b5aade6d562b55afbca9ae7aab11a116cf2abb18fe80bcd150dffb192d8648619599ce7469fb95fb06f8a4d662253890eef6c7933ec4587f4f8de845d170f8b872dcb309b9e4264cdc4686dc226318ee48f69a213427615a56c3a31755284fe09f1a2ef85625f9fcb5b97a9da702252c02e08a4190e46a5e8a73ce0091055bcf0c410342c3521c9ba6b84082dcc408512a830c20635f8e921c7229ed84a1f624448e992a728123ea938d8103631ab34e9410552f4b0d4a5e99a1e0b3718498189932351bae02db80b1d62064744984cb8e0882894f4c04005382891c31250a030816152bc2596e8a40e144c90b083104d1c2142861d1c9078c10443284c51225344114438a460490d2d5c129c4a47546981141a8c48c2a566fc5e30faa5e2192ad8b0bf53245d50276b7a0d7423e83563cdff9bcfc58a0b66b05539f221871e3a050c2314d9d1a0430f50ff7fce4fe89fbb8e84feba5d84fa029a60e2031334784283ea84962a8a308102292b30a9095f3802c0f90c742e549609ac0ac6eafb966c12e3c220d8e700d1afd97abdbfb272c5183c5ee57436cb60c9d3dfb777385a5e471cce8673f1b7d161e9d3599ccf1fab08c431ed2cce9ded5052f5d2f19cc481ab3ade85837660790084c8e1d6f6d416c9b9f8c426314e67730a53a40831174c419bc98aae57239b2259c975f2ebd40a52af3158d9a157fe8030a4362affd5bfa5316badb5d65a6badb5d25af9f68e74525b6b6d487728afb5566b5b70eb72eb72eb72eb72e372e372e372e372e372e372db02c56dcbd26d8b79db82bb6d39a2b5460d9b4363def020208868d6a04f286e4ad2d788dc5341fcea1725bcff3c3b9e7e3d2e1b921146e28d594681c156399511d6a27ac38af06749bd5cb4a335b25f8036f9cab9dcf21e8ceeebf5a95422e5a107a01723d8ce6c122bb331d885edfbf3b940d62dd731dffdea79615fccfe5c58ecc2ec8fa517ef44509363afaae5964677be070fa1729d55e520e6403e8e39e80a2184cf2542cfb7df854f3c85e7c23bdc73eade02f5b3a9491275fd497f3669816ea7aeabfbf34f12eb4ebca46199a809ad3e1d690f5b67754b3c46bb6ef98edb9ea5a6d82b20f8c76af523f306d09eeca26f93b90cd965cfb7b11c48d5517b58abb3b9b779bdcd48b4496ae3d941ebcf6e11b31e888c6a2fd7d67fd8307e36f9321c846318089736548e774a36f9b2981d650cc8b6207ef6349bc3721c289bfc3a0c23f6cb5840b338e4cb5e6866042352d280efb97125fc8be1b269a73de18ca15a2dbfa8da4772ec987fdfded46b71140fcc33e7791e8b2f30a27c9eb7a8ebf320861dd3e90d28d6c9efe365486b485bb2a8a0573a79eacf635cd8a756ab85d49f9f401afa73f0ab46faf3cfd3d29f7b9922f5e7f9d2e8cf4fd4c6f31828bb721bb64862d7a5bae31dbb7c4e2717596d2d2415b2802eb2ce9e8021092b648535a18285daae8b87ff77ef585da23f87c9a12ef5e736f6dcf27e5de40773b2654a8a1420ce4ea5f070eb2f3a95c24351165a9699c692744f8b4391a834f5caef0ecb945ef965aae00da04d3b7d71b2b9d73aa19e9f38863d06fabbabfc9e9587281c448c426c7a5b629e6e903061d3db9227e02703884d6f4b683d7b34a4c06d7a5b1294b3aa06fb9ea3941dcf1cef98666cb06d4ed8f4a6644acf3c5fc08e90b50d099be4000e435ab99755fcc4c1d6ebab9f06ed50adcb744f3b569ee5ec14298bae77d02952965abfb9cfb9b7c9ecf547d7abe77d9b92dc26e91fa72df2846ad5ee22afd722c19feec14c70aeb033a2f38a98576c598e9cc05d2aba0f1cca28a57b44e850cfd3f15a96d3d62984fef42fcae739b52b1c60a45ca253242d40bda24eae6b2368d74eab903649a73c946991c24ef5bb63416593b6bc3b6aa3f2992b9bb452dc9d15297ae516f7d5969fa3a94de5dfb77725ef0e85a2deb7679feea3140429583b9d28a794f290d2bb7bad354fa552f4ee765cb7486ac26664b55513d6c9bbeb95674d6d49fa7ba9fa8249486b564e564d487021c90c36fc5e01f3516ab4554b4c0b383fb0f13cb9a1600537a8c416f0e0461b1a5408952aab603a9c1d66b23818e7175a482d0e06ec08dd02b4a656cb4aab330babb5d64a820e5973b823db4977544763802ecc453510e28804c1e1ceddf943a4b2497cda78aa082cab0413e37bf189a9c06295d80f9cea9ee789f179be4e7ce2338a4d628c0b67a82239639c63f948f6e1d3196532c7f090cfa1b4c8e8c9ad9a407e3407c45110219fe3e178541dd846bf87c5662c168bd55806bed8901576ed53e2e1b0a8c7b0cc3e89c562b1582c9663b15cc32673ac88a8eb320ce80a19de624884563e6e2743d031c72e3ca5571ee25e5ec8b99e508d54181122d4062b86cac566c5a6cff9478e0133a072c49379a88d950d4cfcc4da9ca735ea571cb2d96a3897cbe55cee737ad88688359009199db5cf76e4e172a66ec79497708f8725032446f8c9c73aa2a481349c44a51e3c974311891f1aeaccc972b30a3b4f199115139bccb973979ba572b95ccee572b95c2e97cbb9ece26962933977c6cef33c754c9dcc39521b754b918e40d22dcf495969a756405e7a323f7da007bbb0ed15835168b338a40e9f581c9273bdeaa281cca3dd77c2807ae5f9c7e6506b12e5b250467214d4adc89a313799694a46b6239cd92b3782b1ca4d661a3fa21d99baddc538080d0245ac00d7a3c7d2a675ab6328135657beb27e50c158edef3b619fc07b307f2ce36b8f1f0ebbea783cbec6ef5eb18a4d62d3b4384a56e35c3b5851857854c524c81933e8cef36b574ae85e64cdc468bd128248378ee37831d6c13a01e8c8d2ed8847289bc43a98d6e1b03c45b1724227e9ceaab802fb442c7c5c7e38540f8bf3c2294740d5ade094d75d65b2322b544ffac136d0a7da5c35f2d7a8b35fff03bdcd73f235f2076d0de7421750f8fc3ddac945aa5c24d71ae752b8960c2cacfd1e592751b5fecf5f5aa8540b55a475a8965ed2bbb779eed13e479f6ff39cf67291e38be4a38a6feda2f5d28ea8229c1d7f8a1d13d0c240e51b068375ba6593295d4a6771327f174a2995b3e39f6c4415e5f1d60c26db5a2e030c7fd1c286e11bb7a70432a81e39740e94cf9723e3e41f39cf22c6276eca3f41281eab9e5d1996c122b06647ef63a07cfe2606eaf59ac95806e583513a3a38364754e5e8082046072cb262074bb0ac5a499bda1400e6b43bb165faad7688ea34c58eb957d77f533f8ffad730dfa19330287f5ceda93d65273d18ce45074fa5533905ace1dbbf630a306691bffaaf027157871520add6dabb81765169e9cef31cf0a79c95b37256ceca59390361a50e2c7113dba73f7defbdf7f7977be9342bb136345a75e5a25b24fb7439086deb9745f433ac628da49362a5e5940771600cb481486c8e260271d906e2b20dc4651b88cb3610d72dc72e3d0371bb04631a3742dddd52faffff2a3bdab0d26b4d54df58976578c9e6e026a69fa64e6946d22da533bc9bd158e7a1ea78a7fb21e4236cdd29cd3627ff7c397e397e498e5f8e5f7e795d19f6e5e3168f21bf11f063ecb8453baa4c0869cde3df3f18732f63ee65cc69cf183fb5d60948433ec20eeaa488f9d3f2135493ad3dba93790e9894a4a4b4b4c4946dd67381b19a512e7a5a51b7fc69f949b6c5d8926cd00625fca7d6fc18ddc99c72ab649b96fe8769895a269207754a69b0e49c33de9f8ace70bd26a85fce9452ea1540ec98bfde9fa379a79c522e5a1c4f87b815a39f5a958e1783e0513e70ffd7cd073c3becb8ead895e306b17f9eab870d3b07b136965b202fd83549b695de9ba2ab4e5b23e7e6b512d21a129c89ab992ebb72f19be1b22ba72f3c17dea775a03c04f5cc96939e690af54c155acf5081d23353a4f4ccf9a267a4d87a268a152674529567609d478b8e77345842183d03458cd8e2bbb4859e6172a1679e78a1679c50e99926360c3db3db756557a2d812ede5d7f5339ad92cc711bb72f2599ad21db1531eb2c4950c0cd50b172d626058acd8628a521bcf2938fb70105933315ab3b4e85393c521efc5b62a0a59960baa8e673db9e21098063967f34a1699844cd3cca6699aa6699a665e05b1496c9ee697b379258b409bc4e6cce25c9bc979e7bb5049b758e9eb64085a3859a5c0cca970fbe86d9fd212535365e1feebc3aa8efa24fc09369961b60cb31df11cbe2e534461b7dbed7641769fa3f3dabd7638bc7bbdcd2a072bc43ba1f37aad7ed824debd8262d7f372edb26280c522f988c7d35fa0f37add609378477edf6ee784d2c621f98b8ff8089733754736982658fc21c5e757bc59ee42c1c6744377563caa8e14ccb66e3a1d9ff3ca420fe5b935e46dbed6a0d1fed8f18378d80f54835052361913532980830bb7247070e186444f76ad7e642e424fb5b5b440b1af6da4366a6d61776d376c435acaf212435a8205a4259ccc45401239feb038de0afd6f192022e43fa6ff8c6edb1fe98ef55184a21e707bca6f16bec7f377f9ffff7ffdfcb5b063389975dc23e0c1135bc810db29af595d7e826d5f5f9551059511b4e003132756e811e248175914508860d0a16e8087edcf0ff0fd43d0dfb5e291559dc7f258fed2e1232c8865c325da5527c3324de74f6059513c8b0526a3cb5201b398c82c2426231cca3e27c3322cc332cccbb1ac425e0128c37086e936a983ffc378a7460daed961933a3849f67919834d211623599651213c5996cb321d3bb2926c32cb4a8b437edfb6ba23db2ff90b03169b38cd1d66c22109fac14ddd28d7c864cc78418c89e961935936eac870d6c1185794f1f560b884b486c4ac1dea0b4216e3978d0e6a442c46b16f181fbd8468331c5e22db6cd4464d3a64457b7ed6f33c89101942e4737ad8a88d9a94e7864dd6d0c1dff759242a8c554460aca8f83efc19d9243d797037cf42a11b10aeef1bd051af5c87383b299d7c85327042338b92a24801063424e087235424a15284922d25d071934373769c68276fc6aac5a8082615826628390052c10d8caab060090a27546179484615156f6e743ee041da0181b423ea2f9d22ed76e8df29d2ae68dfa4f0b33833313ae5d4feaaab72dc2076caf5b8756e5e7093e4b338abcfe2cce0b7f5de2b850b2ed010d26a3f876c579e6388fa2b1f7702fd9ff2da439cbd4f25034ff62b3d7da85dd8955f8dbaa997fafff4d24b2fbdf4d2a717638c6fb5ffa997d326c16e01ca575c16f79f5e7ae9a5975efad4d6fd36947280c4a6515200d4a310d5be2833a04e2dace7546b0bece25f581586a1532f2b605abcb0aa2a63573a869e71e96f9199467f8bbc35fa5be403c0dae84bfe0d2dc698999999998921ae2effa72ddaa22ddaa22dcae94dbec9f181cc6539470e3de39f8e74a4231de948675096b8a23c090e38a04183060d1a1c88ab1a5ac7c68d1b5a001dbcf4a5aaaddae64befe0f0806ad5566dd38300c8c0e02aaed2dde8db0a73bc8faa0ac9cbc97cffb6f0df6e53a405fac5f833c7b8ba5ef4ebca759aa36f8bccd1d1d1d1d1c981a1a2345a40d03c18e34e29a5547f18db3e001040000104100620ae64bade81f2e9d104d028eea369b4eccaff2ff0a5edf81f7fb8437bad15edf15c403af6f5e3e55e0bfcd0473fe7b1600b68f7787c87cb03df65f880e7711914900b83c140a95040a8adc7737d6f737bf4f0e1c30049ecca6f6800940fe596e2fed4007c8792165af87fddbdf7debff765c2ae9c7699fef8628c29e5f7f2fbf6de7b2f79efbdf75e3e81beb3a2b406194a378cefaaee0b4a73c62ebc0f645e829cff3d92524a294d0df56d3362d8b457fe351c4a7c303435e09c5984a7153ad47736eb1ba553a8fdfdff63dad354a95496752aeb14078aa60699de9dd264a121a52c4098db9f6686a7fce9cdf705a7aa8b69b2eccafffefdf25ecdc2ae3435e8dfa10c0cbb9221454e7eb4fe8b29970dc22afa8a6e9158218b04088b647fdf6ccb0d3720212252e19c5b1b7fb6e70a114458885ab0028b93224360c6e284c910967b2f2e3274f50cc4c50c612901cf86b00441a404516c6868686868e865c51a203ed5db10d157912125980cc540098fd39c2f93a159f6fef38727f0cb1e11d8a274bcf7fab85d86907f210fc3560f8f874cc7ae1f2f1232ad56e67aad1b1d3bbe2ccf074c3e183ab54622bc033f82311157e19fc93f131ee32970127014781037c247c04f508203fd0f711378095c04fc8717c1f8bf0ac6ff55fe1fe3ff2a355adab1c88245600d2caf8659fbf7e2ff8b71be65b65f065129d1850a860c2b869e9131736f93dbbd8dce8e3a1e5adc913de2bca5b24facade5a2716154abd1d4506c6da929dd89910116c9006bdd721932c012cc8149604ed300106a7c82252db4b3590f8f2d5df4979cd2aebcd542f96825170d0dca472bd5cc60019616c8c26268982ef54f529292d2d212cbca5822cac668c6154de7298a2b1936bb32ed762b0d5bc9d8d8704083460d0eea924e2733d2c0a7064bdb64cf5ccde1c7377bdad39ef6b4273eb26755b2e357f3a50c69686dd240f968a51adab46103e5a395b44e9b375eca1adafc94bea58fa94ec1e153f51dd5c0a70d7cea56add5f23bfaceef667392929494969698b4895d2fe5d7a317a86a0b6ddcc027ac8b86dde8e0050ac96444b3aa84f6c2812486437be07da5492acc8300e4c83122dda19d4b40480686ac2ab56143d03dd07c841b7c3fd4264841203f5e72f44fa9cd529ba5364b6d96da2c35ac5ca17cb2ebdaa687d9539ba6c5f91cb0082c230096416ef40dcae7e21cf87eb76fca97844f7ce2f3fc8ef295414097cea8344dd9e62c64a30120200843160000200c0a850443c1589ae689e0e20314000d5c7a3c7254401c4783f14810e32808832088218418600030c61043185286b200bcf080c3a348d7876db866f6a26794e6ef248cea1d83a763c637ec943c36d68f0b39ebea55b918891650f9c3225906efab33c0e64f440dc7426f6e9d7126712937f6a1bf3f450f7d390acb105af172d3ad4e87d9b9a2388069995cd65978cdd5f0038ec7a6a88fb9aa379639c482a6c9f84db5bb991e417416776e16f0656da0fe626d0726153959a14816423bc979f8c5f060f21d0b4e1e397dd7c4383e6d686126fbeae6952489d050f8846f4e9e2d1261060b9ceb362b6a3ec027e47ec777d6db6b9b127f642a4c63b06175d2f782681322effa4a8a8a6f6044f133b85384a78be12ad50702fda2244670dedfa227d847c62e2c7b1afa0fde99a92df50feca93714c85edfd76acd7f34383b5c417fd087835b36e3c8e965e8550b3993e04461353a015f086a19a495f909ab742875b119d0c4a2fbda75b70ab607c0ad8c6e5fb8392e5f0ebdc005de02ed24ff6fcd20c997b8f6c6d7db1e430a5779f863d0c28e35a8f4cfda14ee368769053a2b5825a55b2db2d098237c808c0deed3326138fc21a9cef8aa72ad5f0ab446863fcdc2e2d35bd34f48075a2b45865145eaf84e9a21f15193e5755b24b519d2fbffa95e51a80019c946fc84d272c1482d221fe40663b2205feb86ab6ca6f1438bd2a6042181d17a806b13e27a9dbcbe9ee9a3e1aff829fe5fc6ce48973ee00700d57068d41e84c160fd46fc05168306816561adc4452673c011093fe32acdfa243db0e4272d120c1b4b3f83504df40825e786eec4f13dd0be0d5ecda3e5e233d1eb3318f07ba4a8022b42f315c3d2a6d0667215d52927eb4d770932b3912142194a6e3a3fe5325eff06575a673157633491f2d49a737288c49c1c205792ee1a24a1dcba011429682ef18e2ac93f8d6a445ee0b01f10f58c825dd6ca0387bf4748208b49e8210a353e1e63f30478d9b47b3085f6d50135cb902c22b5acb91d62e1cee1d8e698b2c6194a453976ff23b31c6cffefe04b7316a6eac62672fcc05590aa246f98f5ce98085956b3b02abbcd8fd26f818da0f33eb5e0c26b4d739ce0cf4714c2e17b816e7a6c5352da7126c9fae82b21043e2702f1f3448116b0a2264f595320db095ff032e72bb884a0265f375ddb5194c09e2bedf78fe98611ef60c5943c71ee658406540c315271e2d4c60f1652386a69cb9678cbcb227831c8c7c6eb30fdad87e477e5192392f90a078f73bc7cc4aa23798e5c9911240e01b995ba0a749f7d0984a9463209a6a9c8a3c63fc516638baa5fc221bd194ca915e86dc1bad2fc2a7891d22e6ed90e84903f595501586914bc7df38a91224b44888b404889bb49f2ea0a6bed066f725c968254acc2d97812b670acff0a1545d25121250fb2105020f84a3793996a62691a200d1df95bdece1cc764fc20ab3b2c5c4a716886e3e4300b86d628b4151a0013944c99c2b36a4ee63e08112e51a3904f132fd3b7e3a54cca77c168a297c6e9435e9e1d6871497179086592fa15cabdbadf60e62ca2ebe7d6c2e7fd9b56d407e71dc0aaf5d0455f3a31406b0584a63c3758cd0f354cb4ca58d7530bf3a28a9b52567f03e61e1b8984e89cbea6722e61b8de3d94d646684a442839a4dbab3c753d41e18f8e0facdc3a140b528eb648931c481d8980bd7a5a7b11046265370814837210353736fc46ac950747a3ff2d987aaf97fed71b791800fd82a04e6dccd5f12a1921367b3cf07496cabe7fa3b0d3e880d5077123fc6850c5482cfb922937e33175b72236fc3f1225555057e5f961344692f637e867b8f6ddf723a3e03472cf475d518eb58d46395b80f8cc62e1d61cd0da3ab79782fcb1edc0cf4db20db6bafc205cc8db0b13ac87360b9b61d0bf684151d9bd67de3ccf595306b2cc012ccdd444e169e191d3c3f9582a8bb17a0a68e171a3e3a414113bcab73c90039d36853e97b7fc9dac6faaff01fab3af7aa6d5989d34b4b6e2f8dcba433a11ee55170343d8817ba51d659e17cad5e1fef210495d559eaa64b2b651763759cfa6694705b817ab987185cb6e0cf024b229140c71dac1d7505efdf30a26aae4bc76eaca5da74e95a83c2de77032179d7697e54581e2a4c8810e566f07ee876be73237f32780efa1d0295bf16bc037a53ba0c7c853d5e85856027efec56927c659d8e2914a34f5f524985f2fdf49b4223439fca239facc0c81e0cda34ed2cb8695a7ab855f2460b68665d1163b259beddf852a4caeeb3808bfe8a7242f0c3ac6809c2a196182e6e377a7324230ab932f5553f1a0d3fed1c88688bcd156a0415ce8c8823b4a2f28b9411ba8d147f71430c1115bcf5845cba6156d88c9460e58f8b70b0806233e29a6c934c3047d5af46065055fce000d83033d4a7913c08bd6192c2064ce92de9675052dd4555a4828f236867597a9a888afecfe02a2e4560b696a2a74a947f5990cca3ad239bcd8384f6799cf80eb96b2c63cd1b372c6f1d17e633b6ae4778698bd040ad9e92a70ee08a0ea58209ccc39d9d9cede3037f1e63a58c9ff2914f33c5f14ce79bcb83dd90bbc0e5358757c1a0808078b9dddf23724bf39f877983ea03b95a57081d3e8751374de01eedbb87b072627070bd329931af602355e62b2f68a5514a1dd8d653e52ea713cdcc31d372a9a91f088b8f01314af0d6642343f8a9d05ac479d81bf27c14f1deb5ba9a9c99fa2a5f2ec7dfdaadafdb17a614934280e7c821a9169e5a60674062b443123b556ebbc7639f49bda8182409060a97101fd43e60f5e64877641a562f7883d322b809e2040025f91f1c999b45b9f2bd1258cf6156bf60b1f690f14533f96a9340c2c2c3ac30c3cb7ad602cfe012a428f4ea0993ff6682c3a2f3ab8cf36ee559b90f63a2c675baa47eb8d83ea691d4bbfc63d31ab67a46f385a46b5a0923197614e0ed0b6b6b92d64ae4f4843346767e61958ba0ad38c3611ffaa6f4ed21496ffa968d3ea1e4c6fe4a2ad98edfe3c5f84b6926e9bfb3cdc5fdb75a8da3a20c2d3182b647ce204e185ce291a7a08c28308d9de83f5f643e854b0f624da1993bb89472bcb84e1915620fdfab79266c70670daed2208386be883848a1cbe7e54ee585331d25aec45106a60c4add2ea3ecdf9252d4ad1ec5a61f42206f14f13ce6cbce5e370ac132b90e4757cbd9ed617db8b797158d1a0b5e476c694813fb4897d0f44a91a992e22c94d0fb97332b3002b7f4b249d48d6bfe1fd529d26ee25daad2c97a31d84cc822514a8a013b29e81dd3e36c4991cf21777d2ef38d426a178b654b849e4712930354d1dd12c0b3ca78d49c346bd09a7659528ae399eaf3537e0de931019d1647b0f12ed2c6307fc8390852c48cb1cd1e5efef4c40534b41448bca1ae5bf4cde71ce6803cfc02605548d6656410e5a182e1af9b955c35018fb4fbc115d7e5fa19620a21eb4d596e043b3ba7ac346b1acdd211c0c059b86d6ef80ef11559c0d1b3dd983af460dc9a3214ed45d2084551fd0dcd2c4efcc3912c0c6b3cb3658cbc1fce893a3e5625a8925c4658ffe46570e9618a95efea9fd47b76462415c04c1c3ebea66d163a1b89c508b02bee2aff6945c17cf7a9b6f3ceeb0ac277754fffe7c3b73994e19cb537168486fe3e5adfe3b2d3c212a70a4783da36c23c15068765d9e6846302be811e2bb8fb11600e45c54dd5a14e8e42b9a220a1b0d386cdc7a0b60d3ca1deef8eb32012f00bbf09bdff825606b1da6e995f43fb9cfa439b9a86f29c9be8fd50c6692f899082418d4e952bb4cd717a5c2bd9051e48674cb04c6814497468a9cfe568d3430c6781cda719d720da97d5c5211b48288bee2ff63caf7465eaf16780eb42ad96d70d5f5ee568352bab429baa2f7c6e89d6d9dec2ea726edd0952e7adab1b09d39b9f1e213e79a46cb6974523af05a116fb413d98acb1b3bdd583011ca6944e61718962ea982d7de624ab114eae4e3b13584f38f3a4636ee13de1bf898751837bb2a6e958b0eb4383369e99bf1c15c5371dfd90ca549f4fdd6cdae19d53099fb9b11cf532e0f62d2a6a1641319250695712dd124d9ca9618c672fbff0a60168d0bac4d915a9343dea37b632c90ecfc60ed55af7fa9be33f3c17ab562410e9deab7aea4fda0674080f3177155584f4af7842f9b98e4f7c815a2c8b75017109f08525f71221d1b03a79f37a96547483cc2f1c8b999cb5f4053c708181c194a3fd5c009495c3b16c5c65cfbcf8be71ec55b18a0df2833ee28d40f01b9ca4ca06fbf6fb95f68ea17b22b09891af7fb09ffc4bf0d4e5d8e817e5ee8666d8ed0207b2dfd83e3dce647c3a8447a07ddef6137e8c4a58f5505b02a7146bad60f9ae35e4727935271527d88c4a69acaf1fa9d6d96328c7072993e6d51fe052a0d2b91c31228f26f8c4926fdfd5814d5e2571b7a88f30bc5291b2a5443f4acdd325b8e8fdaebae0bc10eaae791606e6762e691f7c89c5ead19077e163dbf251b4f97894c842a97cc11168dfa6826cde37e89978763a841817c97a89d104fba6613f6057fa94928c58fc0ad60292c425061ccbfaed88e92ff7db1588612918c3a0fe6e3f672331f6506128b358c5225e8c97c34c3457bc4bc68500e0f56b367802ab40b5daa5e4674eef0a689df0c609ff6da39b4c53e9e54a628c046c338016ea22c38b62b3d953ee3138afd1c2b0220024512d1e45fc4c634580dc0bb960ce3c6fcea1c9a39ca97a1280402902f9d231c46036a79c4e60dde8385b0e75151f75d13132e38e975b9cd86367e30021d2910ba589198128e59018f88b024c19c010ca660dd3280d54358c718c05217d60d0358ad1069a1645f40f809e8940ba86599e2ef3ca8d4ef7507dcff8a9e1acd5827cafa1c1a826973a91dde5d8ca69be2e6724d55b0880e869d80374d601f98c0d745b1e10931d527d1cc6424f0cf74c5891f010c7e62fc38461b2cd372efb790f51007b0fd8021b52b41f1c023579af7b651eb67c899ad6f183840dc8216df717d896982102aeda7be0baed70d848324ce63bd92abca2ef024b67951df39e117026d4a8a9e887131b02e2e83ee0a64ef14f288bb063e3efde72c49f2ad6ae3d28c1d4c0649eca88ee686ce56380c24b809690a573f8b0882396677653081bf2214e2771d5b31ca10f09869d84e36ee7b519859cdf4adb5f860292c78dce3e45da269b4209480b1e945569978b566ca861b7d3fb9b574a1562cd00669ef1467076e3f8bc344da1407ccdd570d8d8fe5c9e00d4c582e1b50485df4b08b165025b7b54e3db41f7d3d62b3e248f2820117db23bf97a6d45ea2fd9bf736f9dbfc28a0c1c25c5db3dd9cbb9df965d75ee912d03468b049ae9cd8f5dcd3d422a36792ccd3099285312b5fcadd3a246a69d91232ac6a12572bbe7fcdc465bbcc5d3545436ea9e8b8002b7aad3316d5871d2f291ec80be0a67e5e4a681443714a3e14cd5076a59018468e48072a6c84165614919ab651addedc4b946035004b24871fc0b62b0d930d02b789856f42325d5855681c2efd568ed1cb21fe02f55f4379f78e49e02656a43d0ec9aed01486af0b460b7b02b805f77c4980fa58d27185507627c771a687ba137d18a4617bc8fdeb5ed501a3662baa64203dcd785e68a4894686f34ec754cbc869024a96734af951ba0715db1281a93872ad39e2393b08dd96fcc1408d07c9880c11a6c6dca5514fa585efa84c13a8e04e272c9c5932cbaea0018304e8d5e15dbb136bb85df35a41110186f303d78cc780a48499f981ce5c5aa13fbcb0c381c3a6d86953af47c555aefbef33bf85016cc9b09f07976f2ae4200472ac360d4acb7c565cfa91d180be99358b86ca51035573de782b9e810a84ac541d475705a0d68ec677d14c8e2120392069195971460f4ed1fd438cdc74d8a02499b3b035c59818062e85d690315bc0d9c304f48a8ffa2871952f628ecde5d1222be8c05fa9ac735a62d6319c6b716927864343c42d1fdce1b687ff1bd08f5f5f4edf150c05fe82953f8491607dae024aa8af6b1567d95eee63168b6b1e0e89f7427195788dfd90c8ea2d29aa31a249b7ef461da7290b657e73b33e416edb17b2fe7bddff9e13272140a1f7ca5510e224c3f94547567ec66f41ebe0df198ad23a2decaa7d1ae52923d05504d05cd985a7abab18fe8d75db8fdf64a55c9a68f367af7844e121f33c08c97807cae070723bcab10ce3539971dcb62201c0af114d6fe22e722640227b393b88cd469a51d9daa3f1b63b465e4dda1d1cf81a4327b97a9c3a114a18897a4afb466fe3f7680445f4cea7eb465845cc0c848c1f3d1b690b4cfdd42031585c5e976b2fba7b93d63eb34369c44b764131efa3412628d25601f052c3e5f084cd287fe6a52d0d16186a9b81f58b92cc02b88d0d73372897e15894781e36a9320ae27651adcd7d6beec5cc257f5109be5fba8dec9bcdae340802d45c01cf2f0c6d3098cd03848b270b826d0583bcf7730e7c622eba1a663f9f783c053d7f5f49a51b5e3ea185f2cac929f0f1d499011180520fe6982f96ddd443a0b4d5fc3d118eb36217866955b3e42a625349167e8db8b20a84615f27fc1d008030ce9eda68375810c4db3dcd0c1add10e48d1971841bf2414d2071fa743b31940cc34caedc32be528e533f6eaf151454812f5a398312d08f425209000fc23117a6717409d93461c6f69f9d90cc2ad43ad6beece31ca6236f2608e0db45ec388c19b343689bccc8fd32a6ebdfc9fa9c83e0bf6117178fe3351442865d4fe92594c3c1ebb60bf490c3508608a551bda22cac694c245e77d2ea1d8da8595f3d8884883dab45fdc0a50bf0893605310bf4872610dcd88d21e1f1717b3dc7005da1e939182e50fdfc874f21df0f5cc184c02496ecca431481010c164aeaac62b652eaf3c02a28ee640e3acdeaa16b6c6ee6c65595ea15a5aa3f02b1adb4478060684c365ddd861533d0d527bb3144365890dc3695235236049dd47d43a394b9a762bb2af0a19c249f50a75a42e551601573f0b96eb3f131c472f3024e2c4c917426af892c08882c82550022e73a0dd0b256c8af8401ea6f129b0086f8c38a1191817c8980e7ff314a4dc3775b2da7f168a18484d9d6708d5314b7431733f989085183a7e2b8c9d778b8c2eed8143295161b182258f976277476abac09c35770983683fa576267277a7dcc1990bdb86055e3b26ded5ab95b50c5f582e9f32d803d781f2079af5ff9be5adc6fbb6717c55562ea8e6e9cdb6b10f30f51e320afbd3bb3ae2e8988af3cfc2ba10a989817320e8dd0158e5898ac782f3f9640fdbf6e0454fa4efaa1d884650068bb01c002f943112301a575a1a91088e897989a069ae7a3066401cec0e89d835b40bff34931dd9aa5dc73dff33ed0d46e90e2923586a23844302a3dea333911854535cbb8fc8794be1d6838d171ef24fde35ca2baa1e710c476faa9e425033b3b357da12c7d451388ee46fcdb80c4414f26c24b70ff77f1f052aa6868bf97b503e5c364db2881dca2292caea2bc1e638de6f890b4926187dce4ce293f67087dd8232d1b8d522375146e3f5c25e4a8315ecdcc04d8fbd952fc5740aa1249033e89b7bc5dc15318993dee5a32e42a6532c1c6feeaaa642fb36e1d139aadc8073f5ac7ff2da11fe166dec95e2c5751def14dd1641464736d52c828fd137abbeeea155e43f534ffbea46bbf211564c8d70a5a87b4972040683e838efe101959dda41519253e1326f743e03afd56181e2198573025717685a290403ae800060d30b7b4e97021d6e7b5f66e77b7b4facd27be27dfb094f6aaa28074a7acac22d9aa5ef5651c818d797a41eaf47e545432011ae42131ec7b308a520d46f0622c53386abd2c515176729a61b722f7e6657de0798b00cf28d27fc2ac0583a0747cdb1e4efb2ee341aba3cc3a6e0e71870c9e1ca7535251cb99015df6871059f684435041a47045cfe8f6b699989d734e71175695c3a4048543174483e16a83b41b434c4e0262bd0579eba24a6fbeea3d0ab490b797b7c6e4e545379ebaf4ee621b578cad064b590aabb79ce22d2b68e8c2f75feef32a773fc5a220fe66478d3aa5deeb59716281de5390a39e7dea169bc91f296e934bc2624e1bc3bacf4bddadfb11544f41dc5eed26b61d7e5ff2d1714fc4af81c1a7b20065cfd81926ef1479719711669ad92d0f810c07dcd1c6709708a659d915965cf6c91c43d9a0618c9d63c05fd172ca8afb53eddf14d754d5c3e186e8c8e58a72d49d9a5aa4566435848261f4f2acef5ced9fa185668250fd31321cbdad7d45081f7ada37d6f1a2d2e9a8150afba07c5ba569819ec1e4419359eb9db10737ed6bdacf1e6618c4bee8e107a2ca0aa7d990942d2d207177112c4597616648c4f1291b6b5709d65bbc0bd922f4f0162fadc1f8ddf830214c255d6849a00f00dbb9ca3f82759dc77efae4e90dd040d75ab095a8eda050a07c98c0f3775428d686c83047578b2cbf94e4534f946e629eb87c5f749e41767409a5bd99d4e6eacd68d020c7a980b97bab13535fa8d2416fe6879a445f0baaa10ae95990e816509fd4fbe22c988af92ea5e2637cd19b7a19314e0a31e41307c4d028b33a27d1abd6442539e6843f6bc88ee5fb50fe3333d5066fa08068817093c7229f4dd12326234f92dcb6bc17eef79da276ceaa6d72ed1acac3dcb5f45faf6e2c713e893dd1ef87537bc19945bc22ca8f5525295e5e3fd04463fe0bd4cf13bc12cb7839a26b74c07d814c14bbc84eab315c09651a9607379a732818034c80ceffb3fd8e2e8be4eb3e6a3136aa0b1a889865faf2ed089c6c4f49ea302f4cc7f96b4fbfbde3dbc2963eb48eb1790670555f404fc9c290ae7aebc0fa01214754ac1db8e7caca7e6e510e8ce73a03beb95e689a548f12dff351238610eafccf355c091b07c929d082b5fb57ff49c2be674c34d2fd9d72ab0caf0c0055a023e6613082056e84c0dcab0dfdfa25719b499e15e4ebf747938c286aee8c5d4ffbbbfc0ab1c7b5541075545a368f289197481f086b00b40bcb20c2b2e398b14157c9b65652c31c3f85af9baba4f6b54410615c5c95142318a8736e01342c095e2be0b997fa2ca674842ccc013371d45403b09657ed14bc45e17e74cf25aa16a66b838564798391efc42cf29c1b72ea88d2b3b4d5c280a30ba8250e86de255b7b29c8996ab67c56e9c0066de216a5e086206533b3df17ef16ee0432d0fbabd48c5609a6b761b18a27ae4ec061367a370ceae5dff11b159a6cffe83efae41f1b0e4a8b3226a69d35b208866ec564f0105d9186743278c74178424e57e435cbc0931fddf70f00d47ec67c9150cb3046ee2fb15cba64d575d09fe71551f9a540f13c3cd0ca5fa8978faf650ba5e7ad2ea23320c6739da8687a6919a7331732e199e8bae0c932a4dc6b9d0036620c0e83fcd9c373c209f7e9ab3fb65f5cb66a0aba028100fe28f70a3300eacc7de46821ecc03da9b5889b179e5a01f885887bb4cce1d2bffd125fc70f0de79b2a3e9431f693b4977d394be415536c4eec417804f3ea40245e7d4d1ead55309c7803dc1820680f4e1b0e44736caa2d08b5f240ab84be2ded11f6aef8a53c9ed5992513b851e3ea2817043ac02a2aabb62a40541e126e1b2558151189c63f46ff9e735766c89442299daefebbc0c4f388f24550498a370ad14ed2ebe9d5af5594c6563ad40dcd5c92a5f806da52265d59a2279dc7b43393e756c0d472c108f9cf775edfce70dcec2b9a39bf98e69e4b066cd54e0edc35fc60c2703093b271e894b2882281faeae34112a3040ab5dc5c815cd17f4eb3de17f57f1c04b3e980f9482408dd5c6478685c8fac22514dac70ce58afc3bd27d7f18882eeed6f15c1c7b8519630468bc35f8ac3ecbff587db5048e42717bea2124340d4043845b5d85c03c2241117ded9a54cea2415a9f3a04330a8f3b61d1bdfa6c70c04a533ce4bf444914ce8618cfbe13fe05ec9ed13de4725b2892a9b672a1dc19d5d48fb9c1402675b1fd740b25e2dc19f147e1b93291253d6e36d9dd63018eaf62df462cc168ab2142ae1db8155e8761a1b6fa827b854e40af44efd52342047d74cdb48a3d1f4b52b983107806ae25f533282f4ea83cc22d0c34cb9575fa97ca73e29e758779c195ab3458ced87829e8bd1cb0ab1254900988fe6fe1c21dc03d393f3ff615a2baa0a53e83b15d8f54f97a10ba9589366c53bcad4a0152d62bf3895c0a42b022a01c71cbe83c6436c086c1a0a080605f5d87c71b40c4c817e064d96dc7339ee302d7e50640a9129806b6037657523c18351cb040d1267644feab24b8aa4e9c91b0057ea248103aebfb6f195d928f963eaaae2587362b6905c00b95d7b398e7f564c46651c023feaa6d7bffa9ad29c260cc2ae3ba97162ea5ffcee3c92ba3dedc087309a98f4964591ecc4c9b00e709b165f50f887846dddcb9d77dbacd610f11f40b800df282d746b46f48f2f61fba194cc4226c8166e0ae23d391b942038ad420a808b8d83a83544af89907cf381c6853b7e569832dfd048b6f2799c5a927a548a297afb363131daa53e9c7340ef43951f9fe9cacc1e4c2d56a8538bb81bbb3ccb383a3b9a1a787d9d8ace31af5e26e70783453546ead4be58bde577e472ee5f342076487a1f924df20f76dc3baa13b8fdfcf3187dc76841d2f9e88e3525503de569ebaf4dba27c060026aa4c2d85fe90d01d4679c07a02e10e045a4fdf883c592ae094f08bb8e95f26f7052be60a3fe15b5a492f03feebeff3edfcbc676288bc17cc049aec25611d9925b92a479f54c21dcb96a728ee436c6f052e2022fa78f4deb310a340986e90be3d1f0a6400ab14bdc131be914e2f90dbbb5408b2ec28eee610dcaac02255bb4f55c5df372e109d8e1720322593c45998b2166a76624706b3e8febca634d50f041c99d2234deb8be3a175169c732c6c063e99a0cf420e11a7df152e7d8fa31b3eda9d484b9a83184efba7553e092475afd771905ddafce6f43d2c21636358018c437d2d6106b646f7603a8477c1f44c77d1121637d41b5ffff13c4951e84d1d37a22500218986a17bf2cb5792871d42fe25af593063546e3546ac573de0ba1b7932eb14a521bbc81cc4e50ebd3269aaaa8d037659862d08c6523d8c208a153de1c44d30481b0505694c8e793e37101c9dc19f54cc4f5f4e9891cb113ac3045b99b5231b28a973729d2e80357467991b02622989ed63f3eeae89b2fa6d7a5eb47e679a7198cc8a28d5f4e921bc6b95e3ee58e1648201812c50a0802558f183ae7729cbdd4a0d5d60018786f3b935ac1a647213a3704345a539cc1487a7a6f4a3bea06531e171d7285c1c8003b3b68f26722e686e7e42df2668367b7500a705027c07e36b6d599e4a5b0b4f3dd54587cd40541031d25dc098b362cddbc8f9916e69646af2b22c2286eaa09fcdac4a6879b76f30be8e43e34ed444ae3664e606d7179acdb23f12d8fd20d77acb623f3f046126358b2bbf051119693b4a95e21dc8cb4d9fcc31368c519088f0a1e77ae89418ccf9f848f01dc6dd4285e0fb1050fa607da20802294cb54dfbcc70f2b5e448315591bb2fee855d5269195e941870765a106817ff325edfb3f483ced20bfec8926427d56805d7c93c5dc212de9c6b75304519520610c9b0ecc8a3c7ba7dbd003f6933626c3b9a2408da7b5c608c645dee3575043529d33d0fbba04eb7b41aef96e0da0b84d4cf6a18c3574581b46e4141aa462599d8c3c08ba8cfd38061ab68d57c84669e5c2923f08874e94c49a468c0170606695ccfcf47b27aebf1131d2eaa60685478e1a9a65957685bfb10f271569f3fa7a3cd584a7a1892ad6705a786f59dcf3a2f755c0a0986b2f29da81413677b51c5b68e127b5f86f51bafe4570890301e994c4cfe197ec5988d21208a75658c5f4c881b0a326310deaadd2efeb4acf3a64266903c054e2eb49dfe81fd9fcc95a6aa6ede5e4bf79a35c7220c78bc50158b96251ff46439315a280df215b5562d8c3c625726e6b507cfd26cf7b4180824758516e7f7ba3ebebf41aeaf0fa78304ab9831efef809f284ca2b2f4ce62e9125332198cb834b21e7f554608f530f3b539f555b37523a9fa7e3fd58f9577c0da3b847864f906d4d7affd3eb09d36482093bb78692d107a34ab364a372815532017208e549dcf82bdca812e405dc817c3e65558131c6f5b2ac68a6923c57be2ceb1877c270b0ed3a93bfec1c2afc569adb86034d9d2d98e364442640dfacd03e8cb0afd310af177d77614db45ed073658ea3a3ba19de4bbb4009baa8f61df86fd8d38e8b1b8fabfdc80d2a1a08d9cdef185ca35b6f5375fd07593357621b54010d0d0e54c0465b3e5018bd133172d9ca97e09cf3b3c2f42f9b494a4fdef461fbfe653068aa44d312b1ea0f26998e1e29d4e4ff1b1e30a814f0a2a15fd3e377c10f317bbc316cdcd41c028053a08d9f786a8a77315637ab0cbf54aac9241e488b7fa2951505258426860e64259401082636db4b6cda76bc23fb281a45b56d21b6a817626b0e6689ae1e9280752403ef9375dae2e39d04189ca24b78e2fca09caa474123ddc334ce045fbee6b2b05e19af7d25f70676fb22fce14e9fe754790ed1483c4e6a3240826ba23e206d32206340600946aef7c1071109ccabfa549b7431721db42f7e4532acc22dbc230d9c1b26240c7f6da57354141628edb1089a339b287954cd1f280caf0c5aacfb57cfd667ae6110cb8a623e07781f32cbb26c6b4007444830621ebbec4a950a2f2fae1ea418250c68614ec56526979a81bc80189e615d0aacaca2cb9fd5d74439b4a0210ba05cbc8726568b31d2eac008ece7634cbea298c69f5d6f042257b6ec6b8d018eed9876cf22e923fda3eccc313a7bfda08c82a06261377667dc2452914a04b37246a3cf1405a20c936109597886d6b03a7ccf723580a06217dc8093cb194e5e2ad5cf3e97f964021ae3c84fd1b7e911b224b04cdf4c0dfd2e41bc403f37949ff9757732e016ce7bb58ecc1ac992fa24606b4c5e10b475f495aeaceb8f7fcb7ccd01b2095df7b31e4690a3955a5a116e634b407dff34228732b070af1eb3acc53de94300031af1bbecfdaa84348d3402465bc6e7fe5ea3753cb8b23efd30fb849a9b0309fe1800816210a0bbb9c161da0d04dd6300e2e9a4080ba3700d84b4d0964aa1627addc007ade4b1f826fd1dc2c0528da0be062a26f3cb0e04fac27b2051d712dbcf22ebf0bf78006398d7cde5e3973533e6fa5c43d20b69f6f0d42b268c703ae535e407d71967eb6e929e7be04e72d831fcd436a2ce97ddb9299b9ca9975faa96747f8a772aa0973625d68fc91ed68ca165c4895bdd0f00afd118d9632dc57fb3550006444c20f86ea2135c45c92fd0b52c7c3311bf4b11f2eb6beee378919ada384eeff0dfed63b966b091735643244f45488074b2a9eed8be7d0f6658c8d158f2ee010da8b9f97dd6fd69dc2e82838df10ee2d72f449bf5d58f2f6c670444ab4fc08207f6338bc6fe23a30fea1c4871209161206473b008b84c256df44d6baea0823eb5b313ca80d279dbab5d91b652a0b2abe56019aa2cbc8f836b0f2d73baacd6d37eb2074ae9f1fe33a6bae7cd5ef0e4aad58f8670dba3a621b4b913376b7ac68f7e8b430e7149002d29ef521002bd6dc193cd00ecde3125d4b2365c763067b7c4c4dad30050ababcb7f3668fd2d2968fc069aa0071d29a888ab92ab2f1269526f60c1d7daca4ed2b705383022505220d65bd570b156eec26a1380f5b84c1e7e168864438229f0ab3ad5da0ecfffe72e274b6c4937417dbdd7820558404cea8de4ea6f1ba08867584d0c924a9722c73829eb406328858e56ad7d3ca2488f8452df88d7b80aea22a52f6f9e607ca9ac500125aa3acbe0487c6ff988a58943272ee31026b9bec8307a986f176139eadb8e449d1c0ee231d4ec9d239ff5ed192fabc4e36095a6f5b4267aee36ce98f8e02ac4ffe963b93241d8c3d52f00715809c4421a992ca2076e87462b5a2bd03f3062c2ad3674f79555aeb16633e6954d647e2db5668835125702b4a0ec7987c8541a512ded1e13566bbe1b29e4a01cbe6b83bd68572588ba22a8cf5a656a1679168ec01d694b6db18014d292a95806020234137f4e00f4ca3b06a7f75fd905dae5b9be25279f0a89c32a3c5c4a2e5b9199174692eefc10ab5231041363c17f83aa9aae8fcef7a87673025722294ed4310e72bab51fb7f8547597c9eb65ab9d68bdfff4b8899f226efb753b80c262715c18ad34b1a5d6a77aaec1ab9c523d15b96659f4ef108ad860435d80ec11c57381d8178f3b8443d9b55c1b10af7dba12be9eb21f32e54a60a2e81d0970c3e27bd23d4489872141da7cd068a80a5061272a58f94e066ae0b5895e9a1d7cda616025ce00e1f89b6a19451b6750332af182a7d04128dba1189aa7e95f338ac94d0802a201abd17b7412d6fb1aaa8d21459f53da8d41be488c764f39a1395ae03580c0ef03a54bd276fcf0fcb8cce9d2f99bbee1b46de60e04957252fdff54e1dcf71bb7052d797b7dce4eb75aa0c0fd067019a06308222333bdbb88d547339b7922636ea236e6d5a375415742c33a3ab58400d06c15d3fedc75ef7946672b0b8288c4fcca8edc00ee4bde260fba186bf1d34efb6bb1eccf17c80664f3cf38f4bde0e1cb9300583cc1aa9880c79fae2ea5ebbb6f73cba464c9b62acf385e1da80f8b49eef19c6a7b4807533a94a47c6fbd2924dd6ebd1a7736a98b8cbd209a662e0124da9c07777e0379ef8421cf10aa70ed9ad54fcf8f8f6f202453ca53fc30429089372fe56560a502d85f8b762550027c2512e8b024e35f682ae62db5ca47c9fb38653369bd9adb756667273cb63183c1681e5ef5d8ad88d0091d2e6bcf2a2ef5c6a2b00d623970e769bdf147d7fb3b4db51271df1baf2bbfb093b10d878b139deca873aba98fe19c88499b140ddb9d8e70d49c41b1f198a1f5f772930a32e7dc93376b1c2b4891e02faa0cda10aa561dfcf56fecc06ba2ae5085b7588e3945179707c66e9082605024e68bf9fef1c0431b18be6b65a53a636464f62318ee1fce256b545e374c4bc71ddb1d07066ffd3f501b934ea07686f1dd707d24d0d71f07363ec8bcde2e5214d8b3c2a7cbb2f14dfc98d82f7df30baf8e935ab7f1adcafa25aaf726923ba2ab78262b3dd0d34adf7844975e3d89f3f634e0c66497adb27906dafbec61371ba0211cc184655083c10b3e2722b2e2b268d30f1165fc7a31fd9f5de6feed7c06e7bea0703f95a903a2dea63daab366e6c70d649f14f36755022f033af95c70c01d947cd305147d0785cc8552a17d35a750cf872f5be86a5554af0390f50f31e99eac3bfa8d2abd7ab94556d221ccd2ec95d568d6fd20197a309bb32123eaed30910a56eedd5b83034ddbef2b02cec1bcb8a8e914c22012e3626eca5b472822e5949cd16791220b07897a48a7997b07dee474938011fe2c4575a8c5c6dc6634eaa9333a381d24c1ed34f688913380e7a6efdfcb226caa2664ba3c90f20cffcb2943d5a2bf40d42a49151eac1747b8d08274a58f93ef4d1097abce380d1072e455b31e96c827664d62221e3f6f5d825c83c9dcf491e60b1d78c519770bc7a43b20ce74baacb9ef70b576878b8bfe4407e075f1a041a03cc82fa114fd11b56873776c3d685bc961e9ed03ba4d4399696ad9251f95815162f42bb50f9c31017094d38c520cd78ebc10c051935380f7217e43508f968c5e0faae871ff75439b385455de3e6ce212d89964c77797895570ca790941f8d22041ba6d54894140737f2c2e3296cdb14815232840233e509cce401fca0ff78440da93dcd9374284430a504c65cd7ed8e64827c2661820c9103f0ab021f67e1bcd305401f62d0fd55d44c4886365f86a0e5fad49794ec4e9a08d8b61c12787cd2de3ddab6b8483b0cc537eb1ae05d5b7e044764053d6505d5e6eee82446fd3c161e1f15772ffceba092e241a0f7a93cfd0412a91751422b08f2ea198cc645146884ba2804d3e5941aabc82ba9b879831178c8640913491d2c0251ddd5a3fed3daffa6778328a98cee0d55133cf9cca66f2a53638f23da16f7c8f330cd499b2821bc377f20ff73c45693f364b0e215ba46d8d85280acb767db73b12a793449bf38be994003428a7286d9b91e40b84c9fe83274c66413e2c8ed3cb2fe826fc8731c68ada09c217fd61a49b962d54292c246fc11ebe7c821e46c764ffa9b53bff79e7ad1b3d20e3bf1d447e5b7024985ab7557771b47c8e518512fe86a28e671b8bb7d2312d54adf0c8213a6d2f6d3e1ca6099a5100bd610c2ce93f5bcf4884e3216026a428f8787acfedfb0c9f4f401acda72df8ccf64d088bb4df4413dd365619a107fed82df0b4ab7efb502ed5a3296452bfb9f2f6fb153766ed8cf42b15e741925c01441721b6a3e9d875738f58bb68013b47a7bb614c80860311fed3e58efd02a0362ab0e938676db6aad819f78338d2130f8d7012e8b5c5d9a53e843c7179c20d7b796a02aee8c448c93832583c55318b00487e7e2dd52c108eb5ca88722450b4360ba58de5bc369c11833aa22ecac51c3b21ec942387ea008ec02eed9b2c945b078262bb741b43471e8e6578b0b81a87a20ca1c9a520ecbbf9e77a9adf5bde8a123910fd80608890c301b7d4aa9e5b0a6120437e3e9e3024ba6a92bfb67451f6969578e639b8df8239a533af34e4cf5f7963412f8651507c231f46ec15a619aec92bd7063892ad9f2f902d34f0e93f8af976660d65ee0f5eb40ce94804bc7cb23b061e6df10a6e872034e33e3db4c4f5c60948aa2f248d388df7096ee98b82f2b12d8e2be1d7ecbe8e8ee5b8c899b29df04dcdc6042241388bd75427cba62de0e45808f58ca858a50d8fb623c2e4d53d93e8b932e2acb2d96ba10a632c5e85387d4d6b3f3ce16bc69a84e5fb11e26bc27581d379c534f9f260118ef9c41b73372ffb342cfadc6e65ea6041249a8835fe086b6cf0b0626ad710f437477f1a98623e81c1b6d54dfe3d78a3f954f1e350b38037b65c31ea13491666765b9748c0d7c402eca5ab4bf807af0f21533f5b8ffbfb3cebc4283f7ff63e943b91238f5d104e68af485ac2b237cd20fd092660f4912f795588fa561f7fa12936aaea07c810615088259321f542dc3e2846e211eafd7e141950703868689165323fe512de69ffcbd56804836b7cd706a0d6930e4d2b046c430a934618ec8b83b387363e241102b6c2cdb42707ed6a9a05d1d2ea544b540d89981774fd38591945c28b5afbe896d0e027d307bf6c67c5a97a260b7550a7b431a04e43c12f056df9d37b655b52352c93d0f88b6950936e9d89dbcca6a6628599942e3cdbdd2e31634e42772b9904b9c3c1902133f63556ca40869e07ea6cc34d5aa0c70973266190efbb702861660242523599aaa819b50aa820af018557f62092ff08b2e41244df95694fcefa67a8e4272c52a8936f067523ae14d72b88ba7710980a1f819f9a90b3426663f10d830eb6b5ccc60537d6d47350cfb6a3f5e1d20a843645fabbb033b39117e591f0cb5013f7092ac3336bd78fd3d72f72476811a4374834451561d62132b04e9137ee33222bbba93b238f399c4f064101cecc20ba21da38f5d8e4302d16421d2e574019ab61f4a22cad43c44d9e977d39f0f1c6af59aeb0e98d10962bc10c0d6d8caaf2745a43e7b4e41d938cbfba3e94f4e444eadb0758ae6acbc3f73148f8c9f37b44c42dd089368b326e5d375872e2e75291c1271d2cda4231e62220feb1187cd2a05154f027ce082bb19a734f22d161ec6b6f34be8deb83cbac3b41f4c8b6f5cee37c98b22503cf2f1908cc9b9f3565a9742fc32ac98e44d3087f6c7f729dbf19c9f96aa116111df2836b7663211743487d270129e17f4723ea2ba3972e006ec5b5010299855433fd8a8990cf9ef01ae8b3991ae32b357471ebf6b6bc142e548222bbaceb532a9116018467fda32e4c04f7fe75f536ccd634d8bd0250687da97eca9ebc8d44359dcb299e367d5901cbe25e9d3ef81b7a1c9c130feb324e34ed9c5ca328e3458be6aa6443c70accbbc85b7ad19356e71269df1ec94ea8fb0b401271feb29f217c7c98f63a012819427046b0fbd3255a30843e8cc9f3eddd9da5e9966b6c4c594070e49c2e9ba685abab044c61866e1513b42b677812964433631556e3a63e00cf037104009e6e68219ff10abe0678f6a65134be557a605b381be83d4367d280e00b579a2a0982286bb2e2cd32950d2376ee90e2fb793cb1f2cc541b2f8ba2f4eb3b225eefe27f4f378e76688132248fca6db21f50a4f3c6b22d43886e090828f77be192ed7e77ba2871b284d3e843fc9761ea5347fd79805362ddd592d186a4bf84d27dd0732789f421cc9e7da8bf3ec6a61b1c27d0404e4d205e241b177b85349f7e7e70d55c05e621b92daf98aa6b11dde1139c3dc01636266a5d317e1c07ede9216be2401d53d6ed70e015ede263187c6975c7f32e96615370ab382b64114993078bf5163a45bca6a0edc095278346ddc067255751ec2435b052ed190d01912698bae45721098a72d64db830b4567eef158bedb4aaddcf9b411628daa40166f033bf600628d9926e8b7b59c1c63d27ec2b99c11b7d097ba7c6a2cba050ab403e987b474105f5ffb7ca734bc90e2fb871b0fc73f60a722cd9a05c162922281375146c4ed864ae8b3ff9a896892c3cbcd06f99919e9440691be191df467d3ae89c7f686537961ba101a6cb5017e0bb1e8e3d36c089bfae85f85f26cceb7ad01bd8f5e48c5d6f0b6e3abc5eb3fd2fcffb9d6504f62f97ce326b93ec51a5b0d1014c11eb0980cf965d0431d6f70cd8e669d39d885b56ea31f223a95ce721d2b7e6d089f457b31142bf811c99bb7b3b7d3585a12ceda1f62ff19fe6894d26d4044cee443217fea3923d1951e330101eef901fd346d0f94363630b86d57821a243abe293b8954821361bf805c1be4138fbfe295d4712c11777386c3a2d47450c4419c88dd34a74990cbc6590d24763d4ada8bfe25c44856ccad9e96d7680bea043151d935b1c2b2e30579d69c6026e43cc067aaf6aea5d72008650cccf68b101a0ab12301c50b2d9df7d9b6cfb4d521a3295eccf1794e8bd7dce5c269d457976fa87c02ae6a9c3eecc851c88ab4bc75978937683e9a1735917e0b8c5b6ae1e463f94ec3266350b2b57c9b228aa2206f58b12f11371d11e067561695776ea951b54b467e3bb55e12b72c092abc36467197ee0b640d4ebbc873bb2c7004820935914176913b82ac0a18d892a0105dfe0196de2749b52f63e7643b1d38af662b49979b9bbb9f33d6874d987bf083bc3e8740b974334e70502b4c88350074f6dc6a883a6d7e8638b2582636beae4adf7f2b601fde96aa02ddc875807406456ec33874ad3b2a5d66250d05bcfb172ffbc015f49c2973035794fb5679c6ca8ca86b7344e2e1065085a9ab18e00ff26830998d4f84bb447c0c63581cdd838af510a7b90a0522af6231469f670295d5f3b80f03cae2d5db9b8e65154e4910584ca0e6fea84f5b945acaaae242183d2d9d794690333828c30bb056ded4a5afe43192174158dba27df9aaca985878285579220d4a6ed25d1094d81d45a66d0128b3d31749391f55d923640ad893fe99d67708b94f2e8c46fbab3d54d0a109d25017e961d73e078ee61213e3d291dec5ca8b3bf0e8bc2bda0ae9f69fe2f9e1128856a7f60b89e321b1a5efb324d524880cf68c8b1f77989f7a5e747f7a67542cd67a2d6460bcb2641148a2b07265684046632829d7ce05a750d1802f6929b6b1c94f703ae48e0c53a81a1644c6f5eaa9629c10af02d19db01ae1199047133012ebf03cc6a02caab89585f40902a5207bb9a143c715b05450c86029288cf7a9644eb1a04a66f7d169cc0a8188849c07bb10fcc71187f8f2427d8fa9e9c2e0da8bd437f80e68755db626b25a0a3ea214a1a812feb6378157b43365bc01e3023908ff62e217b4b299394324c055f059a05318fc90f7aad4e5752ce0b6c772169eb85a42d10873641602fcb2abd6c4c7eba6d82e0d855ddd72688cbae6e36dd517a361575d80471d1151b9d304a2f9b2038d6f4a244890265dbd226084d8ee2a83bafbc0c8ce2f052a912b266fe4cc43eab8d8499ed250db22b52798e68db56d2dcaa1a902ad315af44fa12905de5b154926077c9ab21db89114122b3abf087223b7f69665777e72fd12cebb6f297481e8dceae4e19980cccae4612521be4a136f27f4f777e1a2776e5c5b0f3d3ecec2afcfc3461b8ec4a8473e36557e0e7bfa163579e0972b8252b59c20a75ab902934ba9d5f2457b8fb24dd56c893c4b3493f3bf72021c933d24f9025f13869e6acaf957f65e72cda9209ed1c23d176cea4d7cef943d8cee7e923cf994db2e55fa1ad3861326493786de715243bff89a4add9ce7f82e513cfceb19d4f3f3bbf77bfdaf2fc5c485b8e12a81beba80dccc502704b06e631d94612f95b9ee57b58339f32b09d1fe370ce59f8f3e31a8fc98f496ff4bcd2cbb25e5c25189e9d25204b5bb2cf507409c9a0945e3b0bc96cfb2c11d9b924db799fa5d9ce25daceef559b6719c8ae3c1878663b9b609f5968671ececa1ff5986c005900b2a000cdfb8fccdfe71288f93140f45e09e44d2045a0ab88a4ad4f8511cd694bdb1504fce950eb9eae8b74d65afb23d8b4d01536268403c50ae1dc2c68edf5ea9560db6f6443ca7f2086610ea04a0aad417b9fdffffbfa34057cf1bf3fd955f8dfab3c154fc09f227a91bce4175af3fb14925e7be369d032be1202afe4747e27d4cbc8630fd1d3edc3ba5aa30d81d7d712bd47e2bc50d31295f95f5f6b547adf88c479e16b79ffaa6989de71f6c24f34f2bf1863f0ba7f6158ad5f9c4d1f4a34f20a789fbff7c8fc39fc921238e1a7b78400cedf5b20d3af6671a4658f5cd2fd89264aa9a63793e37bb97cd5d81937ef67f2035342cffbf28bf93df2f3f2e75c9e3ce65e8cb1c99edece5b534a7510b73aa59452bfa15e3c638816ed1943f4b7670cd1de9e3144e71943349e3144df3d6388f67dc38c21da6e00c0a0ebcf18a2ebae1fa54e3ddfdb81ece25b2fb571b1c954325dfddd7b6fa6949ef4efbd97d2ef746fadb5decfabbdd52fad18e31b28a5743c80689f40b6972c2e0025b0836e1b449fab7bd88578425c8827c48578423c21fed62fcede271a858e4742ea50a5e2c84b8bb357eb90f7d570298eea8dc07bef176a930aa6163d1a81380fc118e7d11088b1077afaddcb5f73595fd6ac2049f4215dab5b7a6f0a2e2bb58173285651a403bba2c79e7ff98a3ed20128a8e0fb237df4703b83dd1f39428e1278849e083533d4b807e69c73ce39b3108119745844d6d72d2b8e129cba5bc022af388eb3c35182231a8d4853f4fff3b7d6118a522fd75385eda65dfdfa7ba27c5d449eb46feb3aa0a8e386b9eef04fe6679db2e22851b9657ebf8f7d86141f5114ef0a3a6c00df3f879f77f7251bf023e6fb9225f0a8f9e8f1895033420e9266e6e2944ff46d6fe78d3788434c62d237f88598b4db2355105388e92ada209922eaf0544629f8da156857a308380b2cabcbc6d8ff9cb5e987e483a7d3575dd4461d8d3c0fb53f93e9f595678581e089fe176a134843188a2be8f0deefe3117df347e5595fa2f2acadafbca4579cfa1275e0b2d6c0b9c45bd66f57979812d3f54529d5f5ae1a13a7f06c00e64021588860cc4b9a3c81d377d3234d9878a109124274c045d4a4c90ee1abc99026250862fa8a34e9c0e7850ea1d0b4ae810809ed1205165a3412852ef99049a0431cb2145dc43ea02e4670a48b970746a73fa1fea54937f896d0a52b3e1bf48b11ac85fe363dc245114db85801e882901c06173b5074cba6477450f647854e8bfde1a0c54d8fe892e41881470901214e1ca7484185402565831e73ff7925b50112aa033477cf95683a93ae8b910b7d66ddcb74b29a2d7544a843715c1b2d630efeb34bce395759aef5a6f3accb355799c7a03c701a71c098a67381115dd45995555911facc3a994ec88e0d7b6ce8f303916d6b3498d0e61647106d2a99c83a840b3fb656893401d1023d2cbd89e505dc2a7d0bf78696e9594820e5e925e9ebdfec95bfd9fa6fb6cadfec94bfd9e2dfecf06ffe668bfec6fb9b9dff66e3bfd9f76fb6e9310ab4bf890ced8dfa2452d7106de088147d3a3c25bd2fc52c2c267246164ff0b44a246df9cd8ac52f8c52d4140d76fc8146035d9da7bcd8dd9a9a1af48977b0f3ffee304a097de2ddd368205bedc632c633bb000ce089f9f84724fac4bbefb1ab539f3435bb9a99ccd31755bf8e38b7300e737fb9a78a5dfc692e613616f4afecf324a0d7aa63d6741ddae94a599d6c7fd516c96811d446fd15188a29a21f8fa9225a05a13efd2aea9b356b925d3f87d5e44c0aeeadefd50bb3ab4b82b55ba5c79af5b5f6d1a528a6528a78ac198a6af56bedbe6e485e976f415d09f52ba8db1c89df3cc98fb3dc0634a6082b76fdfa225a0dc17115841a457fb1ebcfae656562cb9befabb269118f8867fb5fd87650969ffc8faed6fd7ffeb4a9bbbb5b7777ffb16b3468dc682cf7d2ff34fd197a493d06054be3fef7699de36dd0a60d5fe33317d340038d6babdf9e19947ee0c13e29a594babbbbdf0e709949ef3f9528c62805a2c251844979513969979516d67d8f712f994c251652cb8a8b3ea9bca4c08863880253a31822d5f71e994bdcc195e11ed80f424a71544a679829d69a6460d619f603f7e0cad0da636830e9a0dc94a4aa510c3015a2c4310546e5459f565c482d25966f9f9f5d99362db5905c564efa4505266544852930868a3e8965f807a639e3030f64745092af8a9132533e6ac37eb45a6c00d9ce0f520f6aad0e83dbc1a714c3b77acd1683210bcdb0cf8c440626032bc19ad9bda463cd5c82edfcb2e60ecf56edf4a85ed654e9b8ab5ed6cc2ad88669626fc4f0b1d6c66a8feb2a19238c1b3b3160559fbfe9ab6c255b2a9156ba1bb1124906a6b28d400695cdaa6cb6c4534bb19d75b88f4b5ddea504f2d1c8415b1252343aaf2eb99d1f248d24128dceb2686834bb1b383760377a78b8e143a3bb7163e706cfce3fb34f1add8d9f9d9fe45599b3f48ad9d5f9f292d9b12b0f0527d867484f199f18b0157c768ec1b3f378020b0662d43c263fcbd31626c7135a3060a32d01c4a0f19f1e3c91a3fdfb2edcb45e1e93a37d9777e1a6857fcbc046191e67e187791ff861de3ecccb8f2590fb2f2f8fffa675f298fc9646975f060a346db9d076fe1b63b6f33fd167e915c6f69967cfa384fc20c9854b92eb66f4e152aeec6e5aa498c7644c92784840ee244b92824824216b8e2c4922d148d28c0444a259d37e064195d24be5a965916e1e93ffd36fc9d2ca4eb7b223b99c45da952497c7e4bf6965d2cd9af97528981d90b825dd76fe1e92ce59e3e727ed9c05f3f94b2ebb327d7ed2cdaeaaeac763f28b0f43853e55403bbf25551ea9aa59333f38961e864667cdfcaa9f14abfad93918da4abc90ec1c38d92bb67aced66a67744e98d4ea3647c3c4ae3c1976feeb2c9aefa3d191606e1a9bb96f404bfa79b7b5d6ad4d90975dbd563b3641767cd0fef489203a9b8a3b6c82c06c82ec9c34b9cfbeae68bfd034b69d3f35a3d1edd09735e9b97536150fe045896213c46513c4b54198dfd804d1b126dd364176484fde40832339fa18ef8f7fd38221471f308f1f97405c1ee66f5af4e57139e33ef0df1288cbe3d2f498fc3470d0e6ce7f240a111d977c794cbaf8b8ff520229f1bc8b3ef0bb94404ab0f797c7e42745a05b5866c79a2d1a9d5d4de0667b29d363cdec42caf0784cfe17522645461778ea871ba56666067564c685737337e47abcdc9254ce057d804ee08fb32cf6da6ca63693ab999931c1ccc5714b230d31129263ffa811bcc7aca9c6e26224bc3755833e71edfee09a9b585ceec5429fb876bebfbfbfbfffae6123a924fac4b5215cf3b1313586793c66a41b05e1ed704fadb5e66aeb3163daac69333de605dfa7f5e9f46f9a33333516f7e5b46e67ee1a3366c2602316faf445e1b8053e398a5deefe7848f8d4984dc89026786019466d74a1cdccc426daf8bf16b26bd7f75c2c3b23809254940dac12a19750acc75155d080846841b53beed083ff3fffcca6599661afd7e797c78020e7ac027dfe2bf67abd6640fe45ec13c38073b89c3f671960affcd2d1e70701d568766591d8f32df5fb4276154413b5fc302ea9387dd2da08b656ab793c78d46a351eb51a8a96637233be3f0e25d77397e0940c326a0b1fefa37ddb8bf93eef83813e692d6804cfe3418d7cdf11faa4b53b941bc162962a888c60bc8cd3cce1f7beefa32daf9ea38650ddb63c154358635b7b63a0dda162ddbab8c3affb1577dc7bb1b803678c31cee28e9c73ce3bbced0faa943bfac36dea3087599a4e5763e58e1611e08970f6be941d5ede78cd2e8c62fb81d094d225f4d27bebad746b213e10fabe6317b5714b039080521b4e7180dbcbd5463cdfa5511c2cf6c3987535ca140202f3e058088208629f1f048ea5ad62b8c73e8d8964506339e7c172b91acb2da9af4cab9f041352cc20071e8478bd6ed8e1000c5d2e5701203c15a82e6b533ce8b3c692a4e0af14d8f249953ca047f55c2e9773393793c5908213abe0f2d4180c32d414c05eaffada395763390c8315af2530dc050f56a1e2fabda61949c07be2a07b468b918a9aba613c138001fcbf119aa6a686efdf98ada6b3167853a111ec6ca5cf72b1f1f001b4ebd32bc301937d86479e80a01ec14192aa6bc5f72b0edaf5f384d8f6474275195e68fb57c5b61540758e9b31bd725c006dbdc7d43fddb4469b9b96f737ad176acb1bbfd3aedea22daf1c7d78fffd570249a144c163eadfb4acad7e81cbe518c14b3a3fe20593333ced697d5eea5f1ebb3aa9931db3abfa2cd8d55823428f0636cf754fc163eae7b0b6f2f01eb565eb9f34b7ab1db32d52bf5ad64a8ce25cdc0976a52c9c94d80d8cb03e76458fdc00c7ae5e767d0b24b333cbf256fdecb68651c71ed6ac66ae6e2f04d6656196c7baeccb5a6b6dceaa405f955144571ea0975d7999ffb3af2534c5f1cfba5e2519b4f73c5eadabda95bfb76f79aeecfa03840de8933ad1a9cf420528a5a7ae6e6d0a28388b5a1b69088a89592c168bc97eb0a0053fb118b53103bb05e83f62b916a3c55037e893defe76fb31f3e376a3b7194522f7d4101c671c179c8134b006da7e4c3142a14f7aabfd7cb313ed67326ea507903e7d5d43a040680ce102acd582080912d544cb49205704300c99b55a2071d12269eb490f41b096254718f80baab061d6b2248d0f2d30b09801d7b2648c1cb4ca293290c4ad654954182df0da4a3d51ca79b33b90d500cbb52c995ad26ac1d62a7ea0a684be2f8f3a14f92e0a8618ad07a3a498b95e19658587ca803e33ae07087b23112700620a21b66d82076a24075a040d4350b12b18553481628b25f015476c1c9d9d9e30463d4c70a29623c728440b75db7eb8dbf645d70f14743bd125be2c2bfc4fc481e310383ea11714c715751ee2ec9424e26b5b7176d2db496f27bdbd6eb7db8d07a6824a6228e3e4990fedfa4c684e6a383bba22447c7a362cc3340e87cbb88cc3651cca8995271f29550b7bf2eb76f9052b618cb30223a1cf8cc3617c2fce8aef7be1dbfd408105639c15d8a5779fc6b58830d18d5b964fea8a540f339c450d5161155663a0eb0401010e1020a91ac0ecea0c8bec844784b56dabacce9c55c4b2446f3f04b2acd1db0f8fa021d42c0b7c1b92208c488f9662028f59fcb8e5f1090b7e664e7a957d403e5f65a254074632d417f6e947353d81281ad8023df4e0a27d18638cf10c139b1863ec79175fb3868065bc140efa3431be33a69b9fa69940470411395c0e87bbd574301ad0608783cd66475447f4a967dfd027c383199f0c0f66a04ca6b166fa3413263bdd4ea7cbd9643fc40ff113fb89c578f24b16628c31c6175f4cc3a1e75d7c710d8f883e290d9fae084a1659ece8171bf26837e79c2bc618df1a3c88904b021880a6468d5413354835a69a278c99ffc44972e05a0eda6da99e6bd7b74f6a4ce879817ba007bad96e365b4df6397c0e3bdd4ea7cbdde8379473ce39e79cddfdf33f9d4ea71c7f729d5caeaf3ca13ea0cfec028914b9d7dc97c83ea92de7d9ddb183a211514491c3e570b81bedd34b7e87df61a7dbe974b99b6b89256eb69bcd569be99a682287cbe170b71afd74432a9c0aa7da42b5854e25a412cae17238dc8da6b37d101f448d56a3d166315b1135e5c44ae129125c54101e7739e7d3cba8bffca180f868417ca82b44b6911370a44198d289231329291f95510b9df3e757e2bfafb682720129e56572d3a74eecf35e5cc3355c136dd03456c11283962f5c521938217941ed00a36464821a9ba4503f317c50a55e8f620199828295634ad40f1d8c41326cf00095830fc63066a09e30472f6250379951063484a811e69c615e83868c8d041cf919fc0c9450e28978225c5860714522914854452291e8ea1a149c75b21a2cb5c7a490645708bed5c1e0277a146a08a292ec805a71d220695bae65da7edfbb8b441fcda140715e28f3981b1a893da17a429f7a171b1afaf4129be7ec9e73ce3e9373ce39bb995d007f3a4da1cf995a0d77775c33e333265b123415121592996c2693fdc068b697bd2cd7322dd3f22cc73e6d5be2841337dbcd66abc9622fc40b9179322cc3f24b175341a1826297832951b2d3ed74badc8d0926b00ddb6a33fa61dc900a062a18d00f36a4830ea2bb3bc63639368c437777cfe5eeb94e3ea72cdb27b5d1bab3dddd3def5e9cafe7b94773824459e801d7f96d93aa06fa3c611dd69d26d3a75d4b32146ddf26272827284768834ede6bbb51176d7284ec2a476875b3c9b1d9e4dceed327726c9b8a3b6c72909434ac95a66b699323941354ebdbe404d955cd09cab1e520b126ddb5dae4d8ac996393834428a7669353cb09ca09caa165974d4ecd9a7557499b9c206bd2bd332c0fe1cc202424c660c40813426a35951d74dfafa10dad3f8eedd6fe576aadb5d69af6a54e6da595524a29b5b4de276192195ef0022d72405f30fb012816e78d7fe301a2ec5c9ed94687898d0e135a2dcda58d0e136bd2ad33e4fe363a4376e53a435b47a7e3c49a74bb7b9edbe8d8e8ec3cb4d171c24427e7363a399d219d219c356d7472d6f4d24667c89a74c772908b31d6a5185ab37e186a8f3cf9a0bd7ab266d51f0e3d6c84b64f2bb61897a0f751c73557ba6badda0bd8fe3648c5fc3d68057e9397d5e57939cbeccb7e00c3b647fab0a63f0f6be2bf240b16cb9c35defc74e5497f42cbb33ce9692b7b8f3f87fb398f3019ae9424ff1cce1a61d1dfcdd236b7619c688cf18f5ef477f4e5b5a6e8a9a8c3a4365c54020f6bfaa7e8a047f565a6bc1705687b8f4fa6fc609541b7388abff237a637a1e557de478bf8404adf22963e564e16eeae2f970f26faa4484c2f4f5a0bff86b37d9ffa5b9ccd22b747201580f4fe22d802f8d7d802b0bc7b25c5af678dedfa555421fdca9fc0f22da45f61f9962d56fd54dc71b32f0b77ebd2665141a8a1187e0e2bc3aec227a1e06c90821e5e16d2449248ffb72cda727114c51acb2ae94af8e1001c26ae735541c43e298ee6b68bf99e262ae4dad934b6631fb1870bd463806cef47f8475f4713fe845f006de1b860021260c080822739fc30d422b900820f458021144507224ad0cafabc3ffa50c462693f504118bd479e9508088e6ec213ca8e98c34bcb1bcfbbac1a1be35fc5efc50b6b6992323206975da8e4acd92b52c9cc8c0000000053160020200c0a878442a124c8d2409bdb03140010648442624c361ac782912489511004510cc0300060000380310019439132c407151766a0bd1a507a78ba24b1ef75afd4550f328e8ea21b544ada45587c1a8df7afba5a1ade1ae76c9adaee32c2354ef53cbe6494cc4f6290bfc0c3b8e3f445ef1093793757f4f87dd7d626cc980706064007f5befae2a5e18451721af138a19076b86a17a48f57e0d9c40173aba6b7aa8b81b3ce9bea96724772734a7561de30f6ad2a285d3ca1e1deebe01b74d7c47a6000fa02068f318927899d8a7f4a33a7d7800c88a7a9278442a4d2cb8f52df6a441bbf4504ee7353ac0b48dd278268fb0fd3acd823d8ba3552e959eb3f08a03df349aa0b7ba3a4ecaa0b01461d1beb92a52fc764f2788013a59c5e6ea9c45d653160a86b73eefa270d626eb04bc304a196cc763b82e28dc8da6043611930de438f4aab3255582e372f0afcac40e71718f974b147a89404c35457005d7c41ca8856c478d68aa67caa1f4023e8e9b339adec689cb7ebbf75e94bffe046b703ec53cb411363b9f047fbe8f1ceed31739010aa05b38f2e1d617b5031f7e90897d0a01350a7fa33b1922d2d39800e3b3769b3b1d801754cff9852d7fe5d3303986ef3d4cf4b7bb9997adf449409c08eb0568f51f374b93e63ecc8c6e01968df87950eaa239fc7736d91b02f14f76090a14c0bb9a65cf2e106c954d6ee2521d9cd76af6c0c34757b5d1a15057532a0886deec76c5d7e12f4ab88b9319e653d5ae9f1a13da886e8d17c839822e631497533454aa6eb455dc0737c4eb3f0033a2761d46c149ad7259410a6fa703058bd896e9291d49a5552faf105bde2f4aaf80d834c5e846146cfd797af3ea21386cec7981657a521c3544db810ae6754eb2901097c3449492fb64ae4d0bd63e0b8ecc3435f768710d31c77e4ee5f89a56fa2c60b5894271d71f1c2f35e73a7070974490c4bc9151a530b55c9518002c0cb0fe4b48437eea66a1d208cfba57cc069e7464bee8af0a182e5e9c4bbd932686df89054aa0000dac7c839c690e17d2c72022e338b7aaf430ac00d8cb3a69654de904bbe70a02cda3b6aa5c46189553daf6aa771c8b8371901380fa5e635925eeee1f1ec52f45e4ad4708cca4187735467d5c6ba9584fb1ed264c20173ab1076d585c0a1ee0d74a84e7a27b10ad3071b7dd5f554fa1ba74cbda4b9e77adcdfe635e9d697b76a8dbde9a7e5f4da377b6fab079628d15ec5aa1d8f8abb71c8c86be3c12111d41c5e1a95709cca7138b36693eea3501c088a53530d4ad3acc135b44d8b3934403ab286c268d60457acf47369d830ed3bc9c24e98bae6f0a4e401c44ce054b136f4f36341fcbbd48dbd08ccdee11a63c3b8c0bcb107429bcf0e6c888d360dae5168b67487268445d154b86e8dcf1517ed438b2a1174e0042c4633d5630f1766a80543535f6332cd0da666f327684686936de1539910c5f224a17e0dea0c928746262e71f888965a013a4811d5dad8a5eca2b222196084412306f1c79bb889c1ca74122cecd5ebd8f581ab6e6bb928c9d120244abb1db722ac382b2ab12d2368cd1326b13794acb7f17b004d31d736729db2a6d57a9b1047362a5d8f26d7d2d4b4badb08e1889aa00461b150827cf5826b3c1db886d8fe230635473194c6c5ef34667bb439b52b341bdeabf1e3609ae2564b93e3b4355add5d136ad14d562a1766f40e0ea643d23e92f03f7024bede7804412c5b89fa63a52f380e6cb0ed93f1fc78af51e75b7bb0987a6722a9ecafa1d4fa2d7d6b058bf4adb7bd7b97527afd895f1b253d415b4b18a1670de3b1d7139c0a7324e4e58d881a5d873d1b5b9951853d506a4a9038f1f7109b36e638ebe54901583fb3e4fc63eaf4d0340090c90919e795b768c7c47cf1c8988436e65904d171065a20196fa74b0e71b6018f8c3298e319268b2c9e376217774520f725d9f92bd63407ea9cb287a7a6cc14ae0691ec4d7694d20fbf83b6da1ae90574d274668c6c1f167f6f3fd4aea762fc13b1e219f53df5eaa7e1883807985726d32d80248bba211eaa758c5fc06d0dd5e5320b8a56e237706d7889e3f26cdcbc4c139e9b5fcd762dd560cd101a7e4f917ef0359497477690e4890a79ea0eaebbcb66a72c9009e44f620774fee5e0b2e2af3f6d326f424f2855517f94e7554f20ec854a2653376c1c7cad67a3633ad3705e88a094cb0ebe11597819da7fe0e6d58b6fc834cf429063bab198c6f23eaa26ce1039553a442bbdff4135beaad75bac9df39be694ed20c6e2bbe802337282c5713b5115efaa8a1dc0eedf8536a7ef8bf666ba0a7a39d0a169c57bb0e0172498301bc6aedeb05d5e9361d2796492e0574d25620f134e32a95b7f5dea1ad3f928d4d42e624c3ef42cdac9f0254e82ce0fe1dc52ed340305bd97523500a7721dceacdcacbbaf6f85d10a25fa8b18a0f9f60e4ab5412ec16afb4da3a5427b4797689ca735a73ab06e29f4ae722d60d66133dd421ada99e6ed60cda3387cbd0523518f96541ffc93aca6cfc683bc5af61af4f1f4b4de36d30d2ddc451db3a903eb964aedaad702469d37d3457ad721d1c4767fae4c695825400ee831479767fd2ac00f89d5cda0a7356ca180a14f8f5f00ba2e12d48ff9290d058169ec6d715abd86f0f2ed8082e6c2ad8979711ceb00163050655f791237f4685e38dda60fa84de87a8b40b6348199ea83cc304568eb11b1023d30f78543696182e3c7cdeb21554cf7e80133f03d6ed597a0f054c0afab8d34cfba362c75ef604bac4a36b7c836c7936eea087ce5d71cb67814e97ef72ac1e968910eff91515b83c77cf88727c9a5e034c9a9beaa39ad249400cb7ca4955beb792359330292dc9da27f36f4a4425317d3a2576624db503cefae55ad129f7300fcc4067424a1bf7954cf5f78ed39685c6627aa129bbf25fbd4bc297bfc20f1df146e8f415a998090deefe43afef7b18d54c156e9888d5b335d9a3495d3a6f13284493291c9c341cca1f34fcaad318a1a0cc0337f324fc1e88286295040eae7d0a17cd6d81bbfd7b968c24d104049e6a0c8b233b7a92d685db3e31dc311d5da41c81a01688cdf31067377075df45fe955563162dcb8a30e1c021d20585b1f3ae875ec4f65215c2dba68988397b7cb5fe51e60e9442501d677ca3c41d8f6d2485e09f726473b48db83a5c5fd991050a6f96807931706436137ba810fbb137882947627e172b0550af3dc057221a4ad8ba65db9a688af68509954131f392d852d8607534cc0e4d23bd8f8f9c7d5ec8a8a1c36661dd11093307ae90cd788d89209d22b021d06b38e35430cca42085d4546cde9e610d304a93688dd14a42393b817eff06f49c9bd6014daf255c4047008964a161530a43b8ddf142bf34ca735e08d7eb1d0cbd5c44e03f75240bc1594b665a1bb6ba9aeffe53b1f3ee28a9085b9e26b13dd8199eac284a32bfeeab5e4f3361ea3f67865d13d1473bf771a84e6dadfc3dca4f32d6c1cabb917c3a0eefc11781898754463c44826b8aa0484dd5400fc8d385079f5afbd9c6c1b9c9e302a1077434744ca98fa6670eee238e136ae64ffc007feba28263c47e95e535697a4f9343600c06e526c43c0926deda280c6b3bc0078bedd7a9215bf30ff0120c998f7d4cfa0899353e0496c1da6e3335963ebc660f23217234568752f2089d16420b485e525af88915d18075835ea017cc4b3357a04432bb6f131486d15571a46989ea28ee4bc802c946b3f0e09d937d342391dc1777375139e4d5819b487507df694d19ebc12565c7d07b914b14cf7afb99b5988a7dd0b4a0ce13b0319f48cdbfae55b31f47fd18a324e84d29e142e2c904e5dfb89127ab48332d59f50a6650eddc27dfa028aab5aaa1f3abd968b7b622dec6dc1811dc05074556f9017783ee8f6d1082941a07e9b1cdea1b7fde178d420f90cda12cc599f33e01996c36b2e41486207666b802328f424004d9e2712ea0da7f8a3d4c5ad31fd2d85756a7cc7d19add1234834580240814dcc419b4e871d169001800a8f0e2442610cc8c309eb215f51f2ff090c09c44c75cc955947c4f864704da90935432e0a6808044d3c9e9440f0f54bf4a5a71b3ebc9b091fb256c7c402eb6363385559935599c250ec62081530fb71f8e203a96fbe6503d887b701e7d3f2d1cb630cc0448719cd3cfa4abb5875c033720d5dc7e9e40833eae3978b227d838c1278b4725038f422f3c246b9e7bc8a402aae697bc2b0418f6a528c8c8a3a8eae38dca8124cce99910bc6b12122e3c4c05b4ba1e506d05698e3318ad661fbbad2fd489a4894e4393e097d913dbcdc044086dd51978acf63230286a213583fcb1002cbe7800d845029ab20dc9d7d707e17fdf3a10a337a98edf2830e9ea608119223967ff6a71919da3452130b5c2ae46258152a2e28b8fa61640fa7b6d3b04fd943e4705d2c7ba092d310791ce04fcc303b23551e106f2ae360faca308369f4ad56348869e4cb58b23f6c9ad723ce77efc6a1f4bc5a7b2c5d663d87ad367c5e513411e67c61a9165e612accca7b3895b6ddc37f49221ec4d73573284b82f7c386bd737030590f94098b3a35acf39f730f43e21e0defcd0d7895c2c81de9dd2e96cc4890e5eb3728553813b11f6cf4a6d25b59c43d1a2fae33a23ad6076b2029ad9f02e2f58d0c477481abdbfbf60a2bdb73ac2aac3920d624899a7b5214fd3c940969b54f46583342240a51fa908c76114c537040cf340201572367357cda10f7cbf178b850687bf23c2b2508acc0a4739aa07fe93c4edcf3ab136b7d1fb9f0583bb69c353c4fd57fbe2ca80a9a33e8a8976a9022b79e08dcd8239fd089826388d80d2e542a35bdba874e8618819b558ec2a83be682454cc795a2806d8c010738106c192924ccf352623798b0eda9115a0c33736c5d5655a30adc7f9fddc452c56ed576f0e727b8fd84ec2aeb482b07c0479c4bb89c32a8e42b9f63db211a8acf5bfcbe25efc07963be0f2e01f085bfeedfa2e8fba1c89e7ee590a7f4a778c884a73ac05d05e42515742e084ea1e270aaa2a1fb4a3b8b0094a5a46604ed863166411bbb3ce08e909dc6bec72411d2e1540130b74fad735d2c1f7b76a30b7c026aa17fc993d550a0e75cd14688d325d3bdb16456ae49b6cfdfa5781929b0d6ebfa7787c7f4eb659894ef5a3711857a27bc19ead01d7604e74b2ba82057253d8c6bbd33efd9154b42f8431fffabade9891342e8b8e85c2a3adb1df83eecd46215e99aece6a48f7cec2b0547c76520cca666974177d09ca54083144e0747af5db462f4785f71b9d45e4db5b57a350ea2beaf6db199735f3751a73c246f6e137565953c468a16a638d1a49207a0c6fd3246cdd1657bc8546b8e19e4c0dd2f152ffc78ccc3846a5a303a76925ffccd09ff143796f46577a68a89c15e433133e9dae2e6906c54a621aa178a235492247bc0bce38413f309a7480223f36023a23ac6ab8c345333fa904ec1caa3648f4720524db3a2e993fff49129cacd2f5694b2b0775c9da4d4b04c32d500a379828b280b90d6a302bf5e70ad0c96f9858a9bb0d3accfe295d6d721da54d319a6b89942ce7a1bad609ad92df50c0a8544bf9bd0b265bb859e6627a096f1cd347f036102a650866c581e843e9982f998af9ecc72769a1917e8f882feea796e142bda35623b4c24fd6fe4f40f38826b938fd4951cd4aedf89520ba214d4ee6293e5d688c40b52756e7d95eb670d15cfe4a7886b84af11f3e489ea7e6b7abae8c9bb10a967dcbbf91b631407a5353880d41888851d94f24537439ae4facf3b8ff11ca961e4376a92511b0553d55cfb52459822123815d9fefce80c8f4ece0d7e7dd4d0954a006e7ba95a7118a3c4cdae1e9fab9904bd8f16ed2795d063145eddee521626f210749e3f89f3cae85033d54c09507afa0b8e7c23b274bd98fd5fd9b92dfe6a21eac761a4f1f65b7f44164aae6112613564164337b958e59e5bf6c2a4e21c0682d025ee20892cf9140560e25c8ab226c6c5a60cb3c155abc4ad745e01827e4e7b7e66daeef0e0f9de4c9ec529f73fac67e077cc35c0da0c477d1b9ae3068608e09c2caecfe809ee7dcff248551a77dbcf041e4b32c2cf34388a5a6ae075b4326f9706b44658c9dedc4d50db5dd45f70b77dee5d7682940c0ffec054e56f74a9d1d48c3eedcecfe1ed4e55209a4cfef61486522abe798fbc96c7844c8dbd95e52b45a5ef8dd8d8de525893f1042e72f970a95895b5ab0fcae0a4198a2ad02f0dd2dd7a046064c60992109ae215cdccb1b45893523c90061597990f0c584682953a19b03fa59835f086a88d6322b669625841ce2729625fc091fae3cc5519bf55d87cc49164cb9240a2a87606a649c2bf4a95241ced6e160ebb939227ae25915798c3de46f7c61c0c59126fc3890518d5d58dd4a7b4303627227cbfa61d92dd232f13121ce968555fc8e0ec3ea53ab8ae5c718abfbd7e1f444c10c38e1632cbfa3484f7ab8d1bac840cd659301201b4a9ae82acc3e9848492b1092e606a98f910a94d10d7017bf12a84851eb3a4bb58a5f726275d2aba6547bfc9dbc4ebc92082f9c4432940d56ffb5b8f8c6873a7b4aed19b895a4c687245712d010aa5ee4d6a948409c000d1c5bfac1b83f5e4b80cc44287ca9abcc1ceda603fd4a02ae61212b2af64473ac3d522f7b628c7bec8b12fba44e40ef04d5f32881d62f13c76038c951cd2983ffa8908e54305674f35b05f077b8dcbf91cafbeffd7bf6bd2e3719b6bf43d142c0c3eb3a9098b369b0cb0f7911d49b163c32f344d9384bc8cd3518507fbf548dee7f590678b06e297e9d494ec61b9f0caa5831a57d0c473178207a186612efb34ba880704e82c156fc90f5d50800dc58ff40e8c83f3b4855f221d2fd606574ea082df31d135de8372f49281491f7f41cfd6db577cb9964680483309637a8985f6f77c798e2796d435d2dfb7de1a3d1abfc1dd9f6a456660c48a86018c84132873f67c0087ee43003080a74da97c6f1cf8f29307daa10594c7607e18f890cd1b51cad24b3fd87240b78f998b33a238b4a1be6d9301e2cf3cc6eb5757bc16af1a1a57b9d755e51e29d7ff09d743b951f427f8c3b2efb4bc61f18bc04ee1a83a818027515a092e92879680a83292d9cfe8ed956b26c0b596d824034703e687f02adf76d79712bdd2044e1c91d466a543bdb2041ff6359fc3246424d6f7f00a78bcc7fd15066ec202f140d20cf4a744a5dc57c8db1251bad43e44a1f3c0bdb64969ece4c7c085f7d2b4fc472136a1d305e1591e0033d4e2edf442fca8365e90f9e5f11e1e6bbe54780ba17bc5e171bb501040addc459b4eb9ec5f21509f9d00c1f1ade7e86ca2e2f0411ca25e8db0c42ccdd4f16aa47c4225943c9f0fa2cce07a1e2f8a4823abeb2e533b74bfea0bf0bc4b8589260c79062bd4d6966dc3bc054b126e96fd0b59947b6217d811bda0c4f4bc8a527f2e8fe8168bd63e913847d0cd9eb81a5fb8ddb833dba77f133cd32d92038486cbbfff9e4993ac61c8bc30d60b45067c3ac7ded4c37f72254ee69c07803394f219b9bc0577b573a77bb71d8158a3a34dcdd62168dc1ea5fca809ba9a8f0bfb3a02f0f539e572efc995a0cedfb554a16d5d4ca13cc4b86a3d1a8d1cce234ca6c6ef54935ee20b6d0f5adcbc79e16c60594f23c2283df098acf7ff5f1ada25660fc316f7166a0347e8abcbcc2810f2c65452132104555140b3e3c80ab5850b6f5d5bc059d636a0b5c8aa1939421f5eaceea7943658c807a0dae66d85553af2ac61f55b91efdd09b779c80956eb8719c101975ce3e7263e086bd11741da27894bc2252039e50939dc842d35920bbf92fcb75ceada9650591302abfe4543b93e984d5541786954b03ee67c033016c0149080f80403289b3ecfcf7f1f47670d84a7c01cc40d97b6725cd885d32735cbafa9a68b32ba7418bd9105e1cd586b4570b3c99709e339ac862c7358904ec6cb6463dbc227c654e66936d1927f9282598088c632463d88ae1a723e6bdd506284081b5455cb199e1c556caaf0520391c2ab456a0ef6c3622d31aa299a7a68dc996137608a4e10107a83dd929b8d2036720abb120c79e007cac666a99917f7c9bc7175e77b411e77597ee1f8f8433ec979c402888a4a955b17fbd2a619a2db5a55d6308ac5d3d5b9db28e1c525cba61b7ebd27a09c4aae0d559d6d5c26fafc0d505bef202fce81bacf21939c9c4dc70742a687ca305def397eaa90e7c26ce017e2bbcc30f249e5cda855e3945a339619f503250347b9a420c244f8c6eddb865d445f47ab16a31b669bc8d342029d0f524d4f6776854cfb3094ce7ac0439c11ff89ed96cee71894ff71e9fa7a2ead17e68c11379ff8f81369afaef9d8caf268128ff92777d21832822ccb3e68ebb47a2527b956d9ef8ee837e78cf70c359397b257c7973e1f95327ce7cab2d0e7fa9361895ea8c22453490b3c76c3ef58f93eac1c34af3b1fce73f6091f90fd30ae63c6d1fcc087c4ab3d93b2801e673a369624671587295a6382268312ad1ec8ad46a79e1cd20f7e231096232b6a1fa5ef5749995e470087792b2d2e4c3ce8a896f6c299b5e955b4f1e7e2a8d1793b5ba12a4b70f1bc0cf0263e3549581bff44e1d465ce724e26c0cf985c05ec56f34797bac586e1572e2d3f971b51e09ad46c3f6c81cfd70f28a667ab2b1dce12e036d9ba5502686fc061c70c5d55a3fde15f6b2007cc386884742df27697d4027ccdbd8b5dc26962803aaacebb5bf1786a93674c864c0a802be7b580e3c99dbb83412b74003be39ce9655570b7d34d5ae0943d9c9135c9c27a8859e80a7bff6d58567a75f1194d7c29622b6d7897bdff72da9758965b7ae95af74e3b9171d76a14c223ff29a05f7ea821f6466bd98391e9a7303a3b2dfbb42b06f5630804ff4a4df61054e1d8bcce2eab23eeb501474739bd0c983554010dbb8b0c8253a732fb17b7b7bddbb0c7a248ac92ffe7b6bbf69c701dd696e27d9c4b9c7190cf13b69ffe57f1394bf2aa918bbe0081e2fde566d7941d20419a9750abe42de7f8f9975e62aa4055bed98a24321085849586becbacb4a08f2f38c409269ef307e7ac97d99cee43aa1692b5e5217ad122ca15bbc0f8179bb6b28b0a1c36b843f6a110fa4b6cca5f21d449ec4744bbebf543a4aa91a1684815e629db98fc140075590b65454e06f6a83317d14f0a6380b3be392887e4af815f68ab348d9493237ed54166230c3aacb4d5073afffa620cc2cc65712b429b8ae9588062c7cb3e946fe5c13793443163c1e1cd8007a0141bc0e112786164bc4a599632461f3dfa972d6befe36aae8ff40023b0608d05a92aeb1a2fd145bd0366c1d403cede68559aa86fc0e8c32afe8cc9a9a1af7860be6eea2aacdfbe6c36356eac3e13bdd2a8f6ad38e67d1e579b0d03e8a4e54781c5f4ab8dd9ad7af6753b08f7a30a7ab53f364dc51824853ff2cae44a3cfaee0990a5aa8adfd6bcb8edc50520ca901cbc4d359fddf5eefcf93b7b32a68767c550fdc7c8992e86d1723c3a42b34f1710dac4953362bd07d6f3d5f0965c55b0bae53ed2f9ffdc2747368519d90145c457436a0b50c42fad71c103c13a1afcec81d0151ae0a582e3d11eb803e6def3ec07a6000d6c6afea786a2530b7490af757d8f19b8dcb0a56beb1a708812253e4983a1bda9b83461493eabf845e56e3373ea10a57ddae5d75e9529a8f57fa658b568158ffac501d2a67cb0f53d1b605312b91c986afed84b5d150a2440fdfbac91fcd60781b9e0a9b3476a50584983e5c958e2a6299c5301f04bb57f9fa48dd3cb6001d2efe452ffbdf47c9011a4c5c77811f5bfec4acd15db404dab6279cf9443b82e47ad32de31c7d324d2228e0ca43405a5ccb8a1fcdc3011f0e3f20ade5e8fbfb0ce0d8c4d62ed9446aa16513ba14bcf2db6165aaf919d4d0054b9eb4c5c82d049ae1c738bc3d38beb63baca3927e25319f883a2d77faa6451bcec1868251b1b60019d0a6b253c02338a49016fed9df05b0b8043fd707a59733db7f890c69e68fa439039447d4648c80e67c4dd5d8e97672750142a4f3e2c0846dc2f4baba20b0ffb4272e04770b1ad7a24c35fd1db3366abe5b9fa3197057a86bea278a531c1732b324d5047c7cdc8a47f517a0b8b48d9d57da702137c5fa358332232800bd4b5abad435a7fec438fc36d08a71d173bf9576935b53f4a3ec7e194d3aa88a1a06a0bc915fc894d76300b09f00065068bcb54d62472352d991b9294109b8d59bd966b338b34be412d2b80d93b1d062db7a856d6b725b05b2452e1e1ae643a1135b298806f04269fc4ca26bda9c87c203a9e775eb5d7ddd3a0ad487ee30b42993511ac4bd32cb7edf62051cf65e95f45bfb24644225d66e908f23c27e32fcb84c8c70db58280270ac22622d6b39f993935ae33b9dd8a9f21ee765293149b816d78ab56e11323f593b94a6464a05b25b7da80e9f609f223904857abb2d74c1956088780a5c7b6ebd0423b1ffc5cc3bf472b382c03fae767795c283be78d807f7ca777b70fd9e5333f7b5ff39ef83e2480856be53d2530d3931e5c4d86b246c7e45dd76bb212613a5c3012279dfbbda691fc50d9acf4b9e0d48bd7f65be63b5a6724c46124e2c7eddaed0ab6b9a5199d714d32168f3020ccdfb59aeb449bec696788c7681b3621ca3817486c9b80150b1c6d2c2e26ce51a98f17338df26c64178124a7fbd08d1218d68d65b9c846916a0bb11ea4b8a9efa102fe40c966f745d44a89dd690bf72b58e7cd4f91bca81555338dd463678c89d31dbeac0db0508f872e671709b89d98fce5816195a8198b5077236f50e21b925bb96832189a1ce3ad5699d475bb73dc1386157251478809870d340c14d3e45febe1490aecc2d71526e5c2bf168f34d44367285d18974d54b3ad46cd6a1d2790f8ea0166012bc9687f990578a1b5f16532d73821281746c4c0a0c06628bb12851fc70d74b7ff737fe4906b3679a64ec44b7f30078b5be78d849e883c8b7f404086e31f81bf98200c9bbcc762cf7474b0f2be93f1eff10d11aa02e94031c3287c9e6acc1ca389b1f3085123a6473f40ff185905e51e6391b4970d21e8111b28695d623a0a3522d0ff3088a2d8debe5150cfa7a995322695203b1f2ac018a210716650e1c8a6b64f2305480e39c06ee2657f9e7020d424249a0227480c07bdb7027d1241d04832a0fd53bd8e1fbe06f84445c4f6be13ce564ec4bde41fbd9cccfb108c0cdddfbfbaea0a224e3ad2b25b5a1417ba0bf275af16abd3e218b72f1145ed558f02291f3282ea2e41d2c53e0853ef7094879edea105d17b63dbcb76f1aab0406ce285b93d4b11dbd4493dc37ebaa59c87ccc30f3a7733d86d929a5fa058cbb38b8c25bedb2a80ade9f20376990e274c0ef5411208bbca6810e923f677d1c68520792d444ecaac95df7b8952b33285bc859b8de1039400404db644e043c86f90c19479d8c6161e782dd1739460d4b76f624685a3670d0891dbf6f8bd3a656e4f52400f7b01b147a8536433f9faffee935c33e919b05d17e1128ef37607d9acae33f9d8524145eda3a0d59ae5a20bb85a6cdbfd8f10bec9da7f14d84776f974a1be30e2c52d0b4cd4ee538846e2f22143b0ab820b3de22f4f6ccc7691a5a729d67a41f6b3b005513c65f5b83f166e8d81a7250666ee781209536d6f1c43cbda864afe098070a84ae56288a6a29e30f436b5a24d9f3c6e2ef9d1d0103eab371b365877226953073f7943cae8ef76aa0d2de8522634159d6aa4de2238b29cbb4baf913f9f2b0f0237012aabccc72f98c9c68c043cf58f338393af406e683456ea2dcc3738126aaa05f6fb8c2334825c9e350be024d6fd6f7ad271e4c89d5652384b6df5191bb3fbd91e681f788c0d304840d20ef0be2c7e17390f6feb34d730785542507f722fffdda367b0a6b303ddd7d73592aa6b6aaf14e1d2eedc654b06ed992280254ade84df76b553708fd27bdca700639d16b87bf4ff7a54063a7322b1dd919cf528802b61438b771298e9035137dc59380f8e828d68652298d75822e76b375028d6971611b627c3a8332d90601f7c7bc4107fa0747251365a62f6aa099eba421b636bc6de2eceb926c2a9bb29ae8e1f334db2f6d0a1adbd8195e82cfb177176119ef43df257e743467dc6a431f12a12f2e6461b9120d34dd1acb273c59a6903a6f1471ff5c7efdabaa324762f655b5ca91ea259e865afda97028fbdc0109fd25d9576ccd7357472dd58ae275eef01b306b8a1eafa0b01c958de9ee06f44e8d0096c7703677b90b672610e54a5d1dc085265aa06a9a86010b34dca3ef925420521c10443cc4443b82ab598439c2ba714bf808d3eb3cb6370089d4edeec0a8ee042e9dfb8a5677291bb9d430753b386c1297c6cc16c1674a90bf7b77dd41fa49237554a523442286e5df89955c39a733e11cf6add6310ad5d11b536fd38826317f8d1147e7d761ae7ad634b82f6478a4eaec042b718304175fefe118b8f7e61ecc1277eda64030be00ce20b8abc360da007cc6a7b993da71ede9262c5f49461e464b1117cd17d6aa3e88f7b0c86c27705c0c396af60ed3f938342005cddf0033c12d7612350e06bacdf307b527567d5b6cc46bdc533b0e19b8d2c44147f50fcfde8806ebdda4376fe52c353634946ba08a522f7248095800b5d621663f986828094e696c2f46f8a936c62f925a1145f68048708dc582cbe712af887d79683f946e345191ab746ca49b761fc894c9b52e74801b333eb7b4e115969618181ebd20fe8a355d2bcd804735e6fb107c050cd2b4b5a3123798094693839a6241ae80369a6b5c7d65c0593905eb075a315ee44117439ded624937283cf5afd8caad20cd61df2f9bc5473b7e70e089d0960d1818950495273c9cd888f3227cc22a50e16127b0eed5a1c74fe18453fd0351e1b880d9bef34b8ae9f0385e369083ad83bfc2ce7b0d212d2c37bfa00122c6dcbd83b4e7a3a8d872f8beb748cfe9c69aeb8d6c381d0925117adcf16d39c247bd7e43892d78af980fd4821d65a2fdf7e65ce2397a8c331cc575bd58eed1d14c9c914086d460be161079b6e194562a6b7b9f168a7f1c4ff083d991909ccea248d8e0520e7fa09d7cbee24858a55bc33c760a44f5010d8fc3d00ca7627cb110fd43679962c7e48e5fc7642a91c79894ce50eea1a7a6c529a94195e3f2b8afad2acc935dde1ad8a337ec2f7f668522520e05d4a432a10c837d0db98493c3414058761e0b2b696fd7265f01d96384361fc1731d43e608559a9b578c555da003a6f367f8d804eb8d982020e9f3986b7fa267135a5dfb72110646f1aef6a9e2cb51fb9cd1f4a9a852c435143efe89f4acaaf2f68987b43cd8b062eab665696aef9360cea1036bed788d56a337f1a7582dd34b9737c00033a636ee016e8496aedc139ab763c70418bcc8a97d3f6d0e9e43923f782cf169603c8b4036418a5b0ec03da6dc7455b0c74d2525e1b3cb038b73b7e1142182823256edb3dfe1eec52d750958b2381f116ec95d93eded692b31f764bfb3f729e09ea554269dd418225dd0edcc67675e8a431ae73109b6b010f07a9116436907769b4bd769605486c095c43c32f18b3a48b5b4a15071be2b0e5b8cd04702c9f5f2a541c3cd508bbe6022f5a0a058299867241b007c8059afd1dc8873f81ae1a7b9534b886bb1a3a8c1f62bb4c8b96d6744d5ee8c835d8f35a949e22e44a8b8a728a69948a28f32662611e28308b8813ae74c205085c594bd3a4c44fb90147c3a96b19c2dda1b43d3224b243e4e22fbc0aaf0055438eccc5fcf0279b8509a2033a0d9e664411d984dabc2a017c711219b26821737342f8be46b3b77f2fdaa75df14856d753be6e0b95c0ea928da1a868ab791bba9f6ffd5a32f8638f23f4eb4e1c2c54a6cbc993ac96eab4bf4ccf66cbdf1c2051395dee8769540f29764664999b3f166c51f587cbf70201c5f7ae4b03ece704acbb938bf5da4418283348bcaa6bdb67b5e373a41e1224a36de9e2a79172b4b7accf6bc9ef1c28289966c746355f2dc5796fa3cfafed6bd9622a79cc8177896e6f46927eb3c8ed473c76382507d1943ecbf475e3869018bee4465281cc533a2429547053a04ab82bfe9c9508007dd8d6149b67d0f287ce0cc48dfac771535a82c5ee22a0c0422140443c671541447c5715414c71e617129b7120b6471ce1242f6ce3f79b77026d980691cafa0bfd9168b6dbdde97bf61f0e96e90c3247e449c102c069901803b13b8ef3f0ada4f06ab9deac0a792017ef7cdc27cb1ab707e0e2d7283eceb08f2f67c6f082787ab500a0a8c5da05ab62cfca06c2ea0e4f06f913db33f3111488e676cdfe8c71c3aa0b3efe37e78394bf1b6918b8bfe9d7a35d09adceee3b3d613e004706b5609b9069f1ddfa240e9db8163c7d1bdb9a0a313bf2bed0dc99f1bb5f3df37d15abdc806f37f259097d989c064c37236c829f40feb0eb8d71d00cae5f6ca7240cc48a709d80f8d1146ac899251b89602d8df06050e566292950d415bd47f31e431e8741854dfb9db95d3be9f31849978263b2733125028beb38d1f2a8cc5b89f398bb9ed7a14045ead8d1b8ce36c497a5d3067708f2caef9b8b51f2a3993026aa104d46408bfcc0cfd9b41e4806072b7c8db23aaf006e22fc9e87a98649c53470df6de4864f41cf62f5d399899ad6f7480a489fcfb1f1a2f0468b8d43363d9464ae5a0a2e8dd41f4eee1864aeee5fad99469d898c4d562a13b5c1c0fa96afe267b66e60444800cb58d4ec581e5637f8cec7b8ef92340d5bbef65f6786e6586652d81d2a84bfc57ae02fa28d73fb213b9802862e1e24a6e43032085aa061a0118c3c89570693634d59a5d81bc8eae533b3f3523b35a6ff2a827180f97c7e0f48020c8eca925c6c3ddadea689e21d7d5391a15aa4355374c9444914d4f3e6843f109437d79d58d86e5c1916b90734c5ebf39d711fe1ab915d16da418976b828bb6f41f81f594798e3b895c28ac96fe6f8a44579cb14684bbef1eb978518e0c237ca6dabd2f97d93f4edc2744be33ce3aa3c2f88043d5368688facc82660109ea7a938b0b29f8982c03b1559e7143517f1d5114ed1a08dca3f8dd7a985f8c405d30312de416fa136ca21a9a7a1e6f45e2412c88e831c4e586ca126bd734e2b342c413eecfb6107504df82b243db86b03154b26a9fa93a30025c3807ad1d169eafeed6a2ba3927651dbe8de72253e9e3780e650613d0bca56c2eaab8d55d45065d8d74668dca2bbff28f469dde16b46510b570a7c32b5741520c351c9a917cc1fc025e4d1b36ae1ac90823107f666f43127ee399db4740ec8b01afe58e44b965caa190bf3c2b58d17fe9fd475e8848aa51bf56200b260702e7533687c6cb43ed477d0aaf8c59014bf5df6e0378d4aa31b36343c5aa7489bce9ab8f65232893543829ec51bb9cb0978e8c91698e468e97d550ed226efb2be4c8c5512df6a380fc50b1d14c796785d03840ed887b7fb5acc6366967c3eaab901c49fb98c616a4958deb658786f43e795ed6d9e7c23879990fa51d48ef72a8f8e125f340de2e0a80155109280973a54d15abc6603515ea702dc5be5893f63b15edd644c1ac160e64954dd29ca9e8b4886b061d8a581d694cfea916298aa18e9b369fe9e0e21e1b147131368fa817bcf307d5b30c8a65ce1df244fad50fd1e07a2f7c444a2758686bba1b4743bdebe436eb4255e5089f7a832cb98a60370dd58ccf5fc4885501185c4c00a50262df5169b7d47a241fe5f6c6add3dd54ea3d5ff40c8fb060b6950c3a1a071fb8b9801aec3e8ceb23b097b6b4bda01589b7623f4095db591f374eb92a9fa7e5e0a218065aceb39ab6ab5d065460c5318c5d3529e9f9daea6a84aea8e1a3bd6468ea842383932720f5d37914d08b5e80fd366af677f8fcc69ee522f0e768f9ad0864cd3d0b8a9ec66ed66d01efdcb1d695743101e0dd3bb1e8d15e0d11353e95070b8c0d2d36a93b99fea38cedc4c65ab888435c69b8e0a74d1decc5950f2b76101a42179b6a47794fb50d1675ee6d83523be606d41a47ad37d3699198cd415de7e5e359e86ca55648a47689375b3323ec31969d57cefd7bfd06c4bd70ad6e06fbd475abd222aef32b3fbea0577ac420a9f1c2cde1c97594b4d13492e84c911d573287d1f0885accded3b4967a7b4da5b78cb75430e39cd36010d519d0ceee7923356f3edb1cd3e8d95362a110e4f2cabe2d84a0b054d8188b4d080ab718905fbc1eb4d37f19accfcf375b654ef83b2581952ec86d9df5d5eaf7dd30efb5550da32e44e981de29f83139966e91101e4c1e19210a5539491148369982c3b24b63f996a92453a978e6e3cbc22c95b80c45fb625f5dae5305cae8b93e044f74622ee72e6ce3b1358d6d135686a96b993dcd5d762ef498ce608358c0a6220d9e0b8b496fb4c87485afe6105e75b172712ec2580334083da918ddf14d4777019d6a1c73e18724f7d042c477bfec1b98e6a3631b81c98c01a39418ae7c87856dd44076acbdbe04867cd981d1ad3525bbde49e72b2e1dca07cf95f3a5979c6198e7c91040ec60e3c7db219625c1540cf112cd3976e7c332780a8d590577a92c4c33ae84e50986f50ce115e3413902499a18e41195738e1f6ebc8aa7b68a6f53dcb78e9591ffeb90b86d8e502c90b0b0a7c69a1c51b211851a4296e5e1bc0699ff452f7ed2ff5872474d4a241cc97adfcdb4810f1ff0fdd088c0484780c549a08dd131e7560dc715aaa0c8d364cab31f04bc1f0352efd9667cffe101eeabf8755e63dbe1ac240abd5bf447d96a17ecce8ed48357d3b2e69af7794fbb284061bbfc00e6037b134821b93cda26d9e53bab59375e28e3cc9058aead2b62777333a56ab2233cd6cf99da16a215dc89a446af23d128a0bc5ce07e5dd2e5c99063a40d955ff0224577e240a949ba73fcf6854e887f46a5f5ca6757ffd826c6ff3a8734551298febb0adfa7886426d1389d83b2b4bb38aba7c79382f3a6977cd2d4af3c15e8550d64723902281ee7e8bf0335c29518bffad54064330ee24b3c5f17f1b38ba95fb043254e8ece2270f5a14e6d98aa9ef428c4e153ed402b1f7e26eb70eced661f8ea406c729c41367de0c05600206c095d3f6cbfbf72aac050bf1c1a0a7de220955ceb6fa4a59c7d0f1e664a2f024625e256aa88f338905793dfef6b34bf99c1f1106d9d9b759f23c7e863b70471f64b16a963c912b83a4d759a863b2cbd3e5506ae34e82c1524508115e17bd9c25270b621912e039a2a1c6af21341f1a1372ec7495572f01b84d34147e3b809ec0e9485ed692893989461a093cf49c7876eefc10fe159fd50ae8b96a7e21d0318758a9dd2cb3ba305444cf493936747f890c5f043f502ab964788c122e61ba5a510562319177b87310513902b1ffa4b3e356a5319c5727cc02c3e610fdff5d87f35ac29ede364021a3cb0718345033dbe405e6570fcf4ac1ea785b48b8bf1eee78821c85dbd1b035a5bfef19b53dae894775e40ea6d322acb368e91be6d49a0a8362e7c766258b455872d41318ab979212d07b2a617043ebb8c27003344947e1f7c3690ee123550822e94c284941acdc87a40e0201590ad5c59f150ce7e4d346113513a882197f4ec058a07e2ac97dc47b62ed27a79bc10c5fece222efc858eedf7a6e894361fba6627fcb2273715ae4fdd260ef7e9f02f2c7402519422813ade3c764bd25a522d93df478b4037434d388c53b2228a6c5a56b4f23978f55a71b0d37b79083f9be8f6edc6a7bd0eb1400551150459c172804d03409d629dd0096e9405a4e18fc6d81d2f5249bdc55f72d51443a38872b27e0678dff67d509289b03be668e42182043435c60c4d89948e1d4006642332670192a8886b91c91913c5b5206b69169a17e8d02863c422d5cfe29a950bb424a5d2558937c0464f25735300f2482cfb776af8d9c0e3e701de5018197d2ad70a1bed091cb73bd84a543ef764b3e494f5696a84af8cd0fed7bdb16abc56f1cea401f2ab0b0f82ff82041826bc8640a19d5c13b35218227642ab02cce25bfbe40bbe8dbbfbabfa865a6f39bae7d833b5d266b27004427cdea9b59fc6c37397ee2907facf64f7d5e8cfde026b57aad66356ee2bc5003e9661258be3074ea63666515023d8916e199a3bdbb3aeeaa7d8ae021b312e31f20a6bf133105eb30f98e265e16519482bbea319c8adfb1a41b795914d83c007b0c252a9d7a6e0c67d8bf128042568f46def4a199e58077f0462af84f0b462882f71308695e24476681dca1428e46e5e44faea6c3b03ea0838aae3c39116d16374c8a297649f2ac8b75647f626cae2bafac8be2730c3135cc795ec72a500c14864fd999b4a6ff08e537be2ac8c6250df8a69c7f1713e1bd45967abe42147299ca514540bd3b13561df26cac3f8e0bf7ed07f1b29d23f2ea500d3aafbfe6996c08bec7d6a1353497be43e13358a35d780cb0bd10a5eca74dfe29627ea0fa8213626f217e102a451df549225910c0d463967beef760e1b9d657cddf02c265774fd7441913c616bb84a7ddb751f9daa2ec531581c453e4160cd626de136de02f6e9792ec950863ef2ebf75c7c5189738f870490b16c672251c819edcbaf3811709878d4029182d30ad94ef7ffae71058332689b9d2435805ecc532d60860fb4f45b5b301f784d9f8f761bd644b2e7b289cd37753828cf200cb19d6a02e867fd4871f3bd2501d3fa62730b190cf66a9fc9fc8c84d847f64b135827ee02d989b0ab301f058d954b65717fb44ba337b43f8e6c827a2b74667bc54e739f5dc40bb90d26e195591aaee6456d9c523357dc0224f4752e9f021f5084602a00e1a4dc56f5b6d07a3e46cab6fd213954100b4371cf019df217fc8c2ff3858f5b646e0271a8c3bfcc7174b97dd346b63e47ec276487f4ceaad1e0e22581643da00ca3f45842d1f773b35ccce2a8bb8fe337a7710685cb2e87dac9302f2e20d3f6b3e8c0104bfc65f12595940a88c0e9c34a8d93361bc39edb91ee62da1b8dd5a32cc32245c847a8d19681422a0cae410d6f9d930f6c77a54cc1265763a6752f90b2fea7964eea19ed557f3ac25c83bcea5c8582abe05236fa0dd1fc359f976937b2b8970eaab08236282d30414508d0fad6580a0e45806a0a259403bb6afc1206dc4b04d40a2d86d24a54f17a179924c9fd03d3bf3ca09946b79d3b8a0193748d26b97454204aae9b556f104052600259b21a8141b08dc76748be1e695b1d99f4be90298fb39aa934f033c2d894dcf2f05f095fa84c7c58f6cfd135accc172ea010a7923dc4502cadf65f019ed39e03970c957e50b2e6efb627bbb5724ef86321eb25d0d6de7dc138c94882e3fe3bc060b5fde221532c6cf4679ce55c9ad7085785efb17e07c7b30a07caccacddae073755d0255361da690196753e13bc4f5bf4a11a2446c85405b75380d350ec66ad1893d4fcf001e57571da8594d66e6f5b2e0896d397f9e44c2a81442cd7a5b0be046801cdb3c13517441a267f86cde7a2387fae6ade2303c47fc9f810df23c5481975ab267ac3d1eb42e1eb762962d51bc28d424c8a691319c7862874d19ef0fd0b8fe8775e160cf80ef23015bece2503431a10dad9df89897facef26b29821f55170bd6feb804e08fe56130f83852371d2e89cfb69e8e6365ddf42ce9d69ea15aa06124e0d0f039b714226b1d526df07aa8826fdf9b3dd24a5462094c092c81e4fe0a501afae3061c8b24421dda61d03f386851cc6f068d6fecc3e513d8161f7d3407cf3510071f4b6f2f91ab733616ecc1b52b8c8558e2ceeb1109f47a3c7a2579b34533b8f9f957aaf472b69eee61bc7c45f983b66102311b6c0b5fa537264648b10ae43f57ea5d37d823dbe246dc5361efcaa7666a51aed1f159c918c44704ba29e7a659200eb39a0e9503864496ce0781081397a373e5081f626bb48b486970138a5cb96c1f63b32061d04ae55502e38aaf28700e3ea2bb58f8bf5e46225c181c73a808d409b8d5ccce8ebd74442385a7cc32db765b937b4b29934c01a10a0c0a280a14648fd209b2d88a24129226cd8a24994eb5f26e56aba094af564be84f7087f7ad7c6e63befda70d95fdc9f2853e4fa100a278a7b37029a42286e880343313a479234d3d4d2d25234d2da51afb1355c12b3a4f3f26214a424b32e4fa89fab979f97669f92cdf437440d817986d893bfa3cf93c81f1816a1737bb9f2c3e3eceadf2f179fa49d2496a25f1ce17306154610729aa9c4e5290229b5510e72a0a4a70ca9de0cc87a0cb365b5d6b625a2272597115b98c5c472a2c903c1bd5539b2ddb6cf53cf580e909e3f3949412737315b98c7a6a638709c729f41335f6d47ec9c7501d6f8fafc72ae8f31e419ee37c1688033597c4847d986d9502af0c0100ed068019cc067b8262413d7df9b1fd64f951fad1f2b3d462ff84f931e387a95bed62a6cf1312da10d0076afa3ccd964f94cfcd670c122155f893d4d99a45cd537da07ba0abc9b5e4d2e252720db9a6b8885c565a045d4edfae2d2e23571617524fadc7d6f3a5078cab480051dc4183a09da017068aa9c9a9e605cc17a25b9219adc54e02658a2afca52457dc91b724cdb6240911b20a52f90ae454cae8b2034d063bcacfb993dbf543f0a1a266eb8977dae6a57684949445a9c52020cd653fbe17835fcb8ae927ea47cc981f3db59e5a8f971edb2f7d14c7acb73f53bf5c463db5fe02064c989e5a4fcd0b5d45aadbcc0df60566e37a535f06aba0ea6210d7c1877a30ffa4a485a9a9c577fa32e4c526190a7f6e49662d761221285ff84b5780d428950f987e0169177733c19902af841e04fc899aad0e7fc624110af236fcd1e18eb5168240403b5b504f35fe09363741c9cda0fc945cc97f0bca9f01e55f50fe7cca3f412e0794bc2383f8b3666b82dce3a55bdd539382abdece37419cf78c7c13f4f115bc127a2a0c7f8077b62688c157f00a10a7a19dad09f6782f4810204080ac827a883071b9ea3db616bba716e4f3c43bed44be5c1797e2b894d7f34db0e6bd23df04797c0abc32c48508f10a5e11d2f10cfc074873d910129c609104a5f679eaa991b0345bbdf4b67f1a02fec0c1adf620e04f1ddc6ad7c192420a4d6e13d64f7f41946149a1c97d1c6f623fc749589a2f126addaa83633ebcfd870e8eb15ec0310c38d6c3db7fe6e0d88db7ffc8c1311edefe1307c76a3866e3ed3f70706c87b7ffbcc1311c373896238663f4870d8ed5584a0680e642a18f495ef8f624b3fe09bdddcb00ac1624fa0f466692f2ed243059f976a6222dbc6400529b27df5e13c3b7f390e1db6fa07c7b8f28df8ec10cdfcea2e1db7d0c65004e252ef04e7b0ea8e405de69c70195c0c044cb408913de694f814a580095b4a04488773200a5cc07c8bc83017885c623039d055ea1c164a0f7b8096dc00ed8dfe1356007eeef701e600758bf23bc02864a6842cc740333e5f0ed4aa4f007cdc9d0780620cb3cf90d0195e8802ccb00ec644168422533986dbe1eea8928dbd7bee9999ee8ad7cd11b75e01f91e0043fe18504a5763d163e4ff3f5484f68f179e227b2f83cfd44f5d45644dc91998e848055a93f900e04649a55f08a8e539d9084da6c7d19b7a17e84dd848030a8d96a0702c2a266ab3d047f40f0076ab660361bd48f833f35f0c709fc69fa296ab17d06fc31fa39fa419a837fbe7c7bff70c162cfd74f961fa6ef1f2d473e4f41409f271fa8a5f9f209d3adf611f3ed372b1058df853f29209e24899e9aea67cc4f1b1c83e2d7601f12967ea248f0d22f20de4e42ad5dd6078355900d7d9e5a6cf725eef8e3f3f4ed3f51edb2defe23e6e7d6ae94b7fb1cf9d838b6f2761fa47e3dd34f14ccf61305b3fd44c16c3f5130db4f14ccf61365fb769b1508363f6d683e68ac5508fdd5395045390a5e1bda11d1a7b2b2a44395259b5590759bd512faa92536ef85bf74c51def4fd4b7ab409b59cd2a84fe94736097aa6e67b31f6b4feddb53602522a156a5bc74a9456d8229d83741eb01e09b60f71cf8f9784574848460a53d6e7c66cb199b1a1e3b6864727ea8b478c2e909c0c0f365bb5085ce55e1e55c15dad99a1a1471a55757819ca7c0ce2938aa964a4879374b5039375d05d6003451502d51ef40967195d9d9ba5236f30e8fd205ec7ca5aedce0697704e8a909ed62a716ac3c759e35a40d9eae969c5ad8f223333da59752ea5c97d32e4adde919d6ca6fd072deeddd2aa8475801056b69980329470bfcb47969431a16650875dc3162e0678729d80ef45bf7b8e77c1b5ede913e6660a4611e35f073862c29c350f5f1d6280e8dd910abbdc5f9bab3d5c4a45f3b861cd004ea26290c08494b29a5ad1cc29d7df551e7439cea36b4ddec39ea6fc6bdb3e7a7b36a0357bb6a6bed38db2db99043f22ecb6d4f6f97ccfc35c82d82cc3e7d82dd056d174bda5d1a86e05f736f0f4c2b651e9a84e7c9cbc3239da77926cfeb088f163a5f4891a5f369789c07c64d3c3c1ce31a55a2411669060db05c85c04f6fce59dd73c9852fc40790fb66b5926ed18388940b6b518e28e83c92233c9cc0ab62891f794965c4e59e9d5840e2c74bad122623743f3c9301048d898da7a727bec81ed006185c6c4e78314518320afc00450b1f6420a3045ff4a492f410d022c61a4a4059411b2ff067271686be7b7662e1c98fe20cdde1153d13abdc0e4739e6ad3bb9cce1061ede6ab10438ab9e773cfb78762163aeb422776089c57583f8294df0b3a68a2b9d73e9234b127936988ccf954fa9346d187a1e2861f335a594cd5a8b537a11f4b09942d7de84f552069456baa50373508577a653a973c3fd42155632a19d53be0a949e02e5cb70b7cc9519995012cd965c9a26ccc9ea887898718958dc9a6eedbd4dd64843e8d9ccc94b8b4369786b511eeb80b42bdde39d1c1edc835d36265f5e0f9befd6a4bc67a7fdf3f90b56e621a1b1649e8bd22715c224075d43259283f69828f340968935d2e7c88dca84235ec864a66a98e3c5952f884334615680e50b6b4ca625dc0a04349ec6927dee8126cc1613d9923e27480ec698e241e01679b5c4b6a4a9548a53a9a992a1de01fb743a0abce3c5956398f00b20c0ae06b06b8628d4114401f6e8156fc69a2f2fd4408b4d81d9e2396748c55ce929323cd6e9d909b325bd136652ce66b33eeaa429ad04a58f5a4b85323485e8861ca6bc74066aafd3da13a4b35872850029b3ee715fc5a91d27a594525629a33a8e69ed925366527a9d93d10fae0f5eb64ba66e0dc13ef209e640baf42e8263320ade91e1f81d7528977847bace0d37e59e972517663399aaa12cb10c4b75e93ab20868ca9ec9312dde5a42cdd6944db3257d494a6f6943b954335bfa25991e3c3129f9fc63e60f97b6d82dfb6f34ea8e130a8abb4dcfd249935ba60b5520814cde66cb8c5b38ca97344c4d16ae71050d74fe5e5cee398bf96ad9ddc2c2820b16e339801af989ef313700d29b99b7f8b6d1e25697365fae742efc40130d00756ee7eba1381043e4a20b61b016bc20c4c212627630da420a07a0c0ca0db53a8ec6c4c0a3163a5469c289349a6048c24b5c9801650e315ce8a2040c08e30b972da20883082004f004d2196e08c1041c5bc8180c2a9ce078411d373c49b1c21139ac11154417562441e1288028820d26a12f9ab4ec50c336431162aee0c18827187218fa820a1a35c470f905eb22071f7c6062a826a403061b54dce0a40553f420632627d0486ac110548870412ec941460f4852d6a821c98d052968019824232f6250410d31e270828b172f7eb8ec47547901c905221664982531724245e54c32f18d0517be9dfa0bdf0e0483130bb49f3991672727603ccdb393932edf84020232a1cdb25af2853309f4de4b7d1ea87a5e7193b50df5d677c07047d85b13768029c8b76e82756bbfd0ba97e29fb7f992dcaad55a3a649578c77245ddb26e6d5bdb379a903b0ab9a216ada7a81b47ed4ab9f5281573ad12b7ac5b96f599242e57649d33e290a44fbb457aeb32be0239a316adcf80f6572157c43be013775ab74af60b473b5e2506f3aa2055e82248f93d8084f1587e98dba4be05b8d1ffb2152918f3d439a74e9de3b82dce1f475c0982f4ce53a00553ee71e028bbe7952d807c1b3ed41d61855705d9b13a0752bfe2b5151f2e3b7d76dae18ae77914d88187236a3bbc9094b4830c518eb85911930312332b5050926a569ea0b03cbe99d4fcbe5bc3e2811d69918b185862904644592bc408e2c7efab4981a24ecdfdbeaf26070a9da55a4532b3770e926df058879360da55571ce5264b35be7d5cc934641a73ce5a7f31a3985b8e215f57b6da79877cf5ccc53e824c306026981fdb49cf7468d95c447a2d1349744054a9e327143fdd3b4115db4f223f5db50a9adfa7c1176e28d581858d2cc0908d37044384b102441114242064edeaee6e2dbe8d18e37bc777777777ce4f9d4bc5879fce8afa5925053f9da938113fea8da81ea86041152a327085051eacb05121860e5d1048889914f6e32787d561cbb773af263ec890d4850f34081149244c3065453f6a3fd9b282080ba216be4f8b14493f6a3f1705924319dfdedf37e714e304d19c73d2f9394c11844a9917fc90e3d2679ba32b5b00527145c2406b286ec3823b368d48d3ba879e7eec1826d84f7887c6e38d3b761dce744aa4dd7eb338c499449a9cda4bdbf8d52c1a8cf64f336a467547d1a1ea8565117ed9b0a774177ad0463f7d8cfdfc21c7ed1d59b414dee92063d8ef871cb73ed33c95df5e5a9c9663f425269975b697a62d2549fbd03a4c30eeb4f65e7ea2468063d8c230a3c5761f735cefa9e5530c4fdd75e785a7f9fac6ef8d3bb27870aaf36f5043b13dc8326b97b328260b823f5aa6d6b27274eca555f7985f945bd5adbdf4eb933bea7c757183c93f9d4a59571c8d0262b0f9f850d72d077e191c37c20a3ccd1cc771b23d204925ed7898f5ad2e0b36348a2b6b79e41a3f9de5d06c49a516672893b2c8a4db62538b1d0fcfd090fd7924c303b45333b5387de469a5a3d99aee8198db473f7d839b5c835b330e25274bfda4e96714eff0fc9176cdba0ae228c77194b955d09139eef8edc8709dd6666bba8caad566cba89651dd93731982485c6eb1bae4e418a19ffedce5094bce233626271e4d8020a73399d361d3e91465d73337172e7bf7ec84831c9f110e6ad8271ccaf094b8ab67271c6c38d47040b2b6c3018827146989c381488659c451b53c36b4008aa8511c64a0491e18cf1118d26db6d8e230439acd165b24d515b686cb3d7379d29252c19531a223e38908389e7ef872eb3397a71d903c6ef728576b77734d290d2b9dd6721ccf2ac49c7336cbc54adde7a44e27dbebeeee4178b236538e0eafbbbb69f79cdc4e99b982312b576dc7a52c08d652163bda558ed53f2b3787a8568a4799d6ae4c7987e3ac4bced659b969b9d592fee93a39cf75a65c322b15eaf40fe6a0c79cef20394757b2ad5c5ddaa2c1ecb27bcf4e37e8308230430bca5f22feec9c1bed779cb596c50a298b5043ca230ae257e1cdaef96a3d79caccb086637c6fea861670e0a02c8f3da3f2c0788ec084a23acba305b5d77e97e3e1b14e304c010c71545310a5a3681250e415a32b48f6da94d3942319581c8e164cb758004761bac53fda3bc294227b59a32fadb8505dc1250c2394b839cf5cc2a8c10530ea18c186288eb3cd6c6f9eb99481c3d367271b86f891b544a97896bbbbbbbb656e4080f8ee3680fc906249e160011c2c984f4c3d2069ede9a38c2bdde352c0632db5541eb10b2f9db64be665d3ca0a211b6e640b4fa3942e59cefec79a1f73bed3f96e83efb6f8ce69e7b6f30bc47752d4f8ae0626beb3e1858ee748c6d93a6403ed41431a4f6bf6227df7f5d8f2d5e46061c3467e642719de80e2f4d32f0b0a0b7eba4759e2a78b33c4f0d379dac54e3378f1d36145fc74a0e944c3d2d79698e393b3d96c469332dac10abe6590228d3b93b87455a78a6fa713cb0baa9fce6404fe6abe5b13931cc450e5dbbb004870b1861b5d68d9a10b91981d84a26c41e3020936d272b4d239426b1774f0dd2570810a1b5892a0e2c60a3e9ce1060d528af062892519ab03a209a7fad57cb766a68927537c3b6d97cef757b342f2a40ff72bc8ac1621b58b296a0906da8fd5680906a31f6bd20bb71fab969f75a9368dd5a9d67e7a95b222c162a5b2122102d2d96c55af44b5878f2c236905e469c2ca125ad4a2d10cd31d2b51256ad7386fb568be8a78fd562991887bbe0128ce16750d6ea18cf37c892191301c2dad4e89e68b978a263de2d8ad5decf4c293af3ec7b48bc757a7b3f95a415665acf00329b73a5057a26e55af4dfbead5a85d335eab8b33549f017b1271d9447229031f65d3286ba3b4d5f8289f78ec70f970d9a3b4228d747c9448afbe319830427c6431a18f3c06f4510abd741f2594191fb9a902f111f653babc2f6de27c892d97f3368784a3cd201c6f0fbf0947964d387a4d388ae1c8b3231c61932af56dbe2ad16c4ddfe134e16869aeeb84369970b45d385e2e1c5942c2d1c37014c170e4f170844d6a2be2d3e96dbe8a8441c29175812815f11fe1c8d2c073c2f13a4e386d1b5cab01eb160979b66e3fdd479649a963104e5a7bea3dc29145fd261c9dba4d388ad46bc291873a8f7084fd386f3bc2d1d2844a371c59d35ba008ce78b14d284f5d261c614223cf18518c8779eaaa4a94aa443624eac29105a3b69f4e0494b7d9a2b3a9d3352e704d450961d878879f5d2584e185457ee71ac7c1c40e462821c30d60bcc826bf604d3516e74aae56b4c5d45d2de1c17d42a6f3789ea4d96c5684ac84304ec02dfec9c43fbd57d3ab061abca41cbd4053dcf17e09612cc122fb94108613cc01ffa521f05227bd84b1c49219eaf6298784d7eecd20c09b097aa8eb09b01304ca94524a5b5a99e9ec314f2ee4e9d1fb5ecd7003a6830d29a1b448e4873b72348e46c7b773503828ddeaa1a7c1684a9fce983bc290b48b9d5c80fa76a0179210a6c31d391a8d630c73a4bb5ae23df34e055334dc9273a664a8cf4f328701cda79ea5b473051271ac470efc944f6b58674336cf9339ab208fada5b4560ea49ded3e09526b6d4a824841aa2395e77ddf8a995982942733b304d967d867d8bd994f829786e7e4e979343bc41a25769b55d0cd8dbce15153632399886a8d31bc196f86ca9a642d9e70b8d23d29657fedcd49295d055df76aadaba019f738ae53492965ea4b7deea93e99b30af27815b472ef0b026fb82b1999947b333333771574efcc95595d1a9a7befbdf2bd1428dfeb40f99e07caf72c28dfbba07c4fe69b596dc04bc1531ddf845dbecc4a04d673d3b3cfa3f96a28a5f912e2ec937d12cda2f9029d7b8e29cf3ea5b0ab6f43b30ed3af399bb3293469f3e59eb3b7adfb4bbb661a4c434d225ba9f3062f8425e52fb8ec47cd710ee27ddd3703cab7df0cc81c97e2b7b9093a52a5f240b62647c3ada9a2ded404452ce601086280015b1a5e15087e717d74406977a7522e59902f9611989ef2ea408a5854660c3f398e9fbcc64f36e353e8388eabd2bb18c3051501f22be726f4ab278a85a6f3e43193a3d3bfe9f748cb4f4ba9b5264c13240064c723e52aa803de9975f4e0a77354fc984a8527d8ac71533eb2eda74400eff0f0cec7ee9ae18e6c9b73958094ab5c054a2a9eae20f378250268011ae284385d58423b145794bec97cea1d904d5f41d9f4d4674bba5c69f1dae9e3b0704739a58adbb927bf1a35eec87a8e712e9de5120810168b4e6f9a02174a33bc93a3dcf2d2050f32142f9d6513ef7c71473945690a0e3bff24b252245d5298992b90b22cd6bd4ab3d54a526e792999146935f84ae38eb0974ef305fb40312ac031e935582af787970dc4052fa5523fe11d295df2fc240b068d72ba12e68d3c3b5d41819319a7aa902f29e980628d71bba04350225a348f381607738c3aeb89cb44188ef326c3d4bb66a04038d550a9dc078eb1209dfc205f9308aac3d720690c1e78108bcde66d36cedb10bcd3de93b6c65585923667f247861a19aa729d074452078e51a7c06c4924a424423e31995466b85b3826fba88fe6d11117a655708c7a3b758174cd30c6b85e502de6bccdd9c48277daa78f73d63e87f8764642124d209d7caa61d3e64b9cad76d6e570bc14e018131c6b4276d1c51863b8c0053f3214474581f3c63b7dc43bed524ade60a95b32731f716c7efb16de69f7e6adce1b53226a34671469dee810b5428be811ad724e5abbe614ea39e513c7e606641d4e57a67c1701c3b8d27b65c20e0e977d07872b7d9500955b9f340bdb051de39b4251225a345bb7f9a243f08c835bed4fb09840e2dbc378f2edc4824089666b8b4dd465a751377380d2f28ef520c62dafb7704c8aa258857cd9d97281744936a4d8ce1a238bef37def851deb0c022eaba66b852cee4ec1663d6963b02e0e5ad83d88dcefbe215cc664d3443eaa72937272188f8b187da9ba5931050bee56c7291673b5bbcc31e95eac2b957c205ea2baa2b296f96e0a5dcf3156474e52a57f90a32ea6c53a0c8aa3215c879389af0b7650dca7721ae8fb0ba346bd37ca99cf35aab5e6cf385453ae7558963501cbb796e62b132ceab05dcd92a61e532a10a2a5f852578fe852aa46c8b1cd06c715c7fdc079cf3ea009c33e75e0a14c262bba65bdcad1c9690d522c7d525dee160cfd52518927659e71c4849579738c6cf711c076b91f334eead4f5c944acaed9e9d82d8d225881f2c0cb73e3b05c1a4061514bc605b020625420c1561b4f4c3d151105468576c414489124452104ea05c19329ae305a3a81e6ab400cc161d1177c7331730513cc1054c12528031ea7cb8ab672e6072e8a146f7ec70054e760f2ce5e4f913876e907a5c738a06033955950c97ecb5a6267310034d0e6c529f2795744a2a29d7ab12c3a3935a2b25183e50ca75573baea31da574d290da5a194cd341e7ec593fd936dc6aafdea1cee4a96f44352164d9b8e317ebe18618ac68c1ad4989159c10e2045778684389282e5ca1a2ca9d615a108061228b142ea670c30cd9c8d3b2ad81444443125120c9a833992f6aa75321a6902215c494499f462bf86aa24ce0313347c32d91b572dc9492f3c9f1ea00aa1e2e916c59259a2f1e6dfe3d0ec6d44bd0e6fb4e2c15b416a4b21a5ade9134b4aa90676bcab8e6bcd464713fd040136ce14ad92465825809c7fae79d0e34ed741b2dae75e9365fae741da22b67eaf8f9ec40ed294b737b82fc629fac4b33bdb854f69c32573cefd5025ae6b90a94b314a8c20756200542e8c1c5049a338e87540a2ecdb39391146b839391134e0988353a2c8060c1b5f6bb355c445951399aa982864f872499256cf074b8ad801862065595a38f071952569e3c9aed61a60202075ab743510a88166ce07838b269dc5095ba1fbed440955819e2782c172544514d485152c556040d4a76b0fd50654809955a0f5b0f13743c2dd7dd759dc70417328cceb8f7990b195356400614a5316e3d6cde9c73cec9712e274542e98485f408a5b48636d353a84ac3492533f39c734e50ceb9011a27d99356aeb32995b72395b2dec71213c601fc922c2f486a729674300045168b2539c62c16cb729572b5e32aed581468e9e3f1baac940428d0aeceef91d7c161f55c0d847c5b2725fc92338eabd4b94abb4a674b4b4b2b1e32337276c26cb5d3b05cce582c564f168b3579b058ac1b38bc310523cfa16ca794d2eef64e811bf2324c01a79bd4a73d52f7ba1b47fa946008d2670ef72934e9f9a540bf497dfb1e9dc3fb5acc69c7f1163548a31dfcd6cb265574aed3ebc6595741ed0c8eb4bb676a154453df9c93520a9b1ffdba950d9adee0d85f8363fd76ef7e0d8e9caa9b2a494929a5947295e3bc725cc7d916f9c894e29c737a900fb4b8d23b184fc74472c09f1023725a64ff408bdb1bb871a5f39c94ce1f57f8192269917d25474faeb32aef93a157a552a928e77529c9a24da5c2f1039d55a9c0a4869fcecadd93a1f12c47ebb74a7568a83c4ee5ad40f95dea6dcaf3bed5a74ae5b058f672dc0e8ed62ea5f2d0b8415ce8438c2bdd5a9756655daa38975c2a64ab84abe180ce11fc0c61fe746fc78e5510c763d20904c4834e24ba674faeb32aafce6cf0f5f38ed4a7a98e997e9ececa5de52213e9c33c4760488094f088e25c75ea047f95ca4bd91cbb62b19a04dd2a840669973a92f99ac9d940fa4ce7efac4a9c016540200dcef05495c554ca69751653de7929a7e12882b72c5b4615a642262d56da4df3d57fca83c72a88035a52d262af94b848dac575920c92a5accb738bac786af820851a3cb3d9ac851fa2c8481fb2781f06f33fd67cdde2eb1de354748545c4a5e8824b3abfc1153ffdf210f5b387a49f3d8cd9218e96dd2a68bb9cbd82dcc1947b9f9d76607ae9d2c5cbf2c1ab4e57225880ebf8d0c726f5bb5db6a4e2b1ced823eaada72aafe812feeb2b8f3d1fbd53d639b672f60dfac57146ddb240f08ee57c600eac733bfb136f7d6cd9a6b74c70cb7eee813466cab20234642a9060660575cc0b66deba7d01f30d1c85532e7cc1760b961b178d323ecfce96f5e96fc5bc0599ee68dfbe75eb06fbf05b0b470d6b759d246e17725ec314f102d1ccba7449c4a5682663590bb38e3a64a98fc3bc751b10a704ee832ab21bb7be82ac03714ab071ceb950059c1a767e13569b500576c30b3a8e642a90404608d51649322c2a5f41e6cd8fe7ab55f0d9c26549a602093e346458689c041d1541c8b0a83ea8221b5942c6dcb79e2a4a19b54bc6b9653d055fb6786bbbbc4dcd88e6b54f162aeaed0d081eb922462e09ce89b7be43cb1dadd2a5b3ab55520501b56b8dcbc3c709456fc832b9ba82b3c339b72e64ccd562c5b44bc6addb28abd4aef0addb13f08b2b9a2deb52fc114eb51491bdcd54d2dbd451d1dbd4d07b36357b6b9fde32d45b1aef58e78ae68b7bc22deb473c27e5adef0811d0322bf355cb1d2bd45b9b83cd816392885fb0a27bc41496f4d6ef54f2289440daea4737a173bbb45a00e75669b6ecbc793f8eb8fdab760fa4ddd12a19c0f30d50dfe1293fd2a2f50637b0849fa745eb21782118c5954ec3a8b73ea1783ecfeefdcc550278b65a7395801c9f0f75a973394b68ed2a2bd75aebcc9101ea2b39615d22ce24495e409c7352c02f2061cf8f1ea8b188524e0f54912b7e15a402d362f74085350ca1716ce6db716a75be3c9b746bbda1587c755ae318f5ea3a5fed7c01f1ea77be82fcf0ea3e84e61579b3cff1f1fb21b4f9fa111619422b3284c2888a28cd9711a87b7f3cbd48247d7f4246a03cc9ab03d070c79ba69b9a8f1bc76e3278f2ed19ac96fcf81afe788fd6ae9d6ff7a0f42bc7bdd9951ce75509e20ef7365f39fec4d79c1cffe12cf403f472c08f365b35f46e2dfad072475518d5d3dffc294cbbfc4b8b62da5543b7b578c40db224aaa0bee78b93b17fec017d40df33c71d69929e7867da7020f8495470fd29039a37a65bc18f548c21be897828f792e73f9c2fb20fa756ab0f362ee73edcb8952669cca541a2396ad7749aa490a349928902f06e4bee8d772aaf44a51ea86eb55f1f6c5ccf7db87155292b65eac6a95bed375e6ebe7838b5f0a609881036145605a78311f9708f769432c1a007aac8d2574469ae96885f1dc7a6f4e571c2343d0e98a51a10a776e5725efd87371716db6d9ce72bc775d3d462a5fe03cc096b5a6c362ecb7db8714723505f71bcfec871db0d979ba61ed3c70cefadb101c7f95d9882fcd1bb598a23716eb39c2938b5b0068e35cd178c882689c8d01d87c088be1d66054634644abb461452b42150860c8ddead5697fcf26eeccdc12faf28c6407dd3e4b90dead6ee63dad53d81f1a85a8b62bcb4d86196f0571f4128a0956f50c84790f6059c6213e23c8144e00c1cf26e351c6b8edd7c6d7ab71a9c1acb47ef46abdf38adfe23c76d55882a8b89db03474c8b5dab210ba756dd71c071be8f300589410fd48823e6db3befd6ae1a2fb676d1b88b87b7e33cb5ab265cc20fa65d50eda2f1769c30ede2111a0913d5ae1d210e4d6804ca4854e38869d7c5a9e18cf191e7c325fc2b1f738472683950bec33bf41de2ac42236272a67c8f98b007ca5385630f948e27b98c0f36eee86e5c7ad3d4adf6be69a249ba695aad085044c90854bbc6224adf6e04ea8d44d16431025544895be47aa086d0e20871bef4cb6db3d53860bedd471a5786a337e5a7fb50e3d670f46843681c0379a7076a723d50df411fdfa3179fbd0339ae378a1e084b24131902911f4088100942848848c44551048988a1105127031c5114451f434451148910c969c96c40d6f13821ecbc7d198e2378ea46a08c44b58babfd58e346c4340742b6d1081418ef667b2366b0cb8b838d8461973786c5761a7a372877bc69aa7d7b919b26907744f87ed40115a65f3add8289f9f611564134599cdcd1278d12013430e24effe1409cfae8dd727c86238fdbe6b4d526db1f2dfd00e7abd6aced29071447e8c0030300b97147ef46fbe94062eee8365bbba83fada47fb0128103716a38359c5abb3819baa8019e18ca6d0d8015019ac8ff885a9c374d2d22f1eca404f5f56992da35de34d1247d3b8d52bbc68fe89594bebfce089aa49b261c9b37145b4dc7a9cd970e8e4d0649007e9200bd6ce6d2084049d00e1078d42c6112c20044188100363705e8c15a61f0e5fc00c223e3bcd3ce79378ef36efd0a4011137d46616cdfde6d09313f7e454749dfce55db97767558be554b0a88426b578d3203dfb89200aaa7ae8028b3283350fc0a8ef2a908aaa7449e168556c15aa340793ae78c0225ca942843d2a308458145f9682934f3665876d0a495ebb06049a554df4a4666862fcd8e8f87d8b36bbee661f3754d8d8d8db4e1513363736373737353c363c7cd0dcdbdb9b999b99159dddcdc7c461e7b17274753ba3819fa274f4f6ccf5d9e34757982825f3d77798244019a7dbc48f7a95de3b5c2f7cb45ea17379445a95f4274888b6fbf4b4cfd12929925218753bb6aad766ded62a72496befd82b17cadb4cba85da34ae916b5ababf2edf7a85d1dd2b7f37ccd4041cd40cd40d131aa94ea139512c7d8bbe7237e944f78a77a07a6206bd2cfda69124ca5a4aaa994987a5429a99c545e5830f472f58175484ab1c1362e7f77e158937cb431a7cf94acb476d10a65a85dde142bb588d6292dca0a4555692d8c40b3ab491b00810708f2a98f95f64a1cb379e92a1ac77815c2fcf103444fe98c77643855432ae8e2444c172750cf5d9cd89edd3e777162c573cf5d9c34bd2480c7591f6d50eab4eb3aa9722e65bf7e798aaa4e2d7a21c7d4620a6c5f4156fd4e6f2f617a872a60a1ae42fb2ce15361fa175ebe1c5ae99686e3f71fb7d42227c71dc55fe29c278fc831ee39aece9ee35c268edb50965b9a2dce71700e303d012c9b21cfd6f42b33e46ab3c5491ff99c7ce6668b736bb3b6dafa95539f6a987ae3848ca87150661219b6e7388ee3869e73dbd5cb39d7e5f0d479582a42c73967cdd7e4a22c1c4c38a958548aa7dc534aa9c400cb98cb85725de71cd7759c4f9f1ebf3af7b0ebbace612eaa244b9972550a044885380890322f88c3e15c3e174aa32c3c3017797cb664eb2629114f5880e2ad03b5edb8ae03a773e088c2a350f35586ec4115301753291a094aab454de658226e109ae2a7bbae60b9e08e36057a53c41d91bc64630b14272fb9fc0c470a64873b5a7be70b0bbb6c9ab4b40e716c4a25aa4a554bbb3c975e97daf5b9f4ca54cb6065b9d54a5729ec8344722045edf2d2eb162fbd7ef132fc326e1d4240955134a46cd933a83219010918d8171e01f04e3b058ae1727cbb1d68b258e057d9799213038593189c6dd511b145194534c051c5172a3c4852fae189c8a90d27b87041860b1b329853f4c0c4048682a1b4b4832915452d8b3a90905c08c34516968b2ebe33e804c38a55470493e08517b72b6a6624e144c654544b32b261718615d9c8f36d83125e48b1e1080d29b215a84ca1a38d3027b0994007d9c8a3a4c921a4a112d83004d214a41e9c9090be8b583eb2eeb313d2ec53cf4e45c0f14f5fd3c7d3698b9f5c627f2e59f14e4797dc25f327c771cc71542533fde39d06f2e4f6eb34922b31fbecbd89437af9b1868e6fa75d04102c21225420dadb3bcc88469edc1106932f1f432648a0e8db67444c01fb6a5c58c0c38f5c234269e427869222cf06a2943a60d043dd9adab1fe6bcfa15b5d86287c3f9d82f4756c0d9eaca9e12693529ff6520a345bde45ba9c4de94664b8302623f4ab88db7e8408d41de52c47882571a8d3a7614d7bb07400e926480792ce04470496523aa5945740bc233d0842c7ca1de56ce90026003151a3aa916ae266674e3ce9288878f8582cd6111ecf6159254c4660b9489f9d8600038833664c501162020b1e7208e1054d5db4b810454d8c265a40a3a0d55033a33ad611181229bff86a72e6b3d3105d7c583c7ea69edc52451674522a93fa254651228e92609264d3104d3fea48dbcb465935f081cb426ad25e8d386a881fa5d0d147492b2b34b1a5f6c20f6fe8c055c4811486972e63c6b041d60c70c20b31509c1145136264cd2f58811b4f5cd0a20e3590640d85d251bb52b20bf91a806c75acbe5adc6a19f3000431c0e0479954c310425126d524c79c74eef99049a91038c62c9578a7bb6e4e0bcc1087f35982f4e91d903f65d3cf1d605cbef325bd5d0c4fda38c6b4d5d79ef2ee880be9a2e82cd69115c38c708d9792c62e59d24e982f29876c4997cd824d85360c895ed2c1592080775c33dc13641c1cab2e9b8a74495a4fa7520ec9420c61d7f0f20466676797b4763515294a97341c9b0709e5280ede911df05cd999e171a52fa8d44d9476412c151509008020006314000030100c8784428148241c8d6479f80114800a779a4e7e609d0ac42488511432c61843083000180020002040333302008b518c381fb575648b30703b5ee744a59546db74d9d1680bf733f87f1e3601945cdb4176ee8fc3c1864378a8caab7e439351cd30e563e42dc5d7bc721c30c2e5d886c7497f712f49edd65e27620c715b961c7804cc881349cabc486d51bdaf249f885d7455360a1a15d24fc665a2a620cae0dcbad7ff05b14376fc9305e27f8c20536ab8f5f0763351106c2bef84155da180b3275d54432032003c97dc740085d3442b41d4fa79d7741c8ac104efca4f3f75288badf45322d3c37166d91e0eb0ed6099814a2d7f29b181ce2f0deb1e28bf3fb4fbfcbea345497ef50cc43ab4629ec02d9a1034100f8d23258b796aef7b8db7c49364ef9d23385ae0eaeaabb8330da8650ecb996038b202c2b09c80dfa2f4daecb695bc483b1f9a7e70bb00ffe87ea278cbf085ce807fca3bfbe28d9eeda4ba126029a659d9a83a4a10a6ca33062e2ea31132cf1f420659498932488a3ae3c7680ce447139240437fa8b0952025524b8df2a28565eb3d2fd6a79d4c9fe0d06211796624b54b3e27eb67cb1acee8d247ed87404919d320a9b37666aa0d5411a7d52c408e6a1721314a1d5b7e9c7c120356f48c2dd307547f13b80c65c73428eaa1b2fbc36d70707c803fe4c1471bd9bcd6d46327b314bc34d5012c7af524d755789b82c40ebe2c43cf65a699a82be7491b3284bb190e30205dd890b39631aa5c64245648fea1070d753de74d3e6c60c072b71e813259dff4676b05a50db9d510bffccead67337ffa3f973994edcf03497af8f10fa36147441c313f2fdd9dd0189c5be3874bd9fc4f8058ff468c0424d33c681622bbf9a624ffdd28526e7a97150178087f2e6a3ae17e2fec2c13752e0e18cd903ff0d8b674fc33b51eb90e4820e3b12299ca22428238187bf7f018d929b635c7168d76be516da95d045b94a7a0fbd8bb65f1011a318e8a42561960226dd76a307476cab967ea18d8b832f0f3eb0bf55d2d28680f88c5c8e66cf20ac78b10f042cff1abd4add7cc7de0f80ec48eceb222c4f68aea21b9043303400a421fdf27a6993728edff5e5a2d0ddc144350787878f49817ccc8cb089f60b34a9f75e82781d69b3088033578f09ac3b0e9a86ee99d2bf5d312591549afbdb78a6ddabdea1fc3673cb1dcedc753896b92b7e31c2a07f04c4ad06754af0d4997173e57bbd1884cb64849bc892b91dad602e3d8e69fa630de71222e9095bd8139eccc84edb0691ba2a8598f9a5701b5ddfc0164c1e869a33a6d62edb4cfeb4f5bdea05f1c8343bb95381fb4171dccc61134a32a5a6d3378d0cc7c8f520815ddf6b8ef9580c90f11eb0c0d41ec0af7f22564d55dc88a7841ea70202ee44ab9f36ecc3a71fc98e1ba4e7b36353a04cd03baa13c74edc977ee33b6545ffa903112be92018d3f8f6a84bc59cc238241f297d1600deff980f65902ff87b23f128240e20f09338918c82a0d138136443d12472ea1c06b831015f7ab0830bb082948709d9c972acdba99888b73546fc48c3b8d7b4c7be3d0520efdc74fd5512076777e40010512c797a362f2772612a077e7b7a0b21873c59c32ffcb09a6ab5df8e3751889178dc47b6aefb98a20023e861f4cc52095797a67104310a2ead57bbac1f66c217620a8b0f924ae8f3b4eb2a8b798015e67a5bd0bf020427638113eaafb85e64805e8146765503f12ef0813714ed1feac8c97cc4a1602c41e8c9a296d8b32bb5ab738302dd1c6d96a3999701c76b8e508f042d0be3206a00283e924d3bcf29d7518811961aadf2143d400478159783fa6d7ecc721aa341b4ddebbcc91d02d98b3d68085f7e26d1a316053ad4a722d51d02ab360df4eff4905a38aedf6645bb427efa48a3afb8d93640c59967d8c103d9ba4733087c83287e1fabceaa0a1813f3feff3fb11042dc2aa1978b3e3dc6f6827fa1c1814b9a9eff829f9eeaad94c471c58427130b39d47e50c93e5f30edb68fa9c64058ad1076c079dcb9dc3568f946c3a06ffa50cc7942e394f91b316106f9b5b1fd89668a4312897b96d3016aa906fffcc68d37deaa321a2d090e5b4913b924b0b1d2b87c2c7380f299d4ea7b1efd85398dfad9a5e68e8daea39a22cf1838742fa55dccdca22d613d8f07b2e11e9a16cab5089da8c8a6650f33e3df1316659350cd836464a89d4acf6f4a090302ae1bcfe0a07b1cd75241d77e0e0ba29bc05987d0b4f7b1bc38f857722e1151d5cc601f99a9b98b976e8cbf37e88a7ba933d14eff275faea39984cedb248691f10a30cc08086435f306fb013a07902514a48cd42e2cc4a8f5ea4c9a6fc26a224500c0ff4a7aa0687659b3121e0d52ad403cc7e07d44f4a1c9bd3967d64da8d49d0d48c814ceb42c9ee902ba614e4c7dacad87fbc033388aea1ba93e81e543adfd55689d8493db581b6e42c7fcab4af7d512e940dd74bbf31f49ccfe89bb430f39c918867be391871f7c0d5e8198cdd1156207c40038f0d918547ff130820a2d30df0807cc193b40a9b5301bb6e3dd01a66c0532e383c2f108092e113667414f1c861b0fe490c7f8c1b4d888961dadd61c961190974e65bdc609071d042741045b4d5a40fd9fa3cb3ffca31f77822b4cef9d748fcf6e39f01c7c44069d14d7d29140d3811dba7250c7ecbfdb1e40c96849867093412c35120e6f706e2551b92ade4efb6d7b56558fbca57aebe7954542178793951d8634222c39684cb75a765caf4f7bf358a4ae676177565705b23c1e3dbf975ab208dbaaaee2827d79af1803572e3f095e52a701f82eca6a8d2ebf9a15c641e353280b217bcb2e6a182300e8d61e276c5a984f25fac986652576ca6d1b76d0b47f525737474baa3bad06fd40dd0db8b4190c76a7eeaf9d9012d299e35923d8635f57ffe46513df484b70797b44e04b191908432b4bc3ae6b1a9f670350965ba230a85080b5ccc8132d0c2205cd1c719238d9c724fdffbd9993883b2a98257d2f6057ff44fecb1f5184159fd28276f6365c0ede7bcfecf14f7f21d7d9b669f6a3ed3d02115a234e847cfbf5668f77dbf061df4d31788e6cc7855acd4218ecbdc3ea7451c27aa8450c6311041f4ee8a9996aa600d5fc9b268273953458fd24a78d63d845bd5b4fed17663aea245554a7cf2b70f0f5009e561ffda93e8356d4337e2fadfc74ca1733c587f8052a77b88c1879a915a8ac8ed394aa8337c9c5499f622906f7a243eead027c01f3397d436c1f7e09fc84419fa04f803db49aae79db8722a2a1e8044321bcc86441d24a6b135dc271229e37a2ce57e823ed331d9180530765c547e0bb005be0a1d524410eb3b5201ff638fdf74ae59f458ebfe0cc6032148294befef8310e53e2f65508ae2ecad1ad9aa2efcd0acd9b94065c704408847a6d9d7968c94703b56d01a8d80b97404cc5b01872032af06846e6e1dc00bc59c45060e2f3a62826ddb64edd1046ed46acd91a8d527a6541e8b04d00ed315da39e9fdebc4c3b721190168b0dfd95f669a78b9e6d863a4d5311d2c8944399fd968605f66f670a732c471309e0d57c697818e7b499b086257a2b736859f8a170a9540d632ab5bfe4fb2e081667d8213bafc2048decf45ff073d157b0905179b700d7ba80d47cc91e938b4ecc1b44c1f8473cd5057e6be8bc401ecd28f50cc3037cbf02c697a82b9da0a3a41b9c8afcd657b88863499dab8e032cfca1b687d1cbe31d3693bb669a90920f0f8a5280f34e25a38141b493e9821e421406a531a45126ce79a8721f1d64b348d1843e3e44ebe25bdaa3e7674aed49d9b6edb77cd3bd233fff2768a878770f1685e406264d865e4183db5d6f356485df6c6a4b8e67e91653dc0d614f76b6a5cd7eab0b3cf32ae6629f406e40ba179a4e0610845becca99385e1b02c55ac1ee93edd17a4a2eec2ee89b0f2c89e6c30191e82ae6a34edc5b82029902688bb6eac2743111d6ac4d9989148ac8160709c13b0deffea8146094ccdac916f2982b3cb46e8b6f23402f8924b69287e96a1ff62a4bf8ed959413130f5fc82d61966372b1218cd2ff048fec3074cab143bfa63d4c7c39e624979c21a3fe029fd5401f6cdee3bc08c0791ccbc0eb555cab49921f68d635efbc208a50a2588a4e58d553671f80f49bdeda49c006f34f06f5cd7aca013e5dd9a65daa84bdf6ae631c1ca5f212f8dedd6cf5a55ae5043ff57f613d1de01515331aa3e4a120e4efcfc13eb2cc4aef5daa5d8145b22d257c204351aa6deacc6fa743fd7bac597320f84238371cc46b3394c405fafaa13d9d6998d9fbf143c24406a8e7ba6ab8e4791f8b3188951fc4428bae2bbddf31ff7845bbd9cf0fa387e8e5f2e84482ce1dd105315571e1957a0aa2f971d064939053d8521c7cc99034006052c4b1b612ce330b6d6b5a794bd367fe147575403eaaed52dc3043deadba8bb9e3050faa93297f1d7194f65d6e1e70e03f7276e6e236b232743fff06670aa4cd42384f0830160e56b14d08583f09e8cc4a3b3bce508c005c0729809246becf504c45edd9d8ffc631b8ab76fcc2430b7c582661fd3431bf8fa4aeff2088d8c781da06acd79683418012813b873b8f84af120fae40da5ce65de42c7bf55750feb9062c119a0503cebe79273be5d027bce0f774099343d200b31de35b3dfa732802d91dbca4710747911288003f4db093e0956c556633b32eb16c5d4fe55a8551cd80a2e3af64a72a17394b2a05f61e02be9a5e1b2a29e303dbcd3b36a550674c1aceb18a193257a610235a547f292e13510491029855f343d79713cd3f34e756ce49984860fdff86bf8d47082da31cd30bee9442482ee689ff1ec39c7fa0adc12a5a3a21454a994825a4aa520c5d2dae3a5e2e6fe90e1d2b39168cc9a53e042962304f903af9515678058985f91baa35198429543693495a53eec5bbb102cd3647b1e2da1cf13ca4de52b6392dad2634c59a616b204f8fe4a7198e01cd4ea6b7eeb32af60e1905556e9aed64680f94dcd62f876b721e5beef7544c8cc6ca4223c6f8239ff319108110a2e3e37adcdea025f7f18292d553a62caa96fbd132e0981c7213b630288528fb316167d1dfff83c3d46aa14029458cbaf8dc5873a159fdc8e5af95e358af66b376689549b7c1363bc3357eecced98bb3cf6214bcc610379ec7850728a640e898348060906c4086623b6d066e09d27d78680d366d0b98e10a2a3a97ab9de36c9b8b6de5f409ef0820fe4b5cd117c915fffdced54912e87eba972c58ac39ed79568854cc93b40200e1ec204b9fb46fe2451d904ac03155ece641d0f94eb99f93700ced0c12e5d20e349e3136c2e6e7f00e2f2996d28eca52dbb2b9308a3b483ee7b0b26a744083de9896c64460dd2feaca3c3af0c620409730d2727412113724036c7629493b04b8987d1208efad90669879a6161a5e2e09be0b8e3664f4ca899679ea8e814f7e335db756eff0ec5813fa3971a83062f2567918afbf43d2b19755dc1d02832649a84678ffd9eb6b1433a77b1d62fd0fa55b360d64c9f55aa990021468884723b55e66b39f83e6cb7575ca88e41108addfbc1f55f5daae97d3ed267d7649d61e3a75d152c18ad152781a39a1acaaf7d02bda7b950f7bf2856650fd9ff5e180f5552496984650ace359010c982a163bfed4a8914badada2efe7d7cdf652dfcca1c1e1d41d35a96df65b3afa80ba3877d0bf4a1375117ca9220e63ee003c8ada11d06e823d0a34b089a6c64b5a7e609502b2af0c05b97597d2b6354c3eb0d26b24253f20963269ce41d5ebde401ff2898520158bf403228881dc1bb54d5e8f95e7b2332486b9a137a8e3d2d83a80217a1c495ec36718aa68ab8a657b928e254f7937aa6ffe95452ca59e4d40ded5023bb19c1709f804fd2a0305580faa4aafae769e2d090156bbfcbec546a1b306b3739d5f175ce3eaa13deb1c8620a6c27d2858f7122e778ab6ea01ed4438a2b22090ec64c53f769539c98fa5d11411aaee7e18f3f1829e722534c16410c6fb7ae74e7de1b1e58422c0780f803446b150570e4eb107ff8320194523ada88eca58df744a0b9780ab8c24582cfca2479ad06118de95a0d6dfbb92a9fadc649dce95df7cd0f13b097f9db0267160efe0eb5e6cc5a38dc6ccea865549b3969a3f6ad987243fe14f47363d09be341bad273ec32f2453128642d8561a5f958c4b9a9d32c1e4c6825395127f7eb209e593669765c6e335e0da1503b9c8114cead60dd90fa0a652539dbf8ee8d5aacd24b0e77c6c5e9daef4eab470f954665576fad550c0e6b560658719f80d5ecb462dcbc2a92ca68be4df11324187eafc09d35b2d10ece461e613482b8f7064ec22de224d5bc1933741f6b76bfac3bd9b3c27cf7f325a4d1a4e8e3118edc34724a8eb0420505ed289fc5b4890791b42d50e4cfc39323c8f1d736271e2eafe555c63f538ec4995fd59c6cb1ebb5f4ab9f09ae0fd0ecd7ade199d0cae90190428dddda1bbf62dfbdb41d63cfcc5916d98eb18fc59ce571395b69f76fc42859bb5c020ad89cdcac7acb3b9f05a9fa72564f26d500d8db411cef1a91c5f4db81b8f2469ef4c177a55cdc25b912d5b54bab8755d59c2b11cc5d82791a022c6503cfa493821e41cd4c234f6c25609ed8ece51cd9bdbcf89ac5b14763a971299654f1bc773ab05363cadc5a33b8050f17535988456f87e40b5e23b39dedf3c7338a1e803830fb00a59a992a1dbfebfc77bf6cd07eb1472bfd0b3ba59839cdaac31030ffb254c3ac9b2190cd841259c335a3a2d7e1a0475d37f51feb634cf783d8d3be463c6e83d6b564438227959c1740bcb80ecabd6a4c56d4fc2f3be7be16d4889a9792f8793e7f6e758385a253b1a04da51204fc7dc22feb5853de589cc6c76345218418ba6f66900f173facc88673e2ff196e8bbec1269c1e216e5ee25a64c9ecb8588bd6a7f8b7b1d8ae17f33ed2f9d92379517cbd523a7c8d23d87a689f7543bd6f467eb0435071a4c4e33a48520cd82da8aa1a3d7f837982bc4a5479bacf90980b75d1b88cf5d0c4f4956264c9869f2b7d0d3c35f04c561dcea846f56dae809a5c82185fbcdb33c40204a222613e7e59bccc1b992a73e7e327d524c00f43824fe704bb9e99ada040b00ec1b5f30d0ae1cf5d5a75349ecd79ee02c6425045dcad1ec999470422152b8b9d99cde31300e7514172ab45752b2a81ef0e16259ea7cd56282e11dc7e5487c9f14d37c4c776a00fd0097afcaf7af67bb184e6f18dc9a2ed25541ebfee4d79a4e2d7252dafe153cd9754637ccda7e565c7c30fd41bf4847bb43ccc858a9a98c0f78f4fdbaa9c5adc4700217c7465119c039b85f1185f58b434d072667c13a63843b8d2d9aa20044dba5d7cfd6f2e0fe5f51c7c8830763333fc6673e069b38d55a45b62d5162e2a1f04ad1012759ff3b0d8067d4f0feee6ac478171f9addc89995902aea1105b26f8cde8dfa1d1ac8074e01c5c9a44b1efc89144a005b0b2751473b75584202b6ee1ec15dcfd1664fbfed5ca77e2373200811fe9b04361f8931bbd1d8627a6f69f7ef92aea74d6642c8cf6c9b25aaca935aaff6ef55a26d849f6cffed046224cfae279cdc9cb7f9e350a30e65160c1ec58f720e6b412e6521533bca98f25b9884c038705879d274d3a6adbdffe8f244764c356b5650a2afa37257ef448c686568ed15a1d69c726a95894eb7a3369df32176102d06f3c419ac6635e7becddcf0f98c6b614483cd505ecdd225907a781374469b0a8b463510f5226ae5538de21fa2e09874a22084509f6994bd920dc51d4015d903f37c1663cee521c021204741f09b7ccc41c3225100465f7590a73c9d0a53ec527b366097c1afa20c511fbc99189b5d8f95eb65fc7a93658a098a5d984b3f5a460437219d65632b1d59aea4968945c0a52ceec277fb7f96398dc700efd9adad510f48d5434011308fd39fab81bae86130c874b56b09f95a98f65accc344441e9b06db7782f5658d0e40fe9bafbdee7d7537dca463734fd2c89e91d59f33b69ac4248450882562b9a21dece4d4be001c12e6b981e1cccd36d23d1023d9c090c65d9557c1d3d4ca618ee390caae0c6adfa737dd02691b1654369895bb13719c10a473ed2144b1ef8094e0b7e012a895754246e61d48721e38035e38ffaf33d2faab470d961c2a8469a89817d2417e7cedf6d37f32f078fb89c4f02bdf621952cef3a0a160ba8579d8cb6d48dd6f703411ad0f398141c5578f1f6e31b51a37486b9040357aa69366a2c0401d527e4e3a704d825129ffa7dc1e5ed35d7cd4965d79672605202da169eb538279185a42b7e77c322fbd558abf5a5e15af4dc602ce605063165643d1af191c97d5fd1cb55c8c0c71f34030d92df76c0a1f065388396a9628675ab8671e6aa7af8f9079cd5d34b5bca18bd514ceba8304c31112ca021da5a74a2d8a33815f54c6b16960a27bb288cbce20072be0b2aa1caf32d3c9ba139734baf1d8a16b89399af1f1bc27ade37581d19dda85e4d1d03c144a35420e6adf0fe0a524d8ae74b65b3e9818c3be37d780e11e78c11bffb93ed29bd56de3f22eb2d916ce39c54a5d1c15a2d0eb18c36966248a559dea227a5d13530b836a887729d3ed77628203303562088a0f38f08b103cdf5b32f6de0dd188f3455e2ccd02fd446199e46edbadf56bdb94c22f052cdafc8426b406afc09832f4b0da9990adc5249ae3961129f1339c583940e11c38e46d70a34fd74d6996b30ff541d240b01efaf2520fec7410409dc53cca0ef8519eccc7ad71d62af948ae96676b215576e242ee753fc0037f7ab69056edc60e08b255a0775275c6fef5fd3ac9edb4145cb19417d265d7887e3361bb3f70e9c938bfc4f752e09be832ef5da349742f90cfd8a6fabf66ab4ff9808a143c57baa2bc1164044015903142eb427f0b61ce29e489fc5ca0583b3f77f184954b8160d5e3af6ed867f1ee8e8ff7d9c2e09eb61884c21a95b5b9009908a4ee8b43a99fd5fd099124d434ed189563ccd14cedf91542f3000d31f4057905daf5c7cb67e8d6db9b76b83e02eb09023e706bc96071232d16fa9f99867df2810cd6e79c7acbaccb7f39a5f2b984947d751a6607a8f606420500026470794da57d035622ed5fa6bb5df52b082451da24947a9cf2f1ae61f875c149854d77c60449542093d4f45cd3e1901a6269046f29b8c20233024f5fd368424a0b1d1f47d103cfe136cc08f0e765961a6ca79efb1c713b905c4b62fc2f6bd9969e432c75d33544e70b29b5b3e5b1be2a88b19bf55645a930c426f6b3c8af60e876fb3a4eca64067090ebbef8e8a2d76d511a0f20163ebde43f5ff80a5111442fd7a2fad3979c062ac8e88e33936cdafcdf6b972abf9e37233c3b425e2f58590cff84e12d68bfa923278026e3c872fe345fb63c3527f58ca43508b8a58da0302a0ad2a9b544af999cb4cb7e832d450fc227341df53b52df19d81dbb6db6c3e40d9134a99012ba0d7c0248c43761e8bf2117e8bde4050e6cc8686493698546b9b4f0dff7bec57a0e078b69b6de87318cc45c3f0994c75a7e93e336944f1283e5bf01059a4d5a013cf9beee9af4570deec3caded706e836289d0a6135262593ffed843c629831acfdedd2843d87baeb183297ab14c281f91cc2826d4ddb0aa6c536beaca7b8f03a9fe41889644778e866a471c3fb6598ae6e5ba3b9ec49920989fea34e79e414d4f4bbe2c6717b280fe67ec7515b037a5fe4058e2a8ca658a71bb3990c577496f77d74ae3a3c32d5c28c2b6de4a66cbf1e000afdb65b5fbf162626c5d0745611d7852762ab70adfb1994b64c3f463c3b09c2cbe9a5f6310c0e5ed9b3856b1bd6944ae860417a5dbd6c32402dbc2ede1f800e81728dd2e54f167b57f8f8dcc42448f2798e39528c1e13431680a733704ba5e12b21fc8e5699abf863054ce46c19c0ef6b2066f54ece4fad39f4ef07fa8a5ef2900fff2483a5e5a869469881679bc46781bfe0bc7473a0ee7bd3b0c6d8c8a9f279f1a387396cae5715a2c0b9bf2327f3eb4f88a5b95cf077109c25fcb3360cb18be5870abaf2404a3610bbfdc99cba86ffb3a1bf8e790d0edf98c4261614fdc035c1c7292692dc26c1b690d874f4fd07d7395a8598481b5019ccc3b4e67e95ce8a61fdbbb710ea6f559f3f61810b61a8fa1578bcb5568a0f6c6dd4879208452afd222e9a15b78ddb334ee552d1acff70596f6de43508199e9b0dbcfb8f39b1ad056279aa8e24ece7730039b391702bc877ae05d531e621bf9d5975bc9f3de0a679f72984046691be6e87c713f227baf7ea7ba3c50c312d7980d62028b878426610a077dc16155dbc808715d5b95fff773231fd1ed3427865c99b2708a4a75d89914ae17a714cf84dc4250cfad303068741d2b023d38a36bb6f478a91b9d568675321119f984d913607aa1d5c8be3efa6e4b52635c222f93167e9750f7cff6c83879da80755de54cd5234e78967371559ed37fefcf55fd2e27c63fe7cbef4fe00949e48b275ceb293e0673113e3f666703727afda784cc641bc9b201eb3dee5ec32fe7ccf51fa32d5cbb0089c47341f9a61ece4167ff0b3e6708ab357fdd0ef990821cf71fb5db3916f945b29311de050d740df004e02d8f2a344320bda11ec1b2bbbd4c9554b6463b2bb687d212decf1d1b4a1f5ae9f6abd1b5543451cd4d3db9e7141ff80765025050a9d69d93f75f0468502b00ae9217620628e80ec5f15b4bb0fd61e27f47c10e28c878dbfe7ac1bf16ed4eba8d203233ba14ce744261be1b143d3489386bc4869346dfa06404baa7de5e1d1a5fd26cf45dec6e72534a3582c061ff9b56c8aa31bbb76e7452efe5a6c3874a2d24f9d103adb259dede370ebc2cbcb9fff9a67086184d88a1cb98bc08cedb19144d5fa6110101e605022e1939557ad4059f03665471b3c02d36837162a6c7a94e128037a3a60cda490b47f92f8eb5953c616c11357475d2c505d1610637f5a9f95413de165d8bd3bca2ac8d5db03274e9da1417b89c674be7d5e68c4ac52a0c2e5ff16dc08ebeba8847be84e29578451af9128a7220a80a37b4ef950d7e45b7234a465470232d3252d94d5499110a6e365f2712459fa84a2b3a4a4dcfc62ff853b1b64a8eef8aea9a815854a358204b74cb641884ffe5350315752e23ae0331006279c76b6277178908d4834bd64a31643d77238a4acd72917023bd96156c15be8439594c7855c5cca958c324417356134cdd43bc2b03e766800e0dee2a17fc40654778fb2fab0de10cdcb56f3c733b8eb4d8b59b338dac4971c8648ef0efb3277331539ccfce9c35a8fc546e20816a8dc7aa35a4ab3596cf371c80dc5c5ccc817f51183d43e22908c1fec301e62e01e6a93dcfca6a52b2426599e61b3c035e00c70d147220500a2a3b9f4ed359d622561160d30cf22c060bfb519be2b1f73ae62685d26f0ddd927ff1074fe2278d44b70239da95b569d085b20a5ec6baddc4efe39eb73c98952fa842927aa53a61a5c590bacfd248de306ffd67df6d4a64aa853c8a262b5707d8e21f2e0a021c5894cf6c46fdf3a93341447376e21e05a45509d34ff3fecca8b23832c3c7202bf7f7a4910fd7ee561ec3933f1ca07a86966a12f207547c0a8b48e2ffd49b00a6926e0432f25f26e0820d14ebdd66a779b310e51ce4f8356af93b7f279deb3b10bfd904435d724c6a8360b0375b0ce4c8337fe7892eeb6977d979929b535cbfe099490c9631cd16d13cf4d72d42380aa404ceaed6f19e995ac1186265d45c350513c8c98bfb79d016b119178d95a5751b0abd87f2aa2a6b47b7c560350695eb5bc998f5cc16259b2b76c49754ea164ea3350628d7ab053aa15634de1d5852aad60209bf9ff1452da49f6231453c8d567b7256a71f52ac28e225bbd4bb5aebcbf0fd6a2191f1b1914fd24e2d888f1e385f5122c3b40554fd565759be3d356ca67b252404c673e3786dd2c403b88f75e1cf6900ccc23d66cd30361ffe8f70521fe93569b64c710896aa6ef22ca3158e9d08c7928c33846741b869c6f30d087110bdb873ba59776aa4cba60c1198bf8da3a272aed5aa4763d4f683d3b54cb7141f38afce25e696f119e5dd15d9ea1cc8741c544203ca02a041fe33444a64d53cf6ebcacd253a9851859c2465488bc180a700acfb82ba821b5700a9c0e4f794a00948f39c17a239ba7d3f2fbc26954fa1b26047625e867c83e5a5f0e95dd0bd0837a544618dc348d77499c23696e08a1cdd02c8a240104bbc0c25589a93809d9a1125687f8a04c615c1c0a1c3ebf5b6a61313aa116d52e466ce6cc1170b5fc3bcda137c3246d2e393b85817f81e2aef085040d30912f3a548eac31a979ad6e108ffef54e2882669ac71638d9c045e7870d976729c3ab0b0790926f15679a19d26bb8c121f1f911cc900543e6c1851d4b4aeb63047ad8c86b09c51945db01269e5e3d8c1a13093b2ea8afecdf9e6722eb64f81457f67dbd4170330280d9aa1a0a1f37e500b4555b88ed83b3c13ecabed3f48120e35a3ef3244e23fd4cc2a31cd51d1f15dd4dfd41d38941ab80335dbe2798ab1e4d24762e7ad383c9464a5a674d6dfecf6b8c390eb4b92f1a85409dee788d4c72c1152d3c0bc8f8328aacd9aaa6d6ddfd201c283004366a0f2df7799419a1ae4732df08ec6329fa63b5a021ba2fb5c682099e0258a88e4e40b2e4563515ae10546e94c2502335575f1324068c5fdcf97f2aa6184451ca2a053b2fc60cef117e6d8fce906db061cedca027f024e1c815d7ac07d44fb125a28a735700dd4b5abe1985b1475c821b64ea0cc4755e3446373a21ad1d23c2eb202baa7dc2241fc23c3610514322c8f6a5186d227622725323f0008a5dd699849295f4be6484fc9be7e7834d9649be5e9560a344cec6912def95be64299aaa85eb1c4ffdd4b4d4f1f6af021335afd6129ad5e5bd35da1be8c74d3935e857dbe09cfdeb7b15725aaa6be55f56ace7a8bc7eac44a5006784600d68d8a34c14e04561a1739818a68d86b4444081a71d869588708964d95a17f0b6daccc08a08460bd311521781103dbc64498a013001b8d230482dee2c4eb7f1a95b311eceb44bce3e1d9140e6e599d9756422aba21f553da12fae86e4a2a964a6bd9a194053eef89f964d12022aea9e902ea08e9df13638649c749c5d9828fa64b9b56f3fd08ffaf386bb82e95f092aab42ab333d28c205a3040498ca6be240743e6a0aba4e514bc6b1c98a71ca5df880a1e68b500138c6df8983b1cee74d85c5f045897aa54bcba23bb47c97337ea301440e1feba9f351bef845fd184d3a1bb6db19dfd82bfaeca220225be5e80d129174cfa30aad0eb3be76b9f86de448403ccd50a369dc2f872fd75435bac3c7e67e35981f5a59406c94fafc2087fa44fd8ff0f02a2b78be44a699d08e7671bd19fbaa6edcf344fe41a17462334c2c71c1c150caad9566398fa3bc0d807dd51995ac21ae81d0e410445e0ee588e008f59e3dc7cea095b3fcf2c515df224db5b454d755f4dffd09ecb954adf120acfdd1103cf741c0efc7820c6d67d60c5bcb36f108c5e34b6c7d4a650d57c7d1f9570ef8cc302ab91a037333ef05e7b34193f60d443672c3c14d223d2fad42f646520700451430dc803aaba04c53bc199200b58840765ec4e38e6f19e9e179f67e1f2512d603a3329a6ddc391633b5510f61d36c8a440825dd9110458d50e0ca12192ef24b099dcaedacb90ae5cf30701157e781a1df0240b9acb74f922017d0d9e818664bcafd88761aed9a853bee41c03fca0508080ad895ade5b01ceb27caba8aab3ccfed95d2cf525c0d241c73f8192ec02d2e3fb474ea35e0b3f3e45d47885e58f92af559c779c70f90c77bc3a3652e2419537ec854b8f6515d447af0ad6be9fe5d344e82d1ab23b551dff7cb20cc013331d1b10aaf736e6970a18ca62e291979f7a40d7e04e777d1424f1f4c2068864103aa141f709438ca9c58e57f130e00bb97dcc12e72bab3c64e48723d2605c839b089b8fd5b330a38c9fd9016ee0f9280512bb2b8c19113334199f927f26962fe52bc39e1bed518f785adf0cce1e3587db15d32363dd281125db9b507756be3f67ccd0e95133e3e362dd52cc7961b2ce69de0a415876a23cdceda53583518852eba74958e9a6a3d14a4b12068f85bc619f27f32ca9c5d17fd9e6f0006357c430ec559a81deb1e177a16601e3efe9e8473cf0fb5688f868412275f4bd99c0fd927ff639780ebac27f75b04dbffa731892f6db2e0d905cee153c9178ecfb2ebf7154ed0380ed1b4580e6c424dd1d34eb2c5dd19e4d1bdd65b228376d08ea35e43f9152c840003b46c02848301ed9df2e340a8185a2400436c8e2e226a6a0e5cf128404f51cc141fe59b2938530d415c8cbec91e151b7020705b6a7e6029ec15b328ec62160eb2008ce202a7f1dc1a6e2bc05b4786cc461e959722ee86a9f07113c2dc109e128deaa69eada03b7f077ea028c59bc4533e8e43ddb1a1d6213d2384c62c26b253b351038764e5afecfdbcfc90c961ceeda12cefe2524f8d1a46aca15e4d5016aae6ab2eb4eab74b1266dbb95b791728c65b97cb794f2cf1fb8af53eabe027431813370c8617e0e7bdec37b5d6aad66ed02eb1f10572910561561252a85df1bdfc259d23d46e09fee0635d7b6ecd1d8111169cc126734e042cee72353f9b782b6d4695e2226d43d3ad1ab630b6aa73aa1a7d03d2c2ee7ca3e3f3a86c971f436cb3655dda71738a1a10b350d6778bc8dcf4662f3906f7ae8a7c7074465dc70797b09d74ac479a7a2e812ef2bb029adfc370473d31388a55e7d9b1a23297a5c2714c47c00497bf08c564f60e633ebba7143ba2f1071f2215bc0b8ff67da776443d6324203e50b9ccb87d683b0afb06da05be1f23994d531d3f1110bbf635809e7b5ffe2e46633d7708f0338dda6e40be80979c1d3d713c2f21ebfa0f8dbea85111476407bc13172338690be177d16a03b04d97433a3674d2d784140046352b11056cba60def56fba7bab5aa7d41fad2f1c6ebb4c3fdab040b920dbb15427bf779e2b5e85f2f2fab036d41a6117bcd258a9e7be6ebf29bfdf064f39404cf2117883234cb8dec315dc53fdb801f1309b7889725d2734514b72301970cd9a663c158f79a9a2ba2b9e0d13feeb7ca25982c1f0d768845cce4d71768e67b6f2b0a77c24e0fbbf4706b94bdaf020ee1173277ee5184a430aa9237d7b4b905677a8fe86edb77a1c40be06e8145fa2191df7609d060d0960c886ea97510beec072c321e6629b2c77bdfe80ecfce0d2aa2edc6bd66a6f1b794c4634aca61e3dcfdc7ab8bcd76d3b00fd0102b9604ec045b96bd81575b491056aaf2368d80e1797f60e8ed4be81cb0ced8e5b32f2302f5b6939e02e83099a6536510336c3b8c6f0248824972b8335322741e8642c7c9b1c245c7813afdf517e081d73802d5660f5c35c2073a1318a6800d19d65c222b544fe1a3acb06a21c2462372ca9c1f14d6627d690a81167b167770766f0cead1b2814b71cef3a0b555f391f611d8db3a72d80b03c75dc5a9e912f0067395f88cf39f7672dbf6f02ec125846274e674d15f5171edb0a1dbeb224422b55ad0297fbffdb453408cf3eb058a04a85d6e64bbf9114b1b5f027931d063e3c049f214770d9a42fbb9211651f98f5dccf06aba7f4a31c93ee92c5ecfe9988b203896ada622fc5e4680fa4b4537cd99da23cc0a837da0431c4e809aa9e4c1c22ef301a3a32258f62018eb6249d5ba1052c89500eec777472d8fcd90148001d3b40985ce3ec3d43712aacf6b1ea1a458a5553d1f1ebdc1f76c6a80c87b00b766ecd25593f8a8f10dfcfd0688874efccb4ab83738e25ba8f7aa769c1b700365341c70b0ee067ac22d4cb58cb3dde6ab0b670db88567cd765b7a1cac309cd3341f8d97d10df3b07503058704fa2b050c11a2edd0eeb71cd187b0bea5cc00015e3f870497906d49bf4b114054b546e08c5b782360119d30f68fad369d18072c2747d597426aa20efc1495259121c312a2134cb22386146a0249e37ce94bcffd2b3fa1b89a32872a450c6deff2b284eb15e789d6e5568df3a5937858a6a5df30ed6edb774f2faadc77dc321f499c76b7e80a056b914fefb64fdd419adf8949057eacd4a54b2e4ea7a7c2f44a480bb04415cdf10d7943e446fb873f598404d0b2ed35c88b1b95ccbd1b7e9c20eab4321a5c958884989359675f5bbd9ae0e6f71e6d9d9a4507ae0ab24e8aa450346e0522ac5e1516c742f25c066ae83229479731f8133b814258d935f2dfb57c7f95a8b644c1000bf372adc559530437c868aa721d50431ad03923ce10db9a09bb2f92ccbdbd0e47cf18aa7c6fc933defb97ec69e273258c314c56dfb1de01be3a41f16b2710a5a717f10fb4bbcb9fdffda3241b3ec6fee00fc125f6f96204ab52a7073a48748111e6c12447dfe790af89ed0fa5b6c458d9a187d56fb1ef72fa5a2cd425efe7ee771e3763d4d7681b4144ab0bff10b970d900eed35df413a9e182552f778561f1654a55c2562ee11132c471f588c26172fdef763ff6d1e25953f3c132e8dc0551af6fc3cf452dc62fc2eec3a49538c9eb4f4a304c1eccf7917164598ffde51bc1a227a64a4e43d5464be455a32173a0431aeff0064646ed02c5abb6a5fffbe024d71f3840909ad6bc26a4e8c120cf267882315804bf52b322b88925d0b4dbc9c20af6c17e90cddccdb4a99449ba08888ad1b74a8504582566984a7e9172b322c6e5e11a1e0fe9ffcdddd34a8313e7d55d608909355a5059b423747809a837ffb9688e950cc4935922c0c37a95d4eda52e998d7ff1f651a1c40fa66f10061fd730847f2fbcc85e70722a07ca26d25577c28c48c9154f52c78b49c4911b4f650218e08f6f5f67627270e103d4c7d41b1098b4d0b8d2456d05177a54b00c48537392163c313242f659e1612032b8d8d4649785b19bc301eff18632327a6ab83a34f452f3782d2286eeb88e95e4a14e452c6a404716a3c1001e3bfc2d7d6d7e8c86fcbced271f617f813e266ac4c4d2fe2200af4088b2ff2aa712e4fa58ca0092a37ef9363414563be783c60250f59a06876ce22d4e48b5aa4562d55ecd697089bdbbe292fc2ea9a02cc5b20bfceda679172e06fe7f6dc5dba4c48435cf36411ae48c8514d25870bd847d1a105142ac6747f5f4af371d80960cb6017318ecf0428723e717e1c37f3abba461d510a8aa28b6beeccc2be818b039236990eb7032a6e06aa238f1c9085cf19a5732534acc7a0d6f5917a898be430120b1371b7a1980f7eff251b29401d8fdcaed811965d2fe4080235638fcde074405616150762d79504b8628e4bb14e813de9a29d2291749b9c88c5ea71b47aabdfa53f112d4111278a88b25291975c389fe0a1f3e15d53a2171ae12086e25487a3e20f23e35f2f73711720f523141328580f675b00959643f1c007f0e1fbabd415d6ee16a329e5bc66b8878111445dae6b03e18cbf9de750929738ee065c1e57c582f6d58f29819e18648479b5bc63bc1c5697ad31ef7823a30f3c8caf716f7d431a59086bc174aefaf6ae92841f97f4ca01864174ec3a4e9916241a761c04ef06090fdea1d24b193a2bcd3dc1fad5d3d567fb944a66b40f5175930c591c4665c8c2851543cee6eebcdc0f298b67ecf15d965ac709f376e3cd866b203a2b8d3bcffa505a54384ae1d25b4cac916b8083739b3f67bb39269e431064b003c2f00295cfff35171a7e37a5426a9a623ac4cbeda97a0dc83572a6de8cc2a8e702b4fc07d0c6f71266260035be97e8bc52c51bd25e180afdb2be4b6a2845347c8529627338587be76a07d71573ae689c396c70120d290148a7e2133edb133fdeb69ddd94e7aed71dc7d1014081a7fd597b2e24770b62f178e4c17b624d222863335c8677d95e99a7753a17599880e778797c0a918867319705a7a55391871f04f8fcdc6896e19a5d504ed06e21e496daa8273c80cfb64b3b97f3400f6117ff92931d0989f3d245f47d7317f9c1596121ccb66d068d3f3bbd68056ed2684f2369f705806453c58aca2bbeeff0cb01d5672739e0884d03332f14628650b33d9baba59ceea93de55d09c44082a96622e52865bab581f761cd06ef79d9b07f7f1ac88f3d6922fff02b8f7acabdcae6863c39b18bb26fd4bd76f6dd2e2b8d3c6f076fc3cab5d0eeaefad4ca34a10c9eda4d04865b7a24a46567093f93b914087b68acdbdb0d7d4bf0923267dc8d90fff83a0f0de507888a077acb371c964d6070e8d6a72f78026b5531dcf2d61752da83596f683042e898f98d3e8d10c8d7b14c41b3656ff90e5fc88f0c560a9d2a21c290839a0bc5c0c0c76f0fb429b5f945474a1a4cc93cb90e29cdeb68b05b29ec09f695976af2ffdc9a5789fcdbc32d34f41db47557f6028279af185e44c8540e71e8a554e1cdf7e2f1d7046993e4f9940b6d106049e97ae68ad832b186dfcde54e0f183ecf0dff2409851674b3a78c4c583985f911382442f209c58df0e8717d541fcc58341772ad8c9ca46fc7ca58c407d217f2646bb7a340f4c62763387eb6853eea19ca865b4cc23e52ea1d3db602971129247f1c9f6b11b0fd4962caf30c78209142ce1b4baed82d38161b75ddd99b1da093dee5a2a057f145ab4f53198ead5912676dc86a1d049536e85e11bec0d635da4a90d9b376a722cfb08a5d89e6fd094377526838d6761b8a548007b7eff8d80dd2e476965091f970afcfe6b013edd983a7c4559320d8f4c202d721adeeacd9c8d30b8bc5d890c68e059a8cb08900695563f1ad9b2be836c28a2776f31dd1e41f12559e2853fd431a9791216533662db178c83a97761e3e43676bc0ebd0085935fddcd0d2d040f6eae94ff54fb2237466a6cca6831f48eb2ec4e301b4004691b0378a043c5d317356675afdea72fc118fb935c96ad3628890edf39a0abca831a6d5c2bc608626cc33fe93b6b9eb41d074d4c5e58da5fb7884e091f0ebdee4919a28192ff7b791f8e12ede62771f46cb43fb1fdba9be67d6136c539c4576c17703432969334ae42ece264d054a52000e8a856a63029be45750a83b25b56a800cc3414552a34bd4c251835bae6f8bc52825c4066f7c56e7f63a67cb5f908de97bf7615f0d0f202795f1fc474b43be08868c154ca2d283a5d16eec70cdd4f26b796b0514db3c30a6d81013e62ab456da1c14404034213a25bd53f41b8bcef94c958962f18ac1b11f8d3db4f63e45de81b1cfb50fb296753098cd5c1ec97c923d407cdc300e7ce0f8c0ae00582b7c3e84ae979c31ba1bc308c014e23b633ead6e2d09ac7ef294257d247a6d53791a2146311f44244401fe217115d517a72695ff0a970ec62cce029ebc9898190ee04f777f6874e1d623522153e3b8e5778aec72b55ea5dcb22afcf3d08e164e8c6b69eb0101e4e73d2a8d14d238ce65f03d0088ef9bb59e28afbae5f144ae231fe3a81f833fa70e252ef9c79ddd22bb41ce073e4b72b10004cdab0f4af623988c9a616e0827c25233b00bfb113ac468efbdbb0216b1c3c63996f76061fd5d5d47ba2448875f033fbfd255a2ed43cc50ee9c66ce51a42d3e1a1c19ba0d780f5ef581f214d7f4112e98cae546d6478690a625fcdebdf0501945a6b2cb9591dbd46869b9ea93f8d04699fe8eb9c8e98b0aaac832722ff1e50628b813707176e3171cd54306213a469433071bc500e8d2a4d0e1db813186a8a0fe505b91031cbd04fb10e47cf1259f096a341614aa8dcaeaeb6b2ef901c19a9179e48b2329c84d00bf37d8cf9f3cc5c09c9074314b3a5373d846a095e26c8d6d707a826626442997d3ae7e4db10995517c7ccff02019ad9e772763306157cc55b34701bde2b3aa62b9a4503cf26576c8376eb5d1da0a10f9c3fd9f9ef955fbf8abe46a02e6c25d1eb38b2814a57345cb8193a30378ce3f7191156b163edcb9669f887ad270a4518e909a774e51b8dbea046c439ff2b94077deed438335340ff22e94dbece15a216d39167a2cc508f2be081c00167426341c2bd43f8f3abd8276ec6479ce1d60865f048b6cc46e58bf466454d48a6cfe02e1998c683aa069f520a9db44c3bee54baa2c38537e253209e5a31869230cd9905b59fef48674bee95b16506f856ba4fc38dd23898e0b9384f6d25c54af5b05babc9665e7e0efc05e4227496d3367e9134da3ecffeb400bfa469230bf8d49afcdfd5329a7c7824ec19cec22b8a642dbc12a425c92d2c395f5036a3df9e4154f8261a02f5f70de4932a27c8823562195921c97bf0e31fcba2d00694e92545b6ac7c3c166ccd6c80b6e5b1ee47b7fe0241f70c625c9872f79aaf8687adf3a1b46489d3c1c0cd1e8841a54a8133dcdd03a600165d6ef53e9ab66855c2ef3171c1c1bb3e1f127d3f082247a7adff52c9f11ec93ffa7745c6c7137677860a82cf2468fe0a1bcda4a7a5ac4b69b28a85597038f8ac6b70eab1c8b01179e9eab1e0f65e838124dfba2f4ce421f65821e2aaf2105aed22f37c17c4c231bfaa57b857ac9918d48cd7619213052ae5446a0c30ec40418a02672d228d1248c7f9d44f08e6a863e9388f811d6a6c4eb34f7b9a32d65e608e135fd43ea13a51404d838a5c544bae9713ac9d2c0f65de2b924a1ff5d9a0ca548629c8a6c2266f9bcb6a0660d8aff8a75e96f11ea6045ce21f6be7f37865e00b94a422f21f3493aefc485e187c3d463d68e4c18675008a9edaa04120234b857c92d62e872fae61704ae4c3f3bc6a977a638e903173e1bc81cb9ac36f3ff70d5f8d3021470a6f8177960b1a53e4122f274c34647635f22d0a38efb9ea25fed7b81389dfe5b8868a0b90eadcdaf307d049613eb52b0e70bf03a27752b9a2fca3a3cc24c580d389cbf314ccc29558188f15bcd0e3f869da86693741535363a94c029373a8d2600c1740d5017cd0698d3c56ecfb4ae0aaf18e9c57fa0ddc30eafbe51a9bae206f495010b4aeac0082bb582c0f2513e66e3003c13dfb6e306691960d9c3b374395df6f4c52ed7951f70eab39ddf89618e9065480004500ac0d1171087cafc438c57d752298c0510c9ead219f85fbff7b3a874e0967b73346299926008dcfb3ccdd366a263f8daeaaeb252a358767397465f5891a28d6f29315590753375e669da4b7b3ed37dd4aad83e982c17b017e1f02643c01c1e054175512f0a500297fbb87fb135ab93b15500e43e9e85e3b29e3afbf463287ead63cbfe0f3e2d1e00f3750287e7d296b0681a51e6e1fa739b8f1797c141ddfe0505067d193084a4f326b7edf1a0384f90ee8af420cbecad1b3ce01f59ba381970166ff37d14a605bcfa68041ef04259f6ba8bc2129e093f7f165e66e1a1160ecd0b949b8cdea54c41cc100176ef5ea0321a3898010c54806787be889d545f89b8eaa3c040413f04e32ea3478061eecda786a65e55af79d613d2b59eb88f0044abf279ddb91161ce6f289f9948b2ce8772bb89c97bb1effa559eb82895777ca26f5f097d6caff039c317a2357592f469210a999d229d63f7eae16e233b2737ed100725ea58dae03cf8a4a54d6879c2af78a98f757a8eea8e464b46a2f7662aee33438509235fcbef9ecacb44fb9f77a2003d785539d7ddbc6edb2f07062d1115ccf470f9ce5ebf6250ebc9e06a327eb875ecbbf2cba3ff9c4464f655b451acdfd7dd706834dd61e7aad2576294d4be9cb89f6a34942b2cc6a01cc326fb159c44de5db37680b1664a152c3d2187ee17641806a9e6aed01cd1d70094354511cb8608bf25c3db5a3e971081cf95392195f49c7a0d19e7d5737a062c015de57c7eea22b2de46e79ea9a3857b6949449ffc851b50fcfaa9265e26ac89a61f0c16197e7fcd0f48d7e62af70e0f5e98907acce959e8e7030f5955e9abe495b3fdff77dc579cace5d65350df78a8d48d9782ccc959c92c4eecbea38a3776f2b806efb1106372a51bf49dfb88754a51212b99f5aa41cc798f7a1954afedf53176950e0702cfa08c8d786ed7d59db6508731b3e04e8b040a52926736857ffd4d1749565c39388f9ec534a3b677c4176aabff5cb7ab1bc4cb4fc7b94a90b11bccef63125d4734d48d846dbcbe91265bf749dafae9413fbd355f0c7460545fdf4f703e867252cc924633df9387a378dffc635c995d6c2d1307390a627d3427d76fb4ae1d4fe5b650fe3a1bd1b6c104ddb5316048d837e1094ccb507054a278f81b050d6bc39433443dd7644e36f3a47bbc3d9c898db1f2ddc7e4638ee982eadc90f2fe695c0a6c72f3a409d15c0c6155cee4995f6505ec1b196f0f981e721c5c79b75e3ffc6568f6eecff7639d860c3d4a494645a52848688549747cea90e80fc9324fc0bcbd077ab101151043b63163020848a1c562deeab235cde257ac2fa7912ad78e6cad8ac395413c9a6defe22ab802b1b738269b4186dac3eea9af1b0b45efe34f4d4a7ea18295f330df5975146761169af332d19ecec237929ab539a3a9507364021a307d884e266963b47a3e9cba5ce203dc616debdba5c032b9903cd61bd40a3f3f0bc477ee30a8ce09644381c4f0ce393c3407034144e5898b4563bbe9a2c0f3da73165776f1a1c9965bcc9c937ef3b5edc9a41c9240dded42e9904ea551a367d995de82caeff834b4e37be69dd2609ef05f6a17aa114a8c61702285f711c61cb3ce209307fdaae621ae429de4f5b53801048fb783a1a6a66d7b69de9bb5b3a6379bd5b19349571a5407f2dcc50dbb0734446f401c19af29e04b1c6eebc4cdf436a0b48ca4fc15c19a1e69fb2dfb783955e39ebcd06dfc178d97a8cd06613baa89330062bcfcfeb4e9786d9297fe14b23bdce39e3d60614ac2462e9ff4c4b90777a9f7286df16ad7b3677a5b01b26b1f07897a4e69f98899407bf7edc5685f353749797a0f88ce71c6f3aa0549db74950d77c6c4468cde5b0e9752e06ebd8ce7fd14541b4dfe5da0a9464554aa902e01dbb59cfb7780685a79ada3f59c6af7b267f77c393119361254ff49f37005f9d698887f52dda5a5e09b812848e57e4519d4a92393a73116b8e9bd2e3add004a30c17cd2d20baf24ebb5587f056f37a32d8825a9d5c40f44e3cf83adece53b04e134b00b51ce4665f41ad832de85cf37c5b560d2748d0d7d60c7e54f0fc2e918da1cafef4cd93a0601ecdd30390f9a598870144a524fd3c8e4c72b0d1734e2ff603635879a9c24aff68575f401f2993d89a75b94f77a948d835ab73c89b3ff8a7dc022c888a8da34dd1b0db6958c43ea6cd810c2d71d610f6250a13fc125f09b3b7f410ef80670235611555c8a1a61b62bce732573a20f8a54d7e63714451cdcb973fd4fe2fd994b0f10506588c98ed9dd3d0e5bafe1b5ff04114f9cef3e79bb4516513076e25ab780245344c1236030714bfed5e619c9e9890bf1c49e45e6972aecc285db6505d75eb7188d0fd50727acbf42e4d64c6c5969c1e2637f782c37d365780d8590ef73baed92192b887aff27e13c91cc30106fbd5fe46a1d285e1429645ed77e369f0027ead80f8ebe28e0369dd89360e49b4788d3f9488d973ea33a37ece84226ca61b72b73440f8f0e3a3072e32ec4bfacd76a9604b4e827a4020c2d5170303e8fec3f4e467ebb9f5a0862ebc240044cda85bfc20975a5dbbe2c7809a966186912d05ef1a6036c8c8ab3c373e39aaf6c76c76eb3e0dcb1897c625e8a305e5718c18d40461cc51f98ed9f1f929c3e358842b4b951b50396baf710c128959afe77b2f4215a231504bad5c17577ac59213847acb075ab7ec046824f9f4f080f0545621df8bc252d9e11c870ce15847bf1ae4a69aed9da89d7eedf472397365a778f5fdc2b03a87ab5faaf5c117bde0bd7501502631ed1d268aa81822aa9f1ea6bfdba6788329279d6be06b03b704a1c4ab73077149db4643c85391f3bf153b40a49003942aa98e37483b960bcbdbde3c49a0cb1cf9678c68eadcd71176331f83a03d48eea90752ffc7139ab2d9b0579fc67d158d2a680c68a5c4877ce7cf759e949f1e248c2fb755222117c682a33ca6304db30b91c1cd8abb91d9159df83705e22db6ab1186f7bc2f70832daecaf6910ca5a5958f4dcae4da5c672e0d35c8e7cc95d3d7446cb86c8a941b266710671aed0aa5cd00e5412ffaee559ba32b5cde89e8a7a95f32867b9e533dd04cfcbff4d56c21c277669ff9de73fd0b41c144f3f8bbe4a097d838634c4e6bc321f74cb2079ed48df74a2c0f598ead37b2389f4673404dc9e8fcb5965053bec58ed4d23e0171e577699ba5ed7254eba8d129c6734923e7ef46db96509164b348243058b6fcf490d86836092bad6da5647066185c16f77c28d5234205b76ff9e4c44829202991c6381eba973db24d03d45a26748395fe0852c63071d283912ea79647674b1e9add586900a865838495ec735e4210b29d4609b63c13d65632eb1f6417f728fd6f712f9a82c977731d25f992680a861db4a0a0b8bd9542e1adb906126612598d0ae736fe79e6889a7d8754a87088d2af2df5dcd356561ae114e0a974e657fcc69449ced3d4577964c50b50f9510a25e1b26142875d937c48768647f4ca5d0e899876f47981377ae451f8fff17fba3e817524a9c58233c70f7bd8f19a60afca05633cc9c176819d6a91324eebaeef93be8b0fb4ec7456bb2f0f2fd3bd96de054876f7182d8b98c40b97d3c13605c2fb68db7d468d42dfb5876c8183e61dc59fb44efe9e01c147abbd67843da6568f5030128b28c39c68e45174751c3d438f62e078667656d749699adaf1760e1fd670e1207d51a298508398e2161ee1023fb8b4dd12001fde3a217da2758416f1674012f1aab8affd7c7742a128771190df8c045fde00e9ae270adcd91408b299447dd01f9a92fe5d47da24daddd859c0ce2faf24fa10af842edf854bbc91b2b2d733f663bfb037492b9ec2c6e229cc8d734513bad64d5bc45c91dc17966de21d73245d9389b18ce9f1f0c06f3bf02ca4d2317d259ba16fcb474091f092e297f074f6ea0bcf9370e70713f0c5709522e83d6feea4f02c98315eeefbf8c91017ac5e790e6cf6c9ba89e518630c2fd8ac3a2b5a67106f267b21c7f7b0b5efd6dda7432c10fba9c71484308e8054ce1894e125897b1bb3f82166104405fca7d313172f92332f242887e023fcf9209adbee5bf7d47425825e4fb4792de6ca5bf227887ce331cdd8432d83d5f74e9b7ee1fd9eff8aa2f1a6c8ed657d827688675cf1eaa58618784ac438fa8d819b271865539d5cca37a4bc42b66d8b1e2810d270c0b9b4c6d02bbdf6f1ff0804216da50cdf5b7747fcb61308db3bef45b8794db62063124cf1468075697d79df7b3fe7d4cc63d4943280c17b7e092648f67565bfe264267aeb04841501ff612b32fb2d17bfa97ca05f6a70a4207c9a3237a6ccd223b5a27ea3688484a80bcd15491127f40f64fb899a8e8ae2caa31ccfe70f110dae72f30ac851ada9b53169be19432dad8f2c4d7ac43b9b2455fa1b6f51604b0fb03317e95321a8bc0248837756ff4a6741af7eab7c18332ddc33cebdda78629f818f0a9a57916f01a4fead34352ba9849c7c2c8cfc4b5902a17b7d69c8bd060951f8791a4fac223b9dc8f892c9895afd65ae0e14857deeb2e97161acb30aa5e5a5f4dd6564c95525477c426c99350b7bbd0c7122744063cfd042858e645065b0f65bc3e66e82e9121ab8f1012a13184400e1513bd028a08a96a4ad5790232452dc2fc2dddef73139f35e7ebdcbebee799274da9e98826c2d7c18cc8a94f0455fd090a014e3750f29f34a99cc3c96a9d38b4901ce5a9b0befa216a89e1736fb101fa1fbd28a18a68e07e4ad362c8161a405e7c764a2056fb6577664422caa9266441ac66d35083a0c358113e8568b417eb27fcdcffb174705a2fa7531cb3586f9aa218e31ccde59c9fa547034de7d8a195a0fcfd7b26c5c78049444d13ef1bea4f9d9d02c28d133857081864ef7e7385b9d4e527be93217f82f2b4702b9f7101d1d4e9d6311ad8eb0c3275217e126d138e33d145beb5dff8d7e36afc0b27e967e1ab36162e25e885533e3ea024fc648b89e21a230b62991399f7fdd1b388c86145b406be8e0d5c654a2db42ff3c5b7ad38e9610720667890297f3ccc8a9af8516734629c90a5642bfe94194be048e9408f487ea1b12865bec082a02e3cebd5627e4c6e2a1da1571f653e04805f2ff7959b6231b4177c35797ab4dcd14e9d27c8f8540bbfd31193725e56ad0cf36d71b7d52f63af17e95f331d6aee734723a9ca4d846df093537de4bb08e6fbf67ee8bc6e4090f595c8ac11c795a7190dc04d6aa88b281fa7da78fe254f682232d33d5f6bb002a5badfa76b8ccacb26cfd25ebf325c4134b1a213a94147c70f1f770cb697d75578a5f61eedcfca91285ce79775463964497a02a623a499ac0a58bdb9e1f69d93b1d03a230a1f9080f6cd5a1d19a768b43088b9681efdfe5d117078a233157ba54146c046d73226f89260c635f2e9290ae431154c914fe74f833ea4145f3f1fbbf9b512ee387bd41d086eaabfef892250a9a9876a4542c144e27d87d4d7e2fb859c08d3e9790765f9c11df0a5df63f2a0463d8a8e435a6600a329baeaab9dad680c6cedabcde889b1a21ee8f8e1848bf74e52e01bd0872574d9967bf1274ee175febd389184557fb1b537a9818e2a3005f630fd4b84cd9d0bdfa1c04a945d5bffaa0d6109cac9bb3823ae369d863291694e92e4f49c871d833e7194734a71f3d9ad3f27baa85aca9e4823b5a9e6dd6608a7be77effbf05c148e7b5b010c290e96d83a508b428240c25c95e598c31f78606d091e17584b28b80a290382cf5846cf131f3229ffee2d7c56ec8ecb9c360566dbac1d6d51b113634c3035aaac64e6e3c4908e7a8166739a8c72c36562dd4a69a1754208c74c9f4d2f2e58c868433104d80973dff93124944502b5d88b89a8a2383536744f506dc39734325f062d623849f0509c08995ca24be3dfb8da5a9b20bedb97187fd3687fe2c7e047805ba4a07574e3f16b1023417d048f1748b8af5f227b21ae50dc83a178b5bd14d4b89c937f38a601880ab6bfa8985efe2dffa1c6db3129642876050a65deea0ea565b931ea02a226db02b535ba087a381557298dcabae61eec6184235e765a06c6d09e4afdc0dac69be58e0beacb637b0b5f505b061b0d50cba8b1fe2b0bc38649effe16f575efc55534a4470d405f41fe84e93bc808d8bc5e787a5723d6b0e6a587a6a204cbb46fcad7291485a1786dbdbf9c10612a0aa7fca1cd221f210faf4d921129e5b56b2474675b9e6e083f4ec6ac6e6c15cbd70689a047ac58c289933381f74dba862453b7e87126465a8d8e15563505e26166eeabf9c8621d4900efb96943753521c8947e97e389c65d6f2f5a398e710bbd030403f0a4887c805ac2b599476d332a33cf1ba1c15aca6b45b269087a85a9e479faad8fb04ab48c703cf7810db853005af30502ab3e1817bf140acda5067cadfc3f8eb66041f774eab31959d06c3e9507a05caa0e9fed4f0c08823e5afcc1175ede85aab90f3c3d7c8ec78d885d717c59677cace1bca655714287cbc7edd6ca8cf564d14bcc47fd1cb50e1924ad4e197db515c3de42226bd0931c4def10d1003fcb226c41e8338dd124be28736e500fc510d2d70184ead50f122f4d3abece29ea3726de9ae50d5889d4f4f06cbc10dd4e30b38693528677ad16b185159c2c7b682490211136c1fd0b36671268e18caf2803e0f6f91a8155e96100b57feaa816b78d427b4815765441d0af7152227bf2348ded07f744a8921e1268343c790c49f31707f098a8416d5ec5bc8ac1fb0b742f1a3db0263757456879e23aedbf88d7bf50d41a3839a829af1b7c01c989bd898ba4b1bba05309f3e9e2c40df701712bd6ccbd4df9672f1d088b88f306b1777fd3344b22366e2c4023ae20fc5a351f925d4e988c9d0a35fc9f2000dacb064a6e917aabb8bc15a285eaba2279295059dbf7f518f1f6d59008a2c09eed1751eafcc8e67f11190b01b8b26e852e2287feef8fc5f826bae828b8a0696207e56410c78f7a7d981712296113edeb03d44226467d98f18277f4650963df60681b72b064e200bf99bc526da604b94bf86dedbce8a1a556e8622469a2d00950f17f2b2c652d872ed5aed2981eefcf2941147bdfd078feab8f70f8922a7d376a424ee3b5e7aa9efa94d74791cdf6d98f3a8d45aa3c6c2e3a88429358aa3ece1cdb3c5a113c7a5486308cdcd07258dafb00ae835a7ace6f902f7ba71637d2ca5d0be8c1d6b5f3bf928eccf9ea7ce0df4b27004033a1578a3a98146a55773a438ce7e35ff638ebe6fd55c986012b84d295043fe0f3cfc3a6f80db9a2dd9440109fe09e0e29e7a63461c2ffeec7ff4de6c8eae5a9b1336ddc1fd4495015966aa47e904a03e032778fbe13cd478a901d5b2267e480741c7453e5b37b2e9ac234ccc93c0d5f5c85c266747328e61e06d14250a0fdf9e1014031e8a22782d0581ed4a5d52c487366e6bddbd6d8004d040c9d66f52787b4894c3e804ef658949bfe9838203cbd137319703fc8f16ef74958a525a586c78f438fa36e5ecdb802d64ba186503b59dbdeffd5ca2b2430f4f87c1100488e5811fa0f86c31d7b40a5ae476235ca74a6c518532c0903a1f4a8840347dfef721c4bdb3f7307aee0df66b1c895796a0a533fca1abfe289dd500b7176af94b6ff826e46770de9ae967fed5b6a059936f2f52211c11e50bba834370e7f7105715357a86254b75d5a253a92ee55fede016c9f817d8d001e78d6594cac5f6cee303aa8c8cbbf2f4320f59318b6ee78b71603465a03d9dc9e95897940482496f63777c8e4c19a9c34bc416e41f8df00ffe374504545c15c2aa0df20c78d918d0e6feeb4cdcddc1e5d851dea0fd28ca9cc8b35fa16ef0ab36e2c100d5075b505602488bec048c9baaef2d0c79862a1fe8beff0586eb2ba9796825839a877667a12cc065a6512fe8764599b7816cf024228dfa82beaf07b21520eab1fe3aa3b7aed69bc728f972572a6af4aa42bfddcab243a24045f99a6468d5395d4415b85fb08731c1bc24111fd3a82c66ec0d36541a154e22bb69e87f72a5463dfe2d9975af030162520b0528acb0bd68047a20cd30f5f0a0ad6ba2b760755234c16b44fc9e1124fe029a69008a1c76243e20ec04fa70278dc50fcc6f19323462af2f9301f49cc57fe21ee48747983c73bc8608b2906ff9bd1ade4d7c340b572be512744939f0ac6ed71af2ba26e7e41a1cca2bb253816943dbca1eef436d6035454108d62fa41e2649f24bda9fa77cf939ea63de88ac44ed14b1935d8c4122cb12713ac53e1c22a48b5c86e60c526dad177067be6ba6a6251e7c36f39dc0b4be8fce1e6f6ce884eaeb1fd75b6a2a472164d0714e1d685aac03ad264db767856511728fd519543fd3bfce2e7345f489488a155b0609df03f2d6b6d279eb590423b4d6129897bcc3e7a69b925df95816cbcf2e309ffc73736d4a0500ef1355fdfe1f17cb20ffca8dc5614dd46a079ada661e087b9e7e057b6b7843daae14dc94cd029bd0e8f79f3819338bf78bb848324071e7f0c14aedb33a47d17ec905572474456a83de1c4ba8b08d4ee62a15a19e2704bc5fd1270235286f10881f1ac596e6340376e47998c481187860a9f1c2136cb62a4ee819dd538736ff63b7707d689d847080289f13cc94db86d9146247e40d8c2718c769097d0d1ea5c2597222c49d5b5d635ae872f5ecce559cb3a95fe350080e8b15ac73224114b327cfea6206d203d3cf1fce8b38afb81a966636de92e5dac4644f8f80152784b0196457be93c3695e371789d55fbef341918ce95b17c2c32cace05da6b5fae1d2b21278411e392253870c1612617b88f79086c7f29da4d55ad678fe8aacbf9cf3cc9ef68643d901be0339085a44ce808198415a8666f2ae9de242761ffc1bb08684cf0a7346ed74bb575fc309f2ef548bb822c2f92c0672515b80faeba8472c8b0ef2b213b6da1a13180caf34fa52b6552562e53cd2da016467f059bcc9012fb640a32f72c6affbef380fe78e51cc3a630ce5c6dd188886951b198862822653346795bbc22baa9e1f961765cf21879eed082ea85834bd91cdc0f114ff061a5f292e8e95ffe33b46f9f84bd92c2442e36807bad35d17cf40b18678faf867ff63319b7259efa8f35e16a313fcef963aff8f91aae8587394c63547cc433c51fa7334a0b359e170ce13071b2bce6e9aee4d7d657f73b458b0d14f349e4d2104cf20346883b614acdd80e7f203303c61b06f910405fe7b8d51401bb325c2f5dedabb31901e3c9f7f1cf3a866d11f55a10f76459177ae751b6feebe3e2dbba6158ce43d885e62eb85ab9cb2230c6b62d78b7ebeda5612c5b35099a307db7444f1629446210ed8c9bfee46bde04842f845b707799962fa4d4ccd024e56d3024ae2ec5cc43c256b4d695de3a5186eea953d8e9b2488f9f292cf21794831d7bb2e9c4542a7b70f1eb11dc16f3e1b27b5de74b0eb5a1d249df21f6c560a60e900b92884c47863cb0af07ee640d5b6fd85e69e23d0b64f2230686c3d03dabaaab2721eb39c300c2636bbeb0c7eee01ed1aa06394e358c410c6dd5a176165182c804e87f20c7393a8d653bddf7de529b66c85c16de74cab905a013d2282c2ff801edfe01d4dc64600af587ac32c92b38b8c07f6f6f65f3e0de1a1bf1abdf42841a0a07288ecf1a13a74b2da6e71585cb7388a337effb1d87f6b81cf310b7cba9aa85434096247db94a63128f6dcb35758079136c6fda1c44a8401a6cd35b4c821104126b504ea351d10e215ee7dc8df8a300cef9e0e401fef306a815148d8b61286407f48db7e89b53b3340b5f89974f392a446a5323df58c8305887e4b9ebfd19e026cd51a69a2eac6ab15913d19e71032c7d6dc2ead3e648b302f388a104bdde9ba22384c2794bacc534469a3191fc244c1306c87368d8dee453cbf798916b84d490207cd761073f447da5dacf9e22e24c98215b49e7a5a8e9932460be451ced725b8205ea662f1b690903c67f21d6c8faff97ea2e0180e113968e3b1216042512a271437dc4f2882de49643cf149be2bc759305899968862979d4b3336461c185f9bddffb765d3a9a82eb4aae07d4549d2383a3418d288757a587afdbb7c268aa914b91337553896c944ea0a2af3e6ed483ec66bd6d74a37868bbee605cd4a678412a40fb2487d04c452f95002c12c2a0d0563334655e4acba32e61d619538652cc92b7f91c46eddfc2911a3242b77d408af90407a94896b7de3d594b57b465c194f58ff49429a9309d5229cba6184ae95cdb28cc268d173222a64f2dcb3be88d42e241ebded9f885a89d066e6eb84b6f30007d0e312949a5ce24afb08810a1a687722c58e247c6d0789bc896941ae648be9f1f8f05d116f18d4a909036e0d90a448621e99ef2a527caf4b129cdccc883fae651a6f0a9a75c2c513426425215b191297a5e27f6b95367b5fb027c27be96fee784490c640b810e41219c80551ff93978e902dfd4d1be43236e4627c57fca38420bd8a4cb4138cb509b114641a8a81d085c1da6199a5ae4b88eb14649c9d402c2d12a3242b58404b400be1f5f5f59603fce1590dc53ff6535a10eca8509eddfcd04bd3cbb556d07cb143ad3afed916852db13b51c7c38d53929da7156331100f6f18a283c61b66d70945c995077d792d858eaf74d1f9c549676a64ba956799c83777dd5ea04d1be42edb4fc1891e33384fd1a7148cd159b0b9e369dce4c3aacf020bfd5e5fb5d4ea34541220bc5eda3d2a2112f69756e6f84cbbcb7678bfa55b5bc9e77d1b5a739375b3cf01a486f3d76e31fc143e605462dcf61345635406e7f21449cf4d38e0c2018803432181286dc34fe0f3e28380529a9f6b08a9532bbcdf61aee5b9a78e9908100a6981d2132d4927e90063d1e718537bd638ab864562c8c800b426df9f68104846df033e8dcc0c2380d70450065f7a3f2bcb8c9d2e489061ba34999dcbb44bcd993082519fef9d83ee3988fe53a8a2e489069195de37095d19abc7498ac379f561273c865cc773d81e9181d49318d07003fb4a4234ab807c1a11d6174b3588c0651216aa0d4e5ae69b405160b2260630b4958c55dbdd48bea813e79c24c4b1002a0820c58b674844e22f911ce7005bf586466dd70c3544b76620d528927552fd99d94b23ba3bc86c5c03d69c2ba902df9edd76f495304891a1d161cd2f001eecc4de919ce68683894552423cfab90f112873ea3d4590b708732c62bacb10468a54bb20fb43493a6dd229bdb2829cd1a17a1e146493104599c1367bdc301e58b0267922d474fd2c471f566b28c7c37e3191b17f6c6d46ba376cb48c4daab96aa2cffd552eb7435b78dc6a710e70afed632959f5c38f5f970b1612421bdc73f57e1457db186cbfaf36d060ca7b066ebcda5db06ea550b6affdef73edadf1eeca7e1706e8010815e3163c81880cc9d4d822962d67a295f539608c50e8ee900a2781596ac2a241d9bfced15c8803c0c01839360ca82854a78eef62f6b02c2d4d668ec25ced2558ac9e098a669d4f74b389eed8c51205ea13a83a947d77314d13e2d967acd710c489ad6518ad65ef286c797b6fb056b7e804116f1c1fb604f86f01132ac24846d304ba8a192a127c2f1685e288568ee3d89af90c51b176ca7a59be4605f245029b2173876c1fe03cfa260650d2d7f55bb8320d0df0697c778ecb66572e3e6a823b236f5050239f41e04369acc03f6f97eb1fb9b32f0e72814167c109bd95cc6fe5f6d3b3cd30a83fbc2bb16976ec526e5c56a9f7c2d148feac11d4d5d144abb1fe45c6db5249329809961618f4c030fd453593e0f9aaae8c9cf7e9824f39698891c2a78179aa8777c102ce7cad13d35ee2a2ae14bc066ddf5ca6c0db7ab02d69e4bda61ab9ca750a8232e3e8b6deb936d153587101251df99f3daf26a88c1a0b373b7355400c59761375b5224cd2fb983f38cf5805e6c732f85875c33be88c1e1a335762b4a1a066cc7c98b857a6b9a810b7849bb41834e1cd0644f1ea47558b066f8cf141e5bb7abd518941420d9d47ea821f7d960552586ab13ec64c0f2b4c760fdd6eaca17959049e8868b5af29d7b30001b039f2aa3309f60af28b2649cf78e04d6802756106783049b31b962f6129a9b81f4e66985401c93ac92a0e5a3a2035e532cebfa5beb057a563b821fc4e84041551bc9520ffa326af11e2385eeb8015020d3dd76384f426defed15084455795b822cee4733c7a88a217087cf42829dba38bb6d1b1dcf7947dd4d9dd2860362041d4a35ed7f53786094886dc7814b3c7a10d4770e2531c79986006490eb5299ba99aa90530a92d6fe7eeba4f7a129d3c7ef546a08260e1e7aba282d4fe0a7285efb7f54b965ce168cc62f398cef8f638e147eb1fe329fec986102d0eb32ce414c6f78b1c13f9435d7ec72eea79ca9854d0f47f5e5c199c69a9daeb1d1ba052556233732d486a050aa047a6e9d0454f424df48957f3719fea6a77c4277a5ec8fd9aa6b6dde678a34d7dd86d42e3e82994c37b298783abf8da15eca2350c80b63ec3614801389d1e17f8480f63906ff8a4ec091f92f715e24e54d1b2df793e29dd7d5609f794ef4f897d7e1eaec50b469d02bf50246c09c077a91f4b4c16fb9cc417a9cd8e4eeddffe40095c11b0b8b3a92f87d657bad5c9e8ddfbaf11490cf0eea2ef251629551abc752217dcd20fbd3c8b88d7682f9a2b0feef04fc9798ce23596542e39872819fe0f8277073c47db9b682625b5c3ecb2cd350f82b317ee756481e0191e107a7b155fd0fd767811547ee2482f3e27767a5f3237670e61c884111052a1ae74a460ea8e9f7406fa9b9194bbb3c8e261d3cb0d87ae1ec1e6bb025efaf4df6ec027a5b6da24bd35ba3e1541a0f7cd592601a4d109cb6d32423537e632008a73b08f2e9c25d6ad282976c15e896318ac59a0923d28daf1217588854ed86882c7835e2178d8423b8499b85be95f38bf5cfbbfdac3e345880d4c14b47e808680188d72d9933c4cf5a236f0b285d6f694c51ac6a0540158828280637b60259691f3028005e9dba410701fb9af08e222240b44e80a39813410316094f437802ae9ff9e41d418c1b8f86d8c0386b17668f8bbbac866565cae278eb10bbd07e61ff88a51f0464ca113af95123b3ce90fff8951cfb429578d3eb727d2c30a919d1b7acf27a98109d81264c9e00aedff9e275d009fba6ea4a756b7aba5d90ab4b2202c801e57f48c6403e007ed5bb269ca23741f16803d23980edd92c343773e47b6de2348e2b0fa79d23c2179862ff338ff57ebf109496134d12fce1d0260b2d129d52da40f55f21b96510a5fdfee264bae0190be6fd34ccfcccb357caf04ba9897ba5a20d2aa9a0eb1f0b0566d2d5601cf9f3ff79f1720583e2c8f8662124bf063e69916b5ce71d016b5202793ccd4a68b47e7cc85308540000749d758d36d00b6d1080e2d7a982046e5cf7d1e366d5d60da505bb2bb6db9b794322519bc0b5b0ba90b40dd923fda2b250840c4a42c09b164a349b102889a14287e76a6c8a43401ba4949e267678b996ce78a26294a86d8c942465b03881d2c966ecca0bb9d3485d1c48de9c2dbe1da2d6493153dd41d2937eb99e07e4527155c0a851b41930a3e2a9a84ac0b9a96749dc735cecb296a1481a684bd9289c6048d8c972af2469342054b7bf064baa562898a15dc1c40a060c9ac99be7b2a4f34a857f144eda5ed2bdff22eaaa2a2089b82cb3e4a5394e05bca143df876150c60e0b529648fc35eda840fb3252c1cad3184c403176f5f22f1900510252b50620225414a643f82df4a707876d087332ad759cf07a6298e363c0ab4e75c82cd9aa89dd9df897e6205ba5724e2561f5b8c9ef3ce8d5a6afdba4374529f74526a9d8e364fa97f4e9d06e9ef649fe3a290f2dc588184d4b80d1c9e039a2f954fb738e76e057ace7ae2119fe7dc863662f286f30e5b6c41dccec7165ba384e47820ee18b6d848582a21247aaec5136ef2861b0095dbf94843f49c8f25143de734445207e639a729923afd9cfb92d491e1688eefc251f51485ee0c21b459383893bd39cad5c059520bc8c9a1b3721874472b64c8a94f6f694a0817f1d9618721a1bc610fe58df5504384801635845d288f3ddfa186dcbb004fc58013f5a87f60832008822c6d0b450db1a2957c3ab554e744296ee07b2a733a38c9f97d3e33847b1dc7d99bd7716847699a456d58cda2a816f796d0678b3a2a54cdd6add1e1563f39f7853370a770c513b533071074a9ff927b61548ed3ae1c20d2f6940ba95b7943290585561c77fbc53c30a96c70a80389f2d4a5d1d3b1c75318d7cd5a7b0a0ad1aa51bbba163cf52a025a133f562adf11fd589586d420c4861076296ea5f665903755488ee7f8642341f5cf4e708254526e3a69d7926afa0a3a3be956f54a3f75e54df5b14e13dcd183f2d551ee41f18ada057a752f4abb3ecebde9abd3eaf40a17d121cfab53a27659af4ea1b4ab739c5bd152114e11abe6b4d453910e89455cea4c6514e82b0dfa8a866c55af59beeaf4f0d56f38872059d553e10c8264556fa9aee21ff3c629cc3150e14caa0e56ffc259a559d5695dd4518aa6cafaadcce4432cafba0db989b1348bc9a659b59b6335abba337d759755f740f688f07116b51c78ca51087c907c88bd4492317dbf449201415685529513744e14286f98a538723f03d883295d4adc769e05013d77cb9e3695cbce602c711ce7c5650e75801eefa9b82242fd472aae888c948a3380dff354521619cd1c002c3064dc3b3b0552a727ed44d099b2b3145741bac10099f96748e50d3768821294a20ed033d61f574474ac881c992292f933806f6708260a5edc9132d8f4a9b3d4a9fdab9590ae279250d291a758be1e3ef4f0c1d2396ba6b83c5d822e3bab6688cbd58334a57409b0668a39e7945d4c950bb5a1387b38404fe5953d421f09f7ab22d655f85929a59f9cd5f3449e24302bce2e6a15a96d166bb6a8733ed28f4efa53047db6a4f770025229ab78e4feb8a21ebe94f2e107e905c81be9349436737613e193f255e9a5dee24db1a0c5651c1d2c9b66499fde2ce949baa77b62a1e374e972ba54c01465900218018569170555dc3072a499dd50661075808109225d061a707e2c3203ce0a2f6538aaf0b2c74bc93544c2fd9c775c59162a6a67f653a7744e59a4f315be9ffaac4f9bfaf81c579d210b4fc3713aa5655cebdd89697c2979d3a9027c15e5b659d27b98e915ebd1130deb7b34b4f993f57e40c398b79e5865ab7ad5400cfd8f84f95f09fd9f09a0174a58cb84d92c59ac162a527953bd76cc226d82e5d3b9f4be095a13fe9b25fc4749789bf7420e05322e03e0bf4efccf86ff79a711fefb6afe0373fc87a2f9af2535f3dfc5f19f8beabf171b32ffc174a10e34d80ec4b08c8dff6ebcfca772f90fc7fd6f26f5df8a06f55f0ef0bf9aefbf1140fb7968ff13bbff00c0fd6753ff23a104d086126602d8ffb558ac962972b5c57ea2bc74d44ba49f273f27b84838c490624d2f916237e94a2f9162472f9d7b8914bbe24f2f91622fa8434688b845c4b5d391ac143cc106c5129f98d8e03abf9932ee78818ed01f6f4cfe49a84f4137d628215f1033ee780a623f059d8498d854b6a4f95ab9c0237e49432fa7e880c9f35f990de8ded4ad5d724a0e62df9eb2d2ae3953b724ee5d4269ca365fa7d96cb507a592886a41f8f6d451bb02f0ed21d3eddcfa780aa2af7b7f96b8ea0924aba9734d48579d35ab9d3a47945a82521f321f41f8007a095d95ba35ab845b54fbf114e5443bd59a053796baf1b3fbc82967cbf301f4239206bd513b608bd7706c098328ddea9f7be3079a9ea25ca268cfd370862a7bd489e84fee85230d7ffa79f6ef939d824e3329c794946f61fa14edbb4508b63f41d6134871bcf9f625be478ef9b878cabf5478639ce7793540443f29db89c9cce714f431579aa2d2ae8e09176fbfd75f78f19b7a118b9efd857b43caef4774830dff662b175efcfa8b5f9733e562435cb9e0e22977f1948fa7a0d3900d678084d9b0e14d6c881768b69a7ff0f37cc9beb1efe51b0a53b714530e22268aee29ca89369e8268a5b19bac5d63aa768388ee0502ef12267ebc42361cbfd9b3a76e29a6540de7afa73c15aab853d029a85f269c664eda254f4646a7a3ef53d0f7e9e809a51f4f51c613ed543bd9be9dab74fcd8a79d6572e425e367bbe2ca45bc21e9d9af084b89ab1752377cb6d9624f891fed12ad78d083d17c39e5f6d30393670b20f1f0040f27e8ee25120f4293879f9797483b1081da00a3b6247131e876a8c222ed1085b7031327ee25d20e4b7c3b240122ed008442f2c182e423860e2e172e545cb1364027605080e46304924f0f374035542721e0f06e33322f9174e862a5c314cf21920e2bc8a18350cdd11ab69897483af430022a0d4fbd44ca810c31a6060000b7b0c1f112298726542f91720841094839fc98605f22e5e0f3ec9d10394c8ec59534f8c1f29f5cd956549a15a671a78faa5ab3648e384f6a1846a100c5ad8eb3e3e64a3b6ecd6a195bcc4b47418a3b6b3a5b13b2703a108528ee748b7229ee17b33365dc2044a09e27f368dcd1937ce9dbe9e71f6aa7b24f54156d747c206a07c65708a881b3bc0df75d1c85c3c5651cf495834e6ff88c735ebf180fd2b3863031de282197551f96af8989d0062704c2d271c369eddae1aa44dc91feac72b6a452d75e171eb716794c1b343034df42f00096dc71c70d86479033cdd7d7245d3bc8903790ae1db766b5d7f9ca01c67690d1b4e3c689636de2a938d65a259237341750a8e5850a62fcf421849fed63bd818489711819991b39dcb881daa98e03b5e339825c99b4284abb5649cd725bb3a8ec70c7afc957b6fae50043236e4dcdc73b4430945d11b523532b6a5647a1d5788d8fe0352e6134a1d3388dc8b37a81c673780ef18655159bb8d3485828f6c0ea374208c6400fbfa60673842e44b3458463ab1638f6557429eaaf51e256e7719bade67173168bc7cd431e4ccd42fb083c9a7ac478dc5a67dc1d37149adc91845b0ea711c1188dd7885fd38e5b4d6dfa11e69bfee542d4ad76a1b9147912e85323ded86ef51a722cc56212d5028ea73ef6ecd45042a35aa8fd3d44c01f799e8a2b1752b01a3ffdc8dfa446e4f1006c07d36c354a015e0772a89d1a988c223c12eafc1caa85160b762805d41f6f78ce3f0fb42805f48f1d78ea5ecf5311f45043bcd3773a9d4e1fd73226798e08f76392df61a559206a88cb0a3b82b04a5ad976a0b1b2edc8b2b23511499dd395a9f1e8b10690cb0fe52cd08fde836381e31a9fa0974c6db6642e4b9e631b1678dc503b3cac34abdd85887dc68c9be367cab82e505ca2b818cd947147f0c7952d478d1f990b5050514e4e8e0f283e5043e8bb10c99bf61d396e163b7618c1e3b6b2edb89d7830b5eb84df71e37165655bb251af3247cdea55154ae323a3d4ac4a65a834ab65929a15448dda8ca74cedd3a132351e4d3c9878dca608c69ad51ec48ceb52e4f3faf99a98ee88f3399cc6c76fb6b2ad92bea69a99322ef51933ee077e90a62b7f0049dcd19364a8c8d496ea4da726e92cdda64f2c265bbafdf8f12387cff9354da09f0efefca414fc59f2354dda57237eb4d99a353e828f20f2c8d91abfa69f5e23f6c0a8387eb59f9e23046353e640503bb53ef1a466f992a32029004384e4b843f2260001f65e95ca9dc5c2c1e99c6f2579237de502093c49180b2f5e007be26e8293e03d309b70e5828db39c25f29420d2e0f28f766667583ebde5322e4ff0187fa1e0302edddae64b874bb74956a95f372edd5669d78b4bb7b7f9dae1d2c5a5db2bfd4ac1a55b2cedba2edd8bcd178e4bf764de4fbf5470e9de9276b5b8742f68be5670e9decc63d22f1e2edd73e211cd570f97ee157951fac5824bf78cbc9a67f3a8f4ebe5d23da576590f0120860000404813e38e012086d2bb8a0c4371d5027fe843f843e91e7a288e2eae88cc5126c67b1ec67bde86f7fc4bec47687542eb23cc09656c0471b435e278a55d89e39d1147150e717416cecb84154bb362424e16724b426ed62ce92f21e7a459d25d42ae28e48c42ce16724a21770bb92b61173b859d17764161c7a459d25f6127d42de9aeb0039a2de92c849d4fb7a4f708b9a6d992ce23e4ac744bfa0a21b7a442c851e996749c90abcd96f414422e4ab7a4ef0839a2d9927e13724cba255d47c805cd96741442eea75bd24f08b9b05ee9967456586fb325a78f76f6d25269d7d84b6f67fde241148bc57070d22f9f2a7be9164abb8afae593a20101ca4bb7346b24ddd6da75d42e79f403a6976e6d36a95f1326ddb373a967ed5ab1ac59524a6f57b7c44b6fa6765d6957f704cbe424226a96749aa03bda9e9f52ba9dd958d968835e3adb9909218f8d2031966cb02437d2a80246829710f28035180f0f15f664c52e9f04f1c98ac57aab569ad52dbdf4e6a10367b162205ee22c346326edce4eda05a55f122699a6c212069a61650aeb2dac569a259d870fd77fb4b397d22d91745b24ddcee6d08d259b6605f149f768c055ec154dc59c2f9194ec80d24ec2461435e093365e3800132cd9c428b77dfec75fd0b66072616e835e38002d884882d9f06b827d62948b1a3279cebab4d455be5d76cfca33f014e10269e58182623218c429ae18457cc165d490c9a114c048208089f0113f2611e339141146e239ef5008609f3570961a384b92af5e032794d060365c8500c6853270c7b158bfe40e6c5cb50862c2434cc45cd52193ba22a038f1a3a4f2b12420d74a45a3020a8ac9aab812c27df52aba0d8f243c2613572db08b37465c765b4b13df2a9c33e1d25de187a525f0eb51e5caa327464f8c4ede67a3c675273894b823d398496b882b8f9c14fd74db9170c5d010576fc6b8f2e8c9cf931f4ac6a52e8f9c30fd7494d3e9d2579f4bd84c010e500b8bc0608ba9208c0cee8c339398a2cb932409b60ac39533ca013c045bcdd74a5c5167d88cc8c3b0199f0955f3fe5c9610112ef59199f053f99f0c282353652a088220f8e1f4c091ec24c504bf937ecd783b177d0ce555322ad50d99be4d5dab6ec8342d7d83f1ed174bcbc8c8c88c48b805d0813ce8f2290e521098dc7e7a60010ef206409401032d7c482376052440b36c49b4dab3ed0b79a8594ea8cbead2002528d78b4f717cf1396d0b659acca7bc59ea5c87b161c36128c501c625bb4aa57215fbe4c9939d3ffef863ff18649041b07265675a930c93cc4d46e6c68d1bb48e795185a34bb9f4238f3cf26ce947a6c18833340e6f836dd8105fc20ef0342ba88c3bb2cfc8414640ccdeb5783b07499d21f2f98756bc0294e95f480392197e9e77c86eca53620c237be74e85985ae2c4e10c8dc39fc2b17178b6f6de3943e3f01fe8331cad4f2c89a622987eb8713a2ce540535223c24645908483fbc01a82b9fb6ae0f9d3e9e421a97ff2503e3b69d65019d79fc98e1e6e0c566e0d5768d2d3840438b0810850b04609964820044c5c590ac20c8638818c01102fb5cb0a12bc8444d183444b55fcc84943cd44273369d7500ced5a19a1802d8956b372d957a184cd8412a66a1594fcb237b79892e28e94b2f5da619123d4bd3a5b9b163d8ba422740214d8a0a90a170c414889357c92d8210536d0e2b3d96ec0d8041288fc3c36e7b6ab7f54f211ea421595e611faa3b4b1ec9622a3fd51da8ab80043bb58cc91556c9ac51d8e2ccf47b72ea7ca7a4d5939cd92dec3e78b36dba7f747a72b604121a0a48c2742a0240c2b4c4029c20a7eb81d3103202b174805ec9f6490ad348b995c76f93d3f4ad740cf374761669e4bf366639f494c1dc43f658d160588090b17fcc84253d4c0e1471e3ac222e84786c2453f696c549bd987f4f38fea2cebe3a7cfd1a2e0833b32104399aaa31ee2c8d2d1b0e946e6f4186808228fae70f2f2e88a297e3a015cdec64f2fc064a6729374d919887f54ef17fb3011fc82716bfae9f4f23233238a279c2069413885397dc642eda23e819e82764e16a5cd1212df11e9eda18dc55e86aba719dd24dd768ef10f9a51bf90c823f6d3e9e5656646144f3881711807878611046ad7a438cc62db17b4564ad33db83c71980892564ea4e92aca6e8d9686a600a27422f24705640aedd3e58f34536a3fd250a680bc6977a571e7d1335f7b8511480419ad1124248b1a8e35ba19317172707ae4684113a245ed1a515952a3194929424890901a13c8195d9a9753b0b0c086e36cbf5e4ec1b245930d0375c30546b3d016c10a2c576c3da44e1b0151bde42b5b278e1512b9278feb6c273b9690fe4edeb4b3e0c4651fbb97ac5aa7d34bc34f9f20929fb47680290f6511f41af7c80a2b3f761063e986b822b4867cf16d3216a9c3de8cc6cf0edffedde66c46e3db3f21d3a588e38c4bbdb2d84143b8541c9174f755491b13e2556fbda3cc313ccff06c257731587e4fc480ea5994a173e698c5917d9c41f59e739e385a6bad373a11d58f453c1103aa1f63782acac0eef918c37be20c2ad0db31026d1f559e739c2855b26fd660430d32cb4b97547e48f7229ff048daf9670bf5bb45fb4cc327bb99c526d24f4d272c39dc4ee5cdf495d2ad3ed299ffe25620b073fe1b5503bfe735fc3e47ead4e93ee4ab8a215bd359ac30c4e1e27190bc996e0308b77dac5b481d1beed7f847f5e9758ad439ea17e873ce4ae5a7abe60b146bad265eefbcd66a35d5f472e6abdeeaadde6e95e9a74b7913ae8a9c722609cfdc1f9d5d7b3b38cb19246682aef41134c027bf94e79d3c3f799f943afc7521ac0b6d4fd44e0d9ce5bb29694b9de069a5740489af8a4cef812581754ec386d9d006fb5dd88921c33ad1c2aad864e5c284519f302af224814db1a74bd23dadc8a46be4d2d8b4e04e8761beacbd37c9936412eb8bc028b9238d7d0983bc19e2b2772f25772d630f0cecb7ab7cd2667cb1bde7dfc46996c79a2dcf69e8b3e57d2ba5dbf9c73368be6e609ecf99ece9a47d9c50a2bce73345dc2ad42e947ba07b3e99b4eb73cf49bb4eeef924923af23d8771e28ed25664bea464cf6998afe99eb5f7769e37ddc56fa07efd86e92ecece0569cfa5f30c92375e8bdf709dfa0d2e3e9da9bb70fdc5a9b7882eb8d8f0c9b6667954dee379e2c28bdb10795ec43b5b9e7b4f5c78e17902fa8bf8e4b4d42ccf41efc2fa12d5022b35cbf313ea25ea34839ae535d37b1ef37cca3c6f26cfbbc9732e4485e017b2986790d2055fde50c25c42099b45b3e5f90d574af7e4fcb3f67dd0f4dcda7b552a77d60c7a8f8be6db00c2ad2e6d3c518617dbc908a5db399c0fd96a02b4c762b12730ce83b4abfa91a4c2398c1397f3ca39ce7c4df738ce47d67339f286a3e2aa7309ebc42673a574fb2508b7a698be4a27372b9db3e76cc9296be9a49c19dc934e4b6b4f59294739dac9abea2aa54b7cd3b2a4b49bb3ab9474ce39e7b474ce39e7eccead7c714ec520b3259db356ce39e714294dfaf8f45e27b99f21ad9ccefb78b9bea3dd7e7aafa3dc77684f56367b4450caf381dc92a2f44ae6b88a83d3230785c3f3a1bc3450ea469ce3ea1057873c1411fa1e191ff7a495f3c09377929fbdac0a820fec644b6d49d1d4ad41e6cbba6c97afcb72d93838383d725038382a481665a1859fa16c51ef401411aac25305dce7fc9e5c5c503bd55fc091a6734927e75d930b13aa28a59e16358cdbbd3caaa20aa46b5f1e5521c507c5055f1e55c1021754c1e45e6bc10b230225282d665a0ca56e1543f8006251d452050f3f7cb5194a0a16252759b7e45b0f768a5bdc597b044eafcda1d6fab55d510349e79cf40b07874be26c1d5027fbcec9b7774cae7c940ecab777b376d574419dcfb777b11260c9c12d714735317e8cbeddc9b4cd9a2cd6b4e89b367d5326a2a1afb7bac4cdb820ce6756a3fab344850a4da66f9f55661137c4011581fbf9a661aa4a494c3ed6a3ef995af4ed7506060e558d7d5bb98561821b3e52255bbb646adf3e522861b4d3a076c18c1408cbb7cf26267c9c56be7d2eb5ebc567d2a47db1868b8f138ad20fdf36394f0091b67f40956f1a5af07d45b684856661dcf8919d7c3ba91d35956f4e62c2553e1fb079a046aa9ba8a85da836b24dd15498fa4a63910d7dfb387f7e851fe79535d93483292668da41e90627e802f62384ada93603201df45027c971027d8f0a9238030c28b418a3960556e334a89d1a9ac357a89d1c545a4e8ac59ec667503b34145c5159395ddd72f8d5ea46c4afc270c1af1c076a67e51ed1cfb80ab53383a3e971f80dd40e0e95cba0765437a8f81b1e83dab951d36882c9c8380c6a47e68a1f0306101fe336503b313030187f41edc05c2cdec61750bc0d7741edd8b02f7e513b2f38dd72b1810b172e2e24bc8b8b5f1f9cf12eaeaa82f42eee5f08bd8b4b19bc8ba7503b2eac1d4bee28b1b89706f706e002f9b1c75f974afe3a6d978fbf77c98abf5e04fdf542087f8f7ac082bf3ec0e1afb7a0766e4eca51a89d548bcb167c8b83a89d16947fa81d9405611e1c691e74daae9b07c11dace04157f140870775508407fd84da019dfee7a753167fda39dd9a2a66ef7987daf128b53a58f256cadb5b513b9d52d053cf01197cc4cf49bb2bfa1aaea181e7e0a870708a7c98b9740926a2c4a04e72c78f344f9d52b74e3cf59b832970a0b443fd061183303f7ea4b1d1d123a9881c4944375072031c90e0dbbf9c2c35b51f75d020043938a2c1951a1071540320d860ca370e13321c0c148bc57ca09452974775f0230ccd833fdae8c081ac41007e944634198cf1edaa2eb2f8769fc1ecdb593370f2ed3954a828c940055dbb4209941a6c01c60e52469083e88927b448220d2564a8e405a3a8e9f610e28e2e039f123a5577510f74f2093fc23ce5f194fac581ad491a36e0020a8dad553268dc91ba0d4c0c90beb9e042e85b65801b1223b035894959e28812475c2102880ba618ac5994ad700289ee6ea9d3473180c1534b290b822ae59fe04a070a476a7cbbcaf6ed8e4486eddb250e4c199ffcc0656f95ac4c8e6adf055042a68410d7780bf812a89f1ae9ef10e6de7e9e1c63c078927031d8f5242ac09ea09c874e61410d6eb02430473b803d697724dcd7d8388e6327a1f5c47efa749e47f94c350a154a9844a18c748b7da9fe6783194fc162413b81548b73ff004243b3d8a768648618422c965a88d641940ab0a4a221800b682a20fc43023205f609c81be66eefa779760ab48b3d15caa316287d0ced72ff8fc1499baf6062662766283133731b69177b100a4cc19ad387cc9f2821fc3d9a85d32c66d903a40e09cfce321fd83bf9178487903f1ec00c45ea8c5cc4b784ec2806e21f19c7e40dbf78493f807f300eec44263113697b1de40dff27a4ca1ba681bd03ec4662105f706932694091f2c60647e56a77d7e6222662286c9b2d9ac553a649b642beacbff094a7e2a4cd1675eacddd9304a0a269ebc9362baa95052795698bf2e3ac629999278d326a487d8e226fa88b34b8cd3463b59acd96943459dce2b2e3a03e6df3b500d9a24e5b9c494ffdabab5adb8688526aa5a9e929a594ce504abdf69c3419d0a4b56b751f867d28c79ef2929b44ab519f73ce22f3e5943abb8a663281617c3c69ed2c75aea2fcba962cee031c5318538ec254aa2954f7ba4c8562cb09bf420250e10b38234640b6aa9f4e44ee1379f9596154e4e24ee77ae58fd09f35512e6a67825fedd0f824c78923128ee3388eab5cd36e66912709ac26e16e223a9de9d2b6fc90a2b5f7aa544e6752ae3caf63ea5738b33e16c80a11c18893592656f6bce439e899879eddab9393b56bf49a80da15d4aef1e4f33edccf13324ee899e98ad8c3aded9adf577d7c0ee83374819e9d135667b2b17bd1d86c55f983fe48396d36896f93c97ff8870e8eb5f8f38cd33af84777a28ee79367c38ea98f54918b3b764c74a85b4c4b204f206f78564104a00e4bbf28d06ca16186fc41853805a6765de99a96dab542397f3d859e9f1c155e3a9b2d8e8ab752e95757a444ab52b362bbf2dc25b17b430f86de9257a559ddd26c7167a58a9c387312472bebc2aea9bb226f3a2cddb2a2d75979066a21fec1f9700aecdc5c399f672a94fa19a6beb95addd66abdbbd231713e2d22162bfc638a9d193205761f34e40dfbc8f120764c5b5ccfc7934fd3b39f7cda253bfd784dedc2d2ae239d23b1a718d31c4172555812f3420e089902bb0d39a090fbe19670419c50b338213859b3788526aeb3db9895754c5dd3e9c6111ffb578015d092246576427465b674615e702990460c3e8c481d1b462420a518529490f1fc35536e170145f1526a944ab79416202bdda464eb66a0a96a6aa2b3900ed16894679a486794ce26ed59088e9f744667740929e9ac9d0e499d2e92375152229dd15913a944462d40a4820757e6f43175ab461c6ac8e7f3a5381ef93ec2cf40f3957220e940b439345f29d148f751ea471e62252c4052cc39a7d4e9170f4d289ec643f3454bb0042582e2d91b2504c7b74867f2865dac420da59f6217358b3de512f50285a53a8cf98e42722d5207e5ec7dd42f3ab32707dd480a355f1d4557514d254a44e5d93b514a299f28e58eb5d6aea37655a319561acc4b548b358a91f9eaa2ae89b46b4402ec6c44eaac7458b9239de1f8fbcd4db17b552a77d6d746c8a533a018b9de84e35b6ca29a2ceec8b416e794b3b352bffaa85bf39512bb688aa34a1c6f0f1d3a88ae55dd1651a4e28e4094465035fab1a32c1d9d51063731db19bb317879840653d14c1ea971f4dcdf519ebdbb66cae5b8a276a4caa5013f444d4c673ce3389e4c9a25b53083e965f8f2144544fa48a51667e4f032accf280448d690cdcba334869e51280548ef161fd02cf61a86f4b713a1dfedb24318e40d3ba594555087508536ad543450544444fd92621419b54bcaa3294bf8979e6fcf2e67cfda3532ed87dac52b960ecaa48be2479045ab111535cd574bc320755a5c09e1dc864712dec55db5f3113ff2113f7d9512795868066b11613d709a4544ab1109cd60ec41dac5473c6f01e5a5adda7808e1b3fda29667d8e36d288fa630f9ea759ded746a5eeac09158c0d0220a18e860c8033a4aa45d77332a4c428fb8c1500e86497298c104067a906631019ac50ee40342443f4cc1848a2647c098032418ea410553a668620918337b8d15770442009c2646fb643a183ee9fcb361ce27830148ce1066418058b1484d15362c1dedea2923826b5d2689eba9429a5311a68f4058456e9e73115dd18fe07b27ffc2cebd93b86a61bee743e67beed95015bbd3e57f4fbc3c3a22fafed17f0402839132849e9f1d880bcc323edce9591fd26f1130550790a8163ca2b722b30e34ae8cd175184a781b4a1a17b4b7e298e4adcf39e79c6ebb2566e28bef4456b3d88688570c16da4bc2d8e9f41b87a1c50a07d2acae11bae395f5b832a9c38ec330489dfa752584eb6fa78053b73a42989b8491183e697bf1132218e4cf6fe71142510f1f7a74f4236df226afc8a6d8947c34ea18af6ca5a269560e1b89a1080c0768d7ca6d8cc4500406a903a793c1d24b9731ba3712dcc6a57f9452fe611342acde40696b2fadeda54d7ae9947b1231c588dab5b2d58a84e247f02b163b3323a275912b4abb5ab44c8824936649918b7b032218dc6024780fec7a4d9a2de9d683f48fb0d650f2e4724279d0bfa176d12174c60677fa0ca687cbceb59670ac4b2f1d158ef5080d8fd01f12db89d84335585c96f7248e57e589edb2b24a1366c8beaf41464d917dc86835188d0f301bef819970040e110efde1b2ebab678e98a0b46727131b4f76d168eda241586242e5a5cbd1b2357ae9d966d2ae713a79e97205c303ba226452d36f1f42bf390b138cb846664bba0d1177d5fec37b60ab16570ed0711fee428eff10799eb4e7788b2ee8b80ff721f2e8780f6cc987fff0b163b37691f0d2adec588c65612cca04ddd96c49b7012cf3b28a27641006a770a04073c987443501aa8904a1a06812c98322098a2fb80a28cce8bc18cff8803a01b5445c5469f60aa48e0a97032fc6281282b44ae40b114ca893289c60298c5050a0381dbdd480edad638a020d49636c7da86cd992ce895554c5bc94c10c963c8be3fddbcb2aa430e247ff8e5508820090621004e3b1518c8660497c28018410409d1558b104d3220b1741302e8a74b8c1b0c4e08c2558873850609ddcc1113cb8c13aa4f121059f942e4a67ff6879849f4ae9b3a5944e45ca3c7bb096ebb84ae7b4e14cdb4958e79d8475a15fd6b7e7d65ff06ce793569f4ece16e75205f74a67c7b36b77f7cea50e4efde193364faa4bffbe1a3ee9bc864788d0efba9025ab82a0fe3c72f2ea1f47c4fbc9b2c579e51ab5b35a00e89fbfe02f9c3e8c5b836cf25cdebe07d6036bb7a7d0e7e437f4f95cced603e1d6209b1ec9942a74f30b25ec14da70a25ae0f1b6a59413d5e28936f4a92dc771734ecb71f3642dd755af539e4c7fc23ed932e57c56e7421f4e85f62961954e2adad033cb9c9653686f94b5764e3b7520ca233e395f4fe84743fe279cd3f0c8c78544ead3d005962d8f03deb7c3207b5243cecb8064fe74d6f7c0ac531491cec591abdbd0c73a0f4f6ac879245305aea74349c331c9886ac146d79629dad0dff9cc92a5a3b47b427b9a0e4169486560a73f82dd0c7d4e521b5a03f3d95be8ef5644e6773ebf73ce2702384e61649681bdabe2103fc24f19cb5319aab338d66f7188d409d168018bf4ebe9f38f58a9431b011ca7d0f4a3201fe11f394eead01ff29b2915abbc69afb55d934e2ab264d5dace36b44ca136536a4353dad350502a44459f22c29a2805f0c8fa3324422d8b473ac911a1dff927398a2ba9100dfdf41b26aa85ea53a4cca184f44cef50be9ded9c28a902aa4f47c0c81d4f1c6495a75fadb82a18fed1b19f16f54501267d40bf80746b3aab5b4c439c664d2041d806a72888055924e1822eaea896b8a3cd4ff9853c4de36c51851faa28510ce8207386297b18fec1ff5f7cd2e653b5cbb905e1b35f15123fb6162da039fa9635544f69c3a838ce30653f6b70e5d7265205202ebf2c6c2eb1a045c62465cfa21cef8bf3e0b0272e3ee27c4c4ce7323eaa3ec63f86f5313a3e06c6677cccf918496b8939fa989898182b8fe3468c4bfe91f218717459f94bd864756bb2729aa6679f59c1303d4b988d2acffe727b76172b77291544d1b3b7c49e1de5f3ec60d0b37f42cf7e9a3dbbc764e8d971c884b8e1356c526b72c355611049cf2ec39ded654209e36a4741d09ebd659fa4eb89692e069fe41ffc83e573884482d823616e011c4ad8a40e1b22ebaa487b0f2c090cf64db0869c68e50deca373d2d9c28956c2a8d864fa84cd9e49ba1b053c38725082192c092705f183046c7a873cdd0e60496056009b229308b034c1b62841900cc6c3570c61060c6049629ec064b33082ecc410c6a0c13a5c553182713a1bb290048c474a51c389125892ef0651c06cb3f06ebd1379748e502308968453c304b04e0a11866ab0245d0dce8075a26c167a602b172c0b49c0ac68c5d50b9d7bded9d50bb6734fb49ea46259c35efb206a87a3576e15693a950d67175a912767c91342b0249d14b0ce7b60cd6a16a67b9d22d8839b8e2b450ff35dbb82f1c92124256cede291f5c1a884516e211f8c0be56c75e0dbb977a60c48e6afa648a45d039f0c8d647e8b2b22dea20df36590cd923839b776b2a351a96c5bcf9eacb5765a18ef5e0fa4e049ea506fce3b799ee78122b537fd4f4fa75927451199222dc1c9632b032a69542aae61eef591ce7f9dbcbbd5e0b4943a379f7e953ad2ab92ce9652b2d7c3c82fdbf648fabd6ffbf46f7a336cf68f63b17fb66493171ac6a29cadf6e25b29f1ee6e969205194813a8eeb4b32574db3b1858df8939dd6acfc9c949e24497d09c3ddb324d27509245106c50de345869f8a53ccff321b2869d0c4723d6b9e93535f9258a6df0cd2ad39bdef4e6c9e769ce53cf13cac7fba7132af47a69fae9743a8d343fbd0045fa4503e87382e275024c7082d46bade2e7ec39ed3cb7968a2050bd22e8819eaa747047d5d34061980e8a2390a43bde977d7bd0de1bf652bf823aa791edc2abe5255bc44b0095f4500690cf89e07b3d3bd07bde32178e3e72de8527ff3c48f7fca96ddf7990693b09d4c52673489ef32f65802f253df7fc74623eb1683d4983e63c9d4e3e4486dc29b461e7941ec12950e7c2b1959e36aa793e2133fa1194e2adac05cf7e816fd29e7378cb56056121d0c9cb88d2284789a588ac49dae3348e9e69c931e268659e6d88a3eaf9451cfdd9451c59cf37258e393d6be19a7655cefa5c8a4b333667f3e5a2ad228d18fa4a1ced8c385ec7218aae0a672eaaa4f352cfa4df10472b8e37461c55d261c4d16d8823eb451c715caef494385bc4594dd834fe79e9a038e6f88c38585857dc8aa3aa6b5a4d75e5cc1172d36c49544f1a4d83b3a08ff3393b9fdcaa76d1cc4e1ce90ccdb5767d4a89007183643606ca745ba2485de55cb670584d0401c9624544433c4bb2d568f2559d631a2eacc9d9d3cd459f7cd938fcf41936c3be992b57baecbe507655cddd1305a203d4826c966a30f831e7698badca8fd2a8369354a452d3b02c70e551164a3f9d8a2312fa7532285a2bf3823bead061d3604c135a66edbd23d39666cc331396507fbbb345fed1849f53ca9453024d2a9fb481d1e2b6ab6fca7e608b901180b1f47dc3afa82153861863e9142269b0599ea7c2d15b2cdf68419a6f53769bcd21293e889283a774064e868a8882868664440c54d4358e0d2db58c16d404c4746b4a62baddf85603b205258d3da3599beac4b75bbbdaaf53e71b4a295750f9c254d8e21f056f88029da2860c6104607871274a8810037043d8a10a18dffe41662a1c99360486a30c4f2192f644292b118a3f293eb6c22806394c390a41d87405165c6451cb814970fbe5d11a3e9c452d070120c57e62b12c66bc952299dd91b403075618c54009e88ef46733c126f8388b5a0e3b6e00341365b22865a1f2f2288bd18ff4b3c0e0b99747598a5e8b2dba60fe2173f2124926241b42f6d34396670727f07113179481aa0633f0c9ea02360be63b8232460bd2cf0fd20f6820fde0031b4cf9810aa41fa020fdc004e90724907ef0415272e5680d19d2cfede5142f925efef4e0655e22fd1cc5b8b86c6b6a77c539fbc8ec3c7e82a6d3e4cc17ab5b2b3431e784992c9bf3e7688da0771962bc2f7b90bd44e2a1e9a5df78c8c173043e6953b9ce7aa70f44b5a4ae0b3b8cc388e38bb3b82202e3ec2c8e361cc6639c07f462c6884fda25b530a14b78433642b19f3cdb711cc5a5271adf8a572c828e12794011e529e7f944d05bbc07f6f9f51e9864b2b33583e5d2349a6012f664ecf1956b0969e0d8fc5afc890ba827ab057c0efa93aefbc41cd6e7a3ea1347d6d7451040a0c560125680d9a2ae23bb9daf3c97309e2772b64ee2ca138bf8d0305b54882c5960a0c8f3891c9b2d1d145c0993556012d6a3c407b5a51a173315962431e7035315ea2ca34e03f50e28c142d281322ecf382bbeafe605352f98f4c5d59bb2b325bd86d37954fd9c5f3384edeed6690780bc912c6f6c144b1cc74d8e72955bd55afb25cf49a728847fcef95f0d226fda69a5d5b39fe385416ccfeab35269d9aa58f3053a4e90db5f18e45a00481df6d5db7839654b1d2a049574f9e5b36f07710bebc4d172a2e46ea16290ae05fa53e4a48e1764896bd4093a5bab156557abcfc9d54a75d8d89871696c542450b528c005c4515bd44f2175e1d93329d8ac5b73c4ad36055ec775371a3f68d2831fccec123f08c2c1f98192205a5a6aad38e99684d22d6b254ab7e48f770c2b44b059dc96974756c8e83a8f03c28a0e829059e9e287242b4641dcac3c116465760491151f9010e2031b19690821048c2232c210e2c58d8c192c899131c5114464b04008213da8916144088454591a238d248ac6581281902f646324755d87746dbc3c12630d31d0f0acdc1c2f8fc4c04197c65dbd3c12838acf8b9b7a792406918e9753ac8cc1716af6491b9ca9f2a99ab2ade470323333b3b5ac21b822d53f0b82ffc56526aed5a60da90aa894add4954d2c15a21101000000009314002028140e878442a1502c1e0983a67b14000d849e487e56190b942488510a194300018000200400460006464c1300888517a3e66db21ec41b12d1b522b31ccc48973e666ae4411df104f1540d4a45ddd62aadaaad099a605449556d95a32af669d2512d70a04ac7de8ecd48275668daf4ea404931aab268587520bed8cbba162b292f95ad772c633d495951b4b54755ecc91cacb84f938e6a08a76a8aa76a532ecab610b5c140946bb75273d24b7c231d18e5647e94c281aae228798fa8642d187628a23e2ddfaa698f1436b6ca4d4ae9d88c7b2ab9011c9ab7c746d2842165a564eb0ecb784f88836a9a9252a9523bae514fe0b08c7e823e60a605cb0d723304880e2f1aecfb409402b8763d793349f5187ddbf390151054b17f57a647baf31003f89e9720bcc8a162fcc53ca44c0bb8b050fccc9dc0ef666fe11ed00f2a051200deb32994f73bbe9455380dad85af97d3e09f00fbd6aee9e3d0ea0848684c99ab71a7598513f395dfbe2f73b288d0362f4855579eb965e710b86cb744be0a1e88d631de9c78d92521907c2739bc3cc9b0d197707e2cdd6c68953579590a816825230ad587f7158243e14db61d4ff132cac32476e734b78621c6862dbf8b0c2262ab718766c4e23ec9f1293c520069d9576226bb92b24500c3e8ac6582b445caa26139df6998286d102c2a7264357dc5df9130c3d0d982780b2cb89de192be91c5df18c65bd39d0e6c8af160fb000eb2e7371289cf161f864ea8977037058063acbfa2f624f641ac7cd5e599203660a449a3d0358fc21526a768a31402bb384dc51090ccdfc77db5bac80902d68a7d8f05414c207a027d5b1ff0dcd4a57e4550c3617c9996b5639a3ab414ea0c0910054a6e6373a7b77afb9745921dcd784790401ff8c841be48a58b136bb2a41c6956b40a258b6628cd8c30bc701ed45baf74267e7e515ca5d67b28e7604d147da84b7dafe3976f06065f98b242cc6a29a53836b4e763753b6783269ffbe9dcbee847b36a009b02a676990dcf93876a4c99cfebba9f6c71598810f1a648cbc483c48c442dc4d6427b8c6d112a831a9ac1bb90f1d66f2a842d8f0996aa24798af4318fbe35857d39b1694bc65662d828a23f29e9b40e1e67d48b869f5082267542cb55b014c7786894cca042f9600b5d9d8fe5a7ddf9b89abd72d480aecef6fb9d19e9cbe2f7adffd261e9143cf600f137117bccefef1f8545f28d19130f01a056cbee1428b3bf86b82412a8e0a50d339fa302ad8c846df9a3910ae21e9ed46ddb20242f060e23257c355d860dfa926007d187da0647f65a7b446016344203c352b20848dd165711aeabc78470c8bc3874d56912b001cf6d166271237768ce49f59d5a1394069388cb191d2d1f6cead1d9e8e49c951aaa5e68eab198d19970ce5dde9036a80102dc7f1226735bba63cee01a1acd84099c87b58d19fc04d9e98de96af2b5f405b82f2b4e49fc9b05351e9c72fa95e6ff6d45d7bf0a3e582ff60aa4a25709c8ebca78b760a21c28ce72192bc3a26461a52d1f112de3ead163fca7a1b8eae4b8062456b40f32cb7fdbcbc9300fd80746a4ac30e39f3f57fa2d7318195e5bec37daf2f1cb8c9a3036fd5183766b231f4103c2b0efa20ed0b8f7e91687982ca4fffddadc2abc5a012897673f229a4d2fe3635df3b9930dc81791d79a6353481619f245e0c5d87fcc19d6b49b56ca65d9c3f793554bc7e3d39aaba8baf708fc7083cb74be4a1feaa74ed14fe604d3d975dd9b8528f892b3514798e54a0d15878c4a0a4f98840ca649131a77c415976232d0ed6939fd342dddb83ce940467d32d1bae117f9d032371adaaf5fe3136ad41de9a7ef69c4cf40839e961a9a1c20b7ec80bb7265bef11bbb4246d725c1ea27d4ab9e6cc2ccf8219d2a380bd7205808393f1ecde0b7b69e4531e91c3ac74871d236a45b658fb942ea6bcc2b144b46a62c8c04cf07fa16595695ef997c314ad8165539f00235930fd4eac972e8d9111f65d96654b649a4ea9d3a80cc8ccac07447339e85d0368b8239020939361b67dbaa8b8b8945a0d8300d8dec0848c5277593e09ba733638e16dd8e9fe1c648dd980a323788895b6358091929e45fa6b4a091b27dfe98639dd201308ded299ff83cacc45ca32c2f40fb0864df653867e730f6913901edb30d979228885eaa174fad6f6ae785993606acf7008d5e9840e2b63fdeed90b6bc6244f206c3bd5b8dc6a219616a23ba5ba86bfb96b5f92687c6404add9ed413885cedba61a1c2292e6397ba582ea393d88b42449c3092dacf7293180a8d8cdc35619e45f18741d8bd40d76e215417e24f532b6c7517ef2cb944d2c297836a4bedf5c7297254207dd560aafc07486a33d864941da2a228462b05efe0251da0439ba712a3cbdb91f217213dc19f461f04ed2843ea41f0ea7f6e41688eeb528343f1ace2db11d094dff3e08861b22ca8b88843d79af0f9870e6230727d92a451546fd97599166ca560b77a0e8c0c7d05a1de3004d8053770b9efc3904ab606005e07610f7d0c3f27343f4b71baa7a6b860592515ee85d7ea262960d0ec568890a0ca4094f54ced73ea73386475d2dfab8c60d37d257c54505ab3c89b0068283eae1b2248c401684b2fb931cec5ad90528754c1d51079d1538954b9128c56490a35ff7d3094ad88c0a0d1eed21f25e911740baefe18ee0f245a185aabff10d5a39ca49cf91bbad27dc4f054204f4ad8cb87de799fef1553f997456670172f2c2b1ec72092509b930b91c3b5097c75ef44851831f434a4ca6d0c10b48fc178282ee30faa5e765b13e073cf191f9542909433401365c6ec3ef996b2749fb7bac5a61a0828d15f020e22f266b860e015630ce0000c0448ec83b756135c305b81901fa9d64eb16ac927b3af19d3a554fe4e0ddbc862e40ff739b8a44ddcb6cd3b2744ad2572bbdd5e5d6987281ea64bbc0b1334500af3e6037880e7ceeccd6267d1206394314d224e4290b3eeafbf2fad7c578e827b72d9be48743c2ddb72b33ea2eb686ea369200b57d84ad53369098de484e3b5a6ca18d6d60ab7d8b97077c45728d816c211bdb1936ed19bb8b96bbfedb9670c9fc6902c026dc61790a280b7d145134f30ad90be2129a9e365f6636358d7793ca1f0105a3f136920adb9fbc6fd09a95795d19a7b083aabeacbd5812ea5a98baf110a90fc94425ecacbe4ffe18dcc2f044fb01bd02feb8036e52910e0fbfe5fc82bcbd72babd90284936b4687b2aca12dcb8a02bfbcc2a24b056c5ab8c65b4f3103dd85c2b7c89ba53e13d713e832099b5e8dc78dea6fdbd57508ef8d32874fb7b4848d6e83ad496159e33143bfd09df877b48cc8e97e389760f3d53e50b74b1004f18a190b691186351c1cdfcb40568e1f0ad05671e9fbb632761cff1a3df281d5b057fd822de56a80abf6c9b4ab2fc2d8600a9a5fc7f7e406581f13fe401e14eb4f80e6086dd703d62ad68bd3b41603acf3beaee98f2f7de55972a65d5be1dce18aa53b04b7d2a0a7c0e8bacc5d68be11955d12e6c1a4123706b370fa8ff659192ad7c817253034b99db8ac123129e17f5ff8af652b7e180ee53a532cab5d6547f84c928c2efc8c16afe9a10b3605de247603f81a06486e21e359fc29b8179f6243a237c42f1ba8d6c3ccbf1fbdd92c0db4a065e639b5fdeb19ae56ed0fe66ba7b4900b97336a767936a8d880f6366cd3645bd3e572bfeb2c861ec4b1c94fde4da41888be0e3e17b70c4c0ddf50298c4744bc9fb0f2de4091c060e1018501c4470afb7a027a6c96012d9c2da20a989fe9839e7b1e831711d00b4bdbd5fedac2239bba70d6027d66a1fc264a14228ef4e3646093434aa91e1b812f0e492b195d8c410d428bc81db328cc1d8f971e2838406dc33cf9aed5a03652cd66284728b66dfc5c0cf6c5e59d660d00228fc1fc5c6ecd4d0396540ca0f0cf4ec57f44554274b89873418485d01a1cbe605de1039d28d01caffc43c575a5d556ab5143fce22d1b526a272800daeed621df2fe76725b73eb8c5c3135b045526bf84cab024fc8bffccdd51193b5bf0dd0a1fdeb1f9df53e7204d7c72abfb44079fd0d8bda0b2d3fea91f41939fdf19f0ab15d3cd416c0c7d756a68ab01afeb5b0bb7840ea4aabaec99aabc444bab0a65750676229a19bba05188accbbce6923ce372d421909902a7542339354aa8fdd104a880b2a60963460aa6771e9f4931fb6aa128e9e295de8aecbf0f359d0d5dba0790e377cac700236299ee396bef6b280a95dc2378842b099c180834237b823ae50eed35eeffb52f206a50ce9ebd62478c72c7e904b7e1a6123991c1c4d9cc07b580924774e958768767bb131cc02827bca6ae60d1d578d6d4c9c5d27b7cc3c8aa0190de5939d145b82e6fdb65a013494d6b5da9ce3af4c15932a141623ab7cb1a6ce846d852730d8b307bb3d9116c22df16d3006345880d8d249d823dae98aae5c20fda7f19830510163c18a30ef56c5f5101fb9f097022004024e624f598b50e48b500dc09484918cc44dc21f84e367d96d0d87632edf7490c221680cbff0db74f1283fed42cca94a8375d940c227261085429aff708a04b59247095fe91ded7ad5b601ead6cf3c151e7b96bbeddeed96e1be4b114dd4a1b5324c35b940ca267992d1aa8b2fadefa714e4f5f2d5b28227d8bc10215e60d59dc8e5ade6f2b1ddfe5f5609c7684f979c6aaca2ed1cbf4f01ec29cd3600509c97ebf5269ba05c6404188cb3e07deb9cbe574ab782a9201acf306dd68463d2fcc6aa2758d72ba9c29d39845b9ddb4569c4b804b2a0881a7947b6a0adcf425ce244ab08e41d53a9ed47f68ff61e806c0391fd35d15872f0574fef71030f40c2b312c6161f451a1420a11e42d8433906a4da6a02356c75de7e6296095c519cf317b89b99bdaf99d734971e12e18725c9266cab5c86fd05be0954aa8a38d247d03161954e31f81d645b85a9e94f527d34c43b0665e7bbe9f5cf2173a81f2d0628e429f080a96c9210a6700ec548aa9f4547c31f59bec4aa30afa4decbb856ad1839a29440f67809fa2c1160f0a780b37ee17144ebfb2fc5fd5cfb5a0e64e90551f5a7036b8426872d34d089c521ff84c7284276ee01b1dc0d4f306c2c65fe020c11af1a67a372828c98d375cca15c2373480aa59288f11c099816a1c8b94738370982c6c47a6ef30a2f1179fe0369a48c35c227d97b12b61225e74396a4945c60cf9f55bf241d1c0d4ae965e0974bf68c0044634ec220cd79e0fc1a8052ee00a0361f3babcbfef0b420a15d408d9606a8bcd37adb6db1742ff0cdcbf09441b8aff167a9c0570211ad7a6001f12fe8aba55772bb89962c0403cea22ca928a0f3253a45ae78160854d59df5b4d35cad9ffe6ef1894040f6a1900b583a9e82afcc34738c70349bba2ac2372ac61e0f7574c947548c7c39cdc6818d03badf6cc7bc961766eaef1addba5b0f360892166c9787d3c453f0f22549e35d96a5572a0baf046fa31229f0acbae46f5b27bc60157fc889f5fecb13a6a36e258af987ff01651b5bc245b76bd3800dc7fc5af21dbd1e8034651edeee084d15158cff786f715153c0d04a5c3f2b291254c1d40254557742f06bc3ebe57edb90d1c34d5e238580c55521e7ef7775b7b90c15dcb741b30d40c519e10583cc7aab1415ab10b11842e0b0f5cea1dae18ec130a144198f1f333f652e5b7d1843f74d469ecea158f972dd7bea4265ff1eedb57b43cf42b166f6171293a2c350d8bd684ceee1d36540c441cb00fdf606086667460e00b00c01f7a5ab332f0e1d0fc0e2e48d509dafa3995b6f5f39d95ad9f05b06ffd5c89cd656cf7bcd2f2db65523aa8dced179f938d6fbee0c57b1c77936805032db863231ea62261e1b3280866ba8db3fe16632ce783a86788cb96eaaefcc35c5dee4b25886732f839876990700c279ce0384b7385bfa287187a8b572c90290a8083e12d741738b0baa42e0b05eea64d1a7fbb88f58497179bab9c2b034500a7bd2c03fdd0cb8a57141b3339fafee71a32c21c4d2b1c4e72fbcb9c0bf9fdcc6f4ffd55264b717f26c0206478a1e99f2be997c9e5058e6097d89152d4a0552725d941cbfc9b1d6804287de0069a2e603f624bf18ab24c332324754d0dc71e4ab0c36d8f5c884d060fb79b4050952525e8160347c43008da15812975ac4108f4a72c6f0bd109aadd5071541230e4515584e40fb0ada325106bdae71e97fbd1161badedc6c6251b720503ff8bd0469fdc00a2c1570336becb4e762205f40c040c77d894fa83ac9bcd7ab292cf75ab08ad7223baf2ee69069d06c00cd932d7a43d63eab42ccdb58517913de44991c5258a9ee6f6af360087d521d62f4fb7a2b2ed7081c04b538c86153a3a5e56b711fd7f223bd12c1b34f845828d6f83957706d61314765faf1e6bc891b22483cf74def2281ca4fdd442d04722e721d3ca3c967bbd5b351ec7c837c9ea29323dc208a127d5180db016623fca41801cd8a25363abe954b2ff9d3a9261f23a664acfcfb60ac1e57fd1f59702c0c0b797fa6ed6d4742b245cc2c52924432b42f6960caeb74da1b5494e6def334fbead4dcd2ea996bd0c3896fa39d4e29c7cb9431058c337cb6ea72cb63963edc14e05f275f61dad01e31d79175efb82e9f75e03814013adf85ea508b03b41663e41604f57890732a63f487fa26648a5015b97e6606628aa17467f4c487c75357ee39dc423bf5d1d373217c590832c900cd250f2f89e15390e391813ad124789acba20f938f29938cb061f9c1eaa665cdae754d4cb7d049d0367e62291b0635f41451d7564140fb550b5fdfdcac4ca576f6892984c61b7ef47d0b81c07f2c96ce02d574de790919834f332a981cf3efed305652c6fb37066b2fb24bef9d8393d4b7a5d87a9b1d15090c587319dfb5d736b5665c60e389c1c57da02c0682f599bf67734eb27cea41ec22c2a6a7f1ca9ee0a21d201688b3b8bfd4144393ca866ea5260a8d5f43f759a5b9e6063218b0bf287e1a6ddf92213fce3264e25e87913a18a0b302c5c9f98b26a8944b4578d24942592d60d56a784a70d29868bf98fd46c07c3020f8372128372fde49483af402c1ccdf1de3643ead44661915171ea340602a1206976a2402c70fd930467110815274899215ea54081d368b884ccfdcb7dc09f0241fa7206c5eed39f067789ddd88a9044bf0d8956268c2558bec64cd60544ac46016a8c337a1de4239f9b0dc322a4c7d16333dca6774efe96549b01844230908f7f2dda7b8eb2980fd5cc10f00ee1227442a6b39193d2ffa906459b8a7c31b8aa546e307b15261308ac7ee46b545b42f632507519d4216eaad7c76c9c2cb47b55a9266d12c184d6311128c7f9812ee3420f07d86f57d50fa2b50d918030afcd9a1092c0faa6d89f63e1341c2c60cd553806be9c9929ff436ecf00365a9e46e9546afc375b58bdd34130c4d8fd7bfea6b2ed17be4e9d9209c5bbb2451e80153551edfb118247477833c18b821195962d883a76a737a0a292514bd786af41a17f6c4a35b0008c81999fb9f19a7c934686fbab7a432fc5c54474d783f06b1235cd4e24a8f81b707807d117867dd638dbf816809afb9b02890b5bc202d0d1a900c4c2633b30caa7e2e673dce091e0819358b4e0194903187984bb379d7bc0083eb6673b1caa99470a1120c146579304e7d17822ac84aeda8a17f46bfb950a1e713c39c056f201113bd8ac3ef0ac0f04cb119f8206b0e18ee5ed7daf2bfffffc0375aca9d29dd781e3e314eafe3fe4af0324f3c1e9377204e83949eefe2dd790c8304b8617066a08b84bcb703611412e9e828e32034ed593471411e25eae904a70f0857a16a8ed8be04320354b11aed154fa96bf4178e9ba1bb00110dafa27a6820df1351a4110e001e6d624334447e00527461ee56d80260517d1ba67fdfb83345a0b3f83e343a69a0b4b459a9a89ae65fa73670a6d21b0c9240440b587794b9ee8f146f2ae299aa8a81b4cdb61e102e4dda834f18880eea3fd1958010a027a7367d99d5b8978da9f1f93d400be2453998e02d78bd11d284fe35e78fede45bf95263c59706bc12c1d8acdde9f5ae8e537b3e550e74c1986b0607437e33c58308adf98452f19936088241b2da1f414ace17f502eda427b63e526f76c95d8652948416c900cb637d14b8f0fa34216576bbda3a5914b7178c8db41cbbfc78f14e7fbc24466fffbeb1dd082706034ccd5a764722d3188fa390f21f9a036e8a33a18d70ff89df4ae92a1546633a41095706be76ecbb14a245542b9569fd311f6b28e28da10fe586d984769c2aa6241ae4d1e108c8d9e51d611b636704995e833ac14b0508af0ad0e04e3ba3b42e13aed6ce968a066d991b212cd43295620becb94849e425bf451fd0d042a72dd1b3999292c65d166f48c5246250e5253a9cf4d471d968e6c356b0e7a3a4986ecfd99cab3eb9702a37b8d4ebdcb98e96a48270987cffae022b2cbc15ce8e4806382d979288df70227399a4b75e3867bc065414575a801c04c53815a1c8de1aa5848ae3a11a35d2f1e59c0597a72ccf7426779b4b13a9c60a715e092420f12c0cf153a0dd38d059cc6782aee46010229cc75cbb42f39111498d56f55a343e425971de9f0be4151c935541f29238a6e45af38a2580e76e30d6a2af33191b1e6e0314dd78f1d64da036860e0f7b25871d041afa9442195bfa0118b374db946865814b46ea2805dc5585587e0008641a351ad5c2be528827ca242f07e70b4314d206f68b8cb22e5c9cdba8a6d945e9537d9ddbaad811608dcc4c079050a4a243402255e4a8cb93cedce066634211086d3f0891eb1e5c92cc0a3109b4b7881dc1178fd484b7611be78d71fb38dcfb60bb46fa8858e6501299c5af399d595dfa9b8f451b52797bb23dbfa01b5f89c3c2f594fa6a8333a8ef31303baf77e7cdfb147f3f35b371e8e31fcb9a944c793908e4aa428bb52f636d72ecb6cec3e308a829215d427ae9678529688042fb619411a86d9b9852d1abec15363b533f2f3f15e9e67a12a06dd1a3f779ddab6167064834edb20b14b152dd050494ac17349377c5fe96f6358752f24652673265b009be0dc059635195834bdf412e102fb5db7d87721082350a76ba2e648ce80e31213789eab5fa6eef9dd7117133e9cbeb7fb591560dc604d1cae8248e5b3ca4b0a4b23c080cb342233a7cf40bc7cd828fc3137c471fc8419d07d14a052acad07a3fc5dd9dd394843847680c461f26614a4749d50ecd3c715ebde4afe039b04241b02b5181d8844f647ee7e58b2a9aaaf775972012f295f619df3db7d6664454d099392b3692cab5bfc9c0064080836e6f33984080cf5df470dfc0defb62eceeb54e97117405d918aa8975390d24811254c19c467bea6b79b01e560a5be067b8a41ba2bc2c1e367ccae68c0e2cdd9751ac284258d3227e016ea2d37aee07a3dba031c473ac4375608b4843f88ca8c447b2330b5b4c277a8d5f7432468b447420951860f496b76b4834b3debaa44eb7ef15ec2a850b49d54771c13d465e3812d1b15c531e6a218eb6745c4f83d54a2fd51bd503a62a10b5a87883ab09ba240ba8617c13cc9cd6fa164c3dbc82b5c031fba4bdc8e10306504563dd438a233829a395c57a8ef185b4f90ff1cc9cf5d76c7e079a9457786f8fccc6c70c5cdecf19fc470369246e3906439a1a5568958be75774cbe05684bdb9663c4554cd1402788c6c499c05241e0ac53267c0f529c3ee879a8d038c9066fcfca39d79b8be2d1054b07dd20e2491213181890a8a978343a04a7179b840a73c438d03fc407b472e6aeaca2bbf117c210c6e69d986134d8661f91861f0b03bf6aa4bc42d4a3be7506fc592ce5449e189410b0cb8bbed792a123fee2ae564c9177cb43a31cb1e4d6b7efe00b1834acb934106c8bb8c726c324ac9d5b19e0fb2105d035054872c375bfee6416330e833e421625be409f2d2077846307d2ea6c341088f03dfa6d2cb9226ef2566d8bcfe8e86054ef801da316b5fc985ee2d419027a906eb6a707717522ba7ad19046c3e7ebbfadb94a1d9d7f442fe9d419023d085f27f7dffcb0ac78240e37aa0fb1a867107f6f9995e17e3774240363fd209851d5e1fb4e5709a8939baf08d915a71754777a00d51f03b58014141c7146830fdd4f453fcc44820c2660c7afe3fb2037f6778c237087611b297a7da48562bcda60fa26f8b4e73604ae4e5a62f023336679f390c2b24d82adade5592ba2c09ae9edd36070131de1e9d83302fc4cbd252a9a4a656d406ff7a0d0a410d3116bef93d96ec19cc10f6677fc6e6333555d755067b52ad88c9441e52989c7777ce3cae6ad476f3f874ab8c972d3fca5f04da2e3fd093ddab0d2416ca601452100b2fab147e7a8ca0504231310e906aa76b5faa15567fdd070ac89739d5035188bc31cccb99e40c36e7093596879dd2743d302cdd30ac6254dab8119d733291ad260d94e90cc584eb3c4e448eef0d1ba8308b1e0650b474bab78d82a35b569450da5b840c7649ab15ae46969d7f0b8fdc2b101270642548d12d14ba38a97eca4f6c626c94882ba535893382fc227894b9c3460959267c8c93d0f5380093f3f2682dbdf8fc78ae9d1f4a9e899c8d74251238a3aeba8a31422eb17cdf62bcbf15b4c1a97fb1a2fc1ce806908fdf2af665a06e92f3d03ca39d06dd7618f1c04eb9672cc66a82abf92f06652f5e4eadedfeb077eb2e01e602a847d14f6eb1089bb221d3e7e323beb308696e05b24f339087c1499bbb1b1325098e0e691328ad7e223c6af25f89048f98b1d037da05f8456b6151f002f22527b3bdd89777c914e92cb23a80b06f18586ebfc2546228625ab19757133c034b8ea4d38be00c40fc1a95bc861d373e8c5f582a1f42aff06c021e1f910c92015f735d35f4a0012e7a0586da9479a634e50bad026c9ccf3a55adc28ed1d6f4dbdc7ea038f5122cec152bffb921f0ed8498af31b60ae385bf9cd2ab615e2f9e65a5797a7f5593a0fcb31ffd9a315f5f0a9c6968213a189b2748fcb9893e663cad5e6db633bdc2db566ff1e47d6d9673a4cbd1145c3725a66561c8b0c24258af92422e62e96b51088258fe0afb46c9e8e3a1089028bec11e8997f7f1b5ddea01ee8d537b5b46f718110203494d42b38a6e4a226f013483a0b886b628f516dd43e307b1e156bf0dc9788c78226fec67b5c3a1684ee639908efeaaae5d4a8586ab3cabc7f84e6b3e261294dce739327a723a20e0fe4b97eef28a7d46f5b7e8c0e97e9b45b8d9b51611f2146e251b7e2c3bba652e7538724c048838670f6a88123cf4f0812982b36525d8630fccd62fdbc189eb4c405941b652f769c012daf38c8c917920db4f527b0d16069aee1232e3c486660fe6f2fc82a558d63830da08da6a0aa194848840e3a1ad99416f6a9108a1c6ab55e1c1aebe63b5d7d8acc9a65119fed6d17676650b46c8b85ee09d18c67440dd4a5db38405e861918fb4801fd140e313234c47ad0f94c0130f1f2083b0264ff4a692fa37cc78269a6c723692e3a8eb0890fc76e70efc230334d935fec8494cd1a3ef246f80abc0b242ea80c6aab0422102263337bf3867bbfc3f11584628096f4ae02fefc7fd93210e6d41db38700679a1d73744cf1302d719653bb38185fb397f95d4104d860ebd7175b4051a7c6ce30674e23040f22c4d8368734e053aa6692e1d2c482676a58447d6e7c305d3e9d6fc00fb8931450dd1a66541e082d5e8e69e85a65e02aef1b7c60119be4672b9b192cdc38ee0b6b94d59a6116017706ca236fe8fab8522741d0e993e816ba850b92735451d27fc7cc007bdd1a3f0440b87ab75314f248cfa754d37603e1ff4c408852696cfc7a6eb834f8c42e4deaad3f50e6cd6d796ac640df5cd64f341153b3c54ca0d7de309f895f15ef5f8f762b9f11802bdca2d2b4744bb6a480a0bb2cbc1e178a50c6e7978d83f85587daba99609946d5f8bf4b0a0c4c39cef70b699035c28ff390eecf89a3442dc9a35a83d91deef9193b8b10cd385694b18f31b4c0a261473d741d83c2b9341927facbfbc8c36503de0f0f79e00b77764ff497e498438b71521096ba2931bd1c3e559ff6c6ef809f0901446dc5223317f3f703134f2c43ee9e0feae99b020f17e9074bf2b7da823ec42696bad8ecf04df413456a48a02a8190dd0272e81e3583501279ed5f14192b543b3396c44fe8957bde111dc0b651e425d3e9b52b0789bc13ea02a50e4f2342de4079e1a019c27b3a12f0ce538dec61e303b4602bb8386e4cbb590060e55ed059c2e1d68ddc19c9d86d62d2c1d002f13444672177e95cecaa496cd617bbd87ca8ff46773d80ce4e8a2bfa8d252bba051c3eab99bb26e78ce88776282a6173fe3fa8a6d74df3350fc1ffc06430a2fbc900e4851027c1a248bcc09ebb61bcf6938e26b533a75a9abb32110b09f3e984c42d5c5e3cb67ef4b0aae60e670bb30270b039f6f22b6c714dd0e223910895a8713662f2a07e1e7f213fbd7f49100e33e0793d9e283568ed88a245cc3912c2e1318ab90d2daec3843d7f71593003bc96641f7f9b29ef122dfcc51659d3bdf267349b1e4e285ff604c26e250b56de5d5be481bbc04d35d5be3516711ae6139ed61b88c204cc90695590dd04cb8c4b806df3ce24bdd08ac345ae1d11490e60c234bc27e23349fb42123bc849260a279333c53b9ae6ed4b7f06249a4010edd1437028aa2794f36af787a48d969a9a9bacd7ac8d75e8130a81811c183504f14aca65a08717e9d516779a523cda9b34d99bd75c153c5cf1ca14a918fd699d55ec4fc2e566a4df8c2449f113f53ba1f55747bf1866732d375a903d82564230cb0aac0ca99d7da6ac3aa094f5969062a712393fa8d3ec51d1133992905aeacc447eca6cc8819c7c0939022cab076679cfc2db66efe0fc865a43350f61e9f8c2d28fec9e59eb9d1b00c9774b7e03608f45e5de9890e5749590144f482ff5af6871bc516a810033841c1b85f938e4cb70a9ceb66105fa2a247f250fce5d53f1652c60bebeae4e6b7c202d88764ad2d9dad4d072015e82b4ad3ea90d28b9b0e327f97fd1f133853a075ae2f93801b5d6e2431e81a7d524a2ed09c94bd31f51c8467070d268c2a090f3a067806d4bf2d0a9a5cca65b7a52da9d2c1005ad9cb857a2f3557a1d03467ce2c0adc0ab4d3145beb5fb302055724ac2e5ab5cee9e326624ad7383c127e1aef1bda4a2e70af8cb3b97bc6a3049b274e4989214169b115269c598dee13ca44c460faebf69b63adafb644cbf33ac42256af3cfe3f95f3a24950c770399ae411dac24f9d3b49a592b4b7e120df7e6e4c6ec434d36a2de53b92ab2df48af7c3396f3e987a8a933b4ccd846aa05024f3f1a5a101deef61d135e97a4895d88edea0cde5e19669a02c98cb33e2dcd33784638b5fc3e23de1bdda2c340fc149ec7aa906473e7ed729027673ee5eb03acae8f9be3f6c1cbd9bb5fd3289e33b2fed69f358421b7b90194234654a2d291e628c3664860908493d829c08c1694190bd25df66d4597989d776dad0a1d90b4bd4f771a2dc965b74a7981966306dab42a7a00a73bf0d23e20ada99fcedaa54502189056b72d9574206650295ad7ea22d3adf8217468c99dc54818a0b0f546d8185a2c1a45c561addfca143c4db91dab623623903d8688c68a9448cd13ebc236b72681332fd50e1a4b38ae3341fd06e72281e58a968fc71278f4d237f715049a1887198b6b308abd62879c170469426b4d8f76ca204114189e7a08e3fda4df8a501c017358c937a1c156dc22c56ad41ced06397818f3e84e1777b8f8a589154535c639617ab3672ee936ec46a1fcd5020df466c95654c46ee491a454384c37eb3db8167cb229223090b95ac726d24d722cbf37122fbc38f902c7592d1081cabc099a5aa8e283d3b7cc37f2cda7444af66bcf709d16c83981e62bc63105ae35275082b2002019980b8d50381d002c42ecad38dba01b812faa2ec31cf3ec4799502d0d163d0710e163da30342ad86a436063b1f306f35777e3210f65f55a829786668450b3488be945d79bc5d520b2432b710e21798b8a833d24c76bc4d20b96562ba5aff4c64a411202bb736ad760a04aeee7a32af02fdb9dfb77a2e365db308646070a3e6424ae8520f20a376e1e89a4712a882f8be27e0aee02cfd1b0f77a1054e5f2cb25f5cc4526a12c48c55bdaa85d35125ad74896ab9a33898e559c4fe6d74c6e796e9dcb91cc4595dcbc9ef99fec445aff59462210e9762f479aff3a75e69a59d9430a4c389cbd59d503cf05be9cecae19643ca0aeeff2d688a8c0865710196f3c78031ff49fa927e65ec4db4520dbf5ef35995c993f76e09cb27ca02527262de86eed76a0cdbced5a49e3ad0ab79913c74455602bc3aa120b664d21c9a6f1d3e8420edaaedb0ff5fe9887b580a0759d2102dd493baba07b00bc3b2a7e1020b96b8b56e607d8ecd7d3c7a1a1fe7277a4378e74d237e9947e6a3aad8a5b0ebf16cd654443e9b02402c2de539e8f02205723ebf935f9f39d3494cee7bbb562be7b27a1c0e8b2d9a48ee819a03ba307a98be93207cc262b0f99593606c058adb819b675fa6b32fb00948904703da94158bcc6ac3850bf45db51a77a7842949221dd83854ea07efb91afdb18ca5d74694cc7e32da6ce5498b8cba295a4e06d3350b0b1214d9bdc3aa244486782375855fbe6ea4c7bbf583003fcb7444f39ee95cfb6db7ae793daee383542074c720c6ae7b421edc076718b03b48811c1f5ae65640ac5a171fac34778472213462e7ca2a2873aa5d621a0d0c835bf69481d2fdc0ac72e898d78b607c19882fd4a7d91632c2c9c7e0d3586a9e26f9ea63d9fee2a8342f09562d9863793853d93eab0bf734aef65d98b2e67d004e530cacb5740a9daed3d098620fb4f00076af249f500e042e6c26c5b0be5ec7c52800b0994ac1171cc02092a57a66a27949c729300c33e08f70b384274bb5dee00718610ecef8b49adc871e469e491cf1da0494881bc5f02dfed1f97ed21ee460b2a73639bdfd9840b2a7cac693dc21645a4be4149db17b97f4ef2aea10965beb6f896925536c0e4a22b050aaaab1085ec502d3331eca72d5d37592480c20c369af86fcd2c79374fc48407028fdff8376dfa426d43336227e101d731582344e2e3d182d01b57eb215c94923223e23226b4644b2d4aa6daf632cfc045b01a72397766dee1d4f4a81d37486f87ee67c24b76735df0fc772c81a7f005ca2f766f8ca2d4f01d46f73f5da05323032f4391448bf4f666d2e6fcef96fe694c910fcd826ee7b8eed24c86b5b49df3fedc74f113a2870b80d9f66cd6edbe1a073777e030ca94fcbafb9b32562c851aee4d80c32bd1e46f5493430d8f5e45994a517c120e354b54c742e35e5b00a5a56b96458155250921551b5f21124cf84a35920c702288043a276cce5962d0c8f12f672ef6696cf6baafbdee75c57c104a5208ba36cda031a104e1f6e9c427da06d270bb3257c8f8523b2bcaa85e789d15b5bcdbd464cf2ae02773c2513f9c5025bb918e82e9cb70375841ef67821e3d0824d2f5639e6c155ab433d5f40359c8de59ca9be8f60ef68857418a2690411b654565520ef75a6813aa98222ad05335a6fa6f19c8e1510e3cde81a47e2846b92e01ebc81688007c519b98c15c9c40953ad3557cb8f64d0c173f06e20fea6e2a72fa9f84c25e5cacff66ad51713388e2c4624485a7b01b20c6f336ee4e7ad30604fc252a018a71720e7ad6dfd16a831a9941541301f1ed4414eb26272facc8b335a99b0a96742330281e5662c85620e0b0df719a3c3945ec96423170dc473727f7dffbd2ad005e5a54bc81718e5a7b909b6063c22f6dce54b25bb3893c58c29c4f9b05152274c0b42b426a4255d1f5b8b3e821b0c55796ba0ba50303d390c6eb3bf0c175560af43321e6fb767c764217507423a619fb4af33884c7a363460893c36b5c47b0c1ac6ffa99892158c198018640ae4dc960231dffd42888acba5ce7174d3fdda9e663bfd8b667f2798386a6b8af8bec937d29f6c85cce48b719c278ff48ee2b1dba232f307b53ea116b4620cd934d3e6807042f0b04ecc9396f5336fded6e7759c757ca84c8d360281723ebd15c64a79c8f656a56c85792d75213cad1f2bc9c13bccdc8e065bd2d762241f3252f8b56f126a94bf395a3a7c4226f756b6a4ecec291a3f08474ddfcb1385750be617b06d572c4c59052a96f5a117fb3f52470aca6edc7d1c8510fd0b022f1458c21c04a125bcf619c1e0b1daebe5d7295992741b5ec080e2b9add449d72684d5f411c558a96d92fd18c403f22878905c20677d1c41723da8df53a5c71a558964519937f1d053c903a4143d112dc7f5732da330d957d7bbcc2bd293c2a293ffbe5cac05cebef82fa6ac0597b0d36e16022fbb918bcd2ffed4607156d09ed04000ebf9460025bffd50a97db7da978a34783a3d8ae5fa2ed1490b190216eb1ed2498efb26295ae4cc83f6d63374c3358062a16ad1fc33c3ca3bd5a40ac7ba9ddd55a654b30f64583ca42c364af005be5ae3bd8b04e928aefa10709b62fa2076b7abb47c48094a2d8f7be6889f46a857c79b44a489aeb6ba7fb142e37b08637ac907b3280aee05d6c25c4e780dc5b40a9c4c1f2b944b21e58ec0cfff2090acb6c679d6684e1713a14463b366c23c9818c633d1fe51d196a8f4810f10884b9d653c77042ba5cf9c6f595eccaac5d33826b558f308bacd0851837b9f71ee0691f0e5677b26d833b5eba10a8f6bcf559a7fdcb2bfb6313bd00c8cfe8c2656eb7ccf7d5a0c4c6e9f1aa89ab3a33070a048dc90778a4b2521132b77a112d4f87971f7c40fce8126bc76b70382a1ef67221a4574f1ddad2b793bb5ffce2ddfade910c9dc47ed66f029c1253eb47aef2f113914da6b19fb901d431ce9fed465be3648991857e76cf090d4907302cd10749bda8486f0464dfd33f99ba35121d73c6e4408b57441fbe1b602b4a81a6fbefe2c057a609bf42e5d2ef2d43b64cef1dd21dcbedc0732c93ada3aef2e4af8e342c5bb4a6d4aaee59772c31c9388aa5963eed2170a53dbc2737d1ad0b38e178159488e82350801082424a2638a1e250920906363b4da4269a7ec4128cd237c58fd79c413b301f3fde8385da3589a82bdc488f7bdf2f0263fe8041df7ad1a03feee4779180282d8013a3225eefc26b377c4188d85e63420a7c4c4ace67ef6ec0445449325704fdcda06d20239371f6033a103a3c014bdcc49773d291411789adc58c4033ae258071f185f4e75b5daee81c0d125dc6ac2364eb77ccd17ea23bbb71c0338729d6a75d02b5e60d53db4063b0eca65e8957adc991612cc6756221e3cc42dd12bfa6872a6708b7ea90e30034bc5c89b209b40c83d3421a49607c49506ac4d544c5f86b02c628d082e66c694c6a59f8610b7b82c68e7efa2a1fea6a5edf84ce827ddfccafc092c4944b4b06759675b05c60661425e2ef429b155d1fcc0a5fb1429654436a225948e906ecff1c0d1355595ddb2d0d43aa134c04021bdd82e8a88efa2921cac91fe94672df20415e0914277024b40d066df069134f261ede1987eb0b5f1a83e9cd0b60da3c163ec00d1ed476519186df5814042b0a3a12e9e00ec67e816380b56966a8125989959c6e83c971534859eb825f2a8cb23d9b3b0e21f6f66c68e892557c39fa8257d64f2865ae8d06e8f93969fc452c014a1d83060e41013258b0b4c4c86a9c8225dc2dd38150bf99988545ed0d551cb1b695a5a5eaa36d8d9759ea5a47d178574d5e81f775a247c4b20df1be6801421a59059a89303631965a7ce33212faa904d8d5712a9efb2bbbf401a4ea6c32724df0c12684e66efecd6762aa430f5cb6ccd00d348f3119fe6ebf2c97f9b7d6ff697bb895499903cd20b1e428bc05f2ff363c98fc076f5b49e55e0a7d04f3e946b308092c1abf5ebc597b62e2058140b0496101cff2a2e69a185b24101f0f6a203b64af517bbab6ca0dadb72283758e696b3f0431780ad37d848e5ec427206e57b803953ee12d451f3e4c93f2b3cb4cecdd6a09b5023f972be84191d20940ca70a0053aca81de7019ded1fc8dad376ebb08a5f010320a9c898cd461f9a275b7f9a49e85ec800a192e2f74695e0428673115a65fa5d951167dc3e5800cf760c938b17f486dd4fe3a88bb055c8b57cfc51f384056d3d490ab284e39d3272b8f8637290d91455a3bebf6f4f3393535313b1c65c01f2f6ecde6e0b60c4f14459f2e1e4831df14027e30facccfb32ea769d7ebbb58b59d814df3809423bf302ca992a70ef63c591ee6c3f3a1776cc57e5f0faaf01b250f1cc76c38f2561ccd211a490c4ef4af2b0920a624454aedddebc5525e3ade80a02e2c20bc670ec93c7f15159ac3e620ed208f594be6b78d0679cdf55310e1a28b6d0be19684d2a4c2a86fe717abdfbf877fe231cb05f011f010f282a90f540edce257a803965c2b1bc9852bbe49999f4e127aa1eec7776ae9528285b0483d2c1a90606ac02dfdfe05efad27542fd432495078e5d2adc1275fdf74c53fd1ced6c8457f3bfff77af026d5136389c9d36c0ef5e4c0936b30bba4997bf1f0d489a2de9b8fd13672a1c36e2d042578fb8d37a888ff0cb5a143717851b632e41623d4afae28f7bc5f8d7adb1e6bb2f53ce596592ebcedf55981fc50811d28ab7c3fb9aed84983a103fa657b7d3bc6e3641649b8b55178350e603092b81b4da44a8c9421a89f8fcc2987a9a09ab312852e30af136749ab6fac2da7293a0be3fbdd4dc2b8b99290471526b6d5620d31fe3e4d0d3df4b8f71b18704cf2c849d573f1d1acfd1fc0ebdea8784f4b3e830d6e46d46f7bacb46ba51c9e93b0e6c1b789a05b9b8affd4ad96d907a5fd5481e31ae950d8b7e05e2504f3e5125e701a22dfc2f795bbac83fba7db93434f5c9508f6405cd4a250910d532cc5a3039173feae3c47ad8d87aebc186387aa096b4694a3ca337e57be1bee6d0a383096d50fb2501288fa454512c126866588ae341dcadb421af8c47cacf2b8cd1f09dcca94aab27615b586509f3ecc562949c163de07309d72423b58b4e85719039ca949947ef28db32e67e848dd6061d04abb86ed9a8f60bed69e2e9c209b8a386bcbdf1973b103aebf868b6318503dec4ef6b3b05d369a3ce94a0938fe3605e809f56f5ab37625821aa189e0125604d05247310db3d407afc36eff936fc6f440f54bea23f4625e2da5a127c98ff492e09c00131071d24be5e2e1d1bd9ded495bd8650126932055b33cf45e21888a74c3789a565f91664cbab6af344510c1875f816ad9f11f6414b8fecad6c534e1acafd7d40a249b686aff933fe9e6d7c7ba705f2638567c023825bdddf277f3b52c3cd2f2dd79a82faa504eb09d6640578078dea329725855bd49bac4873ac65be2dff8a865381a20959909ef158918225bc37933828cd11b75342aa058d3d193fbb0649ec3b80ef9a06c279cedcb09e323abab68eebfeeb484cbfd9714f96020c6839b9d7de028bd90aa2b6557b5cadfb250c54f9ab44a799286ec82cf371051ac2c6aaca0d9f82d4fa778532418827f895527be2ed3a271952b2bc3cda45c4253d1e5e987e0e989331579e15517c91a1d430d4a8084bf24a6fd0d7193cb48692f919893d51f74e27ca86bb1d148ddf80151a745a4d1df077d626671ff5a26c08433b910110098484df5b4ca36f23c81f5a762c35d9e726a848c8421252ae31c18cd4ddcbbff70920a79d594f4caf806aa8717c69403afad9ad3a40c395e086bc2d46c3a59ae2cc2830901ca75db62cfc5049fd48807e1c7ed8d18d920263941bf9971bbf1bc5b20948e1c55f175d38cdcf14c4f04d4eee8e093fc8f984ce00cf53ca1e596c7d8fe80dfbf1d38d105c0c1043c987d6ac4d46d44a4af0488278702023e72547c2b1625c448c5adc4ac395697715fdd1f5bfc145135afe8f1c17d2a319b2f8052d563e4100d169c5651083bb2e4439eabd3e5185149a85fabe370afcdbfcb38c289e84a8ea0e37c43bdf00a775ec10a77e3ea2a6e3a95e7c14b7e924ba2c1f6bd5b7d8c13c4543a2dd9e58168b9ef0f10669d778e3be2ed48e0933a3a0f9f0202b3361b3ef7dc5d10301bca670190ad3e16d09c78efefc884a42ea1d99b32656e271ed50122ede7ac1c00202da01a51544c7809966e32c6e147859c9be82080ca9d0a00277ca6ad78c7a31d84435f34623725fbbab9e3b95002c48e891dcae1a9f582714b23b46aa704078a7e6d1c3f94a03465bbd198f96c9a536ef6c8930cbb2c9bddc4da4027bd918d0c471367f615f9912513b5981a4a0617869091c2cfe635e76e18ad0c3e8f6f2546b2a26be637b9402a953786985f008d70e359fd977c2e0c88d950fba85e37a1926e458d40b6f7e4e4856aaf1ca4f4cd3fba2972dc7918197afbb9ef01db2446cc8e957bd8ca61bc63caa95056b542982e012987cabde8b6d7ad01304272946c2a768a96c440b39ac00d7a6f20d3958f3e6c9f91b03633b6f3489b2bef9751939af7367446017b0e7ab278ae6c94df5e763af4a3776f1fbdf3e52134d54606567fa054dbe5459ac84820f55f9703f64980daca11a1d697f09f69fff790d215d2e624a1d460f465a27fce65cd1c4319dc1a2a82ab1226a0cff3e61fff88415550fd3827f8d9338309d1783ec4cea0266d9be8b21736568289bf4f90be914d0502dcaf0214a403c4cfd159055120ceb8b37a2ab33c0e8abee4d4897518990d5637b1675c18c21ac9bb57c561e5eb32dbf16667a1036cba855308e3ae53ec4dafbc63aa93de44000708f728ce42e8e769e11ad1d28104f30939f97fe628c33fbc00ad626f5301403b0eadaa3be5ae8c2db0e500752d5fbdd80cd2c26dc983d7b92bb5681d3fdce474a3d48abb98e2fc6a7a07532f704905402b73e24643f68fbd95557fc127cf68f5f3d281736302a3f7a3579b41fe52eda066f05daa1d06420182cd7b64e454472e7df90b786060cb1e504e98f0f36d6e1a01b5f9a78b92ed2ce0610fcc0dbd8d0dda1a4b6bdd32594a18288d6a9f41ce7ad1e44de19d32636f38fc5fcf0320a0c15eb004e105ba8faa25a4735ea29ba23ca05cd8d856dcbc84153cee318c8b4800e9f9a6b39ccd3aa6e1ae6c08948c4af9a410910685c6f24889c3f18ab96a03b418f93a4fe4cab3087c27260a7ca779fdf33ca5583a017d4eb85eb8089e512dce1440eb38cbe5df2c8bc4546d98864fcf4ce3da92e65331723bfb036a08623f037ddb0e485ace2c46abe7eb4964091d2350c23a3a7a370f2bbfd8d67f8910c5175bcabeaf05103dd7d14b97bdfdbc863331e0ed5a9bca5d28ada8f62163cd3a4807613d89cc27c934b6708f9b6683f6454bb8954ef6459771c9638461dbc6746fc3dca5cce18d8e48e77ac1bcb164a9911cb649cd5fb19e101b5a8e09bbd6ac3f08fd97c3e513c4a36686abbff4fc8d81a7a084042067b88055fa00f85cf34003d5744563b96c9ec367744afffd57edd032da8eebd47b029610038408c078ec1a7f4047209b47584f8d68d99613f0ed6ea9ba455e804e83873560456b45b9709dbe1e1c9e1620e96c75845da3d622787f87fc3948ccbe224d937ddaad216c15d778d73c2f87c0a97c07e05089fc1f81941fe0d70664ce465cba8e705222a5b1dd41af0ce44bfabc793559e14ba47b638a21ede9585ad8269a003bf7d5496e040f36ea8165267a5cef36686e8bca9318a6d28f468edc45b4cb8f00be37a5749cac14758926a289f54bedbe12321e2017add6dca76e44f1805c0e5d7ba2284e53d6ac771126d3ac8cff4f79a63027200d88e6ac1c0d6ad3de2b1e287b91d667127bbefcb891aee97b0e0ae07b3d8e4f700fea72673f1029de7700d9815a57dc09795a38f85c840a2ec0848817406c66f143a88436d9455ab3baf296a0d2f1f7c66a0be153beda2000dfcfaa5801408f8573a6edf971faabda66bca9adc70441acc7d4331f51292cf9e124f52470ce3f90546e2090c119f5ab39350522679bd148a646019f9a1af6c74eec41e0ce45f5caf505612c5217360f7fe330fea411c39f79068b206da524c69c8c240bda24d79a4e812927c6db00b4777e0ff956f746ac816ada50420943ead5d1792c4ee4d0f56ced0dd80b025dd39296f6902d51f4258f0ea8f3dfdb82d7bd3a3f73caa536a0db2fd931bdbc4d42b2f4a16b3801efa3c6fb11b04ff2477633428ddd407b69097d931a6565468d32dc6ed6017f8511956330c748ee5af14469c6fb5ca298f4224e88b9f46470b696735b7ae90ead5ee3c2b968756faa9a9daf59d35fba14f6d4cbeed8a8934572d137e7dd90b13504a8a5d37373080b814b6e961adf0e88fb235af26335fd40b0f293480eeb87e86868f8dd28c7dc35038e3cfa3ebd2fc672c919781782272018465c70ea8501f82efe316a80961e088c81a5c786af218b31f34e53efdccfa3933c1a11f92c66d9f9ab4f56c29e8c4e287450f8b34bcfa9cbe00c218d58b8eac4fe5b69cac1d951e3a7e1a7b8f39e1f0dd37b9721969f11deadbb9db7e366af43aba223062ff6f4e0e4deafd3d023c565e621f81edfbe4fbe4afe32df17767ad0a73519748fedfb5e3d287fef5b640340838060e404dbfb8731bd3a813be976976fb97de4d6a8f406ef0373012d44e8079af112a1bbad98abc5e9ad276459e6cf63e631442acecccf7ce457eaa09f6116d02195d2ba271d11478cb7411ccedcdbd9917320c0375d49b9b2dd80c75c9a8c5b369e3b8dde6b536d109b2dd0a4afb980d567673c33be1d33db4b96e74496fd41b0c09c117bb428a53b363e45bfa0cf951e5b8a72eb18334a79b986b7105260dc56a6bde6e16d103980af9a2ce625a82ce9dc7455f0d5031a7a1cf75a281131748746be22892c444d504630e6012f4139c29f07a578fc367cb507a6a48e5c9fb9d221d83a8962690b137881dd01fe6fd3237cfca919df6bc256e46dad066e69885f9f6e50fa906d4fc10536cd89fdecb7a8d299cdf7294104d18603bf984b0b847201e2e077994b2cf0b02332276cd09746193efa1aa0bcbbed985b184866d8c09b11e558df43c6483c72ac0e046b3b1c4952f823e9dc3150ef0e3740e9c5127f4282a82c577d38029a282af680f89cc98119200119a11ac5317ebafcd82ef579f7a73974f42f92060dc4099ac8d862c3cfe45a8bdfa867ac49ab7c30645031d995007783ed9e05b41778ce5954395a01bec72a4039d018fef619725b7085d9ba3f2d7971eff2f9d79c8da6be5fb9fce1f258f71ffbb3fe88b7da546aff675ead04e50e227f3130a4cb79b039a4a4abeb65f0949e2cfc7ce44c28194c1e81504481f00b0be0ba11b8c1affe06716123d4fe547f448f749425e9df80a98d83d5a93f33c501dbcf7f5a57dde1afb9d3305e3f442ef33ecb5391c8eb075f1b5c229a951b119e62786f58ea828df7fc74d6444d6d14a733b2aba1b27cfc1999ae54b5d26776ae2010b39dac00fdd90ed2b0089fb3da687f3b43061bda79386e648dfb5ed4e9e19cafe3a6ac3b339e80a20004a38ad34a1d8856f6fa1c9e1a3c2aec1b64827630c949bbf3b58202e673cd76e7c6b73be345d42f4faa48046431e2bfb399d59dcfa2b11c131f951eda8846571cd622802b60c54ada49654e4cb098ed72f1903bfe04486e2616bbfe96bc6adb05dbec54e61489f748ee6509f0e500b0b9c100ce8ed3c1f6e0f9c08c387983aaddcd65b480f4dc8a61bfa7079dd09ceccd68dbdf80cfe74dc3f4c49533a120781d6f50700832e0f20289b5e536b564f991c78e04c6fba920b5402c4b782913975b945212e88f67265827a44fa34d5e4b1a31046fadda5161b2ae6d1166c81fc5c4f7305b4c54d42267e36bcf392f50a4866d631974899221e0d15f0f0da3bb4b67bf396b2c0d335773c34260dcd0582f60f25cdd8f612b2d4014191b2c46e871f0a2511fc7bb54e3ae236b07dba9bea249a6fa848f33adc147ebfaca03aa208162fad2f7445fe1bbcec61638107aef4b909b80cffea4f1fc2fed3df13f36cb285e5207daf93c18262df658aedc19b8a1541f7b06d0c02e67e1c935ee3c3b38fbb310d770b77387fb845fcdbc15999b99afcb6cced866322f90e1443158a90e2014312f1bc3d76b2ae6ca69d40e575a76e6a3b8a34d19ff6d0daf4220e38e251a9d1efa0c2ee9f98a6cf853d02e2cfd20988e0693d1fcae65aa162ac7ed3c8fd20ec9ec41df5b0a3f5d11f2963bd563f284eefd50fb6fb6ef77313eb03cc8ad4242e4ba805a83d6712c266fbb55b37a63a9b6e6042fb423659c6a1b2fca03cceed44867ac958ac013ce5156cf02e2f7b479548b0a2656a668d8d2e7b5639d722a0b9c918391123bf777dce8a3630218fd4852905e83c40e51d511f534ffe6877756096e506a7e58eef718b41b43ba8928ed3aca4531f7e19db8a5512fcadaa573e0a86c7b2587245b7bfbd684447d2425693656a8ec02ed3fa040a73d84d07fcdfef78334d62053c3bd4bf7c068df9fb4840d72b20e8875012f0c8d7e1aa8759bfdafbcef27a1ba9ee5e84e0926922eafe31e4465699f1dbc8ad3db34e026dc345aed708d99793a873d43c4c641f924c5e01510143efbc10448e3394e7b53b194a8d7ba10c3342f6aeca1566662e392e4deaadc760dd7ff7aa2f821354cbbec8887bbe5ea4f80178413f24aecfcae532dd851a0f5d18c5be586089eb851cd866797b9a950b156d403a92a7cebaa266f27a1cb83dc7a5ee9207b9a6bbc4cb0fa3f08299993c7e6b8210f49e9a9dadcffb2c6e798072bc7bcd431cef280faa25101b2ae7452aa8d5b321fc043d22a1c24e45ed55730cc61171941f0686e56686cb3048259bfb214d2b0065c497e555f4b70289192275334fb07f665020d72df0315187025103149f59fa3e693cf310a40575437a47a2b1be8e1807ff30fa6ec034ece84b37042838ec7e01702ac42a191ab45d6c8bae238ccb65cbeac0ff81073aed7eb65daebb60f31b90fc19e3ff7ab03aef8204835b3f89b2781b7a7e56e10c55e8f40b04071d7e825882f19827f1cec72e3fa145b1d55591d608ace3687d7f9a0ff14f03c210d1e23bd1e7b475c1a0da74baa6774c67a46ccd6576972e6638bd6df17508dfd5dee54fcff26d20c2ee402cc4edac2c00d61d5fe255a1444f398c2428908d386a0309207e788757b88a0cb1f9a967980f8d0a770743aaf99875e4136830292c454deba4bede22360b3e33088249a3a9214a0f00a513bcf3d92b42e367de85da578fea53026aef2da181b17abf4fd014805dd99a3598339994f974382c621f1efd3cc1052d4a29ae1677ca41de1fb7427718a9e1faa2734c9a128fca39730ded316b5919be13d30eef11e0ad1945716b2d12ebe240efe85a1d21284c3f268250ca8290a6f86d8d8258d9d49c0fe73985cf3533a49fdae2073cee9aaebe5524e5359a0a8b108ef06be12437a6e351f2f0adf953e26e308872c136de58337543cfadcfafed3ea27682b1e8d81d7e1bbcc240e3b303f1967eece32a097f2ef8be1fa25c81b9ab52368930f003809a0ff87d5d13748deac501937e5a3cfbf0023a43b0221ca4213a0fce302dde1dfbbec63554a2fb5f8c7c134effe875a6d2ef74d76831d2d5f3e06cedf9141fa6bda867c64bae24e7cda7642bbfbde96244e6068807c581395ba0d8b5173900f9bbe1541eb5769f1c005c73cf1bf5704e4595943b629662b52b138bceeb17ac2b680e9617eb427d0943d5dd739e7113f34e57ac0d8edfcc30fb08b982681239d64581b8bd3fec88ee0b1e8916de43a0091d0d8a263434b3f2f5a5d66163d144516db906de01f0b2f30b2e08d75971adcf85cf4f14cb4e8b2a6ba064b43acd1bd252e9e082ad45fde6ad3b52269945c370f76c9b41ca1ae862c030c54640a141857c3733c45bc71bc5d311535efbea29d0deb556b48e7c9ac3232ec7aee209269becbbd44906842319ea1c7a3cb5d077aa6b997aaf4648f1a7e2ab60d6f6497ba728b24cc2fe17ea2bfac8b1c256b09f95957b82301f4ddbffc10c903233edf5e3f1bdc7d97f1d8c834b4ce8fa1a5ebe96da5a5daa0320a0b8c8dad5a4a67701f12582f58373c42a87c24a430f7f1b6dce0420e5a059617495a11eea1178aa2b3061d392b1c3bd61461d0ed564271379a054789a01e7df97b794b159d5d77183363740b7e8884bfcac70cf0dab04c00111ebc04490b1560bbeb0769361008f566ea81ba03c7642d2700cc5fc63210eb1143e0b0c0b9cea37a25cfd7cd7aa35a61b9f76241d44e9f2a54c38ee7804d0d69014c3caeb123b0a53f1a12120bc34f674c82646703f08540fa3dc44017e319816de8bc242b03a3e433273ffbbcc9473542fbe793f789e9ccddb173e4d7e9f14adee199100b310014996d1f3dfb14915bc2a6e64a60b29be9d07b602d50d6807f02ffe8598aa216931f9fba2a685c92e8ff296558edb07ae48e1aa563c1a305815fd30014c3deeba825e44bb5bfc3ee8f4fe3ca7a70008461388f847f3d1fe4e7f18a5d3af9f94383c9bae07073698c2755e0acaa13f6b7ffb1b12b3fe59bf87848b1251f9e0e1f7bac863c615c9c2cef147c4972b1a658a4d4bb8bb1c843317928862b4576aea7de2f8dee9846dc16d08c19829c249e88810ca4a5fd8f1926008ff5fbfb609bd3f8358ec510976cea954d71d3ead794291632fce19f029761812e43c27ac8c99681d777d51e2874b9c5f428858d37155cd14aacc5875b475317a0f8c3d9dc3c5edccd26b48398587c1e292d191b18d193afd0912feab205597cdb553b58d1ce6e04620d74d62962869325c1a8f0bb35946dc692758b0f5fbeff84a10841e071bfe2f1aed3c5cc183404fdd78346496b97ae11502eae63c48dff6890688f2eec9f89c2b209296c8bf8ff282d6ba71a0290c58b427ff6f201ad90879b8055446366adb94384e4098fa12ec8ecdb795c509395ca701ac85a685600c1b6e8c7f2cc484afebb6efc0f354ed5126ff6db6f0954ee15e0b7c03188ea4313e07f5aeb8c9892ef3f1304512288872c0969a7c4bc5703240e472e8861cc1d2cd8e6399804dfc9ff7dc98f88e199e20b709771a803cd4338b1480c2a573f2092bcf65c8b993c2d98600d80f155616ea0e6be1a010572ad2e39a2c971d72fb326dbf7cfbdc60fe64fa3ff01f8da1e0270131be924363cf1cd1f22d8e26ce98f2300a6b440b74f5cd3edc5b5ab40eb52a5c9e1183a02cbd27dcbbd8436d6db18c4536144b7fefe45644f1837dcc6ac9ba5916117386d07be0f21c4fbb96afc99d8bc4419cad98811d9f916169ac39db16d635cc9866ded4d5c41d8df193251cc03f99e268c9f2adc354fb07002c4b76595e8a3c5586709331fa9eb5a1c5b910697f57741c0d255af2914c0e2430a47225071bc7c549bb4f536e0641f4e29d61b3c1e34c86929f7d7ac10b5973b8e00af1472288504e572effa8f6b9c415a7cecbf4695a1d6a416297c7043ae770ddeb3338fd5f13a25c538ca67f9c6e5aa34511b553ab9098a11474118ce7a6c984dec83b24ee98c62e38750f56879b0ece76fc0a9b75af982f492424a9bddee20e006a266257fe3629caccdd0d120e5222b2e9fe2da715f42a5edf0370e1d657087d4f8a127dccf32b867c124f208120563f0dd09514062b6f3d87f2188e74e9b670e4f158bcdace9c39e08edf9bb7aead1c55658ccdb1093fd3df4ad1da635af6b47caae356bdf6980ccfb0420f5b862ec4aecd91c530ed08b02bd6c7ff07f9e9eb91308ff357aceb9ddf56170e3fc196322dd326fd21ff41865678214a177288dc5c91dda6c083a7270f389aa7fe8d571d5d99c483146e5f4580015524aa9eb4f547fd414aabc077bbf6fead77fda191c5483ad2d3469612e713e3e175149c205384b3f5498b4f73a7c529aa061791514294a153cacf59ee3d1e8c0baba839eb080d556c7ba90d6e0dbebdfd65ca78eced8d65bd092055d44d1ba07ee1c59d512e9bb597293723bcf884ec2b703cffab5461231be0aa455f1f98b8523814827edd379437ae8049130e036ef6f80fbbf0ddb3320bb1428fd1c5949293e86286e2f8734d7f7a64ea4f91bce2d9d5191e29e3b26190c63e59541cddbd630552387d6bbe5c7daacb46136eb0c97c20023d5491a8fcb93ac198e326960c555c869895f03d5d2fd6b1d22d8cefdfc6a64bc1c52e5d9b970596979a47954436a580855dcf0f576489ca36bb90ebef30ee97d0c9240fafe8196741c2399c663e88d62135c8a5477e45ceea14837d3d77e68a9a124386bb0585242c692301cae40f01adc2c2a84068647178a3298536b93c8b38e4b3d47752e5a66b4c0d3dda75fe997a279c74eff449134b382b47a3f3bd0e709abc6121cb0f75369f938100187d36cc60f3385d7a70ddbec011a3533d9f2146fe2def40cb5ae675a88fd67dab40628871b36229d3356576b664666fa0892437d6450c988e99e70b1aacb5c7e92aa2945743e9f8e032d6a6c8fea8c2a823e0fcfa83c3b3fad3fc610a2b81cc6342002dd7aa6aa7c6e255caa37c065a55cef9009052650eb0d5649bd9b6b15b5bcfd5fe8115e09ac28580934a0c0570a0570b2f1082ce46af4ac0418c7ed74183ce2fb3a0cac9c0d3e8cf8cc74618c59a8a30596376d8771bf2ddcc0a093e29e973ef3b37a7568fdb355a44fd329eb12729888819f52e9a6af62cbb25a76e11de4379d1857e6ad6878df538b121220582361969f4c64485817bb583c524d5fa70a9487a2fe8db58dbcc9a1593ebd6ff99848759c79c343009d95dc69573b58635862e839492590a4c222df1f8cd22bc854cc1cb3b66598bf636c16e83b7547c19892881c140c05146af28eeee4e79d45511ff9c56dd509243944bcdb2cb3e7a83c14921d3a28a9fbb8c8acc0f500ad7ad35e4121afebf7939a0778169268de0ad3bbfe75335bc36b1d9d684e30dfd26b10f41589c18c3d0805c9ecf77e8a66520672d28ac62dc0cc2b2b8b6f09c690e62d0b91e2107137fbc8ffb8115d813c4ec337dd568ac9baccde9efa7ce3b6cc749fdd2f24a80daafcc2827bd04077bc2da04787c02e6e8e662d38217ef345e056269d8f17aba22166c61f5133431708c3728c79c9d898f874e03c5246926419cc0a5245f9c3203b041ab5dfc4381ca369e7d04fbe310081a3d280ba028f7301190b1f044cbc3eb83157aebd88d9b43f00f51cec55ab46a73482a8f14dce80ed39af80b38d8f06ff60ec6bca4f02f8521e759246c20048742a2ec6032f8a13286e5dc469c0e7360e1a133dc3272c79d8783e2356df2b8bbba0664a94b6d2fb5d501deefecb181d4251d9515041bf564657f857df4ca2714babf30917b223fca1dbcf3ce933f3858662777677147b1369970968e698d24243847110f6732a9002340244ea0b96cb7e11e8270478d8b8322f17082d6d3e92b7ebeafb20333fce77da822c67ad7f707fb0d0092c6090e7671df12089ab4cea7e57c735fbd170eb1390f45958e98da02e55d93f62edb00f2e19a1dd1c48ceef44f330330856099cdfd44a869d18fc1412768a7d2c79ecfefa02c0660f1f3c104e40e6df3bdccd7a830a09e11b1d7cc621a09874a6e9a12103e20c5af88467bd408c510c32b4a813de8dd2cb08c5f7205b9ec90fe0e37e212cc43eab7298d7f828170d300fd448df92e3c86d471221095612b0e8942be580c23889781fda641390d32ab7829f65f3107af6971b5a064a7adc58cf42be9266ee21ff009c85ef15ce07b1496542e2b49b0236a740b091082f21c29645e5bf2616d983b033b2b76114f54dd0aa920c9d68dadd0dac336900f95569a8b064069cc5f4b25c4204cc5afbd9b1fab7269d346e415170a69a5721216e5dfc8b0656c6bfae9d18047f4790b72de1b3f91e4b99353c08d9a90674457729d3c3c83bc6bdea64dbd8d023569fe30b7970af04e5613b9004b6b1592f5d7b6fdee7244d1648ad60e1cade6182cdcae3a7b2a0dfc5ae9d815a61e75f6cb69723061c0f519e8bc065288bd9aa27e065266cc9e98b930906a022e24b14fa35ae5ebf22254acb4faf4e795d02e39b51c0017bfab684abb33c719409112277d10f522e31174dfd5b06c97aea490212667c8f39407508988b0b78fe6accbe2a2f055eee893e182f40b3f7d525914652342948331f2e53a0bcbad8b77f89528ff8c1119a22df98699819ed1c7ddedd04ac2c2e95a7502625a0f31b8405b58ae690b8f2a752ae149e32f00c34ebac6a3df2c0f96409c39d6e47c641c8e942b288708f7ac4c516c69a96af2c5d4e650f8dcf1fdf060b2bb0ef1295128d5a6eb5494dbf78096058d147086386c6a81bc275d9f286032576e5c312d151af62fd8e69b1fb4bc76aa699b5e14142eff64c2fb3f9e3decd86eaf4eb9593bc651c37c7f678b2033dbe1efffbbe430cbfc21f089b946c66aa0e5cce9d1b4f2c4eaf94685418d8577ad37c2dbf9d85aa855d077de7f94fe573f82f0cc2651b2971e342cce9a178b3b4fdbed3c6e42ddda70908688115d4f9ea70253e03d1e239ba7fe49273fc3edeba73972327dc01705576b1a3d3b8c9edaef97146d9118d196e408a96f934965e4266cc2805b4f77557576567946ac12ec0ce5debff9889ac8f1d6d90bfb80a15f9fb1fbab9eb302cec9d96a2ac566b1a46ad57f9c465cc498898b6ebb259089e7ad1a14aaf422f388fa0e6d4a6db3c27b0aebfbffd87ee75291d768bb32b20708c9b5f019e4f8d27170027c7e48325a05110b312ca9274b664c84b1a343e7ff8c83003ac3f8437ed3f0b515958efdbf02e54b8ac3844d41dd44fae1bfcc186a84589271cf91c707dd8fadcec6531e0f5d75ee69d63c324ff0342117d5bb10d0595250412b175cc0618999dfa86ce31b401ea184c064c67c66832058e186cec39c253e3e686889fe9bf0a863e131fbf6529e08869cb7abdbd8999c9df98b0616b8afe3fd8e714afc8fcf2bd941f6e7a1b82afeae204f6364e68bb8ffb13299e218e2e8ad6b18c64ad0c9a9799c870006cf757a4240ca4568c71d1ae9ad5415e21deef6c3504e8e8aaa9dc68d744471a789d939a5eb8a3bc0d8d3dc3ca087b0d30309772caf87b29233a60f24e9bccea5b9eff34367f14514fc0b3f78fba98f3c8a3b13725cb8b05ef8074d9a7e557153d8885e3fbc26f402c71ceaa4dd42b54790ba983f4d8478f784628191bcb12ecf45c391065c53959e6da1159a2457065c101e3071e1df8453c63c639c4c0edcb116889f249c2156c9179091b9bc26a5dcb1c71cf3946f15f175048fd41d7a756c85122a912cc7594636c1fd7b9adeb0b94009f3520e9a6142ebeb1160825e85112c82e210d4a834826b7b4db25fa26cda3dcd79c5163bcbfc1efc044f1fc8b6b40a313fbcf45200bd25013e1a654cf764aeceba1d9a2c01a79a6de5af84c2e55370eb2e15a87a392a17e0ba983ebd0faff136a77d99b723ecb935b4efb787e66ac65fc0b2beaba2368df891e4c3a4b7c2fe8f7ec6a608ca638f089f95c26d6f60561e1a3b245453be3a58761e85114ed8e725c18d0c8ed535c6e5c1af26008892248d0ccb7b0c7806ad6b0478486ef8a996072650b9647fd466ccdd725a714a447874ffa57b338e72f49ff8be1a3a004d9fcea039cb6ac3130b0562f36bab64c7847a664319ff9ab6e89f70e31deebd887af11d40cd31553ccb3e1a4173137f9230e50ce873f6c9c2edf71c145814b71d6e60659ac0ff5da601015e5eb838d8893289d1b7516e559187b12d580d13ebe8066d047a21553cd95c73cc8e8df004ef43e0f6db1eaf2ca7cb0ad2d03d38fe7783f09874ce2beb5de7a425bc4a2c23cc334f5562fa8c009b0e2808051e43f76af0451458b199c2d43441023f9f8cd1a2d337c184719162e5a80ee1e469e14f06e2f86b242c76c64f199f61a4b407be136a436d4330fc13abf363d92f98fa86a4cb28b16fcde6ee35b5c38911b45565f8cfa0981dd3b2b3d7821b739f49a96e52c1daa4b63639b1eff5fd4a4b6ac70f0e8219e43fce7c005fe3254c77d509963f9f2886209135710f192595ff03445c95d1f4f634ff80536d286098e55ebdec72692337d8078ca536db91b9cc0f5761c17e2530b5a46b4b612fd89ce25400f0c6c509f72b09678193a879e8b9d8c58c3e7f3b83fb911562486c6753ad828eb740342a5fddcd92f49ace9874c800ea72bd7f66c9dfb826eb120bdba72c9fead5fa0adec9b33fd5298eff041d208b94c5ed40645f1546f02d594ec3a7ce4c6495f85f180fe5eb8cf6a023c8384d2a358aafd126e6fd84d14a89bda0d228177df42fcd0c2dd01e1edd2b280b2aa243d9dbc48d543a436d487e34a53e3790e7fac9882e7e15f77435485efa65f35807b8260cb5a8f733a233d2d70aedb4ed05c23a19e265a492ec2d06612be05209b761cb0d30bfef4b8c31e1cbada752039f9c361baf751cb817e51020288c198841ee4922f0c1e57802fd7fec2e0813b64ee5096e92e06f6f21a250c687020b4d9b215a6f337a23a29705d2e0e9787bf20ec44feec3527aa9f69de989b454b51069da8391ff4deff6c1fdf6634a57d1148a3a7a9778adeac3c4a5fb036b9a4dbf507f45ec7ff40ae0380e848dd7b11e07ddcdc0bdcd6cd9dd67336e78332b75f0067802bf03dc10889221b025cb33186b6feacf88ea9e6f4e732d4661499994909fd8b2a591edd6424c077d894dac721917c81c6683bef68929c09787622b9323073d2127217d5a47c22a6c6b02d45e12512af6c195defd6d440397f6e4ed3fbad7af57dee253d330039c2f2c7d124d2838a39f3a9e5d5265b4f77ca61c969a2b144df534d5512e15411b3e15eb8ef0b32ddea1dc4914ab2ea3ad8e9e299b9b700a8f3bd422da95fd130bf5d7cb3141c042ab601b8a448d22bf96265564a25443a996da06221a2af6bb20c3bce71615c9f1cd6373ca12a2249c7da59cba2e6bf453e6c0083f6231f41c06b9bcb0c3a08c9285b898b2801ae6b90854dfe9be302768f520153e75f881833a8c4fd5fd627d5a46308d37634da261fc639ed6d34825a039988d6ca3eb5028471e346b5cf1daea53dd0a235f8600f88f11dc5f73e406b7811ec23b2fdbe52b8197ea05831f73b7d6b2b3a26668e25084ced3603497c2dd835ceb88b4b30e84ee6d3e0bad096ec0587523b002f9e8f18fc9341fb4b32a7a40f81262b54ded7d0d52c25227928c4579ae2d92edb470ecaabe94cd0e4e76933ad8cd817392c646c9ab45edd24d96fadec1c883727746a9098ef420336131712f579536b5c6ba76d2ca0ae4d2c60a936e99705f2d1e90403f0f8a23c653e0cb3835c6460f036bc028bec530e6d8f2808e3e01a8f2ec0d4296b65bd9c98b5246dfe9a2d74b990e9a49fc47e3c31553e809512f2c3d5e1a9decbc377338c71494793eb6d4cecce6c54d162dd9a00f2edee6a02149965724b3a11c2b5ece31b1e91b10047ab03e1cf16ff005ecb2278a05ed7464d07234f22ab7baec21349188eadc92888816fc6ef1206c10819d192faace854c0a59dde79e7481d2723955c5909b2924038f24ed1b55bfd2d6ddb6e256ffad58ddff825949506f9935316108b221d6ebd457d8a91842055f30909396cfa1844f921e15d2aa7c8c1e0a658e1a667d6d3f23a6f2b74643e93b48ceb65160511703e9773e4fdc8751650bbfdb6aea7433dd89979aaed059ad78e0701612433d78eb2bfda370a5580f731f883a4a9a8994dd5b7cccb81ca6bd19f3e01ac851949c58a1b36bc4399892c7b9d8ce1b818f93f00840e05a11e02df15e8f7e0f6be6ba9eddb5c286a70d66773762dc65a32c1812a2e263e7282b3770105704022494973bd562b2594e8902edca5b424cdd79aeb1d49f447c057f3f2743d5a646493b1e5ba20a3255ef6016173e0e0c502519a562045b24ab535f6ba81f25496c45857f8c182b1e3714c718bb5f605ad24d21da803b09fa38d610c9d85d40470fb6d6dcd1601d190d830c6db9cbddc656aa221c66c1b4680a29c9eac8c23c98cc84211078c3e9a035c8a752681ae4534a87d3ce2c1139a1d514026950e9cf3f2c93212f463c5348afd51811c2098042c4cab49b8c039a8be850381692806ed7eeaa71f12efa0d0c743ac95ed43fc1ba21ba58538d1be5a69b55a33b90e2b0ef273eec0db0cb5523833c9ea3bde3ac495ea8dfefb842075048b65a4a27f815c3c7e61a984556e2c981d394724702f5210ce51e716c55d8dd5cb2c1e4838f4385cd1c62c1e7abe9f96cde42b562c7d13fa635575778a47e73784d1c98284695d5dcd908ee7256f7baeff26d8a1383f482afab04d46d7bbec479aac53b8c479a82cf617ce856653b4080dbe118fdc03da6e1eb6089943e8bc80b145b1e7f06fc56436266bd6e9802098b51e155138aacdba834b24d104c46dd1964f7b11b73ba51a3b7b6087c01dfc677ff3d2052f9cdaec41929347519a7db8d2590f800fd2354c0db458958e81521cceae5539d8c02d189e226e9718020c04eb25bead0b8fa4cb4bcc2b3b4852c3545a34bea388ad347159c3f38beccd4912313ebbeaecd8e27cbfeefaa3a012e4196161cc33e646d78bff0ee66147322f118719e888e234b0092bfb5e18c8a9e8f2d8f33eb44d08337b51c33ce9cceac0ed6664d0452be07cdecc846d38f5fb95c2106d5dbb41439959b1b0604e6c8b65f700ef2ec29ad8237a76b3004ce0bc064e40e71cf7bb62b4636099f556380e61051d9a7971e5b075a68d1b41f7b84721d78ae6ec462fe7896b3fe7d1caea38450fddc568d4b81a142e565fa409dce678fde3ceb13e4bf91654746e250d4c619cfd64e77b78c94c1a1bfebf29111cefb43056c991d7b3a62bca56b3a9cad42f48184f961b6b30c24cb50f5f821877687e09167e193b41262853c31419890ac53cab3cc43258ad8b8b624508efcec98b1a90f7b2edf53600b75d1741df381286c02ac66e71c37bcc75bd4c521b1e707a1232e1191ed4eacfb354b2dcba9bff6a67c7bfdb211715493819577b9fb34d77671dab4aba43290987a22951134ad9f0583ecd760498bc562110a5310ae83c977bdd0a50c8fe2e36c7be290fc3db2ed2700955b56574010248ee32bfea6b43209f6ec684f0b9cab24cde060bc70740ae610335dd29ff06aae0a9fd98b9eddc89f2482190d47c637906e6f7237414f7106f4be4476a6e707c0c1875963e71a087554deffb12357141904ee97c4f0886eb1f872012e5668bc11fd515a2ccaddf0b402c74a8d372b9cbadc1c6b220f14e80e7149e985773bab2824d6111e8e65873c8cbc961916b6004a574388557ef48b6604ac67d72ba9628acc5530a726d75d6293b913fe7fb0238da293dbfd241081cea28e937b04d237071307bf19ee33ec1bf5e91ad1c3dd68d5d5e83e1088f4199e296f69453325a2d88c3b023e11636346b627ab02fb3a014e6dee0a9c9401ec289f10170ed995930912b553e7ce99262578953a23383e174bde227f63ae9ec2973de5883a230a4886c89b2fd32b8327a5a9f764b442ab50951c9f48867a4d3bac6100a85096c80b080403e2df351a6c656dc88d9315522d10f5cb8f22ef865f6534b114172e9560e2f8888b8e6bb81295cd686f8fd55c41299873ea825a6ef7e491825b1e58a8f356e87dc10a6d4046a8d0d7f1a304950198d45109ac199201e9a5be78ac3651f132870304d52960c0b5c349cc1438d4e234c40bcf830956e731e8c5b3bbd3cee5456010949bb103e3030dffa55b5c8009fb8878f2b0d6803305fb899b7b644343e23026cbeefe63f440ad3640f3b9439dfc5141a3dbfc0913e917828421779bd9c58a59920c400de019725af3cc8bc91339e79bf376cfb04cc31a4bb5bd51476fc0c921caf6f35dc5115a4a31a4e047a921c730122e14cc85905948491f6673301897baaf52f36e21a9b6cb9b71f505a792db74930fa51226a465af3b708cd12482c9eb163721c63909fe97c0d2dd56a684a4a390f5282f52d630c6aed1723294ea45e30bc866beea4acce1a9f9277a45070106c43934cb4f5243e8051c89e4eed61353f0f9a0c04bc8caf4c78058b04d1f700d81ad8de1764167f608494c0c18978e4e0cd9c05ba2d8709d80a9ba3645a7e4720688cb7928063fb5e2c0d692a0e54d8850398262c4357b625fede9d63732dfdebe9867e4ba097de97af15f49544065177b1dfa682a71a387675baf505fb2fc1ce94275d2b14dfbdedbd849e065f9c02ca1832376a8ed2f42c06789f3d6f6ba868db29703b15a9db5534184b0484119fd390b6bf95af105dfe87f69f70a1e8bf072a6683bb755bb934bfd81f8cbbea1dab971f50224200388f8dc402e22eacb625cc05f5bd8e635022e0a092899d7844b4bb4b21a026932113001c6492d812470aef2c6d5d7a547b020538d2b69147e1319a527ba26ce8d80e40e71317ca5b830ee0b9202fb90ec8537bb5581561b3c9650ca347da253e793ac66e0c9b150f43a2f149201cf85a3bd681f43cee746db3df7da8dc2edbcb3f9327ff62d6f1bb6d3e1a90e5b8c6236b0a603961d8a2016292a0caf1fca81740f2b7f75452f30a0a81b8dca9cc0ae44add3ec14cd998ade906b16b77f38b63d3a67835744c919ddbe20f42cf605a9e817c9dca01391d4ab187c56e848f97b4939593038aa784863984bd2c777c33b173247dfe5467ee2f2a95fb87f5f44ccad546d065f020c89cbaca5d5e4b333f1571fc5633cd8f52bee1722422c7544253b818b6cc074551c900370fc19f43cf585327f6be536a641262eb75fec04a17b0c58047c313e5dc08d939cd83b4b6399c6eb8dad15b057079c7a06775906d949f91687c2b575b7d6f5f218abc1663ff1a51ccbab6de95c4187dd2938ec22cfe7d8225a15a7403990d66a269a6af507d472ce03e9e6b18a54431448de3d04cc71c14bd8cd9409fbf510ff78790cb03e9f533f50dbfe1dcabe4b4c483c8cf132b44778cf49f26b664158dca9c568aa7e2292256982fa69414fc420beacacc292a95591db1c9d5435dde4b7dc96ff7fcb5d7f22a50d8f46a378389734016461e61ece058534d2222e5b489b2c28ec80d7b7e18b3582990e82884258001a136b1d22e091a4f0ee7a9d15569c207fd51e6e20e3907a62b47efcf8e3b4cffbfb1832ec485fcf44a16e559163e1d23617cbbeb10a45e5edc48ae4f543d9090b1834c34abd5228fad07ab9fff2d116fd7c17d9046a1a60a0cd1bfde28c936990372f08212e781b49d9039f9174be1f973a32c4e120691e795f168b92e4b7e272cc602ea38327a5a304c61b53989c58f799d0c0e0faf22e45190a5b49dc6a1d625e0652686cb5c0c7d9c03b5d82330b2e32d4030be6a6cafa44d152c14180847054e84820065cda496c8d3230d60753513a37db5cb35d182d7dfa3df429b4899a40c96070f082e0766880f619a20b83b44081d82ec521bbf08538488a2f0ad4942ba2d094edbfaba451419f1095fc48fbff0e7496e44647433baf1a410e41dfe72e009c2fded10ff6311ada828c4873fbf5d28264f4a3d09ca932c192457ba2158f619766d2ce6362673c4b74fd16963f149d49da29438b5f117850041100443585b33c3aeda9be1cf6d22d3c66f9ae8c60fe24a59c7934e10ae2fcb93424fb28fe25bce958f638fdf9f1cca932678db3fe1237cbb608fc9c7753a3cf6b94992097978fc7789277df8228cf1d0e3d9e357806ba2ff128febd82c7b759473f836e6f213be8db69c26513b3505f96944ed9ce1718733d6cca876cecaa2da59d676daac9dbafcda59ce529c6f5fd3ec97a6b6d257ba3781a9d3e4f04deb75e3d7524cad6a0ea69e721bfe20534b7530b54e4399fac96df87f4ced24337593dbf00b99da83a999dc86ff832966b94ddcc247f8c52a3fc2e88c4bfc3d5de9341bbf9b8d7f668aab29528130452233dce336fc3433e4721bfe12a638858ff01f99f853e05bd8254af9a0de69d6c46437cdb41b84a9f79069f7ccb707d36e21bb3b98770799777330ef2e4ddf31d33750d626cd708f26d09119e8fe31ed0c3fb4d0fd322d99749730c71ccee270b9a7a727371b5b8cab7439863fb761dfa009fe19fe46aed1376e85e69835fac62d15cac72fa2f826569111230e96982d3438ac22091b1a8ac03065061a70f8c35fc8e5014a09224939c3b7f0e747f8b59e85bf1b54096e4b6648917d863f99275912a5c0476859c5b00273c52ac67ae604ac7dda954ad799137a57db68f420449b3b3cdea8a0048512c294b0e629ea4dca02f8d65a8b8ba094a7bdea00df4e24d064aa593fb29f575b3b4120da5e3b95ce99056b2278086ea32a5b74a0828507ac328983044af889d055f3c0c19a076ebb698bfb1ddc57719fc5fd1a95d27e05534afb368c806fe0a38052d2f011cd6b1848954342ca1a08ee0d1b8323e56310a41c8a535a54cb8034237a235a0bfd7673e028ee35fe8ce66b7c6dfe9821bc5e1f804db3b516afdace75eead91816cff01e850144d8750d2c8f64f4163d684a96cffdbd0d00d70ddfe493d14457164a7d7d5f617801351107285ed8f82545114a58104cff60f0094d7151ab63f062f1445d11c16cdb4fd2f88d1056cdafe48562f1cdbdf026dc6836efb57f09ba365fb9ff0a128aa036666fb9be8a128dae3452c6bfb0320cd5868d8fe2574288ada70c234b6ff518aa2288f1ea338046cb2fd49fc5014753a02d3b2fd29f0a128ea430a7966fb8fe8a128eab4c2e86c7fa31545511cbc91ea86ed5fa4a228fa84054bb77f0d47d62ab34c6d7f131cad6cffffa128daa344511e706259aeb6bf883c435f59e26cff1079455154aa041059f46cff09f28c084c4b8fed4f9467aa5eb2fd693e14456f0cedb4162cdb1f04eea128da34b4825ac46cffa11b435ed66cff998ea7b5fd3f0ca511b5e8d9fe1e7056217cb1fd857e288aa6705e33b6bfcc87a2e88efb82d863fb77f01e9ed792ed1fe42b8aa24fe516aaedcfc15514456fcc68e82d69b67ff92347d09638db3f964a6da961fb035dd584ed0fb3114504b2fdc91445d11c577e906cfff1a2b32a8d65fbffd810c56cff178eab1facedaff39aa0d585ed2fded8d230b67f68e327e6d8fe20d2ac069aaf0e45d11b434c1acbf6f71444d34bccf6bf6509c24c4e79774dbbed46e2fc659e34b4fd6d88429bed240566b89600eef726617c44bf264b19d66f6f4ee38ee83ff18b38bfccf0c50e88e03bebefb02d8c838ca2fa55782dff00438c2f2d3ff8e287262d83b55c0a47c2b6fc38f0459b9be1c01f33688622f8187c9cd338f0fd5a180776b598e906a54548ab33bb295da34e061a4b9fa830fe9ec040a5098b3e277252244ba8dc00a34eb842c5e995b4a2a9933e548290413add939dd1e703a582e4e521d501b16243840a94d72f0bdd32870a16ad75590b10181a5d40f287d26083ba67a562f5c2ab88b2598afdd85c31e3c10234fc758999033b01f6bea440eb972c54f6c0c254468e181a4646972a2f622156305d020a0343499514d603d11e9e3a799caa38e110652306e8c7a64f951ea3aaf3526508a851a63bbd3031821c014ac330515d6290428af8f9c884c91293638da960aaf4783981678035d461e191b2a30a160cd4812a0c3d4daa6895413d7506fa65489518c65bf5c34be2c0a670488113933d71c10efab260d2b5ce0b258088fd7abd5f183a3b22b00fbadfae6b6d72b5534bf487b5b6c0b4db6912650a63c390307cb0c2d8d074ebac59e784d97979a1546dc870f991e152c50bfa998172c5646a97a6c34ffd81e942060c971760baec762a843d1878903d3959705ededb89a99923315e3d334676bd3aebee0591c36ae60a941a6787e7f5e343bb4cf5ace8d8f5293fac5ab0d61f3d1a3aab6c563e3982449c5042af471a29fde3f106caca92133d2d2e5144d4ce0d98cfce932b3fa1ee0894962a71ede1325aeec07465806429f38425a54aeea57360f8e64059b9fa218166dd9a1c6ba4585913d4e325a93164caca16a8fa96a82c5cb1f21383502b335396cc287132e3a467650698cf55335050335e5e693b49ae63b2b8fc3746ebce3a264e94315a28939ea94e18b542d342cfaaf7646506aa297bce78286521b5afaf5c7844d6ec13c5be4496ecd3ae1ef669abb0cb66f7cbbb44b54ee9426f58272a419038144b05cb073eecc33eec3369341ad1041384082142c49bd934b15f33e8a6faa9d4cda65eddb6bbaeebbaaee99aa6699a5eabdec5f26dadebbaae6b9aa6698a732ae24db3562b2a321a91478cc07e4770f000df40dd96fd71e5df6fbda9cd36cbf7e94e5d755dd775cd6b51adc8c868c4080a282061cb361bf66b2b91748a729d597d2fff3cb0d9fe67b39fde2a7f33cb75739f7f6208e65fce3f9c7f4646d79f02290a966c7f125324c86bdac86b1ea925728912d86f090d44d3e0e562e31a18dbb60d8a1aafb807f6c6daa94bb007ea6cfb04c1de098e00da165b6c61af2b298091def324a197068e40c94159da20b6e53dd97a6266df10dbf29e4411b12def89136d5b9eca07948a6dcb535fd822b6e5a96372a862d944dbf25421e26c5b9e3ac3890b591719da96e7a4b72dcfc9bacfcc73126417008e7e916af610d54f93243bc4b6bc264d3c263c2677b69a4c32f942db96c74409886d794c7000c1c8f1270644d30068e307dab8769620850bdad1da27ea80ad956ec3f82db6165b5992f537d6262d21b36dbc1f3f285da2d07064007d602ce53022d5e6cc963a3aa440d9b1945b53aae0e68d53112d310c19b5eeff083d7c68fdb8a1c48ace004d49f6fdec379c91d48f2ccec893dbecf6d7a5079a40c98a22841783066e8332328ce1e211430e8b4cca2e75b9bd0c31339526c90e1a5e7025520b303860efb80fbc616a4e0a679ec849b284c8951b76e840b1a460858a0f900d6e6aecf4204923058b9b13b25ee843278f962724b9861ba844e9d284478e9d3e67c8e0e8aa91a686902b3edea5810e35bc3d2c1ce1d28252931974f6f4518247891e2e180d5461a0cc4026059e8e061a1e18363488e4bcf4e1f3eab3cb13030d4c685670f244043b17ec3411f2232523f2b9a5a00aa131a6851c5242392d36ec18b32463863c2417942a43ba84474756983725b0a0ba51edd8a0c347cf807d31745c6c6c39b9c1e65962ead36381c7025428833ef2940a89015da304e1012506a82d60b4cc8c31480088d49a1c8e1856cec4e498e0a3890d1e43f4cc10dba324830d9023768abcd0d5b4802168b46e5465edd070613d575db2b4682c6561010912f5898d3d4650d0a3fbc00b797649d85ce9a304e7d9412faec4a981c99e364d38fbd603229e385578c0ac2908794203777e0aa51c1553d7993a582618c1dd47ba7c2c990dd8c1eabe5954d0d3654b1b2653948a34ad3193828e1a5dcae0eecfac2c98771b8c2c210c43ca8f4b6c4832b325204c7240c048532e8981c5d07234c64081ba602921974c0012c375471767eb49d52f0344563729d488e3630d53950f5551258c784986790c8d557dfaccc6a52cb07149499078e7c58e25ad5a172d3bc4bb44063a9ba185c38baa9810a1d19ba431724688151bd8cc888acd5081f295185113ccd05304036d46ee45130fe29d18363a7479628278474618a318738648fc82f1ab41aba969852b15d8c127090b6564a063e6051ad997177382dc05183366a38c26b4ab3563ce496d4619a31a1f988608c978f2f005069f204ca62a0618302e6876d89249c145f3f2dc3096d896cc102c9bc3b6648624d9e7cc470014f4c2438ce825470d286a48de14144819d1876d7978aaf478219d947ac4b1618a173a63d4e0107f2a5a90f1650f9b3e3c2021d663df7240bcb34fdb46c0551e1b51b4a8f8ace0ecec2ba74a579e1e2dd0b0410b97012976d8a07af8b8f025012fbc98b1848f9116e47c09abc008d9f24d5941862e4c706729c2b04d6429c3030cd31c1b7170f6ed06c0f007b0558e17b6e2c84be92b94767f3ef252fab6bc24b91daec8be45b8fb8619add119bbe2873510b5197400e8efde5a25fe51b5277e1e016cca717c0b5b8180144ad8933013015825d0f823698e3f5ed8eb0d833d90693788da0c647b7c306733f03dfe6bac6dadd2f6c087991ef60802a9f604f817f4294f10dd3fe8388ed864cdda13632dfc1faeadfff51ae86dddf6faf031ad01fac3308cc1c1375c06b14170806ded090b7b80c55d742c350fc9d6ceb6766a18075a07d65a80293eca6f410ddf6cddb6e6842a4cb67047a6d695002f9cb0bc8821f3bb34e8aca0832125630ae7e6afc18da1a8496c7a972d79b85e24360b30038c16eefe6ea02137926e4ed003874d17564f0d56a6702494088223496cda352a6851716e165de14618894d17fe864f0a707024fc8841a1a9e80a4742072c1cae8930d851c31523b1f9fa71152609ce4dcba50d6e04140e9b2e2e9c150a8a23012351c281c46628f5dbe1c38d412436c315507831851b33f60bb31d3c842a0d45509c9bd60b131c097e71960bc2b8d0c3e5cf79c482c385cb4a2f255c4c09d7e1957042af84fb806b340ef087f89570fa95704ab8f2957041af8493bd12cec32be19470f63f98764e69ceca2d5907f3a1a4406a52c64c141db4c047fe25d694ef7fd50f40e4287fcc1142d895b95b8194f2d6aa004086f2bc4f4c3b250c6536af0e1f4d3129ef9320a4741dac61b44627896efc2fdd7e49ed44aa3abf8d715676e3b62eec42884e695fa84d695f05b6a63f688ae0b529a5ecca909c1265cab0967d19cb6d2088944ee519feb83c4936c33f757eb036fee9e199d5deb3f1e752944bcafbb21f5fce9344b81498cbe1dbd1e307d3b02abc0ad3846196dfdc9da96b8f8a90ca6fb7cecdba6ea4fc76d384507ebb577e84577c0b618fdf0060e2f183597e3bf1f841377eabe0f1835d218a6f163c7ef009df901e3bf9ed82c71f36f90d83c70faaf8168047f1f82d9843da27982b013e9529ac50982df011fe00981ee023fc189897ca8ff05f60de2abf3bd302f3aaf8087f05e66df223fc274c00c0bc527e84bf8429e2adca153fddddee3aed2e14baf167aeac991bcf585ffb1f58c093ec078000dbbbbccd003f03ec9e71b0274216af84bb9f9ed6fa364d8b612d9f22601aae41c07160cd621d762dc368afe374dbc74e595d51aeb015f9b10529bd76ca664430d8858140340dc2edefdb6b2705f68e6adbddb6b595244992ec6d24bd750fe73420a841321cc3525fadb50581f4b5800213caf363ba768290413795d4ed4a52b65f10942f52afc86e1bb53886e09847ec770cc10f0d11bd1a4a0f53e5198bf576ecb7af88ab7d5fc49527f9fbdc16cbaaa1b4e7dd9ab600bc633e7cbbb1ad55c676185429ef054eb15f8996b9d82fc625a2978994f8c8aeb877bf3db2af758994e50d12d2c66a810f9a3133c8a0d2c0d91e94122e19603c695205c93e0b6c1fc97e0a2eb41b25be37042b2eed46e93f922190019a946ca0df5b25b82d17291f9b167e7e70db17ef25c7b7e2b5f80133a258ac4614012520ea0b3d3cbe3051c302cefa51f9319343ed24776c8ba675b169fa479b1108e4c6f80510559e331c9e220099d9dac211b49ee11951074898b9b54af2c77f3df976bffc453ef9fa4752fc2b661c92b56b6d200edfc5d79f10dfc3c79a7cbd6a44e1e3d7abf0d9ebb0d681fbafad55deabc13547e09704e0b53617efe90245affbe3d349d8e2e7fb27f467d88ad75f2dbb1be420811a2452ecfed820be8b567ccf2fae00b75b0c14be0adf59b4f662a0b06641705b23fc63b4462af0b3904929ef9f4adbbacf6a4b1a3f0b1327520d9f642c4989c465e3478154c40763fc4829fc5ef0c568952eb616dbd8bdf7c6624018871cfc31ce3309d77d862b26e87838a979b3a3078e17aa00b9a2068f1a3d53422c613ee244cac53a25a6284d2b9ae7776587528c2c53388a3c95c9c2b2810268e30ba8d199bf915a612a47560a2500915a75dbff81ac329c92e099928294295a098ffc630c677431a1cf6882e64226460cb75104ec62230968b43d9cb1596802478bcc13115e0bb813694a1130c976725bde14de141b7540f0158afaf57abdb4183ab8ae29af2dc59081c245668aefba0840149e1f8e840f3edc385ae1481871638d4978b7d5faf5faf9f9f1e7e7e7e7e70441b65f9fb7ce4fc334fc79e35a1653fe3ecbf7b7b9bb97315f4b34bbb2f28ef75ebff6defc17ffbdf7cb59c6eac881df6c15282b94ddbe9f813abc2853971745b7efadb32fad85c54779c4663647f3f15108024f79e67d5b6ca4a5f2e98310cd7f963821aad2be85dda92025a57d1042374afbf649739c990cf04f7a51826f2ff9f7de8bdf92a1f92020ed35d42e6a2ec6276b2ec87fd54e25dd6eb1c3578141e062fb5bd21d04a41dd69099466b74ea1819e310d44126948584b05f21215208f47d00b366e0d610b867fb83007fb44ca361bf34d0973e39413da552ba300dadc2aaf02ab40ad7304dd8739b876bc815ee09bb42aef027328968f813a7442971893825ea4429a22aea442c318de846c4127b6296e8137b8e3348dec72f6cd2cbda7aa8cd208d1ba47bed3a82ea0e5cadc01e964ea7d3e97461aaa538e8f3f97c2995ae2aafb6a77103dbb9a696333083a1b8aeebbaea344dd3f405fa7ca00ff4891849180c0828162b39640e1cb05f0eb20da2600e4c9f401d38a5b14adf6ceb7f36ae704d41354dd334dd812b6805f6402c1206145bd7755dcb344dd3f4833ac86442421e3c7c98e5d90cfb9d75e8652cdfd68f4ba7d3e974baf0b782e8baaeeb0ae6c0147c0275e05407994ea7d3e9843cacebbaaefee66c0804081a8d88688210394408ec3744368376d64d657567cb563f2e54d485bfdcfbfd32d6d68f6b08844ea7d3e968e12ffc4d104284887fd3ac15e5a222ecb788c3fe61ae9ccb4f62afd7eb893d1d3e72dbcf74659eca6adee5355b8510a1d3e9743ab3565464643462040524320912d82f09525b95bd1996d8c33f9bed7f362b4b8db94a74ef59eed3fca4355ad3aa188a3d50ec6523239dd34cba49e734aa9d74aa51b7f9c7364e718a6f1a4db1d5c63d8c857d784ba3f8a6cb99aa51add56c026d15f886d3355dd3355d29c0a9104c42bf80607b029cda13fe384db5ef4472bda7343e91f2befdd8bd37ebc61fa2b77dfb620866fbd87f484f54adb5d65a6badb5d65a6badb5d65a6badb5d65a6badb5d65a6ba3a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2ac8d8a8a8a8ab2362a2a2acadaa8a8286ba3a2a2301e329a6e23b7b5a0ad9d2ef6bd42466b445e9cc150cca288fd8a98c46028ea577ebdb0df574886faf533929924b15ff287fc216140b1329725f65b92402587a00eb22c9361bfb21fd79e2eae1fc804a2bf0e289387dc87593a948786b0df217427856aa756ad2adec259b80edec27bf00ffbd22727a8a7544a979a21c8b7caafb63fb6ca39a7d9fe60ee85390cb1df30af455be5e81a28c4388ee38feb7cca555fddeab75af5b07c5845bd87a2288a3a966fcb7fcef552555555551445511415c18103070e23643f7db94a7496fb74b5ea61f9d6755dd7354dd3344db14f555555557d4af5ddea561afb54555555f5853c146d95233aacd7eaf62e966f6b5dd7754dd3344dd39f8fcb51cf79fa24a6699aa629f6611f0eaf5f0987ff03fffb1cf8d75c30b1350eeeeb9a8bd75f17f7dd5f0987bf45072a5858539b17f8cac7dfa203152c1c88665f84bb69e073a0efa6d92f0193fc25f0d75f0226618d03f1ef9740bfffcba4d91a0dac41c05f3f07febac6c17df14b20d690b0518e12098c1ac58b3fe79c7588712d877fe69c33066dedfc804490d25a9c33061d6b51e3238c31b6d662077d3c328ef6832d82ef0e5a533441986dfbe0db1a0e9f06eeec38fc138777abc0e40ef39f38e70f901bfcf1c424789e25185ed80a2b92d83cc18db17e91d83c1d481bc139875a7b2e8086adc09fdf5e117f075e04c31063ac01aea1a82121bd604e4a133631a5d1ff98452210c439bf2c2c35bedd1ce23084ad087f4212d4b157eefdd0b0550548f1396fe036ff0ca240ca6058a53461b38121884269f41f8a9948a6f508aef7558002aaf68f167764b739ae01e08492c7679f796b9b010d1b5b4055de325a8333f0f0fc542961dbf278592393f2675b1eaf0d8f5726eb04f4882a63dbf278eb0b8aa7f2a644f1155a5df2784cfb9cd1d1b3b2e46a0275e8448de4c20d1d5c6b74f0c85909e239d2e4709323ab9c09f9e194e554877795ded8b1dbb1fa91dda72e1d7095e38d9183020d63d4ec13e98718a41a25b92d6f8c96a63151e68c81a252ea6d79639cfa8c9191a60cb7e58d411d20864bb62d8f08179e1835cf78de7dfd4ffc77e6e669c9ec7a5b84fb72335b7c947f0caf9933de2376775cc65a6b6f17987736d31a63c7a05b6bed4df251de17d98461a85f76367474c100b67d33e39cc18c6f6a45f2c60bb54730eccd83d9db8e4a1ddbf5adb17d035cdb6003249b4d458a221bf895bdf22d00686f308a64381ba2dd9d6b17bdfaf151b88b8ae88ba2afaed1bd76e66d6fe6701dcc203b0e63f0b567a6bb2fc9190451d81321ae2181b593dc20e8a0e313049206bee79208348d4edbe70fc19d419d1466ed201886b59901bac85194e90fdf1fc4d63ac9b7b5d9ab70d1eab3745b58b30e82423bd794b6d661dbb7f49efddbe5b7991fdd77a6ed7eba6e5391beb3a7ab3ffe64647bd3c672742edf26f00b5028f2d07a867e6d11fd8cab3282acc0656948956ac59560b0c0864d0d17ee4ce9e36ecd9cc101e7e7c2963d5170e7552d60420d3eca700913478753b262260d8fce12a7a52ddc79b74e34318149d695b58522b8f3fef689ad9f1b28415680410ccebbb459b22225eac75b42669f469b7cfb65934f5afd361946cb26c5d0b011a08413f9cdab8c36cfea84fda3994acbc548ba4faf029366bffe46428e9a279c7e6b57e8f20b1e9df428f8854adba48ca2e88b1f8ae2b657f9d1fdeba50bc5c67f4d283d1dc7e8f2de5ed661a9512a3b802c0db6225f2f6d367e109a0993d95010f6628567e39fc1566099ccfd3ec1d225743fa0a93a61e3f720a24a4d1fae283c1d6694a4b4ac676a7052a78707a763a867dc662074d57409b3f1cb602bf06ce3244f55b45c69e3a34a8a2b6d159123544c9cb4783cd160c1b431c618638c3bc05660cf77441a3303b0575b9cb6f3b6acb0fdcbd9bab4fd7f5db3fd6d281cd9fe489687828fc785ca765e971256b84c5605cdcf4d2ab316391b7f0cb602739527791f0c8a7d121921609f28b6535929a9e06cffd9142f8b9bed5a8e6c4f8162a9a1002bdc430c58e0ac702b51e14d1a33172c423050860e93a7262a3876b8d27a890105831e1c3a750ec842631e6094c058e0db0cac152129cf99c5ea604694f36bd88a7c7b1be317a98878579250b0fd43bd77494a524696b2db6bb3f16799ec0784c79b3736943182e294d29c992954a913346170fe98053ebaaf616caf5264fbb8dddd3d694c79ce66332bbb8db14c76c54cc6a48c2c65b7828dadd0d06bca939c11f176bd0c9ec8686faeb4bcddba2d6fa7ee73bcb3ef9bd896b763f2cfe36fdfd956e9d3b292e8076bdfa7599126da6f616f63ce1e5dd2de7eb0b4146b91b4aa776ebbafd533e579811b365edab8460b7b3f5dfb7631fd98798f7674f27c57d5201925fea98140eefcdbf36a55b5fe60e19bb6e24361df0ff3db170bdf2cceb7b57f7e5ab5273028a2c729bfdd26eaa852a5f124cbab02a533dae738b5efd81bb148dc39fa9aa652d569e763fd6105a477b13ce97436fbfe3d41a67d825bf6fd7b8a7bb693ed44bfb8ba2b85cae80ac53eafce4dd65d3ab1cfdbb5cb2fffc5f5836a156bdf17b9342a72895c2297c8a5519dd3a9c8a5755777d5bbbbba3b056e41425281b4923f9ea1bac31db85a6d90c9934ef009cc813930772d2e7dd2812a9b23b087d55ad0292d0ef6167b607be304603fd6682d7016d60256a3f9cf6d176bdfb43c1d4befccd3755b876f7a372bd5b7e9f0cde2a6d4ddaa557c74417005ad808420c2ef87cde003bee29536820bcbe5e3033fda45996cd676d3666dd429d7941b3ad2464612de0e15a6ec9d2a223ea87435e9d9e169ead4e449b4fbe436a87b5f6badeff8fb37fab7d28b247266c48931c38b2c383b7543093e186cf4aec01870f7ed335e30564b7c42f01fc1df830f3cf0a45ba359b779f62fea2a3df8c06680b10e4d6aa2eabe28f998c0b3dd843d724f8005f6f63704d88cc5d8ddd4b4dd84d35da7f870e75181f2983bb6265e6cdf12c2081e0499a8a8946c76672d0c91b211000000002317002028100c0744921c85712892d6f814000a58923e64583695c8a3d15010c2280aa3200a611884100300320629a58c328901af7673563030c2f6b076330d324136a376ba9b9304684af80ce0f103a650f70f95e22564701e2664850f830cb688415634633fd57b2eee3515ec04789af8ccfdd3804ec1b0d4cf0d6ab81bb4d058930027f2c593c422857224cd21e9b7f9b3ea989b2ded11b102e6893169e9cc544da87b485ecaa1513937be55046e12ab36a1acca56ba782d4c4cb8e1655b630711a43308ff1c84fab41bb597163235b29eefca09c712028961555003151cf3ca4c90bc591b6ee9c40b4a3824f61d61c921fc85ec5b45a42c3547d36ad9de822bd57af288e2d62273da847b2bcfc455c4e7e4cd5e4b0c4dcca61ddd2c081e3bec9a328928f1119a0d339bf8000d960a58889eca166b360242c18532e263d0acddc801b433a0c6b238d9dcbf35850e16280e0bafa2d34bba69552560d10412e4d1dafa1049c80631c751615c21bd10d0cb08e6e8bc1095326abe0df23431c94f8a87e45faef0a33a7d9cd519975d3ffccaf666a8113f249e889dfd3c36bafd3ea16abac94bc79dd0576062206190edfa2a404a58cf3ca9a601bc0102b250076983d23dc4a34508ae39f6e38c63c3651ebe1f205c9c7581e8ffb142a39712a7d826a06120d25116be90fcdb22d69da1a4f6eb1d5c966b74f24350867059aa168d1d2cfb5d91b1872a926107a3fa258df44b17e71c035b8cfa05f837c774a1707a835c3ac7b1445866e8d4a594ffb83f341f611630171e50b2cc01dd686cba916514678c5e6560eab089590fda0903b96c8436c9a004c0856646650a935e3a477186803f155cbb052f2a827d9cd337d3ad58d2611736325d151b9fb188156c2ea49ddb027afb69c9ff283c4e6fd168607c385e36a5bb79b919e612573b6418b450f7540f90efb0b2aaddf444c562f00c2cfc0843f68c905603fdef5a8ec062f3cdb2457dc0fa6807dc06de588f986dc0573bf943c53c3e4686ad62fc8b06b857f05f2754df1d211d8384cf25e13a5c279343f583ed275946c8570feed57c789c2ca2484e306642c5749541e0edce16799d1a6b4c8581573dfa98d0a8961a9d57e6e6e1387fad4a81ed1848ac4a3c105e31f686f03adae7a3dac2ad2298aab7823fa073806be845bb93c9e4ec0257c0beb96dc5d54036922cd5bb9fafa0f70a40ee6b4bfb61f94a699ead97e783c8a4a62ee09a932c7410fc5ad880ca902949260bd9bd800855d04bb632499393b35d12b03f15d0418dd5326287abd2dd238c763dda5d714fe19984ca996284a5691065abee69940f504c4d119ba3433aad9ddd2cc887f54c3ed17525d3403c46b8b1e79d991dc38e80ad1586aaab48351102fec2bac620424638cd3ceb30bec919651a6f2d10c86cba92994f0ced44fa71d5c9a1130b642e8136733b604819614e7372f8b8a3ead549bfbee38f83ede79757e3d15805f08228117ad3953f18d71dd6bd68e4c0e14be6b483d1043a1bd49c31c07c33fb1f2f225f083fbf154bd8f4d7ba60452042ff6bf00a62c4944c341c122c4d4d4388b6619b4a5e1c3f04f30ec82a9dcb63194eca31255c563022988b42174109316d07a6e68f219caafd07e56b1ed11ddba1f234d3a29b31bae08f9c371e013cfd2dce73e41a7fc79b552703f17790ba1cfc8f3c67890ca4914fce5bee283c66e73ebe14e5bbe879470c0a0ccccf77e8ed0586ae41b97b487d009c4437ccb80da946f5cc879f1e80625233f39fe1724b5df828b606864df9c6334220bcac5bbf931df973409d026dc05ae24fac144cb5c342c114768603a4d85b566bc796f36927ccdd846f3544dd050142c098c1ab40b1d6eba265b9890458d9ef40975253870d6c0300f8b8355a05e448b0ff79bb1958b099e982b50a4ba1e5e910956fa9015e882497abf2e43eac1ec588efe2c6d64c915eb132899086cb0339619e8ad0e821bae267e79a284eefc3e70b7aff3b41f07d16bc8149f1340cd72da77ce93952e35f4665003e97c08682f87b42536e729e28b7af1d7b70c9e05d583dd82cd4b02a6407f8f4b0ca22e2dc18fc42799c189e7e52f912e12fa9d0aea620e8023c900a833186250a6fa82615252bf0e24cea1c7345b4da26f863c269fd174bf3f95c1185ab0182e87b26ef6d8d7cef79f043f1c9047aee35ec41208361db52b709e696ad768f90a7112286563121d007f01278d18b88502011373c66f4537aea02077576ebdab3f11039f099343c2f7ff764df96bced34937675e7a713f640464654464e077ed3d91b3bce01f0b309fea23bb04dad7d3c3297aba80c12c5f30329ab8d4d152122413a7ba70087a88d890fd286f298f8a00060cb27ae859448bce52aa0681435e2abd5329d508c11ed075537744d2b21d527f310e43e40cf8794701eeac7d94467eabb3e7ff7009ab418571852d60a54aac8460791c96d94d98126a9a44457751a883d305c00cffcfc44443e4f2a9b7b5e85c827be0b5cb69dc11e4535a1dd6242166c5f290b63e6846320951bca5a270e9bc9d7b2e6b918bcd3945b3651cfe6f7c802b78e8e74bcb984f11314d31d56b2a00395510b880df99fd51a7d986218ef16ccb8033d7ad6301582f822c74b92492e990ade9ee78209d619e2a140948464f3c82a1e1774004b1ce11a20f83fb14663c7ae70e66ba7749a02d8a424956f3a5d99094d770ec41801fc113690f21eb03ab6a04898b87c517d5c08fbb6fc9acc145274ff4ac530b2f4d3c2aace493c87ab860ae1a0458f8a2f3ec562ce83114970f6212c08da54976f018e62b28107410bb312f3e2b48b444e32bcd591246708fea89cb5fdfb4d6a76ee7fd58dd3047a4021da8e40d1a27d86ce5f8096a3d49033ccfcb627efb896106c2249879e6375bf051d0f264f9bd3f5239eea72641a928f2aea770bc6a574e7155c30510dfd9129b2ff174e330fdda1b193117bd5366dfbcb600b8c664e810bbd2e58cdac90d63389c5af93f3d6e452dde42af0167b19f82316e3a5a7bffeeb724de2cdd3ff2341ae45c7f57e35edd2e695fd5b735ae8a19ad9aee1c06567b2320f926b0b463e4cc953ba1e2c0f23592080e66a387254e499e750a32c0200a84f354c33dcf777b114ae8a35de93eed8e378aba20c535d5fe4507ea73339169074f84c4270c7eae51d3ac8f42371aeeb4ac66e4022514520d1937770b21fa63bb01306504793936ee50441d8afd0f572dfd1a6bf5c95fd8fad8c7b19304e37dda57f3e8c0716207f3ee548e0ab714cfe6dec63c35f013d15672f93b5d94aaaf389dd7605e958eb09c76dcd4fc0fee8679c4fe35eef478fcdeb0003cd5f03339fcf7b69daab971da7aaf3da894058077d4922a6f9246d4efea9879bcfee9084075e60bb59a761900043b65187cc9bc561f8120c00437d4afc882598895a2c0835ac29ae007c51a5b79bf2cbdcb316052db5fef39e179ece3f6533298f8ba41fa5981b5fa4e986c5419443181ad7c2c3362043210f29877c74efdf571b86c5a17c97cf6c62195bb6687cf0098a9b3d66333f7403c0ce3f71e8c3c7c537dc54f3280c586b1b4151c875261bea7a46db9bcd8eedcd5037ec140a327d79926afb8819125117f0fbd011745fb9a1d719b800bcff3bd52ed4d9155c668ec132a12f3613ec843a6d19de06c64d55570fc68764640df1323b4337186063f46c4d91aa8a4295f5b73af841e66ee5d585555400f78076e76cf3c0a3f57aecb93aef4a5f27c4b2522ddae410725e0dadbfb01f8c362b50a809816f5d7084f0f1e0363aeeaa0cbf423e9ec3a8d2a34bfd07a9ce289f9e948719a04e8131aca29f61e9bbe75852a1ffe8515cb569f06971af2942d4682dcfd90481a4ecb5d51ef55d4a8338364cf8a53b9820d848da0da9498b6888dd270e7f6dcd16c8a601d3110b198e82e1a08527241e2289bf706c21d6ff693b9d0542960c6a85a3ea2deda488c44ea0b2a0620acdceb3ee0529dbbfcd5ba8e74e6ae139e9ee27ca9ec99d317aef6330ab33fd3d2bb9cbd1b354bd41dd3f593d09534fb1be5ff9987fe37c78946f8520d00f5c0fe87fdcbb6d11c6a751c4f75a8f9ea824fbdd6b69d6024ab19dd889911fe4be2a7d8e361ca8655140b01cfddfcdedae423fcd6c843086b61b020f2f0f662127f40d1526c3b39c8cc17ae9002466e1e13766bb029f1560abddd7f92575d697d85ab91ecb34c64ed6a09d987017d66827484c52c619e2b62674f67a511d80c1ab9179faa0d9dcb1cbaebe3127c4aee7e1562a5915c1e8189855adfaed7ce4f93649cf0e4a7a2e92cb942289c4b4481ea04751ca9a21848b15267dcacec11a9bf960afd30c53d808e3039d7e114807b5803fb36c059562362f614fefc6a97ddd87522d87198c20cd3c0ab98df8b69291de94e8c7b8d8f164fa0d5ae5d27ed12e530fde7517f319b4ec7344496f4edac324f63cc4e5b88f689c038cbe3622938d6fc1c6875824fac87b51d961f1a0e2162b01e98c1671b4af027e4cf09b82d6c412087e681acc5e20c42279191e39a024126c1a32cfabf82d58b3dcc3726fca5864dbe2ce05b9c57f2b14ac192ae22ab47f240abb6297dd02c34486e4f1ae3f18ba6194359ed925b316684806e6f4a4c89c5d5bafe2618251650eaeaa74d3d9ba6d5dd95461691dea287b33ff75f2953150061281e514374823b40fdfbb9efb019915bc011f98ccd4b00b1ab34610bf07f8b67ae38b92671d0fe7705d3e1244e4030f5d97b379a5600f4d6d5966c53752cd51f83b0633cb508fe9595afa33070244dba4e656c1624bd57430b0c07ff281c484bf2f544d25583d40b0f065a78158228c8e5c63c8b89e91f3311e62b9e796abffa9a8c5aa0dd2485570ab4852941d3b301fa312ec400a20a3a0a1c6ca86be27afaec883e4c620f47c9a9b667e1d839776790ebef81b8226687f4b81d10c84bc44ba941e839c30c8752f65f948468c2263897c5f716ef60d31aea0694cb152b518f1f38d9e363eb4370c54cec1bfef3abdc29cf8f5dc1daa30a3e1783879cc4b9dd39c58e6a2a788868dfb829220a1e1699fd5bcb1fe10b43fbedcbdd2a2ef9adedf498300392bd354c6ef1b587aacd4501808e308dfd9b0e50487f92537aa1269f056d4370f4181c8cfa5f98bd0ff3b1bc56b3eaa0543201dd4d03d630a5aa02f7fa6121268f8296d326a34c330e841c65fea35a0f1d49c27950e78453c13cbb563afb8ac287d2dc5978c4a8b471143f9f2fc99eb60eb39f220542f1a2b4b3b0a807893d86ab35276efecfd96df727375239909272e1d352c15416ba8c1b4a143192fb57104d2bd4fb05b311971b9cbbf3cf9d50e1106628e5b007f3832b7ba300891d9de5ca1968b9c7f9db39bd4ff2b51d054436cf4b89e5d2b1f4aed7ce2f10998c4cab03c51b42a3d716521230f1100f0d914b42014ef796669b73c486a04d98247cca0817ac5dae5b64c93172f6c2b9c2ef6de962fab4873e2b641a714d4a7ee7bfa0558eee87718d5f27bf794559957304022c0992940d84a7aba99b898f023aae30ab0b1538a4d95d2a2dedf1ecc621ffd8ab90be424fc2d121da63cea8a74c0ac927a5df4d36a38e054381095a0f10bdeb95a4b36a267ccd2e06b184dbcfffd2b2611e085ab33167089175ddac06f37c1d871cd460ed6b9683869efdd1482a95cb2bb6668304052c64ff9bf7ba5d102cb6ff09ba82df828120bbd28221089d0137b582e3771f62e8b8cc978f7dfc54368c15d661475530ded50008d1456ea26317b93f0b49aa653a6e5d58c282bd9ce0dc16a596d02966c5856149f8a65de45f381a0172fc13c1959fcb532934c5625463e4e457f9ee4a9faa6f5046c3a802dac71b2fb550ab665547bd9549fce3a498d324f232d1a76564700c97deff80fc669b165c1a00526ef5b593b970ed661c0f687ceb93a9a683421ea1cff828d004cbffb457aab8583e71022a19256011298cce5323430024391c0e415465fbfb13110612880f895327b611c6bf6bc06997e88aefe730e8152086bc0ced2045ee9f78ddaafba496977e2837a1394f126ae162248c3580e6fdfa6ca24968f319df4c111cb5f0c3a01cff0ae8928e6b7dee4a500a1f66fdc8b2d196eda6e3e6f310bbcecd142ad019100ea14abda0cac0fb4acec566026d77beb77c61b20875974540cd112d87e1a034cc69f4a1f4ba8ba6f5ccebb53579daeaa6438968fe82418a15ca20d527516600d77eca800faa6aeeb72e09aeb44407a3f969297e7a9117c8bea70e75655cb49fb77b3295cefbb124c62184e1d2e109c0013d2112877d4c9a5c376a9344fa2a0f83562018830e1c2a3404eb3b4604afa8c6493898d0d96a812b198304072368e89378198ac4d3ef7d2f31a157b6a04039e34f8009cc19f2dc9ffc04493774a83e10b3c1ff8a26a86b02f419f4fb3e0003cced74475d6064db2904a196000b044e118f8a6c07b5a273e51918f375c54920498e5282598e3a7189cf4b4807c46e407110535d8a54398d62bca35cc1cc66930a1eaaa6883bcb27b0511f65d757bc73b50fe9344624ef1bd0e7419beee6f08951e6d82b52407d74a38cad30db912bfd7c5942f5cc697e04edec337b86def3ec42b22bac75e2c1e4557a1cac08b2001d40e40404daa3a7b30f153f7dd0bec58e01ae5a7851f2894bb4845280cff70c77cb52009cff5aba04375160e1811838267de297f934f819ac4442585fe1ee9a4e16f4a62d99ca2fda924048e662bb7023a67bcdefb03d038c64c5f918e2e3f9e2bed73224142d4a8623dbdd1f2c38ea9a8341e4a227702d3ffc818277c8a3aa91f12c1c5197d8204886fe557402303adc0c70460cb2a0178fc9e31230d54e8251cad2398529f683b999a5ca8bee1ccfc4a1ce2c10ae65ebc39cf1d913ef1011ac3222815da8a055dab5688467d1b9c50ccc57a8ebabe2f8d45b644e6befda3356a2a324dd68d052640a840a425d32c5981e143923cd03429faebdbcc399e1ffd7d329e81508715786ac21213364dfd4a44aff97cfc11dd69689609ac8d52a92ba13c143cfe7b51bcfad78a48efbeabf5f94316fb9df37a326ad38ee155a048bad30b880bc1519abdbccfcab72ea6cc6e4d1f0d75b4d8572abe7bb4da8cd8378530774bae6c6bb5c30c9158caf56feee6772e9ed5223aaed4a0372782a4353e5780c32ce5e29cbfff652a4402e0ce88060f0815b8628161c04303c3f2046e4d9f1d30079178dce7f46cbf710e12ed3f39cfd4cfb45143bb5121390d1483b5546edb5200248a0295d0c243750c68e9bd4dd52a86a833d30013ae1c20338c452d3a990228824c1f4d288bdb8f0fb403ee493a2502d2ac749e1997355e6c670bd89fc4f66a3c5b033687e3ff0511965d4e27c0695d4d710487b9e586af0a6ee35c6f689e4ac01457a1fb59dba8c293ad7d953d1456be27d5704dd73e84ddd2620c655cefffd2cab87d106952fe9362fae2788842fad2c9c7c9f31e4bec230635ebe355de17963306bbd7fd810a7c343f89972e00586912c8c4efb5175ffeb2a48c4e677e9b84d9a73efbab812a23d1c4ba7c7c036ffe48a4140f22554bb52cf8959669c1f0c6c1c01ff10c77c6028e2f0e0eac58865842ca6f89c12342d934b626067c39ed828f689dfa6d6e5b8723a66b312d695d35c4803ceafcd8dcf6100de6f61523085aaae9c325694aa26a431d2bbc6943beca5eb819df0746fc10d40e514352e020e0477b6811304a2fa6b530b8a7206f1896bed0bf6013528a4c684a9ae63064a2c0d6ea8c9f2d90fbe311fa738a05ee280417e826856a5d5a846372d90ad1deb2c408413d7016ff341fdc4f0bdfc07f3dff6cb5fe11d146aeefabab18a57f464f92120ee0625f0fb6d5008c7c413efd1ff30fcad20e42c8a191c2dfcfa77e82876bac4a973c2edccbaaa99a9da6feb97935b4fe7d8a37fad1d19ba1505bc73fefcf6940ebc97f17535ec4cd7811e6131010dcfd905b18795389856451dd46c02ef80ed0e603ac612c59a6103161c69b1af31165ef717358f280b9bfcba5953923230f466bfcf265a2f28728ec515d547bc0c0676d23423623e119a24ba561c20927320bd0c780913754e772af2c51f63b575bd4d92c3c935a930e195bedc4d2d182e7e178ad2a8fbf4698c71e0c593d8a6d97f7540439fb2ddb9374fddfd677a3abd47c2bfc3b9788c01ed046e9990cde562c2c2c320331858054ca04f9406c1083819783bba9dac78e1d9903b62abcb3bfcb79be2da018c0240ad5871dc3943877025bbc80b01ebb25c5eccb29c8bedc5b6e2966dd9c513f25c72ced22c4f7a5e5fa803a5a8a956782665c13882217cbf6f453012ce4c5bc74a81d4371716267dcb8c7741d421816794109f7ca45e7ff0f20e2a1246fe7f14dd1b62cf9c138afe5440e79da1e06023c87f4c090f17d436d9c4059722cb0be64969d820fdbe9ba9bc26af21f4da3cae9fa4adf00704f0a439d5985d83b33400eac2cd77a6b0726a13e34daa58180e127a76c5b315702408d76b4cb2c469be91d4fe83a705d627003163422810e205c213a40ae22cb9e7382d29153a74bd8bd5eeaf01f0d0b7a1aaef274f85b95b46e6e20fc85993b6d29e689fd34815253e9d9fcbfaf37c3880f12139fc408f68765703c595788ba2cc72a2c6a112dfbb819f2060dd2e419de3b681fbe6c78e622cd9e8f1581309b7e4bda2806ac10558ddf6e1966cd6e6c201ea0940ccd9781981030fabbc3c0db137f83b78aab2274827db274fbc26a944bd2e427bb88d476af9541e1bb0ad0c71dbd4b9af7057bce609df0bbae1b30d040674115f713aeb02fa01b286f075909dc3d0b1992e8b884fa0cf1814164ad593ee6dd03f58141f65e658fee0b973518440da9efc3f7a55d23128993b9fe081c3c9a8d6160270c66bc4df56876669e301953e253eb9f86d8e5c7974c2d60b7e2b0e2fdccda253d1858f942ce06c079225a828c11058751ff0a9d158b0a786c996264884e702b11ec56ff67bcfd4607af19684f3ce3cda6c69ebaf94f1403a5cb61b00dd508b9152bdf3d4f830efc2d02b6500106caee61eb580bef25a64420e5c82f52f42de075a4bd9d2d2cfd8065c4fc058199791ebd85323cb1b0d1a3fbd2fc7b902b1ab449c788063f25fa3dfa1dece2aadb517e61f00c3c444e675591456e992abef43593be8342d6f6166b6e84ba2b57af398c6fe3d5cbe353e5cbd7e4d1227a3c085d96832a4c9dc01159106553282aa3998a2aacaf425ecc4363fd56269850f1e55891e94caefc71582fe869fa5e78ad19266f1257a8368f6505296f52f9aa120d2ad51d41145778d0f880612988cca37f75f2014197104750ea8f352e1af45ea11b686b20d788fd55e1fb63332f00d8cf8dfe031eaff5e825aa94a5796d744e8d221ca8e62801340c7da12073bbcd0e94a1c4f3876799e73ee3e01e5c05374046a9e72f1e645f1dc6b93d03fb907ef83ee99307cfd96f5d336e5f57f5648cb2f361424808e80043ef26ac06323fb459f25001b0d96e1c4ebcb12171a0127ea4171a96b375e0d1e13a17f1a41cda2bf78197c59d1a2e8d951a7998a579db9221b1e88fa80d467a3a3fe0fc85fb90a3a784f5b53ce0e09fbb91e1693799ee8ce72d2707ee30c9ecd33e57951ee3d41cd9ca7e84d77ecee58e4b0372062ab7d99f3cfddb6ef196548e067c082bd408940201ff47a89d4438411f21aa1a2e8aef2b519a0c2a8bac3a66d02289399d62a7d2ca6090090be58a4f6cf4c32c29c25e63cd917b188cd5c720780c889def9fd2c1527eda0540b6393c75feb68929285248f10afbc16b62cd0ccf68a907e6e4eb18c089e5ccd91afa7224d4d4f15e63ca4baa20c2da07483b50d867a6116809a347201043ed76ba111beebd4311509131ceddc41dcbec5132d32b1a5deda77eea81182697efe7bc4cc5ae8bb9e23ace1cc750b5b31a4feda7265ba5ce6cb354f64d29cf05457de7ab3066cee831914838d27f59cb5382c10db495a1fbe3bd628e61ce5090efe5418d9208ff587feda431c68c76fc7c94518d172d60e182ff3a8008f472171695c405f081e9e89ee56d993ac6010f4d83bad0a0b18a13eb891040a29cee8007e0e147ce3082f96637f1b8bcca0a2845b92a805047bc7d6154f87ba16a067794dc0339773506c060f566ae79a3a21c867e320ddd109a8e6e88d2d82e24ed7e194c0741fcb568d13009e569b7fe0ace5d7600a14d936bb0c99fe09fdadab7b8a2046e58504ba728d50f7ad0e098255d95fa57881f84d3ed697de94b82014cbf603c4dfd1cf4ff00d74ebd9a17606639389f526b42a91551a0a80ab71f7f5d6392941900650a68c7f145dc05499efd9c9e269a40d3bb34998b03240040185ef1961224fe3e68e448cd41030ddc1e9372af9b1f79fb86161ed1cf5c7e8b1e59ee33a028a91f64d0e0f125850321dcb22472e4010c3586e554a4bc0a22b33cc11d8fc3bd18012dc95d3a10b4a2ec4818cf29c12f80347b3036605af69f692bd5e02ca3c5b73858850aa45b116731d199fed789bf61cc4f6cf3084f6e93d438cd36bbbb83ed56dfedcd46280dd297dfc9e68dd9430791d03d1e7e13052639d1527dd10a9ce0551c9125a0f39855546ac7ec38ec1e9835d2b8d99ddb3cf3baeeddc1467cc796241051002a314bbb5d6c436b56a13a521a9f9081a9b6e74aa5095c12599509d24b40a00486d21edf1cb6850ba6db8aa68a7a1c76010e5b54a77fbe7da47a54b48f394bea256ba18619d665d3805c1abd6dfba9e9cd574b506f47193b064727469970519c2ba947fd8a5e5725005e3917e9bf0ffc53282af5071830088f154503edae76122defffb4954ca7ed10302335a5aac1ab423aaf6d1d143f56c7987b50dae589c18246a012345ee9769ba7477c0c623588a96aeb3f85b239bbc8af6b7a2f5e9751a826a74b480b11337bf47107b3e0a4e6a87710ef21d0153064c954f3f09eb408cbc490fb158050cfd9ca08a3c1e28ec310fe399c67abf4e67a27dad57b38f2d48995d241e0060112313e8213126d6ecd36980568d59a6870c7bfcc43a3781e164ee6d8b0628401dfa942aca5601aa179c55a761665b9b728c2817827e114bd239136a4573d666cf3292821449be2598c1e352dc3bc1dedffc7d2deebf2291dfb24ce0c199fc97831d99acbc3105de8644045870fdd5c1ae88362b3213c646a701bf83a193d47e5a73ee3c9ccd76b4f3b25981c7f475049b1c1a388a7ddf3a8f32f1a48007baeaa0d8eab14a7db0ed75144dbecca7d4c154931bb9552758c79657f355507d70d9d46a30e57b5ba7e5a322547b137b159e0f555a329fb00caaf994fc860ddfda1ff3927ad62734ec19853fdaf54d1a67c274689f03c5d293172573a7578af717c7748dd1699879471b73470664e47f65970d1fc929de22c2a1a7fb81e284b5035d9346dd1ca9b611f6a816ca440dc052450d32cfc8194aa2b365767c0408ed765bcc97994147bca2fea7ffbb5097cd7b7b8de5fdfa4c328d680fd583482a76103b81b241b2ede57cf652ea4010ca34452fdac7cf39d66ed37b70c2f9174b191eca4b87c584e352086d21f012d4238c9ac51ab6a2c7695609e93054b603b388b18278bf01e7f827b8e9a77d3b3734796f86d052b587a43b919fa32cdd36806c40bd6589adad364401a4aab394d147c9e918bc69e684e13019933e26a82ea6505958b09baaf26a85c4fd0bd9ca07b558792cb0c2dd7097a2e3394b84e507add03f6547a534b74bcca1029572dd1d838e39da1a2fbab5634aeca8a94da1a9e6a5fa51189ad3322255f633476675e052dd2af9aa2712a53afc37b8fa5af6e45e2938c5ca771bf0221d2cb138b66eac6b21e5c37409a8305f5b3927a3727e3e7d0347713aac4e037eb24f4744c64cf49bf2b25561bbf939c291d316a1d75ddc42214b2db6dc70723948ecd1e24ad75fb7a56e3005888447d3734b355ab6c245e3a22453c744e9ef33404c4f580b9772b6dc955610810e901b8db355ce13f58840a811e20ebdd821d98fb2556851e152a04bac0ef77eba0692e9fe930e4e21c4241443fb8906e22a89fafb7edb76d7797e4fd2fa5da8929f6105be93afe9e72e92d3759a3c8858d5532072e577aeedcec684ff8c8992245e1fd147875df2c39b1038e3a91d1c09a2690b5b1d11542daadd141ad00aa30aa45c772db4f2926fcceb51926f57da9a96e442246780e94df8acc58c472418b81830497bc146e26aa4e238ff34f63b7a65087045d79ebb7ae51dd241d71688794fbdf7a5e29050c86c247a26407ac3455d05923d6b28392e2f6013df021f3f7c4539fb212818490c87f1abf586f8c17bae4856156ff8b7a331c50e379f91f9e91ad7e0203fe990ea008b1f7a880d5ae632424e4a0eef815c8fda7c34bde802f388da249ef85a93acd604a313e0c273b5efd812d4ea4a0052fb17c8a51a38c8c31feaee1de217e543ccd1bd29438f51430c797480bfd11fca5a69a2c485675422d3ce91428820a680176bafcc811478f948e9800a1048809bd0f96043e8d2f5b000d585b6875284f6d5947c0715d4116640ffb6d39a8aeb371bbe3d823e73702059f0cf7c345c89bfc41cc2aee52395fa9462191c92b55487ccbb9bafaa22ff3f35843043abacfc5e2c4468c32c2c6245782e55d5f134365227eb6f3966363569ae4f56abaf75ee43a486706788935676ca364ecbd19d2c4bca38eeab95f19222015b65c947217028412833169ed7e4e316c02aef6b2bbbc3c06d00461d1dd1eab5865be7f7eafcc555204e62b53f09ccdc8d6311c4b7fd8c78e20b4c4dbd2828c405e47ee1874a32676ef91a2762b0477079c6ed19e71f4fa657d39329273581a6ced37c29d623d696d6b22df092a0604dc06c740f1f94f44c664fea7c7a6d4bb102094644033ee4f5513af3dbfd5308f52104445ac480e54e807567111857eb903deb178d2d8334a7cd3088d235f95d1c401911e568ffacc42d6394fa368ddb8b11ee656ce69c8f1a4667c5efe8b32e4d334359c128d022eec02afa6a86b0eb84a0ae941da6735c03dff562daeae2cbf13f58e32a7bbcf7ea885d2581c25d32c2fe620c45d093c1a06d839303fc96b8cfecafb391cddf049518e9e72ade8b8dae4258295bdfcab04f7b6e4ef8bc5cdd51834d08c5121ae4135195aeb7bef916a8fa6114685001c6711b0b3517d3a8781879c3283d1aa7c2a63796d4290fc8fd597b11aef808ced42b1d5d32df872a64e1b6f236bed8f8f21980b932180d413bf83db6de4c888f8e2a27b12fd61994d5c9072799de8f94433293f4f39c30e73ddb00970cb0e81d26885c9d3218026ea42a1de2a199646936c801ac384e9d07c479a80264be6320f192d054e5ed40a24f93fbdc393609fba626b7d4415c62b8110dfc84bf48faae20f12743593cd54c85be78684d72fa307193491b06d356b22f839e2207089931f87edeb7eb8b8fcc7f9ae5ae001aa05ec84a166b62b662e04be89b9c52632ce593ae69f70c2109e0f84e77cab5f8302290db7a1ce809f0c3f12a323c27afb46ad158c74d26436034c8e829af52c378d4b5630f2b23062152a582f3e898c841d8e3b381d3dc7e4fda8f6fe60f154228c58734c033206130ba61bf18e70a1f61e7e96f0e62eb460528955d8764d8c5ab5f9dd1e8d1ac603b1d8f470caf88ac75fe24de98a37e538271cdd6ffdf131ef75bb8029b61795c94dd64334b7c0b08cf4dbd7158011e06cd91401e7be9b9d965a31dc618533ec13b064d13a254bb374ff9883f07a2e4b3300949d532bc4fb5a81149c70a5a557c2387d754e9f2c13ea088b4f8bf333c11011da1876681f55719f083eada573b8a4bd7dc739530e35d87fa764a2541b77a2cd847190a8981e258ce704249490088c19b3d02024c26e6f75c38f08dd2af2f8e00089a6e773386e77a34cac3c26fd117399fa23781b2585dfa3bcaa490807995c44e2710ce33dc89bf2b0fe52d070a2b23b3c92a81b91568da9b41bbd981b75ad8162d7f95c06070ca090da316204088550740b623d8f500d15bb251d7b50dfb3b96c71338199c1034a831a850d3f960c0cbbdf64b476d7354dda7b0313b22f91884d8783a08fd112c400bed11a3fb1cda4243ce15ac2b53a5e8e369766dbf01746dd2c6e71c0fe0e69b66b887775ac1673bb3799dbcbb2b3ad75d64b41bb12e916151c578398316ae62c83c61f950ea8af429b10eabbdda51f7701b63a9bda236a1a92a5f9024d703238fec44945c570d0a4c2256b69e7f56cd677de42c83ef80957087c14b2c1aa79ab6097493525312a36784f798d35cd952bd40368e00de1d638371a434e13dca8e7f7ecc07c74448d1278877114e20c9cb256ce4cd13bc6a51e7760dba88b383d01bcc3b80a71dae50175cf981304b7345f708a1d6f7538258385bb5985693979461411f854e02f47f3ad1025e00de3d638572bf6c82428555c4facc9698548632114d3b63f489bd10f6e4816de5375433ce9cf1a2b5006edd76b5719431e5a0db600a8e81f2f21cd1acf9ffda8e830c4be512de9808f07431fb311318a124b62b4df1e088e7356424235e1850b1d26d2f7309f78c487bb16ed8bca196fac3a5c8c348d4dbde31715aee0e14d2cbf9077dca22f1d219333bb2034b6a1b28861102134abbed21c907e806be77f3a81f57b0811f393665affb5e3477cb1f0a10e161a4bf5d209fb1768cfc4f6febcb81f848d4ac21666de8f6fc044d6982a7db008b7f984a94c9bf120175132e5f04d0048605ae58f4d4b587a4b8cd866638481217ca9153cdec5238dbc81e23d7891d7a7c886abed97798cbfbbbae9c83831a699447fa4c91a693180d7e93ed27afe47ef48152c1f1233a23ec8c39e654868b0d76e5e7519b9955a4a8e1493bd4d6da81211e67de8956024e8e00f7653a5a00add733b92990fc6b5d32749a8738bb574c079b406d12b7918d8747b06a1d83c4eec982b86f1cdfb4457a06bad03aa862fb412a8b79eb42fcf41236d10aa4ad192097b4d28d8e470621782f62c2115dffd1d6338f0235b1b308cc00a22c5d613a61772878f2bc1869286248ba8df22b0ba63a05de07a15c58e669c05327ddc9b9867bf285e566b892903cf2ad17a3bfe5e5406b70a29e0fd71ffd2e0634a199321a6430fab5cb75920145de877c844f98121d611888505b42429814062fde62fd38d78ed42a7dd1a94c0acefdaf1c61b36ba175d3a4719e4cff36a45b967c2948cf317c9986b07ff593e1c8627654407261ec1c0e513459af8bf472c5982397b6138a384bb48d35a6b79a5c9149423a9eaf295af9b9dcdae64a125cecf58613fabf4d87239a494cb062d5c6753b74e8d80aea527f4c67f2fe5f23f632a72cc1d8b2bc4002dd260c704454fa843539b90d01b635303e10753d8747b38a8c32a06a24313a1a9ac6c03f065ff5bc13ad2d1a6cf553dfe33c4351d1555f8ce65a933c50aeb881644d0e402e62f37345839afd1d3d4f97afa2d6f61ff7e490affe2581655e91db5195f8aefd3dfffe6612596ca9d9a11dfa383ca86351984759cda113744a0be47974e43943288f113df55a6c13ffc7549543373ecc54fa9e297685431f5bb608da3652ed86944fdbe8ddf43af646b87590335a535ff46aceb5460920ff197b572e91cb715b76103fcafa11961a79f562ca282050c9ec6ee3c801163308432bf1b1d8a5159ec3f341b8d3f41b582fa36d0d839201550f2219f2f6943e74486571051894e6454bf69938699d1da53500f59ea79f66eefe6162d99a1e8a7285eb36f44de423c959a2b717104a8238641dc0cfd06e55f5f8a6912129d40b0d774a63370f9a1827c1d146245b5a45d2b35d7a238c4a7636d492c71252102338b9508e3ad036aea28a61f9860a02eee51424567372eea4698da23a6c5b29c76688c6339cabf8843b4abe73ff7a1ad4d51880ef263d1bb5d5ac0c0fff78dd3961c5043d11163190aea13c68b98df2ca127707c374e3fef59d7ac36b74359146a68326f55d4017b9e82f9b105ea11479089450c8d94a99efb10733ff5ade1f62b3ca2c7336b4171db68e55cdf8115714df69ea6c6b9da505959734b51af822b2b2a8680d11480899280c4db13b290dc08fa9818c2a7df854096a0bb23acd0b7599ec9acfab0c5bf19b851d4b291c58776cd1aab3d2e4b652c71c47e30254c85b43e72f214307743c3b7337d9ad2c1ba6c17219c00d4d50e564c2c7b31c7eed6b58ede1f4b691ec27a2d63f998ac83f54aca1b9381a7e35a709a5c601213736e5ed4242664140d03d21252c066c862979751091f54be60563fb87c50261bb02b41ae5ca937f2b71904bac695e11ccf149231a66eeed7afd56bee1304c2c4c0868db8e08435aace174bd95d68ba6d6b961dd61a042d74fe604002dbd601eb874ea1ada0efc9a71f0bbdce6673f4d0d479325cf725f695d173cdb3175908821d46c95d69721241416219ea45f479c9a079a10ef7caaafce6b0e5550c367cc369b05084d27c40c585f85fcad256784aef4674a95f2ae65eeacc780ca88d5c59b93a1b9007c040a407e621e1f03326f54d87dd6c94e7936b93d6c83497a22b64921635917ff09b38d666a58db4b4fc0125de676a86c190992fd0472ba28a1c40c04c167ee33a723cc49d288b5a1e73a4c9accc11b7b49604e65b2b1ea7974899225134c5271662bc588f5041cca2e81605d4e3f68ba4d85822cd8b68468875d71c4c432ae674db8305cb3c448b487f7225f5c105ad8d41938852f09d45dae89fafe235c41af68d94440be6bd52baa3dad7baf4f5af48cd65dbd1091d03591a322adbe6534284107a01850c1b2290cb9b2c979a518e55bb9cbc0bb5815cc62db729e84244e8d23574dd85e8d245d1352e6bb142476054e8382a2e48c5341c6bc51799019cb8dc080094e6784bc53a4c05d1541ca31e4204890e6b30cf706c0bade5c96bfbd616082550a493f1f94598cf5545c68caa3652680782a716b4189782d66ec9a5287e35c76c8caa460cee9c82cbfb0865e34313c6d2f937220dd57e6312876842a3ad17187460b8390a2752853a92fe262414cf677a7324ce63d7b983d7e847eca33a720b8512edd6f20b5fdc196aa01ab049e7fa6673a8bdedd0732c2d754f1a9d2bcd4271068dff5f0c9497c25531bbf3b5e758f9db509bbdfd0874e376e1e8f60e21c9c6d4d4397d2e2001a2fb84d59915591cca9be99b7e7920ba5ff9fc9c3201c8f0a775cd91040452fbb8d92bf74ef469b9ab361e00197427b35ef8e6035641b8bea77f5abc77649469598e4deb3458537a28876c9c6dd369fb81878cae2436771f8dfdecc7f7e23ebeb2a360692f860dfbd17f1466c3d14fa99879537d334aaf622b6a7a82daf0c37aef36bdeca4ff0a5335d53883baa45f2be7d342a373eb61a0b3d84b9297a9cac643884c398bd0c7ea645ce097de589057a88c0be8c08f49bf5c1c7554a43f5189d60ed49c12030fb7e4a3563621d453c2791ed3137add191c6a72b091c1323ff3d8d899f09ec5d9fb15bc2964c7e8f76de93fcd7eabfe71fc9fb85f81ef25ed534faf06a78d609569ee0f026cfe5fc7208238b6e3cd197ba217c4da3ed2f5407af1923d172292c456c640b10819a3e3286e923cafbd2b8d1072bef183e4b8cc42781c1ae12ffe83f87f2d7ac636db6e51412136caae4563ea5eb47261f55b6b1c58cef7b1744718c699d1acc0be61ecda18e47298b988495b785ffefdcaacdaad207b6833ef67e18bc28c32b5b3767295e2827bc2ef713fd8bdced73e4ba29881770054ae909fb993db56b63af88ebf520b085b2507aa4dd4c87c5b9f26f0ee281317dba6ad4ffad83aa2b345b1a3057b16be3bdecacecff0c396358c4c96bd240c08bd77aec748bcf33decd8b3ff675f5ced2b7fda4996bb4079774781a4ca0cb75b3353786e2dec9fb4b2a10e2448d9f16263ff32be0acdb18a7dc0206ebbd0a7a2fbbca4d21dbe05acbdc9299436639faf57ad728b566159643df0e4b8886831e33833f1bca9437c2a2e82950a1fee0eb38040ee5c832a26af03b3da1433c02453688cfa0f977e3c599ae2c674258e4da33755bebf83d693188acd1b863798906ea159702166217000661ae0d336b1842a0522ba08ca7e8532c5b598b511b2b6ccba89a88cb405dd9e4f0e550e7e17aee3839643397861635a91a5cd22ee3584f041887921605fdeca9501cd513381603b2846d5114e93c3d550f85b50bb36e6161933c34b73f5c65aed297a7bc5208dcb016597961ce99a54fcfac136ae81644990eb764435bb5dfe8a76bbc831cf2d9863b5dd2eaa7d364cda888c22b20be3c90c17a3d08b2790f12802b7e0b7a25c3ce8450a4c254597d0061176728bb3822b39faeb0c4799116f7bce0c090ec195549a7777154aed2b4df3415158d9b54cb6bbfea441c9b900b0675385d177d2aae6e49e597189400a94a07ce6c4a9ef54bec2d0a4ade5b07db344e28f61937bd3eb44417f27532e24b0fdb41a519a7e1c5eb1681b99f065bd2dae9edbc8c7d71d6e6f11521b0ec3e10f9b35b1adfd70c5149ddd5fa37abc412f91225fe50c61ffcaa857cd105351962c14fae245b9942fc512f1c4c1cec3805dffea236f4e3c81d0e5bcfb4b5e848bc52450b5099a6478837fbe09a0ca8d274570b930fe44177404e9357b22804017dc7e1a60ef0cf3a418cb4fc04f3850d0fdf546e87454be49e46dcacd043147f0af891fb59813d3bab327f448967213ffc8191c37f826125fe890889c106772c88cc4b71b42676f64f83776e26509e5048bf99ee0438334e504bd3313134ce8d14d034422ea2ae5a6c18885481db6eef8f36308281284bf1a43276f9c1f16914fb249f4d2e51363a1f78b049b10f02ec1df4668ea0341f74c851b114cce8db46f392418c405e0913d7c05269e20e54bb1e381001293012f06fc6b04a0ccb2e9066909620edc3f719f32a8823ff45184ce14eadadb47222572c246e1a5203dd1bda51374cf00917a4f3a03ff8143445faef439a57c0dc25e82092d86f7dd827b90ad010c303aa4bb0428572c843807c7eb7f5b3084984328be651519135620cbaf1054587c3bde8539d82feb9f045707f17eaf062c23097ac437e4334237b43456ddf19268a8de0e9596ebe6e71f06e818c87fd5e6944caf8891ad4aefa1d6fc0fa8991c4c8471accbbce8fed271a4e3103a5bf355f694216708d55b5a4a1af0a2a605f34dbe1cc0a29f862f3c98223c7a5ea4db5d9358f2ebfb0327fd53a0d06693a7fcd3b1d1a78ff18f60dc4e26f2b0f8199ac6482039bbbe4f2c8598a4fea455fa94faf3689c72f8a634ec4c3e435e6a8d8b1f98d8eaf514b5c8cf0b159e4daf401a238812034d644786aa54199d770a0b7d4f355d318b516a0a7336b03a2f5e0d8f4b3b3d20a287da98bca3add289f9b0635c657d01a9866f6ae6414470e1a92bc247bfa6964d38d0bb5d60858e55347c67a76cfb7e3f5c02b3b87d3ba427592ac51c85b5240dbd0f92b2cf4273ab6c8ff1edf3cd0d23ceafb9ebc1307186977eef8b31b4649f284e0d51b53863d145c0d1d020abf49cef4f49b43ec8620c51da0c0452c00b31987861ef32f41c4e8d6287246fe2f5b4d2f57f3e51ea6724b6f89d7c7329792c0755b8a1980951219472ab4d8fcf12f140905f39250aba0ef101c6714054bf5510cceb9cae00fbdb5f24168eea8c2667e16739ceb0fd996aa88ac2f17aad8152e6b402d699b8162250fc4c0838bc612ce609daa01247629081c1326357f1713cd2d9e45b3c92a293d5d0ab740251fdad5a173f837bb7eb435e44ea7213ecacff8d14513d08f37aa8afd6009aaae0c85da92f6ace9f641b94294a4d0bc6b1aa46c0f373126474bd2d03d8e7b9b7fd601380c87e351d368fd4882146fd06489d2dd18cfa663d28886f7f49141dbd55f9ecef64a05488d7cfe1c899ce44d42fad4ccb2e12ebbf2f526d109d1e77f2fef74339ffb261d36729396df661b25cf44304a7bfc81e9892c224e189c069da45365c9966abe8b00228699aebe74446aeeff77e81105723887940de3990a23f383c58e2784d68f8871c90d43a2e03f77030083e9a6e0f0470c4186c103a31cd427be5b80a8316386c59d8001b88e40a69fd3b6bdd1e639481512ec1a10e3f2289fce648ca26e210689f1f48420887cd99fd0cdd022bfd0f9e4419dc1a8590357d80a444c832e099b3e2b3d10e20bba8d9324847b34c80d50478675354dabe433ba8b298c31ffe8059f25c6aac6acdea9e11f54371d73d21817f6c699afd3b4fa0f473476c30648434e39d8159b1518fc8ce9dc3572c2aa1651f786e4e8efe81eba7f44077fb113b9cf9bacb9c4dc0620f745b9c10069cfe00f9b7feba58fc806dae02c819243e58f9f165a4865248708318b6fb0103225c806b1088aada03eca72ec04d4a55ff8d08bc2360dbcc1f367860572d72ea3fc682a546acab578bffc0b031cbef15b7beea751062806a1b0007b078369dc95dfbb0d5632cfe0386c9588ae160fafc9200397f8801577d50e8f17d54087110dfbfce3522e5733fa53ef96f29821a7fb81f6ca0e8a8cdfe61fd1cd7071cea0af841d3a326526bc6399cce161111007a1c346c0eb006676300b00dff8e7fc004977038de93392aeb31ba25c2e132898fa5028d7a8bf183272a2dd670a69a7f05e987269077c0919b640b730c7f464485db111b1ad2a2c4f9f4834174b5ec61717f88ca2613ab6bd0c546d390a7c1657dc493c05816eac8abdea7d3b600fb0c44d5db23f652557673c706805744a7d6ec4f63161e279c3772bf24153f8f8e3b0e5b8674bc56bb9f1f1ca31c127c4695c5160245180fb810e4691b82a1e2452c7249d03c3ffca4740557730c10bf7af8d0811e9088c19531175c77fcf331b56ce0136ad9f2e93828711433b375bad54a1aa2d36aba2ece0c1942b50bf62133716b2557b88f8580f93ef6dbcac187906179df65a28be3964d175a5c6a038a0c5f975a309a514d8b016d064300769c1dd6eadad48fc022cc4eec8d78213a8913a35d076a8e55e231bb025954413456dd7f3bf55500240c7d4b1b1f6985a519b0f6a8567f2348ced5268e942c486970a89999dd84104288442eadecde01fd080609950937abeb665f9fa94bcb7c20d389828621ece18b165c418229e47441480ac21914808113831a2a5ac082245213f8118330b4d045133e80410a1e4278f8e2155199f6d73eedbab22b8c2d8fa8fa55cdf6da2e7efdcb09da90bbbe74e407721f5d0ea19dfb10a18f38fdd991feacb6a16bd742fadfdcb557bd7292dce5a95efbe823fad1ad56699a5ec5711c37bf5d24da718118615adc43d7a6847176cf5afe302c147a0cc73dc4697a4489e07070d76442d7f40af7c170a1430ec3b44a7b48cbcc6b3b0405969ae430113b2342b2c81a2a596ec56eb324626b58b6d7225781ee9a9e495500ffbbdc55da39efdaaf568564400f7d2615b91de4de81e30215463b17d2acf9cd6eec1aa6bf1ca36b8f195d0b9d2562690f1da6aead693be4a91ec705e4a9fe831189b48f42fa13953f2bcada478f099da459a2873ed2ac1c9548afe89789aa995d79c95de5aea2e7f469d76ffa7657ed2a7a4d755378a2bee2f29357449bb9645783784dbb3e5355b3aedb5ffd51ab3f96a6afdbc764d959f6fa933de41d13158fe5fa883d568d699878135590c056b372e659f6d7af66dd67d7aaab3f69afc2f4278fe97aec31f51876d6f6fa18ecdc39cdd28e65d7b46dab988d3d7695d5dfbcfd754cabaefd7595bd0e53f5577ff3baaeecfaa40e221569604abb88c427c8537cb4d189369225b4e3e30db4916d18e3b1cf6cf92426325bec4bb6240950c40627da44abe14449bd8a37d166a2e205b1192ab925685045a3924552ba65f9935bfea4fe72d0cfc768c7340bbbf6a95939ac797a98d2585ab8fece96ed75cb6a55410253483fc4f6c1503d4fe79c496521282495a88a9232de50efe352510166e55c4cbb7cc5aea2ffecb1d363bf5a35f5777d1e3bbd4afb7cad1a16b55a230e95880d3756d22e935dd378a22a961d729ab65a856959e9e7a1cfe505940a68fdc0a9f9966dfd5849c9d7e0d48944d156cd4a6afe074ec9d38f8ca01126f417a9e1f084a00d28eaf121e2e168df481e74fc6efac4225ce018215d66d3b95c725026f9964d8e73e9a19cc383e89ff7511689dedd13fdf344e700a4413b107f08b1e9352de2e0049182a78a9f27e0ec08e9f12162eb11223f5efcd45a2b91d9128144204422102211480452b3208ed87f372e1175608033834d230e4eed519f3de2206b76e4ec23f75d0de3895e334cabc57dbe057a2acb3c8448d907d375a39b324c59cd84bea475b4489d26d13a84b4cb489f4e5a26a75e2674a965e443d4feca9312b08c38dc1daa67cd77efae9aef5eb54a3ef4171939ec9b64f121fb753a7335ed03107467af5fc59d459b7db0877b6babb8edcbf4e17adc32b3e4499f9995a392fac348fabb971f9df561fab357a9beab3fab73cc931e233ffa3cc99e04479bc808a21c431994234ff49928daed10d978e64a90135483b025e7fa8f9b9aad1a97942ca5e42925634beb874b0abfe6a6beaf2e05d4dc14fe8adb107f05ba51e392c25f89a884b087135f84a1c1a1b1a174d6d3c79aedf431c86c29991e6d4c1e79aaa0e901cdcbf92e4b8e9b9cf4bec1140576540072016661d35873044e143e52d0dc34114467e72222595678c7e7093e12efcc961f248a9ede9d4d7f8034e8b53ab22b8220479b284444d1cbdcfaf1148c065875f100ab38824d69686e8f7b4d671a664e790dccec0b679856cbe4de5ba56fd7220db4d17197b93b96e3771df89ffeeebfc3940ed0b97f8fe1fe7567cd738fe9fefdd32c994b6eca2437c9a497b2e89a6749177df42d7b87a37bf683e1b8ee5b86292d9a324c5d386a8926d12e240d676b0427aa25d2a7892a751a94bfebde610ae7eef831dc3d8d7577783f1f0e883bf6c8133d8e28449ee853d8e35221286870912b3d2968970f791f40dc5d0f44695b784578c3759b27a4b16d780fbb1cba2d5554708f5dc73d3693d2813dd39fa557ddd3f922352c85ddeb2a410ee64ed4055590c0ac1c7b16bdb2dd90c371ab55f39ad5222344bb23477fafa2bffab3aaf9cfeaecf72cedd863eeb76f9a956959feaec35486294cc389baf2d7a1caa57d803ed7116c7655c03cac5f611589d2b1669e66ac59998f40ea6b6c429eead4b5d6479c18049f7bccbcd42cf9794eb37254f2933523876f34127d2291d9c2d2c2587f59dcdf5fa406e791ce9ebc3e56d26bc9eb65cd6c31798d45768d3fbbcaadca24e449de7c3025391e70eece65d363e4605a43cce30f21cffd84e4a46b23fee07104b1250221a26a68afe214a13845a69182c49802c35b2302d81299107d6a7c208deae3e37a0e3d2e7a411ff75fe603d36a954ef256c9498f3fb13e16992df2f59108b47177add527e5079e112736016d68af8f31802df10710555f4f845deb6bad4ff981657dfcaea3fba7e3915dafc3f4f83351f52128f00783b943eecbb466e56467cd63cd92e772cce3abe6f133ad92e7bee551e825d9bb468285ee65d049b93b04bdfb07833177528629b86f869b84c38e349c2891ded10a6938eb5b200d270aebfa96a775b43aadc344d5737a038166aac44f6857db3ea35d855dfb752dfba66564667efae356cf430b02fbf6988c95a3d29e6d6765c752d8cd3075e5ad1eab3dd4efa887a9d7512f7b15f6f95d67dd5ffab33ac73c76d53cf6ebac9c4babe6b15ffdc2c2f4ac31a57204969146caeb0acde4a5c36b3ce467e655dde7a596297dea2fca98bcbb7816d1a18f3df451873eeed06b7085e1102c7e36bc62c321589c606b736369c54e7ff79f0a665e83a68f9284d8893da3d34723b345741a7d480f414167cee101a65adf4d9719a634b975f9bb964d8f1c8e2fc74caa3b0f33a94ecbcca4647254a677ffaeaac0157c764090eab44c05aee0b34393325de698d262c9490eb9ef7ea6470ebbdfbd42240b4cc2155358416dc43ff4d80bc97235f1c363b6c424289549248135805e6483e3e3bf4b0a8263a809389ef3813d871ed87380f1293f30dd3e28f5f15d0df3995e8f6598568bf492b74627b9f665949bf2c94df27c294b9d4d1f7d429b3eeec85f22b1c8aad41de5aaee285a26e75e66befbd52aed275a467eb9bb296b37c93055ca30a5559822c93045ca3035ca3025fa606c49f64e92b79332fe2873cf72e8a20cbacd3cc054ab85efbdc57d7bcd8fa901a706460823a5c1b45a43689856cba3ef748bd32f1345dfdaf4ca278a86a030a9c800c7797910cd68a64a4f06f650fe3e26de6a16f6f8ab59392a162cfd830157f6c89ed922e3f5d24716992dfaf5f267b6a8bc7ed2cc16001ce52b4964258dec5a67cdaef30879aad306e5a6cbd84e4e4c47315d468ea9efb329329ef218fd4fb3bceba7e8b3f0656c875c4797d08181a4c10c340d07d2a8d247f64e7f5cccf20892883cd5cf2c911051f554366157d90369d4879a60add6fec58bb0e7c1b3546e80f1bdc7d74beeba66b798a3143db27f9c8641390f3085f2981e30ad1aaef24d7f39f0655c8777fd3b5bbedb707bcd725f0e1008140ad97038be1ca57f5795fe69991ced32f8a56b5a25ba0d5ac6bb08e732c9f900c87f0d39c655f2c9351919a65262fc24a75ce7eff0fb60504c00c83055438629950c5332324ce90c53598e1bbb595f4b9a181a4e542b45c3598fa2eb5b273afe7c3a1699a87a938e3eb67e2779abd502bd74d25badd0452f39c824479cd9227a15e5d344d5fa924c92759448a7513eed2aed419fc7d93bbd0aa43f7b91a73f2bc2077d166b49e4b3a2c7e08b342b7450484b9096462e3e79ba864c6630693204af4b75dd76d063bc779ad5ddf3406779df36d0268a911781360e04d29f060281fefd45721f7602da4a26d1c80473c7720e96f7790d7fa2832287237be4342b0706f434a4646652a09fe498fa3236cadc478fd91ed2aceedb479a956198adc7f4a78940f71f76eec3be5dbeea1e302d93979ee9ef5ec73c771df2dbb1fc5d9d83fb54618d0b715db77d3022502c21218d44a10e84618acbf35b96ef30137ca0afc78f22bdfac0161de615e4599d562b2b971d22799ee7d1733c88b20ff0d5835b48f415de21d157a51d0a1d8a34097a791577bd0633a53948159322a61883401844f1e1af5fbf9aa6359aeb218410c2cf4dcaa393e4f992bc45af99de835c8ed13d910733be97f143f82310a69ec484e4601feae9b50f6cf87b5aadec95cbbe5663d7b497d53ef000b30fd7a98ddbbd3ed4fb70d9bc8a0d8874be4c0cee4b8aa4606214fa459a357a8c8b468f7196e89f3005125d3f6674199a158395a38af1d159bfe831e6173d26c6473a87e87fcce831628c51089b2a048918c43ef2c17544bc39890a1cf202c3386769c3e86b73cec79b29e393e413e5b3e4337f9666ca344dd284134e2dc334525121b9ca4b2a24979172141d392cc6a8cd39e79c73460d9b734e0cc3b039e79c7062775e64a391c009678c3a07b8af0634fd22454f796b7494431e515ed12ea3910cad72191f9d54b40cfd6997815da43f6d0504dd7487b9b91831d15f2696b76da351affe6197f1d145595ff418191aced1b5168948f9e4397f7f363d462eb9969261ca848792946c72942c32b9e82f72d7fcc1944a24393bd19fd661a2e65b2d936e99e8926e956897899af324ba3451f32f920427bca8051856ac560d0d34387b8278a210a861f6574a0e07f6afd668235b486ca6988773ce3967d5b01ec932a7c0c49e3fc15560cf416e8cdbc1005bfebb6a8fd006be76096d80b6afe6d64ea18dd0b9afaeadd5ad7d6545a14c210deca02cb187bdc3b06fb3046960a37cb3a801f537cb470dc4b525bc9be678a8324333589acca05d2a39d83bc8fb256febbeba6b1b777ca857405cdb3be89a97b79521e0d0577487becdad37706d1f40f77e857e890eb5e61d5e5f9d6c0f6fef269085cad36c29ddf37e674bc9bd6b35e7303aad59ee92fcd5cb979c247f557f39e6491e235ff27992d1719e7b947bc430369ed464c31ca49732498ea5f922bd0c3acca08be0412765d87d77ce6ebf0eca387f5f8fb829dc5e865b24058eef329725a491b3bae131bbe1ef05319617156aad2cc0dfdd46f0e30f776de3324cf5609fecdc63b46f9a6371cff417214e109b1b2db3c10962730306fe2c366ff2187993666597379167699fa7263726719fd87bb79a8ecd8de93b3c3d263615e53423299de2f85861432446ded3ac2d661edfe61cf3589ec57d5ecb79ccf6ec383e4ccb26cf7470d7e251724c45eec3748bf491cd4d133a5314116213448a1d9e1e6ba4a708164566cf6c9101a3078c9e4d2f65c0b87587111f1fa6ad8d0dd6a3fe9a9c0798328169b5483eba297ff7a6c39449d66ef298ec26cdda9edd44b372549bfeee4d2e9f9dc5e9edf32a13fd61ff6e76f94d7f98a6f4dfd55ff6188dd33e3bd049b287334c55d0b68d3e182dee5286a97a897428b964bad4e5124c69f25bebb6a2cd44a128c80a8f7a9efe2e8c4b3ca251c76d9a9761f1070c9d2339602af41c1ef0475e06bdcbd8f3401efee8d2f82a9b9668e6674bc4f13cd975f9629e1e9d1d1bd10690063d8d3788a14cea48eeb3dd357a9d461b40143dc57468747462122df2d4de808e0fb9cf6a180f26a45bd9b97961cfdedace25117190355a26e7baccbc87b58c7cf717a9cdd05cc3be65e75632cc44f5305134af600e67e7d863ad831b7428e7419af57d7e2c944b5dc4880f11e953c4880f91b8bda6b391b2d332292797b288111f223fc9fbbc88119f4ffb268948fd6171c738287ff629ffdee5cf6a904e1f9bbfd3133139a59735206aa294a39452508c83a6fc4d37c1a68c71311f0cf32182c50f18201bd9f231e2c5114983d908aa01d9808e00d9c418639c628a1a3ac51437480c52ff61d2c7c77e3925b3bacba36456ca517e925927ffbee59879d0b59c631ef42cc7c877a792fb300d33e535bd557293a76494cc9a07fd24e7a8bacb7f9905f221026dcc4d2935792f65d14b323e974927c923d143aff98301812e12fd48ef10b57cb0d6a1830be702c219cd60126406dbc1d7cfc7d44ffd89b27f911603da046652f432c0e80177f080363af8058c50043ba6e2796438f04894c2888e91a8c92812318e943008c66922e2c0523c845f41985affc56079eee021875801bd88345408b26fa684251f6c29afdf4c07315171478e35f147023045b5119d3b33e50583fe7a4c3cd42cf8c8822ff8e637f597831e5e75a86572a656c547fd4d9978aabf39939a977799f29fd205945215235cf4743f48f5b7838fd8c4a61a088a8550610246c78b0e25253081b357a5bdb2e3e9e5293899b0d9f1271127b5823f7b477a47b0a308f66adb2dd719a0c303246a6ea95fe429dee50a8c37e462110f48b7230db7638b3f681b7210b8178880dc5403816dc839609eea68c5954158a80302914715855029361975e4099e07dabcaeab5e578d39ee79884d786dce3b3fe7fcea538bf19a578c33ce38e3cc74aed0a0ce8e9f74ce9803bcba70f68503bfba6ee484775e37b3e54ac5396178a4d261811e240b961d7ec70f3c240b7778208290912ca143d1e1e89074485272f81d3ede489693c347987218e3f0318864f9613ed4878f52481693437a883a0cc0a1000e1fe163159285c6e1000e6f385c397cb442b2d8704880c3ebf00018c952faa9060314e0abef03d470992d2d2c04c82af074ba0df92a1658c927efe1865c3ab9c000f2a8b48346c63fcc38d970e2b16bb84004e064c32908d4c986930d353c1e0099d6a0a2a10c93be509c4c261d4cf24a8d07e83c630526e7133ccd50b5e4124499ce924f3af0f5089e4ca61d9b3e7231348627d3e88794bc611e28f96e409c646a0ae2cb109e4c26872613bd92d9f9a414802470fc6a740178ba8ceceb8624f7004fd7435824912d004fd74145704659073c5dc756347180ac430d8fdb8112096280ec0251d70b90214475b0676647946b70dc0ed84587505ed96a3c009467682b30389fb2192a2fab60271d7b1e53b100974fee7bd872c99e5c60544b3bb28c2f6c458696b0d64a51852ea0903101df212930828604c6806087c075e706f8ea5c1ce01be4fe604c090d0b5c57a0832b0aaa15be00e40c5f686161c0160d591270688b6d092cb4600ad91070b6042e8ca00101df0d8778810acd0a6c371ce2851d2fe8542f6c5ea02902e38211cd0b20b727db8209ac163e18154850bb7073b918820a2e2347ac0e4a9045901364f1c3040ab4b8292205467686705144178e4c14bc5b1c49c32644109a2c30de7088133f603881856cc7092858c10921645738214555021e6d38c4099c5aaface172037e0b9f1990200c4d9b2351688224969eecb84d96b44c62d74af939b50e3e5ad64bd2295de68c134a49ea02f0850655a29c74442fd145e75543d582ecc517f3eccb44e11e2c832fd067432299c669dbb6711ad7659d8779f85e746290bde80485aaf480e088e9bc42a2eba25334a223d2bce8249148d8035b3ee14b97aefaa293845412afbdf6bae8bce82c2195e0b5d7e27fb6bcfc90075e23752f90467c2942630c5abcb6c41ffccc867a15773cbd284c8d13a695ce73801e44696864961b523df509cb1929af09ee95a718e5ac9f7a35e79c53df8982aff994ce4b860e8ebf6106d0fa402a2785d75e10b5d7bee6a6327d2bbcc7728d4bea3ecbad1a9714762dd3260685b66cb45dcb7138e6b96e4e128610c28b864a23ecce39e9a4934e92c92ca1d742223013057fd5a02748634e3a29a574d249a1d64a5b10fbb6fe66dbc3b499a6ade780ad56577d228934afaeebc22bbda7a95e96d4c4a5975e6aeb15845e5baf2794d0346dbbe8b5f57ae2463ae98df2ce39e913975e6aeb15845e5baf27ea652fad970d72d57ad927e8add713975e6aeb15845e5baf27c42027a5d5621896d9da838e0c07378a1ac096ec52f3883bd892c7f683553f9aa66d17de188f8d3dda6f5c3cc5b1d45a7ac3715c375f5c5c5cee553d09035f43b6449b169b1b425602d83f25bb2f8a4304126f90f53c62cde9bf805c561eb1e6d291e727d6781e87e3dee372907b1e6fdae1caa58d3617c48147bc411ecac0cab82351118798036f90efa901a421af3d758c02d290bf2148147883d0c6b5f7466cce2b455bb81b0830dc828a0ca312d5cab997ff8153d967f61f38857d1e4b96280a1dce6c66fa93d1e42f32fb9cf35a2c71205209ee4846318acee59049cec9b073397b29478ee38e71190e1ff59f3c4c652088d59c57bc2e79c928638c71ce39a7bdee9cf49fcce47c9cc7e6bc337297d4305867f395354992b95fa9fa2269a690c3e171c7dd41879c0ee1eca2839661c43f9c0f280f96371007a1c883ab842bb63428dc215be4205342e489316a71c5d64eb5671c0eedeeec188723c3b430c1c67e391c98e5705c3a846db308c2b6af1c0e7b69cb5e6dfb4ac0beb210c20811f6758a894441c65ecd8037f6dd82cf9e5c90c19e5c48c39e5d78c29eb30b65d802cd9e375b8b581898e796e167d3d3df2ba0b029bdbd4418a2852836a5af578b2d0b3918c0a643b2a005ec046d9934315399117fec558d1b14422c36a547c426aab057310a241cd9f4f417977e5a41c3a67779d101e60a23d8f43a7ae8c1a6dfc143c8a60fe20a26d02ca1e60a21b0f98173052e8214e1d16981648143b0b0854d630f8cc058075b5e62dc050faec0a92ea6130056d86c27c06fe5645a87b78bff03a7bad3d36e643a363936390bdf14510ea96853fc93c77447d12c93773f31797796e998461a69a4a6e3c798bcc3373da6bbc9480c380b1caf792f7925a0efa1d2c80a1b222739076594f141b94b49f19ef2508a7794929c8120705e756408c3a6d7b4b861dba66d1a26310cc32266354fcbd10848831e3b4167c3824d1f69a4596643210fbff456f7924c5b41f989cbb5a71c45c35a49cbf20a880d4b39b778c4339cbf0cafeec6220dce464e7e50b09fe86f7bca51dea5a0e86f3bcabb9f60c7fadbf497e3e4f83128dd4ff05f6606c6c65d04e6c6f42a6e4cf758007673455270a49b40d74a19a644201c0a792f49959090462210f6608aa39824e1d0eb7d037703a51db513ad1f5e1e49143d2e51c9d2c2f16ee2bed9b9c1e1787a7c4c37f1e4db4b27bde4418860715324e397f2e8337dcb9ce771f76ef2b89f944c3d687cc0346c0acdb06996591e3f2b08c2821b186cfa78739359537d96615a2d929bbc457ae9dadc628e43401a47fc6815b1e95b90c6a5a67fd761d2ac1cecac79ef9f8613a583d32cf9ed7167a2e86f963b04058ef46eee26577137e17eb56af49296911ffd457ae7f27693ccbd9447f3de55f3de31ad92df3eb7a685ae711dc7719eb77dc48d442110f6f4cb44ad6cfada7dace44c20e5a73cebfbbc82e8ecd0d868343435104339e950ae14ce2bd901451d9e2a687a24e904a650cee5ee11638cbba7e498f232cb74d2419995a3329d74d67792ef924eb26bb2d68a558c8b1d2e767ee022b358479365d7300d8382a281995d7bc0b44a2f39c975c893aea57c2685923facbb0abf3bf64ecbe4605ac53d45cb98cefde433294dc3342d33e9ec44205147cb30bc438fa7902c571318673fd1329ffeb0cf9370efb48c497f98feaece314f72953ce9f2a473afb65e23510854c25ec7d14c54ab8888cefa151e49543d9e2d5407314bb5255b5e2f235be8e7a314b2656a1b5f4f73ebc74a8abe06d36af3ddd7e69d1e3f66ded378c23044d9845877bd6402b471335bbc974680a7106608ed95ec8959d062d7fa8ca58541959326524a1819524594524252d2a4919452824026071d0564722d6e3a839e9243976172955246b9289b5ce5cb6c441b7420b6c41be8c0233af088b558a6a30ea4512fb2c1f241401bf7f51c802df208d8912b7dc07cc71ee201a664fccb2dfd94cb4b181414130d532a99e4883351f528d96a5ddf2a794abd8cdc4ab949343d25b74807dd24b74a1e9246648ff4813688d45a034370c20a32a4eca77dc81e54a279459eec71765999e1054ec14c94fd953faa754859d8631f77ec238e7d0c02533557bf7d88ebdca5d7bcdcc344d977358c601e6716d91a0a4e9482c73e566167cdb6d65a6bed9c35f66a62db4e479eec5f643c62db93bac0f341cc964e7ff4dd61ca9ec76cf1b6778fd9de7167d56f8fe1debdd3acebdc11b225e2d847ab57775b7bcdc405ceac5691277aef6e233ed8f173da5c5283c2a7833bd58ba43fcc820d7ec0e026270c653822d4853078010d477040e40b8ab91da64ade45fef2272f34eedffdb43494fe9e52ba7df7f0d65a5930640a57f448c13383548d3636d188fa0f0a6c836111ecfa036ddcc363950551f55206d2a83f1102ee997508ac02da98bbd62dec5aaf80c2aebf7250bfa8bf185e59236eac25ab63bca054bcf79b96c9b95ab5ddd332dcb7cb74f11ad7e5bd05f7ee2bc8e59954976752f0de7f52eac896183e71270aa94e08419023110b2af66ae39cd0a2c25ec59d5aabccaea5912c2b08463d2259a0135a6048967b825d2f97bf7b972e7f57fb5071ef7cf000539fa8fa1e3cc094f64ecbc8946652f32b977998d2beb980616a3b4c5119c9029d20441176ed3257df8a417010e004b6893bb385c7d2d89a7ac43b7e9195eb323d97bdfc4581b35ac190a7fa2c4b9c6311590369d4d723d006ac5fc096eed58260d7ac1ed9b5d31ad3abbb8a3d31c3590fb7d4b12ac520c01698aab7de4caf30a657a57aefb5bfd75a9c4b8a6ea0419528f0a783bb0f4695a5f616b0decab4764c944419c1ab4805153c910a385de00eb06a8a21a59452d0246d18ab30bb30efc518c693665202bc2a9524cb6afb499cf341cc9b6b66dec893bcae1eab6fa378a7fcbcd1202cf263c5968fd0c6b58dfcf4ecc8e1d8467e4ab023b441b7911f20ec086dcc6de42767c78be3018a41072df0c21842800521782105212a421b711bf9b19795d65a68e9b471eb62165f6c47a21045036881092360b085125841848a9e22499c000462c0f9a204dacd486621026708021a7630c4901c1021899d1f1c0143128828030e8e50911d81849ac184084bd0e2094610210934e820878b18bce00948804113864ec8111a6e1ee164c3215d80610b9f96991364f153861a1b248290020610f9c20812d00081e78713231fd8706d90b0090311459860051262d0050c74963085163610528622b432aca0bb26a0c2dd465b174a48c3cb90051c4f6ad470b9f1824b9864c3215d54015f44e8204fb5c6084738a20233f84021f3e20c341883144ac800c81448ba88718bd00b88c20a5fc8a2082668c10512941082186460c50c7c308531e888c630045309565fb8c119b6c00152869d511521a9051b9418e20a42dc5ce1838417359e172ab8740464a0c29409e17384204f20020d3b2a3facf8c11658104184119061089010322081ba403e800384128461070a5afc80573043088cc88289224ce1092facb079a185c925c11524631043954e10e2851226362226386e1303184a861083266217432200361cd285135d2441e2026b74cefb22471b0ee9a266afee6e02c38b361cf28531ecd5bd9bc462db34d21e854038c6df792d5efb15ddf2c6d8295b9b1b9651b646372c9fec6f6b75c3b2696b76c3b2c9d6ee86e5d2d6b00dcb255bcb362c936c4ddbb04cdadab66179b4356ec372686bdd8665d0d6bc0dcb786b78c3b2b735d086e56e6ba10dcbdcd6441b96b7ad8d362c6b5b236d58ceb646b26119db5ac986e5bbb5d28665bb35930dcb756ba60dcbd7d6be0dcb746b271b96e7d650362ccbada56c31505262fcfa686f79bfaa7be420daf497dee19b7a2052134c196f9a1197b6937a096f9853671b62d1ca11d553b443d8db67e2965ed775ab1f6315ae1b5c8bf72a07bc7bf0d8d7f7b5784f4a5f5536d53fa86c284f33b20630ea8ce58942781523e0d5b6e9489e288d7a244ff487f873422fba7fa0515401d2882ca0f252fa604f39e79c73ce39a7ea003e38fe00463840153038825774d3fb65d8e3ceabefae3efbb8b1bddadf75926d63bfc82cb2007e0179e44987ac29e9e015dd13428d336c9bf717b660bf7a456f75cd2b950db7bb577425936113b25c17a5f033218841b3e54ebcc7235bbc88061c39af4706f8e260f8d5f57a0eb0bd4b0a18deeba15e8f64815b4eaabd1e48c3ebd9f2d0eb8136600c1a1a650b54281658dea0248b70c365e29c489f15214ef20b2f3766e0526322b1dd2033547e521a9d8e8cb09ca2c68c93ca4f4678bb7402b92ff84969843715225081d2cb524aaf8b527b5da5aff858122cc184baddadd6aa4411945e96527a5d94daebc2a393252a17b28cd62ccb28cdb24ae94f2e45a4a818638cb1e7799ee779b5eb3a25b6c8325ab32ca334cb2aa5b874aa46b28cd62ccb28cdb24ae96985c716e1397969fbe874c2275c3af1d42d507a594ae975516aafeb86102a8accd82bdbe844c5c505bb6017979f5c6c50bf0a75b7ab5ded6ad7711cc7711c57b7ad564b2bdd6ab5b4564b6ba5d4da4a2ba5d6da4aa9666b96654af020cb68cdb28cd22cab94964e5494e0398247e5625c2a9dd2cb524aaf8b527b5d2a2a2ab874a2b2048f1155dbb8ce2b618c31c69ee7799ee7d9aeeb3c23945e96527a5d94daebbaca8c257a3063c68c3be3ce285d6586fd4196d19a6519a55956293dcda861b930da7075b3af32edab6dfbaae356a1aeebbaaee3388ee338ce6edbb60417371d6419ad5996519a659552979e2b0297192f3c3c3c3c3ca7151e7b4584424affe9b4b2e2c2002157c81572855c2157c88a10971b50c81502a597a5945e17a5f6ba566adc58e202f1d1630281adb5945a4bb39be95aaba5b5d65a2dadb4da4aa9b5b6526a6dadf88a820655e0def0c6defa81b3b77e3c158219c68f95544e8cb4d6bac57be966376bed76e9adb6daedd6de4b6fb576d3348dd2aabd564a2fb51aa555cbacbd97de6aadb5d9a5d766b53ed65a6bacb5566b312a638d1b4a194b5c8c042aa76d748267cbca0a5ec12b5f99b6881bb854fa4fa79515972ca335cb324ab3ac527ae3c68d7be3deb8374e2b7b7563a5c60d1394b68f4e272b2a3368d421a2e47b760f863d11f6ac6c17d843f3e3e4625cca325ab32ca334cb2aa52727f7045f57b5af7a757d758265b4522ca3b5625846abb5b6d26aadb595521b6b1671c6af6a5c8c4ba5c7c802e5e94e54bc945e9cbd8fafb15a38e5ad1c8e1a11257f2ffe5ed588a7d5699fb81c66ecaa571487fb95dcf630e280ddea55fdaac6bdd9a5f7b2d7cd7eefbdf45ed6de0c421af7d16e40ee206936d53d6618616c7af9817d3dfac01ca08511c65ec9507a2a33af28e1bc34ce17f922a1d5022c62059127c8934f153d4278e02f94c22548133849404a730068703c0582de06488d3cb5e4a9ae946864e0e705048f1f765ca0070bd0a9439ee0495800218410420821dc21c61b643c7184943622102c179d3ee40642ca7803c481521a81905b4a4a6984f106f8c905ed4a16083d88e52f4ae927d451473d2509a494524a29a594524a29a5bc4e3a5401c0e1039f16a8042b5bc98a542aa5660600000008006316003020100c074402b12ccbb33453dc0314801277944c6a549c4aa324495214648c01c61040080100000308102846201b0050b7b852d72d16177ccbcfa856c15a7ebe85802eb1b02341cf1e049cf7f3672e4be7c9b0bd6964c41612f4b2287008832d7b10a111cfd06f9ea0f52d3a4a881fd4141bbcbfacdd972747f6cb88a5b6c94312fe119987fac4954f087dc82213fe4f5ab7745c5cface1b1ca0b5859f8c8a006c5cbb0cee25c59a71c35f1e43ed0cadce7cadc3a5ccd398718b857cd21e717c73ea1a88ff4a8622bea4b4cb1ab99e5497d2467eb9f219b7c091b9610d788749fd63b9b8a2e717a9b1396e25612c80968fbed8dab93d3c333233dae7e65d223a888d30edc2e86ed380d319cc1e512e82221b70bdb08b343d380a3758d10ac63da7852b0c8c63828ee864416e96fe8b1cfd1e0d0029858f2795107b54f4574bbd70df3bd6e1ecf6865555495b3d3dc4694a04920b0da3443239046790a4c6d5f6fbbb256a5c2c9a8714dca363dbb343326116196daa5711eaab1adae2fe2f4e8baeb3d949ae14b520e58e1514cfa0c256b7a45144c6df748c099e54c8fd3f1b2c2d8be5e9d36dba4f39ec1af2ce261e38bccf6dfa71ad3145b4a1db464bb36a8a9ce2dae10f3da954adab68fac30478a2aab7eaec988edfb7717e901459f70fd64a331447fb6d5085f325b821ed7bd4dc9d2ddfec0b0f9752e9e5ed7d1f995e99639c6769f3513270d8df9275be234634a068736063fed4d514d86abed2715991cfbee53268ef6715c7ad7081ee33c652370becbbe7cb257ab21dbffd50a4900cc757839fb608f8db99d54eb9c62d8a4ebea53fc283489adc1ec857176d13cfd87a10618abac8797dccd5cce09124bf5459ccb3874840747139152298637adacac766de6b374a1b6ad6e31086ea7040254e84dbf8bc65b4f43c1ed17434c8f036bbe928e4be9450406ffa74d965841b56519296d3be92b0c300d6395170eadafb3038d3c834a29b787f46a7be16750ff002502642c09db0fb019710820056f3be0806dd70c76b0d5f69a8624e25a96097f5370e7dcb4a3b6f32e829c38737d3524d6cd374650f5523c0ca06a0f1a6465be77e65f8dd2513f1db100b7a2384e1db683f234d48948057533d2b70e1e43900c52660781d6cfa4ac2f730b030bbd35fc457ca312fe4d71b7a6df2083d4fad4e4b4e40e355f74b0fe8e6cf911d18446430a543202f1506f9d745585ce61b9dd8b0d3826dd4b8482d438353b2c51d700d49104d099f1c79c96c026cc71482d75e5694a5d55d6f8656f10c8f9523a1d75196617ef8e3fd3b36ceb3cbc88bbd3d7853db93e80d1d8e4273ceae154f6994a73dcab374aeb1e4083ee8094e64a252d95ab77183b9a07d1013c269abdbe1c559dc58febd61858e1797d1611d79e1aa5c2e3cce1f88936ae906ca5ee7ee219dcd3b81cee22ede8a56cca33c84aa3acdf25bebb049a4ae80c1e02ee7d108cc0ee125dcdd2338a4d2a6acdbc43fc460b2bfe5fe695452edc56b8d0abb9cf2fd61aa8029c5cbd36fd2612f663e45f742862f1034b7703a8e30c856f362d5332f0865d837a9b15054e61ccd4d83a4b5fc481b0167ecfdf5565e8ed3458b5038824a643d10c8136d2e9b04f019b7be0dad3904aa91df9a2771f36d79c2c80720df310fd8a3d644e4f149a5a040f882c782001de590cea0cdfbd966c24aa7e52494278fc8b474571ed2c612101ea3355e70ad3c1c8fe43f4498f69cd9d021f29c2abd1bb503f55b79c53f62704496b7b58e31b5bcdf15df302eec6ab2ce30a7ed5d39692200790cb068da0dce889855f168796e469662456d512dd326d3cf528fa850955828717904ee467ec8df6f8d5ac5b80bc8aa472bc9851740bc67ae7f1826fb8314a0cbe952b93eee394d095bf2b7e87f60d472a10880eacdc91655719b8f724e89b6149edc772e1d2624e040f8f6b5bbee5195a9911b2811d17d8b51fa21e6bf17b44190eaadf2a4a6033d8243e2669a0401ce2bc80ce6ea55de4dc0e07cd3e3e0823cd6d25b58b63ea1eefafc7945d335fb424ab283591d0cfe735604162bc9d211a7a39079af1296043ac1bdf9cc4c4752dc14203bc496a55b92114d00c7e39d2c5e440989667fb7205c3dfbf2963780d589947a7156983151973014ab66abd25569ce83f8206aa4f385626611fa4174470ae6a434a650cbd8c555c680be0fe160f0d7e1dfb64098c406bb130c9ede8c95c9e02ecbffc2715e56206d5c305046c2a43adaa5b258002c5906d3a97a380f61d2b631d0c59b4f70152e506e636daece96079386beb66a588b70daa24bb6952fc41e88e02315a198265d62040f392e6a1023a98cf396b2cbb09fe0bb51615cf4c5043a6a9827d3d6d10e347411a2810b44f8dd7fb4b372362216af60cca3d149d476d969015372f6822eb7cc4b260a59f392d95fe8a9a0c523cfac8cd5028dbc6bfc4d2c28d84b62f2e1c52f59e2936e9c503f497694dcbd04ffaf9d6850cc2b34ab286a5e691f233a24f6c4097eab88123d11ab904c101c9cda5372cad2d2587510dc30dec82f1045814780b70a2e3993cb9e9426c86ead45d740ec1e505188947a695054bc5227c8e9c88db6d40932617c906126f66c74b26152a8f414e0ba13df14af5122c5499964e88cbbc37e6478c10799e6d48f4d40f1ad9faebe170704bcdba79ec0e0268c83fc5b9cb86212eae97aa14005c808a11260c817358089cfdc63d7032a5b87cbfdc2325111ac31b5bc6fe41bfa478b3c2993c9c7e71f51a5a8c878422f85a58b4482a07811b2d7bfa0f26fd9949719e85313d23548e5a2806dd4b12aebc00be1bbdddfdf0f9eda963f97d0cd4035036c15816dec3e77fe8bd679af4e020eee550cae59325509205247ddecf70226ce1c146efecd700f354f996aeff0701f11005259a1ef83ec38a8ad069da0ccf4eb0209911f694977101c8d08bb883ccd5f4f44e1efc83eee81bbd53af1b718d0d450ffee29bbcbfb7a34be6d9c850e52be61e1ebf002e422fd85d840fb6163c8e1ec1e4ff0ce5c14a08e16363e0e0d9ccd5131ef3490a8f7587ba978824fb0fd0f7d9151f99b23496155bcd65860ce7dd7dcfe62283879b5cd585abf65d0849964e90874f02853ef295c7a30c7c2314a06aa591f49899da98b190b71474355d53c33fce1cd83e5f595ef928f4b8ff204f3ef6eba78b17e23024d984465ef2481980183f04ddaa73c105537a2f3ba39e5506532141b7d5bc025bee691cc45a9b87f469fba82543354c895dcbb372eb91b3a4d0f83a886b1605b61293621fa7d19291a255806b6ebbc24ba681f5ff9d28120411621791ef2350a36ccc8e013ebe6b9f31e872b605e8e4e03ba1e21fc76b8373764bd4829fe60071beff0282006116bfcf5daa6bc8d0106db4ff38944d924c9cf93b2100b21ceaf2023c7724397a36bc8a41affdbb802b94ece0668c412eef9c183daa15aa4c67657c19814c0e00c5bd622d2563a195188b09410421c95459488cc86430a2f46d451241ae6f69deac8b64ea811182d4900504b9cdea7c9f49cc601d396452274648b19433de94eb3bf04a52437b25458608930397b8fa6c94cea80dfb0941987ab4060cb2a9d5bc8505556943e2a51680bab128f0d8d2a2ad2be8a50aefe7b657f86c95566530abe8c37640decedb79c4263e53abb2fb9a614c3df6892148ff6ac84135a684b1b63184746894c1a1f215ea0474b187e4c296a61b706739733fc80ac093b2cca1f5aa4e9104791988a7cc6e993370eae80ba85af50b732434f55eedee8e4e0c0bc9fd4833238cc4cf01d92bea433aff78cf20e03379516cd1cca4472cd79b9ce0cd64a43231128cf69809ac06d3a85a10c5364d0df063909c5c056504f67b8edc23d45a44484cf08235363af02a201cdb66df592c5b248a5b3b391aa0c103f862c04671a434c230a36748d15e4ee333aab7ee8fd1dd4095fdc1a9fe698c3b548a788f5e7998e475afc90dab515ff6dc43a7ade910292d23cbeb931ba134fa90b64ecb4f7da6d5d969946f4be1cbfa79f2690cfdc4ecbad2a0e63753d9d0116511a9a36fe21eb04b7745e3e1a365d2afd7df4abda62012a3adce91ddd2454b51b32b233174f6b2fef3496e4fd60937ada2a6321951ed7a93fd1073e8d1b2f36f0c32ec6ea2de80807f2d23e1624160f612b2db7c823f3733ebe1f4d30dbd4e53d9fa9338f6d306eed71981556a5d31a41a3af9ba4edc25b1a52a00a8c63e3f58a4ad3c835decbd7c05b4bc13b012d13808f6f843d4df99a0150e82f6bd54c1761058d91a8697e37a61e70ea2e4d46e1e6282d04fe09dcaa94b1e7e5d65467e6a761a50374b8fa00f70714755e5082e132a906ef117b62389c1afaca726878b25a855d7af1effaaa7d0de5d1ea324b9cc7b0b97446735c43a5a94d534a406b90d534cfd71968e0d8bf7fbe0da013be04049df76e1ff61c330871b1d2452e83caafab43f1d04a4fca113a0b17ff1495ae2502e08950009e91a5e1089ca2a75f6e68987c569a811ed3144ca4639f177c74c38c618e570c8802208d4eae27cbdf4d4bd95207850e5b10ed579fb27214a6f62edf353b0e52e9b277f2fbb3c6b6d7bc1dfa78e127076e18269ec2002e6c52b637c662de88f79b73aafaa0984fd5314fd81a5b65f3a7b032f4775646ff6e2b830fe21d50c4fb81e32966664ab0d73512c940b5abaca0addd3edba0b4601c5304710bb881f63f69c741a83c464ca074713bae89218807952ee712ca9fdc6315b73a997829421c276b0a0d6628206c1aa0a12871b880e2a60aa7c9771c80301ac7cdbb377b46b6cebc39c1a378d27f39c702d18dfe317b2ea76ab033b60806f39367de12004aacba460ff7faed70dc96e7d0b03fa8020dbec7655639407c7460b661c6e92c482df2474f1f6f1074c11fbdfa1893d56aeaeae5b7af1a5eb20941910f9d026a30fff075ee6c429181697d40ac0a7cd1e3f0bdf561cf0606100354b2808b67fe72ed156d5b9917cf683686767220ee324b0ed305f81db51403e153d66fe6b484c3caaa87d7eb792f9c7bb0c8f8986b39f8386a677392abcd7525e103d3ebf121a0987c1c81311f4da4b85ee11c100d9415f47ad0fa9c3850cca2c4807cd889ee83b9abd8900b2c6c58a4d6e4681259834e93d5ff08674bf9291bf94a96a1ec6e7c7e43ae0139c918c4aa6a411989801b7198c0a0f3fef6c19e73061013f45213ca08123637455787dcf189fa91de7a0176317fd66e7bf4bbcdb0dfb736aecad21cd07814fde5624d81f7a79d6602ae8b2947138897ed048b04dfc108dc5919126ff72da714e5a1c4b4d3b05bf1fbd0635b57025ba7cdd9fdb0b2356218f43a60e7a4310be511237885f04e1de503f95ffc97573ae19ab1421348565c4da4cd9d63db1c689d8cc18a74fab59a0cc5a8ce0afa6262f771df288d294920fc74c1b6c5cceb5808040206bff9ab42d656c3945ef735e1880745dfc65ca3baf552b7730468005e37227c881a4b9f94b1c9ca709e536f58d654ecff336dc011cd89b5b94d07ad8f83edb9c6beeea9ba2410034374094925c67310812f0f2bc02370c1247b57e6dc2b7edd2b2b67b957729273861f487c7d001d164967f07eb93132abfe7ae6fe2ec16341c2adf3d27f0a045228962c5f4364192203b79b245c91da88b01a76c873d2bb43d10d1a7eeeabd86f4e28aacad56ae22419288870955c05025384c2585a99d10a5dcf24247a416ebd4d08e32abf19409113238563aac693f44b7b0d114430b31d1a495ac1b33d336ce60dc702045ed277c38d637089879886b7e2d10f5d5a564d2ff3f0c8d5105e3cdc23118a44d538fe32274a9c0586a0c82c81ea5e7ad76e766ed03ee1a53c56795f9aca30d43542462ed4620f951a165dfa11266ea93a3d96cd1818017ee69ef44ddb9e959c7b0705de269d5abfada1dd324effa2ab1b923d51ac9f75eae2a6a3d24b0b0bbffe3d012e82ed36e14cb2d12d0f92fad567038bf3e213189a4b6064322b21007eba61d632c76b06202cf823ba8d07c280345d0ed8b47b0444279c2c32e0ac40df5dad54e1fb0b546f9ac17d76637a73066b494e80b9eb89e3353a03cd8767b4bdcd1e370f3214d214ea8822aea093857dcb50e39c1b3f55807aee18820ba897d4696e0500519f7f4563a3b9e71e10eea54aaa23fab48faf22d5643ca1c9f58a3deaf31beb33af26acc45a1d3daae05abbaf8d1b289ec445fb27bc2371f123f7b6269441263c9b10003f1d106399e33403a66089faa79b4d43b1dce4fbbfdce34238db88c963be6707f71cd98b8ac373320770150fa91e6226863c00c972026650cf8310f4c526257cb7e0ce3f10a4dc7b6cdd6921c5ac5e0ba35d5aadbfef3c001346d8329658f9d086277afb18398846276069d1455a1a971a25634700da92b9f53cb99e0fafb84822e0fa2280fdc7c6d306febe80552a853d947e50ddd678306ea68e23ea754e235ef129b714a8ac8fd3451976085eac15a1ccd364286156be90ac86aa7623c56641404ce4b512d78dae2337b25dac83c9f7146957a7b5ea72f60e6ec8a13dbc7c57fc03dd94dbb901b38353339c33fe15d1e9d0be03e4988681eee433471797e3842cbd3c3c972392e90cccadf9b96e0d013a8227754ecef510f9ff77850e8826b5c4162c25f6b73f3e381914b185a63db7bc1314e53f27e7894e4e19c0f89936b0cd778b1ed5234c8e8827c6321bf35489c5fa7bf4c861e83f0d0b7f10e067d7ded8d922a5461fbb8f0eda43a8a1865a34a150924c478201d513d5b0588ba15f8f85539b3df889231f42c72bc24082669938080ad221fc51cc59a681881fe694f8c07be2dbbbf4f1d50c5eccfb1ef9ee4cd4f8abe862c07340ad49f3dab86c04e301426919ea8eccf78519355a183e46f8dff68a0b1d42d2533878877c4d3077643238617c8a6c836290ce36c4949b03fa4e5889d3d05373565afdf77b9394af52386e3d024546a7b14c85f48a14865e0462923ea509d183d6a06fe52b4d0e40e57ed33f07836862dbfe738eafbb625597e923d034c11a9395a0c8047163430ec44be8c87a77af9f7a42fcdb893d730a58ff6605ab1d449894d13762bfbd1c74bdb1f72feb34d3b477147010e0dda67a6ec75ffc5c20f76286e553addafd7d8c0f73994b9fcff0a16453f056e6c1ca5c659a626db5078420dbed190c92684985d0b174a80a553efe92a181273071cc06709c23dddfcddaac2721adaaa3fe221944470064c7ee6d37ca4abf19108b6ed3dcee7805226ba3cc7ed8b74abd6371ddd466d2843924d80d4364b575233c12731a4472afbe3b0675a32c13d7a97ab832fc5184db35c9597c3d9ad255215ff493dc0723692632bfbf78aba2c96b15c1859ba587a27e9c54409338a2436bba9f131594054f0963b88e98a28ea889320110c4d7398d44a6618dc93b1bda8b48ab35cdfe014d4eab9bd4559456575420bb97609eca4b88347848fab34a161a27bc3c031e7a0f75a05b381e1d7a2d8fe8ff67a0a3cf20c32bb307e26dd4881de0912ef24c39d05028dccfd09c3c0319909c415aebcf1bc64aa415fe6911cdaa409f5e1dfe9268e3f9e1a3c6db9db661d298ec3ff258fcda44bb751a90e5690723e83a8274888d424d7e9fe66b6e6ef7558f1553bbc4a2e1d25086465f6d2ce9a17b09d47342ce5c06b8f5b11b0d405adcba565ae5f8be0ddd0f25499e9c06fba34eebd3a7f40f72812011391814aefa023053a2fcb0c1b1545743b1ee85a61195da35429b949d72590b2b4a817e53a3408f667d966cfce1047785e61bed858788e2287cdc1ed8be1f934ac2af88c52bf4a332915fe4e87f62f456ce84c6620308283e22fda0c7a54db219c00b33128bc288cf8f476ab6acaafc2598321389ad6bd9345ad13209ea9c1ca60fc47eaa46d8bea5e773fd248008ce5e0e8023fea4555379515ea4441de46b970600a8a2053fa2e54fbe188e9e198e938ba359f9284a3c1892c514b078f886b72537fc6a08b066b412880dcb13271c80e8a99bcd334e3d912a737198394ab662e98e1c85ee6e725a0e80d5dbfb5a07c58c08fd2b2fb93251b824424295eb621d63a7517416b41739aaa0338718d7c348d3a7f444f226f121e0e510c48e8265c7516ae18be42518d0789d87251caa2b885e00bbc9235c719f778cbe806b20bf61be44353fe1b0c840d440d4deeda23966910e236ec0dd4b8ed5f9f559844a6bb0a0be492aac6901fa27a9d2b416a45f1154a61a16d467095ee92d1a1644d8339e8110f8c5087648c6ab8fb748fb1c2385c39b84fdeb144703a5ff984b71dafa3ad012b80f26893621b6384ce4368b9a101aa38b2e4e6c16878377eeea41e7fcc824b559340aaf238e0e2e03412319b93a66963a57242a9cee5d8fa8428ad89d657ee9353c01d79098dd7a84283c58105573498a85feb194a5121975f2357be6c88bbb2c9738d487008d67fc387b39b378cc058944e1d7503090e18349f027e60fe8b5b1b9997b018e6cc1f18a8f5602f1d4f4a9c02c8bff57fede1d2720dba9d1c3fd8d3b5c1cdaae47ffa7fbc707088a5e22a748aa05c64a82995080fe7d16d90f90719d880e7781f9fc57378cbb0eb74e75541caea003f78fdd7102d25e1fc8cfcf78bbc39672556eaff0a059cb12c86213a3d12f7ee8b937e0b2c328d8705fb3be73f5e0a690a29857b00c467630dafefd6f8b7bbb8d9472ef3e9911cd2bdca0c6fca3f33ec46b0da293623b06b43f2411708f1afbc8885eab26389d6807037a1363cfb154902b9d9503b758a36810599ad81e329592d9757292d1b09ce6140499ee208d070357fb8c8a1ded74d9796118681e490d288627065a8724039acd5303dd73c94e0e282c86186fd7239cf65e1ba34383861b255d8b4d5be0d5b2a27fa5407be942c9ac50a9fa2a2411ed749502da1f0c146e415e8d86f89795320ef24a350a3adf83e017259c35a260316ab8792218683e9774ba9fc2c688e9767af0de90c5dc63c11dc4060aa7eb050dd1b13c5a61b2280aec65cc941354c5565080bc71c75b1d3f5225573c76195e8778dd8485e7af210c612c18a8696d350d844fb81f0eed8978f4b04124b18d8596c4954e9dd994f490f2c86846cd2d6ef1b04d55bb17f9576401de5c1ac68ced9b197eacf88cc77a709cdc014a39064c370304199ae3c026f63aa2a5416f9a23b4517407c1850ad9a2242cec0ea8cdd4b32179cecc60526677689ac72be21d9095a1888d7359d2899a98029da5f25abed78d370dca24a1c32b2fa30b4dd37a5b25c28eb4386e26126b65e76a8c28b54363cae90e703878d2c8d134980cb8d8511f82020714a7ca4bb8c3f4680fbb2054f46b9302253f50848450b97fab682db48dc60571cc1978eab65b18a6c642d45f21252ea9c7f96d06c2db32cceb31621fd41d2a32ca0f5a33a48157c963501dba54eb331a67df940a15be7b4f283be5812a60a47e43c3211c4d91232343e125caea5136f80eacb30abbe899382b96a1c06a9a7988774b4b2af892c458f111310fef1a88a4c2ddddd583de6795f665826fbf77c20a74277d14ba90bdeb450742a76ffb04adf837a43b70ba376f4e167f63226b48538b68a9cb4d7ba799ca44ca33283e3d0a28f6cbd8ea691ba7d630d62d03657c0d286d47dbb8cb0241167af49d89762c78712d0668f96a51a43a46828c9bec1eb6faee4ec250e1136884ec85d3ddea12c25524290c94a222af68ef1d29dee19954147c62937b13911b67c5844c25e7dde4a81473ec9c424ca3ac24849d3c2a09001930a5e38838b54b363ae2e85f2175a73a618fec399104a81cb2ce43212808fcaa66c3352fa971a73d18bc458d02c784b48399c0c706b875285091369b7cb72b2dd34b29379ac7df70c069628e042fe5427ec4a69ce248e88d00537d17a22313bf8281b52b42d33d76fa5918da989367b06082169b1a0515e890cf7d95e5e6df1cdbce7e6810997218ea75ebd22e121c40b12e3314f5b8c9b2cf603099e5a7f4d2dd303b6228fe76b1f39cb866081b4c008829fb6464d314e49cc3f9e0dc82b7bb71e5982e7103f6797cc99457087941abe703fd597a8d28de0beb28d63fa5a39001800d0c1e0382df853734a658c2a48d02177e82f10f93c47f2aeb41338075d529f93d7eea5420d7b00215bed716a9cb242c90e7167fcd7ce0890ad46dc37d0ad8fdac14a2d5941d3fd9f5f49f194efb005d390f2d98f93095c42e71e1c6f78186c48a02e660b617b4de049f0bef68ec58de91f567b86c2749d07a438e776c46e07364ba2ef7b86d4568860900db054defc3e78a37d4f87b20ff8690144fd5f3e1d91d2aee2a1f5802dca4eaa067af74f7eeea8211be87d6d5e0f3542df45e961c21880347963322b4178bb0963dc7120a2a6985705ddfc473b248ca17e43c7d19c7308d77fcb4d07e41ce934fdf9058fa4809d908725f1c2f3487565a291acceabcedd91003d93d3e5f28a6047a0a11922dbfd0900a8a2fa1129209f571197e8e24570029df4a01fa764f8ba01b01f5f152a29025b90fae0295c2e736b5efa5fc6d680b979818ea948407ec26be939cfc8edd596ca2df7c5c8e5648bb9b8e606f21655fb95a5f73d4924fccd54fde6e9fc82a3a7892e5b613462247d27514a1b0adf2e9ae7903265d88659707a9c44a788af13afa90c8af8229214e5a846a1560d208739725cbe61975620e23e29dbcaaa8393ff4f5ed4a6fcbfe2c555b9d26a6e14e6b7f49058532a8212548237805f464a71227a7b246532b49b5cddc4b384b6081954f6ddbb3c4a6ce21a6d7b9b45696919104dc20cefa9ea303275aebe6fe4280916f177b34197499a06bea35c194e11b0d44e5b9a39b06a710fcd9b3ed9d8ce4ce8a7556cc29786f5a34e8c10f0ea9b0b645b0204916c5fe0214d3dbaf500e6dc3027e92a99d90775dd92d644551294f56d203eb720f79eb559e16aa152f228133506c4b117c8dd749018ff8c60b90e0017f33aa436401e88f37c327e99f18f23da94f4b5ff952eadaf9317fd109367b2b4a0839c18c78018b20091b6cda5a92749e8a95c457b7e7d2916b161c9fb1b1b615dda027a621e054137d34a967a4599bf0b0724cc4be1634e7d84c9e12a202336462db2b28ea3936d694d6263c63322add833e0c0af5493e33030d14f177f3606b5f9516a20d6ee81e514cb2fe167d26d41112a3c9c066ee24c941062abd3433dee00dea29df0fe7a1802ad618656c1bd620d846206b5c39fbb416a009ebfe5cc1c0827106bb8bd84bbf9361ece1be6646d5e6d611af53c5ead1c36ab39294b21e7c6fa997f90a61178540200cd6e8d90b8a0bc12bdf1b6a836d2f88310c90bc2f7020c952f9280d0823a6652791241b81b80d44b289b8732fd348ff8e7802f41d40a61fb54ccb0477a469905fb4a7b648fb7d34164e99e3a99a97758a68a6ffab18e2f035c6a8673c509e443c5a936cb69367503cbbee25a40440b6328fc3e59e6225a8573568b4d5c887c59b421e8490eabc94b296af4a57ae49255a113ab9c8a3862c8c1b8099d0494bead9243b6e68f73c01563a42053d8bd156b79b64b10c0c99803c224d5dd3dc44111312d2b0ce7d7b02d6536b4a2a754b13f85ea60092c49f77cf61dbf575c6d249207b16c40653b4c9356a82e5c83f280c25a6a94db17fd58a0ddeab41c3dd9d3acd0e6b0d12efea746f428548c363fc5c1350b377e0b3beb2356b71260dc09d510d67d7f3d8cb4780dddcaad5024a3813e3ba064d4db33ae9cc44e87d64d72cebe0595753f0afb50f95647fe89a6b1c188e2ffe55ed6752322482062735217f1a89a8f63775c14495225b6793078b689ca4c1eb8fdf774510fff6ae73b0b5d015477b2a3d3117b09d8a1065d34ba2f29a80c28a901e945e0e0200c4d2c1a8e77adc1b01fa30ad0511b0c9dcb9a8be73b7d6d48c29d92ffeb37861a33f21a0a918f30753cb4b345b1ed6973959dc221a62ef1351cdd614b341927b156123ee3592bd97307d210b43b3705819435656d50b05cb0775eae1811466d5969c45456a746d108906f6d284302d5f0545fdb4d2db695f9ec36869f460df2e5a16d71a3424539a1c0ad05c723bec9e79077876878fd7772cc71b39e4cfa88ed33aa1cd4fe4f4fc2aeebd61c0bc8a0ed08c7eeeeb3add9c29ab60cab2a082da1e03514d66c91fc552f8d3e3a6435f859dee669e05c493cb606b442b77d0789d7f017f6ccb4028a1a5399cf9b364075df598517db7c00aae86cce9ebea1b98c6e22785cc05cf3ba0a9f173d90be48aaab52ba39fb0c2e99c4875bb8e3de09a6e4de7750cbbcb4882f25b333cb30ce2eb77971f920c2dd5ce287cfb0d87ed24aeff2f939b21a391080a0b8bac2be0097a38f88f644c1e8c8336ea6856ee682c7e91cd0aa37b1d497834aa5f08fcb13ded80ba92aac9c2908b37c28e6854446b80201b7f2d345ba02cae439fc1299889fbbc36007ce4a5197da56e31b231dd83a018d17de98b376bad8c4fc11b03fd0b1d46042449d4c300f25a7025a1f16f57f8e8eabf9a2cfe45dadb4d01ea02922be7255e81aa43d841f2cf78a98c6640069fb277ba7356a7a0484826f8f06c203983f441c72f1b2266cdb23f0b8ae1bafc17822f6f8d8b49d6d3c79173a4666f81b6795cf08c0dcf5c2cbf86dc0c35418e1d89d28bab89570387015951c980f75e1dbc7ecd9bc3bcfd5d5a8b6829bd40d0c56662ede50176a1008f207aa6d5e635073193aa5e3f711807986c079769157ce4f9b414bc372d3a94f8ecdb905e1d7fc4d6cf0087f39870304157e500b45a10c2c558af2c1ac24db5b0c4a38aa6cf4687cafe8dc00c7ea27ee2f9b51bd04605612234cff7521d68a3b4aea721a6075ca3c527998218e04414ca1338d810498836054d712d542ca80e803887828de80da8871fa41ce4e88da259229a68a5916986b242d7f0d8a22822b3d744b42a8c19b68c1528f1054517b9552b42f5fd4fc0bee98144a7c2af66cde7979acd6339285cddcfa44ec52de3d60a39bedc2f983b0466f6f7df732b38943c9ac966a1e3f5eb332eca2a3a3e1725158826fb5f2bb1f10c7beec06b02aa2aa41eede949828535ab8feafed71b48342e9b8765564b41b96b7f2682df90cbe60fb7e572361102834892fb6ffd1d32308137c8361ae67a37d562c193942e1bc66dc6496940a62739c76138656a13344aab3ebc3b7725868f0bd2fa4f86d3bfb6cc08d47ed39f6af9b804454f79f72fb1960eceeb032767113b77ccc4400a2bd1acbfbedb7c9eced1c564a5ccc5ea24f649690f0656cb0a63a8f81ca2618335acd7cd8fa20ea11b49dcc5350fab6e201ffda2343e11e82a4bc085aa30f4df112b962963fef6cde1bf71c7cebd6d487c84f23d15854db0017ac4e4eedc43c50d8313e5b150021fd10df19d14c2bd1199a0486f7c8057499139438dd905d40cd73d85787c591db77159c45dac3175e21de763183de2016ac0aab4498596b0b06131f81b47a3613f0cd9488731df9f8c313a6a5e37fd76bc7d13e0bd0ac93df335432be0bc6e70e19781a082ff5be3c22a5a6625594cbcb9ac65fda84afbec1d8fde25968446b6ecf6b86010cda390d2919c053edf82e594477bbe79547911082e6b6f85821fd1421ad0986dc7738d8656fc0376860d1a9c42b4ddf7d232f2dbe96dbf515ba971f7d41a538fba46e24dda489e0fcea5338f73d61bb07ca8bbadae77ed2687e672852c6245f6ed771fe78dd8382eee84fa9717fb6444bd6a7852658e62a84d327976173c0c9f8782da56121ace03b6e3f5039bb0b56cb1759af688ac0c8aff1982abfe31ac403c1fefa0789c2991fd81d3de958936f2885652063c282791a1dd7cdc367a611de5dab895ed17736510c2d9da22b6d408bf1b57f12ee33c35da73ed341b3562aa9635a699438558e33d24aa3ae2138ad05252c5a434250f1f3a25f2bb93804e621202b0f443d38ba7edcd4ee523aad4300768bc44e81299802f0b9bf6eaad0d50a84900352e604b4f131178663ea7dc06873b9159d8708277c4b71feb9e9b7c4af1886a8beaf42191c799b2df4c155a16f259220369e56c79423816887c5b3cd070ccb4eef9e4f5a67a04c833436012195c830665f14cc626c3eaa5c6b9229e34e04cea8613c9dc8f075a97fa70d5c8f02f3bc333edca3fc10c7c5c6ea57c0961cce55868010c7ec03cdff29950af9243c47b6cc34a3a0ff2d295f4dd251d101f6dcb7497860e4404767e62e97cbb2426449ace2f0b2d33bd4a7fb7c0aaa615c7bb8561baed4ce10f8c382951db1ea75066c23a2c32b2e23c1b8cba0773eb029e0164f04dc306dfd605514879db322bbd1701156dfae9b10936ed01b59f236485fd932c4772db79de0e8ad10923bae9b704fcba9555d30a7812151a61adc8a9114cd26df665db09adcd4f7dc73edae10ff70015f6e27685fe60b6f13b5b2a6963757b83530140791c93b81b41f593633d2d608af5dc449f27692d3276192a9c652b252116b4fded9a2f4b79739aa96176a0069f22195066560a8d6d4cd55a93012373c8f7855c94f1acc057b952e03906960a876a3025b0154ae460de9c1bcd6c4e096cf86f6e32b0aaa146113fe10680bd7c070e2fa5031294a6b82a93c4db2fb53b7cd58bdb1b5d547115bc827d0647155ebd4ed4d1aa6916cb0121241f5b65122e36cd260b97f947a4a9a615bac3fc11ab88e122cebda67715d69cf3f8ba81b7ff3a800c96392692d310f91a322f908fa17f499e21ed23012ac0684c1b487b595a5d9e230daa49255cbdcc1da23e4cdd1e6df4bb57db00e99a24e28024a0c56c252d221010f2572d01ed9024b706c20544a9f58a0176a10a92a2fe179d3b5a6a0bb3f7e587ee420ec7c7c47b4df448aac1ec50ed7d54ecf341f1d52a4611312130a5da7d8c73ee028f2b0c9ffc2671877f7bd053f84201648c33b8ec85044fdcbeec08b47528072547065297a10c854176c4be04c70c6015ce82d55c9f66e880cdac5f50a4c2fcfb5666bf020c0c6ae59232c2562c05427465af3dc0927c05e0f107b9568a73db521accb012ae8e6d75be02763b361f42c5187eba614a70e364d8977267efd50a48642249021fe208b4df35dcf3a93a4550df09001a801f3f1dea62ce78083b9fc9f185f10ba6cfbdf2c9f9505680413eb5ef4ce63314b9c7a9735004a842603575920bf681303f2cc17b717cd766f7bd90726fda5d8752b50760f26b5b7530bdda43372f0d88eda9009af635800dddcf22b1578dc92b61ba8f80dac1b6edc337d125c0b813617dd856f30f31cd0840f299c490248bc0cf6b5bc0382d5881dec6928a41014b2d133b06c6638cc9a2d6eda4204ada02b63e0e5daddb70fe6ab4e5ac7d4a5cdb88e0b6f526e2c605aa7a09a784a992a90465c8fc8f2895b2b6e75840e2a1f1e19241cf4933442abc96f1ae6549c8a920d25682ced4a2f5f3f52f8614562a0485881203ea0fa428037dfcec2cc9ca5b861adf7853d8daa279d2329547f9d60c66bcd02aff3fa559705c6ee1daefa3582e5a0120151d3f75e4c0b4c5b9835c8a8031abc1a6de4e7975dc4c5903bc27297e739a64b25d4b5fe2eb126d18421dd8c4e301d18349f4ef6b8ff59806985079219176f185713c146ee0323af22ec506a0ac23880ea55c5519b495701e6481ae8c0ecd9ae7ad1a9b219cc61594556a406939a26e4a0cbce6df87d51b7d7df434d9cd6d023785705c7059c9cf84df60686a3e2b57f542b53c8349e382350b31f209b16cac9db278b1222d4b45b9f2ed9444483c90c461561783d9e480f95bf14b243e858572d02c13e9b8124832628a4e74350de0e1d88ea1da1e159358119e21aabab0ada80eecd4e6d850353267ab736caa8243551a580e5536550494a588c2d9e5e22215c148cab5f77bcef7f07ca23c3306d353c6784f094a6e28be1d4a495181d766347348c5052b92dc54c824095e1d89372c27ba1b185acc4d13973a9b0b37c81070ff7132598f40dadf6a80b679de6221a6f34a891bf395220c7d6b19e83a8927bc6f7cc51a28f02676b7b774d645384c4cf1d805000d9907cb1ceb02e843433707a86698f1d0430f8a1089ca82693b9b130d134f0cd778fb8b614ec634d281a66ad44b26e6d7b0f4af362828daa2daa43183a92070b49f18c48be047e53268ec851c7819904f30f2941340e59e67bf191a53c25cbdcdca56867d3c8d3acdebebd2940dcd6588b7273689019c696065c43567d90fc4fcb4c7172a8c50989074238e2ca7a6eb41610e29f0e0e49101cf39798dd89050315760b5e9b3872c1f48162f5bcf224f26eb978a5d23f9c07bc5a331308d1312b7fbc2434ac27cb3da76d1a3121626e7cd5a89fd4aaf6028cecb1aac29ea88c1e6fb00e1166551e3e537e160da039dea8d59bd4ec5847f5898176d28189811d289ae154fd655f1e1a2d3199bf9f2338cdafa8b9117abe48a3b7aa2fb9c2b4411ef334e2904085d30c67cb3d4a91b226d27566fb70761eeb67bcb3e9093a787ec9340d1ff88d0b48d1e8fa773923e623c2329ff4353d342d806a6779c722d933309b7af8f39f12c20ef0ca5c99edc7828777d031d511284eb63db00878a5b44f830a9fdbf377daa85fb369bed4e04de7455755d16a9eab2ab09e9ec8a85e6c214ea72359581b459103c16b77106e29febcf36317ea55ddc419572e62c4eddae1d537764adf8ecddbae0d72748614496e599f5022a71c12185014a461ca2ef0bf1613d4c8a484f9021e5d65a4bb1d2ad925c8e2b79adae5d30b6d810b51030d4dbaf1879e2bfe0e994ee2a4634eb7ba7cf8a917960863b9546ca2d292e8e3dc55afad1d1a8d6e9c644dd2149ba4f5f4ce772b28be474b23cd679312f99075f1175b4449bfed0fcd316bdd2a46ae7175357c422ecdfbbf28eb3fc24355e8251df72c725ae1574a3005c558dcb8d4aef1c84e9bcca9a2459122644e5951254adc68c63401563546ccf0d57e1b7bd17ade4f0a762b1484340055d48a2897b0d7241206d4e36aab645bbefdaa2c749b1076ba8b999a5600b86dad35ca209f33c5fa056058c40b1e3e2ef92a3a5b3286e845161452f45e9958a1282c4b86504fdaa08e5afa2b9126b9ae228995cc9b34f1f5ac5242e6a93f708931627531b3f3b99f63aec31a297d633660cbaaaa9d8c52746b25cb5a59009a96dd05d57811094d010fa78d72bc756735d189de8949bb7aa92902cd10427c03694838048aa6d95194a6488d85c05c0ffa01d0cf415956a6b91d6be4f341a4b81cf4dc31408c96f39e8bc3030f366096b61c0c092a4cc64026793c10a8ad33de854a2e08e0c49ec6d406a8441b69016d257a55dab12c951447e97db10dccdaf15c2ecc5e4b3a3ff058209822de356c91e287ea140fdca172419cbfe1a62bdb7d76c71ed00f6c5d46ef6e9775452752e847c1e74c697803d1c657cbb5a3ef9d61ce357707b91a4367eeca369dad96cba23370c4020a47c1b31837d117080aa1a7f7982d1e4ded0d2fac318dd9964b663c045bba76bfcd3d3a30436e1860193330a3689711ad8b118fd6b05d19162ce233fb307b5f0eb532ac480963e7046e2fdbeb721b0f59b3dfefb6ab4976bc06aa842f05762cd0994aac83438c98b2705ba4a437184a05935c0abb7a87de8dda06afcc7777fbc8f149c2f8a22f0e304a3bd73152c68f57de1af080757be4ec8a537d259879fb62d5145e65baafb0f1456c4880f5ac2812ed04164e09ead69b21f417e2be057ae91f1a1209ccfec2d0605a61db35131c580d9f8aa67f20b63b8531730d649934d65071bc58a1f682f1181c133c18cdffe758a17121e04da159f00603082a54249302930b8398cc693f06a067ef58adaba4334601d0d604e072586fc3313217bcb1bd32cf67fa07f622a135c5b1dc3b77506d10730001c2a7050bb28c38534173ccd310fbff1ecb963937fcca6ec5108cdc0e041ce76b4c0637d363eea7bd51ccf2449359598962bec8a422366aadeca0d9d0c15de42baff5e218de2de89a3ae3ab492f06a00b2b2b5e79fbe0c97ecf1d9ed7d05bd47669c82ccf8640327a82da323f596b48c7a02936c149a6b28140f95fa872d2bb7b847ba3c6eaea96ecc1f1b3afeefed9b67bd03379eaeef62338e032d55b00001b79b276fef90a5422bfe74d41915618d29a9f2e7ebc38c6aa79852a4d1e9e22b5ac1dbeb536b84cd0a7af9174714120bb7dcb4c51fe8b9893e3f50438eaf72043ec4da1e9fea120c15e5674206aad0d0668fe6dd5ff05473f06f4d1b36163bc2b56eed10429179903a7bdb81a5a7a3b5e80d6ed31a8fbce6b1bf778e2a0f9b98536a20990f4bbe9a4cb266de4c668a0f15e375431511f0b017cc151e440b039a77ae09c9c9c07741c78821b14404c06f12ec1016322b4b9b2b89f74297a614954608a3812032f95f179bf7ab0a08bbe1f4a23d885b6e7ca6471a8bd5bd381107a16650b4292c0c5d3a75f8db5f16da411843c2297f5b4318d0479baecf2d46fe9eeb6bfe7a35fd078fcc8c4550fe2e0e6487e9d5337bc5b8ed98c6b70ae596ea7d825e3fc3255bcbf26f7eeab742a66586b9c98c631d1be17e7599ba3d4557be9d1f1082f543632320c25ce93f4d6f0cf45da113488f1695416d9da844a64183bbc92827d88f45e5fb3d8f2b0070114be92278fb1464d6dc6e4112c0aca78c1e644115390415e717ef5826b5b6bed04e031753b98341d31d90113d2c8845309192208d34f61d98d883cd0be120f9e1617a611bf59b7440e0fdd628649469486c6d6dd92ea4f359e058413abe22ddb786632bb0ce6c64b2d8cb6dabf41c90831f820e90d173a000e6613b29b87573be08c1b7106b1e606558a308f3058c26d4604841f424c19b12f28031fe9b9572578e9951f5824ca5f209b13ccc425b537949e288cc556e7e0867009bca2d4fe41099d9b84f2f0fb72acbf934ba43965e48d11a6be396b313cd623802cb3c063062902e78188a88f1a826cf88458ebf2366f14fce55e29f6c65c33957e4b0b1d61df7af63f961ddcb6f3c03b06c428e2cb3bd3d0c07bd9314428edf3b9b00c91a64907dea7fd55028597ad1fc29d689a8f850d77e1471f0467e40bbdde529e9658f799147da6e7cb1d9e2e64263c57f4c1388d05068cea2a02316e776ecbbb02ece56f8b4539a45a0258bc9150e80d7e278b3bcb997ca2d8eff8f3852e2ab35462fe893ca4485dcb6e009d7edeca2ed0dd4655ebf496311f14be011988404e220f584b8cf88a493ea0e86a88c9b4dc636d67ef0f0df6e018d33fea8138c330f2ecf5a76a1a8daad65f654a04d62763071ad23766dac24ce70c46cb5d37f40a773045653b073b191b746f039555b2a91482a7d4bf44074cd051533a84a350921908fd61668f14e54d51838e6f8feee8ca560646c47e5c44fb408d0489aafaafbc06826ff1d37bc2f0d1a15f0cdbf2636b946c43f5d1a7826e756e46dd608f4b8714c223a94d0f305de34809bd3df540bf4eedc08720b97565ab2f4d41ca60cb19a1b21049db4578659f782802e0296995e609cf7fa347bf4118e368e9d85d9eb78acc5ec018b8057b1bc458544f5ecfb1adcd2432bcff35fad4e281cd6b4e5136b8a2178f88ee3a08389e3e07e7eda314dfcd1c1ec0016d332ecdf32eee6bce52efbe5078988441e40c0e148d00071e9f1fcb1f69ec1f185ec88e9158c38e49d67824b73834f820de70522901a2da668b9a51b3797a5c6a957eb78eccead487513f0581061923c2344af9eb78cd0930b898dcb6dfc2e6c22b19852f91678ee43bacda41daa0f72843bc3dfd448ae60185320055ab45511fb200373608ad970579689b34dc521f87ef664abe0242d8a9928c7a1dded451d110819ffb3d0a562bb14fbcfbf81f3df763a27caa87cbc4ab2aa007def6c023983219ef5169fdbd3fab9bcffdf0379b9ee4bb3029a9195454a18095d6049a071cea4fad3436209d59f524bc1cba881673535f00e55034fbf1a98eb27bd9916d0d5ff3d072993437de463c94a05b9f8d791a2df807210fd9e8f0c62a2a9528d4b6196c59052dd53cd89685a601caa5c5718e27741a0b63e2185ae93fb978ecfac0d70b5b27831c66269011f9d0889000810d5fffc30f90c219d0aee31e50c1d71706f7c801f7a1640903762299b6750cc00f44f53dcce7b55bcfae793ad18e758cb2c851a57b6b4d47bb433b935658de90d8ec4a5b4c88a4c9064e825356e64d82281d08045f6982448ba94d9c5be9d48a1a264642e0390da4c220605737f3ea9c71923a5c5eef13ef71baea0d742748267d40e95adc8f1ca65485713e59b2f12597a26e45591636ec7a32099f7c3687cd2844d5ede44f4e0a9809e16c6bc9c74f6fdb960655916491a900e7feb87716b4aa5c7a86723bc3f4f35a9fe8624cd9514b9ec3b63abb354e4068b368383c1febe80ea1c50d069aaabd3b7b935ef569542e14054db77baf3eff82da02af6041167fe3029b6f8a8ff22846d2b472a9c545e317304d65c2632ac66345b3dbfeff0a86ef8049ab4caced584a6010747904a6c09ce37cbef590187f5d941c9e0673ce4251fd58556109dc386cd528ceead6276acf025c148acfd69807cd991cd07f2b9b3dc28b6e6194a7b0bf00b2756d5445f8a9324fe31394334a2c8dbee834a7faca54c5c68e9df4668a97b326f4309ff79e9a646cf6405983cda08108d00c10056084ce11602bd4e45c7511a43a0a363b2af34f44e6b3199028a62bb1f061da705ce4c8ace6e302f2b58f02c7a1e5888b91dd4668af5a1bac79030980b3fd38f933ffbea14a5f48dfd48a28745a763295a0c8eb67dce8acd28791576522609ed80c1073340d4db19c862e0ea2433c484188dede6242cab41ac63d596d25150c9383c0198cf16e8050f7a6bfe4353bb8c6c1d48464cbfa3963aa9ec46686024ae661c03855bda041e14d1aeefa624da4c917d650a64a9c094a3ac18038efc2acb71a0c6f3ebddcaf66b16a64ab121e322e412185de21ec3b2bc94b73167d6912f02fbc5ccd1d6c0692a9ed119b1cb1640887d0e16c85a18daa7cf7ba197fd201ce738aea4ea07bce80d44542092d154c6e6b2a1b972fc81950a62653e4f978b03f48a41814e59955f13c05e523fc50a0a9f9aec5120f99d88f7e1152e9dfda32c0663d5db7602c0035e7500554904d76885c18d35094dd2e56d7fc8d218e8b4a533e87e6a0e5c3c0ccf7044862e263ef03fca0180da65a531052e0e6687c61b050f3c7ac60c3e423e48a836cdbcbf33122421f9ec3522801ee33c3db8ed81632c1589497ae2cc8826346489d0b616a67197cb8bc8e72911b815643939d983aab88263ea71add156f58a1bdc7b2cdae7583535f22fe9c1a32aefdf5c6bd4d13c66843d5e2657b54ab22ae83684f49ddccdb73730b194de1a792018d1110ea1463d6ff31e15f4be92f5dec9887e27df76964e26f4212b476409644ee42ee7b5ca1c816e64d2042963afe941d37d01f38ad32d793b40da07c2194d64f722e10232695cac3618c0ab93499aa70b9b806f89d7cf694a1b0e240c962804c7d2a43ada48c0fc32303a3d5027d465178fd883896f60721ea2aa53b85550ceec32596557ca5586098a68e6cceca82d2cc0025d5d3ae556dcf78bc7771898ff37484eace81fecade578bd3f97479ce9e247d855bd21cada939705258babaca90ec1ab6db44c818352826ba70a1d2a0a791456aa552cd0e0636c35e38251b8cdf27d1b2951b6aa14a500c5ab2def521283bdfa184a25024b2379248ee087ad96617da9cbcecccf46956670981589bfbe912e6d412625f30e51247410a25f50370a6e83a0859a21fbd555b2aed5fce5cb76dcbdafa7272152be07c927a6c20dfab9cad2e3b69cd4856285d2269ca4c58ae719801cda64a6ecf55c9d430de08f2481d72b079294cbc9f360c912be3cbcb41c2d1e18c857c3da10ecc5bc897575e421bfbdf1b26eb68e06f0696f1cc980446a4b48f2f9769e3223619822431abe91d69fa5d9dd4453598c64aaf9f7a1ab89f0dc3fc8dd915f25968dc7a1d9943ae81a48044ac91d65118b25d040e32d8ee3f36ba1a4ccb483a3d2e94d49451deed197e895dfeb57c6806ba309225601b2eb7cb7186ab9536bfe1c6fa21c4824fcf21165a90779681cb9fb9c35a33cc868c4c4dbd138017d2f01a75182440cd96d23b138b2df98358b2da14dda1166c16f6fb5256d6d67f58eb27cbae19782236fdabd2e17a05da4152b66f873345537c9a0853c0a3295c49e7bbb9ef482845a0e540e65322f01e22179823125ec9653d02c0566009466f8721137910ec8a30b65f1276eb88d724a14f7d9f2fdeede0e6ddf5bfc5f105046ba8129139ab6dc6d39f15bb7aecf5dca63e2451d2a9e43a804921f984d95057a98c0428888dce06b97db6a30db2ef2ab2378a46b92c33e0f383840318298182ad131ee607a375254451efcaaf6e62d78753ed608b17f0196ad6cc77c2b0277f6bec4736a61589be6bdd0ecddb30bb5e3a3343c0a51c27035bb4c7c76443960dad1e2bfbaa7b1a3b12455e2afebed921ae7eb98e84f8975a82eb9c71cda61b268757f1d0a97e98e4af67554ab7e1fb7c6e0485e965c0d2cb8557675a71b4dc8ec496486e223ee20404a0918a84f84f02af5e0aa9bc8e5d6b2826432361a455d3295dc94faaa01b224e5321d56db3dcb0a69934911a693426daf0aa0e110224592c6303d6c6ca6105d84a81bae16dad5e9f3f6efdf10501686f5dabea414ab2e6329901971374f785a370a85086433d0658d3a9be1b98ed7027b9354bf59a2fe4f0746023b4d8a5d8604a6c4576f64cc504519b59d8671ac9b0303e19b7f01fb955946b2a4f36ed0e19e6dcb07310a0e0127a93f3fa56f1967239efa2ebb5b29bb97fc38e29827f3ba9a2b43834868377f84a06261271349b441d8b226ac06fc5a34b809f66ee87a61c80a9a2030d2bfd21a8604b3b38e518cd1503b2e67b5581840be9f4d732a907291a069133d436e3e7d318444a4a858dc3306e5ef6737ec00eddf75e2b9ab4bc4b0409011f04a9bcbd78d81ee8a38bbc6447fe6be96cf8b83519e550f7309f0b11351fde20d89835a6e0ee4a8cf335cb9006a7a3409a2e419e66da1ba3b1fd2114429926682eef8f93bd9fc261b57f9f10311b067c6c227d7e4c34e96d57de0693dca3e3b9e9b5d8dc91c9f65c628794f32dd8a525f624fc87672d3690f76b5b67b601bc47e37e1bdb609e7c6f292383c613d0aa06160e4aab9415e01f00500680660cf0370e4e30e8222393938d37733f78a3af139eac64131bd14227218f6c0602018ef0946ca43d8cd940cd0040f53c7c3c1f585b15929ec7437c6d3540bb3abd98fc081d15fb1f3c953fa3f4da0cc422f8bc6a670c28f7da3a89241e9eab9fc6121f674e9a5509c7557239a556d77d1282580b8941fe13d5ee4dc031e19fca686d6b70a372303c6f52be0f34ad422b3baf680c499ab541a0be6efb9e58a3aa32a7411e23a1704ed660603baffdf23911658f3540efc86ef5626c2d96824acb403822da22b98ca25ecc945f338820c4c7a1d7782aeadb0542a6241c65ae4973905b9fa314cdd0e8b61ca9465d7c850c97bdd8b0d6f0b95bb125ccb6a9b18494ed928e394472f122f6840c68d999f5a3f9a91afdeac3912a5a9d0340f9d7b71e32b84fabe9ff061ba48c37b3eed9da06942636a93879dc4707b7a11cc2b01393565498db5b76c4be95dc766074112e321825b8887729d6c5079fd44cb98d641f559fc690659a106a04d6ae2330914023f7b1a964d85254597d94ef3dd8ed34a9a6e0e8c16667845eb676ea1ea1a48348d0c04dac6905048d9e78ff2c6d268a02751fcec5c044d193078d9ad8fa92008d59e94375a0096f7eac56768fac893d33f2e67e229addf7cd623f585f4dfce1fe3453e3c82e338c9519d09fafa7b0b5f6bd2b3a812f1796ae64c1fe3217668fed517806feb024fc236eff7be4e1fb5989709d48692ec9f4b87208dc281e42a259814cf4ac165ebbb0e5fc2502bfb2e7c5b1d1fc1d322f177cc7e35c03c64abe17c3fefe001cdb2d3ce6d3663816c1989e261fd80d7ec228a4f5d58e8500207804432af6ea5cef9b51865ec10a6a1c9450ed3c0695ce3b898f4dda269b045a648f70da7b5d131bea52ba56e9fdbcc1ca08aa597bb8be007a829dca37534e2ea8a6f05ffccc4aa5f085339cce19ca84c83044c31cf9b4f937c0affbbc43435fdf249255a0521f825b6e254518651853bdf7e533a2853fb8aac56446d2020ae53d74a0397e353e385b20b4b5b88f80f7559c978a9fdcd2fcffb20ed9ba2262a165598406448b4c3c9cae9b9dfa998ae39effd010c8a9b938e93d02ea060c9148def39178d6f4b5bb57c8a49d578199d7252d12b49f810b4cded8446d3a7e04feece6df158b85e235bd6701de546eecb0f977cc3235422aa323580772b726fd152e9821689acebe36d674f5eb9b3e3d4e55946b88a656bda7c3e9b00a1ff5136d2b7c58ce718e37774f6676b2aeadbe7435adfddb6ab245e2a354b72202df042f42dbc6062d4b485c8ffe442705bcba0fc4f356dcd9262bec35521e3f8a786c02a2d4b4d2ed080a6d544dc9c11a5ed624d0c5ad429cf7663b208103648be80a8d6cc57d6ac49a47da571e9659db3663251f553502edd8d3febbbc16ca136e9b522236af76480c69e825e0e478a508f8a4678f4a714c3db5ef659f5527dde35264d4672e1bd4980e83e00ba90fc4431029981a900f26fec3a858fa51c3c0845b41c75e898a7d93b807d32b33cd6c43afe8896aed9fcba580f369001c5fd18edb6996c24fc046fcaba41fee550ebe138b9714dfca582df4429a16f1823b610472aa0e3b9f1a6e267eaff4829415bc64a44a8851f4f94229370d91541e2459181438d282cae8bc05c673391da5f65383129373c3e0a740909e01ce06deb1ef60575ca5c56b1e65eb87d2224fbbc2bbb0fcef23db256e41349cbb76cec109319f3730f84979f0cf56640ede63318189f7bee37f86f5c8117d2c6770c2fe3e307a4e297028e61537b9c461a1e69d988999d4609d3d2c112969c4a080b51821f70693c4c2dae1efbba4ca75c2f262a2388a539d935f428c140271fcce8b91e3b65fdb95223a88fbc19f0d8ba132036355b84e8606151e6d6db8283cf660638d27c717c47d91a6e89853d7a64c034b4d9b7a105fa1d2b76dfb5f105b5d5d187f1c0108f535311b63234fc1773441fd2302de80e3d26897f32bfcdf475ef3721d9cb84bdaf69e3036c454f69a5039b5f60dff1509801eee1d79e7e65e902b51d4af929b1a8226a65268b2bed89d3f15d67b8077534c8bae525eef1b0603e7826c4d410d36e2dd1786535a011e39646301d225badc608b664849e4a0b90529d8b3e7d0649a2041a04404ba05eda44c4d65c5794e9a325ce1abe4976e252943b07222cabeed2e89b0d1d7ec61eb81d6742d8f27243d94ac68194678fbbf19596378f1bb20ad709615310c5f832920f18c5baed66005370eb9db5e78ef1a05ae0d63867b41e32785ed06eb70968c71edb502d67382fba37d0c94c65eae00defa5e058c18ffc09723ff8a0d820ed748633ba775cfacb2767343feb2667ab10af87352fc7adcf9cdfd8324b0bac725f378d09a76cad319bc796a7efd6b9d7609f95170c6c872934456f5b9754c5775d9996dd8d29d60705e33c089dd4e23d884ae4691ffc60fe02d10c1b064726434982ca0543926b53fbfe9aec864cb7e20c8b684dd01d01a44a03ca73fe805d30d2a14907796c6ca63bbc8cd8787e73623b39bb8a55e76cd01aec6d8a0df55b5e6defe2463fa2bd11730f1960047e37cf0004c00554310c5064f9f892bac47d9b50083e8c139a712c946b9c824e7e23846c49c8de7b6fb9a594494a19f1079a07bb07474fdcdd1d0c9a46bc1553352a8063c79c73ce59a9271d211db9bb1760ce2f0004340d3aceaf41795cc74ad814a0da00b286ab48a6652525d804a652a593f22065fa3c41327d9e1f3fe5943e27206752e693ce294b51e080a34d1e16326d36497d7f7fda70d97d8c73be9efde9512c3176dfa146257b24a9dab787533008d458fc6e8b7dbb7db7bf0f765d28312d53ed2b51284979ca72b77d7f729954743681aecbfb3909ca639348993d89c562518c75cc84d5e337c0eaf1e7a03d51c885da7f4c1172979dbf13693776fe1ff79e257e5fc595684529bee8a8331defc39c64daecb1efcf1e469ab4c44ff44429c5b9ff1e214da5fbb9a4492e367cd67377a8765c765d35c8dbcb9820d3de868ef7612e1b64f963aefa282885480c37c03e25701013e4895801629c1045051ca0aca006578228e1a143e988172931c032a44b1021ab2033f4e0a40d6c1912e5064868e02109246c82f0c85b3e2582c88087205dae131ac82e5b72a85ac205253d4e20ad1ce1430d3cbcd8b0c49624b8c9a64e58888f2b04bc547aa660ee450f0c9b233eb82adc75d9c2ec8089212fc05eb26f09c5440cfba3504cfcd871f382faedbce88131a39860c18b11e785f95eecf4c0f85e9f0e9070ca39e7dc0383f274424334e610322f47fa3b7404a7ce3153f8b265532a8a31fd31e71212c6249760d397935222314f9b605c4fa33ac2a751116c4a7df81129a31fa369f8a569d8a569b9a56939d434983ef2c595a48cfe126ecad48f1c813dad554a597d31235629b32f234a29936f2b90292823ef5add1f8f9b25ae3c2b47cb7160fbf701ebd9043817249074ec4e4f39f7e57cf4d9c4788e7b36f986b3ed0336d6538ef6a5fd80f51705b578b0a63c6b86b9ad13cc0eb6b06acb11dc5497904a10255ccf14fc13e3841a7042fc00999ed038890109292160f06283130f07555b74106181123e9c785244c0319c608b110f4a5f9408220739708152132f0801922088201d711e904ac0c2416ee0c2911ed4a6a850e5052924c10088244b4ac4900548121b10265454171420c609b01f97c0a6701121f0a065881f3f8069824101498e68a28b951198e0079620b88a820b84e181071e4e6a27c8c107238c40c245cad4122f39d8283318e9f3b17201d0602acb0d9270c10da8108009090ba4e880840492231d0d2006d23305ff1420c609ac2da174d003f703f93418423ebf6ffbf92fd8d6021e98051d40a91f4c074e28b725940e9636edc6aad0b402257def3b4fe697ddb37ec6f759b37411d310f8df17adbe7b099403082432423222a96a3d8e9294786878cca9f5de7b318e8d1b385c56e5538caf4fa38e366ee018777cd67dfd9e3a7add7fbfca9d88aba55689cf7e67b9b7cf81df6922efc127fa62f7fd0e71dffdfd4e13315d3dd4bd6b22fadd13b97dcf1377361299fbeebdffdef3c41c409f9803c83332dab3de5a8dfa12a872eaf3c4d8fd7759bd3a0343de7f19a8ab4f1311e51c40f469bff2aff7ce1a0fce27fec4705915725935a25fbddab8c17d27b463e5ef89f43b31c3a6d4ad12e2df7ec67dab8becdfdfee176d967e900484b6b7d6be7dacf15b31e662ccf1cfb08f39bce99dab7f28b54f0160ddff0325cf7c5781377176f6597eeec39fe17d7eeec3e762f5b9ab87f07b9a88fba1ef39d67f5ff550077e27bd9dd773baae9ed33b2e931e8c734d348f7c66c8fef4bd773dd3508bc9563dc4bd4b135909c47d7deead048a558efba297b62d4dc4445bb2fad5b7dea577ec6aafc41a085c5ff4e3d24cf55bbf7ada8e11ad9516b2402bcfcbcf7d9137a328ebcc7a3dc41181fa2736893e3df510d7e9d735720061f186cb2c4e90bcca41e9a6c45fc83939936a528d37377494374eb178f2e6a7cdfd1c40d67efd21fbf5af26aaf3c7730ee5f1df3beeee4495bced7136f6591113fe22faae97f35b2fdffb2a7db6d29b1e7abd83de0c73c270f5397426ad54bdf83981c6125472f6bd2f8ddc7b3f5c6d36f7fffe8dd2be7713a2814126e037d19b54d9bbdfd6390872dcfff80465fc97fffb1abe01ec879f63af445addab07455adee0cebf89a128a904b71f9ac51c7b1351a09ba5670da608df93fd3d28d26eecef7ffc13b9f7c44e4b7d4347faae1fa2efd2580fcd6fdddc8fde1fbc980c1ddbf4e938c28f9f740ec1b0cff3dfa9fd03ccd8b6d9122c2187fdfa06b03af61ae1470d7f1bfe4234fc7ecc044c81edb18ec16c360090a46ad810a2116bca28b67caf21b125d4125cf6dc34288f0c48dbbf004b48d9fe423db6bb749a73c230c104147b88f2b88e9940816ddf00f5adceb1251854a5acda7eb0a476854179e68ed51f336c45983cb6d8f561d2c7dbf56dfea6ce03f85b19fef67328cf68b36553ae49807f0e1d47d0047ffb06f0b73a9663ffc85febe7fab0ca92f2c18b35e5903a91276f62d3a757f43d278166e75337fc71f8d7f0b7119bef719eee34909c52d29a269f53d2398bbae7de01de63a621eff17f6211f53af9f3cba80343a635a4ce74ff26539c4d35772852c7959852a24ace60ee79d318701a9612cf39ebcf598f3aa3aefb191cf79d2e6292b1263906a54938e85fb9227da49d57e4e5c418f952fd24e79c73d6153c79930388137300c59ae418be7d0ce3184210b081c2c59b14e5994fd3cad3fd182eabe1b2fb3494e49d012c8921e433fbf7696ca9a0d00ea11df4a7fdfa463de8782f0ea5797f569133efef9544ecfb31ee3760dbb66d566d4c9ebca1f9d1dede93a1d9f2ab8325cc3d44c7f9fd0a20f761fd0a9640f72cf242d0903a730e2175e6f6585306b7b4bf7a07d85f79598c09d930fc986b8975acda9f9175ccad8ed519adcf3a07b34f4ea449027e2969c0d744c2400974d77b45b9b92a69545cb1a6c06baa15a56a7be6dea9e7df38fdc66e75eba9411b7e6efbb97d2bc2e47147fab0b6ff0bb7e04847d745444c45dbd3c69810feaa67fcd0aa3d16151ddd63254179ac7dbfd7be674509b2a82c8b95041d9dd565fbd3152adea1e3887d0018688eac2d05d23bfab41d310710b54ed6ceff893154a302b5ce4be544a954eaf439cc510e73ee948a26713f2a5225972d51284dd4e927861095eadeb3a0d00efcdc7b9ed889b126f947ead4cfc0acedb037be5c3d93a44ea54ed489b2a0d22abb4eeab4ab10bbfe8f02aabc57ee1b8a3f95568aa7287d92fdeb7f18851a95657dfcd51c3c79233f943349c3a7e24d19720aa86cf9513a50d952d33ee0e37a159dd7a8173a3ae569b1fd3d49faacb6d3f7977ee452aecf11f3fe9ee44a32dee4ee44f343f969fe3e4df8e113d5d03ceb67c8bcf734cff2275a4547aff9d8d35a56709a5a565a507236659032ff96152732d8de02627b8d8ee9d8acf9a1ef6b50fcf7530f8135138c2960e6697e68e669e6cc5f3d047b194d44f343aea7c9ef0a61dffdd0eabbd54fed4a2ef37ffdf49921c7bcccd3bccd330dc5be733d24f5c774f79d8ecdefdebb9f41f39deebe088526621ab2f99af79a9a9a8fbd8ddef19897f91ab106029b2fea6c3453ec6b9eb663444cd7682197f96f246834cdd7fc0c9aafd1aca799d1794fc3e9a18ea8d3314cf343ddd3b81eeba1ee699e461375ba3535234a97e104c9f43771d6fc47f891fd5dc997fcc8938c4ca397e82e0f45ffec4bdb5dc99ffca9e830481dff9695fa2d2b74a6d3d4ca32d54aa2f5c55db5ed2d2bdb5b5db6b7aa5a5e5a615c3bb6ffacd461a055d4cb0c36edabb5568741faf891eb06cf3e11736a8aa07b230c642461784fe5637b1018b42964fbd114fdc85d893ecd8fb6bfeb68a379064fded0ead4a6f948ac317d8fcba8cb153e91f6a8261a3511511e33cca3f588b1f9dc8cd573daa58bba5a8dce6a8c90949a504fb2e44d5096241035b2e99c45dcafba075d1fbe03b85fcd2f4c439ca6ab07c52297962e0bdf863f047ee8499b3eb8d2447ee432d7e7ef9e69c80b67a83b4d1443dbd0397b1f7e1111d3f79f2ef2bc9a7341024983e0fba2904aaa4d69d3e8d34c3119fedbc7be4ba259c7e6e7cfba88a9e8cb61f719b33ee7ec7dd64baffc2fcd6d1fb7c20992a1d01fe147764fa2ef353772895e73a426ea44a5445af5894b474b2e19b25ec2d1e904cbf3cb486915751a22d7a98dae9278f286866b5c90b997358ce73c3c326d9bda3be82c87ca6a393c21c8952a25d1d17bb63b9efa82c324d3a8528ff7542b349c653b978dbb6c7fdab8bd26b17d3bdafef54bd5ae606cfd52b535a2e317a9e3f868cb155e3c79c3c323d797952704d99fa7853ca76eea44d2b4ed7b9ea71428b9ca67b2c5b63fbfccd5db9f50c25ffdfcfc96ea90330964e7b3de67b11b7ed6e3b334d1eab37ed6d350d39acbaccf0cae0fff0bf3d067ffd3a1265a816068c11b8a39bb3c5ab383f2c8236ac45a6ab42d45da96266d4b95b6b5dbb53fb3d45853ee36adce952816b9be157e7e07ac9ef53fd425320dad74fe965814eafc141c5addcad244b426abaa56aecf9a88e9e66b3fb7b4cd4f934ef7e6fcaf7fe9a256eb5b1a04af2f9a3597bee1324b9353df7a5d0effbdef6ae98d44a8635dacd3454c45d2c31939873fb14e872151a787f2eba9e4323b8fb67d0a0aed583deb718264b7cf59fb23fcc8b4669f1ad99f55f6e717fb2e4e9628c59528fd864d1bcd603ccf16b14d6c13fb84675a2376ca36b154ac94cd62b9d82e3609e5a1d922d68811775b44fa7894c2b8d3be1106ba6f9fc6d9a781f669ac2d616c3aa9bcea4bcdc8a7f674dd90ad6cda0b864381225216a48e43a1339d2677b2b2c5bb20d9b44f861da873d9d4a79cca9df2d0280b9bbe4d8287d029cac213c5f85223e6aca34fa7e814f73fc0301e6c99e6e0b2d83418966ad360381497d126506c553725a6c605f96f8dbb35796b4bb7766b94a785d124a107e5994d746cf299d7ee5dda57e6ddc377194de4efd2b78613822c5ea3f088faf47b74771805e97bbfca23cf2de4b746c7fb384c32cd971c8acf60e1d3fc0cd7c33e7c9aa7b9536cb27ed343f95d9ae8f5e10fbd3e9cf9d74f3dd48ac9b1f9357f41999f2fb3fa1a4d24f32bed44361ffa54d8046aa27be4d767860cfeea5d1f7ba6a19a0f639a08df3084ed3b653efc221b4dc434147b99bf355fa38b646464bee663ba4702d1c6d5cb883510c4be28464c33c5646c3f7fd331efbb9fbae6659ed663448d962e93d1345ce6d2b1f9322f8142ed7a999fe17a195dc43404fbf08b687c46114c3b94ef8966bc2a363f3d14c668a70adff543e1bb5e3ad4614bfb962ce210a7d13dba7f93eedfda35ba8fefafc4e934a5a4cffcfbdaf3c8ef53c1aa7e4c5adad7c35c1d4e9bbe1787864c9f766bfbaef028068a362d874b4aa6ff43639c369aa3e6f31c651e21794ed1b1c59ef36f86645a569a3743f2ec52038304e689ca229c7af0e58bd3dc2169b86cbe0d97dd10c972c7eadf9740dcd5454c43f7b72fc2f72ad1717e4c08e37b5f4ed170598c6271fb6e7b4edb70d9fccd35100da9339588e041123d7842938c24a1e3c4e1b2599b4d52677e7eca4fb98a9c8d3eec08d143c49eef49ed200b0f92a6a453475f94dca967a5bc44792494172b7b7e6ea23c126a070135b02055430a45b24b89148e5862b2e5fe6032659b92ef96504ca43069ea51719010251c95bb2da19828e9b8e4ba251493126c1b779b14e1b4f9c0f33265ad28a59452f073cf3b4e6ff92f9ad0f0a61ba594527aa985e13c564a29a5947e86ed381d29754a69c594d270f57352aff6e6bbb136bcc21cf8551b18ac72dd07001ea1e7c9aa9c61a30d4a4727ae7ad9ca0fe4b86a2357812b9a9c02f3c90170b1e5eb1f624bf90490331bd286d49992a58c3ed6b42f810a604a9e3efbd1ef4edacbefda4637a58f5cb1b2b4f137c33a56be6e022775e84b5df31c7e362ae59161abd671276f69852ec73d5bcaf5a23039fbf9eaaf3066ee6c1b9e43f9e3326a838e756683ca0695a492f9e47be1cac6db903ef2288595fa6d8c65fbf7dc31a131d78d005247b41b476fd1e645fb32102b61ee3ac61430817eb8eef3be29660067f99a8b504fce0be300dddd5ff84829cdb4522d646fb84cfeea9b624cc6fc1cfb35bf29e2ede5af565bc36f8a31a11d6eaff54d9102b67e07ecc7e0dd02ef6ed9bc3d57abe572ea19113325add35b02b052c33967ad355fc99a7867ac2b2cc1ee0ba364ef5ba1a54e1825cfa751097cdde77d22a8e03f10f6ca9f96fa4347f940ec50f73cafddf7b9bbfb5c7d12b4b6d65aab05c120f912afa472f097cdfd138189151198dc8c6de82d10883cbf05922c678cbbbb0a2492d6ea3ccc49296dccddfc695b30d79d2075e4bf18f3adf327c745c8ddadb59616336bad55dc16b7aab5f6f3eca5f96745cff58aa9e162385bd271daa0a029c214e00224d0acb5d65c5f14f50a02481d281b05bab5d491df64a340f7943a52f46ee894beb27929d151226d14e88f6734d251caa7719bbc7bc69be9b6cb8032668d8d4dcc0b478affac48b3f7edb52f30665d60cc6a21ac6f586bed4b29e5cbdab9715ac4bcf066ad7d9adb6a6d753a41f84000a58b4f82d66200b6ed7b399fcca92fada640dd64b68fc13a323b365caaad76bbd8f592b11cb8ad06b66fdc9d23c70101ace055ebb53596e5bcf5d6ed5b1bed02dbda6dab77c9ba63ae6eada7f75e8fc0b6e2b6d5a7bbbbb86f0440192217545938c0ca804131785b711d68ad15e7b6d25e0a767373d75eaf75a5a99d522665e10576adb745af7e4f0556c67b297652e77a607b90e670b01fd82eae76e96edbc54bb66e7749c97671b5dbc54b77db2e5eb275bb4b4aaac55bb5d756bcdd25161fa63c568e96db960372b41fa8d5568bb7abc456bcdda5566b5b0b6ca73e95881004ae3fabbd1be636ee0359396cb95e3232311f95feb2b5562cf1588966ad152669a472863d7d1a0590c3eb9bb57e9667a18be16e85a294355f2ccf582c66c3b2190a9d2c6c5aebac3febbcb5566cbb34c9d8a09873a2909a00301246582167730977b905ab29809cd357aeef5c5a5a5abaf7deeb0fbb3418e31923a327a327fa846bccc0a69cfe56060cc2313e6b7df8c79f9d183e8ba1db7b5086eb9538f79c7352e9be5a69297556bf5aad366edbb66ddbb66db3735f6badb5d65a5ba51c2b51adda25d20914f37251a059f30d9f92ba0452f11e93cb54b19352674ad18793a2b3f9349fc61a6654a8008356af825b81714060888602e40f5bbe90f4e166535a11b67c1d49c2ace519ad41c462b1582b70c7b7d4e919cede59333292a9411423b465498c4f8341f6f576b3dd905f969696e4548d29027da31d783e71793a81cee67c923af62ed59824cc2a4b4baf2dcb8e49c2aca2b9ece859b2e332fa37373738f786d69f46528e1c37ab3c4d282d16b5b20083eab36851710df376ed52283e85e199a54a974d7f5be2b2e95f4d9b4d30703dc9f746d997734b9c53469e40b5569974b1a796a188a50b9919991184680195e796504c58d8928e43b2eba9db2164f292e5e3195c24919126c63d08d1a2e24493541608abc2e66cfb625bdbc2da97349965d324976db1d817866cfb420eb6b530b8b02d0c466c1b751465dbff41daf663d8281f51b67d1a944746fd7061aadbd5a35ca061bbe31c76da1d1ec36033cf3c4716961e47b400c4cea6d9b4c0346ba14b8f146cfa319ff37ec203af38e32f7bad2f5900b3ebbbc0a08a43c0420d5a78470f22c0e2021692f26c05fa191d09b3699ea940a38c5608a27866946978bf78b020f3f001869998706dfa314ec0197f99f60a010ebb7e0ec10f561862d7ff603ae020c67321b582931ddd13aa151702961d275081885d1f671b98c038e32f6f610ca254904a8107bbfecd394401ab6ab51a0f2b847ea143470ca27484d9512968d954884dbfeaf004a594524a5bec1a55bb61d71aa54b9e998b969b153fc8e1891f4f50fd00c8a62f696c784d6d0cfa9275da6793e0f7dc5a6b659032cc3de73248196e01bbd32481eaa0f85784b14a94a7c5c66f97b6ff34cd3ad9a78d9fa747de9e664f60956af611594a6db97944609d2c15ab6481ac1525c78ee3243be84a74c4f8719ab2d59e44c795689794ac121df1e3f0c8da2a491dfc38469936a12855cd2f3b680f07227da03c3d7242b1489fb9f17b12e5d91e67fc94093983399b492efb210693c73e7eaa65f2740f6bc155d8f8a5f4c14662e3977366c4c64f80d8f8a90ae40c87cb70903c29903cf5f1d31b260913698a801f3ff5217be061e327780886511a5a5cf274dd8f79225ff1c348987e4c0864b140cdca1208a66317fc192bd6832b6fa563f7257e8bdf8e74c49fc51d97e1678916bfc56f95f0db25fc57fc89e132fca08873bc649a55daf867d27d72ef4cc2ef4d5be988c3035f94a696b278a9d8da1b303589c740e48dfe0f9deab29d0682a42de356f9573babf6a6734c6e9a63030049a6430635203c5c098243b7430a96502902073fa87074f9410a3a38e2070a5498f4dd35cd5e77f729ca5d2b75da76566a05450a59be4743edc9a64654260fcdbdec1ef43dbaa46a29ccf479546b13e5a1b3c9c4a956abe9482265c9964d2995a1d569d34aa54ef74279becc1a5292582b58826fabe993f5293a52eb3de848add740aa6f267100dc858ed47ae1b6044d985fb573c1f4cb26b7d0915a9ac56e093a95ea5b82de14802559095682f7c820cf2f49f64caacd5a6dcf1db856db4432e89e5ee848e791bc61c2e402cb40ad474874c6fdac7beacdbe19385b3ded5b759959a6154ea42245ca93274d9a24b1498c3cd53074c6cdba99677758fa95be3da23cf72d129d65283e0b775e2098daf5e52d31a4329628a90cf430d4dfeb222995d1a497ed55d3f0acaa47b24705a8e692d24c7a7aeae58826d19ad7a88c3e1d22d3af4bf4eb514da26f7343ce40dd28543cf3f6ae87374ab729372641f245bc2537a5bb6f2b69b7577bb74d63ceb75a7d7a1cd7514fa4812b560e5baef015c6c8ccd0d4c46c50a450f152a4e915f50687458b51e602841c1d1bc28ed38b39020d8003a0ee58116eb8a8a150b7fceb13c2aeb8f2e8b8bbbb7b0e0742e7c26bf1e180372bcd12f387eeeeee9ec285e265131393a19981c16668646a62622f1b178a560a968ad5832f44a0bd15dc0dc6d958dc1676ac327741dddd3d47e680e0424707c8d4f11caf2054eba212d5bb8dd9bd489563adb256d97937deb7e20335b81257ac67651595a8e630450b4525aa2d974dbe91af9437dfc8af981a199a19d80c6c06462343531353137bc56c5c36285a28528429546415cf7a71e538380e37de0d4e87c38263d102b718b75176652eac0b102a08399ea34375429828a24c9d493dc72b08d5bab0f36e637881f0fed537d48511c982eb70e6f46ebc6fc5076a7025ae586f3f505959450e53842d14f603b5e5b291b17d69794a990d2f905f31353132343233b019d80c8c4686a626a626f68ad9b86c50b450a40853a8c82a9ef5e24a831fd62b3efc5501d4b0e2c6bbc1e97058702c5ae016e336caaecc857501420521c77374a84e0833049d10724070a1a3a375c4d7d1d1d1d111c00ab602a41c55c406df2b3f01c3f79560c4e97b7d2f9b9bba03244138b999128412132d4dd8a06506b74a0b0bb8232d357446b424f5a0a5e604105d361be49b2da18070da840061025c25b3d8120a08a39b822c534064714ba82c2bc060728b2da1b22449f901ccd6e3072e4c7e9872e4871ab6177ef8d1d5208f5b4261c1c15582e54a0eb0dc50856508a743ae79a24322db14d11d5db944e4145b42f9e064073e8ce03ae1430fef87ac43031ca0a648452992635b424929112165e445c928b684ba828409ae5cd97145074fc7952558aef8087245077e22aa6a47541047d9664ba8a825b28a2da1a28cec04604ba82d4b705d2a062b0a6090a9fe55fc4d9d5d5d0996199ad8ee79089117dcb0c4892d2500a2e52d6fc0820f2e406a3b38550172774cc4061f5655cbb74545121d49824852c40c5190d46053da23e3834cabf36d28cf44d214802d8522525c5653b26cf93026f233112b32aa482d5751d9f2c122643a4869428407134d43866849591a82c4854c9b4986e8900f3b22dc526522253d119259331c6df9f257d8407eac071a27a39022375dc26cf93430f8917513545bfe8d969915c430e19c5cc093b5cab014856ddb36154a90b4b32554103df44c4f055810e08b2359b428319141870c2fc840457ba234d53a5f3637392f6e2aa546b26c3a82116c4a47d0834d49f0c3a6479a8cf4b029b82b6ec8b4d7deef33fc7afbf3ef532be299e911191764daceb62f187476636fc87bc7be858d42e4faf6472132fdf9c2fbb3d65afb3d443c99bd39a50f9dd2c74ee973a7f4e1b69cd2a79b734a1f3da50fce9620ecea62d7975253fa8c5b4ee9a3624eb0be578b4cd9f33d19d9963f7f53d996514474d8f33d9a164cf67cfa15a9c56e6c58142192b78c2252b5a7b7a2ddd834c7566cf94ea408d0e69bd2e7b7149becf972b565141112d012644478ec09db322ac80af674265e60d0a68a01057b4a1f9b297d5040d920b5ab28f7107b4a9f9a297d629b6a48d49ee00e926202267bbe87438384c5101dafb78c0ad2c4cc117bbe8a39a54f8c4cb86554d2961d6efadeacb2278a155b4609b1818b6f4ed696514933ccb9616050d2933d7f0cc224a78562cba824a33dbf7a9595c6ac6818d854276ff92e213db0886d19154308ac2d7f3ad9730518b4a96280c19e3151428cf67cdff9a68c902221c86c19150469cff700f04de9f36df933e0a6caf465540c5df6fc9c9b2da38268d9d3b3d57a1733ef8b82d65a6b676856341436fd9b6f4a1f39df00e0a60fa6d31186884c530497428e89c23b64153660a50cdb320a49881726c76c19859402c725bf72f0bd90bd226e11416c5319ece1db91bf1d381e3287438725734d3c221927e1808c6048be5b4601110244deb68c027284002046f7626a84331e71167fb0030615478cde892580a468f2a4c2892e2ea0504ae184102dd898400985132d8821a684cac6892620354aa0c49cf841021a25463561c00c81d920e53438cc10249899c10916a605493248aa66c2341112c3f44586865961c9ebcb25588aa1e283309bb673839609b6bcb004099948b9f00e0b7c649cf197912cb57c708217584e8c42bc5383d10a67fc651248e51d9c8407b8d3b3f3a2c7090c419ab640f940851e3524f1c0054a2995810acb5085731338e81174640bc63b426c8cffa3330984f1c330c68f337e1afeb0ce34d4a84c1ae1a3241a947edcc14f3eb620a6250e053e6e90d216bc836978c1eef4ecbce849e2254998ed684865d283091295c94dc3b989da519093263039f81154822f60b8fc08b2a10646aa475053ad5291012dd4e8e8b453c54a93289bb6637f906b68a2898f2dca13dd0a79b5444724b768606d905f30e076e41c6505d9e6e00dc1a46baae1085e227f5b42d50064299ec5a874ac18ab8ad6292a0000001000b315002028140c0844029150108789aeba07148010719e3e705e99c9a34190a2288a6118638c2104100400318610470cdd46026df88771fd61ac7e604c7f18a31f8cbdfe09bbb12cb252aabc2150e1559cbeeabd05b0e33f7d2f4da79f8bc01796e461c5c52a51db29c0da3316f8732f43b7be04fb8d302868e8fa0d9397c1013e2277108e27d0945c3e0fa13f79dec76bd3bc672850015ec5e011016dbfda3725527b8a3ac97ee738a368def429800caded0d271bca861d5780a3860be1ffce07d2d525778274b95ca57b0eb5c0936ff06ad6446d5fd2e71564e2135d6fb2cf36cd000b84882f851c0043debcbf52b1afd3917eb937b43257f11d1684e9ca9544184a71e8e2d880f1014c1e905f4ca1c350aa02436249c88f116319257bbbab7baf949a97fddee402f4700c62ca2e38e7d847c2fb0d336246c1132fe0d4f340d80d8b892c6b92c5b6696e611a88bdfb4f718c0732ce528cf213995957c2a9b9219448ae8100767a821fc7f816e8517e5701ba80b87f60bc132f63aa389b0b0d47395e8cd0245a26a852f5d128270535a71cbd1c271b525d6c794d74a659274a56a8d85cf82d7b38d8a475b33dc95578c42072e8936475d75608839eeb71db6b4d1522a67ed5467682116746286723393815ab5470afc850e2b101bf9a16333a8aed6af3dde058656386afd44fc222c489ed330f1c24c46d868bc978f1b3dd4b6c03e327ca303d2f3b8933b88883bf9f639d97f04eba16eb9e1d982cdca4cb4a32ee11ab1be31eeeb9c6b43a1efd40c5a593726dc85157b91ac0a1ff7318eb25dbca0c0ae82677da3165a0d803e939a0ba9f06cda1c53aefd88e20e6afd03f2b686ff2cf5e83b6eea78bb7acf565b178feb3eba9817c1692397555caeccd3781b54a405a8e0308ad94635a79d14716372b54962d0958fa976833a4e426ef247f0ac97a5420d60919f9cee5ce3b98acae3b660868a0c386dcfc00c5ba3a5b1446dd287f4b0006a29746ae2aa591be2a694427771d31055be7326d22d4f8e20fd1d156b63b2b79aa31eb016834c10e021aa4f66c34c13873c12dc6e4f9f35af1333d1bc9281f41e2b0873740b62554652d90543a9dc98506896157e70a79b2ba39dc446041e5505115b140835d82a39e57b2e3d7ad886985bff3b29f114c80f3fda8d5b00f749111333a97be208aa4c7f63a4646e123daa3a6b071d0a0ca527fc0147d544d496e9da19b375f55b5a3ce060cf49d933b1ce3ed44c5fcfa7639fa18a3b33b2b387411edbc0a133c033d576521442f73c748ae035a420b66457ffaad8345ecaf5c1cb95914fee88e4ae5ef025d92eee970a1b26e8819e9b5ccdbedb0bf418dc3076d964ec19be258a7ce413c64bbd649c16b28bfcb3fcba3a6ed641033c971a461389a28d65386844ee7d0c487e489fad850342b5414a816d12009f3b7bc54d27d98cfac434b8c8d0fd65a624d9e8ed40a651fb5b7bc14de5905e3a48b81014b02f42e4a6aca4299dee96aa057a893ef2d1de7f8d2ac70abed835f1459a484ebfb8a35b41a655957cd367dd2126d5cee32a41a243bdb774f7beca9378f447f7cb952909a12c2df1458c97c001ec6dd174416f2bee6e58ea9c420ae0a78a2a2ff6fb6c6f15ea6c2ea88ed951f52babf2cb490816079a459be08df0f5a8c8933278d6f4c70e0585e4e7c37987248550a5cbb1cb8bba47057a970af520d47df688aaf04ccb284d7bf49ab2554871c5ed4f9073d24051b2925bc1c4e382c289558cf091c60a1107a0cc2070e5e832658d8a8a35e6dbf8294bd0e01253b7cfe990d234e106153999475c5594b82bb9482732905c7520ace52129ca5149c4b398fbba2c96a7a68a725073db10194131363966d5eb30dd9b3d744debdf380f99ae102deda26aef585f22a4b11742d17d7522acead1cdcad3c385b2938b772706fa5e06c25c1d94ac1b995837b5b194fe88ea9c7c903aa9434004842b62438176dd5a98ca08be30bbd2c7b66584b86b0978cb02e19615932c25a3284b5648475d9ccf60af434fb9af5d31e9c15b71c20010e28d0035b029effdb0dc24ac735e08fb3a54edc9ab04e7a4999df33dbfdc705090d04609f89f3187e07c65a6edfe65f4e9d21e1bff02558bb35f5f9035c104ae89d0d529364f2ffc37441950e6f8454eaed53363371d69536781f99e39364e6766bd7f2a95a129c6647701e0a2ad24ae7c4955f4eab7fe378e11fc84b3d9025309a231f25e449ed4ec492de8d0fb0e02886f6d295ff709cf6a330ed8f20691f87d37a0c6f9a0b0e2d848cef9b614cf1af2ce0b4e45d69b1a2651e83a702306c1149091a81f1ea5b4a8539454cca549bbac1c0c8f68d53314fde369a2ce702d95f01f09a614cb95f7c720697b6e7fb19ceff639cbcc7737e17e7fc1d26e7fb784eeff139dd89737e1fcbf97e0c3b19ec879f202122603a83bc8182c1d905b37329a7724dbaf8112cada310da8fa0691d09d17a044aeb6898f64750a3e54d0aa1d6db9ee2f8d41adbc912e58882bd6699c67159d34048c488f8fb847315c65ba6c1c989a4268c81c8d95f21672ec4fbb256844b6e7a5139d6c832f66c2b462f217ab806d6f0e979f6ddcd89dcc1b6452179b140e8859507d85a1e59fac7e760a62d012b2d30056c27e00c6e5ad65ed976cda83f685d6ee73e7a947a314bb9c571d7b002cdc8953247aaec6c136023a0e950e68becb849cea106c555dbc4ce61bfbfec0fb330de5168b0317e16be952e79d2d182215ab6f923b00151fa8bf6410d09e1484151d96da4f6ba6c580e097f1f3e68f2995475370c340fe2240c62b76836c71345abe7d19b1fb28d1ed26cc84abc784944f14248d105a8d29570a50b908a0ba16ad740151784942e81ab5d0829daa58557a044321d1e5cc442301672cf7c49a263bfa6bda2ebb10cc57f950c8797ac91fb2afc90ed40282e802a5c01515d0052b804a27201ace00244e90298c25ef1238aba543870811d0f051d5863edede2311388eec9418ac738d665f9fd2ad06fe9641c3f0e200947cb53b7ec5da82b15dc3a068b3b1a5ae115d599c3010d2de98759a19f21394a2c3106345093074a40282672232e7883c15dfc145bfbf1c56125134c620b91c2161c964ad4010d962103c012ac80d74d27c5738b8443952ec61578ff25619e38c7a8bc25a6ff9e2f1c958b8031bfbef08cc1b0081ef88d405bd316e719a475260153f8f4cbabf5bbf4166ebd5772523901dfb9c3f4e14ee5912ea5420f5c3c0c999647bd2039f9247685c202dec2424546085d3c94b7c22dbf4c712adbe7d498b9a7147b198f84d2b55402ff74489b94d08f6356582541c0b0d042e8f24f2b56a40eb2a025eab9b0829cff9cce40c242db26a1a51114646641454b3c88263cfc4d14a284a3dd7263321622f31e5edca1c0d14005cc6230b405dd8b06f9cef525bfb300012cbe7ce7477eb0dff5e4b51c8ce11a1395e4f03fb4044af91fe1b6871bdaae4c750db0151a5f451a2f035198c2b8ced243a5049b400fc9fe4d28667f126ba19326080936d1a8ed7cd777121a32457cc3d393c1beb2b9b1840e4ed208d6e561639054016974eddab1f0142f366a5887bea6e0c0aa4b125dc113abbc4fce9e11617b58fbf69cf8fac3d8f37df2c58ccf1589b410fab00d29df9948f228bd78e19e7ce205b47e73636d9178a4e949984c541b41d54e2a5a3d77ce648957091832654514027896c351d3575ba2b44dd77d88698625f8b3a5d5b6cdb812ec62a95e59c64dd64afad6bd63859b5ed5d0695863f9a52d559939ce6a942b4946ee9b940a093bb9f61aa683847cfdc859568f04ec6c013edb0c28e8dfccfb52da18deb9f4ba85d79849b607890c337f7dfecdfc60ece4637d6843631740bf6aa98d496ea6d291cc7cff8615743319cc59d3fd67886377dca2322c9dff8bfa8fc8b093bedf6740045a8eca13d2b4d8a0c8faae9262de56b311a2a65790c6e80d9de7a0a3e336cc32ed78c5ec8fa3c23f29f4064dce2180510c29e2113f364cfc322414716b3e80c8fc96f1cd5b9b18b2933bbf0d38c180c63f884b1037298ac107168acffa9dda73a56e39c06d0fd3c4af576592a5fa1eb8555b98b42baa739efd3c0638afadf08e3dd43b429afccd89794503b33089b037b2947122d966b30575b84fb481cf4290155d63540a9db5fd052a56d6c4aad41df0209e940dc949d91b2a81a43e2f6ef2f1614bbd578250ea2a566f1f9081bd6211c438a031c6a5c412ada0b166b013f37f6cb0afad3ea8f75fd8a040180ae1465cf1bb808c41284af3b83f6048bf6b3114d3c332369bd4736053b52500cfecb6b3bb035684409375bd842d2bdd140d1bc4269d384f7659cf522be606c3df5b9eeafb459e47dcdc24eb514771ffcc7aa21b1baf8df5c5489b0824bbaf1542482d11c8f2de3e22121f0121583e4a1643938995c17ded1d09667bbf09407d01345425a2c0b526d69247dc7527e6fa2ab84836af7639762cc8f769312f6b8187573d6e2749019312c3dcdc97f980b3a9a1ead348076d7050a07431a51880a65fcafb9025b64243db061d355b246a299c3cf5804b8e421370bb7f68efae0b0dea1353b695eab51cba0ad13c30a8af46ca68b462ebed2e5b57b6981404c103043b669badeabde968392411b20d431bf7107b7aeed99bb66235b48b02aa9312048826ccc57bd02233ade4b59e8863095e18a949839af0e26a99b5bbe7590dcb6372bd70013942ea73675720eba019e79f34a43d4017d2d75f6bf05fcbbefbad7c6227fe555865afbecafd3825746ead7e57803abfbc05d8d8233e2c6254bcaf3783c8f91e724e5e63c8699cfa46b1353e9552b9a19a8abad68451635c7429e36ae37fc0bab548bf042734db86274afe57b451e4e9fc7253066902f326b7b33bdd4fafea46dd521df14b14cd3926fa5f258eb552f57b61bec2dfca18084bb5ea7d893c50e3e95341358618177d4ef9809a96f101b02263e87146134cff2d6169d61cf623311fd84e39516de29dfab9f7b8c868f4c429d8cd7c920888cde890b1fcb633a959d131b39ef3841feeae88dcbd8b477d9a20be87393646a454ff037ce7355223cdf933ce5892f2241a8ac15f0e56b4f7ec472e03d8f0e104855d01bdc17bc775dc69ecc5dadb7ea4df8af7041d5343212a4010893034bc307864acaebdbb3ade2e34874fcf0e7b1a84c8f2e959ea4a5d012de92f88d978e96e14d5e7be78e1be7f9f5a92c48d7b4b8020105ef82a4e7742c31a250df8532b8943b30bfdfcb6f462e36a3247fc0feeddee1a25b38cdc41fb8cb98fdd8ea6f417d78c5b5112ed2265764b7469c4a260aae450a8da1758153815454d05a52a4785a86d8154c519116a5830551dd13e524557e33452e337e6db8ef322ac561c356e363a9bedc3754084da0b4255ce09512b8880ce6d1fd586af33490ef5a7e02030d030fbf2e93a651b762c8a5a83a98263217cad5b22f2a4cc85a3747c0f131745e210e66ccf01d1566b0d44fc491a84c929dc06242c8ac4fc155328df60c3325d87aaf916b399f26ae985b70445f74f0183952bd8cc87a6cff81fdbc154523421c2bd58b3f0ca991583614e0b308cc2cf983be05d4bfcf21be15327850e79504226847df2f40340ed44cead255616991f39857de1415fefe76bfd0d5d62154517646cffc5a24dafc1b7259e5bb3ff0b7d69189e02f331d8f2afd6bf9d93927a2d06878ee891320c9fd1f31d4e485c1e6253c0c9d797648aa98fb339accf0d921cb723ae9278495bb4227923adb062ce22dd69aea9121af03f7b722b1bdbeda912ceaa1251d14de2d1637d93f9872ec99300009e54422ede668a201bc72315be25317a56f7666bef150bdfba5a660b90970d305f9de40af0497f29b28d4ad3bf308c77cd4028a2730c49386180c1d4fa0b439b02403e2cfa0b4385b08a96dac080bcf4c32707e6e9d776841fa591777dcf0671e09a07b7656c0c7093d3d71123cb4bded2b86050764291cf10d08a2b5ad13c829099b039603ae7861c0df47d1ef36558a65631dd94e405ae14d79e709e216eca51d604fd5823475823739a4bf492b96c0d899da86670bf6b032372662c54fb1c64becf98281b500044c839bfe684a253f741a19d3f34cdf95febdc11d5608485217817c8e6ff49a3bf9f7da122ac797ec66d64b03ef0d1ae298941ddada80c9159b2f75972b28d6f0a78ebc2b23cdcb43ae9431a173ebb54d377d2cf61011d518763f5df8413574ac07b9b5c3e946bcd8f7dc20d841985063bd3497309d744a781367a0238b68cc3cfcd386a20c98aeacf0168066f5352851108310552de8936fe1458c49409d706f378253920c72d05ef48a4f08c1856852520ecb11733a01c90cb045258756ea449335d5184077ae11b3c457737b1c5146c631c8508d2c799d4b12bd011c3d565779dead622aee0abec64aafe786c79b4b8e1a973e455edc12b6201ac101be75655d7b86404a2c523e4b1844073e05d21266caa3fa413b4ad60926be891ad2828612154ca891117355ce130ffc83b475df58b5de0f0b460224301fde38a4b2ca5e2ac8addef0704c12fe0fe55fec272d042e95dac413cd28860dcb860b982d4b67e1372a01b65be33f814a6cb14ac3102ee0c6e7a510c4e059ca04867ed04d448f5e1c602ab127575ee246051295c2c69c502164235db02c8824da54cc2f15623091675d134a62a9a41a7fe7150941aa196892986caf4d1a37719ce3ad69532ec04f7a4337c81f77d1e60315a0b3f446e3fb4ef9941d508828bdd1ad053785c9655e08dc269ffce3eb09913e18b6d8048c66d02ed61611bdcd2a8be4654c6e6e8e5f45fa8e0a15d08ed8712feff7184068224ba5d32f723c2275ff072428eebcfeaf7fe9cd936ea1105bbbbe5d83e0f8f948cdc84a8a2282d6215dcb2720a5113404e024b94571a7cb4cce0485afdfc2ccdca000b36f2fd1ba29fb4407fe44b6365cf4c092f292ab9385a06add3fef9d86985851adff20a6da815f40098a1740b805f54305b9a482b4dc48966bd496e39de51198d0c37be5546ae2914ae33f6cf3c0e18e8b0f82051d4f1c979a259e82c49b3548d39bb1b29da993ec062a7082025a27bcf2150251872bd5ac3db38747b66ca6710d5134249fc795a68554e24aab4f2647782b27eb954b8cf8cef0618840817c09aa3a3010c2854ca9769a922d6eafa7df67fb8b64d26d98d4d2464aece2ffa5b6c9d138df5a1644c2114ccc8173b18c7959db44278a4d49cfbcec06a5e1ca991e83a5cf03874c5e9898574edd507f3aa5c28410186bf8a64749c740f46a1cf4df2486f316179267c1dcdbaa9b8dd3ac0aebde226f4740cd4e29e9b7705218ccc3ff8467db45403de8ce6d2339823c2909a2e849fb50ffa4802c96a5d4ae609961146e89d18641fc62327637c4853073a6745a6f566af9714623e5a699f799affc947ea3dc8558cbd72b678419c6a08e58390df6e1202616c4af12679d6f02cef39001434b6813e1153b6a39bb1005bae9ebcc95eb730b4fc9512255df9fd077bb155ae041662ad6261cea5cf2729b935ef45d209587f6d8eb2991159468293c3c76d494ef18a61e6a7805789b2525286f30b48499df75377bc609fa986b5cbce2a70d7f7e775e5c67fc16cbb480719120abb55e2442ec6f48ad0b31060902ce6e3784b96d1f7404293ee961c9a045be6ae3461c1136a16b5a192c797a7dc51b2ccb6c0a7f0912511099ae29943c6f4af77441c79d30612f680e9e7eb17f4b3b0d605943099015dde5f29765c22a8db5c04a0c4764e055cdbe3afde7fadb380e930677bfd9fffaf28701ed459e4fd3da2c78fb5b87786041167822eb0584952c3d37578d0d34896292b17982899f974cecfe469ba931b28f81962272ff05b2f54e932f7d65026ec3dc5ed4ddea8f8d9844fe91d8d85c0b1c025dda6802a6d345ebe3abd96a3e9b50ad9b3893ef603d9a86f41152b8c870addd544d85f84f5b464fce4050d385d1980371ee9f2e3ef63d46b12484a82b2e03e17d9fd2f2a8abf77904480a21e78141812f8bdf7bce2a6cdbf90251d77b60420809c4ad058d1258ad6fc1ea0083f69a25698320004dde378f8207f34524d1c61d65f61781f6069edb599c9830b45bf15a2d288d1b8c325af0c3d6b4abc8d13e95eeadb3478e1e318cd94b1a6a91629f6986cca2e3e77c6bc1ae722d51c28d23fc2b0635675f01f11c0960e6c3f4b10c85fd11a9d33d22c8e01cea8758319dc49813ab7d3e632fd2f10e168383295e13fe1a62e0da4e0ddbe3d0538488e1f148cf73a42eaf681c64e52c1b53024f93c080682ee26b8d4adf623bfc2568f9d7d5af2d2d4b9d84a9e566647832bd2d0e1cd16c96313680bc410c5c09f82a708ba55b1d5c7b34b9e37f212033faa2c6a8c5ffddb87256927bab1495c8433c234c7ce58a19dd5b44447222dd3e7190779ec217f24121f70ea60f4e4f1f5c6bc71cc2813c9f6c9debe9c94af0cc2dc8f153508f9bac1c9e8365a7902c5e1c72ee542de1354ad52f29b1b0134ac8e45a41e86c1d7e30c3a1f19abfc0f34b8f7ab5be311bcc3b55ffcb0586f1cdf4b3afbcfa7fd2bc56a5a99410380f919f421d7a86aff1bd1bcddcc312bda4e557f3dd4d7e97f710fc28b382848bd33e47007f16b1ac1b1d316ca866c43fdfc70a386b6f1d34f0733d5ce9ccb8eae7da26c7e6f4833c40d059c9c727db0bde4a3b3ba6ddd3e45c0aa75d983ea3b050aef3501bc90651b4fff32708b42e29eb5a70e842ac53e3350947f0d69d709cee0a6bd49c875de2f64ab6512126b9f7c764516394504d189566da312d6a6580d563816faf874f69616596954b7cd392355e33a5d6bdd3f12a423853953de1c68a3533af3ab154447b71de53e5801dc5ab4cbe4ffd9dbe19175f941dd5965fce6a77dfbb61619611bfa81df2a0d1e0272306f644668356c80b0c853493a6d2ffbed526a4395e328fc0b93afa7122a3377356688507600a4ebcd107023a01409747d342d70cb60d0609cacf9de288390bc913bd4cc183bbc02ef310cc12d26043c34e85bce529498fbd26452ada6b096f68900f921eae6c30837cd526b1450b9fce84ee4c1ff43051f742bea1e499be3d231129c87a8f67158f0e1fdbe7be401123cbffef94c7daee296099489f3ed21aabbf9f760b538bdcd48a795fe61924bfa5dd71a34f1d88fdca33b63e4af7ed7b5d9cd6095dded2b7e873133433333f8d8bc3ec13c10e1f361f56d88b52d7a0de6e9681b4d0a873daf85fb6875915215f24c5c3fbe84cd87ab6bea1d0fae7f5318c210eef4ff656d869a1d22185f7078b185465cd2c026add78de0e474c5432a0cbb4409aa0202c9bb03ffa03c7082927267eb5c71a38b71421b2dbf54d0cb956fa65df890e82d52bc4f41f43ad136f76e12ec85274915889d0da1138ee6f7eeecfa4c4295e5161431d9d6f82bae75c2715f23f195ddf79242ba46a0c50e889a913855cd5c9153e90246ce6e2fc1f5114f215261750f24543241899aa948657839e149e16e42d3d435e2137eee17225a2089a72a695b1ab709d4a4593e93cd278297e51940787dfc7fbcd580a6d746ce2eeeac4f8885925eae3fe03a6889dce585991300b1fe63b1e4f7f17db2ad97bd10d545a10cb70a5e22239ad6d4b818dc21c5d830cf7623502b1ef500bcaee606b256931af849c7a6ee4d40a38d0942ee72305c41ee61f01bbf24ac0cfb35ec3655d8d6c2be26bd0b703820c0eab42c11bd31f30c31bfe2f2ddf6d5785c7c0f94e39585f2f5752a4ba03b62229aff68b3ace0019be2bb4188ab8249fdd0be080efe10e4266a4ab5923f2e92ece497e02eca228c922f44d4661f363fca9104985045e3832251a3b21dfd2702b97223a3eae2ff55cb31f02f738d63828736fb23138923ef1675b2c488b8ba5914aa04dde4e1abae93d28f1f66170743170f88238ab11ebc0cc0448868e9a0ab391c2f97cc6d997ef4e83e99312e5e7ac5e43634384df8ac2bbae24b987207c1d4fff8083ed30c4a1adac46d40717a7c4e1704671b719a31bdeb6ea6a98c3f9c8f719d3668da11ae8b3eae88010bd2225cfb6360fd4725bd84d780cb678cd9f8b9cc0b361c1758a546bc3613de34617f500b0f93ddc75b59813b126ab1780558c2c443af116a54a651d8d0c157c10df318ec2485f6544dc1a1ee9ced48214fe293351e79e789274d4471b26b40c80cc69c2e1b2c09c33ed7ca6bc78db5ef391a76a547772c0f3e0ae8d575089e2559e24b17c34bdf08ed35092ac4b483b176c60cd64e7a3a92b8df9e00f0b76f0baeda0519f5e3ab6398d403befbb3d2e0129222d1d456c3ffecfcd54bef23b770e82c5b7639a3813dc2fcd6f800c51e7894cae39e5da87b62d16ad8bdb91873ed3eea0b0465f239906ab6abf7477324051272c611e666e7f669c7a689975010d5f431222c874b7bdc905930b4448cfbc40707cf33c83b912ff1cb4144a778b431c270c19f3dacb193ad71e960c26c20180441b3302fbacaa8a18725d23b28b42cb09506981a2aacd53c6d7d092004c1e12ebbf9ed96906494cd5f19f0a811394f7215ce9f7f1e2a7198c4d8ce3a20c3278f2fdf3a7158c0fef771bf601ec38c26196de6686999e0477a4cab49b87cd4ae1444febee2af3250369f177b22efb02724617315d81ce104f36a6206f3bb9c64e8714b04ea0061fd94c452c8a6c031c8453f93a09d4cc91b9202c12d0f88f04d276ef45a107aa6e8e5a6d880b1778ae69cfa2eca31179c1be84dc8f65171edfe2d5172e6ada67b76ca56f56a5a5a764bbb418455a6c266f151b13a52f38bf72b31102697814b7a3fe18dce388934930dd74861c73c210f25f890b40854b34dbde4b74675375af1c20c42918197144226d9c8e39b1348048cbd5822007b25f1b90b2af1421dd4264abfbe90ebbe58d6b77252446bd609b27a5d2c918ba5742a68320020f496f2b666ad49e384d04eecb6d92c220720d73d1e0fa8ca1a18be4cc221db4b0f38fe3b4c1dd100459a378ad5b23011ad94c19063184ff085e7547800d1c6a74b611e0aae8b3dcf0b2d80da503124f954e5fbc2bc27e4240002e1c42f760711c42094cb0083dfb05286960ede5fe071a72dcf71d64d1138bae6126c2858e7e449c4d13c6090a7e6ffb402747473630823798364788a2575682b660d942bff8e8613bd532a1fb0820b3399059eac423e40b847c18aeb0b858ebd07595a9149cc62a3249b97585b646175c603d906f40f1f424d167574aa6b6c2fffa84b35a4081711d8823bb1388b7c3c815f60d1455ce80d91307e88c8d69a9e1da55ff15a0a31979591ecd8225883f3eadcded9895d6123f899a55529abba553d5325caf0fbdeadb02982d29e219217e13d9dace60f1e65f0fa451866b16df3780c3e5159a2b095935cce1c9d18b25f21aea83638b7beee78a0eebf13a76b546c4dac83dd5276f7759b3089bf3b0ac37b8a9ede129e0e508b9612628a2483f4902e3aef27a4e8f21d586c1be009f0854e0100dbecd704ed9b9c2a447c1bbeedaf4a7c2ba84a4459d39f2228b0a022b688de7709dc1ea5c17f91b25a6d220442348198c5d779208efc0138695143f93589deea370e57790b06824ee954276ca676abfab2d547909de81149321becb213e3cd4e9dd8b4a0903e50c4e8ee1997af48c02789ef8b16e2b55525d6962f95b206a15e03c91f0d8c57aa9cc1426121eee97b69004098988174ba1af6dab07a903a41d22af3944fe55cd72f2028939f162c4546280725e559e91e003e362c3659b97d91dc72508b06146cd930f5d2328daaf3d616544d350350bc82ff574e00b7b667af248702e4a668dce413ca64a5dfd74b4d056f609609c73b5bee156641624316f0c923c0928bd027bb3f0a1b7ba101675b5f1cabaaa462037ef9f4a68a0cc06b70baad6afec034af021061862e0f0d920baffefed9ee60379f34128021e811460a7a5fe5f02ec744bfda93f21c613958c237ea387aebd9f52a52e0459c11cbad2d512ca8e2d58854a96cf22d76600d5775deb5aa550a16d79c6dbc1b25f96c5238869c0a09a444eb7833ec182d94730babf742da93dacbc34f638a38263c279e05bd9c65c0e87bf665ab47b3b3e852544db661fd3b0005e5fcb0869936d1cc7827c7ea8605f93100944c544baef0f14e6db637543208e292034bf34e4929799c44e3c443806c2b0ec1c73b506af764c9129fee94010102458f9300e3f1a157f0a6fc01658e1467fe407917b9d8938c162a653fdd8d6d4d170bf01551f363eeef7be1cde6f7374a2a40deda5bede6b50227a25e4dee27f0c206419b17a2aa26f3ca880185203dc652898707db2d57f5b33400133d1a691cac16d24f73999500072077e5e23cd9f7a20c308685b872b4f579205bb3e65b53b710c6ad7b32d4e587d40aca4bf7b99c4228c003eee6e0695be3a6f728e3f06c3cfcda10034be0573081d14076fe8ce22d5931f45792095effce4080a26753446fc7626e33617a053a8f9746b05d08a628677e7140357c1d67eb9ef25f26674a5c47e320130eb6b091c7f61f527d28cd36aae9f0e6dd559457a15d5bb9de86abaf770795c4b79af052c820e11880a00c44d40c4d29f1a9ac2b8af48ad9705d9f44c7ac03d72983573b327c4ada67f534e8dc7b9d652816294413d6597978823aae03fad16b23275e919817da92a4b5494b52557c41a78e47d1c716c8fae02c8f6b39d382667a563a5cf195f58a6964ca8407ce3e0634d70bb0c1a348d77f3fe178c8cb09c54eade6b3c2e71a9686e85557f8dfcc28706e802b8a32cafb39429ab5f7452aa9d4b3dec02dced931e00f2f453c71a27cfd88d2fda413cc32ddfee79a97fbea48e8de559b6baef3c47ae2ca6eea68ae5b4411e070c24f10c875f86c0fe0cdd36bc5933610de558953ca25365281b74f07a2bf321086b1cd0d45f1e9019be01b7671e34f3d40a820d70f4ad70216dd3cec6c1796262d00199c4ce449f71acfc8c52f950e1760c1ebc2f51e71211b004d31f77c7d76ced806c5b763cd3b7516603cfa7b60639c174cf2811316084a2ea7311bf3113d0c819741e8eed154a54a0d32c1485a1c98ea172147ac59d426c68e87f829327ec231ce1c3a11a12c080d34500f530764f33602f71d4b8ac734a4e1c0365207dfaa17f1a41cc5d7ce81b700f43e2afbe761d2439c99daf624e32770892b07c6503ab9c868b79c8e8e9ddd997c895005591d41a7c28aba2c493494d6866d4ee2041667febc9a4fbb532e4d957a14bc246d34e22ebfc354c6ddbd1e3e61e6114772cec872573c938c661caf49272fc96992a291e46c7a032c8f0d5600a8d8a798a531b466380b1ce7a9c4510d9d0bdfe40c73cac73cdf2cbe9cb65de87d569a7a82b971bef6ef031eb45d129f0018d1480641b4e91ddc5da99e30e420a4a018963600b8ae2f849e7e2247bafcb04a41aad1d23b627d8ffe29848446905f76c9f118757ba9a907f16c26088f3c7a41b5d3f6393ed37cbc43bee52bcb3717592e6ec4f4eb199c8ab1439e3654e49d240909f6ae5700fbc8c81d03e28a8c749e11c8a41e291fdec562549942d8d531c902a4cf1e116efa6c3ad6b86643046cd890c6679a05faf5517a934843a1f43d72c4a23815ae6cff4182a4247452fbd29b0e17da5009b6606086b593b084e60e1e07621c3d828d27a311f9202ebe69eeb06ea90da4d372a216cd60b041d8cd4ed438903c05042192ec809444fcc3d2d09ba411a18f35eee0b4f22fa985bd98f63a9e16d43318fd78c6bb7d88553696316f5a28226186a8b864d14e9e3c9a4198b2cfb11e80c241cc7a15835f13d2ae7e3d3dfd29ac887fe3945e57c3c8c2d46be4cf45bf6c41f6eef18f19963ce5661142bb0d982d0bc57c808bc61ac42f7892cc47a4504df4034277191381f8fd47824218bd97b49de4c44bc221ba900bf1f4b8f90b958b442b3b62c83bcbb77addcf84876f71025d989918d4242e37f5a6d0473fe7fed92bb62b28dc11a741e43f4413665da01d0cb4d9fc51bbe3223eab33dfe9254f9e7efa564ce6aca7eb44a5a9610ce8efbc82871c64982889434be3530dd6d84d5fd3bd37648f61f795c232cbc1ed0338abf4be12e17d878c555f1b3114783b2b0c148b1658dbb7ef2623cd8023f0544827f3e84be4d69621c0acdf87e06d27620dba0c7866a03233e0db6aa5a38860931e03a442246a98f4893bc0ce8c8a93bcea5d0e7994d1b9081dd695ffc4b4b4bbfefa324595cee0215d19480338c2bc02286a92eded390cd4df76b1caca405351525dbfcad4c8866fe7b9b9cc79ef585d74765acf4fa83bdc332adcd1d8db53f011ecf6e3a5863dccba6ec0c6d1032a49e3f65afc35fb867c15efad46397b309c0de591856f86e1f812c3bb9b61bce2ebe7f178ee9f83310e97bc84783b186e49e616447b6518874d2b6008c3109e84d20294572ffc92baf0fea87e17d5c45b1ba8d4bb44e1412c55a5daac2f23c4e60eb88aaf1d4881edbd4bd23dc36bef69b92d3706b8fffb1864b3a0eb01d4f826efbf7783a5974b2c80f3cf2c06667808bdfbd4382fe34d1d215728840c3fe843f19a9a10e4b59bd42ed0e6c86694403734e1ac8209856a2c7124c59f06909d659ee309fee37c1d64a1bc2404325df63fe507d872770a3482164a29c5941202b89d3e4eb15dd0c2e5f886909710903e7278aae89bbe39c988051420417439e6ef9d9b29cf721ac3abce4b005ca2a7cd3f129e9b17de5cc9191fcffb60165d6f1e81c82ac9d69044a3fe5f4ae2d15e0a74c3935f0f289de8d940bb9439b8350534f2d4b06382b2b01a1d118571eac4ceb883874db316141213f157973be466bfa0ad709d00ec9e0210a2bc01754e93434eca855f3b8b5ee661b33e256f837e8815016211423b0cd080b22f2f7a6c170c14ed47d85a58fa5bb365d0cab255abd8e2eae31881e0f0d6a9eae2409da22865e2357b957da8d0274cf59a35b984e821a9a3636b601cec1a43d267837644c5b97eb2b036a9df9036d2698b494c6cef6494fc30006337c30e61d8423fa40eacb01c10eafe872df6bc17fdc66244698c90598930d11d4a42017710865125e3e62fecb0f02929149f31a982ec6f4425656b6cff89a4eb0228505bec77c5a866bf100786dd8b6c0e4e3851771ef59d45b8990fbb5c0e5a80c1e9caf0aa1375aa3bcb648217ec36f01b2612f4fdcb06b74e8ccba2004217d582a997ad2749d367514085cc12e2f2a98fd14c49652a275c355b5db1ed77f6ec848c1270380ed0bd1e547793078841b408063bd1ddf450e32d4413d23bfad47eb1e0e3629603bb570f600ac60bc84a882709be053781f0f918b08f6925e0d3bc4d578c0daf619c3c4d2b19e46afddf088a808b4017aa7be2800fa8a5775334eeaccbf09440e0b7eef82e4b160cfe1add551d0d48b8f1186a11b11791cf0343ee67b79f0ca01514003ad3b1c9187c4a20afc8515978afce26e731fe3b7b646ffc92b987f30bfa19d3d80132b13a409b900e00c554d0891043768b20bab5095b07166010621a9f8ad088fc28e66e152ffadbc7f3b41448bd8d5ee6ddc6bbb1af7e0b38c4d570c23583792f96312d59631fa008a947a6296bdbb43f2364487cda71578aa420ab19ee3681208d5a9448f02d531f5d6548ec65cb08e08fc549d1dafc7badf96b66fe08fa3c08516b7a597fa4c21d35dbc5aa5ebae44293c5dc97c93cd51d4894818728872d3faf07b8b5028765506aed73965857de83703f3733779aac41af9d540b8a5ec399eedb370a3fada9ac21232915f60ec600ebf181177ed9ba0e9bb2f63785b29f894dd13022aa58fe6af2470ffe6d10afd80742936ba8ceb69025481eccbe25810cab301011da2ed095d104e3e8256bd3769c3f33daa04c1eae7e881fd049585223d2a04948aa896cd0fe9198b42d934ba2a33c6a677632a7b76dae85cde84d0904e85cfe293e7bc048e23657b6d10b2cc3e4c7e5e51f20835d081f1a280ae86e220ca9f48d42b297bba456c423981150f1d0bf12732b1968d2191a9cb2b81af03032e3cc50a6c8c055b27e4d8233293aef733a98d0bd7f15ab6bac6cc106f17ef9f8c0166f4392b484b76b590a8192e6870b28d975ca441bff050183219717015a547232825fd7723cef8b33f069955d28a92043ea8b22301a2429c81d91c442a01764b0306b92e43f546439fd4c2979cbdc2e5ac76afb0c75b18be57b321bc1c9c5ef763be258057784ef80f170eff9fdb84b70add0573633eb877b1999a052b4a377bc4a1b061eb6b1d4ae48fd8833c387a4994237c79b9124cf135fa85fad28c4429c201ff75ef2ddc5163c6d2bdde14552f2db0d08a8aecf7049f2ee3fdf04806e4ca6a1dc6f2dcd128f234c1c0194a7437fa640291498dc45a687d3ceb5206a9df39b91f9f1ad85cf29faeae669bcb0fe19623b3043229792f469d31b96798c467e32be76db2b60600f7e737a60c658a24455924db0a845f2aa448dda116bd2dbb3018e7632f3d9024dc51f126225ea9ed079e9bc0a74d51f1709052084d81608695b1bd401c43e7300c8d03e256336534749d5a08e981aab841a88ee39fc35ddbd8bcc8c2a4ca2e594de81dc168bd2a6db2eddd018808e5a37d8463b0b4047a1698e25cb558503ad2cf3e6f344c95ae19fa1d615709164453d7100c39fd3e64058940fe414816fa9a3389968ac16d244a67740bd32219993a2e4c0cd9221f43748ef5d6af3afcea8add791e382bf5834659e67f0d4ae89e3418ea270727b4df5fabc9d85b5c58bdc7fee0801c33caed5949dbdbc469e0b6d90a191bed22fd4a4f8c76eed69becf91ef53c828f58d5db22d69b82403ffd0130d097cd0e93ebc4c89668fc73f3c9d45bcb6968a66e1d400004588bf39e2b2b79b9613d67d89380a3773edc4ed1a5431ba7e4ff994d3b5f186ecbc73834749025a3c084f9a81291bc6ac89671e5ca374b09a8b2e987ba2c92b09bc8e32ccdd6d575df9471bfe18aef822ace9d57c1aa45b8603dc3a6b6a19963e8001faace675109f97ef29fab7e831396586098cd194b32fa56fa0de927a0ca8e2a51c8aa1e80bba3e391a0a516738c18d050cf9cc63e26f4b03ddc8e6165f903a3ba43fec22e3cb71b66e7ebdc75045c663133a3f4f0d85d05bbabd87d68dcbb62897121e6431faf747d2bbc341a98732f9fca8cf4abae1c8ad3863e27828b9daf4003a13d704d07a5c4803d34b48b078530de502f16e50c65a979fa5d1ae38bc2d2f7b07885409afd302aa185ef98202e9fab102a258ec1ebcf8a6db3dea579ca2ab5214e7803af7af64c7bf89332ef8041a2c9f973cdcdda215df3521a5d17e69b64661ea805fe2f12e3be5ca970c1a7cdc30f5b2f063601667c5c8ee343bc0e0ff683675881c6154bd0190ecc61f99c2ff23c5669b4bb2246f7a1372bd6f848b0aec2044180b9a57955ea3cc619f423544c80a32c3e3477e0712d9cc163c3296818ebe447da0280d65a706b327dec589031b396c58c312d9320b753054839ce6a375ba6e3fe31fc79f1e7c31583cc1fb32ad5c28f43de328af321131868861da1307f395d7ade9b9688e79a835f2e58c42525f42e1dd861f222e92189529bb946c575d888d369b1222d8674fe74242461e7ae36db492330e03e08610fd9d4129744842f7c4844ccc8773dbe414decb178fbe43b2bd7190931befddd3cb69d95edc889b2d593f501cc55bd97fe542727c1812461c873adb21b1c706eb9a7b5f2f0a69cb6eec50490d1d368edc8bb6ba2d7586fcca4f5a6a1eb3abb18775b9fe9d4de2e63db7aea2b63b86d95446f77be0b9934d7325d6f88e516bb48a9b8cd4250ea63f8c3af8d315453fdb7eefed59b4f1bde267fd216ca5ad18db6ffe5b0342b20aea568fe55ad098e822933817c52d5917a9876a1e600a4619503988ec47b5ce1abc575d4ebec3762a4a9c1b335cf24831935313a9b02cc1dcb3421e48360ce06c46c666c2f9c1a7d03744e4d24e4dcbba35bb9f9beeba61c5a9412da4e7699144c5f4200e35244cf70a2b1441cb2070b965b031bbc04572fe85524c38a5b723cd43e39a93b87fbdd57561a2f3591be6adefe1e11a4ce0a21517e646d51286ae128f5cb2fab8bbd2572a8a97dfba8fdee241fd889bde1e32a6bc9b887c57b41b0cae7f9e5303e0d1c25e36cd669b5cce6aae4d5001235d1b80dc631a5cdb0649573fb842950b42bc8e113677b94059ed169aa99bd82d86c6719af285d71dad110a4003697b0c876cb36b499228a12ea66abb5d7cdc506dc7eff1476a425c3c806a8dc5ba68532ea8038219747600eb2a816d0ee0f37ad9f4ecf27085e8250c0a5cfdbb8613b71215b1f9c36ca949d3526d398e57b20ded2e77bca9b58fa21897c5b9186378b9a7daf90275bed0028b17427c9e84bddb684091743094add181b7e1d37958b0efd40edbae350a66b23d78dbab1e8bba7b9048eb79999f9d7d03b332fd3811cedfbfe2b4af95a3f4b6e997fc403e65d5d524e403eeb7f422b15c5d2f9c9bbeef1153a076eafdf2800f6157fba54b4bbe568c9e1900cdb128d74e993fe2a65ae5e311426083815233d25b1eb4af21f3d40d16576bc2782ca545306b820dec7d6109c8e73f5867f4be31f38226250d5242de88ad54824299971341efb5790a083d56c04c23bd8738649f1abceaac563ef09e1ea64d780c9cd782abd4f95d2184dd2ff159735f96a27f26c15d089ec4f07f38611eeec5cc922c434dc888e6573942aa221382fd2080ca6e89afe1a2090e00c17dbf69c40673619db38cd20c6bc7c9b6f9d4e96f9f3fd8247fc74ceffb5b180867f667173f5e2859b3a830072fb3430fb06e1800dacb9d98790cb81abe8536ae3a10b3bdbfec1d5f8d7149095a9572f90f2fa7aedae02d9231ae0ee40a649192f190261016f386b8f4c878136bc6d5f6d631f4924c88cc3f1543c3c4ab9bc3f6b646fff4a803b4e2ba04265a12cf6745288fde4441a2c8f740d715efb775039d9ff5b7465708bf1d39a2b24d0bb0cf021ab78ef6f00eae1f635bbef5d4013074d1a8a1af632e7e24db923d8bfdcdcd579e6665b08ace019241c9c06cd6f4dc4466ed6d124a88eb440c923139953a4f9adc3ea4347bafe7c181f7df3d696d81e99ebc873c949c48a922aa4e359ee092b84d82a4b1848fe019c13ac630a79725027fa1f4eeaefedde826e3b6fedd9b7fbe8beed1f7e560fe4486d7c4bb19669e0dca7d31117a1b2a1f1b428c0daaccc84aa16b8f19754a7bac74dc0df9eaf7a9763ef6dea9d86093c627166f2b4fd2939fde81f51342b7b3990b5a1666d58dee542aadae4e5baf12874a4e4499f5475efae89164ab73775cd133560bb9718700a1973bad5704856ec3a1bfe41cd218e90345e68020fa2a561a1c23b7905a00c7dcc1d471da9a3cd360aad7b12f465dcdcef4ee8512873b3e7b68688487737b02c3368be84a6a8e2643dc0503051142f980273a19dad9c9f940a2734887db7d74384a182db8924c125e7fce19e6197d9803b724aed1e99154c7776b6cf95ba571d20fc5e670c2d7ce6c0cc4742ac8e25e6b2fa89664c72aafcfa6a2810b2dddbaa4d83d119cd9fd53f5e5851eccc78eed86dc52aca0072a615077d393d6c89f147758885f62de071f476872f3da343a5bfd5a772e4c29eed726285b00ffdf19a310f23016b6e0c5922fc7c4ceeaf579072946af901cdd41ea3d8df4af3689dae90f0660a224e5e46503dbe8d02c3953bc0456977d20e7769c656be321472090431d4a67cad40f0c2a1a17b68436cb407b5c58ca94c4c35f3de5a9037685be801c6e4b82aea79c12f2d401f94c3b4eb8ae9ec447315307b6aac08909aaf1d243a55117ac1ac8b6da80d5174a317bb272155177709c6460b086f5c9e3d5a5468b410c423aca339d4037481382d8518fd6ed50b7e4f44b11ddd9df716e7d1583a3c1781ba372cad7bce9f94994960af770836452d2b92dc7ebca87cd679f9f3a05f81a62fcce4d908bf01a95d0e0e5e7a397b72c70f4c904a37bae3ebc140e8c0666057a53e88e52e584c800f29e4cff349e3f2c2bd38baefccc788c4dc28b0f8179155d70f1a78bd268d979f120ed660102b42981e9d7832fe5b7039d13c7b7f1d43040135837249c4b7637918eb2e9154d0f6b7bc40483cfb90f99b14a3357d63994de7d8e223ff6386247d8738c6ff4e39ae760285867820016cf5faf778eaad2f6cb81d1f34769824c6b26fee2620bd3e4af145f1080f6d642ae201c7ab6d4cd3222aa48f61210594b0e4a42d759a9abab4f290708416bc62e2d40748e73bf40b48f2af8d15a6a7116c8b50c3b349abc94f76f71d6db5d1e43c912af8cba16f9957ab581b055adec250652454d9c5e710e53110964582958df19adff42b63250b354e80741ca0a85bbf15e648aa130aeebfafdbea76d3c431c7bbaf82bd4d341e76e9ea1fdb32664b7c017c5c14fdbf53d50f10f3a61527ec4f1bbcbfcd674e667bdf386c6f7c29ba436584c186187ca7f6c79ce1fa5fdce207123892fb99972b4c2cbe2020d501b6a1f4de73a542b87a25933d3d2a39d8216e63dbae2dc99311121c198456e8b8b904a4d38d072c73e631ab326e510ceaa3748e7f286380412d9be74978f822b35e5f38255543f0d620f82a1759f97c6a6927e4e43da9dd81417c1337dfbfb0a4b266cbe8c5ea0b43efd63b476d20abf141f5b604f6ef27d353b2cbc8e909a7df2f185bf993392cf785f93cb4d1662214dc7c668f952bdf1a1494be9540e2c767209936fdde5975f58d85606aca68950431a6f7d85f272ee7e508ea35cd2a474348fa0457e199cbc972b2e381d315e3ef7efd8b41a3055f9f46ce0abaf0b3c108b17b1a8fca50b9a131e6fcf5bffc9da113f1d5658d3eee0baa7013ee0be477c7ce9a51d124ba176541127fd8c27121c6dd357b8f0c61cdee1594d22dcae3d789e8c35298edcb615b4232169848cf9ce8c040d5fcd810c9df34cf1bbdfed497353840ef7ce43c128acf9a08b1fbd9bd4679b01eb16ae5bf931f4b9b9a53f37dbb96da1f2411f0dd4f13b4b370f3b650514b15850930de0c206329488c888879809709cbb67d3da86030ecca41a8fc3abeb6fdbc49bd37f8475e49885ad530eeb0c24b2404408696bccb11d05045c7b6ac36a08ae42f6a613c54837bf092fb8cbcbbeae85ec4abbbbddfd40021d5bbe99217bfa9770d2e93ba5e8996298cf6d843274dbf9428fd72feaffa3b20dded6ec92bd8e59f80adc1646cf5146033865f4eb85792d6a91c1b35d3e3e2b1f7ba96ff6c53884359b6236d566420f73a6e6f1f2c6e04c2f5c4916df95ed51d77479859d8809160a5016e43892933c4ba2545819d84a802d0aa220f498b496b600e83a9244663e3b382e9e1d8360240cba91b1dad464a04bfff04b1563f47a891aae3e5db1721358ba22b97d81e1482b3b423f1792b65b83440e472e9a246d48fdeb7b56705cb90f6a20a0cb5eccce4b872ab8452bba6a80ca514201deed30c10e9f4efed9a5bf735e9e6f771ec3ed220cc994d4e494a9347a8661ccc8900acf3f10cd6a1ca26b3304a544b7c7285379fe0d9b0077320a20bea8f21642eafa4fe5559ba62cea54c4a247734209aee7b8b41a6582a237cc30158df83c5486ca82fc881a644b110f0f04a4038b7184e951146efd3d0223fa306e526de35c9284236bf3231bbd229ac512c850ed995f7de7cc8241aff537b846d448e84941d0bd0c796a65a64bf20b4392b279ed74ce9af8bdef26c8194347732e470073f011a27f32e429fbe00ea237c41b85f89b2b839df12b3d575fd45c1501ce68452be791dfd481a40a976f8a05052c108e1e8ed2fc1489b993ca0e4b633304961c3b84d0f4a2223ecf68785d5f789eb8c532a0c52f6df0b651b96e3528c29b469a245bc4c0be280910a562487e63562afe344c5809dc3660b7d5e34767d185cfb16a53b9eb63f9d8229fe73bdcc6a3929161167aaf89e58fc2e5702b45bad99cd7e88a1d755bbac79cffb3f063520c577dc9a1fed3c201ca767d66a196cf31e4fe4fc33ce04d9a32224dff0c3f5afc732c5417e54c796b4126db25a4d5356d68a939d9ff9e37f446e98e638c147bf9c2fa3d859697ecbc29e1ee600355a3ef6dda8e809e8f94fd07717216a4063def1802192e6d36ffeb523ee155cede0932f5c833b5b82064a0eaeb828fb48c3ba6916f6d23e68b441819b8f4dbcd40df0e346cd0076c1b9a2ffdb930454759905babf1f751f6cf9575deb87e01e515754ffae70601ea631573fb0a468c5e93b94b7ff7669e326e65ab25e357f22c2962696d25731cf9ab7c594733d917fff69ebf921656062f936a38f8883061077732936e916c26423473df05cb0684c87ec86b3edb42b27cfd877c4a409f34949bb7b7cba110f8d02b9c16ba2e5e47fd2c88bca29f7a5757510496810f746df0fec32734ea13f33a170cea6254a51159b05381b46a46678d0bd0910d84a2242f72600122c5c24a9f6e7430a95bd20b060b2e62cc287d47bb4e7772808eacc084e069140fc5e83d2a837ee30755830cf2e8ed0f1cdfe212b17cf705df210f046d7e1e1a54b8e9b14e5e4e05266a1d4564916d6641a49c540cc275c98358f2e2e6ab41f41b66abce76188f91c62cbacb1584236d82642c6bc07a85409a8f16b006af7aa8e06534322b618e2ac364c671cc5b779e3292ae6ea33f045706c361b440914e0f57e7c52aa6fafcae330d27a4511e2b4656cc282b003f0e33af05aca2f97374361bc121bc702f2acc530bb8e8e67fd1f50ed595eb273c2606fc46613f930b6047d7250eab41f17a36b30f2c3e8c67af2ae731d4fe623b3215161203a3afb03dcb86111fcf56bb2d659d1be0b567ebd06f45837f2542537ccd6f4cf7c4a67cae921c4a98335b641e8ff9016e2edf18b57298a294ddeeda4f1bfd8802102c90c54374c9f0d264272e6073551a71b9cc00216199bf29785a812558a6e6c269884be63faef05c04fd03949053e361d6c4c2ff40d36c544f2402fe5497585c8edeef4906969d61dd18a350523ca6eb96fa9916fff58449c0e8c53e20d46279d7c01c16f04fc806d89c9a67c14cf7a7d45874daf751c985047dc59b9b1dd9e0e7c1dd8a6bcb9ca2eeb940f86c9b78334911075e88c979c9d813d0f692d73eed0df839271a4954de6b5cebd83c150979fb1adbe15320003be166bdd6a02adb12849bce088bd7da5c775bbf5a66c062a0271fe7de56dace701578542390b393667f45e904d6bb1eaf241aef406557b6cb8bfac573b8efeba817265b67a0dc8d284ecd0693c56dc683380243296fdc041b41949185a1426f2949e674e482098135a2cd508d3c722ee25c8193dde180c1b8a8d5af962a1c7cbb08862de930632f028fd02e3c0d20ddf204779d6d2574dd5d8d15e23760b1a89bf276137b8827b5728b51d72fa40c6ed81f2bc9640b916a5e6a85457d4829a95e1517bae6b856936260d77c4d53309fb0c75439d37aa4dcbef85440b5d16f922043a7d9b8227f8762acbbed62c0d8df79a313571bb5c900458b9adf39ea455aea26fcd704af5242a243e45e17578c0b455f168d53bd3ac47ba33b3adf08f6eee7a69166316596d999a6959cc2d7d00ed080916a6458c943947edc8d839e323d7f2ed49c2473147db8840fa0b806a26c8d00751361edb4579f9e5a0924b978b37276d1e16109d5ce5890758063fc3c557b69f695eb7d432af97383fa5238d8a28294a0dfdc48a52aa01a4a6c483562641ed4d4a0813ec9355ce2e86113afd9a0443548e9599ca9d82bfc44019ae9d0e9118990fc6ac9a3ff0a8a77856c867b00f2f5499cb60c1f79c4f6bb4cdf59a396490baf0838cac36ed4b657de97bc69363bc064a9458512c5a563446ef82100958a3f29243a7a183f3ef74b25e6831ed51d3ccda91d881e17cd6a11076554eb5271d98b0a4a211eb13ce67d3f0a7e82f148c1d68be812021608cecb1b21c1622c74fdf54dd9560cbacd4254f0842beacbccdcaa9ca5d4bf4944d0b0f829da84e438a06096f19d10bc985e4fd79e2f82afaf05b2850d902cedeb8587e94cabef3408eb816bc29330b810ad200d73419fbd34b890393ac34e8a5b8169d34c251ff9b176e09002757b847c061e35ee2bf3644cf3e235c0da0f20068c644c9cf218c76e563ef3da6eab0bc5b37b417f95b6611f877b21072dc9cb51d26c836287195095d659492c0b87a01013b3762ce768764191c7de3ba43e84695833566ee7c1ea4b2854cd9477c3eb0abdcd6f55f8ec6bb844d228672ecdc5f36476e00621a168fc6ffa6296368ec4a46e934d69ba19d8428f162d9cccf977d00baed34921c318e5e4f4f3fceccf879ae0bc026b8520ad12476569e60bdd4806dcc68f47b61e89dccdffc5f6c1c782f729fdaf08565f7618b3a3dc2123eaf8f07d011e14aa6fee3e91e9af56091319fd4a7785d9c1a345011a7545b0491cc8af3588b7fa16c4c4193fd1f14116aab208a395784655d9a522a58f622f9cc2aa89221672660428409593a6215b305d641d9118506f94e516b6b267ea13c6f4dee2fb2186b3c8586a2704e429a35a950097f2856074ac0ea46bf85b9effe982b976203433d74405787d541d8360209c9f205bd7d015f00537fd3413a7b4a4f50b012b4166a2c6491a03c04c4c660d49faa9218abd1ac2c56770c67c8c682f64ab4404f8cc1295f6ee2aff2374e69e3696458dbf99f96aa875c3dbe4955ff55a1db25c2f27f1bfc3a312bb5e286b941c32c22e7558d333dc914da107cdb074d84c1be861dcae173c37d18d668fcc46f70aaf7ad0438859893798f35a5553dd9098aa1b66577583475a75131723e3733d9617366e33659aad3c65990427234a9c542381f7c947168b0e8c455fc470cd37ee3f90caa3ad7b4597f7114780c369d2e3989bb0a8b582cf10af6b572d16df405637c91316c1064b8b35002f42bb8bf9363c3b7ddfc6ee64693b9bdaf24f89fb98a4e8321fd906e0c970771af922bdfc9958065da479d7c221f9bdbd3a66c4e9ce6be55d5ad9567c4eef2e12a6cc22d9162cc6409bbaa559c8e56a2b6ae3bc2165eb243b5a66e235db286e2af9c5ba4cafdece48d46c1b8a0c1612017c4c04ec9481a61a60a4e589918f14317d4d83def2c2903cd8e1491d7f091468970210872bdfa0927467f6970dd84537144363fb5922709cb95de0b613110e9573e4aa5b119d308e36720356c605b8abec27ccd87045f5540208489d95984b2104d7d84d28accc5a74bb0ce62d1a017f7eb77ef2f4efe3b8e51a66325fcef5c1a75baecc618761b11a32ad41c63714f23712258187a607efd529801a7014b97024d57a015f3a8501deb017b929411b419af2f8605196c1d48556db2f66f79ea6f455b7a24551f83660621d50feddab72653c4805c29b6881ba9186df1be52099d59914b3a2511d828afd53bf4b83f1f837574bbcbcbb236396460531c7dbfa80607dd6446e75c560db6838d97f33b8ec92c01c981faf6b109b38ee38e2977a70473ae3b85ff370d0b9bb19f388906725e8ccb4b96c8f8ba7cd42998d96c0173dac32fb5780cfc2cdd602c5d452d80d630afb57a6ede7121eef080a4d234321d2e3def2a8a83b9075b9802ed6392c443b923829517c8054d342744fff3532726f4ed349a111059021e081bb01079f4e74695592b7047b23035d6c86da5f093c7a8e615b1bfff089ee4b3b68ca1d39a6ee540f87f0f443dc4cd372dded616360bd6c4a091713a4fd1f3acda800f0475abe2c034a1191bdfee56e7639cf2e19edf252473fdf86bc5a597de0b081c125eeeaa11def2839452d8112811e3db2dd33154a6ad1f966e9d3755aa9d8d1cf2d225526f4ff708cb2f88b45747c618be60780c51980236db887f4ebe6af21f3672ef98f6c817c7a2688284ec79e76032212806e0f4f40721800edafee1bc63687ece56be3e4b9d647380124bc2d56c2b67580f96dd14a951900f44f019e3aaaf5fb58f16417945b316bfb5441ffb11b3c9493607f17d3561ea5ca665a1f471b5a6e23bea17e5e9f89e19d3b4a74fcffaf3d89f6b091e36a0b09998706601215009ee89da70dd486f785fd07d37340a4c997d072f0196793fe4bf85942ace5dbb5f06a47b7de18bd6a9304d1aeaa092bd0db722558d678f7141b75126a70ccc68afc0640f4fb922ffa59c5f761c42312552f334500b3855740a91e420fe22e22fa4ebbe9813e8a45be37246e099e62891de04f9286d274722c993ae899bb85fd2ba593dc0dba65420b3be21071f53742b9bd26126374b437511f655e05276bc03ddf07f0f5e67ba3bbce5b61bebaf22baa65f9d298386f3ad40b0093851456b8dc303ae9e17443056a7527389cb8bbfbf4e973cee93fe79cee5fddeb0da89e6e9072c3d10d3b54a0dc8082932e35a324376cb9e1614ea8dc70c3c98f4f315245796e48e2c9941d484d6e207292d4748070a890ba0391c60fcd1051751260c01890c3188421b86108108481087e84608720621002b61fbe60187baa276eaaf52b54c817e2a07e918a50188b8e321844858da70c7a83d9254052ba14a3e29379b03c93cee96f607c57e5eaf421c4a6af1322f54400e221f64402d7458f2d61bd5a1b31f01c6ae0de424369a5d69d6ab45dfa5f6655a7a6237ebf524731ac1792d5a5a9208c89bef1fd7a2aa223a81a5fa0a9e8630d0654c8854055700452911b9fabf90aa56216e7c156a8b071076b2a0663c3181833f9c6f8e5a9d8a353e157a3627bb18d91d21250bf928a138963e7eb46851ee760820a6b2f8eae98ecc7417c4985a0c278a98206ca15be948a60fceee257a508b31d5e007f24cdc50ec0d7a9008c35182b386c3007153696c19c8ac0d85fe22e1ddcba03daceea9656204a6a940e3d7545ea3345046f03ff95efa2b7a6a78d9cd363897591e115ea33a7a83e9e407958e0b870fc97dea2146d5b31aa5bc60c47faad9c23eedb924763dd01459cbc8a236942e19784ecbdf7967b4b29539201d608530b80086dbb0946d8b690e9ff6bdb51af3e9da255f56d1deda724c5a56f2b28a349d99d6dd1bead7db932c6d390e91f84c6c0fbfa129037baaf3f019984bfce7eb44a14d85ff1589c9266df13a9689f02d1fe4cb4df89f933edc6efbdee616fffdb380b000a7038c66f25a65dd77500fb0e17e9b7dfd47f394f7ceb5bdc2a268a9e0800d1df38ad3e05a2c7716bedb72776feac1414264ce8149d6292ebd3294fc2af34fbfaf48a27b9b0a257b48a6ea161289a5c1fd3295a855275b8c3f11804ec2f42b3fd21b06c718b7f8b8b74ee4bf40dd5514c46984af4691e5591cb034f6a3c7a5fd7243e91cef83d9cd9dc913ebd11451a85450ad581552a9df2a3fa32945cee8d1e4647a57aaa725f6b9dd5a87e66739bfaef37d128a7d527d2994239adcaaeb9be11ef67a25ff951adca2c7e743b2d55bf133bea49ac63e7093c3c82d3ea7f8c7ff1453f82114f924931be7e43d95082c7e12fd22a94ca6995863d427d7b670c4ca79c56dfe861fc48a790d81fc1935cd4b003d9dbdfc21eb21fc1935c7c7d23fd246ff490bdedbe48e70e8f43a2cc138832c3108d8a5e88d2692e6422751afd162296d94b208e771347caa27e4c1c41227194d55f218ac00d895724d2198639705a7d105e78b435ac607d1e536986a51fd1afd2a577bdeb7b4d3ecd8ae114fcf06d214ca5160f7e2dc49647f32510e7b310e9d0755afdfb444fb9dfb29c4bf45836f4c9a167eecb58b77193793d27f6bfc41ddcb78ef935e420d4416793f1a419fef68e64fbd86bdb9ec8ed7d75218766f3b6b12823a9994fe5edab9ddf526d25528c8888e8efcf0ec227c2453ac7f0c87dd3c771f48f54b6d6dbc4b45610c1361cfb421194348f0267d733c82c170de9cc7d119afb87c072ff1676107ee3229db99794d3362098fbedbfaed3e18eb470fbda54ea2b3fdafe65b2bb3519a76d2f83caad39edebd3d19fae905162f1db7b14134f82e2553c298e2771b236d7d16c3fda98da92b72a0fa80cc525499b4fc74ab6a18a53854371c969369c36bdece68ef48ba03079fb4683f5658b49defefbc0c9417914ae396d620ac8ae74baeca7b9f148c2f42779e38a27cd0f01847d30d8b7d8b8e6b4c61458218e6d416e68e82596973c996edbb68570652b7e7b1c338685c5c344113a8ffe94b77f89443a8758034e0b6103b7f1667981a9c40253a013a907f33d0be613dcf9fe935f83dff85906f12499bf1f6183ef7df86af9fbecf77250fe0871e0945be4b0bcc4c9f24f96585db4f2d7a1a54ea3d469cd8d36723f0d992cb631dbabacf669b6587effbde48662585dbeaf1978d2787178d26be049fedff390b2a944e5513f68639fd1e7b2fcbdcbfeb66fbd1fe51b1ef2c6ccdf73e049f3bfafa332eebfefe5ecc34dfd84c5054d7e91251617aa5c65ffd6c1e7be324cb8d469dd2d8f1c4b0d5da6a47089f34d302b9665943543b2832e252483faca24cba8ec4fff78d654eab83aaeacacacac8e9007c56342c783b2d311f29e8c6476e8c8b29cd6548ba2b9b4e39a47feded3bdb22ad5714d25aae547fefd4e3b2ea71d97d3aee3a27d28213a88fed9f179f6d98a8979884bb1f4fef926cfab3cbe8c2577fc3cb58ec8dc91765cd993bc4dd6d240de7079c3e50daf72256f78d496ac41f2864fc91b2e6f7896dae02857324ef399d36853b5bb943573daec0600240d51d6f0972fa3854be733f8f74a06a94a5529a32a15ab5255ca8f7ce64a5266f0d8bf863bbe842a59d62981491e3b2e245ae156678c87bcb9873b52293a5d767d51115509cafe5528cd93bdca8b6cc4ad50f268f3b4711af5a48dce3a736cb416679b7087cd14cf2ba7f9cb70e1da3eb67a9ef75be4395e79345fd2a6eca69f69d350d056e59b2f3b7b63e02fbf31f0fc0014fa740d3963c07865b9b91316217c3c40286f3f1639e3eededdeddeee2e80d7e854d5b5d121e3ccc9d28d20f9068b8b54b61fea904df8c9dbd350873c024dde9e082b7aa838e28cccb345de915865a49c39f2ce08f44cb18098193d0520a2004bb8d7b8810e3148395835b0684de4ed319f1834d09878a1046bc8851822601a227178429289788f58763c76800eb7711bb771b5d718c29aa11692f4e48d863bbe3f3a6c1079fb4f6e9bf71a3d90c515a1638bb2a34ed131c9e40d4ade1460ebe4ad863be6156b396b2d6739ce5a8ee3acb59c6dacd7981530c728007695b7b7e10637f2f64db353a9ca23ff3086a4216bc81a8e474eca1ad6bf36cdb2c5487aae63113e735f75589087c8ae2af2f6be63c49913a1c896ed13f12108a0519f730538650fc83211d9068e2928db3c25c933a62bb16aa093e9d7d9c83dfdfa2fb29fcb39c517fbcbdcd986753e2916f590f9ab60a55ca593fa9c734ab1a93f4ed1f673dbbebbbf226b41a425d996dba8addbb66dfef43ff0a71e3642dfbfc3462693af63b82f2d74b0b4c041a3c58b16aa2c57626991ca02857f1ba02c7d32105859e8c8e142e8fb24d6f149b720864ffa273bce9baa2b504f5a79d0909982324850569c2d5551507db4aed05499e21a4427ab86f4c7bf3b64ca8ab3a58a42f5d1ba4233ab700da29365b5c59978d4569f36574dd5550871d19191c2f59aa44193217f763339b67124c3c995f555b4010930299f94948a5621c465874e18ab293599fcd97ad3e68a53f913a13c76c88469282d2528888cc363f6c948e1caae673ea9544709549675a0aa2ac8b24e12429e345dcc9e4620d71583b29003a70e6bb28a6334261d4b2ac3277df4a143a84deeba6957599336af647f6448b979f2d85aaea524c5059de636bbc85d736bd5ae161dda1ad282b29434c6f6933be89e6a7912f7be256fc81eddd3b7df51b14867cb613b0595f96444ddae95ba981aa44e232448be8cbe912715d19f4eb7a66236e276c834dbb387f3377144ca5da7fee4fe427794511953ee48f352924b9ba9df1b3f1e9768f2c690ceb4269b19c9e742a66e3aabb7dce8d34cf148820addc9367325561734b9fbcf1fccfee8a2db721eb6edb211b77b1b32711dd740e40dcf96035269490a4abe62225b6bed8f8d6473b1450d38cd620ad021d069f6abfda5d18ed6b970c7e7aefb4ffa10e8342edb0fe249f3392ce0db8fb9b54ff31ab646d9e259b648d97e536d855c64f983145502ed8981caa097e1caab26dae4ab27b676d05c5ef808f7dd4fb148e2a21017c9cff1e10fecd33f427191c435a779bf348fbc59965bf43df771103832c75af6b8f770935c427e7d08484a6553371e5010bae5536c44d237d2fdf29d72f4055afe3cfc6b4b548a4351c8b2960e4296822c312164c972561422daef82885f57abb8d695ba1322660e37d12e5289d428ac2d52b92964969bc0c9bda5dfd5b82ef75fdf7650a473632a649637dc44bf6230856f04837857d95fba5883e20af1ae32c54d1337392e01479c29f3f92e93320d7c8238684cff7e8a63adbd5726fba7d16ab53ccaf2d2129305490770438386a4617f8a967adf874799ccc3b56239b936639829a64e03714f9047e6b9000b0bc329c80d8292467f96b78453c53ca95f0cbaddf24d9825ea5435c22c4d3a5999e60fe6a38dec7decfbeeeeeed9753b7f9fd8d9136d6419ea186532bfe085cae1d6e2f15e99ec7fd63a2b874c3488a4e15f834b06d5052544147a610d1e297eb404c240171af04891f3c211503ff4fc98f2834515c70d181fac004d61ed6025490b3b5c3d01998008ae8620f3860956abe9b1c40b5661a43841288c0b7aae7ed872441a3e7fc84c6d519102072a5f8014e0ce0d50a41043460f15253e1813f582941b1bacfcf131850d9f32649ee03a639320420a90150b770a5155a9a3449ead14903ccdb17e2aa045cc9c255c02714539616b4a1b1fa65a18a26a2b4a146eea84e91a225015201c9441a3c218282c2a6c4122b6040e9f3f48e24481460615424ff0305941891b7b44075896606305c9172778f081844b8e9f207a78810918ac1147599e206307871562c06206aa091efce031e289e610a5844a203d73921402b2ad3026cb843550e030031e1dfa386df141abcc1d364a06d10162066acf1327a04972854c1148fa30890283952692fc6843c49c21968062cd98273e4f9e7670c3041aae27c6b0405327cf160e53434cd9b334f524a84ba11d8c00e4a30f57151c2e3f044103212d164b04f1441515345f7cf8138750117c00ede08225424e0b5b2c389146cb0c0e40aa2042cc953957dc2cd1a3c78b172d37c839c3268b1240560ca2028a9a2f4c2ca96104554870c0c18b0c54dcf9b18547892a4e7208040550e3304412384ac8704159d90324a985386a9edce0820e29e471ead2044e116b9c9c60e5e40509da03c4923d46b0c1c3831049aab2689132e5cd943d2ce0d08298292e280cc1a70c0c4294f1b2041c1a2f6cb9ba93c3132af2dc9162cc15ac24453821052109a2ace2e83102100d4bec3c290305104c5460a2901c0000daa1891333fc7043109d46831eb0a8a92384151c02ddb983e50a950d43684d9955fec8c044ea0f15415c7400b227062dae30625ef0418819a63081a1d00d83d450d143cd0b280cd252c84f1b3b630476ea58c10288871f7c10926242171a2850a460a18e9d2d40d080a7093d44c00122076581e2091f700044678d07d1c2891b2c535c05d1054e92209e88400452505ca0e80e40b2a2283923454f145498002167073e7ff698e943e7481a2e4ad0407581d367db0148122552cee880670a2a0a081f777e70c1872b774a68f3ea61047874a022850f95fd4109354738b9926213c6078d1c4a7052051a289e18e383d6962e1eace080e40a9f0ba4a801cc1a2d506cfa64bdb1128513564a602551c6043349f460f1c305500e76ce076f0a29496347092b5e7e5459196444971050484cc18240932d490ca2420c201b7e6c4d00e9401329516c4913050629e4dcb062068e1070e068b1b1e1470f422c7992650910535871a4e628c1e70d1933492401e41aa1254f9a3a4d34f1c56d0f42b6207142541329292cf848428a4025cc0067061efcf49a21366ce2f82113c70c0cb3850ee84c61a7cb0c43ac00050f439e50014686334f30a9c2879e4048fc00c40a55bcf0050eb843154dc4f8c12225a7490f63da6831c20a0a9437960a8a0f431471821e263a2b3f74c69099a1ca9e1d02253922051d6840814b9516664013c889a0428508593745c41046100c6b4e789203a4cc1155d21402d3650a2f3ac0d8a2849d412c8871610bd5f2044316159b364ab46101072758b85089e903050a1c252be4d0458d09960d0382e81267890e697af0f2c3c91a3f82d0c08082191c68144f688591218624f2e09903e40a134ff6fc8064892d390052c3870b9541223882eac70b80a892dc50c54bd50a388cf98245cbcf191124e902f269217184931bb268b1e10a8322e6b079f264cb8d161f51459450e8496b054153d644000917b8d2ccd9b3e7ce0f0a5600c24e9c1e8478b262d578e0843e80888052e64f1d1f0e3813a689541518ca98a12b845a604c382206139ad840039a142143a127c0b059429dd0c04cd50c3518f18402950f1ac270818288264db26cb06924d7115dfc747121cf1428a7335855ccc961071bf6e84193c60f9e126a28a14fa142406aa012870a0d48f830a162cf18604d0d3524c9d2c29c273f68dc9902a6cd99398578307381416500b500c49a1bc0f8c8f3c3698998285200c2cf923c56fa4a0bd4551b3c264c8d800a2e13cecc41a38296327d6c19273356528298d2d503143976ae5069814fd50e56b42c19a63c764ae8634505191f477698e233821f96ecd065c7d8c822c41077a874f1f285859f1952981233a544d4f6c30f1ab034f1439504cf9d1b762cc18a228b55550a537cbc680250559799276e5010e3264e1829fc7839b325cc8fd89a2853534831a1cd9b30370a2d6ed858d990c64e04deb890248e134468e962c140c0c99715906811420a8f949baa4150f89026ca1e6b8304a8f0738508395fe6c0e045f8d032c5091263fc6c71a933bb80fc58820c0a35d4d9f342962f5ac63cf1b0c21a255b757e64c00426a25062c21159aab0da9202a50741583f3889f263e4a2439620608072c58b17a727aa29e4acd9720288259c6698e2ce141b84887a628dedd26304666488028d151425ace429b4f507082583f070696a33e50ed6184037f4f9c1c19695142290507206cd96a4a8ad13c6f499a2cb071f47523cb53015a609992c5b039e4154628c10c286207c98539c528062a50d9f13b0743863b5404049951e3e4d9e6c699343befc81e404892f3bac7026cd106eb35cd0b5060c1059c2d0b0c5876f71fa656ea8e8e2421362a202b1a1c2b7640f4de40153f5b4c3133d406c95d3131ffa305104d70b56ae36212678226a08404af8e86141f609428d9c3b5151a0a8e227853c2a10c1248a931228979312aa3c5370bdb0e5294b833a348c9181d09219465084152e7bcaa8d1e3678dc592a1a8e20915549c0064056ad0d31077e444c9c14f12a9278824415cd97235680559e8cc9592262290418a13c69ce9210c943242acc9d1364b382de47e695db6992d4c4b0355972b3d4be450c64a8ed62346047ce658b9d2849a6944e62587257290f0cac1a36ae0ca418200a4a8012207fd291a9173729020dde4a0984b51bf53eea9a40527545630d22cfc119483045a0c5b5410270a513f60d1ca61444ea1872b07095486b6d24a3c2f381194638a9f9c1c2e7626c19ddafde93671471b18dc515e81c083029e64b16c1894fd35a875d26d9a77ddd0f8ca0e9347fe9211e5f270da0b870634d4d0266fef7fc5026c5d19321de970631b0f77ac65cfc09324560d7464db8ff2a7e859bed12c6ff8869e7146df7fdbb60d8f14bf0ccd7d7943047995e50d118ad85c9fc8963d6f737ee7ad070272c543645757a698058ee3c4a128977baf2092ecd8f5d497b9fef6453a6f75f44cc2551374b20c37702aca76c2a359b9cebe9948c29dfd27c3c91d25d74ce4e23979df851c3865af65683f99e7dccf822378df775354dcd1df510ec7a453553e501f09a5547e549f92c9f5311e65b25ba95ccfe3b4fa6ee7b79fafa4d4f2087feda0f0e34e1cc188f81f905f7f842ad5df22b928e47a6b1545d840cc1ea6390d3f8bffa4d8795847dbc7df33a7e1ff2608b683f85bf4c4a7118c38adbe8c17eef7db8f6e0763698740a77ddb7f2cecd0751a935cc5173196d9b3103b53265276acbd5726fba7d166f893bd95bd7d1add6637d9b66d2eeb2b7baf0cc41fad86fb39ec4f32dc51728d30dfdad943de4dd1dd4703413c09c46367f061433f247ab50cfef7df276ac089db19fc51f631c2e583f0afb4b6f3bc877df88effbef07bf89d66556d632b50070ed700909f4aafd7ebf52f4cc2fd2338702d8479e4ff7d3e02f6e18ff20a73e98761398f42ecc13cca4a64317d2f311443a85eaba059f1386bfc1c062bfedc2ab99abdf0d8f9d5c25a5ad18f4bd9b2b0af812bb5d1e3f0a4ee6dec8b6077e85b88721ead780944398f88a4f0eb6bbda084dbf9f5e32c87cf0213e18b61586a8976d8793f7a955b7baf4cf6e1b49d9db848d2272d2dadd748a92c265ea7c9385bad7529d7d720897b1cf5ca936ad3ab2688973e3c2ed53c3cd6bc8a96477975559543fe17725829864b9d8b4b4acab9a4bca5de69be62ccdd7c8bac430f581265855744b9dc5bf1e98e5e953917bfb5f8f1661758666d0b2ce53b97ffc98e8d301609661260e94731874571a5f4bdf7d25dbae7d5b297818747ae467b5991ad73d7f2494fda6a8e7ecb7d34547afd18cb9fd1fd7136f42392d28a1f6de42f83fc7d78e47e5b213a4c0c45ee277d4e9419734e9e3b0edfacfed1f92e0e2161ecdcb27da0dddba1cd8238e610eddafde779d498db3df7f641df823df8a1383ae5f05e5cc443e6fb3b64beef943f11f69e385e207b7f45ef61a253f6442235bfb01fd13540eab45aa1b8dd8f1e15259365f728a7f9138d9a822697e8110d713b1c935a49a5ba15e6a43fbdef6fafe3da58ea3fe706b247d77425a7edaebd886f0ce28426d7a72edb6cb24da6956ea2cc467c3fe7f46f9a0990591baddfd71f96326b8eedec7fb3d60fc4d26916c7ecec0c31b7fe273ff949a9f5a2dfe1f75f7b0ffb1ddec3de29879b08cb135ba06b50ee3fb17b4fb493291e83f820e9b457531aac6c22e26067ad37fbf60dbfe58c85db5b0fbb5d5bfbb57d4917c716148a94d26fa23fa78ccaeedefb3bd554ea3744efed5155a34779149158bbda55eea859e0b9dedba9147b222ef644ff418cc3bdf55ab9ca1c3888e45c0ff7687b0e5dd92eee83f2f6e10c3177fbe9715bd8893f6788b9549499464119ee702a7663101f94e7dbfadf7b6253fdefbf2a8ede831e2edaa07bef7774ef61e9343cd62fd2d97b6efbba754c15834d937cd6939a5e6423ee18c4a92ad39fdc48390e8f41a49d3c5f8a4df367c576b287dc4fa810f2749a7b11df900b967bdb74d28dce6d36413729f1b83d7761d67250b54d4a9aacb9180cdcbb6bbcf5a48ddbbecae72cd78944680fa23fdaaf23ed05d05aab04fb096bb9e7383ccaaf1fa3d25c868fb31cf7dbe3704fb498c3544a79fd846f48caedb73df108e6cddd3d46c473f7ac3f7deb2d0898299eee4bc85a291ec14c93c0918a31799d366b4c56514e2b8edbd7ca49aea5c441c05cf13c615239a94884663ae79cd8029a01e683dda66ea574972e5dba9475932de54bd952ca96f26798913230dcacca38eda56c7987a46c39f4c427fd82317baf3af08233acee0c3030f290eea2595f1a779fd3a7cf9196b53edb0d360ddf27284bdfcae334a995256d466452fa37469b7e4f4ac14b9b522e5fcff8723998f145c6986294b39df7812153f80a776cd96d1efb8f7d8174522b61b02be78cc562b198b7bd4cb11d5cb69286bb5f29e58c1aee18d9bde1bafb0bcb6df5850712b57b4fca244a4a12d93e0422b10dcb68ed8e69b46634b56a36e810239fc9ee1962ae3f8ee96db3b2570a32ed9891ad04125c26990b17769632b021ed30151519196d3060c0a830e8f48621c1d768c46bf1c95cd0240df92e42a64f83fa2ed62eb7c8bb48d29a837b272174f12206337223a396f5c5bd4543e2782798604e30814fd0b66826338ad918f455b4019dd9c808068c092688c5b8ffa2286bb3d9f4ee60c098608225a618e18ece36fcfe6bc0c4c369f2c57087630c0b99624ef311b2f7e7d1b243064d9886d252820605c9e28134eb7697349a50c7422699d364c8983264c890e12d5f1fc07270efe5b133160e1bf9400e3b8fc25c163124f816226e21c347b215250dbca371d8a373110066a20c97d1322a0514c898e1337ac60cb9fd873a628831dac769f265844c33684737007646431a6715cc0a2aa8c02ba8a02b9043af1104b490e9e8280001a8a0021b4c483468582047a77c0406a02b90f3359230c3861b8000843a8eb60a645e2e8d96fe1a9db245a21132596081054d41705ee01774b52064aa218350998bf3ab388484ecedddb2be400003b041801a3566d87025375fa3532eda60667f9ac7ceded808466d8218b5da8bb55a0d17d56a2f6ab51a9dde4847722b408aeadedd534afa0a12cbd4b19b4f89195f261e6fb7cfd872251e29c83467b8c19625b692067d49652dd7bad1cd52902ad3e7406e86196b2d68ab64732ad39f616606183a030c9df1e54a496978c3d86695e5cf2873afec6780b9163683d1661916ee30cad269f70e0d4929a59492564a444444b45517af20db4f11af48f2f99cd35f86b47b8823e5c9a90589e05d6579572010c76a459569906ad55ea674aa75dbb80ff43e2969bea1bde19ceeeeee33884fca1b2da534c097b7e7f0e7e1a9b5d62af308d6bf75abb18a4167b3b9d7ae5db70737dfb6bf314fdaf0b87ddbb96d1b6d4a2f2ccfbf9dc3c6c44e7272939bdc52add8c869dd9c5740dbac55722ff1489b626cfb7944a7b518c49c384a0a802cd7a862ebb4d8fc2b42cd4168b93ec573cd000071db8f1cc771b4e380b5d6da740174e62ddb721c96b567edaedd74d6f02f74526ae7ec3ae70653fa56b7ca5469a594c6e04e48cefb9967485f0e5a5ae96cfa331441f1135dd29034ea775cfd8db3b3865aeb956fe973a2e5ba9bebc6d4ebd30e9ed68ac109e713c19fb2f4fe93a2cc9efd0fbab7cf6123f32df746ac94de739e671b49dca5a974a4fbf9ded7784ca5cee171f9a0fbf947b8b70fe222f9f5a7878ba4fd0feacf9776b2f79ff745df5b205389fbf910e8bac9bdccf1793fbd0bb4aee7799ef733f4807b39bd67f8afef6cbf3fd23df7470281c6a80984c13bb4e23524def0b65488fff5fe17d6ed75d716c360d8c8d8dfa3bbba7253102ce41b07e9c77204c7ebfe8b4bff7dbd1e067b7577e3ae013a17c553eda8ccc521bb669687915cf1388d02197d83604198b563998a4cfda3ec925b1c0702070292052946a2d1cadcf9a3eca2b9db3263c68c41da9aebf0e8ddcdd5b7d636c889e06675c1a7a3a55d966a366cd4963af728351a1252ad7347e24f5b85618b24fbf5f145fef3baf78f84e05ed84b1cbbec1d95c1efc1b1d9234872c5e60cdf7c328a6222ab946a2833da549d1fa36faae96f9f62aaacb65ada7c0bcab77ccbb7b67c6b4269280ee5491c877fd2be97f2538e5a9af3e9eceeeea53cf258ca8d9be8b71d97b2834b1b763b78eed856dbb416d3a6e7c99e27bbe75bde37a73dc97ee53aecc9c0db792d0a0173f77352da793d12c9b47a1d2d75ae644a339d42c04c71d7530d7dc29c53e6f078c1d3edbce9a03f369cce556c24998a1a4cc9c39c933e8d0dc782df95dfc0c9fec6cc9c33b3e812341b4b195011a99b76d259e5e1bd91d24a593f7737991ad4ace4300f8e7be2f1e6cd1b9f4feba45f6ba5df37d6613b9147fe2e582bd91f4bf6ef2692c6976be4a549f6b7de569e0e7bf640098ac5b051104792b9a756b12962b2179b27b955532cecc47db3656565490f43df78527d2b83ca8e846249da96623971c70dcbda7baf40bb657992bde3cd82b612b8b2cdb448cdf5a5e5b619ea902512922339924bae3877b4ff3dec250c8fd65e0a3e8ed93d288e5d9199ebc76c0adf864e0e75d03cd626ecf5c223cd0d1365f8f77bcb819df79fb755599f8432585aca9f67032983cdc973cfd29eb78228367495723f012406069035fcbb7b297b1f3e8b18d10a518622c80478717cb097372260eb6bc330fc9a27751fbe0d79c37ef853e97e18b61628d6e651dbad3bb971db36c58371b6e22d391b71e7963cc68e68b8e3f6dfab7aaf508c195d71b4d9e796ccf5903103135979a35dc57986595221b5dc44bfda5aaed5e616a2915824ca7954f495963b7bd1bdc057240e793f369104ef50ae3cef5bcb95e41b2ba90435ad547025a8f0494dd584e262911d2b8d569e4a33873f91f80015aa880f40218f96278d4d25b5b22c13565076bb0414ef77ccec1579dfe2650ef7701103642df09116dfe25dbcf8232e5e868bbc9739642f73782f645a2da8b97aab85f8a29c33ab6bf445ff493989621288a3f744efa10eef593c0b31482d7ba3d4d29a47ae75a575e55744af721a11762aa7798d1beed8542bdcdca1d7b1e287acd3bc1fc22d65f4fe9e7881a9f4e28bdeef0b8fb2915684632abdc072bac0e157cb1edebee977e275da53ccc2432821b5dc248e4bb97ef49b8577e8c7ae336c68e815fe0c757886d9cefb9738fe0c0f72f582ccf2d6030475b4d491ca5448ede57388eedef3de726fffb3816e67ee476abb18d7d65aeea837bcc4bd5547ff6c8364e5d5a0f0ca5c219b559a3b7a595e96bc61b4595de0add9ac1a172109baa38ca2ab2ce3c9287b5992869755748564d5b5d92c735f414f0ac11f3d69e8c71192ee8f46727b20371e3d7b9b82dceffdb0a5dcff9a91dbdf54f967c3ed794e50f2168a433a7b58c82c8f4e64f2e6a18eed5f864cf763fa682352fef018e6f1de0a32b5529643a21c7e32c9fbf03999d48fd435fcc3f00994480e233b9245c0ac3bafed4b5b67526cdc4864cbe15379956161f8afefee70488c225b86e111476e02c31f41f0e977e2d8e328af8e5cb8fe1f2c0cdd7dc2f0083ecd2fd1fb501c6d64975979fbdb63032325ce55ae3677a2cc5d64cb153bbd9a1a8134a3d1903c772454464652eae200bf8a46806e67faa31155dae6cf0e294f3103f9602cd759375c6ba502d249143714a791d99a525b524ef3a33537d66bc66e2e50ec7dc665a5bb7b924b510aebea7a5db18154b3519d3cf72816b8b1c04d4b650a86f6b39ead59db515b0b6b5fade907824fa53c8b47bad11e8da5751a47ab924f86e04aac3468f207b39e54c473d3fc6d6bb2d66e155baeababab6bc3dbf7c4e086edbcd845db4ee893b3914645a98bab6cf3f6f8fbba0ba06e67cf7b1c4e9b933abaecdf752ec621242fc85c21de556e3acae18edb95bc51cbee5ded80917bf392ed8f43486a35ca8d5b58894fee08a3855586a1a5a396b72b4963dcae44e83cb6b0cafe34aaaea8ec5a366c8c4e55b9e9fba6eeb7aa5cffcb60c58f76e85b29f6df18d821d94a17c0f9f5e17fa168a386848da50b785a9d966cfcf0d83d4cb420e85dd083bd67adb417f42a1ebfefec7f1d1ea51d1b9e14beff52cb0eff1b83efc397ad14865138baeffbbe8f1385483b6ec741f17b1a32d9eedc4f53a9a9ac1d32bbbaf287b10cbd0c8b2030bbba720ce19f477ee3caedbce2873ef6443fca8a18b00202434f8471cca3ba627675e5a69aff345aad46c569bd6468ab4cfb8ad2d7ebfb1c8f9dc3b7ddb5d735a41c3ef81f28d2246ee7d06b53c903afab5df928d8e10e91c34df5e1db98ea8ad3bcb7b8d7618278efdf523dd5fd9c084277673d68b13e8e19fadb59068d32083a083e7525d883a0d554f2e05eafbfeeafc5979e074eeb5e864c1feef0571d5588d4aa4af4ab61e3de124256cfc073c75a761b4b41a41dd954f287892f82f081d3ea8730edb493f3c0697d153a18686a55a96e3c725f6156065efba240b619260ee9fcc242c03c02c9163771df545f0f9b5f9f93b5ca727777e57e138774575757151b80792422372c04ec460e8fa015bd0b907c30f05f8413f67a187ee191935c3914472af55c7b5a79cb6343a9228fce82c5e1538a052a95dd6914ec875ba32a133f40d4e7b8987a6739710473a555eae5168a4f2e5d0b82b6f1c7d9ef3b710802babaaac8dd2c8f14e066b97153a8e37b6fab56ac6598b882e8c7ae5fca1b314c64ed0a7c63c4c8234d1c67f99347dffbe7638cec9ff7768737241a51e2940304437f2404436f42e718c246ec5bf05b0c431cf660a1a58e9610c4451bf477df58c82c377d58d664b175da9d9b0cb49fcd84d0a853e70dc81d04bb8e28c6428216b215405cb890892d44094416626c840eec80f4d50a7c85af5e922be4e2e292325c97eaec3d6c7b6de1066edfe66d019ff2a8e46d15353d0a15c34800008000b314002020100a8705239150349ae49046fb14000e898e407250198963498e032908a48c21841004082100008088682410085491f700f761f90214093768167e13d7818a9dce5728a1d0eea4d33d539e5af3f86fe2ab72ea3ea375bcc69718044e9cb57d12c6d6e9f74e5e5244c01cfdcbeafa4d056ee2042f659d394784a1b9eb1dfcc436901199a54d0bafe1d64c75e3c4d27e61ed97e44933596a8e2f68d7e304435c2c6ccad7c9895eb97c39f1d334ee21e86c89fb21058dc40e21673627c65201fcbc1a804dbb430e914ebd8ee3a057f78b78eda648f4037e988bfb933885e94511b1a1ed9f0e54835767bbfd7a9a8e66eba1f873d9aaa5c001d23eb2be19073e2a9ead1559020c939937f440dee58016153a9cb62e027552f645a2185cd1172ca1e2d6f77e1f5b3525211fee1ca97e10bf96df8494565a65c7e255367bbaec84623cd04c93d97a8d034d3e7267dbd89fb8dd96f1bedbe70334d50c560dce1077ca6291c647eb6c9f676577ed75420dc5efa1e68f51d3e951535add249b0406d2198d62bbda087e933ec7f0a8040f9c3fd62dbfe875e04792dad429505b4b0c66c1b1910f6275f58dbd520e1b34e7f93d0bcd6292b53a63312795a2c1b511deed9a6cdf91667d258fa661d3f75589d3681c0e568f89ac07f20afa2568835c4e0d20deab2eccad939953a08646eff54435c7360fd627de3cbe411fb889ad59e02cdf0841176a847f5b122f6f1b10cc8f800b049dcf5235c78614cc0c96515410fc10eaac550f7b5506d936720a308d018f76ef56a83b8b01f293f723ec5e8b28250c7a7cf4a680fd028136540f934ca25d817b36aaeef023386b5a8c06d1c2848068e6e4462e5294c41123e1f1bb94c92ff0eb208efa8e230dfb2fde398061907f4f9c3dbbdabedaaed0eeff8c2b4ee8b4e5f296a882528587eab9e262573ebb39932564bb0622385a96b7b8727bf9182bc70be69f2b95250e02be993bd26f21d5818d175b8fcc2d4c37b9cde1a9088f9437093766c7a7b0fd65a19410533b8cd637564870767d7fd210d6e67e4ee7042ef2fd27e069f2b0cbe4c35ce2e087c8807811c717fed1907ee3c9f5fdc73fa2368c3a8b7a12c4310abed7c7b5248eb7a3b1aa2a99d6815201ca2a9dc5d0f2d5ffb03fb152ab64469200c32bb292d3ef23603d42d005b0e97c24b6e214f622bee59839d21dd1ba216c33d7112d6eaa1fdf0e250457b89cf6d69825d452f76f90df22f00ebf353e9ba39b83cf4df4dccad8d0b9fb133512f00f306758c6f2e21d627b75ac7b79a484d088643794f9e19e70b796574b81de1d37c63578a2bb1cc5fd3de87a369e6c4b39c9dabce7132398d3f1d95a78b6d4b42abf33c46482971931eb27472d40312d7e182c991e30e79ef29eb5af6b7dc6a7a0588e2694ab3682aaa6f4d6eae1c5fc4901737313cc31e681d7980c6829b7d2068807ac1f759c7c4e06317afe21c83db0ed9b950975671236b4b19b0b279dc57da8a4d802d2de57a64c3f0e896d6a2bde56080a466c917546812097bd82c8e5fee073c010066790a50781d7fa511b503e2c483b91a5b17b084eac487450dc4a98eff57fd8079ac07c9a706dc1c6b85039fce975c632755c4915f66607b9a1cb8de64c937f3b0f920f3b80806aacc63c1affd5384e452a0024cc35fddc5aa6894767aaa192c9ec063466a8083fe8a0b2bd9abe2f8460d33b3b828205337f1bf32f0c14c7fddffb1a85ad0e01a514fcd7f660f1387826a86276dd8e82685ec7705c5414dee0d3ad18d8b0c0dd98cadea144c4810b5285566b156c9896eb5089317bee137fb480896dc325bd2c8438335a087d1d961e8bfd6822bba8ab53b33a70dc2e216346db75d27522c85017d92d55ec21575954f0249a98a9f60bbd347233a74976ae6ad7bb1d5123363e4a292558f4b3f4b0260318d2c1e49006af73dced302fe9443ec8704b6597cdddc8e622bbeed3c17fe7f5a41105e01c5c1caa018ea4c81ab2323ad58e56e50c576559800ee394f56cec5162461d519d9ffbc5304ed942b4aca55d624f98cfe9864ac3598ea8ffea72b9e3e40784dd09f1501c0ef86e17b9c222a3cf2b69805d6966dcbe7eddffdb321e2ff3f6272dfdf99076ee571c60f12f4eba0fc9776ffe1ef94f6ef43ba11755f89a8739f01e04ca7fc1873a5d4340f5ca448821a267f229b9801fe11a1896e4f05684371c31884fdc111768f03f1766f29b2560309f2bc6d03c05535de86e16a035017886f3094c493cc3b39446087cfd13515da6283021d1cf52e4a6d56c283d7b7a34ad90cf42f16aec102403a0bb4329179541a3431de3849cd505eef0ce4fd95d1fc037b5655bee76460d19add5567a050746906efb399c48d86e605db91c4d6b7ad90d3bef8a8af32e0ff9314074c705f9ecfe12e15c3101a63514dd694d4f393fad5e3774b1fe40f7b5c4b8410b2d706448df69448475afc979e7629573c62165f83761e1a28a70820d9a7a250ad91f7e7a6905280d3969a49f32358e5dacc8848073d4bd9cb96431d2d9e6ed9fa492525a17c6782558c8caafce5ea37242e75a56aa98ed3c64442742f269a67c7a233f70393cbce01678cd31cd4e1aa9ce12669a083073025e40d05b0f7f1bf08047d2581f877000cf70e6b51da920ab287f30ff836611bcf651a05ba1c9622c9775371c617347b4e18591fedadb1874e4fca5c0020780d595465767625ea4b3325c5c7066a6b63a802ce0e64dc3cb9692e0f0a380ed1ec76d05f407e074f55fff3b21dddcf3642ba0480feb6542a84a3f0cdef047705fe4ad4ba3c584bfe2afe358baa8bfaccfb3b5ee12c93990e7d17a703dbcdd7ea6f4327e58789aafccc7b866ecb560efff93d1a35398e44c70fdc19f0258f08fa953f34beaa3fd7800ed522a998dc5cad3e38925b24f8c4d5902a32bdf372634b2a0b4e884bd1b501decbd0602c0053243410224790fcfc68477cfd59b4149630bc5ca81544dd2580cf9f61a24a4bd929691acade8f06a59535760a8305238a22a4bbb61a5d8570cb6d1af46608c4c0a00b898aab8f4a0f633b538bb47a6576a99ee7fbc77f898f27b3c80e0aee6ac93e3f2e8335bdfb3cf0142a7c02f19d654d8043bb8718b6e1f6ad926ad8d893af93afb6856a33359dee20a7b65459fb2b6f1e12beb0dc76b437a09a23d0004944e8a00147bb14b3704606477e80a1fb50e80fc416e24a58023ef3ca0347741aee562bb22d268ed6fc01ad1bb61b0af07cc487d551bbf0d069006080e1f47881a379308978b7be4afe51dcdf5874ea15417fcb60a94f15c8ee0144f60041f42c77a34f91819bfa9505ad3f936785d7382a090aeda2154fe2b4197ff4f1384ce0f577d625032ee4c7f48d0016a86ba67af1be268e2e3864c36c7051abee1e225df0684afd3aa09e211a3ca05c7d5a755f4d43a226d80a5705fb565675657ee802ab524d3b7a476caa1f9183e1d0ca90c2722ae673e4d9d809055f21ab01017528e1fefe519a375abe522d25be294043b7ed2c6add163bf29c6261701fceef8f9ad5baa3ff13ce0aea3447422937c04c416ed68db7f6cc7811aa6f8560e070ac44a6fea506c612197db0e4f8f561d498e330665bfa23ec649ba810e494d38a6287e814cf9dcffd1575a440516c78528fd0d5fd28a6de3501db3b2daefac84d0147f1dd0f39166631bc3d177a682f06a35acc9aed7b0da55c72a0dd7700b9c8c1e96b3814a65f284812c3178937f4cbb781f93c4e2b9879ab1e2970be648f063cedb2961d2be34d4ff89d2e5a77049e00417123cde92f0cf26cde02857def5534e69f32cbd4f7428d44982bf2404740172d6bfc1cf997914569a1d9a3040ebe24ff2cac31b38e82be1e704151532ab15e913b0e8e5fa18724237f0b0f5762d7f62af081c2b189c0c073b9197e29448eb0b3d953e04fa456b8a5308369a6a833c3d83a72ef260d457bddae69a25275ed5c3070b01a5547a61f215028ed2981ac97c975e45dff23e8d9091380fa1bdae333949fe1b51dfcd909b6ac8730033646c32f05df8e8dd65a7243c5e689b0e9b7b02e6d206dda03921c6034c2f07d4155b996bfdb8355064738cb41607b69216e76bb4e5727c1af2c97f1b29297d1d104466678c5560c6ad0d523d2042a7450287e8fcabebe6cd10993600a545cd08f2a9ae3376f39113706ffbd4aaf510786415da0211d196f140333da3fe9a767e3ff4f04f6587a67908e6887a6fadc461eb82aded5174db3ea510418cb1876b28fbac36b497365a3723b1483bd9cd670d2c452d37553bb3cd90b5bdbe0a7b6a0d2e89de948829c15a1cb6838f9e0fd5a0517d4102d0b0baafa86aec5903b5fe6f94d68aa28a549cb48debfff13102a57379c90e84bc8b6697ebc81dcc762a9bf343839071900e9be8cc8358ffb6212b315a8f878d0e573064afd99b613ae8e2aa7b2d1e84abb72b92770504529fca68a903d664fc9f09d05b34d78374b1080df9e5d837f3d97c932d50edf759b1d8fdc331a444d4dc00d7e2c122079a604424ef8832a748663f113d5e3a3f10e2365956e11024c04d8efa89ab4709dbff258121b48cce784c3546f664611d62441acc8d287afb0047c7f189260b574ccf7c10c62a3dd564d111083feb094d47bf5433a09ae04dab51471a65846f59a18c590792f6dd7bd23efa20b5e5da47e872752f8770c851e215ce04db9713573d65a2af1335f4acd03e2548c2877c4ce371a1a7d9a1cb306212179c5b8f08a03d88585550d31f1355ebb1a688e6d33207a3b0de5e25224055cf27916aef30fe8b8e4227298e5ea91b90a42b0faef525656a4f9a04cb089678e413151a306d663297b6c72fc811fff594267fb22fd5558c0d53438f29c0ebd8e1e1c87d932deebab111b3fdc6f25d9629f4025dac5df29d3c6b195e0d94508256dc030059d110367ca1ae6fb306f5d17709eda5aacf31a7299ae5749f1baae8b8c26c8f9659d0ffab876ae685759451257ed64300d5c3ceb6519f3d66476d9e606483b0e32d5afe8ec35cbe68084d8c4509abeb7267441555797c7abe464a33b6f0114bd181e292e02d84df8b29c53a74a0baa12ce017c56b529bbd02dec998cca69d72fb69302395c5f56937dc109f0ea83243a2f110e661a9c4ff4890a9f215a8f34095ae8ccf38858b050587fe7aa152d9ccde1df032b0e22df410afe2b8698ccbf77c6f70190117b1e08abb7206667ef6ebc4a9c3d3d3618873ac098085d78cbb530f62645c0233a27d869abd5d51c2f189e5999c7615aa50acbf3d257526e9ddc59c16a4206cafe831e8aedc7abbc194966c9ada9b21b26e6360835e82dfa04f2ce444382fd22a51121c3fa73749a28488ed32229d8cf9c488578a3c63b24a108848c8da66899fb489d334d9ca52e48a5ff2a03837a59a600a21e4919b82e9d42244eb998d21ff952d47042e1817eb56527558f07eae6e6e29bf2afdc4caef9cf20995333f1775c8e544ef0080f640f4cc231b134a3828d0918d782ad714a3c94a388c242980a61a928f5c63a37d61b966fa9ade68b8252b9d15413b61adc35012f620e7cd7f01ea120809a1eac411bfbfeafea6cc95f373bc65bc67e8699906dd61fac5ee6663f6a39a73111326ade8dfea851fd1cee18ae0da7124687ad19a6864bacee40b0f2a407c646838ab4a7e13bcea756ff622e15971989120b0fa58a08cf1e22c3db7f2aa5ef531ad75ead1fb3030600a7ea44de0a986094b20b1d52e26943f9f6b9e3058efde50c6cdf21474025cdb0546a0d461f7438c23f93a81df08dd5a26ef67c73032218e6a62314bb8aa07e68497021ea00d471562ae3403e2a9b6ff14227e0b9f670a4cf5e7c5cef89b1c26c65686afbf97a81142343cdb688f880b748a516d4dfb214a1be9ae189d4b630cfa262d991b2a4b2a88aa5e36a2572711014ee66d0b511ea8f2f7602042fdf6c9244e89c9ea184dbc3ab6409d44af6ef338d65662cb5d5a85789f36f63d0c89708bddf035372dff0fed3c6b4af07815407b940a963005b7bf91e1dd2b94acdde3586407f2f5b37cf25da46fdd0ccb09adc516175cd1a788a7cd1ffe21a4837618cff70062d4ef2a6901c8a1bc0396c32543f4bacb7d840c8c863152573d8c2a81e9ffef116f2c4e7eac2159f6df32dfbec26df446a6423c321ed99e312c216f6734223a6173ba87306810d4b8bc4255cde6b4c9fcf79460fd439082f26243d4650b850504db98b88ecea56b6537b8a9c59eb4045d2587e594505c3ceb7cddf9f0ab2953b7b2123e1ef95f063b17c20c17258df2480b9d45ca9e11effc90328cb6060966a009e821544c6fca709ec799cf4a4fedaf1697ef0133bdb63ca9d2ef66abc1f2ca6f581ce2eb37e26632d0a7c68edef824b95527b87d7dd2513ba9ee7b8d93b0885ffa42620fe3d2888db85c59837b3861ca49c8173b03ba74a6064b5d70dda7b09bb8872ed11e0b9cb089bdece9c35bc5fa409fdcbe09c2bb029d652791b3b70eb45d1d354554f8afee7564f79f582b0084593682385a7cc423ec74a985a1bc9a9bd61be9f40eccc8d906de825288e374b64e79889432fbe4b50792939d9b4bc57114463483d6c983d80549ad38567263bfda8ebb83cfb310278bf762864c5b42c43e172c9f7227a6a07a60656be4a43aec7da5f56876a4d82c81a4e0cc3a3b74abae5f9b265288ab263fba603d3cc8cac23d198f58dd3ed162627c5b402d2e48a61602f23644f2ba62407ff02854008769e876efab4637fb0b5af4a64553b65c21cccafa32256b741e75a1c5045fda71c919299ef25d7c3d48ff5d69b9b76ad658b6e717907b8619bf8bd5bc881e71ea94216161730651fd4c4ffb842ffa18411b80952a2a1115541c372e1968028b94eb6e95b11269c6b03f9fc9df1e5423bde64769a0d45974fc08c373e59529e8ccf4c1c762def1cbd770623ede0e099e963709d563779254230895190a2a2639e00332090d8ab65e3a80d3618d159eb499e6ea0e07d88d13b01d9aca73054ab70975ebef54ef74eca5e918845bbe4be1dcca54d86a3aca2e748eb4c5eb9d5e6d93176f66f188308e41d94832c686604dd02c5e77cd295bb09567f132e486f62496ef2e9e299801a34ddaeaf9f159e800e6fcb707c3532f04c28cc99cdfa95578547ae5923086e8040b2cc103987256583f2b4c33eae8d1a166db842282e9f2f3b6514edb0a520bad9c1d6a9824a091e946d8ae4547b8c4d0f6e1a6eecff43acd47f8053349c396f1e8da336cb9df7e80649499d495729163f92760786816d064cc36bcbec36a3b80aeaed13312166e1a06c52335102cf26b17c114d1b15075da72b7b40cd87edbe62040e4ad16318c999f36584d363456989754aa8db5a09f2c2f0ff98c75f8c9f89789b662f000e00efc554569557fc0a354f0d97c9c1518ea4b51f718d40b177b3e28fc22969246297acbae5e52ec00780ad57343399ed9d30cac5ee9486010c93366e9625c18e5063a6c94ebd5e1e7ff3a8cc7c085f010505f6e1d31c184b26967c0a6ffdbfd634bd7b2301d7a089e26fa4049f0a6571c9174dade29a7e8a515c5ca06e2b96a61c5fc186c7b7218d907db8a37af7162e44808c06af56289c1c6d81ae8b4d7aa26644f0e59647ce709a8ab06b185354b1f477ec4bf6edafff109be2bbd29b3262b52e68cd03440c37a8fb3b89eee9344ed59d88013cac98ed45517ee42f75f911a4467c29285e5d70049efd5c9e831cae33dda29d9cbe508c5e559e16d156589523d3c23bf04aae72db7967541a42b921471fed7b55d6b97febfcf09a024885f771523f7e494e6a0ed677e8b976e92dda987b9f016c44b543e93f5ea9f57770a0f9badad6e2345e6ba89a68a3b14e6ecda5499a60d0c8ad95c92481b552c3a835177e99fc5f32c33174420be706453096333fe4f410148f5cf0f93471612f987240789a3804fdb89e19d072df2ca3e6673909dab79f46837aa2b9ef32d4fb5008f1a9f71738f4bfd0ca01cf11ed2984629c64d2b8400fb48e06c13c3814faf471988b85f5ad0679c35415527d5f9947374b413bd4ef8c1ecf89359e2f8f33a8f070ef06efd284b82423e185fd86cf41d34f2d1353fba6470dd437a129b6286d9fd6770e2ab96e81f17c2afac05d1334253a919b5c017cbce7ae2ff89584bd2d06c3c5ce497090fca027a540148e8ca3ba672c407455ebe69e4f87824b9d64b6b87086bf1dd120d0828d261ba8dfc3159550180c9bc3101dee34f6c4ebdd61647fc9fedcacde1c1d4087488470845fd5f2988407ee119e2e0f2a72d681c91e4671c0367c62bc250e8d756fe3c5d08eb591ac0f2cd0a199d8dbd278d136b18b6b9ec38a34c64a848c9ecfc7a89ab33bb51533d36b1672b5b1cbcdee282bd979b7f854800e94364e3957f6b26626490c9c78086ef827ef70efa71ed00ac779a9e909799d3a8d52e41c8c8151ec311265395dbc01c10f9095ca47b00f4788f9664911b1a0402f02866afc8aadd3f53c63108a6574e840d03e8b779865501b307f941aeec64834382f2256810e6b30ee27e6ab8d31e89420e0447f002fa3c38531e0e4fa3f3c37e4a20046d8d71ac7988fee083360e8369188193ffa2d84b521bb872f942d2052b2437e7fa2bae000b9c52a2b5b3a7d867ef0eb38b15b0afc4391adfbc11885604d12e768c687d3585ea0e82dcf5eb691536d1408250fcd9766b98b1ecef1808b4263d9e4034af70a7ea5197220020c87c7c283bf209572dc768e424b957ae78e3ecfe47b6f21129c0d15e440b3f9938ff9590705a8c5d0ee0a8278f63cd0c6e4650e88eddeeef984358df0d891a882b08cbc5c6fba1556b416d2b7aaa365ed59478bc815d7a3baf244291e2a6222e28fa98a5c7e2ad2c2b9cbced97170384750ae3376c9c2168dce6fe9f169f59da91e2bee3eb3ccc1d9c9a678572bef7461ea48f3ab404d44a3de90217490a7cbf645ef31da0205fd6d81ec11d9b9809bf009280d4a05ba4e6f761109a8396eb0af5e2118e581eecf22bd8d9bd9e828cd5e3cd309458478f6c01760e22176282a140b8de08dacb7f1fee6e15603cd58d64309441f6956ff0ac23b5802701427700d50336f5c26f020ab6028a80c081c7af9f67816162775cdcce6be27e939d452da3d2b08eff76a0e276c54c52d55a3c002ca8e9fa7cc0f8414880d763302b650848ea980296c759d26b72161e7376ed41e24c71be16dbe939781e32276e8a6e6ad4e38605986733bc06fd614bcdae384bcc8560861c2f65cebc746ad0ee289e87d6b097f4929f080dc8c2ec4fd9954c8fe6fd7a3e9715d04539b812629b762f1c6c30e734672671801d2c900e85a7b013bd30df6d47dbed424fd2a5942a12003e60d6d20c34ed5af7d34f6a7ed1488d14edff0e822a0ef48dd6638115cb2d18efe3e19857af7136cd50dca3763c1d5afd6dc283ecee67bf290f7580a64bbbda94afe5a59be9c8f847abb39a50963d2e3b128ac72b9edb90602ea0af22c579cc86b84ebbe19107932d8267b4c41f8c9a38a8ac59525e4ff406e31f55b86f791c0ffdb29817f695970d7d25cf749b190bcc0760479ac4b502aeac97649529bbfdd8d1843715080ed6158433fe94f9fea7e7cd7f50968d32e8eeaede9a42c53c63f55818446c7cc68899dd6e9db53b3c7118c39c493500a758decc3831b6a93ef72ad8cbd1183b79e4478c3349295fcfca42aef5ab4f606196790089325b605d2191b4b819a7641dde6b4ab483025c15f91c5f7436c3036dc30ecde2968f47b905a5f62f7f18115f4c795835510b6caf16eedd8eb672e2264f4bb5c31f15648fe31f8146b811488be9599090083bcd08d4d54b88b34e169cc82c8706078c62b42b166c556a9c6d90c89f316d22ad501fba7f97c7456aa3f7451b83790b2dbcc09bc7054b06aec00f720f96fcc87d61f51a3a65e04315d07471e0df43379412c291caf5aa42e50fc3b71e5e8ebc24cb00a009b29714621293383d54822ee9438186eb8024d56d54195dfe3a3daa81678f4bf9cbd71c523e2cb303c24c25aee6f7626a275ee1897b099ac9e35fd2626efbd493b00c23ff73ec7188acbca8dfbb96809a5ff182abe1e0dfdff91422091acbafaa7c9a164d28c3696741c0d67ea06176e66a35b920a7f001b5f984c8c893a44a22479e8d279bb40eb6e432b30d050425d8494eef3327499824cfee3d204c6ce6603cfc99bbdc2578d79bdc66a38c7f076c77e1bd9dee578077ad433d2a54d49d80784b3bc7293cb1303154a0a90de16b331930d866094ed1c9944cc14ca0d82a7ac9dd4bbab962d8f07c499bc75764a6a5c46f8319c2f5920e60dea7daf427614d5cf330bcfd0e3c235bcfaa537252646675c4f5087e7a722b58f8fbe12c8a8fe97083e83762fdadc9f4d72fb976aa5850f0151fec19e2cb0454ca83cd5cd51be218517478c39364301879aa1f8c811e4d5cddbf1d5ee5af1dafd24dac2fb11465d54c53e4bc524f0cff7e82368d154ff9d6f4a56a25794b5be543a1906732bf3f4b7aec544e1f203fa064ce626768ac71af7cda1da859530021cf81386d5532a871e84caea62dc222d93a39102f0f281018a3b3f8c1918884129d2f2c12ba747a4c98f96502bfa1c65a6d68f9afa61afb2aa7aebc9a2afcc6c5e2e5f563cb22748693044d5390c521a4cf25f7fa48f595e3c7b7a715d73c5a9ff9de56581c9ec3e5988a5680b8adb1b5a0d54c506c0a50c8a282ad3bc3d6f7ce2c11781030650a629ba8f631587f4d4845a0f59e6db227989d0dab4a210311e93e75223df5d2b898cb38b55800953f357a79912fb107b55f08051a38dfbcf52f04b7c4645d3ef76af2e9d06bc45127559354fc40b9b074c1be53bab35dc6f4df4e6b6641aab0f46cfc2cb747df329728c487c5c65027aa8ddc95bf3512059177030b941ffb4b1a211efcbc4de8ec10d04b5ef959fac8204e01c7b39dd559505b7967439e2d5fae2f8b31566809315a1db10b37b7a7e51abbfc524336d0b98330386425a8b8322933d8868e1a1526a50fb56ec67899cc8be0d5a19405d613199437ec3ea70287f592615b4e4b101c12afb4c99801fd3a0cd52f9e96f7d7f278c4b5c66d9c12bf6db33c1b270ccd93b650bf22d010a18b587d4419c28861348ef7ba825a369bd3157dd0e2dad35858cf16a735741f39b4fed40422d7d8add44c09b02d9c32eba184905bfa0ce646c24ce2515d933550b162f332d77a05033c5715934141fdf158f82195510252929ba421625faaa788c797c0fab1247bc732b8edbb2d7cc407629b81a11ba37f4bcbe34ccbf230fd614000b5d4f5203f8f1fedb214417f14b3ff83b5d9201caf8f3ace516b27e5762473b7ae0c1a9beb8c027bf217fa94fb118053ba840ca63950e69f4b1153410f636367f33a7f04bcde80ff075b1999f2c301d325eb4600a566540d4a4a8852a23605cd6a7972cb085cc35be069ec54bdced3b991231e988b23945b40c7d91c0da7f50110d8828a2d07669b3e63dcb256b508534dc1d5fab67fc0010215f820e8924f4e0f9e4ace6e5b75a8f21d6bd9dce41693900fcc0727e672718f5b024e5a136a4151bb272e2aea66010495690858ba9941806c99b8a216ebf1f5c81db9e240e4ca6284c4947fbe839ab0414f7dbdc8ede15b1cb45cc7015d812aac631e8676e4a018319c071f74453e2077c5c67ccd292d612f85254a2b639b5f6507210a335fb289575ecf57b6071b1d3f00d4bc60354be0d1983a8629a440577ad036752f1a53e10e7e6ccad4e6291c0138a59105b92ab8188e056c65b84870cee00e96a7ce005b11999181d7533536ccbce4b42526d9243dc49f1abc9c16b78ad45219e2a070088d3f0d219538dab5d448b2053e123ed29a676a2f5c3b9843e556782764a4c491766b57061e3e0dc05aed42c893582cda50e472463b9b519cf56e6bfb6ee9ffa8febb1a72c28840adea8bd100b902fe89bcc4608d25276eb836438013b2d485a181d2cbd1f6a11a3d46caecb6b6cbc9f59bc01f5931cf27807d6795cb360b72a7b4d50e9fcfe1374b48d2542b9d75da3c23f922412c3cc18993ef464c6a1672296f6e3adc54cbf8dc93d28d9c0873b6c252950d2989c54b2c02ee67b4de7c2a730dabde33a63ffbeba0813585ba65c7fac9346cae59e05c6ccf8b7cbdf3949434cf2d982fcce3c7546d464b2a79eeb3f0a2c76efbe67b7ba50d616aa19eafb1e25285a802f16f8162df1849ce78f8499437f29ebbfaa7d06a74d615d691fe5f0b888c22334715be2dfc8781ad934284c63ab1e472d94ac547bb397e0417c661cc0986cb63861313f8f4aa80bf8403548689aa07bee6c93b2e199d984380e3633b6a2d5d1b5c1a6ebcfecb567ae67c746c3a50d8c357ff7dc03bca9561c34a5aec0a4f16346f0f39beda949d1ff2a418b7b9ad8cc14078d44802c0e6f943c5834a6a981d34c231bc443b6e8a5141a299c576a15a3a9cce5819f2281aa3822c8d222029b7a0689be2212a9e526563df318d316a7deb884f7e6a5775262fa718045533091d087abea6120c13e149290e635b70fb972622bb5993514d7eded16f9ca3c578f6b445d55c5c82ebd2126ad8e525a7ce5e099c77a907089ac79ef9996e44366bb6802c7ac6fb556e2cb4724cefa0b91530fe08f24b6f038905a3e5fc4b4f265557bfbdfa600930f1ca00fbc1307671dc8dfb609f903b480ec969411d47dbc521bd701e421646fe85f8eec16d5893262cc87d3f66b0e61b0eb2884e58a7975934753f055c035e81c9a954f17f1b85e624c65f7130dbf894d7955adb39e35ce08888a71120436a9cfb1a7226c7df5c5f26048dd8d4eb324eb2efbb0dce30cad2da6bac9d28751552d919d3907c2bd47aa98356e938ce8310102a115f3e3ce37095a187afd09af84a332535e4202e64d69c0bee6a9a5577d508b2d1d467754dcbcbdfc6eeae2ae996931a211394afff6b2ec607a40d44ded114cc1f96e1b97c649493d6a7ddc29f4046193332733d0285bab0fd5a5439e170f0ee51730460685e452a147915474e64ef8fa069cd78b58d43f5c22d076a62f23e62ba0fa67c43d2c1f567f2207fcee48ec0a485feedbeeeedbdf0fe3624ef06cb7f148e89e400b48e526b1cb56b49e5c32e26af394c0692b4941d82dafda91de2bd19743c65beecf53d223c9d37a26ac61f03b79f9dc853fca55fc75d8080d1bce884b36c6143291147626973678f3a746f32fa7cfe6537180a842046af96f920acff2876f3b386e99405b5389f140c0429441e0085c771de2deb727f5730c764643beae9b1d7a6256089ec96c1cf2f915d68c9c5d624c6657315050c609497d17240e5b25280f5e85c06feeb25a9d665a5c0d33f69006337c9717fe9e5d875f0b96dac989b5d0a4274a28bbdad37c613b97565562faada5c32ee9cdc55a841d332369eccd096bc856095f537961cf65a998937b5285ca109ff624bcb6c19c700d7a4ef2fd6818f3d9350ac1d402a1d929084ebe6c122546f6358bfdad958bf07ae2dafaf57a4d291b148b6fcf5ffc112a0a86a3681e53a01d44fbd7702c98ebeaa646618bf02a6dd1979a3155962cb152c56970888b296d7f895319c65838afe8a3596b0f13c31f6823242e24ac58dfa65dab72a162c0c3223d6b9261bb35cdab4e5ef8db69fec8af91c44f17e36a98575f39692e18c19d0d2f50c8d53c78e43e27b4f802bdb0638f1fd500323e2c7193a7c939a506327839c8571bccf6b8fa495b68534e524e95d2273e323cdc2c373a7aa563b307b545dde933f109414708f518dc97d74e4585b788bdd925b22105efc44a671306f5babfc6c8d5a806b871f70c37b7e90ea91fd92ffd2dea308be57eeea689a417064e9b332c37b502da8b890e453a3c3abf533c4211cc4c7492c756d42082cb2f051808a7e3299b6b4907648e62bfed19b66c95733af37956be3b7c86238062c51c300b7691c144e45e00c1a20164483f9221191ce06c490da3a3aa329ce8d91293d591f34af02560391d7179d8857e936ebb479b7b80ab635754800a09545e739a20e47857e8d06deeb162fa9e64ad6748e14db8846730a6390af0ef0267e3001c1e8d1995efc85cafb011d37d363bdec1f404cca8d3a1cb9d5ffc7a702a0124ce4f4cd68af25dc31025cfa11d59c789f2a790c5712a9c7335a45d1ef3ac59ba0b2888c6dfedf6106b477b444d02c002658418e12b9883a500e898ba5b12b055e799c5cb07930c60350572e47231b5aed39c757c7ff7696ed7e42790b72663540aeed29a0e44428321b5691bc78c84a25adfa1f724d4625326d36135cbe7a97e4257b9ecf51ad3878060313900506e6b7a4a74e2fe9a0086082c32ce73089a7d15fcffbccadfd7608bfeef0f1a0d36a7bf6d777da13b90027fb6d267794318786b44b2648128d50bc802f40d9c1a20d372cf4169fb3ba0999abce971ff0006487a33d764365f203d7c1f1e16e4bbfdef6de58569237e2cd55369d0c65146057963f060c2b9dc98061550b635462baeec6ee049d8fa131cd3a41c3258750dc1ab90b4f7f98bf6ae3ea435a3bd90faf71caa33980402b1a6e3095160a8da776fa27c8f6751935fcaf65adfdad94b60cd550fdc4c64af2ef64f1458ee2cf843961101768215dd06e5bbf8bc68b5cf67b6b3c4bf315875e9ceb3bfaa327e73bf01801dc1a09e9ee29776d914e558d0167a8a24ac0e82d36dff92689b18b532c2fb7f647adea2eb071eb9cdab9dcf42ee1f8f4f113f2a77692c6ba104ae36508fec7776ee1b5d4e1d978c4113718f302c54b654274595f3a761cb942f616160d3ad4512958062168ef5da7dd2599ecec0f113479d1bdefec2daf72737214933037cca3d2b709727b4d9e82d10a5db066b152340dc367f94f188955976e243cf913792d46823ef43629152be0793446bba58eb29871a6c06710d6eabceec43eed628fdba0dacf2787382711847c1ff0358fbb220eb7af6bba75d88e1c926507a3435623968178867064a7e53e5a43012771d80913e89dcfe28367c294685b9184156f201b143de9f7ae76fdfe061d9cd33cb195535e5ed2a1c9ad7643256b92391cb8d3a9ab26d5d7024b1843917011be53bb0627feee642228b8c3e8b04906da7b5f5d8b040cd570dc9494a8401f10e469ab7914bb08ecd2f6e46c10116021fd2751e74d178c32f141c6d70c8278fd2ef1f97efb464ea219008eae4d9dd40b0d311d2c47e8c2a05ffc503c1fad9661f468ce826c94ecec9d17d9240c80b0582471e1ea020dd54757c025fe32a934b92651411e8d8af5d69c07aaace65b031655e06ff1ba57e7a0f4e58448e67051ebe298091adf1c3bd01da657649c407b6a91f692d05eaa00d5f3cac5b759b75303def943898c18c8dd1877928ab1a90cd9222bdb4310072bb08f27157e05bd19ada79dc41940755ac93af66239442680d275b0378dd8d410243d9d2274d618933ed2f973648850d53ba5d79c584072b62b6c27d382a6d855a0cc5d385ae0cacf387ae7ae682abe3fe49c0e05000853c866b7e716687ed0b0e48fc5d1ecbc1dd3547aa590418317ef079de6e68c03e4d70e4a069f068ad0ff585b31cc38913505519f0dfd4ee16a17e7dbdf0cf01b43cecfa8c883cb34bad37c29bcb4893619474600027822f4ff7d4ffe45039c059850bb7b72add58493a3be0fcb81002c83b28c9cdf9c03a54d9630ab534d443ec1108dd5e3543dad8f86db82837c4b7a5d2db331065468147ecfe8a0c5da88462d5ceaabd78a921f600d7a954e9c263274de8b157264eb5d383933bc1844045e86fc6bdb6ff6113eea6b43e04026546fab68f335ea60191baff11fa89bc7da90c490ce24ee9a4a9164f3a6af0b2705c0b19be6274f1d3e6ecd06fcdebdb09c8cd68e305dd21d89946ee88ac1c8e14a37c27ba9fb3ec03c2fdfef7938f3d2c3832e72adf8855a3ea42a33df4e9941d35eb5131af12bb81311f265eeff13ee02919dd10eb8110249da88f509b9373b77055dc06fccf17e2951126b773f78f3e26f4015ffc76970ff015078c265a56bb06aa775f0c9f0b755dad3d9218b0220b1894dfea970b1d2b828be79acf1263a3afd40e0b5a813b0cba1a8551161740d2161bacbe060023426c830215c5f2e6a8af60553b4d9c20050d7a64178ba7e50bf01dcb569615119ce7476eb44880f97947c92a4c9df9a32281f83398801fcec38413e366b30f28fa18a822845df53fb26bc94b1e737891032eab80be396e28aa3020e955920a346230a828eb998a269c724da8f01f5e8fede998294b79e40b7634cbb50ee8f2c5acca67942096ac1ed9a952282339832df40380b82b209a0365e9029a8d8aa19502eea67c0fe9f68bd49abd989cf68f827d32e97f6f6ca99e53d9727c3285a0aa91bc1e7f6b384afbd761131e88c45ac229c4507f06117f6c9f32f8122d1ac3d413356bb337e0e1e7f07e03acb26666601f7a4fad84acf80a61c94988052d83ea476dabb2aa9ba1c3605389d8a1c8a2a6c94694f6cc90a95d05a066a69e806566490a947666bd408205e75691135dc34fb5224c1ac62152483d9cc73881e3d8288c9c2cbe4549a6eda812926fb14cfca3302b6ef1e6a590bb62b76618aec3f1d464e1cfac3d874b839035cdbcebe295655d6d558b8506d30cc7c42868729524401ebf72e6c076a818f7996ec128bf49292c9ecec836da684355aa391579d52ee0319e25b52263807d29e4d4cb16e9f5a589c4a014128955af57861163531004b7db5f1cadc17023c5e18e2cec35287b338a5374767079124f01c62ae1e8b9c06e1efc8455269d698404292ebe3beee2b3cc4b3411fc15230c42775483118ec079635b511b180ffe2c40b352dbf3bc278ed4b28609960d7ad5b0e9769321a08545705e9dbec0fa1dfccb844beab512da1211a6bf2d1e8c650b2be80d47d3453abc1e27715fa89a94b146bfa0305040ebab0362c24d2dab13089b42913614b90e64126809bcfbe12841231cd99ecba8ded0c1fd530a066828c1e23db058e61d84c104890795116089ae61b325351fa722022076a82a0ee28aa2648c045bfae8ec90558102021c34e5330ef928c024868336bd33652da7f0ebb531a804bf31aca043c6e23e5b8487d2feade901e44f56f5b18e7a641f32e17372bc243e06e2f6d2fd6a2444853ecb6cdb9f5d72b7135219f12e1732580670a9626563c10b8194f49868b92f1256dab3221a251ccd28104c053ffabb4669eba230d687debabf39cd67b79a6b5a69c4706cc513b7d4bbfa6e4a0e1bfb8fb67aa56c2cfa62ba24d565c01097eaa3133cdec3cefa7706d0adb173d0eb9523c2b81d35ebabb6d7b2d71b92fe40c980a249fe26a427e512747da3f59a610d3e9181ab113b9d8e24751ed81b00ae2db8a2156030dda4835468f04ad16f635da57fb3687e9f1e9518e5ed5bcfea78daa1b46889f74d593c49c69b92284c4d09a939f8c88b62f556f457257cfdbde1cd78608d13f9a3cd1c59f5d9b62b41b6bff4fb3230184ac40a84f4901ddb78d3dd378ba6940b39bbbfba7013027dfa126ea0a6d28c124ce7cc268deb82557c38948469e19e4a1fb00cb41c35215e4d8907e0f54588f61bc6f01b208347d84ee1e54f0c77ba0ce2a32c12b25a5ba91f693a05edbd8b2de0e62b38d81fefa1d09ca590981eb2714489b035c05c5a6468e8d036b6410a74882ab75fb71cb337137968c58c396a90a51a8690b661e33d3088bfa4a49b120b2c55ef8c8d2386c0d241230ee250bdcce8446b04049de18e4eb47b3b88f7cda48733d9f19c92b2b0a07a5512a83430d0340a389298ce6ccb07b6d27ac413cc4cf0e412f311241db342896532ada2c9b6bf43c86c75e891b45bdb5bbcf1433382a73a82fd89f91e1824339ac3e7eb39e0dc053bdabd6cc1c56f46cae3bbb9d197feb88e10ace164dafbd736e73a4137069bf34e27e28dfcd9b0507975b7a76339a110f11365393611740eb879990b5823ca52576c70b459548a2ba21debb02f7e781bf70853983db0d8ae3f5f8ca050f61b08c0d335ecf807e09178ee5306d13f7f355bbdab4656aa8edfce0a232b0f1d8507ae27438e79f02d2d532fc318667257409e15101cc3141ee3409ac9795646bb7ca1ab62c30160344260cc9469664ad163aa8f8f0b0b1c3926c6d3a6caf482f9b2f46292ce56558c0d1dc1a7dd012c25e460df25edeb868a9fca193173441328613fca8667a579fa607f4622fa43f1cf5ba489992f3b0788ae01259098b1df06a97c8371079e0578f2c12b3ec1297ef63f6376a844074b3ec5b0520df743a85a8239d82f9a72ed6388a7b869c87f210478f4daca50939e2d463732fbc58d36dac4ba5e371f5c7941a6f925961f56a2b3c3a0c5d3c6d7d7964573435b5d3b15fb5805aac2f508e598ffdb33bc7d09cd8bf291dc52c217c82ea969036a517c723ed8182cea8c902cd7ffb8b3d7611c9ae500224c7e7cba10ceea11aed55e3641493c4598282eb96a0e00c3a4c9c90262b0a6e1b1f02250e04ea34a69bfcb65bdf49aa3fb4f58196f330a099731e30d01d32f62441651b019c0894a2157db531b86e2dc512eb3af1c4d97590e944326c9326722364a60e3ea9fd41724843782e2a861b06b927bd5f81257adfc576cd1b18c3ff05a26b35c85e83028cf92d0c38b30b23ce04e37c666a1cf9d93763b1649bedb8a7e5b1b3235536a19c51ffec5f7fbbe26d080334ee076a68f591664f108800cfde21ed3c76708ebbdb7f8c76300a2c3bb6bd7f3f94f36a1856c5ccd4fdb1d6ade76750a15b5246d2aaa8d5c20f828fa75c49db11c64fa90230909d640211c1d4a028e67b9bc12bb9be765114abcea1999f1348a50eded358df3866ec187d380ce8a395cee20300a12585d40c33126c5a1099414f2948b03557545002d4953144801b04d72f49e8186dd5af27e72b71d85014cb7400a28846f17f4dd7e1b516cc69b450342f8425b49b4b659d89762e637fdcd40352a36159886d28e920dff4bf9c84646afb1213fc7f7c78d7ce5ce9789157ef251d52b66958c90029f165abe30903106652bccfb8a3298a74fd81b567ec120469acbf6226eb0e3874891115302a0d946449cad8de9ec4714c97f23fc493586cd489a9780b7bc3637777ceedb5e83977bd7b32ee83028b4e96063b353f5e7690aa6843edfaa5221cd970b27cdca50cd535bbca7b367a1ab196ebd432804b058f83d4be357fb57f403023ff8d2dc7ca8766166b44bcec418d7a91e5134bf351de6110a43d4abbf0ccd9c59b84dc51532defef252b87c3b9e34c90b85f05ab34233123704aad1c442c40692e67e014fc41ec637813d2556c574a1b6d798af94d84511ce1630f6f680c31e3c37737f15cc1f2959097f169aa63ef04a110cf689b8746c6abce0aaefffaf83378ce6700931108d5c620cca16064cc81480641895e14d054a0076a855430f676affe0815453ed688b83a52744a1fd2e158f964f4cdc12e056a11d2aab52e0e5010a3d60b1924898f08bedd80121d2190494ff61008c44f7630c0a6acab3e1f23ba2eb14315c3b803d6fe02ecd609ab568b5e6bb9a887728dfe85e4a4f44ee32b4f8e74df3ee74f4e740ce41553f1be02c46d401d74603787a4c877ba460705ac98044f0b6bb1e46c9f6ff2c04d285867de31c3a86c0a3e80be12a228a0f5389b2396ae679b0154742f558f6c477505de9f4b4f52153c219942c076b8533b218c882f16c7b0e49c23e1700e6c2a5daea865ccd0b0e051642d49148831c1c2475257f8970db1f55776cfd9dc95cb0d925e327773dfb0ea0fdc61cd3e62304c8b4768814bb08ea63e52ae2d3ef97648dd224f01356b522a0296d8ec075bf7f6504cf2879c2d464af2a893843be09baa746db35982e60cf06dea25d30e6c7f2c2f645609899235e899a6029f87148232aba441fbadc1902beac337e88a659c4ca5c1d841f79221c5d8a2493b42a7c667876faf6bd4d551f2c06275d262db1ac9941c57ddda795932830eb6a58d8c54878bb16d4f35b07908c9944dff3593c310d01aa4261a054474f8ec75f33d504ada1d939bb6ae21c2476fc80c458dd2ea8743b7a72ecdf3a3214b12997431e17ef23096fd1a983e0175189c48f61eb335b104d80635352e5cae800af523c8250d4f846c26e04186c31dc1e052cda859a1c7137c3c3fbdd2ffc261ac526168fe373b1576fa7c0ceb5fe2a820f2d86a55c03671cd8ac9e1ce390c5612bff898e738f40456a850f93e5b9bbe0e9ec33cb0522a186a2468a6a23c4911659563c02ad6a8b59fb9ae618042920aa25a9f14166c02196b5fb8811b144b275b77683b2cf12b9d338a4f1c247f1b12f16496a9804ec182220a5a911e29257fb08894c4a65e12ee00db94f4be780208b64aa20416dd1d37f456651d3ce494a38e3e8e5e7f1e5bfd4874dc8e2705cb26cd20def0c13798013ac94056e604d781b2487a8786e13f1222100fed94a0506eabeea0ce080a484fcf370fb4b0a812db87e836ec0ec00ee36841630f10a81b8fb383d1a7588a5c93318335f9ada3057b66b035e11f36b4ffb3add14227db11e1458eb6b4d17955228c2df836a99c684644354754736a3b4bb2df092b95cc594687e7439ab5260a95f8aceb899c3441a1622a1e9eee609b5fc10a4932471b7ccb1b64c841c2324bb44c2bd802f6a8aacabd012f3fe91d0d32a02ded28c46a14c5a73a2118b828b25db5cbfe38d72d07c0653f33c54ab880ab26747bba3b8d98a02518542f2741785fb1a37327c33d1425054a295a86a4f93fd4c8b76180d0fc63668bfe8c34c52f3e4f232a25d0642be3cf62ad9edc12040fd0ffa904a7dccaccfef349e31ffc1f038383e3ffbe42b2175879907bed15ad76c8f662feb88a663b8894c3adc5cc2bf794e68ebfd0692eb5ee479f877d97cb0dd8f3520381170859a946f7535da5eaef45e286b17cd279bd23ff664f041da320c26100b25fa0eafdcfabdf38830884f29dfc41767f887bbcb4b14ad5a4165f29c06a28b810c650af2887d4c957dc766dab6ebb0890c68ad2686c666f691769d0c1d882baddadb14fa380c7c928f6d34084c3dc865954d1c8d134f40ba82c22160cb60bc37487e31ad9264dd00209844fc017ded4435e231024ef721acefa2353a69774ca5de6d8ba7e602342bf308f9c85ad97324423bae282c42a5f8286aa658a8cc1daf07ff63103cb11186e39c8e3392403b5022955d91e7b37f34bfe0a3f838253433760a13a2f38ac6d2a944d6f6fbd7c6f044dc4aef63a05138745cc712b07db79da40ce669067a9cdfedc47f1f8c09c29d6e5948445b25b0057eea8b01e8a6ad76b2991396fd0fac9c1ca5e904d883b361d0519286ba5f92009684f1c7e8a44fa98229609e02ee450f667a2f76489c25145237171d0f0bdaf78a1738385685f408a403fd37c8af086ddb08e1332490e2c06bb338c6d5516b4e13515cc787058ac70589dccd3cbfbcf57e761563c94f90dc7b8b1986995b8cf9ad07bf2457487dbbb55cc58cf5d72d3581e3277c860a7d27d3e61b1e7a3a5028d68ddbe951d062ccd8f5f57568b3d2f73b1dd903489e96aa7bdca1de6d9049a416bea60743f1fb12b7eb9125e6fdfd871f7e1f2ea85811dc523a8f5c6f50a13f8bea7481bda00734068f67a7375c5160e245600072b051a5b7cafd0e6a6b2462de1b1842bc890cd1f9bc8080ea0f8a6ed25c07b2808e3c71101c2cf57c8c903203c9b5ed03b572674793f61122a9872a250707d12954cbb0966438a7684ac789ba2e99ca4b58521574d90190815ec9f36e1901633b6992e175bd9505b1a98e513924a8e4cf2e5dd1ddbc89d5edc1673840f49e19f4053b440849c3e075c13c3cf608acb3cd3e3fc0efd5e66b274b6e0c55b9da1c998488906a4db2c8a1030e7af75ccaa557fb2de9c2453b43346621960fddfc11acdc5fd85835b8238db37eea2c0d6c7ce95df772e0c1d1a01caa4a26802fe2bbf91647f8830cc069a1d1483ea01e2f6f83540f168ca78bb1836fdc94d96269a0135dc748ea89f3c4a8232b2d498a0025e58d378bd2b8575cd7ca830a9d958d3991b4079a14ad93faaec8828c787a2fefbbdbb16c925e0bf17ac8152302af94bf711c34bf483fd88fe87873e9f5fb09ab25704807566c8ae80b187d8119ca42d02b0a946884534bbca8897b1d54358cdd301ebd1beae3c1a452d334e72086bef051e0433a0c85d6504c3dae0e031a918ee0ac10dffd6dab3f903ab7100cf70f41997c7b9ac03670f0d8c327f6172b7290b43569eff185090d35f926b1893d9b8b294f341e589c4d985af0884fc0782d4c82558d5dc3bfeeaba352f24eee7edda4a1717eccf396857672f154df328819b501c28faf33e342ee4859760fbf16c2c9b298b9f63c7e2b6b335310c38d974072f31a0504d372b9ffc1e672ceffeb49cdbde72eb455cad35383842d5e78190c6064eee1a7b22105b078a0431f64f2d1ae75724b32da7ae762f4c2fb172f86c3a511f85d427847f928c178e06f176dadeb3ec33bf25c3a498065249a70cd060f2e885e9ba1dc27d3ac1af197dad3ab279edbe2b46a39f4297a694cad66db4c6911baaf406ec3b3588dcf2ee2bbe8c2f84eea89b0a659c46a2594bdca58aabea1969e29882bb7c4b2b401de32f5e047b9c9bb0ddeb4e4ebc95b342569ca6790ed0f9a688a5471c46c7513b24a951ef6fe01ca8784d967a01acea5f79d4f2d56035cba70ba2656fe3c75cea1132569f3ccb04bac0e7ef48c195c6002914da7efe3a65db98b9b1941d9253b3263f234eb199d77415d2fc44f5007009b781fa1058cb882fa8294f423cab658a805b459023eae196acd49dcab43c1d96d68e45f224499ddc1daa2cb303eeb786da4cc6e804d0c1aa8aadd11da881b41da00fe9f750c2dc2a8c96c072394459d9afb9fe2db5b09cd1594ad6ae008abf13fd662927642be29771aa801c82ef654d62c00afbbd5de967e6ca9ee419c825a461270e5807a4c12b31b80af5bc9d7d4b9412c108c6a6fa18286d8ddbdf6b8a91c1fa662c8f5387452bade18b4bcd162915c295f8c0bba6af163066d9384bfa8c65025165d5cef60f51f5f7730e90f1a46407da5ea39fcf78ff67b6bc8386716445a69a15c7608070c7187f57e945622e636cecf6a4d1916f0eac7aed16e7caa1e6a98f08e90ad7bb4776a2ed25d1593f566a2190fc2658262a0b815453d7649aa7d8b34a0c884ed693bddd946148173ebcd66be6442671adbd456c2c1b68bbb0ee3d5d620be5d38ab3d2fac6928a930275ed1b60314d0420a526711bcb3848a846ca4d744bcbe7bad3dc5ae1a7df4539779058905e3158c3ac6906f4f62599b979932741851b1c68f387e096e541f99a8bf18aef7a85683cfc7b83acf5a798a2daa4b0c400e579c2bbcf569ac453f8ecca0ffc88c7c8bd3a723e4e98fc25fb8d7a5fe10be4c1224dede725d44ec13c993fa96b34a9605f6cbecc5653dc8143f7d3fcc03c1a83242ca71ccce5de83e463a7c51a2280402bf410a92389b0ec48332ab244ea38656648a6b1bd43424e1b820150299574166b8ae53ea58212362105d8e1528047909d4d9680d011048d0251040e759b87d7d5ec2f073afe2fc82037cadec538bd50efc127597d3f3ac43dfeade0256630c7bd612382eeb702306acff4ffa25cbb072062113414c2f4c9ef5626fb7068af1032d02afd007d1ee01d59d6fe1c4a7f4022ab466f5b84d172b30f94b3ff47ff2669efe5583573c1ab59afbfc1000d94337b9163e5d5908c8b14b943aaf7d8c505dfafdc1b93152f6059c012ecec83837b47bea694870f8ffad767f8ee3989222378be82f0218f3876bbe0ae836fe2cc4091dee109391acfaea46a61c05d96535935e5bc35beca27a5788a5f588b522ec952d4e072293273fe171e1d9150a5a8ea8ed5aa4504f08a75f265d90c8812d5b721ae9aa7a597902b496909ab3c9dc3cd27db55adc269717cab23e2bc610bba8011b59ca3ed8a99c94117dc8dbecd3d28953eee33a5efc06bbd419ccd53fe90d0ee09ab0051e7beae39096815dbc45d72c4fd216a2928b0a49158c461ce47278da81e011571d85db24fc25f155b45b384318982913ddcccd0218c39d54a1eef171183b50fb0c477962e72a0adeaeed186f4530fdb2fe22160db0079d802b4d835111463ac3957ff16a8943985876fe036058661227a9fad69440e5f11fd47bff44dc80a7f132201adee560bf923fb86f13736d144f3d2d3dd5e9915520a0dfd453da2c51ff0d13132624f03a743526554bd0bdedd48258f29d904a262b66275141a0ff03ca3c5674dd90477a5b291796e953e9448815b7b40f122b3323932db73e908b2ff0ef2679655b85fc43e5cd69c0551b8ae29f851a8450e86fa42b7ec4ec9783cc91f850d15cf18d5b5421899d12699dfa7be6156adf6b1769738ac3b6a8bd6b4f7100a6ec9280462290b48472cba3e6189136d7d62afc15e92f5a9ffdea41578f7724e80eb891863a4a45cb1a37b53aeb518c6017cda9b546bc585dad01d34dd0b931d90bfecdbf5001dd276ae1afbe6d1bf519e1195f28088e25c7d37dcbfef778050154ee2cd3c5d828e9803fc4d6bc0e7fae79231141796f43b25c86e0b027cec4952844faf0b009d77428c34bc40c683c443428421bff4fb0a349293e4b8375c418ec42daa850ff11e3c5b8fab05583c9edd53dfa2a7087651bfcf675f4a2dfc422b23870fba6c694509f3d6baef5b7e7d2ee1f18fb2417494da50fb34e5ce329e340b391588004488259ea5ae6f89c07b9279d281348ff8c4ca112c608165043aa1e18f0a7b932be77668f7ebb922634f242950221845a90e1d5f690046887b717b62ea43f5d1008052917888007e68739f4ab47e2671e2003e7d90f1587d7987a6f9c064475044fafbae7965119e7131767bc59988b709052b0f62fd4ad4ff76a7b5c2e1041b789afb9c116f7033cb36f9257ced4ce4e2181f9ac48eadaa694b5a5f6e8c2e3a05a7370bfc644a65d4c49138cd8c9492b0c0a067d516c79773410faa7354bc15d24acf1a18f4e59f07730ced5bfa8b1f58715e29fdac20b341af796a5f64d08ed527ccb640084af3e397d49011448cff960dd5be91910c12dee1f6493607827a69dbcc206d4c9d9297e633b813c6ba032349e99d84948b32fec41ccf20be0d5c0be56c7bdff859c252803027f4f18db81c52b60a0f1693fbe9e23efcf0dbdddcefb8cc405ce7b2880695e905302f726d8e91e94b711e2eddefad0605a9f146620e46a20d52285161f307829c5390146f04bd3572228b33a01b9f2b3879176eed2d9c58d5c470704e8e4864a3b08fc7a8f60e1348414140549123c3133e3467df0d558bcf5a5b884b16ee71e6f693bf60425e726d4819764deff9b628a63d05b0341e7fbc22a64d82ee98db6ecbd085302a8b1d63443a2c290246938eea2cd54dae5f219eb614901043f7db806e87eaf26c102e0c3d90ef73ab192edeef6b14dd0705bff5fa8ddc577321f816450c9a07aac3b2675b81af878e640bd4968817e8f82b25d8486c671feceeaced885322550de0dfa801fcc727cceac73d296dec602ddfab1ac15ff5a75687408fe710286e88ed27f82ebc90bb9fc0fd2b0b3e992f11b1a6665e4d05497a6e539e25b3ab670ac5ec317b0e2da203124120f960c434ffa87321fff19a2d24e76ea75a5ed2e4de819f450d5d3a481897be35d492b1f0549b55128a237814a29cf3c851c0630562d111e424b65f6a6982825665d147a91ce45531aa11e49b77c3a0c579b27da9fc7ff0ddd9620cbef56028eed380e5250a1864f89bbb07376590a2e0bd9e6453ce00b6d42b7ac2e66e115bafbb5cfbb0511374aa1a9a127ff23039ed60d89195cb05eaa44695e19de40378f97d5383bb9920cac9b1ce0f181ce5ef5d49d14024aeb8c56c54db593f3ba0b86751b56dab1784d83eea7bc660a2f0c939f6bd4733b1bb86b8c7f3cc836437e5424f01cd370416255c88cb2ed46f5de8f44747e04d94f2a077be2a321d0388d1a7da44c957f45d35862de7c48f1569e8358ff313b0b42b27fd7f62a9cd09d92603cc924f740c58518fb3f4f9aefc8ee1bd83019ebd4d46429dae9474b528c2c94aecd8943ada6fe5342a98eeab23a0d459c0a47c3181b905be8973a445c77c2b386028a42131d2002b2457999278ec182482a52b72b4e9658a31e7b82274872f4c238afa2a4c66c6528389c0077878dc396e10c9c5e61c835dbd7e739c0090e5c9274bff7d7f5260fe27c53b3986552d64868bfd96a5af039c5378186d0d4ae86553101225b9c38e630d53ba30ac6b5215d5ef8e459a0b53723c3cedc8d290719f4b1ae10b7c8b9dac72472cb1b129702a0d1418e31cb103241177e11438ab9e6d061078db1abcc045f4cd51c0d1040ad3533fb7899edf6ae20afc160e8254188221c937b6166ed94f1e796f3620801e6bde8abac57516efaaa0495c4440bfcc81b05e4040e5158ea95bcbeb90dacca9b4c9b2ca7899fb40d67f09750acb8b39c7ae2ecc0daa3a78e89fac4fff9d779ee538a3a6f03da04193153e904a41e838c03cd60299d3d9a297fe1baa03dccc4ea5d6c3a9f6c395be653056623183ede22e3755a1c3a706c9966e3b3ea596246c6c3fd4f35f0f343a2480697aa3f6f0b79ae605370754ae28a850dccb359c36970cea0d8ef2fccff5d29986c6f8095fc68d055c3e8fcc517a4d6c0a46962e8b0a9d47525ea951e4b10a4ad7d269a5b22c0af7fc037a1d968448b71c5b77671e32872317606618f380b3a7a3f8c2cfb3ee7848e354a09750adee11a23703c138567fce766a74879a716cc520302532c4e60882d04a02bc3336c03868c97dcccf6a9c915859894b6b5083b8d1465625b074a92d27a89c8c9a5e4cc0d4fdfd23f8c6a6b2be8fc08c6148711db919d7bd0d20294214f4f93f826e7f06bf5ec209ba134afa25e0a8fc5067bba35f31b3e31057a2dabf0c47d43b1157d6a13339d40059df9bbfba48c61be010674183918d6433f7a7389cef21984aef684a90724791b0c4d9a4f0d428fc3a753920971adc5188885113c689388be4d315fc4c55182407cecd3446264a5499efc17e542e18526091cc9e792497f932cb8f6f664ee3bf81ae2b0c6d44d58201d0d099db14ee2506207fe0c4900dce8a01323b29877cf862e92b408cbc1b3d91430fd28470c26c70e59309c1e9f90d5ce5aba7c061db49b208440e6a02b7b1edb523d573cf6175c039b85964db9ea2e0eebd74bbd9089c9d926a5b1538c31fb998c4ef318147d4b7c4ef482bfa97c1e46c5bd7e4a41d866719a7e10ddfd0defe6f14bbfa62cc69e0f001e008e952cb00aaef3a87f3ab2526a135dad3e8b583b606c800e4ee6b63c5e0c1e616cbf834a86331259dab09a589d23c7438f4762e4b2d2045d087443cfa8a84038a9afbd80550f2fcce72a8d446a4e77101854284ba997411675440c623daa1ce27e9f04fda0eaf0fcd28c724685bf7cda67ad8ededa7fb3ae6c5d610a1e7cd488f8d06f06786d2d5dbd44a48f95b75f9d8472035f0d94b00f5f2674720eaf1719f406ae0638f80eae1ef0e81d4e1d3be02938169ee0bc01f411d3b4d3e950388b97f3c40c23902f057b880659d4c625481d5cb07928cc7e3d1ac713b608f404046e405e805b5bbc0f5f0b18fc024c36681a30376083432d50c71318755bd2521678cf60f4d04c21bf4a90c78e4772c2bf469b4e7965f3b2278e3a5f1f2729ba5d6124e69ecd94b512bfd8428463774d4e21b8e19002283ce5db0e79abce1ba8c07204e4a3c8f04092f759f89897e2bdf8622d95ea993137d0c0fbae868761edcc4d546ab7b48b32104da4a50db0c6b5bbc7107a1b616d256c30065bca850d8b608b16de93507c2ee1e82a180d38fee36606cf68b93ea0ffee6cb542ab0de5d5608363bcbc6857ec7ac09a40ed2db34b1b4752cd33afa1535e80862266afdf26a2da00096f3fc2099d2bb570e80b3e79e05f7d9970c1836d8526a18b42ee62744dc9fa6f3af91c8400f156018302f669d851cc1ff5f6adae0178a2d60882c4fe1989e2052a640df69820ef6cf9c7be0f8eef67409d9f31c8e262b4defa187394ee99095681594e95b04e823d208f85cbe3d6ca5e887a3e14a4cb7ad7ca4a70a5c972e6e0f8bf7630a76c41fca7c7bd0f57a8fe8d08016b16a06538229bb8fd7b4d33911761e2a1624454297d5cf2b8f6ab617f06c12b98f67b8a045f338a9f419b08b0045a52ff426b2c4edf32a2359397d5e1a99e53b48f84272132d93ff30785c457a850a480b6bd0d32fc3cb085746533f27d9b2c49d8689a6e624ed7b7d115f3dfbb444da13ebad3770024d1e0816d2bd0d3d0b18b76676b940bd95a8e3aec1c8f752c59b53efc317cc41394e0ebbb3126d5ecadacb4eb6e308841669485d988ec8c5a357ad8eaaff722a0e5af468b911568f0dc982f0540b34464778472998a09c12a80b4318261a445f66e0ea23a16dceac3fac37efed2919f91aae0d5f07f664085f5f635a875e115140daa1a6191c7c8b35eb88ced2cc3e717d1865831dc93b3fe796d55686cc2cd1ade82f8281269cbc0d1254146fe2e14160de452d7c155de910a1c9d96026fe0060ef3b4dba77520ab59b0422eabfc3ed1fe740647e87e6e0417321c303fb1886d664a4405d1c260ff29255b26824a51003610d68e00ea96670982aa22e00ffc45b76f39d390f2a800802c21caa1b36c52202bed826b3e23c7be8c8ff042557140fb73d82a6bedf06f54531c08a38e804e88e45cf01237a45c3f10ae991318880ea458fc2cccae5fc669b0f3c3dfed5e79c8878a14efffce582afb2c488828db70c3c2729930a11e5f93b0e4d459b7f7bb07cf16cb93830e0df89776b803973253597ea3058f2a56443bfbb15dcd50807004acd4a1579a372e459f91efde0c4d1b482bed8f29874aab0289ff84590a295851c2a18adc25e3e0743d2b6ae59f640313f4b9e59f618fc59e30f50cf8f00dc20ecbf04a0eb929c1f88cd82005e440bd43d058174c501f100c9261b294016dc51c78b14fefd421663559b772b10db32762866b9a0143eec5e055197299e22310cb45269f68c360b22b368d2733ed1a173b4f17e8fd02d7b15565462219f90a4dba08a399631fb60bb60af58415122877ed199e8511803095f79f3a8433d33b917d2e981628530ef90e96ca2e0c893a6a07776a7a163bb6e652e8c6a42cdce43cdbb5c70b813b9891e8ae315c35d2895388af0fdfb222a3c6359eaa513faa2aac7d5b29f356bcef85288d5181db0a79addd81ba8f96da9d6af59f8c5b33fa2d9f3232f72ac7084f27b09919ddab3332cecfdebd5d662deb4a03d8aa8f4a60073b453365b597516d3bdc10319516bf27d32db215aa3024fa403e1169035fc447372ec1b57ba4687c096ea7954675c95d404d840802f371f2476f02f408373ed6374087c3b047afa36945930d81da5034547c684f9f8c9a995816ad70acc8e3535e10ce19418ca874817958fe5439a81c23941b0bac81edc3c41004378ace987a3c882ab29d84854f50f7cc10e8b21ff926be2d0e1ac91922f09d97bef2da59452a62403fd08bf083e0942f5796b4aa9a3a0aa619cdb6730817dcdd5e8cd04d819655f69b1cc50d3d12e999999995f9741e95146869e9a42faa6b3724ee965c846ce912adec78f240f1d48dfd087453f4c810ffd0253425884a4e8a19f8a8038f2b0050fbd7828a5941a0ff8c5cb47242525451c78c1b3c3238c686490007ba82c16d2c4e7b86eeef9a1f3cc7ec824b0d1a2b25818c58a3be83f41df057d0ff4ea19a53473be42acf4c1db0149cb298d157b7c0456282dac3adca8d77e53de6a7bcb4ba51b9d7fa9f4b260532517fa1296b85aad56ab8db48aee249fed872d4f94565e481fc373e1421763b55aad562bba4455926535bc86892712a9e42698cd0af952c6c5904c8b9d56cb2bd25243ddf1e18668f169e88e4269d1526bd09a36611b9dbfe590643592935657b029920b3d09d28cce6975e0e54f372c10ce3aebac33e6a1d74a9da7c79f37355de8833407fd358cbcf6f4702410840b8daec651103e98670326de23730a4a62d45149b132bb1d9df3b2badb7f6e44a2bcb656081f8e4fabda6b68553bcde9bdac7b44a8ee11559de65452a4172cb33cbe1b514bac75fa841b65e9cc506af17072ec484426554a58aeb93e410821bc9913327b5914831e8439e3789f37309438a27b6064b6566be7ed9c571e04e204b1aabd7dca66261be01a4a58e6a199042a44577490e5dd892327b07cd7400c8b9d11e958dd2c17fa09bd6640e817407d66272fba8c7998a3841a4f046abe3ce88dff3e6b9cb8c3be447dbfdcdab28ef17616152c3f8c31529e71a2feda7ae20dd067dde253598e6d96cf5bce7e693d4a5f2bd37917106fb4f103597c744c76b7a7a6437f0b98b72b111f7d0742df5070f9f7ef79a45e7331361120eaefd91499de1eb505f4023aa122f1dfb349554fd50b7fe8d71f7a43aa07f7313e1b762ec22e30ea554afa66f49fd24ba93d3365962d2bb367feac140f6f920252faf285741f54617e6d9957f80566bc617c7fb8b32d7ee5c16e6caeb7023cb1506a8c6dffca50d5d210a5946e693c11be94f5651871ea34d478a263de96f3d6454a2fcd2d8f9dcd1a1fe348f1805fbe5c995f9bb6fdd02f5fbe78817fb9757fe0e49fdad64f3dbb71ab0580627fe0972f3236fbe53af0a20ec4d5ba66166bd77f718dcbd975e0253de82394fd71da59840f1b45d8b071d448a03c196ffc2ae34d1173bca91fd11f144c856e7fdb11b69b237c45df7dab15b1ba65d552146c97653f4f9cbddb3ec51827f4d9ed32468ed10a4692e82c573a4b67814882f09d9a29b7131664c53fefad834fbebdd403eb039f30a31e73845d7c1ff8e4dfadcd3d8ecd59645d2041940489d8d11f12578c20e20829da0f47645220a1354142fb0012d916249080c40db6c876a05119ca62a0313982e3e4c1326a57c8a6d09ac8a2683e1cf194f090d0c11e4fb1ef0a9196a75e136f268f866d7d60a3f74ca7de4d7fb0a3b76ba75edf6aa75e2f4fe7d9dd2abf60422fc617e1f50163eeee6e5ec1ca0111a8077befbd07018c1b729378a8ba75701f9f0d8c35efd6486e12741db0b3c4a07fcfa66f4a47fd1795603f9a7b523e8a153cfd5bbc0ebb9d4fcd476fc8fdded6cecc5770ef31f30f00706e61f3f8298f2f6bcc3d799cd1d237345ca4bc1d1ec9fa0675234386c77077bfde420bbe05c06bdcdbb91258196f2dcdc978c959c64f2edc4ee85d70669f310306ef0c1083773370dea954de6d3278274300dec1bcf4ce7ae91dff0ca7719b8d9f8a9dc6f69e4a00db7b2a1936d5c6ce6dec31ccf0ec3018e01900ae035dd85298c6430380ebf3a0f85467291de9c0874fdd2c5e66e83b3954b3ad9a93f117361bcdc9f8dd5e0bdb824be7165af0a7f1b470659c06d59c8c6301d890c0e041f80e0c170afb0ce9db866d3436796b666c9dfd19b505efb294bb70bb191ec3e5d6d997a9540b72866fee33626c1728bebbbb8ceb340d8d4be9820b00f016dc85db4947f98c198e518da785ac851900d85e73333ce53a906693e11a0f2a757db297e1dbc523e3d6e6645ca7515bfc185b7c1927aa452fd3462fb3e4655cc6498df2327208e2e43c958c6fc931c1c8b8354215591997b1642c4bc62d193faa32323e232323e358142743d99297f14c46e6c9c8b8e63297fd24a19bb64e1a75b2e8dbedd675cd25a7a57373a95a8f3e7a0244ac475e689b04a753830da7676424436626064a062ac68c4cd12926276b0213292875ec226dc75f2bdd24fcda32a3dbfbf9b219f615f57a171191471f8016db5d445c89a8544964f99cd5512d90439767ded5e85209c4215de9644a4b27d809c7e475757a5ad0474679c844ff5e805ab087634f1e5fbb9dd06b4ea36f601c2563462ae6c6e4a692474f6d59abaccf38bd7519d651d63ae969799277f1491e838467cf8640a97876ec49412f67b76c9ebd3e95939c743be9321ee3c667c3aed327d781339bc9652779624ed7e7697993f5a8f5b0b868328337c5986e6dce3a8de6acbdd24b1bc9bb9ca78e6d1df7d4afad433dedfcc84c4b1f38dbd367cf167b5c37d796b3bf9bd7c23a0f6b3e843ee3d2f69ae3b62eabb177e3c2489abdb28ddf92f835ec2a61d7bb61bd1b98979ce45956d27cdc7f6c7bcfe67a36efd9c4674d07f509afcf496a3d0a90022b4d88b34ab527c8d90e2bb71f1a1c6763390dfca97936b239f8ae3871f472fbc1696ba3d9fcd9407fda47a6415198e9964ba7cede4deaf2889746fcd3a22ce225933a37b982972af8a7d149c47220f83cd6fda1df63b9757ba4d78faed3ed95d61def7fe84f2f027fde4ede1f5a047e6c82bbbbb9f987e415c25ed9c8e91bcde496c7daadbe544f3233ceb72b79e631b6994d663b6db0e424d78176d3bc94ad5e8b9a966382d9607da059faf2e54ba963153472cfbda4f9abe6af9babb3d985ddae66a3691553bfdcb228cd5e0b8b39f5ebc23094f4d82e7db86fce1a36e98e35c3312f1bb90650fc8e09f8f2e50b13ee1bd8d95f379e7db4a21579607703e1fc9039c63527395b1ebd6e1206c6544dd5544d25af1acc46728d47bb40f249ce91f86324bbccf9aef107ecd5607276cd01a9b4deb368d8c0b8c9352f79392eec866a95086c6cee543fb38f68c8b32ce368798c7d332fd9cd39abdff74fb0d3dfd3f8f159a33df8cb972ff302750ff8e5cb972fcfcd0ea37840c910d40794f8a08682704218713f561e88e2b3d279444740530315d83c40e4b3054521b6349010ffc011200cd8e0890fedc5c4352cd73082651c3c836bc0d40f8de21a46404d417483005abdb6016660a3773927ea1dd7a0d2a4fa93b789e53911858238f54604548fb709757a1f070c30039885d481935ccc497eeec06e6daed6e99c246b6d3a8661da8dccbb6a7186553ae5914a2754c2b5e5638c2d5cbe9f8d31468731cbf30a0b35834a1a4b63e9eeee2eea1b2b7d63d438995fde58fa86faf5ae6b4b27ad601b79c09f91a8b9588ae2e3163f8629f6cae2d27271d11797e6daa378e99db4a232517d5d52dba5588e617435e54227ad74cb4adbed12ab3f2efa65593ca365bd28aabb3fce728b5df459425cfe001e0d65590362f4ccfee8f272abe918391eead2de84cd9aeb25b161107238dc5e15ba44ef72ecd0379d8def14bc3afa618ec611400d7d0324160db5f0ed43da7da08c3672a2ef881f69340e8d74d286044af4207c8794016dfca48da6699036648cb78b3c7c65fa1a6f426a0f0f63760305b8c6c62e3709708de850e341011e846035cbfcd4374dac6b1ead6b9dd2d19f3966a9d7e68624ea762bd5a8dd505f3a16372112dbf8ad90846a18f6f8415275afe16ce4b48a63246daf6d5c1bcd71ffe56273bc0d2c7b737d03c4cf239dfdf22262d0217307d498e6c5c60f352d73ebaa1702750f186f84551ae53b7c04fad804f38e878f414de2ed284a0a281ace628c357fa1681090c45fdfd44600e6966571adea586fcd127bb2343621e396f117efe596e672905e6fb7c390197e805b8a077773c617f6f2a7a530a9c925720bf64586a5bebc7609167b798729b15867bf3b3b041fbb9d4d812cfacbda28133e2c318f9befc656885392291eb083ce4fbe5cb26428c9f717167a433b0455ed244a0a1ed796c9b74f991faf5df26cb058e81da6a4d6c01955b04c842921cadaaaa9234427666666ceb6e474998590e89fc6d3a1ee95d2fde5a539723e2ca58ed41d9a6b471dc9257c80e46bfe5d99228c6fe9c3bf16f6c15ee2d9408f81c2769d648987de496a65e9bdc9e0a1d3d9f27d213ae2e8231791514983514925cbcca45aabf5c62eeb9636d24629bd8f560a19661a51b6590821845c7b4a1a548f1cb7ea1bcbe2da391addd9b081f1f06ef40ff169ae25d09c909ad5e785394873ed4127eb9de5c17a598a9e9de32cab524a8f2ff49c03e55637941335a09afbc1421bd8cec6f79c2ef472c3a4dc80faad4b00ae31bd4a1b7d13bde1edb8671d44efba28bc3c879f1ccfa6739aeb326d6ace1e69645b0e3f369e4daf9e8d2635b9a53429a5a65dce51c6dbfd50abfa8bdcc52cc777e63a9dc373e817ec2e07e8977643fc1ff831db72b04723879ff76cbad55f573a7bc749785d324a282fbeaa3b945e044222115e69dd538c1003414683136cc77134563672fab2e617970eabee4e2f662754664fb5ae5eeab0fa1e78aa1008588cb7db210ef9c8458c11c63829d163357314c42979ada7a7b29cb4595ae6f3b45ab5af9642440b63a42fce5a835ea4300b139bd745ba48f572d29d53d2984d76926b7cb9d571ab9945d1780d95934e3a9fe4d3147c3adcb28661587ee622f6895407b2ffb846e75c699a9b7ef96535e328629f083b907dc6c1d6b906ece7bb6ac2428ff4665a5e673f70e2e8b108618c50b0fcb3a5e487e506fa03aa007b8e392677488d873a73c7eeb37a1d56dfc377c673617d564fbdf359753e2b4d87e5d5a7c643a3ebd093f90aae36193016e6312698cdedc6d7f61e3b7a391ddbaa55371d56f7915a7680e8eb4b4b6ba55ffc98ab269675638c3027ae886a0d1b581f70562259524622c1c06cf10ab17ac966505e1ba5902b8c85c9604830303a6d49b262985f8eb1b5d67748b72ef45989b475414ff24cf3a791849e4400ae8179b675f1a95f9be575eb3ca32d68a287b05a3c58165d3d8107585f16dab59487ee99a50ea471503fe66c2972c651eddb519a838e4a8165ef38890e6c1fb0d2ab610e8a5dd0f700f5cf1ba2cfdb6540e83d6a3c56fc193b20cafe9e2d8c728a87e51763f4b230aaf1483adff4d9d5140fa7737af5e9357ae481291e74def9d99cb2aaf9abdaa08fb7bbba60714ac09a2ebc352deeb3cca2b62a6994452de7e8c2d72b73102950432f62d574489f5a4d73d375203bb3ad692e42d85c3efa23428d291f6c67b35aad45a12037e31776d5c51a57cf393f3a6badb5f04a1963ca879a835ac2dba21f2bbd2eed3eafcb84475d6d2d93b7c4eaedf2c7852f5fbc3cec8480f8db75785dbe87bd99c05e0dd6d1896dd4314d5a3e2d9fcd1abcbaad63886d9977d4b11ff83d1cd92fbf2a46338a39bd1d86f9d3608feb7b4ccba7c7ad56fab3359ecac3833a9d92533d64cf7b8c66ac65f4098fdba9037a085752c86c0c29b0d43b145bb57a76c5eb29b02ba82443c9c076281d1416a7ead9d78ac262e660e66b838f1a749a6b14caddb2965b164a06965d566ef6a47848ff796b220d16b6a390c56ad4d9a30ae1500c22389252a2a1a9a1f1c2b22a952bb8e87611a1943a466f4ff5fa985b971781179bae557f5fb942cb67bddd35a9452dafd5b24818e64fab96e5438858d78665d1c8216d397a6658d1e0bc5a955e6e6d296a5d56b5e8e5f5a957cbbadd4569dcf2ecf2eaecd4a5771c766d29ac5e7cd58d48add5af7a5d8af9758bc0bf3cd60c0a4b3bcb7f28e74560e4a744b07b5d3e9d2757873e6fac570eecd76a6fc76feb5bd73a56ae103206b759d9eb57be3fb408dc615da7df6b1bc786710853852cc72684d077c820933f8d859e09c035aa471d685d9d2615f9a1cf45a4ffd0677fda02a417818f751a80d9ed32874e37223ff4996fc72387b0438716eca90f7d08913ef2f2fec05bd31ca4e17ab667d33ee4c7699a6be8ae79878a3225194e1821116676c9b7677a7d49dd23f422f0e1ede6fda14f5dde22d0c6275d4b31ca73bd4ec3dad28a168451b243787facb7a6eb34dc76bc2f12ef0ffd2943126c874211f9e1e17b80a006e0d7dbfdd08fceac82c2c270b6040a9d32d56175fc984fcdf3750cdb7aaa63ce5b4f75ed1549766deb01e2d7e13d633e25bf0e352f6f4ff41a312d11db3a9882febe487f0f8d57cdeacd63ac49c75c46b7960ad6cd914ac5606b3c50c6ac9f7f78b6cfd36fd2e1b3f979cf46e2bc1dced8e1f6b408c563855a58fe1adbcc9b21fab59db84674de6c735cddba19cf9b3517a1e0d8f524630486710c4f2c5fe6582a4d1aaa45c6596336743a1d597a3a1dd59a552734345a3219b0b09dd5faaa2858f6ec3ddb6d512e0316258e2d4c9b981942860c19be7eec3a6884209fba48e24a144faca755cde9d6fe4ad0b148b7ce8544b8c1941c6e422f61aefb2209487299571baa5dee9e5a547a0c94783896df9a459f37c557a7b9e8d0239016ec3302d23fba1a9ad38dd7baab8f37d6396b626c08022927a8b951e2db08aa623be11a918bef4e158993f8432bf13136e6c403b1f33fad0765243df0c6479e4df40144b1535ad15ede0d69d437d487b82ce142096791650a22fc1d1fe13e0271da49aba2d76a6d971e4e965def6278e78277317cacf111e76302a6f8a85deb4646ecd788eb6285a33f8e11883e5dc0bc4d8c641e04f320287cb3ec0e7976acfaa07e5eac5a61606fa7877c797b8816c1b3893e274e012ccd47b7e1e4638e5682e4bb2cbb4214dff947205c61ca2a674a16c192bee93a09117d53b97cfce2bb2cf24e9dbf23be14c1bb31ffc80094b0fcb41d306681ddd0a303fc540fe8d429e7f0f39e8df5e2d6e510270f6c1af434d2ed073e9d597339fcbc3ab7947c9b54c1a9b90729ece853ca22f0630a30fed9b996b90d82ed5e91aa06d6068853d32ae815c29aa21ae883005aec8f6b4373d04d4358e87d83c16bba35776077ce1b9dad6da30f422b9e7cb9334ca619f674727b5f73333144a03cc665646464c89021c385de64f299ad8b3fe328940c136a8b11e3a84de86d8c189f89711d28b3c5e64c7edaec6b6e46fc8e864c4b1860af26c6c75df1317a8fa78a0edd06888301208ea870faee6db18e8187935d56c2aae8288d54a9a0d987858ff7b10a888f9f3961fdb508d1816f0392cf2b7838ab167c9c45eaa3e0b112c681793cc147e712bc1bd2a30fe3c02ebf1ad3651c009ee28d3b4459e9ab8f9bfe6a3f479102f66afcd17808a5b4621882ec7c6ade3a8c4b0f027164b8742110e7edc0780c970ed437302e3dc6b6b221c3b4cdb8c9636262502894ef788f72a18fb175fe31fcb475f54f3eb375fc336eb74e480cb76636197f9a157abb01c997b9f009ae61f2d3d6359637b90e8cd96273f6bee6ecd56919dbdbb15ef2d738a4cc3accad34dedee94cdab4ad43bd3f76d5534b20b05713a4566b51287fafe39e049a73769415de3b95005caac02008597ad004086014edb4e7401ca10e1c018a2caa18b184125eb0d3ee5c4d3341cac69ca9a0d8e75837cce08b3188216c62a5c84912488e1c65a1326509115cb6185979e22409922c8e88a83059d2858b156f4583bbeb1b5979e22422c9e288880a5cd2858b1546519ef4105ab2888268c84b978745f5c4f2553d813658c2082324ad9ecd738ce7834d250907d8f4e773e36f4c05c54ec7981bc298e2218336cce7567bc29f30e218c235e6543db130559c2089b75f7dda308471d4700d681fc7d6308e1ab84687aaff1836a9ebe1edf5296d3b3407bb06c6f15e8dcba1a5dd78bbdaf50760898dfe44f87d523dec53b77fa9c01ee3b01e4ef5e79771487fa8ad4215df788da7e2b877a39f0d7be737f0318ee6faa6e34fbf2ffd710dea5c4347cf6a88150d1a1cb75ac9a39a2c3b9d6868dc6b6a7080bd392dbe18e38c352671892be7782f8ad98d04f66aba6af4d1bd183d01fe5df152c4b7f6c3a3b90e7ed2d37e78aaebe0bf5ca7a193beb1cef5cd29c65f6518933fbf00c5f2ac3764f3706aa5c11ec08914ffea141f291688538fb42a7aad16552996228853ab51c5528fa2d728760a4704a3d76a2d0ae5ce71ab15e7c42410c706a88a5e95402dd1eb96e8b00846471d25c1f29c043a81388cea121d2681385c0455d1ab12bba46e19d2d19f6345ada56ffaa37751e358446dc4c5ca4730a210c330e2bb3e02c38aef2d1fbb4441f45d7b39f2d1ea97bea35f46242c8c631e9d37a2d89b9c424552692e66692c6d853dfeb415cfa68d78fbd9da4a7346b4ad741185b1dcaf8efa84263649c93065c2e466391129a5cb48192aed2c738bc0afaed35b173981384d459d420b14129c7cec217ca4599a8b1abc140b018840b17c74194c626e5b793671c84ad3963d9b1943567a578fc8279b4ca23929abac9249cca25531c6245a153d72918b9c951863f42755ca472a1f9d93409c88c409c48159c0285a159d098a888f71082f282e960b2756aa1545593e72d15bd9daa8ad889e6d9da58f9a8b1049ab3a8b5645a7322465c993249c098a080b87d0f2ed0505a3c31bbb28c608dbc9291f637c37ea7b471d2dcbf21dd2ad2bf4fc1d14e4b9b72af4f052806bd098f7c56ae21957d0a8e1381a3466f3518ea7313bd1d4d0b091936323478ed99da469688a48203967cc4e994663b5aaa139d9b0218566333c32990b58f9638cb05d73692e9ad55d9a6b7fffbcbd34d7f53617194c6c577ba64b49855f97ec2dcbe22e976318b317e612c5bef72e759f2c760eb05743dda6188079e611b85c738c31ecc23c73202ed75c28c58092678e038c6b9e6240c9330702e39abf177aa8ede02f37956200c9330762720d0892670e84a9244b37c538986e4a9248971b29992eace418c92f17da79da0f24a51b040548c949de8067c49226e9bea6c172cd332d03e2cd3be940b1dba1b9e740d1da7ab85573cf6b4b2add5c8402a47a57ac6e1d05aa6275d25c8c31d6682533338cd397b783891e63ecd8b18da090c39d9333856433179aa727a9038d0f8d90645662ce9a1a771d381f2e0ac9662bd89c6c5d580e49cbb0ab557d3beaf31699dff39c1d687ef7408564b710446e04f3624ece0981aa76e729cd71ed766327f6fd3fc83c44801c58a672594a732614d88ea57cc79864eef57945884d037c36cfe60d400a22fc3f300b475e7a10be4343f5f7b67ae9111dfca91edfc39107aa4220a92d07e87b457d0262ed2a454f5b8a5e8f71ea3137080a8de1311e3483c16c296a32b98c0331b98c5397b94150e80502e3a81b14c3613c6806e3cc870c4551193486d34b46a373f35183146bc3db92a22eb433b3a5e8250264c6631069c0dbf9297903de8e8cc7b841414f68670758a9bf1dd47d3b311ccedcd48f189e2add06bc1dde99f1140ebc33e3bc3373573b32423b257fcdfd8037d6d037959a766ae89bfad06d80ee0336b1b72f23cc245bc74af8518b52f7a9378bdb12eaab91313ad6ddb7ebad5b75abd748248be29e0455a4db699e79cc666acd310dbbbaafcbaa4daf0e94f2631bbdc70ed47a650ffe534b1abad8eef4d1461aba58769d3e62c1a3a149fdc035dae376718d769411023530300c352714f3ed1ef702db57078421861862e048ea843ed290a1656049ea8462e0349ec88cca2c43b8951e13db570601d0b0190053ce33199890a25101a00595ca0557a954db0c954a860a156346a56a620bc78f31ca88a37144c95b68a68a82854f37aec90b494c8e9571c87fde44ec4209fce7278bc92b440a734bec1efa1907f36b028e10b600e34c9f976fd8bbb7c5f7ed1a9ab08ce341a38b05d873ac998b8c119eb032587a4a73dcc34fa2ce57b4bbbb6f0f907cc83dcf6146fdcd1bdb653fa87adf545a8a14db7114b62b884ad2423c1c16e2a9d895a084273c610a5388b2e224cf86ad7d37780630cf07cffeee0e9ebddb9e71c0bc847d4c3484b5bf1a1fa727d8e7f4dde09ecdc361176678ceeffd8c12214c89e1d9b0c75461bb181e7c0d638c8e9e61dbc0ee83fd07109c3c07a9918175e13d08c47126aea0e2045700a1e58b1df6defa87281818bb8fe6deeba8b6b0a729a72eac9ba0609f5779cecfddebd2bd29d827b043e0e1d4a76a97374ee1be2276c263f9eec524c176f5ebbb5161647c63d1a1b8e8d98485c7b3692560f1dd41b0a10afbde632b3e30c45bfee1988ef08084955c098214a3243a030215df71122044f96628a46fbad2bb62a4e5db815c11c291ef9a85ef5e16a526d65710c7e67811a4c2446fcbe39824d81f46b0df6bce798becc24a51152060520508963c7b169f5f10ecd574f0e8290c2fb0a59b9a5ebad64b8e83f5d235624473eb38c090ae4395a6f90aaa34d7dc86bee9c9fc72cdeb90cf1edfe95bcff4cb2fc7665f28ede775350b92dce438941c5e23d04b6eba46a01889912102fb9abbdc74991c8776ef8b83c9dbfb1a816e4a5ddea61b04e5ea1b044d759a4ca67bc11be34048de315ef2989bba4cd331b8913c662bb9a92174230e7f3ca326ac1c5e1d931b3102af1093778f68cb0eef080a7d4df76d6914c790097402a560130e01e79cf3baafb946d29ce6d956da529897fced903648721cba9dd46e24c6a1bf9d9ea26d1da53929cdd1f7efed5492e36072375d12e93acf982d35a7974e6eba41253f79500ce6abe827d28db9316ee26ba4745333c681408fb9a9e9262f797b8cc94b0e24c6491bf498ad9db4ad4c393728084a03de0e12235080c47869c68dc45c3e0255d44b1ec34f97a34015f5287ef218ef384a13625c5672040a33b7747908aa2895b9ed04aaa81b31b991d26522ef78a80931978da08ad29c151bfd203592e6a893b6ae152a699c47c55164f2d45f07a3e0a32a4c6816850a2db40a2bdfd54c79fa1d249ad42b363b7894440b932d48ba4b9423433049dfa4f8a839ead4ab172c34162fdf3517617cd75cc0f8aee6aff01d44f2d46720c23a7538049d40257447fc0995b48a3aa5d49b7a3bc148dbdb814afac622a23ac704da1d45df644eab536f2ba48d77da8ae72d5045bd359e1f0f522dcd51bf91847d4ff252c75a9e7adfd404d25ebaa9497220252791bc7481984c770555acedc06ec773c34a5bea72d3752f2fdd2028d7055272d30d7a410faa48294b731845df748de5a953cb824ae01226d46112d049e34491421d4ee91bfad4211548d437458dc33bd42ded6a2c3289ebbaae1e9857cf1c7bd8cc9e765da9f1f464fe7e6e5de635cb5cf339273631ec769957efb2b75cdb7a7e82befafb8c479c3fd0aa1a76bb218fe2cf7c504ff3d3aaec50c5cdc4a95378441b004c8c705605783835580f4786caec69652327c6082da4d002091158384204494a5083226c9162092d17135e78ed92440a09305b0a83060d9d2123c18327654778045389a9e8fd587d365d359e212b0326b480354a1624bd02db92194d2937a081155d5890840a2e44c0326484225cb10112531031f18212133580410eb164890d86380211484084f7c587151461480840107951c21639aa1598c062664e481a0c2173828923488d09d70f55ae1d500182c833c8d981fd2113c177ce0eae900a42b085102c400108888ae88007bc6c39c1921bd8e0053440010f6860844d16ae585902f6ef8a9522501b6cf16d79fd89478e94e081a8f347ad478c6875fc28f8dd0f3bc24c8317a6c8b213bd1b30cabe797d43ca62239d3246a24ae50dd2899452ca558c11461821841042b9411f51588e34c618e323f2e1c307f4e1c3c70f207d6379ce3f95b455ead46083456dc49793a3b9b6913dd1d5e58f87f6c97823d12aca7bf739e74f080cba0bb0594a1125acdfdd92e4ef867cb1f931b324b57ca1c83c832c4868232787e9df952a5b261845931e31c2d24246ce0e888034cb12bcd00162bd1b2baa3cc740047b0d8ac651342e84022bd4e08a111578f0e50920bc280a628a2278c0050ae60c8a1040ad021111306707308011c4142054590108a26084072b28c2962eaa1802141ecc2089a44e74cde774dcaaca15f5df952a4258a2e31ee8e30518342abe17a2aca2b8bb0041800579373a08f420417c04f115c4d1fc91fc51b7a8755f9554def89a90fc052448db9056b56746b0d0b9873fbc5973a8cc36b1ab261c0a052dac2d395083a269ae0a2411e5a89f54a852f41007e54f78a0668a1b00c1355334111341d44c11af545182f4ef4a1526553ef0ddcaa84b74b7cfe6bd9b39638c312a018b2f2cf1c2121542086137e423d80ec489d05647a17ae7419c3725a9468d1adc8c45014e4c7064850455bc0021c4c4045592108526d0c005514ca008242680f283228880821d2d8ea67084267ae0c5162c9064e1094ce072041f5061e7496185284d0851c40a64f0c3ce5352050a9c34a7e52d298a9e7b4cf3fabcf9aff0adedb8527c79ac7d780cfef058ccdef6aef40088c76626b76ebef4018a2db2fcf328df0dccdf9d2f3df66445e2678e6d5bfc4fe933c7668685c71e3361df1529b0c08657bcae4b5ed7bcae8bafebbaae3a83d40925f198bfed5d91228cbf36a1ef1ec93bcb9b44ea213def2c2f691d75cd4924cd061dac94b1a6c372eb06ed76cf2db7341eec09610930b0afd88c175d7aec919de53d96438f9a0f211dfc7b343a6d019643f8fc6aef60065a3e8fefbd87f144028f0f5982b8c10da06072831d54a5c2151848e1c3151e28c150173bcf1f8fe028c2e8532808de20283870007a5012de81d7480fd07be02e6af083233bd73d025dbad00e756b7be189659415db59b97150c56e5385edbc8ba08ad9a592ceb1b19ac962bb9609c376abe7c6e2d9990a2644cfcf9e03c933511f75c327300c63be9d94c2dddd5b47f3b14a3927a5d5b22ede302ccb348dc45b29898c376b3798cc741125c1638a394da122e53433c333b29f6ff70019999999878a81c4cef75d1ec0b0ba7543ea86c5a7d55aaf85ce1bec79cfa68379e8437ea06cda74f6969a2d7b76774ba7321a61afa66218a682c2529fee987cb0d66ad9886158ac447ee837f7eddccae88d37ae621058a43506e13b9933ea5a7e51778dc70c3d561fa55b1a8fac959f7f66adf5587dd578668c31de0cd1b1a629a8d46df6843dcb189a1101000000f313002020100a07c4218150402899ac99ee0314000d8ca844805617c95110a41442c620620c008001000010082401434069a07181ddaa605845e6819cea16a03192feedd8da33a2f85ec096ff4eb857c6335b853511c8881d44a9f6851cd81795e48cb89f421c878c4cafe034aaeef8f7de8c04e14b8a67e566390bd17bbd82d1dbf2aa7c8593c280c3e9a25e7539973103fd8e73b4259d19ac375be65b643b624a4dcdf3958a3291a2890e98f3184e864c76aedc107d9498f117f87c694b97527417f443403909acafebb6bf9870f95641fba05fa0aade626eddb4121ad3052bde5b6987bc7338f5364bac6674c1c3ef0cc05fcbba71e391ed99ef642d013f486bb04b732b1e1524b76efdb57f6699f22499076a13117db52c0da8e996962ef3eedd1043220579ff3249926c05d602eb836a389f0c56ced3fdfa25860f1423d1399d8f2777551ebd5c741e975bf080335b7661d8d34dba54b38e1716afbac2043c5d204c350093a1e9a33ab1b508bc36398e6688329d1a2d0834625b6ba9022295a5751b4ca0533bcc82600ae73989cefeb9a9c0c1a2fdb840853a45d385bacf9b9b03d4b74caac1b4a24a5a3ce71d338a11824fd9718c0a947ad323f5c6aa468683a2cf561da32adda36ba77bfccf4a7b9e83e3759c95eff81c8a79f50afdca2e3ffa9d9593cae85b4b467b5bd3d9ab6ebb393eb515aeb62a9fb0c0a9e85f0c77e8aabe740db5eba83c6fb28119d7a2153870565baa88207a7c40d1879bae6115e73053b9da083ee9b9fbb64d63523c0f816858e9f0edc9fcd16c7983b79b7219495eedc709404c0bb935026254a8c97e87612eb3af8e8e38b0e28171c65aefcd02d609bad27c9ef07ceacb715d078d1f6a549cd34c8fb7cfe2c8f886effab97f3115a4efa7301775e80b4911b95092b065e06b4f83be008e85bd0c9ba0fa4203d8c8c4f658f54b4f9443f2ce731bb8ed98a1326366067d61330dfea54719617f59eccf17be275207e0d50bfeb8967cdb0113375b10a282bd48c65ee252e8cf9710ec64b4f37ed96191598d513b06bd67feb991d7526ac46bd27891344d78b6fa162466e2c2da7f607bf8aca9d27bc7e1f9b38b751858928e56f323f38cdc5cd64ac9838d6619606f03557e9e25e7818387e422188fdd427f1710cf0e4f481c6b6a65efc94045b43f97e4e06c90c3312fd3f81e1afc5db3c42c360f8ea029705482bd4c05358b8511dff7cbae267dfc02c35fdb2b2e00aaa711a6940e83bfdf4f014d8cb5f9d2b76eb2f87f23ed98726ac126976853135cfaa52a9fa67562c7acb61b08821c0ec668ed49002d7e1bd1d941ad07fcd0f32485647d15bfde6b8422d7880c562691123ed1a711ccc571b13e3837cfbd61ef4c17d68188a599e0c474faea0c8cff9c4440538a0653b36e4fc3905130303e0290342318f11e44d39112efad0d936dc29da78c305e16403cf11d34ab9e182f101737b2c336abe1a25214b25bbf886cb8813e1f96066f341dd59e58cf5b4359729d49ff18acaf32ef14b1ff5b3b45150c9532a2e82a61c0aedaacd78a4c3ba9c393658aea6043af0350f4db50f560acc0e37b7cee01ae7a556965b72ea4cab58f0b003ff8810acef7f8403a0e3d0ac46e6a2fc376e523b684e63cc9020f59654562dc46144498d1297d974e6ae1336bcbdf7f92a4b31ee76e3db836e9212ba531df1af2efbb441872e7a25e0c486ad7ff0ef5a06ea0c9e35c19c1a88016c15a0c4943ba0fa06a65eecc179de5418e90e49ad5d80ca0a04929e460eb7a4454b2b75cc1cc6a21f4ba59afe5fc862645e8708dcde1f663901f0239527775be0f995754234852caa94b4504329cf932c67ae1bba032e0973782bac183a0dea6524ce31b6ed4819c6854bcefdb691e0d585bef9da158abb568d0ef0773f9b63d1c8267636fa018105fe51927b2c0b9defe48b2c20e94486b61bce54606924c4a2ab50fbd4f24f4d4dbed9c89c0319f857fb360af91379962c3fdb9e480f499cf031a5ee73a367b0362249bfb9869c496f2ef7d1fd84186bcf385c9ee1bbef43cc6f53843cca0023488ad320725b6110f5f07ed4e373a2a67c34a6b464dd80d06ae6487e040a3ef1950b77926386c1438239aa2154d9206056fe19828f5d3d4ba6ec8e2a838cc172587282f86b1ea407e8392c8cf952087c4df00d3bba500837273b7947d5e5db1f5feb0a738378de4e02a85a1e929d9d17d8b9f24a90c6b90d378ce0a7b2ad5479d9725f2ad76f26d059486c7f6414e7647b2f9cf4ab36f532b6efd69975bf6a203ee0fa388c2ec5b817838b5fc87e5b17cbfb93f41cf338876a9d3e9b4d61ed736aa41a7d1752886b11efb4e816dce009dae67c89cca46127171d8890586b8092873f2a49a280ba480e429633bb68041f3d7ed105e05287df723b7d03e14682bde148273e15e920d71a4c36c37db19094eea3501e8c29dcca9b6c256b0578a9a40bd3a9522330d6e02dce6d68a4ca152281831b140c9a0b2902d80318a52933cf925402a1251e56cb27e00458190ed324d01ec1868f89f8cae4ba9c59ec1b550b45bdb84df031ee2e65d1048c498401bf35e2f32fd0ae489e145ae1d8259400bc293df405e30bcfd713e447f4057c31c95e03c267d1274fd158e19f36a6d08aeb4ed6256aaf08a62c447489b9f4430705f3fb042254b219333338ee540087fc67d463dd0c7428c1c49b66285c2e815746040dac727eb246d978a0382bc1f7595cb015cb889a4c72bd94694ba86d87707b22c63018040c6c6b9a2912a4407e410ac263d2559b3fb375cddff5aa5ff805096ee46af0da1f9332921468a78543935bb2d9272b7fe0afc345de616f6a889d2a34d1c212df0c83e38df4f545990add338f876d9f7138dcc5372d9b7d12d58de0fc83c204928f1d0fbfdce7a694a573a0b369dd474dd2c9575e449b0768fa116551139333bd4e70c5c5af481884d69942a0a7765786fb5b20f4ab67da89576c2a0dae6f15ec976661754f00c8f007b59b533b5393f83b6ed68119f73ef88a38ff955b8bec61b898da2bfff5785d3b22eae4bb9dd77694be9d5cd8dd822675ef0485d53e356874451bbc687cfb41a19727445bd6a34d79f3c62490b399aa942ee176d400969e006b5f40b043d35157dbd3f015c37878b51bc067a0b019642f5460d4137c180afd7f11520363cd8864a338456be197881fdc05fdb91e7bd10aa40bcbb940734f974a482030c33a27b614811a0b0ed99ec3e9305d834f2bf3fdf3308d0f435b4c958ab67cd144c6e63f7cfed7e5bdb531268376bd72f24f932f86df28aecb0e11bc49a84e17702ae65fe89964edc4f9ffca3f56680ddf07c7bee34b36198c75cc91ae0602b8bb3f66ae68d3a6e248f5eafaff4becd12dc26e683426fb52008df86bd395e89aa673b27d2e08c7df8dd068a9126051dffe19eef7c9f135329a012fc69d13cdf57b04b3c1b0b44173b01f78f80ca4b8b502401e3be6dca0eb19b1428b22204feaae01efd2326d6c66c1ff3ff1e2c550eb81a17cb9f57388a819912cc56f592dc12c16d493286fe48006e7085a58a0256a517a9456a7402dd5eac6ee88cf94bc352f5830ae7f9e2ec54ae76587a47aa0c4683f74d51dd2114c484ea3bc349e1cab3eb10f237208a527912e90fa46696d32b6f7c99ed8fe83a71ce2559bf7e497cd21c74bd0a69cad30a00c453e7da529857f734cbe95f8807247383d8f79de41ca22b90872a3a138a6cd8d5d9d5351e47b6ecf114553757e798e9caba95a676a1c3f0e17d63ebeddf0616bdaf8b59cff7be3d30fd6c5a69e4d296de302f6e320717168d49ce7c24d3e0517d62856f6be0c6f03d945685604d97c1efc24ced30188f9c1c6a1ff5cb5372804a9758681c248080107cf02850c1406baa10df50baa401fd4317c0ddc012fc6d407cc639e003e2c49bcc98a79cf21e8a16ac3ba75352c79e246a0f231991c7fce15d516b6f0190e1021a47be18b9597ec1240643a909191d35bc29251bcb3412004bdb48f64464a1edd90bda20d4f8e49d6ded39f1002ff7d19557b1a985c0b255c5409101ec06b5eb171ded7b7eb32c445fcd5033bf9cd671f062eb77634ff2f273f71e9dd1b0eaa00fb15ce0c4e1a50c9216d510acde21f998fc5320047195f973d3fefcbb938e37b4ec137b346b9a17262965b4357fba9796c1ae18941f04baf7309d22fa408f7eaec37c0de569c093ae120d0da707265dc318efb2a21e773e88c4a0640932ea12f1210182cc8e1f892a5800781af1ccc47d025d8fef0c79683dfc517a42bd4dc5158c017e696b41cfea6a8cfb22807f8402eb4d2746a26fad9bcab37c4d22bacb94a55eab4f73e9dfe3a3d714acbc633ff7deb794b8ab087cda76bcd1994e72370d28b47e6fab8b1c09298f2aece6320895c2be8df0846fd5869812ba80a1fb5fa57b218561ac2d99024e5001623a2197a9245bfe0ab1d9225cec7f5a3f3fd99dcc828feda8ccc7d4cfbddd328983e85aeb9e2bb50049fbedd6748776fef3f97afe4cf567db6f495b1c2af5f32b551148dc1b8375a09487e363e955666aee3a24ee7c6fa4a8e79744a5bf5270c1ffb93dd8321c5effc1b1dd42830932293df7a62b78dde1323e33c2005ba4317a745c46ec8ed2b16329fda6fad6a9282fd05762bfba899b4bdf20d9e7eefd52eccecf338b04d0105e70c817f2b0bc19780176507b380ad50c957e40144469a9c3c677eb3299a47b32c15a5a7505c2327dff359de8120ac8a839ef503a2fa8d3aa2376335e9c864446bb0206354866294d8648d262b65d1e2a0349f0c5dbb94827ae55c1e4170174a3d68300ad070d11bdd6b97b2d68bacc92ac3acd4bff68aa579b633202f535a40b3e47015fa9cdd89fb073daa558045d6c863140184992abd8ee0075b8bd59c256b4fe987a74e8566d4bbc287a5fade9f016ae8e749ecc52ce328a5209b3985a31bbe7a5ad90dee945890a19197928e73f22c661416d6d7504ee452d834efd58392566e49afc654a4c9d90d5864e8d6b3ccaea8316b92779a8d9b2dd0a490347914a695e1c2ae6bca1b3cc040f136ab88435b9eb5c655d49bf5aee5e522e8adb078b5b73611d0904d5318845d5e7ca68eb1d041faf5cf32995f85c34b7afec63cd0f6e9667ff57c266df458edb797a65925f39ba04c90399e3cd7882c258e02e6e3cefe26660df0e871d83809f58f31e7316846a6ea290a26729a359bbc8471b8a28eb618a5e78c23fd5343e6ccb12fb2ed9f12edd37f2b3a41f8942e68db6d266a2bc47be0972fdad3025d688c9503fd731c238c301a4d568ec3564504a08bf3eba6ba144ec1832c906f05f1ecf3c50e95676edf58292048ac6b999dab5f57ca9485203c099cb3e306cd76e6e36ee68f269a0679f069f6f2b11e324e95baa97fb27d4219876ce4f91e8c78349ff10347b4df906fc54e8915385a83b94266464a49d355f49359f4236fbb839d7733243816e0d520c3ce98f6f869e2c48a070df2a9fd0c2bc1ee9e55f714629f841b2a5bd8f9154f1d0edf37ca240e3206bfcf13bfb21b0bdf421709379424ce28248a99440ab5113d07bc5d68f130c0bae1c28702b6b8cfb8a1db1be63b5060c915a00415370100f27853fe3b40759a2e3815bb1555c4f16f797f601954cdc9b5a23a6249e09436b8015278ea18844b3180356e3965364ac78c02ff1aef5a934225549bf9e480336018c65d2ebf11eba1c272e44d6c292d293f54c21ac26f4ac0694445bcea8dc82b66971bbdfb876fd17818c16aeb9108900661d92db56b1261b714f4e87135052dbe634500a9b081dea546f4b87459ee3192aae9d883358732c7bd1d7dce6a30ff75760a032f1ce19381833321fabd03b790150af646a63b58998bc977382ee785fcc37f54724ef35464725686e28fd6292ae03c16878d6dbe59970709893a87037a629e8d48d87521d2cdd05427568668b47963d0c81b61b16134ca97cd33c1af3094289f27ca165328cc52bd1b62f51b79489321251b749c80ae25f9ef2a37122030e80e615c5be49aef16d634148c70cd21b0af801999b5f730adf45de4d99e74790f780d4d573547a341573e1ecdf5034fa1bf42433dc579a1a2f4f96f9f9f2bcc85dd0f9e2bf8928ea1fad4cb28c2548ba8d6bc933673c8760c671528a0b4fc8631e316226a35884f90981739c9c3ad155665975e60f5bce89ba7f99a925fe174175969ab9ab405787e7a8c1a3a3ae4f8138ded8b59733f5f24c5097d5f65a0c583655e0053835d74e3f6d11bc9f5b076a1c917c8932617a82975776b00f12653b266e09ea4e33d1c3bc8fca9d98197a39d8f76a539d1a6690ec2a96332648ccaa4e087e8f83d398516bf1632c4a95e9f88a883d26a0146e87dbdb73986cc270b7cc095ab6f3a5970c1b1c9a768425d256bd986fdace1d1468d04bfa1f76584e8add83aae5706419c8d972bea479b5c048defeedb8beef8c4c509d5dd19d69c58a7a4feaaa367d81213faf4c6dd51ab56e3defa2b8a7b8c6653a1758d2eb75855f9449677e2dbf792146e28529ce71d971f86f182dc55aa8f3f0be101fb096e395f131e8c3433e39fb2afa0dadb76ac00ab69b52ca8f48dbe266f42ab810f0c912522606e950dcce720bd4474fb4a86fc67a51718b71b8bd013a3de30fcab54343dcda2c3ff654ed91b1d71786cdd134ea462847d1973e38cf1011525615f0e1188f6a29787ec72e4c53aa2bd4525ba65157d5b7ce502f31506bf87eebd76d1e0daeb18601c6fdf07503a89429e20392421fbe41b5ff4d5c0219297f19efc02735db80a33bb40acc809fc5a7e2c111c5c4f6f87ba9c9acdb37e19e9aeb4774b10e2a868e42f698904820e04b3faadf60d50475afa953074ddac83158012cdbc32d619e43abbd65bdcdb855817e0f6666847850eff53c1cf3002a1888a47dedfec62ae8449fe427fb82cf71ecb4c253eb7efe640c6f550728fc08dfe43cdeea2822d5443818f8052a928f1e9b32c89416305d46c555fa0cf9314934810992d107b92a3e838323fba778cc35c258c8b725643ace13684fd600d97815ac0528dadc2227e028c5d3044ee21867963a01d54a2b0be3bb79588c47f73a02a3036f20490cf69ee0dc070c9e48739b6466a29c7912819a3d525845755d11f452239eb286ca2dbbe34a60941f612161b9f4c14d4b1b8e999bf3be8246dd67b63bbd8236e42444bd0a755fb534eafe3bf294334591ddbc05f2a30bca7f176ee28710b4b50161837d8fb8fef56fc98ce0f40b447e3abf6a412b7b06e19f518472ab680bac55235048150dbd5b9a1d2f4285670fafd4b1cb10358d8c2cf7543e47c824bc38bce8ecf75e3f34ee71a7e98611cd857afd84e1e5fd7d50ab93e58e845dd6952c45ec22f08a81b32d983b0f77810cc97508b0f3d71e8b6516dab849050561f8bb17f8b0b38754b87f88731f9ec6cc7571cf0a8a226da242112dd6ecc3b73f90417274d3bce7733ee5c75d14f417823860ba541b73a2c9312d77c6bb8fa9c62d9f95ceb632b80fae69554d013d92c87b77415ec017418f96be4ce2630316c98d785682f110189cd8043212ebccde76f8ae15f2ec4fe50010a122204bbe5592b0173ce33d148374f652ef1685cb9169e38a8428acf1bec29a413a022dcadd3143506482ec711596891c3e121c2189101725e24b678c7c5ef590639b9728cc842c3313ca2059b281025b91e35be373ba5671d756e41ab6c1097edefd1c74d114cd0219642618ebfecfacedec080f24ad006a252a22c7e874a714cbfec831486026654bc2d2a90576bfb892b445e591ec2e012d9d2e607a56728c70b0039521540d97495f78b43452cf9ebb5c56116490354f2ed4ca6ccb91b353d6c7001c728c6d3cce1ebfac2b9fad34f159422bbe61ac502c5cc35d17dea923be059f4154ade5f6d232651c0fcbb875a8145aa3c9e4c7b916e93b91c8b535ae22fbe338ff2e79691e497e00152c62ab018ab1193b257ba55a344d7526cdc7e6a1ad15ecd44f534f18bc6e0b404a5f573b78458b7a3ad5a3bb8c043c4a40d4eb16ccf0fe6838f4849eedff30d36c85d3c3813c0edd3cbe780483f53bdd8c18ddfc76729aeb95a5f803b670dfdb4590d2b448197c78ba484e0983791b622eb5b43d439c13d0f9ef19972c684240f08b78c8c4969ec0f2b79eed5912d3272051090e3801b68ad3598e185a9fa077dfcb5f26a17809ef31708c23d40bdf1f8ffd6f8bd24c8b2d2fd420678b53c82c91513df4bbfe241fdccafee49c18b2fc84fd0716c4fe63e54a12dee29fe063cf6303bc3be6f4e057f0b577c1ba1e547a32a39390a43d3e05c9c63d3b68c983f145a0974c41f6a0bffcbd7fb2e44863a152c77c780624042d3fc1d0a336bf764c203e841ff442e52de2b70b9b16fd77f73719c8fd5884ba46912eefbaa113edfe61c41ca63b47e9fbe2249c689dd10a9f62fedf7d92692dcd8f738e498e001aa96a953d5baf3e6d1a8a5ec097ec3b299a2b7be4d3f3d478751fc106ea7c8db9490b76bb41625204846d88fc3edafe10b1c00b356c81c206a91a7344bf917f0e782f233afa808a3d2eb87660321b663efe500ae365fa0623c793ea2a0ea458405ceb58eb30334fcab5dee4701373295e54335c3b5611e3598b6492d0af2f362c37ddca4214fee8c71f6081e5538b7cdaffd3131e30a69a1937491584c95924289af962128447f1c7cb4540ef4df062c2f145209f70e42eed257e79eb23d481629fc2c75d2440072628bcc471e3bbf2f4c3e26d2eec492db6000c26ffe29d22c60ef8022a4c97254047738e850780f7c2d2059fe61195f4bf64b4a8325d4f87a9e9ba1d5731a2ed2d7e5fa67e93fa30342f063685e582c6f3ca04b0679db90b392b5491a98483de1a303458ece0a99f437ecf9f8de3bb9b52599e41b77e7612814ad918031ec417fe79007f9fd0c3a9fb32bc1a9fc0de61d50f89eb84b8393ee0b5319713b4961238fdc771b197c0eb0ddf71420375451f1a33a3164a846c869d3a91ff5b3f35a44f2428b6306e430de39896193f1b5ed77cf3d35a7d7bafac435d91f4400c6b9e9dae36424cb31a987391da5bc032986f14b241145db44adfe12f23ad4ddd58b7a7e4f5ee6135b66eb87d15b584fe560b4cb3e14ba756cf3c7e8482bb95bb6a199905211f153f9ec4778030d1cb5d98443a84e32455a7e6a9ec8b96c5c41120ec85434ec43237bab5e396565659799ab9d2c9d75d2506c5c42264c244258f63e663cc51418d1decfe7673c51957aa4dcb5062f0bbc9785109bb6e9689d6f70f6efe43d22b5da8a552b000043d7be2266cfd7b9d87b4adaeae6e914ec24d72b8606e1c0da2093b8bc691d4a199a1655108a35c74107ba5ae8eb635da1bbac7c7fbc73ce1dfe6b4224327e6e9d1babcd5645f93eaebe2409bf1cc24c9d93142443a9238672c7fdd983e0b99478b8309b1486834400c627ac8ab1b9e798264bbab6d35908dbc94ce3e9050e54d1fd34be9df3b90af803b8b63fc8481df7e52ecae23e57ba99e15e16297a76a53d16db29c14c828be8efad653486576b25d03a225d51944689514eb89e08ce850d1247e2a6341095608818eb07c85e854d67df01f8a3abb85189ca1a6c03d631339dadc4e77cfa7fee3025c129bc177b86c62862a7ecb0061f2390fdf6decb12a0ef5c9f88f885bffc311ffcd8a2ce736d32b103670ebd4d9b2458fc93907443259ab56f1151ab1b3c4bb8fa08613c261787aa350f76f4212fc5aa62a70735258758fce7281d99cac02ba274f8854bf4ee3374c564a72cb76adfc604014047d72a431f2eb8eb0ceef180e6023f7035e22077ceb4f17ce90615c2fda6e0b97c583016aeb496aa1324e5c22ca8d4c4a5d7e898b9fec50945239dd06c4ffa49576d5aa0fc05bd389b9cb052e67178234e81bb6cdc1e24b7f5090b93f59927fb21c2af8d3ece9a2c24757d3152568f2480b330014f3d9db562dd0cd6776bb17c673732583f5a296db2a6b02f37a6b9815c56da514086f8fe07375e666469ce58d8daf3573c0492552dd12169b96d11c0a59f633356e410e32b58c8d468897075082f3e1fb0c38d5d164b0927682bc3ea58c627a4e739d2d1c34388913fa7948a371e7661b1d5e043e2c79a2bb3eec0bdc284fc0df1380b039f6c974d0ab6863a0abd5b7197fc4d63126f26fb12b30c81ade249cc639120e85b41e0f90dbf59d94d1b52019986d010dd0e17a1ec7ce2f1602201463838887d0b2792c33a42b073d158a23418b205becf6aeba74a39eb59a49ef0181ad2800af3890c5a37d9a196ccbc13544e8a6be0b71212a1208338879a440c967d0deab59b60ca65686847da49f1bfdba5fd26cb79d368517f80a86a130750f3718c2a5e89dc321e05ad551ca1aae983e7ab69890bc6ea53eeaf701065bd856079e69deedd565a70ca517e210edd06ca0fe3ef8089f728d849d81791e29c6d134534751a5dacf39b3059b56537426b841907c6d0f5cd9c6de66ead80b90d10107dd8ac92ee758e0cfb955b8906af89ce145e44f13382a63d24093cc55a28c9b5df16530df551e7be8b1503c3f57e9c9a9677c0be683ed6782b100e89907e27804adf6e1581ce4565a48d58acafe300390a4e962d296f67a3e48a750430fe99b11af3ab13dab5f493e087b25d63a9b9bd97cf813c50e4ca590ce1af616f0a8917edaa8565021b12a23763de3ef88e8e8ededdef64aa4cb261b255c8341bb302dfc6740ea301cd50988c456ac6c42741663ed99364579ff0712966c7ac76e3f4d360a2013c757423f8e5bae00ecf67f32d2fa2f8a0e8e9e0b08501c8021d22c66acf2f4e7358163e937dbbe482c1cfb8f0efa68f41445ceb436c54522025900f4ad8f8885eac18a6c104802de686731053676595b1d030f1a42b93d097b5e74b598069c25a55cb1db2c1cac8bb9574182bf85d8454402a14a79dba2e3b7202a654057de04b46b5de30247ff10ab9ba3eb7f7f29d0f8c5e4405a07285ff55d5a05429cb7e76e0ceae49a851b0b2741644f0623a04ec8fcaec192bd8d393c67ead04ef4882af26aca111275c6ed0ca68147431fb5733ec01d940c0e240b13503c15a98550e65417880e9f69531100f999dd852d3845a61672316ee7cef8e734835b75b2e7a4a59f3d9e7f333d85b730563a5f39a1182f6ffaee1f4f5c20d912528023d65a03ff2273ff8308ddfa2dae32b41311f7f3df9109dbdfa9886c67f177dc5caa40815aff77bbdeea6c62da66432bcce4405e862b7ed393502aaa1e956f33acd4a9c509c6886378a94e248f2bf6076b360a25f11037c8d3fc133e151f2c916c408bff552a82c1876f6472e7007398895043eaf6375ad3f9e54240a3006482868375b2d4e8ad8f4c43b8c25b484ae3cc2d859dd542c11ed136d6c3bc41c57a61ee2dd81733564f1147203e013763bdaa5c3606ce110a0a459013f5242ca49cf92199a863904cab66c459647b531211e3e7eee6ff6393ee3dcc9477274de0cf2e7e1e616cf8ebb0650556dc5f8afb5b117f2bee2fc5fdad88bf15f7b7e2fe56dc5f8af85b717f2be2fe1aea0862099977355c2481da55608958fe1270570dcf4c20c5c92dda22986a2622a2c85f3409cde49c4f23d6afc4fa957969e6ceed9422fe56cc5f8af85be405b96682d225044edcaf68e836a98ccb6676cd654440594546539aa3fa7b40ea633d58e3a7f4489ece0ce2836fa615fc1bcf8412166441359a18e09bd910a0a1be1a96b7504a8f2e6cc3ca2d65b381e79b53d5aedaac6e337191438eb9949af361a39cd1077fd41680d5a9c5e31eb9f6cbd4d481c7edcd77ec44fec13861c3acc4d1d411abf63fa99a1dd5aed4fc624f5c7c95c8e80dbb3ad6a19a8a3bec45c7284d0d1eb8db20fb60e1882660c3967f9ecc75d15be044e379fb70dcd9cac5323f6da112e8f3e95b72d1fe3cf709fb9b4cfecbfc4e9bb9f153b08988fe99f452d7bd797ac1ff43b5178550fa9468befce9edf156d6c8ad273db04afe272a441ecce10f639dfe2cebec8553849d821d745d91d416d057d1733483bea0ccd9299006b9afe1a99ef99abcbef94b911dd7b3292b6814ce70ecdd09df7d5d4481fe55c8a330970abaf29140822628819a84cd28ea32b1c3a6fd042ffbb6ce090575f78ee2069977fe2cd097164487eb904ad0da1ce603f1d0806d7e2e6f2f0fa82f5290b3a47c989f696672ef1ae2b06b4f0d70959680691497263ba81c5e5052305cc5d4951f24a69e029ef15de80ce17cb0c928ca9359a17b65de31bb6ac54ac46cb937454ba5c0ac11ac0499d3b5cda97637e28c342872ab0eb56802c40bec20629ef9515559964463615d2901ad9b51dfc65b6fb6f406039fb52025217031199b7113330facb5e9556c4f1c71413bf71033faa5553981a10f04541aa48e027e69d019aec3b889b94675fe8bd27585dfddad2fad3766ff936f7710b6adf60319321a0bb73d344c039549a175a97df554fd9a949eb4316e8b94b10eefa53dbdd3a5010f2d0f88ac0591799390e41a5fc303aaacc81dfcdb687fffacc6e7318449d5b21c9d5a9e2a2864eed1d7dfab1b6a4d8e753284025a4c4a7575db4d4bd9781d00dadb3c00956c76eaa9eea42051dbfbb22c5c30fd710118020854ec07b9e7cb0b1379209973d444371f9599c61789992e83aa71daf5b46e0a3cfeb32103d043c235786385adc23d46514eb8c871ca05a65c125d142c58037cf6dc3f49be7919393b2032bdc4f6835572932543895f6d6b650915d70cf564d8114626a09590c1529398af190170ecac465b5f64368ccc9a304b35ffe72579ebe339824675acd89e5948625429c98e322dd66f920312c7af25f92f3bb7af78aeee55814cbfd066d8187082bd3ff2751357a78959a8460f2e54ac5d7ffc16db23ca0c4e3509511191e5e38474b45885e882cbc4019324e7855db185cd920c6e34558f29fb54b0f80aa81499fadd0ce92c330b949d8cdd601c590131f30d731c18df8fb3a3a18f0162f360f4c7991fbc3e7bd08f228bb037354ded3e8fedd4ea07df07e07d1a2c5390e0746d089a016c0477c2f31679b656c5cc2e236b15dc94ee15b4e3afb15d93f7b5125eb0545afc985745556283ea0d375ddc0d544f49387851a2bf2785bdc207094452a511ac6d6275c7f5f178c122a282487296ef62b51dbaf5b549e4c70c778bbcfd4442f7e12e96a0076787360defcd2f9cd7df92fde035c1007b1b5e82832bd045a009f29bdbd3bb244d7aaa4dee06e95f607e3fd9294e01122f44c987efdc80ab93af42c97992ebf70727ef0944424292177df336e72501545d81122280fafa2c07361a823d6dd19f30767f2dbd7b29444af7449cbf0172850a3a435b701102b68ce360d00aa93aa322fcbd807672ce993c76c9cb7af6cb537c2cec5af0456ac5f3294d3d1b74aa4d37efd01a537ae4a2cc140c2437caf33efc55db29e2ec17da8e937e9540b30ca94f19efc571bcc16394118ff5eef1b699198f9551aef55d040a3982ff78cf66a2b2e83ec57b03a38b8bac9c7887104e1cfb6b671ab9adaed7eb18f5a2aa38acaa462cf740508371cc03cbd2666edff59ec87643948d98e1787a79a2714db1f0377ed690be8ad76c47b3b93030c1177046af53d5ca9b0b9e964f5424b7b6209cc2b9acb9b1bdea117abe50798dd564f2e4747849a1c8a22fc6e55386c92737dd4f5151d93222d0e72ce16743e486424e5eb002e17df25344ee5a55c8ad153cbd131f4341a6ecada8156a9f85d01d6956dbbba76c14560c994f000bfd61297827d93a07ada88c05c5093bc582ec894c2af52fb772463c8a13a1d1cb60abcd604bbe89e00a2b57c275448cdf0d754e532e95ca13032f2667cb2b8489e5ab994a71670b5de3e16cf85f8ee62221f648fbf74e637a320dca9c32eb3b2f738a09d49c0c80c1e76c8ee17844d00d953d3c8d32b5a8a2b5500c847db50468c2c666cdcffee8ac621d3c81d1a1cceda7d003ef5e97815c4475af988452084413afea70fa634fdead5500c2b77b160491d928d871fdb334f5e75f61983a6c71a6c4e16b56c06c9dbc00e22373d0441f3322b05c6853348d8234bfb73781b262959a16172dfdc3682b1d0867882aca92834d6d74bc81040469249aecb634cb941b639c2e7ef4c32d971b14936482a2f0e3070201ab8990207b2eba5b00574420e874f45ab01636188d219ec7467d7b137b3d2b77af386e28039de2dbe54a4f0b076329c23f28bf8d3a6eafa2c53650cd628fb3c710b725034c4537f8d3c7798b18cad633f8018f5dbd664e7539629ffcfa456791816b36a4a89ab471d3258e89f93e4a02fd8a8afc3631db5da18ad17ff945d03c35d80827cf887a29c34200399382acc08a4b223d824a20a2b668c3ea63e02ea526608d4462a4c9195bcbfc8ac21c46959f0d64a8327b4cc56fe15b15bc8202968f98a8c950f79b7e7af6ad6bb789721affcb9d44e29b0d76766b9d5c94452e411ce8c4e56a480d482b0451e096ec0fd28102da1913d9bb6fb4b6ea1e5318e761d25f6b51dac39db1e1500f413d5819bbbf7788a8e6ce4bc8788472b4cc83f6906f4f786e511a345e6231be4bbdb303f5a84a3b719cf81d500ae2065cce97198da734337132dbf128196c9e35e10537eab3592094446b234f5d628ab4bd2ad2d20b04e9c2b93221b8fa83ef363509912396d45d21881ce993458d1f505949919e19465d2720d1cc3887a55a2968a74391e778c27be1956b144633a9c95313accdd19b51247cd307b856ae402235881d67e9f2a7d1a0d358a31be18b83452f85efa00d227d4c9d9490d1c7f5e72bb42af6c4af31dda32fdab35fd81914973c44aef79be587a232d825e4ac84c7b1e7ccfd9a69ff1dc59542cb56d140522d23aec67ac0c1a8d331ecf83a4c11c76fe89df5b52001f0e119173fa8a48aa6ebea40f0ef10585fce1782b1ae4bafeff09075a3893d17a69417b4b7be8de7ffca0208200fe899493f7fa066a6c19d968eb292e56130779313e2903fddeeee6e50de6ddb44c4059f190a6f6ea637d84becae73d5b2d7b769a0151cf429c66ba7163823b3e342ba613e048fbb25d3d575e3c8cbc0d19d41e3e4f42e44cb5c35bd20fd612ce310ccf6c3981a33a75ad870d2aa62246535f03c3ce7c9579432e997e99c25cf62774e9e25ec6163d9251a52726f04d9a0d9f3e59acba6d1d32b53d197a0c681d73971965df83ddc047c94002f9d7b1cb60e095eaa2d2a35aebdced370d993bd616af2999f0c659a4ec524f1ac4319c2c122665ecceeb00802d7d90555ed94aa14b3fb31f73410675032cb84d5156cf7ca08464c8fa81c9b19075ca49681961e12f760fc37c1ae1237ae0da9473c2af43672ca640e47ea4675f726a72177d4eb387bcd478eb6a0b35c7bc39ac20a8da32c579c7b149ceaffd0dc1c99c24a4468a5fdebed76b48481d1da16571cd81935d63a967ff0f5df366e83f85580e6e3af8f6af2c5fa162b012753a07260e66edcc249ff037cca1a45e90e890cf3ba91e23e8db5361375961b136e0c15b2de41005772b62beee0728c8d76eed5fc986ded5fd1a6b0aabbd55674813acbc9740056e9a77302ae3857e7b013d026bedb9651401f685407ac44373e79f041e7959e8a051f50811b1c074765671bf76a5354a62f4cb04f92d3cac180ecf32b938fed08b838284c242d524e45c550ac8276b1c9926d383ee122ee426234c5cdd3d988a0d2e268c0ea04d44139d9b5dafc8dc5787e6d8d0be60e0f272d14c9f49b579acaa26c647114412d0137c5d76048e27cce00e94e9177f5e711253af4175e4bc9fcda2819e571af9dfba370b940c1b547791468053391f8ae48d11f6d8dd6c3fafa56a05c5a5ba07676035e3e8ce09134d38a234b35f2309fb8c72f3b5203f1bcc5653b93ebd1695df260fc50d0c708b56d957f14daab4b501abcf93226962da1c37f999e8f4246bbab50d430ceb7880973164905a34e5cfdf9b97cbc9e8800ee905319d4e6aa9be3bf95e99711a917874eaaf5b8141a54ddb5f2d54f1422303b29590b20b8f2bc679c1fcc13cfd71d447356dc21956ffa555d5b20bf2d66fa3285c48c521193d3d3e342c06569c2e30c2abd080291d603a4919b58512122144230a32d059750893464d46d5db913be9a39789184a3857d7b3577ba60bce2d396eb599ec6c6188ad1e230b733e6b364ccb874249c7ff28e38b5f2b9bf78d17c41f389395068b1970a4faa0e457e18bf204639455ef5b723c29584f3ce1404d0ae79cbac6294005a15e7799a337f56ec91ced62c5a7ca8233675691b24f2d25bbebbb5a09f3d4a7f3ad0084c7e7a063db46c378a08615ca5a6bcb752bf801f177f51dbac2a9a8bf92c79228933dfe600a04747cc7790ddeb76191f4a74494fa9f599d97d47f2c78d96bab8f8d91d4176cee70bb63c3733ee01be93d9b0ffb07e65c604b8c1ae06a659a83957b46c8324b640750ac07b2287ff476adb0407bec6fec116b341164d2c6b5dc3a2a4c3d68e2ed98bab648d407e7a57550b7a8c6f7ded1f91a47e239d2a0ed0609816272e9a7708599918828b001fb06ad873135c5de9b1dd749046323b8ac3cbb5c03968445a07972fdcacc603dd43a4da3608e211374b10615b30f0bf8b8df63118dc69318ef005a34573c18f0841dc26b7d6b3e916f6ead42c6ab2bbf472a652f9c553d6cf9f72ead892340d43a8fd162f01627ebe9947613a9836a9ee7e71b232916240ff0ea3ef049000d2581af708a0fe64cd783ab7a8279e76273c0819608102c2ba8f99e1a677aacc8a29cd01f6e0e7ec11c61a2a63f9de6f80549f8b31171e6d44958777579f6b47cce09344cd0d05926ff1fde4eae6f7c86e9d9562c59177e056c7641404c473feec2e73f6770b8a483e51deb703f06bb267825c0799e71c0cf464a0648f12db8810350ce27ff2b07ae829f9c227dab0d2a72592966a9274a37ec5f74cbdb88fb448bee7cf8ad2a2b83b46bcd96f54131e8fd626da3177b5a12d8f714e65cae5340cd66f9151c7b657c75d98267554111746009b5c6853c050003f69a6f83bba63f9a399eede38499348631c92a5482525c331cda39471f55f593d310e07876b0bd369d39c19481284d0c86f6776a5be5e4123a9c1da709f73070a0365f2bde2db4b63c2420eb565d903f7640a8923abd9107c520e82d2997ef4b20157fadfc9e80003add0913ad738c5d36a4dae1d3de822cc46af8da451f8e9277440dd116d2b695b0ab20f2811f17a2e5d971790836beb8795e24bdb484e4dbfb408c1b1e19b0c4c744e5b64e6acf531466f6a25659c5b19c753b197ba69eeea6392038e052a2aad2a69a44715b5732b43364e192779d172b5d2040184288dd1d0afd254f638a484be3ff022496f6bd6d5eecb1331c8c17d43cbce265956f8776da21726a42d1f210d5f077f5562d5be969783272d5323506f48172cfd4d2390f551c5b9cafb7d50a49aa6a1707f5433adbb8cce5a4be66a1724e8312038dc8c3d01119197581b3fb768814028e895d102ddb382fcef18a12fbcab34689a88bdffab67da449eb6839757906867d7b881fff1fb59f262d18f2db9993b2dac916984513450a5dff02e712f05a810521445ce7abd242c46f58f3c6b60d6aa40cb467fe4a0f72c43523f853c5e90ed6b80833d735a163697d5aa57b7b8e9086c69a1fb9e356d56aeaec9f5acd98fe9406a94a704509b7fbe92ccda2f1457cf5abe120204f74904446b5edc52d86e43d34bc48a00ef340ee9d6042e27be0e9fb5bea00ce180af7888c7477e01c281bb9a2df2f3f5141d740d754f191a351a5a714aec2dc8015a2b841181535ba69c681549a96517778a51085900785348231c655a86eac2f26445b12ac7e584c63345aaa445785c7c9dfc2c1582a109965abd38a8a7a76dad2015c5c1d6a7708f1df96af59dc0034a4a5801613b9b117946b15d87da38c75973e0f5995fc90c88983223f857d7e95554b66495d13ca66beb3a9a120cb5f729207384abda08c059fcf62b9e110b3f3067a23ac720ec1e25041d9362060c6f85bba2c2dba7ddc3ced398a9cea48ae46e9441b76c7cbd3f50da0b8f5e8ad47295de128c370530e9fa4716bdf02a6fba741f0465615ca3f612c81657b30f56ddcb9d232887b6552d63406ab8a24f830314188b2e1182802e34cf10302b56167252822327243573b36c1ff714c254476ae51709bd2124dde64a6a2dfde9836e89e89eca2e365f94c671afb783f888e27ca0370a403f0ee33a6a713909d72047a39134eb381059f0f522d55e07aaa77f40f95ee598981c63da9d1679f2ab717442e3318554f2ae463244ec98dbfd7639bb04f9d9ad9ff8d85e7ffa20b1739b3b1ee5b3d1babe67ec616eac20edba1721058cd7f7c6b5f818e105bc3ae8f67397a3765095850722fa5f299fe324041a4ae842a4c9c0cd9e36e72bd69894e4ebb3e9f0e97c1f3116e93e36d6412cc7fe98c3750a19ed1e8f68a921df628792be91eb866ea1744990f6c90a22987e3e7d25611916638875f06d6f8bf29e43b799c298a2bae19d79975c8440cae8d56ddd52ab9a882043eb18de30017d9902e30faabfb93cd7b3392f3952ae32cc713a00f6ae0e214151c15003f5ceb7e41c7216f9bf77d3f66f18cdb69603061c8aa707dd8018ae438d7589c0819c958ae29f0e159490ba77315a3dc8f7c23e5833a7dcf24a9d27a9f5db8dfadbca814a527fc3a706c97ce745a3f052550b57e29ab57e2cbc6e5b0d81cc7af2dc47492605c7abdd4093ca44f6e0c80d1f6a18312893c41f318626356856690fb4bea2409ea811f3f1d77af16211847219461334155442d55d2fe0ab55d0514803af265728f18e1a1a22e9a790116d4f65b0290a77da953f058beaacccabd232cab919383e3ad4d31392fa0048b96d7575a9adb9b597759fb2148483c2efcca137ac6e19c0a6d4621316c82db7417b9d194118e00b856a88d5685628baac24eeedbecb6cbcc7be33dc8296c34f7d1d52cdef645b8cb54b949c04adf130d34416c02a0171a07065eeeb100f8bb697f6d735b0db5646e738c927b9e531a36fea6366d00043489b64c1f766b69ff1106e21f6b4266aed356670052bb379552ac9df692675012ec8caa94eaa2351b7b787ea4b9ab0954ea38696f2c2c3d94881e13764522f54422dc69e07a50b5a50ba8ba215dfd3c86ba1328eb3066519f2b8e822b3b5e083efd1888ab1c7f81f6ad2bcb0478857aa1fac5717b27887cc4c60ea525e0561dbab2885b36f7e78fd5e41511c69be2984bf3e4eb9222660d61bbee913297a7f4161f5949b144045482d18dbcb29d042e347e5b148c7a2ef8501bffb3fd31f83a4aa70a44c5640c31a3e6e7098ceb1aa06c453252ef9e6b6f7ace636503c4707eef13d200c2f24faf6735d1cc4f9090acfe46770e64af5fc7a3c3b628a2afb7f58de178f766e73046347aa765537ad5d23328ce2ec69651a8a7688cf85c80f51f2f623677a760a69e7f51b3d22f1def11eb20e157a8e165844576db6386001c5e568049f3d8aff8895c872344b043d2f6bd4093913ca5839b96350eb4c759250e2d002a20aa5b69db0227c8080611ff936bd05335ee80a2397c6c580b6f4f7e8c3c1cb744c2e8e13ce9a767f28c88bd3d24c502268243b17c750d99b9e8b5b8c12265ed8446460bca794cb94243db9d9b071e8a40ca481426b2d73b2c2380ce1e38202eed1cfc94e1000bcbcf5b12629438cad40bd647988fb29c5188ec5a052683064a9f0c8baf0db93278b9622778a57b9095a96b8aaa5480a3fda01ab28997fa4bb96b1a839f70ddbc91ee8a5ee7fb927318339e9265703beb05a30c8d161806f6f1d7ae854fa3fea92bd508e784ac378742503668a8ba348ae8e4864784eba21872abd3479329962ac928451b37c876170bd1ba73104ab4799e0a0ae692bbd4f5cb793ee522c0b23b4f1c27bfa7ab5c04f2402ca53a3226d38e4e9139f2067b0b96576d443a6c5ab3ed6721893f53ca40b09a6f5313c7b86c3ad494994289d800836e4e4684d85189271a63395a7154a16ce3266983c29a7a6310e4a669de184939ace1e68544499d384c206634f3ae0418b26307c4380e30cb907c536862c4eb911c1d7560890f7216c7af62825aa41928af1dda9248eec93f147f71f5970a92e44a56d6610ad02407ee09d52f7c46b8680b8abcd31b7b14f30eb89940ff5df211ac8aca75319ff286366cdeab4707c43b3edab2df8d0015930b54739d43f762678f96f450a344e05765acba2d2f8fcfc7ebdc548e3d9cd234dccc3be3b2ec6eb8f9281d4886b21161a67afee9826144b2204914b67b99d66a24927a86815fac22c42ba966904718867730cdab0a8467d2eb773a073b1ea14f24e0e502ccfb91922cdd2d246a8224739d015552e0b674de8bc5925116723150a2c79904e5ccc242b4ccdea5a5f707f93b0cca59d650ec6ea847c872e6b86319281b1ff6790b15140d87825cfd5f05e7bece02c90a895130e9acd6a42f141d35173a8c1d083fd04cf4098a4dd5275a9b8965939ce201341ce40b4c92576bace0a794e9ba1a2f2ac727cb12c6139f53ab722fa54f8a978c655eaf05d2ba65ac9a3a07928311811d7e043a293252b75ca6ecfa7c3b40fb9158ebbe85d5d42213a90ac9a108d786e3e6ca4f1353744835c582317a692d4d488351a7dfeaf784459d6f1f1d8eadb7235db1ce0e26c31ab6c753f0ac1959de8b773e6ed5e92c8fe56fdbb974f9cb293d6b8f538314e11a22cb73f4e05637f1ef6bfb870fb1e718721cdaded75c0f60e3e879bce79ac02081299315c51563f3688813cff3fadaed753af7f5623f3c28691aa73035abed7ae5b65bd78dd91eb799495167e73b986e0e308d01d5a101ebb26f354f594e6ad5be5c5935a8697c82a10d9ec397610932df99f1c36707c2f0e720d07a1b0986c50e3f2eac5307da0b25e41301f57b77d87d38f9096a3f11a5e565ca5416cc8352d1665c1dbd60f602d8e7cdff2211c0867271433fd2d81385d4bd26a7c36770f7f21dd8d7740bdf0b1d8a66fe5ab462776a38aee1bce8143f1fd1b833599b68041421868ddee8a7eafa9dc9873466ebff2ff4ff86a695668e85d38813a1526c022e0e30558ee37d2782490677e1f809013a9f08270ee7a967cef0f40fc6df73116daa4cfd5d0f98e883ccce202dc030a22fa8b6ffe03708bf9c5d4f93221a21c58e5946000abd3fe19be044024037002187d03403a57a845df7b11f5a7c631eb2345d00035b10ab8ec5dc7a25e761da7c206cdad4eb0751a264efc595418a2f231cd05f66c28c1ff8eb13de2355ee73cf84a2c245589b67de53417bd97d02ec23efdd3f3d9dfbff79def4d59ba0c1be89eaa52b8573a5d2248b2ee48f96d941fcf1b69ba2c7c410f5879ad92f53ae8a4b4ac0e840c9d3d7b7fc2647350873c929bfde7d3e0d416652d0cd290ec051537a487e71ec5381b8cb0e71d9ebee5f24c91942603311f3b083e3f6ac9e599b08e7235b17f9b202ceae8fd1991981920aeb60170230d0c82542c7aa851bd6d5f6e4de0f0c00f8ecbfc9fc033fd016cbf873bc713dcd41e726be6d988732a520f6c7e87fb2620e8c1b979a4f6a0ff908e5ac3d7ac2467aeac5d291cba0080e20336b8b3d6040f350c8dc7a0c2ed975cd828eb5f8d70266bf55b0ba68cfd361a11548f40bcc23a076e45d55a4d8257c582c9d9e2e68d953d1209c3efd34a13c792d29b076b9e1bbc8c382658831c7106476fea6e4b47f5cfb8086475c000c99b0a24dd118632742da840c6f09a90281d095bbd4c11ad17d0fba02dd9a6063c9264d8104ab3415ce164f3d129b56d8e9151c4a16877f97a80c0e12c474451d49c94e2c0d25cf2a1427229409b62955d3c6c2cfc6161bccab71c4cb3b004eadfb270e232a89fc1bd50509f80cad6a864dd461cde7d87375c57f9020efdfbca65d597fe3010db5eded724d223d5ccdb09d5f319d7407c2f1ad601aa847fff5656cb651b9450c0990fa7520ac24f6b9d6ff3c70e49bf8ddf43043d1c4d0f4836ef7a457d5359d71c1b29f6e73ad1094c6e156a2bb2329ffa7fc621033fefe857ff5f3a6d07c5ed60ec9825a2a3bcb0f97fb0242a97b357394374685e848286d98983c51ab4e2387c06f101745d06edec828b93b293f91deccd76770a95bd6f796ac81473d4d201d90e2ef1805e3dec993fa3c8b5efabf3e583b26a16f4ad3d0e0599b052fe561fe44ac65d76315db9555581ede8d85d4e3625056da1a726e6d170d727800dd8741cb3c7e846a1285724d6106c27e5567e16ca50afb09024fcc60a06e1f032b2ff830926cc36a02e16bef1bab3c80e21fff01d62c7d44961073a1d9c5b3e7ba0bf2b8f87ec2385578662441177e26b23237b209330a91ee09d3e36f4fa3cd370e4d006235c67ee7a5a5847949d720b33057e6bfcaea86687702dfb837992d6decbfbcb85a18ff4a6d5eaa9000d497ec89a2901e1973761cedd1ed731a503132fc4df696b6cc8d967227cdbc957cc2a8ca6698473d6eb036ca7c1c44458590eafe3e6d8a9e1bb417a0422079d43185c7eeff34a4575ea8800aaed5ac341eda7360fabec8eea63ba02a17128f057ae638e2a232fd08bf0687ab3fc7c18f5ede8d353334dcff48e54b2847fa94e2e4fb0ea72c03b62da4ad2c36fadc38c9ee422c7a1ef2c3d1f8f7a410fa5973b4f53098416f7c827342a469c61a232900315632695c6236ab25690f7895fb010644d1de9853328bab20133aa7e3656b745951b0f27e8bbf0aceb2609fd259eae66a137f39e4ee13c200a03a18d8f309fab0c96a530b2640659c87b7c62eb7ed6544c8c8971b128609713cfbecf37439971b0f51529335791b4a74a6d8ba706550a6d1b83ed9d20c0456f3befc31041c0ac8bedcf043f639f20f1a2af9ed6ba79c5afcf2454e09d41aaf102225e62265be3335ce5e122521033d97caee8d9c9be02fb1ebac1e27490d1868914acbef607fb318e5dd61ab18e4d9588772dd2c875ffa5542a3b1655bf891e674382a53c5fa1b912974e2481f51609914046b518c847a4fbf3e07df4e9d7af09908ea0edd29486fa910c160e2dee194f4afecd5822cccb8ac33879876ca20bf6972d19d691191577efcc4e83e2230257eb890b2d88a378a05386342aa192210517f81aa6141b2516a2d9b2722277244b95c6a5fef0a5365a4c5e71a1165a29a676fc9ed6e9f590d48ca9e622772be0117bb87bc3a7c9b46d60edbcbdde242abd321caa78d622172be4612d90f90690c30e29513c9f5cd3b8b1662a1a62fb2b721868887ad1ad0b7672143bb97890ef6052a0283f1939ee56680c919505d54d64794ed7b6acdd239df17b1dbbf075e53656bdf4e71a8ed04cead879adb1b110388b149013becab5ac919e0cbf84809e327dcb46e5142fbad4181732939232c4fe2d0920ab23204e3450b34d4febe5739940f069446349fcf6605d1016d006e89d1eb63c4bc060b26a0dee18f6b0c8dc2017731fb5766bbf6a5525def300412d182a965f3f136112389781eef335cbbe6536bd0a3f8017a82d7b352f668e9e15fa9d08ad44854e4d5c658e3bffa56ed922fcce83512206846ffe2e81aba0f0dad5ea7302666b69c622d4c0f2298ac03cc171726b2129d4da8b08c9a00d9149e4c398386043301c4d9a1327d8f066dd059ab21cc6885de68ba7661474473051b70002c83cc820420662eaa667a422e08f070afae8eee8ef244eff26a9b582f8d60b952b5ab50e3ebff6988c17a2ded6a10492f6dc0c381061b139ab09884f338878a1007ed01a19925502a259270bc33ee389e568507dd29f06348d471002443c25ecbc38481a3f13b8fc87db468c3209fb4166e753e9259c62c86fd8f4821e0c3ef8c9fbda9590757a01a1208e736a6715789a96c77a4b8caaa3cb6c0274a756e6c9da47154dea06a4171df4effa9f7d7108427658e3f47e310a6051dc1301e2a3ddd99f209310e954185dafed7a86474cb86b42d95cb16123dbc4cc81d151c1cb576962d6b0ed5c0f51c84d256dc22dd4950dec3b4057312a4c12cba40893419e20642163fbe4fce7e81ccf2410185eb8645bbf60204e199e4db7e3d023514056362ed7a155007e9c6d36dd678d16d7b3f063a403538ac1e8e51377d45e2acb4280c6f60b957f234e03c9c76d9b77442fb05dd56ce785a6082c4a061823ccfd75cc4cb433fc8928b6f4bd125ae3673958eb24f72950227080669d4d9f7d9886e1a61360e9344e438f60c9a879977d000c59b625341b817335fbabaf5f086b173f53f7195fe8b00fc4d929ec6da16d5d36db3a05ff7061e38396d32147a7a30c729511780e6d969d1fcd100f7b76c25a82c14a8d0cbf2829c80142ca585d121aee8117c8570970a515dfe368d62630fcb1ba816050ba06e3c00a2adef949865da7d77239bd193f3f548609d6d6aaa31402a1a437a2cdf4cbefc79dec828c9110d17791130454f5108d6e24abae5360285066f3261cedb3eb42a3a82e10541e80950ab4143936a311519008ace6f998ed38a409b18643c8c26591a7068f4499d450653055084dcdaaa6fe7fa696ed76229f699eafcd679f313b44b925d2183d337554e847960e702d3b2c805f581851e4beddf805d911fbd7d000e9673e9f4d1b8f3205e5aefd965dd03185d9ed41dfd6f0589036cf86ea29fd74e81c1f93b82add3a382eb00e4c42c40861546ef42d1a2444d2d39e0dbab93775362d3d264834be72f8590597cc8f1e9d099edbe78c7ec7beac0be2f136ea56773a17668169f42304469a9142494f54d48f9757c532e156a0946063beef78c6a8bc63d748620b6571729729fbb7e977887bf699931b0bb969f8b8a68867d3113c76fddd399203f9282380bb8bbbc56b16e3744e06449aa8d85a936dbbdad1d5bc052bd7e26f99604937e8e14667c3054b96407373c34dd72f88e7a657356ed8960d1a9a2d99083883fe9828a5409ff31734f8967f87b44b43d09aadf09252718d254baba039010894d5b132005e75e71015e911fe9327d47bfabfe437fb456182d6c4738e03125b71352a4b4dec662e16d73e31fba812bb32874d3de46a4978d796534379c427842da77162c00d4144952c56a5bb055405896352d1b2adf58c03c90e933696253efeef0017eeda9a1ce8b0ff90c4b7d3fa0650ebbbe6adc2d2f5397ab0307409f5e4b362268d05d8ce064e2a992e65dab88fa2f6af3ac155ca767f540e5e1c8bc09cd4c2ba0ca0fef1c5fc850c5b7c433571417cd23442fefce8189eb2b6edd922fde5987ec8b2ab2ab353a05d7560d1eb70d5254279f20b01d35fe24eac42d7b3d0cd6564a049dcb5f41b6edfe176b0373919a839369dd784c0cb39c4330071a437edf84ad1ccb28e54a8aa1ea2e5067cefc8368ba475178e21d0799bc5da1634652013aeb597d0ec91bb5c11e2585dc8ec4bdd158a12b80b831c745e7c203b7ee6df24d438ea9472f53e90a6aa6e652439f163116f60515c7cb3df698d4f55d8da9a3b9b4965dd4d611f2d03ced09046f5090c09b5e6d91840722ca85e81c43a73f00aa69d2ced5e077d306255013564cbea0151fc2591546db68e43c62e83206b249a4fdf0e1c9165c62d5c0f8ddd0264208e0fed6f874ffe6af83c4a121ae5f8a61eff0f761101805b6ee27c4efa8b062635d5a654ca73b51bf7da782c2fd918f3a70ddc52002155928ddb525ff152aa61263422e9c8018d9c0673922edf037aed683d2f345f8810ba07254235e713dcf946806e7cecd4e9e905e9f0235aafcb829b849870b2d22a11b336322296af43d431c707bc199bb256961864dc71f2dd84e42684f114e7edfba5d949cd44d44b5a85a1434d69343bb78078e138f2fc7bf545568ab03224671365c7e2c912f062190c7d5fcd0d678e7354eadda1a8072544b114d0ec1f0d866fa0b77cad2e7d700d4dded135d82358933b78a30d6d26c93790fcd43220c13e24a2162210071975b1c6ff7b08e1e3657b2f9f3de4e98a0cea398bfcd5c95d3edf63e9227e55036335fc94f80c6427025b4817486ba98ec4e7ae223446442db2aa2d009a56af5082ee63e223bc2f80b8489c9e8231e113118a6ed51e83b8d8bd8dadddb0fd48bfa516985120fa82603402644aab0c861c317d7d66ae926d394126c2cb0623a2d78f5eac70d25eae9608625567999949533b91ceec59ec6de03531a0a039244b4b521d642792160dad23c8ab4b0c26a806b8b5d3e63d4bf84ea8c06db0033819aecad90088f408a44e10f107821de8ebc3a5591d8b8759792baeced1f2da4a4be3a76a839530533001e657dc0202c40815308f8959b734d15ef5bcfffb3b31277f43fda6304b8257f6ae05e56b0901114397e2c70e5164441475288529865e74cb622b117608f262ef40e89c88e7060a13c35fd0c4806a8b0e217bf6bd7c73925ba697787e2a258c16c9ed5c4d1c98ef3fc8bcb2eb1f908e57b223bc0d0b893a4997de3ea65fc5ddbfbeeea29a3a05ca6f9797ca45118e9b196f080a5cf2f4d6e34a70e53b5c5150f9e0a6d3588c3a8d20d4000adf65b6214e7fd7b8c45aaad26d4462390dbe57ff584516568129758003c7893074f02d3e860314a3ebef3fd48607f5442d42534cdd0d2976699463985349d15eee99a30cbaefa80a7b33bcc241f9d0afb717ce895c0c3d85a66518214c97bb60c8618f366dbdd1e87f91314390d96c3ed9f192e27e59cd5511c1712fc5011b170c1a7cbca4cf3c73de0f1048d535ecd0900d8da2b1b3162d28a8950a5d60144ccc41f9784a89f17615edeb15ee3e86b8bca3a34186d982fa93e6a1351e61668f64ebb4dd3fa03ef89bc5888272782a4a6eccd55c1f55a014664cd4dd6080b3ccbf8f408efc0d38d7e5a9bbb9ca80c91ffb4adde1990e46504237e5e2855f46ecca656b75b369f5331e4f696b5fc3c307040151ba1956068ecea8b48c1b1fcc310aa77f63176b894446a67a3024ac98f2ac136939196d465b064b854bd417cd62e6b64c870cbc72ace961deff82ae2a5f4a9c87de526c1be1b8540df3215d596d4c4a699019fa9e4e08115168cbcf60a78bc6b6d9535458cc6ce83a52e0c44d7e4bdb1c18654bf3977734d8f10a998122a748705029c3b1c04e51b162085b74ca99ed34c70a8ca09c8dbb38cb57e53b830d916c03c81a7100fcd72f1476e912ff9a297b8f1eb90aeff4956bc20e6cd69a49d084c126c10b53bc2bbd1d007caa94e54fae13c0bca1f539268a7101b0a62ea4aec3369dc0f5d2d884390daace39c6eb902dd63cf9d4af0453396d07c9745a2b56825e815543e0bdc16bd6ccb15e262c75bfd28132fc55db18aca7a213456037234a6c2fe0bab181f79f7dcc59edbbf9a046a0836f046174e81ebe370d43419c5f4e5b5a75dca10a313628d0c4cf1b652433151960ad261bdbaf6123af55994001047a6ad2c57ea692e3121a8f2fb4dc5a3800224be6911c3fa47515bdab7ac56d52624147daf230b5aae07b1ce36dab8c42b279f5ce83cebb45ad02b7e549324efe0a3856e31a3c50df6e52f1229f70fb8e76f72379d752228c04d6f2b4ea56f3e6a250acb829029b7055adaf8a321f61c99767771e4bd3e28249665d975d323e5171d7f1f0fe0e11874c4d6484112c21a5403299e61cd50f2524834af9cf1d97eb349d3865b648bb6d0b00bb370dd1e3d7993afe4dfdac6cb87af9bf484db333bf38f21a2b89addb251d21fa5108bde52eb2dd55e9c0878e088cbb07cb41840a0ec46447bb2c439ada44b923cc5390f240d41a9cc087c5cc2654e2280a58eec074c725fc78615dbac3b0cab585d6126132ac71d06ab48a15f88fcd3471c8a23b73ef807f588303011c35a5e3572bcee60a0f2b6d28cd6f99a2164fc57e4c6e488936593718ecfaae7d1ca323aa2aed62762026ef4e2345faab9dbb0cc21018ccfaa776dbfab8e2024b07da9363d4b4a6ae76f05a1ffa1eee839126054579e335c7938ad1a32459ac24c9c269980bace727c3480d583ef25b49b5796a74230ef98744297c6ca23c9a7b77956405b9ab10e77caf4e40ba634ddfca6375584b47b5a6f23be5be7a52234a0cca5b55c243936c4615b9b12db1447d5c93730d497fbc0b73005e9f439073932a47b9a2037a2de8ef0c5a58953274e9c4701a6cb6394d758b41713c2cfbf249dba39d94ed39787934b4442d70a0585f646c6fc294a10d16f31309cfcd9bf9a5e0d5664fb7f36ed61f50538700063fb1f129141cdbf1d77d250b99945935bfff34c41a8c7a6d3e2b3e699c635f0521873ea65a90e4b4fd932f4f37f6d7c60d131f9bafde8ae2dbdc1bb82457fb685fd1a1932328e7be02de5460e72bc6f51f8c2c0b440cafd76211329b8ff637f0e9791f680bc5996544e127b94f2be100143b2997bd1a54665c0916959b15269c6b199a2d70356bf338e50489a6d6a29f3093e69273febb56c1b475e853c906246718348ddd9bd7c200a1faeb546277ab6094e6da350b446581ebb840fbde4b06439f9110c4facf3cac35cf0915fa838f3f6744baaee1198767b2b791b1440d55b50ee21446541dc8d8dc7a979f5950949e65ed3553abad3c4227fcd54100d5d55e19acfbad05e373db54ccccd08ac34c7e50245613789d21a5b7ef0d61740016c701bd361f04769e444b4e76347201f0aa15d3580f4d3fbaa632c81e23b84049276b2e2b7e9d9640f599cfd2a508615349f2f9b3afde360b44094136eddc3b6b853604251de5b79bb4ca4862c21ec9e7ae701ca93208b091344a2946157ffd5bf279ea289c017d8ed7797e12871d793ecf11506346266e082817e52244734f12be0e10ad887efc68038598c6c3c8e72e9ccc70118460f5109857cfc354e7de7b4c06a1d8bfaf229a026cc035725d6345f20a0994f15040b0b9eee6295f247cf63cb4bd723349f01badd063870eec38da176be8bc5d634b5426550ecbd24802f640a22990c622b153fa91339b32d53323b41d83c4a138cb2015d1b2b89690051ee1b75d18794d5642c6e19db73d7cd9005b909af5851c1d3d386bd62da5664067f4bd3cdeea1d5a7d74bcdb7a7829647935066ea950d9d61f54baec474725a35863e62cf5fd28ded7284e2d2f13cf20e64024920837817fbf6f4c6c42d9b8ff6a38eed8ef8c6f0cbdfe0cb725cf65610985008756b51c4be24931033b3c72399f36880d7a180e71f863f5cb0b1f79a4ca1a1afb64bd82a06fab42e74279959c556615a397214ec9d8eac66640cc83ebc977d5bd4d5eec903cd832ec4e41159c313eaed9924f3194ab6be50bca36150cc6ace527f3fa1bfe4a71c1c15bd6f672adc1500885422678b6c0ad2ed3131c54c1a750fbf2a98315529d6e419c43e3f726ef25a0b1f7294f4ac68692240a194160ebfc4e30ebd8bd8475226f5312d36b6745076403718efb0e5acdac21ea5c56fb00d66e2db36ecb6a51d52ac1dd9ae0f8b9899211c30201c6e39ffcedce860699efac4b847603959d03b0cf00929d8038aeae90b60c0ddd096417edf4452da88734be0dc63207f88a6d3eaabfb17d6c074baa55bc601bf344c6399c35f8c3227eb11dbc1211f88e724caed10282d0593138d344c8be9dc34254175c162f4efd57a02c17d2029d1663d0d595e70071f1fd3f64cffac46477e085b7875ae3eecc11092df572735b41c1d58d00abd93222f46c906cae4690e095bd07590b39e9ef38facbc30ba698ebcb6c21718c5f4415dbf951ec04876dfee80624906976f580b1ff8b55b924ca9807e0126a7b3a0692cfa3c9f38dabb847028e1a1b00b9232090ace239f336f76ae06885102965afb3e05e12c3c73ecc47dbdf07cadc782a263c7d00daf4dfa1b8dc07aad9d7972143257354c0575e8394fa12f59d0161e7b5b616cd4e58a800561c040f77ef55824104cb66455727850e989e7209c796aa9b428425d1b8881c49edfc2a454e592e7166d391e618c9b034f0d1ba689b5128fe97ca064336d3fed300fa8085a38cb651d79031565e361c1bcd2c4db56c50eb7768e070978ea06db971888c3d9e2d379bc0ae7d212161d490d66bb6a5f813494bfcd8c3330ef187721a70651e4fba80e965310ffd16c30e7ea637f1e0a279fc64499ef324bcfb793ab02091091662844e3b347c87893d0d51ed1cb7256d0c2761f8ea8cd4dc04472f72d6633f5f466e84b473bcdd1c549b39825c65745a0027ba189746adb02b9eaae1a295963b2133ce02cfd6275d128d5f6d44179bc14d793c5ee13592cfd474f4b023db431189e9e1e104b5f2dd458a5668932f9641edb03967f5e5812d9fe38db315e10a463426a0f7af12432dc9151d3bfd0e8cd2916a3aa0587cb8b08c45ae3ea89f844372b9b2b83821ef59d4323a6004264549ae507271a975ff4de5a5fd38e09d7186e772ff02a77eefb78e5782f386467d726b4a79ca811636c1bf9b428827f3468a42fdd2e1be18e7054e913283937e5ee6969e962fb8eaf16ce6461acc0c2ba168ea03fabc523c7c74cd5be2dadb73833efe454f1da2788c7565f3d144b7fcdf2992f05660daf65a83c630235af927c694872d2af699dcd0a12eddadadbe56607d9c5f0d5a04e44a46fc62f16772ec63d6b042c3a29223ccfe6a6591271744f9ba66e6f576fa858d1d54168b068469bcbe0dc95d64a866c633ea6ee326dac00eefc13a21050575835e1c5d9a2964c96f700048c49e5766370f8b2e278d47045822689e9c28615d5194e45f51162e4ea9c00d278eac5ff0c152e121459e13e7983e257af138230058c846bb25bef4efb02924e923bb2a63d1e2545dc663c30f840450bf564887530c7fed372d63bd3d8be9c50cf23a3bcd28b6b53491573f65562d91007006a608644794f106fe2a2d54eb3ec1eba7ad5c7e9cbe486a3e03c529093bbe075121544424ae9e49268bd98fd0db63cc2126070e7465d7104da4c3320587b66d0d85fdcde8695443ac4edae06069e3b84f3d50d9dcdb7305e19cf6eb66efbeb53b34db7aae7fc913bafc702b86be443286a86251754cadffe666d63e60e44cd06a24a2e43ecb214e4f64aca87ef9a547c7ab2533a9db8261b44520e5d7ca50f3c780e60918ef16d34fd255acb90ffb0c4df3e9bc4613857ff99f80e3d4df534ace8ba985350e6790c8b910deb6865aea1fee37c2fad45836223e204abaafc49c1537ab184ad7966f356ec603f3a1c56e23acaf34361bbde8da688ade465ef436a2a263a3accf46b9f585f48a07761a9b34361b5dd193c6a61b0fe9905b9f89ca9f7d246736d777c54462f0607dbe1e65d00162c2fb01010e81661c5c9766ca0025e9a5601007e44a78e99924f0c4cd2cf6b1d8c162870d84c033cfc287adfdf422dc12c59aa702c1fc8c7229a8fd7040b143a19fd3f3489b0dd5ae37c150e1327dc69c71e15b090c6be5870fb3efdeb2e366aa8ed487aee386e22c22568f928617809ef6962e33178331d362c84d70127fa1a67dbc706af2e86a325c9c7993bb1bd4c8bb724e7b1812a4347ef03ca9fda55a1af37b25052a55ea909a06e65c793404ba7461e0694d70532ad6c0b34bd12e2b37dcfbeebb1808643437bf1f5cc5de779edc6f90c03a60a0abc449fa0ed57b01debaaec35f3d1fd9c5547a52c9a93756383db200b6720b6b584a7d9bc98572d07cc28ef0f134b247429dc37a08928c7a0cb7440c56bf7fc18e6955791bd06d63d10e32e4d3a927164f80d42e835bc025a69db13fd639a58893bf31f10ffa50de49539ce71279950e40d9f0240c65e70bf9e218bce3f928ab839a995b633174930bb2bfb2448692d2d154b43ad628681e9164c49d3363b7bdecd6cf2c40f288c443a9073b957de9323bc81a0ad74d8c6f91d341b3d1326a92b3f80c17de671426a323f36516f96b977e4ceb24fb959a0669309c3e48ce1c15f9c14be1b2641f38e01268a34bb3d02e3c9efb1af10afd5a040596c9901e78cf71b3df29a318cf79bf17a8ee5d6b20898bdfdedf04b6e46a0c321816e56c409ba6e95e1e1e98973558fd8dd0fda9a826181662903c8f62895c72de69545389a83c22f4af29a13d5ee5263c9e78ded08897f8c6667fbba87810c726b165f3015ea696979f915d2af3fc7316de3d063a547ac6de935a40245de1c74793ba55efd508c589282aa241f5285b0477ca420f00d13b4a23e2371c4c56bbfd9a86249cb041607551224d6a5cfd6002829c649ba431c063630ec76ecf3b754022bc4a6793f3b34b50aec39310b9cff2a73c92e3a6899dd07af72cbf8e5fd4c62a8334e9a0ceaca8e20c8cda0b0379b90b34735a71bd1a85cc6c177cbdb8a9dde59209d2753f34998672b7ad0a7d03e5cbaf6e9002ab81211bc0d1555d187c5dfa5ba532bbf44e7301d5219473fa698aaeaf4e62f8611e51d4574b30fa3cbc7e9babde67ffa7c9c5e3a3b2d1da3f6e513ecba795e3e845c5bed4f6928b7000ad82879e89e9386bb182d84475fff1ed65140a5bf6798bb19684167cd1d431a7233cff02092429e964f28b9cd3a552c88d822595208e066e4b19e28d31ec173976806e9ee00888626840383ba224b459386842a498c52768516da67f351584df0974804daa3dbf53493f6cb94abe05ba3fa8462c21ee74c73feec4fcc4e8848363b149485a52df8434829082efa1c2600bd5eaea58db02d5329931555a7d61cd3cbfd9176f523f6a4caa748e20813409f749a679f12bc4e8262f9cf25a0b697e16d5a652e39c44b66bc99905e12f2f5dfe2a1223db2bdb92929a28bca8f4ddfa8d6e0af6f7591f0f4417e95c6dbbad13305a9f3ac76648552db3ec22c65b8950e6ad9add4b152523f347b910fb06079c98eff457823b0273a9c7eb41dd42a446e138ceafefa49c6993a011afd14dc4d838de178d360db38775b995cbf6e0985d84801a61d4368b3f7ce54d938bf8f4a03d056f5fc159fec0be370ca3f91b58a08862f1b43e4756977629ec299bba6a6c424a315204751878200d40d59aaedd3675c2a5d563971112a10507d5055808f8217a519fa985b8a389e3a6eb504495649d47442203b4042dcc8b597a3cadcc5c38b633f811614cfaf36c00a5e3b50e716375d076d0a226016d9c7f4bf4012a878b51aa63fda7ca6eef3fa65de9d11b24a090709876893a11edca16e369630c5d31669aaac6a75e3b07a9aae7d1a4ebc6817cdddcbdd07232297ec412017d69772718ee3b5d48dc984c27b59c0b45aeb6853257a6701266ea7d1e0f36d1d071383250eac584b1b8900ce50ab547350153d46e46e16e6fe4bb8dbd9e28a50e816513d097ef926402e5f4ee5ec25b474ba3c3710cc3a2a0cb8aa14bdf3ab4b8e0f7542ebe96f02cb83acd1416de14563ce51e0c6ed28aa7d010d89aae2f38fbec26ec1911df0bcb831b3b348bcdb22799c0e433c10721455ce9adf971086418e7b8cf545da25bb42bd029068c8e18382f37f4e02c77d6911f3106c1d2f4d4e7c97869a73989da34c1a65aed18e3b2dd9a302073971bf1f4fb5c4b91f5d84bfdc0b3ca6971319f482a7c7c7814473a0097ee27325bbfc8e4faac60b6a69d603e5f91b7683b107e1be1e7c7196f3fac07236527415e3bfb680fdb54f8a5be7e881f52548f0add84a9069e75c2b6d1e42b8897d3ce162188ada17fa884bb849504e76904101fb434254c110293f29502798f19b45f5a4591ff3fdf66bf29817bf22691dc9c8c73cc697d007a143935d096a110170b3e1916b11fe31ab99021cd4df5cd88cafcb127621bb8cfc08127d37c2b2668e31e53637a6ec517068f58016ecdcaa4c909838f0e86362c97ddcc01553c4fa8bfa9a4a1239ded5e4d483eb7f8107570be9f74702543cd2b79ac888e991555688b0dd93f07af3e2559bc59c3913955cb4ff5172cbf93cfa7c442410aa3818f8ea6fc4d7a7978466c310155995a58290735e572db6475eed3c24ccd2e9ad1f432020ec90f36cc5c3a729fd8844b492a40a43e4ee3f3fa583dce56a5424bda587112dec749aa069bfffa6a077d62972a42446dc034e808dd6481cabfe210862b71fc5de39e2c3de21123b9ddbc6307b51cf681e6cc230b82527653ad68dc5a5d09d55f8a17720766085dad6dff830285161f71140e3abd433d98980a29aa7617ee057f539be4593059b29d770a19ab07ba375b35fd3bddbfe90272cc52a69ab1b22e80bb899b06b11eab5ee56a45ecb9236abf6a7b0afefdb06cb882b33222c61604ac702c1c0e64df040b23d13d3c8ff4227e9abdbe92a9b07171982162b64d8c3983dfbb5221dd4b5766e424ed0da96baf28e494dc09cc5624d5e8518d2c413a83e2fca64b1fddf00011c407cc1bc2b950aa2f2fbe72dbe17b6726f1ccd903f090d04e20baa978dff5821c7a561cc0badccf6da66bb2d6c8e0b161d2f93d61736e4849e1818e57be3b3de124a8216d103076e238e180195a626d27c41839c87da86adc84670e5d0c3321c42bdd258f8002aa0f8e4298f16d009677c3584f7971d4ae1f28f40eaa7576f31508aeafd2c5afd74e686c9c1d241c99ee6b5d2b79de978407b7306140fb4f44522c5c328bae271fc31519f2efa2f8052892fbc6854e3e40154c092cbc9ee5fb2812e08876c00533b1c7b0e46f6997336de4b499947fb34af16c0270fed447b9a24ed03d438160f3cd58769afdd353177fe723f093992ec514f5e3a4d8cd9647894aa126c8f083dce19ec1420caefbcbce14527a498958b1f68f19f4e6fdf9a9c632c55e64c26cb88328a73fb4338bda042a00520c6f7aec4ff39bd5491960463a3a885024216702fa46cc895c3300b8061cc307fc37d8ed60fa048d9b94610211d8badf380d4cb3dcc259fefd306532627a2a42a2c8cea3cf22a3b6b535ca07a0ce639af211e65fe75f028d3e8b642628d1c79797e9566741900d71676077cb373edf92f08f0c863f2cfc8a6a3edc60baa9588e0eee0d0731fc6d9bd8c198b397a776a85e4b91993cbddda15972d0ab8b0350f438079a4ccfa76a5cf493e116daa6a9cba013749d3db109a7b65a2ce18a90bf1818df25a869ef893f40bbf1c0cf2d18be156b3e69df276a4eb4ae73213a87d9baa20baf19dba9b958c15da84a83f827df2a8a59a1effb9cee57c0ef4a0e1d0c516b4c75105569905d663596313aed57f63cf3220fc3316c9acf13f38e049e1eea07ed2f606a029a7047f9cd640b0f646c58a8b4a3b38b513539c6c8f6a2dd318f3f1e3ea64f0ea8c5347f93bd73877a611025c8fe65939c0459d5168a732e7dca2824e18a54febc419ce4afa5f26ae793107a10e62c3a5ff3308113c9c16cc63b3e3ceb5b1756eaa1407d5d228e4d26e33690cd295974b2568bdfa65877f2787e82e05c633df129b9e9ff2fbd27e0b04efbb63337e8487d7638fff6477761b3206c298fd0ca9b667e32efd3e946e5354f989921fdf3ce8272135da42b3aaad197a8310e9c9c466ea559574131a52fda2bbaa2893862d3b85d6288a786f60f61411efdfec4f9ddf54a11d27319225d27c4d842f48646b164b7040afac3c95930585d493a61c140804d3138be1beb16e846ac54baf978fc0a516b808c69bda81d1c8b445c2e39d8ec44402ce401c23cf401e2a3b32cc296ce64ce89a3df6db8f39f13eeedd9eb3abf24b2a136d1eed608218d1042f6de72ef3e0c740cab0c388f2eee759d4ae549248ce657c518bb35b3c9ee7ee6ca4c49b9992b2333f3644ade342eeb78eebb8b2fcf56e2ba8b1f3baf48a7a3ab79a92b5ddc75ff5e53f2be5f99f7ea5d1ddfbdebf43dc6debc38eb72f4c829eecc4b311edf186fee0d5bb237bdb737f3eeb1d311dfc9609dee6e98f11803565decbf34176544945948f96828b3cefa11904fb67928b377b484a46c739bcbbc75988b09d5a1b80d5562c9605d66168bc5756b75eef1765dd4e97ee978e4ef9db9774693f16e32ef8e6793ddcccccc67666632950a635d0eeff8aa2b55bb1cde6dde5dd575a6eacec03c64b6b9b4e1d1ba0dd769991bdeab61717ec02cb38f39f3e7fbbad8e9c8790dd7e92680f2d051522e3d3611d4844f2e3d76394ac78e2ff66339a4080000b03500000000000c2982b5bfb83d3964ae710068bf347ccfc371adf3e2adc7031b238c7fd724bb17bfab1cde6feee231dfcbdc388eebf426795ae417b90d1bd8078e673ce486e3f8109ddf78919dad391c7765e33b8f3b37eeca065e0de0c6f37bc0f11d1cc40dc7f18c8338c2c70dc7f120747ee37127762d6870e0ae5375f7f1660dcacc3ce4b28cf7e2f6f0d43800708e1a8e3f73c396fcab6ed892b3d3dc1e9971f6090353fcd2f1e3ff63f318cf16b747661b7058e335dcc595195f6d75961456eb8a701c57648619669081c362c9a041838dcd8cc78ec7e63a5d53a554eaba9aadbb1ef95fa783329d92638eb9eb72943e73efce2c41c2241bc9a5c733e149de2a1731c6b85eddb5e9dc1bbfe1e2780dd77b8d2bf3afcc5368fec8dcfbfb1bd7b90f37f07dc081e364a97ec3b7d8753de2ddfe5d5517e3f1caf0798455d01916c92befa57b973eb73b1ea6901d0fa547bcda9173ef31cfe03813b3066bfe1bb3ce2db273c32db293c3842a15b0318a12aae4cd857be9becbb83468d0f84bf7d1380d7cbb4ea552e16fdfe77933f7aeee714ec365eeea9e86e3a03a1ecf9bc1abfb1997815757a787e4996f9391b98ce73d7632325ed6a0c4df1702918374eefedd30cbf8b1935cb7be47cf53a91e3bcf535da58a99ea111fff3a8defea621e36675dc667e0d50e9bb36e73d6b71357e381a120e418a509a22cbb1296569e58f1b079eb2dfca3749baf78b07e94ce7acc72c6675c863264dc9a0fc9aaee864372f7ad737cf7dbe44ad7fb6e8c3759b766fefd5e99ef614b8edfcc37f33893e3ebfeab8ebb986b6e2873cf64d9fc371c9267bcbb413adb5c758374661ddf704896c1d2d123a7f86678a441e3dd0cff6e4ba6f1b025cf40e358c7c31434667836c3639783069ee13368b0e13ec39d477752c1b9136956c962570f6757f3cd72abaffe7dabebf46af58efb66b9effb3edbf1445c246b90bb69f5d54b2f973ee96964d463ce78a9f5aafe3263380e77472bfe8719beab1b7128b3f7cdbb211d9a47556c388fb29d3f4239cb429b43efc7da7fddbbc76883eafb3e1bf9fb8c98a3f3ef53f11bae0a15bfa1e2aa1be3cd0c1200327ff97b773c5f50eeee9c54701aa9916ccc69a468bb67e479d4ad903e99544e979f55bae572f9696566c6f358b0f0a9b93273aabb5cd54f1cf7991b16c9338f1dcf4cc7e3dd90662f66e55dee2b8efb514c4c4cccb79a1bbd19a4baf7d50d8be4d5b7c9d5acce9380fc4940c66ec5f7c51bced0e96fa5558880fc3d765dd7c99b801cdfddefa96fcda9aecc635724c7fbc3cd32780619c3fb77431ad9bbcef42ef7d59dc1b4a841499f3428df23da20b35c711c667905c6a1cd2b1ea3e971c51b29cc601eda9c7a8c39a19753dbb6b56c87d96e7e1df51b54ea8632afb8a18c99472155c1e2862d39c665b83fd8e43eddb6c78e07b58a97592853d961c186d2a7918e8ab66ddb3a100c71c21a45b3284b9f98d3442fb11ae5588f6ee0b8e1a19777021080bb7de7c6c8ca374696ce8d9175c3c5716fdc2ca80809009909cd5ca71b694a5924a1748be632e621183eb43d72e6d1449a2c30738fc121c8ddc3e15598fb27b134d284a26c73174d68926bfe3dd33c9309b1f8cc55dcb04856f1ad85e33a1dbfcca71c6d1b77f9af5b31ab7026548a98f2cc25c41b798e3331b1e15c4296f2f449b7662e4f8bbaa5e2dee5671384908eba15a3342128cb4f2a55b23cd7e9888f7951b9a84e2a79d565c7b322f5e190e6ef31971d0ffd3df5b1b84eebf4bd134a8392c77743ba02e788f87b4ceacea2209d59e069f40206eb20f87d9dcee34e4cc4ab01dcf80def01c77570103ac7f11b701047f8d0398e0771c36f3ceeb49846d3c8c52c9a46b3684299460dca9ab3b8e137dec30db8450f3a18077e8f1cf6c83fe0e41a16352c58b8f8cd63b7838bdb3cfb77db0508de17bfb161bd78ec7860dc17b799b05a9c050e6966f12dded0a68b2bf0761c2fb2133fbc1ac08de3b88f1bc781871cf1ddf071e3388e030f69a4c992bf818bec7c1f0e7be44f763ca9c72faef8e5e2b1dbe17bcdfbf1669f4711af7674fe706c90c5556771c1bbb8ed73fba741f916177cec7822ebb611b7858aba2569ff889ff9fbbeef47bcd09090114321912cb33871f23171c2a4890464fede4343310f830835d8430f5b28472981993fdc23b3387112db4983f2cd446661e244a873282806db47ba153652962c9320e9a06e8511292be925b1adcc9c223b12dfc55bdcd7b0481d67deb88c49b1a871815b605cc302dfc7893b7c84f31b2b1eb19cb2e2dbec78e4950663bec52e85c31e5962e9560c7e68f3f7d0cb371e82f9fb670e8e7fcf374ed60d5fcc577c8bb972c595494c6ccc439994e59506e563aed3386edc2e5b118af9fe25756bc5e5e595cee99d159d8ec6924a83926501292fadfc40d12d7959a55b973885439bfc3de686357c64ecf1d6efdf538f37c4b951c6efc61c5b682e8bcb4ec537169fcc5fea31379439e6db7775c4fc7d9b1c8b1bf3d80920e60f7befbeef3a62feb087c3f89987f3287ec553d78bf16e91bce206b9320b9c152163cc5c75c31a9467ae33b306676664643e8f66cefc992c19432fa6c3d1fbc54253663138b4d6939f3fff7e1ef18f87432f5b99ab7098651f0e6d96b138f43c999b40100747c66629333b1ed5e9575ce6aa8889f1baa5c2a1ccaacb7810c49935e6d16405b9b2eaab6faaeb5d8b0305ab7a6855d7e979e45d76de67f09177ce7b0a875e779d197365fe5d15f72ecc63c703ea2bf08af01966f55dde53753cab2b7f1daf109923fe0175191c16c9a8af767446a9705824abfeddf0f97bbc317edf75e6e71de6d1e3515da73d1cd2ec3de6df278425284926b12281909088ac147df17b188f70f2f77d0f71722caa9165d686def7ddd4e385b9d73324a38eba4e7b3766d5ed991975d9f1c04454eac6065b1e51e741e1acc196eb7413b5fc058759cb5d5aecaf9fc27337b4399c471995b5cc47bef00f36f409942f0c36a48d336a64a3e51048db5952c41e4159ca1043590957f216699db3ce06a794739e25852d92512d5952d84e87b6d11b0ec9d2c69b39bfdd962c270fd81453605a4651f4f2c02a9632a8066526969138b071daec0a5b8c36cc8794524a67cdb09a654a334c02d1c8a0380a66c7b22ca3f49396609b31c618e99c9d83c5cad32cb322bbe9176477db491d1001a4ee146ffa396038d5c8a2144f535884d239a5300528421305a51ca340b940672b509064f9688324400443124880a2841328f18421ecc4c892a61088dcaf4179be3ab135499d81edd7201adbaa03218da6dadddddd33c6ee4635d8ddbd0292a9132fb25f66d8d55863e67491d54fc9e4eecbd9d194b917bacaad619a66b5aa5934f164e2b81507acfcac2f19c7451b229542128c8e04a00945024842519424283902087288832921091a9832450c4c98c205a02954d050606b8e51a6204d39ca6884c9310a109810658a1046f37336bd62e9159be122f9862658c9c92cdf2c573b62c4f48aa577f62401f5426d49bd509160e5279ddd00a09f2d634439cfcd5f456c1b8c2c58f9ee4c061772f743292428ca413af75f3071634abb76c0ca7cd50e58f9964957a44f74e5b04cbbae16d41668a52ab3715401f126e99a39f5331b40dda9580158a85cad62580127936657ab1856604a9aad21b145d993d60bcb32cd6432994ca5d20b28a050dd9cf3a217bde69cf3a217bde6c49d694e596bad4d3f71283829ce8ca6b181ac5372a793b52d2e2f2f28542a0503a3ba542a5555a95a5c381bfb52eb8b28a45aa6ba1713ea04d3303027944db5b878def4bcf6525e0c0c8c4ae5793131dfb75a4d61c50a95caf36262be6fb59291513133f3cffff73e998ea75dd0c0769531df4a4666252323a34266858a994e47e7ec05142ceabf7de5bdf19ab13dd3e598f95674c88b693a9e14d8c6b3a6a6a66b6a640d66c18286a6a6a6450b172e40904517720556e62825043957ac141064c92d93bbb5e2117f451c64660e2877dff3c9fd8cc60e03134c28caddcf7d908926a0648e663bc9471a204282ac763c638f19c33fb0902cd32c2fb1531c0e0112e30dc541b2897b66beae5b233715d283dde4b6c960d35c73352595979452ca1916c9530a114e72d32bcc78e7e5dd700f964be71ea30dabedf2ab1e3945eec13d14902792fb40280e22b1cc7dfbdca5a1c41e730b99b94762c9252da4f8eac1f036459e3e3e447cf2fc0b27b6e7450dec8b1a5820ed937bb0e99335398b364984a20b808008fc40ff0d2ac92de50347729feb8cd21df294f2205f83cc3cdf749e8796d3025bf69ec8d15c26cb1e3b6b356b356b6ffab437951e3b1e6da2e6a433d3ac04c928827226857296655926a7e4ec54cb613faf5da958f11c32affe72f0326f9466f24a1214244e243d913bd9259d4892b40be4ba4d9bd257bc7eae601c759752a9b4852edea2d6efca4cc395400d66420d14b58084254b1be99f46d2495a49502fc9de4292caee2cfc211a81383db033cb9e4925272c32bf1e825289cc0a15cf6ae4189c1c7363700c8b189a989a98ec31df302882c0b0246734c8ac5940eac8b040040494334c763bb06e071a2e03d42d9967078f955e94ceb3e2e6dbeb0caf97312e7f7373195ed4c002093233ce65bc9ee2cc8043191887de7c7b0f9663e050869bedcf1b163227bd2b5e6fe9f2ca34985d022b9ebdc706f370d7e9174958eef52b5efa84e1cd0b7318577599f402e3a62e935e5e5e5c892593490d66331b9246c4a04c0ac51b4aa3ec0029d3db1335999d5eb924de648f9d94d9b116fa791707bfda28285d9cde95a42e28485bb878965bb0f08e2cafb84ecfeba2046625b004d20b71817bb0bce22d5ae643a924675f49a3a43995744ebcc25076b9442863d239339ed59c5d1265ab9cc52858ace4ec724ae7f44e764967c6e5ec598b679757341f1d35174088a02e470b2c64e6159fcf321a1c5a1638f4e6310e0f1e87387166c5a52a642e77bb1937e686366bd75bb57c359f82b9ab89736429ee2b1e3f5a9ee5f996a3f0aae51505799543e697f91617bc6ac13d58e65e4f90ab923c3b1eedf6763f8133f33c6bb3bf18b2f3a18c0e0e53949745a326b269d612b6b984986f3177554add95e603e6283c4475986bd7077d0a0ff14eef42091905232632878ddeb8d3ddcb0514dbd7a147b7eaba52152005fcd02d9aaf2f1c71f9baba9216897e699a761d346b3a38e4b095baeda79b7e92394caf3dcc7bf80483573e52471dc5bd763c6c3f61accb61525defa90bf398abc2be4dae5e2c3361d6b6609f7dca81dde5da75b11153ee3aeda2b9e8ccabcb81a9ae77ed9a324dd3b41ee81130261b326bda36396dc7f62ca439b06b12f730efa94e7f51ddb8839dbe9dece92db72da757da727b3823c7d09cdaaed3ef1cba731dc4ae61d8638751c9d88b1958ec3ab33bcd9a764b3037a5699aa6ddd26d685a942b5672f6aec484128cc8ddb51d25989b3abda8fbf0711d4a170707b6d4e9e8d143cc5178e5831e75141e62a3695ca73030366c742bd2ac7d4bd13458c25d8a763c64ef5e38edead023670af64703b43fc7ac611f0d5ed7c1e5e2d0e0f58de6d0d2a2c35e7bcb29cc8d3bf606e19d7ede20549fdf5a6e77b5a7ee90223ba9c76eb9bcd4727b971bb6a078e8aebd0085891c618fb0f5a9200d28bd845f903aa27bec7674ee30d8e0d56049fb751f3ca50e07e9ac3d769a06ac0dcef52de3ae6b0d43f32f1fde559f07ab604354be7ee82b3943f9409fc22b1f1e2a763dccc7e0950fd590d4552fb27344f7d3e949b61a6e79f71e4c7bcb4bda5d5eba0b0e32730b1632b3761d74e6a6dd86ddb5d3dc59c3749e2e77d376ee5b77bbad89f276d525a2a5a0085b28da3962eb2b3e503a348debf484d5609e7a911d1dba057387702f20820896d82102f301265cd9894d23ec2a59bb4ec35cf9020bec400aa11d9d89ba71a7843d75e34edc416538036271831825edce5100452f14d5212f3c91c3787442ea8295285e30ca61ccd2a74e4ae97574a602b88225b2a24ff61359318302f0025064c5ac43bcd921b2fa54c87d27f47350609f63942b587218a10019d9220d48051bdab0a1c30136abd3ad28edb36c593c43de88432fecf21e5e8e605dfef2ed2691f345b2bde1b37d774ff67293a636cadb752687d360966919ce7ab80ca0e78f34d260287d6cd7286c59b762c419d6d69b74ce392f4a29ed6efc991027b7ea254f19a534d6cac4267b66184ecc8951bbcfee7fc5ee3ffe2ddbf43fd9b6d8ad963debec5a6be51a9c75ce06637d1673329b6bed2c5e57fd55af5ab10b67995e27d8e29c73ceee8e15cc3544703efe316762178d08d6a0239bc726e5917bd249eba4ddb30bdba4349ebc1a22486f04204f1a11ac4187766b228d4c79e47979c5b0c600589d3cc030ac2485bd728cd2852308d185201cc95eb813e5388bf2ba90031e2304715d1002010f94240e05d2a02cc7a164688ec030ecfa801248701c5042051e5002c97565dd22c7284af4809a80522e38214a1750b04929b96085460e178aa0d3085bf581d73234336cdce430429912a954b99293c398e5892cb94fbba1f8416e2c94203716a690851d3cc98215723f071fe47e8f273e68018adc5740920392e47e04a26c410bb9698e5dff091b7da4719162439c7c8536dd0a6942ce66385cbf6eb303bcaeeb3ad773b88e73bdc6d5446cf2f2c63cb7b9c38ebe7cf46c8312ff8897a778c5a37f48bce28121109570c227d7671ad878d07a3ad872d05eba2b8cbb76eeaeb07398e4aea4dbb31b77b80de7907fa891af4b79571277560f4e567de905d94b3df44e86b3171ad84f23f9c413199f18ca4e4ca13ec1822c1f9f40929ff0b9b890e5312792206ba57546cc1402c962e858b0821831aa597d54868a42769128b63a2fbbefaab3cc3d728ace4472bf6f678d02536b79a234d6dc197655ac361d03152fb7137d39415b657bc3b696255a94b05db13cb33773c0c96a19a25e8d491b6d883457772c60bfc7705864932d866b42a1f5c26ac8828d4430c2951c8b48502587efb0794d542cbd4cf3a52ec795279e5d0b0f4ab49401278a28112c0a872e1c59b1dd0eb41fcbe518c5092472c8fdc009a18c8dc1663946712228879c1349ae905497907959c659941486a85ab08265384bf00015baa27060e2a08524956888c6c211ad81105940ca8a10223bd2ac88a459191694342be6d07a9d59ef5c0b4a32ebb672051564f9900687880e146951aee04344c4383c8125661856ba7a956394216ce022428c33c6f9f8897fc4497b62d7e5c939e79c3473ce396966ad95ce39a120d54a75885ab1469d00e02ce3c9b825d82ab10bbb2a16b5d8d2f32647554dccc932abd2d191afc7389d206cdd4884d862b768a457ad2d7f409afb190a17d0810e4e40033198a0bfcdcf994323099ca143db291bf81058f0241645a328c3ccea9032e38010054cdea65b33e3ac70d4449e566890e7dbe6d4042151e4b3d33b7dd9f1b42d326527d93821e4171a897e38146751d3089366ce390395431b2ca8d082242451aa80852856781225e702a0cd8c32ab5c20cf39ab2491270ad560144452e0c1142958c107619821075f4022620a2433c8c0cec4f1f1f1c1c073c8a6d801123a95b073ce6850822db02198e5147e70a6e064c357bd628c317e668a29c690e723bd902b772b467942508e1d5d1cf9e48adb49fddc845c2041660f96eb55e9b34cb7cc0206b40a5ba49142b2bea19039af76f91e2c632f9cd81735b04066909935bcda01247baebf9c5bcff57a7136be5d61585fda34ad1eab3d43f2f5a8d3b567e6eb52883092c3f8841e49968d6e9cf62dbea8828d52446085661cba15a584004aa6f43a748bbe47677846831a96b91e4e83407aea7b62bcc16990be470245c2ac620f4335486d1aa4b51fda54acde15566bad33622dd8b53f4465fa0dcb4ad85d95340ccb304dd3e4f50cc3640f962560bb75619c25450c24489c371432739c7fd1010b24e21e2cc7639fc77090992fbcca72c542668ea72c471aae76643e3e3e3962dae0125b73f2c26a95b4db5157d05960e20828c8ccf1094039468e519cf0842c7f75b783a4b807882cf10b7e54ec64585e180f92caeb959a80e3240e71b2a474ca7bdb95d2d7d739e79cde7561f26a27c8a31ccf3a8a4759c6232ad84a71a4619c93c69b496ba49961b84299ebf55ae34dc5932a60524ae99c3348ad18889683d44c29131327c71969a491abb3ce7adf609c6731f479d5ab567ce1648aeb29a55efc8f967a61af5776ea056cce593feb7f4c2a29957d1643f2726213af76fce0655ae4a22d95567abbd6f82183d2e4d8f990f3e21cbb15c53d349ce1dffc9ac416a30dd8452f3a29a53407cb326cb2e88c94ceda190822e06383d02c29a5320a9bfc1785bdc1a1cc37dfb6d8dfd732ee30ad56ab9b388efb3e190130705824c3f87543991f306f1fc3e290ce3bfbc2a17d8c1b18f7013d5f91cd8df9013d5f51bcf99ec4c0306c0d0c1b03c65dc5b8604c18f6d78df1ebba7ef3633ecb3730e66d8c0b9939060e61fc060b99190656f20535485fb2ec7858edd08ebf250dd24cbd60bf250dd6fc5312146df89ec49b998733af98f98a8fe84b819581510dda15d85066d665e0b81733b0e147943fa2a417512c51ccd2e930e5893fa278438f73657c850d2c9781ec90e132eee23a8d7365c0b1c19b8b74731b1b9b18df62e0150f188f711d301e03c70663bc48bee67c8c1bfa902f19ac0c180132c6cdcd75e6cd5ddde67e8771873448dfe28632b7f88b9a215ba3414a8f1aac39fdd6b2db7457df9306e9817c4fe20d7d0f119fccbac581ad798bcb8725a299e3c30cbdd0e6290389c7097146504a29a5945ef72f4cb0f33ebad5e2f4b3e63e803535c7b1e6f771d6fcd7992eaecc2d369a1be2b0b82198c3ef86debda1fd9db9615d71c39ac34f49a657f10d657a991bf6931c7e5fcc0d239177c388a4baa1f481b9a10c4a851f51289172f87d44a76fd121871e323670e816fea18d328fa9812349723dcd269ab94bf366a373e80e3d0ea5e3cc9c1251b3e8299d354a44333cc13e6601052cd80007449065c90ae8b71f2ea841fa1135484b4499be64445f3aa22f11d1978a32ec82bdc206b69443bac385339c7d4499fe33fa8e3ea2afe8850f8dc6e10af23c6d7bf783037cdca27c68102b61258c45e1838f1e3a481c1a04830d2793c9a45bcd7dbbb68b9e103969c24467163d2172329b34d893c9f653ef9ecc279406fbda9c73762daf23d2dbcd1b2a00e3e6f6ee16c993db5eba59d52e0e741ae58e2787b64103eae0c819d485e0f7884eb6e51b0e69de7862eec9dca264300c2b69736e316655e3b46bc808244982d05b14920695fc1c69b0e72c619396eef5c3228bd57ee6946ecdbf747779ac5184451de6d74f3fb376fe20987a38bbd269c00671e6cf9d327722b933c99d4131777a771a31871a049a4a1aecbf44d19eb7bd4f87b99ee47e777f6a84cfe1240ab3bc1d9d2814a3069be88926e744711c2ae4b245991e4dd774aafd684b1aec87d757490db606d460bf7435245a124d8916d4606b471a4cc28693e8277347ddd28ce4fe247ad29f54a20d31f79c134ab768ccd9e9c11226387842054e60c1ceace20211444185221021053bd30a4d7c604082241ca052829d0268e1064da284a0094b886267ce1d4f46ba082151edd22f2e477d661d4f0aa561d74a2d9737f438abbd05eb91b9054b83d7ccf279c0c6cbd276cc6259ba457dba558d748bfbc98e5ce7384e6251d8982d58cb228b85c10ef7d3afcf6062b93be9561864e6d3bbae0a7578498397d00957234025d8f0072eb78475491f2db1a7f711908d79d523ac49f2f53e6ab012d5a23a549bd4245549b7ba5faf41dd32a152ad253607b1150a991975975faf3c8836f460198543d485ccfcf28229936eb9e0f83a24ade43ad4e0757d125d9f45d7e7d0f5d9e47acbcd4e17c7746d401a4fcb38d4d54443358ab0f2d85037c1809264f99fa20632ea241d64a585927a084b1d4a9a39b1e5cc892d9f64a522211d1dd5a25889f205745464254936c1e4afa12617d0ac4946498a8088e4cf1016a1a4202b40473f46a8a4131579f9646924cbc70b28cbeb08965b54967caf64f9ad4291236e1a797a095bab542a413327b6063473e64fb37ce4717040f0f73c6baf20a02c359f2c97e47036994838a02c8fcc1f23b2a3b021972514f92f60b0f29c1624844d66734e2a7b0a94ee29e96d2872ce196b956d9424528c095ba409abec492bcd686696690dc5680a2683759e77695a495e286b6bfdc64169a39ed247218d8cf4daf04c4cd339b11c0d8b204b1b8a1314b65bd4444343136d887508cb0116020b93a3940cf0a0f40323b4246c4b8e5232700227ec29472919383203cf6699b55936b38513c86436e32c50b36292a0151e4e66240a402b9c1a38336a4031020a1315d6485109be220cc918b18108628e60b4328202af89a1cfc80f11aaa3a1180c5809829912e461208a0952484052618007a8258c603080c4102f46495218502202972849280c7800042d472f194e184060339b7136095894e0e36271322a10713ad2ac98b3236956cc56091f301935b14040d2259d2e9004101785c874011588609b52d44d11543a0210f704128d094d362424c996482ae9a0045813479a0552a0e492629449b1c010403998e0230508721861c80a1139a2fc08a10539980892e2a38222722c618a2c2dc87240c162696eb92ccbac274395dfc400bbfabab0e650323254a11102511765072aa12986b8c9aa188cc106b9bb1b8a4d9679fa10f3751a5b2b1e357b7d5cf1a031b6e28d88acc8243a894f2201b040893699de1fea7ce2f3a0f5b2eb34ebbe94bfae1a4640c958ef73268532cb0980e548644524fda324b22201b02c89ac9824868d524a29a594d258b596b7e07002d995b9bb1dfa19cd4188d6f216bcc25eb36f8aed6ff5b2b3e55b4bcb75f45b5e246b59a665d90dd2f984679031ba6bd24783f240b86f3d1c0ed283e5ed326f38ec1dfa4266865718de517124aa3c8809c10ff70b884090b880066d481c4cc547b3644e0eed23dad02381b0864dce01680eb080032c600a413372321b4eb0612cf241be63951e94436115c3ae08052024b9a3151391143fc861bc2285929a069248721fbb2a074e6143cf53004fadb556f00936a4a1e91c54b3644fe7f99ece14bfd881959f3702f1462a01ac11822157c3c6f6516d738344f28c51de186fe47764652492eb55eb95dd1e0a10c9170e27407798bf502d50028b11b980cea93159f4a62db43103c72602d186fa781a4aa573e89cac59f4110b367b89d3bebd70a617d33993c9f462baab1793e9e5c505e3c1b0969b830eddea1e6e386c39dc1e90467adda25ecf1bec745aa6b39d7dc761b7d793e9ec662faa41ba9ddb4c9dc9dace5e20d6da6316ffe89e65ccdeee3874eb74fa1c74a0a7ed6432e1b08b9da7031ba250ddda4eefc99ca65b1ba5b7e95669466c1067f7c590c5ae173bb0567230d8e26317f16a473c761d11d3d8dd550e31c7eb8839e2fa2259763a7668728a2cbb495d322e3668d360c46162cecf1973ce3967cff5fe85641d6d106267b44188ce94b1af902cf72b15b2daf18a7f64b92fe583f46039622159ee39255024774f908ac106e7cce48a4a4a299d4479d2ccfa64b5a374ee4068967feadb816479c3ab1d250ca473e925cc436208ccbce241b5d8f5f0cc98182c06c3b81486a5b0542ab55da9d495baaeabb73fa0473e3ea01472376d2fddd9a621a5ad67e68d26730fe8919ee63d763d3c2e3037c53cf552eaa77ba5de73faf61e0a702792b70339e1201c7799b7d24bdf52a7eb8adbeaf4d55337e62d9e1ebb2d65c270e93962cefd3acc0db197beba3d5886f9f60dbb42a69099630e7353e76e0fd683e5d333d536b90b03491de33090f8186cc27a80c8272029dc83e513bebefd450d2c573a67c29ccb9c2edf68fde9a2bc7832954e26d4556f51a1502ad45f5e50cff28b0bea9c45c94ed5a95287c142664ee110857bb0ecf2172c6466175b2a6d0f4bb2547a8f0d088652a65b320dca1e9a06b9edf66c3f99bac71b79baa1cd59ee30b7bdf4ea65dc76575c69bbd5041748a9542a699acc3a62ce1a5c4919234da31fe2ac7c6459731b765dd875d18b5ef49a76551c66329b4788a60e7b97cd1c8a573eb8d3391744b021a5382b1fa6eef55af76c296ddfd834764c257c8f98df2e3cb5d2e77b30f9d25be220339770b8e5faad4e297163bfe44eecb0cf89619f189e7d65e5838b4da39eab2b1f9de9dd8becc46e451377e34e87f545832dd268f598b63ddb7049d3b29776681afe91ddc5e55b76439cacb95cc35b368fbaf4db8c6e81e06f6d96956e53ba38546ce9a18d0e1c3c238c545a5eebb9aef1d8f9a078e5033564b2265e4d3c64fee54576b2c9aa9969935b8d398f28fdd25e63e670cdd2e29434a59b5ddae07c0933ae46ae49426d46b7360dcbee75d48d3b98cb0d7fc0c1b9cd8c97b4af4aa52d4a0124839a1455b924d72a99e45a9fe4fad2d60abd05d42ba05b1503b9d66d5a79a459f51ea069d4bbe8c101ae2b52c97e806e69da6d5193a37669977669b4e3d1367c7d3b926e95300470f28febdbebb54bbba10d8ef6a8653768379c91717258242873dcc31e39c3a51be26499b59ef787914ace019a550f6073602395bc71af59e6d262e591d25d6e68432f67b7377c08e64c1e89d9248ffc8093b1efc82312a85bdd2b86facbcb2b96479a467dbd8b97bb427544b0a573dbebc6ca608e2d4a3539e668a83b44e30015110c6187088c0b4c30851dd4bbe3d97e63d328e130cbaed71b993ab5c4162f650824eea8a50c51246508a22c31308104e9c1725fe6186fb267558a086628a05b61b492e7fc0f0ee8960c0114791e87393190432ef3b021647a4d6c36b3cc5a6fc8264d245240004596c9510a088cf2768abf12885e899443da23b39049e32fa744c2caf5db49e66d93d995d755b1796c9b3ce8fb3ae87bc88be498c9ab336b8cf47587d9780a6d52892598308a81141218c921953282244e72394ac97a04537248a58ce04996a7b808182960620c475c72fc01125c809123136350a2e39079840e78a068c84c939dd3e910d390d35b7488d0f0012492b0636fba44a215b61085a01dd35b2e9117a21d8b8fe87eba445244ec98300e4da3bf6d4db4fc25b1b32f6c66d78165b2746de0c0712fc99bb26c52ca1f092c165d8c2e966593f9ecfe909f3ab4e3158fecd875641a0ee5851c7b91dc3dbbdd65c4a530880d1a52d8bbaaa7b9bebb2b1f3eccd885a31c330e0de2a0434a20a40f4d12368823e68368b9bd0947eca3c5e21729b0a1fc09bd9923270e2f71086614176c182f7d6a6489c322f353fe34f8ad84430b044e3dc166df6892b071b2ba1e40b4bc4e0381617740831d46287e40c286b1c88a2644d66101d18698b3ccf3e617c0d08126b2f8e00b4d50d991f7624e4d165c1085941d0ca1842576525058c288139e7c20044eecf0d0440e68b0a404265802113b5202c91f3903917d0e659398a026b965c87d49347ffc303104e429ee98c0b0c51ca911528810924244122944203131408a0786a40801254729423889528448225f394a11c288cc0528534a8dfc9860ee907922337660aeba2b1faab70a06cb2b4d836ef5c626628e248a8d94a9340203021bb6510e108da5ca8421c2c6bc4d1f8b448e51ac58c932171d4922f9c488a8d65a91aee04c37cacdaedea75b61fd3220b860ebb1631f72faa9e5662a683748f35091573bea312c7d1a945d766b14845a3946810296dc46ddea6efa9565195604a429946658d0697c249577adb50675eb6ac91736b978c2862d44b96d481fe6e22338eea58a8f4e31a785228bfe25d5d2423459f42d8fb1856ad49566d1a3b46c0893a6a49094e9a74fb7e6dca1d732a1a1505ee99f2c8b7624530f64724e30d3f7118dd734d2ade94323e9c8ca154a3f7f3aa777e833ec1afaa16f239a09bdaeebfae9568c22052699ce649a8485b9eaa12492c1ac6050742b6c28999e5e2dd442d18698691bbd14d9b07f905238cc70683b09730cab2e8c8962a5a36e3873d842dd44479129f7925462c3270a579a4acb55d7aaba2a60c003a19d4791c2921ca34421490e658e51a460244ba24d0841487d654e23f2488314b4911dc8a19c02d6eb9247ba555b6e3abd34d2ad96d3cb9f6ed9d3b56fa69bdd140ec9406858bbf46990b65cbb2d3fb5f4514b9006690b6e237b7b0a141b4aa04c2550f71f44329a4d78fb11a473f62a33767fd41cb575323d763c4ea65f1a100dd2eb576759bdb14120563cba05e4ee51fa642b9c190de278a18d3a27120dd1aed2499de508a55060928d523587d3a7ee84f327d3635745924fe8aa860dcd474ec954e3400e25954cb51f49645403c98ad151056288f4a934f7257204f7abe3293dee6c573e9145128a34da326ebbecb8cf8e87cd1c9645a52b89e493243ba55bd745afb0ab64ba812b0a2d9cc003576ee0e4270c3b7509222758200831084306bcb0531f637d164bb4c59055d7c5912320d170824f61c336eac1019345bfea41f5be0a0f491dd921a2a2073baa37cd103b44a40ca6b0d327326347f5d8f5a080c9a21fb2aa573deeac7a88f97c0c1e12b790022b0ced106924a61051c48eea130f2152820210a610851d153e02bb87874c131479a1c9ce10227d22b109507081949dc6475cd769f9446a57429139ac76e8d02095443290c4221a4620897af868d0060c5615a45b15af7c68903ee60334127d5ba1ef233a5f862094b4d3d76917979e410a5bac618da0143d2172228da0143de19ca066602f269896d4adce39710848da11957aad8a66a573625eaf5da92eb95ecbd2ad2885033fb9bee45332525ffa69eca253e46e1b9b06ab4ca61ccce90f34a04d0c2258fa9006db3458b92946a69b4cd75f2c614fdc9406c32b49339a399c9366d50b5f4d5a5e4f8fdd9392066be9dcd5a459f515875966ade71dbcaea45c9b3458eb53536c7831c9f59452ad4abca91a5283f5e53495a28749a94eb1c3a8522ff7013d1a957853b376146fb429301826f5eba1069352c1dc950a86c2dc6a02d5cb0b4c2aa43030982362d22dcce51b8e95503392a7d350ae34444e661326b90e2d6910ca131b0dd68ad360a562bdc78e076649b79afb3215ac6634851aac9f46f45c82e792066bc839c1d9b6ebc270fdf52f8824991ebb5710487252c670180450a6475d67c6a06ed18aaf250dd65ff80a6af04ad25db84683b573cc45dd900ae57aef8617e5b06042b9bee5869ce98632f974c33994ebe7922b89926e69afbf82ae25dd2a3d7b3d0df8e3fac464e7eb9a4c97c974994ce74e4de73800944b4ffd0a22610e834321de7bb00c73154e83301cbd30b4e3a1f4d4378ceb4e0777d3e9351d15041ba2509eccbb75c2ae140ccc5b85dd0bc334f51ca8bfdca26e68ff1262426676394eb7b80629b546f4267ae1ac3e877a9cfa1af5a5ab1db536e56a460dd6b35660431c27b91ea75b339c742b3c21c111714f8cb429da51b72e4c4af9d3508c14b61c5ca633b01de3157ce63ba849b79e583902254f2a450340c8612fc9b387343094c376926736caf395c63e328128e5b2c49dc436b1b1c119914662c992932449526ff692f950a321996e282966d4c0e260c34683335e7ce0053bafd3b373dbe09c86cb6a60c3d3d8e0681ac8f3366a6402074484408a1e1c710128458a78c2101640220461400210a5494c1c3ac10669124626ed393f73981184dea26a34382f4aeb2f2cf3ae13c954dad0c418b2b3da99813c2f536badd56405300c31032d0055c1c80e2d020e9c24d18223357882ca0ea5f471027548d824b745066c91267ce193e9af5c1ba55ce7d888a759a956ee3afd31c18614090464f28f79ee1b55d2ad9658c32e291575ab5b92bef4844a0132ca510a10510e4b394a013a924b4f3ac7c81131324e5f9a19f71a54cf21f32b3684198109614a821a64810d4b3f51d8f0236a9229d6a45946305043cce95504758be6f42a96a810ead6fd0aabf21d750b9f7a9e858939d893c8a287f9d5ed90af15af78c8908183cc1c53639ec30eef2f3e3b9e1738ec9171efb4eb91483e3e3e5906fe96c45cb00936fc8cc8b8f7fa987f9311f342c65dbdf064c8b8d504de9501c4f3bc4b4faaea63645cc8cc3270a8ba0abfa086977e660e256a167d9659eb792ffd645ac2aa64fad3176c48899e647a1659eca9490e4b44ac1dfaee48b73050b72e926ea992744a54aa7b5ef530456a909e1e3548dffd74abcef01a455818b7301e66d9c27868b385f1d0cbb6e63162c488f18f68d6a8818303827f871fd10adfdcdcfcc6c6c6c6e6f562491aa4ac16ac16ac875966bd45e865196478b90c29542a43a58e3ad62463493025ddfa4e8f05614bbac5e2f49810660436d4ad99531a704e4f8b649cae4e4fe9e951b7644e4fa974ebc55d4e4fab742be6f4d44ab7569cbe868bef455def769506b76f1ad772b78de399f9e6e2c5757a8b8991b192616f5155c6558f9d4e579587735df61e0ccf7a55e64aafe46dfe98fbfc7617b7911adc2eb76bb7a16870fbcceda406b7eda806b76fc7b9b4c1ed322ed7e0b65de67a0d6e7f71651adcee72dfe0f6984bd3e0f615176c70bb6e5863c695c9e147147e59323d0db73659f9d4a34c2fc30d5741999e75c38a25af88323d78c32b49a6b7b9e10a29d32699fee686323e99bee686d751a68f714399a04c0f43862883392c11adb0d7cfdb6d930675ab052f11a24674ab3b3d1da24ca893d93f844166b63fc5e86c0fe3d79d57f7794fb744b2e9b1d3e918b7479e82cc6cba89e6d2d5c1d4e11c388e520a234610434130c9d3de1e22154986711be3f6c86c0fe3da4d07196b73f0d9b26ce1416fc088ee96bfb88c8bf39afbe22e6ecc47827af468b05b540619677dfa348bcae4f91c7afcc0ead0e3c2c1e88736caf33670a862e7431b3a387c466464be615cdc899f114aba157e4d3e27dd5271faaf49b764ee0c5cc933394ac90012f269de36ee5322d3875d8e9f51a6df0f228bfa5414e42845035632cde14784a45b619cf215752ba44872928fa85b518a0684724b7e5032fd76cd3927eb2175c921cd252ec619e33168f88c83378c3fc860d6ed30efe21f936eb938fd37d42d23ba35bf23ad8737683ca491e74d9e3579aac833d5e5f91a73668dcfd36843ce270e8d348833c5ce879f914c7ff077a4c68d0dc6c0618ce7dc186fc0631d0fdf924f23f0e05b38ac019e060e71c09f7008822fe1f0e04d20485fea72cc70f0dbc671b2d361732b3b1d37bf3eeff51837a439c661dc506618353d58b63536c60dd219c675e60c183766179786196ef843fc418671f0373787cd2d789b4a833587716b3eafcdb5b8a168b0a66b5ef3979a979a979ac7ae795026841a42fd23aa39575373164737f28a2c50aa7f52ed936bde354f3d76cd91b29aae99d7c8ab6955cd55355e8d57e3d5dc036340107ccc3f23dfcf916ec55894e7d7f1302969b2853caf797d53e20dbdf06764f319a828a844cc36b54dcd32856844000100008314003028140c874462b158309e69d2543e14800d91a248704a1989a32488610819438c210018002300200003341306051442324d2529f5a03d4141d6bfd085c24feb17205815f2f86d88b218d8a7f87d6eeaa810bb0b1cf62256b06f8e7534a14f9651345c80221daba6628bae4d2f4c7e177d161227903856dc0a6e8fb6c724ea61f9e45ed960380ee090734fd2761ba4cb08049ac8167c2fa9e85eebc52e0b8fdc1f42d6e666a4d578f6b045c259292ba4afd95a860ba4962c49a9386a11ab059fe0e1a386d8441bb20995dbb219c4c58444998caea7bf8ff5f4f1d6266ee152e68280b42d10085483e70b99cefc4863ba1fa04bb67e6faff2e9542feb242b9031d8040a0a0cac5771d37fa44641db6e35791d8ea1b750da77ee5a1fdb6ffb1071d45b4dc5e0c0a5ae390aad21be9f82bcefd726b298ce9bcacbec478b4630f63312ea68d0eeea914e0ae743b571a788cc6c4d329a2fbd955ef7ce26a1098d0903a95bcf0b78ae82998d0aefaeece040cdc5eeb1d0979db9f831bfb47cb0b82bec77101c5c117d4e1818a8611d640f44b1db59b09522da5b61d128f15417ca33a7a622f0a58cfebf8688d202550eee0e7c05325b7395bf2bababa87a5a755ddf51e5f43904e738e12252ec9236980fd4131afe635e6e2f8b0cb07c3ea5b409bb3d4cccf3cb4b50c09e9e40db524da6018f0fe36bec5c2c6e96cc23ae3f06d661fa1b581716f14d7db95d40092f90bae277759789cad0b7c61b5c4effff3c5fe8e6190fc606cdf4821e727145c41c727201a872bada08e42ae7df22786d3ff0117bbe0197068f1ed71009d02d85eae0cb75ce53e50a3f02b29e6a176e54a7dc82d89c40fa1bb66a01f481ade033847e70b2c7d6ec18b14a1fcec61d062a9e81ab4e9afcc16b695dbd7518acc0ffedffccd77b5ab466592761b8984749299fd38e1c5d57ea7086d846765f2b8cf785fbce061b9f212b879874b66ff11ccec484f019362a0cf0eb21eb8488cdff5af8cc8a2cc3c0cf1f9e59bfc8cbf5ebfea59bb37cc22a10519e142b269fe917d1a3a302cdbb353568866a29d73dcc46e42059b2dc99ab4b54dfa5ad483d4df98472baa2dc094956605801701ebfa6a87e8cd0f75b298ce4b0a6823104ab27da3b866573f41759444a10d073db9e3492443245b9710f9986f63623f9e4a43b1b29966de2160f848448004ab95bf7abc6261215bb6f63b4fcaca52887132b09ab4d7161751b38425d54a044b8c6e484d527b6f1bc48807c0737740b981ae48c974e780214c267e6c301440a1d0a54dbdb839c28ffdfa7d6965060450c4bccd1e16ecd4396143c5ccc8981b2ed78b3b059b4de3bbf6151f608f3e072378604fcf137db8c92134c880a4af91d17afef3b46654d1c5deebbf7f79c8058743a29f112101c5fd756c55c1f1198a8d1483579c9c118b59310b16d387f0f2b8f2715b0b1ba99d1f0374d44f9331e52cdf155dcfa7ec08064eb544b4ef65d8b03037230c1f1184e3a56c0c1909983bbfbe24619afd8e0d796bb521d80ac70aab170de6dc245b95e8135e2aa2d0f5c83e08c2104413cc844a5ce2a1112c9883251c1b24b4f692bdd88809519f194d74a15b7a88146b02c83a63e38e78fad02e26779a1973da7a2925001c6026229db7f793d14b13039e49500d9b0f2681bc22951444be28520c93bfa668d9d39b081e8cb5e3edf7d7d488331953d40f059158095581542c3ebc940f4fbe56c5183016e1eaedc83fcf0aa752e44179e3051827de2c5268f8319f430b85a5f495a87c797e52c87ef8936d14cbd259e41575cff905b9f2b1d1c336382ff0c701bd06cd309de0edd243187ad0c12759e327f8c92b1d840b5726caf13e24e174dc15f140d8893cca7dc3dc5977154bd128d25066b2ad5d12993047e195bb6fc34ae6125d9b878529c7a49e137641745d4c34a66f11309d9bed703e348c777619cc2716ba73eb43152c24d82b8f613e6ea450e6fcbaedf27ba3e9d6ab05ad6772786e5620b981aae3c8f20a0a091a78892da34f5a2868bea3a4139a74dc17846c048ed067968c470641c482be8313eda016fbbd92f273c21ac3a1c8fdbc1e2db5fcb0403d6bfed45f57a561ac687eb92d4be748ce4745572b2b775257f49691d840e0378091118f60bfb8ac398902fcf6129ab453dd725655559d55cb7c426e4f76eb41f41e718bdebc89664ba2a68e778ae9d280c7f12f375e1bdbfee0f8cbe5b2573cf98f6d886bf3b823d5741e52800f495b86f16b021ababf39e58b245e1a71ecd937e9fd5cc7ec59f3f6b33b38c31636c15a7ddbd27f40cc960a7a0954441616f52e2449d3c663e64080e560d54b2d6cc76c7c081363977e84fb977059573d661f4c01a8168f73e2793728b63d81c637941bdde0603aa1d1083039223d71cf3820c49e294712463feecf5233dc3c8433cb0a5b7dfa4b5e58dc7b753c267256e6260660383bae545f5bf77a470fa8ed589be5c1fe4a7cfc0c3c962925be1ba79e46a59989f15978e4bb04604096081e51f3c4caacd98f368adbb7e02cccc736d65d5b08abca2288d8b0901b98cfd400056f193d28c35068ccbfd17644137727453e4842015914b4132f6812d21b25108168928fe0c4e9c830ded22db6bf43ff9758ede9fff3f28928d658256d9bd864eced434bbd77a35fe76e6562b67836f9f16df6bbb881dc55eeb9f06d1129c1d98ae1d5054e9e436e6e4566b51f12d5e86fa051a6db7bf98ce859d042d39083c31afe807b8ec4c429887541f5ad329a9c29dac059d06e3e673986c47001da3574da40cb6ae618994eedff8217cea5b7313778b709c54d4281daed036fa04ec48a7914b3cdee0658e5f3809b28bf433c0e1584003df4e46143e21c21f1352d0119976755a026ff4b7ba1c1be2c9693a14a13063cee3b3f30979fa05082d12de61424242e5c94ae3acabd67c1c6708e40ccc0604e1cbe372584d3d56e469c26d27945d05eb3ee617e88b31c05d1b47b7c84caa8a687de0a54f9a6e5dc3000358f506ea2e9571cf0141ace6d44c627b03245aa3d68445520b3b99ebbc7666bce7bd6b48d4f34c6bf3c5483dbe76d4d16b5b64500ff809355a5720d2b189378b21e32fe9fe511d7b6e29ce57882786956d4292e8cd79d54a54a3f78b6b291ff68b1c617d6040244626220b60a54df3e9b74c70a734ea7fe8c5917ef34afbd0687dbd3dcc5c514d798c4d2e5e9e611b0ffe6d2cc621bc244fe5d2c0f240a153cb1a5cd12940b6a5dd5ff91e4329b4a18552645ae83a9782a815c5ca69c9ac2868e74c6534386e916b3135aaf1dd453b25fa0cf7a9c4fb0d2f067fee8c24186b9e5e34b157a3fd4f64cbfc44fc5a305c9c120a14f189e46c7098b68c54ab08e9aef7f3ae39c689628a4c4217bca2b400914845c116e4107f9c6cd008903b695b62f8a5e2351eddf92c7718bd95481b40688cb8d20be12ee6ff008431d52ef3b3d4e627aa2b44296412be98889c3d6ceae9be866c54d500ce3be628977a88b1b0b553a56bc521cb0dbba8b866aff2a954224f4270222e631ebc3535f440b0f8d48b61ab3cad14fd5f2f745e3514d9b8212b9187bfd239a4986878af75d33e22a87a2e45944f7d08814da294eb74c9e940660564cd987c3678f20fc5260a8c0784720c91425364ca5f8978b9e59be3a7c3a2f6b319a778580c97a330e4c7eb92987e9ceedc92c892611bd0984fb8074a8ad32ab7b1781d2c50698c9eeefbd505274ab2fa98de0ab7c77c92e56ca84ded12f88770bc3b485603a76ddf11a5585e55f5f6bbf1bd4749165ca9af03a8de4413e6121827aa9c02e14cdb245b8bb2d2c1a6792fdd5a6bd1815635b3e7b8c11df46ab230814e98f4486895b192a4ac4c67ed31901a11e34a0f1acd88022e04274bcd8039f711265e469bdf6920abc8641a83504aff20124709b1a9b91b9a71edb10648c433181159c808ccc8e3db81311b69ad5bc3bbb9d5fb766f56b4a3de663536bd32dd05264cb84947ac017df3764bd942c9a5980e5eeb4c19601b98f1a19d4e200442c2c43e09912316a326e2985c956314660e925263a874057828532ffecae2399a06c328b02529a9e3cd86dc70755adc8e5c0c94ff385f3d62d7496f1d7f6ee19350082d88f4ae64f69e15d1572d5038276430e5531e4ca274fb8d64164fdf0638ac2e3b2228e70624f1474dd5b1ea046a5f604892744e5b5c81c222be5abee25d206487f4b5c86d26f2401bee494189e7ea5ca5c1a69cda0401606db481e62829e6d7ea6fcb1810391bb3b64564fbb6b47cfe36bbf327da2d78309fff4ecaec67abb99516142c825b9672d93229bf8a42660f6b029631fdb23aaf0e688d00145822074424adb40a5802e5fa95f3daebaf609a656838a56686c66d2df317331b4dd2c2496d75a5a79ed49c12ccff0e50b95b9382a89c9cd8d791c74cc115a967f4a252294daefdfd8fbf22deb57ab1fc609bbe307e9731a338bf595dbb9ce00420798f30fb277c463adb968b69941c07ddafa179edec3dc3740ebe9576a46a2b9c6262e5cd8548292dc66b9ea1f92f3d82195ccef3ca78002c3bc878fc6724cd0011724a1cb9035bee7c878badfdf235b7a6b68db832b201c36082c78457d2c3d7c4804ee5bc50d1029bc9313c0149d85cb685849ab3513fa2d98c861fe9c1fb290053add6e44efa002b31e2602faa5aec44b53f916fd9fb6aeb2fa9f199368b681ce3874666ad2bf1ee5007c1aef4a4d7af34c7d246d3555b9937cdf4207eff95ffa9a537e1b1daf557f2cf354a0e487cbee9c651ec5080459f2a2908496ac6b2779b83d65c9d49cd814c2df54040c72cdc9398d1e981dc2fc6a1de6956bf3353637befa80f5a56f48d7add5bb54ab6343a691c5e52f9490ad78a42b4330303ce6492848b42a4418c6e4ac4a10bad2d61c5af11369b482d6d305a541e6df8deb82ff2b6fc95fc7cf86b73c87103eca27df1b145db7aeef917b13a0c1e9b3bcf9be4577613fda3a96992e74de45a415bd5aecdfc38f836bbf975fa1bc49a01482bfa4b7bf78fdc056b094d6575d3f6d608fc51af88be5dc24a123377d5a6ad71fb71cc055819581db2ab37702b8008de6711a13e13a37a843ff5e14bbbb213deeeae090191c2e179198e65b03075041474f14f73ff3b939185019a28de657f40f2a2df9d5f1d918dd077ef865117513f07c37a220c5d9441ba008b51237b868ec0351b49078219b2222d452eeaf1697b6ef46232667592ed5255441e137e719dfcf32cd0d107cadf49948ebec58473290d163db267fa6d8ae19ba334751edcfdf62a96c9447bbd371edb15f7df5567b6eb0ad3e6093408d30b7a42293aa9a8588e3b7f6841be5603e324f298177cb1d9872d5b4dd9abf38d5004ffab4a45d0004ab11502125a97065f7aabec7ca8d2686197770370d459297bf28b53d4ecfe4a7d0b049f3ab5553b9c0798d7c4d21476b166aefb22337fc75393391f009b1078f415f0d7d85acd9c1ac8a0042f8a0b4b79cab3313d6f95babf684173c5314422b4adfb17607b044373d9397c0e4f178f0389578f3c3b1782a840ca5de7d2e5f05b6a245c4c80766cf4a9eecf332cc3adb8e3560a1d33cad477edc60ef27a58c1eb6d02bb0c43dce7b700c2af48be4915e481b3ad61b42add8af9cf1c46fa8325829d112050d6dbb61e0804688a9938072db33c15014edbb9f14788d1c9277cc0811c28c90d0687b77fe90c65044e823d0406d0e72bbb6a8fe04d5a1be4779d443c1e7cc6df0cbeaaae75162c12a2ea7a1c53a192c83f8c5217c631f51d5d25afe05f3206d36fb139eac943a29554468ac6dce920b03d27e69a3f0c07f7fd68faf669c5c1abc522421e29946511ac7763571666ef2305732045658ad4fcca0337b3745890a8c66a3feacb399414776883e48f7024f3f98b04055666df5e25ab68f8799603f2f9d8448b70073ee942b1901da82a1cca2f203461aa40fe6227b2ed4463f885009e4ef90758fa70e8a2a430078630039e6185553b8c509ea8c13150e9a08e586c4c30b24c60a43db32378dd9915eead39034100c0d1b15e7986110ce3d96aaf2f1761e257f490a9d65073be79ee72b5f678a40f980435d557a8614846c2924d47268084452eef28e76984fe72572f36f46f118fce5d5affba092952c563206f1b921c9f0b93ee68cdc73943914d6c408c3d01791c02e4137c1a00e586d63f28c434e526525c21a175c1bcd295301429d3e7ab7571f83c37a86c792010a1050f6084f00e07c1f31eaf3de435a47cad19bf8f89210949b3d01f57e3bdf9370f03d47bc1057574168ad043e727e9cc9c36bd7672a8d931d4ec2c06f84f7a5d90fce59f3c641a3d28e351534afefb74b2b24b31619534677784ee9bab47cf31e28cc538c6ec434ebabf3718f9776346ddf50dbbb31c1801adc872111f9ad1c58cb20f4a03b5066a95cd132487a132f49086f1a7f61e2ed9099809e30b77a1d84d6295a9b5ebef13a5b6f67e651e5f006b14612c3760d82af3e4903c821d42e340d9e8b751869885c598a82e03b1edc0bbfdf027e9ae074fb5b53e560747c3d6221bed4e07b4ace374bf2643cd0c15b50ec7a1785d22d6a442137b8be17e2ec5969c210bd6b76fb2ebb490f3c4ba21789cd2a58ef6ded3d808826806e05bf44ef7167093f07ca361bcd090184cf01ea964bac1222ff956d2a071544692259d2200ba8c37d74be9825c061c2f40c0f7016d85a3cdf41ecc46df249708921388aa76dc4ad673486aabb5f98a3fa94648902650cfa0805b3cb56eace43464adc299e49f311704b3842680885710244b39188a467480c69d2ec85075d449128ac7b2a8470dffe13580689be15a81be067aeb140d77765f506458ea23980d79c4e1263ba59954bb42735965f223944b8ddb0d01933bd97e41ab6336ba012d75cbbab16d07497d4aeb2c1924d42880c906e74a3540ee5f4df74acf60011a3160f74799064eac6ea7d9e6b086ef96f33a50975904d2824ec90bf7115d4fda1d7b2186ccbf35103ef4b5156f796712f0b1eea9197d1c23aea7477839b24b4df434e04462ecd4701faf2e1162ece7b4e096715543f27e66c2d09293e465d3062cb0d703696e55ef240e68c5165be38c874bbef8777d7c7080961d9ff6eef19d23d1399c58aeb412761aa72a6b357d8aa915e2b68dff9ffe5b0c9a6edffecd3097f15d35632f2881970116415debb464c6aa4663d5bff3fcc42d89e5feb8ce1268911b46322198e1a92e946141c646141d882d0e7d01fa48b07087fac86c9725c3f37cfd3cf93beba502c432d6d2a48b3070cc79bf5e3385bc0d5c49e9d96764990e112f256ebc4ec41f83ddd919bc1e494b164ee600e371971418c1791d81e071dd84d60bf80f2b0d52c990ddb4e52218f408a3a551257efa6c80632674612b643d2d2ec3215901c06e38bbb0092215aa1f02f84704900b5714cc54982f4a4a5c58016e2429a5cbc47727460e25434aeb24872f43fbcf1804181fe4616fd5ba8c7c6cf006d11ad28e2f4f0d43930cd8c95629933000386a64ba78baab6d5e37bbe79e1e872c414e0ae62327447430dc9c69a2505474a5c4105833ae35cddf3f48bc9e9598974f1ea2a4a7d79f2b0a344739dffafb3dd055cc929dad6f77ed567c0bfe629177519414df031935990f68d8c114202d6fc4ddc089cbc293ea2e77f13224a300adb6d1a6a719a0af81e192edac2b5a7ae0d49d0082cfc7db4daf688d35ec4cfe82bdc2065267ec46ee6aa554e3d9479aeee4d7e14ee837143835186adec45b21c1dc67313b69cb593ce101ef8f4ee5844fc180187dc6d6e3804348f46cc05bdc010e91dc3996ef62a96f5ab29d14ab1faf66f7478446bddccfb7f8314aca3498e3f42455cd2a2548011554895dcc67c585d33d7e6abf6a5958e94324a2f51762567c8174feec5f086534754b8c8e052f217f0eafb91354e62280e9c8ee68709cfd5b4eab54642e18ad4ef3214668df318a717aba74f79ca6c124dcda35d1902e52aa06f75aacf6544a4bb025d2768ee6f13f0423565383f480769da6da34dc6e0d49f85224e48fe44bbaf12d2693e86d3836676521ae98bb82e1a390a375f9a0b67765a120ec2fa5104675950aa516b86355bb60fc2e2ca7024f1f761462790d0426d391c3eb54263e4ba2abac71cf881a16414636ee4cf065c8db2b74e41a12faea783063d8a6315635be7bcaef91dcb8ffb9ba1066f30b4b99e38e25c1e93ef2f0a31a012d70d845c1c476fd9a8e2cb2c0155d2252c7e8317490719086997218c318f2cbc93144ca6fe562e0a80c60fcc9542433ca92d0490c300f0aca5ae0123c2c5e54c912d8065d7a29b0575be79a88db48661bc21559014f8d4fb7afe5c36bb700b7ffed900099c05ef433b1d2bc59520ac227feb31a5b1b94ac68abd59c8ee6b3c396a4ae4dbdd1a8d505b0d81216d90832b1c9f06d490e8931bbce4aeadb0ee3dc55f35ac3258bd4873b4db3185ce94b1e6be166373f9f9609c111f9282d7bc34f2118c67087fef11a4c057db783ad70d8071d8b6a1400bcf84d92f78ea3686ce9b89a47f84452e19a92d24be2efaf70fffcb7f3ff98c96276acc6738f5452e1b2572214be7366722338117d8c7e4ebda5660e68c94f25d751b893dc11c128570db2fb0c2538773e7328b23258bf1bbea35c48acb2aea1892fad2d7f965c08bbd1bf5403996bfc45cac5700b1514d2a07616753f80ac751b018462e77604d2fbede8cd692ada860ff8046ef4660255a5b0ddfccab48d6056110a264aef6afb1b20c66cd8492f5c64d9a414dcce7212a67ed3932a45b07b0c0428bd718e5b12bdbecc2c58f42d8b8e80fcfb02ebd8ef01e14bd0353d1c724c04d1de49eaed2c30cd466529b427ee0d60ab4b9e9bfa83ff4176c1ec8bee55390dd23e9991a8607b159cc4eb3909f2bb6bcd4811d1cfd6413f91e2a714416b4544ffa28f2984ceba456e51c188342421d9b2918bc3d9b8236141c70922f6c8fd93bb7befe3e68f70a2d2e6fdddabbefffd434c553941333b9078ec1a46f4c0aec2fea547ff2016d66483fbdf0f280cc92e20d2c58ee8861e3acb674193f98af162aab28d7d64c91aa57286e07bcab666c449378957aa4be7ab351cf3aedfa527d2ff3afff7831696ef46a158933ab6a31208717129c9b428a7a8b991586bb399cfbbbcd30146fe9102e09aeaa85afad8e719bb7836c970513854028377216ef8fd3da54114457842d779d7007eb60fe2389e87811bbdf3a459c2be52e922123adff4aad6f6c183da2bc280c2dc020ae186414600b9c1721861aae1abe2dae14cd8b662cc6e38345c3d08ae5d26e0e182a79b556c6446fb451fc2aa3d018390a79606651f05c88015c027631119da87d25cd8a7501964ee4d8f822535f4718f8ee5ba224ca74c5073dd796b970df9e7a6419a1cd0ac8a4d0e5ff9ead7064775e2611174cd792eac626bfac01d04c9bf09c3cf32cf6578a4980f50675c244840fa192183018732785df09e51d5027e77dae6c5b8ce1d87a19e05e400ecb537fd2d99f16b3d8209d29aacc85342c6b87b49bc78f059d23d7cea8df64869fd56ff94483ca0bcb3690ab9f03dfba151cdd931f4d025f4cb4c75b69e153bc4a04964da3244eae6b55d061e360bfd325330d428a7fb7d42f48830c59426a2ee701f272e372acc59b37f612bbdf0efc76d642d2b6d9c38a8f4ae13fed5f4169bfdae1313955ee3b9ec67e4c94ecd27d50b97331fcf0b5ea965154ca727c946682a211711f26939f4759800da59a5fac6e8c83f1c44d4d2688983f857a0b1f10512a60e3476ee2e81ab3f7d0c7d9638af79f077fc00aba966c074634ee0ae48e596c1293b1c12d8c9b95594672a553e44c1448e0c21bff1faa90f33fb6f951da96913a39c54d292129664f3dc8cfbaa36ab013fc534466c3ad2ec581b01f7f62581a991ed2bf198c0cdcc67190a1d4dc2446ee272901541d8712c23f760a102762b19d19847f79b9a49abd3a1068eb1171c132d6c3d30eecb83c1e99cb8cf989e576161bafae44f89ee4b61dc8a1456248dc29c395118a045d5239dbb0d1a885f1259154fdc20e96a016305f697cee40103854035179b1a14fdc7c28d55a5630bc7a636d6e8beb1e3eee001cbe020691b1b8ed99875d51a53c056634de439e4622d2027acb3ff60db00e2cbe286258c78853547be5063011df575bd62a8218cfab92848bbf83a52f619caa18c299f468c6c078b474ff02a435591bc54c26fe8e9d91151c304ec69d2c2c0af270edd533fe507ccfa52d7ab385f1731dcb5b42668b861ab3accafc7499c6266b5cbb7f88219d83de0681628e26ba2ca4b9a03ce28efadf7973e5ac8d0ad4e96e2d1a81700e0e46279eb730274986db55d3bc4a4e76a6cdbbc69b0405567270c80047326e1ffd3ffa9f59c3de7e7e5de0f0f79dfdaeb622b727d82baa3299bdd861233ea113977336363808ebda5e39cf475049f6177c0e08f1efccfab85951e4e9283acccf1f1940991cf7e39b885a3957480a9bd01ce33695ae5ae381a4a8661e754b8c8a3e84e465b16f3a007e966193eb8981128f4267554dc7bb8ed17bffd8cb745f115e623d002985c007568af4562bbb5f52e27f47e9ce990600e0ef8026995fc01c54f533d7e3db317d4f9dec83aa20da6d2700298bdc5c5bc4d34d0b44809d143df053496dafce0101b964ac99feed07600dcab099d49e4fe345e73cc1f293bc456cf69e80ac804f22b2e3fe428bc9cf48aa651569722a50a8ee5821dc51418661378a29dc8cab95cdf8c44cdb5a41e0bed9fd9a396b37e0f4d20ffa820d72a04e2401ff642369d6303a009c966a2f9348e0b1a5fbbe5369843af82282556183a96f07c9ecbb81a6cc42f5aa79742dac030b0004fc75f1fe1ac2c48b2f14d839d57e174fd5f892faffe90b8b4e3d3d192dc8d86a8e2ae29100f6e53466dbb971fb21e084b69dd28c7aa4c6d23c79278ca0df80958800094c534cccd45817b95012b81a5216ad821c825dfd0b89bf1ff090a01a8f9b5c09a1daae620bfa858f3ef441dbb9fad2ea8c381fa9f68552110f491833063f69db4d7e7f7f24bb69e407b21a354edbe4ce0dbba939570f380f8515852552c9ebe959511c98ec6d97a6c664cda46052f505deba4039a55f03b53d23d4ce81338024d2cca916164c39f3d9cc52288eb9bba4f8c931b9832e9d37a8f6875368679c6a62d2d713f44af0dc2294b17e5d1254ea99e0800ed6771007daaeef169015366ffabb9bfb3197d19e2e4b2cf561a471207e81d1264c1517325be5435f390fbd22d4e9ac9519868c2c9347534a3ec433118d704746866014cb09f3bbaaba976afb8f7174bed405f5734d81d773ef1baabc1e6267c2802d7e92e3b3c016d6ba4bbddd9c48a4a23c5f54e2640570dea6ef22752e59ae2b69389d5158d74d79d4f802e35a839d96902511f9b70d95016df4b2ec6ceb91b4c0a81175ff524bd4dd1654e8805fc940371ddeea7b94d2fdb718b37a23c7d1e7b4bb5814edd35f2ce3b3b615534ba5c767802a2ad9176bff38995358dc6612713a61b0dbd738727a28b1a9d9b9d2642f43518d73b4eb4ee35a6baedccc449030d6f2dbf8b1c896f6271af001098708394f71288800b3709382f81084cb871037d30096e78fec95dbb390004a2d12219790108fe2b7a521d19c491212a1c4226e6059463ad4b0d96c5d9114d408702a2a1f087db8b250477a77a8291f824a001af6cffd2fa5719984ae2c4e7617c8ef7c74d39f24369c2fcc10c7005c8ab92e7716d15bf5471eb38fc0ddfeef777c35f2e59f399377677c71247a87586e009a566ad1a530bea8d462aae20c474f89282e01f3e9a59023276484495f983cf8f487dbd26010245b6fad0832eb6c299a222ca8efdc4211dd913d5aebd1f2caa2254c0a0dbb6581c506895833269756ed542d9b105666f4ac77a243d26f90a5b28c993cb15b702f048f35db95112a41b057782ad53bcc77f25e8bc699a4ef17ee6ae39e81825f8f5374a017a737546954c93604ca6b9674865f2136379e4321c1ca09ec080623b67f7219b77b1bddd31490031acd0c93c07d4bde86d4a5af3fd299d84c49e34db09d0902b30575fabacfb20e8cd142c511104e7969cc2201ae4f3b6c9ebcc549e23f4b5d00801bfb4d2a22c74141c13219a3bdb5246663c331b268ab6109579124aeab7dbf7ab8dad204517778e9082cb29a6d246fe951a4fb2090d3d2656f01bb5a21c64b6e74b75397b39b6124ad1bb2e1fe60700990c20665eeadef11f9fe18a8680183c8a4e4cb621f808d7f7bda68e8ee52601bc4445b8897a22baab32eb6c0200004b43f361863139f3f5a2c0460085f8220899ef4c19d82aa85dedf27c5653b5ab394000e6142cc406b3f2793b803f9dbb5dde00260c247252a5e18b7ea6b33c1340d6a33cce748a98130c1e10cc15af1b9a16ce664b2ab46c3bd745560d91a9233378acbc2e82b548e5e2f11fe103d382d4c2b568fe1cd20b9c8fffa06a6abf85d6ad98053c66bf9c3c956311087a1336407de85bce77e9bbc26e8643a4c65b72f4327ae438fa483045b22bdebacf3780b2ccf2d40763808b883a5817c1d625d90effc0383cc206d5331d3684df8c67708cfad403ae6f170195bb86e4509c2e336595aefe713c0a9391bbdbf3c6ebd670c70e3dcc2fbdc1a9e507bc083873d584efecbdd91bb3ee6a603e572255d9d657eb78f10848c1b2ca39c923c7f179c309a92e3e1fc8c3bdcc1e1ccb6bd9614a2f4014b48270008719aeb754b8ba8c4fd50c77751ddc2a80d8b7a8852132cc71339e87544fe4605e86b8912c13f7cb772298fef9815bf38f1596a3c50c9bf4da0cc6e06efa1735d165a3a14d1f10588f717ce9230cfe1161bdcaad9fe7668251800b3dd575e9380ee1c2cb1212090ceeccb51fd5ea9af1218918705a92e09cca6a47a6d89454492067e411607f5463402a7856a3df6af0d34695d681b9ef3f6a16961f6453270273997221e98f8e97d1c8a44942800b7e4108f76c6834246d19b0f2af7d4c0e0c4bd85b148c3f9b21f51dc5a19842bc5c79e38cf9283937616063a0bc5d925c12846ac4b2740ff762619c5388ed8a4b69b20998fdd83449531c7d95b0e4df5f34240d178c258909cd23fb2895e9f3013705aacc8014fa441086e197860705ea90a8ef6a587cc8f5828107480f766be0eed0d40a600fbbd02b8e041cf6b5858a4ac00c1cdee971383921d01308c762431556deec2ab437bc8cb10b95ce0e7a76967004f213d79f6cfa186cd277eda1cc3b961022ec82c15454c1bcc5d0d56b545da8492dc045f4f38ac620fcf0f79671974f49c2c9717fe4cc11f736f6229641fa2d0536d3014f82747e10d227a1c07ebb56b87ea1638d35150243333263bc2f74f19e127926f2b149f1c73808cf6fb62ae82b1511cd6e35c4f144f98cbd777b14eace01368048d5b36e83d245ee44270c8218bb143cb004031d9aadf6a0fc060c63aca5ad3477a8cad9da3580488af1634deeb8b137bf11a4c351432a37327e08ebd1b5719bd19539fbed201ea029b304418f4b1b71f0b2f779ee8cf5663c16361f98e7e92941d2dfc3b185eb74f12427ee8229ff427b6288f6049f76962289237ec8fe00ac55dd214b5c0ab353b9a484f743a298008492da90b008bcd2fddffd90c7f320ee9ac531273b2ee76438ecf349061404f98c353f837995887e12706c42e11dcc4596dbdf997249038527a4038cd230693cc700c215d39e847fc13d0530d10dc6e56b4b21ca3e920229c651f2b30347fdf9733c948fddacdefb268dd262dae275204dce7bae537a097b6b97e86fceaa0349b577cb3013d27697c692051ae09d9056401f5651003a77f0ee6e55de4e67dd2dce33df4e7858ed27ec08fd9963e7a06bb59b30a31866738bd7380371a5ef966eebad42e118c5311b8469dd97a20ce31a62dcbc2f85dd0bb3c065e19ac6b0f08fa13df7a0c3aaac64eaa5e721cb2951b3e20233e33b92ed0b9edb90caaf0b3eab43093c9976cfd9f17bcd0e0625124eee0b1fea577a610e8a5455d41be91c4cd52a7d52fb8f08e4003611522bc9ffec50e9214eaaff8fb0cb7e79a8bf8ac1e79d9e3edc8f480d57105ada8e50071fdbefc2b137270d1ce0d17f33e09338db73dca8839215365eeb7103f0139c3ae6688043de478de22e468020f96921f376c66dcc0ff53c35a627072853758761d274418384b438d8fd9e544fa9935172b40e6b22c4b0f1be05eb269dea79ac488be65ed9547b21d97b55c9dddd26dbcce7dbe4aa08863b74e6974b1802a954fddbd1f1f31f595140d233e952a4ca4db22f2fa255d17f6c41bf3a2851857bb1e3c3beec798026a5e24bec1454ca538f7adfcc0855738edf3a03e579840d8419df83bc4aa53961c604e453289e309dbea0952073beb191638fcf7a73b16c0130b002933e77c20c4d77f8e02e6bc656a36128ffc79a774723701b00fb4ba211f8678bec8ee495cf3f01926606163de01a02342f98ebdf3bef0a2663d3df3ff41af7fbf6f0229f5feea40c8957f9b0033b0a849fc8658c99c9264a14d43b81191cff2f81f6d0b38780438a6e2e3fa048a1009659b619ba2ba04ea23a647bf8a68aeb9c868807e1c3b68be58828c74bfb76d65c4df94727e465dcb75d844781969d7d804edc8a26e3b94cd7ff507f52ba3b155caff81e8a57462d5a6b35e9b1927ba1fec716d6281168e055ea19631d9ddabbee4e7ec9b350e1860882b3b2c75ccb74aa34883c2a255330553e65c3135496e0f305649b3b3ccded8907f95734bd6d1d19a28a02b3cb9f6162f1147ba3802623a2fcdbef11020eb2b33bc0e2529a2178f9ad1852f9999b457b9864a11cd2040893337092647536b926032599aaad978c18b407a14f8af2b8a2bfa09796bf92bfc84bd6998e230130139da2ce27d248bd65dec2520d7987f68f9cf12bbcd6b178a36fd712cee178247b2271216faabc932283234bf22fe7714fa96e807140df5280c3d2422812b63ce758c2c57fe014beac059d9a69bc59ea3f79f101d44eee9f7200f870dc9575aeb1994367d3b1a9cfefa46318cfae897e5a7f681b370e0b2752c47d5ac62fea96d31e49ed371414e59249c86baef0f0ab2bb6bf78b7aece22d26a55c869e22cc88ef966b0bdca418bcd262d22af5ea19fdd81bda200f6005a3c2d6192c6e6ec4e2b51843639ff5fcc66e5726228caaa6662b0a00360b121b8f3f9f6131e8b350e879d1bd88efea14f78dcf9f94992f9b42b7200e20427128f3e2e4c313214c62250a4588877ae2dd8ecc1a9f3e7970d02807fae7b48cb7325cfe6ce15b8a13651ce9246d823893de86d9f6fe8f3d66dbba876090e61df35052bdcb8165e5e01a23921affdb95f833ef76e1593585ff14d30168fa0ec26be48b1b8169cf74be2d0e638435fb75ab2a8fb9a01429c252cd29edaa9eff01a7bf92a85ee3b35cc45d8200cdc895c1d67d51abf197f9b629820deda5e3d6a85436639ff7c69b9a83d0e37b455e8afe95486efb456a3162e076eb575732369667e293ab8db37a59abe186a1ac0899c8a61ff7093ecbe15503b0f359bf99f0f06598521fb808b14a277a1e418235744148c8919a6f49b5ea815582958080d3a8be58dc4d73718a380586dfbe52a01bec9e303094aa90b1f42e4363de5d3037bdc2e374db2e3ae6672bae7606ba420687556263937cc250bc9e2350cfdef426d022a3d9fb8bbe431370852dd2a05e3152c6b23825c2db10ca2aa3e17e7eeb1cc965607fdbce9c0d5047392bd8fc430ccea47ea422d3e1824ade0d4408487544513e4a64c16a30a7d22ddf7602d99e830bc9c1df223d28cfdc3fe11e43e9d4d5043db58693b447be4bcb2fa86fd081029e4ac03cf433d29994148a34dbbbe30f5a9836bd945907686e466f41f416a8d60ae7b9b2c5549ee147045a27f1743a1ca770900a8624fbacbf69b8217c01eac04cc8e2a7ee250d874be5bb34cb99aa80e84dae046ab5db8311332b87a64f3f49aeffcf1db53d007772b29fd251c45206b700138d0f8abf7c1e784887a09374ac80d821a829efe5e6198b78ea3df1aac62fbf7918cf597b99e97a3dfb0555376b76806ee2ee8e59dc295957c05b72de3abe036ede9d9799e4d47833c6b9daeeb5e09e867b4fe6c8d4144d228a2673ab317263a8292e0479f02179152cf31f58854ca0ee1c4a9dab4744f07079217d5486e5b108c5cf40d340de47754bc646dcd1c762bbd84425d171d58e92483134f7649dd749c202595e3efc6e8b2574b34ff12a6f2a9204f6e7f4dd0c2781d74051f7b3badab8526858e344ba074f970b3dc1474e3c82d7f4a7eecfce0935b459307b59ed4860a575dedfd8906e07845ce43e49ab454b2d9d2cf52cdc3189f1e301aec90a841322f7b671dcc7d52a55d138033a5bc06796940df12fd316a8700676906e27dd28ab9d558211e9448ffe4a099cd97c44fd49ab176a0d170077c4fdb0091b2f1e7c5491b48ef4a731c0808825edf1cd46c74a86f48d5c428e48a7c82adeb37143f431d8c6d335ee3d71c2c88b9c52dd240e96f94b700f302847b4d2728e209f2366a4afb494bac16d2a64a8fb64ad44d390cdb9e380d3366217852c8849c931ef4de8e581b0577ff2e2c9d651d37b242f0d65937f6c03317a56b356a75fc681c618b0007fc624176d61a3c5845c272399d76d6d6b82c11990e78f3cfe2a66906465601e67f32e177f542b85ca2c8f80562467eab17b43f90ee1b87c0b977b0695680272c487ed272c548bcaed58caa5c1589db6dc08fa5cc96694d541af6dd91cf6f0235bc79eb7eb0e9912e4e2acabba8a54b9f1792940af21dfb636dd8b107bfd441b4f9cd2e6b9b50bb1467b5edc565d4f0c1200a40bbf4765301693c4a5a3e2c23fde6984be1336e3c2ebf9f91adb38520deb89998a00c851e2e6f82a28c8bb30d0558b940dffbae29159cfb3503fae021af0ef6f3007dee02dabcc229b6e080d24572325850488bb2db15126cbe8cebdd1c64e78be3dcbb7896915da5a2ce9e622561d33c810a5da0df9c541be9b5fc180f07efd2be91f0cf1d7a0334eb658ba92ce85b9a9a78d9e179121019ed66aa43ad14d49da3fe8a27714a9a1d65c84245272d275dab5cf9e66cace5d9e1c3bf568d120e0d876caa813a9c1f86cefcf2ecb2d30d930435040e1dcb525dd2b3faee1a838a1770eb13103c4bea1685a926a21bbe9efc1be640c7f8c3537fd87d312a9663e8be4fca1535d6895e7ef857f5923b48e0c52680c932f033f7c77e2f418cd61dd535392c3ff236734ca26c6d957d9e8a5129a1424eed061bef446c0858635b26a06d862a821489d78a972b628fe348aa38548b00d2794ce7fe69f3900439e1f80bbd45342ed23769d77c7685b56408e37f46b739a8610d746df9f40e1d09eb3501efd4548dbbd7a22dbff38beaf0091394c3c3f5de4c30c3c1b1863d219b055d1aa6459d94a05dfdbecfd33952d2ae3e5b3b3f2feb22e1d9aacd626c10c0c1278acd8a1a7abe7903414b2ce648a251352322e29cbd9c185ccaa90e422b57c46455efd8964db7ca0be000cd7f703dd2de37c00cfac9db3e72c33227b6f628d2595ba56c71704f07d1e5e7b2281b0c76a072723c2c933ac27fc700478231ac3454f4d8ae0f580e01c72165a9284daf0c907e40170443c45b2c518921e3c736cdf4800ec390967553abf9f105bdaf5f6ab48c298f63e77d06edf26e6d768af04473cb37f8d28a1aa7a511683bdb059867280bac063f288f011efdec39e453246dcca27590043484f87d161566f43ce9c1ec12521d46b63b027c14c4ef6dde139130cd1c2c6d08d228319c40235569e7c53525b086982626ba8fb50ce30e9b022f3ea4a133c1207cb397f4dfe660dc53ef4658e6467938eb02a6fa7ba65331cbf7668f0177d62510027d668e25b4154c078b99b565d6f21d94248c1177216d6fff8833529fda106b0f275041164eefe1a7f9d88125ca6506908f4832adde7382561548118f8304d0f510ff2a9b676653f540f49587b8b5a0b641bebf876a36124d247d30253c2598440013844f2582aa12c871b595d8004300d5e5cb3d461bd9408b8910d5c22815f4f5a12593f2fa2179fee57ae43f616ccff3e619754dfa32d734fce9cc301a083f9c0a81edc050a9bb5fbc9b59399275603b2f2aabe114b2a76feba2bd6f7b8cc89554fac89fcaf88e85e96f618fdd9c138833cf908e57cf90964325c761fd3fef8fa131195a9e27895585ab1e13e6906b2af28dc4bffe5160f883d3091fd8b924119ee0708a5b852f8586b19377a3296853079e7f84754bf03552fb9549363bdd055533f0383e635b113cf95a238813d82c1ec886fc422c973b5666c2b950acb3381dafd8af4f6f62649035314149b01be5b3694bdb2439fbe6c0845ed4b61b47873c7133253d1f447a9ebf1a762c18b0c16f7c7b2f4ba1caf653a49e9c181dda0c2804496df2784bb39020b2372cf0445b60ab7488d6c62880f34121b5e682330d7cf50dbd78244f8f3837706911ee7291225b00f875908a1fa43478e943f198354da73c1f477f69c264ba2860026a980c693fc617e424481acd443d1f935d57b15b7b78f7f70262f450ea75febb85b1e0343cac564cebe3c8245998da5a1f4751f5600dbbaf438ae8ef44c3a62dc71255c2bff0a9206301f9524e4a9037ce3ff6e3e9acbf4fe248bef13ded14a57417ae857101bcb7ceda00301312ed2a1c40b25deb25adaf9c982c1ca043ce2e669beb3e2c40fe91e01d75c3ab6e2aacf782b1935d71e7b5c3d54e7b93b477c50d7eeee1f58f4c2338b24fb894ce4faa653b3bb0aa68d3b34e78844f85df13cef7238b9317eb97a61fa1f617306109450d834a974ec640222f923cb01f3fc2ad00da6e7575ca04677756d32e6f5fc2969dabce614c76b6c0c6657e0b4d3ae2ebcfa33c17a0a0bc7ed7dd865e8c1e53d787006559f1d5b6db263fd9454fe2f05c10e34ab8ae7ddc6fc5fb6b7c4c333c31be7a6da80bb8827d119854daadfd068f6b452963e487935e6ecd58d040286225e8b0be93a7cf386bef4f80a743957354be1e748829ed8d60a677e36ee5e87334dca6a11c3fc79e0139613d53a74f9307be1bfc911fe8637bec4ca70721258df23a9c7f9e0995336360ed46fa90809e5b3541d67c22759eb2f108c5cc96b934fb8fa9c62421c4a2068673a22bead43f1c3b8055547d220e2c113f8ae997417418773e9468ce33d93a0cf1e18273e9e12fa0fec57ad1018d9b20fa91cba29f050990dfc73e2637e4b0dd566af37356dbf37368d0fcbfe931e3de0046656e0de3a057da7d0d4789444a2844437269f299788abcd803a028a71caf3f37b9c7b90572fbf8664e9b5a5569eaf946ecf37995099ac63107303adebe88f99753c07444d748870b5d62bf0d5986b878634d73297aff9b3fc58f11623c3899cfaef155f8ac37efbbd9fecb95687bb9bd9cf5ec99498291d5444594782a59c88804d3f6171b3589f7b6aed6faa999b4cfef651a086016a7f13039306cf835b97515a878702fcd709c2521e10cb2683be57545e9e96338eefd50f5604466bde092d88a0a9c40d712c10d05b42b7803e1b940a4623b15531e10c3a89804ce62bb058a42a4f52fa146f7fba890fc770772dedb20123dd57c506b40c3da31314808d0eed7ab286fb8a794dfe8eb27237b0006c711dd28231b5b2dc3d3e2e37814ca176e1daad93bdba6b8f56b3ac9837a3b999f141568dbbb7284855ec5a0767ac37cbbd8009cac93167a82e11c560a5ebb8ea7000924dcd1a3c12c1c4cd6e4b257303030525db66918c34ec60603ca07ace2938ec5d21c66e19b91ff158271096fcaeeda426f055d8e4c47c5e3a0b7a69dd1c81b7fa1af2dda0ca444415d96163312cda1cb2d9e30c912852937bab603166a28f000beceac39a5ec3776166979fce19cc20a2c323ad038856ff5293bba9297486a4f8ef619f78626074068b0ae92d83f050c620d9f134619088278d0beab6652f94b674480c23327c77378409aa083086f246ac69e87e1b26e393a1a5cc2d0e50db2eb79a9955c653ddea8cc5791318f4f719f724053b9360c59359ac4809ad174a2385f56fd8979cb11c4d3f9222d09e7b62fb84cedfdcb10b640adff2584543cbd9186db9925f583a0e3e4156eb482563dd6274cea553f3162a6a107c4c5ba77b4e0f8574e997e051bbc9499882d01ffd191ac4e8e00fa5cdc66e9881ce45ae85706aba25cedfbbd467caec7f6cd445c5d0243473f61ba33a759a57a9f8d44fb28a5412046c8d663c89f6e57455b6d51bb1a32b49a469898a3b0f6a64b19b9373372eeb5b9ba31e03f6e6dd0ea7c1c74bb0572f003773799d7aad470180595d9352d468cd1bacb54d8eec094532a84d7abffc4844b40f95e01de65f20ad6cde53072419dfa403c16d8038c4bfc40c8deb74c2b8c23be9801e2f59d373b5ed0c12167c8168694e7d7c8471388ca746710e485e213e7200417b74d30656c9d4715b9745ed29b5816543f4a3847b735e4c2adc1a0560a695f2bac1d1a75f42064597949221aad09b516e10ed1326cefb9a33e591ab5f618926f91c3cf03014be5250b81bee3425d57409ceb6d3f8787471383c5bfa375b78f0a964eeb94c6e1c01cce5945f6699b5f7298f015abe516e83ff7dd3f5817d5124095938520f6f6790704090e038832e5e78307622d6317afe1a588604b6a6f14f00bf37ebbc2f562852239672b06754c988bed2fe5f4d38fd9ffd9154cd590e554c77756035d94b6828de6081096c06040b120187461b90b0bcc9084e331b088a939ac93b518f73537b033eb9a238a0c74c0c621d9ff0e427661861e664cb060208d81777e0643a53a2d4a23924698cd473c53fc1c8124a0c32f99e0e852f59f33a184732cf93fab0e1926dbbcf4688552c56c58372c3ce9e01d68f4c0734c05f862b5212147de08964f1c58733fde2e1af78e529c6ddb94e7459b7813d16f93ebb2c41baee86d9ffda597995822f5f1ac338b4e814e391138ef9c9361a6d93f70d2e78ce22234a49a20e60a149ffe86d8bf07111595f8b300036cc28ff782f88218e373773d5c0a73f3b626cd1ca6513fcdaba4d6cd71633ba318266a2004e4ce8e6fa2557fa6530b51b7cd4c5d569e63262b8a4c7697593179883f78ea3afaff98c42c7d0c52dbc7e4a9091e6cd74b893a56f13606caacd644b6b5796d2ef91a170fb283e656838f2d18ff8ad5aeeecba709d5b6e5fc19abc6e32ab3870fe0a85a8ce0445ac6b3aa76287790765665e1c2b2bc4d6da7c0f6c74464f12a82a8256f2cb3487c80c78e999d139b26630991821c7f518c66ba0200248a7c72dc86c0861540ffb4e31f4a31b800779ec2d7f7e1ef035f7e808ad4b9401ba6cd540025fb4f9fa50bd337364771b40ae883ed739285d9e0cba86685d9075d4dfbb4b1509d9853b76012c472192cb6e63fe702efa3a12aaa9c00adfc7c2c054eaad50c5262b6b96ac5a979a30d7f74be29430f755525dceb25a6cf47b345ed1b89c6a414675263a59bc8ebe0878674c5cf720d72f5663381c365649b8910724203986fc7ef7027d92bb923580583b3fb9294c8778fe3ea7b6dfc7da576c2ff6f9efaf476c6b7ff3bec27ae0b0d182f8f4f53b3b3a857da0f293772179f1415f1edb5e24ef126695380a9c91fb6cb0ca12c988411390e0777bb6bc8668a75635c435c83414413c886d549d6494f9e2782f89d1373ee3a87a8c44dbcdb8639f987e7080fe2660fe0b3a560b26e4d9e75bf1856361889142201b568bceb788df81dd8dac8608055a4e0336b89b77cf2778e9da6aea985ea99d8065a7904e889d50789f50e0097aba0bc6a33eed76773179b9fbdcb6e711ea48c906741d0cc2df2745b780b927a86319f2c9a7282d35c2d828b82e0e5030c5bff7f4a00a5c06fbda0c63019d0c87100b1eb8258eab93c5272de9e19efae0aba610dd6e6c22a59cbe3f96a4917a02ff9ed0f4cc3fc2c5d5a9b3040efc9b5fb0eb956cbb196cda53f3546276f2451f01650f6b979f1a966fbfed5937efd15c1d5ef103243eb2f4ed929c0f9216c334e73daef536e83f80a977f0368aed3e01a51a9ed0de8bff9aa3d211158cd93613fda59ac148f30ecbd65bd42a085ee4356688adf8889a4bfa0b8e30f31e0dd7ccc11336dabdf6ef4b0cc1f0b7d975f594655a282e3ca098ec8a82584a8a6046d2a0880c54a300ee8c900e18343e6e95e8877800c8373cf44daf9c979342f70026984521a405001d52f7de61461857240ebecb5cc6e47715d45df200638d520c4951e0b36c88c91de5a96253475ee45b099b4b4584a22450c6c7161630cbee6f19bd4cd9afc38d1c423fdd556eb5634a26899fd991140a2bc893a44350bfc7011965ab02e71511e3d2896d14a779e84dd5d6829b3eab9d6f1c900bc234a88bd3c4069a12ee72ec66e749c9e2e6a60df76d0001e107bbece2c63b13aa138ef0d2506ef2021f39a68cb1af35bf4279bd3c8a9ffcb629fe6921f77fd07e5eafb8f1ee6392bcdc64b957a5cb4d0ca04491dc1b63f4f14e0dd8d63b1f46afca0646d2b818411463f8be991c3574ac048d3aa363b07767bfb230a3b380ac91c0878da489cea6f3a73a5da57358d1f64c5acc47863d0201cedacaaa298e3221360b6f9191b6dde3888733a0b07b35c57be1053b16529eef755512c18ad30b16351fda17432ebee6536d8933b21e2003a1e74a9cf067208e4da6b15901fb9429f17016a13b9d4f638a76dc8cd6c7cd6ac676786f880c210e4b67d9a063c2ebb0e0efe6a3217d1f3eab6492cc798286ba0f6c761785edc45bc6862e0f912def1beb18a6e0cd539759e784e8be20fee2607990aed3f546624af0a29e12aa4befe6887f1c1db0455d36e140a141c91213d3865a302e0952b97ffe342043cf01d52cd1ea230d16c409cc2bbdf1a5758822b8ee9a5b4c3215517008989349889568b54bffb54a0ee929563ac6b35f30823c73ef77860039de054d1d20e40f72894590e04b6733848b82365044f91dd0d923131f3f2b447e2af58dc308312ddcebfa9ab03ad26826d810138726e064e428fd87503262116d5d49db3209e0243b721473f9756df94502c7ed9c6363d3000b3f209459f31901da7538b8bcc4431963c193b636689fd8e9f4186303affbad642a6d8c58f9c8f41f2b772fb312966713b335e9e12f042fc951c2d7ec8c3781876e781251472ddef134a41c0e260bbe827f2b4d54f968872ee60b6c7b2c2673603791bf95c155b007871a205b83cb33936fbe54a313fb6776863bb66f78ea4bb2a518ca3c1c88a1cf8b0860dfca14d939d1982bc954d7d8a7e82000099184ce5d48a56be36bc3abc1f28f2face1fadd364fcfeb378da49c034f58e838ebfe7229075303ef9fcdbef890dacb9f3ee7371003e0f5cdbc3822f2b1b6a23f225236cb392494805f67ba79006a3c3680bda2a4c1d74ff6cf159e06429438e0702fe98242f041ec2e9c79b02bb3b6930f04a4da525db860fa26feac44e4f9a488b8e306a8643b1bb45d0a6295b5aa6ab85a809bd9d2c1dddea990b377e2226ab45477e8af830f4b368a1df356ef5a50ba0fa79c99e48c46df343b4c89d0d36c347808f79f017e0e0f87201b00b39aa060e85d778d8671607ab9ded3d0fe2601a9bd3076fdee2882934570bf2725d90993f13a7b7a4ced83c5c8909f8d9c43e2cc7bb7111654b4fbd12ea5d4c1b2296344f5a82c0481095c7fc0c6d15070c8d11ce76540243acafd524f4d47c91d0d6b8e5406777a4cc844350e51f01aaf7bd5e144d2520951fc99c7c6a5f87bbc03a7dd6f088c8ab7d2d9ae2334a709eb86c49e3ac1b9401f6ca434e13a0dc036721bde22b13f09dd050d6f0b2d74c4d043811e6390182ca090b8037ec3b0cc0ea313af5956e405f109dbcee3fbbb717bbdb87a569e07dc7cc9b65fd5a2a9ed37c18e65af5f0bce7d704f797e823cf1056eee162aa0f1f09da08aa227c996103bbec61c9a4d2043410a6a1e350766d58b4ddc8f086a464e936cb92eb40677f5055e6dfd56233c18efd16be52d5c5e00ad56358e8c09fa7c4ec54fd6351236655cadce96b2880b9e613d6e7a8f6af15aaaa23fdacb30d2a59fbabb642d4f81154350f9cf202b249dfcdf7226f39df72922a70a91db2089b691f10be00377b1938fe69b2e5adba632e573dea8af0600caa6929bc519806bb9b084036ec1741de742f3e6b90416d90e09af23941eecd6bba66c30ca93674b1496cb60a6b4d69aa2b9ff878c2c924f36340a0c119bf75c775ce4c703a0708dc4ed75ab60d9cfb37e1abab89cac4e129df590889829fd2f79466cafb26dcb6875cb0815a6a5a0df625a6b08b6b5085eb891a77e9a93d6c9b116f891ea40b3ff1fbb8b94e0cf95ebe259967fb5ef3092b37b012589ece19cee303411bbf5eedd35b1f46d081a28be77b9a5e9a536f3161931c1fa7bce744cbff34764c296e828959c065c7616cfd1854d2e2fd2ecd98f9ee6b38a5f78605cc2ab94f754cd8f356b49a9a814782750c2c2f7e3657cbf49af5f9c68cf70378c916ddce3b38ecadf144c8bdc7855a75fee791601f770df157530cf1861bc7ef3f13c247a4c28063e48a859df10b2c00aa0e2234b4003a26a772f3dc9faa001eca026cdaf51f30a7ed392ffdd35d5237a1704844bf731c3a0f0ba44406fc6eeb792df4649ca01884610a91ce2d6ac09b7abd65a025e88924ccad86d3b9c26ff37dc14fb7c8d4ef3222dc582a84a50743d2b3a6a3cfb8a77b253d8492e64199b93f732599848a14d48d341bc66678d4307ebc68fade0438dde9344e259850db8cf65b6cf554efeb5108e87e0b4b910fe8822c47be4039573d2e60f5c4a1e691f08c0d9b2a2f59f3b007df12bf6386eed65bc5c1391b076c5856e5e04292cd3374e06320beb19cb6147405edebdbb129db08c4a41969f34c807c9c40ce5b39f446b842d015fd1307429e1c9ba3d3f829cf60a1160fed866560c09cf33eea83764111290299f0c7a2f3a71a79fc335f421ddb8e1f7b8efb7e45abb6aee533190c6a69c69e81395481ae43c3978c8a1f14c14c1e67c8fef7105f3d0c7e4568933a76613dfaf6dafe0c04f5f11c8a212174766e7888f86e22d8564a2850faf99b781f18fd4da409ed4a2b428e1f5024f59c969ccda821f7bb03922b6ee5cda87d6b8143bf438fcdbd73a1f784cac27f07cc695bfd4a4e495b247117ca6a7c5425664a09afe5aa6d77566df5d991f4b2536bbeae9635d8c154aca0f4fb6231831fc203ecb5c60c96185ae694bfd0db30f077c7b29888cfb3facd451985c813b6550380d800bdc568fd9f2e5b5c4bb7b4e4bee831008665a42fac5d009f6952a85892990c1699f45a53569937792faa3e175c45dfb42cfb89a9e166f43d0bf4dc9f95c6d430592b6611abe6ca1e5db6538cf659cea6d94d39fc131bfd2bdc3d8ea93f08f17a34e0391b73893df6d991e52eaa2ee738d5d94760720d81087534d4b4930f24457bde192dbf048cc7df4babceeb14e826d83daf117ffb06583eefa1e11c0b90eeb583d89502b66ea06e110ff10a15920820ff2b2feaafbbe8b2b757b732c8734c27368cee3135fb10690cba96d4ee15dc718c108b06051fb218f860e81dbd50e2f00b10ac7a37becbccab92d6b63e74d85bdfba8b93dc8f301c8777c9e7122fd77a1c940bf2fd9e69f079a68b4518dd9ec452240e032f4d8e5536b7bad8bd92e83e86db268a983a3e67ddc202b11513753fe5a6fe2ded651faa5a4d943eef99aba91a75c5d34911b592ba9a8ba6fd338455770dc8a431178c186615786e0e825815750dcfb9767a499f0fcb8ac3899f90b7114b7bfec8325e5a130beba009388108daad020a56851849f2811adb35c26811b92ca019228edda996748865fca1273880029d449c4d7695f9b5a979dd55a26e96f5d1e1b93638003ab86f066e2b1b9992e0f7d611fe66fb08a30f6e9fe43d6410ae730a08c4471e0b44a4788aa1f31f0cb2a7eddd3bf9764a16854683c0ee8192b8100ad18155268089dca576474f403d1462b7a14bf750794c84b507b76670cc7d890cfd98d61edc8671147fb1239a8880c2a49e42ed8f970965a17db8d0d3d4477fab538444820870817b7220493d1cad0595122c886b4bf604ac4834639e71d4d2af5fa35288010efd620ef0606ac5900525094ea157e2d4e0ac7511fe41febc50fe68dce738b8614333863a035c71152b06707b7dfaa8478f32d8013e24bb875ed32e7d306865da8fabe4bee9f6552ab65a5a2b032cee5c6eb1300961ab5424b4ce7dcddd7ea6962b813490a5c867c3d056f642d06c0682244ced72fb53a4593190e0da6e4a8872cade285da70a8dff0c85b9288246b9ef60e76a834fdd4db27979a941ce898aeb601b8e1bb635c81d6df9c6a2110c581a5ecc3d5da2aa7fde9dcc9dd49cd2cda52865904720830d05eaf9621abdd0c0a8cd03bb723188b17fb92bc7e71f1a9711ff91472307cf65480fbf176f00c5b8b09c914a1b207b452ad191a1c3fe964a6674119982b0984c316eec9a9390418523a084958790a001cd9f8fc23c67c5db780efd78f0956a6d0b9631325ff4e1f495745e8ccb33f896544664f4e18da0895d9cc677beb821bee697b222e06d18314e08199f17fcbac44ea387d8aa4e759d6e9be4184d2ff4ecc41350d4a8e6c681f4640c52fdeb77e797b6718f0ca96709808449488ce8c421cb8990b24a433c090577fa68faa4afbb01e612e84768bfbaac5a20949d70720c9bd6f218f74755cca480c06b43bcb3c00efc2c5041d6079753da747eb601c8f1cd5be210c089e1d07f36b07f886a01700905aee7f70cf6de5387ed8ec06f9ddcc89b37a7c2eb0e611bdf7575822aa31faaa45c63d210017377d24c657fd330d0a8e8a4a3f19396bcc85b1f4126c3b03b63995a35e5c7cfe0fc46de53b88319557e729ce4d85d328b830473930f1281daf336e129bf843dccc54b0a2e5818ab4f096ff95a3c496f90a600f168153e06da41664b46f781dd4f4af6255fa5f6eea3b7d27cf15e2e214edec1ee61e791d7f936e8031fa10cad257f6b2916af619d8f53ac731ea70072a32a36e459c8bbc5d90173369233b7e0a6ab70180f08350fc3c78b93f03351b1fd5eac254d819545e3418139ecc7da9bc02205d08ba83453e0b3a9be081ddbeb7917a554f3ec80ccf7430f4949b16065e094d8f7edb4ed70e9f3273ec9b9f7ed90cf30c92410924690da4f5e500ea35238a1ad6c2eb67716986285533269c3f0dacba6858b3952da70715fc16f1e606139db28e5ab4c2ce8fb0fbe6b72a25a7395da8a9cc71c8dbeace952ac7fe3dc944ba9329fedec0366d0e2a6217a0f9e553220d7b878c50245c9a49d27bd5c52634b51dcc8dd45b4808d09ba0066f58ec779390a51a0040a250e786f5594e0c4ed9b1b256bd481e984bef6d06c330d142050e17ddf578de318511a7d1425e098187a2ed621758b052e8e176cdc40c46254a640490b52085832ba20e93f94e8024d508476bddf21a520f17dc1b01b98f020806b66015864cd858fa9372c6e2eb168313aaa52549cdf75fc8c151d3e5b5fba9426069e25856b45422d6fadd2a47acce4cb54f1319060b434c866d3e156a3e745218dbdd506508e6dea225eda0921319760132946a66e134227a87af88832f496ae53e5d30bb29c49e6be1f90a61040ef96c4b5067f3ccc8700847361e033c46c27104ffa87a87178bb265f29258c8d4d0e33979b7d82d607adb1f8c074623b7a9102ecc2e1f9253c3edf4eb4f0fc26dd0de5c9e05c6ae7aae8e1d74cda8d98535080c03a8313ca2acac438509c48a491564c0e1fb1ed896dec85ef7a39b8dcb3c0ac717c5a574255c02aa630dbdb3823afedb5fba03c7bb2f1301e46735de3d6741ad505035c5bae209bcfc6323099d57324705d7a8c34ee489e534640c35bc6a74b879ee5b0bb8dd754f171a7d8a44b37cd90d68bc41e2daf15f55ddb64a17566fd00ce697b4db33d86208976829b3e1f5a7416fd601a290010d323d3434954d93240b28073ffedc7f2b5f476f167bb233632f46a1a6d57a1424afdb0523cad12ff1ed84dfcff152bb6779b30a87661af3e9ad7fb2eede7911eba42d8ae0b3a4922f2c72586453a0903650a8519166838207c7e29c3969ff13a7510dcaf6e7fb6c2b548b49460e18012baef2640890c7d765f653850a98345aef7961d74364543d8aa69700cba09c95034319a7808a8cfe089e9c956d47cac62002c9eb1512e0d74bc8fc74bac22381a98142a690601f86a1a9d0f5c0937dfd851ce08c88be6e43d8fdc8bae35e07fb117fee7dfdd7947e29a0b55342762cdf8159d319e769ead88f9382b463cabf363df0670d7a57271494d8d2cebf1c6283f27485c05151e4b122244ffafd02c638fbb649bf153a866be4239af62f0c8fc4e77693d955ca92fbbb10924a373c3a78ee4c661e1ef97ea11b0d4f51817344d67ff2ac42ee6461c003432af9855a5f76d4daf015727d5087255605939bd34b12451b36ff1db5ad38dc1035047c221be5706886cac3d501648a329b2411e37be97a9a476fc8dd11013ba9a1ae93c02dfccbde868009346905d6b6880432c563f99dd22dfb132e4233c69e39163f8032cf66bd6e465a89be80e4a133c65b39b75d509fb05287e27da7d7285863e3f718c08bfd0a9e5c06890c033c7108b553e436f836a7f5613aa3d7fbe2aaea5d6f5e59fc6962a87c43f734072accebc95013a31e81a50c8f6f2d39832030f25eb07a3b04b8e0304d272f2dc9b47310febd8ced9ab4a655ea31a7691b6023ff12861d76baf6e918c29aaffb05b465624a89ecf744707e6e220e0ae232a1e8c551096cc798985b3194d07333e6bff5dd4829489e49c1209f7b6d2cb30f978bce64ea9b3a514eb020fb3bc6dd5cb65e8d14e833bf021be4561c39df4e9cc1790befaeec343dab7a63b6d4dc8c0a8cc3772b6f938b25e2a47f45d3a063adec573e04fa2b41aacc76bcafee0f2ab456f388583681dd8160ed31f5979e7dbf2c37affe648f6305afea669af1717ea1a819048f26ce28ccd529d3dfab373faa04591b04d031120367913dca965a4a0ab5c6c0832efd89e764974764b168f6c16b3d9b124cb9843f906f4f1a973ae137756158fceeca78b5e2fb43520a6aa41a07f0509c3dc1057b6b05db2016dac4ddd3d44aae9e11580058c0c45252fdc62fc7eeaa412cf92a3610f6d8b9c098aab0c4da270e8eed78ec81342f0696ea18a56125e3fa58bb6bf2e05fda17b92d7ff84a61b069885e3e2c8a17f3d4c93fba969632035108e96daf19c79eb521d4119d63aa2b6d9785009d4fb529dd4eba3484b45f2aca575dc62ab44877c0392f2636d703bd28822f8a1b6d747492be6e5318f225e454ad4115bc930ca147e2aea02782bdeb052789cdf0e2cc670459724114910d8f83112db217258d4bfe7b469c2fa200c6eb4b5aa9628aceb95502881d0f3ac8c1636157bc9481f0bd47f55987d50099c93b0254738d76692e00bbc5bb9ec0d53583fab86c6b1e87cc69889ce2166a2b523d900d449a4c5b5588ab53faa3746101609a94d39a0265f01b9768456ab700084f0b2942ba9891513ef35f793398e607f17e3ea46f356e429a06a03f91c1870fec3d1bad8ee1804755273326342522939752583b2fb3e0656668142f1879610530c4d576c15ac2739bdc6c43b51d00c1199b58442cde7dd02c4bae538d89c3a6d312e4c34404a4672aad4dc04f7b56fdcb6aef4353d554360b3a7ac18a71c39396fa62b743e3d0b008ce25c278fe925e3380090bcf660b042491d72eaff0847d5bc944a4f686dc8f38dc3aacdecfc5693e59bd66155894849f0f8261ea0ddeebda1d747489f70d193bbe8427eeab35a9562d1e052c379614341f867e707f4ca2ea893bd053a69c3b9c97d147b2c5ee2b4b3bcd1fce779d9b2a3ac52000a65443737f616f9a9e32e71f763e03dbe80880e552eb9f5fb72b8e9b17333daafeb47cbb7410588373140b4e77d0f3f93e248d80271aba536271445c214394e60b7ee14d3ac9c69aa2d673c088f097790dcf61d186b066f01dfdc8b206e6f97e8785738db5c38518c98321d31e490f18f82564691de082b51ea9fe89525d4b5623d24e57458edd1a479d23f278a43ec5b12610f3bf1c8a6cd6ddc66461e0d458adcf37616b4b1571bb7ffcc4ed3cf7b75d31cf4d58f96e1e0b1821e6037026069f56fd183c655e90aa20d07f8beaa23ced84129c32028e2d71a54470a21608e30f65c4ab1dcfc3a48bd88efd30c1d3dd112033ef59bdfba60ccfa263e6acee3be5e2ec7bcc14b63b47c2b07154e9f2188b670684890647fa2bc9696c915be52674a70b2c7075a7153024c9a8fa7050a63e63c24c3ed8918241da5fadfa5a1113ea0ef39c81dec1f71cf39b873bf893f22c10b37301dcb9d47d037f3035b31d1233f595d26be61d8941561e897251804cb52358f05a5526a25c8f2b8ebc75175ef74f271f95642730801e9f7f60ab143fa5e67ab97f21fa19f7de4384791109d7dbc79021cacad82ecacae773a034832cde04ffbd3058c46f8700686a47d0b8601121d031cd0defd98fc98b3b3ea7aa53ee629e5d986e79c31de7dc073917a198bcfa4fd213aef21923ee9681667df752800d91749f5180e776fbfb67e569fa4f80577c17dafd108476e9da0ffb33179227f4d0982ea98987b47d0a57350740ed0c7977a1964943c0e09ed5426f34a9510d84bc5a0436431734c68c8e8dd91d883539dcfdca55ca86040d78d83457d8084b4f1e35d218002f8fe22a4737df71b4715c4353e9256a42ed49808561b0214b5e2470589154dd215554b1969d883a0eb348f87e143eafa40df5322ea3351092e85710d90b3ed5d2ef0d627303885c01913c3d594f0b9178d7e0e717a8964887bd2af44c048c77337db1f1f4ba9d2fa096a202a89bbfa8b8bf1bc760d3b84335dc62437777d378c9649326031c3d7dc9384fbe4208f9f51e8dda1a48adef0491c16c3e0171480c36be122641f880d3b058c86199334e45dd49bcd30c6cc731de352a32f227288ed0c4aaa7b409c1c519a062644e12cd11f0a8d38feab8244ad032dd907bed4ffb07d2e6dd68003f1eb739cc03bce18c31238a918f17c889f27047d603672c2f4edb2ea224216ace1bfc863ec1090fa9c47c2fec402a01de34edb21a1e652b6865774b4193b826ba8d82d4b57c80ad1c4e049a174deb209be1b19ef756c6ddf69e9466ac05f857aa9e85a7b16ae8f0e97c98c5134066b472a9f1ea7ba83e9f32b84a9cf806ee0db79c38c31ce611a8d129ac928999ddbbea18ee87b08d653fae943e1950a8ada04a6118e43f98f310c28e29862cedd1fcd1703543290f5a4193ee2de0a2803944d7a0eab7151f2ca61dd325236147f3d525b9f654f802d17141efe2dc3e8ed1f2cbe06d76cce8370f00e95f71105ff7e6da7445f70cb6af7b3ccba7017b86410941438a7e620fb1c5b94ee5dbd6b35b6ea9670aa15137d462baed576c9a65217044ef94112b2dda9cbcd100d8aa518b904f5b0e002569f0fa19f87f47b78cf0ef9210d1a891317d703443911892e23e9125ae2c4636a0c262ce4e2304156aaf89b1c1625644d865da1228bd8cd37f44e4d900264e5a28f11ec33b6b3191077f5376df0200ade31d9e8c751b52fef6a9caf91023eb19423038a5e07d16068952599e51616ba175c74eeea2c6efe68942747f10d7b9fe402580280a34703973600f46cfe728ac7ff38dd22da778266fa5b2345ad57befbdd5693237bf5ae1b5cacd6126ce5b24494c6d7241d53b7d655088a8ebd5dd8989708e83f6b5ae48b393704acf937977a7a1cb4de08fa07c49bcafb10b01d943512a012719ae2615f423edc2d1fe611386339660cd297c28904504d1ae002c07cda01f8ded57d3cbfb4778d07462697b1c8bf7c9efaca93b5d2391b3ee2c09206e5b7e223e5760e41a6e76daa5e59a74e6e7e191ecbd4e6fed8d14966654fce19f251ee4c1f2df62023da152163dee32c199cf14b2cf9d41e362a2b77600ad9562731e6b5732a59033e00c9fd62dc2b329c47d783459504979dfafad2b0314a2ef94f77ecb791274648443d4e3e3238b1d3331812497dd087b9ab8cbe727080d3a2f7746e9fc44471ae48a19ebb8ce60457e19bcbabb745ef84436c3bb4b38a6337dbd1ecec3db2a8869a29f1dd49ad8c48914b4f19743891632a196b78bdabd0387bc8aad04cd7a1ed3a4970ed7d9e20302885b58f54ed3331ca784182d4e13911481ff931558002e7acea8abfc2df92b62daa0f61ba9db684e65a9a577eb6697e570590e5df385906e3c9b6d6b464631cc46ed81e80b723393ebdbe9951cc88ac74ffece1c82ce8e8a1a2542412666ff4e1e6f6697fb461143d20792a7c78330811106e87b71a052640ed28e7896995877e3ca97d791d6ee9c5a9a34f81cdf4c81c03b64b477d5066267a1bc838dff90719b7df7382d875ead54d30e98e68414cda9fa14ab826f93b9feb5bb90f21ed369ce50b4450a1dcc0409863cbe73ed3acd02d1f62b71528e1480cf03750ff4f6791271054aa0969d1eb289b68edb225063f4a12920a2d21f13a732e157610450d82ce8883910c28a23087dc097ee0805345adbe92e23935751b3a4d35a4e052ec0323de5e29d06b492040b900998589d9f9fb75a2a74b287b4ded08c23b0f77f6ac85cd2fbccc087ff5cf59c31e84201f567c32561707e35336487aee85699b2860f977669a2c9b7576b212e14ceed4080f4e33e59ee068c14a19772ecf9d16ed358d57495691bd6ea843f2a381dd2ab53164cc8df05010917e8e24a0a27f1ec658b039ec3febf54d4ef5a38b596165cd0f6ec71baeb9c9ee4c889055324bbe1c0d433c85129f4522bd6ea7befdc0133bba7cc767e30519251aec80f03e5fc6b3681e3b1856226273e329341e60fd59e3ca42c11a55816e6ea0b56a8cca5215ad6b11df3fc86c94ae10aa87ec2cc5d610e722ec912df82ca0e332340fdd80eff795f7c9bae61c233fbe9c9c5b87ce2f91a0616f602f8af2b8203c03b46b083f11afc97cc725ed368a7605c862443a8fef4f88bb37cd1aac76af22910be5a08f4ac4aba389cbb78936c1cf7f3a3c85084301299c08413ef92f8a842ff174981224f1dafaed98a3b450db176b50cb941a0def3fb52d1f2bb88881ab00ad87a9e14a7b8562585f903df48151894aa98159bb9d77e2f85b884c02c6f23cbec79c7c13de70e337815de75a1b3f60e0d7fc07e513a75f45d93b9b4e90109642f32c18d137d84dbd401be6e55dfab30714a360e26384dcca5715c5d9dfd571efa6a82afb9208092f5107a5f6db601e0733369b745805ad42d487dc5fa0f976b878c669af7455f24c80962650689461f027ea483403ca45542edd7ba11afb30b1e9a65576b424febcc88fa06a921573e3c8a1e37d593e62fc22047e9c6b5e69002ca54ed97d567827426c4cfb261ba446dac913138fb4d0ae8347a671995e5996f70e5d9f71ce0d01e5dc6f372809c8831cd18c94b91e54250322c9500adfa24c28f24e2662dae451d9051e7b6dac755c33099b56f8e60329d63534a5d89c12653bd95159a76daefb9e98848aa5195e56e5689b8e7cb21424c6aeb9c30e33afb337d0e7728e949a3b5f3bcc422bca96387b4ec038178c2ec42d38c32680989c02ab8b2a6d49b5e955b8253cb7433342f21a2dd35f1594c118a329d3fc914bb49c2408fe5471163e42eda207e6560a9aa10029b34c3bfa3812ac87e5b120b073483d78d079a0f132470996d689717874420441254e21a49f9387f04e1f4be77d0884e8dee1997f913248d4cc48fe00c922de8d79b4f455c1ebc106659ea9d22129c25b5b5ee9217c0b754553cc371eb9aece1090412e3929cb2b862acec8397f45daf0aeb5485b6875be2c500faf6185827495e0ca620b3e47639cab7cb253c629e03d087907a421efde377e8b109d1281c8e9d8852d13bd7e8ba0344c75b61c2cb528cd5fb6f53a137264fb45e89c8c302028fa9a6177ff5f939e1fbbbeb0cb30df1de5d62330b1395196046a47fe328413870b3390006338c1bc1e8534cdbd29421de49d6afa2aefa193fbe8951b688334805b6dcd9d9e4447516d3cc808b99bd0a75a789f0c8d21b5c7ec1082160a04b63f3c233834f96c6aae9936cbb4a814899e3d05f0f4511d83d85cb3fdf53348dec87d00d0fa2ab4dd0d74a0ab12bfcdf0ecfde43f421731484e3d5ad050a7512d98f7915563c4f17a5d4761e57514d43d1f2daf4b7f462c72f5c543af7e272ce1fb997e2ee353be693d03b37dbd2c4e376e92727525b71d878375b4741d592ed83594ca58af86d206e335337d41a6a2e3badb150415a4e95680e252ade9f249ba3c7be3ea394ffbc6242632ac746f970a538ef787749ca8713b4e446585f7137e2e56ab06bc75992c5f17c82dace8fff4f5243421d6d4172059fa87f9f4c8b5716f10ab2ff6af843f45ac5534599ed880653e87d9b6add9a6d2ed9434608721e2e9013b9e837e5d58c0e62da4df8d2fb06839e8bb982e44fe47c22d5b4b587d85afbe8b01102210dc728f703e6577a96f4264f0adc1800d4706e5aa511de0bc7bc12e63a1097f24a90cc79488c20b26ee2b4968fbaa12221dd7a209431f2742a941f2e0f94884377665e8c5724913d828dbdd9a6d09c97451be6df7cb962c14ce945771af84c4ae2b3fb12b5d25e14dcc645728b8d94875501f6f0827a07722a9d55386c819c0164ec449ab126696cece6709ca4030d879818700bd24c37273cccf185dd36cb647593436505029c4c6aa36d16a73b87df397615ec91dce7da8e3a78e67e4209b6693c35b734b3aff1f65a02845cec47d297a39cab3ca96775e8ab874272880975f391c56dd524003d2567f07a5ca8736b341aaeb4cefec2f67f8cc207aaec8f1fc26a9549d6736479878e66f2233b80dd68c111d087dd1f51a874512b8de38aa3bb96331bbeaf605c3420817be8b90f8c42e5c2361d3f1f53cf93308dd670c3c6d2d40ca0c322ec0c3ccd491619845ba9d84f7a92fe6d7c08502343727949f63f12c82a48e6b52437fbe7d0641f1a61cdf234b5ff309c6904a157bc4a187876e9c4fd3a5e38c84423ca0af24465e6ed0ec6f815c0e120d3235cdf07f6798428cfb85653330481378ea3454e403890482b628ccabf1b4bc34503c693d067f8b20c25000322ba35c2a56620b632376c6350f12aa5b91c024c1b10a20de0d664dc35fa1801f3985701c0fd0c0a3648b8732970646a630579b419e7919e88e96781b6a5ac343bb720b2582410142ef9d5dc609e1bffff12ed527f7701baf0033d15006e7df70341138e501e6fedb04cde03890e24d40fc4d6b8b7300e60597cc340e6c56afcc552f6fd52fb74839705790e50905726e553d450bc83d078ecbc2ad7159a64d4cc8e9a95a76a2a09c36ba4e6d31850bf5652d0ef0efd8c07604083ce33df963051837f70bc6ef43e4990537cdb8e49ed4c9460e709c21aec37785039f5a15e19c51a8637750ff66a00f7601ff8fc20680719f62894803cd5918491950af73fb22904c5b92f4927d3390f30c6c492f01b7fb6b04499895df0cf50c944540c29aeb8c120c4c6ad4a1799de5d55a000d8cafd7768130f41ae454518c683f033f53b2343fd438181f227d5724b012bfc07736535d7bbc3ed25b7f729aa142be9d8119c734393a8ae1840db523212c5ea181843534990d5aa5ffb5cca568e01603f83bfa622e5ffc82c5cd44629a478a0612be855830f86ba9b387eb9dfeb9e37304fbac707e43235d37d7f7e452d43962249bffa3d13f98fec1f81fac7fa93b84b67ca60b61eda0feb325b5b1a388a2ba7948bce1f2d9974d7fea08c07f68914ddd7f4bf1150882b1657959a159f1e3bc73b93f142146eb0ca434b1cedc339f93ff8772dc011ccc4edbea5680dd636cbd0ac65b71bf4fba8cf621482f7880c6fd5a2bc90fa973a4c3f1194815f52cae441cfb020451909bf52d6dba04ad82e9a73b7819e0a03991edc20d2f38fe6f4e6d01d9df772ac7054cd8326c4cdc72a0e9eac003bbb4ad67ba8f25505c9c1a6d60202af1898061076e37cade617d8112e9cd0275f2ee7aedff7537fd77caef09eb3aae3a7c92969b468b77d8b0e18c9f5c22d9feefc6ff68f54fa16d87f53fc1281467dea1240104c6026a3c0e755b3092324e07c9094408564903e0632b121cf75845d3e438bf29391b75cd28cf4755343a4a003b8ac9becc3b00443a86c1eac3fb7f37b3abe402808c78ea30ad4fd4db1241dae85308ee251ef8073686faa51bd97b6fb9b79449ca14120736073d07749a20fdc61317e830459c1fff2007b1cbe1f5d72a88d28391383f862208b1d391c35a1d33a65189d4820b1547e2fc783811934c49c7e8bafee40e4c62622753a00691d8cd32904469c30e49a254410c49ecb41dbcf8a155701912bbad065c7401123beb831831b1dbbcfe640aa480891db7044cecb833e990c58f778a9111542af5f4e723e8852476d79b1f0d16627f1209316410248168e0237637bcf9d10489fdb1b0100249ec5e6ee0212676379af0113b1c5e7f1207a024714a51c64bec60bcfe642b0c22b18b79c1608390d8c930a1612243c3344c8868880190d8cd2851a3c44c0dd728010510af98d8d138b1a18162c3362d2b593871343f1a09b258c344ecb8bb39c1075db46a4a3d1978c337b202f577820fba68899d8d37bf1a25c4fea413b02e80c4aec6eb4fca200c23b1b3f1fa9337bce02576375e7f548c247ec48e3b709a60052e787908dc2507245c3b457c2178f3a36122f6576ac20621b1e38ec7093020a4104410418b71ba1e9e64a1e9fc685898486a473f1acd5170fd24894ac4fad1689adbd78cbce6354c442522973c20db784d89eba512917e4a44b1c72af4b4d493818cd32304f0ebd1a3478f1e59a53b4a66123a619c748cb359e99fad15a5431062c82fac48a1b57b60246435ad7a9ab79500430c2fd1824ac43ae552bbf564ae691cf8ccab3c12f3ed5bbb12b1b31f381e89d97ab49d8f86056bb72f86865d89a8444ca1edc7a2fc686f9aad536e8da1f6a59764d0d55a6badb5d65a11303da3318053f944567aa1be549fe0073ca07d976cc96ea5acb9cd6a9996717da19e396756360f3ae629e504a7fc40c72f058b2d5c4efccc3c459a68c22afe91ae8a34f1c3957651a71262ccb22cebeeac332deb6e26e109ae75f3ced2cc3c74fe40eee0ab779d3dcc1ebefaacdd5d73602267d6923a13b2e034e1a172897977be2b33333333336b5f83ddddddddb4bbbbe9910a048bca75d6b955e95ea094e085ce12b7b2516bad9a6b55f39a39f74634a763966559d69d65dc9e65d676a0b96fee79bcd69ad5aa84b42e418d0d4dd3b46dd336d7506f6463cab4a39db44468527c5883d963a37da4716da76414b02b9ea3805d79f62c3c261cd6984941c17d1d373bd08763ea751fc7b99dab0de4616d0d4dd358d36c359d91e5b42e1634ab9a4f97a71fba99182e7521ecf314579a50afb57e33743c6cf051c16923c85b6f1f645cf63cec94f50ec78f04f18ce85ea2b830e82cf102c5a15cc0a1c890202649660697faed66b61cc771eddc1be96e66e6beacdb3aad6efb03d483b2639b998ef42d731cc71cc731534b43f711f1687f65724e9eb27f5ce03b73179eb3eeacdb82a392259ebb7f4c961411f09c7577b792a13fc2af51d2f0197da6a2cb0e4fbda6a0b8da33155d6e78d9c3aacc997e3bf7c80387555965fbc558af0645fd699031c809fe63bc9f65c926253271d1b5772d6b77edd6ba767777d7ee9d233bcd3c5fa06468c869c16ee62db899ef62ec3af6ce39d0083bf7463a8ebbe71cbbe79765d3f9cb7c76cba9c96c826377c6bd9dabaebb3b88e7a51fde67dd07199773eb3173c565e1388e39aebb53200078825717724f78ba73b71a5852a4555071f2d4bdfbbaa8b73eee3ce58f67e881b60cebaa7438436c50ba06a59429659e2ee8545ca877f4a3e00b944b9d524a29adb5ce1bbaef8766e6cf05695df823df7f2f5170cba0e32d6ccd57ef0a76169f2506af934b5dfe903a4b5cead3b3505ab32c3a118266be8b4c8789cc0875d41b6190f26c1f29cfee91c131c9738351b476f969deccd56957aa3538326ba8cf28a595d2aeb373d0b16fc1b566599665dd19675996f9dca2997da471bd535396187c8172ab4f2f0b777737b3ad37bb6287bba8ce2a70ac8499c70a8e49be3a73917d0878f65ac7249fb99ca091233c05d014402392b7cf53003d3bf7bccab818228c1efcc8ce9f0e131a48a77777777f5c7f3a7ec6e92a7bc7956ba53eb8dc76d373d1500c3c9a655996655996d5aa79966959562b9d5d7400c0e1054a084397ba3da2a37981cdb395143079ea3f9382e27acf566e60e3a95346ea64c4609ccc27133dde48e5e700f3bcb0f35138dff991c17025499058b71cb8814b6450899bffe010ad8f7654f29aeffc887afaa11ec76b9b5b9e91561f3503705fbd561fa902988baf6014e635deebf7001d2c4eb4214a234778cbf83272d10960ff01f91ad801def21ee836daf80c8c3282243c0590c8a08fbf742e4980a700122588843da653a8f78113245c80842b66b78f45fbf95029353c21810f311097c8a012a5ffe01079890c53b4fe83c20f0aa24512040bd291b480240816b60d94167cbd380d13588942060e60b0832a4225395c89010f3c386144041a621016dcc74794564bc7ef838c6bbbbbdb6af2d5f128621e66aecc332928ee74a7cc336be65d05ed0c2b6515954274d2ced5163df287fcec78817f8172d99bb3bc0e13b6e574429932a59486b4bb69b33103d009889820b15c72a9b7fd5a9d716a00ba9a14193c670d4140183b2bdcc87a751dddb80d14d215887938d77c23621ee99a7336f08a6bcd108879368e051c6a3ed6251588cabf207fac407f02c78213ae70adb93ac10a879a6b1bebc57ab14a05c46b3e7241af8d33ae3089559a045542acd27cc816374c7acdb78fe7c52acd790347216abce6e3f69a058524b14af39c185ce923ebf51c850f0e2e95b28d11c20a71571b9d95660ef35ccab8c43ec100fa4c6a37edc9cff45de6555a7b565935d3e6f23bbdcd59e25ea0851906695653cf322f40085d2ee7acce333f09318fce6761f7edcc54e62dcc1590cffc021be0c091c98a99a99ad1fb3143d40c337729776423ef336f8102d6ed6d56bb75eb14c63c366fbd0acd55bffdf19648bbc00c33d7c0166698b1d1053242f68d155ce9fd2365a1639c7126c8884b643ea5dcd14fe78e38249921e6e9f1d361a85cc9c12ef7e260339cde71af667142957bbd8eb89a8fdccb0b6ad606de98d17e0c17f41dd2c6c199e12cf5fc98592633f6f90c8e10a0b4bbbb6bf50674d425056d3f8e276682341854404fbd9bd743cdccbb8c049f20ece3adb020cdb21d648a8b2aa2ee69200e2a6e8c776dcc539bd896a7d6765dd77536b119d2d1e6e8a98d161280309d9d8631871c5debe3c9274a093cf974be59ef34e4caf5b1364645ee33aba47d4df21c3d7b4754e7d6fa4f2b38b481cdd0f3ea419a75c1930fb58159c1ab8e43eab4f3d1e6e4d3f9c9e7a997c0d1bab6655ba6cd520e2191ee6ead3ffe0ec69a760a3a3da13743a727374c9c82e6ea26091b30341f6f8826911e493243da573344419b230d94199a2f56f504915895420aee887211b9f99818ab3a2804ca1ebe422bbc4209cdb05152386c1fc32bdf23897934ef1b22949493936f6f4709cdd569073659a13dd4707d8f3744dfde3d9258d55c08af6fa75fa9c5aad640d38b55eda7a0ef916b7d7b47254f53954fe557905985e38910a649d19d3a3ee5b8ae1c7a067a26f290e2d4ccb007678635b5ca5adbe7373842c06b8ad43c4f530c39ae11aa18a18a1cd70f1f218b4e72f76c8fbb995249794091ee4827cd91e25a3b8d50bab18a66d9fc4eb27400254fc1b1f39e4b610aaedb4e4b4442ef5fa07fa4babecbb2cce767e00881afddf7a54e3a5b33bbae595e4d4c87df7e5131233cbb279b3a47dbb4819ee74dd5b3874077f45efef07c3ca5596bdb578f54d757d7aadb47d9b94a7d959c55a22e46f062042f46f062042f46f00285d85be7aecb716d476f3d85b9d3e30122363946dcadeb5a4a29b98ed68c5a2bad3de514716fb34e6e5d87a859d5adebbc9a95b9751d989116e0c4993c5a2d424e954bfdf4d1c828d5afbf7817c2cdd17783d4291d9defadd7efe6689a60fd0529e9ed8e585de2ee88354b8aae07647ef22e3da0fa0575843a65fdf4e9b83a65bd4eb9a38c110c953b7d24f5f8486295f51c22eeb8c335de1d443b8698a7e4d67724c13c9bef70ed80ed307ae699bf922e451b437584746039985c98ce94c992319a29ebf68aff9431d2119a32463a4253c64846cb64e12475cabaf52e6ac4a9f99c213c87ca2de2a3e563075eed70cdd0eea8e2c3c998b2f2d6532fe6e19c47ab04a11d554a98b2c33557250071c324f6324693036f7e74843e00087120094239ae8b23090558b36e629db27e83821109473e92748602f0ea3e9befb4aa0312c020e188796c7894a1c2d21f75b1aadbbbac9443e5561f56754ffb8d16bcba89cdd0726e9d7975c30587d6c71b2d6fdd47d25b1f6f98bcf59b1fe6e99cfb6460acb27ec385cc156fddfa4d6cae64a8601918381f79b448d0e2ad8f3e92668782e98075a08c0f078e3b28d80d8dbd75afe423e7add7a190b31ef76da51e8b9a5d273b083c57828f262fd01f758474a6f068bde5d19aabd6119aa175f9e9b86668fd0693dbdfa1b4dc51474847480a9930fce9e57aeba7d75bef3a1e2510e550b9d27d5825fd268549c4e66f7e58e5a38cd1cd0ff398dcfa8d11f350b745a38cd15ba7200946acb23e648b3bea08958858659d4b456cde84c42a9ba2e0c8e908bd75012431cf8eb73a436f5d4768ae80bc751d57b3a65b0f7c216495f5223f9e03a39cdc82d6e278e2868f426c26d631cea81267d09052ce09d699346ab5d93257b582748659b53102f2d47f08314f78e505fafa9189aa51b33c05ead3c78ae4638dc100ddd1c6a859ce29ea5682e3bdd349f881d3f10e69dd913e0e77b4316af2d46d8ce66a6a896d399aab0916e11fc306c9868b4d924d171ba3253f4f7dbc69f237404f7dbcc1e1a6f574de3879fabd90812aa0ec8ace366b7698c4838f7462055492c4f9d11c2176538d197e4842c2810e40e2fc1849cc0f5f68212629c168074fe8d0426cb05223a02a5eb082a40c8624a264528e7cb02421ce8fd55081166267bdf9d1273988fd21a13da821c68398a467b0028bbbd1e00617574d80c31a2610938444b4cee83c242926446d8849180685d85f4e152818ca31cad23279fd856b9c1113e7c73968d922e97afd310d88a6387ac922c88a23717e3b476277a37da4e4e00731490723e30c1c56764003212434a6a50826c424a16b680b929d235186293618d5e7e9d58588cbd018aeaff524f4d5c7a9e52bc8b5ba2d0933a8fc6899c0ec0faba6679ff6e23224d4e036c4aae94090346af3a7a066695a666afa16a1315c5c867e6a5d4e44f5a74504e633503302e38755d5eb0864c66d1a95bc6455c7798e0a428961c21473ce392533334f1a60378c5bd5b0565009e34726daae5cedb9880a1744d4c64cafb9099837e6c67351ab07df3d1751b9c18f214c8a986baf6f540e41021869622a413fbd54ea99568719421c0579ea5e0c424ce9969032060e3070a5c04a1555134008030b58194b601125dda0c480047f25ce0ca553a142c4f0f3a7cdc1456a414d09428b20640882074f7d8b8999417a4f8ba44c79ea944eb9312510e329b56d14c180ca536a6915219e9a9e52ea51ebc9e718103f3f2f0f38bc45143fd63c7dcd3c757bb73c2d1272c275dc00a70a8cb525a8c284676d380d51c39d3e2369b80cc3d2e447effbb9c809a137400d0bc6bd671850db3253ecd124f5e078a65d7b5dea3979f3d186f452937ed658a726f5767cb8a3774fa6eb230dd267259f12d3c093252273be36c66469ad4ec59aa5bd9c10e130d4c4b5e4a726f4538bb5b8bca54162158e1b431789792ad1cbebe5ca5ce1f0e92f309a1fe60140112fd077e2850666fcf4ef8c3b7aad24785567e070081c2b0d4cbc0d60fcf47bc43cd5e7d0e6c40b9a2b944f4a3d1d319e140e3da119b6fdbbb9d76a560ca8c574685eeba77b4273e5795278e565319757c547ebe580e91b59a77da8ad435194066b160d9206e3707c5b972d29d6accda853d363f628b6c5b6d7b75dd960db15b22534c3d96a9686a4c53c6c85413f9d06a959da96999a4d83f416d4b22594724450b29fcc678633f319359feece1b9a2cb9844774c5339a2bca452fe0f2d3bd3164500f54fce879b0284b7ef4b4fc3cfa99d1d85c8d5aeb8fe66acc5ada96b9cac0f8b969311b4cf0a337f4d43b8ee8db4903b49e0c72a85c2eea21f6d2e6e757f3327354d19a68f9dac6b3c2244e693e724e7ce56093bbe2ab730dd74895c00c18a09fb565adb552afdfe93b0b43e566d508c85cd1d551a7f904f2d353754a11d528a84b25aa4150aa50bd52afc0687a644c12778cb1304058748c33ee082262d5742aa59412a5e58ef5555f52887caa9d62a72318a00983c364599f7ee3629e9d9fa7d829763a9aabea5c26ab4299525b4b7efa1caaadeac4a5dc53ecb4c56fb3581c6beba7d3ac60dd8248a4a851204d62cd9219c2073d7ca185891e48204e3f25354bc90f59603880cd3003365e202a890284276670050a5e33e020530333acc0c245121a6d88d3b974da428f4eb1194ebf81823bde1b94613183d104bd0601d1cf1f585c396757ca11fd9ccd45c738230c6c655a83c30a03af4c49d587a57311667145cfa8a29f3eea3ca35e210cd14f1298c54398f5821abaf102d384fc60dec53c36cf9276d36eda9436a528a15310ab4e41a522a8a01ae5c5a39c4e413de3a98759a4fb053cb5c02855ee786a31e11bfee2d3e910f3589f5e7f98a74ee7b136791598b5c118cb4f17c2439885c3a7176116caa7fb18b27efac6dd519bc02d8672cd9005bc4255e170facb04d61e607eba48219e4f3751c12c98960b460929c4038f7e76a0d419ef0e18a6a5c573a0eb278c123fbd43b9e68af34155e1155702ee0735c419c1fc34cb44c5cf4f931696a569073f615a39de943457b53543d31abcaa2ca83d6cb0a1666db18669420ad9ba489de93aeedd5043bf75e94caf8e35cb94d42f98b57734258d5beca7941958959c60609856b32e08d3c41d4f36fc8469c294f4d3616ec0424c5ba4cef469daf213a6899f5e2a72faa6284b0d60df3ed41494d00c834a8082e23357a81e389c3e7150f183a3633f9e38a39f6e1a6ad6e90b4e4d2fc33474d201a7a6e3f84e58f8640587d35fbed38def24d4a9e9fd9da0bc9a751a326d91424e3db0cef4792ad281c9f4fa797af2d3479317ad669d7cfa298879a84f14fcb8bd3cb55e3e2b368e3fdd0f4912f154c51378aa49b46a9552848195668e1944e1f39ad3c87035d6dd41f4f661daa391e16615ca0c35e6202e2734438df5d29c488b796c5ea3ef50eec9af9ffcfa3803c47a358b0bca69e2f20c25183403c482754af313cb6805d6cb7fb8231704459b697dad30405f8b582f4e695e9d650366cd2c9923cc92af303f5f9d239242a694af2fd599ad786d07111a7ce525d2c9ca9f602f4f574820bce6a3498dd194f4b2b362e5a5d7a14af4523a47c43ca54e3e8e97d5a51cd2ba9a8749619794cf5c7942fc0bf34b5fa806ab346f304cc2e18e303eaf79ac5933ae4e5de115f784adf08a0b9aa1c63ca6665d1f5947afb18c5e2bf9c8427acdef674f1ff793daac984e55d79c0b9a2b6d66e84f35fc0a8e47aa7b2821e6b1ae398c0ff3a05e73981fe699afb98ce61b10e329e9b5173c20dbf82827ff80fd28d76964b876be07fe58f1e4bad367d7ddb454c4e6ad775b0f65aeb5d6ba39539a7df2bbcca391e172cfde6525e99de69dbeb0cb4c6961d2f49266187699a1e61af205cb68aec61917eb8ab91a8fb4eba3fcb878727965a2707ab160332ee6e1d79c439a68f08a6301174d29579b7677333383b43e8343b25cfbf485fad28fd0d0ebfaea729ae94a7e0e817933b4331ce58f476878869cb99e237a094a507e12a9a4a7d79a4058b064e153ca5abb76eddab56bd39eb4d65a2bfd9105b7776bb6bb4cb3dbb51c5732993c206a5cc9dc7276adddddddadd91723ec3770a06038981c3a62626e8c66a90c9da13494524aa9a6699aa4b5d65aa936e5466b5ab321f080c951734371e837664fbd4b792a6fc7f348e0b90aaa409d9a5f8207e48c6982b7fa7e0609008f67cd001262863ca1d4f3817e06004820000f0032581c117af4e8d1a38708e09742edda33abb5a6727efcf8f1e3470ef8691a9033ee3852effed98903f05c1280cef06b63002522e11380da5022448a1421619ab05aad562b137a88c0eae6201c848370100ec22ce68113c24580a700052c8052ba8066ee05782a78af202a910aa515ea50a7cc18f2a2288ae2cf01780c20404f8fd70022458a7805288001d43880c7020212e0a5f8b6a4033c20670051e34aa9008faab0c20a2bacb0820a9afb484d9f10f0809cd18217815aab04989941afebfa74367d8047bfb185084420021188400b12f02a30010aa8809c01e48c1054336ab7052ce0820b2eb8e082054650510c60200319e83400c48c2b9d6a806a40670495bb8005cbd9776b5ab3cc6e9652a64c993265cacd53b2e98d506f396ed9c9d3368ee3386ed3b2eb7557a2266aa2266aa2265a2a11d1b26a5f4a2bc4740aa419ed2ca355dec0819a2b6aad73be8916fc0932773aa46e3feefb2746b4dee5f0b68db30e639dfe58d1fa662dc5c211494a5b94528d524abf0a663a6817826a469513497bd1ae792d734e161cd14c47d3dda51ef6cacc943fb0c5b8d241da0d96c1cec9d94dbb67d8dd1dd34dad6dcdab9f51205caa0fab2868e90bd2954edbdac9f285cb957e3d2b39efa58c2bdd32b775a651c997acd45a6badb5d65a6b7796bb3a6074d0d4780822f4a03bc227db139b9a191a1990524a694aab94db3aee9362f764a336630ead4c4c8e940feb837934ee9c1f947e93c39a7d0d41f643552bf51c480e53eac9ba36737377edda2370dc93fbc574ea094c49470db7db874ea370bcdcb82a4a29a53b955270460757faa9baf452a9a79abc1d2036dceaa59eaa03882bf5349054c775315026a5946627fba2bde4a0f1fae7ed77373b3ad93a9c2b40a752e814510b2b68198c4c0e8799f166719e143dc9c9d2ce0e8b72a333d4321c352889e3bbf1f272abfc4a1b8707ad5466ded5db478e35c8c3b3f90cac40b3d6be51eaa1b49b230a82f66d56e74c3fcbdc177345b8e106d8a32fce4b8fcba3863f841e9dabcea4a9d453c1192e6e0f4a5dfe74a129f44aa51e1d385cea92eaa0e1b677fd4180a55deaebb8ad33d5584d968e71c6eaf3cc221daa3f4c5d6348d107b65d31e4ea82302464cfb831cf4543405a0b6e0e20d8c08b8ec9353d172161a459416206760748c040878b7a2e4242051c106d807161c2d8620b258e80b98e4862872380c8c0384287ccc9c5f15c74c4924e8c7bb95432ae7d2e32628cce05b7f45c64840e342af7c673911137c8811156b0184164c4901136643db0f7de1c28239ac480030b239822237c9ac4d29801898b148838786169226ad1bac10c444e34c91273d93054840f3a9c004b4cc472748ba061862d0ba1ae081f1aac518c438306cdb555e9a2864c8c21d92ab118aa988323adcaab45c52cb22a47dcd0e2164755889f69aff5aea88356536b9f6819430b975ba575029798c55e15aa00e1f8f5acbd3145447cf1a3fb0b381ddcfb5c4404161e8820b269dc1dcf4544f4e099e0fa731111307044dc1b2eb836ae8c14414324b163c310446871753c170dc182ad074334e9ceb8f4b968881f1586f0b19495986c8b5c47fc18e38396dd5b4f197de6849e3d68862f69f02422050214000819e4e08b2cd080e1480b9e365cd0440d39b842863462d8a6a8a184ed872b5903907c389ab28216b011849c2eac9802450d28d600c2a77e91a381f044f6c4142fca6510a00009f05cad1b92882f72c1e0678cc7cc3c175109c206836e1cf2baa3fdf9cc76d2c8ef97208d01e4d84a5ec97743b2dc21b03bbd93cdd3346ae01b92e54a1f02bbd2896e76338700c495d3c648f6660bcf3f70103ebba4dded99a0c595ce3304b06720687877775b1eba2e478a2b5d0577f29c73fa4b1a5752da26dce04a6ffb7c93c69573ce59ade8184747c64c293fa6593b3b26fca01b9cad622f88be41d4a51ed74a6be83b9d9a1ed312fc827065f873e5e379c80e778c89a999ac12666abaf48aee0e2a9b2b9dac66b0271de38ca8a09fb5bd93ddcc154afd82b652cfe438c9314fdbc93ffce33f404ceb4fc900520c7a5de923d77232c3597fb85ca9488d2ca9405f72ad4ecd66b110d70adaf1dc0aaeb492a7694c32e7e6d51a264fad419e5a41567ebae9c53cd54f66ecf0d3bd3126eb25a84b2de669fac281a8c9506989abcbcf17218fcb0bac9bb361681f56bdc61812aa15a4aee999cdbf04356b5e3153d3b5086d718d31bdcb4b46c109ab5a8eebda52119b37bd58557d4a219bb353aec595c36655e65b8fe0a964959669e0c8b588ecf86d8855d54d68a27aad2733bed6f6fad2307cf579c5649d82ba7d98877a98e2a8e44cb7353d0c19aa190120002000a315002028181209c52281280ba34cd56a0f1400116d86487654341947a21c866114c4400c820c21c0180280310620a454d1d80093af87d57008e107d0f382eae4915aff97e6c010ff19c4bca31e926f67c14a71aa581f0fdec3c384c0a429db9435c7e0eea58c0612b544439cac7356348e7d685eeb58813a6dc52eab77e3168d28f46b2a33a886b6d8c369c6d8cd12a41f546baefea52768f8167673d449d7143c3c8dd076354a33ef0ccf465dd5ad3d965f9f214e97c9e9dada26562ed6b237a60a33853167900c8a94e3a141d803c2c08b9528c6dcf56cb870432db74c442cdb9652d13de599880b539508fef38f34ed9bfacf37f5d27d2fbc81d59bd89784a8eaef3706cc1fc63fe90719ff582931f9f663bf21873710bd3a855cfbb93ad0bcd0e1cb5f4bcaeaff1d52ead31cba5e41d6709eb0865c05a848147e96e962c5e89bd67c2d3c07e416c9d7907396ecc37fcf9230f82112393b9a53363e4b608113ef2ef61f0551a7e167d4505da9edf1e5a684989ec797b2725ea3b54454e64911996dd8fddb78edbb2941fd2b9740d5b3374dee34f1c53c55b7575621bd9d9bc44138a4d7f695e11ce30c3cb073a169b9a4aa2da0d0ae844c81d281ad5982aaa508f815424e91c55b217a4d4fa8dbe5004bed959a3752efe50a5f41b38fbba57491fc2af4701f3a05d08914f3a8da5ea9cae9111f353ab850b0530f2c2f0c1b0354bddcac308cfb7d38178c5bbdb3c4a54b7271091620719333a1241d33170adbc45eca1058b6d1b7e9e89df4eee2cab85d4653a9b052f82a3caa2afd3f283a30ad6f651bc87f75b0ad2025f4a252dde964573d08629ed56bd4ffce29a0c7eb2a0e8c798184daa538ac1e4a020a19e3905091a00870132078f4f78e8d1396984968950ae4349e2ec0cad9e2623f58de8e705a84a04ec11ed69181b30ca0dfda17211cdf0480b176423f38b9bc5b71589c3294a89ea21b157a27ce450c8fd78366d95f6cc83fcc975dca8a99012b70becc1119a3d7186b7ade561604251dadf57aae54e633109dec715da3fc89afb81b4db292851c403a85cc85e59e68d41d5f4c13e13e5fefbc9d1f9139262bff219cf5df41c134b0bb7f8a25989bea18420e19e801cbdd483a747f9d5de180459c8988248c69d1b86a37611376dd2fd5a82766a5f3a2a2496892b6412ce7c9e437843c132ef3e7ba8d827c03bc54fe366448d37f8a9a6a3c178e5fb41e8bfad9841d76ca5213e128150c9fe7829c3f5d018b22f4eda8f19dcde6ba443c878b739fb9c636330594e765658efa24974a0bd812842e8cc387a2b84f98d5eea8e80565e8a2a848088bddfbb9e1ba7e248fcdef89f1d8f43ff913cb258ac247087244065c053125a4acd62c0f66511fb38c48a348d16f6fac7a4425bb5f57e05c9f9dcdd4c19890e0e45f88e09bea42046b705e27b1d3ed108ab697d6824452385cac3164b121638ac187566dd6d963e5775bc022bec77f442b376d6ba0d7ce5d447e439853320957734dfcd3e3b617fa12d759eff61675c562014e48a3ae0584f5ffbfffffa3800f03683455428c27ce68f6f74cb2a26a1e78d021b9eddff676442ad1bf58573567ce920248a863a9fbac1decd72cd0e19f96ee91ff63ee3e9fbb6a31dcbea6582fda9b0e74d608577d28162998567733c1e8f05abf1f7b4397f7f0dd78d8de3e251bb38ffb45c041b35f1c17042714e3e58d3e3f5057e475679eb9db52d312bb8517c661c349fbdbf875bed931ac4f3e13d45f1861a7f51c82acb4d790fb8b086417c95f08724c26e1f9c8c0900505ac1d6053d9483ec5266a5d08bf5b5e31abe97473ed65dc7f5e544d64caecbf324b328e004022cfae956db54c9d4cc9c4bb88e508d9fc08798667c2cf81173582465b85c9eecd3dd78d88694dab70191c243c688955a14559cc0bc0a98980150496796e5034984b4dbf27d70da916810652d2b4d5773308f1ffafd12ba2f702f0c08579aab66ae5fb9bbe2411484f5c889f542205d7034a442d10a80bdc45cd9827ecc90d2666b645c5522f7a5e6ffb1185bffd98c26f4fa47254e5b3f6e73c8791828db59edbafe948b62d96e047365eff035c38aceb228c44c03f08c14b289d3c4bf0f3870d744f2e0fe5929c94119143b2f00d91d96f7024bebbc4706fa8f776f4b0e4e0a3439211bcc0bf0f5ee0dba3784a02ad56e9b63b234bc95ef293ad38f0b4be0d5557ab92bfcda7584424cdfb3fb744ab5a32a68c3c8123d903fbf5ff1ea750bd147043741d1fcd8a47c821c6707650392b0fcd35a84df178cc7b20bbe906ea65f076748afa2bf181f8cf82c06dc4fa1f49050cd715d25f1a4892a222609fa81a44237f8a5f2b4a25dc33325452e39766b82e3ae49a8eace19608e1bac4a3cf33e996a277d553e2f6136549182aeb566a4b74a4d2fe85b87b54bc1ab07e9515b437696c63f30f0ce82e198eb91655a3f7277ddcf04ae9ab07926dca85097e4a7aa0fe1756f55ad8985681080a05e79d887c4c5bb609e58367968037b0a8cd9abb4cf118785c35a2b6221749ab1e5e4db9c173fbf86d457f1ea0a93071fac4245603e6d247ee45efe738e8681718d05c409f774d73cf4cb3cecb94b4d45c0cbc0561a2b9c8cb07852e6c0ca97617539d3e6a022ef0b5f26b50d0f1b0e4b7a7a257f25e035e8161b555ec837e9614b37a90d6f917de20d336dbada1941d6cfc77963970395e7eb58397afc2b17dddc72f806d46ea3a63f853f441294c9f51a2bab0f0fddafd486b2a7efd50c729ca254b72b9287a7cbedda89461754223ab9483d44aed23a55402cbacc6aeb72e47008c2a4af8f2a89b9eb2ba38b32d90d32ef2950aba337a90d59ed995316b732c0152d80c2e62fdeffc84213115b157b3cd2e477fc1e6f1e4ed1ad8a78f9c50e50ad00d334beb77d3634df39fc58153ba26d106a972e60b4c99cdeaa75ff5c15f69db2e2c6c4c248bec2906d9110c914d61ca3db0003e66c47693ab7ce01a2cd304200038695ef495ca0316ea2f7c6916944d961c7863c5b7c342ad5c8089b5a6b472039b0298e3ca3e181820ff43885a31b5636a93fb35ab62b91efb4deb14aad3229750e1c4c4cb1a106056fdafaabb9624b707e596cf82f2e79f50972757258a3c6f6c18793ef332b2195cf4a3a394fec721dd709782516283a25c29a1001e36349d99593205e557589ea1fab0cd896c078e3a55e46aaca94f5e5accab943fe0faed6fd5e0d29aff3cc6a2dafb23dad48e724ccb4ca404436733d3fcb4d3c2c8dbea786615eaa6937ab03e31345ff4355cb46061415196eb1c0b93035632baf9e0bf49fae8547636f6b3e14d71ef55992819b03de9478d5f872a0c91e37b1931fc4fe756a0558394e8f2a5ba3167e6aa0ee872bd97c6a720db711a1e302b136577778c20bdfed633ca331a97360d7da986b3df3242d44e077e72608e55c6a5b251310f8e99d5bf667eb285f9c10c496db2bc1cb81e5a818f6b8b31feefe30ab7effb78443b6a5b5709fe4e940b8eb4e7b3466e9b42b7b207d17302fb42fc754ca3be3e709b5ef5945ca1b3c5c5e9f5ed8a30e567003dd518b5bf3ea8bee48fd9995956e6f500eddbe61ed0442442d3bc11e14d8c4bbd0ac071afd93d7267b9b41ed8fbc4cd3fcb5a9fb178e1548f03b2c221930fff2bb6503eb05a38446e6b3c968d49ac8f056da7624be82c8a013ba0bbe0eed0aef462e5080705637b51b8abe91a7691bd960d718622401948b9a300728ae56c4fbd92b36bf025725afc0be098b5c20ea89b1cd810fe5d8ab96df77723b4b26c533c65b0cb227068a01806e6fde1e3ca4f75d99a3341809ee3a956f219a1c07da907cbbfdcfb22d56e253acd1d79641e0bd642c1910feab51016da748f9df89f75ab1d6245db7b24271860015b3381a10c26c896e1c4a4ede7157de275194bab03785216f177b7c1ee651545358a4d8199025eae1a65a9c0d9b0f25cd5ad5c2ff5d1479c7008a901770e17833ed58a8d929d5372ca73aab2de538a5c0eab97e71f9ab3536ee35f4b440da85cd85f44bd5690a16d2b930a11d0888f8ce5344f51cbac912696bee2e563e3ca5d15f1f49ce45ffeaeaccf6d0360bcadc0b8e7f68e4d48105f6d9042f97c4cd9cd18db009557e80795ac18da3804a01331aa408f560113e38c0bc7da84ff7459e0fe50764b4a949c698c1b19bbc9d4e43887d367eeb51922345663abebcdfcc16a697c9e1498bbe1f6d6bce3bbe7b9f4354114f3bdcbab827f413078bfad1b9eec1ef26e64d170e7212cdb53e6b51fd47a52558ece7d785c1625e07bd5aa23865f86d793e1bb5ed69d51a770c560c5c03f6860b485b587d2c4b6def9275c9b03f8d3f99241a5bf5e947db8d10bf1a87229f2f60f5b2fec79709b433912b52d41df36667e75d0916261de4ce0cb7f1bad8fc793c8ed7829990e0bb0aa0bcf82e2c4c482355aea434c8e50b124981c595a2baa803a81041731b7ca92f423d45e7eee63c70adc163d0bda203e8e4aa0f2bc0e0abab805bb5a4eadf4fed43434cb7656aa6a2244cd671d25fc0b690947b64b51262d0318f48b0fd7157d5b05d0ea575fae153ef8fea5702cc65cccf5e5e603cbc443121ce795201693c5ab7ac2fa7af989469b30034888970a6ed36d250dc4e7c60d6bd8c23961c679c808bfb6a28a97d06919ddcae5f2e52c675baedcf2661996556d2e2e719c652c2d572c3927657507d7cb0f2bc84bcf254b9683e5f7d29f7764157251f9cf68856cf0e1e315af9e816b3cd9d1ec93a85dcb89ab031051ff211c74173f147750d9a93c42536a8ac35ed0cfac68fc6bc957d0b27ec8acf2591e7025fe23bdea21b964ede69fa67a75cf7bcf74acfce48fe1fd5c9ddaaaa31e2f9473eefe3451df6c4427b4c47c44ddb1974639ccad26c54b5e44fc119fae1f04dd3ebb018a67a13d99ad03466ab5af82100de747da4de1ca8ed86e92d656f01118bcb3b64eab3fa5882786985c5312052348b0719c29a908cf50cb6bc084359eda5246733dc7ff34bcb80ccaf2ede8a3a501423301f85253d96ed290b514c32da8e95e763f6f8f4596e26ba08e133da19f468b934cd38bba0ecd178f9d57328b75fe6899d7478f4a1b1ef12dd639ee8da5970cdd3d5b9a3db26fcd0a618f964baac90f876ad838915d21dc4a8b7e2187f93b6f7ee756cbbba6e65c0a773b17a5d05a1a0eb0627e32254d29f6bcc2137bc8171f9aba6bf0c14c252552bbf169d52aa29b1452d801c2fbc2737ef7a0c6eea75c73edbbc76025b7c2e5b173d55cf8a4417d3a9485adb282a77bf9c8657f7f42c8e9f73d3f1fe20da6816feb48feb290b9df8b728d4a10d454c8ebf50d647a640ba3d614d2bdfb4fec046f6ec623a93ed849e635362d23514d184a7042b5ae45e9de1c39c5f50f2b5f7e25e8743f1892b2126e28afaac9f6d265c4db114c9c8d6c04aed9ff2709034fd2815f0ecb1abb84a41d19bc93fdf1950379242a7cd6b32ab257e905210dad65fa89fb9508e3a51345856a794faad129447f9e738915f4b3508876e08957c7a0c56c2c92d22b45b928ff5f341fb353d967ae34bc010f8771f2e897ae13d447a82139b05e16049d1a50387e47204f40864c469f5033b1c9b8f4f81f2968830f58baa951c75570e430796d53713a8932b554291a3a9ad64425e25ca1f8a3d59298a840481533b4f46154812ec033082cd6b2b4bd2d05db61829d22ed983eb1ed18739b25929be7f820cccab4288f7ac046bc41dacd5944a03be4a60c08e2b500dab500ac085dd440e37d92c67044c715947ed2144ce6844358a4578732bcceb83a89b4581848b5e584c6a7de3c6946595e5835b310cbf2d1147d1d622e8e37a479297c6822702f8964949bf103d1abc2b347c78aba0616a0731fbbd44d6343766a8ba9bd20ee53e07c9987a9069e37367c45d68f726a3aae0ff94d13ad89154eb43c63137062249b74e98ddb1587b8021e5d8b18083845ac80eb6afa7d3419055caecfedfd4f96cc2b9ffc23330dab1270546a58090044f4e63a1450d82d81688965654d6c296f0db1f3272bfadc75b36a055efe0469fc99d852602df8042c0b1816e8afd64c2c09e82612075f0b8e9c2ac39d391e1a099c548696650ca3e465208e45b38ac3aa79d166193780ea3ea9a90c7b256996d81d827c400ebf05cb56bdb1f7b7ea5ad4a2a61979ca407c76eb012493bf104157f5e6ce125583c66e4cca58caa7a9bc084aeaaf417d8775c7110509bf469a4891cc7177a3669c692c90ae716c77e6473a5b16a7fe8485db3cffc4f45f2031a03b7a723330f7d56fdf760a75d8f8507dd954254ebf14aaab364ab5caa1ba4a300294a58cfb48b6e488dec4b295f10e622669be43cdd89bfaec4a30424262f5de21e40d5c73d5d3d3755335e26078612253bc59f7d5e9d8738bcb23c349aeaf52d1efc5602968c26e4b181c0e15fb66203d7b966496c182794e76d181169b0fb740621c2bcc7b34b32d9d38c642938420891de4fa02314bc9f50e68ed05fc51992d2598df12cc0326cffea6f054192d9b923c03812758f54a258fbfd913580442d51979ecca682ff66cde115f6af5cccaafaad16e60140e4a5aba538962064a0806d4be62a2efa6fb5e78815374bfe131a98d81c0376bc67a52f58f37274672d6f03e858ed766af871e6a5ce3d6991d036aca7e2edc38ea8b2d84d807c8bddc048c0ada0b20802ffb3bd3f7d9da93d736e879b8e9b1ee34a065987d6736c76c1c5e63a4351857c337edac5d9861cda44222be6a1ee827baaa102293cbc24ed65bab420101f144949671b9cbfabc79c9a732cdf9f7238a3b8c413e292b01996e802f86bcf2a2fe870b4f5d59ed865f9fde89549196ca71f7ad1101340e9db7e7bab16ee97916f49d68024eaa5e0f43af918d8a744c6aead0154704482b458f12f7374eef7a2e822f12992a55217455ec502b11fb67c8738fba50a5bc8590b1bd12aa0062edaf8ae823a56ab95345450528e2fe8dcb147669c98ca29b663bb857827737f000f7bf5b4e516644627fbfba5a13a2c30c3a509a005ce46de317c9c92027f7be5e05eaa3e3671c2d9f21183828492223fe0c08215f571c1af54d33d4f1a51b424a8c5a96ba0c384687605736b2dcca7e8bd6b79fd1761c1e6e42f9a0bee9370eb44304266ddad72412e86916cd08a2aca8ce49f00b6523acd653e36fe482dcd325ce1fd5f8ea916d8a5ee8de2fe7328599eeeffc443c8c83f653c81c59561779cca6403c03f092dedf6e6805a44a0eb1715fea52097ef096f00897447a1e68382c0208ba429691e252a87c99754c4879f15834fc614b23a3d12847102f3e6429fc3ef642c923962d3441eb08c9d770e5811fdb0705e2e409fbbca37ae35ace3efbda3bcaa8761b5d31c06c8c24fcd49e177797d2442f06efd8e77fc521d2356bd545f49b2bc8a561ec7d2ccb6a161c2c21b5a5409a647c23327fbbe7124a4278f316df0a5907cd350a3b91066f4b0d11bfa585f973c26b94fa36739c40033381161bb5f475ec0cd64c43a45f57cefd478a46d7e44b558f12228ebd6a0623a48bb8fc61a5dbd4610c9f816277f2110a1a7d8dd0456dee588680cb024595e9fd0fd7da1879f15bae4ec48fa0f32da853998e5a6c88a2e09077709a3e71c6539e804c57a69391ed2de97b480c86f6234039711b3dbb269191aa312be1370b148758cb52473c69e586ec27fb1aff74fb539fceaee9e18ccde909075b6429aab126bf9839a4acf7b143f58372e90e744f1bf8b313f87b9c4d4944ff07174a30afa84145a7b5cf50014fd1a982dda77c4227f0b788ade3f9ff955f1e92d98d2dac36941347b335fb711bdbcefbe22f91d7a499ae5a7502d533c3d3af0c25cd73ac21abd21bcf6d216ba630c5705432b70724cc43c588faadfe5dec5d8e35d035181cac395dd4d286f2c365f4270724ac63578ee8ed50c8d4ffa3c9ac993958d2d14e115bef45570cca231e4ed2c5c6c044b7b32181d18847dba60486f5bb5c8f6051cb4701c3d0b04c5238bbf2f065ede9612c60900af14c2c8d1058d0ba40cf92accf1271295f964a0c9a9b88be24ab25f42bcc72cbfc6191c25e0cfef73e544ea21efa5b3819c5456409ca6b911101b30f5246a87e3a1a02cd4d6f50b9276503e756b1330709828c635da8662b38e1fa3bbab7a2b220b7fd116b6d45700b0ed704cd61b455246d4b855bf43fe4782620bc85a4581bc6bb2ce8c54112adc067b04519e3b4d0940ea99c532b216f0f7e33ba95dd6806cc1a9160c951b8de42d064172cb8563be3bd4e815a5809c508e2c01f3440594f74dc6fb99f1143691365ffae2b4e22b641c1253064abcb7191fc65ace105699db7a616bd101a986fd71e5c345064dbb83e1213f95c01c21d48dd21f39ce70215e3cb02f1040f5d47d9354ad84dddf665394b9ea478e20ed9051e428fe0aac5ada34b27a5114266267dae7f8595e7b234690998317919050037a42540b52bc3e057b24dc9eac6b5531915d536c85bd478d61606c9119e660a6a01e982231d8e70e6997939d33ee33fb26e1db1688aa7f6399dda43d72064f0dc368e0b0069186ab479fbb1727b7109a355decfdbc5f34dea8fab9f8cfd79431b20f05fde0672fb1d1a1a55ad0d5fa991e3ac145afc5d7717668424713b577fbc288e97e382769ab6ea0aa738b5f3ce971023c005d96d988e869e8733b0ab6014b419aeec4ea40484e32e4d9f95c94bdc9a53d38ddc4acb8aaf4cd9f25efc4f66444fc296d82649df3967b21692ee2b3f4dcfba6ac864348d8068d65bb8a1656f96e065fae246de75c536e76fd25ffa80e794b15a94905a54d63efd71372d5fb554d5f75130025c169c2475f24909a37206bb72b6ee9809a3561020783c894749e4602bf163fd98c5a8e1599dc2fed9c27117cf0ce7d5ea7cafd3167e93d3b90f3b2ed6a875c9e6e0c6abe7d42f4398fea3e808c3b7174033a238c83965a581557319767eb5ff6898d3c6da8090192cbbcb2b13783ea16422d64db54c18d75610b65d3b62fa32d81277d4cdfffced6303224e50422cc0613c6ad73e39c1e13890278897a9fc662488a9962ffebba368b2f6c3d46958bcb63a6c2d89e5d8ddb85a6c98666f9a092396f1ad16543cce5e90d0c69a8bd517c95adebf7a7119a3c0d468ca28e254baa104634cf2f9ff63c64078a740ad38ccb0ddce88cd530faa262b7727499eb1fa00a2b2d54f1840c8d13b31a0efcb302c12c426a1d63d151fbdf32fc81f577ded8f534bf8af537ebc2bbdc824c3ad0b54bfca6ea53edc1a41b08ac8a7ab3417d5ae64757d3198f065d7236006caf6b477f48dc18455abcaaf39bcb2a217ec0c0b322a178e09741a07bdf5e94a3cb0fed4c557cc3ae4c8d457d258182371d0528558d42ecd45bce68aadaf8636e38db64b2d4262995e12c1f91189301f94d7c02c5da82d0e3aee2199916bac3cec3920524ded2eacaf045511c74034ba5724f93f57d00ef78d384fc08cf8e60c9cbc78b4d6a781f5dcb51620420cbe220991257664b029181c003eb9d89a88eb24c458b5d53542ab180176c69a2cec81fc0b1aa3577e9b67d461113d7190c6ea8ba6d44799cd741eb46b43a8a21b38d8395ec154e9277ed1fb49c36f5b61cefd8365840866a21f30bbe8cd703c41ffa31418981dd4a9aab220703d8b5de65290ce091b8a492c6c1d29c68552b58a15df58b37fe7a27648146c45d10e276c9cad63f5d22d98cb352c470e8efcb858db881be27154be0e4dd033c638d1cc49541deb6b90e5d878de62c58fada187452029f7bf314b3cc7aa8997aee0a326e60cf05c4489d40475d154c403c2f3924b2a85fb9306fe0917b0cfdc1aae13763671197dbdaae78bf335aea8643549df88114b2e2a9c64c2f0e58c92b77eb52d399562977615c4823fe17c33dbe9d09f62bd2f3faae9ddad315e104c5e6312d07bcffe792d866e2b5cc9bd7773b849776fb5e79df2f102a48c4579a0b64420f42aa61024782873cd1087b5f927c301fb06cd9d844b44dfde456667534d2e7a9b1893eb9d020ea34ec99d2ae9c4253e02ed221ccc084159635011ff1e9549ed5662163e43a9e15ebadb0b38fa2161a7ec29fb12568e763a0ad11abc0a8c6c3f41761bf2523513114b0390f8c17a4379c26bc66cf1afdfade28c5e47ae53e36871a57998278771fe257f86643107b93090834ff516af5ec7b49c2b2ef391e0906ff0c8567f4c8eb082d741a3e9f4dade60c97e25c14d17e24ab234d046217af976dd85ae0c801db0c79ae2bd879ea91ba10bce8b456a6c15f38dbe2b4bcb500bd0b5360a4cae179a71fe6aac4b67410dae6b2d65240f4fb5aafdb43888162d5e99ad03e84344b11c4e6b3e4cc20c12810128bbfa01b1304a67e8fd97937f4b6a246c190c9e2ddfc036756b91c91ddb02ce3f04b4045281dad8c70474a220d41a6b30dbc5628564e7497d6dd6b1eb67be7582a3c494a5a26369a6fd1ef05fa0b4b0208567e0eab5203551bf26b4da7daaf62e64f41bed47ead3f59441698f23d23cb67f0deb3c2ee0498fc242adb4dfd3509f0cc4a31484c239c724ff416194a5372a1b0014a96443476c623f0acf8cf7a1d5d7cb8f4e39433d85000a4ea46e9125bc8fd8b01ef1dec581680c36c0e3e867503847626372e8f465febb14b92357b4f8a0e2c71ec372305464dc80318c7e91422e95923182c9aaf70a85db149dfa605184de1d14a1cd5ae171eabd8f3204c5c29e5645701b5683e5615186aabcfa838894c4688898cd2207a88888349ba2e5315fb679ec933461c60fede983cc09a877ede71ad5f54353257b9a1bece7b568be2b2d9adddc2ab2bd1645912c7b6d15e7acaa3d0e762f21abb72ad02c2f02c9141ec32fde3f0ab813e68ebc1424254860c3226d0b88e8c959b8d73202da45f5903981fa758a6e51f70aee60314ec5453ffa682042ee229c88196a3bce40f90fa43ef301c3d9334b319e84a95122d109647b273c6bd3e235e305c400e497b8cc7635a779a2109300e478d3aeef1b45aac2e8901458494728276c1de93b440fe65bfa7cdb3a28a0e2a6b8d7481b5ab3a1745a8db60cdb224395be565738d500660e74a3872f7f0f9b1dd3092339d581cd92118dc0a55b9ac117cedc0d2006c0c44aaacb7e684587df5797192b3598a0b36cc0142469a4f0f70564e3c75155fb885fcddb09202283bca17e72a019f9f2b1b1ac4a2ceab20901b3764afeeef4f1ac6c8ce0af7c97c01548a64ccf3aa1bb0c903ce8ebb44d9a8dd30cef6c79782a82c98478c730828cdb3fe92ad701948899b925cf83b4c4cb381ca30e3819a93bb492241c1384915572fa07e24eef8d65d5a8d5314d415523d219c664c52cdc31f6720f1c48515916e0b2c9239ad3b3c27bec5a3b6fdbffe3c8dc0da90705860854a3e2b59dc5ad3621217054e5d236c726a785e73cd9179d63fd0c47327772164f0cf80f60363c816898f23f281b279059e663aa152a16ab51a5a429a621205b0a7f90aa3c4e2dff1b054fcd93af4b8e249a609de1c5176a785b12d101370615af57a58d742b1d49efc6b09e1ee535252db619cc049ed2adbd9211f379bb06d52cfdc40f4764d139f53c5b65d6436d13d73b481569838e3bd1b44bcb8002864fbe7e1c11db33347afc142d3373bb741d31b6be36e82767afe8585459e618f49a31be56d119939b349020f47c747fc23821e651dfce5e7c9d2b0fba382f9b273a6474249d316b835cea4bd8084f6c2890f2346693326cd5aeffb02485c964d561b07df870c731f18a7f8cc53de964f334b2ebf2d8558a0114d0932c1b06d02f216fd2998dd154c2ac4654e26c0ac7116906ce3b5c5e53aeb3a56a465e500fa852b5ad7065084903fefcff76dec3b670e6df6c8fd273f8c3b8b33606fe52ab7ce0928f7b4d20297d2cde01a6463baff446ec22899386293809d16423231a78ab20dc5c34fd39eacea7c80f3bd66fcfc4f951914966fe3e787fcb2470d05a9b0d50dff8bb98c238f48efb417c47fdd33694429a92b721d2f9466975d14927c1f57f74431dd6ecb2de10db5c2db6756d731ecb4d53a710e095ad630f1106dc2aa109f6aa146bb4286ff1dd3df10aaae9103191a8616f39fc25e8bca8dc3a6060eeef8775c86342fc496e18be385e93a0dbabd9d6eea04f8704c682f204629c8eaf84bba02c6f845bd789c2ceb307c4cef93f18b52c053ccfe0f91a580d1862a9fd07ff5691a63220e8da4d8606bc6717ca9e9db46ec3e067fa342f5d6bea8d1fa70ee09af5e344ae82e743af0feb55060fb58c14175c2fb92ac3e5672d354c270da1a1c179c816bd1b9f1369308f5550419e1086f8e85251081a530153117cf094364e74d26321a9a35505c060b7ccadef3be13a10680c78945b97db88626b5039df99aeea0b19e9e73fb8970e2cf7dbf3d6516f18fe2e783c20f5390b9ae00683926c639c41be4d3a0ae23cb2bf026433639a400413ffbd128c2d2ae4881bbb83679dd935f09f95d2ca387f8369812dd100c839ea189fa6b0acc711cf57029ad7914cba159fa8d426d646cd86d6aebd8f63a4f9fbffb1291f73b55c3dbfeddcde8e669349d9fa884cd11cea5ba24122ea0efebf119fcd8e60fa5104cc03ba2204a73e3dfa0cdc1e75ae158f5a1a1f1222ae3f8d753b3c82c8902d7a31eea7d665cd300582237895836231d39c50681366aed62e1bf656662d715cf5ede9a9912ac1c70d6e048dc41bbc647736028d784e303c62d008ec8870ac588b865843a2ec83b430a2e13e16bedad4c8d7cfeef7f6111366d6c3b086223b782c8347adc1f5851a24b2346ba1e6e98cf8918d437f196d780d02fa26b4b9ae7ccaae7d607cce4733427f89b7a0adc271957924690293090595b9c0011b8270bfa65e14f6c386cb58f04f35f81c2098151dac6c384c65dab68e4a444306559e99d72756b236d7903378c8bd92b7e24046186ec47a336dd5abfe99207daac89827782530be56522034405a06ae06234be6be05bb4ffa7cc921b916d39f06703579af9e78b0a07a5e112572e075694abeb3fd330fa810ded3514f6f1f9f0d59132a0bd03b3750d14172434b3905e67415eacd2ffa6cff6db74a381f737066738a3884c8aaf0100a6d38272e7367c8335723d23a61bd9414ab2699fdfd495b72d90bfdabe8661971f1b1c2bf1633f5170a195ed97a8066e2505405d50e2385245a1aa2cd34ebe9516da4bf134dcaaa41e17886584e8768fc7e3125ed62324602ef989eec3454811c886802d2eb1948d406c6fb47235b9bf03418a48ad073d368302b87a2bb6929a795d9aaf400751f9d190d86acc35ab72e68bed12087d4f1d472f995299139061400924bfdb21142c059f76070764a7fe312c13d98dda8a32e5bf4148bdd4d9d2bbcfd731a6764b17b881ee87be5b7f6275eeaa1cdef5d01557416f6585b87522958b5c1c063b009cba5f0608382d2276262d735785d0d1831dc6f290311e30a6ccd8b921fd8a7001f4606610077030dd8c5f6aa23f89aad5c055e47df0077edca3847fbf5c6e79fc48956431481098dea626348e703da8c4cbb45b9cc41b6057ad3522b1ea43f74a7bf3ff47b9fb0a752b4690b046b7889728487ca631c930c4296e90412163d8540d92e3078db65f59195045d36527a553f3d7aa7b3e70945c0e5bada8036f50eb490ff6782cb3df6ca0b3410443a50bcc3a4e733daab956102fa0d7fdd2053ed44c16f0fcfd84228ea6bd5bd6b5765e00659f5f75211582779ad3498649a7af18b0a49a83386af756a764948451c0ddaee01a3af87535985290d90540f35fd6a4c261b437c48588ce0a12691fef668b4cd186a61ad4310bec962542446647e622bce9895a98c93388e51514fbd8d77dce51892a181e08172507703c356f075c70fe8cef8e0cd768a53acef6c5ceb0d7700cedb52be9ede9e6407ee0484a2dddbb7ff7955c763363f0cb63682f68e039d926d93d61cc4c3a142bb032445e3bed8045846ac4313c476663c90a92d2d7304732b8054c9d74d93f9b4fd31ea18a5b0f3c7ede5f999fd41d863b3beded17ac151d28e9b42e48acbc3f7fc36e2e595bce762bc69481bfa3c63240fc1ac8ae2a79e2bc0e6c1a6a8efe9a97c3986aff71f91bdc824a8dfec9e4acbacb00cc40136bd7f88a484f845d2d0c8173636a697b1011a95827dd57a10b0d4cdef2b85cbe80a3540ebccaf2b133c2660fbab6256b4d5d936e84bbce24e9808246866ba906394eb84151a8a183257747a9cb084a36a557c4e88fb8c70adff7dd24190f50f1235625d8b456ba8bd131a561bca53f22d22b494816ad59a26db4b7cd1228b6381317b3c7c596766887eb1c3c2c6e5b943dd1d6251916275b3a935514de466a2bb65d33b8f9994e025b58aebba8984be1b00a0fa9e2974ab241edb3e935c35c3adda2094577248f2d77ae330c50dd58301a3008b0fee658e4583ab48320cfc70e0061476abd18c8540fb448bd3642165b8980c0308101c4cc642f6f5e5f946cf214c32bd421901a5bb8482a12588ccf1b6e3d58022b666b355fec1192d51943e6bd843431f3f491665e54fd72acafcfbcb21bc721eb15a0ecfc792e0016990b98ec4ea114390b6d656131f15face5c24490a60ba2bf8be2e3c8af07c0e36693cc2f97a6f3982d3a12206c00c717470189cc3e0a9e4a011e52361f0ce3f03aed4a3398163e706bd645963eea34b6c91987b58b1d6a06ace56657c4ee5787b69d1afdf7a7aad610fcfa20462f96e30756c86f76473d42b458853b35fc85e3d34962df299d6518fe7f5cea110bc97e5835794594904253f4eab1924699937c86a210d8b7f0746aa378bd1ef096af5c7061beada14da9b1ebc9df7f3a11dffbae9724db84276ae3c1d1e6db258bed793667573e653bebb56b8a1ddd696adf40dcbc410c9890da4768fee44b6f2e16745a3f5f5e538959c3825f948135c26f488d6b54fd57ba29610b6d7ab2f0297a8e5118da34037ab7e6fe8472e5568428a4d02dba5ad4ec467602e39bdd3dd30d693b51b0da02584db6023870ecb3cc185f6dfa83b59c0182681ea799b27819e042a02ce32ca7637b61819e6bf7413bdea73b00afab62d3c1a02805b5a30a203bdb982544ffc2268befd24b2b1b8f824d3d596c5089111f474975b92497df396feea8836c9a819aafd68a0ef43a12086fba3f7e868e54d8d5552f29111d716bb21c233cf2e1a484ac5213ed57b3000ff7ac5a69a0d4a8f99d46e7cf11ba345d08d89867595c0da29ebfb0118f6f019945d7ac9d8df165442878ef9de099f0dea52f4cfc0e46fdda88b51d643b529700b9fe9960e6244742e11fe8dc7b1f0fad97273efde342c4c370672070e5c945673985dc98d62215ed378c481c94753fa81f23390ed15bc2c3d573071884ea329a6d18f76596826344e8158adcb23e27c27294c8a44e945614721a554325ca4cdecf32e272b0e881daacd94617c696144bf959826bfd57ae432e07ff7f701b81815f165ebcfd624b824346e8c5110d8d3ba2d2bff44309ce6e4630dd0490aa8c40460a5d19b40a1189e0ac50518b330fb72f8eb9fddfe04278fb674f7e25c3a1f5053f8d810c3e2696378e69ac62a4e89ebf71bec9cd092d5cf9a549e78d99b4b9055d0375e4d648493ec7b851d4d2f9ecd5c9a5c56ea6f9db47af58921541f2f05eb6c9b6a881368aafc60b8153334329bf2cbb4ff4ef5e3dec2f7d7ed192ee740f65e2567b118fbf24b1cb49fa7c5d490fc8335e7124595cbbc43a15b49842de84151c8c802d62963021573b44c1671ee8691dc22acb7ced1ad7599aef55360623bfa23682fd75cfa69124fa8c37b832a8bbd8e8c69d64c0d89dca1371947d0b9d637043c4d2a6ad36181a13321447d995e6007a224228e6d7da6eb9e2070739df1b0180a8c3399415c8afa06ee0098db8fb0c51d6f119a666c6a608d7b951f7f345f7c5400f71013500a0d0f1f88e7768e120085a04749b52817666fc3108f3ca01f139dd60ccf00ebb6271d67e5de2c8c8f7cb0dc9e0ab6fb0be913984034df4948553543ee325d5d39ff1298131512c5331fbcc0f50bb8d608f7d37acf27da3ff7f9ead18bd0320a9958a4001903f1630bcb1b753f315a25c3bfb9c5e269557ab4b6be74fa4b8e3c825db0b8e5af71463ea35e97d911fce31b7b27251afe6c44cb7a07e25aab4eaa298c79a021b542fadcf96499f981c98a60e81a4668cdef4684a8021324c6968e4fbfc67b6e454e0ff92f2d661f3ae0468037139696d2c3260214094cc4790a6611b58e5cfdf8f4fbf487033e1cfedf760567fe32d9f2d3d00722fd17971428c86477b389b4f3b4cc0faabfee32600419d965462241fbd5b4ec8bfa87fe678db8a450c92a7492e3eac5a78d9d851dae3e7186ec310d9315c7816b76f0fa55237aa5f983891e2051dc4464324d0c4ffbb38f03aff92b4095027c5dd9c7213fc44295c4a5f2c7b88bfd93a052c51e0bfb9afd1109099af560c6465c1e1f97eb5ce7607d916851e68f5833645b42bdfdced0f8feaf8a600068db51360910804f09cf4b6dbdfe8d198a336b0684e3a3903d22bac6affc04f3c8ad44b724e4ce63436e06a74e3fbbb562be033d25945f4bdccc417fb8a370c73a607c57fa44b7be637bbc111c8686753deb43d09f132b1dbadc9e7fb994c089709422f5ec0967c9631fd7d1fb660a554662bf4f65b107e91b862284ca34d3524daa9a752f8a24f8c3bdfb07c9902053c417f61b40cff4569d3bebcdd6000cd9c2f257d89f5873afc4a62383431fa98e3932bd164fc8f09aeae1219a49a960f5e7e028e6c6333d58351f46a34d8569996501b42d2a09a4512defef8996abf5199b625fe43254353a09b4bd8df0e0477b1e64daf53849b807fc41ece5dffee6f521ed601db64f52c1d2ec28bc4c09965ee4a80bf03fd45c4c5dc82f7653e2bd78744b7b7804aff023704a7338a5ac58ceb7d63bcb10dd0f6b421cc195b0e09001537a4b8921b40ee84c931197c5ae11974431bf8917d02ba743822226ce49f216898819c7da86dfc722f6eb9c6b9cdc2ad78785a0a1e83deda7467aac284b0bd02e9d8b3cb4cdb81b0583ff383e40e1b341351b1b1372c973b7be49b2a754f867536e4421c23a574064a32730643201821848c642f1dd28b870dbec0e706df6e692fb52feb559376e777d301e3a90f0f690e17ba79866f8ec3cfab86112bf63cb12051ee5981a4f8489544be0e7d413b5d2d89e2f751d2d271f3d9cd9b3dd3cbb8accc3beb9633b6dd39348a47f893795b9b4a87d10ccdab75f527d7c5cf6f93828b4f58ccaea34dda1362d549abaa6125969de3e50002c887aa0aae129ee8161182802d560fd190bf121203e5526fa35e2b300d864303233588c3083a0967bbaa611427df830e2eb3644635ee11a37ed24f26e81a88e9a31f021b64d57fa9d32023af223cd130a61d4c7f4518426c826697b75deb911297c8cd17827d491d4e465800f094365726674f198c8310774d8cb31a8e5fcfc66955760059b994166212691688a5d9c563b71e278621ebc145f99dc8543f2e2de0d07f8914490e157b877e538911707feee0dae1ff91d310e1691a079a2f26299f2ab0cfdb9ce34b0013600f6870d4d1c9b23811001f0166a27cb1c5d1883aa314861a78045ae05c7780377c47b359801c176b357224ad9e8a69abd94a362746a59a1e03a8ebfeb09db0f70128a6cf3f7956435d37b6b70222b7103f3758c48f59d5deefbb85f3993d0505b60be22fa816f7f3ea8e08361b5b72f10e47c1d73b682b9990a183b02cb0544628dec51d7f6adec81f2f3c0f59fd770028cbfa699ad7cb6f05a2792f850553396a9562a87c035e3d447c728a29fff2f467d9fc2960e389270f15d08d334610015e67c4ecece854ddbb03323220530a0302abfec30ea7b20d23b11575ad7ead54df87799a83e7223173ab60c2bd017e48168e2c732d479a18bd7a41fab8930e54826321f78b33913ef3549b1ff22d4192307a1a0e88415d815d13b71ccc500d7cecc8b0149db39a2539b4fabd7178380e3471b830b9cbad3118d911ba03d540025c4443031404cb3339068a2e1603b930599dad1041fb24e5a8b6644bca39c92020fea715045ede099fc5ff34f1315cb54c730a54f9a3875afe469e4e46e07880b4e7e832041036e075656081d950d1ce4e6dd81d22e1b4a664fffe1625a7949b1d7f535331be51dce44c5e94cdb530bb189e51013e17caf5382dc4377254f69a94ea5f9a335e77255243096bd6519ace89263cd680f0d3f81fddf44b1f47782b94eba96ba335faef18134ce08d3056d453d1ea03f42a635d385258d225e4c3a0a78291130eddb0dc1bb8ae2c2d9bd50c47f5dd1b218a626651dae9eabe42658a061153d2b7fc3350b72bfa740b3a81faf6bf62b695d0a06862fa36853809d8090baa4a2832c7a6cbf569ac7bb63587cec07e1b6520a0305a7ae668dbe5565e786b1a1c33e3dd1a5624d798058d66b06ee8fd6db8a95d09d37895163b1623cde2b68d7236ea0bd24c65848e39df74b27c54cdbd90f372d26d79b7b31a969bc6661989c37f1236293a5e3c6728694217e20db1dbf1864827cb13fccd6a15a2cd48354fe8005be9cf97c3dd6b6290bfb2061a356758b806958511f74c58155eea3a4338104a1e5e685c6a2a88ce1e7398b6e8f1af80f32d18d1a09eee8d8f55424cb0b611926bf9e9b174a8ecbcc5c34370e52d11b0e89f3eb09b89aecc7a486f70340ce342db047bae151dd56fe111e15f95ad14251f97ce7d7e5e9ea433cc31e0a003aab19748b8549254ddbc10bb6dea49867ec706eaaf00fd577932fd5bae29ddafdd756d92f7a8d2858641f567bfeb374c86c114ea41eda0b9cc89ebd46053acf577ce912af981f121625f505652e2f2ef9959d1a81f59da7abc08a839bfb8fd959f3d1b48c792f167fe57b4f32e5703974d9860e6f058982fa7f43ffdf072354f433b3e5d7091ee56e9fbfa78fd68eb4faf8ca544dc5e007cb4c9220ba71db2b743b69fa75a067b2ba1e61a0a5a000ef699248e16d49412fc0a7c56236688cb07ab79465f2de3110e6b2cce8a617678d1b5fc65edb08eaba03478138fb006384e7a414bebf6f3541ca012da3c3cafc6841382cf3e24a5413cad21cc2262864f68924760bce6b0fdac55bec867d378620647bb1580264a3c9ac1fa43a73ce0c55268a3bc51749877234f0b8d1c84e16aa11f427cac321ab64da8be7a46924d8ab82f0815e73aa6080a981a3e307d2a42a99681b25e28376ef01ca109c27d76fa06a0aaa7abf7ca29e82da2f4631c164872b294bd7a5a1378df40e2038d6a59b5cfa4825659482959fd64a90507a9aca996f8ea2b1b3aecdfcc7c8cf34dfd2fbdb2f80b6925807aac6f19a61268f3f8f9866bfa17c13d1f6062e31627730c9a60be93498bc6dd897584142a47d601e2799f373cb5c99281b248525d41e0bca6bb2edd92c31a74cfd12a223d328a0b3c63677c8dcfec880fa6338ab3b629563acbe7b365ace71394f44f7e553749bcef8953a21ae74684e555a0e01a90e3049454f3956a0cfb3859faf0b2a088036f0edb4f4116aeea0d14279319ee429d62278608076d178717fd334468d0bb4304b4a2d58f1e29650146a0ac2135a7b403488bf606c783dc63ee184bb0c22a32594fe908d68bc36aa4a20a6740b6c8b3a1003a3cf70b5a2ceff603b1d50ed2567c47bbd51d7bf589746d52548ea8fcb0c4e7ce02925a0d90a72e23dc921fd8cf76fe797db519c841790ce6f5b250ed7a468e82491e1c7730596aa98db4fbce61e117387ba427494357d9bf6340b120deeda6fd1637aec046e96233d6384f6234c26ddcc5be6ad9acbd7e8530bbfd98a1fc209abac28556ab0066b8bdc144d96f63d562d94999a8c4db1dba442e117e3a8da4977c529d2b4e8ed1cf668d7d9841cdfd9e770102bd3fbfacd70aed70bcc5d66a2af97bf933aab4d5b25150a3cb23d9872bec84fa15f6927b327d8b99c5994329260f8be080d34f1c08a31b7f57c0212a348d5456e9379d0646ba9fddc40ed217be3ef95c1be8cb201602c501658757cd1ac3774b9ec83e9b2f53793952e67d88198696f2be13e0481447b0e549868aa9d626e79dbf10eb7bd588ca6d83b8c84a1c7a826524cf875f6361d69e2ffa284b4fe5a59f746e041bc2a507dd35d109dfea09284c998ec9b8db4798d3f67497eff6547683e77636506baa80a64c38a7dcd1e04c6c85a59fbf2eef8867b68d56a63b3b97a05513b7856b479f8f9cc19b9df82b450df9a7ead1120d0489b8c4efec776873df53cee14fd346211c7faba70b1d916753dd539a39f28564c0f81db3df005e7d7f7aa75bdde2c5ef950320821decb7e559af1f3d493d455427b7a2663ea231db8496d882e4514e834f147bd0abf90f88c455326546b63ae53f46a70528231762afe0982437a03d55cfa7d7942b92bb065ca9596786dad59667e41c648bd11845db607ed34446af52d5b904c7648e3651efa44632643bed3cdabac261751086441bc1056d9beb7049265142fa67f1bfcd7f53a9a96402261de918abff4ecab4e1f892aaca60abff64602b9a36a5c3db1bbd56f7d19dacf413b7ba6d3a34f14b8794a2000f076a9d14e76b07dc4e935cb476b39ab1971b9551708d6e105594975075622a7f9e8419cad9e949d198529733409554f9a417a57dbc997f687c02875812927a63ac7ba20cb01ab62d5ff7a07a63f1865f351b1a96c04b879fba0e0e5b8dd0eb416720e22522c87dc24c5a81acc0f049bef6fc9042fb0879cfb18990dce6021f5f0c388c9f47e9ca2bc620f8733d79d9f22e78b3e10d8999b7dea091271c2173ba4127ee2cfe8a3d822201cded60fe80348d1da39bc3ace6e7a06fe3f4d59bf74305680fcd2a7ac996d0ad8db0c9f80c7daba3a30fd23f365b89630cae3859f9ebf456c5e6eec80d8a15dfdfafc2397f845452458542b29fc398531596022131c453a1a9d01e3ae648137221bcd58827488cde7c2aac286b9a6da4e1a0b76875218fbb1cb0ab7dd1ca0a39eccee6482da315e09d9ef62ba06f8a7211600e08c1e8f2b5e58e5ae05497aa3f94ef1cf2aa04236e3da08387f87b392fad20ce264e849ca9131ec36bf1bbc267fe257e8d221f9e0abc3664a44302c1cc1e8aaedd3d944f3c4cf71475b0556b1e2e2db163f6775820e67bb2ddae8a5e022aebee51aa881864a2d7e79a0bd993e6c391f8e6cbacc3d7754422e30f9c794f96cb5fc738578f64d52f5e5cadc0ea82babca310ceb2650450b0ecfafd68f44624568d5a832ff0b20e09b267a40743647510713869119a569bf42f48aba5befe6e7fa5d1d8ee8ede73d767be88945c1cf3d96853c8eb63c4885df9fba83dc2e66301ff28a340d81ec72dde02974ff5b7d2ee8a7d581c44ea1c1ce3ed1bdfaa3ffc876341e03da44b67a5f30103ae21c8973935fc0aab331d93c4574915b608d76da0168727b24bfb5d039f16254fad44fe99cfc8bad2bfcaaa0d407fb9eb9a2a04203c121d5678dd8e266041818155cfd33d52efae1fd632fffa0a5ed6ba15fd04f4b4b9b4829a594014a077c07e10732bb83be64f45d23b7f66bbf8cfd8f9e7270c0f880f0c13fc1f8e2eff0c1df4f8ee0870f7e7862f1b4f706169129a810ae1dc778e0dfd37b0bb2a74d14dcfe35bf8e42969569a83f4dbd5d6559bdf9ce94d5d390d57178cfd4243eaba724a829095f8fb91da1e4de7ba4db27b8285df720fc1a08c7ec93238a3abe444627d8b0c62b8f4eb0b9d5d38d2290579509631ecface6168e80cd11d835c837c3dccab2f0471b3bf735d0de4058061394b34f66c0a3e35413e998a40e71839e6adc10403dd54eb4aa753dd553f0b5d65a7bc3d38e4c08d3bc768cf58f6abc52b9b51fabd9966fe9ed75fc62f821aee35cc7e3c9ad2bab59a683807429feaa46753b5e0f28b730005c98d9242bf061891326924882c80e7e6ddb359ededbf5c1a875bde0e2dc6cb4f07bd36bb7353c8b0f3a6b627fd65a6bad15f7cfc036cbbe9df146e2c70f204076b2be3b353e176b80b3c0730bff6d81b4acf4cb5a0be1b85bbcf3b995f100523330f0d4f6be1a8ea556ad9165f8f55bb586036cce7d8cc7d1aabdd4aa25b02e4d02ebd229abeda7669d0ce726f80d6dac5c1c029510dcd3cf248d9a05320aa6cd2d6d7bb75f5bb72efcf8af12291a5823fe4d2bbbb6755f144b258cb30d64be9dbcdfacdb47472c64c73e6a736e7ad3edb76cac65d1cc4753862ceb930214d0a2023601a3149001d7275000a3f9532e46f0578c9dfc35b2cce24eb06a955ccf2fe355828fda9cab439a75fbd8e68cf84d3b962090c38651bb79171f7f69635f6cbff8a18de917dfdad80c6eaf4e38eb56e7577d7ffb77da3e8294195906c306179ef011850a86f0ece42f21cb2c153da22696cc5a40450f3b12e80187157a047124070e36781c31050f37808245490d0eb04f73c6d397eb09aef1ac083205fb21ceb5fad32f621cfe0786df8ba999c726aba3d665b303f1e627747a0c19b83fac09d0ef0b1e3ce53c05e2183164903083a71e846e9f0c51c4c80f3d45552058bbd8113c4de527a1e38fa159b6b553ac9ea6b449ebf1c4a7b5fbb476ef1dd3cfeaf9593b8d03b45b1bb26e64c0f1a7ac7ed39bf0d49432859f5f5846986526dcca1f5a1c9ac29e9ace6c5712df7bef88c51c66fda1d5187fdf067598453c5e1df690a8d6a166cd6054e79cb32682b4ba6e538b1be1bebd1fde4fdd7fe5a2c87b7f5576466a466c34291fae4240eefb330bff4d89d15796d91082480c423920424baaec1440870f328841880a831876f27f7e34ff49099e7ecfff58768ba8a136b67bfe05f8f4fc40327a84d8dcb7ef06c2b174464866e587c183a38fd1fc2f2f7cdf39a8d9ac7fabf2dbe8f903af39a3076eff9ea96ec71dd477bf272fcb6b869d1c91ca6b9262bf27016cc06da469d61b63cc736befbdf7c390f65fb3ec64572120bd5ddc18b557773297d77e1e1900bd1fe99e58b4c14b79cdeff6ecf7fc6e61ca6bce18c2ed93d664d226fca58ec78e826b43e33205244ccacd7671ac1501bbaccdf9465b9b1dc297a5d602b3d4d030cb8b51d4720384115c7041b27a4d4d9019d0e620a7d6e1fbb64eb5d63a6f334683173f38ee311a94ecc394680193b28279a131c1cca8ac8da9d141a6a7d65a6b513a954259cb42e6a47aab32594bb385722b4b0803a3d76255fa1636fed7ba347739cd52e79cf3b6914e1a8cdea8a6fcbe128616d285b5e20b6badb53a07d9597f9a73fefbd9d810523ce79cb78db5d66aad61526c8cc89614c1857144b82f1cfbca594b5a783efdaec5649386d941b77ff95c6ca3ad97405ac8adb5d696a4081f88dfdfb7dcd27bdc34abf588cc2204a336a6f306c2684996b550ebf04cc31cb46da3118d4cda910493c26f1882b56e5f873cb460e90b5be86f98520cadcb7ea86d18829f5b5926d270eaf21b5e7bcf348753b4160ad2fbd6edfd187454a9e733c5807d1196c0a538ca5129509e6528ca512950d4ba6ed023d032bc85fa535cf38cfef0fb298a61f8af46ad0ba33728daafec062d423f128af1d7637571d6fb03437124cb11c985975e604c3127544a86856a8666d5e201b0a2f96f844dc3b28f4643639a270c9a9999f3ac79cda8543535231a9b25046714c3510c37a862c18265bfbf2c462199f0ba00c1954a8150da047110460778711396a27891b339d9e67c5956dae498c8ac1fed7f502874cf6821f47b7e348cea8f8642b5cad68806f821e4efef6e0901df86af9a647461eac9330dbfd0fc6ab01c7c347d636242b0413b79cbb26b522f4f8807754c0275ac9ad42cfc9a5499994935a9fae6fb5dd377beb871816063625c63cd8538d4ed8bb8551f85ac0b061463328980c3fa000a6b0948d0c379d55cc0023b0e6c80f78251a6b28cbf1db99439f9ecf6b999e2e8bf8c17106c3aa0863a8113608c508a9f79ed480abf78040094ff61c8535fcd6689d417a3dc349bf37d8b6e52b550697eb4d048c7ffa546a18f96226394314634c0ef83955ab539379b138463fbef8f4446a155bf67d701d7eabda1a1747b55939a6532aa49d56599ddc1e12933e5336576231afb94d121e1e987a3063976fdc38163e9b742ff56408d0c40d3ad58d19e90d19d6474323a9b6361341feda3d9f40fc26fac24e56c0e89227db48f465bed46c0810901b6eed2ed93211cba88b58e2433d62e39df28c1efa357f0fb1bcbc6a6f2e621373588228a284643bc4c9c87ef2dc3b4e29573ce5878f487c42946090e6a759cb25919f61172f3c13e9687e1a752362041032860288e245814a2607fe6dd2147512884167aa549118c1ff0f0f0f0f0e00c8a63f8edd147f4097dbea2bd41739f56098f0f1ed3e5c1390a1f1f9f549326fb037d3e1f7d6586297c7c7c709438f84d110404431004c10f0bcf14599a4c11a4087c1a53cce0e3e35314040f0f0f0f0fcffeca253c9fcff6c12fe8fd65bdc1265f91a813a3682c3c4c680de6ffbe6f6b2c3c7868c44b2ea492248bca717c10144752db28f9701f17d24d9491cfb63da9e2a6cb94f4dffcfa6cee0c8e70d2c4081c4030c9edbd7711fbfbf60782dfe62187bd73a6b277ce5496eced43849224488670430811c411208a6490338f11a1223ff8b077ce54f6ce994ace79ef9ca9c0d0e014c3e463d91cdb77437ae0e1034371fcd66b5d19efcfa4e9fb0ccdbba34d2deee41e4268a150ccb0423143aa64b2b2519d513bb642681853804579c44a18d52caba1aabd06139efe33c184bad53d43f16305d8588d45f15bf4d6c8566d759d4aa0e76bc5ae4720f6fca16de9afc935b28cdbc7af5f9b39e39c73ced78ea4cfe7b3fa03bf1d6a9d4d62288661a8b7fe6f84007dce9cbae7dbfbdbfb64f5f4a0ba85516b508fa4b833f963fc21fe10d7dc01fddf5e808d4d424f6602efefefe79d73d618875aebdf5ae33c4280ce796433be367fee99861f3f7ea49ef41c29a208bdbf9def7fd81d285420c58f1f3fae2871297c40f0e31186df073ee991e20a13297cc4ef9322abe0c78f1fab504f4f4f4f4fcf2e439b43cb81f7dd6779eacff99503bdf5c744d489519ef42ca1f5b75713653de93952ba70d2881cc51d298ae05f91a65f1b1cfce1f28334ca953ff68e1d647e8bab5b2b53badf5bd37672bf3e1b1d721082430c647e852040a80cc8027f4019191999b7b23004653a46c5100422f33100593b90b5830108fe00e2f3e3053d379e1b6cf021f33735c8fc7ae3829e9b1e37b69b16dcd070c30290b50359bb9799b1321064edf60c26a896f56f30049c9da622be0c51e9f80547aa916a349d5403ef7749b51d528d5483a9651684d04261d42cbee0e21a6a975bb86a9cc6699cb86a27b48a63d7f1cd5cb42d9cb3a6dcaee5d09677d098cb85e3143b05c7d2d10636b039dd2b21e7544ee5544ee5d4d84b1861c677fa0689f38886f8fb1c41f19a9ccb2021460aa3f8598f8bc81736f316b5cee6807648893e5ad1a22816dcadadb88ddbb88ddb4a766ee36e3270390789532653a865294ce39e267d75cfdd645d2693cdb1575259e71a20ef42ea14d34df9f139937a243c254147c7e6587b71e0425996752ecbf62ed2d396ddd9260fcdbfa3546a6777763b9b63f76ee7c48ee32ebe57ec5bce0b4fd57aba8382c621b266c7215267aba5c4b5cb17861ba6dbadb3735c1c5ce3b16d181d7d461f1cfb5eb679e0d6ae7dff9ddafa086d3c725c9dad63870c35175c78ad95b9008d41c01f7bdcc7c76dd350430fabb379b80065d9b6d95c803ade36972340b800213219836cdbd8137336bb7df66dfbd83caeceeeb16bd8346c9b0b4ef78099e9ab8bf4f492542de4a3dbdf3c6c42ece8f6b70eab231eb938b836c282533b1ead381c0e273a11ca29397275c4a124d6853f1585e8f8ad888311050e6676c5cdd94026399ba3419bca2dfe566f9c83f82568533796de6f65314647f6d5379f3162d81c8b592949bd593d84459da8e2167e511595a462929e847403bf25cd54d5e6d82e46119574fc4ac41c968951445554b525a9f9c97e5d6a9c8632d45f7debba36c7b2308a9af2b3bee86f58077cb7d65ff999fba55ff83502ca32326f215c968d6f7fdfb46a73c0a7d2f1a730aeb80bda4136c7ee20eba2e941a35b9a296eeea07d04a704a3f84773e74606c0e9d966097e5f0cda2ccc5e373bf8cd22cc40a3c1b022bfec7fead157c72f9b9c7bb302b0bcd1c1efdfd4f8fd9b19f8fd1b1948175254abc6f48d1b810516a98654dec450dee4e0ff82dfc050b225b1859581e0b971b985fffbb4454f531de745188620e727aed5770ea330232f30ab1968b415108c89c61473a30254c8730b48aabc7981df47ddd0f8fd1b17666e5ae0f76f58e0f76f70f0fb372bf0fb3729e0f76f6ef0fb3733eea2fcb9dd469acd152e5e20b830b6c0add605e309106c7e805a1b2bf87d9b2dfcbe4d15fcbe8d0f5ca50d155c041c8cf3ea2d52aa719765dfb971351bb7eb1807cb01bfff7abd48a9d44ce3172a5421a26e55a128ae6e55219fce4daa111f29d5088f8e5d6243ba0176063afa7d128fca11a4f0ef8fe2e7f8534fe8171cbf37e4a91b781a83e7d6bd5fc2fdd77d1959960029aab06108072d4dfcb07331c6a619980a328572d6f35b56ced97ebb7beffd18b5eff7badc6bf1bd17638cf75ffd771481cd2d16ea37b5bb7eb975d930e34c84d52967ddfe0bcbc89b8c02d4a08013b5e79952d7f3ee6a7c83638fcd7d4f6e5d65ca537946fe12f2bf6460dc6ca691206f11b914a849cc8620bb55ab10e956ade2d3ed7fddaa556ae8bc5bb50a0cb85bd6791b9a3d8ea52995ed1aa35bfcd9bf81fd354ef854df5703cb4cdc0919c031bbfb7e13dcfa4ee1e87b7f78c3404df14f59f56739e6cfcac491030abce28a2b7672484105dec9cfac12af74a5b4beec605bf7ef0bd08bd00b1016bd7f656eeac6dcd11defbd2fb87eef29cac5b97fc57bea5c98a428580461df243077c549e7e1188c894531acd62bad5906d3f1b2038cf6029465b01b2f406c7911c2b11854e0707a5a5a4b6b4c691d95d6d2daa3b4a65e705db7233a45b93a2f1d58e9f8c553948eff9a3e5d4f61d0741845e20565f6eaad45bcdd14fc8a8251d4f952096043089a3da0d549b5eb1835aab1c2ea51a303e8fe7d36477d347f584f9a3f2e5e74814f4417e7ce5cb052508177fd200c493d992555b5537b0aa3f6a26271f0d79830683066b9d5d382d1d57cf15e44af9d75bd74af28553afe13d1d5794141e244f49d2fd5069e92d4924db5abf5342c4bb05c6e615da48b827e6e3a5ee0a916a66972d27c6ec26c268c665d26ac87ad75d80b1676c3e2e8b99f21231df340fb9ebe54ddca7cb5e01559d7cb098bf382620998970a33a2f163b26031a2f16366739be69774d1d511a75c9c15c7c45d161b13ab5834e6532cf272ab88958d3f76bce5d61884d19dc4c6c62138e8cbebde2824eaae0ec9c9d575fc3434c873c5c328143c7ddd7065c250264c090c7732614253ae0e6cc8e2885560477638063362510c4bd2f1c7885362ac660c919765b01b3fbd8e3f0616db8ad18445f16358104e10b4a77f8f948e8edfe54c39ec858eff8557eb69d8d397da5f2a46a338b93aaf1abca49c4e3ae6276c665d78c53379d64532cd1569ba33a54d104fd17093a6e7d37ca9e66b8af97af3c5cb2dfca69101707a78be743ff095d962c66c7196ab4ef66c7e23f39a3cb72eab66049e2b9e0b5527416861ead96c347f564faef88b72359a3f2d5e6c71ae5634df6ea13ab95ad1d0d068bd5a318a57bc1974f6dd983ae912452857a0a971ca15d8c1f1748ad9df067fdfdfe01713631ad150ed543bbdf5a7dad91c4b237d1b06e6e5e5a5b4e261745534a2412280159ebe545ec7ffdae1981ae5a57b4dc1b1d403aa8681df7ed11985af9d21c5662a5c811e99f69ca7f779fe9bcf0edc33a56b82943a75730e311a05c77f9f8ce1c0f1eb57e5e0f853cca3dfc78901c77f7164acebb41d4ceb33705682bc6f4384cf7ad6856f0902396e9ce069981fe75a6ee1b7ad37dc03ca41b434f37a1ae65a96e15e38e3b454cef986f9dcc057cb5594a85270aa9420558a90dbc4498f1078dc3e294249ef451cf972aa1cdaf8e1082974bb2655849c000222c5500fa6a06d79819b306a87883c01a20b4243112a86ece0062ff841071eb0603b55298c5e1c235c9fb51e09884c606dcaed132644dd6ae9f6496eed79c40031a8b3304a66408505a8ef706c24e2e1d84d55f8732c2d4e860060dda62e30c69d7c6b8e9fc52cbe1ed1184f0fb0baa87df872e083319610d96ec4ad7fea9dc9de87cdc9fd7e0670cc3e3182d6ef7f4e368063a29028fd7e53fac5a4d4465f00466f0debba3f83847fb62f87b70fdce6dc7e3f4d8567f89d319ee476b4198ed19b403ad3d00419b319111c4bbf5cbf332cb347e0c83b21a0b061198ed1ad8620faddfd3ec9f9e8f72f108ef17eed9322b6f42b94b1b5442a8ea531744e9cdcffa2e0d806a2dfffa6e0988843bfff11e19818833ea35d6bea5f408cfbe53c60770bc0a80541124b84a82571838524ac134a8ed8b0a336450a6298b560831a6e5576b060633171c10f237c7801908e2148660054050eb0e06007315c71e586a11e52fc0879d9247ec8a1dea0c3f2821319a002a4a74991a22d30183de0054f6cd13193c10724d60a229d1245acdca0cb61019e65a4bfa3bf390032c4071d761002fa5962272489238c40421ae119f8d6b22c01525e9061880c497c9802045400192227831c9ae0653303d66583f0476f6decc10f0b84c0010c4cacd8a00187102baa0cdde003111b4108294143922d3bb4200614c0a8fd2ff7e55c39887c96c0b0c4c78e0bf0d4e08829a2d8e1086f079b256621e57b7d9e40ff3e817df4f1836f63e7fecd65997e13be3f4d90033e4dd8faf7a9c2fbbf9def6decf07cd7fb28dc27c10c1610d7ff00bba34f9b5b19851a61ca04b7dbfb5418f1f1c44f4f103d5877ca9ef17785bcdfce7bef9c73ce4b78ec0c74943b1af406f3cd39df7befd55be7d74e0000081878c839e7167aeef776e7bdf5a986d5dc1414a7e68af0932088608c2ffead738963a9a9ef3a4780f99d8f67761a6718744d4426a8ce61ac6c86bd314bf6947fe20cdc050a144bc025475cfb063c2452b2809341441b1ff95897c850c592158288f08116229c109b108103229aec60157153111dbcc461af37181091e39381881b4078198538c4491e84347091ca9315a8625032c4b7e3badb274a8028c2471e7e54800252e2630525319c4a663198461a2fb5f0f8c8cae7c3b91471058ec2559164c86622c94ddc8197dd3e49c2636fe164b74f92bc80c4969e5a1cc7fc09122c234fe114c14d34fec24438030ee324fce13243f60e90e030b680a4053db5b8100912157c40f8c88aef063f590179433400553eeaf6c910115afcb6ce1637a300252c6048063d45558e031e1e1e1e1e1efce9707c6e22432b9a3fe4d7dcbeef9cc1e8cff835b7940cdceaa1c36b5938765febbdb3288a6118eafddf08015b01afbec1effbb4fead8057d73b6a82b0ba0da2331e92a2e9e853f1d3f153b2bce51d91483f22dddb4bb12c5f2ccf7134f10786e183e13923a3ddeabc031b3f5964b821c9ae071625ec9614a440c8ae8917dcc0126489110467f03da7b0e24a0f3ad478286200961b20b10148a887243040711281b8ed291fc11f74008a180a33b08bad869e6eca619241c7cab6f2b1aad99cf2c791066773f6f76478c8d8646e594687d571e10182abdacab6f281d1bcb5de5affcaf6633cd3158f8e1faf7458143f3e539aa131080dceba7078a632403237195b96e1c1e3eac8e8b038672ab3c38506999acdc932b48e3f93cc7047a6461a8d4e199a0c6db422710c1124b72168f941922a2b604114183420050b152b3f331c78e0b26ee1321eff7e968e54967d8f651400cbb04e72b6d765c541a4d65f96398fa4fd11ebbff78eaf31fa367575f089c23d53b0aefd038081a716b7de1e36f6286459b8ffe2c031f2f75f5ab97f0336e7fefe19cf019b65a433f569f8e94dcf9c73977393cebb89e076bd3c2e6d64dedacd7171987796652888677ab37b5874ffdefb67e0d80ff8299b7d7863d6f593b70c43fe7addf58c59c1530c946a5b8a7620fddf1af21490cd21757d7bb9a59f877cbd6b6e696ddb423c05ad0a24be7e0b6419896619060aca2d9b133e8afea774ea754884a7d676712bd11141d8c3303c6dd6f0eae40f3f1dbbfd8bbb3af9d4b68b83c4e684a776811894652884a656734bff686699b6e5967efd658881469c97a914f98fa617279e3a975b5b787efcfab54d3f0a28fa9fc333fd7e6f2d8f331257d34f3ccd6ad482ab5c4c30b71ee9a5647b72fcf25bf81d16e28c4aa552a9742ad54a8b4abfa032b9d97898bd98ab75d1565a74ebba7e69fefc6f13d6b3ae588e182d36cbad2d18c545bcdd4a9a3f38542daaa082c5ff687e0baf1d16dfa209222a576ad6858dd8762c5aa8ced72e684513a42b49f3c76c61e268c1aaf9f3082e49996bb9d09ad57664ee92035b36f3a5030b002b1606150b6b71be76b523681214571e69b5722ee7924373209ad3b996cd9c71c9654daea6498dca9a814563d95835a450a7189164dd6a9ad4e46a54d60c2c1aab06962d55a3a26ad4538d1a53a38a352a59a3b26e63b9db99782e65399e7aa72a5033d4102821540ed50407fd1447a93ad074aae2d443cd5043a084504d503919d42c859aa150b3136a16839aa16628b5f34c269752b7d3f174b6955785a6569eafddcec49b11599839e827cd9f55e9f22111ef8a0e0ade9aa5cc23ce572bd10ec6cb2da29d75c1a89cae4c39f1700ca644958e7fbcd5483a151425499749ba918eeb483a92ae8a755121c2e86ee7627231f14af3c77cd2fc397f347f54268e1c4ce5e76761ae597a458270494e3122d97bc9ada7499ab27be9b24ce918c7604a5814c36e305a2fcb603d456c415906d3910496038763b0177a31a5de50a9d7e3a117a4c75f727de6e584d5743b1d4f67db55a8d468b3d7961e46f1ca894ce76bb7eece196fe59954a5f9738e2cce75e5b3d3bc4149c7d3dd562c16c706a306830603078c167cb5ae17ef459465bd3a2f2a55d69d6ec7e3e9ca53173d519a59a9564f326140d665b3ae55905b0f1d16e5d17550980c19e161c75399afdd4ce984d576ab1569feac7e74996141c4bb22ae3b00090141fe886361aeb4e0165ead375499604422514482a8cf4ae2388e23298e6216c5555c5d566debe96b8783d5d6ddaa5bb5acba5545d3f368a5315fbd51f52c6678aa9919b345cd3902552e6648f347f5a58a85c9734b2573d2acb4a878bb75e5ed8878453c9d8a8666665d753b225e916e464b0ebd95a47bb1aeba1d11af48a7ba2955e4e82a552ad00ccb199569774ad2fc99f971265dad445e3bebd265d9086aafa7afddef667dd7f18b3366985bab16de6e5d793b225e114fb76ae1edd695b723e215f174ab16dd6e5d753b225e916ed5a2dbadab6e47c42bd295568caeb49074bb75d5ed887845ba10fc368b5a0a9f50e4c666ba4867b17ac528ae79a12c33c3f3ed6f1f1c7bc1517a8b3abdfddd6375f4a7fbc7fee95161c36275133d2a330dc5ca746f17b12dfb7ac8ca7650127da4dbd7466c8cbfe9ed6ba19d1bc5b7ba17a40bab23ea2ece082cedbd186bad75df241a3b8c39ad8a70eb67f56116d88d2d54e08cd852b41ea9ae032ba66f258e780486bce4650d4c27cd9f9adf268c675db02db01e6c851561144fd1a9e60f8ed5b710ee9c573a7e58131607d7a458dfc26b87e64f25a240b141c72c9a5307eeac68e0a9eb968572ebfe0775d7ed060afef2229e268ef35f7c0baf1d10de052a040d428788a8103a05bab666ad15c89befcb2db5682e1f9af7525f98af1a5800e007c17c49b130f0bb305f502c0c7f68bed6dcc2e7f9522d0cfc2b1e0915820e41834454082a844e81ae5916034b6b85a2184da0421915ea7954a8840a81a8508be6f2a15d3e7806e2b945fa805e5e4a3d8b4fd6e06a92d4e4582eb066ac1a6b878a854c2a66c4b2a1402388aa495283abc9b15c60cdb20c7683b5630bab866331a8c0e1184179f820a91e3eaac9b16c28d08802c1984c5924330c0c39be785a4f594e3d541154100a874a62d1fb339fe2a09fe2a89c6acac882a85396d37aeaa18aa082504950b899534f75eab138f5644ebdd499f21ea71e2ad7f1b3545346aa29600c48ded1014691d437d388868e69b17fe4d269cc1cf48fe68f499a29ec5721ec29a8c0fbcb974ebad39413132349555951ed4e44272b3828e72bdea9288c825bf8a562b405fceaa2a8d675d25d1d18148bc3c4690a0c898e7fd4755c9b9154959593951391485249929a49aa0b4925a9241509eb826271f0eba2b0689f2fd5ba30017cf094f7ab634634f6c78c0ea03b0afcd2d59ad449f387f5a3f9e3e2c59dd8a8542c996c7a1422a20100124316000020100a064462b15818a49924980f14800d68885068523c1788435110a42088a128c618420031c0108490518688681c001ba6d72f52f88b090a94543125810405fb52094e054d4357382d0538373683e841c56a94d50560d69300a3a709262ff8372dd49e6594ca567882766aa6c7f468b830df84b9e9380e931653e5c7eeed71169d92159dc254112b408904af70bb82893e9d8495780f751fbe45fb462c664c2c8f8e2864c460fb2c79f129da7bbcd45ae2103ccb695acfe2e46d975fb7d80f0c6ee065428a0c7472935d78f529b5a70946de0a4b91124695440bbdcc9171cb7e38052af586156b88037597af0733909ecc23cfb64ffce5e3e98ad5fdeac2a781a8951694fb8429aae072e878ed66ce7f9e76db9914943d67cf10a18ccefabe86a884829671a3f210046e715f77a9d803972b36b8b8364e3e4f639b910b7f2e1e276449a7bafaf3ce5d57ed81c3efe3b31bbf567fda4d30c7fe6f558ba15d95a410a5c45028895cd4783d988b99483275015a3007f5056a6fc4b54996b25b09a32d809927a8005fd5b2c4edfb04dac4e5994e0d7018d739808069a10294153842b8854ed93d11a8d52a0d1d90673110e33f7707f1885278cc25213a47306ca4b92452d0432cffa868d9fa99c492bf197c642ae529fbeba5c7cba4ae582ae4b68f4e2342e44a902d8dfc3615c3a075f3aef16d01dd1138567d43c558d2d2c41bc80b9f9c0d376621b36574158d444ad25c4bc2d2a6585389eecdbfee9296d5aa7b85f07bb60c6f71b7091ce8097914eaf2f6163ad60f62cf3ae436567e62f26ed1b028cc459dae074448580498d1245335bd17913ee2386a340b118efba2e82c66281ac8cbc2fec3e2170ab03ff5820ca2c5c2f48feb47e6068ace0eeffeb2172f57f36cec4338840fb4c547d9f45ab0d74ea94b67e83436fe5feb1a6758882005179407a274e72822a43385bf50f42312189ff56ea16a16473733496c3ee8829a3852e4f2934acb608a5db2d1f9cd97e9a798c980dda537e791a9c1c88fc26221b239c070e9dbb0429613d15db973ddb9842252b5e26939dd772c642b8744456a7de20eb511a7ea395cb20f465bd343d8d7589355bb457898afebfbc82df47906bc5622b28f29ac1831cf36e8776eb37c6511552f11729ac68579396d30c39f9e4da50fe75feb5fa0d942565f720219ad2876c4b7dd5db42d50342d78dc37accc10362626c275c6d110e1622399bd7e5b64426ad34fa8e6cffef4314df8bc9bdd75b64fbcd75be28167a6ae7f158151342dca87c54ae18a6c93d3c6f1623d67cae73f3a5fb608c920b9bee3e43562f20b62ab3212ad75e3f4b6f977e10cd6942c9889c55a0c5f94330a423e815a13778e235afd5971c15d2b63757efa7764e25c01e0c0b8be40c60764dfd45b5308902212e604999cb845b7aca449ae1efcd92c0d5d5d02ffaf827581f0567e334884038c65f9ef77a062961f9d0250756bf213f024a4b42cd0a4a5cc2acd7cd87538c74851d707e6458195bba501b6d798c0b447096d34ee420859c1593ab56c4b7b4e828ddee1de4a5f4ba6b6bf76d5b426752bcd0e4120b5ed2b3824d46a891525847e4c259ff870e19bb0c84af5201ec01a28a46e5ec4c610011ec5dc28aa5832a0dbf03af5e9a7855eb5fb9a2c4f14c38500ccd66a49791278a495988c3f4b2bfd017e41a328dede253360c7c65970e4c400d4ed45c39dd1dcc554b635fe140f8cbf5bb19b1fa4b191d96c9b3f543fcf7403f413b4e6b4ac084476c5fac65b56ec6cb60deb1a73e6bd6f551d64a59d942fd82185a64652ec1fdd43da542f6e55e2ca8ff2656ae107f08da591878a2d7b3f838fd400d19db70474032aa492d9c9cedba3841367e8e65d091de59a6ee81111a011154e3978998dcedb24db7856e234f9b9091c6ed16993bf6f848071e3578bbfc3b3c6e61a12224ee092a815763b62a88e511229456e8f48b06d742781633e34bae34265cba9d654122d089c3d08046eb392c799c02546cc056520b59512cb099cbbd1fa446c3c550406e260814d2ef08ddbfc326c95ad4cd407d53af922fd78c61a04f5681302a4ed154f8566d49eac957fbd3561adb65bc5a5b7e1c626f24006d1e1e5a713c931692af91a6c307bbb230244b6c23f74551c048d21bacb49f6209f0ab2749683a067782aead546c562955c8a15d2b17d60766b9ea0f9a46e84ab50f3ddcbb75431b91a1466f7df5b3d638b736b6371344309a66dc12e8f54236801299c738ae733d716f6ae5b518285d76e20de3f1e5d1b5a15edb0db91c66b11aef931ecaf409712728e007f03b744c1e6e9976b6bfd9ea9a286f542f4b5ebf521cb4b143ebe8d0a4d7f8fe573412fd14953bc76635b9b815f97509f28653b26e82440db791bbddb06c09c47eaa51df4ae2aee508d2f750e43db15598f8b7a07230648f99853f4afbf2cf069dd54b0f43507041a2fd2bb4165e326bd4b0c505964061dfdf1b696cb7939ac32a8dfe81d83bf30669f6e2d20f105345a9f3b7983fefb47c018b29cb3b5646d26a1c22088ef862fc6c9c9fb5f381da2376600159f2bbf5997854e8d912fb25a6eba88daf5698d91556c6f20857cee155b19258ab2b21f52cab5c3b283d949f452dba29723c42fb3d4f3ac1f67f40b4df49ab6547b55c21986d3f12473eb8285ba7bcd60af277958dfe48fbb97ce86cda3745fe548554a8f2d8a19321126675326c0c71433a54cffee6d2f81d1d617f34f67ac1d9be6eed90fc4afc63c42449a0e11727e04fade7657a0be7e4757a0280529dc09e73b85aa16f5b1c1be774dfc949cd9fa792b9b339e082c5e37e4eb1a4b56cefbdf680ce8b7a5762a6a011fa12e80fa5ecdb7607c789dce050692342d916ac066d1a230b0e9475bd8498032a7415a2e484c24bcf830d48edc89ef670b56c4b7f8754c9381ef7bc5dfbf5fa5fb491421677e95ef80d19cdb55c32b0677e10ce48fdd576177737be3afe036f74268803f092a329e1e4afc31b9d583a645683bbc01b2c5ead3f69c6efc418b333e14e47fafe26548652cbe282edb494dd28d462a4c9ef2e3cb9c8b7f18ac0fc99add0b73eaba12ea5d6865089a395f165c73f90de6603f2b7e5dae8c788c0af63a2a6454e0c5f724248ccf1e6cfa14535df0667f76fa2ab686577741700bd0ab1f05b618c5dae97d1736128b7ac3d0562d937e8b77c146346b70986676ce9e29cd6bb94238e1e62fcec109b3de93551c0011a061d14847482a88a484da2885c9251bffd24611df2249a71c943f2698114e78903424569ecfa622a013ebed5cb498389d98410b38274c69bc6d2059bd3a4745303fa8d3790140833584499c3273c2a00a4a110e55094df1410906052242aee1a89a304db611ad674e17c237857e9c9af85f3c93ff81292b74e952f508c21c2df922fd931d5a8c698d056de271a926f80590283d5bbdcdaf3aaad5566d095d5afa4b9457e51bb25dc0e08857601675756c8efa1b0f0304e44c44d160005a3c07d07ca9c2236d9698d3b90684636e7495bb282406f8a1b33a37a79f884f1d1302bcdba4ed868419cffa980086ecb08962ac7d3731834ef4ac63974ff604015b768880b03779ef47a83e6b29fb6e8a6b703de19a5ed6d7e68b81e28aa1fe21f3ca7b0b04195a665103ac2be723beeb355b3c7954b4e03ae8261c578661a3107281c1941952c994ec6052216949ea9596d5c2be2942b2263dc4af4887a91248cf6b4ada2e2560388f456b0d58f70534de7cb75b45c18ed7cce3b434cdbf575e06920206e8e0899ccb0f937f3e4fd3288100e294baae00118b15eb2623547433cce47ef5c09c02c6e3fd8c78cfc642cafea800e00dc607edd9422886a726d78454c06cafa5959c1f048e0b989b5de7f66da2223da2d394df0c5eef25dfbb526cffee7db38def6139484b68eb32021179ebd848305e180d4786e66ffc636e3ecc73df113f9ba698ce4e399dee10345b862411aa5a791d126a8f23538a6748235d38b8ee4442dfc6bfd9600766156f567c48b6cb6f30aa52bc3fb4c64069abc01f1039a2d3f98ab198afdf45a45c610c4ef5743a424510dbbfad05cc1687d057932f4adf2cd50cade351367af07b5b25d3ecaae432a16b464f04c90a7dd1e8c00e4d3f43eef9bdda2845e3ce94db571dd83b79ba5e12a6d641b9e7f3a0e2fe6f55ec87a08489b1daae41865a4d1c93fb238792d0aa53111a233c5f9305dcc10f74ec7a33a375d9d0294ab9cc20e0127caa4b19e586d35061c724f55f18e932e5c6a1b4a6cdc7fea1e568e9db56dd4b2446f45a755df968303984cd5ee2b8455cb5b0219f9a8dbff585d879eeac32a869123eb10c8436fdaae130b1ea7f367376e54cc104365d5d8d27de9552ddbb55f5ae845b7b4915b2b1dda5f1e445dd08e83a64e73a7e90f10726bb22f109bca4d09e25a2df470cc1100f342758ea906220823c04844bf7aac834d24415f3d52e1c6177615e3241119065c25207160df525ce040f10ee445a4a6872b507e04a48c220ce2aa426e4aaec34ee30e405b35c3d829bab5f0c48efb55845db938e7c95804c6064858e8b864d4d6079e26727205759b20927722376cc0dc280561ea315fcc5919efb00b15a0c28277ba19b997385507b2efc07566edc3ac6e3892bdfb585f241f02e1ebfa039fe196ebf00f30c763ce42ecd588a3df50246ea7c9f92d3e874a72bb7a4ddf85ff1fa33e918039f5ea7b7a090051098c3c036088207016e0058037efce04f30c000e7d79ac9f12637aac5a82555fb148cc0a4dd8041b1c66a83c742df3d8bf21f39ee991102f00b5ab376c78c837c7c1e6af50afa2ed9ab372bbc4e216ef9f46864a8e2a9e8ae86479c3f3d939269b24175e4a0fdf4107d2e5b253c8de6822f4a0e07cad4133ffef5530633f96f2657fa7374240e75c1f1042c0418c86a91f0173decc4e88232bbb2eaa8946c9ec23bb229d273ba6849a28178f72b6d3bf7ef4e6366a5aaa245322b7ae4f89f6fc236b7e57153d0b07994388fc7811eed96965237cd852bec77d6f55f5dfee111041695a62cf66045557fd918e38761933a7ce61cc47f40802e3fd8fa6014e7ffd1d40604b9fd0576e0d1c01b82c03412f1339148fd978d1a05ba2960be466d861fd31acd2ca5fbcd49b24a56e64665a91131589dc345219ef546b69c2ed2bdc820e12fa8a9807a16881d1197588fdbb836946146bcb93ab87b13810bda31ccc4f533a599fd57bff035b55273caac2b3e32d92b2cebcad148364f64d60d7c2cfcdd2368750bbbeed63d7896fc5f16894658d0556899034cf54592a8dfec70b57ba878b7b75f285be1f9d21c1e32f4524c03e8a05646808a90429fe533e9fc9e2584f1bd9ca6ec1f6edf8452af7aa32c63f8959562487c0a8be654e048ef957b23738abb5145af52fc2622e46fb79fc281a4c113b20c91c87c03be49768cc8d510aff2723893530d7ae8ba5b37dd4540c2b517af017348b6b9a98ebc24e3580b751ef4b983ee574dba8da748be2144ba5f5878aa000ff8c0e09bbafca89e2561a2e84320a399a62ea474b482452286e6ff168154f6c4cc0f09c00a09a56b7b7aed7d804960cd3e7c3815f853074271ae8a2acb5ac016d36046db5afe04f698713caa93952e680465d45444f78cef22fdc858e2d9ccb03752a31d22266246a01012059ec37cc0570f0ff0e047655ec61cbf797899ef254648de1461821146ee6e897fbb42685a1a0723fc3210fc779cc8625ba88d86890f70bfe2119817ac0897a16b1cb647d4604316edab552f0c06661779f5c158e34041e061f1dbd09965c8b684d229321375f106bd0907fa9ef3aa7ff0decee6465fb3d5984f920903ed4b6fca65f71e0fc70ba454e1871709d59f3aa805cec1d4767aa06f19d41aa3615428d9d3e7205ae0e22b8791d6f7c18853ada0d4f72d8e2725c1d05cdc71eb53ff16d2ebfc7965129f18e81dd5f1cfe3137a0e78a7ae3fa2186d7dce4faa3a55b31199c65192ba63dde093816334df6ef3cefac687ea71093636935cd073aee68fe4a28dc635a32227ab2b5d01c655bc0df937322feafd5eb449fda506d626f529d164eb60bf11f171f444f8c6fa84de56d44526d1f8d08fcbe38a472f4e356833bc8dc11ca3e609f4ecc98cefccb344ea266993654960401b603c2754622075e69e944ecb7343d4bd161481da1a3a8c27e9f57f7810782e1b164ae46715bedf3987c987923e2abe7ef6d58357a7ee0366118b3d6ace45a7c2ed13664f5e385cdcc62e55b9f9bda29ecd3c607b4a0df92d065a202ae0e7c3936c16cfd0d3599d12b43fa81aee918d2e6f8d9ec8ba53663ce80b9806626f7d01934446097df1b410a624670737e79f2fa5076d5d2a183ba9f5aa45a4bc63d12200580b4ec50c0b63d34d0de9448d1c81f831956706d6d3242c612732756ad5ec60613d0b5597812cc5e2af9f049317c46d665850cfe0b39ca34ea4108a8d1a3b42cb8f1768f030aa2676759b4de3d3e869cd644888045d13c3c927252fa904f4dc94be05de02b2a373361c3568193bae3546d44f19eabdf5dad149535adc22a24d1a1feccb838dc73f57bc628182d42a2838477f862870f5ca05f5f99aa8aa813acd2d767eb2c860dc1821e20059728b204fe0897e0efb2074d9eea861da313c7a2fce3412d60248f1211cc12d275ca2aff33880ed144074fb55ec5dd4c5bb8e045c849d43e41f19040f902777b47cd5bd66e96ced565252035a3f864eafa761f6abf1c15805eaf258d679e48061a0b8bd91b22ea4d97ca0c6992fde352f4e860061ce649fdc1e9f8ef210fdda3cd0f893fd6e9b8a2ecee0384c4dea2cc7e3b4803902d90a07a697d6c537d8088dcf6d15e75a872403d3b2e85fc1be02e823cd6cbc09531b9fe967389ca99734d4de7a98a705e3063efe49cbb10eb561ebb14607b625bede0626e2de85930a1c4c4d1f33d60a835183bc4c06f38c1ec8452c284e9fad7972566b65fd2cc91c419c42a00b0cfb6aaf5b6b7881b7aec6894b58dee19b42fa164d90e0590e7d52492e04df7b0ab11649595b8e7824b0303cab159be89380c3aea99aa9b8a3191d26727654013e5ca83e54a012901f2f0b6090f0f375a81212fe22cb56113dd26fc96a24165ac9381a238e97295457f4d291a959037b222f2453763ddd6e9695b1909e4c2294a06336e0b7a047b8b74832ea327e2dabd122bd49f1543ed0313c600ece2c465cd58a7fedc4ca92c8c910743363fc694465cd806020c77ccf13bd211fea15d9dafe27e90414cef3b328109b65409e361635e98b2053a09030fbc0a6a07ef25c334ce8a962ed2ccaf51b52fec1e9838714f97649e687e019f7abd08a811a6971d5d7449b8000a40f34c975584e466ccdb79571bb898229102bdbeffd636bb7e5e839650238a3c9ce716e0ce47c8b0082dd1a9eae5b03cd921a91d3fe5c4de468c5325cbc2de9f0fb140ea143a246424f14de1e46dcf24b59150ee1514434454da9952f83a2de7c366680aaef8f202747614e835e7a635180dcf51622719e5d1ef803a6b3e531149f73f4b860d388a00f8d45b12c78924064dd03a13920962b172524f53f09bd916d4996c073724d1e80967e9b7e6b21141f61bf2d6583d577ac5510008e9ae1c1d5befd0168a198da8f1bc25602a57c3b3c0ca3a7fd5cc7fd9606440bab9423909dd97e0ace2303a6198ed044056340efa3396260d08f96b2180040d61f4c9b6b343068d76823b168d0bae21e7a19cd82d36528cc4f8773d04d0052d1aca134e857c2f0a54de577347dd332c69eef581b3a2199b42715b24657b7cd83baf9c4dc1e6324bb202c1677a9b949721dc63ff17b18c1e2d891401b2363d1c8a6597d85376f9fdc2a149feb937fc9c689f53d52d96b0471bb15caa8fb9c470a15b0126dd32a52570f7c27741fe4a1b5b1c1cfe24b5c967390a95c193a85eaaa97012e56413afbde3ce9775fcfed4030e69ff1c4d66ec301e096b074738e7708204a204d988798d60d8d9552071c6022042831ad15e9e5e568ebc778fe27e46c2cbae9e75b775bb074f94812307f1c4e362af797a85ee4362cfb84c0d160058dfbed6eca1b604ccfa04244583cc5d13ef49cbd4795da20deac657db4ba32945d3aad93e2a9b20b53dc92f4a4887bfdec75fd6bbf75d054eb4024e4a8162d01c061d03dd28181a21bf9abeb3dd8208f0243a314e4a7f13c4ab436d887924c782824b2376c297ad7a54bb15375c7209745b1835f369c73243f778155866390aab42bb8dded6196536d7ff388845a51b89e4277e98041c814a3dd7867e60b4abd14f8853df8f9b5d95b64c86d4c89b4ebddf39bf70bdeba1e85d8740d39c1f2013343d19523b8d97d2e2517dc4a7ec4446c3d4a2ee0cc3ed4c12daf7bb2b084cb3c5e031787b3918990b069451c0469e95413befebd3193cdef04453952f866e503855378ff6ec9a88e7a026f46ea4a8c7a37bc951b84d6be3e3eb539d2eaba148096a9b3aa2d244ea69317f4d9881090b5c764302461c11647abd3b6d711b35352961c4b7c9fe86fecec4d1c744bc61ba24f26b4f2766104489ef5aca9d22b2d2e965e740acaaae47fc7e61d5ce4d68c9ba296746cf2209504a19e801c3f9812d143367c56c67e6423085d3c9f019c6c04280be1c6935b79623faea0271c06b655f4fe48a28fd28262d0ea94349db52790ff2f2802215f18aca55a057a220c80cea40869a53aad95eebd2879132417086738baca77bc736064cdb5075782f8b19d1e9e8aedc1540f2aaab1ad89662e8413611b669499f43c9669d599630cb78119cd0e84e64d1035141372d0c1ce3fc71c5321638127a150ef23ff31e7047f061983e8bf3806d54f5e434f97ef02f25c7f9cb2c0ded4b693113c220cdf66403e55aa82f0cc032110f41a6cfdcaf31a73e6198f4a9465c8ee3f774cc60aa54d68f81df04234995b0928a9067145102d4fc3325164716a4cb130d270d135a4baa08d002b5fdb3b8a39ea7902dfeaf3a3c8f9d517cb56679cb391b752c6b9cb76a9258db2b047ce40892db394766d2a5703fca0e1bade5b3eda5551a0e8eb42e6ea31186575c1fc3150690bb217e3c325d8d10ac93a9530544dc874489f3d128b7a36270b9861e1bb25829c7da5678ef6c02090cba32c9fc40a5e66027476d3f8dd0aacb8803610bdc118c674056c7ae8f32b45d519a533e1a7c182a4473ddb3fd56940009979883d6384405cca36a930fa99916b63dbf4fb18531ff9985184c5efa67c1b2a82fe33216fea0b65ef8efadd078c3ce8fc3ba4f327324b0e1b79146ea3f3f654562d68e7e5cd8408a08af01928f7ab3a205101ea324fdcd843f18131b31fd1f56b9d4fdcaabfbf371d926e3c7760166b417d662d3b9fcde33c26596ad216fb7c86d8c459bae562e5506d4e17d023b164cf4d6021387d73e59b5a7325c65c652d9debfd309ec6c19100e31756229a2bd6844d245c2e775d6a13806c07c54b8f0a38f0cb7747bd3cf940a5138b52823bfdaf6fc89dc640814767b9b83a95117e0c7497153610a0bfca4fedb3614f2390a76c2dde410f4531f467f2792f06e8fd37f4b5d72da79a598f6f28a10e7a6f9ba17788b75f9c00bd774b02bf9140efef108514a545010ebc3089e03888361b32e493498beb07ecc62fec5b488a809c330870ef201c1d9a9162e3218c475104aeeb60031bf5d82c8d588a4d8ea314bde4f93f9fe2fbc351c583ab39d9586c8d32d29fda8009688aeff56bc0d8f87780e48af0ba3df200a7b1464a9089305f37d2b0081b07155f6e12589ad672c00f5d99727a824d2b76024d4ab677c684ca85100e9003ee0239300295ba71917dee7229f3bf61e33a19125bc8f227d746c54c96d92e4af5ab66a46429d0d1a57719adbfabd33eb97272888ad7d70a50e250e45fe2f5bc4c342132e9a21e527a881f07aa8ded011bdd1e28f1d33d2f20774553c511ead3131a126be8d0b92d0a0ef49704b99ed70a8fb0434e41184b4642013094308631968df20a390109174f27dab2e9a7b7c4ebbfe6e1f01cc9a926625916ebd020ac0a0a5c01c177261472b460f0bbda4ba89384b3693fd86a165d36238e9b25c8f05da49ff06cea8516a47bf4261721eb943b784a174a72d69188b09b13b05caa5600306524043b695bde271d02d9cafec9885591754d411e5c7839220b4453a396164cd89e36cfc1340d9cf0b1b945d6bfe60308087cf1adb57ff2511773233f3201fafeddc023f8f3d636b808b41e10c16a1bb447d87b1989460910ff6865f530535ecdf312c02a0f96e8daff5d3c0e2f69f785b2c417bb9ac1178a18ea58fca750d41e977bf2f6327cc113bca6a846fb7dd1719082e52a452ad7796cf613acb02e4f7fd9e437dc2c456368474b81a53d1aa7c9f70f5ef99c2ae707eccd73522e499b5e75c026fd716b353d844a4a205cffbc0b2cdc269878fdf3d6dbe7b1f09a561b2e5bbcf6ad4deada631ccc5f34cda80145f3907212bbd9946aab973ecd8efe053e817500374b7736a40f7b6d66c9ea87556f15e21e6bc8b13c161422db56dad915a4eda49cc484d19a8c6bf3a4d76cfc231a2a1e1a89f5486c3da1196f2f1314933d48f33e4a451de2a98cdc3aa097fa6082ee6f6ce170e66e0e26d3e7ade8c7e83221acffecf61c1c575107742b0c4635c2a6bc743dc64ef058a9f3fd132736c0bdfff847f624b6962824b81e88a202103c2b25cab3f654f7954bfae17b2d8e60e1cc42c31252e2f9b7e951c07ce30816365e81cc707f2c2ba16818db6098ee42ec4de8461ecff34c95d36f65e81a94f80af75ade8935ec11bd57ee297433a21550e957221e7872a8e8117566152378ad627cbae64b76397f34c6b776e2011302dcdefb7b343b51afd034e2b22f1623d9053dcb4aa78c80fbdb967fec5967e35c6f9f299c854c80aef7e91351e6837625b9db29357e8507d2d48f196bb9eef4c5389afb00890c8a3e1ab836961a82845eb44409edd5fccb3eb678a7d4ed9ab31bb5139922e5bdf691bad4698841ebdf560c7cbb727298e9b2778ca4e20a9f4313023be974f907f14e8f7f483b49573247b41a70ad35524deeac5a9551cfc3efb37598e4440854b3f69a195479ebfd8d5454ee86c26d46691665349296ae13c836d7ece8660d1a5c5929c7af63f269e8148fa8553c6b27fe38da500311d808f54a3d04c3dc83f4018b4f70cb8a94b50dc62c88229e844c784fb2ba3ffb6ac0f022caca988207b62e3803898552543e1a20bd01510e1fc82bb4fcf1b6794cad94bbe77eb2751d239ca1dcbd7f0528d2cdeead735451a4e9f10a1f61466021b5cddd87f885eae0ca1bd0e89be616128750cf44511900743f4bdf6fa33b41aad228c797cde430810f69bcfff4d40e5572dace36fbeb6ee9d3313f468c7a8f2273daa29ff2939e373978d41befc544e63755ead1e8cd9b5ea6723a99f6d4f0bf468afadf02771aa39e216cfcbe05ad7b9d763b7ce8f0b1ca74babdd7f115fddefe03d64d39282b4d98d294d0e67a37b058389b51351a91d4c090772e62bf0d2765357a7bd2f3dddacddc2c638a204f20cb9c9f811e0db1c2bb4160ed7958c983909dab21c011e896f250d682d18c462435fc5b9bf94b1cd44853f0ac34d8ab01f67850884d4a2173ea3bc4772a8fcfafaae6635eef0d43b79a8baa06e2641500398aab8949629310c9c4ed4faba16a88d6ca95a471dd4d47874df31d5cc146344d970fec86901e63f8e10bc38eb4b454a4fd50b687e486ad3f9cd26642af019f3db3dc118570df58d67f1196f7263fa4b73cfacf528876e325e72737ef4f57fe2bb5c8dc9f7943ab736ec68460ede1ba290a4ea4b309b600794c6263f970ab8d947ad49efe5b17e3747c72905a705f58980a743e00c85ee42166fa3a37fae38d4afa3c7c5c47f5809d9009a69962f1e2c9fa75b8b6037f791e080caeb4d2adf91c251fff561b9342c357b6a2f8fbeb32c11d697778f354fce7f5af9f1590af78eeea3310c9bb7cad8b9abb5f6cf1f5145654cbcbe0581c3e651bade0074d4191428dba1a2713dfe3426a8c426714ef6723609a59471e96a8ef0fb5ae8a4f948314da6168db88a800fb7f23201e7c7a0f3fe4185a03a2e11ea198479c32b4ffe841dbe51b110e49a852d879aaf49786cb984ee8bc7820e8a3ef269f3079bfd4030a75d0c3826a3d202d7e7d42fd10d3c2b45e8580038910cb71cd73a7cec5bf2ff6891865f13149a24520a905bb70def5586b9649ba090de5dc0a6f66b77bf5911ab8b80edb89f34dafc004d321a98631f1329d722c2255cfa533a6daf926510568200be69b6f51a2f12e3208d72b61e4991e51ed0e79d328e710e4dd029bc881f1055cd449994ef7e227335b0c4c268cabf9d17a824e9c78b5719a4c273e48e38be4b43426b67cd2d87c80a248afdda7a41fa80e68d825b817ace3c08126d5bbcad080355eadc7e419fd6b275903c0ab2e34339205264b196eee440621042f2eed99c95406894b68bd58e214f5de541acb023d20dadedd8a0e83250737a2159a0a23b5021a8fe23ba99d80222c1e689940c46050fbbed8f0ad69c0b5b0548824ab97d81b5daaac31cd22097188debd97c3dbfb91cf33ba0ec6aa0101238b462699f3725ee7ea46c0d8b09e8630ad5be55a4fb4e1061ac4610519f942f14e5fbee506165e0b58f8e26ce03394f3f3460b867457dc0c3d3d470c6269dd08aea301ec07866df174c75cf9f97f0c30b813c777f4e0960acf43eae22064246b6ad387308067b67afe0f669cba5c6689d8ebfb5129584b77ef889564773b13295cf781ca5dfa4c5f2d114db2d64d021a13d7d5125a3351a68090128f423323aa66175e2ff8be72e417e9c1cd0f3b5329b12c47df458d0e10b594b0b88e7e4dbcc6b3316a8b9d3f41a06c3338c6bb8d4592cd4d0fb9921273c64860039dc16d7f5b4a6a163223b99302f4f4ad16c5bed6662efa412462d291ca4a8fd3046a24416da9f49fd5172c9270b283d0f7802138087d999505153fba2cc42fda9bce1509cdf96b168d145a447b83e2bd13c97125451639ae580d8d4299708642fdfe6c11864f63daae9d0cab717e08468b6cb6012f72a251293775138a8c3e3bdc70a2445058a75cc801b81fb15319847d3fed917750c6e97de9f825838f7a704d4c487eb3fca6bc46f715d504696675d74d44c3b85404f0bbbfc07bcb7f02c2e07290e12e11ea4979cc874cb4c14f429cd1894d754e483f6afd40e2bc314bc77ddd81152f1de04a94ee4bcdccca2892849ad2674fc988cae657fe90764d632ef3c6b8ce1f6f795a920601e41501f97b4849fbd9bb00a9a296038583f145e60b4dcf3b659e37338e38c7df2701326e4c490ce02be0fa7bfa618cb80d04af40ebbc3f5a9746721308d42b10e8999820377aa50e7c962e0ca1c06f591ec44b30733a3816949d440d2e9d28f1fe14794f7558249420924ea08a0810f6cbd5a26452858d6fa80d4c97c29926e30257be9168860e5ba62a93215c0262674a070d54e296c7c7f05e22101460515e933c89c4d99caca7521cb07929341e2a4832e31a0122e9efe8a04cc038ce0dc9de0fb345ea7978b685ff21efa8c6f0ac18f5c67f20639864d405547197b046399b1166e2d8f7dc78335f28e58a4e045d2128b00bce9041001c95897eb1b4ce70dbae2dd74479e071979258a834d2176b3859f3a14b954841bccc1c016acdb1a2785253b777a938ca725218024eb428d2b7d4cf18299561ecada4fc4f3957d377b606914b95197102fa94ea9b53fd6eea90940a92f25bf5887a33378e5e935eafcada1ab832dc14b30e8da01592d68caf864cf06e3c13f26afad5e731f789a13c5e1b566df84c9d3eefd7ac67c4f17267e30054b4bca011c2da5ea95c8bab2749a561cf64b88eccd85d3f21f68cfa9945eaa7e6d649231a48593bb28cb5e4bcde79db1df4c246d9a72641034354e926a650ab9b353a9a3b58c89f0a398722250c8224e3fe865a2889fae2f2a8c9082611b44078e3366451176cc999a241e11d48631fa13d00c762e8a4b5a8b5d0e0d2c6808137ce109d3abdae87815c60d8cd04d414f177efbbf46bc7b1df02d7af8e1b5e4e084cffac6c17acdafef39a9284b147c02494c00870a2892c6b4bfcce02297732ec6a98bf3d36208a30adda3930f529a6c7ff2c50596ce7336747206ce57b826f254d77b3cb6e2ab57827a17082feba258ba306033e80b409530cef3eb0f5c68c9c959beafb7440a8516ecae0c1b16c0354dfc618454666b55611e1eb2d63e9b8a535a919ca23787d4a7335d91cace77b4a2f4b23f79e470c75e053c955f56dac5c1cf3d2565cc21d396346cc72f4beceb28d4f43e72ac9c651f9b83b308d025a2180f7acc0579592ebc1de853e9769d235a6d26fcfdf07ef366bcca2e8f6a6ddcbaeb47f7db2c521f0b0e318f0e5659dffb172dc2f0e2c0f13fbbc06b7cbb3ec625ec933ef9d54860b8839c92949cf26369238880b51a2fd11786f82075acc93de1842721a841129fde503432f0217f9c7005b53a5818077135679e80133f936d88742e1e16987063b631a44dbb13e1845d72222ca59193ac38a622b2a59ee8250ea2ce83a7f21cc7f979407d5072c61ca93499f2170decab63edb5e7eb387fab79790d648809db3bef891856adcf120e3e3836a2678ce940414cb942dec3bb43926e696aecef3d53ddbc1836b93f5414623728c50b8bfa7ac7dc9d6a21c1543afaeae95acf49be8540d42937983e579188e1025b87a40af4ef4227f1402fa7143f08e757ca6a4070f7bbb1bc3ded971d9589a2e6a7f7df5dfc10ec91efe77a6dfe898b87974dff839b348d3f34c3905c30c9f095f1179a0998e11871d3d224e65ebda3452217c9a849251dc864acc237794241d23f35d70d83a5acfa9f71781cda5d4f900e07c977acee4069d739e0348b9f5d69fd461f30855078f8e03adb575578e5bce8855f87fc650eb56b7d5abdbe1eb93eaaf573359b75cda0e94e53c6629c7eb018e6239fb94a6b9133eba1902f431e95e2f6dd76304f09c418560b68012492dbcce88c50ada4c5101f8ecb38d7d3f94590940999bb569adbbdfd9374abdd35194a57cc905d9d760886be933b4c69170096fd6ded354bcd7acc3abeb4aeb1fd3c241d62810f3b21c05645de08102363dc40619d5bd54c3c56d6e9f51aec37d0d16952fb107b99dde3a118f17d73f48798924b9b80c93081379ba20835ca75ba0347751a1fc9ce807850384bf72330e85e4ca49e17e78c20d3122d6bd8325003234c4f99c3636564db1954a63e7654c709700fe5b29a948678641e9264ac4288ca7c6d84c2ce43637c6302986d8d141c94eae01741be8f6ae95d328464a1a7b09e589bf8b6edd86965e971ed4fb6b5517613df1420fcf9a7824bf05785fb3598495601ebc4f38786f0b957bdfc2caa44acbe64d30a825da55ac9c5f4dda5837568d39ce4999a924e9a00a50e5759a46598e8c586024116d4dfcaddfa1481f26b7a7024be93f87506bd6ed61a2fc10c2260a3caab0a5601f4a10180ba70b1baf43a497b7af4984ee40f169fa306cd4da8674ec94fb7d25bb91700ce9f291cb66a56b0d481414bdc9804216615cb011877de231722df443de35b0ef89c65fe0c69df4c92676b709920825d10bf361f551a9a0853270e084358ce884c74b7eacde5d4269122487963669678648d1f51f19160f6e686572b5a4e036ddda22f55f6087c0a8745e1e7bdd4d9b5ab61f775170ca2084705e6e4bbba312814c84a5496c4a70db3432be323a8d58130b6b02a2a4cba601b2146c1528c5eff2bb8c8ae56c945827e2c4057453d4f57f27cfc6fb7492f8e98a8fe5b6da88480137dd423a28a924b52415d5da0de5149d4bfed665827c53287e3db9adc085565abb007853a879685d91af2635880c03a854dd706f471a09986f114eb99f73639692d0ef979adbb47883fb4edfc12a1eb92e3c887c3aca481c7a9c1bc17ec6364ccd6d86bc159dfad9b9380c6b753d454413e5fbeb5fe375119ae4980f725c79d184578076287027efa6a72a6839547a6fd1e5ca3f11413bf121a2e2832b7002cdc84d1bf584e1a7b4297c58494faa6bd64f305063ab7b87bacbb7e1a66f9f712f68994695df2611fdb662ed6f439f7ffb8c83e13e7928f726cabae74c03a2e781884a288f9bad844cd5451e022b59699d33e9844bfaac92723712b0d6a12b46964e451aabc127fb91b52e13a725e9d004955f890cd101d55a6013e6d63460b8184219a89202e57a2b103ff88202661122b4d6ff81a3ce78326069f800c1240373daa8572309a46d00fb0de1ba9f1541eb5caaf5d61d0abfd4b2bdf5feaef8dcdf53767f5f66fda7856797c25449f23f27668f958aa7e782d0d1186e67c7dfeddd67219ec29a574dff67b6a83955537fc8051fda24c5eaf615031e7e414c3c5bc6c15a5dce2179e7258c16d382f3e38b9714d01240a8ab23c549833034fedbae18b20f3de2c989cf8c43808acc8ddc52171ef6feb11ef1d9e3de8cdb48ac2715075f32b827fa7da09bb7efb2fe82ad98149ea821c71c16bf66bbb8a36762417f0371b0e47fc006465f4507a96ee81d6658a1a5b394a5f10a301711584b9ca671341808bb44e6579412bf89d3f4740a64b18e30873d0508efe8d249d8320f29e9bce0bc70b48df5ee2acd4eb1fda4671896c93655326c021e9c48f70d9848207e1a43c870aa0e5d8cca0a3222c3f8ce69d1fe3c448da2c044da311f1bd6ac0db6d857baaaca16922924c76760b61dc26ce97faad813945669f77e134b05db2c28385e5afd8a693f82e4dc16e8d251f9c64a39d57f8154cd10056d7f669d90fd33a44b5e23f948a4cd17a980a1f679158eb17d68be7123c6e707deae747fb7df585f6ec6aaca780cebc2d012fd2f8b64ebabd6ca50c2dbf53c26231c0b72a9bf674d589b7a4ce703b6cfb65c5c925faa4ddaf4e83cdd1a31399f5efdff287da35a3c6bf501b0074b06797e5a026119c0f8066cc26fc6e37d67447199923a3b4bf9e3b1d8803b9b3f7c7419dcd93b39593703c1948140a974625dd2d48b1e707277cd89e721a313f1bfe70ec607cbdd6fbee846fe996b1bcd899b4d0cc5a64612c05de8801ca6eba7567888787b982c45249660f425afa807477905f855cbf4ada20aabfe32e515f7d42753515a20993523006c2e4cfe07978497082b406133376ee3136f17d2c51df809237bab6dcda43bf298e9caa2213f57755889ff366498f8d001d869f5ea5fe9b785a0b98ef5d2b99bb39f3c009030f294e67e2650f60d80c8b0d5edebb410f81269d71c00808c0138897f1b47197b64eb80a3afae0153f5784c1eed78d15dcec1481265c97c15a0a9979408a0c11a017ce2886156b7c85fce4a6885e52517a7ddbaa00c157058cb65dc54cd82c16b037a433bd15b6b9db58644637ab2384cb6a0bd560cd13c83aa8d7a652ed7444a3fbcbc0e3f82e0ba567aecc5761eeb7e6c36c80ea36f9442526eb9bfe4fc2a070c44c379df3135233a7349e55eb4a6a315aa4eb9257972b2f3ce327536a0862ced5ad64c42b90dc2b883f4346555764020c3ed3a4d82fd886e0eb1b84374d913d70e48b11ac5e492b9f272b36451d6a439a5bdf95c3bc6c68998a3be6772b7fae3b2e03005127b1738ade6d40b8df4f48059904bba92a00ea38674a04cd0fd00e81d5b00fde124bee0f8d92ba555a55a2b873491287ad45eae51ab166244e3ea991b3989f98f475e5cbd22fc045de39b98f77bda523155ddbc80228eb18c6a7e8583e968fd9e98cd8131bf84ad3a33f523070da76cfa2eb244c229ef79f85fae68b080b485fa012d84deced39b55e8a0c62757abca4a10ab68fb99f8132fe19d5f8be3403b79d6d4619e3509e759839eda842479c3d8a38806f4a55f10a03a89372b9578c49d7aba5612ca6333d3ea1e0d2f59490248ce7c5dcc5473384b6d66b647518564fdd26e4b07e7d88d6d43a0c4348ff2128c267bef7aa728fe83138064a005edc85ef40208b66b0a1932f371086855b59cc0913944f2c1019beb6ded8be0bb29ef17dcec85fe26398569982a36fabfc9b29083ef85e95f8a8f1fc3aba42cd7e983d841ded7d00770af4c3ad29decca04b355ab15c456d785fc80b56b621ade44368a3cafc989578c0c672cf90577e4d8e953af40fa46abebf130f69b9c8eb811d407b5a37668b78c4f54d0f405a0d31b370187c32dc22723cbd663c7ed90c6cd2d365d35356b4dc5c7e79013c9483ba2730e79e77653b39dd7e5db8f4042a270daf7410a1a7f8e15d30e1c3baadecfd655fbbde20f5af90ee94346e4657e6157e1166740acc6da7eacdac1aacdf8cb78d3429469087cbd036f863aa156b5ce4feb7ef0d25dd7f8779633a7ff803ab2d0f375f1720746062bb72802c9aab8c3615779e06c4cade60231e0fafd92712ed027f7d9fc07abb24b8912f6e41fb99c354eb2eeba293f8d14768cd6e10e784abf9341ee6afa89990861df54264fbfcc4e8383187062e88d0ea30dfb1add0a8a8e95118a5a9cda1014e912498e017d4fb6e57abaa500fa75a610aa9bb3405477e81f866db6e3c04b7c2a97a13fcd95a21b111685557e70c2fdc095dd7ed540036b6b5ef40e4175a154e95cf2cc14876aa071a681e8319920a7a05107524f28d9c7f56e01329d270607cf4fe1487c32d86c9d2b531c285a9cc08fe94000b20f2a01ad535688d81b1234a1e79d925724a0b702b0a812f667d8c87e08003188361ead9c1895a140562d602d4a05848c60526cf88d5a6fe1081ab8b5a0132a496b45cfdfecdf44628b4584748465fd151e0a8b9599f935f729dea0b32fec20a4a1aa8e256ccac580520fe7579421ada85f6f32493299c80a453507839ee7ce6e14248ce12398af6081d57d1867ac3105c5d03f08757e8ecbf5acf3a5564baeeb9a4893a23d03207a559fb15ef4f7535d1072a6c64298c2e0a6cc07aba935c72a29bf8fb71cb7e7aa0199bb8408ca1569b08eab1f9ed85f1e616e36a4943286b636795ad720933a38dab3942c54813af707881c67d0e7859526d55857230ca708773298e5b63b04af93302630fc199c7294e7bc15736a08a8636b2da13990dc6e36657a0f1027ce3175aea4c7f9cb819ae8a596a8de03e3941707396bf53f6ded2245c2718ce684c874e026bc807975a4c359aebf1f8fba6fa7b64bd12c17976b6e3f25095a63a1d7b2ba8327ec94421622cefb7cf035e90290689ab628812cb7442c32773c57686156062db141a3f3b88b4094e72384faf327f60460edf6b1c6c272225b0e1537cb4023c092d19a729d7b60bc1cbd8634b49a66c8939714480b2bb9b2f5ede70c75959914d0742b8a7f15ce517df306026d0329099d46bddca172f5059fefd4f005e4f36640840e148415712cf29a58d93cb3ad727298199e29628d2579fa2e4576176aef7da05393a27808106af4bdc6f10ebb5c95b9b4c549523bd89a4f47c3ea9fbc0b5bcec2ca0b40f8a3eea20eeb3476fc3106ee4ae8924ef4fcc11a24ef92d57624d6a6d88e64b8fc7205c9a3459168c6f2a73c27a238455809c5704ee35330b67b08f5723fc778ae06ec5fc5e739f34b23f88f47784330de6f0489648337f002e7b206446e27853a3f29b96bf603db206ab49a32cfc863dfa277c27c7239efb7f055ea2460f3ada5a2287e0e05123906960477049526e3f53bfdb536cad0186acfa8465d74d4d31f81aaa984cec1f69942c594bfb2265721b5d0e79fa8bba97a54486b514cf65af8213d329a1d8c491935a7912a07fe82ef14e1946ac253a2e537cf19aa0754b70348c4179dcb216f27fe9a4b3397b81171202747e760d9b68bfcea7495731283b8fb0ed0c2324fba509ff70234dfc351ea95044c36be6f32021638eb3b871f20d29053fc93764b69480626eff29f133147ac6842c889aa1f49992db5524d06588eb400517a57cc11f110c515ab3b561345c2583b03f4250fa38e304981ead19a42c9a0f7b89e242dd15202aee55c7aa4684cd17216f98b1ba410475808d784e83eac52454b0f082155be2a88431181fe058ad9be4edddf014705b9e29ee63179ef54702bc7263d1c357b13066a686815f768a2942e5c765402dfa821f6c0c001a3ab32c327e1e3f9cc0f1c15529fbea1db314efdbec583a92ff924955b27e17c80a30f2c13c40051dc19d2c922a78867a44606433c623b1a2f120619bdb1752b8f2884a04752a04be3bab1f55d0d2f82434c98fd76fa7bf5304aa0ba1f29abb9722ea7fc843e26e76988851913194e1ba81970a74a4c88248a31b14dc0b6be71ab799a3a38e9e5426531dc77698a94b1fef82fd60607c234801387df0678201a3ada2437c9f24eb228334f465ef2e394e90af4723f7973d552680fb29f7d8f6bb7a3526e3a3ef1125e7c88146570d99aafef3235ba452a8849dfb03898fd976e7ae18d4a056b810ec2e9f8061f1250e673def1ea198019464259c88451d07ad3e0b05cf4e606bd6db3499a9b62543591e2414c74adef5ee79ff0bcfd144fdf26f83022591b4681c28561ec243888578d291adac2f3b87141547837fadfc3e98eb50e2aff306c982eb240ed18a64c95e3376e8b7807fb26e9388861a962d16a7b532a36e3136a147d1250497137187ea6da7c26d1cd620a02260003c3e91f37b6e990862f7155fe74b8e3d1a393a03cd625e98ea124761e56a32a926dafef13ff8c64790a7e731af4bde73c51270b1a427c5cfadedcf34b3c90a325aa4dc20e72542ae754be8d44dd3cace8bf71acbb79d4632de81e4f896019190839d14cdf5b2dc901d4fd7f5069160e3b5cebec88a6008de6e8c8fcd00338749bc7b23d4edd106e4228f609e544dd2e1c603f51a100d44502c3eab449c350ab87e3b1b2109f9d7863b33a4e0ae52fc3a28dafbc4214588d367124401e3df880b72b94da04b1e64bc9dc15efd3f295f841cc09e0402f432ebca9f96cc78563c7709cf392167c8a4756d7e5bfb7f2f4c110f6f45b55d6efc09703e868a2df39960391b868e23b440b897c1944c7d98e4fcd48ad6e04e515160d3d02489d2205dd7ed068ce367d1b5aa4b30660210e3ae299483048937c7141dce48557f9167cc61a51c3700dc529e4829547f3b61194860ef1802b2c1b577ecad9003ce93fa936b80db0eede296545dbc594441484da0afd3bb8c962fb8ee9d293e435acecf5e250af419254ef81b30b76ee40927c4802320091708d8fd21420dfafb6a76f913d81caa00e42ea18ab3c9354818260d30ded891bf02de8fdd63947158604297d559e0aacc9885bceff45db6c836e603ae2488032bb4e6ad8df112f9b95540d385bb81eb747480c757ab6b397da25c2e0db341126b9dcd5b21890d3c7c17c8bdc10735bb6214a123f92ab67c2475df7f68f9b447e3b0ccf8bc30b4388628c47bfe07073f2d77e39d1d86920174796074a03fa59a194ccbae1baa6929b337b1bca2bcfaaa813418999e4b2fb768655a8000e32a66b00af5a2ae41a403414c3145e31a6e9c736f265031398929f80d2b88f04e6324002fa1662e7ec6a1cb2e1389830febf5db6a2cf75d52349f5f4c3df54178f830e42baa6b08b0af0b92a067859d0b2b472d6e6aee12dffa09d57ed185be52970220e239bb0520848962aca0a1b606bb1bdcc19c102748207af694542baa5c2a73495287aba0a5486961fb6a973e76ea1ca9c8718e3864f1228057868b09993966bba466865f6bd136880374fd7f2a849b3ef983d1dd359986234731b76367ba476b3c50d149451edac9c6c2744d25fb04159f0051981c1ec0a0d5c5f6d1d642e8e2bb67dc1522e8fa4949d4ca3dced1311cffec000839bbff62194051f7871962b74d20ede2cce1cb058418f98d0d9b88716843f7a7e7436f5df943e63e0847f39644a60878dc7eb2b9c62656e3118374021f9f2e18499ca9919d20e2817e0e3c17578d1a099dd749aba3271810020606b4f535d4cab69b1e3d85e3a8adf29f485974d5a51deac0b4fc79cc4c65e2688e4eb275861561ce549dd75980486f8fe78d25c44139fe2e79853c7c0f7ce7ec2154f67a3030a00b54f1b79a4aca673cc585868b3c83f062363a9b55430b5f63c2b5d2a9e0ed7b7abc2a35642db7ec4780542b181df494ccef8fbd479506d7e62f4ecea78133012f969104975eab8b6551bf01189ed2e4a5974cade921c095391f5d85607ec8879bfae071783c897ca6952c778ccceb1b8cfeb36c760a3b1ef24533885e0218fdc3df64041379d8af6a74db7d83965a7f2ac8cac759ba23400027b0a31917ee6d555b4621b9ee5b979786f7932b4f967570988c9de2dca7678987fa2db28be205884f6db26a9d9c4cc96ef9d326404f0862900f4705f52362196ff6e7cdf5a8d9d3bc07f498640a6c523d7732713d1e07da59f4e4d71356318223dc1ff3d4e2fab0d8dc0e8b57cfef703ed5cbb2bfcfbc5d2a626be4c188ea248b5b89cee19cf3420c7afdc6f446fa8be78f5e9ab69ccc8593e73c072d5d8865df11b4633732e04b930ce966698901387450e2d03ae880fadf3a809d11df3bbead6a74ff475181914487bf3b07c14694bbb08100ee7a7c5fa5d0a7925e692ae16e9d2a5d971c2a212804fe05d4b6d4320ec4cd2a7366586809e3d252d80a701f294b02b5b5ccf0af58cbdd8ed20d5d43e12b658b090032dbf4ef48ef6932d382a1594edf2539985d1c59febbefebe06871468b807d66523674bb6c282b32bb1dd4397202834593b63e7d81c5edb46a1fa8520138dc93496a649fa251844ca1a96ca529de9a3927ce03eb9857becf42b4a6af6748ac7df9af261373a1b1e12ab0e9c1f0024de6348696279930cc2c9aa4d06c5c5620bc9e43ed099b0d2ee88c352c849a1d3cec46e71c791f251991b12346c1d38b39fc6c318c4392ca3ac2f6be1f284c0fb0d63c0ba5f836868897b1d8b6c361de789648500a5bcee6f0de32a80b9a6dc0945739b72ee8209bcfdf08f4c40fcbe2c645e25ccf9951ce0951cdad7fa4ce4e8e8ad71fa59c83c13551092086b9b43ed2d2a89714b6f4b03ae90556f35548b5b9a683302c5298bba874dca7e5b1678ee9817e4ac6b9298a09a2b6e1eebea1948c576a38973675497650a82304c3985597ed11692ce2711653e1219dec6804a34878bd09ca2d0109923e00154af9eae1364b0a394a38535d11562dcd81bfb8199097e2afafc958f1cb17bff8ef6b63822e77c65d5b63070e035cd49d40a965456b77759b50e6a7bf1c398d3088b68b9723e2c86d5ba667485fb3f6e15f5d49e1057dc8304f8b0b9b25ac291259f890dbf568544de159b9bdfd52e6790283679f49646bfe4b6d8c1d7e9fad4ba699e3e207394c039aaa54930c2793e814c5185308c6a0b1614d1ffd8769a996a23783961e5cdd9bfcb435b226b2c494dabedd1e138aa69df5c059596477c5e4b6a54849dae5e49a9e5719fb7959896947d38cf41a5e5c41ebd14842abb50b271114c15242f6dd1285b8b768a1a58854c8b7178df693956f23967a8c1b8ab04a5bccb436194c4e45f11e995fb63f54f74088d08bcf3feabeeb9e851fe35b22f1384bc2cf7d6421c56fdb0eb8adc78dabf088fccb403cd3764e10b6c049aa23b99888a7e0b4068b41f2f72e89cb496fc60f6f6150117c3960ef5920c680bc1ab05243e6bcc2d58125c62d712c4767823e189e930109700ca4ea9fd0e8877d2b2f4b544d443a1a249cd5c4e895761c056dc1ce53508366f646522b4d870cdd7d46ee4e5505dd1fdc040b9b6cc3ef4f5fd62fd19f22e43bffb4ef9c82b62421cdf9dc848047fd4093313078cf1a5098831aadade0f6e0c64d9edd32285c8d945125cc24eb44e8529c74053ae81ae887cc1e7291ee3efd7a0f9a4a2df0942348887088f56d2248e42dcdec2807215128df8aa9fb731cce2798a465d0ea432590d34f066c83336180fb9abcc2526a30f41cecc60509a3a0e2ffc7d31109d8975bc7c08259f9a585231def61305e4209dd868fd7d5d25fe906970f8e80de85e6f0855a218344457270bd5ce412e31e6267def85a42a41887ca1514e032e3f50d266bc50275357966295d5945b0d0d5f541a4f65fede598d8b18f465dee2dd592673937e6b9cd09976cc9621076438e9bcf7724773cd181c5a52b348a1b0fa209fa8d577a1e5afcb7d3d48d149f99daf53f040f91b42a2047bc3e69d8f1183e2495b4d91666bfc5cb7ff14e7c778033ba027d61e35042f942d982099796c8ba28116168107748f988f54843f91a91387260d9ed844d870bc52f15c434d16114a6b0cf0837f376c55127904abc47e50aa9dfb936a69a94e325cebb5337a89e2a57fea8ec431f0841279153f50664e2c781e30ce88e05cb735f39f1604d2b112c3f03387002b682c30a064dd27dd48bb572a3156b5d953972e2231edba5cb623249bbb4735a61811b825bc2d8462ae841006cff8b2299d36d16427c89f5afbe8e09ef04b82b4f313b14e2e9a8ec7cc31ba68890a94cef2aed005550f07a48075b1fbcb3ef67c9a67af0b1b88fe49ddcc480a04f31f62b30a4ecf41d5a520059f6b805396b74a4f31baa25cb2f72e95936e46013286f9637c4a2adda682954146e8c5f52ab9552d28bd8f3eeb6c45f274c5593f002d1efc8130b72d484a3dafe870866f476fe89a3178c88c7e2960c16083546b9c3bcb0073b02fdaa565378d1b21d65a76d837e6bdddeec6cf9c0b3296eae82f0a0b18b04edf4dacec1ac79b5ca432dfa05b7e27a1a26b6638f5119cb394c34268cd27db5a9e7747b462a5fc293789a178a08d8960a8927db5ab7394928dba9ec6f290269ad161615629840cbacf005bcb606a56c5a2f7945b2c72e1abe5eefc487732c91908ec67a2fb68fe6d6381dba5ebd03aea9f5f83b070439038047f041aa7a7bd5b2052b2da1c96cf20bade6378cfae106d42cb94593a15ddf45fd34cd09cf7789d02fd35ae933457d24cb0f1495dd5d2bb0a06e801502148211ea6942ad760e514f2a8a6f83d025ee8d1c9c9188d58d5566d32ddddddddbd654a5206880b610bf60b3ffcfa8a180890558c0c8e4a916ad0aa3c593132d7899ef7615830304131356855aaca93c5656b47fdeb0aa87fe9f8b2e586a1b8f270e451d5a097ead92575bdf592f21ac5e9e5e57aa5710e9884943011dfa75396866035c6fe92bad7a976b5b0a380e406096df7de8b225fbc866cf5a8f119aff11917c2afd6759fbf65caf9fef586cdf875935f2bbf8e835f2a87f1eb94f21752e40932b8a91f250c6cb28e17ff527e5dae4bfd14d9d281f2ebb73c5b3b0e70c619673c8cb7745c4f79aa3c4d1894fbfc756a56797ae579cbbb44de1a7995d8e4cb04c5e541db27c67a312e8db778d0d4d88cb7225063335e6333a5e93468bce5214c8d4d5662932b151a64add184837bde5a8dead41e443f653e14fdbc1f8635e83a05e59871aedd2269c8181a3653bef0796b90ec767ed620ce2786a23f9267756276d02387d41ad2a74b228af29d4ba0d680eaf4771d9581124fdf4d8ebe775808bf28107ed1d88c734e9bb438ae4543eef0507bce6fdb56b674b4f360b30605a58264b866a9b3dc936e5d2f5104d5b195c2505e82a1d75a8001dc541327f5f5fb5abf52ce65a0ecd5b93c0ff075b473cbdc7ebecc6c44f46623da88daf3ee25026b5c112dda3dcd5fe2b25736d97c364d17bd61666f4aab5ccbc985552084a82e8fc1b74731f45d9b7ca53a1ca8e48b95dbcead215c4ee8052a2f50c0fbce999e81deb4b7571abebd5939b40fdfdd3ea443c8220a48a89bd42f755607b88de300e0848516cf71ed86a3755536383b3729a0dbe6ecba11e2d2a77e9382cbae83fbd68eea9b6f5da95dc16e6500aab0afa753ae007afbc4adf12ace0e0b38391c94f650c5c43d1908a8614782902861f2ed3dc2d8e0b676549b90fb3c64d8d32956043d9338707ecab82a21dcae9dd6f4edb4db9fcc9b1baef91d96455d57dbf3eeadfc799669cf32a02796393d3539310dd188908e966aadd5f34a7ebef57c27437f43b6ef2a536daa57da5e55a5ae24f7ac4dd4ca9a84de5e816a7b6d6abbb1f885124942d7cd0eb73e3b61b3cd157e388207673839a3092dd617b062840a7e40c40e643011eb6e0f85869cda133d926b035193c964330c293939ab51d77e6423cd064bfeac4267655267b69afc6a76f2eddd4dcc1ad45e85aaac0285cc1e7374ab6cd6715c2811bf241076c1606463c93dab118abc2e9ed128363122d246f58836ab6f7bdea8232c37af230c6c6e0e24478e6ea10523aa65a35a8e644344fc6217baaccac4097fdbb6b515775cecda99f316f8e593835f3e39d8a43fa44948cdb0a4cc532b2a890b03901f3e727658e0ca93d66c36ce87639028dc3ca4483eec33860f32c43e3b75df461edeb6a6b6395286fa9c37b9c382f78538be3a772187f31cc7b1f7772b3499011c8fe339e7cab3d6fa793d7bbc101cef79fe3948da53088e1e9cb3c02fca029b1fb9e383f37ac673258f1a441fbd0c224375fa21f59b84407451149d236d797ae4e923fb6a9d8eb56d6d22b20debc8655522bbb61ed93cef471e67733beb91cdeb11d1057f2b96945d95acdcc873840b7ffc882186115270cfaa7456a0edac4075a932e12849fcfabc6387722bd26fb687c92aab53f4f0f39dc7c02f9f18d8dcb6f007bf7c7e738e52f9c1e65681f895636b72ab127ef19092dfbce70586d1981086d55847654b0d9afeac32988d6dce420bdbc604b383905ac00855833431426bb1a2b33ee85ef7d764578ee4f7f94ef7475a6b81bcc02658fe6093d25e72bb3c99099b9593965cdfe4a3fca6be1d45ba90230980da3dab937360e94284b058e5e7f5eceb52356c7eee954ca5642536dbcf9e356c27cb10bbe8ef642162933e0f36dba9123dfbca4f33e5c82e1167a9f0cbd2dea341ba99915d82ea845ec3d03bf214cbb0b4a00bd67a48d20fb2e148fe1475a844c990d216a3efbcbb989db6d605125ac301966aa97c15fa76af5685c80a59246c56df8282c0d0a35ed72e65dc5ab34aecaa0e8029b72672fd36c8f6119b55885f1fd5a902ba08ce9aa9419fe95c9b8cde4abdc42faf6163399bcd6666dc2096099b7588cd66fa28f7b44a94a46270698d8ba6b7355bb335281e15a5eb198d3a5cdf54d8aced61931bb0b5affcb0597368b8a725faea413e1f52bf63a5af41da3baf9ea54275b800491950efa07be4c995676995b8d0797b48ebd1a54a5b987ce74ddf5a256cd6263d40340b9ba9d2f7c8b64cec907f931be03f9ba94a5ba9305b6b57f5ea6787e183af4e6b0ba10abda935cc16415dd56b4f2cd3d7daf4d5c5d112d19bea0040baedf44fab447ca2d32a2dd9a7666a9d3d6b75b9c3fcf63e62627ff8057af51ac65e50577531bc316e19acae81e9abb751cf9aca69f367cf584937cad950be2af16ba9be8df2d59b885f2733792795c626947ac466ad3d5057f5cfebd415e14f4eaa4efcaaff535cfb953bccff9c69df9e2387e7791e4d81cd1c5c5ccfadf78352da44a953d32eda019074db3df74617baddf3960ecec12f5be0d70ed31e7c0adf40f0e8195feb08be6a80baaa77a50f36bb1437aedbf675be8d3c31b02906d19b0af3d515e079df6e6f97e790ce6cff4aea2db410a4a5e3f3ce3fa7df9567477e6402d8a4ce12e29e94f6955d990403bf8474ef81feb3bebff003c8f79bd7339e370f83d8ac5fca864dd143ef19bf5aa09f35c86887f9a0836545c2e613aa539fe82391ec41f2609332937a4275ce6b5057af50a80ee89e772b57af7a4eed79f732a92ad42091997cf5cfcfebe23c5c44fe2b7b7c62238f3cd6690da3f7f8c4c69352f19fd329ff3998f45f79facc3e8807fa9b846074facd632c47ffc8cf4520ebd8bd1a7c64b4a120a01f1a6690c56cf089754f7cddc8175ed86422e3b821a4b53287e06604d4e6ec8696983c7e72a279debd2c96bb4984c35fd75aab8f91b541ff6db67e6ec9cf435204f2ac2f42255f368e0a9b3c2a4025827e83c3e56afd6c18866158b7da9f3eb3efadcc4090cfbb262c6cde005dae894d2a9efecd086eadb5d62ecf1ecb35bb58e7b823aa63b7926be268fce2d8c64f960d18d24ffe95538689f1d59517dcb343d229e990f0ebec823e885f4c63936b7e2b4ffb9bad7fc32dc9ba515236dddddd92324ebdec80d8b457a88ef59243c226dbd4ae334e96277f4c9df118afe30af66dc91d29ba27bdc2b42b47b8267e91cece61e19793340634507a27e98b2479491245922e24395acb529ead25c994bbb50be0a6e8210ca6b725a566599eb4e645ed37df61d27fa76d13bf9c5f9b8fe0cc62dd4b53e3341eabe5274bb144f4868788f82582b3f31097e5e9d170b23caf2d4f9675cfbb97c572374d9c1a39357c2313c002f378b9230249bf0679d2a8544e1bf696259a21375059e4694f9b982be2afc80dd43fb923fb27777474dafcc91dd9119cad8396c2527a04fe274b01c953fc48ce1b06219c44af40e1d710aa539180087ab63126830e81daf05c7778763a1465c6e4cf7a34a5064922a50287d99facee6ec886656ca8b214fa87ce7e107fe6340a3a057c9acf38cf5e6ce183a7225690258929621e2002081b2425391923089f1da0347194056d09299e62375bd0e0450d7810c41065c44eea54031e9e9fd97dc704536a90826796fd654171cda7536a5fd494b828c23086426213a48128e070cf0dbcac076c31cf27c69134b6913e313a823410044110044154cd6e514385c4665d7a982c278cd3338ab625714f70081ca20184a2a15047edaab6568ba235cc6bd751c3b618db7a595e55360baa061e358c4675a85747a1923c1085e480aef4fb731bf24023366fed3112dbbcca79c7384a6e442438dbca6d48e82948fb0ad2c023f008a48147e01178041ea16a2fd83ade8456a84c29a51d94e487e64890c8015b6c136a57f52797fab9b14865a8673033d39bba79d003b6d836446faa6f496a0ca1c64039a99a6638176b42936b4293bb09b1c9358466827a26e88a3582c411e54dc699a03bd343aace04d5ae3340ed7299e9817a57c5724bdb9a50d7767a295aa3872641de933f678090109a0102955415667a386780c014a882d8645509923cfbe7cdfca09cf8b50df118f4b5216167aa0aa0125009e9bd59bb59fb801a73295541aa12a892a89a9e7de667e6069410ea04a8252827d5912b93c9680820974f5e24b02d8edbbabb9bdb6c77b7b5d65a6fdbdddd5b3a8917d0dd6c49636666666666de6c3333777777495ab9373f785ebf006156ddc6966b529dfbd55d54a772486faa73a55c0c359d920593af4e2bad51ae35b522213a4e8e9bdd4c5718e9544b2e9d4265cb532a589e4eb981122e9e4ed9e2e85dc62d889e4ed94228cad32937507aa631d320880ba6cd6e6c14c42434139b206125ef19b1ac0d9bed9b7b54383515a56ea646b5ccd99fe2c948deec1fcf5e6d949fcb484cfbe1569b02bd691c276ef54aba3893433615d355f9aaab418fe46b189e5c6be521f53350871ef44a1a61fa0cd0da83e5e93373d14f9fd98ba7e82bb2555564abfaca7d62436c36934ab61ca0f295f750f5c4b8cab7a31268b80c855f78b8ddce4330dece44ec845fd7db99dad5cefa1559b4c91e76c53d59c69da919be967589cdae9155a95261b37d169695c9534c3d562e5365c81e195f7909605c7e01e2d619e875d6e50dbb9976e694ddea04c9b32a7d3b8f3bbed9a398c651d86c7721b9a7580aa1b5ee236b5d4d71abafb2b8d5575ddceaab226ee5380c9730ad2ccf5bfeb0ec65881715dc23fef32bfe2b2b906ac93debcf77653247cda93f9b8ed60e9bedf55b3bea730276180524345bc2911a6f48e710eeadb38cc678b4abbd5b5cc9426c090526242689f488f0e43eedf866dfecaa92c64d546ec91e86a9bc9d888aa4ec0a372319cc9c58c63206fa0103ff7c2d601993519d757b6bc96a0b0d4663ed5c63166d6cb94ce397eadb1989ea80df1e32cd5a189d8113925263e1179d828359338d97f84af7d3b75b7e750b48b7738d9798c6482d6b589dc956a8f1b58c32e445666211d625f74aad9603a3a7e510fe0eb0fe242293c97e080b8590da9cedfae252af074a19c8a4ae2601a84968122ed5d99e47c824b5f1e1b6330bcd3d58af8b788a380f818018764316c4affa748a2d646f9d9e0ce52d3b79cb333ef2ba8d870f7e9dcd82f516b82e2ca5526b13d5d9dc6e3c9c371093b727bcf58d748571b9a0bea1b3f7349086b1f5d17bda413f7388346c2c7bd8a688eb2c0c519d25145691a8cb7a116f2b126f2912756d6c9f9afc51bb9888818090745018e3405dd66b358af47da914456a261c300ef4268a256f8348f9f0d6cfcf02d11beb30532e1814563f8ea4d46e5ce9e64202bdb1fec53d4978eb8c03a53ab42b4fefbba9ad9ce9d92825a43627fdfa072034d73909e8eeeeeee65ea70cb5dd325dfd25746b758971ab6d39b9a96fb721157338f498e7ecd511973d87a3bd5ae25667176ad86c56f5b07a51bc50957471509ddab09e76b1ecca1a36ab75d17b50dece436e9d8748221e3f5df614f568b7ceead26bd7d65e4474ebdd0e76d0d9c112eca16591d1dbbbd2d9acd75dfcf44c562a75bd08ca5329bf39f7deebb734e2a35d658e79737e0797c6dafae61cd430b18e3c3c7e9a7e730b5463d66d59848858026997e84040ed12a3d57504da6d59a4a8878b5b2f82f2b61e015bc4c5adb5a5cc96468a54a2766dde3eba5856a1d859817e1b4b9307505d74eb6715fa422ceb513d6ad7b6f98b71c4e3c706d1addb307a8b6e3d02a33707356c2c2b52bb841a26963cd43097b22eb56bf3a0df1ce68acbf9e6b569eb6ad36f5dd7f57ce81df97ddef93ce7213984ffeb389e8146782a2a2efbea887b7a9c9bfce2dcc5a35424771a245b3b3a48e89eb7d3a7a30b4140df9c2b5d2c83781e7ab78774b441a887e48ecec1d68ecebb7e47da901b39844ffb38e80df51a720711521b92aabeba539df02b0a5f5d5f5f3f5cfa02844b3de46a83c91340937a26042b876dae535009faeaa20f1dd56127bbb8d477f825745fb65cea48869e43f1b8a8be7e185a1cae8e9affdc8774ce2d137ed570ce2d5137b34642ed6a9809ce99ceb14af0d3fc9c3f9be9f39605b9962d57e0ffbc9918566733994c360316ce5b4b3f716e65f687bbcfb9bd62cb736e83f8b53de75688615b8c033f3ee217c79d15cb73fe3a1be991a07c2c4852622a9c27a280c4e33ef0f3d1c1f2fc56f8dc9bf1afdb7e66b433dd4c49d9e4dcbb34425a54cf7937519d7ecf1359f4d004929ddac57909a48bec29ea31829b658f0d1fa1e49e119c76c358ac1a6403b1c9b90db283dac5f908244d79f2781a2fdb9bd5350d03dd1b66e3a0109cef9c2cfb874d2eecc83ec226e73846b7e3808e903dd4ae7635d3c72acfebe5c902bd74cec3ae24dddd4ffbee241904f4b2ebc052756d988286be1ab3b0670d6b2476712e829f1e8dd9ac61f5a99b81b3cfebd391b2913eef1cf46e6a1bef69b953cea6ec6979d9c4e53466e336ea13cbd8ec5ca40ff207d902fd86d31809640b2c5b0eb071123c0236fc4659e486db7012ca22453d6eb80d2fd253d4830497dd288d987ec349287b8a7ad8b88db2a7e8465924a75d9cdf709b92d52ece632830dd6e7ad9e17e1ee367338dc032ddc66df8697e6153726ea364418c1be557c4f28f2c491a5208ce7f8e33c6fd9cfed93f23bc93f4c920211b248d31147e45e1d78c73ce48fcf2dc9d73aeb112d50199d63f0cf3629cf7117e513e7a3ea23a0aa00fd2074936a2379cefb00e3ae72109d6380de9347e5ab2860c621df490244b5bba0f613306e7fc26886bc36ff84cc94c98899d9ef3d113a1ac519eded328af052dc833266293731b654f510f1b96c26edcb8e124b80d079dd2d7e7a0d7910754bd259ba9996e92dc1825ee599fea13d551397723747b030aa0bf63c641d21ea1379cb793536371ce5b4b6b08bffd569019ff3cb446b73e8daf9a7cc6bf7287c11a1fcb8fb43c32c073fb05f9661c245b35344e4706783e5353b6538b6c1ab2b16ce1d7d9432fe3d7d9b319b2b5b0c979335920fb638f501dfaab95878dd4b0fac42ece3da486f111bb38ff1ce9395751719d39ca475f795e18a4cb79cf8e869a88ebb88355b6787437b1c93114366b97692f39dc7619b7b141471e3f3614295200e5dc862245607936b151da701b364e6395a85ddc0d526664042f5281dac5b90d27c16ff85989482869737ea3ac4847a6238d50167d7ec36dfca44f5fdc286dca2ad4aee69c9d9e989a9c95f873ce43fc62c22fafac4e6c12b1ca8ae5c5396723aab36d621d75a43ca4a3b564fb0f947f248cb796215c7da765848e984ed59fcdb402c39043b66fb9bc902914d972f1fa2e013072e2d7e79c57272f4f2244be72487feb73fa4c2b5b9fdbd657f2114361937301f01043030d091fe0e0890461d01b60f144182c38030752a8542a6c72ee21d9da71c2f05f59b1d0b12e7d7ed6a5e7be9291d8e49c3b6a17e73b1c002d7c5499ea15a6e7bc9d38ef27ce9b89f36e82f97c68b00933842d1eed2c7bcf7a8b964d36afb1160f6b6bacdb6bac4b56bbaa77ceb55b4a8b5293c85a63b3b6ab76248d714b94b64375ea0c9f84e543aa5217f5ef4ba5e80db5248ecc0c1292315b26c25613b03462976245ec3e5fcbd68e209e531f9fa71f843fffca20adaefced3fffca0054f1381ee9362c1e5ceae213b7d54e5597c91a36ab3b4ef59cea6ed2ba8acc5b21a5abe30eea8129f7481c6ca6b075dbe6512daf7ea6536e0045e8d937af5b7a7ea5c0a68f1888fc00d2755d1713e59e39393f1ad6436d504315403123621037688ac1f4b498881981598ac194459dff00427550cf2e4487e82ece9129f7c8b39ed585143a679df7b91fedc2c1660a526ee7278ece51a5cb4b99c2cb967be68c3a52de2d1d5d47b674b447cf38e33def0ef09eef70d7f97cce76cb46952ee5c9fa416f38357ae8dc5949914dd0457208c9aa11e2e2e097e7ec383cde904d84dad2e1f9e69e6f658b73af6c39fd189a9d48b3ff00e20d855b043d3b4ed1ed5e60170f865a0b2daf28cf6cf47c5469b6eb534ac6af93369d140b0c31f0eb2775c46b588db3dfa786d198cf09b24801c58a124c641102d582c5097a800311586e880286283df5072994a400c57a8cb8a424012236e34604e0c48a306234ce3d2d319062465a3c88f5d094407800ec33a5101e003bbbe7f1dc4beaf07cf4ce1b5dd81c0636f9522c41b59b3d77a764d7e9092df7895f748a164e9efd6e614f054f68b05d791283aefb38daf8825b1bd52e4eadb5d68ed613bbb71a726047d6cf63791fab03adb52ccb514a291547b15d588ca21d67adb5a5adb7e3ba6d7777776b8bcca6b6eb82fd5afaa0375c370e65b9ce65c987677e29910bef136dfb58f2c1e4c37ee197aa94c9090cc5facd78ae5f22c0532729cc870fda5bdbadad387aa34b67b7b65bbba038bbb5dd7a4ba57ca4522f3935364f5dac4b3e2ce50da77170967c6c4bbdb4f4928261cf4f93e23c5dba386eb78649a958a0b5f6937c3af3a9afa4b0eb951080a73a4faf8f259c5f1a57d85c36f0e15df796522bea3181a48f762df9b09732f960d3fae0175da55ce8920faa4365344358ff90551ba8f4863da7edb66d9b8dfd21fc944d4faca4cd875f1886a228565288cd8b95448d3aaa4c86baf7de94981a5bb4e4615f5a2bf4d7ca952bc96f3b847949c1801f0c8cd771301b8ced17ce8cb067eac6e25aabb76d9b4c5bd6c6a94e7801db1c36c72bcccd6e9bca83e9b6176f0bd2f47a9dba60b96d1bca5a4ae9b6f150a7edd23d8e620dbbc1223cd715b776ab174e30c2c66937a537368ab03686cab0575d57d898d128a2be58ce82638b07e8f443bfed9cb5561c2deab625c317d192e14adc624819d656555798081d79cb9539b21fc2dfce664943d334b7e6c565bc4143a41cf85fc7d510e1067519d60b2dab0dd146a4fce2d708247881e587f0d35ac20be1eadcb8df9a3c6b73a5bcb970a4109be73c2c61e4e1bce438ce398ebc7143424dcb14e94829256500801896d0b40cd22d9aeb71268874e438196ec21b19babf4fc7dab23866bbe809332c191a991d8c170cb9cefbac88724959dea80dcb4a6fac8365f53cbbb1c76d22786e548aacd4b5a53490faf6e1e158cc65b496bb04991b6205937369c3dce33ad2f3cd7696b3bed9b256254ecdcd63d1adb78eab3720420ad3b830c336f350baae5623276aedba4a44134f8ebaaed6256a2d2bbdd9bcd625b52eb1a2eb6a35aab58b5abbae12516bd755225c407f1359d0b0d045e63bf7bcedc7390d1b61a85107971f37b5edf295dfe7dfe802cdc17bdc959439925273dbb62d44d5b73db64b9263216a246b0ce5d296698fcd20189294ba2edf6f0699f3f1b863b282f117954a9c42e5516adad96577b6bcb1fff00fe2154b48d9c4699a36759c37af95c8e3a4a1ca342ecc34a5d65a6bc59b256ee5626e8c543c905840ba51e256f126895b9dde2071eb883a6214459d7a53610276287d0173e4bc502fbe31caa881ac809217ab98302a402d40619bf356d61fe6c1b3b36984477564a83829b87020432dfddade2e166b46a45487368cda20a2116f66b7d2b8d42151086137154f9678caa6581b7a9222ba42f7696d080805f7696d68863b069309ee49af3cf5a51cae1053d0d5d39a104cd09a104465b40f5338859c1c55cbe7f46323c57a3939f83544b7a5268410429ca08a2ab1c67b4f6b4294e04f1b32dc3d5b8b42ac826932990c491697235412125427494a33380b31c3d7ea7cc450ead0c9485fdd3b798889be8a3f8ea3cd8f3852f831c78fe3383a5df2a3dbd1bd1358f9d12f931f47f7247e745389d38f8e33fbd1735090e547df51e2c71468f9d1c59167f4173d1c79c450290cc3d06f171fd68250e2c35a92251f86b26f067faafeb3dfe77704ff09495103428ab7ba33ba9ae76ccd6f4eafe0547fd6c4c08437c053a58ed22a74fd79fd598fd0a09b044c7cb77b351268f11d5482efa02bbeddece1876fc7e92106df9ed383956f67c187137c3b0f1fb4f8761f3f72f8ae95a08aef762238c8905401e4c3b75713207dd77ed8812abdf5192b6a0ca47b59de124e15f2cbf28c5cd0c9ac3c716409932c3f48fd1ce9800332baa0a20b338a965ca800469eae2a251e7ee001cb0e3278f6008c3cccba3f580031c6134e3440611f22662e289a010b8030fb418c2dacf2a7c413339899198591872d68ade0c2e54f1a050712dc0007306a388c51cb818a226a3a94513bb254dbc1e8ade38c3cf64913ab13469e56e900293182203c3b00461e66c93c01c00d5ba68c608ab77e7365ae68f1d63a8e11a86e98c18491c77aac1b54353620b121076fbd84ebfd245105e81a79ac559d10c58f12cfcefac9410d613cbb39f27091aa56c313128eb08019e1aaf0a768060d4f9efdc67dd204a6be6c2c010cf0ec1e0d32193cbb0d960f0f5e4c01d262c6165c24f149e2075282aca0a1a665885863812204af96234f579ea188873d2154a2ec08156ab22fdeba1fa9ce7081096fdd3bc3ca5b17e176a14232430666f4e0add760cd00c65b771a6619752693c974c00226a50039a2d024aa24842d4628638632b2d8c2c3962bde3a4ece107c78eb3b2cf0a81979ecd296124e1c4f62d408020e41a822041ddee2c868b139feac01820e4940c8e2a9056f9dc63d6005c82888329ea494e153021d6a2801132c9e90328498adf44bc205b32f0522ee793956fd54d590a1f47a76ef07393c3beb074ccf3e03650990eab0aaaa4646cb1664186989f2ec323ee321668ca767580e3c93c9642588d59ab7366fa9fc9983662d156bb56c01005b916059c205163390bc75df8195b76e16bd751c1ed4e0ade7f4c088b7be73c3db294e55de3a0f1f5996bcf51f5982f0d68128bdf518a6f8a00753c690e2ad57a01a2454059617aad3558c1d88b1e419864318b51412360c200860593750e9fc59d3d4821a802103838bb74e6d30c5104f3650d1420556387172c5e8e27faabc294d24b8e2d947561247bc885174d0c3b3873d8017e98b289efdebc19329d78ad613bd8bf4c50ccfde39ddd3aa52c8c10eb41c50c16359d656b3b518118d66c3ae8d2272586b8353e5c5132cae90e0d9ef152bcf6e594c78b1ea1a18ecc40ba65891c133ffce95140d9d8a5c1fbdbd3a05755f1fbd48e8a21309392074d123707d246f2889bd903d1589110d50c48c30162362455c4515494faba062e9cf7ae5db29154faba02287af57f8057ede6ddd5627afc7c813b260a98ead3508dfe7f6f2e2a997b2889d56a7135cf6d31b15d0e2e1c293d330145005eaa3f61678784e9e93c762dd7b549d9c4717b81932c07a5b0601dd7a18d6f2c431436b6d8be36ec8bd743a58cf796841bffcb20e5eb2150b49514c9545421ffd863e7a91ebe28ecd6d792f098220ebb9f204f25d298407f8912db0d93dd875d4dd971c1d45e6380155a7fa3492a628c4759bb7107d0afda4594a2130beb95a9daa579de80d5215aab6d26a53b3a7368c783e1e5f2c2e4f6b56908efe146b569cb015253f3ead5939f2d5bd960efe13041f025a8384b6506b6d535a9424252a5e91524a696530865c468ad33017ef8b83f3629a2977d4bda10b49632d27b7728f912a0514264c1073f19e4e81163123345e1073298b3c3f592498e0a6fc76946f47fde9270f9ce756e72fee134b91a3773edeeb3eb18d895faa176f90e338ae565a5fc6171f9db6eb73191ffd85bda8f355d83519798ce5e6d4aefea1edd259c1757194bf788c17f580f1b1e580d15fbcc7e82f654f51e73d234e8fd163ca9edbaef67163baa39f4e632f7ee2bc8b8f650450eee228d1bd933b22c1e0721082230eb1c9e49b6e2deba1e68ebe7d0383cba1d22cdf3408dfdc0cdf5c9e97c36188930be2db69e57ce85437a5481abb51727b8a7a8cfee23d30cef21897298bc8f88b8f65cfe829b7b1e5b2c814b9d3ae76197263da9aa88ef8ed1b167eb16f47db518d298b4e80c17541951dd4aef695abfca4b42f56a5aafc416b458ac08c3e8e63291b4b2345aaecc553650ced6aa697b2d67800ed29ea940a0a6a971875e6291fcb3a6474abd75265113bca475aa5b5abbdd2682c64b209cae938818b2a6545d68d14b1f3cc9f2ee3249e4e7901ed9b49b6d44b50987cfb76a537a65e626696f16b631d7143a7e77d8f472e694913c53db7a37353f22d54048771694e96bd3071cfed684b10f6cf2ddd619be4835b9376501bfce7211d77701076eb21775cd9dac1f9e60ae03c509f73cf37f7f91dfe9c23bfba85cdfeca209c77de1e1af1bc3248b99141d8bbb3721fe7ecde67d93b67ee9cd9634e62b359263b9271953d6b416116892a60d02cf4a997962c1214669f1c2c7be92b7be97ae549c1f8b39768953f7b89b7a3ef8d69cbe27ba9c69aa9bd9bda5b4e2ebf7cd11b93e7ddcb1dd19bb64ff689ea6c55821004960f52619cf66963ea247db29696584bdfee42d2f336a94b3053dcb36ef96e1f19c0eee3e4ad0e765b562d6cd61f10d5f024d629d022d67272c53fdd89cdf68d6973da9e36a6ad8957430110d041c626fddb37d49af6a9ea22450e324a80f1d63d277200459578559d7515d158a5258b13903183135940e1a307258868ca811858c8a8aced8813326c14d7dab08a7bdab06036564443135d84a24abc2a7b733881e299e99ea2e9a4c95b2f892ba3a812afca758419154422a4364d5b7c7a19a8f554e6639eae9ea6609ceabc38f5737ceaa7cb53d4533fefc394ce238f3b5992366ce80dfb48de188104d3550269020ef28604c009d7bdc5b0d4f2fb2481c48f0bca70e28c2fb4c0000d89891cb494c08825aed71f94e726cf516cf11cf3ccccbcb1056d786b398c6f29be9ddcd37ead7123849ead78662f503c5cd824dd9cf20bbc9f589e6149d904cbae9e53aeacbed396745208cd895b53aae1a00b4316f7c6883726b4f7eacca81c81620c1baee8205605892d78f0c5145758c9410c756d16f65e1ab7f67aed92611515d0eda798be9d7a4865401034f22b99faabd263939d45f237a9e13b0c82200882e00ccc0813c157dfce476d74b244289928e6c6c494675d1111b11a9401794027a01d529f27c74787f1d1673c04c9d3c2385d952ceb8957c53a6b1e7410c657247dd638cad0fde83df2c080650c99c258b2667c875b98d949400d5208bbda6748206c1a1d39999595888d98c8c908765aa04daa90438719dd7e0f7eb5ab6456342bd68a895631de420cd923c6513c62ca1e4cd4b0bac5a3a1445b9868c6db790bbf447f892c96c7c4c8ac6456327eb2623c6685181f6378665e6688b6b093b312b111c3e8184970c14316355061c595582b61573b0f8c6ec003133a20f14513b194cbb45c86b55aadca5346094d19c36a577bcc503b2be1578ce7c4d0a00cf311e30de41211ad2a5183de60790a81f11e796462627c1543d25fb1e8cbb0566510180fbdc715603c743ac6c4f80ec3c4b88f186f071213e334966a4ada6a8a719ab229c667ca1867d1a61897a14d31258cafca2695e72f50beb1d02430244f215e796b00dd93f57dd914995acc02c2b34849968e2a6badb5d68ea0caf6c9b2b79e0f9583aef2d1da18079d635cb49e78552c18b71e43d217552b95c334a1015a558c0a54f90ed3a8298dd4788d873535a5acc8c2c09442fa552fa85a761cc7d668ad0eec4be7759ef779ca4150f45e543031302b18186bad6f3666a58a09436f57a954d64787294feb312e866e63c41b136ab18db2de8c8b8be55832312b154c790a49a9ac751807411004c1158c6a05530649f9e7765c2165cb9d94b5768663c9c4ac54b64c39cc4be737dc72562843b60f4bef234f219df53ace6edb5963759083c62d0dd9e24186554fa036a7ec52b3f56c427e8be137a577f1d5eab7ae3740dcfa3fd8e495bbd8189972f48d3cdd7ae255b1ce9a576d64e8323210d1e1fdca77384645b62240c39bd4f09127e45f39499f8a21696bd0288dd0701a1ed220c33af6a051ca8a3695ab4a21fd2b1abaa8c41a3207e73cf5a05470ab8c089bb5a9cd1dd8f856c278e881fe327a7df1884d282ddaf0eb29f56a5359c3a68d94bbb9dbb0d9d4b08a445dedde8d29cfbb2a4fd6ab8890f116fd257a6fe44a4537777155796e2ee3755c618b2145df4899d1c7948cf834be9441c2d03d6bd36f4e550bc3689624aa98f283122690c24aacc16842c809136568610519b14b637a3d2272596ee23c8976b323cf6adb5c455f15a3f2558caa0cf2e2a0db718597addc61733bcd8625805d9bb7e7d098db4ccb94a7c362cad35ce56ce58bab72beff85f43c458a7e49289ee7a0e8593673b0b0d9ed350cab311b22018872ab536d62b35f84ae144700e17372f04b14bfe5dbb60520c9653f6d6a88c5aa2a613dc3011c1b3db42618ddea4475da366cf45ad24bbb65546a1de3a9d72c54858aa4b563f3eab50cb2c35ca33a9df306aaa555cfbb9586a4aac04ace8a8474e121ae551538a99ea0e4995d9aa07c23e2a18631126b3b7a1ea2f2a7e51a928d62694955056bbba467c7c121465bf0f90eb07e08eb63f48cefd29633af869a164d352b3f4f6b55b4d4aaece0c7a7b52a4c5f1b30bb42bb11529b7386f6ede116a3ae9a314cdc84986c4448b6289b8cc8055b101351173522267f6e46dfdbd146b3955144fc3a3b941327eea99aa96665c855d514442bfd5c4e236fcf0ebc2d5547aca66f20d58cea54d54c3563d88c4c75a48aa2aaa9a84021535d51cd54482a25d5928a49d584220a4125334824f9f6139cf22111a96643ed5535db92706de272de9533337ad3a48a2355462c267ae3a2833b93c2e4a203171d501df60e0a32ba28a086a188dad5ce62dd8b02fa0fe99b55258aef19110b45d4309726ea6a77faeea7a36f94ecdb754002e1a2836fa724d8027ad3bea188b60d45c4b06d083543414121a194f8456b52fc7ca3a87c5b26285044df2e3528921a8aa03f5149dfa81a6a09652b38e3d7a99a1df1eb045b001af1eb73e2bb0970f65d49ae600b3aaa33437ba98173259a60d92a0da09872058b01d4568f1e14a200a3c6de4255a17beaaaee79f764251955f1da05848b0a6a9264fbc9933f737cb52d6b85b4c0b094acc70f222bd8f2d56c2268121130f8935e2182883fa9d3d7994ab7d8ae51dd865fa8af8ea3aee0e82b114cc4efe873c19f348a93db0a5c8daa02970158726b5087d4011a48ab81b40fd4b050aa6181f4b486c511162c78ee690d0b228f0161e7b4c2282d5126eac4b1d09b550165a355d103d4d094135441058609d18c7604c912f7e49a984b4e89cdae327d07c65b36619e07bda1955d4e9f2b36b515085da9352155076cb14e688bd960e29e5c1387c46607abb1ce392dfcaa9f147275ef2b735170384da9194541aa5da9512a52526de90ac73511f9d0366c134bcea95d1dd7d4aece89c0b8c039932748034370c4e47981eaa8a3f93917fab95ae6bad56870f4d55989ab759d10c33ca6aeeb3a2647baaeebbaaeabcc3df1ebe4ae7c57030aeab8a72348be73ee0a09811d866adde484c431e1d7598538227e79e75c10f6d951c342efbeb22209c90ad4aece2dc991447470c90d953d6c769d900d17dc931baa4243dff9c93d7de7dc13bf780bbf6af73384480f9b1c735c5dc96a3410e22a532d15c910223d5556812a123af298c0c46dade0bd3763b3fbbce923392c6cba66b522d83fb26ee416f31a56c4c54797b293955e8fd0c51ac93db50bf440f0db86a648f1047e1e37e39ab827aee6919c87e4e6dc8c6b7aea645cad878f177e0089a1f38d64b5abf32645d773e47333cfa59eabd4f38df41cc7537ae395a710d0678771b0d9792569d879e75c8be36a3aaf434eb6efbc46e117ad3149face69e7674dfa6e057f8add1432e8a610fab342f9aeeb2a0bf1eb1487f8755e227e75278e4e49f731d1092df167e7e43ba33f3ba3ef70b01a66bd736fd8e65dd7754c365a6a965adbd96062b61da5b44675680f9b9cec398e13e221aac3cfb94763a4eebcd57d4356398eebb8e69af3b6dd13bf9c38af5a38fe79aeebba6e46753aaf3527cf11c015c6c56959079dc646073d02a383a21709bd2b232016015d2c693be83beca3fbc4b66ef41ea28f622982e4b531823a574b6f5795bb397b751ca08a0385d5d913ce73b424cfd5dac373d4f57d2cc424780e87b213bde17c36a33a9c84de208104cb88d410cff9f971f486f35aa33a35f8be548a52ee86a66a96452994524a3d6badb5d65a6b6570f9d5a0e739dd51cf38c32bbbcda3e6e622f588547b7f7ce02d09a1436287ae073515aca0633dada96087079fd6545083d1c7cc520c51061884db7a5a1b82e98821a0c872534f6b432c61c510446014432ca90d71c40460eda9cd18e206ef89fb0da10302196096cb3dad2de901d79654e161c3c9c14921670a120f7169a48c71c726b61f28c13d71bda7b525478024b8ddd3da12262ccf528f06f77a1eab65c5053ab08668ac340992799af1707e88f1ae27de29b624fdb0ae151b7a5811cd642e1941aa284131587080a142b6c2010f2f34151640a9284d30b41f2eede9e50813a0a2704a5541010f2e50c86e151d4621145396204404d0825c70bc2e7c0871727052c8691a3d1b1c01a310a1d8e18b0151c8d484270512c8644450370325eca24cd8e5cdc009b39892f470c4a4030f485d9c7104a80b1fec80001accbac8c10e084002ea820b200448699ad2456d07042411b58b7a24ccc0f33ada9fc7ac05ca0e7fe268b93463744d9c686272e20a6f06276a00323921e506276290c40925827022054ee480e44261e23c15dcf1e9942686108326b0d026bad8614440134e5e1356ec2042016a9797a489273e1634b1821e9af0a1891f1ebc256abd8327653cf9810d1cf5ac87002768de0faec7838e76bd2fc27872c50c2ef774ca9329df1301a6c4a8246c56964dcd3a552310000000d314002028100a87c482b150240c14495b7b14800d87a44a6a5298caa32889510a196380318000000400cc008cccd0360097f32720e36e7a6172fdfd6f24e2fe10e76aa9f7c1b6a5611c7cded1797201744c2cd6bed49592bb54bbe5bf706fea3d1a41281260ce527ac0109b5ed61822337625082c462907eed847cb30f308b2e3df8b92a0d129b4fc7b8005052fa4226dce556375ae6516cccd400e6f94691131cebd80dbbe814d54b3de953644e61e7a9d80598c26b904ebffea588500478978cae7f177c59f5ecef27b5772750672045fb7dd3f47d52bbffd1aa57ea080a0f64fd6837f26b61c83209d54436e960048664b6128a6d53a4f9d3619b2e62e35b298c520d978335dce1710077b49680cd11e614517dfad336ffa4cebc73e5aa0f58d6ed39fb13e272dd2c7c102048410cab19495f4843a81ffe6871386c61d4891fb185c850d89c05ec0f60223e2b0007003ca7e49ab443207e016aec621c31818076e8088f54a93d29e082f34541b1d0bda2bffb4d000733ee353ead73a5c6abcd6101504ab31ec81096dba2686ab7dc24ce6f44d46fd1435fccc4e8d48226153a309c5866d856b205a90c873cd6e9f25d18660c7098d211dcd3e7cef9749d0272f5e609554cee8bb21a264f4489d995c8b91d5739deabbfe16319d6d804da3e40fdc031efc63ed0826db742e6698828c58eb681e2d878e820c9e917280cacbe340e490a38bbd7a61fe24466c93783e11dafbed0d91323911ed57a719c32ac674dcf3c3800da83a40ac56c4331f7ba944a92ce70c16372b20c72c69a09d26e64c98ac01f844ce6430ce09c98d84d0fd034bc8958a50e19d1d0a41afd76f6a2a50d07fb48092163af35ae74b6852045f145c892c43d4d4fa760367c311691415050c4fa0f010cab5bee15ea74a489209a1f4b58c67ee90d54d2947c9ee42ff4fb4fe428642a65ad53da591181485b7a38357e86d6e336b03fbc0b114f1b002d3ba82c961cd26624d32c281511c6863d797fabfcbaa4dfbed5cb908321f07c0ce4aa20250638ef199b23d7f1d2c3e487b3bbea0e978f09bfedc1ed4418c9bc34cbe1981becce2f2f6ddf88f76d94869d79f1fae808ae801aa8cf3a48839264e21ad283f822a5eb32a2d96c4af00746ef3f719340b7a7659340bccfa9e85ab2fb9439ac830ea59c0123befa35edfb50fa09503bd2fe8cd0ef04667963512d66fa9d6e3a7477501c48dd945532f84dd4414d70cfa3b29974c05cec37a15cbe08ab48cdee7e80bb896dcaaf1da44ec2cf60337ca6cd540f1ca8481ff63aaedbad5d45d4519ca409e775e609b7d3a022e14c5e88cca8719b8743b29d4babd3e62e47faae9b86c33b424cc47c126681d929e1afa7198143e32c8dabc9504c38a3e9da3c51184ec80211de7b27b122cc28bb29701f266e335756c02ac32043b541b73a005e01cb4978e14e7b919d4804213a156239aae558ef4f1480c2ed8bf2af8444812c369777f660239274ce5bc2904bae95bec6e3f79ff806f6e26defca84d6a563490e4d370082f14a7ef75daff388d6626aa12ca9e937d1567b8d2012386c330702b05adf051a02ca2655a25beeb9d02279cb136f7ebfd3ac7d26a36b147f6a367ca5814d02b59820a87be55aa91a0989b9f241eeb31881215d645898930c6ae8200a5b57de27a99e309561169ea7d72be4a5002d5a74360610cb38826b711cc8aaa0bb0eff10ee75871f5dfe0ca57c0e9aef0da74b1c3e63de12af7f25b5c14de136969167b1a56395cbd22bc703f7cae71034344d6b69f4e567e92bafa08a4c308c570df80af584201819ca652bc3c8fd813bf94d4d5545ddb854a5ff94fc4978916131ee9ff44061a04ca923750fe52d53f9409f1df7bf44154aecefc8937049e736d95826f4d21f7ce92b8280e46d9e2eb1c7b8e7e62da55cbc00da4ffc99b438fffd03b21575972e0637ef8b6814669a1768f00356c54ca794f35144f0d00945a75a9b5f990d0b4011d08c6129c2ccb8da5e5d6bcc87139db86c979f4989f3bdf1a88db028065287404995e86646a187d4840972044047c084466ea09dcdf0ff0ed021bfc559caa1aa9a742b788cab643925b9d84bc0a1815e40362216fa26f98b80b725b83de442f45f55de27ab67e51bfe51eea806a014454e092c0634d253b06b8b5a4b8eab82bcb366671e107af79517cb0599a9d22cb3c116260801a3f77ba182f1d52a038675807b1dacda1b93698f21673d8519daa0fac0d7a2ad220dc4709911cdd884129838e8a72605a19e2641141b4ee780dd5c6d41bb4526a359003bd6a328e402ec2ec7bebec1036afd8233571a29fb024a2aa06a41309d64e0468641acfc7cf4f26238923c5a3c5bccdd1bc942aef0d771ed89c360021dde9bf9646b0bbae635713a79ae6aa2d2dcf004628c71de9cd29317b42d2a7062f369f2f78a110476c28a792213b4f52344d30efb6b811272cf1045ffd954d353f801f18bc69d74a313ead3bb4164a0b026e5ac24b90dbdeeffa3efd9c713ed7619354c75a92544287f2f17ec3f742fd9d21adcde9b2958256feba321ef284a55a8670daf76bb5f0e428de5dd86454f95d7906128c605944dcfdec3bf7df2689e39c5eb527705cbbdca88aaaa0c38902244c3d0f279a1d617a4e46071bee1c2db46dd355a2815617bfaa249907d0938c883832797b2b166b127792ff8558785c5b5dc2e11b7e5ef096cf53ca332ff41285112bb5542c5110f6e9cfafd058f3768eb49cd2adaa48af00ce94a6e60e83a825610469e7d32c700020819273be9e4bb67c1ff6f10fe74df895c5cac317b633b1fa7db34c89ca20dfb94d896b4b67c8c8c50c1409b6de95d461632c017b0883b628c2bf7060f5fe1db9585332be19ddda8e6e2bd982a6d8d3805bdb588ab7a8579f8160dc97550237ab5e6290d2ba669a2c8533500beac93473fd69f014b07850db3ba28f60a60d633e836a980f1e3e4620ed0744217c45955877591d74c7d01774b463ebd376185f428db325c6ab98e068a02a588d6c9dcdabf825c111e6c4e74bef201fa945c4e6121320f06edb2f9b89eaee32e2a84cfdb1adb2ae31ed174670863541c9abb3769d647b150673194a318ef9a98e1fb5ff3b722bc600aa9b0af1c9cd3ba11acfbe877e0e319828cc57c4df0c5e7a1244e6cf2a8abdf1b7403e1708d653f15c05aeabfdbc0054b150a3d8592fd28a71fd747dd54801a99865203b37def102a066fa054e6dfd7f27573ca5c69471e4e38d328a462c04adf9bc05eed4af96ee62831efb6d151f3131e1467228127229e53126b868edcdc1ac57b49be96cc15b89e38b6578087d5c6c8c2c683a5903b73c24ece7f7ff3c9eeb1bb42839ff61fc8c507e061fcaaf5d3e6733bb6fe1ccf1c617749b2566bb707466c4a0f193a62401cba6c53b955a9609cf849488ed7cceb97401b00d65e87ab5862f5af32079f458b256db9ca24d574c813948d5ee48a73e0cea24f26b848020d4c947944223acf797cd1cc88e864bc45fbb7c265e0a0e11435ac887bf7d37d77aeeb84a6507562cd89f8d8632f16318a380c6da6902ab903c662bc57259aef10294caf707e9d568d029fdc4beca267324f4790ef95744e005fb6efd62ff145df34dc5e0a78cd01a797a36f6e80398dd5be66481a6e49f0681cd43f526d183aa30690740355bc9356d2a17f0b2de0175090d8800957058a13cf027b8ff016bdc3df76ba19a64619e9c0388504d578f370f10afd8e8a2689db29f6525608d475c79982dcaffd96349bac8183c4d3ecb169023876e928594db08995ca6b5853729e3cbf7e13393852085d4a8ccf4b33f64ab51569b3184451316339dfcf60b1953bdb4cfc12c9ed8163e0a9da3d40751c07cf4ace5f3a19f6e60afb3127daa5b0dbfe64d24d2668756121f8191eed60a8c1b136f7f52e63aaea5edbcfab394126fce530cd87b5fa56fa42d35bffa3556bf91eaf041bbf38d7c5ad31153ba6ec2d92aca8bb781e9c94ff6d5b3b234aea5210d4d8225f4211741a0824adf574857c620d9c28a949d5e94cd79f92d72ddf455b2ae0630cccdf7630f7e9f4f350954323c3d2722d27487b807c0abbfb8ab6baee129b1585f9de68e88b9b808968f92b871d1e9237e3a8701ac7d8d5cdc695cf7352236763df4449cb9a39aa467559ffe1070269dde01d7252e771e821f7d8283e7d1ad332aa2f9abc29ae813a313c3c804ecf9ec0014a557194632148f4de2644458c3abd0e37c97c3b11ab2a2a3257ee30a71d0c0e555477aa08e78157acb2e9ef2ca309351e543fe06807db71abdcff0329c2245f749dce29901b582c7df6438f1c24fa66f524182ae96b8de7ef128fba55f1a71f41f3772dff136857254d07d0aa52385a8f84c3bb5655c47f79da1a0a3e1db208bafc3b97ca16d0fbf54d17e3d3555f96a5208191d83584a09f37c7a4a56bc15d1d5fa9305a50989d5d901c3d721e7f3cfcf24cb64102d31a318b1aadcc0436ffc98da10da32eb46474d8a2bb941f0adce0d7abde2ac8d138d422385352163621a49c9047d2299b1c69b3faa0bf61450e199a0d670741704041c9e2e8942746e2396135d0c2013865ca60aac6006839a9561ec12cea979a5a307811a51cd217e1f95f93729e1cb32cf2eabf1639cde6f58f8fe76ce372fb49fe62eb322faf5cf9e3fc8c84bff502d1faef9e228b9799671721d05e71e54d4db659455d5d58f24675631caedf8597a15a2e45bb559df6ec7a2e28b6d13738f4249883adbff6fafdc1c0e82c474f2b2347eac8e5bc13d85b0a46f18350fcdef6e96f1a66e30215b16c1b706073da62d4bcf6066f7982e3573f04ca0d76827a3cab2a874fa77ba0da88465b55e4dd9ddbb0e2c521ec60a18c7dbf4d8bc8d58c36807098fcc058dcf56f6439ccb70c26a5e33f1d84608e03239459753d9a5fd980c8dc6f6b47c4887dc50520509dc0acbaa603a3195be497a79b43a5e2a549e6c59ad26c9010e0e69f0ba5d9e6b0328cdc9b392850c28569ad8746f067c6f757a1baa038b08e2cdc2fa8c5ca4402677fc5c4a5c10d4e85175951a9bf7e94b5cbe9c5f446c12b6fa4066dec3ad340dba5b73d5db2f04e6312dd379b99277066be7262bc8f4d5889a09374d4d3c775d843ea8b3ae4012e620e72751737d10d72d46126c9a90becff6146a447d2cd3b72e46a289aaa81e9ed61ac3d60f4988ea157f97c8535102359daad76507051514c72e989a023d12c4f746d2881cf18485d7ef26e4243fa8d095a887f8e8c18522fc48256b6c2e0856526c8a03a63ebc6fae8f2e638c93aeae786755976b1348566e9273a4bad672c5d8d6b3230a2c244e0cb6864440fed3ab89f66d37187141bf4d9916816511f867728532199eefeb090a7f56a0604979da9eb6eb11f5eda00fd76919370caee86655d8752b1ee96f654d9449eb86f66ac50fa6a4b86721f35c86228c3d90a77ab4c619efcf011f053cebf757f28ea5eae1b7414eb17592ec018890d85ee2da6fd133b1dc4aa095b023673cbaf1ddf940d2df100272209a32c33b1cc21949a316da9e63154db7174533fdce79cb7aa141bfcdf016134161af240930ee5c1d98ad16995d38fd04aec670528be3179dc72a770074d794a8669fcecccf232b95576f7b9374581f8730c29f90698947f28686cb24c15c3619a8a9c1b30e4e9e9b2d31548841e8e5964afec7bffe05366290b2cd94bf1386bc06d85327eecfbe70754bc97e061e070e49a960355047f340f1ed090a29c137e520ce1642a5f1b94eea082deef5b491c8cc86f4b205500fc33a912f1ce873f82354274d8e69f37c560403cf0dcbbb3232995362fd418eaa25b1bc5915fcffe4a924939e182901418f52b0cee40165a1e97d7e64fa51329699ea5f75aeefb766425829cbb6f386b0a5978d26f5177474a949fb4be41c5c4e5b2776e220f5cc4d8b0ce23967aaf95d74bbc3e74abcc0b40bda78e7cf59c590532a0a38cacab2836c9362b9791679fbbb666549fe2c156687516eeef3c6c465316ec2eb76e206deac2c3c299d1568943d6ee9973cf234d93f6b4b5dc1743c869ea0c45dcad6657fd295864a773a1a8a09b9db61d2d04f9e7a09c5d579f1d1789bea4620d844c77f66f49c2536b89f247c204ad4fe4c90d4e49f436612d1c455538577e86b2743ec032ba5898aecc70bd8461e34c8caa6b43f2831879ee70fec55d674b1a4f58180cd73dc82db0a0fc16cdd85405515c0a1c0bcfb0c44eebed38b3fc8f1c657a6de16aa7258e58b670841494ed47a3b9c55e37964a0a6e02b41fe26ea38ae3877989a50ad83c44631c50fd3934272630da357aafc3c0775fa55e62c4f5652320ee9adb435881ce52b09dd1b7efc2311f73c3812c575070c26a6cb0dc8c93d806e90fa58d53d4274a408b19f3db6038b05c82683410eb0e8bf06c02050131e226c86fe89f93def15ebb4a3a4d6b27b968517560ab22a1915798d482bd485bf8dd0016dfb2cc0db54e45396ef81e7cc5fea341a83a609ce9cc36642220765b6bb34c111e8a153ce70e89b67576e029b1d8a1c422ad24031210f432505588a117cca017ca8aedd7ec7e596e557203369e2001489256c0f23a5702909fa3c1f0242203ffa8df1710ba426c857babc9d0b44def7aa64aa2b3ae8c8192c22c2ec8860e2367d3092b1d573c35dc9eff49dcbf81ba28edfff8e5753c342e76f054290b1285add577b9c31d9d1bac32b5153d26b85245bcb0ce39b2df9ea931c25240d3c45a1f740033291119d9d743fef667d0e364829503dea2a20be61164e2b5dc13332c480642d3cfebf2c6d4576cdf102fad3d194659874c6ba652297d038e6b56528150eb868d44a981adfc3bcdfc402e13eb623e1a8aab5c0f1c4a222cc25fcfa5beb2432f5d170aa7283f89c2ec26ab678cbac5a19a3e635506cdb89c176c5e242739ffa3adc30b04f2466b15e45457f812d789ec214557dad9ecb4c784bb879264e8c983308a53425c63c84b20e8411779fd37a717e38c925aeebf9139980b8fb25e9b03900945b00eb9ce8c484c732fe3fa8ba45df6c0a1e585c6af643c4f5ed2eb7c8fa87d102e30486476fb729cf7669b0c4840b70de13a74ef7a96ec26accc22d71fc37b26a5f0a8b38a902cd133a1a7dd2922077bc695897c387fc39f72a7477a75464f15a0e6ea264b8fcc14adbdb45d1d55b05459c55ebef11ce2692faa1c6bea487b474509d69bc61192e5743945e10247c40f5411b3c436d341d5f4a0399c721a9cc48c4eac8cb6d4301a1e6f554986b7d34d94045bbd01b8ee63049ca2e3867e079530179b68605a069f5399530f13160513b549f938d47c970a258f82aa5ec73678dfacfc2ca0ff2651229e2d2c4ba7e861d36d8cd45ee1d268902bf08ccdc8260c6d58022112a37caa6e8c3961404a077266fd4a09871e81407a2304c144c0dfd7634d90eb24856b644aebf2f5e2800f17cebfe3a5fbf5f6f36972319b409f59766b1ff9fddc7deca32e4b58652cd410f859c415b8686ac0caecb4fa0a1eccdab11dc92a3cb74669decf2591077a11487448a5f4a69c138329a430b9d85c03406257b7338849fd5623e0caf7bc4f3870ac3f4a80a7b20219fcba3ac3cf5809841ea4a8a617336d0739b99ab3a0c57dd07d8f5971bd722c69080cb39c2ae43c8ea2e00031e74e1d6a20a85fe941f2250bb13ae4b63eabfa9e5e6879ff96e2f99b44209715fd6d1293e88c4ddf7117aae4a0448e341761d8b810aef722f9b490571bf15347699efe2da466f8bde82d6bd7c20ca5f871e439847642145d5c275b91909fdd94f554ccb5d47d7887613c42e14871b8511f8c726ea9c3ee6cc88b1552535ff7fc0b6c068f33bb86ca15a185350f4269d178ed6796f0a330fd52f5922a35189df15407f828a99c05a7b2e47fadbf690d31ce8a97277050620884a52c8030b5300d1f507174bad5eb038e225f779834096ed0174f4dfe90786a1d57990069c8af54f8f22c240cdc8f8d7f06c89effd09db78861a91b13f78494272bd30bed0a809bd8177032102d0d8df69054e26e8fddb6902990974e673ea815041e8f6edf481922034f3e11481d219f4d6cf69074247d0ae1fc2266eab0074fadfa9033992410beec2feeab208a93c54aa95fdd707e4685cc87a9067e01d81241ba6138aacc0d73134d6479c98c84037a6d72e1080e1abb12a450be9123b624a6e0363faa6649bf10ef4dfac99248a4684f46d96cca50a47a15c5e449a44bc581c5b54e9c80aeed1cb2339b845fde8948c9289948c668482c2fecf04a5cd75279a2ee5fd2b0eebdd0385e16d23ff1ffb0b7a06c4b6b22a3c93cdce7db044eae9f716c1632cb9a322104ce625a38033422e4c7418051eb75b4c5c48d0e30ca7489d9088a1220a088f30191a50a12870a704e0982b4e893d82474f506c215f355840e913b2fea74260ac22ba4282a0be159b99fc3a3946c0ab8b230aec5c7a1c425890b3597e48b3055960fc645b7b85cff875cc175f663945852265e008cac12a8185dc845924b37928f0a7bb281d008107fb705bff21e32430c1cca91380fd74f76f437892bf045e1707a13657de12879d708f5cd4a9163ffb23a0d1e796107cc180a0d9120ce99c8531d50c246c00e379a32d453c8d3bf7df833e4613204193abb602b64bfdbde935049702d541d3dfae5924777b9818d8ce55229212ef57903e7db33ecc3c32b9566abb86b6504149428936bc7464c9ea87a79300f48d1efd0f74483d0609e65183a3072539156ec8dc483f426fe426873213fa60a9901b1d4826347d4b81dfe8403cf1020f45e428f9ec991d872da231c3210ee120e417e9c0a422a78336a168a3804aa6887cef5fbce1f4a07c0db294c2561ff6e00a2875593299414a8bcfbc08808de01ad2eb25985dc5d438ad1834a81549546a994c6cae82f1dcc5146064c8987dbdf4b31cd3a0472ed82a1d0eaff8f66f6316a5490d82ef69e5f7a0d11d049044ca0ba5a61d38fe36382b7e7393abf9448e3ede1a0fb66f26aab2f8d2844f8eda63cee7c2f173c0d98603c168e4b9fbfdd905c5189c0b1478c9a0916d82b348f2d87111e4030a28547e1976c7c95a349272f53c061c968bc7d051a58dccdd0f958b747852888d758158aef88cabec45f1fa316d7b91adcd0e01b68f20c0a7ecca04e420f2c56a8044b225b3ba464df7467f4113d50118f6425aeb993e911be8fe0caac35a872907104c1ab9a05326f43842bbf4e5603c92b2a9118e19f01e85da549d7f08a742e1a3d6ab61b8fcde5592af3802561fb36dfc40d0c02a777392318422f804028d604b75649359a35bfb6702000e2ca82839db31dd1a0a9043b6e6b76d1c8403d0be39af5ce5fe3e735a14b036aae20f43d4d53e5cd592f243af0686bf139df098c1532250a7ad260cd20ed0c851357e0ab99361980f8648f1a3707eff8f5b3a30f81c103f9011224e438c3e1fce7603fcd31eeda568959db6937f2766f2366ed40b2e3efa2087f043341e20cba458c8fec0c8703c442314aa81c576717413337e4675ae70d7a97648b5ea60dce81cde2aac32e3ee85550dfc46c97c36e328779566ffd55fd49c0e97692ef33a559f1d47fac5baf193f37aa72b4b84ea74cdd59fddb062a070a8bea0d5770e8d67c8c4a8aa765155ed4bbe55a496bede2e1d572e693312b725aa7e4716c0db71fb048de676e4db1f2986d3f0229e26eb744aa1f80c93312e6243a1921b70815087545ecfdb551ae98ff45f266fd81e47b746c67bfbf05c94dc5d1e63090885d48e3d77e2850c35f79bf0947e56da0945ce6292ad97271ff537a74da630e8e507f76fe2d43bf2a1485482ba2769d27f9f802b9ec4d94fc98f795fc06655857475a36ada9861b876b7924c469f5760abb5f802ffec96455c86c67f642d7f09042f2ef869d4096e07ab81d8b3f1a0fb2c17596eee0c10411f9a2018c86e6cb60ba31f4c7256680c86db8ad85be2773444910879fcacb9c4deb4f5d6b90d5644663c5781f8b76b52a466f93dbfcb3d4ad5a99ceb109578240fda013a17ef4547f12e28a51af9e8f06423e96c1e5c542b0ac3b3aff4bdb65b0f9e36d3c0edb4e585cc3cce838bb46f3711ae3f669b9dd01e0d102b443219d2e4f805477bbd7c9d3c911abfb4eeee002a3526a6a6c2f3c8e1996c91858cc553f3783c1274ba9f5c6c0b1f7e6efa1816db278cedc981932b2cc34aedda18656a3699e0dd8f06901fae1d52aad8e84524e8db022ef03486c7de043f88bbcaeca541dff2b9bdb6a4f5061740a5a2004724618f723fe6b7656850debc01b6867654344730a907e3dd7379f71db34c788be54f72b66a003be127c39585ddce695fe07e2c9606df97e0eac37be11a9b417e2b58d38df3ff0c9b6e30d4f7329554a3cf3ffcd9eaa6fce8ab2088d12d7ac8ee4360801d04897166f91c18230e58f2b87005d27a97d0a16c27681cfb3546ac7f0baad39af0255cbee32ebd18af13a61ade223339639c5efbadc02729818eed49bc4c2405a53f119f45c9c4467dd09111e7338051c1ada00a901da7f367d3ffda227ea6f690fbd554cb818c337c15cc4c786af9c96392da1127d2f624002b560840b1f9a32b04fef6d3e5874a7659532116700faf867479a5e277027171d496e791043c7fb07752d9f29fbd65b5de4ae75bec535b6bdc78e6e0d097fcdad81d86b5867e8e9f24bf2aa228ded4c713358d4c1a96337dcb60ea6d32f48dffd08e782c43725c7772413d8dde737096cfa3c2865993ca6bb1bfc811154f46fcac932cbfb70725402023da363d1bb4b9aaf60157a8df6d32e88e2c0a8b46c3db8b3b918388d5f1e7155e317ece219d4f224510a3b548fead2a7990bf4150280bedf8496d652224234b953b3c460869d6212c24f50b33512ebb679934ed9e0492efdea5f62e63113d28a14f4973662af998eb833c59d678c2b6e1c965dba920ab19cfcbf2d893c388d531bfb0c5206027b3e8551c698e317c188d61b8c019c824bba3b969c91203e51d8c4a3d30d6ec5a4eb336cad003fabb555d5cfa4bbad85fcd6e35d30705768e69b775d1d5e75bb34add73167178bfaf0b01c050b2998036c3caad4f060992da2d624fc7e68fbf4aa3a3dcba15d4353228b5538425da164bfb8a2b0db4a741b9826bf8f8b3a08665b8b44c74bc9355244f3067258bb325b0c7104e172c610f53d11f577d20f31499adafe2c0e19c2e0a073a51910045bfd7296307d45975ef922db3ede32d169ea271da7a97dc4fc602a65d44759e6babff0c70164f722c916c56a560cdce0fc22c966df51c2b22810bf095ca6e8e20fcd043cfe4111c3123686dbaeb630a7306a32bb237975bc0bfc973d821fd191686a50c10f29e4bb02f6acc0d1b0361beac24724e3361f476139c05a8859d695ba242efb993ede216871679bd44cfd703f5b0fe1fe0e1da6bb159b107e68c36d257dc2f7e7fe6e5544e7364bd3298fb01cb016eb9dcb82e4e1749c0bce0203be7a4509996f51c137a301cc2890c689e52046483413366788585bd4e80fdc1e451d529cf6ffaf4b20e531feb894bc5153f13a0b909d95aa149d3e5b9726307abc631b2fd3c542e89760b09777c4304816fda9fcba501a99def94bbfcd2f17869f6735f2a188477d4543cde582f36832f6468277843f56ecb265ddfa9979e716dca1a4974292c8ed0acf71211027c34589875ac227979e76b80b7567c88c81b0538b246dfa2eb5cc7d780aa55634dfdb212c2870efec504199d46fd566c3881887ec01bfc180831918e8fbbd00f2c7695e810dc32f2b1db46f8a6d5117461ec2a1e9386e6046b2cc2d3a988279b3444316a0d561582309cb0b5596e69814b1c4ad5f1d7b2ae8f6c65bb092dff0a1f480c4ec266c87326e50567c116cab44a9e3f1c3fc934222df8e10cc3e52e19772d3d9ff18513086db3ee49bbba6bd5cc01164c2d07ee91101d8d611d7ffa8bb3fadf9675953143127bda477d2e4ac3fde92c8b232fc77d457ed0ddf4496acaca5ca6e825227c9e18714dde6d5de40da5ed8d16066fba17a522c3e4cddb25e5002bc37e91d7221811b727381180fdec061f67377cd785bf75a49700e62b6678f1557f92496f08aec1bd11730e08aad041cd757756f496381c0b4fceaecdf0b6fe63d8f8bb8935a8ac86e82f7feed88d71ebd6013ba34090c6148cecb193417003cd07de1b84dbabd33e5f95568f0394cb2623e74744b7ed63befb5fd7550bb09f49827119928117d1c877912ebc04f90c0fe48fe5e67568a96455d4b301a8b87023b14ab681150bb6d94415a5b58847f912a87a10d05dcf42ffb5a4612651b50dfa881ca3d583f8c7014fc4bb4e265c18f602e691d01a3d824a1d882995daa32b52bf288416199eb36e447ea8cf1a8a6ea47454bcf0e8ebb53c3952ebddf1d11af42aa8abb4e0de45ce52d7bd79f9a3490661289abb69943c3098478564bb25bd7bba459f0edc0f704cf980d0d67456c391ebc1f3db4da540cb8bd6abe48e3a8e7d66663ca525c33acad09b6125eb55e78db515fdd36397792fe8f1520472c80513656a276f5122f2468535a0c00d39527981643281e4ad9fc649a6183c0ab69a8e5e976f3354dfc390587c36502fd72386b48c79d02120de69df2d1de294d421c67bb500bd588fc82b23a0e8923bab5b7216974045a450675ce9ab029fd3ca93cae6e59257cfbb1408b5105ab43ca772b3fb715e9a0b3b6bd887e4df391e2161d2eca470fb89637d0446ddfa8e477839a9605ad8d3ab6500be81dfe491b720289688011dc9b047f54a01115433acaea18dc0e3165b4c9eaaeb485702d79817215a8fd3079527819e8f8f3101ea996409e26fba70d87304024d698a20c129920351160355fb244266da46bf8f32a3889d33ed8f8e88ba3467ba27ae7129edf6b659a2c7a8d7d191ae564cf0c4f1012215aa9bc06f1b7a498a999d5cd85ad0586a4de20214fa3517826ecef0e23810b6d4d93a887d30fbb9402f6d120112e89fcaf05a4a233171ae30b42086de134d38a5522f8265c883904bbe7a3c38cc67cbfed88e8c07b6ed4d714a28fcdf3ef5249a4b1ce40fc4d05e8f04ce18106ad4b3aebd255c9ba1918aa5a6471bfb3dbeaa12110d29472a3a9880c3f7a433692ad7d93babf67c604d5a073d4907c85140a7058399d2393553d389b478198a70771d6dd220b7ecfb6d0f7cfcc9cf8d2b3f3e4453d064634c38dd2bd04c2fccc998c378e0b6fc067463d03d1980bb50e3b0e91ee9f7f7d632c027d2bc9505a5e134f02c86a523e82cdead2de2f400e38eb27e0c4346bf6e61ac1b47277a7543302b6974a640edb7668e09335263b626eae3838e6b7cfcf93c0b1bb93934731826f591c9da3cd66f3e12107601e9f84e1421ca6bbf1789a6bc5126544ff5111a19f0b671795b83012550df7a6e81e8cf0211a51790f2cadc0af8f6e17030cc413a2dfc88f4115b01513cfea5890992605e0b559bbfa01d3a00d72ec07bb2b6d7cd13993187081c002891ef086307f102e8001e812d84381bf8483cfdc0c31c11ba2a7b80494deea08081e5c73962ba3e6d2552eddddc230b65fea57789252fb03c8ebc4ef4f554b0fb1371f835b36268c18aa5eaf6325f7cb995bc40db67dad16d9217e8665ec1b154494ea0cfe27486311b58514ad6987696ccd4add07490c1047bfaa824c32ccb8f92246783c1ef82db1955685b6b91b1f431b19d84343d3b3f04406eb7687c3b5237f0851ea73d5ae1b23a34998484cb90958be7758b8cf820e354b28b2fd31f580501a9e56b909371f902aa3ece5bab348dd4b8d5db7126b80478c33b6e357281530508128c42c737c1744ab7b565834c252aefd58a7021e1131710e609408fd68805335541b7e62970e2bcbc1366c3f7cc843ad6b911d3003e3bec9b59cd7256cad1e6d9c4f6cbb976dd5823eaf4761d3ab40c1611f732392a60618e8929f040b3cec13ad2ca5d1f7f49c164c8c182d25728ebcebbda42bf368c82a2b5677054b7fff7abca1d5f474364bf223d29b846eba729db18b211a5e9abe12be0edb35bfb4c50d9e1b0b41a0ba9b1304b661f98e21c02ca5f617ccccd4ff089aea70f80e5adfa9e4267d0f55129cbf98ec9a36ede54a6a7cfb02690e6e7721d6535e6438ad300a3453419f34ca11bab4330d2cb1b5523c109c2ac7fb4cd701321a972d6d16e15a377ef2de2fa3506bdad1aa4e788c41a370e2631df91349b56685b1c00fb43e2f77a66871fe1260785acd394d5523e2101163b49d95ad546a7fde5ecad364448d323995c6d2f8d50381638426587fb675e4a5034181b143f98ac4ce81be6880b60417085d90981f0d336f26aedf8df4c76297803dbfd605aa825fe0e193b3d208ac05e92b9ec890c45216531445579e07db2d5b085b71cdba1f8835eb8e31990c5c2759fb04583c736d1e8fdff28fa2ae43bb486f96cebe5266db8269e95b68ed4d35564fa4dfc29521b1cd1f4803a13550aaee7f36652eb5169d2400cd23bc1bc577c275065aa0e1492302c4d677707b50c6cf977a992eab4e7d190c1e9b087fcebbf87f7bc0b225e20c5b54fb41bb6971a4a21bd6b80f756a9ffe0404be35422a70659956b48d10c2ddd2c9148845af7f7488d03300aadc4e5841c06698c7e35469b4862bebde8b7c6bc71d7e8cdfa4d32635e88f1e799dc820a033941e2fd3fc6138dbb217f57a023bfe328ff3469b8ad648e832d59199d3120e1da4b23e36c2685e16cb073e6dcc77e5088f3173ff790471f7da000fd8659f57e8112664df5ad1df1ff1c0480647313b889cc08929f76043ef97831574f5c46aa98861fb6bc24918ae4bc341d74ca66b63858a383992a9434a4ad48e53c8b1f8d4a67f78c5989c23105cd2636538128086d5b00b7bd8675c99812f7f899ff9128e36f76d7f29fb85f52e0b9e407d1ced0421c3593b2400361e61ffb8a80a53e4fb85a293eb911612c8b9e44a356eee9910c915c716544f2d41d9e97980cb52eca94a9ff0068797889cc89bd4d5a91b03293789697a0a4667324ef4c081cafe1ea8557189c799d538e0c0a93fc69f117d76d5dfa18f9906a0cea0a9f1bd35a5ae95801dd95f8abdec913132156f4e842d315f45aa23d4b636f558ab594ff43a8d38c7594174ba74ca8e4a1644b5ea9e687f2ce78ffb85539216d53b36e788126cf4fc74af42fc5aef6a9a33e47ac12d5a494f8b1d0b92a11ada6bfe2ec5c9c0eacb09b517ca46533f7902bc139c8b843ca485429f084348885ca486cf0b9db968f223cad6cdd6b3698cf3ede3c72bec068d9990771a28c936e9365cf788e0d6e2d629d469f257895aa9f23576e58643ce6f97179084cf44205e916221489cf57944f99e63500ea72c8e53f03177ad775583e862803e0701b62a5c56acb2ae34d28e947166be7233f450b5bc555b5867f594c8052db6e94d8d46c2da3c1bf7da6c9080830fa5d2cc6bd652dad7881183db4a76922243f90c13f3beb28e10ea7e9d5806c937113fe5d18fa9258f45c1a12a1a7bfb1109a3b0938a7f38fdd91388295b71408229b4725e07dec481dfc2c7d9aafcb7efb98a353d0e5bdf2cd8a53256efbe682492cdf92721858ebf42adfcc17617241db700793a4c783b70655e68d33b9b7a89f17823cfa30f03b2c48baa93fee2994a3ee85d098b20a35319a5d215ea7ecdb7408fbbedf0c37a0ed227a9181bb8b3cf78db355e988831d084ef7eecd9d0e6afb2935b20e42d1f5218f0e9a7ef76ebcad48ec09ad30bc9cae7034a15f59e78d8ca5c88f211c77edb39c6e9d6deb66dd164317a7c39fa76eea44c333cc91455c8fae3ddf83a0c5dacf30b14ba5150213655347deb05c290681d7a18efc9399ea47ed7506e52e6bbce3bdbc94a110e40091c51aa641598eb2a4c6eaeae9597d08860ed754adca74077ba0fb80170e6e07068aee1607b294a86178faa91ad32e3e1d7c7f97f36c8f7ef7dc0c1a31935cbbf0f518f05b5a8070cea244c64ed71803b3af4e30842191d3d66c74be4cf6af66565cb2a6946b23f82b2ada7b0a46bbb75f9fb294ace030cdc80a1865eab26650b101fddaa944802c75560b40d7d06377205a45098f34098fd4cc177106c29fa68ccbb4d9c65b258d4b02d37a5161aaaf3fed3b02ec3fd34edf59df480c5ad54690d0b85c61add9734f57a5bc02ce2888d333cc7cfc1b3cb180d9f31d295af135f4a22dfa826b8c03c541c9a541da7a7272404a06ba257cb425161a9cf10ac7ab887d070cc8177ab796552a9cd115ae4dfad52491189f5cfad25afd02e9045e68924d0c6919c7298f02131d54e6a1e747f4c77d1904f72d27e1e4c217275aa3a210542c918cac7663cd33a971cadb0ce13a943f47b8019a4081a9f70e6599eb3942b6abdf57feab84bf75782c22de1581969c42329b476dbcbc05fe95ea44ea15a9adf15d9baa2d1a82f9845c26b58f6402d4041f3c90d39ee8a8d7cde1a3ea38eab5907ab1542e8c20fd55540c15e00546244f58b14afcd995a225716154709da95db5060e8f344c4dcd4ea9e78bd271490d917dbeee9d52cc7cd48bd74a68e6ab6b83547d9e898ea1c0ea61b5882b491ca60495ffc5765a0fe0c16d296b3949383637e020b43f881ddab980f5daf346f59cc88508ed4c4d22c9f6b8537d91641ddbe36dccd7843022fc37b4a234f8be5f7b2da84e21424d40b91a815a429a538442402dbeffa5964323b7bb4debb3fcb528136a0c27dfa039e0d020e079bad1184e53f8e9092897b302e19469f87431bf775f8b0470b9ef55deb81b764cbada25dc0c51b0c78366fad9e0b72fb3f0186bbf12771b755c24c06d914d3b51f63b10643dc7390ec88a157fae7c101e434a58e1c78dee454e59b49d6c85bd667adaaeaa9d9960567803ffc8af515c36995bdb6f4d9ef9404212df076d67f939d0e7045ecfdcf953e02aa82b909d1a3789145ab96a68e12194c651073a191d981b55b106bac3294076221b780c197d03c1c71ae1cb92f4aa4a27127d4a7332f76516c403069ed0a8e91c202cea7728b39c5d7918f9606fc401fb8be7c6bcb8977273ea288ffc8a560d874f4c782a199da01a8debba4fdd2b7638b7cd2ef0f0d8bcccfeeafb8f9d42abeac75557cf21c7ca8b9267b296a00615aefd87592ec465154c3ee04cf649bb33582f3484670daeb2282f1c5064fcdfa89a7641cfce127bfeca48211200fbea2ce05fb762da48efe24e48f89789f328cbef3765f0c2bb9c67d7a48785f1d98cdbdfa94ce8229f2c8ae7fa24fb8c508cdbc167df2df618cf279fa3e883a8684f2f404e16131909bf1bc831568c22a965a320d637640d24d98cecbb4ed9b1d0931e8260220ecaee98c82b9bfc0b9c2f1b28e1d9ef8959787b10dbf4dbd640888a0708d854544afd7dbdafe328bddde23878c64dffb1c876765b45919c204e19c8b5bec3d87aa40a43ff2c1928b183655e2ecb4877feba0252ac3ef2b68a4f20a159303e27baf291d6d78e8750c004ba6fb95bdcb9e416455ae07424a377995488a7ac4054ef007e5b8288e726ede4189c643c6e4a19696fe4210212d742106cca9c3387d312869c2bf3bb2671be4a7ece8a61e14cbdad0d271b86cda649025b143aecaa0a0d5720104b884cf6efe5fa988a783a8b4d17025ec124a2877ddc6dfcfe0c9831b396aa9e77c1623f9958c11ec6b33db46e8f8825a60d839bc27ded4afa114073fc62833cf6a00501785017315b7bfbf47202754150f1ab4275783ec129d9430d45e6882d1e1d5e10e49bc0248208ccef36a6de5ec201fba78f3704c990f378ca03ef9159d444c872bac51681f955c9a45471c70c48388d2f1e8e6f2125a8c21e91e6b4382fabadbfb7fac89d1a3cb15450d4708bcd3f55795d52243ea37c0df189c3e12ca371aa430e3ec865d3c3c0b810ff331aa1d2ae555bb57d462c4c55d6abd6e3021420919673d03150ebe7ece55b97cf8eea1364c7c7e10e2f5014bf5fddf420cdceb77c1acf843e11d275368ac9101efbb2ae1f4097186f5b433b0872d3b1441dc8e29b538c461f32df766f4052afa47efd22c846e99e12a6365bc9371832eb5962128de997e9b822fe89180ade23c7ffa5847fdcd2f7d4449dd7dce7a4d79f7f7e1099f274de86186c47d4219d5fb7ca3dbfc784b36830e805843f61035ca0f86d7cc6d8ab214f239469c75654f93a5f912417f9a770dfdc8943da33c86278b20e52a2072ec4e4fb2e568ce2d86bc4c095bfa7a974b85bd2288437bc05d6e20a430b57f55af644737d33669adf8b6c3b075feb412dd7a30074b6ff6e9485f833bdfa4de3b6137e9cea1dcc6d1b3e40b53fd399fd4848e0aab34f442f2a117dd391409aad8d89aae2e4f3abbaa7c003ea3d2a9347c38386d32402f08bf92518910d92c179f9f92376f3ae60eb54e3930c9cd423c2c510d0de1da187df2b29ec6b998185dcc9aa88c6e95ef35741020b505a616e5c166003cd7017c2c4921389530f2eced242f9cabc1efc2394292ddef93fe106dee087af538c779bc209722027adad0dc6f66a0239df26ec3fbd56101844333af6919fde9710e7f83d50291768ff0b9949744f49c54d5ca92c82128eea0a618986da47015b0cd5a425c23909863ec9829336ef44e0941d918d1319c48fe6ebb5ee8f1370337485a6e8a6e4ed499a263223686cc0d9406d99d0ac5e46490a8dd2da1799ad32774d1b1a6f5bedbf1210d424b260fe8215116e25e364ad8079be564d14638a512a586f5eb91b61225b791051993a30081a30d014ba9d024a7a0aecb7b6cca4c10b026539c8d0c7960515dec52fd5d3c3ba9a0f8184141bd51fce13de2c85e945ad0df71ad49079f6708fc13c9c4116f98846f9289d8707e227455c7084c502f834c5050c0ffd704b366d409468bf2ee9f4818a25ffb5a6400aad4e96da016cd199a2efb145e1878f9aed01e53f79131c912d3a1a9bd81270c6b70acca62007c822c2d93373fbcdf4cf92cd7fc08f1c317e1d3ecf811410a2223e731af1a173121f0296f810dfa63d367f885630dd61521ee7154a907903cb329899980e924ca1284b0cd5ec1148c9d82c097c0be6cb5a621b19a294b88bfebef424a5400599b05c498e11ec40ed20a66055bf08c223ac1287f5a15cca138a37f62451d1444c40708040097620335057f62414c5090205da0e9e070aefa9e78b1f302aaeca279b7fad49472a93af251990333d58a6c1f071310e61837f0b75d438d2a77c1ee5ab6c217e50ef2fb8846a148dda441e1cde3d54faefb436585d2eb4585804b83a42f5830d78236f35ad3b493565f203dd2f977b228c26f3f1302d8769346181b31fc8898961d481fee259cbdc4148315319d383329934a64e579f5e12b8e50e95da333fe89cb12f48d08d558c78068dbc2762379539131e03488e1c8926ca4e86ac9948847584de883f84b2226ed2722925e71ea39867d16fcc5c7e6fd0da34ae9e496d146d20cb8fbca2e4fa5d69649aec827931cb281678f132d338ac0e1cc400f1bfea636066551a6ba334c9205f8acfd10f60a0e90298197d9adc8c8ecfb0d9bb4357fe529dd7b70572bf2dcd35c9487c18e38f426765775c71b59b85737d04c5993dbc763955f27013154483a5128aceb882b749f9e58d5f35203607fdd19aa0f616c0a6fea88c2731881ccbacdf976d862d26c69b132f24ffb5a2817a946f6a319e4c4b5f21418e255b55a38c6263dbb33b14431532ef3542f028c52ce039cad31e6e7e3a8f02b0b6ac1e098cbb01ce5765d45ce7a3ad9a8bd68e50eb8e4aa3b2e0f6add65e0dbbb74f4d25605bc358f807a5bf3a16dbd69d2b770764a013c88141f088ae3be9fb6f542c078fca6d17e44d0f084241f0ba9315fce847fd313d3a9bb1b1ff00c31683866a17893b423af691f5d9c2d7b6894e33c3ca372d18cfbdbf66ae3f68fed900cc5115248c827fd5a4bcf16edb95236a9b66659f6b9f989d148d584a6a45c85d5cd4cafc55c69da9349d83ea6111a33b1f0017277c0a7fad79dd112ba065225b1d128e60fcf78588e604f072af1b046fa184dd994874d3ce96a00dc4d03ea95411d2b8a6d12ac60be2988527ac10351165678e0fd644c25014ef43a4d2ef5cb5813006e92081b85c7aef0a824cd652907ca6b6575c4380409bb61cd363e86523405c354e114313685897e38063ab30d0d0d1ede82af028b718adc5ac08b948c787308052a9b21a77ed6bbb8694dfe2c42208ecc285751bfa643870ba4dd5a6e1a58ad516848d685d841d892484826a5e54d4515f70b539fe00e63bf9ac05dac05d8e10b3dcb29244c1be4a88d526c3b50496fad3ae7c29f85e64c19bc4426b63dbe37445e33c3cd404290b3b449cd51ee2313479a5d5a17700f44084278a8324b8b08e96a3124a42a820fc8afc06a2fb08f9d6003cb8cbe709b0408c81dd3fdb502d3aeb8245d46ace98eefa89e784053a505edae3ccc7607947bbb43523ce0156d1e30ed4dec329e8d8fb9d66ed77d15e8ae3a0906b60894d9d8d6bd8051c7fe838cd81c286f614eb191d5bde825d8428a6a5d929d6083170a86e56cc9d52aec7ea3e85ee504b2cabaad56fbbb4d686810f210efb646c0ad35a535cd6843f3c53a9644d6c8a3c8fa19e1c25d9d805393a7c34d4ec5592918bffc4cec332735dff9e92c7b329e7a4148eaf0768b86fe8731b159e370787790c8b6820f906450459ae58413b7263c6824b9325ad7b29564ce72c2a034341cfde284c44c987ef680827f95c46a6f1e1e4a4a8fec02c40ae81a032986051a411c8331c44d29d8f20e77ab0de5d2f6f244ff5b624f2568f01455f0c9120a53a699335951892a9c7e4a578be54c2c109f3218dcee37250ab55bbaf9932e2a1fceca2dcb2e48142cf65fe01ce281e2ae43b72da33d46edee8d903a78049fbf21a4f20808986e2ed83684e2063cf0838fa18a446ce93e118e4d818662a2db4e14a8eb071567ae607d2e3106932a4e2de96fe5e6af6088cf9b575d881eaf4b8c420419aeccf2ce3da7b0851427ad58cee20d0c89c271c309b72609d0f348d87ed494541bfea3d5313548525f3e322c796bf5765edd0a24e0711f74fb4e17c7b3a56d63162d5f34889086ba8e4d906c7a2b7a9b0fee6fb8bc4ea1c0217e4ece5fa15edb7e2ef4cd21316ed38cf1eaf67d47577b6dccf9d07a9e6b89522704da0f46c335ac17fc0ed2db35b5aa2069449323b194888d9c3b76dbaadc7440db31a3a5ee1fa6f1e329d2b8dfa6788503d1cc6111a0b94e47dc2df48ecb69fea62f19880f79b8b720d7152ca329cbc420321076977dc9c6573c2b0f977c6fad268702075be1b81fce4dadf5d0c9144cb4211a5429c5b81ea9a676a6c84a7d80d4b07e501b93848d61f7b61a591a86ed71cb9690c9b8045485da2a45e8e009d43155257083237bc9153a390809664cb9a37d7f24b410528e0a4c942149323f621060cef00fa36ed9170191236400cc03d4c598e52e1b0dce250c63d28af8c948555b678aad551ea320ad938318825787feaa542022892da5f867b58d7c5107f2f8567c3441435dde57ea73b67345c06b68b57d1f8d18dbf66130961101ed85f64948d2dac3459ff92c8378136d4943400d7a933f6985065dc0165eb643bd8e94aee6d43c46a9cac8909709fad89887f68496742e1ec97d166102849d8a04d749722103ad1e831f09f4332ed04a01da6ee6703402c81890842c74a842abfff615d5bfce003cca0f6091c4cb27deb7347aceb6ba5f3ccffba680565b1623b1eddb72db2ff2ae0a2a29cd7cab4f5580374d0a8d6b39c12a4f1c61fdb48936a05422a8b65eb4f03cad8f4340d13c98a0d9e86234513b4352abf167e270214c127717753e6f073b9e69bf8b2fd4e05b83d4e1000b839282c9db91266f2fd5c9a314e7fd4346d70569600070732522c58ee074c3a46eb66e032b2482ceb8906c7d45fec95b7a29f7816897d13fdd21be52227df222db0824c5b242e0fb47925c4edf86f3b7ebbea21e948d7a3bab696f698c4b90ffb8bcaa5ca6acafe1799f1e86a5a3d584268db7cfdbc9692d8a8d512643fc24dea5e2ff77405c98b91c6f5543517c3f4de47632e7bcc6e2ae4e36e66939e67d08039f77ae68d1ba7c3620e3680e97421ec0f95524b3d2b2556b2ee966778bcd3a5421208343d97fe38f7c954832a298ab623af884e05960724d192a862ddf0f851b0975fe117111c77328bf93962c9d2af05929fb8950b28ed22679796940c9220125ede1d846563e28e4b7e01d960d10583dcbb4ae6c4dc5af1b5f5d45edbca2efe38aa96df4aa164c6c70434e9e33d0c4a0e64bb16cf2bd7ebd0dd73ed6289a420f9e46b5145a9f63e8dffb9af89f862fff25f0f327c531f3513d9ca424a8690c411cde461e78b164680691bbafc4151324571ec7cf0e659547674162ed44921a56891a4629539517010fe2f1decaea9474b2706f856899afec3736655d77086b47b021d14c1675e0f370d44e2e638271346ffe0f251953d16dd1cd3c92480c1f62574782bdf3b19a7c13de8b38a800f6df44388983be32d6655bd6f08ebbe670b69a77b329ec0a249160a49b65522692199f07b4a50deaf34b747bacfdc7f45b15723cb7389c09dc442389730ef3b242cc7c795196ff50862561be4a5e42f3911b793fa49297edd11cc668eaf5d7cd2e3d8794544c883ab04c116e824b4ebae6c8e1e07021b98590cd1437f8b52912cddfd60ad7192e5bfcf4593166e71b93b3fab53dfcf028b3f8cd42c9daaf8114eb3a8e582e6c8d57cbc7eab1fd553129d2311dc83cf247e44a3ca5df62db4f25c6c497fa193b7355a4caf53308115f6a7f686b980ef60f79368570ae577d7a017d8d15ed005d95d9821d4409e45956c933a32f0254ab18d192bdd45bb68afa239590e0804ea30b3298b89c4a7d7dbea11bb83aa827589d0ab1303f210ed2cf876995968500a45fb34d86c8bd4230cadd345b88e5a171a0637a9058530fbb69b8e55ecbdaa2c15cdd380444005e7e633582847f94faefa49647114c0972e17f5dd7de7c08f1764e5f75eac3c5a8619e8b5c7dd43b78efd8a087051d3b846e853b266a0ce9dd61467d0c5753d16006c13735f58c0a9878e1a3d60be0a8154db1610332eb684e4917a40340d35683d0a9cfd37c62bbb1973d11611f2ad304cd5c031503c9d9f5187c0e6a6f3189b3a4cf190a52935cff9aa469001528e2910c10c21feb833335ea61ab5c4b4b1e9d9edd80644662e5d8826ef173a1b17be10f77444e2961b5024f7e0fce9e73a0631578b3c3d0115089d8220c200b147f97b492b2650943037e87afc1fdb51b5d3dd75f9c90a5fc79e6e3099da25d621242ba8f3b73831e84f48d644255d82b7bc58b713aac7f5d44e011bc4b2106792ddee70958697737bf95524c0a0ff407c1e6b0be0e25ce8e67094585a2b08ed35f28cc2ccb80978c8de63e16c50f20d9de140e01f7a070c0de6dab07f00fda3b960c2810dd25117aea9b7cdc0e76f711febe7c2dc52a5c650860caf8d4c4e06c7e950d8af29a6005ddf501e7fc3fd2576f1e1843d31883b67a9e0337d5ed5119fa8ea88127416ecd50da32cdeb9e228cd64f784e5d6505f4fa255b05a65c69b59bf178983905444d2facf856bbfbf7d1f937dcf8fe8f060ad20e042e501f9390c05030d439aa9dae4ace874076f2e9f038c9757a3bea1782834dd6d6c1d3e4ea955867fe802b75b758f3a93f8dc675fd92a63a1cfbf573ed763f9c9eec2ea4a9a624821cf9c7945cf0e465148235709ba03b1b9384ac6b5f7b2084e289477d2b2f22108103b4002a62ae538d4dde78a6e58aad2f04d5aac7269d05f7515dd95c72ab56710839c72473a308ed1fcfb9dcd217e075eb8465649ef74be13dcbbb06be57b4f50f85f3dc8bff37ce9076eb9b53fb12a7d149ded86f5cda69d2de30f90321600626fdd3f248a05c4ac479325824b20309a39a7319a1a8ef6c0060cf3ebabc54a9c6f28d51a890acfb449c383b388c35daa1ba42e3ff9589abaca85d76d105585f4715b3a073b4405a5235103041d2588a4cbdece0f8c243a1f00acce22eaf9f2b22c89ca119f19c96664733510a35981a492f49951236e55f3851a82afe4f07a896a9826b505e0d55c81383e875b5eb2c9b1888a547c79449bc6869538051bb6af12679e3b2a86813f24fb7d15019254962d6556de4b431b064051b355bf7d0bf77e5c28b9ef188ef7fe729a5d86fd040caf43c7909a6bfd5e22231d0dcf78c2eb876fb076dcfd5cca83efccdbd58e46aa014098a5566d54a1948f37e79c4f58f2058a8eb07da2d0b93859a17f2200eb6c41eb0edeeffa14d53868a961678a301634a5691613104fa9644d5b49217ba7b67a0e21ccc37e7b52ff0c6a3300ac508502789793c8fc68d1994f3a738403589ada8f4c738fc1e83380f1ad53852a74c94060982075f4f53a1eed52286170c7a7a000c719456efbe02ab8f3ca3de73665d854298e3d67df5e461ff38c0fd64e016b7f95ce1a5a612ec2f038f9fef91ea3d39311b8a4252e9b1bbd570003d238ac6b5c30bf368234d88fbb415b28a15ad4a3a2a001799b9554988dd81f8557357538e03b5f1654acf5072a9e11cc67846466343e08c70699d5a48569b0097d54d385e96f08f40d10aef800a135c3fe869c20eaf2e26a191f30d4a93529b324358ae2f7bca1bfa3938453f9558228e3ed8404bd4e2840a76bd70252b78c4006057a8e803d1f36c6602964584fe12f0df32be838e2929653ebfe06771351716243d6c673e911205ec3963d3604aa9e470ff36e0022abb987c63daca10d74275382b177f5b37df847ec7ff00bd314ba045f271a0d66dd6109ace768cc82dc74e51ef7917529cebf283f4f2268c5e827eba2f2462e0915bd168ea2b83049ede1ec16a01d2455d35f3841c952146e93864fdac4e13bd59d1fcb0a89f2dce414ce5fcd51797f93f0c4ef377e4fbbf7026053244fe6a4166c811c39a1abf7e83ca528d82e7786ff00b06ce7307c342fbb07259edf39ce2ddee03259e8edae98b2b51e6dff1f5eb5c4a6b1d37b940d1b9342c2f8e7a426661ae82949055b90a417914eeb95d8a2161c1f9b672b7b2bba7a0e8c4b20f270eed8a1bb056311c541857b70d0dc1d0c9aaeff785e939ec306a95ffbca6a066d70ff888585d7ad1236fc3973909c286106938f3dce9aeadc8b13dcafbbe48685c09424ef352db483e233fc391ad00f5ec820e4d9e0d1674551aafd6bdee59d25f7b9d393863b0f612aafab979754d9aabb5d1b009cdbfa6f8e7fe984e8e1fc695800d1d105765b253035f93eba6bee8f4f9fcfc6786168e607d6425eb751cde3c635d8c52dbd3a4cd8b9d54328e0d0ec1b309fc5c2b9e05fa6904b4faf9bac72189a7bf3e17a9815d63ce8e1425d70d3ccbea47b7d7961325e536488985d297ae4adf84552468e812b866252d58f455bb2b142a048ae7962cfd8a464742b036bbd040bb9d8b3d6c653d3182cc268d07470989f6d5e36fbf06d13686b83accf3222761959c36b1461564c18ff7859322fec2faf69923feafe973ee3dcca8d797896d1f872e734e9133b003025ca09f77ab16225f7630af111a24af7e20c2862580982b4f2e37a4ce387d71abc3a488e02864b18f047c11a46d8e30ee983323585807cb79795c8c16e6ca7c580fe4eca1ea73b684e4c3e18991d91a59f41073eb2622064344082e102a6de49e1fcb71c7f52961c16ac2f57d02157bc1801bb859c038ab3561992e8936978747c77a8569b1a626fa55ac084db28864881ad4c67b8232285e2368357d055342158ff45840a1cb3705be06fc266652b974ed401237d34883f6aa1f6489f6eae917bf5e22693094b80770aa58a786b2243cfba3ca1cfe20de0d7762338c88d1f2201a9c21b7ef1dcff607292ac9d2702ace966ba9db05da2614531b33a1520e831f00afc957d36b9600b816acb57b73d5f27eaadef3a11f515d89ebc27d6e33a3d744d0b73ea0d21f006e0931df70fec35eb6fa2a95957e27f1faee1f6bb26019a89870da7b52c974b0838817049f77b47d2aa88fdafa65362f74c2ade4cf4f7ac7e85389cb9654810be3d7eeeea765c2fd95ec75d3f0d2c43c93b970225f0fc7dd6fff5976282ffaff7e598f540d3257fde4e1c77288061d3cff68cd86b097913960bbf19943ff77bfeb83f19d0d93cc286b1798bc2703bfad55dd1827f301c7c211d750368f710f326c11fdbd36c413be0247f21715eaf85f0ccd8ab68c85c701229cb02d776ac768f7371aad0b8049b42841d3822add91b98a46673f41f43146fe01f996309871c528da3b3e6fc21b52f7bcd10fca3a26c8ceb917e04a19fbc76431a93ef4f916995f0d70c13af35784078386848aba3dc338c5d8f5dffd66960184687b8d1f995b15c256c88f09c4392181f02e072a10be4f43151d75fcd3318d6dd060583a26a8b2fb50c76708184642add16695894b8d321646c36168ab4965402b73bd794853d6817c76f5a1491b609b8a4f5efb8f75bcb8ea540a4b92aef6061db23f818d1e24bf866eb49eadaaa6dc7b41ea0f6038ace19e6817024d093456dfbb9a4bdb06edf62ebd7ff955a58838e53b7acfa7ba82000f2b2166cea6721a3b3c3d6884de63788712b3b50658acfefca8ebf444557cc15552de8e174693c71828140500987009403165a1cf970e52f7c42b8888506a9d36afb4b815e4a2ef784ccb7553e801bd7fc184102bb7937138ccac9e56baf9200192f72c2cc80bd4da10422de51fd56d4489a1c80dcc2e39c9e3a0acca627064c1638afd6b730ea1020c17a3f3b55e6a48f2c021d59caeb4fddb02cffed3119c8336a409a631bf6531870428ad55f99d131e3b872138c32bf9283516a8e705551526154df6384dd0f18e105be7c379b8a58441e0061513fd794b9bc0304d03d5a923ff7337f716e8aadfdcd3977f44eeb366d49cadbdafa0bd09637290019e0e37d50a021c4075c83f69309f29a9cf579ac555a7ba8c5e99d46715e5f81f7db1ff783cb234b595c22a873c0bb79375f3c1393be090e0ef11ba14d8a09d7f29ff32254e1c15ec77515cd06184f7444c543bc6cdb5b883749bafa2c8ab96e53ad119d41a53238f2025c736029636051f80918dd753508c6a9b18562d1f05352f6252b09ae979b512fa5e7c7ff76f2d468a6d89b973cc192ef6e261ecee8da3423160a341d6ab450940732e51250fd8a86bdf58914a880a59d0b28e0f145b043bc62aa5ee5c05fe8db5af64488ce1b024e110cd88fe820e4b54f9e0490f4a8af7d44d5a763b0ed2bb2d77eff363f786aecf55747590a8e82f8794495503203fa7d68ca90012861677652cb358f4ff6e9eb82afefe598354c8765a9f6eabec6fa9d0cda42258981fb67f20bbf9de5c0c9c7666904d3934f88a5283f0aa8db2e5a9c6df55ff6bfad8ba8017e1c0f4bae59f20156eff9096e9385fcd11a18e5caa1564e0ca7be1e183651cb2ea63567e433f53f0d688d4fa1208a82c6d625ed651bdd39361537ef3fc170634b71cecb657b0193d09bab1370dd53d1a5b32d606e8102f7cfc7347a223040609571fcffcc28243c37aa46ddee11c95137fd21084bddc41077dea0a5d17440a4ffe55912ec57dbf92773d8bd407d903c64c7a263592b70d1e0b5337d304c75f1e3dc98533ef78ba9d18be2a6709df42f9c229d15b57a9d47055995bfd68f7163a4d4562256bdb9947f89623e5f8ab430a502a54b7b0180afde9f11a45c1b143fbef0b178336dd1f16fbc2d53a92a5c4385f39174e6ef021eb866a729f4086f07a4a118f0af584f05474b3c07e3af3c9f6293c4a37657e4785d36d30b2e414defefe6838306b96484998686cb206fc610d7022a90753738b741e58b4775ee5cb62d11d8fa37d0b313e7c3938d757191098d7ff5a914283fef2032f64ef7867a24dd340a4dc1504a59b91ed37a241ca788247c3abb50d082e25c0920d7742dac2ccd51ad59dc5b4006ada0fb1d8cf80b9f4d53884006f828dc413a9316a3f0c56b85e81bee2dcfc4795454cb027dc4f02e652b28332a82dbafcfe3dd06f6bd31fb5094b0eaac602232aa44acfe8f78c74c9a34f1f3bea058bd39424da1529afe48b8a406b9497ffef2352b55cb6fef1cac29abe5aa5d0c2a34424e56ac608cd3d64499021413ea7bead593f1ae6356071fb510a3f36e383018f7255041d8456f941ebab96e6ad8515b15fd31a8863fd066ebaf2deac7a62d404a790c1f3fa552af24884f75912ecf00b83922eef7e9d6bcf6d3640907e2a3f5be42e6885768200e745d484f6380a7eca8004f5f4e1ca5f21d959ad53b7479dcb8b30fe4dc544f2cf679372b6145aa43afc43f745fc1fb51f06f76b6cfe1f387de1dfdba9833fb2fedf615f3f1c81a841b7853cf7c1f4dd8fd76cfbb6db2ab9c98e2f488c775a2e9a2a1f71454ada626af50107a588f9631a0f4b0fb4905b98b311d3c6aa68c96229a1b7d2bf178eaec6747a6474b402c23c4fa1bf6f4369e1a0cb3ddc7bcd359dd8c97858192f0c232c5101e2402284d84c9fb43172c00160e45140a5914586112baffd00c6041a0d8c0d965f15e22c2af9288fd3604dd75b31691ecc89af2728ffa758d568b43a34abeeea661971586d58b800daeb64de45c01deca1ad9dccbd80ff5beaccf646b3b287660eca50199ab978447e070f45afe401c091068c0f48cd00372584576ad3c7ac8dca81f84af545ad0e43a3ed8ad8f3a265cdcad06d2a8b41e13bb81d5b97b209f844975933c35d17f28fe78f4f9a57a01697a314bf797f07232fa0a1dd39a93ea3f37e8980d5415785c41dfaf525f066f9547e22a2ef77d82145d1d753df0df8c96cc5be124d5a1ceac0b46ccebb96f4586833f6da56cae5b95429e60f58ffb99d807a99baf29f8930de59d8823b0becbd570023c070ca1b87721fd8f0dec9696c610ffc95dfa016bbc8636fb024df06469b1261d4b0d736d59b7fb0261551dd648ea84a83173e531c3c01ce5652cc3cd3a3b5f9359eb5f441ed038edb8cc5da1728db42242a37d9bb3703a472f42c0134d201115376fff801e9eea1f0851ddb8513c04454f781b1b5cb9751ce03c97a7c7ec698788d2d093ee1994763fe5b876ceab8533d71ccf77a5bf6f022803d4e54719555e19e1ac2cd3f074564df363b7a19b99296ad8d68a37ba8709ec59679e516e24361535eb304f42c065f1c684cb6e3d598687532a17f687a8356bc7a935d637a97299c7088e1492f526763a16be3b934648fcd83057263048d734f4e734f8d70d6218a8d17d9c9bfa423303f50db7377d5585b3e4b0c3dca8e289d11167ed15ea6cc5341f498d09e66fae4b5cb9f51fa1064042405ddcae9055dc85761798b1ebd55cfb61602bf5073a58159de59f4bcee5d2d180d5b7d710b52d88938d95bfdcb0ce33d30266e18fb282cadc3ed3806e8560b8cc660d882a52449ec4c1f922b401a02bd2faeda7e435a2cf97d403994acbc945d77789d0fe507d97f5279938112cfca242f371196696119a3efe05c9d1ec473980b931472fc60f440612e2c834130c0109317d995ac5d32426d2028967ed1620b4b5cf72cae86e27e1ccfea8b951ae37c591ec2710fbed68180ee19cb183b8ea2e089c0b641d6eda598431fdb24f34258a78d48075ca32c9d2cc19b5662d73647ca6e8ac4edb20bfb29e1652eb03ca207566d688c36cf608468477a70df1cbd7210eb245d2c9ff2e0b3ee269b7fd91e125bc94e816e7ade02e845987d4b655588d691574d111ccae888f8c173eed6a9fe1961ec7a9917dc9378f93be2f28f5567f023e4c92f23a09b6872a0162c2de579a6f37272b72fcc6104ea62aa3d3b303b9e0418e0330cd3131a6d5fe51d45bd8b68fc6f4f9ca2c878798fb8be51ecf19929bb88a3b53e41ce3d8736450f9e9fe33e7a00024fe489408547500d7eabb6111c4f25cd37392de8a7b28607ea8997bcb9088fce3b9274de2e24c558abad82ddaaad6d5168c9b568ef16cf3931a55f8e8c13f50dcc505d6257e89f980e3d12b2b3ddc765a284bc5ba4993eb247e18c43bc8e3c6bc9258302b701bbbde3c70eaa2298cc4d4909626b24f847d393c843fbd435e52b1bdfd3e2a1a8bcbb4c8e097803d06644e5841464a88116fda5a345450dd67b295714df5a4ae0b74fe81374008098a4b8da50cc0a55b4bb6101f91e50ef2877598de71d38888f03699db410d26de4ce8ad481dc480ca5e36c3175e89386e9818ab5299a7c93d7c91360b03e21af938b031de8243e74484ab108bbadb132cc0cd4800dc8b6b3a169c9d14166ea2a6ca512436f27a0f4f3893e9ec24819c2058e7a8651b98f6c0800a0942fa08d8748446b4cd6956cd25100c1bbf89d89bedc66ae24591c6eec784257a20a92d22ede8a4c0f0cba6dd160b7471a7982c9631a2c64aa30c4c0b0b31fdfb3005c490d2a58752ce41aa119d8e1dac2027470f484b1ad9ed4b1406fa8180840e394267f4aa14c5eec152666b621c47fa2ab6f45fb527f7a654fd97a005a5c2e49f4aa22737c659dd2cda5aed7232f5b95f376b9d43d4404d274d4dc3dc2c221bc95c63498057c21cb9d9bfcccb3868d2fdb4bbfc7f764a2a747c6f599deab90e53acd82cd9eb7b3939cad2fac87969c5169b6d5d28531a00e8ef1d490e14b1b30bd0578243068a6b8875cffaf209f4b5f8e1c0fe507c802b4a322e24d47af489995993a0a5177bf41b84cf1babd516b749dba3d71a08e8984b8c25e1d06aa000d3b045e918f90c232a2b7752ec9c4c6f1bd76ba55f2f8df3c46e7d4a87d3c6d5ecf4c1998cdf51a8860068a2076a689d2828aadb4fc896d38f235812a4374cb2b60ddd10a0e1d3fc597747349923c305d006da300f2ed1c8bb5d18d9382768ece3de1a26cf1f261b89f605d9e04e61bcf44dc7c43165497c866d2d19b9d00d1a44ebea7322aaae6959ca8921e767d3bfc1b692ca5dfdcedd6de2ae4eab1ed75918c08ef84d6c45d6f850b5f48c65f1486b0391874c220175311130e7a41824bc7880318957969554e3ce14a39b0d65564430ac12027c1ccb96f22e705c6a588e32a43115ed02af079514aad8342f77b600a0c5b5d3b98ece0e677046d02529078d9e37e51a291174a4e86d2fb8f8f6da6b820aeffe3ec6879652d5b955f54ee5cbe284df84ff534a4f2d5cc10e679915d15a7ed8431339e10bfdd38349e90b0182184f0c89053dd57bce63ede097c0525f15a53a11b5c63f00df1c7226553bfdbb5c36d7f68f7c41b623558aa9aa91f6930feda8a0005198cf888a2b31be77f52004800177e0a3c30dbe8d29282e3768b8908a52979d0bcf4c51ebfa4de342f0d8e678f1ab20f3df585b897a9dba7385207e01ad5c2423de4bbccc796a9db719e87092f2cb2c18d4de456a0fbc6c3fce32961a8e7f217bcd6e9887e876d80cba1dfba923984a615d38095238ce1f384bd2ca10c6f34ad5a9b69df25cb89a63193be925027dd2775e6403178f5f152dae8e8fcb587e800f1611d9d1fbef67ef7c8709e119c1fbe2517a5afd2bb2a836036f3d81ef5970f6df62436c1e2fd4157b56166f3a4d0acc4e738467ec56afaf9b5b29aa151e5b7021d7460e86c700d0d248e76d0ef69f35e12e6e6e55338a4bd6b0290bb211712a7b1c1e74f7962fbbeddec2d6cc92cfb1f7f04616543d8f527016cb073fa717e732c0710d4547959e091e8c658a9f2c8aa48ed9c89c986874bfa0fc3ef87a3deebf669bc2f644b341e925b4f72d8d83817584fc7c736de12a68dd25a2261f179b0039bebf3b5c061d973b327f72531fd6e005d88d6c1e78a3a949918702efdea8fe0d459c94f73e758dd333593454ceeb980e754c23389b5e4e60bece41713af37f7f4044e07c7903a248f8b1d8cfb7066a474ddc52e074b1be9d46e1f569e4bdf9296039343142bed2cf3f4fec7bd6f59d6526b16504fed65b30f6431ed16e624d880de8d951f904021ba4891514463fdc557369e3bd00c67e2fc0ab7ce0569dce84e5a41e148963b925541d6964ff8954497997d28701e6260bd4dd99e10cce432b31ea34db9420f0713ddd7babc91b3a2c5614a0519da2270739fbf955724633d038d0b9b452c9387feecd72c4323f2ce16394df91e0d466874e261b928b64e3cf22959cdef7ac6c67e3634cc8f2944fba34c47db84d7dc2bd7648e80fda5d2f9e7f18d9212f5a528c8654295b2ae74edbf457d6942beec5e2049c30b65be1c470da965f9b326d1c6facbe59dcba2012310be5ad562c56834882f3b20dff8fd730166ba462d1d38705659624408bf24948d8e5c565e0c1037a92ed672b49084f4ff26cc4c40d1ec67181aa2785a6005afb3e2802fbe27277117968529fe31650c4228de1b1cf361d5d5d19eaa1a9fd9c27e142153458a575388e2c5970c2435c7b39019c0cf2dabf91aca5eea146eb69fb99adc144240ad1f4c299a4979170ad9f399e1d814fcf27dd1ee23a9484834227419640ee438e4eff9fa74a5bc14180a9c3ff17ae43e5b54f922e96f29ed4683b5edd7918701c4753b1eb5d59ccaf6009546d131da84099d6b1a80ebfa0a213c1524d5cb2fc8f90c96fb6a03c7293d5cfb6fdadf60c819c379fc23de0b1e2c79ed40c916ff5e5a1418562da68bb839c9e28372bae4c1cdfc6d4d40025b5d94fabbf12936a31f584253c6f217252e709b18014cda517ed7f3a660895ba53ae4ac50df543122c4bb2bb183f11b9ecc5ff41dd3d92d9845c70cdb8e80704d72be2cd1eb8c940afa537bcaf323b6b5b226379a80bbc1066b9a1532f0b6be26335c203358061598fff38e1f649f4e77aeef87f4911c6d9818f420229da2b06a3fa08f9b95d18062e6faea3edf2641d8d35f473d2873911d030554484889eb81f7c7a20ef3931c5df86f321348ecf296b33ea11a0d9cb84614e340348d2c04d1b16f875c3893326548d37f0bceba34c73ca9c94882574f82ff807532b5fdfb2dd31c97a0f0c8fe314b4453e1e4e773cf4439735072e037082b4bba80f4b26d1b70f4b468556bc873087c0bc9fe87ccda14d5191a59e687f100268813c76649b6bb0ad48583d25a88fb539fd8b9bb7848206c7c1a160f388446345d9c32b083192f2efeb37cdbfd5815ae30fafdfc2f60b253422460a907da1d7715f48cbebf8be56b5539e8bf7c2733826f28d8ddaaa72ae30150be1a6debb3b5333d1c291ae6fe135ef674b410240bde6cf54f3075348622e0d4e8059b9ac58238329719ac154fc3b000ced433dc01edc52b60aa467d58bd9b16ece3663c837c2be63389ef02eafa17586a12c74e50f779c6118256b899d1e95e70bfa0798fb11412f5bef40d0ce042e7457545411e5c6826ecce962797cf644c0836cab223528a3505486b349a4bf4aa66af21eb81c1c76561490a99b05ee96112dccda8888e217caf5b715c3f5ecf0910630c49377e8f94e713820d085f283017dbfc981b90ca9d0938cb3afaf07a06824f400b423f4fe4f049d43c3441c8b26e5e233842716450714cb168d0009e5faf015c213cbc61a12a553f1826c107adb56ddc22265407446349e12d2b43cc99b2f06ccb20160bd6506bfb396397d394f4b3a5452e06f21860bae7a31a2e777339997e696e586f12526119a6c654fb59df07d145f5a55186b76473ae92abf178d03733b2d52e2457bb36a071dad4632f413e4b3b4f8293b701b83c198a981846566b2a95c04ad885c80a93dd61c0250c548c2d19fcb094140e9e53b5ff89270d10a7ec9523e9549953fe698040a8f2db0989c8050ef47b2ad4755805ef71ad42eddf40fab733e36c0b8418d69a954d325ba78c4c172d97002a06045a829f8dcb76fc01b8929cb1dedd51d577fa49dfaba1870e52d95ab0ee360d15be370571ddca3891b0c2f2f799a52b0e2a7b78efeb470b013a2bd63e8379d5f902a62b35c1fb526490a1291cecda570a8219fe2c714e7435957d7963e3214a9f36395cf87a5c5d0894237c7a893819318abb43e71f7b3a877f425086e3744f093c3cbfd1016d3d5a462f0f612c851e806781bee0ddeceadd34381be8e7fe950b27a8f4a4f691c466df170a72e2da5518814ad1a42b54960f12c32e4915575639d802efd4c3bbc44135d7f2491e4852cc9266caa4581c520a55ff197cea635a9201554ebaca6ed1689690cc60c869bbd2403985d3b1f95e51f4aa209f7f399d2c8dc32b708bd5f8abe2c460f7f5eea64851ed4da4ca826988c09644f3640d2663dc3aeafb24f35dfaff7f1a4e2a5da347cd50f6d526ff0d717a9974b4cbfbc27528f753e7951a70deccb4ca54d4241b0a0f112288b328fa2da6d14a8bce4b28658638e96febdfa0e1275a31633b3dde218ec0820dc641c30a0221fc362a1f96966eacd8ffbbccaa44e2fdffc568d3c11e4c832dbbdf9860eaa5e781e80898f2520dfe9f870d724d38370272b1f0bf5b82e01980f551d3757a90fcc07472a62c3d913913e468e255a6e0ad3f9ea09440eadbc7507383dfa98caf57892638f50b0086be2c2cce2a38fcdb190a71c300273068296f4dd112bcfe5c121d6ac597e23f765e59855306781d9bec5738883d502059dbc2640efcf53d129596120e084980017c0ce202889909104845ecd645d6022c746889d5da2ac4c615121495b7d226ebbd6681bf9a0849f67548627011a1021eeab423677f0ff43edefedcec75739bb77745019c55d97efbbb7a13687c6dadce037daec19fc309100b18c856d9ebf7dc2830fc5df202f38409c61b6d7c56d538ea8efbe5fa42fec3f96ecc5b43e869da65fe09c1328d045b1ecba5b1d93346190839fbeb81b414ce0227fbdab1710d02b647d5c1c052e4f44ef0d571050c968d30108af3ba8e50c678d3d48cf5c89bc6dfd3577d3bc56267cb811a52e8aac83d57b7e6741cb07a69c4f169c34f06df8724a03d9043ef85f0531879d3fa91fbfd6b6c1341ee1df38063fbac9a88990bda5dc01040ec70da60d34cdd2b24c9472695dc2ab95f6a0a2525abb190c19196658abb46a1b0cd538957c78d267d2ac2657b325902a32942bcb6447e9c864a1ce91eeb687573bf38fb406e61b349756714b42bd2ba3693131d951c84587bf845e50ac73fbf5994851a8306c1fb26ddb3693c9047e9e49eb93ddb66dcbb20c44927d03b76799c8b027404c1f9e00d1e4ec2ccbeabd937dd263ff78222cc7b159a753923b65472873e15033699aa66dda2626c93e395bb7539f98e8ded7b2db3dc99268622786656ce13a544dd3aee338ee1509b76974d26b62177547baaeebba4929ea66d929ac0f6d17725df78eb5f29ab689b5d69ad55a6bad477855c239e79c3da137bf9092279a1e4faf076d08c6ec7e61f6cc204584930d174f01c31d9a55dfd7818766f18f5010862a6176a9d801566224552fe5d6b9a39c64c8e2223c1b3dc8cd5ac167d993cb81cfbc135f9e27ae9c49e7a107c303daece0ec3abbceae53d548efbacf3375e74e3fd27ca46350288e6366247c0d447262f17bf965af128ab555f5a76b07adf68925e7896568124b542796efd1b866814a8c6c1d8869b0071e67a3034bf8bd1196dc0cf284328cf9b523c4d0933b672c89937aed34d8fa2e47465c56df38a6e64f97d24acdd3997cf3504effc4285798c83ce651660e7e8a4c4299c75c4aebb35168fefd64a35c6142f3ef5172fc741d148439c414a511c39fce04c7bfe3f877b233ffaccc71d82354d5c55e2384b375657e464cf5c17f9711532da67288f9e9313ffdf47d3fbd6c57d82e7e38e7e4a6455110499590aaf8324e08c1c7bc0c3ff124c65846a22a3e683b885148af9a3dac6e73cc28a4624ac904eefcbc25235d3ecf9605a2476e4a7e9fffec12aa126fe8711e79b61c6fc9af928bbe5976953dcca33b9f89506ad74ae711526d8388d24a1b88459e925ac24cc4784a121a1184e55c0ae3838e1a841da414c59706f3a4593797ef43b3741885fc1ea6196a64b90c0e607ebe451d9af4c84fe5d02d7e5115f35d00c303c9404a588ab874e9f2ead241445b8c6247af6695cda91d24f0f31a22eaa016e2c1eb1c67fc60465117622c1d21082e984b49b820092da86ce9819176b5d3eba020acd7be9d63310ccfd7f9558eaaf8955fad0e2e666b8248964ce15eb9f36968106adfdee7a57398ed5ccf625d717682304a7d6a09942b4c4cd72ea5a58950a204b125b5c453153f3553535cd2e4dd5b408af160718411366f49add9da44d796d4d2c414103dbaa88a9fd9d96ae1c70527c2ca4434fcf0abe45ba572c8cc41414a4b94952865258696f8cd049818330775cf18d151b34a66e212719798ab5facc44b4cb08264687b48dbb641f1e509364c696ce1b2e58b238660b4a50a9810926a9ed0b9c2999a78c1185014b1a5899a273c7ba7381e0d5d8e0876b919558db2a03b3f6de079dd59ed1d306156b3da4d87e04d9d92863b6bd44718ed69a3519808a34694a42d130c06ab2af078cadc29a66c98539cf6728fe135575ae9a454bcd9b3670c60b7bb6adbd711d18ea86e5c57ca514a2badb423ea0e964d71a29df5f481bbcfddf493041de5a408bd3b635a96b4b85102e6e999a1c8a867a0052923ba01125f0c6909a3886f862f621fcda843137edffd686ebea2a16ddb52b004161d335a40842a8d30c6cc5c2715caab1d9a2297e634df0f683eda535457ab8391c2d32697db03c38a6704729dac4e1cdc5ac56d708ef55d066e77e0bb5b3da0d5ee13d862ed31c8a36fe6cab5f64f385fef8c69a9526b15e39c96155c7ee5686e905bde1411010630a4b8f428308a70520a97052785a52f5e20a1075f2c21c588257290841438f0c10c8ad0c0e04c5b0f0c47d80b23286e87a8d0c78db9333686d28d654e30b7c32f8b2157b10c7b6c9a27d9a5dd2d721d46c81d45811a417f5f28a1488bdbaf218a88fe428adbaf7dda030d8828010fce1003c6829e00193f0002147200840417ad46157561a57e345f4813c40d9e811b3c5da411533b1a1d1af40add73cbecc28516ae0b226e3fec2205b7bb58e37616a1db599070bbbd00baed85106ecfc08adbdb178f9fd082859e5a1ae69dd1222c5bbb1a9d49229ccfc437b5f33728114bf85bfef2390c57c475b2e665aa6597052c214d9a41b41aa32a8d8f10d61825bb059e71cb99544e235cedde96dab6690652bb7e803a0a57d82e2eaeb8335f6c055deef7c945d005637ab6ea0310ae1e9ad555f968beb8e54d98bafc85372c61d2a459d9b57376071d485e6a1514a68f1096b88804788876ed0e9824cdb5b227462cc7d578cbef7ee217851c2f7b8836a291b8dacb5ee26a6f2870bcc5725523f2207e87880a73e010cb20b7e4221ee8d7809275e022fc8439248858b695318aab3d462c0f8a6533715173a90c456ffb364bef0ca2dd003fb03ab9f94d1a765fb759be9e56dfe0b51325c3f11225796895c65903fc0c4d56bd124eac73dce61c372897e3441f7ad478e8f187a8c759ce241327b6ab1379a9de549826e990cbe55aa2a55999aa8965aae65db24596a99a97b5648b30e6386fb9e3064142c8efcb35ab66ae9316da693e959d6fd6a50a286e972a56707b6a32f7bcd6344dd334bed65d05a5699aa669da4c0e5a4124da719c3f7329b57cc269b67ce5be900655dee078d5001077bba594ab81ede260ca41d8a12e5cad9a7b3535a2eb0aa75d137ba0b7e46f40cad5c001304a642a8d01d3e53ecea5cb438f7346641897d9e58e5dc43c50d53cc741e9713ee9713ef43eefab9b58c69c3f588f934a8f5042194e7bd2e36a8cb0d2008408a5c7d9415335cf8573d24c41619402c53a681e218427c8600744e82d6bfc08018b2c6082d03a693acefd661855cd4feee3aac66aed6b9aa6699acc35ad65440cb4e8647b29656887a8699f548788c221c246988c58aeb4932dbbcb9def18f012e871feb31e0f946498e671ac55f34f9ae4d60c1373614bd8466217f53803343fc9b0b2021853999aa644d8aa29a6fa6fd22c6e8a31498ea9c1674cb28b5a353fd7b8f3a3126139f608244cb865faf09df333421c373345383f6f0a756fc28bfad1252c79eefcb67dbe675eee0243b33631a59daf26726c0617b05821c2cc728c8f7a9ca796bbb8a006c7e6fb68be8be6dbe83ad635e342758d169f3fd2b3ca18a008a21d66f6d1843d521afb24fb687aa43deb10680d6284f048f61a687acc326d45ea80f5ccd0378b19b101c3cdb22dcbb8332dc0b819106e769dacbbfb668c8559b94a696f2bb76c2b7723fb7586121733c808a38856f61a28b9c30a159431c50d17175c5ad957d97bb26ff673057953a73cd52ab2185b9a41413488aa681025e98b55f41c17244455942953716982750961495f3410a2435c44837da714e6f2e207062d9a0213d590c0e2d26b302ec26929983e5c1166107afa351aa58684c98b51d26484c915c1644ae221f4b4f48ee00d813245e1d27b311c97a39cc7844b0f0661921e0c3cc2a520132e05a57069cc082e8d3922268932a6894befc12819e3c136b1447d65a28f11a2bf492c7bc07731429ad06968aae8cb18a14b27c81561fa247191d3d01c424f7f2a62fab078c5a5a755c484094b135215979e863a91a3a7a2247a32ba287a1c5b84f39627a48a6442aaf44a979e2231490e33552f2ec26ecc21464c7a82c9ca4e994eb9fc12c72de9ebd22514c10296903fb908dff9378d692a34b89a6530a7989f2f5a6071cb151142700acf8cb0bae083220cef8c699102930bc2993b635ab838248427379c10825e3c20841b0f3c2e687c4284592cfb819149085980616a41165b78485958f1042e3c719ec993c2bb020666a184a98aa12f0b1318d161f1f2a600e3a8789956dc128cd8563d2b9e1e2a8a3a6e35841f8d0bb92fbcc288c3810a3221ba713340a25a11d3569c900ae8aa67c5d333b46586212e59c8ad807a862a8e6a4c063f3f542c71589498004018222e5c8225e9e1c7ed38a209c315793c68e20dcd4af82d41c3126a47e84a1066494b3af098d0441926686206d90e9aa8a2a34108de6946132ed034ae797a60e08b71bb771c3de165d73b81979b3dbbc6725c165336748b4777b669c89e725cef44e47a2799eb75dcb6715a5629a5cd9393d2dc409e3741fa7537f52acf4a659ad01dee80e950e7362dabb4bbbb51b4bb3babddcc62d9e1a976777777777777770e2770d39a691bd781b533795f8cc77da08985a1100469c0b9a8a31924a6fb84389e3442b7ffc51899804cb0cb13068db909283c859ba9aba18c263393cde0a8210e5a2bad343942d47c9b864c40e15773e8084d413a7600551350b3e68e98534c8c09c8046402aa193a25c106e66e54284a697ba935b24628d6eea6b56bad599669611d79d334a4c951ca5b4f8d87086e68074cc8cf6a46bbc70bea1018d4ed06ded4e1a655ab3932ed9e4635dbd7dbbee9996c395feb14b9bfb89627763f42bbe6c01899191cdfb465e699be0a52ca4d540b4a9843394e3232dc56afe940a13a2ef5181e789c75266e337d4c88c4a01bc20e6095261d911ae554dae252f10621ec88c02438b78a26a11d257c6076430d604ceda9b25ac59c808c4f8ca79075fec4439925d70be6830f424a218a9ba2f35c48785367c22e7733ade3b8af366bea342ba3df6ea84106365e42fe669b2c89356bc70dcee465de57571b58651c3f90bef60d2ae6f46d5f8109d33f640ffd8290df554e5f67ea1a3654511c2daa228bce8b212a389230f4041cdd0c1ae672b9506005159e1552a89d17578cc16937e8181643e82eadd4603a4c495d976358b6b84c7f989b43e8019015344c10668b19b4cab9e42596440dc4d8a2854aab64170f31d18c7d5145052ea02fd850a35572121522c8000d33ae60011659a48ce00654a89146d0154b6db4501f3512855d0a84c512aecc651e8248646caa70ddf0271049783ae3f47a3a6141e59eae30e39e1e032239a1bcdc98832092980a7e5e210467c51665c06a67c5cfedce81483a2ec78fab0e5529bbaba140cb16410872b534991c54a185ab754ba5e79470e927154d04260cac6ec7996c776efa6c17b9198451afb14d7aac47bdb3e592cb620fab9e8bba87b24c7aacb1d929e889dc0c8242bdb3618f28d477a0e44dab50af7fc245ba3904f57a1d742e6a8221004a64d2638d6151871e4737423e27e484cf99381b364e85973dac6a142d54299fe36cb9e4762a30efc8614bf1e2388d2d51178788447343fb039f6c49531f63cbefa03dd263fd6761e8b1be866671aff54bb88e4de20c3dd6d79b80d047f78de338eec74b4e8597e1a832ada01a55e025d72496debb528d7aeeacb2bfb8f53f8a1096406e5d5d20f51be7c364eb5d5dd38f1f3f7efc38abf0430553c789a20f3ba94a2713bbc876cf0dbc27dbcc122201a2137b5d993b636680b91ea73ec398644aa45d6e68cc060008672c076c5c4e8895b09c31ee92c392e9c39d521c4c1515db8c4bdf9c9029c22333d4f064bbdd761d9a1eb7772fc32394bc3955599d566d73f6b81a0ae71ddf9dfb669d74e7442038f0ed3e6ff684dcb998efb8b76d2012f0f559b27022f77aeef356a221cea29e084b3a24522ccd0ac0f98df89c73eb48cf4b29d1bc5e152988842996223a63ec15d5da65c68a6a172ef2c42e97debbf1ecedbdd46f0e36b949a56ebc7e52aff7ade63737160a8fa74ca292ec1b3072b94391d2ca79941c317bb7b8c5e351bac5e313cc01e71cf71a911235cb139da46cd89e7d1329961e29518f599ac59408757a8aa5c6f432bc1c6751af34ac7a501fc332bc28eec63b6751a7f4721ed500bf81aa377bcdbd6ebb0d1200e773aa3c9cf3cd80741167e2a13a67116a8587d06f3adbf5380b84a34cadb049646ac5d22e2b4adeb48ae6220de57273fa1a7434c3a3231da7347351b2e364c2234a66465c98191d6546a9531e2f399c97e195295157e699924ca624f3393325191e2187234eaa1233ee37dc2bc7cd7033dccc0c8783c3c1e1e07070c7c1551a9b3e7de9372555ac12e5e6387513696c06e9d223bd10188438e75ed2d801700ee50a7773c3c3e58e73a6a70984b38c2fbedceca594cb65379d7b0dbd26d4b76fe20493d41cc5dd2cec91eb00bdec4d908692cb5e5f72f7668de54c36ecd10b972e5b27f5db0e4ebe84890e3678622965820ad88ef32d7b8cb24191b8ec66962b81b8559c3b6cc95d4edcd17d311a8b2333da6e99b96a1497fe547ee27ba437a826b95602429b97a86ddb72c8c452fb137ab9df0071b76f1bdf520adf8d0c42e8a402f5bfb313024c62e472dbcdcdcdcdcd39b14a08a2a46e4428dc4a69df9e5db3e5137aeb33ae5586774e556633678cd376f44867d64b4a95bff4489bbdecd084bd6419b779453810d2b25ab9ad074ab3ca7934825bce255a6bad272e212b39c1a29776a73d454a0d84a2aa940db54a8a49e724b5e47b951413eff35691f64ba9b3a72a5a35256d4729a447283d6b294dcb3a244a63b1309752971350a09f52500aa8135ea80e4a29a5d73256892aff843e13c1a534b8e547b7dcd3e5cb40564d72113dcf220370c2893964de0138f1c40c326f39c97eadf7c5ad8e5228bcd444e8bb2720ecc428a6770e267104465aa6734d84deb32155d1aeebbaee554c6208a2f483684d70492742e156ff277c8f2a357011a106425c1102c70b0f7edc5842690a305af404c6891d1491618323845a3476822658e186120990d268f55d6012ea57812a8c424163707b92a6ae22314993c83fdd12b74f69104d9f06d12aa608cbb97481a06485358cd60ac4402c0406e16cf54b7e6d5d2f51b213695285552a348992340a6a856910134218d0eba75995a9d0a057f78f8998cc4693bd4455cdb1faeac46d14dc060a435fe197a641b74f29a534a99b26354d6a9a44936895499a6ea255dca65adca65fd031a819b7a91ab7e907f360a6daf1170eb6c1345806ab3f754a18305dd78993aa626010b69e60ee9c5e42264d86506a58d22cae7b39b7c0c00344bb09fae8eb455faf1e4ad6255ed50633298c89d4c5b1b81d831e286e3fabaffa3a5522a6aa8f0221256e3f71bbbe66909ead9e91e121263923f2125346f4ba642e3a7d38064cdc66a0a96a206617bdb443f2845e5e624e86e20d0e422f8b4094214fd2871e81c812d22722103db6e9a556b5ac3ecb7ec2b2be5e1998a429cd4e5fd3e719974f361f7aec03f1d59a9932cafcbe3e9032d78228292332100dbafdeab17f188892f5a755fd2a8af52585b0cc82b034abac42e5137ab72c8a1eb99f8050e62ffa027a8d715f976f16446586fa1c1786a849cebc5f5f949c11b9ac8adb9916332fb32f6e67639831d5b89db54113722f4ec688fef4ac46aeafdb5694a24862e23695dbd4eb0175b959b363aac8c86cb01c5bd1a5f71a465d2f7c5e8bc2245f10299556d1fb88f4e536c4a5f73cfe5eb0455eed576972badb2e1a381725b59ccce5ba34c7cb4ac5a4e3ad2d652285b511a67c3c95a42f8f9714e6e3f44c5578ff4c9f9dd323e7396de2d2ad8939847efb3bc84555ab71fc51a8304cb5514a05b1e47808fd8e973e2c91b70d67107a1e96c6a5449f33fef427c8cb55399647f1785f8577a6c4fd1851aa15ae01d10711cbd5ea43c4721c7221627921072202f9788b9950ac85d51021216989885a98accc6e47afc5a8e791cfb4a5175e6a4ba4cfa78f98015118c785210cfc5a587d1ff249857c52209f2c04b9c7b244c4251311cb8a5132136a153d0b2f39202f43212f51434e4dda92b6a42dc5f002cf919d98989898181c1c70c42a517cfec2fb5993170ee54afbf8bcf0eeebc385e3b8f0024db3789c52574d0eff4c9fcb60af02fd397dce22f4670ea13fada014aa94c25129fca1142a65402d88e56ab512cb711c2296bf10b144a180886518b220a23e9bcc84503ec42f3c84de268858f2b03d8e636b8f1910253324aaa25a8caa78000179c9b1f01594542faf2d6d59cad58f97e37dbc44e564424259f6e55220628c85d80a624c252e515203ca9028d9415b162ea27119c57235fe10cbf12a88e58150404b261e62b665b9f45e0fab730d88926da40169402854a86399ca1ce9dbf2a549dcc8cc9ccacc340c8797d0e73dacd81c1786461a5010172ed2b0d56a1c7f144ae625a771b954b40c83f5e951329f0dbbb48d28a901756b4046ad0119b50664a401519f933e2267e432fc12358eab9591908b0115fa88d6ce1e65b0b7e0a4574225d1d074ce678f38e48855a2c4d04d7c0ee54a16430ce2a42a1e62c9c3edfbfc08ed0175fbf2b0e593edf671c42a5160b80f1318441f114a9193e08a068cdcfa208af47cbee78dda1276531825b90d18b9fd235dc442e1d60b2390d2f239b7a09020ca077c6ea425a5e553e5033e6fb14a08a2bcf028ad092ef111a174eb85176ccf6f6c99dd9458d2d8d81fb8c696343bec0f3a280ce8d2f01456d0c2cacfa42dd1dc522b19564e29dc27dbcdc42f4b386f8fa5ffb622221d8405ded409f2e235f869c42441b1a36855bfc53e2772258da2eb57b6ca1266e72f577b495fa6bbdd13cbd32dfb75bb83c29ff0357db2ec35abe7b2773133ba5c7a67cc8cd8f566104f2c9f6c37bb499c3f30c7af1ff8961d4423a56fad30536742fca9b3e2a112681d6035e4b6581941fc656a99d7b19d2d02cc16bffb9848d398a8c90f56c485b95c3af485bab65c0ac6b87cb95cbaa092b2a14e8c4bef99b22ccb4024a925f30498ad291e80450e0219c8c70561c94445cdcabc8ef28f0c42284d9e4aa06f60d1a9b489f76b19e6d440241910f3331049166611962b9f1684e58f6455ccc41401666b4a7f0a77d3bb67a16522366a1bbb746a69430759585902136ad09a52d8c28a1b5eb888b95cad49b42a57ff5a06195e6f75d8a4092599e8f43e4b84f5d2da5c9352aa5ce172b99220822360d1aa356422e3831e4c3185292e177ffea4850d966e9ffb1a97d7d5ce278fbfaaa44170b34beb0c52c59269f31b0527ed61b352366499184e6ee338e678dbb66dcb4cd9b671db9d6c5cb79d13e746b7d3eb64eb998925376fb2b355a20dd9b39492ea7239b9d90f328eef4db332ba514a29a594524a29dd324a699665599d95bb76aa0dc912f6b37e1982dadf349bdd5a6b92e7a19a95b2a1b3b798b281d2be94dbe29cc0e42dc219b341d2dd5e7fc3f0fbe6afa6a49580a0ccf8de4f70fb91d6b46c8add4cc86896d511d6454030328e2ab53640628fbf9b267b260c357cb12e6e82ccf0220d2dab311f1cddfebcae953268437509a1443b15e71cebb6d56b96ed149d707c83d2accc1b54adb5d6ea856b4b174454314480029006818c32ce826a03308c224e407908234a701d1161841011620c8da145511194ca902209a450c9765bb6044521d3d21d512ddd1a9996fefc5265054a323082073840020c3008b797e8028d9f2354f0c2a2d51195a1041a4cc14204430c2184328478a1e24a9614bc206603d711aacb8d1fd50740f5e6c8162ec8c8b6177858626a90184230515747713a33d470034786954ac6d06dd4513477c6c800baf5ce1819546e8942f2be78939182b830bfeb2d84b63b2908745f5a2c48888a0329036f52c8df2801a60fb64081953496a8410f84d0e20dbc49e1c6514ecbb89e1cc7715c4f31e5076edf05b5d63a4190832aa2104619563c610d16d010428d2b50c0a203257a441078de38e3892e6c60861b520ce00446444105558af0d2258c2d5e43f8010a9ea0c6115fbc8855a0d182130c210d27e8400b077919cd6acdde53ac9496a0b99ba39476b58da8502ceaabeff76bdbcf499f037284908fd0ee81be6e770dc3ca7df445b3a8a156b57a352265845fbd755e6d66cd9aa6ddda5d4696bd72999024c25abf6665f4bb1ca5dfbd34ade2673857758f34b5375464b3d24a51fc8c99451a8db2117e2814a529337186b5e7f6602aa22205aa3a7e90cdca4760a106b5395ab50a223162d455bb26969a66cbbe9e966959adb566307a844cd3c4fea493ce1b5416d2d05b7e618f773ee40eeff32851d201eff32c51b201ef1371519201ef1379515287f789c028b980f78900515201ef1309a26402de272244c926ef1319a22402de274244c903bc4fa4889206789f88112599bc4f2446c902bc4fe4889204789f08122573789f48122597bc4f44899203789fc8122505f03ecb45491cde67bd2879c3fb2c182595bccf02a264002869c3fb2c214ad6f03e6b889234bccf22a26492f759459444f23ecb889233bccf8a511200efb38e28d97a9f8544c9fb3e2b899247de672951d2c8fbac254acaf03ee9a2640cef932f4ac2f03e09a36491f749204afabc4f0651f285f74921728892e4fb24112559ef93459424f23e6944499ef7cefbe411255d78b7f03e9944c9d5fba4122587bc4f2e5152082573c42943f01bad2be049c0b574deef795132c8fb3d304a0279bf0788922cbcdf1344c915deef11a2a4eafd9e214a8eeff71051f2c7fb3d459454e1fd1e234afa78bf274649fbbe8d159b217ae9b91ea7e229a5b453e04fcfc2106899d8fc2294179e2083a396cda965927a8e086526f1a095bac71bb03bb19da31da49da41da59d251e17cf8b07c603c413c423c433c443c453c463c413e339e241e249e251e25922e222f222022302442488881091212244448a881811891139228244248988129125968bf562c15840ac2096106b8845c42a6219b162ac2316122b89a5c45a225de48b849140641029440e914464116944c6c82312894c2295c8a59e570fac07a827a847a867a887a8a7a8c7a8275625e73af6c5b572c41e1755f571aed7df06ac0dd10fd6a64803d6c6c8076b13cb80b539c280b541ba80b549eac1da2839b1364ba54db92c60532f1e6c0a56019b02a2804d054dc0a6842460534311b0292208d854d1036cca68079b8a39c0a68e1a6053480cb0a9241d6c4a690136b5a4007be34a80bd7935b1373004d81ba003d89b2003d81b2126f666a800f6868800f6a628077b63b4c4dec406606f8e04606f9070b0374937d81b2525f666290016c76583c579d560716034581ca02416270889c5119ac1e20c01c0e210b52c4ed1b53846472c4ecc88c53992c1e220c560719260b038452c8e8fe5f182e5d16379909607cbf2206279f0581e3b96071155f55db03c8aa8aadf82e5614455fd95e511a3aafe10cbe388aafa422c8f20960710cb83059be35ac1e6a86c0e8caafaa3cdf9617382a8aaaf82cdf1617386a6e47c88205c2d7b1d3be78e7712ac5a73b6f09d0711261150e491b4138bcd161a87159b3ee0fb3daee98373fb46b484a5ce0bb43f5e33488f3640d646b43630aab209cae17adde6918492c71176bc942f4cb97dce962fc06e6b79a25ca15ce1e7f66b8688c25295a3da7955435aea5ace735236c524f59ce7a472725236e7399f200d649091654f80c14ba88b30f5f2b4745a3229f51c51954949b44f99623ba6582a464638554394dc41a1b81d364ad5d00e1b335b42da1f8bf6fd03eb517cc9d194689fe21f578f3da5c7061580f39c77a00278dc464c31b19942139688b56c2ea59563534c72a04c34a268e55c4acb9be122a49447d20c4246486f398b887a8e7adc89d9f4b886581196395c3b2e4afe392fb9540e570ed7bdb813cb1167efc47a2726ee7c99310ae7a5cfe1615da616912497a935a9aa24a2747b27766f2caa552d863dce968d0b30d889ed205152650591185680b0842ab8b4fa3b4994f4c145050ca4d872c50e9cd0eaefc4768ec280c140ca8e6e82048f6b40460e28fd8b83497983838cf0eabd18cb48c0f0479aefe926b1e4aec643b4eb2354fbe7c5d591bee1a1a9aae7b855d29d465a862e27de988ef3238df315256b666870bc136b2c942b486ade36a9c76ad4e3bc5f7a1ce94bfacae8abc91d481446a59e32d1ac206aa5eab8d50c27b810157119556a56d947b7be6622c8b6dc4a297dd117bd95b6926d30bdb4e395fe181196ade4453b8bc27aa42fad959ae4567d2f75c85154ca768b660b7d5172b50ab7ced263159b88be72bc6c221bcbe313cc81c79b5bde677958ef293b5b5f9594cd3dfe78589b56ea3bac0e4d5bcd5c070f553bc3564c55fd1628306a0dead00c52c90b507185172c9cb8c1088a68d5d750bfeaa9c7b13796652c17f11623e6c231e097f4e5d6d7224228fd2bd9fb8f12829a93805b3562aa859f7a36aae7a1aabd76a9ae1b6af214e472b984d872eb998da25b3f2b1bc58c6add34fad3ac6e8cdb6d1bc78561bd768f873a6eeb590dddbadab8edd218caa57931ff4c26935239c66204986c1cdd324b7a985b9f19018cc988308bf4fd6c3847d363ec3747d341cbcdd174cfd6399ade4b26d3cb3635534f08a87e26cb5f0571e0fb8961b74d563659df64795cc83ebc2fdb8838d3d2f461139b1e36eb27539925cd19a413cb275b786a6a7f60ce474cad1e05ed753e11080ceccbabd6cec8185c96443ba56c89d6ba44c9e6b83044a1b2244c92664ec8deb8552b8176825bb515dcaa3971ebe7ac722ba723fa106fa4eb30b556fa732b9df289e557a3892e315d1a7bac1cb3a597cb7adccbac8ffbf8045751669186cd21f59d77ca74c6930a1e53c9a8649ee9cbac567b9a2e6549354bca924ca24e57247b123ba9eae4c19023cbb21c346f1f76fab0d961b03c6506e127b14ba88a5fc436e991bbd3c783f1929a642f41993e309c5fc324939cbf64b28a9c67510b975597bd2822eb1255a900c67c45ddd9f4f7b8ca129a5e5e67ecb15aef9d1744455c84a9f0905a04837de145accf935824ef9652b52eb51746c41d70bccca1a1a1c1798e1c38383aa724735cc83d3db716f1d26a1c8baa495c026bf62553cac0b46b823e5e46cee757b34e343e5ed2ab23e487ea0ca6eb29613adbaf76b5202c394cbbc47ec970232294189ee21b39b7685ed6a56cc966607aac176233251d9b7d01c3e395c77d8865739624d346d84658d2a525ba347d3ad00e798c5dfdb32d1c8935721fdbf3172cc943b4c4b52e7db9270af928c4c76b76afb37c3b4e26acf9a7e9b77d3d4b2f0591047905337dbad7a556d5735c18d6fa5a44c923afaf46cdf22839c36b4fcf6a7573c6f1e6fce6e4bc0cef4beee67c527206919b2ad3730010722d714ed315e73479a61f3185b92613ca8432994c47cd499a5e8677079b4024a667a69bc024f47a15cc815d542544c8c74389c1c8a31809f2236de4743262c48811235162789456b762c84186c77030d3877c3d2da2a491d753a366f5b05ef6adf9e121f535445e9ee7256ac7059837e4f5744ab356af2d4c1f9d8bdbeff6e3652bf1907a9b1bb1355015bfc79216ca0cc28fc19e2cbfde3f5c240a935631eb878808e311613b30362edf857a1ddb820d3fcbd529cd12227aefc4767962a5e9560a225eb440c4f22c88256a05b10c5b2536510f0cbb9dd8534691c3fc68a5ec1f88c4c792ab599d0f91ba7a9ca2524148976898aeec81f98b52b338ccd2ecc41e18764d3fd22b20a092cd044c105dd3bded53c1eadc87cdde43491d5ac5af610e06d583a2d1b1df0e5456e35140988a0c404c224f8ff50421bf04b2d4994ea74e86eedeabf7fa0c4cb6d475412c5d4f861e3d7e84f6b0e1455baeac2dc7fadb1f98e3617fe07b634b9a942d399b259afa1a5b5226dc7a545dfad54197eaa54962c2ad9f09652caac7eb8cdd263ed96eb89b80308697345dd79dd8721cbf2ef72f88a5fb8965f71ef8754d0f62696bd8ed4e56037c8bc2a466955a123cf09f8fe960966412b919640339068f5f468c183162c4880c4664e0570c4f364de98546024617ddda3d83c03e7f91dca4307bedb169f896a88c4483ba2a318b70161e52ab0e0d3755cc379c25b37d1946c906e29810fd185629a51d7df71eebd86335d9668261d513c771bc29439ac94a01c143ea7bac291cf540a0c21307a1bcf0882ccb32304910d7e32617039a9464cadd27156bc47f6c96f75ae6dcb28180b2a07058936d1a71bba67b1cb7713dd62ee3328ebd9e26d3ec91d2a09d5bd22c167f142ae41aa8559dd840d3875fcf794a749d8c4c4dcd988290a6c9ced64083ba69963dbd3bf87a9dd99a4fb0c966ea616711ce3287d467282e6532994c99dd8eb2dd5357c7ee3081f9cd393e495d9bed59c67d99d5ee35e9318ab0bbcd996b6ca99344c7a27a449d7b29e5b278be1ef88120921e0741ef4b9a559b984c5f73f1fc90671146e221f55c438f75498f751addb0a4c76a7a8ded711d6bbf59beddc424273055f57507242ef28487d4d72a7e133d0a22e9c419f0062957bce8ed404920a8aaf20f0fe11f99c3a8701c67534aba9a7703e85ec3a58e3a679d706716534a6a4427dcf9ddcbee9ce824256e9bc8404c056519d689369eedcbd6f36cf7b01d245adb429e8e8fc11ab1684b162f49cd48cc04133a352fbddb79a9e39c897848e53eaf492c4d672098c93213a820a622321338b6856c47d163f528c84db0890aff21fed86c0735132f99d733d10c4aac11cbae9f0d3483644192410a4b66246f96e18c99517459b833664612d7d3be1e96c7459b736b7f207e98d6f32559388e157c14d9ae8305166c3755a621a5ea9aeec39abe82355d654d6f919b414c62d9c38a819a653ab79541dc1a9b7b1c8e6caab2e3f8bcece50b676f1042d3391049f61bbecf64e9d5a6f76f822653786f6eb22925a8eca8cf9b030e00f58c99b974d2c3ea96ddbddf9cb39c98529289a897a8b3e8c413734024a8f36dd0245670ce20d96d2c0826d19edd64e9f5b61e56d749ca864f28e3537c28525a3f1ee5c7b9f5c376ef165b25dd3760e4f28f1f3f7e44191fa5d5adf1600e2a7c3c1b713d67993ede2bce4b2ef5b26f1844677df81c4bc3c386a39d1e50b336d14977ae7e7210256f9ab1f02baae2882cf28dc8b01eeb538c054c82ba771b5b83baf78a7aad793d03799e656f9a02f4d3963d77da506fe95d1e71824926067aae93ef31f76a287bee3c17f6b8dd747aed4ba66ae2c0b9f3333eeebccc7c789a67f17bdbef386cc95dcff31eda932d7f5c4ffc816f8c4863513df45c2abe860c881eb3d99fd39ac452e7ced93bdcb912224658b22b0b09dea2795632161a3b9b48c95d8ecb9e95402e77af5686d22c76b52a3bc7856179d3e73b65a22fd9c56239da1a747ce0ac78c0492ea1aaec939bf334ab2fc2f9920625def498f11429b3a5619ececa73be4757b3a6347883943be9d94bf5a7c9946a51c978fbadb384923bb26c87a883e372cc6cc2c0c343b973afd59661a53cb5d6c432884bebbfacd6ee8686e3388ee332283dcecf581b45f74b329a1ee7bfb059f445325556d9b337c9ae43ac075f786e76eff4f698ed6fdbb26769ba3c73d9e63255652f790669910a310ab303d159352bcb7e43b3fad997641955c22b7db84e7808ed5bb706454a4bbbe7696f9b89fd5a338daf6513e501b9d9119055102d256b6a0577b73855384138efd139e79c993735ee180a26a197e79c2514ba036f3217e1348305a81a0669f8a5f554670df3d21aced92c5adf9beb361012d09d5aee8cb5d1e57a415e2127f5c8a9b0023eba652d398945f6d223f984d0b58577ea9d7aa7db3d26847cba7d9e76de52600a14958cf7fd6fdfb679db46edd863edb1b357d3b75f67e34a150ef45e8a3b7fda40ef89294e54325eee9c38721cd771dd970c9d6de3ca026f52e00b1bf7eb32052e4c086b8fdc953277d6dc3b46985dc812c2f2574bc59cdf3d15e3fd94e334147bd77e9db1331007fe97f2de9f36f03f31e5894ac6ebdd1347af037c3d11488f2d9aec122bce4b46985d47ab5e7853a764d75774092567bd1622cd24f0aa2a42fa1525e79c7345275ddd1b84dee45457051496fcc3494c9fc97cab6d20d796b579d6ecf566975f94a49452dadc10eae332cee5154f9059a40938c91eaa629feeb9fcc9db575f84fc6dfb2d236c6275e8b19fcd5aafb16e9072797636555f72cc71b5be56e69eb0e4ae766eed0d228181e76aa7da3dea65b369c699c6af97ff6b1496f5cbd5bc5ab9ceae9ad8650a56aecf9d5da4a0743dfe78787aecff305fa6b89e5954325e3e8bab1ed9e3415872a9f9724ccd8f577b4d4d51c9783571d5637fbcf596dc7d527fa8f7729517b764087ea3656211488fdca455fd9edc3cfa69046199da3e3fde793eba3dda717e461e87b1a58c950bfa63172c45b3fcb815102e349ec068285d66c097183698cb19453db40951a033f8053950ca6239f872f9256043075174f1e6d308c3697cb97cee8934bc5ce697384f809fb0ef09d100070d446990a50209d1004b65a308ef05191b534026b4ec8d18136cda1338ce8dec8b1850e892c235d858a3cb11bc35b67c62d800e4b4b8fcd81a587c6b0871376b7409ca96e0876b007589e2562882c233be54993380c4106286c351049a9a63e6f2d71081eb8b1d5cbdd38c2d5decd0c284262b6a4c556c6a8a8d2df574a7195b8eb8d9e2bad9c1971d2011ed20c6e582c1f9b42de017dc1078846ae4107d07423b086226760014da8a86c8ede005d3d101e74307496018cf0d15be2e8daf8d99367e7c5d1afc8f8c2fa6032eb833867480850d2c3a68c175e9b833a6831777e47d612e062bb0010612158dd3ac80850fc823c388a32d574b1140be232c47568e9848c1ed1b11ce581a5d382842500a42740c61c8eacbc1d26521be31c313e245f84de13a7add194b83e896e19d47ae3b636a28dd12a54610d4e872f95f04626820c5d038ba338686d12db3d79dcf72670c0d2b77a211050bae388128c219fb8117208ce007602eff33e2cd2aa1d6a5080d5809030ce13f1d201f1aae5bfea68bd277c6d02d7b38073477c6c4309a398c1105255a00852b5b7869e96022ac8192a92550aed07afa1639aaa224b7b88aae2da945c514ea54c52dfc14f55015936e4191d21aa98abfa4c9911ab8590a9dc586d8c842c199410bfc67a1b011c048b145ebbbf7f197d2ea148d898981124365ec89eba049c69db11860b9554e628f17a38344386f5f24ee09624ca375fa67a1788f79eda1dbffa18290e638ce48f3fb448612fe93c249b683261761d94141321d14ead0220ced6cc91c0ca22478997714f0df1b1453f5dfc37f8f2223a6aa98ca01bccc997c0f0f9607e0a125a825020c4c2fb74a2846d981aa7a0955f56719b05678999733260419d107a418a614b540d1888da6d26c4d90db98412c94efe0bd1b6031420b14afe88805c15398b1822fad4fbca2c30aa3366ebfdb983e93db2ae529a6687b629b449137511333b16c2cb7a90f397e5c9f9d2d2f8825d444df633e732950aed00f4a8c14d78baa38458fe333457f80f038c428e0c3cf88e16c71100321e92c119ec4548d11537404a098aa220fb598484c5131f5fd00e063c42857be833fc0f798c77c9fad232dc508227620948497a1376ad06a1c63bc8009274cd1041743b47ce86005615021c410a020a2d552b05842164b683c81684b125afdcfcec4202c5989955a296b2b596e63992daad35deb98325b86a3e0ee20223128afca33397091dd5a75469729a59467a55d2a63e79db132a72012993748c316e408e1e9f331e7afb8d5aa3f066908dfdd649d50f1267152ee9106df163cf5819235473d8758723b4e232a1d87b8849235228ae36677844e3049f86ec63493dd0b3b4ed1cf6bfac9a61ea39dfc222cf98bcb0745203ed187272d63809fa9c464890b6acc585867a6846588ba41f1e7d156ead6ee43932c20a6644a318e42a35c25bb063822e8db70d0ee030f4b134111dc7a293e4c1903fc7480ef01745c8718848eeb780c538aae57ad9218a614dd18a61451b18a2b862945ad20748857fa572abd0e7ba55f7f19b4413b02c4617e2ab9f83ef081e0e3b022c21bb745263d2e4125219cef640cf0d3ac79ced1313fded4296718b03b3f73b872e478f51846f89ba39f634ab3aad4bc9fa33ff0d452af0bc2cd728080c419413958f8222c7762497a7adaf4fa89354336441687e808ad56d412044ab7ecde4306bb608e6da10644889137086bde7d27d6acf2b4746bac4d9656f5391ba22ec2df9aa11b4cc1851056b690f1002028788006195698800a4f8714a1ce4be74707a643a5599e9d399c03766d4e07da2a37a7b64a0e98a51e7b24f5d83dbe782d822a9a009ac2e9039fa3e3ad6931aa21158e7ba045fdb33baec3b290e3a590580e175581df61bf9f4ea7d3ccede9ce3cc7dae8b0aaebba650ed74948172147a290588f39b1a39da4f75543a14cc8331f28048b102c4288c09c83e2417b100441f0ab54248296222d94cc39907bfdd9d8f986124c589a685401ee74d93902c59d980ac682b0cce1da89d1406f95dfc615c61cfb25bd262145ad7acdd0f401bf82ebb33d42c298544851f7696c88b80b6950dc111263c56690064518254531270c29ac4752f748da12762f77625df6baa5cd0a412bfc582184e584c4aa15b7cce1ca84b8650ed78e979da5dbd73cdb5755a41aeab1b1f4d8f4deabc87d5684f352d516559639390c2592306ad95c4aeb48f708130e89a295ba94d64e6ce7a859f42052b37088ac85bf602fb80bde82b5601930166c0557c1573806fc02aec2523014fc043bc14df012ac04af8093602418057c023e828d6022b8043c021e8285901225041ff000077a3ce9001020801204bb58043c8549c02f2e827fd8040ce3143015560103310b98096e0107b10b380a9e828518066c85a9e021be82b170164cc45cbcdf734449cef27e0f12257906eff72451928bdeef51a22483f17ecfd20bae56d0f491790b89ddfef60ec60929a2aa267b24ddc371bac71f0ff07ad94763633e634fd4c65c6fa35bf336b72d263c0e4b73d0ce9cb5d81fce60587bd16ee05c647d9cbfb01a38cfc0aa70f6c2fa70ce627f9cbbb019387361c7f31616036722ab3a6b612f70cec2ae709681ede18cc5b270c6c23a395f61819cadb0e579c8063957612d70a6c2e69caf581ece56ac90730c6c05ce30b043ce2fb014380bd9d5b98a9dc0790adbc2590a2b817314d685331436026717d89df3131602e720cb7376c23ee0dc024be4dc84dde1cc84659d97b00e38b3c09267256c03ce40b6e7bc02cb80b30aec0be724ac0e672ad6e78c845dc03905b6c819055601679885e17c029b80b3096c0ce7236c93f38f95e16c8445c0b9086be44c843dc0f9658f9c4b600d702681bde7115826e729b6751ec216e02c020b80b310960067979de1526c0e0fc2227914bbe4506c9287c00ee020b034fc03560007c2d6700f581cde016bc339606ff8131b80f7b04a2e6359b8d49ee8476df85b7bd662377006c3fa38175915ce5fd81fce33b03fce5e580d9cb3d8f1dc85f5e1cc85559db7b0193813d915ce5a580c9cb3b02c9c65602f70c662819cb1b03d9cafb041ce565827e7219b73aec296672aac90f3156b81b3153be41c03cbc3190676757e81adc059c8b670ae6229709ec2ba7096c24ee01c85dd39436125707681e5393f6123700eb244ce4e58089c5b6059e726ec03ce4c58f2bc84dde1cc02db7356c23ae00c645f38afc036e0ac02eb734ec232e04cc5163923617538a7c0c27046815dc0196663389fc02ae06c022bc3f9089b80f38f357236c2363917618f9c89b00838bfec3d97c01ee04c02db3a8fc01ae03cc502e03c84657216819de12c842dc0d965915c8a25c083b0491ec5e67028968687c02e39086c0dff801dc081b036dc035600ef800dc0396071f813abe43dec0d97b1aaaba2d245a8021255650fa3db9f41222c7762a50a76755e2b00b5aa2fa428a987f87e0fa41e4794fc4b2145b747d2eda01e7b05d70a535678d95871dd0dfc876be0efef28513203efef2c511203eff3b8287981f7795e94ece17d1e18259dbccf0344c9f27d9e204a5ae07d1e214af2f03ecf10252bf03e0f112529f03e4f112527f03e8f112525f03e4f8c9211789fe7889210789f0789920f789f278906ab21a8a40cb586594c95a9111100000008008314002028140c0784a2b1502820a9b2643b14800d8ab250705499085a9263ca186208000200000000000404a34103ad9cab2ab24c6020ea6d7a2f35c769b9ec2bc8072869d2ce1431415c92a27a98ad2ec8c34a73988b66580cae8f803ce74ab577e5ae22b344b802a0759a07f4806106592a83a393ebb617650da8cfca3705aa6c3dba4d93333430d94697d6d9177c728e8e3200ae391d38323e2305396ca4e24250a6a1431b6c3cd93e800020694cbe2b7d995ce1283ef79e29aee52a7aa41f4b6d2ba7110f7d5713a741fabe6c0996340bcee56dd760d6806d7d50a1695ba39458b9eee50e04e5c0cdc10058237c51d7f4738721c650f65c202eb2a1ddaf79d3930f8e2df964d290867748664ba6e61aabe6c1f5bb5bfa14899283fc198ed29717d35c4707a27d861efdc4360c5f7599b1906254323d7892c3b3bed4a0f5a220d4ab571060d4e322c815688c1d712cfe416023d5ee8769ec0bc1393b1749b913ab1c38fd897bdd859cc167a4ae56ce703e04b660f0162d60b7d862ec72d52d0b44b348d5b7507ef9d3c5869cfc7b2d07cbf19e67dde622777a5888074a2eca9b8282209a47af7f8d892c7e2f727906c58a7d8e842662d3a310b9acc06289516759dc406692c7afbb4d29b9018f1c2624952b5fd7b13664a5f424d1e01eca98e5eb6ff4743bf3f5c1128f61a256645d9a563469207ca5566531bce65fee7ba3468f8f8bbfbb65dfbba47255cda462cb6222f731dc69f2ba430a240c9b12f0d4d6b68a2b95839da36de1940c9bef3c786e1712fedf7360f8eab243d80851cb60609d932a552815bc6763049dea0ac3997f243e006cea759075a053ced5df299e2383620b67e488dd154c35108dd95de2f81d7b738a9be72e3c667caa369c09506de88d4fdbc719bc0033c4e52058cbaee1acafd2b614236e3ebb141741c14d0152200ab0ef770e736518f4b09bc30f301d859b947fb04f028f54634c0c79541e28ded132b8c6f18ab0ea5649f26ca7f1c886e48b8a8a051f3fb22398ace18e989e26e8eda86013d8331640c8acbfb2804c9ea7a438b8b55a4fbbbffae24a6632f56072a8976b4b6302b69b69f101f1ca7dcb7fc0acb04647b5d50c648398fc14e1e1c2887de739ec33ebe3c6a269e143b3a689615d669e35916641f33fdc9680c0613bc6991eb80cf841aa141a437388b0d51c6689204667c20df7720cb0657e4ca9e65015b4af5bc1e0150d5df7a444cc7a2b55b9bb378d345609fdb8ed831ecd83b9031c1c53f7a65dfe6a50ef392eea0a955fe74d6bfa94ddbc49a191301b13c02c7d7360620210c4ab034ccc395d788cc60b35a150beb8511056c91d8bb31166c177d3feb926bef567ce114a61dcbb02e392bdf0020c8f0cf3523c768c6722d965aebb8a6a6efad42a9820c43e19608a97e46ff38aa4a4c64447c4ea3e668d5e7d84194277173ed386650e670d81a8ca3df352bc469414a4a919362bc37d602904ba1580e5618ca159fc7ae53e951588daa10a1473836136feeed46c67eb50a1e09a25a1a6e0fb3be4b876790b8a838e6bd5eb48b422149f4621d539f3cdb57019ff65c5ef50bf23d37e17ccf9347bdf4b826b7e0dbebe09c3505a3bdbde70dba9a588dc7089e556614d81a3021bf159527036f1395ddcc539641b4541183304d52ede412e29d3029b8f17365e9bd401fa03f4042f994eb11089ef1d1d6f25e92b0389ddc59c9f91f0c97ace373cf572f2bf2d62473c3955fc80cf27b8a2fc0b4d79904321f50a364e609b0d1e33dc2e4fb3fa1416fc4c66c3b262c3c0697c80c5ab66b45849276add0ac6b3d4242fe3437a014d3048c25ca8ad61f8c8ae7bb2369f9f0a22f988749b4deb688596532a489253e16be23d88d77b915c5bc31c1f490ee98897a4704abbb78905a2e83926bf9a2053eee97de9dc4897b5b8b6001e44262830d8c9838bb008d55db85d0e3e35ad163c16a7d9f67da5cc7e4fc8793a6de0a8b6e8cf6325090a64b09074eec143e9d58212747c21030234e963ae1a62081c8d7bb239ad32fde8fbd5536f94ba9ebe74fa1cc958de137701387ad2a423afa47be0d30f5e7d66a856cfe8daee3de11c4a132179b777a53b7634bc2061a0ba92fd3d352b52487aea663473a491527fb15af13b47dc936ad552119fff1e30b9dc158870d1651292ece9849f3579da6b89f8f3b86e4d5fa1c51056ab1314310ec47ce04978da46a2e2e55fa767b3fd499b19afad6ae74d8dd0c0161f7972e4379c13a0d715206f407e5f94d995df0999c564510bad4f228a7f62f2892636085e9b96c9d277839a2eafcd2b8edafd13de22bff2696b2b210c74b0c43f4ab42e4bf68ed51a99782b688f1e08f8d698c85f2a9abd2a6700cb956953ff2316708cd730a80b4372d213bdbb45456ec92d8cf3c1639bf64dcc49a76512b3089f10baa456b22039676383a91386bddea8221d12d90bb8c64a050dacf7e29bd18731b5b45ce4c2a2d876b2a9bc4c218617061e956cbda015e8acc4d7626d021af2f7c3110d5c43f9863f390821e7a5a068ea5a7a757ab7de0e8ab9f4378b2c095f1eda3c1bafd3a0f46fa4961401bd50bf891395801d6e43c310cde64183a2388db67beafcbfc00fc4279fb3af8db56c12a3443ceb393f65621dae00b41f32c87fcfbc7c725479a56d3b8b37d231a9bbe85590be12951c1aaf55a9a00e2b345f7f94292dba16ee547602aef63a0e1fe800b40912a45ddc8891b751f637a4d4bb57d0354677127e32e8825b271dde90083c98eaa435667218f003ba889dccb5485cf08d6a7390c9a5ef0af5d27e5e73a0771f55201611ee3132649d8aa081a4fc0cc9a2351d641001e779159d129495806b133beddeb75b9b72f44788191c743449c5b2ee98d6bad088427940409f1e04a5fcad8697272fcd5bf4c0f5e09e51de80e159a953905dae7d4966faabc1e85f043bd55029166c20de0497247105ceb9f70864a6f0a425a4bdd32494b2083450f7be969ea6007bd4bfe543d0b7d7a2b03ab7b1def861c6c6ce1bfffc51d13f2402b230233b5635897c6989d1af86e09461fe7189607da75904ed5657b71afaee777a7f516727a029ca4249239cea847c883ea0191b2ee69141036f8d60ea16a88b69825b7fe48b64e82f63c623bfa7c92146093050b4e3fcd0b0fc74f2d5ab17849de54717921435482b38e72f5deb07a9d8fb03e3ad312082a2946db08cea02ce2b587274e91874fc22b0274b3fc62c20949e4417ea0dfffb3f1189763f732b3a5ddce8bb043ee76437dd088c5b63e43b84907599b4d764ee7a66ad4b4681eddd62f370a7c8cf08d30b338331c70bc4452dba8849072345ce466f61fb751e5a17518fe8a5ac4152be18e8136542954e5f3517ebba8a106b38924270f07dca77d77ef344b47145a7363b2456cf14e0e88e4aca036005d0dbc9284fc3ffa2cd5605597950a2710a602dc57428fda53feeebdf0bf75742f7228fdb269efcf8ee666b7bea6561571a2461f46fdfe5b421008d52031c7f87e4ded41a067a7c79b4a1d1d11128097658b7dcbd6a7964d02d01e409c4ae43281aac09809a44b0228dcfa2309e843e8d46d554478e9d901ffd134cbe47023a1b186f47f886ff975c8cd32a60b4062c84466a3af89faff4dc1ad1d03a77a089c1a3fb49e767700d028c4af719651cd83e4376baf50c0301cecadbd5aea660bdad6cee8c417d75b9c9f20c0d73ab6b0b2d58ababa426539885c0752dc7b08a01c8582ad80652e29a65004e7cdeac2c0d7fb3a3bce93f8edf12662ddd031dcf68ad4127245675a73b61b3d23117f4f37e3aa1f853f6f29d1f5f897b3408fa0e8f4c2832e64b81b95e8086850d2b8b2feb37133c49afa9fc6ca2a25133505bc01401639e7dfb134975e59df1acadc6142233387a6c6db3f3728a001e878b37299f64f07a55455e86a3eb77b772353bbf3877fe5d5a86c373aa0a3ec432d40819e7f4a6edb973c40cd6cdf42981540dafdc70fe80c0f64af76d2207d08a7b544962996d77fa5b65a0d6746bd93acfd1c8395376a73ea94fa64beb377e32c8ea3a23668b304f6de2067680323c2a0400310f29015c3dbef93d7e41dba67c5ad67eb254bebbbdd426965d47cb521921d65f819d69e304c47e556363c3263a774007d8ef31c1e508c73d970221c7393047b2374e130fb8b59e148dd171eb07972cdf34c1eb8df548cce9a28c51003ebd265aa6e8bf72f601987b9bb9c5ba7121917a4f269310f4c4002c85025066c2ea1ba58e35d203b03493c88bf8234b591d50187e4db4524c314c33ab689ab4060cc169d8891bc419f75f00a8b319e9998a3b812d24f353570a8c52c6b39ce2654b8612884b34b6fb4c5318bba707e2dd7a1ecf60893ba3028cf70b78a4ae07c7847e6c25a72f1808418ee569b5727fc62b03f595529a592556153a33809e2ceb8872c145ab15ea1df1b05c0cf1fd2cf1d55670bae450d1e8576c89a93bcba238a598923f876ca1ff781199c9b1804b165a64bf2221e1085cdcc48282f1f48fb9a87f58cdd0b92c5b1465b8163df3f9aad8ac5945c037968653ed0667913c9bfada04b5946c75b4fb1e52a99a90c044781c0b6fe900e46657e4be16f3e8e315d5912f4d2785481e082ee6c68563e787218aa291358183aa4d372a63af7f2a99b3c4e2c0dd4ec35528b951968347a30c58b60168008cad523ea8ed2fd324df4f1b0a9d2eda0b43ea4f8f2aa89c6ca90236f5990240c0e0434fb61550a7e2ce4dfc9c41b631d1819511b9e9fa380b285c2919da9cc9d4f1beddecbe3ca30ebec672b367a1a68b1dfa75a67d2af3a96246ca794651003a104c392f0f34ee12d69ce54731bd8c446c030965d2cfa3baaee371e7d6250233023aeffa702aad704c02dc93a94addf52cc517e3048dbd0b4fd91186782726b1505ceb436a227b1abf062844f7908723cdbe38a340036c7014fe04f8551fd864f7dbc85f9d5057059d181ea4052d69008e2ed829bd9b4aacb1bd96fa8d5a5e6fff2bfd257016a4f5e00879c9e692f8353a3524e220550e31bee1326c6e0addb17525b527fef8915ae83a75dc70da00a09e1422e93367a325f509cd4d81d555f8cf402c7cb35f39a1e2f8ac2000389227fb676945e020acfa90e5ee6aefd6aaafde309f630ab925768f1280bae8d0fcc06eaa4e995c3871095d43ce321fc3be25c24408937a2a28741a275a41070a34e8f76fccb74fd5464dd473ea393d3f68540ddc095bf438e7bebb4a91cc6de086cbfb043a263f505040248c624cea1dcaba5e809868da138b57b8485a82a48b24aa09798b50448a4aa9adde069d0721148c01dd469aa318fa221c316fa898669a56e3543d482da607b7a1b16718e737a031b4688beb1a26bb7156593cdd5d271632031404b29b8c6a45ebc5b032aee22a1e95f21eaf380159b206b42b12084091f7660951fb201810e06cf2164d044f002c20fc9a8e75d72c47743de4681af52582c1830698d10af56e0118110a54b8cb535c0ed0b545e419b4d00568412020a1aa770c302b5bc0e514f1d2baa7f9b3656beac7b20b3dfdc1ff4fa18cd47616262e6f063151a157550c7aa4efd2b3c1c63ec7aaeaf2e2098db416f0393c9668872dc73500a992b91801467799ba95f2bff78e47c011d88659b17d61da611c7082a4d08378c08b1ededcfc870b540d32ae30e0bd9289dd65020000c4e82afb54b1b4707749d9856b4638e17bf04d6c7e00bac080ea9d27ed9a35d8cc50e4e8169420acff7ddab3b7303c67342c745f66a8eecf6873aa3efc6fe92079864dd5ade19750223414308db7736fac61b9d6f50e574a08309c450db436b22d9c00e2b61878f77f655cb219c2a7fa7995af41c4fe44d7f12a8f7637780dc5e66d50febbd218bc18082982667dae442910ad020934a3ecbd6c8a61ac8cf2be07f2e1ca3ae0617a4f4c980c9caa13084c87bc3fe8120ffe938a0b0eecb66b9f65ad2e4d5684bc8016ce7ec4609fb37410f014ffd50eb59752fc9d0e0585cff396437d9641833730bb57070a8cb9f13fd471fb327360acce5a580843915a5f401ca44dbb9e9a499dc29a9f45329ffaf391a82c9569c00311411e85e94bc9c9109b3890dd0e0a0d36111f640dd6dd5deefc77df184541aa60b3784551b3c03d0b8d7c42e9988186763c467b910198f7fdcfc64dd373557ebca2d3258bb44a0bb2082f16967dd9af5435c2b2715035cc0cae863d58318a45766c7f34fb9066e4a1d3d62872c9431b033dd6ed262b4111d9afac7dce646cb7c8fab48ec0e3bf8de01deb55d19e13234b2e9bac38de84de2ecb957e6be386440f27654cd8d433f263304088e4c0c6d50bb85b6903ec528064db8a3cb637d40404aba81e32916a83429ac0f39c376330f1e6a48fdb98ecc23e570aed64317cbdb6282dcf2588cee2ce1db1623af306201ee98dbb1fed2019f73797bedd550200f0a819986376ace3a71f1a197237f1a34baef1a99bd8f53122bf01e475d44e0b469d2b7504372aa357fd7ac3335508214cc2e49f7aa9da0c606e54f42b711fadcff7b981303f9620b1e040fdac1b891af09dbfa38138a1dd9336c322525b7aa7735a0b6212dc5a5e112447402c43989bdea3ddda0e56e6751974413d3293fe1353cc73adb87d19621ad237cdd0b32d4c127c7b7a0561587b078fd96201c0adde6b26d6c4bb1240de95e113f3aafc9f58a639d7b8321f977be2f64ba4ca6c0e501e831b75c28380275239e843d5668cb980d1fbf9223886f1ec6dc3d14e8cffcc1f57a9051e7c66e59b495efa4887bc82cf79e9120ee18ca9ea6a54451fef68fd79c682a0babf029ccb40860a601c62251062bfb2fbc9b7d971821a34f0b9a213d9ee50f151c11e5f9af6337f482a4067f85fbf15ece96ab200b8ae88d4a786f89b867ce4be1d86604bce29baebde31482ee51a322ca3604f847a70a5c89c0ade2f8a649ead34d751c278384ababeac1980acffa32d688c2f499d40ffd8c52c09d096a2835cb12d61274ff331ef7ec82959c82819beed01dfb8bbae539ffe19de38e32da8e181591d46c061abe902a2f16e047801a03f093abfe8140672b10e33ca3dde731bb17e6008bbdcf79543ac5c7c49acdd176713a70690b70b3fa5963c165979cb2350216b4b2a00b31741efe3cd1cfb85f7c9280ceeb0142ebb8cd03f76574b36b4d6f2c86029c8dd12789c1cf6d32d6427f6928f58cfee5aaf3af55116feac5e6385a53c465f9147187fb8cb8a9b7a2121ba6e3b720d4906583fc50c3e33045234041a1e7dd283e458453733435d8eadd6184d231ca9999e5864ea8485aa1c0a351058ec24472bf99abd597b8741bf01b2de72c74f10315a1cc76ad2ac343303779945f198ed0c66ae7819c2bb4c955a462b96ddc3caa2b8ca76963257a00ce14fa64a26a355c9ee8164517c643b13992b4286f02053a58fd1ca63f7d0b128ceb19d6dcc151a43f88ca912c66875b17bb05814afd8ce286685a9506ee4077554bb7d15d37462db4bcca1470ce920a6c5c3e8dd6157a86171c4b02d16e6d015867409d30261f47eb02b64b0382ad8160473e803433a816911307a07d81562581c2d6c0b8539f484211dc2b43818bd1bec0a152c8e08b605b7813958f8b13cfa272b4b48efa1bbcb1087197701b23a548a7bcc563c69eb0956dc05a53e5be68fd317b2b8155f1da334920b47d73a9b65fb257a17aef6f7b7447b36723af833ca10e2371ef4ae1345fa4d9121565a2526baefd101183b78a5d1ecc3870972b7c1eed4c5bfd63d9ede14b9c5132622aca740f8da8b60be58d70b6045b6184bf154dae6c4ca767f45d387f359c71f4ecc48c629d44840ae9bf110c33b0d0d9fd8fa25034530c784311f917d9207fa03833e7ea34bfd8d142e35e52fad4d97a9c1d068295b568c64f6201de63419e6c64f6f97c3d2da099a70907a15cd50590d7ab25308576f4d56ab16ca1f1294fe3bab44bb3d1d3e5d9c8f5f7387075053cb542519fa5d71b3ee9d42a103dc807f6f0ef79c39746bcc03dfe632ff6d534047a2fc51df0ce396227dda0ac51fe6e5befde2c847c4f06f53167a8b1ff5da1400de20e77e7df3d05764e1c1fa531d434e6fb1bf82fb9141754d671701882dc30f8baf2b5434a27ddd3e587ca980a211e1fbf661fd75051585085fdb03eb2f2aac28a27ddf5e587d51812210edcbf6c3fa75851589285fb6078baf2ba068449d7d3bf63af830c06f1f072d54195b37e28b844fc7cbe4ada92107b174a304ee3ba08bf4a8b0bcc78d3fa693ceac7ed2f179d4b669697cc33dfe4f97e6596f154c32e88bd2572a28beea0c3ae81952b1e8ba057f0cd9470dff19a24fcb21bb40d3b3f7fe35cdfe874944fc5758cdec57af0519e7249e258c0e9d96c872f2e2cf978ae275fd0b399778b4a1883907e8c7de9665cba5ce020f29ee1cc1ec4aa5d35b4c5c7bc4d2a117f925dc2cac5d9649f5aa94361e073e9d05520e3dd010f7af308964fc50c9013f6d00a798104a3d791187a929833d9d83cded6e33e73089ff7b3cf7e8acaed1c046ef786e1dd910c888d93b942ffdee307f68e380042bba79d5008078e22e3ff6c052d74bbc929f9e71fa5250f404fcbe726ad51fbdcc2ecbb197824c866a7da9a0656cdf75b6965642995d575eb76212ff4eff71fbd5390447ef541af50c15d28e69501a620f403c31adf3099ad6d790fdb9fd711793baf6c7e0ffac6ce3c6e8b52327f5e77667d993196c914df5eafae234bd1afab0c0ccbf46c6fab8e7a46b609c47d92af10eb04988de901734b186177f9c0b598288612625e0d740bba8c32f9b1271e56d3c00402cbfab368682e5ce33a23bb1b1223eb03a53e7f0f7b893848b877fc7de4f1c2b59a789e3e3e71d42825d1519dadc35846e62af310233546870a0c695963ae7c28312526dbb9994a51126ccf7d24f06689b39f540f24028f390102b8fae12470210f2d1b9659eb18043bd49ad8d9bdee05513a208c1066dd7cb2b53b3af515568025dc5537f3d470c2101c2a64e773fac3bc17ec11f3df2e649f078c4063480e5e223970aeccf231a923a40e692c90f778b52316643d8fda2b502f86a6b9408b526d6a6a1327434e80e016ba99fa4d94f0565b8650f9f9ac75ec0a30f1eedd2bfaac3188066e54384b88eb0675939bb9da31beb502f852017a2c8e111dad0429bb252aca025f91a943111be97a2d45b66c3a5c61e2840a48dcd0be04041fc7b0f2f8aae0f93af9c2d70867c660d03bfbd64b7a4db54de1b1a755acca6c7141f6f1a62cd2a5509b642c79f764c6c1e3e2a203ae042c1dc3dc1e2765d5dcbc82f318d57800e0aba9c3c1ba60814b7cdc5fb271ced25bd6f6a71a13cada7cf8fb60096399e9150d61acae5e27d46784066d802982dbe372190706693a4af01c15210f253ffbeccc2c1a8e8228863c599cc486dd30576f83780f1c5d346f48518e9f4f9dcf2268399c8febf9f993c810e15aa9bdf0974df6e3b8264d036a93ee1a0cd541d678e617bbb9f5a839a86f2bd2411b571346b8512187a388c8b69d2a1791d3224c5031a6da12cfec7e620348930b0193d5f1874616ab99583b43ed99b48aeeb61ff4ca48d7322d8d82a1f106186b7301073eeb15f58f9b58fd0fbea06c4fe9b8f08613c0c334e3e869ff9e14f08dacda82455232b11f8b6a876500fe4f7f4ee76cf4c99d5d52474e4957c0f2a267ead9fb4ad2667f1cc7533a8b9c53acc212b7ea6ccc1819c95b120f836d7ef92e19b3f2659157929d9ce399ca00139e42335f855724b38f4054d2e36ae9055e4084bb548c4e959cbc5901f7535546fea88c61863c688be424f5e9e307e243c3a62a42a4b215952419c83f09a26cdcf80a88af1a11c31a7ad1a9e00704b314d2379c0dffb630d0fc54eadbeeb2835910cc7e473d9a3e8e99ceb572f46e02719cc6b363c8c1a6dfad397b574e4f00beab831d706341fc6c68185a01812249139adf1aa9515eda5a49a143f928970b7d7264327b2e8b2d4332369248080b3f922c145bff03971afdab66c82ab807664e8e4aaab9c9df97a7d9b8661a9b26d27186179743d074cdaf69d76f422845a744e9d7b15fd945a20a1c2cd01e5d0e88542bcace9802473d0421517bc6633e9f9d30ee8b18fbb88eff878f7dd669cf8d6793088f8102a05407115f8fc8261d1b10bfc778206059f29b3fa2605c1924ca4e6022a882e48a8cfcb5c4223838a1315359c8324ce92865c1c14e1e22193d3879d82bc47e44a0ca4f7855f31d83965a2d716fd05484178f48114c36149d62eaf296193bf8ef05ac5608df560f2c5f4af31881311b380e1582435a6140b083fd4d030b0651c93fe7d8fc659f33a54e14b68ce8703700538bfff6347096aab9456f903964d020eeea1ec6f9690f3092e0a192367cc9be1806432eea7641f415d6dfafceaeb06d15033688e20baa30c93af04b5a4956d8dc163a788130d94415eae6db083e4b0af50f06f02402655340e3a000127654f5f6fc55e6123da137f8b203114e11ccc928cd42d6a5403a60f3b20e29f381f298bfacd01c00fe67922dc896776b82b8c676200558d37ade26c50bfcfee23a80b8625c311dc00b0465c9e5863effa4f601f81b65bc5f666647a27612fcdb5eb9de4a9d101bf9314485b2c6def34efb5c52c57572ae3b586ee694c5b0207444ca70274fccb2ead251b1712ce63c72b977889dc9be222b8f10041ad0302790f75cb24856bc7d6d158167db142f8577ddea3ce677d06d8d1d109927a51ce741176bf6d14dc4d7017a7a66e0a8e5e6e9c5316c5d710522a44395e489570545b2c2e1edc21aaa755944868ee15061e363e71837c72149af44e3691b66c83312a247d0ace90a1b53812887550e37930692725814b0acd6cd6b4c6088f1fe0f281b9c443bcd14c769b23a03295e24d1fb7944c29dbe866a4c512e475a01c862220a5647c5e82e0e3787258d8620d5f61f0f34c48ca854865aef7378accb5b43a516455f0d52fcae8e1aebe56130442072ce63a04800498b1e6aa747a60964f8c7a39216709b6d86bfc759d42eca469f67a081cd2b7562c2cd17d423d8e1c93188a30b659e556dabffb93748de98f70a2dbb98e8141fc0fa238147ef0346c7d605288e126b215dd6804465fe2034648a097e017da85aaf88238c419cc2339c091c0e44a84e66eb63eac9b41054b0124adcd00fe74dc22dbca2d1ab32fb0d8d561a10288b93e2a58e5e7ee34ef15a99b83975a109c99da6075aed66cb84bc013348aca7e3a20b9c93682cf7a11f0046166b04032437f9433376ef415e5be98dce2a4fd0429ee371f9d5cccea3a631d9ae48403bb025c1ac2c32c844b5487992f2a7dd39d770a55d2ad5ee33b487be36cd9e32e858298e64c9ae2ee2e66326ab06e1faa55997970a7c1a6e2060fd3a0522bcddfa1bc3298e49a679ee75fbd8a6f0d8da9719fac76328f7834238384e9ad7019091c99bf777107ab796c7229328d06795e90b9cd53a123b003b95d24929edd93515751cddd12fdc7c6a30389bb86c90ee66b49115f8a7ed2478f55a988df864f1d04e991e85350426224ed945213b561b47bfdbdf7997e5e08dc01148d9fda81883f6d9b96a32cd8df3c014c206267005dd4f889613239963db2124b8620c2e6297c8fc8e5e1fef2048da52c70a8ad4b27d959734d21a7f3efb831932868fc28093ed67fcc0ce1c9a9797303f596794e3988c2a37c8f5049960fdc5cd98733f1ccc81c0a4af6c0e5f228993ed2e035be382a8e7e8e5ebc3b53809dfb22d702a68dbe490ec407201be949839e85fa0043601d9cd5336e44fef241747eae2fae301aaed33c17bdd233a5d9ab665ec3cd8640193f68e185d3ae922d268079051914458bfe2fe86e59a5a08053bf9182101cd5a4de37065cbb9ae5e58cc7721ff3b74d25016ec711d676f04513d2022a09f8383d62b130baf91ee0f99145546e40e9cf3d86fdec2ef98d7c3b7e4f20785a40e5f3970b220dce08d13066ce0d380b84e4ca841cf01176d86a9cae8b9a5f2f0748953685eb9e297707c6882cc282dce3a6789a247700e24009c0e4351743dd2a9849780b834c00683dcbcfb2bedd5c087c1b869009341d78120f5633a2a2097c34041fb78102862a18e5cd6f9068cfd1507f4a8983401322f1436249ffea01a6ccd542ef2e0357c1d9a935282a0d980851b64aaa8f649609dc5f01d193e29c0b29924118cf07423a7a1bdc181f595fd0dd5a6310aaf7a1af4fb70c5057529408a5a6457259a60a09592aa959d731fc1ca9662afc975584e01bc2b3f3b829834d00811678ac2c4c0ef0eb4078a443c09fae4bb7044c5405de41ee1c062ec1271e26673f1c6458ebf714c187c08d43f543fcde3299be0401c500caa1c71415e2c936745816264cedbedafe5b0a32290af7ace5d8a2f8bcda6b40c13629900d183cf75231b5a9281f7de9ad3d607457b9f817cb9d4900a66ff105a8705eb56a44394b1d1f2fccc45aa1ea429e58bb5d5d588ab561a05c5430f721582ef98428509f7b542cfa3bba6a7386daa99fa5118d5331d69a44ab46910d18036bf7e46c8865059e44adddb53a5ebac2292585a448c508ab28c3ef710c0bff592306a9519457bac8395cbc4e283fb49f3bc203854eb70f430b513d4adcf328679444be0f052723eae4b21d62227ff2b2b70637c3a1631bb5efe225389450e83615e80b30768fb2f7af69c9df8e4ff82619df4e0551874f1d831fff75d65b34849a45a8066f2c70e65e5fd686d0d1e0fb24f7e1f46e52afd6cf4fe5388796916d8596908bff708eea8d5b5cc38ad7b8b938849fa8cdf80c399b4e2e8a92fb9d37127d7c894ba7c1b8acfadbd3eebb3359ee770cad81860f8c80fe12dce2ffdcf6a74e19fa8814f6445b7d484cbf9fd6c99206cd73ab7847b32bb445e868fce6690d82d6faa67bb4248096095c606be2e97834bf81dded680c679087a09f2440bdd1b6871038c5d11cb1f576c8cca4cd8f1056d2c6a75d53663e831ad3f0fd56e19785a02c4b6c9c5a2c7e47f63f5dd8b9e27a2efd1577911e1eb593be7cd00cdf6f66b86c62f3b0af73e1626873f7a196555f8ffe7b579e28e102ab7a2b7a01f4ccc8fa17c2ae2c2032e9977707a82c3fa770202fa51cbb1cdd6a61bb460234eccc6564c97f8b9c99a297a0cb11c2e0cf992581cbc3ca367de1d989bd9b0581c24dccd8273c4ab4d17b98279158315c5b98c4dc46505c1b7636272e5b511cec700804cab5856970dd082d88505a96040b3b9e2b5604af08a24f396b346a4373b79534386adb6849918c849c93759c2f0baedc536c424bbb73fb0213b7aabdf0ef5daee664fb128f464cb1a3070407529c9ada5db600602da1f3e4463024e5d9f4dd8b230d17836f396f9d284e7fd7b62d6c4f15d120d0c432136975bfbb3d725ff73332b6c025232f91138263128c449b80bb93688b03000adc4cb7c49ef9c83887f935e7aad69a865be661bc8014c677668de22ddc11e118ea208087288506ea4cdb79ac81adad0dc5e90a9712702ee21b644921b993482290a18394d4a68b121c80d67c8265aaa207c405a84321903ce04c0185a461818cdf0d545c342e262f000e310db372cefc91ca81c544a6dfe97e6e330115404cc15c8c5a79d20fcb92082f54b41248b03dace430adaa5db4a63afd137e96fa36a708d3a25c4278108feb12149aae4e1d6d3b83c9bc8520a674e28e9466245e96a8e978c20e6b70ea131a2d9bc72cd0624167f6e327c07088e6f970600353942d04e5e900e68188e22c87826ca90a004cdd11cfe77aaade78f0437a2581e5ce06ada8f1f856cc641260da932297a897d227a45e2df197cbf36736069a123daf0ada390147ec3e05d0450e18f60a25135ecc0e0762229ada44783011f631c70a314954d649dfb43078935990353f46a7c4b1a292ca66091e472559d40900b400fcbba936103d45ed127e5a4b0e4a86e439ce946a322fd60039f53f112a2aa237d0dbce6f520303cdc2deece9af680cef6ae833d51e746431d4112a5862c70d2ecc6538585b4fdd5710482cf064eba2d851fb33524799c630ad65bc2a58fe34501a1cc0b1f3ce4acc1d2350b83cdd37fed7388e9856bc51eff38b184a63a7a86912f03c1f469ca6b3686d7c859a4711b26c53732178d4bd33c1b0af00b6cae44c512a379de78f9bedf5ccdf6f7de13bd4cd410a223b17fc60c2b493584bb60dc5256ec0db29f35a67cedf7a8b5dbdbc20ea8e1feb00343445cceeff1e3de74511d297e254c721d6db15ea9b559f073703716bedfac4c0ff0bd4b489503ead95f5fcb38f32bf3123c2eb8c1ca66c3a1d0273fce540c5aa55ccd56d3924b7507b6272c53c738a7cb8d07b2e5b416f438cf33dc52d28a28b7f4b4491597ef08f0a6452f633562ac3f2566ce1b3cb30cc7c30b49b51cb311874e3ec4d014810900e59f2505adf1a74845b12448d55ce244ad90dde23af8010de4d380f631c5c0a12ee6bdc040e541ebd3a7302e328443206a68e8ff890e9748d765d918150dc9b80720a3d5d5181c6c9813a32d729a94fe23da705476721c82bf6137d411239a1e45bac56b2913729fd11d889425f0e8fc0349e316d9af8e27c8063e56bd9355cab1a967e7d35ed164d20815e160f4e06737a9426c9c65180036179cb3bbf9dcbc0a5bd1551bf0e70489c17f75b836a5ce8185810432fc3d20018588e9c4152fd92fb52badd36731dc5e4421409a868fd1ba78b4652fa698d0178eb9620d808a1166bd163861a7bc21b3518ced71090ac78a66fe83a3fb1ea028b8873c8ac88324b0c0e7d014bbf1ffa76a07973f2b436d32e5c02fa68ca41bc5737205039a08a9b82a185534db2a7e0713e0ac391bc60fccd8037b0455e42b09f1c0cc24b81cd2851ebe1ffa2e9585580018536973bd0904939b84d8be4633ec55ea91d6c300f8e88d5c43a9b3406616ee5142a7ad6eeb6327d0bd0e990924691154cf4f41115e2b290285d1c2c87ea03067bcf85da40c43b361614e0bf658a77a2d6d6f76b3c35ab403de32625f7158a56fd2bd999e5a6d348afe0e708c2dff4853160989cbc2e2e6e71a7146627489c0bbd8ba5a49a04714d475c57dab4b75639fef47d3fb3793b31731b556d55631825a9676354d7229bf8b0956d4302be892981a3c49ea9cd2358c336ec1914cebe9d8aaf7b0a313278a0c119af29fb8c6b1b13e48c9247cf9f8e33a72af8937e4002dc1e0fc8bda1686c9ef0df4c98d48561a16fc594c0fe96a1632a83e3990353565cb493fe244b97733383578ec29c6140a5a29d90ea298d76465a35d665a0edf86575e1d4fbc7cd81b3eb8a3276de2c74cab79e77caa2a67ac96a1c657299c0ad474ed8bd97e2166fabea63da8d7216e0418facedc31b00d4e1c12f03f19fbb64a5a47d68e7c9a36793033e2d5658567621923cdeda8e82a96d338d80df12a21c044037835df3e89829eaa62b1e299963b7a8b32d5756c0fe81822248a98ee2b6ff00ee37c2049182923bc11ee8099c2f4084c369944f02761276017c4c8bfcbdc00e41cf29bb2d4314701778f11dc501a4174849008642ec828021f21064e20a060201542aa4d199b018cffb1162d0fd9d7df57664ac4ce993dc4a5a632f2c382b54d742d551fc523bdc4942eac3ee11d5cbeec4e62bc9ea19fa8b3deaffc6f45a0524b5804c0cef3572e6cdb3b6b272876f908254a91d807dfd6de402fe3652bdf05f167a56d239eddec4103e96cdb3f0ff58f410d98bd54994761b00a65366027dcbfa82354ca68d7b3155d84f03850b518d228281d809b3d0fdc888a9e01e542351812c87a1ddb3d3c30ceb4aea3fc68f16ae177f96e2355dd6a90e63bbf3c7cc4c71f928e79cbaece482c7b1a3dba088c6447136e6cd0094a486d0c7995f97798713731ff8e533a95c59f8dbf30e9511143acc24c321f102db62972812ebd0e3d4e169803d58ec928356949371264ba5cf930a1c0654fd434cdc3b08ccd32401dad42e8be3f2d25e29afe6db0a7b4758d0f8db04cc2d9d2902b53493d3f4420a9968020799d9c4f365ce068674be976a40cb5495d3491bff6b5d63cae048612730b647b1cda157b5b918bb386f7eee8e0147c9212994d3ff5fe0bbb27fc47f60d2dfb1806c5f7d848dd2de04f3aa258dad904adf14e7c5243f95d99e0e71fec037d3c3ce93e42e019df0d11fec36099024bb5e6e99a58bba78a2cb3875e67f9dfd5a584642427fb7f8bad3c6e04c60812d3c33370cda202d1f8387492c0364d9711dd7f9b48e35c11cecd566ab075e16977c61a59932f5dde659f0e936174f8f168b11a4c49c05266db760077742c48d2090053e3f62f66dbde4f4630306e2f3f7aaf1ff80e488c0cd59f4b765618f6f716386befa2fed59e544b051ff41ad051ce735ddc388d976d63c75262857c309c1ae7de5b38990acbac671797f9acf9dc5c0402ed152d0868ce6258e47ecdcf48613201a43239dbd7577a5a1c922e70733f19a0774ed660c2f240b308d49d5bf3130224b045f0b9b15b76f50de02b7f293710149413ef06842cba063e11b819fd98b8a2633b68a98929bc4b14042bbd7297d5b5b4f0dd91599fb7191ae34368d48dcdee8c5a0fd070e5b2fe62e503d6af982fccab204dd55303399ba18279033dd3b42ae94da9c652eb1ea2c02a08f5795ecd664720863673b9b2d826b3999b1a62bfeb7fb8dd887fecb80947b81eaf3dee7cc8fae18e52e435e7a4414ab37ea6d2224dfebc9e021f3474c1260ab5e5684882fb4579702ec3b6c21722093fc9d5fd25d5ab9b19f3afdca76bff22bdc5e10dba1d5baf0f5ffb3e5f6f933b79e6d7247a48d424f475c4741cbb4a2ffe6dc63d2ff621c4745bd7bedf582916b1926c1ad718d7abddfa6f430745c2e14b30c84e544624639fd895c089357c1207259bb29216767fd1c8159ba800e7804271e7ddd73d1820f9d2006743cd5d7004d9e134f02dd795aaaba8c644652adf8034e992e2492f21674c0bcbf42808ec39316f15d8192c329009aa5cb0fa714d7facb269ba14b48d6097ac090ba5a42b4edc8e98a8946f52d3efc383ed6f3f1f99444e17f3f0e9fc14f10b7c3e159612eef4e26e1c1ae43f604a3d8a56db7ee6d825df827736234b8f58bc1efda0c223fad4f2f4b6e7e17454f5ec29ea5c2f82360ae9a90b8a84ca52d2784f901402e39efdb16592cc5d3bc1210a67a766b9e90a35dd9ce4fdd9672c37311cb11ad93720bee52f8ed2ee0e75a5a4cb79415949db800af8cf12eacd93745e488d97ab7e73632f4ac496b6333037f2af5dcf4230048e9335352b3899af4e043c3cf7a6ce6fd42487c4b28542e52e3745cd889b4ee469e8fff742e4a95d095d32e1a2c3dec0842a2170ebecd3ee1857e939dfbf060aee844996614bbbe797e69724380fef4a88d7db33bce6313687b0a4afebdd99632cb272fbb8fa2f7dc4789dddc931feec52d3905acc327ee1ebd923ec42c9b993570a296d745fc57a2847e798122bf7571fa8f93e55feaaa2c2feaa03e1c0eb0f4638346cc4ba6d54586bcc1507432b1ae5b6ef2af465ae67a818a579b0696bf5587b68425e3fe2e2aa79571e13230f7aae22e5f9788cdec566238b8a1d34bd0eb944d19a5c27798db4323a205b95f7c5d4b30a88a80dae9c41faa73dc8ed00cab213a19714b357e46a5e1a8a68212c571c1f06c7b67abb408b1a27a218b222fa0394aec1dfea9a46ac9c1da620d9279e987a8034f016597feb33a488ea3740bf742a360362cf48e3a4a7099abff2f823fd5f47ac2ffa2beb4652a74d4474765fd54c250687ad8af27fb25c5802aa1a4a476b5dec1672f1fec2e848ce6fa4168a8ad6c7eb5dda0c660fb89a2aea3fe4eb2f96ef733b4462a4702725bea99aead2c12b5506abd2087ada8b8011d95338ef70e442aa591a2df0cdd8c9791ed967d9e11ced114a09881015c296a3687f105158268a4efbece2606531d94a8dbb570c5916fb843bc900760b9ac89358a5f880719b6c9ca0e173848e0160b478b3416e32101861b496347db1a0a83efe4cb82b18d43de044a2b754e855c08aa32ad4172d4a2fbb61eaf1139ffcf22927375929ff872501c29fdeba94ab19df9f6c724152d9ad827d5c5b7c31bc8cc8ffb637a31829931870bccf96b693a4f77a782355429ed794553ab6593fe7c90d6c3d374ad9529796d90688391002fbc2ebd0e3c71ddd9b46aa60a41031e62a0e0b0472841e720f2470913dafb47b2d3ea801eb9ce37b424916e76b66d310b9e3ebed5b3cd6829a8d8f2ba6b01130bb9380b0ad6cc24c69764f7a0236a5a29061363f7a0f80f6e735ab6e2cb808554575ec75a399c5a5af304ac9dcbd766d102aec67c0c91aa71886a6fec28fea584a85eaccfa74c5163bae12317e5c3917dab300388d317a62f19344a68271cff1eb9dea473522b242f57ad9801dac894487130a4e42e8d7ac71daee543b32128c2a3ab15d8a910d2206f212d243e046087fdecf4fad8464241209ade57324da84cd4736b4404f5f633e6d6f64f17ff08d571c41fdd2c5d507af7d14b4cfce35671f06a946e79391c8c657c323a4ba87f3526d86045c9104973d1f68bdad04c55d72eb145b5d6a2ca6331776231730edf789aba01dce00e1c7d9a0efe8d70df73d3ed1722fa084167fec9f2519ed984f9824f218282f4ab8f3a56bc644b838c4f8daad5b1dc9e9bddd2df38f0cf9ae323128b37726c9d756ecac7fb2202a9fa005ed3585214ce1e0f81c34a3c5d798e128cdcf18b5a855890003983b1a28b202cf1ea6085ef644f32014763ba785504e3934b206d89874f0241d2c7816609733f6a7d1fa3a3e31e603efc0e0d4d6042015b5b9b187c9c4c9f0088145f44391b4ebfb8938069ef7a941888a0eca8b7e88384fcce02285034c60482f19f1cb83cb0329d0d7878f46fa4e2e503bb8c8a068e827c30eb075519bb351c7efd82aced6445b7c2721e10129f6c5c9cfd08b49b3c079c3b3850d4f857acec3b2a36529e290fa430f83a81b81faf8098c7ad1994d9ad4148684eaad2e7f4c5ba4a797161aa00d8c6ec636602a523d22575bd889e14bbaa7ee054c1d24e5d120fd74c654dca2af2f0a7a0b7cff930cbebedd7ce15b73b2b04fa22a0aca05f99939cd6ac2692988f1ee6cb55c57248f25863c03c91aafe9c901fde1ac77fa04037132f50a4e6b5576575b60c3d79c0b1525925139e10ad2e765fca00b86cf60d7d18dac9a32eee07c0120a18d7cc11fc4303a35619c445f3e071569aaf19abbe72efd06d62679d9662075100986421c400f5ab07ba19cb84d2c6fa8c4390d4eeae966f92301fdf4796f3b92a367ae4f552c39eb3253908109f1a719d521cd7d86a36d3a17bbca9783314b7e9153d17f56a963037e4bc4df8134a174ca23b656927ec35365732cd224bef20d6be81210b670f01d64e4f4be861586132ce318c827c36a8352fbc2557290269c9b59ba8c775d210517d2b01bf4e000d5e34522434359e70437b0f4cbbcf245d1fcf8cdfbc483766e2f628d36c3f1b7a4eb5f0a9cf56e819da11b6d078f5ada3e521caf4008cd2f57b3ee5ed2fc67ba0bd7f4a8367e63541ff47fb787c91b2b7c4d3a170fa5a3e06a2c09fa5f7a8afe40e0d6faaf1ddffbed5a01c8cfa7fca63e9c87fc9762d888aaf14119849f8b50a83bc4329251e0bcfae5513e959e331c7f0859f8db9d19ab77ab5876bdc3df415e0d0e8ced5662c0b934d561f0028b8171c354bf080028e1dbb520c59088f5ca6692d4a772e7b11f6f734a5dcae3b849b88c3faf14b45d4dfea00ec0076760a54f62e08cf4501f861e1aaa5a3f3f51cd1d26bc649fa367b110d962de054ad075efa4596a005c3c73c81c8600ac45e2b2db01e94eae0ae009e8d34f0492b6ef76e6c69ae4543a880896956e16f7f8486ddb0773ab9f76c4a23a23749987367373d2aad8736c8b7f16a5a987d0704fd042c4c363b2783e7294af5df9ca4de4a194538a4f8869bfad94d75ca9585364faf46a4c7d5c8d57b6d62df1bac60c68bac31dad82842475a3fda53b92394df24f320b7897575cc644cab8374150a41e33389be97eb63a8f9f068a04195e00cc0e3569e29c4cc300784456017154f0018b4708118c375119d569b97bed293941190e3a7779520791861c1e76354fe917ed3da0756701274dccca6084d7b1cb992fc3804afa691e9c7575af3989e9670221b102ee01428fb7c56c3db1e0c5789e05292a76fa31211517a7cae275391224a7c1407a16a6c3fb7dd7160145059e077172f647bf9d638af80b0105e6861b339b49d67b390bf02f62a8e227f8fc43a252cc30ac653b8f06c4c8c30f377a82624d40646a035d8e0762706a974b52502e2453348b9b1e9b14c47e806eefc92eb1f17a449ca26c47536852a0ffe7802e6f4260d448b19f2042875d7b740430c8d18e13102f5742c94af460fe3e569ebaf61a796128ad10dc8be6771413574144ff375e61e100268e5b56a0451b2c2d0c6d97567463fead046f8f33f73a7a5a6e5a6e7c417f3a95ba714bf107b44394f7f2286cf2fee2108d03569750e8908bc1c2463dc93041aff845dafa15c6704415a957c09f144f4a42b9a92132ec1bbb1bd56c9af2309ddb88392aaa58676f1e82d7b1c6edf76d34f9f3baa885212e64630e684866205c4742f32080d67404c21e8db212613d0e105958201d26eaa1f39a636406fa3d7d576e21af2d911a7eadd30782828c02f96582c17f7673d327e15540c2d12fd7f8a343d6270d69ebf6e725048f3b3922f70fe04fff7bb5fd30ee557dba173c294a8c6660846dec786d09083d43dff4b03988a90394482ec14da2d3d5fd4dcc4ca5ee5e97c079894f0794c5fb6e71d03109dddad7ea9d3b011e019aee97927bc4df490d096451423c3cf0968138946a9746eaab93fe36fc59c1d7445271c1af017ceed24b3c751cab88f0cee7d6423861a41b5f42313e10635648a05738022d34962cd094237c67da2033a33117cafa7136a7c992e897302db14acd4b40246f444e1b2605f091288581fda4ee85648c5959b59fffe4bbb4bff3862b376cafd2c2671ebd6ae607581ac800c12f5b6b19991dc8d00aee5f06fc141c37273d7cca7e0855b6086c435072b7f785bcdf20ee494e34df7a5c89e32e4fe930844cadad37c22726774ca3d12dc6afa70659425f4969ce95681c1bc633bb7136b4af7ea4702b7c80dd021b6d2450bff45c141064baabd4057464bb67004a65ff6927615eab43218b4acee8b5333a3c85c79e67a3c22c21a028ffae7a3e0e836e10425fb72aca4693dbe6e5d70e71d48a89e341a4748c64d81e3fb1d6d3c22ce1e5fec452806fa3876294d9011e2e4b3801375914e715b1015d0aed6094d6cb945a8bde1d71df0e9823ea1b78539da3f068a68044540c2168badd325cb21a7ffd67985054ed39b0f5b9ba418656dcb5123f9eff744947f274040e951838e2d0a147bb90fb7f07059949e6f1a5ad18b4fd98243ab07d3cfb3f0a4c08a43ebaf4be1b90abb464b530b1158c9884e9e1033977812fc3f0e39bd71644d1f8cf3fea1001f688f3e051dfe6988ddc8561fca07d09e84ca4b8cf863c9147041554d31682d13df034cf97246a10efbf13960cc7935a902e7c6ddb377b8ee7bd0ce74ee50f19c169facf28271c710374051d4b88d194b57280967a820c4ff651d00e775838a3020f9b45f3b54a14727eb0e74c590617665c9371a60ebcd5f3ce98f35a2c9632e2d0ea29a49f76795520f9cdf25d70764c69ac966d9c2abf85beffc6a8368069f0b10c2c8fa417eecfd06a1fd87d98f74952da8010efdc84f47dceb714410d420185903d71d4bbc8d361d540419e1968e091266b01aa104d842355068cbac81c30aced3d19b63f29367377c1d696ca8f7a41c30479f943a42a7e62284764991518ec7552c8271b3c3d042b4e7736337c864453f90e1170d29ef3e64203fa0448f68b991729d106ee50058730a32d4630a8a8798d74dc09a04275c84092de27bd1b2c64115771f198f159d4e41fdd9b9382804e5ca9b2878313325197be9cd8a84c41fc15b0e708378a2ce9081e3c0b9ce5619340c9e4051b23b862706ac845e82d9f3b3da4810a1b33288d56e2aa29cb3ce1a231240d33cd9e320e6ec04dc73d8d8f954caafd0a92b322ec212ce1b2b5fd9fb2e023f22b18375ed320ea93bacc624ea093970a15cca40e45a4b8bd35cf6abb0302fb882816f0d3ce550e52c75d4b197e6e17b47ec41a1b180d79d35dd2de02ed4a13d4cc69659adb45c4f208eb580cb00d5d85119779020700212ca921e9648ce8178e3a63eb2e678a34012358a994fc93aa2cb22de36552294ce89a88caac6215baec4fa059105c812800288f32d5441012ceda786560c3180cf32e65ec5ec2534b09b71991523670b14fb889ed489593f7bb52972a51b7fac39224011f03ee9436a944cf959c6c53b13dea8d1d1dcce834005f95bde898f5f22da7e11b0537214f4ed634f5bf31f1d3ed4164d277f8793a0e05b463edd63f582ee1f1436986719424ee35ffba6e30e161058765cc6d7fd442bc7ca6fa8a8b2fc008e473e8984c7bc2bb219aca23ced85710b038aa26f3922d7f8988b707b513d811976cc2bfa2f0ee4149f6a36a16385820b33cabb5c1133a6813f3eccd75b533c6060532cf5a09c27117107f5627e53ac55e2daad72945acd15be5804fcf24676844b08e690226eb8000ca226540c78a2c40169962d3417d81c98e0fbc8225abdf67b2ec2a255c37688fed611d7fcaae580ce0ea58c7cb228144ae510b6da068603162584edaee754f7de143f38fbe6c9ab759b320f1bb8c41a270cd0338e55fe8d0df327398004a363584206628d476d5b836a751a097120419649dffc4ab5cb506d35757d96835950ad74ce5201cfb1c6e9cf74c4a41e914d94db015394c9cb98e4dcae721a7ae8e6505612bab0317c2436315ab4bceede57d9042e8daffc82f7125660c461fb4241a36434da5de35618b0176ae0d6f8ac7ad3728157eac9eedb7128a95ca7a7652e5dfa1740c9ef180c82417db3b0f783d0996103327fd2b977318606aea870dc54695d0538e51a825a65c04b68f10ccd82356d48688053caa8e55a2c0593b650e07736e30ef8ca2e749380b22e5160d10b5abac45f99fdb30e74b0322bf63d4cc4ddcc37e2aee84dccb236a4176d4c6039dfd00c0c10b55d49d786e7eb617865d82d89e1c058f73fe488e77e633c2cdc2abd4e5e1e1e70e02cc2017883455ff999491b1abc6360aa51ff154a6b5a943294fa413da37401aa695077296115454f32b03360ee3f2ee80ee2f641e6771b748cec8b13c023597b2651f0782b6854f3cfd181167bdc34e111d1b099dc18ed8ccc2f2bccb75da9e781bc2b802f88175fe518233de0c938f126481dc33906724f7820e66bf87f1d4812cdae07ae41c7257211fe235b74c140d1813fff1121827e349e4fdaece5f5289cc80a8db2f3e49ddea4c2052f95c387737d598879219a6b85a1af17a66a8656db018c081c863cff50d996391746ee929ed5a72b145932f70b2a99fce1caa07afaf57d691fa0333da1c751dc74549613a1585981603d93658a82707667e5f0518210c89a95e47a9e9d272615f4412f7e3d9d4c68e005cfc6ff7c5ba3d51b0d5cc15e51db1bcbd3cad370690aab91deb3572554fea9e336e65a8112872babeb9a3de62df0a69287cf9ffbe063d339893cc584aa84b7c839b6ca17456e47d5538f4364f0a731f9a1efb3690699bb00a9adb25a3e5eac4de6505561c106158545608ebd88a9f9186eaa2380b2950d4dd8c03617098033ef6fc3c8410b7f64ada09cb59edb448a0930c690ac081c24fc057214834121a35b0722264f7a2a1c5f4453b46f4838a37f5442cd9876d8182d69c3db2842cb83911641539348f50d943086a1304eb4858316b67ab6310660d660f06acd071b1fd6ddde3ec8c9f1da29fd8c9107748d5c0eb2f7301b12ad70b68f452982db7112a069977942512b47400005703cb807c02921ada532bbaa254c82d5a755c89b0d50fea94428b1e751bf820640871213d8addd33428add0c08c254a33a60db57ea6113b2af936f5b881e0ef721f45528207df2e443e3638931c1939843917870bc3a786e514b571343fadfcc0686ed211a245db849c4758cbab2a11e3a060e0e6860f203ffef2836a35f897c50a13e4a074c64111810cd02e097d79ac33115a25ba63b9868434c421abeaee37da5e96feef23e9e1950f47c1a8c15e6c6417bc80651b6ea0272867817fa27434485c1c02c5b476abb61fbc5b834cfd61fe9c8c55a3f430174be51f26ce448fc5ac53e54cf919722c4b83efc07766e906a319e8b44eba5ceb4d2f451bfdd28b25076d271ce066576853c0a9c06138d1cbdf6487ad9852ecb6ef665be9684bed157c64e70233b11f8136c449faf419c2d01a4b22dd94463d7ff7f6909d7cbaffca5ef27809b2cfd1c12358c807b908387d0ab7743e5663aaa83295f34fa0bde3b114c1dbdcdce1acafd0a5afb01cb2706f0672b259ca2b5ce731e8483c13f9d511c587264ebbbe95e716b2cf570a1af43f8df86a8ee041a4c3a94074a13244ab028b414b87a8d7d682cbfedfb2b62de4f74f2b979dde5cea17724e1408dd770bbddf8ee637a0861ba280414b705d34387f81c80d347034b0263b568aea0f1fc072421c49ae2c76667daa7f6765a57ff4b8216f61fae5b30628715b75249df9384a11dfac34fafe88451192969030e22c839e27b103c96039ab371e4060f18e544969dea55c100604fbc57c7b56865e1f5c694b1579ea0709656740b66e142af80348c1a826839636509ce7acf97747e54343279a1395301be24bc98eaa01af4a5c76e03d1eb04b470b09b884405780010d5773982655732989a86f32ba130199c60038df1600544b21553d3359a1374f486a30698f7992bb459f70dca0d1052582907452ebccebc827b93f7fa6a9abed8338ab3da843bc2a4a0f5a1b906af6446f387b2d8f6b3572d3aa52ffded5a41530608a37b40a029d355ca58e727d3831e25eed86dbabf998b8088e2daee7c704d7f9241516f3c52ac0ac960cbc92b9537d9395757bb6520beb8a14b84615127bea95d1d846171c02b8537e86ac42df6eeb49d2ee56a6428f16f7435ea864d855ba9efb15f5119686222095e24928e048335da478206972e57e3cfc717bb0814a7a80211f01f830e5fae1dc40364e91c50ebc3df8eb1a0dd4bdd521986f753f7c71cc00e03cfb03a4c13a617fd2dd3e1f9ca85ca6aac844977af506aa49c5c86c13f47036b752f428f1aed0db0a175ec09acc90df7b57c974e79042f81aeb884a42554b6e205bf568319546c6241f1349adbd85172214a5a52b8ed5e4a8ec6cbaeb42f5950768809ed2070c526bd50b9cce91f524ca700c70a9893679711b55eba3f25504ae8e2df6d6102d8e8059a69dee8a50df408a958070d3c60115009576b07510cfe7eaa8c3f2db6a308301854de8753433a63c4b6804aedb119ebba9ad40017eb63c3545eaa045301b0d96bd29600024931d20ecde301265860c83bfaf6ebd8fc94f9cc6eaa9d30f8a2aa455121788e13408280d815a30721e2a941cf40fdaf68a81f3811e4afd22012307f180359f1abcd2a137f088c597d4c36458ae6d56340b6ff8f4f1ec6ccd55a64e1098284e5b3cf96e0a1a941764b44290d580dde8f46717a5e12d30408aae804d8a7dd3d21fb5d56c7d869b03a3c66a881fb5f94649415749dd2066164ddafefc0487c69041e9af42af8db0dd48aa488ed642a7d8a3a18efe9e40b6195f2625df4897016ddab8c7d1810d53ea19f3ebc3ebdd1683e40c647e2244a4bf6c754c190af5787b1bd8f10e209837d4bfa70fb493d73d50f976b6d4e994e6908303dd128f763d3cd6b2f978c4c7588369400394e62d086d506921c978d6070da0aeefab82adfc56cb42020444ef792570555800adedf8034629f6d1cc0b292bac4d395fdd86ac7d18d672def11e64f90c61414ba3ffa0dd85f0f682216f51826ccef1d7a23b33b90d70494ca9171f63a671f91d06b053847d313be3acee57256ac1989ca28a9c24876dfb5d03dd89d61f50d19e760906be138c0244404dd2d0c743943a892057da56480522626d536e8e747e17d74b74239bd2ef06d7ec9ed9cf453120cc856add7fddbf526ba5b92ebcbbb518e3810c440c3cb4808114b202e751ee69e4a9d36111c740600060e9dbbd4101730795359c06eaf4104a2f14dedc80dbbbefe5e8ffc55fb6aaa2a10905a1a4a47318230fccc5776c98dc678611a3febfa70dbfa97542c8853212248ff9cd226a8cdd2f566efc0987787ce4f6332ba3993bebc28502a1929fd63d8e3043e75ad78ed6f87a6115390b73507a3db74f6e12233f449db09d36275e9f94161bebb4714ae2326604f2d306a571eed2716596ccb3cbb22a6977583eb3f840934595ffc28b043ea617ad75e507b41d63b49dfc86c3aeafe746463bf3c9d8d0daca3e3361face57a610f8db0338c5618d6bb318c4921012090752e75eb778e78bf2978d49b23bb5c1f3acdf66d6d8f0452f9cbf3ab5db735a016a1112c7e24c08c261215dc8f982e636eeb907d87eafc02762a4375e824a1551cc6750077669a7594865e407be6532f5704dc44078b2e1166fa1d938a7ce671ac47d84d62c76b40e81f27e4098e5cdb757f6c40b88d63fab52815bc0c36dfed807ac516731df84287be6994be860fd5857340c61687b2cf699effbbc5390b549dc61b02f89d3a149abf6e41275f5446884057e42dc9906d2b720bb4e29e15616c6f96a1734650db832ba7d32e2442e31c227a2b44cbd3c5b4584ec905328ba8131b94d434bdbce317b519b40c2adf2adb8505e4ee184654988b04af4f9d1f5c497a648918d1b116415eba165e644ac32861e351f3f83cecbf11e2089602ea5081ae6d2f08c12e523752785107c86d9833cd9fb36d54eb1eac4ebbe7b168b8fce595bce67c0b5f2cc6917766bd0029e7f00c6a3c70db05c0091cc32bba1efcf3c62112905cf8aa16e5d42be08900f2b25bb436bc3e8b50173ab95d25fdfa0a5f71cd0fa8b3f1108b27114b2dd07d08a6ff871f71f2c17046323ba82a03e5fb17282c735924cc872d2fb0e601a164be3278b7afb262edff903a76018ae2845648016cafeb86db051cfa695ea5b5800dfaeb44081f38cff86a746a9b1aa80160a638e1dcedb9b2de9f226da8c24586b81a080ed9607a513b87ae79a9654aea54803058e72632180d54583a0e2aa1b0d89f9b042bf8e955d94307dad7582df39382f6842e29949085d9ddf6a4f24bffdbf9880d1635024f0d563647c20edfd58f82c92564723fbad8b4c1722598f677ce27a8b0633d1e176ca890d822574700b1797130cb0c42396f1641124d88d176c45f7f0d0aa1b0d116814fc9627aed5a9b35bb15575b10c3b3891f7d964060f8290f822b28ffb8c06bc5429e1bdb4a65be95fb12169709120049fcd533deebb12056dec2c65cfc2c80118440412cd19b34e6977401bebdfb3bc383ee68875a0125998153d903d4df5239a16a2b587bf75cf5dc6a35fd01cb804059f684290f752c2dcd23b8328098393223aa24e9b7380d24a9a3f0a879539e2b03dd3d843b5fb89b84ace681ed8232c96b116ddbbf10a212c55058fc535d95a71d9a6288cb1fa36eb08f82f2e67aecdcd43b84d2edf2b0c21cc347476c07585f54b355f0ccc369084f0a8cfe9f7c4d8132dd4f074150834cb5ac8e10d8cba4834eac89e59faf5150d41127f7f3da37dc4bb7500ab0fd38170e28eec8e04bd362e0872118571e25d15dc50fc819caae8c07d924e2d97697d31bd88d982e36405966b556f28afbf35a634e34746270b49f9b42e2e2bf0239d794da4e2acaa9b3f5b6df923588cc693ad176d697faab650df59814318c22ea355d085c7d80d2996d0c7ac8c697e6a8fc0e0a6115a58992180f92b294795ba9463e91bb8aec050f95649800290b6715f45ee7bdd5219093ef7c70d1c2d1f3172e54478d690aca5fbe38379235ee0422e2875dff6b3508ac1e2861227a6656d79377d0b2e04d78adfeacb6db92a5055de27975495ec76d18cdc74629c1eb68c0f7d73e1a9f1d65366c32323d1107a058ec54674ebac2208387d49284a8c28df0dce5d9d02a2e68bdb54beac72bbe7d734b38181af23d9baf923def6fd221ae8c7bd90cc023535fd2826281b83818ca820ed207f59efaac027f69b89a23fc9e59ed8bfbac35cce63366c627aacdcf1d585a651cbff32702a1d7baed804d469de068884071536b4c8a226a447552f90892b5f5c0b0d02bedaf6981508d10b863ed2671de523d0d43f626c5099f22f99488b243b17596306ec5e176a994dec97035550567bacec5cad2e476ceaf61c875632883f96e84d54c141f784d4ea3bd92648b8d872d8ff0df4f30642bb1263c5799b2aa2b94edb9161c22dcb23218a78d4b9403408c90e2f826344a0bf4119c035ffb9620e225ca096f4bbf69913f8a020ae9097463d3ee490197ac9235eb36b37062860f9f1c6696c4585475bafefd2a361ed608651f8cb1a518b80ddedfb9fcf38e80067077986bac05c65a22c0c91c27c6665bddf2626e0f2262e939797a6c5f0ce16b44b87c5381a29d4d8299bdfc53a80eb8899d7fe566a02e28bd04c326a058d50812dd9be4502b2c38705ac63d5186899a1aa4a7da75f59b5666e99b979cf05c393b3b34bb231b9316cc543351bb03d7ef8fa967be9bdf2ec72459a59ccaede60347def1f368649abf50e31cdec0e201e3f585783e4d92154a260506dbbd13655ba6efefb2b04388306ba2a83450cd54386f9a3ebad69e1998aef9e9288622d6bfe5359349c84b56293072cd82d0cf13349d4bd026bf7310b48dc4eace20dec3526d75929ef345d47b02d3c100e255a283fe99114ab7fd548ea5cf29944b5235b2db49e66165f7339daff8435001a37b3a74a5a32c75157b3941f422ebc7ad5571613388811cc2fb73b0c73fff362f7af6694b3c6ab493452f6a6fc41ca75403d1e9ad0c868f9ff5df4d336ad5c022e516532641349574a5d901d1c1c579209a730433e34c7f30c6b20b49430ad0576aa54dd3c8f6d2f19aebe7313ca1e2980df0400ddb036b0bf56b0929d116cb784fb1dff610363086f5b3dc93516013fc0e4fe3ee008e88e2806620045f97d635e8097c15f3dcddfb3fb7a4373380b3dd01d426cce51e97d0aee0481bea5d44a3957cd7a056004f2435ffe35477287ec72788d519087cca9b27ab7fc1b2a0a2f75ea56ee1c93ede0390a4a3d2250ce97856b71e4c636e21ab03784065fbc70d342ae25ae38676cda860acf5f6772e6900e88159f365c4c4eb9fd04820c1ebe2c40b7068b6c784b32026958b29f7717112f93be5fa7ed5791e53c1820bc4228110e8c72d4e102115c2460a993eab2eec994e0c6b986f858cef0399130a2d0c2e960d3dc855ebf4a57e4e673fc351a86e4f95e24a021a57b74e3d6836fb55dceaf40e8509a593fdf7e84e828d51769cb294ae7895e3a86e982a1a6ac5f46804a9290dffdcd9081f7c7e71e6b9a4580544e1d07964c0e29ba110c2748a0dbc861ec8531748ea0399ae9689d96bf57e853effc0d798c3e037e160ca816bfc251b2394da3300d54083ed58b57269ef6c7ae6224c26dc0bf0c5c56902d8ef8eb066ef2eb603bd219ee97f5586c872922595fe77d2dcb9510b84131a3400044057444e51898053a1bd84a854f09c42e7701174dfb5612ff1967385b55cf9e0682781d12452fe075b04cd1413dced7c52b60e597bb53c9071d149f7f198b3fac95970a106a5933f8cbd6416f6afde1aaf09fdf37d2e1de30d7332b3283d7e7d5ef2010c611c463e78738699355a98792f0a66fe3f3033aa73b9428fd4b9c9d2207acb8c4f4f5ae04bf768b00e7cf65b4f1b29b9cc53f8839747db9cfdb48fa9526d38d57f10f4589e05f832076ba5e941c03bafa431e94a33d05281cc9626444723a1933ec87a3582a984499e1c01a79745d7adcf61cea84a45eedee0cac958300f361cfc83ffb939b92e63b7020f1babacf2e1399c4436c46fd1059e59aad7b458ba4f89f6b9213fd38db6cb81ee9b2ce5bcca898ddb9a5e2f20027fcc3bdfc073af86223bf641c9afbb3f85cf9bc5d7416df2e92c9f39481b8a62b35e0253bda1186836ff162b58621d934836ad1ee9deba36857e1a83f41ef010a109ab5c9851fb316e8d15fb795600b946e9bb4fe5cb0446e9dbac277f2292e883a446fb4b393a11b019c1ae1ac9fb4789f61b18b15ed27d8873364c4ee8553286261344da980d2295b7a8bbf4db7b89238e2631c333324ac37d5b2f1d831a96a7f141ae3bbba84bb32d337b31a6f98566492cb12e84cac3810651e80725a7a5484b46fbc074255f6110ec6e349a0222556761f6291d1913a175e80b76c8be6c55918ba6ba9f67b466c6e0ce98644fc645f6da538a6230cdfc1975778c46a1488adc089821e5f9bac427225663e33fbde91032607355976876375ca4a98f56d1d9ef977619b18e45aba296b9ad9d431eea856071a3874933aabb39baa53ecb6507c4a6d87e5ae4b05ad33d983b15ad95a2d38db1f5036c2c0c1a786fe15ea6dd02bcf69caf65b9385355f72f73823e710334f47db116665fee051937ad19b836b36b843f5e44e4c4350c0eb6618627f20da3c1066be231c18618147e32ec139ed04765fe14efd99f9d519a11fb61c83a1a5d4a2a105176201eb10a1dea7826837c42df0df059a973778b694ecef96ff10e4918f0428ecd81e109f0099230ba0671b25d8caa1dffad38115790d167e64c1849bbb2c6c5556230af1acfcd5d3ede0ca90d3d0ccf33ac5d65b2d59f137962c189cc7ebe511f50c938f536273cd40b816ec22f639bf02ed9f1bf419b2250875966463998f86bf8546f2e096fe50b744da19bbcf1a5ae371c4ce889f2650a740e53bbaf1fa9296804927b93c70f1d182a8ecd4f9665287ae87b5945ffa5e62e52ad7812feaa9c29046a856203c429b1e4eb51ed53fb03d82e311209426d194593b68c3adf0bc535ad59ec3991fa33b00d6d4fd7c151e68ef40aeccb78fd9fd122c39124bde40e94b2028e16eb2ed3e44ab17670741bc5541316ed264f847cf221103607b2f1678d6d0227f747e4e8668f8ba34500cecc66337571cedb40c1324ab4951fe15b71ce7356bd3911022741e2446ec3037ab0630e97288e1561e2f71705c8fda167cccb044f4eab61afe1c6acf700d657bccc7017543837f4516343e9e71a5e862bebdadda9a2edb13ac572c3894bd3d75ee565829f9baa76aa7891aaf1a40b7163a97e07bd302209f98f95b5628a2f45113c1dfb781b4769be61dc0582c8d09e4e446dce2bfce992404923bcda111c42e64c21a20f856fe8200f93782f6aff18183dcc1a0dc1cf16b7180ce1504e8b72cb68d90f0d4ee3b6b9bb99f0cf92b7f9ecda7e0e94c6845ba7f08e5c328f91ae7614076b8bd6d9da48ef16f9a29d5c34a0b4f2a82d2d92e8907dcc0ffc97a19456e2e2413e794a53a0100bcd11cca2087c32236746fb3716f0a9c62b33796416720064833a0cb62fe92d62ca463fd5ec128234b37e9e518a6a3c9fa3b96d0d88fd3015be877ae4168871932f558c52c932a610cc03b6683c95d49605d2ebf2d75d4289d0e88955133c34b505ef7294bf293a0c171f8cd5f5930c0051bfb62284557d06f04e4364f368d189b1bf6aeb04f00ea599791389e3332daf6a701a358d2422f9ea68468068b0476085094f98464bc305f0ae6f44f3d2adf0f4b4a8912ed59932b10ccc8cff76c7d11049a6fd1ccc0e28f76f5b861fae068041dc0fc8f16a14e7f02e63b5cfe61c622ad2a7092de1fb57295056ddd9e5c0991d0629c9894bdd30c1ab1a0e12b2e661d25ed5d981498cb37eb2768394f39a492c4ba41434ae1f660e9e20005dab853310707622b0c019ffd0a4e28201b8769117f95cb0acb209514d7ff9bdc2477fcfd1b8ee140b908982c6936b0bc49835d177b24e6f80aa40946cc69b85cb61c1fab107776a2825b8d3a2b47a91ecf32d51e2f2c247cbc852b681af9cebe6e3ddbacf4ae40fd80794a810f8361399b81a1d8be9b5a615196930ebbfaf09d16b91dac2baa8ad96564fb305cc434d966545a4bc6e8782253cddccfde64bec9b503320539905d555b30e09bed0576dec47bd8a54e0a9d63acd2556adbe217eb1d7efd03a615decac709bbc4bd5e4bccb6b3ed5e8a42d9d08844c3c647f95bdd75dc7370e7c4806dc4864588a7b446b456dfac8d92511b1082553e8f2c3457030aea4424429e5686ad1606f6e119884ddd684ccd05fa913602224155d2bd2642e51caf24bfe2c2254bab7bf56c324f835adf74ba4c7715e1b3e88a949db7b28e335961a76402fcddad420538d538415f50eed8251ab0a7f0a884b535605452be2cca302c6042a24950f7433f69553d2a860757aae2eb178865dbde4ddb1dd11e757cb2c39c81ac1d76b2c94c7004768786a8c5c94ec273e4d51ffd226c659a967c2a05da5d01214079feb612673062507ee4ca0a6577ad24c98252850d9c024ec17264601305ceae759132f9c7eaca1c172806379adcee3e8a3e3313126eded019564711c9e7c84f27f853d506964992f96a3e5a817831a8dae5e15f6d52b7747ab574376f2bb2e0acd8fe01fe2e1c2ffe7c08c1d677552a1fd96b2cdb111f3d0dce3465938d25079de769c39c642c0ee805eaea7306fe96423d02fa9566e3fd4090a6e5a4a054a1bf400bf7543cc5c001f5a7e6017c64d90d112afe7b26e5ef7625cbbfa5b8269da9f6ab20044cf771709e11d5c2603329380de4d5b5009af874573cb3dec4d00bb34264881e1404240190b17c84ad5d67c6e4081426270beadfc6a80ac8e0e25d49c5c7b11c14d4f8d8aeb1c4e9ec29a1d8f68591c716af9cb803bb5bffed4615bb1b306ad3fa31db16f7da7da8444a70409203c92168270fd23c018f1a40877ac1b44744c8370a116406a02571919b8faed40100d628c19c55661d5f4869c7a24ca0d87e2a7ac55cc52c4fc7c40ec9b1ba40137b9d71589479dac73dbac12a04d29fb1e1960a37937c2871502491079ee4ee98a375b6ec9740ff6f3ee9257060ccaf5ede7bb1fbadc6a34634caf92331bc895ea24b069074aeb8b94642fb9979db6ada8f76198668d43ea5828008cc59fa8f30eb37bba08b922556ab9c2c024359c2dc6ec19678b643a5a8c4ff4700562a18b15a1553b63a40c7014ebf125341100a453ea14685cfd218df9d4791de3fb8ece79fd78a362c529104088e8a5951ae1fba028ef7295696f20aca994c5514b90f3f94c9414dc42a36d1bfd028394d2545d80fcb540d99a1fef7fa349eb516fcb2d96dc25f21e1c16bc424beb1245d33d0229db7ee814939a6a4065ded5fa483e913ecfec27c14474b88813d1cf5b06a486a49dff2f41052f1f5903b2eb406c6615985cd659d2b3b3abfbe0442f7dd3c33c0b903f3cfe6839d9a9076886ce568cf089696d301baf18c17a65509e72758047fc4eeb7cc1e4db1f10538e362b4f5a25ad9ee2e25780a438ffebcc33d54186190a0d039135013f30c9a8e6a943f4842d7f0fe101103a68fb8906f6f9ee26504849ef3a60ab7bdc7efd3f9dbf63fa0a8043dc03603165bbf3a227136a48c8f4f7a978a6c05b5e6a890ac0564d43055e22d5eb132a00986a76f83e9928578f072f6bb3aba3a7905f2eb9cd5bfc725ec114e69e475d5127d22f73b619a3880906de89cb87451dd7272025e4250582bc079b97ef02989e06d973b6ac7b8eff4f3d8f765ea5e317260e6d5b12946ee326e1dbf6b2a2d0de9a4dde0f390a35f198302d5d963a7127b83d532233ee021aded82ae303fefd871b146ad45eb63945a80805b555154e7c4088eb545ba5aca314514eb9f3cf384fa85acc8cf27dd7bf87f337e6ec2d70d3a5d976586828e40de7f614db195d4a29dbd536f38d548121715f4c1d6b75e49b45b912d96606240b0922ca63e80ddb1d599718c46fd7e6df5d9a3c7382e722bc3b046a316b6d14a314818eb2288f32dc50e1a1179791afb50f2437d6d9a4bf932e2911d6f3ddb0a7e024b000968c44c8738f8eaf37ad7f9bff93886ff317b6828e2c1c81288f8f4ec0b986c34c8b63071403c7f72a2f56aebfb27a0c4492d6107e91185c3037812e72fa776b2d296fe619b06488e3eb880d2f574a7799c1aa7186a1398ce4c518f0c2a000b37eb3122e4c6d220d61adf2badbee3669359daefc311313a9c8925339db6b62335b80fcc53c01871159099e11578562548b38e1e4cf75cbf874f64af01e9f5e60c0b4fe5602701b289a5ef8a518fb71167c28de281fce9ebe7d200b8bca7a0dbcea9138380e550a7fd40132c329351c733c3c6f21066ed43d19dc3fc2ab8ee6e986cfdc2d5c6bb234d81cde22f1ca6f674c32cab2b72a232aa05ece2419f4c6009f04c533b6d74f7a3bf60e2d4ae802b935edabeca00b5c453c0054815470ea1f69a5b7b224aa40d27ef88dc575856d175f96e5306e6fb5ccf3eb953ac1fbbe994ff4ecce3ce7448fc29980749691f8006eadbf80c115e4f3524011f3e88deb867d1b33381980f8af90f551e21b8758addd274dbb4ce4c0724dde52fbb86460f927501b3ab11db79f5e7362ebb0d896ff9a1ca423c6ede30a61408cf93021c289363220c090716780f704b03778443cb2f69d55acdf17ef998b6f849693713cd2d66f51592e230bf246d06565745c46cc58802dd3583d3128487c25b3fdbd221a25d01438fa89648955106366bec4c254e7402cd9ccb19a19b1a70a3b5813c795c1abfd5efab3f759af6d2ea2cdc3255b0019376f99c1903a0d6c94ce6f652a31c061cbe638fcca6b1aaba07d1ebef760970fbadfb6504a30b62689fc5802747e3b7c677b455eb50901599c5592d584268358cb31e019f8e28ff6f247b1fdb4a18d5112f76401b3158022cab1494cb79fc3bc89147191dec29955785a68ada6026821507328e4e38ae85a1355141cd79e118d1713fd9686ac1e67ba3173176b4c70878bdd4d62883f48e0d4c7442dca3a3f4c5f453b8f8615df3b99ebe285892a4909242f25da0b1a968ea134df08ce5255b70db6ea1aea001140ea6e28a59d53a5a6122f539744d361456417dbd74b6686dd93f1546bca8e16f691d34506b802115bc3b22c4d71b4bdaae629aad25561e7746c3cd11d8938d9ccc29accccda95120338026598919ec4487eb6d5ee3521fa419478dea4ff62025f2389faed52c0b7bb46cc09a4e6cf480b6ac1fa7a10c46b744aab6508ba7cc8c1a60701125060252e8b5ee602b6b8e156d506321442ec2ae029b57f881a6388561a9ebc064f1fcb6b0acabab4e6df5572c7243bed6727c093d36442fd69c07fb9fc8348975e8eed7a48c29e56333ec05a7f86e067df175922ec4bb365971ef362233f981721fdcb1880f3dad821671bd5aba306d8d33378a77eede2fbf6015a01172430e16e3633acc9cf0decde5e430454d6161a24235f1cf42f0857aba0aba4f363a9af85c3dcdeb067d88716562b03ec4d77bb16872307fbfa12f37585492262a0188751dc6b8930373184a03f6a5e39602f8538c7bc0464f6928a47bd69608475ed8a802c62edc70ea50f6bda90543903c5f5ca389a5fe91451a010dd45e7d11c873f86c4b94fbe5af2414c95eff960c09d1ba3ce4c1edf1ada8ccd8d500ad91c617758d3f85474b3bdeb9ac2a780d0ccacf7099e84b26618760a7f122ddf5883b0e106c9a05f136833d9fc12e6dc3201d206107b09c254fa7a4f7b8d55a48b288ff8150034a3f63e2095f9465b939d803ec66a44f938d4b56abda57f5bab7f07fd653f56c1bf0cda4cdc2cf6bdaf12e508fc33d920c317ea860857a3a08f364f94fce0e007bac44942383d97ef219c92596013e7ea857f35674db1e6b124f5f1d5cd2a399217a4b37d3e93d4e56b318e10bd14a0d9435797d25331b180be34e5003ca666d1c892d119dc8b00c2394b8470ff79b04f1c36ce2db3b975fdc880c7d9095d810e9880260e58225e443ecdef7e11d93ff1f265de55717c82920cca981ab0e55fa2434b46031d7df7f2b5db934a7ee3d847074bdd6f59b111ccb8970e0de8ddd82859a29df511d12107feee38763bcc25931f669d91e16d02eb4a7e61f4222a85a21b8cbf3764be7a55e6dff78da96fe692489a3180191c5f27f54ece1f5c3f998fef1c5ef9cdf499755ad7d972f11518b421498a892d3d0f02a4fd645764f32caa501898dd26f09c90dff2217cbca54572b41e9f422dde1ef6a2eccacdaefb7decc4e2bd3f4697de3ccde2488299b250be9662c6d7c0baf8eecdc01453ef1ce916f5218b7abbfa400ad1e38b1b34ba54d8659f100d62ea368fc5f92cf5b4128c432b50504d406d760105ff483d7e1f7a06d01430e5b552a98f464e60d4d133edfbf5a981c52bf565163ff81b10207c3ee85cc6d3afcfcfb71878cc1c4c203843744a146cd937f882ff79d6fd9ddc8464a5605b03590fa51deab21749d9decce17cc20600d85b9d02a0e5b0004389b96905b3f2597a1bba506a91936d57b474168a8080fccfd24f145fec3f1ff221ebcda7d5fe38eaea33794f95b265cf7d26d365dd95a7b2dd272c1ea7ab3149a6a0f068f771f5ad845339fdd78ace24f6da42a60afa12865883a7918f93b5b695beef9900b83d03f2a0460f50ef397b3294706190770f8cd840c61d8a89742d58d1cd1879c96821cadc6a065e7ee5cc45e32c9de62181e4737bc99dabceed8e052994c66d9b08f974e6193e9d08d8babcfa040c74a4d58a817f86ea11f3f9288fa91be3c8cfd847b42f74d2e5cb2908b072682404812cd622ec6dc8c1dc5befdd404ede25d1eff0df141870e471401b56f06964da8bcb5c25ff00a8290fd64c9f3aa4250d6dc430b47770c673f6ddfc1b4ec52847b088870222947568bbe9fdf49c99214c1847a0840897df9853593da95a6e859195e743c251b83873d5fb860a7c5a9830b54010513b8a47c83d18755fa02d13883ab14b60b5c5576c2d9dc751b3bd3f4670fd2123983c624e2b97cb850c4b56c85c68379edc2776efdb682a3e50574765b45d5165e8a7090289afb772d48771a1d35a60df739ed4e4871b99ffe95e22bc3fb271ef831a8d183fed3f9398d8ab926798c48876308d878ff704f4b2daf08392bfd99a3938723cfe78f05e8ee3ed694992c539bd24f826766eaf15ae1f72f3545e7eea45767a64a506bde436f536337754389336d91f8003fceec451870824211631577fdfab7239f5221f4f959e95ca8db3bea0031bcfb2b9b69bb0acf89adb5ae3e85efa84f5caa7e84a5af22f49850582b621e8954042524623f61af2c20662dadae729398fe710487fc586d2a2ca1e3151b1a233df3841a90f69b133f7c1529957774fdf505ca804f202582613b1cb3341dfda9c3ea3eec5b067cf1329f4834592a217a779eb28a4a9c773c71716ecd8917a76fa0d760bd4a838fa62dd0b2876a2757f447622848c18871bd94d6b0eb7d0c4fef3b7a452f8b1b46516248be8ed619e67c87f1166ffd8ca8f40c179808b2a86fd628877b4a8e4f41cd05d9e0b0d35c4659b21cc7560dc12511c507ab4589900b07d7b5e3f6e27d428773bd44a21d400b1170391b552900dc4ef76ca0b21a470eca1188438cffb6794b638675b3021099dda521538030a4a4647315f82c1c901ef6ea60a897d6ecdd144fefe0c959a8e41f578305eaf2aae00211dfc4ea1e8700484c71f6a25cce80ab3a841a9ff4c5dfc334e1cce5cf35709107252a4990c2367b654fb89612989684c5902066819f49585a9148b052116c43e9ad76e268a98c8de63ee9a714d8f3a256a4d426e620abc4e03f86f934f7d82e59a1947754685bf18be0d43ccdfc6a5f5d28d82c6f65be024a66107d6054ff1bde0246a1562ba3c94eb9987c86c894c0c02a45361216f810e1a3b7c98cb163ed352c44c439b7950f53759bd33e492e609895ae025d214d141b3a2230a78623ec1b023ad7b77bc62bb86fb9dbbe02448710dd0f746515cb6be570be8f2996f64db26d6992da5796fe8894ace0b3410b8bfe80479269f7240bb9b788d6441c08628b7c7887ba9def329eb24a090784b89cf4b0b706e126a699bbea6fca52d0522e0f7814c886882783636fc8085372407700e3658d64c0412b7697688c2a0e73c8991847d9aa08c8e68644e4880137e8027dd3b8a1e1eff41cc77c1f3a813aba7f0277b639c6f5fd278b1f58c1615792db173c17c1428e6f132d8c34c59efa520235ca3f9788254ba977a0d19582a9d6535a4a8356d711e1c3ea004f68ca72adfee186d70daa8e6ca144753b52acce2f1f270010e99539f1523fea8793c7e83c4aeb1c187ed5cacaa0ef3139d975fce5c94b85dcd16e5ac641ff0d8ff28475db2c3253b47053eaf0c1d4d03cd96ec4496301dccbf99ec338da2fc81b17f40571c304df64510ea8de0644f04291420acba3250ce93a35ffc2c21c519c3c0ee0d4b01ae80d2d5ad3e39e509710775b26c2b41e5c248381fbb05a1573a0b2be999a52a5d5ad2e6f33f0e387fdd894172ce846deec97bf6ba2ed6eb8244f6229f7b96004effdb412fad5b91d6fd87fda4cb82444fe260d58f98d3357495ed8cdd090c7ccaf04f63dc73d38e38323efc5474d2213b7a0a44497da5800946d58d6da3e2803ba048aa73e93760c262c33a8b23a4224a9ec11288efb1b01e07dc32d7a0d73365e90141b86eaeaecb9be083eb45e648e003026c5ad3d54bae96b01851aaf565a370b6a0b69e5aaf7424d7d6206d36abbb22ee40430ae08a1c9e33e0ca599709b6a3b7cc1aa4898c3bca704f012c01046a3dc3a7582018564d07f173b09be9d6c02f57728dc798bc69fb187b6732d3649d861e3542dfc7bb257b4f67d770ff80015c20969817efd16fe5a43159eb9bb12cf5126f7252b3c16bcf5ec447f58a4f1d4d76db7bef2de59632252903360a650ad909a76b326191cadceefefea04eba747447a61acb8ca1dfe1bc728677be8b9a37a190e95759339fcc183a9f4e9bfbbf9b79badf7f45995b0b95f829e5f264d68432863e9530f4c7ce557c1f61601f676aa67cd2d4a4dfb9673afdc53777378b0947d67ec41a4ba6ad7df77744c9b7a43d89ac1744793aad9abd1bd779df08a44199e68e54c34649e5060ed34a0e1d2c2d3b7874a14769a84f991e27af430de04d4d562dd368b3746f5559e34333a63a6cc6780b366bbc1fd89d1e2b47852361ea733a43c818efc75341aeeffd589885d9278dea0183f703083d1fafc74bf54b4de120d65d02a34020001e046178b083c16030d0e5c16e36035f78b06bb55a20283ed80d0d81e183dd0a0441f01fec8080401e0f72b3d96c06ee78b0dbd9015b1ee46030180c6479902b2a02753cc8b55aad1698e3416e08e486c0950739d920089a1ee48080401c0f6eb3d96c06de7890dbd901551edc6030180c2c3db8151581361edc5aad560bacf1e0363463ea10487a705b812b109c0f821b1078613018ac08bc45207ef079e899edc1df41d274ad560b7c245d87c02f226beed00a5c812f7b05de15f83de80f0281337006ce40f0b90b048276068277070461f8868000f0523018b0804117d80c0683bd3012bbd650d7ea5a5d6b28d7fff0fb6ed50175ab6ed501e5fa3c78783b66ddce6c36eb7672fd1d2d1d0bac08068315e5fa2d3ab81c5c6b886bb1ac6c266ec501712b6ec5ad38a05c5f078e7b639b713be036e3b6d98cdbc9f573a8d812ac6883711b0c5694ebafd8d06a6cadadb5b5b6d63694eb9b4858c0358fb6c9b6da80b6d5b6da561b50067f9a40101447192404560483c18a727d1c76bfe1e8ba4e1c7ffc680db55aada1cedf0dd35d5da0bbbaabbbba40b9beab509bbd656133a67e90c7f27c3c9587e30539cacbb1309e57de715927e4fa3eb4c0e39dddd9944609e0a94fa32c8c2ea14001f89132c9a3d38f9e2e216b6c91cbe8298fa479e1474f538db22c1655b1a80e8baea6f4cc9b216baccb75461e894f1a655550a214c17a26e442d6581f9f260c491a1ead4669b15850cc3562f9d17b14b2c6e2b80ff2e8fd47d2ecf8d1fb4ea33497cb795ceeb3248f5a7ef4ce0259a34181e22ec8a3f71c49a343d6288dc59ab13cc57255949e315941d668424261c4244d8e268dd254434f462b57c81acdc7070b21498383d5a81a8bfdc45aa36f9f9e29fde8bb0959a3e1cc983afa1e7de334aaba5c9de3ea9d910a1ab2a6428192c614496383a85195c58ab164acd9939ea9d185aca942424f80491a12934655954aa8c908fcd1cf1fc89aea3363aa4f10f2e82790a4c13f8ac5664f8c35e26090473f8f903515c787465197bfd4774d8122258f1e04b28642993a92a6fbd1f3d033f347bf83a4f118261a3d92465196bfd417d2228fbe88aca14233a6be14c24154a3efd128aa3ad233de8f9e88a4b93ffa53cfd81f8d01c8a3c7389e619035d4c745d2d01ffd6d94c7fca5fe68843363ea8f7ef438be21a3a06c61b62800a2850140b43018440b73118b72fd177c8881c1b72c9765bd6559962bd7b7218e42abb23e56f5566555d627d7afc1e36389599c184b2c6671727dd20eafc505c5c5e27241c9f5b18ecea4b1843496496369acfa600e6e4553693e9acaa4a93495e693eb8f706ca59886132bc5be58fdefc655714119b94aaecf0525d7f76cd81a95558546228bc512caf53b920656954f15ab8ac395a3b18a23d2188dd1188ee74e0c4cf3689b50171497cb0525d79f9b38fec8f5074b88c562d5f71bfa7bd7fda4f2a12a9f5cff86f46dca5696007801f88abc70a1961b07b13469e512e613b18f28d8f327a9542a05a44fa3b014545fc60b7231c89f389143b1b3bb63677666677767c54ea19e71354a00b864f2c249d4a7fa3dc209a55fea77eed9f798a3eecc29ceace9a6cc986ac238966b3725d7c7c9f56948df862c1ececc519eaa5fa6cc1a2f081953bff6e43abb33efce721542c2d41fbb2ee4fa9c978a82e97b41f74ae9fdfce49aaf6dd23323b643f6e5212e35eae56bfd77e9acc785357b6b68d678413366e5282fe52ff5316ef95076cf57eef96a95eb8f5ebe7379d2be432b4fda634775f245fbfa5ed0ac2db96ada8f9d472f28d7e73e1b3e772873bfe67938d7495f7e53a433e0fe996b0f20fcd1327dc2383f018f4ebcfcfdeef67479ebc89f9b437daa2f8070c2faa57e0f592934f5cbbbc8649ae73765b2977254c974fa9acad545fff6885cba87223ad9012c877e5a597b1d46b4af341cef6f0bd07e1b671af9a6f1ddd4bf2198ade860a6620f609e528c6459238013ecdf239d2eeaea93f6429a004bc0a37d656d356be66bbfcd60b4af790409e31ab720fba2740b72d4d6b305699f235301cf9ecfe5dd208f5d0a88f2e79a50c0e3e7cada6f3dbe6d3d77ebd97af24846451794a7dcdee427ffcd337500e8497b11c0f63da976addb2cbd1d5d89b9755549b86403c70d1cd6237aa5ea743baa8aa56978807f533d5bdc415bf39b8707d694a37acb3bbe1ab4bc8957c2c8882f48397b64f91d0cde9434036800541d7f99bf5ab1f26cc1f07279410022f7f77ffbfa0db9dbc6fdb6f1d0b6991d5f6fdba1bda6f5f09ce6b37c7a7a011e2f4c0b0fd1070b8f1762643c3c3f47570093180003fcb81183abcc94502dd6134b24677a6249eb67c913ad1f5f5259354212ee8090b403601af0e768e6a3f17e8b570d118405bd7a068c6d2167c03040582c8c9718effccd3c22a534efb351d2445ce3d146d55455e51eb15649f09c90c0234d8287d5c39304ab876715e41beb113b2b55e89db79d23f2ec59d111a7ed646e3ecd1d02bc3bbd4e066f06211eed93477b86f6a9b3f12e8bd4fe7e27f30db9281c5e4f8027254db55f32bde7fde8e58e0bca1a1794c79a7afb3e6278d49efbb1a6aebfcc37c45e2ec44028ede873a1d7791488e7d1d06179ef6f9ba6699ae6afd5ab699aa6bda689e3f696da986f48a5dd677b80aeeb86f82b571c28eee3973f15284b39d6a0dc35617effcb4688fdc5891a8f432178a8f15d8db0b2c2569ecf8560585355f5023cd6968f2c3f2d84d1d1c21ef9ceb7e194ed779aa78502c81ed67a92011e2b509e9676aee228811c4a1b2d7cc9a4f0d6d03536cc2044bc1fbd6dfbc9daa74835c030c01806b5f707618eaa214a9278fbc158cf804161c819f024c120d00a0cd297f986387d04500f6fee640c00607079c1074c8f0188e1f3d8d1c22280975300aa92af9168d136b8e8030c1f60f8002377a09412664e70488ee17e1be86ce9f3d5cdc9836d98b023271374f28cc0f6e33a79fe9cb36e2a7f991f8bc96479ce394516a5de5377eaaaead419758de53d55dc52fe02b3051e7d8795fd7da767da79b61f2f42275d464f969dd2299330b4f31f57c1dc8b207294bf9ac834894c8fc83496a92cd3173b716bbd1cb579ea67b2cd6379d4883422c7af1165ff6ea84fb03ef9bf248cb7b2df7caea236b469446ee3c16c81476d28fb8f5baca936a4f9f464750a52d3acd5ac464b5d4740b328c7a0b4565a297d2728a52f3cc162a294564a29a5b9d64a2ba54a9ede80a788973195e0b1a38545478e15138e1b160b1b3548181c65f99f279fdb68183a78a4d96add2ee8640f00a8bb33f0e875b36708e827bf93a10d326f01edb5cd5880ff57eb7711ff4fd4514523d56b098988af44b54cbed0efefcf91ad807598e3fc29b64f27477fe2531c266182c023f5d1a1df871d07aa333a4cbac3437b24cdf77404fe68f45e38822391aa66e6be9b6dfb29b3aa4f74be90ee90ead01cff01ea17d5cf193cf251f829cc9ff48bf4b0737f363855e5f4cc266eed9e69741869aeb6efe9047edff155cf542cb6ac653ab0e88e539f964df166ceb383af57f41579c9ced3273a9ff489cee953fa44611286eae0efc3ccfdb4c5c4872069bcf07bcffb1a8e9fe889d4a74f1457569fe8bb48978c814767f964faf4a9cf48bb88d53372d634d5338e6a1dc817fa3eb842c06d85a9b5c8f451b26778329d53bce6e9991e097e43a637c9b4e44be8744da11994847cf9b96275d6575de43b6f92346781477f98a4b94fdfbb9034db53fffed189e6fd6efa67ff6f46fa7dd4e1a291535ed1a653d2ea3c3dc3893dfae42887f50bfd7b3136997efcbb7d7fdd46875d879daa58ea13b5b84fd444f34cfd302da07f7efbd73f42439fe69b98a0a59ef1a7d4ebef4127a5cb2ccd2642fb0e002ec023ce8f2969be7d53901589224280ac693fbec82019cb1fd8a14f9afc1ad297a10df5299dd53fb003119309e3232f29d794524aa9e72a8e40d06f0827eb3784137fd0e99ad4b8a79913257df92262e551c6b2f6f635bc83c9519a7b51d65ee3b80164c9451044f9fefcb64df4b49f01d39f79bc59a35226044a29a594babb53a71b2501f1cf071698524a29a5148949a518feddd04a3dfaf252d95ffc30fcff6f697916965f597993e95554be547a12e9f1f7bde7fdb6fdbd4fe9d7b7e149e9e25ebadb15fcd50565df5a6eb159c3e9e86cb1d98136e4cbd49127c5f11db9d2f4022b7568c7b5389d562b04a50d79c2215fe8e872031ef43ba224e7e941a1a8a7675ad99f63e2dfadfc3b9e9e999d8f3743cff4a3519690a8f993e4477a834ca1a03f68c08f94496886444d18921f69109916418f5880e542a23a75e447f7223b155c8c223f7a514b1412d52c223fba14d9a9f02c86fce8af47f58b861f3d06d997f01be41f7dc98a1524aa67427eec31729be1412080e50a89f29e203f36127273d15e1ce0c786a2827217901ffb89dc51b4143198d0902897f510b959d03120c08fcd4342d1717e91a715e61819a624e7d044c2bc620e21f7cf1f48140d82e1c7f9339f984de4990490cb8f3347fc710ef1c28f120db985dc0f821bff456ed9058f1fa51354ddbcc78180dc31e486c94d02619097bb173be0a7013f4860370c58c0841d31925240aa488a45846503ab06d610d68b8657025e36af97906d350b52d343d373801e171017ca35e38ac1f5c300052080cb6462563e5630ab20568fa001040920e825e85e8c4dac988b8ef8028f1c3a6ed4168f1a443688461714c78bb1a6e291c76ed56ac2cb15e4b29287096bd4278761e7ae85d3e1018bebe1563495639c0ab8d44c1686e2e84f3eecdc1276ee5682a2007bb95a2cdb6a0bc2f6b3b1b69e961671f49ddc7925ecdca9c836176caa2d359399ae0b0c22d86b65451cbb499e9d55c2ce1de9f614a1f5c36295661789ab735537a5a2228e8d936767d24b4a8c0846842dcb6261836ccbfe9048e23899e4d9f9b329cb845d591dab9aab0c2b431623fac4511675dec2ce5dfd51822ba8b5bd7c85dd4ae5a82e89aee544143d4fac9ac8fea326c3fd63b7ea21619afaf8063490817eda2393438bdf4f5312356118f891aa2ef023d5b1c08f74ca0f1cfa894475aa023f3a140afce85126d02ddf406ee81d2d896a96047ef4a008fce82e08740749d2fdbe2351fd7ac08fcee3801fdda701bdf22d0449e7904954cf6e7eec19037ef4d402ba6938d24d24ca7b8cfcd8430af8b19f1469956f22447e6c2c8424cd0d9644b9cb861ffba7861fbb35a44bdf47686894cb12f063e7d8fcd83bb949df4884740d2289a2ab197e9c31197e9c3204fc389f2409d24c248a06d5fc3885687e9c4d0ed0dff70d33c681f44f204933fafeb992281a43f564c5f0a371f041a22a017ee26403c8fc0802599303f8fd3cec2069b8981f654c12c13c12d9a3bf88ace1c04b901fb9bf874471ad233d73bf9fc8e9c700bc7b86a1df45d2685db3c38cf97ed337a47f36a0819f0cc8cc9ab9240799cce96060c22e306116104793ff8f59335938885bac02290aa42620029935d3e70620128840b32020069935b32749906df50071bc0e1047ecdf0021b366f22011c7f62bbb11c7cb00710134cc9ab93a225e97911e05f4141147933f9159337788b85c8eba3d36b86a700d993553870671b4b304b8ccc665d965d91fc9ac993942c4b1edcb5f6658c9b04280e89f64fa0719ca6e5933e6289b4acd18ff9a209aa0030401815d9438de1971c431883fc4d13598bfcc1a20668cbff6336b4a3063fc0da053001d02e8c88863c7882d1fe288fda78738b6a6fac0acf1a1db61d6f03063fc5bfe2fe2a967882a91e8224659326b6acb5b4bc2300cc330ecf0e58d18862e2fbe8b18861f86e14b0c670e5f43b5858428509e28610a1797269c0e8bd3e174381dd60bffffff1f8661f839be9bf0ff850fff85501c67feff2533ff929f275c412daa8330a4ac17c2ce62cc27168bf9ec68696969696969f9ffff6f59f96ebea525fcfff0c571e616b1a5a565e616aaa2397408ea4355f04f71b6a09ea0a0a01e1e2c2c2c2c2c2c2c2d2d2ddfd2c2f2f2a68585e5bfe5bf451c676661616181c2239c99054aec8b3c3f35934da1a5858847d879c78a8727c7cacacacacaca0a0b0bcbb3b0ace0f86e5856565a9ee55b58c471e695959595a099573cc8857c084f869a781158589c896c2593c9563a4c2693c96432adacacfcca8ae9c677b36232b1fccab3ac8826d3cc26e7f11e4fc25bfee32c57626545ec9ce3ba76aeebbaae6be7868a8a8a8a8a8a8ac9647a9349e5e58d494565e54dbf6212c799555454546637c299556653b6e0398e5386c954747b746ecfedb93d3a384aa552a9542aa9a8a8bc8a4aa9f4dda8944aa65779934aa9f4a55269a8340473228b116da1a2f2c21176be31cb99cd663935482412894422954aa52f95482f6f4a2492ca975ea5248e3393441289f453239c9914365043d1a4859a49f3a054ea252fd5eb655f536503638c31c6241289846b7c37248c4b4ffa12491c67c618631badd345346bd546904862e71a2c1c168b357146dff77ddff7618c1fe3efe50dfe3ed2e327e1effbeffb62337f33caa442e3cc29b3688281f1846253336553363553a0e7799ee779dff7fdf779f8bbf93c0ffff7f8f3bcf73c4f080c67f6a6906b5241047b4d2bbe6f068161e7110c08180c3685dbb66ddbb6cdf3bcf7bc0dfc6ebc6dfbdefbcf13c76d9b3d7389c96402cd259309cf9b3cda4f09b41fed47fb9975f7de7befddb6edb7ed8ebe9bed5eefb7f7b67ba7dc2966ccd5d4519d213b732a1f8a28a594527aeffd7be9f7dd5c4a29a534e4a2280a94265c2961881cd5adfc65ba49da5a6badb5523ad22c6f68ad5494b5fea83ffcd5f5643780949130b33b1d9e6c54b7f26ec9859b4206c8bbbc866b354a4e2fa690c76ed5df7d37b369ae2649d42991fd7d48c1fdddca51b26b9eeff293bd63657f69a7a099088fa7cce299dc6bbe9ebd40705096ddca9faefac56be4697e0d6790a7f95ccb5109902ff5fd7545d622cfafcfc39b5d33111ea97ba92c5f50928139674085ebe5052ce3dcb84f7e2f969e27615c7642bc6cc8e8410b5c361eea80f22d440f9e35924750be9e77bfefde3902c10f7b974442c30731100af8054804ea00e527794e4e0875b9171d7d43660d1b9e8da694eb1139569fba37284a98d95cb12a691b2ab1f91b60ae87f301e5ce98e3883c670e9e3c3f874e9eaf42459e2b41798e2a3af6a7cda882a392e332d89ff355529286c4e3862f447cf1a3b25d0cde7bad05ef1ddd7052a10b4b10d9d8b0a5089b860115b270d9742bde0da712b3590eef863d8a78f2b2b1a1b4e9747836d491e49a6065d3b178d7bbb6b3716f8d16cf864a6cbaee7380b411c22ab1e9386e2561fa418e27f77349c82d8b49474e4619a3e2e8e5c54ac2b872dc95a4a9332561547986765e81ebe1baee381f0e8081c7cab1dc65b81cf090de7ea17535430c6bef7408c578c7b6a39b4484e184958d0d75706103f6b810c99736ba5692caf59514c1e98f4048aeaf44874a5f864c9ffa8edf91fb77cc4047b97f047894fb473cb2929946ee704441109dcadf907e19f630d3a03ae8302243ee1f810cca52d4514486dc2f834c2377d4a3d35ea1d3a175b4be7fa7ddf00a78fefd6e738e078f7cc51e78642969be1a70ee61caf2e71e26912767cdec203aef3ef7dda7d551a50b08b7e7421b661a99db3e9f21cbcf48b561867cc51e66c8f73bec4dae9d8e48d0f9072d5f2bded4d74423b88b39674b8d7adac4384818ed863e69efa30c3c7a79d37e94441367aa66ce0650da732003b226c98cd15ed39e084877434981382a898c497bcd469e3f4a2664ede9b649a2bb7db76db4feac2f69bc4aa1932e3d276d97f72e07e90b2c67cd9d31fea71b1c75c589f3dabaefeee530de4c233804ae3f4e1c2b8e27faf55e531e3b4f2a678f8474157fa814f7ceddddddddddddddddddddbf47d249dab9201c9851ecb3b2fd66d19163c5f438666ebcca97dec6d7203d067f740449cf548a12df9bfedd4c27b2e35b7e8748436e1147d0f2fe2fec4e7ff2782fdc9e863b34edbff0fe28fc1e0c478f43f04921fe1a35be54fa1ba1cae3c0f12ba1a925ec0105a97bb18925e4f13a4296cf11eaf89530c777e1cc628803c58e27e26dc4cf41fa02ff6c1f054379f23f364ff8632f916d947eb1efe1cd189b4c1d653ce54e1ce7d85156c4f1945b42993df1bbef8187fadd7846bcf7f75a5ab2378fb08b665376842ee4ea17fb9203f2e52569a86cfb6a095b6eeeb0652c61eb087b16f64ae8a93ed937851eba2a7495d075fa6477fab4ea93f3380eddc747a12ff11f206f05f5c9be18ba50bf34991d652e69a09ef11f7fd09b439ffabb092025cc8f3c29a5007041eda6eddd9fe11ca2de4dedaefdd3adeea0ee281d45d2d4b7dfb22873480c59339dc8182992666e2161e64bc2d8f711061e7fbcdc7fdcf4c3fdf6f345d4deab656d9fdba4e06ebee66b164d2752ce2eacb5f62baddff5bbae90e4869a2f7fb13fbf50655a5f44b36cbf21b65ff66f1545dbb6d4278ba44ff3652599fa34bb1bd645aef5bb76bfba8a5da5c9fda5a35bcee8a4cba8016994c95fec5b6b6d9074a1f90197ac3f376bbc97fd72f4dde8a354d3b41c498b7aa63fecfefe40f3f73d80f9eb80745df7b7237a98ebe27bef03b9aebafdc5ed2f6e7f71fb8bfb55a2fa0bf962ffdedb7d37a3ae7bf0470f8ec46e76b7a148943d798ee34cb9b6a364fbdb8fa3dfb0389ebedfc41eea7ee4df4d27368c7beb11e934adeb36d70bb6e47e357426c3759c8907791ea5b3fee4b323eb12caf6ddfee9ffda6bbbbbbbbbbbbbbb35d7e8133791133b6e1bea53f7dcdbaf48ffbd186bdefbdff74423a7a98594e77a91cbbca16cdfc70c8f9a4ad3d18ec8f6b59caaa95ad24823dcfbf541856649d92fa2581640962f2226b92391784a5e54e2e993f5fe7a7fbdbf61fd2fa4afd9bc692f77d1a9cba5114fbc2fbf1f66f63495cb5c2661ac96a3d33395fa46df39ae5ff3ee3b00652873dfdc77f7f7bbeffa937adf7ddb7fadecfd3d0348c6a321f833a4dfc911977335cd83f449d6b2464d59cbfce8fe2b6be4d839cb77d3621077b9819b7fdbb778573e230e8a23f8da9bbe1bffad338001be222f99fa7082bb49bfba5b4ec3d68184b1df613f2163ecd7b081fa641f0cfb27ec1dcc182824cc926649185b69ed1c9f112a4ed514674e9f386ea5a34ac95cc6b1701ce73297712bdce8b99c29eb17fb404064764cb25c05e6feb3e18aee3fd0ecdf0398fd47df7d9fbcfe8ee861a617764d1dd4d40973cffe5e7b1ddf4d8b9af6deecd68c90a819e42ff65d307829019b44a3d168341a8d46a3d168341a99be21a389e3e254713f7382a6cee9df64c29815742fc653a73b6de2dcf88cb4278ea0f87da7813e66dfa51ee60f37d3f7c21ff013bb0dddf86eb8ef963446b8ef1f7520bd0f33d7787b310647de9742d78284b16f23f431648cfd1ae124853327bc8ec5f15e50f4fab91fb7e7bc59e333d2bf893fccfc7dd7853ea880592e9b35327b91cf7aa6be7dfb74d55df568a39ae52ff67fbc56c9a26cbfe4672a9c364e0fd13892866b9cc6691cfbcd7577c4f1dd78a2278edce3af48f79c0ed876a20e9f3873260e4ecfe8e81f01a54d8ab27d97bcd8f427ea72b9ba0278d38be84e9fac94a2cb0ae0321fc3b3e0453ea33a7487aefa648938caa7748ec5c9f6afbbffe644311851b6f6ddb3ff953532fb7b7286cef4903272a676e0f4795f73d7bceedd73239d26de18686ce65e6a3ce8f130985ab0caa7a9e48b7df99a1152ae7cf03a30d9002b316a30a0b2a987656440a2a6cc41d2dc5f592191e40f34f7f700fadfefa6fb7e1d5f0d57c45e9fa85792e1e179a8693a39aa9d7eb10f0464c98927dba794524a27a595d25c05bedf52595ab65f97344a767df54c0f336fdfc42484995420fb238f95656b94aeacb14ef1ba4f1cb51fef3bf187993771bcdf7df71e176a4cb49606f4136a3a12c6fe166a474818fb5ea8bd60c6d8a7a1b6d3a7559fac8b41441ca5f1f48b7dd77d61189116cbf6351ffb3bbe1a3a93a1923436dba733da44d2787694a6a2ae1963bf5db28e1f68f6be07307b7f8304f9207d9a9b2cdb8fbd4c2e2c8364ec370d7231b56095dc236d22db1fbfdc5f258a36215fec739246e3be9bed1bfdf72399bf2373f4ed63852c6739fbd4f5efda3eb0599f91ed3ff187495dd6a452b5d50114fb67137b890b911da6ce04ea1ea18ee5fc0cf9acf3bdf79b75e3fd157f98b97bf1aba1fbed3bd1f350cb5d38a4ffe6f4c9fea7d32ff6ff4d18bf6b3fd936eb3352c55e325fcb9130b60cacfdbd7d721b74e87efbfbe3c3cc9ed84b6ec8e2584b284fb6ef84cadbef9f96e1c65b98fe4f3c752aab5e2ede0b5f0d2deae089fda44ff67d98b9131bd627db43433da3e3fe268e807a91cbfac4eaf76e09b398decaf44bbf7dd941d8657a3dcc7ef7c3c39ab9d0e4284dd52ff6edc5a6f13953b6f3fb39ed3362dfc51f66be22ee93fd7cfbfd6dd664a3260114bef7effdeb0010ed5e691f4d708af60baa04fd42bd59e4c5088f96343ea58f171e5bd6efaf7befcf70a869e76c657a3abf452fa77ef909b9364a1bc76d2c264a2a37be26ba1b1efe86e0c8c138b66d7b628a0904ddb6ca0b3a53ffe81ccbbdf5e6d09e7bfa429aeb6176751b524a6bbda1274ff56ffdeedab90380d58166221ae242ee1f6bb27c1aea6b2fff0040240d779d56eb28151bf9dde6a8928dfcca715c67a3fb86d0efa4a36ad858ad0433a6fd47909ddc3cb97b34e1aa4844a4cd3c92cd94af0b359beef3bab0da7423af0ba94d077a5de8361df63a92d785d3c6db487913418daf0829dbef6cd4b0232961b6b7f970542598352790314ea344c2f48f4d431cee7783a3409b8e4660a661d38952bef84dd2a79b5da4485ea0a34636f27bf886499fcce974a94dcd353a1bf9262fc4d2519c8dfcaee4a8cd46e2e07e2f0d4c6fc8ff701881fdbd7b6e78d2515d78c3c6da4c9932050820625878bea36ed8c8efc0919430bf750908638cb9e0444a3983e5a1e103522a954ae9744105a334706063c937f6c541a39c651eaf34a4c8dfd3ef2f0b4a4068f7170b5d8801e6a8156808ddfbf61b72bbb0b68b6ca7b020db2955c8f60ca06c5ffb865853183830182365a369d58bdc533c6106291bfa57e218b8c093dd51350b5c70c105ace49bdd1232b64571a20732067b6c52420a4288c213b12410296193a41b7773a9dc3f42c85dc30665cc6cb004278a286c64a64d3d7959d91ffb9bcc5842f6975c64ff29e46b62c1e25206918f590b72bfe9fb04e49101042b232777e329c8e4b1b40526ae97942764e06421ad5dc1851661698d01c5e54615f0f4bf22c6a08d09c253f3a1b6a0a3d74a7771a45f35777f17a9d36009da84b775010aabf244892df030818c317e803822ccb0a53182d9cf124be4e06431844f7a5d98b9a2c8a7093297145334142772a010c18503278abcb882e7a4e6076a9b01db6b7f0452328218697c22ee13be4e5243981b1a374b2738f8a1b176779f4dbff60e40b2bfe722357cb280c10e40bc271388a4165de45cf1f3844eac2705292d0544e0f02b62984256851674c0851498b0620b65b895d256ac90b0557421cad64f3827daec86c678faf122630a4b9012032d9c6183c2b1051ac8aa30a50b4300b219e54bc81172ff252aea21ba4ce83a29693429a5942de594d26a95d2a64d74f5ab13485c14f2429fe606f8ed43914403020a1d0d982f3fc97c39c524f3ad0884145f9c3f62fff911e89732e868e8a49ce9a7528a774e1b3ae9327ef69f5f0d2548a55241648f07782ce552cfc899f9fe038f4c0225b7451d46da87d93f5f8b4ee691c630c1af0375a7b3e9ec71cea6b4d6eac42766355ac5716699041dc9659f9ed3323d039a4c57fee8a7a16defbb43e74ea54832c6df0753f69fd6dbb7b6fe976eefc3cd52c2d8c612c6df8623291c79c85489a3a0db8fd06189523245162f226a4046124ee0828d3c82194ba230848429528081fc2be5cbaca5642b0a2b26f4a415d4e405238222fb4983b50488f523616acb7aab32a9ad566d492d75a7c018632c63c2fe0283bd6081312e618c710fa71272633c73e5c3cc41cd14f00084ce4ecf766d0e3ce000913e0589957a5cccf227991f40847c60870fc43e2063456853cf34c6ddb3bbbbbbbbbbbbe76c1c9c4f7d52b50abb2c81310d3d33b68ae50736b99c6480041142c4aa724a7d6a156e55ab4aa612c6fd82c2a5675abac81f9e8bbc25d39f7ac800b17e030e37f40d12462f95f709138b0bc5f4524a2fa5940e75336ff98e2f7195eb388ff738cb7f1cc8835c093deb298e33deec39ae7295abfca54595dd5b59f5498555a026f5a7679854577dd5a16b7b7a66ac5272d3eaa393694bc2dc1ba4d4e362963fc9fcb04e592c0f7bf75e25e8206182c891249ee779dee801f13ccfc39e07f33c1c72c0017603ac47e874bae1c791934c1021449024c9414a31b1b8f4bdf79e4e38f4cc68fa71e4241344081124496ec8c1c462e24c26530f991ea7d349ca2e152e1863fc18e39ed9e475f52003635cc218e32ae8c8489ace445cdb05b26d2548097101902034504a29a53f240c25e2a24976208204871c3ec0830f2548d17b8488a32875517a04c9111711974e0e3a7943a7b9ddaedd369b82d7762db5d6da27f9fedf1e82e4fa8ba94f5307fbf747fbde154bddedd30d7dea7f2dcc54c0aed9eb9e22b2eeaeb9e804f742dadee03dc49da35c88837b0e386c5274b2935eb55ab5b6fe542da1c324f74f1dab3191a467b6a2596bad633491de2e810071e9d3d4a1bea98ab74a4a828011aa26511d393d334a17e50010a8a9440e3bf07023cfc8d7183ab967464d9b7eadd22a7429a5f42461e6d5e993dc9e7e89b2c8e06c4fc4db6c2214f573d0effc1b42bb7e9f4c9246e6df421c6a0e375812048c58cd17df94502ad998eed3dbdddfa794d47d66774da7b44369d676afb46b155dae0b13b47a2ba439a76bd59b4a413acdb2fed44125bb3badfe92869a289f3af5222e7ada905b351ad6dc39ed69651d51517a969f8b94ee000717acbc158137262c908c278ff8162143620cdc59be64313049eccd1ee0dca38c2cb5f5135911b9ff62133665f99212cb1e0ec2f7b17fcf9442208380cc97970c2c1c0c30f27c79041b4481440e589046105cd0c2e5c42a0a2a308295453f992fe7dff9a62944ba9068f7852382b6f681c6b2894faca07a4f11d4bec92251848bc5e2b50516aca02d16e2f7e9095b20228f32a8280d15b84c2bc660419bc337b27c15415144461e6536247cc18331ba2005eb052d702df9c014ba88c24e4a070b424638bd9014ea419e2fe7dff92697cdd2094e1a535c8e58b2450f9808ad863001109e2042166958a1e3c415f278ca634cf1811358e0d3841ee098920417e4c0065534a9e28af9f2ce37cd287c68c2881f2734e1029c554f61bebc623bc3c70c962c410b515c2d10832eb181143f5809d10420aecba30cda19ba2eb3ee8aa63e5951c9cf176130d103a122a6b0620b2f0a75e38247548534583190c22c0a94920cee0502a85571c5a80a1fed5e22b095b26d5b1539f0aad8a2e5f75e1cf8008c1f8891451652acc819825a2184b799b185ab0b2229448002194b4c97a381c8112d8f2eaf222bac94820419b9ff9ade84234b2748300109314c44a093190d443022093e2990220768b816f21c23e552c118682c91624a010734b88288a808520c010b6ebb5b149f1537f09149b0bf8932d312218bdc0d85dc7f72998ea902755b3cb994b0274d43c8c0c91048e41e8a829422a1269491852a7825ef25e549c6593a198288bc65e96488557e5d612ba3002940c2136c908430a4a0c2868c3c97e00206668c5145103f688931d184217041074d563baca2684203293d4b1c6104ad0547c6c829821742403182ac896ca4c862064100010a5430f1910333b290f3041386f0ba02b50415568802c6853070b06de1068c2c70c044e6050c8ce8c069be69de2c8d603385b8d2031d1d22cb4772801fe0796713b866e92415cbf3e56896b0002359fa472c119746e1345110180b7e70be1e31b03033676ec89718aeb6696a65147277d1cc5307faf647fa1eb5a522641eeedaa7c93244eb93f83d7a79f60a5658e857416cc7d54c102a6ad9742885a26063849d27f408610526301b34b45002cca6ab5e873240361e02d1413016156c50609760233b86116bd28e2419674451d9a0a0e7bc4127dd096b967df0aca34d905d24cc945d64c1e6009d74195d2777cfc6ea5e79f7ddf4262069eabc17e371cab253279a369dd8711e21f7cbeedf3bef8f3b9cd4600519f66ad87ff2c23a9d489ad67e1e6109390b49a3bd13962005275c410ab9a79ddfee44b356fcae7342fa8bca18f8fbb157267f6927b0bf34c9fb09481a1fe52c8b09489ace6d051519ee7eec559ec0cce25e8ce78eec3feec89d7492bdf7c44ece565b1e5f2e218fbd925d6427b84ed75b0fa808849ce580090f6c50f07961535f898d873aa0d415e0b02ec153a45747120a451258e8d8a0e0c3810d7d25364000d1a2850d556243461201a572a8b4abd8ec7a1c2aa20100000000e315000028100e870362b17814a7a1a2db0314800d749c4c625e389509a318077290310829040c3200000004466464c6093a8da9f6ad05b91ccb04988a9131ab67c2e0b991b1977d0988a9d0e7f7d5bc433d8b339da42fc8d552e7d31281609cb0b24750079c8130ed05250b2058a388eb2cce857d4e709ace3cdca279f7eb810ee218d9a121ef0270b5d3e00f9e07a11e74a5b97e716845e431833eadd0da4a3818981611a375120ead845548cb9863d0e98e76cbd16537085a4243fa02a40c2a705469f0f92ae35c71219e0c89ca1db60c4a68f1df754be56a819d942143ac2b64709eff954c71acb142c9bab1401b31169e05914cb42b35142f8f889d6651ed7f6c47129c2363ddf9791877078c38f068f55704f8de769fa04762bc925224c6b97f93d6c30a6d9a473b31fa34b6f779aa1d1eba2939c8caa98becb88dc071db3622ed9285e8ea764e4a2aa9cb371d791be6c35d83035d14f56b1dadf2bea59906a70708074d837a8552da3490be379bb71d14b90cf9b224e4a9b45b5bacb908a811d8f869e9bd423ba33a08285d3dbf95ed2fcbd7f1d0d485b9f6eb68aa7e09f20070c3d7cfb9f16e1d98adfcafcaec0eaffc58f209d0a902df81cab0d03cc9b0fd5e9fa341fb7a3180dbc75debcc2a71c38489d25177ccd06acd7beb702099d2c863e62229ead4a964d01854c3fc73accbd1dcd23f7bc16f274be2b25ba95852e280e08a1a617100c026c6db2af3a7f674230f50b9c102a2b8464ffb7635ecd49d2c8fa315d4bed746898cec9354274555bdf35a5ffaa0be8b1c04d358c395f678c7c2bd60eb3742445d8e06c41774981f748e3fecc89d9a869dc75cf1aaeb68003fea8260b5a0a41327d9791229b831f99dbd4b5f90357cda0e36ff5126acc106b341c1d9c0cde103ff38f579121ac6dae904b73ba26c15a45e757699ed806a97226a822461fcb7f94b3c8774389d5e90120f83708c9b02cbf9d9d24d180c344f7db58cef61adae56d1a21b44d35c6f4a9e9e8983fc54027a912c17ebbec5758be0c946e6b3c7bf79331eaad4029d72d51bf04e4751af87a5b7168cecde1f78e52a0f08fc5d0ab192fcd2fc02ba182c7c8eb1156e05be1811e4fbe943fdaa1e023ff96c9fdb57633aff762a5649d9227a9471b3f1fd77972e801a0274e6963ca41ffd71958530a09ad1773587cc8b998a909c99166ab88f4ff83e9a808cd055ac0951da458bb43c71693b59548d4028d49f65f62deb0a130880377ed4afe80175e1a075a62cff804f8fe899dd758b6f3ae158fb8fc9b94be8856532477a6f453d5c8e844827d4377fd0e9a5b6d842fe33fa24a7f8e7a2e2a89f8c6f2a75c6b0de2af344d352b1bd3257434ff58bad95ff85b7b4e9c537831b3824c637993c0b0a700edf1e279c7ab0125d236bfbf79b1d2ab55e5d7d28535fc3a49eefbf63460b87aef458a6fe9d6d392ffdd2d68ee6b96013e2b047cba1c1a77f289745e926faec7cb14488b8bb90b1310ddf2c8653ffdfa055619b2701eae7c8c75edbe68863bb0d316f30c90940a5a2d910d66ae19ce3fdd8b0e633a0120b45f14dcf2f09f852d139c2de4f7c2d4d4c38c25d673474e3299b34be43dbda0b96b0203e3f89f32aeda4da858a5dd244cde9862adb59d3e3599e5320f918d0dec7e76df6d6cb055a84967f4079fcc0f3e1dc05d79247834147d254c72debe58f5f3500c83f6e10d391d79de47d158b295af0ba12b2051cf0dcb6e1c08dfa8c27ba9c1e1ab3f376f33ff60ab4a6686acc9f122f6e22a40ec7246d94a90c711d8355413ec31fec89aea07ba272c10586368ce030fe1fe79207d097b365d58de66a6801c1a5f0858e5a614256b6848cbc7356433e5566d29f73be01662b5f8dd4a47c89aa08f1a68170ce3e777588d31505bc6c11317309dac01ed41bc73a99deea7f1705ec704da387c375d72f6c39509dd1b369ab634a5009c9c3b04dbd048cc2bdc8b540d12e448d903e29ca1b42a7fba2aae46532f742f7b23271af66b961d82083fd584172d9415e002a09709b55b36e12e582776f76c3a4b05590c6e8db3af0689b20ae2b884fefe1a61b3973e27fc6490ec8d5c6fe0f8b023a726fc83cbc5bb16e03ca1ba49d71602e22a36fc4af9f5ce7cc1a5550d408e8509c20a19fc4b968e058cead90b0279d38dfc6425ea126263d3d34b61c2db0e0b0288f8a6ee038ad40b4fc8060139c6e1ae394afe794ce999b6616104650390e135df06c616a91885d57fe566c44051661e42cc2db9e1533e5b4d2a572aaa68d3849196311886852c21e212a7e65b6cfead987fe3a3e5580f048dd389f661a4dbae51274d8d70b0e433a0440ad97576d4379ea50eec0be184e9ba56fa7dd5e26f8105b52de06bd416f3d3f7c85c6bd57ece78b634538bde54969dc4ef3300ae2023a49ac8d4b79208db332d939239bcda7af783b760d86d08387551a8702bd515bf5f778c233f379a8f1a06fd15ea8b446c8b6ee74986d748da9d59252474824f5e528b2b17ba5879120d462e605294328de5c98534bebf3478194065c1973beea4b30058bcbb19a618ebf39d3309206fb59bf2784ec265d1448dbcba98a4c5865d720e7b240e836aa50c4f042f89dcdf8f1073d1341cf67fda842fdbd1fac4b83ee70843fb64bf6fef4a6678457d36eac6b96fff73ec57c1637ac76f0ec6bb2a888efb379f89c69f6849fb0c1a8725adb12dd8d3a6e7f5af6d990f78e535d9fa8b901cbfba336404d2997f457a1fcbfc1bba11221b0ba07fa23da392ebccf6e2f4b18f67d9ebbc0df42b9a387a61f256a77b881abdf7b087b388a1f426ca176019c3fc6e036c117626250c1dcaa95a2677578ffc263cf475c9271a279ae9de2eedf481a85f702d38f4faad37a4851a0064a9fde4c1a02b4086117cdf455af1099b0e4200b980e357e001cd6999a017a1234ca6aa3f4aea7741ddd144171178e6f3ae27372ecbbc8094255f23bd477965df76543a47a1bea8a4fa1f5a6c322ce017f2fb5d3b583bc37530606addb181f9517bb99951a19989a1ee4c63595a95a4431a62de1f470321f027baa07b929146d973ac1c5b63da001b20ade3b14d526d83dda276d72fb12238b79faf83ed531609a6f543f2f67d52af6a43c66873b51c419294d3be94a4843c745fb9f7d56bdf25066103762d3082aa350dcfef1a076d35b9486395d666f925b1acbfe82381d4504cd97a43304886c5b9e0dd58033481548815bafe8e98de9c9cac6ae9db311a9f1dbe53ea8a9e9eb749e7d0982b3838aa6fb74bd9a20a9c688aeeecb87898bd3557b188fe895e6f1885ee53ff8a29747ca2676ecdbfdeb26c71d55003cb7eec750c7843ce0590abf6b37bc8034e00d048b7c667954bd7ca4900d4b45282d419ca2bec8597ccd90fd268a70afae2d45cf4ee3d12f37d6a9016efbd656b6c34769cafddd668edf622580af0ced03d4637b54b76b3820742675af35961968e87e53d8be6a05111c831c1b040363fb8afe75b14f5806ac54d40759b8e62c65b8bc0ca5ba474cf350750c3641eb4d0d88c695a05a82b7897f4c0ace25e48a6082d9f15beeac220df4ca9babfbc4d35b0bb06947e03f1ef062d61c5df47a2ef67fb13b40b7da8931c6719a567f4f74a9761cbf16ee5427f911e60a8ba0b0e4feb899754bd400c68d3b31aeff1176ce34f0785274f74c4550d2914d7ce4d81c74f050e43d892f07b25713af8514ff31e5d8b2e6e7c3284470a6b1c77ae370c3c2411ee7931d7c5589018246c8566c460e1d2e59931027ddf0bcafddfe3df131bc34c095482c8b61df8a9ef03bca54499a3a4e6e12fae68373f70a6d1a1efc5956130e4cf9e0396953a74ff4f8f9b2dcb86cb4a011e329a1ce4f4da65b99386be3819d306f48889e08049dda80bee42eaae079fe523cebdf139fc95ebfb0285dfaab7e788509bdb1dea5648375b5f4be08fa8190c39fc7ada8236ae5067b36da2fa61a530f0aeed36fe159ffbdbfa62a480de32b27bfca31179be180738658eaac37351e432c8a420d85aa562c87f8e48eacc425dadf72a04de3ba538e214c5b7cb07256646b53eafebc42a1e20a76bec600bae05d19a9be31822421cfb21e9224f5f6e761e984b54768971263894f892c0d023c67c699e9fb7e39d67a98fa9ab054b743512120ac832b606df39099fe1f64450c36d96049eb118065377a5f37112fe2f02562bdb2450596c2af13886655a27892cff1b5501df2a97cbbc812a699751b9f1035701d0a688ca7d77fefad7b91cc746be61847295399d6fdce389b804e3c0b9f0670cd1062456a64cf7136cd1dc94cb71750a19b96ca5844b9bd48e0464333fb30b674efa2c07f7fef3d1b80373b93ab4f9c0539c5d98fc5662ae3bd2ac558073ccffbd744def436235ebb57ed2a502162e91eaeeda65daf419b5c04c0b8bca4ddc8e47310a74e6e665e50730c98f0c0a5ceb466e3154a847869052eb27c78c890e61f0877f9a8f8a064d0ad60de59e04c1f0d3f78cf64f0f2b1a2fe6488db1c6ced04e07f320a9356db841b582e2fe94f13df4e5e65d0a0eb281f18531ab0ed37cce97daabfcef3939c481fe088b60c7d9103e57df1b5c61bc132ca48d4457cda01920c9b496415efda67d47a90a92f847e176f087f93d2223a2b8f145eeb1866a6817ebc554f0f85cc7f5ef153f52c6a1bf58500d32b2e59de8c2c014f74b61f88cdedd352acf4421bb3a769e3ccfe75f2221349f618fea2dc0ed6ad1cfb5c17dc473ec473ba4c3ef43877f64375171881ac0b4063be27513a0d63163176a7bd9dbf487f34d159bc9bc5fb15952f17ab4f4755bfc2cc8db2dd1b2c9260bc500c87dc6fc067097badd6e8eef82628e56db0c1d31911779a93c603806ed6806687c87b4450804c2f467c92d6994f44fed965964f8c5476534e3ad29afd7b966b5f76ad0dc95b5de83461ee814183bafb4d26e55880cb90a1fa70a44a91077ffc385392e919707ea145bee886c273389da6c5d9a66ca5dad2d45be0a1a55bbce40656a43bca474050eae6b11a2e425717fd18c4419f6bc688d6d73855aef0768cb1b1c9dc5c9cfe2e96500a3d62ae032c9cd4dc23130373e30fc74c3ed87f320d6f009113843dfa36537f672cca95ee6c9a6795431884f897a488e0807f1808210444935e8094697984da22c68057b4e9c3a32755c2fee8acc18aae8c160b4b48553abe9ee0ef0fee8e4d65120aaee022a8bfe253ae12b343911a238d12dc2d85fb86654948c68d4df1186aba98dd48f2d78854afc5fd97c7040162a9d08008e60eaf453059a769cd915877d4fe923943bccf748a88f4103890d4b963402fac82e8e4114ec0147b90a47a5318627231d17948779b620bb1d84b96caa8e0e01a4db3e5748415c208134a574689985eff8daf0719b5ead8e0df3c37d91c261587d1c4568436add23aa5b438519530bf379e3f2411f2b38a099f0839243101bb6248bbbecad4c136b3e9df10a80bf243b3aaa686f2f9484ba71e4a7eb9d6589a67449249a0b279e5430186e94613737ebf282e411e828400c5b8ead904aa1e2947f989ac782c858a6c046fc613709904a35af3c4e07d7900d45b9536a16dead1a3c7853027cefc6c2c34aa70f9ccaa364121184dba9dd220a164005521b0cc02fb354ab65d1029ec903bf60e9b5842712c02e08a55708ae4efc18ef5b920811a18d207db42c87e7b8e1304ffff351a149e9bc24c1344a23cbcf55309397e410b5bde5723c081653f3e0825956caae259c8548720743f666d92d0884b39244446f4ff049882fa8112cc8f1e00bf35ac4ce229329057099eaf3571734bb8b6d25c8373bf7e855c3f09b06a7f7e4bcda395ed8a0b249b1dacac9b42819a3690c400b3ab76dbc9f3d433b95ca9f05aae8c4b83319a81bedca062676407150121fb77a8902fc66e52814136a53130793a4e91daeae458fc2a024b8ebddbba1ea7e051a6f08de2c75d2378fee3ba7550d37bc1db8112a21ab9839865ec7a9b665dc221c6bf1834c6b08c006f40e6b4689b0909a69d0f1b7981f1097efb9e6b15a11695f644cbdb69f3a75d4fc1efb19e40b8db83d178344cfbb58d63121d1cc981bb82a2da982487ad53a050d0df684d584aef3a3816938651ac2ffd4cfc2a7e747098ed32c0117c27015a2e7bdd5ace95b1e9371678a965e1f1aaf89d0713f88d6f393ff989945e63adef2679e7e7898272f02a7460b87c8b2477d419f3ea526d71473bada923bc83274c6e9c67488d83ce28f86756c1738b3346cbababf17c9b4cbe5c7e1894868c0f4ec9ba049868d2f8a4338c129564bf5dded27a182f7fd78c55de27dc586b90d159755657619e3553750590db45af0c7375b5d17fa8c4496b94248735f6ec08b729ff411fabc2b3fa328afc9866e35da067e41def2bcd53318e0de70e86642e2154c4d9aa77ce6c6cfa28bca8096f30d1b00512318e5d07a18f2b87f6bc385d9999593e440c1545c149832cc71bdf976110d324553c89577a995fc12e4d6d44e26e43f28c3ff3b9db0dd74ce079ff787028e4e2a95d1b121b77e492cda06a0c89d9165097a58e77c0815029aacaa0cb0149a8095f1d42b5db2c19f58502c5a70672cd986857d1f642f8a16f16b33a04a01f233635e8fb310449922117c38054ff501a87496c2a9a588db5884f04089ebc0d7408749f4395b34882acd09de0342aa963d8602bb1ba0190ac3cfd6e40543a603db0572a2094892b425844d1ec059f0d9d847c9264a35bbd6fa170707d7454dbe0b7023b16670548ec2ff3811e24d57b0c7f114b9f603a7d0186e6aa64862e639b6e1b132fab15bb3259ae2c4027e5d9c41356695a222e9c46476713965c03fd4ad403c3c2f8dfeec97971086569af872e6b3ac31ea230d2124bbfc8f9a0801fe68d7f552ce9c7d1049e5975b70015c308c99050e810deb70d101abdaa26d227a157b340634427506f77083e80efedf04cbb33d84b402deaf2db10a4b7178c5cd06a786659e25216accc5a4e5a7e53dd6301fec212bdb6b522fed489b26fe2a03737b892382040ecae225cc62bc4944824393c19968e3112e2e7345cdc391187bcb16acedc9432621239a2e7a753a8488a732d747232f658a0b206059bb18f32c5f9f279bd256ffe590b402c1ae2e6decb119031eadc77fd2d4d202c3650ec59e9e496c3f16c4744b93fb681c80f9181ce4bff606c203eec83c9df76402151256ff620d071f0671470b606674178b01b66be135d92efa2110c3706f02f0a931b27683888399fcb9c8bd29f5fd9ba9323636de34f3674cb44142931ba9571e11476dc21e06bd1e5b194ce5e3d504704cda92bacc0636dd403c95980686ec63a7da12687032ef48b12c4720e6abedb5924782465952ee9851549e031fc1ce08c3f787e4e0fdea749fd5326a7dbe0cc7bcfc9e024cac0f24ab802b4ad4ba80ab63472036bba04b982b117710e1066012597e914214db93b0fa67d730496e0be7be2abf8ac055bd4f90bcc0610067b2735526b01f8322ae2136f20b5b1c0549b5a7aac280bfd12e4c809b1ce15fe37a510347ad53543b54bcc5a5415e368e428b75f810c07ed5c13f4c220725325387b06eaa1b07332df569150c4a3ed22be6f952b612b1c36a571874b6b6078072c055bf4155ff7039e6892621274d6e8e904de7fa89bd5045a72b146e0e7d6d75116aaedc4d207e079d7d57a0c3a63194b4745c05c160396af62d226791fb9842f9ce90887507cc0ad15641532b6df78ee291f5be1ed911af9dd80f683fb1abcfda6a96d4aa6294bc539778ae5f63031e4008adb38caedb343e2aa799e05f0bef535de39a1b21362fd4b7e594e98ee6892b354813ad4df666a4789dbd294c626f67a3355b6cf7e55fa8e844c7b2dc18f3748e68547bcfa6c8dd4d5fc23316df916e3f91cf6175e024bd103ca4c70eb11a41c072e5da6104ba8c3c19aade8d8d65983be644805e140baa198cb6ab0ccad2c8c19cd1320e8cd08e728fd4cde46bd108be8d217d865f18a9fb5651547fd805340487f5a363c94b87a432e65471fe3b4b82c79312945ea4bc2b8c95f997990aa07653b991ad82b600469a3899dd3273bee028e0c8be326f695570d79d7a67d4d2ca3c7725155c7fad08ccc8ac90f22b06453126b16cbb0e171774521ddb2329e0beca9cfbe8e8100b370a90557d8f5c5518864813bbcc37b8166d029ab3ac6ca2a8d288724d7dbf1833be94a90f960f2256be370abc4562e6235088f5995a9ba62c84b33b988b43ba22da25c96b116a250d9c094a75d84c74a07e54a623eda2e2876d206cf754e96123ae7d448418933bfe40cf14e200aca329cb31977e6179e247c7a9afc038a4de00beafb59f1c60fd935050b5aab00379a46eb390c0004a2db0205f6bb2a2106b7f5ff045046f04ff117c98993b4d922a5f0f333cd31c3f3110c2c21f7012ceb471cd73192cc6905b97244cec9cbfe3b61832c1207cec436a072a76fe1fd2e546d09c46890c4d17b174f21484d00a3f725e4bccf9ac707219cf43f23c5c4c8137061d790517d51d73989c5c81711b6cacdbd28fe623d5abf9318c72d52875b864e076baf01e990816c4c4230820204a88455177f57641458ba9c4aa4f6cf182ffa8c53ee3d4d4a1c686a983d83dad02066222cb266ad6d68ecca93d80a1a3b58687e7a6818dccead0310c13e418a2cbecab0dd52d57c0897adcf10ab711a34795cb05add6f8d391c265126d2dbb90cf4bdc80ad019d8b5445e71b939bce0b335628414eee849ad10345d24dd59a3964526492f924c6f4f182d8e19b8aa686f53282a2a8f28b81c3da7220ab93bbd43a441c00f492f64d2d26f009877087bbb1b93cc3f485f9a505188a0af3d993e7ae1ff0e9546a694195e310384a9bfc2c1d9f5e724c0aa984d3ef55af96086194490d5b62f99a227b8777f85981414cea5b2680038442363e89be6c6b6e87b7f1994ca23cf8822ae7ab3efcfbd0458dcf4c28412b50733e2e585dd638c4aa6076c76e9722426198690f6f98982e5bd26708a96ae53b9d6c70a1d994b4f3f307afef40767e62b612670903e49162cbbafd944d19b84692c8e7beb0b5e491824ddd55ac6076a1f8568bee3b505a838381b6bc06461085226788ad48291d877c968ae0ef14cf192cc21f4c2492ff8b301346d1475ee907a0d6efe072bdc085ddf90337eeaff039fed1740a6451b4a3af409915d24e8b2d8e5ef1112217a676edc07ebb8e19017317a12bdfb86ff4033b7d5a4ea454c7c6483870ac52ef0889f5074c4796f63f7824b28f10680ac3f4c3a335eb6f2410c567afeb1193f0cb2feadf3fc5afaf9a107754bacf1f2abf001bd7079b179a98b4eea5f8643d3c0659da5b88eaa6266d1061cdafcb5ae90deb084ccc4d1beadbe8cd30bb35b90414d80119dbeb80bcefc7991cb15c8f13afcc07f024eb8e2aeac80b0f712d1a21181e88deefdb1aac00b96f1ff86a2f36b1a1cdd1a21bf87f5526f7280ceea63d1e3c7a72aafd17805aed7e87f8b457543ed30cc0d31defb848bc96d3ec2a18eba42451605a3cae5356453fd2f18ea5e1deae756942ce135592c764b9f9bbb56f6dea4158cd7b107b8b688882e464d409a5eba53ebb4d2abc6822f0f090b50f99120998f58088df305a1d74d09f79609d243de29245ede611190bc3c6347481b05e42c230090bf35bb06a6fb9e897f81f81c313f260ce68ce84a1fa3d5a5a48485e45fab2daff8e4d399b701594d1f6c715d544f03c40e9beb38f5bc0b193a86dc9512b3253b042b8dfb75e907b7ab7741725dec67e1c133ab2b5d1c91894909e4400020a878900b2884d23267779ee78f5362dcbcb4d32e8d3a3a7d374672197455fdd1eb70cae61a8525d73d6c5368054e7cdfb03718b3e1146a85117afe9a7dd9f98be5dccf3cdc75ab52792bddd9257c9ecfc93b2808dae7c8fc158fae7aab5a0b3b75eb56e01897a5edfee8ffc5c1aa68f9976ea86892fa5f3caaa3420978e524fd3131bbe38c69cbf0f267f4590ead3894ece3114899f99ed506b7e62d12004b4fe9a9e4d31e85b211f26a9d5c734224bde658470b4d1a4115fbe59a7aaa83c54cab5712e1030bba7d36d0275250d49f675b9c08ae7ac8f59586212eed4f46f59075b777912a5979ae32967d7f220091c7c60853c218bc45e7f942814cb9dae5008040a84098b2d08d82b39174f6d2a69da87d757aa9ce90e11aaca16b6db5f2ad9547bbfc224dfafbcfcaac20994730de58da8cc155b18fa226d6e9e40ad8758c959bda3e425955802d8107e189169e4ee8ea694312eec0f87e2d4f5a2a2f0d2cb97777515df8ecfb9121f843b648a10e3139ea1ef02423ed366764ef7ecc19c54ea7c76a5d10fa2dd33775cdb98ede619fb1262f4133eaa795fb395d64207b665db3e7499640ec397a428a5d42612893f69e527aeba301e6ea437453baf29b74e8a548017626930815031ee6372b93e9266c251630ba6e4ab8f8ecda9fa29934263fbd0d13834fe7c71527010f64d33a43791c2f9af940ece21e43c1480c614cf26c92b6d21090e9aab49de99c7f46f5be67d88908fd4268ef15839311bca140ec8d62d354a9df11a567d291d8abd3de7a537950a317b049f05dbf2ad7be8807cd8e7a3f76d061215e3a02a34d62796c0e7aed05c07d7da91d22c814e7f3b7ab5ae612cba1efe3290172a953d7a5cc2655d0f84eb58a12d90eb615d88e06aa158380f7374e2c30b97a8f7257daed87fabf072fdf10d873edc64d2952aaea1503efcac48c8d1e7c5cd546b42e3e6931307d3d3a818666f3618b0c2dfd74037bc809f7af80cc0a10def3224cbc10a78bca77315be4b1204841bd379a7ccb7d47b0c02f91dad3f8eb51127b7f2cabd66a7dee00662987de2185e1fb721065eade08a78e9c112113e302773ba976ef7f244c3dbef45f7ee1c5a42198d21dccb0e5cb72f30923a346b8652619508c5babf5c297588323650cad16f3789d91302d22cab54f1a03dc449e74190570043f6543ea4c5bb12f20535c549ecec81b02c094298ab1d69d4fe1b4b0b71c45cb5e6f84d155ccd058e825fc087f761ef0c95df11be05bf58af00105e94cf2e3ab4ccc0ff5f73b4c4122a2559c15391c680822253e43932711d98df6ff1940ae564e386ab9b43cab741195a9d63d3be8bd80737e108d3a44047f288ecc69906a36068e8f6f795ec23a483dee6de464f7fdd85fd64ce1667a0b0937f831359062e206e5ebb6913128ff28c6acb3f3f1ca4b8a1eebf81c518ff25681914a4f0e1db7d29549e8a18cbfd70abfe857facc36206d3442ab0fd34ca788c01a0fadff3d28343600a4235521c5d28c3296dacd1c630bd017ac936a40048be280f37af8dbcef34c046807933cc7bc146f9c892533228e3238d30d2e027f1a8fb51f1288ec45c09affcdc53ff61a1e0c815af26e1ef1ac440600aa924308cc663056cb8a04259e8c7605736108b40dc93915d8ac2af4590b68352cb28cde3a16bfcd06940faa70f0c1a2a33a75e191385a5dfc8e05bf8af0bff677aba120ea3c07131c602005771f3f8500cbea4d8a619b130cfcc5fc9de280fa178a6b639ee2888fed84047492c17c992d9deeda5661e27c85aa879b41d3c7a557fa6062e7abd9f2272809719c6ccb336b8abdadf9987c88738cdf31fa504bf1cfefa442faa08c17c123052fe3c9536ca4d762274fefa47026999526fac55698d522e8862ced8734b4ad8bb23896de8e1167d880abab1f84d4b15bfc30963496bb2cfa277957ce33a6df31849809a70ae12b30be5f8946913246fccb0b31c0e73f9a0941ccdad943354c549c8d297ad57b38ead07bea701d3445a6be21e8e5eea95c5ba4d511549126f81e0f565c55142420753c68a9b913280a2bd272eac1d110f9a4f3861db3ee5e259e8143c325cc3436ada11d623865b4521bada6323a7ba75e705caecc7c2c278233a23f89dead1c84e08364427c008577fced0f22ddb818768f1efbfa7d14ae67ae26414056657bc9e9400d7212b86a30f72f28672eeedb438cf99342a0637829bf500837765bf53bb2fd64c87c47518a9fa14f24c6f9c93685ffc5e845a729c8db101277366ec9ef42434d67ed99049a6ca1ee056940be8dd984552b194dfba524163285e69ec729a5d973d03817387c1f15083ad01e00d7a5b392c618a8540ec8820ae22492499150b549aaee81f29810752b40885c66827b7998a12fa0d5fbf243deec9575678917cf9f3733a881ada58758c4a7548390dce034b95362968ca8e34642ca8f15d04cdf093c34b98a58ab53ee33e3312e8c1765246b01ed9961f4f814ceb79631651b9a59d92c28fce92e5325e6bdeb3491b0f953fca84e94a69b92f55646eec768688915192b3be1d5931369c7b4e753969bfac1472669ce39cf332a93de0768ed324549da11762c9272b2499ae267e39f36a02919a3472b9ab951bd1ea964a2d49866cab85369b15bafa469a838b6d193729b9c797abaaf4b5617e1de70a3b8bf2516ae467a25d0fc94dba37fc51f4a032aa9b64b36c01a12584580426d532859aaf14ddc304ef10507c93a0089b9a2e2cee7e7e0885d0ce2c9034133caf73366b3440d3986bd119f90fdd403243dcb702c401ea165640751652fa4c5c6f105e5913b3de3b30d0311b49e6ce71282e8a5fdb567a100ff63238b4bd068bd9195eec69b6583612b91b3bc1729784a011f78336955f823be5560c6771eb1b0003982ac16b2736b68afb020afad084175f7cb2d2ec6d1eff79f5e2af9a4f9e995ce4a65acfca8d0947ca155ab236511bad1defa27081dca29420b6440f803ae5fcf405d65c9e9ac132353eb27f47fa9d82685c140d1f2d534751594dedbc26b53f13b8eae0d85fe3f9764ca85130639ca9925b5d6486e77242229b4df7b2e484465f3263a3d8e8a5d302c4153fcfb8d1f566f4a8d312c807d4ee0b683b328cc33dd3c939bc3101fde61c6519926ff848c84c6d0e18bae13b5750a68ec55e562860433354527cef4de9e389e91b6b634a0f7e6d0ab637427a77c87f63e31ffd1c281b1782f9f8f3c1592883145d2523bb3b929a73c53f4d64048f29149622d085007bbabc9c7a0f32f7842c786ae58248d15413f7a7c86aa1bb74cc767d495b0945ece260e6189054db7a46e93a96e9fb2f255a9e7fbedaa09433ddb0aec85883acf3a6a9580fbdbb4d8b42e4618c00060668f690bbda51112e08d074d858c8374c74ee80d664974f1ae0955e174f918a60e54eb22c8034eab1968d166e8edac0eeccd337387f5a0b61ae71d6428858cbccbf0ac0d137f78453da341944328db27249cf5e9e745da681af297f40317582cdc2ea3b7d71199930230e00bdee87fa43e95bd033147928e22077129f3c5a7e0f26815c6cea73a094e7458852455212ec8de562a976808ec0f3695dbd70e0329e7d44c832c64a5867c2b6299861e5794a823538cdef737dcff7f8484ff7a7d31e93be9dcaa480d39a843559d878638223b74948b6aea805232d79ecb48c4e62eee074328ed0e68f79429e0dc026ac6454cd77208d9ff3469542bf86102b1a0b6caa83a0c7c93310182858ec390d395db865d98ea5603be6be665e80a938ed64597afb15db10a92e354c3fd718b40a8d588aa42018a6eccfd35fabbbbed52c2301812dc722a913df0eae6f0708fb76fd8b5e837a2c0f56f00cc6de60fd5e914a1ed9e3f10dc8708b12cbbaf10c9b1c7a55934add6265256a5373e54aed42dc0d81eacaaeef96d78bccf157e7460d399a653b2bd6901cdda503e2200ca9abaa0684f9ea428f8082de67de51bc1e8c844be0fc818b2053357e745196e4f2c71d16da3b579dc39330902eb17b026fff073757286cc54c223075802a492b10f563ba64dc17827c148bc07c790b869d2ff4284c4c428779570fe35a69792050097a8fe6154fe37db4977272a7c986964bac982159b9a30dd322f529838485c654d9e8c6fa2fa050f122d908a2abd2cdc4b29a674ce27d429583a153168946b6d0fae7213590df70b1c498ef31bfa88b04a7a7229817e695b8a9540515843b62eedaa2907a005f39521a58334dfc2dfe7a3942ae0a37bdadd63a4291378c0d770fd82bdcae0d0d8f611db5783569b263d6c023b49dbca901b1fadc9cf65201d05287de5d5c7788cb24b00f3f889ff734619c27485f7620454158950eafd3c9825cb383cda6f425e79ad7b973eabaf24275b962749b1525a2cacc0021069682d70a4372e2b56503a52f99096681f5c2245699c92e0376986e02c388a89d37884239b01490972f1381e93be27c813b4100fff910219558d5d06761d9621fd75f48861963d8a67d4422caf30366aeb86ef7efffab8d50dd6b3ce92f194169d3197f68727d4b66b07906e9b8bd30266540a6caaebd896369ceeef263acd1c0fb370a27d5462597fb989d7ee97e7f310511cde2f7182085e214d81a3ed718b6d4f53839d76b6f68f57cc9ecd3f5a48b08d9bec21c6feccb8daaa9846b94d345230285df8efbac0d24f017242709106cfa8ce2a4d90f8303f129ee6cf591262c805c94ca94aec594ca103d8949f1e07a3e11b7d622fb939e723c7f2491307f47f14179a1908244fde9ea32a3e9b8c189dcce5e61ae22261d5acd04cc636f763dc0be1ba1aae230872862a807dc15c9961ef8e020045c1d99145012e62f3309c6152ff7d488cd853eaa61566d5c1a3c5f0b4a16798555434b13343eb4c53dc93cdc5715a40587237dfdac8a35b225286ce3953886e6c639773aebd66e60b16df2867c2d8a7829ccf2002de5f3484d031bd47ed048424e6d1289b17dc1754fd9b16e700fb386970a4ba52ab768fc90dfb6118811cfeef5f63da7ac9d17bd996a1fcf3480373ec5804fd93a0fa8fc555aa4764d39d5c317d293d36b0bf00ba2b4e1c748f3186835bc5e026305b9cd581bf743d0e115559c82c6699e5c801df3bd3b5ed8e5642197ae6a18211e169ede542c2c87cbb5d684de7776da49e04e1c95e4add8aa9131ed83ce51b0e5839ad413f3c9efe81c3ba474357604cf708620b8ab695693edb70747c34c311e9e3b4eaa080be5559f5967563c582204d6f7a535c91982019476e397a372ec4eab8e7b81ae21147153c77b41a36101bd33fba3b33bb189d124184953a548cf70c2a23e3986c4aec300540662534cc176e01a5802f4193b0680ab0085d7fb8593e583821c61b3ec8f08df9442b276269dfbadde2d2fa8a3c6f4c73263ac44d10c9840343ebe2f846685ed81b5ddf8b4bde61a2c7d32c8f8844d3fd775b30f40a2c96c55270895fa937c6c2ef968c6c6f82b036348873fa94ecee16673384a955aa0d0fb52819b0b670c15dc872753f1fbfd2a0ef7dbd9aa06e53cc42776dc0d54982b2aab81b9b9a8fc72935c2e644feb0de707de186c4010bbb51017559640d083cc181148f42a9a6cb6671e6e60f48257f4fbf93ad5bab95007e2a4c762f9cf6fe03e88217b01ab695916bce3d09804080910840a669f76d8272e15f9cdfe16e996e5bc618af225d1fef25c4e3737ab942a61cef08574e2488c773ccbe026489000f87d0a5e33544db847009740a4a98414bf5aa5321331756169a85ac66f604b0c621de48e296fbf0db441377af1401a1f3797e06550f180d09368b769c27c4b2ab287dec2aba5cce942b3d5448399903944db1bc34ad23aba570c2a2850351290ce8e6caa6dabf380dec59da12244e2f4b8da3c6785da877b8e86e4a5e630a661de67040088a58e1aadb2d9644ca3d59826f48117b39ac0f528a94dab06c786eb1202539379adc469ad6654cb385f2d7ec04d11fc57e4cabdd7619042dda31abf38917cad4f7d448349ffa9ec9112f12d39d73cdd1d00c5e6889337e057334701200437607e2ccdbf8a4b794afea80d5e3c8132e895de711963fc7972cd8ab79afb3108ef5eeeece52c49d4581e2d71ebd882748bd8c1b546b1560c9645a1c509761db733f32956bbbfe9c9689599a05b6f8fb453a3f5bbc7a02f8fae5571357d62d02a1d1db1a6c6564d36e8ab0122916606fbe511d5890700d53ba3d6df72778ec5d3d547e5a5fb1aae6ab778e8267d4475f6aa98b86c1f72a5161d0ab636bbd83210478915fd2fcfeea902f1b1af6d17ef35ce2c6aa547b02868d1aee5a860a13c254102a003009386f998ed1436d10347102885836117530739188979c81fd77ca3274201fb724e0cf7dee307a7a7295e251a507979336216c93a9a162f0c8e38a54b8dc0eb0b6fd6064581475a3bdff177b08e369368ab6364108466ee0780c44fa8d244857cf611af68482f0437f5b025e0104a9751ace979c653285039625f2f265d9f39fed662d92efc5361a87785b23f0b362744a8b6949eeeb8b495e197a5f95906fe57da6d7844de2e37328840271e9d4d10541227a36735e4bdfe42af6ad23b3c8143b82fb12f1208ec50ce2e30df351527d081fa47564fcd7e96db375f4af98cc6722556107d9832fe7495e1ec8294d611e51a740c302bac4cbc0ae80a0dc46315b6a0af42385b5dc2086825b06b4ef50bcbe9daadc12579282512dfd616f9f43f5f780256ae027ad7afebb6109139d287e10776d906fdf7bf3eafba204f89120c2943e9785b6b82f2c0c5082e58a9f12382840b9f652eedc8382c51942b1267d019b7245183b489214243e41ab5acf4e65b405393562cac63f265f615a9ef97158e0aff1bbb4722ee7b15345c669b3c6a7d60e5539bea0afea71ce20fcc2ecfd1128a33dbdda9b5a5ec866d22a0f64f35b94464088e0683bea137d5ec4442a1676638a3b4c7482d646b65db326a9024757504ac97a96e6c98d1fe54be95fb8cd422e8794ccad972ac61ae66e93a45e625af9466c6cb1132d91034ae00d24d5bd345d3e2cab6c75ab01c52085751bcb8117666a982ba0f2cf3f9867367d44357874988d4a72e7cc1252436167c3991f474b4547f42d8675659a21cc997afdb55032a683880c0408b8b66c5439c99f9d3d578a29b2b0659cc3e8bb90d5e8814984c60f180890cbd8bb6b0cc5d71281a87441c06705165dd6253160ba381044700f0a616cf311890941c9d082cd1c234f7c71a5f97362417e22a218803bddc51efd41091d6915387780a43d278dbb800bb724f1a6ae8882877c1e4ca11551e2bd687b937bd638e352a1e2074d27aa8b8669d66da9bc614481302e9b4e570684a680716cc288c2d7b9466e8a15109a1fe7b2b2e30b378b71a6544449c843e68a39a8955d7946e6424cbd413a7c44dae255cd57bc4dd26332dc791dc1642939ba4594464fd6272f86271873b78d52d6f9d6fd6338e6a2aa8e29ce7303dfdd3f1ebfaed3472e7569af82d552108d0660cc23cfa3857b7906f515a1769e2836c958b0a87e3ba6bf14d97dfc8491109bdcaf6ae775ac32ead0d0ecab1eb03161d33ce922ba70c3f6f65f33d5c8d373c1503f0c90bdb150104a424305da5ba55fccf256850871f64937f34a42cbd92a5924a7d1496d8c30967f1557e57f3c4219444c8e57dd17916a37144d7796619a0db7f51025366f8fd794e27ea36cb2eaca3fef607cf4466941f6b333f0a909277d28b60829ae8dd52b05fd77c06af38be8ee12b2cc5173e706275f2026f9828e2268585e21d538483918a645d817a0d77951e473f8cc4e8498e19412ac42ee4cb6b7fcc5c000482d2d512f60d58e7fd0de413400f195a0a6c5ca922d3a42b9cc290c8ccb0542c611d028bf477e0999264cfc25456ab4884592baeecc7fc9014c80a73ee630903d247bedf1ff77a39e0b042c9dab7dcaa4fde049dd6768d49e54b3c00d1ab8425090ee3e805c0e7fec6c173612f0ed3452ace1078f3bf89f817426c1a754ca14a17edc6e94643f3aac513e057e88b4082f164c43508ca04cd15996153f2a71bc9abf95f49e71342da557c7e4e8b85313a6c229ea874cf02c3947819813bb89321ae6330b141e8de3ff3062c9892a268a10c371cb2df13b4a0d88f043a0aa8145b87fdd64020f7af87005c043bf8c16c3694ab703b1789256ea1704dfffb432a940f52436bfafca62586975b23720c392d1d39a663f10e7880668a5767316d62717a58ee1817605ee5896dd61a24aede3e12bf6bc00c7ac4d7d5d9ea2c4d563ff0c10c12f2cd0936439f1f3440a05958e7f87cbfd28eef54b634c317a48221fa7ca51a529e95ff3e6403986097142fc6520f9e42ee29f1d41cca3ca171959afb87a8d32d72ed640fb1ed9e98eb34eacc6e2ac3fdc240d7245d4200e572a7317d5b356ef98fc95efc5a2dbe32c980d53430bc5f2181c63031e91897d44d77a8a791975b742ff0a77c204ce0b11912fe386c10fa0a02e91c14288718b552ea7586209c303d4b6be37650f7a2b0197ba66dcbdf20ff9e7170b1e647d39f765046f67aece8771789efaff1d87e5927fd98a6722573b2f69a28b6e1178d555c8e2691120ba976d0ee435799ab23d0e3227ca020132ccde585dc1dcad9b6f0442c8e29c4139278ed1b1ce22fc605848b7972f718b664e3902550e5cfef85d5c3669a26dc90b94e030cdce647f8db5b8db1b132bb5fb4cfd5f50279895e6de3f3b2a7bc3a7eb147885aa98c5e93b99c1d27cb0d4ff336bc6c48bb9b77d6d5c0f4b417fd1bdc88327d2010d76a9837ca3501e1627c46e307950aabf28053db1f4d685c5f23115c24248181d3f9d017895b1df8685d4b3879e992e10f34886a61819b7536bff3642e8178fca4d8e68130132c31151371c7f6e1a2e08deccf816130ee61a349bd5081b552ca26dcead9afa709425c9122e585ce1874c412a6ba9551fb2b7522d05a7dfa447a70d6be39cb485a26ecd61e0b38fe989580ab919fa3a7c5a05b54f12f1893cd09ec83ae9a25bdb1df0619ffe50f6249408d4a61cfcc498634fbc3a5333a054f4e37f4f1733d25c4da273963d604b79039c13a89393421295c6aa6777406c5e4cacab0421eca4902c69c4000e31387f918c2a65969325a01b052577e5546c01e915719615a62979751fd83bb6753765b22a9b8a69a562ff39b6dc77978214b14e07224454056cc4b4b5b10b71514b010c6b3b11e2a6a154cd471b3d6b8f1ae00009013edca416f8393d605e7dadb1c975fd5431783094861833cd35ede77b941272256e3338f8953a1a207ef085cf9754dd9b8978260dcab672949eea2468c5c711cf514d0858027fb012d0c25d75da6cb887f4d64cd6a4e3d90d8768aa2ef018cf12d312f878b02cd19e12499bd5c3f93072cb16f5bcf422918df17efac710e6e2fb6182227bd75502eaa81fbf17ecbbecade7192149a830213f6cd2b561f942f0f96347120716621d7a366ee1ed980c5a3503e0af5becf35b95048c4e08d4c3869971364672c50901c1b2d82172d2b61b6b1ba6412366bf44acec6c0721d94de55fe3a233166f746a8ef8680f76c46f76d202a5571fd9530b128cfd75027d99ac06d6bd2e58e428cd21b5f0e06561618c792af21ce8bc84d8a0c66662a9224e513b1e744fbeea11ca5fda748ae56b8952f2b14b8b971deb4ea510a50089b4e60dafc09118ccdf695888648d220e6b2bb5c3422e4465bcc57aa3839605eaf8cd054331e3261878ae160ae0efcd5f94b2e106dd2d2916531f4fdcfda9a9158536702e44b22f6d4715d37c4395ddc574a55d8be97c837c478eee04804a3a7fa67fb7fb4caa9747204dbb77f188203f9b6a6d4736a9a0e0ff69a4af7b599871122e567c83764e3f6cc46a4ae532a64487c5f0cb38bcb1886ec73f4fe4923bfddf56deed4e2acac59da657655d5a742b0688c6493ebdfabe7cb27f305649f39b9f1e41f9743741491f2ca4dd5f0425ea60f14d664b6b1e714df2a1e639567679a893767da8f31d4430391e142c6854b1a4c5e853510e15bacd1c4a0a000526cd56f67dda0e7922e9bcf1768d970e8c369e90ee957550e3b90a5f6c5fcb019958726bf16849b1aa41e4fc2b90995440b5d617526be5c24931ed9b281fab80e8ad1a23c57df4a9342755fbb14066349423d787e2ea51bb09797339bb2d4ee7bcc2dde06839aaaad1a9b6a79480a963503b019596645daefa74ab6b8f4f7934bb15c34ab3ac99ec05d0d8427d7cfd97c2aba8f80f39dd61c7dfae1a284dc261064c4001f0b33af4faa5fce083255b03ac34433e201493fb7769062aafea6b0c2615b658b8cf416ba1716198bdb79f0d7aa3bb0f5c064086cc07f8d6a0c83fdbef10d031fa8d5fba34f0b1a19878b3180d30a56fbd09ad36896cdb4bf9c5dd79ee16f80c030d091be0f30c26d825cbb2851f118231bcfd0a2037d8f540613214c8e4c0ff3124e12c487fa9741f7018f440ed477483dab9b00952dba10e8465000737c7a56115407c2588eaa47af8eb677a60c189f46fec511d39fc2091705ef8df0912ed1da1da038d73b239638bc4a78480a437d7ebbc15c5d1405fd7e0ef4b68cad09463463222ec901e8c9106bb5a0d1a8de073b558ec7fc2576bd22965d68828ff4aa33922e9150c085004271403c7e52fa81fc58bddf93380a758f1a65b4ae33781a610e928fe1698376835d1714ec401035d55bba86183cf0e4e2df2ce91ab68d90ef07fa21c05e782da074d1424f2fec1f0295e90f08985725a7569601700ee672f0d4ceca053184189881b0647f8fde7132057e09b4813ec9a8456131df7f242ccac8a1d1e142be1cd29ac03d16a4268881dac9cb01732fd4698e80000e1a170fac5ea23fa82ceac6029e4106b8be432c32ef377647719dea2d7dc7e717e10ae72ab8cee3a9540b98a932a0dc33f5808f1ccef5293d66dee7db2704884038ca4417b827b3d8e2d4a9c98373cbdc7324aa072c1a7c6c5e41cfc0513b55c52bdd027e3abb746e8398ecf8108df69b854dd012fa0b72c7a9ea312578abc15d0073e11e3608cb16011363920aa2a64b62d9da90ec5dc3b1939ce9d5964180dfbb8514e0c8b51e4e5a73e32588ca4b320f363763a60d36ffb392141394c728371c67be3036f40d07d159d5ce42466902c931d3170a29285517277483d10e1c178823630fbdaa445aadb569c283c532d06825e618e593ede3ec594350ddce4314382ed89448365a89329668a24b5792bff3795a8c4f2a6cb88f43ce6f13f803cecafc58e0788054d35d8dd365cbfc8755ff07724d0926d8666004737c6dc1182a2367413a395d6013f2ba5e5e4c96cb43815fa3f31041a5300ae6ad566a3574dd055e943d940130e1ba3e048850cc5517039acf31a5acfd43f232765ea7f4dedf26e196cf43bee984140f8308d2b1d33dbe551208cb2a96ddb85a0d61bf0d224c4ba0a48edf1e47ac05d2261e1336edac6455fa84dda915afb66d63b9b5dda0d001e2775478bd18cc61028a6f1c24637dc83565c65f4aa632d340ada64856f093a09ca3c60f9e3b4f25b71e0d43f40f00c61be945adb9a6b7ca86fca1764f8be843d74529b93290dbc6ee71ad4392a54f7be2649071f1da4ad0b439dad9ad52fdbbabfb5f67ded7bd0d0859f29a670749e97289f0cae51a3bd49bf1326679ad5a1a6742998e8e224243a7cb508c57f662bed3460bac7fb2a894fc814075e3a537f8ccf33acd84c018ea3c7f82b28bdd0508f7bc5ea48d301c20212d961c35602a6ef1438b2c3b22281014581aae3a7a98cc02b07849c0abcbb380ff67994b48ef3f4a16a080416204441469b7b21437aa0bdc08741b1d5bc01699968533d4c398bc5c9c0751e2db1c00c45f5a6694f8a187ffeece67791f60608518a09b0267386c8a3c2fd1e250e4fc2a174b1681d860dbcadbd1e3752401fdff1eb7802c79d3df0226657130aef6ac45de02252e41336f39adfde8687d010d3c334d8751e0e8d515bfb316cd8d59c3aeaf933d629fd41a472ccf81d0c4a36d2a5f78aab617008184933beadea99c7b7793950cd626c98815522caae3fb15b36006af3b7ee7378884349afcf944759d49e3f13219927849a4724eaee7c2390b93bd40912f1bbb98681e1c17af53e81a20e6455f5a90da6602e100ca86802beee0b736b70986802d8257773ae2350d54203fa1038c017e88f961f40af5f9d50b586a7f070afd5cc6cd22e43e0353851d7773d0d8546aca57d1936ee66a61cf5fe39eaf484d07c16d94e73844a5f0e582eee138c092fe33015b9d8096512c22a830864fd072ae7e78d04d810df5158d418b57d61b8e3718300ee4a2118a2ff070f72df7c88540f316ebd25feef94d6d02907fab3c5d3aef1f14309748aff49c2f718d51dc8c241f16addbd8781382d913fdd7ffe93681e11e166559b309f9585cec9fc79519932a6b9010df30e69bc8b74184a214bcdf130b09bf38f0fc16723410e2fe791641f4864a18d8f411a287c10c6eb348d4abbc413e30ce825bf3deba43ed1215ca3636acb196959efafc3005e6fc63e1b32a083edd87534534e49c3a8015132ea5ece8968e5403afdcc34af1a5e0ff8c5e188498369d79135db0ea96a8d312d7366cec0838182027a5068f86057219077946c35f7c3e10ecf3fbfa93ec101f23c999dd2f64e8e98d91fb60202ea1863bee271d80b7fc196485c0cc195c848000182b7ec3321386bc151b4779cc625fac0e30d04dc922ac79f775e930a0e080134d3c496ab8b8bb58c1b1a0b58278e765c10aa873c4ce32ea7a15098b7b46fc347ddcfa4cfde3f039d8607613dd0d8ae6d0892cf2e5817af3d7f13b22e6d773aa085af133004a85867867fcdd5be822251e2d5f789f18a440c08387792cfbe478e4d1204eb122bd070cdbdb5d867a76f0f316491a2f7b1fbee69b7c25331b19e876b8a3185afd225723bbf29f6ea5e54041d7f40a7816b9a20bb7d1dcbda12a11e96615630e895863b1a3508e4ae5cbff2fef957a9edd1df149a925a4c4ce9c7fcb466ca25fb17992b85f550c2c72f602065cc1b43a02254bd57904c14696f0d6226b713da394601304569e72dc9c3412e562f2c3a37a0e88e4ba23e95c66b30c14f83dfb7b8703044781d507c4d6fe63a1d6f57ef19dc81e806e07617393232930e399a0debd7a4670d88fe95745a24b627c847d2d4362ff5a606f357f2452514c1608304d60712ab9617ac2dd2d9af68e1840491480a81df64f25439e6dd2a32fd29688507143af75a45d371105ad8e8b515e78b5de7b22a7825eede83a7868b97bc2cbc294b606883e1a3170e549bc78a9b48703e0140100b250c6e7dea4aee917c7cd64af9b8a7710da522c2a190e483fcdd216695c778683508c18856a8f215f82062535fed0a2ad4eaed5769fbe50f05cda5320cdcf2032b3be3183c65074612c8c7c30cbe29ada03d3eb7a07b73d48df4f6be6fbec2f7d82c3438a678764eb209cca0a3108ea0602e8672e82c8cca1301775be296a757e8b217eebedbac44403e8d319767073df2a27543f56a49f62bc20ac4922fef850f3ef92f58a40ab5ba4532d57b0563b805c30281ed1ec241b950ce52372e675afe7eb3450795e00bffe901d84b7f14a3cd5309a985b98994441d481982746cf0c6cdb6d817aaab0c41bfff7262e71535d5f19aae32387212c67a90ab6b83f8b1dfa0dc0c86a5c425b59df1c5d9723e372b9dc7be9f6d0872bdbae24d51eef0dafffda71c0e9f52babaeb5882b895d2ddd38cfb033ebb3d248b94e6513caf915d200d3e657c476a3e21e9efeb6984d68ba7ae59ccea4656d6455ea2912903b0cf126a6535108d5a616b189a35d7b0bf474ae910a4f31a907bb3b66af22aec037610077aef6de38f51d85e55585cafd5e0d5c430d3b8941f0b07fe4bac02bfca1f44eefd42bbac60997d257fbb377b119d0ab617361a4ec2073e62ad8840f61e942452d0d31d44fba5b41fe509e7406febffdc760bd26f341ed63706038917e5ea1ccc0eff802cdea838f4921bc3b9701f4cf1c57cc72ff135cc96c729f81030483c4a6c41fdc9798697db41c176a17e22414c4ef6d1616e906868cdfb700038342c883c9c19279efedf764d9cf19d0def1474475b363596d02e3830ffd105e2dbba68d22b8b8a866acdc89c43d395f4206a1ce0e7b7bafab4d5419c5359dc491d621927a15a87a6ca826a8e9dbf07d6e7ccc3cc27cc90ef1143999c272c55b3761e9c8fddcdf3e8b46bcdb1b8335aeeef5ce1d78d64828039a2f0b3ab16864821d8dae73009630f901992034766b62dd15aca856c4e8c16de796c28fe0d0cab82c923effc8df439c492c7b33bf33b150683bf80bcc994465ae9d24b23fd34694e93e6b4693f2d35cee77f1d79ba74a5492b9db4d34b3f7d9a69583aebf403e9bc4a50501d6f412960d03da481caa38cb5044d81ed8461734ee9f741e5082d1cd8aa39661aff463ca9d193362999a49777309895efe6ba8c933c78001c7bee52aa57a4c9bd6227730463a93f245e39140d2a61417304fbaf993e34060d97f0f97cf40a5705e478a9d31e15e712def92221fd77946d1c2db7445de1e161a07f188da563d158e9e2fecde6353aa59031fbcf6235e0a5aec69be5e29afd5834353af6fcab94eaacda8ff19f51fd85e673ff383946cb52067c5f4fb6e71476c36093172dc6d54c0760ce45e0347a7083666c1f0dd5b50daa51b0f2d866f553e4dfcccb4fbe860496ad3c2a188679f1b6127ed92595f8ea3f68b624b32f052a9b8a306a139cd609b0bbb11e995a89f2e40a5620e915111a896c5f20957858b9943b430c4194b10292db3beca2c6c883a950b75442463b2c5545155bfdf594fbe5f9fdb1db2b16d5ecff53c26e45755ffe25c0fe49254cfcd9dbb645751fff4174701fee660567d57e02888beafed6d79793975c517d9cffd5266c51adc36f60d6ff37cbff0fdaaca8eede875a4fad1d601515a7f9d32ef797e8fbc7b4472c2aadffa30bb715d5bcffc24e296a02fd9f31fadfec5577c2c0ba68517141e39042a4b4e86c7318ff2a8800b0d3a0a862f06fff5c5554f3f03f8dd5ffcdf2ff433758547719a1628fa3c2b07f51c534bf3ce5bf38bf7e74fbdaa29afd7f82dd6151ddd7bf06587f043cff9c2fa845b524b3021f207a28bcc7b1a27290f60b09c8835cb213647dfb1b32fca97476c1788a38e2c5dd98ee8c986c507d4428872b2a8edeb2bed0f6c880ad4d45f5994abef42a40d92e2a5ea7de68ab6ce30751bf2d3c4535bf75c1d8ed960586fda009afa8568ddaa4a2a2f9c99bfffe8dbfc5d8fa6d64b5e36c9f5b992aaea5a82e35b32308a7d453b3e5363f384dd26e57057a50cd4320ac562aeab92653c987b43dba9b8cd9c24ff10d98848739aaf23d43c70df3f8df75f794bd0e626aa4e0ef35f137fddedbd4eb3d901803078247b9bba47723235627cd0ee19d97783d6989a272244bc4491afbb8a8dfa602d0936e10c73751539e31758858d0a859b027ca38a4fe7b83ffaa3d30cc8fa3fbe5edeff4614ff3aa85176dec8a19c217143aaae1eb6575c5e7ca9cacd7c3a3d04689d98d4fed36714b20996d67331f49110234ff75831a0fd3629b8c0869a223b1a0e187a45e110228ffba641a37217d6021bd69ac80aa8426f4da8e8d183d644a8912d66fa86e4dc4c8b54420c2433e172c81b08fa4224442260715956d294c537a67592a0351545d3458251eb1cb1ec440a1b0f075d54fd57a49672783721e055bdb543a24ac2768447f8d24cc7b7cee55eb42b1149af1ab9a4cc0ab422f9bd49367d0f1972999e785ac1557d1cce87c5bfd802f81e470ac61614b6b129d2f6166ff89e60e3934eebd554a70016f431153226c916805407d91f9662247071c219aa770a72462f9bfbd00a5a80e83fc2b04e47408cc8cf264053901607bb20c084058d229e472246c81529f8c90c4c2f844b1cf121b4184672168485403dc3729d5543fea32a8a91b43919ed35632f0cad877ce36dae87f7b1442e1276ab076ce8d0b21ad0bf0d551d702b0c0f018ab7d2458700d3add9015d0599250fee2cfbb225d51abbf5fc8c32b63c7edc5681d401985cc78f2b3f2839ef4a7889d075f9fd48c53895947d640f62a8c3e862d5527f05291a3e59046ae4112463394a8de6bf192c80b84d7708ecd5a9c844e05ba581a8993fbe018e4c8bd710ab6da81b84fec4b2ea97542c0b6713c0d0da9344c8996137434ea9d9af14b87655a9c3777c3a574348809b38d58ad1c303f0d73081817f9e9bba40548bab8d25bcd4436d6d6dc9a046a2bf631fa2a2a25e3297b3928bbcb555d01c60bfc332b621562e254961d93b72434f6471b1942722f0e2796375bf699f0239cb3675a52c66fef6caf6f844901ec491ab4dde11a988e24457c88335a3f110958c6f9f7cdb2e135a7eec44ef2b3c0985909b29d88e21efc9d690c84bf951c264b7ef7d6dc4ce75562506c683ff82c3b9806799a7336a9e1e354e24ef0c8f35bf57dd815a2454f8868d55e90b09fd5a2cc8011f3a0b92ce274207e1e936082f233961332eeecb64a6a6a19aab53d826466d4e873a0174ad6aa266d013fb32171e29bb8b26e54be894d15148b897e80ce744df7a0cd4a62ff75290d49c969b9a6215b8a1d1f1377b062fbfbd7bd0681227a0a291c404bbf246a921d6176fe29549434fc3a1f46e5e1f1132a231a902b3fb51de61213a88ec876e935703b2025297db184ed2b49bd2e075f3fb4b4315e13fed5a9121ed18fe84d9d4802f17d17e0098274295091ce52cb6d5d4f5f2787b2790b7680e40a22494bafcfe40ccacfccf595735d1f977112d70de8327e6de1eca5c2eddace3d15ce83706c04c834b6d9172c32964f8247d3052e38de2ab59f90143ac46620bd53e8098fc5d8724526c26d18eb75c4d0c82cd5f74d93d41f81de3a20d580310ef8c18b1ee1e84b0e427951a4dc01364bdc46d68dc701820e965899f3e7c029922b763e40f29d09a16fb028210393555dbdd5e5f7ed7b3d5f26946e37964c631a0c6a54fde72bb2e1c7f8da2a42a5b50e54df6e0b559dd3bddeb39105ce2783c127f3cc3542078e4367694aa7221ec703998870260d4788ca671aea15cc26f8983fa44dd5433beb9df66c29ae083b6834487e32cf1b3fc7b4ecc2540906419397e8c069f51842cc5111a6fcd7cebe65ea800b000a094b2522eee1fe50318d0fa760cdee11640e99b8c8e9875382d71f2803333ea50d48e848b3d16077ac5d04222f989bfa7f47ea54714bfc85ced1b5056cf3f62571e63e8774c0bdabb499ecfef652be7b8185f88dd0e66643149b002955c137bb0f22679e88206faf369e7fc546dfa34063e6b957c888b16e3d761d794c79c46441627c8c1d831f43dfc17fc337ce242294e7af979be42a89a2c184bd741125163f8300f95a5904453d82ff2c8cff32580f7db840a02b148d01f2d3a6ae525a34392d444fcf2b14f765f630af9767d393d8219784ad9700f2d481a991d5dfa893bb0b8483d91cedfb1f3697ef21dfd87cb758b7e9f0b910f210819043a6df48ead066510205312cf5a95664b4a08e163c4d705cca994b9abd86172093fa400aa217a1fa73fb5e34f9336cf6cfa09f224421f41949d5756815831f44d3ab1f09dfc36c3af2b6aa459b0790117cbbe6b8be147c546791b25ac2609a4726300472eb6901f8e0a98c60049cc8e64f3273c68c075a69d33f45e2cfe2574d477cd369e81bcaeefedb034421e71c702ff97825cca817a239ee6545360dfaee78f257a8ff460f3f4c953bc2848c4277ba99fb12079c1a5da4349934c784a2ccffa6a8656b8e76cb9d5422525df4857fa32b073209ac26d443fb5b351c9b84c0b44b9fdb53231f0abbddc0034d18edcde2cf513d0ff1df51c03212feebea1560f388db90f544c11eb4f071184146b5e59880598ab35dcb976dcb87b6fda2131befa0556efd886d05d2b105d47b45b128a5bb826169879f2f9bb01e7676c7984a0eab686e509c57e4cf99c785e18f3bbaf30f2e6741e831a84e1ab17664056b025e6a83de3c374cc51686efe8938b582b1be175281b3d8414e4698459209a2379cd3508c8ce2efe48442b2012a38328363a57be62bfde636d7b321c7036ea5554df60df8d677dedac2c3509041bc46b5ce7b41395eb4a71a946c9d5a4483abee42e8855cdef50229fe11219806f48a027f57b476ac533bc516cb054b812e795127f71a32923f916d483628e867308e5842257a998379d86e49bb8ef2cb49c851f678e91b0c4cf422edf616e539bb4c6e2262059a554b0756fe17a82148784e256091093d590cbce88fc80087a2339657cf768c215d448c785c866f682291215f9c1df95872b780ff4570f9f4be8dc32c2e4e744373b7907048fd7c627b37a6bd4c30e8727a3f0181695628a4fecafed3fe6e5409122be10dd8ef9e6136bec14afd60838e1ce0b07fffb7ee83d8af4e29553321d0127220fd91a14335f19fd4cd81b9f78e1ebb1a37a1a80b38abdae97d1cfcacae820675a3567bd3973352485787bee03f6707e5c40f14713785d83731097493576b1e7c3be8f362f71584a0503f4e6b1ce6b3d3f41acc29843ae10c297ace0fd6531ea573aedaf9feae83a9eb101b64ba2d7fa0ce7ff528b2d17211902dc215e5e4e5175d57d1423a38adc494508581389d4a46f2bf297279913b633f272d0c3eccd460f3171985ebad1ccddef8f69cd41e81b75383e58aa39834a4a29c5d45863cf8f454c430e20c3bd8846054f0f471a66e37cee1d402f8f0cbc6d1c421bc37b13e6a4b01eaffd4912b27e02afaf2b489ad3d1f79672a50b62b404ac3d6673495785e9407f04f46cca600f457de60cd0ebed5d12851b7f238ce9176154fc83b941ab19f60f913823fbdf2e72728af98a50da58e5e09fc911765bd8395221e56e6e78103a950372812242cb545e1c69b6c110f4c17598bc1984ea80a8f76ed63e580dcdd1027984b38602b8e00f9ab481e4bb60f1645f8c8c253ba2205b64001c034a0841a24e1900973da2817947c52a9320282283904e084d946f7f3e433e08c8b3c70be42de3ea148415039017cdcf15bc897ae08da84c1e999058ccc685ea80399ecb1b972c90822f20dd7704f7d5ddc90676607e37ab1fb8651c06e1691ab00b493593fb1535ab65e4cd514b03c0bd5195326abfab7a7fd706f49dd98672cac4edd7604a9f0e661d7b5879cde1a08780c3f9d3f2b44bf31b4ee2535be8822627f540dfd8f4cfe0f12d083fdf091a235a8cdce1cd591d83c660f7a8f2ecaa73e723947f734b3b305e8b28636cc7b234d51086b14626af4ba1a2798b73d147c6b1b9c00634e011055841ceb586542adb300732fd528073831c53a6e74cf4e0d472d2bbf60f3593f5f50860f994ce7a0e457c85127ec9344afeb0c29cbf9a981db518f11dcc547803ef7c11ab9c4de5dc6581abd565f56fb463dc845ee6df3f01e248e50c8df0fe7c7db5020e3ded1ca8a94fab77ad2f684ca382e01c42f3e10cc3f56d238c78774cecc2ddb5ff2299c5a227d9a1ab251f091c1c73db6e952405fea0a92770dfabb07109e5938bd6a6c17318a2b078e6dbab8dab137df88267f8d527d5cdf992ab3664ea538ac1d8b09f983f708cc6299c8cadf602b5fdcd5006255ad6e2178ef1e494020c37e87d28ac208c52050d71a868ae31f7a144936fb1b330b617a93f21463dd4d907e1a8ddeecf2266e6bc187e0b8ffa009fcbf76ce3f5f6329682d7a5f6390bac3a32f43f7cace8e0089f3442dca0880f02cfc61f82a957bd1fa4e4c8725f8d1ae1497d06150d02786a56942a3e7f3b7c55f14b1a068ec75fb8d5c4937805b201b57ac2d03911f76dd4c10c5951f6eb7117f5fe10132d9a5e2673af7e564861726f468ec93dea1e7f0b755e166653f9e0dc24a9ec3516a57eb58f6017bf3005f5c6930cd2c2181777b001b7e726176f8d01bb821dbbb91dc3242b3721904ea591003b95119463ba9200179f877801982e9ac1eb5b4ba9730d90f579402503ad36f9bf0d90818645c999eed0eb43e9efb4ef4800623a5fcc0e189f0f6f0dcfb700c300a85de99f9e270f8b8999c544017a0be97bf7ee12452e3c9b298a19e107d6e143bf90f94a3ee460042dc8a83ee9112905f0962a967bbab4dcb73a0e30e2627377dc1191c1085b2c748e91d0d57fdad9ba1d4b8bd0d6b66fa49257b61ddfdc7bfc0277658d19c801821cc89e6fb50d997639e38b4525406a23a468c4b6f8b10520196c2d5a3021223c3a1db9d3bfbf5e6d2baa861b763372da666c9c9a480da4b25cc26269452c51a8f4ef3b31633d8ba495f7b62e98a9cbef94d4e3cc903a14534ef147a0ec0a89641bb68ff94b8e22f3e201d14b6219c38b1b9b8aebfc0bf3938f2e8c20042cdbab89e0e939ad5cc45021976c18447e19f067b4312c55722338403756f8eb99da62e91988961d722f7400a5d1977329ebce1599427e6fd0c42fe028f8c111507ed746ba2176bcb05410ba8084fa2c39780e1fb6a2a984bef187d6e6e7f5ff05b1f9221556cbe87cdbb5423910e53a386129e566f9f9cb5677831905395f39881a0299551896f7066ef6f93fd27335d7eb46634edd9b003734b307047d36b205edfabd1408c10d546d30f994325f20083cfe6225225ef500ee2401306d6f127a0c4561d4babed6c8d921b8f15cb29b1d602d8971b6e20a7d35dea18c000d3229c3ecc09a90d54a41f25e9e42570438e02b06dbc5c9fda16e45a000b852df830e3f6288bb3f629f9657ed539f453988bc42a9eba316128521f7cd5053513117b09c23848e381a304b3a56d229ef24108ce714cef21662078d27fb305159d698d804d0a0c8745588f8620c5de5ac369aae5936d301d21c36a4c01fc494035dc1f9a72fe518eb17b78ce3f330ca66216c98dcceedb30fa35e5c9958e3f02309be5804c6fe6a958282c88345a83977aea0f2ce8d12504016001305c7a51e5178c0195b86dfc22d5bab7ac4f3739be1dc41a98ba1df9853eb7b3ef61498aff704dea7bd3d1018583c21b25171af7630b6a2eec061e63b7cde3f90f6995afbde0fdfc2a3ab517638162cd01a7433b8419b9ec2ee510404016b3010907c2f71a2521aa97f696898994d52b5c1cdae5f80f6b4e8a75b47afefffe8cfdc509fd8da7ad1095fdaedfbfaf0feddedacb7394a019d85a153e40ff275b6d557b0dc0a008cf5e992df994d0ff59b4576bdaeac0d4dbb88cf5fc21db8a2f7dca84caa9db32efaf306840c03da2396725820ce61950e03c936e082c9d319154add78949efa902ef5ac0d26c2a152d00c2d693aaab055a12ef9ef297a551270400170c2e5ed33b4a43f39426adcbab7f85d242f968a4e8bf7f2b343b463edc8277bef2db7943225290301070007f8065745b64b8ae3385a68224f1b103bc023b63b424cdd01424955679aa44dee89bdb0ccf0cd99c4e18b4bbf3dd2304fccdddb13868c1c5487ed0aa55bf661cfb87723fcfdff3fe9d7c07c25a5948274b657eb4dedb43fd3adbdf3f35a7dacb4c37c9f099fb37fce39c1e66827f17d2ba884dddddddddd2114812baedcb5532ae6ae9d32658a67cd1ac775d16cc139917db0652958b070e1a208734438034a3455f47bbd4444454444444444444445ee6ebb6ce55aa713dd9294f198967c7129e3d5c39e316148d105337bee0c39f3e308b0cc960172599482858becd30c9b387a321d93018de267dcb3c6d0ef4e76aa273bedc3d44fd9bf9b44c9de560e7ad696b7992058fc8092fdf66388ecff3964ffd9ad894ff61b1033d9ff0595ec1f4386d310d97f04128864ff11bcc9fe356e040182f64443530f2821b7282a215e55574290c06aa4791bb2430d8a169134940bca4af6a75d5e5058d99f7e3dd18a60354141193113758215aaa5aa233c5c6129d18292fd86844ff6a75b196626a45ab914cab495be732146ebda769c2646d48c10e112220d591e4d6fe94b1cec011d8acc20049c292643c8b6d6da2a61f94217647f1c4ec9ee032abb2ce9477be971e5c9a4b165ff0fcf169efd8a921ecae60068952771eff7718dfa4fa7579441696ee4ffa245ae16f6fd33a0019a44eb5023ff356bdebca953a7a45126501368b64ee998a051611d12822a5c5f2d3459e35485479563849590163cc1bad26788ae3b733bcc790f61acb0f818d1c5cc991b2c99ebedb8e0ca8e2a314a5c38c1696a015101c321a2797ce10174cc71b4db438960c00d54766a6576e0c164da9034e56125f7ad070b3ab0903192258922649ef28079de7aa0c0f57595e763e15aa895e79c73cef9e5512e95321d6bd6dabf9cbd77ec19753f829bd028f489c260d8598f7255bab396ef02f28cfb9c576e9cf356579a46f3ada51867d05f621d3607edaec8373d89a45a8ee0266194925ab919cc269c7164f34ed7d127ac84396badb5d6da5ba9157895c396c3cc776dbddf5d5bedb5d5cef21d2157cb5d5beb48491d5d8eeebb8452ab3c6914dae408d592a74de9e98832cdfabc1ed99d382fdc2849464515b692d751d78cebb764b6508bd6bc7240512b2a7a95c31652ab1c663eaa4e49e9e86836fbbfb3469f720e3d628c39b394cafb65a1d7b464bf4f6bb556c3ca7eed08a04831e5029ba6cf29ba4fa59ecd9761afd5aa8c754e0ace1d83e603b9f65c30ed49a153c1931f27543dfd1082862346744b1a641e59446ec883c4e685c7173d03c157e8642f23e2b82038cc711c47454b4a0e51aa04914201c9958a24cb098f538127599878c9c982e3c475574ae3ba9e1d4d1b539bef5fcc1b539ddc8f652f8be57963b23169795440bc90cb236f89294a5518e679633a7263f2c1b4630ed72414caf3b624c4750a83f2bc2d717530843f79de96ca74566e4b60bc2ce197e76d098b024b57b8ce89c3a520b9941d11eabc6e8824402cbcf1c95662cb4928042ac8d6140b5203bac1c74f90ae70cb1098d355ac4a117008d7cf13211f102d188f22423c2790478fa7cec9f60d01859f9abc3943b8aa6e8bc805d28575b05c5c96531552bb3238b9902fdb42530781aa549a280843a2fcb623ca8893577f8b3ba06cd79017feaaf7948e9ca61f0c82e152cd3d5ae8b101137a65b8841c0e9d5308e372b3844056ac11a107c50e8124c34067c88d13fee469a343c359215010476d44bfcbcd9023a523164a20f8c03184384f5b979a5ce2a510ccd3d69543eee684b03c6d5db59c44011ad7871b87b63b6872cdf3468b21979fbdc62c7c5fe732ac945dc6f5b5b862f76bafadb5d6d1dd6977777777779b50f2d1f66a3b7bb1ad7e394e4986655e937d7dd1fe1cbe2f6a234ddcd9ceabb6daea9e87ef46e28b9ed5cf03dbdd56b715f460f4939294c27fef9779cdd660de0b37d2b2793f2f6ab3c5e44261c2d6a6f6daa697bb4db9210a50b23ecd40798e4a1cb8373491c54c53f4acda6967e759adc74a1a332bec2613998e26d44c636a65a63c47b10c634dc39f9fa6f3c7da8a6df866df7bff855f117f27362991f73d91c90726ef372991dddbcb35ee3e10f613fb3ef004b556a8ba588e16c37e622150d0cfec6e53a0b28dced94729a55869ae4a1c98330c4d008941f54bafb596a042a289143f41611038832efe3bc71976e4b80f276c68e2f738ec46553c016ef1af3bc269b39a9e98334fc82e00cbfad32706e0d098bd8e77ac6d82cb0fac1bc47347d8ff79dc58ebf469c7fb2b94bac9260963cce18fe3525c0daeaac36aa1065342fa0d82ded8ddc1155442faeeee1ec3efe592662978c491829f28d88a75dc72393a6b955dae5e1077de27644be06b391d2cbe1c9e02c2f0b59c0e165f0e4f81fdf4e9c44b7e3f60275af28b819d5849273f20b0139bfc82c04ea4a4277e6258a3042c84300b95c03fc9fa3f30187872778aeda66261d7513c15024df1a680826047b3d90c840ee57d3c0a85f74bf07ae2cdfe60235d136ab62eb21d6dd89a0afd1e9eddff79ed469d57bb11acd53d094aebfb083ca96dd1083c09bce149dfd3266dcaf3cba9359647992b8afd50980660ff8d39863040c9ef29f98d4d2009fba64a327def03937472d03d2852b26122541236fa588a91df37792f0c19df382707ddf592bbffc2257321cc2fb46328291d1dcd669fbba9a5e525e32e0e0b617af1c65b54d435e7c3fcb66e7f9c515b880f37d205321427aa09a112414061ec07067e5e87b96bab379dd6dd73501ddeb0a8157942fa2bf2b8d79dae38f56778fdc0bf295d617f9445f9eb59cdfd29c016fc79f19296ace4e720283af955f0c244ef67fe623796e97ba1e5862050e754811fec465cff8e10a8993e5773d3aff7bd762ad55e7bcbfb9dfd7bbbb1fe8745ce8ea58e6c692e812641455badb57786b59eaf5880b9c1d460cefab127ad9844f4839f2bfa33e8cf449d2bf7994e3804810cb8ff0f0fe0a7049a44b67fdf67d08a6967e7c38d8401f8617041966514d7829c9d463013e416ca257250febcef0341d0c558fe7e404e9ce44f0c7411cc5f08baf8e50f0874d1cb5f0990c399cbd7e60f05e862cd9f0c74d1f377014874c110063f3ed615581cb99bba0f76c20bf2bc2da1bd094fe47993aa4325c5c3124e2a4cc52188e37449613902474a2aea872b292722706c51449c10070b873894618fca4b851be469b35324974742c20bf2b4d5f9ca13a2c8d356c74d0c7576002289064342fa5ae1556a1aca1ebbbb4b1912a1c9fe267bc4c1829b83f66d431b83fc433882cb6c8e7118ca64ffb31b1a080947e8ffba31bfe697adb6ba25c939e7cf4979a843505c6badf55ace626bf1c85d5937f50ee8021ce4b34e21fd2a809f1dc9e605303c4db7d79f2abcda5ecc610e0871ceaa00ca0db4b5d676e5c07a31b6360e14e85fb5757c3b39fbaf74c5d0621c42adb5d6103e37a6b5a12e0e358e28bb09354fdbd1d16cf62f9351a8282577a740931e5a17422b7129a46fa285b0eca626da04e47b269125e4fc79e29b9e444e4fc2f48d98196a705ec8c2a25ce0a0e1a25fb07ae8f2b696ce6a2de922bdbac18a560ea794c8e7e549145513440c5c648b96c812f26b1e526ade022e5768d5ab8621660bb4a8f075a1587969a251a2ee50611bc45461755a32b840f36e73a03ed0c5c9a7ce538d6c91003f901fd8e48b0d06ba38f140e1c345b6d8386c21bf9ff0e795b8a240912d8e58c81c4d9b4badb55619f5b798c31d5aef1e2f05bd56af3de38854191e649b8764bc70a3696d19c6c1508fb72b7b4332fc39c834d31101998e3af2d7f64e9ea58e6cb9a68af99ee9cfa942c98deacf3ae2c841fab8c3f7615bbd3d3033adad3586e24748e75b9bfd5a8d982ae6cb8860e5f91eac09e97c0fccf8f7d7318cddab3c9fe338cedadb94adadae076371da313d9bd8da115bad9ecd178784f403fbb8d6faf78baea8b556112cd93f7bc491c3e6e8fef800d1f66ab93157cffda21ff8cfbf46cc16e0cfbf5557b55c86e18cebb7f2f4b2796979d5b03c29f4e2f2eaeaa4900be33094c9aeae6454611414549e2194ec32e59897ed63f5e4190b87c0c9f3ad9bf941f864e3d8ae46e29a2deccff9d6d69c67edf53ed07e1fe87520ac7aded761d88f779d87b99fd88dc733e895a6965fdf7aae175b42fa1e972785e0582919240e601ad92f21da0f4ba407841916823855d87fd13018f5ed0a2e47cefa6198035a0b280e358066f03bd35c024d6262aff0cbcbcbcb2bb74c089727795c6dd4fd1e8009af3cc9eb72a3fe10cbc297795cff41e5feab12a3075b82c4a269f43d508afc7d08e5495e184285503fff4520e3307bef71810fc6c632fc193f0e64fc74f6793f6bfe7064fcf43b11ac73084a9717c7cb93bc3b1d3ed10f3c371f53531f5af6f4c0099f72bfc73555f48b4e1e577b5c1e579e2b2e47f53d5088014918255d6d6a681206890db344f47ca040d3a6c70a4880a1c10a12fc2507219562c9b261c458e7a1490ac90f4e8ae4599226eb059cc525005e8f16b2c028a1e1d683114976e63cd9d266abc987100c110dfc4b094ec00245c6cc52162d1ac832670839533600e921ab2b28cb8821365490609119e2081582e0833b1c6686eeead04d6982e1cd983234a8253534753514116aa3c30a0c6762c05244eb871f6cb09beb04c651c9a262b3b2ae781e0fcb5e1635c744ad69a18725b0bd93ab439c8724bc79dea2dcd85113e41665fb7e68dd89aae11a6e513184b869308e4aaeb99c3ee4727e51261a1585875c522c4a63b9a475f264c1092ff050e4842a208a90e5cc023cf0f045c64627cb0eb29c5dd38b36e5a65026b8ea618d5498206c90204b5aa504e64c0e61725092041eb2a457d496fb27f58a2ae279a2a2fa298d4a85dc4f6b5154723fa685b2306a299733a6a32f970bc25928b9e0fa0431e951075eb5d73fb8a08c8ee7254f892c2f71ec4f9e37255a50b24230cf9b922db93ce2b4429ce74d89162b205558f3bc290977aa68343286704e0afe583b5e114f15d45a285805461c39c81eaf4c24f6107943bac8020c4997947025a921ca0f1b7850dda082b8044b114aac1047182173c12a7a5043d611f2e3c02a22255952457e18aca2076dae92905fc7e60a49140face2c423c6c9870face2afa121c1058255c4f16ec28c4142e6e8400a9337647d1771d02a7bc81c424ec83a5671b699227303e9e23cc387ecbaca6d93c366863fd6d24f1ac20409d245a12f9ec2378b4c784ae9acc0378b64619d481356717c30bcf2bbbbbb5bfc7ec2400b83d5469af393cdffa2598afe81894f2416cd8e58b878e11ffb212c414aa97ff8b63db7d7b8bbf79af995958291dc5f6b38ccf596bb7b9f71776793e97b7f95c9929bcd9adb3668a6b59baa7177ef347da6c8ddddf61aec3277f7dabde6a8d5f4dd127277afb567b3aa2685bb7bafa9ad66acdd5beeeeed6a6ceee349aa6d50dc902631406d8d1d420088fcc81ad1502eca55c51851e14cd75592a5aead7b77d8b103891db433b5d1d446238a192c7975d9410f97958fd6e3577d45cb097ee52fc69b5a872b4bde44bd6a0a4580502fad9f2658424c2d281be630416d592e5eba64fa5bf78a52d9a6dc2b17404d595068b840536bb5f6d6f432bdc4b8baf038d328501a8d8287c9327306fb96579b6ec0fcf06a3ad8afd486f0341a6d87d90563e468997179936b0b3b1afb362bf603cc9bac2da2223146542012ea406acc1ca1da686ca09c2c4b6c5b4e69341acd86a7e1308d46b3c1a1a1c1a2c4960e7068b000cf503953054c4323058a14f45ca191024d37ad891ac9469db59a66ebbbc91095e9d6168d231354ce54399211e00c15343464366e48a3d16c5c4ae909b037c579c2e8f3f89ec26034edea3e615dc708e8f0517105c19e799d6e6bad3ee703aab3c59216518f6d8f326ec6e4fedaa006627996716366aa825f6e51b6b2509e37286432fe69e48da58d9b61de738f394f9c81df01337f9c58ce5aee0adf13cb17991361ef8936ee0cf0699e30ef71f93d6c2c218324924822cfe0667ca38d9bb9b77173799ffb8f76e20cefbd710637ce180ac1bee7bde84e4747fd062cd8a7a4678c952d3b33b0214296321ac2dc1a68c0e184d7e4c1973759a4aa7e90f3dda0e050b10679dea054650cf2bc411942e79b4562e8d8c8696248481f02971e2141043d739cab38690dd55126a842da54473412ed17ed6e6d7094f67ddc57d6b35a024a581211856135f2cdf76c82294c1aead74176ef7fc393867a94c134dda8c674a34efcbeeb1e7c11ba07471c56baeb5184eebfce1bbbef460ae74a9d15962cd5a0d1c82337f2cf72482b27f072fd164f40738b267684e5f4aae149267a84e5e4ba4302976588d033b7d1a61a3720c03d09e687341a2dcf11808e08cb39a96c9587e18863322824c9fdb5311e5a14801629b207b964f1c48c93dc2ff392fb7faa4dee9f49e5a672a3f446f6848a05c6a187f99c429ce7ed49d47d2ae2f9f0c449c8931e2c58272d38d2446a4eb6a46040d59c80410283c8573f97430d6278844130805161900a638240f243103f383378be38356198a70d4f1700f2b47989f1e19b5e6b7577ff5ac770d6fecf80ced93da1edf572cee1ee67f9fb92b6e28e72453ebbeacd5a6bbd6dedd0eeee76f739e79cb367cf9e4a476ee4ee2ef3ae2fab5cc7e3526b6badb55a7707359812d23ae79c47d36badb5d65a5ff86691f7bc6f6bad0e54e79cd77ed9596be79c9f091026fb89812168ad6da439a7b576cee9c5c039a7d2911bcd5a6badb53ad80e8bddaf97bb2f7323ea312853535353754a9d9a9a9a9a9aaab656d529766aca4ad514a787b6577bb98e3b0205d5242aaaf32e65a2d126cf121a43642605dd42b7d4f0c2f1f58155045c344680a90c1b074680f9d14cd6b0f901c502c16f059a456b3d6bad36c2a21f501a6855ee2e14cb663bd22d20a01081185a93fecca25bda684603dd92fbc10f73b07fee5aaeeb2e08ecd5824ca9158a38746add9c38652b00aa2b5aa10db72654f01686024391a64eb6b53c867e56b6d9bec5d6d645c98e14a6ccf0d5c47e8c8aedbffd91ad182bd9da0f2d8c277ddef440a74a9e1a485b6bad56c2f2bf47f6be3199cafdd5719422fb73866cbf36528a6c97ccc9f61b06c3566b2d9633d93e088d1466fb31ec47a0adb5d6eb2f6102666886b0735965a0a010fd233ace82ae42daa7962d6dcab628dba750f4482345090d11d6a71f444358e2fc9884b75fd6ce223bce0899640ad6304e58d64a4713b08d9bc12c3a9618a45f3872fdd4d7d41372c212cc7e1fcf24fc378c6151e456d442a4b296762884fbdc73633914255f2a3b9a209465cea6bb6bb750cf6c9d301b9ddba5b27e16cd2ea83c8dfcfdd53cecc554e1bf35a7077481205d3d9b59b93efdc4dc61176bd4652dd47d7d163349368b665299028b9c78c531ec9950cfe6d79a95ddfbe2a918dbda359deedeffa2a56cb5f6defbd55a6bab15e2388ebb1978f7782679df8d0151fe58ca7361c5097bd2e8c06449149b0e26a0101182c4ca8e94145b0bd9412273ea64b913654c9025283e6dea7c41e1863e3308b1e4137380012039c245cfad6a4a4b14e72427851a6a538228f3e4858f06515ae043844a1d3c559e95075c886386ce55d50d743c480a676a75c0703112857be8e084291f6c52b093664d5d81050b34ac19b21404535554b74586ec8fa36e0f069059a2a40f973c607c4c07040a47682c5c69e2c5737292fdb1147744922f4b8931437a2011030f5a5ab54d100e44a4e0d4c34a1e1fa2ee157f2c051a996988c07326c994141a6e3d47470a39ac81a384cf0a397459d259b5487244952c5053a2a4c123812878d23819b161091fcb030664981421a4c89a1b93630972d282aa3e5235da90280f4f0786d0010995a53a5288a8674020074d9f21564a86ac89fa9efcb1d40e2b40e552d641a63bac402bcab4d68e5c64eec7dc0d8a08713bb076e4f18fc84e0365b29e296d60242c65d45fc78f3368b0981903430d07b20c61e893fd6b11d98508224b192f6a92c8f142c240000ee1cd7b32b54bc6b80993a429062c575e90220fe8700493275b98a2be6aa62f3c79f8a55f50f3421f186cc8fdfe5a987e423138c50b564e1cf182927ca2ca903f2b33ecd39f2dacb8c154d14f6bb93f057d23b21630c3df4729f7ed58933ab0c51081c651b7db0d94584071df60b6987c961290fb8e4398e4e0a92663841731aae83246e87fd11f03ca53c1feec5a7bce19a5b368b6a0426349c3bebbbbfb84bd7d9818f6accabe3a5bfcdc0a838d313e3055d4e8a40350dd606a526bad55b9c2bacd084450b970fe2e581bc9849fb78133a780264c96f8125f9a28815d94b8458d04f4f36729c21731f61a35054c04df45a06f31f60e830164c3661028a6e40d83011b87204033ec1d4033ec03313c29451bf96c5609cbe955a391beeebb231573c5cfbd3f2309464e3868975cbdffbe7e0d124cb051c2fe036163d919f63fdf6219863d74902a087a16ac20e87d5072ba51f7f7b01272340056020cc6e77d6fc373f7d89358b08212ef839225be8e4332823e68c441c9a0d09330598f54cc15f5b915b9a6982a6af5b21df154b115de68a412d7b0d1c2cb3548bce146f54b982a6a4c151c2009c5a15a6b2dbcc1a757afee7d7d6aea3ebffa9c1f9b3972ec69ec5ff48c7d1563e3f7ac9d163ac9f3668455aeb99b684de91214d9cb542c813a80cb3317738e1ed5b3d88cc56ccc4e2b2d51575697524a4570f7d8777737adbbbb5b29971db394d6148bc5622258b5c9ad94e7f42a9ee44dfe3455b437b1109694468b817798d026f48ea9a28946699dd4016d6aaba658564d5aa2aeac6a6d6a8f6a03e47eaf3255b45b59b7eaa4035069a935d21cb3d51e95bb3d2bb79bc9ed6c727b9cdc6f27f5a8462abd4aee261dc57995d9a2c9932853f7534a9b2ada00533628222ca7191270f7fd1c10818dc671efd723a5167a6a1f167b2a964d9b352a558402d991296d168a33ec3215124b8873ceefa9088b4d3cc5ef673a97dd7249e010038ddf3f2367ee0913c1ca101e75e4f0276c041c3ac8f03978eafea6f57bfd340679402ab45a664a57a6fff589329e956999295a7289cb4c999281f22c33252a9799e264ffc6ed7b2b6dbab5bf40cfa9bbfb8650834b328a4ed11d5ad1b3dfe1dafda52f44d065738f24f4cc5170fbde7a6bdf68a4721291b9707840720966a763f7df776729eafcdbddb34512a60affd93467520736282d55d995b6341413c85544562e295511585758f42a7195f30beb81a2542b09371a69de5cc092fd715014cc16d666123c49461bf98b305578368095d0bfa44a409a660b8a8b50a94c5100913e5064c10abef741c9efbffb13fbb1ecbc5abbbb69d75a6b77d7172e76b436fe86756ddbf6da0db6c2068952d0282a896acfe8ec19ee1242542b0e4d30516fb0c849a6880c55e4478fc0d1a257ba507991a16c4f5886637c18a165412b521580b8b922eb05adb8002275b8c82a8e58c88f03ed0a22c2be61d1ca65512e5ddcaa3ce5fed95137bd9fa110702e73647f4a99e4c1d244d617947648f122ef5cc143233b8d9a2d4d138c0b2dd4f6f488dad3a60e549257932bf2e3c0168980902ee2a05d6a4f4ec81c778bbb787674d808b9e2f261e07befbfd1fbf9569aac44755df73a32c61dc6dc1791589ce4bdd77a4d04b54adba12b9720cccb4c532ea76d87ad30a9c96babdbc95e279760ad2416e73492c2f50c8f325a9c6f080338689d2bd2fbee6dd0ac8121fa28b0481c28b0c8efbba71c3c859f272067cb072f5547ba4ce9980109255439c06083172672b6c04e9218fa3051f2c38b040188ed0ad51e3a3fd821fbadb6f22ccf9b150bf966f595e7cdaaeb66b52387b305ed6cb952c77f781c8a31337e1d798a252ec16ccb1bb92bbc5529225ff7e7441d998a363cdf91bea12ba657b1ba93cfabedee765c9c22b06b43d51a273007219f97270f0b486725087602c555399d70ab684e564fed7a721bd410e9baaeeba4d832c7711c77b99b94accc3d911cc771dcb5a15e2b1e55d18b62c87e86803c6f527ae49be74d0a975c1ed9c21df3840f5896e8d01a0a539e1b6a53cb4d13216c34d2b1dcaef4089cb5a864edf814b564664c0400000208f3150000200c0a8703e2803016a792aaba0714800d6e883a72543298c8825190e2208a611808629021c000409031c010e5d4dc0d706260ff91457b5f01353193402f72bab5eb96235f5f397a294e112b7ac4b82985396245f84e0c24aa7e109a93b73c63d6b489fee112f7931643ce8c30a31377d1d37cda4748603df18c3b6247653ab958487a9173ecffd586fea4b6d412a877390f3a96d40f7a847cede324f69557ece127f5f96d9301d9fba4a83a3749dc7338c596ee5f2ce49e5262a6c093b33f7dc3ce808f4efb0889bda4c53f792f12f83143de4b10a4ed154e9c88fdd7cad33f4e42f691a3ffb800206326a15f9500b2080274cfeec312a9f7ebcca958bd8fe7695eddc725f44667b1f4e882207f3f707622f65f234ffb3889dcbf9c3811eb4f945fffa084fd49c7058dd28c276ed6025530e5e9fbf4a7f2096798ba161bddf88fc4557ab614f294b2f2b2f3ef4c3c15eaefde607116ea5501357f2f7aae2846669c96c345c185a29a5e3961214689ff5c35e13631ff2fbb8514458965bdf1d8b2f3b8308c1f38276f0d7b8b05cf9722c0ea8b5102681fe1169a704abb0104bbad057c6483658634b5b6be47b280f48685a2282ca179c57d2cb6d215b34469264b6ba87b0de6a7ba830e33ee2ca624ce9886e5d27d553df0c415eb92304d9b5bc62f4c444f07967e752a884ff55013221a454f4eff02da4aa9ba7c84370a4f996112868f3eda4671c004dd4fb7f69f272d25c214ce3d254001611877a5d9eeea29191994f42805f3c1eaca5d991e392105d2555e6479ecafaf302535071130ce619a59049bf1b6e4f6aef6f6da6a5942468acfa81e30b37052d5955966906e46504ecc2de1a0b1a0749db70ee129a645203ede094b64ef7e5a2321b3496cddc657b5221ebb1e49808a15c43c0312bc214799920bbb27aa906231adae80686041dd6e85cb8c0a04d39872fa33e6f8e8e9a691acfca024e1f9a78223e0eb119870a8ac3b5d7566e6e42bf7286871696b852a230b4d3b7b5663951de0a2283f79bb875139b8fce6919189c02523a9de9235dd02a39269afc5a58a1eb46f02dbd55ae30ebb3367bf5c78dcc217c98b8781963a8b59895fa556b79bbb8e734f48ab2bf5e12fb6ebf34d3de28d3fa53ffbd4089253ad94eb20d25e350887cb8e08c2c7785cd5fada830a2de069bdfde38cffbc7fff618bff057ffc5fa46afa3a9ae6bf198295d5cb0c63683410168ac14287d894195e8106468ddf256181092206e6e017e951178ff5691e9191633e31e6a18161172af2423cdf315a1d4b7c6922d331332079c2c04cd0154cb14943f3ee18988b50604869a570f987d469e92e8be3afe175d6f241df5d37694037a8b9ca764199567cf50ba0fcb55c2f7041586c1b41c0c218a4c2f0a7d276111a6070358eded57b22f90a43c5749ae44d6c508b9be3b011262207a5b039e1de50ada9b8ed35d8c39790db7206e4920fe9214c2c04603efe1ec5c6ba3b54dc7c3ca4d5d87d4d7053515a701d16dec6abcef9556812bc73de79c349883b04e66b01e7a16080b41ae985598c39e3f053fbd6dd30283d77f54ccf9910246215a3631ccb97199a1bdab1be00e8493ff491f6711c633acb037693873ab10708911fe71135dd1472542d2590eb7257bc5dfaa1d4c9a1cc447d525912a06c08f24392d90d648ea8b0dea3f424303278d842d5a8d2e734e1a4610f2c267a0a4257080d297db59f4b9f2cfc44f70138df5f3dd57010498f86cfc9b92eb4f398cb161e11333047502329648e4441636173301411498c9d31633d87fbef2e095e0ccde53b9b91641861502688d790116f5e656c483a859bbb6e8fe5c0099540b4bdd1499b71cc5a82147be4c9379300aec1b336a988800cae1da75f411c226cebebd9b5ab400be7ec19112e22fd4b47c7e91e4f932bacfedb775eeeaea29cf80a8e74a7567adfb2ee552165eda125b5058cb59091ad4c324f6035c6de88c1de8fa94d72080a41629319739e334e6d2d6ebbcfa8d5058bf727c0b9da34c24c30f0230c0b0d8eaab344b3892606778977e5feb0ffd6b4c500e8578c3ddbbb0eb1b6d3c881087b93103a7bf6772a5a66e96a920deddb0ab5a5fa0aa15184dd09dc4b7632ea029cb928a765c737a9b9d3d4b22151a896bd15283c28161039e3720aaeec1308f06b602b390f98532a3c18675545e0f7a95681bd0d2eb3088f8f4420730e5dbc0534b14dce2db93bc648f4dae2cc34ded484d230ab1666ec813264995cb802c24ddda0e081651158e5c6ee28492286508dbddc4e7dd9b7b0f5efefcf113995be1ab8ed9f89ed3ca03775a2211190624b15e3b08ec7d407a54b8695d6c9f8940d4a1c90c0aee91248aa53b14d6c8fe5d7bfc781ab359b29693a40689c71f565f602d5cc60a472bf099559c458bdfe4f7d223641471aae71030e2047c3d00ecb139162fdb66c9abfe19d4bf28f974e6cb841908abdfb42d4a1fd110ad2364411ce60529037fc6c18771b25d4d2e6e354b8561a25f55450964b587232b2128db198119ea2095159ac520a4c14f7212b14b3a1ea1c2262fb18fa5880d9f9971585543d9e1688dac03e4eb66687ce09f397a049426c7f844682ebb3096021e6542541a47ec3999473c463c5a6f2d92cdfcd04c87171e692808504fa1a5fc1b0f89674318a97e13e5bddae94ba3ed93db1651da17b7cecc691c6c1a9128411533e6aaecf69f93a820fa29a7270103145c128a114809765c8ffeda6b2bef4c1453d0ef9b7c0d4548a621013a87797c5676426a0f71c9f0db3e2540d4c730b8abbd97dc03dd2a02930a52a199f40ffff8470048013b64245e629b50b80018878e6effe673ea745c68336e7e202157c7826075b0080bd3874a24c61bec107f218e630743960773a53d676cae0dd7aad12d574c592883492618466180a6afd54045759f386bfb1ac802f8fd2d943478bee47568cace032656645299227b482d91561364095a124f4a83cc0bfae7bd60655768ca744d09fe8f9a9604ea9aa266757e3351c5ba1a1b655559ce7e34aadec251a4ba2c0476b9d4410c986a0f44100ff5b5eca05245afcc5f74cc3dfb28309fe2f164ce35c8d2fea4b497f098f83cc6bec0b459581be27db0a297516a498f6a82d3ce52633beaf8a5f739c9489fc8f733529a8c90fcc042d7bb2c38aff73f6439b5299d4ddc07d9ff94dea18b100095447ce46cbc1ba6c0084f2f09eac00fc807a81bc133ebbb96ab42f79107cc77fbff474d0104fd8564704cc6deb311d351bfb6232564028b8737997fbfb56449d0e984207b58d9347fab5daeac6dd87dc0e9d00057afe654990dcd482646609139915492230dbe39d6de53096f2a6118347633d85d3f26d79daeaa5d78ad596997d5bde0a25e918316799914ff6796ce01fe6f0a275f004edd4a241dc80af1ed34fc4c2561bfe867ac8ab8f4654ca9171c97dbaad7ea293ac4be6893b3ff25eab397956ec1627c07f0266051c74402b6b8e77977d680db5c6d371c9414d3ca47692f002ab27533fd4f210f601a38c0334bd1a803b615767bda68b36523fb2dd1d322285202360b99f6ffc2ffbaa79f403cc06c34a8a8fe22b5b03dbaaef150237026a1abb2aa6e12fd619d76636d5a5c982a5b0d4846059b8bde93c63a399d91eaac6b142f7de254233a886d46d1942bec80142b3a74851a366e40d0afd1524e4f8d2e8d8b7f247f22bb34d771b1f548be48e5baf06d8233895cd3757298a8c19d15fcdfe11cf996fd31eaf325c4c0c539c0a4a595bc941439923fca75991ccd77c0f7f012247d36962c55f5145a53fac608dd88397d51176d92ad534467f4285a13c0ef927eca7b2cd63c1e1f83eb2dabb2308efbd6fc2021b2c2a326fa4bbf7b5ec6d7f1db6430162d7cff8d5d076572f9d5a5d19e53e257540657d985efab09535477f9979870d2a4c6a3b2238c5fc3975bea87ec92808f02eac3e97f3965b3675c50a80684e9421377b1f13d20e555c403900d9d23fa9f980b4e80a96386d21b3c4c8761eaac75913acce754afa2036e9b309a1822ba3e814219229fada09d2cddca00f8116c2abd3a4eb71d60df75c16bbe2ad0ce24ca1420da8020b3ef8bdaca7a6208944c0de4878fea36375cc8b3b091775778f6bb9552e41bc2d33950059bf7a6d3955684262af3b59ca482bcc373718c0b0ff4794b67071f58ea784c207749aa69969a92dfe67061ae67e70d41f193ea35036c6a819e7bac0b1d497de70ec538b4b951be18c2097b169a46e2f68646bd55af3020445321ad404297115bdc8687cd1b48312234ddee4644c43ceb5676cf218b7775ce820042eb6738cdc0092127c9573d12021c5b239ebf423a4369cb37ce538a26d8e97e30a1d57136736a2c1ca3c988e90305ab881baa9fa11e1a3310e1ef92b8a81f3fb3b9023ceafdeefcaa6076e9b0019589638d0c00b9ccb6c6f037f0788ff3808044f1aedea9dd52e5d36c1eed304e59308d2c92c9c0e71a24081f23fd49e452afd583f73f4252447f4cd4d270b5983f5347c4113a35e8e25935031780a05a04bbc897b92f99047aeaf846565bce821137e6e9843267f189704a2ebe197f74c13856b80babe4456e3eb0bfe32b789b80eec042ee18ad9af05c10d0a51d31dc5a825812465952c457bf4bfea39972162261836c588f773815d8649cd1a4ee61b8011c5f2a258a997a1637b1ffff10c8f97c15645b8fa3383999edd4aaa9762afeac55e2d7aca666794deb1f96da447089d3a22069dcc4d286239755b2e97773400934f887c04c54e062c2c030b21045ddab9ab5a836272a6d032952865eeb07a4e61e84ad88f93213d7aacd2841e2c548aa0bd453b5018bff23caf2ba531505dbc4389de4dacc0a2a503fc152d4c5decf8d4a3294fb6b074dce12585d14b4b4474646c48db0e571e5600a233e7977765aa7adc8c9380d3b491911f1cd4d2b88c417d34f02cdb9d7c2b7eb1068f91c56dc51e56e0bf13c5dda27db00207903f9f4a7035bf6c08eb150a9c9c7e419678efbdeef0c79e1e0c536600c5f7fc1c426cd011cff77d199ce9aee8e0fcb8cf22393c98811ff284e07ddf41c36ef582f8303314bdd3c1ea91f08e06a005b48a23498a68b01537cf2771d51459d15cfccc7edf08e3a4e6a31957ea51ba1717fd3dffe3a28b2c11529cf386bed7aa2581ccbb5f155900f9fab6dd4c83b3bca7d0c30e21ed08c2ecd1309194ff02a0afb872371aed068a9178ac0633fa0bcb82145677b56203d425208f250776404a6f1c7b0b13ebd14c3ea5427e8bd3d9a6138c7152a19bc21fe6deee087c84fb7b9d962d8d3e467cd8897aeaa85380f4dc9a274efb50ef97f2f6a6be56a25b4cbfb17e61bcd7bd5ed8bce7e172b0cd0373881041a1119d5c9774ec4ceb4f2783e494ee2b4c89ee29794bca175dd603a1410d811b0cdad5715264c87a0fab6fd15c5a404deb9560c5bfc16eaf7ff0aedc2489d4ce11da09bd98e080517ed66b5d61a741b621e539a0977706fae7ca22f46b5782043eec65b9e05f01114111721865721e817e303a4bc7ac942ac72f2e384e582f8dfb3100630b35f435b06fdb1f4ae58491a4c8f99c58f3f4d5f6b93ee538d93671b3c1befc824f943fbdf056b13465b39fc1f032d2880bd6bc9dc708018b181211732a9a5c62ad70fe1628db9c9d7a7a3962f831605d2311ef627a801ba54cbfb288a396f30abd0dcdbe313ae52d45d8db49450c0cc0b9081ae93a11608962dfb1a0f05525e9628937eb0ddee1701cbb49ea6d0b1ab08fd9a9822e835b34af400d504c9e928c86c68da3066fe5584732f0412055d1cf256a63f14ea3be339c621876f7c80b10be7a078d26b2d6ebe5ad83f7df04e1cc8e2921b02583c2767620b55c66c98b7163ecde3a8adb874f77ef101341b4e3117623de8166aeeb81a2551988b5a20e206bbbb253dc55ddaa1349aeafe38e4e12d305d7aa8ac8102e621b2acf717669091d04d95e2e2687b6f1f6724b4b175460bf608bc5e18efe5e2c1a46eabaa5993d69444b42f11ae0702b0d0cfb5cca75cff19feafa13d426bcc614392d2244691af5526a0572b173911403937a15adc1e802aff7231859947050e48ab06bcd8dc88370ce78f43bb1cc1a8bfd8d66ac220544ea29f50d935d2e2f13a523130ddbdd20c9cd2457d50411a594333ab6f171b6fd1a6da081b1a1022ca590107d3d71f02ec1f128f7c6c80e6e4219363ef0340c1c01f1781138eb342899aa4a0a7bc79bc4179524a19b5910fdfc0e97f82340fc1449290664247a63a41b877fe80b9ad748d15d2ee0d68969b69ee7cbcfb989e96473c2b38f33b92a497f8373600c729e0f46f00945fd2e89ba1531a4963db7f3629dcc7f3abfef5fd9a6d4ffd4fd4e06f8ab1740f8cfab4d390e3fccf8efea50fea9d5095e8097e2f066beebf13d6fe2eef3521ead22fc487605109ad594956e4f7ac29ea8649644a24b56a4594bb8958bd68b19e320fc1f2332f33c544bf30b791998b412547500c1322062a5b7b85755ee23996b9d1cb64df4690e2b27fcda198758d64a9691c3d41d88f4c4b29412a2c79eaaea92dbbb19b88aab40670e176cf15f4430611286f66e00a399f694a965d43297121009e46b951bbb8884334af756771b775702b1c6db2567eaba538ad84decac4e4021953b3771eb32ed6e1fb888b492af55b8aac480f44f94028aa2246d09b77af9dd25f7ba58722d8b71a02c92b9fcc845d1fb5096edc932416d4ce73fe38e5ab1df440bd291e7f8e1da84b627922eabe4b4aa8540a995bf202fd2577764338abc89f522548b0b3d55b0bd8675c62194896225e3160504f1def1701954f67fb2dcd7ba4defb9b1e706b7cfa5267f546a62cb3c3c02216d1d3e7a63b4c604bd85c8b285698f643eb3f312f7f7796e6d0b185bc336ddae5aa45d0a3d66b2b9840b7d34c840f0a32c7bf733da87f0304adbf78445c61d476699f4a8f4165a0b0acbb6ad3970622528de8364a4e5b7c48c41eb452470bf72aba91de15ffad2b8e444dcc07594e94d90d55ba4c95a8c9db74eef8e8a5b764da7ebb8a910cc5955b93bb29a7ec2c7b34bc0d951b8f2475f8a33c6406b54db773e32774c1d2b7d2d4271c1581ba57c74f6d9f4d14899f1267884ada9c662e5195b8051c9a3a00505d09981d4947b892da7d42a9351c3de283e64d455956ed8fec1de09737293e790f9ac85846fd674be46d0560bf069359336283ab31494b922e58030ad4d2aa0fc751fde9c1c5afb91c2477145d44595ae1503cec8f6e2130816ee626ca53a5827fe4c49ce089e07f2c2d26d7d254ed6748c377c70169506ae8e6030bd04060f6201f307619290c67c71056d84a4ab7b07f80942c5ca85c43beb94a7f51dd03a4d13d24772c00868bb3b90c75b4daa61112a182aecc2ae4f63150074baf3b7c9aafc3507d845111644f0700718220a1028f708d4d947ffda1af51dc12a0b497359d95fc33d5b213fd623049973be0d3dc02256eaefe5fc4c946597417a0403e5017e54294342f0ad210c711c4097e15aa59a99fd56e26dd130945340ebe397ade5f1171ed7f10e86e734f2c60cf9fdf7de45e1b0c65b3ea8bb2e53ce8508546f2c32538065a7dc68851ae68e2ea2b20668adc945a403ad1d6a21ab2aba6ba8980e76a86b250b531db5fb4b6a40dae8cd585ca9eb2b88fdd95296e2b02ff5fee79f1f558aee4050aed2830e5588a1628e5fabae61e9097e6de01a211e911ee6f01d8acb6e1f9d30c65bab127cc3609c25d7ca25474de1ce796d9734e221a564aed581a8c5f37e9b3608afc6608053a077ba96a59bb5d0021aabac3a2457a741a88d9e0e5ac9af6b297c3e90ad1031a17bf44eea3a040548359b230c4067364969e88e0c284d60d470810a422290279008237a2d337ef74d99d098fce2ef3c04c2e589c056fd6abfbeda44242399835e8cbf0b4d3742337c542f289ddf4f93c4e6b6b9f25fc59be0f9d2c5ac6ed7aca79ffa9f5f04d3fde83b72807b63700fa7f17bbe835078edec3724df0e3c2998b6a3ee8c9323529b4cfabc9b47c288bc1585933152cc3196273747b9ce6408f43ce3e91352ddaca186e7955d2ed0afa0c443e15a888c3d032d627e6b8078aac7777ccdd55248452b9c9f50b5b03bbccc3cd02cc2deacf20929b235e11c27e6562d4b9d8393140e837a06916caf876c464a44c6a43f3a506b5e18ded26995dc95483d60ac94a87c1196d7644e3079dfe3d927a3512ca3fd70a5b2d292a70552cc5b8d4111dada812e9c205fc13467e0e9961084e10bbb16328113e2e519c68dc9c3e5bd3c45d5e4da7a80f6ad432be1486ca5af16522e4876483b65b57a4cf7a93320d9fcc512015c5d89386018a4f66e6328c17918e09ade1be41b10b47eef91759db823609a6e92ccd37e7b95591215b4b17101546cb63cabdbff11e7786d3210c763c2efb80145b0765463f46807bc1612d8a99a8755b4fb54b26bc70323631570cbfc9aec314fed8f55f4263db481f3ab79349b8a94068b821e27f2c762a8e61a693f1b101b417726ea68bb218e66eb6c2c106d382e58c7e78f30167e96e5029388232956815d44797f19eb690bb07932721751a41ea49e9391561f0f2368742964d7d0d5144b3a6b3a7efce558865ec294b57e1f8009c4b0c9e27847bce12f763300c60ee428ebba9727921edd9958c2edfcb54c51d010b25779ec3226c352267ea47c460636ffcd8d62ce21b08eca8603d179b50c6d3b021f28934497f8cdc6a9734a2bb1771e99ffa270d499738f4794ae1b301d680f669eb4dc7b18d9db3987e7b6b2d24838aa84db0b0ef488f724249d2c1f2087c1eff68b69349909ae74a1f0ac010b0b002dfcd5550dca050873620c2faf1193a8983d4151bc08886a1b776f3bd4d18e451b6e8863847d95cce003f3ba8df3d933fb9743fe02a69e0f2d171d530c0404f0c9ca3d44646708255cbcad64e0ab3c1d822121735b2a87ffa17e29d18b37cc3a226af691bee67235d981802e536077c87c573f1db45e0509140e9d1f62dcadc364f01798e484f555f86abda2c29704072943e9c212f7d0929462bacfbd4b2cc2ee37508c0615490ef8f408e705f3de00d2206c7b7aa815edd6c58ac5c2e564063049c1f82fd9a9553a07c7be444c030f445fc701c33e5b594408b7ee3e7da152baac3d04f13a3f9026dbaac3576541b185998c67a7e8f594f7d39ae0e94bcd6dd0f7721f88f3ccfc5083409b058c92c3984b5be2b408801574322666288a34ae269e104e3f8cf555ec08271b1bd5b35a27019b13c4cf8d4f361063989c1934c07e5d9a70de5393c6e408e62a9e0b0225eaba0ad49149b3e9a50668b408f7156568e4446994cac90fbdcce2b0c35759709644eec8526f3f0d2d0351f4c640b319379a5dbca78ecda153ad7f2047b3ac1f3cd9d8985fc71e1433839462fd7fee7ba34f04c3ad35ef71207bfe3396c67bf28a876980441a7b906980a6cf29eb0ed23041d0b64ff51dc543d44ba34c5ee73c858792315c542d8a55887234c594d35e341a21c85e5d34d6a629528852c3e2e50b05008dbb44f39394898b161dc72a6dd5ef020817cb9ecf811d1e743ee521f9cdaab7c8ac822eeff5df003e19e302744b79fa3d7d4f3fc11a186c2b6e79c1de282249b28f259e0f8481457836d8e3770625fabfcf4cf394f8ead59d01d7042faf3c80a840fbabf65f7dc81350fa9d8fa3ddc517c5bf8c0caf98054a54a7b85da70a14e4c10f7103834a19d9694963d9427e004788b1b344f0142dc54b95ffc7dbd304eb0184386cd80d5a468bf27c8b2d846ae5e7eb2d9f5802b94586cba55dc40e00266430c55435b8ad150464c5e0878f059b5d7919bc1e94354b62d4c5acaf5f36cbbf49064846e19c1161aa2b88d10374db65dde4e0da061f14eac6b1a6eeb3aaee45cdf9404f21e29d84bc55ac26d86115f37173ecf0534a26bcd89b5862f4882fcd12b4e2f21b0cf59b1b8237645ef8684163f8971d5ea20402629cd2b55488619ae541b9ef401106e1146d9076af9e00baf4a503319acb542d660a49d99631e58486845bc66fa585e5046f81713130b63dfadce514364abc255a71e9362402ff8b73ea0af2b2af4591d245fd77583939b2e094957108108a1e47c8d314043c0d68aff62e50d578141942d6a089b566eccbff7a36083651c792bf7d6b5e10533b9dd64e83aef1e24021944c2a4b4379dd6960695c3f2fa0025564d243b611024615a2069d68d3bb6239650debe0773597002508f7e3e69358832f4784b99ad67699768ff32c3461216f8c11e13c28104903791967cd7c41cd36a4b307388135f19ecd68630b7ae5fc1a048897ca885fe1a313fe4de14f3a67050a6b6c213dd236ea401b67279f2edbb509a106a018e4d610a0015c8ce9b10a7b87bd88fb0fd8c9ce1b6edcdbf7779c13b6332758f7f62c0e8b8645ca84e0e4779cf11b47c90ecd8c1864669e35a8b4951a87f8088c14d46d0a8a61a736b676bc7d9ff39691168fbf4bdb2d76ed900d66b6f085583a9db7eaffa83f9a858ef53897a4e98fb0274f107a1151a3c36910002dd324ea9c0929542f1af2f16779c9d1b957164db25e0163ce2c6008cd3ffb1c39a60eac7812caeccd2262f5a01af84674eb246c05356587c51e0c917b6fda2378c57a38366fe34acd711c9484090e3dc4e62b02d2c506a86c1ea371e5059431684849245a634663d7e2916d5cb18c196be35620fef886ce8aba3a8c5bc68136668216d4236dbe9d357cea77e1412e1bdc069003127afb2436908c96cad7f64b02a435b32b9c5092666942b3a190f4ed623599f30646169d219b1c0ba3303311a99ec740991b359c29bbe4ced9da9611d6be4335418e597c0f460d0ebb688e1cd80eaca8e19d05485308b5403b4f01f2e14894e444c558842deac5d014cf2f363e28d18fd82b1fd19081634e7ce84332ad0a650624812ab776968d3f2eafa0c74ba55a82cdc105294b994ea55029604f9d6a7c7835ecb650a9be61cd184bf97a051c894295aaf0e96074f7733ae4bca8b11b6a3f95beb5f8f4aa40a3c356acb66fe38f8f9d50e411656f1dd8ca9bd8da8043ef486cf31ae1ce6fd092c0f5161e2446b84773a07902575ddcb2f58b0e509cc7ecdef102d52bc3d1cf0035c626ec9bfd17d50f653e22509aa0edb5ccb7382140f624d0b98481cffab2bdd8b38c37bed88c54d91fcd63607944d9762e303df4ccab23765ade1930abbbdb911c1c6fed43cb22a541a7e2bb7c27f30ef96e440548880278398c9fd11c2a24ad2301309fc4d15e2c699b67f9ae75a834c371ad7abc88adea2e2b3929326e34f46a72f8bc34a72eef20db44b0a37ad8f4f41396be483cfb7c91d80a1feec66d11b878a4e98b90e8846f30d43f384e1df10190947d3cf57c66c00f613cd303f20c6968a4ce8ad4edd159b8b544477da2117ccc0ce2c16686404491a1c0ce065e00e27c0e43424b32c3e4c2e128ab69b53b309f4e72cc4616bbd76da5a3f78ea99e0811e0447b4b228dcbfbea3ee47e78be9fa2d958845c8da27cba46b67f94d6e2763759ea54ac94c06ba27b16ab6d24be11583a8a9efa0a4a0343a2d3b307a713460791db00827c6874107153271c42f5e05938636b4814ac0c7d008a304ca11a7fb6108f4f37e3863fb69b3a90e39e2679fe6f009a49ffe19617ec4457675b27d11a2f1bc071cb0ad81a14bd3130164a89a063fb818f2fcf218a66f2a1390f7ed5d05f275391213076274bf8b273ae67f1b47f72675c4fc57ac04cde9b6fe06ba5b5651bbccd5628eda10f262dac0e8345fe143bbfc557c4c7c03e6ef4a8875481bc6587b4d99943b0b65ebc9c222a18e809e17ca7785f67122b2b28774057308bd1eaa8052acdc4a954a9d389d488dec20f29fc5459901087a507b015d3ba534aa69551a96ca675cccfe2f95e4714dcdd864b676e41939beaa826c5338dfd55999184a52f7b3d48d10f546b72657c61b660448077e92ee72646f3a33f2aaa5b07ca8b80ef8ee6f6cc630f58cd420c38aa721f83973d19130a01d65d796ea8844ee45482e96804b10ab2049a83065fa8545e9f60a0a120c494b6d0d9829595ab1b0d5b6355c8a534f3333a40a81c48c33439279a830684dc2ccebdd80381d57d35907515735d13c4025b9d807eadec8170fdcc12407a58a14102e6babb6da44527a4f83d6a2c60a2dcced62254b80fec2200b48e6b136edca9102bcb75782b72f750d100179facc9d89ff869bb4616876c5d02c12c0798050c821665d23a6094266a1310411072b90ed2a9c74dfad18d44db88fec2b81859c2698e6d8ff0ae08a2703494ee48a672862b2af3e2c5a9a478c8978aa5746fffee24e3b495edd86f49fbee27e190e32eaebfb9fa20914d5963448dfb0ed75e5954039cdfda3f2d777f14face466d2a52bda32238862e830e00726e46f9a922e09c47ed18c9ac42db1ac1917ce5000886f53da6a09b9b49212f050543da6e73814213f53207b3a8a2f827e67b9d841fe64f7878f2d4b84cde1a7aae5b83612605bec0b9a9a5d7b4c258a560337a7e2b6c66b64b3b8fd997cff4b7c585c3dd114554479171838b87ebc2a0c2abdb5fb99b9ec720c27198a26dffcee1cb3212b63763332ed2d8bd485333138a36e27570c7a0d4c247e1ea8ffe0f72696d00bff2c9a9799781a9c060c22753c1ced45d4970bbfcf93fb36771e5f67bc557c0aed8151015b64904189d5132361f84387f6b9fe3f11b8758e4cd19f0182fe352f91af0ecc955cf727d71e3d5c1233c742634be5a756b0db1361735a815640a8d34872914511d28660514a19fd226aa727dae1ccc36a4e632a337d123455d2242ddfc9ce4272c9aa5b26a16652558855558685665912c8445586b5665d5ac9555b0082b3959c9f6f3a0e891083a50fd3058cf94756b55b9eeb858fd8088183b337a020620fc80b6bf1617fb59462bfcae40120d66f48ffbd7db68794af9005306874c041f556de61a31133cd4a2edd5a2e369f2e70b283d92780b523cbb476ac28301c31e4dad30a706452369c70ca455bcee55cd9cab3709d969c8e4d4947f2a7d4f9c37918afdd3ee9abd2dea98268ba0a2d164a1432a4f1b1523937cedbd6ca26a414db7e857da819cab69d849d349676227350b9730d51bce649e55add7e7531e08f087fa59a39415f32e8a680e7e05aea4bbf55969d0dfb5d80e2b95e28668cc84a522c523ac81632a1680898168ef664a1150c7e3e3a07a5d1310e0a62a4d95fd781a67079794facc8377fab8a1057f5ed7e8033255388a595df426ac8f0d20ec1960340b98a3fe1fe4e9bdf999194f0320d82a82f93e52a689cddceac55b0464526d47c199cf1290802177eeff399323fe3660a507b8c110818635a162115bdb3f0750f1b04b74877b57b25b76902f05d6b44b13790b718b3e02839709b014341b14faa859c1e2a68073cf0ac772e24c385c2c846468d4e9ea9e78cd74028f3c5470c415004d1614069d8546eb56db0c549b5d1b8cd8e2bd3137c6cc82c8e748b4e942aa5f4dea2d93fb507601e7ba29605040da08a4bd2c7f7f1d99d230d18e375aa1bc8dc014029826f01f11f8971eab3d2b43c468ee67fbbe4634601348e813357ded76c3edd87da05c8400d63424d83dd190e0a085300cb9fd856812d2e33ba30f9d8ad06c40115ab22259cec048904f0b125988e574a0c6de06cda6595e864bc0c68836a3aa81bc4220c3c9d2bce17b64a9da6e3231b81e9e069f4741e304e6010bf4210c00d74adffdb43dc0c5e3a04d49a8000dc8a1d12db83a25423c27cdaf74920b57946ba092e931d1683ca2609fb5ffb641ec17a6a353dceaa78549a1d8058fc56e383b8068dddd4d20a6a351b3fe072288bf12566bcc86976e1e3519a9496e29a526a3bfeecf3c84821244600ec8d76f04f06329d62489948f20722494dec29a1bd4aeeeb34550d91717fc2014fb1804222e95f7892d7e3b36c1da9c3c95841460d0e0319bf9f8d8dc737724e8e241d848dcca03fda06f84135539f33928490aa7efcd4ba2a3d456c314c039a759c38c019c739cfdf17b31f4f3f901712a871cda2229ef2406d7ee6569bdabc1a7edf4f80e88c138e6b9f34f08afda467bd780ab8f00a95cb42dcb54c6993b08cfdc956d0dcc0f46e426773042c45fbc0f222a89aaeb9f729f360b2d8f5a7f00a15b31d2ecc6167c3bf2710032e2c486bd516bd6fdf66d8e175f8255dc29ebceef1e2f0b4f6f5d63b3ebcda02596be5260e31ded6a6ec2c1d288a74250b12fb90fd3fb137c779484eb7268af1afe045bc6a7f0c529eb505182786a2e44a7c608099513b5418364e9418bc87ddd4d7cf4c225c119b14210d9baa5bae677848136941be63a9c7021478a0a0d7b427745717262a922c60124a6ae045262b0d9570c96e46c62a04b9736c865621a1773ed5b43101a3c1ff00974630268524d185116e52248702969dfe20579c4a5f6300c6e4f74e247b7cd9e241815585a05653c10fda5465ab362c59ab9e26fd5e2a00b3a20abce27f41b2994894e69cf0931bc8cf5cb35f55bd189b211d6d5bcd9c7c1e57c0ed6968e36ee1b36ca4179abeac3d2a30c2407302592e472bdda290bb6074b39ac6ec0253e460ce9e3e939041ba1572c71a7487b699720efb0bae8b945a86327dea1dc8ce912735b71ea6aed1f18d5c50711e464fd17ac92ec738c969d87ae6a8a2ec46ca7961e000c855b34584e81e5463af3f4ecb6328e9617b08502839d8b92c080d87b6ce602b0994aacb5f5b11691dfaab00226dcba927132ae824429fd95c34ed013da54f4c6b42642db3e446ec946f8d1b12d2e6c00ea460a650f8e5a124fddbe54a415af7b77d66b01720f15d332546cd58bca89999359e95f31b28e14633cdd4305f5a565dd513363695726aeb59fb6d8c1b65dd94958df032db9f52f1a93fdc0bb28dae400298d27b398885ad9a51b5d629b4344508e05ae24a0b84ceeef2c90ad5550e59b00d3ddb9372dd2b8bce06cb056f62d283b3f9867faa0ccd56b1dc93bfa13f68d630201b9e2f7c740a2985b8b4fbc85caf6f99b00cef2f51b43685c5b4f5f7981b809f627f84b2cd5292de93a40be40f077505e0036c13d353e1dbb42bd042bf9985db89552f6644e724c6ee692c37375f4a24168d46ae71432a9eacd5f09a2d3b873af465e316c2341adf5a862a5da4a31133010971ab1efd5520fa7e1a0017e7cbcbb41a206436fcd3f789e3628d96c72dcb0246fffb65e3d8c8817538d403f84fee7871a6bc2f9b0f0d164f60e97d3da993654e2882ed35b76d7b1da6384d36b1f0a685ca9e69bb7d32e287aa6df96a7058fa846a1ad293c8a5db204bd76566ea358260a9d9627203e0c625701b75ff4f15422e2254c7da2fc6e94247a3bc7b05660f41dfe23a39468cb513bd83d4b31ef9da9d6c51f4d25d9a67b5f4cdb82f7493feb6bd10c540df3eb119304bdc5b16ddf6fd49d165cd2f945557ee93f4bd4231407a71d7c94f41d1bcf02dc371478b5ffa0b878a43834cf1d7fe148b783b4b0b06baac24b873af98d9747921fe0e944900f3e5060cb8b1ffd0d126aad74bd19601995f73012b0a8fd6c095190d21eb99f22e05cf396a6169af5faf5f503613a1191a90cde5a1d5eb31d74938b3cadb2c598d9d62098d9037af00319f3aaa5d724c8494c980def30a1b03b34030f13a818b87c3080f9366e98eeae094c475fb58cfdc7b5b05d3998e69ad369f9a3be1f9948431299d590d9802e50e52ea3f39074da5cabf27c87a473e7fc41cfc0a0658c059dc033b4f7c5147518325b1160b6013b710cd0195c0cb7024e082b882d812d409f538d9a826d24c5123c36528afbe35b5a4b7291caaf97886d60d7309bab5060bce3b18211c1414a2fc5343f4b24024ba545b1c05b15b72df389d98fc6e3b311002546ca9ff49bd534dc74ba3b07dc73187beeb8f34a49ef0a86c984e67ad53a0805bb89f9355c6cf0633ac7e44e4504dbf2a2be1a715eadbbf5db2511a28cb34d347095e23f85302c36f1ffa3dfa17e428ff49f6c6b9a9d2fe5eef7ce23697930e21939f58613128efce3fff14775c78d14e599901365df12e866e580d5966933d9042d4023870c66aa6439a71364f870889d1cedfa0fb435787d40a0b42c59ce70dc487de167feda34ebf795d6e28099f1caa5a4d46ac735a46f2a40e720386cdc64dde45c508fb06075867f3f443cb0a34e05a97b92805bd5d216e4cfbe9f877437d6c3cc83fbaa0f39a4b42776da9f2c1197c323a30d56e9efb0f4a651a90dc773a6e0b68a87b31ad347ce64e1adda2d54564de07955b8dac56bdbec7d2c00e69e01808699eb37e20bdff9374a433f72c6310bc906628a7f55a15281c79e92858610547bae175b4edfa5bec9e0e9496fd639dfa55a8bcdd3b31edb734e17b46b2b1019308cf11f7210c0d6a057ad2502c4bbf84979ecc3676a8796006e5e2226792613ad16c4198f8e490e7db63cd715775f470f32f4c3fdcee4d7e1483916048a489af240802fca246197076069886c3007e86ea18a684d60078939631148a483b29ff60bee085cdf1f837dd7607f54b3a7b6bcfd945b27231831f078f55167b83c05dc50532a0518c598d8c074f5407086a7a5296f292291555eae7f944fe816d4180db56f3c640cf901890735e81be51f6af43e1ea725089b1953049fc4860d970197b4a5968120aa5ede49b432e9e782e9220972fb13caba22a0efa66e4e2fb92a537fe4c1d20b5067b868377cc4a3ffb1a739e67aa4c5c7e5846e4610f4de5cc703b425813cb706ca16789115473b2b3f418a116f5b3e089accd8ed7bb80609378dff2866983f887d9662c8aed5cc359b4d53d5e0cdbd475453d02a1430bc71dce10926368498b136b7d3eed49ff1a9307ba1b34a552c17faca45fd6ce3e8245e023cd4d9e44c789be7895ee88ac38a196c0deb35b24f16e75ea46785f4137d3a49a69f83ece72f0458b1a322e86342bd6ec13328ad9854ed11d1189c6d3537c638db48047bdc6b49e3915b41d9e4b0f417c35bd6061acb3191cb07b96d9a3a4175ac2d367463b755cb69588194a45f10110e8a28d1dc1acb4590daa651df53ee7fd94c5f744684800e29160c8a15c712897d473fdf3868aea719c4334387d60da3624cb2b0000be220cc9b921af1a398d8087fe89c500ea4310d94dab04132dd11044e91180042aa98c8ef666c58bb8e30612301ef14424b00d0243a4a1b3af5755eb51c77731f0e0eeea3a29962777f4e14535f7a1a7db917883773b769d12a67dc7f5a082968d6ec8c3ccabf3e1e5e6c72d6d6de95383c643f3b03fe3f3d1b3fdff1e507f24fc53928f73984d3c64176a6fed794534cda6a2477a4953daad7a03b0bd388fccedd554708716091e22b625cfd358511f79c734047ffb3c5d85409301536617427d2c9fe284de94525a8e214ff0e09c9c42853ff38eebce5ca6d660c7f59e4282cfe507084b7f165a8ee96937fe139c13896c1f207bdd8e981d6b10ac191ee37fe26bccf5b9601894a24f1fcfdf851ab670bbf690540c3c483710897bfa16f1974de8c624bb650dbc443164de8e333d62afd001550a2edca75dc50c1095f7109dfac5612618b06422d39b5b37f71e8bf67af784f6154221b4b389857292214ce13c68888c42427a06559328018d7ba726ccb6a1ea3e8692bf164559d77423e78e12a311e5f6c2e2542fa9e45bf04c9670125964681b741791e881aeb16c2d4644d2c65c105ed2e4c5128d455841de464fd072134c7bdcc91beb0a9c9f1e48133712117d6b3ab02a63340c01fb14a47401a8a15a8aa0a286527a34eb9d52880296385114da50c5116994021a80176b023686943fa827325d177ab001383fe8d99801f278acd5f022ca48157fb6e4c2376c0bf7a7c91282cbcf62247029dd9d742cf3937193ab474e65d13fac34bd6fee7dd5322840f00e6b0f18e7f889807668aa78e68bfc2e776cd2e6ded48510d88128a9502fe2020f677164058214d30908634db3b07adeed1b0004c3d9ddeea1e05c98dae9a272f8450960ac2cd6600cd226d63744d2032aee2c8fed717ba313be245405d5b1bffce4bfb8d2998a0c3529c368933f0af1bc9a8abf6a9e350c3994621591e34383ceae760502151e5c4a6eed8e04f5c3a67d7ac7d2c1d703db137867a7ce908f795ae810ae7e55b79f289db12cfe5119853a5427e2156a07163c582505cfa024879ea0866e388cda5a0b2ed22229dc419cf0675bd3c8716b761f57c45c90665d6e5163a3da771ea99a0464337e649caf0a89b060dd866033f0e84370ce1ce307b0ffe811a7263b0da653264a6d87a8de247a1782e8dad6eafe8261beb112257dac393970d0a2a97c5dfab939e2c43f0776bdf6e43bad90ff7cd188f42571ce4f54e04321def431c1631bd77ec3e31061083179d750321ef0221468a6df6ad20e2587bfdf8c7ea2f13e82065c30e2131cd32a1f98773f0cd06f6d28e1f27e3b37b924115e86696066425402d2383dfbdf110274360cebcd6bd804e107dd54ba9dbdfce209637eba6c0ff0b47f68c48906baaaf1cea9efa819b0a5f4caa1eb2e4ab3fdfb6a5e945fb9b5bd852de0d305495c8a2eacfee5981d1a589891cab2865a3961811ed9331165c579e26c1ac2edf1464b63ee34fca824fc44659423482f2eb454293bfeb270dd5bd0054eb27e8fb6257879421174947ebae70426d9d4d7f16906216bff4ed5d69045f40c0ac059d35714c57a6847c4505c020499d7e99b2af48b60b4bbbbb7f68518fa631b01632392e079964ff95c59f2b52eff8478c073e9fd8ff542344172fb9c4a7338b2c4bf0a4543f6c0683f6e71a3bd1751c51069eb301f8a7be5f8c4c498344b9683e3f04b9145f285a180287fd1e9a7717b671cebb2dcd547f3c4e5c47151a6b2f9c540cdbbba991c0890027aa96270c82fcf901a66a33b76e131fdd9c37cbe35d8cd8a97f16a059f8cffd68851edc82661673d951f26150970d71f6bafc6dbf5134bddc4974d5055432435d494ce93f9783be480e7fcef2ed455eb2daf491489a54da84ed55227cf43c512062fa6b8d94bd41557f053c6b5c2c2cbc77c8850ed691f103019793d97ae2374c4230af2d19b80412c3cdbc19f3c1de91f254cce6fd881580d715352824949a91cc6264410a5145da6b738e0863b6d04838668b425b72e11382a207281acc75d0130ca7c04158e5df41bd42cc42f318a87033a9ce491f40926fba1c98b3ffebb564a10acb14ac6cf8ed0502b627fd4e5f3204a0f1678543dad9d70378ef98591dccb0a420b6d798c99d1acca4d7f77101533c5052858aee07c77d33582cd193401b15cc8aead73f90cf9863b2715fc292a134372f38906deb69bea2d588af36c13e2bd5594d18ed78c87fbb4f94f1af3e832d5342bb4eb584308dbbe412eef14921deb821acda3d9d7a03324118583b0b0dab45a0b1d214473c1684a9e5fcb2ce57701f4af09cce560b6499c94af4c614f8541ff50309081f575c4404a2444bd4b1881ff46a09b805f2b145821a227c10fe75dbd9db94112d70cb3e14d6c854d261499e18908477d99b7af1a55da726ef8951f9090cc5947c989c80a31a231840226cc0827f2996ab29aa22f59fc73ceea11eed2cf1101f8782b56c4d013b408c41eebcdd7ec81601a8d951d63fe2bbf59cda994004144cc23a984d28b8cef7cfed3c7751ce32d6ca48b95ce9f1196feaeacc19b915ab5f8b960c45ecf6dd0e58837b80263d78a6ab2aa00bef5cbc809e1fb4278a13894764d4a2dc8ac8459d259baff7e8403d01355b9d4f616afc9ce3813fe3ea9391a288e9752b8dfe92215118cae223dbfa6e935cd4ca6e836628eecd5ea9c24706fb082399becb88409d7c172ede8e5074e5cfa78ab9fa7c6707f7db6c69d8e7815573805ac21bd0a8743dd0f007bde6e7fab7f8a4685a7caba5f603dab0db13b4035344aa340e914091c185f500779c22efed705cb93202fe5cca5605e68d16ee750bfe8777c7ff098f9b778270ce008467fb7058d520c7a433afac95f739d1e307c110a7fdf19d1a0ba8ac9420edbebc9fdf4060e5d18125f57158d912f9a0d6e914642f044bb8299868be9de3df04d14430416d307041bf24b5072252f08fc8d0f45dd7010a7ae09f8f23f3e2affe39f3c44332914dbf1e8bea7d4a4d6dbcc792e3b220a17fdf39aedd750b71383aae5856c52a9018432cd15559a880d93e12903436b69cd1af00a7ed57e0f3f19522cb6f5636775fee4a1680a32632e1a750bfe9543275dbbfdbbae0568e42f448e030375c740af046ea408f7bcb82852ee77c21ad914edd38e729bd013ce1c3a4773f4b8af016ec37753ec253f9936d59eb28a900739e9a639425c8b011bb0ce0c9a0199a6845e014ebfdef6dcac4852c920f3a7f3b6a20aea23fa1d29aa53bcf9a69d475f79f08a115898badd78a224efc19183f792f60e75ac0ff7961ea7f6aba874719b555c6054ec381279be809147cef8839da7684fbc065a9f61607b20cad8ce14587608fec75d5e0b82b6fa131f4c861bf896ba900dc55c8a137985cc229d8f199a9be3a725197de1bcd1158f8b0fc4ec03b551ac902cf8ae3e4e25063abedc2a9cf863922e45586d9c3d6658d1b296e186b74e76eaf3a85dab982e870f266c23f7a3f22c2aabc49e3f3bf1fd33c2bebf10db76f3ecca3552d4b3e77a9720bffdeeed4928fe3e7cf8f9f7f9f90a46f17c2b391ce3e34b11fa1ceec4e781a62f1c3b093bf96b18e911ae083c2715d915c16d329375ab44f4d3778e6c24d0edf87daa1f20ca1becd01fceb7ac91fc09af4cbfc076f8e0ce7b42898c515031716e8a5d35670408b740dc29654f3446ee0bf197dde4a9303d6a78ba3b783eb8a0e87d10487a8ac379baaeaeb194f26636c4ea70ea37686aa376c1b0918b581682382b39b26082a329020c0c6635e1d2c0d7c80c057b400fa9b6dc86ad127b5277f26da39651a25096427f572ffa5bb1ff3f52d3016378d916ef935ea52a98453b53d9972d2051d5c04988cb08b42c3e649c1cd7d1c10f7f5e79efcb4f6b69ec02cf8a21fc7f8bfdc3c9910f96ea41442091056f5c5ac1f4951bc0b2ad2fc80565f14d262916a1c58d79087d55abf61b9ac9b0435965695f20995efbf450317d042859bf21b840e9e522a20035dca400b5abbc90c59064a8e688b54469affa062899d7ab67c68154ff8f406b38622c33312a1ffe0d77d99009db6240469d5164b7e65956dc22564e89fb88cf4cb1cd282a05f13f3523fa35b06ee879b6b898c4284f9144e862ce238eb9162ac26124c133dbf1a8fb22b97566d758f3e214299c6e2d7dd4a38d3c311f06bf2333d31b61be386dbcb3c03f3e524ad76e7bfb22be1bf56f19d77cb0e5a0d79f37bb460329ad4e66fcaf8bec80b973eb28c3d569cc518594c6bf7c2d272801bc72bf1d1208b02145313c64970a019cd27d999fca967c7aa7711e1c71f6530642cacaa06085982e919a06fb1af2bbc335b25262db643c595384a87c4ee2cef5e5b8d04fde0dd3262b347f63eec0ba4c00ffe75558a01bd1eafb40eaa8fb5e76f1bbb682bdcde41f581df5079b073aed5be5cec3166503ca682c4b29174111aab6c0b7e4a7ade59315cbce7a83881466f7adb76a1b2e8dc3755442287b9082e555b2e267595e56830990e4bf41a657d074b5918faa805ddbdb7108424eda1631d2dddf337f041fd65261170c3aea291e72c0c9583f4775ac233e2342ba893f5ba4315e03876a0b360f35da77545ddfc0eaf5ba9ddfb08b2f71b16aa6dd8f4185b73e8bb82946ccb7512e0202d4fc8380c065fea9da78907ba4abf92beef83987735d3a6b9b4f38f19c831c1a134c054aa57d350ec2e6a799d8e82144e386c898db017cb2caf7bbdbdf6015ed00ad80b8e665180cd50efa5b1932c8334d76284c63079a4082179bb40053628ef211be8a28ba003bb5955a0942c9eaabe127b1bcdb2b014aa313cc4a8d11ccba1025157789af29f744bd96b209265ca034df9aa6e1abf7fba4024413a9d8c0b4ab2a4d2c8b820254dca1a19094ef0a4ae9921c1049dd4fa191688404813ed0c0589540b70fd5981649e54741910a44c49572723c19924757d06085210d2d788a0a31aa1ebcf0520f3a4a3c9d0094cca7e438a4c82806633a5f50de215029823d82f71576712901f16e0f46c662ce85fb06cbf3f50f45a7d6957c58af170410102746f9fcc0df0d981dca0bbd75ac8c5f15ccdd578408d248729dc8540a6da6aff5d7d069b41c6dad7749e0f4ee013f5357021985782bab1374b46c692643ea77516b4debe46a7b4261f66534e663800d4d07a1d8be001733b2c00d6d7e33d619a8814aafd26c7a8155e5b668dcc1b9abcda37fac0007e43be17a7798b28ffeecd78f81fd4574fa153e633a97b2be3384f8d44402f3cc8d4784ceef1eb7b8ff771dc6818a088444e10ca150a00d779101abec79602c4f378241f7c544c6549121975afa583140340b87e2b5dab2049b9caa8c5a6416e73bc4fe519ce4a9788b685a39fcb124745cfbdf74cabc41f776c63a4b7ca2fc81ab7a1adc6e6d199792b8413ca339f4043fc9f1accb597b90d485440472a3ce035af456f0dd8e25514e48fb78f738486aaca74251147d6193b834fa46fcc48da746e5fcb50ae9bebb848b71243547094d694ad94b6de293e37488ccb1647a899ce6eec15fda3ad152d38a3ac7e137b80d8ca748580addd78d7d17b0f9849103e3fda63192e7b3f8b2c07bcd34c4f860a0418993ba1af811940750a94554b9840937a39adbb742ebe036618cf873439eee490f383b838279a6a9c3be0ec6d853916e3141e0256f414c5984e844ea9ae23eb1f05bef84de1adde0a3c0aa4388c2c10a0837436afd56201b497c9661e344110d019e5fe96d19f4163d78e36b532335f82197b3b14df347e0427013223c8164205551d9696a2e6d105b372f250235285a4227d9cc0136b1b30801ed4e0a94b4166de14ff59c5df4f5d13544496705566105a48477ab1a931962ebbd69fd2c1fda14cb523a817ee8d9002b447158f834de0caa7218ca841ba1f0d27891d431964e090e96f7cd195577bc571f3ca4c3b2996ec30e33fa707a6768115f41af0cb8106c2d899d75db120be8579e514199a1b40e57d67f58093cdfe070cc14cb6cb28703fd2492918b454e308b1637980307175e527160cc9c92752e1e3d712ec7218df11723b2646931f7c313e13c305d1caeec7bb86172071bcb55f3e805bc5987a9fd0edeea1029ac4f40d851de0c80f1cd8191d141980110fa1aa130a14ceb8d9c0754190a1de6536c6968e4f7a32833af9342cd0ea99180ebeceb01505d8facf61d69b005518a1f2fe771aa72b8b747d0d80d0088119707d43bc0fa33b6263c83f636e46580ffd1be6627b00f7ebca80aa4652fd1f5409005c6e913a52d7647307378fd74a4cbd51ddf7fff6106dc743765d01788bec067da8169b1d3136c45212380f8e1136e30093266899631331aed50c05517fe7104d03302fb24ad272ff2c769cc13cf6e800be182787d446c7768341d3ce03a77665c6ce87552eb7ea581b2c638fc66b4a22091e4aa384dd690f0d074e0431cec99ef74ac531f6e4b0052ce6e0ce05c13fcd6a91c7c96e2e0d803455814bebf234945f0220587b1053a0017a9406cb593a81a886befce4a8b4d26c79120420a26744e98e0c9c8ef7d63d158488177cff5e43d30abe5d1bc95cf21e9f8a4cbc1d634e93c58584055e429d7104de26f5c4b55265dd0b1f12bcbf8423c343026135aabb78814a96b0e0a4950ddefc9119124b74e05a89e3505ab612b4b8c02f100be90b41139c38333c7b4b213ab7e2e65853069dd9c7009231be949db8b69a462123a76a21294a7936118f8d3a735769f39e3bb7b159f787806d65b528c433be3ceb31c9a275e97363e21b96d3ac0d9d4d9f6483850f6712c5b43e994b9ea4276e7b4dcee822047f910075dd950d64af902d4716b621cfe82b6dffdde22453f3d4c075016c2c5916d0c43aa5d18cdf04284a7e2aa75da4cfbdedc09f61ab9d1dfe62429c2fe74e950d0b9038a9f7c39226c2f612c88fcb753bf43d33ec64011553e634e4e37d8ae7fb81165b069a608ce01a8ed5bc68d0798d5a24c38190492bf12759983b617e7b011e69f0164f1b1882e24ff2a28bfe34cf638452d56acdd4cb9461c9ed11e0ea400963c8157ba608be653b52499067def0b3d5d305546767258497463d5386f2632ab6b343a36c5f982cf9a2f561a67b759ec4662a7079ff6968f8755cb1fb7bcb09c437bdaa17a33c516e686e8e637f7143684fc1763facb72007876365d1107063141558c918f5481283a747e5e734b1a6271c09a6a139a703a0a982f3854981750caf3b428d2d0c45b240d79cd44162bb3addcb97ecce1c7c6af2c3c3ac8dc1f020970c84e54bd19f86a4dc3a63fd742795c11b47d7c61758c2f79fbf3734b54f408e32692b563c1156a832b310ee40f965d81b262272f12ac9b97914b706905f448499c041fb5c00131915088475f00fa42f3c1694ab52b996d6273a0d7e19cd8d97219cc579f04a9e0a2a5ac7968256228b21b4385f9e628104bec6accf83bbc87fbf1b30ba970fb815fe1eef2e3ccb11af72d54799d26926310e7765bc39419d30a7042fe04731e26d4667d2b95571a5e212cf12833a71b6c813ddba707a334965c31677004edf5c9c75a7f41bfc8469a0f806b2b8c5f359352c16960663a080550633405f81849351a14948c86ed62032bc370189aa0db417282c9426fff71801bce088297e387b163f949095fa9f818d07d8b4659e215246070514ed023a26e2705a300e524e07d8ca3b0870767acff7f7a8833a2d0804ee9bfc33be7cebdd75bed5e8d21817223ce77a20fcec8ca2f3b206ba49000a40a463aa30ca2375e6c1d104b41ee13b0a6d5462a6bbb80166d788b884c8de7bcb2da54c49a6ae07b8069d06afcae28124ee93bee7d80f157d0c750ffa00c52232d6bdf746623eb8fffaa75b0280566b95b7be94b374ebf3165f5a7a7347f0562ba62a576b4bcad4afd1956efdca4e566ca1e250f8e0ef6857ea17974112e2d6772cfdda80be8c7132f0ad11f077fe53b164e173cf2a1fa1f8f383fbf039d18fb89731eec727d66e099e853e08be159d260119b32f63f6a951f8f912f8dfce4b207c503c01fcf077c413b2fc003fdcf913c2ff62df738c846ed5df6181e0b3012593323a9215d50759b27adfad5ee7fb7fa40ef8f253b1409654c95781e28ffcfe07f87daf128bb0d7ff2a08d629ee70d07c4f143999d76483507c436497be649960af106ece15b2dbfd6a71e98b000287794fbbb760c84383d2faad865930e4a171698b355a08c1573df7f6c7c51ff72ff2e3933207a2927ff8756e3ffc3afcfd0fefbfe7ffbed7118b7822fd7838107d3461087c87f870903f57d6cf0ff0bf0745162debe787154721ff08748c0b65f508f75fc75a666d18f6bb9e5fe8b3a10f0d2721df1f1f43fdf277aebcdd8b3ff4ff138bec7020eebb3712f358423f54fcf9e18945b2d04e2ce28946621e8b631d8b63ccc14013861c447f8783be3edb647bd45763fca18fb394ad2b155aeea76d73b57ef56aadf5b9fe8e6eb126eceafeae21f24bb0d73584bb2558ee7f9e9dbaa0b9eca403951bc24f7df9c3955f2e2756516c512564045800dc39c6b8f3c702dc397f14c09d1c34bfc59ac9412cae7cc305898b932e4eee18ca2e4aae94978b19015c76e28285c70c61bdde7fafe588312d501913c4143e2f84fdefdecb77bdbc4f841cfbf1e1893f9cbc15b06216f187137f20f0bdf712f8de134fc8f2a37bef4fb0ffbd04bceebde71855fd40c0fef72374effd54f918fa443186b0ad8b8e6110219761b7641870994241114dac1c2dc5e4941ee03c6d6993e4268598f4c0d052182590302344125e78403a82e80a146a90f836d9acfc795c08f976de277d6f4511e15ff9ac14c2d1872515c2be97efe33019c3d1405e594158b7c5ce8543a8460ac220acfb1a004202eb3a334f71f4e75e3a54d106c914619a6652aeb018292aa8991295c4890566620a2422396a6e28686244871886646083256a082a2f1d5c38e812daedc7e1ea96122870082b3636538694c44083234b9c6141d41a3929c4e4032141d6a8e080e6b2130e62ee68802bffc565271cb6fc57c9affa41157f7a9ca4a008293ea049fa01ce1b282b44ac59628632b19e2492b0a1055a134545b10a38cd20e6298a0a569089c967958d33e168af946ecaf0f054a687220ef2b9ed63b6f25eaa7af4b5bf26ec97de7defe309d4615c32bb8e7d17759c0839a8bbe5ec96eba0e9ff7efe94dc85ab3e7ccccfdd3240171607b0269c9f839d79329ff1979dcd03a9b0551f00f920e64a3001a8e2089353181796b439c18b0b46dca02043449698971a8800b9582fc6e532485baecf6527a62d476c70b94c92a4cbe2b213530b688caada943525c27136391172d3e5f9f4484ecda67136155d2ab3618cab262c9a4d2e2b721827e540133cf26e77ec9ac023079a522e76bbbb8b63c718652d8970943589261c5d56f3de9b82e293c1f98ae278b7cf48ca619fc80e54a9544b7152e1d835dd39bfb3798c13423ed51867a68126948c714730d06cfa7e7e27e5b099cf89158131efe7734d0efb44906bbaf31d49c835a23bce231bfafc046b176c026d777e5377b9cc14a4ebaa5a97c4edce6fba521ce9957c59fe9436240ee3588ba3756993e2285da8979d985470e7fb91f0ce77222625f28634c31ad04a1458a2406116450b51b000c5134456a61c6162894d0a285f1718dec44009273f70c1e1852b96056fb8289620c1643bc2a88d1b229a00a146cb5214238ef8e00414cb248513982a6aa0ac143104118c241bb47c992384910d28f0152b484991a60729da509141044aa0492a220a8b0a6dbc5b1419241b908ce9a2849c5944451a3303261a720843a5635ae17a0b5c549aa3ca449f711ab484798aea418632489870374c9eb4f8c0a6065b9222281f1304d4707822c5c311433039a20a143031050f508e9ca9e289b7c4df1e1dc172fd499c14702102881f9882986ae28b8c3669567881490533332e8913c0ac80051eb8244d716596860830444ca89052c3157505217890e18d1134bc898383db82e488812229ce104c8879e2e560850986208e70811447d31685654a92336590a8a9012492c0d40c3098c1172664cacc27cab429d384144c7a788ae2099804156506b22f71aec0a0246ff8ae60a1a6464a0b5156f0e42be2c611344069f2c30d0931334d08e44049c185284e71c01c713508c006333c48f14414342d734889cb0c51ca1c19211953a3a0708517255b91154ea6f0222aa0054c941a96f8e1caec0997c5a3748001ca0b4a4889f2022d4a2697d1cb13922676f072a4548f9620b3c495219c90419b2727c2480e2eb08149152d8012050468d4c4133640a94972e6065913c14f2a00620ed1152f284144e702232c6072244c1423a331637ace0252f0a202941992102df1c4891f61e0b8d1e28219393a28b11138a809330312253c9ce95af0b7506c1b3e02063bd480488192992b576480a28217185c9166081da0c400c4d410513944e126081b50e0d1e40564a82c99c2091b0fcc0e4f69e2a4204aaa369d607001d410493001020e1328f64c2b8d1b1848b5293ad364be80820c599c88c10ba6a8c0c421f12a08a066082c5566e0431c18aa97de94db1a5c7aa3b7799b81013ae5466f9db8a2cd229c5f3a3d38f476a3701ae6c1f16ede6d75f37208697416d124fc6200b7fa99c728b94e9226a74bd5072e57ada207424277470f1e1cd4cf4d2c0e5a2d8fabb833ef0742e40de71565a16c27cfe936eda369702bcd611fadfe47fb681f6de5449d6e7f342aae6c1cd4ef6304eb4559285b329d3a2e7e34a7fc24acb45aa5615f958f96837e1f8d161e7d37908cdddd8d84ab747aaf6e20911c17d93e997efd0de49c36e084f27dce392755bab3abe1bb712494fcfcfcfc378e84f2651743e77d9deae3aacabffa137765369bcd66b3d96c369bcd66b3d96c369bcd66b3d96c369bcd66b3d96c369bcdbc67889bd503c1f7b8073fd0fbc0eebba3fed22ba294baa594ce6b411004ad70a2ecc4b12fad17fcbc592bf55b6db7dd01414444444444444444444444444444444444444444444444444444444444444444444444d4f1bca1d3b9ae7b8e7e673bce764d3889dbd583f2a4bc270fbd2787716c5acfd6add9755d9744c57a3bcbc9e9c99f5078360eea6fcfc639d8b3d96c6197836c9fb472b6f33e50a5b30a7758b4e0e971f1c207468c1f191a40d1647349c64f0c183e2f5c006003196e44a33628173d3c2d58d00dfea76b3437da09573aaa67c530ea9a0afc3c9608c388abddbcaedf8a337ce48b46b7bf25cd1acd78712dd4ed966ef7732a107a6e77636889868b7bd5843c9738478fd7e9f424f18ece3397bd9ac7e43575abdf0bc3e4b095cd6bead70aca0bc3af559afebefb568550bf5fd91cb6925a41f56b35b54ac3af559c9648e44aca61aed554bfe42a0ebfe48c5b52d2895c41075e1332904da4ccb379365bcba49439ece3a695b27e59ce727482dff7b209c3beef9748beca799deb86cad2e04a994c265ba1e092bd62e3d96e3fe550e639cdc9f2220ee2ff269db18629fd7771fde9533ae7db6822943fe70ce1c575faa4bebbbbbbbbbbbbbbd7ea2e29cd42c56f6a0614ece23ac7b9fb0d52b88a8f698ce092cd8bda4ca38962d370a4f8d686a18fcfbf1447cae6a86da409473f728e63d3b04a5382b35499ea50ad5525b5a93aa9b68a74549f1cd62935507fa7a444f32125254b4e9890aa8058aaed9b62d32935077d9267eb480ea22eee6c5bbf5e54340d549fbad59f001ac2b15352ea179686395203b51f815e42d08f6cfdf2b94cc1255b44fddf8086e16abab59c83b65f925bfc94e59c732e31333315f99b734e6fad09e54fa5ebadcb9e8d85b5b685b5d55a3bdd2dd881737a9e4e052dc7feb6b26c7deb51afd6ae2f39ebe274cd78a267ca2ad1b8462bc735a8279e2692ca9a4d9e1ce64d53f604bfe6ace96922f186bc216f68c91be29c187507ec4d4de6dc262fbb5c5191c3ea140deb94ac5a59bfeaad5b93fbd984614818d6e25c9ff45572ac221baba24d0ae58fd6fda1b0486db975f1a92f3d77ca1c566fb729ab3755975087104da8498c1928a95b0767aa4e6f536ff5566f1ecd23ea6e73769ead916112d3794c62c830552aeaadbe6158350365a6c6319383198b8481fa398e6b128662062e7d59a7dc3d70696d0672132e6d6e3967adb556a628393959dcb55ed75bfc5661f62d6b7620c6acb5abe398f2458c1934516ce4d45b9d3527ea2d883695dcb4bb7d4ebe8f9fcca0798a82b2c9f6492b67a7b5ded256ce766c3beff33e50a5337574bca50e832a5eb94096cea783e5f2af942eb3c270cbe5df59623159b0f0962c9ad62dfe293872a6e0f450cb6e4d5fc4347d3163abc923e94422c923e9445691461dd56c380ace0d48e3b83a70f01757b7f865835652d9ee73525a27ad95e3acedbce979ded2e36cd779def781aaa952794b95fc4009aaa4ce2a9c61e82d43955cad4019eeec8092458bd9a285b76cc1d3e3e285cff4f1f1963e3b56de110c41b27a5421c8dbf2862fe68b17def285d5f9e9d16375c6105cf93d578a6deb167f01a47c4c63fc6ca40947a1eba2ed1fb1b14ec8fb559398975f154db7389653c13e876df4d726af35097eb953c12161a019c36a5403fdedd62814dc6e6589eae401c639747b9c476eff947507cdb520a576fb7d8e4fdd641efe957e93a1df1cd6592255d5c3de4456f598778a9dcf61a7a2e5d319992eba928679cd9938877f93131b92436171a92d7eebb93299949ac33a330dd48fa629cac6e676706ee7522327bb81d073a7d8d9bad53fa38950fec8c9e66cd5072eb975f88be10b39870942b83973a6aebcfc3dd8a4ae144bf0cbdd92391660bbae4397538605682e8ccb4e57d06eedd61f1f20cf6e95326ed5ff99af9a73fecc6f7d5f44f5dd4780635fc4c781eaabea0b4d714804381676e2e8a3f3a3108d716cf5a3fda13f8203552a0e8900c77c88457838507d23b107df48fdf9a1faef55e28f8ff07f7ed857bd0d2ec4e7c871809c7385784f90523a4a1c466ebff74b8a2d26711f7e684123147c4c63ec9e2c6191133cc5bc67d50fa9c40343e8fd58bea00d2736f4095b1ae816f57e2bda282c42ded85cfaac1442c9862515f24d626998f7453af63d7da9e4350940e8bb88211ca5924b9f5669a32b2a1842e15230f45f649c708ea3af7269b79452f657dbb5ea8320accbfdeca603ab38f08b340c24d22f96ecd2405432d12eb266e4a45f478dc443dcd12dfa543239cc138bc8d827fe74b3de8692873d11fe6d1afd56a2df46f4251309401ce6638439670737a9c9b611e6fff4a84b96d07638cce540f4297d188ea53eaa72293b81d9c2f4e5525903f3e58e20058d60c072c756ba74cbd5a15dbb947e95afd155eb17ad4b68d75372a964ba544a291d200ab5ca84ae86dac9f2310d66daab6100b8b5d2967e8981e32adcb1b1a8683e54b963337d9173c7fed24d6d1bdb4c3f5dffd9dddde57adfc02def692d8b2ad11cc652523162bae5b54aadd241c01675e110ce1fe997eb4d54cce8b3b2d52ad7bf69b7db8cabf5b7451a48a322034db13e65a235dae4d49dbe2cd9b991241ced6c2c4cb89ed3dd29f5c07252ba74e9d2a5db77ee5df248e9d25b4dc207f4fdbaf43d31a45f76e57fccb2324a2955526597af511157aebbbbdb4f70796ef5596d6658c4094709256bd078f40b94cffd9adfa0bfe431d33039d440f2c54816242c2681e47b1e0b16316288e2000670e38690dc210d20593584f2613ecdfc2307a0517f55f4df4812d2e4371696df6212362d46cbee481b278d527983079425299364125a7229b56854fea4cd2d1e78c0e4301f212197abf5df6a128e96766557da10dd6cb7d34abe35a884ddad4125b4b7069590bb35a884f5d6a012d25b834a386f0d2a2137f2d4a012f655f530ee742107f1db39997dce39a79439589579823c3c2d845cfa0c19be1b73ce39d9f62834a7651442764a67c0b07262006a421ef342d1e5315598ee687d643c8d2a2bc7c22b57b9dfe11cc7713ee9f428a5a0ffa49482205be66e9d7eebfd5a72bb0e3de15231e42022b12a4405ce5a2d5775782aebc7bfbe15418855b148a85dde9a5f59f672914facfeb702395b5fa7fe647da7b22a2107e2ff6ea46039889f5308e5f30e337b57889c33c55104aa291204c1972a9d5518ae7454e00d99dbcad1e9f3729256a1fc722c117834e958c00e5aadb270b8682ecb011244d974da4d425151425109e55fa92c22c2412d41950a4c5ae520c2419273708f8e108fd7d10976202bb4b2db81c122f2442c57e9bc21eb24139144bc769144b64fea6a013dcb0746b843848d30ccf34417a5942b5b904d24121d514ad1e713e515219d6eb94a9faf955248b02da5dbba141e529a740849119a2c226d027eb95842b7dcdd722285b1333968b6fe959ea5cfc91384a7b35ca57442f98496ab74fa9c73b6583076e6cb25a825a825a825a825a82587918081fa51b89da4b642d3139a11b7df2372c5d4d7e9f4ac5ccaaac7fb5e2091edb367ca7593925a5e2c22529504022a505050500390403310f54ce8b4c0761868a93320ce13cc9eca396c0f23a0ca2954ce9e429ba543887a0d07a51ccb8434fc72310809ddea6f6ed15a82ebf33fae25b7a7633916b6a7637e16e7a8cf9283c4ebdf795ff89e8f8ff7f1d7035a17100b46c713840704baaedb59cbd5e9aed26972b558977bc96152d6b55c922dd5b8a394d5b84a3050bf13456e8c1a852e4abafd9d0a04ef4e5142cd8162e9ddb63a20f7ac5cc4a07680dfbf27adb5d64aa7ca556e3bef932010ebe70c233333db1932d449e96466fedf209cd2ddddddddddddb99b33c255eb0bfe1fbefdbc5072ecc3309c2b701c154a29e5b88ea0bbce52789df52667794e96b5f29b0245c8f2c0cd9d1aa11a48ff252a1bf2ff3eafab3d7b5e4a91e334f07274d9a907241b979d7890bade13e7f8dee573cfbd0fced13d77e31cfddc77cd8d3847cf73ef509cc33ef743a45b568fd2f5a71f15d75f0582b9fe3a2ab8feaba8eb1feeb0f84fb2ecab7efc9464e8fcf851b1fa11041323fc51a502183b3faaa27c58fca8a3e5fabf68f1e32a85ebcf8192e5e2ae9cb84a27f8f682e07b5eb3ea4f567f0f6bfe0edfd13cac21f2b660c11ad9e972bfc31ae594cbb9b9dcaf5863f370b9d7618dbec2e594b81c287a451ce49f8021c2917a453748d23888fbca92b71a07713f596ec441dc5b9643711047c375ee8effd3a3031c274a2a4ed6530bf951f16eeed6cbdce038238a125037b45ffa0b3360f03927cf39e79cf3a34f29a54577cee95f02fa86ce7997f8b45c75771b526aa5f874f9734ac94d5fd1ea644adddddd0d7f5326e7753686062cbe4d0f434add6381768a9d53a67b9f540991736e77b7a4ff49eed6e9be53e67062d0ae49e99c734e6bc3b09563826063ce6983836cdc763d03c906c1ed962ecee1d7e8b9d55fe44a3b655696d861d25a29a594721c154b68ae47759f735a3b6b926db6ecd2b28b24736bb24b77f70814607f259470c209376e78aa452ee220a031e128b74c91861c8173b46daea9f96034531c47a4615e935591c232258d249254e2ea949a73ced97d458874cb7f64a859eb039973d6073239ae3e107f0dcc7620406658b1ada49f612504f243033b6ab6dfa1a386c4062d1cd574ce2938311c27c20f204188e890323151373872381d0be8d7d8daa163ce39715ca6ff57f4d112c1871007eb78ee3f2e00dc029ac00326c771e2102e006c0059e3ca6f8d2080b1027cbd399146002c70593c7c81db9381e45b56109e96509117b296b9314ce7a494520ac26021e4310209fd9abfe38ad02fbe95bed71715750871930e9808476efac14384269c8341ce8aa4c1f25bbb56596573d0eaee96dc9a2f550d30c11d4d2027bb8e149983e607d4ef1ceaa5466aa4ee6ea47047b702c082706c27eda49764ad95561a3208e11502822b439e3216e89c94524ae9534ae911aeb4d65927a59452d7fc5aaa74086903782efd8fb24ce81e26b8aa1e54d500bff404cee018e943a3d572e9e0f183d1b46a626c4f5045fa25a5e496b7ae8fad968b35610944ac8f9d456108001ea48474525a699d3a84e69c13d63d77f3c651f5bfcaea31653213ba5b5c9e20b2c70eb7fa5df27dce1d0eea77c977be7307c707ae7c6e105cf9de922b194a27f3e0204dc82868b55c3a78fc702434ad9a18a71369f4199ce648a10b5ad7a528e388f3252b6edf6f05d210ce9f2f61b586424d8645a6428c3e28615e2d859aac8b8c498c3e8f8485b521994c8e2c4a8cbe0c8900f1d267f9a200b893d239bb5d563632fad55dd7d9ce72d0b2b4724288dce9b18656dc81fc231febe10e3600e34a9e0eb4e020f95205020f082d6ebf0e07c951487722c8412118559727b74051489116933ce9c9e8575fa65073b4b7d2dace6d3427a573ce39ddef27851c2682991b318453643941a33694522ababa95c4e1a0605348e9b3ec76d0e84a9d754e3a69adb5d686d5afcf77d65a9f16b9b5db0457deaea2e456f7a075ec135cf9ae72809c33a47e09f6d6d1c895d6c74e99924f7e7777777777777737fff04b293d89fb1dede3d3f7656074dde5125a808f0bace4845ee95cc9e9d76887fa35864d42d94a0e96705cc1b9be82b39a32720e5a73ce3976dddc4c6076523ae79c3389fb16a1817c1e09a709b31f2036b76a09ea03e690d9fd3ea72843a74f4e093f03347fa13f948ed030af010942a45f43cc4ca71c9f9f5f045ec3524ad900d92c1ddde2177258ab81f8b9f2df111888dfda6e7ebafc92c50e48da992f64981114cc4881c5091d90d4bc4026898a1a1610918233320879043558a1018946850a1756b374749b01891daa40c2cf3bbe9061b01dde08714354151764d081244f92d2bcc18285a8670d192068b0c2484d10311642ce0bb7355164718ec4f83901dc041827b429bc202561c6c89417d85aa8a28508222e120a8824815f8a27882764f921ff04fe399f278d15427e1b0cd4ef813710532e161461ab8ab763013c5c2cbeebc2db755d278ef543960e8b9b415c77aa589e3c4167c4b607f17ddddddd3df52a22e46e6f2d5ff5a177360fbdb3e9fc6ffc38ced2670f80778a6309d78a41c0db0fa7df4c41857477974147712bbfb955bafd16e99ce3fcc9713f3971fadde1963596607facdf71b7766f59b3f3cb737cf4db3eabea03fa75a460658df576b3dae3966ee5ce124b667777972ea940c2dddda54b6677777777e9920a24a0b84fdaecee4c5ddaeab482a005634085d23bf2f95b015e762a23c51ded1dc00cc2cb4e65d67454b5160c798a70e247656a2b984959a523a980a15b19a3a34e54c0eca84c9167431f27a83b7eed09d7822b5dc884322e3b5da17160c216979dae2c717a621bcec0841393316b84c832e524040e45977b1f32662ef71fe572b6275c30a914ab4f551f543b668ada39c686be4ab72af1b1dd79adabc9651e4fa18627d0ddbd4a18c09cc42c8d51e1f6739819dc7e0af238dd51c6c8018e1e67313fc8b9fed39b6cd79bd600c18549104884c972fd77b053981ed85666c50302c2eb6ec37577d7e0872a468c7618723374c58997322e44cb0b76a801081a192262eeeeeeee31b96e039cebdf7577f757103f14c900793e309f9412f6e53251a44220af3492539c184967a37e8d2ee576bfbc25b7a795f9ed32556eb775fc6bbffc21f8e597460de53c9da300346ecbf01dedb4197c4c0347b78492b8e7587d1cd49faf8c59e5b774e0100abf15faa879f9d5261e3e2e4042094272b8098acd6d4a46ad6c429f5823028fa590aae67f93357f8a43e80f71f5bb28b9454d70fb039ba6a8f4ec98252d75866c00008080022316000020140a87830191509a848134ca0714000d7292466a4e3c154823398cc3200862180842880062002104408248a1013a036aa556f216c4c9e53075ba782eb0d1d5d130ebb8a96f9de584576a48171f5536d9d1137a0ea5c3bad00986dab1d100646bcd9d751f7275324b90c9712ec4404b833799f33342dd33bec056ebdeb9fb4f0a4ef4b5b4d2dd82c6942cf346c9df98f750fdcde0d78992cf65e40ddfad351ba7928e23eab141abd2aaf21874a421f6e6047167334a9d56cc544dcc23b2f8f154ae7ff9bab2563c1f532fad781ffa5d9286dd43cb248e05981c627f411034fb6d959edfc742c0c6dd7a33f630859bb746485049349f5530ef4f91fc3daaa4bde1f8307a872099541e2177bc70f25c40cbf75da827d905eadd5bd817bb389246a74ce8f63c8664b1afb32f12c547bac8eca8eff51e3dbcbfa394af3ee98b7d0d925d524191a83bb1afa32cb0b07538ab08f4fe7a042186019401b8d91d4fbcac1ef43eeadc19c0320b9f90fbd09656e684b3cb1226c5692b671839a38a982cd01a51c4cdd34d50d2516df2a6c8964d67f88fe3500c7c5420ba0cea263da8579f96e2206320d5983db0c76264827d552182bc4284b462ea63d7321df62ee9658d49040f37537d3ed9716c6fec42192634e0eed962204a6197c8ae3ff88ae6d6e22ce4133127f016eb8338ac734cdd07770bde600b9ddf6d88f6e23127e57e9d838e643a83e9044048d76a403c2b5c214afd44ab15018e708309696b9b01ea309424d6b1a68979311f9bc265589b2fd631d2e06c0287c9d2456cd684f986aa7db8a98065e56e56b8318f1a586f4c5afe156eef89a62d7e8ef55664805aff02396c45ac1a189f8373e685f8c11d8095b43a5e37fe296e43a31eb4606251bfc2f2c81b7f559684baac937c11289d9b7a197ba85ad27d3b3784e0ea7a4b14f6e7e4ae3323273a66e1716cd53a1b7c4f105642770102d206d6a156fcbcb42ca82a8bce4eef09e7d2f841f2a570bb52fb1489bc40907acd74ef019642567c413fee446da9ac25804b782d16171116b2c20d568460b69284e080c9d8407355b540ba36e26fd767ab7f22a51594f27cb9e3736a4342b54f86adf201b23f6e1b52d1facf95d025d8d99e3af4f4d1270862d65153766619377d4683a246b3f844a8da89cc41095a3f1aa82a7374e8c9c7ecef8ff16c30326fb118309ddf2be4aaf09d0aa8bc98709d8e0237a3fa5ec9da45aa706e8bbda9daaf75a9ca798322e9c2fee628247801df20078fad8dd7ff2cc8324ad1bc8cd80acefe78684649f6ae8ed3fd50ef51e36804e2e57fbe6e926596226b623360d99d2a70faf36af2ff2f790b4a8149d9df2702945049ba81ca1d6df6af2aefeacb0c541a3bc6e11c4dd9c2fda2ed258a4112d7587b0fd36df0e260bd196b28443359b1c3dc294d29dffb4042cdbc04203b3b09e3132603982ea146d04e9451ddba2dc9658d3aae3790dccdb7544e91aa881eb1b44652ac128efeb135fb4ba59504c5b9429e2590528af58e972f3ee276b1d7cfc0a3edf9d01af4e42b8049048149838544425c99b6b0a75bd675cd22e84d496999b30d40f707c97ada4ccd59b369762e40889e9c897df57bef717f0ce94cbf2676e3ae5b0208359cfc51634b5d5c2cc9e91cd91d1fa05f1ac1b5d5700236eefc9b90c2f585be632a519174528a35f0b0c21a82f9b34286a3adb842e51444188336798e1eb748be75033ea3f0d096c32813b39eb67930122e5648d2f637b59e868ddbf8f9975a2c05aeef3191b568b2018feef6880e80045b3c5cad4a249dfd3b3a0d98c6587835f060408ddac0cd75fc81b321939a13effda04813d894ad25b660e2dc2528fb8d16fb0264fe83f6e308293a254459be18a737efc015b7aa1689cf824f53cf32199f2d4f358e3980ea6021c22d446fcf5f1d7ed538ea3155069235e9c5d9ab2f716cdac44b0ec6522fc0641f444c0596a6219bc553db1993b142d9ec076beac72e9175e993f239e8d69387b07627df4d905d40169444bc749328153225391f11dceb5946542a4953b01f1854ba9fa384fa56f121f2eafe71715e8f536e405a3ebd6f0b9c4fcef8c7570025849a41b8c1f9ce6320ac57f740a68d6c381f9af595f40812f3d39fce70a4d369a0221d586de6a2cc058b06b28579bf30610704b06a978ddbb76d5dfe27f88712aef86d820e68feb7643be98f03cbda4903a3b8b03061e14ae72f870310b174107803dc4388650f574d8304f7e2726834fc48dae4bac9c5b95b51c2e9f16e26f2ae5318c33dc328ea0f1af2534cde4885f11fbec4c330f374eece28b77f4c4c0f048d672919258fd8065a00af53940104455fabff732673ec878d3b114503948c9666add5b01113594a8f463b8c2787e47410054e0972778058650eda2f2219c20ced9db6dd452a90775d3c1e7e1d3c18146bb0e42606f49900ec9091a6274e4ff7f30efe8aa3acf210f0d88237814cfd8732956c952d9880d0a3676c88b2151234811993a66db99279a42043c52ff3a71d8aa04369c8f6611066350274315d64d8a9a89da45f7de58381e432056830441e7963947b9b93e8b7d71cd29aef303bcbf4f07519844d57ac45b184152718cba676f898b69a258428a8e61181dce6eda9bf6094c93e503d0d8927eccd0f789bab435d96f4b121ae0d7a33df84460b064108eb891e1512e530bc06da251548e241173878fdf39668c0c5fe85f088073f5c14fd0a8b9de2065bd9f1f4ddb1fe589aa64fdd441870e665209ebc5248922bd2d1e2650ce2f31dbe1e2121c7c7c2fc3ac27b63c06592dfee9d986771148de38b77841db863d89a15ad1a149b17031f8268fc04ac68258ac67736712c8c5a05fff0aefd137f69b48b13c5c059321f3c3d9b221262c0c43e21f392d1ec657f6c4bad8341cec76e9cfd22a2dfca3064449900c9925dee022c604aa9aec9060c78eb802d56140c6be131960ed2c9813ac83df08cf8f34dfc867900e149e838abc80d983d96309e87d07988a4bfee88ef6300e65a91d299e1827b85ebb44d24f958b03b7d7e4816c389109122df113f7a5efd93c13b798e7e2cb08e1f46e929ff25d55d248796b0a0d88f50195f979a8be08f0b87aba7e1c1c93c207050fed61ead15480803686376cb69bd81bf2d50707feb628cc432772688f23941e062ff953d72d5e6249813c6b63b85b6da58bda26f04190c77f7f54c7083f87bebb902ff10ca71439837cb7346524b1639dd0823781c413908a0df79fe41c0786886a65674c827bb7a215a5a5a62cf06662ccde91747c626df98656c89cbb6b6a76fb568fb6617c1aacf1ee48eb4827ef0580e92124b63cf666c28936e94ef94da913b39eb8dc4d5475c9e1f449d352717652043b95bd99e64eef79c80a0d78365a94935f9e280929bb1784fe19dd2e7b51dc8f237ea539958c3722be3f2716c8e82c3ba417401dc8d01086b0100dba81356c272523447b520e06db0efbb9c06c4bd0d3ee0590e0dcdaf7b439a77519c42c3257e289eb93cf15b950ef0b687d7129e8316917b64a895d0fdf908e17db611f1a793f31dc9c284b56f96fe13641135c734c7999b3337f70044eef404ecb4da9b3db7c3187c9d0091aec382c33ce7d426cdf57f9a0ab67826d311186fb6a90e969a4da7bc7205c4e2ca91aeffbc12d749698a353ff3c8fc68a86e271263ccf66f35c39b18e9170225e3c6ec21806893704950096f5ef01468cc993f144d57c0527bbcd62900d996614dcdb75a93209bf5cd9156d874418305e30bd2572b087c927fc49cbb6128a3419b10083c88712519542ed31946d609de71fd04aeb0d8e5916f7a2f20898b708b8170544461d811a306c162205558a11b4cb92f10c4858b422b065821c0935f2bc1394e8195ae813d3dd600854481d6d90138d72f2f815887919698f99ada7e1a818531c904b188d8bf4889fc183900c9189b45f8632088b013fcbeed75988e9f079f451458e441301ea5b5ece99733917e46702ae5be5ccf24380f48b9bf89bb0e62123dec8bd04a418139aafc2dc705701981c055fab1870dacf4ba5d23dc1601d3ff168e950acb961a1558a927c2d22b82ff797cc8f8458f5c437d3e43b43eef344f9f611a1042332146260712dddd7ba56e27175851906fb734759778da3633f100770b413440b8bc4a7311b03ec77b48d64fa4bb16b65eb866b77c3b1c85b9e7e465e4e89dc185c4fab5af6eb96b79c21d7617c94d93e543c1ca69725b1bd1f915fb2a917bfd2c11e88532ae24f87685f17e741310638ef48999fff6ffe2d0dd021f9e3b8b287ca17ca22f610379c11da88a221128169761c10268497674e28ac5a2f631e5b9d67d2582b99544c2c9c2d48963e84edc318fbba3d682fca2a1e5319a495d3dd7cdd5dd24a5185f9012a26b1b284b8c60b63584db33b493a456a04418050f1019fde1332367ff508f483985cea2bea4b14143460d144186edaf667c1ac45f247a2e91bc14dca476fdcc6ade5d1354fccc95d6d5520ac3558c02f283a9e4793be280f03d12062119cbd0969aac77eb03e21ca9f100a18b2a2a309fae4c04a412c6e301f9e5d663a334e24eb448915920e4f300a2b22e5b06d543a6f139375597d8d84595dcc9fbda415db6bf028e69f89f437dc4c39a3be9a046e016485299bf6a2c57c8c9f54013f75135af124403643e844c28547873926b3b375918e10445dda37d2d286f2bcce3252df6bfc0430275fbad46754aef68791a0d3e7f2343faa23ee845f6b65d2ddd02f425e37470b2f678221e9d9799fcfafc3d321456c002b80eecf40d357067f8ee446a79b335f246a5f053843fecf3922d9ed18696ab45345ea1b089a30a8958d9efb315960a2f9a932c5e60fd3defd6caf578d116f874de0e433a0f7652c1203e29d7eb2eed60269247c864b2f2487b7fe51c463b1bac682822276d3ffd94084d036f8f33038f009b5f6b44a4dd7a74b1b71fe13a0795b777935c8123ad086232220b4bf3727628f3891a4b9c2f33dfd983cf7942bd08a25197e05a9e0bef01855a78ed49ceecf87110f59f0bb602104a4430690237edc0479e482f21ea2d2c85124a2c347c8db4587d19c63dc9d83a1d43025e6b5d45d9668cc9f43e17b0706c56563f367ad2af59d1f4a28c22b96a34765d7aa1ce5d97be0700dc84c62e16cdd8722fabf491aec4fe9c5529f051981756b756e2c08366bb00a59205fa7f28a483a304dac4aa582058224c06c7ca96dcbf898ce2cab643c83902bc127d22b76ade95cb4f425a371b7f051e0245656391acf873d08a471c2627dbf6e55e89f85cd663d4ac71f8ab693aa00825bc06ecaf9e3a9b107f909cc4fb258229a28862fddb556829005ea0df3cc6f95f2646abd960a9dcce0651bfcb5fe6d16393ceb6012c7142eb58a823f2dfcb06f5bc48074fb81f840847394fcfd6057122f0b61662e50b0303615b0f8c88a425f5027c8b62abbc90d7e833868f94cc1fefa5280849e3a648599546ba049c4a2d6656cea69c79d2ff91e29bc7901104e5c501dfce287097fdbc4908560262776261ba7f418b397c7009a37da578dac676d10114fd138ff6ed07b5820a1fdcc807d10da9595ef8e73500d165afa6a0d6cce335f6d40a22a964e5ad12508dd501fe78a76532eaf10e1fa4dd493b73fb0f29763b7adb2dccb74d5a7d4cd20daa62399b29063e59c7a0679ef2c454b21c40a00efb09076419fc6ec2d949b049c74d243c18a6f53479362472f47d19652dce2904fac146b81f7c53251797b6b20f616fb678e0a2fe2de25e059c7034f1be9d1239b678b2d4f827b828b319b549c37e467b33fb260ea7299d2f969bdd2137614297eb86126bb8f001f162ae4602c8046ed9b03e27a282a0b5bd2bf42093867d96c03c88902763ee2ff8903100c294d1043a983c3d308f27fc201c55b1d46a203cf8432c56894bda802b322eeb22ace54fbd72514295821d2eb8703d410488d6093ac0dfe64ad802bf391be33eeae26747dad35313c6e9616103ac00fc809f5085458f7c2c819719052c477418ac57d91cbe015cd24a4f97f2e001e61acefdcfe0910a8cc553c062cf77df057627554c87d683cdf949af06afc91a1cd93ff4ead84c37c04a70637facc0e080be22b4363799404305a9338b71f07f1f2ae55b9e1382db9d64871d8afd80798888edf3995778393642c51bf8816cc47ea4014da3623bbf32c8c2f9e2879580e79cbd8359d3736d0a93e57dfe2428646031f6dbc1739e713e828ed4a2582fa14b495f445ad0c31d78261d88f6a0cdfb82c2de0d94dcefd2af9579aa997bb18d5bf2eebe3adb373dca05c4f842b346d1e085400dd20d2ae1c4010085052109c1a75abb40befbe800ca8e2f05dcece4c41a5df386722154aed44e1c77a4df5b8b0e8ebfa7dad47c1b7316a08fcc9cb7421a59e7a960f708dc347c39990e77ef8a9296f6cf6469ec2f11852bbf1d28e11cc6f9b85dea18319e35c712a80dd2152307f79baf41e5dfd2934544a059880a228b00ac27a8c896249fd523ab7f056b20be270aa5935fa1c75e5cc6b68a741965607e631559573c056bde25e37f48b00e79d7691ab29a46f84d5873a7871a3fdebc2af4ec31fe7f72642591cf82330724f03875ffec741e39a8ca110d4adace4cfb929730cb4cd2bfc15d96b7f560af62dde0565175a7db7124ecfe39e3bf5cd39ee14ffe281f257f06afa1495c0be71e1a2a768fe2a6a4e08830ccadc965460b44e0f3487761072b71faed4266762bd9134211cb529810467995c3a8d7b38500d541b1a987a7730920f1920e2363abbc1a85c47035b5ce963dc379a5ef74c46d1b7bf259b673a46b876b90daff7b594eb80bc52848cf7c12fd3608b7427b7b7153898a48614475940944b900495837a3a6851a4c28209a8529a223410355bab766cb70249524ba88e5645ebbe070d8d55168bccb0cb29ba1ee89d15b8733549a455303d24224ba2c1c63d3d7c30231e57c7a16d386c1bd5e36f7b6e9f5ddc000bdaa962768ea5112264ea2331c2763dbb331fbc1f58d71910a5ed7e036e74ec0da2452846f370dea1f60323cb2b02cb3e00e205b0cea04d77fb8f2c0f7f4891e2f412820427257535386fa3b9bef4a8ae49aeceaaf69592447e8d4d9b13ddaa1205c32dda2ebed8c57c439e94e45c97bd0b0eaac37e25993584cba2f37804c3ea26cdff23b1fb27d7b80e13863a05a92edcfa4ad34957d6a35952e11c788bb7817c059ee526ce5dfc2877cbde26ad7f87a2b0c88c204698d52020e95fbac0ce5be65937c38f63f7aa57a889edb1b4f87aae5abf7beba73d7ef1b561abc0c7f398269ff0c5fc6877dd4d9d49419248e94913ede5d3ac2baed901263e9226c19128c395ce75c2323be581f5aa555fd8da4a143ce68c73489aa99d46a6426f212211fb3f7eebbe29d1500cd1e6fed370c79b6caf93efc7804410f3bc75ef3d9d908d756ed3a656784930e59ed7c1e033f48c8224917c2a0c12ef33eadac2027a73c2af78b211072aed4abf6bfaf194e7cfb0bebc2b9854d51f0f2c4e065ccf35619b6bb8b04088a5ab7a0f26a6c1e36c3bc9737d22209e5e17261727e7e738fd9366dc76084696a245464f63b9676f2342f42f067a7c1a39a8cb12ec16ec4018e5c36ff87b63510927d0a22ebf25b59e01c9afc4694fb47e712ee33d348cf9944329599ad985ca072b85db0ca1d4ec2ff582937b52f6e507faa79c640a5372fa035c455c5db18af4de3ac8f0bfdc89e0adf6673e1c0922b9e53be64ae61d6c86719c41ec156fa8da4483805a331a3cf1870adea90f44bf1dd90ed17a84be3284ca32fb3197d09c32d04cabb2f9db64fb404430efc5d39faf35b9ac975a22fb74f135efad9af6954ff2f83e85c32b9c2f6b29330976116c8876ca5cb306968d48c389a2e2224d985b450bfe3cb508640a1256215aad8e9962186b9160838c1706cf91143b5a6682ba595f133820501e19581886991b55936d06f0b4afe11072d03cd7127a1ab92a75d38ee607352b8d6e30d888d4cc94f79a355521f866f059f4ff51791b785a5b4b0e0742c808113213b79a8e4a2b560c36d35f28ef7f70bfed2a8b1463209ada2c36db004489e88214e685fd18edd3c58dda9db1a24887b8cd38409bfe73be89da7fc0cb7e5b87de1d95ac5f362bd644fb8101f46d2a6d05505d9f50caa3aeb58fba675ba167488a9b8d8ecafdd7819d62c0d34edef4c939c0b0328a1f2f2aa11222abf9f5f85b11cae4e64a557b98e1f252e9cfbd1b8dadf2ac4831aea87b8de707c08f69f4eca6c262ba2d45e05e1bfc1615391ad1b1d1f34d2c2398d0033b12124b39ba96a40b653d9c4c7774994af8cb46786b935122df494b75f4b194a45bb1bbcad6e1d8544016336e274abdbcc9c9b4926334fb1fd8ec9df13ca8a7fde3909197e5d21145ef504f5c9a29469604190206f00d77c539c2dbac6c26d0b429ab16b5ebe41bc3ea6a56c0d4a8ff9c1742c1f3b505be7b61a86b7ce4def2ac939e589a42d7de47f17f226a72249ba0998ef75eb64dd0cd74f19767e3305b5a7d5c3e1d353731a08e045c061a64014dd1b5b5fab414cd8c5906e978c8667af8ee2595b927abf7055f831c5008445122e3d56df6d1a1cf0a8ef8d35d7a7526f891f32aa5b4b9a620662634e76dab338b7cfd0b8ec72d179a2537df1b119357f21d6d2cb4b0748837c17cc6009d9d01cb11d50cfc5a60cc702f588ccf8d6df667f6fd979511c879993c4a75420b6840d628caf9ca372d563690a94646579e0c5b8e9118edc680666259f5eaa0a13f5d00255be324281faa76c54e73edfe17d1bfefb40e5e18784f1a9cafc758f004da379a0c3b1747ce2d3b0e90e33494612f30f6159be257b158f86c68391c01d27b12eb19402d3bbea839f089a4dc7baadc08073bba5a11fa77c7e4f428bcbdd410f9dd331e783c7ac21643f6def65cf289844423887b0378450fdd5dedfa793f8daa9239729520219fd545cc03eee18219813dacf7e6ac8c0e1b6db0ef4c3013169e789f6f81cdb463b26e266931e225811b2de1d8bc4dd5558a926b3c937528bcf60854191503768647dccdceb0fc108e09335ea7a2781f984153417052bfc8f1ebf1498f59a828b96b03602584886d17747b253392a179c40d5f9ebb94f0e307c3bf2ebcd555c8e687dbd16fd44e51ecfe518d299cc8752e39a7e35742071d84ca44ef3978da9987a58e54facfe12214db7d62ae7c8598df8da86dde2fc934b6df311b30b03f97dad3f796ec3e4411ec9f7878182463d2791cb8d11bb95e63a5f8961731b7bedb7c429f2ebbb2b9f130f0eea007dcc170011529cb23ef7666aaeb8d52cbd5f8d50233b9782b3d6e8f9ebe0d698ae4625336d2219d13b836570d8bdd9eb17dfeb2e6e04f59c063164b3cd08ffe449bd489162f0aedef4c32bd754a5e02c804719f503c517ac736e20d80d46973ae175c43bfa8be5d4273c3f220177ebc4c42d361d1ce14c03af8db99b04c82d2aaafc48939d2b4853abc9964b4e739122d7ae11f6524de0a5a1091edd71b9d0511b54efd84e810402aeee4507ba99d736a7526549bc4ce7bfcd18efea4ee2f0c0eae650070dada7ce2dcd0548433d0f8ddc409fca79d7046458504cc770261d1b5dc5ed31fa2002ac0a47a4f1d240a318649e310d1be2d85aa87d8a162b25ed8372c08a382c641d3d73ab65b055af578d1862a173af2f89fc72b35ed2de9500ff10f242e7c4869935582b1f28a3a1393d6c9542c44b66191e5691e668d050303b8ec1a6fb02eec751fac163ee8920873cf2a50239dbaab679345f98e1a57a7c4121d2858fa634a64b01b417d4db7bb2923dcc6fc04ad1a4a7c7394b844240daae6e99407ee2dccf72e47fcfe9b0b4376b9c1f2f32b7f928a34aebbdc2be197e24974146fc86f148a482487c7ce31d7e5cf1c6f38ebc6a6c849df9b7fb7306d82db680b79fdbfd82e352deac044175ee246724b85876a3d7c68fffb342b2e265a443d9f345edfd593d36508110303c0f4d15c352cc13e26f288233fb057d87aa837419cedcb5c2f17cab754acbeacc326587b1946d3c30fb1c39bce08177dbeb7b83e0e9eb8d85893f2a10d4501ece892ba34b96ca41bfc0635c7585cf28ee2d47a503eefbccc8354c1ef84c1d2a62d7ca79be7d8771590df686de7b485ae355d4f75144905a12be611567f56cd34bb34dc3b1d3a28ba6995804e1171acf83d494f9af3c15c73d20ba0ccdc4c6faad2ae1c0822605577c82c1de41384ac581fad3b828f5ac322785bd5f7959e199ec31f5a8b27a8c30de18eb006f77e361857f3d7b1afd16b263347a4c9f4bb6633c0a2dae831f638627dbb94ae76c9b7479ec6da3a4a9fe283f4a2a7876ce4b2a78913055260c67ccc8ceab15522b896b3a3ad0ce3690e678ab3ca995df37e67957defb4e6c5f5f40bd63e3bf330018a6ee6693aa0df099b238c6526729e3c70a0bddf299e80ed19ba733e730c23c771448e4fc1354ba1c223926df9d6a06bedc7368a5e6d3b6e593b634a8182b9470ef54e934b93149c4f85277ea56584d8be090a004baf0490271ee465f9266e5abae7d1c94694cd6da2ded1ae1b42ec569267d6017efef78917698d289b06d9bbf1a74952c517bcc680068e9637b4960b30207d38e987d1756bbb10ecee1aa8f02718217c7780b0c8dc9e1eb3d0f0035507dde1368efe356320e562bb2a1961c37fecab16dd53479d0e48d6bd5d569fde6c4386d45ef73918811817e228125b647a80928e9529702f536bcb2c8739a91c5bede15c8d62e8a5f4a9bcc201bb34409fbca09efc9a736b51add8ae8d19dc623c448734bec3ea5bca99192dfcda9569464004dae50f4fd7bb7ddca984645f7b64b3e6b3a5c6b8d27ccd994a350ea92bbced803dd9bc0043bd1d1d12abf3e256bcd27ef63060a268777fa9b3bca24087ea11f40a29e93f738124a44831f480081c21a2b53d0388a8d096548d8a198a9d5d95c62b964893087bb21c6cf3449572fd186ab2bc941dfb4b0092cf32a27f43babaf618712d78511b98618023f251aa604d0112c3dcc8a510a4e9340368742370823cd78213976a2ad4d8e1a9d84b3d0dd342c31cb28ce76ce7f5d33e1c38f8b2261bce6a74106d7483d63c3f88fc0d80624f8b3942ac5ec72ba9428950c7482744057446eb45f1e1cd1b3e7a436d6ab68a080e8096820d8fd9cfe0ae6c871cbd57c144893fe2e892013ad957884a454cf1d9655036f38a5bb512431e8ed7c34c61c784f86577450b557cff6691508cd9794558b898823fe3737d56e517154ef2fc5ed95be58d10a6e745faef16022436aee6233fb7d60b68239459baf748009ecaac530ad5410f85577bad3227dfd502bb1ce008e961c8060c3cbc518349e0bc50fbed814c8c5e4b0b1c68bed0c31ea5966b00c7c9f38b0de022afc01568eb612d6b6a6a51fa7fc6512d312c5ab968b6f66b9dc09ea9026221e3d8612405e8471eaf3bd033c457725f541b29870386860031077221cbf5c13b48f7374ff4ef83be322ea49841630ed1cb1b055d68c44536d46706ce570167d09725683e209304ff1efe09414a5a5f5260c3e8f2d7db009906e3e727a9306a8d7d7a7e5660c20ce2fcd01a39d3bc8b0925c2146ff35e1cc6c233f38a53270fa91ac6624065c343919e058f387adf81caccc9552d030b0396d650e193a1f6e803e917c171defd6984aa4a8094f14093729f697a6a793492b0ea485ec70acc570d94276f78a7b2903572d3e9178c7347da900934aaea4e41774f4eddf6a9acec799f923c1bf5bd9b3a57bc1d64b63b62af9524c7f37f224163a881119a0e908f414ee36f90425f22e2d0e78560763b5b822b2d47da38e2bc163fcb40274e833019ba4ba7c7305b2c085f4b7d5609e15d7fbdefc7aef9a8f0a02b7699d603c3414a8bc11f9e8a4b28dc0a14005f389294d003ed9c2a3180077c08a8e06d281322b162a5ec1d79bd3f957a9112d54c6117f849ac49be8ed55f6590160e7437417cc27d94e198eb9b7ba807106fb195618929a03a11c2dfbd9d6ce27e19bf363c885b0d4ad473e382145a7ba3159072c9703d6d5498ea34fa14eda0d50a79fed5062d5acdeac0a2420ac44464e0ed9410af515cf46b924fe2368fe1ac53883502376de37e1e8acd4947f1d18a15f9b83a726cdc58c2f5873480b384d14d3c84aaad74341babe1dace7c2662c60dbd6e4e27b3e41484a346750805dc564fa4bdc35d269e5e05103901887111ac6e122ead54721299ac23ab2ea3ef654510e8489fc1eeb5767375641c1914992d21a94176d60b732f31827356c832d403b29b9206920d78face0638e4c3fbea721d45a59e86332069d447d8f1489bc15f55918ee837d837c16c9e65ee1cd6b5d4d794dc0697bff94ea3dc2791b1647886c9030392297c498954865c6cefbdd0788ea7555730ae826ec52564981ff0222b04a6d5f91a32fa01462e80a1e37d1a1bc237b64db9c4bbdcd2264fc61d67475023731e2f4aecb4e4d31527070c83b12f1355a7ab60eb35bad37879982994e211406035eed7cddf7fb3fa18625a94e6d9f066141e7a7589920cee1a4ea96a4685d5fc05707d03b6d6e96d799742ee10c75fb97cd48e2bd14879406656ad5322dfd993ee154cc289a653ccd4b80c25eb66c3252257c80b15e20ab651ae4d55cedf41af565a07b402b6cd825ae065765c9344f5878055a22210fcbe548918fb3bf6c8b7d99f61db9d3219aa8fbc949fbbd0f6ad2d2566e2002a5977c04ba49a62eb9542d9c1e615418275bbffd856f37b549223b6016fd82edb86a0db4d69bd461d289f16237b11a3f2415c8b5a4a8555b4b01990b352b0205411b65ef2058bbd498e864a8cf03a57aa45c15323f301db16adcbf70498438d9e1dbfb31d26500656585f8246957b7e1f2707bec7b28994aa4393f81c4d10e412bd8b69efc7f1369a1cbadc6297437c2ce4d70ef25c4ef6da26b2d6eb55e9bb3dda11b7152a038c0b1b4e12579e9247f83c8a8528d20b4e5322ae1d99bd5ad847081089bf97bdf46fc4d41b7e7f20232f48272b3de5809809fdbe7eca7098675552c5eff02f4aba9acba941c2f0092cacedd2e6725ec4808222621a70d2d86eca0e3244e1532572464dc61f91856be389b124c3e3fdeb3c4f85d27bd143e56be51ba988b9b109f1d06f13fdd28260550009bb170487ab053fa31c8e61a3db54c015f6dddbb43f529106b4ffbd454ba69632c0e73c217650983b31e3cdb50ff1737ad192cf526c521505a331e91dc550a05a79edbe16cbe347455e7ebdda65a6f14a6f6f6eb905c7aa21688ff6fd47681b78aad246f12d2834867ebba6805d2ebcf5813d82e555a565a704962f226b5c866652708738ac267cbe10c8c4df881419c815b28630c0951528e29fe608615170f854a33d41567257b114f89522907f6329da7755897378f04faaca548296abe7c7d509d0bd7c14137e62155dbcf8f260459c02d74ba98b8c908907e920e5135caef74b1ee93ca54df87756a68be4c8a8725a769ff8126f44934b24b6ebf3062e7549021716893fe6e535d6b67eec3083cc142cceaf01bea4e09ad7fa0b0056b5475ac2d4e7fd3193b2ad55174a1c6cee18407572890f93429ba23936795c0883db8709be653d28f0da03ba2ac9d343a99e43227d245ea49ddaeed787c33b30b1973751862f7aeb44ac75b6404725f21a2ee8e9b13f5176cdf9494527347e18354a98c83f8271db1c83ab03eaed965e6770815a9912b59ba20b3e4ed1c64baa28205859602761a2ddfe53857c35bb46a881c6baa18936e2c31145c44281b7c705ff417ea94bfe8ca448627257fed3207429a54de9be0eae64e864ade2c28738d12dfb8ee8ee4091cc9abeb7088c409e5aa932c299a010c67cd61c521a607fb6dc170750ed0683567c51894d10157b5e17390a34de85ddb9464dc831c7c8bbefb07a49bf8e4d55442a3a9e135463f5a66e41d659442c2bd2d369c0adf14649d90d27ee504dda10fa404372c6a07158fc1e60c1b0eae4da4640fb1a515a3bc3b024bff4e0e0df4b604ef5fcfd7e3f33590c202f90df1bccb690dc4d00335e525ee901292f07cd99af027f09486bf1d508e560bc373212d816098a8b691dabaddb86ecb2d0f380f5520fcbf1d97b2331f83befbc19454581a84d898808be30bdc7f654084900d1f9adfccf28f97073ac641d70760d5d9dc9cbe8fcbfdbc091c30301b0a06edf42c80a82c202f1b3d3b59ea3d3d1c20af6d52caa66deb8e0a5700275787eac313e346cf63df4eb91156bcad3ec4ed048a9fb3665a5cbd8441cd8573ad114c1b9c2d72b23e5550f0e2afab6c5ed9e330dda498082a0baaf6cceec1bd75520863c64f5b0e50a4e04757450f09621c958aad3b75ae767a2007849f99483344dbb63f3231e1b93939f7dc0dda6612e824095c87e263512a7dd4f7a4229b3dd493f75a72170d7587afa257364c25e1c27f3029ee6929728baca368e16e581b6c0b320a9c2b2dcd660fc99c16f6bd9ebcbf243a58562864fa5d0c9211101256c7afc6c036da2b1f26b897f3d3a7d96e5dd440b07fefb483cceceee3a5762debcee8e95ecb7467631f689bd2f1a8e9ae33ba3752faab98cc394d777d5313b7f4af0bf8361de743752c44db761a94b83e133236cffb4aa594d85ff2d27299e831d446fcab59a313adf57d11d30caa7ca96926fa91653c2a334f40683df55681dc32a3bd5d65f577668fd9aab861373ce75aafefd5157fc65ea35d10afcff77306373ddad5d768fb82ac9f8ee741a3dd5d4dce3aea870bac68d99800fb28ba5cce1179d3a19756584b154fef5e086edac9a7a9493c275da0f60eac58e6f2f72c1cda9635917c0a6307efbf178fda23d793ab70dac3ca025f42db233db457f00c18b1e0d7d111e05a3fbdce3caebba17e8a862c611348fd6f757d547ad736ddb1b10cdaa6e93c6e7ad79b3f95a71924e6773ace80b6695d8d4f9bb17cdc74d79ad68d9556e48ba944dfcdb7f5799786bd38e90487d69043fb3e0168af4899af8839eec01d4a8bfec7c0b6951c1e1f683adb94631c8db9420ff8963d6a7637d7c90b71187d9107914a42dc55affb84e92e20c3fec67a459d6c276abdc3549325b985a0e7f747c34522e5cb5cc169575e8ab1d2f6aceb5efe2addc269a97dff02c102efdc057744e3ec89ff5638cd10a69ccdfa9b0ab19c0d0015b7b6ff36b22b1b30cf9e13923f63a86a72c6ea736a06267395f93781c1f7e66ebd3ef8ece3ead7a94dde93a7a9866413235c808f28ad1be6c18a461434c0fe7c5f8d54a4036fa7ab426b92a1d7d9572115a9c0f1d81f68a81ac9cedcb325cd06245223f6d7db385eaa2e93e5a05d9940364d671cc736edcc6348b6b77a13e85ccefff4785518452836eeea8f4e995ee564e2ce64b7250da878897f9e696ececb440ea6969ff664834c4d82e922c3c8b7f655cd25be29ffb92b3ddd6bf394f72c7805ed958520493411a51068efe29dc4bd323ed7e44a9b1bdc791bcd44376b3e400ab5affc235c3151f49f4e09e691e1a0a0c7975b750a3f41ce0e2370ae3686ab520fbf6a50a3655b58d2ed3e35b7aec02a02d841c90263e08d0036a5ac419d9325310a6b53cdc00de1da6ae52873e6db704033168e94b81631e41a50166c1ee95e71d925fb33d500237b1006e3d3685e18bbc6e6ec485812590b4eb9725c1c690f853e81c352a5505aeddbe3bcb41d67129be061cd9d9036839f8c1c111b3bb15693ba448af104d669031e61483d8b6bb1eaea46245bb11babb936dcd721d64f1cd0cca0562634cf7e4dccdda217ce4c95a2f915145bc29b1c83a0115428c945bd1d087f4e08a1081a39766a60a9f860b093117d2a654c2cc5182880931ad5f136423342bc69db47ec04a15c4102b76678aea5a8a889ac4775596e68f7b1e906147183f15db84e984d95ebba136ff45340a45ebc3118c80c5ddca396c91b0112a41c3dbcae110b52850e9ca603dac4ca981adf496d035ab2023826dc88d2ae2069261803ff34da3478d4439ad6fc53cef98c77718198b4ce237e68d14d6e18ac42a1416ecf9d40d9698a1cb48ddb57a0fca2cff475d3841ade192fb9f27a507da9d90988af1c1f32341e32c0e2c05e7da06519bc160fd0a20b94693d889b00b3040741010ad39d0f74e074c48e1cccc3664547e34ff39caabdf79bf3c5277216e3d4ef07a8313510df07661e72334165af5cc26956aea36a76b0b15d380ba68d0daa9b8aa95ea218a64f0dfadf9e62a3710c8f8cf520475644e868c5afc99d5f533e8a7162bdff50afa1f56c5a978ee9bf4a0da4369ffe737efb4037fcb53324002b9b7073468f9c9352a7364d168a5afd6bf0194d097b4561fd2ff81574cc8676643f6c7e91f702c85f4a029dc304afdc62a4ec68e3dc2a12bc2f0593fa5d7db44b216b45877a4a2d0d7b11c48c6235f363c2ce0f21a11886c4344dedb6561415ed08eb4395c24d56ea68534f59c018552c069c706a1203a647504815b3c0ee945729b78f3d678868eeb77c4490ead99a3722455b7576e65961dcd23043c252ceee5e0169625ac5808053b112e20b0e2d15cf6b55213d8ab903a0d504e722ee5d3a1a7bc4d491bf1499cd5f5eb171a8146462898432b63c17fa648c8bae26bd13e8481c01b84d3032ec567573da398dfd491420f8ad89c476ea0b0e04bbe050f32078d8c06cae99b0d65e9c93ec2b7ea1fb8b767f5d312de7dd3d6bf3a8458d4fb380e3d4558f8a2d5b9948d529632863ff3e7dcc8ea28c1939b9f4509e471ee1a0a85a07e496a9ad43e4ded6785dc43d36471eaa085dd9feb2ff5194176d502af96e054d46e564702731b2a82f387a1f51553b0a64492ad26aef1fd07b22252877740331f026cb67c503b7edc53cb5b319b160328394cb53217de0c9ec378b70b7392fa5f48084384fbe415911a49a463cd17d0bc9c7e3f0fcb832e7a65cca4f8cf2c1cbbba7d2eadf0deaa157518127c1957a97a3f54d7cba1439945b81a403da2232763a3e6f44a10311bb2afca59c3ba3c6f8145271da22a0d84025f8777eeafc5f98e36fd0c1a62f8fdbe2b1ec3839af7470be84ead429c1aad7be9b3aa40a454727e1d8f36d098409bd2212d9b7b70ae258edbc9da595bd91f57e4a4f73211c574a81a79980d68ab354b7506c37c70515ccc647d0275d917a2c99ad264273e780868605563ac2520de5bbb7e83f998bdc82642f0fd0c81e8f6f8354fc30a526c77c9bc033f648c9f51b4ee8572b4c2a6059d3bea96c6ec8fbbef943e33a8b8393596a48dc2f3574f2b84fbc735d51cee8c7380fe0235b2fd9bcf18225096e7f391b43a705a61f1cd23cf8dc30c08119b5102c14a525fe835539e4c91c1694fb13f85532fcc4c26ba70f3681f2b0047c378aa29906bea029e33700b4f75d0c2d57dd0308131aed3958b3f1b04566685822947e31a1c0c521f20ff473f6b4b41acd5ce3e2ced0e4388e7c3a3fca49b227d24315034f851a23b19c2702d95ba574b7bfa6b6f7e2a387e43ab4b4b735b03d7061a0083047c9ccb51d4974c37b17568123343e9cb3f78038476a32ff1d2929e8ca57bf0679d57e359cf43bd4ebf22541bf8474e904465204a4af6723ab53df393ff0626ee240dedead2fc64f2346b7cbc8511de14e2dc7e9339944b98f82b710b9585133688588e63e88921d6825f04a00e07e931c4c610de5dc42a68c87e15c46847ae4e832dd4f487fe3eb0bba9d203ec6d70ee8c09a49d15b5b09b74481d924a09c55552e605d591fd530afc70ad80098b1f73be4e2c1ec2ac84378e6ae29fb011c686fbb683e669bf3313b375d74f4d9c334b2931ea89b79e69c1bcb1e59a3243ac99b6ce0e9f3bc7f51574b0a2336c0e1bd5b7c38133b641b2faea0a17972c613e308f60c60f1ebf037db9a3f87ae0349b43f463312d59442bc5b15832f457292a72a0095cda79dd13590c60b9747fe400beb1cb1422c12d1bc2c1ec30e9388ad220948074708568b47996c4a3f6fe18c168f35e1877bda4caf9f9ebb1ad6f39a93e8af9cc652202d35cdcc7f4d738252901661639ec3cc07c31b1cd417c03c6a9f1df933dbb68cb34da6e10cdc68480b7a23ef02bf4d1c60dfe14a5ec9a18c124c07723b7ccf1aa698d52e4dfd625a29310156dc5cc2c76e55a9973e6c029f5963294c858aec6fb8f7c1249a9b5fdcf3ff2b68a8b008491b5e2baefaec2c048eb339f733aa4c9aa8b0e9a229ff163699d4f2a5315f2f04d9cfa090190f6088201927f4b3283899d40e02889e829f3e0c4c25bf27b8b985d59d333da67a7b710bcb92bcdc08c43e228943bfb54a5d04244e0aaeb98fa126fa1eb8db2356e5050d40166bf558fc5a5dd7bad7a757cd56f88b821a0b6f2878031c032182f2b9d558398fca602a41e200c626071d70bffc45b9e6cca51010004304a0aa504edee7b1ba2af055c38b0f0ddf12b0f99748267a9aea415d6296af4c16d61bbe9f881583dd328fe9a3253c67f65d084bc41738010c6df26815464722a9518084daad44115ead00c7c7090ea552248678e015a06a748e560a2e3fb2ab8814a15b3c1cb680f322bbca546382183bad519e46045ba41a9b2a510f0c4d0a8099fe3908565c80f4a4327841cb084b6fa2f3892282ee7b5ba44f2475f486215b4d9aaaa172ecb026b630978c13139e90649175cce4bd01798aa80d591e4e2e39150e52f4dff2daa941a78f075a0ce5623ed3f79605a6ce5454f0033b334b117dfabdf554871061f59514ebee049abad06ecfd4e08aa715f29a83b44062349c7048abe1bffedae15c974f408b94b6ba7879e4e713fdfa182f13d0237ee2465ea7077ed903002d38d0763a50b3bbe3e1738f2551e619675b59d9be2df2590a96f71e6e30ab547fbc496fb8d41b1bf7f82249d436431240d9ea48e76e24de09f804e35bf7bd1e5a94cc4ad35c5c2c6ca074552671e69c1cdedb278b770047b3959dc29ca091699ecfa61594340fdef3690588a2a5c1ea5b81d982ef24cd1ce317adccd859914f2f935002e82325721b223c4a60ee04680f45b9de2391930e24ecdc21054f7363fdf43e7cf5d3fce74be188b9b656238015755dc7f46a522659e2e4a385981fcdef9d971cf197399e8e4c89f26c8ac364478e5c606a4cee478d8e6097654986acd1dc8ccc50b43fa7c1855de47e6a1c697e4e8beb5d17cb4a066f3e116b489df8d7c442a0b2b818727121fa7517d1509567737f3e5a486b7210c8c239d30be00fe17a09efb66297b1dc6eb620f6965096794cf0854a14f5797a56c08c6130fddfba155ff0fdfbced8ea119d66b9a6acbd3ece6415334daf99280998af37e74e21d4f92e5d84b9f98d2903880055f5fea2b285a6e915ac8c5dc7e52f0b249e5d6195fa8d029382b3dfba3f81ca7ed08ea9a7cae3282abbbb30901bbe26de459d51146316f763290652a8a5681b4199ffc1de357caef374d535ca3d3c7fdaa4ff149afc4deabdebcab051cc9edd56eeedfe975e8cf28c397648fb4bd5f1d6142888764fee33693fb8061a5c26bac75d72d4ee1c0d700dba6d13c9946e2be873bc9ca0976edf8b3576a90eb43b1ccf6483aadcfe3f33aaf5abcfed50cbf62402bdae47ae2fb6e83f070c89d0117bc9964a53badf25ce9e531a53e03ba09062584c50f8e74f4b9f3afd26838a0bafcb13cfcccc8cc0b6bad8fd90a12d6194c94c49836ee13b0953d28c43d581b6e546de9760c02363a0f191e257fc106e934a937f57acb0c87e7a78f55efcde2263f9ff41f9a0aa871f2d781eb396924c4b2e2923c4a779382c9316eac7cca31817f1e801ff423e6ea97654289732447c52202f2a646129cf9c436d8da74cc3ba3ff4dadcc5a466fe71d36736091e943be5c025c8444ee496b9d015186184960b2da4529001b2290b86b3a039fb18dfbefff9e8472709aceeb1130c09baf48be881c984c1c1435f0ed9e63085804947fb07768b8ef55a66bac4df6d28940f037cf63f5248582268ed164c0976bfc12295cf13e2ad10f186279551308fea900c8760e7f90877a0a170fea09402e3f66aa37abe8522f54038da520499ad0f59128504bffe5030d4049973cdcd07a127ae5523d6ff0400a220a5f4712b75234c2f5ad40a7f3598e61f19453bbb8f795a2636869b2d7e4c03254b8260879e4431299970f73d9069fd0f86b541809c78382a9d462fd237e57d175631caed1b4b479b4409a74dc5148ad57efad8f052b867ffb347a96bd694bb8ebc03933a5aefa8f1758b65c24c4822de363776e268a6eb38bb2bf40a1edd459760669c26a231886b8df9dca3c11cc2f1c16830da04c16c00eaad2b24f33adda369fb911ad352d597285da1a5e5aa20695d9fdb6d3a2b72852e1e823210e28ad60d6aa313aeb9ed5531d20491868820b3395aba57ad3d676311a1787fa6f5c1681229bf4c881e85f213274bf8633146df8a734abbec719215f36f48a800f7776ba10577ddb0a8b7931d89124de071f6bb51e541a5ee8101bf2bcf8b3efac10ca2a18c5f2458dcf0bafd43d1406e488ccac7b3d44a0bc7c771b45892b408536b881c8e85db5ab2b1377dcbc9a1a87beb8ceee2df332c2f299c1cd15d84481cde222ed80ec0ac19e938cc178ed6e2b6a31b11e832900e7b378f6ae5308a92d9bd4e4ba44ff5759d3b66edf4af3836bacc4f7f40ed7a4a1c6eaa365e8c855af0b50112e7b84e22964f94bbe48955899ae2f076dde57ab5f24364fd44b003ea661909756d4e6e4f5bae9f4ff50e3e54f198a11e57b50039f9913b16e65132f69cd87731eb897a5df46d3c0e99ba431c7374f1888bbf723e4242471d9890cf7bf8b8af051ee9243949f9cdd70c07651513c4d5109c87019a226e3b71a5f039aeb27633195b3f7ceffbb6a0e9a08f9105d9ce32728fe9c3cb440b6e275bad11ef5e653eacf376774a576affeea98e7f320bdfb149d1b7c0d56f436f3c0ef280c53e1e9e803a1d9939956397d5cea3f73f716075bea52fe50780dfc237baba01d050b2e6eb0e9d896b4e90e06d2317bbe08a7cafc4a39daaaa421b1df71ed9a74db41aae9c6af0c6c1ee12ea63887aa60defa82c591e991c61cdc8753d47dd09287de0d1df24b9e3960bd70bc3c36155229c9494a2fa338d489d32a34e02a9a2d679b72097678f9d0c9c6ae53abf87657eedd0fe9a956d27594e0c8fec1fda4b77bd701db54bcce94db13b7b2bd77fbc516e06d305fc73c2bc5b691676bb64ee757043593d06e9ad8a907f88cc96bd858ec0a8d49faec91277afafbaca2e35e6067052e93dabc5bf2f36f74748970bba5cc63de808cf04d7360dc382f79742ae5ad81cab9970394f9ce7525dc3dbd54a603f47821459215b1331d4e7227d775287f9baeb6ee79d6b1da4663705fd0eb156ea6d9b15596899a8f19d5c8047d15368b7bc674de40a6193b3daeef44d0821c9e2330e961d046b9121a1b040de22d8aae2ffa247028858f9022dc9614e419845e961c880d632ff5a02cae336014503012da8bead62e574809cda7001c9cde1e69e31403f80b2d7be052a2983d87724dfcc3e477e83259e275c4ba1f07d498c6b0601bf36559df1bd7188543fd8c1917afad596c1f20c9a0c7bf4b5c64234d90cf223f9d08602012c3c1c64399a42498bd5c91c58e48fd760051ce14ee9f5fd2252600fbce4f3e0803c00303a0fca21ac74d1fd46de1ad442a3883cbffb2d272b5e5fb1e6073788f1f9b1bcb93655a2a05b1da50ab9a26f1904a0d0c84e85c5ae9dd66ad741ff4d183cdb5eb8fabd686f2db866c1b404a85ddd50444ac33dbb944418a29564da730558a1769be7b8d2f967b55c42b08ba02eed8249ee5743b7f41f49cead0d8c94ea15d8b2d7ec994933f602e254db65927ef6e8d63a3eaffcd603db8ad3c7b32a8d7128094f750a07a222ae1d3398b9f6021385dafeae8033af5dd20cb0ded8bc16d16068546291cc01dbec1a4125f2d9f0baf6b22341d039dc5d83455985f25489b2b4d7176e85a1bce6fd195e10981294b5066cb39c2944955e8fb49ca003b6cc47946e77e6f60e959f9dc34802313e5b6b0bbddc4158e43a81764300d30f047aa9d5dfeaf8111e6e30e896870f854a486be804e4b420807c44962ac480a7e83add53b1c2b940a47ab3c6fd88481fabc580d68d52c056bf1daf20419dc066b5a0706fafc0c99e0adede24ad0b9b587781428bd34b43b648e7a9bda3c7143af77103478e447004e139147d724b09e6316c3fed4e847a4e3b2ecd5d768f9aceeeb54708900fcbc9493dcc9936a5dc053fd044478376737a08acbacf141177320c10340f67b42eab2e6185b4bb04935c970be2d5a41d308e3fbb4028a78703327307447073be2d237e6c42e79dbd3611d00a288d23606034db9b76986459ca179f079d55b7467fd56e0896d6edeb6dfa4d49f13cc97cd34b63350b395e085b44e9f534f6d882695fbcdec75917c21dc5ee616255ca1b89d58954338c8de937781e0c7d5448e6f5a311b564f323825b3257f0567cfbb2acb98aa85812041aff47fe7bd4910e6f9b688ffc3676fe5614e1c1e08dbe091a4f83703706f14e8bf9786b71a521a16811b33aa78f20dbf8079ef71eae7b57396e25d80b0fd1ff09b6da83ed69dae704aa9855475497e2b40709452c50a2580ec27797f35f7d947812f551925418f6186812047bf0ef012dc505c4c56336c4e44db1c4a9a34e14eca06ecb131951fd700820f838e3adcb9456ec426f15c07a074f32ca56e39ee3d3591d76301fb6c6b29822fd6f3c01f60f55a5ec7c8becf66d2e7947d73cc167275f744ae055df1409aa33f79a3346c67559ad131dae6dcecff332f5fa1808682c22a4a635de0dc1516e256011e678f48f8fd460ef2713428f52a7fb145fd111eacb0ce0c0da3348df8f4f862e765e46d7702711ef4a4f7459c4612dccb9ae93e890330673a1e87ce789b8ba933d42b5577b90b9ef3cb32931c54aba3b4ae7aac45bfabcd4deea89e9c8bdb573b14881839e13643dfdfcd931a904647de520d2231b27dc54b6b341b2e1c79621094b4b964d44ad6993fdace4a7363eea1948a0f44a4f8166938d3db81bf4d1165900916e4ab7b8f918ba70f52d6c0c340890c194c870539e6f5a32b4872ac34ab354624cead4dda695f40fea2d931831ca4eee969d2e1b9e580379a6274fe517a3714da01ba642bf40d23563d0f4ccdfebcccb9a65c262bfe778d8fd88b5d3f221dd40a725feb36ec8b57f3ca92f4647d35ee74e22de8dc5d06a97a7c7c62228df1d1f41ea9e76e6b4d89a09226d261248fb9fae2cbaef719bc65509e7963a3b2ddd1b9893411aeb258c18e3f4d41d1e8270e23b243df059cc768f9c6ed3b82ae186d45219d013004610079ac99b6562acfad44d18b4f52a0b793f3d8bc505d736a418f1ec5e30b5d290f6044e62bd46c4497b0df7c4fbe354faad4d1b32f6980d45f12993246d7aa44b2632f1a4675e22bac209fd62814ea2345c27ccfd3eeb4e29fd30a7a701a98f64216343d369863212eb4363a98498d4c97d01c867c8fd4a2a1eae15a1054265216f9f89d3491b48b925140764e7028a18e26a5c4090b24a07adf8df33112e31a78274c0f56c902e491953524573f29b067e58011ae4a369d0a80f0497eb645fd80e747c0d73c5d6ff2c6a0ff8822920e6a8208599e02f666c9c3fa124ca2563e000d58edf24217befbda59452ca946495065c06e506f657b0d65a0c65c55030ec457360b5e25f9bba9ff29ee5b558ad489dd27e090d3179d20e6574773742838bfbb514a6891700044929ba2007e407028e280e2fe7e25c3e2fc3bb389729378cb89f6bd1c204c63674034829a53e5a524ab98d90da21c618638c9d9b368d4bd45a8d86780e6cd204071e9e34597aa5b0dcaf972c6132c4c885fbac0d7a36e5b54618e22e3fd0f21c5865adb34ee9a325a5942d1faea365f02ea53a1f2d10a057800354295f051dbd937d20c04180383d42b02d1044a81148f654ca763c8ca42e86b56c954d8726525dc81fa2703eb4773353a7b469bbbb3bf4a8378d9bc786535a9aa51494d2999259b97cddc049c13962db72514e38f17ed24f9aa8a5f45157c1d24f7a4a5369a44ea2d39d7421ea7ed274bf66125dcda4993449a2ae44a92bb94e3c5ae2d21495442157f22e3e467cc5a038e45370f0dc045e94210c727c9764c0223b6390418e41f9abc99f2bb912539463ec70bfd924c79f4d8c5c0a0eb087e7f9643219813dac9108c09ca6017af1b930e16602717498b05646600f7b2473a8b2565aa9bcf1b8a0935249e5f4d19a924a4a29b5967eb4cf8292e12e37cb7c1b62e2724ed9ad7ea1c0021326433d14d4d4f4b5a0ed970951a79f401b7f4c44d453fa9553f8917b29cba32c51c8ed55beaa9427b007a594ce18638c3176d090934fd97c1f641861077514006d22c526409cf83aaadc8ff553ba377d52055704abc85fe78fc5823893096bb509c17e397d49ae1ce2a57d07d4f3583e80f464c9e21d5549525acae2395f7b91636769283407cafd1a0b1625da5d5eb854524aa59452ba7764a2944e3a870089363923c896625625abe23958b2a4ac4a16024f0030b85f4625a39221c56c698b774b10059c52ce1b8fce3a67f5583b84ceb3569e39dd638c5718a55644600f974c521a49e213b404a909d34b6888099d9f3702d29c736a2c2225400bc5b502c2d752480c9153a90f2cb491f62190a863244626a4d4e70023dd7c4ba711262626a01764ffd96f8489493ec46907741f0a596744600f9746968c2860a0935249e59c73cecd5aec356cbe95944a39df4a2a27b5f2b169316abbafeb9cb56659b5c18d17db9b0b8249f9a273524a297dbd86b8e06a6031d56dc88bd9ddd3a77c49306e28a5934e29a5cc52199030ee855eb76f496eed6cf48fac2cdb865649397efdefc67b45d4be225a1979ceb792c14a871591bb3b248a2c182277652f4df98331ca3a18a2682302cc2b4bfcc1a3f61551b4f7a63618a21c5d58d7410f84ac88a2cef6764594631320d3d7226cb7dd07142b5e4d0ce566f7ef5d25d12c986ee69c74521f2de8d528e27eada3ae690775788eecab05e4c60df8ce015977b1d4a3d05568ab0fa137a59c73ce99794081e2ce778e84e830ba409bd0c6b12c2b741a7cc01e934a4a279dd247cb8701f207910c106d84104224165201e04ebf007548dc4a20ce8d231fb0c7dc86e60e4d16b6c750eb6b4e31438dc310582c88e359bee417f6a6de83fed0e6c777ab7c492d3548ada0e7b17c00e9c9e21d5549525ad2925590f9f3bc55119e33e146d0432ea76713bdce0ee0f6804464f801803a40667d2f5a1b530ec85d9448d45a0b7bf85075e2db565393815c715dba6043424a79838d8a76d8304c29e79c736e2d8872ced930787737a538d88a0670e3710f4d883a27b874539915b178ea86401b87ad2316f42c0b26653924b7c42924794db34e8190c10e4d4ae99c73ce2dab107a18b598c56d60cea969b469523ae79cd35f99ca6e9f4f70313ce1017bb8e7b17c00e911cae21d5549525a125ae26448484a29251625f2e72991a111466458008f00508701d026beb70088838beed74a3c25413c600fcff2c995a1591f565aabac92fa68514a69aa88522b21004770afd65abf521eeea574c0b00df6702eb474ad9556eab8b3581cdddcf0f83182093a2cdd5049c292458bd4c173a4bcc0480729a5bcc93728a5fbdd3411770316595049e9a453de78524a691fd6409bf9a99c020b3239059f2c25a494b6ffc43f33d0904a4a279d128b49b9155b2d5d76993d279db46fbcee6ed8ee274ce97f2d1f9e1367f794b3fb3c723cfd5a9431ce1ab496a528abc82a58a848aad2425905c9724f67e87e4da5a934522f49211243f849f9ea00a4944e3a7db466ad78042028f0b621a9c4ec39e594eddedddd5028a4325af1a0e7bf7d2be4f6b1f22555703b2c4bfcf292ddfbc21e313fa949b5065183a8543ce7ab32a8473508da44a4eae872bf5a6b559a73ea404a29a59419b2503a686702f6942fc039679db5e668bfc9879fdaba865ef6acccbe8621c4c99e35047b37555c96f360719ccf27bcbdf3ecdd1e51c71f8a70ca157b10c77f470ff7abf1b8aee65de76687d195b2bee72d17cb24561d63e9d0e1698e45a1d59861b4e91104dac81d3cdc772f2249f92b115cc823c229530c04e2c80f5293e58b18f7b33ba0cee60900ea8039dedee7792e63a0ce667ba75f8339f4e5a360ce7c9a944c4c4739dd8610976788920cc337aa86580c738d192e05bdc04ed9c04e8bf55bc8599611b1d85e6bf19115e6d72560dd11faf36bf77516b3986547ea63f85b2185ce47e86b7812793bf1fd1829177a51e7468e7f59b115dda2280d1a5c69338ab8322cb89f7c18c44676c9024b67f81267e25e7c9752caf92a49f3a92f414564f5335f047f0dfc53e3f1cfe0d44f0afb580c5d33762695e3f1cf55f7f37fe53931aff992232efdaec330da00893634af1a42438355b35d3333dfae99136a7c0aff14f5d4f8d4cf60d57cd5abf04f0d5c2483554264beaa84c0bc7c98979f9a799a2f92fa1a9ffa1a0f5d2bac9a5ff3402e8c555ff33daaafc13e45dafba8b257a1be468555f555f3551aeec9f1ab5f611f558655e8e91ec797005d3fa24dfcd5d77c91d5d7e0ffc1817d3aac54047ff71dfea9c126e01c5855b10a3d359fe34b80ffaa2ff2afc23f453daabff13f39b08f57e4c6d77c0dfe516123d0055d40ae0f22e5f8af55f3d0a5ea1eba70fce7e587ae0e17c1bc7ca8f916d3bdbc4c07f3393a1cdf75367ed5fde3eec637a71a32f3453cf8d44f3c84e64df00017c9af01800772cdcccc78f000c03e33ef012e9249a57e66e6f1ffc068b3fa9f19cc02fed47bf0190b0f308c36a91c5ff3abc7ff75d0ea73fc09f86bb06a488e1cdfaeaefb7675ffc36b74aaf91e3c908ba6534dac1a4243837d8a26eea9790fb00fcdd7e02298af21b51a1d0da6f91bfff3ff4383fbc6d7f89acf585c1caf226a2226385ef5ff37e23f8e3fe1c6abb06a080e1cdf2e1b36be5d36fe8777bb66d7414c712f7e4fc9711cb1a4a324a4a4a3d94b30f7520e9918dcd597ff1d8eee57eecdbfd1d9e82e122f4bc0cb47b0cf7e7bc79f07d4cde8805c34ba1e1a3ff3a89fc13e4528dc33e369b08f0af5340f5d33de4785c22a22a99ff922359e06ffd47c8d9fc13f453d355fe37f549f7ae8a2f1402e1b755259f62bcf397d96fd8d3a342a22aa4fbd0935b86b3ce454e8a97915f669a66813bf0697005d3d33f3d0a5faaf9b7214a2fdfd8b8fa05e9bf9caa1ba77cf3d98ea663a18b30e7b9f9dc452e541ee09b9171f7b1a1d74cda0ffc3dd688a3fb176d2496d482e087d0c0304e3335ad4d76a77d58610d74ff63110a04ddd2cc7a365d8a92720acbfdae1b672fd96e7dcc8f5fdedd265aa35beb7ea8b70ca16b7204efd1fbea387fbddb4807430dadcb05cc747cbe87e9feb7ffd540da58f61998d3663f67f78b56fed5bfc2171efba579f75c4b5ffd9ed85a8b27d1b755a6e53bfbaea5fcf69b957b5bad910e2ce0c7f56d9940c9ba9ec6b7ffaad5359ac22b27df63f3ec330397c9e0a7b0dc32a2259f6a7c7b0f7d94e9d0a7b14767a14fe41fde939fc53d483c227fc83fa0cc3a8420ff728ec634b80ae9b4157c6e242976a48967dbb4ea76fd709bab82295100a3374d91fd17aeef9bb37b988792071ad4b22a4b7dcfcab7d6a1fb318ee5bb3c9d0c75288ddbd79769d23ee67ad05cab6d6c6b0748fc752acb9d76c77a4fea7bd7dac5309a947b423da57ccf56f9df158cb53f187f15889615d2bfeb4e7f0c4a8049c84f8f575accc23dc671bfe80f26623d67d2bf886c45fb3f893d96258c6fedb62eabb9fcd54da6d762701ff0c1fb14fb12a48fda75825a47efc2a83d4b7f883eed9af365307ca5a6342d2feface257b37a7edcf3eec29a534fbc6aafdd9a500339d15dccf5a1f20d7a6f900b9b4ffac06b9211b90aac8fdfa147b6deb7eb4f7e17efbd845fcc33191e5877bed9bebe1b0fab33b01f5a7ce461bfb154b1354b8211bf601726d4334ecd32e1b6d2a093e647baaf906b98a3fa09cc4ffeb1eb7c2c418d99f3621250320432b4d08e5e64eb8af611fee2fb6d1666212fc29ea4f4f5fa813e26baf43e5fa5bd4d6fd6c7f7a8d4e99bb6f8598eb37d602eab9d7b48a3ff97d3e4ab560ac5f67f5c9fe01654f2166bb82ccfeb95f285ce47fc23f1c110eab7ab48fafbdbfcd569ad092e16b5ab77ded7c2cfd862ebf14a8bab674525d1ab8b3b58ef872eb1d0a401dd1c22a720141770866792991eddeb7c95c8957b84eee07b1503c21c7f26e9ea4406ad2a56cd97ee3b9bb639d7dfaf3a5acd6ce5f2195bb4e3b2db5f66b27a9c52a041d84d12615574617b7e0b78d8eaf1175898376ba11e54aa79f7d115a24bb7883146612c35675fd22d9d7fa3f12abf85db221ec1d0bd3e43e8b156fbc51de88726386b0b920ae0ae22fff4a114679be3f50fe60c97b37553b9f800830f334c4910e71e43b179d7260a61042e7293b08c6a01833b071c9f1e7c604714e33c0884f431027fe29e8b481de39bd4e429ef37136680713f73b0de5d350ef788e7f5ae239fd724f42ef386517f2e74357cbf9f57fe8630d1bfbfa45b0aff8a7e807eb223f1643f74884361fae6692e78470caf3e14f2c9ea3147bb89fb7943f882576305cd8e3e69863b7c53dd118ddf928c84e294b8673628133c93d38bbe04083ab6f860c96fb9dbed3508e5f63c795abda4e43a72727a2d3d08909a48d5544e6d3ef994fe94f9ff91503b938a41cdf421591faf67bea5b0ef7d4b76fb14fc5b0f1c731714a393eed5ef9bb75a88422d235b14f0baef93f7ce2cb748724907b748175228ba0d592fb82032517c5648bee098acc887baa92b900871eb6b85c8656b0c021072c3c808485126bc5961bcc22ece5b66811433606d3b685892518194dda962a560747d99617c450c52012c308bb5b52b001aa8325bb258ad02423a86ea9410649061165d92d1c188aac168b476b074fa20d2c43e800622c8131c69016278e628a50fa4106afbb1551c30aa0fc90c3e6e48711e47e8c8c1f62a03bf8b0032fba4c2e7cb0424b1333195a99328696412c5a58299cee32fa94b16543ed840aa33dca496b36abc52c662996d54ca3da363f2fe51877ba297b42394abbf745d3324c3b41c7be70ef6e778f52ca392795f15a6bdb6f6f6fa736d0628cb6abd318638c114208619c91ca18638c32768c115a13d02d468cd2182385106adb0cee4465507d638c14c29376b988594a658c11f5b2d19739310b632b9d18c7a5c8498c4e627412c5498c4e62744214c5498c4e8a88b02615b3488c30556b218082c51ad2252dd4422dd44242b12665da1c01089244c08f0dfb4dcb56aee524d1c6adf7df4b7ac83dff2451a799b88df74ec7d042b0a903b3856cab5157c813422863282c042979edcbc7a6a1d7eba50129c878f1620a2d292a42007325554510563879b1a28a0a60bc8853b4141193bf48450926e6bfc82576e929cdd45dfc8913b95264923b22e59894636472cc1548392626e6ef155be49887e1786252fd7a65987fe17860a4527e9942871344bc5cf75ab1c2c89ede428ee339dd2b997b588512dbb6bdc6f16c2f57b6c8bea14e0f11244174f472652f4b1c91318c0a2f32b62484152aa2fc0d229e64fb95e3b1aa5c1f4e318695410caf4c651353c85095bca428a229c7f465ea7ef9fd4724be329b4ebb68d7d8ba0dac9546fd035296524a29979052044d0b461246585ef061c9881a6c6ec8c19206a818820c205c122e21ab30620618236416a69fc24be24696ef3232b20524c8524a29a5dc64b62bc3ca22029bc32b4f4d05043de5c8b30a11d7d64692f9e2da4721057b402dbe656a229aa994e7a8b03e8238f3573fdc0cfb14f964f8ea7082083feaa4a803e4420e865042031a5e00e30ad73c72c592104c6440890e96b8e6f751d489465c5e10050c23ca28816b3ee00436bc80b46414c6906b7a18454491f472c5ffe12a20e837a702e2c60c391e17a05c4d7ce4e535a10c5df835432b5960c092b837432b51dc207fac24ee29432b5130b1a258a2d43ab4378a1408ddfa0404f2004b48d44a14414ce201a6409a6568c0ab602259168a2d40d0ac168b478b8a2e6e03b3a5a2c96d60feec856269035520118142e9680acfa96da9bb6b5784c884ee8c0cadec40b48d60071abab859865676e840fe5816efc00125f0005204415b6b46696468c5872a4730d06a0771e2928cf33d56eed4c73ba352d67ae5cec33df755eeee9693caaf052a342cf78ce6e14929e5b7fa683484f062a721e77f11c60c319d4adc3d4a1d28883937dc767747e940c185702a812d63b451a2c8d81f65ac324a1425a436310be4085f4a3a6192211b0a5107f54cf079b8ce471beca1a7b504e0352b1bb8d36a1dfef5219535a8ee421b4dcbb4cc6a5aa665d64eacd52eaba4b0062a59fcc1a49a5a3911dcbd66f840ec5bf97dbaeedc85566e1d84b5e123e0877b40760a00754437c0cc115b8c0aac02b164f1e1239b9eb97683ec02c592172fac40618225b8d4c81e4f4091e3ed9265471be08d6d42ce0d3722f1f256d33f33122f773b097a0a962a8302a51a86a6b862c50ad777b3c481162da83c610510485764432b60416f32db958955dc9f904129b54409a6e072650751b030c275e5962b7290821976b081861db8a494d246d98123a2c8f211e03975870a7862b6a2c4022634b8d2a00db4f1b721c46d1deeb9eec66dfcbbf3d760eb6294e3c93e809d1d043804f46f3ba7dce426b70dbeb5f7ceaf256098b5dbc3a8b369f5b1fa18911a23e5b67723fbb35adc9fbc1125e74dddb77fe2362d6611b3b15238531d68b9cb18a3bbadb6a672ecbc75b8bbbba34edc764fb075c0af71447b9493568b65dac69d50f705f5027be7346944c1c4703346669399a1cda091a168cc60a899944dd16cb0776aa6a9416bf4ce94aa4dc5f1cc57d548fa591ccb0f1f3f76b2943725c38fa823833944d26068cdb561e386dfc031b9edc8c6ebf6be7dc1f1de3bd25a88d371e0c56def407b61efdc93b5b6c6031acd1a6a660d15b386f2775510eff7efce531542679bbbac41c75058dddd64f85b8803f18c73763178f6a0bc823b8c36315641691e25f5186374e9dddb6873a48c75c7bd5b079d324287944aa751ca4e06967bb6b3e3de7cd0e0ea9b31cb5e90582c8c05809f78421cf934cb0b480cd4ccdaf8532396d14c9bda26372e4a8ee378b0e750d9cdcaafc56eb2b62df7dcc74def7cc0f2e80e8d153f9b7144a64bc32c0c051427191a5c754f99cd8c6234479e4add2b3b62191fcb62acb67a0bd52d46b16c669ad4b69849b9713cd96fac1b8b653babd62121ebc6665505b08a597642b20b6d28c334c0a5ee65c282df63b8bb3b0d9e64f72db0242e648761d425f6bf6ee25f80203be78dd4555ae90b285acb0ceeeeeebe832db6f020a509134f1021d1c516618c200cd7d75d746edcbeb8216b2f3da795b5d7381e6dd51cfe808232f75af7b160b8fdda731d12d8a47d37b9bf9bfb8ce3396115a4f6cf8d0094b51c355c27b2879f4ccebcf040ce322fb4c8d9df2e47397bcbf164298e079320e46fcb96896cbb40caf6210dc0c8f629c763ef06e5c55341c8d40a0d7ac8f425c743ed945c6690a58cd799e4536b2f4cf902d3a2372c6fb6301a628b2fb2fccb4511597eeab9d892e57b5c88c8f227cb06292dcaf820cb2a240e296bb26441964e64a9c550965af0f03ba4988470eee5410bca5ebbdd2a591cd94b91d0e0ea9b464ed3c395f15e661a4d4083ab8fd2f4703fd825ab584802818e17e27412d8e356f89fb5afa0fe24fd2cd05712462fd84385f9d963db6338eeb87231ac67922ac8cba3fe83f2822a9af62756cdc79e621df7b33e9d8f59cfa63c2d2274ae2ec01e73ba6687ca8e52489fe46198bb7342fab5d7660724134453d4e9246f8a34b9b3562d0bf5d53b355cc8f02b8d0ff7835d0c90bfa69265134d2929a9b16ce07e6dd4585c87c6f5d3be7da04c3b6ab7fcc3a7b80ecc810a9828d1440e4a6051854b7e6711822cbfa5780e96e5f751ed5a3edceb5e62eaa456722f56994bf1bfa62899b019a4a94cca94e964713b1f999f1965140d0f37665c72e4a14c73db3a2d675d670a515d74cdee9421fc319462f24f3c22230f5265f9feea1d2ccb779bba9ff2763c716953ebe82488236d937c9b6526d96deb66ce01c3b37892b7ad53f90be2c8afd9e6d9a9bac93df940b9773871e9b74c0ef7eb6c764079eb62ae31e3865bdf5f5147fa5331301ce3825ba708e99f581504fbf9534a10d803e56dd3c9e27e1300cad9678d23613ef69513321fc31a17c20c329ffbada3f9b1eceb37470277fac84d6b55445e1ef54560fe7e09d0a5e485178cfa176052b7e35e047ffb5ac661f83392b139bbfa590a58872495b9ce19875b29c93df9314bb7fed749744aa7a1b66ccb937b5590f9d99f18286b954b0045f950f6a06e8a79987f79992f2a42e11ed4cb601f144621dac8bf494d591ea1ee0f51128355f55f1ec805835515c7af18857de8946813eb4b1b6ef61c0d296e96d96fa128c32a217d8476712f4a39ca165e91f2a2536667e7788664540c88135febbe0dca2b639886e15e7ccae4de07affcb0942d46029b4840e5f942e691f9d86b939641332c42ebf8e0951f943212d8942bd099621d2adb2e85584619b9c6a71d2acf0ebe18698e61c08e80724fa03347b934855f0910e65ffe8302835f2d141d4152241f09862520312c9d5cec33d94927777b6d83f2f282fafb309f7af917ec53040383e19e974f611f8bbd546a02b92e56612f188665a70a121f024079e6def140efd825da6f49583ce7f413e6e73751d4b93f533fbf9590803bada475cc9fdf04e784210679bea6759e372dd8d738fdb6c5ce5ad7f9a475306f5a723fc6d46fa44b56f2cd2a723f7dcd79050c79ce258803637f4cd73027807b69c3e5a0c4c47450b499329ffa992feaa1f1311f13f3a9ef89f9d4fc180ef7c4fcccfc184c03b75e7e0656711ff3402e19ace230f73ea87f790ef7c07cccc7609ffb300fe4c27e83e9e894fe8b4493a40d57c32afa3067f87b1a525c8b6953ebe829b99f7628b82733a0cc371018a97a27e66e7550d4a9dfacdcdf2ffaab192ef6d99bd0ad29e9252db4059b4da8148db92439e910cd4800001000c315002020100a05c30171402ca0e8a1367714800e72923874543494889218c8611443310c04116000308020428841062a1a3a0324039395ae1164b40b9cdf92d84fd776aedef2cf8ac5364ffe8616c29a01ca4fd4d81fda65e852b6768406f6315546c0375096021121a6f18918b083d224b8a295fc3476b22700c84af806d66f573e0a1409e316a2f6eabc588ca254e6936cbca9d16df08e9bacdb48aacf175cda3d05a0293c26dcb4dfa84e526425f2178b07ca9e99f962ecf52b1d0e654a21216ca31a475de7c8194a1cdb5bfb96ca59b03491739d8754503ab60cb83ec6c1528cc4432747ca6e552d2f538d8bcd6f4c73902543877ee0927abbdb0e97093d82ad624ce32855a0be24c2b2e6c98e6e786e431e36593263fbb7f9a924846428faca268906b7891d4f8c732d1afed53646e2273d4f0296da96157466b34395b954d4d9bc97b087c2fcc73e73411c5043906a1dff2f872420d9c412485caca0c131d32c52a1198e23ccdf2c815078eaf508c570aa2f29230eaac0e02d7ff95611532732518ce49287084343731aa0779dbea5ca433705bdac2db4775c84f5f379c082a3d3911b02152f499549a07aaa9373f0f8dc80900cfad49fcfd26c780743ae37e75abea52b352c4c9e33236135d3635551cc63c8aaecf17c4cb7f206ffa257f482d1cf938f6e0ff8823eb72e4839c94f9ac26aa2cfbaaf9c4682b04f208e5ed387599e072d2f96b09fc5b08b907a62851f96ea5225ca92633f9e5980af0989fb6be3ae8758c71375442deb0739e3d8003692089bec00f5c7e0133ccd44bfa48a889334f6cca893c65871042d8e7e586a8bdeb94bbf9980daa80ab8b399e5948c453795b0c0f9e433c8712802f681fc2fd440f5cf32ef6dd73796bc0220702a7574a42d79bed87d42654541c87422146dd07292e864f9f0460c7cc6c3611f717af8645857423d6faba1b7c52fc15debc6528546b2ec868762cd94f6aaf91cf4ba54c6a21a0978ccb8648b348751a96c0ab483a268091c0beb3ba430d2ce83bf3e905c34869065fa6753e2d514a20919874ebad156c15927303aed5a59404217dc3e47d78fc4fc75357177c6e337751a7913b3637703371992ad3b6ec88083fecaaa2130bd2af795e1da2988336f2af1f3ad8130ddd1c5b4390361af02e59f1f5629c40f2adc742bbe38382bc728d50fd65970f00c0b9d697b13c97cfd74ec2276183042132ad2f323304af0154544ec4bd19a0754c1615d3bf9cc8ed2c4cf50f6a89aa4426a9e300f9a2209b9a276ac48900400db195fd286a5a11982e9cc1e56330897d5cde5b619f709b49833ff50d157854f06c1d96913d2a22617d029d578404bde1713c05149a8b742e9e18d83542a8cbea23a16a5d3101725769123931b5731be32f104a989d6ff64622f64e3a9346c7938f9ad5b0c7d0d9f9f7b948e2f7a1c3bc882c301650d0a4953902dbe4ce367328508ac7e9d457b17ac8a7a19a25547c48520a84795d7c82544a084fec6c397d7d33d09b958f4aa3ebaad335612f997ca526d3d1023f56056e11f10f208bc25c76b2dc79937b0f90de9a71684605d164472218ce6d208f821736e6307a097759b15513656c47d84cef3b540edb53ba61d95603a4f80d907f42e3c2c0df09a3a4371ddfcd5443cb4d1d9efae445eb2c31bf6b8ae10a65380ba5ccd1983f5e4f51fa09fb098ab88e704a3830911b9e8a5e35753c537295b37ecca9b7ed90b8db1a05d4d26cacb2cb87607c4a5d8c6424d1fc927646592e92fca2a8b30f504b25ac924c504bd51d13920fa8ad5908c9713222b66886131ae645c2720906f379d80ca521d3d44f68cbb6982d1a9542ae3cfd3cc743e763d6c85dbfc743362771ea4dbcec853235b6e4bfe9d032861b4f6fde5dbfb3a28d8bfb902915dfcfe3fba44dce473a55f7da2c359eb482c09a7a13484def53d524a241c5ce9a483369de0c05e279d83a62ab750c6061705f1b09e4fe78403b5e4380d43edb16969d0dd74c3352a62b4a462b268b14dd0d9dedee94a611e870bc268d7e5f28317a98b478bf8eb1f46bf024c8fdf66c620100979539cf3f746e32d4362128840fc429448eb14ed4a1cc6209c05ac60ada7076bb0a2a966ba0a660e519fc298d53275ac8472dc509f48fb1bf22505505e40cd956639ee12066518601ed6becbb3e9d022244138f45a310143cfb0b8abbd33c991d4b56ac19d974b878edd72cea908d13aed787789b362a799eaddf61029ce599d159ada719a7b3c6bb44cfac4221aa0d65d33538338cff546da4ef59a5c4b40e1c72832d069a45799043d9152af09bfe5d801956d07544a867bc0bb934f83c922560f9d6573d582ff7ac0203a82c789b1ce38080a8f79ece6c91fd13a6d6477f0312c8b30f28568ab29071d47ada3237ccf8c2403daa58ce953be71bad30c18e6e5f403cd2d8563a0460f00ca460945ab796e5ff267a0c12778648f7894228ec114ae149a1625bd94dda7521fdd5b9676fff7109f9698c240e5af5538d9ab342ecc03bf676287fe1ca0427f7d9f27f450bbe018a4d3fefeb9f32f7321371bdf082a92a8848f88c55851ab4d63061750547b5990c229f98d88c6225740b5863d72e4edd1c69be141a75b734039d2573ebc2b140410ccc3dd3b745c576adf3191d1a52afb86fe232b569838275a35aea2547ffd395eb71cb91c4b1802f91f8569204bb14a34515a2f89ee9a8da91a50ecaf0eca43207bac8b89e905fce9c8fdc1d0108d4255f41e33d90af6387dea087d070d824759bee695a7b2f79c9ea2d3c43f14b970bc9a9832f1d2aef8050949cbd05e4a39d455cb98a863d04e092ee5af66dec3c13cdf22d98792523b677abba7456d74837e3bb1290f183f1eefef8cdf11dd7b9346284669f85c3bb6a07dc6c59dc9457ba6840cd0ed355625fa58b26a143300550b569269763056d01b360a8507cec4be789ccbd70a0ba8446bd4e968b21e38611371476f28c40aa946f243cf9af053fba8493ce81511254f02e11373690b1372b382c7f021d00450f8cd0ec20be8b45a4d7ec73f65a1b0a0ac4aed028ef8525903a2230802574111180c095c0d19059f04442fb56ceb4b94481c29981e8ed66e950ddff73e3cc98cfcc9ea995489d818ccb1809c8889e2104d9a2caa274f786cbccc87a4f935090c0423bdc027ec101df4e65c0603365ef87131d21bac0555d08a5c4896b6a2f4146ef5f994e3555c3ae1a27772c18813a39a2ca317ad4607cdb0d13b39c1a043e0f59a5a019ff81f875da0c4ee2206b607f73af12598111fdb32c81f019ae1beb4ecb4cb9440acc4ebf83855919c0a6c273e5e152375d65d3f0b6eafafdd6c85c27f98b61675cab783c10d8e24b91b4dc4b995126d2b3ebd7dd7fddbc6c5669ddfe27c81cf84d374274c90626d2a44ed9a7c62863d38904913d562aaa8bddd1153a360253af6c479875580dc4319240d09795b0f57e48856f332981301d40f86db49602856543ced1044c209e5d0ae06f3675bb396682e58423b5e717a29255a20194a06118926401f23e85e880a5ce453fac4930db925e1afcb73b79f5f5f5d00483c60e22fc9f1593a0065920e70add1017cd71f1a84c83c7611617e4a5b7666bcda85b889ae18cc6270e1182053d0223bab18bac7a78abc9ffbca0b8382fb4f8bb13c09fddb4d485656bb3d6e678029912671f43f447cf9c2ebb5c14f876594b87698458c20566e9167546d646495466b3ae08d44387b99cba80aecb2350a831008591cd0410d0902dfaff8843fe0c8d9ac1f0650e4fcfcc827a0671c39e15e4e5f4eb399bd9f28f67513a95aeb5e7448667540703290bece59e9c6ac3346a4969769c5f6d2265a4692b76964d1faa6c162ae3df10ea13fcf5fbae7a0eb239327db1f2de357084afedf8f65fa8ce7eb80965afb4de533bc7b662c8f2df9b88d3d9d76c683716779c8443d74c61c534162a7c8d4ea8a90c35349b3bdd725255467f168c447525b6dca6ed99921238c8136f7aa8adcf4972c85389bfaf928a59a81f26f85cc8674080fe89e19a0faff35cceb78af9d265e978df4cc75ac06249f12a9838fe3f4b59427ffc7f87b790e0eec5e03ad41c009891c254afcd95e0cc727ee13189cc458a073f11f7dc51e4388ef4be00469b5b51feaed70becc70bb245df3e52943ae6279c967670e0568617e3f13000512f19de5b5f138b251c56af9483edd41f9538504de82f8937b1b44e4afcf2a77cf8cf66fff30cadd1ee862d89111fb69c5546116a9a16603ea57c2a40a72913bc1e51accba6c147160f256fef859bdb42f794f55e5529374ad2cb909e1df2410edd57b023786cd9a1c3e24719d9116bec17881de531845733a0ac779e8c1c66c6ffbbfc59b716c5ebd8ef7b537068f91f263a3ba9a7072c95e658d8442d0d304c2349094e45a823b4a82de61164814fd4d15dcc54c664673a7fe956be4167f82740b7a45dc46a0e76cb4bb091f56d748b14433e97256027c9dc205975e975cde78971b72821baea71d6ebde0e523a44c945e32be7a07ab764057bb13d1e77cc91555560027bd136ebc9cf0e0699ae74f19b2fc475ed9866ca758ac350f66b02764cb47e4b8118ac97ebce7e9cb774bb57b7ad6defec1f393e6f9ac5eb8725d6db82c133bf418a697aa95e8d5e910f9603ccb28491171a4c9223fa3d878c73850432a5dcf783e7e6ed9790c81ca9c8318b2a9e18acda2ef9f9755c0facc0741a1d4388dea422770886ed13d70f2845c58bc97672ef68e0be6a2a09e228e92ba157b4d7625356705c637b4b69d0e2f229268481bc8b5439003c05f7a85e129abf8ec524a75510278b9d2251012e6692845164f85b36bd0851bfad7f5c2f241e60d92c4ad11c857edd772dca8b5b637f5d2bb0e12a079cfd1a81c369a548450b60d0d44566cf9022101a35e9af9caa0183927b907d77daa631e4ff7ad08277bc4a2efee05c049ab6e64b2ee57ce82ae01ce71df8d303fdc7c6440398e2c7b5d11e026abcf645d04f6824349fd020dd80df1e256a92890a169cd56a5a85186b0b3bed2d0aa0211df01f44c401f0aa7cc54b06b5280259320bab0d7dd68e2871db7f290871539c0dd91ef1faf3b4d49857d3d7ab42e20a569ded80ccfea5d53ba8a3a0ad9e496894daebb4fdd6d14129bc644ef55b6444733df160d22acf63fd427cf73088396df6cbe01d16fc30051cf131d265fb6e7d9c108033a0ed2d2683b416ef671712460a5d4ce3898e00c58b962bfb74919d06f019b4f0867b442677c924afac4201dc10010a2285dc2462be84f7832ab31042233c0b90b1b2446d529516240b5fda677ef3a9bbed75d5a4d810b1c16cf0f1a1a87d900383840d587c0813bd1705e62c47f050e5d156536216f9df43c6b62044a0ee04f9e3e11c9c572f8aae358d06cfd638d9871d1bbf1c749cf05d6a64214af337a12caea3a8649070efe59501c29b0dda634278b6dcab3fe83186c5fca89138d54196aab2d36e2297ea8b47f67f53ce577873aa391585321e4769aef57276479f1404cec9d4e297486e7478744a9ab6489ac54d1ef23ca482ed45799098bd03684e6e39be035e9acd296a368d66908d6bb10e569e66bb620a1d414b3438212ab15c520d8d64511c2c801871142be625ef70c0638d2a10c9c5310887744d27b009d8e60ca6622053afc6cca07ca7825cc20587649e61cdc087f2fe4592c078507e4590d0685f84bdaa0f68e2cfbcffac4e9b19fcfb3c9bcf63a6e5f76518d7dd7ba1801899772ab900523b4869d07af5450310dea5525bcea76d0782a87245254e0b91030b9f08f531e8ac882fb1f4770ed4c027807a9f60667c0562878ce56aec85d1a03d45a2a16747cb49a6f7623aa139903b024f4305e6015a29008196844204148b8c1ea8fb7d56b79e5282bee986eeffb91c9f3250bbedb9d57be4e42bbb44359f4fde64939abfc16573223321f9ef6d9d5b7be3ce80c465c0cb842b5739ad8240dccf104ef8e7f6793f27d5d1921da879730c27d472237edd4497e74dfc39d171e34103957a6c8f5815108f9c2fbe64b28282b9843dcc794580d3206f60bd07c9a6df9d5db9e1082817ef29eb9df5aecf649c38986989fb3820c30648892e7f4543718bfc7cf939bcabb2965f9210734f14dc7b251d1c27095a9b806b460c6da44457d4d7783c4cf0065c4e8a2a5c03bbaef663b7c1918359ec7d1efc4234f764ac61f24031c012887f4318093725fc23e96d8aac44f80f4279e720c7ff3bfc1b92e127497f50fae6722eb0a9354e1d7486d81b0ac0fc3d018394627ac49eb9955ce6983153c00626668d5ec09b7d423f314d306eec73d054fa930aa4be78a9d31bd07bab0df6d351e37093a8fb41b01ca0abb4809e94bc42289d4457d7a983d902ca6340334d4074c58b1afd5fdfbf1c5f305a7c61ad5d491ed09b80269b26c625b8dccd9036872a24b93fe9be5dc8eaa171f4471e591eecf747d2f713abfdff3055015ab3cb3e2b9447cf498cad075c01607af482640b68e4258143712ef422d49eb04600c1b65633e7abbd2115df1c04cfade84b881f8099747b42a6f07873592fad8614298c1d6586c503472de19c08f12a51c4b0fbe08ef61c00c97350cdc7a5a62c84331ccf45e40f21790197d8f575582b40ed4a05da4b13939e2094624a61001b79fe7a810f37a0fb07924c2a4b207745e8acdd46c20bc8bb955dfcc2d0588995f2f9027ec38f16b4e8c8b2638ef94a6498d5cf9e9aee62eaf6c053e2c39258504c05dd4807cd07e0f155e3d7119114dba0b03175053e6f601ba08c5a0be42ec239647c88c95954237f630a841733c230d3f18395648a8fb399e828e400f9b4276ff076c726526fa85f40c746ce1b5f017011c9e562d826c07e0021a4e1db789af0896987c586cfc76b1bbd9eb8730d29cb6dcde2a6d23e0243cff3ebf19bb63b52762d171c5586eb698606862cfd22b9917cb44101e634a8e2ceb81bcdf08cf0dbab94a1eec9db843262c2c8ae200e68312c3a4214d623862af842675c1b2637a81a71563986fb8903c52e73abb71ac7b9a21cf7f8148006af02deacc82b591caf503342b882731fff7aae833b6b17aa0f81c98575a61937e91a56408540b2330da83e13c650c7a194d6020b452b9f094dfcf84a4c5a81decf9c4d5e0cc6419fc6901207d18aaf9860b04cbdba03838a159e6a03531633a2d40ac6296640a6b8f8efd94fd6596b91ddd7a08d63f4fc5f0eaf8066fcc31d7407247d27e2e814e0ce441e2c48a97b69c8955ccb86d1c6a6fb23c8cc4fe8dd56b005486750e4a187a443ddb21dd03fa7320f8c03f5da3c3f135768b2d55b66399eb49881fcf95b6928461c4a67f9ddba2652adfad4b47f6f64b8cf2ca4f82252974ba2ba88f977b6599da48fbe8a40fe92989e1500a43b75e46ac56637e24fc5349805e4b8cbcf3489f39d4de3a8784f5ac992627b9eb1e01561a2c543414c818ef396244ed38541144271780d2e4448d84410ff7baddbc100077033ce4562d6a6fddb15ea05f0852b3c0c398e6a0295bfa2d9c90c14ecdd29c937a21274c7daec6c84b96cf3140b3d673fa640766f079528cfe4002188b808d9113d49f124675dcdeb48b9b56b99f62c27753f2998762f8bbc3cfaab06265c2c9b0449418633456d761f4996230fd485db3ccfd3150915e68ca62e8f0340a025e549ce67b78f23caaf0cf6eb4d662e8966c96812d701ed394e5b3348bdd6e4a88b0fe1ad3e06591c6f547176388a764d30c16215310c3e86220787be2ea31a16b37a3e35b0bbd36c8eff5c88a7f64b1f9ec61df1c402e399915176306113c9f96bac1411d41c3c57492941628fcad6f163f0cefc4a02f75312d7cdf805f65107f5312f166c531988b69baa14d774aa5df42c6412c26196166c716d4378657d94152fa278b49029156d73673797fbc9a542c0487286310c99111ffbf5c6985de20975323815007410262b0e0d0e3bf32f6a265e5bc073ab9474921675eda294eb39caf4f94212b33fdd6b206117da192bd5ab999c20e7e18122ad551d0d48f1f7e160291da91440a473a3e79ee7bbb5ae65742a745843266d804199fa79f1d1927fbe277e598925f3326a8502aecaa92ea5d3215d2cb95898f3f13a85f793715a54f489e7e40c1693e85ec143c87a9caa041a135815d504651414691e87941c15ecad6c7f87adb76624c91ff9501f40f2a7aa59e2947a13b8f9110aa3c23d37b14b21a3a3e975097186e454522fbf407bbe02c91c1c9ece72dc141f1e99962926ac636ededff9db2187e208d9b04df44f9c34316d217d9e1bccce77f2ac23a139b5582f1c18fe2fae14fd92bbca23302a44406d8c6f5db67126f94c858c549a1bed0e30b61d2d1526fcde447db6bd03d1e45c5a7a5531ab6596ff15babc2e3bd5bbb18524d002daf030b5673630c149c60ce10b9674a0f3af231caa2dda0619121f5b5ba6e96648da212b4e5fade50d29428a4b7233279bc464e910118658ec606dd313f5cb0b5b640970734c767bb62ac5b50ab25bb21c5fe8814f2cc1ce403b1b4aca4677de904a473088a4ec1457a8a4428c45c3a22c046d64d38ad032d923047957c0a689079a5d645137e90b3263d5cc321d2f2af01e98c4b020b1f0f1ffc450bb6e31f650c9560c9cbafc5eb71f16ab4a2d0d23502781734a1450b39d4af4440ab2ea165797f1305a0f365930a685406187bff7ee6dc52b586d566ad416f3cc7a47f1e95d301c12092cd25b280a00dfa36c47d0f7e72fd1b41cbd2655fe6cade36d0723e7ac32f7dfad522b498fb94a5e026530c1e0a38521294df79735f4cc85b4111e9d0c8b8d4dbe16abd5458eeab230a628c16e7df65f0724d9b5d5afa565ff539bbd43358d88bf36a35c8eb8bc069d50e9ebf13bda73ddff91e961fce57d910544289f182b779e747dbf10565c4e778e7eb8028ea9a980433c4b4fbd694eed95f41f50c1fbd24a9d9da3c5d590eea74ed9eea3dd8899168a027f96008522f138310c6f3fee5e4136904336c90080a2e88fe2439744ea98b4965c19e44d6e64ea9583870da71953be00a118b7a2888cc4d192817d13708858fcbcde4a2f2aebacb4dca5009e99ba6c23a381d6a9ab4bf68509c9b3c6b050df92053142affb6d20d25a11555becfa24712ed947ce00071c7f7dc3f2ff0474353e92d515259ec8826833851d6475bc2cd9c2efee0a16a13674502268347dea2be41d3b11a64b0d0633c8b8de9489f7fbd99b380978dcf4248ae5520cf3f905e8e22348e6d79746fc196f3103f5f1c852e19225dbc77fe93172891f2aed11fefdbab2272eaff910a94fe082862faae5282d8383579b5d8f15399627b988e62994b257ec365f2512a0551cd1527c4b8e0e5003c493cf9ce38dc59b1213acb908b7d47a2dd2e72fce53b82eb6fa2e04218537086a8e1d54d99715f2616996f19d57a7688310fd0c5092a7ce1a5758cf788fb5d0971b662e7262edc0c81871ef5d0b65f0a8efab07c0dadbce309b162ed544377ed3f59108ff96f8971f88b444f09de7611c023b2f528c945d8f52e05b6b42a40d852b7119c44ce3f5c8cb624c8b8402a35602e60685b71001d20c8235bb5cae0d1b0ed596433ce91448d46e05f465204a3025ac365f5d8051753633db7704d82dc5ea852c722277e24740318e770fd95162df90e9ca9c81eb0390b00a3f37f51fa494e9ca6d7859d0676e4d9512d6e4f49e22bd3e061e8964f83888e88c99536ca31e2bc0d15979007a5eb4a6248b845a039ada456e9541066e9ac46fb817114227288e692670577ceefe54814f4c3aa7208ed60c14e71c1e331ad180e6a6dce1d523b5a70a2a7940aab8d5954f030ba7ccc7a82079920e593aa9d37b6fbac9b76ae17ba5f7243b763a796be087c43fba20aa8a7384034228e9156105fcea086e476f6a0f5a53f346cbdf49060bcc445dea28082660a14968d79417da7bdd819c79abe125db981624bc0558ea74f906081a85bc5a6a5e67526ab62a814c0819e64d7404d2e14de13d35ccaaa1b8251376b8781c643314a5cc0600f8edb5122fc77a0ccf8c1a81b1e7bb3d54945503aa2a0829770d88cee1a39a5f079886a747f176d0aca3c234af29d4559b3cc579ee94193dd6bb1c2025234d65a75615b802f1da32e31e8d5fb1b70952a921a2ac996637e473e7526aa048259ff0c20f7712ac99cc6d6f63a5077980797e232cd030c5d6ca9a82fc6090f7b13575e3686c760fc2bde582aa44d10bdd96160831df199cc1daea2d957ef97b5843f6d7782191380b6b362f0a939da3fc68f784f1b45f59d89661caac69342222b3056b3af21a630d4c6a6133301c4b3c5632abdb56f88458282f191d6125f2a24426678b7f963ad5a13ea184e903822eb0434d80bcbb61372846385677b33cf6b0116c2e04c4c2b178c8e8fa344fe0637f2e9c5c4cfb576b8eecc3648c5cc98404a868f82812356d4ac055e06a6f4f9a07238a251fb974911e9c33b8c8279f40d716cceaae330d608ba66a4fb89cc6a29a7b922a8853a68e32b3464e2b182e353995a1c7d6cb04c79db743767bcf659b052729baf783a45145855cf2860b063fba20f55a8e0120e0cff13b92ce4a27f4299222bb3268866a7bd513a0dbd7f9e97cdd13c414f223470bf0ebcf6085c201ac6ac2d03d31e584b9f27d5b72aeeae5e979d6ccb48cbaac101e7fe66d95d851b376a8030d47080d039876e7a51d47da79e8e0bb972e4bf1780163ff6a6f83935496e32fc8acb039124b5199e9175cd313300131f59c3797ad80ac58ba17579ad73efc98f31ec93810cbdf65de2a53002c0590db69edeaa170e69020cf4a20002b4bb7903928fcac1e549c2915eccf48f60aad08370aa9017f52558a298d6c099d0285ee71d29f19dd6c08452eb1ad8a0733385ee2ff33010fa2531389426b5b4e54ede7b747705a24a7bb2fb0955c68c4165ae45170bfcb7cef375772d72f0f6c3f0edf870737595c2e1a4eaac0335c5957db05b112a04ef236e0b5c07e03c7073c20e0e794e5482d3889ba0316cc108f82f0e5651a657e3e8f4c88e071bda860c2a441660a04d8abf9f4edb97e8b7333ad28cf8e3471d1b1672859ba313a785436f50fec4f6070b7564de8d4837549e757bc980b8370cd88341e33047cf7f7bfd91161b4c3e5819028119fc55cc6f63ff7a94498a4f2146da03786ab4990b8b56033e3634a7ad98671ba24d7676cf0064cc193c72fc4c967152666a860fc867bfd47cb60ef0b28ca29a22a1b6bb1cd86d40cd5786f797c8a2ab113a7abbde8fd08b80ee8ec78c5a69426d06c1c8a9a69b55a9d76d697ab6e780ea15aa29e6629a530379ce0d27f6b12252a1b3fc18bcbb88ebf3b55ceacbfaa0828ab185567d37777bd2d0e38806a0b395915d018b8e120cd9b82f3f95fc5b95c5d29ab041829209e0926a8f2306f4aff07b1250b5a935250afd33830ac6665f13b7692a6034529446bce7137a0aed25f4b3330b62fc75cf6482c0c59e96ca2aa847ad46392f79d96ab9250d13463ffa932f2eceb94f51bf76f9c19cb1d1a85fa72dd253c984b3e712ecec5d531ca03a2738f495d918bc55570c21035624358e40a68114ef338a403ba41618eabc1032f8805e1ec92354bf93ab7135f4a484f35b094929874d460099ab4da504bc22a2904a35cdceaa702bff7afc1a0fe52217c48a62cdfe6bca8ca16081086dd39288d616171acd4da2e24923a7c6332ace2e2d516a8d3f69f4521de6c177e56dab2ab195c1c3ed99d21ebbd3d0b32f84f060ec0ff7f223bace58770b8a0484648eca97604f6fab5e295644ee2f6de1d13e4e2c48e68f3f439a84666f4a83d74a151709983ff08586476f52837c17c79c77800c44a776d5dae895807266d6186713fb07320c9b894b040aea8cc16258555da897c19f8bb629381a3807dc0826112b21c232ff6f339619845c80a2682573441665cf211d059c435009aee5078b326b908407a472d3a4b575b64214a1aec036d6fb4b00ba91023ccc97f73910d3296645653007f8a0ae20dfb26c3102b53fc482472aea7de76137bdf1adf1ce0c03f832d37e1dd9123a9ece3daa140cde4b5ac97dca833f5711e3fb58c57dba799f3628f2ec756e2537b5116a956fa915d294e3daba8b1c7f6ef1d3982394cbc127775308ca32b4df9583d470f8e568d776b28ed7f45bd41436086801eb1f192ca6fd03452d1241e9494660b1d9b81128faa5a5c6498ceffefc83b92344464089b207b0363b4fb714b11530393b2e3f001fcc2c5eab7522bfd2cb9bfed5872c1d5c9c35cbe886eacb90f02e2fbcfb779784a94265f8ed526e495b3b6c128032e4fb7408a266fbe1e2ea65b1c12a113964b75ccfc64c02de1bbc86aa7c20e87467c9427b8c8a305c84fb398570b35027ee5e9d48fa40335e850c7fa244265d67f87cead8a02ace84e14b77db9d1bab7c4f504aac790ed7312b7534d352276737302c3e48ac9e11477683b993a27cc3eebe418ac0615edff517e338454b84a5f3e6342ee73af366c03bb40ea27b17afdb8ed4f71d00722cbb2f0097465831099518dde2c00b7b9409d805a4c625e7979ec8f39e8cc7d29e97f7b76450d0ee718333e2ebb86fef77eb587e5ed5e9583ef9f8856e5cb8fc3350961fe8fd32800555ef84fe7cfa355f7639152f669eefb79fd56c70da67a91592e89acf2dbc2eb868c0189a5de7abf16a8a474defd3bacbfa213c50b095210a6c07ba1a8df6b0c204bdfd39c666bd154d662a19601b653e17b371eedd01edab607ca808a40d9130dc07eeb095041ee232ec8af7fec875046a85239b71a50c4f482424f2665d62e27cd4e325d84bbc57265f86e7e81fe6f256c7fa7a92dc3edc34fa101994225ce50218e4e2ed741fde17f6ee99a21d8eefe22b86086428fc3dedd8167e14c92cc4f82b2544ed0583bd3169881ff6143b366a647af41de23d0f8a8526cea7301295b48fbf28fedd7690b23cf294731dea89086d7f035987f804d606ac69572486d1e2f56925103082c416165c1e557b029044b2504f3b0fca8ed1de85b0e7a0dd1ba08df520b69c0deb32f043c2a621c64b538f5ec98f9294b25c7c5b8febd4242f791b3d033439cd3e620efaba7a9989919e511b08472813094ac3b011e4cb13404c8367e2086ca1c217994dac6da74d0331ad72d2e7575c6c41c1a7683a6b8f38101e8270490c5c59cc2113a8b9821b8d33b1c9473831efff0f40af77b702edea010c2a4626e9df10b4effe873856db52455448d7d61a94fd20e4749b0959c7e63b8b4340d3801d6f91c149a6443cf89f0eedff5faff49e618910f1f887e4e8c77decc324bea02225ad41da9b42f1fc405762175974907c36ceb95746d2ea666790d3af3ea4f74e889880cc8fd3e60fdbe15028ef6847aa13eb32a4f15480a86118136c7160d571805bc248dc099e703f748e2eee883a442c043c3ad231d86a96addcbb8259995733b4f5907a1ca936d82b6433511944a07a87757256c46f0019184f0198867fc45623db219d5414d7978dbd088d5669039d43abacdaa0cc86671b46a5766cd88144d442517b57fd657102f4a2453d6c14e5c457ea214cc388c332329ea3b466ae318c9b1195ba812f3d1522e6db70423980f8f9d52c03b650759d8b25c596e87773237890a4cadae79c5dd6646dcf21c794d13ed8e9728d8f591bd68be01609d61228618a3e05049e454a38ad5c3310431856288557cad19df6c13ebc876eefe25330ea276d3a34f37d23def7ced8139d026ed98f65fde722034ffa0c05f1a5f26cc0d53eac8d31dc92c88672c5d8684556f9dd9093c51e3516fd47830c45c3f059f634b51f982c51bf2bae0c2eb892e518565a9519457cf97fe27afcba555b0491f1bac354de849baa349b0c10eeec72632472af614d449f524d22b1bb0236b521639fca5c0c19f0bac4d98e5b20c8019c1e58cf8b724fbd27da766364e4ae7a3731075e9614a518ff8528e31e442c98469159a34c322e1ad1aa59e5e3d3d7d2709f6cda1b4e4ccbde02cc350e64ef3bdf553fbc6db0f06a4dd5b733a4569bbb59d06d9f636c5751ebfdc329477d9d7c0a4e94e00bec1609fb8a821b552626eafab9efda71f94daa14542cbe00f5c727cfdd6bb1f3f453b9f899def1609b7264da844e47986dcb0ed47ad958fb3e4030766121d4054da643005e73497ffc36a4301799b51391bfee5191d89740d48adf3f7e0c2ab26d088a41453ee73d7c540a07727c5efb1687985c4f7acc28e0d90f9d9739d3517b3c983f49c2cded6d7f590e2d86c28a728960bee7faf701515bbc8ac572913ddae2283ba29679b9bbddbc02870e9c1d7ed2ee452ca33f3f19b5a92eeb2648bb5b102324cc0ae8df6dc76ef2f5320c544eb0a0aba4603ff40adefd1bf479bd7dcab8c1733bb082f233d4704ce532a3b07c8890a4b4fd1a574aacfd3b4a90eafe01c58601423a6309c8998de210695b14d40221b66a8b83163e820ba0aab59d5aa993188c08a857c5a60939ea83fda448065329c7418ef09a2022cd9c2e734d81dece84d7ab3da3f2b17e63d435c117efe54847363d624bcd2ce9ef5be39ab8511b68b555c1126697aa4816fdd2513e6226111304cd89849fa8c21cdeec1baa0e85bc5e0b91004c6e1974c9026da792db7f79d98c83dc53e7f2725961dac8aa243a6f854e68d95bfaac628a49889d9c521634ace21bef42a4f6ccc7c3bb76210ba047287760292ea5846bb657607afa94d2f9b40bf154a271d892664e9bb4fbec61dad1d11b351918ead6738fcf3cde70386d6abb527242a39174e29e73b881d4b36d27ec4c955fa4fcbc215030e67e033a68b6b5808e0992169ed1fbd4bd137f0257035e14a6d653e9a9fb925f6576a193dc6282f7c29a6f82e02dde9a1107875e38ec0b275f6239f4327ac841f09053fbd4b958c894cd6aeb15bc914ae631a220cccd3b0d35fba357ed034b00de1cb3560dc41da13f2e782672a169801bc46f49d86ee03dd7fe55dae79c775610f09660989575e853992693df3458d0f0a2c0f5b974e5c0ab7122e6c7e97188f44d2b80ebc2965caf5b11dda1bf705ffa4e8fc1200166a72f8ba6ba77b54601e8bf70a652fb335615e7ad60d6c0df411968aed0168e448eda5198f780886c0814323c616d42b42d84eed03fb7aa9ce1584437c7d10fd2ffabfbbc603c810e89bac01f29275feec499c643eb77c121912c354f8d1777a4cb1dccec39c2328ee2eecc8ba6ae8c725aae18f21d81585ad2890fbe5c8bcd1a6b9f6ea72566090f20baf3984178f41c9251a82222b1e70343b500c55c70098b9ab5d0dfe08d4b43cef23630261606a14662e304873ee9a8d5f8d9014b2dd8f7af83b8a8dfa57a64eaaa6b9b125817852ddb70c216548afe0dc7092b26d1cb2dca29326ff28e81162ae03bbf112bc0750f740c68d323352ca95f92d5cd2c7b872965b2b4c7fc295d62f11407b61ba03783f920e408d5fd0dadcbab286b55b061b855d12d841f5e8caa60ee03362db1b6d2c1ff1cfa02d6e82c35c57d5beb2b0866595d7440c72e058f98a9418f86abdde71b352d1531fba52035b42dbda41b5b2de98f3cb76346a35ab3f2006c2875c9f977d531ceabf5b28adde219f8908431717f33dcfea93b02526b0edbf7bb3b36f0218389a74e29e8bf5d783d3ab83065a38ef63ad4889832151b6bde9bab6e4b008d2ce03996d35795e99d0bf4a3f03484564fe26badcf091650def45e40ea75cc430e498c9c70ec1839274bc20417f90d776a1a52e370df9d17314b8e5b205b182503251f1c245f26175342169db628df3535cadfb11cfa9204063b44b853addba3add57c1552fe91cd7fdab98dd81faa1fed9b8e2a32d65fda3b0b6cd7890ddfe1e54f3b7a7ed3c48e2979113f2eca4e5b3075c7404f717212d1c4ffe1d8d0a442845bf355bbe7fe180ec8b33ace3f708a883f866c0c2373ba8ee8bfaf423a113b227e1f88045023515412652b318cbf0517cb6b3e4a588e5a73859d26c117c0ac3f77a59a4554955485821c01df0a078a8b6543231e4455ab9456244bc39192470420489e7982ffc90ff3e8a203b23e2862654a47af8bfd307e6368e3a9a69b27c84d134c1e0371b66c6078aa009d1e53d9773ade5d64b2534697f7946b2999f76862a5aff297845ba2c847652068e43830927ce58656f035dd13757a9bc55a92fad47e7dc5ca8bc42dfa60856cb82473d6dce270d0b65171449255c04d206465570f79780ebd2907a216f6112aa96856adb666e4afd02cd943a249c0ce948d2e91192fd18e298f18a7862c9472f290e9d9254210e823545f605310e19acbba0c9c235e6105c86ac70a0c204b945d33bf9cf45df376b02bebc790b18cb3d422d12a9918dc01c611edd7db0d5dcd9db8ddcf761e2f6aec5ca7151677b9ccfe9cd25c53e588e4e95de8389adf0f020cc5fdd08da836c33cc4503790105054517c2b61e290b1818c6be015bbbde6b6dc2d9273e54e1488ad659b220673f01489d8787264733a37a53295a8ed7e881a11259f976b0f79d51bb965e4e38c6ce0e729bad5dd9f898ae5ec812d2e8e5622aad87a1222afb9543d30da37628d099e0899c459f131e8112cc3287084e7cd2ec269db488d404a34440be0ef505a290d6e4c5941695148c3839b443f5d48a96c63eb01ed46bbd60e9b57e414c2860e05eb62041290f9e7e794f371faf842ad2c79d32789c70ebe1197851816cde405f772f117b3c0f9fc17930a3ce8c328c1d31b4e48cde913d6431d8a5b87b1e4e9fecfd1c4b3d4fc62191ad4c2e049331e5d75a652f2420cb53040acf70cbde66d51e719f7a3b809cdef7c9d7c8f3b10b48c9db2cde1d0827b5aee59415081fe44741be81c2421ab4c04d89f56f76de64a5ef0359421300ec013edd52db215cf5f0b1ccbbf0c4c47075f3916d47f90970d15cddb0fbb65e91167ab478effe40d20fa8cec587a77f588fbd8eb4c18bd13086172391927d0a06e9d36238e2003f92acfefd64f09d1c59b18b706be370455fe66d6f5ab9ddb85f48d64b4aa0f8ef478982dada015d086b7e6f5083c2df88fb53a64057dea6a9c570a5e42a707497653036756e95b5d84f8a8e0baba9ccca6e01f5976f7c5a7925bf06553a0262ac52c3b8b2031cda6e8464921f6603a30e454be13c47ac5b114332fc6fee7c7a98b33853eb03f288dad95996dfcaaee55a8d37190b470a24c9071640266b96e66561590692dbe9c2466fc72fad780a9eb816457b3eb03befc8e65fb083a595b5ed67f88047af5fc8d04f9091776a10b5e428b4e54a2e5ae27ee870bd5535eee39bc255c745e9c2cd3f9b850847d7e706dba8ac5a351b30c4b53e598e97e5c88620a6bae1ec3be9323c7ccca40ab85b37447d11beae646886d8fa2b46735a81eaa7b4df7ee08f02ecc35f9289b44dad66bc94c617a8f0c49ef31d0347ed706a006699f1ffe21c1523d7d4061d2955da07d58bf9936a73927bc249d5dfef46b43d037332aca67b4f4a1ad398c220f89e5d25749607dda57c108a7843c63eb46b48f60899b7930f6b0160ddd9b61c38f7ae412a0a050189ba3fe000fb479b2b34f6e15abf2f168dd589db7afb0ec51b1a745d85686c0946cf4242d3783e81d6366c35dbc0f6e12d06e09bc7003272bbec95dc6826c9d8636c94743ac98b47d4d7c432cf6a92cba316bba740799f1db652031ca4cb2b302e5ca0311c844c4cc76e0b6614e854641693b94bbd1962c1335eeab76dc5121fe7544ed875f4c1b6ae827e5f307d769be673d9b5e36d5c2c93d73a7a370b3c56d91c2bc7173360aaec79c5a46ca9b6e227909faf6cfc5632a58c7c3af4764e24172c22198a2aca608cf3e7d972ea8af9c18b8c149b9f3191bbe19a43b6edb472ed02dc1f3d0bc2287bdd0b9afd13a0d2e4d247dc6ddec8e53b6a768fc124a4437044a58ba986b47186939b09cc8e4bd09a62940c6c8df8de40470031594105670904a41977d9f20c532027809f10606a15e13ab03b2273a9888941e88f0dc15754e7cd05afccd49ce46c209598785d1659b46e111bb2c77d2af4a4353a7b45622d040b18e1cdc2a968a96754ef1d148775dd65d93922eb60336c59971ae4733db2c334814ac757594ac82993a48c49e1a5292c01bea3770ecdc55603312825a7284f0e63e49d253a253b567d148e2143a82023e1f079b3ff014e9ce60085a443cf7ba9ce723bb3fcbcb408747edd6b73f5cb87e706246bce7bf2761cdec151832b324afcab3fe8ff15504a23408f712ff54b0593ae70af2359b11f0216d2d20594b15eaf38f618ee813c6e9f09d3e33716f3e3e8e9564e510522097d81d567ca2516cdd206bc27e71237607c9de71f5f50228211a139240fe43b029d20f9c4ffa2e0ea803cb1bea7387c193d09918969f839465423250f31d148be02bdf3b87f935b934c653d3005afc167acc632587123c26611915213613ce4bf64a2460fb9d264356fb233cc76043bb4316e98eed8ffe0cd1daba0f7608097c6e5f0a88f4df216d4aa6c3c9ed6d3fb056ee6d885a07175cffe5fd8ec728552b2cc617451597aae0da2374cec5eb5978b8953993dfca38de2bfb439a218c846f00294f2aac1d7f7d4be860e1c53e952bb0b37b72a9f68ec7625ed21fa6838fd491d30e0efc82b60f31e52eb3140f888ba0d747f90dbc0caf7256f89a117f269a10c4f82a177af75e4bd57a81aaa8e75c630ad3f37946594e829ebe5d53621f9059258a898ee600180e8041167151d7ba65e95b629a0f0be2c094d7a752853f0b4d5394d920252b096e993c7390951ee49264049b4acf320574d402894fc86ea03a27ec2b337b938e7b2fd9af8abf04192d2e9549e084f5833b839960b57e0a28fe83ac6887f2165c4105cf59003e60fc74d4126cc62b184d67b1448bdab78fa6901efcca2a97b789a5a7986da95a68765bee96298122b9ca50d03e07e76faac3dc6488146520a175197511a2602c295034c60025990c80481dbbc757659e3f4473ee1aef59e9d14e20e3ce072cd5ed770ee673e3289ad49bd23cec1777c4af60978e6ab25d555ce5c2e3a70757e696a0a2c43fb1b3d6dc89162d83914918019c08cad0c93baa46b64dbdb81eda630bb0a1901a8d7dc158e53a02b1fcc0286cfbc4bb65daab1fe95b9f2ee029ef1f6ccc8505dc7265c1988816403dc33a5e8bb4305d2a6e7f792ebc55e14c44a45467c91c1bc379ee2dfa5373ba505384768c9237c5f5dc7f18148be09b6a02122a1743f2da301081adc1344b748157c36ef04bdfbc8d17670d410009a36f4e8712c8459755cf187b941608e51f892a758a51abba5689a669c80eb7caa9c7255839107aae2234be9cb6fa1924559d92822a959e0501e156b94c1bd3b2950cd3a461e4d29dbaed062808a85edcabd133560158a9ec0fdbfa36b50fbcdd95c2dc22ec92a486d05ff5a94a3731eb586dfb09ed1c1c384aa104444bbf551406f5f8bdc10a49c09611334b83440c56a20d9c6e0ea42c9b4485b08d2fb3d2489674d20ec0b6af0e50cffa548b0f892d535756824c7bba8160094d478bc5b6e10e152705a5a9d9d329c58be80a912670b7d4ff758a1967b6b480fb521bb18a48b2e802abfac95caee15b221d6b9dd72768595584c5d7b0e24a8d6d6dcf498a9f262b7934380ebde5907da4dcb280691f5fbb672651bf18c3b93690e5d188aa147fc92cef1abc3791bde43df2105904527f987f176559e295ff9850790dc71bdcc60a1d716843e8535f2dca2707041a9592b0c2d287d5824bf74fa68368dc07a2c6d541e0b4117c4186abd814aef8cf00467628bbe967273c36c5f126e905b3fa7b65575f6e636bafff7d7c9d0fe371fdfa942398e63c94404ecd2320e7f892bdec5c959c6720a64dd0090630865fd1541b3e063c6ff082ef50ba212aa5eb0628bd135a56da81c83544949df002a012394738cbd39dfba21aa7784ab98bcb8145ed5902e4218613ba62d0637073437236e3a02a7f0a563cca9b3a9fca031cb180551fbc06d8316a86f1284f3ffe84021317beed54cffe815526cea9b8a162b01bae96c114a5ce55147d500a20d5201611652b534c274e1d4a26220c08a146de942b1454b7cf377560b4dd24aa4c114b24850f40521b56d4d19e60a9fa634406fa0fe7a25d46e14d8eb0d30aa56997d3b1fa4eb55a740d9e107dd1ab9707ad4868e8b5a89d16605911846c8e74ed420ba5f86a6c545b0142ce366f877e939f336fce276e4313a02409f906bb024e0f8ac7d5a1737437b2473234ec151b6f412a89ad1533a569e94d2d44f84976a038479410ba8e61fa04d39c626d6a042059d2c6b917a375cf6c97db1a7fdc873a5204795ce9293986ffc2d4dbfa0e494daf1d63dafa2a26f0ee97cb1a961d78d49bc0d99e493b2e566d43b34531ae761d8b9a0df17b18d5da8e6a433aa5fb231a92c246671b1c5836063df4583882a64edc7202f6d3dbc96df487254770585871d0d3eaf47e08f431b44d27b73be881e480f62f1d469080b89ca4c2a5f1d4930a172ec3089792a570ad338456ad57a3e500f1fb924af9bdf2d89de4dc5015e8ee764caa34816fa3861b77b3cb050cf29058bf643944211592831d6ee9b7ade0289c058026ee947d2d4db1424c774bacf5426868fc94c6c9cd12f5bc9469e207a85df49172acc92e03118df9c8fc6e5fad69adc342bb4eadf143a387a6cab1d1da0a1400e4222d4c945ed5afbc2f3a342680e09908cbab2f674f79d9cf27c0ecd7d57bee0be9345223403e10533e3f60dc0e665e8dcdd03f6afae734beb77a56ba02e1e46ddf28dbbeaad342b7f1fa5cf582f3114712eb278231aa91913c6bd2706a0da6a18851bf1fcb1a1b3d89ee50f4ee650b2fab209730de2559875d97c61a0a4efeea0b6a86e9a72dab2d2df54d0bff2c584e7df9462a8864ac41704ffac85733e7f1f667c628a177224b6698dc94203a8db7faef7e7d7b05687b15831f4b14f30eaf737ade0ac8e310a39cf76e340dc65e036611029cc51e2971660dbbc98fbe1885a4587cb58e6d2aece7b8c76daf205ea0edf9821dd9f2de3b3b5b634bdd4f42134f2cdf9b9d8c52dcd911dbeb2cd82d6e2961a6d7620e87013a84c6a0f7d0a7118205e24da5a598a11d242ee2378c8bb0b81e69385283863037eed40550fab0305875f059c04245a42efeefd62a67709a6896a11d50d7da084d75ed6d0005a03568c3119d15d5264c9f98ee0b5f9accc813d8309c5c8bcc1c75d0ddd01a669061987042f057a8a0e0071a7833448e0a1940d437877232d001e2c9b5e8ad8321ee1dd02a67e4b3287b203707456c7366530d18991198c25ce8034e4b36df61cbd4a873c8f56bcdc1afbf3623c016765dac1116d58ea98c610498548c45280342486471aae59829761fbbb6a202358e200d3ff0d11dd196c51764a9e4efe12a967c4f07e04fd593b20e406c023303e2c6f4d94d9e014ac8b4aaa1f90d25788eb5d22d74cbc270564891909d2f635927644253aeb6fa2ee0a0a7f818bbaf40e00c8974c478ba45b9f1082aa24a14b0a95be5b858c2ead63ccd033ee3b1eae140ef4a55c3e69bf67b4771b57e4efe92454f7b1d9a2ffb18e0dd284226e79dab7896899ba1790c8e0da6320f6eec4740884076211dc035e1f25c5d667f772be8e37890a87e1c4cf70a00e660e33bc5b9203a08dd2a7cd8748192ef443644c71010812946106232ffe7b59fc70a5da065e46cbb922e25493b17858806e026e9003ded4621c6ea31d37d5cab5741311a936d740780d90f12787d885233396271db389d3f07d175193018e44aaed5b0cb31a8cef981a1bc5a159ef25321dce765f2811b7a1966768a504f2ccb20dc405d913fe11e4fb2c5f323656b8ae6956da2085860246b30e6e5e0c135ad40703afc2daaef0128221b8ec984d130ca0d7cac76a0e836f080a4a4da1e9c1c795848262ba92387cb4e0b8a1d4f29055d749fb6934346e61cf1c2b95f8b86b478bad7bfcae81dff6ff4c4e1fad98e33454babec921126dd17a8a72f887a32c21f5e73b84ac6379c706d6b78d2ad0cb4850638d5053103b0ff42762d3cc41f9721d9cc3cd73fe456e58c2ab9cce588af394a14bcf3683bf611c51ef2cd49ab8aaf4e67a5bd0c3de4059e891a643b68c74623c9168322f22d7818170add4cfd20ea94bf68f3a03f7cabc4e9a256d6f9d8c5c875384ef80e1ee588ea40f9635a94316d712d28a09c20bf3b6364fe01efddb04fbb372ab864b20068fc2fdca68fa8e66cb521216e8b4d0791ba23e63bb62b67376c84cd0c364f5e90aa8e472f02a5831c4c8f1edcc1a849a44529542f0b6de1f335a78e39c2a30ca2810aabe9271ac3d31ecb5fb470a693676a68a368219f61d4474bc2ece8f977caa040cf7a0e2f99e7116e13060763f1bb645de99779d4f275f1c50f60b13c677d41a3c86481226dd51600891388d8b158d191bcaa89c9a9a98738eba3986949508841166b2b4a60a36f2a5dc28e72e88dc320d2f8c97e3e7ed2d259e4c5a528974dda497948191bbec2e642dac4409ef4158a0eb409ceede90e318137bfa48768a2bff4acbea99f221d44bed931a6144b6f188085e4ec991e41537d588c51bd6d2c635442fc3c9e60195c4666af05724e3494cd5d0af26a61bf38ccc98d2ff80e22825ddde8d777f44e7ca78206c7747ea0e968282105488f64e2a168cea86ff523223622488819c5f0a9a6b9a7fd80759817f33ad9ea0ccac59d4b8646414e5bf84552f110bc8e83c4d8026ddc1b19bfc5002697f5bc03a9f54cbf21d9f4b40d65ed14e7753074ff50123e2d9e28092f0a6757aede6b3d73b8928cfc6b96b2934c2aa48eb88cc5336269ffe37cdd8440d9481576e0a8d634f34a2b0155f48a7e98665efb088d9957fb394b4d1512f371b2e7f0103105961e5d24a6478aaaf4cd4413de3336b08996db0526797fc153892bf53af6cc0d2dd7c5dac41a75d92d6c438b76eac325b2abc01f8194d0b916be682bce8658c6bf5243ab436e84eb5340e82ee206de118406fdbed6cd17cbaacc693572a83349a045d913ccb02e1a5db2510b9aa2696b1406140323f228e7bd8d971054cabde0db581f678ff14a07a58c12b0d44617118b1b468c2409ebc3f5c54affefca9f09135c042f11abe6b0a06c8a6697e0c46e37dfc7568c4c9319825f328c15e8934571b383a0de356e547085405120b8d31c6556ca66694dc78c9ae76fd4209b78e0ae96f229633724a0dcdc53499f1472a41282512e99256760d4c0b550cff7c41585e30c9ea4654d1a092d99e22a97c898a4af29de0d88e100361e11625877999c6b32860e3494c7513ae95545af5d6507faff50116e9bb46858c451739d4b1ec161bbd9b1fd0c4828949a80b5a4ae461a6be41462cd882a75357c4419a1efe763d6de05c64857c461a4e266f227c80d10b6798af4e077efa5c70c426355c28f5846cf193d4b0b87f7dab9b7a8713fc9a46864ac656e10724572c78613c159a31300f42ca50f9bc4cd7de006ef0c983f700f4aedd792b31472fd8f2c4df4b7692f9816d4353584ba81aeb00321c5b321a57512f669ea56951c6eb1a8274463bfde7e926775400727fc330e87fa45bec1cf7de337dceca0e0968ac43afb0ac59e9879db764c2fea3d1ccf1b03db3f241b80782a980820d8aba2657621952ca190888f10d99071c59454fe3265fff141a2d264d051190ba3af48ecdd2e76198a98dab86562a040af6f6345cd2d57f584dbb50e6b5962726a3b4f8e6442835f0180bd89eacc3c4e503b508d70ade50e1380f7221fbda018e2b452fc92fcbc68c2023a7ed80c7f5da7b7ed8fed0d8f0a7154ed79b13ada2d1473bd7454232fdd474bf2b367dcc2c80ce4035acdaa935920ce424e074d0b92bb3c0228dc10b203c6ef98e173df70d33396dc690fd53d2b55714ec47df9b43305af9d2f1386cb7950001ca172d26eaaec16f601985f92ba529b4fa61a21c1fb4eeab4b20679c14676b72df796322529032b09040907099cfb10c077b2761310d08114d7b66a8ddb3e89807eb20e8af2e4e049b49eb86a203d3ed8950076160a1405fb9edbf7c4164610bce7f23d9185826905ea2651e5892b76f5085d0844ee68074313388476aebd009247fbe81e1ea4a2dc0cb6b8f375a0b8384994c9275b1d4871a75010bbbe29ca0f7090e3cf771f5059827522abf31fd7cfcf20339de77563cc3cdb81c08761d54a9269adb5f36d0f61177b8e042225c764185a98887d1685e131475c2781b015699b7d7976131856ab48c909093204b4f2832fe407425ce99c0bb1f65efedcdc2964c519c4ae5494eb89e30f1004b00b08d85f5bcfe713863d01a2eae2b2907eb1dbd1d6646de5892310164ca215ad759722489865815d720c659f42cc429dc4154bc8945d1d282e5745908257ccc7fde439a544d383803743d2ccf71a6dfc089e6846a4d17d6d4a6e2b6d1c556966779630e9ccbd0cc15309d2a9004870fd47e7e9d88f0e686400428c284a5c61236bf7d1b19f2d47552c39a3022a88e822fbc992431ada1251dc944124eb31a22881860b2654ce3881ac7d6787cbbe33c3e5a4305e84915aa061f2ed9c11c3a4165e40f9766e0cc32667e6db3f9c1c9be3ec4340663f893e3e3f1b105224a9e7266b4370fc741dc7a938c467c755492c0a86286794c8a2ec4758f43a4fbf1a077572949ffc8a3ed755228ee8e33e12ec2b4389387e7222383baef223d7719c88ca4f7e128fecb8caa5ec0401949f421655853a8e13a6e61cc2315b893e33883614349bb41b3aa9a99a8d93862368e518f88fec029f43d32f8eab70fc86e8336f57dc117d66ad5d85a3738b6e6d67e24c1607862c4e64a5f0393b911cdb39ebd68516a5338252be65488e7ffe7d0efa90cf41f1f394e8c39ff8f9c8b38f1674c37b9693a3734bdd107d6e38088a3e3744ce99ddc41aa62cc549185cb00a71dcaec8e2fc47767de4a41d97b29d9c948f3d5b39fb8efb6c1982c33ff7013a80ec8683e24af459f996213b0e8a3e2b9177441f1e126b98b21b6291caa54ce5230fa9441687e3528623b23851481223ee88717cc7b70c516de1dc676808e83ba24fd3c41e92fd1a39a354c85d11f4b169a07864cb101c07838ee088ac21a813ca4f2c21a02f40ca40915beaa10c2080309acd645dd4af761f2096e1780c22cac71efaaed58a1acc0d489021a306473c120328ce4416154fe85bb358421c0b8e4867fd6a07fdf3b16b5476c9486326c836c4197552df5022eb7394c8a2de2c225306fa94814ea3b36f500867d4af76ae261b29eddb0760c3f51f3b5a7b07a59b754060378b8a94885d4d833ea85f4376f5e3ca9532938c06bdea87f2aa0acc4c68bdf060c3a38dec5700aa40c0cfb3fac76eca468d31bc1501b8e24e20709a7822bbf8ac5dad7c0396816ab9239dde430bab59d4e7872fb963ba953ba65200b03009fdf4cef9a61a7c8b9e51470103c33a61869f32bc4cf3dc6340eee81668f003ec8d7061d81867bb7e4d9ba558ee481f845d16521df55c0a7c63cdeae76c5577572a522bea9c73560084b91cd775950b5279be451596d621c5a5ea910473777b0737706f7a12b88b06535876493074f4e9a0ca2fbb54466af5d6a18a136a7d41f56ce59aa0799c7901888f2f33746b413b57d56df82a4a9bcd665464ecdddde1d7307cea72c77434f4022edd4d69534a45d60954649d30cecf792a763d4017b71ea056fb690afa59f4507ebab80236ddcdcecc4a6ce03b9819a15dee486bc5b91c275e9cd9fa1685b0c08c17e88f2cf0b0c035275e64585819b5117e4a294fbf40a02d2ad25741ee0042044838802a940ae5ca4fa6b3ce4ef4167bca40a9ed39a7af90a89547ee5968bd651728774ca1ad25cf76a3f723abfa7c1c2fc2fbe0e6a77761a882dc319d8ba4a41c64b95cd925f5fa34c815acd26264f960d714523df65d87b018a69b1d24b877e88c57d609236dd108bf65170ba08fdb457c10c4ea523a914ceee0061a64dc5ebf917c963616dd4309008aee485734b494a7484756fbfcfa02786fabb9565800bd380882f714c51dc136427fce203c034e94913eb8853f6042e98d1c1c213c86beaeb06d7a2395ef4c41f3ec6e6e6ee6c9719e42f2f37b588b1ba4923ba478b999628c74010df602a8ac36ce7f9327a3b0704e64ca2a99d516f567b78f1cf9aceafd9af3a924d919fa524a2d0e195d9bca35cffab1be114a9f05f02dbb7e827412ca5d74ad4150ba2395b5ceeadec3204f5156e978802e6efbec3072949a52565f9c8f86d6379873006e58f0a17a813e0b3e7ed2347625e58e743a084a2048a9486d334bb28b167d327653330e0ce24ecacd96343593354dcdd8085457faf35f6ee8a58e7a794aa1659ccf137c1304250982d27520a0ef9e1dc5642b2e056b2b1e80d25589b30a8e47e41179441e11f5e91e910968ce5991cc0cbed63ab65add2b6bedb9a33e6bd775d4274729ea8867c760861a32cd66507c3a454f87e40fe9ed75e6119980e40fea6d7af6b122f9a6b117458b19bc89d6b1d54a4ae9995832c7712936a8a25492f41155a453f4c54ae9ae9100c7563e2608518161ed5c3cfb20c0eb0c1ffd2a52b9cec8e4f1453a268d4c11549307fbd8271f1d0301bfd857ab7b977e640c04f9aaeee313adf8eae306afc333016c48216c8074558741aa60e5aafe00f9aa9d38d6af6354df2175c857f50eab02be6380eb4718bef3611703a9a608211061503d08345812d4a9f9a48fb155319ab62a44fcd256c54b063cc7144d84a85a78140bfb8ad198c2f0e60c56bc6298018611430d94c989649737d90d0fcf4ebf5879cecb326e922cb5b1ba756964b5e2785f76a29439c66a6b15c515eab58dd49ed86ab17141b620f060cc55dd295eecd1ed97658e386a391232f412ad95f669ca5eb05b4a902f4ff24465cd1f98b2cdff9c40d676e4c119497070c61934b4a0c1c6b7bb964ac08383c8f6e4346eef12688c971eb1049047416ac12d18d6025a0bc030c1b33348e9172b9a524aad52170a5627dc9252951f796c12f77b5946090a18f7823638282ee7c62be27a491e1287e5da1b38bbf6c81e71bd2c2c0e17150567f3025e6181153558d0c3186ddae8a10d196fdaa4e17eb016a70c0d0c421929f9c61c9da05062bae2067a61e28959fab22cb164924c42d8d0d5fa25db8ce086116a426d96e0300216416d664b8cd8bc4932021921364a405904b12163c90863a0b0b92d19218b24be5165872f6754375fbcf8927cb9e2e3e14b13dc08be28e1c39723a6b8385e96f9e2c3165f9270435ed858bd2cc384142a28042f7dce6fd93d7934ed168179f2e4d14cb9daed9a3f44a03e76c3b6506efcf1b270d57ad78980abb672421cd759963caad3c931d0ae95a395a34d2827542b07d3428563627253ceb9cea5e7f8e2d9c7ef7b2a2781c9839d4747bdca1f52ba354d11382e06eef758726d8f3906369d82706e7279a164e7d5ed9100b53127b74a008d953c289fc00faedcc5137b4225a5e010324441238a282ba05bae0d03e853a3e8d4c50da634c0313a786f88be3040f68d49091cba204b50bc21383568360a0ed5cd907543746d51809a045943c88d0e41d0406212e4cd1214364041d2305a12448d5b193057b494c0149d000c13d604d7f4b20c9821286084bc20c058712136ee2350f27342b9e5909148f54c1d4193afadaf7504487c859283ef50d49de3bbbb8724df3d9ce1c1c8a9989bc6bdb2eae27b0264822728e0c1895fad92803b247976ca3ba4b197bde0f03c8f6fef198107c67122abfb79da968ab5dd84f6040459b520ad2063f639d8b53af90080aef7b49bd0ced3ee2305df945e7422e1919ab6d0dcf5efda5ce56a3757575ce5aaeb9b273333e32031b96b8e72575d3c3e2fadb42bed8e725d3bce769de5f161f2dc6de8a2bbfb5db9bb7be7ce2eea0c935ee70ed6c6e9cc715c9d7704bbbbb590524aa69372949b2796acf5d65adbceaf5cc739915266e9eeaeb6bbbbd9084d993265ca9429455faf40ae564a8552319a5275bb9b40c2c90e5aeef7d2e60488499333ca68b2268c6b5fda9aa8f958704f2f6d4db8f8c2b8a897b626567ce12d419b6d395a6a526509174d9c34390a6a82a4091745b4a4265c00d14ed024c851cd2f68eda98932539420872d6a694ca1c59b6c98c2e8471054010155395e2fcb4ca1a655548da074de9000bb804ffc44cdc93d9162c5461b33b54ca339669af939e79c734e4b5bcf71b4aabe02f95afdae61d3218a155d01ed52743362ba524149344127929a9a2d892f4e0891e60c255c60b154460e63eee5c1092e64e098acf1ec9471d8e239072ccf5e576650904995a74e9998712d3319cf5cc23cfb08947b9631c38501966059b274c318256380a0134e509382d482b049395052452a48cb275b63acf6b69546be26506b0dc39861d4adb79418569da3b56e2b1599db1f13ea97f5945ba04bfb5107942e5747bcab7e592cb7863ba66e6f7566a95bea96baa56e3d6c778c8d79ebad961a86598f8d61588e5b8f09752c06d42febd60a0901752ce5d6a5c4f1934be9deb527715cc580bc18d05b77318cddf39cd0c52ecf3dcfd3416bdd18c6e3adb79018c63b6e5dc7ac632ab7507404bd75d93a84deae7ed401f4d6555d6eca553ec24a40bd496cdde40eeb2d2f57473cb225c78fe878ca77a6eb78ca87e888ac934b594acc1187ec88ac93e892a944964924d2a59cf3233a3dac737461b78e498fcdacc511c77b5122ecf6d653614fbfaceb841d0471a7332b85ce530e81ce53a26c08702e5b88ec97774acd5e3ecb52b77ed9d42c45c31d7ffee35390af75eb97a5e17e3eb63410ba9c0642b77ea7f1823ff93c29a07f84e1c71b259cfcebc05338a6fef4e3913f85e3e94f4e4f0ae07e8c013c8563f727af270574ff811a08ddee4fa1f89f05c193c94f21076f0a49e8fee49f374f8c32f9290cdf148e22f8937fa6293d8b5ac1e4d4bdd99fc2154c27989c7af5ee9480daa106ef85d2a644c9cb5307536e0d5b50bdd7a5e83d718506c514124bfe6c2f1cc22eeb35eba630a52648eac62e9bba394b057659972f017659efe1511ebdf556525b6f2db5f5d6adadb790acd0ed97d20c27bad0c11bf17cbef75eaa76c7d6ed5b3786b15b8fd5524d56604125b6a0fae95814fdec52f4535ca17d3a1d00d0f5677735fb6df6557341e32acdb935a9b78e9968d6d29a54ced25051e4d0e40d5544a39968261a8dc25a714d2f6d4064fcc853b3011d99d25cefa50d280bae897b5fda80a0c8315b799135017db9a113e2971c225a4981c259d08ab78635fc924afc9235bce197b421c92a083bd012275509ad034a52030de294681dc4865a2d430907a1e1885a306a98419220358f096ef81204080cbfe48ff6e264d1c4cb1a8bc6f55e96f192821c2fcb3031337dc0e3f2284fea3c2f4f0178791a325f2f4fd6f57d5a4003e061601380b6bbbb270fbbda031703913ba4f3f04bcc9d5e29ad3e68ad22b52b4a296d2a59435a665dc2706262cea494a64fde385953ea596baded3ea42baf8ba76340e48f59a45f7c3aba5c178035773ac7d9dbcd5a6b2de88361ed2b9e9e7e719d58a4bb5b917ebdac28c22e5a6416a9b4e9ed76b33239bf313e90032eb6d6d9dddd6375aed6ae768b5c0db5b3b68c76516f8efa844ce79c9373c9892086ca7155544356e6da3fe9aa382e849d7455efaf035cd873ced97d1da05d08be7d1e913baa53ce8bcbfa8a0abd5dc924334ba6db582ce4931f6705d8f8c18b353b28516289cc4b510d4a5280c6133cc8c639c3a0eb727ce7328def9c766ec57ce7b70635dfd950e53b578fd9e3a75b91032e77ecf1d36d904e1cad77ee516bea4c26ef3a21ab2ac43dc08faae75c023de794b32539c173347859d9af250843a90e3619c0b0cd00e669d153d7ecbb59a2c0d69200024ce3ba3cb0210942f452e59393c7cc8c67674f0634556431869929d4fcf870830d6850b1e40c275c6683b1831368801f34b9a3cbe5e3a76b821980fcf279496f3999f589349318c6abb627153af738e7e997f50986633efcb26ec1ab5ae5f096cb83373de617a5b728077d044d3ee6e07c6c7930f6f851be79eb3d1d439d563cdd7549eba0b59f68ad7c592ba5ac282ca073f0e64e24f0b344389915c0db2ab3d2ab5b6baf3d7a5b431eb9c33a0782a2ab5fd6d15c2bddfa24923f3e5368fb555b565ce12675417cb2f5fdc009328754a74e85f48b1b72afa8c278bd03292e5de38ef6392f6440c1cd19c642c173eb567476712ea568334309886435a43f3421e3380f0abb38294f66c778f8c5b9b5975b01b9d3ab78abcf8fac8ab65fdc0755aef59107749cb07ba628d609fd9ef77ba0d84389fb533379bb38ee8bc8b81ef2c787e35f487f9e70c417146002bb38b75e02bbe7f4ab27ae244802cee9049a405a827c4991f029c80ad371dc0b2deb84e99e7b3f7d3a618a52ba3cc739f907cee9394e38c230d6f7e4a348a03fbfbdb00b797ac81f464290b10ee4c8b1b373faf1f423cf733dcc4d5108bb38a7cf71415e11196c7179ce0cb608123623663f824b2f6d43bcb17161f3a797362e6ad8258998b171a9fdb47129b271217afa52ee90a264570b5ad0a046eaa51923ba4c2a6ab041fbe9524ae93279396b5d95e33ab7473ae73877715e27d74964dd73229c57f14875ce3df1c816161f6197853d3e2652ed68725e7d9691afe9f3cccf99e6e71ce3674dee98b3a6420ba9cef957f35a4dae75cf87e694768d1a66de0006b8862c76764ef4d9c2e290cf39f7f1becff3562cfb26f8797dc43ae7353cb265881559d43de79cf32326914545560ad6ab0f9941fd9aee4a23a94c0bd25429430a19e756b4ee3109b8a041072e4b824023ab528dcc137d6650f59a4cca38efb8b0565f49590d53b6454875e93934caa8253ed91a75043de308a5cca7e3083b2ad2e420f3f9baf8b046cb4d764302203c9145543efefaca594747474788dcc1def2723b08bad4db41ff26fdfceb36f9c7759676e8f967fac0130aecd0fa877373523a2ab0c31dff76ec7f37baff70ac2e003bacce02abe7f897137639604e0e9813f67f15ccc9116be874030d32e81c2c0c2ee0158e1b3baa294f1c742d548ef78b3d45ef2ac4d1fa1cb187dc31bd754b5171ba4e38caa4e77a271c47550b0dbbb8a50a5b0058c11d75cccc3cbb0e1ac3c6548d09c3c6e9ed73a60368e80bd3f8499252cdccd017da133ae71499885d3cc4899fff74bfd7a9df9b721ca12a54f9ce0eede80e5585f2144447f62be5a3fdeb392e39a6e37acebddec33a661d6bdd3825ea4876e978ebc62e769ea39bf2b1754b39bb8e787b306eca755ca54ad9942a25b28b95c205be72221788b2dd87080ed16725de7eb1e41046699bc2e83b70e3c71eea294f794a7472f4d2e6c4e85d2f6d4e825ec78c61ec3a9230cc7b76f149e838f8af2765d42882f03f0ad6d309fd384e43947f15e44e24d03f390d41ff38b03b9d50ff731a9afceb407b3a817bcf6968fdb33edebf38a870bce0393f852e746db4d1c69b1bcff9178e47c6540ccf75cfe1f071b59aa16f19e7e3f095cb538738c2c9f90b516103b4a1da4ca2f150c7a64ccae41214a2a3b904858956b55a905091d16d22cd808284d8e8d9d724b56e2d3542badc51c74c88907e69b963ebd66ad6b4925a4b0ce3d43c7beb8dfc31ad3d411750246df1e2044c58219bbe9231a9823266524c19634c2d09d97429a75b39a7a76a1dc3e1ec3265edbd3a66dea364c975cc5235f9626f71b436a563d6fd14c78b34a652475296b384439ccec19b3ba66a0ce3f1eca9238671cf9e42c330167599ba316c8a2da87e034f8c6698aab12b95149372c1973627483fa66a2d35ec62efd2427a6edd5ab750042d6c00c95454dc31e5291db59a8e19bb526eeea823d4a9422849a663c6b016ba40f1fd4df0863c0949d552b71452aa963ad211a4620e8cd0095d21c7a720b50329ae901309ed39cee5845db54ead1744ada54868d6a3415684da80a2a5034f56d67a78b9ed1b70310beeb160845d0c8104785e849374e5190d494768d42025292dbd61a04abb688629c3869a5a6516a90007e12418a855c400bbb8c6ae8f86df3f5f335367506dc68449c872a1e76c54edf6ea1f04ddea2a1cd982e32951051691d48f2c250a91b751de8fba620f25aeca77704494cf277ef20fa6dc1e157c40008414542192a54457d7efe95388000e9a0505cda03673346b3368ce39e90f4dc86ec89721d440c34ca623ba12e08945523a447af84424e57c1a72c375441f290605b21b224f0fd1c982c8089a4c4774f5abbbbd9d258cf670110c0851619c4a73cd0c120009ee0da976a4e33788dc707922e2e386e8c32a92a5449e7ea55c47f439c0ccc9d297f239b2e40b1e8a642957853e2260a38a4cc7d52ff63944a32d24e979a7bbfb44a4224123d3690115328a6429efe194b3bb64914a87a52c154a990cba7ca3049f6ccd196860d06431888045972299e7d22573dccad9854192144e22bb871b8875b3059124e8882ebea4235430276d21d43b4ba9d71aba1043d7c3d43b351269ded8d5222779218b88c93d37893098dcf36e6bef6da076d38d22ae1a210ccbe28eb3c631949f49f30dd43194386ba71a58bb35a05a5b3bce1ad798b08bd640ecc219baa33c02328526f4ab811aa8694c80fa99933ac637be8945dc5ac77c084941fea8ce2f94913bd86d903bd81ba881382680a02f0da6813aa8b5f4501355da363cfbc83756e224eeeeb6c14c0d69d63e10ba23cf6d312680a02335b39664d626d2549a4b33a6cd492a0c614ee2611763b9f28f78220c9ed730659eb3178a71bd89d4ecb3368f6a18a0a0c9b5012951c17d70bbce3edd26c58a4d8a14db0f6b0ef0d3ef4bdb0f5d3cfb27bb5b7a95f414a4abcf1e9e50d8a08d1e000dd35dd3eff455101e664e759e9e345ca6d460022219c6fc7841c20443b53358c83469b5789a7673332d2389524a3da0de39d8558b604048c74268d38228a6e4f0459720592d2243873545d4b420514356f955bd4a9672dea0f1cb9a119aaf124dcdc2892fb5cee4641318266d3ec8f0758caf3e42656656bd6cb8ec758ca2a76f9ed6aeab0c0ea18a5046a8211411bf79ea7c9a3d755e923b6803215fb4459494d40f7744ed3c31a2a43c75d410c3465511c3c69d27be306c3cd59e3a7514110accce135304146df2a04e5750e494dcfd5c4047bd53c1aac0aad687706e51449c0faa4844fdc83c944bf4f991b1aa73fe23ebc2aee3441feb9efb70def98f4cc53b45dc1a28da73dfcba03be2b812bbb8257625c91d9cd3b0039a3c389fa71478bcac2bee4c81089575a24f0cb2ce3bb1a3b5e3c81fd5696a34c872bb97de7a00b407fcd231011993b2001283ef1c88fc21572d50c1776050d1b9945bb8d8d5b55253eee8c34707eb1c480a9c571fc2791d521d04a938047413fad53928fae857e73fb254111a64b9200ad4d9bd7005fa7d9ec83ac1e49e976072cf08750fccf7dce2bc09e7e58cd5222441b32476c6ba2224b7d997191399f53b63384533dc6666664f64d657133693cc82deda2cd77bd95304f6ce716ec7aa3b4e524d01ed442939cb59ce7296939c5c413a95d65a2b3b713422dd2b95cd49137a8cb8ac19934a4a788145186ae0d02649c626d1eef0703d58f9e827aef4dac457ebace133f3ec259576a5ede580e0ee923695e1fbb89f73cefadd7fcc3a3fe7bc737461b2f931e78dd0e92d03774354b16847969d3ca4f3d3557f5c77cef7ea99e5e5b135f6b5f246b8d2963c299beeeeeeeee6c96334054720c35a58fd0ad3fb9a5111d19050d0a4f10401c336d0de320d2754ec2045098c131a3b0d5e9327581029f190c615322a16e9d71ce54dbe098a1f5d4ffc288b9cf8511e81f9512635f1e30462e2c719b4c413fd386b396c947441f3e2062a92908d8e011cc2d092054b126b9ec8461f29f420460d31679410a28bacc551d66688e28b9624cc7c098209198b3f382ca105094992a21e8e64e82288376668d105e9896c9c502cc0a508206e6803041915c8a498334038d952060e54cec894f82650a2a8628da3268c6413cb17639a5033059a36b259ab3549c117628e001384ac0a2db47c21852dcb1244c87e9828e3d686872baa1822e9740619b530698868b20217c638438ca51ebab091219bb53068501a6a728234a8904d9f421ddbc00a1a56aa78c1856d8c6cfe6061020d2882b6d860858d6c1e756db89d028ba12cb02862088b2a3f5e6ae7db48c004b3062f6d58d030268d1f5d3532348925444c812508871fda28fa198117b623bef8210d2c537ea418b16143942664b6582201597e3883450b9e902193841543b5c40a14ac4132060d3053a41926243e3457b471628c27a6a0954903690731ce10f14045042b905893c5cd1a2a4ec8c6d5144f8839b3c51a2c3c98be58a2da1493971fa11290208a19324e50c60e3f4d88ae28138615316a70d14a3e80f4f0b45d61c3b3dbbbb251b1f4fed226460c315e50367e463004501343dcd086062d64a3db0db268a3444909953072005b2af0ae622d098027770457ed9c87b7de6e53976f77758ccaa48fab5a5151c4826ffb236598b45141f476f699fcebdc32ac9b230f8fc5f1cf714294fcc08fc8490447fb56ec425557913d86fae3314dd343c5ae28775479d02b7709608a20bfc78790c66d676b5d67dc66661fbe30372a806cd7751ca594524a29a59452efb84addd66ab14c97d58c3ee9b215cb2068369b2199ded1662c6b82d8b84aa58a72471b5ba95454a6104b4c39c011776cb55c1ccbb1a23c33ca17de365784eb816904cfdee2f9c9c495b6a1db73ae7adbd053afa7205d279f4a505531878a5d54bceca2ddd57881cc903c7b4e0f17c84bdb9095e7f8670c4fa178961ecc9934f452f2f0805d2e39c6d483cd085a0841362d526c5ab4740f25d6855a4a9b98eff3fe933ca75ce1a5f7b0141985efcc77777bcfa215b7e79473167d50a4536b67a3f13177cb1d23bfac4c2965cacccca4e9a5b2bb8a10aea8ced5ea5ce717945b755f6634aaabd26903457ee0930ceb58921a5cc679174237d7ca3dd53c21a1308c7656ca7ea712cda4dc4884de042941594353f39381a6b3198ef1ac5fb3ea9387fe88679f46fc6891d85927b4060a5012974167865fec6880d4d0d6744a1cd4bd61a13ae3a1aec67da465a3c69fd760830d381bba7cb263b25aeb0b0405b83a16ca17e5ba679a1c4e87eafcf4aa527de7aaef9c272be4970ca320c843b048e0fe4f4a296773f37015c68939b88abd5a61b921d0d58a79bab185a9d997e2941e50ff703c29a5284d1c572b1a9f64d84a5631cd44f24986810cf34e1ca8b476d7398d843e8e5eb07a1cf606275f8d7ae17482fd1aaafee3c02ee73f0fa4e17d9cff40d49f1efc0f05d2f0fb0f07a4a1e9bf1c1a7affe580295005aac01df00688035c7d2c0c5054ac7247bb089e43c080e53cd4da6bd43e297ffcce0a3b407cb2633217c342aefaf4d50534a4c0579f2fce3be4380d7ccd794ef6d33134cf362d44bb4d55f249867195d4e7f649f98327ff0f9d738b0673d22324bba419f090fca1c1ac4246318513728a298514cf3d21a550e2abdeac736ef95e74cce10ad9b8d3c5af669eccf38acea5f47ee56a0e9e9b0007537c5a39ca5336e7957b0e690e2d295f72ec45e682a36a3e086c5b906c5ab47861b7f8e9431790555659abacb236edf64b16ed328b762794efa06ec9a17c4a250d57ec6a3377a4406f18467d4ea64dca946972473b05a2348671480ce3359d1935246ac58d46714497a8d1238c6896222a4714cd37bd11d12825b011d7fa68615cdcf17617f292dcd1ec46be7ac98c2c3c545c51be89ba492b4142c4aeb1938c929a67492d7619b5f7188eb919179a6feff1aeb196c6341ad39867340967874fba15335160b843a2ed16fa0951a3fcb442a3d656b9bd57058d0e56be01f7a54d88273f8550c26f7e523183ca8d4aed572f6d5494f8c95ca85cb13d3fa9b8eccc9629b311ee67cfdb273d842dee0c73b8533a187287f4108cc24c157cdfe753fb094218979be7dce19b5f72ac1893d7f8ea3d6cd1acc8e0aa48b971d2538b3946773aad274a29d87d953b5068ee489f762014773a25a1ab0fdfe4ab022f8692e40a3e06278e1ee626cfd46dce394720f3ce3979fa6466669ea9da9c734e19f3b3c83645cd2cb205cda66d8a9979e79c939b99bd847ee639e79c73823f39073377501a340337b77b4f5a666badcdc0cde5327073b90cdcdccabf838b3b3691f5b7d68a2f4c9e2e5772477b9fa81423f9c3321a7496e40689cd6d8ca334b5336528936f2b5c4880e79ccf130aa08a75c85c9f1f19083493f4142da1c126690ca4337cd456be8518d6795be79270cc72cf66bb64261a5cb944463d18e8d8495e71f455cb96ede10aeeb8fa9e4b3ceb382623cf33358571a5b3cf5e8652862c29e5bd52cacb3e7dd638ec993ce69c8543d7d5afa57e49778e59cb021ad488f193e7a7ebf2acee40658a0f44d89c28bd9cc224f5d21604193f994829a5742aa5149a5688b191c04886314553ca1d2da889c0074213f04b8a89999385af587969bbf2e447faf24a93b72f6d579458e95edaacac61b3b2f4d3b18471c0975984954e0a07f1a6879f0e22d98aa05559b2b5081b2ae5669f0bdf27dd02fd741364983174687635fcf41e1f2d0f7a0091616e5c2e9725c9abd857ceb01cad1e36a1354deb2d492f65e022aa25cb2ca916c10647842c238594976f7efaed84d6088df1d3afb50981f1464a1018e24a9297362e423fce97bcc4c5073ab9d07ebae532fb91dafad2b6458dee97b62d4b3ffd9394cd373f3153088a9fe1b4a2c41bd1592857a5765238eea54da8879f938a500e3fa7509297362c4a3fdab76109e3b997362cb79fe1ccc2c5339380c816268ccb8a0d8bd1c442f4e5a50d8b152c51b0341626dc4bdb10670cb166d221d090f9a95434b9a1d602beb40d11d40d41ab2f6d43008d16f5d276250d21befc083e4bdb15a42bb7ff5edaae6cf1d3b332b35d19f3b303f4cad7c3cda47259d2ed7fcf92a2943ba48d08a29736228a7ebae4e0328088a52a495c6fd9050121ca1021990c01228488b0f2d246c4932b4b6f7a692362898d08a4ff5eda8840e3e3ca9501a2d1a8c4cdb555392915a2190100000000f314002020100a86842281502898ca8a2e7d14000d7794407a52194c846910a3300a32c418628801c0006008218648114d1500999781fff6363125a6cf805e0e05eb46da04f7082f61ea02130906dd1f342e4d324f63692564c2a0f49824f08f1ce2f529e28d78cf2d677aa9cd473b50c2026c33facf9d626b830623a289ed9c275aa174112f9245af42e4766f6386da9c63507bd6935951f5e2765185431ce021eee31f777fbc9e675256325b2093d75ed5b497f55dff9c9e51909653cf96418989acffeb9ec73233f9b5959c2c6f120f65e247efbf9a4aff5631d19f9ad2e92bd861d2ed1443d0516918f43eafc9c06f0f66dd2cad77afd954992fa2eb1d6582a1997548f63b54c747077829246202fc94f22859faff35f3a30fa27cb94a83e4ec61c9d7c7443aa217c0069490cd9fed335128f6fdcc3fb4a2e7c893e22748531702a9dcd310fb6ae5e88d2a2bb4f495b13d44111bdbd1366c2c5b33d0185c5bbfbb2fbaecfaea6782130bc3835079ead2e4f1155f0d74cdb7669b7d75d4fa82195d7dc4c49e9fd58914fd7086feb8eb72e4a7dcfa5170d75d647b2ba9457ba35ff444a80c9c58ecf473f91e6e74ebfee7b814d4c240fe8663c3212e66b128d782bf925ae9c84c3d6fdabf5eb9809f5e45ad587848cfefa379c422d7fc68515e388c25971eb8329b2cca8db5dce286bdbbd4635daec806b84865c3a95083282bd59a02d13e11ed4b4a1a1f902de8214abbd70c8a1a03f1d0959c222259e3af76afb9d2d03b06980e66ecaf62d6cc19ac602399065fb2ef490808bb2b0133737b845d93210d7def844da1d066454c91f4d6031d5f53840b4339100dc7c3e5a012f98a56b0f097ee8350404b98d0537761b33a43b4f44c6b524fddc1a5e3e107abcd53041a9619933916012735360630cb8552a5251230b3ad8882246effc9d634c33242fdc529493289a9e82605a18831923559d766e9cbc481b08ee559ce1200a38aea62c9d95483c24d82422587804ab9e45669a1d89056f5efb18936731317e2df753aa6d486b92972abdc770f65d37906d3605b8e545fe61e2a02672823bd06d0192dad65e1bf2a4ca240bdaf86435490e020c720a3393600f16b92d97ee2180b945570dad2befd1a9c354a293fc412d057edd63d1da33d195aabba2ca253f36d05d6b802b203048b9129fd8a4a4fd2df4285cbcca087378533fd0e6aa1a4a5d9aad8b3b23f10d5e3c06cb98c13007abac2a73c8375ae5ba7135302ef6f7e223576af1b48ff6685f4279861f7e3a93c260b1e9213944024574359c104c5029690c7ef6a74bfd804eb7a33be11e86335390a7474db65ac72452d1b95ecf9c02fbe4c2bbf84c537e9fb1071f118b2560085de4a307075b8aca598b6ab057a8aa6e26aeaa1d969a304b0b056eea120384d0595b7844881520dc7332c49165c20d6609fd246b0c512f81361cf2d167a4c83b5d38db1f5164856e470fff35271e29d1fb7c5ab6fd56b7418d8bc9b80f27b7d37c3ff46b67bf56e87eb1755a2d0000aaebad269dd58cc4b9493b030e3266449a1bc7b533ebcba9626f44048379622ddc4a7bb58367ede7183d5d8cc281ec334c65a6529b5fd253f7f79a9d8575128ea232b8b93d6b0227f73474aa3d4f8732266bee7bf27e8722be90c19fcd40987d4d01feac7ae450889d6d45e0e38951e46a6100c4dc24ef18e1730a7e7966b9b1b69867b362aa21104fb005d94202219eba3ddf95a0c21b6393adafaa2d6e1a41f2299691a6cfc501f84eb20c45dbb3566dd89242538e5bbfbae044a879ac05057b236f263a94633cdf9508acd0417589d0c357139a67a9a51e51b1aae51dd223e86b9b3558796366ca7ba87df979e0dc9fa9aa7c1b112f888825e4659a814b6a1352c433fa8663729f2704bc3ec549c483ea079950f08669c5baddf35f6a06f2845140aea958c67eb3a8b1562f28cb7a64747a65777739f28c14a4f5685d6b1bfebfad69e78c3aa6b0ece8d7ae35dd6e9c4c92b16964b2ee36966d5d4e3bd7e88f88596dea313f7b9d2aaa3b538ed2f5aeef5ef9cf603c6dbe546c78f41a0bd7a68048cec6e579e9101e4bd37a8c7a5afb1028f130f1e2df0088a55290cf302c367771a2bb58fa9ab09740462438c524f187a5506ebfc55358d5789a0d3889aeda79bcbf959e9eb8fa10a6f30ce2721f0d2a941e55e5f336bba7821dc725983e2486ebbc4ce8d6caa2ec7d29261aa1ce6d5b43eac36251058229afe4aff14ccfc17545e6f6b49436daa1a5180a0abfcb321baea9eff2400b5847944b0f0592864218e512e8818370eecd4ee6d4124472397ef9fe9eee25634c2329d48921188e2aa8b43cf645f450638678d77954f48f479773bf0caa6840d86e180756c37f21d4a7a8b991de923b8a61580c255f145cdefef7548efd748b803eda5506d569ee80e0157f86c58532ca53f60423856509974270b45b04b31e8844cf8b63d54ddbc7628b6796bc97358886d44e572bae4398ce8d6ffd194c00fd2a2e5639d16847e51202ce6e99e1f2ada9967e6063face75b742cf5fd25698652db811a0ac661226722b48722f50c7612062894e632a22a50548e2a7c975b55f1ad723d24a1d64422798349175005495ce5aa9b921241f7edf287e8e23138cd8d828c03ef8323d4255dd55e022600de2962d8ef38017041818450d5cbd739bdd0c4563f4351ee7540a3e729ab0eb8387d0c9ce74e24fdf947519d23ac0e2c08b3ee1c6680dd5de930c4171dfcd23870ba0a4aca702ed4c5940230ae720f736530cf775195b81d36ed1f7e6021a9aaba772308b71c19a352e57f701ae31425eb7d76990df4482b3a445d76b93a4c143774b961a85b13c2019194a96ec9c8abe5a6756092faf14e9298c4449fb4201b8780f7e3af9128a87559a543f9a0f564998bd043a2dd746f1e564a6d86ef46405972f99f7c1d840172718b0ccd00a534c5cc1562d7bf03bf5b076d17ad9af8c5d2ea596f87f28febe059957fd02cbf677beb08006c35c27a18c7508d03bde4ff1dd70ec920bcb41eb58cfa6388ab184d24e0e9e679ced0bbb826c65af0b4594936fc9cc50f21c2aecf2ef3bac3d4941e73eac5fa724bfc5bc22607b57ccbf362a853320afc4c89b550cda5746192eadae91820b901402c4f680d594408dfe65d84eec9c095242748b1cee17bd07c1931d763e70106695bd9d13cd044d06ff051d52f7133eb5165771c588063c60fd041024f1add45693a23d915088bb69f01069deacfd8c20f05c3b4736f9af716040ad16ca39aacc1741847da22cfa3cc0de04b6e4490ff5c140f0141037678e8634feaee2945877e5263bf408003059672c87a151bf7e8cf81b38c5d28a84304dfccada89f38aa5ed9aeb4882f1a100711e1e22ec9c822474b962c4e834fa21ef2bd85b613b927ca15da266d29a84054729c3b43c58a984c43e6366a0e17aeec7af0d96e460ebd76a53175aa1812542f343183fc5812e701d9acf59f1dc44417cb9cdbe0d9e8dcae7032a52dcc549977b60ae082c30ba20cfeed6f7e020b360cc12748bbd1a2abf23d5c31be61acb142f33193bb93a0afcca25bf061f50037740a06e0d0504d6cc84aa78c8e89bac29f90628684ea51ef2ccd20026ffefea14a133e444c39b2a98761e8785b2e068483dfbd0a1e72e0d4abc010cdc85e29e94d404e437dc4cdd2e0702b0f6f8f0306897324c8d921d49e6eff040c1ed73ecc6b47260ac902681a18b2b6c338f951fb7599ba9d3ebeae9ba6414da5328a558fb3f67a883fe991f7a7b0cf7ccb8dac09b5d67ddf0d3755950efeb33d288f09b480ea90d0878cfdbd0c87ba7b848341b7cd658e244fc743440d13e6a8e06593e698e884b84a89e176f1f512fa82985260d9a26080ab0000c468b596c43a2f28a98ddc31b3fd4266f8dfda766988627ea80eaab925b3ecaba6d3525b88cf790278d570f4706de42bcb10ecfae944e0404795071e0b747cbffc13788df8746b23b2238ae5de88e5a5ce119ffdbd854c2a44f014435c2d42e8704103e140f67809edee09226a844aba11e5b22c94de778e4c85842e413c0d9d10848d1679e3f5504b60b87ba8bedbe832920b42e9250df83dcfc1e74dd4fa1950ffacf3f3621315cef02de0521e0ef77a188d6839f18304d680ab135d855ba3f13f3908033dc824d8a5368c37700868ba0e4a4dede7d43e1f7d980c2cef7e1f23a672b1628b51be83ea355adc8a29ae8ac39273dcd582c7121b4d2d48511cca7ad78237f1db7781f3b0097125e4d14d6020eb0cd1c7549292577cd2dc3e2140c453ea1399231d0cacccf6db1e849224d47becd5b4f7957068be40d8d4bb6436a43efc40a8b950b7c85c84d99cd271144816ce35e79c3397b60f08c3e45b3fca67900b15ca98616966b910a211f228812c4186d659803f97609dbb0177c40ef49f29cecd091e83aab9985513dbb2258670c6578a76b0993dfa6daaae3fbaee58e60ab1dac8d23ed12d327d27280b1c006772db9c4c5fb4b20567c2385546f07e28ffc31ec7b93385a958a101edfc4c6b8cda0841ea54191923924ece4819837d2ec2b81f6a3eedaed4ab6d4489b1891b7bd0a0e8a64d34db0cc555dcc703ba783421fcd78ea278963aabc176d1d13238e8a032362a7a305e190157a1eafd31ca078328cff87454f9244540a8d9864211271523fa351104defb4c253087df0c5f8a5f16e9213c67966f958657d9874ff50f91cc3770f2e16813d438bf19448076a1a7c996e89757b642f1fcbcf878023db2598518161e85569c3ee68c46a0e00173be66bce115a6aa2ec2c0c36379617f67a3b1958d214dd573058f9057760a60b34f6f9538c5229098da0374b8a22036d86b7bcb71ff014ec27801f16ce29dc43a110f6c00218f43068579b33509502d164bcac4d5d9547cae3b898b01bf4fbc0b4f24e10fe7990abbbd9d05203814d353796dc51301fd1b66b02fce6fc46b4305685b7ea91901e472fca76ad607d5cdb76a8d2287f2fd25e3d93e400a8f31dccbffc9be7d7294afe1c66e0669ff2933db99f40bd3499ed4d9752e8be57f893b9e353cb79abcd897066cb55c9470c88ea12b6746e762b316363f5f594c298d41d441dc3b98037dabf6932564dec306d9750d00c743758b7c85836b5f8b02a07b8fc8e5774327190bcbc23f4a6a070ac5b1c884382852c10d5f0b156d8be34c49def92da2600ee63d053f6842fb4470252e21f7b971342a6f7c25ad41dc1b521783143e63fdc872ed087c156b0d5a0a1cb46e8fdb056256f9f6c3a9a406b02fce18cbf572527875e57dda93c39efe6b490d74c6c2ab836b2d2bc41fc1e352c923fe091422793d784b19d27a5acc8f1d01c8dd549c2e1e44021dd2b32591b0d75e720a0ec9b7c8631a045fe65a33fbe2b2fd3846063554f47aab51d71aba5f350d96a1aaedaa7f9f0b16c47b26a41c22546283edd8700685250314044cf62e1174b30bc2b269ed0f1a64c7c931f1d61ab1fc1960520cdecd93ffad9323212c3811adac07646313afb2e6da3ef687c5fb919b7ec2dca190dda055e6ae1558190463bdb028ad98b250ac80f930475de29b8632da8e642b330c0c41bccd0d629f35686e7a333fc0e6b862936c964bf4e0db5918b06e9cea86d9b63b22fc26bf8e415088bdd57671b4c58b6c91ba71a9751c6bd1c16a01827319326e17f886c23b2fec70b650035c3a98af57f22c24ffd0125381fc46559248212bf6cbd2eecd21518950737ef46983ce248d58807bb8519a19d066b656e9fb3a9047f770c7207ce52c7dff33a96f0e6ce0c45698e71af11a5fa842a7f6b5be6ae5059697a15338c2d869f43c011543ace6042e5f295e79946946e62597991c5c4150d67b62ba4c792526330f7b32162129f3e34af06056c48081fcb92c437aa89dafd930887ef732dfa5fac344920ce0d13ece18fb04a2dfa1c61b9b48fc0399716808854196b6a4d14065d58cb670a51b4e3c323884339d47364b2f708b81dd640fc3d9245c5ba3528eb01f5e81f5803b7819bd05d0fd54b0af3fa1e1eb356905bed5427b85b524f235273714d540bc36f4fbaf264f8f2e8a664992c496a9e680a01c5c4e1401a4f72ad29c65932d148119fddd1550f9da517ce38c44ca8e186f9d7f2094c5c1abcf6fea7621680f01e730d481bcc1e97c8196ccee756233b7a99157292b4c361eea50c4855bc298734069d58da4f140b6cf885369c9c0779cc24725062ef4712b45381f933c2641669568351f298364883ba4dea871426359f5440b72802a3367b96347758ea7494d1f9db3741572c28090bc258994b4a0bf64f1f0edaa060bd1b5d6388713011b52085581e77e7dc4fae80686165a7b7e88bbcba284583e5d1f05718e4c0960104c1478fd1f4ccab0ce0bfc765b5c6631e2e95cded4d50f8f570e6911600e8f61976b4c9d3e877079c905a6a3d7870f74072ed2946a404ffaf2c86148590e9c00ad4c303c4232751c8da3acbdaf53449ac9308221d2108a4c3bf8433494f27dbdff5e048b3640f40fd6c78fbc8edb3d97086faa0ac13b7d08c6782ffcbbccb1f1ac93f13396cfb87ff79f9efaea2ffdc3f69bda8bc07c8f59e3f8c06cb764768f0e73f9e69bbaf545b0bf7805fc7f16a3db5d802dfeae443a256b1fb0aa50fa7a60a023a2170166f14a6d266f77b495076bd47c5efc73820e3cdf2033037b64f7ad382f7f3dc93b4d8dcda2e8ce0c2b37e3a684b0759cd5e55b15ce950c106dfc753e715cd8c6e6c27a28dac0ca99e8b0ab872326ac358f2b4bfe15e854afcc04239b50c84f0cc37d419ab7564328696733d49f7727eb5f2ce30c870c0db998f76e3c12835346e452596599afb527bb3de9e6c01a776cdab88230cb2eed0c02b66826b77f32c188e578059e852944f0b7b07c1233302985662d3c382906226d1036685ba361fee474054c9370090bc5cfd133112516727192704317978005e8eff7bd2945ef6c107d91804fea1bb94aabb6265b6f9b602b83834639fdba1f1a66ff00ab58e415de65a9b70971aa6da7811c1fdd60c8385da0703e800914b43902edd866af543fce1f71d02ddc36cec590c3fa30adc80837a670946b930654c6990776c230432f10fea54e314bfcac5782335fc326abcc4027dd528694a94c6fce75a22a1ed1c4cd43bfeb5b17f6bf0b66253b36b7a91415750c187eea6bc8449ec923788b5eb3ddbd1cecc8cad6050b9e4a76d894c009c9609256a29e49fa460a62b8127b6bab33b3008f12f66cf4c21446dbe427d75333c80778b8b9ce954da32b0333644a958350babafdbabf7a2200c22f530d3dc891795c523a9c95bd48cf934ebcee89508d2f9d0eb26ba2087847f1d9b220feef04d29a1bdfad65261dbdcc38cdef44d85390e5e1d7adf260e6f8c5c63b07cf0eb3b6532d18c2a608bbdf33173b114eb609e0e8502d464a29d8cc01098f19aaa33d43ce10fc53df8d0239d225d433703efc258007bbb9450bf49dfafe22edfc479d28993a1df6ed29a545ccde108a632da823e016133b557dcc84e6a726687514e9b017b58bb0f8490084b94f43995125dd21c45b54a6f4286b275ba41dfeaaac150a728108c5690aea622807a21ae7932268a71552b35b5b563d72b0248580afc823a3810bf8eb2d268ae1536085db0c409822c257092027f200a72f61cf655b89385da30f5c8cb109e68983e452a84db013a606f736584bae2322427cb9eb33fb3829396ac4cc6f6f31a204eb2c9b68f15a2a07f34a228c80332b0906d675e88354c2919aef8c5f107ef83c40b4bed843eeea9d4349f9a9cbf4b6b699633c046dc9c3183f4abc447add74c5922fd2f8354ff5385792c687ce366b1740b6536640d2b84c4176e72232658033b1638c0bfca1f3a67f6c0a393c44fd93ddd4ae80553f12b25f3fbb126994776243016da910b4bdbd6f5ce5197031e287324585599b642103af14cdb0c1d66ad2c14ad17b138a8afc9a8492743f6a49a611a7100079bc06cb7642cfe2c8e24f572c172ce410f1261ad151e9523ee367290d5714b5c6618a1cab6c09ced64a0a7981a26b0ea181f0d2703046031c4c4029524af659d462a7ae4dfc1388b0d8248a24c3532a8a1486d416cb9949ea6f5e043b7f66703f2aeb9d85dda03fdc48329232f05bdc710392de64e82e0370480004b89a07fe523756f5d1ea180773123c92250ea2548b1c9c8f3e9bc01aea7575bed02a8d1990630f0fd5688876f6fc4231120ab9064ad0c263a1b366238e9a27989389ed087a653ececdda4e70e4d0878f12e280c11e29a4acb691b82bffe14df19fef18bb702f7abdddb237bf42ab670071bbf39d313bdf39c101e955079ef603f6654fe2043de76fb4c5023cd4388c41c45ba41162ba9a45e23550d29a2ff8809d98fdbfcb6bc23b8b4bfe0d944c13c51735756ae3a933164709348be8714c712c6d807b028ed4b48312ae91b98e386a7fa0dd8b5795a216cdc6e98f95c8d113c8cfc71ad4d653ae278177c39910187eb8139d9153c1cd56c38dc913715caa21ecf6dffb26504df10745299b6a5ab46a55d8c0cd5554eb4ee3a0e4bb494a68b407dbb23ae7a23c681f2fa71b002cae0251eb7549f7f84dd7903507abe77cae8351dfffb5a7092db274ae573c8b8092fca9eebb48935ec0a1a152fcc716afb23483a7af1e4fec1131bf420ac693e341d236e001a4c19342b10e3a0a2812e75d575dd6d92f4163940c953ecd6b8b9a918e7485e17366ab015ca65eceb4bcb5612b7682be44c620801c894db5b333cb12658ca75c721919fded5b9c1de2fb758e901956cb29201c901f7241551d87d53a6a6004c94966899f3f028d170fda5b4c001990ba26c814207972481b50d9144612dfa4f2ddae8b1d4db950e80caecd06373ad75d9ce0b9733d7cf81e6f8f2c4dad2f5adea9b98d30c99ddb8977b5a05ba9c9a71c13a17c088189473edc9011e73c6f130b14267f617e458a71c7a02d214bee65a173e5dacf700bde0d7456ececc79c6be847114b076286243b37afb283dde53a6ce9897158229f8047f6627a2bec2d0a2277bdb87c7272e731e9ab12a9aaaf2219a7546d1ba9811b2aefed336dd34de274070f5951898a4f0dd769b96ef177848703a8695359da0830cd8fc19381a880fb1fdb135fd4101ea0e1296c38c52db427f96405c9b1818e4a59cb217b247bed9cc40578f281262238e40b37930d1663dd51c7ac86eefc9e12ad6277b8512d952fea7ee450d92ae339eec48081e743a184c638b6601878212a07942c0ce0895344af91f36b5dd4c8a8d36967d0a09df6ef3602014e7f301fbe84396243227bf7188aa2a9c26306d75d8b641ada1a39d070735f6c8732b53a6dced0a4794f1c808df1554b786196ce792bc60e8b6fc2544c5237551adc60e0056da5c356fac62684d8ea3158dcf78e26b82fb425498bfda6b509abea485994050664e7059dcd04ce5976d0db28839e82896d99419e60926ec730aebcd1222f27407ceb49bcf692be4ae25e711d5459bd22729440a301d70450d7d80b2aa92a2a922d50c2911532a8ad85167073bbf3ee7d887b0f809433e876483436972fc32e0c91ed5e775811a67c733c0d95e288f9d2fad0b4425961a642fd42354b5df11436182a4636a47fa52c42c838155b26bf78240f43c43e128682ecbd4dcb42259a5a2b0a322ee0666602d34c246d8f900dcd9cbe880fa7c2781f769b1ba76e8de16b3fd2a69cfa704582e03aca8f7104b918ef29bd5b0d52de34ca83d2b18c9cc95cbafedda48024c41e1c9c432b89d14bee451711d516a9892a7343478764ed64a281ad5718330b5f31bfdf015e11ce84fc70c04cca0611a078bc2aeae3e62455835735c886539ca1f51bde2bb1b0c0746be8b5b9beea72b57df228a2fa52b8a381936968c040e5f1370c9c712f52020525b49b3f0ff8b62e5f19a1dc1f11a32d3d2221ad5eb490387dae7825a3f90dbd701d6a347b34dc3d85eb02204b5bc27f88387dfd4f79e86e7d8208115dd3de9b94727be2a619b1cd248296561d82f8a5463bafc1c9a4a366004b985b2d1cac1ec09e7345d93b0808203c18948473e54065c6b56d2b41278f801c3f1e176e942654986be4d29c9502305e847a15c7218d1ea7416833088b5c5dcfb91ef237f610a254e3eb576809ec515cfd7cd40cbfe04b21e43e523e096d4daa4cac6673591c11188152ce82405184e0a4ab26d89cd88b1f9c16ef207fe2cb8037185b5747d1c61b2893bc9fd9af746247a8450e3c3e57e1977329f4c648d0c8dcc988d3e978424d804423d298f591e5e11cb3688c96edcf07f79700e3d4f2b9ba1f2a3e714a09eb38af5a69e18e31de20c5c8579b5c33d1d7207c2957e9cc0d9eac8028898a4c0a7cd5318a1687b6633751943f7be648b41b7e9755ff2adb4e1b06035c78128fba018d5ae94f1294f1f2ac364886b4aad77a7001153f68359953cf9b78d58220e20a76648f6da1455f71aa53178ab97aae4eb3e8df24180553dad93ccee3bc4a7541fb1dc57f23424f2e804d59ba6fd8c2724e85d21d9a87b0974115f5192b7ec0d905de6bd50c83e2476fc8cfebb43d0e5d4402b9070ff2237ffc3f86a038fbf9c242f46cb61fd8050d3fcaec622afeac2c38e66cdcb39f80563ab51f67c380b6e355c86aaebac0172160c4f00f31d1e0f01add7017477ac78d5eba9075cbe6a98b4a19f03aae0955be6f0aeb21339584346d17b4f98c04ce6003cce100bdd6eb1336c1aaba7ea25b9f048e4fe5ef0fe13ef0f4443143eb1c6f873cb32c5429572b4f5e5b4f24756a4eeae51f82a2b2c0195896ecee68d54d0005e62f0869c24b3751a2fac18e4eac5f7ae69eff207c04f30793012184536070c9acbf498bb4617035f68abb7153a99170af5aeab7184d14b461f807407d9d8d93e36c80d866437ff4ac723669c41cbff9c9f2ffd63c4ea036167509b79f50d316dd62f0c08920175e3df1c86009eae810156d4577f24475eb730cbf43e68282c856cf89000d084d9c14217a46f114e9e36f4ad4ee48e1bbdaa0c04343450a830083a636ddcffe30c91831f05a9198fa5d8582401ac1f46f8489ecd1c64ac06f54b5cf7ec19121f0682fd7f09380f071294fd12af06d8b187d864949332afbcec609f103498cf5c505de0fcd6342bc5121063359d1236b5f5a7857bb228e21131199f02a303453e7f438e0e7408168403c34340816077d713cc608b00ddc908707e2b0d74c2883dc694b8a8a3dc8739f5e532b211970b8f19b85e740f0034142b03a5a80652290362f6ee91719230e6f812847acd3291f03dfc309e609931df0baf754546ffc4826fced50f22ba8c49f6a2a411fb36f01e10d3d097627c12c4a355eaf598da6cdbcacc71442e111c8e91d07c64c506af11078a576ac808f2b28ef21db216fee52dbb0f75cffa3f08d53fb347cb809c37699813b6f395b555bc19c0f99ea0c50a653672c98e30a0f5f28b3ac27de04121b6b8ce2183d4454f4d0631a95f1f0484c1baeddc6320f400f95410aa12d36c302010b75129516b96b8473533e8d2b157e0d28d1c04a13e2e984d22be5406921dcbab782ed286f18f4118fe8ffb5d186b1c5afbf99f05ff75811ea7e6f2592e882dbab6024eb182e143fe406d7cc0c78818872806aeeb40ff6cd97636eb1d421f4785f5642b33c1e32c86024f556c3bbde9c8648a7fcc77a55c3b440c10db2f929bc1342c5457e0ab7408302292f0ef60e85d541ad3f9c18e4de440200df44069610e8fc04436deb94c8277d2164135721223020f278a3e383700053d8d1b788d917c2663fab57848370fd95460dbaa626bd393185dcc7fbbd84a2a239d8b90891d5977b825ba0c21ef9978219c6bf67a4c7279090262a27ec9f7338c66578369db009959947d69e6316c0fe2bfd107c79379762ba3bdfca653f64b52c4fc5c2fa3efdf9bce9c090cfd02f24af3f5641626adf6341f51678197d4afd578d339eb41f04dfb20915fd62258757ca367e957caeec23863b960609c0499a67fe76f651e8c990a8179c7b4ec30e86e6d7b5c40882c3fab28fc6a480518d17797de4bd1be050aa63e115fc404a9425960ffc74c82dcfb38de69271778d8463b471ea68d6551de791354d9c703014cc9eac0f7c84596ad856c380f4dc53ba58b6680c2d001fb2f6b7be2d9fd30edf8c1d1720b02b062de9896c3295e082d7348f050d51bedc14b96401c258ae235370efdd520a29c4dcb3a4ba4a408419e3d5d0239f6be64b0c2fbea09cec02af9b87665d3eb3e5a11b09b7e2b731608a88ba510eac3b4553ccc1eb85de6d2bbcfcc444f00428178d6ec8aecc39cbfa22caf43320d8d8a2864a13ada09c38c1fe627e00bd3a1e80b02b04691946cd05fdb4d07815f46e6008113c00383aeca5fe5358bd2b10b950b6f2c14e26b22c9d780f7c58a3b035651c23c10eae0849210ab72495910799d900d9e78e7efc31287ef04d3007a4bb53a0840bdde2279d4ca961a064086f251d3fefb747677db81343de0bef0e465899a22aa68d1a5e648a2d43d8a5bb95a38f0b6a5ae99c80bb91dacb96237955ca0255c8e075314af2c2eb38e9dba6fe644ec819c14c7a9d373942e3cd7234fef6eb62ac7110d22e1c2a57f14f5c377821e859d2a6c8323b38a87a8ba5dd166777ef9d192fbc950c749b1d1a5be263a05ee8fb0b0caecfd4a3856108dfdd0d788074006f244e8a2ea2e19c51405fca6b7b3daf00c6bcb904ba589e39d0d5ca2a7a40926f0d5eadad7c289b012ca0977ee07d6b51ae603dbb047b6534a193c349b3d2a497143a685e45affe201e16977f91a2fa62dce471653dc1894966cb6245ac32dd171217d9918f72a9282c097e0e545c452e387501c1ce77614c9f0f63855ba8d7206658d5a0968b6dd312e5d80ee09b4fd5f673e792b566e6b98da939a56244d76d4cf98b028f3db3ce1210e4af1f4688afab4407651e19ef75fdd4bd2e8eefc4d5635fa1dd3f8aee6d608502db1e63b7499d9dd14b9730fdea08b565f4d9781bc7ab8c1b50d08fd6088d705a5d8261d72eff540fe6a675751cc9272055171186003c629a9921ccaa7dd0de7e092a2da4bc741b84412833c90ff149bdd44338d9172e1a5d6e1853622bc6fc5072936d66140343dff850f54391885c98cfe464122d3b20dff3aa130ee12265f5360a1aaaa9704de1d276074153a643a9f03161c062d9af66c64f8d2696429e5e14fad23fdf8c116a7dc183cba400626c1b9e089b980fc02208351e60b95981b677bb203c9be0fa2128b5b260b5e7905bfba1221990904d7432d6913a22bfc861899a515dacb8b3e6327c9f89eea2ba2efff70aa47e9e62644b4247484955c05369b3ed395248bae8aca13555efa148b24e35af0058542386bb2d76eada065970e01c49f932f1ec4e2ac61459e8f2f8a4f715380dff02658e807a647ce1875b2b5e0b6d8453b3e7fc828b3d2f9cc9582e2ba288eb5f7e6a72d225c30a46bc72461cc440f33ba4afe69c130141087389a4d8c263461e4b2a5995f5b5993d6362e431f09d7e3b315ad6951098b0c8454f8aca82120497ad786605208a68c86fc1c40b89aea6b54d98168686ebbddae8ea27fc00c099f22b0c44748d6ac7ccb58f43811621e6a0a85b0aa2374226607961453687952c88d8b70a9163515a0bc2e213b72def7a94cd152b6170db16dc5b6b5948d151763af4ba4acb4dbe84da5cbe71b37cdf7bdcf371048e42ef4b07a72083e06e3924d0835c16cd717632f13f92db485b6443892fe598c9b8dbe1ddde4cd82364d9f35ed9dade2b99407677a42fd8231a6fa35e1e447a31c04aae501e3ca37415ba441541168859b9ace0c5811571eb439443186ab684341e0bcf8ad4333ab9900a43c972b20f4da6de1f3af86de17075f676e65ac0528ae9dfff4bbf8e10e50b2d6e1276508cdaad49cdb8d1281f0c0c1f4f396fb714d3f5f547649e516fe6ab86853781ce2e7ae05d2a4100f76054dc1c22f814063e786ccd7d910ce3bf203458ead67bb99e2c4ee7ae14c8ec2b9bc45865f8a08c3de253ebf02e709cf852be00c7a4074346de7063b7dc60ed09c0082fa7eb6c0e2884addaacde7b9582340f1cf815e2646f8f6745bfb4070a82973bedbc4834f37c51fa2b8381a3b401057433bdf4019840962a96a1d77b3721b02c900dbe572f0e83ef60ba5dd5ee461b3256ad719b0b9790ac8fdb64253d47c11bb893f7698386baf440674c060fc11f870c048c5c16c7c2f28624f6f27e2b3d4c00507d536f079c9d6572726f3064378fe89100d51fa779cc28db7b3ee8c6cfc0192fe0716c888b46637992920be8ba9ffa259866d27de3fb9f8f237866a6f03545474ace6fae82ce727ace2bbd219ffa5cbbdcd44d9380675a26e8a9ff1ef993dcc3bf5e19e0c5c53e64514caa7f396a2efa84b3710a9febf611b14cdd41bf63eb79ea3121b2bc6c3633a8eff82c4f7da1b4d6a020c5af387a9d2f00541afd11e652ac432b59409f45b8c2b38e466e0a33f644111b64235b661d109941d8fc9017d565c0f3bd80ba722969441b9bb218dbf6ba37364605ae5b8438f11e49de68a2cfae6c83c1364ce12b8ffe1e47f4453eb1d4533938ba72a9e8e0183bf0785efbf748eb2aaefb343119cbf6fb5c27f32fbf592a31ae67dd2205e6d926e30911bfa1bba6cb22c44aff642d9ed1d636959b87f8ab6bf10e12b2f6eb45b015775aba84eec7c56e3d7deb2eb689f51cfce2f6c8ee77c4a1298ef8eb653f4dfa778c6cf7f50d51fbc92aa84658b97e7218f4977508a100fcfaee4834d46bc03b61f1e08df544418df31ed10fd5cbd621af76fef119d7a8887a1640b0825de028c295c24ce57a890cfb1f233d0ff9afef1958bf7253c830e9378921d911b76ee0f34bc6b7eae6c437a228a4c95fc513d83fc8b343c607f79d75ec73fdf30fb091c2baee1bebce0a6efc6354d2f4436a7c7492fd5504350fba0e3a52a0ae529f0fde1710786ba5730b565f5fa302dc35c578add5291c3b9ac91789731f26b0525aa3a752b845babcbcafc98cb9da6139009e572aaa06720e0728530be8996fa1880eec58b3c405f3aa97e8d54640e37dd036c999d42d7d4d5e76888b195b431086379f9763138e02501557722edb0501327805ed71c2c4df2cf229ccc12d9aba860d79cbf14210c18063967443257d3badfcaae2ce5aaf2f944b822fb28e5a949d66e1cede5d99bb461875c847c928b8bb488f74b2d517f2283a595017b10eaf1272460c27af9e7f4d4d05ae057d84028a7f22115df635ed374fff6d56f5bd08ebb7df676fd920511721258e657dc366e76887141cd0c974674d97a85866de529c12dd7daf8a5b9773c4f7003f98ca88e8810cc2b55155062c0342b7e7afdeb3faa968df8b98a130e82b30b647251ce5af79a05acef2d46f807a2da070d06372e535c329172f1d4d90cee2f825e97bde04852247938488bfb3d6726b58072b76efea2ed8aac45bf617f520b7a3086f947a9771ea910fdecc92b8874a6539227e0a2a3df1317b919b02880815cf4663b47d5c5840a84ee92c7cfde56d3524529a8dbe392bbc5e855f31e107a117918f9412ab8de8d337af180bd6a4bfa82c38f58d8eb46553b202c035fa841dc94c4e7762f9bfb011f7fe2003cb2cc7bfbcce14182b1cfb3ac393f9fba5d5ac27e6d5f80e96f4bfd73899fe17045dfcba447c04de369969462bb4a56acaef3bfe3ff7d4e1f7feb223d6a940912b84f010c08000282dcf20b5f5a99c3c00e938d4146c1da8c35120488db8201ce5ab949437fb46e4049ec774f9a5c08b8b76bc87c087ee04374cb6ff76a1b00fb3e3468b41c4a3e3792a7e5d3133e0fc599fb6030e465f54e7098c371cef82b1a9529394412988b0fc96826f614a1e95e7967e26982c310523d4f2f122a027c24715db5423659d67f692710866f6a4953e2dd90a63925e075f79ef10c277961695b0db64c2098ac397d7c45f83840268e98d00af2c3971b5e98662ad3c525e5d784bfd9ddccfdbb3c2faae25e77a727089fe37f2d5380f3df0cdded4f8b6f182dc8855b39bd20da32737f0fd4085ca6d7c6ff8428eb9cba943cbe801b32953832993ea19ccd97b34a6da33181ca69b2edb606febd118dd914c6f858e69138a37781a17188aaf52631d6c567d77484a9aa8cc284006a7e32ad1694ba9d8902742682da80e97b78262b2a0940e77f7bdd2d5582c95c5b1a8b6d540b11029e071669319eb7e377a8badd8a93e29f032d4a1450a8f8331d97770975c589617141bb00b64ae36f50aed6574d7efcd9f6ea18c0eb22d4101510c962a7406f3a743cf9dc3b28a8a409b2394658a6a8ce087aaa6d2135a711f1f5f906f99f8a59b9491dd3ebf41ada652e9eda9dc8ae0b7a790f473cd92ccfa1bc0a2593bca6f5e9d82cb77aa026b8bd0e7fdecfe6e7a8362a9bcc6481e7c7c06dd2b1b979fb2e688edfa2d669adaf2694b345d1c0420f63efe39a14f760bad0835f7628c36677ba6973e4a3d8ec4b6d113995e9a51c62d1e80a15cb451cdcc39b19da32cd86090b45fda6164280d7d8fb2c0af89f21e654789c5e78e53fab7ae8c53b30f6a7dcd602b9161c1a12dd45b613978ed600b5dd792338962d8cd88e450c0e87c0330473da7cd227436fa3f9143a7f6d8dae3949daa004aa34ab6788ad11e05beb5566cf4167e4c094c380998861a7d664bc448022c6120e30d600950bf328e8d717ffe885410932d0c73b537baea0969699d27a88c412c7a0565fbc2dbe56b21d1e9988a044cb631375646bfeb1a29a60a6dd3f3c0d4a5970aed62bea6efc993e26ed854398e75d7e7e44d6518f556258fd1ac76a44481162245dbe53c759392edc86dccd4445e68b122aa487e316cec0ac23dcbb85fc12986d49368fe67385ab00958088ae122df96591d7ca6eeda8defb262bc446a08167d552c430b1a007c7cee3632dfe7e6c6d223379b03aca3a4eda4630635e13ebf85576200a6cc0778a6cbb4b17921ff8a88209d3ec4a106268715f463dedb313ee0c04d078a807a07478559b7778e35b57ad1af35db95f87b1f45b61eb5e0ef3d16b9b8a815ad42fc652fe0de463cb7a4a5cc43c6d2c0784550c138d5e3970fda12c966dff9c7f3bc9469d7b9e102ad52db75b2664f376ffe2e9da7127befc91ce78e31d0553e81fe5bc4aff6ea82d83e173b3c8c92e41b4ecf468098a50c8aac7bbef8a1f50c728e82d96a03edae0c1cfc77e9df7986a3494c7297b352d415955548c4f214afe114bdd931758318a3478291276102688c6582244d531cb06126239d20c137b2d90ab1a440241fab6a690400a8a6ea80113b2489e68dd0f547b324a05bdcbf8a192dcd57722b71da40867ce4f8834579f0ce8a870f6a6b4a74fe5c227e1ca42b3ae220577098344321a59ff0b1c6bb80f9992d6555c9480be681dd0bfee3e942932bc71f1de77ef65917de1da89b84299c53a2f07c40cb61527cbc1f42d34565d08dca3a75d50a9ee215e0502ed2dc20c7c54fac1821ae3a8d6f959cd74f260266d05f43b77da84d41b6ba5841810ea97829386e8011825130aa6075a5c97910ed9bc9c11cdc46d821a51ee0bfea86d8317d9df110553a0b822e2d78f056e293ee63ae779d9bd4b0d6c210073a473fb7d1800feeceb3553c8f24bfa9fd96562dfc34f7f9bb27644ffdb51cd2ae8d92a5408ecbf0eb6af4661b351d0a764bf11da75886e49c2f21958da5143b1df7fa13077434ae545a0fc8822fc07f75d403a21083b84bdf2817b5582f8f13cc7d14f853878cdada202487c6bc5869e722cb5e37591a3201e96fa4337d28e0cd0289c2409b387e1ed92347d09085cd9bbab97ab650cd9c0270a1b6b3efcf306d41089d853b8aea0003111cf72e9a7a7e9d95d7a8138517d6730e740d9644d5f7c7ec25488d71709f99d3305a316cfa69c831704af9e81fd1330588f53fff3dd32e566e47abab17b0a86bffaf6f4ce3a6e2a953efa89dc6c02d5850637aa9ae8225f3723b8bbdcec05b2966d4ff5c5162951b4da24454663489d253f9682254926581703139c84845225172672abe99f8e654f91118eea1065a5ebd86c1eeaa777948dfb387a99af16d789501d4c9f4f0b3857d4ef75f8e5b78c485500a01f73bc9e88f4b96bb25a108fb8424a49ad2e810f2eecef8683ab1f0a8873abdf2d242afac87598931daa75d3d7fe709ac056110a053d057af375714322c5dfd8099a0e9e79f20deb5c00a1fc22f7048089929563cb1d4ff555507249da7e9c303bd312d0e121cdab4db63a5359b2586649ff13196b3b23257b7a6411f58fcb562688c698391ed25649b43963d0863108a2cbab54f57a1f79b88616df9ea35aadf0bdaaad50dbe66383595e9999b20c898ea9aaab5127309bf1e7e96c7a97cd05d35285a49132ce66e8280407381b47141064c69c4fd1b083ae18381d361275533a103446dde681717b5ebbd5c150c833321ee8b2199a926cdc4209ab945ac0b922ffd8de3bfaddda5f2969af2d3f90354a5720664319d32e7b6a35ad6926bde4ae1528528e9f0f599160b0e4ca89f96e6a840beec7dcb11e57b3fd5d906659e7370ff860aad6ba1c4a6d690b6ba836cc744d0ade3bdd7059a12576f6b8ace74c0407104a353a9be98a8b8a48e9836b19de4a07c75758a0c73b03046921b55ad532a6df5b8f3041c16ed7f4a5f70fad5aab558d8f3f7af43617873703cf9d9854ff297259a9836a846be54b40d5f8c187d1074d0a7855c347d18b0655b27949330284c9c6ef984041b029cdd0eb91ed1855276272d953672f9cb8f17424c401377c622a4cace652ceba36d2ee81a6414e3927456fa84151ffd5f026f7137b92f886c458d79d4f3f72b7ccff155c15347e795859117c22cd44e705d20b128d85ce83648c38098433c45be2de2e4eaf644d4b5b067838fe57ff69dfce51dbf4360c19e90e5ceec6c044e38db0c0dc8e9b01ce4f0bfc5c9a3addf5f1047d2fd0b4495f1126c6990b865e759937643a35b5161216ef9d45f5533a4565c3212548948692fae0c827d175d7a488042a9921865f1f494224eaea5fa69e949a9ac23ddb743e230e93d61768d6ef062f5a6158ed9775336ac3ad2c6333aa21073f6446ed38b0599685200fb0db663666753402adbf057c570daad8c8550f9673eaae104b0306625d2782409cad601f7b74e0a8f9cb964829022c781a1bee49317483c8e2acb6fff67bf21d0b32612707c2cf4898035a94e8a14fba5c52fae18ea91386058ef7ad20513ec7061feebc49f9f2387cc45d9afbe320ab896fac66bf63b06d09e087ce6c59a2bb8bec9458f13405dd8c936d36bd7c774470e93441b1771e459c0d1a9eecfebe320f82c7c1bd65744ba9143c0c5a47c29e0cd9eca2ebe19372a323303a23c8b3b79305a34e4c1a0b5a40f3f8009eb16ba734a70131d1c5f6f4a06946d4a22c81b93799dea41a21afa47db29f30d7c430894b63cb3f8da20d2ad5f31287fdbc88cec245f7325796f520a09bd249277e5e276638c9eaaa1611876a7c30656618077a728a8b8a52c959bb5c363184078072bc1d17a8f592cc4046ea1a3fb2eba9564cd3928f76122d5b9534ef26e82f4e549f5560093daea4eb89b9d04f668f038563500ced419b3957d3ca5b24aacda8a0e7c57abbf11be2b22b5f39845824715b014e388ce9f6f2cd3eb75fa73fc5d4d3649f28122d6565ea21a1ccb272e1d72345c3a0f155697df4bc310e9c43c60d1742f13d6fbc1af0f0d6e438efd1b3b12a196068de9f27f849a395c75d838d75a26ff5eb4f1e09871da8164d3ec3dbe05f77843e8a1c03be4409e0e0b7af31b2a88576a3f16851c5ad18ffa381563249c18ee0a800a2d90ab15e1b9ba02d071f2cdeee6b4d67f3e877705f4057ac983dd3a40eb21040475e721295eb4021fe755617e1b2be9cd18898dc6f64cda94a0f75481e8d2792434a0fd10d1ea5c35341a4dd5d7a0dbb0a71bbbad7aceb2ff42778ca616f6c112f5ba83f4714e75393c2862233ee5a0ca4c2f8f06a5a429108db35a8230a952e451e358a238ce1aae9af258c4f50e2c1f1cf48a98472cd241d29ef248c16653372f1347104d7ea4c46ebc191a4e53606fbdce714275b76d890efd978b0b5ccc03de7fb7acd6fe1e11fa60a6060c936fa1ceb54ecfda0a975782807cc199c8a99b7985e5e50c165a61738cd7dcb8ccff3085ceff16a84ac4e41fd3c89fd1c58e65d078cb9b32f8aa8e48cda0d77068b9ebd150b7b657ff3297c06d8a7a2dddbd17e420cd82282da3386e9b6adc9018de5d7c68cb5696b1275d440986904a30a4928ccc0dfa11d88725cee739a127019e81f98cc81e76f602c551a0d6e8cf63918948a878659f8ede22ca38ce8c393011f558533df3412cf575cd7cf07bb8acc0d07160c21ed9df6926ced0ee0d334ce3dc29c130bf676f5205a08f970262d3804a8da5374301d53aa53b9a328208e5ddaf7e4c73a588a73e796783bb3ac618e4523d1821288ae2a28b4482d87dfeb1d7f0735424930763c3cf8f7276d80978209f39e9d6f3a649695ccad95a309d466ac5d13ed801e9ee12ff85ce570885928a29bfff2ea47633b933c16ed7aec3edcaae34af8916b18c2b38381647e06a74872d526bcc5bc85b5333beac7522c20de7dd8d666cdcb53bca2bde7d151a8a13dc2ea6cbf75b9ba7b1ae2235875a84717da79d90e35d485e3815762bfa93d1a996a10e4bead328551b41cf5daf7691a2fa6030e9beddff877d2613c09a095b003847555bd6b62bf7ddaea07b64444013e272befdd730ee9cf2183a9f99e060ee551cc71cb3b578b2f38e2d0169dd8ebacb3a4bbeb4ca40275bc23ae8e86423fae3999d37f08ad16581858e6c6e2ba6be850d7202cb25731cdb19fd370a5b087e5d800e7e43d07878ffc7fad8627ce3680bbdb49aca6b6974703d39cda41883fc5e2da08667563a9f1fbf3e9264257dbdfc80edab56037c1d0eab9b4b4955f3b8a53b4dce991e0ad6ed4e00e744b871aa25db60b6bb0a5079cb335a2ad810164996a9f94ea8728c1c01082d479dacf541a5f1af70672ddd157a6cb870ceb34b3e43748145bef3a48e4180c0659b3d73d81194ab751309dc3580e94027dd9e6a875632781d24a5e083c8335c0ec479e18401bf817caa9fbc22348aa4cbfa35fa3b03eebf23a5db02d4e2db1f9859c7e500a12f7d3eb0ec301577e52f03882ab153320610b3edd0bbf05908a10ca47d388aee06f4f3d0abaa723a13b989e3310ab12ec40710d2e80045412338665d881e055c66067a8b4cf4ae9ad0998a5e830b9e6cb212713fef5ffb340d3c39dce240f3c4c9bd33fa6b0e2d8e90ff3ed12db9d1c27143be8a9598b33f15426df27ec99f09a5bafdaac3c961aa16281e3571869e052e08d805b304f59aad9f07db75c93e6cbc6b697f332b604c22df4385267cfa49ba2a4c610e35a598e60c2dc9b905c89f528d0e83166d7cd03108c914232ab810a54d5adf2b39c41f922c7a2635baa613e424efd0bacd21227e0d47201d1b8b7a24c0a38d42f5f63be1ad9651674e6adb9d140932906e9667ada46b2931a6f3edafa7b92b25b598db046f131c3ada426bc474989d9eab98b9a00252add8aa8adbd841d27f1c716ccf71120bcc687b14b50f1b279517e31675d4648389ea1896775d870b62c10ef3a41bfd1176c2838d046fb82a72027cf59f1c364a043fa13a79a1256910052682828923cab1013a447363fd7a1a47e4540210f9712ed49b51057da3372aa12173648b23321b35e4ae8f06b1f0570dda9a1365407c46ca73e40cadd33dcc900d79ae3969a769c11f0abbd334a527833c02a29007bc92624ac2a6afa440d24cb22697273cf681915c5e1e1301e6654532ddc6ac00c8f8c5aae063388b07c78a4b2c8d7a386095066f361fa5a027bc10111cce752495c6076d7787987b2908d722e1c9f1ceb5c36eced18fc31e2ce65630acb8aca84d0aa759e79113431b850312a58f1cdf480aa75151381fddbf8c24d11accec1e06e1a7fd463b216d8d861146c658011f61018e06665a704a06529592742288e37b645a4266d50755359ab4ff9b9ee40675e9c7aaefc84d4454904d7462ca29889e685aeefcbd2d91b5927cc9811a8548f1c28eaa1fd52fec07028273540073a987a47b65e2342fd936bb7c012e26cbe49246e953c92fa91c2931895c5713b9e5a293ff93452c062e6960bf93319f49c71edfef334d115078e66b38b772d49f8b7338598c69db99fd7f620cf1057d2d2dbae8a92a167312d8f84f6353ff8629631d5b00ab98274342458bb3f06e3bbc52a76f59710e437f301868276c23136c1faf8d185a19368e22f084ebca23a40f3850165e8623724e2c7068522aae95fed3a8c4f652cb3c043d98e2aa8ea0d564d4457d4b532f9118a8c26fe02ec8316984955050332cfc7474df770671210e2b4a641efab66b5182142b49eeb594274139022537d0d093a24fcf5c550810daea6c78f3cea0d7c578f9f4f6753e4968325ddac3be3009a268605571153b727571d54303c9c1acb4d6a8b339d670ecd81b4961c7e10f05a207bf97f27a6a35e7179adc88ed40879bb4ab618fdab7b9d691313b81857438fb6dbe228aaca12e000f46ef0e3a9e909b347b1e51d4bf984620cec3fb425b2cce371651068bf59a2ee67075db271ba371dd374988e82ae8bac3b6c3ed5fb5f68e760d70442b6a2804fc7104953ae694a677ed580f8a2c81894b7c5a64def312bee92e6c20981401fdd6052dd487b70a3dab2186e576428d3de5396eecea1e95145e45ce6d2f9cd06e0000f6ef351383ffd480d3c73a38a9589ed7cf64b2f79c462f47f0cf88aacd877c5a91039e4ab0bd9ef335006a0a45f5c74ce3b8a370911baf5b2c5793e7cc9237ca4f4694da4c5715fa3e01ea7169fd69dec76e68b8e8c6e26eda2934d6bda30981bdaea30699d491a09454fffd5fdb366695846f350bc974300c5732fdd4170888b90bac847c5d581932935368964493a5e13f749a3566094d33ecc0e920cb730c0c0fcf3910e0c6bc84e9f28d712b2b586d4150795ebdceaecb17e435a87c3df2a98f93dcd56d351fcc1c18c19711d103f001e70c99ba81d2e82ab16b1dcbb3a76c02713b5cc52843c3d98d5e6a0939f5a414a7932caa97718db08285a9b0c449b2e7cacd77ac3163a6eecd9ecc8be1e9a6c2a8a6e8d6590ecd65d5723dd95b9292ed0b7e5da86d787a9a6f115cedb269f95c35abc2d9e3d3f5da209548cdd04ce4f5d4308ef1be33f78c7301793f88f45d250b188cd3ce1e06f607a6f4c728952db48b13e0c0dbd7962ab4c95d83ab0e99fd6aad327aeccaae2adf8928fbdaede78ac51230e1dbf8cd4dee8d72380cec273d36d0dd4b7cda225e72b6d9e098975afdf20c2eb7ce4077f55b8e4299af6f9112828f372d558674a60f3618926f09be24581081908bf8d7e02878922d4836bd2794148660d3428209522b86383bec4cae035c38f164ecbc83c0dd267c3341cc8966dca7301890f38158c92c197b6cab1096b745426e92e45dd7814b096c4743e794bff3b48acc912a568dc1efd6e6534e3ef76fc07625ddb6d623217b8a3bc29df8c968126164ba27fb9f89efed6b5932889b907ca5e003e8a612dc811fe2206803ed461c7fbb81b0dea4709f0f2963238a59052565e60dce8ca0c20e3f020b575476cd9cb033083e07239cf1276be988330ab9693cbb08b13545c6ea7bcd6cc31f3a18e71339a5ff266a4437fcab53a713e9e67b55a7dc06c59911f1d3a8f6559f8ed2e033fabaf4fca00c41464caa32aa7e8fc2ae347beb41d1f52b3e811e7787e7330766384d49755c8af9e3042a546dc8524776a5e67c82edd02ae5b0a00f97a089bf612df56b8ff5eb5c5d1ced596ce95100367e037b2c3c9a91220317f6f30a8d09e2dfb43ca7aabd2b1118d000afed11505968ada62bb895a350d14aff94b90f028e378c3b875c9d55b7e16643e7861b910c4dce27e1ea3836242f0609206791f6a61c40d719ef25c96988840c5afbef9c0f465ca4fb9bd98100f9204b08001e84dc0006051acf1620176748100596ee0601ef70420ef38fd032edb1d078e98a36d8becf0aaac4c56129296ded04f9ee5a362b2781fecdcac299912ba9fc310be03f51fcd4ca6c4417bbdf2e71efd56fa9303e559e7c59d5d1ee8604650dfaf0b944246141af9d673dec6a19ec4168638ab8fcf29396f12393a2a3ad1bc66dabc5bb927e98e7d8923e8eef128a0786b41acfadac4a0b829591dffa6e84d496f6ce71ee7d86b4f8608e5434f6f6cbe6e45916ac50b4346b912ffb8918a80c7a14965c3f580228004d75b3306ee2ff4af1821a835f06df922742af8219f2079703b48c871499182ca5789f3b1321c79f809b17db3c420ab46c260ce9df542a3e64348f84e0824e621e158489646c67d560035ed3f9ab8edf4fe0853f2064e4eefa0266e2575945db387783074c22a0d4bf6c2a537d8c3215ec23549937773b4cdfe784ff4f3250fa5cc81a6a9c725a8bbdabc253778e7149ce005975ba233315c5fa52001d10ab7bd3f08519d5b8a4091330615c96e285203c5104cb0494ddc96faf649217158e71c1f579ad1a2acd7ed3819bc7d6294b150f9fc0bab9ac653e8fd856e9749bc6e316c12d682a1ee73a7e16b2fb181b5108858b209b9e092cd3fc6d2edcec7ef0ca145f2239f882096aa7394d9435c05fb1df8f8f39ad4e45e7c15bd33b116d14bb29d652bf4f28ddf19cd8dac881d452ee16b225179591cad0a78c847d2a472c666cee1bec59509c5279b353c8e98968b8919af60d941a0c11c03d4d6b76fba1347c85cb4fbb90bc4f4497b7e2d494e2d30c05a8e5fd135b37556a3e372f1c41d9fbf055670747d13b0a592be968f0b492a287aece4c23da6e0ea6eb6aedfef4545017e3e46ee1c93b841edf9dc38088127941809a0484669a558d6310f529ea18023ccb4fe8f976c764b23ba50cba85b8781754ba52aad9a3844a2ab7895e258c776c97128ec5074c029f9ace1a820d6b0af5f2ea74cb06b8107c949e578ea9f07fea17d192e938d4a7a2f3adc56a5f5eb4030a8f444c5e76ca378a18fdf8d0befe35f65ca206d72c65c948a1a73f21867abe0f2df8bfdea4094b39bc61dc7571bea21031b88997b09a0c36cdc0214814ce17ce5be8e8112136c74e1c15bfdc85ab2df94bd4c214b07538cf21791e1a00b7b57be9f41a118c4aa348b86cc8be9b793b8103aa30e46e4c7ad533339b761b80df657e51d0fbe54c379a3acb973af1a2ed47bf71c63be6cc12db6200bff75ab440a2c6f7c1915777f937d3104f2e5beda8d37baac62b96b84fcca6906cde63b42bd71c8d2c3e8bda925bc8553d878362ddb1298f1348f86eaff21bc98f744685b548718b150703207112559c60618cd9d6270b692926e90a207310447a35fa828572de92a88f14ffce723aef9e7efee03332a52a2419581d5d42b15a3f41b548020b2296ec9687b7e19117ad9f835c8d111a495de68888945fa2a77a6ff7d4e47256ee3448c1e335879253efb7a62eb167d913b95dbf3480753df5173003ab6b2c7f51644945d0cbc6eb2a023617f36b57d00bcfc607c1f6fc7cd29b8644d12053f80b48bd427411248812314b9b2c8b18004d4cd2f719d61440604441707840fbe97f4aa4ba4a61d06c1c88a6c5fdd808c3e22f62fef30b5870b5eb9bf781525de773dbbde60ed24f1195a6e92b5e7a5dcf4f73964ebf034624e5199e8012867b675243a3779ea041eb9a9c0fcaf265f71d003c80ff782f058c8f77df5ba05dd30b0affee295930ffa6059bd3d72751e068927457f43e5f1a6b08b296950dc8088c370dae3f31aa48b91f906097ce3b01c2f14a164102fa6483c5130b8c04d60a4fccfcf73a798e6205dbdde4f8ba89e054167f5903525195063ff64e6f351e5a020f038bb856031226150331fddf1f9cc9a6cd862077244bf38a01f343d0022ba7fbd94920486303c91ae2a51de3788201b9111a3723ab77e17f3f9cdc18b28ad7ba1629ede814efe0a8121fce02c1c0485c3c3f1db5d0533515427c87ac023a6e526b564800f01026d986e7ba3a3e2a29d55580be2ef86fe79d7cdf09a6699423d4366333c407e623a45dc70ca9271683788909248f2c1494518da0a74dacb98d93b65551396facdc6b16be2c07dace04d014934f1018243dbf68ce830a0c0b42b3f5442654312bd462c2206aae97a989ac74d7d12dc86ce6760c352bbc43edbb63b66007dcd7a89e3f7090fe6ddb011b0bb748f070bf544567d4fa138a9bdf7a731892c251170497763c1c00b4721d093d3914ca4f25a53d7238b5a103e747903eda6a7b7d00979259dc28fa5188aee7749c79c220cf7c360fbb1d3902e36db40c33efdb39b067c9093862f3644d7d7f1a5d4ff55c3aeff4c132e0a921d9464c205a799f04785581afb6c51f016b7d557e2b00f83f98efebe56ba61b201e409175922fd430bb475d65ff8a800a597909194d1bf94b89254f9da9572b294fcb65ed527e390d86e105200317b5a38fc78fbe09503d1655c8d9d39c95cceaa3718fb2931508e9d41a52e4b1a42f13adafee5da3eb5c373dc290398f823e1826bb0ab6c3443f42411ffbd2a29cf4587e005a49422914ab57f8ba92d2a4f0ffca5ae1ba131c888fe0671abe03944a672cff7d70f76d409c9dc4bb67873d93a5e4c25c75d0a27f12689141a28c4589d015340c540a7934bf243e91048a0f19df95819485b838a9f3ce11cc7926c98ae6c131362cf26440b8f3572a138e5eab45884334e9ab4074d24d1f545f8ec05a620152f5abaa9dc7bf71ca3da2d074ccc78b43df0a75a4c15a9d80f066ac055b26d4d8ea430560ac402c15c9f745e62a8ee9bb862f564099fa0eeaf9e9eeb14a533a60312960e117ea5386578005e48a002c56496500b4c2bb36f36a667b84a3b191603442893d3a9983e01b41bde60897cdc6ff73e844a6f18af02fbeb398bf9426f765803bc053702793840d00b9bf1003cf24659383f9c3d6e1deac23a1a958442af39438606589139bd926b653e17d736a6496bebce95ac8bfbaaa9c7d26efb091e102dbdd7f78e97d2111af0eb9a4a6e4e8194498129034e8b526e49c4c3c8465255616757621b47ed4c98948633a9d5324c9f6ff3a0b9cf1ad28d42b460ab63c00624bec26987da2012f6dd2e1b16805cf45f6ed4e13e309c0ad44e88947acc3541df1205757501507f11f66a0d732ed0366f192fd5542996dee2bc95079db3a035252672e686cccb7b28c64afacab0d458bc3246c9fde8cf48ad491f08411ba408b6add5ab4b2f80f3969898064d8c37b460c694a2b727881fe5ac515a06c54c682fd9d18ab16384c77ebd61c68311c9a50413d6b5051d54e159a61d3a60a5036c535b9fa6b4429b73900ec084fdaba8b468c316e005aa5f29a059d0372e059288f9ca17f38c250123b56a3c91032bdfb481e64cfb639ff7fb8fd345435572e2add48ce2e180bddc4b8dcf430b0ee85f74f5d8cb991eb88bba12ea6d79c8ee4ffc501816ebd96c86e659aea483cea45be6edd0210d790e24f8650b965e1e39a1136138fb0ca045370d67326ab1d7be65f0f2b6fe7814807ea17fe0198f1127c1a0a9ff8cfab3c35815624fecc63938feda3254d318bd0a2cc277466974d6d3748560b994eab18de90618e453b35507c6343e1f378109f0e26edfa1337b2caa38727feea2cb014084187668d0c44256c0b67579d17be37c2d0053f5ccbfa6c1efc6af8d985680dc636e0b4d5dc524a473d6a5a499e5687ff91866382a9c04aaa524bb0fb48ed21c83575efc0f8a8a80b953cf4c2dc49ec3581e18e55a6f8024b88bd5426905eab6ef96d940d0fd2618af49c00095a57a7007a7900ad2ddf87118716bd2f6e0242d7cc4e08383a5554cb5adf52269713dc91cc44078e7d82daed1a09fd644800a5720467cec40ba99d7a0c7a479b528b440d64ba0357934d12e55d14c67003a2a5af585f5769552023920b06f345541a8df92f295a37e61b92574f6e36ec21d411f4dc98021df950e09735f420cbd5472a9e88183b3bb353b2d79df1a2b402fae67457e404ca2af61b3e00a3647d4f42e1acdf2d6da3f94e657075cec4d8d9055894bf321073f8296ae89eb5c782881d69e44c670f027ea1964679d0f0b8099dbc176a86f663fdd51e7579444b4d498355560b23b2c9aafd6615f852c78ab64ad4c810312c691cc631c4d3bad1c4e02576cb03c862c14b3e8f3d5b445f88af4a1fd2fe97443853e1f904c0d3747b058168e80f12edc383cdba76330fcf3e8f5c05a8acd11ceb24fcf804e86dfdb80f03fcaa7ef8ffec20f0411fd7da2b38eae7cc2bcf91612ec11f8c65bafbfcfba142bad03e45ef96aba001618eb0a426343ff8bb4c290f71d43da02ca28dbdf4a28a65196f0c5eb54301f409dcf58d770c4ec1ee2d6997c5b5a66a53e32a68dffc4fb89721568d70bed68416e29c92732ffd5da4c8541b27a0c51d4e373ca90fc726db4daa099b53ae5e618c99a16362ea98cc600977c78a9787402eb05b97b9196f0aa9da5aaa3bc91841b42e1ea8cfe8c4e283260cb6afc283bce209d6c81dde8d9cab05615cd7acea9a7ec880bcb0d0e7b74b97dd94b0c91007da703b07c38caa63adc7f9d57e67ff20a6904e08e9c119d9683387fe4015c0242aba057a911e9e70c6e5ed9afe73dd2b3dfb5ae29d4b06976ba005064fa379ca8a1a11eb3de1342bbd7d5aa32419e885b4d244748117b42bce39f83092dfecdbe2b755df8c09976ec5d25b98c2ac053682d7e41f0b85981eda679699e1fb588b4e49107132aeaf538fe9e7e5231d12471107e8839d7d8f170c67ee63c69e8d33d46edefdaa93b1f8f1b4c4a6463c0bc428a235e7a822ecf471e2172c2c04e570801e38a5152a1839c8e50f2201302b01527a64f523feb1ef901243765fea6a5ad639bf8d27738956e8480e61e0c607abddc01ef320bc0148e053c405535f38b9619b9a2ff05e5e061308cb1da54d585bd244c9c95af894b714a14e7c58ce9d5340df0108e4d888f6df3fb1b58fc0fd67e734b40e1dcb770521b5103a96a23108c8cbbdcf308730e7044ffa3efa2010f5010205870a5f9b4ca3655aeb2dce46bd4b922f352b6c934489a40ad9567a022fea884c963a7716e660eda171ba5d6c8bed49aa37ac353a26da0d48b385f1b71c68b1b5d84b1f93fd22941f8c1b6be92a30017a286d4f0e5c566959de19b8c37883822db147d7a881efca2c596b6315a2407eb938d82d2c4558e93156dbfc8bd432b8703f37a47b6036151627a42d5d75b072551696aa85a1d202c245da9640f9a19616c429e338177b23a4e00868db1ba8627aab4d52b80ff37f4c6fbe0593bf66f4e2d8af4fa1bcbd87365a546e6cd7cd3b44cd61b173f09eb419eccbea79fa5914caad46288325085b267597374be920af940dcd06105f2681ee5f90b53e6416e3551cf39d803a8fe4eded6a325941bd1db425b114386aab4ef782a491b94735e15302ffc5f98e148b9e6e3583828ccbafa0b4ffc380852fc879554674e6caf8c75f79e260a41961408e1951ee083cbc1487ec298f69db8a15bbaddee517ebccfc42fe8375d4af9ee07e3a1e8639113d20dd2de8e8fecedee5a5dcb54e345c797b5f86d1aff1875cd076b1e8f0e45f19581aa86d9a40e4180fa97e2ace6eac9a0449949b54fd198e1411b62487210cb80c58ac50852fac410a53f61103206559b968c9afaf60350d55add5e3f61aa5ed2b048eb1bca44eb2ed44b8bed1e8d5c8ae6b67f52505261ccc1ffc2c68dc06397fb15670eda9556095f4d5e2dad7e93be3f4c6cabd75856cdc7b45ed5eea832dbe47bed9e384a5a94bfaf14ddd834fdd8018f872005a7f2e20c2d64b747bd7d8d5be6b891d0ccb30ed73d867929447a3e3f81c727eb5098a38c081e8cb6dc36864e7af47a762615ab8428195d348ca7dfb505be0c402e0e1300bdf41d48e78c6e12f096e794915fe5dc0109a27dcafd29b11a46b42fac0b02de72675d2783f535cd16d28d8027ba9bc85b6f6ac2152e13545ef1d03210204601ca70596139c35fd2c80f7709f48d85ac42e4c70da65ed030d3012d3089db2a9a6b407b3b4c58a45db66c0c50f4a5b4e1cde16abe0b8e0858109513d54edbee7b9d9c30d27021bd7361099909e66a5ee21732f4d61d7ab4dead5353453ac3f0db10f9d0d4007c14ef31bb042361a8f05ccf3836aff34d00ac532b95981a6ffc88109fabe19664511dce40237e475a0f9bc41f59e7e227c17fc1321af1c1c361531160142527f22ac4005862820d3da037ddd8cc5506b1b0d8f3752ea5c700432407b01aad334a9dab534048136b0af16304d0b2613042fed603c42c9c1a6d03cda570135905932fb6eaf334bb66c0b7d22a260f1471c763ce718bb25a212fa6f19822dd6419fe09bb2bfd12538531bd2d5f9e3100784496b37675c750ed9ec4a7d671f978b624859b97706cbf90ecedae6334c08742eb8471c03478d2151394cad0e1e4e28e18622f5e17b3cb01cad31a42ca5cc09a1dc2dd4d519b0a924db51a06605c9c5165d8bcf1baad636198cf629d757add24caa53c897a70bb3ced23a19926ca4df8addc1bc983f319b56971714cf5555b4a93a32cc8c72df886d14f2286663594f5fac8831081612176e56237f1fe7aa41ee7f9dc25104ae9059caafdc625c7f96b332b2265c0535b538525fada061816349c0fbcd2d47db1fe6b6679235f7dd948ca3a4be46da24fd27f6d3e8e741aa791068d19de9e6468d44701dd8ce91dc25da7520ca655c4300599abe96c52e7c08eeeb7e215fcecdf405637f93258a74c2c221d29d1ebed13a493924967c64723f88272b7c844d37a59af6b3cf7e1d729a51a640a9fc3a61308e51ffcbaa59f153daf2a598afcd8f67ae37a3c6999ebeab230fe99a2a0e5edb183230f6015c1c344b72e295539e5e6392d1a28aabb684ce5efc6b29b47fbb4b54c09619e5cf3c248c7ea6da52bc357e30785445f8c840bfc529cddf9ac8ceb5c1519aab5bff40bded48c8d6ec61ae42e4ebcd85cb863f3a14f838cf815c4d02324a726693512cb8ad68697a7f0d08d6134d3525882290003442fbf17e2d2b1d83f7803fac7aa6af35002ba3cc524d23ceda762f4401591184dd30f1865fcc4607fa0d555ed03c6a1e8a7c5c75af28315b2810d7e55dc8812b5df9e0bc7572d4fed371631ede503fd44a50b06ff655095bdefa96eb0d455761039d8d789bb38e46db7e26720b7d61bf64a1d164847781ce3501ca5ceff69489f11492e68407eb0d2ce333bc074e64e235983119e32037ecd9f1b59b6b67556822b98358d39ec724db1c3eab9226ea38a8357b70e3ba2d5b3cae4d42d1422c299989b66369644e346adf6d510af172fea164444e34ac8c57ca47ffe7212ca98562f6b84cec1bbb8e61825a8975ca2a15e93576f2924e151160786954acb27d1168924a7eec93c7f2c2fbc3f96392087444603e6c8aec9f78281e3338058d9d04db2cf26b7ef6d54b8af0ebac77adca7e02bea1b179020060894c218927704724437f1145f800d80e149a03cae94e975930ce3a6ac68acf33641a797a4bd2307c8325f089262e0604aef1090e60e713dbb06140c791e2bf78e4f629d733367c52c72a0882c86e4cc501a2ee8b76ab5f6b8e2edd178b5c2ed4a3fdd6f65beb46424b5676776f29a594329e08280a740973ae91c7ae9b698cc8f4b35629d6bffe559f596e54356b98a147e0949a4c5fab94df1acc5bffaacff52c5451a5d2748032c62c91031b574441638f41634c1c4fbc8c21060e1dd6a801e5431729c08cf1050a1120192e763822a9082a5ac6ac818394524a39ed992cdf43a7a4a17f9571c30ad9c51026e4c0040834d878e114061a65c091a608316d9cd6a4c132679436640d5cf27357b9c197266b6cc1e24b0d485cf0c413356820997104860f35830c9211409ce1e20a377c040085923069aef090c51aa824f96b6099731fe0f26012ed79b9ada40752e95f99d6aa0ecd2814544aa1cce9f2992f52795cb29eea12d057faaa5db2de6ae923b26e593b3f3b40b4ac3a4c28c073cbca7325c63651dbe8f576ec1a762a6a5507c3bc4d8825e79e1e776bb5f10379ed526ff75ebeaf9af7fbee754f2ff5c8e230fa2a76c7c4f1c81eed85587bdbd8b953d17b27d66fe258021750ae7a146281730bb1b64c888577d0e4b9b34b997d7a22781b5051569d6ddbb677a25675b64cda2f76f61eea81fc02c3300e1339aede3b11faaede5342df7d84b9b5dbec5ad62529c398bbb6baea86d5fa49fb772dbc27829af616f85dd3ee799e87d20e2002afdd7b4a1cf3e56d8ed244cde63d54fbeebd86a391ad8159cb5ad6723e883af80f95c18f46f6f7efda987f359b7fe2f83dd49b4cfeb4cd351d6473940e6a5d1bba9f1ebadb467badcb9f585aeb66757b4d8f98c502c1d56a05ae6ea77d57fb5657fbb2c7711ce7799ae7d97bdf344fdbae2581ba3bd5ae7910ccb2cf6759e665d9b950a3c4f11bf52c8e4676f63c3deed89e0e1d3972e0e0b85c3737ad168bb55ad9d8a854a9140a753a8561ce20f87d9ed7751873dcb6d95cca20a954b6bddf5cca20a98f7783377ac4b677d65ba2943e582a70a5479bf22ee58fd43d3d9e447cd4b3787f12c7a11b8a23ccfddd6699f6bcfc0a3eebf11ba41b14477cecddc76f0eeba1bb399d5d3ecb1b69cd1413d386ec567a827c9f516aa9a516d3466b456aa9a5464c4c484d496694e86bc7524ac5ec1803ee15a1eeeb0109d6ac59a3b4ade8819d6da5521753206d026cb93485119612c3e43fbfaf951a77f5a871f5627a34b287eed8b347f9a14bff9168d3571d68530d29260ac6cc40ee268b7f14650f26dede55d7eade5b374771cf98134f07c5596fa61e45f81ee1669f86e473dc4171fc0641104c690710ed13f7097257896378fbd5539cc8adde433307fe9e9ecf899827040cc38f7973a176398068879fa9cf671010ed940ec18e1dead1c8cecfe7de5339710c9fad9e4f7ae8ded5391db47a4a07dd84e2d0dddce7ddae8337b747b5ea8d8b75cc6bb5e6b1c962b1260bcbc06c3e1f6461cf66a8476c825d778c76e7c46e7618c3303ef71f45d9e5c1ce9f3aa8fbf6efd303eabe696e77415d9e9e90dae12c562cf1eda477d223d13e3d258edfa987e268648720a8c71d1bfcbc6aea71870e1d3972e0e0b85c3737ad168bb55ad9d8a854a9140a753a8561ce20f87d9ed7751873dcb6ad567ad43678d56d44cde663b6c1bb2e6510d5c7bb5767b9f4886df0addf8852fa68d9882c6d63a355e0a5fca13a08ea11a5d2a3f7947d28664789e3d03d8923cc9d6f3d20baf3a7e741fdf64d8fddf3b3084ecf01443987afde8fdca11ebf27dd531cbd6ba7f947eedd94bf3f500775a3a787eeeef4769bf7f8d1c8b697690fd95da3b899362da8b5d65a9f437f60afaf4aa948353a767bdbb88f76d31a868d4037f580eaa079faaa54edddb6e3d5b951dbadb7bab38ebac9e7701655ff4490e39eaab5d65a6bad5575303cc83de77f5fa881f0f79e9a457c95387a97ef1e661173e2988ff3bf5bd5b967712461e763e1393dba8678c7dc413dba420d84c17bc7b8abf42693390d0ee98e7550f75007a5ce81e298f510dd9dfe5297bf4171a71e504a5bfa6ae95f140afc054fb739dde66403da70a24dfe876bddf9dfc9e6d7e61cf88cf5786db883dcca46af7e577a9c9b03410e3cc8fd1ebcc6811c075e779fdfe73f3d721b07e16fe036e283def7fc79efc06d3b4efdfb9eb3ea3636dfc3ab424fd21fe1f34351d257eeb238624f9d4ea81308be0337f09b1ef14150f47a28087affc4f0591cedceaf1ed0f750cb9ac5eca077f0eb3c1d843f3f6e9772e79d61d7d6267064fa1f30d040839cccc2b9061800d1acd5ba29b2983025f02891e79e42e69e4b6a30ed1e5b2e8599b2c39809f302b69df43c9873df6c32f250dd230888466db3cefaeaa7b782c06f07a9eced28284b4b4cf6a8654993c5cca6298c75d2034afdd3df316fc876103cf74e4bfa02bf7bdc53e2d85dde01f8b90345eed283c0dcf8e039bdc964f0de2be609497d3b28629e10efae3ca43bb7fdd3a32b6ba0ee3ba73709fb7bf7cbfd7a3f736f4fe94d266ffac3e774107ed641a86f9f38827a886eac3dd4e55be268f7e9d5f36002a1b4a52f961e57dad2d7e913f269d5a52724bceaa1389a60ab8ea7873d5175ac759ba8faf4848ce03d239bab754f4fc8f81dbca7eb0ef55882adbabde609a15bf54dc87750ebd17ea86ed568e96bfb67a347a2add236d503a2dbe69b68f3efdff67d93c943f57b260e7d43766fe75122bb3c98fbfbdcdf3d1dc45d03e2ae6917d0bcf7794f8fdcd6b8fd5d7a43e6350e7f6207beeb88bc83a2a4d4db4004babf63c9012c6712592e2d2dd9dab90e3a3dd2dd1de51d1447241b4c5da5d2a3dddef35323086af8d33df059f4448c1245c8c2538f83b9c377414374877a84b93f20babf7fd7f4c8fdfbc46e7a43e8a7c7b9bf77f746bbc1570fc87bd63d153c463f91d217bea72d7d61ba3b1dc47d725abb7c4fcd1b13e5d00452edc99a9e964cabb55604b891b3d7e39b4d46c6ec087407d56fbfb11b0b0dd6667dd346a072a7f680a5e71ddb69e9d741a6a5b701a7594ae4abe5a6475b6fc59168639f19772c3d0ee6e6b87770cf9d6893b06ba611ed6a9f59961259729366d1640a2b69957bee794b37a05572fdd5f51edca06b96d5476be4953824c9d662f77e16156d2a7448a10a29ac58010a57a290a10a16695c71e20a208c50e10c4d4a06802c61aeb860cb252d362c65b13203344029f2a2054d18294924e0c61193161c5d81858b294e6abcf14213439c51c10f5a4833a024b16245881cdeb0e268a806278430b302124e18016685344360d182136414855123c316484b51c0a8d20519444ea83069a2a10d2b340cd1a47ba30d292d1081c310653610d325852c94e0724398a532638cca90e28215701cd57064420f48308c11c31665ae203501854a0f428c6142146c0cdbc29a37ce6839010c5d6a98400f5f9069e184259e50816512c3d146260916294e00c517250748a38d24379c24c9a10429704812430949525881441247a3174a8aa080f2c2165a94b42a69c008428c2064c8820525ac6086268688b2c6cc1133c4e0c40c4fa0c1841b47f01005104dcaf810c61b5a8e66a0c20b15518c9244a942c31218279a50e00823cadc00e585294451a22851810a343a4c41e10b1638e43086cc9a1b56f0054a0e4c1ba881c4850b4634090207ce8bcd12295eb62053c60e2ad0c410ad208935c2a4a0c21938be5c51834c0e493b1c6186970cf410470a3f146146891928507801072c5da4004c9625341f8e06a0051a2a6b804063698b11f440460d4e8e5288e28486b9410254c2e8c1092f98d820730128253405f1031531dee832e50608a459c3c60a297841628168052f6a683912430b242d1c97189c3061060b66e080438d962635062c472dc052c60f59a08172a6cc067c48420412461c39414492c605102645668280630b993272b40851c507348868e249064a589e2c41e2a805516c112283265820c4971478d06187151955349949028949135ab66842e945a0c7d72ab5554a29e54c62ebbaee1dd1e6c40d2a007cec89803da702bc9c6ed4c6177d4ded45297d278e09107b6cdd83bee65fde953ce6f0e821ffe4bc7a50aa5d401fb05b0fd9ad7943b4739c1e4fb138e68d3fbdddb4f12d7d51dd7d7a1dc87901be9c1774d9b1889dd334aca8eac85f9dda4d59dc56d76da6a35b09723299452f6c2bbb7e22eda03a26606b5afec56aadb5ca7b57f2c472a002d340cc1a475268e9a1c915306596784183154e967cee113a0bfdc40088065daaeb6244a7642593b5ce7be909f90eba348926b4d8c46861e3ae502b3d940885163051210a9931c244a15b5888038837aed0c082182c7872e2b0d06965e3aeec518a195f9b6e7126862a927ed042298c0e9ca09482162fbe6c11c267944a5d9e6060baf8d0250a539719ca9875043c3b07c8a0637539b58301102fc2a03b40763028da32c41055a2e092041192cb19388861060635c0d0d0c406274692fc44f7ca49e54e764d588c51030b4f98f142195d56985636ed521d17355cccec249a9cc84d517cdd18442dacd0041352709022862a3470822a698290e28928599ab4c8b0347d917156b87cd181619dc5450b172698b6b855bbdf31f79c41aacdf6976a5ab661d8bdf7de8b8942e24c7519bba3b8ab66e570ef9d94d232a6bdb5a6ecb679dad75b5f5b05565a6b338c8809f004955e6bafad934ec1929e52ede3f6f17ec4ae69af6ddb5e75eec5302c579db96fad785a6cb196568a7bce39e714122dbdd65eeb85a495ce5a29b5d2c7a4e38d0a75dab505acd11f52ea11c31e839dd36a6d36597befbd76d27b6dad5a504a2bbd92a7b5bfb752ec5e5b27a5c1ccea0423a3246baf9df75e2d63429ee0a6b690c4b2add6a814365f2b25cb886b8572b616cf6e76313d626063f729714b2b6da6c3c8e2961fbb2029337985c46db54d81dd007d19a12f5b29a554fe524a8bd017b556530a84526dad0b853c6fed116be9776857eeb5768ad226eda4e8bdb3d65a3b0c7b7dc6302cab7383b79a56cc8af96e18ff00225f6c6343d8aebf40e88f6cdb8cddab5d1c64dbea23d2c7bc04637f20017b08db551b15f991c3eb55add50103905aeb2ce35a3b0226e39dc1d65a6fd3e71979a5fc78efbd265c8f8bb4f75a6badb537b379639732556b927b654a4a25f2c87a8ed51dc8dd630703fae3de256bd5c06217bb17bbd584df31db6b7ac4aad4a94dbb7a2c02a22063538f294ba98058cb3c82c8db47ee23f631fb78b76d7b711cf7aa23e7de304ccb32ccdadbddeeaef161d8077eb8f3363336e6c278cec70440d16ac786b6fdc4be8b61187631ecbbda7e3a34e92886e5cacf39e79c76d62be5e67836edb5a37d04623f26c08ad5b4ad592d39f15a6d3b56ef66a5e6c9a0e14073464a8fb64799d890f1baed7273da7be87d507dde72cbbcb5e7ade9d1ead0e7a55cb51ca4d02d184bc9a36519942f6b595547526a2fa59a35a5add65a4b3f3094ed0ee49ed287b451b0ce4e018688746b8918d05a2206ac269fd96a2ba5e0b696ca11b22d83b880b25d9fed7a45906b36466b25bad4f2c0430aab870f0cd9cd4391ab02d3e77efa5cbda4f3b9cf55471e08dcf5e0e6610457d75a6bcd6a9a50f2552ec02a66fe040b8725e6064fcc0f1d982a6d58c1609282d89b0e9b127cefbdf7526b2bd19d94068cb30ca395565a6fcd320ccb30ec5a5befbdf65ead5dcb627d47add44e7aefad9c4ce86da6738911bd9452249352acdebb894728a6bf036781b51f415279ba8f49e5c9973f924753ca62a4c6445f3bacd36cc6a61febab52a126f275fffac9304c072c669331bb7e26b9923776fdb47403f275446866408b98eabda73bc9be6b76ad15fb346d44fab8c7618b1f489d7b90e9e8137ba4cebd275e299db8832552cfa5cefdf602ba41d28c4694d10d0dd30f7d112df1e48c166390b16445cb91325d80484118356851c50decca1a45391c52cdc23281d919353b6388382e9a33b42d076f3d4cacb370e8c66c19d694e4c21e25521743520a7bcc181b198e8c0d1368666dc9a97cc69299b32587b1448f42393bdbdce3c54ae5cccae0964b5bc46cf1b2c797521b67b2cac9b893e2911349d5993dd2e49d790d3e1b724f037e1ef0810c8284844929053267117d4d17fddc54232181bee60d30e583223d3f395316fd2021a167be0193524ae9ce3c06f407067b1e15461e73d63a4d278916452bc812050c46704d24c103162fcab8d0e433df53798896285591823248a88082227ce67f8ca6b0cee4f13ae4548fdd1191d0173d5275d4e891268f3b94e708d5a13d6cc8638f4d5f65107a4488cafa8b10027d5197fc7449a1293df43ff43bf4405845995eee89c6964b43a4d97387209b8e08edd0262aa59460aa6c0c363d2a8c4c2f17cd56393509e64a7fb3bd5424b226a094a260c3a28a0f7fb1e52b5828e16c8b15d659385b2e6db961cb0a3693a6039e52da2ba594f2269846db520a3a91e7a97ee95791472ba9dcac8efea0daa553069892bf056065861eb8dcf046124bea94344a9c1146d31266f8cc7ffe352fab0b584a09d65a99a4d0b9c4cc0e02a4c8f392fe0c2c93dfa94eb4d7abce779cecfcf0d053758a7ca034e94443a99541ea07f405db9bfa021944de0ba40fea7301939cdfb177a6585d6a0b0b299ae1b0e5842d4ae8921635b44b75db964b5ad2d863ce455366db02e72769604fc430809643295d6db9a4258b9db75cd2e2c50a3943e9c3daac1de1c59c2737d47421c5b081850b54a280728518128a8c60d2a2e98c14ac10871b5be69c73fe354f65d468991395a4cc1c93a6ca5ce3461573ce34d29431e70c430d5691913bfa9a735279a202bc306754c7d025cbaf2f59b01151907630064034e852dd1726c8c81eec02cab29d01aeb5d65a6bad1c0897dfc00bbc265fd909ac9924b7b1e5992daf75431d3822836856beb20c09bee601d9bd9180f1ac90678ed4c99e7bec2cc32e5fd9af0794fd136dcd2ad68935ef2c849daa693bb8d65af17fa50a8f698b48314f34b1314d8b1731603f8b38ca355de1be6c60cce03155745d60304e644813851118428c592d91060c4ec2e8410dd2166a844a72705a94512b065305ab544761a50c154b5841c5081756aa20b38310268cc941cb32c607945dc166bf48d364c6889733bc84714202e369ed15ebd57246597117cbec8629e1858b3d666c94b72d97bc88d9bc8451a271196756d65b2e7919c24b933dbe9c6c444ed68c81610a1156d678c228090d432cd9f0461651b0133a2e8b0c862c8b951ab29f0720655e58902981c43aebb5e5121468405146ca1071e4608112518e825023b5040faa3042bc81ea1284092726464451f510c40fad26d05a4b05866d55db5a2b6fadb5d65a4ba594326751fa41cc910b2fece043c4fe070b2d2d6c5aa060210b52d11b453ed65a0c8cf298e9b5495292d9f20da03fc02dfff300c993a5bc804b96b7526b76005038b122a2931ae1574a299da7cfb5869c3db7ddd76863c795898aa15724c975c9cc606a8b342f50810c42b57d42fac02cd3c6fe4516f4854d61c55825fac2b4ac72217d60cfc4dac5bc003bb65dd1c66e4b50435f98556363f64efb996663338321ed760b40218fd6881ebb35b2495031d6c81a59236b44c5689666d1a34ac9de1d7bb542feb062c81776acd3f62cacc71e3df658a56c4cfb32e661afda36491fd80b30060a15b3b15328540aa542916852e5913eb30334d93e13b13dbdca247d601f801af9dea649b384fed85230a22f5026153b3ddab65ffe188936f636c872386f834dfb764b792c525151d1131fee973fb4675996651b70dda66f26d67dafd592a6529218a626339d48c2f7129ca0fba75d15903e2474ffb4f741a7b5aa03844b562633354d65aa4d4c26132229549064d3911090541e2b8100e9f9f9a0889105a080b4b16b99f517962ada48499587c8077b2712f958a5ca637db05b3195472231d92619645e231b14e4b66ddbb66d18639cc3898a86a5a03285954476045602cb61b8252611f417898f096b035765d3ef68f4af3d7c4d505c45b16af9b5a50915e877e8bf0105a25f3863dd697b26cf2db7a64756766dd5e3cb8aedf0b4b1465b69db4fe185beec311629f5288451795fb1a46d8f445f560a912ecd0b98986a533553d3142d9141662daa45754935aa4cea5185428fe8118522850a52920c32003499be8ad8961e55aa3763260d2da24ba81165a28494a42486a9691a4d2326f36842914cdbc8911090541e8954349750a51d203d3f1f1459000ad97eac52acb5d65a1f4be4635f912a8ff5b1af4995472255a52a460699d608097f4084b966db63216448b673af80f64c9720bbe69225d0be6997943e52d472d5b90560dab90772fffbd77d26caaa4def83ecb3005b7c5079a8cfc5303d8033f9526a51c08ee7d4d3685e801dfb4906a17aba30050d53d08034461e659a0f365abb02609143a8489066120f774c4f23d6983f4df372ee582cfc4d4fa4aa83bd005a6499c1089aa6076026df3fc94a7b16d11fe3c4553bd6b4d35df7f438c8aecd14e80bfb5c72fa21d34b1fadeacc6934a14c238c0ba9643a2bc59cc897963fb4db2d87beea35ed47fac8aca669ade6caaab2566ab5add7a67d827f6410fa9c1f1964cbdd77b6c4a23639d86acd913e00804949a5adf5950c6cefcd979c894c76764a3dcff348d8f81ea65d873b916863b1ba4094cdb966c889a22ed4ac91a1053b5825512c01070d60b098e20a0c0bd110362831650d18274a459061faac9002052bc400260a1042a081c10517d860610d2c658cf8382b34e8619a62a0a832a16822b37200b65c82420628967c415fdaddc192ef954ab0567950c7581bdbc180fef08edde6d85f79402cc79f9de5cc62617c4ceca99708782957a5b25172d26e733f3d6a4a4e5a12196dede189f6cfd6f498891b6c7ac71331a02ffab2f7db6ed946945507e3ab442192be26c6c7383f7525e04ff9a913c94f6922e04f5a7af8f4d489d85c755262a3d2b9ea58ac5d444e0f531aa565d5d9b0877f6d2cbfea583d0a0901318146d4d1ac851f68e6aeb7377b565bd7543fa57e739bd55dff8e3d31fc27caba9a73ce39e79c33bcd4c7bc21ddbd0e3cedd33eedd33eedfbb42b83dabba6ede81dfa924472681cfdaae35d13e71a162b6bd74a721cc713b587e2089e7aa9672f75cffb77dbfd7c979a36e6ad799ae7791f775ce03b3dba3ae82ef5e802350773cdf6f4fed99e775707a0bca7bbcb4df45f498ee37c7ae09f4f248706e79a2bc95164cbe3e822bbf3721c47490efdb325a8c79fdd81c752efc4d1f374ea9a0e4a3deba0d53521209e1602b253dfeaf42e1b6da5fabda6c71b2de9cbe696be6cf43854b71422f538b7eaa74b4f88eaa78f26d827f993269ebc7fdde99abcf74f75fa3d5d93f73a3dded377f9b54ebaf5dbba26b6beeffbbeef071aef737b97557a97b5ab2390fd5d8ae3cf9697abdba42e2bcfe9b2f264d7b5caf3dfb06c522e3dee6c79a35b7aa5c76ca3557a6469941eafc7d7fe1eeaf195f5b833ee7cbf28abce872fc5510848e779d57ba647ce133b21dff1a779e05c9f488ef3d8218afa57a26387d681c343cc712de254cfd3e3c411f573883c7e51bc0e7187f53cd8401fe74a783cc7ade78a803ece89f0788eebf84f0400cfd1a1af65d55192a38bb6f769972b02bf78223abee3bfa8659557a2e33b340f2d7a407577f73c0f76e80b1f73408473f14a727c07cec513c1b9a8f189e4f80efdaa83bfe3e289f0b8dea1651535be121e3a571d7cadb1f89fc88eeb10b5acc757b243ebd040aa0ece818f23ca8a5f8580d0f7d49f4d5f8580ec11a6962eda728265e501c78cf5387778aff388f614b153713b7d28565dbbaeebf0b3e3530f886ece041fe063c713eb6eb3acdd43f1d5753afc15f36936697d28da67b1041b6862b7181fe35f5113626d74344138b13989b24fd4aa4ea6b3797aec19360ab1d0e4b931cc520c338125d2688603be3e920d79069ec7f71ecb9d251827eca53b6de4196ec986ccc2822ac6e862830b4aaed8c0135e7421238c2154908598093c39b3022e96929c11e589329ae802081ad4d0018ca32359460f4d25a40003952c4848e00d16604041195eb8a0b156bee0e0040b4b907200838a214a88418625a614f1644a0bb01c0961a50c1b5f8c943843456ec1a2868c384ec0658d9a2aec1629b46861c5145058b1660a294db08023891933a02843c51425a8028412264a0882cb1701a823c2305ce212656389b730bcea74f2c2a39ef2bc5085ba867a28ca9d42e10c1b92611587e26877f8ce03cafff4e9f7f60454c1673d412dbd21197d7d5a236135330bd822d919ea18f7542a8ed9e9e73c0888363da63614a39ec963f1c39835be36c677db36ef5c786939ee2008de8ddb384e3b287affc431d4423db6b7c18deb193e2a75ca2d3d6a298dd236aa6dfbf52ce0a238dbb12beb50ddb30c9e0efefb5c5f2aa55d3f72a7de81dca913ed9398654fa1ce9d32f027916867e033783023b2b773e2d8ed4d8f423da4dd55deae68a7644ac6c9f1d4ff1dd771497f881fedd6813128d2af4455d71d14555fbd87a2502814ca6501fc951e5d546e227b75941e896cd4b1065715fc3ed0c8c63fb3a3f4221426b5d6ba1255c7787a40a8af5a5f8116e8a12a71eca152a954dad5d244785cab8ef546bd0df2c32ddc36cc23fbc2f09ec7c3ebc22edcee8979753c3b1e1b15a54f4fcdbc07b87e7350a4cfbc1f7ad56df70c8b63eb9dde64724bafa80e72fd460789287114eab112c79616eab15d5ad2d7afd2a3508f2d5e0289dad2d7f577e8d041698e1c4f3d47ebb895e3e62d16fdcd5b396e58add3160e0e0e0e7dabd56a712d6e4aceeef26087a794869803eb11a95d6aae567a05825d0a5c9202ca6e618e258e3d36eb37e24864dfb45aa73f513dbe72725a3838bb8542e9d34fabd6e94aab8e52b57e6a5d521e7dd525e569e931ffe6a76bb1a7b244f91b117b16bbdb84dfc25feb0185dfc3e959409fc7310f8c87ebdeb1beab955e81e06a14eab1c37b77f5d4f07b0f0dbbccf380883e8ff750cc83f2f8491cbbed1d6bf155677a435a5aa32fd4a94a7c61ab1e8ab8763abc39d16e89e377eb3654bc69fd445554a5ba4acc0fc594468940b6773c3bfa3dcc5ad257d683488f1d549ff02cc5619c737610f0cd345607c2be39206c39112d8ff59b77a9efc79fa83a2886299b87aa87df10d44fff0e822008822a0d84d227f03df51bbfcbb37efac42d6f600ebfeffb50cf27105c280df481b0f3e5066187dfb18da5627dd341ac9f7450ea5b98b359ae2ee5e19bae5be1aff00aaf56ab70f52dc4abd5b75578306cad74abd5021f8661188644a0f448b451a77fe20842a8411004433004c383c701c130c409f598fa77d571403d763fbda79e44f94fc49e1247a19c9ddf1d750ac3d34371ccd9e1290c73e84b26b5e817c7abd65a8becfbaab5c84bca4b8acd53aa7b5fb1c0d33b16ea955f5d98f39637d5435557646f5fce397fe12bcc458a64d54ba57aa8835859147abdb6eab559ab8d65736fb5ea3a1b1b9bce267707ffd9e40e638c3b295df7f9ae03354dd3b2fec4f15564775deb2c1bd6eeba9f342d6716eb147e77791904c1fcda75af5791d71e61ba5171a687dcfaccd6aa734488754488e52488cd736d2dad4b81a88379c8e9e173d7a134d029c4db7644d3f276646f478e1c4109b13621d6de8458db25c4da36cff33ccff33ccfebba94a753a95410ac231bbcac3c398e3ae931e7c8bbd3423d6a422c168b455fac9d23ac3dc2bc6744cedb6a96e26cdbee901076f66f3a847d3f644318b299f6b01305f52de5faea36adeef9db7116f13711ecbab76aadb582b8b39b7872610db4792ed77b68b7e177216c073fed3abdeb4208c175dce921eb7275767776bb6cd7a552a9542a95fa964a7d4b811b2aa5512854144dd3344dd3342d049e1c3c38a75027d4a0a43a9aa6816298c18799beecc65acbf22198d943b5453b366a09da4db88f45b2eda80da9c7a8b608b0cf4420a4f4614318aa657cd852e7b0f7d56d5a1dea5d0b757048f8eff9d3dd779438869ff7d3e77d1d0a4977d075da484e1a2894d20792f0f350de3f1de4fda4835ab56bad3056e1eeb853617c7c3b4ec4ddf78b3b15c61fe65258a7525dd7751d8f0fa0778af1fcf6897116bf4f8f44fb7bd79d8e42853f89767720f86ea87e394f6fc8f7bd13470d28ec1e8a22cc351becb2f401eaa1bab1de644e21b936cd1ea2b523b393b3eda8610d847d2f76b18b3d47e3badb69cad2d9274a0a82500e1115a19c9d9a46b5170ac6d8fb865abd75dc850771289e9ebb6f841dfb043eccd979cb39e72dfcf7afa786e2261f8adb087e039f6f314f48fea7693987e197ba500e08fbfb08c2060f7e7b4fddb00d3c69a11cbb09e508e5ec55d6a350ceeef0d6a1ee373db630cebf395f9b5f1bd6ddf2c1f0f3bc5fcffbb68ddbbe6d83927510a65afe7c0ec2d7404dd3349c03c20e553a5f9571706cbecaf4f58da99d59396795f85d521ed7a7ba4b945427ebb1930f45ec287114ca398956d3beef7bce393bbfa78222dd591cbb679ff731677b5fa7857236d6da2527e63de680b0e56dce1ea1b2e8a7064b8a6dadb5ca3a270aa8941f299364924cd292b4242d494b82425f55288421fa4ad292b4244dd358fb47a23c497a3179b22469c98bd2177dbda418e19e9e06fc3ce008d6a494af63edc5e51dcdfa8015670ad368be405f1aab6a1bc8922a797a9958e8abfe24431e279609a5ead41e69f24fa7a5723d552a2391c8a0c00ad98a148a54c872690b35f33956ec58a1d4a9d41aa17a070806776a0c300052e915e5ae4f80156f670a26112c39e20c105bc07186924fcdd28592121c70bcd084059f7a23475a70bd88186ed0018b3698b0410c9f8f853558dae0a14c1c4f7cea334b534af57b2a4f284a9fa42c7dd4ba268a302d80c20b2f6a7c3868e28912121a323b3ce153df5379aec516483bb001868c1ad694004493262ab40087167cea2f60227f53db050ba60017144a3043852c767803c9d1529927258c4901cd1a50dc283c09c515139ec0ce300090bd030609d4964b609ef0c02491724055c9d618a135a0e7e701acb3c0306131798166531ada6052d3821b4e468072040f3b58f9c18738b2c49126cb1a2539382e4436365746113b4764100db39f395207ab59b6e5e50b3b6284fec0ee7b3ed8a202b282b12d0bb2e4c912902f2160153ef8481d7b2c76b14bdb06b1ab01b620db5aecea02481fd600db4260d78c0dd9f5d69a1d57cf033007846db3d4c1ce894634ed3e6755cdae3c9a587b69f6c49176b1cc666832e746cea2c8f7087f916696d064a68aa3ccca5fc2d8e34b498a00bac0c2461059a42891c17921862e3a1c51a1430f2fdc197838a62dcdcbca972abe8cc9b65cfa12c509195851caa2091ac29cf0041c417811461138a4f1c28c2af7891ddc981e59ae28ca218a174a3e4835ac58a30217b2483ae3040d2e70163452dc1699a68694316648434b5218274dbcf0418829493829218d222e4b1a2ae02a1c29808105a0e3a8840c596873d22106491000002316002020100c87c3e1a0501245c222e70e140010759c44624e980904c22847411cc420639041c618430000919119a2a103d53cd2f32a64f9ddcd9eb4e7ac9e6430a891382849f415451df91b8fe4cf8538c3bd2bdfb4b6778df5ba72fcce6f119e5f16e1899001c9c598f3330fecf7adf39e9632896afc12c6ae5ba8888fe40aa9a7deaa7b64cec9fdf4906cb419365543d84c44b12e35f27b4b4eaf44dca39037071bd9f6d4bf60f8b7c92c0a749fcbc28bf8f6c03e154b1278ab3ac05ba1037aeca2e67bc8894a8e913c1ec93a2e89ed97f4652b61150cd48235ad01424f866932b947fa7ff42e994a5781a96103337d338766f5d8ad85f4742989570086a426b0091b8be5652acb88ee4b38f70dfa080c22bda06e446e3816a7d216f199837b2f504f4f3c883338bcd0eac0512389d3a745befe564c845e2153eed5b4cceee00e0fdae29a5dc89c5bf342bb5b5c6d20ce2bdfc961e44685afb5236588fd6c75b9cdfbf9470cb00f07c3cb031c7c415de8e2751589266a4736b61c12dc47006ce9b06e8a1044fcf5e0f0cc4044231df29081890ce20f7fb83f350b4a1ac4fb8f07b78ebb47414fa34af5a7cc622046901dd3edb1736ea24baca6a43a4d11916b7788e9e3dd8e8ffc6ae91207f6892e8b858693f8123b6f197a9452ede015db810a69c6f5ddb588c3011d3a378b38dc45274afdc7d37ce6d7dc4691f04abc657031fe1ad95b226c98b4facb36995c96493f67549b1d51fa8188d1ab584dc453647e313c6ca93ee4a55bf657acc40acff8f08d8f94529f31ae88bca547a4b7897082d542e83cec4878122aab06b92fcc44e22ab4aee62eaec7ccd2c2515a2d24dd620b8c4fd533ff91c33f2f9997fd9a85591861005fed15e18c089a51cae810ae19d4de54319def3c6fefc6bcf321a04a17197fcbca8480980991d150816d05143a210a392508e39616068a6ef2c55c29bfaf50c880183ccb23813b8b37471511dc4eeb731c4fad0822768cab50131d5930e90587ec8b5d10d11e7e4ab853851063a418564f6eb031994deb900bcc5541b4e03fef6c8c9585abe3213c5a970fdd12b313dbef2ed98462ff39aad3de099318f3c1977eebceb93e49c7e967d358c84fb649e38addba7948e4557cb3321678f9a5a3f1faa2ee557f5e5589100982fd4c400990628852ab9940192b0e23196edc12a0805a5dfd6edb03cf32066619a43710199f56e52114b7e17c4008df5a6caf13d8b74264e414f825c64e056cd0d04e44a3125eee9cccc7c04e8c187190922dfb7f4aa20aa2b0d3f8de069d0dff78b429d879ab648b68438c6292674ab43de38046ac916ef1105742bec5961597a7a31d04f8e77da303dba9a3caf272bed54e094becaf1021957b52d7368f2ea066cfae6a7846dd83d009e5f94473d47296e8d31f54e152fc42d60d9817959f75a7ba71ea1cc0d2d730aa2c034745a3a512eefc31255a9bd7928e45ff99a3131c182df8ceb2cb061398ed3d6be1338ec28d23ec504de8432453f9b8e4201302ed4fbacece54017c5675ea6d01feb4ac457ed31cac39e997a74078ad3d2988c3d105434e3f3f3ffbf29dfdbfc45f73a63eaa122ffc50858addc59964c564776144773308fe740721bd9ff2e8279cd7fc5d887853d78644d7bac8fcde2a0b5e30d69ad7d20af6d18629dbc0dbb3fe474496217c234edeba3a02e84f5bae7510ac1b6ff26c958dc3629dec2697d5ed85a041573aa31a68ea2d2cb4c7f143bb69a0acfbc3841e50a1e46b0131d47799ec71ba999f26bd21620472db62e8ec1530f9b9a9e44a055717eb412288beef5f50613613edb38392615d2e2821b88d1b8aacac5a176e8b43b70acbcccf4e6df6d9e3d703979b29422153a1059a14355055ac0d26ac652e84015e01c65c4834e3bcc790b438882484ea82aa55678fee87ba388c9caed566a2c296f5fbe692cc4df103d8269c09acda9c82926a253d72931fdbfb0b42f416a39eba5a1a83a8c851c8dda4959cd64d8a19b554b4148e1143ee1188274b2dfc5143390aa2088505382df54743c7c305c9c7f5c4fdf12cee2b834f6f45b1b07904d4593f9c86206427702a56f7deee69ef0d5f3f3cd6c0a64b8e80f618a2928234a3169ddc5f4c891d1f8142f942fcf608481dc0e46ac43e42f81128037baf5476f1f456ac2545e57f040a447c611c9a927da1c36323bc62b328b053e9e40810417c0621e56faed65bd7c9de42b825e151cb30b243414740f259dc6038d3ad1fb4d95c9302c4b7935edcbe2370d8e27dae84a54079f2b37a6b98281aeebebef5ce5a5c04c237d9a07a0cabd5578aa7205b415a01d6c3e90a40d2447358a16fffc14a4156aea8dabf7295305095ab025a1329ff43eecc0c7641df5957b5c05f318db0a545e6474d024ea0c058665e706603299a140aa6220ea52410010f10e7dd913ee81a78d915bfcf07d3278122f5e0c9ae3659f41f7df3967e005279c3bc8b06f2dc73458ed045dfb57e0a7c295a013df4bfa782a0d1f40fa3461d91643070ef7f6f06e584dffc5cf58b15f8441dc54ce7e7b7b8844a6465c51ffd37460a2e8a578c952ee4163484e3d060d3167ac657e98117d3132268aaddc0c8982b3f214d265e1870fcaa11b0af0113903bedd7f8848171275c7d32aeef5cfe631b3f0ceeeca8f19e83e1839109a96a5fedb307d5be1497d7a62b92e14b1ffbd8875015b3102fb83abbae86de9f647b08812e8a1e2b1e2cebc91b1318d574a39b101ea17d3deac48dd5fd0b5c022b9f13ece802795d74769a6da88fe7f174473f99e8bb0ec269470a88a69007559f529994cade9505c2631e351a99341dd9f1d1575871e43da545ecc767da0f272f359ca59222c5ba4945ce2bf1c57897cf67d649c14df0181feba6ed45cee117811812ffcac37231723abe6fc4d71e082886afdee3f06bad35efe73fb5cbf6be631f210416b45ae7163a03e4574c1dc368035c1380cb84f3a0adec401c6b5411e73af4c5465f05464fae05bf3ddf4fb1cdfe1d918ca053788648d30628dd071a6996080fce364c1fc53074c7d77b15ee0aaf50340a85c048a3b57c5ba07471c389db58ddde4858baa69ce229f31de121608e5e2b19af9f0059001c4b290d6c0be9cde7966882598cc5615d4b1974ca9d77f3dba3ccec9481d962c9522678a527e430ba7b850f6a399d3711a561e16f756e24f502af47d537a7aab56b2929e2588fcdc68cff9e740400593c9cabbbcee707ccd5b673821d410758e24b2c8fead9f213f3f66647547d230d4b0d46b00f1826b57c14dae220a0b0383cb09702e6f0421b44b800ce4cb99d52bf08abaf1d2b90feea8be01cb8d57d6143627b0122db4e4453810dfb0768605a35927d01cbd269cec09450cd54382374519f60131862aae38161b480afa4098941fe06352059eb2fad7d7f92472f5209407f29967762f8a7398b662f2a07308362448ac6ecccf7fed231670038ec9e1caa29f78fa60a37fda56d05fca97b02c782572b651b9d945cb9fed2bbcdbd6a6b27db5f8a0e4e5e246d10d405d04e76c28af32ba53beea7307e9d799fb35ffaea224b11c9bacec44f6cd676a62ca96e5b62aa947f4f84bcfbd7e29d35821da6e92aab73d4dbff8591c9d5987ec7906dc0be0ee7984a56f9b1cea86a3561ea307d49700bc76bb9feea0cf0ed24d54e2dbed5994ebed0aacbd43573c674bea0654ebbf4ec4cbcd8811620d6cc23025be89f965c48fa794cea5d6b4017ac718c821b5f77ef88c623bc90cd788115529a2aea3915257ad6e9f61bb69a1e272460aab6ba432664889e06a079c69df0fb7682082c329405534e088bd9003a4325d41df2f469dadddf54236ecf2446697901394823861322ae3c22fa838957a468d150303510a0d920fb5a756b904de073992942a6169a787a71d6079e761ed1c0207d42e8af2291ed38192159faab763662337225eff40bead88629f25de7f122917b660562776ab8188235446d40ede6c2591c55fa62cdd9bc5a5f8c42e501388948ef342e999a948824f7eca96c9bfa62761a4844be8f44c21de0676dd451f16c4e1d167926b6212a0d23685d6509442ebb1d45dcf177074ad32010293c91670ac894247da7af957c432072b4eb4db1995627b068d299e974dce8ce2f57100291dd39d4726202915a55c199e80ca443205221d79b4a3887049995a151e9f9dab3a98535e9ac9f602038bcb93d6fba2e433d51fbf8132cf8b158acc91619142f92ce33b3df431486a45359d7592e09198b6060294a5474a6b8e3f428b024887c1205ecbc60a9f0cc0a17aa7cb96bcaeb04d85c23b7e03c19b3b870c83ae7a2f1c15c0033f8346531423ba378592785bd5940ec93d75d4e712b0950e7fcfe7ece14efb90781e07c9244b2269188c527691de70452731204786ece486f4edc5b0346185914f8d1229cb31615a0233ed940c9191ed61052f7e29cb4aa54facf1b5ee264524d612925cde0cdf41bbb4fac69cad4424f100baf5127178c2dee431d01d1417602cf28804e7ad30edb3ba6046a18cf60058d12f2bda9a3dc1dee61734d7528669506f9cc68527652c75425ad17c7824e9ad6d39949e9de2590bd5fac725f42dda7888ad81fa81b5b554ba807b773f6f5a2b46cb311b01797b742d39942dfabdfad5d0540a1d31cc032e768b46276d27e955621704ba87c2a63660c9c6a566a1696fb7cd5cd00b3b3450f038bd45832981001013c2de49309ed6902346d7638f6881f5cdea954a092210ef5e519839b0b52dbc3b019d4dcb8ba08afa65b909dff738c991170544eff093a51a3c7c165207a1c5ba0f22ad922082857d54c282eab8b2240a4d982de1705d7e21d983ea8932021ce540f3bb84392dfd83180f60a9fce347f8380c27c501b64552a42ec8364dc85259954a048d02da6ef7a830cc2d76d870282e5bee2d09754732e0daa1ab44230bb390f68a486a0e079f72c13c099a8614602a04ae8d022d4d9a252fc732767cacf42bd835f73305e74c6e52e4171775c03d83dc37cde9ed537c331d816f81f4bba25dbe44c102640d6db1c554787187c76e55efb98ef3f75525955eba8eae2e1fcb456133e0be5b9cdce0e9721d6052f2c9bc715b7b216501708f3d9359ff2892bdf5b90a2b827363d19808c8a162b0509c0c860e5e37b739ddf2c8483bc1d3b057071bfe7f083db4595d8ef0175727e236173a21af9629874e020122b4a50ea3c656eeba9c7925e04c295decc43f2e1dec46933df1e33d2a71345906f8874c35aefbc58ef2b1c511598b9fcaede30f43284ec5f3a9749dee5ff483ade21b0d28ad91f20a6ed543b2cf394978a4206f48a7413438a16333fbc55d3287831c14e1091bd5558529c093eb49f2cce1c18e273d412095e8f3684bcef29ed8a3c6153c7ce0c2c8eab5708caa4dfed30b7fe0d323d0d8203f38753af28f8672972ac1b3038f7402a46af22c038aa0f9887d10ee60cb33b8bd445c442f1c52a50672a6d1d6737747962881fe3296684744ff1790143ea4452e54514c231194222a476741018342d603d4b6a9f72370e984d951c7e98d53ff515f633402da4fbb41b084241840657775a650154b5a11c0c08cfad58d2c690770402c9f3d90fe1a30c80d48658017def77c43d27a2961313e9a6447402ff8bace77af2ca43596f1218b75a5b86c63bee1b1f29fe4936097dc5a4de16e29253c612858a3e8a7eee2ceb9f5ae0013ba9988f14e5cac068950c6a22bae9963687b707ad0d4e6776acd7c67a9be9c9994e85ccfdb4460f7866b0663919125ce2e59c5da57555b4bff04374f4ca0c2b30f4b08a89ce8ba88b8ba63e85eb113746e2ca46fe0747208dd20a74a07831a1ad94924c13f3d070929beb2b41f3991cb19fd2a728855010055399322b5a403aa3b0c0ac1174a96ae89aed96cb41ffc31b9ef5c8a0915bb17e2dad03951155ec9e557935c8ea043db8c5c9931ab7eb656c9583041994d071025efc9db9cf327b3219ac07f51b4098a13c8dfae340fc52c6a0a7d02adcf848b46004df48b3566c99ca8637f01d9dea726d1d1440e73adf38890ac62e71984e4559ce4b475b0eb509cfc172059ed5d41e0466e3946b0d61d38a582ca36fa150192ba27ebf014cdd2eac6005ab0f294a683206080104a90938b3e427da6d6b4c5dd83248e1289734631e3d2606393c1a450957175b86c4186eee4a2e74bebb4f4d4d8124eb18e8b3bfd8d288ca34239fdcfcde1a45daf8054db866c8546a0ebeb598c076d881dcff194559a8485eed59946046765b8ca2374fa69acf6143e3746544627c4fa9ca60b6a9e1e03428bd1892c4e156df3e2de64031be57926e890147412978550ab54216b12aed0fce3f8284978ee0da8295cfefb133accf91e1061d9887775da6c0e2fc9770a418a93ad2bcb92b19787b76d9bce806c61bb3364087e3c493fae3647ba74e9c29c163ab7a41f4a6f0e69d6e84feeb60c577b01fd462252860fad9855131b6566baac3ce118bbb98add7f7840bb3094187cdb15170187b47ae5f1e2bf6f533923dde62bd852f707b513c60452bcb060c854a0fe7877e7a583a875521a1282e4df448b241a5c511cb6a4cc5137e1789f95a88d4fd46151fa8d0c6a35d29cad1721019e5d490f7389891722de6c4c725e0935f2b007048da88a5778caf0a60f393b77db243af30a53a0409dff9213489b7d340b8daef868d8c7b6540ca3674abdfa706cd82a0c55547280be6de0ba21fd5eea1510e054fec626e2097e4f08e00891d4d1b69c57f5d47c8adbdc97ef24712a2e5c38e958f6453006f3967e724e307062107f6b96ad0766e9d4e2d9c626d0a4138ca26e9b05902b5f66aad737d96e1178763b4c149d29c28e91d43801244f1ea6de48d764c4644ca8ad80ff0ad5a36f5bf3cbd04e3df981a43953bac60fb7302c8754acb34c4d24d24a39718c8835a0b92d23f4eb207eff06f4457c9005d1f6985c1bc4b23f1ad43af0b0921d9a2996770da3e966fe65d8815521e27a70b7b38acdc58676e23f220cb538ff2cd04e8412931609d7bb085a2a08f9dbdaefa87c1e314dc220a8e3affc9e3892384882a0c5b77439f87496b4ef222015f905bd8077612a7d8c83bec933801cbbd35a9816bceaaa522d5fb553735255b3a2f5faf542aeb8a184c38a143bc5de9957de4f92a5ab9c097061bd3ae3cada2692915478866e90831597821fdafca7e314a162abab7db911cc8c659afc547f2813ecf448880b91dc1f56ebc5b2a26a46c5e15037df484118582f9d0df163857e26d30417e6f67eb965eba5528d6154eafe3f355a2a849f89c3d70cefd3140573cc686a65f6a79576642b10b21746f929a60ab6062ca15818f5248557b91d3b24e6c7ad97890c7f031b19307e4ef64135e85b19c308e1566e01d098831f222401590ce088bedb83ad2a72eaa7d463451f616c08a5a45265984c8f44e84bcc330b71bf0b66e7dad0d7c1e6807056bd0d555ff8b148535ed0549dcac0b413c13738a6eaefa3610cd4b7c6f788395a4e079badbf2dd24444c06336927da4218c3cdde85bf51628f12de1e6f3ef8296562521d33e2061c59e187a31fdb450114b9c0f80e420e060c4dbf20601f0cd8a2b34f41c6831dfce5aa690f0eb068554ae4b4408f4d851b1b9213fdf026bd9002da984b9d8753d911e41b7819c0dd1ece2730d7565333c8a4dbce18ab77e590f101e8d3aee9cb871d1c54f8e810b8d947d02dabc59c5eb0616d7163f711047ee5f9f8db53865ad96e82cf5dc4b4fd4a3e2c6ca42393c1feca0c7869aa6a19f1a195fb6247a17829dd70a03f066c869582c0015cc53622e3ed53ef104516ec6b8bbe2c63afdb19fab149a7c5beadd8601adc865b38f96143771ef56d385ee1f062ecdb9cf8d7756c126ec491ee38d33024a80473c439e1bca9be47ad5cdc7294568f77071ac7686129724de9e847024bcce9edda35baec8eb97b97ea3935091f036944fe8988d6979f5eefd849d646572df8139b1bc71c8ddc748201b9fe403c4899ce3f95df8aee872e6e219cd62bb096183fce4abd3aa4292c70c371a3b2f44431a16cbc29940dc7b31660aff478e27d79d43b75b0bca74bddddb87a6b4f87e07ab22a27716bc40172f1d1041bb5474bcdbf7f985c6c7ee93413cae1d52fc893f4263cb93dd1757bf083307d3960202e74e604553aebe3887ab2abeda776537b18e6cf9e1da0ee90c9c119b08ad888571dd0518cc04a5fdbe3f13070013f09949b9cd8b503b3e9d4425465a1c0394bf5014abea625e80d8e7ff51c284f6e91d8a09191eaf8afe265ee3cd26ca4001b749902f882873ebe726c973d8691fe43cd52560ecf1381b62e1f946d689dac2f7d55a5273a4e168da8f107163ab5b49e1eeeccec46ed812acb70d1c37ce224896078a7a1fb3f7a831ea503dcd6915462c11588241bc62047441a0234200b0a5f8a87c1e09c2f21829f13ee6c8183cf164418cecbc2e7aee6715b6995645bf9bf920ebd567ec1f0b57eefb4d0ef95d0a1a2211a5484ed278ebc19b6f511e1859b645e0cf1b3fd0810f6fb8f3ebdcb925ea3fbf7eebdb02ce48f2cb80a8659f550f0cb7b5ce422ae650e274dba1dac3fced9854a957cc51f8cfe152da58048b99091d9f5539bd81b68e82de29aad8bd85260ce7b76586c573adedc77c4dc9c639f9a6a81e520f8c33772857d13cd1fbb0f782708a76b5ae7ea3a02d1c607836640cb8457545ceb4a9fac745057a4ee12556329816b3fc97c9c8176caedfb01be52eebcff14b4ea6aeb36a5c665b064d1c5302e2b76657f32ae0f31008e45ccbc09c4931e060de24c6ce30651e45598660e405dd9760fd9b5c9d1cd8f2b09e8f4f26bc2078ce27a46a4f278ad9a94c5e76290369f07f843130b19ec2b32011d8302b90c6a6588b3b60cf8ff694381a0b472d19e57130641191f0047d3dfd0d00c9a4dddcb9532d76d22d59a18d74d65fd21820f1c883c06d6654f3dac198a9875a5d42fddc2a4072bfc98181a00bba5d9574fa6a37618191d5884c14d5ff1e999e54f6759b8d1b01c0fb23239da356fb587448449fc90ed0284220f4b58900b6ee2a7c1b4caeaff980ccb334871ad0e9110b4de23233aa24a0f071215985105af4a431563fbce53817fa6a4dd39b1749a97de50550529e30cc713a52e84834604c217f00f23b4973ab9206deb16469974435183c3026b8ec9861bb5d0c9ac304a97f4f4d96e84192d30d4c01409d1f81e6013134fc45f077348e32e5fa792e64123b63b4f4932408f415d743b12c4bfd1b1ef61f97fa10c310a7595c9f30afaf1aae2480b4e83cfd57762c0222ec878f935490d6d797d1e3549068f5f78abce74fcab81b8f6378976e2815adebed20a07c4e6e97551f81f891dc8f4a3681beea06d3807ae92677370547314bdf9b1a27960ef07063ed101f5e6ec8b6e1edf49bf79f57860ed1fd1244a097158c9f735fe33511c3708479f89246f7307023a8f221fca5d70ce41fdb934e5ac6240e82940a63429d6c674b4ba48ac57449f9391f80ea6a1e646bb86526dcdf007c7c739e9a9641b610b3d8077e124112ec2abbd76b2df4e4af820bfa70899ce1cd62331afc629727eb4457826173982a2666cb756600265034140fa3240ccbc6b90fe6e92db9f8c3e26644d0269fab16507521a3e9e5491319cbe7df68c1c1c1f65585c4181306a3cbdceb582f7ae6c9643124bff93e225187386a33081456994197358093bf4725d68e8657b4c64d3d1f42b4aaf4dc02ab19a8b79506ba96094ec4e5edf03324f1c43d9a6a69f32c9e94074afcec8964c2c8c3dd89184d15d627075bc29c703f68967536267bb9285275b9d5906798545091b5a1946363685c3e5470fb788ba1c1afc852d1080ed3d0dff93aeb38b4da4e40c990b10643d921fda60e40173a631388c591e9e3eaf7e11d109278b1da36496609c44c75d1fe5b51073b984e17c252cb6aef322a2500e0c588110827772baf26d2a42b0e6ff6a477d3bcbee330ef0e0fe16f8fdaeaf35ec81a8072eb3e12cac3c9805bd651fabed757c74c5feb0bb894998fa00b1562555c957ecbee4dac4bfd8d044f4ac9071c51f8befae34d1ef66eb8e268c206a406e3e73416d98c6df4d885f3b96ec827b96a3198608647f6dac0bc4a50654375b5d9033920109587a565024174110c63dfd70e6b5323ab8560e38cc1d34d588dd74868101067e578cbb61c09687172d314cebb8b8646dedf2c94fcfb51e7cec72cc08f9b98c5cbefdcb2719b27f8a2bf088e0c787f685a418fd7e9c8bd180458be39ee23af45f93e18aa65f674af0ab8509f18aba9a8737df32595501bff3534c3215ded5980347ea005d9970a9bd3f74025fdcd59692ec92a9b722e5bd920bb24eae813cc29ec73d0a85757a35a143b2625163a5321b6772f985e590cc0dfe14d5e779b80f0021497cc0e5e3603487f488b9bc82bca719aba94763b51933ec9a63803b9d78d1f48b43bee1572e0119de927056cf61a9dc4b79569d356152839ccd70b6a07d347dc856820a4f3ac13a16401ca1005b3c10b860e73ee15efdafd062f2bca07286e33ee26d04ba7594b55316d78bbb21dfbfdf2b5ab630c96040fbdfc337a41c94d7e63e7cd23b68bc536abf38cdc404908f58a14197083352160943ccd9dd92e79c0df9ec909fc83fb2e410f61fdfcd5663807328f19680c600a559a9484be8a43f8cfeee5c00cc045d38615c0d33ffdd52c862fa517ad8527af79bc99b64e8e27a9c0b87facf608537ee683286f10ba21a7d3c95762ca0203ef50b0bac7408f15d4988787ed498afef79f8d9231700922c1d541ec4e908b32bddc65a13cb8d9c6871a5a7e601a2037330814bf83bddfb2b02b0433596cb1888307d479ffd3999dba558ecb400acecc4ccd169715b984701811ba53a5ce6b4f102db471ff0633df561f3b2072ca028c2ad0e19d6e35dc25b54b433aad418909a8d3c2e166830454105e50097dadb75735d36228b32aba7ba2b1026cbcf074c5d2d993a533f4a3a6e15704e6cd7cec5c5403e88a64ed0d5a828aa61abca2cc4b1d81ad6613fb199f1ba22c8de744244383865a429ad418701b976646c527b6e17b539765608382597f003abcaca35c00b646220314c48ec6d6e4afa5f9a1e295eb456eba3a6d42483d5528b3df802eb07d540a8a2c085d85e74c61eaecec3831a5d2cbe84800713d8107141bf49548e82ca99683f10ea77889e1dd34eff027097ac7469d825c8e773e0025897ab49c6eba8cb1e3b82c8c2d48e70bc45a91b89eece59289692511fb39af80c4207caa64d39b5cb78b700e0026d79a0ee295123e76b42c15d2009f7d9cf7297caa557bf4cd36d3479ae64cbce879e97d8159ecb5fb3d954e4654595f0a5b889c035a8ec7ec514cdc91121b7322e9776bb6e522a40ae74e8acaa85d36a48363c77bb66360808559a226fb103553b44cc8a188aa45ec51d3e8ea3ca9808f9ded722f336826db0c5eaca9275f73787daac109dcbd80238f19000d26d0fd617381d5f5815af2877fcbdfd3212bb33a08be94f4ac59911143ca621454fbc98da9189caf506971c86fb5c9defc5e1c1f1297a08a85d14526685c19bf879e320a874f36f8430bac7cea69b777a08f97b19ba33697d9583b7900a35f5a9a5a2edc678bc2802d24591383531de2395e73d8e2771a17892f2be908467a171be2905d96e21a0963a53c34f02449b19d6a8957f2b7d842327c0b1700b6c81acee7cd3b8c297904f9913a2f5b3b77fcace3508101dabfccaebef5bd5616b1fbbb84180bd97a3fba3a867921cf67e3dcc3a05ffe1fe244159c0ae43d02478d905389c7f580a1e434ae7bb8bc69997b107bd9ca51a7f6931d25c5ba4eef84563ef3e0a1cdd9b2b3893a69fb31bab83df498fab119db758e77945d40dc9e48c187b994f222aeeafcbd4fde7c2738eebcb0bbe4e24bbe2f18decc53f773d0f0cba015175e5773270e2cf1ec6f001199168fab0224dc9e1d9cdda67ff9828e1ff0be21d7820bf01c4df023709fafb06849d32b1382eac47c6a4f64bcdda56c07ac257d2066d535489a4c7fc2f38c7fb2216b672472474b7ab63b2c16eb58623ebf44e03edf105fdd877a09df7ff0b329521903807fee5bf300d2c09e8b39b3c42104ca5ead0b752cd3c5573871a9abf844f4d704441fe1d9147bf3a29d3e9f4260e3bc43c9e0d48122ca80d13c6c5eec40e4a4b56b11bd8fe1f1ac717911f1941496fe8a631a45a39dab2b83451967a96e59ada704835dd91091a51a446ac6aaaf6c7d2cdac2fa8254f7d1aba331fe5b40f2cd948bc1f0f1bb2345e9f13ec80187bef04b01ce6db68b0fd06f2f9259645d40686fc98237b1f8ead2c0dc2f38a9d22424eb61c5fe43d6790f96b0bc3ecd07f93e11a203d90b3c81e0eba7fc90e2792740cf750402d16a3a2b5c098184113efd0a870653d461e993060181f88f8ef60d93c04f01f66150b90d5df368db187a2c8182cc63d1fda6744e30ae6011a6b569969262367d43d4b8f19ecfabfceba0d93ddce0924f978f8fd45abe26d0b64e1d5af97527ae7f13e30c81eee8e868f134cd76f1f66806cd67cea61bc30cac2d3ba1bc33ba224f5f1a82df1460cf92877795015503c7f9a5f313404cc7e8912b3bfd9b98307eb14566eed05a3a38b9cf4853f8177a736ff72cfd22ece5e54b9174dec4a0b056c6e56213d5ce8f1e7eb8b44228eb1a8626458140711b0f5f9575dae22b21968b4e27693855818735cbe91cc9a4c215f98d22dbc6b0986eab9d61a4569a1df5d1ee8bd303e9df43d297b3937dfd6a55f3f7a545c111f209c1e06db2efab9b9ceb7b764a845bf72ef31e06d5a4a774362bc43eee6071ec9c879483ee867b996943203f555eb284052ba94d979d2d01ca7821ea6f29b6187db752ad66b19815c380dceab1827b74e3bed401384fe6799129537fc5193dfcee405b6453ed64c4b21a84864e8ec1a95596ef21ad25f772c4b5a9efcb9fd64a91f924737e2203e721e966933968b73337a99c91ef43908a123ba2964abffb66b18d9dd489010faac0251abbb5477c3862b07f87c8b9b792dede837388bd20e11cfdf1c9ea74e2c5016411f9e5721a0fa6cb11e208a79500d40b2dd269fa45264671d4030e0c0ff536cdcc7ac7db60145213538ec10aa3740f17deb392acae8e37030add0150c5a08464e69c244144561cf9811996e8cde52c4f38de6432f7a2641996998b8627487065d80d286e8c072808bd7dbbc6eeaee6b64ff3235fa7c2056e0df1fa11f797fff3c66b40c49f7c14d104572731f4c7046840f8a6237cf62af6c2f1b72a27639e0cc24c749c85035876e7db01e6f3dd1b1ba341a77e531ffb75c37c6ad51a71da626704869d5a51da6e5ac24ba3baef4eb0ba7e34ce95b350fd38e76880b37b3e396111d2d5d2f1b97ea91dab01cb4f0a7501b7a9134632ede9ea664e34e9d86352c175066fac335671627cd2b4b9572a6575ea96f501e5c3bb5b4e6a6965e937aa5ecdaab47c7034ab74a6b5e4a5daf9b8da3f45a5748be1483ae8b68dc8a6a168a9beff7ad66dce922ed1a1cc73a8a9e3ebb0e0f85bf4377f9b56bbd9d69f4cd8c23e57a5007268e94d2529fc7e03e7a989372bd2c1501169c7af2a7970d7ea0a4061f6c306417baa25be74c8d30168ed1b19961b9214bc75174e1c52cf428188eecd370eae5c4865f0e5c535a4562c3b439b8cae98d722e7dda741c297d49fcc25e4277d4a518fcc25a2e58dde648bd752e360dce94be8098302d1407d261c2ecbb50763bffe9b5c8f24fe74ae6bab8f477651f37913dd7b2a089e1468fe9d1bf52233873e2e92d9708968f99a9b4e68fa85272daeb95df8d5c3fe72e18bee02a1973fa5df7cd38db15a260b4d7066b1b4577e4e02c175dcc4f471b05e167e348b9da1d2bdc29704a69758d15a6edacf8b95f917b6b8c33e5c29728852806ec9684d93b52e63ebfeb7d046602fa905c092ce635e1a0ef161600bba63db52fdfc415ded605e5845e54fdae635971a3b476458375022014fc3812c595919a5752b2745c29f75e74c2da03ca94860b9d30d87f8168550a201eee03040d03ac7ca2bdadb32efb1df74dd5c1360d8eb99b60f04b7229a52df50806ef058794862e7941415e2eee969a02c310ede0f7b98301934928b832674d490ee5d52a2eb1734cc2a9f8e5a81685e19e2a442f58066699eac3dda7f3938b4035ab2e38d3ae67fa6d3cda4dc767029970867dc6276865c8149947da1369a1fdce17497a951269dccb53ce501c67c765deb67830f06a249235c6dd1f73c612fba7eb35d02c8d9f90ac655e86eac26ca48df56243a53fb580526e273de4892ec431f298413af3f64a8405c0c357842f10bcae621472a932602b2f83941e27683d53eefc1492d98136b11ec16cd5ee46a96bc83bee5128da27744ad3c5c87d78a21150ec48074be1b1fe45c9435405058f1212344a9ce257a3cefab7c10a5a95b35e0ecb13cdc2dabeba7ccef4e57b4a28e35b9b1c59fb884fd4b7344b8c207553d2c823c8b8581212ff239c274dc82cbd5342009ac7729e1411df8ea7381f3d9b56c6d137c94fd229b2b89904ad37dd70d6a2cb4f61476c1e1f20663bf946ddd870e74c6c7185222d9a8878f06ff5ac824f8df1920b86bf1acd1ca1645dabaf1ca564e4ac382bd01ce59a97b376c1ab32ab6005a0b53dfbff26850e1d356065ec0d25e1b9ed39206edd76e06f6f0ef24b3a0271ae478a323573416a08a9ba9786417b182afd5fd32eacc5e5e5f55656814166fab1eb5ea9776bc446aa4c5ad950f78d59f9e72fbc961e8cf9e833c0c1e946712943fb8d6332d4bba9f08ce60ad8674ea3e2f7a8c5b232d4a3f0da5746a93cf82ba5c1867a5ca4f30599adc02c4b0e75c0602aed6cb5963cc57e794eb57d3b7c02afcba56190854eea8912c1ea55f14f70d8f6d956593634c6bc77978183252e4c8995e5bb36f0c37102db6455cd5801c79fee8fe02c0d427bcb42977dfd05727e19f480c30f27fc338c0efa07c7b4687229ed14b5dfe192e3b127bfadf3a7e6b31fcde2e606cc4437b6be1ff9a7491e6121c91f6079d2e5a276e13f507e8db0d292bc16d84758527ea81eec8016f8da288aa2e9d7b81af03a7401195c9d4a84288e23342e468ae27e00f1f233e83c17f94aa3b50ab0b82110742ecb3f94f7d1a6a89dd6aac217ae584831e373ff9b97cf010a4a1c48cdf41c2b2c389b8260c73d3a7635561b0767b1e31f5f0846b463c57a494aee9573816af2de6b00990d10625d5387e3f11a3a064e7e68b871c17871d57a5d037d4b6d18afd1209afd426c83cac9367383d6b149eebed5ac474ba079670ffa8a4e55cad8d4ed1823d0cc4cc6d99cddf5a7659a17fdb2505dc6096fa8c9e2d9fd910e705e2c06bf73e21cc9ff658ed6b00ffdf32b8bf25d7098761017aa8ba828434ca9c60e375bc7526744d84b8f014d1b9cd0de762527828106d9a343d2bafd38724da34421e3e197a9a079375eef1b175be45c7cda14b7cff6fe81a72eb603e3735681cacd76f16ac457e42db550617b6213a5a3494573f850dacf02581f847dbc6a719e8bbfba18716b1b1d772d645c68afd88974a25422277a897b143e420ca6c80b051ab2adc5d47bba5047d56b9942bc050952239a18151f874a024143ad536dd108f350225f6406376e61bfd465c50f0ac2c8d78e8afca6f480993e38521b5cdb7875c0eb72ffaf1dd9028d8382722a9cd1b2689891af624681ab76d66e754334c60971b948af008af7c08f1bd948264cfc2fd9677c444e37e18938bdf0e4a64be92c17a5ee8d4811bfc25d58af867388670d9d6b9e029c0a7a84d0efc7843e4fbf212906477bfecf2338d359cc307bdb87ae0908414b345b8ca543a66f2bc4acad3f93d92728b05d0a04f9da48f231c82d23297fe6b14c1f80e36f8c0bbd224192f9e3e0d1708c6008599c17427219a7a79f3ffe14ab5c008cd60ffa390313e0c8a3bdf5beb32ae8116076dd4f5821b3484010394eb9840438d0e0f43be9d54d6250f234beb94def3c40570ad28f321e0c3d661e0bef41f1fd072a09ddd4e9240e3bb4cc4ef64f32cc291800b849ff47c7e014aaa74a964ed1576045aa6efea77087c1a6556ef1cfd5e209e44878864926a727a9357b564cf263ec8679a9dc9364450d6c975b314474095e320cb65cc6e50b0618086d3c21b670f3747ab72b03c721806ab0058fe4e1c17a1f6da57ef3c9a4736a016e814f70c1a24b9dde97a342b4d11d7b25e238ccff7bed65cc6bab42f575364463e776c6cdf87538818ae9d1868849dbb1d603072e435233ba66bb8b434a92e3f8e1150cdfa3ae7f0bc29e36d15ada20eec357c32c5cd048e25055d748d6fd10157c3658f41e23d4d771a66cc50313eb1ff82abcdc1282c645a7eb2894f4c9797f9868395fe56e485f85fe4d228cf82114677252701f398a720070c48b9edc37ff8fbda1c178bd67f5464bcf9801dc40cc305ad1b6b37d747b2c5c18d1c87ded2f9750043e3e1bfba796595c1e9bd4e342bb3b0a79794d8c07820b629d68c58bba85a7c98f4e3ab08d52ae016f8703fe924a8e668404f0007ed8996bcd601f27380b7a3d4d4988dadaebdc136eee3bc68dc6db5c39f950456fc6f508b62e9a36197447ecfd707a97bed86a4efcacfcbe54c4cba09ed9157c77fbb2e6993c6533f83f3d2104e85c786b6f75b63404625c84996ee301c1194d5145c1a0f1ca8e00a17582c315d89ff6b4b227005f441360059cd8bb62517e7a2851e7c15131448d808e0867ec60613ea738e5927e51e4901edb05f7d57acbe1a2c44bac297b1cee91077ae1db5b84cf1e125392d6ede32ebbf6fb6fd7aca761ee79d97e65762d5a224901b3441759004b873d9547270efff35e218318e7426b4f7802941365e43d5ca68d0b053aab3c2450b96a86cb3cacae0baf9de2dd7b97324e69848cf648b772c2e772be6359816ae55058b5f1818d48967f704505928c1c58c2949b9a3ab020a84b78dcfe05432e1b75ef5fa93242047ddd376fcd8d6247d5845e66a72a3fa284e6d972efa573123263ea70c091c8541603d11e22bb46f6596f898ee3e4fa94e135e3ae226e0324861ede129e6f40e047c852b1ec609021ee6bbfcb9a843c772b6894d24613d550907fb36f93dceec5a58444e49024c428cae37494774cfbec49b937191d025c16e7d1d85c2f88677e6a64c993522b1591df46631eac8278d9aa31ef170db4ef970883eed55f0ae50bea40362e6dfb8179eebbbe2fcc2ca2443e33f32038fde735454defb168342029f03e6d0c285f799a7dc5c610c471e784ed40763858dd6922a879bfdc48761aa7562e5ff07449435e4d9b234cf20bbd57c4742a96cd44de29b124ed1d4ee1f4c38d94177bd95476ea7286057b7fbde36958c0c3f89a3d6cf46202acd21a6bad1c9f123674bbf3ab8e857dc692461f4e5532691244f4dc1d2fe7a3f1b184d634c5bc409377413ed4397d27dd73efb6a42e9a821ab802900d1e7988d3533b2ba563ad3e4a5f9cecb5d8f8f8f273cbc9bc122a8036b3a61f2daf6ff48a8ff3d9cc59ec6ff171c34e81d4cc3c0ae3d4f137c1df007a79f32baa2322857d682d67134e0897fcb5b6dd98076c285c1be5c069ad831a994b9803c614e42ebe06cdbdc0fd55fd47f668e5eea90007b08721af47f9a5099cc261534e0fb9736879c027b96c7f04caf720fb603e6a043cd5270d5cce123fc419db24242545c5c1be416a5c304767405fe36df84440a3a4bfb28548a6b60fd6a46740ed5e497939b421e67fd0c63e204dace01a74368c55cb370d2d426b129b60ff134b77257104acdceb7edf8376d753b9c1b0964f0cd41a7cf28bd297e9d386f779d9548d81856c2201fbd07d1a45c5e2be42c9a640c046c765f754fc31bb2a38ddc9a590105f754dfd98ca1ee76fbc64c7a32398930b54fa5f7dca1a0944366a81932972b2f7fee7b7cba6759a9ba7ea050d32c8102fe83909ae496aa38dd54002b743981c3eabd05261c6673c80e1fe53c14b8cb448f3d8eabc789f2adbc869ed5b84ae870bf16b0ba491b145b5c391a6c98d2463d7278b3d12f93fb9422fd185006e66b213f535083f79c4fe187af011a20b0dbf7cc1be94a0213ab71be70c15e9e86343928002ca332aef4ee6974ccbb8092d75f770f4796d7f5b712c24a6292a24635e6530c2d35e764f0370b2c23d1b88763d9f2a3584cd869240be08999b02cf4f039a34148961e37c1f56e853964fc3cbcd34d94fec6c8d094e15444ef524c45eb449a3ae20c7fc6f71b0192b9f50bce122df6fd03d2a764903a93f203c7a94389829cf09e1d842cdd49f4fd44003e841375dbdd4d9b74f69bfc85207fbbe29a8d1457ae34ab75b33e786b381d9f80c482de0a4bc642dad4a6709f67ac99245d5fc1759d787491d9bcee2c0558e5c7666266764565492e165f07cfd6a80b829ba381ae4263e101ddf798433510123951ed27df3f6581a61303f3d9eaf13c15def610d73408e36d0666d6aee9500dc2d88a949c82ae5874b76d03e923c6263ea8e4773cc62d0299f81e429f04ddbc179f4f6d575ace2bc5d3bc3d99b0cc8e58691dc5af3e0cab0525a4bd420fef85d73387be448ee3fbb8739ba98517fe8caa84a84116744c0987b4ef9c972440f8cb4f05cba995b38402d9afb93325e0ef7f235918be618418daebbfe6372bfddde723440b84b4508e9802636e62d3172076b6b5deab5419ea6b38c0958879ed3f39774f31cd5c92e47b1f8c6dcb93e8ae66a8b40bad12a3a2d5638f99d3b5d18ed240a284d19d020d0950e33795263d43ebaa93d8dc33a1f6acfc9fd2ef6b306a8699f55c1155493eaa17c06d7bfd272f4966eea01b2f2b60efd8780129a40453395ecc8d92eef2d82a96d77b96bcb524f87c888c75b11f319073ded2637dd6581a6e7bc81f441872085ab29f96d61390eb789c00c948f70c97285f615e2ccdb481ba39a13ea74d1bd6bd336bf1aab18557f77209dde1303cb023f128a648259e59d39cc5e897734b8f4571844cd72507b4e3b269a14d122464677bbed591443800b7460ffc9ae9bedc9153fa596f73398bd6178b3529b658b942d3f5c780b115a502fdd330f2c16fa83b74a87f214fb1e1fe13a8cadbd592af88cae66f303a732366945cdceac29698481250be7dec1f8c4dd94cd9e6703f2a04fca6a5c891a6e2329b8eb870d5d52b982db191556921341fd304313816343fe194b38360d7a53d5670a7701fd9d52c54292f2b2361cab5eaa36ebe824aa488550522f1890b01367c4ad00d8cbea49e0a29362ebb030f5569fd1437c263a4d89feadc6323fcd452c298b14cf74fcc681e1e0ee852c63e286805ce27803d30629143dcc5c572101f7417a7f4a4e23dc0804230bce293fe01f1a4af65611f468c1206432196382e4cb65df6c14f6c7e9ccec81a8138ae52f7103e77fff7844c84cf7fce6947effd913e06ac89defe00cb08e2b015cfe0a9b9594dd9e15afce6aa27e370099a26f8421979df63c64cc2c07dcfd41a3554a66d8be7a909767403d4abe3f35a164d3b1770c80139653c16859bd38f673c5a66a97590c6e1ed83625eca66d46f16d10eb2521b9e8cfa138043237d632b32f13f93d2258da4cd928bb8b495d84097e646948ea95086659f4547273ac6276e5ee3760f53ff09a2903618de900aafd6138e4d12d2a09a72856e145459314a7bdcf9ff1217dc2c1d31f7b4cbf5d3e5253b9fbab9c791c9235567ac58255556fe10a95959ffa6c8b80c1d1b70c7040c5d00471fd1a002a50849b2864d8b2442dc75a899054ba28aeb3fec0d95f6be6292add779c422407d660b67bbf2d55b9c3524e1d63e53df41d03efb12507d0846ab99598e52fdc7697dd435bc3049bb2b91665749358ea1ec1a8ab6e69259cde24ef0e52f27e312b245a528edd8085366ce6a5cf9d7012c1a996b42e35f7701048354da10d194533cbd4e897234041aedf6865583aa7bac5eac4c687cc3c223528a942a40d78a93f98747fd784036b86c8a28e4a3f6e866201d8ca5e2cd59fd2e943527d8557f33ec1c201d3582a2cd51135ee0bb3e6819364942ccaa4e2403950d990382eb46d43003ed524a6881e2939e703a810021fc32631de6fd21ea0025514b1a8a25f30ba0684290104c0c881a28a193de873cb7a3a416d03de14ea9dcf951040fb5f8cd55ca18bb197a70996236cc2fe2700193e24ad0a232c700dfcc46fa4498263d023a6b45c1c10bba1d0b0cb54b82a53a14c98d79c9f8a4ab7e738e9908208e1c6b130bed6044631fa508f31d6f74d2d42b21183b568a91464511942531d200459323074b200e327f1a5b0e61fbebf35dff8ece0a88fb5d1d299b49f30ad8755fee9834999d92dbc36fbe62b9529460f440a4fab4364d77992e8b4e97fb0c486e78c53e59aa3d226aa53d1263e33e0918e3cc3ff730292740a6c7078ac81bd8a8574d4c169d029b16a418d6ca8a3bb04d6d8e27beb4d3aef092a53541511e74e30f9ff37208f982dd1026849095afb6ff09680768750e8357bc5dfd7b237c5328ceb6b10ccd3371751bab30ffbf1d33893f992e138fc5c0986e29001a4ffe0dfb1f2c7d784b37a6a081ec03a9b26eb7f6bb131d90b3ab65b82d913fe1c3fa34a3d99cd5a59320d922ab8fe32e41893d2896feac8114076b83384cb83c90a2e5fe67ccfd80177a0b6e8befc1026e90c028813e8378f863b29953a3f9c88d11611d2b93d350b029180127673136c89c17a1c5d0606d43309d46d4f0107a247346bd90d20fa8431ea8935309d0f63f5be4a4ec50534468575474acbc79cb4c80e6211fbbd3587f5ac6e66b402012432bc7c7cff7cd18b7c7678cafdf64fd36a9fc0735e4c599f9cd49e18a05432c5c9c4e302465f11db7946d259d9d4015f7b675bb566ff37e2c916e9d35f50a12c90d88cd3e9596c235653ff509e53dffe2936a30b3a15c53901ad4d18d8c607a2792c54c19ed3439aa44f3292e5ffa4473061d3d578e6079ab4b08f1ea82ec5ff17f378325b01839cfd644611afe8fb7eecc3e78edf985101a17317a198b45d21481ee029365c1daf416f776b71630207053d582abab22a154b83454beb1ed8219598364c787db6923ab99af0637b8d60b7501b67eedc16c0301392980e67711e6df3c160ebdffcb69e53753ae12bc97f74451b5836a1ee12b5a326b9bd3ffcbc22025988769510bd26355c3266eb8e57a3aeb7de252427679031f26416432006b249a96940fa2380cc598321dd32da9cae6dff543d6ce41f96ced671c68dfb3e2101631c9825857687b1e5a71c8c6fccd3b6852ad3023be705049085366d002fc1d8b0cf379dd27486e36f88b42d19d4f6029e4c9294885a04fe9b7106853504999c85444be18f6b2eaf03fd06166f9feb67a1db8c8fc594bd822fff579871f58af5cc0c168d48ac51ee6fa9ad7f44081d7638642ed1d2ce6cfe8dbecc3c739ba001d0d2bf6d330ce6a6938e14a7b644e8335a3cbdf80240026465754aa30c2d3bc63c9a85a8dfe6a0848542412c1ef2193e3baf1ec925d1855cc63e11a4a8606e184f70a4021cf01ba920be54df98652e9c15a7b40816d37f98739e91b4021f8a64ce8ff72d319764a04bf2814466a9644f563fac00942caadf46e251e6352c7bf2dfb09bac59306d0409f52428555b203d5361b9891130095c02c0ec527ea2330f8b0f51524843c463a0277b80ba882611d03500f4d485c3b482d04b06ddb0c878ebc39777e39eb9309f5fe666058d5143c554e58af5ec717e96cea4752f33055bbd981afecf09757903aff86b60e777ebcb473e3ef404452dab8e4778569df1f988f7e6fe9fd554d27a0b146cc0aead03f8461a38ffc634542ba2e7815f1a377f88b836682090a8aeb60d4acf2211bfc3e74ad67aa8ccdbe2b7a7c28f8513ac263d53b9b231385392040f257884135d61361d9d8bd6b6c19a5208c7b234d031565019178c268db3c02a27f1b14d660049d21a2418f4cd3715b014564ae63da4ccc96a5561ba35b217068c1ca03641d555640fb339f2ddb39a2b711a1130b4a87eb19bd3638a10b7edb9c8a0d76bc822a63bb086ee98df7de442b283cecde175801479dcff714689932b9bf2f553e664476a406c020eff85f72c6675f2931748a56a8634babd1778c8039f290ebdaa735bc340dcd22590787926d96f8a9df3d0ce7dd03b8833deb85ba13f52ca0d6cdc806b881de51207fb82ff5200df20c1219d02b89f22ec76bdf6594a2221f7118a464ccbf8971bceb739e54accb31bd0f73b529fbbee248eab0788eaa60af5c3b31f40ff42a16e275a44280bcbc605c279fb122236d6f6e8e94e6d2918f22c7b898d6f609a5f4ff3f9131b7273ab5fa8e1032836fadb61c325f36d7b1ead5088bc398d6ac4cde1eff0fffb49b85f8a13830489c7781ed4790741f7fef6c8264f2172161b456a9657c5a732a0a34bf2e8669b34286db6d8f042ae7086dada33e467badc8dd45d009784f888f1fe2d7b8b3839909d4e1921617bd72b74d22a52d00fd21ebcf1ca25c59f13edb2dcbd4ba90d8f9b5c203172167e0b3f94c4cc7c132c3080d2ec4909219e3711d97988d531f1007b73cd92eb8f1f7d1648417624404a88f4fe1564daebac7f1eac852ef89dd2f6456ab1cc81e2f0fee7015b3081df09a9de2432cb18280ead7f1e460b13fcbd92209407b69183f7fe41ead005e4c371db84bfcb0fb276248e7f3eb6df90ad32c536ddd0ab25e3516f153c882a405fab79f8e0b5576a96cc910f7c619654f249f60d44f97e52b2ddb1874c1d6a02916303c4e9aff8a14cfb62a71ce6f4a56ce18a127241c8eb377133b2b35a6e459a0ebbdc2e5f177fde76b586c3624cff57b54eb4625f686cce16249d58133d5b55743ce63b4c775975eaeef95c635124141b64255ff8c60fe23245a51364d918239438cbd7dc346b9dcb0bfc51388c221430a42dbe4c84f3d277f7b2bf83f0a20634bad0c1b8f5a45eb1db4009a5ee4c1f712cb84e39c052d5bd4541a412edb1816d11b314dcf4e332f7c72ac0c4d007b5cd243f06ab66c677098d0c2af87267e372718b8b3e1775a54e53d6fe626eecd6cf0fb77282096090cfac5e77df6f4b885c7d99ad5b81f838f116e0bde05c18b1a9d6933f74402e4cbcff9125630dafc958af3c9133ddb4524cac935fd3a42cd2574a37226d917cbdd7e079ffb43f08935bb869b4a180fe30587d1cc395fc1d56006ec454b3d27990ec28812a2da5236fe9c142ddbba7fa9c7c48bb468dd9b36a6610bb5c9133f90dc46cc40915678f12915b93fed8dcaedcd4dc9a916fd3982c569a4db08d433c32739ee5e58612d828c51de3bf83c995a3a3b3dca4eeed18937b17d01cec5e08aa45134cad1bc545f7ec0edef70d862041e8fbdfdd4f93e7961851a7302257b2000a55204d7c8a936d5d2fdc4c85eb2cd8146fe7d95d304e2db41cf6e96e6a6aded7a174c00ea5cc1f9466392745d13cc36104bcc042752c2f958df8073849ff16eb1315716288f99fd375c8dc6d8b82b56c1a5980a7fb54e99f24ad19e569714c0d89b9f994d2adbe0ef8fdf5acd1130e606f8bb618b9cce12fdc86cadc1d1aab4479278e5f07d8592ab8054e37329c3623516d228492ba9f914cdef6e484792ef31a2b7747c883329086a4bb36bf522d2d9346caffd6bf9919cc91e89564654f0da7846c57337a258c7479efe4894765ca8f1480a29586dc5c95b18688a9ce38fb9be0cca2d714f27255710fa184e6c28cda21b1b4a98354d8dc42404b6dcf50248cca03994cf0451ec4f2aec82c68193b971826805f4521e1481d84b0b90ae0619bf2258102b7888c9a32e4981ddc9e712457db9d942895c7d2f296f0ab602be9edf8f2e8abda2275e6f0d750f923981cee8a6a4fccc3f03fcc3d5e10c5e431f0f7ffb9208610a639a98f47ff3feab3c90d1e65c425f5898ca6d0bdf7c322f33610724ac803f9f85c372e4479e2302e0baf75739e295e02cdcd5ce905d08f69f0ebd10ad609bd2d182b98c6e0eb6f2ac7ce03a1584ebbc43578fc83c08a889f5355ce730d0e2e41aa65412050ac2b4ca450d3a979afb2705faf90971e3d0c7e577f41974c0baacf635ef5fee5606083827bf0ef7539bfd5b883f7def07c645728fdeabd9498c5c41c4563ab839d21e3afee2ec388a7c45177e40225c8c24e8e7605c5d34d8e59da157aa72bc597ecc98158057dbb52aae38dcdf04899d72393e4e4938edd8f8d134687a36565005d5993229b90078f2ca6f6ff74c5fda0f0e2fc4059d12fcfa2624914de0beefe320d895ecf8fb8f439cabbd73b6dbb9250aa0d55411336eb09a46d50179a60bb9640d22655816936eb04a46da9149a62b34a50d2867a81291beb9486f252ff6d78a2c01d49dd9d24599ede74f9b3a4912ed52324dddd0f29fc0302ec18461ec373445c390b7f282b9404c5cde320e6eeef3b6632daef2a4106064e62c926ac0f2596169ecd132d6ca0027f34a684bb1604670357b56e4fed21ce6a20e8a75a7fccf4ab1035abed887269a6e4ffe0fda6279440643cb9663eac53fe032d19fdfd2f8e4495597e09512e0172a7a53ae537bf5be4f8bd4720231d366ef2561879f61e4a57944550efa25b50fa8bd0cfa857e77ca134f5959b16100ef5d8472cef2f3229422a89bcc483d2108fa028f569f52256c026e7e9f002d64095022b19cd5c957175c9fca41b36b44b050c8a3904b1c912afb74f102923de654f14c314486c78126d23f6ea140ec1532397e0a192814cb7992e53f47155fb409d24aab50d6fe5a48c7d768a1b539801c75fb18f305399eed6e1bb816afb55ef23dce16ab1dbd30e0cbdff95737042ffdf04ee33c32d262600a9b57070207d5bad2f7d3fe96b313e4077f17bfc2053336945d5e43f796d7e51fd1bfad0c8cfe41711318ce01a49824d62517e64a24dd9d1c8b2e7587463430b4e6785ea7c2464883de7063fb571b584a20fc47e234bcf2bda07f9d84d28af1dab43ad37585ec08b9ea87229d7ef6de3ac7efb18c74405a1b8615214fb6860c4c1662b6f440a2a52644386e8a6ed167bd71d5de3fab3bdba8d3744a871901294da7a7e6deec1273e715f4e1fbf06ac1725233943d83783aa62f2e9107bdde40eab60dc35ea57ecaeeb3b8b5c6278e7635969190f8f22aa4940b6643290b7c8603ac1e801ba1369c3a4abe14b7468769d242db21effc2b2147828a612bd1552e71b1cff82745ec5794ff1aab721ab28b4577c6faf1379259b9a9af5f6e785e71530291cee01d7ca2db743dddd8bdfb6f8b8b02570fba2ab707515499665d9b45eea137a45c5d204155fb09036588b54185784d1f2224e28b5b4e5fbe3a2583ead82d16fc79dc5136e517bcc22f8e28c203145b47c9349653c70469c981f9f5e7674c22d3f6dd3e5dffb15d0b168346d20fdcc9f50617f3b204170ee8dabb13ebe795390646590a15813cf12fb1d7480285cccf1e44cb992b6d40b4db1592520694badc094ad7502c936d40b4dd95a25946a435dc894cd6a02e9b6d4854cd8ae25909656912e13a4107b201677b91bfbe6d29369ad3f875ba077bfdffddc5b465f26a61979277c01139eeecae103a79dbaf8f3b61afef4946cfa03962e344114d6cb8529a6fe22a4a1f80f58e9ff8a2ab299cce714c8f7e49fd552db664f891372d0170ae19368480c923b874c85ea40b6709bc3650726d19b275dad1f279f9a45591bad46c0c515b43c4fb35eb53a291dcf1bae51d3b6a1f4fc61dda4a457e425199392b0ff839a1e9518b6a1c1b329e888aa9027992ba203346b4d8adc1e93ab181d8fcaca24441c965d4da6129c3e096034bb0b45c6652df72ca391fd8bcc37d20e049e74d91e271865e71bf48a52365b7ee21f07f5e016829b0cb9897e8b04b398231441d5a8b2741db0c86a2c1061b1e6603d2cd26eb2bd6b7295c26858ae0ffdfd02ed2a59cbdea490aa2847022534f9e8d361dd4d898734b55f1a6ebe366531964e5dd0856ebd03be0bec65d279715577d8baa694dd35a75ed7781d911432d5ac22249375e9541a553610b4fbec72306cfa6b8cf8aedd463eac513abb7546f6900ef918a9bc57986882849c208d4d00237d4776fc2fe71d7506ac3eb43d8f5beccd01349f076da25ed2b1e10c67503a81afd1416cd19b57ac4215150f4348a71ad40e63c34f9a1787744a388a9eca6dde327bd38d400d8169aa2a0ce97807f818b65048b2051db2421ba41d9221150871edb49adcd1340990a44b3a4117cbcc66793a08c44a87a4f763f6dda4235d17578a087d13996871ec80d1dbb2432d297152908ea8f046366cedc9d329239693068e839c0eb670e96c1f3e198aa83a24b456919ddd87844e0d992578cd433af2342cdba8a462a1026c94a8c648c3758494d279f83e7024a6b6ad81eb4e0f1971d7444098806ae93c3d233d5b3698e26b3cc8dd7f9b36ccffb240afc03ea5b34b79001dc26e52991c2817d57e9b44f34d9cd366c3bd582c984b5720ff488b5de0daa4152e4ae316cd54903168aa44e750b7271f35ae80f87e96468ceea93f4d43943e523f23c49b9176c9d4e819d7ef5a13eaaebc42e363f6fadeb93ae030fe77e1d6f4358d04905ae293cd57d6df7c0a9da0d08bf634cabeff0979b0252ef532a7ab3d3886efc312f0d0a90cd2be06cd083aed5dc46508811885d9fbd98e00cdd800c4aec7c4db8a226e4c81cc70dbff8db9fa7fc8c61fe3d2d298fd6a00deb0774d21f21077f862f65da8b574faa835a8140f1515dab481647ae09ee1b204cc14e896e3f4ad1970f4458baad4dc8b200b74ddbb9a88130678f0c9d7b9242eed0fc1116a234b9281c3934846855b6d80001851f9ffcb1e3ef663aa77aa09751350cf3a0c4589fb455779d726fd40fad630ef143e742dcbf3fb39503d94a2212039334ea8968a409b4c6981926dcad33505ed4bb85b2cfb31de8f31df7e18efa6c084c2a0d56577be420d17f984bbf3ed3e1786def8fbad20b1cc840b401b0aedabe8590965dec3da307a3011ecf52218707f1b073035d4f2a191db63ae31bf8f691214e047d414410b41210d39489023e6a6169b402bd197b6d8eb2451badfcb0da9126b1bc46bd9ed792925564928b289c4432b5baad8e7ab88cbaf356e205476f45365a4edfe4a7b9e1e77d129e9d9eb7e8dc55d7d0fd3bcc850b1b2cfa82a666091f0b9111077efdb46323009018aad4f8afe9e086133321b5646d4e93a4dd04258491f584570a133f6c9e29b6f622bed41819c04b5e889ff0e26a19f067720500541e5f743d0b2a0f9b0d603912cc8fa54eb0e7967e08071de92b11bc3a7c400722c48ddeaf6c81b6202d33d87904fbad6878e888bfcf3a3a2bacc61f558d09f048e29e700ccba1641ade38fe78ca4359aa7bfa3c1d266b965440859883880660ffad0243cdb6545d344526fa6097a3ad20ed7fc1775e6698af76fd1453738c8596a7657938c65a6cb74192c248e0aa2d3b1009dae2dca5aa8b4216156b53591769c2c22691f52f624950377ef96c6df43fcf6c90450c03b6aad7a52887ce87fba5db75f35056ec0fc24c05dc30fe90ca1b6bcf5b6e77db27f12a06c8e4e0ff52ba0dc6dcfdd0cb801abf8ed99be7fcbc3623a21629fa9b9aee28b30ea8275456cbec2318938f9a6d4ad1bff03b06504ca78bb3999401121120423b0d501dee16f3a13d52d804280ed966e9c37e738c290913d1d1f9dc41dab882505c065bbe57c858ec337121889ae559f5beba40847b8e6bd9be7d0175cdcb3022a01921294be079e8964f968fbed0a1c422b68992eec3dd9e6c885e0962c5dcef2e26117e4f87a4039c93dd88ada817adabd373b2e12b12c81046f95ace565ec680787d12ac94ca2c40abfe5103f078fd4cc159518a3f21bc90b6c88033abf768c592012b3491b5eb22dbd7135b7399dc8c4a54485ece46d86a00021e6fd7ee399dff7238727d9f7ce7823fc383b44bc00a89000001057e41f377500ce2a005aa39a52a905c30c9644c36c37eb6f1b694d2421929288d0caeeee1d6c0820080a09369e43357b402e4454aaad07aeb22ccb5a225fad2fe546dcf23e95e57c5a2c7bf55a37e65cbb116eeb6dc4553f50542f6910579c504e83f575419a47ae6d286e733799b4be77b4511c05766c1dfb72d268d39a482ae56e326d1487dadc888de770233b5c87760db5ed08d8780ea7804d0e0aecd8475e731d4788bcc6699549a55261d4e9743a5da122f239a34ca7195337d38d329c3d7854d188111bdf91c3d3e136de0e152a02395c87532087eba0808de78c8a800ecfe114d0916367363b021151d548cd85247a7a554b7221e54d766b3bc6d2b157f28861250fd2b49207b1635cc81e98532f8fb995659c469aa4699a0bd95cf39f1f91489af719f951c9616691bc9fb74aa3ccb7cdf12491366a4876920bc94efaf915dec86bf9dcbe8008bf39cec4d8beaa1cc55b4315fabab01cdf0eb771947ff4519a74cc33995c733c6136049f503a24971f7c143879ccf3fe50e0e433a6b5a63d930e2072b0186325c75c6f1d924bfc5fb40cf325a7a1a1e93add759dc94483d5cc013bbce4da935eb314a45b597b26af1904e2ef70cdfb486edaff459336b6c3e5ded9e1a5bda3c3a5cb8fb4796498dfb1a14ce570bc75e8c871b241a1a43c9d4e24c7a453c949dba9b4915c926a6a6a6a4824128944aa425b8e1af29dcb2c3bdc7919cd68e433331ee6e5feb27b6993dc3d287b9036e9334ef2cd933e729c39f2f20c4dd7759ec54ca7f36ed2d0d0984c341a789307711ccdc837d3ee3ccb971d8ecca1f3be9fefe468b45143667ce442667cb47fbee46ddbc8fb8c7c8966a3d917883e38e7b8192f0f6509fa99874353a4f1238f0bc7718ee78ce3998d1ab2f98c0bd97ce6e749de47a5f7adf01979525792dfed0b441f2327799f083fd29ccb19054e64981c12358355ad156218439929e3305d61857b87343d4275b3bff511797cafd62693bb75650df89159b6513999772e24dbf4846d6642b29ccc473eb3cd6156b2bc8fc85f994f92c3b9edef6788bfdcf2cc017fed5207857048fbbbd4479e0933d906a7dedfddb6b63f6d1ddbd6efb63dacfd11791e55f5a8c7a322499b45961a1d9ee7709b1dee9d96935de3743ad73d5175afd626d3e75f4a736ee7704ed3a5005f49c94d2eb7518c1fb3cd31e566dce47df9cbaf3f5de2469e0832a57dc633d17836a60c7b689ce69b0832a539a73dc7da731ca933bfbc4d6bdfe13c4448390f117e0ed9eabd5a9b4cee5f8a73ac336d47db3df193fcecf4c4d73b477b76f89aa779e769c7334b7d42c931cdd3b5d725f979ed9d6f7a8f60c78c93848c1cfbcc7246258799c9e1c867f6a499196dfb23f29a9bbc3bda81264d3db34578bd3f22df39cee4acf5bcab998ee77aef788ef7ce0e1ed56bd754efe950bd8d7699e5ecd89ec3c6e601283fa14ea8d3e974429d4ea79ad3aea9a9c9a222228208a9140f9a92cf94f6de5b25649baad79a0047e8cc3f954a454425b44396d208aa4ee48de575cf4ce7ab4faefd9980e86b75225335a8baad23e0f21f749efa04872930df360dc4f40b69d6d8f5b655bd711bdeb66d43610c6b1310bde6b453a6b9c67bc784b9ae4f78ea13adf596f568a4b5d67a8aebbaaeebbaae6bcbd9677da2d1089f58397c7db2016102a2faa43e198a72dd3abe2e6b09c15ef7111c695df86ecbabcfc461d28ac5864b1ad5609d65d6e1ee635e762e07ca94d69cc669ae3d6ee330a7dbe6262072c03c63e7bc6deff8cc4f121feff4e464474173ceb38eb6b96ce793737bc794b16dcb7934721cd98d72371a8db0ad1be9d146229148144baeb9663938e79c7d7354e69983726299e940a9478e67960333219b73dfcdd93c472d44fb96a3371af00050e08c54431a719605051551aa37f120169509583297cc2c4d904e9929e3b0874fce9d320ee35376726def9c4e2513cd4ca9442275a48e4422913a1289446a62435dd7bdf7661d5c54853b1d8558a10af4d20c50324c74344e33e3a51ace2f2f9b462a5cdccdd7c8a1733ad3dc91acb56b9ae7fde5253fd2f4b567bd77b291e7435ff34cff3239f49a124d37532a651289446222e713e727cdb57bf52995bd70975ca76e7974c95c3223174b8c48333ef3d578c9bb0dc3e69c73ce39e7dcf6d7735f89cd636d37e0e7f1e61ad73857c335e0e7352cc9cfe33d821dafed6dc9638ee11aaea6c6b9bd43836bf2fedcd4d1cc904a5d47229148a4d16834b2ac254e270fca1ed775afbe326579fa69640e02b6ae58ad5f7eadaa020b62befac58298180414df5082cb4a9d935c739c3decda4ccebbc8696be8387bda945d67d7b09c3d945601e52787bf15419d5810c38298dffcabd126ac7dd37bc7e478efccd4789bb6671c3acd4ca9239146a3d148d39836c234cd711cb76ddbb66d4a5cd7755dd7755d396b41afc5a0bfae4b859387f2ec7d31a8c8c9511bce1e396fa718249fb0202606bd92189f0cf90d50be7cf3ebd227adafcb354dd3b4cbb56bbfae8b46d3d775e98efb32c0d1f8d4bfaeebd2a0936a4ea85c536332994c264dc318634dd35c6bad694d6b5ad39ad6b4a635ad694d6b5ad39ad6b4a635ad694d6b5ad39ad6b4a635ad69ad354dd3b4ebcca7da4aa75f2d79bedeabf5edeebd5ac3af6ec48990b832cefca4e1cd1bf9e5694df34c6f1bc76dae392f795fc78d38bc39b7f1fca675e7ddc7f3da3bb71c1e69a7dcdec13eda3b996b8e233b8fa7fb369c39f41349cbb4fd9948a49c73ced7454343438384ee9ce3f4fe74c9d3af75b7bbddedba8ea7cb7ea0f1bd2ecdc3c3d3f1f0744fb895ab739ca1aecbdb38ce4f5bc937aed397369af1be517612c7f1709c6f9b6b3ccf397c9e91c6791fc76d9eee356def6427ed9d93733cdd73be759fbb3f655c756e7fa62cebbaaec3dd755d34343434563a9ed74afbd325aeebba4ea63a9eee95c46c094a9744612b9df8de2347aa8c444ee5e68e8a473af345ddce6bea587e2df4912b48d94311a492e66d8e8d304ef38b47dec7611986650f18611997ed1c8e738c2dce5ed728e348a512c9671c660f20f98c930240d7177baf0af6987eaf8c3b526e94bf955e556ff78a04d59d3e5d84f95957ad646a5aeb24ace48dddf3b3fcaa5424a85696a7923f62102837bdb3d680218f1f30c31be994fa74826774a2fd1467d059047d743dc5fc0785c27c741c08a4c7fab9a91767d070688b977f38b4a58b4f41de548f3488fea5620f3a5ed52a05c5577780bc217df5610b105feb1627be561f6af5e13f13bee6e751c9c0b1072940201711f9435341ad8a3f6827685135dda4f3fef4364dcc2df58bed14201079edbcb9080064a0231014ed729172c75709618c32ba8c50a6c9346574195dc6785f002499c81f4c346fe241b41d25064527f1090c7342b4d06210c33019cb98fec1305874ecd9b37c2d8bda6b518a61f1f46368afd909d9013f39ee1db9c8d49cdfbc22b7f8ddaeeaf44a29a594515e6bd3b83f7ae50a1592a9e99a47a5cc1e5664ea2607fa8b5cb8442f514a6e0186de83feaefe542b5510e92938000289580c51c6101653e4b5c9e1f2e6f27bd9a9d90a1f3b741282e01cf3f5e2579faf594ebdec3357e99a471d664394d0b7f6bd7f616e3d73cb4756f791da86d90999e3ea61bcc2ce8e71adfe935dcb1501d187e596952fb73c1264caf2ea7d33bbe5e11eeb73c61b6ef09a73851de7e59d60fd7a28bb11107d58b7d6bb3f24c8d44f0fcdf152b1b2fc8d41329592365ee8cf86e888dcdf7da29f9e5ba239452cc1097903ad2c0f5d36f1dff55fbc3e07ee18544a43df09bb2c5306d18b53998219181dfd337da954943fb434c91e533c4a8960c412e2432f2f7f988e235dfe8041e6dc500a492987d2801642cb72086b85a65b6bad96ad134238e70a20adb0c231b56a3a06da0a1dd25aab8f84f3068c558710fae538c24dcb6ee7b5be6bbbbce87dd3a35fd7370d470f6762d2332f7b77ef40873c1e781ed3cb4bce2c52ca29a594d567ce225dbc28798779a9e4dde61973e959fff105c3304fc9acd999717b1d6f9bc95a3779181631e831c6c8420ee69a29678f39b3f098dd9b9bb08d691ac963e6344dd360842360cdc3de671dcb91d43e73b43fd73cfa87e408d06396c6eb8916839e790647d0dc5ac7ecded13c2fd93230af2cb261f65a6b478e799f9ba696980a359e731c719c674fdbd42da7711ab795d0b78e7123c7dc3a67b19192f976e7e8a087339d47e734e80dc1bce4321b52f2ce94f767f9e733b387cac14cdb292b52e33bcf9cf32d6282d9837f4d6ee1ecd1d54c851a87b387bdde79f06b3ccc5361ce1e5d60a39a9cf591c7b46dfb92c4a2a2a2386abcc683b347867a3435cdb33967d9b669286d17a9e9dc7ad671e791fce499308fe49d87918a9c6adca43dd4e61a3bc91bf9c933913c3c2a7272937fdb2e5273f24c359ec9adb7b9f6ac9fbc1a8759919acde433b9088376b2262862c881275f9d027df559abdd3e1e9d96dd994e753aaddd9f8c143a8d39d43a9c5f0e94294aa5c3b97de24b2536ce2f3075fce30b48e3252ba47c761aa1b7a80e8d97bc7a3ad9b563cc7f74d1f35effd125522513956377a25b1eef577225f2774c8eb9751de30ba146370d6628ec6c0e1f633943c7300f6edfcd2ce4688f58f62d3bc45878bd64273bfc243f1ff10afff3d9617698f5e488617642e7307b74884261dbb91cfe8c7f3e12471c231ca106931ce4f6873976ede68baff787215902f43e12eb899f25f979bd35cf7c0695b37776a2e715b66d85ccf1e64d2ab79cedd22ecd370f5fbeed2f023d759c516c7ff4b1ece5792592088160d4b19e159e44829ebdb8a9631efccceb994fca9b3acc48d931d7a8cc511cc92f0f95f90fd06b24395b4ef6a844763be732796cef40d7dcb4a193bc08947f8b40afd138741a97d9101a2fe958d7188db739cc7268f647e359fef9c8cd43e5642f79f6921550de8a54341caa82cb47b78eb19563a653fab8772cfee2ebccc39b5b4fc3b46318e6d6fb68cee63e50a67e3e6a1dc22c1f3d734c73c9cb25aa631dca1456f2e067effbc1ac37331dcb33bf1e0675e20937fbc14547eb9777824df2f3996b87f286c32c8c7a7ff2f55df2d846b73cca1eceb4f0310fe2486d23e6360763282e3b1e79d6356fcb3ceb232f734dfba77976cc43e99d33e328cdb5738ed2768e8698671e4af30c45f2915bc7345ec967bcce73c6309fd9dfcf43cfdea7ed9c92a3f2975df3cc43e59da367509b8f3c3be63cec33dd6bdef98c87bdb4bf9fce91c4c73b8773d4e6dbced19ee5919739e76dae79f137b75ee623cf3ae7e96dc3cfd990037c749fc8137f7efa8f2d7474587f78d1d1a175b7bce8383302cdc49a26f4e1bd3959a622d5991b6eda04a5d7ab9c965a4b2d4a3f6ae54c30bed0d4faa4544a14a8c7a716d5b1fed19c9973c98031a45e75e8d1ad639c2eb74948741955985bc6276cadf387a582b56db5f69228c5ac02e8a3eb6cd69fd21873b6282d9f994ef4cc424b4e896ba65bd2aa63bd9ce8b26628c8fbf337be10b474e83fbcd0d3a5943a3ad1a7941185e9d57f7efaa10a2844effb41217a74ead7bb7c7ad42b72fddad0cb50989f2e72f7953a08b8668070ceeb734a33a8161ff006a3d3482392f8724eb7ee93459fbd83ca895ebdc7da4a3af0f37627a9ee53bfee24d595705f7792e83ed947ba7b2c49862ac5c3a67bebe66c5996913cb19b1949a5a61a2a2df089e49b97af6bce39e79c73ce4cf29c4afdc6eddccd38374332a28d7cd98891cf33a49919e7f64e4dea2fcfa99fa9c134259a52a9944b99a6542ae59c9576966539e79cb316f7de7befbd77db369b6ddb463425d2e449a5b89c53329582598ca4c8a85297fd5256a7dca4f565d54d4544259f6cb9481ccd88dbb43699dc5329aee3eed5da3d95da54c8e1364572d810195d9abb5cbbf64e37ea4ca8191a53e93433532a954aa4524d4d4dcd161b1b958de7b0f172a80218cc978b1581c981094c1324ac0c3682c4454aa9e3c0cac0339890428403185c6e28e38449171714717145882e5b4c2b3a0e035280433d8082840c9c10e205276fecd004881fc63c6186126082c0c8bc94524ba188848a888a13551122c2c1961cf42582077d1f0e11d980de1668d19de9870730425cd83083184dae884b6881c50c845cc0c486214c0c2e4a18a34d5674a7a3f0f2465e59d1354c04610855b080091dca58454429a3063c54710319a02f30685f5a90cd2752d0262702d26a04a11e0ea1f1050d2b9eccbd92a91b747e3884c613bfc2010d237efec868101154f370088d175420eaa5c9a6ac3bdd99542b12da8821aa562a125469c0602b9d52eb42cb60685534d994756759161731641e9dce265fe5cee60c28fee32134e1c05970e4ad17004c3c6c2c1ed636b90502136972cbb24804f138c27487ce2822c874994c9e3a03ca7faa38ea25e594d55eeba5a0458a2955a200820cdac08204414d86c2047104110e5670db186170e0a460fdd09665390c7acb0a23cc5b965d59b9b6d229b3165481c9703d1c3a03888b981590418417559a0019a9b2c30d40b812041161c2b06207f6c6a7bb3266e03dbe46dae33f2a29cd0180379ee369e9bdc21f97d3bdb15d73ed08bd071014944f8da872c30f9ef6781c8923f524a3d22d2a998ab5d69a85fef2ca88af6e61fe59966555ea5654ab56b776cd4ff7dc57ac585c4e25ae3fe74faf2fbdd63cedb5f3cd55864bd249aba59fa67e23d4a69482716c149665ad6280460d2b1dd05001bd6794f92f95c5e72f578f89792983276880410a315e0044100f00f5a0458c2e5a48828820d24d2a29630bf06782fe68a33dbaa94b156070f91516fd412107e030c0b22ccbb22c4b5f6c05853603086650f91d6444424b87d2af7493ec1231371e3a9115d4c10c2b550445e9a2052782a06045105a507141961b74594111c3181f3420c443c6c789225e1c6be3e7101c40fc845eee8dd0fbae7b3738c2f42b7a487c668ff5d6a79164e734c723bfde03e10897922b2110257a42204a4c7ff905ff6e8ef97529b97fe3b6619812548e7fdcb9aece03d6f3f196c8d4edb99b078e30dd676fd6a3e61ff4826473ecd9f3999f1d5b6c643dd4c85e16cbde4ef6ad73bb772ebf18265dbbc71a793b56c73f057923e52712253dd6633d970f2ac79fe4f0497ba747c9fd6b1ff9cb731dc7fdadf083c4e7fe759c89238e2439d6b3c447498ff520e0f100b635205313f3e8f7835e50d6fd472452c42ea6468eff682b991e9218f3f5fbf913029fd3fbbb7973ff6d2b997fb3e821b18e1df75c6e824cf1902938bff8c59bb37bb1df8d33ef44e55c8f7eb7089d1950fe835e86ccc881e8f21e12eeb56dae49a6b0e399a3f74c57c111a6f724f9f9b84dbbb3b8cbaf5b8f8eb1f1589887197679286cdf3530a2879955e36116adeb77ef58bf8f8a8ea9f12814ee63693c46f4d5e1c7bdd9dd63fdd3ad647ef40d02813f575668392116204d431c7d91d662c00b1988d4b8428b1d6810824b0e788822054b932862e05083a171839f1d8010e20561644902076c5cc08b1b63e06042081aac882105859d317f88641451c1c40fde00a383a124b4208206656ce1820e785069908174c0172c80610639d0c00b193f33c8e2c50d4353f0408b1e80c161f4254a2f570011751737b4fc415b6badb6b1d65a7ba9b5366b09848da3852b3de80086052ec0f123a5063f209185162a9c6871c50c20e07863276d78ac8ed837d75abde4e06bf52b53da8a71850c443b80011a446704f94c8f8215384059011c64b67811a43a1cf242c4d75ac6d7ea2abb5756340c423b2656c063d92aa5e3482caefdf1ac542b12a853aaa7432a7e4ec9856e4b8271c50525de5a99041460f8601404183108c1eae11756b83c01860d164646dd3cab7ff20596eff170e80b32fe3e1cfaa24b14580141e86b1583944db084d60dc2e5891074f8212e4cbcb5eef2467af1d653d62d4fdbfdf1acbeee6e15d4c3f470884b94cf1e0e718192554ea8628c51084e1e000514757aad95ee54a792ce4969e751d9bd431e74c6d10412d4a5748c8bf7b75ef787391c42f795a92128c0910792eb13dfb27c4ebb3349f80f96f1d5ab8ef7d671e6bd5ac36f07cdcfcdbfeda74f78637d6aedd3ad0d7a62d4cba7ce5098d50de004095fbd66afee0108247ef50f4020d4eba88ef690d48f7cb44590a9cc39af6e82bca9ce7999fd76bcdd78e6792714ca396f2a5358522977249594521f5634dd57a66af50f0ad54aa5bc91d2536dd198576c5f99aa96656508643eacd7b2e0fc81937a424737c0189d6309f1eb55af65d94d944981e106ae4f2c5344b1f2a787534481622d0482fdca21d7a7c7e96139e398fbe381a4fac497ee40be1d1fb78ff02633c12df8c3347bd87bafbf7513dc6a3b5b45bbbff9d363f4eba7ff72e9f5a28123f3c7e87278dd8458c20afab06ead9be68fd1c6993dac733b3369d81ed1b2bd61de1ad0f18f9b47a6ac1fb1961540133dad47ea5adedc7bafcecef57cfa2fb57a3eac68e9b56e2d53f6be3d226f6ee48e1e9d114eed4a7923f7a7daa2b1c55bcb94f54b712090f9b0ca16e211d9c37a0a0a1ddd0063344e2c21be7506c81be9d68ff8c89bebf6baf495c4979a105e58a194d22ec8fc473f9b0ff350fa10b18b20c905981985a84cd1ec96132e6c9091ca2db6bc94130a9e8ee97f165a7afc71f97424d531e6d353f226fbccbc1f36bf367f5aad0bf1cfbcfa4765b6e9b40295b83685fea710962d5e26d1241364039421a85cc9c285bc91ff73f39844c9ea06a467ee850e9f397e80f5babffdd8a9cdae4ccd3b915c2e24caafd0c787613604058f339f9eed4f0eb1407f2bce3cec391b32a9c8542753d3b33dad681ea61579837dbacc86d43da9c89492f85fa45efc2122f4c2c94bbf598e8e2efd3ab67be2bcbca9041c613a9e114638ab56ef878279650ac111a61b000efdcd29532615f840b706f14d339552a956abcbafe3eb5e97d3a98273b552b99b4c73ca6bc7ea5dcbda57a662ddd029ce8cf507f29aa0033002e451422afa90fe790400804aa656b38704e613893ea41b51027a160f739029558402bf08657b0920f1d2a595e82345005da74b8f4234ff45a1b8041c61c61ce44ddc02a6a4cb784528fa9051083a68c9c1cb88e5077004e95f44e28574f49ee82fa451f0f8710b1d85a2df1b97cc1f4edc994557ff68bc054011aa4f0f0350840ab9fc40a915caee10484c0115cb45883983945a6badb5ce90af961884be56d7b5d65add30c909a440ccec01ffbaacd51224c55bcb456ea1801d14a0030f3f2ec0e28b1e9068a207576af8e9610a358aa8c24416451405d1a0e9a005530b563bada773a7819e52153ca5b40d6b7ba8a5890a2415364e2aeb74e329a59452a7944eaa0506a2a79452ea06b52ff8f03b1e6a81818acf1e6a81e1ca7f3c66955a73b0af8e44f575f76cbfa7b52b36b445e57c108ba78e44f5742ba133479f28a97843cc5442046178dc79c52a0c192d2d1069012253c6b2ac1591960fb5bc30037ac9e878a8e585175c28e3081ca74422a61b419429025383a11ab058008b98184c79020a1b64c041111a3b69a33a7cf2558b0b39ccc42a6d6ca1174d55196084570b59f46f8a2053d133ad93526ad93a551044a662f637c27b6badd6bad4b275472804e1cd1b4b8c6c70ad11c5c233d0489941bd82c043145b94f9810e52e87c2173441718080141086130a161040500a740ebc10a013cd402248516a026ea65467e034724b6f0a18b21aa2ce1831a43245001032fb230c38d2aa87ca942bf1c41c2327060c8072b5f2c61460c6e30821139885963882d6d30f105caf525088d83f2432d40375c286fe44aa7d40fb50035c1a2080e27454429a874ca55ee6cac1405e1490f878aac0ce0e150510d454ea2148ca54b9fb00d1b967cac8ee14f8f45298c3ab188e8037a9c22a5c8548c9e742a32f5d363410f3af52035638341b2e7bdaaa2bffba6ec356f7993737623f307cdb57a900d66f90f474cc62ccb5c08fc6ca334dfc97c7ab6a76bd9fbee7dac79d9bdace39fbf6c154343e6d1656de20179e4ac3b13166d0209ab6bfd0031861551ae58411b29a0f2b3831a889e18834a1138c08063b022e79c73cea214fc9cb368869f452e14b930c6972c7c3066074474d1c4ca082c9a5083075a407181172d46b8c9c552d0268f238c50b6d65a1604b156eebefcba6b81d6e2a8a10c910d1a067a07bdad5ca0b55799f22a773663a69471a28c0f9ce0c764b8d8f8ccab2e1a351d0e9501fae93892cc1314181b4aada539e79cb3db7cce64b8cc596baed633efe7b1e9e14c4a664ba482318d2a9091f2d971a496d06fad28d4574791794226c8c6e6abdb809164828a34b182cc1ea8ec70680c97cf18e6d8c3b02b4b965d598673e699e30c3b8ed77565d975b18b79e659c79eb597c8941c1ae3c4679f3241988e99b23dae37744f2db71bafc0d03108f75cb03242702fb3d0e899dbeee150991b5414c0292a2a8a23c80a3f491415151505d9b09b5760e8cdb5ff6098ead1cfbce9dd8f9ef6d8f61f0cb3248638fe234151d1df15183aca75ede1509920f9df098a8afebaa671dc57e784c8b22ca3de860c95874364b83ca6f2d871ce39e78dc27c277b742c7bf49cf9cf4fef5ba239cceafcf9aa61c79937bacda33ef3507147388275383406ccc321324f1ee3aefbea5d991bb03b7b6058962b01d7b56d5f7d1b33e55e1e9429ccbb373b01d78d4a017e752199ef54d73edb0f46a372b4afae650e87ca04fdcfc7e97d4b7e7a963d7c79f67e1e5f8e33311f8f423882f5cf8746384466cbfb64d2e60621289272861b53ccb8e2839429b240624a0f72d8011b4b8c6c70c5e1658791171ab64aa7d4f170e80d1e6881a3935dbc9066a5538a69e34757586cd269ef755d1cdc48a2862db2604283252f0083862741b8618a1a8258561853dedade8a3578cbb22e26c6cb1b59bca18507537653dc614201c044dd5751ac5e302446085bcc392d6b255324c894c9c67988d1a2d2f0d3af982a6c151f8fb0d838a1052e9a54a9a2840e55827c291d60086f289db2040991c48f3ef2463a8d3ee1fe60065dee19c3556badd7c216b6b085e78578ef78979aae6eca4bdeeb4e39a5636a37f5697918f66f7e955f1fef8ffae53893524ff2f3138b96869fffa2cbcba43a9328d19ba482bc108b7c0751162310f85ed8437b77a653192b80af0e4ce50f18048a71f2d0f5096bf98349944f7a74264d54d49b78901762519028944a00c3287504321dba06815c9f1eeba727f9799f1ef83f316e6055855ec99bec32bab4309f38af92d0d7e15fffbadfc93c3b99ec77e6bbbf8cb397f9f572b267fbcb3b9663d9837f2d87d9f572f676e2b53cf32c8f274ccfbc09339a4db6ba21db106a834a3dc156cbebb5baeeaf3ab6722cb7fbdbb15e77ac67cbdba95eadc3ace69de939da35562ba17b576ff70e0d010f1e1e2b24280d010f25f5727cbd7c7974a5047d2af38d15ce20240413a12b5b34c9a18b3156b061051358bcf0430c300c3f4e8ac0ecb0450c9330c8c0ca782022c1819804b7c41258c6a082c5081e803e2844001eb6bcb065891db06012e4d30f31131f60885e70c118685cd1220874c8019fba8a62e5031e3ee0e183f959af5e3d5eea365ea7318ec1e4fde1901a4eac11e6550f87d658e2d5a09286105c7cfe925b60feb2e1c327e0e15098327e288c9530367c182c8f3f7aa442ad37729cb1e7ef8b54682cb7f6e783f717b54c277a9609d1dc8af0b1859dca14ae518a9c734a6f2783dc9cd8a5c43df3776e11b184e938723a9973fb938f638cfb9b59c9bfe8252caf67fecded33bf98e18dcaacfdc57f80e5717f399647b73c4ef9f1f3d17226a4e4115bd3ade5df8c97fc835bac6f46ceb9bf394bfb835b8458be2375f25bbebf24174cfdcc836f3f2b4194f18b5bffcf0a16de5fbcf23b9ab3f0d1fbe65b2f31fd38259660f9dcf1f3d93df3bfa865b7a48f741f392595b718bbc49a7ff2c373eb681e3d468f994efde8993625fab0dcca8468d13b2f3bf5689c6479167e9432425d878f39ffa293fccb9c34e2a28ffcbb2e1f6dd475ceadc7226209965bcdb34fdbd6a332a5f7b7b9751ab84548f41d4d277f8cfb4bf2f396679e3d4af158442c017ea672d5fb95e02fc733c72262095f7ccbad7f7067ffb05fffa2dffdc12d65e89dcb2dfff0f6995fdc3e33ee3ba5f69de0f299e5d4bff6f5e85ffcd68e45441f9693a31ba4d143501a3188210d19888888c004115df125cbf645081a5dc34b1959268307326a30925dc8b0c19201031a75c03101033298b8910d418c209878701a020e66111b51a460862ed628e385034ca1c50c8088630b2c57aca187f042015eb03588e942c61562bca0050d64418318c634594306431843cb0606e78213555401c2105f4451458d21d8a0650b2cb6cc80658c318618c3881adc821817ca983266075288a10524830c942d5f829822ca0ac08cc1c680610018c8823143cb60b408a0c7a894ec58697b54a22992008000b315000028100c0704e2a048345104618d0f14000d86ac465c4496c8434992a3280e8220838c318610000030c68011a29aaa00039f1823b94d2ac8e3268fb87b5704f3a4a81cbe53726bf858d72aea26473eb1ac9cc035a4a344a3fcaddcce27437683475e62053f2c189b16336d4f9e4c8a7e447a15aa3e5f8c3905fb8d1a45fbc2a12e3e1a5c03f4861b5928650aa20d2ea9d1dc7d4646ce8f1931fe1cd2be5387d46e3be50f33ccc229fa8d33d5710f3850110e5e9130d003a55185ca9ab1395564a381110c0d934808078c409ac9a509de6940ac06ff877bd58966e2f602187c15774ec2234ac9da36fd9a53ce34b481352bfdcab857770de765402b2d1d2346da004edf7138dea768d9da0dbb2da005a1b2d1a74e87f83c64dba90e819e9094f5313d90443818ed25a4cfe492fd72ed02f7fc263bfa71e07157e53cefe0ced3afc2e82ddb3bd7529be7be3e7f7336d37b9d8a70c06ce5b1c4cd2e569945407473e7d1fb37f09625b94c085856478f9626929da91383b912483cdb85b46862977a987422d9947cf46f7d670e033479b6b6a1d2472a61842c052ace20246338a47ada4cae22b804fe95aa89876f93ca6d29d546377b4fb72fad513738f95c92de277ab5afbb453e923c73e8ed287592e3cd046b18d9ab950d7dec7ed8b31f8789d0e60d09dd6b1167b734c9d938dcf66664b189c82f06c0b726a5e79e6168d9a4a15cca1449ceaf98694dbd97895d1bb2b82896b674a3c0ff23ff94e7b800f7b0f586f545f83f53c002941133b604a104fd82804e7a31ffe9721fc7647fa38912aae06ad6df3cee9dce004469918c70d2c5c6548afffdbc755def2d11d07d0e9c3dab29536b13e70785c8df501b56c3fc4ebd193ed9733d9cf5c63a7407f1b0c04b193ff9417817b4f5b0e2071b8a99ae7e1ae244b9dedecad8bdd6465064399b70320190f26c3024b4333e539b1be799c68b285b97394ef6bcce8a62c8bf08e3e40443f174448d44aca8e1c209d4509f5736f043385689fbb11a5dec624e1856f113d4f5a435e54438eb6d0884e4c84c341a18cc6cb0db72a1da719dcb9a187a4024a841aeab3153d1b449197c0cbcbcb7694ea62eaadb2be94cb43692b4989f5e3020ae7200ce451124fd4877877155c5447aad2bbb229224728c184e6515e846a1b7ad33657ce549a50efdd0f8d237f5dbf3818d055c8c51f9f66f7e2585a9206cc71145f662c9c998fae43ac8471b901518b31fdade91da2e45c7117c67d525ea4320bfbdf830a2dda29f18331364c782b216fba7f1400ed49d77ba9151aa23429f4a863c02b174dcb380a1740be81c8e2ae4b98cfb7dcd48dc8513203a19e416a887185e89b60cdc2b9c748bc1fe719e195bf9236d04dc83b6fb5da12b709d3a505e45f8acc43561e83a9e764b6ef44216eb5d8edfc479f6adbce59015b7588cba5a5fc18545385ecdb33375a56c921a56084e06fa147ab255c4e5f0d13adb54f02dcdc3336c356f6e976f11ad87ed693cbc405437b718137394332778ac56447d5aa6fc70de531b5d6813a8720b69e68f9636518bdc5a08b40fd52abd8ba3ca6409a94b477f83f750cf4f2ac519eaffb84146c07ab812728352ec4acc34d58c7436e73c787a70c7a5467bbec17067bf9d0ef9db0fe33e146e6870145b2451fa820a0cb5467fe5fb3e337093856f82965792a4f5396b9b437e3801780a665826717f1a11d3ffe7b03f3cf107efe1011c4177e3bf3eeec122d9fba653e2679307fbbc4e7e86385c0a2a65c5534ea0fefd010c9f6a89346509bceb365e7aa0fe79191c61bc3246c6fdc2bf7ee247fcb0bba5c9f0a3c2db71485e6cad63c7ac97ae7f839002b11979bc99e0348d1c27e10ce499e76b709a478d2e0e31f9ba91954eae28946c67b9b4d255f818d0690119c54c01031ebd003b722a5e1e693c058bf0dfe7850c532d929de91bae65c27a265eea9eb39e9de98075f740e73017444794beeca0fcdb485bd25f6c2661265907d1b4a9727932ef457232cba6ce00d8a5fb17bf28ae4dc143959d2a8a1f8261ff40f3c044e490e33fd0c4410863ee52039900f99a14da88e49bbebb57ad6006bcbf59c5403f3eed29c0bf28c12bdeb00a401c7e96d630c952abf2873892bed1f6160b818c47f6952b2c5351a118ec10ea45f6de4b9ebe787449c238d86418fe86a123a3166c9e93331d56a4347602280866dcfae88eda446cd41f880b3ef525e9c2046c681d4d0eae7685efd808ef29553b4dc2c06077827b5f9f8e2727d178bf77e05aa71d663241db0017ac03357ac52f8ad9e0a2bc53d28f9638a5bc2dc031c0b4acee8e20e6d13e06363931d9e4c034b44522d90f59042e126161f306bf3063fb3bdffba539c78bad5f3e6fa53c44a86e88e1cf00386b3342b6204fadcb0103271e61bcc32ee3c38314830afe5e9b40d521afd279e8aa4f45624282be107fb515526898e06d38f78a6e2174dec5d8bde1375b0aa9e93473de9e0d922fe10c2a610167e94c3491642718575a54a3f93a8b1296cbfcd0e87db0fbabf47073e1f2b3055bc514c1546305c04077e13ee421ab01b8ac005ac8b2b7d3d8ba5c3979490f9cc91b73933f27403a7857d4ec315caeaf418e9d22db18f9b4016099a9555c56839bdcebd0210ad45988c2717db3541cde2751697840ae7e0006492cf11c85cc3a4551aaff5d2974c503ca7636f21cb18853f1bef98a4bfec681136eb82273cb902a7ec7b923beb96b9ea03afa1fc1351347e89d3b7dd58d635e50202b7e07ebea7494ab2b8e4d89d893311b079d856e694cb8ec0203ca7214ad44e4aa7453653ced2314f611ba930825f973b147f2310675270f7018eaa2c0a2e0289c88efb305defcfd9a544219858f17897e68528d516f21c623e36ffbc6dbc228c99d51ea0ae4b55726a792f60a85a0348f51cf438d861c98338bc11c28ec8166f1f7a0661b54166ebc45eff49ccbe98994ec50b2806dd119eda149490456eb4224f20c18b3ebad9d9ce7ba0df72b2e060e66b9e9ec699792ef190d79d9e0f96c1456bc5255bf9c9ec81eec4bc950c156f19bbe3d4fd446a1e2cadad6e972e9ac7ca005e762ecd673f15e89cb376ba09a5f6a7f501cfb31d8ccdb8789a97bd674e7063c0a1b72baac6ad5f1e0bd54afa2e6f304b63295e1252f0008a61a804471eaeeb928b72fde07a1ae7c2983085de0946cd5c21d2f45b7868a6bb9baa4f0c53ab68dfb65ab807d37dc9b1cba02564677c5db85ceab9e8c1d88c83ee417079918b624b91f8462a90b78b4c56e1344fa7e67ec4cedc72497988d071ab572187bcbdbd0fa021f7874ac1d145d9aec3be5696c3c393e56859d4987c22028f88822996dcba3db134586fd6e5223b5fe697944ca6d88f99f2f788076aa4b4da52f622ea0ff83cb5293239ed283996b04efb73026d7843f245a7ba5f3b627987cb16b446ce6855712b7a42e631268a31e68f526e2872ad274469cf99a749dc0e7cf165e0941b8eba1151e7f37979621999cbda0e0b2a742809bf0d725528e2d14c82a3931ac2f2b2c2223ad53f33e83b65d16319d2636357b3310f2429c84654967aa70ff8e4a5ed200840af76b3bf2915ee11fe3c97d1083d529dc10792bf9c5a97e2887a99bd1e1c40af9515cf7abc2e0d915155a87a2bd99366216c1066191d86c809160a3facbc4f7822a98bc7d58b918c2b80308e13592fe825121cc641b44884c18963e905d63b2d730a81ce1afa93dde99bb35e6d30c93ffb1b910569e0bd7f0661602232491836202130a909142265b534b967d68514d8a37a95606f82a89acd01d416aaeca47cd62f5a6ac1ffb9d615b950b36823a6d1138d837dcf21414fd2f1e3410a8d848a0748f9b1de5f5203b11b47c4a1049bbed1ba7a6c11db24da4fdc0a7b0f758f71da20cd82da50ca01ab8d27dac46a937dba07b00f1f42a2c8c4acba53f82f6e94b0220ef780131f0c1451627037a0783171cadb8b213f2ff31a2572c75324504df087237aab7938c87a39d94b2c86d482474c0d3f19ea6ddfbd15cead65d4a048ca69b2f63518a04398a8426825edb61211fed8ae39b6f18bf0c59370211e4f61fde23401b2fbf47b69ee94e8b871c6b3b3af4db3a91fb37ce23b1848292c8dd7000cca8ca00c994957ed230e52fbce54d1ff64545105824354e5b6681765fd11dc5ec23c1fc85f29bb85a722a05b0a6e2fe7a561f7196ed10a5e1847572f41450fa6a514dfb129f4ea036d3208e3bf9f40fa484b4c195de4795979a82a9b4c67a8b2c7ac82061ee2fbe68ffcf76f4211cfc579c7b7dda817384094b52186ce5ca42285d9f3647e51be712ea36b5b110fa11adddfaa43b4782d61818212c3e41c27e006128f80cf563bcea0d6b854035890d223746086434211ca147b03ca7fc09625cbd675fc63b5721bcc084fd703d1282df77408e1b65755957c7f9b83c2565615ab89ec422211e90b315ee468dc2aa176cb60e9603a7e805f6048c83f2c4a667c114307350bdc35da112f3eef64d6e0c9b574688a0e431c17c872a453d798ef3ccf34103ebb8894d8cd80bf84ddaf9d906ba85cad68edd501f894c41f3c075738db4936df7f692a71b2fe6b960dba4437f2ac1b3835fb96fdee833b882379cdc3f9f2cc119d052129771feb57a3699ed97e9069e06187206c39825fa7ca874d582523d0e382bd59f842736840e53936de1e9a3ad7543d0b7e540695834faba561319a9895d5f1c7c15aef1777c945132d0c2a7a58f06d75892f4a2a9f4801459367559b8b1ea99c2b174968f897c63a9f2ddf6af978894ceee69105b43d04628ff2764c1e68e7a08082b5fcde876acd93ed90113bdfb7f5a566012de31d56036d438f6ea49778ae8a41fa48d913c3ff3fa0006c5a93eabac45c9473c7bb9be5a3853ed69330e5444ebc2e1a4d1ad2aac5d9cc9aa58513e84f6512fbb2a1fb718a559b9ce7637714d550855a13b03277d279395d1fb080ee6eead4158ec8a8d92ae07aa944d043d4f8d5c1333c877dd4a8435e6700319fbda60489b87ae7f2c2cd033325c90e52109313553e0618ceab8201e000ea25a3f98294fd73a48790337ab1dfa05544da19781eb9cca02734e5ba693a5b39a26e6f2ff2dac1e6ea710ab659793c134f0acfc814becdd13297a9bc22476cb6ff839838e32d72907b0ea7ab0b1d6f5a6d9c3398916e8b4b2083e6ca60a3fb83b0f95c96da977c723ee6d7b4281952bcd0a0e6826bea356c055af4b2ebd10354142a33021a566ba6aa746a3fa628b798287cc21879179e362e0b2680d2fc36cf901c1756c2565277891d548ec358e6c53631b9cb4f182dd7f41e3d14c42299c50b8895414c7642a05c0bef0e381cc7794246023ccd833447fb1fd53207f806ea244e19e244918c8a459635b0a83f4621996e47c4249f1ccd1a62f0126d0dcb82597f5fd4a5632391d8a4a911af5243fa78e3f579bb608553466b353837a25ecfd88fc00f4ae6a167c1f41362f86a6a70f39ce55f656fd8bcd017f3024e2ffd9ed5d729684bbdf99e8027a9a8bb92931d6118a8d1556ca44a7a0605d8fe22925741e225916f338861e3afe718d334a7a45541c0537768f284672c4359cd7846d166fb1c7315e1b73c6223c90689bedb1896c0f974b5f05a5b40209bfef47630454d670b6691c666b623ae22079fc6a4ecf5cac71e7806c83d4211ea373b0ecab653bdfb5bdb80c6b7bec12d4481dd40b006a908e766c4310dc6edbc2d01a8370af3c354e16a8efb44d710937fbd17e7ab96818340cd9e7d9664382bca187a864b7c74b7744e0d4c5422d26278c86ca893bdca530292b758207ccd688b839a0d1d54754acfa498a93e541fae72220640beba3a8db0b4cc12721164477b644c86aa925bd5c79a0eb329bcd981f49e9ee692b499ff19cd7e541de2b34934f0c358260a9b178adec37566c85164c57d91b3af6c00f60f7e060420448338d38c61e4b58aef30224f930391fecb7d01393418217eff003e66c3bb5ced0ede21f42201f7f49adaced743adf156abc5cb7c465adceb10c2e87d50c644860e5cdf8c9e67a2b330e32ef0e41f047928ebc76acbeef17f7800651fc22a49c22b72885b105951b7e3d7cd79e375c01890817c30450ee5ea420fde9e5d7d14152c423fa63e3d9bdfecef3f02015a06a1f0f5edb978bdf72b365222fadbc4c4681c66d2230f910ccc370965543a75e51448eb7955372bba3d8c94e1f176efa7597db98f4122bff76fcb13c2e7f6eefcb1a8611074ad15da4599ebaf496e226b58a9be7f688312bba5045c30411ed3918b6bf064df64ed379b753f0b3f4900c189e9cb2101a0478eaa0605d84cd75c61754a9c9868923e5884c3611437b605c0d01d04bc4ae18c4005d97b0ea5cce17cc7048ee22aeea0f8ea3f69229caa84930fc0d36aa3d9d172f6df2818a68032b1632a31bfdf3c5f3c0a157dc7f23eaac98ff36ae340960b79bc65509fc6396421b8e0be0e67c057c2b5f76642260b5958b3736866f6fdee9baa1957b088a74545be7c35122131aaf9c474c380758a176592198424c17a2c0b7d7b14d77132eb10104da8e876cfe646ca195d4a406a9d89659f345733c4ed215c4f5e493f2ae99e119e96b0f78831669fe98111f26fcd25cb48b4e4d2420162db1c963bf62a6a27c6cf469f39fdc596cfa8a78287efc14533bd03c5d3ee246773f44053867f4540b9fee347c2272ce8df9b89571c2af07c7e11363f80c03bd4eaf96398cf3659df65b12532b4b2c0ee30c56da1c807bf85290eabb7a2879de636deee178c9b402640d1eae576f2b2f5dbe68a7783050e4880d6572c0a755d55fb75aa1ff9a35a23f2c007d9df33e3752f196c4d4fb6f37a7b1e05b2408d5fce6abd37affa99f23ec0bd8f80a129f5a21acd23bda9c025352441eef41dee22464765bb23e9fec3c7b1fed0a9cb29ee8414e69f51bfdf09f4283d8e84b8020c0ffcae4549319a6609d0e2aa8fd14004c74ac2d304bcd131cf7accbd8310213292041106a26bcc17cc2c8040127e07cd01f73da0441cb4449184c2fc166b027db817d9d685c34326da02cd96725b3c84bc7088c475f4c3920729cf04d4f52b2ff8d176ae262e5fcf761c30f8a534ab1fbcbb35ee86f59d47e03a9e31e8a2c0cdc42283c89d20494e6debd65f0f27ba8edf30b974fea8e3824113491c02dacd467592aeab635e2d8ef468c9489221b0f2071d6aa72abb8bd7ae30cb26075d696ddd90b6b6613151221da8ea4e743dc19c3022b3999a016a1d2bc2b272756e50dc5bd2404f6644eeb9b2acfc12acb76090ba18e51d4f609878261e0539eb4f83b71f03f07ac6e3c5ac0234c5fdede76c757e24f8f9b83918416bcef3a34485c869036290d1e4fb9361dac39bf2ecc2d62549cfff66ab974183b7437a6001a181dc6abbf85000d4d5d3050fb645b6c3028f4969664b060d88649eada547d47af2b4aa9cf108b672bece53a0b3916e9db10b464f815c16af87aebf10614868855b02de5ceaa972a6827c92ae8d24346ac02bb60716eef7b9cbfad0a14d44f659b1cde26592c61244109361a9087a8e56d8d235b59e0a6fa2215e3cc31434223f545706aa4d33e5715b089bfec261a74c174c6126acd404eb0f120ad440f19801a244620de22b9157de6770799e2d14d7d6689b350cda2d2e68dc18abd55cda50ac1083032f63d4c6bde2a739d78b76f154b77b408ca11438bf530768e8634800da7c1cf4af1092315c830d9b927aaff7e8af2909a9f3c878a4feb301af84a7b3636e1d66b1cee486af54167e4e28fd4232d40367e5b5dec1deb6875c3b8c4036fb30389a550522d19ad1f7f39f2ab9323a3c5a9014e4946d000e30c001a88d51804dfc0ea1080fca7a294cf1b211ce5580b03ed6dcb495d13ace3762aaa624e4562ee9870a0cae20fea4eff320fe4a1ec31a10f1e1fd1e5efa767bf04f09edbf8876a819a5bd959021c7bcb32cb50951175107585e3aa8da01f4f5a1f13d4c1bb001789dd0904ed8db84e342fc96e928a6f7b68d2eb8698bed248183c4b7343f4e75c62161478ce76d00ed448fc2173480e9868234e25ee0f550ff56eef54b9b88723e096b540b140deee623f7478468ccbbb6e2d6708256c2e86b87537728ebfea361dec0fae36785e1985aec39aa15ea606e8b529200bd11a3ff82b45d27bdcab7e8681d128c31393160bcb6e05ef29886313d8bb4cec42598c61ac94e7772c9cfef41952ace35db1b595f8ac97cead2f35f55046bec90714284de484a7d61e6bf2d219556f0d6f4a9c9baf76ac535333c9945415ae2b99cd62d54d8367d5541ddb8e19b8dc988a18d4fbd08b71048333bbf05e3837b89ffd788afe89d5b56e9aa3902bce89daa8b52ab9692e47d51c3e6aa328c2b6ca110e5d6905f01d8cdb8685c30677a33502a3863ecefaa028f9b5926529bd77c75463598023b404ae79f0db08f53a7552d1227f2611653cf7b8c1dc0a9414aedd100b9b52b7a709b00e6f9b3230ce399c1b7128e15de49aceaa16aa1b11ee5fe11116418c91a786993b05e1de9ace5177cb029dba0a51014e045cf98ceebe242b57d116cb28d9bba35e541f8429c15975b317ab6a2be68e4a53dbf86cdd369862fdc73de35f5bfb1b82c4f41f5f3196ef1024c41acd27fc111866cf2fd0876ea3ce9f6e7c8e7cf05b9acbf6133bb9c3f11ecaec6e0181b22e634177cb0b9db9c049fd4f98fe0ed52ee4356e1659cf54daf5d9424cd7590b153f073436104932cd44c35fc2f8a35343e7441e6f7427a2273dd0c174493c22540f60414d730403c242b00c7b22f7d951a7faf161d8ce8cc7de9ba529b30233ab94c83e94d7c3cd207051fc40d815be964b42447d15614e9b9cb1a6939e7640de8589c543c2a341075ab170baf7ec84f0703e85248649c10d9a3412f26119b67eaa750d4af7bf0ab59808ec6e5a814d6dfdbe84d621993d81838231ed53998ac150c3deee04e25bbba7560c570013bc73742869b8ca9b3c6cda654f1aa415ba3fd95dfa98bb88a4bae07d27312a44874835d4861f0c7c8cd94df4af0c30037fc8215dde890a4480ec5153b7278b5a33efef47690e3c0d6c1417a0b96e4a22bea87f86be2860c8f8fe60d0b19f31fa2b88ed8909b81879f75ba7953e11d4c740d7199ae2f5bf9321b858a1a412fbf35bdca0d061b92b91eaf7f87442b2bfb89f49746f373d239d1407a3cfd2efc730be6efde274ec6c1a56f04666a196496393e1c45fc8e4d0101aa1d3944b55630e5a06cf1c0d87e41ad9a654af63aeac63b24e514ad6071e4ae70150ff82ad96fb9d94172736a84c1c9652501e789a16546439dc8daa8b6bb328f63318523ed4ef0cc9e9d22f1d809321c92eef9b1768c894e7a2ac47336b3c7b5e91755cf149ad095f888998de30b089f94e23e8057036ede34a5826a22d631458bbfed52da1211876850c9bdcc15a9fcb31c4f54a8e3624861708c4c2654495af97c8ed6cd42e2bb4d8b8eca261bfc8e3ed9373bf24507b54d37b9cc2c50e697d6e96434d5f998375a577038a52f6e65cdf1da17c057918190ec38530bd320c0cf24b52608ad6ba6ded30adec8a7f29fe73e5fb3be9460b6158b119c3af2cc8e4b8a77b1749f715ea7b9752f57bf8c768d96239283e7ecfb232c6b65a8502ba5687e581fb2079a25b812c197667e5f5dfe55a1d93645ae978f7a3543cee8a1b49723827115692d46e6c326a482bf436f13dd362a19824dc02a5c3edd58d42ce09a25638cb3cc82c0b52c02a3e9915f0f6f93138d6826ce6e82fc33ef5d774b5de121eb40b0c9c8b50def79bc9aa22571eecb1c3c9b7f17e8de82817929acc48cdeb1594f463068018e6271f6fdf29da154b7b06e6f32790dc26e9bf956567d1085fab6cb94b04fe29479ab8706fba8cc3526a64214bbf6a45049d7e4f14734709ee4f646606abcf264799c51a18f9606371228692bd96e775c1d75a044006b1002a82f996a0d59d9e124c838f3178e5d64247056736b685ef655c6d05a9a490e596a8a2f07c31d1ff7bfe42acc004bdb838ebbe80e782f2b814932d3dc55370a2a7754b50f45455a261b4e27236d27971347a395a1eb73c568e240964085704ece9fa0817763860df65903144b098220d9673b09677589cc5d9040b6d0d61e23d89b046a10e4905858713a34ff5c38802ba2de40593f56fa7c94c94b061eb84261aa67b2b5e1120e78d2046edef9575d32c520556f66f5e63f7a27b722ac384d77055e302e472804f03deb4b2f523d4c7ef352cdb3a0322c11fa2b1a1ef2819753f69aca06ecc3d3d3b4fb47a50fa8cff79fa194107ff1a5dcd15d15b6d41eef5748892a6d7393428bb3a0b1f9df8d06e096eb93adf14bdd64c44cb09236aae021ce6ab97014d16f80d5b1038f56cc30dfea65cdcb622ec9996dbd00aa994b7f0f66dccc41bf920769860a0fb9676e9de9c1ad0d77ea86ff7fb20ea03850f79cf61a7f1d9fb8da4bdaf2089026ba8c1a86c628a3afed53a224f8e6631016a2511d6e69f03bfd6ae2a6881bb8c4f8b0dc47edd464bdf3316990b809d875172bdab65f1e5047131709299d0c20c7fb1ebdc310c7006af14ec07f1e3147eebfb01c6618945bb4d89896b4d01ec1e6599b83f49fc6da1e2b64bdd1c0cd8cb3fc50280f1c91c7e22fddaa01f709aa36c809e993399063396b8a7de4a179bd847154bcedfeab1443a3eb0302622a7ff334170dd917e45c8c17063962909efce3342083aad680436a602d678128724f5b3902eca383843ff15b7bd270a1c97d7639f50ade783fdfa40ffddc2ff759e29c38c214cfdc7cab8147ea132355ef9547c816d33e3c6df4c4edb871b0a42cbed5f1ea911e3663cd150698fae3feca591d5e04e69dd62ba1393b47a622721b2ad61f6d86031d5d2c90782f4b75fc1517d2e0469368fd480b0e49499b83f4d5ba6d4c8bd2c65ebabfd8ea4c6356fccee4bb45fc9769fd3b4383382e00c82842e273f01f38a199d715922d4ef2c463228450b09a713380ad469fcb51817f4c217143ff348e687d4a4ef7e5cc25e20dd20fac68bccfb21af5151edd72fc4a5e78a11090e67cd72428740d2dd94749e18a5041dfa3db8a14ccf3971082826b2998c1ed19320cfbea8e6ad29ed830d1885ed51eca376e37e47dec735439430f879e107bae814f5547bfaf38ea58bba57bb5c96e027adc0e5dde5167858f0a9759431783f80668f38f42126ee580fa4b197746e93dee3b64e22c4a025b5fcf07507ef25c53b68da96207b543d9ad93c7041ebc5f3e71f3394c2172eba6d9e481ed3a07e4cdc65a3c6a804174e7cb1d309f7b7125707ed83048aa49670a396ae3d16c346a20d534f8e5d573f48b67b957c8aa101fe26d3ad70ce17c71d6d4bc44c9bfecff88000db5bb3cc71798965dfd899dd3974ceb469b894c9667b2b16eec7bce93282bdb5f3fde85a55f14164fdd7f57e1cf265b10481c10cbe7c6058fe42a754a3b024f7be58a41c39a25e54b6ade466e6724c7f50309518cbb14477d79d6a34dc6bc655af6565acf296aa4f52b02dc792c81d537f6ca056ed77d7134b1c57688e9f0c42e1e56f286b40c1728f1995fb1f7601d90e8af785c4ffb389d118c5e34e7b66882782d7557cbb53a01eea8b73e62f33bbd7009f8d7203ef964de3186130d0b78158e6e284a8e074fd4f9748c4c689406e7416f8f5a9107552ee1c2321aae286e7dcd26562bb5357cf2432501075af989dead5a520a10e4a12d19eb6ae84b02076eec3bbf14a67e494e88bd1970e8bfa64486c61a4c7ac3330b0321a2948bd088850cb0a74963060f048119f171efc0e5aa5dd4cce94e651702de9e82b49da578cfc3c0339f31840dc538d7c7d6ce254cceda2f07e71cba71debe72dea0c412cabc7fb2e748e0113ca52b4bceddee84cabc021d789961deb9b292ce53b178fcecebf0bb960f2e423ee3d9c4375cc955bf3d6290a0b9d1b8359c2e5e807e6a894e090ba8a4aad017e5a0a67926e39b41ccb246ee1d59dd25645ec4c1b12318c00a4b5dbee30b76b7f54fc80fce5c3c88599096fe2ef6a50fa49fd96dda76bc068f119dcee54987e75002a21e987ae2ea8b4eb06e213d237537ea429a87b4a2c58f92c9038dc5f65da661d0cc0ca5555a784c8aad7e365f9f97e79563434843a6543444840e31fb3cd34e7017c910440661bc81ad0ef10b9d8c1e1701f891dc11d3146c188525680e990f7c902e36508d3c7491bf476bdc1480fb0fa984e0b5a31c6c861c82e01feb40c8ddc1cb54a0d2c6b35241b5410dbdea79b2b9cb103ebd9135dc9cd93619291bae9350038f2deba387c42995fad10bcd1510b3c0544187635e95a10bd75caab5c3e351ecb31a51a69a1afeba62ea5abd5575f6a05f9490e53f2db65e53108771ff5d9accba1634d84dd9ff34821c11b7d8d93a4fc744e854043280a9223b6dae6088ae33b5dde70373d8010653e71962a70ddee07c74bf948c3d33a5a17061869a6c77dbcd698a7a842dab3149c477f409da42bab03aa9897b0f4f0624c9419d506f7dd0e7ced0426c92e97b3e0b02d032a39d5a870f2ed7ad735979b7108578d262462fb0911cbc8854c26296ee524d6f85e00da929541e782f947dc4131994ac306cd0911cddedcdcdedb117e0bcaa06cbaa4a736ae0555bbbb3e28df8f60f7be47775eb14e994be878a441ff892a3bcbb01dc258f6b6e7b86b153a12e4ba616f3ec16353597277c04a40db87e87e4330a975cbf932b97639e4d51b7da1bb5c50d45f9f0f0aa701258598af19a07287faaa7c7989382c6f6726cd67c526e74a0c72558e82af227634e103718d7a42cb17b38595301fdd967eb9429968f98bdad12760e66304cc2858562f32d56e587bcf88d26bd591c742d6a56a313816e90b42f1dc5747f58cd54912004f6ac59741af86a959532698e812112c49024e9fe6434a0455306352a3b5b8d81262e5fb869e0caa08d63061100fb1318921c6a56c4ff61281d86565cc5ac63edecd11803aad4abbd72cf2839938315ea38bd8ad52066880a8a5a08717162a260ba7596ffad46d82839a24c802e21b19e54bc745410963ef69bb2bcdf4a0003ae3c4094de23027da195efe49295430551e41d5688657e00c0a8bb9c18d86e04c675729a208ece20e2a5b87a189b0d5b5a19e93cb3d62de6dc6ba5b1246fd01bb4353a629759439d54ce1b2342049526f144dbfdbbc36bd00078335cd760c94546845485eb26e22fb100b7980b5813d505813d5128cc9ba18609db23dd639680447e98d773e5be0e0c13e9a589319702886bb2a50751cd0daf5028dd76dd14d02975c53b002f4c636187b0d470df5b23d1d5c057cd189b076842fd2c1a564effc64a964ae575f39a4a609e210c8be4e92f8ae2348810fd1a04dabc0236e3543501a83afff2d6919b5ce4067b330142294e329ca41236212dd2ff32e3808a998c057ef4210ce5114606b6cae62b9ceaeb2e00cd144ad62a46698e9352af64c0d4b83412bd49124928fb4b10bdb6e47852eab0cb23874bd12b75bf249a3f4e56605d1ef864cde00d30cac4e77c181e774ff252055d093bfe9ba713b13a26a201bdc4301de32a7a6436f77e577c22f51d28aff816f55e01f9de51421e79a19dd4c720e20d0ace259df63a4d96334c44522648f6e2b849f779bae0a7d320583a0935d498e68c249d589538d10915522a18e34550f77b6c3d65735a8b5ae86417d244e8193ae674562f6e0553278a0d0f472da5967b5ba50fbfae0ae8cda87c985b6667b5f2e77816996120975752dfbc6c2e24cc19f6f42dc6ad0551fe1c8d08068349c9d2af3931312baadcfaaa3c1d6c9e47201579654f62534e89b3f180d1f220a5ae2a16fa652359b71bc7831e4397506c37605c4426ebe5174992129080225468d863937912146808b613865702027697ad00bb0ba3519a3812790412bc8e36a0386f060dc4d76838015dc25c5fe0c080bed2ac646cddf0e952e261c4ddc269153b72b1728b52d82a75e81882e0545e2a52110d7b729595a888d9454569a4b5998b4b90f4b943d583ce73d6372c1bc109943c4fe05b7e3851acea9873f66d523e64365685cebdece42b39241ff0209e87121342282f300d4b6cdc8e8c3792642ca5e7bdd2616d8ffd4849eee9942bf916dd1d2fbabf3a8882443d7e87b2ac9d1f339daeb22831381d7ed942ea4b96e154ebeb1e2dfbe6d19b0d409196d13d7b894202cc539de8fc04835e946be4c4ddb9151e6bc295d4a10ac31afb4b9b2d1568821d79ad67383447b748b22840f5a9b2e111ff3d0ace8c9c374df49597a91966eecc5277caf3e0bb6774cd9f8da94951e871658d005316f22267c2481a2f343bd9209643c9688358bf749bdd3bffd6a3278a6469b28123568819d7c532852aa9e56bae2e7e32e1c5342561b1ccfaf6db1c797c346ba4e1cd53d29257e607f7400654d7f6e3c3358c05e0d6883f8f8d00b17cf120c36cd04cc27a1962c40485e365051b1d01cf21a5d46754888c57a52227547b1f326a2df4652712af31945c55473b5f19187a70ce2b6adc1be7cd2c0d0f5fa4c140aa505703f953c627b48ccde9840a46b2e8745646d426a7dfa20224e20068390b3abe4ee39acb6a6ab3886f86a6a064fdbe099dc7d0fd8c4de747617e480bad5865d720c81a3c9f31927bf8d1903c0c0c8cfb7f18db19b255ce4a5a328e1cf9a480d8255e42931d6c7da4825c8852750851493ce541d8cf518bea08a679d2b0a6edb38c51500c880b4118744983e3d96b02b8bb3012d372d557d43b7f7f3a135c0171c750f1116c0f62d988d79ace63b489b654eafa2b459b2898d89b4a535723182265ea2f23cedc1de0ab6bd3b5abca379a1e48da48cf232a50b4e16cdfaacee9fd8d47a5cac0e043f5c8738f0b4d9c10bd556222be15b9a8707ebd1c0fe5806015c9e2d103532fb71cfb8038b3a1aef3304d297ce36aa6c9d46525da6a37e95b64cbaceb57b95cfdceefe4a8877fc761d907152b68cd2c916510040d5cea0278642355a00bd0f27da4fe105610ddb72a408565db5261d2e50e76aad6d95fa7baae2038a43305a2bb54250144678a34a38aed40a2663c17e0d9ddab0f50487e51ace7d9a1fc215daf67a52c5554dedbda7c6fc4318e79b163fc3d1e409206115f67d69dd0251980ef373512621077ee73a3850a5ef58c828e249fdd93ff043c0424c0bf7e898a46382b9856aa89c8e44b10542d659ad030b0021087a5afeeba5e1de85ed536f2b54ba8ece3da03c0b2c655c9e65821aae91b58f0351ca9b0655d623714e692f5f745d6394cd460f6d0a1e0046d60c1b5a8fee02df72c2454327ea0e0b2084e0c17ed6db9642868735739dbc928d0768f9b14af09712b191dea92190ff61fe3c07129ffb175207b758c6b91178b1ac35bd41ac1dd585d2c6a9d2e202ceb2275e40edb6c553a1f68d7b645d8393e3ab347c786a084dd22f78871130d7f33b9fda671b79de6bf51fcf22fcdf6d7971e6da1e00bce1c821246fb92b930ebe7a6e849bda0768f06d8a0a882097f2f6348fe82e2b9ad27a0ca0090a2e51021ee4027b0c5e2928a379d8a36cd714f7290021b2fcd61cc54f1c926c609419a9d204cb66dc88e30653a207ca8b74595cc82f2f7d8eb68d74c6c158702ed1a6c24624cb3f897da21bb0fd482940c6bc560829282ff6977a891b62d346e6400a8b543b282ff6925534456f4850016199247914de8d54ca1ce890fa76232d96ce25d1f4b197a2f4c5e3ea56a3e153090906bb14df533165fffcedd97e5bb7939a59676cc4cce831e90b0c7bbb55f928f85da162e67bb587de32f0d6a429acb5a3fdd9cacacc9f7171399c10b321e8f8a60927f23e67608ada6cf725ff674c710e44e709fc1bc530ef7d0569fb12cfdd87d7f1334cfacf83497635ae0dd3781d92a51a849af6b47d85666c0072d9241d0c24833e3ad18806f0c6e2712e0cb06c00dca151ce9792493ed453760629790066245f17df01203158a60dda52ab8da1e21bce43263d7b9d25ce2c288b3a8224d93396e6aa77a9b4ca2d9aab28d838b45a63cb74ef7e0e8e9e4a0e645ec30998091c06183f24d132262f03a4aba7092669816089a5796368153748264b4374db1b410381bebde8376c723b39abd0a14779082f9ec5df5466aec92a14a61a9928487959399cb2409d6027425fa6fedc10898ae935794ec4d44681f1c730e4077f2afe59232cb0b28eefb4d9c861a105c9560b8b7182ac417acff345fcc76c3d50e5e5ebb25dd2c2ca625ba03fc33ee6790ee9bebe35f7306d23abe12439e24a6795104530db7ea958b389c1928be48f4b03d4c8532f604929f188dbe22afe853e75a7903b426346682b068946fda9a58d4adb2d1cd0909ba234bdc15d9020cb9f88102debfbb2596e2773e797d55404f00ac75d9ede4ef675a375cc8bc33a9702d65871417fd43dcfd561ab01d693579b6f3f208a549d19c49e44d5dab2df0fc45931e77ce8dc6e126636cc232c1ccaddf7a845545bf8ed59ace5f3089a942a9e89a9dd030c65243307552869e1a48ba76ca839beab4c22d93bb6c05f86e924678080e12619244e0ed92ae67fb14ace537a3165911655d5424279e241567da10bb6ca592a6c0a69fdf526672db1f2c5c282f070c88e756a52fec1919d870e9c815c8ef408f53f32bf7aa57510f10e6726bb3caa3afaa182456b27bf8de5180819f1b85e78a779e0ab21e5f5f71a01ed4a4f06e26d3f34c8506087b3d440bbec263bb4f53838b3c248c4eacc8a580c13aafae462372c3803cc876411c36f0a7c5d3f267ae0d40ea9730a5b229d7f19338360a860bf2aa0a10d1dc339b7926ad0ebd417ed18778f86a4b87b5c508a965e27031c34bf1308ce8c5da7ec95c1271a04671e614804b98c06a894e6276a2104c7b2bb9284c9e1fceac0f46f87ae3e8119d5aec66c488538875ad46174ce321feb30b535b784fb56eb544ac0b98687dcd2e2bf6ea176ea739ace974b33e094df00c3d3e1cc3ff362126587372e465c84b306035438cbe4477ca1a3f64c136771582b0f9cf1036a8d74ec05d13c0116ac6ac812ce62d1000566c29b5cc27604c06beb627b5b3863907fb3837a05d369015270c6d77f267703908031c381f3488fd5c1536b9700f12b5b38337615a14ecf12062f7b3db6cef65d7c3843baafd34843b33c88a81d911bf3ce5af0703602c2e3e81c0e7992938b0bf32292e05c375e9a94b27cf911f1c3c2f609e02a825cd90ecee3072960236e725b8c578d18f4187d3823c2f20b52b706a002012a3ffa659c7016dc7d3722239c5fd42f68bcdb6e76882a03ce723e51b9e6f9a20b8d80b23e81c4fb3bb414443240df343f810a926587f30bfc339e6c4162cdc0d18556090a980109ce2c161bb6a1ccf100ad2b7cc80c863398f29bf35ec6a076d2566f7db5b5220c12488cbae92c1a111d5a827064946c9802cee4b5dadd52b22330381bbeecd1bf1e7b9d9607ea6097b0e2186b3f31f002e77a4be87b4b106c39a3f5f5b5d409a743fcb31d49c1d7b2006708da91f7ae2cc5b540850297292fe9384e02e7d78633284c01ce7e1ea1856d2da70e86dbc7a8b594c179a4934600e04209ceac2b2841b812d579de4b7d94be42c955952ec775383f9c6b8a00c7a647eabd706736c20a1acea96f3819d2dc17819d0d6781ee392db817d170757c918171c164c0f9813fa22d04e709d5984d6362052238fb3d6938773964da66109317d88aa6380a2b7fd63d321213e16c1d3fb611c22454429a758075aed1bd0062712ec6b36c0313ba1b360fbd4708441e94347b0ee01c611b9cd7847270677e6320bbe08801b098724ec267119403abf579bc05cf19e99f6ac737354fe7eef3883c9b5cae130eff0a351ec5f85190f2d6f5dd079eeb411539ff22e7cb1092c63d4b4844076273a1921464d8c66084a62a3d7a60d8b76e2593bc485196b8e0c7507d0a3449b9ca92b8dc321110294ddc82a47e296e2c432c4cade6192affcd41219dd6af25d33c9f400e25cd8da7da941b23e4484dc02fa07aa9c642c139f81dec81b72a346b5d6e594805426adb5f54d77d2ed7bf7806240ca92b6fc0f7db05ec22cec12fc107be7aa00b0b72a202d772b49e6d8682ea5827189476e37de8e050ba9a8215f53fb0fe9d4b51c024738606d1c6c196be9a8ddfa2cfd8ba6b1a199515ec0321e24859830faadbae219376d4bc5ffa26f2e1abb01a31fc20a2d56a14e4849aa5b5087639a6f2c99894b0ead992750f0f0818ff5443ffe864fd53cac4c99cc6697283e6ba4d6dd005954c60973f218c8a0b9bc5f6e8d24b29d8c39491884c61815416cade9076cf74b43918e46bb5aab5780280feb1c0efb2dfdc636421324b5ca841464eaf0061fb17908a70e0adda28e76ab4b7000202a49bb55ae335161e8359797204c7a7e2967413bd2fe017ec01bc4670df1de238678a7fde2b67018102962a7c0e8cf3fb0e924b3ded000537e7ff56f7e4502025703603752301381e3ccf9e7b5cf66eb8d71c9956f5df0292a2a689527ff51d67ebacb54d00540d35d56e3e5ebfc73cd6daf8a55b79a771bbbdf9037b4c3dfc16bc5c8c33ff548b3d019c0bb7e9a31d07160453d2fd5dfcde47072eb4be0cebd0036c14439f28547aee38c61cadec777499eebeeb481e0596a0fc54d39b7b069f7dc132002b458d3ccdac05243590a90b94fadee65580ea3ce900239d1e7ca3b316734d395017225542998f783449b9eba167a60044588e8ffff7eb7048d7c97ac701917f7ea156356d1e8a62172b40e9d23af9cbc14e4d066701bd8d166f3f3090a4fdd568c5941097ffdda1a7346000498d85447c5914578d6a35d8c1cf0b4414907a798d608db14c5c9790b23c7de588472990de1504972b2e7504352aaa9cc95809519a3274947f9250d5f483d9fbe4270998949d70845822bff187da1c2ef06bf58ed8cd464d1b61a4d2c6d2a21dad53b8299d22456db46301a0f31d3a6d25c815e76ae708f50edc615f1a8cdf7cabb65bdd11d4ffb035afbaeab5e846c9b29903bec53cc491d3c34c20bca34f02910df894180b1fe88b2829b6fc77b842b5e18a521834858b36917e892c80ea622078e71cbc88fd2f4a9f3e2a9c616b1da6791cea20a119785c79c24519af6d349966426c493f0535538689fd4c96008006fe581007fd04063ad3209410e538bc4afd804a6a322cd6217fb1876ecb37e28964f01f15bbe300cc25b085130a3c14da6c8778d07ab8fe0673d0d934d82316dc492b50a21a28600e490e4951e8352ad795e5a115e5e50a0cd0757e69796707558226971101bdb6d1fac961fabe8dbe68d77d1127017a9a6c31b61c2e2ca165087d65882ed3c851186841c012c69bfea93f2006cd82d4e77246224ddf2828fa496284d2bae5b844bd6f517220668bf5219102d32c7b7912ec7d41c4fadf1280219122ac7210a4b44d89f2605463554242f18265599adeb2665c14792c43fed804dbdd34caaa1214752ce8acd0e96aa53ad523151a76d07036aa61a0e86b7c7546067c6e3cf565ee7332acb1c7b0cf677133ad795008fea3d8ad0151cc782140d1cf43614d7e09ab287271f31f13267dd65f38543475e5b20b467463068ca2eb0e7073d8d7da450e2bc401f7e376743f23e2bfdb089415e111457738bf0c06c5deea9a26ad6f53ccf3acbb208a715fb76a0df68467248ee2856d90f846bad3dda49fe0a7ea05359a4b025e6f825f1ec1e4ef18a626cb299c28cf3e0a6e3a907752d3c29a1b1fe1ccc6206292b13699d56d14a0063715e86cd4de5560fe03d9cec888e3f281c3556e8bcff5bdaa4d1d0fd7dca742c5e0f00017a45cd6cd8d6c3ac337372a6841215639a791d5003945aebe9d42d0ad8a96f934097dfd0bf1c7ffe2894a4ab81c4b3c963273c67f3dfab76ad437f483f333b04f28c99b2f3f2766e11bcd3a63f4e64091f16672a0a9c0b0035a50f578640753e4455ec323a19b6790c7a4556fe3db29f45e53ffb6d017216a28f79d2f94a03616038b53a875605a15298016a2e403103eaace529108d2bdf392c3a9858dca6b93cdc62b13e5d2f3c3620ed530963a9d80caeb12a99ac2ca0c97100aa2376d16434412064d32217e409a58e52b2dc0dd8c25a993f4056bfefb3f1dc6be520f0d1bd70330d0a58cbbe38b51e80849c01b90c0008c1dda0cee27c5ae1f83d39af9e1275048032cf30c946164c4993f2453ba09c35f3fa5d8b35501b9968732d80d71edb0e18b2eddfae4544cd27c583868ac1fae11f7e9b9281b402e556f379f2868ca731d461113a23f44b00f82d5e16f192775c5aab0ba43056e1c7bbf0abd588435b3465429d627b11be2551f38aeacf3e7ef9b847b524356e27f26d72c44c54832e6c9dc606892e19f1e72a2b7c7f3c3ceff691fccda7b643d7fc5bc9b5c99aa094eb3ad5f310db88c17ce6db2d9e997b4dccea1d77cb8d04121da4b3ac551d5309608fc4cf0f8f08f6578031f02e3af1d4c7610d6bf455ae274c4c390872625c80737bbd088da042c5597fb7b607ba69eaf95f69861451e266bf50ae1b01f104f10746e41de989b848e3c4c7b57f33547c81f959f9110bd220fadeeccd81fe3086399a29c7975999f8e7cf8cc8391731f6bac0019f23026e7fc6883753d8fcd8c89a4e9b5997845201e82e359220779b8e3d9852663812ccc74aff5c951a9351c8e10faddc4b0a8724aeedfafe1a26383c35808c169d408d59a4de4a1f77bfed094c288c00d0a9698fce3d71882188c558d83198023c013622f90a0873a4d7b58f6f7ecac156f75bc691c9dd813e792da9cc0d8aec7fefaf7ab0a6f4db8dc5003c828aa5638ed2ca274d79715853b7eca4311fd8a7c0b208d7721d5969d7cb86029ced34a42664bc8f602934a93faa19fa51f4eae180fcb0899bf558c867150472d474f56bb777b294eedfc0e0b4a65967961c142630d3ea6c534f78ca6f5f01467af8b50166f48d29c485ce83e98114ad67fa6aa96d06472418e09ddd69c26b42c7bce7d600cab8eb299eda2343e66ac5ee73ce337c778a2cd178d062e60aa739ea87fa3c362fe468dbcedbfb6650923e4b6a24d49e9ef9706b3be8df2683409a5af2181b2db0683e690ce46c6ee8887878ed778ed56e16a4e71bbaeddf42dd2839f37702020412982030340f363083844442a01bd21059e872071938e78f5d522ddd96e73ecc68ea6c1238aed0809aa7daa19021966c44568a14f326a12b4f7df1ed33285b6160aa1fe9f8b093e169622a3ab8fad0bcba02cb31f8dfcc53b1cd168d5d480a6c22a8f52b61843390860d7ddb36f25aa8e89618f5e19ac7cac8d637c9168543e2b7378d0e2b141cec8b5e5c90b575c5c82abdc4420dd5996809f30ef87bb4c5498fa9fb5a8ce1db703a67d8196a6ba9ae9bc78d3761ff0913241d2037ab38f3efb482da658ac3ec26b6604091ddf8e160859a0cbe805ea34079b56203977d5a967c9b9b60eeac148a8617c05f69c5bac12d182e51b85a589c128afcb13f4cdb46b07b4240c129fc7bccd898c820a0aa75a5957aa2d5ca910621f8a902fefeb80783d923e7c9f4662eb3a995be13023ea696343cf5ff8e7d300c15b6cd3f499d1b5f77377c1b1c0ed4c3fc0b43c3127732712158792696b650927b8385f90cd1bb4f2dc78c0bf3932969921a544cdc4ca551e19f22b94ac8872ca9101833c9b94a8899d7221191dbb566d1871d24d5e976cc0cc93287ac98081a95702263bbc3cbd7298cc4007dd95c664b53c327aa14a361881c5f9a0e484ccbd53be965c87b1e25491479f5cfd9be177fbb93956aed825c5ce19ec12ca115513f82a57679f71701c3e4db4a283ab73dc5e7cd9e8375f46bf874fa2ff512cd198047ac7b4158805d71ca8219e71f5e5c7361178075457775296134489fadfcd9163166a772a44f21cc2679d0dfc2156897199fc42a18363588f3d62c1560747cc49b7c854d73f68c1c0d43669d447fc714aa9d470a0cbc8d1d6e58e37004a84a7f442b51f85345ff04c76ba007988d2e92c00e97695a7b46c7ea15a6422654c76048169a8263587a8e72d356c3290247383c343e1217687d054273d4492d687488cfb10112044c4304f31dbbf33d2a27ee0ef2710694b67d57b5d006b176ead0719301e3584e648220b9e432406cfc02323adc71d70d321b712b500be0c4ddf535e94f4bac8f83c990b25d2fa1a487a138ec75915e2f4c81f1976aec5e1160e6847caaa66d8af7657214fe1b006b01b2a35351c51c9bb23babd4edb5ad32b53f480144d003025d5445daea67300e277f90e7808cb23b2c31c7e7eb419f8db18a5150d21001172a8c9010e2b427d54afc25600a0f6d45c9fa338c95c82244a294d0bf06964cf24d4003b92047a1abc5b121254584695ddc77c9ce4328c2664aefa30e8fab3da99582791dcab10d89cc5a5548a769d4410f900f19e3e3e0cd5659d04ebf36bac490f224ca87caaf460627512611aa9e64b08f15247050d70fb232c760ab158f11f30759db2d6e4b759d552031850379c910c887c84ddf87c0f398ccf4ac976fb3672fbd164d08339680cc25886a4f33d7e3ad914749a7f5caf1d68fdcbfc781836ad026aca9a685428cc0dccca4ac8adfd2360514922df078021601758f768bf43ba112abc9b92abe7151262762bba94375593d49e0430fc9a375fe1463601e92b71557f8b4aaa6d5afd0f2d6ef33bce09d024f102132516da8a24b1e55d807c0d7851acfa3e86e1592965ff008ead66e9abc4daf2d8bf716eead26cd9c702ddef9f029fea69050927c2f8bd1c858c129d55a3445d3211f5ea109b471701eca9eed414ccc24646093ed4c79c14eeaf92d379580792f8bec10cfd6cb7399af3c8c1b70547b2d8f683ce22dbd12ee7b341f6617f57b5a27137636c5ddf87b2cdaef8def6c5e5a43f1c1800640092dcb7f596c38ad0cfe6e8b05b2de85fbd44b78194918f02a8948d4157e3f1ee2db2fb16758ede76ca21b389b52827e083566338a044a482396af9fb246b5cec366ef1a7ab79418ea5003b4b29ac855035dc62827238a93e6b7088bf164f340673e2e58580123821c03e222c07bd48698994b6f1a9fd895b002848824100c96378d38d2f2bfcd12dde0eeac52b92594e7448f1652ef060a98e38222aaf8c8fa3d9f642b5afea67eb909a5399d8596bb20cd6f90662ba1b4a45e851503e5029d34945355b64448835818b70927b0016265c98699a13fdcc37c896ad1b480b8c9d3b3014d55c71d24b4669eb1a5c94736e6c25df42963641c86e0c0a93bacf2aaede10beb878025773512e83c46a12eceb5c6f223b32cf258af6006d427b8ddf94670f4d63ecb472912fcc138db5bc09449fb63b17f8c1a31bdb5a61f5dfcade59b6640b98b795b267c1a8caa5cf2259ae2d5cd315adf1062f62afc82d00f02868527409aa6379b42bc5f0065e13946481e9a4584b7fb66a48e8fdc94f27de0db7440229300ae09498c042082ef00e18bb76fc231f0c222e42d48c651b5d3ff1885812a2ad91bfbd5529cbd142d3b88299d9cd0ffff937345fa09ae72aa05bf2fb886c222cc208d8cc40577ea0e2c0bab1a2a61a95320207409fbb861ab6b496272b215f91f37fdda6fefd239e832eaabb62cb159435f8d6a828cb897798a4daa83aa6671ca829435ea44fd97183121e4d320895c915dfb0a7522ca9e51bfd33b7da46b38265d4cada0ac099256ed274683bfc77e8b7dddb6a3623a5520c48722f428c25b280f2bf95e751788cc543400c23ffb57b9c8427cfde2a6fd74f103f85632ec423784900a98071e8058f352bae0bdd1303aa10fd2ae380a2525c45d630cb3ed0cd8bbf5effdc15ce82c9c41154483a272b4f92af26c50831717b0918eafb780a6bee1018d265e71b6f7f54f537db82af09c411d4ba81208a314718a80ad6bbe317fb0c137d3984075250076245449275cca1cc85aa5452668194ce6ed7476951d7e44bfc85c34d4e3ee621320b1a3262cf6d538f3b960722186b08f5c863aaa5f1735a2900a0cdfd4afb63c89a8784cb0313e1e450eafe46c4b15ae41af28ca3ecf31edf5cea9912bf58caac2f75a6358cc78ea3849cf2a645e8c2e4a138c5ebf7cb7aa3bbb345b0adbc9542da440f7d1b8a8b73d22976b52266526ddd4d88d05bdb2b1d96d4f27ad073b83d34e022fd6f432ffef38fc2f4bf87b7cb44557c28ac6a215c8933116fce96b74c16621d6c32cd3028adce70ae3d65448f4a9d76273630be06b77fe17f2be84794a3ae239d51636124b47c01d2004a06ac1cb4bf9b43a2be93bafaddbe6803ae577f84685f5f72b0f7cb26ecb8f92bb1f00986fffc379dbee5445bca05428209449f42a8ee963f07e5181da1de67de91ccc054ab19c1fc19841598f8e717000209e22ad61971ff4638f143097072f7a613cadf014f48a69364dd157f8af52be745443daa6464b26740a7cd06bb0935249ec2e56d2458f685bc4d57e78e9d0b5860889e139256b8de77d9e5052a42c495dd2d6699839bfed9904b15b2679cbcadf52563d8bdb3c8dd861905b996e01f05d03f871468fdb8a821e6f33060e1068e42cbff00e954f7f4e5dce616859a0ac42847a0813c077036f33f68978b3333d9c4372109e541bf35b2234c13666868d4dee38d9c38cd1879ff0b4721aa0c6a532e3889a4424666f0b21d0d904d3043e00be6a9f47318969c685fb018ed650c131a1a02d52cacba130e11dd44ca14ba74502e35d57b0a3cf536058be82c0c5aa33217041c51ffd0da0c112278418da1542b4405d7c203add0f38af48f2001abda7edcf9d69da6b1c00f97c511027695f9715d8777a0595ee03dd1f51291125712998730bc70c79826ece3c6292a0853506a014a8a479cc6edf94fd8ca177dc2e72e81eabd3fa98dc6a979842d829e66fe2483e6ff83ccefec70809b5ef6fa65b4ee1fca10d0a2564a34ee13ac7bf0c6e806a2def00f8141351d4d16d0d20b617608bdf482eb03e311de80d7daa5641eac5734eb4d380410d4df118d723c2f1709b3fb40029897c01b9aa84bc2f1dc624658dfee2b943f3fd11985ada6e0c928ab133f96370d93fbe32422ef7a9833b2d7679bb77a3c1939c618050f42efae4469b84b2b640282b7e6102668ea8f45962e838eca65bae2d1424beaa1a3f7d25504e44d26a8eeb2727a8a3f2446eba5e959a6fe239e247c2fc1a483cf887b10e8f4111914e9ffa870aeaec6b365bda9ec1a940172597e06d76fe90d569598fa91a62f838c655d2b127df93ae5f608927e40ea2ba27b811c12793f913ff1d4e3e6315d89929e422471b85cb8104fa1ffd841a05fda8c22f5a3d939ba38ceaaaf8f928891c656872a9c368af98e54078a98a566afeb6797846094175aba4e94d4d25c125e170947b6a00e6a7cd3b19befb5b37f44c24ce1eba1a752d081cb7a075c1c209f67b0bd128b2acd01eab3405d9a4f521ef73054c223e78434a983b15667826072d8b5066e100ff97e3b3049d4200f7d441f5f46d7163b265d12b07454a6e0d355a56a2e1d0d955fe836b8880412faf0c67c8466149a6a96379063391d3bca243bba20e454edae457c4765fd48b8a69f4beab27775ce7d20ee41952ae30e5b50a298d9ee97008c29b4cd05ce8c80b612b308fcb61a906d9770c79c4d872bafbc232eb80768797a0ecb030640423335f9053280f80b28009aca90468bc9a8b307b094abff2a7af19afe78335088e4520a034a9a0e889ba977ba66b0ce19a3104df54e5b6fc450e8a15f3360006fac25dcdb355255e6b71c2a0b91bed1757827f2d590997a6d38a4e0fd87468fe0e3d6c4e063014f72585918f5a5db9a3c6869b3485d13da4067a1713b40d4786d109b112886ba469c2fe8685a660b52229ec38128b4e65ec273fc8bc2ea16d494753d82a3bebb75401dbd0e5e5ddc2ab342e791708e8fd1360bfe3bd395423db6f66dbc1850ea372a30e2662a2831b30b92cfc600023aa337e605318ad40da3d47b887ed00afeeae0782d8e7b07fbadeab6d4af43a57e59cf91bd4140ed01391b17e43caca98438028c7a64e0ac48fda0651322921a5d4c9269041743421e1414a1ae50b6d384e3f1fea7ae4f6d8ed0f353342b5217f31735f7b65567750ff481fa69fe1133ac284ff886ac6c2eceaab3c869ca31c79ce9835e035f863fd10ed0797b9a68546a563d113b8df3d4b6340fc0f395529c1b37a5c197796d8b2271e3bbf416206d775491d815eb136048c059871664961ce6a06a8f1ec35e59772eb29b793728cd89cfaa80c1261de61e8187a1fe1c6de0eb4a770f1cbcb47f04110d8b3247fbd9d12cf35629c3e2e83213503dca790e208df97bc4493c9a53430420c1f98193323462a254f1d9e88fbb27fb01174a852abe2e01076d0184ef39b622a9fbb91a65383b98f3ab87c24529cad098ad2d7e639112d0276244d6ed53059e8ee7145006381f6c6a8e268ae6476dad419944954f066a0e6c8aabca916f23e763f3f44dec4e865b215535d0282af4845ec6d60a46cd020b1d8d43dab4f22df5a672a960dae07041b3b9f032b3dffc82429041b02af5667275b1692d149e148308a5734e953663a538063f727b3626e1c8d9f4729a136398b0c5466e5e45cde21ba4797646fe21c9a193fd0a6aa99be6dbd70c4db72a87e221f2e39a371c6ba2867d49a4ecc378ecb5d0f97786aaccf7bc19005d4d2dac005af4b8a4efcdc082a13a1a3c304e6ea037033a1480d5c5b084c6c16ab027e69b0102a07b881cd59d581d0bfdd5747cf0f0f7cd80a3727b914aef76c5a295371660a9364075cc9b81a8c9612eef9b8134eb43135cddf49f64300785455f09b84bbdead443fb36031407995c7a63b3e5c8c1d9a4452a15ac0b2aa8601865430b8934582ea9518769cc360d5252605da2460db3315bda48a48171490d354ce36c69919602cb81af1c4c13b03c13d324a09b27ce804dd64c94942f6162680c33493384485a9229c565c741f2979f3f5bf6e8bfdb4bfeeae780658d9bf44fe1358f00648d710cbe57e90a797bbef8e54f20ea129732bf56fa6a70ddd2675a61ad7c0366030ca0b6cac8c5294db440a7889b0460e2926f767420928d66ab91c79e1180961a0200d054ebcf7a0db149a24b5e02553cebcb9e981f3ac36a8c27e42144227d5aff51732bacbffee934d4e7e6147d7981a7265bbd0afeccbff55f09ecef54e9441a42cc71ccaf75bccc603e4ba4933789944a0c0b6881a8402d9b4dc09a7845725050e4948275d44b4e0db11bc1d32d801467b2950d245cbf205b6705eab896bf46f73088239daacf1af522545a8ee8c74c9465eb40cbf911f7552dd5fe9c8c02024e7c19a6f2d43fdd2d80f10046e1048f44cf8489d35ea43da39ee14159b9f2299c646a71fb1b74647a12f70a70315879773ea862129cfff0e1ce8dc3be5940600aa8c002f615a428a9014bd78ff80f887f39feebe0ea3b26feed69867f772efe396f1321774f1ccedd4c3ac5c0f6cd9a5acdf71922574ae3fd9fba59934d8581f705ea8fe25c34c0c0220a929f66f9d4b526cc4a827b5385cfca23f088b88c230519de11656fdc63b22c391034a296c6c06c2e9391d12114cb0528de0c6df8e2a0044ff035c24ae27cea02fb2f0904713bdcefc0998c069919f3ac1869661e54b3b35b418d91a4db21ce31d2e6e8cab63b642b2252464f4ba9a3979ed651833e8c052e25efcb0d722e3bf8c30268d21ff604f440672136a8214fb7d5c9db4ca2f69b6dd8e3e79e700517d56bac75cea716f08742b180865364a5f953d8b10d86e865f545a200fa2c42e143b2a6bf4eaef1314bb373130a5284f8dd6119570299bd035f92ece5e097041d9701065e995fde364e1c134c212553d795853bfeeeaa5c9ee0b9eca1a4ca7469dc497e39ba3a41c1650b93cba04842ff95041aad826db330387bce64691b0807ed0ebaf1367004986ded71d0fd4193725142b2a9806363713f6b5e52a04f55e3dc4676b83f08fedaeecb32d3a8c214b549ed2c03c36c0b81f7c8183f7f9bdbd768841b7efe02e929f71c7f94e8d47abe6f37800cc36cb3fb6acf8d5bc37b9ee579ed18e3c041f647a77b370e994909a40d046a361fa65920923a4a8b01a9f14edf7f4ee0356cbc57aef3163142b01a7ec74563d1785d2c9ddd6905be5ffedc1f52f5ea7d43b550850250042082a87c98010e72501f4fc185ef7e6fb0fddf3e7451fde8bfe8db6e84882422894476f70e450ad909cd09a7fb79bfb1b06927c2760bdb89b0222b6cfb1f79ed37ad87d60159fa06fcd063538bd04128c2e7edf81d113aaa83c80d4334aa91ddcb6607734fbf1c678a6072e8f8f6059fd2ed0bddb68c7f6fa6f1366ac110b353a22f92d91f91846167f99b7bf336baeda3970864bb9d198a7a7ac8b3d17bf7ccd2e84f6eef11cc8e47e9539a3d8b5946f590f7eed9b32e7ed3aaeef4d8e966adcd317abc596ce9e397cf0645334b6944e92f56b1f40bd5f1cb17cff3e8b6452f1b99216cb7cd0b0db365f498f76dc3ce6d56cb9dc558053f520febf8c50b8eafc99ccc393eb30e9ce186fbb475f11463a74f5fe84724b76f745b3df44844fb49ab6cfcd2fd8b5536d69b0cfc11511d3947a6a61cab6cec19f6e5cb970a66a3dcb67d760f3811c5b42c7ec1b2460830fb1bbe8298f2a4cbded9f0f504ca0ec209104b9436b7e39f6cda02a45dc2862f21b66c219e3c29b385a0b2eff6be546bb2fb0cfbe8e973ce392709544cec3a21a84f0cce25ae03c23aa0a8e94d3e1dcbde6f46f1cc1ff026a5c201e110fae071d905b9fa4b45fd7dea6fb531fd8da0dd7bef90843beed5feb8430f69622290fba85dfec342f88e10e8120ffd8de00504fdadb6c0d1dfb3c078300cd3181631ed2919ff5d6bd58e6767c11e773c6e3a9c367f8e0f99c78e61d9c5309c7bd951f1d87cccaebf1142d0df8a87fe0e825655a9b17b515ef5e168a2a93fba497dba16670e1b538755da46ec272fcbea13ad627b47865bc3300cd31f356a618f337e451b7bd4d4ccfebc13c921d461d858a6747bcf4ec34420f31925b33f2f7e72a33e3fbab43f2fbbf77bfdd1a5ed5dfb8e4ebe39c5dfc7693591dc570f614769c8835d638d354ad1b363fd76b07bf1d8519f39e64fd3976e2d63d877a0bc87e984a07eefe5f99f79934f4f659aebc8d95123ec2f4659ed2f46e97294093f9be2f97876c5ff46d81f0fb7d53c7642eca9be6684d5ea4f79fa4b613cf56777e576c6a8899e1eda3eba63d488648d56d96731cad4a9238cb05afda954947aed981112b6f7cf0809dfcece3ea2477dca108a105f3b21de87b0ee4ebb22dcf61ea3ccbaa3c5ae316a1815138178faeb34151347c04ec5d025b8356ddba8181eac949a7246fd1921c10809247c4648d0befbb83722896d9b46c5449cf8352ef401c2f503977d632724eb410267af1fb2740e308a6243d8e3db6eb767871d91edd97f24cc302c9bdf32067d5a20a25b3b1c72efe550d027ee785786cd7967add9b51d90ed88c84ab1990df98c8cd8f6f266595c31e0985dcc4ed811c12284f671ffc86df537826d37fd056dfb19c4c6f69178667f46516cab2114613e434111a4dde2475e582a654b2756b68cd4b5a78635b8901e814b2ed726a2d9d82725e236f6bab4690b474ed8128a204f846dd90206c6a60fe1c8093b42111c0085500d7570a30c40d0f3829dcc87303d94f1c18cbd4c7aa6ac7258b0a4872636ddf0d503191cdcf9103e1e3b0c072de6e0ac477cd34a040ba6919191110b17d50bb15a18a65b55cb2e745fb1d30be163df94b6773a2e6dba456d76a1eef4b57ebb10cdeb2f159bdf6b3a7a993a530875ec583bfe09eb1666752bd3add6764a7f2f949dbe7b727a774fc72dde69740b3bcd6d74a4b2535fa35b34bae5e956b7a5fb76ab57682509d9df67ba95ddfb49c7272d7a936e799b6eb5b6a05692903da75b57b77e34d62c7c4697a6e39225a525a91d2f54a20bdb2d597c52abb6d8ea94974ce32d872c4c5ba5aa5994c6284a2b83d1b6b849ca7a811f8ff4327566cc474988a61749646eba6fb20c27b06007745fe66609ccfc818414c2552a1d97e2d28d5eeae8ee4eb946db5d4b86c84a92a7b932a3a46e41e8c7b9d0007e7388731e03d06f511db71c833b7ab7b987f29ee3de7348d510efa87b47fd9356a04e1d76403abe9a7f310fd5e70fdfe424cae342ff4d520b42aadffc9a87a6bae846b76eaec2a9591e3a72c299333fd8aa2136df6ef3edfb4648dc717b8e6fcfa1bfd3f16df29158b3da3f5276993fbc6c59410b0b53670add1ce75269fec0d1adb8242e993af3d04bce3fb8f445174b00ffe2923de36f32ce6316ba398e6ee5df7c476e19b1765c76aa14707e731570f42d109fa8f4cf54e5a5a24865eacce32829452af937c7d9517f3074a06ea31dbb21d502996647366241fd79d7749cdd4f58bf30f7b7da437208b6bb0b20c72d53673e0039e66400e47c9d3f2c7f43514b4d34979a34692297787ce27ceef8bc44924be68f9bcf7b1742564bf524211c4e9d88935b384f12c2f377e4d68e2709e1bfc94837d7dfdc15e9664746bab92a23dde824aa8f9cb0e9af3f49f5d03d54daf3d662dce5217bf92bc3092c9cf338fc8e674838ba088443d6cd55dfb09b0d5f3db8f68d9cdc327f2409e1389fbf93838710f6a0652b4dd9f3128bccc27dfe2882383c0e79363cc4d178eacc7feacc0ff3d03b3f38ff76ca80f346074d9d1d2f8261c7af5d3b7ef39bf35065f8d9a500593bba21341f3961df879065388105c2a56e41eefa24207c87def4c73d0908bfd190d32f60fb6a2820e812dc5c5ab153d3df0bd8de52a1a89476ec824029019c3a124a0976bc46259ce3e8d4d471296d552e812a9760c76fec827668db4100b276683c759436ec80826e9e6f744abaf63419e590374274f8aedbb9e557b2cb8efbeef00977b6842f162cd9f2d583971db702b6a41b67cef06281ebd564cb183d43707935a1f26a72f46aa2f45a0199d70aa2bc56d06405482f159469f252c1929e215e2a0083553a3187a2f4f0862d9b9e526a002c66d005958207276ac073c3170f5578b8814919fa23c5809e1830ddf0c5648d1d0f9988d9b5d65a691a2f263c802f263cd8338e1c9e9e1ea44bda2c6253c21e248cb55082310d68c0262b47989b8204731bbe983c9101a3367c3151d233830f768f0d3b80c9d9c1cb9c29026cf8da81c9ae1bbe76c0617fa98984933ad2984e894b03a348af0de90b7c7558f160dbe0a026212971b2c2e322536348a909d214314082019627e060c29584431134280184452a82bb392031d9906cd0c2c053e615717877c3970e34b86430e0a70171783c4e660fb829f38a30f094c9e3845ae171b1298558363da574062466b0b229cd1bbe742862e7d8f0a5c3922d9a938c041e05f424c1da86af2558f4581560c3570e5f72f0627f2b1fe4c0450c396821c3b6e12b871b5058719e84f684bd4b789c3031b9c09381c789d403080209183134f9628b0a525862cc0f57c470041b41b0e8a16b531a94d01a789cd81ca2ec2df5882dd83e85930313ec0c0f150d26b4cb0ef3a701130b1a64ea5f32831a7e1a40975ca921954a91b063f586af252c58a2647f3b5b6c9425486658e2eac6cce02489141cd248820318243774d901d8f085830c70a8527b98c0c0ad33f284c13a48f1448a5d8f8a51723c50f0b713d441abe1d3cebc66f1ce4401099b470cfe764a38057a2002c9b52bbcc1aeb87e0bd875cbc636266365034208eb0f862f1c86d8f119a6b55be90382d9da3f23104cec1e9061bf9f5193fd6dda0804b3636b6b1aa6c9ad05c93418b63d1277a68bf4b8007fa63d29bd01968230292003692803030f47e4d0431563ba2803bb403a628d28beb8614c1032d82e0e2fa84a969494c1b5d65aeb0d4a52fe88c00d5aa40edc9fc52970d18841936001fbeaac662677cef9a855438c6c9448591cedf97ff5726655ddf1fb304d07d9545607d9b62ad341b6adc274906dcfbd7f663d32a324323535b251ead0d3464f1bb8470c9697b587663b0bd08a46711a610173c58b0d4f44381ebeae64b9b09e38b942ddd5a50f58a1bc9e707d5c98797c83808117a80da90daa2cc98ed1731ace0eb7a528d590b8e5e9c572dc477bde66886d23abec9aebf8bcb4b2b3d3289b3b86b4b9e3259aecec2a22fa33bd3e7b82ccaee25ae2061b7585c99cb9de8a3b22f319eadf69dbc75f8e3bc52e48087484606fef88d45cc74d5d8c02d37fddbbc8799d90ae8b5d903984eb88d8bcbb4962d9d0e9f3f1c342f06b0d50cddceee5a17ab851a710eca1fc9d2070277c384207624041c513d6ed86d89ddd6bea82cc9bf24fda26dfa86152db9ff6f9d90919c1906d047ba3c79dfeb45d4fe750fd08f6cd5fbda4dbbb5f9b9c3acd39dee5a11cdae958c6ba20f4f8b50b426fab8a88e93a6c478152a743d8874cef6ef3477f6f57243bbdfcea103d3c0da65529ad1a6273d36d6ee2bafc61e73c791dd986fee635637a887b7dcd4df5524e470a90b5c348869b2781f0005ffd7d64fdcc9b1c36b9260f71bf9a46e3ac22c2dd74ee26ad7111060718196daf43c1280a585bbb6843ed34c3b0b3d1deb67b228c3b11b68f64dbdec4156d7b2484ad1d5e18b6ddb408529b28b5b57aafc36285cb9edaea943cd9009f7bd2c09b0c3c25100ce14b092b5bd21d281165c3571256ec1f1bbe9260b2af097ef58281932d81f8913af23d66e00f5ab182f421f42d6f04fa4c2a66f607c7d8f1890be5ef0ffb65054caac796ef3183b3820567c39795275652e0c4999c0d5f4e74d163c397134890b0e1ab09389ae081091bbe9a30a2091c6acc88f1bc705ca8f0ac1051a30313496ed2d8f1526df8aa62e555e5c98e1e3238fea330a523870daa86e6c69b4e5cc8a136ddb083281f17ee6d21b765191c3f87a6965b69bf80753e6e2ced23d97e81a633ddd9bd806de8d342f4b25fe03ce8d34274ed2327ec78972bc62f6e5402c7dbd7125aea8f404a72b2afe434952d3fee3ad994257f40d6115e7c1645af0f4734a147d1ee882d3b27cee94dbd4375b851087cdab13189893433c7a056542bd894ec899db2442bb38d71392953b670b7072db1ab52a8782c7801931e3b0b60ba4d708d81ccc93af192d205d268483756c91104e77b98c0df8e14242398e4f45081bb0d5f529c4869b2bf1c586bd502633aa34b1886d51a065231b5d2a59a651c605a50060c982f5ebe8001732557370b35d4f0fbad95d4cbaa8bc4be836171d39c4d51d45622f2dc85c873da68dbdffbaafd66a38d11c9adb576c5ea95bbea61b167d9b45693b64a39b3634455e93910e7b01edda8c4916f79046fa971209028648c388272c6bea64af7b7b33f23246cfa09b32cdbb6d6ccce7b739838829c9a8a8923dc9a21092a5d922b496b95dc670487521eb7590c4906a074a9c78e006db7473470249e39fc8276fdcc99fe22a5ff901d3be66d1008860b469b522dbb5e4564d6287bfbb4c943db6d4e03c5caaeb71957736ab46fa0081c97bff9d122759e1a4d94d1b4d9afcdd9613724a397c676413852fe489c2c83ea49224beb84d8947dea36dfd0ed37c37d9f09e98ad0edf636fb7df633e18eabb48e3bc4ae12624f5379c8e67073b985d473dce4cf3b7d2a17a9b7fac3eff4b715c1a7bafbcf1cb2bde639b210b8b7abb24d1eea5ea33b9efc190d79b2d3926655bc9452ce5ecafdd2ffc86b2fcd2d5c8ebbb6877521daab711b1421bba68f120a981daf33b9fa995f2b08fcfe91b53b4282a31850e0da10ae20acf99ba7a7540fc14f7da48585764af74682f481998e3baf5e6b338ed3e4b79fb63c5fe79472a3fb9457f79a7286edae6e9c2314616eefae7540dd33dd9b26924462fddea637e5ee32952b538e562c67f22c0cf026334d769a6ca58ed6719799fcd6b8d38ec809797bec80b48d6eba51bab10c313486c0f055849657114a452051c4930b5cf8e39a20953e22bd7c5cade68fa7567bd61d2d1721bdf66a24d8447367df3290a66d9e9b4a13b8c343bb2da79f9259f03f408115b2dd3d4de4643d4ee00d506085254ccc96d4ce523859965dc15f2a85b342b6810f44207167678188045974255d1891307c11a1b4e16b082b3602367c0dd1c3ae96daaea70a8c7d6251733d587090f42144810824eefa1536f001e943be3e6219a2c9a69f7772a1bcf630876ec9adb05d0b23bc29c37de4cedb016d50aeea36bd9333cf394ddd3dd39198a5cb0a902b4aa35010bba7e6e9823ffaa57e44a4253d5a30fcc7f36fb54d9de99aa2c9f4abf564c15fd42e71b65267a6e4ead69b32f72d5722bae9f5916be39cb05ced9e18e07938b5d01298a749942d1f9b48a8a589953eece7d4b21f0634008520e9c303e3c90e68bb5aa54a01bfe62a9c4ef31a8de4741a6db4af67e2de19751a95ad699ae6e40ac2a93fabd42d7161ce15f2e5ec6b71a40fcfd6d7bcc709fcf5d8f5da7b481f34a7d3bc1ae1a957004d8622d46428754c5ee66cfeeee72da723dc35d35cf5a45403031a23d3e6128f02e23de4d99fd1923daf41c0b3bf06eca987b80f65b7f4e3f4c763f7e9f8ff50a7dc651eb9f20e734f167cbf7d47fae8608f0daf694f7f764f08a1fe38ce6af9eb11c4ea0c66da491a060f955bf440d1030511854a9462557a40fdcd8865945c7d3bd80b85152756d90207a70ab6d7926a1c25ac8aae782457979288ae7a7056833962c96799e460dd6030fdfcacc76615fcd9207272a44ee4d9f43d628c31c62a050e9aff9993677719255790cbb7b3b3b3b3b3336f2dc69afe5229ab75ddd914903a3576f4105efea0b6c70618e7cbe992eae19202836118771a77ad899ab24729e65029b992af5b46292f4d71ea60a9e974d33dede5f0e35338873ea947fd19c9c1357f39315bceda63961e3b14a1d66f4c6d8dcd0dd5df337dd601a52cd12402ed4d863b95e9d69173649b9cca359926c79bbc63a0ce74d34d9aa86eef98f71fa965fb9f698de44c3a33ddb4238263a507626c69948a558d6aea99a2878c153b73e68c965d4fb74e0601cfae7a886a2b5d20c8d943f4d8e1edb52db5fd47ae76d45c771fb3b8efb96ce56adbf4672407ee0c043990cb9c9501e6e46abaa0278a2cfbfc4f967fe839e8e3c28e212bec39753687e8650e92ab287d649ccc43f341a6fe61805cd51fb9aa158a5087401f0ffac45d663f433dfbd5b89c2e88d136511c303bfb7db9a0cbd64ebd7bab695e6d0764bd6ff17613614365eff6de965107f28a04c910599693ba08cd5e900c91b50dd14ebd6b47e59afc799fd103bc6bbf262eeb808ec43334fb7ccee937cefc759ff698ed8a64b7fa9b45b47745e8aff72be4a3dfce7545b2a36e334aab88dc7b50fb90ecdfd43471bee636595724fbc7fdd33e77848204e1013e7bec36379b5e933f1afd196da238e43d6e9c87ba6b8f365ede38ad71dc10487b9e72d0c7857871e700a3283a2173c36e08766dfb2ccb20f601268d8d367722792484edfde6a1ee70a78070e48afe87cde15119db7c31b3e186fb6ebaa37cec509a16df497c7b934d0774baebe83fd2c9136abd0f779f49bbf01b6da42694a76445aee86384cfa13e4ef0179d58d9f49ba67544628ce264d3c728914a9c12ab401fb829cc72a0d85cbb943972456b5724bed35f77d3bf4ebb60746317ff12c3c9fe12e58bd28edd6d277d74efba22f8567ff2f85c57c4f4abbf1c79930b465b62db154169dd909a1c95d0d3e4c8e407fc45177d0d67d95c1eb359a48f4e4bb4a2eb7e63361deb803a9389c644137197e9f66182e96d58f8a85c5122e943ea9cb7a04ff733757876f8088151a01f4f8ada15e88f2218725cc76d4e7568d75192b36c9ee3f4ab1c19ca126c88e43649bd92b23375fa3b3d6a2a7d9c7476ca61fc4efae84e8ff38bd3e54d0636fd6e1d1036619309c7cef4aeeb5272457f3b7fa020579c90f4f1753b64366514e48ade0707dc59b1298e198520d3efed804c8f510f759758aee817956c72496472b90ec8747d80c01fb4028520ae03925d9646e44dbfb0c3eff20b717797d98824327777533662baea48dcdda7c9f3b407a05c6171dfeb7540dce5ee95d2d4016d546af6715b3d542f619437754072cb49ee4063be625f2e82c15e9ab06997bda68fa02c413ec959dab3af56964c52111922927b62b5aa88442b89b816a33f61b31ef3bc8c712753907a6e484584dec33f5de621ac6b1ef20e379134416902cd3f2249bf797cec3419e92c1ad9328146b7a02ca1be457318a02ce174284bf05ea529cfd364c8bab003b2e7301ca43bd6dcabf79f69ca435eeeb28a083d777aee3f13ca95dc32cd6635abf5a3265977b81af6cce61a2569b7b9cdefe4501ba5e1136475993b96f4deae7b87fd4eac0886716b6b45b0b83b7cc21a56843a649d4e5966833d43e5c8ba9065938b50d771d809a1ae4317e568e1a21c6fd9bc26474ebab7c949daefbd272d6eed9e32779cb76730cbb20cc3eab525617676cc64b398b558d8dd5b45f6b75efdb900c396cfbad3d3696edbe974aa9a76a2dccddc49522835fa8b5b229d5563b46f77284da8d1ad24fc56d2e935efb05f4cf34b93e3c62649c3bd8bfbca6fdc6fde5e93214b9e48b6bed6feec89a4cc708316a3188cbb62698f5e622831d159f3472425848f463bca0eca15755d08a90415c1308fcdcf2e6563d8589876cdd7cb18843616fdccae8da58b603885b183227e70c41303d4ae79250abd972c5b9e040584d164cb1e32029f71f0d7833b0bdbc63408e06b1cb28160db33f26c193232391e30b258fa25c9b5a1865376388c9a4186c393bb658a3068e080d8a224611630b27859dad9862f2f66705fbcd8411730aa7c51e68b25139731fdb9a03f236be1b3ae48bca6bf4ddbffcc2cbb60f4000c8503d60ef7c76d99eda965b303c224d0503ddd34cb126b5fa83e37f8a4a076915bf0f8d490c3e353434e86e50b2edc95deefe37d0be9041677a95b4942f29e6e799742f152b73c8d75a7934416a7e5e335624597cab230756866fa0a5387feada4eedc3d6df2ae694fb782a60eed38dddad9e092b6a31db2b643566441d60bd827646d39cb5f1cf2c0fc9fd03576a60e2ec5d1e9a3c38ed538b93aa2892562d8d0e5072a8ec82d00030e36962811062b7b950103237696653e4a50ab1c14e652e73814cecb0b257bc786af2fa26cbbe1eb8b271cfd0f7f5937e6a22e059961744102909569cf640701c84232c309ac0cb2b42331ca461230627541479a6e250565d79ee9566465bfb10b3a8adf5441f12aa852481212c2744b86c8c27e35526461dfa8a45efdd4a1f45ac690851dc2ecf4b58b0064d95700b2e8ad0064d95f3a25964558a9f52a9258941d49a667388155841d09a6673881251f244fa5a42a7891a1e435060b7c7288e30636c2d0e06383cf0e5d80d1c5161e9c6d3270f6786b2db4b2a5ccdc2f0b3865b3ac1d835e76baf0c095e936bbc7a096ed311f991b04a96df5f7816d24b56536a74f0cf71e619c8b92824ec726134d515290e934ba65c4f2345481de5365d79eddfe9e72d144bdc8f45692d0e928ddaaf9e92d9ae3ebc8aa781d872c2faba256a560ba7715e675680d5951b67c565453a4e3de63a74a81e6a60bd1dca42327452d54f3a96d2ea2b9496bc7b204ca76ea6cdfe4932e966c49bba023ac8b92ec299b74cb8875bacd4127cf3379a6a0234fb792824c67c5a023fcd36f96599029d09b6e011d9fa679abb9d0b2e5b32e5c5c74d9b10352a5e07934f7302d64fad4ad9a9bb435229fa9826c0eb7508ea3ae23d3bc371af41348752f446fbad0bc771f17fe78b613762d3a1d759c8b741cff52fd6d3a7212779c8bf06db42c8bd9b3bc619ad0e9a82dfb96ed29a7a64ecdb491d426400ea4e4604b1abdd190b7d1454942f8285d847f0a3a4252a3559646ab322458abac56dd0b099db42ad3aaab53309d5e056fbe0b2a1baab46fb737e12d0b997eba965330fda45b49560b79c7bae5bd959d9541169689e8964ad748931807326099e223c36b8b2aaf2d8eaeddac766f013875b22d9571bfb700a7371d6424358d3229b934f14101521732db7689f2d2c20b8d8bd7acd8da6fb06831450756bc749085a339a73a69d8c31ed68478683841d05cf753f7014ea5c2b7008f83603a87750aaae35c4885a38578700741b3b04d1cd6346c24b5bbd48d8e7befbdf7de7b511a854271f73ccff33c1f14586bedabcb18af2e56acb5f1f6a585936d394ec5fd74958793b200ce41b080ea3c5499474a08e72008a9ce03e74220681c954ef17c20e575e1b227a5b1d639eb9c531e995bcecb493919f7e46c1c211ec639e5d428945055a9a4945209b1b8e53c47cfd139299c94424a2985b0d67c64ee2a2b9119446f79e9436aa9b53cd545e20522c618634764ce30d0460921841042082184328b0b254723a591c623ad39e5189b5e4620f08b27a2fbc3fec1cb38b5df89d529b726ede186bd5afdc98cd95b3db461adb3d29d1d5369f52a5bafc22a9db25eca4d215ca8c8674db260b2b70dc3b20ccbb22cc33221f5f4928b5896654374845ec811a6a55cddb0eedb3e4435cd0dac5d7ee8da2a3f14a5946e944df6791d594e79d764da448cb0e070667f46585caeedc2a4e2da5b0b5a5c9b9e68dbe2c2a83dcbb83ea8e4da54e788234c541c611eb2b1331b28c23cec503d3f9828e8b37d1ed3a068074464eb89037fa88d4128024ab341e5d8b2dc281becdadb5114cbaab861d9a75d7575904da56d57d50dc322c568b63d7bc6e5c98bcc8e5d11232c4b5cdb05a328a8b8824c2bae4da9b836d5476619d7deb4174798efb838c23483e310341ccd704f94064f9c56657318cbb0c5186b78c3185fdc610e7b42de9781fd597963a2946e1ba5df367ab7ac03425dc1dba15ccd0dd3300cc3aad60161da6691258551bae5b8eb770f6fb665f0f67b39746976b8b367c7bedde62faab0f833b37c6462fb46d55569aa6aaf8a966e475a9b7247e6a69cded78b7dbf9de66c3b7c654126f1b0039a5613001f2549084f25c698d2c128dc150481524a29a594529ce7ac7a481f33c6380029c30d12373c96058061cfb24502cf7f340537005848dc39186637cb6af780f9ec586623942bb85029db908bebc98e1bc813985e315c5c4da20bf131ce09af09088e875c5c4d763492315e0180cec74749548780546badb5d65a719eb3ea51b12979e01da40b02a5f413d64b2d177200fb3cfd2811ecf3b51b426b0794d10c03a1018e73e34f6ac6851cc83850e53122f304bb3ecb5f1daa9f32d64f2146588c5c9ce3383ca58f29511cae34e2a8727618e0c65a9812b252eb30c0cd8121bd36f82849cea72e9426c847a95138bee7d660482f86514eaeb063b9c24e33102af8e3388c6194520d25fb8f9c74669a06d73f86fb7118e772052409fce1ec89f39c558fd38ffcc1055203c63848dc267c94043d0c695639144e65205850291c2c43a51990a04c4cea0e1f25d96fd816e883dee2a95513ab675c0e7bf82829ca12e48f92a08fadd65aeb85e4aa1e881218852021e96352995b8891463ab5e845a20048126cf1c77103c8acc5dff7b7edb9419d1e5b0fb99adf01b203fe7a6c7bbec70e093c0ac09494f0b2e777e785224c14e46a1e880ef8bb7b1e85a05a730b71cbcb98a1d20c4c62ee28a6ab97346e90b8299e36a989e5d05a28f15112274b989f874090c0f29f154432a0f30d739e527aa3cdd373009dd386317eeea82b912046320895228b932d3f891845b1e96796dd10a8542fb114371ee69c1962c3d70f86d89f69c393b0e12b8e34368c3a62f87321a63f4a8af233e2e2d201e620b0dc908b6b0917976bc3d799a5fd9d7046695f138e5c499ae186af3355b69d3f762435037fa91e15ab874a33206da834031355d52dc43dafa1c4cb571c60e48539df8f92e6939c95140f29c4590887cd03a9418c7174d9f065c68bfd85b0e50db0e1cbcc946dad0c78fe43e517a89c2f425d2ed4bc9cd8d42d4428c227531388ce8a923ea0c4cb0805e9acf81f3969007254eb67e58020c1d93f6af14c7d574ab10c8bb2ea17eaa67ace2980a0604bc46695b6be0738903061e58921b810038c124a948c81a30833708c81c17186d4628b4bb3c8018d4958334db20a47131f0ca1848b1254d040c5982d57e4a8e249920d57c278c30c8bc211655338a604a006282e732931e3f2c2ae7a6014ce8f7aaa13c33ce1620b24315ea0b8010b882a4e4891411667b8ae964d290512c30e1c3f98ff7cc1d18685a38d1d93a001151328a3c9105f24f172030b48460c6ab0821a7041030d64c08a4fb1608c9923c26461a64c18770659b4a08d2d4be4f00414707ca00d9e367cdaf0010f6f94f101928d09816d6fe890450bf3c442e1460e0a73a98fe1301b1beb224436260419bc1126880c2461c10c6810d3a40a1a5078a2054080a1a10c1348bc3e45971dbcd185a34193e695d4996fe0607f3956dee82193c28d3259dea8e20d28a814cf4a724a323b5f1655f46006597050061915307385103850e2e5022ea85426ec557283936f08f106142d0aabb921c50d2038322de8a10b1a50b992258d1ffc7003209cd0011957980c6145160337786801b6ed7063ca5471230a3265b25828dc1843c68d246dc0c129e130c6c61a775c2e974b89151fb7cc1ff4098ffe56557fc734d526ed69b8eab07f9f5ee60fc9c3b3b3239750993a4f616cc492d30c3c0463973d5b49516f8993bab674c55674b12651b631ea22ca36a68780a43e02cf6cf917b22df56734c36600862cc8aaf34774b1e61b3b7ed996f08d06481f1168830c5c411f1168c30db882fb8355ecf9452ddb0c0cdfd8707f108b3dcf387ac38a333cb8a18316d6bcb2041a5bba1421e58a2bace90416314890792dbd806544049438a01051864c1a3d60653103132c7ca0460f48b2604d1f62e0228729b678a34b10acf99df9230429da60220a2e92f8000dd6ec62690917e4e0439929de60cdffd42736894f1e529f31e69c5e5caa34ab6c430b1b5c5e6d88a95c1738eab4e1451b5df64777940b73a0e422c6281fe39ca20b1b646e906121a382ade30c0ea017587040230757e07086873194e8c10f505c3a10c28c2b3457c6b4c18613325274d428238e1d70c1e40a33a00d96680192325c3c89620936a6989ed802b5ca19a365cc1763b458838dcc099a0d5f63981803c4162936036616b7601c4a35ea0352a4f172b951a586309edca0032d908eca04c1450fccf0b830a822d56335468635cc44913c3c5658cb634319505001041a4dc29061e50a296090d4304612343ee0c4fe56fbf136b113a0032741f8b0461664d260ed60f224cb146492084385159fa2c2063c3620c0185f584982092faec0810c7c9930411623a494e104184d8658c30b1bbc7a10e6b5e4030d4a113bb821840f3f2cf15a038bee5631aad2450639240d71c6120f3e55cc1e98590389358cf08162a90cb5a8319492f0c1b2d4657fd827064b36d83e51e06cc3d7920ca85d7ae20c9f2b5f9688c0c6d82cdd80598b8dc0648c154489620824de48415218417481e2d2e14b1a56ba3466c0dd25d2a042d35062f2c166c7862f35c8a831461bf2908c684403174cccc0060c76d8c207020053c5152b5a4011c6952cb012320440c88084cb11165850813531e60a34c4785283307424116badb5d65a6badf5938bf9f3822c6304f102e3c889510e5364d1024b1431989941199b4bc5470a16ebc46a8cb1d2491f6bac31d6a901d8a3ea13608ffa08b5d02106941ebe78598249131da6208388306630230a247648a20615981a56e4c06a30458d18d8334a90c4c00a2ea650c1c115451736f84026ca15336a54d9d2a842e70d6a1871eb86af303198d1820c09bba18106c6387534ac2481860d3ebce00c313fbce075c617253e2fe0367c9da183aac9299588c2891a6c4083154060d1c403dab8020e2096b828c344938e08ee884c8d1b36359a2479717930803be50c0e7c6882c9173d80e1a58b1a40e921075b6c60034b0ae20ddb7dc1164e8a5277839d65383338a30a06f30a63e61566061f255e6292c8321f29a91eab140ae3e42c8171ea3c307306106734c178fa2cf1adb68c58b64c03892352c0440e515ce08505c5f070851466dcd0e20665244d39e24ac1801426b0a821494c191e8c30c2881c23a0f0d08c075e9461c68b076136a53e51541bbecab862631bbecad092d1df38ef968b725192d076ef4532b79284ee762abf5dd342dda76e45a9319e3ad93d99e33a8edf4a12b2798c512895d241ba952494e351b776a64ef61cd7a173e8d68fce8e752bcb6ea3e1d4d93e73772e5ffaed42f49b6e25696fddd36b5a68becba2bebac5c2d4c936a95bd1b52446dd5a61ea64bfe7244d763a64c58c46663559cd212b6614b24c99de6b2505d5e85664e5a2fbeda75c942474757cf2a25d06a64265ea64d9371a182b6a254100b2eab143d6a68bea21eb8b5476f62d435697218bcb9075cb5052c6937da184d283a20ac2ae4264d5ff4896d294d912d71ede28752b2948b7228b625a85ae1e29b26afda68f929cc0c28e1459f5da678eac2b23ac927a2c9a83f20af631dbfff810b14dfa79facd7eead8473d75ab65ff4125294fed216bb2b057ab87e0529413cc9d87396130c6ddef9178e75e3b550addef853aad853cad27c60161b9923c7225646a68b40f4feb10f66fc810bd861deb86c463df7eb71ccfececdbd4b402648c18165175b5ba5d770f4f632a7a1c373d4efa0081bfd56ac5791e778853f3070952479efaf8803f12a036c16482c904a4b37036e533fb5ce769249cc61a4f1d78a9492d8b3b7f6c77cbb07504e366b6560c33bbc2b02709702b49fba63f75e45bdb5bf8aed8b7185cf5113af3914dfbb0dbc3eec8a49bfd892a2592f617b3af74283bddb31b929d66b5d3e80eb0ec6ac5865eec0b4b80e14b8c29fb9a606bfbdcb0ae08942bac65c4da1ea56e418523284b983a73bb0aa64d5b093f2857a88a2b942ba9a518a6cf68c7f86bba9ac6d5173ccbe9a1225da7b1cc2d10d9bf80dda4ed6d574416311d07a2fba864edb495450d32c666240000006314002028140a888442a150309c6a9b281f14000b9ab24464481408a32086511064103104194308010000630c88d0d4900375cca1d5a4d723ce37062ed9346c430fdaa20302e9c162470da16d8d4df4049342b18300ee09a0694e91046d6c61790453b8e0d7c5ecf0aaf9a74af07cc8a3684dc86052d0dca3d2572421fa9b826feefbf1a6c5cec608898130d811f8e7d9452d7351c83fceda5653ddbfc07476a6bf62e95fa82bf19321990a78af02f7e7cb5a320cfd459d22ebac62ab5ce8768f79c71864a10548404b77bb6f0527e5573d0dd40f2335db85011e851eab4f493631016d5d41149817d69dc1b6069b57f359a719608154b751badd8d2d86d0191324e2a053ade5b3b7eb85a05eefcc5aee10c6b0c2e51a2d5f0fc3317756e0858d8cb28e72c2321cab02636cdcecb946e4d5e046d74053ef53efbb979e20cbfda4d23b2ab782f7112a4e285097ddd8c0d7dcf28cd9f916c7c2e2c5568329ed7a1209eda95d1f6f4180ccf5fc270fadbaa11ad4600e7be6f3973f1991c41a18ffc525a91f86f9c508ed7d007a425db014063ccaad2699455cfe3d698dbb4976f82ad92fbac844a027f44092903d6ad03f4ee382a79071730171d6940392aa4d684dbb999247f10f7bc874d64b883b37ec6a9c06abc364b4c2370a6e9a49353fc50371954adba91b1aef2031e14b77d1053e9d2003750f9a4c697af3d830e342d74a1c784d7359481e3788445dc5ac73e5a15cce14ac5b49f44de4838159fd87fcb5254f0720a26dce18351cc9739fea569490f379e2cf108493a87c34911d36ec1cc0026c1fed950ed963870ad3c241632d36f2f400ea34dc0389fb6f425a45bcdfe9d350aaba40d7f5ad6b6ec1ff52a9a73f9feae33017da9d03898a0d90dbdd038501f4c072cfe9985ff38e4822f696e85c24a69d986956da0d78c59eb83693148f571581f8763498503b0325027439d1254b96c6a099cb200c6b927d80359cb036ee47692d2a18b51e22478dd3b30c825b7e5e97e01c3b34458ae1e4828807af34d7e46c32fa8eefaf74082910845d557fff5c0a5f2b8328ff031326f1c1019f65acce05428e67ddaf484bf853e32fc61e509c74329b20cd65ded6ca0dd02ff154e8566411e6d2affe00216bede025ff8379190e0c75616725e6ea48a0b28157ea97716fa471425de1ac1694c106bc0798073078ce01c607fabfc7438e94735cc07610e831d15b6d902520b6180a982b7187ead6351210a7b84cf9be18dea677020cb0df4338d642d7a3178c347a67ef83f69d2a4ccc89b1db9712a3f25d004def3ab4b73a38ded25008e5870613a223204e2a8fa9a7a968ba81f1af8420de6920b8152d9402ae1a325ddea1e987b841e8fa0a2d3e742d5040601c18eeb9c954aab181d56a1420a134593b6f0f0f3700a970a7c0900b97486fd012c7f312ec4ee589a2f104c84f3fcb3f1ad5c55441cae65d24460a102afe71a102e783b74080c20c1a649cb04e2814cf463357328b07b97b7278d556e240620f7e8f5f14199124485192cdc693fd839e9982e7cec52fb7a96f564b05cc858c4cb4f3d8c6451c016c829b5d508f22f57c6b27020e17b43cb2a39d9f59b7b44aac601ff970c9735a541da42fa5f492282a197fc19b677b990186e7d99d0075724f3d177ef2fc5886a0c0292b6ba29a55b62b63efba2f8b37964e8d059db649a4a017bd995a1b7ff8e8b2cd9948bca23a1ac53d449f350533cf4528b1c22f44e78488f4ad77f9e41c82cee21234ef3913c1962a0c5184f1b918b18295f5703890774ccfae07603a5cdd647538810be93f6a938c6b7307c54a138be373d86c6c87257e82862132f7d33ad69bb600738aaf74954a384432239b0c1bcceb68d3307915cdce8b7bd2330ed59b49312baaf85897d20022284157cfd16605d03572d2f085711248464a965f01038a5148769b22f1434c18a9daa6f21d646b35d833804547acaac9ae0b3c8d9d782cc092e5af51f836aaab8686de4e793b671bfecd3447a20730f3da0ecadd60729ebd0f90cddb5c683bc877afe8875b4bf9c1cd2026f2292639fce437264842489c388db189f7149bf2f3430409fea07e4fd6d2e750aeebf5c0dc9d25e0c0ae70fcd52a070ad3509b0e9a320253f0a82ea2f40454e52ec05e85e8b5c4b05031dd6576b24b290fbfe1ba443b3af4c469eae307b13730366359226454465f4fc30e6c95da383b4d2c6858d36f9a1ded6b28f3257be1ed3d297d6f50e3c4d4b1ba8981a05778552261b547628bd7c05191a7d6a1277a00288faf7ceb106a97fe6ebaf2c24aaeb6549dd823b8379bf2110da970f4ecbb72463106e358617f2c892064d711efd333871081e7828a6299bedb4c72c4b2377365af4fc971b9f25aa5874ea4a6934ad1f76d6eca5f3d586d23f9ea506a04ddc13fb216479f5d30737227132693896daea0a42d00aab4932f06610af722180686492481047001f1b5b91481e84a02c19fdcb853fc4bfff0d60c2747686797340fb31157d2e3d757c0f3295ecc416ae54a8d9c11188f366f0d3cea941a66e87b868448ed6f382f444a0bda0da5647129e3f7bb938c31cbcb832c31eb28e958f4afc95e8957cd04dd24669151e25749840882ec18d220b75285132e6ee8804cea6203fb7b322aa122ece02a278ff7022f09e7d6acbead7ce1e2b8262e08a7a4638c361951b370e10a3feba1c9ffa3ff4fcd744e3e74631b8cfffa33701f635be1317469395c265013ac76219fc797b7d626d395a0ec2484444d78eaaaf92bc82c29478ea4e9f13316aebb3a4bed6a5a542b3841862e09abdc4b71d82b632e9a9f171945ccd2b27850ea290ee4778c2e5bd4d2738e8f6893c92e2b255a6f328baa8ed9b639d09d953613cd078744f755081f568eccc26e795fec6f2f18a87405312b6cd64a866dccc0959c2d4e7d0ab13668706df6b095971ca0fc83729b863d701c5c3ef575c2482f4f0debee90b3cfa62e309724d13fcebab7d9aff519d9eec474c156058a23d675de58c1d5b0b72b238e5c464c08a229c85626d09fcb4ec466db18310536be245a13be0c7c5728158e7bb57c0d511132f879187f56fc3208165193f2c85ed5abc06f105ec8e780f4110a6d997c0d3e15079fd4a3c6988718002cf3d8a3268dd61333c9cb33b898a693e22f90341682f127820b0891d9c9b8e60c154f43035d502f29eba1dda473c64d2165571774f3c681d24a9faa498df999af5ca31d806678b0a4f8d63898530e54bb8feebbb8a3a9b6c4649a71bce208cef368798f3d9e29a9696401a5e5ed8e322af089a5e115087a58eee94a06bbe10e45bff371f7f66391a3069ef4cfbf6a627596621691c97230e2426ccc3ebf393391295046668a343ae56abd17507643d11b10fbb35e3b0c040d9bc5f6a5d53e0c93a3c642914677c3f4229a3037880724a07408eaa1c8cfa4642828ac084268063d48cea700955be4bcb5a15426307a1e4898c92206ff9462f2b6aca1f61cfd67951575dd088bbcfac1b4e0fb37a75092d821339b5f0ca3dfddd06649d9f8f5dbc283227699cd253a65510c84046b5c71e8824d2448eaea6642ada3846ac6374449e57de714675ba50277fe50928725fd963739a2e63585682742ac2366cc4fa0ec6cc1ada4c9f9fccd6bd828276284b74ad4fc54f7fb8ba4ca6033028493062da2ff825b35925c67cb75f3718dff2265c43a7b2b45fd1633135ce61dc994c58d7a6595c930d542d3bb1ff73d62f70fb88e43d9a2e47d888daa3b5fa6c3f6a9f1ad31b13f40c67bf52e5807b2652758896a69619b5759de0c099da76909c5e9bc0319cb6fc0465402d44054c3f3032d8bf517cec179a1bdb4627e621804b6a444d7d050cbf9e8ccda074dec88fd87c57f38256340c00b1a5f1e01c19ab76d3109e53af0cf272f766235c37e01bdf3487f53316f01a9736c6b9e4a5e5991f4e22bee6d6b24e72184911d4e9c7955e9ee17c0cde353ef226e6cb589570efb3e01c40db2ca5998c1e247d5e2b8c0a90ef80274ccc0f28c129bc2f7f54ae0d37a02966c3a84fe56358c4a232c981639961bd92400a9a8cd6c879c2531c4476dcd7ae8cfa67a1cd5d5e640248715f894e4059113831d21026058d92027f452863997071c54d99df8d26a0d260c0db532d87281858d2fc317245528085604b246a885f64e466ca69fcce9bb158446b954e681c493b4b2b8873539894eb70786487f375dd5f5dd96fdf9e443b8796a92a28a819caba11f6b75eb6b862b7fcfe3c1e6c473a488eb5fd3c9d5a6d6e0034744608866d8add11d0675c665b6ac20ea55fb661816c006500c48bff14b6bd4e050b0015b1724616030d4624599ae6326e6fd90022021e166d4a84e685194c9d52c4137485c47eb5724bc7072b1e90efaae45d05866caea9537fccde205eaa69f3e88e1900ad3430e156ff8ff30e717222a56baddeceb188135790734b99a181cf2d369e2617098797d424e7522dd0cb96ef3d6278f9f189182f8294b3158cf587f032d5122779a0904ce024e5a1f8a6d6e06e7162dcab3c5f55f01028f0a7e13812857dc87141b2ead0a66ea829f9839248640a273daf00dcf807e711682d90981a8511df71502906c33fbe04f2498735b6cad9b04bac891f3b1324f5e9bcd0731fc78481bbe86839bdf7c5c5dd7ce2b5301101aea334105c4180648b74b454571daed62a93c2f0caeec1531012d9fdc2e20c94540d654f278fb59c1b8bd125a815cec5e5ae85f2e27b9f7f2df15727b37ee4d381ac3ea886b60496300b65c30bc5c5dd0851342d43a85b50802ffbeea7f74f04bfa6f66becb9099769d15f35de04563ef841efc4a2a2815d246dffd377a5c9e1a5466d5c271567eebd2f305a2b5322e78acffae9410411988bdb8a4b040779005be80fc4d9d785853c4a6fd71536d3849936caf90f7ee90494029be1a4c258eddcd2ba8bdab5d46f0be9273055c1325bd47c446c0054a1873798cece702767d625b81afb48cfbd07f40e07cc96796e46b956a2850bdfb8a0f58f0dc82afb1db289a8d094d88e0f79eb899ee8a440703b04c449e604294e5c6688f1ea5384a604aadbd9f6406f537307eb4fb82f48530fac954ee217939bd675b749e6a1d1aaa16d2a1e3170a7400d141473d2de62496941f0777bea68f3b1b1ffb293d224a26f6b924a856677713046299ecaa2f41e93a5002214bc8dcec7175176b6cb6362850e403766101f0dd15bdf573d688610c162e60aa0e79438ea58616524cc5aaa4d060ab21f40b4d2a0837183e4a212bab5fb3120d37d58bdcfc113ff0d3c24d98eecaafa8a7bfbc8b821dc33f79ca6cf063e6e1ae8d27833e0c6d5184ceae55e6938d9a7ddbf7d81abbece6f1b62a5045b44e2d73f4add0ee7de36bfb9405f669c4c1bc1b2c6a0cb83f9f85079484033a5d61934a5cd8aaca2571a386a2270683cf5240682a4df7a7b172691e1c3a00dc0426cfb5399921e2f5ee9c0814ed6bdfb58f6100c9ddecce8770f0bb2eeec4801f66e179d11960e884b981b1854e8c05ebf5eb85930cac10b50222c2e1d4c783aad6ac904948f832ff3ff13dd26ddfb73601643319c995630c05effa8b561d847259099c88a7000bb9c8c8fd18a8a306f49062b93cc5454037e013d6a5b82b2ffa12325b0287d33f6614def781d56887007a04f4e4f7a09bb9c831cbc1558a0ca56487f12669871da0bac13be4fa25e25024a7adba77be103900e5e6161c0acdb62171b20a695967e2d0d0e3d2cab135e83481bb3e1741773dad9345e7e5643468ad3bc6e222ac7ad32f4027f58e462d308fd283c71db63a54c84adcf87471020ff5a1000bcd17c6db68256ab24f2a2b6e81a6e50bc9b2e051b76df4b38a4b2c65661b9f83b0adb1e1a2c85b2a50b1af683bd57f36146eb0b22d49db107a40219950ac06d935143e7b1baf31a229cc7cff218557e6e18d73005e0d1100c7f323a5d78939bc35dc5680b4cf41a596e7c04705e10b03c1613a31ee107371a81661af5e454118726259e928475b357b3dc9b8fdff6e3255f44109496d940942869069766a1de5d21bb89f96b529702f473ae830ec74fd01fa52d2bd7cfa5411957f199e7f2ca702efa6ca53ae25bb661cbdfb8d92715869378b2fc6a5bdf1085a701720b2a52055da3c509a3105bbfcd165aa44a638b6cc51ff0525cb1cc13a7a13d3649e677a126cb41055c6442b1cec2100db75706ffb75228f67f8d5fa09841f79cd5ecea8229d0cf8787ae6898f2ce08662c35cfb732950f6b2c0d95c9459122dd6e8d6d9d5a3da598ec47e3fbab14b8f988a3a55eae6e09fd6d87764e1861e31f166ac09ea49387ade84f0c33008371ed7ad3c6191a30269c66c751d99e2920374d50c5b5ee44cec0a9d9169f90433136859e69e09e18d524a5f4ce6d58025b8154398948c1be4783db935f2ac5a248ce29f7bdc425102f4308f19746e550e910cd8cba6aa11785f93e8fda6cba32268dcc6e68aff85850481c6bf11d2afc0d8b812b2c1380c42a8b01a03ce8806b66491eb159a64a2b01d5712c909a6230743e00f86bb380dc7b332b06bf410b434aa111096f749c6195659ad1c1e738d5fac658dd05094af228deaefa87f03e623e3e88d593cf9cfa8c04383d472a61797c4718a524662c4934daf37072663a1c17fa040353830fdacb1dae4974326e20893cf6468dc1fc0d977daab0c318917b221d35d0c01bbd4228483efc804c82c020c2ae54aab227e6d882a91b420783ccb1954a0dce21f2b00e947414ac2eba79fda86aa939e58780c8253cd8168760b224663c07a90dcd67177223ee8dd6f30609115e8f164591b314178729747e56340328b50a00bc900980c7bba9439811889a86a953e48473c9c47e3bebe9e9372f251d9bbbaecc8d80c0169f8c392e6e91b429186b2d6eae30b9985abc97e093d4b11f322f548927de6a32f1c03ad1dfe273c4e56042cc96c395041110046bacc813f195ad87a9cd90ae11206f56271c93c97fafbd001642d1b18f516ccb916b2ab601f46f12702dcac13d817efd79f3cebf5d2c7ce4a67bf7966ed6c3b7c4bb6d5795254099a230c926bde7803c506837526822debfb541422025d5c1c02a5810a3bd23495752897383b92c02e24549d7b2210b0c0ef5fb4f4b7009afaaa82def092675fc0f6332eb41e77f410a4e5be715b3d823b8f55b29aefbedef43152bbb185b024886029f9861b900b9b9083cdd364241c4c1151c2a99205182b095518356fbc64f4bef8872ebb9e7d0c00ce476cb3570f0ea0342035dada8af2024aa96564260b7762f293d28b16e186750b009d3a4cb1004ad45b8d9fe697a05ae8a24452a1260c2024ad23d31d48af58bb65f172861604e5bd24232cf74cf3a8ec4e17b6c0f59f791ddd62120d94a70dccc6314138a4fede758b1a622c785ffe61f1995fc0ff348b6f4cc2011f29980d6dfd6c9511b84c960271d80a754993e9b0739535300e0f3d995cf1aed410105a294d48dd54db35fbc6c87768134f5890addd6f7abd01dbc476432bd023d7220614f277fd0654335d9fcc376240d690bd18a754c20e598fe99a41b5fa628aab025edc7095e3ae41c622abbd09e61f56e04f6f441e62af6b81f6aff82c4756b4587191006665a3d83c103a67e8a2c28d253ed59602770a6908fe96d0d9ab50e43bb0ac32039de831515f097f5e4b9a717bb40ccde2dfe702abe46f97ba333c1887436157ddbd3120377599afd200b749d60d82028b38fb121fa53c2cd122412a5075fe3503817e2969f2576721c8932970b41b6762d000f647d142a9fb508d2ceb213cc98634b5e987d114b2cbc306f153c735f35f121e64f3d9a513576651971329ad5a8107ca5daed6f5cef0777c656ce5e76efcaf03d3eb8a07da2079ea798678fa19c284c2df23c759dbd3416e336bdafe4d31c22010ca41345705fc5120b5d5783e543577cf91eeea98b6fdca501c3b3811f582974db336558fc0c8d0b30d83cabf22bf45f55daddfebfbc421bb429f8986db1ecbc20130ba27e801d1f1003f8d2324ffac2d70ce19952b4a22bc9c5b23cdb0231b959ade517f8e16598a63416b620857bc9fd1190116c529c0e16bf63fb0890c562995e4753652ebd926073df1003d39081562aa19e41a7e02f1a4e78fb0747c8eac26a64288ff32e03a8aa5338b02cfa945a6b3bff7cd886323daeb47616542ef850729548d8270fa1b25740405b0d46c2003f87c04990f66c382c4ff6dfe77d07116aa9332720e3456270acc3450c29e1f4b2f80ff9b70552919321d45e890fa1d06a2d5ce105d85095c7e67c789a1e3047d10a6ddcd33c716c078414fc444b1e8891434f5f9096d0ce7c6efc63e1bbfcc2ae1c4750ad5ac217c3904af7c55d8747de1d70be7aa1e7291311feef4db04fdce0d40c43d7a1cd7927e11ba6bae1ed8f1b86726d06c84cc4ae77ece3325898a78780020c8806bbd8ec690d0ff4db84497a24416a8b619d62e53c7d26d6d31b4e0a485159869126bde4adefd4bf836ff553e411bac580fa246e342531fee8b57260c6cfb50d2b0049cf0227b25fcae7915df6d9626c86c370a533e5335f08ca17eae088e5a3c1c9512f72772329a8897e1815b303581e3a5d6bc10d3e34a240c606f5a20533e47d9853db244a0c641d810b9d346db675537c7f5b4c812d8f890fc0ed15431d80bf23909f29025578c0c97790c849a668a2a2c81192cfabcb7259514175d3b43f2fcaa04fa1c3d00ba57543823604d4b22a68116aca40fd7f2f59ed2772e224a311061810150aa7af9b89e50fe3d49d40cfa87577c09712c670cfee67bf0c5508f4b589b90c7087c1cf8cbab99259f5d5ebe4a25fe2e0431c4b6b99cca78f1a432061f5e10ce8a58094fc442af5e91b69a473ed69b9c6ffacb092e8cbae453ebc53630d40d57509b5171af2e593e367da0f2f02c521fcfcdc0d088511a9ccca8ff46fc3e7f6e2ea4ce021e63133d2ec45f1b9d708d198f65b2d24c5646c649ee5a2e2719dd029a8848c003f7d6e11bc6720946a29f584fdf75fe62d60eae5942d4008e0fb8523a8131b14ec9955e7485ad9df0b354db3c1749980f3974c4784a0eeba8eb9701717f89c872a645c4b1f6e2a8c752617aa4d220306174724bb3786de6872b75d782d5dac18bddc184adef52e4d3937102fc55d970821e6744f7f1eb6e4a0953b07f7102f8fb9ad35c9259b5efc7fcfb9235870c6a631479278aaa788cc75198e14fb02caae2c3029cc451bd15bd6d9794d8d79e323be17b0da039dae9d00ce462e0b71653254b006a2a416e95cebdfa45958cf3f36ba300b8235de6dcdebb768223ad39f7921420c87cdaa99779e10d25fbe58f99f018a7385ed724f889fdc852acf6c3fd2b5339a094965adbc4bb619cb5684a2b81728f41452e8e85a9a32c533f0cd1094a791af6e91c584b71beeed55099066592cbade189b72ba7fb68e662d7d9b8da7a927b1c2bbd3d8090f6d7de7308eeefdb92f649bc4cf6ea96b839b30506cb7105183b2c3a0f5183a71efe89772b58a6f37280853fc9d5a847ef73ad6c04f79ac46eefa4e1ded511da3c68e84b13954d8b2061ed1d70b156de7bd6b5616de911e689cc276cb05cc34887daad156b2bbfbd6bd689325180fddb99cab17e29589da0fdf3ba587d5cf4ed201f8711107e39accc80db9e8d0c0c2d1cfdd362070182c778316229f0b8b39092ec4ebc2c64afa58bab0d367e364f9966adc4fd9a0b7b00d0d9bde643b717213c8b055f4690ad7c2bed69377052bd223c157042604ac51a1e4c70ba4ff2d33aec089d498f112b4b41ba04511628016ff342b7ea4d8b9bd0243e546f026740d8f895cf39f618ee5e6c7007c15dc4db073f7b56f79bd31b39deea881f5264a36458266dc201c1c56bccf5b72d26d769d75dc62131e68e6cc9d6196e9f2e314e32cc22aeab1fcb62ded1e819dd1e9cbc5ec1376137484b83a552f01faec0e07d7b84cd79a0c7d27dfd6560b3126bc626266d3dde686510410696875a193e7f5833f8fe6774f604edaef39ce9c8e2fee29dc868828ae6f344d45c805b7bc3017cb11d9a3a7398b97ffe94ea1eed630502bc24470fdd02186e847dcfb946d0969484c1cf77213cdaaa8ecfdfa98b1195f5ee0870b3e164991436c1c4f05f89f6584721107863791bd6b918c799f3ad8225413db5b909798cc56d21b9285a66693318ed3861071ee7710403ec0ff7efee9a144049ba7d14a230c14102fa1de8d56240a08bd549b301f532ec24d48fcc10807635106a17e53a918011eb695bd848fda0ff490c804da7db58fff19e51e39dfdf47214625d64fea1c973ddb3007d5c33fd01664d9bc9b04123e826671a2a7e25e83e28055b61ad73794a85de04356e60cfe307e14086755bc84ce6ac73384ef0d9bf7d1ecaa66deadd57d36c10d1f74538d1af710c084557948c187ee883f6329c99b40292b866bf79155f598064f89e61d2ccac48ac613827c97e128ce18766e7114a6570e7d3db8799227549ba5b34078d74efe910cd394924f27755d2713a07b1c19349e336151291372df3f6fe56a1d57218102f822e9cd7a3387d82c8188bb931774636da73bdfd4a8582e5143a65c1e9f7becc5ef619dcd5556b4995a09000b8844c1e221c3d8f64c2f6ae34c20733c975fb00db5cd3d3563ccbcf191da754621d100c7a915b7d3b1e078ef2d7cecc07bb536b28c9c865eea8a552b96217caf5a92e7288acb180451e67ef1059465ef67b7622dd2ebd3daeb89d4062399b02183b8e2116d005b6e866563e1b1de58139381cac69fa574b3ca12839f1eb46891110c649323592e231f155d900548b658ee51a9af10b6f2d7860108740823eba52783c551b6e06947df791261d372e799ce4eebcac667f38572e61a42070d5421645202ffb9348d59fe2999333b3fac6e8f2700502581fd31f46477e45840dba4bb46e2395f95b7790ba8e69831cd9756d15b4e36cefa507f08464cb0ccf5bbfc9ac7aabf7e284610b164bb8d5b92b11d9743908825c53b3fe178002f0d68079c4f88f4c35cbbb90db1e26a7fabcb458952b8b4f9523d16d00fc37796472dba68fa007fb9918c67f8a98ba815f86daada277eff315a139930ead8bdc16042c913f7d905454398384c4a5da115fb0217ac67fb3c6c0392b52261899e8ac5e373f235af9454e0741137f300b6209cf3f54c60aedb94842f7065caa919cb1a7d19f7492d3c1118968678f39a130679426a8a2935b5e5be30a7e2a6a88fa6c4aec976c0e0df82b54de8fe4a7a2e5ea39c7eec1a69c6985c95aafa2f25ba8bb310d1926551bc582875d9b5d8dcc2508c00f71e0130f896de1c49857c948d7483a92b4f1012254e47083be50832812aaadb9fe2c2a47f1f856d5f44882999a97ddba037dc40a88b6a434d50dbb4b9c139aa7d07a1ed013aff079467d9e7384b910f25528266cd8c2afddce7fe4cb3ddfbbfc816d5e6296cc424c6c3b07317b1aea13bf9d00729377a749070fa8995e08e8b9b3a12a1db6913e5449fe8c8dc5aeca277443357075e0f590821207c780369446ea701ede0efa2b769918e5e66514ae6ac1fb4ae7c69ed0ad7053703bab8b863e3a936a14cdca2ca2169b50d05f7cbdef977853731df999b4442f83f3a8661c948296cc3f5014b5d1e1a973a920ed3aeffd5ab702ed519ef20002a398e72788ad2a0567fbf80aac395f28a8f3b993123310cf897e5476ce92a7a17504cec30fc21ecdd677ad287319a21df3e5b804fce3527262978974214f530d1d4dd240a678ffe3f94277573d4ab6df4a25db68271cae801a0d220799f7d64aae020930ef51e2c75a8b92552aba3ade5fb73e72408ea87284576e9b44c6c4ed4ee19fcade7987b39fbd5b9e9cb0f575084176068246901660587b611ec9b5c6db454a9f89628a35ae8041fa6ab5a4f4356b2c7b98ec5fcdce43dc61127859537dc88a50b3b3a17ababe213cea8230855a924485d933b1bae292b835ea7aa96951a32dbe4a6168ae38886c5bd199ce85a37030c90aa44e2272e24fc70351eda802c2c0c79439a45c15a31b3b351459f0342e562d9611d3ae9ecb34bf9ae0c5d0f81e067e4a972abf1aa2f822058eab122bc687f29343a74c32d5c96c590da92987d1ecd5882df743cbff9fe9a515a1f3efd595880b50102d6ce1555747bb196b9be029e4ad788a22d8ef74367af2aa3076cbc2bacf46676bc5f76dc6c25d035ee6d121ecd2c320dfaca27d28befaf1c35e05b14f64e4ebec53bcbacfcc5251da32553ca5cca3d9360f62a7108fec90e80642d261853262cb6aba91feef50dc3e920e169cbdb7ff773f8e8de046ca755c2af1dd6c14006ed84088c01f15c1ebadbb5906bec9e4559196af6ebfc4c12c4a7ce76b2171588c00f53eb4283e717db5f5675d108ac56e6915f1834ad390b3b03dd351717ea6b17b5024bcd03af387690ed57f575017efb129992144f1772d1311d8f087433b1f3bfef8fb26d93f97fad8d0dcdc45fa67aab64504a1814fbc09353ac456cbebda00b5698b8ea13dc15b039867bac64469a5dad31b3254a3b20442e0413481ddabdff3b47e8b65a342297d68b46ed65e3f4008b5553a3a22d2ba247f9571842d65f64cb1e5cd1378b656dc391cd96ac65e901e2c0bbff37347af4998f519667df1ec5b41c77c3590d977b8c4594f43713b3687cc93cfc4767a2cb65417fca2aee4dbb88d2475df71f7f920e267765e5f225ddc74fc1109dd63e2c27d0694cc5c53d2a04f74ed324817b19ff38b5144cc77531df0cbb745aee55808aeccd9de9e8ca2d89d271d1bc61b5433e02625debe08acc917fb7ca1383ada5eb967458e016799ae4018fc813206091e11c32ad2789e9b60f53d9452e26f1b1f7a81b1a989b1556439f613bdae70f94c884746f0d11c39bcbe1eb17c6656a861a2b7afb6cc1af563e7e1688848ab30ef00046665aeb2d61506f1c738c086304f54e5f3013ce3185f7cd8cc298e213500279653ff118ae7573e9153729a6e714e7ea8a853604afb34bc3eb07d2a63643566bf10762619b4d2f514026388d23d8b534b1ab94948f15bfe0ae1e5e83decd52e23a18422fa2576ce84c7b987f15c8fde0e1aa14fbfd3a2e5a983d448f658a58cd065f04c11fccc0960e4b646de68c10952b09df051a730aae420e792acf8e8bef3a979cf5fac658644281d45bc9a51ec3ed58fbdc056e96c0642173133078bd1c49a4ab97da68f7ec06bf601504cae8c7701df7ff7c47ac7c1788218311010e9ab4436c4b531bd7b9629c4f401e8a685b292e1af6fafc954892930cc4e3daa0c968012eeff5b4a1986202d5d78585265e363a8bff5aea69be86778237afdb6240c0be82ecc6326ca2d5502005f42a0ed67b4957699fd1b84292ae33e78e109c20d3de9b219b9c4a23eeee1fdbefb801ff21f0eae0be8499cb0ff2af3805f1e6649db684ff13eb4cc626558c8af52fc8f99564448571cf04b9377694e81950d6f9989a5756d014798f3d0fbb1bba786a011525bc6fba7e2c52a4c17363628a4c3e1848b980db121ed94e580ce0c939956c12157bb6f5b8523f0654939c0232e0450a7516824c4c978852834b43e32a8d25306bf8c20f5a0024b2fc617ac1554faefd02dd4b6334f4fe41c70263775a68f59e354ee52f4d931d6b00c2f4a7afdf3ecc864fa41a98821126f54c0b820e73e6756e115f7623ff380380477a82ea69b489674d213c1535009176532e496fd83a192d93c33b7937bb4fdcebca835f0d138128f0cf2f94493209eb7b5f76034a30f8f97b9a3b13cb6ad9b2e6b734b53019042600848c46fc9f4636d256cf72ac74493d1aa722a61b0d6769cc1b4bc50f8000755fb4f119986af3f7f9153ee67da77a0cb0fc5215dc81b1e74d75ea9e73bc1a053c225ac96f612d1610e7959109931b6372b37cd6b932337810141a37672e6020a0f3f0a69fddddf9c0e042b2af207c93f7e5524b384ba23c714d42148775b7ce23a227f43ff0730dff821ee140a0b5cff6981ed33711d2e0a8616c831f98cc8b6ca804d7f5e07eba49d38e22cea98d299b9e88087ff08691861a951e79457db24054e483b2eadb9d3f746ebd5efd2531586ecb0bcc8e2847fc6e0c9535471b3b5eaaa0a3d9b1a1d925a405c76acd5216ebf9e6766f32aecd060280d846f182a8195a904fef37a0605b1e86ce83e9e6da11ec25f13e389571759166d09059c7c95c505d2e9ba676ec2203cc2277204019d518a4c08757c4b0bbf8000e3be1e971500e1bd32511d8bac287b5104d192fcc76a21dce528cd8370dd012c13e32902435431750dc9f5ffdb9bb0618e4f5346821353321c65833b0ba695017997852db47c718480c30e97007b98d518fb3d4d27be1efc14979affed6f8098224ae84c9d6b481f945852827442aeefee7c8dfd0ccdc1b005cb835d064c58d335ad8e474bc8a9ac656e59c5af7184ba514347ba143dec8df241b9aa7bbc0b9839ecdcb16c0a52203c372d9ce80ed47147bd47ddf4ad69e6d6d72536fa63da27bcff39c50fec180dfc612370dfafba3be709a008ae4346003e28f992cfbfe936b3f79a394e14c1d09beb3c32e0a0cdbd67ac85b6863cfdcc9f13a062977fa768c3bb905467f4b8b5d15a888316cdc9702268282ea2671333d7c8693ed41bf080fed4c4db39606493e16de827b6d94ff6db6756aba5a9e19d1b3929945fc131efb31b21a20ee5b84f0feabd4f14ec03e26e9b075b2c8977e37404c15d66c93a08bf217da316b3c0548bfdf884794f334f330020d5fc3545f62eba20e95c4c761401ac87f0824326a4d413931ec664eca3199befd3a6f7dda6f866537cdf34bee726e7491e894a87b8758e749365cb0ebaa30c9293385bb414acd212dcd2e57a0a8a5ad3095881f65ffd13b7880182cf16ab085c4ff8bf8bf0ec4bb12545dd97449da01cd2e1737bdc7fdcc4004972ecb55494aae2f0d608943cc327a25559f653e77327a94f3fdb8c2afdaa6e36e252e1f05cbcf6ca9608e6e249a41c429593c5acba6df2be2f8c6891d594fea051a9506bffb913ee0e0b89a517a5a09024a70a78df66becbeb2ac8695a2b4580162360c11e9ab986110b528e3cf7d56338c70ba7427a25db33518b08121daf1a8d9ef4fc9e4e0ce9dd5d6498b0d38ac458c9df96c1a42ffa44ddae1a5f3cf0dc2d06eeb941f23b826af7f4b237fa2411ddbd61c5296930abcfe6ffd91101eeae075859e94d16d7748aa1595b471c08bbba3b0278b66f6e5e9ae248f6e61e16273f5bc0c01e4bb5637029a20902c90b68e46eb7add754f6f90f751f473c0decb9589778a85e60d2b303999c3a2f03bc0ed72d31567045622be0fa391f989b1e82677b750a085f8304c2aecbc73e14832541e669eab294a828ad6b911f6f05d9ad48111fafc228988282e7097940204458d9ad624528ddeb42fa793385eeb5c90417c067fe3d408db38fac67a9610c4b07a38cb6ff294b2944178ee026c1d806dab3cdce6b4dd40fe52e355c4e50f8d77215bb54ddabc9c7cc20f09bc253fde1bcccdbf03511cf7d4092f3822338e45b2a78602db0e8d7151f3a4db14dac4e042d4b72613af4911baef8e4632a509dc3bf4223416d76fa6d54faf9ad9150da7d2d7679ec093cc02ab0772ddc8bcc4632203a3f9e7d098a7010ebb494c2dc1ee2c13c3b46e8262d27b4c28a25dd9270b1bd6dd7b449bd61c2d28f38f8acbf878be81af420f83124a0d4447ac1befa127bbf112f6d8b7e5dcb86e51d8c82b7c959921371835184aa26d535626332557291d377279c8193d0e06c2255371f6b028acddffcb5ed6a39b7c3756aec449d91ff560124c78c657c6cac26238cc8c27143d8be3f6bfad0cb16373bb653c56e27128478a8fa171cac4d9c347560b98c133a97272d282c5fdd37a3098d2bf2c6333b96809f46089265b95b96b0a05cd3ffaace3ae0fd59eccb2843c5d6868be717797864b352dca0381a66e0a364f6754bef6f4bc6a9358c8c14db743e37a8540e90993ad590ee447dee0af45d51208b70a52799bad355e92d4e6c7ca8d9cf2fd65d5158839228a165c963a4574bd6afddf10de527cb170965d122813980fe7717d5275d2caa7ea137ba008c5578bc84fd6fd30849df64fbda79704dacbeb610a55a9d0140ac9b24f49e15328e51dc27641edbd2a87c167b667071c5d3750a62042d0a50af263a8cf6f3c19894b040d22219b6c74c654d0145239c174cf0983decef273884adfd1f50b4b4d23e2e99ee534399af07e825e6636df8274bb43b6748cc4215045a20d9d29f91496aba3321ed9cd6abb2997c455a6a32b90622afd5d42ec2b55e174fd37540cd15d19c881ccae574ea1c6326ffd5adbd46d8e228541fdde8c6f8e131428f0c785d6e05e5b494d61077c1e37ceb2a1f629325360624486b71e162cc125c2cc09d98797706046ba921a553d7e700029cdfc39e5bf3c2bb3cd308e36e059a928e7043a3e4102214e03534e59044712b2ff40c2d9e3317f5ccfcd85fdc3302fa87017d8378eb58a7b7f01516cbc007fcb0ceb698170817bc1c71bdd79e463063bf09914e4a6015b5f1d3794659e0db611694bebd405a7c37114687e97ffcf12a31c28ea60078552504051052a3aebd6815edbc52a4c2752332bc6eced9a9c602598b33f00a1c089dddb14f593911d4811485fe90e50b03d304be82bb063ef9f0b1caaef472120440948a0b0e5e17780fe829bee4612fb6ba1fa874e45661e285f11f2f95e00ba9e074fe897b24ffa56fae96a761d009cdcdaa95f4c01acf50b8eb4570e3fbeeba20f24ee103aabb3a4099bc9fe348840c91c322510c11323d2e988525b0c7223bd3144d873960cf6300f25738faa8ad27e663cd6cb776cfbf764a591846693e80db0044bb4f17e299a1843f2ca242d7cf76a200cf38270d4604522da029fba014d3a277361803167203bfb3e4d44c207e611222181e1efe1a4224060e4962d30ff986a72e02c8342d839c0b6717fa8585a34868639c333a0c711d51c7e86553156af978dad204979772ac01b4d7a3e7c05d1ee5f8dcc89bb54eb0cac272dee0a765787400360a8c39e5d2df0366c35108291064b5444a3c74717c650ecbb5d7f729c3811d79027fae00e24b560f9392c473d639c48ee0fde68b4c2a3904989d512299f21564ac1e4b4a929bc4ba47f646412a2cb1adeebe19e91175da4e7bb1108d0e8406d266a050a739cf5d9f09f3504309aba699f721033f986ab49e9cec7b843f73acdb4d36995889eabb565e86c651a3500aad5bcf9113f45710cb5600d5c69952de46c1f7d42c114d128a033395ab6aeea3d834473dc5f5e6ba19bf322544e9f71b7c63b82e6e87db764cefb916385480c200dffac7bbd27c4d4301f0c1c2ec5a450361ce580d9cf19e8b2f3487b857ea13e78adbbff87eebcc48953d8e89e61fa60976e0b1c3f3ee87966c1ffb9d7cd80b1e292411967375a2055c4ec6d29e4c450c3240f4baa209bb3b1f0fc663306e6115ad3d6b2afdce796f9e9dc53827836fb19f35ef8ee04c4fe4cd8eb9d00a7249ce1a03b39ae7ac22085990d9c36f7e1ba3c72eff776315c556eef67f297da44f94176d65340a7711e2dcdf411db34846670109d9a65e185fcc4433dca42658e1d852308b4a867e2a5e00bed36feaab7fa4ca8356b343f034cf8dc4f03193a47e61740b477d893cc4f5162433f7008a7bbb6c3b06b3190dfac3117ba3d471a8a5b91d7d36f9880a2570acbcfe3b047316749cdcd26185e77aa9f85ca70f7ab1b29016ef4c85155a5f14508827ed15c6f6da4579a26abaa72c087f3cb49d14b83f1649cf5a45d81d89833321e95239b5ea5438d708b529d4136226f25e2a1cb3ed8386a86328a0f3bff19e6dc52d472bad98f632bd2b982d56a8dcf4e6cdcba2f5778ab5e3054a94ea1310406d811cf8bff765e67801ff105bdc2cae7d2fe74d14ecf0f4d2d92abd996ea316a315e316a98094d0379a16baa25be64d3d9da804f9219037c6454627d3c5c14ec4862c9b99e326c9305f433e56aa316b5109fb30eb9daad680516242a0a5311fa5059f49031fa785cfa71d9fa4051f63a24049339ff3b45ee72f7d49b774af4e3e12883ea7c8660c4e8cc22eb8dddb2d2bcd585e27698a2def63bf06cfd45ed84e5b3fcc1b9ad75d8e5bab1893121c1e1fe4eb524d3b7c5e0e3cd982b7bd83adc705ba996dac54a74b8fac2682cf3d31e71a7b096b4a614e47e71defbc243c94e4ad856a1d5ddc058e80628de75fa5bb026d36f2b0d1a81424ef94357c02c3335232954149809a346c7ce9b3f65a076c52880256d9ba2ba54d7ff1ce867d1bf9375bd7225ba61ec260181408d8f06c9d2af17b1031a3df020b5cdffeb5fc55a5aa70c4a1668c4240d4d8e80328904f265d1556b0d666a27d3098b5f6a6a445eaa0ba3e5d0859eb22f6aa3584f9c989a4464c2635f70d144f3684aa241873b4afec239f7d57639592de37ffe00386959f764029e6e871bfb77274d7688d18ec993dda6de7a49ee3f999287a218c78d5ddb4af6af45383b3744623f7cefbcc25ebe3de1d5c0db6eae20906ee812330fed9f673e99f0bcf79145eb8ad15173acfd715335dd63bc95ade462f8c399bf3666ec594a2d712f6794d4ffdbd8d5ad1185a6ee26064a2243f428862879b5a5def6e1a3b52963d91670da864b4f4d8ab2b402c3ce2d4979ef371fbc9ddafa3655eb55e0c44cfb38ff00e57e474f8887dc2cd0e523adefa35d1f671392781d54f4416cca02c8a2fcea9f74b5d412c8ab205b871a9ad8209403ce07e017cd0ddcf1bb2b39415f1504161d351f981fafbef8dc9943f584625da43923a05b0d5f9cf5988b59a883134770f71e9fa33aa2af07241740c7a65c163cb0e8039df50a8faa2eaba8fae018334b94ead87a294a2c9b216616ce2aae249f5d3cd38430b2d901025b20e8512efb9c6844a02c21ca76263c3500751cb8181b97e260c59670c9b0fc874485d8e0538ad0f802986662cf25d1e980675c366bb601e8c72d5a6b5814cbce4300d44624b539eb87169686032b59fe8c95acbcc26d93ff0257ff8c4c06a598dc47daa6a3201044a9eb1446e96cb9f27c9548bb5ae9150eb4db8f436e316ee5ab206879216099992940cb2a442414d57e2a95ef1f4e3a0f9075f52e138cce977752e4b5f3b71d471d66a896060c77a29fe792c95c243b8821b2c82c1286abee51052cf019f15c9e6149b3bb73d1728a4cbf3bcfa2f07c37dba23fe9cc8e5418fe88d459455c29806ed4c2996c2543a349d54d84f7fa414c913aaec4b6060f3a1bdce3e585d5b2d9652f6526ba9db9506b10bbd698e546e1a69f209412c84c6ea76f2cda2439bd5c69b25a336515b2bba2c0c54e5467ad2aa51d223ea91b5652d0c4002fb7169d5571e77faba43672ba0c7cd112b4754707f16628dac3fd20eb019c58e99d0238ce5fd155a3c853fca50dbec164fdbda9c29f73a63f3ff75169769bb0d8f519ab28e86b65d0d49335aacf50625af2df198512961969b6347a5c6d677e7f57dcc374c08329508ad8b664988e76b9e515a39cc4a10e98dba3f21ddc2ee494b077818704dbd1dbcd7bb06d1d1daf2d27f7b124afc74d6147d74864d6115c453c693bd1d3760a922306e37391777ee80335335f0d18d74dfb6d727267c02067194ab3372d2eab3c89cc45377ca6357d5ede6775e7285de0340c5272abe7e413b4c4a1074f1a6e086eb2eaa94d4b293a8886197105d8b7893b27ef33922a8f229503a9c5bf0c62147756e194ca5395db271cd0453ee59088d3b1faf822cb49d9b8e7f42c2776b7b1517d348336c6e17d6e87f21ee98a676935b684963a2a93f590dc0a93db97e0f175464040e04ca9c3533beb26a5389577419c7a74b8408ede494cedff0d38ddce98ecc9cda070b9e3ce14a1c40ff8d659a8081f87404cd0362673add444f949cddae44f2f5b784ee746a8b75b9b1c290c6dde1f7604c7cbe7a6a5cfb963eb32d34bab6b10a74249c0955ba120498271f2d5099ba6074bb5dfd90f8a8ff07cd5c679f9ddf928932d8bc532217bae830c575b1959b75c652e3802e4695022be5fa02541f9a58ebeb61598d1fbe068b4731f44a6d74e552eb7d84e3e37b6870cf7dbcb848feed28cc4411f9a4ecd75fdcaa6cf30540c7c0c8da6e403b342b06f157c762c1755e2f580db540b2932a9430601da490f79cd42c00f4780cbc69c7600a1462011a6782843a03925cd33ca9c72672318ea9bc791742befa29e458a90ccb6cd36031b35a2302090ab735cccb9591f5573c420961f70c38dd4d000064800fd60a1eecd40529ecb9dfb6b7d12d16f53d7dca858642c679d4513c5054afe0675cc45046623e02e999be6b84a28a82682af957c2e412be575e8df8a7020c92e10e62010c4f8c398b7694f3e9b725d8d8780a5883b3f3ca700aee1ea544b04e79be44b51f6b3c4e758245864c624e073fd1d3577e2eee1941aafc01f199e3d3cf3e1125155616d915afe497465e2c652cf4554024e9a0346823a2500670a8f4d4dc911a4391655fffb0d17928e99c15a69ae4837dd19f7b2be03157770c0f74627cccfcd296f08ce0bc77351abe5b9e21760a69b2433e59d992fce56a49b19a99a75039b078388f6d5d909468496b09bce286817dfaeb6c2c421778bb8bce7c22f526727ccaaa753c71f16038c3f1d8fd32bc7f375730ac5f17ef7887984cfe2d320ccc197f8e52565e291ad789e6c38321f8246671c66c2989988c77567da27dd85e911eb322f6e5ff8bcc328cb0ed80c1c866a7be31a43c7c64739e0606ec9cb3809d12f245b3aa52b8c2d9bdc7449983120e8af7f38d3e891e030b499d37adcc08f5f05a6cd6157dd41d95e5bad95e116ab448b2e2a37ac835ef5903152393ed4ae96b02c3e3f4f56defc775a38bc57679421e90b9f2b4a31e2ec9ecd0152c1f0a0713422c4e7aa276b59cc92900ae3b5602dc853d384b6667a45a92b083351506b95b483a7416a30f503b7cd11d704c00c91f74b822680fed52814e65ece3806ef3141328115f6f2aab0ee96ce12c566f11377e4281d0a33272cf9fcf9cb7f6b242c1210624cdd7e4552a2954bad03d8069e820c4df0499bdc07d5d08fb4a4e098404dc414b602b098d01e3c09dae967875d13314dbbd885d993ce8b8e043d315e943df62b1710ea80cfdc04b74b1eaff80bc8ebd2f93c2adeb734fe873b52861d7e9361686ea30aa7ac4618000feb3623e1b000662239739589fe65b8970a14a862bf12b9bd4aa7aa168aad02c48746507537070aa5d70490c02280589fa6943c14990de0725661488f640846ead27bd09b18138210276dfd985a6c84db69e28ecdf362ab8603df3ab452db4bdfa07d423adda16273fbc7019d45d26e76c1c0cea6992d826cdadb59d1162d361bd5f9f6b30c825b38364bcf8fe3aaeb6a0239753b2556f121d98f562f6cbd42380c8db7b947d545e2aaf43e1e0164dd8ff7a7996a8aa468f42598e70d5872bf533e747af5b8f27c89ea8550eeb16d95ae20928f902c5c7b1ca59b8ca20caf562de0fb5302a7c62e8dc446e9482ac100d40b74ccc94e7746f27f2cd8f3bf26376e68c12a6d7a634b6f42c2d5fd6ae51d9f71b228a490f1062fd49a54d048b408c2b111db1ba01f3900e82950c862ca8c009d846fccd99dc36aca2e5a8c596d6feb1ad3927baace933373dd6dfec8fd67d38ea921bcb4bf3adf97131c339498bdac87f33a1820868fd262cd25511008517650fef9a4ccae9adcd7e9aad44bc98c27b596f6154a0d8cbf1f98095dffee337c4847dd3718d4209d91295c7174e86b5a0027c19742bdc556e681f0313034b36e9cea6606dce0ea3c47fdc5b86f2663e9f6176ea1988f064afa6c064811c569aed4ab988fd6decf104f8b07a564bb88f56559bbd05e5e8860c2c502e769a3b9b70b1d6864a79b3ae47e05d3c18acc16153862352b4cd7180aae51b98318c6de8a22957005603305de95e439759b2a4fb493f008d22b0b5ef8298a15f5bd3fb5d81b94f79628c3e6e963d9abdd41c71f01ba51bb5200f548d5dab8ae15520fc3018bc026a8258d766c4db456be73faa1e487a6c4754fb20ba13aaf74befd8791690180a032081f8ab91075b8e1569ce84906161e0262a2b12e82bfc92a500b7b46d4661a94698dd4b66895f90aa165fced05ca1759825166b7ecf05e7b6320a4325149df1e1519f480773f46f3d66cd54c73a46e797ae608e684a2fa94f5df47a6fe0cd99b2a528d7f8b261abeb96e0e251b9706b24f4aa9f20eeacef240a88e95ef7b543b89e19a999a8ded281dcaa7e38ec540b523dabd869c576a1fd0e1cbaa326734662fcdedecc855fd6e08fd14bbca2760e540d0f8353a721599d88b5c965d375202f5cf49a309039b976e5ee37961134587102d7a47ae3ce3eb4e3ef684c1a1a659f083b6319f29489ea9ca087674ed23aa7e7677b08657c48bc44f4a40f63ff42d476733a8a060e905936be916c679cd5206a9982d7814b8b80a7af8b7f2a4f0b334e2e347c04dd22b928771973f1173f5592cb05e1f322f1a152c7cb07af8640da0fe0be43b39398b6e0d13052520cab054930841b08eb600d401d623074f0af4d6718ba3848a7f8df3ee6a3611e7c8a0497f7de07462f0615ccdec66fbde29e3b5fa96ba31f077165adf3a97ce979e912838f5191f979d6a5526c01445e22601a26b30c4e16b5e3283374c292856d27f46454e2b80a924b12252c699e2f70a216fb9f1650481dd2c7b1b04ff3e9f05ad02ec7c46025794502210c730c3129425dc646a221353de04bc4ee28901f5e60ab7151ed30797e59696412486cd6fe6637277176ea65c47f16c46db7fe6283683811a34e3a0b1dcafa91e99286aa0a1ebc2c423cbbc8108e62e03c28ec95e442de0275de932ddee8134ddc6bca195544d2ece401737c6e357e677f2c6daa82267a13c90d29fd43338e4c5481d0a9153aa6064e99976392131e6a2bbd186d15da1b935897e3118a2a139fa660fe1ed7b42716d4f6c3aec2e49a5ea9fe1d13a42a9cfa9afa24cc2dbb9907dace8914280f501d7819044d6085e8823042943ac97519dd1ceb7637828daaaee34309520f5922c72d2e822d01c199be9699cf70365e9aeb556f8dc9c7544e957d7a6866e6cf497b9e64b2bf0d5a77885b5a1c6eb24d8ea0cfc94f8cc12254d82645bd44fe095071ef83976c7035f4496c06c463141afaec39df1cc848495b0c18b75471a072a46b375a49bf9251cd6bc3c3843943f68427bdd33057f794a7b835d7971db5b1499d2a51a577b08b6704223fda4b82752ad7f4fedc4d81921ef17ac64b50ea1078a42ebe24427a911376815b84bd0e1fcd960f3f98a0e64e5e597b005b1bd6bcc92b46e52e1f55940f12bcba317d2fbeb2109e10e85a539c71109c5fa4d75aeb469c909a209bd1362fb5ce8c38eb9c854afc2791ac6e35466044b1241cdab8b7567bd1bc962a9c055aff33828f4f7484e109132d41cd49a59ccc1108871a2db6855714094e679cb73c56aff6f709110e0e98f7f2731ab8b8a8b6104c4efe1481d926bbd59c39b2579e20a0e9b6cd4d26af93393ccc97060a236d8cd3474de061aefaf8791755cda8046de5a79487a08e308b553ed26718dbf3a87aed1920e7ef8a24b017223a69935ddb783c62e6c9a0b5f95c5a87e88615694589518da2833f18ccfb2ac8e333c7deee42e1c27601579bb91a2c0f5794635b375432cddc3c830b1293a46797f0844a970b4db9cacef04f2d3027acecfc0cf62d18dade32c903f6570e4aeb0b8d858f036b32525fd6b3370dde336559684f3b93e0962113472946e041694fcca04d5275c4abb6c1931b23e705417f839a9c0d3a6db8b7715ff73446ea23daaed913d11886fd74133e6fa88a56753410ef3f0f780aa1d8863a3bcde15935b82df4a90f5bd1cfd2ffaa7bee84732c7137d1ec168b55a8bb00a1522eeab70d7f6c0a2d6c345097d53a26e2093d8da31de1c8e8c68269a93d646db6d6b536c05aeb276fb9bbbfc56243edb3ed3d853593b8d8d423008ee306d637dd55f829b3567c6c36ca3e6dabe2c56876639163ea03d9494002d3870ac8530999703a7a4a09400de5343eb3da240a90f4f532a1cfd2158d4d92033e284ce33387272c3dc9cc0b431f83a44dbc00897555cf31a329ec022ae649d2a4ad79e47b5ac65ea4fe58867359ac00d63707de44e35bc4f8e7c33481ab64047a3e3c694bc8ce97aff20cf101c6bfa2c6809ec3d33dce29788ece5a68e1564dbc109039cbab296581a57944d055a5956d512fc8d60fb536745c55391ff8dce3860b6f0b0f58d3b16ce6d53a8cbfa1e3e53327abe355d4a602bc3929f18ae1503106e0937e0fbdaeeeae1d65c023e552953bf46088c54b6690cc9ee7d12eb22f2540ffdb018d7b47776aefe9fdcdcdfd55b1c4dfae7762999784b69d75585b8cac2468e9341e9b1d7b0883b95de483d2af4898676adadd788a6cfe7b8268cf0316db551b28d7174fbeb2df8de4d91cdb4a0948b9554b1e92f225b7a06cb899fbc30f92d66b44b7ec8bd7c3b31bf37ad096c7aa726e1f598a04cc13db04adc153caca042591eecd067454ff7cc3c74092d83450be09a3de07913602815977d46e0553591d3cd9092999d7118114da2f4882656613c99b048525929580b08795990d3d01061590f8dc29c2fdc48a96757a61dd3b793e276bf524161363b6b717a58e9e4c1904107ca73d31051bbe28e4fbcf89663a7b63db638d5e8b945956d46816ccc9679b2cbd228ae5948d40856a0685b3aa7430b04d33131eea6336cd0f092e39b6457fdbe76fcdd0e0feb50cad5ea8d41e5aaea4f72df87f1c92410d9fbc13ddff8e71eecc756c9248bcccdfc1c3e6971ab7eab31a782f3bf17db4a9650865de61dcd74e85611ca6d7fcc2c1c23d6fb6ee7fa04f800f6e48d15070b1567d3404df10c2040996489055cd1a5cc0df517458ae71b6cfeef8fabbae6f44f1c2ba48b796d31ae2eaf15cb2edf34691887062864786db213bc110da5f82b2f2a9a2bb31320461ac21525aeada9b6337a8a8a3911155373df94cb8f942857eb471d358b8dff34d9c188a0744245a2d275118102c7c6e4dad370c0b0b83f167de09c0d1e38ccee5acb367f52646459d85718a2c80ec36583290cc7a9799b27f90c0d634ab84422b61e725227d70f64ed01334ceea9e8ff2d49a50190f77e47df84a645db80575bdb5243393178ff28e824dc1e3feabd7a2e384104d62bf832855cca4476a151905584d0326887b48ea403f07c40cc44079b3c994954c3b152287653634605e03ea791aa7e818f1eeec49827abefaa9fad025767404fb26227bba4b19f69c95c311fea170d2b4ba3bbafa65f98f5d1f23d6221aff3b406da2e55031a3c59fa75aa2daf73b005a2e5357151aac1f95412406cbe90bf8e2f88f64d7054139f65eb76ec1974556d127c9d9ec1758bd9c5e13dcb7bd44ce564c5431b2cc894524184f860f257d69f1e93ddc0c40698ff385240c973dcf15ae3b27a2fd876b54a4789690b633a87fad9539682f696a3f75c9d3ce3e99efdbd8f879302a13acc6816cddbc6a9665973b38b52bddc9ff0d15c605be245b07925ac499275106d46c38598a85d122ccd8365572f6682efda1872ee6b1231bdb2fcd5474c3508d6d158cc733cae1c4ce9f3de4d9e0775ad742de2ebb8cd12cc74cab976326a1f62b237af621bcfc04d638967fb63c189a63a6432fb2a86155d49d3a5ac479498adee1913711eec8b1814b985efe17dd7f0d146c2fb233bd4ed724bc55f5efd4bf4b23248ff9027e2d3f4c33aa11aeab8835a3813eaf3711491f9ce4411c33caf3699f85212559c1aac469206440eb3b3d0562c311cd6f018abb2f0edc03050cc3e8628c1e161abb22f2bcf10757d001dd03279411af44a3ebea96fcad71319758166458af597a224c1a3e2350b63bb570dd2bec8c413885d8e3dc95775f9e66142f7442ca7a4dc95c217325cad61faecb37098c9b89640bd3c9d13e81d0e4a684e2abb0120d930c9a673491a097e34481745dc1561b203b1c1162e93a1bd372c2ee2786e42e83f50149cd4eec12c605f772c9b2c7735fac722287dc0b71f948bb655db28b1cf4f6b58e5536b8b5c1f0d8e0e266cd21111f8a0c0d9f334b6914c13b27e065de19b88d7c592c766b6d21871a4cbf2e262be9c2944bef24407b6624e1112bce3e1f96e46a5dd2cf11d48575f942c6d7fe1b9fbd02125e21c407cf6b1b695ee50ddde20dd99227aaf51d37b8e56906b56f1656b3de60c8b1a0edd57a94a51f90df58391cb46d45f86378056dfd954df73eb798f5f6a4ef11c37787c726da9a49daa6dec933aa75a0179de0bab5f259d3e54a2ca4ffbcb95e3bb50387168457de4f236b97fa93539f465b56cba9238e53648cc51fc2361c2e8577200fd53f68ec9850e00c5122e26714ff43e3c564cad9e2abb389721a9522a2443e4bc59900915910a9c6179acef2c229abf69d7adb6daeaafbd0b7c299e4a8c3c124530feda8cb78762bb4e216ef815906ad7a364215395b78ca8392cb059586ef191c29dfe1ae8a01e0946a49dfac8d6317e6b47a2c16f0c9a25bf3cf4f4704a47e9d0e4f694f229439027897f5f2e0e43fe1654ad44f0c462a539ac627e42008a9425a26899fc982258a91b2da13b64240a922a58ad820a4498554ec3da20261196c38612fe4d4bc6a518aea5fb6fd3d3534878a94b1919636f51a8ca903aa7e8681354d2d7d5841664f14935e5849859212bbad9d883b50d6734ed3f004e480bcca5ab7c4959e6562b7d340188f6c7ceb8b3bad596d8667f86c5868a0bc8a88f58f2f990b6266ab555d0860a8b73caedef9cc71bbcd53c99e677cdaeebb1e2634b73e07fb43fe1265b1739dc6e1b36f341e5df2bf03eb4e9b75932a558bdbe35f4c0a8b9ef989cb697c0e88c6d66a1402210a3cd63a1802a08fd007fc1e4f16f73d2f4fe8b4ace7ebf6402d9a24f50b5443238c2360cd59baf9abb2d046a859deb54bd883c9c8db9680dd7524e79b0bad1ade5001e1cc9435a14285108199c05047935b3582973333e98d5ef71982ec4819a0981b24359d82048a593cce761ad64869ff420b4fd4c658cce944c4776f8eb96be5e973d4a00455fb0de93a10bfca339b982356fa415ab9f17cbfb58aa866359ff763e6e9f7bcea6d99ecfa3ccf81aa0c766d75569fde1c4a6fdd16c43c026cf543dc31ae19850e4013e86c22a445026ec14c50d41d0d7d67f94b3f67d4abfbddecc019f6ee97e7d018bfb4a0a8def6f980b16e711261bc8a4392cbbec30e7f47e8f5a27dfa0c1177c33caedc9b953b7b040f9704817b49b2c24077e759cb73ab9aa7f88ea7302d3a40d8b7e55ea55a4d46c6d152291446289f866eeb6669bf8c13a8cf91f9c03d4fbc1b9dc8407876d940209b860c809bb1128a4dc6033aa8ce3f30c7d9d43a18961e24291d098aa3ca5b85ea594dc885bf915f5bb212fdd650300ce038766347947f1cc69d44caa12962bb28b3f95b30f21c99fd64cf9064e0497752915a84b481a8a089cd47ecc312183e4869c1a441c6c21675ad66c21eefc0d8890456f6b3a6e6299ae792b56f034100cd532822a4ea9a92c263af5260f94b63394663d92b7a7472cdc80a7056b2ed27e173b10eaa9fb8a302278773800a549485b5c6da6394275db5b984fc475b3f0594f8edd1c71341820837493c51c231a9a1b7b39718ae6196ee79e3ca55bcdaf59c837d3e420b0032407035484046dfbc251e1711410627060e8c16b25ca529240fbe2dc8a9fca2654ec9b012faf315d98a876780f9300694bef6cfe484aa626e117abfebdb59f9fe3befc0b3175e1116cd4a73402f11314045af5ed61f102e681204c5372d47295f4c4841725a090593bb27990032b1744d4db0306b777b51ba30a2f811042b815ffc35497f601ad2c3b5b8753b805eb4d47c8430d56b9436f348aa61fa10fa934c99e6af083357012d6636d858bcb1d0676ac1e7b8e89a085a88d521640b8150350f34f1bc53388bb4256beda77f718b8432f0044ca8b0420d2f47b8f32d94ff0c2019ba96015f97f30764fcd3f0ef04154365d1e6dbb21261cc71ff78c347c62f13b92b5d46a14c8df63bf8adfe93516e3274d73887a0befda964e4225f9dbd1a927c3f7ae22a882a4c0a60ca25e2842edd528ef4a1c3a25f43ad0faeb11c9e30470c23f030746cb4bce175defcb8f0e480c96f475e02af8aeffa6ac2df9548a30e55f8b488fb586dddb7040a63149b6f6dcc51a8da83338ca191856e707cdc810127a67a2c0127510509c2ba4b0759738bb224223c54ebc04db9372126155303f2fad620d2d7a0f3af9492ab58c393c028a757150089183c5c9cb8068dab86b700bd8faf01192f281cf3ddcbcc7f0bcc8a8709dbe538f9f15c9b7683d262e8987573aec0b33e26b4b5ce7d10ebb0f65a1b21af805508d018c875be4b2dfe97619743920b2f27256c180bc2d848d1fbf0ce434e5dfd38e56de281e56111e74b8b863daf7c4ba44e5ce3c0051d34cc61cb00722e89c472e9aa2710c1606e6dd34a23667fc2e20f2a026c367e329d4fdf0f3b53bd9941eeb0f04d9fdc7b2ae1e37a161c0eb07d1081ce4a6e9c8e12a2395a14176fd62d37aa671e05e74abf86d339d59fde4a1ed3e38ebb1a471d6142962f2351d771e85dc8438af1072382ec06ae39313d356446f2a91f2f82c52df4aab805c1ece0520724c9a08d8d4d31863d98aa233a6abc53c863110fc967f233daa8d793828422aebc2318dfb623db287a7aac05a5adc21f934077ef69bc58f64e2ad8190522b282220c8e23cbe8e32bacb268b12bcafb2dce4d6e945a400867046c9c3c5ad29395288bbfa7beafbfdb3785877ba056113727806c8c41b54adced024ccc45a5fc535d39deac049ea332866d0f22e9ca011bf454f5405db78682d09145bf2db15fe473494a451dfd06b7a2e07e21d5159442a7e300631c8051a731a373ca984c1ea7680463ea18487d68f8dc2b81659eddf9362ac3203d0852d31c45459abb88584a279c6728b842e7fc4dfa6bcee4c6861a92759b6f869f092ea4e2b1042302a509b0d48820ffef1c8e068379e3d8184b1b04ee407bc10fe2af4b5a21706174d80f9ab6d2512cf836c50689fb1037badb4966f62be8cd05046e1e6ed8e39e3704ad39ac1afaf52a37888c4256725dffd31c99d035adfed75ac0ffd2638be471c8ac91a884b64d00cad35f595669f8b757049ef80c7f1719c512cc940922aa10804eac33023a781bbc6c4d7b44fde4ff05dfabc0e5dbdd8f41414ddd0c1645bd36497beb7d6a9328be2d7cc4ae2a527c2fa43cd6b5b687ffe9c1ba46816c2dbb6c7d8553ea086d97803dd5d63cdee4bc706e699b8b7a30b1718bed7e1b1c27469f996058c7a45f6e1575e9a9773b81ba0192e4a2fb3d96b992f7796856679a4e58bc85e371862abed4045894aac869b8abdcd731518af0342757f8d9b19c09b3e5d7262d2a1a25affdac99540771c3681aee7b7b5875b5d426ada3857293a07eba429d68886b1cf0a35b748b44f9e931742d4a855907b1c20f4758e014e6e4e5da391b78955c1c0935b3db41f45ab0a586fabceeb0bce9bbda725909dbf96a27410d93fa9147b682589f24258597dead650599a2ac2145d7e18dea5d6d46d944692fc2e9a148074d3efb3553a02240ae0b09a34b13e26b1d92a6f0e17431d60266f3f626741c80deca0076bf14909d0b63e4ad025ba18c987ccde493efd24b09e1860b5ace34e16a2583a710e1ffe4803b138c5a649d988c4f52895a28750f3e099119c674053a648940974a3fd615ced2ab27f35795e584deda150f642053b86157d436ab3b8376b45baceaee94706c0f04bc1cc7399e6ee7531cc864224375878dc86b25a1f2d996b533b6fedc703b867ea3f1180e53ffa5f301ebbd84631c4badbbcf240aa53f22210509e60bbdf5bf85d0b5d87d8f8fa65233cdae68ff93b1966814143478f384fd5f7f78c092b1ebde8e0b58431be67a5d43a0e82fa22c116f485b3584b12a02437c2d5d6cb4d5125e492665a944e9b64d00984aadf292d566a700c9c0cbf19754b1344d89386964b344a994d81feda592e3ad4c574b0daf9c906d287ac015996d523f3ccfa053edf3361822b5321794a89353ae98215733158460d77a982904680f4e577167461ca1d0ceac90a9f2643ff9d748645da43f035d5b267901a49d79d81e1beb9ec0fcc05913f846417a618ed7cdd227951653246f6619b4022880ead95c7ed32f1078963a6d930d3b43278988b61fa78f396d67ced519769945f6f5c42793db6bdeb68de0eb36e9430d98a0e53ac7e284d1a6298768a676b18d7d8fa42937cf674259f0a9f6888ebd95f5dfc32ffb22e491098b994ab511e75817ec348f64d1c5cd163a4a9e7b96da4dcb54f67ae3bfd090a234529eeeb0739d7ae200bea2040a554c4fec3838d4931e29eaa36529a377d724eba325e0e2987923845c9b57442f4083997036ad4f099dd9a30417e671904b8b155204794030447e325776f5d5af9d21c2f6a0f0f1857da2382581a05ce217d26abc505829c311416c46e46ac6b7a66b0bfbe8a8e99f8dbc9a442e51fb796ab1efc3913c280e8f8a5481decd3c117a2debb84c10265088214aaa87d48238380f03ee8799e8d5ddeb23aa5ead64806541ba31aecd3075a4ce75a11a615069612dca921cfa2885ef372d8428914833f6aac51f115da95199086f50e3922acc601720e38f5cddfdc60d9447a1c135811d280accfcb8de9dd1aa6cf5e6c05d0613664d9ce238e6b0d0840ba1678b9d7216fb613dc49810f8ac9c647a42a43bebe46c56a6c2ba439532d459417e7b04eb7fc2493a24f13103afe39927c553c8e37117e0ad9abda0f216ed9d5649516ec002bf2d6565eb8f9a0aa9ffd6d161cb7b10b0591552050ca7321d95e0b00cab4da702044ee9cdb3d083d60eda1cd21edc568bc65145c89e71ed6722394140760286c639f1a3f2a573e32c429042b7c47523930b3237f3b9975f0b1a43da94b723c782cdcb7702722ee03cca58551f0543adb28e3755563639c3c43cd0cf83a39e45b5469bef0a13bd39051e3681feff09fabf9f55f986685e83e2b0ff9c8a9d47fb13170da29fcaaa420abf94bd21c2de6d191654236f172953bdcf4253dee3e46c0494b06072829cf7c29ea0df881857a9846ef3ac42a551870e0650131a45f9be959304160a2c0e313c036fa41e8871558865b29ebab6029c153aa6682712cf9f02b6d21e825d1b5a53630d889f906422cde9875336bca4fc6f0e10f0713c97b178dc03ed50fefb2555a4060e94deda8ff262252b0b138d7b9ca46b7d2c91ebf86d50ad8a2143464a7a9c608c3d7d43c0aa65a472d114ccd02655e1e2347e783b8adb8bdde862c6b5460666ed168316f70c22cc18c8a18b156dfd7463410c4e773162191b76b573dcb6fc307d023cf7b82143af76b8455257889e326e4fed50f6f524c81229a02474bfa83f786fc3a74b5b04f5b965c06fa016b8661a394d4f4052e2aa9ad80fa69273a653d27513f5e395ac36a1a90a06aaa00f680ef40b47747a277254e06bfef07914497e07cf3ead005a52d107aad592b2a30beb14c457412ca03076bdbe332024b9f02123b2324ba030c47af19528d6a9b17035a1cf586157889ddfa27bc6eae194cfd84a188208036ac13d02f8e3f36daa2f0b6e0075a1d5e1cd694a7db37bf2e285283b9569a164d94b1b513a442eb5ba530a7fa11f9eb3553c99981b75f1018465010a09d13b5a400a21d60654698c0e02254511095bb742ce4a46881e453687d225ef6503a24698402928dd97807b073afa09f4ccc23b867acf01b525dd05ee951be539424b36d4565476647e7e0d4c98fa294497c95771242511c9953ba594026403a703bd0303f0aec6d25e436d0df9e74f21c502cffd967389aeff2deb0c95e9e77dbe131f67116ea4bca182e75e63711615cfbd26e32c44cf31b8a047cc5aee1ef2def52deeaee91fb4a03dfd3242ff1eba082058cfd49aad6102cf3d3a81f0e59fded45acdd45263699199eb73fd2233c1d0358722f47f0e7b1ab420e46001107a40e107fd5221020a427d427f6da2f613847acd77f2531112852b5c457b0c84170833308014a105423dd45e0001a80085e96baccf4f3ef77b686b306fca6410f9bc6badb5263709b5e729f028dedd5a2cfe85ad21f5dcefb6bcc1d7fdf61b6d6be89eef1c9ffb0ebb7b2d74cfdefd4a4375f5105084feb59c76fb09359c7e0181fa41cbe40cdaf784fa47d7e92415085184ba4697de347bdfbbefbdf7debbf761ded73aef6b0db792a95b87f037574158d81350b4b57dc81be02eb055b4ef800df4e42c3226bae973e4f83953a00367e9c055b4ff4d285b3864e044ff54480a55ae829e08895c45fb2e34d396a5136f0a87bf9243067206edb7f621e020d0bdcfd0b5af2f1f0879c3879cc125b65c207bcf592ef8ecd19ff0f45d353d7437a341a07bb7c33f39b1e0f30efa934205093c0082c1a18bf24f0b4337e59f01105620cc5c458208f226a6e4c9304d871e6ce205a10f5220cc5c257394adb257b1be89db77ed4ded3b60619b37509ed73259ce39bb2bdf834d24c932e79c73ce390fc92cf749be7989731fbbb9de50d1722d535d7dd4c7ada197f75d52ff1e508640fab6f0839c240220e02ac1f3f2cbf36bdf087d1f779907922449923c4f21f7686fc1b059287832e85388a6b9554e0cfff43f2119dcc4d3a3c2944d3c4f8f92284afa3cea899b78fa867e13cf9ecf61d987a7900f53be6384d2e7eba1272a745b3eea3fa028ea37b1d71bbabe114a19ff5195ab6c5fefad63aacfaf9cf8bd89a83f87e78930dd44d4ff50d4a33e3d3d079ef2d36417ea63b4b22c490f64b7efe1cc6e42b72c7d4eb2d6507dfe6cfb2ef951dff37ae896fef4e63e87e91ae72949b21b40ddfe02393c8741be1a7ba0baa8a613b827f5af839fbd4a41f9eba083ed3d94431504d77b84e94d10fb347d2f4bd397a6ef807c7959bf54f2bbe99640aa0fb04ba1feeb97babf9ba67acd3f6fc81b4eeff64e3283e939bf42336050b3cf265a0035939a83fc4b9f537e10d8bf2c85fb9bead6e049e066c91a941fd04dbed63e48fa10864b6739761ffd39ce81fb2e3104dad3db73903f1165b3640af4fce94f101a649f92d25152525050be5683a6ff20fde92fbd1a4ca2ee106c10e5e63b68e639f20c61fd1d1fdbb6d71e84d621a079a33925850211d4b8c64dfb7db3b6b7cf295b5ba1fa6712cd5b1bc916afbd6b95a43459895c4dd508351eabac340406726f594d5d763e582e2272a4b4751fa4c094b68e0027efef0583ab281481b7b26000bc719de9c28b8492d3925668c0da18053720e369124b39e06186b7468e75b40e1d22bfc8734b7834ad80957410397815beaf268148d6012b8193089961d254e56b9aa67992a6699ae6533613c08b02d66836c660682648716242b474d4cd48b5e8b12256b3e185a522b569f2af9757e6d102c6a06dca3238016f06ef0bbfc1c1972c06fc06df20be1948db7b059fc1c17aa788ce7b595e5d938d3009ca6bd94dc32b046b11697001de24305a0a78b30dfdd5d32f7217acd9de9e991a181a132fbab49868004789d0510c369545b394d63c693cad020ece38f2101fb0161cd7f29e80654cc014bc46f4500e9b82b58c0ab9ee8257b3106206f9a6f924c17b046e5f1a40ba4d2cabc53130d90dd844f24d9389460d6e92997485b9b1c1ec0b84c74c0fd1527946005ebdb503fe01eb1c9717073c8020f084177db3dd0c38059cf37a9853d4161cd5dd99d4d70c90a21391a7301a9d6fa6c626f53dc2e66b04a5ed32dd0f3403bcda45e6c25ac399d33f62194ccea4faa60955c1db84ec01e71ca68e9ed294652946219df13cc69d8f23f0beb17453ac834b58c1c00830205f181a892ec107ce3d90c0333003b860df04af05ef0254887a208e665541648ee28c762e7829faf1d62404c3586395caa4af699ab1b1eb9b5a8969c41258c7789201cfe47d936943caabe4cc6455765f3248a01be104028c1f1c466e633e479a9f6fa6314cdac9c2ab8ff21d91175df455b60301674c0778730d4d97c760de22c1000b3b1e24657734d63ec8600a418303049515d3c4a0294c7bc49b9d973b2686f1714541059ae59047f5f8ba2623e293b10c4c92eb1a50a6f87f3fc73175df55bd5c4c1b8f9652931d369831a2d7e27842f1f4229ca09c1c234d0cf3624ccb9349833bae692b67f9735ca4faee0a8a1417b1d2b83814bf2cc545a89e1657b51e17e3a2daaff0735cfc6a59719c69adb5d65a77adb5d65a6badfbf04c790c1445c9b22cb52e75699a5a9bda2c4b7284fe3d6cbf85a730dd423790334972ce39e79c73d2d47524d4bc893ebf498ffaddeb0df50bd71f6484d91794fbda73bf8764164f5310368eea6aa8cf7d053e6cd5d2cd8ab70453773726b7ba0551db2166a365136b816cad732b581253ac15d35a046e8b245a26a51c164649862d82d7eaccb52284acadb14c37d89cb11c5d2b8b2e2a264e6d8e113be387bd4ada698392685a1b4bb8ea58a73716c887190e1364ed08af76ad841b5621a0d8b923b5a0d60689e707a9668d6bc2964012135997b4b12bd45a1d356080ad46b4da17572da26e92848658213d2c8aa3106547451292c888130284880eaeb8150a3b82c1265171e2b8a4f5168de8c2814bc490e1b176742cce5bcbc376422d8659bb15c6c25b3ab2da362af6ee893d7154426815d2436374d39e19c5b459782b10ba15eead4d626b7ba8ad6d666061ecd216912c2554acd2d34ae4684aa845ea8129ba614d14c5c0e159d1d5786b6bd8da1d6a5b66ac8f3096c6560b96b526a1626d9ed8baa39684d04e0f2b03df34c131ad11cf896ec85beb836d0da2c64a99a185456db537597454314fadd1910821d6ade7c57bb3dabd31ec0c1e0ebaafb7968aad5da1c67e99b14761ed8fad3648162b47c5d23c291dad7442ed909e36a21b128862d821783d746d9137168b8d75a1d6ce99b546c25eb6d812d4338b35aa27552b03d5c648629bbc297c5a1c599e211b9f2198ef66b217efb75e42755b45fb5308ee734f72ceb95fcb10856a6d32656d096ea25ed93c88ea6f200df6de7befbd77cb422da35a2895fc390e72fa466aefe738c8e8b36f90cf4f2135002ffc1c03e501cd7d37c8e9d03393e72e39a9b5d64057035d3ddc2c5a830ef566ecedc839c78e7debc66c4153024de1904049381c08080728e767c93118f444baefae90a5453874d8c40d36f187ce8f4387ded36e52b21068d0cba6f7ded9a2fafb39fe59b11e517d3fc73f27be1b6455fc3c9d424733e84ff3e778d37befbd37f9bdf7de7befbdaf6c3e77a78fdcef6d383c663a043d9104dc77577e5e2f3e68c6d8470ca72505a2f8ac605223e2770b7eef765267ad0b16ed870f1211b3f7ce06f9c06b931f1629f6e171818979b4beefe7d8c7f84f7e8e49dc91310f930b53cebff464c24ff386adf59317070764960cc28abc54925281aad1a6671401e4c6a21a0452a4428a73442569c24e1ab34f922d474ef5e7f898f44fae2351cdf167e6c8633cbe7c7745860c08b49059363879c92500cea2780e2d6dd5dd6797a8fde77867ee0351309cc19fe39db4ef06bf0bf4912f90e47ac7c14bddc9c2513110f34ed3778951db7479213949f2221bdf4e4e4f6fa78873fad64d357f8e75f0be9bb5823a43787470f0e894d5e888cde8c4a0d17161d4c992f13fc73a423a36df5db9f9e2e40c8b2f97a21a2485188811fc1c077a89482f71008f874d13ba9d266e45c0c094f47821b232922d3c399602484f959a748a9e334d62324f7285d592699ae6499a2609df348bd44dc54d92e4fe34eea1921795584065e9b95ad672519471c2520b2222a2c91312115d1a99119822356d175255563c463c9c4ee850d99da1d1c518218365044c8f9a544cddc854f590bac9f17089972e256f0da92b3294cfe047bf4ca174a625e49a84de56dc186303f2a064044a46d09b8e9606658ae22fb1941871346d44c58b381b5c36e6def062e24a94f5a8c0da29c9a1b2c4b23113e0f9165392ae33b01c533e8e60726cc3c248b5a4882cee2ae6c7c7a0b669c5a4a8a99b26d6dc37cd1445df4ce12934a7f4de7bef0d90537b07aa4192244d4faa64b6cb215602450c1edd8fb21b24688c4c436024c558c5f4fdbdf706cca95925cb5c52689d39831561cb266aaa384ed442929ca72b44929a97da90b589095dd51109230baa364e6456157d097e8eab767c134dcb88ba5aeb93ab70eeb7560c9abd90a855c27cc02c37558c620da8e45ead1af1b927a9e03ee79c7397ab949892a9f2aa68522d262699c9a74ea028fba1d8b1b9ca0d86f9ab840a3ebc21722f3e323b710b24437c2cd911f9805191573d1d5a4a85523ec65514460a287ef81813922ca7dc7e3965e497cfce3eb5638a2c889650067f2a040cba4421c9605d8981295969e0aa53348458e2194642b4c8845cf09fe329311e4f897117132d1ec86369d0928b1a417668b852a09bb4bcb999a9253596b82c45319948a1648294cd49549b2faa489afeb8244595f528b474cc2529caeb4ff8398ec22ae282e4e3529c11d6c346c887169c873bb6172432e2aa9d36d26d424ae2f4993fc7264ea84b28706b20d3a0d65a6bad49cf94872e44aaabaa1b78ba28fc03b7061e2cf3e624d9f7f9e3a0e4c2fee38093bd729ba8eff780efa6727f6ee226f6dca350819b13393611f4f90f023a13b5d79998bdefc73d8a729fa5d6c83c984066d980f3a18be16735320f2cc07e3d6cc9643c68810ba0003308fe1c9b80f9fce718aac97783df0612600f9196acadfd51cbabb1220123d26a435c5145ed24b16c433e3deb0fa8104d50c5b8246c521c7b04480ba463958b9584b54b30c254b0404e942db561efd80876ea0987c7534cfba264c8085be4c63ef1a071b6becd6863aeb4ac3314384d308ab07c581c4e3aed90d34c7b95c7e6a063c586b4377ab44f686c1a8d1e31da142d5854ed78626b9460df80e49a8eed4c93b145c164cd3b620657c3c67aa51dcbda982f242ed8a8143a26a490d82042ed9b887606930d8bc7f40265d7121561b37eb02372c499b525c86855bc80c362e9a46428b53c88582324048dbda8144bdbd34986666c0080022318000018088301499223318ee492ec0e1400074fd46080603c483c1444e26030100883612806a01880011000a118868118042321b2e801f0fa88d9c4266ef25b3125ceb6f377b9624ae864b5607252a5fba5bac39c02852af3722b8b2ae1aa1209d184514d6f540391c6a953e4cca42f79ef94cba5aa34f97d827eba08e1019936051d5809416dfe80b90099a29ae5ff55772bc1632108f6043fa6da858003c0d1c9e84ed90f2f3c7d9ce1b4772bea188f4556742a1e122c8932f8ba25502497de4beaecc1e190d2de1c10fc715f08f76623052e0331a17611df5c69d7f2a8bc12f843144ca773017c472710ae1fab222dfa47b1a6e58775ecb7a27f6778d4158ef78231820058d5af95acd97c6492ded38b2e7bdc8d3906c9f4ea16fa1cc25bb6473756fe8f40a90dcc11e7a4b6d0dcefaddb02c70c1889083408aca6bb366de698709e82f4374ba21996db058f39a7a74faa4e60b88d6ec8e9d3c683f47afc777ef8a31691ccf5219242b4c08f0c31b8e204f489253d203eda0aba4147912fbae21e83868022e7d244e37d5d5f22606bdafe154750591aeb8fe90dc6e9f5f8712155e822887061163093a42ed359452d3a2cb14f21607b6c64e7b85c5b082bccdc10357c68678999f201b880f4c2219b16b0332de8885905c458f726529155fb06bc1e13b4172d3b2e79b3ea6f8bd2e12e7029ebc154efa1a7e3474abdbd7afd59481b854b0b873c18a47fb39a24dceca73a05f8cd34abea649f1cd167825503e935bf13cceab597d1124f1c020415c4a50b4c1282b03a698280cd866ff1ee6cf782cdb8dc434c1d606d2e53c5b7c0d6e1b4cc11a1a5c4ae3c91ace35009a57b9f3514a87fcf95eff129dd55d1eeaf5820c9d936714cca80e35c3ef868be166be6f4c3927d975bee1259787a7c39fb450e55b75fcb0676377d3450e901eef571de19e2467376227a9208788231b7d4c8a2581a453ee48aa56d5c2f577569e0ac2057a57db3c214791cfb687fce4dabde9008db3dc55ac73a86115a04d56cdcf684517112395e315fa2a1278606fd710904c11b8e53154895eea440bc3c3c579c7ac0a921e191a9e520cb31ebc4c2a39be88b3b7078d165f70dc7ef4bef11133723882e53380fafdcb8bd4b34b1503cf72d560a14c022b56c7290d470eac17b1034b65816a571d43eaf77f59404d4a9bce7b1f03f000caed32988fa97b35ccc1505cd90a5c242471b825e79dea381066259e004fc996a6a6e3e2e2d5e985699a6a9ccae457cc41cd3f7089528fed33dacbe5829f6623e1e1ffd2041d3c6691107f119e9231f8bec9eda74d0de4d6d0b4c2e39530b1d72340f2cd6b642960ece433be3c53cf129ed450fabe4d245869c6839a7b71273a425b83cad0e9be2461b280eba84e2541bd0d1fab702e96497394b4496228db914d11ce792c51d4e8fa1642fe0bf392ad4b3e6bbd50fc907690462fe3661f95a6803c90a74926534ce7afe661612485435918ec4763e367247f23188f0e3a6b3da7a4fb6e4f90e8397fecad14bd124d49f91d0a41946dbed32d9665dbe8afaf3b354b1df41c59027a8fa08a9930256c0db0e29a28fc3b438e625e002c748482f7f19d4350b8d653cf8e6db3e184465cec367f325461917d01253556a802739c7502ffc52a7c8c756c114d57542cc5f91fe4709cc5e365d0b80c1debb4709ae3e5e21366143ee0cc7130a02a0b42cc4947cd86afe59ac96d3e48b07518cd1aa21262636d41703399c906a0d3179418340e676e25724aa92b0f1095105d80221663ee77a2cb41404c98251b987e76fb2888442a804ae05df0d46ca3e997af14869fc1ccab1cceed6d9b45835ee4f84d1732e8e704320023e4871e19af3f6385dbc787b2b755d79b78a285bc24e3edbf4bfd0c14b41bdd677447bdef9b4bcf04e60ff808b13de5e509965450e83c91f8cbe636a0ea5a32e4b7597d480ac627bba3b1dc0e4895c52aa3c124564568ac7f8c7838cdb9ad823f7fff6e5a2124613a3b1e42a21c8387528a1a33566e97456f59f49c925fc683b5bf74d1afc7c1f49765b64ee2a91975ae7a8218ca81d1ca1e31a5795b4320c8c014dcfdfb6fb62101553bd9c50fd4dc696e4e494cc00d07404dd474b1d72d15131cd04ba29480a8db3e17e77da1aec072e12ab63c515e18b2030e9828d397a35854fcc592c4a80dcf98375fa548a0c83c295d5da1b5239ac60b5731be5e17de846b8f04736c25c47399b0f9b74aa46184da7de35eab7d966544bcacb6ea78fe2ba191531c13690e4b8a1062b12531d470d1bb83ba7092a05dcb591cf01858d3f6e11bbd5e02b95531311a8ce06c685cd651bdafc0726689813206bf112ae379c815d0a82a624177411d706916e98acaf7c71cd4d26c8cc06da488a751055b4608f541d39b66044cb22977a4c53640843d03c445d1d5dd740718329d3c63060b64b9c230ad0f378c3867788197734aa4ca072e6d24936c7fa5c2924a91179b7caf5d1a6446946e572088ae6155589e67b698f39514be85844e1d914e9540c518e175c9b076678430fc828ff0c19e212b8609f11b10ea35100f7b98603d0b5f6e957f8a784b78a5c501a861ddbf72d4d1ced170338da5c8c9d942ab4d05d0e07447fd533d2d5b599fbd4071f2d98e1182d4ed72b4a4de642e6d253d61145dcc3b7b0c1aca01fc97ae4032d6913d40afd5db9e69aed66e4039e1ea6a14d08c226a62fe41a989e0d58d7b4ac76b284abfed231b287bdae97d690ddc3ef4b64ca75a543a7dad8063e4d1ac3c62581f7815ae3115341ad533a7cb4f26835681cda84302a05f84935e28c50d9d231d686f574eb79b6b46021b1c151eed1d860e6d01703c6d3c3ac3ed6ee14f434eb13e71f6d9cc7b214a004785823cf501e0761abe3861049907205cc39ec00ddfd2a2590f83e3d6963e3146d045c833314d1ec56bc94562b55dc3d8fc1227f152cc12ade6380f8cbeb5ca069f02ae2cc179bba82ecfadb598c9b1f5a089e0ee2b3229f6cafc35d89a2a184d796ed1d41a8232a957574e5eeb588d7cc62ed2aa360ad8680460d3949daf09f5fc1a3e18ec3bd57f02a34ce9d50693ede928c3d4b53b94d95ced1650158489605e162d792daa9642af2b997168c534a413d4d94274694ef2545d1ab995b9364f438686e4f8dc5dc28af82565ffeca8c522b1b03c81f58636083e23921f6c0718fc058bea333fc7e933e422662e989c3dcb4d12b07c135329c1b548ca85dfec146359b503b6cebb9b1a5eee7c4cdf0cd3fb65b04522454fdc6298b1f8e4f43a2eec1ff283abd454e5f4e184f5f180b23107f784c530629f1b229cc08cb2b00efdcc8c372f0614fcf6c1e287ab915e37f836ea23ce0170736f94bc8a2d6ea3e134289901b4dcc0afeace20a512c23d675a3a408e116f665aef220ceee4f950fa2ff9938c10ac390a09a0f18556033c0582704ae35ebf306c712cf6b69ad7c80c202acb889aa9816326e272104f9b0ba4744c230a4ec2ee8d05dfc317b057bff65b8d3e1c8ad1ae3cd4b49a56de3478a9f3e1cf61921228fd28c6eafb46d5e6935fea1f61ec16f32f8e442ead2ce70749baff2fe056c4d33a2b9d7042b34949dd5bc3b274c30c1148e0cfaea5f8e81c02e6e829e8138b3e6022d06bc96a320fea810bc866471597657c3b35da96984ed7a7f18c38c71f20dd8916e6568b7a2de3d63dc899e77191d142466a61d1186cc03cac48f94e9883e768253e303948ed639e23ba1376002609956fed56c1c18d8ff4667d36d406946fc0574b3c181030f37e369ad9e7405dfae499227083cf76e60d40d02d9ea9d7854e3c5e4d4b88f057f285519cedff96cf58c150eaa1ddb6a3d15d447713da77b66ef839ed936ee57db1298935b5df4df6b18f890402e66466bd25dccd64a63e77a994924a9aa3d5d7a1cc88de48dc34c17cbc848af60c6bf49d55bda413a064459548ccc140c141b58c98c29eafa6d7bbf8b9df28aced0e870e2c241c78c5e6410e3299eded3a84e92ae4c76f0a8d1a8feeef24acb1e56be7365f223712bd95dd9a6eca3e99a7a1715a53a8fd9bc22c2a600122643f46a278b02e497a868e283355af3a8a07bf893ebd5265d2e51c9a0a25f954a54bf0ff38962095642b372c4e6994443ae163722566ead12f08ccdc7e4f65244b8c236e3770322f5436dbf2fda073cd2b81d7af6430fcce4d54e2d268de57eac5565624729b256831176cef09ac3b80cde1ad6ab45f3b6c09b69004ae5aca169cc9efd40f744fdb34046a36f5370db0b73c804dfe094a27918cda7b877762317b532a6ce8660b2333800fbdaf61b3bba73d9e1e2bddfadf5033fb56137f81f98fde42e87c965fd5f694d6f15e5da33cea95f9c0436d860bafd5f4495d3fe52b268bcfe455d31cb8b69874b920d2b1fe6aa094d104263915737924a0dd4b00fd659070bd4cf54a8521769d22ece09bea62d9c5fd8075bd7d64b6baf2064ef6ba22079b918399136c537ad7f2b541ae36e88042aeb63eda74b22bc53ecc55dfc424fa23d02c371936f11ec4f1ecf99d6f89d82f36dff29e5e8bcaf7dfca65cf737d99d1c3ffd7f79534868cbae0fb6c024ca4eea87032e422b3e90bdbd899add83f28322902171447b2a75563979de5ba2a3805c0b17dabb5f54de48f14642201e445ba16605b7a73def458561b0b5545f6dddd33d7afdfa0a6d81de10fbe3bcf86fcce92d706dbfe9d07086f80c4e88e2fb137441c51ce2cd6027b343e48261539e397e466ad9708b8ee0250dcf590817dc766d8f006b5980e3602d261be5935bc7cbd97e62376e29b4da3b7c79d0313894cb39034fe518c05b09916e732675d1e987ea7616aa83abfd6cb7aacbab57655b6589a5355d2813b27a681804a59b4d8b2ae1f81bc84a72d25a127cf52f997d4f223acea6b0400d5539ce223e5bece1a591d96346b6eb5e4fe6272af5aee777a243206bd7e463579264f1256ebd276b0fb406935df592b15bca41de8009bea53b88b33122950a980478f1f5461cb27432c675a5b1594f24fb6d8a31551c21dc3808a1c13447086010a091ab425845c7065f45536270ffdf0f013b1e6e5ab9948153097bd59ae8bc0e7468d3542e901e93c5bc71eb62570e742c3281c8bcce54ab3b35d15d6dc93baf1fd499023a79c43f0c8b1c07a2848c9911c88f912fa0ed61bb0b14db601149a04421a12290df3a1fb912c7489cb64042244f1ba59c16481b9795a3ae0e1945d13c855f1d73acd34c8a8c60695b1972ec870c10a4eb231969d3bccbcd51af4923988e8b23221d86286978043d1d65db691b19c0f1e140769ca8e3580e4d2508cc31e2a635a59623d4a1c75180838f038923dab176083bb8d951e44cab97158e453bcc17d10008ce01948e21afb46e4a8dbb423b21fc8e36c714b959c39cb78aff731a79d13b3ae62160e0c280b64b95e3324af4b2b1ae6227d37539683204481c7161b40d2ae803a979cd777d1fe3a500bd3dab325188eb1c6573b4b80a899f0680e03baa1d4e47ca01d0e45a2e1aba60a1c081fcebbb98515d32924f5b0ad8e1c49174a46837f8dd0c49bc73301c6fde15db01b7903dd3064746be348d90b248daae852498a6e3a2b4250d68646480562ea17790e0687d29657fb440c98c834895369a2c3bd6eea8b34468e751ccde6969c95007fa7c1a33a176c03966d721cc24d2604aca62d0b44428c7b70359bbca4f0e1e8ebd7018f2a721bf909c68b964bf1de8b769a89391415aa96c6b8d42770c88e3d82d0d9180e7e8ab31a486032c87de5194a3fa8e8e07453b14ea858cecc688785cb8c63b5933939c59879878e478b6e39013ad3659e178b46369621a014b593cda522ec841c44ddb462942a51de5fa1d6535689b9291304d5ab239ae3810d241b2a0e1900f4733079a721071d2fac8d951f310bc84053643937c7d6be4cc27e91fba0991192dca8b211a9a3464844a33175ec70607a1a38303e9d068471151bad1ff1c4e71bce36d252d8a41e6a5a806ede4629d330fa4fae62e32dfd1463f8d2f861cb28ea6c3b4e3dc8ef4f534c6c888172d52928eab1ab96c72e072c0734497e3b40bad1c89ad8324475bab98e64b032488e12884426351315641d312147690fdd1862323c1e0b2b3f33487488b43531104ec2071d18629905469e7a4c7511bc3fa3832e9af919ce5f638a74d8ac8f5f49f1051d0402f860869989c8c6c696d511d203936388a6a928277cccd513692f645ee396a1ddc8ea423d2a1ca31ec60e280d31a09bda0bab665b8691110380b529eb622f023cacff2a20d9a562223765a5a421cd1eb6a8092b22d16ed1bb1aba5045c93253cbb7a2ffd9c682dd159701933830f388bf4cd31fd7133980f4e73a12758051f982f9a6da2e904bd967e07ec1df650489310ccb0066cb62fa16fa23cdbf985be4542b315b328960ea67714e9d1dad27447b1e034373139081cc0e11841d3ccc29b638783d8d17c67b6602292a675703f4733bb10297f875878631199e978918fc61330390699a145c925c7ce263d42d45624ec1c6ac7908355ccbdf54e5bddb100af9a3469dd94b7452d2addd45636730e64db3abcbac3e114c03a3c5b4d13258a0eab43a31173f49eed4b46129045e36bfa528ee42729a337234e728c409ce32bfcb90bcf399ca00f3b13960e961ed9709c9347d750e6995e3f02a4db1c6597b44cf285a3e93077bc716c1df2b568413a2c38be0eb443c71c6539682b1294a35e384802dd11b698f5655e41a86f96474918846fc062149350cf301a593d1dc5f24ae56b03d31989fdcc0742df68b1d8b5f98e9e426c462962625a1fcfe8932da2f7f144de8357ca65d2c8a30c306069917525325e202362337b159e42cc5d838141323b1a53521b44843582a660a747865a5243251dffb1c67e36b07b549a30b9c1e8a8eaba6afb0683c063b8b7ec4a479a4776c0ad401fb33b5490d702d89c6b8381c3f8422518f0da00a191a15620cc9f6545133b80e48588b3111760587259dcc1f4dd691b3914b3e4f21e49bbb5f94802c789a97e286e2a226bff3d884ef4023639d13e5a0bcda23e98162f11af665cdc0f1f282614a79991ae925d8b7579a4da2de9ca2d8a2b64c34b65ed02333ec1cdc43a4c821e80858c8419940aedc46ee8488b0a4570fd7e987c70e96b0453baba909907e31da86ce6f03829b20ccf1f07e96157c7daa25ed4f82dc00f9c18584c99bad6585a6d37c2565a1ea4f2c0d0820ae41f537dec3547d04d3bee90ac6d1d9d6df03610a044ca4a0fc4981169bd44cd1e72ea576c9ef0c72ae5b2a27a50f37b9468dbf3aba5e08991f59dcf907f86b46930f9e7a904ffe60c15801f3ca77574ba740b7a99834f73bd8c671b7231d7db35fd2d6e09f2b35fd7559ee3c05a593691b4a5d0d4138e03e3874cad46da2b26cce149d5d70ea3f522f6bdf686f70ae0715290bf16872850f6448d12778d8ea21957a8fedff0fecb96df21c771a6f86d6358d4a89236ca83ed368cada272d0d66d94bee420516ccd6327173b65e5de00b1e4f926a0a8854f7b47cb08859662333fa9b51257c4a4a5ddec6bb4c763760c46943f8d9eb6037ad39ff0b933101f90a926a0a6f871fff3f62ef35e62b31f290f9589caa36e9a7ffe2df8f6ccb334cde89a3517fc160942e65668ce57b5e14c744fc699b6878d25c24d69ad0297a6cdce8419162d2c7b90822bacd2b78f6a2a610445ec0f183b7ff054ee3429d43916091a6645529cfad4531810ea9e48eaae559b2de832eeec2520caf276cbcc6c07d9a359112f79c574929d5872928d9db298bbaec8c8a6c017c6ef9409d7ea94a73902e1e56ff167eed2a38fe32aca9c31ea4676d39b44220e59d62a589d3230a2931c37e4ce23c125bb72422f28a2602804755bedede7519aef4c222ccd255c7765a5b5fe8e599d70a565ac55000d5e6eaff9b05dc8b53adf9580291b04a27bb81cce7658148bda2cc7fb33bc194e043157fe07308a5b7ea413d5c3622620c7994e5464357a98db8a1a1a2838858a198c33af95026e1bdbdb3dcc6c9daa24f7ac629b568347956dc3962d40581bcdab175930078ce8242b8f52a6de942cc7fd093728d65c117728dc18e031b979dcde7718367c34107880132b6f350b4f01c247a63a2fd68f64c73959f3ecd65416a1a58e2058ff6ac5e174b5e5046b6171dc41cf0577d4072c5ae0376d99cf99ae8826f271cfb1d8c4c12242090311a0848472756dcd09ba16ed45953726059c2c4ea1325823911f68f9294f9f0b36b16f422ef1be8ea62280bef6cdd2ec8812a3eeb3c04773c56902e253b07e011a308c87cc42a49e4cd6c011d4f95bbeac1e80c282eae4380f08a552525958958d655c5512694b4a596907dd159fb26809809b5e1f1e2a0489abf84203f0981e1a1ea7a6021fae92096914dc487d9c42555c0d4eb180dac842258586bee2251dbf4fdede971e444f581781b10b3585a9e2445f1c16e6cffcdcbf4226db6f50a6ec9c96c1eda2ab5674887336b4e881b7fe249f65c43895e1fe4e2ff823e8091b405db30bc958c0b3aa0e7c561a2c975daa660672294e96b9120470eb85afd288897af039444cd38177c807422868de667ce602fce60374f412d9c8b0ef2f3ab341a96d8c7175c128918185c785b0b70fb51ea462ca3126fa1d1de0c169abad7814d1ea41d0de1a2534e9d0180540215fb1fc3227caebffc977732aefc7654725422d1548ea10e8b7efa09973ffd3fe75494e791441f3648b6a3ae093dcb9a282bb1f7a35dbdccb321c3700f41c500a406814e6f6ecf806d6eac948a85e4b0c33435c279f5731a8d148c168cd482fbfbd16df1b89a899646121b61312843c08dadd0aff701c4c23edf367c9d70352041abc23c00e449910df1cb903381c8342a4c4ed21d60ce1466ecafe686ca2c1f0cfbcf89e7871e1954fbf67e6baaab3826afe835f19ce03546ed4e32be4ab7e819823deb3f3910332057d5963d8e8ff768dc03969cf036ba1801b5dcc017bfb9aa9de70d5fa490ced695e29900ec339673488917ae9223d6617fa48bd92d2f6955f14aae00029311a628cddfda993676735ee22f8ec430d5f2cf39b3596b984fc5c399388dae81fed3d7c384214baf8e6e53178d2c8c86e2060bbc23372cc0033900a446150ef2bab12b606c6ca281393d3312436aeb26189a171a8c0997161b3d57786899d16b9141cbd140218c152e0099faec92fa7eebe9073a8c0df8c18d240ce3375472190f9379800faef5e0224b6fa41c0dd126f8c46639cedaa94903ea03af43a15b31ddc090f221293bea3593abd58d4662ad339a9b3b783bebc4eef3edf0863f541d41b6fe47b5e5a354a86a61773c2d091905dd84397a91e2fd11c9a91045ed31a3ae98bd8eb0639e59974f7fde0b169fc17da925fdd0507eda23ae56ce20a8dc7169387a13c3de87bd13b96ca6f37f7d3ba36efd8e9bdee06532943f7f17f3359b4b539046231b2a492fc6a2b66423b55792b986400e5ab4b83496da4eacd7c8bfff2ec2c164f8df5b871c035273ab61a3617a4a8cee46661c1b339387f1f0a7436b23bf14f00f273bcbe0f81dac72507e90fe94ffb0f66e75bf37f304182b63ebfac405686a0983abb03fd431db371ffbad540d293550202dd0fc4254aa2aaa023f9b708fcae47cc470d9a9b0ebe2b82c9a75050d7513a5b3ec2498f6f25b6e412dba596a369d87a302de615692d34f104d36efc075b8a15ac7f771a39307843a4b1426a1e5b2f45b6d0a00a5014eaa1df925df2382dd839762c6e2c19453f99be3f00621296f2095a1a0445d2402d01be483049ef0d48cc7b2b8d5265830381b7efb8f83a7cd432332b290e77a8011f8428d50c42090531334043de05380282639ce9f41228f8f1106de23be5e0424dda708add13229cfeaac24badc282593114b61f8614e5c73672c9a43bef94694b82770c9393ea09fb41691aca079a9377dff2422a063fad508fe8b1fc1db164fea72cc1d9d4c716e4949b169af68608999494b2851796aa73cc665a21a394516a957548204c7905c507467c9cab076a3aa4e2cca65e2c2bba6647fd859479e7687199815a0bdeb90337cedbeed15b13f76be5cb63eb6625e70674058c29c91e321107b1c0a12ff76ac71fe542d3a30e0a9e36de4e833a5f74f7d287a0e225d9edca2ba4824594575f543cc1260a1ed440af351b19799183db8de351cc1675b49c855ef5abe4ae8b6a76f1d83d231ebcd576e97fce39b04f67ccc3b9a435b336b458dcbe7d88a0b2a211082de9abfc903e63f129a201a89cf9de123356ce1a9bd8b078d61d0ab4f908880672862c854916916d64bbaf48172d28d8cbe272d7706477f569447058c81580e20a8d2ef693ee6704402cb7523965c44690ca9959bd33d9cbfaf1780a26a754c2905b8fd94fbe3d5e21e4756dcbbe3e893d47c3e757df270cb41f9d720789ce9b345c88b1431c25e7389249e7b49fe4361e1f7145ee33f620c63673f065b049886e9006df39f4ae374846e31b52e1425f594145c9f83abd2823576c9b277ce6ab5a9a0f51041f083177992d4ae45622e57a5a76ea55943af2cd936f742caaf6955a6fabe2b7a761da890be11cbd77d94f66a8c0ff894c46385cf0e52338b3d33969c2435a771c123d6ff51545b85687ffc6c8f450bac9a4415ccc827e4d55dd3420742dbbe4e6b2379498a43510d484028aefd9655f40503a4e0755203e33911545b74d2323f8a564ced74e659d4d3cf31537309cc312a730786e85e75c15045f91c44db1651ca8b8e2585ccc07dc93114bfd38c10aa05823c815c8e806d0b886d88bbd9827242222b4a4edbdf7965b4a99929401a406fe0522063ecbf62b0e6dd243b632199662a51f259d124fb7ba8b2146ec5a9834fda51fd3a65b61967862e860e85ee878743bba15742e743a5d0b251def58e874743926ce0a9d0a5d0a130736716c49c707d9cd90fbbb58d7587dbf17c67c29e64da79857dd4f89289104ac7cc72231a3c532693c4f62b61acb6cf5ca72c96da3c8fd96a841e8b7b05125daa159d39f12ad12b3a61f25da25664dff49b44c88368b6da2245a2748a2d552add0275a1988f6caace9bfa2c5326b2c6dd6f477a23dc22221da24eedd4831a2c53269fa57a2d532697a1ba7d532c3214bf31b3a2d8c56c518d9d2fca6eb6c673bbbfd7db2dbb6af395b9e62dec6168c34a34fbf244bb5f7c42bd9a8b33ab34129a510d872c9899ca86d34f8da3c16b351abb95f5babd56a79fea5322ab3b66e979c349a0dec92711d8923f7fbabc79785cd1afa76825e673931728f757697d459160186f015c35f8417ec3b5991b8eb703fb3a6be4b28b43ab976311fafdf2b4dff66b11a6fcd71fe8d67cdac99ef955c388464cdded4469b727c0c67cdac015fa8eeafef0ddea8a32836f832e1df5e47f71cfeed4bc05b37626ec40ebec217bd41473a762995c9105e24127009780ca149fc02b7afc51778838e1bf8f266dec09c54eea64183035d3184b4452efb4871b75d5b7d7647aa20a0616e8c760c93555071d8dea0f0eabf6cf69f33cc24a5af9afde7c47356af60055fd4bdf43a480f3e05c75208be08fd7addec3937fb4feabd66be77cc455d6f6fbfbbbbbb5519bfe755a7eae4f9eeeeee157c6dd57ffc27cfafb5d6fabab628536bad7dd94be7df7b5f75a341f3b7ede5d97bd74e48cea32310d030b98eae9d107c75d7624c22efc5ef8d3338df9bf3bd77e3ebc9b372a25742212746c69f1323d75184be31b63a94e77b604eaba0e2f0fa5b0106ab4fc72ac6c813f01b0b9b735a18cdff459e223cc1ff336d02a6ca4deb16fdee22cfcc563f5955ac6eadc49b49d3af125d93a61f378d99698380d9f2f7ea8336bc8a37dfb8d9df5563e290be9f0113a7f4fd0b9838e1f72b60e298be1f67e29cbe3f011307f5fd214c9cd4f7dfd880f0c1c4f9be1f01079838f7fb0d3071b6ef7f1a1307ff7cca009d075a800e8d11544f4e21b2ea73492ca2436384d593c4223a218cb00a4dab4f718f124f220958991b8bac46ec34a609027d8fdaac225599c680409a1a675497e99fbcb02492c0d0b3ade9389ca948670dfd4db4f5da8de4bf1255e2749a0dd5980aafbe574512eedb9f5b1852908412ee8847ec346132055f349748da60635269a2f0eab2ffabcb5da261f2045f5deeb203b9f75e5c6f1023456c5f2f1b11ef4e077a8022d65afbe12fb31b686103173e3f302180142d44a9291105134c96a84ee026385ce0a30658ac0852e54a14363b4cf1f282225e869eb4d0fd98502862300401d921080b42b628aad2450b9638a10312a46f89113a82265d62455820841519904a2d36042111cb410913a49fb089fcb032c7c124a4de1ef04890878122528468a155d1b2138609213c3c1105480f318c604b4ab880e1a103b642173c545041c4972840b32b868a18c15abb05b104f6ef7698551f17f8b66ddbb6f5b06ddbb66d61ade2de7b71bdb7c80b3f4636285122872f2d58c10f3bb11e2f465d980021555875b2c327cb0d52aa748165470f1d6278c2880c31406284bb448b000061a4c2e8e10a0e5c6e60c2420e270893a48995245b5828024c72b7e766a03f45dd4f96cf86778b98db4304b7e026f99e9ecdb91bb87b97f4d8d161c98e1b040fb40898ed873a9d0042872d46e030c40b1419190fa513584c5982431046d8871e97f9e4ecb0010b1c8274f019428a142b80445136914393262e94827af0067270820635f0a146a5070986604228880a5b808a2c519d40820a33459229acd012248be944902542558e9882851e3d3a08a13bbb3b321092a4052f4796b811c2c4cfcf112c5c808192214aa494be1e5ac90d384890d4f5c729d01e216018a26746c591142d1a7882fcd082213c7238950414a1658523454811051648d08c0021030f4a867ef878f0cb640a135d62adb5b64a0a66a0820e46b4cc404393273e3b4d68c1c38e244962e0b7438fded1c124e45d090f064c864cf60631ab9f4f91273c6badb5d67eb847ef601d86fa8795390e24d8212684a4882341c050c03264040b57f00064b405a1c31d79c7e2566c2ee80e8c14b1ac2a397069b2d3c54a0f076a447686e8896283229624d1c167a48847200b248ed0a2944aae98618608b9520ac1e30a254cac7cae448f0da4f0d1430d4396b8228a00cc00564397253160f992e47b8020dd7bab77689b60d245a907ace2c9151668d8f28317205ee082082aa4d430250993f2f3a30e3a2e6e3726f3ee7b25171021592f8e4ceff5f0f5cdb83797ed7badb366fec5d93ea5a909fadf0658ebd5ab65b1eebdd45e4ba9b781beb9df97fd7b4b12b0ce518e54bf46e08e3334f7905767ea9f30e6a2f073da5ead47af8e1248e0e76ddbb66ddbdb39e79c33e319fb27e0bf6fc75c14cedc68756b7650077577bb8fbb7b2da245b556fb437facbd5487eadc8d9c433e61fdfb5e876fbd173f8742bedf3dd7a932fe1c55beaaacba621f6ddbe4facbc1d65679feecd9f35ba86fa63b6ce2d03cc8aa7c7f3cc1ff7d246dd4afb4bddabb61aef33e90540a4d27544ab58a61c9bc206dd87166d6d07f1767ec0863d6d0181ccc1a1916a869faad60a1300b97ee92fbc916503fe941d7ba6524f6518e2ad3d1964684f46d6dbb327f866ddb5c3a9ee8e04ae1c90d4f5ec0188309bf3c859ea4c024dc020a275e9c60b182932732d9baf2c1ed9cc0e0e3871552c0818b1d9eec54b1c3151d5694f8f0045f095d5c54f01243163428b1640a2c02e0a506a12a5b5850e3a188f739b95ef0e56cf5766205cbcc10217ee2520188ad8a93d80e273a3a217c29184e886cdbe64418b381131d2f4f21273faed019208a92d0c035038c710da125999c658d299af870c527d7086a422493618d2a9a08e170139e8c31c6b8736d0943d6d33c859ad0f24c9e424dac684294c9965dc115c415c415a4fe9c14e32f35c6ddaf1bb5ce1e326790ab4b375ab58e2b17fe552ec0a01b2363d10406f98de9045653adfe67d254e36add32ea56a5771b72a14c1f57a2ae769d37ed68b7bbe3cad9ee7ab6bbdef66170fb30c891ba1247ea4a5ef899bcf0338127122a5553296f9afac4d3109f4c1fd5f34dfaa9f09bf455b5d23f32027364d4391a560b02b2120424442b298f0f2099bef3501f74088dd149e9a4745213269dd43fc8b44ea04cf3ac230dda5e6db5d69bdaf66aafbdd79bdeafdabb7d77c31ce6386fca7d1be6b8cefbbceff3a69ff775def781a412a954f2a625d2078239a452f895c09cd074329d4edef41403be502c19f0a57a31b39a99f1a6339f09cc711105be689ea90f05e6d06c55e08be670a55aadbce90a93666e70769c329844a290eb93f55eea24f2845cc7a6750bb40206752ba85ba09520137832d58c4ca61ae4f6b35ece76c30545e8f37d82af188e33ba21e63a7247abcf0406996a4eb38d2b1957ab7db559d3f5cb0fa86f5aa869e8e7f01c60c0181975e9c2854b67c99249ae46abf5583db6aa81100354cdd69f89737f4e4b1be2b8e8826c8cdc05e94c5b5142b8e8c924e98854739afe8a92cd9a7e52cd5d67d6d055eeac344d7f17d42d5305567014d4916a41b9bf046b1bd2d272d78c4847243016d7dac51392358b198ad07fe6b34c7a3337e6db7fcd1bfef65f4e69b77f9fdba60ee6d4e94d6bb5cf72e738fb9e6d6badb5746252637ce365492023dbf797875ec1577d5bed28ce9a69c1d79da1c0f6f7757023bee0cbe2c81bf8aa7362aeeb3a8fe3ba12b6bf4fc236861082d3709df5582caf84edda8fe3ac7ddb8137faad676badb6473c6be89c5e07f6bbe86df40bc30f2461fbfb81256c23769a06c106cc6cdf6bad954e4caa2f42bf4040c3f80dfb15cce9723a14ba5cbb6cc7fa9d8b3337e8cf7f65effb3098c3657062d08e23c8b5c7dae10ecf2b7977035f348c8b05e1369276e6356066fcdc774fd6a1231a3431f75329a5947a91ff147557cc53e4ee750baab5daa3dc6fed256fcf88c1576fa2cb86103f47bf0d7cc096bff75edf7beffd6eb7ef74723f37947b24af9d43b98e9334012d77c0ce408f720088e5de70f6fcfba83d42a9eda1945e7b848eaa1db3d52a9d95c8fa1851e64b62b6689545ab2b8bb0be6aa9a6d3365d50ab76bc4741d6af5e87ccc77c09fcbe0492204389494303be6428315bfd2f8ccc1689239f4e3fef8c7d968d79d68802998f7914b07ef5384678c1c91076af7a7a7aef650cbd0c22bf91393de9f9fbf9bde89938f63be61be799d3f4933c977e96c8173da86e28f7cb8878d2ccec5d12a791acb96d9a945194fb6b9849552c04947cd1d3cae48b9e2c4389dcef89a48c5a6e5807ec2c8ba49de5fbce934956eeef5e4067eb7e7f8c8827cdfd9578afab4b48022086dc8f29b6b307502852a00020966aef67d67649114d739ad8f77ddff74d1293616651af33cf149648a07b9caa042acbb24cc57cc462b118ca894edd6c369bc160391ce6308739ec7af0891c9850c6beeffbbe8f068d54a6418386e7445c89047a8a730f5502955e7a65798ab5cd7dd03d0e9bbed9cc93cc66b3d9ac2600334454a6b1582c162b3dcff33ccf0412939e0c892611e7315c22819f9b30179640655972a517f3118bc5623127722e4ee4444ee444041800cd29d3b22ccb92e3388ee362b1fe582c86bbdcc0cf4b5bad61ae042acbb22c633e62b1582c56801a53a6b516c618635c6badb5d2d2bf2743f7ca72abdd0747f2fb52291830668ca407997471ed4aa0b22ccb92c65780cf158350867118f6586badb5d25a986b462425e9a5b27e3bc4caaccccaaccc82b5568dbc5ae36a0dd7da566bb7d6ac01541f150f30e4003eb91f013d1f7c08f03ef8a848da24b91f84d9b401c1c6c69bdad04ab969adff6e1919fecb72c0dc8d9616822a0210df974ac180316386071ecc26e5a6f98dcd325403739403663b5a1a10b48d238179cee8905ea0e6b54f9aa24d290e52c0408a0d6d436fee17ce1a6765774ae32f4295f06bdc6ed539bfbbdbdab66d45f016eb9c4da34a21626295a790941ca9396d3c9bc2b4b9a6ec9348a777cc96bfa7306dbc27bb2c7bcc69bc761923db1f96c716d9225b648b6c918d75cb6dec0e0f39774c9b6d67b6bc6db658b77ce3711a7f8cc390c57a72e3c9fe4162c9ee94626d53221101ca409eaf88277b4f264f3db6e824134db56de73424c7dae62464b678b6d2c7f6b3cd7af22643d1668dbfab896b47907e453f4020d91fac617e5d7cc4401e608e0fcc6754e43726a3a6f13781a9e5b0473c60d94f5727fb0948690bcafe3d9a6addf2771911925d4996fdab58e29935def1b8a084f42df8f294ec03aa8144d845ac0c954f4c2129b32c82b67956926611662189b3c3702a86c056eab356d2cc8dec23a981dc31d41042284a91283f518c4409a2e2aad2eeb59bd66ab5da6e6befad52a50ad6010404d45eddd65a96d65a7befbdb70cc9eeb2562bcb320421b061d2cb26f168b454cb64a85369a2b11c4e22a2922b4ad8b4b6019b0a49a1157f79d2bad5a6e977551192a97bc7b05b5e67297dfb369cd2896f525d71882bf96556ad5caab2fe5b21b5deb5e1dddf1caf1573241c84852839d48812e547941d61a2b0f0419415a07cf181d204ca0f50a2c0a1c4c99427a4dc40861d7c3049b2440d5480c187280a1426840881628a215c7c6ca161e39144132d38590114503628472cb10594263f4011820a28436a348102048a0fc6b88616473c29227c42e5c993274a9ef8f0c4c8132e5052786244c3a43940483056cea13371aefde0a88f8ec8a6e11f1cf90d2804cba2c325d68527834632104c4f6e2f470706b95c40da5aee1a9337b103735425246dad036373d43674acddaa24989037d3007ffb2f774fa57a7a771d433a437ffa9d359bfb66c310638ec5721a7cedd7efc01bd662f730b46fc5d7ccfef6fdab7df5d7f7c01bfd39d4ff357d037330686b77c49b33eedd36cfdbbc6d768b6cbdcf3c076d78ae1dc2553e127a23e9b3bcfd06ce70de73dee391286fef6d9ef75ef7df515051d1cf51deb6a794524a3b286fdb7777b75b9ebcbdbb7b2daad5fee46d7b6befe68d2e1f5c3e9070064712850c921e67d248a2f02a3de91dbc511a87f2f6a471f3c091eca3bc7d4fde7ce8a280963be0335a45c741a25eec6f30ad129261aea3076ef9d31f0ccee13cdc077620d37d68ad7be4f2dc1ec7c1699834fdf7c8b4f19dd9ea9eb6f158b770f0207ec3c421f90fb7c16b701ea761e2f0f8cd9df90c2e83fbf01d6781f7f0d8c4397d5f1e1fe24472bfcbbac6ca149654a8cbe1adf348e0b7bab33bcb6d0a4b5995425d0e6f9d4702bf93292ca952a87b3275e7b0443309ec38bcad2ea9fe9663aae5d234fd5d8c2c98cf7179b46e081ecbabeca32572b920245b7dcb4e0aafdfcb21c3d0fbfbbc440116ab7e9fa7523865fb555fb59672ea0472e8f04a36c6afe44ac281b928ac5f5f93f6c64d8eb6577b378e64391474ab3ab946427a42ae3cbae5321292ae93ab1dead67db269354a2985c01664a356547677374f1795eeeea08d5a2b8d594b7588f2bde41c72175d3c84f8e97b98cb9941216fcf8d240ad9e29fe08b8e38c727e8b88f7254d9bece2adbb169ddaa638622f4ae2f3366b8b4a875868b0656091645508666f8520be23048a2a33385063d9c7c6006430061d245861eb4a82a38608a18aca0072e41642cb860c5ac7683293208c1a2bf52005c78a122054c901f2332b4dc1ff56adddeadac137ca9f2cf7a37ccd93be9db0d739d573dcf9b7a1715db6a459513e47ee33aef9b614ae9a434d35929bd97e3603ab9dfcbc123f77f3a301fb99f14eb56d76ef693c4c9cf0ca89cbd54d96e5bd1894b9753113d11cd93d1a97622a2311e1f407862436431ee6d7065109095a0b2a28450b46e35aa441da18c50605047a85a0e18aa0656daed5eabc7d5906e9c17f381b9fdfd893cbd9f4f8ff4c8b3fbf9b40b2979724d441679621e21793e9549318189737f06c14212b325666705a4faa22292c2f22498fb37af4fcac0e8c84292fb6fcc96dc6fbb9ac7ecd08c579dea8bab88e4fec6292c34538cc390c5aab4d96c369bcd6030180c060b4da7130a95dade4ba936185f9ddd8fa3194646923bdc12ee9480485f4844faadd7321392704bb893fb6f09a8495f48445ad633fb2983cab22ccb3236fb39cd4eb3d3ec343bcd4e33d43755ab1903be62bec992a93232de54e67ae2cc190ba8f32df1a8f07e7435104bee775027f7f7b78466eb51e1fda81d35ac75623ce5ece74474223a119d884e44b9df5b792f381b703c602db48bdccfd1b2b392fb43ce06b9bfc4f190fb29d65206d186888a663fb1582c168be5fe18df8d6d52ae972602d318699392fb65b6eb25d318e5a1b29ed94fad2ccbb22cf30c8c183138e040868c17ab287a5391956f0eb985e836896e0ed70a99e034f48b6a47b0948ceaa06a7e430393a81aaa46a26aa8181e073db95fc6109fdcffa99eff684af6a25424534972ff989a75503be8c09b7630c7544cd6e1525c92461ead8e94cc6f68b8ac9c05fd94b98372bf27e35b85806652065683b0a30c6cd6c8c072abb20ccd02445c2e5091e10b959d1950e9e1e241258618507981082a3bb45071a18611545aa0b202959efc224f212a52a8fcd419256a8f7997b566a9ecfa84e4a4f9f86cb1c4124b648fe220f25897352a2b793166793e0cf001a918332f9a4496808317322c27912690c18a59a92a893cc15b128902d1149648e07749640ac68d44aa207b1d8c9844bf3ef1e60cb2ce72bf07a3cee84cbf7099cab231798e198ab0aef21ce7d7d9cd02cbfede7d96a8ca30643a66a54aa19e5ef15b64a932e63aef033d18ef5df1c55b51e6618833a5272d4db4df228b48768a0abfc0c8a40928fb77ca0434e33d79dac9feaeda79f17d5d9db6c8806f7f46245148585986374c8af138c7b00f4324ed2acf88a4278bc9b04c7f861d6dd6983ad8ac716109494f56a24d9ced5fb06256aa9413911589bf2914c9ee88ec5f22819fe73df0fd1102b7a66033943b52e5e91e510dc9c499595845424454ab599b020963ceb4f8a245192abb5f8bac49f38ff95a5ce58aa4496a619d911a6ce23cc6789c4f25501f04340c9eb9f5b72269366b9c5682b964109229982a45cb4e9b38a7ec2a992b89903ea992654fc168d9555b89d6c1545675a9b3c09b33c2a7a5a7a4a74f6b13077cfa5d4e9cefa9f7f49d68e2744f4d469c7e4e2cb9df89e4fe4abfe47edb7f77f04944bd4984d1bd499cc1de9bc417f87b9328834f22f82691854f22e94d624cc627b1f4267195b19094887d927eb94f36d08e6fc9ddf824865cea714ea14651954da22a8762499cc0aca14f12a96cd6d007455a9b35f43fb14b8fcd1a279a4790544816994ac9b447f6272c0c0b8131bec194a32bb4a06244032f4ad47eaee0d2c505c3142aa6449183cb53684a17502c3743c2151f8ec851db82ca8f1bccaae841e28a10cf941a3ee2aa10255334d0949fcc419e425382a6002169b46b5e561ab39368e6068eecdd4f0425218e2c829270d2b18b07a35dbdda6a6db7d76a6dbd16d72323b282a94758246d0e0babd5d6bbd98a2bc789640d02b212c4896415a26d1bae5c67abe779ef79225979aa0f2095c713c93aa4ca3cefab20c9d65211972e4546b55859cb61759ddc6fcac1e34551e08b92ee23f7a73ca6aa2a953755799f98c449eeef9f6e750ecd5d2dbbd5dd3b51d7cb7960e9945ac9cc8829536d75162e5d86f40d0d1aed93243ba10144c34a9edf326ad43726108121ea1ccda37d34905adb70407d4363312b31a1589618972e793e2da98fc640c2212c9f3c9de4f919e81b8abb84462c3039328d99d036f7b3bc10cb8a073f8305e3e683be6999771f9907a884cdd6f6b074a84e000028282317000028140e0a44a224caf128c6357714000c687e48625a341249c3b12c4a911c83310c23040063080004006080614ea14101bfe371b10e8dd0ffa59458055686ed94acc295e451385fefefea8058a5be4e56e0f31ff98eb44e957310b845e5fc57c1a40e4f6b069322dcc4e8027b2f1c88815a765d509458c94d9d07e8d452888f601620a56ce63f9beae0a640453de94ae0516eb369fbfd9b71bd54fe85b2324227af68fcab1e004fd117fcfcca84b46ce8e7aa1d02de9393d1565648e45613605f011686f02f4379838b1f5b474ec926e628a4f89da8ae633ab9e87c96ca7a753855a0a0c8490e54dbb5c3c32dd8ce447784676a512f67c48032a09b474ac9a6e69841a40b88b59544823d6929fe9c00e95e8b6343809b3b141479c882b4f93506df623bf7a8adb448dd93b47f2af5a3c2321faa1a116d6bbab13e30750f51ea84d350fc574b1c7ee377a8051aa5f3799b6fde56437a71a19e7ae296420ed05f3c286089a3b4924a30e2962e1a12fc43fda2b6220a5ece7057040f13d018a3d33f76e98737d816b29932b83432a330abf5f84003395e3703d835d0a3bd1f8bb34a645d46e37a8db3c18fc8ae71e4b433ae306b03d45dd4844e7d742e87aa9b73ee2315194094191cc4174a1437429cd3ecb590b3ce8fbd5909be2e713ec748344659a8ea3211a3fe41e8f4cf0031d3dd50ff8c55c422ff1f4abfbdf4c01dfff934f50aa8e9cf04a71a0a7e629cc85dfa1de1a3019024f4b26986d3564a831c92209a66b3a9f2caf4434c5481d6f6b5d28eee68df1258de5100bdb25c451f3e2d0f303d4f6cf63d26048a659a5a2a74c803824a862c39cbd0cb678e60fb3408f6caa8afcbc10f5ad921bfe8d6902794bc06b3cd4daaa6c4b506787fa979553a9fcb50ad9f5bd519687a2a52de51c16788895bb086acbfb6a73334466bc0a52a54a9848368c368d028c9e12ddfaaf44a06f11014e97ab330450ef845687752a9f2f62dde0f9c29b21135cc63099c7652e20b8fd2670c4390f8eb15c5b9c86349c096ba6fc8c2589bbe65df869f53df51faed4a01d7dbe346d18e6266cf2324048ad99186bd2e3876906bea73f216b6d28e53de462c6cc628da49c26b57dc402106aede0849e7a6c4ca4ec2b7d02883090a6d0a26a828d9c05b6000da0a27c90732f6416a496423f3eb5ac997b6c5822f9dbded3bb26dace7fdf2e22a342ae60c87203f0f2113e926571f2035a6d2dc4b9eafd49e3bf9270c13a469a2bf6f6a22a78e9301d54121590976fc0bf9dc0814d90f3f5e4b43c4051c50a45121cd28d389365f0dcf8dc37c5c69c6131c93329a210d834eb8de6c592c734d54c2a336fda0031ec27eb994bad3bc030c7728338cc2189debec1131ed898d72b20e1f4593892c8af19fc316362765b56dc3bece02530da724f8fce030f4689f2606ba22629f8bd0c5db3243ad9f3427353dfec5968d4f110e048493950be3f9f874cd8dd322dcbc06d1a7ed6f7be56944dc6dada76870f5d3369fc142ee37033eed6fa6e34a0aba1ff40a72351d6076b4855a6e1b53b585b4a3a3b4e873b86770e69a896b952439bd703d3b41071e86fd72d3dd736ee335aaf5ea85e996e9d71cc81976bb142da43f56cd9a8d26b1a74310a5f5bef3a88735c64646910e48f1684efc27ee5f3b9e6eb2846d554b32eecc39e774c332839c471f0cd39cb3f05694707a89097fb9801e0b25aeb4522efae9ff149c32a869a018865671b803227864993b2cbc097726dc8e2487f931fadd4aef54c679f81e19177d0a26a8548082f7e2007fe1fd5977ef69a23d4c8cafe7f1b698e8345ef43abd67af9df8836102aca0645289fa35e43546a4513fe04f7e25ff61172e112ade72cba20d7543201ce60cbb8a52cfeec318c2a6403b705b661f18da9cc26aa252a09945057fe14071df0d7e64a961dff908293667c880e70088f937d4cc168f29c720b6dabf5a54f79272f5ed10b7de00f797977bf4f80195195f8525a27595819f9cc7dd04feb72a608b723185ea9b22e19593529d72d7d035dd991efa61cf6f272039bcc3b962be736e56b88bf78dc51e9a311b58e066a673941468a1262ecf24fe4659a8837a8b18ef4f448345ea45b6c384007722d56d801ddc27af1cf0deb74c6b910940ad419fab64e5a43984aedb945e8ff5386840972b365da0fa3a1c6c1609ae3e46373abce969c8465bfa064544e6543bd5f83ad1d1cf4aa446875ec15bc3ee6ba5cf862e3329d7e1448bbcd93ccbe43771d065c42abfbf529433f41b604f7d8a0dc1bfca40b1b81022f7a197ea2d66da5064e28f8ee2670cfb9b3709b42e0656b855a705ebbd515b3f57189f792a47009e388316fa10072b117f5f1105182183b59358c69d9f1fe13ff0f5bc6e0ef266c416ebf9dc5dc153efb3a800603957da5600a9aa64306e27a580fe02e33d31ea0309c00664d3984ae4d3b9f5a6bf99213a0d77e7b4118d4eab0cc37a1edb721123baae0920402a4479303e85334b1dd066e4627bc3e95ebf2a10133f9fb74120602281b6b3303355a5bda3cd6555bd35cb6b799d8c367a00cdfbc1c4a48a9a299d93a4b38ceddf3481bfe7a2717451fe6aeebba930f4a99493a9cb3aa69771f268bc55f8ce150e283125950e50555babba2c5599a9a3bb0c62b15547b0bfa4b545ea4acaea1f249b499a1fe5377a121bac9fed9a1b5e76b94e5c91ce718cf359550cfca3272fa11b835a26be4512cbc74569088ba978584c426b098a284ea23de9c6f35ad865b86b49a3a7c5b8e40cfcbcf17e34f4d38f0785ab5712839491cb620c767b1e5e52830c96b3876fefd90a8b7c6f773d5ca6ff84b1a0dc11aab4630f352d23ce00aa6513528fc31fa5e63e6d84bdddb56543b798f6ec46cd51e1d48b695fd1fce5721239f1c4be3192b65e9b5f2df18017050e37d8af54169d4279d83b188811ba060318f5d025ac99ef525f7abfc038bd60f19212d8c46c4b51ad006d88d68a74b8b09d8f4b3e1f85581d4a2e3105b66c628c91e4a820f0cf4da10f92046b8934d15f34d7e96c72983599451106c616b4ea6fab9533418383ad9ab7b9109068f180f7b0d8ae280f80fdf50d0b7717ec8d0858f430f6061eb183226900a58a4ec36c2283094911aa055383914ca3fc960378916086616265cfde7d4b3209c73d6228c243cfbf0a4becbf74ec0a99350c2f3ff40cfee9ad1c74a394142c1cea99de19fa9d28fa3eaa65d9a19ea617a603561c16abd1551dd872076c5e0f77a344b5ad8b0a970202725609462104a5d9c817d1714af2954457eb4226f3d6223d0ee26ab6f2e3394b3b2a9f89cb9fdabe7458c78685dbe06b2ded27d71ef5b549a85308d5eab30addecfdd96123013b66a6439f45f459063b60b897b58e792b40808e836d3db03e22af4c099a50119b4d931071e12c334b03f8895f1eecbcc9e54d2821027eb951f3c68a74180562879620a9ac27beb5fb80024bec066c886392f19a766ec88fbf106bc80f03e3090a8b76803a0e38dd4d26f98f403b1ffebd4360c5774f81077217879e128dc6f4a06db1bdee6c102058ac32675e3428fc10bca2485b4143e79fd7783fbda1299a8c598233d8f03107c5592936c09a8a6f8d2d41f2524fb41c0b2aa2706cec2c5e14ff01197068b763bb53f32314c3671f6023f376943ac3cc73ede0d9980d699efa91a3063ac061601eb2f160d83ee43e2c0777abe855324934cec3954f17d1e52bc01290f27ee7aaa231ea053e9cf00da6147dec57fd2d0e2892d68f25bc20c2913b26d0acd132bc130ab586dfe217112a0b29bed2361a9a57e01fefbf3f045ace954769a4b80d5f72bd48c1e584fd4ba2debdc44f7b2442a182afe675c3229bf00713d5262480d6a0a124094212bfb3e2eec98bf2213a2a44cf9af761688daae04b19c43d9fc56c2956a582392cfb280b0ad62d28adf7beeac7b5ce5415909044ff480cdbb96fc5dfc3090fbfd91f8839a7c7d767507ae41373fdc98318aea746b389131b598f2a44c6d439356dcb6596a3272343dd93b6d238d6d20241e91b49609b6964a98463afb1dff51c04a9d100aff0de507d29b3d9d19b0ab9d0017d869b7867b9852607358c9217cd69c0d13d52f4ee5f537b423112e93eb97e0ff9f9f0c4bc72af2eafc44585f8531505af2ccaad910c3e997c42a0b8c880b4d66c9a51ec26d0a20ea3a5102ad5d4ce08e031cd55d800291b48e0b7a5d30d12b79802c3eba42296c610f80fa7f69d48c53b8edecaae8a89d9229d81c03e933a01f0655688edea465e41d997930ed53728bf323f317a979c0506aacb557103585720afe8483103328b18a19827475b4295cdb331748f1331db8046e2fa251bcb5767dd6b4206032ee63454bdcdf5ed2734a9c024fda3fce8a118a1755defad0a40e37bc7e259e821439518d6a0bad751cd48e8a6376210c6fad5e1d2e5dbbfe1589176670f34ad113ba77b791f280e4580ad121f1e3dda37501863df06c6f5e836f3a3432aabc7faaf012a2301d879895b3dc5b19cf4f212f3d7784c7a98b43ed610dab852d1249eadb9fb822b322de24f1e0925c2d9c7dc84142e6f431d3613e8c6337e9bd70dc750cd1b1cad136e5b1d49fee2a50be9eaa979518ff38d1f110197c9c9548a213e14a3bd7eb1349f479062b5bc8358eb7c837f448fa7122d54cb3cc75aad0203e0a4e88359681d76bf121113a17345debc3de68505741cf0378ce4a642576aa3a460d1cdd5769991c4cfd8d1d2c5dc8e88040c76b3f172d5a76be7b2be026825f606f3117ad814099b130a0e38dfc6e219069cbfc3f426fa25bfa0dd10896ca833c7374861031b1c4d070f75355827696517cb87062126ac91be562403ed5934a8255ed34943d980b17c1f53c16a03c9b4e8013cba0c5232d2d1a0a5b3cfe53300b84f2769c5e55f90c69514f3eb66fe7c1467cce06586584953fccd4972c3453f839018e5e8752c22bfe4aeee9d051ee359303e8f838f4346d1852f425e7dd1f951b6e3af02f07cf5fec6be94800922cf26a73e4bdf57871cc5136391af4169ba5983c07f7f0d7086a221fc0c61074b1e1be678841edbcac9d8982c9499bd68b49ccdc91c169e763faa82a4e7ac12a815ca61fab12327a440ca60e369617e13a2233a17b994f30acf02514d6bce9294388dda850600202003d860293a6a78909468f3ad6a205467ec1ce53f8dc81e8cc31cc60124c6b07c8625d8b0f765618c03b32eff85871db6df5c98a607bac9c9916f04cead6855087a500a65adf740137f12637b2b352ce34fe6df043f778db99eef74d3556ff8a3737c06ebd4d4733d2417f69bcab2d784d44ee5ef76a1449a782f9bd38adff597407cbf8afe5411f748271a0b5a5bf795873749bfd54a131eaed25c2ea0acad5bff556bdaea3960ca9da3ac8c084dc39fab7e9013aa50f233b8c67065a04ca2f155c3c2f767418a6b14f261bfb41d6ba88c315223613612479262779b584fca74f925ab3b85dc2935ab57aa9b0d3f41f2ec94c32ffc4a918fbaf982b366766721c91e7c051b097ee359bbb2eb84b40f49f6a1fb406a8e50ad5bd7d548acae52390701550ced231f6518450e5cf2bcadcd770ca8c66860283e6175bfaef80132b13996ecc1311ef61c7de706f100f845618335fb05c8b12f281e5d7e0ac0ebbfe2878d2cb961ed1b16cb8315dd499319d23453e1631309259bee30bd12ba23c169c8ab57d4c46d4c4c84abd712196dddcaca9d20f9277b598d7f443103f49f644dd589e8f9653ac733703ddd1392949c41226171ce058d71c9ca470455f95755e68269de6eff433c30879575e0d5cf6fb5a62f9bf2945b2170779da5012b094a6c29ddc47259b9831e44c75f05e2fb5fd8d6c47899faeb8ee5787aa702a131a2c152c23ac92107b0fd34b66aba71e4b5ecdd40f362b8c70d2000800752d6cf01d0e16f343400143a173b91f50a7dcb9f7dc64ac1699d9b8e9d2f03417510d44f3b68bd840c527ed2178bf86c94f7b89dee9c4ce991afb171faab155c3d765b54d14ad7f7f339807f947ddc16e52176aac9a2d65a4cd506c1d241fb8cb598e6f1519790110a5cdaa732555f15aa13ef15aa4b9a77f5d85004a20c10040349e999653f9e7e038a007c7fb4d0de784e0b0f68459e070439e29ad791d672a2b9e2d324b4a07bd9fb9e24c535569749b2b8deff80850efc730ac614268880dbe82d9bab433821b85e211ce978dd209cdb792c10ce4251cf2803506babe20a8401a143454752d414c280514c234ca2f1f3512626dfac322add205cbc1cd38c0d5c2cac384f7a073da3c1969a076143d22807bb8e5e5118219996dfb958af87d5b9136121fb8d36cc85dc0f3837dd96bf5978f2014b1ed79c9b1a5db41b9d294a6f04a98dd82d7165472dec968a13f0066faacd5940d6eff531a9371f3bb01fa6acdab8dca698f740e5410cec7e2a57940d73e27730ff9f4b05d9a66908176e56bc92bf1b7174580d9baf1b9f6e53c59d83fc096b2593dd1c83f097885d6cbfcb5cb89c77c1a4c8d96d4ea0e2ba80a6d2a666227798b26f2bb344d79441f6215cca51126447dc2c60da4cdcd238178b65af6abf7bdb25b4f5c5c0bd826c8fd9ff555b97c8cf7589162ffd4f2968d7a8d44088dff55536c0f73e52e07d0535af6dd3b2605edddfca469e7b5f06637ceb40567cd58341c0a2074cc369e54d048f40464bf29c9b924658217837cfad2eeb46a8d35ebac8dbd0c7383afccddf4cde78273f59d161a19481c9a6f12685c4136e575b0ebdc13d7875d3003320c9dbc9f3fdb912d5788fe194b5cc68129472e7c9f7c4881cbe7f3f273f93f02685b407d75dfc4aee8296cf02af5256689f4b4a122d7632f883b2acd328823a7e94321a72461955183d0430233a592acccee52522813d8b76f54b49d146d4b4537e5259f8ea19d83c3738af2b9d2a0a2418768d9e0ee774148e9f4c70e23de57275e1462a8d60368db4952590b81552ea9ace36687ede6deb1ea42755b464b699b2d172ae8505dc11a68be34790f5e4d4fc50c5e05ae0dd7760bd98a585e1ec5c995f4601058ae328506768bd0a89add253d621a42c5646ef8bea8a445ee3e355f69d6a5e572bdc27c2f733dcbf9bfc537e663e4019cf559e36c2df432a9b2e0aa1175a4e21611ad16fb9693226b27ca5fe25cf41db8be1a131dfb45b5bf7e3116b1b82ac952aa543efb1a42fc79fbdb091919fb1b45d38518e5d970a8c9bd1d889be668755c2ad83bd87c295cdce6e43d55f193826e72c116503516094192bacf7494d85df8673daf14c073f261449404ee652dee2a08100ba443b694dacfa02b2bfc4a97da90e3286e4e80134761531b5ecf665570760719fef6e84c179069282693f57c3ea6b1328d45b0c703c1867a21551ef355fccaf190fd97148bfbb49855f7fa08460ccaa44c94e7b1b0b0e0a92c7a105c772dd4d758e94aa2f073c6a83b73c9fdf85ce258e4d0f33e007e0545b2fd9ec789280d0ab412af6200b665f43d4672b2df296040393a8bc55069c61e95499df1efa45f1cd94d273f97ef8c48c664e0df88cc495ae059b4a0ad2b3e7858dd136877735fe09d705d133322c83d6727a6eaa95d209cbdded01765757cffb9555d69c6cac9aa8a3e005b17f9c338a80287a7808c034c5545d63dd0520404159220200387f514f14d065dc153417dbf220853ef2354aa989d395001a11cff242746d66f00ae96e9dd174b2e5438676236ef488fb8ca22e914c5fb22a9f97684265f9841793590dcfa21ecadab7b4bb7528d02435a14d8efff8fb2a722981b64341a3f5acfb579670b65644976dbf74b02917f422d4855437ea225f4814f3c6200587821303621d6db41f7e196606c822c68568aa068dc93b01018e115b90259693eccf357eb560dc0523c9c9950ceb95489eec5a1b85582f6bea8e6947a094a0799931a1eec734e0935e306549305837c6dcb4eac72b87f519cc2ffb33ba65bc328e7d5339c22e78bb67f86b49928fa925f2efc6504ccddeecc7dcef46de233f6d9081057f10c97f1c4e5534e35b58454f4b324f253e30abbfeed55a0409cf791299acadc112e90d02d1b8c3ccdefc05e2cf4ce1fe982d621993e141a9e1864ec417196135ffd4f76167cde7f81149a947da4bf6bc0497e46bb95365d073c8fc4a99401bbf52920163d7536140ceb6e4b1d18e0392b2f0fc2a744e0449b50fb9c555535f1606221ec980c7ef6feee0d8f2931ca9251b29678c4900068d8d7d0d80061964c35fe424c7f3bd2c59b5414002a223b49332696447d54a987fb3b793308d7ad3e495d2a615aa7d4f77c21cc214b39a9efcc2b2ee615065f6c4556dbf8f1f39a958be6e7a86802b9140be296e453e4b2ed7e1b8bd877a13ac5893b3397cbca4a70b2ade4bbe541dfe1bbf9bf1c6477c5a49e72cdb707a50fd12daeb0d3e67659a0e88c137f99e46dced418b86189371437ad9b12acc41cb7b3d080d3b322f7abe2f9416e9d2e913bc192886072d98cb451f3f47dadef34c792e3c68fb5c7dfafc25de99e03432a863a9fb5eda5ddbd443ee3e0703d7211a9b93a40b3c8080827da66fadbdc725ad6e12210b1f2acd511cb8be50f8742918769b06c9176994c705ee2f95a0bd5aabd6d37dc56463fd3ff24e848b670a138bd236c31b345b00a288cc293b8ff6efb9e03989b1ed1b1d82f6f095c1fc0f2679dd64b5fa7809193ea0601362696c3e247ba97810a1021805882e681c43ddf526621a6f3c99319d8f5e3e5bbe68cd1d4828abb97b7db15e668117e3fb7022a70d18f1ccda008380918c6fdab687abdb85c8db810d8407d85df2a92499a8b4147c483a4d996f2e46f119c0aa0e877ea73593aa468f50d60c4fa64767aa60689bc1e6bad80820d8ca1f4a09fb3d38de040d37aaafa5077b7830d9cf42f90b30c5ba9fd407c5f4d106d5233a95ba0d6703176f90d76ac27be38c28b86048b6b2918e087d749c973806ade8800f9b6180bec44302138a5eba1edc8f007ae1bb84e890d7530dd0de9c8232003e46eca18f97bd451f986b30f562c75cc87e559e3e1e58e057ec8cbd85d44632eb80a0a0f7d43362272ba4a0ca821acc2a3f6c75a45b6f1910d374aaeea7d7458c0cd90da7e2712882d24e4af4d79c46688d2523157edc97c9c4f861086bd7b03ccaa1411ba159ac9f6f88bd9f69895a4fc95615944b23e3dc80d61b0a3482037de49466ccea17f429a6954180fdd5bc50e50eb6215e44d5b94103975ac5164ba685fdde00c2acc8559f3af840aa8908e7e1bd0fb4bca0434b88db4979d7ce70d66ee04eb0d64d21131a035497268cc54b28b203bf6e4e78cf6f792e456cba5079feec008c04e650a7bde060034acc0c1c553b529efed106e6285f4c68654ef08e668fc215c15fe09aff5433350302fcdcd630489aaad0d520715150cd0a6d4da47ad5e67742fa7275e49d1554f33296235fd7dad2bd9161771c4a63a59a2950c492d581e44665f0c423d58c8ef904d57b2325129e760baa79176413e7b110b0e5cb8f554d7384d18864aab9d7bfa545048247fc0a8d359be8ff9ff37a1a0f0848cdfae97aea466c251152f919cceb83da88a3597361ecfb798e333aad37f1d2664f9d10e749dba9d90c442e0ec11cc6ca7ecde322fc63834bfc211f7bfe6dda1379f11f95c29a830e6672ee4152b35f0cc38a71c8cbd46be2ee3788b5a5be2cdc63d37272546adcb765441b157105b665e0bf9c3a79c109c53b7efdaacd87390ec2e463f54a211361a199da804ba36235dafe0ab08f48397e07adc87de8d38e4d0d8411781c615ee2e1abbac2bf767cbd7f760590941bb21b8eef5fa9679b92eb40c851835d8ec1c64ac907291b677cc1262657b704e3b377bf48220e70895b49c90a4c88edb8e109a692505eb11500587f223592dc087019c7b3104247edef0e3a4e14d2392aa06de0b142a4a5545c09b22b0fdb2169d11abaafeed5f1f196e8d1557c0166d0318e54a4c372ffb0817738f91df3e536a24486c8045ab6244572dbb6e5312c0103fb163608cd87fc375a812c4ce430a87e80f286ef793cc2515c97e72df9d3cb80a89230c19f8294ed5f048dcf4f80c2188d5349c0ffe4e0407bf195e52a8ac46b5e49e818559c362fc4ecb0d30396d3fc52a834c93bfd117348a2255c3f9342f92b2ba31ecb18e9aae85d5c6fa04479a3fec5504e27d42e66638cbd8338c3cbdfd77be5c3fe7b80d1a654c194c4c58ad547779370c48a85078ce7d20c60dbd16f6042d4a3be8b8cdc2eae5b21d0d0a7200561f467b3bad6b06abbe6c60ea451b8aa18dd1446bc54a7e72e7a1b2cbdb9995b58cebca9bc72232773e97734ea385b85f62948ed5f45562348c5a3d637bd75e8ab4ff3316da2956134da5c4c69476372e1eefa45f073b56f847ac8993416453bdfbaae9ae173d7d834a9d57b14e156254377166c31449b0200df523cd5486e72cfde7870a9893de1670b81c8829413b329b0fdd28c27906fb31c59b70c2a30b297a1cf2d4c875e08a27950655744905e68b6330ef8042b6ba77559e61e61e6004fa4efdf124e95f85c5a039de3154539aa62156e7c716f98cfc4e31186ec3716a9939e28c05de0b19167ec7fa6ce79307a8d9d22ab4e5faa56da0941c0d87662354781216deab3879b608deb1504f3254070c26addda39d29501e14c1be92bba7346133ec6fbf4599f6aab0641362dd2790a57b41777db1581454805e476172c26502361b8102b08edf1541d5deb29987e295ff6535f7e0cf03ffc8cd2b4018599eb3f61cbcf4d03f2c5c2d17947bd6f6f7137ba0da2a1d8994262a60e69cadad117bf224e10b13ef171599b0e223b64db469bb15e08dd81e25d149d04fb6593c2f491bcb308b33b2c4e6ade2379c5013dce8c36ca07a0598d24354d9b89ef694c9f63ba69279be304cd7c804865856d78d49b93178dfddc523ad1a4fb1719ba86f5893bf22b561bd13c3a2749c07917696c776dfc07fc6b7c7289f5b89b6f0f6f9edd0208023e14b2c3f467e32416e9749ea8b04a6c8ed68326a729f2ee8019808e2401ac669f37b3287641f7d9512c0aed799d0f4f1a16676cb0c9dab4e0fd5aa15a78c3cab1edecc03d6d4eac719d780bd45dd1da401ded6a2554b62d5015af959b8303af2d64846d75f56046a5a59d64d3694978d874fd027dd0a60f15be84e5902efd179c2dec2ef15409a4e801c165eb6b69c98c5c2568334f00b42316daa0153a2fc17a0e9be00f76a86e4d9e12d05e31f177ec6bc74e8486eae2441a04d9794792ed739c2893f31ad9ef7e10b02b75a1211b1ad415380cddea1c07f40d5ca27ec9087e81d5030e38e60985ed6ebc76cd4e84e45e8419b9fe088c5f4f09967c0f01d41af42c6cb73b86809a4e750d8ddddfaa7d3565e9db185aca2eac7bdac0d9f879b27702bb1b8c0e6e14fd3c8791d63fd06496d0663e00c8bc8bdbc92e5cf6a66f7f7facf4906831dafcb4edf20cd466f367de756a034db15a86430629a68bf2f98c47cab3e954d5ced0c81f3f6cadf2f4753444f26e714587b837c3e91e7f66c7997739019f1519c1cba83c7322dc2c59d2091b6fdd2c71a6f40ef3127aa18b499cee749084edb0f8e032c50ac345ceedcbc4185b5213232f3b8b3ad8ba604813a9bbd60e2458d85faa9337698c797639fe69f09e0a361f970c9bf7141b5f8125d85ea56ff8072e63fb966ae333800cab7e554a6f78f90cf8e91d5c52d513bd25a6a19ab91d1a0bb147f642cc79a10b034aa5ec1bd6cb969d164fa3e31ec0121f37128a1b19e4960fc494f12b1541d860702e4b612fb6d564ac69a9209151f40d0b22fc79310fec909d6e9b37f2d286c19f38f6357e233bf5b6e2de4cb8c17d7e77e486148b3a07a9b7ad662524a9cbaad877ea0304dc940744645ba6dac6349f107dead02df37b143bf3817c5f32023e779f1a895c48c7b7d7e8b4ad9fd04f1170ea4b911106abbf8ae44866f21ecaa4eaae0794d0144930a1f709ed152a7e764f93dd0ba227c6d9bcfaac35fd27787830483d1c8273d45fe3c744bb39aa63e6b1d0e98f25161d566aeaafbfc618d1abad01df1e61c77708b4a11b525d9157ec5c20a3961bf661f8fd3dc25a9703230a95af40cc5ab8908ec1db334a02b0986ece14dd16c2c41ab666455ae0c2c29529b99f412dc21ba4964c1255a840249098fada8e6d8f1d1bb25d8428e45993b028f31b9bb5c2b9eda174dbcbbd28a31a5380a6499bd580be90dfb1404da0916dd47bba9b682c351104c48ca85681488f45228849134c0b812beb0e912d222aa9c35ef557f0e01cf5ea3d1858a3dac58c0858d334840cc8057e692004669a3eeb8530b2472e29eaaedb683feba82775e951c8df505452a6e4c43d2a4341cf4c246d2c18a148c0fa161aaaa54772b4eb4c4aafc114abc2436f5002be7f5d6993afe117651c31034824743e8a3b9516e52956876044860d52a4ff297771596017255bba30d5310db65e61d0eed375d805018592d6b6a57d3cbb44e617c89398767a0c1a8c8671cc90619c0bb45583180b98f44bab102d51d107f04e42b03331263cf8c1bbf5c4ab99a5fb0a7c8e7b47c6212530d8742bd47c2552960408de6e0574392c0ce3eb8c700ca782729c82098915d72a691850ead19d92daf5c273ff87d76c740746236faf9cb01d2b166d6a26fe45e4805216617b7460300a7471fb360c48fa3b68c2b51f34babb1203f7f6fe98027ee407c82b5bba54f501f64e26fe3927c1e736fcf9527b54ff809ae172d13f31cf61ee39b52328e49d9249dbe932fddd7d0d0b9f004af22100180efd40b8f2a30bfaf804a3a5253e3809045ad4d6ca1eb641d300698c283e98105e3b3c35d7bac3c5c16bb707e19387804d73f59280eee3005b95971cf8531da0ede34a00a07c2166ae2871e14c050dec297f7dd7017dabbb042b8aac878c9c36c1c962bca5caeac5daf5c215100813b62cdd689829f975ccd2b70903259d31ec35de076cac66644ed8e2cdba42e24a297bb34cf853455684522f8a1684bee856f112f678284a19a98c72844dd70bab43f54d1148bd2330053be603c394b497e4d75baab7b6c0299cdfa2d5052a1d2c28fa2e87697978b0d0621364b4b2a13919c7f9007d8578c66e7a281b24f9e127de5528e0685bdedf5faf1bf12d22dabb7bf46ec60e619496dfe6bc1540048d3ef67bb3386ccca5be622d53ab9dcfeac68abd7fc459871b426c799945839eb1ce85ffd9d7ccf6d3b7a515072d94c2091671b996175e2278921f62c036e2c8eaf8590830bf6b022a92c4bb20690b94dc7e4e52b0906a778e2942c06c1175d4050ae6c93fd288e93c8c1d397c463b6fa048cb30058660112cad015102f140d32d005056442e1b6ea797e05d56666e053e1a713771654197ea402c459a8a4f2ec5863b1fd66b72d310a2e29abe1e7d0f65bcddf62a165db1a28e74035aa9b733e0a4e8949e8973ca80e426435909a57d738e44b926525ea60697900138f62934f9b4731c5eed49e2853609f01f6ac1e9c81af875588132db215ff8289a690c5400fa3d442f27eda1fe452d971b711fa3adb2525e86f90f95e835c6b80ab23351a21ff3be96d8840fca942ddd48eeb322a99ddbb26a7f276f6947824a24c05b479b68c2a6ee091a5c239be1fb59986195522e574fe205deb12bd6b668edcd546ae368ecc9fab7d532569917d45a0f369a8a10000aceecb6749d4d00e899ba9e97450f9994b2005163fa2e60d980798a8da54716e406bd8d1c4e7a3666a368884193b14938dcc067fa04c420bf954d58a1dbc155b599c916d37474854bf9b2bc333d14aa276d6a107145815676552440cae7899cd3b863a9c49f676c0f34027d4f703e1c2b60e4beab5b800087819ccc62385afa10c7c55a6d656df4ae78014383299fce61c681949c313a3b5e902a15a0d48f2c2534cd2573108e75dff36a98ac267d65d0e9d22ae1c3eb9e823231ae34c03fea6ff5e56e6d163937bec1dd06a6e260d515e2734f68d172f874cdfe2090515c4767e1f9fa734e594a2ae7291398e8cedca35411fcd6a6b1e8adcb0ab59d1eadb6913b73c7b596330bf6845b4bb1b0921da549ec9b79e3cb63d9977ca033e4892ff3bec020547874f71a1d1c53a20962d303251b7bee6ab9deb86719b1aa0b86864b6bb98e71eb39f9b257594d5b1361375adce55d6c36454eb1d13c5b1f64a6a8e1decff0596b178a18d8c849de31acf680646a0adf1b796f6774d850793c5f6dd03f597861efda0c0d4ede9822d232d13671c6399c70dc93c502be57ac63d79920755770692d8b3478e7ffcabb00934e9781c132a1446ac9f605a2bc9425f1d139d5868f2d572e51cab7e349c3a0b0db45fd630351d8116ff84a9a68410486063c43ee029619a38d141bfbf3b94990439109eb6a47021c7946c374c58282847ae326c534d6fda1360e419fe0930100ff0ab95909f9c4a69b41752eee525a96c4dff1740497a7e3d865fba2fd69e807b2493d286934c23c935f5c59b768a0941d372d5c88ef8cf5f20a2bfd67ce937e679f57d58bc8930c4cc5d712065e79e9f3234609fcb27866a6ed8021deab42d7b41027e4aea6462058e729c5f02a44149443daf27eb1ac4dc36529851141da9df05b0b64d6c39345eab9770c2d52fb8e1666342260b76759c66472f86fb3207b7ebcc0cad99adf5178ca08a7e7e8a0bee970b88b08aabf4c1d496f184c98819fcb56ae4127904fd93dd5a5eca23a070be4120d85b18640fc8465778570d04bf19bbd9d438ebcfd002752e7bc23998645b74c1191b702e040b410bacb0340f40824b1d6f81fd68ef6559b79f0efe6691363d6b19c6563f231597902730c08c7fa2b1ce61df402d3d7e8ac08c1e97535fc47f521abf0f531f722f95496f6dcba877b6307d81ac27a4488fdad563cedc2dd30628f674d2c8e482f20fc6396554d723334cb012b380884f58c9378f5bbf1eb2a116905f1424e168f5eb9a12f330578a154329b5549f9c83d698ce3ba0d6860fc0a169e3b701972d144b483277eae00aacdcf47691b8b405217a5aef7b31da52a0b877c383144004242720112c26025dd49cc0b0935de9ca1ced952d682a91bae8f1174a6608c41cbe2e69cde4cfebc01b6652d57d918e610e854cb7555b000b94acdf59d587cd46fbb9aaa508fde2757548b7ce681945c72d2df2a1d4d9323b74ca638145bf5bd1968733fb189eff0b8f6175b59127eb5b6327d123c1376faeae62bc83d2c5a400d2cdcad06c2657ded64d2be8051c526c0d84d219bfb74ec7c2708d4dbde40a8c91e11fc5ddbe6d24fb39de9ab6136c5a0100168082ee581bdf95700b34d7dc4bfd28888ea874019b8703a5a10e2e92942d1a2c1b4d1a9801d8accfbcb16508e0497cc72e7314d358989bfb81bc78b7b32aed9bf4d071b0dd516c958d2256437dfc650b142bee2eb397a755c4e788b73e3362836e5fadf03d2cc036b0906531532ad04b17277a8ca8f70a03c8160f183638b180af1f53eb47a31d4b05c46df4d1dc02b0e60526a727baa34d13d029fb836f7ba406644c296a7fcdde1dbed8c1605d9647f2fa4b2162cf75bc79a43f4365200b02100897b87a0a4a154b9c216b555922946f3fb97a3be95cdd0cb4e7f45646b15c58208025f2a3c7e2780085cbe3b89ce3031446c2c50c3d8e994f37a06c2c98b4bb7aeeb138736cfcbe3c909fa97e308fa0ef03f6b6cbbef5e7deef8409cabefc29df23c8ef7112651cc6606e523d1a3dba1e9bccf041df907990d9bfc594ef2cbe8ae130434c2d4e3da265005efa7d4e1531bd853ade429efed141c3a85f7747f83ba54850a99bb7872a5a04aa5b0cc321537501d26fd04355fe7a50b4a91f978948612a1b074520d9682cae70933e92c52f98464a93641a35c13785655bbb9330123a63928a469689dc7ee9a8b1c78a6594879bb0d69450a0876b037285727586442a6a512a746a76269141b0477113fc4353592bec6afc464656857966604c8908349ee1f7d08db14f37c673e3bded0489900ad62263ee46c56f4c2c028a8b493157e596c02cb1e36bd96501c150f6eae08e4cf6cfe81274c23f7e04c7c560083635df0c6c4c4081802f2b24fa9732531c4db6a48c633c141e487644e55044723a45aef4ecf74018d04adeb5f58f33b720460421bd362b81d142e51b63faf70ca339a6e2be0711b2e5d2dae1d78c8d61cc4ff8358b995f86d7b181cd7813d01e0bce2a3a86c6c52973ac5bc31513e7bdae622c890bc78e24a1ab11b3a3c94d946795c223d7116cdd795a4ef38ec8b8b70e218f45df337168531f49a29fee83e474a52e7dd0ffb704d5c314402116191a5e32f6a0128fcfc15b384676bbadb7fcf1ad7ebfe7323e1d1eff0c88a1364b483755b26db8683f0fcf782de4e88752436c240431c6fd3f5cb93864f224e80da3203a56829c9dfbafe83243e501040b91ea321ec5ad1fc7288d3e5b1e97a7ba21f12b87018635f1e8d7ffd49ee7557a08f5c8068cc201b5f0fa65b285fb08a1b202af5c2941abbfa60039d0738fbfa6476a21649547551e6e50d2be0a0cb8b16b0963e0280f4af12c1954db2b6f536cb762cf4f559df4feb89508c648bb42efd0ed11e041e684ea407e876e6cb15615592589d739dfad5cdc79585611180ff71972d3db723c05c69180353bc6b4ffb5218f803717ece0ce67437e1b2783965072d3872e92fd001c36ba739c0cd3a05911eda30422424b7485956a396a0a2182f608583fea91c63da3f74c60c6931a62e4b670718172f990bcc7c1729e355be8420e86b80f9fe1dfc0f44a56ce079837047a3f57ba6139c8ac44b6dfeeac81481c8a5e0478c619cf948394c9712c3f0d5ec3de17cb6e6449aa2dbf491a920289a21e43c3901f9372cf89cceb9e871525fff02e643ca55873eaa7a8463b0e7d5b6c90839b9c9f4db9385ce3bb9474d85e72e591d527ec5220ad8dbd1212a33a8f3f533c29104a13a180dbdda185ea29fd46f4130832354798956123ffa816ee74d5cc10d3240e0e0fe68366c0b99bd0cde17d7d40d55b80b855d63e5f50b085cd7f5f7f5f79ebe1e6b2e73c04a9f24c058328df4588103d9d931ae287aa8a024b46a39fa838e4a208827222b73d58316d7acbe0061d7ad318a9d215fc3dc082f5547dedb90543f215729dc75ab2228496049780d45f5a40d020c4c82e7c85321aa96500e0d65f25f5e608a96745ffeb8ec54006d4aecc6ed2d2a88a1398b975c70b6d17e51ed8eb5a6b2df10342bdd033208431bda8a60e100e39aeff2f7bfd0bbb6a537474dd27805455c89666afb345cdd49790a2481ceab38f1d2d175c208d6393b64b850868701516ddf7a9382908c120f4e2c6794a7d4e67df699be8629002f28cd53fe632625e5427538bbacf02010bef2d30f8d0cabcdc5af54aca4a2e35689b39d71fb48251d161764281461c8ae018e3c2dd577788b0f2577fde3358c723c94ba1656232492cf3953bc6fbb178e5ab991027b5ad5ecb104640dd9d5967e5c851b54a33a6fe1fc3c7f22ee9ae5aed9b08706f84a8b0423ff3fdaf4356c605b1728fd2bf5fe6686c1b4a48adf5a8808244fe3714ac0d59c002047b24533f124c6becc0175da9ea40660562a428147db840e3d962bc10a980d4e9fc99731c9d9280743225c271cb7df5af167bb78a762ba53eaf1eb3579f0d0a26dfbc97f304a1dd7b18046a2331f52a5d83dd8ed87c5d209fac31d77b547326a9c45085d605823f4d11ecc2f987783431b3d874decf705cad333f85f38172d8fec90b7e05d6125b45c22bbf0284aa5baa91fa1522d901db46287561d55c42659b9df28ff538f9fa4ba45495869a987b771b9441c95b5cdb8cd94ec7200f71393b78a120f039737e1c09fc65fb1b4aebf9b9e5ee7fdd4f7a8c498949cf485e657de75bcd4f1dbbdadc31989d63af007f05aba858f2fcd2dea0f1aad903a9cdfc1e5bda1c2a9a0bcdf33f347c0684ecf2234f65a0ee8997bc07801a66fecd2d43fcea17ea39b3aca48db3ba6465840102d54423c927d640377a34a6cd2b596360f6aa20b71c6e6b5d664cd1178d904898a29a402d2f6154266021a005b4028d24d07494a3ef36a232fcab801802d4805c90116c863b3767b2c9865a819a111a42bf3384e7a450f243418b1526962d514550832b18d0a6fe472e75e4017a39b816e98f465fb62a4227cc1c2350a9a0061c24ba0af1d857b109373c54fd24f2b3670f731fc809f585784c9adaddf459cd6d4a1845b9dc09a1a60292b89e8532efb86f8937327c3112a263e20845898ce10e2b80a8b60a6aa1fe9e658d6441ebeab1a8246197138cb39e33da7fd7f8d7afb982ab186829aaca63b01f719aa595d3dbe4e12d54c1a1c89e48675a3f6c58aade858419d018390fc554ac021bb0a9c660f52cdfc4ab39834d194e8aff1416ea1349a6303af58b683e4d9a682283a341f65a4a1eda4b7a83e102ffb514690bc3f4562d8c004e3f3a1a5c74eecaff2132330b9cfd7b215d0a08cf771e18665a10fd0f52b9e4d90959c804d7a8793628fc434bf6934568e22cfbd057663e2c9f70f9611c88c3b20293fbc567e5644acc0bc876cfc90705444955fe8a6b6dd712ece12288599526a11dc64ca861d44e257e887a615e1614f464d142ffd5afc231692b9b8556327e0b307e2f373e2ffe58de9f5f1528b9d5408f69d04893b798686cd768d431678f4d4ea48169931b51d869252fc503e2375df3dd4ddf4d01c1f99f8452e1b09e03ee55c79d16386f6ebd32f3aa70721f195e50c29f71d69d09da6de316e9be08015a063a0842b94b10704bab99d8e55b1937d374c6ca01a85205d559d9d0e882b31f56b952632b6b7daaefa4ebb498ec1a33ba60741bcb8548dfbcb7341337ad124fe16e128d6034ac2d0df1a32ed43f823ed0fe992d9b26ecc844ee23bb07b505f084f55f968a4ca718b5f5e1922eb71508963a5366fcac67296f421c9fd7ea0e85fe490be48331a4871fb2ae558ff44d485e1b49549ae847759fa8c1162c5d47008f926d06b2b83b80fbdf56902e115e4129c12cd52a41b56a5248655eac1c63b3869862a4676b2792febd25fc31ac4f5582f87cc8d278088cf154f2e2388dde9ec0a920b4c326b7749c63c9c5a12122b40ccdb1334a79dde61fc901000c33dded256a8605ac5c00e6241628ee60c99b04cc2a199e0c65cda87995f8406452cf47e3481d98055ad9b77fb996ed1de1458110ee7601e261216222724d8fdddac7cf1ecded3d1adfb7ece4048fc9393bf814c8c7f38910cbf0386e51b0f0904f8dfa44e39b5e7f48fcbfb181e62c08a801ffa2ca1a5af7bac5e87f3da06ef521630cb0cfdf7334b91e76c113eb95c400d66de5bc49fa842468212b051417a4d1f42d35c8d5228a3e683bed30614f1e1596f34b6c10a02ced489de54fe5032d1db18a32e3e19eb2267479dc7192aeb58b86cfa77cb6987db879f843013037ff0a656ba3f6f083ccf3b12bb509cee18a625e022ff7558ce8b7a3d024c46fd7e7ac095a17da2027e40920bc27fe301816aa7eb519026f83fe6377a3fabf6527e56bf4c17c5e32dcef7fd0dbf4c2b7e3b8ccd0afaee953da32077b4a16ef9a7c9dcac5a2d696e3d041756fa1f0dc84ff726021bb39df0fa1d1b0248a6ada6ed4e0e5dc487c945beb80283e4865799737ceca4d0e181b536579086ac38ef9e2390e02f3c1fdc662d8236183a2eab97d12095cb628bbaf1578e509b4a73495de6ee8d7811edfc7e88439c2096b634808950a101e4bd510818b425994c0c50201a1d5e400e0bf110fcdd7897f654b21ee72de8b5dcd11519820cc76aeeafa0b34948c007d39ff7ad4f338f8964364dc7cec63143b4a671f06e037c90031aa9ce885d40d4554d1b9dfd3270dfa5fc0e217b0171daddc2f3ef0d7d37045beaeced93832f4e0cd7d82686ab8f6be6c1df2249e8de237c7b763e603b06f3014d841cb0521f557efa2ee9f85bf735a84714c565ebefebdfd6ec924d24b2bbbb7b07ec0869082f08b5c66ac413bcd65a6bb5422f800add5b6eb47c4a6684bbf0279331910df1ae4210feae67dc155aa4f6f971f0d77069707a78e1851d7a470b97ffc28ecbb5e7f08634776bdbeef862d0979c461ab9df8bc16d9c1a9e66e519609bb8ddbf5f0af1eb3ee1a5e2e70de3cdc4e752e031fe7dfafdef5db9f3c580786b3497c6d2e0cbe15d69b0c6e14d69f0c6e13d6930e6f0963498b3dc467a35efbb35c7f7e62d69f0c6e51d6930862fe2499a24953813b38c2f4f52de1b0d877326e112d6f0453df186792d6d5643e1375e170441a408208048203b4206b22378901db1c39fec089c234680e48a8be485c40a19922964488a6e2093f5c864b012c86436c8642358821156144166c413322386f89319614466c40f6446f8c88ce89119b1020e7fda17812062448f031891030f23486083206d8d70801f5d221ad27509247094a0e4defba4889f21dac49fac881afca061308422788a50010d973f591145cc29e07b0fa59412764ff62862079756040d3af748136cb04790e0920207dabad1048fd9f24a78b46ebc9f06dce081ec4810ce8d964f1d21c2bb0a2dd08ef4a0067980233770454e85d32a478ed0807747663083b602a7f2722408bcbff8b9e148cf0cf2080b781c29c1eb088f1a82665c094147f6464e1cb9496da69c928b292dad7df15e782989b6225743d7e02a47730d176013969005df311ff2bb1db0c267ae61ffe61a1462344d6a93445fcb3528702b2b0cb72db8cc36b8bb6f9c892a977613f530888171f1826ae1d2c2725ab140a4a09874899439bc69b6d229c512f9b709290421349c5840115990104148295b6813300848fe9505fd200bfa81d71eb88810ce1ba8442d61062f57901f34a1c374028a263e84600426e0304133e79c530ee173ce39e79c72ce39e79c73de54d08f2014e8a02159908b882756f02723828877cd9f1219114132228cf0272342888c881cc88800e24346c44b46c4cd11ac43cc64121c11b648104aa45fd91042644300910dd1c39f6c08986c081dd9103328a10a9910444ac8844022138248106442f8f4c884e09109f19209e1920981f3a2a36ef8fcb76d48f3685a3a3a3e4cf9c941f74b5d6f86f653db33a675aa038f9b0e293f2fc69c2aaf1763ba7497199f2eb3d922f6624cf9bd458ccfd7c1e5f566d49ff3bbca8caf5c95199f6f4b2f3e2b45dd0405f1482389267d7e51ce59ba93d6a46e846fce523763cac67b29d34bbd7f9583b9e6c554ea7de94bfac598cfe51afbc598bf5f8c99238043030db9065faee61a73c358d3368db359022bc899f978cbf3b77fb9f41873304ba0f498e3f204b852c9f4a5df72cdc64d1d3695ee8b71f3bfac25a01f672ee6cf790257ceccbf8f257cf19274e9f5e35cb3f152b7a1e8d772cd76ed46323d4af7bad2435eea7a07b8ee5ee92f2f913a1a423a478ebda56bbbafe002cca2db4c70c381cc8815fcc98a3ce15dc7644586701d5bb212b3b1c2e7ef1212ddb7574c65f666cc97f1a98ef1aedfebc590dfe9ebc5e56b21ba00b388b3bbcd1ecf8277ea7a31e4e7a03dc7ece8cde3b179030d213dff29d1f5697d4f370d212d1fda78b00759911f3c1c3e0765457c70f82f0a61c81043860002880430082245b4602d14c8d80296403f59921fde391ebf06da05181631c20300f893010184084736aed9101a6e980cd1841b05f8a08aa3285c468c80c80fad06dc78000d221c3cb9018f31deadd5801b0f0042063300e206206e6ef09cd467038635102162079717472d4036b454a151721089bc6087d30284e8c99f8c080b88ec90a34284869423bc50a0d0312591a38fdc5002e241b2418f1cc40a4ec005818309301625d8a0388106840450911200fdc003a827054028d0018826f000c413302094e47004c411179123cec29fac080d456e7857a185116834c10581650a1f84702428073a43f1df8d4d8e7083273841a630a28426aaa002919432222184048d221c11638442348a4084191f007f3222c4b06d41bf2569ac761d86204e3fc64363570c29d19822888c083cb221c8644370b594e06f4ff8f62c459c6c8810977f9f086172dc93bac62e6fc22ed80443104adb8059705cd36108e2a5ef3004c110a4b18b097fc9e57197c3456f1d34c6b72fb99c682c25a7e49c493893527226e19c4ba552a944630a4dd33419115eb22110699a065f930d01e21a6973269af4a6cd9968a9691acc36b8d735f93b8dc5682c28c61d01a50b34e6caa417509e8dc3f4457b7a5ed487bee0a9680fb8dd437d680f9eeabdba75efc5d05e3e9d97fe6871ced0555a5e34ab75b5bb9c13fdb8eca8b5f787951ac4a34ad76db84accf45d02d91103d48bdc81f92e7362be4b1cc8d2e2bb0038ec18d03aed5741ca38a1933e72a6f3018a0c782a59e4c674fdc2745dbbe87a0b7fe9faa3ba9eda5bfe964cf8c4a89241459e4a4a9f5db934e2a94e1f5f2679aa24dee9f42814b41075a2fcc9d39e5c0649284f5b721974da91cba078da90cb248f899780f708d0f52c2e20e5df4b00cae34a42fdb67c3f0ab7d82befb25756dee559583e3fc086afb0ac74187237eed52d0f480bda52b6a590cbae3c51e8d36fafc2694481df65a74498cb462972d92697ad398d2874e95d7689d38842eb77d9244e238a2096bf9ca5eb1ce02c9f79cb3e753d3ee12bbb4b959db2bb24924ff0f8a6dd5f1282666cee4e5ff48727e8870e19cb352697d901b106414c706b104c2c1961a5c5ec452987680dc818cf61bfbed51efb88291e08bb0df662dc5dffdea7db46fdfb24d2735145bb0da721e8e1340430e0b4c39b151af2522bd21f79afd6b2a5c57e8fa126ae0686ebcef4e4c5889ff5c7870fe0f7610c41fc7edf603527ae6f94361e3c6f9b8128147596776aeaa36c3f85de0cf8dbbf2878fc37e3d6d8567e7812dee9cf8b6245c92906859491837833f18ffc28e1312fe1f18b98e0f17619e8a5fb06335da184c77c8af1083bd3131d7447094af1bec1f22baaee472ce20c7d9c9ba3775a7c40f98353209c7edea1dfb7dbc527312563f45fb6d162867bc04f86ce3ffcf25210ca96b26ddbb66d5b1234e43a775d735d6fc15dff1414f903b96eebf4861c4208650e2962116568199f74ef47ca90377772939dfc180bd75efec81ff9237fe48ffce9f2e709ef1c8f7dad2bc162aa06bad7231e1ebf1ebd195a0d7887f0c9bb5f80bcd7a352edb8a0ed2806924a6edf5eb956393bb37341e69af3bfdcc29be87af3f6b5e7b01d35cd296594aca9eb96d4e58ec3d6767deb764cae75dd66a5a3c25d12c4b97a4108e193f049f8247c123e3e770efe765ff98077f5da56af7beffddbdd375d94fdb28b974a3d77efea9dbbba717d6d93f2573a2b9d95ce4ae7458ad5f5c40f2658c4f71e7e9b49130e833f1993214c827003f0276372c35f67dfd3f7543e7dd6ce7bcaedcee1420cdcfee3d4761d72da77c4d7278f9d05e3f26bae616bd87ff9d99734c6ebd67126da763a6cd82e87d649296f7dcbfd949d4a6c3aa99505949f05c3f5b186eb531f089fba288a52fa2cfa38d7a0d53ecc2e6d1bf52e767c1a9332debbb0aca448d35621ed7a4c2a6fdbb6e5e05eccb6e5bcc57439f2733195c23c8561c152bed31e4e690b5828a574cbcf7d870ee06a878af9f8366f33d2eb47f9171afbc699e8ceea2c188f8fda39dba84f6b6cd1727a7915fab6ebb038dba89f83dbeaebd70b8a2645ab01511e5379cc7e3c539a37a5d42606d68bb543bfb27ef06e512f03a71b64b176583e78b775eb72d047e587df77c47e7be4a8afbbb360dc12d103d4025ee90f0fb1fa94c793412bccd70e451f45dfb680a2947e0cca3e6b07a64051bfbd85d9a577b1f3a3f6e32f5b6fd68bc6a48c6dc6d2d1d8a95be9684ca54ba131fab7bee4ccfcde12835d7cbe49db28fb8f6f34c6513a93a943d9cfff9232a8909c997f35157a09f518def5ab6357e54cb40e1ae3f9bb0e4a5df97390ba9e9f0a491994c79b813d4968c1110f1c7ea7457c521d38fcaeeba05dfbf099b9c7cf7ab17a401666c1f8b596d503b222d641ebd5eeedeee4b676ac1e3005baf2d3a20543664f9a08c255e04fd6040e9f1531a144cf0fbf471c01fcc9887cb88cc8257362262382ad5efc721e5e1fe7fc99e5f4902ee1317e8baeeba05d8799cb364a7f1fbfff56ae95ceea155537ee996b5ca85f659bde6efd7897229428b074f484eec5bfcad911b5fb6fa5ddd76c4a0b2a2b9d94ab3de9aea8743d72d2afac5edcce0ee3be634208490f21046e87224100beed3a0bf0eddb87d15aabe39772b6b1f2965b89f6f547bd1223be5b2ff1706fb7a2a35efd9048d63e7e58729adbc42003bc5d042b1fbcebdf117394225cb9563990053fae7678d7b7851e7969f5e27d477c8dbbbe7a71fd1743dce5b05ffa8bf555f9db5dfbfddac7fa71e4acb54f2ae9550e4cc17ed51b9a5eee935eb9361d78f05a2dcac36c03869778d87440e1368a84527a1448825f22a1fcf624940ea5bd7edb4b3cf0dab7225e77ee3af2b3ecd37e52937eeafa26adbbfba5fa2b9dfba4cfbf72ad7420ebf1fc32d7c8ab17ad5c5be94056875cca90126bdf77ccbe63eaa02eae69570775d93bb976bb950e4cc1762a3c3ab6fc906f42195b44d0315e1863d4e08d56563aab6c1141c78f7dc629e18daa3e5f76bdbe8d72cac97b4b069a76574aca29c532dc02d27d76cd4d253ca6c09b1703de4008e19c70c299922a4ff880a30c42b80c3f73faf0770383e0401f2ce08fb5d6eeb0d45a6b6d9cb4da99c3cd6aaf5685ccab6df86e98cb1b97e7e4707824e19064d06584cfd7f3c9a26cbe148a3e22103e9fbed90b7a71f462be4cc126f3e10f9f1f77c80f4021d0c8061ac0636cc00d984323d0823b3cbe84f138f49ad8f0749b178f0fa394735218a59c93d23a69add6deabd9ab69db8631b77135575229d75289ab5a73d564e22aca9cbbab3057e6ee02387193859ab8babb0c44bcee14a203f0f92a45757701f0f92b2b5cdd3d017cfe4986231ead8d32cc5ed7618cc757e187f95a45e7d27031116302064cf828c25aeb2384eb53c4b2c0f2400619ee80091c82a850c211ae58c268092782a065bceb1b5d872082c3c40cbca76a9053d67b2f124d03239688c1122eb0610921f36708c9b342153a08c9d3528556d13b182279768075cb9382091f3da8a2a7871e57001142921b244a9a5042637770026e77c0c3c760852a745c628725784c815f8caf25ca486bad4f2801042572e04409204af028618223256a584092a1274243530c112921a4c49112454a502cc815400927849d7e534ab9d275172e3b095f42d882aa416d8e62a153d97500706b6f0c130628f0f85535359c19e863bb67d800aedc8daa14321480b5d64a4a274591e4dffd68e04e71fb2202e0cfcdc91909858ecfa9600c70d95d15e40f3ca3ea85969732b0b43c3faeb507f918728f06cf2d65e0b57e7daac2fb999be3dcc6d930c4e9a1e6dd43edc16affc28ecbdf4b61470b0b8793430ef394f04949b2df8ab35e6eee8d73fbd164ce444b140b3fe18b2dfbc5985be3735f2ef186350d76737b43ae66ab46ea32aca0b9b9a78caa6c512ed420b75def00af1de66f7368d52be594d55abb04ad7d3084040d1c868ab85ab7fc5011234d2e2088ee578bff2d7246caf9f244b1202940a4372ab54704bf88638c24cd668ee18b29d28d292ac4d00c3ac048e268079924a2b0c15a6badb5347248220997325a43763821073a1d901b364fcea380474c1239dc0093840d35b8b842e7a50838a090d86901c40e2e12891ee4b420d1830e0b122b7821c103123928c9428913254b9420515244c90f4a80284181121a849020b44448263404212142303881900a72844890c428891249900cfd180a92242845a4a77874a2456d9491d65aa748d26343121624e9c16badb5d62649729048723343153f38e2091a3847082971841045470c9936890e9cc08d43ca218c33f7c59081866fc3319450ca4e6f3a1f04f44d7cce7f3cf29951af8b00ed38e4400caf2d786de195e5b4008e356d53717af74a871a7f91bf15e40ce6b07238399428702c63844f6ea4f85d843000fccaf0fb82df8ce3fdd0cc6e9fd03d221c6329238410c2873752fcf703b7b966bbd70598059721dac86867405274841024482031826408487c80c4064862808407090fde5b6ed02490350149132d2fffa275a3013125bb010e9aac0787bb032d1b410d7509d40a5e6badb5d65a6badb5d6bf3d0eed371cda6fbf7531689d0d0dad42cf97ff125391df687ea37baa023aa26a7e9c1f810b3c9e8aaa490f300510558fd77fff96bc787c491f05e571a7674168ee63febf9b5c2b3e269729c7f432ed6c3d52c6c35bcfc73c6c5179f8304854ad3c647908218750cfa28a28aa5c7493a882f9e88376bd192d5f5d3c3ee9b1101e8aaa958f8f6711134515cbc7ad87fa6072b93ee66d68523a9404fe631ec77f4c17430f291f43cc7f97d2e1d8ffff5d0cffbbb3a1815ddf3171ca3ebdcb5679d8826f7a78c7422e2ca4676f469194f1df2f2f95dec577c9b58bae438ef21a0b995c760a961ccc4ef9975b3ccc82bb78eca21bd21dc6b88b37c1ed6277f82ea87f7968c35fb60eeae228976fc9167bc6948bfd8e6f78fc963d8578f8fd8e850e1053f14f5b0501c4547c95aeb77094ff7ddf0ccac76c14f9a616bd443fd1592812f1d84d2e938bc7a884bbf7e4226e308cb2115fc67f8f826fc35fc781d8d5b8e86a5c442a4ab319a5e2e8a852717464a9a0828a4b85d5a8d8a8d0f05f8c102aa1e5a4e5a4e5a4e5a4e5a475d43a6a1da9fc70d882dba0fbe69a97bea48cd70b06e3e1910f4b2945251f0e493e2f067c1a322dbff7cdc5217571951f290577e9528fbcf5238d3a70e32105bd19b3d4d3c3a54c119269f89df2947a26d08b3163ea51bab98cc5214451545454a3b5448a181428386fdb3671bf710072fddb93bad612bd3dc9c46ddceb6ddbfad6b596c010c4b97ffa2bf7a66eeb72e48c31c6186b8cb1c6188a1aad25b1d692d6120e06531899c2070fccda22d87c5b9f16c182607104464646464646464646464646464646464646464646464646464646464646464646464646319e16139e29729afc48618509a5047f23e2dbb3c864520c3122e2f2afd11097cfa964182ce75c042bea30141515655886c16028282693c9d462d2048b26415204211549ee493018ac450483c18a603c4b5888afe7951e88e130bec7f1e4cccf5c432b7d9fc5b4115fbebce122c6186394524a29e79c734e4a29a5b5d66aadbdb789561351ca5c53b3d9d062a2b504fc526b7674045b5899cd62942f29a5ac734e7a7444e9acb2bfa0a0d6ac35d3f09fc642944fbb4859a83246d9cd58b44978511549dccef12594dac69cb7b46d3c5e7a9a6f3c5ea23c78b6114bfb4d4f43caa37ccd2809a8bce971a874f94d8f23a5b3b986ad5fd22e9e4b5d6b48e7ae730fb77ff9730dad919cbedd3510e5c583cb9761dae42895009b716adfe9cbb57aad5ef7eea07395b3d2f1d94268cc0524b4d211825b1013f07bd1318d45159c7176ef032c584c59166cd639699ddf62f68f4ff764f58074c3af13c216685047655c7e2b898edf5564c4b0811a9e86d5031a00be7c958ec29f73569a44ef8b7b82609dfaa02e2983f4b0e50a01f18e562942771ad346748efb145bcd47c8cf10a0558eb672fda03d407ef444d506e44d2ac4a4ab9da0d80f2051758dc462417c84fc6c2b9d988f915810212b9d55ceb6726d2b1ded4979dabfb7ebd0d5b2d2a1415bd47ec0ef058d05e1d087c38e33d1587bdcbd298780d8c2eeb68ee84e835eab88cea13dc576e330855cd76d5ec61d6b07a60037d6cb01936b5d4bd28e0ea1401bd3214051458d44557c930ef1f8a4d40787df2242d787748891a892df2aa2217cfb7428aab419258aaaa2a8ba2f7987bcb37ac014ba6ee3840f4e85a2aad326322776b87c2aa3435c3eccac1e30858eb56323a7b073a0059df4c88a5a394a394efeca9573a692d25a29ac1052aab37a51c94108394829a590e3385af29133f649f93b8d01b7f65566dcd2a7dcd26ea51355af1255b0b3d5662e44d3ef5bac0686cf9f374342c17b2988ed31144170fb925248df722f370c7fd906f774f66654de443f28b8fc1bdb6235302fb7b6cd98fd4b611c3ef731fccd88e176c27db998b2a51f6e7f9b41e0d6f6a8cc8200e743d8d3d2026beb8d9ca16ff1db0e42f8479c9e7c44d5d33fc1a2ea4f3f20fd134f54f544d56987b27a0c71fa3c07dd29c6315ba1c442f546cad03339439f96ea0da7a6dac3e9ebee81f4ec96f48ca46759cf669c76938bd3c71b057b31e8e3d8f634668b71dab110a7cf79d0f4513c70fa5a3fbdf807c169bdf92ca494524e1289d4829ef5eb4a67e55ae5c82757392bd74a67f55ae948b972c9954e4c8c43232b3951b5e28aaa5fd9892adbddacbca20a1655bf8203573a93eb68ae03ffe58d0a0c94b7bd220aaeeb4f0c0f77050c50500b979c8178fbbe51e1127b31e00a06e2f0390a6e38ec313d2d5e2e3eb480c104c161d1eab5d289897111fb70c4e5e356119da33ee5f27da2f0c171e4606681724bbbb4012184b385c9d1396b58db8305cb3568ad33aae09c733ecc7ae59aca3720ee032dbfd31f2ebb1f3c77c7ea0153986f6be782dc8e86f22a2753fab9723967ee92eedd9ee372cedc73f439ee7b87dc46ff5e99f175ad74563a2b1d9ea35d01b7454aa9fc9da3db9f96d2483fdaaa6dbfe518b7c8fa9103ff23ddfbac9dcd0549c2f86fcc376f5572f13e77b78f9b76bb0e9f571f26a731c66fc398dafb18ae5eab57b6a5c7586bac35d61a6b89ed638c4bf84b566bacfb8ec97794fe4a19295feafa0e8d65c915e35a7d4c75f5e1b8eb3a6ee6f04a67a5b3d2a12f52b44ea2c8e128f0272b9a150571a8c39f0c8a9f927c54fc3773ce99f6edebcbb0e54ce17d58f3ed936badc8eaf1f931d673e699f57ba639f3eb67ede45634ed2e03cf015b20bdfd567cdbe9ac95dea6d352777fdaa74fca9cd6a948560f98824aedb86967d062b42c58fcf7aa7c55be2a593bd6b27e441ba3b5d1da686db4ac1f31c6c87a45960ffba2b5b6e2afdf75c4ae43dc6996d54385ab4650c4ac7d29abb48fa58482e789274f14f102f0277b228894f2b3fc2c3fcb5c7f6616a40b92e7aec31a1b7ee77cfb3ecb1c1a106412aad829c1cab572ad5eac979cf265395f525abfd62925ab47544939a59c3b5a0e1f15e221fd4a87447afdb4a298b45c353bb30dfbf8b1dd603b7bf321297595e093f6cdf0396da523ff692b9ca3b7d56ba563ada6759d56fa28dbb45bc0f6e14fb8d2e1a9525801b92556701d7f9ea48f1bab07e438db60bd583d6adc8a36a29892bf35f9b71171d9698c838f7767f960e51a51c57a450d077dbb1755b405cc63dc70ba20b3c882c7a72d3939131d29fda31c1b95bbc082bd17f36244daa344f79859cc9bf1a40c1d23ed62e544b887ed31c6db9e3ea54d49184688379a2d4a578cd37f90db6e628a7e2b08ddb79e970f4ee96f2f49b7ae6f389cbe387d1a08efb641be6ddbcd86b3b966cf9bb13d9d4f38fd497f62416f284e54697bdb3e6f758bbf09f17971de353c4d0fb8d6522c07f7df8bae372122ba1759b115c96d2be2f1c1ddc37ab17a6c3ebc4d681bda66f083d041d8810ab63e9ddfd53c75bddd1827a62276e11c7c13534a68f812de0883c7273c72b1caf342a2e73f1f213f43808c04c984866644454d8eac788205abc70bd692e9d90324aa62373e3a04f5f1a991a8d282843a1b8fcf8a6893379fa669f0d6f91e765b776fa40a9c08e29b3f991340b66ef58aa909730dfb62489b1612badb20b6da37e3711cf771dbdede7f5cdb3aa86bd29fc8e4bea8c4544beb08dd5f74ccde8c18f8e29cd09212c1e3c7cbc94ec7143f81eeffffff4f83a409708cd583b5c37ab15e2cd80830754529a59434cba3a21e995998724e2925a471d35fb92895f265c7c5f86a8f91cf911010ba0e4ae5cbcd7abd94314ad96d248cff5305474fa870ed98ab9c950be3d5cb891027334080131f2f2eb43051309aadcf5d5a08c67e2818cd76d15aa86933d780792c730d6b6d6b48ef7b511c6a1ff7dc7d035c9b35b40c6b15384e8c5c3b2fcf6107d13b82455105e3e330c2d8c987a61997cf71d05c9461129226219ea35d52abad2c74af375a0b8ff163bd89aa8ae3ca79ed4415acfaa83c5185821da7bdfcf82f1ad0c2b96720a6e4ef8829f9dca5df13f91253b2a5097a6846d4c47464b2c2f424aa50b57bf60508dcef51c5021d8c97f95e6fb6d89b11f3f5a6e244d5092fbfba6a0ebc312c7f827cf13286e54fe05ca69b7ac30261c078199997e96290897bffdd1ff33acd624a96585c2a8ff26171b1e4b0bc5876744fd73d16b88084d005dc30e6e13fec1ec27b9a45557fb3135154dd5393a802c04b1f4eae37c3c59bde241455312fdf341455fff24db3a8da2fdf441455dd4bdd837d6071b9e2cbc41863ccc7bd5ddcf47029c4e2b200b01206e6022db80d9afe69762a923260b49c5c585c9ec5247461601e5beec58eff32ccad355617f35bd1864f17f70231256f6701998d829ffa9987b15130054309d73e465b816bbf5331a5fd095bfbbdb5efb6f633fbca19ed531bc696d9f77616882969bae1f25dec2df662c8dcc9dfbbfeae3831253f66d79b98920f3b0c636399ad65935012bad79b16afe51a2d505d73140a651292324c320a3d0cccbbd8dd86bb789c6bb89873da5a63fe6568c3610ccc4798bfbb6f80c3a4bc8b8df2766fb9468af62fbb5398ab85cbee94470bcbc924a4555850b49010974fdafd44c4e5e7dd35b7d56edadbc9b74006e4cbb43921bec8e2e177b92f3d0df9492f821ee28b80fbd2dbfc241cf9495de41c803cfe8c9bdfa0391adb2fa6e8d77aeffd6ddbe2b6d2a134462438c9e14f56841737007fb229867849453f7921e10cf8bde8bb7337e28ce9b90e07e051f87e9aad3cea75fad3ec44742a3a3581280420be00709a4118320fe371c83c8c00bc8c0c0a2f5377f7767fe400204d3198b23253310931f99882986211c009c0a3f098f3a84f01999f790ac0f894087ac081c2a75e0401e850f2675ee6673ea2d0a16487aa9d04665ee671ccc83c8ecf4f8ab906a9eebeedd690ae7677189ee8b7e151e7afdbce988add8605a15b5ee6617c37c556662a6f8a998274fa86e527cbdfdc78c094f9a8bbef3006839cf031c60ddb3ea2caee1f51f5c2e5ffe7cdd03fffe7efef4ef879c383e31997791b9a08e3651e078c979179181d8ed4c378185d0c303ed5d9d0c4ffba3bad85f40dcfe641bdfc8b8cb52fb294f52d29461bbeb564b6b6bb115373e5bb1642bd5e8c793a3539ed7d228aa9f9bf4f331e3b54d18b311fc6461dc999f9321be61afc068df2a8a23723fe7cd491940143e663bec54d69092f351fe6df7cfcefa9e893989aef82c7e7e6c390d94f42ba84220ebe14f793fb92f631fd34eb92bbe83ae5c1fb6936795e76a73c50bb531ede5f5e5dbbec3e7d6861d97dfbe0f34f27959d12e3ba0eca73882f1588a9f9e223aa62ec248e987a79bda2aaf2c4a24afecb0eccc7cb8f979e17205155bb07dc889433c5f20b76fb93cf8bd1398ee354b60a3538fa051b5b9421f330beeb0b6373714666bf38c373d01dcf287c796dd89b51b74cfaf764e87f4f8629668ac5d75da9eb5777cd39536cebac29c6039f8f6bb76124d01dcff87c4b54d4041f4595ed6c3c6e057e125558703751858aff54a029c65d7ac3e40c8a05f9bd02a7199f9b87cfffe123d0ddf4f28a4555fd098aaa6d7353101f21a621516502321989aadacdfa0204e4f7d8c17ee90bacb13c762faf24e83efb8bbc11461927a93eb6f33d0827a9cef83768e6d0fea9365d7c11c566515e85fb9eda17cbffb9403ae86d425c7e4dc6db8c789bb1ce6d7a3c390de6a727bd5ccc36f25f9bf23854baed71a0fc93331bc65ddc9dba5a433a3eb775189e70f8a67f1942536c035c9736e9391a7b136f3342bccd08237ed8722fc7486349680825677913daf27ce69acae3cc1ab7b759297243780e3d74d73cc67f9be328d7d1cf67d05d739596dad9789c3eaec2013a673bb9b94aa774e167c4729d07544007053210011c16b800af41db8ed3a0e17dba1a41cf97f5b7b7a1b9b54349400ba1d9a8c42d75a639ea14910c000014a315000030140c89c322a1501405829aeb1d14800b839a4a6450988a034a90842888318c184214020000400c109999a9a1420008040026604c40cc00cc81980098033001300163026206600ec404c01c800900d39ad962ddcf6fa366e7cb9f4600de1cd0148c29802980790073105330e600a6604c014c01cc03985f103023f54977fbde90d80cb223a95a02654065804a804a80940195002f012a012a0754065c02bc0ca8047809502281c00c3c6128526296ef8925346130334013e026802680e614003447c7072ff1a53b4f0270a3890011961fb0e845485018f031abfad970e13309a609304d904f5829e059e8540efda3351ddd04dfb5efa40dc5252e2b60dede6f502acfa2a2b36da04494c87d6e7cfa9bc37ff32d1f63c4d10d9f167bd8d9ed59944a53fa58eefcf7c1d72bf9000ecd48e6ef3969a7ccfdcb89537d79f21a937fbbd66738dfaa07bf0a90d5a35e5b6f991268b56b5273e5937f43eb6d4025755674a066d11503bec58c1396c9f793fe73de6d107e047fb3769be0ecfb6149411304d602ab1ff36ee968cbd37cee25046b5562dc3482c025c4329abe901d211f03afa0a41aa796245a32a991aa269ac1d3c6d58c0361fa6602df8d6281ab6b863992e05e840ae5497996ea6e698c1b145c9d21a87171adcfffcfc9272dc537168a770303d7f1086d86a7a4719718db0caefc97a9f7244a632d575c07f94d1db9b415d9a6a9e2ac4ff472519ca9f4ea63eb157e20c6082e42d1354c5d4075cda21f246aa92204506568332de3ee640a0122aaeb690c52d97ba6cc5fe5e67be1df59b21e56018c03394022f3eb9ecd82280a40fcbcf456a615cef2c026f59038283348df8b70b47ad3a403bf85aedfa13a6b6f6ab9a08e59f7a9d1f100ce269e14db53dcf82cbe1ede51b0ea4da78616cae098c1f827a6e09d583c6454806f427c670ae8c0240b463cacf20bd4d059b78e10df3b6160676ef873e967ff0f57208778628e760cad931c5b6d45ba83129a9061137e8500c730a8b1dd51e1ccd8daf1e0a0cd2239e4ccd8b9512f0db63d96c6a93c5d257b52a075e85df3b56a734abdc0e43b29a367d1e29eb6819df8f3d48795031a0befe460afe8be1d05a089f4f6920931e41e3b9e75eaabb555badb85e1858387cf97f99c1bc2e48fad76b3f42674557980a738028ed571dcbb0d4b9df8975ea0eeb3410953027b940e391131fcf4b209c7813ac248aa628d6f6d9bac2c26881056e7a119ae7defee9a59976e78974d654c5424ba2889660abd289b24362feaa99f407da23711820e0514360877a28df52fd337d6707580d6196ffdf02273bd034bc369a2774bcb2b3b9c5f0187a525ba121b71bf4eaf2992e2ecda86a12eb33079837354a5eb56b6ad7432d8bdd83ced3756d301c5f59b052bd7b3afd23e35ef9266a2c380ac560cf20971638692379c19a4765e50fb570545a47594d9ff2919eaa7d4cf1d13a8ec1be05fe254a3fff9529f563d16e10f20a9f674f876563d732d47aba7f5a9313f1d690352552b8ed7d1f3b47be7c23c51abf3f2edc0e2f4c0a1ecbb8080276bc5f10523da4864c2f1867f80d88acfc63969f3f83caf8048864953dc1061206fc0e3419ed67914de79c6decd98719e2518c0eb5dc55c4957b975a39ed8d035137e965c2e856c1c0e03678a9ec7c95b898e33ba6365dc2379f76c98a9038af09d74cf82ad548f677e8d98946afb6a6b2e451c5a3ddb187ea5dfc9c21ee1c5d4313e135253f58a309eb08d9c71cd9ddb358d3f21180ef0f1001e1424576c7fb4e780c5b35740d7d143078ab79a143574a022de93b6cd9c56b0e3407621061df2a05fa1696cd91bf72849b4911cb982218cba0acb2d8db512daf567329f4b747395303a4de209dea14170214f9613c7c9942d23a4963d358c9b0f99ba347602a1d061d22773516f73e45a79cfb4455552240727422b8e9e19dc99e16d634b9157c85ce9e3c581cc72765671586c6cce0fee7f0eee7601a5dad43f12831b8dadc4ce301bbc540b5e67450850d9d749ef34e7a8c22fced215c41b38668156f16ccabfcf6a386a1d8c606fb458581775a1b1627d5a7c8912cef368b1c21acde68cac0a159ddd9076f12c2ff95155e7e4861e370521e984b7f90d0c0c48ae27129d8ebbb5acf315eb18cb2a63f56bddd2edfbe3f68a27afb53c57b04ead9806949f0403ad5266eccfc401ae95a8adc8839f8eccdfe54b58a5376e8d3457cdc4005e1d0fdca48d3a57eb719c90aab4e6397044309137299d4423649058484796ab0e3248eca1b8a61a06196e2d45dbf9760224e2a9fa7817342f9bf813a8585d78247a532eb237da8d0ab3e850373207069f409431ec3c64ec23b072d369e1c36728208e43e9ce9ecc43a212a49b1a6aa8a8e3684267b8ba0a8cc0af48c88f80a609121fc3fca8bcf84c81099ede1d563bd21feeaabda70f0347aacab59df5c9b9b93c7d4558d641d8d1c885c9331683327191f5160b0f3330889082d73927e4ee8b0b3feb04cc4b09f43cc51b6e1106eebfc16affe0d7936d1dad8b34883816eb49fea43e88f36aae6b11a9b4f5578c8f05b0b5b59334ecdd25fdf4cac2c09d52fb114b2440459a42f552498729a02007cfe48419f125102ea50eaa4d21833b39b1532f334dc7d635bf4d0b9323bd500b0a382de9702f911d3b6c16e7486a94be4097d2e6293f194fc442e68748f931137d7ecc39f5e5325e14b0d3cb023638a9e54d40a27cce419b82e9b7fd927630c0de85a4c600e853df12c53ad273911ee56526608557ca05e396a8d81dc8f2071ad71a47b4d48194667acf361e5c72bd3dcc97c98dbf95225f86e178ebfc6c6e4514cbd110bd95a9a7262fe256c625bef211b0f744ec6f7fe03192091cae7c386bc846b40512383d94790d3be59de0acd070d69b57b8e5d1676544878a5d390d93c4699fc1a47c94cecad56a4b3b7d9252ecd2497eaaa7696cf0bbd3bc39fe1f3a1460b01faf6922a18cb54951122495d75fc2e701c88d56dd4607bd5ebd79b66c382feaf990994898fdcaab91621062189dc7f84179abb33d7f7683886215bb3b68fb9460d4c2495345f7249189a3fee0386051111431d33a88647a3241b388a16dc35cc5733f3c4199cb6e490fe9c9119680e332400cfab9b733e8566d63622182deb3d8c4d0a0c58f188aaa843ae2012a9e2811a468169c4d2fe734baa4f90ce33489078a6269cb12ee4ccbaf9a32f0a8ac0899bc2383e70c0b1493b1d37f9a22cb5518ada9daf799dba0906b053cfe8925050e1fd9ac5a0140cc4cbfa2eb8ceb6cbf40001a603f8da0b172eb24aa5d7c6486134982350d1fd9f07949908c8807b01b60b4011a570d9bf09181daa5e279b717f021c8fba206ba3c361500eeb1ded9f57b3ea11169f76458f92f486613880f5a26fe84c04ba54841d02aca921408640ff000fc2548492a66ea6579ab06f91387aac7f2aebb135a4859477ac7fa30df5435de3a7dac71da5f5c2c443f1d682b8670ee0ea70b80f2c7663b3e5b748bbcc898e7715a9b1e3eb734da4d8500a35e0d2152dc6a7eaaa9d69742fae122dd4b7f9688d4a990dc2bc14827431a6b1856c876380a39a7d1c61f95fd1b61b75b86aa64db9cb058eb50ed1185e62265b414816043e17226721164510b4dbb30cbec7f9e2f625ee0e108f94a60c7de881caa2e508617918f1841e09adf7e452cd89406921c09f10e5861a2d3c1a9118f695276e370d1f6508e88bab5633266b5bab85215fb6a352f2702e0b1869bc732e05ec706cc702383a8e0501d58626bea93eb9e66486f66dce60c06bdaff2718e427f27aa1ce10c066a0fb5e30eeb24c51ee4f687fdf9264b77a211f3834d7facbeed0f238211ef8ad5ca68da6636e811f105d2df1a423d06256a99f3845d760ff4a6d326b79c6baeba489615039e1e42f0bcf18fd09e379e8f6d78b3880888a9377559b5b3e5d06c102dd1a72359b20927be899376e8330aefd93485ec0025384566744106ca088186b398c8e086cfecff9fb43e54e52b1423cdd9d434dd73b31523bb123c17780373d48ab2333697944a06de6cb3141927ba3c42feeb72ff1de2be7954f6504c9b67adb9ce773d7e6baad0be14ba6c6c9954660bc3bd16feca9d734c765ab2023ddb67fc6648f495116353ce0bfa5663ae708d831518a7ccfddf67549a1314bc0d39412b89226ae7fd632778b249d293d89c42dc61d0995db5e64af701a9f88854b4c7ff7d20e82c6988efc595d8dbe368bb510d34e00f62edd724a66572941486985edc010d6cfe9fa845c5de2372bac9b1520260b2b02059f4df5bde705b99a83fedef25b73935e9b3fc8f6fe55f6fad5a304b16717088c5c751e1c9ac801d21ef5fa5484423e1bf424e44945ee82bca24a22543e13e6a137b80fbea8cc187b9b78f8af305e6612682a8ca5add0290a786e0abc33881acec66435f3a5eb50c118f61da96ea3e40fef88ad6f3af7be8a491b228458c69850011f322ea1d4c21e2a71c1781f93369c645de493d97f933371f38886736af5b830ed8077447c3b471148bb241c1e689fa6bad3497475bc7764ab2848c73d9310084e3788d5ef37c565052550c7b3c87c73938b1600c1cefb84c6e46ad50cea6fe0cad6b6ccd0c2ab53e848c21ee311bb45e320ec5c6246332d54de9f6b6540c3bbeba894bab1ad1f7da68da3ab3c8c421fff8e1801e066c0333586aa07597c9549f98d1316e89d99e6b711da51c096912d86466982c5ffcf192d855638e7de790b346bbd1766eab3ee9bcef0b27bab4fc00bf358b048387d161c4065cd1d11777a582ce6414dc5de5feb1c758a0b4743df69a51f28924385c64047bffcedf9b063ee423d9e8a8fb389937400a97a54eb708b956d6e0bd33e283fd15c2d46c008f7c3fdb1e2f2e130389d47a7d3780460fccf2d30e0dafec74401cf0a9d192b812fade4ab4eb31e6b2c9b86f64c131b02a3c8006624baf7b34204e71295227acdb12a6ee876a3812de6ad6b1d5a4d10e40f4c71c71e6d8482f49d7e1b084e405c612fa1459bbc30f9ea1e406d6d0fdced59103b63b3f5f216d81a5d8932a4809afaf380236a599caeacc01b8eaf9e913db3709b67513795df0963672ac65cbdd9be18b9c92afc913ebdff58372f4f5d41418f731496cfdb0720756a811ac81eacfd38406afa0df633bfc58033092d0c8001578b798093bad91c9022c29698fc17a9c2a47d78d03260e2592b0026b418ca0e8ddee57b93ee55319b8447446b3c5413d16a89d80637e83d651af25df5c7287dd50dd45c05b088b59c929664dd40a11964cd3c511b9a851addc97463328cca31464124108c5988f9b70044a215e6462d8192da84f9d9d298a63a5c21b7313a8feea100c438afaea79e3f7e955c97f99877e44581dd5c73f38d6b58008f8b99eebf75e72e3d6ffab3373cf10a60a1fa01bfb95398a876b33cf3ca264f3e85b6ef4d2ea562f2e0028ffade636dc50b13953f54fb7a7349735fd987464d187d8803f1dd973ea8455c96dd629daccba5c7dc204069cbe227ed7834808b45de3075e7ca1d50840a23df32551a0ba1250455b7af6e90281748e9d21747606110e71a121a235c359d7270834aad631698202c797115b3adc61b126bf835a6292674069ab12a19d1eab01ef2603895bd302d6aab60767439a298e9bc61f32b1289d89a943e793beb47c4a25fc6889eca9526ca125668170269336817c4489c0d57f484c92a879d8e495b9aa99c290c3b6e5f51d79f251ade66a34e73911763e5a1b09aeaddae99f626fd802ae8f4bf280216a67fe8ae68e28d016121b61182396145a23b44ba0406357c68480660a4307f7a417bf4bbfcba025de863731a7ec01979dc5de4e22c012c3c15e58e8e060316d5db194bcc4f9c50dd254af1dd4e7a796da319725067b8f5c07305e3c468295f4476e9f4d9ca2db4d6c2611ae4b5da1391d6a4ed2dad96eb368d95d3246ffdf4fc82edcdca576fb6b461a7975fcf1983a5e7d1b642dfb4dc6ed6750e2a50f3d2295b6d3a80e9fc7617c2e5117d522b1dc2864485564588124653188909458b5efa884e877c28e6956a20efb773097383e74fa17cbfa09ffa46afc94f93fd224b393a7fa7da98a653c4f00dec7b7ad2f62376e6b1153d7beacaa25ee20eff3979555a33a337e20e2a0bdeed0508744fea85d52644a0caa104a90a1cb9c64b477abf7a495de037c78340105571f42cf420a06dcfd520abd7d3c4d66313805fb3542b7cbe5e635c43cccb47cc2c66a6f6bd92a45c2cf3ae03ef79b0c5472ae6e2ee62caec759de577a6d5b8234c3af46ffb93d433dbeefc413587cd09ac5bc810d29efdde83c030949ecaf4fd17f5a3c7899937734e1003255f6f56eea1f1ab8d645f868170fe05dd29f67e7e9f6795b3cfb1a678e95c1c12d92e74788454645a55714a7fe235c1c1d43e3014eff563a4560d14aef19a433f96a9bfb9c04e5135c0a4fb8611b1db98f47d263aedc3682cc34e9121170450b04ab755813303f7d1bb9d819f0306de80a6e40556f2e1f647ac39b58c0240ab11f470817b057cb55a544df396e6e1da39503f025ca043016b96063ba12379c1898a0fc4dd7648357b6577f592a594adf4658d6cd363199d2ba160ff456c75d12721905b98a504f2839721c5388b5245accfe534cf1f66b4a8295ae2fcf434ec79e013460a86cdfa7c68933c85cac36bdbf2f7fd9a81a39943034892b0098d603250961b21e294a4dbab0cc94314250765a6856fdb070ca28e938316b67e7cce63f4e1f24df2ae18f990eb534fcb1ca9d807eb061dac62a276389dad87b1116b1959adb00b8297b25b22b9268a0d534e2667bf9bffd1e861d4f46b68efbc325a3e3e3bad4ad713d86bf570c4e42fcd1aaa0c85e012150485150b4bd627c5b8ca746122f7413b779dc2d62cd73dadd6d7e28132f91fb9a4db2c93a53b06f18657f7fb921995badf567e147c9a73099c5373249252ef4277e66edf43ffeb5a0d58fd7543d84eff636e5c829ef423c2b3527a35230a286812a92124567e7c2a9df870e0fbae11e7ef2fdc174eb9db8963131aceaa7dae357e4d68c4171a307f185faed9bb02f6ee8fdfca5c7d87726428e89c9473e32f301d7f2c9ef39bfcac7c7d227069c1bd063e9fec6c26ede5ae56917e7626cb39d5210c5ea719510df3cb4b576dca3e4595bf19f25de889dd400a42d9592d16ee6a521c21b7f8bca5a5cf71de6eab7ae4d635254f22961c3ad99cdeec956a2af1a305e8883f2ff63ced7b9fb43088f859e17f83129269575a4c41a2e21168233f0b5501c23c4b0384c1c5e472f5cb77ce62bf7f9cac6fda53c390be5825c82647e496c341b0c06a020495c0c5cad61563fa699c761362e7e4e131c8a5e49f9f76128f342f6076f73a5ebeb5cf4a3b5c198ad4dc21ee764d282c3491c4fdc60c5664a8810b85ff79ac8c2350ffc9a17d59369a3875664386ff38c27f98cc11fa28e16cc75c71b23fac0f8b4040b229e16672926d04f8fae079c8b0280110ac1f1b7cd6f8184741b1927a93438c4586515abbfa1dcea14757ff2b7b61115f62a2502ceef8331fb8e91d6f207c5633bf83c9a74d0b2711293f2bc8dbc72a6fa547a29ef69e4a62053219192528ce2607828f38270b2648ec1cbac345aad48cf1ba576b6d72f5cd955fbd3c19c218f6ff987c12d99753d97b31104ad4e1758ecf4ce6467b770e8f1e5183e3b0aefc77e35f08e0f14cbfb58453b3d94ba1bdb3b0213fec5222b3f08cfa662a1f5b987c75005d2d05866d3bef1407a880a004a8bba13e698366f32811aa8f2ef696fd4f28dbe06053fc42d446a9cf3c8bb3b5883e742f419e8f6b30760cc98a7e1841f1221a17d7d4dacf560760f6cc8b993ff611b4ee9586b46464af04fd17781688a57502f964e222e3529a637e1226c264f943e1cc699f0efdaaee8e4b16bfb39847fe242b464a40d1d65756cf8b9058b0fa1fc1a1f6f53339f1122e8fe3ecb384350732351a3a1a14d56b0ad2959cd7674c8358e60a75b51d520899a6da412a24cfb5c4a56921708485facae9231f0a5d8614252886d80982a926bf32238f3d51d4b9f003d623dd605ddf08f3f883526d7768e6597a5fceb14bf28c4831b1cb1d2d8c82c327fed9d0b6514e4227cf95b2bf31a3c69c7af7208a522ed16b343bf1767482c52b57b84d96583d5816d69f7ad63932880561df413f357b37f687a32278362b38ff9d4b9d676c873404ad7a967c98805495b7809a73f177fa585c968b4344b6992ea8f8439e7cd3e226bf78a59ba179bae58c952584a674838cab3544efa6cd52a102e68f41e66b6a7f965c5912e546488888a0767220b114993401db38878f3944ba4b8275696f5e10377c54931601b21016bf5cf8b07321dea3c97d78636a7bfc6f378122fa015849092b68bcb655940dfe87a508fb65216210849181911ceb28c6824ebf70c1e0131a2fb85a4bfcff2d7e77bf95ec441922814874de36e717980c3a6047e382e174450a4327878a934a4bd7974bd1128dcb71aa7ec11e0986495c660b535b877e3125612191c0ac1629fbfc79d033197bd40e136764b792b1b02fd871a13f561b40455236e65c8184a46fb44486df70dd1bdf91ec2f39f5c29baf000bc49fdb51a53f0d39f915ff25d4aa953782ff129bbe081020c5e612e5264206ab2044e23fbf6df546318da6d7f3e010a5e814a4c74bdbec8ec6b2a70aa68dad5cbd205aff91c7cabf43536305a1bd68491ebfba7564cbba07582fb5b0976779a244cb38747f56f482a982c8589306554e69222e862d084ad62d3ac1252bb9b893a48d87965044735f2397fdcfa3d655c6889abcb65a8b5ab18c4895b6da5e9b7bee8aec36c606a6641621bf3ae91063296ff4e6dc2b1dddeafcb84c7f913ba1aa15e30120c8f5e0ad83e7bff98975e4f08f0942902f41de5e10040d5a3bef8e9c5c0cf7f7f62522e2a537829471698417519d6829e8908b2cab837c982677adc9248ae5c9833f0dd6d6f6664888b3f6777a379f9c15ce964acc0c05bb7f331c18477991a5303c9ce0ed6b006a1d185df1fefa78075713ac7bd6b2c60cc613cb789f612f206a684c7365b5cacc7d3b16a775a53fc7098e6cd8a889f66e89e1112fbebcabfd4b8e0e66f4786be126f41aa5d978f03a7706306819682408c90dfc421d6f55ea7fb7554d81ec3882fa8a0a0660f51ec391f47247dd3b84cce819ba27833fbc637c93bd4cd3e567e8cac0d22c10f4ff1151b173460c61bbad0bb9f5ae3d847302200c047b6eb4d39bf3629a0f4aecb2d173fe3a6f11d2b3dd45a715bc84a435d92d5cc0b34df9699274dda1f0f50469247595b3ab069d0be3245d64bb22faa30c90a50081565aed5f32867534b456c758269114d39528bbbf6a31ca9d5e1fc79f913ff081d2a2bcdbc55b1ac573f6216ae8359f9f2a625ed35d1af24fe8536346190153b4671025a4595629b2060b77714df023a43fd28db88f4727356baa0b4ca301ad1cb1166a509628dd4b01182b3223d059e15abd1836810a8c079f0822932a234b1e40947cbe0bce8520d91a7445b64a57e4a635e7682b5818a786728c88be3cb1f5c368d3ba8f131aa400c28ddf1f4fe2f2d1766484be953c0258aff46e0320ae351f417c19729f8035a84085c42e11fe82320f0128ae352fc03c12e08b8e4fe45742fe6fc08cf121406542fa3437818319e5eb51f529225acdcb5a7071c37377378689bcd01f938a1192bb8c6d9dbbeceb1c752f7a5ba7ae9a0ecaa0a912185ce4130a90ecbe26e660ba7b0b67c8227ce5c886b76fc862172c691384a085728a225dbd107b7f80fc5af913afce3b1f41634b90729ee7c946d7101df3dc26ad987b1b574429b91f0028e94e4eb41d294a9d241216ffe806e5c604ac95832862f00fed8fc1d232cb382f44228b1e2a8a265095c223d628d8914d5b4a71b6564036bc71bff852686c61a995f28922502fae7a93466358f3bdbb9f54b37971a804c43840b12bef1304bbf9c853d4bb99503b4db5a4db8d4adde56858ba545c4930549d07ef296a47213378d01f4822cb1e18a20cb2efce544903f77ea6aa71613b4062f1b15cb8bf85552e33f109e840fa82454962af42add5fcfb7bc76e98b6b41ece4f8931fbd859e6f11b7685a3f684e82b4896e42517e0d0f340947d7f7553740378e79f6f5942364135f2c40f0d0a8cc0e160593130092bd16e30111ba418d00f31668855b0d9f7f4be84f20c61f4edb0d8c35255c5a0010b30400ba27e96bd0eb4848109eff46a6b89cbf3e7ed21b602711fbd936407f037617f0997426b94648ee684369257f10a2469990804cab66934ae28903900bdea986a34a92e3acf1ec08b4482f0ca51549fa8d19f7bfd0b870947a692fe6488f2001fb3be37f27efbb2bacb8397b0110954b1665b57f7dc4444895bd008baa0536ee5f4ac00e72b56eefa90188be86fea7294a4ba6b09769faff69e00188ffbd6c8d4d5c5d716422fbb7114fffc865426ee49522252f00931afcf1be8ffcfe8f65a864b34b5b07ed51545a26aa8693fc88a9c6aa368230e23f159a85df6332274b88ee07ef76e5481a8586597ec1d1d97f2ba9aecefb119dd0a6eae87e579d5c3426159757e753889eb2d17ea334e8742da8cbd9548b7d0b12829bd1292c25f022de31f5f5cb9c2dec96897ddd6bc4b90e39d33b43aac5b8e366553548e1b4ef1fb020970a6840771530f3a6ca5ce304ad2c272fa86cc3d8f5df2ad70c8fecd6c7e4c2199010f1f70ec204f889cafda721f05a17196c7135a2479002759bb841d62a9e77ad390825dadf97b204ae725bb556334942629279f9fbb4afd170208754bc5babc1096716c95aba3429f6a2364357066b95d58a9311eed15bfff5ad77f60d39a821c2a85ff1932a1428edef0117bfd3aa824a8ba697da58cd35ee6884818b661ea91819118c7f77dc84da57540c1fdb5857ac3a34a2f42c8e5a5e72a76939854e90c18655f170a705681bf46d9e423f1b4032676838f5a1a141059253235983b4a779dff2522e787b4c86ee9f131f09de0a48d13af15364f7bb36ba3b4204fab080a038a8249d928c50c20221a4a59e03b5e9187e141111740c29d6981c06bd07020d0c779eaad808c6807bccf2d69049a056b723124cc624e9ad95ebc1a8510d36faedcb0c591bb50a4ddd407a4bd92dabc10124437cc7453b5fe2e4f4b4658c71c122b3c4242d9044b25488a77a711b6922b533e05e3814a76d7322bbe39e909e82a0060627880ee97fcc2001460bd82a905bfed606086c2068f1da79126e5079d0979f256c8e371192cbaa89618b85a1dbb264cda258e2daf2607c0a1675397585b9c04134b0eb1b5b2ec325332a112863e904a8db6080c13755624984885392fc9485b081d5b110607351d43be063175ec05818028c18fa32a788beba3dcbcfa765375952e85f7d03a77c127a7189f7fcba66302591cc09e2615ada7904bccc74838427f0452bc11b1319fe5418885c6a9ecb3efc9db4acb2479a680d77bf53ab6be6d4ad48aa916e7b8e47ddd0d21e552c375697a17199638e6842146532d4a561352b4d239380fc5ba8dc86ae603ae4085520ab461d7a14b5b941b1c568b80036a9d7548a824d0f3ca40f81a28c8a3f87e927202485b047c1e2c65a76a53fdd0cf7155e08d5c07cba4f7b18c1924ce1903b4e80ffd4b75f30ec2bb16099bdd095cd0084dec6a918120e4dea26e45348061ef76b2def870e710f35ac3610abd73ddfc5a6b288f95c62e8c1be6d0ac1a90a6731a77c221f675d381bd1265c3856a6637a45b1052f89e350f0f4a57188b94aca4c2a39b40e2bd8014ac338cc1e00ebdab05e7b968db62f85ed2fbc33b7543b92cf944e7acac6781bae49fd19e7ac0315770715481735c7b0ab2a8cfa2d5a7c110988ee3a68e4b0019fda316462b367e315008a9a6592b883dfd0d1910ba64e610b44ff27c80e390466a07d2de6abbc18771b5a7cc7840183368eea050f819c661c46358f151914e8843549f7cbcb32b61970b509868ca9b69c9126313b52603edfc66d28537a02d3949a544ba63636be49563eb55d059c9de0a88a45aece58c7b5eabd77db2a353f12ee615c4901c6a2bef0ab51bc70b6b35e8053bfead2024a0c7749a8163c89dcc3a538ade2371d674dda4469ce205d875f3d132923f253d02620915413b0320043e8680823c723ac89bd5c51f952697d1fa4592343450070de10685f1227a9482b649a1f4c5973112c19eda3cbb104422f78c9d902314fc7e43218f86f8f4cc594ce888e806f43e3416a676390aecdc801944b43fac853c00dc5f363fc03ae0c3f16a40bb8f249e9958732236b94eebbafc0171216799a7db432611fc1b988a2f63fbcdf6f16ea480a28624f47d1143428d8d9500403fe50025330b10271c619b6cc365d47ccdab97e051efac3c5a7d8962fd62c575240b477b49afe575b2b1f49654340fa442f5bcfac0b94aadd9915cc4d9a485795661707fe2a9287754ba59ef328363b84403e8e08d58bdf24551972a45fb774db83213563ff4990245d125bb62cf6ae42e78b6fcf2e92120ec4b7a7154e633e74758dae37020da7375949615c6bc15184f26aa3cfc4206a5940136421b23df386833c8fe2ed8108e304ab7bf4c91c69b88c9aff341f2dd16949b149c77f71e68290ee90849f4a6808c14eb9aefeea8582c942cef3b5cc7f450e5e03d3db01c5c6fe7a0a9d95a4cd4cda26b0c542f904b5c025aa210c5e14f3c05c3f49344b6168e774a768dcfbdf14865380ef6e00422cd48bc9eff701a2a4f32b8ab671492491924b30b9266abb15d2e5138194f8e8773d1229973467a6d1a3c44bf3ab3b3e72917b3b29cee93f0645f811e4a038040ae5322cad15061bb979d70d95ac69a1e2c3eb6671670dac875f1bc5e490980b44e40052d6358839e08a229efcf0a15d3ee36c0eb51469a3488e90f47c9d1b8c257082b9bde7df37af533e371f89b43a4a5034a16e64149af8ebd52682c54078648a5f2878fdce3310e18d3e205e893ab0e78a5409bd1d619e9bdb550a84075722613954ad4d0cc7eae4d4ea8042ea76edd0d8aa7a3ad1bee8f390c292a9b470c2372c827c08942160314fb18f2abfcfc931de14c9431de1ced81f8a9c15ae1c08520a381ec34a966bd0ce68ba273d130992725486b1ba64c109b70235de50af0e75ba1154d0b916b5c9dbdd5dae19cd56966b4d6aa0c2b24930619742c7a5d3d81b9606157df5968ae8ba2594619270317347764b87924955da98411346674726a8bbfd93deb65994343e260b8b50cbb062a4714422da50a34dd2da2106b8d059853fba26cba7c2a6ee23985cd14ec6683e16b7f37324e5f290c80564ea7d117db318f3551c90ae14e13412e6e3db02f82a5b8aded061bc1a93e7a2e6258b911852f35f69e9c7bee8de409946a11976b7b972a4c08d425c48fa522e1582113130600ab03b9d0039e4aeac71283b9145502ad59c06d6fe00c197fe6dcdd50e4fc803752cd12351a4bc6005adcf8db3d8b38e910c6dfc9eae24138331ceda2d44bfa81e53f905bed90b10fc37a72c097085686af13d146118991408d8d4735aead758340e97542c8183e39b7fb9f7d8def70f98622b61b28b0edc0a00c758d625c97ef3fb8bb6ad471d8b8ed6af045d00d5d6c4a891888412e984b47fbc35c4f95b05f8bd20d57257ebe0c2c81b15d0d81074116181eeb1e4b0297fb3df07978f2cfe77bac02fcad002d470725e4148ce3d99c71ae29e2c6b9c4df99510022142a275bec4451c3c701d9a77ef9dea4fc9fe80b084442279cee246b320aef439bad59fb0c1a56a1925059a9613dead9cebb079182328c687477e4fff81d31f395f67ac2d908cbee0c8865957d92b6fb99d6535ee8a08e62d870921cafe304f3397dd2e57afe19a9f66d1b63bf35845299bc12364e3a7cdf8345bc259a4f669b9b7e8034f3f5a6e70bc899ad803d35731c7f369cb8a1c2dd0680e510b3264b6c2d3d766b9ea2f2f6a3964b0dd1a60411f7f6812e735444eaea8d1c5abd46aa4fe52e339796c4020e85f91657ec5563c98b8315340242986e7748ca4f966409fde6dc4a01f2b2c74c95abc0f16e5796bb80e126162107e147c92c0058a886055b58f916ae90fa1a058944d5f4ec3c4f8baf0efa3bf5f1c093eb828368215918ad83a911222257f1bd5ee935bf643273d2d43272db2e3bf8c7e939a7603a67cee368f5f6e975b5ca71a40a4188daf2a9ca4dc203040b3d435245fe53159954e3723c519014ef15cd0bace0804c8c9c910faed3bc58b059a270e6f52873ebd3b6fbd4b791e28e13eaa19c4b46bbced34d56dc759834c54d647fac526c48b798a7c1e2003cd2480fdd2919caa62e852032c2baa6ef1e2c68c1b10365f37c41f500d3621af8ff61b1975c18ffe647b9d2e30247de90e285efc8331e73ae519de25b63fd40fb062dd7886ca5bbc47ffc6e4fbe5bc24ad04cee9e72fb8a0d5112e261b94cf3aee98f0ad9f0451d53b0b107367ab4b8068137a7958b30a6f8391f25e32edc8440ba6788e7206c623e4796fe04b64bd5dbbd2a59f3412517592b433034a8509e3c42248208fc63a75fddc3c3b3d58f1732d3732d790355889601501a069193f68c25e1c610a843e2116125a5f129178a89df4564dc7adc81c627ea290d5e6bf294058036123733db35d308fa16f7916e2f3d170a6c71377e49313dcc4813c9de988731be92f649f5bb35dd11e8f2e477525c4dbfb5c20d907c3b50cc790addbeb2f0948f9f60a356a22f7fe384045b869fc918cef88a8d57b8a97bb25122112809453265dac8c853f1f901dc98342c20246d4a576d95504c1f81da525cf25c5c6ef670390168579360cc75f92eb17028ab5374abcb65c3f96686f0649f5c0ef1c5d75ee01bdc8568cffdc77d77049ca85ec52cc1ad8c1ccb5aeeb12a7c2ec8a4d078cc6cc9ae1b395691b832bdcfb9f95d42d325de0873319412ae426deb8116013ce42919d95d4482b43489ec69f571a1c756c293c501b15057da69abebc42832b0b18ffd88956c83cb65bf8f60c630d789ff761312877ae9a1555c7f05741cef580fa1a17c24b73d702740b9230227d156cd813815c73a777cf790f5381054d45d58e68ecdeb3d9a12c74299602ac8e58c25c764c6989f74a838a84243d4da9a719977079bbb10d5c5b93b6448c1fe0b85f5e36476a060142c340f6b15115b469189e0084ac075436ca663b35b419723f63719cd4a87a119e0725faf78e61c1c518170a82d6ec4ec72db2aea146116a183fde20eab21f200f6188997e77e5c24b221fe0000be70f44790f70e94030268fcec5d91ecef4d1aa98914f7394df33d5336fa1f4ee7f68b2038e1a0b21980bd52a24f99a379ccc7ee8e42f2245b98789444405f0e8b9f1086966ce0e69b1565ff8a0ec990ad71e6417b02e8dc2f71eca999dfd830d9488dc08d8a39906337442e3ad01b6ff27f005c268d1b99e129420158ef27d6039e9897ca65cc9095e019f45e244e0ee3ce6e3b5a872be8be0a8c8599b5505486be967280acdaa66a85a904a06d71eab8dc5db9ef22f589004f871278b48bc6ca869e170d3c64c9c9246645c150dd2a2ee582a4b68b4c6f58c5cb0a13fedb49dcc7ea98bcda6fc993ad0f68458a561caa4f3e28b504123bf79d491c8ee240ee3707e4407d5403169a89419a8ac213848efba02b97be6ad501c73e2e1f505802935265bb5fec0511aaa4d0a09b5dc39ecf4ea3dcd5b452403771f66497bc6ebe41b42c2302e8b92a5fc14aa202f663f81b0ccd15493ca45877816d40f6bb709a988df0a1a1ef02b868920908946d1466aaa2454c4dbd064be9ca5a0da9a685232b5e67c7495c0316f696280609a3f0ef85ca1be3cef4bc837e7b0cb99376798f275eb2788f9f0378d5bf878c089400de60aba9b91801ff842ece22d2d5f02cecd883b68a08543aec4e7be1747817488a3d54b5842f9839239abc6593914f80f8a345be66acc65f34a01d40ad20148396eeea4fa1080745db7c52c43f4d2447cdaf8ad87849953de7f8e0b39d3cb7529ae22d81b0cb54254358a01d30f402d8b81e90bdfa27d1dfecfd475145c6071d13fa6110e47d5e0311e8a7a58cc228c01a2c6f47099e56b609bcc6eac542d95dd66d95fddf24c75e24a053b92ee4df897e1e528cabf10f3a273327b12eeb6d5d223430765f842861d4fc28839075961c632a913a27586cc1be7e36cc64677138caf9fcf185b43c600ca180633584e1c86a3e4d0b9873e2c672c098d4be68f5360271980956d334a4eb295a41ce3b94cef07ce9da4209e2caddc6270bdfcdd78018ab692be5d3d017415e66fc3e5ae0c046c25f1b3346052a484aae89f4e5b2be934f64061dea3364ae806840934cc342cf51da854d9c627c363b5b97be018c887cbe194a6a5236952a79c56e74feec6ccea157acae694cd3fcf288696a0b19e6867c07e61bf06dedba39bc867dacc15dd04ecb404548ed166aa347be6d0252cf2dbfb66ee0241f37fb5287e7f366b380d1c28b89c09755b682a89d80a5264d7878e7686febf3accfda31c75e05c97ef46b4317aee4e57181e77b799c966072091fdf5d476aeb1f2cf02a013a50a6eed347396843bb53fa27e06b418a9e3771e827679edb12026d6506793cbf5df5d80ff2bd9e7ce5ec0d3de1e0a7c8be1ccf9bf98f50a91b93ca32c99fa8b847893e48b04417d0d647b03077b06e93764fdedc397f1d021df4f9e8ac93f63075c1d388a5958798627034e025b441e35443903d133f2a8b28c6ebaf14454a6157d2caad4b9bda874326a6e74e8990f7a1c72ba937c812f40ce51b0bd45dd7050fc3fff5e4b91783c7f9c0a7bef7e397b644b00aca9145b6468e4051ec9c3c547dfa1bcb4422f0a5d4c465de61504e8f616b04742052cf2f56d7b01e9917ad3e34c32d80ebe7f1e50e7b13d7d2ce98edcf24f9a7be1cf083238fb00f4a4c37b3e5f17ad18464b7e6a843044f3ffb6128e7c0752c08204c51c1ef8f9d5d2ee10b33b0703f25656fbafd1b5200f14a51bb68003028e4961ec2a8ec336baf68f5eceae67aaeaa2348aab71002700394fb851348ea1bfe91b6dfa2131f28fae5e9705ef72fcba37773f21811e6ccd8be4f9f8e270dd21f7bb2bdda55bceae2bdd9a43c8e71afc17ddc93757ba35208941c941822eee179609e8756f8623e572cfe85eff33ccde329748e9923220b5ecb788ded083911666d2e98b1f388cbbf3923ea97a5c024a7114896cfe5566897aff674e8468871e2e2afd08befc7b4a35b3e2b6c9a76068219aa99fad9107cee479aaec76414b3f913d3abeaaed70822e3adb65a8ab989bd0223c99126ab62e0f3ed79d79424fe94adfefce35996123547648860a89382d197b5e71518703aa4054d47612d5b29a26fc4348c37cc46d692015d223749c77e8214d7feb2225924ee9ecd08dfb6f6958d22b7b0978190ad10db05a5729e40fcce2db750b8d9e1ee7e4e8087b4be4bd9f3bb27ff86fdc3b761e330cfff8f294a871c37cd12d54414cefe5064066a0763e1550a482abdb38555b37ffba85d27f0040da4271631523e739adbc5be88c571e5f612d030ccde4fcdace948d3cb5a8e64876e0fd0b459eb3f982c1277dc1fc3d0d4d959675bd8be0516b9fa10f7c803d527bb1843086001d8827f81970230c907ef756dbd5c399241560bbd9b4b3e03673eb5fa4d961b4f2447c8b02e9a433ef7c825b5df3b25b66185b2bc77e6301fefc541fd74d05585deecf9fdc8315a2a544827ccf4b33a5adae099d44a8f41f86b42c766a88e157ca6d98c6c06971d359e654ea3052673bc60c8a2eddd7c2ca3822494a83347a0a2134fc31515551d74cea3e9d119d5a8244f44062e8a0ebd84841ad356374c60ea8953364776f27b2056d6ff1c18fd183f4fde04f713d48235e82c317e12320b654ca8c475fc2866f6800ee30fa657bfbf63092d689ffcdf3572316fd306a086e21814b48b879c0b9f8cbbf74ce0a705a07d62ddbb5da87c50495141de87bdb739a5f5db341ed026bb9a81c4cff2638224aa9d48b462c4cdb5798fef00c8f235a618ad4a36c665daca437f67ab5be47460824fcd0e23528ddb77f74df099691a1cb5d3e2e090efa4675c07543bf1c421ff063334308812d041d545e83b1f88a93a0163382f1aa27760bf0f06ac277d44b37b624e95717cb3e4074b477ea037cb853b6ec960dfb355bd449a05665c5724429795d70bed49e64cbd75a357d51da67e10ece86118a3ecb670fd4e646e6098da99e4a7847f246b1540c48379ac25b448737b986fc2b180f2e133d3be8c790263b84886875b00fa0492c711573750a3e67398537ab7dba9b770df07652f7cbaf3f56308cfa6b0317bc7551d85ff286fd2b7096dcdc0e03e10de17f5d1d85216c568b1710e34a29db4109b7210482ddec0b760960c467715d171e0fa9791e4ab8756885e2c309d967540d47807e089404df7402dd23afc1b4e6733bed9c7a73dc68f6bd9e9d13a589466d968a8af7d4a33c992486ff11d87c347e0346678a7f1a567d85e928d29a292068ce253e374802759c86546629ac55947b4f1e992c9afcaa551ca39d21284286de63b67ad8d7c5413fe1d226afa20a84126b7165a30f8fe88ba02842986fd0f2cca11d1453e0991e10323a0341b5caba6d958543dce09b91d3c1622b7940be90ad2e6e910ebe57ac7515f47ed5ad3539e3b5c580a9e66c538a7e81dc90d71a3c8ab3fe8f9b4d7ca038237b99522b03d12a6221b43537d38858b28ee14378cb7ae767dc97eaae1c32d4f9ee5fb811704a60e2e78a53e2761bb4910d1807d812463da90a87a273c2f27f70131648c605dd1c029d045cd3d061972d1a8060c389402c511bee1617a89941552b12693c8205bd0a0d3bc0fb3d650fc15730bcc25f563d162fb729483dee5198030ad1f5f66015c96b381414944ee0e678304840d36e75c89efdd9c09c4431011f640cc02cac61c457810312c3275891c88366259b0f9e63d2cca2314f30600d8d676f01a311a9070cfe334099e6ab4b11654931652e79c759d5eb5e0d97a26724884e591f74f865dc0f4984bac4190636144391ab161b283a7e05c6d1b7d37354b17d2b58112c978d416172c00286536602c8e021d4fb2eaf0156f0b489532d0456fa2a60d9ef0c27463733fbb612add0b3f1c7028d4666e26485b32b968534bc4f75513b1082cbcb53c38de5cbf0ed2d677d4d27d3628e5a9155261463a34cc96c26020548c3d1c8f1689a09704e56a93b90c33d2ff5c23076f455c12b0600cebdb3782c4ebd188cc6c25208307a60f9a5f066b402c4cd478590be85fec0c65d4beccff8bfb589bd447c8a01831ebbf3e0fed6c977b5c031edfa17c72512c6f319c0a2314b51c00588617ae043269198bf440b21b687558329eb4b27589b5d93f887aa0cc14d0251fac40ae8e55a68c0c0a61cc7232b0d46a47cf19a4764508f21306946241c5b15fa516140246840e5c6732144c28e59c7288560be925ea0e70e5488630006ed4738571b3104ea6005678dffdfeff1638eaedb0293e578c111e9d6cb07daa61047e2757351e12e83cc8744f697b2cb1bc3180c8d8cf40c7b30720f4e700b3ed404251d26cc43d18ef8eefeca22649dd01b60ae1d5ee2147568ba1a75594d0c00a42e729f0c9a3956c7d9c4541d91eba5e53c7c8e1413f59f350c4511c18321b1235f57f7ccced036f2cb8d8ffecb3688add92348c4492a1e71f8b0734457dc09e016fafc74310038079ad9edda607f0a3b7271e069633ee99960c310b4c855b23baa8ca204b5b27ff626586aa5a5404886407d31dfb3ea33a2f6ac3a36680f7a356b606b38fd01e0990c1b083abc65004bfc74718e86af92ee8a7a7a3711885d4094072e3526c352a32c14d4197f385e4e5f10e3f0d59ec6d176dc548d1b100e92f852c461fe529d4f73426c1f6bec768f3e68a64beb99412daf207df3e8b01c5bfe911b3af07079da5ca82b0298a27b9b28b07940b5e3dcbc25039d86428ac0c0f03987bc497ee5927793007de0b470ac0a37aa315f346d4dd1cfc4933804bd84d14df20f5cc71cda368a3847a151306b6917a81ac4e3f805e1d8e6c2d4406944e3e1cb40ca60bdb6792269db556821ce868ea99225b47ea8dc31ca3a561deecddf847ab52dce2cdc443e954b462ed7c653f13dc7802d9a54934570bf0aa3e2cb3f1e5c217b5a6c4a5c12d426150e62869e3664c4e4a0c443d86f5576bf61ce3a4b54fa3b48d51a6481214284a22b8485a7f14a9a195471061d446685991771af2a7fc250641ffb649cf056c0c2f6bec2f88847ad7a5db01264f7f5a54542fcaff9f4af66e6af9098754185baadf59dca0e892296c4a321246a94c70a4f3b029350a027dc44530ccbe7d043f8401a2f3f28fe0297a097db53ee8906a76c569113f81d151dac61fb678d2779592b8ea5aa04c85a9a40b747ca104d2220c185290a245efb1fd31eb9e3730896233434d4e6b1e82702086bc27ba764060c7d33c55060e6b666c722ad71bcd31de49d6836cf8bbca348b37cd2e10d226c79d5b46b3ab7fab3b7a4555efc1a1384aa099eda5d2e18ad7b33d9dd0d953ee8aa066f6f89e5df337e5ce8a21fb2aa67e5dbcc6e01c28b6f901ebadbc55583b34e9742ea1d0cb7c87510f210e1bd3825a24ca22b47fbf7dc706388df5b7868fcc942ec48f8cf55ecd6893422924c1903df21811bdaaebb77728d59aea9d7aaa48c89734c3dc3e31aa4315672c65a4b34c0f50c8d5f96420731b445479736992162a6f062bfceb9db6ab758062423c6f260c066dfd626cb111f0c968a2cfb0a90958dd0010ad13a5dcc8b02144bd18897ff794dc735601ddb11a2372c5ce1b5ac7f67353e0f9fd90ff7c2dfe539a68b9fa6dd6dcfd18a76ed5783996cf27686a9d43f61282b21ee0a1ede9cd85f00ae2e62bebeb4387ad0874caada7b99c2b823863499e16e920b05ea9c439b46933a551ba82252a6aec184162dc69a08ddc9d52d8d5eca0025bab8dd8975fa4a9c9d4a14df7419347c5bc6fdc470a055c618a344f6f2e8dbbc7283ff6a89915b5033992d95aea92c64881a30a5649567abeb6d4b13452d2b2d99f3ec768c4d713e8f072ee4d256cc335f5aba781f3209c400e86a0aa991c71e6c4c834989f302259ec93425022d5333db48752b7f645ad83d82d1206c69294d199ffbed406b695fd09534e03f4ae332db21ec4c32917affbb992af1a726a2254ada9b0b28d3668f77213d4c5f94860948dd61c37929f6948fd8c50e1e03445015198d85947b422a6cd18ec5d28aa2776c1997f09263321550c1247955314e2aa2c2f4c8539df1a302aa4c259f6ac64f052a994ebe6a8c4b25543129b9fd7d828049876c1ca02083b790599c2ac984fcabb98eaafe3a1b24429741dbcbbd805b5eab0ba0f3154b809ebddc0ab8e5b5aa00fa5eb104e8d1cbbd805b5e0b18d7620c3c727bc08daf1506e8462d19853af153259b1fdccea4831026e4bc7b492d2ee276492e6c4bd6285108ecd1dc248009c6a536c4e666c6ef9f1bc1c1b96105736cd348d252b759d77165437c617f83731a8b91d93c8d11d2e972b75ca78cbb8e0f5ae38ab8f00bede927dc9d9dd5ec41cf365d46f19039ed82772c306ebf0b13286d94ffd1741c76c8fffa25d6a9b13ef3007a63b88ffe6db33ea41f11747290b06ef1f26c0ca6e671f4eb13d0b6069a80da6a20f81ce55c29dd5748df388a109d7123c8405cd83a32088f8a03a7466a85c23eb8a02aeb00aaaf0028d424aa95134ee6dbb88ccb41280b94244d01bad1534a0c155a3eb5d5387d40c4d1884ab7058a88703314233c8636f905530bc512c205293fe10ac229c974654b37e5f737a972c0e1669993792e98c21771ca68c10f15f6f5a907216c42a1d1c3e0065719ba1cb777761c4496c8078853835e4006663d9e9f3a41675e918a7d300af214040913785ffdf63826eda62fc5c34f54ee2dd49295b35f30c55c32ef2378a63b839524bcf1197ddbc3129f9d5baf9505d0f78a2540ef5e75e34ea953b80d2513a07d8d934cc41c5545c21c94fa1d7ef1048fdbc5ce67cc3316894a4c30a2132a0913af25aa930986b41b174f868496fde3ad6c053a411fb0dfbe3a37b7efe0da41a8d8c223edce1496e7466a82af120baf4d533e30f2f52dce2d405c01ecc3250b1fb5a62d2a77c92713bbbc86043baab4e5b7d96c231f4e80284b83d9623778df946e3618cee7576f6b556e641d66e385eaae70a5b19277ec845226db0dadb560bafae16505c95ba93771642740553c0d0dd4bb3fbfad73f1b7bab74534ff86e5d0e05ea18945a58b3f5bf6d44f69f4db7c8889ada95e66e2c56ff51e03eca0dd793738bc89198eb5f9354eab190b53f489700e65e6b045a70d72249207fc92b1324db1797a6b242dffcd30f4df7b2ddb14b810a42c1875655d6625cce97d54b17ce43ebe825e3a92e43c6939418b01ddc5172c39ac0a02a69d3d72b643d141d566b95c470693780be0230d2dde8f8c1cfd4a28b739f2e88f032f719b178c80765ee23bd73a011ab0559e17397388b7213a9335e9f667a671297ff19a86b636035df7e022faf934d5b55a96e98a5b0337f1212a692472cde1ce1f8a444a0652e43c1d1f6795889c7058f6bad0719365cba847b9c0eee1679c930643f44603ab179966d22b7d71ee38d4f31df642e2904d0484339ddd704bf363d08d39f0f2ab5086a232986d8e0acec8fe2a58eafcee861c6a3f1d8f033979a75d3be3f93657a3caee627718c80050109e3430aeb6aece869b78418241ea42dc7d979c9d29505ed2c0b8484f630327b9fbb27dfb0560ba2456745f30668fc5df90018020ac523528f67e9cb0936673e54b50bbb5232909024c71d687253296e5d9ccbb20b7548a4b08a8041ed1e8b1ed15c1402960215eb062b2bdbce0dbc780be85c7340560adbde448d14859983dccdc4a3fa57400550d917ce26f3273a1983d05e972be7385514193fd7d681aea39bd9fcfd97f13762fe847c41e2f1b0a458545981812fd1a08a8471bc2076325ab15adc5607151a10a49a79dfbc589aead50ab5c8b36bb1004a8f35a72d343c1675a036ddff8cf37b34f51d800bb38ff55dcd8e2fc4533d85b05a0a51f25aa3572bd84da2a4f075eda043a2761d96485101577470129d66e4e3c1df916daa81a6d82ff9502445da7ef39a5c9f0ad61bd13a914855b913a6352cb8ae21f405e98857f207518d7a6c2bd4953f1908a9f47ba96102dc7996c3445753dd1ae90a4218ec36755632949771534bf3b5ca14ae64b30d5ae6f0a257f0bd595e734b8cfcbe8965e3d06c46f809472d14aa38ac2a79a2d75f30df1e6e808986f1b8df7834df969631e8e5925a363bb6f8fb61d19a7878748b4c724a018a73747b7cd09d3600d9551c1ec4e3b2602bf6932394114cd5cdaf7034fe5473c017e7603d5ff443968a00e001a4dfb706cf24093e299030eb4621a164946074ebaea15995da9d4336e92b42a2db1d82d12224a027eaa0a8c53efd753e2c78aea5bdad3bef03d555f1bd62fa25f4104510c94cf4ea5e197c6f2ba92846e0b8948df35f61ae86562c282bb76d195438e721fd44dc3f36ad7fa45089c23a71215b46b5f78019cfd3412b53546585abcd027e859365811979fc4be7bd911e321cc3d13ee4244f91bc154b05395d0caf2b41813fb5eacde9ff53bb2f84e96df3956982bff4b389f0f2b70162f7ef7eeeb63fd1eacdf87e577b0fe0e96c97b775a02738c8d6bb3a2f7959a7996af19260ed1ca3916e0338cfc8a393f4a9a251fd6cf5f2fc1f2a1bf8c0fa9d3daf9d1604edfa6c3d7527bb07fe2308e817103fa7ff2b439e2299d54fbf497b74565add06c6d7d0216b9c403257497b6820f7b317f2893c726521281309c151ff3b0645e36aa42091462212c18961280f59e58ab7749652dabd773e44b24dd6a6055c55714d8af53078e859c94ad6778f5aae222d8d01b97a2b777a9ff8ae165aac8066fb25f971a7029d6b276d6f6e8b1446559b7eed82da1ffc448a8bdf19281d6d63de2a7f888839402baa7961ebaa64953ab283dea5f6dd3b0f89029ec5a77e6ea9f55b1937075d0045b1729d5460606cd709a683e1fc1b80a64ecb07920202e8dee9370d1140e55cc1530d4392f0b2bb180e43a9d34b0e69c3aa08f1ccf05c7f192d849b84631ab1597bcee8120f8eaea248513d333023771096f2749e99d7f5ed1dbf7bc45a89a85c422b029578cc5e2e029c246aa2cb00fc3a7910f067c4b2168aad6be95fb725a0db2dd47127cef30e1e1c52bdd922078f516cfc24b12bc14bc062316717285588047d382f04479c5e23452aaa119240928b49bac19e202907170ee9c7cb1905e33a84cc27945dfb8090fda8b3876d52127ec396f783ec6f1472f7bf82efb7e5467b30ecb516d7ce1946eda4559888572cf2fcc4b6f229f8c61076ac60a455d13023b834b34935d56e38350ca9850c57fd578b5a4ced6e229a72a705dbb4cce5a650dcdb207a5bd18a0e6624e0b20de6f7a597d49dd40c7a421a9f151bd52b0f5fd6b52057f60df4c9ca6962a0a1b227b58909cc61083dd4d955778d1a7586a8192d3f192446789b820d2843cad5ba1c959e54a57fd58728c4fc0cc208c9a4dc8fca2f196bacf5eac0ddc1a273eead25b4716f351790dca9f837705ce57ead0c3a0920c31c8bbcf9592fd79d390ccfb166b6b10a3d6672b01fbcdf23189fa52e8a0c3275196c03e8d3e63eac28eb116824add7af3add894ca209c2245352a6ccfc4deb12fe398e95f88d17d7045689a4051cd131c8cc89df1e4081a08eec72f4879ad87c14fe55b546e992de2ff19b1183e14da5381304c5b0987aba0ddd605179dd2a607cae25bd90ab6120db43fb4e8d8e46162156d7bc6969ca4c90d85e09fd27baafd9554c46c421f1ae920f9cfc9f2a641487df717c219091f5ed1184a313ca461ca72c8794b8e83b1269b8488e7ce8ca28629fa95f03ed29ddfa6ddcf35f42a9035cca1ab676cf5f7720e41dda1101d58c3292a8f7cb33cf8a08b530d4d03ef14b2888654f48b9f4c3b272a152364077670f77078b89959d5abbfead3504be9bc9c54be28148105f834de6a9f69187862a4f2554d63543e44d3e7a723917790ea7e55b1ae8fc98b0acd272166a626f4fcad751742050029b3c4040c896a99c686e981b3be97f9faf08423ad1ebc74cc914d54d96551ff0dd35979825c4271a5f04919ab9bc7a448ace189d2db3f6ec6f70bb3bb1e8f97b4688d262daa2a21e6e8177cc1208716390c35ea28aba7427062ddc11899e92d62912c25ac43d89b3acedfe15742df27f6008d05cf26913a0e79fad755568171397c03059ce32cdcc8e7f98d5922e356132865b8cc55d686cd65631f62515bc277e224c3d2cfa392db83b498c1fa7b5ccce0af499db649f81f0ed34cd40c63e82d7155c3fee5c3756e9337e2a504c9bc730b6d04ad67e3a3a275209c241212b311475a832387143f370a64742e14156ee27c28ffcd322bd46c0d54c329badbce586109c2e827340dd9761a953aab5b40afdadf1577b0e1b2b50cc722df367ee08fc0f7696f8b34eb6c925b38e3d9d6bee52d1a14307d6761c0790c5ab4b8e0b67e9c920bbd157f358518bf386b4dfcf812aefc6c5085d43526d33b221c28a2eff85b1cf72d8843ffc3b2c460dcaafefb54f2bcd95206d9982ce99c4d4011b66c894cdeeeed591f10db689288093e51fae675e69e7873ada5e5396d1bd7e156fbaa622a4c604ff5c65745aa98609f6a8d57e55499c01e558d7b2a53cde4eb51d578aa4c05d3d853adb1c5b16cb52fcaecae07d6512c702549ab284c118c77451d043d795f144f0d3679df176d20f8fc9aee51f447cf25a36698158eefaa483ecbb8625cc9b4cc164f3d9a1eae6e41ec41c08585378cd364e64c1f419cb215714a8f464d64744d2a34e7fde0ea98dfe06e9f87effd5cdd3a9ec9f0e7c6fc774194e897b04b3f72d4a32c79a9db0580183c62add58f7d9f896edceeee2e3f5cb7d6affaecdb4ef0e3b502f59302180b71a35bfcdc682b7e1469addd5faaf12d7d7a1fe81a80206513fc3dad45f5b106bad4a7d8bd37c41b0b50a90c2ae8fc04b32990a4c5d32b1444a5256ac4590ad27c060b3789f14d0fd0963e05c302620d555c40514e9584cebb3f78b63c9e814dcae858fe1ac18f88a2d45168cf97b565e62794da6328e1efde6c89ed4bfb08ef39b40d03d8d4f371413542ecdd1f6f3c9ac8f988cf1bff8cf5135d2ebf89124beae5c691ec426bbd9edf9f0798df7d9359a02d068f90a1bd14ca16fc4346f51e2ca88b127e915f77f2dd5f2e5bcd4b70ae2db583ef7e2dd72261a86dd88ebdfb7864bb3582bf9bf6427dcfe158f6f3b561b69e9896a4549a353dff6c2b06c0df6a445d8ae7d8faf4af653c2df7f65c5f9a17d5181ed0b6d55b6a85b98cba18dd7a8464df4909c65cd2b1128c99855d1e10674d4709134fc521e08f2ea0c8c9073eeb9e6f1f54fbae948075fa4d6405082ea16d2ec103560bae773f640b53160218e2cbfebf02dc5b045fc4d24c291a5aa9c39b63ad86e0d4854108419f15cfc8aa00aa03e5a30915c205a21d182f0152b95670b5e00a5c049fd6b9eca769b0acdc4b7045583b72be3f8c25d60cf89e39f98777ef9ef9c6d802ca0498b96160460866140603653f17bff7e3ff2123704904ea0602d549137126be59bf6b608198ecbc493f1f31f32b13c4eb7ea5328e0c9019a01c6931ed99a3f0e8cf948885ff1c14d8bc0269ce74e8f13ac9a1dec06f0ce3a8d92f4b451c6ecc8e8987280f234418af63e657e1a9a52f6d39f1836e31739682579483285775ecbc0626ac3e49cd6cbede756ec920e0a9386fafceb5744d781dfbdaccae9cb87ef3bf8db00fab9dee72b2c8409ee91142d9465fb33e78d3cd857be7522941d7def8d1cf198edfa5b11620a01011615380454d99004031973054cf62741e26a3bf4d57227b35d6ed64f178118f0cc9707aa432b9d0375a5383b94627cc6b07effd227a9fe25ad97bd5dacf61c08ec0247c2007b237c472a2046016454b670b72fbd8eb0cd9e98c4b34c47987637386b706e86b57ce14b59d83885deca5c385517ed7572f216359633b8b63e595d9b7182efb8981ef10b092986c8e1da7cf9cfee7f154321b2e49501d2521e50ffce2eb211a62615a28e45101e5200542c8229de7c58b48564f512eb4151e97e986a29cf02a45dd65ea879ca9f19e3ca341321842db39c26cf0f570d42c060298070715e7a38aecfcf0cc9cafd4a1fa44ff3472230c24a6137a5ebe0ab63dc1ea2043604a3dd870d47b52c5891020091938257bc823ae25e9981d1e87e94081eae4e8478ef126f60500bab49738bd845ff315f39b5aa7f78f64882f2686838556a3ac7572d3fde760cf8c63cce725693f4c3144ee6028639b00e29c1e5710c5017f208628b536e07b22e3e185f6386eb2c38dee6b155dbd6327f1274a4f5100ac448f5fa1969255b40270ae546c92ce2e7070c6b7f76d804f4b9fed61b4b15416641694b4ec714d300914a65280c94e7c1454db072de5cf7ab534b0447e3c35d1368005ca1574a499ff4c6f2a339fb8cba645ef66f71cff89534aea3f5f9516974618f74bba81f79469571bc00924f3140f1e49d1286ec6745c330d8d5ec0411a5f896ce41941d37939a81a720c58b6b5b856474cc5e896d573b90c98e53b471b46b17dc8ceded1616738cc886b721a21b3e97baedb9a01de5bbfb65903675c93221ab0ddb27ba2ed6847738c549976f69274b37bfed4f53d8df921b72b0015a5e2d3e2401cbcb0257e87715c26def70e3dfc8f482d115d074384651dc50d8f5c344e1c83e6c901f7118a4276e829e2e9b25d9855527bd117192616318d55afc0c3fb25a628ad0150c96d99a383045c999cc9827623f1c674abd0f806b0c7979c53a4df2c93b5e4137380999ca8098d354abf9c11f456d782de4f138a193fe274234b5291e5600434813de91b143d5af1050ece1650c3dd924847a2f281b49772a1ffecc4a348089c1d66b47653fb08534d7c0aa01a9a8b922c69194a2a72ef0492cac0ffcc8aca9c4e1b428f85cc315a27ef99b39aa0ae92185ea898c319d748e462ac71cd9132d47bb0e7e7271b0949a6b731cc5834228ec0e6d77c00c2741e0aa2958e096a732fcb6075a5c4d6724a77cff5bb1a95ea887ff4ddd050041d56ea418969038edc8e924a93b12a9f685cef66057fa88fd68d98b543bbbbe218b36d794e3f035131891df0501bb641fd59dc757fd5b6e225b692937e90a28a2699420e7db7268e075b744c7f65fe636dbf00dc173d448bda8d2f70f007329141615c094ac3dfb2e0b6fb9c82563627644bc667bdf3e5ff707afba5bc2c984a9c123e28a8308b39cbbbd31a0189d2fa5afb1ebcc4cd8de8f1270c03909db2f8eb004009c7e219fbbf6173584c175d48f49e47aabde34c764b99aea7191023c3dda97f219488ea8c634b47c85d0a799b7609275ffa3bb860f683579319e9cd9e40aad03a84ce125c385858ee86acd413eca3b0bc466ce3c81afd2e0d2c423d4a7e25984a3e285ceef8a9c3f374ba51f5b16a18d86c0627cd449993c07b5c4271e8f8629077485dfc0b9b29476ad3020daf621d2e58e99ff321bf3160dce1141be326f090e38fe377c0456166677ad58a63f276ea1ae2f61c51c41031b06cfb4f941616a1aee9c8622cf8b2d5507619a5e297411d1435069e4e468815561038ec6a3a3f9f2ee772a735f9240351126683f9dc21a227293e5a3b5c1c07a0f3693c2abaf81348dc52726c915d7ce946d2ee96c6072f178e6f0af70d473f285bdedd40d47e4b012d36fcb440847bdfbe9b8890bd5355177515c614af2aa488c2892d491c918022b418a0894e1021600108201575668d326a2c88110615229e01e183b515995003dbf20316233422d842892d43478276e4b0a989c138f811010848799289530a4c4a4943447e763298ddc0691da0071c6ec8604c110a3cb180256478e2c448108f1cad0e8021c349d1094134b017036c88a201460c20081d5c40c0010ee00313a7221382dc786ab062f0996995a1617c645a2bf84ce01ae371f185e003018d989b305f073e2d3e293e06b09ef8a85a535e08de07b10e5c1ce0f4f8767c3abc1c330d5c197c334fd6baf1d9b86a3e9a58ec83c15c5fcb6379decb6bdc87cc4713ee65adee6bb329e4493cda7b7a343eae7c3c694b653e80c87cf490f52043d6838cd3a80dd31daa43e993565f4bb5a5db521376b9e68a1b69b574ef6a3dace81101590f07c87a288046a3b76b1fa7a33bf40422aa7302d1141004a3e06eaf233aee5292f5f0719dc19b7fef6a0ac95b7b8bf5b927dbf1821d5b5cbbf593946e7ca97e4cd21d9d1ff389f58e28761ce1e4fd5da9de6185bc2fdb01b503092da90dd31f6da969c6b65cb7a67fbb35d3803445feccfb75596dee0e24086c8cbb7fe0ee1a23039a4f0b8efcd0229af3399270e488d0112347828e143942e4c8902340477e8ef81c3922242464442848a8881011a1214240423f423e42478c0819316224c8481123448c0c310264e4c7888f9123414241468282828a0411091a120414f413e41374a48850112345828a142942a4c8902240457e8af8143942448888112241448a10214264081120223f447c881c192234c4c890a02145861019326408d0909f213e438e00090119010a022a02440468081010d00f900fd0911fa11f233f413f457e88fc0cf901faf9f9f1f939e223e463c427c8a7880f119f213e403e3f3e3e3e351febee3f5ca62306cf9dd7ca270c1d44dccb6a2bc56dabc3697b6d961c60b8ef9e8aea6de9676cfb1aad94b52487143a833a3f59dab32664e76c4a2ecb51baffc5549fd8567d36e9e3be347378a4fbc4b86d757497f77d6ad61cd5fda5955e7bfb1ac59b923fc5851a9e1a1d1d18280c353c352e50ca435da09452ea5e82bb931024870e1d5a030c773a24d3e00a0d1c20d320014e876ae8d0b67d49690d6de36e3653ba6d5fd21e37ee9a3b01c80927b8fb8e4e100da4641a1071a7dad27dd628d5f6719b0e69f29eb8466d98ee1353ab274710c1bd6688ee4ac9a71aedada69928431960e165adee339701039c0e915fa33bb27e2e6b53c63927e970ced8c8f6e458ab47848c0415213204e8c74796810bee0e823b67cb20892c0324afa59d4f06bf5f5ad59c8fed5aa7ea54420fe8034ee144f4ced5a440109f20355a44a7f844b19d3529dc1d0497cd48d71924710e8b14dc3f313e147ce517f40d31810057b8cb0c13b86719c095e33042e29e0d5adc83c8dc5964dc9df44a4002af8bbfd889462fb6db64b2a3b3db66ee456d41db5903d28413483c1c9cbf0addd5e893b6ca92146fbacddcef5dbd9a27767e82281111fdd1a1449496b0002696500206190ed9a96dbe2d69e170c100a8b85178a1130112cb8c03f418011050a0e08712442c2ec20cd961002276e812126179e9e14684901690cef8c0c352001540be40c26a82043d21b074b84252d2429524b4a8810396d9840e1541cd84327672b09aac405600358a20e2c600372c130a203b22052cf8e167c76bb128980fc49021f1c40acf1aac970e4cf42006049010e1a38ccf0513a85ab22489239064607c60d428c00b2238220924a22f3e086c114107a2a4a0c00103e8e22bc009b43ca00b213ef0190af3cd90650316c87001489417587c362c681605f880142b0554f12db9b206193bc6c8094f20c5f713812ea8889262c1145940f16d704606089013900501529af0d65040012c406404477610e108cf04255042000586d022a28008cf0a01900085941808a1c50101f09af852240a035f0630e5841d3c206c88620310510a80010b58f17450487d51c48e043c742f7852c6a0b172010a18a86ad89e9111b9040c4100317429945e102ebc30481f260f2d203d1c209a2c0f123f5600458a8f51c5940ed230a2d20101a47c0b005811ebc107133f02770a7029c10bedf0c5892d486ec40b46237ec254a14610f72b08601264841d04c84922e239311e103a8209165ce1783cca871880b270a105524c77212f4264e18206603a1ce0eec10c281cb13213001a2bee2c27ac5c8081266a3767c89009a300d9992c8840b2c619077420023ba09ca0c7f3947166881da4c082063f5dc1028c33314891821809c4cc870bbe3833b271c303c600805f71a38b334c4b8410c2105603f1813067c02fa4c8aa20a104135e589cc931040a27c80018176468a28a36d2e00017119afccf0f04a468c38b1c6265cc5081430715146d5421012433081fb614408b26da50c28c12a21657360c0138a20d1f90f0820208e892822e3e10d1c6f6220d5d08e64e0c1408401b2c0cc0a9861d7e886284263bb4818206a438432352019f282b6d8870434d74952fa4a0c30b6dd43ce0014e6c59e2cc5597cd8618649cc04c1025b8000c4628d9109300266c5070442ece20d98862044d9421c050538015296c6809820a01594c40040d0c408a8d1a304002a4203e3ec0812896b04165e54b182f3c9001144c4062e3a9014237354828d2801e88d8282206919a2c3b4868220544d8f8a1c5062d382004154294c1c3868760d7b001116818b2228435548065002dc0424b599870b0c6062c200b4050931a4e8ce4588301341065b09b02812c9edcac31800560c1f40312aba8dc99d61a2eb480881c1d0861e8b08135d6a806a0d2840f4e20a1810794b186d2075490a2f3d384000000630d1e1ec07ce0041619d620fa628dd9144e7e1023a402c14d176a9c7146c78035a690a20b2661d4e88209b38d1d5610200357b8bbb781851a1550810f2d5827f848f202c7bb91e3ee29f11e004fdcbdaa10dc3d2a37365e8d950e49d6dd8ecee7b016b50577746aa716c9a71a1f3782fbd62077cf8b4105aa444931256142129330458950c2267a4211ddd1b95f82b6b3a62bcd2ad7e7b57eca605bdc8716d17d6b0e2475062fbed54c0ad9dd7ed7bcb590f149a05dda5c5168885d88914519f7055d5c7561051a4da4d019e3d3106307164db497f18cd40d22a30fb0d0818ea0109cc8808dcb080da072c508600b1c5aa4704daa123c6520031d426ed8420408c8537ce0862859c0173cff9c58f0c1082c2486c8d0430a0e70e1a28a5617b10f6861061820ec2f84e0d021c406cb8b216840011479ce83098b0528a45106174908f1822bf402928aa90f3c6f85d1930ba3d4d08418338060c318b802eeba2201701b1758c0bd9f1e770fac5c390b0789bb1375878171f72f9fd3127707e2b2d7ef8a4b9bb8b3a490dc793fe3d1879e5044eb2e9f7c3aa4ab6f8c74bbb6b4354be93187b8db896fd2c64eb87dbbbf64b7cd5c92bbfb28ebb9c74afe1edfb44adaf6669184807e5e57a39b35c2586acca1bbf7688db992b6ac559769cc6115ee0eca5a562e255c59dc7774f25b9bddf749a63dfe548dc916779fe232978ebbe778c4dcc9cf656c2b6b8e223131217221ee2db8ac6535e61a041e017d7a488b7bb6be7874edee2bb8ac8553b3e5ceb856dd75b833b9b7662d16eb0377d720037799bbe370f71b7ee335ee4ef3d3a37fef3cd3ddc63d8acbbe2cae33f8241e9ff4018b803e3d4245744d88edc7c7a7c84fadd5808048233fb6a01e6c41fdbbe611dce5adededb3107ccb75048564bcc7c709d1f97513211fceb72d4d4d69bbd23270d947e45ed61d2efb6edc3d87cb3c337e427e7dadbb7b2ef3bac880bb6fe0328f8bbbdf2f2911fd1a25cb13e334dda1daea50ab27afa55f85739f45b3eab4bb03b97bd8dd7bb8ccb3e2ee3a83e3dde9d89f65ada2b49c9368f4567a31a5ede89835ebee3934b4cb533f148d925f96757477eafe91eecec3651ed519dc391bccfde519e7fcf4564afecde7febcefe7be3473966e5bf3a6b76bf7aeb98c6db46a71cf0877f7e13237dd1dc7658e0077d7e132cf81fcd2561bd5bf77354a6d985e6c77a5bb8c739fd319fc80b83b072e731d779fb9cc91dc5d874b5bfe2a4c77db8ed5aca3cee0f855a2ae662163d642465c9655c82ed75cb3a5bbbb58eede72f71b77fff689777549c63927e9c0c18b8d7bb9ba0a9da583072f9e0d9e0d2d2b573c6a432b87872c3c41d802994764137337b2b901f2dddc7c379fdfbc727e28c1e88b0a9693c4eea3af9c24762c8a42d8a2ad9a16adf9be1b2034f4c379a2a12d205660b0a11ecd8d1b7d406c80d88082118bd600b172c53362511b50306a51cf33fa3e572ce78a45bd1b2b38f40a35aaa13120566ca80dd4a8867a36395756608881456b72ae58566ca04637f4c361e106080c31b0284ece158b7e2ab0e8475938522c7ac533aaa13440ae784634d40688151b5030aaa1573c231bdabab9c981c1c64a2cca8d6e72607042ca867a14861afa513772bda867148621a34a62f7d124762dd9d3473d9c1f2ab4e847a59e58348b4d8882f08b18cbc8f75df9ae00b172a595e30415146c70820a0a1f0e15f7f2e485183ecaa23178b4455f70420a466be80b56ae502397d48bba680c2fd8408d5c392fc4409373851a7931bc682ce70a356251584e0c2f8fba7258d4690c2fefda8426a072d11675e5b8f7b06460794a6cfc034285e52a63566898c1bf96fb90f7001b970108954761395458214dabd56ad10f46d382516fb1682c463dd614cb6bb158ad298fbe98bcaa6fb3688cc658b11760b19ccd5af2609445f5a2c9a17ac55a39acd847a95e3954582e20545e083fea9f8b8b8d7b1eb5b159f2c298d7aafae8ab05840a8f90ea453dacc9f98050e111be72366ba945bd1c2a2f8436fea2b49543c565e1472d60f3f2c2168fab35d50a3f1a1a63f1b43c9d100ad34e080b127a3953bb15b6bcec85ad1faf0ff6234659ad50e7d3f13ceadac1c6c5dca045cc0d59c258e8626ec022e686ab156cfc1383c395bbf77d1fadbac9107e4a3c02e85cc12274313774114ec56ee012c670e012d2d0c5e08025b461dd509d2fe845c314c85e2c1bd6abf55118fd5ad45d406230186dd11b9793879fd24761d405c4719876629ea713b276f06ebc1cd88bd250d7eb870bc80f176db95c9ed2473d203098f7c15a3f5a2c17cb45dd45ab6e328431263f5ed4457542578e4e063eeff5c211b26809a18b99614ce872d947bd250f3f0f091b0f612a168bb1aaf88260316a138bc5621487c973c56e5c39add78b7a30188d81f1c2781e8e126f8c8bc9abf13caf2627067bbdbc7079168595096134067b167589b1014ce8a2af5906b3190d81030d70943c2f212c67eac6867ace940d102b4e4cb9e8476da0464e50c97094782f1c384c2efaca41933305fb4224ac1b1e21f8102d49d14a2b2b5888d8e2416193e346c78773c3e58626b6c18d8b662a46bf187dd18f06478927c686319ec714a330ea38321a202e1c252ceaca69c5be16cbf3cf0b4e1a357cb1e20b0d3e13534317313570095d4c0d5ac2d0c5d44080f015ba981a02400418a177117a13a1531716214b0c00a40859744a86d087aa5a3f58493f583940dc71dc8128f1426fb764089d3a2b86d3d4f4ca71ea1e2bc75fd4f34212c8762b7ce134b9a8e7fc50a2c3d3daad90f582e134794d5332fc78b5743ceaa23fbc97c7137af4a32e04d888a1a10930369e4d4eeb53924149cc0c5d4231575f844e39f8826ecc842e66060fc173a8a8938d7f343a1f4d924dce0f2fe7bbb1a11eb5a10102b3f9be1f361ecdf7ed5648437f50f1c2170d4d8c26a7ca0b5b542f23ce12c11a5084308fb63cd60bf6e3f57d30fa03465f300a63bda86034d4806caad5f2a1186d51970fd1d0168d51bd680da5014205070df5804cd5e4d0e478f8c1e82bf6a2b0166d8991227c515713a187ae18a42185982b65c259e862ae74117a218bd2b8e8d4eb454303a32f58eb86ee160d8501a179b96e62af168db16c5855ad10a7c9455f3f94e878ac1775fd70fd70d1dcbcc22e642fea1427e9c5f4852f5a75932164518ffe88519d1dcb51a213bee82b4787e70b7ad1cfcb71b97ebca8ff7851efe57af1788fe38829c0c686e70b9afa280f1161ceecb04648435b3ec572d5d05016cd6ee1d01bcac249b2c954708455ad1046772b6cb1583f582c5ac362b1680b674a8632de7393f34389ce548bfe50a213c6a63ecaa2353494a6a99553e5fd88e5e8c810d2d8140df5bc900432a71e75fac3a32ffac3a33a1eebc5fac162f1b83ad08614a1dbf0fc68d1a956e83f5a394a74429db0153a8bfef0688bb280fcf0280f0c4605a31e8de5b0a8a63c8f7a0e152f74e55079947e393cac1c6870b0945e34bcc0c6c1518ab9dfd01f3834b406070815af898685c3a4c5ca51f2a29e84e7eb800d8b8d31a10d8db5a82b8517885118fd581fbdd1f1947ed0d45098bbd31b2a4db19c2aea3955ac1c25cf7fd0d018a5c951a213c672743e1d8fbe603954ae9cd68781d6174475e3439f8bbaeb9533e539931665d123dc5b32b83caf015642166d0520fca84d19218bb6c2a8f145f8511bb7c95ccc95172a18659285fa9361ca45b717e6fc60e550e550d9d00fc8940d7db570986ce8476d5c5470849aba987cd4c60504489557e5c568121b1aa755add0a35470849e476b5839555e489343d5a2db0b5f392daa8f6e2f74e550f1c2580b896f515818ff168da9514518a32f8a83e52c6739cb75dc73a67e28d10979c258c8ea808c8a177e94c5a236ad1665b5e8cb6bbd7098e030bd28ac458575c362e97c39555e52cb63da093dfa01997ab5688cbe70583854aabc10e6a254aeed9109a306143ead8f8310c2cf693802d994cbe3093fea29a9f22a6083c37ba8a723a422a422a4c2c6c35172d7d1b9a12ab891951a6a7069c1c2da8925b16e5c382ab870f09d9b16cb75e306c5c9a1e29d09a590e1a0373e9c22f9582c168bde8001c17173837303c70d1c36de0d20afd07192bc288c5ef12117bdf9207613bbc9b9a12e2eac1a16a5b981b97080511b188cd6c0288debe626278b0fc1428f4ec5804cf108a73e88c9a65c43842f336cb4178653248553789e37c5f77db02f9ca24c19162bc98b95144ed172b55a38495a2ed62b9c42e6d1245f3805ec4b8a019105030d20234b2801d767c38d03b008a762b15bac05de8d2e3e9818568c032d1b0bb824107ad4e645068c8cabd0a3319a3542cf8731b68c71c4171a2e504516686841430068f080460d679c39238d33c638038c33be38838bd0c38cd1c519599cf1e50c2aba8431d344183347781bc062083301308325f43066aec2291d34ca80b2097fd0c0151e188383107888940981870851181c84c0b3451834aa00a3d584b7a08c8b09329e96d712686c117a9830685411060d2ac2a03101cf8a2f3484584220ca8040190a28434be861cac052860e6500808c36c840838c31c668e20c2e6768398388d0c39cb1c319339861c68c32c298510233c29801810098b1c58c2c0230030718186658998a9571461965841ea68c2fcae8a28c0e90a1a58c2fa111634891c615cf4c99cfcc9810b0c29071258c990e782d33aa20234be8d11b335f72fc2a4c1a4d84492301a18731e38b30691011260d1f2e504516a10f610142027cf4bbf2837b9e47433deac2e2c3e8d5fa6eb0a8d7927dae580b16a3af98eb86d660436b28ec864ae1e4b8b848d9e4d0c4983ca3308bccc5a5a68bf7b8ae70e9c2a58bd0a35d6868c68c202422242224824be85d3c16b571cfe805248bc7fac22cb2cf03570ecd17ef69b162748618e572856b0c920fc8a672782dcff321980b890a64532e2fc28f8641f0e0c183078f315f19366e4496634c180bb9b84c00a32d586b02322f6cade06573e541e8e5e4f852b346161797355c61b870e1c2850b17562bc798a91ee19725ec4186838b07612b27c71717c8a66caebe6409736820fc707099c1e6ea8b0e2e1cf0127e948cb05566ca861e6db572a6cc295ccbfb72a6a47c3e9d9007590df51f521f4e92242c1c2cae63433fea22e3867e30dfe2aba14464df940c07c8944c0b2bb4c999920de1fa12d6009922e232c3e5cbebe6a5c1cbf3a0178d852ef73c845768469645cb27d302644bf851282c23322eac9d8fca86700d88083ffac9ba841fc5d2ca91656185217c3a5ed883ccc3e165069947a76cb078a159c106210f396c6ee8946b8b67c6f36c68c228645e6823739961c560af1b33496c9a88c95a2ed7179b26bea09a9cef8beb8beb4bf8d11b59cb55c4102d1eb495a3c30b8f180f322fa441e651306453365cb4841f957589c55a74ea83d035868b0d17598e0e32def3d1503646861b2fbb8e178f5014a409d9f649a3957e8dfcbfeea07ebb6d596ffe6df5d55651f23536ad9e7ec67b6b90fcd7bbb2ac58ef9eca862bfd4dc97f0dc3f8243ef29c905528c84f84ba547faaccdde90944940ed16874eb6ad21f6fad226f1d97d85d9e4d45454227d1f84e886833e4d0824cbc72f7135c269aa1424dd0387f157abb76575e4cc9fa25c59bea6d6bd4da6ca7cde2f478040c0287f4f4ecb6cdfa62533f546e845232216fb8d3d8e9a33e375addea8ebad92627eede25021070f71f273e4e6e552df438ab15637d2cd8f782d9e890fcdc6fa6dc6f26bded9323f27f5f262962ec33e7242a264952590a165d75b42ceb88e43229486451ca545ceeb7a55fdeaed53474f71e9745e9e2eea03e6bb22855dc1d5f1268c819e504776fe23216c8385896afd395f2dcaedd3a5b1bb5614ac15d599e364a5a92eaa7364c71e3ae462c540139d9f7ed78c31f27fb7e6e574df175fdf2a156c840d04fcfb5f5f45c9b98c5155a4ed221f712e31af9393c1611093a898a88c67792b1cda688f357b9f8eeac6b594d5d735235a3a0e8c86f6d74df6a73add2bbaa2bc3bf58f74e3f66ac74bb76cc5849db3e3f65bc9d60db65aa7b0cf5ad6654d4ed5aaabab5f824e6240a07b7fedbedd474e36e3647dfdced4a2f7ddce679dcce9db75c719b3ed5bd5394931b9a98712feb88b369c78722a36ed7eafa797ca45cffda2595bc9fdff6b92a9f47ee3bd18a481013f9565629e33d561b896d7ed3fcadc9fba5fd31635cdacad2d6f23fa9c09ffe5673f6b349967849ca848dcba8934a359990d72a21afa58a1ae3ee4d2e8b0a421685828353a46c5c6a1ef25aaa285240fd385d4b4d49f29fdcf7ab9e6a8b93a2e0c6a5a69fb39bee8cd666c737750f792d950e792ba589af696f9a154769e4b554347abfcadee893bf33363f537a2bd5967e7e9c141359147d73b7a97e9b0d4305b12b739f9fc82f4bab04a7eb8e229fbcd84897183fd5ce283257dd4361fc445e5cbb4d2ed38fd58caab2fabeed91ae6d2a317efa2aff59490527b76b77386ffd48f74d26bfeb2ecb50573389dbf689d65f9a397ca4f597e4eb126353cce19bff29676b79f494005bd519ccb91c91a1a19ccbe5724fee4e87444d55df74772598ac706f364a34cd3a3474e4a74811a0ac8786fedaf262315777450a2a02f49f71524a828a00555c54501120ac937aaa5a1a010dd155a89f21ba0ae5334457a18e00e92a941090ae421901d255a820205d852a02a4ab50448074156a0890ae420101e92a940f90ae421df9d15528a11f5d8532f2a3ab50413fba0a55e44757a188fce82ad4901f5d8502fad155a89f1f5d853aa2ab5042ba0a654457a1827415aa88ae4211d155a821ba0a05a4ab503fba0ae5a3ab507fb153d39726284d459e7f6fab93fa5bcd31e5bf5dab64de9e4a5b73491b5ba8da3f29ebed97ec6a696a8ba4878676db8a64f95669b7976c877ad26f75efb4e487db30cd9df7298d461f273534544df2d67cdbf7ab92644ac038487eeed49f95180192afcf6aeefb399f475bdb25e17e2d2a8102926f4bd3acf4fed7a8ae38ba5fd3ddb619e3f659a9ee9d524801d3062e63a271d9521a2e5b0ac3654b645cb6d485cb96c0b86ca90a972d7571d9d2167787c95e371b68c8997f57976027f271637da86d737609f93a5c96e1c64e1b63a88b6dc55925b9bb0c52ee1ec368f7e770dbc2504585a5b4c64e50f973a7f617aebdd9cc84ac46e64985f11277cff9719b89bbbbe0ee555abbf2247113f95fd62a79b11359e2a5fb557056daaf7578b4f1bdf52877e6ecb54d3a5cbed1d3aebc4c34794f2577d7552d89e0eeb2a4292e4b6ae2b2a4251ebbf8887cf29ed848efbfe52a457eb6fa9176b736612ca5c34b3096faadcfbc6f13f29e182ae331ea3776526f937d4f7c94ff5efbe476717ef2556c5466b21befdff2e792a8c65a2bcbaad484fc7bb351d756dcd165228af9495b85b3299e660e1fb9e7af2a7194ce19376127a9b2ac476789a3c8cf78476127aa112f3977db96b666757da5edaea9c6c77dbeb8764695183f7d59aba5ad55e16db2314e9bf546b9bb4ed2d8741a1aca7f6dd3f7dcb3e775c5993d7697674ec86edb5c4d9c9f859dd1c556db2a8bd4c4dd3315a40b580515213204c8ea877caa112f3972ff8dab86866ed76e5c65ab2e1ec5dfd8c95fec8484e65caea727e772e2cef6bfb3fdd7644752ee0e44ebcf2459e9925412c59bee5ec968cbdaf94457504a4a32a23b7f95bb0b01fd9cfa564ba59ab755525986a3b64dff4afacddd1e1ad2fa625d1fca8809f29e586963a727b1d3ad1a7dae465da68d9df2dbaafa4b1b3b695b0e0d6d6d4b51df6a4d34b55d1276e1e6eb9e1c26af843c84db29de54937fbb15379a98fe79edbe78b479d313aff0832cecc0835cfd98d416b7296eb4381de5b9d5d246a5c8d7273df10afe024e6c0149e940a00ff8539564c4044a2a8ae6b9b7c5d319fc1ace5bff803e2050cfb565fedccdcd506081eb0c5ab38abf6b95cda62802dd7099cf5ddf4669fa9e6215154d9f94fcb256efd3afd1adcf1c256fa5a5cd3dbba772772a779f72778b734111180722e5d484c99188e4d4840994a8242a098f8e9294d05b719be617c97be24c4322e00fe8d34355c7f1a9ae62ded5d50c573557ee5757fe5d39ebca71b8721d7470af711a77bfe29ec395d35c5dcd7075a5c395fbd5550e573478b7b7fde770d4934f46af3da934f1490b4fa63c91f224ca13169eacf004ca13d91014bb9a73a51953bdb3f1a51953f27bac7c286ee39a8ed7201bf261c88aee4a79e850fe3d56abc70d49b95dabb5cd59db67935af91c11f222223f3cdc5dc8ce1127ee0e4449a025d01f5af5f93511d0a7e7c9fcbaa73473d6ca8705991018772070c80fe8d3737538d770bae2849aa014a7eb924c08018e6d97e9c99a6f6693adeb12bbed65aa62cb7aad52d2ae34cd5057dc139468eacfd5bbc76ae6f0d1d0d0f8241eab80869c45627ed3aae6a8465cd6725937dea255cd59d5dc68f5b5795bbdf8bad68a80869c44563587af542ea805f99ff4d010d5884b230f0a591129f6f839e3916e7c35a5364ba9b6f47152d4049a98b14823df4867506f9bb365eeb7de494579fe279b2859111e6e02912dbef55f11679b38bea9858c5f45a407b27cbb42fe2a26e4df263af7b7aaf53769fd23ce4f4a643522bf2abf7ea8ffac346a9b1b1ad2f7a9f01e1ad2e7ae3beac75bcb6ab4357ec2b6cbb4cb7857f2d6a6b2e63799943597b113a977f5a9887cd3b479eba19cae3a22eca47f050f0040de8f77c373b97b76199097fcdb8ab9fa79ebf2353d818892365756d9cf01dc41100cb2bfe4d9f5f38d6eec44414dcb5acb732ceb78bb96bc27fd5ce93f192abfea4bb1ea5bf31ec2e98afb49e2ee3b639ca6186f9cae383af43a7fd567aa3388d315c7e4ee4b2dbc45f314856cbbc9c556e77772bb96c4e34ddad7e227bccd5d3e72f724493f5787ee2e020b2e130286bb2bb97bcb3dc9dd6f7553ab33b8afc5e610d008e8d3f333be769d41216011a09e1e9caeb87b0e0dfdad66bcf5d0d0e7ecbe6716e2230414e2c3dd6974884677a5bfebd694fc6d9e39e32392697f59d6a79db1699e5afc5ccd367727ee8e44c8b69bdcaebdb6e63e9375d7dfbb1a0aeeee15b9df26b72a95bf963b9d6e55ea49dc74ab52f9c9fa50f9ab9830b95d9b9fcc4cf647fdaea32dcd13697f94bb9fd0b2f8eaf0a5ba4d48cba489b44cca3ada8b6f1d2f767277a25bddbfedb53613dc7de84bf1ff32297dce4afdae48ee7ec4dd5de862bbadc6a6bb1be17127afa5ca38e724770f2ae29fc3517fb1935408c49b22444d103aa31411897a579ffe6da2de5553eb5a1ee1c65d4d6d91b4aee5f864e36ef675ee0d70f7eab212d8204b7bfafc80403dd8766dcf9775d4e4df9bd51ab7eb5301461881fad01380d0fdda66c3ae2257cf0823d0138a2879df897bcb022d2890b8b77a68a5d0817b8bc95b4bde324146c2ce580b919100c599be1d5684c0c7fd5bc1dd4597ed98b0ebcd56d5775db1ef89477a4211bd5f8adbf6a5489618c6c1d6243de184316b2d3a2ff72be43fef9a107db1dde397251e8500fdfc0c39528bd4b715111a420ef923458a000d013a1254a448cde783aa8d14f209fa79202121e3d66716b28214d351f8141e51226a455a907cf27fb47a9d7fe32afaf9d43729081241b4d0212b1f9d5b957ac1dda3d024a9d00913d4539027f728f4884989e928e929aa470a4a92941393a3204582f090bfc7337f95dd3667b33982902cc88d209ebbebd02951a8131326a9f08869c9d353f8d46464b575f2392b457e155bd6fcf7937cce4a1d4935291232e127ad13abdf5fbed4d368f3e74e27a9a4a8a423a7a4262a2445ddaecd3b4a4849aa095292540895d4d4c429694998040595c4a4c424e59484c444a58e92523062b2644913a724a92326499c98a0a4a2a814931154529224292726a73069091326463b23f838ab2a881f637421e22d27b003c44066715808929c314595907b00c035423ce101879da185c905f0c719879d3973e6cc1930dc35001d8c3c61b3807b14896b8550c2bf9c094fa4b0d371d70058171832421177b1b004d20b9aa8fb0e8c041d4a2872a7c2e28a266178e1de0564c0d280971587f560220345acc1c45b3415088058812a87d9f0058b94189cf1970a36f5842f67dc61f5db5240f1cf054be0930f1c71676389ab29485ae0a241599c0002671c762403a316a209b86b0546d03c2f3bee6dd13141c7a580b39aaed06005ab04f8872467000ce4cef84bef6005929dce595a2079615d4139ebe9a98a041254f157010230420360b0827f5d80965a6b74e0b01a0480c4d59203384c8c1688a0c00dcd5d8c02d6f0818701fcf523480a37b470712f734573580ee62d6f9de0394c8b47ba661ecc7178ae0dbc1b5e0ca865c45901687900fb6231578be579bee303fd734f85160a9ec34c70398e97e79ee7b95c443c8ff52d791eccfb3c8ff5aaf2799f07fb3680f2bc9707f3cfc30159dfeba55343bdd6f7b5b0782dcffb6a3c333e8fc65d5eeb63bde74d7d9ef7c59a7c3f3eef7bb53ecf059fe7bd3e4f056f87e7b1bed8055c2cd80d6f03cfd311637906f03eeff579dec712bdaf554343e4bbf18a629e2b04d6091eecfb5a3fac9aeff3767004792eb47e6c94584f9fb7fc5380d762e9b45c9fc7729700bc1378feb94c2673cfbd96f7796178b18f04cffb5cdfc7f2bc219e102fe6dd7cdff7bd8ebc20dfd7fa3c1f1668d32336828793818f4f08cff5f5f05a1ecbf3efe9a3355ecb3ffb724af3786039900b788e24031fcb7b792d9887e3b1b27c5f6c88e509b1bcef6bd1ef8646c773791ecca7f5f3818ff7d5bc621fccf386bcef1b01ebe5d17c2d569597f39d9ec7eaf1686c3e2f06f33e249f079f075f072c67b987c37b7d9f87d392b15a5f8feffbbc97d7b27934217836aecf7379378cfcf8986036349f578bddc03e4f052fe679accf6b794b5e07df918f83cfe6837d2e8fe57dde67f3c15a1ecbc5f23c231ecbd3f168bc1bcff34cf03cf86a58302fd6f23ecf1bf2bcd7065f0eaff579ae9677e4b3f1589f071e8bf57dadafe51df93ecfc757e3d178b1d6e779de90d7c157036379ad58cbf33c23d4e341428c885582cb6379345e8b874743336413e587071e082deffb5e1fec3d990e1f9b25cffbbccff3bc1e87b9e92cc8e09559210ca72b144106cc0a40be00e3493dc1029a98608c4b01e1c2a38ae8c047552d1caa08290c0005043351a188a9324f95089225457840c652e5c0e748113c841ca937983062029c108c00a9f1824ce0b111443b738020da868334353ce8a16df978c6208179a80f092568e111e3031abdf980eec8c0b56830c569ae1f9cb600e0ceca8fdb980825a27adb9b25f175af0977f7c06594cccdd684e4ceeb5ea35ddcdd85538acee09b75d42fc5fd49464d9dc1ba2b29e2e060fd604d71d5e2e1aab5e44502f56066cc2043e2cae5ba818dc0c50cad227e5a3b6e8e60e295c3556b09cd06351cf000c015cd0a2d16581ed8ecd74d4c2926f4aa6199c0a3c6b581ab5897ab1c5266a031c232c018375e5cb9ce5cb96e7c80e0ea66e74c7145c3e3c60733c125c415802b3a6c7a0841436b872b1c40700061f5cc84c478ccced0e02a2126042362e16011c58eb86235fd0879d5bc9ab862d9bc7cb4786c7e585eeba6c553a3e42a01b6c2cbc7abe6357593e326070d0f9a0d602bb0d8a0e1e583c58506d7972b11a65a392a8ad0416be806c76bd6f2583daca3d6d3cd8ed7077b02fb017372e9b8e1bd6c58aed60d8bc5fa5830568c4553c3418b056b02cb00c753cd0cf682792c588b484b48cb8bf560fd9881e603171357379a9861f66afd8821d1d0f282861c91fa99365620822d3a0046035fce5bd40f58928c5038c108be62842124880c470c168505264e67b208e3810f7cf0c9220412027811022daca022024f2421041c6100e7f64167336050a60b316130f000196268410a0b5098382551fac0831d1b483101199e58400c30881d7080ef0737bc280195dcd59936d038434a941d1bcc426045062a40450420c0001f7ac0416c6185274c9c98b4c822031ca04096a51170a1851518a0620a248ed0a10600c0a04c08c4503185140db072f52453d20ca74c0abef04203189840145028f1030f4f8a2d4889f28429c984a19d198e165968c00005f8c1ca932da4c0a4946482a7e00b2db2b822031388020a062801440f56a6a4c0748209434688fcec64306bf999147c51822b3490010c4c200a062801c40f3df060e504e21429280801d7506389121c3748c001961d7480f2c4c7081e74d85939417386d5c6c7864b8d2f0631265a4b7c4a7c497840df0fcbc7036d7a5825b0e8061f7c1c783f603e583d3c1ead1dde06341a6490014bd6c271734386e3e3a6870d0735331a58ec05fb5e2f978be5f9cb4a8d081bb882d1c0685a376241301a560737523338e5c4745e3531a1d60d0eaf75f3125b3a160e6bd6da020004b87addf860342d1e9c0e84167055e3e4866cf1dc98e123f2c3d5a4e5bd882c7075c212a41900be564d6bc90e1a572aa08104303378aea1d9074866f85a3e583da607a4c8558298abd7eb83272b3a7018d5c04a32c30d573d689edce4b8c95145498719d335675c116d9a24342064301a168e8baa85c30e33c45a375e1d783886ccb0022368480081959819216e7c362e2433d89196928fc285060d5c3a354a3523bca4b448801da141024524a161091f3690f958c11997e450c595cd692b0000708070784e38609900eb1134430decc8ab26768365842909ab831aa533ae6a604c3851b1a51f494a35344b3751249104ae76b478826a5e431ae0bc6a6c92725a40e2aa26b6e4414d872b174be6f25e2abc6a5c26bc482832b2396982c4965e35b01e38483421b49c6876e4e060041f1b90102b4109d40833d4c440d8109c2146989e60372d980fd7102b88e6a615842281d1c06a5a425c4a5a42483cd70f1610d7063e34b1917524c9041eb19a0d688ccc70e4644324a6d30aa115c26b892b126eb4905c25b83ca009a1c5018e1f5607345c81423c968dd5d362b95cc869218829f07d620160bc7079628923729001062b4d65fb91d6821416a0386912f5b42485a521a1213a238800026d65000367c400630528148990e3a3074c0b07b048020915925018ea40c78d4d78f3d4100c141de4cb9add607c36be144cac865a8cd959e14c1e6981c56f5fda2a9bcddfb5aca3fe232d90807a88cbb4a0f9afae9a1aa7eb5259d623ab2f0afa39b3b8ca028a67e193858c0364c02934d20b072c074c92ac1c409213f7f8539f39e0239fe6ef7afbf2c9b27ec615f114f1149966a86b0d110fc9b4b3fd1b0d0d49511e6d452c7e747fc5fd1aa7ebd2d09014b65d2630eeb427f13844dbc0167a996ce08a5975e10668baeac20d2071df4028bbe28b839fc3a3aeb827ed68cfa82b76ae5efa38a92b9eaef07fddfe1a7d5d3f6b60cb2d9f660e679cbf4acfd54009927fdfc8651a90c9be8871079d644c69651d9340dd6fabdfbd12bd7baadaa98b764f55b47b25b9dff6493c7e31fd0b922f32700a4def13efd764dc0a2fbe5f5b91c5c12934fdb76a8a667d9cb85f77e0322b4cb12c2d95154aa872cd7d2e9232f14eaabb22d55d496d7b285d73d2d09054d10ee5011af2243eaaed6a13c9e494426e84a2ab59dc768abc96ea56ddcef85af1da5c59339eaa3911db2ed3e7ead53827ee2aeaafb237f25a256feeb6f854a3dd5fc56ad1aae6c8ffb27662a4dc68a5ad6a4e29634b24e8bcd832118def644815516e843234045455c5ce1dccaf4fb2e676796a59152394baea6c962c8b8686a430796a912cf105c660f2d434bc40974a9a27262f60e554f89ca23b3a53748d720125e4195523ef47e9fe5a61a3b2ac639114918d89795261bc84c4381d19700a4d57d1025dee2962f2d416c8024ea1ed9e4adcaf2d6085c9535b80861528632bf95b56012dee0e9eb50a24f11c66bc4d9b47fd48dbea9df25f1d93dd2b191a922a2a2a9222ca58892889c80a1bb97b082ef3b2c5bd98322f480e4ea1fdedd6d224f39345f9a132d2d67fb3e43e4d9cae4b4544e33bd1f9c9fa4bd87699b6c6e6141fa0294043ac9ebc53a461f52493a1a1ad7fd752ffdb84cedbb5bb6a6e8dc7229289b4626e849271ce8fd375293742216d5011a0259cae380a3899f15da5f0f252d820850fa92b8e4e408d092431812777f062f3a4e4efb152f2d61ade3c3cf9772de904645e3f4345d16587961ba190f7a368bf6d5827bf8eea7fa5285a8822b6b5cdd910dc96929ff1c5b81327812fdcefeb50025048000012e0c197263e55dd344ad62e6174c9e260d311947c628ab7cde14d29597134ece2a44bcc1dcc915211c0c2ac79d3fdb208ec22e0e3eee0cedf8c6b3a745b0ade100263408008082c3948dba13c24138f0e8d5655dc499a00011d5e1cc46dabab9f1fa0c30382a018e3e0e7ecce98f2d4fc557f6fd7d65dcd41f1e4ee4c5ce680329cbcef00313207ece0a0d6a736f1d6f4a4f2f99fc81c20d4801334004b79eafd37f2b4aada5a5aa5edad2a34008a7bd311147a2bb5e1a406f09031c0020cc00cf880011fc8b860c1c5caefd728c6b71ace26d5f67ed58d96f5bcb58982e49b26cee44e9f38ffed567287d6cfb74bab0ea8afad393bd6d224bf8a0b757730d7727c1237dedaf3f9e80932e48ff471524f68b927b6d1afd55d37ce7da6e113a683f977696d4f2c81f4b35e4019f0628a498a7574b7539655875e1d5e40976b73f9f7933a88d4025e7077506a01424e9071076db6b33656936aac25ad549b1597ff09bc96ee60f2d4da525d754e78019d4842e6c493a578dfaedd27ced486e9aeea28be25e5f9b2567163fd7c4faaabae093337fbf9abb0be55d68416f7fd9fcb7893b69aff38a92670e0eed61cc578e7bf3add041277908932fac46d4bdab23e4e8a892f3226acc09da62328d48649ad3fd7b6d5d9f88e54c6848fbba55fa3d1a4644b8c01c9df9fcd2f6b9526b5c40c5b677dd6f16b947c7a4ffa3829f2c9a2a2b2ac474554572a4544941ba1d89c5d221615ede810e54628a255cded72b5a3b5fdfd5a91d64043cedc6f2bfedbacb0d1d64fe64628454456d848a6840e0f97291103a7d0645bc06cd9b20577cf2d3e0e4ea17dcde651d7b6c8c8fb7a4c7e5b125c3c375ac992b802a7d074f95b243f09289f42bbd8eafba63eb5f863c6b6a1215a14fa1f959f6ac4a510d0cfb9ef8973b5485bd526cab32304f473521e1da2f19d8c45f96dfe938a58b42374521ea15387e88a5fdbd51cf9fa4b8c6ba2b6fbc49fb39b7c2754232e8d7665c162309a22e16c0642083a20384d11978baa34a95c22009901f971ff51c1dd7fdc4510c1e5342e1a9c1916387777970f2aa044e9254439d0dc63b0978b86f579beef5791163bc95f96f5a82ceb91161504b183115218d1c50807dc8cc88c38c200089841004600c0852096ec9c116f440b412c1901c5ddadcc5cc6591b7ec88216c1c5292de39c5fdca749837259114dfc2fded49e9447a9080d645a5ae0645977b5d18b2f0ffd1c8ec2e4a96ba6c5e91cdefad4e56c2d472b9ccd9bd535912c2d4ea4dabadab0cea499cb386c13f5eb2cea3a8a358bfbb5aee419f5b56a64e6ead69fb3524c3ad44b4c724cbb7ceafa59ca0994deda96bf2dee8976d2e466b57ebbef898dc8d2e28ea8ead6b7c9d655e988fc5c71514c660ec9cce5256cabb93e69245d737d0a6bae4fb942e98dcb287d2b2e491dce47b911caef0ab55feb30e79a6cf59760f28c7ae209776f015e134cb87b4b784a6c71f79240c2dd538077c45058fe78ebd18d3c53b85d4b75f357e124f96d173bed6ef8e64e6a6785f3d3107e5d9fa49820d15d7d3a732605698066146007e75908e1c5871388b1854b600020f0f2c3147524ba04d1850803332e000c9015041bbab80d3a4431f2624217b722c101ba486203342f83f802041398808f571cd4b03a6ba83f41807863012ca0b81304b8e080002620c2952020c4160207c0380a356011440e2746f023322b57f850012d3ee4080aaa87cc8a83381ed4a005b5f94e0b3ed00083fab9083b5c94a10282e4406a06a0112922c73b60e3011d48010957ee030621b4400d99e73c86886145491649b88e295a45bcb8c1876b7005842096c830c5673a5ce181209c94c0715c801e8000b52e57377450d0c50688b4f84d17294334f002832b9bdc120a4526c16b8ebeb822c304059c464802500738411157b1a5081c416409dc158c0a0735d8e064057f3d51a60009297af0977791982ac0e4aea740a2788103496b0b08eca042f383b75800c28726640802098b05e1991b7ab0171600aa8c305778e28aa5d33282012098f1f289290112471be470f5ed90c1034c2a332f1f9315b9032531d0f2dd3822840d4b977a5b4860021b8461858fb765892412c8321be3c10083130b50a38b27cf09141518401121a83784044922d0008a325e0e2c04e8a1e5c0e266320f4ad6e0c0957be1d3346e0294f12f32ec114050228b43d141090ac01ee878111cb0bb870ce0fc07164ed0e2a668c96908824814379a00e3e7122b96c86081256ec511ec94e080166f4189a72f9880e2ca9bd00c61ad9800852f21a5904086e98ea4254783528a1dfc04246a3eb013c47523158718722f04f98f171d170d57ca380f039ec2e4548185eb8cd0811910b270e021cc7a50b8a2e28a539b1516352419c13bf8318409333488c27d88b0446b054398c077c0108215041c6980ebe891450384e410c4332803012588aa40037c0653a2b2a10726c7b144c78fd404ca380e05c6a88247931bbf819202ee220ad06d3668238a1e1890c069ce987294030531f018191a986007213e8fd58c30822953ece03021323fb02c6186bf5e10a329881dc07057980c30104601d65d4435e70520d4e02d287e0460881c2e78eb0897081411840a676d200268fce0a53a0bf702dc112cb0c259321007890ea8e09f971d03177af4cf4a0c19cf04a27f4232482de0e7c6bd361c6064822e0770ef0a2b7609a404b947448e1a0ea80206f732d1fe028318f796643985e8c4c03d12be78f0c10674dcbbb981c05665873b0c3c10c1f5c200dcb72080124b5c4000ee16108090323b27ee0bc0828b27988c7137008c2cd3c1903b0fb2aa145a1eb8c7208198030e00833b151094c06b2dc0dd86c4aaea82e31ea504b50f062b7087ba6185303e9eb8a700022e84045871371a02044042bce06e42187393f0722f424512100ab870f759b2586820c8bd04110361e436dc4738e30b129460b983e0568c22c8c2fd032a55e0d0460eee1ca040231a0a807b0f9a10326a74e0bec106a6982141e39ea30a15da081fb867f0554865b47097d9b0c4088440c3fdc6163b70e1840bdc719cecac6085e96ea3c68d13c08ce05e73811368051ee04ec3c4073d94b0847bec8917a8a48001ee300edc2136700177d82705e4a105ee2f2562d49c38dc5d5b7042b0c3e7eeca2166cc0a8cdc5b580460458c16f7560f2b8c18000b77d61338b10d90706731e1c9a28222dcbf346c68e8a1c7fd63a2090d040085fb6723e19b3670ff8248400a006471f7c4300294e173f728500480d001ee9e0f3170fa02e6eedd9e886e43b87b465facacd1c5dd0382860f458edcbdd60b0dd04101776f81c743dce1ee1e08f272c50fee2e4508b22849b9bb124730d143cbddb37c8e0a15dcfdca0a665f6aee5e458d323060e5eed7851d02a071f7110524369872f7157472784289bb3ff1b8d9c207774f42c62e08ebee488c0853c50bdc7d480604169cbb13d97932868abb8323a8c04801772781874f130db8bb08608878e1cadd819899fa401477f7c0cad25490bbff98a2a999c1dd7968c101490feebec10741981370770dbcb042092e6a209385bbcb68b828c00adcfd062e972374f71b18600d68e1ee365a80282273f71a2a65f4e0e2ee34351f3fc8b87b2c878218da70771810576140c0dd5f1c28857070f71707b42e94dcdd0543931684dcbd15c699e487bbb7ee0f2f4631c8428a20868808e2eeac20902900eefe9d4008066c71f74f88ab0390e1ee9f94cd171ef4b3c78a4d27f16bb5d603081e1c4a24b8bb03e181bb3b103d40204692104840870135dc9db58209c584981c67c870f7d616239478d260025f8cb8bb870210519860c1c10052b8bba7830b3ed8c92e2226dcddb3004b860fa410a60aceddbf1b40c88100148870c506dc9d65c60b5a6840131818e205eeeed10e70a48912102d90e0ee1f142d3cc0858717627e707716979b1ca47e3cd1022677779d902b3242888fe70977ff9624a190021d82e88086bbb374f800260c21b248e10177f7cc245c804002a088c1c3dd3d072ca0e6385220b1c3dd5f041080192fcc501b48dcfdfbc28c1f5e584308141871f7560168b021701b98f2c2f7dfad7ff7884284e0d7d514d2d404d268bb9253687a68c8567e142d0a7ddd84823ce45b4aa3d5fd39fab89df111583ba39eee8aa37dc647d63e0593c0b2ac47e03ecd6be9e730ed5f094bd1fe6d655947905696f588f6361eaa8235c3a77f9b88b728fedbb0ed5af17eb9ad4e6b864ea0ae22dea24dd4fd15f11669d486cb5a654f2aa4154b8c4d11ccd8f6398cd35933fcf16227106f11a4914c21ede23b566b8651229871a51b9f54a8b66688b40b33ad4708cd9a7597bf0a8fda926fad76556f73127057da6c98bc15772d99311e4b3ce28db3b9e9aeac39bccf4a2f8fa61aa90dd721bd5fd39b0dd3f2499cd327f8ba676737befdb5b9e66c36a9b6a4d5b5bf91f54677fbf7b54c4f2125ed27d9813f3dda525bf97a5b6a048220e5012ac188f2fc9460a4b36f25ef8ffa494af1b6795bca63741ff78482e46b8d234ff271a336adce864f484e215412d0967fdbfefc5305f26fb72a45133f3f9931d5d8a4bbafe92f4d5a626cd6e8aee69c74d556d3685affdd23def4a96a4e97b6ee27e9cdbbda5bedadf1d213a877f94f2a4c4f61c6a02ecb0aeefbb5fc4cf5892fceb8f1ee74d541c09aab69c3a22802d12789b34931120572bbf656b7a6956a6bc3398cd381bba72aa9f2e76825ada8ff9e6091dadfadb7c6651533d6e35bfdc5b42ceb675314b5adca04f7eb8c773525274fd486a97e51b4559925c626a5f7766dcf67932a3979a27ae7b6c636204ab7c6385ab3d554936fb39407db44f16250935f5562fdbf71d5586b94e7f7f966cd63d4ae26ea6d453397712288755973f81e5150977f6fc58d36acf715a9e44890e6aabb3a4cd26bb735edf8598b23d6f9f5b6e12a8ae06baba3d13e87a3686fd2df95b4e4ad23ada6fdf1b378b1a5859ff111131ad5aeb65f49ccd82682b4a3dbb5f766a368b4527ceb58da2a569b7fdb3cea6aea7d62b0ac27950fb27bdcdefdc5b7f2d45c4d5cab36cc444551a491ff598946b5adb9da6ca5bae64abe3ee9adb8ac944632d55c9f68175bf2b7b63a7feed4ba9256acb99a2298d33557f3d6f13f2b51907c6b9ee3883fdbecd3cf47f4d6aa2f3f535d478ac5c7ed8cb5fe713c69d87699a0cc13e724349a0e5f1add950a57d1df1ae374d48629254b8b1bf7e336367738fae4eb5aa3e457b1d8762dcee689f38dd2c76d50ef4f0292aff1a53c9a2cdf56baf599bbf489825aff487efe7dcfc7e5fd5c0e8f9f9928a82da596664cc9276d15d33e4d4ade5a96a7ed49bdbb955eaca744f141fdd9fc4ca3d1ba99f0be7a8f67dedaee5b6bb8ac5127265129d04a75a5db1e25a1d15d9ed5f624b566485aba5fefd7a415f7ebfd5aacd256fffd1bf942ae3ef57d7df1a8a3e01e6bfe7d2dc55beb2f45f22dd5b6877ed59428f42f1e455b15a5d16b29fd5cd5f42b8eea1f2d792db5d52f2979ebc5a6aea7d6f46b3cbfb1137d7d6a5bdbfada7aab3b9bcdef84eaafaa5fbe2e932848bb34a8a6261edaced5f91c8ea227d0a88d89829ada72c636a8a62651dcfa6b5ab4d52fc59ccb691b16732e07e230ce14aaa9897ead1ae1b6d589a2086eabdf178fd75e2614dcb9f9dc64ae38aaad8ea5b76b6d968237f0298f2e6db5edcfa3a6ca58b7ffd2c76d5d47b2e25ca35fc357a7ac232ecd1fc74abf46defab51d1aeddaba6b5887569af1a69b8abba7cab84dfeeb537f3ef7a944c18be957d9926afbbb52babb95820f5acab371377bbbf66b94c4b8fd394cf5dbf23325eb6f5d6dc6f766a9fe5ccdd5a4ba52dac6a5a651d54d999ec2db89719b0e25493171a2a0aeb22795b7b9daa969c65afff8977eaeda86717ea2603e494a37d596ee9eaae25dd6d166bcb5c595e68947aa2dcdfdadea937e69e6aad5d31b52d3e274feabc3364c2bd576e352ef903fde13df5be9aea4baea74a8d55f32d7db677ab1bd56047123369fee5a824946a1d179b3b68bc74a6fd7eeb62debcdf6a4f2995ab3529aa841bc29cde16ca3bb5cab68595a1dadbb92fa71d7be0d54a2bbd2342b782d259f3ccbd152fd9fc3e33ef5598ed7d25ba935c39c6bdad9fe8d40bdefdb722ccbd3f6faa44c4fa1b8adfe927ffe4945c8b6fa2beed72268c3389b95eecad19ae11326a0352bcd4f95b14eb4d93048a974ddf4da8a1b7b6c364c1fb7a3687eb23e4eef5e0968cd1a45c1dd53d112a8ae666d4bbd7baa27f1f8487af754e2ef1b6852f269d292f0288a92bfebfd3477e66cc50580065106671dd536882e0e0681450741037da8b641d03a03e2cbe3b6898118c23de7ce9b3b2fc964f50f66c029b4fb26fee1cbad5f8e3f64a1c2e60fb81f7cccb83b2eb5fe11cb17700a0d8b88d3f6b1d0ecb55874dc872ddc072dae6d3ed4e0830fb86b0f65f29bb4872f610f3bd45d91740f4b7ebf8798ee2f0f5e78b8e23c40e181c70e6476d8e2be83b97f07243bc45c07303a5cd1018a0e348732e0145a0e39e0dc73f0c1c18ce3d0059c42c3e10a8725700a0d07d9953157b8b85f311d9c42bb2274dfc457620e4ea181b921cb0db41b7c6e701bbeecc6fb5b5badabb9abd990c5069adb5083b85fcf6af0b25fd780c57def1a68af6bd0995929034ea159e962e5cacc8a0a3a87452c5ad5dcd561b2c478b4aa39ab8ac57d925627e236ce5f456b118bb65c25ee6af9f9331e45ab9ad3bfb17edcf8b9dc5bb455995a87715ac4b7dc5b6bb23eae52615c8ab76b75aee6fa6434de9d0ec59a78f1950de72b2019bac0381d8dee6a3cff364a33eda6d930e5a1fddb68a1d6b57c9bf86f13ff6d62d12bbd4d34ed46bae9dcaee2beaf8b848686c8ad4553c760c6a748e1c1385d142914cc36062eee0e5e1c430c48dc63e0010319aa9afb8b290c5ff0130c59dc4fad6b09c3938364c6365bad5230f02837ae7ac1cbaeb4babe70be401d3c699487d48fdb7b575d712e94016fd7851e74f89ee4dd6311365d90552173cf2a5faa6871f72aa67b95255ca577f5eadfd5b26e6bd6b188f2d4a322fd1b3bf98d9df094f849a708495f5c4435e225bfabd7d6ad6b7557efc655562eb0d2f2396c45734b7ec5e5688544c98a475935ad3a32475deaadb8a46519eaae3898b1ee7550f0d66d5c46d95a1518b8aaeaaaee4afc4441b256d1bc4ac7dda9129413a7d38cbb9f5c78ce2b77da6b5ba174ef44a3d7da66a78ed7a80e72ee2fe0eb9d65d3665b666fd96eabb5d5e690dc4cd7253ae60ee62ffae62bb32c74d2e8102a63dc3d5f9efdbb90cad5df9bad66ae8e54a03888739fa3325484739fa3b285c9e5cd2b8e7b2a6c9a38f739913429f9580db38ea712f9dad65c94df161d6d5d97d42cf67107cb32a4b3cbc5bde220f9a57966b29e54e8e34d3569abc24bc49d36250a8de6bf5d5c5292b4353abb1ee4bee4684f68e66b8c1b733bb8833ad2e66cb91cee2005495ba33aa5524c79e5e0b5942c1f47b5a556b7672514f75247db32e6545fdcc119d516772a2c0ed24b65c3a59e32e3eebabf535bd4a6b8383885264e594931418a9afa291e6ecb580fd82bf7d2ccd9247457cd209f67f629b43edc2f125354d5e498247ed1ce6793bc5649ee4cc23db9e37c70b12ade4e706d4e146f55371166b62ddcc1283048cdd534f1a617d39a6f5502353dafadbad0b6e54e11a3d0a8eeafcdeada943cbfaebfe4df8bbb8b6559c5cf024ea191b9e2dac562fedbcde66dc53741fa4bee5e1ff73aee471f73f0ab584d7271275f20f1a666d5e95c356e63d0ac3a4dbf1c77e3db6c4985d08d4b5d473223168af7eb3ae266e3e3a4467777128fb406c6419269d7b238b893d3c1ed9aeb13ad363eabc1502b01dcd5529fe569da5b73b87c22f1a8fb5bbfb85975ba6631dd9d246b45e25e7938ad8ce3527c1a185a02f2dfeaadb9a212e3da5f8ba7689ff884c88208a40518b4a0a585215a409a4d39630a18076bd8ccfda999c07bbbb6fea96b0fb843a3e950f27b6653b04cc94de1e10e96f8946246ca172958aa265f0a1477293aee1ec54c388b428128b7281fb060c6c11f8bedaef975adf234a1d5d2e66c940d536d495ab62c6871a7d5fcb4190b341a0b321ac59b66c32b2025d1d13c57d8b2c295575c955a21079430c01dba53965587ee72ddf72c7155594328d7e2291343c1e2b55343a139883194988354a3a5daa94fb6202dddd17952cb7c6aa727b4273ab4fb4bac6ad3d0102d0ad54f5c9ff45a275c1c0c5262ece4e95eece424468d9a80d1440ba8b3adb90914776fc2c149e5c92aa586d496bf18975fdbd51c296593c211154594095e8b53d0e15237d1db1dc98b6b184fe1fdb92824da294a06e28d2fd41710ca0a541215cc501ebc77b75615b858555baa70e53e53c107b4375cea3a32d127330e92afa34f4f11983ded4aadbfccd84633c69b4ea10d0dd1a2d06be993cc73b6cca78d3219e3a710d2d212e3272658c012639b2913288e315252ce52ecc444875ad51cd596eafe6e8cf3a8fb4b5e6c54f3b6fa22dddf6bf194786daec4d814cb5ac3e5adc98bfbace90a94261dab9a139b968071f7d9122e164f2db9222e59a2335362c6ddc59912304ab628b9721795a8a04592a9c4f8a928ffd09094b82bd4d1ae59b4aab92ff7c619b78b76748a8aa4889a509e9abf488a48bc36572bc238eb2f315489f193fe12e72229a2ac97c6804b5bdc974cf7a52577f770c97b747f937cf16b294da2b3aa4d344d5a91b462121d2533ee2459bf2809f11b5715d5f03c511e92897c7d8a4bee0eda241dbf5513e9cbae397dea505ac634a42c7eed0c0969b455f9f5c6778644c2ec882c31a5bae2cc7ab4038dcf8e68b3a39a31ee9fcfaf2a5fe71eb7f1ef5b733d6f74e38b1bcfa89bb199fb4db535dae246a6b6746684c4dd6ff5291c330b15e0e0eef94c7735ba2bb82d79ab8d1a1a92fa5b35ff56cd10099e851e805368b9a88848e713d77037fb5724efdfea93ae6511e5f92a36caacbf440434a409de9789f2089d667d9c51936d6b4550484b444644e33ba11a6b0d0a29e3a38c6dbb8a78dc186771a9acfbe22a36635c91eeaf1645ddc43cc9fab8a29a9da21dcab33f2a8ab4f9d4353fd35885cd8c772d4b7cb41baf4dc4bb8afbc4e2e7ec364f9c937c59b3e689f34d143fe3a3f262d13c8b9688b649fe2ef2a9e92543434343e3576d5ba33c42a735d27fb1dd3bdc6d4b34be93ddb64b44196b8dd3754953e5aa138b9c28cf1251f8744434be932227a223bdafad79eb8bc7a21dcae3a4b3b34f26948764d2a9c2e6f84ec82228d1cc9d5fc53a112d3173798909a7ebd293f8e8622b8ae715cddc295ad55cdd1589acbf248ad7e674cd7f44872f25e911211e5df3ffae1422e30eead2e274b467eb6aa3b76b85beb883a45029b4c34819bfd83c29881b7725efe79aa33c9a164523695a87cb12dba8de555d5943235ab4b53323381abe467cdc7d1634a6fe12bdf69e4147f82c284a508f83f573357f9083536845c01461e2c92a366a671f59d526f28b9c501e251b97511b97e2d0d047511e9289f2602745444745524f64c5e5ae9a24d3fe28d21625e1aa225bfd2544e33b79123b118def842cf110b3e7331def90a45308e8a7c9ef9a87f000f28063db65da1a3fe1b67d026452dccde280908026931eec444d9801555cfe6c81efec8709a75627de2fc59fa71f9d990f191f289cfca121291dbe3e393c95ae5254ba6e1f21f7a39990310e92f76fbabf9fe94c88172156b78e33215048211aa72b0ef4a2ebc63d17eb13cce220696d20cddd4c320379e4ec7ea8cf55272a5da57ab0f4d07a847a64ee3c2af85cb5bb6d8b8a9e8686663c347710c72304ce786218a72b810cb8672560d9d9d54a803234245502757712cc5430e08c842b900412623b60e88e961d7347a8ee50e98099e96471f075c59934d4a1e9f83818c40cc6370818f78b836cd141ac9e6a949a05f101edcf46d8427fbee16cd26b69b5e13ac216f029761a81e656b311e84c04323488085bdcc99a13e14a8424be4f1b4e575c4e2ccb1ac215a79a850025041d31bfee6de25f0c42997bda3e83b00504118425f16fe4794110c1fd73566a969345eeb4614aa576655977d14e2ed7b2eab2ac4745455463cd5bbcb68827ace1d9956595a23c479487f2508d354be5ceade96877dd3ac808fb7e6e8ff5be786deedadce76a7e36493b3e793f879bc4f1c9a2dbb5b95c542445f4a5794a9133fa644b9b0de38ba92c876f38fb600b904632d13ee8025eecf48100284949a6a50f3470af5fdbb93eee7e296adc3e4351f777e64116c739dcc483eab75aa37f719307b48ed5166a4be2f27740e6566b9f3bf0024ea1cd3ae0c186c57d71b689d78afb34ab3e9b9ff1a8b7d5dfcf788c1a1a92ca6f9aa778abb5d2d64dd6c7ed44511e273a5b676b1349317f152e4b9c49a6a1a1a25d7da2bad2fc5bd7dc8eb422f9bb7e9e7160e6cdc1925b5c157220fbd1a5b4fb9e347f15a6ba2c713641575cd4ecc795fb8f27077ff8fc70cf67cd86b7f08145876fa53e9e707ce663e643e6e0e7700f14cc7a607130e3cd44c11e4f396d6fc58d9f9bf5e031e3b1c58e927fe9e5c165c6430aac178f968ace78cc768c0177ed715b874b3cdbd105bc12b263c933e6d9212b6bf666cba7fb77748312b83b58c4447988acb05159492b5a3d7999c8fb66d40642b30d78cc749439673abee8c8e23a4cd2e2aa998e2707a7d074e8805368628e3576153f5b534911c94434cb719503cacde1e30e6a30e6739d6990c5c16d671ad0dcdd7bf0c5cd34e0e1387f15a959065dc68be9ae94a664491323310929844af2d4445c8a0a97442861138dde8a9b32b072cf656ccb6f56f90c96c05bae33319ceabaf53e31c5b708a7eb126ec6e59eb32be2cf6650dc67331dbfa7ac8d8d6fd18ed4538d4043ee97b76b6fd7ca74ce9bee2f0e330ee2f882e32ffe1cd6210e1f4ab5bd31a64677904b6f6c719fddc039589b66377860a719ce98190e1707e9ce679cd3a1bb5b8f663850dc431c1f0733a64052331c0767375daa4edb9bd5f8d2fd3bca43c31babaa939add20f152f7b699cd18b59d7e9bf116f7cce6c946675663e6479cd568c05d57a2cf59a9fb385dc645a1556ddad8e64d79eaae484536d1bc99b5d79165bd7d59da9afb6b6b6e46d3c57de7198de995e62987331aea1e2bb367b105cc62385b7336ef6a7e9ec574c8cff5cb7106dba2a776ea196c8b83ba8633186e06a3eebb5ba96e82cd5ebb6de65e5fdc1d3bcd5e5c1c24ff9566af2ce0ec05c5b7cd9274f692cd5c65be685716f7abc3b533ca6535734171b047d74e3d7309f9cc256b9599f9ac0586eea846bb5b4e80b468a7ee8a4479489ddaa96ba72ee2a92962a2ba52225d74fb5c7745fab70d0d49cd585b5cb3ae5850583eb97ec992815368649d7d6448b27e5eeee70f0bce8afbf5ec43e2e0149a38fba84ddcaf675e19f18bbbd7ce99a7c57ddb93cacc83019c42ab9d5ad4459447670adbae2d22998894b6987917dd3bcdfc8afbae46b59d396de642ee33e7519e29e3e0192ef975264c9db97277906a7b66c91dccd5f16be4191dd7f68c836d70a1641b58a0da30dddb40e20e92656d8387bb0ef2c406995a568d4d2736beb8bf8dcad8c0d1abc3e4be2716f2568a0da19badd18b755d83056b64e160c6bb9e7847792896b2bb1d1631511e29221b136c49d28a6b58adf15485b369d63568b6a6d58bf7b7d5ab51e68b7b396a712773d559393885266ef3c7b1aaf1a44608bba2ddbe2736fab7e5d7d8c4d9144f52cc78d77c8ebb8a39ac4fbcffb32d0634f718c8a80e4d030cb893c6965d8deed76958ed6b7369e8287199193096fc3c9a69c2cc684686371a5ea08145e392ee13f32025511d2ebf46f75943c304c9d755dc4643c71d6f1d2a3b630cb893b1adaab8af699b3b834bed8c2b7e06eecf5802cd28e3b527290f8d56d68da5cc0003565d0e9b9165ebcf3233a0b8833b66504a7eaede32ccb80e97466570b95fc655595a2a1d15520692b08cb2529db7fe4c77db92f6a4624b32c03878b3388af126430b59e225b2c43832cc9ecfa61119443425a7ff2c35869931beb8975534ffa9c6c8e2e239064e8f21e4beeb93a238460dc94435e225a4d5f93fdbf4ad96361848c1c0c9d7b9e77e9c94a565c03838454a1429f496d12255e6292ce35527c610623cb93b782db5569f3f478f28f81793364cd356b5894675c58921a3551762db65fa010a03cbd65f1586a9bf2a0c24ee61f0a87ac10b6ed7922f58c2bd6868488ac80a1bd5b2ace30b90b8e3b6d5bd80475956b14c05e38bbb8b6068a9b24f8aba823183835368e4ef138ba4ada6986db76b499cb37af2ab585df3d7fca2bb1fb9cc055d5c5bdc1577509ba10ba0b88337fab906d12e005bb085d36814b733d6d11650b5e0c9cb1d1d9a5be0a036ed68b76641095890c541f2b725731d6bfeca02d359d043660b77077327a92d252f1e5ff75813f66b5a33193257f9ab584d66c9ada5f9d889d2c749911961056068cdcff3f64d9e8cc3156871d7f9aec08ac4b915e05081142ab8e2e0e34d773cbbb2563776a2d556fd14293c51a45050053e0e565da8821b590a309002d33d054fee2099abaee64f41c639fa8519f04f2af69e94bc35f79be90b2fb86d3e49775f94a5fd020948bbb6e94bba6b130575b8a4181f7d21cb282803a2000c4e6fec84822e200a5c40810e68ab1ab7a9f94f45560a4aa120963b41175bf3536da14e8025db7a7402f3043cdc411dc67d0eebaf95f59a000a9ddfa4f7477dd2d726686282d7182eeea0105da90ef217d36876c736d12e36bab68946ffb5ceae667b927e8933cd78cc15a8a9ee891be9ad47b2314b94690c0fea45184eb4edb359b4ef8ff99f4a8cd39f715bb4aa39247daba9c5dc79c98b6ba2786daea8ac24598b8086e0742d4d262510aa620964eebeab64dcf38b98045f3e93600b09acdc5d24c1d38bbabf24d8afbb30239af5731760bad0d28555fe2e96bae0e1fe65ade6bfe55975cb8c008cfb08b4e4fcb627c57104563e02280e4ea19d23a05c98a917d7b8d862b4545c70294b9cf7e5e24a73b1742d171db88b65697163d1bed6ac4dff59cac8d6fc385db72ec26df39176e417e92656b58988ea9eb8a326b76bcd5317e97dada88b767488c6775234be13b3a888f2107d3e3536ad92ca7a5e261dde028a83fb9e1889893a6d2104d22d5c5bd229045b3c155408acf8ef6a47ac75a578e72cd541b6a558539eafb2e52bd121780a818e0641190777eea9431f045fdc1d04c11512d357c1d9168260090431777721e47fb6edfbb7e6f29b545b315b4889e1e2ee2ee674b0aca3bed5eac92b46480c8f30661c9c42b38501e3e0145a182e9fc36c5dcb8bb1a658873922fb8019777087fc2a5bca3e00c507761f28f2b953bf4e481615511e0f78d95afc8defae2259e4443a15116d6d97904f4a95f5af2d2fd3d2fd72684849fc7c9a9f71074220eb801607ad9e3c6fb696abda92dbeafee6d3a46107ae48b27680d6010ada2ade370443c6419e5d597521982de5be38ac76a834d0c936ba20059942cc8c80000000000063110030382c188e884452c96ce53c2a0f14800378b8648a5a1d4aa3284721841c22600c00000000000000300400a242a4947caf40a86d2d8a2043f4b6a65378d87ae6a6ab230be45da167914bc20d90f0fec128a4574c60adf3063630b74488254ef812e75ecd5d5912a184e0648dbd6c3a38acf26b630308eb6bd7a1b8759bb6511b2594fd8a19c0b0000519e059de9ac31adf358cecc69c2ca94390fedc361ae99846f4121ce68c205e49502cc3391f58f1866b308eb459f613e311ddc805947da5aba496b6ebf0a33843510343f446049aa4326a37e516b78a1e10e1b8d56f126b02e0f6f4502bde60d6db12fac33a28bb704ee5c358c1c79eb3122a76b7c3ac0a80f35006247bbf75d9d4d9ae60cf90a1b4e92da41532db3e92ec2e17e59c8793fd12b814dc5f8a4feb9cc241d3138a2d6b1d98a6751c58e8d83b211683f54b50a83abdd03487dab59ca6832611fe9253075d680fc6e530f061ae390ee16ed95e5c56b71c6679a68199ef410a3c22eff800bd26f830494dd13e1fb318e84a597c1b6294b06dedb353990559984e9193d908970d06274a08dff4b3464ff42814dd06f2e95a850daa3b049bb9f7cab7ed688643db4b160c5fb7c2aa475804f0dceebf899b725ca23c97c835c4ec81fc80a617131e0359ff1ec43518e74e4abfa25a798d169c63b71bea6a0876f326f9594ff3460bf10deb64836bb63b129802c076e7ba8e20dc3ec319378ef2e82fab4ab6a65f629b495bf8f5b25e37cfa1b639076c8f09dff532b8e291b1e173eca8fb1e39dab46ccd9c321f1a5771164460179178c07e4cfc10e26c471bab3b007186501b202e8305fcccc20c68da954a976833cd217396075c089c848db8ccd07f53bc1e67e884cdf247d3d8376877c427e14baaecd9b47b570099bef7a97a8b71f345d6ff6dedfd69608bac7a8e2e39a75c728ec1ac7a7b5efc062acc8db73a68901674e3b1c2303144902400b5ddae592808188c4b9385dc0e9797ed9e89cc7337c8e7149b71fd5caf1a93b1deace9cbebd8df990ec661348e112ce0dbfa729e605060520b4cfe2ac85043b846e8bf5f689bb61bcba1a6138773228c4269ad81d8021dd7e0c2a08b4cd02ec0e2616ccc1b135ccdd3e57f53455a5baa2de4132e39380223eb18fdb65e909703dfc183a6a49e9d9d17d0c8a3f7924b6d09db6e7630e4103bc24585a948edac13c5b658aefe7def5b4b30cacceee55be5d96d763a4f2ca8fc11c7b2fa04b0d8036d77af9742c5271643eaed4ba91c03b3f0ff203956c2c0465a52da89445dc7c5cf39524b200768bbade85da51570a4da5e690d1156f16eff5323b2821b40489d8796938ad50dd605b1b1c581101e228b80bf78aa31fb0903e1960bbd57fd23ae16558def81a29dd2a3f97988823fd9d93d8e05c0ea3c3766f895c62c05784c91dc311fd4c53d5887e521502c6a5257c3793f0aa6a0d0771afeb24396cc5976f2b0099b52dc96ea8fcb818efea56f1c79d3cae5460ba2ef5085613c2116857d801d376db11764f84c98af92aa703011998ef3e7d0e009d5c0a348a2cbba4c89f4d1fad04f9a916045dae11a1fd29dd72094e0134e4a8be219c8bb6fde59f12758f78cb5e5692595101858adf0b29f5b7206778829db3257231a5d5e6cc4becb375c5cd6fee1cb40d98f205830aeb4c90208cf9cd3d41296dfe3269a7ed43d9b47782c2d031e92e5711d4cd5fc05e1ad14bd89bd43c0540555f855e0ed25e486a8a13893485030163ca7033f2578413b7f4194dca3173e90073acb74d78c47a50a60bcb16a02c4820a57ee3455b62a77378bf4df520be0468ced6d5b26be746edbb78918095016c046dc3f54e77fd82da3269abee40bb2a9afce3831b2ba014604c479f35cc441aaff21a35f3a7a2150c2b2c2007cc1a69910bef48be35cc274f3a59168280125d3437ac4ec80d185d8629d809d330c900a9c0f6e4596ac0d679a9adeb286623429188955f92033952c5df7746cee6d628bc82a612f27937147092c34bd1c0d48aa52758e8eeebd70e4bab2964cf61dd364ad4c38b38817ad349b64bc2af5279c71dd3d1b8e8ffbe65eb6ca39c993c7ba4ae14d8a4dcb1126b26859ec199d9119bf2112890f40d19ec71aaa58251a71b934d590ed45259536a8ceaaf8b4bf1a4fedaa785bd60c635a6b1a6c2c3cb8db5c10eb98e5d8785ca2e84455b7b7e9eea30a9f7296be652b83a765f95b390d86124a8c9142fcdb0c3c12f07fcc2043bab2101317b31fe0e91348d0b770b41cd71cb5c5d9188fa588e9a58f38d1ddfa851a9077756ab35253721e84df476e823d661c2132f25ebafd9629be55ddfdb1e7f8be611865660fe2e7df10d065dd7618c1c5c930a783cee06adf3fa7d7f1baf84146f08548906291fb79e4266d61c78811dc5ade2fa391ce8699d45021ae73718489538dac4004aa9fec4dd61a323fdb1fd206539adeac18d501629c7e892bd82575f2a3c11f8703f0f08e63fa4c6c2e116b11e7dd3c56741022753ada8a238e3c54ac8cf1cf522107fc17c1b51fcddf69f0836fb89fdc09b948c152ffc88dabf6e876e2497655e9c4ebbcaba3d50a49cba5039dbb113eca4c5569501fd4f68b77157c65cef1949e3b9d6e1fb3c359024819fdf5975525d37963bff51d4e410386ec092f129a71c65929372d94f1a6ec5c61eff688db14718766861564693771c893cc7806c963914652c75265bb90cc146b0b63a37fa91b627a17bf5b14f93459ee97ff4f7bab8826ff5d3edc58ece21718bb3be5f5f608a60abd0b8e2019731c17422e6e0022f052c9487d9540e3ca6c37d560bee41a03887a871e40dad42a752b4eaebbb91367be22c87380f22b28f32a229d7e103d39c97a811d144ba498074752e13688fd97b1ca330f1e5d7b06f84362c1fc061a306b711008a1d081c5b786ff24192c106b857ab43c529df37559f7955acd45cf2fb50f54bdf6ebdfa4dac735e98957a5ada8b2bdbce8fd8a43595068f393a1f30486bf9156deb148575bcf944788b9aaaef8ae3a2cab9efba10e6c7d0ef63e15c0ec7d0cf72a7c91dc38bdee0b4f08641aed518815c0485f27fd12b920b7e8ac7658c52c84e32bcc9e6e6669458b63dd922588c0c0429805348d512a87112433df46891ffeef80cd400752384490554874fb10204f9e9b4c7d81b6ee5b1c3837047a3010da714faf243f281d1af4f3cd0a3f8ee0eca5842b4e7a2be32329b77a2db0e5db2da37e0fa71ca73f95987f3318ac78dd945b1e6a605ef2a71fb72bad2ea8491a4c0030b0342209cb98a6b1d32987752785fdaf9160d10eaf620f97cae3a7473f813cdf03255a9fae097840f7681973540fd70f951540b073d055282b06d4acf880c9106af8aec4b237ad23da49b8571ce976ad74f22303576cebbea854c77967f862cffc6a9e425b9d246652b0211d61c00ad1e7ee04a2656291f7f66669d1b66575703153459574cbbaff7393d7039e0f0f93d40234dfc850a84d597c1852271fb56a3e185dabd41971ebdab0d1a5d3006faa02545a3fc37ba23b5359cc672103a05bfe4adb3127747d2ba6dbb33269086b4a5e050024aa7b5bdfbc904eb13019e5ce306a8428701846e95ebe7dc17628953881340b5568b1cb43b6f89a6ad15c5afed95104643d886147181fed4dabcd61e1c7740c1ba0df2b435c78133a0c80f3ca024864cc57e03ec7404da9b63242eab11998c4aed9e061e48668539ad4d3dee42fc8e7444df14cc0365ae61983d48369e078a9cec5f8f7943959e37233584eacee5cb0612f41a7b36ab6dff11a5c061532a51c9dffd358addab4b8e482b19df67eae8f6c6c8d2b0dbf0330966a124fa037b000dcf857c0b169c9a6184be227ab0bc75601f66e6908ea428f4d6c30047074a031b7775eca8a30bf4bb8b33211ae16ed10afa6602435f94bc10105711f455d1e9c39ed9abd7ab2ff86d629566f38c1a9c4cae5ab1ddd6b041b7bee8dc75c53ac0e3bdd6cd5d68ac00f6ec0a561ba8888eedb9104f4bb7cd74e4293d9c2ecc8b9315f683e7bc1a26b8bcccd0d03554317677d41fe03ca55fe46006773f83f9aca88586beaa61197767873dbd7177f53b99fdfb957961bf7fa5da0aa61d18082791bf8e3d275b8e91f8d713bf7377d0905605cfecdffd9de5298f0e9fdf63af6c11a8430345f983e41df7ef66c22fdd39f7928ad88a1dc4f3d9390889835457781ccca3e0ca4790fa88cdc06b981686831e0b11d3f2ed1f6b1496be4de62b0398ae49c4944ef23072933b600461acaaabfa661a2638cb0692687a3ed1929758fc5e194c46224d742788c53d3025c8c1c987132e4614e8e841abad5cae229bcc6e83c0c3011d5d617ac89d63387b1ee49ff10853f1861389c51d8267d7b7835fe182a8697b2ba3203dd8daae61def1ab272dd3e5e083eb80c4c948132c03c87981af1cd2bb333fdc7c7e3eeb8758cc377458d2bafee040e6e9f72243571e4df6ae67ac3808bb458826bab29a030f9c08db6b220c5334729b7d3fa19595077af673fbf22da884789db79912f9be1771aed99666e34b0bb7203973093ba55ff2a76109cc623041f32b8bce392ebeec21f78231d93a6e8ececa2beb88c37fc33b9326559da768108b3c7d4463d404458cb8b179c1f1cd6cad1269bf6b1b78302a0035c2746ec9c2b84f1c0bcdda1b7a90f33c957db52d18a8881947e810f7733c16ea5dfa666ecf35f514eb71655a53561da5535dbcd786f13d81129ee297e7e13f9b57d818ccc2609b706f300efbd65e7b8d7cbdfaacb5f4ff0e25e64962ffc12e8ad326bbbabd8ad2440de7bc007b9d6846731ce81c74576f093ad6757b98b7fbcd0928270b6b46c9f06b073f8102c52853f4b665536e789bafe9f0b254e0cdc4b431eec6fe74e8f8766c635a775b0e55eeeccff9b77274e1c29070b43a2d1bf5cb1ca596b77772d0aa432bcbc6388e1bf43bbca732cd87b9d9d552e1a63c1f744b5da9a8002a705dec7b70e33d4dcdd8f45bbda411cc5dac33cb47d351f99830b3fbe39c01237652f69e9d0fb08f51a8a01128a0f4a0e4198d644147f4a409e0abb3a0db11550503d6a1a5ce7d034e89bcbbe8139f5ba4f32a7562bcfb4f3e7c9905c91f20fcf09bad8e7e0818e220c03ea3bc7d42b3ddec5caa3c8195aa034e466f2bd6b21e89c37949ad37e4c53f5c8e8060839dbb832a15ad2451bd88c9e3d5d669f0c9db6af0c5d27c002cba277174f82c914f9cadc28c33b03d47b04992f64c8a10b0e5c60c124eb171b00d2c4732c7519fdd920fac3e8d8c2b6f4000a9aed34add05a177be3f8c3e6944187178f29544741693a8ce49f3521e41b21d8e983d89046f60e0824651f4748e2649d701c1d7e736aa3b1a758bf6dd52e39c736c568895c92b18a602b2bd16c595de76a732372757d1be5904b8bf0b076067f30b3c1b7ce7d5dbaf272bde986229f34f57be5e1b441cf445b629adf841150e3c76418e2119321c0467506763dbe65d4f4f769f47593552050e74541aff423c1186959532ed21684eab88b69ba02a4bcc0e8f71df1dd1220675dfcb1758953b576e0deef09448359776d1a438745480f541290a86cc6456648ef86def693ae25c5ef9f28fcd1585b4d15530d86ba46b21f6e2db6f1f9241bad1ca091776364b80cd3c3d136e53c3d125112b7d0a227819ec31771f2f46ff874703d48329fbcbbf59142846ca62a808df258f56f08f3d449d4d4aedb12519f13ecf86ed2a6b2c3b8b9eb2223e0922d0d9933b53c2c4966eec2a3f3d3ce556fb95adcadb8b6d4e6e625bc33ffc195fb3f6be70bb3f7987cf2cea58d388ae4bc86eac3ce6321f06b47589364a996e240379e5e39481ff65aae11af122f17e7ac0dbe92c3156d1d52161ae418e7f2a64acf04a36449bb7b7003b3bd9ffbec2274cc14f5b466cf4833e4b19f43710601a118ce014c69edcad134a065077ae871340c61d6a7d47cd2f6f5aefc9a2c0d0b98a1bb5b257b11915fb0fdd9396279269c11867a848e97a6e9de433ed42110bd2974fbce69d7a55581109f2a1f9f4abcd300a53d59a726c8c7bab233ca6cae15bd4e268745437dd7cc8f1c2d1c868c50f20da96f1c5e47aa5991093fc29e409b57944f9fd391905f156e708a754305bc733ab84c6dcdd1e66d602b3107c07a40e4959ccf0407fc8cb919a63d3084f4c1e2a1e5cb733635f665ce434f20da115039d6c300add5cca807edb4b41dba7b877badde1c9b9c32d571e8dd8f875e7a14cfdf59a6d6ac3868d78cb63a9ffec218b23f3a87afec8d2821dedef07c93b0338a8c8d474741b7785baf0b496419b71e012f78fd822de46bbaa05d8efed91fc8fed3f90195413ec38f5121657486f57a86d636152d4720ba5a73f01adeea57f121c30d408529982182daa57b3a514c027c17f25af6dcd3cd6f0185370d72526f689b6f80f809d5da38a0de6471f6136753db13d63a4f9c7d1cb4aa9c99b3862c5c025cd12f3b409c60978f9f48d4c3079bdcfca93dd2568c3c381f8bf0c19d7aeb4dd943cc1c636ff0afbd3e6476c3f8ef026ef2f8a13f7a7fa54892bcfba608702c13a1bf4a0d9013f7a27e86d9adac116b4a59961d9935d9ebbad15d548e18f453dff263e4e27e3669d8d27e6fa980f5852161874f74d9ba83959a56112fc5f3df51cce1a90c0b211e10b5d65c860e130959c066089eccef095cf6167d39b4dfbf90b7bdc5bbfec34c0ada482cfdde98cd8d885114cdfdf1fac9488c781beaf892bbc8b1f50c9e66fb6c85b78c17a9aad0948d4ec112e1e72d920db1cc08640e10c9c9d7c8e1a276906a511cae32395f938fdc37038a2b9ba94e883f2f65a561fc24e080aacfb93594334cabcb3c5b2701ff148dcc017807f5822a5902eb68a53e19fe7095f5c79146e2d2d59d416a0e69a6e29c329e3da3e893b8c851352206d2c97c92388b93cd76e761ae9af4f83628544221c439e386baa3d4636c73064f501bf7733ab507265dc3038f40c2585c1e7de58d865bc35ce3c2ca7d79bcc3b7ed21a4204fef324bdd53061c47f908c527db3281ccefb06f1aaca3d0e0680096e05ae573d98cb0677e0bfb5ddb97fd09c66bd4a27b3cc2acd51f070480f228b79e8b9bb33fa55d0103f7543301dee3f0e9da248a7c544bdaebc253f435d7db3a2a468724e063714f7094cf9395d0bbb38163e7449b6f4110f2447f4f3650bb652327150a2f744c4a26cc48fb3f5512045bdc93d47482dd0a8bd0dce7a1a6d301ddbc98165881e068e059bb0402e80dc8ffbb264f36d700018011602214ef9099ee221c5cb15614a23904e33e58c2e71af7e7d8ba5589de18b8dd3318e244ed3e5c3ddf9384c341fbb0494f0b6f1dc91d39928ca0188bf8f570d4f3302bfa3c12d0e0738717689505cb4f116630be4f3df61b0fbfdd0ffb23f3b089a246c5c54a5317fea31e76edcc1ad7d4acc6046f2577b37d8ca36c78057d5ae76005f85317d41810d2fc2472ed8b0be076a17b2c8bbbb85e840cb108407fe464cf376d3a6ff154f6d09bb487026f4c64e896fa80a33685caae3c173b8acc32df56ac0dde4a92828fb2a297e1aad6d060e89f7a919b0e6e35843f6b334f760b0547649e06a1e7d66ea0cc25b429cd74d7789aa1f7dabdd2fe73c76e5883af0a43ce030d35711d89501a67f330c547fac623cbdf874df7396aa2832595ccb0e828639cbf006b8cedbe6d72dea9a83ee7499b41b73936b03040e117e601daadcb23909c0f9c594d8ad848ee7e9e12e0d6a0472584f645207ac025778e212d31e7ea41ef20e53526de45cdffe5344f494272811dea996e1fa15d711587a95d4c01f268527d51565b62985526682188db3064aac5fb520001ffbfcaf43123bdd1eb3fa1877ea3a4b57e0015e61d940dbc14427ad6947d331e2cc84cccd839b272eba7c8f2e5849afd1395190c634fd5ce501e52af7d2762f8eda94841ec9e644de3c17245438f212f46de4878d6a47c4b1707a140253a99de3ae24e7142fa825b4542ee9bbe07944055cd39800fbd73f98c39611fccc2c89a9975ca328858d57ea4da7dfac70f90bbe85852927ea01c991696012854d5817db5949a721e40603bfa642089c3a27ac885edb592b29d04cce8d4531bc108ed86036f166005896be32bf30721d1708010d637e86c6e18401d4515bf3d72009118fbd3a8d6f1b34875f87d39885b43317b46906ecdc5d82cd376ff18e79d23bf8c0480528d559b1ff825acad3fa56338be42ea7ef957ef5d50979aa42180976c018a85ebd14cb98c25f129a147518580ab1f67ce95edbd3a487933a539024526d54b35080dc851b0ec2da21110bbb66db5b9695b48baad9225f0c6fb40623cd8a4fc8bb2fe0d4ae854ebda765c1009bb0ced6ad4a4af517467144081a18cb01c1b24dda0c95be9e074182ff10517732e2bdc9be1552e2a509f02644d1e7033047aabb27fe79711c4ed05c01809cd931bf223c0d5e75c32e31464cdb312d00a40777f1ce170302e507b278eceb9f710f45b450a450dbcc6e283d552fea1c771aff5e06c532600a56e5c033990e752e48e4baf863b796591116bbed0def1e681c4e922bf3890fbce60df408df46626415d5bb3df03019c52b434726e31ade970cad7df969b360c21a987575a15b5e651ad366072a81b8be6b308c086a8d3c6d9e9bb707c8b201fa1bc5f117c2c0f8f82bb872d93c6d30eb7c1d6bcc209a5dda1de6f25323aac4baa2dbe13162319811d1705e3d7729f3e2f3e50ac6e1e44ef00ebdc9c953afc331a51ceb62027e89f82bc31c3fab9a0d6e9606463181e741844a940ddd0eeff499c43b83a5554b978349f790780c1651b33894ac840b96109afb7448a9612e6a16d6e5d521c8df41a063621ccedf6ca4a64485f858db658f074bee25a4d6f3e29a8434e5d8382da74427b05307b4ed882090466bb06cd5c2ff92167159b21beb201bea790465a267eec2c9cfa426629c7997ae8c0aff2b80064a519c7afa7d92c43ecf5197ee145a887571600db7691c5f56189232bc2269480802a6b3b1a48ed9aa17a3b8a9457738639e5c9e8dc34f4380f253803b9beb9a121a00c7dcaede2e876b22bca907107bc36ee30b867cf2517ab9e109bffaac8bee2bb2843032b01a0a6d3b5123f7c19cdf742fb7d3136a7e975771b522bd8a2ebf8b11a68eb023530b6fd5873e3a348be2b2f896ad0e370539579cf1578bac83ce79c50b59fa56364b3c7eaccbfd3010467254f526d8155a86057d6a13f0400f7b8ce5428b0affccd01107c514f9d0588423cc6c8d22aa64f04cf27a27bdcfa3ed2fe1eace28ce3216df3f4e650434553db643170df77e5392cd48ce4fdc94a21eb3b321358bf685bed0cd7b81d74a8da70ff699617767d7979ec5e36865682c73a5b683b7d0e827898033ef077df037a8bf4e4e824bfe90e4a147bd7ec4bc6926dd63e498fa46745a90e769b25dd7f66a466055046d127c08ec1566fcea300cff48e704ee09a466303ae4f2af9047b51cbda7516f6c0ac23bc5e289caaa3d649e8a4670632bcff035603b8ae04fc8741b62ee8361fec684e19c2fa8a240b283fe197358432648da5d3373ccc55f8c6e73b9177ac1c04470c65aad5ff96f2dc8335f1f8f9aeb9299b4e4a4ce64f276625947f3d18c24159b35b9955a5ed7e1ec62472bf1cb6854a46ab03cd57399162c5ec92c6da4bcd7e695432a7134f9b682dd8c78988a7019506121333fa12b6609100f476cd58d30a9c5f4f0eff726d2338c30aa08cfd3554deff7d3ecef41f470629101fa0ae97f54ecd8790cb9a1c125ab877da0c89c7b6082f1066ab46c464b1de83da9bd1c8a29fd582119d7b7d400a67165a607ac985b96471816518c44df2467f7961614316c38a54ea8e170f88d1ee2714ff3bf0247d6a03668e5669358f0f337edbfdabf52c06c624df51be78aff0dc46c06fe90bff90f32c1d7a569835c2ac33ed9ed3d7f382fe9087589f270f575c3ff0a2ee527dad7c21e08274d40ed3b77bba23909abf131929669321984cf33702e76b8375f68ad92314508aa7a595aed7dba1eb8a24fc8363ecd8b1286d422d19f7cd38c7938e5a693e421667163847cd32700c8f041c7997b247d45b35548791484a6bd7086ce7b6eeeb7a2e868a0700afefbd2f03fe2688f77505190ebf17a71f12a0969a82f3050ecfb6c9b20e116cdb65d199953d3d597aa66e76cab7ad4cb2873f5754d71bdb02ebbc44e97d2cc41829c5ad3d66dcc992671122fff43c905152448a7434b68e3e8d0c2a32a3f0e1a25dae6cbebf4f17e2318f788ddcc901be8c6cc31dc2c61ae7f1c22b79b2eb5fd7a191b7a63e5ae42fc328dac11e065ac87424ab73e0ae6cde0436bb82e8f6428ae809c2f97411f2595e3ee8dfe6605383c354c6d2e790f97701eab8cfd45faa4136a7ae37abdb04e4ac467fce8dad7284458de002e4fb324fabb8b114d9e6eb01b9b0a2dd07c622bd4035a8cce0ffec55a2a581d2917faacd81b15de5fb0e023738c26e539bfcc648884543fbf12bc81690c45cb0ca6255493297a1d922952ee5bbdd5b5087771e2bd87a13bbb8588efe1f586d97a10e1ac48ef5b4fe451a34c937e70b516dc4ddc43d537c4a653f8747eacc75f64f84dc7f9a64e6875234643119e182dbbf30468d9a24e37324084af9f5763b95039649e4081d3d43c90c95a2c36a0eb9669ca8b06c4720a7345be1140fb03ac95cea190ac984975dec607138d091c1f92bbce8b2d226c69e61b043c5a0a17eec7615922bef7dc068abf9dba04067dd9ad839d9505694adceceae320ef3cc92f6726cca5d250502f1c0ffd901adddb4eab0a965a253d4c99c695a0a5def568d49cfd1a3685bb80bb835577530a56894d0f30db48bc4beca12657833435783c6b4d0484f2b26f8c69bc9955830b8facfde87ca3771a03652500227f57d9333bf4fece04a1659c3e832c35e24b07059f20d7928e5e95013b67870c8f7ea94671c69e3c0cdda93c6fd28fa964890d130113c4f771e115ae0263d8a7dd3cd59ab82f9965a36c7480d74312d7fbcdc3a1772a8a72f0c1254cc9ba00d7bd0ded67e2e2e3c41fe5b9248992ce3f52d69a86f05218182d3f90184cab7f313ba984114db776136cf369e090501044c9daf8c131a9f79413e41c0b4a32e1251d94638ba54389035cc4ac2c4ea4363fc212bd797038139576c395525f7c1a9b878680eed45c687c8aaaea3b5c89f94e972f79198c4eca065c00aa2e6e51f5639317853d5d67fffce91edb6265a1efd9649c3e11ca76ecfc37f117cc43922d129a29d10e988eea9de6e40fc5c922d6524170ac23c5ccb687b5f0c0f9012028bc6dc0d6585c73d0be3906fd2eca7797b60c3ad3f96b2a2affe680e4a9ac8c25457062c11f3fa9e448fc1e69bea68c62951b922296f238077165aa99f50f424292069356eba6aa2b5f725c6130e8d3d040a4e9d51f7f535441cdbd195c8e8379c602291c50c86d4bc4c1f6b6b88696c534f9e9005da6da789c7528f5dd0f2984aa05d8a0d73bd6abe224d080c41e8d6fa150c2eb3322fad9a60fc38a928dd2d91c2fd7d38232628acae57ef00af9644a3e5f2ad37ecd5d0e8227f9afde36da36dcff574d3a662d464c76e4ce08d6f149fa21c4ce38463ed6f5d2510cada4f13bb847e0ece7de41c46a0e419c01275fa6c3bffa4559a3f8ae5a9a51a1d929c467c2c2f98bd1accbcce4899f3cb893471c299c95ef35b4d84b7b9c4d995880824df49977c711ec01fe7eec9cce08987ab829b428779bc54939ab9cfc931218dc8d8479cba05e4aae9feb0a2d43be5979a45f2dd46c0b5a841be90c74c7108802294f1d104489cfbe06856d4af3488eb80e33b48a6c096189abb6328fd0de0933bc3bc0a1c627fc15113081382790b2f1c59db9169195b4e184907b08016390c011bc4be5f45ae496dab5210e30a5b53667f496d9b9f595f803cd37bab23cb20a9c76c8173f67a1ef12fbe7b479959eeece5c7378dc4d5eaf3db731bb135e9783b1982f5a7c676a71f23064805eb09ed1ab79ad9e8fafa9ba5fc2e8b4ac1c25044b4f5b639ebb54a2152237c0cb5b156748f6d358869214ef9da89d2980776018c318527fd0566c1f205712fd0bbe1d75214bf867528079c5b603dac8f6aa00d2b4ed5bd9194b9d213cd4141c8427b7cb4fa55a48dacba6d580cd6622a5d0af88a1837efbfcdbc62e4d0385b6576dac1b6a0566262d0691dd2af6c71cf91cc60057a27fd2b9d420495dd55180b45c6d837aac4a4b4a33e230dbeac78e746795e187ba3384772bb979e63bd98ba0b9e854446ff6b8a60cf78a2650af325dfc19aac5d96a6f8df0fc475027c5baab491cbc6952172c4ac426e3bd4a46e7055d7c6d08ec22ea7a008ee2ebb05178188100d9f7754990745bb40a1deadd69595f6eb210087e423fdf104b2e486df6737d805fbf08a2b9862ef8753de4cdcf71b14e37464edf88e61f25d911d69d3c2c92065f9a3f641904ceb6ee2821484cbad6178f3a8678271fe6cad4afc18106c4cf1c609d116091131a2fd83076511e91faab225cae47e35af0f62b4c9f5a42e09da06cbe7daecff10da2d01e812ea8cdee8726213b3d2b67e2d956d7b37cc8a027047a2f18849379777987e02b18964b85565896c8c30da08d366feaadd48ffef809e492f090c5c3a0e44c030b91736ec19c4b5a217eab6854e709c84589dd995d770a359897ae1c47fe8f96df739cf7c275f16c9d643d7b0e1a5983f034fe1c550680b3a8598610cd89584b6882e21d28a389c7741741d66792d34d4b3b5bc7ec50b1bb762784b28f94ee7341cecf42c9d6f33e9813038598088a956f14d5ed10e43ef7e327a2968a9b629103952a45300117a261e1cffc2734382a0bcceef5092e767b13a1c935ddb236f191fe52547fc867d04b258f87a27a8eb2bfe3cfa91d19b208bcc5300364472f95b9c835b21ed891f3aeb827c55dd7829e64e0bee43ac06106a824e861e64943ba7b3c3c4636921811e5ce6ff44f74a8ed4844bb48a882e7abe0dc3ab7efd833c0496cd17178a91031030d778766df72af88ca75ab851d099c8deac514d87c3bbf2329a8bfb8e70e9b3be7eeedcc3b9c99737f344243beddad2ea823f69c577db5e7c74bfe922ed431870ad63a5b7739096d1b89d3c6e8e7f8c14cac4221f785162992e74ffead22b75498c2e406a2bf5b94461cd252f0285034622fbe7b80c44fe51e66015ebb263eb5e854990315095f9f6ccb2a980e43bf8a63b6e33a3c3c6654bf7abb9113bfb7f684c3e0f9066bd87c936c22d64e45e88e3f93d955d0193af801e7a5d156e3cf61e8ecfffd1a004553267e96d0cda0d4369a0e886f335979a755db742cb7dc01c0cee55adc85b9a773a915364889adf5ca82ba23984b34ddcb278218b90b1ceeae397c1297305ce932ad9ceaba2ad0401ed72958b64eb1053dd6eb04d836e10b82487940fd75194b491b6f16c74a570966cda157354347a39ee92d5b9a79995276389674903fc709c8f865115e5fe9516978d53eff389632e4bf49aaec5097423db3882381543da126a35acaed80f2d9dd51f36d5974e29734f29b34181c0166bd0e43c95d45cbebb743ddacf9d28be38e9a7ec2830bb613cd9e34d56569293aa183972a6bcd3f021261df9151103c869f90ca875caba1336783901cb2560159b0016c6552b934da25897afe619efd2656982933808106b07018174e3f3c1ef361db773e4c6694679831e29ca3dbb301834ce6d4d1f42a4af463ce245a0af0227d7c1c95ac60611ae0894963484e7b4b57c9d5680ff30d1424904922b6ddfd0fd4ea8fd9abba60b208ba44a3b1288be51600162f66396dc8938fe58053dfd130972d174d0eae726d252cb38a41b9424f99547363d00d548f1cc8a60033384ccdcdf74baddac3bef59f188c7b3a29a296319692d16d62a7f873faf974a903886505154c41e1bf8a2d0b9f21efab68ead8c1af60e8a3fa642cf25757cf1cf2ee1a32db8071788ee97714dc55b42adcd0f301fb3303e7dd8a2c99ecb480d004fe006cbd91059c124d55865760d0aa7226b1dfaf37f1b275b6455a17adb433ae2b22aecc9d6b403816dc58e1fb0563616b8f5acab03c3202c93ad2c20ffec73ee24cdd802ff3921ca8ebf47e457ac471576416e3b6791a11c62f659f61711bce0941502d32ff0d741211fd1ca45e472417a30b848f341332fba21cc02edbe467ce92ebb142c0182290fe5aebb030eee4ac3cf1c759148df6afa8a147bd79be701ca45c9339b7460c06cd3ea136f97403c12e2801512e90a020252e1518aa73b708c9190294a181d40d805854e1e9eb83735daf62cf915788c5313fd849094a02d4532d3a36c800a9ee504bd1ba4c36bc651386ac3109e3eb85e7cf4ce58c2951ade425647db4115c88cceab18b2dfff6e4a11c3cc358fbca4db9624fb3855e0037e736764da3cc183b1092f880d22030300b6d35afb6619393df9f8be098e1adbdb5480963234771918ac70ee52925570404e5306324044b28a713560a37ec7c0ecb32628ecdc809b3c2cf279e63aee920c8b73a60ed72161239d8dc6526bd9aeb4e1b0342e9d9df84d4bfbba94f68d4a326147c926005522cb1b34c2210730d58490de128d9b6057d27309a58ff01f308f47b927e81f10564d2d41b2cb02a8105f0d59dbae86e33811aa3dc5994ed06d838e9d94ad86224b7e810be2948c4f75eda32ca72d319217135d0f965aa8cca688a48c28ff5cd83c2a93507be2b7cf9f49f8940fba268e6b102a6050ec1e40e923c7e07a8e86d0ca54864af59af82e41d0469b84412517ef30f865f82cede49b95c2556dbf10f707e80cc42946aefe1a1826bc0fea3803e80ea318eb9d4ba567812963a11c3acf437b6aa4402e339136503059f9da027e26dd3268c8a941e130b7a009f36274a0747089cc7b3837748346a563c49e88ae4b0b363916d2b457e8f66e9e201b27cf8f2614c2ee88989a9c5c0cf264f7a6f663ee23d10af694ef40688b4526ddeb01674569a6830c60574a826e18b318accf052226b55bccaaf97f4713454efe5cf335e96e348cf05016c1cdf151f2ede23a2b9373c252ea1d94dad157f27e3a960a95144f11ee985bdc38e81e863ef15655b462d79c41e8d6406e4c31ced791e225c2f6b828bd61cfb4108e7a2c3838846284ed6aec879221c8c1de2f01c7334e229321c9119c54f90f0eaedcc6aabcaacb71d99e42f583b325f214f4f335a56150528a4cdb63e3f86494ae8c0ecbe3823ab4ea6e61a849e2fc22ce7abdc727d7423e56ea37982e82d03fa482993afd7d4e84fea21572654f4a04bdf44b83685486fe4438192527c49575243c8e4a271724868fc6e78a907be3ced294299323a8e85b00e5d9b3cc34fb60131cde12913b76814d278bc790cfd2d7af9d129ce9c71d285556781b01dc869afc7662fece00fd1aaf6887e5401546acc6c2e7cdd1eeca5543187c80475b950541f4566b9203d3e70e8f65cebc6de05b9fad8c679e355e65b24c978b1e415aaa50e74017056bde602ae463405ef017c98927bc0ea9963926bec0ebeb5740056275f820e9f039ea3e0ab8d93c510f20c919d5795ee7e040a3da355f179e277f01d8206f78f0ba2b01ff61114b6a8eaa4bfb78288479de3d60ce1acfaf06d9ca30ff99a02da5919175d42c4b00e1277a7da043aafdf231076bf88f56e0f8523c61370fc752848c514822dbe46397847e3bc889d5ca0725ec6336070bd396d2558ee88c48fb85deab6c31767ef76b5b2b96c6dc17bc7f7e87fd2916144957da36e1c7b79c142af2bd07b53ebb3931e66ed4372911c6dd10e87b8a3514bcb04920df5d69db9a262f4f3be6ef06c010902ddc24e922e6f16e3175fe7c2c11bb32b38fe8f8e2a66e6a311e593b1a1013a6af94757fc2daf6e2534c3eb4114685107e11f7510f5986a5095a605e908cb319733f2323585ae3743fda7583b692379a275b9223ad0b8a2246d72e31c337d621256761ea68283a198d2fde9d58746bda697b3d7ce8c09dbf11ca83f77f2ba66f7a3eb1740d28777dad1330a279c805b4df5187b953e1180856930a760e5b026731272950c1f3628250f459fc295a69a664dfdbb699865218c0fc817dd2373debe6730f539d4e3b045ea907774b71e15d062129aca8d6011b12ffbf4ca3ecfed836037cf7bd0b55a9cb1bea5421d497be5a7222d992cbc4372b0b701b9c9750ac4f2ca4d5517722340a74337117afda1241ba760ea0c97f547fc07a29b235fc62d11e2727aede2fdd7b5121c4a2d43f13bfcae8ad2e3ca718e6c89c9807eba3013d1f94444eed820a52f530b75a71871dea862413a874e153c48fc1e99c0ff2aa1d463acfc9c2f19d70fd60d0f964f732cdf16a2917bec27d67a195bff9039f825834f4663f87427059bba2a81f656cfa692549e30330af31896b31d752e387958dc5f0c6befc665c94f69211eb33e5e878de96d5950f30981212e84cccf0df376bb9e5959ab332ecd8a549f019b51dc946a6c692db92c1ed4f6f59dfa0a95a1c3eee808258c9a370b940d1509b8fb490c8717f3abf3e2327c10bec8b33da0bed31458c8a8919db0be2b7628380272388a3e4961d52dd6daeeceb50e5ab49f79d401129fcd51735ace8ef3712a30f3a19a602ee00c53bf558488e0dfc84ca3d67437c516ca60dd5f64fc957cf4120efa6320c09e669cdcde41ada747e33e5cb00eb3bb602cdee9cdd735d64b61f1a16ffd49533e80093aa169e64b9733213906d70dc97cb0f435fc78cf5dd71413125e9f05c85312ac2ff33361b65675e3c5f5da1112dccbdc682880a43b2eb1ea5d8730ac208dc74f1745b1fe72944a88554617c123ddd8e25aa123f8e65e0bc16f53fc330edc1dd5a01f7793fc5601873bcb6a92b7e01c832ac4fafc3255b36a02a664679b90eddcddb9918586fb70b17314324dbe23ad371e754d8d935c3bbad892da3101e5a62c51ca5bd493861ad6da8f13956f4bbf424cc0ed0b1b3e7581d58a358e8281c4f1e28502fe5c1af35ce6525fbed66002e77dd342f30ea7b2343b75b117f9d6d4c0cbc110b7cceafa3cb90edc12105b8e023605938feb2813bdcb7c736c8bc3127ebc0de5ebe2bdf30f779736a5a4742bf411c9a1db1e0941969b86ee9b7d2491ab3692721052cfb16644bd81214e6b465d60253867594717509a5eb4a5bbcb8a2898ee3258dd7ba33d52121bf43e59a0d35d22936196cc87b80d1377e8c4d02b2e25b008b75a16ad1cc1bdd24d06cdeae2b3efc76b99e521bbf35398b2e21293ced4e8e969246fd20eaf330b0a8ce2988ef38ef80ef4bb6b7e6adc66754e1b285500999db0b0b64d1897ab707c9fb89900204e8c7e226059cd4b99cbbbc020770c658a2aeb3a44d22bc8b9ca37fe146602969c920f0b78e868b2c94dda81a1cf51c2dfae09cb67f4f59aceeaaa0959ac4be298a1c30453414effdf7c4895fab5caa1fc3b50481bba2156e3c4642dcc3a1056f778a20c998cd591ab57122b479ae389be45773e751278921b0c7a03847c0caedb9cba9a9b6297b45d510a3eec3699c6408e01bc262b26064006117e49c0f58def19f1e188c7873c53794e8d7cba6777961623dcab408f867913a8922cb26eaf57164a331146c3ce0bac416147a7513eed2eae34a4b1b89cace63d80fda90d5b3f284bd815f413cd25e28dee664af2d4a5cac71b7a7da49bfb04c253e8513faab650a1a58b1da293a9e897fd17a47bcbeb27db8c47ea902a183aae8369b54f03e9fdf256beccc0b6cba865ee98a0492254372aea82ab2221eea99315126cf4a7ccde232f082138acd0904eede0da47744098aef5087f58a5425d282da84aa3d90ed42545d11e04deb04418e1d4ceb933a34391fd670ef0c4ac8e192474b8a1874065f2b69ac49e8a4e51bbee100a3de1042dc68e1d6f7acffab1f6230c00ae7b255aab618880d1723361dd06598192d3dcfc46159b534847bc26a24688d30be649f786194a4bc8e425645a391ed6ea89d6e30469d984d62d85e29d2e8d5a4e3ef868f19db23eeb1c678dc1d7899d5d3669c7654c773348629d3817c3dade0e83b45aad581b092bbd62d1af6823738671a88446fec433fd51cf27c5dc89d2b063a4d399675e44856d76ee420c449b8587eb36364c02883a41176805bbc39c4e519e001f59a03f922df212a7550e2dd89a3b6d726679230f47d71b14f5b8b381d8cd36eddf3cda3418bf63985de4bbd896a6cfe27865a2002961353c74884d9c4be605e615759252879e3ba4583dbc00f643314fd3c07263373aa5c5429a1da3d3c5e0e779bd53dae9fa0de2002b1665a45d025035858ff6ea8218db969b6f560a833b42c36ec262397da4874919b4f4e395430aa28eedadf89659ed3e9761d0ee88010d7f626755a9d4e20cda396c6e1561663866b149415466514dd2a745824f6d8cab0b93869becc77cdeeb4ed8e51cf9d381edd83ac9ec7799f97f795acd7fc22a75ea6cc05ee46bc1cc6a39107be14725f65d172ebd6ab6963322117014e1edd882f5dce6f712c1e19dad33d791301148b90b91eaa0974c2a33ebe81ea38edbb259cefc228e8984fbfaadaef2368e7de99e35203fd36bf0ad0f75459d36cfe567b689d445bf4f152c75f1cf05b08b1c366b5b6e4e2297d88fd0f733dd030e2e0e31a30e821ca757d931528ffea8b389bff0f4a2f08d895422432099b71a61c42da13b5fcfb4fbceb3f940d907c835118b098ec265650db5f72965172672572da07254ed35983b01406deb4eb085231dcdfc90532b337e27f8ad4533e897177163fa341754b14925fd85370e10f009109eea04c1be8a4b23dda5f3e04121eb01dfd084285a569f8ea1222adee759bb0b02b09bffd8cec14436ea6400651d268f3ddc0ae7a8da36812900d8c9a16cc38c74faecc00ea4d04bcca9585099cbd663aee4f43bf37bdee31be7425517142b55a02b6fdca888e8b744d2c986bbdb426df3e33a42bcb3d73a056ed6e22cae8beb1b44153c0d4024038a57f97fbcd30c97ac01460f1e62f06e0803ad2a9e2ddd5646846697f252dc3ea73443b7a29ad0fd9c5814d30d9093827e8d9cc77563501f92631860157a197dc10d1e882067480a2eabd7c0eb79d5dad9cd8b2e281e2d55b929c8bcecbd16a78bcaacb0c2427de57469dbafea49ee20d4b529bc233ece2212efb76e5fc8b390cb487c46ef0204becc0bfd137b921a8619e9e2708dbd1b639f25b3da5b459356e8163ef550185d04c85f56a484977e39ed6ebd5de52a1dd2568acdbf2f9622915bb58cfe554c8500cc899b67dc916145acddebbf0dbb0b28d2eed199b9dea1ae89a7f2d1ec2a22b5f3facc8fb5eb81b6edfcb4c56e006ac743a31aacdfa9226005486188f45cffc5f9ba0d8fcb2716f51d130bc7d58109b7532d1f29afa0eb121d3f66fb713b901a59bcbc9ce7d0ccc520757613afa0ad043fdab712d09de8890edc0b43ca877b74400f08f0bdbf46521b84cfa52c644540a8a456484551e57422eda13c20e8847ab16da37bf52d49a71dcbe98c9bf6de9c085ac9b0e757a7c0510c4a956a6857b27237fdb7b68b96a26ca7ba9f8ae4197f0dabf41b6d4a3bcbd2178fd375ba250e216d703bef9b2225a7de448a87d6733bb2544c2b13ac4f64b0657ac69877c1e99ae01506aabfb90e830a02856e0145d94e55942110507e66faec8b8e77d5d7fcef048c87170f1d0267b889c7e085069214cffbf162c368c3651160b7e2298ace8cd6125f863a1bd28483136916e7cd36df4f22b1c952fef94317d6203acb6b055921b4cad80a04b4b39aab73de349f2e343ff0fe3ce6ea7d5d34bd5f87abcb45f15788cfbf5f957c38fd5e9d53d07cc48d0050f68c87c7ad62be9dccc4a7217370e32f1cb011d8dc7e7e0708fa1d80bc1b8bed3c4258e6d7629f1f707c16602ebf490da0bafd276b4d4d293fe3da73a247da58eb765c06bea946c7f3062835af06fab6e3cb853a1a724862bbc1b3e3e8385c413a71ea79d565e57cf85c43960fd7b5e80ab787fcf90b87372e5d62b44289b16e9ddc3981ead2b504b0eede61d888ad63b474273ae84b435557ffb7bd8e8291ef8b5674e719795c2c165f7d5e0c8e0f448a3eec77d2b4e1af0d281477fb4b7ea40faa1abda0fc9d4ff2033956ace05ff23dbfdcbd2839459fdabfa65b813ccadf80d04cd41f02fdec3ad058d2e0b3a303edcdf09c06f5510d4fe4ab615768ce84593fe0c9bb50efd45079bd9f0f148eb407ed208fdb96c5d7efa83928200cc8d0186515475af7cac5f2432dc9e42586eb34c29a61f8be6f4e957247e7d5d066f7c9019df3fd1386b300f54684f04f398431dbf532cb2cf7572c46161af3ff749880c047b207dc637ad2f8ecfdde8655adb922d0ff6362ad9598885d6bf9383a146b7b0659a12e1fc2789077015bd9e6b06dee2b64d4e3de5241ef990d23d45113602cdee3dcd247aa12e707c3ccf2377618317e54793546dac4938d56d40d57040b6e9b97d033860d010a1e6e26888fc6ddde77ab90ea14592b5fe7bce8bc00333a2427416d1981294b448185b19fd6c25355b5da16ebb4d02c3137b642c06f4ac2cbf473f2e0cc01448ab54477378e8955097f922ad30e019ac68dce94b292653ed98c78d9b4f1cf4b8431807c383f618555ca30e6782c84792354b584d188a269f0f993072b3e28a85e99a94335cc3a22540c85737be5a64e4e37f6ea09512027026a6a0aecb7b7c8a8a60df33ccc8dedec59e37d0b0beda473399431ce44179f1f2c1a3a04c4b7647f319499997b7bd3ffe3dd7dc7d9aa8241fc0a5b9dc48aa17ad7636e3647e2cb1b0e88831af7628b8f801bc8542bb4f1d7d55bd2efc6311ba22f2d4ed698272f6e480d510de988af7d41cc9cc2638926c4493eb97ad00909f68019b4ca84accfbfe3da88e6e497752ca73f884e788929d2aa8eeb84f564e14b82840ae9aa31634426fbe0272993523d87def0e84ce78bfddb93c10b0fa3c711999c7e4554489081840a2c62d9ac6303a7042d19c789de08ecc177d3e7cfcb72383a6dde326f3ea0c0c630689ee92ae330290d804497476f247a1d814958189e0e64be4a24d39731edd5526f4152bf824645b346193d13b93326096031a1d4b017b748a84c4fd9a69e1a112e2bfc0fb8096e95b4683b4ab170aeae100da16c1771685cef83aab16e146314d0336789a4360a5b48caafa334c03d7d8f7aa87c2997f05bbc89fdecc15ddc72a234cceef4907afa47d8778e7eceea02f5391b0406d0da1e1e01c4f3734e7e2fc9460aca5d74f80231d085617c5532da837739edc466179e83e5d0c002809de60445b0baee519f5c24a9b432fc2cc3e0b973937cdd212201d7a183bfc18f1bc67d6d5d17df556cbb0fb6861aeca3aee47008787690b49ea0dbe8002794f3b1094bb3ef49770ed30bccff559b203f8004d6c0c17b0463e6cec3993308dbba86b3a0d3f372f9d64a82ec43d80a64c9d4ca62cd45cfa993078a8ce4377045b230efe08dc56e4d0cbb368d1cc949830ea4588cc70a515177f971e0651e3c9122721c8b45c3523689cefd596ede9ef96a9fa2c2587eef3f5736183dd54c9af715ed9670a5ae52648d9ff6fe3f967048deaf9ae753cf833505bf75a801b1295196497af7f0fb8279bf24bf2c7dd7384ec07c046e92cfc36ea977fab76e0e31af4e94fd0857539119d803c972539a9ff31f50d612ab05f53ee3f906a11acb6e60d317deae612c093dcf25a3f02a422e2c290d19151a23cfde1e4b81f641e862c6ed4b15f3e43311d5cfe06f602938e2f46ad6b777bba3c779faea3d3e6e848775447f705776505459caa375ce1bfaa8cb18bc3fbde60ed201ef1a3bcfd5f3a43389f6dbe4d21201cd36565687b58fc34b32dae63937a74eecdd992280e64121740fff2af329c0c2e1f3b6fe0e1ca57f702693cd648a97156de10cada7503f357b61c968885901bd28eb86b081b77fa06db3a4889f60cc5a70c999fbf65b8b83b6601c0bbe3396d066f64486a73940a80c6866b18fee7f1df150404e844feb81d97e049232a5924fda17dddc80bfc8419e013709d000d6d7e883d274e183b5bb387ad8d3c35b95f1c92ab88981721b293c1378cecdb66b9d48748a406536f3079d5cb18e8f235cfac76a24e343f887dbe86c822807ace21248a18249b3fef24f18f512e8305f0c1a3bbc99fa8fc700b3b8fe9506ffc297c5ac1895bc16f223792a65ac2f967c33442043be3e45b90d3554c9d1c869ad9b1803999cb4c5e8909e2c95bd470bf44386e30563b3a717d8479da5d58978ca69ab6b2e1a3b571cbc742e56a7537a362903f0c84084cccf21520c3ec2c0d9c0753cfea016949590c1b9d5905636bb8de491165a4a6513a9a8d46901e8891ab0cb4768112ff3924533ad668da529db82c6e8b9b9edc9680ea0a0a4e0aaacb061f6032a65271ec0c8809c2a3f079cf28591676b7fc0cd356d288506694e4bf23af36b3f7af56235760aa0c472cf29c7fcee13d8625f863abbcc19f5d62a380bb1ca5c1e4bb60cff0851ec70ae7489fd2aba2a7bf35e75a51e22431506b1435935d9d2ec66a501905a55fed91f565a57ea493a67248a18a42a82a863c13c073be2648021cf6320ba5372acd7a2b1ad3c50c066071771f567b4941b2c5a083b898d16cf14a30cab65c44cfed3109c39fd631645c12c7b0b910e429ca208c6d0ad62db2f154fe5c305dca40ed10b86fa4a027f79260836d5ff40865ece59a47a1b271b3d31653a507f994c22b98776923c7f7b3cfcfd0c1bce62a8426c5a5947e7045e03377882b46fa1900003ea83623a1977e967a620cde641a44d8599946b9b7295684f2eaeb999dd86f768b197e7259e5fec96b0537c33f76918afee021f745c7e40fdf8c00bbf327c818aec16635e7bdc74c940a18ddf5eba21e2432f1b2053aca67f7270d3253dbd3154d36a695acf01cfcc4ccbf8e20e4480656fad058d3acef84893da18304a06843d0cf67407fa26e3e7b0497ea9d13dad8c39e00e97f2a6b08830309645b2e1514a37e3cb1456219b92283731d90cafcc76f38d0dc0bb8a20285a36634a398bee59f7ee0f24d0b7bb59d7e45c501d5d8d087ce062c9d53415023230141d2e50fbe5b15429bb671028dc5e44b04110e32b513bf6d21fe709e156861644d6b690b50cb3b9cc0c9cea2d952ae83bb8bfa5e25a564a925cd9370f982b81a95ea1faa664724ddf5b2a10b12baae6a9e607db94ef14d8dabf09bd25dda02d8f70f9695d5a9d425ba86e0c07302ac1a898a33482720f469a361d0d53f94b38126046de1ffa0468c5f75785fd5d2205fbe44fd03d676269bf45c60950466008b7b5509d2128b6326ab59822c6fcb14dc0eeed561fbd874105e3a15d99c19c60b6483f3e17bfc5f49c76df76f44312a6667f97336a274b94ba5413439641ad4a51d03ffff8b9d2e6e5f6a87eab334ac39ff2f93d5aec0438d738fd2494910dea42ac86d8108a1d9a4c17c09cb38e9a43b7f2d81a5550d6497d98f30085c34d6da797a27715e2bf1376ec88b75d77d4505cdf63bdc778769c3cabb1241f0d14e1d2ad03e47c9ac58a29680d5cf18bb1cb39e6e84f54d53c534a91e6710104e83c8e32aec7a36e1dc315fc1d6a211ec7bc05b102f00f1e09093e0f867279ac681d942a7328c8777f78da8087ff8c554ee7ab2cad3a9e4723c2a7980393c56763654e6cc2f16f3a4ee9316da8f89a3d3be684d5932bd2aca276c1e75bbcbf45495e0b2b804e2f401ce208e04ffb9e8c02977ea7c8891d39328a911fe7fe275488583409f3cf3dc7d61f3ce71c27ecb0c2391a6eada10b586ef62fdc2904a3104ca31c44e001242986753270bc272998a8b202240ba66d6191cf1f6728100e69f614e2368fff58a77d1a809814b7bb395d929b0c0e79d699c84d050c9d64645d4d17f8aba0ecc14b70e4f5138fa90a5c886ba6a480a09e27d1d4e5bf5a7017dabb55d3c866b1241ebb140acc2fa34bad6741e5f2d56547b50652f82a00353133ca212a226646bb101e19fe13034c8c9c49ea8e6f69382d1b189b2e4f44573101775c87d8162a83f469e2f89c775c9d453ecee8462d217340e52b8d91eb26506be5c4cc33427e9a171c1fce97a81a05354b624eba5ddf1dd82e63df02a47113a95406f255058648796bcf8545852ba3bd2b8c2223c14ab5577f3db246df63520c9c7a5965db840bac925f4a1cc0c4e26b36691c1151c23cc336bf6ef6befe89b6cc4588e3615016070ba1eb623ec36d86186c15bbab76745933d5fa518309273aa6f91ea085ccca37b30e3540d4a8d59551cb4994d168ab4b4ff39f45cf6f186c8f0037961629039ba116e4a0a670f116f63d935d9892e62024ca8c176d5516b19840a6f1427aa2199ed02ce5514a50546caf52c3a17e0815d733aa865ca639d504160151e2319b0c089fd90225256e9896db769a38fda1e70e5d03518515a0ea62d2e3dab44990326e97c932308b558cb346bfaf11a1fa4fbd1427e2a0b80c1bb55df25a64dfa14fb35f8bc619c05bc8d4bc26ae001d2c7a979d29a22cab54b4fa45601e0dffb2abd33db10e11b0563e45c1738cc476cf919b0e71237b208a4f5fbb470d2c4efc46e8d6d835c825f98f9fb8b4a52979be2e6d4a8222a63191ec2fdaf2bc82e1c40a7a2ed45409710fb1cff0e105369cc45ec8da3cc560a43929817d2318677601c1df1f357996d84cb2bf86e1e2aafffd8af0f6d6782004df50ac7816b622eba8aabeb4fe84c5c20ed224e714ccf16a3eb66a4751c86b0a47afb1d21a8da7b2ee99c9c164844f864615f544473ef6e447e00dc02074cae6f1fb85e917bc1550b1d6923a863159c4481b11dcde8b7e8b46049f8ba06585d2f1d3e359e23e423628d436b724a219ac6b7740cf68c8bcc68486bb6600a17ada64b40b77400747c72731b21b507e541d4a5ba1f3b568ccb83bf22c08b574f44e3c5c8b5511747484c6e9c78ac8333f40bc8b8d9cc5e6e6f7b6d321ff6dd8df1fe260512d2317f4c60f7e53dda03ea34e853d35d11fa790685c43579e5cfcf3d5fffec32944528304a57a7fbb5cb41f4acb45008c820e4810f8e42c8cde08546864ff70dc28525c0132bf0e5e90802d52d1c6f3650970a2dc8ab215d9b21b4dfcf365eefef1860d49311ab79a32c594523da38bc4bcf74ec3dc84c2b0ec64b99c844a8bf3e74905058fe3bce47662324f6554beb7fd972339579e9e486d41bda46e9f8795dde061c33f27629cedf19453e93c3f0d47103159ee3265c97009c2d352774b7d64b08b4222c75cd98c23c1f0375dbc8abadb85db45e90b44170d9b9013788e24da7f5679781dff25580dcc270f3f626396f4a9189ac0c44228518bf7ea5163ee94d64f19fd3c0079dcf1b4a9cf8937bf9bfa045ec58bb19e797223b2b0ebcd0de8a13db8baf85c7ac381a3f4601788091cc02d3efbdc368703bd4699e8321d9afc856cb726cfb0eb772bdce207c670ee33e042a47495e0ac5d50a756a4c4f08a129d3b5c8a3436434282f56cd709d538f5476400fbfaff7aa3dafb88a701adbb8035a1ea708a20fb91ce50bcd3eec491f525a208fc3ae228bea8101d6692acc59fae5a5b8107b280f5e478d9b3513a20917fa55cf98eded6f87ba318f9bf85a1c79b876e4853f137d558df1cd1c22b49d74c1b2f2f914ebee663b6fd81b99cff5f75fb1edfb3e4d13f22ed908eb05a452ef843d4ed1e3d5a21fdb03834180a7012d5562675d0969f6443c89a82fdb0a3f04790590a64f80e2f1d3346c0e61a2c2e5ed18405ca9b47fe97873a3b711a7f3df9f353200c360473efc44f0cedbc39e33e355c17f0a251db80f1d2b1c8e33bb6c82bf77fe08cdcb1319c61613d6207cad9ef33c5a86d1c49bc457b23a7cb99504de500aafc96c5ec763975cbbf71f22d70aa91ad9a380a7adb09af91c130431caca822e61b378327127ae608342aebf332ddf46dd7924e8120de5549302a88b28ba5c7018435ae2301fa26a337c2be264bcb4c8e1b52a13f6781d1af4a1a7479f72735051a9f4ef6bf4dcc15eef415f54ae51c7b90ac407e41246c2f397f2bbe145a5bbcc583ea7ede083133c58bb3aff714f9bf87f61a8e12cdda3fc332842152bb27989a861d2cf2521b4fde1cd99ab8ccad8d174d820af65d891ed20d7be929426954b34822375bd3fba301df18c20de448b941c905ce3f82455912aa9e1bf22d312c8042ef4b70a6e838a39563c7be9100a9f0d4e2d1b36743fe095958a8797a8fd7cc16c3c70e495cdbdf7148855606a6093f4cc2ff9eb01d794a4fa18be3b2e89c8abbc03b6155240df7da90e1c07241677b93cf99fd6555158d0a141e55eeb069f10e5d59df958e068c900b578b7c1548d006dfd7fd41338d552953fef4251d16315dbb263d94543238866a7d40914f4c3f1f6042ca873a12311bf085af687f3aed3e7d424dfc326c7de03eb4c0a1d6b940af7b6d16f98ff20c519f153566fd6c861bddc2e6f1623c4388a8a44540e5c8a1d967ebddc48b2043b05bd0e36febb3d11d5888788c309e0342a0819074f5334a173900643d981f88f94e398aba5baed4a04f40aad0f9868586101f029f877c0579dee1ae3144736adc8a6f234367927a0d204bebe635d6a1f9e9d52fe22186ad3b76e630bc25c20f3b77c9751c15f0c3814d89ab4bccf251aacb9139f593012adc33051001627850343cd59faf8f1fbf9e85493819ab00cac570ad0c2f96ebbc2c2f4425f8cfe557e5b79ff7d009e3f1ab33c85ff339df81000a069a6d8577400cff24e9a9e1bb5b16f766c35dd29b696170e6275009c5b5aa909854eb0b4710b6143a042e97739489e85a8b12e095099f8de4b8d4d313bd9446c9de7395c061667ee38db84893c42bd069ff0daf9fa60d0a3354470f134690de3c4ed6716560ccb1a54bfc5dbc22704382a71cc780931f003b3ff0f831ab90cae3df53aab74bcdc34a81e096cc77b999f26a89c9bcfae34bec3bcf82f106b3800816d71a505dddf4fcd292ce1f1930cc4628782126d1d978f021e8938c272eac58bc14bec433599b08f31b822c1e861c9164f55a542eb3f72dec663804d8b72d319d996d1dbc60d5adf1fa17c3b94d66e5c7e785d333dc50b67f140d80b03f09e3b2ae5510d886d921095d452303c10dde3ced1acf1caf4ffd1abdbf73988c9ef02f763668be456bf264c4baa31f71b3e90af68ef48f011d0c837b8a76a8d530b7fe7e8d976bd0e792c0f4c8aa4ddb9416f784ebbcdac29032d7abe53f518dc3d91ee5f7902b34279e024ba217d2bc50a221b80d688e9ba46f687baa3af81014982febf10c8d74125c1fe9f08e69ba092c0ffba7ff15b8632747b2be242adb5564922241399773c582e4d0a4b6b6e45ac9e1a6eef35ec242e6eeb61c21cdb5afd57db25b4fedc284cc4b7cbdba672edaec3385233c5562531f1d0603b9ee6f3cc41c63258cf82b66530852b091d7c0db9a87cc9aba6566216a25d86cffda409959403c3847a46d8a362edd027791e638dcb029cd7a8c215fe37947096aef7c4ce2027d6b38c18a46d1d35bb6ba7bc5e44139ea3ebacf8fe165751acd56a2e9eefa6cf91801175009326a425ef5f1926a74f5f21d0aec18da731af88468688b44c53940ef4002489942b3f749188202aa001134d29d036c12a73c4b55f1e510a11338231006af54c30e7e8b25bc6a79a10f440fd33653bbc088731341793cbf5e37613898ebef9954720a64bffaf80d4345459973fa17465ffdac8242735076e4d32f54df4bd8ff5a5185b2bfe816005d0128e7d56df331472a28b57b7d7a4ea34e8c7bcd1f8c6c0bc10e8cf55daf18f6057bf72a526f8b0b7ea44a379ab1f73d84c0d77c89f1f204bc522bc8d270e33564989d10b328548ac300134417592b43f7316dabf67095dc58cd04ee0b130c5a77fc883e9e61f589607041dba73b95f12258aaef57f2214f4f24b10cbc7b4014685039fdf0e9981f0c55a9be17fe0f38600b6dc10c43f1aabbcef496371d26c4cfee742f691c64f623d52b6ccff4c1624269ce90217ced070910fd622b99f6dc10c8f3a97bf48f7ba644fb75fd776fe35a6b443b92f38c54092333e97024574d2117b328db0081ea86edf7728665e7a99eafb8ac3b9207e6a5f3eeffbc0324e11e7dabec72c562f5591063759933adf605ec38e0423a29a0ecb3385fd16254784698c592f97dbb8c4149204a7ac2fe01cb2dcd4a9861ce26e08abc687b3198131340a5e5ced197b3fdf00435be8340761348755d163e3681ef677ce3a9294079a5741243b97d36589fc68ab0f8aa9d003d48f5f2004c3978bac354d0cd0d54128c9d5c058f27bc42b061f5f524876bce06560b1278b2637ec19074f7862fbcb7d1e36ba69a64c72468581a25828d147e1d3b462181a8e4f1bbed503a25776f75ffe3be1956d71d33e8fb6c4f35874fbf1fca64217e291b175feade3d73c7bd514b07ca01d7d6142247ac50e0ac2a5967eae5106fc41bd928b5ea5ce204cdf35048966796229aaa27c14f24ce734c240ebb57fb52abb78d7ab41a22078c00004ca3f08b780cce231cf247edd0d5697e4146207d435c618531c709f507558370e1601c551fec6db80babfecf4c05c4e2c80cf69dab7b2fbc74a3f6ae0eb04eaff9f1f91de33e597c0be454b3684b3ef7dbdac4d2eda8b27f024fc768bbf398f7093a39ff2db919ef43bff0bf53f8d8e4c421e702f3a00a37869b3fb3f4dff397c7900a47edcafad53432f3415176eb7a0befc1cec5bea220ad87ee866e0390a1b64cb6cdd63829c88bf5eed58ff130332b37aadc9158ebf166e5cd80c2c3e2ab79060a7fa2db7eb10b3defafafaab70047014952a48ef508fd47ef779ee0a5f915644bc8ac667f640cd276e5e7a6598b662c86d2d620b78f0d5e28b0992c930a443121c7a2ce086308d2c2191a0cafd2148b11e47bfac034bc0551e1c5bff1611cb7e2b8fd6d91f8c17012e3af4065a5f99b7411ef10557cbfb0a2d1878820ed1c41b1f15498f760444ca51fb07477033e543f33893501f50c2c7ed36cd0f510b9732fa40748a9cef09671db96ee70f7af720db35501581518d98e967aed2e03c3b207928d1e78f09e794f45b3ce62dcc8a1386226f39d7da845f4cfc9114825c5f8d84c056f55d21a1db5f13fe1395dac2c3e85bc9e2f1e3c284a2ac61879705d8d65b5cddf712bff13f4720bc02c08539571ae3872615c2f5915fe241238e1022a3f8ee03c9b120f11649915edc36a371fe872e01e89c31ec3bd8325527153ddf5352a9a774dc22dfb5591fbcb83aa4386e1eff844e915c5d7419aec98575142ebc280fe3d1526a1a7cd25d8571727d17872e888e823d79c21504ad0ee41da990d50a49f1d46df83e8af07d064b6a133242d0f77e02b849a60f437accea3667c6d85d53cf402779045487ae2ba585d11be5bf77cab34dd42294af6f2eb15a459601e650bbb6e830fde95d6b322c53c0c6d56062b34454ff93bca5a8998696d94a85cd6c779eb9d16d028f59c29fcd5d643afbd5698a0c671f658386e50005fc93acd44febc1baac481e5b0082da64d3aa933ca4612acce1be89fe5df192be1f67a85fa31b0275c6ea945c51e82105ff1f1c0c4317d53b9f659dd8435f3b7de677fdebcb044e19fcb058872761026428cac7f94139e8e373e881a3ced8290d7afd023398e77ffdc3232c6c56ded4b29108be2a0b90c3372ae3e39d7826daab3fb4e71bdb732d2e8cb62f927d440a8a229c84f7941f1d67f75d693cdf167d1a2df402cd0c940667d024d297c5e69118d037a1aeb17a5cbe138bbaa48f61f49fda55cf4d2052bc22cfce7e50b03a61d1bd57e30abce3ff70f609cbe5c43226fe9f4afdeb9833e95eedc18e9e7c421a30fa7ce6d8a8e8069da343fa03b6b1ac3f1eb852b2c3e9689e9154d3b2195ea0d3f83b8ffb17e212e367b599471ed79258c70b0f5ac3cd3ffb29f78fbe8fbec7b297e34dc6e583a8b098427f88c255d6d6eb015250ef37cd313bb95fc7cad3f8f36b551b26ff5c7ba49295e3c1e8691e714270a47d6cd1f17b547a1296ef490026be36f42be766ec7393bb8732f0bbd53a36b2d0d085718fa0698c2b1e114872dea3aedffdd61bbf8a7a2149ee7263a6b8bdb5d2bb73fdb450f284c030ff8d47ca862343027d4205af147070d41d8fae4b535c5b056f75750d342f50125ce05b12fcebf0fdfef56b68901cf107f6f9eb33ec4df5b10afb3e532ba1cb5f2baf9f50f9bf41e4afeb02d487c1807e87b0bfa4a2f64b9bbd34b288664ab76a29fd1ff40e53068d5265e6838925dcecda1466eb33bef3b5cdcb30b31c84137d044877669be7fe092aff7111f4efca7befd4f4e3ca85d4e876a185f09b826fc8965bdfcabe486e4f8d9a1aaf4231600a8c8e1720286ad5368d41dda4eee00b7939bf1456e3f4c40261e01d2318ef7a3802da9644fec7368d5b274f60f59218e3024c6446d103c2f173b23a70cec4406d957b6778c451cbfe98d43d395fd391bd7dd56651d075d687ab65be50ddf1733ed1c771b25e870dd84b23d29e0fc7839791daf0d3d300598e630ef03b8ec2aec6e320587193dc634dfcdc1e42caa5b7ccffa0371e9e7b6a5f4af500f2de8324b2f60cb6a28c2351bf7cd7bcf4f842561da72f385e2db85f097d6c4996908d0b5c9b133940ed41941f60f06af4f54ee44585b17cce9bbf78878e9f47e43e7161bed0246f593053f2597bef71c271dfe365c839df95d855d067085e289bc864eb2cbde759654d68ce3ae98b7757246b0aae623cdc6aa2f227a4919e8d2fee9517aaf33f2b4c5f8428d9f4e351b9834c9d484a2a993bec6fff3d1ebaeff03fe4530adb891f1f191352eb59af1b079a563321a5cf7508ea6692b62ccc8d60a640e8b9ccd07b45375ef221b8392ad6ee8237b6b3de1bcc2bd8d08c55809d27bdcc0bb6c030312453dfb17f2ee4dd391d0093095f8b2b654f1ad554a39f654a4cfd7387b8419bb68f43c409e35d1d6d023875420ae5eb9613e769b30cc6bcbbd0e1b581db153cc62be18f11a712f901b154f23e48c4f858589125fc4962af043f5e9c4bc40f8a8592fb0131578a1f214625e9f3c4b0c4e863a25b9fe0c6a96efdfb0dccc7787748569cc4a18c4f912c2e1833b3f0bfac0f129325f4c962af043f5e9c4ae487c542c9f940315b0a1f290625e973c5a0c47d494c95e027127b25fc71e25c22f403bb32da598125f60b6227b5090099e954ec4ec05d7c1ed3df0aa3e8ee34bac119057bbb1c44990cbd0e71e1c9d4de5756124b5a7fbe993d53b8a9b183ff891a46227f673912df0892cdac36a6aad1f8c1d45c1ee3b8f802bd4a5ed0c8387e5974d1d8bfdff303c83effcadbb3418fb0e378a5132330aa6a7100b97dad054353f804ffaf304448db127a4ed06f71bc9cecaa1ffecd0a11379266521cecf039d73c3bb96df21fc4a1205f7edcaf2612fac906173fdf5bedcd3c37c4a5b4c111ae6b4d891fd372edb9faba2d9563a88fb78ed741014e362b66416342a1bac44a3e0fe5d98a7ce1fe7b42776ff806d1856f33d76776cad7b2e6255cc8141f773c86527cb7c99c3acda39b89dc04e9d9ce1f3438e1d4f6ee6b47827db47f4a592a8e3406712f1495e63e3196aaedf79d5e56d326d89042b803fb1e86d88341c58392f75deb04f09fc0b14067361591e881ace8298242132c4215bcbd9d5dd0e5c473ecea456b77b63ca07b3afa019469a5ecd2f21f78a95f20572e1f96c6de9ded86b30ce6d0034494903c6be81089e1f4bf6d7e0956af442dba5ea6e785f92752e51ed19232691484b809e1af88a9e84cc5ca7c0d7eb52bc273c8acb3b4a10601982cf75a7206f317cd6f8e85b9a0de3957d80041fec4ce573259e3c64793e4e72ec04a843b074604a4d31963911517ecc8d775e12c43f1e53330393a2e6e49846f4ccff3bc01c446ef6c5732edf0e5f2ffefe5a5c1371092c3e82b3449ec383ac6f33204bdb50e65d1b6bfa51551a0796fe8f47685ca22d7a9b00eac0e720e9ae9c12632a2a03a31dc571419b433f1dbb8ed1001ebc73debfcb3d256747a259ae4aefd3b886e85efef4f453be964a45afb75463155541fea1930e90c2c3c9b9501b4692c11946cfec6fedb753556d8653d82aea955bfb9d6108000211e059e99c6f55923ca4a8cc29c27bf4ce55ec386fc8aa32e9410340be858cb36aad530e424531932561db70ad6d0b87183b08e6f0fdac4eec2f3000eed756e945970ce45ccd10214058301668670211c70085bda0f1ca8e595d73e185a189cdb0e768a0f642c33c7c44e8613519323bb7777948600b207258d0bd5f01a346f24990634b720525048f3892e653e6dedf9256a653f0bec3a01d68c561517e368fa4b14542be20c0fb2e34f5e20ed5b9aeb776a4834c6e8b21935812863ec4ebf429661d38a49595d0aa06a030def16444cb9dbc4ba5cb7954dd8b0a3ebdfaa258397264f64c285a589f9270e54973fb079d558b004f337c6a5e8da05f620559ecc4db2ff28a88a29a3554b90a8da88e5275eedd2f849a824d64717bff8662f0b875f002d3acd0a35d655649d06806f5ca7a38ec32f6e54c04eb12144e4a8bb55dfd9125601e8da151ef89bb16204aef4f906c4ca3017ef0759b8fabe81fa0730d8a5eb764c2d4dbb252ecef9832c353225fc361f640f8d83d4c7c0916f5423d29e754dde63e81b17dc176a2a4843a375fdc2490b207fbcb82b3cc4b8e9ec54ed38d5760c0db8ea8a8692f63b6a24286ef45c30fcb287c2fed4942da80a7a8c95db0a0b3cdc5b937cd6d8f23426d2712c2d3d67bce591735e2e47980634872844a1cd594c62e13641dd17cf529ffb854e8dc6ccbaa7f955e4d8e4ea21bd990018209e8168b4552d5357bb06fc6a5fe5186a6823bccc5c4b34fba9301a6c64425886441247e0daa9f04d73c3e477a24529e6faa6f23206d89064e55793c650ad14ee3a101dd04ffc9f69743ca80430acf66c1b2dac996bad6b4b88b0441ec9202e758aec263a6c1265d4dc45eb28123859f1ed103ec880d971cb1561eeb8cbe5f48e2e00921d4f9e9bd5cce91c5e66b970b034404f36ff0529ead63301274388791b9c866e663991f8550c0d43120a2b62e5b343fd8c0df24bc6acaada66cb4b541cc0d92f7f31d120c8a00522f3e657f5e598338a4c5857055a18ec366a3ecdc658d4939c113e8c51725a035342dbb67e32635b015bc99de47f944f7ff2c2aef9423a2fcfd81cfd7aac7feaa7814df402f127f84931073593297d247fe20340e701337b8050f66fd24aacb62977b5cca415e8cd17c74dd46965a535e532d06e4829d9d6469e783aa1fc6789ee4052848927ed2270a0e8045cbd214972a2a5936744e1db9538a1fe189141bf4815b12170ff11a8af103b81fa94e92634074bae14b7087012839f16cfe41b6584ee19343aaa84a38484d2b7304b0f94e46b2a5f4445d8b3a2a6ed128ca1938e5cba1e58a5b71ca6d019bcdef760776f183662ae4923c418c476146faddc46c1436219987a79c9f02ea72d3cd49e86e1c9ab6a49a79944c867e4d5bf67eebfca2f33a10e9e080572312f7948f615bdb49ac58ea534c1951f9b7217cbd06bdd2a14e57bab8a26739869318f25720d78c4e31fd94e26813e4e4f4021271ceb8e76d5c59aa9a6af909c6342dc19a58348c917b9e6d71452e983925683408310738044f2433f2651e613d5cac7955c579da2dba92ca37880c8a9af709c057fa6fe387341eb96a1d19764d0e6d5b4dacb5edac17dac2ff750403da464b288e148e81983474452e9ebe0588d4322f7119600e4fc868820ca7be9451869c54d0c20fe8bd7b842ba150e7d799a40057064e460023d794738598eb3003399e79f91f600ba8d502fddf306ad2c74b7a8e6ce995cf9d96fd0f927b29f6981b6410fb8fe112ec7b1037aea67aea09a9f068bf163c3165bbfde371203c9a064da24752231365ce951cf2cd364931a0445190afa111cfad8a7e07fb0c1d432e4266c0f48cd0ad003a51461d86f764cb3b0864ee98ce1776a6388da639e5d19067cb453f2eeb2985a6f3a5e2890a0ce7d019f50f6021b0f069da97e618b478476201c3b272da360fd527e1a8d9ee96094fa9241f7592f8d389bf5a89aaf132709ff394a938c562f915877dffc21ce04eb9cb908f173baabff5eb57ad2dc9ec017f77012af4d411da88c2d813988fc565c6a40def27897753504800ba658f632b3f9f95573350495838816b4b5f4a6a2b3a51b427b5975462c32cb0700f3bf160509082fa178dcac57101773933b34576b0fe44a7afd85095e7d9b5c438febd081c302ce1bb12b130d4cf792d35c4c090ed7f590168de284abfc0ed9fd5dbbeac14e3e803c9e58163207478fc330e796c7f0cfebfe75e96c3c0782390cff0efdeb77e5bbde1ec5e16d46726061db102e57bd321134648a48b79befc7b0e0e4db9458ba76c93a17c37ce7d1e3bf5f9215bf687f43722a1870a521d1db8c55ba09186560487670d0269efa2d19239439f7056cc2c0011f2bf36480a41060bd0f2a8493b45fb72561d36dcec836b31ad8dd8117e9987662c7d38a0e58f94d5d59effd74beb3292d27608d39a457b44260d2a01c9e0b472421736e9d2de4cb786bd392741f43eed1926ac3a6d3d715f4ac4485046d67a015d131b5123d231f196849876669b0b9547cf0d3cc9d24a31f4d6b8093831ac9532c4f2e540e2de5863f76f2f6e27b0ad7d955fe87b02a87bd673196a34563d7ba44fd9a1a45463728177b357eddea5131aaf841e71c0d4badf439a014912e3dbb597c464aaa8100d20140bfa3e940c9edd9f41f726fc1393fb0d5d505f0995676eeed106b51c9acc30e0bda048531549590eadbd810cdc7116c6cca672b82f42dce9f78e1abe7dffc7eac15ac68db20ef9716cf9caf082a427115271db2afb7bfaa8b82d89035a7830843cad883d0628ad3981a1e60d200c27de3a081cf7d9f29ce9596058bf096ff33896a9cc71bc6cc6719c81a6f7a18a198960697ad4309eef93ef2c6f0db9bbd4bf6445f4c428fe1cf695834860d3712425c508a171379fbca2a0029aa9bb90e639efae7cf13f02caf9f0f81274a7082ba0613d57dd6ed80a728fe601f1a7bd16df138807356090e2f5a38cfc13391f21a7bc071c99216f861d6a06a4c9d4f333bc907df5b9da2466e8ee83a5c89134a60558e66cef6c28d08d8784d03439a498d5bda832d4c11628f8fc12e52ca304c3e7c5b045b1fa9d050bcfadadb096a2e6e56e3579d855b20c67c95be7826f9bbf45bf11a34add265d4091d9c77e43069e9aaa802f9851f693b62cd689197e6f8a495fcdd8dbe782576f08df47a661242ba341176db9eca8479f0ff6f55e865d90520e1a51413e868c8d05847c84e2b2488bd0819b7d3d915442d2d0025f58be7bbbe41ecf533e801c061fe4398272a9850e26c1facff99bbe7ad4c1dc740287434ab2c04f7db7cee5f8453ba33ee2921b4a32226502270718475dda922a26a22b666627c22905a61a29bbbedf3d6ae29f6bd8307a9c4c631378b56aa8b185f659dc9aeb2ace038618af4428cd9586e9f4fb8b3f414c31841e4d223c924291b0c65605cd63477b4f1a96eadeb7f41ab9ae96d4db9b9d014ed0a9715c533c02743730c25e4ae4eb9f5b290d10a1ed5c921f2654dcf3d44b03bbf9db16d97413033459e5305c284b416c51482ce5f604c18ecc886e8549b81d86dbfe8ef8ee0b270a8aceb08e5ad8b8ccbbc522469b5f37d6542d898d69b91c81e42f835524f3c3332ac28d9d5aef981a9517490cbe1dbc650c91c32785b4efeebd25cf6cefc640355af00dac234a01553e7f0f2e70746683800e300d9d174d84e3b5ca3f765c99fd24970bbfb357614e2b9d60b0c0f29dd83933912c2717622e592d1b9f040c5ef35b54eec7707baa56023141b462726554bdb3d2a664445eb9d8a00402097005a8acbaef5dd85400bef8e2e95ce5c8d4fea0118235adb54550ccb9b810bdcea37c1144225c04f7c5918a1478aa00dac572e629a38a82f25f55a9bf3bc63723031c80ea9adb41c582928750b7ae45cecc61d90299c99fa39ffe2db4b770b84f5d9312a00b8247eeb8de444713c5a4c00b4ff889de03dc0c3c1b4005a9b51c4de14abcce7eb8e5a3dd451c78c8802a03d71dcc0cf8b8a8ed63520bdbad4821a083256f5379e9901e2476caf1da23ee37a1689e075424fbed874cd57e03a308ab130b2f0e8e6b3cbb9cd230e4229dc76fd02d7ebb34262bf26bba44498a3ff47e6a8316f6a14e38f3c9a02036d6e96b90b816eee3924769508524c5e9262e635221c9a9c8b43c597c78502f9933379bfc9f8bef3370ada0e0a829a8975a5c9c0c1b48adcc5fe38fedac1c95c84425d5ecc9ffbb7e8fb4cf35673f0e20877aa43d65217b5fbe7a4ab7b112e3acf456e94b65c8128cabd2eb85568f29815400e461e0bdb3c70c14ba72cf2011b62b21aea36c39ae05550954162a390b91642c4400b9c4726a3be8083871baddb6441097979340c4c429e14054f6118720d94143b4ca2c3ecac0f4390802ee3e66d440a19c8d601f39d6b39c4e68fea6a8da56292da20a398891a15193b8e30c07d9c0d1286fc836203886fa383dc50ee96f145bbb53cb467f3c628de97d10f230629ae34005aa386ff8762c0267439135d12f7f95903f1e5c06b718cf0dbc5ab6158dfb97012f9c6ad49f6d685b5a8debfcd47dac7a544b250690dc8aabc17ba6080900c3b59acd2e5e8e6a9ce3b460401ec46f43fdd2f5df0d00dd568cb42bd4427eccfdab58db4229cada8e6ce12e6a86d7305eec5b72b5781010beace34a5a64a2f9a0180cd5ec9ee1d777613802185f872013890c6bd898d369b70f371cf96ea7304346b2f2e0e23786ee1815699f7a32e3cffc7b1c0ae5bd69ddd937a99aabda3a89874598fdc3045519717793748b1871273e204f8cd6dad674b16d90f2e928b0d7a275d5fc4cfdd74668634934ef3e0764281b6b624bfabad181bffdadff29a492e36d079e7d78185bc33b69ad164ef8225cf1c03d8439767d93940fe021554a0b2c66a6ca919dc51b0ebefcc6e767d0d4aa355c260c1c53cd522d0b5827bdf2da543bd9848058669a6d8d711bfa8e7334352d5f65b179a22baeb1f2af9168c14a506e6a1cb51dd5146e4b2d46fa1a2bafd9f163f40f44ce68560f0d42d3693eb0a7e28902753a236e0b59f45c1596c34ceb3e78f180b1aa5ccb93091ef436bb174a6963de10389e74fedb3a1c538b3d0b19922dbbea786ab2b3cc70b469d48628cc3d6e30e5ac49df462d4efecdb7a6856bbe11f35f37177f855ee97e760e5803b19b22bd455b7bbea7559c22c034a5e9d25dccb32e4b87a1a3388ffba37f25c16f43cbd4c6fa763088b5a67b2e24787aed83c9902907bcfd3c2a00812bdbd57ec110b8a416b71ba7e52b262c4982b0cca8375f62934c2a1f4161d5fc1383384a1837e0d3cc3bf164f0b0b7924db7b9d186975c2ed6a105c21e929fd8473ca45cee439d10ee44c564398301b7b8a1e97d5d93a1d10883385aa4424ddccb926d81419e6c5721d68893bb22e9f2cafa10154a71346176e3373f9e7a76664523ad252c301f67779262ee04c2e094f9d7557080ebc5ce7289e471bb9d3f899ac17038548c26fdd83ac374996c0fe560504c094170780e83e7db321cadf19220bec88e99dd92cb602afb5c745d9c3d16ea3e6e881e25273f320a148118183c61361b7ae807079ee1cc2b27176720846c2bd1992e5092d040c1a968ab568d4b13249c97803c0c9c64ec075a8d8634baeb6c47ae245599553b5b468bd568dee97c1af0a1af4d8c4a1fbc7965a8f4367759a02305837a983079a11885766c82d20d74417d7112a0e32881f0a09721a9dc0000b75c2989c18986a758e2083b56c79166f4041861da0cac783006c434e52174bed038783911501c0208021663d4889291cc700b5bc85d3286f4c010b81435e082d830fdcc72490b504306f7be0c21561aff2a23176531e564a12af868639106a791456118496b2e7c86302c0b1ff95dff89e9e046aae8a26b9af52cfddd33d8207615e1fc5074d49e500190d53cadb1d4833b2ffd0fd9efe2bb1b05749156c10833fe32f3b323d16d1fc2218ee0792b19530b1970a8460b263ab52e4340dc38fb10629a6c65ea5ca167360d709beedbb6093708883e06d3446133692806aa4e6a231248c98da65968489adf15080afbb9147f9ab315d50221855ea010193d72f438b09a98c5646717eb0c6b8e134728241cc74ba787c91e04340a85f1ecf580020a49404b0dced415b764853e9104060d87f8190b9fae5d9fec03e312ba653265b6e32d1396cd12348e9880055e64c2dca2815fb484101e5d6159ba79206412cbf84cbd66c3ee6c8f5d0516b2ff88d91660e239b30c1b95f2a6386aac4ca6b7300164bd39c758548cbec31da22ead9fa43e03300803d76609bf41a4ee48f32fdb79576c64aa2807e0b7654f190209def41620d889f1532f8cf677df21e841ca856d68e903038cc8d14c7b0d55724ecdc51429608c8e23164a7f5f18c3657b05db79ec8edcb1676ae0f88075ec542487408081fb4fa4490e42104ca3ee06cf9703104541afd5d3d61aec83d52d7c1110b911a925ec53e3dc1da3562cda552cc269284c4ef7061519926f258794d1067cc2202f77f967c89b478ebf3736bb243578834ed65c0e042be3989e1cb55df2293734ed7d1dc10a9cefa1c1b291cc1bb16b9da908e914a386e83ebaad470d461712942d47eb739f0b32f42aa5ae87c1b32e0047edb12009d116a5a796e4633319b0260397883c02b91bcd385593527911ee1b12945446c091de3581b6ef99412c91d2ea64c482292e706fac5c148f54d9a08581238c2318d6d91290d49cc5d609e0901d857d7c2604bf8562b3dff06313127c88c119441983d96c07db8e6fa19fdaed09d9444a2a7248b6f686b8ebed0516725dd5dde2c62fa8a898b27a6c014ee9386c5f92908e98f7b6783418af5f4ac1ca47755a03d33a275491570f06e25c0afe8b701d765564270580870de08b3455c225273498cc29e2d43fa71a24a98a7f62c7651afd00983f8843a3822afaeaaa690d857bec819ec15a1323892ca00d3de06c3d521cdebef95ef0aa4b08fd74e3ae0c8181d12aa09d5f03424a4e7c11dda0f818aefbe02f319fc162dacbd0796f9ba7727928955f142ce981e2eb6d815d2a992eb773cb4729bf90ca62e21097ba518ba5fe9d898a9e18172800f59b43b8c8b403a4f9380953772fa240e203dc7dce136969db04858676bab10450047cc3c545d8145a1eb4ac1414c34324567115d0a1d1e559e4a862f3430141f8a564fb31a721dd7de22faaf51d9d08d5a43073dce5a024d666d878a7d70eb674d697903c6269fe2051f2fb634244868eb1d7a022ea3ad197be98b5c1b3c5a312bcd5b2e5d0c619d2b570272acfc853634029183d0184ed16550b86bbe431d937ac2777998a126536e09bf8cce20ed105018e9e1c99c200f49356fad5970e16e93eb9279dfdc6279826a4dc9c771625159eca1fe28bda940b04f809d7905072fff1705d0d1d193adfdbfbd501abb060f2e742562057d4bf0c7a3d479c703b83b84840820b6fc19f7905c4c342efa433107844a0c7496487cef297d2cb9ae62140d30af385aa5442a1d0be13e40a95ccd58eedbe7d4c4bc99e0a0687c6dd617422043bcc2c2fd9c4f228f8c77bb91eb43c9690827db99bd58389eaf2130f870e3cdf50c53d4f8c99bea67e90f2fef841cd0f657194d69d7c128e0c5e49c2acd06599e813815af88f8bae00b56b6a53e9125ebfe2c3a0c1f90b7f0e7fbd18de959aaabf98a1216588acc49409456f5f93978d8161db52b507e76106efc3810911832aa07d83ae438880bb3eda62111ae517cf0f6a40676f97373e08080f10e3cf6022c3c0c1ef87da25675377190f653ed79b8bab0ab9b55dfd0e1dbbdd237be593c2d81edda209002b0e28e2e9d10bdacf5130bc9ab34cae5f6892e23b34497ecc1ed13e7df8588638e79069d1c75a05188108a771f7c554c54f989bbcdcc213f3caf249a0006f0d56000db92e0bf380048e90f890638a8fac81db68be6ed20eb078ded4a967becbbe5d2d600192b6a37fc6794cd2aa2317d9722ba581c342ae4baaf01038ba97365d2b79cff51506d50a088875e1887990a2b77b009e38b30ac098159feb1bb07e351037a0c693eae06ae932c83764b3ef16569ccd52303595c91870373cababae122afc25727c5005e6609205e662fc30b826c62efdc1e62ef4081ce7ddb8ad9109d2326642754370cb8397167be10c1e7f7b3f4af5c35f39a53c1b07b69ab542958e394a3c6e8b945d13bd34c87c54d7ae8597510c9e328d5d1bc738b8e09097620057e240fdec7f14553ba3c745149977280502016ffad4be4304b0fdef9aa644a49e1054d974405ab70f330160b05cd6dca41d2ca4566473331b45c30ecdbe628200080fc23bf2509ba58ffd6a10d3be808974274624782090a16b27d8d85ae24f087eef01d90388b0efad4126cb9043a315fa95510382b391da0006b82d39ca834e3e741522f4cce7284671af491ac3f5ea87f4adfc37f92275559fbb7549a2de58e56a891b4bbc98acb1ed69e1b5c171f489a5445acdba3bfb604169e9155d235076f657612443605990af209180b756bb266d93d22c76ca461a25889c2c778488396c4327dcba28dc75f2a8149cd2efd56ccd5a5dfea9a5d1b8b4b9830fdae48e137811829ffae0955239dfb29babe516bdf12eba35a4d29e9282916dad683d7c6c2dfb87f3d9206adbe76283cdaae2a4443f7492572cc4649b99f82c5d8c6fd4be77e4a186e5d91b445d244afb1f8845ff7b5e05c141eed8f1de977aa8f5453bae96a54abfa32e9dc4f19ca1f0beb1b7d2c3682569a402cea9a5d5b5ba49a13ec9bea8e11e9dc4f0943fd16f7affc5624ffffb31e314832c56cd7de32c3ee8fb68f696d9192c652278918c336ee5fa16ee95c157f09d7621917e5574137c544d1de88f2c3b04cd7ec7a91b45986652193d67f471cdeba54cb546eba6edcbfb4a67509ad615a45a5a5f0995e2447abad98415a53c8f4dc379175f43ccf9bcd206fdcd69a98f1ae3b62662a0bd6886336dbc2678ac2cf4d972bcc94ac7e980ae2ce9ab38abb9761493d242654965019412b73f64c22ee23881b41dc8c5669acfda248abb409048709e44e20573aea721f456ecce05e26090d69d23469851972850dccdd1d001bd03961258214c761a9e70f13fc09d0d32a6dfa9873024f1720e0628b2e3ee0810e80c1810e70d1812fb6f0a20b0e70800b2eb6a8b3012d3490812cb0b8820e062e60812aaca8a28a2aaab041260a25eb05847c67c9ddb338102fc270e7a9c08e0cf8d3dfb9230af730bfa6e1b5536aae30df59b9961ceec9a9dc3abb40e23b36b5606fb6da8720ad5e61c14a152a4cb2d8920060af24a4292b18a950e46a110d0929e958ab354848e2a3ea19410424211c31b2062d024200887ce04107433800c01a6bfc58638769ea90b9ce1dde642e832c527b1672668e36ae84473673b4dc9d8aacb1c61a33c70f676d4be3810254dc76858da5e69940911313742cac434e4c785d87c23ae4ce338598b30bc4a22844e4ce23011e29469c8b7822c003019e07f0cc19e2f3c609ba70773d6fbc71f71b3ff8bcb1e59b37622d77d7f8aee60d2637907c1fb5dd7923c7ccc0052bea8d318c6543af994117175e9bd039e97e58e1ad598e03a288c3038709283c50ec40a0013b56ec5ce13b74a68d273a7738bf16eb64439ea389eae1b66dae4a34895d0713779fe213e5c475ee781a90135bb4baf3bc71e761803bcf133c4e60e1ee3038100fb460f2e4e061fddbe481b943b92fd2954d8fd476b1d0dca1a395682537f0f788c18e56deb6b5ed4a5214c2cf6548ab175bdc3de7736784e94a7327c874164c07c17434fecd664b3bbfd32f25e5a4df6568632fdaae8dd59476a9a4f7c7ba1c8d8ae0b5ab55ed6c4986bfa2ddc03a443b2dd6fc4eafb5280a7d1fd595d63fa2ddb53991ee7ce00214b0581b5351aafb933ec4445bc35c8044bddf35245bda054bd2506345bdad312c0d2a3c8d329e0698c60a481a3f80a4d1038d2e9c46a1d4a3140d22fc335da1d1058dcfdda9f75aac4623887a5b33a141dda987ab4963b8ec8c29ce5073c60feed423816ad1d6b4d8d819e219023843e5ee947a59eaaee10c8b1966b0cc30803bf57e6e56ba4c7adf1f95881ed6515d296829ed9ee8aaea2398508f2b4968a988d630d57adb6d2bcd895fab7d141f5590be55a146e0e35c65d87019f91956738279de8b64b95940c74bc046e2deafc56afd19833a3c1a7e07ee3edd795070f7eb403cb0e3ce534310771e26de09d32a5d69fc0a1f0536dcdd456563d868ab48845fba1ac5c818ddbd23c30377ef74bd51bb2fad44a2588970b6f619d6e1bd627d86d12e77ab7dc237da655ca3f749fc994480bbdf702060bcc077897342e113851f86422678c18bb35cde33139cf126b89a24620ccb0fc3d51157c7f0e2a16bc7d104495c038db83a8ea3ceb0a29a5d404a0073d74cb42d0bcdc2fa37b1fe92e72db58ceaaead5d2a853f5a59f89f8b3226aabbb6603a346ae2e4797a8949e7bee9430cca907cdfb7f7916040c458220609eeb96f1a411aeeae97bc7e1b1ee5719c917a7f6c044bee0ef4b5aadd79dcb871e3a6091e2644e000d7e2d325371093aea7d5210a2ea08d93457ab9bb8ed28184e0c749f16fb3fc3c6c7896f02e7c1728de6164c0a97771ed5b391cc6ed51a63d2f779ee779b3990780700394f980ce1debe271c4208bb56b556b690259782da9d002beb520b471e553f92b279c608ba85cb1569a5a586222d3025bc1e856bbd5a42697151f71dcb72e57b1b47d56d26d1cd62611939d12259e26f7be510f69894945bbdcab498d094d286ce26add642e1548185315a61b68af64a625fb4c4bc85795274c624d642eb06005081c0540b56d62c2129690516ccc6fab3447fbfc377a74d3d5c8f3409bffbe6d679c849335817f039f8cf1ace151c3a384dbc8c16d90f9c2eadc7de8e407a8fbe9805817eb16661fe60702ea8058e03b89621d750f102fb6e8dc95230681b4e49e808074a1c6ebe96f064170d1b15a41890285fae042abdab547f296b2d2ae3d12a594ea7881e2423b7b5bf8b3f1c6931c377078066950004a1009b350c4282f6c74e136cad0866c5183bb7f88af173ef5882803445950a0d0ee6de1e700a48e1524903a413a774ac83aba0640b8fb1507b2010c907504f77d271c965a9363be013d784457faa9d22a2d871653f13c1d517058c688b291cd6a5d6975284a2c28e32251ac446225f23c1d9ef7e1fd30b4b79925eb8cd4daf39a000101010101fd003d71724213134a60b2042828a80605053dad0e053dadd2b00c09d59506fdd7a2cc9c3e939ef744cbdd3beb84099ca8e3dd0db4b7a7d056bdf1eb0a395143e70492133c6ed8b8a9b901d244144d84e9fee211e3fc7d99f727624cfb3ecf937d4af9af1d7ff5f97c5f1352dc3b204dfc70af42a4c40418ae9998c34498fb3a7f0ddb2ece5598b8e5fa4c38717767ad9aea3f3191abe402ea5459005d00e9de2d0006a44d0a2eceddbaf7d344b1acb579e30e9623c56349dbe46ea55f9428513c4f1654530a7aaa4b41383c347baa398a9ee1d810c1e636fbd97ecde6c76fc9c64697e5abcdeaaead281a5793744cfbdc21ba1231a6a9a2bc8272795f97aeb96a716fd91a347ccd9c3538f76e8d14ffb2d0d547bb35384a206ac0e8b49a376a6e6556e3e45a25c4704aff6221259450024767c3fba0ae4fd44559b9daa380281480f33e6390820a80919448014988e1ded124d2c8928021095812aa8a5795ea3475701f5e5095e607f734a025d3a0a9e3ee1db89fae5695a64283735ac6399a7e914463e4c3baf8aae83d42f3c3e8cc1b9ccf7471a76782dc3badcf6c60e68d7b670647cd587137b3840a86d16bb3fa330675d6d7ee3a0382c41b249030727724e81174fc8830eef4080260bcc323702019c1c6882d5f9601ac428b65005fcd080f8ae0a288314558a925ad8552d32270f8006e580640bbf08998e33522ba942411469789081c9dc6af21c0f0ee623a449b2148f78be9104e5a43e0f02ebf05ad58475a85f0c02d7d6eba1ad1d54d572355d0972e7c0b7c2a429078a45f44052684945a0d0b1126808a0474716ab475c6b504fca874eb5b193ab54c1877ef5c0ea48c941c8de65de673198415e51eb70e0287fb7d1f8f41cc7e0731048c591856202ace5d8a2e6f0904192da2e8b212d59c60336dab7668d75958ff268455d81f9b615b9ded3adb751625ca2b48fff84b41e05309ff963947fb214dae648e06fe50c30fdf0f2a8c9768b54e409e541a43a7674c18fc2434e6e69db663707897a311f940850fd407293e0c11238677a118fcb4e96b311fde558cbbfbddb68734b0a425237a2bedc18a77342c527f616dfcd247a25845b15ea6cfb03008372b0842bc9856061065ec0984103a4ad012322470c7afcb845fb88565afb00e896551a8ab91d2c6af1c60e0423fdc5c5b2420634eb0b51559dae664f53ddaf805cb89b9ba42d7c6afdcad493b63be707f6dfccac1871e678dd6c3763f61248cab49f8758f624f5f84b1ec855fe147d103113b4aa06070f71d1417a795da794ad0cab88856c6367eedb7e557f8b62f22c78d5fb432b6c50e67e86ab44b9cfba22fc58afb50bb0b74086309ab7ecb76ad4640180bf81346da42b66bf16ba747181b5e26071abcd3e56dd3d5db9c4113b50e318559d794b0ace622fc22fa8c93f02bbc7808b6f16be317155fa6b8bf74eea7e057eea784a1107ed530ce3c46bc8cd9f8b5f12bfc8d5f6ec60bdcf5e318c632004c1cb29ce0e47937c070c3df103aad618a4445a6690e6f502003990f7ce9f2811b72182205340b16215918864261f89f4b58f83648c0dd6d10e336103969f27d1f5dd5cea70910cde5b68a8695163eeb47bb7135a9867d541d25812e61f235d5fdad58dfb7355dc224cc4fbeae435dee9b3e550dbf16abbf9a036b58d7dc37a9680e87f3ea73256b12ee9635fc0a2f137ebd48ab9769e3170f922d753c77d4961766b3d94c498e14433cfa0366a0e264fd9b278a15e7bf72f18ba875f1cb85657f84653afc7ffd585fe093311e2a836de7a704eebe440cb4768f34d33d6a0231d2fea48d5ffabf28b6f1eb0923d162f815d6a13af44badcb14fee7f27e8cc99d470a9416d762f54e98023419411c0b86317cc2d0020c4a1306239d3b50db5109a82d172a261737938b1bfd560bce8708de63f37c008b212568711f2a2a23e820410f527074900407002840a87077146e412c7991254de4085864516788170bf89962e82b72c743900972c0800b0869bcf083113b61bc90c347d535655cebc2d76283bbd3d913be340b16fd52f2b93c8209d96c37d5f869258a57146b7993d1ce6783f56d756baa0a6fdd5ff725a1947a1ab3ea670cf63c8de6eaa2dd0dacdff4034477a5d4a3fbd66b471cfefcfc00b17e7e80288ed463a93dda599d318c453ffd19837f9568a7a9b6e24ebd9a13a983b891ad2d1278737ae63afea2bd3d9125cd3db83b4d887663cda2ebdae7998b9c665c9bb987d3e9c2197aabadaefd10971b5311eb6e11e5b665ea3e2c593e2c7495e5bbe96ab4bb2cb9fa54e9912d57aa35ed76a5d956d2453b5ddafa54a97ea23eaafb4ff49f7405c52a44bb6debd3cd63b64e54bb644aaf72657b2ec69a6a21daddc06d9f34bd15d421d53bac2c6ca395de72dc75f65128515a4735573257da51274b69f8f99decbe4ff1ce8995aec2271b66d2e6b7d93e4d6b18ed76895742b4cbf27d589c886e3e2a5658ffd63a12c5dad9aecdd56c88475ac334cbf737b012dd36bd4a6156a29dfebe5cad6871a550ed445188a2ca08df85b7f46fbbe75610c582496be33e69640fe9534889493361d254eee4986fb38ee13d9ed7a35be4988d94c2d7825f17db5c7dba4c44fbeac72dfd5a6c0ce7bfb254133f86654af749dcd15cd536ff2ef1171efd58bb28e111f8543e9affde5cab50adeb4d2759caba1eedba6b6db7864f73d115ccd57d3e30ba3a527d945e4b8f3e3afeb61f8c453d4d7d682adae96ba94f55d1eebb23323f61f195dc8ec254da7ade4773356d77d5f5837d942582f673155a7d9937c5fbfb3e1f2b7435537d9f95eff399d19515d5a7bf26b06692aca0757a8d2f6551ef1ed16d6f25474ddfe65ab4fba074dd47df666f7dc2e1b54f3877f6bfd5a69538a4d7eab7b9d29ebf9895a3b16a35dc737126edae4ef60826444ba57b54b444e5697568c7ce25152c75d1695a85fd17751a6f4d5a2b9263fda78f1c4b1ab5ad708c76fbbed6fff47d1f45c187048a8258935012e87f6da6a2502869f76cbf88820f69b70a142b38834245254ae86aeb327fdf473bd26e1fd26e15d5ac9c787116eb98f5c5b68aa5c5a4dd34eb4abfefd3181f21eda6622d77b64e473eb17edb8ae52844b12cc4e327b996423bf46278ab51288a424bb0f0f10b768f7255fc562d53b947426fcb150963191e3fa98565b9b0fed2c62f8d5fe187d7551665a5d00e6dfc7252729195e8e8627b5dd735748f722dc258a6733f65e9c9eefb6cddf941ba351ec5ea7324bc650d5492238fd092a84092765f7844bb5c6b146b9aed3b595ac661bde1da3da2077077d1e70a5b7eba254b585ac49864b15059dc1d076a00abfd9a565a4ad631039f2b1071775d73cae7510cdc7d1e9d39b2818bbb172d51b9b68ab058788572b09c08036d78e441f8637de1d7d615768fb00c57933ad647f7c768579b721d6fe07d1aedd1fb7e8b92d0e97aeb4a70a27f5b423f97e044a3b02813a7265d57744ed510a1235dde2793d8e0e131c67421e1ee94ee84e122adebc75b2eb4caa0e1d9728333adee1efa6c4971622df1d9b2e1de85a8a2b91a3289e0b85b9dbbdab59e973d8f1cf32dac7ffb1083a258674d98dc36fc0f3d4f8a14294072dff417b7b4100ecb5898fb29a10dcbd84c810e0ecb58073e53f8c1759de1b0c461a93b135248a244da9b9e51cecc6694944c146b7729ae26753e34fd819445813e15fdc04f57eb64f306edd656b4fa69ae21a1a177aaadae09b4998635c5a190255ad570c5db86377dbbeb13ad347c96fef901da42b4f3795dc1efa3789738a75ad162b4fbd0ea5d628b29595f04ed1336a29db6a25e69daf9e84cc314676b3912e77d9fd636beb7caf7fa3e20d6ebfb5bcbd537bbf8f3a9bbe7e23014499acb1593f469e2675c63e52769cd529f9a8ae664b4cbd5266cfbfd7d2c2b4a5564b9fcf351bc69958dd30f5e25da7d4632155e2ea499ece54a52c1159bbd948e64444c4bb2d84b8968b6e46abd98be92f5b51a667d58f3a6957e15da5d6ce98bd24ed7b02d7cf109df26d6b5186455fd1a93b92473b9eafc4e610dbf88763ee27f9315559f75cdeff44ff846571ab437b266dbb52c08258df9372dc5dab57465bb56a48dd54633aec9945e5416634a2ac18fd11068972449fea1da62b17bc1fcc119719e18a0a860791a8f01ec2d6fbb96d084004b2868a7122f1d88afede801cec385a5ff4451a0d7521e2a50c8edaf559dc1129a94b466290a7f31786d094d4aaa6da7cb16c22a443bead52a08e8f1c9c2c1fdfb74ee3c6fae12c0ba56895841ccda11c05449c05fbec44cf261cf6b3d48650a0e4bcda24d15175fc2629989c1dd734c404edc879471e7f172bfd1e38e4a75f11d1be69c002585470dcf1a540a11e8d9f1206abf74e1c319f84edd5ffca2d752d75ffca2b8860bed6c36c28f3b8bd5ca29e99a5df7e87111958b8944229dbfe96d15a675ed97741572cc46757f524da9eedacab848df4ac6322eaa29695db3cb458ed948c33452cd1596df163e52eea784f58df47fd1fdac8bfe8b72f99dacf82dfd36e734599f62b6fac2b2df7589487f885fe02f6d8d9334ee6b4a9a56c66e25637a5fab6b768935bc6fd3fb9635a5fc6419d3e17d3237f42126facf55185993b4de37e3225cbb4763f94c5817e9261023e9b79aac4962624a0719fd5629950306a91c6b7035a9c3f9b56ceaa3daf5f3472fc5d524da89386f5d86e2e77fd2db2649d23a12a29da6354c8fecd7477cf45bab2f06ed882f065fcc1f629a2ad7baa7ce67b5ab8a7ab918e5f1810222291b488472974211b15d2bd26c2c77291e139c7ab8768f7297da2193dac1b92ba1bbd2b7795f9bda2172a75e0eacb64732d2df5a2a12aae56e16c5c912a03268e33c6f0d32d6b0c28d587a9f300a020f3012826aae81c475ee72df34132b88cf2801668718c4e00ec4122bced6e9be1a6dd440a3060e6a3c51cf47ac1babe87b1f11599f74084b6a51fc0227dcdd459bb840042e88e3023819d75a2e6abb76a671c6933852687f2cffc7742992b98ce5ca21239cab95eb8b44b66b3516899efe458ed9e8c59785ff5a2f71f2bc11b482461134823c71c61037333430230377dfafbfa41cedb513054a0707683106d00e06525006116ee407bb6aff341b96f5da74adb99217df9a6d3b1f94e163b60210062ab0310725c7dd797080ffb56258bf7585fe736d9261858fbf8f46bc6fbdc16633323c328a9031e4043f9c60cb09b6cc2964f03945e9738af739c5159f53c87c4e61e4738a209f53fcf89c0289cf2982f098737ee61c99c3c3a79c16f89423029f72b2f029478e4f3949f89403844f3931f89453f32907a907073e3e88b3c5671cd1679c98cf38527cc651f98c93f20987053ee16ce1130e153ee140e1130e1a9f707cf009c7003ee1903ee1549f7092e0783ee178e013ce0d9f50900105163ea180804f2816e0138a323ea1c8f98422e6138a9f1f423c00e2e307ed20040f21d580303e1b80f3d900d06703947c3620e88df6f9e6f3f946003edfa4e0f30deb4d109f0c80814f0680e1e307074f58c0e7130cf0f984199f4f7cf1f984e8f309269f4fa4e0334d9127807c3e8104c83402800ddc883edd7c3edd08f974d3e3d3cd0d9f4d8cc0671356f86ca28dcf2676f0d9840b3e9b60f2d9c4139f4d78e0938918f864a28e4f26a0f0c9c4189f4c6cf1c944169f4c143142a4c8082114f1d9460d9f6d36e0b38d047cb651e3b30d199f6dbaf86403e493cd083ed970e0938d0f9f6c787c2ea1029f4b84e173892c7c2e21c512717c92d93e36c0b1c4129f4b509f4bf8f0b904cae71a16f85ce3019f6bb0f0b9660a9f6b9ef0b946099f68cc5823c6e79a1b7caee1e2738df5b9a6fa5c03f3b946c8e71a1f9f6b7af854d3029f6ac2983c7cecd88163887730d5bc7caa19f2a9c6894f353d3ed500c0a79a0c7c2a51c68f1f4964c0671252f84ce28dcf24d2f84ce2079f4974f19986059f69603ed3b87ca641c1679ace679a223ed3f8f09986c7271a337ca211814f345ca0a1804f347172f0008286fa44b3814f34eef34c0b7c9ee980cf3356f83cb384cf33627c9e79f27966e6f38c91cf33467c9e19e2f30ccaa719327c9af1c2a7190cf83433019f66e6f834a3844f3343f834b3834f3307f069a6f46926f46926e6d38ccba799954f33447c9ad1e11309347c2231c68e0e86f408e1d1c1e4d10112577c2221e41389950bd1d1840bd151c685e8a08103213a70b8901c5eb8901c685c488e2e2e2447cd85e428722139582e0487192e0487065c088e39ee6ea48907de81911d3f8a90c167112df82cc28acf22967c1631e4b388273e8b60e2b308558f39c4103e87e8c1e71038f81c828bcf21489f5fa8e0d909e2a38c169f656a3ecbbc7c9649c16799d5c40104199f4020c0271024109f4f325bf82443c72799099089c22799277c9251c22719217c92e9c12799303ec9d4e0930c0c3ec9ec1c3b74cc311ef03946033ec754c0e718287c8e61e373cc113ec77cf139468bcf31a1cf31b2d40f0d70f85067fa60854f1f22e0d307383e7d58c2a70f46f8f4a18c4f3115f029068e4f31697c8a01c2a7981c7c7a7941cc169f626c3ec530f914d3f22986894f31487c8a09b263fae8a1c7670f3a7cf6c0e39307357830814f1ebef0c9c3153c3cc0270f4df8e401099f3c00e19387303e7938804f1e60e001478f0d7ce8d8a1043e77d0c2e70e16f0b9c31c9f3b3ce1730705f8dca18ccf1dbcf8dca18bcf1db6cf1dac04d9e0870e5af8d4a1023e7580e35387363e7520e353072f3e75d0e253079b4f1d967cead072f71e3b7c83d4102161def80c4384cf30637c8691c167189ccf1c94f8cca188cf1c7af80403039f604ee0134c183ec168e113cc057c8279e313cc113ec188f10966069f60ac4f30557c8299e2134c149f6096f80473c42718203ec1a07c7e39c3e79712f8fce285cf2f1af0f9858a1f00981bf0c07183832f433ebf04f9fca2c4e797213ebf6430bd80f1e965062f2ff8f4420ed160e250864f1c46e013070ef8c4818ea792a8f05412cc5348753c85f4796a8a1853484fad80c60a5d3cb5020e4f1d85f1d4d1104f1911e129239593940a6d3ca582131e3b6858c1270d29f8a481894f1a92f8a4a1884f1a38f049830f9f34e070000c09e3ce23e51ecc19a4f89c01c8e70c20f89c61c7a70c23f029431d9f3168e13386393e6348e33386303ebb94e1b38b183ebb7ce1b34b153ebbc8f1d9c5099f5d92f0d9e5079f5d6ef0d9a5003ebb943ebbdc7c76c1e2b34b92cf2e463ebbfcf8ec0282cf2e00f0d96587cf2e3adc79f0e8b181f718e2387674204c07e8176138f5be68433d1cf5be9042bd2f70b8536f7a51c7a9e70512eed4f342a49e173fb96fea828a2e3e7747830335b8738072e1c6b968f91674b6f8b640e2d4ab93823a719c7a7560ee1b18630369e60664eed41b430b1ab46861510b1ed4d300111a08720db8bbd30c84c900cca9974518ee59a4a15e16a1532f8b1f2ccaa0dec4624e0d0b19163f9c7ab32bbaa0deec8adbbc4235e98c413d3a6adca94727c87d86013a597c62e0e6ee1828817ab30ba031c79d7a35b853ef023077ea5d8008f5b418d4d31668433d6d8196bb5be007f5ac40425b2152cf0a1c55d0f159451977ea69d0a30a24f70a88e1be4b12df921c9a14b0625220cea400035cbba36afe8d9d848adc516156e00ec5dd59400512274bba1f162d64bdcd3b4ef8b9962ee8d9586a77d4ed87fb4caca81c5060505c505f505c505e503848600aea49e0059dbb8b7d54530a3a6005a570e3d4cb34f01449412445cf942200ee4e6dd7e20dde326fbad2a2584125112040047ee60386e8dc4120055a0c77dfe21302a1cedd0dee8ec3513694b7ce0710e128226a40d180520285001e3e878b3975dce96d5bdac498215240b38480250d1a4721e15db699fcb06697ede2fc042addc03209635985896259d454dfc60e0e88008bc58af144e1074019004543ada67487a290e21e278b3855cc38108833c647b5f251e1de47b55aaa6f547385ddb44dbae96a54b56d3dfdeb7351cd1576992e5356d2a2d06bb131fda0dd613500cd0ddc75d436293cda69c2ddb9f87cc3466725a128da1a4a800103d0b97be20b77e7d1e2441927c2ae13ed2e592cbb4b3735f86bb1fa7513c6b29d131640858e02f85c800e3267a47377829312911cedcc9255067787c1271b14b83ffd4bc764f3c45de73aa47fe3d77d16f26f4c85cd5c220596076a08502a852efc4a801239ce4cb386bbdbae7d551cdcdd059f6940b23ec53051eb434c24c444a3a46c2114c22ffc92857f1f095793f43b5523dcdaf8752bd17d52292be56f02ed948d5f196ffc6ad276697fd2ce5d21dbb522c6302cd3431a202171ddbe720ec4116ddc8ff0e2eef388a623c0235a78d315f556d43b8288bb1f61c3881438f5f0ed89ae8cb880fe90ee128f20ce880318e1823bf556d4fb0162914580e14e875e46224db4f8f5b60ac3af251e938835ee64098b05f49bac619d79de1cc28a8c6dded5863f8ef69bdd0ad637daf8258a65d1c6af35dcfde6530834667047c9e08e8ac11dd5c5dd6d14c08f88218c3b143e013f3385f8e13a77f7475bcd37f2869d2f3b3284471a937746f200394f13e7019a091081c91296d698bc01ee46dc3dc8533c98e14ebdcc0318b75d1bfe8338c36827e2fb4d38d3a8b64fff3fe6c09cfec2cfd34bb0168568b792227545880e36e8f0aec3b3d58874500f1b75fa6b95525d622c7ee053f9c6df568876b68bc58c65b4d3a5d6175b5dd66e78ebb8dff685f5c5cf47f5f9bc166b4db55f7ffacb7f2d76b49bcc45366f7debf831daa5be84c0f5a8f58338574bc438296cdd5d2a85b6c26c687277c9f4be56849f27d8e67a1fd4b4d21b58c66807e4d530adae663f40fb078876b68e56c3b4aebe4fe777c29f7112edf2ebba332647fbb1f06675a1a53da3ad22896d68a9de560be577728df5465735f753a8588e2508562a62dcb98a8462dac9ee11afbe8f46b1651a2862acadae4fb44abb77aa2f524d8ffc586dd736e923d9d25d921bd32ac549b4d33d9f8831ec637ddf97e5d31f162a8af5cbffa47106eb96327ee4a92d46eeeea3dae2f44551acf485389e033d4f967a61e76eea05cfc3f9afa86619cfba98e7c9baf841e62e624d535a7eb8ff502850a8cf082acf8b220bf2bc1c069cbe131e498c5fa4d2bd479f8b70653859f797a2a764407052a460651ce765bfc519e1a2e05838117043701bb8d1cbf39a38799eec2da87fe96829cce550d21253a831d2d6652bfc271c5e971398f20a77f7a3528e7735a9b35b3fcd89d4c85f6c4bec546b14b7281df1de788876374b73b9df96aba32e712e865b34e316c8d26bd236d3fb84692e718eee5ae29c11ed7e76ed47e90ae7d79224278af57312234a4bae2bf4256424a34b2e99906b4905ea1aeaee93b856c008ddb285864fa144a15bb6501f95b6dd7fae751ad317edeae755cd5d21dad59c78f1f7e9efd32b5dabe10f6bfe719743f4fb58e042295d8574150b1aed2d46d624289452fa0211a52f5a72c420bedff7b9680835dce9f105ee3ec553634f0ff5423b5bb93bf6d4f8a4fb5ad5252de707cb10e77dadf8464837811889d2d96c564b23fca5a77f95b154cd0677ef526f029a71ceae0fbf042726c4621d6eaae313be4db4824f6a9a3b52e4c404ad8f38312116a336daf55c5b6fb456f74dd809bce96a14fe2d3f14b1aaebf4b6a3a747a0ddedd397d62cf55151da7d4b9894df577edfd658bcf4fb7262f9f9aca2c882322efab67e5ac335aadf6696c6a02585827c5494d624da6d6d45fa4591058d7fdbf9da1a66ed12536cc3e121caba9652c1b0dab547b4d4146bb1d215edbaefa3399a4f4f1d55146fb226ad7649fa5c4bbbd651066fdcbd7a4a005ab8db306283de74bd953f6091a1d4583e2c312c485888b07458565848702731f511414595501f11285d89403d12d395085445a977db369a054b0a8b06574ee04e3d1f559ded6b3d17aedddcb1583c37522cc0dcbdbb75074909808521eeee368aec239cff0aae268de51dcb57f8b89aa48fdc6deccc2ad2c5d6095793dc515e6c8090ab51a83716634ff816c32f9cff8a10ce7fa508e7bff221063d5c1d67a2686fe0be16b7c005f7e36ec6dd8334794ac645e72eff0ddc75bc21003688a494caa496a4a404e0c5dd53302229180ed88e1f71234ebd8883e0423c88f3701fdec337f01daea3470ef70be0a822c49c3048806bc3e587194222dcdd35f012a3c2532e264fb9923ce57279ca9582a75c4edc3d478a88059e221a81a788bcf0145106dc9d071a25784aca124f4939e229294472dc40e140915d50472cf52045851162f057b85025642977da04091db9228915a8ab3024c918baa3802be470a002f93163078b112234c7e7ee4b5c8c0c98f9a1c20eca63c5776c98c381919318aec2ce034a0801171f4c4841c916d9b1610e15452d18010b4d4049498640918690231d8e40b8d1e353c85902a578511842766c988312a3884e9008f87802062b34751934f91cc15386112cf4283ab34151103c720c7965227278aa68871d1ae0d0c1cad500857a91016cb879f1137462e070161e243f4414b8d1e35758f0438b142fd239da932222d082672a7932c4415095177e8830668c1d7722c56ff4b8bb8d36eebe83fa0203125374f122ec01b0c03b0a405440e6ee9efa826883281b08e36eb3b4b1b4e18ff96d96969f24ed9ee53c1b4b91b4b7afe5bf760ceb2cbf286e8cb33b6a0c0c15668c5a4de9bfe86db9de88dc51267047952063eb8e22014a0c8d1ac12d6bee281184164485608342bf3f831713a18218321b411c2a8c0380c0016ce47c8893f47f511358778c1cdd511f083ffccb24d66fe117ce75f4637d69b116bd13ce85b14cff17d9ae7d7d88c16fe17c9976c6642862a317695514cba2f02f933bca033377549ada677714181db00e407d015a126f7794175db88776c60177c78192e3236825bf2edd515cb8a3b6b8362b5677d4123c51781dd40678e2a0bcb88f2a8aacb36314da5d6b9fa08401c5003e1654515a4853415d7439a80141628260accf64102b08472d854246a1a57397f2caa8294f4dcabbd194f7937a52c6fe9c7ad2060aed42b0d227b8d7c2fa4c3e09d24f504e54907252270aa59e130638619372f2038d3971c1492b8555ea8414ac4e68536b2246aac91b2ffc307c5951cc841ff4dafcb0dbb6759f8fcaf3649ff77d54db9d63e58751fd355a7ade87855e1fbdabc6d524da65bceb6ceb729cd112e74a9cfba2c882569f4b1d5aec54b5a63859fe6b939674cfdfb6ae6415df849509b43e997fef1f8a1d3942751d2dad61da7db4eafa351a3e6def9eb2672c6f75834fea6bf3eb9285f7e327592ee3f1c78bcbfa9f8c769a8e611dbfe79f7edbd2854f5ffb7d9f3f9f996a2544bbcf7f7b226b92a69fcb5d35cd6d5dc94a75cfaeab9f9512ed40bb51a8d4bae8b5b42cdf8725cbf76111af12de9476391aed2e26cb55c6abad9ff651b2ac9f7f04edd6f8daaa0dc35bc91aeb436a860f23f0300326421defbaba302542162baec2cfb409c422923290bcb94f62243720d1924212ae52218ce120a659b080a910429fcd5247e87c1fcd189632b285c54a1939725a0b9f24eb532cfcdfb9a1fcb06ee76aaeb0c76048f1f6e9b05ea968177ecfad59da69b23ee914083505420922c6e42c00b701ac34112b0020048047009c7a17eb3acef47fcdab7d9e51efbf468980e1d45b525a5a725123468c0041a48bfb2cff4a496a080c435a48113172d7e3ec56900812771fe229223cdc8ef6c5d9ec7505675b67fcf48118140a14281fd0f9c0531fa8f1502c719512fc00e883221ffcd0b3940775dc3de5c111290f6070a71ef5b26cf65fd3ffb554076b3ad852d2e82afc8cbfee568a43fcfa3a40a25e0746521da43ac0911a2202779ffc24cea48a76d433f236e73ab4c4863f1b41dc909e21448604a1de6c45531cd4a1f888140739a438b839f55a1c3ce14088536f45e9aaa4799e66724769a1810c6481c515743070010b585105aa02ee280ab8a3a870474de0c7faeabed9278ab56bd1206a94910af20137a25fd3fd217e1dd9ea502a480d30a482e454905b2ac8476d9546c3cf828596d10135853b4a02ee2829dc51114039209552c3dd6d7588a260abb40ff16b878c542a4967ab347adf058a02de21be5a4c4d3a2edaba6f977a44b95b93f0be96123dfd8b6e7bcb254c7c54aa1a26819660771533d6f7495c69a54ef540e33af74da91e425dee9b48a05f78f4644e96fb29dfece2ef06e6fa9ae672bd9676edaee437bd530df7e86b6d4fa3556e4fbfccb24fedf0e2eea91d5adcfdbe16fcb928b50328a8dba804f447d5510a15a2110000000000b31000203824180d06c3e190583ea4d7011480015fa05ea85c48168949948314528618430002000006000000060c05f108e2a2c0fa27e23275c813160f8790eaae5683035ac3f1ed0a5520b38cd1166dd7a4b61edd93012995edb2959fed232dc876b7e49c82be6346161d5018ed6f17dd281dd657835718650693cba7b933284500ab1539dfb965a99e23433dc0970e292bb4b9483a6ceddcc954c0fae876e61e5118334f80083f464fec9364bf3a59ca49a8ccbd231822e84012690a361712798c1a5dce3a0265932a2ee0f608b22e390e99a18288baae0bcd826a09dd5d734bfad65acc3d07bf9862eb968410dbbff638f6dec3397dbd0b39a6845be2a1ab67fb26fdc4a89c6a26736d86a9bf95cbb420abce292799a34e214c5b74e40b2439e2e891a1be9da53523d321eabefbd1806c97aee584cb3ae84a273da6869447fffce0f06a0cf383b01a767d78a7b09f9729f85f904ccd4e4e6d1fdc62fce6b6c50fc2683a4e9d24f94136995347b7cb2e8a6c0a4dbb80cc8cd33444bc6f0eca02a1589ecd23a9b2662643f8ff66cd645d65fdb36eb58179ea71e3b3c2e874673c7850099223811c2d064ce5b1f7936470ade688481cba513e7ad2589b6753a585260c72237d019ae0d9e985575d2690098914311d4bd726c322560410ec4435cf26929536652669707f76322d892972fb32419f44823a641a34e21491cd3a2c81d4cb9955d4beea47fdf8294e34a51afb1aad50030f011a211c4d82b02d209b85a5398993fd0b74151a21ddd2284dfdb657a8372a5fa9d314aca439502e5973388d03c21286db1bce8e78137961471cdb80885842eb3129ae018801de6bda021d41dec2a05074a6057d403ffb27031c7c47c1933dc20180d158157c9ec605c176ef4bfe6df1d93a91d325cafc1d8a6431ae6a3f88dceb70a092d58b59000c437e6af8ff5775b18332b38b262e80619881783948263c234f6b1052d09d7032dbee6d482119135b6559c770be6182a52414f541b8350b331246f61ab4a752562b1579a8afb8c4788f0ce69f1c60a5c14658286e89142b64fe7a97a09602a175ea3a296d1277cfd23c1cdb698879be58ee263d704f0624a37e2f80d5237f42606ad4af1e747b53a99ee2978e14d04773d342b050f7229e22cdd40fee8e219cc9c143ea77fd4eaf26fffaaa5650f28ccf7fa3b53668c31345e9dcba9ce140c96187398c9c8c9ea61f3fad8e85d0214bd09590e57e20611bfdc0fdb09aa974c8c2c39839d7c9fb19a033a6bab3fab5087241c2725ce3ff3709f2746954a71b6a58fa3d78e12d354cc520f0a75740095861af0e1f167573365efdcf3577376e2273508035a4c11d162bc265131c89e23b13ec4a3bdccf99c2b43f5b560fe34840bb1b70d2e21e134a98f3440a72c7a6e0cc9e25f98992454364397f420c1783e19382eb24496328aadc98cf437d5e678a2f28ebd1ca5296ceee181d6e191ecb3c34b54259cd2576b62b04ba031f7ab277b572f2daf5c89e4e3932003777d227748c1abb43edda9bd7367fa4c885a210db124138552e000b8d8e748ced43eede885849c7de21e23ff0d65856ada89f3ec1407ed72c5107331b8a505c8e4694b302dbff7ed0e5870354033159264866fee9c4f0eb75a2d6e96736f88c4d6e2a925b67f70495a02e9c0f8dea12d6860d9fefd203f6e12825a0ba8bbfabd5569e7bb71ba6e627d8e3eabd041dd733da2014b68fc9f09dc7c0ca1ad366db80e0c42c60d56135659f3b175784c6c8b6ec711d013747030c548fd0dc6489aa9059fd065853352d69a101c11de88527b469b7b4a9b7c2b5517f3432da0051ca5e1f2cf59703833c651624d6cd888b8b4b953427292b0bfda063a13c6f9db9df596b7ec440bc640b5b1f4d1aae20ac7dc7625264ee433410d3bc24200b277263f0fd94a9d9e1c51c7c0eafb2ca2eb6b73c6d053a1710856230089656cb10b6033760c2acf7feeed160c6ac0ad2dcf01b00e757e751b972a814da1ca3d42c5d2a0e5f0ff8be16e92cc79315615f28e5e864a53ae87099f18f9b6732a42a50b35ecb270d18fffceeed261c34648c45eb7734cc0e82589a3861f8403ad251527ce89cb00558f8b9a9ba39f94ae2e9e711491f51d1abd404a7468ead47a8f47985b51fe5cc44355ac9ef74e1126b6aae28f4d5e421af62dbb8a093e37e37ce1e2858d35809490c4e09c257ccd51ebd56f8a6bdcb116c7cc7c1e4b037a74ceb2a64244674c0fb74b9f794dffdd0da42f2713e01678e78a0056088d25247bcd4e7fe4f0ca95f3b0c84546f1031072cdd354a6947103e04460dd78d371e2bd355f0eaf1d86199520fe98dd97fa4eec0aa40a739407fef4a0b4dd46a4f64b44f004cdb4e98984c907c1c344a0e039f0bc2fa07a90d93e87381cf2e8f77bd70fdcb9838a5d72843fc49ed5b5cc640f380088c796bf3e876cb434902e21d31fb5fef6dc5e6865af4430df0f16ec043424817510d258b44b0b33e57a9fb4f69155d8a8734002e53516a24f0654f94391f6702dac5d5a4e3d8e7fd0d70d06a717200982d37266d555fbf68e6fca3306d61e975ad7b6c55a8c6c3e69f56b2e3f5974c55dbeb6ac9d9a1c0037daaae69f6bbdecc1b3b894c9657528f9b04972822d0401473cdd07290f0bf4054653481f19f4a159c8c7fe463ff88f8c3821860d5c0ec440a2542d160e903089e176399f58a5d724d6c0ee8affedf4446578868a90b57b40d4e330444f3e9a4246ac208e5fc1b81a03e0929030701d9bc7b66e020711d67a4345fc427613ed676c87ec6208723f525ad30aee001844b29643f4c210b4d401a8d08655adb1439b4e0713a2414228b936d000ab14290041bfcf44d80a427442e86299533039bc85463dd7284e07936dad702959a1ef8f947d0e130e9f1edf98863c9ff92717f2ec3673255ac224062d8dab88350fcef16f6ceba41b755d2940a8a142950ee48c46482094600730cb5fe6dc8875de64a3c5b71dab8056c05cf849475c54783b0e85e72917a8e6900f3f8d0a8143de95cba56d347541714799fc87f8b72fcbe3eb78119337fb10a4455f21d8a799e984bfd2f14f755c6ad73e4438e2ef12d0ddda699d72586af11bd9618fef575edc24f31dfeb8ac6659e30900fb209330b38ce1df81936c59d4dd911c3a025d1af3b1ba827ce1104b0e42563652ba0eba2a0ef8546029b00ec1dc9a99b687ff034e858dacccb484eb59fd23780fe35830e8112596b53ff4a648cbaaef992c08a2f3efd4f063745f80c163ab5af697dc54fe37d440d6a85a6c0643ab2352cad8067ce0eb59bd315ce151a66a29438202deb6bc954637c104f51ac074cea2e54005773be881329ee89f7e89761ebedafcc2ec990fc1a4b6cc418640fa4fc8668f7a50962b77dd35bdb62977048baef33a460eec8f595485e0f0d00582d1791a04950e807d1b30a85f67e3eaf2513f8a2ecd47dbaf97e67b885265756108f60dbac7f4e884589a5acf61bec81a564d70814a588bb2eccf4dfed4d7fd864754e79edf85695f24615a0cee3d173fe88f106a79b0102c61f64f12235af9208f28d70c33164c125b06da0dcc518b321158baa54248e7145545ea242e3a91428bc1b986eca7be150cc5aaaaccdad116d8478190d60078807b12ddcdeddc3eab1db015fb92039cf98e97d0acf3c30aaeb7834f66ebe946749bc0708900245fd7e9d942beeefddcb0747b4abab0b4a0ce2eabeaac9bb8ac74bab2391c8a68777d9b03a445fc5c5c53f0a8fc2973f36da5989991ab77b00021cfd1388cfdfd6cddf7ee9381e0671c0dca0c589676033210bbd8b226ef87ecba77ced76fec30829251f1219dd48033ffd24723cf8008dbf55defcc40110da51b49f268af47fceb74aa710b72684605b345ec419d67d170a1d4138e0b774fdb433364b0f73be8611ab24d043e01b6fbdfc53ec748939b009e4fd55e8af645b16006e6027d17a7ec32ff278279be74c9c47e0c27a3bee42f5c810e6db9fede02f0677e6fa9f69221d6246d25171c1557740bdce34f82663d472d405382078faa5536ad1eaf45b42f509b407e639ad7c103a57c39c17c06cfea7e423c44270fab6cc40052fb94b1dbcd82826b96e97840f9c7f6409006bfa61051a1f9c877f1490a17bdf2832a418253189bf8e6b84447141079b6254c272bbc6ddc7371a134099654676de08627bcd9dea5aa6773c02cfbca8940ea8ac86506ba2d51b884dcb7f846633058870f0bfdfe855d60ceba0bd79f92e7722ba377a05029e37cb551e3b8c1fba4b677802f882511bf13d1d05dba732f55e9bef53781e564b3714cded0dcf821ead4578d66ebc8dadf74d195fc84eb0b10fdea6be4057b0cd5e2aa7573190b417f74b354d116690c5fb68c732c6a6a8dec755bcee6f36674a619660aba1d4505f5229711dd20ed12041a7373b035bdcb4d2c5cf68fd4f341bc2757d33822ae73a0418c1d6a1285fc45570e83fd7bcfcc0c320fde5c83507074e31fdf2e5e638b54163f1067024910530466b87edd33fcf7042bc7b8a941a09b641891f042844a2ae526d04f85f3edfd1df48563b093a9c62b561a7af2e8c7144b540cd86717d363909d4e1562fcfd0cdbc97f9d34c84bf9e34f32afd9c1a89d789e0bb45114aebf2f3c30d79a472e930a26b183e295b1fffeb6a770e72f1cbe324e385603a0b378b0dc929375bde0a182675b1e94edffb4dc996d9e474283cbeb7a6d306e47723b990f002ae4d2e18b82018bfdc2ae841538c7e80d3a3459da2f824aa7b4506c4bd0f7d4c4ce1198297ec722e9fc9714463c0036a3801961a29e2614145bd010b60a8e2cae395161430872a7f552f7e2e931f4ce11ae6ee7de44f746c2e0ec5c2c49456cf1899c7decd7fe76635188d902209bc294924b1d62601095b46bb91a12cdaa1a9f927ea35994ba956e266caba92988c7c3e909a05e5d0cafe295782f96394ce4cfee5270e0887ec6e8987856228cbd1bedec55ea4131e021aaa5ecfd2958e99dad067335b40b47159607aba2f4fe8f75426fc1ea0b953cbf44b707e96c72f88b6c945fa34afcc66d4a6dc695b985eec0dd083bfd13b224baf1a11478c25d5e805001662ffb7c0fba330fec56e5a3fb88e2e2bba51353ab215367160c0ec6c76bb9c4eabe1e1566d5791de6f526bd7c69abdbc3646bf169c38e1e9e175d81196cda8bfbf195d46c20c3e756043b9d5dfbf5548c03aa494a102c3ba50e4105147b86c100573846248fe81b09ea7c23fa4aafe6a6f684a807a9c3c9f9089d2bc97069929868354834f2c06e9983ec01d0bd763b3ad084e6bb496229bf15efa85ba33eeb8f74835c9b5bb993c4e08ffca2219e82492aa400d0c7c7d2f707e4df9b78d348c5034c1d1ad7729dbf6d8af854f6fb823f19ae563e4a4f5fb496ad02e01a41192bfc1e918c0835b3a93f0b8112e266bb25a732a11ec1119cb2be38f20f06e16064226cbad63527387d140c210fc5d4f7a84d4c5b8cce4c8f0309f6d2fc31b0672864ddad848c9831183252013720289aefb5080528d0d81fdc52bd24956164d980992d7e6b75018ca17098ab12758ec61b386b68764e5304fa7b0259046a8c820dc1f59f6699ea0a2d5ab5a9aa04f6c1d108d62f6e787f48363672e96fd6618e2f5a51231d47f0970b180d7a6b9c5a4243f89ca3877ec40d118e6f6f8ae294caa33f0e099506c3138c1945bf0248e5abbc28403c79fa82d1460f48b2603a55befff5d867d1dbb24af65833123f734bf614d5b0a372df72182fff2cd2acbb77d5e40b62f8fb5b21c833f7d2525d516f23abec2f98067fbab67d3eb954986411c2d69f6da3843aa6ea2eb8627b9505746bfe8cc800712bd9677caf1c87d5f014b25292fe1cc29ef4fdf89b6dfa36bf044ed3743eaea6024fb3ccbf1ed188a6668c2d0b19bddeb0199d9502abb7a250ee42f9a6d5a246cfe3a88d42ec80ee176b5822a5b22a8aeb1e8fe6087c32926b4d59bd7c77034cc0f1dd9af431e6698ee06d8793a96be0771b96e745418a191813f34340c2881f62bcb20047b0fd1d17d04e3ff34aa0c0799a54ca71dc5ac4cf0bf809c5ff51fda1fe3703eff909043bc0e035d839affb59833258ddc7d84e3d7666e938edc3909c7af4d67ef95795cca6968d78430df1b7b74cc8f36bcd2f9e4d49be72029469ade82d9638c5beffadcb0b7e3cf0eb6ac05f76fca29ba39a174793acfa68a97f8bc359dec3812caf6432087501d1a3876978707280dc2bff96090972b0501676b97fc5b3f1f7a98a794a9242cfe3c439e82fdd74178b1c2561ed5a0dea3d134bc19f2380a1bc84575a7bb12dcce17907108490e52d06bd97bea11965adb6bedc03a88f2c41e78aa788ce77a992f7ef2f3e998eb0367f88896bfa5c51f1d4f573683d7e4531ecd7d8ec395c31fbfdef6916206921fa8b892b45a35e9c4be5db2b72c7c7a5940c318c91586179f0df70c43197c0efbc86483e73f71358b3f1d42fc84152f86147c606f1b2aaeb581eae1446be3df8b115975d442303038d7c710ba4d21b480cabf5f102027b6fca72958587bc3e0933b2d35971a5ef33168e36ec887a43e23d941be69768eb2f14abfd2bef11b135baa5bad7c64e81668bd44f6e43a2d7600250f5b161675f0e2539684ea994bdc84d924c3829c8c0ff90d7c613abb7756e8f91644bd25784c91d6747cd9cd06ef5d2c9181ca07d188e4cb4c8dbcb473bee5f1052a0d6a3788fd72b56167cd0cb9ce8310803434b281050ef6570f80d0afafe5979804392ff7da79307f7c20b25855a064fd5bc84a097e872283542eee33242da0eb9f3f83d049d6b188e5eba810f3629a611c03afdba705777d756d36f3aa6ba8dfbc801d1f8071f8432fa0b6a07ebb923d64b68cb5f3b04ed3c3459edfcc3e7eb0eabbe8b0109ff2e46606e621b531fd781de588436f6fcabb81fd56bdc8bf887d7e1b7dbfb1643f481a683070b611e38558d6577ed38c25719d3c077c24ae7cd93e680c08cd947455cdd763d34706f7383209c884e2bde6103d6f76e0df72e8913bf88f34caaaa1c470d88be6e7119fd8c44889e19f70bfc943be8f7868bebf63fe3ef9b62f1d5e0c1d468a4a75a7df57cede7ad85d1afaf44f49284846d50709f6615e504460b48d720c419709a7a3951b422a2a20072d6459eceb07b489c3b8a5c8a414a87d6bce0e5db95d0f0db7c945db5f94305cdb7e7ac22044718daaf8a1cfada48ccdadd6746061cd9e4892d3e3a43544911f0addafbb0f0d904338bcc2f34236d8d27ab32bea5fda8b84522c502bb4d89b1e59f8563bada56ff3ee6b17ebf3fffdb1779e90767b408d20a9fca41e1c9dedb0b9c569f67b66159df538a7dc5245dd00bddf78cd7bf83e1d1d524a6fe95b14c947e1ffc2d64fafc1cad76f3a23cafe0c763ae8c4bdbe2b0ee694e7e85027d187477f8edf0ba68544cd65c303077dc1e23d64d0385060b08443dd1fd0e2303ee6fe9a0f0e2599cedb750592923419809c41149168599365fe164fbe1e8fea7c746a5636d7234def88f710afa4b9122d22b677ce590836f67ed7f399810ebf794c0ff7a40636ffc3807f0bf2296cfa1506f65a989d2e6827d1b7bfeacacd3bb2d4f67190171518e8065e6811de2c990d9c20440060b945bb1404c3d1e7606c4b26a77dd0222be965352d1c84bcb0078bd78a5e5b454178da413f7e7079d3d9da25b8e7348f5dd286c9bd5051b04c95633e2bfc7e7a61292854d7d17031377821ee2b501a6afd35b299232484d9e2b45028ebc314612fc60d40385fb8020f2c11f1e64d3e430c300537954a4d52f7e072633b5dbe0436aadc6f074013f0969a96834b54c3e65647694d433051bc1cc3d3c5f1570a21e180c1ebbe5ab926cdfb68cde6720cb6e6fa59746b4739570021e7e8b513ba3289599da75bffd44654ab87ece2315fce28a5482460a3ee7e2039d0a82f622e669adfd2599300a7938036e129c94be08d6670e6c619e820d646fec001956b39301edb85f286484c92e2b8c3d30260a873ae0b6bf266976c3db25d010050f40563a305fe3396c19e9364a9d890f4d3b0f2c88482be8a405b5b95519fc76976c45be1d945bd821f9b8e1121b71d68f8f7a6e338934e69296d2d82a086e7032f59ecafe67774e91810f65d073f021ac240d36272812bdcc0438384a6c7e7d304b0b8f6b1e8a0f4a39385564edd7055d4704c6e64d26d9d6b6057c59d4195452f9badad82654ed49718eef8de4be81e54865ed62e6e61464f0acf8d6f3f6533c9bf03543ef072434b847bf97a529565eb290815273b6f86fa0cdb30cd10821601ff3987840bbe3ee7c0fc21ef78fbfb1d2ad2b97707018e637cccf1050e33641fc78ebbf90d7945c36ff5864f8f2b82df28673c76e8a49fcd4fee4f85eb1064dcca7e0f26f4d9d91af1fad3cbf37132b5c549ba1510178e1c059efb6619cb45598847b77278c0bbfba45f5ad9d71c9ec3f06007ef279d5eb7bb832fc174a97aa14cccb89b0f0b512459ac82b761c57f2586d1f8198b19c5f518e40bdb9c9244d4881bde83cc26041c138d11992b3e3b275fb14fc767d6860093c37d8a9d6597d9dbe9d019622f52ac08e99390414a4098c26c5fbcceca250799e14ea0e4be621d2ec828f5b54ba54058a1ef1b7f44af3e955a1397d061eccb53348e9ebcb167cc2e06a6c115a2da0b0f55f14e8bca5a1656a34d8c452c15515caece90bd8c4d764bab380aa535ae93c3304f0e2f13d7302022650bae99a23199081a828deb5955738667c3bbdae78606dbf9d96dc2752589112dbe8c594665b900969fd08c158b70a83a08d0dc9f490d62bc0e5b5101e2f32bbd23313556e0e5d7f9fbbc275d043b9a837d3fdb7215a2793aaf465cd57aecdda38c3154e30ce6ec10d700e8dcbe815574f08a79d85ec78bb5e8a0269b12c2a4055c09dfeb1ebc1172d320141021cd8b14d5aee843169d43834c823be65e8d74b98a2feaaebfb0f55b5b35e284f4a93a6e1900f3d234ce1c71f66a38e0bd22d0d35fcacdc61171d714c72d91b44cae9030001c6b06beb0192aa22a44c58bd4b2c9fb308bf934ec8b84221ea075965c05cda31d151aa2e209106bde6eca4ab4cf6c787532500fcfce9ae6be96f59732a591cd4c8ac06c40b996633a68ef3da75e65bb36a93cee1e4a782c5cd06d7fa9c5cfe0516773dbbd3dac82f992e1c86ef0fe93883ca29c7c19e1c0f754859e388019acde6b3d722a9103714d16dc62a61d76bb583c44e88f57e762c9bc7537dacb0a6ee92bfc1a41952d839b0f45ce8ed0575bcaaffadbc17fa8ba6aeb35c5c8b8dc8c21d00464bffdfafc6952e64f4322fb26a860dab46838be5086b5776c55190affe7569f0f6c0513ffce22fc465c5b1ef854c729af3d73ebab37fd8afcee052e23a3c392b5e5c86c58398e6cd22673bd998fb7808613c15c76e474cb305dfbc10bd37e8314e9bf5fbcab1e589e7cb2aa58a978eff824ebdfacaaf05cc7ae5b663d6fdd89d465509b57f6147b9349fa78ab19dd0f89d8e185c39163e6146c1f3a3306dfbe80a7cc397a46ca9824748e00211de1883a691dd73cb106001b543ae4cc5125b9e2e0e538c728dd8443fab97d2bfa8f0ea938186f3c1510a7c36b252efb185cddd7aa4f726a7cc32eb439f22a0ab5adb910e385f5663447f3d7df06a6ab5222072c69adb29068ebad421b77ca5deb907fba9dfc6295c29880675e1d822da0446d36f1c1cb8edc7dba7f0b4e7bedc9869f70ff8f8ce05455d9393e4a2420dfa805ef799b40f69d752afa17df2204b418a09796ccf8805926acdf182765291b99d58f762e8b67aa04d84760cb3f9bee751047ab497161dc3dea34fc8cf134874095d742b5bbc5234bc58a55d239c33cf3c71d1c13b2fd2c08cd52ace8ddb94cc076b46a76afcae7582228383dcfb49ef8133bbe010d528149346ab7aed2894f74bf06cd6f63b173d055c6651086b33a67163c675f44fd0839fc7cd6a12cb835d3a7706cc7c3cdb6c7c44e394cdaade6b84d17ed918deb4dd5fcc374126dcfb2b03795bdf6508264d56ff454ae42dbe79147b5726a6e7d61555741dc29f6b32dad814bffd01c5ab6c4e1ece03c8b9c6d28c3503db3e1ba4c521c5e87c7469911682530e2b7db0b39bfd82bb71a667aa4ce31b1e3abcd7fff1c72b37b9e47b45d11beb8bc4303887f06fcc1a76c98910e34787b5bd21720bedc44f76bad2de2b17b73456e108cee146e1fa9edb777c6527bb0c0ebb91ea94e6e386fd1240338fb2eb734e78122bae58e1ced1bfe9438c71dfe635c4b48e69d04584e70b0f921b392e7166d1408cb4a556c6a7588bec890ad5eb3b88e4f5e87f6015b717ec6dd815a369ce863e0b3ab65f4861f5206f75ddfc8fe48382d1abed6e8831d919227372c8c3788998b44da61b66cbeb6b568070c6aa09b34b1a2582898be304869431982fd42b1caf60fdae4e6c12e1ff2031499d076f5fc4ad9df48c9293ccec5356dd92ccc03cbf9794c6cc7239b810a46082477bee5a83a962c0301ae7061512f73d52d77f2c238763ab4698752a51e179a9648e038aaae70562098d31df9cc868ba6aa11bbdb9e9984a571666002691433214053fec7bcb755a214d5f4b696a5fb7adfa47778c01aebdedf252197af2d5da38fd09f27b6861a3620951d7601a24a11081284dd9aad9caa85c5b971bf62dd100556a3e932473d3cf059fcefdd34d437a3b5aa194d5f18b40855b83a25a35473b38c24b1eb6acf897eef5872bad2f0f5bd8bb58f0d500c6f8ef76f11f17b93254144fb269fd43e772834bb8c6ccf019b08ed47f43b9427e1ca0fb19d27bb61b84203ba5326dd260f5864864fa9e4ca4800ce2c054e0404f6865eda585161a61e613e9531c740fb0351a21c6b5208c7db2f4bdbbf9622e394bf9e13384f318dfa8a9a9f8975d52d58f89a72020fa17d7d8fa4f85ec2c3d4c8fd207986c93ecdd3ac8b388d729d30babccde8dd02d5860e3af18a134edee12f100d4709d51b6a1ec9b4cfa324f849fc3e526c74452bf0ebd4a7efe91e3221ec0127d22302ca9d37f18df55fe928686b2918fc43c9d6a4d452e93d3dca588cf68a45a398b3b2e31c05de3cc55018d32822112822c195e63acb99f956f86ab00db82d149e119b836243080cdc7e40817cc9f3df4a5a0d540a7d8e2796075dff1a820a13f05b41c1fb8b7705bbbdee6be35720b276b65e53a51497272f1e9cd5b98339704ea0544819f8ca558b121bc16b42e2b8864d25a7013767f6b5d9ebfc609d33628290202240e982768cdc9f734ec702b90a5b5187bccbb1de0d1905ef282806205e90a67a01f5eacf3ef302b285773cdaa0d08f5dd7873d85666562e99128f294d9cf159bc55c9c8a28ed57e2261ab13a5023234b411babc1ae500820f1084cd12317b7df80101b547873caaf06cbee836d10ed8e44aec3598fe126d1b7e65c70c121741ec35075d48f2d80ff84ea1753725385864af4ba969fad521ebf30335a3e38cb82710e42655470ad2710ad1ad280cddf5b40f26f01c4c7f49d1b64a5933e8fe880dc3710fd613035a3290196cbe3b8bf8ea5add0b42b10c13ba45a5fcb2f700ac6ecb2c50d97e6af9f4d76f16eb1e50fc6511dd700d1c972c35b8e62904a0d0d8f64d31ea2b91e4bac29c7c6735069aaa58a388062748f016a79a8de0fbb035d75cb9de73a6b6ed58c5c947df8836d05e5c0d511a82872c046fd4761d1d1e83bc0381be0e18d00936d29369f9b5938a92201604a92be1079f8e91e7fcf7cc0b4565222e161fd3c93defc85fa7816d5851258dda5592da5283eccfe96fce10d318f520cf7731f43930531437d3719424a96c95a620f994280896b61106cfc3e7e3baab02df2c7cfc483a84da47e73e15c39d0e004d755ecc4c71cd50b169ae37f3bdf0a6982498260983f5a301136bfe63dc4874acac3cb626cb3a83abfd07897e5062b1b1828d00377d086185eec624f3cf2819079a31186062b125274ed89a9ba6be6e8537cfb51cad84c3161a08b38a5f5d7e700c1a103ea2f4f46f2e1958bf554a9ddfd811bf375ba1da29350184d433776c12ed2fe43ca160f46b87aa02f4978d89fb58342d0eaf82b5c452baad8de6a2f70e84daee54b27e1ab6aa19144c4079c5c2f971221bc087ddd8fdd2f7abc589fec97c63916a1b7ec6ce359bd10b7ddda10d52e1941a2d314f3fc21fbb6cb358bd6b527a23a10349b697e7ea5840e4ad22e75249d0829ee8249e375b53f611320cee8ab3be62723f6f666462405f61635fa088bc299fbbfdb67af0a941a1ba0879ddd5f24e0832dd27f4d37a2f3565b0f744ab9ec6923f0a35783636a43bc0a7a5b3aceb60b279982e67b2b43fce5c3794a88749fcb8001c0d9c068f683355569aad090b477bb08d242bd219b07b8f301f7f328d3dafd750a9ec178a10ed50a234f203551934f96d9d82203b887a1a531dfd5021ed8aa264696683c2442dc06e640f411e99aeb25dfce6883e4b20e66a12848533f9e4c179e6c667b1be8cb7a68ea137e9622c56c666f08a0e78f32045c7d239feb2ae61858333402d2600b2846e4ab26b42940cf9a3b815c12f56cca8b41f172db2c0128ff80d9d5772315768bcff19af2c3ada97776a68d70b21c6b972006f8ded1e71b6dc17c2598a786b53dabc99ed86f271dfb80ef630d8697c0c37179145f2dd851a398cefea9703e68146662d8844cd9d1540a4fb9acd052cf16c56994ac9e9c3cb4097759579fd3e056a9f03c8d7655363896aa38bfe72598aee468bfd1c9f5b4c10ea189f2dbc33f7c80fd45e545e1b9736c8a047f2700552e0eff2314c766cb61972041178643f4e23d5c96fd9231205d38e3480f56d13c19c1e2f743e0cbac20d3f2576f8d3793040aeb255de3662047805975363c6918ba5f8e3c80757bb282b123dfa51efd8d7b43c5e5dc7258c9bba0f7749b2ad5af67448a23615b0dcd43c559ea7e74b367f23dd719f7bbe3fa9ec5b2a4a82af9b36608f8e9122e362b612abfc6a5793652aebf021c118145c12a7029649743ba3f2a21a80df5a52a9215f584d194fc777bf9d389f579bb0e06a3ee8b99a78b36a5f15c385ceaa92189261e177c8eca89489edffac2281bca61bb285fb7c867c75a7458a331c9af4465541be1873ebaac4197b89655e0f669f6018c421dd2e5c6cadc2b0a7d6e657b3c5e65a95ffc87d330cafc42f01ef82663b234e08e5c541ce1cca0e17b861bd7a743f4e68330b88444512744385d6870e6d2c0311dc257dee346ff47e7560ce98643ca316f5deb3af37d85f930bbb0ac671cfdafa36ed79da4edaf522e0464e7fc9b5547ab22b1423453a55406c02326c3ae636db885b40ecf4ca736d4b09984d75077e1f1345c48dc710eb40f842c8c260f318ead01c588e62cc310acd1088aa687716fc163773cb298f124133aaa4023463f0cd52d2eb7f323541877ce48cc03f075fa2591790da009d9975f94af8a3ae6f123fe09acf17892fea827c4bf06eee9cfcd1a76c446bad204bd3a66f8f26f9430ffbea5bfdfe0bbadcd676e98dd58b65576fbd57b4efe91efc20fbf773197bcd6a68f2f9edc33001ee6bacbe7ea6fecbbd36f3faffe35607d212a5fa8d6903962b5c3456c1f95f4bb285affc71a3739acfdf56293349b782752e69b2a6f0914c8835ef6bf7da85b4467e2e712ae674d5b021e0d2157bf7c6f2f29ebe957994f41ea2f12d3eafad46e31f72fa7f0a53994e96b2da8f49bc144e1e602cf07defbdef28211aeb6a16bc2bdcec07c5a3c06ee39a07dcac45d231e0c7406e1c3d5c94f55fe2947ffe0ff80049d03b24f147df9db9f6e871b027e139339efbfec12e960af07e90f257e142a33dd419776835c79a3f6bf188f20c79e303bf6074ffc1b5721d8814fd35d291e103b4bd2d1274e9a5fb8e34afc837a889985b76c1db0f63a3cf30eaad9df2f0fee218ab28175e53df0edf16b4310749dec874e288f5dcd8c47d81b2b7c7f7dd3b236deee72dbe50ff6ce4d6f7e05ae3eb9fcb4f0bbfa74fd8187385fee7cb829f883badfafdf0a27fb2af89afd045efa6eb072c4f8a8fc90560176a934dfe989b520b37f8fd5eb05e386da7f31a8ad256cfb0bef06f9fcc31943fff7b6418936c4301ab38a3f3df88e3febe3985659fa7793e992c1ba53dc53c7764c98a97fedb6b2c7bc8e4c28b679544f455bd99aa6d8867a1cf3baf876ec20eedf4b51bd848d9fdce0228fa3eec80d3ec7b8b20745c408ddefafec8f60d59eb0e8c6db91e75b80502714c93d5da526ea22c010339e3f9f75e7830e3f99d775b08c79da87fdbc3ccc9a72d7c13cbff011a8c942f504671f85ca5631ef43879af78afe1f48235db9ad71e3c6c343f07c3bfbca35f452a904b8516d344c433b9854e6de32fc2161cdee6fc3611e07e5462b14b6a1d75c9907c53ac2b7d8ff42299377f5dca0500c345d7a65fe41c90e9f4dfef90fb3a09832bd2099558e52ba3cb67077e741a0ffb8f96f3706a191e4ee0ed93afab2eb85f407682d0324fba4998baa08a3c30db9adf96d5366fac439dafac65488a173bda2396e0899eb46ccb9a8689f19dcefbe48e50544f3927366dc2dc327fe471ebb031ede7cc1788ae5a8b394cef2cbfbdd3ac608f1741895c6cccd2bfb64f6c94153ea45881a4ec60ec70234bc7be0032f783bb3e05e88977d4371bb8d8f9b1a5a01c8ed7d20357663f2a2fc3f8b05755fb3fc7f28dd840154385883cc606a8cc6c4321ec8bff5643be57d4c24309d55bbb7e3f36d943194dd430874266be3b33bd2f463fac2ca496aac392ab011fe6371ebc37c46a8d072ba49e6a6489e05813f5c90703a2a6bb7867feef1668dbddaeac7582ab7ff09bcf1382e2c4ec5fec1a89dc99156fcaa7258e8866958450bcfb3e95e7db658450c3aacb1c3cc30e68c7f1bc1abdc8e5f10ce50d8ed7ec1a8d6e734c9dee3b1950d74c832191d3dd1dc3a0cc01e006616d6741405b0aa9f6243af1a99c2980fd7ed6e64b7c582d338118115cb9523d0b6bc4b6ddce86a262bfce063ef9ba418e6ac387130cacd1e17a298948699c05e675a783c62dba8b321880bd34f80baf5787956f95d1578d100cddfc0ae14fcdff406e4de216bfaef55e72a21b7bb42408e44b06277493843fc4c9403c476c90717c41cb6b8ef53791f19e4e664d750bfb84c4a229bdfd60c227a8ab2c147c26e600222885df06ac46cb27b039dd5ed162e727e65f785be873cb35aaf867ff6eda1a8fd5c01bcdc8d100b174ae08ed6e21c7cf8a8549a64ead3a0195bc0fbda4547da2c6fe66af80bf113aad814b1cf6dfae1e8c7ab59cd2b3047cf7a9ced69d061dd56f34affbb39207e5ccd53d62da621ad723f4e2f528059111d40dac226b10c9a78818501c2e6d11b069fd01b54d95d1fd5c617e366e4ef5a9e5a446888f454b6b39ee005f27a25ef61cc2f333c47ac5b2c27be4e766390518f4aec1f1e267e3c913c341cd6d1c92b70d3b6f3d9cfa9f056c484f74cbfe109d2f0cb7411e85a52fbdebc10550ebdb6faa05ce227179e87d4c6e4e37594233836bd39f6bbfdc96cb955bb66ecc03500f1e1ad8f857fcecc2ebf23a2dbaa06341176e014937d11523793d038349301c173f4c93d01a03880a0bbc5762b9572fffb0c38414d85879d7f8d3f7089e5c3f03899deead317c57464ef35ccc2c2830ac2156623cc79a7ba348c8b4cf67b78db3ea1f353302f50c40f3263c3d7a6f786787d4132fc5d73f94864f463e0dfa4f817fe0126ac4f47d0136f38b1c34ffc57813f37c8e1a5f4090bde285af4f2ab1bc2c0380525bfdd0839547b92be07905b267d1b142fbbd64b02dd636b725c1798bf96da30dccabbe69bede0788232bf537411496a8df78e21e17be7be3f04642932296fe5bd4dbef218c6f548e624ee603e8a5f2bd586f851b522e426726efc0be1f709eb15b684a5a1f7c106cd078681c98332f3836f742b7dafb2ce01cf2be4de60e2b2a5dc05bf01d8b80cbfb9702478236c44eb81a92d0f8f0f61e100d2a9b75e7ef3564159e30b3ff15eaca17af5f208f403e57a641b73be1c40e5cf0d4e176e7c56d720cf41e7298eb9256911e1db860dd74657cd0c3c8e757bf4a9bb7a7832e52946e03eb29292852cfd9a11e1808770cdf84845835947b42a4fa8d10fee113c98acc01dd4832a99ae76dd492bcccadcf15664ec233cf400b3c6d4f1d5860f9dc137ee68993167efac27f18e5ed44b3b5e019f763aa8adfd8bfd408d33f244f734eaf127b73867ca5c4f94cb5f56069a78b05196f17903f69e5addfc27710edd439a3f79bee5799bd8e25ac17001e4b0f836d108d74d5810ffee07264924e3ae98277e672242108235c902651ea5c08c25ccaea9befab934a1951081b1f7699dc70c6d278dc775c614bfd880d337f4404f44f1ee825c8eb4fc3411011d109edfda2578422a478de3967d010ea2c9d405608da6525bd2171423fa7d7d6f1ce8a4767866184947c06582d7846e93e46d2321bc73acb9354ae3a9d14cf7249b36d4c349690a2863da1f3ae3a41c7ba87655bc299f047edf7313cdfefa7b509c2c4c32cda273e4a1cc793479cdb4cff2016ea9044dbe4fe1a0f25ec2cfdcad8db31a970bd0f99041cee8bd4c0042764a1eae5ee51881b42189b8c5dd70d87c27f3cddc3029fa07871b6386d9e1e9953f420fffcc27781efa4145533cc18b623bc5702d0c0e133bb92af4f0aba1b09d16089be399159efb0980f7adfc2fd71cf2adcdb01a3c83bbe12c885662d0d27ea299973bfc0c0b85014cd1219a540173e02b8d88c0ad8b96a96e9aa8f072bbd20d026f4524c1fd746d6cc1e503e397a37b4b5ad57d3fba4ac2236b46645847519deb301edd0080d4096c60117cef6cd8b9145226a7865fea8a4d52286886383783742fdb15a2f57b7899667421f5612b8db7a91e0d68c4e8b7cb00c227a0f700e35e32948be40f62f6bbcfb9ac244635b3526e272614616f95f03cc7bd4e4a459b0317fdca0d2d3715ecc47944b0ae874c90cf0d03df27c8f60d9c5ce52768eb3bf820518ca32959f248761bbf21d3e6b4e9996f9f605247ed0822381c04a0c05d93b2f5343775eba913cd523e39a4e279043812f11fa0f71076a8d7474f99f79b4db62ad675495f3902cce7f802908f101f7edbcfdca38110033fc97709d7b2edca2aeec54d599cc2a5db86f8e58bd04bec040b8470daf854eeba2fb930571409c9dd7b9fce829f926ebbd7547620819a0f617e3bf3d34aeac88b13fcd4be8182b61e772edec7fd9549aa58bcfe23221cb1428b749f3e05a3edc7807f2493e47bc34d011e2a5f2c37ae624b182db9d67eef0b26cc87d55d926a17fba17b0f9dd14f77caea0ab82af84286a352c324a20b49779aceeb45ed24d8c2e380fc894235b16bf39f6fa321005f37d01f95a4dae8e7ce2132aa1ed7bdd45177c3b412fca272a867793a51f2b736bc6c6927c103727e3eb2081254083d74539d0cb4792e16e16a0f5a7b0533964a31e851c5dd6762f6649419e6fb958d920515c0ea395cf538ec04d42d7e4801f3ebcd3084cf6c89aaa3dbc696cad2561f5647c3ffbfe610d6e0fbce6ddfe6ff99078fb9c77f15afbdb10a6d4f1123b5904151b3e6819364c360555c38442aa7c1c8bc3cc5b8d4853b964dff0e61aac91c20750d557b812b72e6effe347f0c3df8fb4946b53453ddd83b351422d15a938aa6f186b4e0dd3d72deda3254fd3702fa9e68eae4d8db5b460882d45afd5c6fbc74ae44f2dfa05bafa07c35018dd4677593c3c63874e97ddcc716a1bb7c6b0a8d15214ca9ebac63335c97618e398e43f4bf6ce7c1233a550d4d0749eb7a2c7a105eaed15f852317924f147fd41972fee698207bf0383bc322a589ac1313b9a78b4a6193076f7290ea3e36c7132e41597bb555006ce90804e47d264601c8a4014e24e72dd6ae353a51a97d8e19382cf58f42b15ffa637f0e07880fe914027acc4d01ce5b8f24ddf3746ee7d3c4cb07bbcb2f29da8da1f31dfa4f5493604a07557d8bb997ab63e86d51724f72db36df521329d5ae169e36deed193fc0f46d32f535ec57ce0430e8e0742db6c52452cefb3ebc2233001d49d66cb9865b116df77d7c463dc6b094ab979eecbd796adae9c49fd887b8c30d0b4fba428f22a68dcf3ebebce88e08bae18bfd8b6fdf180a10295d590f145c9270e4809ca85dd7f5f089251ec57544cae907765827f904737a92664a285fdfdcfd32dacf0604def890a1bd7ce521ad91453b6b3e8c8658a3618d95503e1846ea38c1fe98a484e26d28716cfd0368af2c9efafb060e44c01a474a7fb969215bdc6ec1cb46f730e21d6c1c5043e51242b439bb89e2398ca2e3acc677c2fd0c04ccec57baefeaa99baed66a78515ade04ebe16865cd89b3b2d21866e3dc2f5f1519f9921914b565021caa1de8a2badf45198b8052b6e99124dd65af8b447dc6c60f29b514244ef69d8af6a3a388e2fa74eb4b789fc178d3057059638217acbed94639b6e20cb68d158b90712a02a2fd1431b13aa2e91bd91811539e636aa9a30b6bc91b60fd472ae6a6480fbeb37bcc6bca1c7c0339b2301f7542f58c7eb1a018f600fc6b4a8c1d9b5de11fb0aa54ec02ed91e1ae055050b5bd0db673255d0df283b4f9a9fce270f4e156de1f8ac78a30c6c8443f67508efff4f3b725e423dfece93d4486d19f9733c2f267e5dae814370bc0cbd0f15fc369c5c8aeb85d7797cd781736d47701ab5399e5308646fc2a0b2a345634ca6b4794fb496e1b9423fab684fd4d49ae41d655a067482f262d81d877b650a9e837a3fdbe3f2ed6479d258a940584ce6ad687a6c258e2c2c0ed8700a67a395600674b469d5b379211e7a8fee7897226ddeb0cb8b0a46ea59534a9a6a8e1756b1be48d81376009d26082c6d884a4d50b8ea8522b1a8ff70691ac704436e53c2114b12e48d51badb0a6cec07a8fb0b79aff247fb450646b08741382fdabe7f9f501d995f0d653a59cd9036bee1816d9d97e8f502d92216ca4a8ee6122422fb841c2b36dd57ee4d7ad40b0828771f0ba00e4780c3a0a05be8b34af7c98d214bdbddd903204773883ab8ad50e3755a68ce4a1ec480e22c4bb24981e2a0a492202f822a70fc7035f1ab51867a40547081a8e0c8a5ccd6e75c32be2b43ff354ff41b222d7a602538374c80dda7add9e8cbf29bdab4d642b9432d1d8213e086c67dabc40b3f5bfdb6c48b16a9478cbaa32cb5c057fa82e5b694eabf140c75a6d22d51aeba918535e5decb7b4d7ee190cf005376f0647ecc41b00c5a30cb20801aeaf2cb06e0249fed8125157c8cb94acf7c9a60c5ac8e475d76b79a7457671631c79c1875622f35c2f2984d583655b90ca8debd2c7359aecd26577598a7727d67e1cbebe3b9f0a5b12d297832d430e58797072f3307f85f564a3e8a621ff03b9d12d3a5752d27dbcd222c09dafc86997677e07d915e8ca6677b73b1fdd318907e780e5fa8ee0363bd8bb28d0a30b4c85693fc905f8c93fe61518495e69d7466e664145c1a5483fa689b0e27ca524a66e96ff8f72007d0b92f4cd7f5cd1a0af56a980a92d93020713323d4e92aa1b32844a5fd7a4515b7f438ace888f24a5528f05cc5cb41a2879e46c5bc05452f43161387dbdd74fdf58c7374475d39b63b6431744099d8969912416cf6fd1530ec76ace5236d28037976f63d49f041eb196673ac2347f31c5528082db84854d4512d94a27788e55522122f1d69bb4bf0252d9a6d5f83ea441b651559419256aad7d21968f39e4c57e9bc609c5a2d34e77724323f46acb0e8cfd67b1656c550c9da21d41b8cacc9ec74023e078b49bcf9440a45a09eb0a46d56cc84306ade04b38909ab3cf6f25cd2da57f14661867a95483ed22021293162d3e4c28696d7b91f2406aa7fe3d07aa64b5095407a1468c3347852d4cbe680370c25583fc7e328a662855cb397c63cb7cb67bc12da0ba97097bc94ab55952244424a665126ecfbdcd85408ab69fdd4191190ab5145f51e550fce49b4c634ad654185030c2845aa4be74700295a5524a02ce356ff169567f3872247c90df47a88be531b98d4ca97006b7e6241a90c3ce15e0a99150349bbff626bc823dea6d2d51bb7457c2ed3b7c00e8fa14acce5c9e7cb41623ff9a8ff96101bb2f31ec9da9237789cedef431d4f9dae4af76c8a98ab5f06b09884d2c60df6fa70f91bc763170c6a6b4ca4443f7148a3278dd43d0f2b123c96ebdafdb494ae2c4807a11bdd614abac40a3ec5a8fe51d8ecf64bc3dc623a29c01c654950efbfd61dbc8bab131cc1a75cc84f0466fa6fbc3d337a536e582e4488f85db87e9d9914dbb60f485c523f2c0278f0bccb6d7fa1fc4d4069825a5fd4698d85061ce223e398b73a71ee858a8c2942109e0ea0b292a6c20548e1328a1ca2cc5fab9ddc5d6fdfdb3ea82f698862fecabde45b030c0994d094d1bb8ce84be6e9007d676df8df455187e8542118a900ad17dc97546d734801c3de11b10a36fae0baa36bfd676e6084a683380eb0ff02540ae0014b347a18116bf982176dc65cc7bcf221d5f806b0ea85b53cc2b79c23caa2ba7e82d4d0af9a844d732240b1d73c29928b555886ab0ee6e06c3bb1968de056a62995c1bac9493d00a877b1c4c21d9f9213e95cb59e50885e3a973520d71e66c543e3cc1af4eacf5d25a2dafd9d1e3b4e389404651abe3d7b01bc1e35f02a069c5f7f9fd907438ef1fd0542bd3be89d16a40765b9a0da9aa4b1d966b2661d69cceb160cc86e9a26398ef270308306d10b58367d0c596b4ff8d4e1c644a6dd133a7034877545f4b4276c392b1a7d1044ddb4f6cfcfe948fa1ffea588647d2ac8163706e47f7593f37711b549c7c0aa118dd563d4d42fa7f7f249b6118b08d0c2d5c6bbf71a4e9f99240d6057a160b27dacb0c445f05f84401c37003c47b3c9a58411b2624372b4a830ff527ba24d7736379a594536d5a888257ea8daabddbe32c337e5bd7182408635715002fdc5a5deb05c39038fde72c03701c387209228841dd3d3e8e89ff09284f65a45d45a83d7b331f38f87990c9933bd8834d27d35e45125ec6aa076a01101e7f7f1c8920d80138984e3f3a2b264e9edee2d554e64d4d2c3e127d084976d8e88e81c93a2171301fdeeee3ca405f94d88958b941655cccc9bc8d559405a0750e658a8adbfc0747f5794884f18a29deb1460efb050878fd4af2b022b76cdcb7ad2a8136f1eae8bdd0f34da2dbdb723c2ca824b8bd29b13612b4e08d158bd4933be323a8ea5fea33f8e4a92012d76916613081697b63d02b1d1c0810d66aa6304197da6e3abdb24212c7f46cc67af481ac07472f8f1b0078c2b260384ab711c1f95dd634d02922301afc77e96ec73d639d675ae8b03f813a3c23501f606d91d0ae30f024cd94b9897087916662b7c45fc02c34d2080172709f33ce1f4f6fd53a03e24a7c8837f58a16f72de100e21fe6466dcda8dbb2d9b5237e20dd630abafbafa4b0ad8bfb5a146f35a10cfce9f02593557d4876b9c64fc91e1e5a4c904489aefd1f5a1e49681c96be6c2997abc0f935e25619f2448e172e73c145d88c8d3f8f79ab46e5c23053a954dccad83544a8d025cebec8467f296d061424ecb64424eddcb15e82e3bbaff240f835a6a699ef35e56d6f3665ba300b68b6e1940631cd44e7f21d433e6436e3cfe0dba5017f1a9b68573f40faa704b37c22987ddf8764f11f01bc4e624d3268aef1f98045e00213e70e4b629b97bb3c20cd1c27526da21a277c1a68d2022444eb3ef3fecbe89cded59c746cb80cc1ccee08f59d8700a96f7fe467076957f0b00dbdbc8cb5653ce8e8da705639280e969a58d67429c873d369f5991f44838e64bd66607e96e7964788377b8c2b16e85de5d5a23edc84ec1a6d3af8ed985c659bd9b63ade5646ade730d56fc0ee72be25697d7f89e5bfe776c585c598f8b66dc2cf3bb55b5e7c5ad290c87a17fb8f5ea924788cb17e29e0079c3da55272dcc3567d6188fd53c3774a2d5e4d34a7cbdc5add21c64e36801903581596845badc59b44c1b2e090aaf86cf81a1d13dc6701b76763104d575f1fe146277c107a7c403921e25c342bee76a7b3a4b1e0c8dc7beb18a9ab8b8b34c2332976646cc1daef04760ab015725380c235df5609eee5f7f16aba1cb3cd6e02ffec70f3d59a0fe502f3c46048685fda042c79fc952241ba4d37faab97a4771bc4ace8ce4dd230093f62753734583b4cd3e702f08268d048e6a164880d7b1ad2b614bd4fba6205fca499e711052ddf8d4ee111b4afcdfe631ea332b07b36196c4dcac1f8638a0e62869185905212c933b2543cebd8c177f0bc1d6f12ff170799b9e8341043bd5ef14aa3b662b98a3f6c3635c241dfbc983c9f1201af203b46a82878303e1a2523b2d510733a400b577b7a13e84afd19893a06e63a57a38141414f510bc2d4d7ef4a36cb4f9d9b4a1cf97e680b082b4df67bdeda8106493a56efa0b79fe50266cf6c3d4a2748a8aa8d3a0fb3728164c23dd4aade807b29dbc3ddc07e14f4579b3183a3fbb719a09ded581aa15bd2cb105a6f1e6a1c8ae2be6808c36d46307be0804048be9ccefe6fa78fbae3e474055f166566e3c9f5e9a629e9205b5bba8befbf278a06dcc9fc573c61875f5bdea043e61cbdd27720a7fb89ec3b9988e64d37d9bf2f24d330835b1991698b229accb38983ddbf9de938a7535358c66bba0cd01ad35c42f052b2c0a963a5a1e0b8ff47cbf0b39c1b4de63877437a8f7ff12760d1b0461c5f9474e1a13a9d355009a4b42279389afbe80f9ecdc199bdf0c63693dec9ff283e3530d46d5de0b83b45f1b007438baec00249327504b398df7aa1f3c8d9388ba32d0f6b3bab00ebd1a44bbd2f45591f1aa2d5ad6971ea307f3a5b3f3c7cfea527318f56b48e0c281076ad2b85e8b1b4adb9febc842ae9963219c7db99bd3f004c7acb2eb154f18358e8f3dbc88b916df16f5300a8f6410a5a7fa134bded2a63b0145a9b20aec17676eac969ede3a95ab82376b4f804736119c5dd03e9cba718bd20bf40a9f4218301ad4458803f09d2bbf88bda875bdeacccb180997fd98558efa51e3d9c652b9ca7da9df4fa79327b1ff4dc41ada4b224983805211334ce906af613348512cc193a853d8fe5cfaba251833e4210afe5a830702fd0f01b4a23ce6db39738d545ffd19df74d1d5d21f678d43f9b979b5e20faafc735ea27e494e0916256cad15939066cbd484b729242596ef4ff526e1411e0067f774542d4a22242d29f10f8b6a5d03498085448fb01a056c80b9bf16b3705a020793046206dba9e0aca456a22a72dba9721181c76ab9b8cc1664def5b3979f0c059a2c3a693ea33f244971e209bd0f1475c61994a536b05d65c5213a70f04bbc9fc7ebef36017466f5c985e3bf06e2d32dcf69335b5903ba1858423bc5bb78c6c8491b4a423acc2dff5969347849b6cee0e4da1526f3be4dd951482a455814dcf18b6b1d2108f51f016241c9071fda6ec6961cbdb6276e372591bc022574cadf9d3d0b9a783dec0903acf275da463dea626f1ce9870396a8b90883e10773e79a3f4d5eaf24c488f7250ef7a5faa6b130cbfe5b6ad2b25c00ffe61a4280eba5903e93b27b12aabf010cd57091fe86b9ecdad6b7032942d5c8d1131b97db2f0b1c1a1258a902fef20611cc753f0a12452661ab014441f5b3420db3f51d6d9286d1dafe100490ec1e977817cf8df45ffe8a53bd35fd844d1f7a98fb3c873c387a4fbcbb964292fe425e211c9b15baf6bc4891f6cf60a64fcdbbe0d5a483fa8c0507e442777a0f974e14cf2fe3d78965d335dcaa0b079662737d36ea510cfc2bceb092185199ec5e6deecb1c0d57e2ca6e959580192ff45b3c222a0119b6d4003e3a0ae8707f2936cf3672bebabd09f204d99c6c4d3bfc22d0df4dfebc496de0f5575af7662b8c041652c14589ce4a4ab15ec096e4098735fe06fa9bbbd1768ce64b2975d72d3680a8662efa5d3daf8f630610bd4c045adfe0f14c2f91d38c855c5b31d37ae8e60b05a24defac39a2e22c084cfafd82a197c289d4dd74b4aa119c4358ecaa0a5943d60892459e94bd1cd653849fc03648e77128e6e94b0bc2f929f764520dcf78428cabb65beafb31225437025d0d96bdd9766d221189760daa87ceaeaa5165a9e4ace484667f79c9639dd22b9bc6798940c7b3b88707d3156606b04a8b104a650a9b55fd42e37980ac3694e5c63736b0354c1af1c2cc76fee2b502d06a45ce0f311c248f821ab209fafbb8e5f2efe58b64203084dfd848aad7c65f8edd2bced25de939082a4345f95c82f3f24ac30794daf1a1b732b9d3332efe69f64a0eaae947a8e460c3f9ad8a49e7b595a2a23bb7ae97f53fc7408a2fa4db5962fafc0913b9aca2f9a7b77223131f9ed2c6718603bec29427a6c975d9e8895f23e80e495fd8d4bcf030427af4b4338ac66204e3ad875adc11114d76d47f258c09a49df9a1b0c537ffd8e80c822faddb111cddca3a0ba210c02b49f5616ba0922837208bcfe23aedeba813e36b32ffb62538ffa7bb6cc7845d10b87aa935b611f71d775512969b348d1768e79c6b127c02200e63b4aeceb7150ac25cda9eb0e9e0619516b79753ee48a71cfe8b1008909e43d1940bd1e38665f4bd9938de9778e97c63fa256670c993d61f95b0916c674e830bce781487f009133aa977fcd3c2a68410c04f2ae948e977e9acf74867b888c93a31290acdfd13b1f70915cd68f0d1cbad18227e1917c7c8411d76885c2e4777cb5cf59cf73402f0951662daa6129191aad5836e1877d4366b07dfc24a225d6e053d7bc4dc1e26413b7773f02f544ead1298ca88bc167ed385bbecc21ff3793f5ee23074353949a4beb95e8ccb803ca62f9414c326df41e2eb2cdd8f0cd7975385df4878b7075562c6aef31a3bca0d050affdc889c14a618be76699302dfdff940fb58d810b80af013e84ddd9892c45a393d6b8eeddee1ae442a5e3a4c9e3ae4d4bae24d3a30ae1837e525bf09ddf612ca692904ca1e35c044f5fa02c52a28b4c8e74d24383527c396b5cedec50b60098c4fc2f0774d5606e83ffc55631f81a949567db3e14f646b6de95db67c1f61fb372e40e641b14d35672274267139bfbd2cf54ec15b8c03381cd091c5ed5140623154c08b6882ec6c4a4f599f538984ff18e6714bd459e75b52f5dacdff5b374b0baa627de0735c18d2a5be8892e2e8b7380fcd8e81a84d18dfab081fdc56d4a778ccae60e076f2234645ec930f5de341e515d419b22755fc62dee142b77306ff984c655010646d2725ae96676ff003860553421de567315458034e3ebef00722d4a66b4b8409081be245b102ee0fefab34ae436dd35ccda34f03678a27d7d70f012ed7fd467cdc7988d80f2bcf95677d748cd234f11b69ac01b8a9bf372191f76226ba74e23bb0f0a0eca3a4951a8806439eca11a6913ccf6a5d537325522b468c720260873127069e61e2e9fb16955b4045525698706f0ab73742493aa4e838e4edbc0f9dcb98a4fc9ec26e81ed7b7e948aab348654430daf3b664e98b1f3c7caba90a0142fc8fcba278ea89eb278502c2b29f85771c34a371cca63ece5c2ac193502bb8b183fb4f1776031bf66787adbd99049ad39f987f96ea0fb4ff8df9a5c628240f393729b5e62fffca4ad94375056d6204665590a9c4b9d275cafcc2509d26cbcf0df20f81e019103b3e89cc47330d3199be8d5c6d3dcc7495f0aa1ff29e57e33b3a0fbd9807bd2edb7e49ce00fcd37b0f7cea2b5973f057f38fa6bc7a1fc5c8f8afbf9f7eed8fc1b0db9b54a52a5f95ebf447aaea9346d98bb280b77768bec716ccf500d708602a040b4203e300e8ff834748be2d862124fae459fa9e69474a1ae38a1920797ac2885d1deb004ee1a9bd4375d08c255db11314a3dff62e6e75de855fed3932c1585fd3ec091850196b77a4ca6415710c3ee2f04d5acb42dc8201fff44c880117c78fb59f7869b84adb37a758939b9a90628a3d32313468e52c698a32e526862c3201655f5c49e945cefb0c418fdee39b8a64ee588694919c795339ed004ebd9e3ab0f1350fc3a6c5a60167aa31dea2fddad06ac3bcc5c933e4f133419df5e6f7253e5518332f7d738516242bcb37ecd91ba17ba5bf027c681dcfdd176da574152788e01f6f9b351bee74cef4997b8fbc3ecb4e1d8e0ba657acb81d0874e181b207f2ab6f3caa750bb4b5efb6fbcdcd2cfccfb8b72f43e89230a101d9512a42bd81881543bbbbb63802f110abc7f97aa787cd2d2359023acaf74970787ed751ae2c320f0ac96c6e688d5171552315af8aaad3b828678b2d4fbea5469efe0b68b4cefb7af29e12d99a26440d1008d63920ab089eca8dd2c1d3d467f46f947747aefd859057bddfb16cf1c0fef1dc971567ca6db258badee44ae4b2851661a5842a9d0508869cb6a114dfb3ac68fc42167b4c097e6e27129a12e48c7a7c8dd303c98739f59f007ba661a764e797ff5f4d6ee06dcbb005ca757f2aa8e6c136e245d8e9c28ddde2d70f1884255e0ca0d664eb22ad454956094911a1242b2b78c8617683b992878c0bf1146e6cd412fa2d1a33dedfef5a4356ffdfce6b4962072b84b7067a676f68e6d3a07acb57b7399908b573da68d19820333b6d216290b36c5d5f88680107cc48b97d7171b2f762ef73c8cc4f40106220f42d8e161ccb1eb842826adb91456bf02bf164f7df3e58671b3320540f6aec011f80c03d63ad02c295cf3d60be00079075e8481f7d512ce5019895d4e0ad00887da68e1cf9ee18f5d09ccc11b68172b84e79246c3b24c444cde58ef39bb61418a3bdd00da4d6449ab3e77a33fe94f287adf6d4625ad33848c77c11393e6275d9d6089ac1ae30663a7bc247886945c7b0a422e310bb3eb86996a2255d2a0f09e30d0b92ab4b15fac9de31a8d27b095e5e70a6c33cc9390a00773d7a983b01b771db1000e1725def7628690a30db19a1db2f97b2b449b0958d13c127d32b87c3dc155d971689e831fd861273fd8b7c882c968199e3297df81f7dad292c57b1a8921c76601ea77d791326688e03e01855daaa108b10571763030cc1cca946c4edb826fbdead307b005fa149dbb78908803137825e09fd3a116702c4b7ae229405aecedf04813ab33c47ef196a0cd8b8237931d7ce1a5e4aa35779b5aebf5db0dc9b6d7bc76965b4d90c5c750be7e1bafd0e5df07102103f06aff148019f48db411e0e17338b0f80c1a0c29a52810cc07b68911e29f69910befb82dd138611485e580c4f03a735aee5d207d0610df3a04bf98a89e4b033eefb3354d3de1fbac64e893e460a73fd5099635ac32a226268a5a672f10723f42d2b9817056eec4840659c97800642492e9283f7507c565c018cb3dd2403790f69989434c1f27428bb3df1e653e8bf58cc59e85ec40e754f1f6799301f15fd96076ec0fabdb3e0a11dbf8b0cc7318513e9fa621488c5f06ccd530693f9d565945ac74d00556c3bb36d59d407a9e278f98811800be7ce2666f947ceb13f7177a042145b180db75bfd1186f5dfee54409a337ffde42212d5f08e8151d1f5e4d1e34a730136bc63f6f27fe231c60d52316c6936d5b9e1f8e12f5d30b060953686c1048b6aac961a605fdbaa78de5e1ffece43cf8fd60105793064a909e47668fe133579abdf3248b8eade57861acd1efac032420225f6b5b0a3f372c173c39f79db7b060122f755a67e3fe6d715a12d9404f5cbf82ee0899b4941eb62c4e3248b266539bb5547d3d5847783086ff837356e6605c857a668afdb442c3d7746510ad0543e7d35b7c8b26e159f4066f889fe3a0521172c4caaf9038e585b252502a54eff79f047ed2d044b0121a31ef87c8e6c8424c826ddc98e99b5459047dac23c8cf59fa4016586cbed3734f1e4b83c3c16fc14ec5c4f019e0f0b6463b2eafd6cc0182f7346e0814d6c1b00136432d295336d871f0d1b4724dfd6363df5cd1305ced1e6108daaa401c060a4b37240ce316804cbde0398cd4abdc4c39ad8968bfd06a65e166a3781f7c8cbbf9db1b354e1ee7a3b17e1acc4816690465daaffe9d6f37a0f37bfa77542711bd4590b6aaf24247c2c1c0e824e3d285ca52dc5657867506d34ce1a6b524ac94255aee8d388a12483ce4c736c562df1b9b9674277257061915a31016c1be32b2700fee36ae388e3cb0e3f8780407b6ece46b0b30a657e73fe054266d953d539cf70c43f98a1fde972ad1903716e807f73231f969d2bbfd5fc99b139c05cdfde7bbd0e8666ab9d7cbcd7e9f3e68692193f091250ff13c7b94cb046760de3ddd6517bbe307de4896c1a60185a901d402e797c760a37f601364b9cb2708c06f73c762021d4bf8b0936144dbbdf0930d859fa2806862279b615db82c2b7a6035a0f7ea76078343dd26290bc2e4ea1b80af691512799f07d563d546b806c7924d00b2fb848c2f4e021c88faf95060a1f9535f51de841dabc667942a5667809e5add0d9e0bb739b3802a6c1ec0ae8d75de76ab1a2aa2e7e1845f2f95fd7ccde9cc5a10defaec486b5f802a6bd0b110f63d74ddd33dcd25da4d6a734e143f21aaca60208e6971502a0951721aa11ea4f03dcff520a5d458650db682bf83b4508fd5f92a151378d69d6725ff308962a4d98dba523c3165a8b6237f6d6e2f08ca4429536309836ce946aaab2bf3b7667ceef45347fceea65fdba03403935c7d0da05c4d4c37239e3a58ee8942af27c466a48f36cdf4a70815ff6b67b6ed33a522b62855a4f62fa1d7618004eafa4b71852b2dd50e730bfed254de97cee2d8fdcffe21e691d18381bf9c58951c2f469b3744292c66be1127cedaa0c4baf971c30d3b5e8806d8385b0be0837d7b69d37b3a559b95c26ff7c05d746953a9fce696368e37dba1aa10cb5ce357c7b1a7ee64b4b1e139516ed3a014290cec1c50bdf02280788e132b9fe45f0a5fefb50859653239649b94b05e7e2db4135bd417d9ecdf397bf5259fb8b65243d36ebaaf08403a56a6b7ab0a443ab079c0fb73feed0ae1f12ce9dd76973304bc8d63f2e83a37b15456d9bc40001c9d092c8836f1e69fb9140b4b55271624d4cdf0af66f8e9dc1e9bbc9874dcb6100cc30b7ac078c47f7599e82fbc19f9443ade2451dc8fc8d44e8747e7b7f5beb48ac5fd8b688e02d381b2b84a6477ab53c94d055866a25fd74b0a5a5f63c296d02611cd39fc291bc7d6c1413a20c902cf3865ab0c19476c67f7580ea8764961f9dbd2002f1001834c0ebb151fb410b3a1350429a6b57ec0e2cf0950d9b581c670f3b8fc1e34d4a03f58a7aa404605c45b334998caa472d2804f5ed846edf37cd35b375b794dd3b8b113bdc3444159835e449c70a009712b6639b113df69a61cb431b6a9039fb7b3f6198242ad0226f295f5c6a289f06087efec390fc817a41ac9733ccc568b1d9764b11c6a510d8f78aceaf20347cb2b47f126dbf87d314759e1afc9a1c6a6bec1647b74ec05fea7717f80d624bc1473b913a645ab140de9ef9b0692701bb1c7ccae5d4a24fe673b91717e3b93d85ed98a4c70766536afb5e802f38f450362ca1ba6ab53effe0adba03575f0b262e992b7032a35c4c0e9dc291b4c97c2f5537619c83f32ce32d2830675b3474c89f4c54c4760b309c5e14c675e3c1d99803b14be4e49257c06516811582dda7991b1178addfe1ab76aaad447bfa62d1fae21257aea3498611d52aa3595f4be2e82757099495cfee802c33db5bf1c9c1b371c6fc1dbd57f4e3db9babc19d60b60d9d8349a583b60f5c13b2de1788868ca5fe42c4301b9cbf0084ccea1436402b542a1a720f63c1aaa5c8740878505629da46835de3306adfd7696e321c2d05df7ce657fb15dde4ec891393410057684a28387bced7e61f5ebf8cad630b7502f9b3c65b186cc252aa0dd986c389dd27cd8d02c9eef56e000860d10e95ea6519235717a472b4108cef31a7d84b2fde8da16f721f8b45fdbfda6e934b410defd5a7676702b95400a230e665611aa9694ad4aad2da42ea8901f3e00bbb50718b8e48a511d3e58805a764cb1c11251aa304f265bc9fe35c9b90e63855fc349ff7e359cbf30bf05bd665e6474a2d9f491475faefe97efb358b37bf4df600d28d97f28b82dcf9436349ce52d5a31b31cb5cbcd90834883b0f92dda6829a9192856fc57d5209465e4423c9e69ff1a820907b677b91b6dc58031303d5fd9bb857a11400577a02e2af12e2952587b3fe9a0c86e66160d0f7ec60232915f418fd9c388e94c3f01c6b2697cf020f9c3eda8df3e7e8f92ddc63730732026313d5b946fd448eb99ee03af4d8f39e38e3bd2dbae64756713e9c306f89c3e54137f0895afeeb9f54bef4bb87be4b6963313d8b614b90b847cbc3f488282ec26e6db5dcc4edb3b77147c3f61ad3363ab801804160a8de649966829bf4df3fc553c0fdff614270b30d6811e23201de657e2938068ec06643da9ff2814ec2d022fd4efbefa65cc4fcb0efd7ec2864f8a8391993029bfbe4640bd6bd086fab349b80dc6834de05fb061ca30a3619644c137818708c7ad832c89b66f030601b15b0799037ade079c0376a619339e4932d7e3950791526aadd6d7f2588b1677f1c7ece7e918267b2a7f86d88e22d5cd4507c38359d54cb0b289ac3fbd8615ad827b27e45401cadbeb43a60052ec33b7dab978ac117eedc40f213171688d4c953c52075070a8b3fbd5f08fceec6f8d7a420f0d9d394995ce46efcea696972b61a8a9042f7125630984071091c76b57f59f70478bfc2b9c5abbe71f220bc9445b653e06a77d176e1a99afc5b5bbde9cc337638347d01474fd0b19bfd76f0b4efeb98473fdca5c678430fe666d2f79850f0615f06a4b5ba4559e9362635efb889fb283604a4418034a9bfb67b7679983db114f42df6c2765641d77e45e8baef1e43f1764df8b65ade4b3fedb5d4682d4e7bf37d5a7fda9befd891eeb437890b7a41edad8ba54a1355e990f2bf440e329d7073fc67e373ffd3cbbb541815bc84b5486048ece65ffe1fe926dbc54a00f6b72829ac5bb90774e5099c20633e47a55b008e77baf4389af7ea6f2b0561043379c4628af3e8d8bc52eacb549c29076520bea8c431c9670ae4fa3487917fada78b13604e9fc93cb1af7fcb3f0718869a9f0792969ec3019c42b93f49386d04e03ec4b924b6bdad5fc0d42f69807c1a60911dd08ca6120f88f762f705fa57d29e46e8b7defde3c5be0e713e9c1f7f12eb279c7edef7c7b5908a4c47e35f19aa13669c40a0fe3e6bf3a4286cbd5be134be859705e010335d91db15f71ba99fe5542b5a1277c1b4304192adda772f5e77959513020938c754c8a6587a39c885618eee822639ea200794700bf15346837974f13b297e2a574a1c5256832369e95ffc7ad8799d6221e2ee526ee6f0bb21100ed925527e8c8e7b7ba6ec67b1d62f7d0ba75296ad374c4952804ab7c9849243da86b1f50ad483170e135d8c9d820f5559fffb682e514096aa6c23a8958801a930772d7f976aeb0e508fd1f4f7ba8fc6dd7081fb7da1ad5d2a5ac26243199b0518841d72a248c0c11cdc50883dcf3c621f32931f570c499dfc42ca1f6939b2043e3018187ed370cee8d4242b1b6651d70ab662799514b1dea98cfe412d8dc18ce5127187a55834d7a71422a22a037a6d936275096650680d6eb1436f9685ae2925ca91bd8ba66838bd23f8fc9133ca5df315f284d3ac5a1bea85f5914d1927e42505c64ce068f7067514494a97bdd5a2e206b27a81378293f636c0e0cb875d41200675ee2d0a827f2d808543a2c8c1a3cac1bf4fc4e60754abdccec2a4bc40bf248258a97b139b953639fa0ff8bf9010d52b287a8bab22fda5eaf4dc4b7013f02478a28c9d1af5438937e8154c3cadf8eeb0a3a9280ab9b52f91513d07ea414336d8f717c30ee98ad38d72537eced2f81a1e17fdf78225830b2d7f07ee561888a23b4894fff2f1d2dd20f1c76a121b27d9a401c630239075a20af2cb1fc5d8208299862a7660d27ee1bf85a647a0ec47658cd0bae52c10aec2a4c5b1cfc2815701389c8d72098493c027e7c2f0f08903ce9c0a93ff9d9a084e1d70f8af189e6a0342e8034ae9c8e391294f7de4b62f40d5e19673e4dfa48dc07d85e489b695f69d09abd52dd36e8389687f19b2ced7938d0373ebe1362ab069a3a6efa4a6686a1c2d083407267d98babd9b02a5e78a690434bd598760b0f93a9517db9f36ddaf849102d73c13b2fed034583ed3c1b6bc23dc286ace28b35e362f3d1f6c7fe110b520324c44649a17589f9d1811cd412e670e76dd73653178fabf81d0cf38446e3e2b3b43f243da53e2fe4d1ba1147f7344f25afb2e4887f72ff196e551de0feb6edcaef1206dbd1b25c381a3b1204c08f2d920945bfdb8b38cf6813291e8301fdbdf16f9e2eed4cfb388a7b7d6d280789fa4e9fad83102dca538b92dca21d540625d614121987cad8dc15c41d08ad83b9bc7ea9812181dd6cac55400d939770a781c999823859ca54f9909ab6177d5bf514d8de8dc2d331d80ec8dac8ffaac8a451648a4fefca73afca857af9a0a1cb2f97dbc2999565531e343cc564c160ce023e8096477456cbc9560f16d6a9213f3de507843833c1f161d2b7e1460d37c5039040021e9e1a3edcf38ef91a61a9da403b55c5dd34b964f681f43717fe0b0f016412a1e3809d7a662cae02a03bd0a6fbf2b86c985e8a77039ba8a70f266a257d34b9d02233bc26504662226a6132ccf5372335d0451db3f0b4bfe3cdedd4af0d25064bbadba4f5b6993b36b4e70e478f98dd5352e5f567b85af5492b887310a49690cba13f982ccaaca47acdd12018192b572d67261eed6ed1eced4ebe182c04be813fe12667decdece75161f9042d0f83a9ce7d38ee91982ad4d8fd7b68b48b36c95d6fb359c69ebf13eb6f1353955af7f2d32f4f2b14ecf5b84b85e8449dcf9c9655cee104462c29a8c134728695d57cf260dd7fe600db3f0fc2cc26017ec96ec56fb0f003615b7092aac234db925047ed6d52cac57f92ae434c0f3580fbd426bd30c9b3da6e7e961bdf780c1381f561ec700c4d4d80a7109393b0e21e0253e32ed2d48968aa911471b18fe88fa7d4c61c733545ac16aac7e795f258ab5ba5ff3a8b9c28925c5fbc3ddcb6f3824173b130fe7d1edb07114b65ac5eea1d15b800d0a45b8af9af0305291f1d00fb503c64c94720b1dfed29d3b7d373a75aac48ed5c4c6d9e63ec8de93a2395867c66a91f52d271772a988e3e593d7d64f53ddad795bd8571ccf37014c9a3ba2ec81e75cc1775fff39366338f6069347c18f3fc0a234e09c364ef92a4a333828f055028506ba79811fc44e9f6d0a5e8a0d239a1e29b8840048ae5f9c34c5938b1eeb6672fdcb964039055338887be1ab3922fb7ac0fe0cffff9198e395c7f81552c135971ed0db30099c0b46b78070243b0a77429026f088a33ca22406df9166f28484b0376147c7b0923b82fec804433fe003caf41ea99a0721e002929841efee3f17210039270515c9dda3cd89bc42061a155471134e5cb8c578f35dc2fe12cd516c826d995bfbcd3a7bc99c11cdea54c6b5182e4468f1df462a02ff8d4421bb7d1c63fb341f0dbb56298e6a15c91dc2abae98ec89acde881c92c6de254210bd23aba9885916209608c7e14d296d1aa81a541a2ceb04d9c328a75f92e1619f06f027d1baf077d77fc1a78804e0b0e7e27659e056c522b4602342e3dd8ac9ccae9b85cef9365c170e9154fb8602fbcd97db075003ab8e80521aaa5bae9e6fe6234f2863b1bb95f21e258bb20bc3ca780798bab9d5c1cacb31f5e6c16b4d78983b481f2f0d31a1643b46be90de1ce54d675f2132698d5a79d8c30db07de3f4748c9d31bcb2f2bca8f44315ae99c8c53a6088660a9ca24a4a1453956755b534b8a8ec6d9cf2534449fa04dd232eddb09385840f88e0c04c30549ab56ffcf2b45e78a8d402026a6f5d014579730ffa06bc5f9724ffc67c6253cdcd8db9d2506ebfc2ef6c057374038953acf06e17316dd12cab6e76e21310abe0b0411dd8a007319181b107bfa28e03bfd04d34203826f20421f695c11b7126da3bc80a0062a283aa63914c204adf5b26aa234a4d094898b96798bc3b24ee2791853b515d87efc0c60cd62b6206ca33c5b134ad3e56f035f20f514dbe9e72fd6bf9cf610eeed1c71d6fa821f3c1f69bbad43a6c52b198838e2299c54825faa794fbf532a8f5feae05bf67e920932a6e02226bfdf7351e013e3bf9679bfc2c254b71ad82b9071c9619f94bad22016d5d9c92481ed459e1219deff276e773ba705df14a1547ee8fe3e223d30751c85682ed519409a4e17c051f0233056420bc9b29e28f849e0216a50805abd1a163dcf32d0a2762e6da38d1b936c4b14b614e66c90e060fcf56279e4539310c8e8aeffd3eab1224fbf3547bce3fbffbc6e77c74e8b6ab5740176759e95986ff00acfe7b21c3807ef10bd25dcde3d104757057086673f775b8cadd1a934dd94ccbfe7ace6ce5b6b4af46b10806eed9d5efdb3ec9eab586f64276d67112b9ecabadf5c7204b5fc3d7203a3a6a89ccf3ecc842cb4dc93ca29aa759f57dcd909be071bb3c4663d347be359777100b92d3ec1bd7f0efc2431bcdb8b193cd2f35ed0831feff86c55ed15216feb390b5a34c8c1ab4cbffb88493bc3198e08e61579f773a7f9261414fdfcfcb3ae26cd38198d91c5874f193d109e4d513b4101e58ef7eebbc3906fd101989426c85ef4764c6a28e3ade3884516e5c638ce10115854ecea91859c6c85c7ed0712f2896069b3ea2e664907033bdd570ccd10e719467f02f6205e891a4198ea6408790648c7cf098b8a5c2ba8a16008f19e01684758c3264c88c06f243aab15ba58e061f8c913746a5d65e547eae5c724023be228719b3007a38b3024272b31a8d2147cc8c52a9d826ac8cb582549e6c5264dd9da3a455f35f25c18787a23f5668d84d0a9f1a5ce25406067dff3bcf0dd848dbcfdaaf5b9bdd774933ebf1d1a3990f6179de1924b9f56df49ac872f21a9f10d621266252ae80174b78dbc9e487366ee52876289d2e74efc6c64e5d1350f37664ae63a8216fe9b0036daab4111cae5fe872281a1cffc26be237893d2f554a223f5644002737441b4971fafd3ec92f5cfb6d573cd08d1f3bdb2997400413ca5f9aa4ec054c683494eb14b7c3bc33b2240bdb54911856996da2b4b0e05841b0c90156290e211b1aee77539ba067db7d16e1205e69b142c81779cbea478940fc48a1ca2341042f761b321eff8c7161891a57e7628d521d775f66e3fbf885b80ce689e7570b149d96c07740f782e38f18e3ce86a4687bdfa9fe22b6bf617054d0696530e55590d65f0654c4dcb192cd1230e8efb2c3a2f51f25e417717aad98d142426c992aa7be4b68f9b28922a3be9881dabe508d01fcd6d0fe6f3b00549b5abb6a67e6aea102a076302eeaddce94042466298814ac127786abcb5dadfd26e2f6080a440b4175f3242d96cc71f39e525329eed653909526335c02a6c1b5f98b36f18d4b4bc02e82d22aa24526695df15a6533493ac2d576e2f90bf7dbc84425624bf2d254047ecd89dcc68a13287afb90ce0ad870a33724c64f7a0e31cee8c8eff1ee1831c941c092de071e8ab733f8280e2206457a2cc8066b6e049c4b39242b62d35cc60c69ba0c1518e12e22525a7fae0c86d010cd3879b2cfa51bd1958337c746a0138480ea59d8356a24505c584432d4e933e6544c41dc3d8c2edc3518b9166c10d32119dcd810c66d24e22346ac3d1d4a7f3de7366ae555d17745716272a99aa40439c6e09a230d490e0c9f418f4c3e57c311288c84482b0051ae31602181272cc2e49d7a23e9a67e45760c2411402666b1f9e12f9f51c654acf0034a55bdf5217570223244f9aee80b2523d37a0e6ce655bdb9c9171f982962c38db229a29d0a3a4f6154cea5dfaafe22df48d36342d6c3d70e93ab25532d27a652325c73a8b908e9726c2ebf9fd5d5822ad66879c414cda578d8c619a3394fda1d6fe21f7569c437b6b4927e5a5d1a66d8ad1e0906f8cec17cabc646f3f281b71c42fa9f4c277fc76f8e48a1e131e6d513892666e14000b6418005e1548e1896ec3cfd672e2c9d47ea04a5a3b09457f1ac4b730389a6c6e459dfa0c7a110f5e7c2062f082e4a013fa687cf189f8d45030033fcf0d0ffbcc23755d5762cfaae5be5e79fedd6aea0b9bc01b8649bfd501fd610858e603eee2e7b4d28ae7adf0583c06c3c087389273a60ba671d33fcaf178f6aa0f83cd80a4bb08bc20b94fc55814c85b29f4195f0c979b8fa41edd934d34feef7dde76fb3a93848341c8cbebe6b62edb88761bee006b8e77ccd76be0ad7e41feca4362d5553b1d5bd63d97d9b0831a5c873f1892e873deb03b3379fbe19d99782771b1c99e0fc32ad797aae17e1828002cec6f87b6d1bde7e24389d8fd6bc5ef254eb19dc759c7dce2089f46d170c11b2132836fa657e004ba9ebcba86cccdc8cf9fae696777fec357f078a7e64fef36497609d08cae124496910d9612f64d08bae9050fe98381952b54cc3e2744b10f52fbad469456afda654568dafacacde8a8601b9c4d9945727b5f49d6624bc6a4493937074705e35c3347264c64f54c07aa1db272b6b7d37478b06a1c9d3a72854857c0f72862ec2b81a5afe76a745f633114c269aff290da56acb84edf5aa1347fbbc288a7706104b43f5f4c9f3b5fe53477962d02a291103f64ccc8227420a99807c0caccbfc8cfc14be88dc24fb3db69f17ed472a45e7daff71091f7696a3999811e263f13960ca0f346edbec7c6a672f99bb361aeb26efb5d8dcbe88b498020c39e740c3441a61b14e6893fcdb8c4b1c2991ad4e01560cdabf94eb47185d9966262e4525d36dc17265fd77130f4045bef1a781339a65242c189796f06d08fe46de9e55d2438bfef6d808f962f6fbfe108e7b559c6945915b3986a993475e9c18d7973077bd6cd90a03e5c95ccd78b3a91e29fed207cc6c4b732a371227847750c4ffdcefa114f9e4a3283c31697827a5bf224e1c2e42fa9bb120fc87cf956800116dd31a70bd975a336b18693a527e9c73de4fbf37e57ff72a791df703b4e1ab30d0e461b4ee5b38103400a5e72c593d23c7688bbcc9b3af64227ffa5509b4f0de0ba6d522f3863ad4e04a359b477c5e237bb15fe20dbcf52bd4ed6dc38a528bc67afebc2a2606d1413ca1144312fce4de17799baf882fe8e28d3ed2000609900cf6e8958cefc1a40b9007811141ce122b4d88f4d15484ec0028f3e65f1a56257f686adb7d54e32791d9a767be74e5a8882d70ed4212575d6a3f70842c2e40b68d791e7146a6444347314570498fda9cb68558a069e9b82df13b42619889cfbb7237be59138a11bbea94a6182019f52ad28c496f6800740f32f8ed209136086406802d2a4778eb302082bd69b0ff3af68ea87c2538408e7d97eb393a1ef053e0e52b9692d778ba1ad2b5b960f40d1605fc9ae179418fb88997d506b9b322242e616c18d14420b4b8344b52a0b7abb3dda42aedb87b3fe45e93f3e1523ca82be6f184361e19d49024db371b117f85fdc123cf6b383b4a55cd10182078cdbb16fde54e4748d7df468c66fc2d3d5c93b470a4cff1bda2dcb96b3ec7739e46dbdd30b70c742d51a89359043d8760babd76381a0092c71f05fa3cea4c64cfd37106c2cbff0dadc39e1536924aa394cb97148933794684fb1bffbfae2833e2c333545d02eb6cbdbcf65b8bd4d6161c12dee5cc13023c6bd886cd413417cb46edf1cb4ffa611c50e0ef9bb53a0b5201044f1f1d7a84d83a86e515dbcbc0aae328c4b894b0f7384f0d40ced95d7359dcdfb90502f2674a229c3ea219312c333313371ff1cbc33c4273cd4f3c416454415204c72fd9c6dc81319adab931c11374da0284259a8a19cf9752bc1bd2c17030c9899d4f87beb3c7ed17cb8ccf247a8f7b5d49c79cf91caf83069ed849f026c78ef0819b90f24c066025679e24c4b467dcd19c2b5ac219806e20c35b3302d4c96f1a879f8960d5dc859575058d5652278ef341ac20568358b52e5b1a65b0994064e7b0576e184cb2e82722cbf8297e0de639b0949de196651c64dbfcd891a194eb2d3e323c1a124d580353f407f018e4821d612154514b06e972a3d53748c096ecd4f38a02aae4966cce190a7d704141119265c368503c0fb7c538527ec06d3fed47aad30ea6920ee45eda35d799b8af8a867a8a94c0edb44079f27fa7d0f7e2ce56cd7d2ecc5a3b998d3a6538b4d508cf3002dc366d6d4be070d91cfe36b246d01a0fa9ae2b1c2b10a14f521cea1d7ae9e0e2c064f4024676f849d36d2fa57e44ef416b12883c505fa9b1cada820d2c91a5e63821d0e11cec138f5680da63d700bbdd7b8c1e6091a07ff37fefb4887021d9251f792482b97d2748e5f4e211880ff809488763731968eb1e66a824003b2ad46797ec36cafb2b238e4a8b2dc0b66e6a8a32b767136e28380752f4c46655a17cdf13ea1ac8720399f18df2314db099d67e3f5b2a2fccfb3882b65baa8cdba2afc50deadc4e6e5dbbf8847de74098c33acff98d805c7fddabe6bfb6c1a3b53491135873768072c0a053dd3a4fb4ee4fe0028612d52b4519ce1f2d84ab7fe5c858ef643184b26e5f0461fdfd8d1e4a676ae05e0a5b47827800c39a818365c7ca74fcbab407b1855e9a5a3aa774df8f6d152b0ae30173c251836af4e4db0b64fac467f8a0f02975b8e99ebe67b59c45fecd5e9d389fd974c373a898d6f0de73304c0159e38938de38f4c0d0afe4c94cebcf538c05203df2995b1bc8f2f9ea1e1e4b231ee547c232dafa369ee678a1d0c1b807291a0f9a12a643910b6ef63298ee70ebe5b0560d1b4fe6034fdf7028863ec182c62cf74b633ba88484f5a69a13e5c1d70619bfbed270aebc9ae3780f3cf685677da2ea0d2bdbd3e9e78b3a7270445605b83544a8228af89ea36e51718a531dc25396ea4e1bb02c34e691a6252408ff325ecf33d0bd593e48631db727ea43a657f5913484ffc0033befc8a086c1006484160b0727ca3699c56454ed04d9c21d6534c8dc37852f88b294304c2eb36c78aad72e18149a7460f598cec914c28bb6af64de100d2a0dd8a730864dd29ce92da12cb0b329648241e6f83a7a77b867566809a48f6899965b6d5e64861c98bc6548e012ef1c335712448f31a8e5392c246b005b7602df7888dc76135f3eafff4b61666afcf9961c7c2c3d562d330e88fee497bd2ccc13b3fcb4045e18c4260e511020ee4a7549b768e229019a1c4e1e66094542309c8c3014346292e7a35aa0a0d87fb0f41b6960844e3d0d112992880cea0f9cd5e3f90e2a1c58eab92dd9ee2de28a7cf2883fa17ad0769894fd7c7c9b02200e980f400e4a3f3e3df63a292971aeacbf69cb6e3ab6712994aaa6543597941de5887ff3757b5590968f0e05ffcf12c2fa714181a19be9d795a52f94279c9703147982829aa99448a97d9a4a89a0dad6086a4d65bce5dc3725fa0afedb777d90c6ff0705f1deea18c704635354a5a15b57699c5d5376ad9d5bef68aa76fe6efda3e47304db8236575b11c1b6a08deb6bfb82c1b6a01f259fa30d886803e2bac77c60db4b46b5a66ad1074adbbb719a0b92d58a36eedcbcabaa2e424257a42e63955874453abbd3eba649ae27b0ff47e260dd4d9360dbaba36c1cef2a3b63f7a6b46db071fbed1b10ce401dde5a76ebb6745ba5bc0d286f40b80a6c73c72f7358965e755dcebd8a3d37d8176e53faa4cd54754a13056d6e8ac485b57da6c2a860b01fbc0de04acbb81ff3c826e5b32951b249e16d001b551518d04edbb7cbd83eb5e9a26d838daba9acd50be4d6562d6548e9f5ea2dbbf9fab2ed4cf7d9f6d27a9b6e2f33289575d7dc80789fb946f8eb4ef1745b87783ba5571a477237b7aba97edd297d4ee9736bd3be6aaa3555b7ed294aa93b713e54f4f6a7a1165c4b64ae2542ff0f84b25bed4daf64b5f7ededec654282b4b46ab4a76d9f71fd6482b49d34979bedd4d5443a85c1ce7765d397956d6aba571abbd37e4ea527fb6e6e0ae4a643ab6be4d540ee9b6e444e9a4b44d5dbdaf6192d062f686c4f986a6f8d5d913697c6f6956ddbb2f6a6aaa67d60c526f5573341995e1a1367d9269d45572427579aab3d35e2482dc7b9049fc366a4e1ff7f806a9dca6744affa9a76397b56da89235d75beea5acba2ae308e708aff17f2aeb088edb5bdbad6a9a6f4e90a89986ecb9dee49737955fc07b93c21ffdf9243fc961c46be25c704df9243e75b6fecf8d69b36df7a53c6b7dea0f9d69b27bef566886fbdb9e15b6f50f8d61b2cdf7a637eebcdd3b7de187debcdeb5b6f8e7ceb0d05df7ac3c0b7dc54e05b6ee6f8961b36df7243c6b7dc70a10688771d09f2ae2338effaa1000bedfd54f57fce12ff9f43078e13ff3f5a7073e6c851e3766370561c1de4470639750cc991e33fe70d900740136773dcfce7c491039211fa9aaa514cb8902cf9777178709fe49fd5c510cb8a8b8392018e1f6c7ebcf19fd3e63fe78dff1c37aeb8bda32cbaa6e6ff8f3054fdaedaaa57a553ff396db0f1c38d967ddb9eb386aaeafd9fa346ce0effba9dbbff396c7abae63f870cba228511464e1a3968f00ef59ff3ffe38c29ca4083f7154a8e9a1c3372ca78165d9172c8c8f981fc901ce939b2fdbfcebb8e58f02fd36b4e9aff1c09b0e88af423859c0838e58c91e3aa2a6d09c8ccf513814f192e1f33dbabb704c0f040403eac0ff781faed8abfb6b74ff98cbfbd5c59577b0e2c1c1e8ccd8d4d4de9f3ff7cfd3376c63afa7fd9bb8cf8b432590e5a531d1474526d170763fcbf5b04a328abaa1a455d1c08f9ffedf5b2b03c71159152048a5fd9f49dd166bac7b657cf923e35b97ad4e834eac96d3932c8d573e4eae971d9ed6e6bfab47b9549a5bbd5eb65f24ea57b140ddeb50114a68b489a7787b277059b288868e92fed4da3c07eb6234a36d8c64d71e5c6ad28cad465ec1367d926aa3ac4871879b7d6a9f8faba431be7c284a0927024180c6567bd03e9add35c377d61477bebf3855df52518bb699653394f3755d5a8d510ebba408f0bdc5cb0c50554fce6b36d2fa74d6a7b55094202c605efff0fe13d4bfa044deb0a30fe73c4f8ffa6948d42e26b9f39bd6b4b23721a725560f48fc46936e5389a6e6e4f59174a5b3141725fa09646c4cddc26cee55569934cf39525e2a8b63e8d3cbdbdb6b1ef06357359b4abaf913588aae6729df977572b767de2722de06ac5a1e36a9971b580d8d877735b0e00ae205fb88274e10a32852b0813bfbdba03a1a06ddb6aa8218737bff3f3ae20eaffbfe94ed5d955e90a0284e40e197997046c9ad45ed593662cd4aa674f516f7f7ad232e12a13ef3cd54dba2d4957043e425c11007145c0a2c305811a170462b820b8c205c1025c1000f16faee9d976aa52c3603d6d473b136ea5b140ddc6be40b573e35a16a575dd966d6f7275cb59bd335a7b6ad3ec7a6bfbec4cf7d49ac26030180b0bda843618cce53620d80b26a5dbbe056d5560efacb6a6dbac77aa1fd8912bad04f3d960475ad608e6b32179d70edbd07463d7d6477335769382bd5eb02a1bfbaea9abcf97c6ae7ae66adeb2fb897367b31526b575604ead6937abc0b6976e7bd0f6e3ca68bb4df7566b750b73dd94a3a8528754dbb6d7c8daabca2716c90f5f69b0d58a5df2994acf0dd6f6a69685826d1a9782c1aab8dbdb5d1d4342dabadaf946a5d2ce8d7d37bd6d1c4c4ab725d016b4e9b67cabc0b635fde9b023a8a6928939c58909ea09e90826057bbd90aae8f64d37a0963dd3a718906ebb9bd6aa6c3aadaab93a69e3dad70bb6f5d9dad25e1feec7a7ed6ffa7ac1b6d7aaaf402fbbb6e9cfebd63a9509abea69dba66e7775cba560fa65691fc0aa6cae9175a6651d28e5eceaceac7efaa9d7555bf9b47c8555d96ae7c6c6de2edbdad811a827284a47b63ea5d957bf3a91edc7479602c13a18ab5f2244dcb7afafdb5f74ddac7c3655a73157f7dda390366eaba967ba5dde5dbeb2fb5d3b4fdda8e0c6cb9a7624df72b3c44d5d3740c6bc108719715811879938bcfc6c6e0ae4b3f5878a238c83678cabddce3b90db6d68438730665ad6ade000facf4173a60d1b6dbe680381364ab8402e3b5bb78db739fab762d7cddde2c12b84ed05839551484f4ba551c9a4f5461b8eeb363735d2f697add27d36d36f8419817bfb1b4f7dcc0b9b1b75b8a1861b62b8e100f6dd7eb635dd7c8a6c28abdb971bfcd59b1b49dc70e0a7d674cc0bee4c96b601c2b68de182b61c30fe73bef8ff1c2fbae869fb72f19fb3c57f8e16ff3959fce760f19f73c57f8e15ff9f9353c50fbd5e3be57d85c206cff63272dd964362ae71c1ff6b735dad461d1ce77a53d5c0e2d500a383daaec6153536359ea8c1b5dc39c5660a1b2436ddff6f40459d4a4359695953c7f6b24d36eec7f4a1622ae1f512eaae26da5cbdbd5e5049b66ddb02a024c9e6ae017a8eafec99c61d699c49038cff4d48c9b6bdc6ac90851369a0ff1c10e7a99ead5074aa0eed74c6f6a7fed4d553f7b765550da5d55d2deaaadad288bc961a04b4d498a0467d33c83083016600f18fc40c155a65a0699541a6558612657c6568f0c59a9a4ab553aad66dcd4ea55fb365f9ca9e54bbc78ab6a4819ae956a751f2bca7bac9eddde883541b75b75369a8ee3af19ed6b8ee4fbcb72220c3186acd74f9dadd96181db43de562efb671afee561ba7df8d635f2b261b87b29bac5b6d6e4b63dd24176537ee4755356edf94f60373790742d96dcb9d4aabe713d9be32f9dadd2fd02f60ff4fe3a2d6da428aff77e05b5b30608b30ffbfc55cbd6d2e3bdb4edeca647ced33b5bbb1a9161665b82df7d2645890100cf10f25fc2f218229743033b4000f343495396207206ee7a9fbd298fe719c9045800d2b20c035abfee784f9910615534d846f5581c73fcb7b556c56c389dac1ff6fe55b5568715bae0a2a55405541c1bfebaa6ed09b9e3376e778d93daa650666068e14b0161560a828a14545fa0fe5aafccfd1e189fceb40d09a028daaf69d032627871c1cba8c7dfac769e2c71938647242c071c00d3624a0869c2f577ea440c30c32c49003c30f28390878e1711c6895c1e33d70bb0c68af35778100bcdc32669451a255c64bab0c51ab4c5726e7b7a8a6c791a387204f403b4d81de967300ec99982d2770d0af979bab5eaf74a6835a4d88d13601812680f84722a355b1e7501357efbee9696e4a8698fcd5040a916e2269883e9bd8feb757130c9051f39b12a16d7b9169e29f0c1832379089fdffcf9664f319b2bd5e57ab6498fcf672c91c2133011334fcffbbfb9593e606b941bc773708278a06b021a4b5c3019e5bf5a5eea22853eddcdeee72eeeb6a75eb556aaa5bada6af06aaa9412bbb35baf5ce5b761674aee991b7d78e6cbcb71add5cbdfdf0eefebc6eaaaedac75d3b0d8b668d366e43551debe7c66e346ee3fda7e6e32e7144181896f0d22eb19780f2ff542a8de8bd2a65ae8ed2afb492aa773ac5d65a5666a4841dffff3b0000882356dab6f6f64d81d0ed0856450ac6fbeb05abb24dad299014acab3c3557f6e59c14ec4a2bf553055ada60eeacf727ae967529985ed38d48a77522486c249e5e401ba7b1e63862ccbb697ad537293b10f9cf71e1755a7544929223b8cefb4aeb20f64eaddd75bbca8511556c2faadd8da019416544132344a04d0990db81288b78ffff559524a16dae36dff4c4e0a7881083b5f39f07072d22deffe77e76cbfa6ced79b21c00052b0a5614507053b47363a754bdc160db9a96494443474543a5d312143e581520718e5400d60447c31e87ea0847c5a11a6284f7a24e992d21d208a1c3ffbf3655559b4248f0ff6f9a9cabaa4198c00691f4ff5b42d836b7e55eaf1610b31fdefc9026cc92ffedf5c3093f6cf981f683fa83953078fc6f4ab617906efb98177278b349b1ecf0818d9c16587877f72af75d53dd96503e60e083cb87067cc04182d257fe7a99e6193c6830c4ad75aa0d6f41565a2bab15d4ca69c58a152256aa3c902140b81d065a6162eeb774f8e25b3af49012b01cb020fd1698ab1f6a8169e075e000c26ae5d042100e6970c802072cb6dab96d40bc20a5854353eb0635ad1ba4f856979c6f7191e35b5cd67c8bcb18dfe252e65b5c8ef8169717bec5a5f62d2e55bec5a5e85b5c80bec545e75b5b28f0ad2d6b7c6b8b17dfda02c5b7b624f1ad2d437c6b0b0fdfdac2c2b7b614e05b5baabeb5e5cab7b64cf9d696a36f6d11bfb5e5e75b5b22f8d696ff969638bea525cdb7b4703164674747021c1cce7d529a9cb091938710342cc01de008091c4f00e1206401242da4d6977ff53cd315870b1a9af82ff3ad19de7c6dd59a7d5756e5f47f4e91ff9c1e578dd3785fad983a8ff29fb3c17f0e919c0c8c6448f36ecb95b0d49a32e67f4d5b31f09c055a308cf0ffba7aa6ce663024f941220f40ae7461b31d4dd971af208d40c11759a858b8e24a8b203213d540aa060a8518182668522136331cc8e22547faae21248c304e2ae992316ac4885932220d0014535988708244eccc14097b3871864a4c070d7038fb607214a3ea56887cbc1c88816f0001b2028630622c13be8802c4aa3585c50319c8f143cf1b53326051009536189c30a5eb58c1880d9c803105c4c416363a4c0c80deb1c70c6e5cb96144a338cd8883cc1179a02a5ca9c01a93002ca0514cc062010ad0a20194c81768f0608101253d0e0d1c4085cb95ee6328e83c380f367a019a8c10411d54b4e94b627cb104e5baba1022686214b150c4d37062026ac206426e840c53d026d444f15ae972ea92b8e24cc4a2978e28a6e011008113f889e5b0c75013258568491f9b8724ae8c123095f78c05045019410214fbd5c5a96413668bad938110dd60a387930b3b9e4c37db08670288645001360fc6d99560f365840fe2c4522364e1e24bd07b98f96202497c0de8295e45402c5841a35d30033ae1c1962b6dc53d81bb628f3a340751805184a54019d81c60d5085366c081394124cacb478e1b60252c89e14a308204cca98728147270aa0386c19928e5150c80c35c830c33a81630e5309310001b37af248c985c52d84188d41bad793413a1b6a6892a664b05906b92c10810cc388232c11a51ba8010c48e10570d08820508af0e94209804d080b02487498f9296214058008c6c070d1d440a0c2f51c6252aa0498141c50449152185d415256a8e62582393fafa50da29a3298ad45f31f3e2895bb362851915b811c4a98a9518ba1c91199280162b3a8389410ba4ceca0046f888a4018452953b7e586163019b922a50d8e0e4490a3566a892425051033a08400a55a20e60030c483ad8a10a11205b8aa45040042a6d2270ea5409c2810a03782c10b5f470a12282d5511e1d06998a120a3fd8418119262a0208d3460a17180fa6a0c18613a910146031a58821241054b140c494abaf480f78b4724c29b2e2c61b413e504c79600f09ae8e864029629889c01460be235276f8028dd1cb0f19296c03d23a9e080252483da48507a9a4e42829e17a526ea27021f463471448a410e50b1f10b8b9810d3551b8152e070280c34b944e073bc6b47ad0283d6c6862ca989792280838e99a4c01431f512e00e120072f22b822aaca1225581460294854112cfa50028af10014396657f001b43c8042668d21beb8cac00a28067072930626240c940f2440c7044918b101e5020464708031585c2835d20859f3a8c30a94029088280243992d505b888a0853420e1c5493180a4b904945a08230b1801415b30c9ec8b883768a91c47efa010f293688b0c2f4e40609008e285b1a78122a228522e6d0443c0141618707f82cf5387d11a68dde00313070ca810a82057ea650e1941269654c14413a7d16ace0004e0e2a9c7e0206ae900285fa41154d9831041460960f60988182a8ef88990f6057674850068aec832355580bd0811242931d3c35e091c3e46c7aa20c1e04843758d074000ab8f9e0cd1e661394198a48c008922e4d434a06a041c203be30bd11e6015c9ca94015a6055880c50f127c9980890046a069f3804b02a6a424145a480d588269821dc2d0c080c18525342e20c004c828fa61498c1e690a31900d2c59714ca021830a254b44289819624d0e7cc90131afd18b861c94c270e2a70b335928500ac3079907c83d9a28ada71469b08047ab3432fd68c3833cec50fad1a625a0da85a42d30a0f530b3c182241ad68080011d47f024ed36d6ac89820e264948d2e8b0e68c913dc9638b921c37c0a23c91820b5159a3d47ad2421346a34949104fa850d0c6150a2830f4a4e78c124c7055a18593374aa82a19ab9013269868e28a614be78404ed4515197c3ce1e4030b1e8000175e83130b40e002c48a170b486cd860303a9871919018c148124a80b182a465a7873650de144846540c78536405072409dc4c31658b2d4947691480e60e21666a473ed87125851a30faa895d243ac8082358e94ec1f39ca58e9e10800229450870d4e3068e285165f1c994007249ae02080314551520b9ac482e4b1456d4ba38947a45b2a944430ea838b04b82186961f46667c488388511536182160cc124be8ac0230325570630290237a8c7e84a690605270a3ac03abd1438d3086944f18590389064108509e300696357d8c80534211510703f20fb0dc503802e483e3021337ae8a6a63dca00493258270a838c3c91d4cbcbcd96247e9cc0b4c9e54b0060d1a6e9a98f01881c1d512292c293a43ca1a980b4abe68082864534b66bea8ea8b42821860be68a84c4d54e3e58b1aa0b28383ef9f084d135264d0c23f5198ce06364bfe896415c010463caa62e3ff8970c0985b1659f419c2186107e162881bc10b1e57ca98c0c29229bfa4e7df042923c76d393605e25fe7071fee98a30d161aac32c888e0d9b6ed69e648b3264d13ffafaab4757bf559ed4d43439a150c90c6cb0bc8278d549a6d95d154766fdbab37daeeadabd729b66d696e5f59f5075dd3202403d85a765bd24bdbcb4ef96c9bdbd754d73ad58f11ee3a9a95a0aeeb3a9a159fadcff4763b8cc8162920d8f682b1dbaa5b3d33622a5978ff7f561fb3d5b16dba29cece03638c2e31d2e8f4a0c383ce0eff3a61fe7574006385d6a2b4f53d695f48cb1e2d0c29ff1b501847ef0ac307081b3442744e62aee3701fe1d1a8ffef42038566099a4f465b3b1a9f9f69191a2168feff4c1b31fecf0c9d79e2b7333c3ceccc08b0ed5d5336f6ffb03350679efc3f0cb64dad7ad55d65d973d36fda9d6e12183d80c1b341fb85149d196278b7052fd47821e45b2a2e2face81992da2b430cae4b738abd7ab6a9ead0ebd5c110437418f0ff4cefeac2842e846c81c5161dbc077dfcbfd4bbb4e08fb91c74d563415a1c3df02e2d8c68e1fa7f2346b62c4ef80fc17c9445ec7df08246161f162d7080450bff6f5e7559509031d705055d21c40a3e56b61d9ac9a0b4535343ffb6fc6597b8ee47567d89eb7ee4aac2016dcf92aa33591bfb7f76c9ed53f4669374144085140ab8a64033c50bdb1450fe1fcabba680c55c1d836d1c9b6a1704c6bc0b025e5e76e52e082c1982c0ffbba488e2ff613c6dda7c4da9b610779190663228bac7cc8d4536e55e75dea7b4a9b2ecb9b3472bd3810b8a312e287880e2bd28cdbedbe6486f77dd219e9aaf9757250586525d7e4285ff9fcd74d493cd95b97d4a736a00c0850e97256ebc5e11fcbf13474e5ce004044e3ce004007e36d366d19b2f210dcc1a0a10e60105181a84a1d744e9420f0414017a60e68aa033009b345bfce8b2d9e12273848bcc12323d2e269470352036e6ccffb72c1af54486f4f6a7feaeddedbd35e261d5ebb504035c0b68bddb722acb9eab90d055e72b47328538ef52d9e3bdbba690d08f4f92dda3d6f4094f85381292104fe2d63a15475ad3277daf29d5cbaaee164ab27bd49a3ee16bfae40b696b57cdecd190c0f21c297b34ced9a2d7eb082c8ea0721473f591cb08220e8023c5957f9d542796c3c0991b3e0b96344448f97f25ef2a4289ffe7f6c62e15d14211b3ff229a8ae08ae8d988d0c345849a7f2e0a11649edb9c5d8366380f8869e3b69c1828c428918418312b9b4e9d419bdefe75a680e8a0ee9b9eff3a9d73dd63ff3afbfcff96733dd23810d3e312b38198ed8536a0246f4ae41a624c183cb26b0897105c3c0e08385138610f0b02d6f603841f54d6c0e9c205c4983c80f0097a29c0f543091f54eb542b8d5d5d2a165d91bad1cb46e9a2a13755674fbcd57cd5ef537fd92a165d91f84a33ea6d53dbabfa9269b2e83ad3b2a827332d337b6fdf138923d5a25e2f105c3c08fda36bcab24edc4a3bb97b65d73064b81beaed4f36084048861934004194122247936c8c174e48283ae2ff03f00387f5ef72a18777b970c3bb5c8081a5f32f4196ff8ffa508ed90a62fecffc3f95ee009c273698231673c04a29c6ff5bcd61011c357c931a1a28c0961e130c20e4ffbf02221ebc444d10c4ff0761800e0ef8e0ffffeb59c2042a5c5f1a20c6f5258ceb8b0cae2f24b8be60e1515c5f9ab8be24717df15c5f38707d71b9be4440c30a34b878a82a0d851f23579d91fc6d1bca6e9a4804d70c3ccc40c3ff55f7c30dccd534ca7fce18dd3fefbcab485026551d52d521570c643e7bff3a5c88c7afa6db7688af5074825676490748c782a9b62badbd49e75d6700bd65dfa795e96559a7b73719a588524229a0944f8a27254bf9cf6983a5f4b61ff5962d5a5376bf29114fcd28ec6637ebc4d7feba3d8a966997f624e6ead8bfce11204fd5b24635a22635229756f4aff3f3eaf80069696a93ba32e957f37f1d233a426afdc93f4e133a1cd43a55976bfdc98f33be30fdd729f2ff40742cc8deaa6350bd65958664ecdaf9bf4e8f8c5dddbe5336ea0527aec6a6a9aad39d729a13379dea49b1dda17ad58f14ae7436e854e9ca2abd29119d214fc5a4b3e5dcf023d5d100c70936ff3fdea09a05c9d620b4ad72b5a0febb5810f3ffff3a19e860e0fef878318077b1d0f32e161a88b934d70a6d9c9494fedfc83f0e1b2bc854a080ea82d5a07026519c9aea77ed9bebae48dcf65ad974aa756beaaa7b958e751c3774ab673823fcffec5de61aff383be860c03b16bc0d20dd926c57605b07b0a7a6a6a42dc996c2784ae416a94f6d6f72fb948bb2db064a3618920d06db38cdb66f6f37bc056db0cd4d3be836377d7adafa19d46d3df6ba4666ab4cbb1d4ecff627e8a4b93e9ca744f8da3722ec4e5d1a4b9b4adf9575b93e4778dd5aa772ea53ba89af25575361eeebe5d2d8da9b4e759577daa6ea97f75ed537a094f683b22e27054399d69e04abb2d5ce9aaaeacd4d5d969dda86fa949eb1b31f577b0152d4454dba5239b9ba80f97f98ab8b5ae3bad37a4d8db9bd35ea42fe4fed978bcb13ff1cebe2b2a48a162e147009f2ff2faa9f9cb85626eb2e2d52681941cb989fc9645a6ad0f245aa4accd547ad4cc6b92b4b1b52cfd4a56a59abada6aa6a4b63a76c9c4b7bf576e4889b76a21915cbc66a27fbbae9db9f38efec39246b9b983cb99cabea2a59abd5218ef795b6a6acdbf6ad6551242ed7bd1ddab8b6772a6df5ae9af6c1eaba7a563bd3297ea595b81f9f0d4683c96828bbf5297db67a6ad36a8a3e1935216d2dbbcd6434de044b6b2aecf582c13634e57dab9d1b106dd5547a0392d17458394a508890ba9aa29b7b6e4044b3f45dd32bcdc1d6542b1559b93ad6d1d7dd9a38fdf6da4edf73ddf97f1913b211b601b59b555fdab8960b0a7abd605030348f3bda709be54d6db02c78c075c5eaff7350d0cc75a50f489d70a541bc4c738909e9c9a98c32cb26434e4c504fa6f94a69e995ff77c5d8b8622ffc3fd75ba01fd88f0ff7a9116d1c517779fa76f75935ec26eeda1aec077aeab69cebccc0e4e9b9ba3490db725e95ce05af63c1d3121427a694212576dd4caacdaa2badbf6c551c526bb891375dd33c383ba2807049a9ffaeac091794987f1d077ef72a738acb4a9395a47f7565537728086dabd634086dab8c9ca262ae3e32728ae288986cea0f95ee4e4a01674cc47daf2cd9f7cab41a85a33b639f74893f4fe4dcdb1bfd4634879ce3a6c0ca4fe7ad459cf7ce1de73cc644dcbea3bbdba1cfddadfec8252ae66796bd9fe0d98d604635399e6137d27a8c9c7ac4ac04841287bc935dccf904d18f14398ea7acf9ba6b684a9161e78da3d88da4f87560f6bc6f24fbb83b4ef6513cd1738bdfb65276cfc367eef4d78161d7275aa5c419dcbdfbd02f9f5de6e308d563e4d493bf252a66132a65389ea0d8690f0ccf9888cdb74f91a1699a27d506f31215b368ca2766d0f3b03782a1d87578f4bcfc8d27d9c93d8af9d4224aa2524a0dee1115c7cd35d98ddb8b52ea4c7a1ffa658f87e3e99d2d7bea0da2f4dea4a8cf3083baf7ec612750b817821ccd3b9f18f5b487c644ece124a8fe6911ef9173504451f1ec193f792a4fbe35f8693e92df89710e493c3a95e8078a79e3dca1b9e38f3c6322e6eceb835244c1b1f3c01ce67ca2641f39679b4a4de6bd47ef3c43347b1aa35d26994a11c5e2de39ef4e76ba9f39266275264bcd515c2a4794ec7aeca2388e9844d19e73074a65d761117f18f4d08e876788ba6298548a23dee197bd7d765dec4e4fc6b6339c5f4fce908bddc3796fad77cf5d4cc433b6e5d889931287b87314ebec75e1879e614cc4bb5799eceb65241c7a62279223e879e1e7815d18667067be39b871c7990c3136330697a8984f8ece2665474150871c8764c7a7e87d1e18c5a8fc3aefe4fbeb279ac5f1dc684cc4ecbba626c6e108bae7b744c5ecca12f34ff4ba4776e7d99dd92363221e7bcb16b53aea24fa70b844c5149994dd1bc7b1ebd0f3f4b0776e1465cd3755672e9954548a9a3cbf7c82e389fbd9654e547af814c12ea320d7ddeec42f441a2abf50cc1fe965cf1337d93b1d13b1e9618c43bc7b15d392523cf70631efa1d65fcf9c8c89d8dcadcede5e6beec7c4832d8a9dc8b93ebb107b61171371ab63bbc33521ed81e1477e9ff6b60776241a1371fbba23b844c5b452529eb9e7139f18fd72c875df3111833d504d7afacaaa451cd75756dd5db844c5ac92a41c4f94f34febfc9ddd163d1c13b19971f8ca502fec40c6b2a6ca5bd35ce05592609847929f60b775ff481c1371cca5cdcc9645439c44963b8f19ec1fd98d2238628ec6446c8ae35866ceb1479268d86db177988c8978c4a25872107330e439835b7778d431115bb1ed94897358929aa3986fb1eb780441ac632236f9aa598eb3d803d5a467a8a7a807aa072aa967f728115ca2624e01bbb087231e7588f7a94790e438325ca2626af9f077628dd1be3b527ffa3bbd29dad999b8baa7cb34d30397a898aab7fb377ebd4343ef1bfb378e4f1cc771f94333397e1925413e76a7a7370f6322ee816ad2c3bd3294eb814aea31d7d49cb12d17bdf69d693c6611f4c40c8a22ce3814f19731e66455aaca95a3d7755afcc88eecdd177a3b26e257869afd4d5b2bb2bf691b057c65a8d75b767f4b544c279de9d25ad3ac0009ef6608e6239c2c08f716355bf6d517006d03307f8e10f9717d7c7c8cf0559b1f07478ab81bf460221ba02f8d08367bcc34b79bca2e18626403d2c048063f185cf085185870c55abd1954404108e64d081e3336cdd826002313b814f064966d2b70b1ed9196ee6d05413290e0ed53144480dd0b20c0a94cc6fa3c908938d0eaae6e0b1ac01730903708c03a95ba4316c03e40da2200e05dc66e7127f7165385609eedee02e96c9053e487696e8073a5651ab0b0c9aa26ab9aef55a7e087dc780085185697e935c70328d06880813fa3020d436f25a5027df041813df4c8038f09dc61471d74cc21c71b3770b479c38d36d858430d366bd240e30c35669441461a0944600c31c24073068c2fbce8c2032d1f81073014fd1083813f73c21343cf53d4e65f042f04d8f112071cac0838c16a82e88b9d2e5cb6fcf72ca92b8668dcd1120293129a70f9ef62c29008001076b2ecd40240c692a69d233c4b549e254dbb1de2f120cde780830d251c3d09476f4bd441de8a6c6b57bb4dc9906ded2ac823f42ab9a30e228cc05f5a6021aac9032547bf13074f9208fc4e55f66821de79dd9d7607cbffceac294bf9df61572999944a75258d9d53a85497dae1fbfcdfd1b0ff1d730704299e519cc0ff8e952affdf028fb851f97f203b537e6a4d559a0b72f088e733c383a1783a1a783a2035ce1275b9d69f40b56cdb7a2cfa8519264e03ced5b23b4a517e27ea08d21121d9be2a77a0ec40fdef3c79fa4f5728b5752857bd5e7c4d9ff858f1f9e065fbca69e7839da29da61da6a51da59da49d273cfddf71d2ed20ed1cf1700064a7c98ed1ff4ef9bfc364a7a888acc829db5733dd42a92acde8096af7aa7f9e2178f00c91c05f0933c3bb43be213f3810b80df06c38ff394a03253408c28b7273066a3238f3b27d75b22a1157d32846321e0caee0c1e089ecb9d9ab7abd7295108b0aad3d4968ed495da6afb45297e955bf9a0b71242419bbba2e57d3284249768fa2a17c28c4e3dbeaa61a6789de1a67898484b297247b505e77658bc68c6923add6a9be30a5094125d93daa82113e57993c1578c05301e6a960fbd95e3efd4d5b2bae4f77e6fe17d228c0f2b4dda3dc7436e4a6333db5ce64a90b65c5160999f2df44884700523c93d9d3f67d3267ee0454e8b6ebb60441100441f0fbbeeffbbeeffb3ccff33ccff33c2fe79c73ce39678c31c618638cbbdce52e77b9cb5dee7297bbdc65922449922449921cc7711cc7711c47511445511445510cc3300cc3300c4310044110044110fcbeeffbbeeffb3ecff33ccff33ccfcb39e79c73ce19638c31c6b8ebbaaeebbaaeeb30499224499224498ee3388ee3388ea3288aa2288aa2288661188661188621088220088220087edff77ddff77d9fe7799ee7799ee7e59c73ce39e78c31c618638c3b4c8e62087e5ec64a32a4293c0e24fdbf6e3b1255f7a7b649d54eed53d95fde572b3d73db5ed3eaa66a26432d6d6fd68a55d175684913a96c6c5bd96ded59da941bf10cf1ec64f13c3b293ccfce93ff1740077f44039267e77b1e9d389e472789e7d181e17974ae3c8f0e93e7d129f23c3a0f3c4fce1acf9353c4f3e4acf0ff25d213bbd4764dc4355191935bc6de3625d2445da5bd3455d545e68c46c5b2aacbf10eb8556f9babfba9d5cdd53df6b6e9a9b735fdb1ea684dcf90544dc3b2e3fadf69fdef04d991802707f6bf13c1ff0e04ff3b0fec38f0aa66c254b4549a4e4c4e4ea559264161c2a4644a195252d9293d63db595295d094866aea542cd4481392f517698a5585461a525fd3a9b5c499f2cf72add8f5c9eb7654f7a4984b6b9a31edb4ed329aee496bf93c3f0af91f1fd6eb7081c5bfce163a5a005181efffcc028b5ff59acad276bb29ff2dfa501fe7a0f40a2bfe65aeb6dad82377e6463d61e29a54ac5012fd6ace07d85b9eb2514a681bc584b6515f583a41811a5a6a82f45452417a32999454909286909cca27734af9c42c424a2282d2f4edc1c5a36dd5ebd5dbec954e4d23f8f4f4ff5f4863f3cfbb1e1dbc9e7d67b5769bdae5bb0bfd088d348e84070a38cac829ca7dbdfea752a19f0d88fba4e617d25e5675dd9755dd9f2fa46d405f48f3d9464e51afdba7d0360a6dabee10f2bfd959c97b3f3591c9a2d5e163959299eb4aa221a2a723cb4ccfdcecd1424c1b6963c6b42f4c99e6b0e1d373ccd89ce3c89831ed4dd137f2cf2a23655d69dd107ef852c257fd93374ffcd3a6d2b389123762fcab6a88a1b4bdc9f566e8bf88db72aed9f699cb0d162e3752b889c581471c640001134703ffaf571c55ff1f7b532bb7e54c73e6b691e28da437baafc0023f7c10c2e1e0ccb3b6605df1ac29583ab0b1bdee6d9733b0a0838437cc80e92226040d928879bd2e5566d858b1410b3f5d885e1fac228cc14417078e58b026878516b8840195c6055cb411e012460826274baaa0c14546c613211e013fb8886b9298f0341bb8fcb8800ff2cb048ed8a2050c66acfca490b48506ab9d23862809c096fd02d51315ccd0c116244d62d016e4c0434b1e0e448d71eba0428b140118628a3247462d2d0039816a0b283db44cf981c84848097768e921410b1cb2ec9821bca16200893dc6a410986062460106a082510824fc5003e704255e84d0b4c51be06b06098450811ae590094c01210b1b12722c006ad2250b1262a86503b56709018503c04622d82c460b74e025284a0a5924004301f4006266b5341f1501b0caa254f3a1851c3a78c18c925aeb40121220c0e05a12345a5d5e4f841a002c20c2c2ca1dac2b2fb4803327204c5ddd8000139210a2ebe16a2a88d70f141d63ae321c66864410451e567d202df9d6c0c18a9599d797326db81c618580d365831399032bf30754d01e059859f96c518502121ba854d131c600c2a4b085a7ea01509871258b265375c2149b38cc2c21a9827aca400a18a114aa36222e4424f58186e6c60f3ed0705921074d892dfa58b202f204cd8b0f9d8117645334273b80614693110cd07898c8e8e23e20c97b460348e4282ab0df219a10c08d2e68c0e1a579a10594378b90778886858c3bccb85e06dc5c2043141f0ce0a29192c61035a71c5707350b2042c5cb85abd6e0444e0b45003744a3e3a24a55162ece920aae169381360b2721bca1071ea29d61ade20418ea38a13d1f00001d8aee00a30deaa34d400e723c6199401f514c40810a69b094c95f1959c24803cb0a0b60820f17a6506191f202123ce48ac2b2011045a078322498b971d9047c30b4316bc00f073c6584809a8db0bdc1c406138a664d1540e1620bb9c58c82324d783163a50bbbc60619b2904d80057bc496124b4f10256c9613c8123b0580812d2d781da1b2a18a8d000d18e268620bd02a819c215bf4d066ac3dd8b0c60c7b10a0cb8ac58993191498fab2762044065e50cbc2ba330489280e104503b22e945801ec408b99cc06a809c8bc90861a32348821aa20627a43968744bd12e06ea0f281b3410b788c11552abe36964c91c714ea0b2d3051c3484517aa9412526556684004d548192e21034a402a3a70ae0081e547a81c70a50c3ebaec08416502942e385391a17ab2a2a10e9fa88e4a83225018c087374957d878833592a04200ae24e1e60b6e18064784b2dbde387a6127338879fec4590d8ae36a2fc799b8f6665602700850ee1e76f9433fafeb74e61cedaaf9a269cc35df96754d30e37829fb4986a0179e631eb5d875dde1b765ddaf0265c737173b11cc7ad46407e63eca1014d131672ee6b1839a63f65dd3918f120ccfbebb477ebd13d13cced89609c7cdd8969324054aed79681776bd4331fcc48e7b9a9c9a927a7aa092f620412c7e2299711762307bf8fb46d1ebc00d72af8f3914bda935adf10eb3ef8a04e69996792f4b8f121d45bdb9debbf79888cd99968978acb56387f328398945d0033f32c424b83bb2e4388e834aea115960e1519227f98d7b730fed3abac73e8192875af7af7732443bf1f3ce3b4a8fd4df78ea0d861df86dd28e52e41e7aa23d8f2117fb178a7594ddb7c58dc98ee68f1c3b95453f1689e38c7a8a7a907aa0927a665a89e3665ac6716eee301d251a7ea3d7852307f7c745f2a4526927d932a1ba2715f544f514f53ce929eae17db562e2b8b653b55a8676a7ced9258ee3384ec42188f11dac39ca8ef7ae8b24b83f54f753d43111cbd816fd7aa09af4f0ce713d50493df8c341b0e42875f6f6e981212649deb94792c07a53eadd9de3b8bff33b439204b99b12243fac71487678f44074ec719462f74e14dd1e1f73e76171c3519218f4d00d863c771d18ea31266212a9a7a86d8d769f3d711cb77bd5d8619618569bb2effdf52d669044f5b7bf5a13c775f524c1f69d49b1de28c751445151fcf4f99d61ef27cb0d8cc191fcc20f04c390ecb2378a188f5def20984f4d7e797bac364a8f243daeb1ce1b6f10f5381ba597f3296ef11bf7a94f71edaac9ae268977e7ec06b739e2354a1d9263f7a1d80bbd0ed52057a3ec783f79c7b7c8c3512449b063b129f5e7ed7c92a038628cb3f869a8252ae607ac3525e8811a3dc34e83a03ec7b0a75176d01b39d7dde39dec21ee388ee3a6f4cb716de72c344aefe479cc1d1543b07727076322c6d2cd174ba939aeabef4973396e5ca262d25867781deebe0ff4c64f0c470f779ee879a09741fd7531a31d7be08e89b865abb48a9a53a9aaf630d7586a4a3d8264377a1deeb8478a21ff92586694e7d8f1d0eb5b9fe00876e8d879e7544462f67d7bfbba694b42b1ca28c58df9de79ef8c398a6a50ede952af62db268e0b45ecaa19f3c122a30c39dee7263bfe32b9f7e69af3a1946d2a398e771f5869ca73047777ea737f60c7f947d2786fc350882581926f4fece0f6c4afe3243fc30e8f1128fbd8791fe98560eec8bc4f322662aaad671fd86a55ef278e13db9000ac314a7d7e5ed6e8d99d24de5ca5c93ca8262a4d89e3bad10b961865878e5ec7fa13f3f9799d1c6322a6ca56ec0c0ca30449b0771bdc9d16f73ec33126e2d46cb1b899e338ee0b691cc7711c27b25660a129c73d8aa8b84f511cd12fc45d4cc4ec3b62f0fb429a17f255a4eaaae6bdaaa5ed102f5131abb0ce94b9f378973729629c35064130cabe47516b10e4e8ee1d14bd2f4af21cc1ee13bbecf5eeec204af3a2e4a2d7b527927d6f8e7a1eeea2f4b2f7e15387b86f10f43ccc4529762fe3108bbcd35c77e3b845796ef00c49116bdcbfece50eb3b4d821ea7524cf5f0e3316774cc45f28ce64a987c4caa2243b71937cefcf133badbb138b72d4e2e789bcc327fa718fecdf1565d7fceb229af9a9498f0c452b4a313cf3e771b493e0d745de55517ae0cee4d889dd798e9e287e66cafc7de4f7657c76b88738f4a828519004bf3d8ee787c52f87798af21cb7263bef9c731da2980c31044a3d9260d779077a2709e22cce6650a4283bdccf6ff74f8bded981dd78d58fda6ec52681c02a53724c76b1f34e7d821d6b0f4d89acb0a22849fea17d771d04516fdce0f7852814e5d6e4994774873bc431d61365f8a1baf7bd75d619ec5b8f4f32b635e2388e6b5f3794d1d607949decc89cb528e291f4f0d7410794a3b7f589c7b0eb1e8a4734344996132507b91e75973f0e6af244794cc4567df5dafed4be4f188bedeb36517ea4ce19a359e7500cc51ec6442c635b9474dfb17d5d326538826197353972b0eb3239caccb5ab22aecdd80df67090d603d57382b41e3277161325b8b9ce9dc77b3e3f8e379997b01a507adfd975d6a3c6d8fbd0bd4326ac31251fb9d6a326c7af0bbbee9daf0c35db2e628fc580f2cca3f8f54e04c9affbf2282ea0dca7de9d878e9df7e1fe75a78e2d518ae83e3149ee8ec50e7b60672951f253e37014478f93e3497a6288dbd7f59228470fec3e94c45ed78562ffc4d75540b9f9798e9d1647d4eba838862acb9e489459ecc08e3c3f1493bc8b1e1813f14ccb8e28c92e0c419044bd3d92e8c7c554edb011e519e6eefb3ebc41ee851924dd224a340c45b1db249a43318fe40812516ebd37888258dcbc1343cd3f3165ee488ea262b879c747ef0bbd214a3077dc134332d4591cc98f67214a510445be31f7cedc47b1eb6010e53982e7873f11dcbb13492cc6448cb1c802a20c7b3f73c71dee611fd18e747f283dafef50c42229660f1c41d4f3a1e4dcc3b98362e8e9b07b7ae71e4a54e47a04750ff90e31ba41d30c330f65e765141c31b8499d418cee9888772879b77b276a1005370fcf110c538e21e89160377ae0486a0ff53e960e25ef401ea260c7bbc6798805a61cc5b07b7b2433d6f91bb39e791c2b87f21473b749fee9dcc36ff72f26e28c43497621eea8b7bd917ba7f658fe61b07ddd1bcace4345d41bb1d7a123e6dec9b2a1ccdf899e1e397661174fd03b6322567302bace64eec48c86dd23511d13f19831cd7c65e8c8aaa1dc601fc78eb9de5e48a2181c3336595f4614837cfcce133cc11e7e5f4833bbdbead98765b4d55362d1508adaebdf476a527b5b1c3b99bd23d60ca5f7e5ef233d3c7aa70ef53e632236797f4dae67246efb6b447ac092a1d4a74633b8f5877a9fce6168aa5a9692192c59319421f98da3179e1dd8f12e92e84c2b8d2c184a91f4c8feeddd33f748908f31118333ad8480d2d37c7f5fb841b08fa2e7893111d73a9517b25e28d1cd33099ef90337297a9ccfb4920ba53e33e879e0a9f7e891587faf9b26912d94e2798e27f98d67df593c45302662f0ede3c862a1d4df38a224e9817a6fefecce6e8592dc587f7b7bdb03c3efec5ea742397e282a927b24c70eeb1db2522877c67c0c43efd32288d14f8c89187cc2787c65a8c742a114f9398e20d9bd4f8fa8ee3a651da0d4dfd99d19a322a877de7c88eb266e8a7542f975540cc7b18bb8eb9993a087c3cc32a10cfb17eacc3bece19dbbe8b9efda65b4a976880497a8985258256c3ce60fe3f0cc689775076319c0db63d785587f64e76d708c89d85b62915092a1d879a4d79de0f971afeb48cc1aa124bb8fa33df78e35086e0ef235b55a69ecca2a40297e3b77dcf376163bb8c1992cf564b4d513a1cc7ad49bf432efda3bf388b2e88ab4d35537711c8baea9292e513151160150fda19db83bdabbc7bbf164792943f0fc506ff4c86f7394f7cec361ee526a706f8e921a3dc5be37de31d174d38c672497d2cbe7d835cf1a25c3f1eb674cc42137bbacc766ba9d0271c6e29632e467cede26c1ac41de7557dd9ca29d9d9b61ce62ab63fb7bd252763b8b5a3c3b0f87e73e35564ffdd2649d371442396e1e765c24b3c6f82345de6529fbfebe8e76233f5134877b8c899877d3cc5955fbfe6aa5386611eca1b849117723dee11215d3ca552962bd4f94dc9ddede279e23c771bdb52a49ee65143cf7898e6088512f26629e9a7d567bc57588e36aabf696a89865558976e226fb187e19e53a83bd07aa49cf15479a5ab513c7719c3b63b1b844c5e4b47263ec7de09747f0eb449dbf9888fb9bb656e02b433978514f871d1ecf10edbcdec99e2575a1701cc7c9686b08ae18ee1115c3ceebe4276611cd1e8d457bba4c5367b21494d156ef95a1b4b60c51dd8dba87e749e6bcc9218ee3da7736864b544c8da504c57de6f3eb34b8f9d69fde9d9bec6b8ab88359b949dcc3f343cffe8124e63b2662f39bc296dec73dd2434ff123d1aeeb5fb896e887e630c41be459cc200e4159a977fe50d4eb421e9e3d0c794cc4ef8c35490c26a965d831de58ec67f7895aefadafba1155398e9b04f348ea8e8f5cf4784cc420d09552dc9cdc5e973fb477e19747322d4154f4341739d891f93cc72e26e2d7053152ac24456f04b9ee78888e1d45bb98884d9526eb70f75d656f2a251a43196df5924c95bcf3ae7f58f76edc1e39ee9888dd114fad2915f942cb9014bbb01bcf8ec7eedc201a13f1da5573ede69741b0971dc724d88558dca8063949c644dc612b7666aa3419b9b253bae5e128ca682b112f33f75010a3a117a23c83986cb2f5c93dde773f73efbaf3d43130b7ef0cea0cc571cc62388a1d1992b813bd90ec7ae49ec8bfad4f8ff3a380a8d47209c645e91432460000c468020424c31100304024188a8562e17050301cd90f148002548c5ebe5440178ad32846619031c818630c3000000000220333b80aa92aacca547144c509b08644b2ba816a2a29136f4bee651fbde01ce9f36bc3f346d25a1805597becc63af2b65259667229655124b5fa84e0895df5c73c00a7e547841ef5a4b5425fd7d4d43f69da8bb9cdb0e4d71a35bec0d1b892f8f40b23ca473804a02cb4851433904b9e622650194f0277f1d0ffb2ac078aa9fb24c6027877484e116cb16533e8cb558843e04bc2caf509f9d77ac89e787b204d8939d823707fc3d8b39475ce4386e4c8f9f1bd1919647522cb8061de5f98daea6aa6507d2049a55a0e9c236484ea6f17b51e7d5a3d5755fa0f2803c563166680495439919a04cd13b966edc09d4f9ca7579ecf4ec297234c3efd2833e6b2834bc3f759f35a3ac457484901eeab1cfcb83b50d27cbad55c0feb0f14771039050bef3894c30224bd033f1ee39b3e9d267cfc0ece65e3bbaacc86555252765d90792ef3006e15772dd032bf0503a710f26033fcd33eaf506d09d969a29eb214343514b3915e2b1be955ba99dd4ea4ac5014e25abe507419c0a0a6d0290310f3323dff539576cabd81f7da014f270f721d226996d18eaa8a7c079068d675f58bca5594501980fd270c4f6d81a0591b1ac0d4a51b13be5b3758d65e8c5b27c1417b8bcdf0fd804cc7e510c8a331460c435cf38396f95997df690f008569b69fd7b5512784c95f9a77c6649bd12d75870a5494b218f2cfeb4651f455d11c712030293789bbedeb6c2eb4eaf6db4a3d4d7446ad859ab532aeaf58e05281ce5af1d41e0873953515b5def75237bdebd45207ee8af041fa8bf625ebaf047df8abb4e15ef6d607f537a9bb4bf91bd88ba0bac37a33e59d21ec241c3105064132c8cef5e0695c28b6ac365e1e4695410c11d9e013e6af73a54962e19cf04ca384cf905b48e3c2ab40da0011980b7d40c08f2d22c1bb80e576dea2b5723c2b53936d4b864d480d4150e0dc975e03a807bc87ddf9fd1c8564c3422f20e3ba086907a04d23daa46ade08c9e3bca38de0f4d4224eb3822f7bcf82175eccefbc1a60a0096f665af42019ed390040a0a2898453402049fbc979b2930644da4372b493bd4ae7f6110138f5fb072b546891c340566e59e4b8d7864a7b208fc4642a7a084204fd625484881994ecc1805e08aab2667d8f19418366e1f9ff84879dbabce9bb22b9fa5cd812dbbb2450bbc10b94a041a0a9e4a36bf3e328a7a5f1347dce250701383a9143116e1494484a685ec071d19bf8e58f8c5a78c299352e2594a5bd9fecf3225d8f146289a1ed9f08c4e864661db27ef3cf1e63df66d67f9af0aa59532e80f1b7a313f65993184eed2d80c2ac245867bb005792842e5b89162e92489d617a60650340fe03ff63dbc05d353320630c79dc3f0c6e5c1c65926d5d423be33e2c4af5245add0fa041bde670a59fff53315d37559b45ea97da0a7b355e46719905809d00513a8dc379647a6270394323775f446741bc049b92a13259852fe754a866546b39325bb84b5b50fa31be9767a72a061f92399fc06b9c02d1e2e38c0987af8512cfab742100249f615293ba9203cb62e1ae1419aef5e4de38620a12203071cf0345f590a7ae906a2aa05aeef50a4be17d41084ad2b6e08758aa1dce0f3d21638ddfce08a137cda001f76584b0d843b04de9983a31a4fea7b8e2cc3f709118913196447df72f9f5cc65a0c7f15c42224a7e2b1d6a85a87208f34ac6c2b5927327d45e74b6f8fc820bf8cb7fcd50ae28ad45fb27d92b88b282ab6a90c4f4f1e0252c1a1a2e9aff35aeb712ed643c5abd6c865d18578734bef679ffae60e5ce4fc3163178d44495893fdd2a0ef674b55f0d8087380d6672e523930c53e5e9aa802abe931feade71f3f8710e04262751a44be16bfb7342b5a911eb56a3e10db1c6a04094d3f205fc3866111f4f648229849eef6f3bebb08c92e761930c014e9dd7d232a9b2c7227b1ec2af301eea8ab7ffe795b7e885f8c6fdb017402b687b702ac19b82e8784720805d79fd236328cb3084859158e39ea6e50e5ce65ea94da27e6db68dc681fdc16b27f07ad25745196357a2a88f9984b41a63a3d25a8189ef355cfe01b7beceb105a03c3b1e45370878207a6cdcf5b9867171f3951a9f3e1267cea86049ac0caaf8a0636e7e037db334be795644e70602f9e4c1de1c3d2f41b910cc9172c9088271a9ea392644148520dd1b4989da2a84572fb979f253f73aa5a72cca79ba94397406d79d0021d4367ded3e1813f755bdf069ed373ae16b9a41dea5047e223c34473727305a90c0860747181ce0db078cbd24b2af1e1fcc2cddd95f56f89ba313d44c3e359f64228e57d8dfc01619cf902c25684b977a06ad56b91e203c9b83b87dad02ec0b9bbfed486669b5c92895c9a3df42e6286aa9373bb617aaa36803d60bd6c28d185826cf4d2c800f74b400810f1bcf5cdf950c6e8103a94537c58c401ef01c969466009a49e298f9962debf44bf01061f4bb6c29aa49d6db54d1b4c5434c2d46bb2e47251c304526a9c13b18253fd16874d6e80dc88c2c5671c1310b15c8390fdb456f5711bc7c8cefaa337f8298acab2d08a7feb439b8973d3c9450f098c355049d40bf8e29e23d511a3b6629c9c489a8eed845a05789c0a94dce8e3a91bffca53f7da4f34db2b1439c09798986343e0ee80de22099482fc8620df3f79d9f7a469f910624e0d37450f5e440f267543f6bfc16ce2d8e31376f20d25f68df5e45c6fa6cf3807d42bdbd3326813d3ac56218be02959f017bf50e9b9f1584485ffceaade51640d1e0cd568f3c56c887abc3053d50b9797da6ecaf7b85ce1d5edb1f5ef928c466694aefad46a5481c6cca943693200ff45a8998b29352285cc80fd293b5aaca1432994bcc4c287bc81e10a2b6f971c0e8c3c288d67e7a4e723ae5b07f862101c1321acf840e1e1cc98b62a4c37a033aa73df986ae14c71b29b2a1e2fa1829e7f6cfb311d2fa01c8190412700d8fd6dd8b6c4e1e52d8c4cade52f176e675110b2a1ff4c78a358dda0d3c12ab6e607611b13205ecccf629be7b139fcefcea0db1040c928c8df9f34b19472f160bd6ea72f4e28625d50fb2892c4be176da8a03126a872959b887cacefb396c5681594dd3be86c6a6415054a55528acf4436430e45a5c3db24794ad255291e973c55eff6ac0d4af0e5a413cda1b0a6cfbf13ff44f2064d406484012f7c40df76673939d0ba571a8b04e154b5098529548dab13f0913ce26d8cc67090d42f6105a658d287bf4c43d496b1c7f843c2fa41dac44485f13d05d0aedb154f7585155b64ed74322f451ccb09481ff09ea2cd6a82b1f2ea1028472061e94627294999e37ff6014a9e10df05aba80fa23aac5c1c949ab56ab0417ba2609ea998b6f7719b8b664bd7fec1c430132ce7007d8f7fe761833e8c3ab5648f803e42eb41745d8dfeba4ac13464dad10749d476b0a8cb59e3d50ee51299232577f28e2780224f59d409c43401210c83c7365af00d5dd8806d28cae8c206c472b125badd1758b089ff6435164326a6f00685df7ae1ebc530bf989fbc11e4c5a5266120f711b0dbd056b0b37289933f65aa4b5c4b3e26aa63483f6619aede09f321ef10dd1e42844a3071dda33545c525e7abe4adb4f6842c9a86fb282d62e333bc2d02dbb3d07b9c6e7d05ccdd50e9360ebb900d0535410cc6ddbc5ae82443a4bddd7ad0ce0d26e956ac2220f77f3c75d6c7e50d011265c41b03f84b2e7a1f50e506349f3ba6b560cec2e742aa98f94170809376d6493b8c894312d0020730903b592c0d3b183f85059a0424a3264ae9bfc4b68e3d291e1f4ae0e49d96f1dbb34928409fbb9f9f1f22842b090b5423d2dd1b41d1828e7fe585e1d35b869496c823e1aa88bae5f78cdc66bc6c0fd9374fc007abeb575d45e988278a79a15a10f8eb6cafaaf07ce9bb9c5e98fd36dd405b405f77aca741f84fec54a73616b796d5d439c039fe3ad603e433378b6b81e4e82d61e822364a83089d49ee46554e30bcb9c61e0d647c0c0d98c2e9f7554c6fd133e57f7f85bbd32eeb7c786d7768278eed6907e4e69cddd826ee5148d69feb49a5a58e8c01f4028fdb4f80bffc8b099f4c5fc0b78024acc07d1f3d5ba3cde2de67e618a0f0c18b71cd1ec47bbb824628f7dd7a30319b1653187c417550ad526324bb1b3e75f23aef3cb01a5fff775a5ce470d6da758d23b994893575dac18d9f08cacf80e62708085c73699c167bbe4d879e38e4a59748f3769fd2a50bbaecee23d1226c8f6d4abb3582a1112e257a85ae3c68d5599f93dbd3d9cf7a1a4849bb16c43ef1f20c3e6c2cea60c33e3a67a0e3876b90ff4e10f78bf99a8f083e5a4decfa0089ddfd64db7976055e65f08d3341e22757a82e1ffe14d0a494ca93799128cda3b1fc3c5e113b7ab0fd1d64fade81de10637ed844d39dcb7bf95ed78c8c12f94044bdb6617f7f95d8f246c016e8c81ef9ddabdbe15fd3eb3e97406c0af80c326f911f5eaaecc0f8841dded30b857793a925cdc69ea64fa91e17fd096460e8ce3aa52cadc4aaf73b4c46abd250b6187f6f4f8623d9cf95b9e4689ae6a66bcef5423b13c8a216f64c8257b5f7f1d3a85193d0ced745e5e1fa9c355bc3fbafcc53ee57dc887db11cf32f3ef94515244673ab830b07616f41838f64086111e9f7402f1e836c360c872359e5a391ccffa21af1ff061f278dc17967b74e1982947c5c42605d242898f6884f45244e01f91b6144b7a146a033b0b3d6725796fa5f46a20e6fba196229f0acc4011d3a35ebf7af16b91a49677ff85aced510d50f93a55974f336dc0ddca42803bda6288a1e0b10e71f6e4e47adab7232d8c0f6b3823b122b3b68d0fbad58fa1f1d2b380b3474263f4d9ab5ff77f26ec54ab6e5b6db028ef23e987c4913d3f0fef92a8586fd066e10f682bebd83668e4534ca4064b0c3d5eb0e20b0cf412b61691a93e2a3b520de32dff3230225601ac1a4634336b6cd9be60e9956b845d262a229d2fa989bbb2f48258ce4a18f18b08070d2f9dcc802b4f433c6b8a36f7701e453b2ed01ad2ea7a4f8fbc97075d0abb77f7a53ab97b5147b770e606541c3bd7f3b02aac1e832133084231835cd9fbf00576a80ce0fd694264aed8df8e7302dbcf99fc36081c6b561e2677dcbd6bd7ab0edafb053244b2d3ce1184fde137ebc24f60a1d48c02cc56b53ca22b62b86681631b9922f50ff25a3b9a41627a65da80061f489ffcbba6f48906871865a8c4d2d52e849901565455e5b0d5151544a0982dbcd9070e84e2f56df050e9bb67984211abdbbf6b7eb74944154b0c8df3e90c8f882da014ce91cfb21ca8c29f8f5972714765c84688682f6ab970ca57b1145d61f9497f6c38db2916c0c25c5e2f2e3eaf567ac52ac5dccae5278f8ae59a51000fb7141f749906f22536a8e6a1fca8f2d9d3f101c37185db429d11e5d2969aabc889f1502241efc92e3e24bc4a847ac369adae2dedc6488c6540cb4f8b9eefda596670be6ecfb6e17b832bca750a399fb031c3d18898a8eeafcd3325325d668873fcb3355a09fc57ca19253f26ad1adabd5d529affba8ad04184c6056abdd9504e9000ff93233cfe01134545160d74a8e23a6dcb2f0c12674e3ce86d7cbfdfbd5e382513dceb56e9dd3d00c620ad676efc5bf43dcf104e7b65e779fed8b25a50081d2a3e1cbdb80a15a7f458c8001269e745154436f567a8ef10d17e64f5dfd23859d7651bf5c17a09022463ba4e007a0a5d6bbdaa1a17790834464d20870aed02c1a68eb2d1ae72cd04d235e64840119f9ec009c9b060cf814df9f7a767af0d7a99fb23e9181781639a640e42c8330760d24e5cb4f9cc9fb7001e2d3e43ea8ba7fa38b1477d5c9ac284ccd370c251127ea4a1355be6844f71c22a6fc1f0b6e96ec71f7e7260ea7ec4913f6c6edb30f62e319026c1ee25013dcab07973382bfa08d7616003c5415c4ee25664c5246c7372db0902a5615aaf7083f74d37b497496db0824f8eb2ec3459fa7fb33c36948a5105cca89328df572774cf024a0767c82feadfb46fd84023eba1b3945d338c1a1c59aeab3a8a8304910bbe00bf6a56861a9bfe0529edbeecea6d0dc0e67e6131738658335d348316ce06eaaacbafba05cce09eb425f14b2b98cec1edd06bb16950e0b88826556fe30a7c97b3aea58a83e84d3df19e31a44cb5d3f87b1e0b40adc6ab4e199c9bc991ce84608971bf25596e2db2c479eb324916b05e7bea98a337b92ef4138171039b6a367edd9e112b8ace31fbb07f4ae0d45d10a93e42b51ee6e77825b802d7dd198e0b895b00d7d094bcd370ad18ba3f360b4506c704299abd12040626050c9cbca3b74ee1145eedb875330c5fb061fe1945f58edc994ff31f1265ac0d59fa5e48a1f6fc67228ef19fa0b5179d79ae5c5d4fc2073fc92916ea7dd0ca596fe31332c5ceac7d4bbb8b05bbf5689543e3d9be452bb6df6104af5bdd59e4cf91f136fa2055cfd594aaef8f1662c87f29ea1bf109577d63664e57d9a7b881176edc32a99e2bf87897cca378c7e42aabfd8d8932de36be651ba53e8d28796107253e03642b5599ded16b6c0aecdd55ab36a0ea0064aa60366f5d531ce690edb5688ca33db8dab43e6aef5f5ba837ba96d478a80f9db532fa5217e6fa0254de13fb4d7a4fa50ee78575b05b09f192c5f0f26ff4c6ea02db0179dd76804ded336aaaa87e57d0b757406f80cdfcef404fdc19d62aa3c8940221be51844e693f30e99266b18f2963c0f04bf59f62054dca571471035097df1535a7e129c1bdd04e26047553431732b8720c74b35eb71db704eb25b3534e739d884328dae90b28d2510f9e5494305ce2419c4033641f0956c4696a285144f8e20222620b91119e4248cb2ecc4cf5ad95d9d924111f35f1061588d8923fb15e1b0d965a89c0d911e698f213a487d23ffd919e5ec96888ce64e7363cc154711e140f1ad1c0bcb68e75626b7a1eee33cd19aa8ac3439bf88a6b5c3c858dceac3e5dbab819c94aa767616ab926dbc5cfc2a95ebf92bf21fb7c9ac63d7737ed3b5a61f1967aad9944f224a66f8efa179571e8764ee59b402b30258159b2a3f6d716d348a170c812ec4c2dc26bac46e1dfb92ffd88964404fda6264cb0c4031c26a6d2eda5613a1fa5e1571a19253070fac1b8d58d1b436ea03098cec05c59519d6b49fb9405404a8178a33aa1fb00e0105d7a6c4b75eae2f8270b3d17c578f9c76200c4164d5bf3f1443acf6385bfd549f70059ba573675c1a375dc6416cb9fa16ec4fc3c31f9d98403a0334ce75291a8d3991e24b9c5dffd5cfeade9bb65260b5e6436f643de28b613db13f23d0981438db69f4f2c8b199d256bc979d8e5bf94195632b3070b963a99c6ba66be5a2fcc9ff60065e41e846a1f4e8000b2e74c35365970fd20e21dee61df0ec4a9879268794f2171027457220f4863fbf1140b5e7a7eba760195c4089cc918fe04a04d5f536f0e5cc5dc2ff7aa78228278d6005a6ceae75fbb0d98633c261390ed4affcbc3e79d5508d488cf4638229d74b216e29d546fdabc0d22e9dc88bb029b4d77bc3512ddbbe85c8acae4270cd986b05349efc559c8f30b1902560c8ef66b8c44e600e61277ee7af4a06aec250d08450fe92404574f6b1fe80cd9aa3a9a624595f2ff39d53634c024ff22dd627990c137daaf8e8e4c9cf956cb2b82db4f4a2a54e1a97cba5021ad93e10dbcbe8bd07ba09fc4b65df52247351990830c8a944c630cb621e65da8bc40f49e9596f84b15446bca6946a7a2e5aeca82e2d6d8a27fdf849139aa81c45c07bdf5aa3ec9716e7e8c809696174d8e03c11b443333d3a4a18448684d377175f30e9cefe7e3eb5ae2cb03c2d8bd048cd13d5bf9080d203f755711ff0bc60484ec110c6c644d7896347e6b15fb8b6a7418b207001f5f827c467da1aa98cb948130a728d91b5419a8a58f95b0a6efd90f48fce27cd25ce9535b4530979a8af2736e9250340ee8bf7b60cd4b934a9049c382b21563c98bdd10000d17d4448fa33a9e745a39a29164b1269290d5ccb40389123d18a859e2c78f02fb50cca80a03a2f0817f2eb0d69ea0af544c11960d0c7201e6e70e14f636966c4827d6013d62f176f69444677e131f0022d4e2e485b3cae76c644209ad1410de5b56bee1b31eef47c858a9c26cf0fff4702ff72fa17ac867a97073594f7fe3ef6a3f01a52f470db25c46623c786558c052fb91dfabea812dd510b71ed84c7cd1b5dbb302581b3f15415c02831815935957ce588171d6cada2df4a959eb15f128dac373b08c710249931cc370b3288876248af7a75dba47e360dcd49727c4e70663eb7f28a4b8ec38819ede943231a630aa4f0808472fac3d5bbc3fabde58ff9b6cf51537b76c81ba5b9a1ab69c2e725bbe29b839b18cc3effac0c2b8b786751a5bdf0d3a00ff969a7f8857c72cd81e311647c9e699f0b2d05a9e92ee54ea403cd52b35d4ba6dd07528f03dd4b657a7891aa0b4a8f2e21853d19075fd56454abc25ef060a9150f37e755f2369e1b06873c97a876aecbb21dd6efa0804fdbd7a837670c2e36b66f95c6c4c3e1b2778814ae322d88be4a749f5c7e5719dbc53f458980a2b01400a6fc7983b6ded3f02f583fed730d785f4c95850b805f48c6eaa3077d4175b2eb2cd98b1127d2146d6addfe780111f599ae79b4edff72e1adc2fe850e4d0b1d3f78f5701750c0a6eae963682cbf91bde4938760ee82f194704ac23e2dbd5ff3e7c7cacae737ba1a25f79510b147e1a5fe212012a7a78a5ae2bcfc38cc007f5a869218ff7194487788b2cffa1cc7dcd7a7e877ebcfddedbfcb98aaeff37d350e8c0e936bedf6b74f4d9b107c2881e446954fbaeb0ee2f07b1958f68a32340cdabd13b9e63b211944583ec33e482ea873e02d9a33b78dddb9c7bd51cad4e66e21f6d6b1dfca0a4a62cba4edc8e4e04e2e44146a6b322f101c9066d6884b36caec46ac2deefaa10ca05d81e7c6dc222928de4fca603f4af2caeb89f693805f4109740fe321e1ba33dde0bf96b99a45d307f85b9e8ba9e830460b4f14e606d0dd06140bf2bede83f1033a966976fbc6d267af516fad7cada39f758206f3e1d937de7d1bed0352cab5ea164373f60fee0d862f409fabe5f36cb07533e6b948b5d0631399411fdfd49bf5feca38b176928b0299442ad9b7e18b23c9efd8a625f905e06579003e52487cfbf29ce191508e9410f2fc37d5eafa62653eed56b8965afc58cb66e28c2a97e5d80e1a712c46dbdfde3a23fb5488c6b6ab3add999d4059cc8bdab3bf6b4b269a142595356322395ded8c6f51f0ede52cf13ec8a77a8205c365e2da65a22d636f4cdfa88c35dfe214b7ae7d395a5a41968d26bfe480c78f98f3b2d7cca2cb32f439e2babbc81a0536504251ff9f31cf5801bc07bd22d1c428372e7e7e0c14878d99e42da84cfe0eed1d7e9fdbee6be9660cbcad22edb930f00c504fe0c082429395c9d20c638ff28ad4e52e599754d60896aa402f24d0340824f89a1d2301e23208650af1a6c278616908a7df954139a5a8f5ed97fe4817194b3a663b46f78c2e4b510b3456070b905a85ff25cdf35627d52f776efb2f04cf32c3011bed2812c400003876dfb230b738fb23d3099b1d6074b805777b91f75e7e5a0dd40612c22bb9c1e807e4b84e9cbb11e3c67f3869b9d2afe56f8b4dfb47d1cdc6eaf8894b9f527434c7366d6f6d157476dc08b129174b75722c48c2154f0a25c743f0212ef9f8e78ad1d4adc9251c0d2a53f02eeea6815e3bf706c79bc390f022f6ec4e746f7a2faaf65f9445be726fa701e2d2efe95d9518596cfdc91394a36805b17e787d469a168ff5d98c351c349bb8139ea181f8c712226c26c16ed33912835a334daae18e8ffdba3313a11a214172891f331367f78fc5c3b1bc4296cda06ecabf02498964144c1d5ba955386bff137e1a6b7ecd5dd8bcae0abe408639f05c26f89d8bf583e45732b2ab71cd8d74577a3f177587869d63da9cc28d5f39770d77269e173385966926ae888324901627df087cd7f38261aed3d2778bb00e9cd74f82f089327b11977b738a31853205335de44b007401bd9b98fdbe8ca49bc57bd77b3bb3ebb500519f9a108d3f1f7f15d36278cacf731aab6b8bb4a8dd345c7bfdc1ef188b43177fe70e5afb5a5a683d440f89206f4e723826325661efd2c24a3513a69d12fbe617a52874142a8e6076383eaf89dbc818a95193ef2d7cbd995e68fae7e89ff106b07701c90f82ec6c2e18e69d40c06d1a66b0f143e2fa3dd4a981c6bfc39912c93a1fcd1a3a1bdce83bd8404ecb97305b74d163a28eeb70d55ba511840a88915f86b68844764c4fbe939b291372fdeca1529870ecf86a61a920a43ee747411388daed031aec217af5e46ea00f4639d7c1882ed04ebf2c2df157166bd0f7cc94f46512885c9815dd9b7d5ce581d866b14a0e9893520cd0d21d4439875a118366acd863a16006f8b42264f6727b6ade8042e5cd34440489a13eaf248c685278c15dc1def7c107b161ad215ab67d74604a02ee52b60919a3a83f60db0e14ee6b18b2c167ddeb75541254e37666ebf97999647353ad4ef704090e6df0d5ba535d48309498a9d3da15f87159fce7cf6cf80b169482e0bb82e13ee37b5ef6967d32a65d007f0599bc41a5dc6bc5a05fbfe75e902f30caeab8447920f7dc39ef56aa6cc1ff2bad077a6f00b78d3e93e63ad2e42e5c7021a07eebc412f3526a7c90a21797f22c9ce31bf78ac58ed5f4c156b663c95a2ddd7bcb2c220b8d89e166b8723c5d2b6d3a3ca90a0c3521e8431e16b9673e56c87ee1c91cf4d20a422aac232dfacb547e1db904594225ec9bf8e52c3deb9cb9d2162e0551796e4b58a3c3d430be15414cec044835ad296fc19257dceeb3409fa06585b6f94b74f19b10234740e5f2c791d9d00d2d6c4ee208991a83cc2d87107db14e4d687531b7216f9558e55b0d2742c855324655ace039528137b6c3bdd787e365bf61caedf1e6001625fa4988032fdf96c27ba60f31464939beb0281e0d1a8254cb72cb19693bd00ba3a721148b26799208f218dc5a30fcee78a37912b3e054fc783dbccded56a3f7ad4f2990e20b36f80ea080969ebc20736c1e582de7749e1e58bb93cb1629589a5ca0c8b7d5198397f53ef671a19a903075e6ef58e6a5699964b30201d392c3f3f9c72ef2fbdba85a7ef9da61a7a43adc8ff02323e19d77a80009c17ce622aa2558f4c48ea20d8f928990a687cc7435b86b55f78ac7e4b93f84105c19668572340d43004914c5a742509641c17f65a5d0be40dbf082641a8e5eb36806a69a99bae544f9f9a432c701c048526837d244d7f86cd5a80c5accc2c0c215fc3e04c2b89897d1ce46c2b5a3b93326b9b16988f6ff8782e34c801baf2ab517c90bce5790d27c8873d917caef67154f1768efcf00b8a2a4acc223b11ec155abbe8594d63788607ba803f804b74d3fcb0a3767e30646c4c71cb4792f921f8e79ccaa62d707346e7e62f29520c0e8c409e4724a37f34e316fe119fa310ea2da8cf2bb9487e659e0e7db90f8fde7ede96bcd02b2f0b8b6f5edffd9556d498ab54f2bc99686ccd2800f041fcdefbb591d99ff568aa61d9447ccc3655755f32f71baab84d8a74bcb1fa90cae45ae7cef3ec1813a98df9e4ee1832de2ad1cc7a07be379b0939b2e364745b5ed7006b8ff481810fb98f1204bcb74239330892633bde21ad3c7db838936fd1411a09ada95a3cbcf3a50582897631c5ed206ec8229e54b1b4043063d9dee944001fb808a4ff3c3ba3d08c6f9282e228c884e20c344753c36207837cdeebb6a943364e1dab4d7f8fcbd5becdeaf72b284a03b0f7eb25edd403ebcd00b5abdb67ab08c8fd501578c7f36273f7ca2a56dddfa8e28527bafe6d370250bdba540d69bfbb53168c8354667391875f3e531e912fd826c44a2dc710897809307e532de26326e99b519fb9018492f6430d5c81753b73b4bbbdca2718b2aa943ed63c969cbbae5085ea84bf91713cab7ae40c39fa4cddd19ec87c66be47c20686c2ba773f9de185fa8514d48e8a68347230af7fcbe7702b7fb0b6eaab0264b261c932b7ca476741f47f0072bda370ebef67046fab697cb763070e993109903c68766305fb69963eebc5bf87c0f057a24534c174c099f2c09e8012cdd0ab001528f5bc64780d4c91884b431e669065aeb504044e01be46e7e64cb7aee5ab8fdb3309b6303488b850cb68065a57b37f0e9614516208d848443c575ccceecf6fdfe8a9450b4919591506e9558bb2529d4c4126c00793f0ae89c210cb8c800dfecd85ebb21304863a0af570b9fd2566c219d428655f839ecee9e2d071f5a08d78d1ec971821b709f4334cf71ce17baffea460a7fd9508c0391fb6097f97d598f8d19bcb02b4d1501e8730ae959df6e7e40f8a4261f506239f13099f30405434f2027ac9dce736a730b3c9c33ce1d986672fa8233e2215f04fe4a0fbde95f4e95e26960ea80e94035da399c5f739b5343af84a15233f3fc5430e963397969724c0377f3cc8ff3fba07c274eab6a33beff72942b3f0e64854991bf3fb806e0d5778c47e9339e40a7b553992de495233f6e96ef6979d8fcbafa5f71d4aae993ea6218529b005619dbe0b5945369654d8f6987468e4d73bd47dbb723da768dd05bbc2e679d0f98be4e7fad692643d988001aa3e1df5622ec4b0867ca432a47a5cdc6fcd04453286c333236b86c6acc18c7749195049351c85a314dcb81903474bf57ef6b495bd83221fc200be0c49e6696fb2802ce5528b65f9ea4983fad05989f36230e35acfbd39075aadac19775cc5de59bbaeb6a07d5746e1085f687c8316a56a79bd497da4fbeb7a27916b84d6c01f74fb1bbb2a04f93a18b26b2b37ebd5533b8fe5320566ebdfe42f2c1b54147995b0c2e5919d0e107a6e5bc179d5312fc34bb61f5e26d079da4f8bf21175970e05e1541271e06c584bbd70aab1a6a7787247f1d1a9f51cb280bebc8d218fc7532b9dc591611c3c1ec8bff378f731482ccbb8fbabb2522bb0c4c1cdb5a777415186c0a45ae413e6bc73057fa057693083886b29a43c9f593ce707bf238124c6b47d5c08cf981f20ec655301ed39def9770911631fef18f01940388e10513f8b17ccd58c12fd5339116883f5ad0f9f827451ca8b8b7e8dfa32922aab129e9e98b02cef37437cbb8f4bff7207fec50aa1fae1e854ca50763ae9cda32dcafb9302d406894cc3b6923b2d18ef85b4f636ab489c41b32b0998e743708e9b38822c9e8d993b65c4cf2372b7d63e9db48b99758197beeb53f98f1dd592ee6a4c6482226f231948e39d9d851a97339eacd4ba79fcebf492af5c20b78cd9aadeeb696769d684005885904cf50a985c85e8fb4b647423791054a8bc0163460537a9bf609adf7537752a8ee0defa00b79fbc224b073c9d78ddb63998f276fb4430fcdf01634d070000c4456a571e775d1fbeb816d4b2db857edc34cfd787c198f9f2d12c62d00ea555bc4ec8f3e1ee6802737a6afbac9b7e12cdb110d83c0780d2c4309bc71974c90eee5dc86dbc029540d4e525a7ba033d4d8f17aec80e4e88412ce6d3c0ec07c934d9861957f49514b5e8a78016555fdb98f07affa497f3f31b609d455d35de08e2915892194d4e25e21a20708eb13b63fe9c1dfaaa41152ba941761f2adabf0def7bd3ddfb19d62d1ab3ce690b179efce710f97d96dc750698ff2324efb1792f7e92ceac16266c9e93ef9fd73753c898301e49914a080fb63e0a16c626ba713de0e890a9e1aef1b7c22262676eca573db5bff217c6c11749f64a1e35211dce70514204deaf85901189e881ba2d71841c6ff8ba848b270f197155719bcf60536e77ce488d7bc45945b62a2bb5b485c68cdbff81807e48b8b1adea3bdc2f96188f76173d2bc08539585bff0c5486927524add28bb2a1bdaab2aef3b5982ee2bd5840ec9fda6d376598e169c0d4f32da38591b263997c078b369ec9eed3cbe26710f1e261fd3f8fc7865c4d1dadb9c7f2c12e675753658a944aeec2477e3c5334619b6f53077ea19d7010189d29aa3e28175151248abda09b8d204fdc677b52ec6ccda15bb8e404a8c426cb5988e7ffee424f06ccad2df9d4004e492ad4b147743d96dfeeb1ad2b8ddff66b1427db6d1ff69089f65f5ac09c701cddfa1cbdf3497202b62e14b440575a402587e4c4bad247b2eb249e9e82dc95410f207cf566d212b4c0b7a70cffdb48971653e64b42ec70b0eeab84f797115ed60196a55e8a5baa663853cb0827594b0a86b9500d5b25370e861511adf694de1000911aac3af253bb95fc89a31f0b66be2c68e1a3da1a551cf93d3390d36312a0d316e2ef9ac37a750cd510b55faf3c679f1478047623c1506507c4b20dbc1dea1d0c67179858acbf89df1d101257f763c0640b1b4fb5c62e81d6b322f9deedc14ae328518ed89cf9c11d69d909a701550d242af51119cfe2ba0bcc2123a7974cb06eebcbf67835e3689418abd67f28f1a02feeac2bada8ca6d7d741a2e20e0c169129e3f55b2ad68d5bbe33af9ba32d9f70afced44ff4f179a3bb6833244a0074d130e64a7a729dc246269bff6d4ae5a980de8efed3c24ed84a47bf6f604016ebea9e6133943c4741d50068b2e4e41f9293e94ba223c9e7b4bd76980d122600b5c469ee5771a6bc699449829f4a3cf0f12b6a35d7238190a85da603eeee0575d7e568410a8119292009e0e7620cd5fee8eb4f8c15d499817f646cc4a38f2870b2d97c004b0961a9408363303289e160eb155263a2e20aa6b757a29d6a341465b878d0df9b2d0f3d92a6de3a19aa2622a9a458b0201b2e1a8383aa02fe609bbd94f871f470cedbfb52aa6ffa7ba9ea6826498f9c1fab174c4b3d579c8f5ffd4bb0e5ead72b70e05d8babbfda3cb8928ec6825631f575da20013963a1089cbf535e73e7672157fd61a9ba26fa18330f7b1b4dd6d78a63d302e5caca40d5f260cafce16125c21c7a006fa991a9d694559450ff14c05f6386fb374a3c5008aeb7be585d54e6387cb8b42fc59b5dfc94e30f163f6aa630c5cf5a0eb90fc8655ac1d93f4336e7189288fde928433cf972dc2059ffcaf0658efdcc4495fe9240a691f85d61c564ac509638429a6cdcfe8930b1d30932bb84457391c7e0c5c076d5a725274295d0694c7bd35c43b400694e296950a9e38262e7f8dbc163c52c19a1be4d303454a59f2499c4e9528abf3922d1894bf0bd5c6278aae15c20a63927290539ac1385dc22283be4b636bfc199abf25707009a8e5838438dbdf59ec2a088700c07f1c242d9e51cc4c0a84944dc9814e2de885ef6144581599d557f4de9831338d74cc387bacf28365d1b0bd9728e407523664535deb70d0d2e43a71d948d10f0c79f55d9687bef5be387751de2536c4e44202000caeb9bf7aab2eca6d14a9fc4c2133ab6300166cce8dac5ab493fc1faedca99bcc74a4c5285add0455d41b31f49007ff102df50d12c8119faab49252052cf1c9f25e221ac008c6b4496e6a772a3d4cec9d09aaeab9035cc914bbc4c01c7011b9e80315a58146d391b44ff28e1d8f606b43be41a430d1d8e3df5ee05d6e24b9f44d461b4a4b9e8bd181541be8f73ed4c1255d688f835f230d6933efbdded80cee0e86855f47bbec173eb0118e2c86d122685b862ff7e5b228fd7e0f9072635cca88d4b891cde29b7f5b4e8512765c0cd0520e80dfe8ee402e2a92f0d8b3def34819af74cbfdd6e06d660fbae4a96486743206005af60888e2aeac5fcab5765ee7b24c388fb9aa0761f8ad88f64f860213be2283ea54365382888e452f01ef5de8aeb83ac7245b49639fccab1133b133f6221dfb1ae4ed680f1fac930c8cfab4f396dc54eb5b439e35cfb04be015032c510abe37bc4f60226723b02dd8afe7bab56ead5331259227c6656f5cd5ccc502c266986bd1c8cca1dc345dce4b8f478c14121565375417d170dc9b6cd764f39af0d51bf006441f340b480e4dbd1184be574aa187cc4d8470893da74906a1531c988f6bea9d1d93480f2f66de4fdc6f286034e46ff136c7523b1f1d2547a8f6f4b16825941ccc4727fe614bf6c9f99adeacbd403b5b8ff7693ebe823c1c108b887a18609a8c53384186580b20858d5c18048d8d9a93ccfcff160e544be67129d3881c120cdb23b4be922e84e21017a66831096d54fd1f6859f40c5acfe0a4643aebfe4a0f839457c0c17125a04a55e604140a991974bf0c8c795250e3d6cd8f292fbdc43dd2256c50e64a9e22e7f39959b0e579e9d0cfefe7713857df4bac0b9fd1274fa2d85d2fe5ed41ce5288d1da9881535b1ab3a640cce9fb91b2aebaa96026e5cda951d8846660645e01cc909710c27271700617c58599a43da6d1f4e3c5aa9a53c138fee66a3e4333a0b6ac193ca91b2cbb3037f367d23882a39f6eea1dffe7965e23daf02234c65fbbcc0b832b877c0569eca487eb49371683e528a9520c582f73bc86aca57ef56d9e041a079eaf68670a16dd931f91d00be81cbfdd5853ca9b8df9a1d195848bd909e32ff515bb63447151d1e40eb85626ec496b3a797fd8e66a353515e30b3fa6a3e0c953a6349b425c1a513b93178dbd62419a983544f2821edc6396c69a238e43772fcad253f59227b6f781053c8721047482735fd7a43b266c0bb8a4d8c009c2fcbca4aa202e0a847fdb294ec15be4ef301be9037483c82563b883a5c59cf21abf1025281f7b35f20094af6dd10114ac141efc164b4e36ffa0dda1dc6b14e51c0e483ec75ee74c2cdea64b7ce672956a214aaa7cb4f4f5107579a43f7a9826b902af3f78c442ab7a4cd085b1b1202cb5766b1ec0ef7e816d45f430a60c4e068caf4d8cf44ec181714701e6d808680674c23d698038d8be88d29337ad2c3d797a01e4720ebac2ac7e2962dcf59c0368cc4ef83000c100e842a550d0f89885738caa5121523d1d86317c015e4bb2bf16960dcc14c51f7d2eb8d499dba80769c6cd0e55c4c7d74f4e90b034c1e75cbfdc8d7a95eda4276202e3dbbf9d14f7fdd7d0a30a8f09b91f209b22b1db6b64f1307e07bba701a5eea0a8166d11823b2de41a71986f67c585a98f1659d1b97c79025bd145adf542f398b94db3ecb7eb8f8593840a434ec627c765958b64e278a7922e4679ea4948e303dd7ebc4c3f5dab1b6ffc573ff6db662639e10f6a1766bec2e4444bed8dbd0b6b536953e6b22eae7efdbcb038c57e4b05f3b8f5c0fc2cf0118156153ce7df394166892af6c570d4de800297cdf1b1bb94f02ec59ed7721dff40f65121b6b4ded6f36b4db3dd307b95b7ca39e5a7847343ec67c1cf3fd1406b1b9176a7a0d5917dec567860099dcf45187d2fbcfa63ece59a71dc466a6291a92d44196c334689e0702bebff99421046a60a82b3db25280c5dc007b69421fc82a4e72dd6b078e8eee75ed1e513d9dc1fa0d36302b723260fc882b1a4cf5c8a457b1691cbe1362c14dc96143b34daa2b81d0b220e678e19548e129fd195ea53f479e807873f05a158c07ee1478ddc6a13510075ee457ff8988029e55529388703a81e78c72fee269c5ac3af3a4b7badbc26151387cfa81010d62fe6fd0ff43ee17e7a36c89f92316f560f152b03b66c6f81a953a19feb0696fe1eeb92d0e9ede899deab7dd6fa76be0ab5cbd7ba0931d7c7905c21a2e8dfab91fddeb77870c882fc5b634a577251cfc4269adc21a3835c5f6d2b52767f038ccd0aee015dd612f64228303bade510d9ad95fa9b86c7f6adbcc5a2038bd2ce275a51d2a5cbdb4bf4d99276f049ddad5b32d560eba0c48908a5ba042166eb172225c8e8f47bb76e871f5b37d1cf7a051d365a7f331e681ec7d65a58916943283f51066529586975d309ee4963cbc80e7c6e553c1faefadd873e755df422851327bd78b63d2edc8d20efa06babc87557bb6b6f5e51e74e15a4d54f5b07798c7878964745abfef50a806ae5c6fbe39c151321a75228c7ad13b30a03ea45dd7161af317d89878d4b5e5f23e2e8e50d01308fdc51d56f262e5222db53ffd5b120b07aeeaeb347ec6c489f8d289ce0e0a2924306540105680f422ab9e9bfa7d580690e075a62d2926f6953dac66dc3866c325367bff7bfd50f8243a834e95a969af568ba4f7b7306e392f0c2be162cbe4d051d2142244a1319730ae9697465712b96cd25ac44473ebe96cc6e9eb76805eb05af66a7302b515b3d9fdef4304c3267319dd660e6dc998db9aeb0ab409290eb78662dc082accba18174ada35087c57f811bd3a03f04a2b4350d17e28f663425eada2726d6e0f0f6be392f107124b03b4083c58a4cbd8de72e57c0c86018cadb5b68c4d734f8f9538d42d4c43c9878b5d7482ab3e8b904c86e58b0c85a2bb2609cab63b49549436c7612f27e21419ed60d3a105d753639fde3f91ac2361f67f980d81df794417ed3e2344d0caaf7513101165669a61379d7e50d708e1958d4d1b537751eb7cb2fde91951566d233c05480c1d59c658da027e65e39b15fd2e992ceb933b320bff6f4b8f966ae018f658030be8e0d9657e6381309c06d0eff6394d94f1601a92779cf0278f8aa673bc60d3f96c4646d31c9af7f4f093b1a8b3baf2d9347816d4b043206f24e5985c439e99f61ad0f68ee7a32b3d3ab1a8b314308e04e2fba109603187fb036d6dc74dc61ab9419221bda146cd628aa79a00aa015fd5ca24bb75c590bc050b83b696c3dbcc20211d8aef61c2af93b24eb95568c4b079580a98cd154132edaab95e238d0e66c61b5d3d34dc9c88f211bb644d57c6aa49d6bcafe40f3dae34272b2ce417752b4d3d940a285110d7b3459909ac20c436b30857f4e5839bbde29cc2ada0c3106d6b9791041a99a721e275253b7463184e415e37aaeda6229fca6f10d19272419e3bf46e4fa199725092faf198c829e53aea04cf70b97e0f8577313cc642723705b9fa8a956cf883f0190be4b6264151fee80910cfbba667f4442aee653b07e5d96e884bfc629ee5e904a73555c7fdd94d06c31d3ba9d201e577ebc474d9a22fd506df340d06eee39c0987d8372522539997b580750ae08e8d68685354b08da7de4097cbe78ef7be390d34630d8dc403cd103d6656b735b6cebc9be46b5648ff8e389580e07d200baed0bc853d01b198e05a50e097a9a82097b4194e967da15989d101c2ceaf67d440738295931c5c4c2a7d2615a57a28c6d2d1e119889e36442c96c56c20cbc626bc3381779ad214a25e9d4b36a74c566628d4128e6c4e631fe327f06b5fc34e7782f729d5b4c687da8ac7fe7a274c0a5403f2b56881bd85edfdedcdbb68711709f4707e307da4119602af6edf30308e9ed63491a7446d5d0037b58fb1d16d9ca62fe93c69a38549a7b33704e54d420d811b2f0b4e870d9c98e235ba836c3cd1f7b4a335dc4f96eb86139a70c942b683f4b41a2ed5325d4b2772fb545dfbb9d139201d9b1fe27a13a026f22d5f4b2b15135b760ff86e9e37536c34854f246d2539143d753e1e9603964d20e84a035d1bb5e0dd171102f0a33cf5c021dd12bccdcf73f6915e8d30ef1f2c373e5c8d3033b558ca2eab3266a1672a4cebcd0b148942ef49b310818ea418f66a72ba3c9b0223625103b6d59047669b007a83688615417f23c62d7d2c12f52c9739b7c349378626e13be731f2b1b7bbc9b463b8e991ec74b9b4b9218c2e8a55dbdf2ac5fddd1ccd3c74d2499c97654ba0025a9a3ba0be0291cb2debf23b48536e787ac5b7265f9120c1f56c51607b5a1568d0676f7071af28193b4b20aa0d017a19c97a38bbb7c7b3416691b1ded56c74ced11b094b0ece7de3def490d3011f488c61623d40064b5461475585338508095bc3bc5f39ce4948827bec6bdde570203e68c8237a86fe101cea12e3a314c4ba19ca434968a75144155d38ae833cc185007ef4f3bc7898117867c8a8435f211b9ec614e51c92de7a39ba93a0c0ceb45089502ca88d59411af04b6c954de0d1263c207c196568fd3df64c0a04fcfa573e7e55ebc23cbfa7996c1754c67c00e3f42f1a504f43ff84c9ee58d1fb10d0006b15babfc84b5fe11a6f27c2c14124dcf78f3e1ddf789626929b8122c2f578f899cc162e5f055b3d87dc4d094c610270e8d819f926c01fc63a6f3e5703ecf282501f239942563d8e93da687285419fbaf3844fc434f8b6caf7814cae8c86d0d75da03a7e1cb4e8b5e46ccefc38191c2535e8f03928039d9b220c3a9d0dc3d910992aaa6dab188d248c26eab21c84e279f3abfbe81120125544d2de39bc6c5a9ae56576db867c2d3931459c71a84cdfb7e47413e6facde27354ab20aab2883ad4ca6c60d64eefa695fbc97daf779db55c8911094e2a39bb9e43883471b7ff9cb377dd8a015aba7371c227f2b6e70a198dd16cbab039e2e0dc208d86d19fd37afba327bd1d4e450e822584c2450cb8e64be5869765439f9e56afc0899e0bbf47a556e992ce289ec5aacc98a5a5f22c9b1befcccda501411c35c4d498fe16f22448d00c5cd42df6f737d820e7539c1ec46c7a26143e528dde70c361b89214194794cbaac76d94ac88119dee248905c33e119c3a7a06d1ebfd7e082d165f44de70f66bbbbabd10908517b96b7bb7c88307ad135958597cef6933fe7a4c489ae3f81f03319363e480ebedd8de89102c014cd46797d36a4dbc4c0e46e69b1e94a41da73f0e84fa2e2723d5c58658b6458f827d3355d6fbda28e1acaefd030222a600ecf58acefeeb638fbe932a7b1ac9188ca6891d73e20e20a4bf08aa98403db3cce837d36a067a9735d4e6c779344cde2c38b009ab4575296ce879b445ce101859060dd090d74edc8ad05ef93287cb8ef6dec6d39bf4ae97a80f9b914a24370ab584ad5170bd778b79d143447c86ce12413d1272b55f7671fb43f2646ed9d3412a5748cbb92e28bc4a4b0c163a5871f76815ba3d0312236e190ab8ac8d175fb4e75b4ca1f0cea0cc05c573cd079fafdcd11bd524c0e91e09afb73d53ebd6a9097ef877a7fe886c1c64f0ca77edfe5142b67be8b5d680c21aa5de06dc9df97d4e3d1e88a8f65564d3de2fc4cb52e22ad038f7097ebf6c288c537ca49415558d04a2953408a4a446aea458447a0c943b2a16a38794b36e4f9fe1520a9f1bd9b362ff15e5b8a4aeab998f1c5e6707e2f10e9cf26654dcf965a6c38d124ad96840cbe03dd50a03aabd8d58ff2e82a65ccb0e3541ce7b3ac37ded10d9381e2b273a1b501ec204d312977ba8e02114c77b8af113c2399082e8bd6c3d953e7b7a4a17241bff35f37b128cb27916f1468d28d2dc8b204a70f25a8c7f54a23a0e019a4371ecccf450cda43f9089838d79e29998bcc18133748b41cbe11811999631994cd5c8c44caa7cb6081da549cc762ae68126cce94d3111eedf9f9bedfdf9e19f5705529e5ba58cd2a7fda91e5cb39de4881b490e752119d98e03fd02d5eefa28b43f3a36c66645c2bc5779fe6206f7fb7826c7fcee9c9fdfb182e576ea4e2f69be6f668e359aa3f959ee724fed8ee2f9afeca0154ca9fc73f70ceeef9503757ebb76d89a032da22f6c5e7e51e47042aefd5acc5c07a6279c934d21f2d3483d471585c68c3642bd2612dd67cbe16acc3c03f69213610c205cd6304c0a3961f4cfcba17277a899f1add9354e3aa7bb8739ec34301c33704cc2ea7837f669c8e11ae260dea386d7a1e88ffda72976df3ff0109e52de0697fe4bce9be7443f55b956b3f8a592e14b0c33b83042b804f883503d6f4f032b38162e918a73249f84ebc5f0f510beaaa05c5e081e33d5d34bfe307e40c5057c2a66bafef9aa71a0e7ff31f385c8eae987bb5ad70d556d13d5be37bece02087c672dfc03b444b415f13e4b8687880dc8dca4452e5b8c30420c9422a7436e1031adf0b7c4f41a94c783b2ce1c8682c123599b6a6d2cc239bd50ccf172d7ed6bd6a77ffe25445b148a44c387ae2b4fa38d0e76fee6e7fd77cfb657c11a772270dbe91142148a6902be217807ca65239c6806cc1bf142c12ca5b34fd6f85c89f8bd05b16ac5fc91ac6860dd38a8c6e88387133cfd4756330966b461a8cb744c0d991453336f4a60cefaba873b04c02269537db6f3fe08dcd1fed27a2c862dc86b788d6d3c9331643ccd77e389c5d16a2753fe0e2ef53bcf5df2ed827b86fca49ed059879e093be9d91f1c5922d7c1c4d2bdd6076c8f785de6aed30797943cf38c985796177186caf6f672921f51bcf4cbbb7fc29fdf25c6659945d4ff583b4c137db78bdc3f87df86686318d04389fbc4b518d30bf18b717cb1f776915897c56cbdbc2f725003bb9ec5d79939770926636e7bc3097edbe9c93f91c6fa9134d50934cae224573e523045e60ab65df0825f613c77b7622e9f3d3629cfae530211fd4f9aa33a73c8433a4ddbbeeda59529681f19e67c869d81935d92d1abeb7a3e3b967fa98e1ae393e7f62b93f73c15a983c294696fcb46cb5d583c555742c8aafc07defeb4c811c41dc7446400cb5572279c7b9c75956818177480cae5c8ad083cfe015786e0b0d8bdab5ef3ce5a3e85688b0bfa861150b43fe56754b508f540df5647bd8be4f0c7fc3fec0df4b43aefc6fc308c810fd066a6e4b862cb27c26f811ba407d60c9576d6ccf0665cc9112ac10b79cbf5ca978bb7e5dd6addde66ab9db6d71288c24fd877f6aaadde4f097c7da2ba7db43d1e5a13d2c21f973219abe2040e066262621ae67685997d59c10c95c2e357d20bd23d55e2132e3594f7712a799dcc56008dd6fc518bf2306486aee8d2b987eb7f340008d33565953ec4d53c46294aead303c4c3c04053d1495a622bdea1de7148c1f93bcb957ade422fefbab23fd529481c69913f51bf24807c57e1199f83f625cfaf09bb4cda90c0bc5beab4c62ec19d16775d2bcaf3022165ebebe7f35ac9cf79861bca2136125d2943ddede538cc076ec30e00f2027821b786927cb61541bce803ac61a80fb32f6a8b0a658b0b77f643c8c83be7a6932c7af29764d085327548711a983744310030b7be2421c69536e41e1a38439e3904114da32e0281ce7ee8b443ac41a22c5449de9c4f9b8052e6e5a1bb8a54c8a7b2884ae56fa2ccb780026d690fd336fda4156f528bcf75b8579e7c50d31565938335d0d6794a313920805fba890bcf3e633c1c3b708688507c77e9234c107de03e1e55be280cad7e71f265a6c93bb649273e0b75447801a74bf66e248a91920c2a11d4294bb5557d31671b8390daba19566535bb30d99e195e996643dd1a75883c8cfe2ae8ab2ac5c2d59d69b5581c4279d73d109af34196dd6424ce81dd3e68d98a4ab07a706c5d0d08d8507dc1a9181e7e9f867bfc56ee6d1cb7e32e4b9a74de40ceb675e98876596747cb25402ee1d7b957130428bf70e02fa4adbc25e455c9b7cb0c0a4ac4cb751018408025f61ab87828ef100104c9d4d523e2d6c369e3d0bd25d2f1f3c18cba35b482f9e32f6c5f5b8d11fe5b59bb40263a1aac6c45c9a0f07dbfe221409a386b03711fb317edfc3a6702c307ff035eb485c7ad29a2e7df0f0e129fb3d1a59154c164896eb741c627daa4c958897ab860b969456796e9c3185e5d1d55019bcce21f609b58e4950635837bacf76c02e7a427ef2eb3952ca563978001258e420ab603601d3f4bdcde27454325d9de1602fad21c4df95b30ec827f9f15c461251f82362d7231a4c0903937e1efb8063680a2de64b77b6e4dc0be880b091eb437b962078f144e2f2e84efb602fbfa8146cb819c5c34c1cc35584992c56586b3c146d27de89a1cecf66082b8b1a9e52738b3042915144e614832faa69b5a549749f2a60dcde95ab851019f504037a9362e6bc522c7ae72d7808723f8d5fa7d453d4930d38fd5c42a0f63d17ab908b3115a617cb057fed69af6edc9dbabe12da98c379fe75b8bb12e1d292ed22708f10110b8583f53759014c8468681dee8882ba8355fc8d2519b61defaa065a031bba01f695c72588467f3ccb32a089f1b9e2ef50ef92a9aea27ba028058c7e389b4b6dd3d109c827d7771e64000267304d9075fb552c61a527d4608fc91ec87602caa7731f2ca8036b6e8c27504270ecd309b639fec63860fd4a913461eed05c700cbacf648b7bde403f6611ca3adcceae53ed055b434ab7af83f22fe625ff6fd5bb56be0188ea97bce96a82e379bc646946349a92c2f42e9084d465a887f4b216fb576509d133844cb9a929c8e972c3dd0fcf0c0d7eceb762bd9e8c7174cfc05a7d83f5c311c6fae3c3cc54edf5084cc8fe12b34b196bed581179d8ddf6e24753b1de6137592e9b4aa34f4d19e104523f88b66a6f67375bc3c9d25fba5f7c2cb03c17c29f98e043dbb4794dc5d4352d9bc12ac1baf90ae4f3dc2f2503182200e68a5c5f6593898cae49aa80ef35b87150d922e665583869562dd61aec16d579c689cc02f1b0b511bd507e869b2902f5c8260bd783aad5bf659907992b9b3d4b1c9a79027969112b35bb30583a539942ae70ea1cbfc6a1ddbc1042c888634277fe2d260019b7b2d2303c55569e6310b3547b98a6e535fce26c344767b53c1e12dc022023fd5a5236d30ebefe8c7b25760fad60c325384010c8a188a7fe48d3af43ad9737df237e3e970e0982c391f458058f354ea9dad4e089f83ad1722b28f0cb1a465a443ca2a9350172d38c919a975c3b174cae8c5f2f43852a9589892578f122eeb02acf112ceb5aa63e058e1cf6b9ba9840a9bd9118bd04178ca06e688b4307da14d76d1a67679365beb35d800fd02f73ead9357c2446bafec606d4e7b57f334e2f12ddf6af322fa1676d25dc6a04aa0da50d1de12def527fe73fbd42bdd17a12c850003f236c4ae3a1e5a4efeaa384b142d8d06e7085483af9343b256754b4b5d442c6dde70c74c606e772b6f25aff7fdaf155256f34be054deb4a48740af710000bbc6b79c628443cbbcf2035f6ad1e9db946011b0b9f8752b0d2ad1e5cab288ed79827ec3193e8847a17686460a0dfbad5cbb868f586b0b2a3b25453677766d32dcfe886a1a64908ebaea4bda1832f01aad657349f59a66cd23c9c8315f4ef008eef9f81c658dfc7e416fd2af0e5583684367c5c08e194f1b87b3a66a9a30af84453bf64f2ee5d9acb596f7f956dd44fd2bd9c93bb28d5a1a4baf42ad59ac473172aa83b0a0d881d12a029f5dc1389ddf50126ec0d6e7a29e71a497660e889ac8c0a193678d1b5b41d973c75ef6014652ec481ba50e0997055f9db557ff5d48b74fc62e10b1269b221fa21174e617c6f317e044c8c7c8fd115504831e579d348174aaed6730ec38024e753a4c8ced2aa222bac40886fc7c30818d3b92b56c5055805ab79c05be53fa4ee116a4059bacab0af8ac8de5c30ea0b519e7a0c732966ec0a7859f49e6986ca35f82555b057e77aac3bfa9d8be60fc59409b642fbc6fedb94a9ab2945fd4c87b12503c6b396d65074e34c29903d7905ad472199109a3a0d148e3ce2889f1dc5e40cad0aebfa1d56470a403ad1af23bb471fbcb3c2d208cc21bd6130260c38a4870ae1114bfa4b89e8ca79160da31824e1f2b603d248b26ba74135c07daa40c1826a87a3f462f845d70cfd406e2e7ceee0d4a5a7c7942ae6d4ca997b2a8bc91c00ea5e56c5d46b8ab06f44cb2c9088221c5dc3e2447ba55cb824387121c2f013f756ff08e8a4d6ee785cc5e8d902355adc26eab1b3573bf3ffbe4aebb7e4960f48b6019b0808b6224ae70ae2b843fc787ac2bd8dd297b35c4ab055f8399fb1f1dca36d6e23e4e622e8e558fc2e4ab23c8e5ff06867ac72bc6a9944866c9f934d277d3e3746c1b28ae49649ecb9e80ae7f32b399658ceb3179f25154fb8e413e1354aafa43d83ac3221317e1e05e5a4ba3c3ff8a2f06183e2da45a2fcfb668d2624c63ab15166deee1fcf052cfabead1a059937ac665871aaa65a28f07d91c0ae4c032c803e563eae9436a3bedfbc9fccc13a491c2e5629b1cab195a7062103220dad0fd45a33269842d65e4d68a8e04186a58eab3e19721e4349dd74e1a5bc264bce2adffdbbaef135214e9815117014889dfda36750cdcdeb295e31a53a4d0af83b9d8c45e718859e4333eb63326a2de15fb38317638e18731859e04b4410585dbc9edcf22e685c76e96bb522fabc87537b8c3a0e1b14e3b682e2d40c681ae22c84606a929e7cbd015e8ca74fd66b93186dff915adef2e774bd99f6266532d7596c944d5b78347151802006b049afc698ea7394b04b79bc61fc5b31917d8f8542157274ac69556fd24d7fa940787e41140408e50e1a26df4b9d2290fe9f7d8d2c69488ba50b788e2026381523417e41f316e15291b62110627029b1d588911b9f32d7360fa32e1fe0ae0354898b20250d185ac42cf7749e924ddc70c230b8a949e0f6efe2c0fa2208dd25c9f29d57d111b623121aeac48b38344be5121ba97c7ecaa29483615151a6693a102845e0b9e55b11ae3587c8b15a3bacceac7fd910cc5949c55b3bed876fc50a9ad4fa45d114cb730b012ca490b9cb62e640394a3f925279d759b2b81315dd7847e3c1b09a54af8c189308e6e2d2c00a3261641300c648ab033579dafdf9b66fc7f5e34b6011524c0fbd040275932b3f067ce322f1d285bb8fe26ea0515f94d11c73dd94036567a7afd0f5b47391c53a1ef47f21920ce6d680e0935f37080412f425a370a9c71c631e5ee0a98af6fbf519a00977ff2211d77db1e04fcbbdc5af721bcb435817dfecbd861ef7c50158d82acb7a42853940d8115aa234c041e4785596247b27b549b2e4c79d17da87d3182d16d428f17806d63a5f4a87534e02ad3a4d8d7c15ba9425880a37ec987513b2c216eece8febacf554c972b66bdff1880bc3ae3262e4530dc9cb2df4690a9280c9454900900feee4ea19612f52d3f66781f72a3534aefc1bbad320e2054b10c03aecae7d68cc767a032f1acfdf76320a796b2842db7675bdecbf7b37e4eb8bb3778b29a2a0f0238437bd6c0b82875c3261dbdd45114ff9bd01562503bd9832ca38e0f3fdc53c8f629071d21941d921250cb2d2388109aabaeac04ae0a893fcb2b840b5a3e55460e08b40200cafe2d00cd3892025a15d7914fb243de80e44acb8c1c4dd1c7a7de825381ff3b2393c44f03ba96168e1f061c72e404e463f7db98c085ddf347a9ca9831b3082e06ed9ef5aba4e62fe6d9900b290836acae265b5945b7084240e5e4dfbdfab7828794fb53a3ae1eae7b4c383d6d465ccf50fb8405e2cafa1ee1ee5572ed83ad77e8c32fb81f083e459454b3e6afc9acf75d35983fba17e5a3893e037fc3bb93a8020a240412794862ead78462142650a2899121aaa343037708381d685c5da6eed9681027bbec56cbb9b75defee364d4f4f9c889569e601d634c1b2befe62b10531b5b7ec6956044ddd69d7ce7c2b8019b4e0adf5db36e05b7a9920fc00c3470d6c4ad6947f52662d1488df3e404563cc054d85f76d9a65c8f259d4c73576c150d38ecc85c70a0e252b595843bc5663bc241fe504fc8cb4e078a5337071f3df1b6070a3f012c8ccb52375c96760124aac0900ae7aaf3254de8412da8461c838838b91397c814dd0281e4a6f1f1dd4de3d707a39edb5f297f69d0bd1646d7b078df5ec9ebcecb178daf0a862b78a1ace6b11555f7bed6321b19ce58a4667aae4be894e5eba30d469db7f64e162f814d7d80ed00d73fdb3949fe42a6d0d69e70c18f7db67c097a23c735821d4c9b0e9ca0520678b47282a70d548888906b8f01f3cb8069f07fdff8cf21b24e5e42df82ef9cfef3f9c69832c786c87f52a36954a8533f41b3a7c62fafa661f8360c8aa7d07fa3c43383c30a20bb1b366c978afec8d6e791b34c568b37e4e0c566ff9390952e281b23f2063689f8ff06195bc3c3d3aa7f57023aebc47e621230e0003e7dcac69e261271550261eb07f7d756c47b79a5da7d703e85c630280b71e733bc8bedf8127cedcc5bd7c31d358fdb860fa3870df8c31e2e0df5d2511356029f792fb065b48bf7ee7828cae1234ebac626887fb6f82bdc02aac226f25b88a3616fd22683b0d5ffa1cb95d6085faff98716147f35786c952db88efdaff0bf1015595ababd050a5342ce400b03fb9c12c7bb23d1ad1fb6cd74360216c6d15375dbd3a3351007ab2400db25f834ba6752ce1479e212443f3d9ba989d15e489960f83b987fd80d3cc89750fd90ca7b3cfc27471f7e38734fb82d1638a8415716c6987ed6941494939cb3e2ec0e88617c26fb1e21d7c87bee813751ae6ef765a7fcc301f23cd3f3ad8a87e736ea47db70a1f631438c8fab066768cfee2b69ba3c5c477eca90ca55ff8f5a21a36c3c6813d284811ec0f082806147e5ef62414491848c25fe43fe9fb771fa42396bf624a85f7a4bc9de2320adaa6a41cede35f36d399f950687fda0ef80624d7a915ca3aa4f9b07c31efc0c1a2cf9f4aabb375a890b858c5fea67e7e01e9cbc3bcca0af0ae42f9137ecc010d49a2b0a0c014afb76e8b78edbc650e04e50ee78beefd9934e0733abd9c2df7d8ee43130b45ad4385a3def1a84fe04ee125c264f11cf421ff7b0e34309623e46bdd8e6708e10791791ff3d870799e3231c22d1044f6acd57a82e8b157c4d141ff1045fa8ed4a4ab0b3d2c7989ed0e6e2fa16778efd5653d8bcc409d674c9539d082d68ec3b2468310d66653df64e9999bda139d9385211bdbd0267d8e6545850d2df607f47a8726aa2f9491b12684ec24aebec843e49d2f7edc1adabad99c0aa1f6af141c3836b1eec524f08f30d1bcf5f8f75e225371e7153f8e3a4bbbbe9dbc6b5571f4759e24bd4084c41b987a177a1b756457065b8c38c0e48606e8ca9b91051e28b7541a788f1820a3437d888b2250b2bf773ea0789d21fe7175ff603c29604e04007dfffad7549200b98bde0809a34ebadd5b61a85df34d12b80eee0f1c3aa0de0e3feb8631001e9e6673461b0e0e8484f90ef50d0bf3f97d42da04d58edf88ad918fdd6bbe4b94fcf7d95f34dfe4a76fe073af76d2f1f3c358a7e7c2b70571094d4a56a352a3a53829ba5dcb4401f0489c4be5104c2f94ef686e857cd84afc1f8defd79672f8df4434f55af9d1813b6e9e510a8a0d46665794a6dff67e8aade3880cfe36e99a3387428e4dc5c8037cf483dc6bf9a56e298a104a51ba0cb0827f9116f868dfccc262d9ad10ce8e26aeeccdc1cf8560814630491caf460c0edc6c36961d6381ac906a5cea972fdd9eb9ac7144b38af39455131864e208f9309b89e7f69d84e2c3a47340ccdc56ef9e9f5c156919b6f56429c41d652e2f17f039a12d11ebfb5e0719bc906a1f4530e99250e8c2d9f490df4f91ccdb2ca1ed0f2a0ee547deebecb2bc5031dc8d78f315f5f579870fc44b9e4ccb3b8055468009434d700d952138f01320205f8c370757bcb338b27e6a6b4d07dbf459fa29013f67ecd0e75450cd52493d5799691c6503d2046a2ea24ec061eea37d54bcb82245c881f96d527c9a64fd7cac0ef6e87d10f34f1c39bbceb7c9be3c8a5d862c6a548012fc4ced81ee0b17651fd29d25d4f0491cc4e1649ea5f63089703cd86602a4b75894fe5285cc59e54edfd6403d0fa10ec727fb38fc57a28104c6365b05a5beae220b47e3a1c6518812bac76488786b2d2af641c5fc5297a5963daadb628f46e4982de2b5a769ad28cccb3df845ea573622ffd8cdbb656fbe439e6c94ad60bfd2c472b20e91b133e8a26c222e65de226556932c195fb61926add60ed33cc9a5336970d506960cbec88893b624bf20204d3bdf224cf6e1327a98155245dfd6d405336a01c17d654733d5ab3f1bb7e4342e8923cb909d5df21ce630e1603aadebb2082d9afdd70795d9301dc07f0f991455e36752657f25302fca4ff547257092f109b4a207ad32653d5c273f3b740b347b1cbf2c302e30612ecef8b8f2a94f1188379a5f514e921804f6d2378a7a5bbad8b3e12263f030961ce6a9785db1fe2ff960fef796bad70101416cb3d9202eb57365ee3a1bfa3f5024bf3d62b7feff5e82cbe0e0bfd0808f4d4dcfbab8237e541f8da59cf99e324bc3eaebf236132830c80324f64fbc655f0dbff86cad40323cd1c3439b1df014baef56807e0e24b242112715b2e27ff5a54ef79fe169f7e2f2e616ad138b2e06e4681080697f76668d3b71639d4bf11b21ffcecc509f8a6a195dc3cbe5b0a5e6ceb4580f4a4259c47b5f13da7239250edd68c35f982f5b5af2c463b8892f0c8d499896e7f8307b75b27b7d87a7875d16640ffde727d34e580815f351ebf16e99568729878e74ae83de92ea50b5fc31a18440b3be822b7bfeff0310fec5cb570f8875313c4604eb28d7026a0e97459aaef060086d98a6ee2fdf34ae8f533f0e98e950284cbd7d9b1604c7a6f2d5ff44bf46f41d6d3c6f71ba2220830abd642e9a74d2d831c1775e881cde01d62a96c06e9fddf14a3fd749a0dc9801365acb02f0a799aec61428225ec20a9e73997c839d8cdc363a69bf25bbf14fa68f3ade945a48375348315484797b1275ce3bcf1cf92ad993361234c45dbd2260f4fde12db4b33db7de8595d6285d87189eefcd08de10dd5bc850fa6f7b88cc85cd386e1c4d9e562ab5c630c6d86f01a1e2f2d091a0a7e07f8970f24abdbb8acb0fa71512901ceeb1ecde83b6ce527baa24388e1dc380b02cc0c93dc22fa03d655c262a4b09bd402f46399171fc2558a3aefef3792e9076c1b0342c0801616b1f641f9fd9ce9d56264027f2301438639b0e74313357be2739560f048e2ac5d4888976e9b7edf9c07e167ab4e7957c3b39492b9ee10653db38409987ae120c29e3e2ce52c3d2c7ea58fa68d11a03db298e46c353489b0ceb9fc5074046690cc7f34b553a42060fa12e19960aa09b8ce3da030b652b136930d0c0e93c26521643ce93e4c3af878c7c40cdfec6d309acdf49ffe196be469731ab8dcd702e62ed6fed29eab8f66130d5bf630b80b7249dec690dbb0c51f7434bd75c11aa903c58b09d11c44467753302709af0dceecc8c701aebb653fc327f66eb564888f81fd139320a27523420beb1c60cc541d995fe052ffdf14f11667027ffb5fe0c46ad4f30f8893e8a32636b6313ec98fd2f19f262f13ad839647e3f49ecaf65ce10c69e80752d18a7fdc751d4d3eacc56c73eb078b80e98c1f3c215db8c10ef0e2b81db7b6326a2b7fd356a9e0eb0a6b5fd868648e08976de8c43f37f614043f64d81acf907c19f6035964d8a1f77c337c3723d88d0c631853d3a86f05df7f55665c3330d84dd0828d88129e17d8aa40bb494b8d5a0f3c17c4d19c11c6566cb5d0a87337a657662796526661da396bbef7abb225f11eca6236cc3f18c47162ba5005d8451367372567a4c16df34613d03b891fae62d64021bdc6b15bab309c6c984b8be4e83e49a07439b2cfbc1108c63653b8578e986cd9108d26d845619993e0e2c24dc292ac20e53a9123b0d1bd180559300c8cc0d2355a3d17545cf5fe1e56c15c01d1fc44d067a89b6a59a1cfcf0ef2567241bd2d00c71bcb38138a4b45545bd36f0c997609fc85877c5d84d65dc3d4bac24e581ca9f613f56255f7b0639085308a5695b88e144886bc7e7c503c69d1f977863ce2557097d28be36650d25af4c4005d1bd3365f3ba9cf8103e65be4e86a624aca68624b4dce5b312f291de5df77511f6babc36631c76f19bd09faa179ca9e7da2f2f68d981792150f027bbe01733ae05a61597bc570db52bcd149966d690428517c6add8fa8e68841bce3945f2c07f3503d083723ee36ecd73a1c729e7cba84290701352147ebbe1b82bf5f34c4d6057615d73547890e7cf51f96dae7a9ac8bf0a40f8480488a2e946d9f2130d8f805b4b29299020029f79d9ac324275709f4046526be8fb379fbe9e948106eedba19a86d692f9b46ab10c8b90b6dcd3563d132a10c8c1912a26b402f374d3e35cb12d0a0dbd2f7fe2e08edcf0237ac530806b4399a50200c5f0706e09a1ea7b0885acf54caeeae3ca5cc23ded78a8316910add2401f24e4b3be625bad3cbd145214b1399678b8ea3117028023e0a53c6cdaca72b2a22ea4ead5a87b7fa8e1c6b7bec0d2bb1544df143172925768a806c56778fbdf4e93b833cf3307332cdc9eb2e194c91d93d9f9c97f5488d160e4edfb36fc6dca2be6e799553045f28cccdc139a8a11a995fff7715310c70d5b7e58bb46829838ddf7ac1b6bccfd392f220c4cfd0d90237fd68da4c36561cae2d0dde1e0618225dc7ccb2833eae7b937857277d11f2cf05d78bc29ee2c8e314cf313718b4eea092b437f0568d6180e7169e11cfad5217131c1c7cbc37da447c8dcf08a06082aca5251ab9f33a0ff62d28337bef4fc3c012690f0d6b05db065892fb24c224393673af83a30ac630bce4aef3df4546548aef67d09d2ff4d4f839b111e6c1184fa587d6411dda9ac3d731405f0acdaf6fa0e9f74f381e2b81e8527600fbe783cafaf9c79a4cfa647c3f7c983e2e06f155bd78819ddbfb30c4b5a194d655e8d4a41cf426a7aea59f8e3487cde2eebb2965ef8439199bcad3a681497c75769cfea464b19bb4c3b182e6721e5d375ce4165fda92fac5615bf610bd8870eb3002f7a356f28ff361d6fc4dbb7d2f430b195e0960d93eeafdc1e74a9f4cfea3748e07d70c90cffe5d6df5da9e5827bc7477ecfdb1a91b20d9bf187907646bdaab59e442bd7455cf2ca4cb79722a5abe344a2740612c2b285b74325b6a1ad8593ad19d266c8a36a236e04813c6cea3a135bf8fd3399c66b82530e149b470d91f0a93bbf8bf815e86c3950aed0c2bef52aff9ac16fc4174be595c767489c2dba7400ee1e81875aec76e94ab0c2221d4528824219ebac7c4ce15fe3a09d8f7ba8cf55edeb8b08bc99a9497b4f38797edc5a397cea3ba19e3baaf317e61d80ee3ce2e53bca09253ff6a3ad2d60e4c312d4cb1a5483664033b376c8b7f1f7f83bef921687f92e969126e1755444ed37e359be5ac79ea0d88b8f7ebc384490c9e47a06e3c83b68c5d00a17bf4894fca0edbcc177a818386e357e6de8748ed30755d83f789edf7155a0df5646d44ab3324009c2fe8284fcc24e32a4411c720294fe7cf872c598dc7b0bfb5e0fb0d8438bc6c5ba097b218ed1ab6b611d9b971e8b99b3eef3d9924e89e1878ca97d40caf4617569e08a230357ffa68c67154c62c201ba42e33b896884e88636c11d3851d93bcf280c90ab52b8cf4b3674fdadbf6bff0d49b331587ad43efe13d59f0ab20f101ffc36676c3fdefcb500b1ed02464be3303a6a8bd88ab588bb139469e53d0298bdc436c82cc86cfdee884c8686ab2d01283d8f7f529136ba145a61f78eaeea67ae1e419696f52682c04c385f6bd9015eea6447c59deea94d86ef8f60e20a3ce7e811f357ab6a6885ce565ef8cfbdb1a1cc5f2c9f0175f568e8c2b09c14a4c411002245860036df25e0e2fadd8576a2143b694fef465e0501c033adfb62cdad5b343fe36d1811675a969de9d3782436581d6cd8a89da2e9a59efd53ee8df57a62d581ce403e5a63f7e36806e1f2f0506675737653b6d4e96795811cc51c0778a277193dc4f67a06486ba6f8336ae0f7eff51cb6bc78a84ee440ce8e9ce04f6c7b72f8643296bfa408e753f96a64042889d78ebffbce226f31fdda9890a0cfdc0daf67b7e57df05876db1071c8fadd115078d1a1066451067f36033236142b292ea068c9d4d1842905e597f37c17b33fd9c4fa29b8214fdc243aad59dd210fbc22346c83124546088b3ca08d7b574f4cb0e608a272043bc54172e8e4c5d1f9d77d29f705159c74f0037b7ca6588431e9ac856eee8dd7f3a4171e8811127365d61eefa970d7b8de8945df83d496b81afaefa4ef18817cde13749357ebf4dce359c7a75382cd1a103e6ee189eaa17da8fe07e9c07505e4da023e4c46b40f3360b0a4900330333333333333333333332bf59ff17bdab9911e0f43a4289092e4ae8864253e9d77daf01769c35f44e2f9b663094b103110010ff7c49471373f051a68e073fcf62905e94852a8740834cec048dee49ea0de9185a41938ed499ddd83cedc792b03933f46dd959964a946063e88f038eb476bd2cec6c049fe3842b98e4585101a62e0739caa31440f3d1791461838cda1759483b5456e0b1850f5d6d2b4f412add71c4a73e687b92fb016247fe5ff69eaa0452f70561531f4a45a2649c5c82a0aa0d105269ebd07153f7a73c88891b5870663cc2002a51d40830bfc4bdd4fe69c24c6fa6c81f36842ba14dff7526a686881cf156b42e3e5786d521a59e072bc8ee9513f648e2d88055ea472a2d6875d81df9e8eea2e243d5acb0a5c4edbe558df6bb5bd2a30923a3f3c4d21d7688a061538a9287def1ee84bf0694c81cb1f670e2f76f78a78a534a4c0efb746c7962fc7e83e8d28309592e4871cd875c78e0614f8207914415b72482a590d98a0860c683c81f58cbe501ec51c529a6819683881cbe951a40effce6c730c349ac045def2fea4717214e21a051a4c60731839f658a5de6fb6b151e3055ba3ce682c81cb1b19b572a43a91221a4a302d259b98856cb15424557ad6a8ebb757132149602553b465e59fac7a0c1894f182198c11831a3028a306b1315ce0821bc30516a08104b6cd55e3e4282639f328a071043e878c1eb48557be6a0da06104cebdb6e26793ae102f348ac0af07b59f5a21b1f2f5061a446053be56c63b4b9f39dad8d8d8d81a55956ba03104a62be710d1ad3253ec6908e16c91ac60a2ae5621555489663174e091afe5894610d894b13377b6edc7f588410308dc88c77a6fc9b2a5af88918512500d98a0c618347e50847229cb90d4ea2a1629426dcc9c9cc643d9051a3e60dcd423875a485fe4f8a217ec58da58b4714b1fffbc604353aeeb980355b5f18b5df0d144ff3bb9ab0bf62ecd7ab7f7d3889a0bba4bb3526464b308352fc9711c6e1b792d7a830b7e343f4d5c229855cac6c6d9d080096a8cf1c52df8683fc69ecaf669390e5bf095d382eb47deb298530bae72c8a1c4e491379f87165ce53789c894d9fd9d59b0298ee669cde9515e47165c4eb00fa9fbc43ef463c17405f5bd9ea899290f0bd63ff0eb2868ea8dd0bf8231df90577a7557f0696f297d103947bde956b0adfdb1b33efcf778b3822b4f5fef9efc432b7b159c788c11f2751c424c695530622b5175e94d05973448a5f638e6c86317156cc87d6b2175e468b73d05579971d3a2e6a0ef5153706b97ff7a43b4146c85944315cd1852ea4852f09e96b4033d7d8f2b390aa6c646ea2d3ff61c92a26023eadd6b8530147c9d76eea79cf7af5340c1597adae7bc1ba2c47c8253f11062a19248dc3dc1b7f54de95a8eb3aaa413dca9d55aafe7cd390727d8f4b431fd73de867f9b602aaf2559ce619ae037858869eff64c309ad9525cf16b350f26d8609adff7c9e3f0f512fc24dff030dbfd678878f08525b80ef3eb55dae4b1924725b8906ce5221e9460b2c4d72fc91e4bb56312fc07e192344eec9cae43125c8a39e50ec93c3cc74f24f8dcf8db1943a68df7810417c733a6ae18c5ce721ec187df3984e0631d72ee3882d5581f7d7ca37e39f61bc1a57c413cc42469a9c3308215ab8f8ab71ec729c72c82f7e8ca6ab921e3e68d22f80badf2c92caa1f6b12c147e8d3cc49b2fa8d0611fc678994b223837b901f8297eec951f63849481f6d08d6cc2b5855fa8ddf5d083eee47fb1d76d01cec26049b3c9d5574e4f697d283607264dd518be6403ad782e0f26e45c7a6413dc63a10dcefab448e1dbe6bba8060ccde839c39f6f8d2dc3fb06a5131ba59e55171fdc0bd25f31c6e78a0fe917de063fb97ad84d0f6b8e403a39176bc562c761c967b6077facef323e299877a605b733f47217daee82979606b4259580c1eb8f3fb8e0c0fe93a2f776052e7287e93d71d8e67073e69f66a7548759f3af0e1be8d644e3cf30f1d780f41f26f799787ff1c58ed8e3f453c7b743539b066eabb77da41928ee2c0558e4777520e3c7a0f6d6c6c6c10257c010726871eb6c4cad14296fec51b981c497bc85cba1ddd74c325592d22859248123dcd3adfbba7048b4955a236f081f9e60fdea6a9548740181108e3016170000361109cc1176ce036ffe247d59df4c1176be0a28b75ac106953cafc0b35f01b993c27f3abcc184f03eb1634b4647af471284143165582e50ef5aa2439dc57527f48d0aac8589fc10a1d8487d6b3187321f8c20c6ceecc163785bef04ad91865c460bf2803bf1aa34a47d559c3eb2564e0a31cdf3c5987db7b8e311c69923c92ab467473c95faf7eae6f8d2fc4c08654b01c79148f4cc1303099abff6139ca786601031f4142a7f2dcacaf912ff093af11275ada8d5a79813f3bcb9abaad8207d9d8a8aa5179f8a20b7c34e5d145a77b4c2117811a25872fb8c08717bdff22855437211b1b1b1b0a003b7cb105d63ae8d882848888a7f1063dc6185dce1830302ef8420b5b7fbed46521659031010f846177822fb2c0679d349bf4aea8f46381dffd0fb615b76d42ca15ece989d7a1af4545c50a9646a860d96d6d59252942ccba57297ffad0ba2faac087ff194adac6c5dc2fa8c0460fd39a283921e6fcc6c6d6282a626cf8620a6c47d2ca5ba1e3e9582a030622d8d83865c080061b1b991a30418d187c210536492ecffef690982f5f4481ebaecd1a2939884aa12fa040ee88edaa16c2d272a490a22a9447cf1eed27f58b27f01d44cd31068d14fd2131b2363636366000820d417ce1042e74921c6b1adb4f8f37810fbdbbd7ef30455099c0a48f1e3df958ae067b09ec8418bab692a33ce10b25b05e92111e068d3412b3b1b1b1a135aa905198f04512b80ef2e5f29c9af73f1b1b6590a182166c6c10277c81047e37e9c5686522f5d21747e023b4494f8a8e9e59fac2084c8ad1736feee8ad939c14bce0b4a0052d86295504d66264cf1a2af765778891456a025f10818fbdcf93c49c254e4e20a0a24ac01743e02f273acc93b89573b286aad50716510326a8a15f0881cf1c6686b82eae173431b2ee8b20f0e9ecaac377e96c33e00b20b0af152d2787a718effbe2078c4721d6e4b969bc102246aa20065de33460821a1b5ff860fdd82a8d485eaa600b8ed28b3df0d0551d913d2980410bae67f082dc662975c65fafc5c84a010c307066ec226d8d4ce9b2a826261d2229de84aeff1c7ae5a4200530c0400c60a0818d8d14c0a00537461931d04591c4924a881e11521a1eaa3efe48437d18528e185931a8a1980b2e846f1e4bb1ef2d3b189481300041162e18e9c9517f21ee792131b2de05301863026398710b763db859a4750d315a1f90c0c6c6c6466d61862df8d3103b267f5dfd6bb4a0b55ae0fd811bb4e0011b1be7052a388eded8d8d8c0622c00001166d482cb3fffca9039dde45206649009d80366d06216aa4529e948a219d1cc4d8386897e9cb7c39c912cd88cb593fa1e7d5aed58d486c2a2365ec185108ff24b7ecdf974056f154dca3ff77fd8be1859686394118319ada80d63051732b2271bc9275a1eabe045dbea7e4248e11d5570c1e38bb935f4c4cf9e54709f22e7c61c58486b2d2ad8287ebd1fe4088d6d9e82c9714abe26921baa29f8db4ebbf992eb81574ac107d963db8d1f5eeeed48c1b54b784ddd48b858a360db2dbe75e7c0fd4f2246168a825db5c9eb67b707668482c931dbcfce2e55f68818593028a306195a3e03146cc7dcc13394e4a91c072a78c17e823dfd8e08f78e2f45e6093e4c29e728d13b0fab61596836a0aa514585c558403ac1f8af5db579791ce4d0143083134ccc71b4971b296aa7498cac1af418302883eaecf9310e193b63135cbd6d7e092ac9a6dcd41a8584199ae0ddf2760a39b674dce021a3c5e040d6a81bccc804132dfc3fa4d7a36307134c0aabb42d8f3f52a497e0f3dda8d98666cafc2dc15da8764a0f3bc4c9b195d0245959081524755594bca0251a223b4b09b6f3c64aa1991afbc3a24d983109b6a5c30a0b69121e93046f3a1d7858da7134df921a30410d31664482ade415630831a3a4cca1356640826949e61ed53c9a452e0604f611dc5ad6a5b3cdb16a536264a999230a5142a5a45995452ccab3527c3ad4b427da8a9135060cc618831bc1844694bc5d9617b2b1b1b1810685198c60b27696de98bff2c576117c904c577dcaa397788661862238bb9c91952d76e07de32682af948e65f9b7624e16447059296e04edf0bf626ac62158ed48a3354ec68a9c1982d7f3a4df1e92445bab105cda49e4bf0e9eb12642f0914493488effd7e1c70e82f7a8738a741f955488a82038f11191cecfc8c1b31b1b1b5435ca90a1021710c435cc0804e3ea5adf195f2c26dbf60a3300c14e477feb633164774da730e30f6c7a4909bb96a8f739c8e8164460a930c30f7c85d08eb6eeb0a47f326ed01cd8d8a851da0726d685b00c397b602af1814b1f9e859ff9e4a883670c12060828607b503524b5235aa40896b2527f69e407a9735aba8891950215f855193388c10b2c605535c60c3db05db9b3b5b667b3dcf3c0e64afa38c61e4ddd0a1ed8de9cb19255456fed0e5c87377aa1d6beae2163f40c28b0b1e183197668daa3901193e7ce61d180096abc60461d6ee94a2982848a65470cabf4398e7c9122e6a6ccb931ca8881994107562a8b755d6574cee40c0c0533e6c07b9c99ded2e1468f837260829667b4751c7ed05171603d8f4551bf1c3d7e6bc02fe1c0079a6298d6e68d493b31b26a20061e00810d608002883581196f60526aaaca29ea07f1324e0a482919e9067268a56c1e39a272a66468c80e3df66e9790ee196d603fd6b69c3da70faa270bd4a82b0d98a0060f66b0818fb443c44b8f611ee74030630d9c8f7e94e25658cc08ff052ab8c00866a8818d132a1e985d85caeec606160d98a0460866a4818ffe77c94ae37176518c2c1ca38c18cc40839aed65dd16b9324a5d4cebd26688186ac522821967e0a3a8d38de699627e6e065e4b353ffaffd0a153065662c893236e774a122103d7b1461f0f2db6448fcd18031b623186da5821adba6264c1e078c18601020a6819c20c31701fb4456e8d5e795aca1c324a04c2000106c2d0a2aa615450460c54d082161c12cc0803eb714e93bc4c3ac5f6c1c09b468aa0659a391cf70b7c14ead1670eea83c89d17f8dccfa97218daed535582195de02a8739e344b888c63c17f8bd1c55713a78a764cdd8825129457496584694abb855875977ddadc5bcc00c2df061a6d89a2c48fe60e92c3029cd4d2dfee57c1e2946820003619091f52d50c10cbc48600616f8da2bcf49b24f060f22e40afcc6945692bdd42e3d428815b88e1e3978102a495448aac05b8bde8bb7f65469845081891551528eb3650f3b3305f652b2b19f3839f7076da8064c50830633a4c0e4c815d348ecdd8fdc0dc628e305fc01a2861951e06e7dcf3f65debff60956d528c2861950e0e32cf5ee89faef172df08118cce08f04fec460067f3ecf0c339ec0874193e594f2e592546e6c94613a062e2025cc7002f7a9d32177497bab765f98d104b6554b72f7e487955902c20c26f04144cb93464eef77395ac28c25f0f1c75b675f71f34e574a041a81194a6033c7b0bc8cef69838f1a30418d9f91045662aaaaeef6ff52f3612086ba610612382ff50d953cb9a36a2a88c103acd030e308bce5b768e9ddd7e9716c94a2418d2a4583330213d1af32ae75aeed78810ac4a87170a00232b6861466148109fdddf9517e9024a23a600611b8e013f6817dea9d6ec8036144200cea40181cc0401808002ecc18021f87a429c71edc4ed316831b80606ba81666088171d50f5a72e4d861f245b0b151a86a54926146109894304b1e6b3bf3ba3002fa67d1ce66d28291c8398c413b67abe42c780bf95672fe4e264959b0395e9ae01de6a0ae8c059b3983e86553dd2861c156c695e88e57b01e265d0fb4cef67705fb1962be0e66e3716c05db793e69db753b4456b0659a27e5f4c934c72ad8b2d897b552f37b54c1a79872d8e41cf3f754f096f1bcd2acd5f3820a3ea6ff0871cbb17ee5148c44fbab9ca9d5a34dc1856e0d0f5c2c7a9a94828f396306d58eba13420a3e6ae52e0fdd519951f081075ec1c4bf3e79a26027dbe3b7a8a93779a16042fee9db8a288b83828f1ed9233c573bff0413db42dc0bbad3ae2718939083c5d4b459ed0497d5f1c7891a27b84d11730e2c99eb669b6033fe76e05d6982df0f235ba8786c51592638cffb74d921c3047739561717f5c83eb24bb829a2f54ece5196c8bd2d4dccc1a312fc75f8ab390e456c725082cfaabf6ef963cbce99041f7d54fb71e6448a47127c5c51a67bdf61073991e07ca543f238079bea4082c9dde1d15d8e1fe4ff083e2587e9c47adca277049772ec318847b1b3c41bc17ebcb9e3f7daf49d3382d5f620215be838ec7b11ac5d4adf574821a4ad08d673cc514ce9463aa513c19bf8fae41cf273d588603d33f405b1548fef2118cb2bb34c2166f51a820d9596bca31ce9782178ffb4395fe767af84e03c6d4eb0ef8b3675107ca499299d831ca4101504a7b9b93b4787664a03c1c6cc51ac4754f75240b07e973e5f8ade1bca3f702b15733be8c8f0f003ffa179e820c4e8eef481cd711c22636e47c9463ef019ebe30a379536710fdc6a250f82e4c03d827ae02bb6a48f37329787796082e730d886984334050f5c0879f247cdf13a57eec0471ef4a4096e953f881d984e39b4cb0a9efc2775e0ba6248883cb394217460537b253a732a343207fe7db36bda49fa614e0efc655734ffac51ba8b03a7ed1a69e3b9e4e9e0c075ee86b0cd923eff062eee74103c8867c13537b0131d62a6715292581b180f63cacd2ce6a93336b0e5318e7ea09a83cad6c0c791c31c761d21f96ae0ff5f7298d2b98374a5818fc8cb9e3352a52c347057111deb66e59fd419989ca392aabd374eca0c6ccca9cccd99e3dc2903bf1e3d3f0711457232b0a9bf6bc74fbd5aaa31f01f327fd746ca714a2506ee723fe4204b523aa9c2c0588eeba23fd97ba402031b1eaf7e7cfa29aec717b8b4f9b3e97845881c5e607376ad69741c963abac047be7992f56bea3b5ce0c3d51072c8514ffabf057e7208d21f99bd685e0b6c4bfcd30e6274cc7e16d8c80d313bf6f813792c701e07aa1e7594e2a15760472c04ab94d1dbb40217b2a53275e6d02fa60a6ca69ceb4d3db6dd0c15782f1391909f6e333305f6fe6e42b97ff0ca4881bffed6cc21b3f2e351e0a4e3fb8fd284fe280e05f6edb35f738cd2883f814f3974c494f726759cc0871635739a5f7ddd0476d36be6a61859b34ce0e368e355c8f1275397c098ef8d9f8ffb445a09eca51043475a96c3af93c0c4ade49ad34907b9460297355bca51d588497d04b623afedf8bbe35cae11583d0dd7eb8d1253b6087cbcd9e3eda7e5ad96084c7f925aab0d79a21d02ffbfaf371a3fec0f21389691e32006818fe3c5b79437baf740e0248877ec16ba9ff2033e48770a661a526b01f0019bfdeff2b669fe487bc1e7e8e3ee0da2d9392fb890a04957fac31cbd0bfeb38434d6139ad705d77194d9cac3ea2b73c168bee051e575e052e282f7f20fc54ab3aae316fc8450399d75a81fb6e035a6348f9b6c42bc167ca7b09ea8d5d96d5ab039d0cd792224573b0b36996b78c594faa92cb89a1ceb85a44697c482edd0dfec3205891ac282cbb29d6c9a3e1ee7157cf8e87598fc73e4bb828bfbb924674ecf602b38db28d50cd92e66ace02bcd744b4724466515ec5e66eeade8e86354c1c7e579a2a2c95234157cdfe65023dc7c4a820ad6edbe3f48f7959f537053494b4f638672cf145cae1ce4c9bc68ed948289b512b572f4e96f52f0b1a34d9e92e3d8e62838ff2c9d7da73aa488828b109d39a46fe464a160c4c3df1ce9e7180705ebfa57a59f1db5f30926d56dcc75ad5d1a4f30b62b91bd11a9f14eb09139cc19b3c7ab2327d809669e39799c63496d82afc98d209afd7f4d705a9a3f77d4cd6c2698cae5edf12be5fa98e04c53a460d771b24d04c625d8e9f68e23f2e62d89c0b004db22f97fb5a963864e09605482c931d3525c2fcd0d3e25d89ad4f0d4f5d21ac749f039f98ae7b0e320d54b24c145951e0d51ed2711a92680110976f2b647e93fa7b6ac4382ef38478f4de6ab05f7118cae85966075fb1eef8ee003c9528f3f1c1bc1be6bbfe5cbd48bfe61041fc71d27f148937b85e82218eb8e24f7520875ff15c1751e3dabcc7b89d871063012c1a57a0f327aea44b38e47808108563b4aa9af94e491791c82cf9fb3c6defc8b18f1620876237e592c2bd3ff142d041bfef1e8b9476f4ad706d228000621d8d7982475a7778daf11232b07c1796976a474a5cf981623eb8c1103ab304040813068008620d8bee9cf1af1f6ef6831b26a9c61d98037369e004620d8d8716a58fb9547538701020a6c6c90c10218c0400c238001083e6bb6fa4a5a39544cf9031f7654c91fefa6f08ff981f117eb683d58c88debf581efe852c71da5d3a43b1f580d4d1afa515bd490438cac1a662f08c1c6c6ef81bb9cce2baf8f6eb01c62a4046400861ed86dbde41ed47ab8394e4300230fdc27bb1ca694cdbcdc27238d0b60e081f7bff48f5b2468f9bbb141d400c61df8a8397d0cdee6f799aa25c0b003962f5bcc1db68798f43d805107c663b26c298344741c4260d0819bac48fa795f3921a400630edc759c43bfc9b1e418626264e10ec090031f07df0bb61e73960806301883e4008c38f0ad922669598d5fe96264dd608c32e0c0795beab8c35be37e6c2902186f683f8a943753d607ba81d18997a3dd88a74162dac047e9618c9c94839c29c806c6eddac389115352770d4c921062ad44ea1d330559651c0aa8064c5003004100430ddcbfdd65bd1ccc93bf06e0021869e053dc8e2d55c7b1c76d6c78fbf1f602f0020c34f0b69a6ea93fc791f32d46d6d612c038036fb1c7b377d8d1dea78891655700c30c7c941b3ad0d4ba1b2515060828108130b0580536364c042a88c103c6704106c670810b2e0b609481b3f678ea1abd22a7bc1859859c08541083071045804106d6f244d3cd1cc7ed612e469616119441060c2a40060cc4a8a1821834195100630c8cf6df67f28888361531b24a6d6c6c6c1cf3820cdca00c1594b1b1b1b1414604c20883037f64e00121d8d88840187633f83e15d8d8d8d8d8d84011b4c09cdfd8880119698023802186bb3eb69caafa3e2646d6182aa851952000230cacc761da28f952948ea5e8000c30f0492c567d9ef43dcfbec0f5fd4f88214acee4e311c0f002ef7194e24787b1a30b561820a04018e6011108e305c7055e667083314040c30d60748149f6d1c42c1d87fc8e22461619c40a6070818d59967a1f8ea4d83b185be062908af46c5ba2b9d40217aa5ab7f34cb78320185960bd63a7c6aefd07606081abfaf032658977e039c0b8021b72e284e6bffd5002c30a7cbef6d190a35899994255e0274b5b65a8800c15d060c3000105ca5001192a10418d1a1b1bbfb121811a680830a8c05bbce8d1f143faa04b31920c2080310526ab47fccc5b51a23962649151a3063d069905c09002b76e9a3cf5a2e6b4d602c08802fb1e7a44eefe5072889f0bb4e0a0c06ed408bd29cfed2e0bd4a0c0c6c66ac004356e00c613f88f52113b5e11fffba4e09cc0c51c66bc5af1e8e3d81423abc7383380c10d6a004613f84866b9a36e95cca589915523062e3057317041043870690083097ca7c9c9aef7d437826264556df4008c25f0ef29645a757674a1a2023094c05477f4ede7a2dd625a0b5a05658cd11cd8d888810b108c24b07add3e1ede3eea123090c0862c390aabffe83498476063fa38b0607e9f2949140c23b0395393957694058c2270199631db9587c5046010819fa4a5ad95437e4ab1010c20b0b1f1c7063070cb2134414d3b553c9d9ec4c8222b004308f994d7f6c7ba5152bfb1519d002308e777ef74e754b66830f88d8d8d8d0d5a9bc1093080607938a6f14b4cf28d8d8d30404081304a8d51460c008009307ef026af5cfa8167768c82e103f2e6e497b336fc72f4c2ca69592f3d2ccff0c2cbedd67ed9533be22e7a89b993597a6841cc1a6a478dffa652418f0182bb030d5d94c673564f4d8cac303e6710860b542006191b3408c373066110c9c5da99112b248d16d1f5380795a1aa3266e0821a2d68411b2d63061978c1044a0768e082c98efce259166babc92dd8d2bfed905aa2d95d1b1be7373668d8820992f15bf76ed32e550ba652c4547d919f26850c18c440035635ea3460821a1fa0410b3662dba50e633d0bdee3681e27fb8b1272109205677b77b59552a8e82a16bcdad7848cd7f73978e080062cf8702473b68b9d5473381956afe02e9877b42a5a1fe7942bd8360b5eb1323a4be62d6841531268b4828f10a3e5e620da81c524c03458c1b4b5dbbb6dc7973f5e8c0c03041408e348e056c1df6f4e264143e5b07f1aaae03afcd0f16fa2471d98a9e0a463f33bcbbb13df50c1c64bfbe97f221ef277be461a1963cc2002649c326e00011aa760428e1bd9b73f95cb9fc10b6898c29c831c5688ceb1792019344a514c492925cb22493417eb4aafe016f3c71f88a4301aa3609365670cebbc71cb210aeeb39b2791989a13fc50f0ee4188216eb953da028a2c6874ba254929da91cd3352c75e71fdd255f4092ee4284f7f2f7298e3d83b4fb07f315b4c965a2547e95d186874828bae1ed629794e1e62c609a6fa473fe58d128bfe26b814f69eb1c32b73746a82ff6cda9b163fbacd5f2678dfca488fcaf2826698e024e62093e55beaf04e59028d4b709df343f3e7cf4a316b092e57881ff65fc84a41a75109265d82e4f59e976b9894e034279564eaca5339dc88c624f8203564f1979ced3a87185948c629a33150041a9260728584b859736e841d09c6b3a654e7597a27b83420c1e4203784c5d81f4fca79049bed530e53688cd09c3b824b4bef9b358f9a77aa115cfa8f6db7f3438c2c31c898410cb68c19c4a08c196c0019c1e4b8c314db55b3aae33416c1e6e0512be6fe8598dbc6c651d5a8ab83030d4570a9421cc91e77f6d0311b1bfa051a896043c71f871e63270fd2bfb16134d8d8301a5835810622b84dfaff61742c8bec21b8e0717c52f76511238721b89059f3935804fb3c6b542158d1d89fe38cfd71a987105c7dd25cbb79663f9a41f09ba7d3c7f96204c1796666091ec4dc5af336107cd0fcd0426693c388800736368e4003106f8b6b7e6021fd47e61ff83f8967b13a7b5caafb813da96421577efad8dd075ed532ef5d508fc3900f7c5c4f1e7b4988529dee018d98a2d1124285b985d4e8bcbe6eaf29c40e0d3d70db1dcc42aefc1b6de781891c7fd3144d2b22e18153931ca61b8dbe94531b1b687760328a44ced8a1fd68c60e9ce5889891a3cae469ac039ff4562abb6efa1a37f01a3450c19782000d3a30d19fbeeac792c75605631c326a8c018316dca0cba8516c0eac460aa5f6d9eb71d434e4c0e616adb857371e7b4e1cf8c852fd8667ae9bd4c2a12049ca5342a8ac6cc92fc78ed2b6d4f6063e62bfbfe6d1cb36961bf8c914d5c8d4c80f510f68b4815151d5fa502b8638f909d06003ef6e9aa3f44d1f3f47171168ac81af9c52745e8ddd715f1b6ae0e39c31887b9985f2fcc6c6c6c6d628b5028d34f01373ca09d805f001bfc1726465273b4d6902f482fbf83e97e5304d31a509c00b6eb37969a965176ceee4e6894aeb82eb209da40c1e7dd090ce056b952c76da74146bd2b8e0a3e5b2983937ae47fa167ca07f1bad5e532fd2b6e0c3bc8865d782959026679b2ca9c5a3053b594ffc22d47467164c88145137595eff65c17fa59e889f9ee31b0bbec3d871d66aab060b4ecfcf363d144f9157b09d238fd4a1246f64ae6042b6a05174b388df0a362cf3473bd13e0a59c1c77b6b9deed73454abe0bc2ba262a889f4a10abe3f96587e143c2e53c1ebe438674963661a352ab88b79792af24698a7e0c65282757c5193789882f598f2c77a31d9454bc1e94964fde0e178c821051bd29b4a62f2e9f85170bf2956caf2ec18140593a4dc52acce1f5342c1e4e48e24e441c174d29822f9092ec472b3541a4ff02791398c9c77828b397e20f903ab09694eb01b82bd9a787d8eeb4db019b277a45c6b82899b3d07ed172c7b6c2698a831063b91641fbb98e0b5ef5cf237f2be7b09b63ec5b57a8b7bc9b5049fa129f32a8be76c2bc14ebcce5a1ad3071aa5041b9da33fe9daa65627c1f4e6641524ac36a49260fccdadd7ede3d06624783dcbd9a2c6943a4a428237ddcef125f908eed4b4b2794e993fd0115c0ee191a257aaa3b111ec86a5c90ef7a8153282cb13a1bfa7963e5216c1affd76ac6aa1bd238a60b753aa84c825820db7cba903adf8924304ab318594af7607217708d673760f358b4ececd104c852cdd1a53e48a6c0bc1e8787c298db584606a2ac6d6edfc51a676107c0eb2554eb163c765ad2078ffe8c3cfd91ec5606d20d81077c5a3d41575aa0504f7d99356488e249bb47f60a344bb2c2ad9bf39fcc0844bb6e4fdd10736c7b9b9fe41a7f5e0032bbe7f2e493a1ecd1ed897d8e351f3efaf0736fda34fac8914953c706beb572d794f277860a37be8316367c8b17307be2fa7c81d1df5453b70b12623db7398b97d1d989c77b2a392fa1c74602db3a6667747fa9803231124e56b530d8d910353371d954f8f03a7395868640e07fe3c3d4f7589a61cfd06be423d768896fcbe7603d7ba5629f9dd0646625b45b3698d58b381cf96a333e9eb1ca25e03635652be297ee40f3530f5a59fb9f91d7aa4810fdd359dc31c3a7a2c1ab89f98d4c3de3330de29c903a96d0f5d33f0713fa7ecae1a1da70c9cef7b8829ad64605aaa62c7f531b4a963e0d53cc689292a06a6a7e207cded774fc3c079da983754bc354d3070a779a1839c32e62dbfc0a59c52b17b244f2abdc04dd65011a2e5741f5de02a56dea40fe4027b9aed6fb7f7e31cdc02ffa15cb3e6905a7cd402bb1dc1420ec42cb0e9437d07be5d35412cf09ab9a6a390b3bf04afc0f874bc3b92f51b422bb09e3cf78fa73469c22ab0a71d332c6aa6140b153897c8cf981a517d3205364296c6ddf58e1a22053e7a3bda787fd371a25160a2e3e8dbefdbc142140a9c6d4aa932e4a8ca73fa04364dee0be239818ff224ebbce671bc6b021b424d2c84799433c604b63d881fa7dd2c8fb525b031a5d879b22ec7a02981c995a6e395e86ef1d224f06799f36712adea4a91c0f4c608a9b3c3b718d22330b9827b548d314735a911180fb2c7a123d6b79216810f3af2b1ee682a7d1c44e063f0ed389fe4fcfa310426c779edfb2c781c3a0410023bb126f6afc4e8ce1320086c8c79d99176a0a53d0180c06ee826d188f8d96108f0037eed3a5e079ef17243001fb0b12ae80729a7d534bde07e3d2fbba6aaeb79c15ec550e3b93f7aeebb606def4a7c4c3cb7ae0bb6222ff4b54d75a0e782ab4f9d199d263de8b860533a4e3152505fcf6fc184985cca73181562db82d5541fa46bfed03eb6165cbd8b5eec585a706513aabaf7d33f66c16565c9e551aee49c2c58ffd0927b1cf5b162c1e43cd1bcf23d8d14165c9e1423a494e5005ec1591c89953aef6e923900577057d767619ab2a33207d08af5bf831c578eace034a57acbbe1e438babe0a2550c12cfa20aa6726dea64d7d24b4905db164357bc90b95e4105a3e68188d96775ac9c82b5cbcda6f98147513105175a123274ac1f764829b89e4c898ca9271d210597af3f34d7894e1319059317f3d652fe30f31305d367d9a1e69cacc60b053be167f9e378e3a1060abe3ff26825477610b24ff0618eb586ee38b4a73cc15404b518ecea04d391786d97eaad599c603a5f688ee38b1423b509f67b24660e5de25aa509b6a3145531fa48852a33c1eb56fce8703b9b871213bc7ff2903da939e39497e06212df0b9aa3b821a525f0d01e6525d8c9499be92379f6a00437eddefe9e8358fb24b83b6bad9ffa8d5a125c7f1027ea7dd7a891e0a398dd49d30312fcd69e87ba8aec96e3116c4e5759fffda2b4c311bc87312bcae33482899d63bdbc0b12721c46b0923d8ea3c7e11afa5904db71f4122cf557ba1c45f0217960412b4f72ea2482a9b89ba3c9ebb4d520824ffd91a6f41de44bcc21f86c89636d9f9b1a37045755d69e72fce06b1782ab142923354a9c5c1382c98bd6f1b4467ad683603b2a6257748af96a41b09a9f8334963de8d581602c25ff0ca176f6b180e063f7a41473f01c7ef60f4c8867292b7358e4ac1f98dcee761d0d66d2f681a9ff14152a7a455a3eb01eda58aae57045d53db0651eb4d4a317ed540f4c2aade974691e789b0e2c639ee01e291ef8949bcec53fee5af20e4cd48769f735b7426907d634fad57992b41c75e0fef533073979b63c1d98fc9b24738e93973e072ee8e5fdcd98f71eca818ff78278c5300e7c3c9efefe7110fb2e70e03bcee0f1063648f4286367c8b30aba817789296df288314305dbc0c796d399655709f5900d9c66ebf03c4ce33979b806563b27dd8e31598ec3500dac96986a9ebc9453c334b06569a7171a1a986ce6414f861c796667e037640fa22ba1679919f8307718ecdf635ec9cac0c71d897ee5b2d77c9181b74a9fd9e3f4e66e8d81cf712421ba5ea5db120393b394fd7594ca422a0cac68e61ed11c7fcc0a0cbc47eb61499876e87f81fb8fb207e1f9f2e65ee03b2b3bb4cc3a7c09bbc0a5b869474dc2f225e40217d3c4eed8c3ec4a116e818f3f1933b776bc8a500b7cc5da1e0fa121548459e0c3ceae8cebd729736081091551752b163aca7105de538e43c892de9d0e2b30f1e3eeb856f2eb4f15388f3fca11f69226e65081cd996f1f9b87f6963305466b4ab3fe635ded4881ddb65825a2c1eff351e03fbb876ce392cbf250e0234279278f9eb28779021f6f244dba9e9caf7102d35752ede17efe8e3781898ca93c0e19253b9a099c56cef7d5facba897c0a4c80ec1fecc3247096cab871c43ed56ba37097cab851c6e1a9aea45023f6eeea1835412913d02176a9d1e7d397bb746e043d7182d879a3c445b042e69c8714647bd6a2a11f89c6ff71739d4e773088c448999d43e4889c9010881fd50543b7b76c9901c4010d8535ffd9083b6c5710040e02db679a448d8a471003fe063520ba976d3ecc301f8800bba1751246ffa0fd20bde32779c58d1b253c20b463a0593a0162a7976c1a758519355d0ea485df05194a6cddf5e1abc5cb09e273d4c19d563f170c18e073ad691b9b7bb5bb051d5a11172f453670b36b7c53fbcaed482cb2a92b3786aca88155af0bf933992977ec75165168ce79e74f8595be25464c15ac8cbdf51b68c199558b0ad12257c73c082330f11c53d737d1ce7157cbc7ecdd08fcbf2e30aee436e0fc2727aa54f2b988e1fe75c751b2d1e56f0e1640dc9ccc3298f5f051f7adbab0797a839af0aee73dc9cfe0911fb4f051f56fe74d495266b1e156ca49945eab632d79c820f75696d732c62a531057f5695620c1afd717829d874f5d8a34d09a9765290e3382afd888e824b55b7ccd6fc3814059bf45527424c964342c1f4a40fa286dcdbcf41c1f49a7f8f8fb54efe092ef9da567bf9a66c7b824f3ee11ef27eb4b53bc1e6e8f414dbc562bf9c603f47cc5a8b314e7037c1e55ee6a6a996bcd1046f5122e7ee9a185fcd049fed5f2f4467ca9a6282aface8dcda91871d790976377a3db28f3b86a425b8fd8b39db637bff6025d88ef6bfae2edb5d9012dcbdd9c6ecaddc3993606232f7cfd197d25f24c1b4e4c8d771bc1aa712093e886892e3feef5012487092efc3d7be28e13f82f3bebc932bf22fe30846f3dee338aecdee36824b3988d0bb1e23f8dfe875d16a2d67b708367b495e799822380ff9ddee1e65ce27824d93fa5c6a29e61044b09bbbd351ea1869d52158fb8f4edb10314469d571488a112904e369952a85383d2e21d89c961e4224a64e8e41b0a213e3994df7871d82e0e3fb9d07d1a1c38e409c9b22f226092098f0f833f55d72f3ce1fd81c5d8650cdfb816fcda1a78e7e1f98ca1b62eeb154937c3e30f6b716b273fca063f6c0a798b22dc7589fc38d1ef88d57d277413b6b260f6ceee6b8e3b8e381bf8d123dc73be948bf033f29c48b139253a7dc0e9cc490561e86ed7e741dd889f71fe57a50f6371df8ca6114d31ca4adda73603aa5f68ea8d7115f39f025992e6667a470370e5cd254d135ffa411170ebcc6742f313c0cb1ec1bb8ba5c29fd2ea163d50d4cc6f81ee465de10a26d6092076952a6c829c6940d9cde7a983fa3a3cd916be023cf92a37d875a39a906de52ec28b26b9a1c9e69e026e448fb4e851ccf43032b95b2c3a0d217a37967e02c539aae081d87963c33b066e39aa31873442caf0c9cea86564fba6f493c32f041e652c9b159c814bc31701712cf73ae5fcd514e0cdc89ff75eed66b47b930f0e9cc227c3af2fc210706466dd383bc8a9232e4bec04fc8aef7a4917248ce0b9cc7a1e29f92cd62eebac04af6fdf724294a7dc70576f57f72aceafd21775b60f2762c71a4ee226ba70526daf7fbac2cf09a53a63a893f6e1916384dadb7e5315dc8b9029f3bfcefb45de68156e0730c16b67665495205bef63bae602975ea8a0aacfdbd789047fbf25360624ae530859ce3b65260cb73f8b97ac5758c02eb6d9583ce99a98602b71ddd458f26216b744fe0efbcd2836887d63881d7889903dbe9c9e5d1047e537d7eec543ded6102179d82a498c5c35f025fee23163757fa56094cceb288a8f66c258149561b224dd48e0a12d8b28fbe081d68747f04b622e78776d69b3946e0d3ae8715fdb3c37c11b8749f97d69b3dd6108189f02c518267ff7608dc5ddcc8a21d744e11027b97671337a79a9009027b6df635395baed081c0e7c9fe30e6b4007ec0a520f5d99b2e8ad502f0019762151aad9aafdf5e7095d69f5256c8109717bc47ff4842eeb415dc5d30519d921e52f08eb2ba60aa6f24be89d77e682e788d8eaab2a62c652b2e98ecc98385d4b9e3aab7e037c33cdbf22cc4d4167c4fa7ecd1e6b87fcf5a30f1241e4670dc2a5d043e8cead291460fa96a22301131b5a5eb4a1dd443603daa4ae3515d21f09319e1398c7d9eea06811df5fadd6ec953e72200084cce9d126e9336b48f801f30d137f6adf868ca1b04f880af919c2cbb4deeacf782c9619e14a21fa9a69a178c6edeebb24a962ddd057fa97bff76515db0f9a3c9949a2b574534175c7bfb478d22ea394c71c1bb985aa69ce92dd8094f9ebf25bfa7a4b660247b3dc70e7d4c23ad059fa6cf7f7214a37b50d382bf8e4ad344c71e5aa4b3e0a38d2a9ea1729cc34fca82f58ea9adc3201a3a3a63c15de6ef92aed8a776c28277bf493994e9ab27f3155c8e728839767748f939ae60520e77f42ac7a92399ad60cbde237de4ee7a29c90a56ba3f8448a1377d2a57c1e71ca5be28a15405e3b61e576d08d216ca54b01e6da8a9ce1f74e4810abe3dd3d9e6cf29ae7f0aceb743349d5e8a9237057f3bfa671d259ab4a5e052d358ce2a3df15652b0ba397b1835e646a8a3606b42d04c57e2c12b280a56cb93aedb9945b230145c8e43caea6b498358080af66ee2c7940e2125859f603bfccedde15f3cc16bf7f476868af1624a27984852f69633e4a465e10457d92ee6f4eef0b42c9be0837d9c1beb5234c1ebb8fec4f4507db592092e87e379c13bc8d03930c164b878548ff5277e5c8233c9f1bb4951ff7a4bf09b3b3dbba5b95d5f09b662a688799e59fc3e94e02da4d552329360e3448447c99a2ff996049b3f394cc12e877e6d24d89174e51d5f2a4b5148b02b5dbf63b7e9b3f908b62e8d7f18c2f2c5cf116cb4f13c4f398ec262a411dcdda40823f8380e3234a68cc871e616c1a4a66d268d2b82558d2942befb6c5513c1e62ccd71f4feb1e4484470f939a5504dab54f3109c68883192e7ab8f4443b096f386e4fc923ad51582ef38cc1c67cbd72ad511820b7edd295dc714e90c82c994cd3cde780ae91604e797432c104cda2d5d891ed584c400c1e71427348de70ffc258bec28720eab347ee02368e6cf24edeef13eb0995572c71f2f5fd4381fb88e19f524343f28b7f6c0fb5488a19f436f5a4b0f9ca5ed4de60b261d5979e0f623c79f2a2a7a280b0f5c284bea133bc68a48dd81711b8f728c943ddcadecc0a6aca11f67fb1ebbaa0e7cc8d16f90a89034e7890e5c4e4d48d7f47587cf81a9503166eabb87db7260624eaf9e249a4c3571e05a5244f030d65b04e1c04a886611a3a50f397c039b7c444b2d85dcc0b6f485fa2eb3ec618e36701a39886ac4f44a123670e117d1327a8e11ae81b5907218199375484e0d4cce71b67fa591300d7ceaca71e534b942060dec6768fe8590b3ba2231b25e705a600e0d6a6c4de000676034a5bd4d8d9272c70c6cb8f7beb82431bf0c7c4fec8a9f34d5c69081e91cd67e14d1a9fb31063ea8d3cad7996a7b31b01972583ac9b9969f3070d2f9732cd1fe7b30307133a4569407e1f9021f6ffa76b2ac20792fb031ef685e7e0edaee02fb51865f9e4829840bfcc698f951f55b602b748c581d460b7c849c59f2ddf2654c16b8d718638e8e3a46d660818bc8f4133a6adedeafc06ed65881a96d09a94752c56815f8a834c9b4b5ba3ba702e376f7ff365e914e81d7dc21e7a60f8f925260df739caed2e51c865160ef3edb448d7ab1d250e0e376cd10838b9afe04cea35b0e742db88b4ee0f58386f9c6da94b6098c644aaf9f72bff34ce0bf2a2d7fce1d25cd12d83ca9f28f5dfaa054029b36a5afbd47d524815bdf7fff9cde425091c0479bf1dd23f1e8398ec069b0f68c19ebed4c23f041d00bc9be99e216816f0bc9722574e51422b09f3f4c672d9d2e84436053d61c722015fb7245084cce98723ca9e3d0db20b0975ae2af871c00085c882e751e77982f2507f801d39b3c5afb2b4f8f3b800fb8d1faefa0bfc300bd602fb6c7cc29378f78870178c1d5e4c8318e5b8cb13b0cb00b765483a6bc1e4287ee30802eb8d469297858396f8e1d06c80563f1e350dd443a3ced30002ef810faebbf62b7e0534aab495bdf2962b6d03b78c56099d5828d1a9239e5a022e5450b26556d0cf57f1db6350bce63caa371333b4aea90059b3a4c317fb1e0e3e8f3ff26cff5a982052baeffe9e9e398fd0a36a528cdb917db5c57f0b5ad9d9347ab35690597d643f4841c73d258c1741053b6acaec92d3a56c1dda64ee7d4dfee51051f3c680e42fe380648052ff15b43ff570edfe3180015fcee27df00a760e27d10766191b939c700a6e026e97aaa14995b738e014ac1ad276bd3d2645d9f6300521c075e4933e547c1759c1e3d33e958fe45c144f5bcfdfc97a27e28b8bb4e912e3df5a60f0afec3570b0f43d48bfe13dce4150be6613cc1e41ca3e4f477183ca6136c77bcd19037e7dc184ef0a19a92eee468426e36c18490f21dcb35c1ef87bfdbe7af8e3299606d2f730571959c6382a90c397718c9efd37e0936b32e7a1ce3f66f6e09b6edffe3984c62f65482f73c2904cbebcf9e12dc0756512c87b4cd4c82c90f59cf9206cfae24b8601fef7ded48301ebe69fcd37f4f1b129cc5348f03ad1fc165288f3d8839d4aada11dc847d7bd8a1deed6d04979b9e37a464294d96115c87b5be1d4af5fab908ee2279d8a12615c1e6907247b929a55cc944f0615fbdd122a584242218bf8bb6313dfc737908eebd634ba6e0f71134046357ef393dff03b314828f3786342d413d300921f820b47322998468de20b8feda24a9a2a9bb1304d717dc7ba2831c5a7702c1e6bb4f1fc95977ba03086e62e5d97ff4af713b7fe072d884e72babd0d9f103dfff92c9d53efeb74e1f588f63a84f661d3eb01fdde5fefdb749a9b3077ea4a205c98cce161d3df0bb1539f0d4bf0ea29307d6a26d472d2dbdcd8107fef2e31c9ec7f7921c77e0cffe24e4f250cb1c3bf0122dc48e1a2b52fa75e0c53b735a2f3fb09e0eec997aca10fd1cd8cfb1e74a1de75897031fe66b85e5c83d67e2c0779cca18ec6b7c87036fb5c9c30c212dbb6fe053c769912ae63832ddc05e8a9b6ab503bdcab48149e9cf23d3fe96071bf88e58766639887e0daca61d4b5f69bd4a570313e377ce61ae5dd6d2c04928b7b10df553a1818992aa0e519f818db4ed388eae19d83eabf50e51fd915b06bec34c69e7692c5d4b063e637be69493db73e8189830abad14ea9a3b540cdcc5f18cefd69e1f0d037fb7e2818aa98ea46060d376983aae0e83f9f90526f74308f5d0728ad20bbcc40eaa92b506efc02ef095af3507aeb997825ce032ef682a8fca97c32db03951312d654a15422df0d1b575cc8d9d31c22cf081d487973d050b8c558847a9995a39e50a4c7a64d6fd673f59b1023be5665d113587a85481fb1cc4bc3197881c4205be7c920651cd2529640a4c9d485859c60f39f752e0a335c7a4d645813b3ff5f0b4cd4e1b14d8cc9bd25f70f18d790267e7df717565ed502770dad97c2fd66ea709bca5767e7654f7554ce0e3f175e9389964ca1298ac9c31fb790e3b47af042ebd6df6c7e8817524812b1bf10e32340012784921751c6b25edcb618023f0d641df69af664d87018cc09947b9639fd6f1388e018ac0755e8e834899b7c7630022f039d2f45b0fa777720c30043e7d7772c8193fbd630021f07937dd15e59edd314010d8ca314eb7b887371d0300810d9e15e2719ce378f306f8017f912362ce903107be017cc0c50f4b1ebb6596f7bde0e35451d772fc2149cf0b2e337b1ce7afde90c3ec8237cf81e4f851e8a835bae053464a69a53daed3e482abdd888ec31bdf89c10517377ac8903a78cc995bf07e399c8e93755fcad882cb93b45f531d5bf45af09ff3c73c7a9d52745af01edf390b5eba3e4821a72c915c16acd9f969eaf85173742cf84d3147d1c3538ea761c1649eb28bce3cdaf72bd8be7cd19cdeb19876059f4933630eb6d16ab782bfcca16e7994759666051733bd6d3f65588e5d051b394a8ec8618ef9b3aae02d456b0c412a527c53c1a4368dfc6c79425251c148e86cdf93f8514e4fc1a754464f71d74c4953709a6f92c7b79a3a92a560a5f2e69cf18394bf24051b4a57af1f051faa876ff9e314630c8a82cdddadfb31ec3f130ac6b737f5470e6697020a567d53d77ef6e708f9041b9ea5729424d9fd9e60f34e0e15aff246ad13fc7ee5ac3d2e1da538c1f4b7db4b5ee84db709b6637bad8bdb93a309d6b44f4255e4dc31135ce520badb44458a0c134c8cd93e7dcae8f1b8049fd5e2b58a947558823193bcfabab1e3bc128cc49883c4afe0775382ff3cc12b7ff2b88293e0e390c395ecb8352b25093e6affcd0c21b5f948b0af493c5d4b2546c84082dbff78c17df4023ce2fcebb18e60c4367fbb63f30f3c8d60625acacc99e2153c8c60dd37dff7c713c5f38be062ca5e1dead64ff38a603d8a12a37b9477433e116cd4b20f434cd929f78860334eb4b5b2cf349a43f09fe3fe2cba7e51620cc16765cd0fbba394670a81c71b3cda0c213815b3cde952ca96cc20383597ee28eff50f417039bbee6b8d772a03c1eae6e82f67b4e42120f88e5243ac6cdf61e57fe0b7326fdb93f76bda0f6cadc7491f84c50b6e1ff8089f1811b4736cf9c07a500db103750f7cf8a61b52a67ae06debe3bbd70ccdc93c70399a8d4749f2839778e06f235d9be6d8299677e0e394dbf6d2e2d79476e04edbe35c1da7690ed681afc8c156fb52472948072664ea541bf5724a700efc74ccdb39aba6ac500ebc7fbc29e520a5958471e044c33258a4c081e9b70b153943f45879037bf1ac2268d68d49e20646aa62baf4e4071e6903eb9ebf2c04b18e52c4d9c04d74f3489b4dda73be06be345e67667b089a733570bf1e47938715193f4f037b41da83998d77f01c0d5c5f660f3a4abd1acfcfc0667fd7b5ca4a229e9b818949dd2346284d1de665603353b060394aaf3727031fe4b28adf71f2d8e563e0dc524e96a3493e75b91898b4f6ac9bd2d45e7918f86c1d7710b9962f073918b81cda3cc614e2e59ffc0bfca5b81df787ad1758572ba9102abf4ac8bbc0db646d568664fb0f2eb01279d39991c5ca630b7c6848ef4a1139ba1c2df0398819d2d55b0c969305a6a37a85f86e97f4c7021f3bf4f412e268b57f054e74453bb22599f756603a7d14ddd2a3c5ecabc05f86cc1f8fbc2d7a2af0a17d9ca38d8e4a35660a9c764485862cbd9791023f2ea69744b26adea3c0e771ed10620e728c7128f0b9a37ceb90374a8c3fa169b70ee1b61398387137b847d28e6c02ff96b3a5dbe4182f6702db917ba61b8d36f95e02173ff2d89d13f2344ae0f3b7a6ee6fbc4d93c0f8a47493d7a663b848e0d6fec32af856ec8fc08e65fdb823fd286d23705adf922b7dfc6c11b875fd97901c54599408acc6d49a1c43ba8c0f81b50d59ad69760184c0c4fefc90e3df90c7ec0204810f3cc7faeb95bfe2d805000293d42a7ff7d7378d5d801f303177282126bb10a2018bd8a8e46ae42242e1e0c05030180a0381c06037be3593160000000c180d85d2340d34c176071400033b322c3e2420121c120c1810160e0c0c0c0c0608068482402018000604c240301818c493d1ba06de9514db58cd16dcfabd95d3eaddd28c5d84376ef3d1e3b9bcb6ac5e0f606e1fd8a2c511771fc97706f0c30c1d4a7cd1468d0b6a28d1c484c9b7712ddd33b6e90bebaf5ab25d45a468636ae64976f9c918e45043a3a2007a92372148d92eaeda4ce51305f8d8ae815e59ca75ebb6525958c1ef3d03b17ffc0cf14752b2d48e9078cdfa85dbe8c42065cddf8a2bbfd283a97105b1bb939b186c0f779bd66a81088618b1898a2a6ef49216dbb4b435a202bae2ee765c89d435591fc7ca987177cd038e50269cf55a4d3c0c2816fc1c0d9294b5ca28a8091379954c1eaf1cd07d64297ebceebb705facc3e881a20743fd8738538dc3c79dc13cb45ce6d0a05da635f6c96c224a70df5421956b07e60cf1847d456fe6eb9133ba61a38d1b7b638efb1a5f49f7b1a689fdd4a3870c87d28e0935b59e746b9011c280982aa54275246a4e532ace09d86ddf387874d2d98703eb2eeadc935737befbc08a435040372e1f6e3a41bef989d04913694e2c87840d719911ba192af553bd994d007abdf6f4f1c34350573e6c5092b7970f8fbabe72e5dc31bbe4214016a533090a441d499328134c5ed7d9530797b6bb1a2a208270d954440e1fb64c46026dc774845d2a7d5426bdba2b1fe5258a78edccd18147174edf3d7770e7cc0127a3e7a401215e7779e0d17d860b099ae2e038ed8e6dccc4b7c778647a7e5289e6834549244a34c184d023484f92c4299f7878eefae99bf36e1d1237c74a428502688abbf0e88443e76e4edd3d7b76fddcd36397cc3d5610bc217ecb62ce6326b042cb13467aa49bbb3c35c8d1b2358a358a1472cba756c41174152db13bbf7c7cf3c6a36737a71e3813d4e725fea23dac51d1e7387fca5e122a817e0930be64843670834f1d7077efe0bed393f78416903fee0d777cef712b09d2919c6cfa642745069504e924a7a17dcdc1324899122b3afceed9b3dba7cf9cbbece8dac98dbdabe0aef5b8c09972c708b67cd69025f14653422ab727126a4d9ce6f8c8938fbfa9aa1d94c1cf6cda5002192ad4d0491023eb324d8f633bcdf0e0ad540af9cda14cb3063a56b2798d255b4081f0844a5f182eeee529d920460339959c4d43339ec3ba305d7affc831d720064cf6ac72f7c491b7f7ae9f3eb877eab0d6578942b0be38be79e7f0facc8c09b9949d3b758e810ed1af77949969d1442ba10495c4ed6a68219498c53cae555165319b6da6f5274e9ce2559d24330c1b08218ff4fc12bfb5be35b5cbdd93c6f013b90c4bab836879a6051b23784e718107946bc5ff83c3dd7efff29a0d94582d2ecd6d37177d0751e71e47f1f1eb8b07dcbdbdaa4932e971f2ac5cfaacd1dd75c7ce5b4b341d0268153dbc940c92085049c63acd9dd387ee9c3e906366cc125dea3662a14e8c7f8a9e464774ce84829bfdb571b86c9ecd523d596f3bbc7becf6f535e78e5d9d7620673b7a059c81dfbd75de3cfa08c9dc329be859582584ac930066b488e5c4a2018b84f4f8fdd91e0bc2778b3a77e8e0f447876e4e9d3dbdecf8c5c3fbce4fdf9c7add8ee39bf7cdc51b4f5a61419a7fb008f0b56783d70a8c7ed249907f017e14d2489a32f9347e45ad0e4f1ef7d095a383f74e38be350f217ad491a44a76ad534777ce9d3cbbc6f9e5b93b470e2ca774e6fcca933b0737234c843632c91c5b8392fdd40d87ae9999016ed7410a59a448d4c933fc1957f3f7ce6f1cb9efeeaaa3937b270fee9199a359f73812b45437ce3abc76c264faf5a76e9cdf6b9274ef8cf14898681811adf62d7ea6b68f25ceeb9f01f4cc9b53141e36d721b351e369362223fd7943851141c889e369a6a7686d1a37ce6d66f35d48f2dcb55307b97102dc838594c7270edc7675d9d9c9b5b3e70e5c7f7a8d499a09134f9d442448a490247dc2f4274f964639470843f0898c60c34c680b7d54e5a08e5cb28b6ca02929f5a633860f1d12b40c4f48a7b8995da43f15fac74edd3b7ff3d0c1c519e7675d38f91a52fb67e20e419877c50006ec6c9d98ce3dc720c16546b8d070cc61449db3d3eab5b44516b350eb8d77615d1526ab9874b1315dd4617a3ab60895965b5ddcbebde7e8d4cdf9cfce2e39bb7b681ffaa4b7271d1fb78058dea95b27e72ecf3b71fae2b98b1336d29fba71de6cca8533f78ce1829028dde44a9dbbeee8c2d1cd9366f8a73c7b76f9f98563b62121c249ebf0ec1ac777cfee30c4fcb037497b9f067acc865ae1dc6c6ad26d5d3a35e898d0e225611b21129e3426d0b36590d910070a94fc6a522d1ae6d032cdf07755245480a199edb52a15c7582e4475e6396541fc6ad691165d377f13db231f847e3c0207ee8926e27d24b684f4315f3a5d40d3ac37d2312b0104e32d3048d731b75c9ed484da8179234c3a61901e616072f36f530b1a09fa97dc50940928afe39f14c728cd943eb19c77e70866e198c6a6e8f8e7048eddcbc22d038fc433a00e004c87884488627d3dc98f195e28bc96819c850a5560e5f605b53feb0f0e91ff9a1ac354e1f0453899253e1d9c085bf8e4aa8588c7bc0b98880d46d5d062097ff8d2e35e9dfdb53108545281bfac16e14ff86a1992ff4e17b5c574a798bd1ebce18611c7b452ed0c9a4a711026fcd1205e94067b1510907d0b7ee7a845fae81fcb7110b948db36c94c638205de06625225f4a8033b048aff6ec689590168f522513b8a4aa05984e91cf209013270abb2357ee3b79acb49304a6236acaff154e7cb8e39d9ccd6baa8f4b41744550373c14e8e932cc63cc42885f2398c12c0d870be215debd4a9ca16e6d74cb101360551ec482dee3481d412f033fc17ec8515e59117b4aac04bf8d33a7c4d6f007839def0d289145742f128013566654cdede54a23389289aa58667515da82854e195facda491db4fda1dbea95bc4113c15ae4c5136f86343dc1d7df81cba9c6d61e0a34ab68c0a376f485e988bc521b442cf4cfdba458a6cceba78abcccdccde708785375a6db61dc2e48773f08a1fe315ce6744a2ee1400b793b122e474970eb11d7e0098a37218a4f4312a6c12339f07891e10872e1779d0de3fd7fbcfe4145bcac9620c04bcf3295ceed13fa42de23231a18e732450148903a5ac0ebff0c08053fbea353893946e6b3cc4b8c33475120afa14b6f64241b8c90b65c84b44fe90e916204e2d979b3bafaa59a7c5df5d08309a7eaf86b02a2cfbf41f588d65139f8be5dbc3230d50831837d65b1dd49c91798eb2353c80aeb8e71087113589db0714215f1d1e67ee54c4dc8f6447a4d1f0d9ee8185e6a99e30b5e49397e7337c708a4d2c50945da25593ca99ab4758385608b67bddc087b47fa4c421a83606779ce39ed0e94ae91ccaca959063300f1fa4a7cba8a224803d915cdecbcc00b84515147029bfe6b7f5b023d8c20db56aa90678662cebed044083e9ba5f16d50c09e12ba91aac8d68b157a98a292e99d77432ed2302fa138e03dabdf5d00d4d465db9bfdc424b27cdffa014b80999137ee4770d4900ee4fdd0673b57f147f6f552ad4b87730780714812a0a1648edde55d2c1b7cfb9a29c7a70c66a141ea681eeaaedc7292d00a7f67137bf3e3d87ed06300ffc770652c9435eb72cb00b4380bd5c0eaec62da08df1f303278bb9a66c5cdc6476229f135f3cf337cb08041a6f3a8da2690603cfb49bfda52a4c4a60479ad9e258b81d034a5ee177c1983c819e78cc5e345749de13d79243ebe271c14a01be909d7d5bf75784c31205ece6c966a4dc0076a44070b31bcc72f1816e4857150615d7de68a3f420cb3e58a29840102e1671937e11da73885b89f6c5192f0473f75641da416d3e1b1a289c028a1b550d18cb391f30685c0622dc1671f6995205a861cec29afd6d49f02ec8c00688f4a178dbeb9b9568e4beeca50a946ead82dc59c3c52f8f6b59c9fd08ec991d6ab500bc2382aaa47272267fcd55266cd5a77accf28f220d9ef287003df79bd3cc32f0aae9d9f31bc35e69350a83ece6941669ea4346b07006ad90111d371ae9d2fa3fd204019987181c4bb89b020006668033f0b7777b58d01007c05f0420a48c2141c5183ab142b5f51c47a7873617c9df14901052872e5b19b6775b2b06f05fa7100e3e3c31e9f4a48be41acae6a451984f8c2c5100d0c1c372da0b4021007d923833585e6a8c1e9be263de024aa478320b002e978283d073dc58253602bf1252517b813e4da907e59e549dad232f3a1a4d395ef6bf975a0e83672c01f9b4615bb610b4752a9c441136a2f7d642140e69314ff6498e3a6bc77688e3f30e0d4ee5710fa61f61f6cf19388040ff91bda50666800c578f785fe4fd69087dfb1d50c44a8303529dee83a144cc668aa0ae5ab401407d94cafae342c013d32216c6b48882f70c5aa9a3713d132da8881ad539d5ff66d079d58c3d6381c1b0695aed9ee22b42350eb7f8e47ec501deff1cc0eda83b0c1e20291367e2a54effbbdb69df585a37d1df8590e32c3a6ce7e42286a940385c0ff0faf6d1e940eae5e53912eb9ee7c3584b44cc60f1930c9da0ee94e8333df7c7d105fa2b14a1f13d4822d69e9fc2b1440a1d2f21a3c01ee476a407de062e35ac139fdc76f682d9b7c265f7d4ea3785cf46d32e7aa3c5f0e20577ca0968a59914dbdbaa90ef29dcf6a82100abe8e7d17cc8799a0d6535a6d38844be2abb852900817bd451d0c2530a11de0e87d07b0ea35546aae4d269484f0d2bbbdbe73876c070a8a6cf7f89817b38876c9a95e054f5b0f4376e9611b7cc3d289ccd4660016c3b1466b567d1ca3393507b1deb075cba4588445942d676d283aefbe3c26bae958c08450f2496160f97b01fddfb8630fb333bc2fdba2e89d9d0c1e2d3f52f51c5f1f07ec550703d696594c6ef8065d8a516a645ea42da8e9d26b18efc41be48e19664cfac553b35ac1d22e8af9c0b37209ce73587b8f430da485c7a00d1a8e83a8a98189c342372cf6ed8c2cd542171e8aca047c606e4a63c4edefdc31105a50b52ce2d696007be80b60d0b87c39fcd57c0db799b52e536ddaeab45e20c7bd8c9c525bff5cb4b2d6699af31e538eac01adbc88420f892e7d24cb25cfbe65512331ec2ebd02f925981ba25a54ef72638c132425fa2aef98568de8c34ce362323b6b7dadbd123a26328f42a67ff3b34005962038c44a11602be0947cf0481c4d0f862a564fcd41fc6786a1c2f46aecd135a034c11539e5d8b5494c166ed4924bae9f025c3776a3709c55f094a2b753d01663dca1fd54384f02dcd2a687de216c828b4240a671e5ad2d154abfead23bd211cdb160d7c5b959b2e5bc549f355a96cc5b01b45c4ca204d0eeccb1b8fd95201b8bc4e0f10bada9e0ac25d6a2331fb31c7dbfd2d391ece50a05e397285d5b2b0d12646bee054c5d7b592181afd64cd5317987dcc110a0bca066f8b689c7b72e8ed9ecdbb56cb208dae4c4d43fe6c50045f13360f41f042de0cab7bc64df9158b8152b11560e2608ac216d9da582a30a31d48ec61e65be169053c8fb72a2f92a4cce0e2cbedae73ef35a9cf732893abbe5dddc6341b09a794e7660a2199a5ad5649e8c80d072972614865fde8c02eba4c2377b21e68161806c17333d41aec9ba4606fc2e2f4932d9a60e58c5265b674f12c1ddee63d3402e313215b370e58cc0044e4be9a1feb805e6f72742642803905accc01b8766116668073263cb82a21355e8908c06a798711d9ffc961b2d08f18c77699ed3363c474fecd9a81999af865745f896bddd8d1b0a2e9404113a84d04759739616118b420e841450496485907ebc9552896fb3a2ae1c9fa1b52b66813f39a37bf82d22e0bdd1561b2502238c7e69a6bb4fc482d021197fbf5a259a0d41b29c1c82b0b4e9e9185b84f65a50081bf436b541454b9de23e5736d6961d1ea008b6e71fd7ad4ed8476a481f038b275a3288eac375cde5026dd9309725a6f342bfc2fba982038a4ff8139ee0580f9ed0c3448a1ebde4b0f0fc2ea94ecd758faa4cef4e25468f1f9560181be89bc79fffb4bdca91242362eeb229e64d806da634909ca842bba29fe2b60e7318c97dcd058c36734dce8687094e060a5cdedb667b38d505521f45498eb7393055901276169b0382dd7e5a2c32a64e9bb6354c96c651466d8d714c22477b8ae057b1eb02f73ecf343acef7668e79638cdbe9afda34820116378c0842a6d378bf8c60c33fa5e13536abc895684ce9ebc1e775d8d41b8157c6261acab0725846d2e6d6c71b301f2a3bf213bf885b358074633a442b51516436a5ed64e4957437548e8f29f7e2e185395b84ab67ed1d93b99e38e6d10a11b99e752b7eb9c76ff799e6cffc121785e7271704b3006a112c15067694c193520295b7e926dd738ad353bc5f3396620b3b868e3024152114ec19d2a6bd99f2f620fb3fb91676ba448a0a48c7d4f45b9967a425e6d54fd571de3e71ac6581cf30e3e01c82c3c769fdaa122aa82477b6bf55ae10187504ebd262124152f1bedf84c4a8de2c9fe3c7c0a25c13bd1707a664f53b1ef8119199d275e8e132a06adb928119960150a245567a69ef11891400fd001a396296ce8c8bcf00ba09e7874dd4337904a99cb74ef2429ecce7120777584c36caf020b594929906f6615189b9b0ea7f1bc1d102870566049faeade8d901a27c1e81309809499b336c9cb91ada67bf9649117db321683af1608fced281c4ac0a39a887d4a426b82a008a374f7cd92a8f8fc9040ccda4eec63cd080f0e0c0f675c741ac8f29deec08fc9a7068296e5683cbad9c8cd48ae62fa0f88806d2a497871bf2d304aa3e8d586bb99cb468b37219868f4cdab06e40669930dfda328c5ba93e652a292be7043746772501d8011c7178a5f91005c57cdd71a20d7163162f768b13ce18a0ca97b5fc8edb7f4d48c9bf8492c3fa9d691b3e9edd24187491ad00b898857927b75f417489523997e3758dd3e329fdd581c851ef7dd4119af25126347184d704f31322d103e5f8c6ad316ff07e10639912417b365110f7d6b0ce6d8c136bc6700adda71893566c4a0c2ca84118b598bc581298f5517ce10f5d5b5cec7980f4c2c0c11d85b991d682e27154960b860ca61cc65d981bbb8a87e8ef98389815931c814cf868e480c239ab566c3a9299de5a6c92f3302862d34451cc20c3fe4748e65e36c26ae116ea582f7e40508d093838e2788486e499028d504d3428f9a2449a4273779e2d4224125411a12934d9feca4c8a09040aa18d13338c281ccd34b9ef7848d4e0a741a46877566ec49858597e9fccb0925afe82778940ef93cbb0304460ffa605fe7289512f5d509c0b6c291407c22689817b27e1bc2d4c61b427e0acd70e4a974445d9d4aad06dd05c414692fc8fad1ca1533e76cea8ff8fa4b84a669377d9db321fee05d310647e484cd4bd754098ff55ed8fcd8215c6ccd0fdcf94bf628a154da105ea710ea9ee2a3f34842df2d236a9ae0ee7eb7ab62382f852a2b765f34e33cb94e7ad00b5ca312abd7e0dedf35ed497f32469949167f22bb5aa1dd1e8ccb3660cb2b48d0d667d49bf309cf66d026b71c1ce1c635c840959a6a682338ba5b230db024637d7b5f74dee6d3f5101fd98caf0c256d5c5623804a0835c149624b5b589600cff67e4d561167ba5e3afb06ed46066501096e63e8d7b16e940f00618c51b2bfe61b566550ece5dd330ea8ee529ab85aa8a69f27cfbf31013c586e0b6dbea680a90003b7939b47f4761d36f67cbe922be9ba5481727ce0d5807b2776972ec71fd123f26db02a29aec1a15ddde7b4db0abb125d5eb8705e91cab02a6312a2c4806802a2324ac5bb693275a015d1862db9b7b623173c6760b34c38f8c5ca725532820710576525ff11a73b71008556082cb7bdccb59844155bb980a821c1e511bd3277de14108780eed5695861352dbd5deb0d45e4c40b445adff00120705d801d901301d88a28697bbb4f82bf158a02de10f0e8e3e9735c6c94df592f4613785eac5becd98b05fa0cf5e5b515b592949db22d7fa5014cf65a7fddc37f910672fe694efe4f5f6f341f9e3c0fa1fa793645ade3cf6c5deb5cedcbcae11470adc4c2363d2302c2eebfa5f7d343056b5a45f26547b93dd9e23682b575b55453f86c957f8efbdb0a187c50c26a4e62516557099805249c2609b662fb68512b3cf831dc212573904a14e238daa06c02a0a43a30136f6a6dcad205b8d40305b0b683127fa23f41aacf30643a763f9cba541990b6661bde4903870260ad6816f3f6211fdc761f26205a7045ce02ee9ef93a2ac0b56f4523fe15f9a41111b86fb0e9156cba4cd33005dea7f379bab0ef2083cdbda90925db4d43dd86db1fc5b4682405762066291a061948b36c2e7b806d8c6024058770999ad0379f2dec52a143b5ff144f1b37fb287943c48f4e8c5a94625ae48355649a803e7cb49536ebe8af5fc3f7725127dd25d5db38ec80d3b3712105d6a926337cd3c625dfdd706d7304f9946a6d815540671e55230a01c39d7ca853f4caa8825393a89876cdbfbccfe63a93f3ecce4a4bec1515b765b7aee8988e8774785cc18df362d9aab9ec596e825e9d269efa8389858f142ed4b01f862422b316f385e5205c47a2a19d77a569fc63d95e40dd870c8fd87758c03f2156776bb0bd7c076080e2ca187375da4f1c5d7986f231b1723d07f8c31561e76495d2d7e0d3276ed0ecaf5f1038e6b4ac4d05031e1ac05b1a0c2889dba70b07b499d1366d5696eb084115d17002839631235ad5536365666dc76061b49462e6724411513da426d706ac2d94c925c7390026f0aa52bbab023d3f854f923754343c44b102fd230847c6c7e33c1cafcb80b61b5303060df49096dd224ec0917c821c8218f3c9689d2dce4d359338e4061c9ad05f09487e145fee02e372d1c8d6441484a8466cba454c60b45693eb36493e14882f0075ec392b40f8fcd2ef7eacb7208d1543dff8c61e806ee04208f596d647df1c92f0d91151eacc2c3c63834934d09b73ea6d2ec9e97a8d696105c71031a4c8e934d399ebebb2576240e673d6c9ed36fbc604bcd2c123162096051a1345b992535d9bf015af4f6085aa662854dd28efcba599ba94459303c2e421de6b93edae2a2ff5bc5bf66b03c4719b3df82d4f6f3ec16a9df894e836953882c8ca2e8949e31ad17584255a1145d4f6f7d68bee4e3c2588022bc070618474128b7e29c6047610b1994d7dabeb713a07d868d047f471862c2c46e4ea7aee32bb41ca8ee72501f42a1ff82862237511671fb2a006deabda92925fba09e25829883a1ba19f2e7a38acbaaac4886da548ccac63f57a15323a454b0d6c15d1a42bc7975033b87236d53e7f404a869a78eb89a4443e176bdaf7c7ce501953043603d8ad17892d609636f889515d5465372041c9977634cdc3d3fde6e013163c05f5401f096a2a80403fa55b6427800329f1d72896445d80b218c191079a76859d2245ac4d4a1e8dfce89162c1590aab01e84ba3ecc961902d7f69e7c68e881958df474a410fc00b86f95b4ef4367ca50d018e9a6740ff19ec09929624c2b0ae195ef94f9fcb258143ca1c533c502d33693a3505e2e84d3369df647e1a871ecf7d61bdbd646e570b03710421a43a1b727bd47da66476b208fc5d494a9f569319f5a56b8817e7d53e16a2635e48c9edcf8dc65f828c2d92bbcc87fc5a2384874e5e12b69c5389b7d503abbbed981321c8b7328593a9cc1f20bc5fb5a2b92f4e365c73f57921a3263a0109851944ef996fc2e2f79e57a3a786f35a7cec2583be413b8931da98970703b2f539597940f0ca016a74c48753d53bae942f37cbf614bb8834eb09ad37c7311f449f3ef331ee6e9eff931a1db26ef98aed026ff4c8011e25a5960f8ebae9b45202ae2c0241222aff11fc04518563943edfa68d76171c648450c313d0542daec89daaad0ba62d3615dec018259d46bc5aca668288577ae4aa0ef9944f962fd04bb1d7624e0a85434937fa16526d2b8ccf4bac20425a33cec72dcc6278c99adab1682af7cb0e41a24d905dbff7eb01b32b986f932aee7c323da02115893fc758bdca7fa5203f7a376c4840b8369bdd2df9c220cc541f146629ad23025cfa26b984e330cd798295fe90744c06710498a3ef2ef3265fed393cce2b7615d050309feda65ec3a35a1250dcc7c4ad210695b2d4360047abd7140a5a0a7dd812b196a1bd6775152c45029e2ec4a60a3960b46439330f0f0f0f0f0f0f0fafa18d90da483b8424c8946412af76dceaa5197557a694a44c2909ce4c7cd91dbc8377206b6467c4a7934509620a2f0bae0a5d7e5612c6274e5153dbf75f7c99ac270eba2246549377299b979d38485121457b98fa87cc685891030c4e9c949c58150de7a6e3927480b189e395a9a559887532e66634ec03c806189a38ca8a8d980d977d3fb3a0616ba5071899385f040d3959ae4745048f031347ff0f95d4f9d550f382b9a0e15dbc1e1897388ce44a2d6ab63203862556ef5049c9468c8d2d6c201a364200a31296685132cf48b26994408b74564a066050e2145537cba5b1c352bc5cdb9916729cbe861a0d05018c491cf3d594f0eb1c592ff130004312a7494253df09bf60012312c7ebb3935a71439e3941e224d17dd734a8304a591fe8c2c6164b01188f38977ab9141bcd1a6a8e38570c32cfebaebb0bc068c4294951c24ca420b62dd6502bc133e29c737bc9da33b9db4c329108c058c4b1532e62aebad450db82b9d002bde315301471ba8d1199c9235c84bf869a3d0046224ee611d6efb4ad2815d650a311827f2faa0276efe7001888389d8eab4d51b5a4ff431ce732e5b4a410ca5490214e3232d585388dc65512d9bc2caef850020c429cc7fbd5a25ff6a6880ce26c4a95ce74997f477e8238e91eb30c9d6c6a9436e363c69a0046208e7bb2499a0a237e210910a7f0962b7cbdf504187f386a285331c1477e38e6899d2482b4eab7511f7a4d21649c58623e9c7e938ca92184d290e73d1c946ab694b35b627bd6c371d56f73fb8dd5ee9c8753ae1e4bf99f9d96331e4e4a4c7adde8fb1d0e26cf23e6fbbd9453b5c321e5dfdf87cc3721cf3a9c3425c85a1e8d913b4287b3e4891584a65c26229cc3514d5facd80be570c863b2f15a412be671389d929b3c83d01be4342c3e667c34e0238b2b262043468e40868c1cc1192dc080c329cfc45506cdb5f90d0799eb340b6de3a2bae1d89a215ab8943c93eada705229236224c9d870ca9d3196898fb469b6351cc534c5966c25415fa98653e65049684a0d49229386a34fbe0849de050dc78d9727d4522b5406912f609ce1a4a2654d5a3c821e13491c30cc70cc62faf462927b3ac31a38b8011f33ca68d88d77fe1aa9850518653858fe51f54ada94a9b5e0d8c20b2eb4e00206198ee329a72a5c87f69071f00df602c6180e7f62aaa1f67f23070db32386030081802186939688997b5bf14f4f6138650491a476392f520e86f39799a5ca5c3891375fc0f7626cbc709c9c6717f53798b0b50b8768f147438dc98593e8bbee8bb316df2d5b387ce5da57bf65fc9668e1e027f24bdbc6d6f62c9cf5ecc664ee35d98e20049b87808185638989b9d12ec913697485c386b9d18d642148535be16426c2a2ebec894bb80a07512526869b9d974d8563c917b1ab396137de291ccc23a920f39f94e0a714ceb3a243f66d46cd3289c2a9ed2bf67b8974aa7450387f5cf68d24d913ce9a4e5c879297c1f3aaa1368210d048fcc43000817201c8030c279c456ebab05d89fa736ac2416c92a0fde2c484a3c8b352a64e5d25f92fe1d4f9a54509afe81764251caa5dea92fa2609c749238259cea96d6f9170d098c9a13d312feae50887b8d6657e6aee7b538c70b62cdd8a672292e94a114ed935a5b89908a7fc9f5fd2f327e84a86703689e633daae57260ae17c29e5bf8a77eea7bd201cf2cf7dcfc87658de8070da5cb67719edc7de82f18363a5d04ce7235f77be0fce1b438ae297ed1e9cb252388d5ff72563cc8363b01a11379ba129da1d9c33e84a9baf411d1c57a4e60aab961c1c44be942ca892903d3d1c9ccdae94e685b234767900c60dce93fbf6349e12fb7fb2c1393628bb58a6279b08d5e02c26262692086d23453438e8502327cd058b1e9b191c4c48bc50fbb1517d95c12957b8f8ab92a6e1a23138a4175d1e322c3038e9c875994bd246d2ef05e753718236cffc1d4a5643ed3fe04fa39c0d182e384c90fdbba067bd645a708a184a4bac0a0c169c5636baa9b1f82acedb13fdadc4d37e47aa38056da6749b48d779e1541c2fcaa5f83b5fbb29898a7305f5216da2ef87aa9ce2a0fd4385983dc37655a6385b88ef97e2945c5e677c7d529c3e6f444831eae58d3e8a4310daa4968590b07d8ae2d473a272855852362ea138697c6658d0cf9431041407d9c917e26ec9698a9f38b6ada776c5ca1307d917f355c62a624c278e1e692b65ca262c9d8913e79131987c7b869f7e13c7b653ed12322f4df29a3828dbcc99bffefcf24c9c228e06adb334264e42b7a4a8fbf3258eeaa384c7697c4d3f5be218a2a9c6fa7d2b71d211bc4fc64a54b19712c7cc3e4bd72c67e9ce491c36485b0b0f91b4dc298953f76cdbeae4d2783312c7fe7feb519599549b90386493d72c299adc51e6238c71bdd61435c4116799a8a6f9da24a450234e715b6335dc6710f731e2acf5f2932af6a6642ee27c2a6b9226466b508b220e96367c75c66dec0f7441e33fe0e7b7f02eb80165481040224e6ad3b5e948a2174f0120e278eded1fe726b346c386af0704708883be3bb56fe32e7bb12df873e0a011822dbee862e6de6d744104608893c9092e12d444c9badae01268910c737c1181126891300034104021ce3722a620be4d1c1000218ea3b2355f3e6d947635d4f60b0d086010071d93d8a32c28791909e26476596fd35f6b6b62122080401cde4f6b4e9a98dd1702c431bd6bda7f478c1163fe70086ae36662b44831b37ec04d8f188d23210610401fcedfba26735c491dcbf3e1581d2ac96fd3b29efd1ecebea1a331d4480f47f58cab3659ed5d64f270d09c20ee93eb4d36888773ea46481627b488ebee703e4bc9648a9031400076386cd6e01f62d6e170fe9237f8a620b582e8702aad48e21374d94b7a0ea73c3a8345d68c31822c87d30615ae614a5ad4d7c4e17c21bd7e88d312f15b381cc4e4db7d6e1a794af28653b6a0f2a6fef90b3a74c329fba91877662405ef04d08663521f191776574c576c38dfa92ced21396428ade1985a1a5f456d2bef6a38c89455d1083a4cc8d370f22a7161d2e44d66a3e1b8c9f54cdbae2ed26738498e244c5614bda349663845331d7df9082aa4a90c272547f95f881e190ee2e96e1674dfe998c67032b913af6c92761b93188e3fc2e7c26e27ac98c270dc9798134524cded12180ec93c46fe6a9aec2b7de190cc36c52a8b1bcb7be198f6ce72a6b01db1efc2e9f287d6ddce85635a7ebfdcb61223dfc231f4e289bdaf2469d7c2b1769209d32ec15ccfc2512f95509bf6f6478485f388307243d63d5bbec2210565ca54257527e3b7c251ec5764d4f4ab704a3e9752cc57316b792a9c54843b9942c5464a7e0ac77fd1155dfd99d92485631212b4a4a047e1745b32560aebb17443e16c65bf296d2bcbc44f380611997d6eeabb66271c62ca96922aa937e128baefdfe3cf2d93ce84f3a5acaec154be84a36625b9a9527a4d952be1dc16a92d6782c8dc51128e495d1e19564aff8810120e4a49772bd1267e54d0114ef14f9b983416b48dc808e717597f76a992e91015e1a0f233fc6c564f9321221cc44faeaed62071ff1ec2b1d449185b11429fbc8570cc1511bd2d97874a76100e49e48c280d2daa5a03e198fe62ca67f95664fe07a7b2115142aeb5b4b00f0e31f65ac85992ced41e9c3de304ff92a4e62ccc83d35eccaf31e8f65215dec12924aa5a74938d90631de0e69dd9926c9c8353fede4ad413cfbc180767d3d02e226688088b6f701e716a84d8c7feff3638c98914fb746d4a3daac1416bfe081a432fa30805ceee88ace15c69a7bb2aab6380871a4e32c8903645a9f8999d86b356ecfd17ab4965ab071a0e9752241567458ee6ca190edf9b3526192fd89c40e161867368d5dc9b6cec3bcec16e6e20161ca011804c7894e170192e24b75a501f298107190e2a628839c278394af039bab031a390041e6338693c33a12ecc89597e080f319c427875c9f50ecd75ea834718cefbe11b638f96a825bb8307188e32774248485a499cec0ba7d899921eed227c4798070f2f9c7c6edf4f6daa281a77e12442bebd72176ddbe171e1a816a772b8445d93bb1b3cb6709e64316752764cc8a40f786881517726e45610ad520db5c32ff6230b067ca08b0ac890f1376c54a08e6bcce01a3364c8e01a886b201ade05dff0c8c2f1c2dba46c3fd6d02e6cd0489d8207168ebb9da9e773a4068f2b1ccfa2d987fd4912daf9dfe01a38f23dac70de517a825b280b7de11a6a9fc3c60868e0e01a6bc3a30a278b166a55256d64996aa871f1c50d1b5f88208f70e1410563c50d172cc409d6dfd0c20b4614784ce194f13766c8d3dbb2be865a073ca470f6f6fc92383ab9281c53c85d5b4f99fc1a34080f281c74ab558af9b58c372b203c9e70d26ba27d4a69dfd5fcece0e184b4a67cd59a1b6ec2a9feb4474e5ac634ca82060f261c7deb92cde45259c2d1e6eeeee2a8f48dbf124e69f9336ed0162fc4cb47c0cae0918473d8eba83799e812c25cf04042d9165696d99210c2820334bef038c2d1a22865ea2f55fcd53eb2608006687818e1e4ae775a64299965b72458708046043c8a70cca6e9de5e7d4484b986da0d1b39b4e872430239b448800c1939b4682c3840030b0f229ccc4559fb87b2865a0e2d1a6d011e43386484ab79d6c7f6a4861a8eefc2068d3209202db6b0400e2d1260c71e42d0fc4a7a88dd90e5e0f7e26fa404cc8f2087161af008c2c9455c0669ba94ae940081cdb8a523c2e307e7b858a6444c90114d7d70ea4865631543479d55a885f0e8c1317885cdb4298dd6bfca070f1e1c458548298968e789a03d78ece05c297aef621a5563af76e1a18363ca095d1792da13a99483535808b69947c490f7ff076e7ce0c647a040c00307a7f417b19bedf2d285183078dce058215cf4124b93c93362f0b0c121977a1ba5e25a265fbce05183939d67d22ef1afb839390f1a9cf2e7f4569c7c661bafa1863ef098c149b2564ada6acad662060e1e323824b1303b3289a7ab9a85b13678c4e0b8a3a44aaafc1be242dee00183f3da48ab7bff8ce0fe0b8ec9c243b3286d5d705cd3792d21befb7e6cf068c149988aa8070b8e96dfa352f3cc551ce75b540a4267658c93183a5471d8152192253fa93a261c07828e549c92baaca3dd465c4eab861a1769853b5071da3b15b4ac925788f31447dd202208ad2979a638681df9fa23161bfe183a4a71b64a67d71aee7e7559411b1da438785aadb9a9fa31e3c3d9c61626e0097c6471c5c7651c3a46714ac234e809a5f7249a5c8310d44087284edf554aa43d150ffb1ad0118a93d01052eb689712b382e220428a9133053159177fe298a4d68e4c2f71df27c6130793d1441aa5d23b434727cea3eba5cc57333e6d4e1c52457bbfd3e9173a36715c9fa0a26eae53e7996a02e1ae72298292944c9ce2c98adea544fec919828973f986f09a2e9e5c142f71ccb420b6b4feac65d01227a9418624966e256b0e2b71f6b1db1853282971f0bc987bcc3456d224d4b07c15492411644c1267d90d32450a264afcde489c246f87b975cdc8e822244eb1542825d72765b25f858e471c27c860fb95c29a94dc5b204717cfc55aa1c31127f5932d6b9730a9d0d18843981817b12fe95ff97114469c4b9518755731bf4dbd88f38a30314d3572225f6ca42e1c708a3889d1df52e9eddfb031a32311e7f7b1a0273376f13d441c421e6fcbcc7e9f3b15163a0e71feaa9535b9ab4a491be2984d988ab11e5a5a4b4ee828c4d144cce8a34f2f745236d2123a0871fe92c9f466bcc811d3a60b1d8338ee05d9aa624a759cd008e2fc69bad56f696b260ec47944ddb2bc895ee800c47936c5e7c858b5ec2b0a1d7f38cee54cea299b846e976fe428c1f998f191c3868d0afc1621b8f11148fc57dce022024af4d0e18793bb6f0a61a1324a56169021e306175ca009d27f77f4e1a077c44aca52a3454c7c38e5eaaa3c2aaadb947c0fe73c151725a63e514d8ee074e8e1a454acda18d5b4e593c9c3c957349366381b35193c9c2cc55b49bee81d0ea35637779ad468c60e47135a2d454cc973dfacc3b1728890820a173a1cacd298ba1435ef2599c371f625eba6b92829dadfb06159e890c349347acfeda91a11b538681b4995b5e90a0ee7bef7caa332416732bde11813c3669dd8a6abbc1b4e9b2f840e9544ca8a95c5151f34acb4e1a0445c1c13ba345a92b3e1bca7e74d677e0da7645a1b624a133fb71a8ea3446398c592b0164ac341c6c9a4d46caafc7ba3e1a8b5f9efdc64af8658061a0db3eb38c3215fa5d6241ab5ebc21d66385fcbc4149694f41172194e69c25ebf566234cc28a2830c47d98ed39ab7ddba3763386e88314ad26ec4702cefed48aa52674a360c873d8f6093cf7f33ad6038a972bf0caad42f1c278bcc90cf37355ad40be750936d215e94a851bbf0c5bbe9cdff92960d7470e1683ba6f59362082d972d9c3e4bac8a522a36c7440ba7140b136df375cf255938a94c1b84668d60e1209216e1e67e622c835738d68f0ccf542366295ae1ac312bdbac99f8e6b00aadf59ba4af8bab630dfdc8e20a2f70987650e1a041c6c6929762a59ec24934259ac8d923437b5238bfb589c94ba579f451385e6fcf5a5c9c95170a07bfb093dd5f31a0e30995bc0ca7a76571e38f191f3264e0c861630b1c5b870e279cb4c94b9bb4f5f2437c3a9a70b4131b359923d4cd4c388750692bae4974886509073fa54dac970a426e5009a7f0aa15dde49ea64ec2a926799bb0b5cc52de133a9070b40a2a4919113ec2d144662f17e1d6b1970930a1c308c730951d1a4246091d4538bfc66d536d9d6ff2255ce82082a2eff296502a47a476e818c241d89f9a51cb5359f7113a84702a51d33241565d7077c6474de063c64749e063c64761f131e3a322f031e3a3aef898f15110f898f1510ff898c0151f6ee808c2f154239dc58d191d403897a891652b412925b316f8985103073760cf0be6020d161ca0e1808e1f9c2fea294d2762941dbf865ae6a1c307e70849a208a01102c4bdc5169c6430eae8c151c279af5c467bdf041f021a38de6d74a1f7850464c8a881831b500307d7b8e28ad7e2461657c89071e365c868d74774f0007926b3e7b58c080e3a7670de903535368514db3f2db8d0e28a8f191f58d498f1376ce4b040123a74700c37a633ae26639e59430d87163264e0c0f137f2068d2d3870a4a02307075da5a9777295265836860e1c1c766c7d53c9480d35bb0b74dce0f81b36c38990a2e9d372a0c30687d45f15825c10bae1770570f1b1800a7cd8a0a306a7ab79b11629136dff1a6a67775774d0e014e76cd2bac6cd7eda7761c3ee011d33386b0cf18da7b5c64cc6043772acda62c1011a05e890c1e15bde42bc2077ee26171f5930e00301bec516cc850564c8409342470c4e4ac577f3957c04db0fa1d00183a35c9ee02ad9572ea88968e878c12158058d5177c1d0e18293340b218feecab030de163a5a707a11f194fe45cb2aedc70c1ad6c182a39c1c25f267f22f1f59306089e961c1011a00f8028c551c54e90d1af77fa523a98002ae00431567977c714b49f8df2ca9a15664c830978af3bb66d9d3a8da692d0dd6e3dfc507b4b0c01460a0e2706f2959b890358ba41463028c539c4ae8d3b9fc9772c85843ad468ed7c483618a8358962057a11cc113d6820b1b5d98600b1b13f85fc01e0e122c004629ce6f695a16b63552fc3880418a83dee58929a55c21867514a7b0cc25e36ce4964b860c18a238869cd877abc965e32b0618a1f0b7bb5e94bd4d8cd601800f304071f64ce51ae96e6d9ee826fc34e58815ceada5ee728cc7d90a062baa7098a0823e21226aa34e54384c904164a63f8563502337559221242197c2b966df4f5585f44a3272c18a289c548c94444b432a7d3e140eff2f1b54fa3671a57cc259c447c8b1d558fba1134efa679b2e29d56dc18a261c2f4553d2164bc4fa13138e998297ccb6ef124ed6ebebd737a3c4460907514b5d8ba6e2f7c30482154938f9a83497dda02a79e31c36b2f898f1b1c5169c45de07bad022878d11d0e080154838e64852d226c65757c88d1c6507818f191f96052b8e80bc4d61923c5beb3e19a19558df6649ab4a3615218db027aac1b4f29a88f0a9c81cfffb10b014639824b5617121741669e28f8290c30a20a0b343c6483d7957463f682c93d0923399423e20f9c93c57cb92e11e5821744e489c7097632b7890c64ca916e31d98214ce5e7629f90191d9865f36a5836d3a4cdc197ebfd4bce5f650907e88a2e9b221e129237289245d1fcddf669411b64ca6a2c5b9cdca15403448a4c7154478364d613aace5f53b5e70547206d8bfec28697e9c08a191c83e6bf785142a77721e01ce4bff898f1f131c30437fe0b0a7cccf8f898f1618287c0c78c8f1b6feb0573813cb04206479f24ab59b1ac54750678c15ca0e981153138e5dba6a99353e9fa0483b3241133e69674d22afc8253d4d37b199171c129939a4ada33a4056795e09af6da324c6756b0e05c95dafa2fb6600c5c0362ace2e0162b857c21aa388fe6f0dd48c137bc948a83e5d0dd8b7f1911a2a8387758f6b828d974c4f114e7bcdc90bf2bdece2586298eb6224348a737fe09efbd6c01314a71144d1935645d4c95f518a438583c4d5f27770d352f6cdc701a26b891634d70230b1932100e2db84029c418c529c689ea1bdf92ee21238a9354c97fdba74ef67f1fc408c5f96408bde312725c3441714a273625c8abb00a8b7ee2f026aec637a98c295c3c71d4786eebea75e2a445488b91edf5ca7e4e1cb45a8590f5bc4b6acc12626ce2f8397ace267c5cb818690009313471baff1131057595a26a35d4d6beb92d1347cd9c9adae12237885ae0b031014c9ca4a518df228d984a290e70c0071e0b193270c890610224e307312e71941417c9eab45eb610201b5b74d1420c4b9c5e53f6896c4159e8bc1287a4f26bf4ff579a184484189438bc858a2761ec7655d7102fcdee122f44964dd9db5da4b983189238ea8c0ec96f298798192312c749729bda967d279864c880c4c125e88a215974495a7cc429dcf574369bda6ff46f10bb2286238efa36226ca765dc383f2046234efb12b4bad268488d33e29054d7cf4e6e10255e193264c8b8e26306161ca0b182188b38c574ded9e75a3971451c459a49bf6497428e2811c92c1b53d64dea45c459f7729b88f425b2ca87385784bbb4d61ae2907db646e8370b71cc663f4ae556aab610421ca36b8fcefdcd6ca70de220ff82b03d75d9622909e26892d62456967511fa409c47ad2ccbdaa9f4a705c449ac2c25e50cdbadf20f0719b7fb46e8ceac4cf1c361a4e9510fb16261efc34154a40b3973c1e7267c3879a89d5851db12c37b38c4f63719412ef86ff470aa2c2a365c7526c42c0f07192c5f52722f3c1c4ec8d55032bc7e43bec3f9fd5cacc3e254cc163b9ca49d963e13ab21ed8828885187f3c92442a4905b0e2dbaf8020327884187a35a4811ba4a755fc498c3295b9814ad2968d3a395833331e28fc9ac89c36135f4c550a752de6882c3f9d2649c1426415ca76f38e8dc2842e978982c951b8e6159e5f52ae7662578ef420b19327060c1011a3662b481547fb59f25dbb3e13a39d714ee23e2a8d780ce1b512fd5809b927759624b8ca14b037ba2aebb64558c1442c339e389d8956d1a12c2831867386fd2faf6b33166388d58b2b44c7941db329cbd7a4ef7b48b418693c509b3fb4ceb91ff319c326ccae7edc9d3530c07292a3a9bc454743731c270c91d19fffd82c0f0755fd0a2440e7de1705daa4e466d6d1979e12483c964526643f75d3858760996b6612e9c4b56d0a095925851c9b185c36fbc9c1d3967656d2d1c34c657659066e1243be4868493c1c2219b4c7ff15ada4368ae701c931445bb4a2b9c53c2240daf334d2756e1e09e9b23a60952e11ceadb8450425febbd291c2d531022c97092c2612e5bcc33be21fdf63868c890f1ae5138255169a6bde1567d2c3840438b1850e075279ec60c91715f10f013987fddd029e9420b2e9cb06ea4788e269872ebad64aebc5b0d35e7af71966230a136537ea991d2ae1a6aec058e8db10445b25cd84dfab4a86aa8992087175c7c89c04716577c28002f957012b5385ddb2a42732909c790c91d235fada1a697430b2eb4d882468d1c5ebc27128e1ae6e4040bd1f6bb9d11e3088cca1075ad952086114e49aaa51da1496613a5229c4bfbb2e6d4d0203a12e194f46fec68fd8fb55c16c5630ce15426832ef5674ae64912c2796c4536cea659ca3d46104e624c9dde70eb225312100eb925a56d4a963295fe07071513e23f4146cb57f9e0f8223792a94bdb0aeac129a80b353b99761f2f0f4e4205953dc1de812a2ac4a09405d9a0cc1ec4d0c121aa08a11b536e9dd4e7e0a0369a5f62353838c88f535142ba32d10d4e13641257f56dbfabcb0c62d8e098c2ed051339474fd663d4e03896d2abef5757d2eaff16b0183438869aa03317f2dd583e83f37f7a8599d930aa443238f7c4519df2a3189c6265b598a59c21d5078363bafd8c4187fc82b3a49c7f12f33c56171c7305bd0dca92dbbf18a305879467d258b0a83b293158704cd617f4428fab385f5a50a32396103231aa38994bb4794915e2f24ec549e84e102b7751711425ba6d3dd7531c92eabdad8b144648d21407152f987b04ddfa3296e2d421329c503d8d8da22f48710ed3e2f59142f56946719e1421a5944b76a31d8ae2d8be3e7b21188a83525aedcba4a4a83683e22cf2a33a8229adf43e7152a546a45bb798ac7bc0c313c7922b32da27892d912443864727ce73c9de4e2d7325536ec1818386a1771b33b6e0b080c9bfa1587080c6013c38711217597d3345ddb0f526ce21642a1322ef85cc4e13e70c97a99d1f7ea5f732038f4c9c73b643fb2893e4b261e274327d9a9a578b9fb14b1c8498d3741e9aa747b3c4c1629e8d10a3335656e2e027bfb3976a76bb428943ae8ae152cda5332771debcbc41a9fcd520a287240eeb23aaffe2898a9d08de02eb118993122aab48d09b279ebc0c078e4708015e70040c24ce27216e3293652535a8863ee22442a83d11edb327a478510f479c32c4525518690d35921e8d382459618410f71edee6178c38c637136abccee543a7c1c5223a194a735393dfa988c365f4892198897b7ef87ec523111e88f03844d225ba991296459521630b8e36c4c9beb54d89101f29a385385e85cc5952b110e24888639824bcfc358338243913122fe404e111886390674282d99aec9e64f131e3e3021f13b8e2c3031007a1920c1593429031d713b8e2c3e30f7bdd5c4d1c597aaf871f0c42344e6ee632a5217d386528b770fa848548c0830f8d5051cde343c5bcd2630fb5c5a66fd7b518f5f0f7c99341847b9ba0ba3ca86615453777c6743df0708773a6747131da7ec898d750b304dc35c0c30e87eb133da24397a60d9a79d4e1945962da925bdf6d7e036c6421434609be068d0f2f5f7cec171f5dbe3014f0a0c37934a9d89325c489f8e7707851a144c4fcad92457238ce56f28b5113a75e138783ba4ade251a389c9418cb15f632c92df30d6713f23fd7117465d60d4791a15e6445ecbfd886f3a548bb0c3961ae2b6c388f684cba1d0a402040160e415e99a639b99a82b0700cd771d715bac2b9f572c83c42d2c58d158e26729e17db646ab50aa7b0ed12a236d829930a27911dba3189f3b6e44de1a0c4f7ede6eea4706acfb01941478418bb289c35b3e35fddf3f20405c3bd65fab553aa80004f38069df6db97bb36424e385d50beaaf15fc1454d385f703f3713a6d2cc4c3858252fbfd494216596706e3b0b2a5b259c82da7e8a3cbbcc3409a7eaac4d966e4b7d24a82641e9d095239cf2f547a6a0b32d4233c231ae9588cc6317f4b2221834668f20e26ac95c0d9df171e80e4873040188702e65f2bc4f96943d0de1341733a6a4bd422a5f08af768c5b8a93fa17c681800facddc8e105003e4180201c5c82d0d29c03e1242385c6ae491235f483936aff9514969b2eea8383a59862a788580f0e26d42f2d24a931e5a9061008c083d3e40b937d839b66f67770d24ca52bf2d4c129fae4d199effcd4f939385e586568e91399e3e3e05c314c6676c4a99e6e7032e996c93d34e9736d708829a5d998b306c7e0ef63c2d2ca29d16970de90296d082f99c171aef24de58564700ce2cc542add8ce4770c4e33c15e4d3dd4a86cc3e014847cdea697186212bfe019cb681155171cd3c9184959d80a13710b4e21a99ee50b255ae44b00169c3cf4bfa664372f395dc551eb6c738b4aad13a48af328a5952b4890396fa6e26076797276acc7c9a0e2ac3549659fe40b1a2da738f8a9d5202aa864fa35c531e4b90ddfaf77df95e2f4e223c9b25f525e8414c714b3289594b8b875ba519c2f8e4db83ca7c25213c531838f8c1cdf97b22514279710e3a8b2d92fab7c3000501c53fcb9ddbad9a429f989c3cc89c8eabfe0f0c4412911da3aecc47964636269881387499b33ab6bd2268e3116dd6c8297c865d2c43944367d260ed252c8a1b4d4d3d7c7c4295bf5fe7a84a462f99738467ad5baf491f42d6f89e397cbfad76f48d2f295388574ddf4912a74da4e89d36a6668ff2ac99bf4499c75476e5b3049e29c67f2b52f7d479e237112ffaebefc69c15b481ca45e67987871b2d4479c524cbe7f964bb78638e268da726ea5a1469cece47a8c568811e74b2e2792d266a46911871141fc3745844c157152639ba75e464f851271b4145126376bfbdf883804a16d95bece62ca218e1e4a29b56d9762c418e264329e5ac9de18922bc4795344e267d68468b3f2b7e48df020ce2229bb4bd68d200e26834fdeefd21c6913889334bbdcca7db27b630071526a7f6232b4655ecd1f4e2b1629ac23dccec4f8e19c973ee22d8dcadf993e1caf47c9598d193e1c920a773a520ced73dac3e9e2cba58c889239e9e17c152cee324c65caf370d26ed75a4153cc20e3e13c399455d2a941c8f70e87fc954296e9d72ceedae1a4a96eb4869716ab6c1d0e5f363f2b527fe2b77438c87c5149c8a614e4b673384d5ab5bc5e4185d5e57092225267d8e3704a09a32496b0701b048783c5b49c60296648e1371ce4a89ecaa152248dbae17cb3d1e2ccd4361c368892bdc8291b4eb729fca436f96791359c4289ac985d8494b654c3419205e16e266641290dc7389564aa90454fdad170d0204744b6cb1bfe198e1a93069933c80ca748226f440d97321ced5bd5c42493a021850ca79b5197936d545aaa8ce12443aa09975511c3a96e75b229f195d387e1b0de953ed297901243c07010137db38e26d538c9170e9b4d9fca9ab4a0712f1c564f34a4959c97f22e9cb27c5e5e621c59e2c2a944e5d37095a2e9164e5669639f58c654222d1c4cb575690b65e120367be44dcd9a62838573678998d0e9150e7bfa2c8ce8c90ac7b4aba65c92a5881856e16476a5e57ba7c2b94e538a20eb6ac6bc291c2f69dfa0662eea962785d38b56ac13b9454cc845e11893aae8fed17faf43e1d4611631abeb89193de1f01abc9234d79c70526f1617f97337566bc221c811f55a1582e88931e1781a6b6aa4664b38dacde8987c9b21f94a3879cac9b4add134879270d2376d5947e9503946c2a9ae45ee7fe9bbdaea08a74c9b67dd2229a9a76484d3a9dd0ab2952fc2513769d8b86fb1c974221cc2c60c2afbe843388f7b5e9cc916a42809e19482124994d21873ce0ec22989346a379899be080807eb114189b7fc83c3a5a0525710611f9cd4de98fbc6104facdb8343da1d4d69920c994cc283e3fe8b1255896695d31d9c2fbb05e556afdd1ad1c149b3e9db789a8c859883735eec1273a15e41e97070109ff9332a96f7896f70b25ac91b577923656483836a3999746e08ea25050803a8c131ca84582ddaf49f667a30001a9caa2da56ce608a536333805e9116287a4cddc1a13062083535a4b2204a59268cd1f8393081f95545c8d1b71048383c85b6d4abd864818c00b0e2944e508a7368d3ea501b8e018d297efc5ac2e3ffa01b4e0245a46680b25739ff807c08283accad817b2056deeafe260dd9f5695aee5d657c5612d8638499e9e8a734bf6cd27c442ec1242c5d994882849524abc1d9de2a472e694cda4442e1999e2a02fc6596f9858dda0521c75efce4b868b902722c5694c3cc382d6a3389ff6bfa64ebafb0d8be258bb69577ef396f04c284e722932ed7f5d88a605c5c17bd29a94249276de4f9c3647ac58c5522966eb89a35766171311edc4a94aebee5bb48e9c1327ad4a9be426b334a74d9c6293aa8ca01ac5d4c4f9ea4e85b03a13e79ea44a88de0d268eb1c9b37fc3bcc4c9747556e957ca362a9638843121d66a1b73a4a512a7d8582584aef85fb050e2742a448c91567754582671fe535632c8587e32552471d80c32a61c3e89c471efcae2ce4e927b219038a890252fb2f65b04e1234e99f735ea255a36253ae274f325e35509f11bb11107f5d297cf0e19719898912ac435d7b42ee2b4692e481d2b8dd9a288c3e6492a89dd505edd25e26c1da9cbc3bd235881885312baed7f54e910c7cc193f21a5185eb5210ea3db5204b529c4b9f2f859725115527b429c5236df19d56c7ab23d8884faae6c29fe268873462da5ad0cc449fa5fec05b990646e0171ba517f3f937e3df2fdc3796382fc16137b2ac90fa72e8b5f2797a52a53fa708c90d344d67d496b1a1f4e31294c8934f98bfd1e4e32da9908229e157a30f8846427538a4fbaf3d05c690916c6c594783095bcd1bd133695ee70d438579bbcb42aea6f07d484cc2eab212eaac3e14fd296ccc576a90c1dcc942b2e748e733849ebbb9231c44e14570e9ed8eb3d91b1445ec88a381c937785d0a52386a42afb2d9a6f7c0e071831580187c377b756924fd3ccf00d27b1759af46a379cdde6a46ff08c3b596bc3f9f26a99904165c34149ee95604ab3dd660da7245d8266d1b170991a4ea1213c84b62babd2a4e19032882bddb04f8d2d1a8e7ea78450e7259489cf705095d23fb2ddd8298f0a5698e1147a2d7268e6489d4ac18a321c279fde88c41039494c8683256951824b8a79cf8ce1dcdf33326917b14c59311c47f9a8287b1b5632250ce7de8f1d93ba11f3236038c88b3b721e4ad5462e0a567ce1142cb8c698345e38a6ccb10c415e10aa7117ce7726df72da890bc70b15dfcf50a3216fe1a441997a8bae9c31532d1c25baa81865c45cc4250b674b9aea279c25cbea62e1b47d4246d35a5de1048a7da8c4ed0503c248241487030251201406a071a600631408001828248d0663f1681c46c21f1400035532263a322c161a1c12121410108a03c14010100c8bc26040281408050220a158708c23d1f007847dba4834dd96206082be846a04161d2c8fef3dbd4d033507082790a38c201019fef948e867f8499c3f26e558eea95539d24fc7d646c27e09d412983661790432923c2dc12a024e850d2673165f2884998441ca06190047fd3e798a239f72f9f7e44734056563b43db2cd2ca1d65799e832ee56eefb4901ece6af072484753162ea4e3147c2ef08c246980ff51b5d6d9d6c4590822ce9d9192dfd596f70c2c4b8de089751e7d76d1725858f9e53bd4a87e8e83c74d7b637f35fdc01368c6eca77aa4907de187491437042e909ff36c2e6c98d989d7ed5506ff6dddbefb6ab3902f11a283cea273011187940a3e012085e70f6910592088817810c4a5ded280e6c817959e158a0594045c08081e2d5fbf683dd2027201d9060004a90c5c03e41e4018405e009e002670164030725c84c4980ebe1caf780c0d73f9d0eaa78506320764f6b1217609220379f890db420d715a61ea08c011640b93d0d29854d2f80820016007a80770013007d1ec00e3b39b9611bf8310061a06080e005305221f98228035619ccc5686cc0d202bc0a2a0d1c0b102af867203903984e208ddd2100e6539e178b4080c0b0df682e178017400168073801c8af006a8e1d7676e200eb03b6075a0d9034c027400370012005f82920290357b6bc3794e1027a03ec0720cf0086dbfa08c7151018b08379770e40168012107fe0b940aa0b3005b2cb573503c2081fd88f011400f6660050adc690843920bf401d00da00b203a008ac1240293809032d9153420536000a0318e39cac6f36065a3c826ada004d1b10188052407501e000f9007003d00d204c40d8dd59ea15809f00af47c015d5fd2f8b06681db032908a00a69c00a4af118f25373790a37818cdc7a4db03f320f97107c2e22349434eb044873acc1fef47da7dddcb83e041711e84920360e1ecb1ec81301fa7aa41d83069b01181deead7cc0fef3cd6e7d0092f6090d3f27e375b01cb2c4ce6c5486bdb8d91757d1f98253014b6360135714da0521cc08adec6a3b6d9a0c0a480a758a310a4687314bd918ebfac90a24f214d814c318cc2cc2bfe94be27535a14fa32ae59583686081e60a56ef64284fe4b3e7114bd6124f36623052385491180628722bb293c4ed94c9d9bc241814e114241ab28fe24190b9dc28ae40b458b83a6108dd7ccdfd428fac206bcf81b714f9f7c013914316d133d243feb214d4249089089081c060c4b09904f68e5d0089ab9fab945254087809862ad68e06f84ea8b5e75db2aed8310609b36c965b5a3b8beaeafa9e46024863d7bd88517329ff0534a80ae81a8616900e3fcc10990ac16e42003167837d3b2b21f2c4f42d9b5510477682942e3d5b0e5068d0973d70b38bb82c0924408fffae4c0c5b064ef78174bf2013a9528783fed9b110033d7193e6d1b6584eb72f7892b7111f551c1155aebd8c478c0b7d3bfbca8bb6e7e05f38a2f8cb1178ead0878891d7911f212a11db3d2c433270e6a976b2f5e615e30818ff4acc2646a3f0d7f9b16e75078502a2857140a5058e2f5e306e162ceb5d3d9a491e58c10fc8b2196b607601d8fe4a5d1eeaa113a1bce2501a49fc43ab0900a02405c708c76dc9e5e0a01aff92e56f46224899fb5408942609a9fae403708428311b6efe48b31c573c6e3e41e1b1c474f19774b18bfe2d4a462106a0e0d7c20c4d9cfa1fd5327d52bf1a52459253c71ec75db710cd31103884cb7080b13bbe5a5914d73a3d3768b7b489607bc47008b377c918c2496b778e9357516d720ada64e11f3f15b1c9ce923cbca1a0c4a83a60111113f72a55c9ae98aaec73137a5975c15c6fec86b0354371f3074a5e73b8c5968b8304bb14d841abd6b933b5c545d5e9b4d67083836d94cf932f8acdd72b9ee77dce37c27d980eb040c3bb3c3565ad2a20e207e49fccc9fb862b26385750d82e857461f06e4043f039cd1424c8fcba17408132a369ced90c25eae8b1c95730b7e6348b2b100da71dfaca0d19593cca72380eadf381bfce80e76eba1378c8594cb0a4714de6d5a5e6cc464e3c4fe0375844508a5b2e1cc91d395b4c2ed2c9a911f939fe5498827932983fd9c3ed86a90805def7426caa232b4b4a5d1f7e9ccc63b33bbe15377cb047a3b74fde2a7df74e6b36f9a069803ae439cb65b5af50b3c62d8a557e33a9c0772fbccd4b2f80135e4ddca2cba8c256c431451e6ccb1f8bcc56deb6fab1bdcd6dfed006907bad0d26cd0f799bc0b27b6746d31d7b422accd7f288ca13897f15aa628e9e430832d16b4c4472d9101b6975de840b35cc7ad8c22ec43862b061bbbccc4c34de4062b431e9f223d9de6f80c7a244a5d0a1a9a58197c1bb401302417bb46a30b267c71e76388ec274be8d5a316a420384c34eb5a881197e004d44704b27af5f6d2700da618a4701b63eac61594ce4a4ff3020771cacf0602522e5a5cd5bf7b18efc4e4fd42135d8383b62835aef13320d121c82f1630f0da01c84588caf3090e1171a70e02637324be16e1457bb8c88a81f7825c71fec462c0f1b900385a3219c4af3722c9f8efa2b55edab2b29688ccc0ebcda887229a3a7ce0592e760c4f0c9360621a0a8b6f7131835db947f4004095f86b87c326d054e10a54f608ed3993eef43e169baf188b2fe4f3ac02cb10ce350ae06e8ea2e4169ae5b506ab3a21a298f2058467a9601c206951fc9b035513cb51d33e96e24f2af51e8e250e4061ad00f2dcc858f7a3b838e6d01cc16c21b80ba6f3db58c1baf9967796d0582bf83b3d930b70286c9e063aeb8362ca57c01c196dcdc2cda5f347f83c8cb8e527c81196c95cc69d8dcc9a6675635b6e21b8d1b64bbeddb84c8e75388e24a801b20eccd97c3b4bb7042c14a43a7e6200606c73dc2809571c0021ab0a14a8b9817a9dea734ea58bda00939224537589acccd48630f73c7cfc28ed1ad8b82aeebe01c76c7269907f22173bcb3af6a4fae7c46ac5049ad8a7fd2a5659443b9a6bcf6fc2cd202c28cefa0cb615e15720e36bd7068f31d95b5251dde77cd29ec5626e9e68332d9b087c2e2d19aeccd6a2a2590cef301802e2361b6392f8304400614da68014ec4de0f4050e7d93c630bf4803e3a10dc6743d83e539b92c32f3c2a2b078a37afbae9568b0f4ce6380a8e732b822b98ea928778ecfe1d57a69dd985e69f508c67bca385f979a1ac215f2ffa2f437a04a7f74180de147b3f0dc82316091e7c40e967b241f8c0c28a857b9d7b118c99742802a3d497b3f342e4b3f6df239509e454b20efc898a4a614b54b085d2ad23518a203495a03c32092973ea1a037c2917400cd783c8ef879bb6c9d8577d2035002ce311039c6639e4e511387ec51470327021d0e4f772299b1da8383e7f6f2cf5f7b8459311449b210a311f0dc261c1c895b86c45bec40dd3bacb812537d4b31dfda5bef7fd427e760fcc86c5a3bc8c65f43d885b733cabc220f25e754e3977201858ee70caf024d4145b320fa82d6bfd8193f64aa0d112de4883e13c247fd7171f1046a37ef28d23d699511b636dc624e6e65730025598ea825bf3afae4bc0f8ac502da330ba139abbf1349dbb1b42a49eb590ebc07906401af02cbc8cca9f7ec2a24fce99880549003480a060a1098d04e2cc9a5e423428fe4abab24d319115ec3ffdf5b75e35455865655ed6ec4b734f463f3360d36bfff312715e8926bfc05d89099103bfc2c838ce483a1f179a95671106bf4c0e53331c5d0683ecb7e04879399c0ecedae27636a9130817f930f12dff03256cc3e750cc4e519143fd7e5050d611756e74dd0a4a7754ee03100abe2f71b5a9c7bd697609eba2362ca194cae3fa9781ca1254bfcd0897bca290045fe77e0d359d6289f61fc7d9d020e0065697c86701e6eab925d9c5821e3bb8d98e90ac9217df0f155aa3069cb91b17264d481b55e2f774330716bdc3f9b0c4497e7a5b3640667dc80cb349dac06636116ec1945d09a2bcb83fea8ad042a168ca7432fa6d0ec72299e382d0911113d9e3956046e7f79f232929e8a7393da1d5de568e845893cf51b3a3813eccd2276d491fb57b8aa909bd5e60f2dcdf34582331c43080b6affabe0beb78466d199e64159524a54448ef5d966c67cff52b47d0508d1c6e15f18b21da0ad1a505197ebd119e255ae9795b8e535156ef8670d1b177f9145dd3d457a0f833ae130f1c8fab9d3962b463d2fcecf29d8a390caeb6883b995275215dda32e7b748600b997aee62c8000bad3267ee0084f16b4e11278f447d6def22098153df517143d192d12cab0697e72a0f5b6f5a4a46db399c6424681fb080a92c27e2c870eb2f00d5b605e125aa3d41da28940042d6c783742f04a80ef7dd2eaeefa88422f6ff968a757d28480805ed0f6ca7a0a8f4cf12c4b083dfda03e46b96a5fa1b511ff5fe6a00265a6306f762a66490f38f5bc819977b8e387bd33dffe48aec2fdbb869275e9ce25f32965e6d07a68bb654d088e60de38e1f4f760c45524adf2eb2ffe279b1be2c0ab614fce0995ddb4f1e66f4be2127778df03640332d92fb2599712bd960ec04eb1014264e10f1a3b4d6611a2a3a6b238848f872491e40533d8a5c01b5bb8b99580fab7c6aaccf989f43356ee4bb82ad5cb4454dd466a74adae9010ad994f187cee4ab15660e027cfedfef1dfe7fdf034d4953c61aee9ff7c5a36739034fd7f6991864aef9c2d2ca16074b7e989c29e7046c04757444d3f86f4ea42954814d690f45b06deb962b6e1f8b5b26ba819350e3a4b36588910ca01541061295303769a195d7a0d4ba4379ccc61f31199b82909cf516a62e2f0f6ed7b48f9d232b1fd70bddc36520eaed8116b509920666a0d72b917c74b5fece793c949f3a8628e117bfc40fee3ce4114259870cd6436f049cc8e44c2963897f92f01839c98f7029d0ae5b9a908f00de3ebe885f23bb341cd1e118a0881d664a67439a8be538a269cd0e9ee0db247db7a330107a58cf2bacac673a0c1923cdb4e9efc06d5e216d7e8bc95a29414155ee49ff1b8b33f4b4bfca16cc3d4914082c9506af6a28bc373ccb2b37bc4cd0d231a7453288556a8aff5efcfa173b289c2d6b48a7e7b879430147401704491126222029f938e30b142b249eb40588ef4b8c2e558869b2b96d7f3d103b4bd547d6f151ffd37cd47a7d945c8484bf150ccb32d300f69d9c8105e362e0125bf1267a00adcf5fcf954c6e106e5c8125f63a7f6170f812b4c8de7e3fd9135ab6aa2703eae49e00c28471f0c54212ad72ca98a71f16ec4e03e269049021a07b202faaa7f78776407e0c13db436266c3591787329c01cd8fe59b3f319021e28be96e5db126e1127c7407dc47250b2f4661fd723d46d6b0b7864d889c3965f6bc8b3b0ed65a59515c6b7e0364c4e117bbfde2c7ca977dc9163643c9d66cc836d1ffbacc5eb398b1df47223b1164e89c9e33ab33a14d58becdd4d7bd6252190b8bfbd6ab7b66322028b2cc56c18f7646bb77266edfeb2d3629842cde9dc980ddc0fd55512dc5acaea0e9b4be0e8610f3f1d789f0b770872e7f2e0b12d1817aebc7fe3bba51dbd7c7cb489e598aabb13716e36fceb055796a42c88d9e00809861b6540dfd236f20a98d89c46a46abe711efb11177e7b75eb5cd24647ef85799b8b4814e583b85436f2e037f4b565054e2b6cb5b29b1ba8627e8ee5963b02a4b9425b0d94be6c15548125aabb8613ffe533f12ceca01301bf89bb1adec531bc610bad9a8e9c8b546a1949faaf29926302b59804343c4bde249aecdbb029f09ac7167c7472c02116d33f43036aab075937bb742412192b8fde2200920f8e91a859c6cffd4a510b2c4b4971e59d049ed492496bcc0b72dec80593c7a2d40cd4644a04c54f77cc484a32a864e436262cc189ac4466f77252016332af23cc2bf3fc32623612cc52e1502ee3f96f28b77be49ec0cda786874503d87f0911c478d33cb67c02a3841b1fe0bce80cde7454a4b5d0e1126060079f27a2fb05d55ef5b183d45bb2ed015f4083dc4457cb4c698fc2625aadf46db28c21cc6261b33182ba99ceea5189c6c576855bc7ae015164b50c1b1da74e15d05bb2c32248202bc1290b79cf033d713110f46ca252818cc91e09295cffc5251bb94659143e981b0677761e64d25463189249b9c150397412832de2d70a5cb9b294681c54a1380395fcc9be04790a741ea01a12f87a3b5a1afa9f307a94a08fa04a2563bce9dc540a406ab760e0029138659c76278248408ea6f656260e09639e103895ebb2152b1cf929b67addc50fc22b56002a65ec74eb9c4e5578ed1444570f4e0826473683380cb80258edd9cccc07cbe9e3693003f308bdce46a1da3cef4239d068bc26eeb3cd5d1a6107cb30e10ecca731116e0918d8b6aac15c25c2b96fc394d98ec16ca3cf48a15d5a88a437ec14b5aa76d57b0ef02a47ac8ec40d1d0be1d5d130510122257e4cec22119f009348ac679e3943034902b7965cf70d56d9ee7080aeab50d03fc04156d248b548daaa26049b94ede1f53c06bd4a235071eebc4751ef46e757b424d3732cfb087322936bc1fd5db9fc13769e8d2baece84cbae55505db0f410bcf8fea91b4ce0e094060280ad7abae2746330850d544c2f46f4c9a045bdc444103aa41102ba31acc6704fd6546ac0b707c5abdb82155f8cbc479b59aeb53b47a92a6a518399be85f8eb518c94b76ebf4756ca342a03ed9502e60227c02720ddf715d3f27d849c8fa510740cb32ec2a767d57cc7b21bd7f9e9ca78212acf2eaa9e31b3b808f043f6f42b47309fb21dbd87955122ed90b310e62c946a9632a53fb1f9bc2f7e4c10bac40e31f87f1ee1d1ccb39555092e1ff82ae164c60baf30533f77d13066f0ad4439ec5ade2d5e990109add165a455d365251be84b72a9c214a2aa90f7eb09883988c5143decd144498089abfba8d9a15c2fb0d6c1693294d902b99d08b3529fb0723c727fd89e36c47705514b486ee7ab68c4d0945951d2e10b198ccde3f6a276ed8f1dad814f1b1d6a8e3c0f7a0b19bb744de09eb9406d48d319a1c6e70fe6e0df1e041834617dcd49f80fc2ce23835b4f06956e25ea99ae2518c6fbaa80514451968447ec4a6a1e9cc93ea9c22391aa5ea3a2d16537313303adee96fa0f573851ebde45c1aea8bb285e4657e8e1297afd80912feac10eabac22a3c3291c80f6478a08a1a2892258af75ea3ba8c50b70a98f3d9c64adf6a2632471176e99e7e68e5a6aafb7bd354097b7e942a86df34a3ac4fbd28d1625b1844991a6dd3ccda080ce04090ebea9911851987cc5bef050bbace747ec36c661cd29e1a11df12aadeaf8a397b1baaa480122cd202c6252713345996e646ec354837a98d462b53076433aca6c3fe5a6753b365e8cc85de0f22d0459271ea7734a6fea0af6b898e39587c6a2acacecc0c8231e978407a728f95bfe6c3c29e7049570d05d6cba1aa295b471b59361233ce86ed08fbc9bebcc852173931cce92c81cab80a1c0fbb67c654d39be57f2eef1d6f6fd2eba2f7f5cdc72800db38451d02c835675cb2ae7440518d28b8a32594822945c66a21afc47a1443299b7500c9ecea5bf4b971ea7ef17c000cda4bb316e6697e11ac58c69b032c673cbc7044111769a04db4dbad48e2cd145de2bd3a8dd0ce316c3b8d4554586f7210557862acfb745aa2e8c1029b28973d19742e40cc66293430f44d8cb026c4b0b8192af1eb08d256365922320521296b82239e8966969921e1b1268dcb8726d0d4b92ddd3ec79fa97081b705ca6b7117d533a2c87955f43df43551854cd8b7b69b40506070758d5a06471d1f2291c8af9f36de2580c5e7bee539130cd586b36c8338e85c44057e5607a9dc1f906188002124c0a19fbd4c98ab47b8d88efe4814dbae56812352fe145d3e2811c0c3aad7014309eb8cde3bb1ef7267f65152ae1903ba7d1a5144b550437692ed2232dd48dde0964d8d452fdc6f8e34889b7738712f094d8d1dc00180524ed83c1d7c10a8451e24863fba259a16b1c0b3db04992053df73f2a9a97eeadb0d076f7dd6d9da08367420de842d0b48691a0640799898ff42ad53aa187f974468e73513c33e7a34e267bbcfebe7fbd2fe632eee660b1f716724a792b1b0416ae17f3ff7ed3bd6f5d19b280a646ddcf313ade2732ed59a1e4ab8b059cd955da2331ca8d57802c973761b1c79f9bceec5fc67750a9c93025f4a0906a534c09a3bd9154f2855cf677d2601bf82ec9c8a16340bcd8f7b9419daec67a58a9a04957283d62ed69a686e13ad78b1b032484e19251d53473178ed42bb8a88af1677efaef4416da218070af16d9e56cb7e2dd282c15593d813808bb39e42aa96ccc482f7a27b6fab1553c861bbdb12bd47aec90e0ae70c511e642dc48952c568603f9aae414f95d3f2d9fdfedd7ce651a38b45baf5e880d8db75867f6da2f3185d7ce39af99e2fbac6470ff1227f3720042f116f6733dc87a6f0a6e5b65703d72e997f87dd8126d1469f6eb344de11edfb7c25f35645c0acf0c1bf52fd4e09a3b1725cf44a79a9092031357983e514649e22e092aa252dc52be91098e4fc5742310be83650a4a81f108d9c2b2853a4f6787019ae68c47f9f046c453034efb04d2e527c41febe11c3fc16113d4a7ee87a6ded5c850e3e2154c6e528a3684c6a7c067fb6e237a53901a51c1dc89c4ecabe61279acc4bdf803a6206c3995710787c72462520e092312ca11978e213441b0b63e4e085631102cddfe368a85073e702181f4185df956db3dd8cbf1ed165ea727b48af87b017d12a2d35c8a73909a85f0ef89bbf56300b4bab2c43915fe38c1be2f476da46ee2b3e5e38ca76fdacc452d73e6bbafa9acd2fc3fc582c602356309ed6e2cff8f5547d83585cb352c751320959ad30c9e68a5c136a14ca79d59203480a93d6d0fd3ac9ef08cfad2eb6a9fbea599f33e1cec7494630ecc65094a96e46c69912f82d3f5db53c593a307f9ef2fc3c54ae2c7e344b86d2e6774d1fe974f97b6953a0da23c0c5adafcae24ba5b464711d88b8045d1af283c6c24aea6480bb0801e354d6b726d86a1588f48c7f1d6bdf08a5d489dd7b3a219b4978cd3bc654a0effaf58093438e1941965d30a6058268e414754df51ec90600c7313dae79f71627f4c44e10a6e69a053c27635258e72a93785b1a2a7a082b0a0d430e81f878081c1403e90b12035d53feb7c1a7f5f2a4e4f28a5508932d3ab5ca9659c0e8bdb7f0f511a4ba8ac40aafbd01f955e6b7e157e10eb643ccd9f1adda37232f10d2183d8f985e8a0326f034e4bdbe9443c0279670772a6d0ae4afa75ee0a852def06a68ceb40c42896749d813a7fe85334321f1c866b7bd8af0fcdea68401de51efdc67c4e24449733285b6b3e15ceffd40d2adacd187e3731966f6c412985d2408cd506de469a41933fb6a42bb7bb0ded8c6763a0e17e8c39c8cb711cf8d87aa4831cf15f796105ef37252957fccc44294d51b9c55ff2ac418707b7007bb75b64e91879ac8ffda2c75409ce485b65e02720d511c7342eb8820a71660b14c176850ef1e7495b52847814a8c3d1ce08e2d371553c739bd9cdf37d8f5b9b90c64215aade6ae35bc356b1e90a88af86224cfa911d01021915bde3b32209a31be6c19267287265a99202307331f32bda84cd934dc0ac0e0e607dc601ce31fecba873f2d66ee0fcc266f64b6982da82d35b466ec1231cba558e0525ee530a774ac15e3272a5515d0bd4a4e4f449e9af09c5b8a39292eb29a97f258bb925b1713cc179681e1f7df34db90d4e609fb39843c73282a0f0ec0a4fbde6818e1ed6f1a01fcf98f0d03be5a142693f62a52d9d87aa5e179aa36ccfccfc1e09e88b0bcd12e682713a12478d59185a8e779f86491f6574e58c2b9733638a3bd1942f94ad945dca9d65b99cdf3d47458179451988f24a9988725f4172b51184d37624d5f1afab60d16ccd05bfa32f3df26fbd433ffe5bfe64f7283b12602f189f8e448f5f8b4b8ba7c3ab85637d8650300911e92433a96b65f5c96605e81ad7c83b5aec227ab118b4b8eb889dd8d384e26e644d442d5a03d13a88dd7a23a6857251f11e3b4393e233474f8d5ccb8c660d264c730693e698ceace447cd8b571e6a341d86e9b8c04a0ce97121621e86113340be8627168660f092af8a05bb7e0be3afc49f91d02d4cdf82eb2da8cf42315a706ba1bab8305a546f638ec185dc60b09f0bd292a1d7c030e202a87af485d2c425cc32703a5a422044544b832fac7b452333e4a10790501a622997b71d7ffa929a987e403e034c1ab57b6a247d5dd27b129553b80a8ae433f0dc5777043b9712290367690e953914b35b4cd54f33878a24bf34bff0a3b39bbe463fcfa0412e025d4f295bbfb40fc0af7d284ddd81e94a2585426e1f8618018530a036e40190a25e1cbd57a1017dfa938c2ee2b2462c865217092ef761408a4d27f88ad8e55aaf295b565105b2e4ddb224706f7e56f35557a2a641b19436a9243df26b43fe2b9623c2b2275b7e0f89d9cd0bd49cadd7b3f882b4f03d2c45ce083bc8dcbc27ae3be8f7655ce5b6b3d5b6edea19cc9373c9f4b2c268fc87b351258ac77c13bfad6254407e41a0235780dd6027bfe4f6b39ea6781263f278002803aa9186774f8699b30f683fd93496046845e08faf69dc4459d23d49808328109b08012ec16e488f6c22797c439af8f62c65395d5a651b67905e988bddf3b9c7210f8c49a2b8e02d1e66214043e4db017a784f8c3b80b0a4900330f0fcc0f303cf0f3c3ff0fc009d37f1db165bdf851b8a48410aded15eab1172b9bb52ee8a283fa1d4f9398a2bb6d90128d57f72220852087b08e308e42e057a7d0ce1b323f7b0b228d047b626ce62a4d6ca4ef07c6f55c592a7308112a4be2a7fe07f3945042885be97538c17429614bab9e7cb614cf2c3a021c028d4930d923ea51c4cc649124014ca6cea9bd9345be953040284e2d85257b6c5982a03c531b9519773dafbfd4431ebcc278f8dcfb33df1860e374dd071dff23b81642cbf9917bb0927fced0f1e778ee3678537f19588888795262c8fd385943ac4904c2469abf294c7937248004ce8527a63d9a74be881997c8e9a8e2e87b825b47f9992d71829c7bc7640804a28d962e374982f22e54f09dd64c6a3d8cce9fbfa49e8e7c169ac205f1e462f09ad37cf6fe48fc3d0af4742bdbf701792e698bfe290d06388581a3b64479df923744b132c3cc8f0b03eda118a784964eca5f0f1e64668761e6c4472e8119b19a175249ac34c591ef2e54568ad215e3a8fc2a6d4b122d4ec91ed04cba61df92311fa7bdc48928cee583f10a1f9e5cf5b7b1c42abc94147cafd61b777184289de101f67f140234721749f6dcb3928cd732521948f1d4a6be642d8e00e428bec16ee71201f245710ea874f6d1ee94b749440e841238dcfe6c97ce400428f73fcf114cbc2c7fd1ff438bcc8f172dcf2713fa8615b2ede2b8773721fd4088f4ddcfb7fefe583d661436ee2e498fd1e1493b0cbd159f0fc307a503f24c9610e32824d300f6a5cc7d8f77eefcee141b7fc2083db77c79a3207cf299d54e6b083962b928cb975d03e74c2cdf774d0dae34d737acf41e9986b67c2b3859095833aed63c6416bedeaceca120e9ace5575dc298dc5fc1b947c8931793c8faf73833a9721e11f498e63db062549de10a32a987e1836681d75ac7bb1f6624e59831e2b7418a77a54c37eff6ddf9d2d69d0357e1883062597777c36e21625953368bb1d427df0e82888c40c6af230d5c6f86b4e1d298362c1e68265cc296b44c8a0febd650c5512fee36f0cca475797993e62502ae2858860f93cae5c1814b3ce8a7ba12fde3a30283985ca94dfb9dde15f5037c5d4445586e4c70b4a58e41897e93a73982e28939f3e4e7e883b122ea8b5614ae38e580e21b605a52fcc37a7c8fbf8332d68d1f144d69cc4e3b46741b7b05da31327e9c5b1a0c7978376ebafa067fe584c3ffb16b35650f3648f92eea70a6ad7e5eeac1c77bcc9a282b27d16dd79642a2635053de838ffa39c4b41f9d75021ef32de76444193caf83c613c2ccf88005050dc3cc7c977a32657f40435a6f841db84f9a033ef042d6ff4c7933c9c14c16f82126ef29f7bfdc6993c13b48b1d570a1b2c654ff712f4b0638e6cb7c30ef7d14ad03c27f7437c303a6f2741cb293f44fc2b56e73412f48ccffe1ef51114bbf459825988d79a8da048867b7c6998751c5c0425878eef74af34c23f4450339c0711e6cb27da33043507312c9e5484a04eeab03cff658e1cc60441ebd1fc8d1cf9332706088a58107f8f1ce5f82ef303cd2a7d0a1fcf91fb9f3ed0e3ff7d5897c3cb3fd9035d2f3cba147310eec7e481e29e65b2e538c16ecb1da85bef9283bf0fa77bd481321f6eb80fd976e30673a066674d9f3598c61c421c6816938755f92c3750a643ae904b426f8cc4069abf8b97c649b2572135503cacb98fc1937e3cd3408be0b9e307ede0367c33d0ae63bbb3fc09d5efc940e9d82749e5cac540dd9171cb419b7e9c33186833b95bcaecc267cc5ea066ca614498cb5ca0e7c79c3ecf9b660a590b348d94b5dcc345c84fb1408fd35d7fb1422f44b5025df204b7f01c22cc47055a443ad77133057a143ffc9c72d8b1d204054a3a4b9df45b3a817259fe121f97560a014ca048f628f7cc7c29f4f84dcbf3760a9dcc9342ed10c353c814f3a4f247a164cc0be1adf3c409be28f4f81cf5e638ea9c3bca87427bcb21119252dcf31e149aec5f5ca838313dfb27143f0f5305f9db938f7ba2eb4fe69e49f34e28fd22525693a33fc939a1a5907b6c2648c56b37a1586ffeb8ab3c8ea7d39a507298b97fcc3246329d09dd62a6ea38c95bcab331a1d969fd5c4a22b9cbbe841e79dae3e794b6845a93ea23b2b7c448e94a68231fb85f7f74e8784ae85af216bd3908fdf324d46a19f31453ed1d47126acef371a7cbf0b09b4828319e5e7fac2c966218128a47475dfe0829d5c88f50538a1191e3287684fe1d74e6bc9177b23149184023d4f0f142e48eb3e7790e46e89fa3bdaaf94cd59b6311daa40f5184ee1e3e88f25c35251d89d0a3c3cb7e9d909b7b44687a11d2b9450e1dee875053feac2ecd8fdc36841ea3e3380cbbecac6c2174ff8be92a570841b20e4673c2759cda18792418c020b44811a4e6b3c35cdf0a420f1ae275fe1c3bc7b10d84ee17234b5e0c104acc2392baec7b63b67f502fc78cf191586f0aad1fb4dc61868b74b9d2fb7dd07fd3659bdfecd29b0f6af21399349aeed379f6a0e58c1c1159f72fcba307fdbf52ef1de02b28608515be81f5c0154e634615069007fd33eb6c0e6e63681f0f6a8a3539538c5d13fa1dd4f938ee92143b14f72aa2bccc3aa89fc6c30f31c8e74e07c5a54d3e86938e08e7a05874eaf6dcafe315392871c9827e6e09edc5412f8dbf4f2962e0a094eed9fd7fe60d8a9d7d1c72241d9d9d71839e637c701ed4ef843cdba08c7ece90ddd9a0b5f9051d0d1a2376ac41c9091e63e5ec24dfa8410bb7cbf350962387641ab4ca7e1d9b64b8ec52a2418f29db30d9986c8f33a8adb9643cbda6e90e33287555d333f162a6b70c7a902badd89a227c2c9241d3f1b91c48fa3898188e418d937ea272da641f2e06657a6e436bacec110f831a27a9a39bafdeb2c1a006ad5012f9a37c418f19f5e1aaa47275142fe8619274937260e9822e35e3dd953243f0e082221de4ef460726716e419b0f9aef03330f9942b4a04b78c8e778b9247b240b9a7f78bbc8cedb88112ce839d6c7d56196f0b85f41afb7915cee3ebf492ba8d51dfede834408c9ab8276afe17118a7a3821e79ec335365b1b27400087b46cc09396cf1a0d885bbef587b26d6e70ecae40a9fb9976387b3071e772c733975d0c390c47d934c740a1b3aa8992f49bbf226546ce6a0799033eb5f7f96ec18396832a3993f0eaf7ca6898336edf91d7f7edc183470d0f287b1fb2ce68d7ee60d7ac7f156dca499b3c68c1bb41c3aff7ce4b78b946983e6b14ee7dcd6cdc7271b34fd491b9f63cbb9f15c83f22192b9af063d68aeef509ae3fd9306ad76fee2e38986cda041cfc86163ce4e4ee133a83bbbf17ae6c13d2ccda04739baca9037587c1c65d022777e73f0931d4b06f53555e49f8e9b9033062d430c1a530ede6f226250b672de3096f62a2c0c831ee587ab583d18f4ce49a2c6e52fe8712c2307752ef93cbca0cb57aa1f778f62ee827e96ce7d3254ee18c385ff23475d0e62d98292cd4ab77b2d282397453e9a2a692d0b6a7e961cf78c7f5863418de41a591f84e80c952ba81d467d7b9a98a9e3b0821e7df67bedbfcddd902a28769eb3dbce5450d368866ecf1474114fa571a6f1d12805fdafb296a410bc668b82e27af399a3da628482fe13442ba6cd1e5f754fd0f29df869db4ed0aaa733d7b84dd03f967ed68d35594163821a2f45e87db6042526cd61a86fd9d25f097ac60d19ebb257ef4950b365a48fa3d1a63112f4b090e9720c8bce1d47d03aca77c182e6b0dc184193cbe14bc2c383c78aa05458d2b81f39bb554504dd629bd4e8875d1f3c04cdb7267bd8a937bf87109438d97218723899f31b04ad3f48c71c83f4a40c10348fde2d798a0beeff404d97826fb28d3925892841850fd49c7c92c751ca17808a1e281d3b2b62c6bcf1639a0a1ee8a29de7c2766ec95d1440c50ef428428958786db18feb40c99e420a299c470ed4daecb15308c173ac1f1c68f29303cf4c1b4a77bc811e6cb2f2890add71b2d840dd0e4278f990b3327f35d0e3f7ce7e921f0f3ba381a9b28be494f29a815e1582e5780e19e839471b43ce21933d4954c4404979c282edf9a4ae910a18e891d959788ff30bb4fd0f3b22696bcafd7081ae2f9d36d85dc70c1a11a86881eed661aa8ee1ae997311c82050c10225c33d5bfee920d2843860c67a208b4650d18647be0a94fe1c69f8e7891cc44f813a9be793d2e9285077729c21538ae092f509b48f392e33420e2a4ca065ac1c9232c5afcd4f29b49ebc7176b72e63e89042c9f9c07c735f8c51e328f4f9ce8b99781185dae1c297ff68eef886428f37a67693289f9825a0d042f674cf7f4e5d26f9849a7a7f5b37e27e4bcc135a4a7b497eda3afcb813cad855c4e4164ee8711ce8e77d894b4cd526f4dc9d3d85ceb429e4506371c51607a92a28a080edc3149ad025dcc7fac14765e3716442b164d79b6e66628612134af828ecd684e023235e42fd8d78d55fb7655ab184b9e3cbf0521d570925d38664f9ef93b92c4ae88197d7e7e578337610bb05126b8a49689a64420ac9c364610a4930415c643a0abe158cb2c21491d023cfee91cc166cb22d85292081b8040fdbb1e765adb0c20c533c62b3ce20e952e8949918a6708412fca2f2495c74e4762394b0f96827dd5f8c876c610a46287de12c76841db91cc1c2148bd042f2fd481e7936870b3a610a45a81d836b6c37c9369292401917a64884362923270d9b909803a74084ee5315f6a310de634e1f421dd9ae8d55f13184b0017a01a784290ca1d7ff867e9b6fce4c075846030aa1bf95dddc79ae08da22610a42e8398e1bd3eeb139c5384696d79061812a4c310825f48465bbed18d2dc832904a1be077117e73ab2d4db1481d026e43013ba3baad23c20f43059ac0fb276d8b1fc3f68f31b92e4dde72095f7c36e9bdfe3f6141f5d1fb44bf793df47bc3decf9a0a58be05a7aa797ec3de8f1c6f2384b7319173da8e3b12f71ef764bf33c28d9a763918f82db68f0a0f9ffe56c9a0facbc8362e1357764231321d57650a68375e8ce1336e75c073dcc93b556ba4df2d3410fcaf773d0cf41b7105a23ce7dc841c9914c3a6cf2888356dd91e48d9b462b0f073de43cb6b88f52d6b66fd0e38c8777a4d210b11b14a9daca410c164bbad306edc7afe330da7eee830d7ad2d97c1539e472cb1ab4cbe9e3348ce791941ad42099dce3b839fc964c1af43890589315ae49643468164268b967495ce70c8a77746962647eb74c9a4193b19cff3963192a97fc3c53fa162964d06adc237b596f759a8c412b998ecf83fcb0ab11cc90c130662c8d1018a08a03949d0a4c2106dd4367491894c8a9dc3f8610c3851030a87716326ce43c61f25fd0ee52ee93ae8fc5c3bda09d4ff0ba710f66c3d70535acc6c3e01fef7e94e382ba31684587d53ef1b2052d82c69c1fa674511ed1829e5376bec9b0ed1a5916342d97ec8ee72d751616b41293b3f073791ed515f4d85a327496ca57af15b498105f3ec6ba90db29aaa06674c4f0b2d0de310515d4dbadb3943ba4f898827e169f24871c2928152e678ec542ae1c4741bdcaf2e17cc2ddc51c0a8458d5fd6f162e6a2bac3810101a3566dc144fd0724cd98255ecb9c9ec144e50ece378078d0a911f374513d4dc51ce81f7cd0435e7af3012638cd4f64bd0fe3d4c915c32e69f5e097aacc12c2ceb7d601d4f829234beb3b674cc573990a09d8699f3943c47d0c3e90afdd7d5b12766043dea482ec7129a27f8550435ee8ee59cd3a789e927829e66376d0e9a0f410fb635cbc22746c7b910d48a3936351b2cfcde20286133ef548ae7d39203042dc2277898c1723cd6fc40b9fc1c3fbee303c52be7f37d799c5d323dd043f8be0a9515c22bc803e5a7fb2342f877a0c64d6119924407fac576201f41fb6ab61ca8c1d2c454a96338d062a8b3cd331fdf4175033f4d7bf06c711b28b962c347fd576796b6985103fdc3a0394ee7c647e9a781e29174e6388e5f8578cc4071db8e4b7af771ca1d32507c3c8e426bfa0c6b37068abc747fb88a57995b18a85d239d7f52e7cafbbc4029bbc93cf99126d2c7055ad89cc3d0ff713375d8022d6c9e34b1310bf4cff3d69014243b5b56a0dc87fcdc1f6e0e3e8ea840bd9842c81f6c8c4b2437bb107a1ddb3cce02c6d2d819a70b1b3750ec2cb8e48e5c9f1dd2840d1b28356137c791bb5b75aa140b6cd440778bf4e1443bc5e44e03a52dcf42f3ec32934502aa38c02fa0062f8d1a22d019281f7d0af3f02ff9cfde81ad616586810d19a893d2e68a97f925586b8515a49eb011032df7bb555b0c1ac7ba23840d18682e1fc7bdd7adcd92737eb0f1023da549c9d3c6da05ba2673cbd14b4f32dd375aa0e64f8a7b5a17e3741c1b2c506fb2bc34f4327ba7ac203754a05ba6d84bdf6da440f138d8503937967c4ef1c1060ab4cfda4147d6b2192727503cf0e8f4bc4c6257b861023ddc194b3796a23f8ec9e82db42885963284f70d5691d15b68410a3d7de6a0c62b88a6df46a1cc548cd80d1dc58c31a250a77c82c57b9020b8088592f4da43dad8cbea4181071e4c3393a4cc27d48ae329e6d6d1ce1b3e3e70e109a5cf22e73b784e1d84b1135a7930a39d6386062e38a14476cd619fe12246fa022e36a19827e9d017733f638e035c6842f9388248e5109d6de12213ea848a7d396797e61c1723eb7cc005263489393ac9e1227f9c182c63865f075c5c420ff2d2d127860b4be81d749862547490e0f118593268cc380198c0452594eae02b5cbe860c1a309ccef502300a179428748c9f2117236b125a248fdfe5f48d91d559982cb4b1d842468c09586185b100e4c18524d418bec285093da92c1e60851559c8b8020406132e22c15b852c1e3234064f8cac04800b5c404209d963ccd1c590496d3d429dca57926d1adc4e7384966e3e2cb9251ba1bee44f931d6c8ee3f124b86084f2a195e3ebc9c7615e333c402306125c2c423d9dd38f7b2f7edb5302178ad03b48ea8a103fce92424770910825e730eafdc3903ad68f0825a74a23547bb974fc105a6faad2fa1cc4c896c3105a78c7e1e5e52869cb5b085d3cec28aaa2bfa3ec1042cbf795c1e384e7704206a1e6c8987638d1b12f8250e46aea7c2c24f4e402a15bc5bdfb6dbd1cdd00a1c7113a4fc749fd41f338e4d6b80ab9f0835d3105495261c44d18745550400165072efaa0e60df5b193874e9cebc71e562ef8a08b7b9cf0399cd2398d175cec41ffd8f5f5ebf42db8d083b2f9e38f2276737ea49a868c196e85155658e19c85ffcbc80d64714507b6861651709107c5b3b844108de11f3bf0604edd21894564b250562176bcf9ac24597234292ab8b883b527c17393e65d02a54c035cd841fff0f41d74ec75d03efac81e72f0f427d3418f390779b3a6944f2e3c07bdbfa2e3a426e496e5a064cf77992a5778a71c71d092b4c7317dacf7f072e0a05d9ab3cf0c1d6ef56fd02b4d8e21dfe4b0501b3728c9a3a8cb97d1147a6c83121d59b8e810a17bb341bdbefff899943568952e4dbaf14f0d7a5c0eda2eb44b839e9bf947633a4cc70b0d7af8f68fa53d2e668f50e0e20cba4488c57790262ff366d0638f935f6ef22883661dba9134c7fce4c9a0644b92fd2d26899ccb31a8614288c60d1f42ca88082ec4a0496859e630464dcf260c7ae530a7f2b94da58f8041bd90354d848a1ff4f3052df64be5728db0a84a8b113803bca07f2c4152ce7b0e229e5d503a44d01cfe8470414d193331f967cfde395bd0f368c7cf39885a507232a6fabe8f83f0962ca81fe52876b618c3829ac962eadb6c93e962727105357c1cf34fcbaa810b2be829795021c6182a5ea6aaa0b5a68d8f7278d91d3315d49aeec8cfa3dfb0f04e41b1898eb7a17db6f1430aca07a691837cc9814b15054dab4a4fdfd307e51514b409314c9d841ca783a08b27a493ecac3747942403174ed0ba43e463ee8e4c327603174dd02e86089b1d739894364c5073ca9d5899f662db25860146408302c5c512d4d78e72f85fa139060426a346160b38800b25e8e16f3c0ff2459090641749d03afc6053a810211d752e9040e8e0976fa7c443fc1ce133cf0d29e2fbc731c21f59daa0b5313b2f528443eae039e9d8748c032e88a0079176f38554c12a3443d0fba522b27558c747c1800b21a8399e88201fdf88e5254626400255181a3566b80882f72974180be54132461604ac68d498010617405073270d97d8ec29f527015658f102173f50477345b02b8b79fefb40c9c164ee68b64567d23dd023a78bfdd32e3b8fc203bd2c5bf83889f8e796ec408be1eff52f64e37cd381fe7d1def6ce898a34be5408f314f0e1347ccd24f2e70a0042b999ccca1227b72710335dd67c95196dfeb8c5cd840cbf92e480e399fb9af33b8a8819a2ed7475772d1b4330d74f933af7029730c7321838b19e895a93a43f853066ad544e87c799c65bf1659c8701103f52d69c6904f18e851ce7d7b6eb9edb17f81d6e935a7f6c6bb7217e89793c7e1e3c06252b805ca853fabefd11cffe72cd083dd98d7ce1f36db5e81dab1e57479d2c879b40af4dc91cc1f6264b8f9488196339783cde1a4ebb845817616e2f79d6e04c9cd0994f3fdb83c0c29e40e441726d0c4837ce8b842e4799452e8521d3df1f3c5ae9f9042afc9b755216ce89473a3d033e5ac113ff09034178526396da4fbb18aae2b14ca84c971d68b07963b99d508dcd0800a50a87bd65982666eb27e679c28a8f88416d95298799a98379823d819ef129041812a0e50451507a8a2c61632dc035750e00a095471802a1640c341f080112c04acb062043bc31c800118016801159e505c333c3a0eff9ae33ba1a6c957625ba125cb09f53c8ca49de3f9307d13ca96c741900ae7671d9ad0c3d0b124cde513f44c28269a3b8e733c64b498d0e3c955a5b97284652ea1b46b87a12ff7c5182da147b289fd1072a62a95d0db4c4224e4889794d0fd5af42f773f092de56d8753d54b4273d1918deb10337724d478f183f05a43420f3a104b12e3accde3115a6e9bffb0dfbce774843a379ae2e328c4d9c41ba1c6da4f211273c8719c33420de221c7981c8bd0c4c33ac73d92cdb28aa87d62878f4ad389d073945354d2d051efd988d034d24d7786fa10da74b09cdbde3463ce86d0e32cd6d53dd956321742ed1c861cc7ca39303b99106a46fb5732b7f214f220348f214d77acada12a2c08b53da7f871be7ddf8e03a1fec7f1f886e994cb3f00a1841852b887f9077dc7836c19394b58c8e10725242387dc2e1f65bf0fda957bbcf8b75da1f34191e938a40e0939c7f21ed40d99c1620a8f6ad6833ab6399b3fcce641036c50e3ad620e36749063cdb106edf23fd984c8a841cb6539436a2999918e3468b1a3bcd339b7d170d1a04e8e983777d835b13e67d02e879f634bffb1a61ecda0c7311d379285cba3974133c9b127276bc954173228fb2d27192cb70721640cda9ffe944b8e03eb1431683f9511cc3e8e15338b91056304346a54991761d03cc7abe2e4b8a2068d9d718ca1e135ae20087801062b4cdd95a40f5bbbe383175f28b74d55a418fdbc50bc840f162bac3010240b5e74e114d2c179ac1bd5f78c175c282bb078b105a5cb728e3672723fb6a802020da8e2dc025e684149b3b1b4efb3a394c22fb2a0c5c47def4cd2f81e3105bcc08296ba224c7e4c9e67f315f4f87dc2e51c5faca00751fd7576e122a7e94515348dd7f039de4d9e568efcf0820a6abc6d499983e5cf106721e316c0861753d052c87a793c5674b5468e03300083042fa4a0f48ddb565b7647efc7c8926180f0220a5afb07215b75a59c81a18507bcc615300829c30b28e83732da71e016d993f2044d3c38bd8f5ce9bb6a2728369dee3ace79d104adc39cf5cb42c4ed6d63a4d39041b00d2f98a0c8dcb487dd162ba5548c2c2cb05882de39112c66b6a4c7e32bda69cc805103c6d25de1851234bf54b9cf423cba0a99c28b24e861ee91b2af7c5f3928bc4082ae1542b22fb3d2cf5d8c2c193560b4db11d48f7119d9b7f3df3e62649911f4f8dbe96cbd90972ea7085a078b0ff1df8c2d64d0d80012418f25527820bd4172e55c1f5e0c41ff8c1f6e2ec4974b9b1c5e08418fe925c2479e7214a382c38b20687921058d0f7285d8738c1740d06296ce1c22ccb3a79418590e83ee092f7ea09f7847b3236217b28a91d530cc0b1f68915fef2534cd876c01a3d769c44826bce8412985d2defc93a9f2173c50629945a488ff61b9037dfbb72793574eefbe84173ad0d398472958fa0f3b87bcc881f2e12e4688a1d5190c1a768417385037898cc88f17236bc61637d03d9c5c235fadc92a33c20b1b1c62f6d81a72f72f034607f88a0e70e1450dd4d87f159ffff4d3c531b268a05e6859301e043360d4e0cdc2cd8b192831bbfbf9c52aed281223eb9d060cf7ce1732d0637f4fc5c83a1fbc88811a3c937ef8b72aef28465a0f5ec0406bcf54312b77746fef0bf4e8a2426cd37c9573bce1850bb4cec13e7cd83acb1cde02753c9e8e1f2652b858b240cfbe5149c772c386172bd07398cdd1a754ee529f33bc50819a2b6d8a2186e41e39f6450afaf0308c664a39670c2f50a0f6792b861727503b4c6e2529598e37e8a55f9840ab8e3f5bba484123e6520afd3a9acf31f8f869b731b2e83648a1849d4fe8927c597251d818853a3dc17e24dda5f05144a167cafc7a971de55c8d2d64c0a0e1357e8b8542eb90aeb0677559221bc20628f4703264d987e162cffd84123464e4c0224dfc6b6c21c3135ada4996a1cc62703ba14ce54ee7e870e2f8727798d984feb675d765d18626f48edfdb49026123137a1cc7b1d961e75964b13118866b21030b3e1e6c604297e0fe1acbcc363ec303346250e560e3127a69e7984ddb13f3c672c386253469110b7692fd49ef5ac3462574fba06c424c4a681ddbb90726993dcd3f093d54082316cb2209ad83058d182d9272a64442cf71546f5775920a1324b4309da31cf9f8f38a31f908edb7f6f2d3dc44a4080c1b8e50ec7d7259cacef0008d1884371aa1c7c82c69262b7b87b0163618a14bfb4ffa30f79c6256586c2c8214a126d9130f53b66264553dc06400c6b091083da2e2e4f9d05107bf09c30622b4f20fbbcc3fd08e99372f6c1c420f2c67df449e0dfa1a2a6c1842a973bb89fc7bfa11f21b85502ccd8657bd46ccb86b6083109a04bbf0be5caef72931b288051b83383afe8f9a3146560896060cf72f26083dc78efb7378399e63baaf0884f651dd9e47c390d1570908ed8330c94fcaa3bbf730386cfc41cb1d75ee2197b561c30f9a664b71f2644bbb6f7d503bcba760c1272bae830fda68c7cfb817db534ddeb0b1072564cc6f3162c7b11f3de8b9bbef83858dc7b66cd8c8836ee1c30e997f3baac278d03c850e6c63f06a8ff433868d3be851d8db87d09a1836ecb047a1b3e9818d3a289eb43a864e1132a4f7f4061db4cc31ee76d25ba6dde7a089471d5eb622a4d851725047b4d35d661b9790e2a0a58fb3a2e3f71c744a9d0d3868697298b27de7be3cff0d4a0739a44e31173243bbe1063d4899f37e94cafe2a67a30dea46de68ab8fad4def0d3668fb12f244922a461e1b6bd0624a1d527e25fbce220236d4a0e7c8c6364f9ee9cbca0e36d2a085a4f92ebbfe25458e18595589063dbe8ee3871839ecdde60e1b6750225c08e7518fa40ac90ccb88fd070c2c83962c927cd899d35c4d06359cf9758e236f099f31a821c227a49c52440c9aa7fa8a9c8441bfb7147396fc82414d9ac29ca6ccad243261e30b5a4dceb8193ce668aff782dee6162c7fa0e9829a722c1562cac8b39d33b0c105e57348c983fe84d4db5b50729c23fe5b2eefc77616be818531638b2cd6b16002820d2d282e2e9badee80165b0386f3623183686c6441eb38700f724bbebfc6039b058c2c2ca8d1a1a9efc7e7ab3431b2647495161b5750672b87b85397e1fd8221a3cf8c0d2b681d77211f87b3c9623e461616ce592c63160638820258c44615b4b3cf8cb6cc410e9702b1410543978792381e75141bc3116c4c410f8348a7ae1ce4cd392f3f6c48410b1f272abe6be2525f636bd860230a7a59a52ca29993bf075571002b4ac1283a6c4041cbded4e52e2968a7b49031630434b6d0629d460d226c3c41cd1da57517233c923846169913144979af3a0af31c6f3246160def000d192180d104bdf3ce6d82ec7c842ca9246c30414f9bf3847b98e9ceaa185923582c1a0b19bc812caee8800c19078c1835ae08010c2df022b1b184ef2288c771fa38691d23ab03326460d15798a96143098a7b124dd133731d45091b49d0c3858fe27ef889910563091b48d0376648861c58caf9b278c00162d4b8e2025a60818501aa38401516a8e200551ca00a25545e368ea005e9f062e3ec56e6da086a876579938bcf39b54550642e7af272bc134e4304ddff3ed9c67948f00c417dbf37ef16afd8b208418fde0edf8e47bfab725ec0043682a07b5c221d62aa81a06f7c4dffe1f74f628e1f28c972360d7ee75f2dfa408f3c11b3737f3479c41ee84166bc8e15471ee895bde7395968db1c3bd02572969c3e88e840cfb5115e95ae45e38d1c289bfdfab4039b1c73e4093670108886a874ae6402d2581c0e8783e170180c0629b32e00e31408104080b0341409c5b2280ce4ed0314000447281e382422161c22160e18101218080804c260604014088502815028180a8445e1714024287e2e4410114be70b347121a8d17b55692ca54a395206effcd8a78f56bf4da49d383846a0abc2c2da1662054a2cf91ec8df5bd4ff716d4663b2093c52680fc080dd621f5814c117a61c2c5edcaf263bfd844be25fbc988009570d0c36dcddb95a0c99375ed6262c153edd349c045ef7c1d692108d73bd7bb4cc5980a4d22131e704bc77e60701280ade49750c0f94017de7afe251012196e83d42ab2fc9ce18888db86e57c53040739e3a02c6cc590488c9a8a3ee86c02193dd87b5b2ec7f1191ca702089a79d2acb09b8551095ffb70439823a05f30e64cf08be9b384766eceb2173252013c3283112e2aaf8fd014b7ce29d77067e419304885a0a15bfad18a6b55a1d02ad0becb05592f62ef15a20c2a721841dd905d2bae195ab6e6116960e84c0df3e68e484ef7100bd117cafecb368e8ffa64c742a197d217150821457419352f49132eb5d80e1be28942b010af719419820ed841b958538bdea243f6484631b3e6ecf9562a9283fd22b2dc9fe7eaab4e90294019027ac577edaa1f8484ad9d1ac08b4d7c4fb5d630e8dea580ef3f5a56307b5516513ff5e58d717972063ee348a7440f2595c68aba30164e9380d7929e48a3ae0ab4aee0332f2185d20609c35dcd9340693ed5e3153866303e0b0f4f2c0f5770ed2892ab6b6c6f5b77fc7cd47a46a4a45d56b5197c906089ca8d58e959d017b1a95caa69fd3279dc1bf89752deeb0e1beba3950a63622b0971237c917ec4dd439c78db414cece333a1ce1424eed2f7b79320ce3a71da30b295c20e623b11d109c45e0086aaa4fb0b62fb648b4cc4c801085378406c96f8a067d9e3397210ada4ba6e8389785188440581a4f5506d37a8d18782f86db1f542f98bbcfbf1ecc48f4404f7ed469030848c7290fb10b704af54e42fd640f3a20962386fcf359f6bb3698941ed6923853cd8da29b21d269fb4a4a777875be02a4a723d9ac08b5e76d7d0ca85cfc8bcce8b9e19953daef3c1ac9cb8defcfb2647a6a22e34121c799231fdc37ba1c3b34db9ecf0858f3451f18d1301ebd0f33df5e6e02959938e2a0a9b98c671bd3a4b8c834b5261d5c0ee7e1747c64b6f30324f2acb8fc8266bfc0a481e118f3e421e51f3c5a24b53a721451222650d3963c3f38579162ff9e86830005870191e492626078f455f3119d4e9beece502824cb4b0c40c55b35fc89d1b69888096ba695f7ffdecec363ebb3a8c5a6ae9a02c44830d695dc191181c19ae384d4710c90f7e2f40a68aaadeb6a97350388251dbb04250276f9104b757f59ad58511c91aafeff4fb014008a6f07e0f6a6aa6596e449a793efd8c7a5ea7e20aa5794156afae5b808692dc42d704b56fb384137c64977379a76e269431089307cbb437d7675f4b66c076ab371129a2e7b8ec28e36386c4d58cf831c8475b4e0c4751551d166691806d277dea189d21921a110ce8e901167bf747762ad79d3324acd1889c0a19b3d73da1ddacc917a6ec082c9c730a34c370ad5a29ea431acf08c9fe531c47cb284df29118878a8172703d270921dc4095e6196784296638dc8f6c5a5e7a08d9ce38625320204fa23e0b50e801ec0132006033318e602c94a5dbe271a17522c6b8d16851edebef086a813f3100909fe479441ea8f84ba9125bd565367cb449b5499d347c01d5e6583061013d04b83182caa49feeb1390c2d29d23db8ac746df45636003c56f295e107338308080217438621092c375b0e0662a7dad3c1c19acb42ba8d886b8a39a224f51ad020d7179e9982d6187a4daa0628b9f67cd1973bd5b513bbf18401abf7abf1c9a16615676178ef515f77bb01c5635409ac02915e492e397b039778caf3f9464d45de5e2a0954dbff1c75b0eab86be97bedb5a3f44a0cbce8a04b0ef736af092c1bfef425a55f0f68e05d87fc084bc813c3b988c38993627d7ef0a31e585ff6cf4af8547622ed87f2b27e0cfd81bbd33a640514c65f73f6cb5be8b00f9b7dd286ff10486d541e26891acf2f165197d569d3bb41e561e9b86453ce5d5cccb5859c2088c3e9991856c3cc33469777e95dd57bf18b4e1693a5a824049149a6488bf867624f7d351d4b159270eb2e4d835c8e1071f4ad237b627a5c15c0f045d9e06fb776eb3cece63174b06a3298cf4f1deffc7d4ca4929fdc791376f036cc23e8a251e0e7ab9c39cc4673eb9be09c1d6ccec7a3e322993926cce72985df8ea3e00ef67449e1831ca41dc6068607294db503c578ff3ab369d884f7b667b3cc0d38f213608d7f2ac2be73cfaf6680f4a4ea881152523eb9def5fb66034b505b892d2d0dfa7e18dbc60c1f1aab420154d60011021ff7fc0cd30e36d48ba9be0448e98dbc47a837fcbf7d94cde68c987f07fabb5480afd23c9ad1a15ee8e0d2d68d358eeb4ccdfc2f5cac8963c15e76e77190988484991339d221c978d587c856136d1260e9a26829d9473056b7e6bdf8d52e391202f495d7dcbffaa495ad0898ed23155a1e21309c4bbf46e0658f840e0ab898336be263a92ec82d681931598f4850130305b84f5d7023994cf6d8004be35194a817e69e62b80ff9a51b2b61ab79ffa5ca092cc14c88ff401fd228179cb5fa1e1c218d5c09af1953b16bc17a14a7eb84d55ed59bdb680b847422111aa21859db7fff960e9539a3a6d48a3568ad313ee3726d10af65680e1575a1fca2efb9c26e58b000122512572cbfc211efb65a4bd35d9526866d74a9131bab92ef6b2c50d39f7219999dfb72f6dbc8bafb51d28d65f7129990d4f00777e405658e1d7685709bfad5b07edb3e5b68aad89d4ae370ca0a3a9dab8c7fcd86d39cf907f076b56fd9886a37039265e2da0d71895a9f17e43cd1dc017b32a56078d98d6640d7487a5c19800b812300959861fed99e858e531c23e229115d1d3a99e0803399026872ad5d28a3b1eed4b8613bd17b40b6d412a8e3889359e60a9acdb396252a7a2a7959170a31c5cb44be179de9b9deab9de64e3034a3f06fd178364060ec66c125b4ebcddeb630bdeb5e8bba4c4510809ea07b461aedf5ab5971fda46a0ba8b2099aef401e8fff0b9df91b9c15b4d983b5cc7ca61e45698c4b64f5c391a55ae4586d49ad579314a622c46d09022c29661d9fc37e592f242f2d69764e1040e22c9340af1c5897a0ad053c22d127bfed7aecbfcaf518d4bac4e4d66991523b5f0bd1ad7ea581ef02c6ca327c9b288bc04cd55a7a0711b060b35cbc21af5552caa3b1c5b3cc8d0daf02cb082e2156eb296b40b4ac1994f619205031914e96bab41005279a6d0190e56b800b5d75d8422f878d63ad98a321b9eba8d1ccfb410d06281b0edf42f5d35903304ed1a4d2014116516433c5891c3a5c8a0baf803319089e63742698865f997e49cfb548cc0701cfc908ecfb48ddd466473dedc8c85cbf4c76ff4c89999accc70376e66df37e296611e664287f9b55656a5194377874219dee61cbf757476716737239a99a1a79d7b9121a2cabe72636ca558cde1101af4c6b629d54110a1b0294714e12b1681975ed96e401fa8d25e7a311e00971d3eebb20e5c27debbec469a6b7cd53ba110c3be0a744e5d7bfc1c6624fb871cd3c7cf49aca0cfd84bdb58c466a6dd6f241dc766308fb41d96b289aab689fd2be3b8188717acfbfb511bf4a7be53eff9e3e9c230c38d27d22c4a52e8ecc483823e897ac1e33dbec016a136152aca44a15c8a9aebb233fd81e137fc5dcf8374124177646b5b325feb0f91f8fe881e23c08f6ea543762e7eeb7e6155a589eeed3771d0969769078025cca6e4594705e11ce0b24fb4821fac5257204dfe297e1b84fd82eb910f9ec5152253d266af110b4ec44924547a6181f8a8e3af8be3b61eda8b8ebd5a4dbc344f3e3eab283eac3faa349ff925a61479dcfa9b8aca2dfa4059ddc2a05529a503a7ca47240981bc784697e9380ce317890d2afc771f54bd4119c8495c5e0bc4eb8d03403f5a9e50a5b2225aa579fcde9c75c5dcde1b824d042d7d703078eb6638195abe801a2c765b099eea38a3fe636e018d28052cb75b314ee2a0a7cf584040c3dff72f17dd0e09dd59185342fe86f0fcfd60f50f0781b9f522c1b37bd52dd968e809e6e6a9103ef7ff140df37666d450d5ad5adc0e297756042c0144a4c890af52fd81e2737083f33430c99adb5e500abe9e671812c4c7e393eba9ba21e5c7c82a39b3350482fd9ca646d285750305a8cbffbc1310059e8d628f50f3874ae2aa4ed286218cf7242ce18d3b0736d485d895ba257537571de20e6780fb41f081f10132f5b4c7fedca37e561c1f5309df9753ef90869ff3f23edcaad7d6fd11bae6a94aba051b39a6dbf8c9b74b6830aa89d441fc629257aa84ca78be4c941e52715bcd20398d0219468cf724bcc0bc1b4ca85ac7cf8266a85152bb433f046a415076637fe57838d05da5691ac8d53b80b831382d4af6ce3d320be65eaf38095882930084c04200161c0d4804f01668e40ad6720f11687206dd5fe4f6cd929ff0dbb5b180eb87ede37fb4057c46ab5b566280027dc954d5e846b7eefc8e409ef4c4cbfa58d4691c0ff933c2dd2b5c87dacdde2acbd6341946bb50d509dbea442b3598dec0814098ded493bec25f0ddfe155fb9a72a2652e4d2cfe8ee012d2f7dff6dc83d7017071d83d1858a6ca2af5f2dbc480534a2ca7e379982ce3b68646acc0a1c4e3258bea243f02f3204b1e1114aff857281effeec2610da2f47d8200b10c4df68ec81e18f1726587f003912f354ae05af870b1c9261d9b4d71ebdb02b362cc98047744472611f7328a236bc4c98ee9639978f0053d06464c5c66801266223ea4b3f4751a45018a09a66ceb0a5c4df0600646610a8f13fbf5e3c8a63b86a02e2dea4023ddad1e8e34dde07368344aa6f66e5bdbd55ea7205a12da921a900c282df81ab50d49e10cdf545a822f6b18653cda1b4ae05398a11be479f9bf9cb738fedff9a0f812319c11d514943106774415f415b852011486d474687531961ab76d574c24e001989b2041aab8054fed36d78d312b4af8c33606d29e8d917b5ff75519a40a010eef5e276e0dcaedfeab9c07521cc895c19b03971f0d24fe5e800bedb0ed9d299e0c9c9a18e01ce8084366ffb6800e2ff2b500c958b7a2d426ff2bd45771d4ac357f7560cefac6c385e4700050a08759d511b4f97992de2519a545aedc938e96e97a20c73b27f37077fa4bfcf2bfa0ec93539532b259a65d506f4872ebde5d798835d88e6f1b2042c9b0f2718f631f2f926106ea3e82a0b26eb97197142765a836f3dae5c05bed1b68ea0f9e4011a6b026f60776492f18bafb6acb52f0d50dff6e771a0148f02aa1214744bf2e267cec8e107c81e1caf5d49a72419ca69905897c6108fcf09291912aedcd5ef436cca2e788610ebb36fa2322f0417a39a1eff21a6386c315277ac34d711aa4110f00199cafcac3e800a79c5409472d1fe681bfdbf7a055746bd9ed4be42608983f39f793b41bca0fd8c528d465125bba24a500bb1d91b72d338077ce98b229628f6a5c8a65217752998b8701756866b72d938c0fc3c23b7e22f151f26bf77dfaeffca2fe6cffca9fa36ff417faa07c587fac3fb7efe1dfd0aff00997256b4000a1fae43ce56ae7febc9273999b6df4dce67eb124839db21d4107893d24f3b874e6e80e7407c8a2cc02f03a5853061a9e0b8d5f6a52f0c48c77c9ef40c5af24104d5a5134562a7319c901283f163103e5f87f15a13c5e159029d46b2c1054f22737d89cbc8bc59b06029680b170aff01d86152022cdfb39e1c5e619febca85f875e1a2ff56fb177ab57ab37c03ba86fbd8ab79b83b4bbe7e86cfc4075375befed25f8eb0c0cf4f2f75abcb5ee0cd53d4f8fb24e1dbd73f49e73070b329d6e36eb4e4e62bf163a75715d8f2e882ea11be89e747db3fb9a3451d5e97891773dbdcabd01de59fdd60bd2a30404d63d070cdad54dddd7bebbb596b3b7e3b60b05ac8ba6abd785e83edd715d9b6ed2bdeb567439ba6fdebdcb3d0d90600c66eb35f046bef7771378b9dfe95e062f3276eb126e7f1afcbccbe941546279ef37efddef95b71ee14ece56e1ddd7e62033c06f09efce97b83be0f59d6e1d21451a9a19de5e929b875e43ef5caf8a37e92ba4ddbaee7611190befb997d019a2ae97ef3631a818c816d9d5f0f2fea2f50a7a21bd6b5eacbbbcde76ef63af8a77af6f9dbd71fc4b7f9c2bedbb91fc6e5cc34be62ace0e0365a0d74820c25bc6b573aa2c85241bf3d70ad666219b3a527d3b857165cc06857d0db70cb11e168cdc6fe1efe37bdde3b2e772a4d1ca8974c5ec6eb1b81a371aa5e18f99524a429a318fa8ac19f0cc6e85cf70e5d334aace1eeade860bae60bbbcd014f7b4427a65cefcbd104ff0898cde623bb0f7988a7dc8d118f81e8b4256ce248989497ef88c6d853117ba0bf6908ccec1e80d8dfb01bc3818f78cd9106349a9e78af2a871393618198c641a8b3de6defe96025fc72fa37702758e7e0ccec669af705169331b6bdd85f50eee6713ea499f6b167f8e3aeb119273de9eb05ff70368cf4109ec12a36344d359d0b1e8b0d185364d440298ae4d2755b1f1a82ed725e05968a39b90ee9f5802adaa9703f586be82f714a87f74094e6e29bd99aad083063d739ace72d668294fe8160f5f0a2b6ce9db3be76495744e6c7245f906e624fc78529cb0783310f02d94d196204d5d2efe4636a7e22eea2e6839c0c7b1cf3127aaa183c13ad4512b878915cd6c5dc001c586c6a2c08e46c060359ecb4410c7d895251391b887129455212fbc4ef2af8652c128120fd7ba145b71834b0a00e4965516bcc8c50f2245dff2a651bc656b88f1ca7823dd550e8f85d1b37fc98596468c26be69a07f37e2d95b2872e7c08997d8be28c09fe411221c1222b758331844082b1adc781a453a2178a9434259d609d11a28e5f4dc5c6a96ea7632931c5746f8e2a0cc50204e73f8d4079fd29f82be9a9462af4e8de2c512e8c4a4c6f0651bba286902c157a04e33849976f4e27c284d0287e272c3747c295d09510133566e0e7637f066651f83aece5ba06fad102dfe8d2f30399cdb268faa9f0f4e6bb8f25bcfacc309d3c034d818c8825e198675db81fe040c217e3086a028340d5e0e528cb0f9adfd7e140a9b79ca5169d80d46ee0b7ba4db831d02c78301017c609d204cd822f01f202444840331d8201fcf949c0e10c21574825d007dcc8c6ac7b989b93f2143ae89f8a2350806c76002823a64867414db238178d936dd87fcb127388fe4e7dd40f1e021e14410186c0e2e840c03c36067003e804e18e857b2c396f913678357ac20d033d4ef8fcbc8396396d62e1136de0c105a9a8997b1246a8c1f843a288cab1e9dd4e8985e2b40f95ede8c734a5b471f115bb3abffe63354d73b59000ea0005801a076408b020eb018c04b1857a45b9ad484ed468b29805d7dbfdae70045001b000d70080284b37bea0913a001e6ac004cfb20d7cf01b48844968d7a80510068b99ed3574b80379831acabf314a78acc60aaa757e0349a02a0067800360029c003700990250081257b1a800ad0000200a600a0407b00863ca8a9ef94628e5932624b4fc4dcdccfb279e228437815403cd93279fd367850b566476cb31688146af05551e8f775edc2e5e0136a0b7f6dd979342cb421a1249c3d030363bc9e47e411647073542e726aa003fb5f6daab89910fc0016db731a12c3df4adc0d7bf34446450c735c850e783b487e23b6257652bfd3e1a5d94c692c0452a03ef0c55de2d20a20089c470f5585185a83772f061903da806970198c19d806b9c1c9a036601bfc0da0c36043e2a10eac0880070e0931c2c541408244383cc08fc0596825cc234816a612e6133a0419bcf09961f32b6c3612d369ed0a1b1f0259d3df12665439e5cf264437c226f0bb2e41b6d9bdb60793f596eb89f2012504e10227612a613e840d2b33c66a36d8980f9f2060184ca6ebe4cb9104b918f40d7e06a7816070d40d167dbeb4d119026da07c204938271c4808110c839b034a02ba22ccb41d80c6258141252c838ced100682c080a283990bb25f4a3a6184b0b129885e6315852e19dc0cde068cc164e035d8a6c13e7215bb08b8c1d56065103290548345274017011a98061f83b00169f019e00df80c9ec1dec0131a4cd6eded37be816330e5a043600c1ecb60196ea0b7f80c3e83c671b0cfed666e0883968130c83af03cc20c6b2d76664ea0778338d433eeeefe6a650ad06d7442081b0a2bf7b26b6019cc0d60069f81de203198c76005c3762806d400763042d01b9c068b413e7c305947b975cec077409c504fb8247887304332b0d403399a7903a035b81bf6f65ebd26300e17a67b38b8e5057ea133209083c95abeb4a80543c882b03bb04ee84380165e3a61e3a2b2307e25c4ea819612cb6a50b5de278a114aa791ed99d066f8ba1324834e30d807e574fd35c2c1603dd849701f61063530ec81f9304802fdbd78cc28d839507b0a64d7b76dead53e7c93d33402660e591c6697dd3073be96c0c4840dc710dd6b21ec7bf42218c5081816b8846b04182b6cfe7c6abfbe1e2c93dfc28fd3a01a486307bbfb05f672128013ec08c560fea07b029370432019700f7e12a6125c803043cb430e6e066d72b01524dabd403de89cd040380656038a034c420fe1881a2c079e44c268100c9848c6206ccc029e7744b70b3440b0a6d0013c0c3c0c3c0c3c0cac4dfcd6d7adcdbc05996492d3d03e5508ebba24d39629a5e44de39f441a5167ca13b7ef90a20b3e0b620b2d0c962cc168ea5042e36e890eaa04a7aaa593888ac95644096e93a47b1da249b02f1e9398b3c916fd4882b11cf53befe2dec9468293cecdb593e5a43406128c9d6758fe08de84ac50e23133491a4730b2bff67b4735c66823b80e96a4549f3ce89d11ac2621fa26f97d3c4916c1c85c79d36715a1639228020b3412c146d58c2d523bb9668b091a88e0845276a3a5520aba840cc149328fdafc4107d3642138f194e89e5249085ef7db2a4cea5c264c06c1c7cdd7355a4170a952bcb4e992283a0b041fcbd3880e566db295008293398b49ca740cbaf407be45d3c9d3d9a4e1072e7484be6a2bd349a8d0e80327cdf3aa72cbbf4585061f52a204d11e78b5936212da04b7def5c027bd5e591ae3eb79f2c0b9c93a2a25cb0a428b073ec73f93df62eec099a4dc846c91164bb4d881ad9ce4bf53293b9aa5d481b394cf045d5da1035b72eeec41e5e53db1320736793e5125b33edf2a7260d53f74964a2194928f03634ac558caec24994170602beb85d226e80d6cd612af5cc93d9bee06fea2978d09253a9da70d6c55fa3ff71cd52d1bb8ceb1e289a9e1ed1a788bf9645633f92566d4c09b9d0c5aba53a8a434703aa58c5ebac43249140d7c2e1399b1fc5a058d33f0d71fb35745cd5065d028039bd45c4d59493fbaa14106ce4f4c82b6335db1928c8193dd524ebef99a938a18189d529434794278520903376e826ec8a47bb1020646e8bff760514db3f20576a345cf5fb526c5bdb0c924664cd7a982a834bac05aa7ed9d9bc905b6c3ec735f474f4a32b790b058d94665ec58f3911c1468688135e982c864f23f7efcb97103ef7bec7014a0e9096864416960814d2d9a84436d40e30a5cae2a254cb6b05c41293b41c30adc8a0ca1429355e0a44aeeae1e4fb60c15f82c393b2679e9e49429b06351439a1254e511a5c0a718e29615ba89058d28f06149f449b1a4072507853328f58ba6d913381f25a7fd3cb3d22b9dc07e284f269324a4d104ce6de4c8ac1233a98f3498c0bb5a8aa9a950ea8290c612f8a0354b0c1f1d6928812d31689de4f9bfe41ca4910446b4944a761eb3de83349060e58b9a96fe92a0a1048d23f041a8eeeb99badf46e0d4e5527277c84b825b04766b3449276e6810816b93b32839b35892541a436033a8b74e2767be6c823484c0092583ca6af6b1dec460b0e9979359bb8a27210283ed2b1364c98ef71de22fd890222bcc8367141dfa8237df2bf590a5733fec052b723385282595480d79c189799fb4c9c25d70d993a4b7478b09c2a40beeb7d35b29cbae752ef87cb257caec6d0f175c6a2db16bafa4995b7082494d495473ebcadb82dd944f5fcde3a6a45af027c9f1f2d4346dd382cdc92e09fab45970a2c49cb26d9e8d5e65b260544c4c9a4c932679c782cd5ea2b299c792731616fc5f9f3ceaaaa49cde5770f2c7f3fcd3189e5d57f0efb629c6dc69dd6d2bb83ca61f73b97e3e1956704295d0a7eff45b8959057b3ac850f7f14d8919557019a47890f977e6692a18e5317a55aecaa74c50c1e9283165916ad9c4cb29385139dfed56fcd836de1c6698a21ab74c25774ac1bfc53a3d3284126c52f0194b4dcc1b26ad2aa3e04ecce4891fa46d2e0a2e7fcc5a3b43c1fe77b2cda3f2c70f0f14f96776cd7fd61f1a9654ccf804d73b4a3f5b48b3b3be5829667882cf3e667292c245e5ad139c6993947c0f4baa63c7093ee978176c2c5797ea36c1c74b2568935212baadd3045f2908cda27e1ed7ba4cf0de991f4dffa44b4a9860f427a1bbd16d34275d82cf24c99f6926b304f71a969dec64afafd13c07d231a3128ceaf50d4a4c929c5344093e8eba8c26e7754a8226c1c88f67490a133eb64b82af24c9a2936e8bbdf148303a9b78565996d9198343518e1c7cc283478b51823052d08112842146182708438c32cc8004dfc9e47437717b04a32f0893692b5f996e8ee0e4f26c92bdbaa735c146307aca2e7b5da8ce1d46b09e97359d9caabed1457092e82cf182cecda32a821375e4c9b2745bea2411fc65f1a444ab99a42622f8cf49b6fcfb9d9f3e04e7c12f588ee3bf1a34041b324ff4e5fdcf275808c6dc3b644992a426a58c109c868ad18427a9412b1b049bfc4af7e5310982df18e36ba9a4626e522038f133e3552725dff980e0a2668ff1cdff037bc2734776cc6d52ee074e8a29da6ba6a566b0933e70c2a2c6d1d896da1932767f98c1073eeba25a4ab2cbafde031fc5b48a77a7b09cd1036f9ed5a3e612b4df9a074666502553795a0dfab9c30c3c70d9fc45c9655228612e72ac4cecc0757ebf3f9d2ae536333131a30e9c92aa7ed3473d4154e8c07930f76d171b799739f09ae42e214d6a911bad1972e04b9d2a617789c30c38f09ea4b8c94f75d09f7c03a374c926ed348de8b86e0262861bf84b7dd59777a23ad886196cb8e42b49ce11266a41d4812366ac81f3f04cfbd22625d1a9810f4a4a6e31b865ed581ab8f4974a4bbbc69224e9f230030d7c75fcb55862859faa70a8253646e3193831b59ff44ae357e29a81f3244f4f652769fbb932709973a38ac6d3a5a52123470af430830ced57dc8b5b23f21f65f0d091e387ff188fa76387f6983106c67ce3da07999a536862e0eaf429c1537ac3c06bcc7623c5dc5b93e817668081d39a3fafc392dceecd17784baaf3dbcbe2c20c2f70726bf00a42755abdde05764cf60e3a5656a730830bfc7ff6944e10da63f23e29ccd8026b9a4dd2dadc5962120bc30c2d70e6257e3491ba176664811f73f75329699bb51d988105f6d4e5d49583c7d28db760c61558cb65a95c533251db92608615d8927d2a788cfbd6f72883c78d31a30a7c3c73511ffda702bbbb194cfe98e050433a72a462831953e0dfe4f65c2e2e0fd2bf1418b96aa2544d5dcad1db30230a7cb25fdd4cf96453a33dda3052d0013d28f8c172aa3cb1242d42cc78022727e9d3a2b6a6248e57c68f6ec20c2770620cba9bcda44a98d104d6729a96d82787093398c098ccff1f732651d641e6862561c61238997276cb234d9e28cb8e8485194ae0ea358a097e3fba1a9b9104de2435a26bb44bc30c242c5adf5e6742bea80c338e60caa51d2645cd124c439b602769ed1c05c30c23b0d7f76be1fe6e9efc22b0b6299e2df849dfbc89c088f0fce9639b9ad664087ce7934491d79f59af324308dc99da92fc244f30182f292993c9eac4cb3b301851974ef4a5a73cb9f30bde7352ba93abc6177c99e4375ec9e2e68fe905278752925c25575ef0f93944e9135a6de4ba0b4e68d2a3b3094a74c1f787e79283fe93e44bc905772f6a479f2c5d192db8e03caf5349eae52dd89393d063c24b7d8ecc169c9824359922a3e8cdc95ab0dba5b6b172df9b9a169c261551aa2bc876cd2c58f724f707e15ff275ad031f3774ece0d13a40810e12981861d48fc1a38c06c882fbbe8d232d9b30d9772c181f8bb9531a008bc4b29b2497a42727a5bfc224be6582d0ef49da159e7c63622a59cd5664f2077da2e9ac398eac3899b6246d7ad5ad0c1f377057c197144b52b92296f4d3d28406a882931a524f0ce9c9a42fa582cb5e1dcb6ce38dd98f0a763d4591ba1e2abaf753f09baec24c16edb6256e0ad6838b3c3998f5e7f69482f3bb54cb4a4d273587149c88558799e0a32cec51b015f54b4ebb39dda64614ec987496a56a28b8d4494e6232f9ddc80b28d8ecd027b54575f03c7890821ecf8347f76081111af009fe4dcbd5629027384b4227758237b54a5983e70de0047f9725a65841b34f256d8291163a86e8ce35c1297dd5e0f17fc73fcb046b4a450fa5d2f6631e138c108f79b3aae8e827e9126cee7fca0aaffbca794bf0ea174a8a3b9e4af0a572ca11327593983594e0f2c63b39eae67fea49706eea452d8a9262ad24d811919e7fcd62f84623c178d4d7101b51728a20c1979249ffc494fa43e71ec17eeea44decb48e60df4451a2fe08a14a5d0d6804632926ef8b41c4083628d9c4cf6ab2b4a22868c02218abbc92f4d7679e8e14c1a994c44d72ca5c17539408be74c92ee527c898b70a235d34a54c8d2cc1ef6530b9cb729a68f24a704266beba730f253811619622664af5497160129ce5fda714111e4f46129c0a9e8470b1ce1db144821725df4dec5d4ba80f0412fcb76ec7f44b52df57208f60bd3ee92ddde9691c3e72e828838741418f1d1c30012c02c4115c2e49cea74193ea74231df80e6d04a374ca70bf7ce8c841869e0f1046f09d37476f074f693d090eb51e3dfe068f26431764115c86a693d5493f8a8970a8dd50d3914304990344115ced7dc98ca22f076d72e373d03ed08c0a4012c1a6bbff52d21be87ffcd091a37b203b1d8020824b4a988ab13f49728f6a0b9043304aff88b76e6188c10a1043b09a54a576665b082e49c1e4ec133355b5150a1042704198f6961c3a259db1417059bbbe7b4a86132082603ff9a7206c3b9d091a1d65eca0044820184fcd59baa485fab050122080e0a4f509ffb63e53a74104c81f7ad74d27e3c80fac8e2e557b29ee49361904481f78535a2a5ebfa160f700c20776773c59fa0a9e53529903c81e58cbcd3926a9a0ed4a5a87067c348e12d801440fc9936b437cd5b28203903c249a1853df01081ed85319ab36454bb2575f40eec0e51cb373ccc14daded4c4f80d881edd0496436258543edc7fb3865f8b8d18663e4f85146d681379196a34a929553376e58b209103ab07963462bd14aa7b18e473990397016d59224b8c83751397840e4c0ba65101384bc6c2708e5b8c1831f3a3a073fc6f7b86b9038f023c4c45249ee9cba9101081cf89cfa4c49afa6e29edc486000f206b6a44e49d0315da154d60d6cca6f5fa34ef24d17d31c5cf2029036b0f99e1d4ad7ea081036b09a9d73a865d1c9b2ee9808903570298666932bc858524c1b4b80a8813b75725f729335d9ba8b0b9034b019c2d35452b2a6e97d472f40d0708eb77b42c9ac39433a733a77fd1e6f8098810fd966a34b2c65606ff3e6b6b1cb3565ca01840cece65cb7eec1635079f3fd02c818b87c6fa59fe4741db59476ece851823a31f029a54a297ff02eeda91cf9c3478e10a0ffd163070a7cbc0f159402240cbca8d378a743fbb49f80815182d6f50e356a4afd021b6410bfcc9233880e03205ee0a49427a8cb495da9a959f009902e70c9c642e8d22ed964c805decbafcf3573d01f3b1ab061432d12205be05492ea4efad169729203d102bb514d56ced6affc570740b2c09ed4de963ea655651c040b8caadfb14cb9de9f2f03c81538a11b6ef2999845e8e6060ede879ae1008815b864b1aeb5d54d5a1dfd8e1f8a3eb40aec49ee6ed2bef8bba6763b5670e702102ab09f94bcf1f7205fede4861640a6c0a6ac7f92dce51eccb3366cec0d2c804881b5169344cb6c13f2b1e387824481ed1e252f6c93fc2a323700814295a5f299ec9b82fe06fd030548049027b0eb49ca166ee238d46ef0a0c78f1218fa30ab018813d8bb0c1d409ac0868abb214ddcaf8a291b35d091a3063a729838803081f3edd594246142f529c11e75005902a35a53a614b7246d9267c7184902c3118028c14c777629345bdef4469a6d8f5f9024f0a13b9f8ed1fa242979923b8020813ddbffb3cb5162ced311d87442ababe60f16cc64044e446f2c8effef054dfa02a408eca7b374c2a398203a2270655f7552546915eb4186c05a2a131332e6da8f8d437b7c42cb0211026762c94b9deb1f7b8482c18b522738d4d006fa30c408438c303c033c701b366e945d2f1c000cfe43a5b5b20e8ff9f52f38ad4abad2fee45391f1059bf62453f064740f0ed8b031465b2fb854523cd1d6964fb88517ecc7f64c2926bb0bf6dc6e2d944c7173864c060ed005af795a83cc7bd1b1c3e80a76a42059c16b0e6cd8b0e1805c7059f5fa3b9f20754db8602bb38bf230dd820bfd61bfa93cbb09d982bf6ca13ef7e824ca530bceb6e4e86e9baa849a167c10999efcb684f27716c9cb1a73a69895059b4952e529e717f54c2c1821577bc36df75f58b05a6a2f6e4c32a78cbe82d5fda4444b962bf80fa9d94c4ad20a3e68eaae7553e399c20a4ed05df29992bf56e52ab83777cffbb9af27a80a36a47fec6892782ec1940ab6abb582da7472474ca8480755b2bf662c9d824f6da3593d499b4fa66094e8fa29b4e49125540aee4e29318b8814fc2795a4e07ea26d39a3e0a48da7366a2d0a4ee8ca98a575d99336147ccc9d4bb54cfa681a5070a9c4e49b2c66c7019f60e4d726d5ee95ad4a3cc1a567da942d669a7782fb6c6665ab27d4979ce0f47b12d362aa4e1137c19db463194c50326df69a6034a85025cdfa4c702698eb47ab8e31acc704bfb142d5a5d8da31e7b41c7009c6456505fbcfd9b46f09ee6e74fab4a24af0490a2a968eb9a2a6204a7055e9217cd44cd43e09764c7e4cd1e48a345949f031ad5a6e93a4dc7223c17b5a6edfca7184d04082f10cfbccb83e824d5a9e49c8b89a36ea082e9a581e75268de0f37e0e0d39a6e1298ce03efdc99654052d82ef20ae1df2838f522245f0a54c728da574899e4489605bf55d3c88e074693593e1a5748d87e0ce3a8f0629d252c5107c8a9e6f264d3a31af106c8dfeeaa90c328b0a1182b7782abdc8fabd3e0d82937c7412efdefadf17041fa47a285dd1d43b0f04172bac83ad05217340b027f8757709fa1f3e3e070ef80327e35bd0a692e4dfb61f18dfd357bf0f9ca035babef8dd5ecb07c67e3fb58ecc1ef84ed32c71a407ceae629dd0ea6cca03976a694ce82579658207c63ce8933a560775953bf079326f4e1e3bf075415bc537ebc07ace13c264d01d4007ee4c85bc24733e3bd11d30074edcbcfb99ba7389cc1d2007c6a44d933deaa978ee71401c3821f367d3397338f031d47b8d5062fc36bd81516b4a7e4ca36d16e406fe46e9d63c1363babd0dfc655139aa7336b09b5f25ba41b532be064e4cd00c9d6412847dd4c0ed9858b0368d3189a6e1aad1a059538a0646f7abe468a69d7d39031b2d55faf599c59262063ee53be9a9c93270f92e5959289341956460ff524e791dafe278670c6c6ebd492645ed3cd9110327e231492663270cdc49a75ffd4785d6ea8081ff4d26655a12ca46a9bfc0c82064073511769fd45e60a38ec8a0493a59b9d45de047e5d51a9961b1ffe6029bf44c4de50fa14e8b0cfa4824e1802d701b7a416e4a62d4d16b810dd7d2d5a0d4a3fa59e0b74ec68cd56381cd329d43e465f6a02bf07f23d4da655341075981b70df66e59af02eff6c1e44beaa602e7398f9fcaa5baa1ed2970252fc91774ce13f2d252e0939befae49d923941d057ed3a7784aa91a0a7cd01b16ade468e6543f81f798a4a5e80a9affe40436a81cc3ed04396a4f4d604b9026fd5dc610556202970411d56beda1dd5f426f4992570257aa437ece124fba93c07fa7263b4b6fd24448e07cdf9457d6cafc089c89d9635f68bfef8dc099f4e3ada66392c4bd085cd44b9d729299543880085c7eb38cb17a3d95d60143e024991ac3dcf5da5c070881af13f116d9ddb1da0d069b3b4d544b122a99bac0e0f3424793d3aac83ed95f301a4b6dd46865298aeb0bf662daecabdff682cfe725779b92b2c46d79c198dc21ffaae42427fd2eb8de7a8d9bc41cf27e5d70dfa5b3b67663879f0bb6fb52286d4d2b13c2452687ab7774d12d385da73df34b83d0215b704135a8e059ad5f3ab56094fe7862c7cad5e1a105d79b3a4c8c496eb1ec2cb812ad19c392dc599705279a297b7d54f3a089059ba4c9a99d4c56bb0c2cf8249a9c83da5c7d5dbe820b558d1b9a5f455f9596a0e10a2e26e9275aeb3a5334ad602d6e0e4246f51cb36658c1fa08e55b23c355b07eb209161aac4c7d55b055e9154d253996284a05179a4acc24392ab853def5224597fc4fc10795d37743c70f8d29f8349eb3ae5b9e474bc19a9a955a97b2dc0e29f84e3ab37f7f346f47c1f8a6fcb24b2b0d51709ed18312d326213a46698482eb9875d35510e129a33440c19ada91e6714cb8892a8d4fb0a3f5e45dbcdf3551a5e1094e0a326e7a8edea9c4687482d1267f4c69514e706757efa219f3ada99be0d38269ee9910fd996a82bfa42ab782da9333a599e0cead7cb74d7b4a29c5047765194ad0a64b7051f4653c93c6dc9b50e0b256a950593aed3ce70791277015d3b9dfaec8098cba922989e64c2b95d38492de4b6207b36b0f224c6084454b973ea618367c882cc104224ae0d3c6dd98bf61f1b22e1e2249e0eaa2e7e69c2ac68f3b0eb52282044675d4742a3c3d3b96e050d381c8111825563439784e278949fe79249a8f05818811f84fd5226d4c501b998243edc60e64c5072245e03ce46797e41b3ce989c09d7b2c57f5bb8e3169088c3c1d3ba407e19559171182296d4a6a9d3cc160e36bcea31f3bd5540d0c3e878efae9f81a1adb5f30a226e6d2a3e4e5a04f7cc1e86df224e66f1d2593bd388e07a1f3551c515083178ca66e496971dd00076cd8f8319281448d5df0a2af64f44cd205174f7ab2a4e4ea859f0bd64fa93031b93532967091d037e9d641ff5b30322695c1cd04d9824d266d072bd3649de46bc18d59884a3a87097942b460e408af4bd5b13f799f05f72596a81d43e347d165c19e8e49a93758e8744f2cb8ca360dd629ad85be61c1c594ebd2e9e78f19ed577063fa22538caf2bf8b498eefa62eefa7a5bc15bdf8ea796969cc76505379e4992641b2585f29855309692ac96bf39aae02e3da3e6b849f48752c1f9080f6e7ad544e6a082d3b4dbfd62d253f89d69632c7526a6c0d2d65d36192aa5e0533ecbfa18e3a9b714527099df2ca50b4d8d3ad61805234fbbfef92745c1e8a7fcdca2cf24992443c1554cbe95735efd354950f09f5344736c0d329bf0135ce8d0fcce1ff4045b395e657fb5d4de6227d80dad5c163da6cf0d72822f49e53c2698123d4f7013fc055552faa43de87e4e137c0c91ee55e3f7a5bd4cf02725298e994962af394cf06939fa62ce5c823539abfb9bc9a6df9d2538592a3aaffb54da5d094e4caf59cc4e98a42625383b319e2c3de1968493e0c41c2fea7626094e50b2c935421409fed2e73249294bcb3524f8283672a389d193245a38d47494d123ed118c56b98d1a9daf7f47477027982a193c8b8dce4f2318dd62aa26985785a83082b70e6a774dbbff7f76118cd071d426bdba22584d927439f2ff922ed144705fd97b92fabb458b228213e36f7bccde92772687e0ce544a1ea326a5f74f43702d7e621453df154db04621b8acf427878810bcc953f953532a932c85438d070f8231da7784c0778cb163f4e320f8f85dd194ea917ea31a82e0b2c432319b7725133502c18919eb3ed7aeaf0b084eeaecb294e63a3a6abc448d3ff0a1b2494ac7ca1d744cfdc0558956dad4ea21abb5448d3e5cab9693505e92207a53830f9c784149f28999e3503339ba043e74e4e81f3ec8b81a7be0a44b7d2757ec14256ae881f34ba2bd72f5c9f410d70c35f2c0a8379deef17227f9cdbd49d4c0438d3b70ae3178fa9e6aaf5cfb7bfc40418fff31dac550c30e9c98b7e4a649413de524c90a76a4e04560c346f2861a756037b9e5f1d0bc46a2061df892731aed3625a6fa926ba831074e2ed1823e935392a7a46bc8814fd57a1135e2c05ad26954104f99be92706063ae58d2985935dec05ec778994bdff8c9478f1ce9c68f1c63f0f061e6637dac076ab881d50e4b3d4b511c6a64f80e6e201defc3688d367069576dda6e5f49df5ea8c1062e28f10cb6fb5595693276f88f3248906ca1c61a38d10c99741cff98afe7420d35f0e6f153e70f7da6c54b83963f98c95da28487434dc9f016dca2061ad8f5f73c4998c8933052d0010d88408c305810460a421046d638039fa6c1f394ae92623a1c6a7664780bbed00435ccc09b9cfbff464b12735296815152965de5b714d2c431d420032b4aa7249a244d63e0469352a293a8ba77480c6c98862c952b7b1a1907438d3024e75b8c0103174268cafcbf5143a9260b35bec056efa930b5bc17d81a9326ef49522955638d2e703e5e82974cf209fa9a26d4e0022f9a4c4cb27aca16d8b09444587b92dbf6d402dfbad72b4293184d7a5960c4b3a69493e5cf582650a881853fc9622532c7ce50e30a9ca4a4646e52f95ea69215d850a3eb4e92b26609aa029796c2bc4653f4efa4022f2a8959f9bffd4e48a7c0e54bb9fcbbcf043d1929f0ffdafbbde246811152fcf6ce63ab09322870224b92afe2fb2731b927b021849f20476f4e6053ead89525d9bd046113b8ffbe24a50da1b3dac604b6bd2441c7a44674ca9425307a349bec2564a7125325f019b5945827a6f6932a097c901e363af675d62e41025fd5a92a566a643699237096513fc95fea1a46e0a493a4969c775af5536a1481cd59deeff7a62da72422f07e26ad49efe3501b03d5180297af4daa53a9a9210456efcbc4de4c06834d5236d1434781c19a549bfab972cc5bf3177c3e256faa989e84f2135f70d2552519d3bc54e8d60b4da7355d7a53c40bf69385d0e6a1fe2af8bbe09424692551ab4ed4ed75c1c7743a7ea8a04c565273c1c5ecf2ee8a49d76b0c178ca57d92d49a8eba93bd05f77973326171640bde3cf52993c4249b68ca5af0dd5782b4e0f4b9491bb36ed221fc5930a25373559294820891056f365631061df3794eb91c1c194262c157d0ea776a3308f9292cf8b4a5c96292db4484bc82919f469924ba497a3b5dc1c5de6cababa3712f492b6c0621ac60945e658ec9f362d2dc87ac82ed91565243e7c6dd2144158c5a4b6551ad82d636157cf64e7143a80dab78082a582d2956e7924b424ec17af274310926640a4e9f24b8b929a1b42c1d21a52843e7a0b3b796d2e1c1df20ad64134248c15892547d7da7d44b49aa10320a5eb54f673cb929448828d8dc55cf49304197322d0b21a1e0b37e92d726090f214fa0606c44b76bd569bf2df904fb9de40bbae2f59e4ef1045f923ee9446fd9092e9d8e1e64658db17ce404b7aaf74147fb0c316d82abeece599b2d0e144d70419499942c893542da99e0e434951a2aa568ca144c30e241c52f593de5f3521222e4126ca518633e559324a5d496e0a4a0f23796076d3a2d95e0648fa37247b1e4d53525d8f11c93db93d0bf10854c827d4fa5f6829b982c953052d0011d8f822f03073974a800c78f1c2cf851c68e1e366c502144125c67adfa553c1d8f9748683a752621541248707a8209b5a44f89a15b0b44c82338f983f2ffa09395961447304258671e21930011d2084669e7dd710de2504b3b14878ff6406a0823b8e829ed7470cf95565a04ab5e67d79b92f59bab08468e59a76029329b894904d723a679744f29b95410c1c5ace9c430a143f0258690259fdca2653104277e3206bb52eb5116828b31a6ca9c2284d22924049764514147d7b1d3a80d82efb02b99daad5652b28c10417092ec6f4ac9b182456f4302c1dd88bb496dffd974c90d238400823795597143de7f60cb634a924aaae2871f217de0febfa397e42e21840fbc058fa12c8910ded00d7821640fc9a949348d5e22aac135e89b5c319909bd64f941881ed8f4efe1d726a5acebe40742f2c06d2a7157ed452fbd28040f3342ee1046881d2aa40e638cd162d0610e140891830442e27023078f0c94a046081cde75ecd07123070f0784bc2124c40d3e78f037100969838fcf91019110369490358411a2860384a4010d3342ce7023012166904048194c08196efc6040c8187c7c0e901031841112869010303820e40b0808f142dab1a3078f2edc48fd632420840b3f76ece85180902d4020440b4948167ebc8f1b58b8918347067890fe35a080902b58e1c60d1efc0d4442aa702324840a0a0899020e1f8d6324440a0f0889c20442a04023e4093c38379cd00426f82063093b52034294e0e3878ecfc18724c10221487040c8118c1081902240208408120819428810404082e1c3c70e0f2c0004183772f0c8000b44407e5101105fe4e0910114d000e9052fc200d9850fd4a375f03d72f0c8808f3176ec28c108882e725103041733406e718306882d7c3400a41634406821020380cc220120b23800482c0800020b4e6c8f3926138458f7e504905730766f7fe9772daf63aee0635aea4b0fb6c11f5f0313460a3aa0810d9001d20a1056b01e4dc93ebda3ac360ab20a3e7f7b499fd52dde5e094210c6085e70030520aae0cee2580eb7926f64387ce418e39401920a2e7a3653f6b7b5261f03082ada37c9ec9498478d0e6401c8294cd67f95f2e7c6bb8ece01ea3253585ae117ad2b746671688fff91c3478f1d1ce8c00d1b3678201e2ae8f13f7494b1438c313a073bc8f813e820810d1bef237d8f1224037003482940487120a3380665824cd79b4445c1aa26bbd126fc6e8416871a48281895fa2da3c92c0eb5ebf183070828180bed1a2fed67413ec1f899244553997b8233d56da3e2c8403a6136403801b20946ef07fd298f65f5276982d75ca272840611baa64c60825192cedc3ccabf046ba937ac0f4842f1c5e44e23203c00096ce698efa6b4c97c27fb8023b0177a23babebb2a77d0038cc0793ca12698d81781cb799d7da287d0a099071081134c12efac62daf85732044e599da0194b891da4e50142e03726e9977694c611058393cc73e88d6f25e705189c585265d73dd35fe52fd8cc9dc4bdcbf94fee0bb6b5c4a04cc772a4176cc9cf959bf25c64a54678c1e811992663a72e533138b414c7c82e70441766c3e0482e8a315ea97fd5ef366cf878ef51c60e1f62e8c8d138c4b06143478ec6e185115c3496c7df4cc6334138f43dc7183ec4b061037d0e1e3b3c077f021b3672f03dd07afce07188915b7095da841079d68ecd00099a30620b6e2fb7ea6ee894ced75af0dbf6a3d6c71ca1c58dcc829317541e19843e220bae4dec526e7f2cf8f32c6a3f29999643040bbeb45992e935a764f20a469989b1e3e55dc145d5dffc24de0ab63d2f95b9e6ac60ab63dae09736756567156ab032bb8ad75105db15cd2dfb6a77de9d0a46b42f2d73f46c26d7a8e0dd3a0537559258987b0ac6db43bf6b8c9a8233eb9cb5a4bf905ad152b049e52ae52226a4e072d2294a6a32abab925170f23ac949b997a4352aa2602d357a079129a1e0458a3ca1a3df54731214ac0769829b4c59f519fc043726556cb711ad4ef1045b310942889dd6d4dc7582ddd54e27c59337fe6e9c607b4dd268d2a64636c126b73729b3da3ef465441366a307239960834a21325e05a1a007096cd808c20826d831594a0c59a5e492e518462e6136d08825d88e2769e8efb859c34825d851e739896f3aed37995182d3ac7925b3ec488c4c823d19e36df6b7fe92948e1891041c4622c15b5fb2ff8c6ee29f8104e3754973534992ee7c3e82d349594ccd973989274d8811477022636feb07bd65291b6984d918638411ace6e67c72060f32e53db208c6da2f5989162a824b926c9db693fd5dc96a3092084ec9244f2cc97bea4a58b4308208364cf8e709f3ec594587e0f6c646f52539e639c5105cb0249b56108e14829374759027b5f5c352cfe1a3cf11824d3244533e9994fd9f1a3132082e6a27d5f9d48a5fa97168598f1f3c94302208fe649f6072103d1b4602c1c91b25f2bee41c94d094e41323806075af62c8d4232cc95a1846fec065cd5ce5553298162d31318cf881f352f6a64f12d33878dc50378cf481eb8ba724d9f4bb499beb60840f8cc5d4ac39553eb207764337de8526154e8ce881933f53d2697742438ce481d329bbf6b54226008218c1032773e852da8234d9bb0388913b302a4a87c9ce26aaa2e70e2376e0f54cf6f426f489a1e78ed4818b57c16d57e4a792d27448924acf2bf46f1a8a9139b031bce412d3f9ed43732372e0f2aea7fdcee88d4b8cc4813d3d99d13f9a3c46e0c0a52c8b7d9a72cecee1c81b58f72a5994acbea0c42438f4863137f09e34c4f265cb1a5a0e32c8d8e1628441c68fc161a4200461e0f01fad8113884004e67a9041c6f6481bd8a4ac7793d2cbcb8af7f81c683dd8c09e5af656763d29e7a00e1decb8d163c78deed13ccacd61640d7eaea4d735c9a91c3776a03c398ca88151f904f318ba5dd3db869134b02b1afd4673b0f8a68443edc60e8423686084e8571f19639a888c9561e40cece79433c97eb9c55246861133f02955cc4ba93db5b3c96462a40c9c3e0f66da04fd217b632c31420646271996838e08256b3406364b4ea73b0932e3e51313236260446a4c8fb1b763f51f063ebae8c7beb4f9aec42b3102062ee564594afa2425dffa0b6ca6cf98ace437594c4e61c48817d853722cadc9566a84cc2246bac0e9ab1c5ef229a995c47f8c7081b18ca9d225a5c42475e806235be02e692da166315ae06a64ca12f2a4c8bd71240b7fe5ab984feecf47ff0946b0c0be595576693e93638843ad879660e40a66c3062356e0cb3cf877501a138c5481d131e4c94fe9781f2982112aa08d1b9982d9b01129b06d4a9fe55c524d5c53321205cece4eb2304b82022b7a4274099eed76474f40b3ce82562eb91338a5b1832e61f126707ab98385aea669d23281cf1342f4799a27f9a34b6037a5eb5ea69ce378ca8812d8b437395d5d5012d84a72527a2cfac9ed20815dcfa76485ee9c61ffc811f83856b91aaa937812f120a3078e1c310227b6ea58ce5f5257770d8c1481cb95849e67af1b0bba1122b09e25e546bdae52e522244686c0e7cbb6186d93a8b27ef0e0ec8f3278e8f840c9c08810d8bf1293499e1aa3ff2d120cceab52d0627db9a47c038337693c6db64ae4178c98baadd5b7df03115fb0b9b62fab92072df19422bd60c7b209e141692d10e105a319fab4374d9f527fe92e38a153922767934ea828a92eb813659a4a503a9ede5acbe582d3b16a3c9518c581a6c70f1e22b8e084c6abb2284ae78b9d5bf0d91b3bf589c616dcfde8e793ff594cda5289e09f03366cfca71e9f031f9f831ccd8230c420c35b10460a4210460a7cfc73800c6f818d058029446ac1263bff134f7a12e43efed38daf418fc6e166092d78170f6d2d55a9b46997831b22b3e0c3c53cd5653d52a043053a48803ef0cf811d291091051b33d6a7f292b72a44291089056799b1c4b22406d34188432de19103870f85059b24dd92824eb2939c0c249c107905a332e7d239c554e5903e42c415ecc95499620a9a27a36dd82884482bb8cb4963d6132a4a3f8a39b8b1e3c70dbb80082b38ed5f39e992b4b2499655a4b1df62860653db2e5105b7b9f12495c4ce4a376dea0f412ad8245ee98fc61e2544a6051154f0afdb5563c12bccaae41466830622a6e074ee1af55972be49726220520ab67ad376d29a6ce3f7a4d8b5b67cdb5a1f397c07ff68c70846c10932f79baeec25b41e3bd28d5d8188283831d5ba9f4a9a074442c1a5ea2a1db5e3aa49b11c21d004147cfbe572d1d723221d37ec3e5083d640623f7ce418235920f2092e08abf6bc29fe6ea38591820ef810230c31c220c35b4086ef2011f1045b1e1a636bba5c915d27f8ac19efb7e49276f2c509ae4dfeba8d16dd045b7b62e22951d404a76308abf1f466821f35d3da27bd98e0934cf9c4ac3697e0d4e5f8769f5aa3a8de129cfa6675b76c578231c9fbede46d77cb9d13885082172d5a2dc898d7a0753419377aecb83109ee725413753f25cd8ced4044129c0e2d966e9477d6ca8904dbeb6a996a72b39896051148f056e2bee79c7e049fd3576f64107a53437806114770b1d4bf27cf26d7db46914670779fbfb5a40b8db184117c8e126a648e3a4158bb0846c3432715a2135104a34a2f5b35a713c157b4cff869d9a4fab38c4104117c66a5248f6852c14f120c440ec19a8dd9096dc183bf3a440cc1c54da904a5aa1b3ce8d13a729c52083e899a73d27d6e8fe7b143c710210423ac62129ef1ce20880c82378f7ea5f7714130a641c9ef55d287854920d87c26973a6f9346ed06109c8992617ea1fcd7b23f301ebb43059d52ce10323fb0c952a536d1174cda7de03254962cdd181fcc2798d22066b27be0b4b947139a6338d46eec408803113df0572663925a59eb047507b23cf0a5a4cbd16972c614223cb031d96591d5eb1dd81883129e6b64ecc05645abda1c54dab4291c6a85162275603d5e123359b68e4a111d92faf72c5a96340740108618617880073d761c7483073d7690f1642439109903ef1e83f664d993ce6c0361881186b1b25d88c8814b22b46b0c75e3c09f778a41ba499941fe70e07f3cd3b7c43aff45dfc09d5ece5f274d67f1de0dbc8a302ded9ed5e4752ecb20d206de54da9aa4fcfda2989c1844d89076fa25bfa0ed35f0a27674b224a90646879a6cee22afb3e7d3c0a6df93745d96a081f54c2986d4763fe93e060c2267e0b6c562e9fc61fa352562062e43ee9b92c2c7824819f83826f735f858fcbf5c41840c9c3a416bb6103ad8a78ab182c81838eb0b192afdc6ce211503974e2d57d212cc5385641009039f5f74a9e5f926d31e0c8cde161594ee85a554e2500b83c817b830a947875bf01df705225ea853675e78d0b92e70d2affd45875a04112e304a0ef6bfa9194aede6cf9f1b25b1cc2844b660366e20a20546a3e964927de87d93e3504b4260d2042192054e92d5839a7492e94b6ae22258484a2072056e94b4959f17c44f883844acc049b771ac76fb245549912ab0654263d0ecb5cae6d0011ee20e112af027678cf741bf28b52422536045ddb54eea9bfadbca0644a4c09a9292d427a8a0848969c42312055effad3f69de1c5e8ecea8d430455269281008c3e17018043170305f007313000018141892c642a158389e6b8af403140004502e2456302e1a201a181c1c8a04c37030180a0582a1402810000502a14028280c4bcb15e5012b410b5ceb89042187f93993d85d140fdc05ab49609df91f12296db48bd59eda01b1e28d204fee5335cf61a073de20c10c282e25ccfb262a5bcb656404a527db7e23519dee78ee6b0d65ab3e55e79e4afecc0fcfef5569c172e7af9f338937b00740f9090e959b616007e45a8838a00d700320341a08fabafc451e0a1cfff95108c11dd45e57cef35f12f99236c4e07e23aae7c50fea5e6946110c8f94123bc17b04407bf50d7868c33274b0e57f200ac0ad56f94d9889a6e94049e269cd9e101fb313405600072c9b282ec13365663ab9e517a2860b3d3be7b7159f623d7bed65e04bce1ee79901838ffd334767d19bb4608f6facead0eb0d2cd94fed4adac97caa15c9460c39792636d4fb3e6ea3470fb872c9839ddaa204d0a2ef98c9fdd429d61bee9b5f56b0decd4daa4f3020d44b357bec191a3fba42aab23e575281eac4aa45ede540eb19a35cea860b63ebcb04b4227dd4fbdb03bf1a35a3a7d42daf37bc9ee2ceb731e84d1d26364feb2e7d04f742a8aaa8a07107e12c3e28cb8af61d6a42a10aada10ac12e9b7197946e5f68c201e8408e351d5e9041f909494f6ed7f78fcc71b18083172986c60490da6335046e199dde4ee047a666c95e6b04b2dc9079219213c94e7afbf6481209c4e0efad215414a49461f34d5843afbdcf94e3eb8830f93f7d2846af9f6c2a0088a40ba7534140f909203749ce89b1b450911759d347be57032614630d893be67e42689acf2f8dc6319d170011b7f5e643d0a7a67c7dc9659c48817270ec425e2dfdad8b310ee32bdc40ce7d1b023ea24f93efcf30b41004ebf0fb06bb30cba3ad0e3bf90de6e61e3f522f42deae4edb2f504475d72c1f580fe2ad1910fa2be8c7aef9ec130f7b2fb0df73d58fe31679d0dad2dfed116fe5a0861cbb4bcf30447427a712239cd726147f36211e0c2c98dc1d9ed433d8ea88296d9e36d7147060ada48d28401bf4d94c983eb665637de103b1d41024a7190aebf7bd8c40093aa7d1cd6e86cfc92646973fbe3ead7267412c8f59ba9533963f2c33c77d4843f177ebaa14da3645b5f8de6de6299ee285e3f9bd8a3759e27b40c3e32afad090a97809f2c9a822b8fcb345c13988392b04fd333592c5cefa3853e5a5535e33918ffe8d1cdf398a8534a37b662c4deb3de811f988e9742cb8457fbd0586e46defe885f7f2e06aba6e0a6a0397b92e3563d5664e4358fb6cfb531ddbc12e3e6679bd6029bb781bd0db0ac450e23cda64fd40863a5927dc292c1cadfe0bcd2e6d479b8a6729dfd522124137903d4795317896a9766d5d8d5bdd19e80a090c4f121903b96bacf6ab4966490c7aaca4d37cce3d97ebc330f819ee1037c587b6a4aa37b0ae494e9c5e1ac9c76e212b78c07d3d39151b916e6491d3cc155dfb5f0a85ad8d66982eae830306cb2a1a1cdb9dd16e0cc45fbf2c66b3bb49b06bcd18c660474a2440a30072fb5735ef6a9dd75c705405c6e34495c4db0dcdd95e8b9098012527f60bd0c6fc462d8472a7d127f06b8d8d2c83e41eee2243c47f788dbbbaf24e8954ebe161920b840b21624e94591cb9ad145799c510778b90bb8ea88ad31661e76a1540869149760b53c5cb8d0194aa5fb961586fca6ec8dcfa506bd0785482e50cc7aeef91355e9b655de81c7737a9f9566b18aca1b8e2fc3f0b810558fe8e5bad26ecb2edcfed1af70d20e177fbaa3ce2efc98a1f31bb93538c6c293bcae311df40c56efd4a7bcc31bf36771f880a84dab7edf7c8cf334490fd52f759d6da6825f5d1e8bc1819f36313c58d1306e55c5f41fba318ebdd6185904b99348beb82db2276c88c4b11014631fda20e3d8ef4197c54fb4c621dadc1863e502f45e126bf840230ef22d5e230ddc8d834e733a6996312cf85f6b6225262218ff138e12a4328921e2aee5eecb41175a7c8dae8b5e884bddcea219e67cbb1a6d2a943408d509d6a67a7d233614f576a84d6684207c6c10be23335e553682ae60bdf6af9ce1586947a7f67e55122d97cc167538b84a08be56e3de910a947a191edb50e6668b6f314ba04770551a4e6f79e9834735680fdc0a7b6e79774bc413e1d5cb7ad05938ac9fb28e87f5f94fe54de9345e8718338b938a53fe98b43b8841d2960911ab25938d0cd3687111dbf4fc86cff245b65199f71faaf6b5d1559015e38b42d5f8d53744311308ec26eee79f86a10623d8409417dd4024169ff0fe2582323bddcf441217a57a108f936e168eec2c1383d558e16cc8941b73c113910816788ba3fbb8ef98c56d9afad909232064b8ba20d3339d2389ca05fe0da9f8c4583934c301bc6eca2b50e3c3c960fe941deac60f0bb6a2931322f205934b12b11eedce7103615bd628e8351c38fc03d7ca3575d58e1fff1665a452eda103053653cbf35f501cbb51d3edbbb4748f36a57314c44876a6fc31882aa27cc2be47006918a01c3af95a790a5f33b306cb51007f1a86d07a8b4148c071b6bc640fabedc8f470be4a6cb0064023c38f5c60908eb0808c48169f4b853577a7e6b91727d477dd7deb8fa5d8878f0511762ae3c8bd0634b8e8a5a91c3f4a07416b67c07165085b2078c28d00502a99646dc78918ef9757e45135d46839f481ae61fc188fd77a5e503bca7fd8e86badfd245872dd0aefe4a1112e5198bc016bb7e0752fefa4e65d858a40e352239cca58925b16f28dd5a8aa1e109aabf40787ed27db30ef0afdb5eec62a1f89427cb0d2d95190f9d1be9e913632467918328cf1a9c952b7ca256fcc79687bf49c97e05e18d2e1c93cde32fa57ea0c73306ed22a6b651c44f18221ee87cd8f737dc1709c79441e64306f03869d6d0d13ce03c976dc9a889052ebd94293cd86e3018c99c0796c85700565917165b557d61f0397b9df336f6564e40e0eb8d6892cccce3771d2dc5b0ddf8010f0b26ea59d00f5f88953eab86cb77f67abcfc365485884e1d3f2a0de4769ec6168ceaed12027636cd6b349c16f325be3fb195a0cb8466dd0f7895ac27b47851ac7693a52fbc3a61b890788fec113fa99485240ce5824f78d08a9b411546b072628f74ad1d860c0d9dc12c6957c939fdc1e022698b4a580e037a54913f9a008228c21d609207eb49764af300f20cdf3bb3a4d2205e615dbc4a3ccfad3a4e423c86ce891b1b9df6b51c2a06f74b00070ae45a29ebbae188b1744b5e7a9b79eb868e4725dcb5761998735b77e122aa6c2d114f3f7f711877c3a229a630ff2f8fe61dcefd3efdf44a77b203549772e66db295ba709b862774312807a22cffc8c9c49a576de7202332d78a5e69c352a6e6d8295e6ae5192d5d896fd8da7bd1a7a3a41a0473e017461a4eb8f528d7c3c9ca6ab419547e86adfa3a5f6958080582f2b155d914aa4170845d6302b16067a7998fef993a10c08200d3763077940cf2d40dc283071132359d6b183dca58073748dd0218334536b1fd2d1029fe6fd2cac30407f6f2c988401eb5a791951776d247ffa333def6b19d8616dc1802667398725197a548f97f1285d6d4070ac533c9f37ad8dac47418de44b559d124afa67d38a4fdb8d9bab86cde6e7770919bac6acad02edc3bb522a1db29949a5aed1409e99d03d1610301829a687342073d4263125c8317fb335f11dfc8db34a0d1b29d9288a6745f9ac5d1dcd9c4691a0d845ec6461af88fd90297acb0ee334cec4ebf900807848d7b59128be4d28cb1f26b0de77cc88e358f7050d39c9798347b69d07b4066c04932f0d9a825ec1dc382c205eacc4438b489a840ae5a30b05140f161aaf815950e23e18d54a7b1099fd852a450857e1cdee158564d114ea66265fc756dc48d5aaa75013ed5dceeb3c80be74ae7a7870964dd8b139860381913a50e1b792e8dfd1a60f8e33d47e627ae3c2b52e2f43559d7466e0dae13dc1e0e76e0879706cb46c77ca85be1fc9af4091987f3137e34b17c1ece9f7af60f370d2b80bda2f31423c526c739de0f409ace1b227add63311bacfb91cdab7f1c6dd32013900d0641069b3cea81c1958bcc3c62ca078db3d6a69bff3084cd11e2f6a6d60ccd33447a57a804b45f628ef62de9dc84120cb590b28e24937c7864e38ce1fce84e38442313e92b1904186170deb7d96ead9c4c1b9396e707f687e334a606f74c4236d566be88143d881151adbd3eeaaa4aefee53418c48cd2b983897e80616ab0b4739680fcf4aa5046d678efd9154791a282869c5208e0bf82c9f242787e97f60c258bcfa6ed790fd6680ee067e24e3dc8dff11b637785432e8994a545beb5290ac159be4a34b69dbc1b54d8d48140210070c8765c4d90f53d5df14afae41fa8539316265e7ff97da65aa37a6e7030b45b194b916dc41f0243bc6d2fcf5dc1a5f24253e9f18ab3d9e52a799d6779872151a24259026b700240369d54ccee63eae3dc21df4196291a6720dc5d7b373032156475f510ce53941fc14ed197f19dec70a70299764f4d267bb8040882e9c61026d64adb487168a18f083066c96a6b898a55436a4be71f6ef2c771930a0d6c8888f10005a8b50d162fc7d2a901b0d45c726b65d61db0d60e82ccceb09e9762ae8d325d440b56d7b4760f2ca1452051a3c39776872ddeb30a7152e586b35a4d754e29d368846c3f7201483d09eade2b00e96f59f291f63e7040a614bde8ca343eb1a248f6c455aff6bce4b0bff9806b2b07740d739a3c517846547596dfc6170fb63cb4c55c0f9ef8783d8f483000b495d1e04751fca0568a4120cfe3f9ece905aa2210237427b2876f2ed078b3b1776144ac9ee30240bce096dd432f1a16b1b9b02c02e2372b4990469d26ab805fab27b4ca597dec5efd3423bded6815a5614305577b7511076367d085764c8d599c6f99e3106d96c8a4875334e85226082c94a8b9a651f84398838c54493fd30c56830784da99f86a72bb28234245c5a1e151a415f12ee5563ca96cd3b73d3813d0a34fc8d824bc33a97e89161710e3abd4228b7cb85794e30b426f18ea3414c90a448c2957756f50ba7dbe898675db31f7d3d14099a1ea5a25fffae36f00b7f5e17c3fc21c4cb45b9c3c89c09a21332a9a765dac9489ae25f04a5820f898cd0ecdf08ced45a393b01ee0b3f4ce351b512158fa6c9a96d161ffa01e78e6f06ff02d2d323b7dcb12bda98ccba0e3ac26ac5c84926103d1f89096e18aa8fef93a5390830f9e03915b5c3a5fc9313f37acb8e3fd393f844de1e77c96bf29d0c0aa6d3fa0546fd58fb9ddfe75db22a90c9ef3a9c46fa321f257847a137b231ce8eabed7e119633fd12904ee673243304b6291222906c75647745efb5cd4f634bbf22a521ca5135dcbc34c3c714e086608913065a9d03c8cb98024c546e92dfd1d6eb1c45a421fd777bb22f973f80de3959cd76994b3853a9bb7744faf3e81d104dc6de3b6577060b5fbacf0d6b31511abcc8cb30006b36af1d5bce6e6c909d1ce302736db03cd92c465999f24e98fe5049a21b83087505620b5cd1e36cbd5fab6dc8304905c329eb4cbc164403038a0dfcb591e3b7a351dfee759ebfbdecf4c5502bf101594740817adf5f3974dfa9f03ad4066efdb6d664704557972e8276e953abe026d749930edde307c22c536ce537d31648694fddd81648a118b77cc65a807e4c9fcf2ba7560524dc308b64cd1ca531ca4b4ad9e909feb6e9a26e62b99aa81ad28babf4b7daf439ede0cdacf178c713615965e5b9b9cb9b3fa2a1d4a3194aa15753c997372619614b3b25a9897eee74ffab9df7cf5fa8a4c2c977a1efdd98ca489c08b9a5ba74183b0546319a016bb82c53c8a73daae72ef42b531f5fa1bd9d20c62aa144c6ae2883797aa4011fee2ff788c6fd977fa4d14f996f3c93ab36c1696f02cb569ce9f637d9c13a132ec8c8e7815286f592dcbec23a5c337281a4245c45e903855c983b36080aa94b25c0b47d4eb8a6d240b5f15fb897a680314b30a3526693ef0b5daa931dfc1631bc550385ad7bec4dcfa392a8b98f456cc6917fd4c92caea2eee253047f13285bf23129c702fbca032dc3c2ab9fde364ad0c9d200bb9bf446ba4ce6dd36866fc0cff9c04d319415319000becdbb5a180109261289823da53671b50510af7c9cdfd7d78ff6dfa8879621a00a32892e0a64ce8af9e010080a6d891cd8200adc4fba1faf607df584a0bc4ec3ef4efacd5f96a7f7624505584b8e78702f2797ce13b447e34c02bb8973867d517245766a0d9a14b86ed395936e8c61a180fdbdff8a150294d6e9a29363c1ee0cc9068f37630ee8813bb182fd8b5d17550c3a0df415ba9608fbecbf49d8088e7512b5c57e01077ae1822a45ff416c07a71cc9543315aed39702f42c6f0e486d12e702e298fae6aa55a80095ed3426340041c19beea0db49b59b0bee2830a2331cb2c6f76a94aa994b8bce3934a7c1d664d4f9b7328d085bb666173d6d6e7470febf8bf347068fd331c61d09b704246969ce2e1631bb4973febc65952c65ac5afe376a617649bc59b5775e3b06589a51edcbd0e20269ab985530692c950e651c51586f11c5c7ae4a43ceacc4c455a1bc7d88542dd09eb114d58bd308820b722382f863bf9e2b92746ee7d5052ff0ddeb09a0b75c875b2fd17a69cfbd469c46ef3003bd46f33abed79a5e2cf022b5173dbda055afe3853e244877a5d85d3eab9e1108ac13ead1d11ed5433a0a3a812ce763567bd4c3a6bb4523929e39ef930f92ce161d52274daf898e1925b49c57edbd76f4c2f65e9be472e472abbc16eac5b2d7545eeaeda56df65ab4d4c48b34af88edb5687537dca4ad5c158d975dfe75ac3d759c65f37fe3ee94eea4dfb748613db41888bee0c34f74529ae80f884a0a970689a96921858ee58c84df2d729ec4761b389cf817b96e18fc63c726211fe6917500d7f0cc741e5bb1c6bbde9be9fdf146127b111ffdc48e841984a9fb1fef368e61f16c9038a352056af21fdf4107dda6c132f4fb0fa3a3ab671446408836bd3432652136de6ecf0fabdc1990e828ede313dd225bb47c3cb102ea1842df1fb81c70c33cce090ecfe837e7c942189994c4a212ad49366b5f8aa8fdc713d865152417f089a68b865a5cb174099a8e4de0ecf88e10232e8ca10ec5202968b2ba300f302113e2280796bf4cb9aecd66c5d4220db784878c962fb6fc6ddc802e0fc1ef84bb21913c9dd6502c318af1d4a3ecf2aa3b36895512dfe30d919a4884522b9545d8a48088201609630b8c2c0166d3f6bc5f712517589900357ba0d8c51c39941038eec0ea4390995dd11ef0aab39f23888de7c000260328d38207e7108ae0d8a4ff8a15255e6c0896bf022065bd47b24a910c772bb1b6c634af203dbfaddcc414c956bbe9d4f414878025034f48f1a0d2721370a1017370691e23284d40f94a4c55e7e80ea4d8ee5d94a141a3c59e4897595791b037626ed701855f744e8b69ce40150a328edf77733cf58c837f2e63452c6841f4eaac652831b296408c567304623d3616b98be5d3255cace5f58314c166ee2b96d4908971079e1f9cb530d89cd2efcfc13791350bd49e3f0576275c1d303f2dbe7181d49660355fcbbc83b206852197f97a86f35efa3339d4b00307b23ed6e6491145393012e5c25aad3869724932519e583bc363e7ba20a9973bd601dc2563ff72d4ee482e67b10e5644b1cbc2369e368d2ad98092bbad33573d1789fa46524319e954dee88b88212e0e9baa27f840fb4fc1dc8d718738b4f91c58e18d40843e12b98e8c16027fc14f72b89c915b43b6c0794647bb2ac0554026e5d4dc6d25bb36e3dabc4520e2789f26c67c3a97c0e879768ccfd48a465d1e25176bd9e7dca8ffc8569489ef345acd7e853028fdf40163320b17b3b631bfa4c2a7b1ee3920fd4043b418ab78b8d08023bee8f62ef6222d439b3dc22a07fde2d09e80cb040f85e304d463b421f6dc6c740133f848a5c7b85df8a0e8cc9ba9d7703d05f026ce4f4c2da88c08a98b6776dcf4a5a78281af14a5f9757f74af544821ef13be73209b46f60225ede10b9eddc2088b5a8eabcbde7bd8165e19b733d39f389c598c2deeebfc3a255be17b1548f57c2ec2e13629c445e8c67364c5b29bc5db054223a59788d0e25e4cdbe00a64ac37c29d11c7afb6a1f72c8b93699b4bcab6c8a4dfbbb592826eeb97b4055001be6eac48f0de06698d370f44db0d2bfbe272615901b1505ea591cce256ff0b1732ce79ac0ef1f6cf765de34a0c6a6d3322ea000b9a86a37ea36536356d693d2be6a67be402c0d5c47b85884ff6dc2c9a2d34aa80199c00bdb9b5b356e4826a9662d18d078da4a83ab956636e1657f9a64f335db474dee55ccf5bdd78f47721c933f09d2acc7ff661335ba1963d0421bba8ee311a9c9a70ef00394f16626ca5461d6df59951467434403aa9f223723893edf8a0bed0e4a2455557b0ef7b07e05baa2b0a075db59629824d07adb468208e99c70474441c6fbcb03270f62f30dbbeefdf8dbe5e859e8a6f93370bb54708605871a31de84842a53a1f79c29858f22b8bcc1ae712824fd65635300267586459498721ef2c17ad952dd054d91b1d854f39ee846ca2883fc593ac5afb26fb2e8a07cf0a124821d328f0de02dbbcd1ad200feb480e495e6e20fe5d3e609de552748666e1dc8546b5dd7b096471856591044e39df0a9d6068753c7e9a49fd125890ea45cb422ff864d945dd272fd04d5ce50f848986a6d5dbed8bb38e2ccf2f8d9e2ab1d24f57b3eca4e8643ef751e55b7f785c10bdb9db7fa12b7d5e2c87f79e3c37fc60ace07b5b3b74ec8c4665d31f9c32efb80a6f9dac05ff166c1ce475bae1ab01d8b3c9d81045e18b1931739fbee4f1a930ca477a2177949383944a82462a2265fb15fdf4bd6c802d25525dda27cfa34a2f0945d18dfc8a3460b8647f0080020d36cbb5e04940db199f3bdc3afb2b69bf916b14b4ae4abe29b6d4c96ab35c346dbc01d85333d0493e7543cf9d8f8379116728807b8473bf4bd85281595bddffde3328cdf2e58cdf7b6b5930d9d84a73ea7e639f38bb5bc0a7e8b4d02ac3412d12bd6825441f937397a6d0da4df366c80ba629c570438c4f17f9096decfc1d6d1c3b71f5568f27355f5143ab3992121bfce92f867cfd03d4a8f63dd3ac4334547edeb97dcfa5122f79c0338f2450b7f54b00b6757af6c7c8e5558060458f92ddefa38f9eaa7ba56de8c009ba7da844793f3acdf909000b8dcefcfb461996c95a48ebe7c18ec5a53688a698607f9a7e0ec9b1c4ded553d34452d7da1f9b529c7518ba849ec9a63ab8f08bdfdd629f5a133dc37fcfe938f694fed28271a7953a92dd677d4d4d5e21de4747923d01673f36bb1f057eab26644d132081e04ec3ba1e3a5b233c7336a75e40903b079b5c256625d1799cf247b2e428e7320c4c8d43802ffbf11a5e8a701a86b3e009e0ab3708529cf052447021d5b011b1170ba2c660a05360a7c410f4f749fedee45484441929119728fc010bc4bfdc14d7fb7ded793f8943e151c8e64e3422f11728f149374b09075a631356cea10b09d4e5e586c0a85b6fd51a1be5f693f335c119ddcd72499f53019c667b39215431bd067947c5337eaf88cd1732eb11bf085383069f0499714bdb127f95d5793dbdcd86e100a6d33c680c590eb9b0545deb62411378042ab16244bdc43bf805f27ab910578ecd5225a9d749fd4e31f56b0cd90d874bd99ed4d0569462d6f2492a80e9470b0791354d0b08648569d4687c8dbe0bd4718c59cec479256b796eb5c86d11ec0dbd2abc758d01513b3cb7875efbe617202e868672bf5150b618de59c57e8d1b1c7adfa66f380d8984446c244932fd8c346ed5615e3fa460d2dc3348300f945e3f7622dfede037e2d8f61121622e12776f4f4a81b0b9f248879875097261c08c3fcad13ec4dbb06bb6eb08dbdacd032c7f6c24cbaefdf78eeca6900b31241594147513ea5290ab607412298393fad525c88bfd0dd4c9b50dad13ea50facdba9d7a36850bc4b26eb557f81be003d573b11c3db6fb5fb2aebea2b608f6c86d080cf748db66e39387633656683b0cea33bb23c3176eee48870742b97b1726d565a9a8a6876ef300c2786a4e8c51f6989ad9a7be3026114ad8ad7bcd65ec19fa7fac566b8fc93bd91d43f04d822fa288c681aeff8bcc2d7575dd933b9f9bf744315abed0cb08bf94d9d410641c3e85fd3f1f4abede34877c51901322e5324039694472ce5b48fb080aa6224bea56f1cf5c312358a5394da3d2f3c4682d7982b4c461556cdbeca09418c95a42ed6939643119b0233acb72540fabfbfae748257d814b0568b2e4fb8808c4ad9b3e50dd0c514a6c4a0c6bc1ac22f06dc8aa3d4bec16cca4dd297d66ca6eed2d4107fc9f97500dc9f86ca7b1cfedb3b896d4b1d0ca803e8f036b234892083b52208d94642ab06c97133bd3cfa0049c93285c1b1572d0022ca2fe0ca4a10db8b4f10a8358b954e28a8a491c0d2849d70e260c723201494ab4730b62036f5274801351e1581c501487c075eff27c9ca86b1524284d88b76793168521022b0408f13b81634c0d331c5ae66a46d1e8aaac04bd716888195fe9246993145d701010bce7a8c2ed670dc21214c490559f8fb28cefb3b5621a641f0d11758ce592b89dc7ee71af0f1652ca16048deae07aff30e0174cfd6ba48f1fe496c4d2973af939b152f8895d15da431482637419e3757df5fb4d059224f718d3a83c5b206aeffe9a9727ce3747c147beb129850cbad377b128dec1777d590a8f13c4c309e052062780aa72190ee4b78e052e4a64af465d69d29c56efa191698f7638689de9e7ecdc556d7db1cd186413f06145b3acc085b2f7771851b342149af737a9a334c680ec8d45ac2d476062a234e073992dadf7a8be86869b1f25d680d1e2b18618f20a6713e92162704b36fa066e3ae5292aadc21f28783863057c07d0b4ddba29926e8929304acd0fd976536733741d3c0c87a235eeb2f6c6b1cb0b69c175be3a6b1f70448860a53f5a24bc0629f4554cc2faa459a19cc18e3bceb0f3e08e379609d973d2c73d3a3ed6b692dd911d400b9998cdc5abcf50e25bd6ea8d3674fab28128ca6caded58f1506df62abebe037e0426d36dc02a9467f715890797224514215d368009ea04f1c660aaa150fd1428b05edbe0d9b4f432188156c28362ca1cb9b55038033a61c5c0f285ec18eb03f7b2ffc92a404c1c0a5e6a276e4f2aaa4b0e4e086a6a6a85d07538d01cc14523ef79d6a0c82afb0a03476ff3c17caff8d864af4e1d5a643e8f5e3cda8fe2d8240d0658e42b851795b614df42cb3c4995fd969e8412904495c255425a9f3817d8f94747a896c17a394b7641231dfc77617af56310a8f565f8f8ce50fae038f12f9710afbbe237a13b3edc42e98bc578eda9826f81951bf0182378e89fbbedf155bbc64f6b62df85ac9b0b32c9944d22cec2e6948603317121878925bf96c7f6c8e7d340188fb466c8933ca257953442787baa6a42099ed3ccafdbff5da7f8e65efc76f81ef6af11e31e4bd07d08e0b873eace86049f62fa4ed77b6934cd3dbc94e119952fe8e119d61f37baa2ad39429adf9109b08d3ffb93dea239675405cfe63b1c5482242ae403914fbb8377c52ce93cd9f9cb8b3ea3b60fd393c4b000d9803fe53af806708c1c2f7f6142cf47e1f99b1d97cb07fdb84d1aae946f71037b3828fa1543e5fb82e06a8de643a406648d638f1dcdb3e5ed817e57103d881ed1f7f432ba5e9fa59f934fce77248eb116ff7c8ad4dc5326da0f84f3e630d444d625f54b9db449401bd68b1f851ffe5ecc6679d8c837387105623d02e0cf246a58f7c0ad6b25d6bbea8a9f14de3e2da9e715ea9f4557f4b40793a4f79275a50e06bffd0961635a495c09cc61c7e8e5abf6ea2c8b8c7025d4a99666a4849c35ddf754f03ad28435761cbdf353e3cbf2c2cda07dd7b7d14b6bd899b3aa5fe912fb59ce3b39bf4366f63ebd11f824b07c528d3a697cd2516561cff241721770ada7e314be12a90df4f9727c39e9ead61257bb333f70ffa59718f8afd650615bd9ce1332f3101c788b5f3aba9a5e1198608d97f6b8daae4095e89a1a29e8bb17d49d74d4b73b876eaebfe997ba735ca16a9b5fcabe708ee12b3fdf7780cb5f19c806f2829f5f85baa0bd34d7d69f4f3688a81f252d6c2cc6d56e582bdf50db6d093aab672a1cdd771c4d2ce95cefb74b6e2a4d4603ba832002127199bd4f1fd1f31a2d4cce13a24fd93542f19f2102ac114cbef03021e26df2dd697497207db69d1525d6430d306efe7f62059375d3d881df4387f033577c567fe3bf3e43a6eebfe2d5cb8228e401b936c3cc491cc3d083a4a118ef66b497ac904db606c343759302aec0dd257ad99844ee2d5da3d39c0a2f2540bcfb3f7aa366dba3332fa5b6347db2f04e43e5a1f815d17983a0dd5d7b12eac6f41e3c3ae00831ac28de5464359544f41b8399a8f2f500cc9651fb4fc5276197fdbf4712e13efc8df3290f157e3067e8d725870c3b9882d7233bd7f3f5abdb4a0e57f2c05c0b03aef6e6eadf7aa81eaeb7d284c9f9c45ff6378d97d6efdccea6451a69a866d4274bdbe6e20ef29a34dc0c4337ecdff323383e04da30057da5bb1a10c2123ccabd334c6d37860c13df3290bf84af2e902656fc00317380530aef8f15c517f97b38d4c4553a4d901dfe2f90ebe14e3c2b789abcf58818686b769df325b8f3793681bd58bbb774b8d42b9467a55df806ae7625ba01f63a1ea623faac65c60413973835feec995b468c2eecae957fa128a2f022fa4f6e5d82f3d287a46577a1f2490e3a78856163fc185fdccd86c25fdb4ccb98fef61de0ebc5c26975021ae3104d1fc74b7dffd3341119bddbaa294847c845464200fc73d02602685bef279d0c7d8bbf03c198cb83d80bd22e5014ae8ab77aadaed46fe230f442adf635b8aac9b0a11574372063278e2dbeb2f41bc9a54099bd8dcf2c2419e7ec66c53c4d59052d712f5bf5ab9b68293cb32e6b4b193dd032f1c9cdcd70dc388db61a2b1d46509d97b914a0f4fe4f70c93d867fbecd46929e0edde64c0b6fd11b7cdf8dfcebc97334fae24f44b91a05a572583ddde7fca4a8953e2550e11428ec24a4208839ef95cb98e8b901a0e61931b03e987dc5934d453932b343802a46275f8a8148d519bc9a0190c1eaefb25ad765f05ddb989eda2b36f44da0a66d2f09d9f07bc79a3606c00e3b88a08682d88b48cca9f27acd0a7e592cc809dff3a76772f64a01783b8861b86319adb44565aad94a07b3d5520869cabf792bace920f5ae572f8235b00519fdd49f75f90fec1f5c73b398c3c4b0c815752275098286be4d9a3bca50be701029428d38f80283f47800d289cbeb4a07ed1bdf628d32933e5a8fdba1827b65eefad5b87fdcd16fbe732227559a8bdd14618a42383151522ca1fb1903c87336ae0dc8bfc9d50c4ec9e02d7857a2c122ff5ffd7ee5b81a1cfa69e59483469f1fcb59988c71f0bcd23e3aa8a73fc3e808651f992b655037b13051b469c96c15af80dd488c9fb75c5139d4c9cac1493070b54c919f23026ee11496cbb6af7e90eeb0f9da6c1a577ef83adf8abc4b317e3d11bdb25dccf7f0bba2ab3d47f6f6d6937413635e9a7328b8bee0d245cc9717e5f5003227e0a65335be2e39ae2712b84e108cec709771bf5b368c6ec7c049f61cb8e8c12bc8ab6b6cfcbb59debcf593a77f8186bdf6808d0306551dacac1bf89a1b50fb8d505ba5a2e5bc801864ed80da3c703ad2345273f6567cf5b77993709c765a27849b8a2e0a14e31cb1599a6f132213e0db39dc7c13955332373cdfd42eba37e2bfac33be68c09d31e65759e78b5e2178b9e576a62e170f19c5ced8f489a1b6a407e073957a42598319c98a268632fa3d1cd140be9d2240eff48ec3c99d922f86e3ae1483212233a1c0dcc07b50acb50c91a3c08228358f45d782d744a810f7c92433280059afd778129a13d4e2f7bc54d67124b98e538a2539b2477ac17c64afc71b29cb173f3c5f281d748a5fe1f3d1f8b71ddd7a9eb9808104bf321fda754b4c2c34c605436ea01f3f76119c917712cd34e951e6f74efca8880363f1e93865c7f57bf9024052e2af95d9190cd2599e41b7519f7b6d0ac5442894a88ed740819e3ac7c7b77602e62d6edcf4f108ac61d0f40bcc058c1c238f6e145dfa0f2ce45eb85ea8e054c0e2f74ad9c55bb39c7912e462ab90bce8a4930a3d0d2b96256964286f4d3634b45548e6a3165f61ebb896d3e3da091ce4c1706b383dc6ad3e3957e1c9d376990e9b8a9f75f77c41f05a596f789746ec2f20a08bc38ff3f63d110ba1208126c6c73c39fdf0a6cf0c338b67cc16add107599c6849dd1433908c6cf7e913216666bbcc9bb46473bd32b51ead62555ad87e2ab16490dfb4dad823d6b89c63309d8dbd46e99f33f15e82f71dca6e5663877cb986ecb932d1de56f18a687add963561c17c40871bf274e40396cae2b42f863453cb901dfaaf01a489a1fc15841e494df904c81f494defaf090af7139ba1a30f318a24467b1fde36350656daaae3c835386c9e9b04a2b3bd646420c8dc00b7834d0e96d5613cc41bba09205585e669b94b70c6e17a10d1f3ad0f3fdb121f198276a5753369856f790042ee2a336ac846f4910074640f4b2693f402305b498cf144a4d2f8b4fda9b54b9e355e744116111b1ac79e544e86d6544d1dc702c801787f7e1513c3c058642cc3c814a84456e0206a10e205950738fd2ee2225055bb532f260dc78e278927ca4382ef05af1184530fb109a9580c7c24838cebcf05fe42f10c6c702a61a3539f905b8c189ab0c18600f00da32b9f3c1776915212fc28266e04dd0384f548a290b2c108208a5b3502df4eb3850236fc4c5b4054eed48c888cc7c838a923f19d27a961beb115d4f735a62dcf5a6d13e14b4ead17024f837162a600cc428fa252bbb6022a0049fb800953598f9aeb1f488c20a413b036683db29ef29c61d02e9dbae454d8fbbd76d4275e02e8ee2a8e6d24841b925743b95e6c40fb526a3b6f47a9d5b424cff569758e0ae1b1d216b8219e5bfc0b4cdb78b93e2e2cf0c0c48158ba293e73d94c8899acd1eccc3c00f3e56e4e0cf910c589dafcc03b17a6a86fcc7ae17af6e0052daf8af4a2ea2fdbdd7c53dd292796eab60184f9a48f548eba4641843d963e749b5abf3996ea49b452a18bce8634fa7e204390bd9d6a5e868ec46024078e61e7b009d5ae213b50144e43729871eb88189174131a167f57154e8f500f3a88fbd2882d6d1037051b1f5857271852998d4cc6ad25f480e48b39225f00e588e2d35814543602dacbc3993760b46439330f0f0f0f0f0f0f8f4408b7b60642da169220934c12709a62d0c82499924c2945c2153b10b2371bfe226df88bb4e111120004a809b60954097e2e9b9894d64bef6622f8f89fbeaab288e0440973919512748ee42118d5f14b9e56baa934046bf23fd7c8bcf57d29046bead3065d1a4270a7df6b3a7e6510dc9f79ced17eaf3c5604c168ebe451f5c440b0651a4229394aec27080826e6f4a11bddf794d21fb8dc7e79645091b39ffcc0c7cd6ff99fd2072eabc4f28b1bbe993b3e304a7ed2ce31b53d702332e99832c60d3aa62e20850e3d247a54b209b1abdf151d79e033e76e9f3ceaa74c78e0aedd76c42c7d07eedd44478a7442a1c30e6cd24df71eb3eef6c460243aeac0e8a41954d2f8aa2f163ab09ea4e7a76c6ab2480e9bd03107f66f6352bb79b6194f28fd24bd35f552ba858e3830c24b5ada0b716bf5140b1d70e0920c6d2a395edec0a605e9a96336154bc4ddc076e477bdaea30dfc2699d484984636b05a952309a1295407915c031fa228b3ac4d5ea1430d7ce99590db22d8479a7c816698a046165c183b1d69e04430fda2a5732b59850636e5ff52dabe22784ee940c7194c2e4aa3fec7e0191a5a093bccc0da298fae9f2287ae0040091d65e0b74e86ac7531ad628d2f96091d64e0be7af3f84d9041ccb278858e31705ee14993ae6006500a1d62602c69d276a546c7af182ffe863942471838538d1194de2030b8eaa9f53b47911e1a5a37b4701b5b58c7173839b636f13a99a660d3d09a800c19bfc5692ddcc6164be8f002973528cb9afa2ee652e90213cd544a5a636d71a3c8a0830b8c656df98ffc696899a0461643e8d802db9637a6e99cbb337566bc16cd0531217468818b9a1f723c8e97c814d40e80207464c17c72434f475f2c7039263da6d2ba4c075d175760f285b28e49d345c9f52302001c74588157dbd1765481bf9464b092639b35561d54e07263aa51b24fd7990a3ba6c09f893c955939c6a44926e8420134bcc8c2031b820e2930ee934392e6fcca5eb14581dd7ef10832a93a6d1d2830a92de99fd09427b0e19931b6a6886de23981976016df57c5c494d6d1044e9eaf7972c99fe960022bfabc3372d6184b550a828e2530c9447c517a216372130dad2e14031d4ae0540af2d276356cccb8b1680b1b3448123a92c08dda16791683b64b161a5aef45165dace064c8906146e8400213dc72c62da12a5fad34b4bc682fbea040a22d6cd0c00a741c81cf3e52b724a6061d46e064a89f529db3f4655b78e1c5df480a741481bd8c599e73482764bda8ae00e0830e22b031588cd99284d0d09af12ad8c2c68def22cbdc1018b3344aa387ddf6c408810f539a829f655fd0c8133a82c08646fd18fb7389681010981c299808d2528a12b1fd017b155f744e61f132d34ce8f0011729a5587e0e7949653de0456e322969723a840e1eb0a1156ab3b492ea2b848e1df061229795ef46fb5f75c05e30fd26297516858e1cf8d515aa112d2774e020539f3721ad4517cdc577910de8b8017f693fec53d561035e82e7c7b46fd6a6fe0f74d4800b2a82d27749fd43582cd041032e4347d698d3fac68d093a66c00575b2e27a0e49716441870c8c2316ac656526653978e70d16e42c7642e634b40c72bc424d93f2e8d15cc1e5c632215483686879f137be8b56307a938eeb26522c8bc9b72d3858c1c78e393d2b54e98edc2ad82d993d49f34bbeea34b44ec0a10a26694fcb885e961d8023155c081aad5f4354b0f9ec533ad1d77b0d1ba760dc74e759164bbae427430613384cc168ae9462f48a3a5182560a6e73f05c1da93c7fd0719082d1fb0fa1b59641e48a718c82dfb4d737ba765ad20019324c148cde34939b1afb6a2ba16043cede97d4a6855c1714bc59d250e215a2c8f13ec1295753ca47ab7e7ac98681c3136c109d22c9f3b213dc858a81818313bca87893d488663391db049bf37ed03b99e1d0045b1b62e792a124f5c7ac58173832c1f5f9bfe8eb16536d16810313dcc49c543debc8d71cc271092ea5499b3cf9580a32ad21705882f5bf0a4ae4986efad29560824e253285f429247d29e1b7490b765a5426c1a694f5ce257ed0693949b0f6e6a696af4d4d7d91e04f2f62ddc71224b849f69533a8b3cd177f04232c4fe868428e602c9d6fb808e9ddbe3682731119733368d74d9f116c10aa699b7f4cc58e2f824bd52127ab5ad4c92b8249e94ca51ca39b49cc268271cba147969d56d6112238214db38a568b55e21f82cb1075ba25568f25b9214c6a3aa29e489242b041e4942da7ac9f7b6942706a915375064b27c71d049343c65c2f2243da4a05c1997c8b582a8d49896a2050ad512540a44a9b4889932b7fe0c4ef42cab9347ee0b73af77bf2f1f217fbc08b88192c99ce072666f55411fed903a7f28ba68bef114b89f4c06627f5131e6279607325b36e0dbaabedc6037f7e514b345ed2d3d51db808fa6ba37bb56b291a030e3bf0e719218dca98775bc251074e4d889de9ccf72c533ab041582953ca53e988983930d13ebf861c52c9905e0e3930724b8b03a3e2c99437a44cf79b84031f548a6363da1bd83c39998eaa615bad1f81c30d6c88419a92a939bb841c6de0bfdf36dcf7eb2d651c6c60f37f47b13bf5f821e158039b1b5457a8cc0d42ed345439d4c0a4fcd3bd74a3c433a3ec091c69e07254b7b3131f0d7ccc794b6e48798d2c4ac990c1451238cec0e6123a57764b31d794190adb284a4791e00d82a30cab57a6242dab68b9d4aaaafd74be105bb33cb5eac041064ec7dc91904e9f255d4901fc81630c5cf27ecf331d5137e60f38c4c088aeb3d2133f258ddb488888887877777777676666666656555555556545006fe00803e3b944d58bbdae05170c5d08d3aa9efdfa02dbbbe982889f953624993c707881f174d61137a56069121a5a356c6829a594520a22222222e2dddddddd9d999999995955555555add14401a881a30b6cdd0779a6313749be891138b8c07765a6535f2d217c1f706c81b1cd924763ad05fe5cd355a64f22b59959e0e4a65aa78d6982d48a030bfc87121ecf2f7f053edf3ca70e9ef386885b814b2a49f64e2a3a9234abc00913b92742ca39e6272a30f6395a293ff7586e4f819f904452217b8a14b894354fba489e1dd216054ea59560498550275f82030e28702b22afeb3f664ea9d1d07a021b27f9b928ad132c6d018713d85431e435ed3a2d93c990718109740040101c4d60a4c52c1d64bee49a837b430b6f02071378dfd3b9743c75043b97c0a69ca89d54489284bb1b040e25b0153c527b97ea2f9d01024712caa732f7c5a1c081844448b09c271aec25701c21edfc31159612388c80c43c6f091262b4a5c05104469767c564621281cda6d3e5f9ae8f128b0c193b04f6fe6bdcd3edc520d70d7c063884c0e9bcbb9d20e4416052d84737b9ed92b473010710d2a684ce1cf53e3f607564af859aab6451ab0f18bf7819f288509e37ca028e1eb07b29ee4955c9c103ce5bb495e6f0b20817af2be0d801632266c5f7fc0af97b0e1df0612542c5bef08a046dce3719f85c1d3d35da6360d472f88f8e5e29d26260dd2bbabe47bd4b6d187851b7ca165330f055e1498ae7d98847bec05fe7a4fa2364558f395ee0df32d32dcbd3e82975810b4a25e752d7d10f6a2ef0fa41946be64aa9edde0293b3d6326676b5c0f7af258d3ecacfdb9205d65fd26be54abab4e5b0c0a691253188bc6d27b52bb07e7afba332c4ff245a811fb9b1720ce9825105ced4d3754fd2c2a0029f53cc4bebae5919c09802a33153c90a5d39c5523252609295e5a4de4a2ec802461438bb48e7e11ec3430c80010556a2df88a463a5274a0c17309ec0097fd79228292b8d4a0b184e60bc728fd0f53cf931a86d6c914501184d602b55e97c91241a5a3366dc680301cb000c26b015f92b628abf04ce5208517b2d4909ec767d4a16e92544d1a1a1578f6c54000d8c243066a39be19693d226b2e14529a2001848307810a9f65e4d787d04d6f6339bce4976e93a69689d2f6e1881cb9cfc2ea938ea193766a0065a00a3082f80410416c01802d7aaad41e6a4934aaf3084c0e77417a9a73345c8de1637b60b0564210046105200030805e307250361f88051a62b278da634b4740b183de03d33dd8dd066d5588105c81560f080fb20ec63ec182f6c6476c08f2ce5412711354795349486a3e08b195ad8b0b1851a063a1830812d147ca1850d1b5ba40a60e8808d9aaf52bf763072c05f1e1115dbf2c97c32f5c8460d3ca8022198d1001838e0c254eef5a42b86b859ace1002b1837e0d534a713f97f3b378a11806103465878fc1c64e4d03bd680939c96694c253bbdd38089a73178a949d2533d03d6239bf6d12682fa88c190019b6cd3d64242cc2926c5827fb50c225d32c182db5499e259753cf3ff0aeeb5d54a8b974c3d7a57f0bf35316d79752ef9b782cb194a5ec6e897d53e2b38574fd2625756d2e0af8213c9fc821411a982d1591777630eff18940ab693259d3b28dda5324705ff964327a5b68a9df25370133f461dbded0832a660646534b10a31a4a839a5e0fd3e76efd8c689e721051fb2489e147293e84c8f22213778b6585e8b828bdc665f1b27c511da50f0fffaa79d5b50b0254f86529a317f5b3ec148bdced1354dac24349e603d2429493188f4e9339d60256a36b7d6d02f9b139cc4e45d9eecb4c78b6e827f4f8f9643ed5f5faa093e799bf9a810f3f529137c7e1759ea841213ec69f0589f31e4cfd9bc049f431ed312dca5ab67aea045675e2ac1a7b4fb0b1a546ee99460920afe66428293e0a4e86d176591045f4a4dc34b55f64f4682895e6a2dc71c735011129c7a9498cbbc47b0216b5efd98352b5be7086e6d35c7e0a335829d203763041793dc98f72eaa3a69114c3a6b11e999f32247119c4c9fd46a6e2a118c755232d52521829469fdd9ff121a5a66e6106ceb8dd2af219488ed1a82b710a38d67d03c22a41482ebfa98a161840721f8d488aa5d29c91c7a1d04d799e9544e5af4a5ac20b84b4252b02fb1cab23d02c179f00c49b8fb0a080f40b06982169d5e3f7ff0f8031f648b85d09af5f0036f1f82bab01f11e1d107ae4a9ac4c949def59beac1077e4d35489aecf5ffa94278ec81d159d1844e2562c7cfedc1430fdce90941956bd6ce4d0b8f3cf0a6d354edea453c190088c1030fbce853b56926f284ac310078c1e30e859c44f2b012ca62460d1bfa5ba008d4b0809a871d784fb94453ec242dbd08004df0a80363f9b4d74fa2e8790c1d584d52624a692f6e993c0b1e73e0f3dd542e1d245534150b1e7260a4a5182fe7d69c8304eb0c1e716063ad774a23e4dea60e1cd8eebbd4316d87f6856f603fa4c57c1dde99dd74039fee25d5695687142766f068037b1db442bc14237b3ab181dd7a49793ce9785bd6c0eec91fa52d281d4d683cd4c08ae5082ac64bbf1b5d1af84b7252b21c3c6faa4e083cd0c0669151d4bb257b699186560c3ccec07feeae534aef28d10c1bc8cc071e6660b3738f6f68e5609d7f1918f33442d9253931dd6460e47bc6133153caf19486d68c31703a8baebff1fba082e4c5c05ef5491e112d7afccc230c5c14cd9b3a0d067663f68a21f6d60e3cbec0e53a952b871ad38d280dad193650b981871718e9f993d05a39a7a0f91728e8021f936648aa793c85e75117bd822c1c787081dbff117af12628eb81c716b8effc119da2b9fddd85172c781b5a60334a905bc2dd327555038f459f9f58468ed98c1a36fc5170a346046a580003f0070f2cf0a9496eda30d331d5bc02173bc99327575249d25b81ad5cd3759319fe7e55810fea924e6f2d15d88f94f29ee8af51ba330546fb538e933f2749504a813777c9b611c745732cd0048f28309652a55777a690c1030afcdf7da7f1d45b4a795ff078427a4ddfdfbea75df07002bbe9a7c73e53d362cc3c9ac086ae12ebff8b9be2e8091e4c606367d055e3a639c7f412f89cfd638ac493a42eeaa104c6ebec3655a3789da6021e4960cbeed5ff5c73acdc17376e645124e08184548ca22cc918b38805051e476043791042f5e7a02e6cf877910123b03184d43b19a44b8aaa166e630b2d0227515378c698f5a6073c88c0a54db0d4956a77534a430b031e4360ccd45bfb7a8ea75a4a010f21304ad4724b6d0c083c82c0e7be9afc9c6ed2c882e602089cb27611caaff2c63d7ec0e5bcb6b93374bdbd4a436b468dc0c3076c7fb9c5696222f0e8819264e9e4dd21a9e8d3d0eac28307dcc94c4ddabbe59a275fe8073c76c06871315192b4e96c86d7c842075c0a3ae8ce41c31a5fa419a8868d33a3055d78811cf0c8011b414b24cb7fd7a44728b031e3cf0c1b5eccd0c2c6ed063c70c0a994520c11b2666a33feb4a0b9d0d20e78dc80d7a4929a982c6afcb752e161033dde05ff06e1510346a9edcaca1854e9ba2b30070f1a30a24288bbda66b7d9cb83c70c3895948974a363d28f6079c8804b4a899f759069f3361932ae23169c6b90db171673042d2c38db944596499658d17e05f7ed923b66eca4db4d577029c8b210bd26debdde0aee52a7bca7eebe6296ace0d294508b1a828bea7115bce61cba4b989e7a509121860e5570ffb515ccd6e48a1da9e047c57deb2c3a968674a0824b1e4f26353298acdd3a4ec1b6c71862dc3599824bb6fbfef929057756aa74756c3dd59e14fcde77a62b9decf1f551f0e3a9a47a1c656a37a2e045976e5ebd5c3aad1d0af684461129da939e3428d8eead78a557d1d7329f607bf46e79283dc1987d129a41e73ac197d0d1fa39ca09bed282d2954dc69221b609ee3f59f5e4d5ff136982d3a4428a91bfca049762343921e9a99027d28109465f758c6595bf0493521219d46b869c5b82532aa8eb521332ab852aa108d366d52ea2043f394f8ee9333e09c6cbb2cffc96049ff1f2730a514f39ec48f0aef96a4dc8b3d424487029c55394468822fd47706d99ad4143a4577a1dc1a476ff2e13318bd8db08ae63de74a5258611bcc8183989b06c8bac2e82730f42597d5415c1018a8e1d2ad735ae54602375554e499a53e0ab72a37ff5c4189502a74ab495be90fc92a94481bf9c257ac3ea74560f0a6cc68f9ac652824ecf13d8a044830c2ae60446df7890c164d2009ac0299d24ab44697c8f1d26309a83ab08e5fd123825e2a5654a21746b45099ca79a70cdcae125c424f0a34442fc4e4202abfea23e0217f285e688904c757d4628c1008ac008899fbabdcedf4c44e02b7ddbe91821768e0f81bd6ca1b93327a54647088cce29920e4241e0a27f48429dcabc881510b8ea9427c5cf2126d34407f0035633d48e8ac498e3ee007cc087143b6e797c8b105403e801a76bd7f7537678c056b42a91f7be72657b077c28b3ecf36c75c0c9f3dc544d9e84e99a0336e9cdd18409d3219289032e7f8af9dd4e4fa4eb6ec02821a4dd8e0e3169ab6cc0ad6f083a4ba7067c0679cad2521a30a662ab6d6be5d42419c00cf87051dfdb5b2123650032e0ecca3f47a62f59568a0537aa52bd75768f758105ebe9d9e26ff0155cba27e54127ef0a7e257fc7cb296a052341cf445df55552102b387dfad974e2ab607356c931955537c88f2af8a4632ea9559d543021879224d77c458f50c1e9d4162aa5a4bb4b7b0a4ed37dd2f92e798fc614ec050f962d5f5d0aced36ffeab9c149cea38da64fda80d77149c92af17224657146ce7abecb9a550b096c33ca84eb9bf42a0603f5e86c631cd272c8627b8d14968b59c329d6054aa5215d45789a40c27f8dba0d2c5eff46a2837c1784eea473497bc10494d70932ce608fdff2892cc0413b3872426784b95f7b5339fc89272094692ebc4e0fa31ffc9127ca43f759b7494ee7c2518912628d1d0d6d2714ab0b146855a10d1b36a3809c64f4f7f85a4e9238292608398e428d1bd48f093ba762d7dd0966220c1063fa9b9d4698fe0729b7c5d4bcb619f39822dab5a4d31e41bc19ab24e99397777598c60df749c9094b69876528b6045b5781c91bb3c8ae0f53c78acde3a8d27824dfd889fa1fafc438860eb9495109e94a5c70fc188984829a51412c3105c490cda25a9f6b491c428049b43c7f6901074ff2284e0a3e68f5ebb97e207558d2fbaf0c21c20c62058bfa0829ac8ac20f8b254b735519b636620187d5b41a45229e54e16200620b8129664befbd15769d25007c4f8036fea296d858897ce263fb025b3064dd155927a4e1f386d799193a95b92a1e5039fd59227d12951b72b7be024855e6b4ffa828b1935b2682fbe0b527ae0f3aab2b4e5185492280f5c2e2de9447d48d6b7e38133c99f4a53fef22bfd0e6ce4bff4bb14836ba8edc05f0ed282e748cd7b771db8f1a819bf364307765584c6cbf5217e660e4c4e95216489dad44be5c0e58df5397554a8d2548c38f097b4f2440a318d52a9187060734a9e9c4e4571f53d10e30d6ca6d320593d7eb4deb8814b1a4bbdd66b7c4515a30d2523530c3660f1255fceba3eb135a881b5bcf471c921758e791a5aab31d2c0c61853c8d7579df5a91a349c3b6ee9b6a6b36bd5821867e072debd29eda14ef54968c430830b62940165580c32a81631c65019882106ce44e7b8ada12cc73ad120168a1861e0ec64b9da69cb231e43436380210831bec0a735ef4ad125777304c5f002d71e512fffa95cf8070af1418c2e580c2e9c0c12c4d80293b5749241db739c2cd2025bc182cc74ae6781bfbfdc5d971b2cb0212125bb9390185780410c2bb06361c95f2c53aebb1610a30a8c8410ef4d086dd39753017d35b6fd273931a6c0785ccbc14c3d2930e95d82487f7282ea244614184b4a9b8aa6eadfa650606375c5cad6ae76227b029b72c88b219bc71d9140218613584feb12dcae734cbb3781118f92f527a7d17a394c60f344eecd1de51258ad111ad3378ba1044e5d9749bb6a93617954a3679c756ce0011d352cc0010b7430a043868c1849e047051594a9f691c0e50f31f9962788946268a8d517336ad8b8da13c438026bae913b7278a4d0bd11cc97c12d827b2996eee019c620023b2955ca3ca232c44c8c21f039a76adb1ea1591154d5b181076c208610f62cf3149558b142f08e202726cf62b142c960f1428c2070d72552c8feaf2afa03815313a3c994b2c5f823af007a2f6e7c8d195584183f60d52c8936ed4cff4af201371a5f2b5a05d1b6a51ef09eb74c5707dd41e879c08d92d93772324f39da019b469f103a754e698374c05a1a95354d3352c673c0f95d4ab79b83037e5c42fa902235a4f40df87859a322a3478ea10d18b5db2c9df4bb4e35e0def327915e826c9506fcded5578a60dbe6fd0c384929c94ef1c49001ff29461112d36616752c3817cf946458ac4f5258b0218890cc35e43091be02156a94aee0c7423c959d72a790d20ac6e37e32b993f4eface0d2ffaac79369f256c178a8e9a82f524486aae0cdd5f53b2f89578752c18fda547e13775470b9ef2fff6f3f055f6eca7f73ade9bcdd149c9bcc67275d948ea59782edac7f1d8fbd26472705a7f39de698b25bcaa28f82b32472907b49720c2a17051b2d681ef17861e93fa160bc333743a96592fb01055fa3834e29e83df5cdf90463daee25db554ce58e2718b5a126dbd4e93ed5e9049f3c9ea568e526f19b13ac88c8961c73125542bd092ea4515242cce37fa3d604a754f094ee2fb5e3d9996092b0a0a654bed1383626f82454333b477555ec4bf0d95a841ee55982fb12a9468de5d4a6aa041b53b473d5cf6d0f694ab0a11ada6fa1448ea64930d937795e1a15a9aa24d8504148f3eb4d7e478289eff5ed99bfd2860b095ee308113ac4903e8fe0276beab2acc935083982aff5a4d147dd083ec6f5ec511546b0169eabcd33b30836985f66fd15c1e90bd713e9a389e0f2ce72cc1b447bd02822d8b72bcd9292758c513d0497d36795f0f4a64c450dc124ad94e3ba8eeeacb4106c86a8bf951f42b0af7d25c23e575acd20f82064a84fb79e269a20b88935d9e464ffab54068275cf7ba7418858b980e0369bd24a124f7abcfc81b7bb98aa5979d476c50fa94b97739023c53eb01f42bf7f05f9c0b675f4b168a99df43db039e752263fc9167deb81d5f5b496b3bde5f3c049959cad43e814c4c4035f4af443f6afb6e8dd1d18a17e9544e5d221c70e7c7f59e6f3182b5b07d692bda6de5856f6d381ffaacea739c6649a9e031fd7b6d6d52f932b077ef33c66e64c11931a07c63462f5a7dfb0bd0c0ebc4d504f4ff91e43fa0d5ca8ed7463fa7e2feb064e83c4a452ffe93adbc0a5082a779299620377d23fbfb95a6be0532b9426dd15e9cf52037fd2aa3b9de97419ab34703f7e49a96822d328a18153319666b98954317f06467a8ca1cad754dc1c33b0d716b4ffe7acf69d32b061417b254b0a88878a515cf75c271020077ca911ca2ad89e0002e080117aa571e448f2f49e1bf0a324aafe062bb19cb10113d577573c526ac0893e0db943bde6f4200168b097b60aa926be240d0808300336d3e5a5af0e5252b75c74177d812e6c143c1040068c5c4d79bcfffa88055713ff2659b0a4f98085114b06cd75a65fa2a1f50cf8d4376a7040868c2db678cfaa57f0aa9daab7f482e7ccc12b067cb882fdf7907c726fce9f4c1bc3472b1897a0353187fe64e729317cb082d3244564bf9fe86d57c198ec241e33d737a6d40dadc2872ad8111627a5ca4a295847860c3c7ca482b39cbbf72c2da76a0b0d2d1aee45a3a0630313b0000d2fb2f0801d3e50c15db68ba46c7f62221fa7e0533419849efe4e62fa3085b93da852faddff28052fb94e6fd494113e48c1a878ae273add5ace75146c4eb591275db43f49110517d2f8c91cf742b2988582d37f5aeb49d64e0a2550948c4fb0ab2de2af92b286e47e788297981e41da4eba738b2cb4e804177a623ab4f9c1095ebd46cf44e807b9f8d8049772ff8d0e9db2c58726ce1f5c4d69f55589660b3e32c17906ef3121646a8b7cc10726eee312f8610947941ecbe23d41e9faa3125cb2cf561262f283126cb4bb6f4b9646a5fdcdf86312eca4ef0b2982e71d49d7c8a2bbe00f49f02942564be5a548f0a5dfa72cc95189ba4382c91752eed5f54d1a9ef45d5c601fc1dfe6da0b31d446929f1a5fd060000796081f8ee0d24deed01d3b430e79207c3482cb4a9b84d29cd3c5d41a1f7c3082cdf628f6ebd913a2c6061f8be0f26bb46dea4deaf50fc387221871afb869fbccae2b89e062fa66ab389e9a7db3850f44f0695de36f96a02989aa858f43705a72e84abbba11f23704a7a9fca3bf67de695a08bede5cf5edf253690daff04108de64dcacfc397a2e8f61e16310eced4f1a95b46f6d4c3254f810041bed47c93da14ae836104c8c21a95c9353e7f693133e00c1483f31b517d157436b02f413b08f3ff09d822c992688a949af014df8f00323f34995b4d90d267cf481edfcf96e4ca2c7e8f61a3ef8c05f8e79ed9eaafbc8e6c71e18f563d933069d0f3d703a676e917a31755ea81f7960f3e432a1feb992aae4071ed888ee9382fc1c3d0ada6f98eae26f9c013eeec0ff9ea6d0497bc81c94c65f0533bcc8e208e1c30e9ce95112f2250f091f75b07459ac2dede8c0a435ff5093318a90d6b0310305378c9c1b5b20d6c71cc87fd13565882439f01352564e4af47130b72a56e776d11a97f0010723a95fbfb20bbfca0b1f6fe05ac53c567255ed72ddc0d8969e9223d936709e5e3b87fedd47d1b08133af095e3bd235b0de56bdd5a174688aaa81d39fae2b588d6895681af8d3123a2ce7a9117941039bcd5574b09442436bc68c1b4da3d8c719d8d321b5952c8bf91e3370278445da6429c224a50cfce9b411af4939e51dc9c0954c5abba23f06ee2245b09a64b2d23c31309efa1b270911db932a59a05c054230230035f80803a79571530c9e22e6a081a16af0f1051c7c7881ff209397d2f9f9a1aa2eb01722a4cdf38a633ab2830f2e309e2ba4cf132ca4557f0bac9b0c22a59e5d588db4c0859883ecece4675a9e2c905de4b548d0fdb1b447f8c0029baad74166c8a7a5dc2b7042a83b9dcd532726d70a5c1c2fd939a90a8c4842e4bcf52483245181116da15d123724efcd1458df49e9358ee8558991025f955ff2c41c8f9d8f0227725869fa1dfbb82a14188b20b6e933e9ce127d02979744ead121e904be4695e851dffaaf96263022a28bef7585097cd6ea6584ac96a7bd25b0e9e6d99a9a5242d1c255d484dc5e8d38c21bdfc51733b4509005e9f09104c6d2ebf5865216621324b0f143d27ca6348ee7e908fc7fa594da825241dec9088c6de5b23b8bc058d29a2bd9ae5d2c44e03f664e761f9596636908ac8905659d759b3e2d04b6cd22c44add36d90d029bf774d09b2110b888653ae9f67ec07e9c9883854c318690f7019f5b3aa98ca61e70a5b42c6948e201bba2cdc72495ef05ed8049ff593b5bad034ee84f1b95de31883607ec95eb284d3138e0e45fc6f536b967e60df80f1db5a516b701b7a195da832e99a404d58017af3d3bed0e0d18fb700d6d25a3a567065c275d912382103ad21f3260ff4352f34ca1f4b8b160e2a6f1dc51f4b26560c144bbeb88b7196d92af60824cd9c47a62489e3aae60f36204d99f9364b15bc148bc1d333d2bd8ff49124cb555b05a26244fbc5a15ec9ee9b67f5d2f39e95470b232248e0c6954307133894a4c9237fd5370a63f597fbc3791846b0a4e62e7ce3ababc57dc52303a6de6d58c99e93d29d88ca294474b2a6a8a8f82097aaad54b6944c1a7592edd0f6da231130a6e3492341533030ace94b595ed464fd2ff041b3c8665aebdb75c7b82d169b2c25fef4dd34e3096673aafc909c64e33c4fa897983a99b58c58457525f13bcae4ac821266526b8ca08c192da60821fb51cb4c8d8a7ca7209ce3d8b6d289dd2774c4b70a142c3fa476290a04af05ef2cac5dfd3590e25f84df5936369ca24d81077fdd7b22a77a4488209417e16d5b613dc8391603d68eca83a2224d80dc1fff3d4d73ffe083e3b27d1d9a3c7fbac23f8e029e3244da3926d04a3f26a9a2795392dca083ea6bcfc91acff335d04e79f940c6ea722b8ce2942ee9a07d12997085e3d6a3479d623825393bed7299e10c1ba437032f95db06c396bc50dc1a86c21c44ae942b02958e5bd20b4b62404e39af5a2d89a67b88360e3a6d4cd0fb611a282e047086531ad66816054e598124f0d08364af2abd320f289fb07f6b2c7107488d85dfc801239ef4f62a5cc0d06d007f66aa4e5cb27731049cb072687faccb9ae928301ec818b5bf9b6b2e5f5c0beee5966a5868dfa5ca00232645cc08b01e481bd743aad650fc2039fcb34e987caefc0e8584ef2ad62c4341a3bf0aaa72425653a9487681df8243a56c918547460b209a92a7952b41cc98c1a690617377a0533de8beec20b1b5edc7d170b18c01cd8d611f23abf993c213930d2f4327e7be7912971e0d2c8c8232c75cceac1812b4b679a5755f3c4d21b38d55f5f4963c80d8c0cedbfb20db5812f53e29dbb3f6ce035be558a8dce1af81aaf68f92d65b5243570a2a3a564224a0317735e771e65a38137394affa7bdcfc0e468da1df33503a3fe22a83432277a956560e3e42b8b9184add964e02b9ad25da32963e0329dd6524fa318f87113e1d90c035fe9473db86b08fa2118d8f6d1252b646e8ba32fb0a292bd2b2466858c17387d8b6a75f9ceedb32ef09d51b36554d3f2b0b8c0b778a8e49fc452e6b6c0efe918ea26335ae0bc73a49379d22facc600b2c04a0c3a74e678a1af3b16780bd15a3af54516a573052e746ad3bb7ca19e3c56e0242799942c3d19afdf2ab0950a8b11a8d4eea6026924160804e270381403b1e9ed010314000000001413c742a14820908455f40114800452281c3828241a1e1e1818361807c6816030100a86028150200c0a8402615030201a4403aaf905cdc8200a1182db1ce778c61a327abe50ceb843e2acb7b0b269b45ae9c6b711fc689a11af0dbfc9393a5cbb8360edea843fc6be24a486b549cde159628eed8992fc07fcba280c4308828853c9f222e958fd16698d5b7692d44e3deffdeced41112a38f470d64fcaf6116f16d197283e98de2921d840f61fd0ddd59b462cffc96d7997500f40d2031cfc5c7c594dd57ce904df27c1477ac67a7aaa61b7df6f927cad393bd824f39b938cc3d9bf0583bdc9a85cd532eed4e2cf4b73e15aabb7aea75ac32b488c0eb8267ce32cd698f425318372b2f78ab72a47ad9504911f7fc21cfa404369497844e4913bfb2ff05eb812d722c7c0e351ed6e52a8f1a73ed228b3964f52f8998ebe06550384046311721dd29c30a46a6795d409198f7fca1235601a123bdf58f83aefe14b70f21476740c4156ed53830415a7e31591b395b3763fb1ee065b33d42830eaf5c53b5a6598988673c2eaf54543a5e503a2c652050988a494446df3856fbf01f67cc600cb50bd1cfc908b459fb8df33497229945cc586333a9f15ddb218348993f4b26d96462a8f2824ddeca0ae903b77bf031964ff28bc90ede3f1b7fa7211771a72632a3fd16eb6fe1c851f23c1022ca9a4dfd77858ddb9beadf1136082cbc7421a8e2c16ee067f865f145555be73a52838146eaef16b1ea7c657bc032dcae8c9f8854a8300d0b07c0ea3e5875e4cb97382fbe4b27ff72241256e1eccc63b7f55e58055b0e90dc748d3a459710be42bab91496d4fe4b2d9e79c427e0ffe450842420a87a0bfe9d37c677bf7cee0f7c4a7cf4d6687f28c978abcacdb1ab7820f54e82d038fae65a237e5e88101924e3d335ac257b5a17034d674a621b30da68fb0858054bd0794633b633a3b684203c505ae0d4513e2f37c004b1d33b2c7c65ecf8eb4e44f845eb7768af33fcf43a4ea1e86409178fc943af49af5aab08d55dcf3404a93af36628eb4a7fd304adf5058fc4c97d973df0183f989c91a0cd6b26f634f0b26f1a5007f4bbc3f758422c698bfa1f794a3aeddff6a82835ae7d8e2846c8f91ac5c465f07b95b46caea83a356e8a69a40b5036f711467e0c868f6690792512840e6b9e558de401c9a5f042981b7789308e48315a3aece28e90c94fb7a08a18671078ce4fdc0d485c4d3ac701fb40d7229fc817b9de8cec93e846254b33687f617102c6b455aa08a592f3f0b31af5d0f4fcb7c9e30066c079265c78366838dd58c56d4dd876998b4f4315cd3045bd3f45b6bdc6e781daa8d51a699107e010dc2a9c468b3eb13031a3a13de3de43b8a47d8c355032b9a7141a53e92a1359cb0bc3059e6c45067bc157e80871c920308e4ba025823a4b966b8b508760b01c594f7c1d2a8a053e5cba73a81eda523d08391e89d1be3515ec6ef27a91aae98a215c00e0e7eb797ac463d5920c79453d08d194f2f1c58558eb565bc8c7c3307b6e6ea0bdb28780a17b006754a771b53f6648ef5c9548803819ad8c23b56a09f6b125613886a5bc173d8d298f52eab85b90d46557626edf6df67392b3c7c131a1f5514d522b56b73e6302c275ac1aa180e480206105b66d0ca0c1a9b07ee78b0ffc95570e7c51051aa094700abaac002e04a042e7000450b271b08f0615c4158fbf33a69750764177863c1906d0678cf5b869c0fbe50279fc91ced55f0b4d4e290e642a75fc4cd07140982294c39b3d83d350740a01ce75d7a154df546a886077dc566234f0a3568f10e3ec988af19883a4eb9d33ee5c093bfe8535db65879c69d9b27ce80e071e80b814f190759399be01b40bc01847aa019a1ccace809280661481159d3099a81289e9a47595160148e40271a10f887df4e59ecf32332fb025200c5ac5478a6cb69d0ddfaae0242234ce3d440b456472b801b03e1a28385566422f5de2ee8aea60c8e959beca450290256c788dece5acd1534325af841a5609f7bad35135cac6627d7cbd9909027677bc58d1d3a74960fa51a789a8df7738c40289500157218ac756027002226b328d74a610168bf154c5d39a971d3086bc35706efcee869ba568c65dbac59aa9d9132fafb467ba8ae15dff5c472de8a246ac51309e65cb4d68e5ae1e8989b37b7a290b5e2dd0ae6355affa59eded24ecf4de3ae326898b5f26b9c9a28cda7ca906a5e6a67906534b44af3b6542b6c6e9ee540a1af34679dd5d0723a755757d0cfa573d767aa158c10ca5a2ba847414fc83f1469f852eed756d6ae512b125a981882d83fa38cc31766ecba9631ae694fd3bc282cd9c2c07ebcf652efa607d67bbf325ab981e3275a0795593467c56edb279f9e5bf1aac66d63ae8157ade8465e19432dfb514f8d38ab823e4b8b623d01b0de18215ce57c1b776dca95c1f9908d576aea6971fb93022b09b5dc96a1987bb1e6b62a274b582e06d1a4f1a7c2445f9d12a2e103ebf05392a69733732934e31786b3b4e4326473f116eb8b39399a9272c2e778a5601808bd14cdb6b6ae3c8232f6af6907b4163aeceb08c96ad4c873e76699856646d3ac85cf20f53abc6a780a48407d692032bde7a0fd00e94d5df2635b555f2d98931809f0c07b466d3beea8ea388c4c5ba05d0da185e91ce3e8db67a6bb1bfb6ee8af5cee056d9d44c833214d0b0a86c902d44d3763946adb0aa6c57994d9375b05057cfcd0a3719d4b8f4d0fad1b180a3d4c9b8b9b56744fb04601410ac39626fd1ca6dd93ce085733076b360d2b5cbef0fad7b7f69b8528171c784cc3a4534fa45f46b45e3042479fbdc405516d591bad77634074e614d0ca23c03221698c24454a20817162a09e5bf1299a088e7e211b574edda336d3a6bd402f45d46ac027ebff80c874ed20632668582f6060001f03d4deac9cff9ba259a08b5fc26da10090235009641871b030894ad91646dd41cd8c018e7d4e349765936ef84c201640fd2a02e8a41ea66b517b6d991725539259503eaef2908d2feb8c4ef63f2812b02d31aa38245cf9cca488757f262aa8c9da6da3811535a9f0cb9ed9c12fd1436c6e547773bc39b32e890d9204c199f1174a6907959b312284bb1d6ea3279b318de908ac409b35e96698d8556dc5ea862b39c5e4b4449c19279b8ccc693336acf76a55cea993335be65ebf0d683366db2d03b6960363e6cc90310da8b08d75fa506308837637acb736e56d54d8c46d136536d3b0f02d481dc42779d94cffe6a69c4d72367046d95cc29b3192e85083d7cdf7c51933f9ced22ada404536d32c6d7eb2f81b7e1c2f6a53244f823b51c9498f9c19e4f644845443bea6280975b3f8de88909b6a4f5387da73f8f76c844af760303700fc3109bb6025739ca9d27dc74bff7a579732af8bbbc29d10bd55b41c08b7d0fd98d5f8a1d282500c9ac6099f6fa84dc822040e62215043955e8f57153111e9fe20c8ac9719eea700db98f838e1641ee952e68763da9387752dd475b2b22883802ca7847ac5404dbf309829a107179251b4e6be74ea3f856b0caa8647a407ac8b253f996456fa690b19f7686a2a1af76f57c14776c5611970bc3ffbaa6dd1e87c43f10809d5a3adf8977e836cb8d739d6d6d486be8f54618d6184f7c227c13aa919fca89e158487a9926fc84b6ee15efbc94a1cf71383841fbe1bf9695462b1f8cc8e0d28ae5d9ad848146a32b1977aab44a90c55a1399b8800fab5261061af0687bb9a346849cbedbb880acf0a67f31722d0da7efb23f4a90224808c7bf6f2cdd8a1d3cf87c54e94077a2d9f467cd6a5ba2c13c6c004af8251bafc8de92d6a5bdc7a05106fb73bcfe840d68258d803e32516e48241704cd0b2efe9c391f3ec8b36328bd4d11af88f08931f26e14d7b2c2d9f2cbe5c6f854dad67b8366e4df66408579b61bf76b6b9781bb31280fce5e018b6c4461b3d32c34f7c7d122909080ce4cf2f585182cb98845c62a234c699c64a696dca733695143508d428c16072ab40e9fd361621490a39528860a296b6374321aa5d1a2b924df82cb66d1f884aa20d20f1671ba4cea6c25a542e22ce4782f2e2a09194c9a8550be9fe41ebad3baa60228487a07f6e240474b27af9c2344c55c5730661c90bb85a3290408504728053ff53c129b91bdb55b74e0eede061b26f14fbc72c39b4d6c3e79d1b1b1417dc8f10bb8fc129c1d5c66083ec8c9480c1030a0a801857a7005789db26fde1396c7944827d900d67ac5841716cafb1b06254eb235b35850dde3787576fe2078b6f041a9e367404b9f6c9abe87a2387149e1f9599d4d6a25036758d20bd8f91dc3fbce98235525adef5be39e18af0bf70b5861b5df6a2b961e1342158b01231420cc97740279a13a607e0506e980cdcd8f8228159cf7b14994346601131c33116cc8c77803ca82480b8ef1887ab0a97df1bf32879315e82d7c3abed257b01bd582fce2bf37a79355eb9d7c38b2ebf5376e6d6647af80292fd321746029bc442f4322f7b3b3c30dc099353c283957df31c0b0f571081f21adfa1a4f0d8a62782974851d217d953f3be7953de61e27d160266538443b0612630cad73e0c086cf61d98f3b1d525ef0d61630a3c22ed5b1802853066190116f17af658b32c18f605cc33b68061bfcc8591c09a1c0f8a1fb2a59de38d2030f88ec43c2fde04dfa4f048f863784bb832e0e3af09d709147a355cdce1c2f9bfa8e4a0eede61a5bd39980d490fd78d3bca6fc93f3714e6dd69cad88531fe25518c4779bdef17a20722628c016ee063fc526ee20308c600b2f3d7b1e421d504433d6383b0172447617855f9787095ef200f76f8dd6b10f4854b050d481e598b594c179845e9e371d0faeb82000ca6c28e551e160921c6974e1d84168afad061e31180b7c9883f62d958f10e53472fc5165986aa3cea706ed3f64f30609c94fafe237baaf4d45f21a2c923c259cc17e6bcd8ed9d23cf4251f804d1fa7c8457ba091f76413cafcc4a1b7601c10eb56c189b16580e77b30700376bd54e11e34353b22609fd4a8c0101352edc67a223a219b00ebf2604cb43e53ee571674746ce43c2fee137d86f289a837e5292ee7eb2fa234346065e4f576852fa1f1bfed9d6bb93335e25b105524292a98375f4729edc9a13cb1220d3eca399b2fbef2da0e478504f1f7f39545dc90a00b29d7d00dd365b2bb5c2fda9fa13f2fb4fd05a487e48f17de2c0e905424610eabd0fb2e3a4cc9414a527bce9eea29f19f68084fff7ff61bdc6fa333661a9b0fad149f98e55bbffe5c1640ef53166292ac6a57dcd1a828f30b5714caeaa3808c8f2e53a9f75e05dd8773b673232608dc12c85ec5b64eae067e00a3430602ef25870002efa5cbba7e8c594e88065fe028aa90953a4cfe7f667a7f02639dd06342410c2717d97b597a2cf85640aee211ab116b33e74144f0b32d487d0244002deea0af34176e12893d52e2921a52c74c41e0995d642cc84e4095512ca23940742b3cd9c0534141b82465453a8aaa11c445d4205844a62e82d5a05950421c45d0acdf64f16ef17174921ba2774e9d05ea26f42870d5d46a23752d1cdc1167ae7a112641864a87ef16a9a17c26828064539408d0c87fb0a951005d0448422be45fda4e82d991306be11555f43b91670f121930d9540368c08331a5420191a4bd1e728ca5db88464704d28a7c169ed65a29caab4d2e40d7a3c3504734abbf27c694e606e802afb33d28006d9aef3247283daa311aea72e9261dfaa1c1b7c7380625c5b05d084385d46afb2f4f58f2bd355631fef9583c2d1ad6f90b7919628a23a65fdde0384eb59babcd03ab987ba9c25cf436b83b51d0021d42265d5cbd56f8d6bcf1c07a690819bf1362fddf9a6032a30ec259b2f5dc8c3d75adc754ae565999ed26dfecabccd0fca755fc50c74a11ce04df0c3174eb4404813025e278996649206a99a49e10d73b82b17b7274009f549744b79390bd225d5502d6eb167a1d81601d689b670242e3c2457b25749d9479dde3d4147e87163a499ba121b4441a1a622749b9b9d890ff1640e2af72bbec3ae62c153a191f83853e03cc0c7cc0db0499d701ea837064f441d744a77096058f6ab00088e2ce5d83f1591a3e2eadc91f6e0075a30ef7ca0aba9a71780c2087010fe28b2178628c1ace8e234dcdd81726295b5c6d930eedb0062c270e42017349ae9b19478a82fc8cfab5368b952f29dec97f12cc7b228d865175e6da0cc869230d7553f2140b987f80e2239b0f1c28c61f87e255034306080108adb7c4e0d0907839cb4228565158594d2716542948b0e2ac19b94401ee15646b8b865b45bb4a2b0c6f5487cc40d4f4d104e5e1d7908666db687550c1203ad97f0e2dee18a29774805c710a8a211e72df61703d01613d5f5da3b73174d4db4bb1f52ddf9d8e42b81d2b540ea204e7231c7cbf54a59507731209e90c7aecd650e0e5af24f7f0f8a82dabf78992f76ae87d91754b808c95a8bab247a25a341f161aa85610e24108346922226455585c93f6ee28a831427526976ce871fbd33acec1174f89fcc72501711b5b89c5989b821a49b466edfc8b06408881d4da7f13b1b470438ae3bd03722a5d7ed0becfe087a8be060e2e3a780ebb36fc82f97e4504f6ebd651ae12edbff1f2ac61d5bce1590743d7ae1b0913786c256d5523e3b2af8b09984306c60029b4c04315207a220b39faf69320530c9ca230e01f8ae59637f89b9a926dbc683c1dc43b58856da4a10f42432d7f5ea2c0a88d13176f9bfcdb7a9a64ab48c8abb6e55ae88271adbd71be52eccd483f22b105d1d908bb9fe97054b4dae73cdf837b7532cf227a2a121d2b06c32895b909f06603964880981c7a1a5dfd82c605094830ad1be712512053c278eea78df25e86b63e173a4c027dbb98ad81a30b6c55eddde559bb902d79a6a9a79a3910b4302964cc5938c47f0608afd2803a68b0bae471430ec84cf0505a81352c14da711b20048a20a405606c8291c32b898e5431164f938fb8f318462224e1fdaff6293621b9fb6044d352d76cfcaf5545606d3fd93717ab4005b3b6eb662604486c7301c4bf0d1e093e80dffe6e4c1f4984d12f4ef3d743f1668c32d44209becc59b0166d764d2b4e076b7107b556e2d244150f3334df42a90db4e2a40504353d9b5035f79f28253efae57b43dad552c31da0c0cfc0a064b3c02c5269b7a7ea0c02a01d72cb6bfa423ad33e22d19c104528ec2875611ba56f9a83f0523700632fda6ad994347e501529a262e66cce08aff5a68073e20f01b7308010d6e78a00693d3dd93d9f3cf28dfb8f2b878a13406d6d96de798d1d8de3ae07d99ac3d171097ee0b51a3b9345ee2e5d5973209329e67c82e4b5870bb24255b27ae5ddc668dc7897a656defb5eb5c2cc349381fe35da8833dfb608bacda8ac0ebafd6b9e68c7fe3db532422a137ed34f364b6b000b717c3b0daee7d7d5629952de86073d23947b5291ed2f69a1b2b8b967598749585bd9e9cc4e931dc5b2bac20a3769413d185afd4e915ddc1c2f5285790b2c4a150a427abae82e10a16bbc173e1b21a2e273bb01a00f043b81df8db6d6004cb6aacf1c276acf782682f7142740227613e115a2a637eaebda0bde85536b9b3202d3d15a597f619227419d0a66569c03f84b56bafc09d1cf24ab0192a721e9b71373b9b06ddac8595e4f5b85ec580d116c6602daa5cf2dfac335d17655c0925927eb7e8cb643f09074afa4c0a2a296152aa4954cacde496768044f50d627aeb5e821f5b45de9433d9ac841c27aa855a5d6e5b4854672e026deda2e968c27b701149ec71129d031b984c20a7d6f8641b621fdb3bd9f62de3319f0f7c43ba80f47944681e4b1d619cb3cdc34a7c2c912049eb858716f23129f4897eb320bb7d80125219404eaac14559bf204b1c12b890ccc32bfc98a489025422f5c0f623bb908e04b294b8ac6ff300e1da8f13ab3e875e158fd30382639f6c299140fc6eb63fbf787ca54fff1effa9bf634530a3692d74cca210323a4a954e7e36216e94a21897704c132b6ffc657a7fc9df58238359839c806a7aac2434abf1e5ba16fe0cc0304a166a88081114d4af11b1aca7541012967edb09e922cafad3a56f258367dc2c61b3fa523f110a82bde03e59cdb7b54bc54a1c4f0aa9496b737011d69924394020645d6de5dcf9d5a38790020b45f4f26a66be5fb302d28d7c4589eee5ad824c8690b398a91371d7ddd963ec28acb6e8b9105f3fe934b3382c5a0699cff9396312b27011800b6a0215a03b6a359a3135033f5b1b7343659c39ad54786febb404c50fb75cd143eb49b57f3e1c4aa21d98cc22a060adafc9f2174d2bb1bf471263491b1f5bd5839851e66aadba49ae528f34f19731e33116e67525edb84b8b42feb056257adeb11f0ac592923c31cced48e7e5e97dc5929ee7ffd9a67342178f871008c461b120c7165e9c9bb0e0bc020ec9cfcef7c97107156a5a2e652c2f440e6a6f0791379d91d253ae1371f3fa47b67ee5c7c224afc28f121941e9d7994bc36c2a719556b824ada85383fa5012f56854138fab55eab15161ec81cc017066f5fde63cdccdf7a5899e8ca1b5e2e890fcb61ef4917186a1c9cee50bdfe8535f3d746ca33e8fb447b6207ee6541af91c71c9f34612e81be8b992a51b5a732aa08e84af5d5c30d78c02c4af90a7bef8a8da822f915d6033816ee8b4dbc960604b2d40c1622c7bc63b90eba3d6ae783d668fdac4f22d592f90bdaf79d4840907ec12f1a5dcc0aa8dcb9555e1a8641741959713888e6fb341e0ffb89991631567cfb7bac6146147f1257fe4c74aab616ffd60278b5763c508734e209ac89edfc0313c05af26439df61ac9ef05f879ba4f8b50a6cf82ce2c2c2deded88e32d205a210df32e010d2dbeae771d12fc02e3707e7afda2b4f5db4a19bdff161898720d0ede78a1dfd376637548032877318fac9310129cd262d995401252b0686dae6297fb0d6c2df8d833c82d27abbfd330b8a2efb580966e8d63d18198afd4a1481efb72260a8a18bbe53de1e35cbc8229d63529ba14a629823e617eb42aa96c160820cc5053da0fff77f56b04798b1b1b60424521da0464cb36da313d6470f53b57372cdcd2d7e08a5a98d50199919ed36f3b5d3b7c2008e3f409619042f18d175808384031f29108d9ae46ac1f9b5f6b2209cf363e4fce67a15d68a98c21ae315e9fcac42059d0421df9269bfcb2856a02b3f4a989de4a5b34a75234dd19178b2ef0e2b371f4f3a5d43de8f6997440b4a923855bdad261aae37fca4704a6b2449ca9e8e25ee03dce4d268d0272881153951e4536f2af1d4d61df23179bd0393bd4bfc7c336841acd7cb3d10b94e749e918776721fa06382e29d534d01a14db7de5dea5f19176c45c1101767f5f42e767e57ec9d1e135b44eb920bfb7b07be5778bc654e23b857fc76450967eab65ca5a0430489ece93cd4afa1a62ae58207965ad791d70f405f1707cd1286e29ae0a6b68d6c1aab5a7c03eb92407b57c1697e876263c9326d130136bbc1b02bc6854d6386ee51446c9490034595a58a8e38bacf3e2e3588be55d827a56c49347a82747071212667b09d4cb110fc259af9f2dbe80495a1f5b80bdcce7428e2b50ec2e113683f4b2f1361721b8aeeed659c17fbef995aa0c5781d0aa29255d5813548a5544b904aae0ad008a07b2e31dc4dcd7772d9188ea3d5432c52741e35977d4daafdb55dfea430dca168ce95c3cc431ebc771e48a267486a85a91e5880e1ced0ce48962df5c3e2e52fe45ee386c8e748de232954cbd75acec94d34e00d76ec2da181b639c1044ad6930614d5b97b0ca944437855053c6eb78b9ef97e75b1cd715293f09b40d9e295a85bc05cf15a4ef71bdfe209e19f0d890b33a6addc8e076880470631dab86b1832a32ecd43818cc30f90e37de84f38148b94fd44d0597e57d9a0700b9dae6370c631b6941bd5b594453daf44eff87cd9f026e128874521634367ff71d98f1ba9f17ca03a80c2e60a61bd07654f41545db55e6270f3767099f510af7b8847575ec69a4e8bfa393d2b2de42acd4ce0a06c305e1c2089c0e1f8dac0181634b08ee1e27188049715099132532b5114e7d45dd100856c189294b837a62b54e1a16b003d0c0b4e0bcc2ccecf0313a84d8db0e40f0686bac9a7d90f5df7b122f16c203f03f8a7f9ed8043ffaae6e060a35ba019821dc9ee3c00dacbf07638b98e05862273a0004ae35487130ff89edc5a28535b6bf4c934aa1a4211874daa0528bb0038e8cb846b7482cdf46d9ab0d6be8393812af7cd6c8a81f20533fecabb326468a8693c8d96375fa92e21763f6b46141fcccd2549d53aab1d85f9fdbbd7baf1708b302476b2002fd95d346fdc9d553e340fef37550e88eb5825e06e1c16d933b00dd87b9013849bd616d83736c7f68073da0f3a255290d9ed15656d872f7c523fd52a61747851eac1420f8aae7441aa727239a5ebdea84b6af6391809f670142214adffcb7be057fdae8af08fe39551b7634fcbc2354bc48a5c100f16fbf11482f1c4757889d603b8898bcda65f77c9b77b59293b963edb2b5d841fd3ed8110b13e7aa0acafe0e6fcb012d8d003efbf94f94f569bf859479e2011f0f81eacce56a8c528fd2e4f1303ade1619fc5af08aa050d5e83eacaee645e7d7ae48e7a216eb247a611d4529d0f188c53a88aa6067118aee2c5a91ce452dd649f4c23a8ad20340ee1910400904c4d0801daade368ec33dd9c18ba92d514735821f970d540175b3e106a81fec0f4c1aa6c35b232fab386c45897f78e1e351e07ead474f36467316a978a7a315eb24baa29d4529deb148453a88bc6067118aea305a814e472bd64974453b8b52bc63912ad2d203d692af3aa8f2590a086e168d3acda1a968e7a522382f3a0032a73a12a30cae9b5cd1d704696c5ada53c4cdcc836580c11f6ca9801264b225525fe9f034aab0021a2629a7d7c6e47dedb8db8c2c04ac037fc31f3bec88ae688a52c7c0a67fe870bdb4038a5105811b1c635bb8a9d9e08ac730d5c3d78be8936282a74a4eb5b173b781d1ebbb5bec9e1f234e81354d63f2a17d7e3980370afc7acfc71a67514a32ff3b36ae28055879510df6d88255dcbe77f3c335740a956604519cc1b411d69df65297b8e319514e69cba294aa3a2f657dd859229090d9606d0d907490ecce627d308a162ec17efd0f1af683db08f54c925b89c1b92f9925f2400614143d01ccb6a33dfe52bdab814e3e1aaf1822b3736ecccd87d682305140292297d8562dfebf3ae5dbaab348c786e86eb7b058b855079bd03dce5110bd2feecf04a6793f5ba8ead8ac52e16de01b291450d0d6129acdfe72ad299f197d2f7a37f6a2025bbc401ea503be9c8a0bb7483a0562a8b20f71fdca74ef38f69965552b9a12faa4737a766fa091af9bd5f2c98e35b828704ac721202cda0451d87d1169760c230b61793a35a8dcefa63f8fbde6dab0fc98cbfd8a076342a0c0ed86096fb3673100ecf7a9ac3ebf9fef5f9cca287def788e217cfe137d1daa260444d2978184c485cd5ae143d11e001f9207d2c314e94a43e3733084751a96857aee32c58e629af77fad13cd02f5ed05ed3c8763b76a028c9902ae0b04660a3860b46439330f0f0f0f0f0f0f0fafa18d90da083904429a4c494ac0c9624aa14549a694a44c2991bd1ba30de8e9ccc4a733139f2e12872e019d0a420a700a33d470d8f08234014628d2bd2ffa3666fc4bdd0b608082db0c56212f4ace3ec188fc174253b6ede51024c0f004773a82c8d8592d72df09ae525faa0cca93caa9e404bf7e9b54d29a7387082e8e22c0d80477bea13bd1460555060e1a9a60254bf2889f5d77234f1899e092d2a5933215d1bae2828b2fd2596a010313a7fc98ffbbf42d5e022f502738c0b8045ba3548c37e9c534441c396858829d54425d27eb743a56da03605482ddcef531525f1f1fd706189460b307df94ab3e46dad22418ddac5f32b3335b64788d3e4b00b911c09004a373f41c56ea9e1c604482d33d39e9fd6d3e3e549083868c06801a604082b78b9eae34d2569f07c623d84c151d9e725ef40d0e3882d3b9ce7f2d898d602fa8204cdfe3a94b32827d4ba6a29b5f4a80b1087e54aae410f447f6978ae02e68932d6efae39d7e13c16437d19b848710ba11443049736288f40fc1e79862a5194fc61a0d39c19921b8d6c956f991b5ecdb42f0314f2e2531efc9285a1260108249429efaabca20a6f241702745fb266afe885aae11600882b11ce39aea53b764d2fd018c4030f246a753690704bf13b4ee759e52d16bec018c3f70d1732447ac0c0f60f881bf8d172ffadb26130d1030fac076be37a5848aed972729680974f1058e1a36b4f0cf61a38bbf40cd01061f5853e9a172081ab564bb07c654a8e94b7f7e49281102430f7c5f9034396b88ca70b700230f9ce4984468cc931dc0c003e3b9f9f427a1c7028c3b3051925750b9d33930200194020c3b30228745f2d8554173ee041875e02ad47910c9934e214774602f8ae48e1b74c7882e8c39b0a69bb72ac7f7582109c19003933d5b08de2263daf1c8c813c08803a334671271f7941c30e0c068f091a0d29768e4b051e3ee068c37302945e6cdddaa1b92d164b261b4814f8f6a715d73a8cb1f2e3e870c1b6eb0060c36702ac9287aa39deec9da1a5813a2e4c9ee0cd5c08aa71cf2664b73e0285d809106d67c445b84887901061a78558dd8af124b4eb0cc50eb824673818e0b30cec0a5ec5dfeba4174bc6e8b1a3472a0f722056fc200c30c044619c84003c61878f5db6dcff4618881afce97b23d8f64c008835e1252ca9023677b87a909030c380809607c819f9093d69b269d9f5466a8c9a861c30bdc758ed9c73f731025047861e386023e3e70e4781c385a0b30bac05b57d42d29eaa1c423c0e0022fca226598d71cc8f10cb81cc0d8029f3108979cb154a87881a105fe368755ed0429cad61a3464e4b8f13870744a004616d8681db2bafa0717bdcc50b39183c691e1050e848105764f698b97edb1eded0b7c7ca01b07c615b8f6a4619d83367bdf0a9ca90d2674f348cf7c15b8dfac4928b71315583bd56313a3fda8264d81bb1425c8db520a9cb29c47275fb390cf1405f62d63984437156497a0c0e9fe0c16c283656b9fc0e6da24d3c2ee3448df096c6551a3f2e3041db46f0267e9cdf4c499c0c4cb66715744b6f64be094cc78a5e95402a71a25a76bc693c0978b6fd22283a7dc50f0356e612081edb7d6fc5395637d8ec0a63ad5cb15947d903102278448a37e841c11691118897925eb42f46e0822029733bd333ce6929e33043678304df9ed4f83f40881f588363176d6a4cfef20b01547f7e90d25b30f0436570ad192ccd348fe0f18b51b636a467dc0e9341e96cfdc1e70da9982cccb2d0fb8b8bfa224fa7da5b4d901b7222ac742905a2dd201173f6f25d6aa39602fa20413c94d1cf06b5a22e85a35e963de8073bb354fbe693c4ed2068cbb24539633852044b2066c523a89068ca5947db462deacd20c3895f2b8c91c52be2492017b398455fd27ada5833062c04fca1ba345d5899d441830e046fbc2d5ed3a48ce2b982472a81c695f3b645dc18559f6fa4f9656b09db3e8d2dc113c968515c5ed3bad82f7cb7a1a6bf446f255c165d2129d605d7a44a9e02249d14f3f2a1825f1dd93f47efb9ce25531ddf0099e24c5048f01aff1850d2f6e3c8e0e5370dad43393d4482918b55b299e2e85146cd227a405d96b9f9246c18510a9eb540e16c316057b5b21e951d3bbb509051f4b294d6992fe3fef40c1e690ff7a7ad927d8de6457edb7e149c9135b47eb13dd8bf419006ad0d109fe8450416ba87082f16c9f6f9fa67ead9281e6454be00ed0b109fec374caeca5df4e760303a509ae83d2496aaa6c8d39efc8049b3908cd99378e09fe829a8a31e279094e08eff40d41e5e89158820d1d293ff42feca844991a3167867650023b26c1ded968c58ea3aae3da2109fb00434724344d5b9b33436d0117c0e220f1082e930e964b24b5232694830e47d84747233869af9add5e31dbc969540723d8d0ef6b49c4e86239ea58041fb7468e9ea0a4084e4694ac26af4bf4d789e0b564c98d9a2b2238537dbe1abda35885741c827f6f3dd567aa4f6594101d86201d8560544d8b9fa4bbce2a7510c23e72108ce610d931d36ce81004afa1bb621afcb57fe3397e0b2d1d81e04ba61d8ff8254ce56b861a0d2fbaca1a3a00c1d89dae0f1d9d9feafee16a2f37f5f90c352c32c0803474f88151a5a3e4105bd3d1074e744addd724562af9196a9e1d7c60d367ce135352ce50432974eca173738be87a42a866a861871ed8ecbe5321c4a0aef1321eddb091cce4814bfa2777b0d0fb91d32a880e3c70166e2a9dfc5c1bcafb43c71db8e4eb2932a6d869bfb40323377acc1d3526d55a12ad43471d18797b1ef4f6eba62e3be8c076bae7a78d6d08ba9ad1310756dd64fe72ad90bd3e5bc8a86123fdbff82f2c5083021f1fdb21073e67b333edd2229468c481cf298a062d31c6f453e98003b72731df8fc6dfd486d7f8a274bc81c9312c9bc7535929bf2e1cf0393a070614b084041d6ee023476a2fc9d6c9daa2060d2fc7818e36707f91acafd1c1064e86e957478fb8759f321c5b34170b500b41c71a98a4be27bae49b24c9c3d105121374a881114129b5f7f165e8e1504d037b564a6bccd8d2e88206177a1d68488396ee0b96de82fab7a8d1373acec0e6d08b22fadaa2a49c083accc08d489efe66517bd7520626e4d86ae55a0719b8cd2445a89cd3757e50171d63e052545efe9e2a2195e1353a078d43cc8c02586881c5d540000e2f6c1031948e3074808133d91525c690cd50fb15d0c841a346e3f8428717f832916376b7bc943a9ca166aca30b6cb4521a33a7d48d1c355e031d5ce0237ed0186b2538b8682542c716d8dab390a259cec8a1430b8c1e8da4de3b687578cc1b3ab2c078ea04757e771742270e1d5860cfc623e47c5d9a257b05de9492546b95b4022735f9560832749808abc005dd922d5e2519b386b0830aec6545d19c9a24a3630aacc9b413ef75a5d1210526c98f496f43d2083aa2c004d7b088d9de0094a2030a4c12fd1b37f88aa85e56d1f1045693c6ec563a66bef99f7138fa238001484587139898f7fc3de71b061d4d6084122b9db6d184959409eca5bca62229751a417574e858021f49e9d225af8466dd4a60ef6d47ae7ec7ca61cda123096c72ddd51c27ae8767ddd0810446e692d9229be408bca8a89164cc27aedfe93002a7ab94ceca54f1be4a277414a12444777e9269d42e741081cd3599b54bcc835e5cb5d031046e945a55ca8fe91002ff2926d51bb314042ea987f6783a760081cf1bcb7692c6cc50cbc145cf280ee8f841bd315fca98ef93d1e1033ec40bf6b9ccb30719dd8e1e706d61fbb1d2c94a1fcd5015dcc800fa13986d1f3a78c0c40f9a3a4e48aa92a677c0e6fee6fddbbaab8f1b0a74e8809da4facc47689fd0910356cb4b2da7ef38e07a271d248d1b55b5c504276bff4ca4dfcd30cf25f8e89f7f82ca31f1532ac1c312ec86a462d6e89f2dadaf04a7bd322d683135d1130f4ab01fa369ec4e3998e6dc63129cdab9c5acae784a0425c1e80f5983f25f772d4f24b824d2a89c2369cc4d8b0724d84aa2d33a7ad48e27f3083e5975bb4b30ed1ee3e108c6b63a6f49c849768b1e8de0bd640771031e8c6064d2cc25d46f8e581b2ec16311bc054f7af2ae76e8c420c143115cce90ac3377da23118c8da87927514ded8d0c1c34520d1e88e02d674c2ffa53624afa21503184257da9744330c27267f4109ac733a8106c4c7522a6e5eb1fbaa2051687030d1e84e08249887b0cc23e4e109ce6681bb5bbfa41571f1fe71108de32247954bb50008b0c4480143a780082111234bfe5d0586aa63f309eead249bea8fbd94cfbc2c30fdc590e513ba8cbba5ef2e803abc1f3757eff502572b0d0020b2db4c04206161988001638f00e1e7c60548850497bb567919ac71ece430f9c648b9d37640b722b96818f3878e48149afd97edb924b2aa50d37eb8107d6555b83bcff7a0d252b78dc814ded17ef3426ffce423cecc0c9905822e69ae9118b471dfc14f3281db8d1c14c254b316b935ba8e031073e479f0a296e96db4353f09003a336b1934ae9b794ecc4e0110746961a9136c848d9173ce0c085b67526cfa49fd5be81511ada5b540af934c7ddc0e5607e96368fc2e0d10636bfc71c53c22ef3ea6c6027bb564c96e51af8ec2649c83c42ef22ab0626c41c3da5a73b0daca84b228be8100d8c1c95beea274c35299d81df58d2c4e4bf66a86b0656849e18428d5d064637a554efd720522764e08375a8d1513e39235990e6497a7e8888811b2d328994933ce35a6160b2be85203f4efc48193e7880812b3795f46d66bfc0296d37bfda4d7a810b42a9348d6dd993a7bbc07bb2dc9f3a5525752617d88fa0df444ac12d709123071121e6a1053e24bf28b1d4b64716d85342928ecc7c9bedd2010f2c705a62c81a32dc73ceeb7ae07105f4b0827de0f0a8029b3d317267cf1732a95081519274fcbceb4353e052e56814930d571d7848811dbda72bf306652a6467a84581cb25fbf4998ece50bb61c333071e50e02b8886ac09b94bba44468ec7416ce0f1043e6528f57d9541c1d790a1821b39d6c3098c7e37ffb24a1fed5a2fbcf0680297a2448d22d4c804937550714c434e09ca6309dc68ff98fc3cb8d808b51738f6ca29811b8f26bdbfd45b8f2470a762be7162a8a890c089b45f2945d00cad00175c7cb100228035781c81b15c1325d4c8e47fb211030f237011cdf27e2559394278148114f6294fec7bed8307118ac710180b39449f1ad51fa2cf50b33d7808815151a33e9e2c48cdd7305af0080297822ee91f96f2aea76620bc820710d8dcd1e2eaaf9a213278fc809f5c6bada2d4ff69e5cdbce0e1032e05393aa8bdd2dba9341a78f4a0a4a9f26eaa10d7b2e1c51c60c3c6051af0396c4cc0480b3c78c06d7e289ded3ee8910a6efc1726d8019ffde44b010f1db0975c336bdacf0cad0de4f84202095040113c7270fe2416bfa7712303a50c1e38f0b8019fd75c2505fdd9d3b891812d6ad471c0c3068c4e9a949f858a6b5a8ec1a3065c3e15738d2ed18013f2d57c5329d3b14e3360c42c89e9c9b80cf8ae5c71ef1403b64388dcef9fe301032ee8dfbb778d1944f30a3ec8d115cdcdaabeb42bf833a52b77d09be8752b588f413368cc2a757559c1454cda4f97a7362dcd2a18a5c1ae7b4d8614fb54c1df687c370b9223a4cca4824b3923e635a147056b6aa2272ba5d036f2533039fba78b505a75948829784dd394e954599482499bcd82fafb574a5716a4e04c88322f1962598c82edbfbebaad4cdf13e94216a2e03ba48c5a9597d2a7e80c352c32c0001570e1851708d0228b503031e26687f2b614d40b8acbe213ece97351697592852758911cfbe39b8d597482d3dabfd2a1ab196ac6b86864230239687471bec08021260b4ed8c767b189928526d83c3542e5f38b1799e0ccd56285a0732b6414095960821325a3249d648914c4bf047f29a6e9fd384154095982abb49d56b364703d5125b853df7ae7d143092e255ad2ce6954ad69127c923812a40789f99346126c342552ec6c151e3a18096e94ea2b214ba8df0a063e3e929005241879d5766d133596527a041f3c66fadfa83b82adbecf9b2b6ab4ba682378addfecd943c8083678bae6c94155f7733dc862119ce95e488c557aeda3b640c842119caba6b8659aa9de671689e062059142c8374204173a2d8efc2041da0907591c828f31a81f2d0b9e39655918828f2cee63a22b276451083ea6fb3db5b6e2f59d1d6441084ec791a3be17f4887e06c17f783ead1dec2ae7b420f8133d4ae40e03c1b665d71cf504083eb2a4ef8d21e40f7c6a8d9bfdaa2961fb814f214acebf51d3074ea8da3619eae403bb39418268d1d018d53db0ebd135699e187244d50337da35a87fa7f2c0afc59c92744d15fc4a3c70223953e6ccea1d38bf8979b1d52d8687d8818b68e98214617a745f7560d2989b0e62fa54ec181dd8cd911cd3dee84a159f03a345258b869047e41191039f4b97d60f9df62e46e3c0b9bd4409b936660e221c3877b1db31fb94f4856fe0ab25a9f2ce24a309ddc09b12952b63b6b4814d5dd3da3c11797db2817fdf0ccfa742966c296b606cfcefccfd44ed666ae0336bfc3b66acd377a5810b1a7c4f660591fa296860b255a764253c67e0c2d28d0e4a86181e333398bdb2e5cd29a7cac0fb95069535e7d5acc9c048fab4b2da8f2948d1185851a5aa52db79d4498981edbc6e4ac4f5a85e7e1818a1d2ed8e5e1218184f995975b6b1e2565f604349caf8d79d17388b5ba1abaca26675bbc05fa4a8212f8438d9e6029b64cea4a386bc31f9640bece954972c3b2bfdc5b4c0a989ed172f8ab2c08feeb4d5633258b0d47b97e81ccb2b7069ffd4760ca65d5964053e8559f43b0fdd16275560526e2c11a26a52c1332a70d1d4c812cd77156e4d81cbeffda63ec4e4214129f09e7724e5540ffa641b054ec490175963060536756bb0f4a03d42dc9ec07f9b16f3981944ee8a1318112c757d1affbc2b9bc027a5b5a73675d097264c6054b4b8c1829744c862098c4e5d77427dc5981f2a810b3277f8951615721293c0295379436789042e636a9290829649de8ed0a7797fd0258446e0fedc6f7784570476f2a7f1dfce664104f6644d0c212236044eeb2ee94eeb29c8241502df7fa24f9d8b41e076b46809aa9a3f4a08087c081e44e9b5bc9f46e5079c5dd66c1174c5075c6eff88d1ce2b68ee7ac0499d74def6271e146da49d8a1be90d42163be0a38b2ce1adf5415da403d6dcd442a4bbeca0cf01a72b688da1732931ff70c0a451a5c24567734bea1b7031ac2a36604765e7d1a4a5db12520336a827979c93d6a0ae440326e8fcbcbad90cacf3d55306ecb678e494b26a8a746370a65789640183f228695a43902e1160bc82491bcf6ddb5a57302adb77d09474becd7a2bf8b81b22478998b29789155c085e5ac9eaf6620757c1962a399a46e3b6f64515ac27cdb8b529950618e7da9862781aab2c01062a3e8d96a6e4baade70b0dc629182d9e419d902963020c53a067659372670318a5783dbe4ed28d1b19c020c5173046615e1de9363a248ba3230a466fe658ede7059ad20fbced26bd2562f46cb5f68171dbcb9236c94a793a3762f081f1e0d13cfd5731f6c0c874b959fd6b23683b861e1871ff3f1b751e182dd5982aa21d030fbc5866ed33cb1d55217760fd3f7b4a263ae5cdc9f2871876e0c736c5544f32778851075eb35dd20ff51322871874e023a848132165cc1b392ac811831873e0cf437d881e2647f215430e6c89c6f51c82c76c17ba03b0d0020b57438c38741e295792fa396f04074e47daa41dd29dbe5bd4a031230337be02e60131dec085d0e013d43e62f0387203a736dfe9aabc19a6325288d106f6fb32d4bbf28927bd9d0762b08115a529f77eaecbf7118df3f15146d660d6939836e60dcf7c0c35b0213737b64b34461af814747ad99d090d4711a17d6f153a03d776aa7b9a3603af92a2ea5fb0711176195853923e5eb995b6cc92818f6ea5796d9245cd8e814939c493d54bb25219313051339d0433d3ac4cc3c069d0fc135576ba2624a99a4e5d582e7d818f565aa76df5b4c70b7ccc1caae4bad705fe826fe594abd4940a165a181738cf987b6409a5a7712303ce058d195a6c81cff590a46ece1484ccec10430beceb552c2dd1f2a6aa64814df36cfb1d3f5cab9a430c2c30713f77d29439076dcf6b5c1a625c81533d55b20223b2ac4cc67c568111398d1252e3d9ed5ea8c08925532e2276aacf488c2970498988a6b4ab75fe1e430a6c3ecdf9a3dc14053ea6cffe74490f054e0409a92d64718feb13d84cadfa1da926a60a72029f2505734fb5d9c2bf78c013623481ffdfaadc986f26f029632affac7a09dcc8b3ca66572e9a2b814f9b3269245d31d9a649e072d9668f562281ddadbfce51c34dad6ee304318ec09b16a129f6a7dee6d1089ccaa0eba77c2c021b4964a5f04ea9ed2644603fc5d1cb36c9cbed2130d23d7d345a88221702af315ae7a867a33925088c1ead6934440e086cf88d98bc4f776143025868515b88f1033e48127a82d0cc07bc58dedb8f145b375226f0372af0f1914288d183f4c9b397b895e2011f2c95aa60254a5de5ec80357713f6f11443074cce98f7925fe65ab95fc4c801a772a8b8a758282b79e2888103b6eadad66bec1bb0752a447d1c49f921b2019f9ff2667f91196356ba8851831834e04314d7d851bc553cad8287c0f220c60c3891556e9e27e5516d8921037e448a98378ba608316250aa684c99346968ac982d9ed29d36f1ce093160c0af78b068f133ef525005e1e315dc886c42f44d743f5cc188106a74793a55de7b01400c3e5ac17d86b0e89e342f7f7c86da711938ba0c070226f001192df860059342d6dffdbeb3d575869a8d1c3256051fabe0f2b388ccb1b7ae4a7ea882cfc9b2e4cad2c9083c8d1b19c8f1910a6e2348ec3fbddfe7251736643c6a2ebcf81b6a15f840c5974325d50b2529d53b828f5370a74fbcd63d86469a22993ff65fc8d87e2958cd8841097d13ef5472c607290e4a464cc26a1b05935d2d28efcc19354351f02af1cfc52a49edef43c18e4e51d368f1d44de003149ce8788c5aad2239e5355060033fc157e63feb20b38968d4134cc89e92d648bf152deb1f9d60ddedd63f7ec78d14bb61434669c00727d894b41bc32c4436c1a6cf2239ed4fdedb574003c78df7fe19664fe3468efdd004a7a96fda3666b7f493b274e304047d6482531f8f24427275a6bec60726eee312f76109ceb6836eaa78df47ca161f95e0425d54e710733e28611f1f9360f25dbe25c18564d92da4b07c44821141c40eb5f0aa9c7c483039a8cc7831a6caef9a47b0ffa1f4a53121e4e8c88723f8acedef4e4b295e9f1f8d6094dea64b5ea54262ce08aed453d729a14570294212fd1666213fad085e3b75b609a272326d268291f984fada8eaea7ae2e3e10f1e8e834eddf4f2d67a8f1e0e310f96108fbf828c47d90027c1082138b987473ed6efc67e0e80668a1a9878f41dc8720d8c993c36d93e746f70b4302c1754c79dc3a4bc5dcfd0cd4a800c108ed587944e211287f607d54ca19eb5e645ecc0de1c30f7cde8c61dfdb9a472db0305f08e1a30f7c78b4f51cdcde02c2071fb84a1b2db25799f6f7b1072eb7a5113bbbae4cd203232fd9e42d553ff2d0a94d6b9d94c3942cf8c043e6a2635ab5ec5d39173466201939ce8cad0c3eeec05f7aac8bdea66bb7f9b003174a7fd00fcfeca8918f3a900f3ab0e96d358b6db238e0630efc451c6d1692c849ab490e4cccd44b5df9f2e4fb7160f7357f509e32ae780e072e079dd7827abb12b1f3063e44b44aa3ce76036341432346104a3f881f6dd8eb47d297c89a0dfc5a59ecbdcdda4fd7c06e0e59e21d94ce2162d4c09889ca7d9d21c6a453d2c0e5d3d3c152eaed6c221f68e04d44a7f1d59062eeefe30cac9578c531e96ba9ee871998643157b2f4dfbf95b278f8280377fe7ecaba554306cefd5b44a8d14abb6e1e3ec6c04634d116bf2409c9ee8718ee230c9caaa77b9f76570faf0c1f60e0d6db2db544f32b8b99a196650c1f5fe0bf82482269fa68eddf0b8cca953a889919281cf8e8021f5488a6db3ca4b1b5b9c0ebb59dcec9cd45a7b7c66fb15be0728896a8d7e4d458da628b1a346e74d1c50c1cdd801b14e81cad800b4840021f1f0ed042cdf0a105de4663fa8de469649eb2c0c9107a4aa9a46a9b7d0d7c60814b96c4b643c4ab0cf12fbc2816f8b802e7191683596e52e275bf173270ccb821638b3e12b88004245077a38b2e08083eacc05a8d47f6097a0d6c4123011f1f55e0b26d5d6b0ae9196a6a0af8a0021745e737492928b724f442468e96e17ffd3105266596887834f5e11f117c48810f9d981fea2f2283771df8880297b6c62707f32084d75ed028010e2870bfa68305311de266446f031f4fe083d094924ba8baf0e104fe449d44b7dffd949c42858f26b03d2684d2547577daf2f1714ce07d84f707e176697661a1c5165d01287c2c814d49aa7b1c9dfd42dbba527c28816b3b377d37ad9cd9fbc34712181952ec2c4b3b12788f394809c9424d33e508aca6bc6d97fdb72ee83e8cc0e4e039849cd3fef111868f223036e963e5242302a3fef69b23cfea2a3504fe62e6e44983f6a67a84c06877644ff9930892a206f808026fd61793709328d16a20b0192b4a767111fe1efe80bbecbc69cf7248ce8b0ff858f957e97475f61cf9e801579f5310bb3f3f78c0ebc66c1dafedd2d466076cccf422bb47e53acdb9f184001f3ae0f2b42625f3f76be8d4ecff460ef391033e5b9ab2945b775bf2880f1cf0218e4e232c88d0bad7f88f8f198df8b801632a12a396cab944e86cc067eb7ad296314bf8a801977489dc2193a7840f1a70d2b2b7a46cc1779238034647f18b1d2b5f2c35a166e14306fce7cd412dbd660bdaa3153e62c05eed450a514d9486e403067c502551e3e71399eaea2bd85abdd25dd7d178a62bf8edbb9c837a116999d40a3679901f42ff4859c18a5fa54dae238f557011ebf3c673f31b1df75005a33ce845fe2474b3728f54f03925499d924490a9f44005a7e377ea177d4966ed789ce2d7ca2b1d3c75537095e22f724f8594829b542234837f1ea4e064a888d51b9a3478e9310afef2557d8e4a4a2404a51c3c44c1f649dc102704dd9da4013c42c1da59a6b4918182af6cd9bf43dacf7ea62e920f3c3ec1ada6ca1c4fe566280e1aebe10956c3555376cbcf5093f15c6c5183060e3bcc4e7012927a50abfd195addb091fe5c7a7082979083d98f593c36c188d64692d9a3f79a796882ad4d42eb6fab1e99e00a6de02fb54549b24dd944367067d965c13a9b8ea4d7c0eb28937ffd5103274544919d73d2c0fbe67e4caf9793caa281bb929bd28472cf6077eaaf8f9c3403e725b25e4cedd9524a193893125448882275bd25031b4c9baf08d5a4447e0c9c26bdca1e1e42c42031f01e3dbdbe25ab6e320c5c5a25a12c9d704d2a6060b3e5cb4df22c5e90982fb066a1a26ffe158fbc17b8ffdc39b4e25de093d2b14b3aa95ce0458f12692dbb057675bcd592d4f6204b2df07d622aa4ff0e4296b2c0b64889a13f72988f05ce7545055b8b7832bf029ff22819bca268057e4d6d5805ce3b5fcc937f24d353810b5a4aa34af44e1e4e81cfc9432d058d4881cfba5b25c213056e437de648f1640a6928b05622a9cfd39757629ec0d77fac1c437950a17402a3dca2c8bf8871f3ba26b04169eeca9373ad746302a3925049f28dc5d2a725b0c134c98aa5be415b94c06b95cc244a4f50515249606da3664f1d75bd26a98c21814d5164c86a4ae3023b02a3929297458fe837a1590b981118dd164466341d56b932432d052fc3460e19e450604560eb6a42bcacbad1412442e54127f14cf22b36047e52fcec907933c8b00b13025ba222e513a1d74b75160456f376d09245e6d8498de34fd0cf858cb51b5d74610c087cb2cc5cc1feb27e8af6032e454a2993e9c8ff79643ee04a64fda63508d6032e4a52117c7d7d720cf29f81a367d4f8c214c078c0c5bcc1eb548d4db2640008b6034e8d7ee7cbead94c9e706819311d70f147a4ff5ea82fe97f8cc172c0fad5464f614a7ba3f803c30177e2aa1641d6a6c9abb9011bc9effb3a8ece1738f4b46881d980cbe8ee41890e762542ac066c90f194a854a63289241a703a5a864ea33a88f47d067cb95a8da58f4c06aca6ed1cbff3551411b2187023eab3ede73da56390c1800f9542aee01d744b087a059f26c5bb64ea96a26d57b0c9dbd279a78f92476d05a72668d34a23f1344559c1c59446927e4d57c1a98c4984eb6bf01b53156c27992cab68c4ea3015fc660ab2b4e7f0182da8e0d4e82f997b84679a9453305e793c4fe68a2918ad2a167f83f5c57829d80c8b77c2d3a4e03bed22d9c4dc28f8507293df474e147cfacc9ec15348b9da8582db546ec17dffd4f8060a76c783daa02b745f8e7d82d1acea929444a7f2cc136cceb95b3db34eb0d193166159276dba8c138cd98498c1838e96a34d70ea7ad36f0c1d71d4046b72344347ff18b393092e33b7f6065dff2a263851da36b3b897e06292273ce369094efc82f03eb9ef5aa9aa04577eeadae289128c3c59d9828f3f09c6d46531732b61597949b09ed5f6f4867c2458ad9c2f49cc6022760e2478b55117a2d4a3a8d123d84bbd6572e3b66ded08fe728510240769ab6937829568a2e27e2a89d0352378d19c2396c8f5223849ebd529563a21f38ae02ee42444ce8e3f4a2611bc557a45d66f11c167c88b549d630ec1aff7a8e58d3104ff41734a4246bcb68829049362acd6542683d94e08d64b071f112f340836a75349c84a9973982a087e2cf9291f990c0427d3d4ba9bce2f6e4940b06b1e4dc9ed9cb327e11ff8502273a618f5031febfb5db47d6512f6814b13da84ecf841c60bf2814bd5392cde7d6889ee81b51ffd99fbdbd7cda20746d276ca499ace08ca0393728e133da64aaa170f6c32fde423c5e44dcc1db8ab8dd8a1985925a97fac0e9c103a84eea9081df89c2832ccfc9c0317e615122d24f7b44f0eec47db8b5945895d7d71e06405215553e45788393870fff9ce552bac3b726f6044d2881cd1cb972073039f2d2f2ce96056d5b136f09594f24af3d0a65e63031f34677f901735736a6be0cc3ee7de6cff2f2aa6064ee5cffb66973430669dfb476a16919da181bb78a29a4967dd339d81dbf5bcb142277a7ecdc0a78da64c79f805a12c039b9288a7f32559ca181918ada13dc772cda41e039f4b83d010ac8a408981f53aefad98dd61411206564ffb586d9a860c333060d93ab7721057eb0b7ce7bf983faa7deeec9417ec6a5329e9bb3429e6868d2f70fc16349c7c810516e7bea8a102cfd15c2ce0e3a3c6e7c8d15ca401e4a0bac085e705cd2d3e17d88f549774c9d316b8e4a93e9a52500b9cbc4c72372fe8aaf72a0bec66cd20c5f2080becc79049e97a6ea6b194e1b8ba0213ff449bd4dd98f309dda0f18509509f1dbea0acc0aee6a49fc1b7d2e972068e96615505de46aadb57b0edc8a6196a26e8c2f7074505467b94fc18ea428d8360a1a6c06ac618836a8d90c43bfff8f0a20b274d2829307a54123a2789b95f1927a7a2c0a7a89e2334ae95861414b8689ec9830c494ad07c3e8153a133a994d652ae4ed190423981bbb16e3725721b866a02df1faae9a344103dea7aa198c00815217bddb2876fba0436e77b87f0ec2b81f75c9d3b8a3651229b60a824b09a4ee7e0393a3fdb47978aa64c17ea08fcc8ef4ffdd1ebfaff4e52bda8af3b5045e0345ea7564b9f82a520192ac851e30b1ba7ce0645042e2b4c2deccca4b95b831a02a3259a11026b6b7a83901e83d4cf82c0de08edfd10433d5d12088cec3b6da2bb82a89ffc802dc97fa1cddbf201e7f613c392b69c2ee6aa07fc5b9a3461728347ce140f58d39f2d28cd5bed80b153a52383e6be789d29940ed824d4d59526297ea3ad00142a077ccaf72041cf53881ee1808961d6e92e4fccbb3937e0929ea6c7a4531bf076a153b4e4f89fdc523560cf2e6d70f7fca1538606bc6b55e9b7cc50392c35034673947eb588556b6c092503d68277b57e4c79e15dc580bf78ff1153a9c70f220503467b90173f7743428c57b0a7c722f5f9565cc1c6896fc5680577625ba742ba6c5e3b2b182dee41e69034b9dac40a3156c1d7298d41afa555c1061db93b87e8c64805eb5669bcac8448f1c2196a35fab940c656100315aca49d5c417cbb0dc4380523f5bcd5b34677b77e3310c314ac04f9c1b4887c3632a5314ac1ab558f4a4d69f37db6f02f6ac8c004109b816af48d931588418a749a9b3ca19121c628781d212c5bcf62c8498982db58e9f2f8fdae6f2a003488110ab63e930e39e4d441e731063140c15b4b88fbd7b7a622c62738b14e9b513f674dd28385165a6091a788e10936875876907fa226d73968e0b0e14572214627385552f407b527d535b2450d1a4e03bd0238c1e6d851b209567363ed6bf4383aed4d88a109f6849cf4e5ed255aa24c88910936e61092334f98e78b09b64bcf2269042fc155d0df14b4ff6f75c812fca7894907954da413a54a7021735ab41083127c5516bbf1ec0ea6e49360ebc2124312acad5884b41ab4238811892bf4bd7bd0ff6240826f11b55a3313a9427dfe860c1b19e3116c7bb074227e899c42770427d3eb24e5a153428c46f0954c475e8e1416623082fbda24bfc23f8bb84c6fd26b31b50145f07fed9233de5a52a5c1438c44b0318629d1bf3b1a2597818f0f44f069325488db9ffbba5e438c437035f135a57acfd93215c3108cd0bb71f5b62aefe8310ac19e86887e3a4d57be530c42f09e29d967fb28d1ec1883609279444d22699c148f21082e558a559eb6744a9581606fb42555ab2240b0e9d4574712a910e30fbcedae5a0a295d2a8e02a82411a52269281609045220083108cebc07e312000000081a140663a16830220ef54d0f140003582a22523a341a20261a108dc681913010088481e14018140885028150208e84711ce9440f0fa10a8ea20c32aec88a0b5d2ba0a6bff5d4f98a86ab8caa9e0fee014a4d2a8afbd1cb5951a85a684a41eb4ab956e6aa12d5e2a65eb8cad082940eb4d9cae8f4806f163f0a0b75cf6cafba74a5a6a932e596e9ab0c101c9d6122534f85bf6e9bfc683e74366dc11556b33268fbe9abe24fa92189403415b122ca3531d789a80947257aaac8abc5ab208a324c78d6f6b63b18b1cbf571a932357ff991cbe2ea19f8de99ac95f068d7870ff96ec2687d75c15558bcc2df274eaefadf9f99a46046638ce89c13294cf618c94de26a0cc78cc19c1d627216c69a27b1b3cd8069f9f81df640ec70f3d353cf74630bbab474ce5dc965522dc2883a6bea8cdd6bf0146e6f76b00f0ff5ab02980bdc33af510919f809e221f8d6ad32158169a98bf94a43a7b9c5fbb5c7042830abda4478dd076243cfa908de9f6e5bdc41af203c346f6fa5c18c2080b01de1a58042c9b7d63a78dc890bec16729404f9e6b41ac4d97d3ca4080bff2027e52211d4590f542eec0728170dfc4cffed1e28085f60dc6c5904230dc642c25bb8d891b396d66c33114ffefe925c3dd6cfa672c533bda43734b46f8ef37033fb90501a802eb4a47d83d1eb2b060ef753f91d7b67bd2186245956c7a4806bb9d979058cb26e6984f50319cee2277c2b2b42e5dd99268974d7ecdaf0877703ad28f0f87b328a23384ed34052a0ed8f64e309adeb6bd88f05fa4156075bbd6fdf8e876f6e3c35b0b785ba5238293b93db3da70ea16030e9f2fce9c0e86be878bd9d067093e1c5874c841fe217a2bf18fc869a09fd3bf69e8f1e094baf38e070863ad2dc52e0adb0dc249846c0bb7e9806eeccd9cb50e22fbf098e5ecc0bfc06ecfd5855187e0288055e0a6cc4c8db6d8538e58c38a89482aa7befc630151b1d6bd1522745a330f1db8283c75b11bb2edc7c927e93c61bb4be0a1f49b2d1f8c6b05ff0667b085e5e20f2bbb4080b614c930d30940208de0b82b93a662292a495688f437ad1f7dfbd8435ed22cf766f8a5ba9c5cfa29a847ec0d19db7eb392127a15127fe6797494b468a8a3b8b32ae458c203bea0bc18db6464591ac26e9c82289af5438138bdb4132c1b99de295e0c7a76e1c5dab341927a571cdc34585a016688540b6d6863ea4a44325e0bf19c11308b807df059a4b31d266ab314aec2ec0d7c471bf1c3b533278c0442bfe1eb3706f7a498d5e830655c115bb34f2bfb2636c4f7ab6cd793c5ac391d03082f4d282d03c6af13640045afbee18aaa3a54153df4e821aa093a1e9a5ad21260024a29e202436819d2179a25abdaf0d324e161ddaae1c794d0ce615f87c155b0732e01577f04cf1dc227776609399d3355db75a4fb1f4827b3f405380b7f108a4697dd290c406f8860f54b66c9ee74ef878ae283ab65b5649aec84129ffb7015016122a7702be0f222e30f70ddb900b31564bf4cf459bb563c1804b1b39a42a92ac3683134eed5cd208e33418d2e4e72d78f09122e7845378dbe663b82aca645b21733f60ac1b3a5c579b66da55e81234992f3805ae7db88dc141c3767544342cc96c21a032cca4a1dc4ba5a3088c685e023ca0d679041292c63e3b56b9eec183bcb7dd5ff6b2588e81367db76597e1f843c54f245ad5499fc0f148250835cc71c2aef3ba33d499b200a45b28d17dc2c7f889aa236b33ad65b3147aa24cdf691d592351e9491e2da32a1a7df0251eff862de354a5f6229590206eda64006a576dc09a49e73c48ae8e46bcc81b813462d45e81cb8e4c2b0c456194f189dcf92e3e1055e915cbc82a856b72b3324e54f9f8dc1e3d61e5b0b31f513f4ddb754ff7cca18da762263862e2491a6393ba7b0969e0c5d2f63d514ce470f96ad9669a0c0945830b66d5427b3aad663efee4b4f6b7182b397f71080db6d22c5d19bef9dcf33e52410c51aaad0b330fe5a0a42049c15b9e3de8580d663087455a6b6f3037c7a88a82a728961560ecc335c094999f294c221b1a86372d69aafb9b1a06067f5d8c8bce0d6370f676584ed388810f21abd67cd22c3153039669940cc0a48b201a25a932cccb747414176ff587089c296c910d035753e62a5b762689a7cf23dd7c5946eb69cba7a091f7722a853c5bfd58a51a1229174f0ff5f6f064ec446f53fe631ce9323b1f038003d0f52aaefdff4725294d6ba870c4f1b827edc8dc8e1c3604aa45dc78d10d545a664346866b004f6644d56c6a7e381391a8aa95a2a519960d01ef110a6401b6df484d62843c4a7f7a29e3a07a37befa16a529b3b059f7569d71b134f01b135f18ebe220b65f2bd964e42af70f56de0dd792b43fd79b03b3a6dbd863503ad392e3e5deea6e8e820ee4a3cdb4842140c8b54cc0927073aa513614d83d95d759f3739c176e4a9a015896994078bcee486388d3f051fab043803d8cc3b54f689f37c1f714b95fe81e4ee4410e59a22480ef5f68f148012ef979c92b171ef8db54768bc11e82325673cd14c4933681f44dd29a8ca0e89f398599acc8306fa6bee5b461933293332a5883d3a3b417b263107b98b7f28f8e2d8bf85338eb53e8b1a588063be447ad2da264506b30337ec6ab123446ae0f87ba9ed40c7e70e33a59180d7558815a424af1557401c022899fbe46993c18587526feba74e6fee652b157cc968e9290b460424dd6e0c3b5026a2cb6848f2a84e0c742e81ae167e8282a44a3049289d523cb03ef0b60d08ee8b8640feec235c8e25237c2ea6cde6e46eae1508bb622532a1fa0d9be91b6f20ed4254af002d3663dc597b23e6f8c3c14afc78e51335edc62daf7ba6ab129996e5ea6447e43ade9849118644eb0419a3d4ad824f9193a05eba2b9485bca778bb2e92c4868460fa359daa3b327420d95a00a633a14220d570a53db2b025014527125d6c3a08b6873048b607866ac3c85c56bb2fb3f5e7901f9e3ad2490b78c272d63943793384d57f5fb3be41ea6260ba57e9722296972ae36ae620186ea4bfd93ce4382531e42aab3b67f56fc54412cdfc270812487ccaf6c4fc3c9914866abe65e110d6e0b33c3597e6d3879024685a5acca745edb9486571acf07f230a8bda9d2e9f2923060bd9672ef6c4dcce0f26e255853cfa681e3dd9237262c970689576367cd6ff7956b9df1f2b0521c6e563affc96aa4ee44c706432bead59332c0591da0114807d7e32f5633fbb12b54943c782c603f43a24b3bd1aa538644745d98ad4e2c467dfbcf57093791dba9f75047a353c26bdecc429ee568e720d66d61e874c3eb569b2c10ce2526dd9ed89f1a63344edd259f602855d79e2721436021653111dbde067e8c39dfaa37f3a2b4e71e547de5cc4b81fa105ba79a7cc97cf8d230f753799762df14f65b34de75b2d56addfec6a2eaebbb7960a478fc619417e2f2dea3fccada923b74b06dfdbc4b4465b60800af43dded429244e26d4a4f50aefd34681b5603d8eaefd02a339326c2215eec8091366be77583de18f9875afb68481cd82f74ff674939095b344ca2d4084fa91bd82ca5e0b2142f4a9bb62694753c3adef93ff27b2389b79bcd9ddc36264466f6bfa636c76dfb6a3864ea103d6094377dc08224d6081ae609a9875890cd5c9de45d6684f30b39d9dd2fefdc2d4f7ee29a8adae69a658a6884314a1881d38f2d49d76461f359506f43e068483c358433820c7993b8e6bb148c45e8a904cb14b8440007a1435829ac8724190a68c2c0293ab2e0abfe04ceb9043b10f2561bd6831c074f16c90684c96a0e92ed7f68a0cd3245150921a632eb4b8bdb06842d0ca0d62ede1d0293053b94462353c66cbf26b17b016d9c0fc8e2fba5d22b3cf0c258e4f9fb14b0f355168b4604fc68035f3cd1fe4e875ef48ebd5f712a9baa179908e440c7a77f80b604775670e85947d8b0fe8e11047a81c56c109a500383331cf99bc7dce9c7a389fc875e4476f19de171e4936df0e54c8eeaec26ff38b2c72ebdba214875b9efb95cb0800ab702fa072bb4186be9aa88199ce1480f91621ffb88fb3aa9c68c8775c159b1a27b883c6c7410c2733b98a3c7166b17cefb3506651c1484ac944b9649766c350a5fee61a85cf4901ff160c367989363405388da425cf7996f6f8182204ca89fa34b119aff20427e525c33511911c72e4e4d162b03d6f8e7653426855dc06c1870c94823d80d2278c62e4b1f715a5e4a3f60089409a47c78883af64218056f6539a12c7bd615e8ab19b4a4564c25b459e5c3ce920f49d4d368ce7573e48aeb44087ae690db4420a12976802859eba813b88ad0234759b8e3c8564bb2036f21b5cc120ece61ecdb80589b8da150df63d23aaaa90e25d9df002e61dd87cf13b004dc3fbf740978eb6efb349037d4ecdb2694c410da386cc7bad5937a067232da02230a9a885824e4109405e92ff51e81a055b56b90e9083d11f97f6dda63013f90a664f2b3ab0e47c480e388c0e925d8ff92cc378ed08bc208bd800dd05dbc30bf7304db282c8cf3e6d5c610766693af0ad2471d20dc31457bcdeb23507c9b6d1d9d86f902d7101768a913db9d07cdbf9e44ea9ff00e5b771a9d78b469eb5d165c76e60fbe4649831529f4b82b8e95aa92522d00cf740405eec623a244ca2d5bb82af2dd5f515f349eaa4a58cd21484643869f86515052936b4bb9665b731205766427b137031bce3cdf32ac9a78b7d87981143fe01a1870f3fd1da6b2056877763e5bae6609fb1dc4282bc0478f2244b5ade95bbc0cdde018cf25df80725155358bc5d0488bd5c1066cf2c0589b3242556bddde9dd091ea44405f55c7e1722a42ba669e4aa740c49fceb6a67560a28ed2d71f7f147144bc41da845d1aa2f35f97215980175502ced6f760059a8c00cac02e8a877f72524629b8a3548338eab82a806e0d688da288faa3200c10ccb06f91b2ea53c5a14555abb8e882b33586420ece9152952ab8c8f50081a01b18ff41ff6f041e2f93d465d5b3a5cb1d9fb32f6230012547253bc51ccf7f6bc3b568d95c3b175cdc37eccd929b77f7b68624dd567e9ec8d5c7ef5ba30ca88c01bce8f8a46341e9381636091e51eeb64e86d6e38d98b9a47630c5bac748a4abff61297970e9eda38b0f9f9926bf9c0e8780e7efd6eb0b4449fa05a6dd689ce662a9f0182239a66b04ef146fb1eefeae080348f6fdf82069596688642e3787af907f544749cd22f09806d13df7ada56129668512921de147b053f731d1f7125afd0d6032a4562dd06bc98fb3971127693167936533082ffa3478c85fd8182dc23a8270ec18bbaa336ae500d5477ad23efb0a204dd3e96f3ad6351d91f79d8a23d4fed33113d1354dd0c466d84de9549bb8cea9f622c25281a77910b9414cbb34d6d600590235779e6d675fa2c3e8bf1516fa95dfdc99cc44a778f5cb0de8abc8d38310d8baea7489094b7f9151694c93d305b536da6f1432edae98f0eb55b9743a5c3a60606fb12e07aca1310db5c86803b4ec66b7dcf052353f37426b36c65bb31ebf191ec67938931a23c854228d882025dc4289f8606553d14e5b141f1ad9a903a42b183e3d2f3218ca0ca81d2fded7dac0692fa937a306ed4222afa4f31b5e85f5b5baf11e3c0516b7218e77e17eb9e3ee2479deec2980aa88c90ee2ec57319db4bd05657f876b7060f9ee6875ab854ef95d630021a1c575b51374404f0a0d505637c9863ce1d44944edb9872a03c197cb9583bf2c0e916fc047a04bbcb6dafc35c692781fc500507a7bb5dffd04265c732b60c3f45ed3745cb1238a976995fa9e2814bb7800d17f182bdddf70c440cc735ddcbdc467874fa89e1566c9fd2d70d93362c27eb65d78e68c95d9806e94df87a42679f461edaf65cd8aa1c929f01b668332964f87428ce07a216a29595353c359fa19bbe9163828a2274a9d7cbb646e5d160af0613c6253e34bf67c5baea255a1b1d7823acf730c95692bd280a50c2747394460546e75422ca961a01b270a73445855e43d7bc9c824ccf7b5fb82f698e1cdaa8771ea3013918563aca63b279a32ff82102e088abdc63967728e946f27d0b1748011976c4298012acfeb63a5a152d8a0f8f471520122b6bc853928d7be748b90b86cbe741a7a023e5968b403bf2e268c260eb1bf129aafafac0693e942e799baed33e184acd9339b44caa0662de0bccc90962ce6d2e22402f22cc2180926856d83ade7bd4fe615c93746bb36db5dfb8f52f054934e0cdfc4926d39ec70c0ae30faa84b1c07536c41f6ab338a406b9a6d72fef2c021988c0acfd9340d84ee83e5fa3d4d912500b7658057d103f490b73af20d82283c6a63ad99823c6d2cacb940d3a8ec0add23378a5619ecd1732adaf9b0d892a427377f8e344bdb94a04f3d63f3fcb2c79b175fc9153e333be81f4dab8d7ca6a4d31acc844d8f71b7c190c7f63457a7bedafeed917f146b7fdb922141423d50d5bb83a452aa217d2886d4859bde54df7e1e9b4aed8ae6c144e6263b923bc11b79bb2bad2514175cd854e2c44384fc5af72f8836eafa93dc5f38e7f0172c079a030356cec0d0b5562a49ae405b7a04d6e903199a5efcac17c061fc63f42ccc5551ee557401c6d3e2365bb536b579587e326a595a8bbc752bbab9ea5cd9a4da6d41684c0a2b17a6b32f113ec2c09ce0f471e07890dc3a13b0f982308cdc412808db829ef1d7846161d6bf2bc74803d8bb2acd9cad890d7aaf6599c604936c80bcdd2498cb9bbf81e6a53cae73de2d408382e07e25aa059811eb1ff999ed42276d0d080720053022156af525122fa65f422c589ed53dfb792c510a703c77ce2b84d54e5782ab5869c70d7598da6434aeae21e8560ecc2301d7424129360883193ace0dbf18e353d8424ce5b80d8ca4fc3ff16ef75256d7a0570d93b9ef396e72ffa5cf541b722935b5abaeb7357d60068013245cc2cc81a7571fab7f9155291f2aa00befda8b8981a544079ca0c9e9ba6c98645071884936b9c7ba6dbd22f55090657be6124b761ec12920283e053b99aa2a3cab3d3294c16ec956d421ce3afb8f1225397b8db4488586ee2e97229e32648aeabbb62545100c4c75eda431e89be1074218df67cb2d863437143c342f41b782a68b70e49eb32e48c94381b80a139ba93a9005ab004fedf653c7bd640ef70c0dc66c7b003e0c58a0c6d4a03026cb868399aa21e785b023bf3da9fea9678185dfbbbcb8e4a5ad3d6c0a1f1c94745c52585e3201be8843cdc4c0943e87d690d610f8d32cf3849482190d724c269d12e06b6f678f7c70e9f7499287a4143154ecc0a002390e261feb5e714786711fd73a26c9ce0b51cf972274236bc9aa9494f9ad2650acac3b144ea33c0abed8c5b5a9d0a5481b543d59f350011eb34de0ff7cb2074f11aae5767082ffa3939ffb7a4d20f82aa0d35c532b90c625daf5f3e9f06f70b78dcc6f5353836acd98e49d86a0a796b87019832e313428a139f5cfad74f8f19606590b5189eb2890b025e63230f66c0a4af94bc7e679ec54a71bb09fb0658eb00c0c2c076c251b857005ea36aa5fc3a5d12b621be8df4e279b286c7b4d8bedd940aa2fcd9866af709fe5d744a1ba7672ed6a731f277f5e166468801324d942d3dd9472dd4b8b62160adbfbea45d8a2ff858295766f61a7f6a2014224639b64093b64811ba1fc06bd99699f001ae3c5b6f7dfee929a3168d62ad55acf6fff1373b2ae6dee4adc74e30c8919c045075e0045297dbbd9d48b82058e3908efd20bfad8c482097d361a4b5ee2c90650ae35815f3510956cc13bf07506099036fbcc765e8e58d90b1b2d2bdcb4aab5a7f044731444a67c5892a00fa0c3b11f2655ae799441ec4a60b484d6b7992d73e09d526cb94f2c5e02ccea6df03201b4efc73ad09451e2d3539e56790b3000b870f190666aa9dd786d445f340dabaeac4440411e98435815b82b8c2d0400dcc30089c7a86980a694879032f3e8951fdf680d77bb60800dfcdb40f807883a910289f461d41f2355c05baedc114914f60108a7ae4d68228400831f03e302acfdd86e1406ce50ff677770db4d4b74397db5bb2b86084f97f16387a09dbcd8a9ccad68e390b0e3c14e7567d4f6e78c629706bb29ca90cde81a98da74825d87338aeadd87690a3282d07ce29ef011c1106b41367a6dd32df31b9921af3b4fedd248549c4715d2e3830613b89c2a7139afcbab491ab441e8e5256adae8933928684c1251fe731935b8ce4851283e931f1ca2369d1d2e809aacbd891a6eeb9240ad278c104356622a60d491abe47640e006321e7ab23774f2b8e10ee4924fef83b3abd3ba637ff4bc09c4db3b7bf6b1d12e4067b64721de97623aaef17b486d1293764d46e661fabc2a20a5fce8a14306e93d7db5a541ec2593873001a2d150e656e380cd08c73a962f25607465f27da3992b2f34a63be435bc71eedbde1d4d006a2cbee9cca2430d1bed4e9d6724d19418a6b023cbe57fc21fec9237bbb3ad8b4731f0a13e6232a10556588711a6272cf87088093b670a39e3b00677f0cfb0083912a16ed8cc34eefd4035903225a932a14b8f3299e890adeb4b0a4494eefec562217b4a991a2a6d60633067ea87c925966b2cf2a631a4bef3eb25ed63c128cdd8a22b8fa326adcec00ab91b06c4d55f7671685254a186542338207c980f17d2621035bfc3d5c6bb0b81a5f9364d965349286c3670d252c9041bdea76432a4cda5fe2d5ca6e2db6f899d6e90069368719ec62df63890a233bd6df53a0087fc16ff88b5d7a1ed1a4fed88ef5549a23e66e26f02535a649e8661da032fd59835a67f511a87f64b38560dc47deda9a2a0d5fe3c5baec4b1bda9c053a21d5c29ce1c1fb4ead03fe1cd580ee1d67a2eb4451bc4836ae35b1660a52aa2f217e9febce24041834c0ebf004a01af5b6c9deb4fa2207029409e365d57bf6e593b380fc92ca562e1f6be2b5dfa4c133f66ffa0a4283baa2a3e979585adbe1f80c5199b4b98301affd00605e856bba6bd13e065880489e6838404a0c658e11b3f005f13c8c5caf06e6f12fd492bacbaaacc05678b29185882a1677519ddd088a91980fcc389097e9a753c5d23c92398dc7e51561d81516b18c06e2c02ab32e9d10248739ee4ad6ea66b97e13780e72ce268090e94fc29aacc6bb780591dd0e985c715f2b2c2d166013b561d43aecfc8da7b392de1cc91b136776d4c8f645603c048b4032512ea36d8228d141b143924321c44bca156388bcc54e0416d9886101d286cb4c8303ab28fed0ae20dfaada1d2ae3313196de5a7e7aa68bb78ef9bce8e1857cf7389756e68b25554abb302e774fb072b0aa50652bbd0a4fee015a801636e4320ab733af2aab96271771fc576d4be73e96b3875b5ac581d282b7f2bc1be8528f0fa72f513b9153976bc94a31f37c5b006ee43edabddc04848741c7ed39742f0551af58aa690f180a790f14d2f53b37c0bf7bb2848c52d5fdc71928c57f5615092f53de736cc26c415ab9c785e41752797218bf88f4b794956cc13d9fda10ac8297c2f11b18695445ebe17154c0b04f45e6d33729629f540780ec7ed4930c09ef1a562460ac14d24be450052f0a4d94a1b2e1c74bc9880a00ab1488db863f9b8e87f25a2f312c442d4ad081029d5817dc7e5614d3b3b9059230b40a952316328e7da98e9e2e67b62e32b45b52543e5c3bf442285b6dd6e4b54480dfd9b43e1921c21dfa5a65b9534ae38a5a8ed84638053fcd731c46c3e21e6c0f912d3867481d0c8129a966a1912fcad0c3ec7ece6d7f8f405f704a969d8fbf0b1b2f3364dd2ec09d15f57c195dce08d9f83ff79b404cbaf8906276b0de004a31762e32f542a4501b1188fd7641b140793347d6be05d10d106c7399a9428349802163d985ed008a58344d2bd2d0d951f541688ea0b5d60220343fdf2461994b361919301681c2850a8418106351354d4505207a5439450b31a6548493111793f8a5d0f2527541135d0388f50768022f6517dac500c4ffb4011112a5ba7ab96f052149fb3c87634949da080a410a8af4fc2f44c190a2202c450b4a1f5fd5a3a50f08a9280946750ee031450d9d9135a1b056cb3a126871bcaa05ed796aa1d0022320294cf50d4a05a841a26545e42e9d03c5ffe4102a50514a342f5ad836a0844550087d628685e448434883714280dd57773abc12c93424214b0b4fa295b3551c0236bb268531407d7000d9fcb0922ea2b25bca00f95f18ed2a197866af00a2524147389ea7b2dc5ef9043a15d286059aa7a026d146375bc4b29d25bdc2cb22e0ab32e20d1a485259ae265e18b5116a5bc84021ca1746ca6ce4adefa0602052314880205e400e5c4bba4fa60f0637378dc8e3c0628f145015df74748c2e14f109a13d6e8b46043fec756601ce22a16e148034f153610ed38e30cae056a44289a40498562652820e18064fadfe4aebc46b41bdc0808273f97af182eedb95f5bec2bd444a0c9edb2c11bcdc33cd89511e726871b73c6cb2195b19516d3cfc81ee5cf89dd4b9e31edb98e1032f1cecd225b27b8f88bb66203040e8919ab40cf374fe12486a217f515044019ceda9b04d96a705c049a8fb9836882ee598b5c7d57dcc6466bc2d6d1016560dc65c40c2dbcaff6fe154868c814008c17c0dca226e68d08b1425966d301a3b4e8f9bd780948852f9e4b8d716b7a49208452662816ea935028662e67b94617d696fb62d010d5dd97fb61bab6d0ad22c9b6f1a9bb17dec7acf85e4a29b96bea7bc3c35ab2cd3b5eddf83d8f8106ac952fb9d186d3fd37595380120287683092a5e1df946b0a95d39925e3370fa53be1130089405100849b619f9a3a55bc2c800dfcff04bd4d88a2678c290c0b9a5b610e0363aa28a2ac27bda18978217e280f89f4357afb98d2635ba135f71d863b01b0ff814e0e288528fa9f91a6b1753a6878ddb724883cf521f3061845d32820ef66bd1bb55c0de30ce8e48b0a7862d133a5653a8d2eeb59a57872e4d605ddeca4534f1c7beb93d00381d7c3cfaa635f2a031e403275e307a54d2be02e53bdc13e0ccf2304563e37077534af28e917a9f8b68b2c955cdb217171b82c8a923297f0152da3edbac650c5d1b908414ff6130a2760004815ba97009354f33942d99fa1ef81071caef31dcac440fd7f93fdd26077ad414621acf4bf52f6ccb4ee1bb0b0a4f55114bbbe8636476ef158f24327fda0fdd2a56bb27009bf0f592b0e85c3635485d249e07933201e9d162107f196669a1649fae0a31cdc6eb98680e715a3a065a0a7e3e42c6fbe06abe02871c1760ebeee958634fa8e18bc712c8ba1a9a720b21662a91e02149a7a3d4a504f10b3d8884207af36fd5205dc2d81ca6371aeb825992717c50f523d327afa6d44f8ae328b4549e16901456f0e7a66069c126641b43c40a19354048959d703ab5a729fad650584498b73122998815001612557a1ae444ed7f06c6a9c64e0b7722cb272c8a9881fcff093bd1db8aca722bae375076d934cb8ad5c61f16aa002ce12bd9b76607e2f80e8a58ccd4124bdc3b9ad94a723bbb1d36a1410796f567cf2158570bd1266856b88e215d3799585739928207604e7b6a8527cd24c45b006035052080b2f699152eb2935908d305a68327f26bbb56dd17b4ad649ee88435f252f0fcb4a8c95898652fee24bf371859b1ede0e5f3e304ebf203c45cdc3d42123fb2e277ab3ff122a68652ca91083e19c6e78a1b1c9c483ab4ce318c9fa922e57b199f8e8390c51aec391f0f2c8a6d18c2841811ff614f6070e246860284cc4cf220a5729c61720b61c942dc042f2ce2d7c1a7e7eaee634e47683ab069cdf5b22fc761e902fe59597d87614c1178f5d16351c01da0b72b3c29cae8c6981616de4f3cabc817e320702f5aa69b95072adecde27c487a47961d5406b933692837aa7e356e26f0b2e6231e7c99f79e9a5223408f2e8599ea5dbc2fe4e9c8e60074ee7d2696c1f01baff768dd1aab19adf881dfc7909843388e58ef3a7a34d7b81c0c58dd417c23790e4d9a9add26848faac8f3614e360d4230afa9124bfd69ebce2cb04728838e5fab1644b03f588d1242dfa2409b8507240be1b9f2b7cac26360459e725c1f593111c2f0ad8d526bc81433511032af84ce47818f21a9b25a76c372aef5865c38058c38b5bfa3d5ada2f2844e8a206d5fd794ebdbc5cace469a326113a7a958af43672e560ccf3fba4d23afd530da9ec2904e1f54758ec10bc6749457eca59467cd7fa1dfe3d7c61f5180384295afed6b342ac28cbf608409b5aab1dcc48045867e30151462b339d1fb84a9fdcc5418d21c7870ddba6bffdd5864324e7a7b2605b6abf762f203fe1272359f4c55fac95023e86aea8bdc20ea35e33c7c8eb3201eb5bc08a9820f66c6c33bb337a6d373ef67132ada9f5209d149a589a24b7424be61106d98f8a45c03cd4a70069944f430cca07a12bb4f66788739d374cbb648da417a58708088e0d186f5a47f6c30d1e7358b19f65d27eca04f927165e01391bbba01a47fb3f1f60719c34ff7c8d0491ba481f2652098aaaa844ee631cf7a219dcae50a35d2ef144ecbd2b3279e64899f2e7170c1c92329ef8f43c38d98609abb98bbd8251428c81026f440c5dc699f46869a60db1bbd7cc368a14b7817aaa29a9381897c3998d8972905e4a3f5a4388c353ca931dcef75bbb9a0bce7bc5003a0d989cac46f66f0daefe89602406f46de47ad5a2689c1f413ad0d038dbe63f2dc39a6892f54375c8de8c6403c6ef64caac8752b43e81e06b499d8c2c499b25ebcfb3cf9fc7f4e4e68a2144a9e65633167860ce477e66e1e686c72b9224b9cf2f88068a502840105089429939cd16abf23ee5131cbc731417d4daec546445a9aebc7e9920242dde50ec8f9fccd165db97b3bbc2758ac1d3a5be2a0c99afe1c801c4eabe0e5a64dbe1e08a02008ab4e0c44fc9bb06a4fd0615fd079052f0479502acceb7f5ee7ff5feba06d19993e4151599a1065acf01e0019d67467f55b8a5e333158b6c5ad61da265856eba6470d201950f6f42f8cc58b3c9e72a3b70ec9c0cc7d9c8de55914915379be3ef181e9ab3dded33dcf191765883b4078566309edca81723baed52b4131caff1ce28ae7530eec4535efb719b27acc3c7333644e19d237dd8aaa841c6a9c1e0838ea28c44c2620c323c10673f65e3708018af78342c7d5a7199d6d88838dbdc9dfe42abaad9b65525dfa0659f34895b1aed3168331db369a650b402b88b41c6d299a0c98c1e71ee1786e735c4dc60981c95b8cd7960084b50fa6417f8c38cd005d3a1930d651c9602fb126c7a50e8afaba2597264234a57d038a4d268d3ac554736a295a68d0e6495294c4f3ae7f03278d958b3373b1bd1e298187e987d4b6d7d22525c2f8759e1b9df5922463b94ae671dd24c34acb02ce7ecfbe68c6ba85df4f5ba91353f43ac6986e6b014f0486158f2a7f2d9e05613730186d3d19194dec98d524ac54c922ec7ba59982ef36e98897776a92dead567b11eaba10d48348fbd6acf71740544ef088d1242ff2d54df03591b37846dbc21773b71aa93332456a74ecb6fe791e6a252b60a2ae3aebd05a99f3a0108fa5886c6c9c2bdae2738a6015d6a8185b3c11a752c9313f9626599fb84f4484d454166b64099e4e8383bc5f027e724340c09589a92b2d6660af422aaa1a7ddd2f1abb5bcf51f922e63320a11485053e921a161cd9b822c7c54e960fcf895a3b8ab7dcf555a1917cf8e8c34d64e1e7647b34364bad78debc216df8d694952bca2e43a5d1dc4d36e58e96bf30b61adf2adc5cd115edd8478e56856acfe64c1ea14ace8142e3351b313b0562596f9540866307387962a4424d1a808d406e0ee80da312a1dffedfb487c1872fa102f9acaa3d9405ee73fe4b19e478ab69ffecb4b1006319eb42aa0d0ba49837b5a18df19ac635ea03b308e010084a2fb59fb020d44d483599bcbe1fa25072e6863cd144d0ecc864f34958f05bcf38034fc6837325cb83c53664e8622af5e862f23037d42c67e1597017b0e86a6b91be51639948160e34ecd33276e3b189683367e65a751d68c7235bab777001735b9d7c44cf41c711b1490b810d8b79ef2657fc27b4f966a41949e3a701b207a1f6ab4b563c7d53cf620903f1e03f0a50095162db8394e54721de4a484271fc74bd61650cbf6b29464e7e13fb791acf2d8c926722756a97a309ade68028b2c23c360909a602553e41aa8291361c68486b0b76eaf1d9f45d5fc56b134097cb5d271d35e8729a11e3a21271f22404518314dabe25831f092981f3e0b69da8163e3fcee32410ad644e1b8e04ef80c22229b36c5e7c1d4a092b8470e6641f9998301f98e6b8276b4ce6611f96dfa6e72907ac131e6fb5657f56bed1d21e07a31db74d24d796a6e9c7fff57e791218cf3ae845a139200bc5901da4f6be04ed458a85a37eebd9ab1c0e678e6d8f5da008cfd6fa7c24d1a41d6219772a8f2a0e608464cff4886c45f1fcace89be33654b88370059c05291ac08f815f44378d888fd78d26c49cddfe04884f09e9d5c4b172753d62c41aa7bf988e9cfaa9c651f6b88896a43783d91677765a0b17f84ea48941432f7665d4895693882a6a5a417ca035043a8c2338a145af4050668f47c529fcc6312065e11e3e8c696363aaa6f613133fd4c8c37b1446ea625cc8fd1967d08edf87c0b6ec0dc8c0bf63d21c09c539beac70c1a92505723c4dd3417e756441e18d8896dba019a0fa3d82d64545b389f59a365b6830c516c8285e7a472342e5277243d51597664aca5be1374b08b4b271c305e565f50098e1b6984c630f6e43c8840ee0248c3d545c0e8d094d7b55587d76d3d4f3964e5ec18ec67997b96220b4ef2e8320aa09f95bf6dd2df9a55c46eda7c3f194ceae7c0d7843ee452a207919f3e894c8b73e4dab887788e7ecdf66d24fb0d8fa3d2325679e55e8c0e483e0b6cad020e96c45e0b2b70b61a0730c30c33cc30c30c33ac9ca72df7fe8317a9bb39252525259709cd80c6c584c6c584c6b50de701140b4c0b780b7f08894421a5d118628686055f8c158ccac141c249314f86f697257f0c5df002128c2a13b400052b58810b2e50b6b847b849b35d7b4639473871269639549c856b849362cac631c13e5ebbc4c231c20793b43192b8fb9c369b67b8457852e1248ab7df9d38960ca708ef463b468c6c541ee544f8281d653a8ced21c2d7e8e181ffc55ec9dd1dc20b398b74467510b21d6708cf82a7574c8fb9cfa11cc315c28bce91ad345bcbb54c121c215c6b982ae5b289e1b335084f3cb45d5a88df26ef04e1bd4b466a3454cf8ed5e002e137f60c2dfb66b439860f10aec7742a8e442e69eafcc1ff2c764ee1fce025bf747119d41bc52c85eb839b1c887dca281aa53c0cc3707c70aad33d0e29ec4a1a695eb83db89212425378e6be4d0fde27edcf24a6e6c1b1e4153d3c78e1d343738bccddc1496f0c3d835ce9824ace0efea4838ab3be94e1375707a7a663e5fc6943c0d1c1e91862c858a293c34cde1cdc105ff3e910ed31454fb0b5293839f852628f2f35ef02c334b838b8f9f1b656ce920b56400707274778adbe7ce271db09f6dee05ba88fa88621df021794b7c1970bce0d3e8e17cd2a733131fa27d832255c1b7c341aaf31323ac192206b001a708001a008c706a7e423e69ad11851fa35b8e92d99b466e9e86be9d4e04bf2c9a0a18617f94a1a7ce9194d6bdb7a106b3478fd37a1a99534567268803b83bf3d6a6972f6fa89314371d2b5f69252067ffa2385da0ce9d84106ff2c83a694db31f857d1434369c5e0d46ae69864a799ba307853123acea70306371e6d7ef1ff0b4ec6a833c8c675bf7ac1df4c0db545424e16e982977a4c72ca33170867fe783c83b7e0fa4a55ae4fbf9d472d785d114ae6c52c38975298e8c8191e7b58f0e5d782a4d0fd151c77891a7923c60a9e5b8c18c6ec1b6ace54c193a894dd96192af8a04185c9488ae77f0abe64c73f39266965560a5ec66ad964b9fe2d1a05e7c16a68913fb9dfa0e0464a6b6af6ca139ccdb9fbe62a27789e2743cb1eb6dba5d2043f23244465ea14fb99e08620da5ab295736996e0dafcfc796b72a9b412dcf28e998f71127c0dbb8b41fc4282d7491ad544c8c33ec9113ccd3086c81a2ee273c408aae3b8641bb122b89a1ac5cdd69a544588e084e81411d619e6e80cc10d19927b0c1db1715e089e4d68d4a12b9dc64c105cbb680dcec2cb630b10dcd09da2edc2f7036f62229a64399fbc9e0fdce0d8d16904cf20a3bd7063844ca7868e330379e1add86be58a51b3c5c62efca011da3795535a6ae8c2cffa71cd9a6f947372e184479325f8cb3d88c1851fcdcdb3b745d3d8dcc20b595679aef1cad5b6702e74546e4d311ee15a781aa4a5cf238fac490b3759ab584a74bc7fb3f02346ed6e8bb52cbcd8e629e653040bad63e1a458fda64c0d0b1f86895219ab83bc9d5738ebbeedb9c2c7ec1857b83d3973fa36cfc12aadf0b6bf6385354e9f6256781e659a233e3f8e7715ce47c9d02f4759157e8a315da3a0e9392439154ef89cab26c6149d4546852f9172881839a7707a563bf27b7cf68e29fcf4205386d408277f297c183fb3f19f21f785144e4d57869e1a937965147e88f0e917da4b43220a4fe3c4da3233078b84c24f6b29b9a77641e1a6c494831871b2d1fd84db187834ffcbc0b1ba9e3036e830e339dc4e389f53aa36364d336f39e1e694193eb7dbca49bb09274b689cec31ca16bf26dc8a6c953617e53f9970a3d1c9bd8497ff07269c568dcc612e657b8c4b781a12a9659b39a786251c3b1bfb7ce3db5a57c2197f492b352b25bcec1e7e3cd45a63ac93702d6da7e39390b2b14ac2edb9476a3d6a24fcb4cfd2c03778b507125e9691c79f24cea3f3081f2493f6982b5afa7784936b2ac7f0b071b7a5119ec7b56493b3b45061847fe10d728cd822bc741c177f961ade5084efe9572d54f892b413e1bbbdcd6c4ea92dbd8908c773b0cbf8ce43785dbe59b423a3cba521dc587db1159eac3b5708bf26446545760c1e47087f22499294f1d5566c108e7f7d8b85f9e7d552105ebb59105971d43895817063e5f9d031d4937d00416cece338ae817ff0672b5dda98cbad1ae8072f747aec935cec839b791a4ab4c62934453e386162d228971d4f8cb8072f39ead5cc9dcb7f433db85916eeb637433be4f2e0635c15171ec9c3320e0f3e18ab9cc9caeac7be3b78a2619363b4f49cd4b3838fef616450da8da37475f073a7caa58ba152dad0c1b594c3c4acc91baa3207efa39fa5904198f5b41c1c798b5932223d0e721cbca4fe8eed633838dd20e4303926e951dfe06afab746fa49fad20d9e4f454d9ad2b3fd6d701e238fc7ff0dc63d36b811c3b5e47bbf34da1a3cef3af7cf91a9c1fb709724a7096360692053abe70a231afc9445a6c2b64f3257677036b4dd77c619c48a31839f9281e498256570c2245169ac92c10deb763ea11a83371563450adba4492231382d2985a83f94e48f738085c1c0e09d4accf88d19581c01fb82772b593d4776378f792ff8e3682b97e4cd979ee9823f97d263bc25d3d043b870ceea924f258e2df81f23527a48bfbc59d682935db3b95b3a2d0bae770eae9952c7b0e08b78cc4982dd69069f0c76052f1a4b7b78b558c195fb9ca2c2af4dafb805ab829b630cc96e1efa55ea50c187e21731bbc3b9953d057fee91879e7415b21c26051f66cc1a6ed36951f0fe46a32bdf7b8c313228b8961e5cf60d19f3803dc169f1088df9cb72e6a605cc09d604dfec35a68b19461d454990674cf02cb7f4dff5c4e64d1e0b6c09684c5d49e471b800a6041fd9ab7806153c1c35b8646149f0d342f75c6b7ee0957382dd1e181268c9d6f431fa7bca0b5e900017c8e00537f8e20531b8c1f957806d05404002015cc18ee0855f36b7d4a1720d62850333829f26a23374c629826f8dba3de4f4b03685be6044f0e7eaaa44f267508919825f8e734ee5317fb69a85e0e54721fafb3c25cb2d172c084e65d1c86a11dc345380e04ce714a984c17ee05d0c7d65256216b2623ef034b37bc6b081bdf06c935fa5188d7b45f342f1c26d94c5719a7d76e1d8a72c928dc2baf0b2ee62b498954f9277b970fb7eb23f0c142e7c104b452323af4f0dbe851773b9cb75b06ce15df86e1826c5ac4d9b5a787df162e1b91a4a86d1c2dbce94ed1dab789669164eb8b2e8161dc4571465e1a399f1541b7362e1440d6f3f36fe9b53a460e1847093debdc2b36d9825e3f0b8c2ab102bfb36b6cd658f563849335f869c452ce4c20a37fa1a6f86d95c851f414c72f0987ff74e156e085e1b35354dad5f77a970ad5274d89d354b95d8864285a3311323f6a468b319a7f0e2336504fb0ef628dc6a2853b815b6c19878570a7f1dd54f239b3cded3198a149e24af98e327ec233a0ad7bc2a948cac872c8f287c47ad6a86a042c1a5a4d690621e544603234381c24bad1e132cc2833d100cf50927a5f5c8b28b217779c2eb8c71d924a323c76485ea8493613566a3c5486dbc5c284eb891af4f4afa1f746cbb09afa3f85f5abb54a134e1f5984684d8f249c36b5aa84c385ac126483bb630c155acaaad746476b98c6b5feacd32394d41a84bf87241a53346a7b4ba2ce14fe71c5e24c83f885855c2793b4742a128e18ce3adac068eb1e40baa49789673c87b8ad992843f61b64172aa5958ae22e1038d512c87cd552cb50c19c8606c3500031c680614249c6cf95d35493ef737f97c41835bc1abe08b2f5cd0821318a01ee177474b86dd7184336b292bc2d62c661ae1864621f7639c6151c2087fee521d83e0297a7411fe854916edca83a7d82bc2314dc9dba56312e18770cbb1213588f04396b58791d2a5670ee185d8953cdf764ad910ce46f6ca30b914c207f7eadb195a08e1b45768ce78c728fc41380f366535e4b79042045187e7941eeb4078d5d3a629db3df41910deb9a4a3f0ffc11579e49d34763416fde0e377c9f361227da0b5b3850f4ed6d4108928116a9fa070420eb73927f7137e0aaaa1ee3594679fce138e45c9923cd147fb613ae1e7b79432aa59adcba040062b9801058c07c90947bad287cfb4f8c739f583dc84b3317d847acd96b4660f5213dea749b98245544edb5ec039c84c78a921d3c77e7439c61c4c38e9dd2e224b94b6c6b98497a24743ce915237f058c249513208e9eca1ace52be1ac4dc84836172e434f0983f57dcc24dc4839ed720852ea939184f3af9251393b049f48249c87a11e47d7dfe6fc8084d3d330bd671456a5ef4778e7a85d2bd886b18c7584976ded337c8594b7b6119eb7f86aa6f48e31aa8cf02d5bdea41a53f85575117eda1812625295d9a02ac2f193e449846fa1a18d86a4d8701311ae54e8c70d2cc798633b84ef5b9652eee428e4184378793c48c85e2144c4aa105ed6601969a699985a2284dfa9bcac2fed527906e1869a761845b22c4f47109e667ca934dba6acd0098497ae3bb4add778d03c20fc6c90d6213d5ffae30ffe9cc55423269ebbe1077f1d8614e233fae8b7e983134436dfe4ccd158357cf056257bf0e3fd3a62db3d5259f4e0a7f990a96d953c7837edd5ea51ab2d4af0e0dcc685c9dec1f3958e311ca590413bece026cd65e94ddc4a93a30e3eb6185ab1432d3d0c1ddccab098ea33f73b90e7e065c90c42f9c772f0c3cf7d288decaf4f1cbcbeba2812c270f0ffc61cf5e65791e4bec1cf20a7b6e67937381fd3e5d77eb6c1c7290387522525f94d36b8ad751943953a8a5eaec1f5a97fac1ae61a22a7067f5b528a4aacacda5d1a7c0c22f6c59482063f878c635a4ab660d933382239dd64f8a5d8c8cce0e4afe83845668b7d193cb796e065f926635c91c1779873acc5305fed18830f3ae4d47ea15296550c7eb8a3ab737c235e5286c14dffae1583e147a9d9a2437b6be082051080003570c1175fb880c020bfa09cdb44cff66833fa03e98522c009b20baeb6c64551cfd9336480905c70326477c687ea06c6050ac82db8591362c335faac2ab55083cc820fe5aeee4382a7c7512c389931ccf08c357905374f466f9d41b2e90aa5150a292d3c5345cae717590537f6353e87e66eb6122ab816a91e597414d3249d82ffd60d5622c6de5811520a5ed79c550e498d822729cd69b88f515c24149c08bffd2627a39d2ad7802df20927891d1b3f5a904ef052f97cc7a434c10d1315aae5e9aea94a269c72ccf24972c89c608b309730ca9b1ab4b483dfe681548265125cff6831844ae4ef9e344d05a39497807d218317c822917047c83402a208b2086ef00d55f564a5d21922b89a6d1e877a087ecd5f886f97d8a91621b81639cf658e36bf4910fc8c8c5cad16b53b0d042765c931760859e15ff2032f8618c1fa72237de0d8e7ef740f8f21c4bd7042a61c19bab8c30c2f7c9c3dc4827f6417bedc54c819a5b02ebc97f31bfb60fea1712e3c2db5b81cfe6287ece2c2cb9a7144c914e32d7cb4d2a0e61bc4b411b585b3665177eed7c943ad859b8287ec20e5248defa485e358b4424ed779a39bb3f0a1afb8cd947b467cb270455d621a839b6d8fc5c29f0c9a16fd86855392c394a389471bb357789d2368d6f099dd29e50a27ad23d2d9a4d70ab7c291ee984556f82054bc556e7f153e0cee39ac8fc64c8d51857fd1eb2462264c456629ccb26fc30168c001182000010840000cfc37e0043240410756309e010420c0a1c24b1e352ea8877495c053382ab22659b1d7ae61884053789f2c6323d6841cc2054be1631cee28bbc32829bccd97367cfd5d388eea285ccf1453822787c13534120c1485f313eb43667708a99141c05038161d24e78cb5fe490282c2491355d9c1f959840437f842066604fc841f9fa167e968933ee509ff51e798314507f128a89d702cb64cfebbd0d9eb523b20277c98398688d91d8bc7cc26bccbe141869d3d9af0522d79b0fe960b3196093f35b5e39b5c95278309a7cb3ea30e7809ffff3465b9a83d29394bf835e1151f29e7aab10f2be136aa160fe14addc3352025fecbe0ba52b28e9d0027e15caeb8e0a0a65b638c0428092fa26543cb23dfe1fa136c550123e1d4b54c24c9f5f2af2d0021e145cd5d0f3d4965087c84174e42fabd32de2eab41a023fc961cd4da1b5f4e0f35c233c97f8d728530c2b506a32eefb9638cba10b808373aa6c5f556dda744115e700f71ac61c942469b2030117e6cc72c296ef6512d44f8695d52f02c39c45417ce031ec20b61c356fa9a9475493ca0219ccd9d95633cd67cea0ab010bee5486ddbe1d305b52021fc697c6134d4c62ced4fb0655040a28083f0bfacad2ae547df890109f01350178021a020fc9ebe09cfc6e82be7dc0103e1a35895dad01c867c0808c72452ff96879b7535bf807ff0a3c6f4307b5e0fde0e2fa01fdc148f32c68f4286d9f83ef8e9fa2a58d44e1b900f4eaa71a41a630cab01f7e05b4ef3615d3ca6871036a01e7c3019784c99a13ee79c3c38a92754ecb29a4879d38078f06347787a88d8bccb25c03b381d3663340dd301b48323514ed2c29727d832c3052bb8c1784111b00e8e276fb9b7ea0eb68d136c0d01e9e0c318be619f7dc3dce609b6be20810b8ce900e7e0860c22d954b67f327a82ad18bcfe0c7e063068811b00328172c0fd552c3240410b54d0821990e00666fc178b058c83b32621d584ec0840000210c0030358c00028908104fc37c03cbd02e1e0a5f412793ca43fa56f70b3fde5ec93aebdbce306b7330709414ae6be6f83d3696e272b0d7c629e0d3e0c591d530eb5d99135dcb1311b934ad4e0a42b7b98d271c65c9a34389d2334389d83bce46ef994927206a7353c9a4fb9ddce2b66f0fd71248ff29e327856b361254506672b877c912b4608d918fcec2987e059c63ba4c4e067ca90e5f1b73d530983631943c91ec3669062c0e09987ead09b6a21ea2f789abdaa1e62e20527f4a650ab1cd205b71f4dc8f0e83ba490c1053fed318c1ccdde8227f218ac05278966181997b3e08d3490b568f93f628b05e7563de36493225da257f0254334b064a25511b582db2863ce1ed266e3a155f0f38fcd6453a9e05b5f5606c9814a6d3a05c73ffd1e5ac55ca15229f82af5c8c5420c13ea8c82172bc7181236a3a830a1e0a96a9b76c48414b67c82a35e39df5fcce2afa513fcfc7d6aeb40a39b954df035e4c71a4cfaf2fac804ef2393a954e85e79e0127c1c53688b1e1bc28751093e944849f2e528934a4c82db3977cede2cb92d8848f0debbaae453ad63acf1086e56ec0659f1753d3182e3ef3397ca52b8f7ae08ae6df94b248d61293b2238679929da9a4858c6109c0c2b66b056d127b82604ef6263b4216b8ab0c982e0a75411b2efc32bd5050457e44b4e73593ff024564697962603cba00f7cfce999963da6a7a517bec563748f3e656f8c175edfe5eccbee2952a25df86942e53bbf2465e1a30b273cb6708fe6910b2735c6e1c33bd86f4ee3c275e423e9417eb0daf3ddc2cb199944fb6a5b78f6c953d2a429fe63b616be7b5c588b9d917c98b4f033dfc74ef779ed2a9b85f749352daac34c479e9385efa1b5717bfeca2e3ae062e1ba45690ce9374f46be57f00216a0e00e070b7f3367488eabb25535ee156e8cecb852944d15dd7e802bf84e2d9b22caace35ae1a67b14339217b92063138e157edae80d1a568787d0afc2af699c71ce95d2cc7a2c9c2a7c891a22678ad54c9d63854b851b437e1c9e197bad8f8a325c4adf29fc1cf3e518292a6d4ff64ce1788e9650edcf95c2cf932e774fca48e1448ded9a31dd1b85237e9171a6b16ae144e183d8d45d123d62b844d370a170d4b7f3844ac58c9466058597353ae6f2e47dc299dce87358c9776c7f9ef07fc2c6980d1155aab94e78f9277c67d022393b4ef8995190702dc14336f336e17466b2cad1efd384f711d66fc173e332e1484b7bceaf7d3156d032384c7851be194f26e9b5754fb0751684bb849b59b9ee1a86344bda09b6583003e6c159c28f91fc40f3ab9f6ace09b60c095a8002133c411c5c259cf37614a7a511a9bbc151c24d29f44a4717ed26e1894beed0b5fea56a3949b829cc939896dc01abf073a39b3c394223738f2a7c9b7099518af4f8ec543857e944925ccca6c8a0c2cf9ed72323d23d85f3bfda8d362ac3fcdf9ac2372b6d60dfb3b19e3ec192e00b19bc805cf085e696c29f9461370e6fe12a34a440be2b2ce4cc5ff951f8a61552a489398fd52e88c10bc69f806945d1842029f7a61e0b7e3183f25fec0336146e9094b4ed06a7aa0d50782173cc0e11320ec9d34f386bc95187b4293478d2137e58ca8d3ca22283838213c480046adb09365d739d64989cf0cfbdccbe3504ec26bc13c930f3bd59c6999a70727cc68c19e3c32f2df8e2652083f10212a860fc1704371394aca7918a86c18417c2afe6bc39cdc9e65cc28b359137c50ca5fe2a96f0cd3c5786c9a06e9253093f4ccc23ff9ea4845f3ebf769161842927e148a7142cd362347f2509c73324874fc9f3197d24fcf7536bbb7021e18a7a5b45fa9f53cf23bc1497c9f2478e702d63b6b0d5d40d1c8df07270f70d973384a8c9081fcd5a4a90d011a12d8bf05ac66f8364aa08ef32e3d0a9368970423cb628218dca7f10e14f4d5bf21865c241c6211c4bb2197a46de105ea9b66cda14b5885d21bcf5a03d298609e1784a475af39d41f833d15182552d08ff62a3adbb6903e1f8386cd1d4f4b9220684232ac9b1fcd857cafa0f894cf77ef03f4850b954f954b1f6c151c9971635271f1cf5cf1c43d4e41ebc14c3c9a4eec96c51d383133dd59a65b5960cc53c78796a835b8ec143882a1ebcf990c5b2b2e243bb8367dde173e7901d5a991d7cbce2338dfcf346eaeae05874091d3cff9442ee7e6dc7aecdc1bb68b3a5f931e4e0a564a22173acff60571c7c1492a61c63f8f4535938786ac143a6b52af99c37f833133ecadb9fa9e7b8c18d5cd7e0dc34ac41dae0ada5aaca6a9b3684880d5e54b3fca1ae6294acc1ff5c6e7196b305b94c0d4ed218b59861747f3b0d6e87b98cac298306bfa6c33a7b4db0349ec14b91be1e4ce8d60cde68fa24ee3065f545b765582b8a7bbabace6c60206083372d40c117a3052850800c58d0001cdca006485832b8112606f90ec11bc38a418b61cc643c6c50c906360c4eeecfea073369ee6f30f81e7286eb589b5d1e7684fd82d560bde08c7a8890be55179c34762a5e165a2ef80d335a4677bf2a55f218760b4edc6b5c5a4b71b9cc09b674001a70800b4c9080131417bcc0bf051660810bbe680006084000bd0a8080040158c26ac16fd0b0b562f26818fc012ce0109b05cf5289d6f5e7349db662c18b78943699bb2b40c190c133e0bf0175d82b78da1923ad183953ac15bccd696203c970010738b155f0b32fd671e7cc171a0358c00230124b0527db81a5162d4dd08244ec141cbf071163225a54b14c0f2b05377b7a370d2af3198ac5878d821b551ac5a19a464b2928f81bb4e543e76c186388ea0efb043f95b8c5c49ea855e129d609cea79a982979ca4e6c137cd190da63aae076d72d6299e0ab660bdb61dde53b2583188c09ec127c9f98b29da69a11ab042f53e5b81abb069e7192e03dcaca525b23c19918a2d895a763709923b88dab6108991ddbc3085ebeb4ac718ea6d22745704e32df24073311ba8ce21d43f02ea5e020ec2a32305108dec420275f194b2c4f101c7f640d33c3dc294db58b05828fc424a4cf6e318728d81f78934cfefed53bc1d6fac0490f36a5139595caf109d615da0b672dd465e4b06942fcb8505e78af912a6ac63c2d7417cba41821d9cf545df819c4321ec61c124ea583e662bfe4e939a3ce98fd93c10c5869608ad025e5b8cc241359bb6581dec2394b375e3321a38b0e5b38a19549a543f95d4e65e005830524d85af819030723a6854069e155aa10f375eb889967e1bf9ba64669c89cdd809040065fb820060b0295859f4c267c86f138c17e418217fcab8004672ad058781b192e2236eeb631b0f07b32c5e3e0d8afe37136027d855be929858ccde30ab71e3bcc41bc6f85afc9cb65c426871433ac70430ccdf090bb188f55f8ffa9227f259b51165578f7ee8e31ccf8c427a9f042e8cfb0b0b8e96aa0c2ada90c52a2a54fe145d330c92a368593432b58ec724be168d87649e17f4c7f9c1a4ae6ec76149e8d65cf4bb7d8ca2a0a673397b84c87cbf26a289cb3dc39c25bf8dfa8a0f0453355061fea279c3495535bb265cb1c4f78511baae7106dcfd0ec84f325fe9e1b573fba70c2cb6ef5affede26dcd290982743a70927f643c7573949c660cb849341aa08963ce63ce1c2841b9742394e9d4b38b32e651316a925dc0619a718bf8bd93868ae95f0d2264bc5462bd6b6f24129e1265b9fa4152265ec9d9e842fda49ccdca1c30ce29284675d26a59e34d2a7250d1a093fbd1f07f3903b66ef760c0a097f2aa5479651ec118ec34a4bbfabe09223161c39c279edfb724b13b43ea7116e6d6bb4c79f3b37d630c2edd77490126691d3b208675385484fe9bcd22c8a70a52cdd573a23115e56a7d50e0f5545634438d519458aeeec219c07e19a7932caf86c13420de177c80c07392675f557082f2afc6ab86984f051862e1e3e3a0c91d520bcefcde358a4fa1e8304e16948139e2a1b26bf27105e568f901d6600e1468cb24f7e3985dbfe0f5e8887bee94d23bbec071f5408b908e9434cb60f5e4773f9103eb87953c5ce5fb3073f3a4c0e600104204091a054a17a70365b47f3280b31843b0fcee4889dc3a39bddc3281e5c9bf5e86f5adfc1ebbeb56df49ae1e5b583efa81e848fc3749d5307bfebd743c80c33a52a1d7cf039f8c534ff0956cec1b11799b28a3969cecbc1f9346d52b1222233c6c17f18993394b47c9e378583e7d1f7d2198c58cc287d0331b2bcb793eb06229644f6cedbb6911b9c7f6684ae92433aab6c70c372e6f033a90fba06d7c4b378f0183da81abcf1142a40681abc9c1bfd68feb19c26a26870fbfc62dd7d0420809ec1094f5a0dcdb1755033f87d113c5ca7e5a065f06366c76555f1e29d4c342819bcb7f03bb7b7f590ea0632d031f8314fcda518919184eb102a06277bb32d9c879cdef209b6d006a001070ce002e30bff62d440062d40810bb2340cae865023d7c842442818bc4dd529432aafafef82d3021458ed41bfe06568e1410c96b11dac136cb940062490c1c7400631e833ea052f3bb55f1dc378f5f50dda05473e63a4d277da91473728171c5f73104524c7863eae41b7e0a72c4d23924a6e6519c4c00517482df8969d1ef9852899396d2706cd82eff197a3db5c757834161cf1cc9b53e3c9f678c361d02b78252215eb1a5c84c85bc1690c2af5e4be54c1cd20d66d2c9f938c6a2af8e0256d632f0963f99d825f6229b52e7aab67530a5e650eef2ff1176f6314fca025f9257d4bb3040a4e46121da6b43ec10d16a646fb5cb227cd097edd5df40ca4ce2ae434c15f8d4c66d7284c701caf668859962578762984f86910330e132578ee9ab339fe8e922c2309cea76b2437418267aa591e47cab619ea113cad49a6dd993282271b2be4f88c2278216d1ba6168bd020028312c1bff8d0672953c21c6698abd20b3a04cf325eb5c6216c8105fe051618c0021470061582eaeb579331ca4eb075835701096e40484026832f3e062d40356810ec8ff93f3de42cf95620f8a142453568f0d92e2de80f1ccf31fb038796c35414a03e704de32ee3cd91e9d3ee857f8ecbeed185178eb5a4986cb298bbe0532cc58c65ac0b2f324619639c71850e3717c6bbbd4c99bba9a68fe20412178eb94fcc75a9691b856ee17f08590ec237e8f4c4e08b97410b2090b6f06175e4a496f3b5f0caa38f048d6971dad2c2ef8ec8da369741276f169ead4cd67e90a13cb232e0849485dbf3a0a1240f21bc5b4ae12163e1e491096911aa324a242cfc939c4348d50e041630000730e009f90ae79138cea6b2211cd2153ed47e14958346a4cab4c24b935103edad1c7119ac705423434dd963e11965158e54d8ccf113ae35e755e178f615759ce554783979a669fb07010820874485ff21a2f78791d0f2344fe145ba4ae7471f61214de1cb25cb17fccbffd5a42c858f6a82ca787bf99a0119fc173220a6852485f3e062509354113173cc841c8523fd1f52e70be92a4414cea34b31b45456cfd1134286c2c9ba881ad382da8be5020d0a2f39cc7479c462ada52df02db08006fa13ae6b9a3419315b45115d069ef07ec2c3fb854ab3aa7c0b36d0093f2d4ab6c98d32456539e1a645aeac49bd4295d49b30f23d8cf4d8644d388f67da53249f98ff41240613200001be78f3850b6883d099f0c543457a2715135e54659cf517ed66cabe849325cab33f2b7537b42db15597aadb6747c9e5c7b12476587827d8fac2050df8159021832f5c7001021080055fb8e020a18b90233b65f69c94f0cd66d4c17a27c711ea49781763b42c6722ff634ac2752cde3836763a127e0acf517c520e0a209169469d4736e347381fd2c7fba9e454ae3ac2edfae8fe7259231cadf9d35c1a41153423bc9cc1f6e5708dd232caa5a017e1a750bbec8fa13347ef02179015b422dc8c3ea58c695ba3e43ac1d6178f821383197c418218a0802b062b40810c669008ff529a8d1029c58e616b44902a2551d1bcd130dc8770da1ed87d76076d393ecf807fc10d3440833684ffa55922a667f43ee209b648700219a0a00624a8c1b740ab10aee5f2c813a983977f02198c0be00964305e803790816113a2cbeaa8bb2d4bb1ad748db64f294ca6d3bce50964302e4000029c400632200001ec053d08e743a61bf96594db6341389a610629889d47e840b8256f139534a4bf4903c2f791d4ff301e7f70ec32ae0931ca7cfadb0f4eae94d0ca4e761f9c343d9beb525d57a5f98099b9ca4446d6da7564c43e770f7ecd868d1939cc92ebe8c1b1efe9cb15ca26a48c3c789b793248455d0eb1361e7cd478a2590e973b38153edaa64eddb6832f31112634964a1b235d07279f65cf1945d6ba8daaa1e9e0457bf5cf5b1936fbdd73602dbb4cbe4cad356fcf35b69031c81c185a0e6e9e1a5bf5749f2778e2e0cc688e29a7e31b0e7e0a198f7bda8a6b34cb00022ee83778e71ee2603e6f837f000b5cf04503dce0953bca46e7bd520d7382adac18547d2183185cb7c17928a1c73a54fb952502cd06643e44a342a64d53eb35bc2d558c555446abd06b702b470669538ad4e08c55f8cba9b34e8357eabe9a62b34b82e241a3c1778952d79872d8469f3e835b0fb2c845c6b211d38136839f516a30f1df29e542ac037419fcf38c2a47830fd3b9800524a0010acc6ada06309b0c9c9659499686b8de1493064a8fc197cd413e494619645c693178951e65328bc6647776183c3b0f4d69424c8e301380005ffca1c1e0c3e86f194af7e66c321cfa0bfe798c31a5360c8fd1e4437bc1c9719b4522860c1da62174175ce9bf509b341d45c39a0b8e89c474aa6be8141a5bf02fc6d460a1b3ad053f2e6dc80dfa6e6db29d85bebd55d32d4ea4bea6c63e9ac490ebca63754d96ed7cd1ae265fc1f70696c27430dfd41b40b415bc34f2117f1e7615bc4992afeb6f3c2e639b0aae46ffd60cab26dd834cebd053f092250bd7a8f23a9a54069f8196821f7129fc2201d6868e821b4635460ab9464553d6d05070bbc26cb56b347325af9fe074189fedb4102d5ab973682778ee32f1133329a21fe38b71011b7413fc937e4992b5dc6682f7e26213dfd9226a3bc1165906b87a09be858f6a1a294709cedb0633cb7726219d2478921661a2ac6d6da691e06c1af11c36ac32b8fa084ec8ded3387ad808aef57fd61c35b37d0617c1d50e5759a29444f0bca53f926d0815f586e0a5477f3c12c25afa08c1ef764dbbac06a9371604cfc51b5d14bf09f75503c1cb5c1353bbd867ca1497a17fe04f0cf996f694551ba4cdd03ef0ce1a9ba4e49414824d06ee856f315bb29ddc5a280b0a5050cc99176e374ec9e3d15845f6ecc20729b4523ea5c8e0a2ba70f2c530da49836d8f24173e74b18c5170e0c295d3b2983ac99d6ff016ae7796cf6a058d5d9b2d7c0d9edf431eb9bcffb530653e4768e1e75eb74d99c2b36025e262cbb2a6332aba676d2da6f3ef64e1672c1e15dd92fc232f16ce65e03866e49ad3be8585971936b2d33ce56e617e859fb9619ba50769ec1e5738d7c025bf46a4ef07722b9cd83ffe1ad9d0ff1cacf0cdb4d32449b95578a1baab518c1c33fca60aaf53b2b5481f5306c77903a762f0c9b2e7caa89603a3c2bbabfc613c03e9143e96986206cd0e627fea052f18a6f05a2e3d3c94b7a8309a814be1bf439b4a313d76259f49e1468e97b0942454a3703525896e93c72a840fb2289c8c1af972d68c4371ca64cf79ae9f41e198d979494fda49dffc09cf3d976d7c8791b8c09e7022d6658fc8491a4a783be16cb97cc6b8c29cf067b68204eb18336c2b068537e185499a1caaa6dc51df6c604d3879a2564bbcd2c764a1800528980067c2cb281623524a5ee9a7c30363c2b5b210ef9a315508365fc28922d96eed1a51ceb534b025bcebad0a7d16c28aaf5c09b727c60e19d6730253c207b3295ddd864a9a734ae04978c122a4343331bad88325f1662cf9824d58e84747428d1e1ba5b5e4136b010a580089c4b7c363f173f0234aa952be317804ec88e733fc7f2c8f071ee64654de5d22ebb2eda91eee7da5629a433a66849faa1a23b9fcf92b3343e045b8b6e23835657a9cedb3225c999467aa7beb61aa7022fce49acbd187f81c838311e15a6730a129e30fe1c9c7dba784c5107e68bc25294838ac3c29841f3b330c0d3badcbdb84f01cc8794d37f84e9f0771c7a5f6b02cae201cf5de8acec1a7ac0f849f1a35aa464ba4081120dc4c2aff1fefa919cf7f70264da6a670cd311beb073f64ee18b16dec839b2b030dd3e893e6431f603e38528ec3cd843c6529630f4e477bb3930be92193b3f0f0ea9a4fbbd0fa8676d5ed99474209701efc8c3437bead1bc078f022675f74712800b4c07770ad4190feaf9eec1bb4839773f2f8872a19a48cd5c1f10793db436620293b58603a78a7397b76ba0c29f01cf8f1b0de2986871c3c15910acbd12045c6380ede5f7c6a7bf965108397810c120007cf3dc48450a932acaa33a0057e837fb153696e98a5410b2070636037f8d91da4680fdb16a0400261e036f81b3365309f53e34f90001738800d6e54a6be89f2c802afc14b179e634ad9b8330c13d0055683b76296f2a44bcafcc02f5cb000d354e034786915226362ff8219b8e00b077014180d4e63ecd9e4f30a053e83eb399c6c6b49f9d5ba053683ab1633ca5eb53952ccb5c004288841961606b80cdee5b48ece6bdee49118038b0093c1514bbf6116c2928683c7e0dca6863d59421383e313b3256365340fbd0187c1cb171b4832879a39a32f5ca034a80283c19190f933ec1ce92b2d7fc14bd9f92643c6602ff8f7f93eaa1f4489dd110377c12f4bf5217d50c911395cf09283cd309a5ccb5d7e0b4ee4f46bc1abd4e96a22a6742137b2e04486b9a35f9694ee0616dcec17c26914b9496b5cc1ffb0a5a5d9b7822b5f2187b10f31d17115dc185cbcd3d74a048da9e065d4e51aec29f816f15f41a37c8ca14ac1aff586a1ea68834fc951704336481952c38cb68fa1e0af87104b1ff0131c8979e31ed4c4e430eb04533a44546e5455accb3c84e99e076982e778726c5af850a6a11e9809fe49ba60556ae2a71a37f012fc96a9b4feb51a2bc1bb3f9ba8871191e5e73d7012bc4bffab7922cd362a7560247832339dda2d1fc17bc7b1b161d509b011fc1422a548991ce322b8594a265a0ae1d6dfbc021381aa6b9558d3c610dcf458f216c472566859085e649cc7189f0e82972ea949b444033bf18c5f41ef8081e0dd652e15cd97ea1de4074eb4981e457606fbc08deb8fef160d72312468236c2fdc14e4c43afac67cd6bcf01d54980cd1617d4f8541d85df819d604bb8fb9612c892e5409e70e26a63bc1d6e6c2c931fb52c8e359edc92f3c1717bec7949375564e154e2e6f5cb080372ea8405319606fe1a714826b7d6768616de166b11cb4b26abe183c0420c0f98206e70b1adc5e616be175c567f9f05241738716be75ae8918f3eb48e99d85ffd192f6a58478982f74bc200332408102d05a305e1003fc42062f900555570f3e3748afbae002b1703e539e28517360e1f57f78b97fbf4fceec2b5a9f3009796ab386d615fe78f0902c8302ab32197c0cb6157eca69a6474b1945cb7fe10224cb0a27d286501a528a5913a9cc6ef0850c5af039820d8dc4a884b084025924140804e280388a61b0e8f800a31308001038280d4542a168341145550f148003522020422c2a1c262618101a18128d44a17030180a85c1e04018140a8702a1a089b0c431570725dfd8a894a05b8acd0826b4f06fefb46bf4071403f0bbcb34e8309ba499b5787c0ceeb8086e43b1c12a4848be5fcd8011ca94345e40e68f1be4f5c8a7b71b7c572d262daf3b536139d27fd6edd19ac45b5bb00c8870f163671b76cb6b8c41fca09b49be1dc9d6a6bc0bedc1e0ef38b4819c0b507a7f9523936b3bf164b536f62105dcdc9dc438b5c9fae2c4e40a7053c4de4b5b3e197fb05389446aed2ad1c52923085a26c7372e053fa25c79b7bc6f28536c9b0acce84686099290bfb740a252448e34d3122bb778f40758b4ba9d490b52c462dd8167438c858074aff6e0a27cab773c1ecdfad3c057d999ec65e247893254d14b9ca48bcbb036373fb1f183d4f8e103fb9f09cde0a75235430df23ae3f757353bd38e7c2072466e39fa9b6fcfea9b4c9dbdf4b9258e786ff770b2c6f32edaed785fb4f46a9f93a919d3fbf05cf899fd87a3893dc9cbd343bb51c2da64829d379d6082d5ce31dc83c709fd545d80d9f08da41289652ff07b91924e440366d2795927a4800b2260c20080aaab2a7f2f4f93d844e180d54333911c4be3bec10c5b33329b7d8058a1f9dc97ba30a189a5d9ad80039b77c00ef50bcaa881cdf4ea1681a5103f1d50f31c0b91dc3126209b20f7296e846edc3e4dfad56491a096049d595ae41e9a6313524d385867f64f87a42f2e6cb6bcf0b511c31746faeca26d39b90a201afe48bd1294a123ad46e0271f34c37e82ed9ff2a2a9b1692473ba5d16772319ec102d6385583cbca578d62dc6a5ce7a65be29be8161eddcde56468ccae2cf82da33712cb09973a2c234f15392d408fe383e7eb0e09ae48c9c5a043134f9e54353e534721766734767d796037cb5dee1cface38d8fd7cdb891c0f955ccc625b75f6e49230a7358fab3f744bf7db0f464e8a1dddf1876de0aacff5447ea373b6f0d32321f6c6f4d4160bc7ff30ef39494fd498922cf358cb413aa35f995c3463bbd500e39c4fdb2a41eada8076eff05590da7bd0f42d1dfc2dce68eade6cb14bb26feb7f8fb92ef22f9b7d0ea8b427960e2860b628ac9f3ec30053d968901b9bf9edc80a8ce5df9fa27a98b1113e630f73ef5443476f2324f851aa75b0e9f856ae2cdc93e013439c4b7d32a5a1c9b94047170c7557e83311dfe39465708c586a8345e428171c125beac92d55475e15c1125ed3ae0b4218630427e560f114dd387980424f29371fbd9cb609456f002348e3c58f3a010f80b7a6cf0275d785b5609ae8a5746b9ffab41205cb607c8cf0c92591ea66d484c068dd1cbf31329dfd4bb254d2bc827685dc97b8833eda34de0686ded6adea96ff821df2a9125a44bb9e65786d1e609beec8563deb97fe2f3e5100a02ef38d6491078ff743b31519f0c312ba5f5d23a0d9b308186d38b517d098a0786bf38066e6241cda78d2a977f61b3fbffe2e5f0ae9afa4799497c8cc143c4a75c41aac7636da418f9f14e027f8b704a0272de9680f02b4c7ad257f022662e71ac75693519adb424a689c7945d1559dcd9778ed3bb8a3eb257436014298d959fc58c94783435b4b759359cc0d4ae6f39675d14ab6d9e076ea5493c76efda5a811696b0c70812e5dc1136d156f12716e41e630025ab64d9b96bf98ee3fd5dc6e332dc1e5417e2d0420b4e0fa23d8cd04973d18c6682256e46b7d85c2fd72a2411aa1e45f6d91dba5fb2246f3b94892cea2320d9bc5281086b0a120e82c8a091cb348e8e2ea4277f7abdbc693272f65c139c197c960e56683979158596584074b821e44d45fed8588db855dc17859dfdc22e2b74ec211cb36f7061bb6b09f5a359901ed52ff623451f709c6cad3cce12951f20e2e4e9053e54232858bceada6b782bd05315714f558c3d2f542d33df80b9db30d0b5f34fcfee7914946eb6a6a1cac56931de682be808d2dd9a127324f7af4568027fa9c97004d133c72ebf6923a364018d75320b48cc3f2dd84b8ef7e8f9ae5efcd746583abd2e5a93d2b31a79308b0a1405735eae4529ffc9af8eba1e91b1f8a7bf6bd1fc2e1aaf956c7867fe9fd6c529867ee4e6ebc5701a3e3fa221b10735a406e4cd07160d431d874b4d0cb0a91d2f9f1c3f0f1add6a45d4f26839c050994f0a2f85efe6412318fd26bed8e7040aadbf0d7308ad96b634c4922b39f6370b6fe013183e6a0d8b622c58efd6297482accbc6c0e6975d3228b6db3cfc38729cd1598e36e8744fd3a40b286ef566bedddc2875212bb97c3018bfdb79d58a66e66d149c4e681220d77366e9c5e7d91e579944b43f26d6bc2b09bb687e663295115438602eb73c8044e5b283e4ac2ff9a91ad640ac0d1401cf9057dc9623bf5e3c4d0b9339e953307db2f5b7246dc2194cb88bc4eac9b8917ec5f63b56df0095e458b9481ad300068dc7960e1ba05a0e2297d594679f7ad9876993fe3f849e32e07e86fb4e99fda6be9bb86c238f47ad1b46f97d7e13aea9b05c443d963d6ede1ef216e0b6cb71ff7cf081072ce7991a5ece4fed3e6392c41d20759fb192644804134748a25709999da173ca4467cd19f44f739ebdd643094fe117b16f8d900275c73000dfbe3967eb819d66b9a8a8209bef9821459422a1318c124dcd844c1c46e8b97496fc9a3a0a1d7803d1b05aae07d1b401216f1126769c028f2d3fcb8ea7122918e509ca8c1b9e813c4ef687a4c40edbb8ec23a59c71ee8cfcb8441f9a5ba6bb91e29484073a0640c5d119e1568306e840e7695356375ae6a478469a589d7791310577e32fd5a193aaa282529de025135140aab93e674fe1da7442bbf3244a7718e587451598c74567e14700e65bb4682ff314f9c8f8e3882342e1b7d0e9469abe006791ee3684666bdb281cddc52303c1d85beacdde13af5a926d2657275e92bb47e05d05ad53736fe9fb03333bc791b559d071e932e0082fd7fc4bda2b936712042efc3b255cc3402ba656c2058401a20f462f009fbd5b8495986d436da541638f38f367e11a38dfc34a0103fcfc3e79eb0e1d01873f2ec13e7f2e35813c92154b6918cb253334e9394531742aaf9ca304213e0513a66c3dcdfed80b4f4b1ad0a3acf8b7030839003818ba9f9f1e582d503ac72dac4a7c07db3dfe4ce4190922a7bd1f0937b387b6d712bc8b1fc7644b55e822bb84a1190cc4cff1e8dbad8fd1f3d32d7504ad298d05014fcba5b0952783e9847d44b99a2205f7a3bcdeda6f48117e885bfeb164ccdbe25292f700dbd4c1ae89121495ba21931bc16b9293712999be8c279ed24eefc24da38fe43ce79204e6dec823b6c234fd1ae96648890778cb0a9f4e503abf09bc0535d9b06be490bbe5327f5fcbf85f9a18cb2c7bc38b535f136bca3fb675d1046c972a6c01ed9b67f8b8167c1341ce8b97bd27e075bc9107e6b02fdebd390d8c9d25a58a548bb552a0d004026892f061135b7058078290f29761e27697cba9d74259c86f5749d5026d22f70b2aca8d5788c698d8d8411de55e68d7f8d0ed545afd6511629c5a740f7ee568f0457a218539c5426039a7702dee9002ca64a7f6c70d7ad43b3104338322201274828391375a6b20187355570b6ba4fc7d44048d824444fba87b51478a1c3bfe5c9a816479e7918ae91cd27a6db11673f2df01c3961d6cd7c7a1c9c15d166409e7ecb671d8961a0c83cb595b82fd57b84ba1d6d4d260e4848ca413ac67af54cf01ab5bb75f5972019d4723b743397da61950ec447736a59569b41a14652a0ff5b87c35a98f18b49ee911f21cc24e6d346fca2fe4a13832400e8769eb961d18561c7a825e33b9dd1a8873c5af6a92dd13f31252739802e6e4667edb4cd8b81597255704a6d2dddccc64d0d737e718907cf3c5d60e8219df6b94dd547ac8d14be7228ef2f4c4caa66cd6bfa319f378c8ec245817a6caa4ed350293fc0386f0098cb1a09e3128b53de0d95d5028d023b33d38a9ceecb8ae0526e4cdd49a847ee9d1fc37eda02a60144e1ca0b79b7886d6a5d860241b6946fb648e5d1ba9ffbb06f35039a197376d15cb0032b3ff3d5f05b2dac6001051ff120e563b0f184a0e10aa19f41153df418536eb8e4cf0c5bf0f2aae8b46e832d31e6cf110133730ad5707d9ba855b979a6223cf5cdc9748afac3305928870159e3d7698a608144f1460c47b9eb74f32a47450160a020789be4b4dd0fd882704f21561d63c5df34e940312a37d8bc2710efc04794290ada8dacb9747780f0fcbcc00cb2a400084e1177164706c673ed1fc4b2dd8e08f370ae6c24ce6517da9fa5ee043986506ad8a4a36a66d61bd8d93bc99e53b5bb032451582a907b0a99818d20270713ba17240043478fa434844bcc99502824b87d5c093c9195685213698e868e22fdc01f2c371a95f419685cf3238a3d81dc7d9f91204b137e97fa97e27b336858f861a16c010bc867bc2f50dae7a157043a2b5f501904470261d28024e4e40d1f434fd624cceabc457b3aefac45d85cfabc57aecd5f5e3941c5503a742abb19fccc6e4f74b6cb6c4abfa41751bcca3c6281c371086dc8180a0264befebc25d8d000d104bad14bc36d784750fd03eafcd0279f56514b14dbe6d3bf573f6acd866553f4643520e249e0feea3c47ef088f230dee7403a596fd32b2135601a4995f268df40ae6d51442b62cd794da251a15ca8efcb9f35a096dd438756fe6fe845aaace485d2d8e009a28ef023e14ace7e09bdd3881074125289832f63a7a4407da4c2c6aa53a207037fa5148571a07c7f21689d21a38f508d919c9acba9b2300c51db8c0285432d77d155bbb2afebb7b2ee7263b381d9b67173720ecfa5a808d5623a6d5ecfc766c11865f1547acfc85e87e56059d65a4857a8f9c269de37ad06204a9b507550562581bc2ddf3e9ccbb3674303b92c4bc15cc5e601c1fe83624045fc9bc066752e2bdc6b175509e2cd8567dc419284f62f03f46ae1b0c2d02276fe28a37f4f6b116b5a7c64997063df52d3dc46d2081f09f74b1d5d40ad014e30b3d22590bb47488cca815377f5edfa296b5e3c3c92f6293e206bfaf5ad7c56488a04c1b057662c98186f631e289567d904bb2d508bac0d7a1e9aed43beee201a43dec9130d9c6aa2f4807c1ba7f1e68d85c82d6fc096402b7432e4e47ce7e5927abcce79be0d6a527b5944c568adcba0cde32c2db225a91d58c49f3b803f6b92a4078cc0821040a8c7d17957ef026398d97ce71688f168f601aaedb79fbc36306227c69d73651370c158e1d13cb64104ff7c9dcec9ea73ecdcca26346d82d70fcc44cd4e20c0da68a9b0931782be29ea391f4ceb19e29ba065b1dda8f00b78baf1e071d18941ec3b54c777229732c0a3e4a062cc9cde6d713c46d8a487383460074a2f95d43e9b3181b8e756645393ef464b12274e20718a14ecc47d6b5d785d1cdfee90ca9d44711ab06850244683ba8034a8ad1c5111f437541882ea2313646614687c9460aede690614b96c748da692bd21660f0b63691bb7b65eb8f355b2d92f4cc739e536744cae28e3d3146c5639fb242afe28af70ef664c22c472d7af7d1cc241330470aeba8214e7326e8a6c9df3832a10fed90d9567b364a4f744408542f431db45782155ae2b5ba897aac94a0e22940b72107ccb26b826b57f59d5f04319b9cf8f3d42a99bb565af8f49c532e8a16c58c388bb0f0d44b1725c12f3f6ac3a9a42157db72dc308d847892335dd913d48ecc2d1ce3603e7fade8c7632298c6a71c3d1aee483c82bb91c6da6a3fd1b0b0f07a218e16ec495d188464a0fa37134bc1efbf18e3e421aeaa3e82369b74b9b225ff4462bf0ce709bbb8c986694dd1ced204d1084ee483b56e3a8fba5e83fbb4fa8e23454c4450538a4948870e8f95391da679cd649c93076da9325b69a0eb3b13e952991f2c5dc68d5900b655b4e22cf402d5b6f220a1eebdb4e37632c2b3ef77b95aaf4ef660ba236d1431fe597aa7b00dafc4edfac76bb3c95fe80ce8ed707fad5927d42a25c2998d0804969487c90234b0184b96889c332740279c5eeb467e568624b99be4c2d2d3b56fa5ec6a633450127a2b82c5516de01ed37ea9a53053a553082678753f400864777efdd3c1233cc7c2c129faed1684802b91ab9d96c5c398e94e0245929f3d0fb7d1d0e045eb6084a5a2aa8960aaba65df98bf5b572632cd836b4fce506b6fc7a5d2cffe640404c1312b4077c81c4885da955dc2fcc0ed0af655613e2d9bf6af1b721c44e9a7a753634d988847a8f2dd53bf6ae35e15380155d22db8e57068f6770f750ef6cd01cbf166923b3e46b436172109695d23492e43907004955c2ddb91c1143f82f80a7bb91fe13c4df44f06199e01876a8b01fd564b9eb0722bd4cc1eb65802627241d03079bb1b281a5db81dca780993d0bc9e256743fbc830fb34191717e2b3edd2ec61ff1e8eed902261262d4db67c5d04461873288d1121897546cc2ae2c3bdbe8feadb3f2287480e08911f8b86dcc7bf28bc0387148f94436a47a173b1b410b6e6616a083b061d42c7eaf3e12ca4bb464cceed2528a17d433646c89e158933ad01dadbc2321a471e316e34e671e183e48368e6f894e2a62ec8ba5084f0bd9baeda5c05cc9a9ff9a9e37a8b6ce3a68ff7a74027d344036e9e350b4398b72232de379c33b459784d19e762cce7c89ddacffda67a508fcf71e599e2905e528becd86e17a9b0b122ca4b650656644a98c53b4688258cb27ced9fd133f6eabea54ec96c6d01867984e54b02fcaaad62a3f1e44538ad5e5e7c768c0cc8f93071b2f5198d730a60053d17438b26e6113f2c2d31e8be54e494426cc2775b0d9ad728877533a1dc25e86c55419a3623a64c08ba5deb8cb5cc668beafc5cde5704dfe9b648a39e8560fbf1b2966e1d7c6f535a92a7035d1088a0318b5d8cdc058f653c3f3163a5dcf43939651fbdb333fc8090d3e51bffd15b7567c274d31a777cfe67628fb5504d0262a092074e2181511846d6b55d8741c1188a2240f661155fffa99f28bfd1da14e457adba8de37a2baffcf0a3b157ff234c0072b72de09aa12912fc247841891d58988d608cb2b49ad6d6e4baaa59c0fe2c97ad28bbe415c844752501ab253f45ea4febccf8370bb084a2249c7c81397ae182a8b7c3b4552660cb9ac26222911902b993e24a9b53eb610aa1344305b0c8783631df7883fd1f76942b844a2cb25de4beb6e2e23b84f657d998b59acbe488ee998e27ce1cc9334716f1c90331cead8a0fdc729734353ad7fe7e824f0a92f7f853b774fefe1657b31a855aa22ac07706a797229e250203df4b12415fabb250b6246db6c3bd54bc3933bd166a1b77a6b9e29edcf54507cd326d8fd503e2e8461dc62946b139bf62c23bb53af7fb1d1e9506df64519aa6431b2e30bf9512be8b4a58823e9e41af13eeccb51edd0501e06fe9f5817a0732ea9b7dece49c388ededaaeef6fbe5db716c6a86ba60021a8f4c6655ae35866ffaf820f849ee5f10679ebdbfd5561498fbcc585f3212f309cbbad063c2f48573b9cc83032832402e5e1475695ae60a060b9e8d74dd9c5b0807804492799efdf8bd662f2f0826c51134c942988892764c3e5c241126a4180078b5d844da995b7a6b3cbcee505a2fde17e007c76e8f25107265c4de665c1da7b9d9e04a058afc78ca9ff14952eed2d2d6f375487d4ceb8227814ff234d05df9fdca4bea62b0d190112563de7434732dc0cd94178f197fc4215061e6384f104d7170c599a337f772d5a0bd88b44cd071a3e5ddf8af7c4c60bad44a10475a2aad1552539f96b079e2cbbb94bbf6417a3eb222f196f420d47588684a93d913034200468b4c853d8366a83288906b17b52163874069747c84b31c4170c4012f50f73c8156bf3c278a8c272c0489d42b17d1f2f76335eb43bdc19d4e4c0074bd8f1b49e3000354826f0d65511f395ce8027d93fe64f0f3f2fdf3afb16b1e125c8ed221b62c9308c8a50c5fe9444746182c0a8bc4bd19c1de2532531afa34760395788f65a827eefb2e67ba088f0e494e11375b79ccad06f97658e6f91641d91a8c2eb8be5b0df4ca22647f470dd76fddf8982871fd636c5c7daa838294ca2b198908e9843880306fd28bed6f1690a8e91b732153103010b9102e91c9105d104610fd90388a8091909feb5e55540b808001812bd9bd42c4109e9d27f9f638b15f6894a0d3c070c52e4263021d0def1d8d7ef26185c72f61880cef22684d945e78230b51e4eca0562410c409b51e4a24b298f0066ee01bc4fd9073e2bd4b586f18437c1c84dc65b9f20d42149196e9dc5a6043c85b6854484e859e994b32d6380ae2885a851219550fa2533089c81d2fe03514b45d1efe4e6a3153510ba0db55f5d8a7615662cf3f0b33a4b57fa3d490c3d0be2dd6bbe0a5462cfdb4d51e6573961c0000849cf4a63a03eb184430d8794305b14bd64be2e2a3c4cdb7546aaaa16b6a22cc6a6e1b70b1091d6bd6b1006c6980d9ade93d65b4105ae44c95bd17cff87a9696693c3b141eb27d0c828ec86183fe41054520e3151bd8eadba11f40e005e5862f64199610d7264b5be29c6d12c736aa9e85c4ace3e5f082881c9794b1264634f525e5602ab5e7a492671e6d9b8ed9143c9ecac72b2a892b81c46f27788a8f50caef547ddea6fc712fb25003291ea31a4d39e948b1503ba82262100528e51704e7996b6054de0666d264698d4195d1687032ff4a83d08c1603b205cde63ddfc9dc947f20173c2abd5200fe79a8e57f73d6284fc8e1e331a699e3d37f7dd8f1d231c09d075d497f0ce70021667d87487d30d51460b64455e6a34894d4c9a92d8272fd423d9d9aef5037cf7edffbbe9d42bfda77afc3f83e1558ce50e699b4a3ff69a855ce1700c05c47c188067bbe24c898a1375ec320f39cc0d1a720c1becb9b25186bc97b7458f1abcd52fa8e0b8f01568df7a0c14239c20bd58d7111e060054c6c9df25722c9c1605be8f4524c6569c321604c028a40ccc9f2379260d6324fc33e7c9a062e2a3ba5cd7a62df0523242871c6b0fe0708d085e5738d3206a0acd2b0026097f9474423575cab5acd5373d4aef08b57f24fbc357d6dd53f6ef864f26bf127b0f2be1410a8e60678fb22d04006d27d904638cd72d07a92f8eb15cbd9dc445ac0a726a1e6670b30314339b6d0733247d807d1920c0449e78e4788a22e1d0f89a0a3c563e8e35cdf64407b2a9d86fc2f292423e5bb09898ba0bb46df4f8cced86f5b24ae58fbf875121ece5219d85c0d5ee65714e2bcb5602458d0d84631fb97502dc5ff709d38b5aa2f79af7475e097208b455cb4e8e073be1cbbd8601a2f926cc2ec72e0ea2f33e7ef75d19a3bc52452985db4454ea603aa0d54d192175f20645d93e78032dd87b0ec83d648ca560f1d42586489e83b02dd2057638a8c018b558b937d4c4d0b0a3e28120f58e1daba65673d6480c25f373f774e4bbf4e6a56ca087b5326ef2af02d14b5f8a073105b058c781cec450614cf071d6586297736f4815b0e8a03f7cad9397049ec3b68c110ef47f0825ddfc9fede954ff8d5bdceba33b569135e0ebe8d81211537102daaf1b9485d08297927a8b4e62c30a9606ef5a39e400154dde9f5fe5c83275fe4bbafb5209e7184a6acae12073ed621abe8025a824a1c29e4b66bae2d69554ee0bdd1aa97c15d2ce46614b32f923b44330c69b1beb5687031253d2abc90fb417163669c4bdc71998e205ee23bb1e21f85f520d443d1b44d7777414639241b871498b39463f46b40ff8c62ba2416735fd712a39316b3591f379eba7655ec210f45a72acad05bf355cee0abc55604b339eb6bd7446c3f5cfc22ac40b3e5eaf86b28130f311e250b7d306b4a1796e2fb7b0c61fdb82e939c97829e49a0b794c54d8669a682c7f115002256c74196a0e3a3ecb392a3662a8dba6b09ffff68d23165f8a3cb43d21ef8d5b95d6632be8c5df0e5c8b3c0c5c3cfe9991071442dc8ab34eb2a39183c571e732d49c41dd5475ddebdc62ebfd9dfbee38746cea27b9342080877ed1e0f3823925462dc2b87e26c8b73663cf523ae50cabc7aa89b44fdd5f7021ff1284075e92759452058dfe0519387bf04063553a6c68ac4f6074a276c661e5a64592ecab3f00a29e61e9debc1498c1e715c5cc84c1a41498c527b05c361c4a69121158df6dd911a3cf4d9ea889ee84e5ea481705bbe339b717500080d3db17344d0b144d4637ae60c0de36fbebf509ea4881e67fd93c9b0cfc7b5a727549dba72149026fe397b5d1d2d3afcff6eac4918d32489f07e309f2954fe8eda8032a9c1da5685c9aa576746f6552c03ca03d5a107e52b1db517793bc44562e195e25c63776eceb7d2f2bce912b4c63ce2084342c2e35148ebcb1c14f556f0f0388fc47ae1f9b41175b880cc72e9e694a2e53473601d128816ede60c9d4d9a8b8a8bb5a96dcd9bb3ddbcccf0d3eb8be3a7b953dd04a0946d35bc864ab42943760e99262d19b36e5affb766f0901dd865c06143160008aa85ce447685a0d4d7d8e6a3d97385b0255ab21ebce9bce4cb6ec35394290f8164a77c3c6d4decb7bd0e6b0277a42f329c544b4e531e16e3723bee82b80330aa0866a7d45d15bcd52c5f89b88274cded89a828ee5a454df0f4b325a385a492911262b2dc63645c474b6d11a9a2548c8a268956b4e264948a52112ab68c158b94921332e4188775d38aad72565c952a4b7740effa503b32d015b20d141611456c9f08f7f22efeb3801725a26a7d1ec6b31f7b23b186750abd06a43691a70d193f1f08a2f1de4f14ae902ab4a5d5a1532644446025035908b29e4d1d83d22198286146b3cbdf670e2b90132fee30fb0a5f299bf0d57af9f1c34833cea641b89b4a4925098b4cd3b6940b7445ec5fae42988f9a58cad6f84095f50a5191c2f01191852e6f37459a04c18ec349ec4497cb4043f720fcb6a3637e2343b249f9a626cba9429598afe5ca5040ce085a4fb469495bb228f0874d5436400d9e4222b116ceb7b180e06a849654758b7ad1c6c4285f3fe22a0be4d5cd6e0572f34391fb996fa1fcd91e986e27690b7bbc8a44839c72461c8cd5e6676baa9b6b995de66e7ca09f224b96b013e80f4ca35de71a97cc5b1b47faf607e1dff51b9884492f23575adc5b8783cb315975aa5c839d41b9311eb2570d8570dd9926cf387e7fd36055b39d63b7d5a690b564ef91d57e9b2feb647d5b307bd864580f6b0a99b0e3a696b43e272f30c87c23cfa08ce35a07996090930b2991ddb36e8c8ba2207351ce619035f37dc7580b88ee0e6a790a9f84fd0d65fb23a800ad6abf9be81111396a54862562238f9700ccf55b6d4f04fd10b05c566d2e070ee659ec914682d2dc3a0000b79ed1b37c58e7ac0b6ab658eb488417e35c250f5a3e98763285a7ad9284789026879fb38fec8739b38079aaf74ada2e602bcbb9efdcc4a8f676c26048b70b293ca34a63a9d15e07ba60c82e499f2282900dfe2a9aef5154d299e45a1fa818cf21529f82d559d3fcbd6716a2938e50958ce053fb87523f178667ea891b84f7d1f7b1584c827828883f245299bcdf2e3e81a6ba96dcf1850e1d0a819cefb68d65e2e624758c012aa1641f44e1a2fca3aab05a28647c93b8f756344b2d0aa0b11a4492751b51320871a07c1aa28588ce98eef944e210e3bf78c550a10d868e6946ac16dba580366bc2585b75a97cd85e53b0deea2f93695fcd9c5ebe28d694b41c71270d73f6344d6b43af2243b544f71cbfdd0b3dc29b61ffd45b509ac96ab9e29268f9a0821f0b73284183bc79173f2ded6cecc0ca79edeaa283bdade49895912bee329cc94bfa73bd65a84424e760cb9bd2f5b1f570abaf110a197c520146a66c18822324e4fe04b445131d4f473c089f49ea8dcd65192e9db97584556302f0805b360f86d1750c85541a83cc74bd8da45a215cc2f316ebcdd9207a3fd531d50356c251a86a2e2e7c53fb36b97f7d4254ec42076a1557991b95d638d01c078ba60c654458f6e6d96c20bf0cc91178c018e7e1de5bb1cb0ffc624056bc855d26538216daedc5582b328e0c485ff04cc97228b8bd6fb351a030b947bd0600cca5364386cb984abf197845ee8c14659ad5b870e79b6fa8914aec68ae4b205b15de4ac26544c46b41811dda3b6261b6a975e4bf6c95d5175ecc8cf6cc7f53440b3af2151b3863b523cb3561b74a7edf311b80a05da52576a981115f9f829d3ac6691dd8f2d5ec3ef1316c11e375c923863c0580547157b18ea2bfb3b1690d4b1af01a7039ef1382d47fca7a422c52a36906e025a5e0e457cfb2665d353792f5f981814cc348dfa77f2e2fab1da47688ec6f805cf99054f978835210f6f8c17ed32cc2295469c3e5787561722c32405aaf55421916032784962c3b5b0ef8f665f6237cadd8e6b7a277030069e81bb96faf7800b5f3054cd9a13b5690c20aa65ccdf4a09169711a2e1b847a91c91c2a8180db59a894f838b7b4accd862a5be076b655e9fce90918c00099e7ee58a4f8831e3d0417988e9d96d27ccd1e2eb641f4a5a7694151adb7571b1c68af54f4d6df425c0fa97ab537c28ecbc88192884116e3184ab5bf932655d1c33dd771012b1d5799061cf6ce6d7a5497424e14b5bbb72cb9178c6d6f932de03a936ad6dccff70c0d7c735374b841350f1d940b21d052a329ab8f0b198f49ae479afecc25ee1d7204619652657eb90c54aa554af552242a4cba859e063d752a722b6c8339ded156a920c72541fd61fbdc94b2ad19b8a06e34d9d4664017748cc6580e65b2864884b726d8a3e855a2ab8e57448c29f133a2030353b48102b36450d6b319c9a70151ff0cecf2190c908015bd8113ba5a3b6dbe1d6e83b835be598cad6e3b7d2a5205c6968a7628d95b22ef0513c6cbb289755e1931728e8ce1a157307370ae33e09d1f340acf9b4d0a125121fe56686c1825fd87964faf16e39641c9251ef5cf568d4112b59184a2cee08d579808795cf134b876d816464bb16264a5d0ded24a8ef960bc91a059575f94b8588960182f454dc1fa619474fccd0348ce659d748bebb9454bf57167fc2b48b908344d38f06fc72aaec8df82b205fc9d24eb5816a4b9990263367afb528e158a155dd7c18083e843555bb393722c642ac420e233f5f2a3ac5795a6dd9c7d74accb69e5dc26200c076d877eb9dd43cfa0664a1a8e15e22e14a544ba7ee16b062de90c0e485f1baa77e4803422ab3312b516f2af1c1438b516e006b1a76040c9258e24a45d3bca809eb0fb8fdaff0df156293f7662a1f5d13e3de8388825c290e0fea6101d025116e1a2464e52e94e57d05e26958520573c2d5ba4dd7b66aef1c3578c6de1795dd64bfae0d0a0b246f290ff6edaa2aa5e283007213ae08c03a31f18c72d080aad9a53326f73c13ad53552a851c2ae5fa11bd2be28e3193593c564b2a14854986b289fe16fe34a0c064e08e0c8cfb144858457a381c709f3065bce039db8ca6fd22409f445eb237484338c62c3a0b4a223d0630b21b7a0ac3e8f4dc106a10cc890a7e89c8350885ad4160b16359ad7e2ef099ef4aeb3e64e5582a78b2ddd1defe72e48bec7661a6cf0856b65dd0b69606271c5c6cb6d66ba6d14461b060b0240e150b29450625f44384c16784945e473a382b2ed47e19cb9710214556d280891d1723792229eb55b05bfcf2ab230953dcdc8146a345b724e65caa16d0bf536cb25da840ef3c12190456b261386701ce3fb49cc45d1e097c3905e78af94da0960333b1ef5db5b0d225d807e6208b6b967d86a9ba6f77e30b02f2c3b33c6a306567d6d1a4b0b9da92cfc464ac3fa4eaa036a3b46ff1404fdab21f679fc0eefd22e7114826a7753385b05bb6c88d8065ec6fbc75ec681bde38acf274d1491edef0d2e3f415bffe7e0456ffdc13693a7719fb42588db2327f280f8c55a3a3d0412784e5648e3120c3630cbc6821f208552be7267cad74c45663c90bc5a72bb24404791278247971b9c731d8642a2486f4e38932c4f72618c02755dca5748f6d44a3f85f60a939ee8412eefb632efaa92886e53ee900ca3a56beb6c1be4ed4415811e6fefdee423005f14e28cbc42208be8a56cb6148a4602827118628b479a9a8ea3b9860674c30d60268bb2051ffb0db0c1a8dcad2054c300d2ef1034b6b3a73e3da0ef127f15b7219950adc3c5acf7a346d9447d9d454248fe51e9e621b9e7c0c44cb246275347d46d9504c31409bc228cdce17fe29aba8e9e4b92cad612089b06a91b7f6ad3d9b261c3ef60cd94a7e5247e4dc077edf18e9b793b68ee9c2a31c24e4f82e3f5674128dfa567370f3a34f8960e1852345d31b04e4f8d2dca5859d1888d18f54ce0a96e77a947d519a065909d5af39f53cf73aa0edaaba690ac89ab5630431a7367ca15ddac15b105cfb3b0b618c7ca20a11f941be889e0cff2185fdc11745aeb5d6b5006cc5573022e4b04040b3340b2a4d001ffffffffffffffffff51dbdab7cdda07edcb94361913500c45c8fc84a494524a3225f3f43338ed03ff151cc287100f0b6c0acc0a9c0a4a3c39e520913005b1975fb3cc322c830308244c313eba69a8dccfcbbd8d1c3bf011e6d2be8138421fcd0a48234cfab48d560a35dd184118614a2d25afcf9a4a005984f13e95685ac642375411c62f335ddacabb94249f236127c26cfae652e724bd5423c2a04fe816f93d0f953c8449af6b9d7e3fbb3de11a6b6608534a2bb18e4218644f4a2928112a4ad6354298c5d664cb31ffaf837c076410863325433b5dc97111b7204ca7443b09baa31aa2a1c3c659208c1672f1b1b4691d95c510c306093c3c4000e163d8e0c118653000e40f56ba1b1bcb15d6b3933c32727455633900e207731284aefaa42ad907a37512bd7637ef1b678d354e3bca28e38339bb47dffe391b6d273a6cdcc03d184b963072edf43af4600ea192126d9ec44ea29e07a3794e425a78ec4dabf1600a131fe127b37b01c81dcc694cc3afa46d0763c979616445f4b8b97530cea6ea8de97e3c9581d0e1f5d0cf5eff49903998c457ebf23149fea58126079356a8fe53e154630d75e36054cf16173d0c07a35ace11933ebad2e31c9c24f91cbcc1a4ed4e9afcecd458d33c37984a94877e0eaa4c94688d353ea381ca6a9800a40da6dd33b59f4ef8874e52636dad05206c30de2533ff931a77f7ea5883a9d3785e4b52b2c6da0e1c38d46012f48b891e9583b6d4c100240d465136221696e754856a2c1a4c65626f27a14c0cb5748d353e83e12bb89aec5945c5e866387f6e85c9aabf0ca6f4222b2749b2a5bd548d35268341c7c9e1533e99a177a200640c46f99c1dcba348eb0a0840c46092aec3e4ae5ec6581b02903098ccf2e3cf9a60305a0e5afaa41925b8c67702902f986543081d4a3679269ada05205e30e55c363d25eee79dba60d2398710d54949275a88061607e142524992249f166da9b1564e06205b3028f5a1c4ae534dd18c86164ca2a75392f44ea22b00c98239f5ef4df2144ae49bd052008205f39f1484ec7ce92b1884c8b7bd93a3493a9605b142aa24bd566b6b8db5260148158c164fcf7df0b7a01d1c62141a89c60e1d386ad0d0c3c118367080031c622c0e1b34c83020549802881444001205d5dc2c2d2e68c650007982c9d69289fda4ecc491937143043ee0800e1d27051e1e1e06e2049026f0f5b2d9ea59ad7de5a9a49caca28e594098605e4b39f1e2b64936421c3bc610c1128c7a7225ed4949b224a5b59192083ef0811b3a0e0f3c3c9293d000a204c37aac279debc2824a27c1fc72929e58236d4f1222c1ec49b40e173a7d5ecf811cc160726996525610239837fdaddf336f56669022185bd3737dae83502d99004204833efd241ff43f0c934a5236b13bf51fc230c91384ba1065929069f90886797432cb59a205184693da2fe845f74e3afa8b362e59b8d3b2e02ea79569a1ba7b94245f18f76227e9e7a504f929bd30858a551a2127d5a8145e1867c3cf73ca92f373ca2e38ab28e375156bcb2aecc7dae5445329f87e125d18ce5cbbac44b53993fbc8854994d8a13d8f1017e6ee720f3a9c9494d0f716a69157d1644b96dd45b585d94abab575d0b072b7166619359324954db4307812d54faed678162635a22bc927e34b4dede1f139fc6561ca3ff34ea243dcfad5217cc4c268728553dfeb32081fb0309b55884a2594685d6b03c70bf4c6c72bccda26c68937d1043cf870c516541cd3a7726cbb15f9874e95e7da5167561c941c9ecda28af26e15cea5301d46ffe9b54d15a588dc76aa6859d20f13aa4a3015c60c5d7a266eaabfde072acc56264d54b8925318ae4aac1353a2867dca16e1c314c6396984ae13b73f4b45c34a61bc3b553a4d1e5142ac1c7c90c25c794fb783fc5f1ea9181fa3302555fb1dad2b4de5134e1d3a5833f0210a639e94662c66e95c521fa130e5258d9354feb9fd948707280c9773dcda3dd9270c4a56ca75675b269afe3e3c61129f8451fa5acadf549dc8be4beba2ddd55b7795c897ca27459c308cf5e7ec9b245ff8d884b9d242e627cdb64e0b1f9a3025b9b1193732f7b3c52b7c64c26079593d688a8962745cec122653b75dc1e4e5e1b13e48ff6c0983d8b8d87ae2bb9f302b615eb5bbd1159b1226a9aa537849529e24a727616e75b5539df4ee644f12a6941dff4818bb2fa5e52d25244cd9c3d37e28a5728aa347943b2b68597585d3d2b4a81764e57fc711e611f6e5a3a146cee4f8d10853126bf4e8f4764985cdc3e3ccf0c108d309b5fe12be63624517619e37bd22da411f8a30795a2a31cf4e5c35b59de1231126b95f9232199d0face00569f8408429e5131d174eb6868f43184fb549b765db5542e4831ce7868d317c18c27057b9c2082587fd8b3e0a618aa154ceadebc53f930f4258f284910fc2706f82295d268e891a0bc22073bad74559febf1a0853fa649ae35ea1acad0f4098dbca53e737753ae8fce30fa6bc999db42c49a2d33dc2871f8cefb97fa4c5ef9c242d7d30c99e7225a66fdf7eb4f9601c5da16e4d5dec942e34ecd159277cecc15ce174f578aa1ea99687c7871ecc63d9737e0927472ba93d3a280a1f7930bf86be7dfe3ff06034419b7caa2374b4943b9852ee6b2b436c4c6eed601c8d3dcf595475307c7bff9c7b9f5813a5834149275cb4b13ee6604e4f3988bfbfaa37793918de7c4e5bd5cda76012076399d63ad3351ccc2147bdf6760755a27c834996f586851edd5070b93677f98a9975bf261fbf3698f2c8508230b1e4935a7eb0c132abe8eded16cbd2ac429d7acfae269558ca640d66ab8fa5fd4ff3f02864e8b8fb50834938e94cc67deb81e6b317039581c3fb041f693098b55df4de133dd2f001a281056d061f6830e5ac8b122fa91a6b64dc484a0e14887156e0e1710693eecf9f36fdf81c33182c087595a44863f0510693e8e059e7549758aec8507e96d8db95c43268ecc08673c8c7184c4abac505955de2592f06f3996e379d4c4949e7160693872c97bf1c7dabf5533e3ec0603cf95b2c4be835d6c4d0dd31868d32706003ed063ebe600a71c13e65eedde7bb630c1b65e8206b7c78c1249a49f6c954384bd6415d3077a55ba7f315b3fcfd830b861be1a19b969ddffb8d2d9866ee533afd785549d00588850f2d983e3cfa96e0414f9827b3f09105549714dfee52fec082f92e5dcaa93ae90aa69c277d8e118b3df15bc1687eb7bed716c382f75105a3dce778b1fc2413d753c17c296a28e9659f82f9632de7b8ac5526280553278f6619da55b9e947148cefe597b2e9030a0661a27bf9b59609daf3f184d2725c4eb9d0b69469e1a255e4fa047d38c16cc92f9b0c11237ab90bb80b1f4d3089273549bf2583058d840f2698e4c7cae6e9120f0f2e7c2cc1a442c7b10fa13ca77760c369948123070d7fb6157c28c11c7663a276ba54eacf111a49f84882e1a4efcab5d55c3b291f48308befb6c83e29438bd0118cf717475edcc3dc9818c1685f274bdd4b95a0f647118cf932a69e2d2dc7b67c10c1a4e2e7fe938354bf5c0e1c63e0c046d9304c1fa2efe4b1b41b69aab196703c8d1c38cc84a1fd67939229cf493f9f033d3a18e612a57c7be354cef7e06bd0d81c0d0ce3c8a9dbbff53442b5af23c721a36df42f0c6a9d6566df22d4ff3472f04f46b131da1746d74f3749dcf54c9e400b40707e059fd81083051e1e7e3e878747752f4c42ab879ea8d331fbe585f9249d72a5b45cef42b78e51f1144b2e72e326cc83c996433c6c565d98459c4a5258c9615684395e478e9382b3f7cb052a68a9d3233e19376c7878e0b0d1b8308f5b98c7695b98472dcc9f44fdbe0c352d8c95837bbcb4df2c0cea54efe7bcb485919e79cbc21c6d6d776ca45d4e158dcfc1b951168b45c3e26653d52b5997c78c68dd176161d2aa9676667b8449a219f42b4c622669d3f8b25d61f636b9a52ae9db50e20e742bcc2ea2935f92ff4e6c6d5c192b6ad0aba0d1aa30aa9859ec5a4ad2ec9b8a15342a160f258ffa14868fab6c262f8ebc726f0409294cc193789cfb2496671426ffa815df3c278d4561d6be14cd721d0ab3fb86b79d30280cbb1a3aff423dff9f308927ce76cd5d67513d81d85a2a497ed90993de3925ca09396138a14a97286a3d266ec230b2dde52a78eda768c2a039dea25dc98451ad24b14d0e13d673983049aa593ada9524e95dc25426b9698dbf7852f7963025937b74c931ea93ec2b615c913fe9b2f69430c8adae97312df2a7491864c8d36162f9e82849c2a0f30925679c226178bd14aa5ae4ece99030d7fa96fe3919df9247184f4ac1d2091fd32bc81106f9a943fefd4698729fffa829ffa03a8c305f8559524927b5672fc22c5ba37a4de94f232bc27049c79d1225ca25d1449892ae1acf96378b1311668b25c5e7d31a26070f617c1344e81acd10c6f730f9de2ff7655808638ebbc54e3a69890a214c3bd77d927b83309b6da5c91ffaa73741983e97ba19f19e82c80261fc5075f29a00619273e4b4ced208d11fcceef5726fa28b6ecbfd60d4b1f39b37257d30eba9922f5be283d1a37a6a7153a1b1efc1e8b167f23d1f264df4601a157c5fc74fb414e5c1d8b19398167db9d2c18369b7cdfe04f16addb983492a41a9962d413ede7630c53425c6bfc9aeb7eb60f4360b1b3a9cc7ce743049522dd9e87251da9d83c9c7cb6d3cc8d0a59483f9a48f8d6725a54f308983f9acf42529e7ba9d96c0c11c274c586deb08cf923798e2a7b648ebd0ab2b7183c1deebb2b5dee4d1b7c178727f121edbe48fcf0663aa7e1215a4589a246b3087aed3ad38174bfca8c15cad5a925ca72e79380d2679276b0861a73b321a4c6a26c896f4d4ebd933189424992cefd3bbf79ac124fbe74a124fea11b70cc6b558a3d39d49926cc9605069733be874694c3a06f3c5d0255552c560d0be6a33b94bfea40c8329c931a98399b42523180c172ac7afcae215c62f184dd6091aeae20573a938f92e899f59972e18c74ddb9392e682b16333b7b65f2dbc2d98639bce754a8fc99e212d28aac4ea9c9414ca82e984f5dbdbf8981f1618f149ddae770593b0ed93b5d5f52b59c17ca32bada25f0593202ea782c173c4a86c9772673405a3b7578d2993b39f470ae6159513a7e72898edb2ea6e7b0eea562898a414ebe27a3b74e709463349f8befc49ca5e3ac1a0224dc44ce8dd49698269be644fcceb30c1d8fee59e4d6e0906b7b5dc2657824105f350b9a3fac95fd2a5c4f0d5d691601a1343958ff224fa2318d7a49ed27be249b311cc225a642d664eaf49110cdbf1b2441b2082a965946c987ccd7d8661521fd9fb310bc314ba9f82d06e300c4ae73be70e36304ceae941ed75fa17065d5ec29bec51eaaaf685f9353dbc4f3e31e1d40b83d09eef463d47fd122f4c6ede229f4abb30e7281152dfe35bea7561148f2de2d299bc26b9309c38a3623eef58d2716190bd94740e2a26e7b73089271f27f76c9e1c5b984289deab9494a0e3a4168538215a74f9af21dc74666152fad1439778eaffc3b2309d5c4a4f28d14ac99a6361924fede734f13dc4ccb0306988e89244db3d09f91506997982a50e3acc8bec0af32979abeea378740bb7c27432161bdbf1e23f668559949da07e4fedc98d5761f0d85f61b6b12a8c66b7fd9ed4793a8b5361124df94a173ac44ea830e9bc2174cebda6bf3f85b1f5d2b6ab73fce2a63049e17379bcfc452d2985c1d3925612457b3e8714c6fb24a2a662e8d8e951984afba6e9a896e26151984dcc6c8f8796953714e6fe93163f545098e38890a74ac5954c3f61bed15352b985d2997ac2e815a6541ae9a1f7ec8441ccd3d8e7ef2539e484313fa86b988af893b209b37934b972ce7dad451326fffc1e2dc9b75b2513c693544cea6e3161101df76e2cc66f6b7b09c307a157d55ae6165b4b18c5342b6e575b09f3c9f5cf494277f4244a1854d33c45578df2934998c4dd083f3d494c9648c224e79294147ff6a1a248986f54ba7612a9322248984d959a7ffce8234c79fdac93fd59298f3ac214462bed98db089387fe8ef4bf7bbd19614e79cab29f1026e9781126196276e76e4395561146cfe19d93a096a6dc44983b8efef4d34b49a528224c79566d4fdeae123d3d84c9a47029a7673a7f4a0d616c352fb162e1494f8530ec499f922949cc8612214cf395aa4e272955f34198444f5352b092bcca4410a65c975573e440989429d1d9c4c6e70a02c2e0167aa673f433cffdc1743aa7e7b8f7fbc1e871c2a81cfc3e984a9fbb5ea9124b9ff0c1a442cdf8c79cc9c1640fa624ede8fde59c28213d18b5f5b7848abfadf3609252bc8b9573923d45f1600c0b9e3394242a32bd83393fad4b8ea71d0cf65fbad4899bce631d4cdbf61566844807a3c9cba2ba2784bc0bcec16039d485aee594e5827230452d3d1197611c8c21d74a8558c93cc1c1fc995de1498963e26f30a7d2bcb653b7322637982fdc961c3daa93ba0da693a4a062e3b3c1e46aa944bdbe06838ea272c84f651f4ad4603a9d4a5a5c57bf1aa5c1d8b75e42754b3446683086297964bceae84f9fc178b37fb72787934cd80ce658419f779bf8f56d19ea959155eb26190c27ad6c2521945b07c760ce26492943e59294876230898bb65ddab5245bc260f0942264e32b603099fc9e27a712d47a2b5f306d8a92b564cd0be631938b5de2739a5d30accd78a5e8f9e552850ba6a463bb5ce5c773d2164cd17faf6543e54ebe161c13e24a84e85930b52561b904132c1855f57412f4be82f136f4dd7bb682c994fa24792cabdcb80aa6a444cdd968a960922cbdc8bdab1cf64ec1f8497cd3a14499c9492998e5ccd24f0a2f0ae69274d0974d2c39e8efa0600e2ab3e4ffbd53b47b8229ddcfe70473fa0d93562f4e1c53134c722fcd4305dd9d9298603ae531d7b4f6472d2dc164312a5f36c92cde578239098b9d3be59360d05195db4912fb794782b9e436314d253549cc8f60ae921e23984dbc8e2d2d39e92298e7bc926ebb35400493ea9c9b25499ea4131e865176fc5a6f45f8bec23096f814de540ed7ef06c3a4c4f8e5b91418e68ad721f6dae92afec21c4c5011ea74e60bf3dcecf75e28e969b35e982c9892e4cf1a2f0c9e6f2f469e1445671746713361d2e4b9a095e9c29c73f4b4123b77dc5c98c4f6cf5450aa9368c285c94ddd5daeb5e0b3dfc278325f9432496c618c193f497ea716a62bd9430b9394e409aa634791a69e85792d534f5c0c6d6acbc2246527e59eafc4da3a16c53d112ccc39bd63970ad1d22b4ce1db04ff5322d6e20a83da5daa133ae9cfb81526b5f5f46ce9c4cf9215e63013e7250517f9acc2242f594a19f27bb4aa30857f7193b62a76f052613aabbcaff7b658428579c37e4e2e3bf1ec2a9053985408a56a7e4a7c535ac282278098c218a3439b259d7545dfa530aa9d2a2528499614e6718bf96dc2c74f9c466198b9d8271e2d0771368828b074234ede791e2414a6a4e4aded3e0f3dcaf262a05305105018a4880ea7af832a9df11326292991dbc9e48bda314fac16aeabc28a5c0e75cbbdefa93eb21a6261274ccd6a4b163ddde66e2e5677e91fed9d6eba7b018413261f6d1eea79e46aa53b4036619204511993921c73d0a0b1e371900347ba62d79a306ca5f9e9106a8d35342e904c18363b64286d2befc191209830850f99eaa626d6a7cdbc8441c5533c71d23f081d8258c230d7da6d9bba086ee0c8f101b41d941c366890a103f700520953ce9ea07277892cb15f4a98c24dda9a947f12a6ca503a4b5e93244c579b8a7d2514020b93204eca5df112e49fd22b0c63bdd5bfa9224dc97185791cb6420db1b895bd9d0d6185495c45286d411cb20a83ab473b191796d524435491b9c6cba57bad6617920a539d94546fec8bece4a3c21cb236db834af191f914c62c8f7721a30b318529cb7df5ef8d4a61eee429a824550e290cdac43e6182ca25e5da46618ea5325ff254c4bc22428828cc55fa63ce4913cd2c2914a66a8fd62aaaed3bda164240616c0bf2629faaa421e413e9894e1846899e3dfe623d25c91a6b3bcecd08e1040ad98439e3c404e110030734c6b05163870d1e68e2ae59757b7b17adca2c8d4dcd3ecb67e7f3185f43471964ac65c2ec597bf7d9b36775b0c69a8d622308c184f9043361dab9e48841e3737076749b904b184c7f955c63828a250c9f3c334d3e75250caed9b762b3274b398450c224acba4af01c9a8439fdd7c97353265d724324611cf92d8f26a5b9c51123615ecf5d9ec4cab549d2658440c2145465bf99ca0e7bf2904798938e63331b2a58678707218e308ff2ba8f2587ce277a84904618bbfb3d09b28475fe890a218c305ac5dbef5e894b3a5b44882230118888cbd5f2e9d2f9935cc8215071abfaf7002c21c410e67183904298c711c2e899df265c86ec8ccc46c820aa3399f54a91ef0ba7b684755c0f4204611893ec6dd3f37277bd0b84f1c45fcbebda3b407c616408ad5fe5dd1f4ca14b647e30fea95c7292cdbceb83b1d594f49177f2f192dcf1c1e41eb5776486eef660caa7fb52bca44bed8e777a30a7b314f772d0eb336e481ecca34caca4c604dde9a329383c989358752525712785ddcaddc1d42b4a50972127cb566707f3497d174e9c0815c5bc3a18f64b5e5a9354e5e860eed49bf326c7b424e9949b83c1aa837c52bab46bfc0b918326a3dc455a546bace9b09197829038186fc4a934daa40f0ea6b5be509efd69f7a37b83f14db678d95343bc99e5dc602cedbc921e4625b3dc43da604ccfa962b28d3ced710d6183419bd986daf4c610b206531cbb52a1822a253ba506838c35e9fb96f71742d26092be737e34157fe6e25c08418349d05b4a49d79d2443c8194c395f3459ee67fd2d66216630552541ea7d09eb2db98494a18490c1383a429497df55563a86e291831031982b7b8a5715461d48418484c124c949faa0157f08184c29d5be6708f982a92cc712c43ada56aa663a42bc60fa5252647de56bacf9fb0d1c39129f0eb3414817ccf104ef4bd29987102e98e2bb987cb1be6209a58e2d98b6d479e92c3f440b9b453bef2c4bb26b921ea1521b92059314b6b3a9d8cae6af3970d448c6389fa3ac48108205932c1f53bdd2bbf5c3902b983dd977ccd236ee7d9f60063470106205b3575fba684a123f992e86902a9844b930f5df2a71fb0fa1823955adc55a25099982393c55fabe90140ce3614c1a59898241d9f5afe9a1600ebe97af214b4f3005515e416ee4844a8ad539bc859b6092e307f9a5cd6782410795a12d993c640926419a509517528239b5dcecbead6e7892604e56f66bf27ef04a22c1149bdbd6c14e5cb31cc1701d64a629214a2ee64630cd251f934a8a7a9914c1d4f76ef183988d168510c1a04bc71813ef30cc713e8b3a1d4a5f528561befc2154d007c398e9f183881026426018ac840a7a964b7e619091a7d7215f187f2cc583b43659fd5e98e24a45d73bcd9bbc30eb5c72cb1eda8529943835ad756130c1939b9a3ab991736190a332adfa45b830e8b6869b9436445de816a6f5e829af6f0b939c9fd5222aba797b6a61d836b32a25589acb0e2d0c7b22e45b4811aed6998549864e37499f8a3d4ab24850614b8832752c8c6582ee9e64e3a24b5818bcf4d77f986c4b955720c7f2ae3096d80bd159eb56187e7f2f457e49b2b266854949e1e492bb64152653d124514dae33c9c3aa306865c96151bc63979c0ac3fee5a8a44654984e5c59322ddf2d1e9fc224f3d624497df707159bc22426e6ceaf782c41a9140659a3044be984c95393c26022cfe4d3df8ec230dab45b25917752b6a23009f774b1846f43613215cff944bdf594406112f9bb2b96ca5452fa84294486bb8ceca8be3d61101a5b2e6a3d4929d44e98d4a69da493977668ca0973105fa2c88d36612c179d673e7874cfd184513b5a6fe83b13e6b294a4fc7e1384943161fab951979dbd84399dcbe82d614ccdd2f1f2323fa795308f9fdc62a29430cc9d5a2d53cb8c4998c43c31badf4a499835eef3c793c748983e898eab13724d9b20248c72f721c4b5f8088386baf1354177db828e30a79b8b8c88558ba146984ccb96502578da5133c21c2bdcff9cf692e35c84e9c33ac9498a75e75714610a17b467d3ae53e74a22cce1aa3f095171f4fb8830de5a4ab1f52af3f24398e4770ef5a09d4f7c0c61d8b8102a7bbb42187d6eb67366593cb1238431d4865a5907616c134f96a70a55724910e69254092ae951ffe1b540982ffff565bf29ed016112c5940eea2e5495e80fe63422eb722d9f20fbc1e84912bc64cf2528d1d307737ad5a91c2c0fcce083e14fb6ca28298d050befc1e0213d4b96d856ea473d983db9c88e973d992721cfc883594f868e1fc69e810773fca53d593ce7ccb883a9543f2849d81f4d0bd261872a4ebeba92b99cdaa68852b9c429b994c74b52077350c24ae45a34bcb374309e52e331a48866ccc1ac9d9f6e974a3960e9d5dd9629a62629eb51b1b784b766c4c19c34c7a41aff0a1c3e1346a94fcfd68c37983c774e759e4afde79de10673ccf582196d4087e5ca6f67e849b2a1b46dbb4ab92c5aacf911f24eb86b307a3a0b9de42ce2abb621cc5043eea74fd08c34984bd9675b366d21c734030d067dd2884add396730a93df30f9f6798c194b6f2db3e7974b530a30cc6f8e0157af1f3ee8ccad0c19f3274608a0c0611fa835059392787f1f038397ec6188c7de69b9fe25fce1dd631430c5c975e8d4fc15f4187c13c66d9463e3d34bc2f3f980106d3693f4b1327fbb8858530e30be6b42d9a5e9d448c6384195e3065d3ada44ae77b13951865388e2e98f472c71093a48f4098c1057392fae40fe7962d98f2cb7e50923a9562e533b4601e39230be6dd10359f727606164c52e59e585ef5a83cba195730a55fb999170b0a33ac60ce13e4d689d413473e560583f0131595928965273f39cca082f1b4f4db799831852a091df35cab9682397fd2740e33a260f6ce2baa849e9815ed0c2898f2c784c6b0815406339ed088d1a7da3c5f0788194ef0f3093aee826e0f8b91b209a62046e8c9d9c4bbcf3899c1044b4aa26928396ec9e58c256c767529432c76a7cac9faef9736b916c3c3e36628c1a07b37f34f94fc1f4c7c0677a7c4d0bfffd30c249853d49b7cf7e28c23983bfcd34c76898bee8d60925d9492ea6415790f17c1b0a26428499ec9a33e398308a6afdfddcb7a320ca397b6b82e370bc324b645b9786f6d20182693a794163db54ae2b20160184df6d241b9a95cbeff0b835afef872c28c9e92fbc218ae3a424d1e373928d9402fcc6f4975d0a2d793cebb015e984dad09d749691726dfae941ff94ef2d2ba30c8d930418d3a37900bc452b9b9bc5cd8ce388d39b962947cf9bd47be015c182ccc4f52bd57d2be85417db4d88bf15372b6308599137a746fdf956a61169d334e289f16a6d0ab5b15f92ccc26c5678d08fb349285b9b742b34b74ff8d62611ecf7913afe4b39561918e9e8258a96c7a85c13a8c56b2d615c6f5dc5fa2bc28d9d35618dcfafaec6207999315062545d997e5925236d32a0c335796f2cdf28530a9c27025051d37f9bec49652616a0d21c46b899656255498c2e5d4a293f0a2e2e91446f75831d174e52749a630959e5bd0132f85e9a493f36fe9b229053b7af018180307367420c0023a30f062a0b3a38c1b0bb08000747c8e32769c4f341670010054c00e03671828572781f4c714904079e47a58040a10001f200c2800003b7494b1020204e07170cac0c118470c040840478e4fca701c39c848800974d820e3282000b5000000000000004060c78fb143c7efc000e2781d870c072c4001a04307f2208000ce8ee31e0508c0d9711c470e8f030060000f48408ecf417276e8a0416301000840030e506cec10a3868e438346024664516cec701c376271801158141b3b7660c3cba041230123af308997b19cac4d6a2c9f957176e373d0d8810da7e1dfb6630c1b2a1871c58ed7d10a326eb0a2d8d891a3860e3168d048c0c82a0a0d1a0818518501465261aafe9404a5525b5041541874cb47ee53d029a7ce298c5552d0b14387d27fddf81bab23a630f6579a18e6a6e4c6dfd01b7fa3e818c3c68e1c2f860d326ee04876a414267b2b4168e95c6107525082097c600513c0b4c408297630328a2a9e347d9b593a29f588280c3f6ba3bba17bf2ec91501465eb33ed6eab4366fe53b6ae31afcb279f6006345a30020aa3c89d3422de914f98f4a7142766661f58c10476f88003628c78c27855662a9ecb9cca6a8d318e182928239d08c20827ca9da61ef216c6debd3e2ba983914d983d9aba7f474734a1239930d7fc968c60c2542deaec44852e610eb1a1e26437fdded812865fb3244fc85c1939691d64e0a051239518a144625d9d7ecff12f274732868dcdc17918ec24cca7935c9a312289a4383a8850da628f045aa62d972aca24198184e1fe4cec77ff8e65a547184b0e2b1fc654ff898f71c438578830e208c3dced6768132d97be6bac8d31d208539cd359eebf540e96c30893a4f38fd2d14a4b32f911c392df412f22f38f1db34eec5f30a20893785151ef36cbb2ce35d692df818e44987279282588f498f2f91a6b85630411c6d17b625c304f62091934c6c071e3068ea4588e914318bbd4c9e16ada8e114318f459fb9f160f395b71ec388187879916c2ca1baba0741ed558eb010e442307c91103f58bb10395a1434f30031a141821c48e0c42470461b4b025a5b5cb072293adce0a6336daa22a1e3f48d33c51d515afb1f6635c937123f9c008208c59f2e82f5349237f3066872c31cf84f2b8c960c40fbba789659ac96aaa452dd1e4bd52d92526df69a40f0613e27ced83b769ab4046f8703546f6701e7a30a9d52a29d4739aca96913c24ea34de6db4c83f713c98abba54f4bc0e1bafe3c7c81dcc49e486507a4eacb4fb881db20e5b3618a1838eccc1a42b3684475f0ec93df567ca3f290e066182121c8c2596e91c6e724a42fd6f3069ef50f217773798d4e8b96c32649d9ce36d309a945298ac39618349ca8b9ae07943c92d598329b88f7f882f2b4174d46078cb1e2b6bab73c99d34984cd8ebffebfbf0d14683714c54536577e964eb33dc88190cc273cae9fc16af4cb60c261f75299fcce40735194c51d394d8b6854a151c4331677767df2e73312c05252ad7411e11835145f4a44ec235128632028632f2851bf18249f79654763add85d26da5e4e908174c693f4dfc3211e4f81d22781ce4781394cbf13b6cd060640b7b6ab3b4fe338261440b86134d0e954ebe9dea932c98e72ca8dabf207ebd62c1ec95842c31d146aed0a75d34111d762b182ffb7eff08bb0ae63dd3714a4b72840a26f910eddcf1bfa1323205d3c9572e77eebbd6272998d473e62d892fd9aaed228c44c10c25ea7b12e4fa0a05bbba4ad6aaac5db347fbc8e9850d234f308c52a53f2b7a18f39213cc9fdd3fcd8d29b74ed204f3284950399e526aee3d4c3076a7f512712657b67009461f135521ef5182491097d386de175d79adf182912498a229a9ba62877eed7d368204f37deac81565795e7381c3c8114c49e5af133dc95b67a211cc1d33faf31e4fc7915d61a408e6fab7f4f5e291c208110cbe774ad67f7d18c64eb1a4e90abae6a426228cf455126b44f6a41c54826190bf664a78b8458061b292b3437f1b37ca2f4c254fb624c99a2c0422be3095d67653ffa79467d78be4a4f465956c114a8a8618e74478614cbb4cd379ca1288ecc2141f43eae97e35afb8c61a7b40a4fe8f53be7aa1a992402417c6b470c19274299cfc15eb20820b93c679f796f8262fc9ed10b9850d115b5c2d54841629320b7369e734694f1caba0d31c3bc810914535f3157761c1da2de6a28d4ee93d9938a2f1d658cbc1694189a4acbc46c49f4a81857135dd6396506a59ba2688bce25c711ead300f11569854ec1c4d4b2ce5e5db2a4c823c9d2cd5afaa30eca99deca1543fd625920a115498c44d999c6a07955b899cc23c4c6192e2f8a8f47ff2521c8994c23c48611ea330fbe7f73529b497a093444461927949edddeacc2733147a885730eb9275f9ed70b97f4d6c2e020ad35ab61c1d4a1d804c887cc2647625d77a520a15f4229e305dea4ee25d4ff68abd7e42a413e62d69237f945b9c688b70c2d8bb96a4db988aeae8092012229b305908176f3975f259521326159e92fc1d9bcfa7cb84c9e434492c347e5c344c986c7b74ccce2988e5942e442e6150a29efe2816114b183f653dc4dea6758e8954c224077d524a822539c93a11a184f1a472511f4a65adf6e01099843975bc74aea39684794ecf426aeaa9309526914898ef76f32c5c229030cfc935a52f3f970c51e41126252bdfa2c77684c136d64f9576451a612c2947fd421461449d24a1affd6974c6b07123c7b7c0078e83038f6ce44874bc07506411c60f192589bc8c4f4f22a20863a9fbad0fb58b24224dcb32266e15ddc275980a2ac69569ed8b082288306cbec941a8cf97bd43987349d966534f9785dc10fe8cf8f826dfa4102649a7f49f7c564218e7efcd4ef6141984495b69358b1f15dfff2208e385f8cb4edf25e56ce204914098e412c24bfcc9950208736fde88a648cb1244fe60ce275a52bc5c4925bbc50fe60d2d5a3dd5a95ed12929d207d3b765653d93b39ea8dcf1c19cc39a18dbca692e28ddedc11cd277cbf4cbffe975450f267525c449f7db4bfa532e0f86710f537a3a3a05113c184e569ab7f8bac81d4c5a224aca88a99ad444ec70b625ab5597f30a9be5c240a40ec65ecf397a10659e318b41840ee63a759dc49372ba5451292273308d1057a664b7e79b89881c4cf1f2838e38917b4b6986834988dc1033a954d02929f206837f86fa6c4a4ec40da6ef58b9cbe28a5610698359cb94a84e556ddbd12c88b0c1e826c8d2313efbe7cc4e640d06a584d89a245d4cbd2435984bcd08e939e55fd489481a50040d2a72066377ca61ba4e8e19cc29084f7a52d2595cb63298e49c4ad27fd16c248890c168a32f885d16392a976330579f99b47f520a27e445101183a94337f45f2f27059130984cea6cf392b3db9e1087200206d3953c7aa753f8af245f912f18aeaeafeadcbc238878c154aa4fde5b5c184b97638248174c7de26ef7f58b70c1b49dedcce494cfe1df10d98229a83939a6c7490ba6cf355917a79205639a6b28cf9a08160cb3a3b488e65a0221720563b9b5574a234f9424a714112b98070e44aa601e2f420593b65827c5cb39e9ac1415884cc1203f7f1ae59504e56a12838c1b3674f00944a470ae7a66a9d27f0e328e05225130d55f5813c363c4ab480c326a243bce8d7c8108140c3662257aa7d26953167982418ef00d5bd1151127985b44259593242729497d600513d80d8834c16097a4648b30216b20b2047352d663a37b6139f94a30c992c2a9c8e734de27c1384a1056b284aa8d3a418239b5727dce614b1e751fc1245faba5ab24664d6e0463072b115d725b04a3e8cb9eaa388a10c198e1e9baa4d9cec15b43840cc3a85b2a3caef6981027c2b0c45c95784247c34348304c9653a82badbdd793041886d55c933be83a3565fec2606ff36549bf5d5a2e0b21be308593c3782715bb905e987b9497ece085f0c2b0a27582d0bf3c3da65d18bb042bc982858dd35d1706a1f565ba9e5c984f3095e41bdd679260e3c254b363929a249d720f86dcc2244449c1afcf6d3d46b630b6e596a07feae1e3b53029213f5d57a74a10420b93508b25f9e9ab9e3c08426651d97bcba5fabd9a6c9fad9e105998bcc6f4622407426261028bbca8844d86c202712c14094522812814c549de02a31308001040288bc562c1589c4792a80714800261301c30301e22261a1810161c140844a27018100a05c36030200c0a868361704038d72bce01d27eef3b00b85f514892e958453c11e6f509852fd6ada4feb37c69993829a97f39fff8442ca5d66769eedf13552078fa948f347755964205c2adeec70de4a7bd56f01fedc60a52b8fe4e865a2707a21a7b1b39cdd7739a0f21b1d04218dfcbe703be4b8fbbe8cdd5a0edba8dfe5c5052de871837ac68b65c1f84516301e64f33055d564e4841430c57abbf30a7111465d1855cf229ae23dab1f9d98093a221048ed6d73c2b5e2ebb173b4225444b156c07728f5cd09f9795bd4d32b89a3f3b4ab83a1eb80ba8c5b2a2e6cf5c80a53a9caf80c1c9c0420f93547c67c43496e2e0b159cf66549cbc5d49d67983af744d3a9cdffda91a3839816ad87a60072c2690b22688df258f08ae08801288d08dca75a5112e5967a1d9c9d6ecaf2ee9163245504f4ae297d8d0c3b23ab354be07a25a1b7dab9f1546ea510b691178ff3ee3dc69a6feca02bbb77ebe8922a89001681f48392cca6e919a45ac28f4064c9315efe2e506ed11eacf4fd913d21176239fd38b5c454f1c2feadcc24b5c09a1fca23267941e115c686c366b51438ee787ec7c218f2f3334549a06a5b3d5297ab3cc66069ae4fc3b9c2856904361e69f3f9192269c106284cbfac206694dd6cc7582c8902e8b7b4587b6dea3fdc8690b623476e0d197611ed745fb30fa1a02b2d5557aad99f7e96790b8a43b1d4de056bb4cd140064e4576c393363480a54b0a490963eacc1e86f0beeca0866a9a3f9e9092ae474381d56ba45a8b5b470585820a3175b3866719e4585255411d33da9d9320e083f3a2fc01d9d0fd3058b1216ca0f5c15466994ee37c9de8827f6684b292d5f2eed7a2f479eefb96df17177a269cbb261c6b1d99ccad084f715837f8503afb878097b7499e7bdefd6b8730ed378efc1d2046144a94077ca6b359a33a9a269fd18f1deae2513462894373c94810ad9c2c4ef39126295a5b3bb99471c106a55a96a996a420d9485ec6a3f893bc554d13c3b31a8a2e74f3b374a7edf7dcbd92f2c571414c91b724edb8b56d7cccc9dfab9da7175e40b5c3a22b8e584c64625297cc92a5d12f59d91c426cdfca70cea6106b4bfc77a09ac31750d0a815ec275b6e42ee997396bd68c608608629f38e7acebdd0ee29da536d6a57acf58626520db54e03d74b36154003c85072a6dcdf2ab8e9bdc2728a4c21e7ff35af8f56384f969192ff6501702c45b38542f1dc6c0882c04bfd34c300a1a7282cda3c5084a84adb665eafe29b4619c726062681da7b04edde8599eb04c913b24181b00c63eb24d0d89a321270d0576a699e82f91eff82abca9d752db3659ee7772e58cc5cffd2e7db2df8ec1f44bc5420613316f34758110ef78e11f4870587f55768330f4157bf239b242cb36a33b0d8ff0d6046df21626fb402afc73812cce104abf16a4b6530f639884be0132a5b791cd423262a94c4e1c47ec56cd416eee1fe8087f1310d3bafe70d5db643291c875994f17631c1c2c6635a0e35c5f31a513bb19d26a29c40f3ae5ee0c28d5d63df7e932d3cd0642b3e863e90fff9f3369930dfce206f6079e51b8ab8a787d7238cd77ba757050cbf2053b644689cefdce821a4096d58734c50789273b0b5ca466a83a411d353d9ec2a4209b743371ab4491abfe2e64ba060f0b9710b929dbf00f28736fd5ae23514ac11b73e0c94f863477ed0ba0a83f37827b51359469559e8841f6f32d1398fd897358baa67f858de7f406eecf2deff960266edaf14a828cb3d3cc829b26b45e008ded1282fd53bfb94810de98cdd7578add51b674a7580df293d368841e71d9603f06c9b58ebf861829e73949ae2f1cd63050defd4589cf707aff9dddb41796b22ffe0a82f5445cd59e5c1c637364790f863c091483c61de7dbc347e1995182c7d27826b54ad434161a85876c558422a8721f6f16c2b916f941c337c725554cd5d298a28d5ea7eea5398c6af90ce2edd9a3df4fe778163699c87c2037cd9c4445b6d2d37dfeff0ddafba6d7eaa8d91f17c6b2751c761dbc7774f54f32bede8981420d69dcf716543192848fe20e80508512d052599e96bf00680aee4bd3805a81159eb5c92643480e6eaaabab34b43885cd099e6a5449984c1a6364fbf728478630dc8f177a6f382f7587166162f829b2601c9939b37049dc47f10a79fe074ec8e70c877f68f0c7cd07ee37a01bfc0e6f36807b9da112ae79e72f453ee9f9680ca7aac9caadac15c2b80a7ef8711ff954d289462c4544ec4a4252c594faffa49d4346404e2bee859b065bd82d1064d107bf863ff7ae119bd765a901542e702974869d4611580e09f6c61649eb00e1e54a9c267989f713aa6511c27c40aef6beca967cc3bdaaf4a54a327cee306b7697400b62d2cc25172da176b3281740c0531022a6236ba6860d074a4207132659dd70f97588c7c142585268b6611c86d5e74ed83538a11e5faa0ffc71d444630bf645dea15234430844d183182ef69b5d5222f15448dbf63174b00001d38167758450611487cf9ef9e8378d0af1c7d52ed9a8a40d6de75022cbb1c9e90fb72072e490f18050753655e36073f7e8c80452d2e87110da03808800f4ea369a594a0c08b90435dcb4f97136d7f2defa2ab168103a33f723e3b762d77396b3ebe92e88faa2491a3c222819b1f41143f3f9083b353e991e563952a3bfbea4f33ccef436edc917ea9d26ec2f4615c64cb43a3c089f013e0e52b727f6d4c3109eca2a26c4e5a40242aa93f1345bf8d4d0a483b3b2158f91a110abd60d1c705d21c7b98dcbc55a326330a2605aa31995295aaf9781797b22b15ba412dcfe5c22722f732032b8a5daa5e7a4e5c8cdbe046e3e43a6580fcc8a6900726b7f609474efc7d0eae89e0c768dd28920ec504fe5ee0055b0bccb2ef17ae5cfd1c0de09be413aebbd6f7a5f122702d359b85dcdf84a70e5eb3ea5df2a9f9733d73bad765dc202ebef2c7433562166b1d42bc4c20ace8bd951990ca5f450f1106fc28891f9bf3491284dfc54afddd2bbf0ca04071f5293cc6a2c57bbda040442ca19497fc5e35a6b057cc1af3eec5c5623c008ca1c5407505c0927087987b5669c6bf50b66ab40809d0ab4b7abe1a0ab7d1f5f524cb52cd8826a68f23f5e221f65894c00782f598327ca4efefdb49192bd5874c3e3f6aea8499b475b85ae3ef7ab44edb7962b8dce476d86895b2f77f37e46e5f2b422698f93d131e1ad861462ebee61e34b5a1db93ad762c1143325b79a8af4e1152a4c522711f5c1852dd34b23b650d1eca3a66b99e84600ea141634eea15004d982935a73693493fad6543ac344a38007884e204353070f84a4addb531c8933f8b5d7623c08b993c18fb4be5a24d5510d5cac3d2a654f44288ee7c9217ccd7fee0a1a9a0568f8174d9aa901b3bc69270a4363c822c778532d025d3caeb03ee49f03cda6dce01029bbd5a89bf6e6b4ecdd6e122ec0d089c71e78aeec49defbd8542c953663be180f13176e54e2df4f56b621b499d9c8374895ab2dd66a892eddca1bcd823b8ccbd06b7f3dc4f382be151ba4f063635f411ab1637a9dcc8343d7581224794963cad6d7bea7ca9459dc185943364048bb12638d642a78e22049456c84ad5f819aa74fa8b8a26e2cc2b7ee68c08e0b03d8b9011a084d3603d5c66051e56440f95f377a101a5e9f956184c3266bcdf81e11dfe30a4454ddc8b8692271e8f7f6b1ee9a9642d79f5e29ab2cc77e2a06e1c9a1318d1e4e0dc26aab378590d9ae82dcc131ab7aabd14cf3bcc01b7f0991b0db060b4d4daa0118df6212d8a029c91b79cdd130f99dd87a845c220e5402fda990c3915080cd15e62ad9c971b4b1f85f24eb5d18b7f03a129db6d890af048c062db80cf8aaaa3e3569d1295a5cac7b48c0140453ee7a0883a3ee1e559377c97f4be200a569f6535bab0765180877c6870480a2b27574916919c0f42579d9d87be55b2c69941902f75bb0522c68c244ccb7fa469520ecc49dbf2c5f21e57572c8d53953f3a4dd77ba40f20caf009820c831ef7240055d8e23f6df7b1d217bfcbffcc781d09af4d8a3042b2b2d4495dd4f663a839944471a5cb6e22168651bc0bc57da031ccd2624af209394358fae0258712066c4d417503a1c7d467eb4a55706a3628bbc9c199c8fe8454d11bb9300b585d23ea02ed701a820d0f72c3a5c0e984ee9e5e86318cf6bcf1180170a484a576750a36d3a3ec24b81b16a0fc75adb90990c77ae8ce6a239533a0732b45b3341343364c1b0a0bf8e5fc342c8892270c932971ff7cf19dd4b73844185915a6e5b22a0b779ed805194c9593ed456419dcd1b67447dfcb09b342ced21449b9e1542a95d8fd7998e00077edf8ae3eec342bc72b7787e5929b4652d0fa462f211aaf31b584e85cb604be2ab6f9f0d4b663b8e3671b3602d18c231fb1419fbb0d5c448a25fce60e5252e5a5dc790e000c5048b76988f32475faa4815598a8672a39cec134be4283533d93c86a86917e56567512464d55509d387b2a229ca90159a5c7fc0999bd1fc01db1081d66c95472674f41c6d6ac45ada8fa14112c3fd03d96fcb43657e87648cf92cb467c4bc626e2f33a02597c39aaea18882b6911a87de7e4f75c71acda31fdd3894122c87490bdc7c955a9dba4e670d257e91c40608992c17ae3e5ef8a78b1b71c6d2964ded9e81d82ecf8cfe3979fd550803ac33665b7b7c0421482905c2a640914f81defa09b2fd1c8dbfc538e9ec473685462968ee2a76733a4650450806b943690dc056a3e928102e62a95194cbec13c1c0817050c40518e12b9939f2012c43c43ce8242808934c4be4e050366ac0f97f630c2f977762f439e3c8c2658e5093748c4d9d041a637a0a85708a5f69b53c6a96e69f55d6c7d17cfa951147077527eeceda06695c58b21b82b67c8ed511ccd040c9a6177aa912ae7f752a64548e822818aa540d1f82062a970e4b3ab557420ec31f9382bb4d8895e4aa08d747ac8da75dcb6e4755984c4255ef08568816f774b0258b3f79bc738a0caa082609a398a139f2673abefca59ccd5ac600a0428baed34bd84d9b784c4bcb803d96a86337f9ecb3de64e0d5a6f9d5b2e2379f2a3ea82bcc1cc9a4b37cf97ffe25db81efccac93dd706317d245ceb77aaf433b2404d8eb51bf09e09c78adc2248695d1a461d40eefa4878a03300b5e5a683e11f50d6d15e9717cd34d8770089e9b5a3eaecf56de5e8f164291eb3fac13e26cf130c0c62082b3ee1455af50ad212737974435e78972f11cd587c07cc7ea24a6f3aa382375152a83191fb40780a56d43fed559e88fd9e15eb781db1a2b85e748d6659bf0971b2745e531e53d9a34c82d5ad9261f04e00292a2c15dd0423454624d34f427dc7480f5814e86ac82f7c101e014238ca1cdd84d918c6a1d9deaabfe5b2881f8e39536aeff8af171a06da130b8807f69ae243cb0ad29429f98c8fe6cfa9c076f72af35a8a18c8d7c953f20dc83c7f98029e8dd83473a4c8f77e9af0cc02ed643a173776d9acb7025f72a40c51ec25301d122c021b9428d4a8be54cc7081fb932c6cc9ffd69e69a105924ab708e1f32254a658b9280f918b80a439257449726566064630389b456aa4be15d0b0ef86aee0f72579e4a6748f78c5b72e14a61969d5ee344b098c0bfb674ae4170b8008adcc08ac8197af012570cd7679b6c872cca65ad24f40ad8fde0036d6d2953e405b4b81aec8fe5f26baef852f521d94ddded9d1291fab3e2f1f2fd275ef4bf21ace4e61cd8cbacc392a6c06e29d28e3bc5d6d65458e247420242ba964be407f16926cd51daf5b67aac4a5bc2ef1a5c722305bc744422a41f4fddcbc8a4c7ee3a5e5a980c1a6964c21d4400a70650a3a6c576a5c924bb300fe47b282d584e452c07339e073ffcf6811f8ceb7b7c1441f50e2e6336b9d9a4b1a478113db3e21b588b8f13580d91cee9f6d2585ae1e86d8c8e59edfd2983cae5f97ca80c3faaad7091a6b9c8814ed9e5ae34331b9f0d47ae6f6e0ccf74325d3226ba3d68c4e5739043d15c410148897e03a4c8d47cc070120a58c963de934184fd4f9bd503d579916ac173dde7d18476bb4bd07d4ac8e96dfc7437fb1e3dc1e4533f0b6692eff866b685f1852f7ffbf2ae2019ee923d797a32669682031ab53e3c57110acbdb088aef9f82d8a3b908c25a9bda7da060cd0be8569b442bef9f02cf1a41c97d100fd088c87660d3a0de2f840b795aed760c83ebce161606f1a2091982ae49940a36e4884f0dd990e0324b5cd204332b846e2dea877074f7b4b4a6c4a6098219c467baa7778876fcd52ab1ca743183c52609cca21fc8b55bbe1dd9565c69a3f576459c14d3015f39788f4ea8a1d0612275b366467fdefd608e6ba0748e3a4313f12782f695fd09f3bd3f2e1b1faef10a82d064730e429da5a24ff5c37b6b6e0986f01fc10e85340f6b04947f60ae6ecc6fa1d79bb361e05723854bb0d01ac26534739c06fea3848628c351881688fd82a8fd009ca37ffc5cdefc6707c3e10dcc25276a50f8ce16e6277d5943d506cb689675e7aeb0e9775428f60e417d1c4542e9829beb29b22dbe240ec5cb015c412ae3c74e3a7dfec142e2c0c7b4473f0bee1b1dc1e9565c3800c71857c2b1cd6ebc41e33a976e85d9057681ad4c5e50add5d6cbdf0435fbe593ae660c2427b9ee6b909daeba520a2b8e1cc6ebf4fa4ee83e746670efbb76e745522fc4014331064e28f717bd1ce930e72031a927089a8ebf8dbd6627b835ef7774d13d58fdd3fd4494a6e46cffbc193c679a2b527b9b00c3ceb16aba988fe067c0242467cd93df9fb862262181a223b7f2c73b48693995e6f532cbb42545a68eeab263b7050674958684ba326700a63c51ac307846b79536ce33d7229a4bd02180eae702c98a5b3a7959f6acbe716220f5a86b8c46297343fa183d0cb3a1a3e3e24e66de6b3d68da27b78576b85159c09fce7289bfbce34cfd6404081a67e5c6f28fa4420b2b33328ba39372ab5a8aa5fb27606bc758384ed701ff83c0fb2e4879be8d015206e3a7189a9841dbde58abe96762e39cc0a713c9638b4bacd66a4235620229e43efe040890febaf7790f19f179af80ee24e8d023fdedf3148f7e1b069a7ede31ec29560becdf24a2abab0cc5368582493b23f1587c372c8538e92e535ebbe8fa73ac20a6a0c93e5969b827f6737564682bcd4abb84d0efe2c8c032d9252e22e6f17caf6a15e520c9a2f9a51a1fea042731830b9c3ca53bb129d1b53f27b0a12f8b130de612bbd514bcce13f1279660f440aca7f28aecc44c47d856dd80b0deae205c47da81fc983964bf0989cba8c9edba8a363073adab7e325b9dac56bc37e5317c63bbe8b98c3fe668773e5a1baf818f66373068b901da3a414f653ecbb24cab9a85fd770f1a11cdd0d75e84ee3f22edba93f96bb337984829cd4b6829cd931bbcd5375804fdd655cdee93aa6631f9ff2b1f4eb1e5dff05c545ee705983abfec33242a10cdebf6d5c911a894a579f5063c43be06bbb2b67624ae902973fe6b07b2edaf5b7a2daf4a378c462666fefd3b1b9ffb649c62e6da6d0c3a35aef4a09632b7dc316e929129a4c265baee137b66163c42a295e53a27ecb33e21f4cd1235f18b8cb29655b246dfe143ba26ab59ff5d1ac9f227b114cd93d0632fa5bbd8caeebd6ac54c2d90d1496b49ad248b84441e21d828b0992d7c57930a68dc0932935a134d91100da224a76c2cf687b69b5ae4fffeb72699776394feb98d8edc02d2d848e7b757d07e3fe4eb3833f590e9b0f1ec8240c5d93cc52ab25e524eda99fb774922fdfc56191549a12061c1af075f53c613adb111deb9b785516805229a3b41cde2d3b3d1b767ac9909a3bc4238ba69c55228aa0457fe7b38208bbdf753b947694828f3d16598c3a9706f745872d9ce7b4c8898b440c99e185ac8db6680dbbb29d25022a93fd5d8bb98d7efcb81ebc7c70c7edb63f90d3bbb8ed4319787b5738c20b7501bec8d97964d5e82fcb2c6ca61f904c165296a00bfd14767a4f779cf29622d8d1bf29a91544f8d3ca8b286b4a9ffc786bd85cce2a0ade7d3f491896af57c1b22144623f32bb59eca872a960336bf0d25fe2a5e7b070bfd893fc715efc5be49eefb98e539aaab059aed227db8f719a72434fc01761e6ad067e0f202c72d0921385929aabb5070f75146d4c88411753d891ca9456eee3203ea69090f76887fb403bb8cfa202fe9267bbbd8e7e13d42e2d3f3b7825644232539e39ec4a591ff6c83493182a41c7ddb89d844abcdd8468010fbbccbede504e31942fe2511ae804d7b8fdf0b85ff4ed07de98fad90b94c2e37944737d1dbc41e3990bb3bb8b537be79b51a06ef3ceb7bb59480ea51912a8dac763d0e24fa6a4ce70d4e24733242f476842225711736f564913a9691960d5f0ad512b80adeb44681b503461f0139f369489da0427132ccb947afc99a2a5b006be98bf1bf9ee1aef88e3fe06e4b9e18e35d283df391ef98ee88ab84457fa74f06d442f146fdd5a4403fac43be0e4550b5b43c84efbf7a2748584af707351f1ef51222c5d09523ce2ccf2fee4db20e5fec98167eba2b80855717965c76dc19cb33edec85fa39951d2a7146ca8c7cdab9abe74acb20c1b13334337a90699ae8646283c9e96fa59859a55228bf9a8870c36a0910722ce1dee6616772d991bd8decc6c415dcd4d37e75a519def85b94682ddeef8539f910b919ce4c16d46bb60659fdb4f0e11f53de68a76b009d2ed7d8538cdf07231896e622e1a8b00f54904cc6f20c1ec4839866b88dc28c7388c755b0a58e23651b3d1ce085d88646b0710fbaffdb5959a8e5388ca360fb0fdda5e0238d756f5f5cb91af69e5e9afa899404de547a9b7c2590bcb3c05018c4c80499126916e2d0f6d1c4f207276337e32f5362f0ef99eebcf740544d9c00ee80ea99dd9aae37e40975010854c829cc979f2cfc9d5a253a5674a56690e3066c9fa6fbf9416c29c28b8052fddc84d9d69bc72aa258edf0505a99e93fcd55069bbaabdb8e120c1d34b0ca7218e73a022a9c26f687122739036048384de7fd89733c6f1415ccf7009498b5a4de553b483d3436dcfcd250968e3c80bc5a11506f7d03782afe0e48661c84357d8c2dd1fc759525f7a1a80dd346b93ee33b526567c41da09f94f02ccce18451d5426944c88eaccd8474a4b11bbf21b526dc8ecb30903323316a07631128b680afec1a9f2400671ff745a903fcb8c7407c1894894fc0cae758abf57a5cb89872186e42845b5f69b4f573019054746080d12938d399a64c7cc16e1bf7adaa72d646a1d4f785228161462a08e71fab62c84dd9c99e5fc45d18567647a9c89b5fd9a0929945bf5d49d50a63f61971263a45255ca99ab447f5e348b515a25289a953ec813e89c1b69d1329d78b6f6e702c45221e3713066ad22ba70fde00cbc46521106baee69258f3df20d8936e3b4de2cb7e70c3c824c8119ed0b63fac29774b24a9e319cbe1ec922c7e18a0a3cc58b8b2b223a9e3f20a6b577826940abce6dad8f467fc9187b300823b57a9234b968d804dcea80d26391720fd45582dc1be011b390a757a8c8a4c83696fc41859a46efbbf8a30adee59481d3c31782a6888fc13a0476010604cec3c70f20d955b0e8cae6f8d0b678181447d11798e9b5dabc1c7ab88545d345e6f1f182c8308532a333933a8d21f4e640628ff32195aeaf8adff31560fc7e68cb6b183627311cbedd03258aef3a30c712b92f0266b233de40a517f2b13fc5a0e6aea12bc435eede24c8caaf7e4ec971cd951181f36857e1c4c78ed8d43e5380ce88dcf5eb9221eef013dd0d3e2af7b66aa650d1b0e2746ce96e04ae6b3186c51b22c240b3be458ca4538bb1cc890fb6cbf02ce20b9a0f478072db03a079026a467478b1125c7169c97eee394a2688719ad9e293dfbd1bd7a0e96a5d18bc554ef602b483310408c764367ccf323f48cd7f9ab6e8f725333b19a66a1431a9881f36c359059cc1125cd31d1953bbc64572177a725b07c78a89c67f11d019fdd387cf4ec30ee8318dcd76a99cade0d0c45a9cbd10fd93688f34a17f5b8999a2a6c5e6198f74e0b4d0013ecf84c8e28b3eb7775058eeaab7c32160aa0f18b8c33eaeef60f3697dc62434e60d99faa3628b3a94d0f0d85715c383fef38216bb06ef34300408bd73279aa27a0c9aa1549a63388e32d89d61afaa0901c126bc8ef786bdf082c238ab1fd84d0cc9b485b0b796cdc17e1b7d16300060a481f6c5b99551092fda0786eaa9702f1e7bcffe03a5a36422341535f148ac007a3ab8c891eb363d54cd9dd39178da932f25816a0a474e96b7c77ef18b88604c55992a2c99c2f5f09004fa9fbf53bd45085c45be40c8872cf3399df238e21bb8b304c03208409eea94bbdde75f4b969af38d7216532a7035b1b47800b732e9fb40b4fcae45101f9432e965414e9f74ec1e122f2b0616d69383831420a0239d2acc4efb4e07f387a257d21118d9e52131f46fc4a9dcb965e46d112772dd5418be938aaf6c0ec67c9ac27137af60a38177c42a85a4a6135e4534647aa28fd55bc3bba0a613d88a72455ce08dff531b031f0d5c9ff8e069d19217678e817edca92e9b726047665b40cc021801c4eb719d1f51659b3292d2f724e4d1fa99bb481295472c2057f3cc1b2ecfedf59338a2f7fede8128f53d5f87953090b2c7823b47c9f53e93ce0a0eca7c4d0227cba983f5a20ae210321b20c2f4b9a97dda5238268322c5d18ad608c088e51a6cb14fc3be9dab5d80a55af0181b4e95c9394acdac928cbe79f304ca0037ce4b8c0a1e43ff8e54ad8b4e6b97c13accd31640e5d44b8d3302f364bb14d7637986c56a7893d67a3f96f8ae3d2d05b6c5b965a04661c5119080c4c507f0b07d9de283bebc378650c367c24d2658652012515eb75038740ba97954f29cde1978d30297aafa06f2d20ab72d485b96ef2b135b324a64e28f147c9866e6153e84529c4a220d4c90e2fccef893e39b12788625eb01dbacec409bcc038b320c0e80053489ac76a04ec39191500607c1b01049a3561c1f9652906bb28b6bd0ab6cb10436e21dfe546388e6c79c89b2587696736b4a4c591bb6b5567a916cc297fce14235c27ba0d4b9fb7168e6fea995fc71ced97a7a07be3109d429745d3b2167ef7bdf98e50dcc0e70d8eb5449fa1bf4fc4da340abcb189732127b4dcc2a73290a7656eb91cb0893cef64eec483e6639e0e416b3f5fd0708404353698e4531e4746a9726cfed18ac1bc7819bb16d6308dba9eb7716d0661172eea4f44942b266c0c50de7ba1df2d3757685d82b3fe605a27bda547b187f5af0ae0d678fb27089748c7b2d4646c8b1a629fee0c9df95e212c3b053b2383cbd6c85d4b065b6995735566420a8ee17db532a60c34c072d14c111a8e36c741081e2ccd3b2ad18faac5400e9d5c3411740e3268b4449c0a84a0b24310da871dd315ad3beac036e0b6e74c51361131f58ba7cd186d5b437d0c55a07abe61b01553a92c2d3eb01d5587f61d3f2ef9482f61e4d1efede76d4340d87b0fcc9d7421267f14a22cca6ff5bbb889c99df49f24dcc0a8d4c1f72607337949f75a74e81b35b38b4e5da4de598cebf9dab3c7129bdef825fd1c4b4f8468b8ac0810f14d4c07e9e965c0c6317000b1f5f1bbf5ca3c9771b33278dae93483ce9243794e62036dd1f5bd3102a889afc922e6e512bd235a70eb2809ded3005a07478aaecc17002dba479371de6bdd8332c2ccc073aab89e83af8b8c8e0c0b6cab14e1a33b9f69c85e3e9c22b1ac0de924570309273fa30e242f35d131d8e1f8c51d11d7b5e6b0baefffaaff96020620556faf460954223720d04fea71bd015cfb8bd144970791c9b82fdab2263513f03d4c26ad3e5dbb004bf5b63163e3204a9d0dd0a5d0284e4566be9258b96a5a0164e39bfdcc2b58f76d4112dc52d7812c58c9a47e73fef65a45bb898fba8d7fcb8ac1d266593e9b0c2052f46d2d3e2dfd6dc2fe1d656d28a3f04c01d003ad9656c5100b603f819f9ba32eda9dc1643c6cd983283534208d3a4447850abe58d22f971298dc0a5f8fcaf71e534ff4c7d70a783ee30c57032b86a4b9205f3000c61ffebe0c2cdcedd5b02ca80c3b1f5d301c482fce2f83a5cbe317b9ae0466105aa993188faaea171db0c5410e0b468d82b6d4d183b860f1787ab6cca3f567f2f29c179c1e538c4757f825369b411196ce827a6c0b094a29243ae57017ad2db6204609d5f2b4092296f55e27d55801d2c965003a50af84619f93e957365c3fb6518c9b18e070f8186316404", "0x3a65787472696e7369635f696e646578": "0x00000000", - "0x3a6772616e6470615f617574686f726974696573": "0x0110c8caee6f6eddc41c6cc55e554343392cbc13d2a8a57b97f6f85fc965bdd20ce801000000000000008270a62b61639ee56113834aecec01de6cda91413a5111b89f74d6585da34f5001000000000000004c669b04865e9acaf7b72bdfcb0099d70d9ec63c8c2d6b8cb0552815d7b50a0a0100000000000000c9a68a26e9aa37ba6334f1a20275e3be7d3a9d4aa988627eadac8ea0d0a2dfbf0100000000000000", + "0x3a6772616e6470615f617574686f726974696573": "0x0138c8caee6f6eddc41c6cc55e554343392cbc13d2a8a57b97f6f85fc965bdd20ce8010000000000000083f43ee3e4521b55de0fe830847fda88a6b017b87979af1a41b180c39da1e4b001000000000000005fafb6219eb8d463bec0370b2aab69f45fc780959fc2eddbc7703760aa34202201000000000000008270a62b61639ee56113834aecec01de6cda91413a5111b89f74d6585da34f500100000000000000622c382187c0b2c61ecfb17443294d11a9d2ab770ae6f1fb49184a43906d59fe01000000000000006e309dfa4c8de814cad140b8612a9e41bfba244f9ab1468e1b5d9b3cc1f5e56501000000000000000509f9caf32fda5584343c473b386c433acb99fd9400724b8cf3c618d840133f01000000000000004c669b04865e9acaf7b72bdfcb0099d70d9ec63c8c2d6b8cb0552815d7b50a0a0100000000000000e41426f7465c13c48335771c5450bf61c50a9cf28b9274f170c7421eea7974f10100000000000000ace46e899b90e75199549d8fa2ae7097e896ab3c845217e3155f99b6ffb888030100000000000000c9a68a26e9aa37ba6334f1a20275e3be7d3a9d4aa988627eadac8ea0d0a2dfbf01000000000000006e60f1e253735fb113c183fa603f591e4456435171f387c0849001b428b5ccb10100000000000000c8de3a01502422b59dfa601c9c3a04a98d2bfbd79dd0810d1d1250feab4241ee010000000000000064adb43a7628139f6c02100f6a5465dbd33422418426c572b12547c5a665008c0100000000000000", "0x3d9cad2baf702e20b136f4c8900cd8024e7b9012096b41c4eb3aaf947f6ea429": "0x0200", "0x3db7a24cfdc9de785974746c14a99df94e7b9012096b41c4eb3aaf947f6ea429": "0x0400", "0x3f1467a096bcd71a5b6a0c8155e208104e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x3fba98689ebed1138735e0e7a5a790ab4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", "0x42b50b77ef717947e7043bb52127d6654e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x47c9410b11325752265d54845357656f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x4da2c41eaffa8e1a791c5d65beeefd1f028685274e698e781f7f2766cba0cc8300000000": "0x1003000000010000000000000002000000abc3f086f5ac20eaab792c75933b2e196307835a61a955be82aa63bc0ff9617a0600000010ea9400f05e7fb75a3f7a92febbf58e5a3060dd06132ed6d5d68a3d75ec452826ad90a2c3fa2c756f974628dd279adb87935f7ea509856276e3b86f759b22451c161d0af40e6efc165c17d0189bd2d770bdfa0a9b8393cb89113f473a2e948c682253ee3c02d89582602ca5b0570cfc01dc82cc8d1b9d2071eb5db6318749124b000000000000000000000000000000000000000100000000000000", + "0x4da2c41eaffa8e1a791c5d65beeefd1f028685274e698e781f7f2766cba0cc8300000000": "0x380a000000030000000400000002000000070000000b000000050000000c0000000000000009000000060000000d0000000100000008000000abc3f086f5ac20eaab792c75933b2e196307835a61a955be82aa63bc0ff9617a0600000038ea9400f05e7fb75a3f7a92febbf58e5a3060dd06132ed6d5d68a3d75ec452826ad90a2c3fa2c756f974628dd279adb87935f7ea509856276e3b86f759b22451c303094c583e253794c9db14803585baaa74472f4ecba846defefc8aecfb6214ac45a6d0878f808b1baaa85dcfb4e930ae06e3205bb38855527aee6f259e3327b2253ee3c02d89582602ca5b0570cfc01dc82cc8d1b9d2071eb5db6318749124b02683131f96baec9121383995904c49a02ce2c2451f8038291e5db2dce66663e0a988fb965b156a07debf072fd9d34a9c7c0fc0e0ff5bd63ef766afb76e2b32890032c39c968f486f77f8764301a8479206f063d49eeb9f6d499333e2a1be045161d0af40e6efc165c17d0189bd2d770bdfa0a9b8393cb89113f473a2e948c680ce84accce1ced0de223aa72f760f1b3d13ddfd267938cd63e25308378d320084e1a59090261a7e6bd82544df1eebd96dc87b4eb1211346645fa44d6b932960b441629077e228528f91ca7dc17051bb437408a5ae272d0950e58961846a8fc2ed0d052eca7d732d9f560ba970ca48f67387b899e76958ea6ed342a3a553ef0229e065eea4143325fbbd26967c26a228d51a3a8384062f7434973f15d1da2c010000000000000000000000000000000000000000100000000000000", "0x4da2c41eaffa8e1a791c5d65beeefd1f4e5747352ae927817a9171156fb3da7f00000000": "0x00", "0x4da2c41eaffa8e1a791c5d65beeefd1f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", "0x4da2c41eaffa8e1a791c5d65beeefd1f5762b52ec4f696c1235b20491a567f8500000000": "0x00", @@ -90,43 +109,113 @@ "0x5f27b51b5ec208ee9cb25b55d8728243308ce9615de0775a82f8a94dc3d285a1": "0x01", "0x5f27b51b5ec208ee9cb25b55d87282434e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x5f3e4907f716ac89b6347d15ececedca0b6a45321efae92aea15e0740ec7afe7": "0x00000000", - "0x5f3e4907f716ac89b6347d15ececedca138e71612491192d68deab7e6f563fe1": "0x04000000", + "0x5f3e4907f716ac89b6347d15ececedca138e71612491192d68deab7e6f563fe1": "0x0e000000", "0x5f3e4907f716ac89b6347d15ececedca28dccb559b95c40168a1b2696581b5a7": "0x00000000000000000000000000000000", + "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe700afd9ad680e7563894c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10": "0x94c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10", "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe700e8cb253c2fa33f382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c", + "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe701887a9d70c3dff6e6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627": "0x6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627", + "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70198703ff0791c2de1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413": "0x1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413", + "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70260868bcb77241070efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108": "0x0efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108", "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d", + "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe7065903e26e594bcd6d41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d": "0xd41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d", + "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70a8a26ecf38bdcfc668728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69": "0x68728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69", + "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70ab8d3e1fa99ea7e2d0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c": "0xd0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c", "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70d12736684524574c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152", + "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70d30ebbc69dfd05038eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964": "0x8eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964", "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70deca1f47c95ad9e99492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70e7e316712ac574569037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74": "0x9037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74", + "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70eec11b5fca7cddef5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c": "0x5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c", + "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc402d496d20c019d22397accfc42b7635d94c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10": "0x94c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b100f0080c6a47e8d030f0080c6a47e8d030000", "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc40ffdb9747f7a8bfa200ea2291eea6bdf9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f0f0080c6a47e8d030f0080c6a47e8d030000", + "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc411fcf3e922de10898cb04273301735e65270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c": "0x5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c0f0080c6a47e8d030f0080c6a47e8d030000", + "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc415fbc5d9b44885a2dce0fbaa3834ba211e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413": "0x1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c4130f0080c6a47e8d030f0080c6a47e8d030000", + "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc4279effc2672354478fb5e3f7e65726b668728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69": "0x68728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c690f0080c6a47e8d030f0080c6a47e8d030000", + "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc42ffe7c0dd7d3d8d8485c403a33fb7adb6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627": "0x6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a22596270f0080c6a47e8d030f0080c6a47e8d030000", + "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc4350ea875b4c0459b3527d475e9825c99d41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d": "0xd41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d0f0080c6a47e8d030f0080c6a47e8d030000", "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc44d176c4abc6d2410c43423c2714b909c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e11520f0080c6a47e8d030f0080c6a47e8d030000", + "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc4585bd5ccc1af6cf60c19d791bb091b750efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108": "0x0efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f761080f0080c6a47e8d030f0080c6a47e8d030000", "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc465bb58c29c9825c1f6727fe8c41650ac82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c0f0080c6a47e8d030f0080c6a47e8d030000", + "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc46deaa967a94fa09152be01a253511479d0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c": "0xd0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c0f0080c6a47e8d030f0080c6a47e8d030000", "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc4829f3f4d6d7c991993e0c044ae97410b043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d0f0080c6a47e8d030f0080c6a47e8d030000", + "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc4ba2db252d1b8c64ec51b04d0f50d7d2b9037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74": "0x9037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af740f0080c6a47e8d030f0080c6a47e8d030000", + "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc4dfc40f350cc4563ad9ddff5ad92944278eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964": "0x8eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b8689640f0080c6a47e8d030f0080c6a47e8d030000", + "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a000000000afd9ad680e7563894c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10": "0x0f0080c6a47e8d030f0080c6a47e8d0300", "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a000000000e8cb253c2fa33f382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a000000001887a9d70c3dff6e6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a00000000198703ff0791c2de1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a00000000260868bcb77241070efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108": "0x0f0080c6a47e8d030f0080c6a47e8d0300", "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a00000000572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a0000000065903e26e594bcd6d41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a00000000a8a26ecf38bdcfc668728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a00000000ab8d3e1fa99ea7e2d0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c": "0x0f0080c6a47e8d030f0080c6a47e8d0300", "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a00000000d12736684524574c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a00000000d30ebbc69dfd05038eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964": "0x0f0080c6a47e8d030f0080c6a47e8d0300", "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a00000000deca1f47c95ad9e99492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a00000000e7e316712ac574569037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a00000000eec11b5fca7cddef5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c": "0x0f0080c6a47e8d030f0080c6a47e8d0300", "0x5f3e4907f716ac89b6347d15ececedca487df464e44a534ba6b0cbb32407b587": "0x0000000000", "0x5f3e4907f716ac89b6347d15ececedca4e7b9012096b41c4eb3aaf947f6ea429": "0x0d00", - "0x5f3e4907f716ac89b6347d15ececedca5579297f4dfb9609e7e4c2ebab9ce40a": "0x10043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e11529492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0x5f3e4907f716ac89b6347d15ececedca5579297f4dfb9609e7e4c2ebab9ce40a": "0x3894c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b1082c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a22596271e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c4130efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170dd41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d68728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69d0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e11528eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b8689649492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f9037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af745270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c", "0x5f3e4907f716ac89b6347d15ececedca666fdcbb473985b3ac933d13f4acff8d": "0x0080c6a47e8d03000000000000000000", + "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a000000000afd9ad680e7563894c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10": "0x0000", "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a000000000e8cb253c2fa33f382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a000000001887a9d70c3dff6e6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a00000000198703ff0791c2de1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a00000000260868bcb77241070efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108": "0x0000", "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a00000000572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a0000000065903e26e594bcd6d41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a00000000a8a26ecf38bdcfc668728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a00000000ab8d3e1fa99ea7e2d0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c": "0x0000", "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a00000000d12736684524574c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a00000000d30ebbc69dfd05038eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964": "0x0000", "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a00000000deca1f47c95ad9e99492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x0000", - "0x5f3e4907f716ac89b6347d15ececedca6ddc7809c6da9bb6093ee22e0fda4ba8": "0x04000000", + "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a00000000e7e316712ac574569037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a00000000eec11b5fca7cddef5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca6ddc7809c6da9bb6093ee22e0fda4ba8": "0x0e000000", + "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e169030afd9ad680e7563894c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10": "0x0000", "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e169030e8cb253c2fa33f382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e169031887a9d70c3dff6e6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e16903198703ff0791c2de1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e16903260868bcb77241070efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108": "0x0000", "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e16903572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e1690365903e26e594bcd6d41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e16903a8a26ecf38bdcfc668728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e16903ab8d3e1fa99ea7e2d0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c": "0x0000", "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e16903d12736684524574c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e16903d30ebbc69dfd05038eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964": "0x0000", "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e16903deca1f47c95ad9e99492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e16903e7e316712ac574569037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e16903eec11b5fca7cddef5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a000000000afd9ad680e7563894c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10": "0x0f0080c6a47e8d030f0080c6a47e8d0300", "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a000000000e8cb253c2fa33f382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a000000001887a9d70c3dff6e6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a00000000198703ff0791c2de1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a00000000260868bcb77241070efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108": "0x0f0080c6a47e8d030f0080c6a47e8d0300", "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a00000000572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a0000000065903e26e594bcd6d41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a00000000a8a26ecf38bdcfc668728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a00000000ab8d3e1fa99ea7e2d0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c": "0x0f0080c6a47e8d030f0080c6a47e8d0300", "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a00000000d12736684524574c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a00000000d30ebbc69dfd05038eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964": "0x0f0080c6a47e8d030f0080c6a47e8d0300", "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a00000000deca1f47c95ad9e99492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a00000000e7e316712ac574569037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a00000000eec11b5fca7cddef5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade980afd9ad680e7563894c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10": "0x00", "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade980e8cb253c2fa33f382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x00", + "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade981887a9d70c3dff6e6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627": "0x00", + "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade98198703ff0791c2de1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413": "0x00", + "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade98260868bcb77241070efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108": "0x00", "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade98572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x00", + "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade9865903e26e594bcd6d41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d": "0x00", + "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade98a8a26ecf38bdcfc668728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69": "0x00", + "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade98ab8d3e1fa99ea7e2d0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c": "0x00", "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade98d12736684524574c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x00", + "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade98d30ebbc69dfd05038eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964": "0x00", "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade98deca1f47c95ad9e99492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x00", + "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade98e7e316712ac574569037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74": "0x00", + "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade98eec11b5fca7cddef5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c": "0x00", "0x5f3e4907f716ac89b6347d15ececedca98c2640cda6c0d801194a8a61c699224": "0xc8000000", - "0x5f3e4907f716ac89b6347d15ececedcaa141c4fe67c2d11f4a10c6aca7a79a04b4def25cfda6ef3a00000000": "0x00001a93fa350e000000000000000000", + "0x5f3e4907f716ac89b6347d15ececedcaa141c4fe67c2d11f4a10c6aca7a79a04b4def25cfda6ef3a00000000": "0x0000db02edbc31000000000000000000", "0x5f3e4907f716ac89b6347d15ececedcaad811cd65a470ddc5f1d628ff0550982b4def25cfda6ef3a00000000": "0x00000000", "0x5f3e4907f716ac89b6347d15ececedcab49a2738eeb30896aacb8b3fb46471bd": "0x02000000", "0x5f3e4907f716ac89b6347d15ececedcac0d39ff577af2cc6b67ac3641fa9c4e7": "0x01000000", @@ -145,18 +234,29 @@ "0x6ac983d82528bf1595ab26438ae5b2cf4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x7474449cca95dc5d0c00e71735a6d17d4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", "0x74dd702da46f77d7acf77f5a48d4af7d4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b150e8cb253c2fa33f382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c01043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d0132eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e115261c091f7b7cb03000080c6a47e8d0300", - "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d000182c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c61c091f7b7cb03000080c6a47e8d0300", - "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15d12736684524574c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e11520182c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c019492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f61c091f7b7cb03000080c6a47e8d0300", - "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15deca1f47c95ad9e99492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f0132eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e11520061c091f7b7cb03000080c6a47e8d0300", - "0x74dd702da46f77d7acf77f5a48d4af7d7a6dc62e324093ba1331bf49fdb2f24a": "0x04000000", - "0x74dd702da46f77d7acf77f5a48d4af7de5c03730c8f59f00941607850b6633d80863c87cd8a129fa61c091f7b7cb0300": "0x01043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d019492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b150afd9ad680e7563894c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10": "0x94c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10000182c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c61c091f7b7cb03000080c6a47e8d0300", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b150e8cb253c2fa33f382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c0194c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10016ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a225962761c091f7b7cb03000080c6a47e8d0300", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b151887a9d70c3dff6e6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627": "0x6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a22596270182c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c011e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c41361c091f7b7cb03000080c6a47e8d0300", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15198703ff0791c2de1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413": "0x1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413016ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627010efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f7610861c091f7b7cb03000080c6a47e8d0300", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15260868bcb77241070efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108": "0x0efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108011e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c41301043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d61c091f7b7cb03000080c6a47e8d0300", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d010efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f7610801d41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d61c091f7b7cb03000080c6a47e8d0300", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b1565903e26e594bcd6d41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d": "0xd41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d01043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d0168728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c6961c091f7b7cb03000080c6a47e8d0300", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15a8a26ecf38bdcfc668728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69": "0x68728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c6901d41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d01d0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c61c091f7b7cb03000080c6a47e8d0300", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15ab8d3e1fa99ea7e2d0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c": "0xd0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c0168728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c690132eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e115261c091f7b7cb03000080c6a47e8d0300", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15d12736684524574c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e115201d0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c018eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b86896461c091f7b7cb03000080c6a47e8d0300", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15d30ebbc69dfd05038eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964": "0x8eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b8689640132eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152019492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f61c091f7b7cb03000080c6a47e8d0300", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15deca1f47c95ad9e99492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f018eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964019037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af7461c091f7b7cb03000080c6a47e8d0300", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15e7e316712ac574569037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74": "0x9037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74019492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f015270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c61c091f7b7cb03000080c6a47e8d0300", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15eec11b5fca7cddef5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c": "0x5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c019037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af740061c091f7b7cb03000080c6a47e8d0300", + "0x74dd702da46f77d7acf77f5a48d4af7d7a6dc62e324093ba1331bf49fdb2f24a": "0x0e000000", + "0x74dd702da46f77d7acf77f5a48d4af7de5c03730c8f59f00941607850b6633d80863c87cd8a129fa61c091f7b7cb0300": "0x0194c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10015270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c", "0x7a6d38deaa01cb6e76ee69889f1696272be9a4e88368a2188d2b9100a9f3cd43": "0x00000000000000000000000000000000", "0x7a6d38deaa01cb6e76ee69889f16962730256ea2c545a3e5e3744665ffb2ed28": "0x00020000", "0x7a6d38deaa01cb6e76ee69889f1696273f0d64e1907361c689834a9c1cb0fbe0": "0x20000000", "0x7a6d38deaa01cb6e76ee69889f16962749d67997de33812a1cc37310f765b82e": "0x00000000000000000000000000000000", - "0x7a6d38deaa01cb6e76ee69889f1696274e7b9012096b41c4eb3aaf947f6ea429": "0x0500", + "0x7a6d38deaa01cb6e76ee69889f1696274e7b9012096b41c4eb3aaf947f6ea429": "0x0700", "0x7a6d38deaa01cb6e76ee69889f169627ba93302f3b868c50785e6ade45c6a1d8": "0x10000000", + "0x8671567f6bbc0021f6f23105f33002a84e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x89d139e01a5eb2256f222e5fc5dbe6b34e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x94eadf0156a8ad5156507773d0471e4a16973e1142f5bd30d9464076794007db": "0x00", "0x94eadf0156a8ad5156507773d0471e4a4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", @@ -166,52 +266,147 @@ "0x9c5d795d0297be56027a4b2464e33397f43d6436dec51f09c3b71287a8fc9d48": "0x00000000000000000000000000000000", "0xa0eb495036d368196a2b6c51d9d788814e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0xa37f719efab16103103a0c8c2c784ce14e7b9012096b41c4eb3aaf947f6ea429": "0x0400", - "0xb341e3a63e58a188839b242d17f8c9f82586833f834350b4d435d5fd269ecc8b": "0x1003000000010000000000000002000000", + "0xa8c65209d47ee80f56b0011e8fd91f504e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xb341e3a63e58a188839b242d17f8c9f82586833f834350b4d435d5fd269ecc8b": "0x380a000000030000000400000002000000070000000b000000050000000c0000000000000009000000060000000d0000000100000008000000", "0xb341e3a63e58a188839b242d17f8c9f84e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xb341e3a63e58a188839b242d17f8c9f87a50c904b368210021127f9238883a6e": "0x10ea9400f05e7fb75a3f7a92febbf58e5a3060dd06132ed6d5d68a3d75ec452826ad90a2c3fa2c756f974628dd279adb87935f7ea509856276e3b86f759b22451c161d0af40e6efc165c17d0189bd2d770bdfa0a9b8393cb89113f473a2e948c682253ee3c02d89582602ca5b0570cfc01dc82cc8d1b9d2071eb5db6318749124b", + "0xb341e3a63e58a188839b242d17f8c9f87a50c904b368210021127f9238883a6e": "0x38ea9400f05e7fb75a3f7a92febbf58e5a3060dd06132ed6d5d68a3d75ec452826ad90a2c3fa2c756f974628dd279adb87935f7ea509856276e3b86f759b22451c303094c583e253794c9db14803585baaa74472f4ecba846defefc8aecfb6214ac45a6d0878f808b1baaa85dcfb4e930ae06e3205bb38855527aee6f259e3327b2253ee3c02d89582602ca5b0570cfc01dc82cc8d1b9d2071eb5db6318749124b02683131f96baec9121383995904c49a02ce2c2451f8038291e5db2dce66663e0a988fb965b156a07debf072fd9d34a9c7c0fc0e0ff5bd63ef766afb76e2b32890032c39c968f486f77f8764301a8479206f063d49eeb9f6d499333e2a1be045161d0af40e6efc165c17d0189bd2d770bdfa0a9b8393cb89113f473a2e948c680ce84accce1ced0de223aa72f760f1b3d13ddfd267938cd63e25308378d320084e1a59090261a7e6bd82544df1eebd96dc87b4eb1211346645fa44d6b932960b441629077e228528f91ca7dc17051bb437408a5ae272d0950e58961846a8fc2ed0d052eca7d732d9f560ba970ca48f67387b899e76958ea6ed342a3a553ef0229e065eea4143325fbbd26967c26a228d51a3a8384062f7434973f15d1da2c010", "0xb341e3a63e58a188839b242d17f8c9f89d1fb17def62216d598940d64654f69e": "0x0000000000", "0xb341e3a63e58a188839b242d17f8c9f8b5cab3380174032968897a4c3ce57c0a": "0x00000000", "0xb8753e9383841da95f7b8871e5de32694e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc602d496d20c019d22397accfc42b7635d94c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10": "0x047374616b696e67200080c6a47e8d0300000000000000000002", "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc60ffdb9747f7a8bfa200ea2291eea6bdf9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x047374616b696e67200080c6a47e8d0300000000000000000002", + "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc611fcf3e922de10898cb04273301735e65270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c": "0x047374616b696e67200080c6a47e8d0300000000000000000002", + "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc615fbc5d9b44885a2dce0fbaa3834ba211e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413": "0x047374616b696e67200080c6a47e8d0300000000000000000002", + "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc6279effc2672354478fb5e3f7e65726b668728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69": "0x047374616b696e67200080c6a47e8d0300000000000000000002", + "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc62ffe7c0dd7d3d8d8485c403a33fb7adb6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627": "0x047374616b696e67200080c6a47e8d0300000000000000000002", + "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc6350ea875b4c0459b3527d475e9825c99d41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d": "0x047374616b696e67200080c6a47e8d0300000000000000000002", "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc64d176c4abc6d2410c43423c2714b909c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x047374616b696e67200080c6a47e8d0300000000000000000002", + "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc6585bd5ccc1af6cf60c19d791bb091b750efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108": "0x047374616b696e67200080c6a47e8d0300000000000000000002", "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc665bb58c29c9825c1f6727fe8c41650ac82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x047374616b696e67200080c6a47e8d0300000000000000000002", + "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc66deaa967a94fa09152be01a253511479d0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c": "0x047374616b696e67200080c6a47e8d0300000000000000000002", "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc6829f3f4d6d7c991993e0c044ae97410b043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x047374616b696e67200080c6a47e8d0300000000000000000002", + "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc6ba2db252d1b8c64ec51b04d0f50d7d2b9037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74": "0x047374616b696e67200080c6a47e8d0300000000000000000002", + "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc6dfc40f350cc4563ad9ddff5ad92944278eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964": "0x047374616b696e67200080c6a47e8d0300000000000000000002", "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00e473ba7fd26e0e0000000000000000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00e4be87ea9ef50f0000000000000000", "0xca32a41f4b3ed515863dc0a38697f84e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0xcd710b30bd2eab0352ddcc26417aa1944e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0xcd710b30bd2eab0352ddcc26417aa1949f4993f016e2d2f8e5f43be7bb259486": "0x00", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb30e8cb253c2fa33f382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x4c669b04865e9acaf7b72bdfcb0099d70d9ec63c8c2d6b8cb0552815d7b50a0afacb2f987caac6c1290a9784b1efdba78343d39aed805addb12945efbe444000ca3c2703db1633a27eff681d979967988c3a6752c669fd41f1abde10f3b054462253ee3c02d89582602ca5b0570cfc01dc82cc8d1b9d2071eb5db6318749124bf0e6c42698fffc28f9fc769fddcdf165af54c171cde43690cc8f73c853de1f0426e2fc857945d01520797a75388c58e710c9fefedd28387af70880f1682be41e", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0xc8caee6f6eddc41c6cc55e554343392cbc13d2a8a57b97f6f85fc965bdd20ce8b07d600e3487e2712dcc3879c7b17c9b29cd2243b45f0d9343c591b89cf82a650edf2a41cb81178704560b02c35f5e01a5a97a568ebc10c025ade18b6ab2fa1d161d0af40e6efc165c17d0189bd2d770bdfa0a9b8393cb89113f473a2e948c68def964eed9a73f8a6610f1a0373378dca6f277eb7787869ed5841893105ad930f89c97bf5b2c07c05c84eebce4ffc7b28766946c03741fd1a71fdae0942e8768", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3d12736684524574c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x8270a62b61639ee56113834aecec01de6cda91413a5111b89f74d6585da34f5058108e1651614afc6a535c426fc013945e93533faa33819fe4e69423fe32330274bd654c470ed9b94972c1f997593fab7bdd4d6b85e3cf49401265668142584ead90a2c3fa2c756f974628dd279adb87935f7ea509856276e3b86f759b22451cc083b0d0bd7d6ffd14562b4c9e28738b087ccc32262170c633c18359ff84877992cb05c48fc643f057626c669604675c5ad5a836266f260ae7030c6fdc17a543", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3deca1f47c95ad9e99492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0xc9a68a26e9aa37ba6334f1a20275e3be7d3a9d4aa988627eadac8ea0d0a2dfbfae240842b74e5dd778972e451558134f434c7a1d8a52bc70519f38054e24553306bd8fd81e50cda2bd67bf6893d921d1aae5cb08409ae43e0bff4d54e1830e58ea9400f05e7fb75a3f7a92febbf58e5a3060dd06132ed6d5d68a3d75ec452826bed3b452f869d187be58a4ba98588611084283810728fa75981e792beaec4151763d070989ead31f265b40cc7a0cd29d47799b766d6a7f084e44c82baedfc01e", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb30afd9ad680e7563894c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10": "0x6e60f1e253735fb113c183fa603f591e4456435171f387c0849001b428b5ccb118bd0f67d77f04f1a92400421813d8927fad109b40a8689254a5f0c8b346857cd28145a7cde195a4c834276730d30f074b212a150e770931ee9470e853e7d22402683131f96baec9121383995904c49a02ce2c2451f8038291e5db2dce66663e92ac14f8ad1811cc83861afadf12f3191cca1391f1f3af705977faa2fa2bf46a629f9fd0dd7279c7af7470472d1208a13e33239b484974d47cffce4ad47856440333022898140662dfea847e3cbfe5e989845ac6766e83472f8b0c650d85e77bae", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb30e8cb253c2fa33f382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x4c669b04865e9acaf7b72bdfcb0099d70d9ec63c8c2d6b8cb0552815d7b50a0afacb2f987caac6c1290a9784b1efdba78343d39aed805addb12945efbe444000ca3c2703db1633a27eff681d979967988c3a6752c669fd41f1abde10f3b054462253ee3c02d89582602ca5b0570cfc01dc82cc8d1b9d2071eb5db6318749124bf0e6c42698fffc28f9fc769fddcdf165af54c171cde43690cc8f73c853de1f0426e2fc857945d01520797a75388c58e710c9fefedd28387af70880f1682be41e02182879ec92e811e2a8cc117f3cde1f61d3cba0093134cfb1ed17a4ef74915d4a", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb31887a9d70c3dff6e6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627": "0x0509f9caf32fda5584343c473b386c433acb99fd9400724b8cf3c618d840133fb2efe7e70daf44b3466c63ccbf4487f42c6a9f6fbb7050b849691e36ce92e347bef47a9e4b47ed57461e1d28cac7da327a52ebcd64d74080d31deb3ac7a7645e4e1a59090261a7e6bd82544df1eebd96dc87b4eb1211346645fa44d6b932960be4ca45c68b0248885d190d22068c6628ee2f00d9fa0706d5a5c1c8456369f03eaa3955187f755708cd6a8104314b962ff5043e36efa3ec5d84df40c58b44222102f4f4d0eccb899bf2d611b56e0afec7c740efba404f8d0e82a545f988c45316c4", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3198703ff0791c2de1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413": "0x5fafb6219eb8d463bec0370b2aab69f45fc780959fc2eddbc7703760aa34202274dacbca0cdb5099afef67e622c147614198e669931cebc605629da332632473b414aa148096a92a1831309f758f944725653363ccbaeb21817b7df5784b8d46c45a6d0878f808b1baaa85dcfb4e930ae06e3205bb38855527aee6f259e3327b2e2f75472708a497d1743f52b04edf26c250d9e6d220f3bae3d176f02f8e586c2ada042fb4bbfd9b6d8c48293ffc4a7722632c843a67e608554c41d06aabc41303a0af06322d100056125fac1df39d161089b07ae279505aae8731c4d110a54ad7", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3260868bcb77241070efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108": "0x83f43ee3e4521b55de0fe830847fda88a6b017b87979af1a41b180c39da1e4b05440add43e5388a81aef665c9086d386c0be0ce75e4f8a4a3d8168e976ea821f98aab6f52520022d011a6eba2dca1c6327edbbcd753c170dcf0e9118b5f0f25bd0d052eca7d732d9f560ba970ca48f67387b899e76958ea6ed342a3a553ef0223e547f5cc3455a61d0404d7296ceec7375cbe20322109d118c5e725b1a5cbf04cce3ec06252cf7cdad47fe1265047a9bbddb9059ee4bdc6dec83b67249b4a93403f045328f504c13dac9ddd9b1186098aee7c46cb8d55289dbbf2433bab7a26239", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0xc8caee6f6eddc41c6cc55e554343392cbc13d2a8a57b97f6f85fc965bdd20ce8b07d600e3487e2712dcc3879c7b17c9b29cd2243b45f0d9343c591b89cf82a650edf2a41cb81178704560b02c35f5e01a5a97a568ebc10c025ade18b6ab2fa1d161d0af40e6efc165c17d0189bd2d770bdfa0a9b8393cb89113f473a2e948c68def964eed9a73f8a6610f1a0373378dca6f277eb7787869ed5841893105ad930f89c97bf5b2c07c05c84eebce4ffc7b28766946c03741fd1a71fdae0942e87680325fc2095902f5fe394f244bce38b0dc3d631cbc05f0b64d5620a71bbf2514f0f", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb365903e26e594bcd6d41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d": "0x64adb43a7628139f6c02100f6a5465dbd33422418426c572b12547c5a665008c1ab03b1b3277edfedd24ef3d3359b449b64bd95ed82a04e7f9fbaab7b71dc0152cd12c731d91441f0114b08d314cd3f9a9f7fd0240d467fe54adefbee4d90762441629077e228528f91ca7dc17051bb437408a5ae272d0950e58961846a8fc2ec22ce14ba0e59aa974d4a05c9208ba5ae18674c6a23c9998d91e7d1ebea7e06b9a86227e204a2d003399c2a3b50c2c869c4380c195a014a02f6d2e7048941237029a1eb2e31dcaf468dbb516f9b620fdd7c3f090d58a88e02b51b25255b2182dd1", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3a8a26ecf38bdcfc668728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69": "0x6e309dfa4c8de814cad140b8612a9e41bfba244f9ab1468e1b5d9b3cc1f5e565c44b3e8efe854419ccd5801a82ada22d39cfccdbcece382304cdfeac81ebe402a8a03d86e6c0dbe180cadfc7994121f462b28f7a8cb1be7e0e354147624be7340a988fb965b156a07debf072fd9d34a9c7c0fc0e0ff5bd63ef766afb76e2b3289ef622d2467ed115fa0c6c86303e1ef6a08a0609c97e616aa69b026a6d3f2663729053f28155071474b4686323db5f7a318cb3f088b76660cc8ff5e3e11ec32e030e901c390fa37d101ff25d70594acd2df67b4493ee77a73684f25d39313536d7", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3ab8d3e1fa99ea7e2d0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c": "0xc8de3a01502422b59dfa601c9c3a04a98d2bfbd79dd0810d1d1250feab4241ee6248d87bd2a640ffe26d6b831735887c24e2076a3a0f3a74f7ae7568c27604087c03ca47a3201455f8f89defda4aa909cb1d25dd9ddb7fd62a940606f79b566390032c39c968f486f77f8764301a8479206f063d49eeb9f6d499333e2a1be045165f3b255dc17054e6d4447c4005f689eb5ed2f99fe201f4ff799bf088495850047277f22b9ef92a8b99618f4c86c2412f0e3b08a4f965f842775672043d1e2503586dafcdab3d4647d4dc68732a9cab8aa34c00c5edd04e65d9dd44c2a1fd21e2", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3d12736684524574c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x8270a62b61639ee56113834aecec01de6cda91413a5111b89f74d6585da34f5058108e1651614afc6a535c426fc013945e93533faa33819fe4e69423fe32330274bd654c470ed9b94972c1f997593fab7bdd4d6b85e3cf49401265668142584ead90a2c3fa2c756f974628dd279adb87935f7ea509856276e3b86f759b22451cc083b0d0bd7d6ffd14562b4c9e28738b087ccc32262170c633c18359ff84877992cb05c48fc643f057626c669604675c5ad5a836266f260ae7030c6fdc17a54303aec8e80ea0375f8669d6e55d7abb6a3117678d7bb851a1bd100a01e52a4fed90", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3d30ebbc69dfd05038eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964": "0xe41426f7465c13c48335771c5450bf61c50a9cf28b9274f170c7421eea7974f168a9ec74fa35b3425eaf503dd36294ba8e758e7b8084c4d6bfd547f8c6b5827460fcc9d094d21fe17cfb7426501f50cb3d75c4c9395a3140e0f255443f660d3b9e065eea4143325fbbd26967c26a228d51a3a8384062f7434973f15d1da2c01068f7a83678a377701b46a5e6a4637e99868186ff4835fc0e3914cc56a76a360164ffc83f4f86cc595e607a00b977eeb6641e02a4e6e556c24ab163aecd7d146c03e843f200e30bc5b951c73a96d968db1c0cd05e357d910fce159fc59c40e9d6e2", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3deca1f47c95ad9e99492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0xc9a68a26e9aa37ba6334f1a20275e3be7d3a9d4aa988627eadac8ea0d0a2dfbfae240842b74e5dd778972e451558134f434c7a1d8a52bc70519f38054e24553306bd8fd81e50cda2bd67bf6893d921d1aae5cb08409ae43e0bff4d54e1830e58ea9400f05e7fb75a3f7a92febbf58e5a3060dd06132ed6d5d68a3d75ec452826bed3b452f869d187be58a4ba98588611084283810728fa75981e792beaec4151763d070989ead31f265b40cc7a0cd29d47799b766d6a7f084e44c82baedfc01e029338ece1c6bc6439dc4d16bfe33f28c9f0af31626bb8142849742b7a624f6807", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3e7e316712ac574569037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74": "0xace46e899b90e75199549d8fa2ae7097e896ab3c845217e3155f99b6ffb888037c8348ec95a0faad6a638ef74864761028c53221bde07e9ff7c81a3f427abf3f30c40adee5476157ef3c2a26e10cab95ec7d54b62dd220738f5a474d5f86874e0ce84accce1ced0de223aa72f760f1b3d13ddfd267938cd63e25308378d32008eed33645cda7812cd343bbaef9131b2794812f2fd37701ccb6cddf9c1e293d387aeb767131602e6612e607a9eb8e26b4ce4fa4765593d032bc923ce8acadda420203d51bba2124f480e3507eb1764fc3019ac7abae8ee215683a285078bda7f51d", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3eec11b5fca7cddef5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c": "0x622c382187c0b2c61ecfb17443294d11a9d2ab770ae6f1fb49184a43906d59fe50412bd7d3f1075e8f8e3b682d05ea20b391c287d8c849a0e49a78f568553e6994848b8cf2cbc9e6fd72db8d80676591b5be4d1ec68972ada48cf6fd01228712303094c583e253794c9db14803585baaa74472f4ecba846defefc8aecfb6214ab400c4164d016282b202c1d42d9dc8ede28cbe4b751d463bab5f23fef42b295a8c15606f4c121376097ff0e96c2a33ea7b024d812b42fe2c741c8b8cee17e63d03d46c454f9b620603feef8c3a2a5d7098205b8566500fda0fa0b456d6ded54538", "0xcec5070d609dd3497f72bde07fc96ba04e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19500ad445de53dc0cfe6175646980729053f28155071474b4686323db5f7a318cb3f088b76660cc8ff5e3e11ec32e": "0x68728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19501153f927dde4f0796173676e80f0e6c42698fffc28f9fc769fddcdf165af54c171cde43690cc8f73c853de1f04": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195012ba4e8c9e90550f6173676e80b400c4164d016282b202c1d42d9dc8ede28cbe4b751d463bab5f23fef42b295a": "0x5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195016108068be661c046772616e806e309dfa4c8de814cad140b8612a9e41bfba244f9ab1468e1b5d9b3cc1f5e565": "0x68728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195016d5d491326e20327061726180ea9400f05e7fb75a3f7a92febbf58e5a3060dd06132ed6d5d68a3d75ec452826": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950172bf7b1d4fd362d62616265806248d87bd2a640ffe26d6b831735887c24e2076a3a0f3a74f7ae7568c2760408": "0xd0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19501aeb8a0909a0259a696d6f6e8098aab6f52520022d011a6eba2dca1c6327edbbcd753c170dcf0e9118b5f0f25b": "0x0efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950206946fcbcbb55c06772616e800509f9caf32fda5584343c473b386c433acb99fd9400724b8cf3c618d840133f": "0x6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195022df7698c94798dc6261626580facb2f987caac6c1290a9784b1efdba78343d39aed805addb12945efbe444000": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19502bf84b6fe354ffa4696d6f6e80bef47a9e4b47ed57461e1d28cac7da327a52ebcd64d74080d31deb3ac7a7645e": "0x6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19502d8e55cbf27bd5fa6175646980763d070989ead31f265b40cc7a0cd29d47799b766d6a7f084e44c82baedfc01e": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195033918a92ec5a0337626162658068a9ec74fa35b3425eaf503dd36294ba8e758e7b8084c4d6bfd547f8c6b58274": "0x8eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950365a4d707a6ade5270617261800a988fb965b156a07debf072fd9d34a9c7c0fc0e0ff5bd63ef766afb76e2b328": "0x68728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195036942a186c91d1fb6772616e80e41426f7465c13c48335771c5450bf61c50a9cf28b9274f170c7421eea7974f1": "0x8eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950377ac9e1db771a9a696d6f6e8060fcc9d094d21fe17cfb7426501f50cb3d75c4c9395a3140e0f255443f660d3b": "0x8eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19503a73714b16fa1b3b62616265805440add43e5388a81aef665c9086d386c0be0ce75e4f8a4a3d8168e976ea821f": "0x0efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195042faced8513a5a6162656566840325fc2095902f5fe394f244bce38b0dc3d631cbc05f0b64d5620a71bbf2514f0f": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195047d6f38d0fa588e4617564698026e2fc857945d01520797a75388c58e710c9fefedd28387af70880f1682be41e": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950486e2fc6fe1baf7d62656566840333022898140662dfea847e3cbfe5e989845ac6766e83472f8b0c650d85e77bae": "0x94c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195048ac54571b914843626565668403e843f200e30bc5b951c73a96d968db1c0cd05e357d910fce159fc59c40e9d6e2": "0x8eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19504d5070934a9a468e6261626580b07d600e3487e2712dcc3879c7b17c9b29cd2243b45f0d9343c591b89cf82a65": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19504f6e7e79cc9e4f1c6772616e8064adb43a7628139f6c02100f6a5465dbd33422418426c572b12547c5a665008c": "0xd41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19504fbe8be23f344f7a6173676e809ef622d2467ed115fa0c6c86303e1ef6a08a0609c97e616aa69b026a6d3f2663": "0x68728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19505214a412d15781646173676e8068f7a83678a377701b46a5e6a4637e99868186ff4835fc0e3914cc56a76a3601": "0x8eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950523f922d3cd709c261756469809a86227e204a2d003399c2a3b50c2c869c4380c195a014a02f6d2e7048941237": "0xd41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195053557db7980560f461756469802ada042fb4bbfd9b6d8c48293ffc4a7722632c843a67e608554c41d06aabc413": "0x1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19505bb3161fb65601ff626162658058108e1651614afc6a535c426fc013945e93533faa33819fe4e69423fe323302": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19505d3dc734864e36256265656684029338ece1c6bc6439dc4d16bfe33f28c9f0af31626bb8142849742b7a624f6807": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19505d9ac86ecdf920c170617261804e1a59090261a7e6bd82544df1eebd96dc87b4eb1211346645fa44d6b932960b": "0x6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195062a6c483038fd12d6265656684030e901c390fa37d101ff25d70594acd2df67b4493ee77a73684f25d39313536d7": "0x68728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195064cc468ab603f8926772616e80c9a68a26e9aa37ba6334f1a20275e3be7d3a9d4aa988627eadac8ea0d0a2dfbf": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195065ccc9ce9f581b2c61756469807aeb767131602e6612e607a9eb8e26b4ce4fa4765593d032bc923ce8acadda42": "0x9037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950678c21325827e7c66173676e802e2f75472708a497d1743f52b04edf26c250d9e6d220f3bae3d176f02f8e586c": "0x1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19506792a176f707220962616265801ab03b1b3277edfedd24ef3d3359b449b64bd95ed82a04e7f9fbaab7b71dc015": "0xd41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195067f9bb125e8b25cf696d6f6e802cd12c731d91441f0114b08d314cd3f9a9f7fd0240d467fe54adefbee4d90762": "0xd41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195068d2c0f060d8e93b6772616e806e60f1e253735fb113c183fa603f591e4456435171f387c0849001b428b5ccb1": "0x94c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195069561cd534084d42696d6f6e80b414aa148096a92a1831309f758f944725653363ccbaeb21817b7df5784b8d46": "0x1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950717bb541f3dc932d6772616e80c8de3a01502422b59dfa601c9c3a04a98d2bfbd79dd0810d1d1250feab4241ee": "0xd0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19507180ab4d5ffe8c79706172618090032c39c968f486f77f8764301a8479206f063d49eeb9f6d499333e2a1be045": "0xd0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195073b0d3d9242f219d70617261800ce84accce1ced0de223aa72f760f1b3d13ddfd267938cd63e25308378d32008": "0x9037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19507466b8cede2256a27061726180ad90a2c3fa2c756f974628dd279adb87935f7ea509856276e3b86f759b22451c": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950749c8bce91e643ae6772616e808270a62b61639ee56113834aecec01de6cda91413a5111b89f74d6585da34f50": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950795802224826305461756469808c15606f4c121376097ff0e96c2a33ea7b024d812b42fe2c741c8b8cee17e63d": "0x5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19507e5838eaecbd9fe66175646980629f9fd0dd7279c7af7470472d1208a13e33239b484974d47cffce4ad4785644": "0x94c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195085436b71cc38efc86173676e80e4ca45c68b0248885d190d22068c6628ee2f00d9fa0706d5a5c1c8456369f03e": "0x6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195087f680d5c3a7dcbf617564698064ffc83f4f86cc595e607a00b977eeb6641e02a4e6e556c24ab163aecd7d146c": "0x8eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950892d429df08b8e486772616e805fafb6219eb8d463bec0370b2aab69f45fc780959fc2eddbc7703760aa342022": "0x1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19508cec3cc48ae13ec86173676e80def964eed9a73f8a6610f1a0373378dca6f277eb7787869ed5841893105ad930": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195090ddc04de598f6716772616e8083f43ee3e4521b55de0fe830847fda88a6b017b87979af1a41b180c39da1e4b0": "0x0efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950961fc927b0be61a66175646980f89c97bf5b2c07c05c84eebce4ffc7b28766946c03741fd1a71fdae0942e8768": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195097b423b109a1a3e7626565668403a0af06322d100056125fac1df39d161089b07ae279505aae8731c4d110a54ad7": "0x1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19509990e89dc7508f8f626565668403d46c454f9b620603feef8c3a2a5d7098205b8566500fda0fa0b456d6ded54538": "0x5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19509c34aadd3112cff46772616e80c8caee6f6eddc41c6cc55e554343392cbc13d2a8a57b97f6f85fc965bdd20ce8": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19509cc6d5a70d4342de706172618002683131f96baec9121383995904c49a02ce2c2451f8038291e5db2dce66663e": "0x94c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19509d726f19fb7e918f6173676e80c083b0d0bd7d6ffd14562b4c9e28738b087ccc32262170c633c18359ff848779": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19509f08b41b3412bdf7696d6f6e8074bd654c470ed9b94972c1f997593fab7bdd4d6b85e3cf49401265668142584e": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19509f796bfaa6092095696d6f6e800edf2a41cb81178704560b02c35f5e01a5a97a568ebc10c025ade18b6ab2fa1d": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950a0aff76314285afe6173676e803e547f5cc3455a61d0404d7296ceec7375cbe20322109d118c5e725b1a5cbf04": "0x0efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950a39a50629306361270617261809e065eea4143325fbbd26967c26a228d51a3a8384062f7434973f15d1da2c010": "0x8eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950a69397f216a7497a6261626580ae240842b74e5dd778972e451558134f434c7a1d8a52bc70519f38054e245533": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950a8670a2360fb4f7d626565668402f4f4d0eccb899bf2d611b56e0afec7c740efba404f8d0e82a545f988c45316c4": "0x6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950ab8402a39318e10f70617261802253ee3c02d89582602ca5b0570cfc01dc82cc8d1b9d2071eb5db6318749124b": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950aba2e02edcf42d916175646980cce3ec06252cf7cdad47fe1265047a9bbddb9059ee4bdc6dec83b67249b4a934": "0x0efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950ac3745478a256f54696d6f6e8094848b8cf2cbc9e6fd72db8d80676591b5be4d1ec68972ada48cf6fd01228712": "0x5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950ae71ab667a6367956173676e80bed3b452f869d187be58a4ba98588611084283810728fa75981e792beaec4151": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950aee25d4290ea4d846772616e804c669b04865e9acaf7b72bdfcb0099d70d9ec63c8c2d6b8cb0552815d7b50a0a": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950b090c0a6a6d34dc97061726180441629077e228528f91ca7dc17051bb437408a5ae272d0950e58961846a8fc2e": "0xd41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950b40a2e291d3757e66265656684029a1eb2e31dcaf468dbb516f9b620fdd7c3f090d58a88e02b51b25255b2182dd1": "0xd41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950b469b5316f2df49e626162658018bd0f67d77f04f1a92400421813d8927fad109b40a8689254a5f0c8b346857c": "0x94c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950b6da07aad040c4596772616e80ace46e899b90e75199549d8fa2ae7097e896ab3c845217e3155f99b6ffb88803": "0x9037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950b9847e90f2a93d58626565668403aec8e80ea0375f8669d6e55d7abb6a3117678d7bb851a1bd100a01e52a4fed90": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950bad6b6ae80666780696d6f6e8006bd8fd81e50cda2bd67bf6893d921d1aae5cb08409ae43e0bff4d54e1830e58": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950bd1ab2cd6f1dc5a6696d6f6e80d28145a7cde195a4c834276730d30f074b212a150e770931ee9470e853e7d224": "0x94c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950bdd996f23001764e62656566840203d51bba2124f480e3507eb1764fc3019ac7abae8ee215683a285078bda7f51d": "0x9037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950be81b84f18801f516173676e80165f3b255dc17054e6d4447c4005f689eb5ed2f99fe201f4ff799bf088495850": "0xd0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950bef85ee78e59fe8a6173676e80eed33645cda7812cd343bbaef9131b2794812f2fd37701ccb6cddf9c1e293d38": "0x9037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950bfb5b848b461277c7061726180c45a6d0878f808b1baaa85dcfb4e930ae06e3205bb38855527aee6f259e3327b": "0x1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950c1cabf91e699980a6261626580b2efe7e70daf44b3466c63ccbf4487f42c6a9f6fbb7050b849691e36ce92e347": "0x6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950c2bef1fa94927270626565668403f045328f504c13dac9ddd9b1186098aee7c46cb8d55289dbbf2433bab7a26239": "0x0efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950c3b366be127c04d76772616e80622c382187c0b2c61ecfb17443294d11a9d2ab770ae6f1fb49184a43906d59fe": "0x5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950c61ff1db24ad5e86626565668402182879ec92e811e2a8cc117f3cde1f61d3cba0093134cfb1ed17a4ef74915d4a": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950cb18236e84f2bcc8626565668403586dafcdab3d4647d4dc68732a9cab8aa34c00c5edd04e65d9dd44c2a1fd21e2": "0xd0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950d1c1264edac2bfeb7061726180303094c583e253794c9db14803585baaa74472f4ecba846defefc8aecfb6214a": "0x5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950d4b8d8139a9d2a3862616265807c8348ec95a0faad6a638ef74864761028c53221bde07e9ff7c81a3f427abf3f": "0x9037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950d5b902094e722e906261626580c44b3e8efe854419ccd5801a82ada22d39cfccdbcece382304cdfeac81ebe402": "0x68728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950d74338095b2b3a5f7061726180161d0af40e6efc165c17d0189bd2d770bdfa0a9b8393cb89113f473a2e948c68": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950d84b213ff11dcdc96175646980047277f22b9ef92a8b99618f4c86c2412f0e3b08a4f965f842775672043d1e25": "0xd0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950d912f1fd94200c50696d6f6e8030c40adee5476157ef3c2a26e10cab95ec7d54b62dd220738f5a474d5f86874e": "0x9037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950db189b9054ee003e6175646980aa3955187f755708cd6a8104314b962ff5043e36efa3ec5d84df40c58b442221": "0x6ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a2259627", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950dd45ac2fed4ab0db6173676e8092ac14f8ad1811cc83861afadf12f3191cca1391f1f3af705977faa2fa2bf46a": "0x94c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950dfe86daa30500684696d6f6e80ca3c2703db1633a27eff681d979967988c3a6752c669fd41f1abde10f3b05446": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c", "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950e326352daf693946617564698092cb05c48fc643f057626c669604675c5ad5a836266f260ae7030c6fdc17a543": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152", - "0xcec5070d609dd3497f72bde07fc96ba088dcde934c658227ee1dfafcd6e16903": "0x10043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e115282c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", - "0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609": "0x10043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170dc8caee6f6eddc41c6cc55e554343392cbc13d2a8a57b97f6f85fc965bdd20ce8b07d600e3487e2712dcc3879c7b17c9b29cd2243b45f0d9343c591b89cf82a650edf2a41cb81178704560b02c35f5e01a5a97a568ebc10c025ade18b6ab2fa1d161d0af40e6efc165c17d0189bd2d770bdfa0a9b8393cb89113f473a2e948c68def964eed9a73f8a6610f1a0373378dca6f277eb7787869ed5841893105ad930f89c97bf5b2c07c05c84eebce4ffc7b28766946c03741fd1a71fdae0942e876832eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e11528270a62b61639ee56113834aecec01de6cda91413a5111b89f74d6585da34f5058108e1651614afc6a535c426fc013945e93533faa33819fe4e69423fe32330274bd654c470ed9b94972c1f997593fab7bdd4d6b85e3cf49401265668142584ead90a2c3fa2c756f974628dd279adb87935f7ea509856276e3b86f759b22451cc083b0d0bd7d6ffd14562b4c9e28738b087ccc32262170c633c18359ff84877992cb05c48fc643f057626c669604675c5ad5a836266f260ae7030c6fdc17a54382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c4c669b04865e9acaf7b72bdfcb0099d70d9ec63c8c2d6b8cb0552815d7b50a0afacb2f987caac6c1290a9784b1efdba78343d39aed805addb12945efbe444000ca3c2703db1633a27eff681d979967988c3a6752c669fd41f1abde10f3b054462253ee3c02d89582602ca5b0570cfc01dc82cc8d1b9d2071eb5db6318749124bf0e6c42698fffc28f9fc769fddcdf165af54c171cde43690cc8f73c853de1f0426e2fc857945d01520797a75388c58e710c9fefedd28387af70880f1682be41e9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20fc9a68a26e9aa37ba6334f1a20275e3be7d3a9d4aa988627eadac8ea0d0a2dfbfae240842b74e5dd778972e451558134f434c7a1d8a52bc70519f38054e24553306bd8fd81e50cda2bd67bf6893d921d1aae5cb08409ae43e0bff4d54e1830e58ea9400f05e7fb75a3f7a92febbf58e5a3060dd06132ed6d5d68a3d75ec452826bed3b452f869d187be58a4ba98588611084283810728fa75981e792beaec4151763d070989ead31f265b40cc7a0cd29d47799b766d6a7f084e44c82baedfc01e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950ec74f4b6dbe6f07e626162658074dacbca0cdb5099afef67e622c147614198e669931cebc605629da332632473": "0x1e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c413", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950f10dac37e2a0f812696d6f6e80a8a03d86e6c0dbe180cadfc7994121f462b28f7a8cb1be7e0e354147624be734": "0x68728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c69", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950f39a56e067ee2ba9696d6f6e807c03ca47a3201455f8f89defda4aa909cb1d25dd9ddb7fd62a940606f79b5663": "0xd0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950f87aebc04f6a52ae6173676e80c22ce14ba0e59aa974d4a05c9208ba5ae18674c6a23c9998d91e7d1ebea7e06b": "0xd41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950fc36b10abae8cf4f626162658050412bd7d3f1075e8f8e3b682d05ea20b391c287d8c849a0e49a78f568553e69": "0x5270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950fcf43201be771ed27061726180d0d052eca7d732d9f560ba970ca48f67387b899e76958ea6ed342a3a553ef022": "0x0efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f76108", + "0xcec5070d609dd3497f72bde07fc96ba088dcde934c658227ee1dfafcd6e16903": "0x38043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d0efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f761081e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c41332eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e11525270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c68728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c696ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a225962782c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c8eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b8689649037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af749492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f94c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b10d0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327cd41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d", + "0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609": "0x38043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170dc8caee6f6eddc41c6cc55e554343392cbc13d2a8a57b97f6f85fc965bdd20ce8b07d600e3487e2712dcc3879c7b17c9b29cd2243b45f0d9343c591b89cf82a650edf2a41cb81178704560b02c35f5e01a5a97a568ebc10c025ade18b6ab2fa1d161d0af40e6efc165c17d0189bd2d770bdfa0a9b8393cb89113f473a2e948c68def964eed9a73f8a6610f1a0373378dca6f277eb7787869ed5841893105ad930f89c97bf5b2c07c05c84eebce4ffc7b28766946c03741fd1a71fdae0942e87680325fc2095902f5fe394f244bce38b0dc3d631cbc05f0b64d5620a71bbf2514f0f0efe248e3ddcfcb4f29675b70fc0a8e2db66b65381c45d299427b60d05f7610883f43ee3e4521b55de0fe830847fda88a6b017b87979af1a41b180c39da1e4b05440add43e5388a81aef665c9086d386c0be0ce75e4f8a4a3d8168e976ea821f98aab6f52520022d011a6eba2dca1c6327edbbcd753c170dcf0e9118b5f0f25bd0d052eca7d732d9f560ba970ca48f67387b899e76958ea6ed342a3a553ef0223e547f5cc3455a61d0404d7296ceec7375cbe20322109d118c5e725b1a5cbf04cce3ec06252cf7cdad47fe1265047a9bbddb9059ee4bdc6dec83b67249b4a93403f045328f504c13dac9ddd9b1186098aee7c46cb8d55289dbbf2433bab7a262391e30aa51ad68b8918d2c46e914986818c111bee03582610cbc9fb73fe0e4c4135fafb6219eb8d463bec0370b2aab69f45fc780959fc2eddbc7703760aa34202274dacbca0cdb5099afef67e622c147614198e669931cebc605629da332632473b414aa148096a92a1831309f758f944725653363ccbaeb21817b7df5784b8d46c45a6d0878f808b1baaa85dcfb4e930ae06e3205bb38855527aee6f259e3327b2e2f75472708a497d1743f52b04edf26c250d9e6d220f3bae3d176f02f8e586c2ada042fb4bbfd9b6d8c48293ffc4a7722632c843a67e608554c41d06aabc41303a0af06322d100056125fac1df39d161089b07ae279505aae8731c4d110a54ad732eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e11528270a62b61639ee56113834aecec01de6cda91413a5111b89f74d6585da34f5058108e1651614afc6a535c426fc013945e93533faa33819fe4e69423fe32330274bd654c470ed9b94972c1f997593fab7bdd4d6b85e3cf49401265668142584ead90a2c3fa2c756f974628dd279adb87935f7ea509856276e3b86f759b22451cc083b0d0bd7d6ffd14562b4c9e28738b087ccc32262170c633c18359ff84877992cb05c48fc643f057626c669604675c5ad5a836266f260ae7030c6fdc17a54303aec8e80ea0375f8669d6e55d7abb6a3117678d7bb851a1bd100a01e52a4fed905270ec35ba01254d8bff046a1a58f16d3ae615c235efd6e99a35f233b2d9df2c622c382187c0b2c61ecfb17443294d11a9d2ab770ae6f1fb49184a43906d59fe50412bd7d3f1075e8f8e3b682d05ea20b391c287d8c849a0e49a78f568553e6994848b8cf2cbc9e6fd72db8d80676591b5be4d1ec68972ada48cf6fd01228712303094c583e253794c9db14803585baaa74472f4ecba846defefc8aecfb6214ab400c4164d016282b202c1d42d9dc8ede28cbe4b751d463bab5f23fef42b295a8c15606f4c121376097ff0e96c2a33ea7b024d812b42fe2c741c8b8cee17e63d03d46c454f9b620603feef8c3a2a5d7098205b8566500fda0fa0b456d6ded5453868728d12a90fb1a9f5b0f0b2814d730401d314964113554e66ff19e7067d7c696e309dfa4c8de814cad140b8612a9e41bfba244f9ab1468e1b5d9b3cc1f5e565c44b3e8efe854419ccd5801a82ada22d39cfccdbcece382304cdfeac81ebe402a8a03d86e6c0dbe180cadfc7994121f462b28f7a8cb1be7e0e354147624be7340a988fb965b156a07debf072fd9d34a9c7c0fc0e0ff5bd63ef766afb76e2b3289ef622d2467ed115fa0c6c86303e1ef6a08a0609c97e616aa69b026a6d3f2663729053f28155071474b4686323db5f7a318cb3f088b76660cc8ff5e3e11ec32e030e901c390fa37d101ff25d70594acd2df67b4493ee77a73684f25d39313536d76ed774481ff68097867271bc8ecaeee3500817ccefdfda74ceeafd32a22596270509f9caf32fda5584343c473b386c433acb99fd9400724b8cf3c618d840133fb2efe7e70daf44b3466c63ccbf4487f42c6a9f6fbb7050b849691e36ce92e347bef47a9e4b47ed57461e1d28cac7da327a52ebcd64d74080d31deb3ac7a7645e4e1a59090261a7e6bd82544df1eebd96dc87b4eb1211346645fa44d6b932960be4ca45c68b0248885d190d22068c6628ee2f00d9fa0706d5a5c1c8456369f03eaa3955187f755708cd6a8104314b962ff5043e36efa3ec5d84df40c58b44222102f4f4d0eccb899bf2d611b56e0afec7c740efba404f8d0e82a545f988c45316c482c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c4c669b04865e9acaf7b72bdfcb0099d70d9ec63c8c2d6b8cb0552815d7b50a0afacb2f987caac6c1290a9784b1efdba78343d39aed805addb12945efbe444000ca3c2703db1633a27eff681d979967988c3a6752c669fd41f1abde10f3b054462253ee3c02d89582602ca5b0570cfc01dc82cc8d1b9d2071eb5db6318749124bf0e6c42698fffc28f9fc769fddcdf165af54c171cde43690cc8f73c853de1f0426e2fc857945d01520797a75388c58e710c9fefedd28387af70880f1682be41e02182879ec92e811e2a8cc117f3cde1f61d3cba0093134cfb1ed17a4ef74915d4a8eef6710734f5d1e7d2eb303fa8f04e9bef65fb680647b24624723f95b868964e41426f7465c13c48335771c5450bf61c50a9cf28b9274f170c7421eea7974f168a9ec74fa35b3425eaf503dd36294ba8e758e7b8084c4d6bfd547f8c6b5827460fcc9d094d21fe17cfb7426501f50cb3d75c4c9395a3140e0f255443f660d3b9e065eea4143325fbbd26967c26a228d51a3a8384062f7434973f15d1da2c01068f7a83678a377701b46a5e6a4637e99868186ff4835fc0e3914cc56a76a360164ffc83f4f86cc595e607a00b977eeb6641e02a4e6e556c24ab163aecd7d146c03e843f200e30bc5b951c73a96d968db1c0cd05e357d910fce159fc59c40e9d6e29037d1020ed699c2f538d3ffcf0eb98087ee11ca4bd07bfddb0d68633806af74ace46e899b90e75199549d8fa2ae7097e896ab3c845217e3155f99b6ffb888037c8348ec95a0faad6a638ef74864761028c53221bde07e9ff7c81a3f427abf3f30c40adee5476157ef3c2a26e10cab95ec7d54b62dd220738f5a474d5f86874e0ce84accce1ced0de223aa72f760f1b3d13ddfd267938cd63e25308378d32008eed33645cda7812cd343bbaef9131b2794812f2fd37701ccb6cddf9c1e293d387aeb767131602e6612e607a9eb8e26b4ce4fa4765593d032bc923ce8acadda420203d51bba2124f480e3507eb1764fc3019ac7abae8ee215683a285078bda7f51d9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20fc9a68a26e9aa37ba6334f1a20275e3be7d3a9d4aa988627eadac8ea0d0a2dfbfae240842b74e5dd778972e451558134f434c7a1d8a52bc70519f38054e24553306bd8fd81e50cda2bd67bf6893d921d1aae5cb08409ae43e0bff4d54e1830e58ea9400f05e7fb75a3f7a92febbf58e5a3060dd06132ed6d5d68a3d75ec452826bed3b452f869d187be58a4ba98588611084283810728fa75981e792beaec4151763d070989ead31f265b40cc7a0cd29d47799b766d6a7f084e44c82baedfc01e029338ece1c6bc6439dc4d16bfe33f28c9f0af31626bb8142849742b7a624f680794c4156ed6a101ae478a3de3ba70a05fce8a3d67be6fb85f33bfcf2777ab6b106e60f1e253735fb113c183fa603f591e4456435171f387c0849001b428b5ccb118bd0f67d77f04f1a92400421813d8927fad109b40a8689254a5f0c8b346857cd28145a7cde195a4c834276730d30f074b212a150e770931ee9470e853e7d22402683131f96baec9121383995904c49a02ce2c2451f8038291e5db2dce66663e92ac14f8ad1811cc83861afadf12f3191cca1391f1f3af705977faa2fa2bf46a629f9fd0dd7279c7af7470472d1208a13e33239b484974d47cffce4ad47856440333022898140662dfea847e3cbfe5e989845ac6766e83472f8b0c650d85e77baed0b4896a63b672a7a74f5234bf9ec8567ff3c5bb8f93795e15e2c498b48d327cc8de3a01502422b59dfa601c9c3a04a98d2bfbd79dd0810d1d1250feab4241ee6248d87bd2a640ffe26d6b831735887c24e2076a3a0f3a74f7ae7568c27604087c03ca47a3201455f8f89defda4aa909cb1d25dd9ddb7fd62a940606f79b566390032c39c968f486f77f8764301a8479206f063d49eeb9f6d499333e2a1be045165f3b255dc17054e6d4447c4005f689eb5ed2f99fe201f4ff799bf088495850047277f22b9ef92a8b99618f4c86c2412f0e3b08a4f965f842775672043d1e2503586dafcdab3d4647d4dc68732a9cab8aa34c00c5edd04e65d9dd44c2a1fd21e2d41a8a9678862ebe9d1a1d59cac1c8430ef31e282f9cb391cf6f2b4d9ce2fd3d64adb43a7628139f6c02100f6a5465dbd33422418426c572b12547c5a665008c1ab03b1b3277edfedd24ef3d3359b449b64bd95ed82a04e7f9fbaab7b71dc0152cd12c731d91441f0114b08d314cd3f9a9f7fd0240d467fe54adefbee4d90762441629077e228528f91ca7dc17051bb437408a5ae272d0950e58961846a8fc2ec22ce14ba0e59aa974d4a05c9208ba5ae18674c6a23c9998d91e7d1ebea7e06b9a86227e204a2d003399c2a3b50c2c869c4380c195a014a02f6d2e7048941237029a1eb2e31dcaf468dbb516f9b620fdd7c3f090d58a88e02b51b25255b2182dd1", "0xd57bce545fb382c34570e5dfbf338f5e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0xd5c41b52a371aa36c9254ce34324f2a54e7b9012096b41c4eb3aaf947f6ea429": "0x0100", "0xd5e1a2fa16732ce6906189438c0a82c64e7b9012096b41c4eb3aaf947f6ea429": "0x0000", @@ -224,4 +419,4 @@ "childrenDefault": {} } } -} +} \ No newline at end of file diff --git a/polkadot/node/service/chain-specs/polkadot.json b/polkadot/node/service/chain-specs/polkadot.json index 71dbb9004038d1394cebe568627e1e6163ee9049..5f8d88102d7edd9d9d640a21b43a3c37982430d5 100644 --- a/polkadot/node/service/chain-specs/polkadot.json +++ b/polkadot/node/service/chain-specs/polkadot.json @@ -17,8 +17,8 @@ "/dns/boot-node.helikon.io/tcp/7072/wss/p2p/12D3KooWS9ZcvRxyzrSf6p63QfTCWs12nLoNKhGux865crgxVA4H", "/dns/polkadot.bootnode.amforc.com/tcp/30333/p2p/12D3KooWAsuCEVCzUVUrtib8W82Yne3jgVGhQZN3hizko5FTnDg3", "/dns/polkadot.bootnode.amforc.com/tcp/30334/wss/p2p/12D3KooWAsuCEVCzUVUrtib8W82Yne3jgVGhQZN3hizko5FTnDg3", - "/dns/polkadot-bootnode.polkadotters.com/tcp/30333/p2p/12D3KooWPAVUgBaBk6n8SztLrMk8ESByncbAfRKUdxY1nygb9zG3", - "/dns/polkadot-bootnode.polkadotters.com/tcp/30334/wss/p2p/12D3KooWPAVUgBaBk6n8SztLrMk8ESByncbAfRKUdxY1nygb9zG3", + "/dns/polkadot.bootnodes.polkadotters.com/tcp/30314/p2p/12D3KooWPAVUgBaBk6n8SztLrMk8ESByncbAfRKUdxY1nygb9zG3", + "/dns/polkadot.bootnodes.polkadotters.com/tcp/30316/wss/p2p/12D3KooWPAVUgBaBk6n8SztLrMk8ESByncbAfRKUdxY1nygb9zG3", "/dns/boot-cr.gatotech.network/tcp/33100/p2p/12D3KooWK4E16jKk9nRhvC4RfrDVgcZzExg8Q3Q2G7ABUUitks1w", "/dns/boot-cr.gatotech.network/tcp/35100/wss/p2p/12D3KooWK4E16jKk9nRhvC4RfrDVgcZzExg8Q3Q2G7ABUUitks1w", "/dns/boot-polkadot.metaspan.io/tcp/13012/p2p/12D3KooWRjHFApinuqSBjoaDjQHvxwubQSpEVy5hrgC9Smvh92WF", @@ -36,7 +36,8 @@ "/dns/dot14.rotko.net/tcp/35214/wss/p2p/12D3KooWPyEvPEXghnMC67Gff6PuZiSvfx3fmziKiPZcGStZ5xff", "/dns/dot14.rotko.net/tcp/33214/p2p/12D3KooWPyEvPEXghnMC67Gff6PuZiSvfx3fmziKiPZcGStZ5xff", "/dns/ibp-boot-polkadot.luckyfriday.io/tcp/30333/p2p/12D3KooWEjk6QXrZJ26fLpaajisJGHiz6WiQsR8k7mkM9GmWKnRZ", - "/dns/ibp-boot-polkadot.luckyfriday.io/tcp/30334/wss/p2p/12D3KooWEjk6QXrZJ26fLpaajisJGHiz6WiQsR8k7mkM9GmWKnRZ" + "/dns/ibp-boot-polkadot.luckyfriday.io/tcp/30334/wss/p2p/12D3KooWEjk6QXrZJ26fLpaajisJGHiz6WiQsR8k7mkM9GmWKnRZ", + "/dns/boot-polkadot.luckyfriday.io/tcp/443/wss/p2p/12D3KooWAdyiVAaeGdtBt6vn5zVetwA4z4qfm9Fi2QCSykN1wTBJ" ], "telemetryEndpoints": [ [ diff --git a/polkadot/node/service/chain-specs/westend.json b/polkadot/node/service/chain-specs/westend.json index 697675871fcd7b4b11cac5a25e71c704d471cbde..775f3e72ac75578a0f0166cba96531244af760c9 100644 --- a/polkadot/node/service/chain-specs/westend.json +++ b/polkadot/node/service/chain-specs/westend.json @@ -14,8 +14,8 @@ "/dns/boot-node.helikon.io/tcp/7082/wss/p2p/12D3KooWRFDPyT8vA8mLzh6dJoyujn4QNjeqi6Ch79eSMz9beKXC", "/dns/westend.bootnode.amforc.com/tcp/30333/p2p/12D3KooWJ5y9ZgVepBQNW4aabrxgmnrApdVnscqgKWiUu4BNJbC8", "/dns/westend.bootnode.amforc.com/tcp/30334/wss/p2p/12D3KooWJ5y9ZgVepBQNW4aabrxgmnrApdVnscqgKWiUu4BNJbC8", - "/dns/westend-bootnode.polkadotters.com/tcp/30333/p2p/12D3KooWHPHb64jXMtSRJDrYFATWeLnvChL8NtWVttY67DCH1eC5", - "/dns/westend-bootnode.polkadotters.com/tcp/30334/wss/p2p/12D3KooWHPHb64jXMtSRJDrYFATWeLnvChL8NtWVttY67DCH1eC5", + "/dns/westend.bootnodes.polkadotters.com/tcp/30308/p2p/12D3KooWHPHb64jXMtSRJDrYFATWeLnvChL8NtWVttY67DCH1eC5", + "/dns/westend.bootnodes.polkadotters.com/tcp/30310/wss/p2p/12D3KooWHPHb64jXMtSRJDrYFATWeLnvChL8NtWVttY67DCH1eC5", "/dns/boot-cr.gatotech.network/tcp/33300/p2p/12D3KooWQGR1vUhoy6mvQorFp3bZFn6NNezhQZ6NWnVV7tpFgoPd", "/dns/boot-cr.gatotech.network/tcp/35300/wss/p2p/12D3KooWQGR1vUhoy6mvQorFp3bZFn6NNezhQZ6NWnVV7tpFgoPd", "/dns/boot-westend.metaspan.io/tcp/33012/p2p/12D3KooWNTau7iG4G9cUJSwwt2QJP1W88pUf2SgqsHjRU2RL8pfa", diff --git a/polkadot/node/service/src/benchmarking.rs b/polkadot/node/service/src/benchmarking.rs index 400daf1aee3448f841f9030f24b78a0029fc0830..a0c4d3b04469b1bcf85990d4f6f62922f09bf88a 100644 --- a/polkadot/node/service/src/benchmarking.rs +++ b/polkadot/node/service/src/benchmarking.rs @@ -222,7 +222,7 @@ fn westend_sign_call( runtime::UncheckedExtrinsic::new_signed( call, sp_runtime::AccountId32::from(acc.public()).into(), - polkadot_core_primitives::Signature::Sr25519(signature.clone()), + polkadot_core_primitives::Signature::Sr25519(signature), extra, ) .into() @@ -274,7 +274,7 @@ fn rococo_sign_call( runtime::UncheckedExtrinsic::new_signed( call, sp_runtime::AccountId32::from(acc.public()).into(), - polkadot_core_primitives::Signature::Sr25519(signature.clone()), + polkadot_core_primitives::Signature::Sr25519(signature), extra, ) .into() diff --git a/polkadot/node/service/src/chain_spec.rs b/polkadot/node/service/src/chain_spec.rs index af241d1cbc558c849bbf533dd644848b20fa4491..c03ce1db0943d40a87c80e00707d3ebd0f9c1586 100644 --- a/polkadot/node/service/src/chain_spec.rs +++ b/polkadot/node/service/src/chain_spec.rs @@ -24,6 +24,8 @@ use polkadot_primitives::{AccountId, AccountPublic, AssignmentId, ValidatorId}; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_consensus_babe::AuthorityId as BabeId; +#[cfg(any(feature = "rococo-native", feature = "westend-native",))] +use polkadot_primitives::vstaging::SchedulerParams; #[cfg(feature = "rococo-native")] use rococo_runtime as rococo; #[cfg(feature = "rococo-native")] @@ -120,7 +122,10 @@ pub fn wococo_config() -> Result { fn default_parachains_host_configuration( ) -> polkadot_runtime_parachains::configuration::HostConfiguration { - use polkadot_primitives::{AsyncBackingParams, MAX_CODE_SIZE, MAX_POV_SIZE}; + use polkadot_primitives::{ + vstaging::{node_features::FeatureIndex, ApprovalVotingParams}, + AsyncBackingParams, MAX_CODE_SIZE, MAX_POV_SIZE, + }; polkadot_runtime_parachains::configuration::HostConfiguration { validation_upgrade_cooldown: 2u32, @@ -129,8 +134,6 @@ fn default_parachains_host_configuration( max_code_size: MAX_CODE_SIZE, max_pov_size: MAX_POV_SIZE, max_head_data_size: 32 * 1024, - group_rotation_frequency: 20, - paras_availability_period: 4, max_upward_queue_count: 8, max_upward_queue_size: 1024 * 1024, max_downward_message_size: 1024 * 1024, @@ -151,11 +154,21 @@ fn default_parachains_host_configuration( relay_vrf_modulo_samples: 2, zeroth_delay_tranche_width: 0, minimum_validation_upgrade_delay: 5, - scheduling_lookahead: 2, async_backing_params: AsyncBackingParams { max_candidate_depth: 3, allowed_ancestry_len: 2, }, + node_features: bitvec::vec::BitVec::from_element( + 1u8 << (FeatureIndex::ElasticScalingMVP as usize) | + 1u8 << (FeatureIndex::EnableAssignmentsV2 as usize), + ), + scheduler_params: SchedulerParams { + lookahead: 2, + group_rotation_frequency: 20, + paras_availability_period: 4, + ..Default::default() + }, + approval_voting_params: ApprovalVotingParams { max_approval_coalesce_count: 5 }, ..Default::default() } } @@ -891,7 +904,10 @@ pub fn rococo_testnet_genesis( "sudo": { "key": Some(root_key.clone()) }, "configuration": { "config": polkadot_runtime_parachains::configuration::HostConfiguration { - max_validators_per_core: Some(1), + scheduler_params: SchedulerParams { + max_validators_per_core: Some(1), + ..default_parachains_host_configuration().scheduler_params + }, ..default_parachains_host_configuration() }, }, diff --git a/polkadot/node/service/src/fake_runtime_api.rs b/polkadot/node/service/src/fake_runtime_api.rs index ccc3da22400dfc38f5e94aa4f6e89969499dbee8..c6cfb7a27d042bdf9e1b23cd41f88b8483a414a5 100644 --- a/polkadot/node/service/src/fake_runtime_api.rs +++ b/polkadot/node/service/src/fake_runtime_api.rs @@ -30,6 +30,7 @@ use polkadot_primitives::{ ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; + use sp_core::OpaqueMetadata; use sp_runtime::{ traits::Block as BlockT, @@ -39,7 +40,7 @@ use sp_runtime::{ use sp_version::RuntimeVersion; use sp_weights::Weight; use std::collections::BTreeMap; - +use xcm::{VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}; sp_api::decl_runtime_apis! { /// This runtime API is only implemented for the test runtime! pub trait GetLastTimestamp { @@ -60,7 +61,7 @@ sp_api::impl_runtime_apis! { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } @@ -396,4 +397,22 @@ sp_api::impl_runtime_apis! { unimplemented!() } } + + impl xcm_fee_payment_runtime_api::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(_: xcm::Version) -> Result, xcm_fee_payment_runtime_api::Error> { + unimplemented!() + } + + fn query_weight_to_asset_fee(_: Weight, _: VersionedAssetId) -> Result { + unimplemented!() + } + + fn query_xcm_weight(_: VersionedXcm<()>) -> Result { + unimplemented!() + } + + fn query_delivery_fees(_: VersionedLocation, _: VersionedXcm<()>) -> Result { + unimplemented!() + } + } } diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs index 6dcdec07ca84bd0fe9456e2bc29cbdf6cee6a9f8..4f4ede537055ebf58d0c126890256f9be2dad830 100644 --- a/polkadot/node/service/src/lib.rs +++ b/polkadot/node/service/src/lib.rs @@ -92,6 +92,7 @@ pub use chain_spec::{GenericChainSpec, RococoChainSpec, WestendChainSpec}; pub use consensus_common::{Proposal, SelectChain}; use frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE; use mmr_gadget::MmrGadget; +use polkadot_node_subsystem_types::DefaultSubsystemClient; pub use polkadot_primitives::{Block, BlockId, BlockNumber, CollatorPair, Hash, Id as ParaId}; pub use sc_client_api::{Backend, CallExecutor}; pub use sc_consensus::{BlockImport, LongestChain}; @@ -806,6 +807,7 @@ pub fn new_full( let shared_voter_state = rpc_setup; let auth_disc_publish_non_global_ips = config.network.allow_non_globals_in_dht; + let auth_disc_public_addresses = config.network.public_addresses.clone(); let mut net_config = sc_network::config::FullNetworkConfiguration::new(&config.network); let genesis_hash = client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"); @@ -1060,6 +1062,7 @@ pub fn new_full( let (worker, service) = sc_authority_discovery::new_worker_and_service_with_config( sc_authority_discovery::WorkerConfig { publish_non_global_ips: auth_disc_publish_non_global_ips, + public_addresses: auth_disc_public_addresses, // Require that authority discovery records are signed. strict_record_validation: true, ..Default::default() @@ -1081,12 +1084,17 @@ pub fn new_full( None }; + let runtime_client = Arc::new(DefaultSubsystemClient::new( + overseer_client.clone(), + OffchainTransactionPoolFactory::new(transaction_pool.clone()), + )); + let overseer_handle = if let Some(authority_discovery_service) = authority_discovery_service { let (overseer, overseer_handle) = overseer_gen - .generate::( + .generate::>( overseer_connector, OverseerGenArgs { - runtime_client: overseer_client.clone(), + runtime_client, network_service: network.clone(), sync_service: sync_service.clone(), authority_discovery_service, @@ -1099,9 +1107,6 @@ pub fn new_full( overseer_message_channel_capacity_override, req_protocol_names, peerset_protocol_names, - offchain_transaction_pool_factory: OffchainTransactionPoolFactory::new( - transaction_pool.clone(), - ), notification_services, }, ext_overseer_args, diff --git a/polkadot/node/service/src/overseer.rs b/polkadot/node/service/src/overseer.rs index b2b0786bfc24e5f702ddd82aec0676f0f9f795a8..9575b2458a259dfd2716aec6d688a95a73f5530d 100644 --- a/polkadot/node/service/src/overseer.rs +++ b/polkadot/node/service/src/overseer.rs @@ -14,10 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use super::{AuthorityDiscoveryApi, Block, Error, Hash, IsParachainNode, Registry}; -use polkadot_node_subsystem_types::DefaultSubsystemClient; +use super::{Block, Error, Hash, IsParachainNode, Registry}; +use polkadot_node_subsystem_types::{ChainApiBackend, RuntimeApiSubsystemClient}; use polkadot_overseer::{DummySubsystem, InitializedOverseerBuilder, SubsystemError}; -use sc_transaction_pool_api::OffchainTransactionPoolFactory; use sp_core::traits::SpawnNamed; use polkadot_availability_distribution::IncomingRequestReceivers; @@ -40,14 +39,10 @@ use polkadot_overseer::{ }; use parking_lot::Mutex; -use polkadot_primitives::runtime_api::ParachainHost; use sc_authority_discovery::Service as AuthorityDiscoveryService; use sc_client_api::AuxStore; use sc_keystore::LocalKeystore; use sc_network::{NetworkStateInfo, NotificationService}; -use sp_api::ProvideRuntimeApi; -use sp_blockchain::HeaderBackend; -use sp_consensus_babe::BabeApi; use std::{collections::HashMap, sync::Arc}; pub use polkadot_approval_distribution::ApprovalDistribution as ApprovalDistributionSubsystem; @@ -80,16 +75,14 @@ pub use polkadot_statement_distribution::StatementDistributionSubsystem; /// Arguments passed for overseer construction. pub struct OverseerGenArgs<'a, Spawner, RuntimeClient> where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, Spawner: 'static + SpawnNamed + Clone + Unpin, { - /// Runtime client generic, providing the `ProvieRuntimeApi` trait besides others. + /// Runtime client generic, providing the `ProvideRuntimeApi` trait besides others. pub runtime_client: Arc, /// Underlying network service implementation. pub network_service: Arc>, /// Underlying syncing service implementation. - pub sync_service: Arc>, + pub sync_service: Arc, /// Underlying authority discovery service. pub authority_discovery_service: AuthorityDiscoveryService, /// Collations request receiver for network protocol v1. @@ -111,8 +104,6 @@ where pub req_protocol_names: ReqProtocolNames, /// `PeerSet` protocol names to protocols mapping. pub peerset_protocol_names: PeerSetProtocolNames, - /// The offchain transaction pool factory. - pub offchain_transaction_pool_factory: OffchainTransactionPoolFactory, /// Notification services for validation/collation protocols. pub notification_services: HashMap>, } @@ -160,7 +151,6 @@ pub fn validator_overseer_builder( overseer_message_channel_capacity_override, req_protocol_names, peerset_protocol_names, - offchain_transaction_pool_factory, notification_services, }: OverseerGenArgs, ExtendedOverseerGenArgs { @@ -180,7 +170,7 @@ pub fn validator_overseer_builder( ) -> Result< InitializedOverseerBuilder< SpawnGlue, - Arc>, + Arc, CandidateValidationSubsystem, PvfCheckerSubsystem, CandidateBackingSubsystem, @@ -190,7 +180,7 @@ pub fn validator_overseer_builder( BitfieldSigningSubsystem, BitfieldDistributionSubsystem, ProvisionerSubsystem, - RuntimeApiSubsystem>, + RuntimeApiSubsystem, AvailabilityStoreSubsystem, NetworkBridgeRxSubsystem< Arc>, @@ -214,8 +204,7 @@ pub fn validator_overseer_builder( Error, > where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, Spawner: 'static + SpawnNamed + Clone + Unpin, { use polkadot_node_subsystem_util::metrics::Metrics; @@ -227,11 +216,6 @@ where let network_bridge_metrics: NetworkBridgeMetrics = Metrics::register(registry)?; - let runtime_api_client = Arc::new(DefaultSubsystemClient::new( - runtime_client.clone(), - offchain_transaction_pool_factory, - )); - let builder = Overseer::builder() .network_bridge_tx(NetworkBridgeTxSubsystem::new( network_service.clone(), @@ -298,7 +282,7 @@ where }) .provisioner(ProvisionerSubsystem::new(Metrics::register(registry)?)) .runtime_api(RuntimeApiSubsystem::new( - runtime_api_client.clone(), + runtime_client.clone(), Metrics::register(registry)?, spawner.clone(), )) @@ -339,7 +323,7 @@ where .activation_external_listeners(Default::default()) .span_per_active_leaf(Default::default()) .active_leaves(Default::default()) - .supports_parachains(runtime_api_client) + .supports_parachains(runtime_client) .metrics(metrics) .spawner(spawner); @@ -367,13 +351,12 @@ pub fn collator_overseer_builder( overseer_message_channel_capacity_override, req_protocol_names, peerset_protocol_names, - offchain_transaction_pool_factory, notification_services, }: OverseerGenArgs, ) -> Result< InitializedOverseerBuilder< SpawnGlue, - Arc>, + Arc, DummySubsystem, DummySubsystem, DummySubsystem, @@ -383,7 +366,7 @@ pub fn collator_overseer_builder( DummySubsystem, DummySubsystem, DummySubsystem, - RuntimeApiSubsystem>, + RuntimeApiSubsystem, DummySubsystem, NetworkBridgeRxSubsystem< Arc>, @@ -407,24 +390,17 @@ pub fn collator_overseer_builder( Error, > where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, Spawner: 'static + SpawnNamed + Clone + Unpin, + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, { use polkadot_node_subsystem_util::metrics::Metrics; - let metrics = ::register(registry)?; let notification_sinks = Arc::new(Mutex::new(HashMap::new())); let spawner = SpawnGlue(spawner); let network_bridge_metrics: NetworkBridgeMetrics = Metrics::register(registry)?; - let runtime_api_client = Arc::new(DefaultSubsystemClient::new( - runtime_client.clone(), - offchain_transaction_pool_factory, - )); - let builder = Overseer::builder() .network_bridge_tx(NetworkBridgeTxSubsystem::new( network_service.clone(), @@ -475,7 +451,7 @@ where }) .provisioner(DummySubsystem) .runtime_api(RuntimeApiSubsystem::new( - runtime_api_client.clone(), + runtime_client.clone(), Metrics::register(registry)?, spawner.clone(), )) @@ -490,8 +466,8 @@ where .activation_external_listeners(Default::default()) .span_per_active_leaf(Default::default()) .active_leaves(Default::default()) - .supports_parachains(runtime_api_client) - .metrics(metrics) + .supports_parachains(runtime_client) + .metrics(Metrics::register(registry)?) .spawner(spawner); let builder = if let Some(capacity) = overseer_message_channel_capacity_override { @@ -510,13 +486,9 @@ pub trait OverseerGen { connector: OverseerConnector, args: OverseerGenArgs, ext_args: Option, - ) -> Result< - (Overseer, Arc>>, OverseerHandle), - Error, - > + ) -> Result<(Overseer, Arc>, OverseerHandle), Error> where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, Spawner: 'static + SpawnNamed + Clone + Unpin; // It would be nice to make `create_subsystems` part of this trait, @@ -533,13 +505,9 @@ impl OverseerGen for ValidatorOverseerGen { connector: OverseerConnector, args: OverseerGenArgs, ext_args: Option, - ) -> Result< - (Overseer, Arc>>, OverseerHandle), - Error, - > + ) -> Result<(Overseer, Arc>, OverseerHandle), Error> where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, Spawner: 'static + SpawnNamed + Clone + Unpin, { let ext_args = ext_args.ok_or(Error::Overseer(SubsystemError::Context( @@ -561,13 +529,9 @@ impl OverseerGen for CollatorOverseerGen { connector: OverseerConnector, args: OverseerGenArgs, _ext_args: Option, - ) -> Result< - (Overseer, Arc>>, OverseerHandle), - Error, - > + ) -> Result<(Overseer, Arc>, OverseerHandle), Error> where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, Spawner: 'static + SpawnNamed + Clone + Unpin, { collator_overseer_builder(args)? diff --git a/polkadot/node/service/src/parachains_db/upgrade.rs b/polkadot/node/service/src/parachains_db/upgrade.rs index d22eebb5c8d4edebdd2174f3cb1ba144fea0b130..4d7370859609d5559eb9f3b440bb6b4ee28cddff 100644 --- a/polkadot/node/service/src/parachains_db/upgrade.rs +++ b/polkadot/node/service/src/parachains_db/upgrade.rs @@ -93,7 +93,7 @@ pub(crate) fn try_upgrade_db( } /// Try upgrading parachain's database to the next version. -/// If successfull, it returns the current version. +/// If successful, it returns the current version. pub(crate) fn try_upgrade_db_to_next_version( db_path: &Path, db_kind: DatabaseKind, @@ -115,7 +115,7 @@ pub(crate) fn try_upgrade_db_to_next_version( Some(CURRENT_VERSION) => CURRENT_VERSION, // This is an arbitrary future version, we don't handle it. Some(v) => return Err(Error::FutureVersion { current: CURRENT_VERSION, got: v }), - // No version file. For `RocksDB` we dont need to do anything. + // No version file. For `RocksDB` we don't need to do anything. None if db_kind == DatabaseKind::RocksDB => CURRENT_VERSION, // No version file. `ParityDB` did not previously have a version defined. // We handle this as a `0 -> 1` migration. @@ -183,7 +183,7 @@ fn migrate_from_version_1_to_2(path: &Path, db_kind: DatabaseKind) -> Result { // The total lag accounting for disputes. let lag_disputes = initial_leaf_number.saturating_sub(subchain_number); diff --git a/polkadot/node/subsystem-bench/Cargo.toml b/polkadot/node/subsystem-bench/Cargo.toml index 7de91d8cd5ded29a6ad2f2f033cd0dfc5ca580d5..b494f05180d1b06f740058cc368c63b0d9552277 100644 --- a/polkadot/node/subsystem-bench/Cargo.toml +++ b/polkadot/node/subsystem-bench/Cargo.toml @@ -8,9 +8,13 @@ license.workspace = true readme = "README.md" publish = false +[lib] +name = "polkadot_subsystem_bench" +path = "src/lib/lib.rs" + [[bin]] name = "subsystem-bench" -path = "src/subsystem-bench.rs" +path = "src/cli/subsystem-bench.rs" # Prevent rustdoc error. Already documented from top-level Cargo.toml. doc = false @@ -35,7 +39,7 @@ async-trait = "0.1.57" sp-keystore = { path = "../../../substrate/primitives/keystore" } sc-keystore = { path = "../../../substrate/client/keystore" } sp-core = { path = "../../../substrate/primitives/core" } -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } futures = "0.3.21" futures-timer = "3.0.2" bincode = "1.3.3" @@ -52,7 +56,7 @@ bitvec = "1.0.1" kvdb-memorydb = "0.13.0" parity-scale-codec = { version = "3.6.1", features = ["derive", "std"] } -tokio = "1.24.2" +tokio = { version = "1.24.2", features = ["parking_lot", "rt-multi-thread"] } clap-num = "1.0.2" polkadot-node-subsystem-test-helpers = { path = "../subsystem-test-helpers" } sp-keyring = { path = "../../../substrate/primitives/keyring" } @@ -67,6 +71,7 @@ prometheus_endpoint = { package = "substrate-prometheus-endpoint", path = "../.. prometheus = { version = "0.13.0", default-features = false } serde = { workspace = true, default-features = true } serde_yaml = { workspace = true } +serde_json = { workspace = true } polkadot-node-core-approval-voting = { path = "../core/approval-voting" } polkadot-approval-distribution = { path = "../network/approval-distribution" } @@ -74,8 +79,9 @@ sp-consensus-babe = { path = "../../../substrate/primitives/consensus/babe" } sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } sp-timestamp = { path = "../../../substrate/primitives/timestamp" } -schnorrkel = { version = "0.9.1", default-features = false } -rand_core = "0.6.2" # should match schnorrkel +schnorrkel = { version = "0.11.4", default-features = false } +# rand_core should match schnorrkel +rand_core = "0.6.2" rand_chacha = { version = "0.3.1" } paste = "1.0.14" orchestra = { version = "0.3.5", default-features = false, features = ["futures_channel"] } diff --git a/polkadot/node/subsystem-bench/README.md b/polkadot/node/subsystem-bench/README.md index e090a0392cb733ffc2a4b21edc73da44e5aed708..228fba41c46cce48a2a4bda96e869f6abcba1c44 100644 --- a/polkadot/node/subsystem-bench/README.md +++ b/polkadot/node/subsystem-bench/README.md @@ -1,6 +1,6 @@ # Subsystem benchmark client -Run parachain consensus stress and performance tests on your development machine. +Run parachain consensus stress and performance tests on your development machine or in CI. ## Motivation @@ -27,12 +27,13 @@ The output binary will be placed in `target/testnet/subsystem-bench`. ### Test metrics Subsystem, CPU usage and network metrics are exposed via a prometheus endpoint during the test execution. -A small subset of these collected metrics are displayed in the CLI, but for an in depth analysys of the test results, +A small subset of these collected metrics are displayed in the CLI, but for an in depth analysis of the test results, a local Grafana/Prometheus stack is needed. ### Run Prometheus, Pyroscope and Graphana in Docker -If docker is not usable, then follow the next sections to manually install Prometheus, Pyroscope and Graphana on your machine. +If docker is not usable, then follow the next sections to manually install Prometheus, Pyroscope and Graphana +on your machine. ```bash cd polkadot/node/subsystem-bench/docker @@ -44,7 +45,7 @@ docker compose up Please follow the [official installation guide](https://prometheus.io/docs/prometheus/latest/installation/) for your platform/OS. -After succesfully installing and starting up Prometheus, we need to alter it's configuration such that it +After successfully installing and starting up Prometheus, we need to alter it's configuration such that it will scrape the benchmark prometheus endpoint `127.0.0.1:9999`. Please check the prometheus official documentation regarding the location of `prometheus.yml`. On MacOS for example the full path `/opt/homebrew/etc/prometheus.yml` @@ -95,39 +96,16 @@ If you are running the servers in Docker, use the following URLs: Follow [this guide](https://grafana.com/docs/grafana/latest/dashboards/manage-dashboards/#export-and-import-dashboards) to import the dashboards from the repository `grafana` folder. -## How to run a test - -To run a test, you need to first choose a test objective. Currently, we support the following: - -``` -target/testnet/subsystem-bench --help -The almighty Subsystem Benchmark Tool™️ - -Usage: subsystem-bench [OPTIONS] - -Commands: - data-availability-read Benchmark availability recovery strategies +### Standard test options ``` +$ subsystem-bench --help +Usage: subsystem-bench [OPTIONS] -Note: `test-sequence` is a special test objective that wraps up an arbitrary number of test objectives. It is tipically - used to run a suite of tests defined in a `yaml` file like in this [example](examples/availability_read.yaml). +Arguments: + Path to the test sequence configuration file -### Standard test options - -``` - --network The type of network to be emulated [default: ideal] [possible values: ideal, healthy, - degraded] - --n-cores Number of cores to fetch availability for [default: 100] - --n-validators Number of validators to fetch chunks from [default: 500] - --min-pov-size The minimum pov size in KiB [default: 5120] - --max-pov-size The maximum pov size bytes [default: 5120] - -n, --num-blocks The number of blocks the test is going to run [default: 1] - -p, --peer-bandwidth The bandwidth of emulated remote peers in KiB - -b, --bandwidth The bandwidth of our node in KiB - --connectivity Emulated peer connection ratio [0-100] - --peer-mean-latency Mean remote peer latency in milliseconds [0-5000] - --peer-latency-std-dev Remote peer latency standard deviation +Options: --profile Enable CPU Profiling with Pyroscope --pyroscope-url Pyroscope Server URL [default: http://localhost:4040] --pyroscope-sample-rate Pyroscope Sample Rate [default: 113] @@ -135,27 +113,17 @@ Note: `test-sequence` is a special test objective that wraps up an arbitrary num -h, --help Print help ``` -These apply to all test objectives, except `test-sequence` which relies on the values being specified in a file. - -### Test objectives - -Each test objective can have it's specific configuration options, in contrast with the standard test options. +## How to run a test -For `data-availability-read` the recovery strategy to be used is configurable. +To run a test, you need to use a path to a test objective: ``` -target/testnet/subsystem-bench data-availability-read --help -Benchmark availability recovery strategies - -Usage: subsystem-bench data-availability-read [OPTIONS] - -Options: - -f, --fetch-from-backers Turbo boost AD Read by fetching the full availability datafrom backers first. Saves CPU - as we don't need to re-construct from chunks. Tipically this is only faster if nodes - have enough bandwidth - -h, --help Print help +target/testnet/subsystem-bench polkadot/node/subsystem-bench/examples/availability_read.yaml ``` +Note: test objectives may be wrapped up into a test sequence. +It is typically used to run a suite of tests like in this [example](examples/availability_read.yaml). + ### Understanding the test configuration A single test configuration `TestConfiguration` struct applies to a single run of a certain test objective. @@ -169,42 +137,71 @@ usage: From the perspective of the subsystem under test, this means that it will receive an `ActiveLeavesUpdate` signal followed by an arbitrary amount of messages. This process repeats itself for `num_blocks`. The messages are generally -test payloads pre-generated before the test run, or constructed on pre-genereated payloads. For example the +test payloads pre-generated before the test run, or constructed on pre-generated payloads. For example the `AvailabilityRecoveryMessage::RecoverAvailableData` message includes a `CandidateReceipt` which is generated before the test is started. ### Example run -Let's run an availabilty read test which will recover availability for 10 cores with max PoV size on a 500 +Let's run an availability read test which will recover availability for 200 cores with max PoV size on a 1000 node validator network. + + ``` - target/testnet/subsystem-bench --n-cores 10 data-availability-read -[2023-11-28T09:01:59Z INFO subsystem_bench::core::display] n_validators = 500, n_cores = 10, pov_size = 5120 - 5120, - latency = None -[2023-11-28T09:01:59Z INFO subsystem-bench::availability] Generating template candidate index=0 pov_size=5242880 -[2023-11-28T09:01:59Z INFO subsystem-bench::availability] Created test environment. -[2023-11-28T09:01:59Z INFO subsystem-bench::availability] Pre-generating 10 candidates. -[2023-11-28T09:02:01Z INFO subsystem-bench::core] Initializing network emulation for 500 peers. -[2023-11-28T09:02:01Z INFO substrate_prometheus_endpoint] 〽️ Prometheus exporter started at 127.0.0.1:9999 -[2023-11-28T09:02:01Z INFO subsystem-bench::availability] Current block 1/1 -[2023-11-28T09:02:01Z INFO subsystem_bench::availability] 10 recoveries pending -[2023-11-28T09:02:04Z INFO subsystem_bench::availability] Block time 3231ms -[2023-11-28T09:02:04Z INFO subsystem-bench::availability] Sleeping till end of block (2768ms) -[2023-11-28T09:02:07Z INFO subsystem_bench::availability] All blocks processed in 6001ms -[2023-11-28T09:02:07Z INFO subsystem_bench::availability] Throughput: 51200 KiB/block -[2023-11-28T09:02:07Z INFO subsystem_bench::availability] Block time: 6001 ms -[2023-11-28T09:02:07Z INFO subsystem_bench::availability] - - Total received from network: 66 MiB - Total sent to network: 58 KiB - Total subsystem CPU usage 4.16s - CPU usage per block 4.16s - Total test environment CPU usage 0.00s - CPU usage per block 0.00s +target/testnet/subsystem-bench polkadot/node/subsystem-bench/examples/availability_write.yaml +[2024-02-19T14:10:32.981Z INFO subsystem_bench] Sequence contains 1 step(s) +[2024-02-19T14:10:32.981Z INFO subsystem-bench::cli] Step 1/1 +[2024-02-19T14:10:32.981Z INFO subsystem-bench::cli] [objective = DataAvailabilityWrite] n_validators = 1000, n_cores = 200, pov_size = 5120 - 5120, connectivity = 75, latency = Some(PeerLatency { mean_latency_ms: 30, std_dev: 2.0 }) +[2024-02-19T14:10:32.982Z INFO subsystem-bench::availability] Generating template candidate index=0 pov_size=5242880 +[2024-02-19T14:10:33.106Z INFO subsystem-bench::availability] Created test environment. +[2024-02-19T14:10:33.106Z INFO subsystem-bench::availability] Pre-generating 600 candidates. +[2024-02-19T14:10:34.096Z INFO subsystem-bench::network] Initializing emulation for a 1000 peer network. +[2024-02-19T14:10:34.096Z INFO subsystem-bench::network] connectivity 75%, latency Some(PeerLatency { mean_latency_ms: 30, std_dev: 2.0 }) +[2024-02-19T14:10:34.098Z INFO subsystem-bench::network] Network created, connected validator count 749 +[2024-02-19T14:10:34.099Z INFO subsystem-bench::availability] Seeding availability store with candidates ... +[2024-02-19T14:10:34.100Z INFO substrate_prometheus_endpoint] 〽️ Prometheus exporter started at 127.0.0.1:9999 +[2024-02-19T14:10:34.387Z INFO subsystem-bench::availability] Done +[2024-02-19T14:10:34.387Z INFO subsystem-bench::availability] Current block #1 +[2024-02-19T14:10:34.389Z INFO subsystem-bench::availability] Waiting for all emulated peers to receive their chunk from us ... +[2024-02-19T14:10:34.625Z INFO subsystem-bench::availability] All chunks received in 237ms +[2024-02-19T14:10:34.626Z INFO polkadot_subsystem_bench::availability] Waiting for 749 bitfields to be received and processed +[2024-02-19T14:10:35.710Z INFO subsystem-bench::availability] All bitfields processed +[2024-02-19T14:10:35.710Z INFO subsystem-bench::availability] All work for block completed in 1322ms +[2024-02-19T14:10:35.710Z INFO subsystem-bench::availability] Current block #2 +[2024-02-19T14:10:35.712Z INFO subsystem-bench::availability] Waiting for all emulated peers to receive their chunk from us ... +[2024-02-19T14:10:35.947Z INFO subsystem-bench::availability] All chunks received in 236ms +[2024-02-19T14:10:35.947Z INFO polkadot_subsystem_bench::availability] Waiting for 749 bitfields to be received and processed +[2024-02-19T14:10:37.038Z INFO subsystem-bench::availability] All bitfields processed +[2024-02-19T14:10:37.038Z INFO subsystem-bench::availability] All work for block completed in 1328ms +[2024-02-19T14:10:37.039Z INFO subsystem-bench::availability] Current block #3 +[2024-02-19T14:10:37.040Z INFO subsystem-bench::availability] Waiting for all emulated peers to receive their chunk from us ... +[2024-02-19T14:10:37.276Z INFO subsystem-bench::availability] All chunks received in 237ms +[2024-02-19T14:10:37.276Z INFO polkadot_subsystem_bench::availability] Waiting for 749 bitfields to be received and processed +[2024-02-19T14:10:38.362Z INFO subsystem-bench::availability] All bitfields processed +[2024-02-19T14:10:38.362Z INFO subsystem-bench::availability] All work for block completed in 1323ms +[2024-02-19T14:10:38.362Z INFO subsystem-bench::availability] All blocks processed in 3974ms +[2024-02-19T14:10:38.362Z INFO subsystem-bench::availability] Avg block time: 1324 ms +[2024-02-19T14:10:38.362Z INFO parachain::availability-store] received `Conclude` signal, exiting +[2024-02-19T14:10:38.362Z INFO parachain::bitfield-distribution] Conclude +[2024-02-19T14:10:38.362Z INFO subsystem-bench::network] Downlink channel closed, network interface task exiting + +polkadot/node/subsystem-bench/examples/availability_write.yaml #1 DataAvailabilityWrite + +Network usage, KiB total per block +Received from peers 12922.000 4307.333 +Sent to peers 47705.000 15901.667 + +CPU usage, seconds total per block +availability-distribution 0.045 0.015 +bitfield-distribution 0.104 0.035 +availability-store 0.304 0.101 +Test environment 3.213 1.071 ``` -`Block time` in the context of `data-availability-read` has a different meaning. It measures the amount of time it + + +`Block time` in the current context has a different meaning. It measures the amount of time it took the subsystem to finish processing all of the messages sent in the context of the current test block. ### Test logs @@ -214,7 +211,7 @@ You can select log target, subtarget and verbosity just like with Polkadot node ### View test metrics -Assuming the Grafana/Prometheus stack installation steps completed succesfully, you should be able to +Assuming the Grafana/Prometheus stack installation steps completed successfully, you should be able to view the test progress in real time by accessing [this link](http://localhost:3000/goto/SM5B8pNSR?orgId=1). Now run @@ -233,8 +230,9 @@ Since the execution will be very slow, it's recommended not to run it together w benchmark results into account. A report is saved in a file `cachegrind_report.txt`. Example run results: + ``` -$ target/testnet/subsystem-bench --n-cores 10 --cache-misses data-availability-read +$ target/testnet/subsystem-bench --cache-misses cache-misses-data-availability-read.yaml $ cat cachegrind_report.txt I refs: 64,622,081,485 I1 misses: 3,018,168 @@ -275,7 +273,7 @@ happy and negative scenarios (low bandwidth, network errors and low connectivity To faster write a new test objective you need to use some higher level wrappers and logic: `TestEnvironment`, `TestConfiguration`, `TestAuthorities`, `NetworkEmulator`. To create the `TestEnvironment` you will -need to also build an `Overseer`, but that should be easy using the mockups for subsystems in`core::mock`. +need to also build an `Overseer`, but that should be easy using the mockups for subsystems in `mock`. ### Mocking diff --git a/polkadot/node/subsystem-bench/examples/approvals_no_shows.yaml b/polkadot/node/subsystem-bench/examples/approvals_no_shows.yaml index 758c7fbbf1121a249ff3094bc74967e3ad2420da..146da57d44c4aaf973e13c886a357028cdbe3559 100644 --- a/polkadot/node/subsystem-bench/examples/approvals_no_shows.yaml +++ b/polkadot/node/subsystem-bench/examples/approvals_no_shows.yaml @@ -1,14 +1,14 @@ TestConfiguration: # Test 1 - objective: !ApprovalVoting - last_considered_tranche: 89 coalesce_mean: 3.0 coalesce_std_dev: 1.0 + enable_assignments_v2: true + last_considered_tranche: 89 stop_when_approved: true coalesce_tranche_diff: 12 - workdir_prefix: "/tmp/" - enable_assignments_v2: true num_no_shows_per_candidate: 10 + workdir_prefix: "/tmp/" n_validators: 500 n_cores: 100 min_pov_size: 1120 diff --git a/polkadot/node/subsystem-bench/examples/approvals_throughput.yaml b/polkadot/node/subsystem-bench/examples/approvals_throughput.yaml index 9eeeefc53a4277aae51e44ee5940a2eb65111cd3..6b17e62c20aa3f69153fb596d1a303a2e0320ddd 100644 --- a/polkadot/node/subsystem-bench/examples/approvals_throughput.yaml +++ b/polkadot/node/subsystem-bench/examples/approvals_throughput.yaml @@ -7,11 +7,10 @@ TestConfiguration: last_considered_tranche: 89 stop_when_approved: false coalesce_tranche_diff: 12 - workdir_prefix: "/tmp" num_no_shows_per_candidate: 0 + workdir_prefix: "/tmp" n_validators: 500 n_cores: 100 - n_included_candidates: 100 min_pov_size: 1120 max_pov_size: 5120 peer_bandwidth: 524288000000 diff --git a/polkadot/node/subsystem-bench/examples/approvals_throughput_best_case.yaml b/polkadot/node/subsystem-bench/examples/approvals_throughput_best_case.yaml index 370bb31a5c4c102174c29382ab6de70ca336278e..e946c28e8ef5d4e38736ffc21e56d8b1c6cd0ddc 100644 --- a/polkadot/node/subsystem-bench/examples/approvals_throughput_best_case.yaml +++ b/polkadot/node/subsystem-bench/examples/approvals_throughput_best_case.yaml @@ -7,8 +7,8 @@ TestConfiguration: last_considered_tranche: 89 stop_when_approved: true coalesce_tranche_diff: 12 - workdir_prefix: "/tmp/" num_no_shows_per_candidate: 0 + workdir_prefix: "/tmp/" n_validators: 500 n_cores: 100 min_pov_size: 1120 diff --git a/polkadot/node/subsystem-bench/examples/approvals_throughput_no_optimisations_enabled.yaml b/polkadot/node/subsystem-bench/examples/approvals_throughput_no_optimisations_enabled.yaml index 30b9ac8dc50fec25bc4952ef3706f9878aa2bbd7..8f4b050e72f27dd4b5bb0c52bd49162cd0bb83ec 100644 --- a/polkadot/node/subsystem-bench/examples/approvals_throughput_no_optimisations_enabled.yaml +++ b/polkadot/node/subsystem-bench/examples/approvals_throughput_no_optimisations_enabled.yaml @@ -7,8 +7,8 @@ TestConfiguration: last_considered_tranche: 89 stop_when_approved: false coalesce_tranche_diff: 12 - workdir_prefix: "/tmp/" num_no_shows_per_candidate: 0 + workdir_prefix: "/tmp/" n_validators: 500 n_cores: 100 min_pov_size: 1120 diff --git a/polkadot/node/subsystem-bench/grafana/availability-read.json b/polkadot/node/subsystem-bench/grafana/availability-read.json index 31c4ad3c795230402ec54d5558c24a3ab9664db4..96a83d2d70fe2e627eda9f142bdf7c67c54fd5a2 100644 --- a/polkadot/node/subsystem-bench/grafana/availability-read.json +++ b/polkadot/node/subsystem-bench/grafana/availability-read.json @@ -119,7 +119,7 @@ "editorMode": "code", "expr": "subsystem_benchmark_n_validators{}", "instant": false, - "legendFormat": "n_vaidators", + "legendFormat": "n_validators", "range": true, "refId": "A" }, @@ -1046,7 +1046,7 @@ "refId": "A" } ], - "title": "Availability subystem metrics", + "title": "Availability subsystem metrics", "type": "row" }, { @@ -1397,7 +1397,7 @@ "refId": "B" } ], - "title": "Recovery throughtput", + "title": "Recovery throughput", "transformations": [], "type": "timeseries" }, diff --git a/polkadot/node/subsystem-bench/src/subsystem-bench.rs b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs similarity index 62% rename from polkadot/node/subsystem-bench/src/subsystem-bench.rs rename to polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs index 0803f175474e69a76640b12497acaaafa4cd5213..10953b6c7839d8ea2a4e0396a4ee37b9875828e9 100644 --- a/polkadot/node/subsystem-bench/src/subsystem-bench.rs +++ b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs @@ -14,54 +14,32 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! A tool for running subsystem benchmark tests designed for development and -//! CI regression testing. - -use approval::{bench_approvals, ApprovalsOptions}; -use availability::{ - cli::{DataAvailabilityReadOptions, NetworkEmulation}, - prepare_test, TestState, -}; +//! A tool for running subsystem benchmark tests +//! designed for development and CI regression testing. + use clap::Parser; -use clap_num::number_range; use color_eyre::eyre; use colored::Colorize; -use core::{ - configuration::TestConfiguration, - display::display_configuration, - environment::{TestEnvironment, GENESIS_HASH}, -}; +use polkadot_subsystem_bench::{approval, availability, configuration}; use pyroscope::PyroscopeAgent; use pyroscope_pprofrs::{pprof_backend, PprofConfig}; use serde::{Deserialize, Serialize}; use std::path::Path; -mod approval; -mod availability; -mod core; mod valgrind; -const LOG_TARGET: &str = "subsystem-bench"; - -fn le_100(s: &str) -> Result { - number_range(s, 0, 100) -} - -fn le_5000(s: &str) -> Result { - number_range(s, 0, 5000) -} +const LOG_TARGET: &str = "subsystem-bench::cli"; /// Supported test objectives #[derive(Debug, Clone, Parser, Serialize, Deserialize)] #[command(rename_all = "kebab-case")] pub enum TestObjective { /// Benchmark availability recovery strategies. - DataAvailabilityRead(DataAvailabilityReadOptions), + DataAvailabilityRead(availability::DataAvailabilityReadOptions), /// Benchmark availability and bitfield distribution. DataAvailabilityWrite, /// Benchmark the approval-voting and approval-distribution subsystems. - ApprovalVoting(ApprovalsOptions), - Unimplemented, + ApprovalVoting(approval::ApprovalsOptions), } impl std::fmt::Display for TestObjective { @@ -73,39 +51,37 @@ impl std::fmt::Display for TestObjective { Self::DataAvailabilityRead(_) => "DataAvailabilityRead", Self::DataAvailabilityWrite => "DataAvailabilityWrite", Self::ApprovalVoting(_) => "ApprovalVoting", - Self::Unimplemented => "Unimplemented", } ) } } -#[derive(Debug, Parser)] -#[allow(missing_docs)] -struct BenchCli { - #[arg(long, value_enum, ignore_case = true, default_value_t = NetworkEmulation::Ideal)] - /// The type of network to be emulated - pub network: NetworkEmulation, - - #[clap(short, long)] - /// The bandwidth of emulated remote peers in KiB - pub peer_bandwidth: Option, - - #[clap(short, long)] - /// The bandwidth of our node in KiB - pub bandwidth: Option, - - #[clap(long, value_parser=le_100)] - /// Emulated peer connection ratio [0-100]. - pub connectivity: Option, +/// The test input parameters +#[derive(Clone, Debug, Serialize, Deserialize)] +struct CliTestConfiguration { + /// Test Objective + pub objective: TestObjective, + /// Test Configuration + #[serde(flatten)] + pub test_config: configuration::TestConfiguration, +} - #[clap(long, value_parser=le_5000)] - /// Mean remote peer latency in milliseconds [0-5000]. - pub peer_mean_latency: Option, +#[derive(Serialize, Deserialize)] +pub struct TestSequence { + #[serde(rename(serialize = "TestConfiguration", deserialize = "TestConfiguration"))] + test_configurations: Vec, +} - #[clap(long, value_parser=le_5000)] - /// Remote peer latency standard deviation - pub peer_latency_std_dev: Option, +impl TestSequence { + fn new_from_file(path: &Path) -> std::io::Result { + let string = String::from_utf8(std::fs::read(path)?).expect("File is valid UTF8"); + Ok(serde_yaml::from_str(&string).expect("File is valid test sequence YA")) + } +} +#[derive(Debug, Parser)] +#[allow(missing_docs)] +struct BenchCli { #[clap(long, default_value_t = false)] /// Enable CPU Profiling with Pyroscope pub profile: bool, @@ -122,10 +98,6 @@ struct BenchCli { /// Enable Cache Misses Profiling with Valgrind. Linux only, Valgrind must be in the PATH pub cache_misses: bool, - #[clap(long, default_value_t = false)] - /// Shows the output in YAML format - pub yaml_output: bool, - #[arg(required = true)] /// Path to the test sequence configuration file pub path: String, @@ -148,49 +120,58 @@ impl BenchCli { None }; - let test_sequence = core::configuration::TestSequence::new_from_file(Path::new(&self.path)) + let test_sequence = TestSequence::new_from_file(Path::new(&self.path)) .expect("File exists") - .into_vec(); + .test_configurations; let num_steps = test_sequence.len(); gum::info!("{}", format!("Sequence contains {} step(s)", num_steps).bright_purple()); - for (index, test_config) in test_sequence.into_iter().enumerate() { - let benchmark_name = format!("{} #{} {}", &self.path, index + 1, test_config.objective); - gum::info!(target: LOG_TARGET, "{}", format!("Step {}/{}", index + 1, num_steps).bright_purple(),); - display_configuration(&test_config); - let usage = match test_config.objective { - TestObjective::DataAvailabilityRead(ref _opts) => { - let mut state = TestState::new(&test_config); - let (mut env, _protocol_config) = prepare_test(test_config, &mut state); + for (index, CliTestConfiguration { objective, mut test_config }) in + test_sequence.into_iter().enumerate() + { + let benchmark_name = format!("{} #{} {}", &self.path, index + 1, objective); + gum::info!(target: LOG_TARGET, "{}", format!("Step {}/{}", index + 1, num_steps).bright_purple(),); + gum::info!(target: LOG_TARGET, "[{}] {}", format!("objective = {:?}", objective).green(), test_config); + test_config.generate_pov_sizes(); + + let usage = match objective { + TestObjective::DataAvailabilityRead(opts) => { + let state = availability::TestState::new(&test_config); + let (mut env, _protocol_config) = availability::prepare_test( + &state, + availability::TestDataAvailability::Read(opts), + true, + ); env.runtime().block_on(availability::benchmark_availability_read( &benchmark_name, &mut env, - state, + &state, )) }, - TestObjective::ApprovalVoting(ref options) => { - let (mut env, state) = - approval::prepare_test(test_config.clone(), options.clone()); - env.runtime().block_on(bench_approvals(&benchmark_name, &mut env, state)) - }, TestObjective::DataAvailabilityWrite => { - let mut state = TestState::new(&test_config); - let (mut env, _protocol_config) = prepare_test(test_config, &mut state); + let state = availability::TestState::new(&test_config); + let (mut env, _protocol_config) = availability::prepare_test( + &state, + availability::TestDataAvailability::Write, + true, + ); env.runtime().block_on(availability::benchmark_availability_write( + &benchmark_name, + &mut env, + &state, + )) + }, + TestObjective::ApprovalVoting(ref options) => { + let (mut env, state) = + approval::prepare_test(test_config.clone(), options.clone(), true); + env.runtime().block_on(approval::bench_approvals( &benchmark_name, &mut env, state, )) }, - TestObjective::Unimplemented => todo!(), - }; - - let output = if self.yaml_output { - serde_yaml::to_string(&vec![usage])? - } else { - usage.to_string() }; - println!("{}", output); + println!("{}", usage); } if let Some(agent_running) = agent_running { diff --git a/polkadot/node/subsystem-bench/src/valgrind.rs b/polkadot/node/subsystem-bench/src/cli/valgrind.rs similarity index 100% rename from polkadot/node/subsystem-bench/src/valgrind.rs rename to polkadot/node/subsystem-bench/src/cli/valgrind.rs diff --git a/polkadot/node/subsystem-bench/src/approval/helpers.rs b/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs similarity index 99% rename from polkadot/node/subsystem-bench/src/approval/helpers.rs rename to polkadot/node/subsystem-bench/src/lib/approval/helpers.rs index 623d91848f53f26e69ad7595c52a0e61466af6d1..af5ff5aa1facc11f2d0caa8ec77276976a0554a5 100644 --- a/polkadot/node/subsystem-bench/src/approval/helpers.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use crate::core::configuration::TestAuthorities; +use crate::configuration::TestAuthorities; use itertools::Itertools; use polkadot_node_core_approval_voting::time::{Clock, SystemClock, Tick}; use polkadot_node_network_protocol::{ diff --git a/polkadot/node/subsystem-bench/src/approval/message_generator.rs b/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs similarity index 95% rename from polkadot/node/subsystem-bench/src/approval/message_generator.rs rename to polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs index a71034013247678aeb9aced2619f8d1d6d29ebe9..3b08d0ed86158154137d4e100c5934bbf3dd8c6a 100644 --- a/polkadot/node/subsystem-bench/src/approval/message_generator.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs @@ -18,15 +18,12 @@ use crate::{ approval::{ helpers::{generate_babe_epoch, generate_topology}, test_message::{MessagesBundle, TestMessageInfo}, - ApprovalTestState, BlockTestData, GeneratedState, BUFFER_FOR_GENERATION_MILLIS, LOG_TARGET, - SLOT_DURATION_MILLIS, + ApprovalTestState, ApprovalsOptions, BlockTestData, GeneratedState, + BUFFER_FOR_GENERATION_MILLIS, LOG_TARGET, SLOT_DURATION_MILLIS, }, - core::{ - configuration::{TestAuthorities, TestConfiguration}, - mock::runtime_api::session_info_for_peers, - NODE_UNDER_TEST, - }, - ApprovalsOptions, TestObjective, + configuration::{TestAuthorities, TestConfiguration}, + mock::runtime_api::session_info_for_peers, + NODE_UNDER_TEST, }; use futures::SinkExt; use itertools::Itertools; @@ -77,8 +74,8 @@ pub struct PeerMessagesGenerator { pub validator_index: ValidatorIndex, /// An array of pre-generated random samplings, that is used to determine, which nodes would /// send a given assignment, to the node under test because of the random samplings. - /// As an optimization we generate this sampling at the begining of the test and just pick - /// one randomly, because always taking the samples would be too expensive for benchamrk. + /// As an optimization we generate this sampling at the beginning of the test and just pick + /// one randomly, because always taking the samples would be too expensive for benchmark. pub random_samplings: Vec>, /// Channel for sending the generated messages to the aggregator pub tx_messages: futures::channel::mpsc::UnboundedSender<(Hash, Vec)>, @@ -132,11 +129,7 @@ impl PeerMessagesGenerator { options: &ApprovalsOptions, ) -> String { let mut fingerprint = options.fingerprint(); - let mut exclude_objective = configuration.clone(); - // The objective contains the full content of `ApprovalOptions`, we don't want to put all of - // that in fingerprint, so execlute it because we add it manually see above. - exclude_objective.objective = TestObjective::Unimplemented; - let configuration_bytes = bincode::serialize(&exclude_objective).unwrap(); + let configuration_bytes = bincode::serialize(&configuration).unwrap(); fingerprint.extend(configuration_bytes); let mut sha1 = sha1::Sha1::new(); sha1.update(fingerprint); @@ -241,7 +234,7 @@ impl PeerMessagesGenerator { let all_messages = all_messages .into_iter() .flat_map(|(_, mut messages)| { - // Shuffle the messages inside the same tick, so that we don't priorites messages + // Shuffle the messages inside the same tick, so that we don't priorities messages // for older nodes. we try to simulate the same behaviour as in real world. messages.shuffle(&mut rand_chacha); messages @@ -567,12 +560,12 @@ struct TestSignInfo { candidate_index: CandidateIndex, /// The validator sending the assignments validator_index: ValidatorIndex, - /// The assignments convering this candidate + /// The assignments covering this candidate assignment: TestMessageInfo, } impl TestSignInfo { - /// Helper function to create a signture for all candidates in `to_sign` parameter. + /// Helper function to create a signature for all candidates in `to_sign` parameter. /// Returns a TestMessage fn sign_candidates( to_sign: &mut Vec, diff --git a/polkadot/node/subsystem-bench/src/approval/mock_chain_selection.rs b/polkadot/node/subsystem-bench/src/lib/approval/mock_chain_selection.rs similarity index 100% rename from polkadot/node/subsystem-bench/src/approval/mock_chain_selection.rs rename to polkadot/node/subsystem-bench/src/lib/approval/mock_chain_selection.rs diff --git a/polkadot/node/subsystem-bench/src/approval/mod.rs b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs similarity index 96% rename from polkadot/node/subsystem-bench/src/approval/mod.rs rename to polkadot/node/subsystem-bench/src/lib/approval/mod.rs index f07912de1887580a58770b9bf9e76624c60bdcc4..6ab5b86baede30117489b892490dd14b415650e7 100644 --- a/polkadot/node/subsystem-bench/src/approval/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs @@ -24,25 +24,21 @@ use crate::{ mock_chain_selection::MockChainSelection, test_message::{MessagesBundle, TestMessageInfo}, }, - core::{ - configuration::TestAuthorities, - environment::{ - BenchmarkUsage, TestEnvironment, TestEnvironmentDependencies, MAX_TIME_OF_FLIGHT, - }, - mock::{ - chain_api::{ChainApiState, MockChainApi}, - dummy_builder, - network_bridge::{MockNetworkBridgeRx, MockNetworkBridgeTx}, - runtime_api::MockRuntimeApi, - AlwaysSupportsParachains, TestSyncOracle, - }, - network::{ - new_network, HandleNetworkMessage, NetworkEmulatorHandle, NetworkInterface, - NetworkInterfaceReceiver, - }, - NODE_UNDER_TEST, + configuration::{TestAuthorities, TestConfiguration}, + dummy_builder, + environment::{TestEnvironment, TestEnvironmentDependencies, MAX_TIME_OF_FLIGHT}, + mock::{ + chain_api::{ChainApiState, MockChainApi}, + network_bridge::{MockNetworkBridgeRx, MockNetworkBridgeTx}, + runtime_api::MockRuntimeApi, + AlwaysSupportsParachains, TestSyncOracle, }, - TestConfiguration, + network::{ + new_network, HandleNetworkMessage, NetworkEmulatorHandle, NetworkInterface, + NetworkInterfaceReceiver, + }, + usage::BenchmarkUsage, + NODE_UNDER_TEST, }; use colored::Colorize; use futures::channel::oneshot; @@ -119,7 +115,7 @@ pub struct ApprovalsOptions { #[clap(short, long, default_value_t = 1.0)] /// Max candidate to be signed in a single approval. pub coalesce_std_dev: f32, - /// The maximum tranche diff between approvals coalesced toghther. + /// The maximum tranche diff between approvals coalesced together. pub coalesce_tranche_diff: u32, #[clap(short, long, default_value_t = false)] /// Enable assignments v2. @@ -174,7 +170,7 @@ struct BlockTestData { total_candidates_before: u64, /// The votes we sent. /// votes[validator_index][candidate_index] tells if validator sent vote for candidate. - /// We use this to mark the test as succesfull if GetApprovalSignatures returns all the votes + /// We use this to mark the test as successful if GetApprovalSignatures returns all the votes /// from here. votes: Arc>>, } @@ -241,7 +237,7 @@ struct GeneratedState { } /// Approval test state used by all mock subsystems to be able to answer messages emitted -/// by the approval-voting and approval-distribution-subystems. +/// by the approval-voting and approval-distribution-subsystems. /// /// This gets cloned across all mock subsystems, so if there is any information that gets /// updated between subsystems, they would have to be wrapped in Arc's. @@ -472,11 +468,9 @@ impl ApprovalTestState { impl HandleNetworkMessage for ApprovalTestState { fn handle( &self, - _message: crate::core::network::NetworkMessage, - _node_sender: &mut futures::channel::mpsc::UnboundedSender< - crate::core::network::NetworkMessage, - >, - ) -> Option { + _message: crate::network::NetworkMessage, + _node_sender: &mut futures::channel::mpsc::UnboundedSender, + ) -> Option { self.total_sent_messages_from_node .as_ref() .fetch_add(1, std::sync::atomic::Ordering::SeqCst); @@ -504,7 +498,7 @@ struct PeerMessageProducer { impl PeerMessageProducer { /// Generates messages by spawning a blocking task in the background which begins creating - /// the assignments/approvals and peer view changes at the begining of each block. + /// the assignments/approvals and peer view changes at the beginning of each block. fn produce_messages( mut self, env: &TestEnvironment, @@ -746,7 +740,7 @@ impl PeerMessageProducer { } } - // Initializes the candidates test data. This is used for bookeeping if more assignments and + // Initializes the candidates test data. This is used for bookkeeping if more assignments and // approvals would be needed. fn initialize_candidates_test_data( &self, @@ -773,7 +767,7 @@ impl PeerMessageProducer { } /// Helper function to build an overseer with the real implementation for `ApprovalDistribution` and -/// `ApprovalVoting` subystems and mock subsytems for all others. +/// `ApprovalVoting` subsystems and mock subsystems for all others. fn build_overseer( state: &ApprovalTestState, network: &NetworkEmulatorHandle, @@ -841,8 +835,14 @@ fn build_overseer( pub fn prepare_test( config: TestConfiguration, options: ApprovalsOptions, + with_prometheus_endpoint: bool, ) -> (TestEnvironment, ApprovalTestState) { - prepare_test_inner(config, TestEnvironmentDependencies::default(), options) + prepare_test_inner( + config, + TestEnvironmentDependencies::default(), + options, + with_prometheus_endpoint, + ) } /// Build the test environment for an Approval benchmark. @@ -850,6 +850,7 @@ fn prepare_test_inner( config: TestConfiguration, dependencies: TestEnvironmentDependencies, options: ApprovalsOptions, + with_prometheus_endpoint: bool, ) -> (TestEnvironment, ApprovalTestState) { gum::info!("Prepare test state"); let state = ApprovalTestState::new(&config, options, &dependencies); @@ -878,6 +879,7 @@ fn prepare_test_inner( overseer, overseer_handle, state.test_authorities.clone(), + with_prometheus_endpoint, ), state, ) @@ -934,7 +936,7 @@ pub async fn bench_approvals_run( for block_num in 0..env.config().num_blocks { let mut current_slot = tick_to_slot_number(SLOT_DURATION_MILLIS, system_clock.tick_now()); - // Wait untill the time arrieves at the first slot under test. + // Wait until the time arrives at the first slot under test. while current_slot < state.generated_state.initial_slot { sleep(Duration::from_millis(5)).await; current_slot = tick_to_slot_number(SLOT_DURATION_MILLIS, system_clock.tick_now()); @@ -959,7 +961,7 @@ pub async fn bench_approvals_run( } // Wait for all blocks to be approved before exiting. - // This is an invariant of the benchmark, if this does not happen something went teribbly wrong. + // This is an invariant of the benchmark, if this does not happen something went terribly wrong. while state.last_approved_block.load(std::sync::atomic::Ordering::SeqCst) < env.config().num_blocks as u32 { diff --git a/polkadot/node/subsystem-bench/src/approval/test_message.rs b/polkadot/node/subsystem-bench/src/lib/approval/test_message.rs similarity index 95% rename from polkadot/node/subsystem-bench/src/approval/test_message.rs rename to polkadot/node/subsystem-bench/src/lib/approval/test_message.rs index 8aaabc3426c803563bdb2ed8455eed8a0061484e..f55ed99205ede2b8ed254248d9a024bde3d46a11 100644 --- a/polkadot/node/subsystem-bench/src/approval/test_message.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/test_message.rs @@ -15,9 +15,8 @@ // along with Polkadot. If not, see . use crate::{ - approval::{BlockTestData, CandidateTestData}, - core::configuration::TestAuthorities, - ApprovalsOptions, + approval::{ApprovalsOptions, BlockTestData, CandidateTestData}, + configuration::TestAuthorities, }; use itertools::Itertools; use parity_scale_codec::{Decode, Encode}; @@ -32,7 +31,7 @@ pub struct TestMessageInfo { pub msg: protocol_v3::ApprovalDistributionMessage, /// The list of peers that would sends this message in a real topology. /// It includes both the peers that would send the message because of the topology - /// or because of randomly chosing so. + /// or because of randomly choosing so. pub sent_by: Vec, /// The tranche at which this message should be sent. pub tranche: u32, @@ -91,7 +90,7 @@ impl MessagesBundle { /// Tells if the bundle is needed for sending. /// We either send it because we need more assignments and approvals to approve the candidates - /// or because we configured the test to send messages untill a given tranche. + /// or because we configured the test to send messages until a given tranche. pub fn should_send( &self, candidates_test_data: &HashMap<(Hash, CandidateIndex), CandidateTestData>, @@ -175,24 +174,24 @@ impl TestMessageInfo { } } - /// Returns a list of candidates indicies in this message + /// Returns a list of candidates indices in this message pub fn candidate_indices(&self) -> HashSet { - let mut unique_candidate_indicies = HashSet::new(); + let mut unique_candidate_indices = HashSet::new(); match &self.msg { protocol_v3::ApprovalDistributionMessage::Assignments(assignments) => for (_assignment, candidate_indices) in assignments { for candidate_index in candidate_indices.iter_ones() { - unique_candidate_indicies.insert(candidate_index); + unique_candidate_indices.insert(candidate_index); } }, protocol_v3::ApprovalDistributionMessage::Approvals(approvals) => for approval in approvals { for candidate_index in approval.candidate_indices.iter_ones() { - unique_candidate_indicies.insert(candidate_index); + unique_candidate_indices.insert(candidate_index); } }, } - unique_candidate_indicies + unique_candidate_indices } /// Marks this message as no-shows if the number of configured no-shows is above the registered diff --git a/polkadot/node/subsystem-bench/src/availability/av_store_helpers.rs b/polkadot/node/subsystem-bench/src/lib/availability/av_store_helpers.rs similarity index 94% rename from polkadot/node/subsystem-bench/src/availability/av_store_helpers.rs rename to polkadot/node/subsystem-bench/src/lib/availability/av_store_helpers.rs index 261dbd0376c732fb4688e6251f32e21d3ec8d4d4..3300def2235ee0ea90f466316f9bc5a7a965869c 100644 --- a/polkadot/node/subsystem-bench/src/availability/av_store_helpers.rs +++ b/polkadot/node/subsystem-bench/src/lib/availability/av_store_helpers.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use crate::core::{environment::TestEnvironmentDependencies, mock::TestSyncOracle}; +use crate::{environment::TestEnvironmentDependencies, mock::TestSyncOracle}; use polkadot_node_core_av_store::{AvailabilityStoreSubsystem, Config}; use polkadot_node_metrics::metrics::Metrics; use polkadot_node_subsystem_util::database::Database; diff --git a/polkadot/node/subsystem-bench/src/availability/mod.rs b/polkadot/node/subsystem-bench/src/lib/availability/mod.rs similarity index 53% rename from polkadot/node/subsystem-bench/src/availability/mod.rs rename to polkadot/node/subsystem-bench/src/lib/availability/mod.rs index ad9a17ff8f47a1a3dc7e8e8e990cb0b1f7f262f6..fe98666906183df9a987a692b4a8be646eab60be 100644 --- a/polkadot/node/subsystem-bench/src/availability/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/availability/mod.rs @@ -15,29 +15,21 @@ // along with Polkadot. If not, see . use crate::{ - core::{ - configuration::TestConfiguration, - environment::{BenchmarkUsage, TestEnvironmentDependencies}, - mock::{ - av_store, - av_store::MockAvailabilityStore, - chain_api::{ChainApiState, MockChainApi}, - dummy_builder, - network_bridge::{self, MockNetworkBridgeRx, MockNetworkBridgeTx}, - runtime_api, - runtime_api::MockRuntimeApi, - AlwaysSupportsParachains, - }, - network::new_network, + availability::av_store_helpers::new_av_store, + dummy_builder, + environment::{TestEnvironment, TestEnvironmentDependencies, GENESIS_HASH}, + mock::{ + av_store::{self, MockAvailabilityStore, NetworkAvailabilityState}, + chain_api::{ChainApiState, MockChainApi}, + network_bridge::{self, MockNetworkBridgeRx, MockNetworkBridgeTx}, + runtime_api::{self, MockRuntimeApi}, + AlwaysSupportsParachains, }, - TestEnvironment, TestObjective, GENESIS_HASH, + network::new_network, + usage::BenchmarkUsage, }; -use av_store::NetworkAvailabilityState; -use av_store_helpers::new_av_store; -use bitvec::bitvec; use colored::Colorize; use futures::{channel::oneshot, stream::FuturesUnordered, StreamExt}; -use itertools::Itertools; use parity_scale_codec::Encode; use polkadot_availability_bitfield_distribution::BitfieldDistribution; use polkadot_availability_distribution::{ @@ -47,40 +39,46 @@ use polkadot_availability_recovery::AvailabilityRecoverySubsystem; use polkadot_node_core_av_store::AvailabilityStoreSubsystem; use polkadot_node_metrics::metrics::Metrics; use polkadot_node_network_protocol::{ - request_response::{v1::ChunkFetchingRequest, IncomingRequest, ReqProtocolNames}, - OurView, Versioned, VersionedValidationProtocol, + request_response::{IncomingRequest, ReqProtocolNames}, + OurView, }; -use polkadot_node_primitives::{AvailableData, BlockData, ErasureChunk, PoV}; use polkadot_node_subsystem::{ messages::{AllMessages, AvailabilityRecoveryMessage}, Overseer, OverseerConnector, SpawnGlue, }; -use polkadot_node_subsystem_test_helpers::{ - derive_erasure_chunks_with_proofs_and_root, mock::new_block_import_info, -}; use polkadot_node_subsystem_types::{ messages::{AvailabilityStoreMessage, NetworkBridgeEvent}, Span, }; use polkadot_overseer::{metrics::Metrics as OverseerMetrics, Handle as OverseerHandle}; -use polkadot_primitives::{ - AvailabilityBitfield, BlockNumber, CandidateHash, CandidateReceipt, GroupIndex, Hash, HeadData, - Header, PersistedValidationData, Signed, SigningContext, ValidatorIndex, -}; -use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash}; -use sc_network::{ - request_responses::{IncomingRequest as RawIncomingRequest, ProtocolConfig}, - PeerId, -}; +use polkadot_primitives::GroupIndex; +use sc_network::request_responses::{IncomingRequest as RawIncomingRequest, ProtocolConfig}; use sc_service::SpawnTaskHandle; -use sp_core::H256; -use std::{collections::HashMap, iter::Cycle, ops::Sub, sync::Arc, time::Instant}; +use serde::{Deserialize, Serialize}; +use std::{ops::Sub, sync::Arc, time::Instant}; +pub use test_state::TestState; mod av_store_helpers; -pub(crate) mod cli; +mod test_state; const LOG_TARGET: &str = "subsystem-bench::availability"; +#[derive(Debug, Clone, Serialize, Deserialize, clap::Parser)] +#[clap(rename_all = "kebab-case")] +#[allow(missing_docs)] +pub struct DataAvailabilityReadOptions { + #[clap(short, long, default_value_t = false)] + /// Turbo boost AD Read by fetching the full availability datafrom backers first. Saves CPU as + /// we don't need to re-construct from chunks. Typically this is only faster if nodes have + /// enough bandwidth. + pub fetch_from_backers: bool, +} + +pub enum TestDataAvailability { + Read(DataAvailabilityReadOptions), + Write, +} + fn build_overseer_for_availability_read( spawn_task_handle: SpawnTaskHandle, runtime_api: MockRuntimeApi, @@ -137,87 +135,51 @@ fn build_overseer_for_availability_write( (overseer, OverseerHandle::new(raw_handle)) } -/// Takes a test configuration and uses it to create the `TestEnvironment`. pub fn prepare_test( - config: TestConfiguration, - state: &mut TestState, -) -> (TestEnvironment, Vec) { - prepare_test_inner(config, state, TestEnvironmentDependencies::default()) -} - -fn prepare_test_inner( - config: TestConfiguration, - state: &mut TestState, - dependencies: TestEnvironmentDependencies, + state: &TestState, + mode: TestDataAvailability, + with_prometheus_endpoint: bool, ) -> (TestEnvironment, Vec) { - // Generate test authorities. - let test_authorities = config.generate_authorities(); - - let mut candidate_hashes: HashMap> = HashMap::new(); - - // Prepare per block candidates. - // Genesis block is always finalized, so we start at 1. - for block_num in 1..=config.num_blocks { - for _ in 0..config.n_cores { - candidate_hashes - .entry(Hash::repeat_byte(block_num as u8)) - .or_default() - .push(state.next_candidate().expect("Cycle iterator")) - } - - // First candidate is our backed candidate. - state.backed_candidates.push( - candidate_hashes - .get(&Hash::repeat_byte(block_num as u8)) - .expect("just inserted above") - .first() - .expect("just inserted above") - .clone(), - ); - } - - let runtime_api = runtime_api::MockRuntimeApi::new( - config.clone(), - test_authorities.clone(), - candidate_hashes, - Default::default(), - Default::default(), - 0, - ); - - let availability_state = NetworkAvailabilityState { - candidate_hashes: state.candidate_hashes.clone(), - available_data: state.available_data.clone(), - chunks: state.chunks.clone(), - }; - - let mut req_cfgs = Vec::new(); - let (collation_req_receiver, collation_req_cfg) = IncomingRequest::get_config_receiver(&ReqProtocolNames::new(GENESIS_HASH, None)); - req_cfgs.push(collation_req_cfg); - let (pov_req_receiver, pov_req_cfg) = IncomingRequest::get_config_receiver(&ReqProtocolNames::new(GENESIS_HASH, None)); - let (chunk_req_receiver, chunk_req_cfg) = IncomingRequest::get_config_receiver(&ReqProtocolNames::new(GENESIS_HASH, None)); - req_cfgs.push(pov_req_cfg); + let req_cfgs = vec![collation_req_cfg, pov_req_cfg]; - let (network, network_interface, network_receiver) = - new_network(&config, &dependencies, &test_authorities, vec![Arc::new(availability_state)]); + let dependencies = TestEnvironmentDependencies::default(); + let availability_state = NetworkAvailabilityState { + candidate_hashes: state.candidate_hashes.clone(), + available_data: state.available_data.clone(), + chunks: state.chunks.clone(), + }; + let (network, network_interface, network_receiver) = new_network( + &state.config, + &dependencies, + &state.test_authorities, + vec![Arc::new(availability_state.clone())], + ); let network_bridge_tx = network_bridge::MockNetworkBridgeTx::new( network.clone(), network_interface.subsystem_sender(), - test_authorities.clone(), + state.test_authorities.clone(), ); - let network_bridge_rx = - network_bridge::MockNetworkBridgeRx::new(network_receiver, Some(chunk_req_cfg.clone())); + network_bridge::MockNetworkBridgeRx::new(network_receiver, Some(chunk_req_cfg)); - let (overseer, overseer_handle) = match &state.config().objective { - TestObjective::DataAvailabilityRead(options) => { + let runtime_api = runtime_api::MockRuntimeApi::new( + state.config.clone(), + state.test_authorities.clone(), + state.candidate_receipts.clone(), + Default::default(), + Default::default(), + 0, + ); + + let (overseer, overseer_handle) = match &mode { + TestDataAvailability::Read(options) => { let use_fast_path = options.fetch_from_backers; let subsystem = if use_fast_path { @@ -247,29 +209,14 @@ fn prepare_test_inner( &dependencies, ) }, - TestObjective::DataAvailabilityWrite => { + TestDataAvailability::Write => { let availability_distribution = AvailabilityDistributionSubsystem::new( - test_authorities.keyring.keystore(), + state.test_authorities.keyring.keystore(), IncomingRequestReceivers { pov_req_receiver, chunk_req_receiver }, Metrics::try_register(&dependencies.registry).unwrap(), ); - let block_headers = (1..=config.num_blocks) - .map(|block_number| { - ( - Hash::repeat_byte(block_number as u8), - Header { - digest: Default::default(), - number: block_number as BlockNumber, - parent_hash: Default::default(), - extrinsics_root: Default::default(), - state_root: Default::default(), - }, - ) - }) - .collect::>(); - - let chain_api_state = ChainApiState { block_headers }; + let chain_api_state = ChainApiState { block_headers: state.block_headers.clone() }; let chain_api = MockChainApi::new(chain_api_state); let bitfield_distribution = BitfieldDistribution::new(Metrics::try_register(&dependencies.registry).unwrap()); @@ -284,178 +231,47 @@ fn prepare_test_inner( &dependencies, ) }, - _ => { - unimplemented!("Invalid test objective") - }, }; ( TestEnvironment::new( dependencies, - config, + state.config.clone(), network, overseer, overseer_handle, - test_authorities, + state.test_authorities.clone(), + with_prometheus_endpoint, ), req_cfgs, ) } -#[derive(Clone)] -pub struct TestState { - // Full test configuration - config: TestConfiguration, - // A cycle iterator on all PoV sizes used in the test. - pov_sizes: Cycle>, - // Generated candidate receipts to be used in the test - candidates: Cycle>, - // Map from pov size to candidate index - pov_size_to_candidate: HashMap, - // Map from generated candidate hashes to candidate index in `available_data` - // and `chunks`. - candidate_hashes: HashMap, - // Per candidate index receipts. - candidate_receipt_templates: Vec, - // Per candidate index `AvailableData` - available_data: Vec, - // Per candiadte index chunks - chunks: Vec>, - // Per relay chain block - candidate backed by our backing group - backed_candidates: Vec, -} - -impl TestState { - fn config(&self) -> &TestConfiguration { - &self.config - } - - pub fn next_candidate(&mut self) -> Option { - let candidate = self.candidates.next(); - let candidate_hash = candidate.as_ref().unwrap().hash(); - gum::trace!(target: LOG_TARGET, "Next candidate selected {:?}", candidate_hash); - candidate - } - - /// Generate candidates to be used in the test. - fn generate_candidates(&mut self) { - let count = self.config.n_cores * self.config.num_blocks; - gum::info!(target: LOG_TARGET,"{}", format!("Pre-generating {} candidates.", count).bright_blue()); - - // Generate all candidates - self.candidates = (0..count) - .map(|index| { - let pov_size = self.pov_sizes.next().expect("This is a cycle; qed"); - let candidate_index = *self - .pov_size_to_candidate - .get(&pov_size) - .expect("pov_size always exists; qed"); - let mut candidate_receipt = - self.candidate_receipt_templates[candidate_index].clone(); - - // Make it unique. - candidate_receipt.descriptor.relay_parent = Hash::from_low_u64_be(index as u64); - // Store the new candidate in the state - self.candidate_hashes.insert(candidate_receipt.hash(), candidate_index); - - gum::debug!(target: LOG_TARGET, candidate_hash = ?candidate_receipt.hash(), "new candidate"); - - candidate_receipt - }) - .collect::>() - .into_iter() - .cycle(); - } - - pub fn new(config: &TestConfiguration) -> Self { - let config = config.clone(); - - let mut chunks = Vec::new(); - let mut available_data = Vec::new(); - let mut candidate_receipt_templates = Vec::new(); - let mut pov_size_to_candidate = HashMap::new(); - - // we use it for all candidates. - let persisted_validation_data = PersistedValidationData { - parent_head: HeadData(vec![7, 8, 9]), - relay_parent_number: Default::default(), - max_pov_size: 1024, - relay_parent_storage_root: Default::default(), - }; - - // For each unique pov we create a candidate receipt. - for (index, pov_size) in config.pov_sizes().iter().cloned().unique().enumerate() { - gum::info!(target: LOG_TARGET, index, pov_size, "{}", "Generating template candidate".bright_blue()); - - let mut candidate_receipt = dummy_candidate_receipt(dummy_hash()); - let pov = PoV { block_data: BlockData(vec![index as u8; pov_size]) }; - - let new_available_data = AvailableData { - validation_data: persisted_validation_data.clone(), - pov: Arc::new(pov), - }; - - let (new_chunks, erasure_root) = derive_erasure_chunks_with_proofs_and_root( - config.n_validators, - &new_available_data, - |_, _| {}, - ); - - candidate_receipt.descriptor.erasure_root = erasure_root; - - chunks.push(new_chunks); - available_data.push(new_available_data); - pov_size_to_candidate.insert(pov_size, index); - candidate_receipt_templates.push(candidate_receipt); - } - - gum::info!(target: LOG_TARGET, "{}","Created test environment.".bright_blue()); - - let mut _self = Self { - available_data, - candidate_receipt_templates, - chunks, - pov_size_to_candidate, - pov_sizes: Vec::from(config.pov_sizes()).into_iter().cycle(), - candidate_hashes: HashMap::new(), - candidates: Vec::new().into_iter().cycle(), - backed_candidates: Vec::new(), - config, - }; - - _self.generate_candidates(); - _self - } - - pub fn backed_candidates(&mut self) -> &mut Vec { - &mut self.backed_candidates - } -} - pub async fn benchmark_availability_read( benchmark_name: &str, env: &mut TestEnvironment, - mut state: TestState, + state: &TestState, ) -> BenchmarkUsage { let config = env.config().clone(); - env.import_block(new_block_import_info(Hash::repeat_byte(1), 1)).await; - - let test_start = Instant::now(); - let mut batch = FuturesUnordered::new(); - let mut availability_bytes = 0u128; - env.metrics().set_n_validators(config.n_validators); env.metrics().set_n_cores(config.n_cores); - for block_num in 1..=env.config().num_blocks { + let mut batch = FuturesUnordered::new(); + let mut availability_bytes = 0u128; + let mut candidates = state.candidates.clone(); + let test_start = Instant::now(); + for block_info in state.block_infos.iter() { + let block_num = block_info.number as usize; gum::info!(target: LOG_TARGET, "Current block {}/{}", block_num, env.config().num_blocks); env.metrics().set_current_block(block_num); let block_start_ts = Instant::now(); + env.import_block(block_info.clone()).await; + for candidate_num in 0..config.n_cores as u64 { let candidate = - state.next_candidate().expect("We always send up to n_cores*num_blocks; qed"); + candidates.next().expect("We always send up to n_cores*num_blocks; qed"); let (tx, rx) = oneshot::channel(); batch.push(rx); @@ -503,7 +319,7 @@ pub async fn benchmark_availability_read( pub async fn benchmark_availability_write( benchmark_name: &str, env: &mut TestEnvironment, - mut state: TestState, + state: &TestState, ) -> BenchmarkUsage { let config = env.config().clone(); @@ -511,7 +327,7 @@ pub async fn benchmark_availability_write( env.metrics().set_n_cores(config.n_cores); gum::info!(target: LOG_TARGET, "Seeding availability store with candidates ..."); - for backed_candidate in state.backed_candidates().clone() { + for backed_candidate in state.backed_candidates.clone() { let candidate_index = *state.candidate_hashes.get(&backed_candidate.hash()).unwrap(); let available_data = state.available_data[candidate_index].clone(); let (tx, rx) = oneshot::channel(); @@ -534,15 +350,14 @@ pub async fn benchmark_availability_write( gum::info!(target: LOG_TARGET, "Done"); let test_start = Instant::now(); - - for block_num in 1..=env.config().num_blocks { + for block_info in state.block_infos.iter() { + let block_num = block_info.number as usize; gum::info!(target: LOG_TARGET, "Current block #{}", block_num); env.metrics().set_current_block(block_num); let block_start_ts = Instant::now(); - let relay_block_hash = Hash::repeat_byte(block_num as u8); - env.import_block(new_block_import_info(relay_block_hash, block_num as BlockNumber)) - .await; + let relay_block_hash = block_info.hash; + env.import_block(block_info.clone()).await; // Inform bitfield distribution about our view of current test block let message = polkadot_node_subsystem_types::messages::BitfieldDistributionMessage::NetworkBridgeUpdate( @@ -553,20 +368,13 @@ pub async fn benchmark_availability_write( let chunk_fetch_start_ts = Instant::now(); // Request chunks of our own backed candidate from all other validators. - let mut receivers = Vec::new(); - for index in 1..config.n_validators { + let payloads = state.chunk_fetching_requests.get(block_num - 1).expect("pregenerated"); + let receivers = (1..config.n_validators).filter_map(|index| { let (pending_response, pending_response_receiver) = oneshot::channel(); - let request = RawIncomingRequest { - peer: PeerId::random(), - payload: ChunkFetchingRequest { - candidate_hash: state.backed_candidates()[block_num - 1].hash(), - index: ValidatorIndex(index as u32), - } - .encode(), - pending_response, - }; - + let peer_id = *env.authorities().peer_ids.get(index).expect("all validators have ids"); + let payload = payloads.get(index).expect("pregenerated").clone(); + let request = RawIncomingRequest { peer: peer_id, payload, pending_response }; let peer = env .authorities() .validator_authority_id @@ -576,59 +384,39 @@ pub async fn benchmark_availability_write( if env.network().is_peer_connected(peer) && env.network().send_request_from_peer(peer, request).is_ok() { - receivers.push(pending_response_receiver); + Some(pending_response_receiver) + } else { + None } - } + }); gum::info!(target: LOG_TARGET, "Waiting for all emulated peers to receive their chunk from us ..."); - for receiver in receivers.into_iter() { - let response = receiver.await.expect("Chunk is always served succesfully"); - // TODO: check if chunk is the one the peer expects to receive. - assert!(response.result.is_ok()); - } - let chunk_fetch_duration = Instant::now().sub(chunk_fetch_start_ts).as_millis(); + let responses = futures::future::try_join_all(receivers) + .await + .expect("Chunk is always served successfully"); + // TODO: check if chunk is the one the peer expects to receive. + assert!(responses.iter().all(|v| v.result.is_ok())); + let chunk_fetch_duration = Instant::now().sub(chunk_fetch_start_ts).as_millis(); gum::info!(target: LOG_TARGET, "All chunks received in {}ms", chunk_fetch_duration); - let signing_context = SigningContext { session_index: 0, parent_hash: relay_block_hash }; let network = env.network().clone(); let authorities = env.authorities().clone(); - let n_validators = config.n_validators; - // Spawn a task that will generate `n_validator` - 1 signed bitfiends and + // Spawn a task that will generate `n_validator` - 1 signed bitfields and // send them from the emulated peers to the subsystem. // TODO: Implement topology. - env.spawn_blocking("send-bitfields", async move { - for index in 1..n_validators { - let validator_public = - authorities.validator_public.get(index).expect("All validator keys are known"); - - // Node has all the chunks in the world. - let payload: AvailabilityBitfield = - AvailabilityBitfield(bitvec![u8, bitvec::order::Lsb0; 1u8; 32]); - // TODO(soon): Use pre-signed messages. This is quite intensive on the CPU. - let signed_bitfield = Signed::::sign( - &authorities.keyring.keystore(), - payload, - &signing_context, - ValidatorIndex(index as u32), - validator_public, - ) - .ok() - .flatten() - .expect("should be signed"); - - let from_peer = &authorities.validator_authority_id[index]; - - let message = peer_bitfield_message_v2(relay_block_hash, signed_bitfield); + let messages = state.signed_bitfields.get(&relay_block_hash).expect("pregenerated").clone(); + for index in 1..config.n_validators { + let from_peer = &authorities.validator_authority_id[index]; + let message = messages.get(index).expect("pregenerated").clone(); - // Send the action from peer only if it is connected to our node. - if network.is_peer_connected(from_peer) { - let _ = network.send_message_from_peer(from_peer, message); - } + // Send the action from peer only if it is connected to our node. + if network.is_peer_connected(from_peer) { + let _ = network.send_message_from_peer(from_peer, message); } - }); + } gum::info!( "Waiting for {} bitfields to be received and processed", @@ -637,7 +425,7 @@ pub async fn benchmark_availability_write( // Wait for all bitfields to be processed. env.wait_until_metric( - "polkadot_parachain_received_availabilty_bitfields_total", + "polkadot_parachain_received_availability_bitfields_total", None, |value| value == (config.connected_count() * block_num) as f64, ) @@ -663,17 +451,3 @@ pub async fn benchmark_availability_write( &["availability-distribution", "bitfield-distribution", "availability-store"], ) } - -pub fn peer_bitfield_message_v2( - relay_hash: H256, - signed_bitfield: Signed, -) -> VersionedValidationProtocol { - let bitfield = polkadot_node_network_protocol::v2::BitfieldDistributionMessage::Bitfield( - relay_hash, - signed_bitfield.into(), - ); - - Versioned::V2(polkadot_node_network_protocol::v2::ValidationProtocol::BitfieldDistribution( - bitfield, - )) -} diff --git a/polkadot/node/subsystem-bench/src/lib/availability/test_state.rs b/polkadot/node/subsystem-bench/src/lib/availability/test_state.rs new file mode 100644 index 0000000000000000000000000000000000000000..c328ffedf916e1ae9ce7fcd05e25750e01dc7506 --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/availability/test_state.rs @@ -0,0 +1,268 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +use crate::configuration::{TestAuthorities, TestConfiguration}; +use bitvec::bitvec; +use colored::Colorize; +use itertools::Itertools; +use parity_scale_codec::Encode; +use polkadot_node_network_protocol::{ + request_response::v1::ChunkFetchingRequest, Versioned, VersionedValidationProtocol, +}; +use polkadot_node_primitives::{AvailableData, BlockData, ErasureChunk, PoV}; +use polkadot_node_subsystem_test_helpers::{ + derive_erasure_chunks_with_proofs_and_root, mock::new_block_import_info, +}; +use polkadot_overseer::BlockInfo; +use polkadot_primitives::{ + AvailabilityBitfield, BlockNumber, CandidateHash, CandidateReceipt, Hash, HeadData, Header, + PersistedValidationData, Signed, SigningContext, ValidatorIndex, +}; +use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash}; +use sp_core::H256; +use std::{collections::HashMap, iter::Cycle, sync::Arc}; + +const LOG_TARGET: &str = "subsystem-bench::availability::test_state"; + +#[derive(Clone)] +pub struct TestState { + // Full test configuration + pub config: TestConfiguration, + // A cycle iterator on all PoV sizes used in the test. + pub pov_sizes: Cycle>, + // Generated candidate receipts to be used in the test + pub candidates: Cycle>, + // Map from pov size to candidate index + pub pov_size_to_candidate: HashMap, + // Map from generated candidate hashes to candidate index in `available_data` and `chunks`. + pub candidate_hashes: HashMap, + // Per candidate index receipts. + pub candidate_receipt_templates: Vec, + // Per candidate index `AvailableData` + pub available_data: Vec, + // Per candiadte index chunks + pub chunks: Vec>, + // Per relay chain block - candidate backed by our backing group + pub backed_candidates: Vec, + // Relay chain block infos + pub block_infos: Vec, + // Chung fetching requests for backed candidates + pub chunk_fetching_requests: Vec>>, + // Pregenerated signed availability bitfields + pub signed_bitfields: HashMap>, + // Relay chain block headers + pub block_headers: HashMap, + // Authority keys for the network emulation. + pub test_authorities: TestAuthorities, + // Map from generated candidate receipts + pub candidate_receipts: HashMap>, +} + +impl TestState { + pub fn new(config: &TestConfiguration) -> Self { + let mut test_state = Self { + available_data: Default::default(), + candidate_receipt_templates: Default::default(), + chunks: Default::default(), + pov_size_to_candidate: Default::default(), + pov_sizes: Vec::from(config.pov_sizes()).into_iter().cycle(), + candidate_hashes: HashMap::new(), + candidates: Vec::new().into_iter().cycle(), + backed_candidates: Vec::new(), + config: config.clone(), + block_infos: Default::default(), + chunk_fetching_requests: Default::default(), + signed_bitfields: Default::default(), + candidate_receipts: Default::default(), + block_headers: Default::default(), + test_authorities: config.generate_authorities(), + }; + + // we use it for all candidates. + let persisted_validation_data = PersistedValidationData { + parent_head: HeadData(vec![7, 8, 9]), + relay_parent_number: Default::default(), + max_pov_size: 1024, + relay_parent_storage_root: Default::default(), + }; + + // For each unique pov we create a candidate receipt. + for (index, pov_size) in config.pov_sizes().iter().cloned().unique().enumerate() { + gum::info!(target: LOG_TARGET, index, pov_size, "{}", "Generating template candidate".bright_blue()); + + let mut candidate_receipt = dummy_candidate_receipt(dummy_hash()); + let pov = PoV { block_data: BlockData(vec![index as u8; pov_size]) }; + + let new_available_data = AvailableData { + validation_data: persisted_validation_data.clone(), + pov: Arc::new(pov), + }; + + let (new_chunks, erasure_root) = derive_erasure_chunks_with_proofs_and_root( + config.n_validators, + &new_available_data, + |_, _| {}, + ); + + candidate_receipt.descriptor.erasure_root = erasure_root; + + test_state.chunks.push(new_chunks); + test_state.available_data.push(new_available_data); + test_state.pov_size_to_candidate.insert(pov_size, index); + test_state.candidate_receipt_templates.push(candidate_receipt); + } + + test_state.block_infos = (1..=config.num_blocks) + .map(|block_num| { + let relay_block_hash = Hash::repeat_byte(block_num as u8); + new_block_import_info(relay_block_hash, block_num as BlockNumber) + }) + .collect(); + + test_state.block_headers = test_state + .block_infos + .iter() + .map(|info| { + ( + info.hash, + Header { + digest: Default::default(), + number: info.number, + parent_hash: info.parent_hash, + extrinsics_root: Default::default(), + state_root: Default::default(), + }, + ) + }) + .collect::>(); + + // Generate all candidates + let candidates_count = config.n_cores * config.num_blocks; + gum::info!(target: LOG_TARGET,"{}", format!("Pre-generating {} candidates.", candidates_count).bright_blue()); + test_state.candidates = (0..candidates_count) + .map(|index| { + let pov_size = test_state.pov_sizes.next().expect("This is a cycle; qed"); + let candidate_index = *test_state + .pov_size_to_candidate + .get(&pov_size) + .expect("pov_size always exists; qed"); + let mut candidate_receipt = + test_state.candidate_receipt_templates[candidate_index].clone(); + + // Make it unique. + candidate_receipt.descriptor.relay_parent = Hash::from_low_u64_be(index as u64); + // Store the new candidate in the state + test_state.candidate_hashes.insert(candidate_receipt.hash(), candidate_index); + + gum::debug!(target: LOG_TARGET, candidate_hash = ?candidate_receipt.hash(), "new candidate"); + + candidate_receipt + }) + .collect::>() + .into_iter() + .cycle(); + + // Prepare per block candidates. + // Genesis block is always finalized, so we start at 1. + for info in test_state.block_infos.iter() { + for _ in 0..config.n_cores { + let receipt = test_state.candidates.next().expect("Cycle iterator"); + test_state.candidate_receipts.entry(info.hash).or_default().push(receipt); + } + + // First candidate is our backed candidate. + test_state.backed_candidates.push( + test_state + .candidate_receipts + .get(&info.hash) + .expect("just inserted above") + .first() + .expect("just inserted above") + .clone(), + ); + } + + test_state.chunk_fetching_requests = test_state + .backed_candidates + .iter() + .map(|candidate| { + (0..config.n_validators) + .map(|index| { + ChunkFetchingRequest { + candidate_hash: candidate.hash(), + index: ValidatorIndex(index as u32), + } + .encode() + }) + .collect::>() + }) + .collect::>(); + + test_state.signed_bitfields = test_state + .block_infos + .iter() + .map(|block_info| { + let signing_context = + SigningContext { session_index: 0, parent_hash: block_info.hash }; + let messages = (0..config.n_validators) + .map(|index| { + let validator_public = test_state + .test_authorities + .validator_public + .get(index) + .expect("All validator keys are known"); + + // Node has all the chunks in the world. + let payload: AvailabilityBitfield = + AvailabilityBitfield(bitvec![u8, bitvec::order::Lsb0; 1u8; 32]); + let signed_bitfield = Signed::::sign( + &test_state.test_authorities.keyring.keystore(), + payload, + &signing_context, + ValidatorIndex(index as u32), + validator_public, + ) + .ok() + .flatten() + .expect("should be signed"); + + peer_bitfield_message_v2(block_info.hash, signed_bitfield) + }) + .collect::>(); + + (block_info.hash, messages) + }) + .collect(); + + gum::info!(target: LOG_TARGET, "{}","Created test environment.".bright_blue()); + + test_state + } +} + +fn peer_bitfield_message_v2( + relay_hash: H256, + signed_bitfield: Signed, +) -> VersionedValidationProtocol { + let bitfield = polkadot_node_network_protocol::v2::BitfieldDistributionMessage::Bitfield( + relay_hash, + signed_bitfield.into(), + ); + + Versioned::V2(polkadot_node_network_protocol::v2::ValidationProtocol::BitfieldDistribution( + bitfield, + )) +} diff --git a/polkadot/node/subsystem-bench/src/core/configuration.rs b/polkadot/node/subsystem-bench/src/lib/configuration.rs similarity index 76% rename from polkadot/node/subsystem-bench/src/core/configuration.rs rename to polkadot/node/subsystem-bench/src/lib/configuration.rs index 00be2a86b173a61465d7610d75df1abbe09363f0..5725a5137ec4bb602db15f88dfce6a300f1c4bcd 100644 --- a/polkadot/node/subsystem-bench/src/core/configuration.rs +++ b/polkadot/node/subsystem-bench/src/lib/configuration.rs @@ -16,7 +16,7 @@ //! Test configuration definition and helpers. -use crate::{core::keyring::Keyring, TestObjective}; +use crate::keyring::Keyring; use itertools::Itertools; use polkadot_primitives::{AssignmentId, AuthorityDiscoveryId, ValidatorId}; use rand::thread_rng; @@ -24,17 +24,7 @@ use rand_distr::{Distribution, Normal, Uniform}; use sc_network::PeerId; use serde::{Deserialize, Serialize}; use sp_consensus_babe::AuthorityId; -use std::{collections::HashMap, path::Path}; - -pub fn random_pov_size(min_pov_size: usize, max_pov_size: usize) -> usize { - random_uniform_sample(min_pov_size, max_pov_size) -} - -fn random_uniform_sample + From>(min_value: T, max_value: T) -> T { - Uniform::from(min_value.into()..=max_value.into()) - .sample(&mut thread_rng()) - .into() -} +use std::collections::HashMap; /// Peer networking latency configuration. #[derive(Clone, Debug, Default, Serialize, Deserialize)] @@ -45,19 +35,34 @@ pub struct PeerLatency { pub std_dev: f64, } +// Based on Kusama `max_validators` +fn default_n_validators() -> usize { + 300 +} + +// Based on Kusama cores +fn default_n_cores() -> usize { + 60 +} + // Default PoV size in KiB. fn default_pov_size() -> usize { - 5120 + 5 * 1024 } -// Default bandwidth in bytes +// Default bandwidth in bytes, based stats from Kusama validators fn default_bandwidth() -> usize { - 52428800 + 42 * 1024 * 1024 +} + +// Default peer latency +fn default_peer_latency() -> Option { + Some(PeerLatency { mean_latency_ms: 30, std_dev: 2.0 }) } // Default connectivity percentage fn default_connectivity() -> usize { - 100 + 90 } // Default backing group size @@ -73,6 +78,7 @@ fn default_needed_approvals() -> usize { fn default_zeroth_delay_tranche_width() -> usize { 0 } + fn default_relay_vrf_modulo_samples() -> usize { 6 } @@ -87,11 +93,11 @@ fn default_no_show_slots() -> usize { /// The test input parameters #[derive(Clone, Debug, Serialize, Deserialize)] pub struct TestConfiguration { - /// The test objective - pub objective: TestObjective, /// Number of validators + #[serde(default = "default_n_validators")] pub n_validators: usize, /// Number of cores + #[serde(default = "default_n_cores")] pub n_cores: usize, /// The number of needed votes to approve a candidate. #[serde(default = "default_needed_approvals")] @@ -115,74 +121,50 @@ pub struct TestConfiguration { pub max_pov_size: usize, /// Randomly sampled pov_sizes #[serde(skip)] - pov_sizes: Vec, - /// The amount of bandiwdth remote validators have. + pub pov_sizes: Vec, + /// The amount of bandwidth remote validators have. #[serde(default = "default_bandwidth")] pub peer_bandwidth: usize, - /// The amount of bandiwdth our node has. + /// The amount of bandwidth our node has. #[serde(default = "default_bandwidth")] pub bandwidth: usize, /// Optional peer emulation latency (round trip time) wrt node under test - #[serde(default)] + #[serde(default = "default_peer_latency")] pub latency: Option, - /// Connectivity ratio, the percentage of peers we are not connected to, but ar part of - /// the topology. + /// Connectivity ratio, the percentage of peers we are connected to, but as part of the + /// topology. #[serde(default = "default_connectivity")] pub connectivity: usize, /// Number of blocks to run the test for pub num_blocks: usize, } -fn generate_pov_sizes(count: usize, min_kib: usize, max_kib: usize) -> Vec { - (0..count).map(|_| random_pov_size(min_kib * 1024, max_kib * 1024)).collect() -} - -#[derive(Serialize, Deserialize)] -pub struct TestSequence { - #[serde(rename(serialize = "TestConfiguration", deserialize = "TestConfiguration"))] - test_configurations: Vec, -} - -impl TestSequence { - pub fn into_vec(self) -> Vec { - self.test_configurations - .into_iter() - .map(|mut config| { - config.pov_sizes = - generate_pov_sizes(config.n_cores, config.min_pov_size, config.max_pov_size); - config - }) - .collect() - } -} - -impl TestSequence { - pub fn new_from_file(path: &Path) -> std::io::Result { - let string = String::from_utf8(std::fs::read(path)?).expect("File is valid UTF8"); - Ok(serde_yaml::from_str(&string).expect("File is valid test sequence YA")) +impl Default for TestConfiguration { + fn default() -> Self { + Self { + n_validators: default_n_validators(), + n_cores: default_n_cores(), + needed_approvals: default_needed_approvals(), + zeroth_delay_tranche_width: default_zeroth_delay_tranche_width(), + relay_vrf_modulo_samples: default_relay_vrf_modulo_samples(), + n_delay_tranches: default_n_delay_tranches(), + no_show_slots: default_no_show_slots(), + max_validators_per_core: default_backing_group_size(), + min_pov_size: default_pov_size(), + max_pov_size: default_pov_size(), + pov_sizes: Default::default(), + peer_bandwidth: default_bandwidth(), + bandwidth: default_bandwidth(), + latency: default_peer_latency(), + connectivity: default_connectivity(), + num_blocks: Default::default(), + } } } -/// Helper struct for authority related state. -#[derive(Clone)] -pub struct TestAuthorities { - pub keyring: Keyring, - pub validator_public: Vec, - pub validator_authority_id: Vec, - pub validator_babe_id: Vec, - pub validator_assignment_id: Vec, - pub key_seeds: Vec, - pub peer_ids: Vec, - pub peer_id_to_authority: HashMap, -} - impl TestConfiguration { - #[allow(unused)] - pub fn write_to_disk(&self) { - // Serialize a slice of configurations - let yaml = serde_yaml::to_string(&TestSequence { test_configurations: vec![self.clone()] }) - .unwrap(); - std::fs::write("last_test.yaml", yaml).unwrap(); + pub fn generate_pov_sizes(&mut self) { + self.pov_sizes = generate_pov_sizes(self.n_cores, self.min_pov_size, self.max_pov_size); } pub fn pov_sizes(&self) -> &[usize] { @@ -223,7 +205,7 @@ impl TestConfiguration { let peer_id_to_authority = peer_ids .iter() .zip(validator_authority_id.iter()) - .map(|(peer_id, authorithy_id)| (*peer_id, authorithy_id.clone())) + .map(|(peer_id, authority_id)| (*peer_id, authority_id.clone())) .collect(); TestAuthorities { @@ -239,6 +221,33 @@ impl TestConfiguration { } } +fn random_uniform_sample + From>(min_value: T, max_value: T) -> T { + Uniform::from(min_value.into()..=max_value.into()) + .sample(&mut thread_rng()) + .into() +} + +fn random_pov_size(min_pov_size: usize, max_pov_size: usize) -> usize { + random_uniform_sample(min_pov_size, max_pov_size) +} + +fn generate_pov_sizes(count: usize, min_kib: usize, max_kib: usize) -> Vec { + (0..count).map(|_| random_pov_size(min_kib * 1024, max_kib * 1024)).collect() +} + +/// Helper struct for authority related state. +#[derive(Clone)] +pub struct TestAuthorities { + pub keyring: Keyring, + pub validator_public: Vec, + pub validator_authority_id: Vec, + pub validator_babe_id: Vec, + pub validator_assignment_id: Vec, + pub key_seeds: Vec, + pub peer_ids: Vec, + pub peer_id_to_authority: HashMap, +} + /// Sample latency (in milliseconds) from a normal distribution with parameters /// specified in `maybe_peer_latency`. pub fn random_latency(maybe_peer_latency: Option<&PeerLatency>) -> usize { diff --git a/polkadot/node/subsystem-bench/src/core/display.rs b/polkadot/node/subsystem-bench/src/lib/display.rs similarity index 89% rename from polkadot/node/subsystem-bench/src/core/display.rs rename to polkadot/node/subsystem-bench/src/lib/display.rs index 13a349382e2fd776f41685fb133b96f1058e7b43..b153d54a7c36f43bc110dde9443f2413a34d9af6 100644 --- a/polkadot/node/subsystem-bench/src/core/display.rs +++ b/polkadot/node/subsystem-bench/src/lib/display.rs @@ -19,7 +19,7 @@ //! //! Currently histogram buckets are skipped. -use crate::{TestConfiguration, LOG_TARGET}; +use crate::configuration::TestConfiguration; use colored::Colorize; use prometheus::{ proto::{MetricFamily, MetricType}, @@ -27,6 +27,8 @@ use prometheus::{ }; use std::fmt::Display; +const LOG_TARGET: &str = "subsystem-bench::display"; + #[derive(Default, Debug)] pub struct MetricCollection(Vec); @@ -85,6 +87,7 @@ impl Display for MetricCollection { Ok(()) } } + #[derive(Debug, Clone)] pub struct TestMetric { name: String, @@ -184,15 +187,16 @@ pub fn parse_metrics(registry: &Registry) -> MetricCollection { test_metrics.into() } -pub fn display_configuration(test_config: &TestConfiguration) { - gum::info!( - "[{}] {}, {}, {}, {}, {}", - format!("objective = {:?}", test_config.objective).green(), - format!("n_validators = {}", test_config.n_validators).blue(), - format!("n_cores = {}", test_config.n_cores).blue(), - format!("pov_size = {} - {}", test_config.min_pov_size, test_config.max_pov_size) - .bright_black(), - format!("connectivity = {}", test_config.connectivity).bright_black(), - format!("latency = {:?}", test_config.latency).bright_black(), - ); +impl Display for TestConfiguration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}, {}, {}, {}, {}", + format!("n_validators = {}", self.n_validators).blue(), + format!("n_cores = {}", self.n_cores).blue(), + format!("pov_size = {} - {}", self.min_pov_size, self.max_pov_size).bright_black(), + format!("connectivity = {}", self.connectivity).bright_black(), + format!("latency = {:?}", self.latency).bright_black(), + ) + } } diff --git a/polkadot/node/subsystem-bench/src/core/environment.rs b/polkadot/node/subsystem-bench/src/lib/environment.rs similarity index 87% rename from polkadot/node/subsystem-bench/src/core/environment.rs rename to polkadot/node/subsystem-bench/src/lib/environment.rs index ca4c41cf45f99851023e1c6899b3d1f5e7913eb8..42955d0302232f35e387a044e34a0c7d665512e8 100644 --- a/polkadot/node/subsystem-bench/src/core/environment.rs +++ b/polkadot/node/subsystem-bench/src/lib/environment.rs @@ -17,13 +17,11 @@ //! Test environment implementation use crate::{ - core::{ - configuration::TestAuthorities, mock::AlwaysSupportsParachains, - network::NetworkEmulatorHandle, - }, - TestConfiguration, + configuration::{TestAuthorities, TestConfiguration}, + mock::AlwaysSupportsParachains, + network::NetworkEmulatorHandle, + usage::{BenchmarkUsage, ResourceUsage}, }; -use colored::Colorize; use core::time::Duration; use futures::{Future, FutureExt}; use polkadot_node_subsystem::{messages::AllMessages, Overseer, SpawnGlue, TimeoutExt}; @@ -33,7 +31,6 @@ use polkadot_node_subsystem_util::metrics::prometheus::{ }; use polkadot_overseer::{BlockInfo, Handle as OverseerHandle}; use sc_service::{SpawnTaskHandle, TaskManager}; -use serde::{Deserialize, Serialize}; use std::net::{Ipv4Addr, SocketAddr}; use tokio::runtime::Handle; @@ -121,6 +118,7 @@ fn new_runtime() -> tokio::runtime::Runtime { .thread_name("subsystem-bench") .enable_all() .thread_stack_size(3 * 1024 * 1024) + .worker_threads(4) .build() .unwrap() } @@ -204,6 +202,7 @@ impl TestEnvironment { overseer: Overseer, AlwaysSupportsParachains>, overseer_handle: OverseerHandle, authorities: TestAuthorities, + with_prometheus_endpoint: bool, ) -> Self { let metrics = TestEnvironmentMetrics::new(&dependencies.registry) .expect("Metrics need to be registered"); @@ -211,19 +210,21 @@ impl TestEnvironment { let spawn_handle = dependencies.task_manager.spawn_handle(); spawn_handle.spawn_blocking("overseer", "overseer", overseer.run().boxed()); - let registry_clone = dependencies.registry.clone(); - dependencies.task_manager.spawn_handle().spawn_blocking( - "prometheus", - "test-environment", - async move { - prometheus_endpoint::init_prometheus( - SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::LOCALHOST), 9999), - registry_clone, - ) - .await - .unwrap(); - }, - ); + if with_prometheus_endpoint { + let registry_clone = dependencies.registry.clone(); + dependencies.task_manager.spawn_handle().spawn_blocking( + "prometheus", + "test-environment", + async move { + prometheus_endpoint::init_prometheus( + SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::LOCALHOST), 9999), + registry_clone, + ) + .await + .unwrap(); + }, + ); + } TestEnvironment { runtime_handle: dependencies.runtime.handle().clone(), @@ -403,7 +404,7 @@ impl TestEnvironment { let total_cpu = test_env_cpu_metrics.sum_by("substrate_tasks_polling_duration_sum"); usage.push(ResourceUsage { - resource_name: "Test environment".to_string(), + resource_name: "test-environment".to_string(), total: total_cpu, per_block: total_cpu / num_blocks, }); @@ -411,41 +412,3 @@ impl TestEnvironment { usage } } - -#[derive(Debug, Serialize, Deserialize)] -pub struct BenchmarkUsage { - benchmark_name: String, - network_usage: Vec, - cpu_usage: Vec, -} - -impl std::fmt::Display for BenchmarkUsage { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!( - f, - "\n{}\n\n{}\n{}\n\n{}\n{}\n", - self.benchmark_name.purple(), - format!("{:<32}{:>12}{:>12}", "Network usage, KiB", "total", "per block").blue(), - self.network_usage - .iter() - .map(|v| v.to_string()) - .collect::>() - .join("\n"), - format!("{:<32}{:>12}{:>12}", "CPU usage in seconds", "total", "per block").blue(), - self.cpu_usage.iter().map(|v| v.to_string()).collect::>().join("\n") - ) - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct ResourceUsage { - resource_name: String, - total: f64, - per_block: f64, -} - -impl std::fmt::Display for ResourceUsage { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{:<32}{:>12.3}{:>12.3}", self.resource_name.cyan(), self.total, self.per_block) - } -} diff --git a/polkadot/node/subsystem-bench/src/core/keyring.rs b/polkadot/node/subsystem-bench/src/lib/keyring.rs similarity index 100% rename from polkadot/node/subsystem-bench/src/core/keyring.rs rename to polkadot/node/subsystem-bench/src/lib/keyring.rs diff --git a/polkadot/node/subsystem-bench/src/core/mod.rs b/polkadot/node/subsystem-bench/src/lib/lib.rs similarity index 87% rename from polkadot/node/subsystem-bench/src/core/mod.rs rename to polkadot/node/subsystem-bench/src/lib/lib.rs index 764184c5b37779efb7dc22dead338cc05ee9b942..ef2724abc98920c79d8dd9d94f97bed32b0ab8e2 100644 --- a/polkadot/node/subsystem-bench/src/core/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/lib.rs @@ -15,11 +15,15 @@ // along with Polkadot. If not, see . // The validator index that represent the node that is under test. -pub(crate) const NODE_UNDER_TEST: u32 = 0; +pub const NODE_UNDER_TEST: u32 = 0; -pub(crate) mod configuration; +pub mod approval; +pub mod availability; +pub mod configuration; pub(crate) mod display; pub(crate) mod environment; pub(crate) mod keyring; pub(crate) mod mock; pub(crate) mod network; +pub mod usage; +pub mod utils; diff --git a/polkadot/node/subsystem-bench/src/core/mock/av_store.rs b/polkadot/node/subsystem-bench/src/lib/mock/av_store.rs similarity index 97% rename from polkadot/node/subsystem-bench/src/core/mock/av_store.rs rename to polkadot/node/subsystem-bench/src/lib/mock/av_store.rs index 0a7725c91e0421e79a20b2ec77d8ade72ab05714..fba33523be85dbfd241bc1fcf42d8f8069ebf95c 100644 --- a/polkadot/node/subsystem-bench/src/core/mock/av_store.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/av_store.rs @@ -16,7 +16,7 @@ //! A generic av store subsystem mockup suitable to be used in benchmarks. -use crate::core::network::{HandleNetworkMessage, NetworkMessage}; +use crate::network::{HandleNetworkMessage, NetworkMessage}; use futures::{channel::oneshot, FutureExt}; use parity_scale_codec::Encode; use polkadot_node_network_protocol::request_response::{ @@ -39,8 +39,9 @@ pub struct AvailabilityStoreState { const LOG_TARGET: &str = "subsystem-bench::av-store-mock"; -/// Mockup helper. Contains Ccunks and full availability data of all parachain blocks +/// Mockup helper. Contains Chunks and full availability data of all parachain blocks /// used in a test. +#[derive(Clone)] pub struct NetworkAvailabilityState { pub candidate_hashes: HashMap, pub available_data: Vec, @@ -97,7 +98,7 @@ impl HandleNetworkMessage for NetworkAvailabilityState { outgoing_request .pending_response .send(response) - .expect("Response is always sent succesfully"); + .expect("Response is always sent successfully"); None }, _ => Some(NetworkMessage::RequestFromNode(peer, request)), diff --git a/polkadot/node/subsystem-bench/src/core/mock/chain_api.rs b/polkadot/node/subsystem-bench/src/lib/mock/chain_api.rs similarity index 98% rename from polkadot/node/subsystem-bench/src/core/mock/chain_api.rs rename to polkadot/node/subsystem-bench/src/lib/mock/chain_api.rs index bee15c3cefdfbda061a1313db6c631e52fd91c12..86b030fb6fdcf7248b57f7fd47cdee6085bf8377 100644 --- a/polkadot/node/subsystem-bench/src/core/mock/chain_api.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/chain_api.rs @@ -89,7 +89,7 @@ impl MockChainApi { let hash = self .state .get_header_by_number(requested_number) - .expect("Unknow block number") + .expect("Unknown block number") .hash(); sender.send(Ok(Some(hash))).unwrap(); }, diff --git a/polkadot/node/subsystem-bench/src/core/mock/dummy.rs b/polkadot/node/subsystem-bench/src/lib/mock/dummy.rs similarity index 100% rename from polkadot/node/subsystem-bench/src/core/mock/dummy.rs rename to polkadot/node/subsystem-bench/src/lib/mock/dummy.rs diff --git a/polkadot/node/subsystem-bench/src/core/mock/mod.rs b/polkadot/node/subsystem-bench/src/lib/mock/mod.rs similarity index 97% rename from polkadot/node/subsystem-bench/src/core/mock/mod.rs rename to polkadot/node/subsystem-bench/src/lib/mock/mod.rs index 46fdeb196c011587dd081851e9e1f31863a0530e..6dda9a47d398f3e6e952cd054bce9769e8942e70 100644 --- a/polkadot/node/subsystem-bench/src/core/mock/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/mod.rs @@ -34,9 +34,10 @@ impl HeadSupportsParachains for AlwaysSupportsParachains { } // An orchestra with dummy subsystems +#[macro_export] macro_rules! dummy_builder { ($spawn_task_handle: ident, $metrics: ident) => {{ - use $crate::core::mock::dummy::*; + use $crate::mock::dummy::*; // Initialize a mock overseer. // All subsystem except approval_voting and approval_distribution are mock subsystems. @@ -72,7 +73,6 @@ macro_rules! dummy_builder { .spawner(SpawnGlue($spawn_task_handle)) }}; } -pub(crate) use dummy_builder; #[derive(Clone)] pub struct TestSyncOracle {} diff --git a/polkadot/node/subsystem-bench/src/core/mock/network_bridge.rs b/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs similarity index 96% rename from polkadot/node/subsystem-bench/src/core/mock/network_bridge.rs rename to polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs index 4682c7ec79ae3532858365b135636afefcb5400f..ec66ad4e279c217f7510d4c09def128f41fbffe5 100644 --- a/polkadot/node/subsystem-bench/src/core/mock/network_bridge.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs @@ -17,7 +17,7 @@ //! Mocked `network-bridge` subsystems that uses a `NetworkInterface` to access //! the emulated network. -use crate::core::{ +use crate::{ configuration::TestAuthorities, network::{NetworkEmulatorHandle, NetworkInterfaceReceiver, NetworkMessage, RequestExt}, }; @@ -42,8 +42,8 @@ pub struct MockNetworkBridgeTx { network: NetworkEmulatorHandle, /// A channel to the network interface, to_network_interface: UnboundedSender, - /// Test authorithies - test_authorithies: TestAuthorities, + /// Test authorities + test_authorities: TestAuthorities, } /// A mock of the network bridge tx subsystem. @@ -58,9 +58,9 @@ impl MockNetworkBridgeTx { pub fn new( network: NetworkEmulatorHandle, to_network_interface: UnboundedSender, - test_authorithies: TestAuthorities, + test_authorities: TestAuthorities, ) -> MockNetworkBridgeTx { - Self { network, to_network_interface, test_authorithies } + Self { network, to_network_interface, test_authorities } } } @@ -125,13 +125,13 @@ impl MockNetworkBridgeTx { } }, NetworkBridgeTxMessage::ReportPeer(_) => { - // ingore rep changes + // ignore rep changes }, NetworkBridgeTxMessage::SendValidationMessage(peers, message) => { for peer in peers { self.to_network_interface .unbounded_send(NetworkMessage::MessageFromNode( - self.test_authorithies + self.test_authorities .peer_id_to_authority .get(&peer) .unwrap() diff --git a/polkadot/node/subsystem-bench/src/core/mock/runtime_api.rs b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs similarity index 98% rename from polkadot/node/subsystem-bench/src/core/mock/runtime_api.rs rename to polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs index 0dd76efcbaf0de5cdbd456d0bff91658ad0ac737..3c39de870a28411a1f04888ff9ab8d11d85a40e0 100644 --- a/polkadot/node/subsystem-bench/src/core/mock/runtime_api.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs @@ -16,7 +16,7 @@ //! A generic runtime api subsystem mockup suitable to be used in benchmarks. -use crate::core::configuration::{TestAuthorities, TestConfiguration}; +use crate::configuration::{TestAuthorities, TestConfiguration}; use bitvec::prelude::BitVec; use futures::FutureExt; use itertools::Itertools; @@ -36,6 +36,7 @@ use std::collections::HashMap; const LOG_TARGET: &str = "subsystem-bench::runtime-api-mock"; /// Minimal state to answer requests. +#[derive(Clone)] pub struct RuntimeApiState { // All authorities in the test, authorities: TestAuthorities, @@ -49,6 +50,7 @@ pub struct RuntimeApiState { } /// A mocked `runtime-api` subsystem. +#[derive(Clone)] pub struct MockRuntimeApi { state: RuntimeApiState, config: TestConfiguration, diff --git a/polkadot/node/subsystem-bench/src/core/network.rs b/polkadot/node/subsystem-bench/src/lib/network.rs similarity index 98% rename from polkadot/node/subsystem-bench/src/core/network.rs rename to polkadot/node/subsystem-bench/src/lib/network.rs index e9124726d7c063f15eaf7b048b291b1b2115e897..0f7b7d741e778e4535ccad1fe032f39d290e3ed2 100644 --- a/polkadot/node/subsystem-bench/src/core/network.rs +++ b/polkadot/node/subsystem-bench/src/lib/network.rs @@ -33,7 +33,7 @@ // | // Subsystems under test -use crate::core::{ +use crate::{ configuration::{random_latency, TestAuthorities, TestConfiguration}, environment::TestEnvironmentDependencies, NODE_UNDER_TEST, @@ -154,7 +154,7 @@ pub enum NetworkMessage { MessageFromNode(AuthorityDiscoveryId, VersionedValidationProtocol), /// A request originating from our node RequestFromNode(AuthorityDiscoveryId, Requests), - /// A request originating from an emultated peer + /// A request originating from an emulated peer RequestFromPeer(IncomingRequest), } @@ -219,7 +219,7 @@ impl Future for ProxiedRequest { Poll::Ready(response) => Poll::Ready(ProxiedResponse { sender: self.sender.take().expect("sender already used"), result: response - .expect("Response is always succesfully received.") + .expect("Response is always successfully received.") .result .map_err(|_| RequestFailure::Refused), }), @@ -761,7 +761,7 @@ pub fn new_network( gum::info!(target: LOG_TARGET, "{}",format!("connectivity {}%, latency {:?}", config.connectivity, config.latency).bright_black()); let metrics = - Metrics::new(&dependencies.registry).expect("Metrics always register succesfully"); + Metrics::new(&dependencies.registry).expect("Metrics always register successfully"); let mut validator_authority_id_mapping = HashMap::new(); // Create the channel from `peer` to `NetworkInterface` . @@ -790,9 +790,9 @@ pub fn new_network( let connected_count = config.connected_count(); - let mut peers_indicies = (0..n_peers).collect_vec(); + let mut peers_indices = (0..n_peers).collect_vec(); let (_connected, to_disconnect) = - peers_indicies.partial_shuffle(&mut thread_rng(), connected_count); + peers_indices.partial_shuffle(&mut thread_rng(), connected_count); // Node under test is always mark as disconnected. peers[NODE_UNDER_TEST as usize].disconnect(); @@ -958,7 +958,7 @@ impl Metrics { .inc_by(bytes as u64); } - /// Increment total receioved for a peer. + /// Increment total received for a peer. pub fn on_peer_received(&self, peer_index: usize, bytes: usize) { self.peer_total_received .with_label_values(vec![format!("node{}", peer_index).as_str()].as_slice()) @@ -1041,7 +1041,7 @@ mod tests { async fn test_expected_rate() { let tick_rate = 200; let budget = 1_000_000; - // rate must not exceeed 100 credits per second + // rate must not exceed 100 credits per second let mut rate_limiter = RateLimit::new(tick_rate, budget); let mut total_sent = 0usize; let start = Instant::now(); diff --git a/polkadot/node/subsystem-bench/src/lib/usage.rs b/polkadot/node/subsystem-bench/src/lib/usage.rs new file mode 100644 index 0000000000000000000000000000000000000000..59296746ec3d4154274ce68d9ee910bb61d0f9f8 --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/usage.rs @@ -0,0 +1,181 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Test usage implementation + +use colored::Colorize; +use itertools::Itertools; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct BenchmarkUsage { + pub benchmark_name: String, + pub network_usage: Vec, + pub cpu_usage: Vec, +} + +impl std::fmt::Display for BenchmarkUsage { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + f, + "\n{}\n\n{}\n{}\n\n{}\n{}\n", + self.benchmark_name.purple(), + format!("{:<32}{:>12}{:>12}", "Network usage, KiB", "total", "per block").blue(), + self.network_usage + .iter() + .map(|v| v.to_string()) + .sorted() + .collect::>() + .join("\n"), + format!("{:<32}{:>12}{:>12}", "CPU usage, seconds", "total", "per block").blue(), + self.cpu_usage + .iter() + .map(|v| v.to_string()) + .sorted() + .collect::>() + .join("\n") + ) + } +} + +impl BenchmarkUsage { + pub fn average(usages: &[Self]) -> Self { + let all_network_usages: Vec<&ResourceUsage> = + usages.iter().flat_map(|v| &v.network_usage).collect(); + let all_cpu_usage: Vec<&ResourceUsage> = usages.iter().flat_map(|v| &v.cpu_usage).collect(); + + Self { + benchmark_name: usages.first().map(|v| v.benchmark_name.clone()).unwrap_or_default(), + network_usage: ResourceUsage::average_by_resource_name(&all_network_usages), + cpu_usage: ResourceUsage::average_by_resource_name(&all_cpu_usage), + } + } + + pub fn check_network_usage(&self, checks: &[ResourceUsageCheck]) -> Vec { + check_usage(&self.benchmark_name, &self.network_usage, checks) + } + + pub fn check_cpu_usage(&self, checks: &[ResourceUsageCheck]) -> Vec { + check_usage(&self.benchmark_name, &self.cpu_usage, checks) + } + + pub fn cpu_usage_diff(&self, other: &Self, resource_name: &str) -> Option { + let self_res = self.cpu_usage.iter().find(|v| v.resource_name == resource_name); + let other_res = other.cpu_usage.iter().find(|v| v.resource_name == resource_name); + + match (self_res, other_res) { + (Some(self_res), Some(other_res)) => Some(self_res.diff(other_res)), + _ => None, + } + } + + // Prepares a json string for a graph representation + // See: https://github.com/benchmark-action/github-action-benchmark?tab=readme-ov-file#examples + pub fn to_chart_json(&self) -> color_eyre::eyre::Result { + let chart = self + .network_usage + .iter() + .map(|v| ChartItem { + name: v.resource_name.clone(), + unit: "KiB".to_string(), + value: v.per_block, + }) + .chain(self.cpu_usage.iter().map(|v| ChartItem { + name: v.resource_name.clone(), + unit: "seconds".to_string(), + value: v.per_block, + })) + .collect::>(); + + Ok(serde_json::to_string(&chart)?) + } +} + +fn check_usage( + benchmark_name: &str, + usage: &[ResourceUsage], + checks: &[ResourceUsageCheck], +) -> Vec { + checks + .iter() + .filter_map(|check| { + check_resource_usage(usage, check) + .map(|message| format!("{}: {}", benchmark_name, message)) + }) + .collect() +} + +fn check_resource_usage( + usage: &[ResourceUsage], + (resource_name, base, precision): &ResourceUsageCheck, +) -> Option { + if let Some(usage) = usage.iter().find(|v| v.resource_name == *resource_name) { + let diff = (base - usage.per_block).abs() / base; + if diff < *precision { + None + } else { + Some(format!( + "The resource `{}` is expected to be equal to {} with a precision {}, but the current value is {} ({})", + resource_name, base, precision, usage.per_block, diff + )) + } + } else { + Some(format!("The resource `{}` is not found", resource_name)) + } +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct ResourceUsage { + pub resource_name: String, + pub total: f64, + pub per_block: f64, +} + +impl std::fmt::Display for ResourceUsage { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:<32}{:>12.4}{:>12.4}", self.resource_name.cyan(), self.total, self.per_block) + } +} + +impl ResourceUsage { + fn average_by_resource_name(usages: &[&Self]) -> Vec { + let mut by_name: HashMap> = Default::default(); + for usage in usages { + by_name.entry(usage.resource_name.clone()).or_default().push(usage); + } + let mut average = vec![]; + for (resource_name, values) in by_name { + let total = values.iter().map(|v| v.total).sum::() / values.len() as f64; + let per_block = values.iter().map(|v| v.per_block).sum::() / values.len() as f64; + average.push(Self { resource_name, total, per_block }); + } + average + } + + fn diff(&self, other: &Self) -> f64 { + (self.per_block - other.per_block).abs() / self.per_block + } +} + +type ResourceUsageCheck<'a> = (&'a str, f64, f64); + +#[derive(Debug, Serialize)] +pub struct ChartItem { + pub name: String, + pub unit: String, + pub value: f64, +} diff --git a/polkadot/node/subsystem-bench/src/availability/cli.rs b/polkadot/node/subsystem-bench/src/lib/utils.rs similarity index 52% rename from polkadot/node/subsystem-bench/src/availability/cli.rs rename to polkadot/node/subsystem-bench/src/lib/utils.rs index 65df8c1552aa8266497eb2738cc562f656050b68..b3cd3a88b6c1324ec18a402cbc92524f4865380f 100644 --- a/polkadot/node/subsystem-bench/src/availability/cli.rs +++ b/polkadot/node/subsystem-bench/src/lib/utils.rs @@ -14,24 +14,28 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use serde::{Deserialize, Serialize}; +//! Test utils -#[derive(clap::ValueEnum, Clone, Copy, Debug, PartialEq)] -#[value(rename_all = "kebab-case")] -#[non_exhaustive] -pub enum NetworkEmulation { - Ideal, - Healthy, - Degraded, -} +use std::{fs::File, io::Write}; + +// Saves a given string to a file +pub fn save_to_file(path: &str, value: String) -> color_eyre::eyre::Result<()> { + let output = std::process::Command::new(env!("CARGO")) + .arg("locate-project") + .arg("--workspace") + .arg("--message-format=plain") + .output() + .unwrap() + .stdout; + let workspace_dir = std::path::Path::new(std::str::from_utf8(&output).unwrap().trim()) + .parent() + .unwrap(); + let path = workspace_dir.join(path); + if let Some(dir) = path.parent() { + std::fs::create_dir_all(dir)?; + } + let mut file = File::create(path)?; + file.write_all(value.as_bytes())?; -#[derive(Debug, Clone, Serialize, Deserialize, clap::Parser)] -#[clap(rename_all = "kebab-case")] -#[allow(missing_docs)] -pub struct DataAvailabilityReadOptions { - #[clap(short, long, default_value_t = false)] - /// Turbo boost AD Read by fetching the full availability datafrom backers first. Saves CPU as - /// we don't need to re-construct from chunks. Tipically this is only faster if nodes have - /// enough bandwidth. - pub fetch_from_backers: bool, + Ok(()) } diff --git a/polkadot/node/subsystem-types/src/messages.rs b/polkadot/node/subsystem-types/src/messages.rs index 549e43a671d6b4f9bd59659647cdaff8a69125d4..5d05d2b56ed0895562c22a58ba3215874518a911 100644 --- a/polkadot/node/subsystem-types/src/messages.rs +++ b/polkadot/node/subsystem-types/src/messages.rs @@ -45,16 +45,17 @@ use polkadot_primitives::{ async_backing, slashing, vstaging::{ApprovalVotingParams, NodeFeatures}, AuthorityDiscoveryId, BackedCandidate, BlockNumber, CandidateEvent, CandidateHash, - CandidateIndex, CandidateReceipt, CollatorId, CommittedCandidateReceipt, CoreState, - DisputeState, ExecutorParams, GroupIndex, GroupRotationInfo, Hash, Header as BlockHeader, - Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, MultiDisputeStatementSet, - OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, PvfExecKind, SessionIndex, - SessionInfo, SignedAvailabilityBitfield, SignedAvailabilityBitfields, ValidationCode, - ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, + CandidateIndex, CandidateReceipt, CollatorId, CommittedCandidateReceipt, CoreIndex, CoreState, + DisputeState, ExecutorParams, GroupIndex, GroupRotationInfo, Hash, HeadData, + Header as BlockHeader, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, + MultiDisputeStatementSet, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, + PvfExecKind, SessionIndex, SessionInfo, SignedAvailabilityBitfield, + SignedAvailabilityBitfields, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, + ValidatorSignature, }; use polkadot_statement_table::v2::Misbehavior; use std::{ - collections::{BTreeMap, HashMap, HashSet}, + collections::{BTreeMap, HashMap, HashSet, VecDeque}, sync::Arc, }; @@ -81,8 +82,15 @@ pub struct CanSecondRequest { pub enum CandidateBackingMessage { /// Requests a set of backable candidates attested by the subsystem. /// - /// Each pair is (candidate_hash, candidate_relay_parent). - GetBackedCandidates(Vec<(CandidateHash, Hash)>, oneshot::Sender>), + /// The order of candidates of the same para must be preserved in the response. + /// If a backed candidate of a para cannot be retrieved, the response should not contain any + /// candidates of the same para that follow it in the input vector. In other words, assuming + /// candidates are supplied in dependency order, we must ensure that this dependency order is + /// preserved. + GetBackedCandidates( + HashMap>, + oneshot::Sender>>, + ), /// Request the subsystem to check whether it's allowed to second given candidate. /// The rule is to only fetch collations that are either built on top of the root /// of some fragment tree or have a parent node which represents backed candidate. @@ -207,16 +215,20 @@ pub enum CollatorProtocolMessage { /// This should be sent before any `DistributeCollation` message. CollateOn(ParaId), /// Provide a collation to distribute to validators with an optional result sender. - /// The second argument is the parent head-data hash. - /// - /// The result sender should be informed when at least one parachain validator seconded the - /// collation. It is also completely okay to just drop the sender. - DistributeCollation( - CandidateReceipt, - Hash, - PoV, - Option>, - ), + DistributeCollation { + /// The receipt of the candidate. + candidate_receipt: CandidateReceipt, + /// The hash of the parent head-data. + /// Here to avoid computing the hash of the parent head data twice. + parent_head_data_hash: Hash, + /// Proof of validity. + pov: PoV, + /// This parent head-data is needed for elastic scaling. + parent_head_data: HeadData, + /// The result sender should be informed when at least one parachain validator seconded the + /// collation. It is also completely okay to just drop the sender. + result_sender: Option>, + }, /// Report a collator as having provided an invalid collation. This should lead to disconnect /// and blacklist of the collator. ReportCollator(CollatorId), @@ -724,6 +736,9 @@ pub enum RuntimeApiRequest { /// Approval voting params /// `V10` ApprovalVotingParams(SessionIndex, RuntimeApiSender), + /// Fetch the `ClaimQueue` from scheduler pallet + /// `V11` + ClaimQueue(RuntimeApiSender>>), } impl RuntimeApiRequest { @@ -758,6 +773,9 @@ impl RuntimeApiRequest { /// `approval_voting_params` pub const APPROVAL_VOTING_PARAMS_REQUIREMENT: u32 = 10; + + /// `ClaimQueue` + pub const CLAIM_QUEUE_RUNTIME_REQUIREMENT: u32 = 11; } /// A message to the Runtime API subsystem. @@ -787,7 +805,7 @@ pub enum StatementDistributionMessage { /// This data becomes intrinsics or extrinsics which should be included in a future relay chain /// block. -// It needs to be cloneable because multiple potential block authors can request copies. +// It needs to be clonable because multiple potential block authors can request copies. #[derive(Debug, Clone)] pub enum ProvisionableData { /// This bitfield indicates the availability of various candidate blocks. @@ -1104,14 +1122,41 @@ pub struct ProspectiveValidationDataRequest { pub para_id: ParaId, /// The relay-parent of the candidate. pub candidate_relay_parent: Hash, - /// The parent head-data hash. - pub parent_head_data_hash: Hash, + /// The parent head-data. + pub parent_head_data: ParentHeadData, +} + +/// The parent head-data hash with optional data itself. +#[derive(Debug, Clone)] +pub enum ParentHeadData { + /// Parent head-data hash. + OnlyHash(Hash), + /// Parent head-data along with its hash. + WithData { + /// This will be provided for collations with elastic scaling enabled. + head_data: HeadData, + /// Parent head-data hash. + hash: Hash, + }, +} + +impl ParentHeadData { + /// Return the hash of the parent head-data. + pub fn hash(&self) -> Hash { + match self { + ParentHeadData::OnlyHash(hash) => *hash, + ParentHeadData::WithData { hash, .. } => *hash, + } + } } /// Indicates the relay-parents whose fragment tree a candidate /// is present in and the depths of that tree the candidate is present in. pub type FragmentTreeMembership = Vec<(Hash, Vec)>; +/// A collection of ancestor candidates of a parachain. +pub type Ancestors = HashSet; + /// Messages sent to the Prospective Parachains subsystem. #[derive(Debug)] pub enum ProspectiveParachainsMessage { @@ -1128,15 +1173,18 @@ pub enum ProspectiveParachainsMessage { /// has been backed. This requires that the candidate was successfully introduced in /// the past. CandidateBacked(ParaId, CandidateHash), - /// Get N backable candidate hashes along with their relay parents for the given parachain, - /// under the given relay-parent hash, which is a descendant of the given candidate hashes. + /// Try getting N backable candidate hashes along with their relay parents for the given + /// parachain, under the given relay-parent hash, which is a descendant of the given ancestors. + /// Timed out ancestors should not be included in the collection. /// N should represent the number of scheduled cores of this ParaId. - /// Returns `None` on the channel if no such candidate exists. + /// A timed out ancestor frees the cores of all of its descendants, so if there's a hole in the + /// supplied ancestor path, we'll get candidates that backfill those timed out slots first. It + /// may also return less/no candidates, if there aren't enough backable candidates recorded. GetBackableCandidates( Hash, ParaId, u32, - Vec, + Ancestors, oneshot::Sender>, ), /// Get the hypothetical frontier membership of candidates with the given properties diff --git a/polkadot/node/subsystem-types/src/runtime_client.rs b/polkadot/node/subsystem-types/src/runtime_client.rs index 7f6183076101b4474e8059435b42a69b108fbb05..7474b4120cc918b8d3a880bbd09ab022b6a4445c 100644 --- a/polkadot/node/subsystem-types/src/runtime_client.rs +++ b/polkadot/node/subsystem-types/src/runtime_client.rs @@ -21,19 +21,23 @@ use polkadot_primitives::{ slashing, vstaging::{self, ApprovalVotingParams}, Block, BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, - Header, Id, InboundDownwardMessage, InboundHrmpMessage, OccupiedCoreAssumption, - PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, - ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, + CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, + GroupRotationInfo, Hash, Header, Id, InboundDownwardMessage, InboundHrmpMessage, + OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, + SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, + ValidatorSignature, }; -use sc_client_api::HeaderBackend; +use sc_client_api::{AuxStore, HeaderBackend}; use sc_transaction_pool_api::OffchainTransactionPoolFactory; use sp_api::{ApiError, ApiExt, ProvideRuntimeApi}; use sp_authority_discovery::AuthorityDiscoveryApi; -use sp_blockchain::Info; +use sp_blockchain::{BlockStatus, Info}; use sp_consensus_babe::{BabeApi, Epoch}; -use sp_runtime::traits::{Header as HeaderT, NumberFor}; -use std::{collections::BTreeMap, sync::Arc}; +use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor}; +use std::{ + collections::{BTreeMap, VecDeque}, + sync::Arc, +}; /// Offers header utilities. /// @@ -329,6 +333,10 @@ pub trait RuntimeApiSubsystemClient { at: Hash, session_index: SessionIndex, ) -> Result; + + // == v11: Claim queue == + /// Fetch the `ClaimQueue` from scheduler pallet + async fn claim_queue(&self, at: Hash) -> Result>, ApiError>; } /// Default implementation of [`RuntimeApiSubsystemClient`] using the client. @@ -594,4 +602,66 @@ where ) -> Result { self.client.runtime_api().approval_voting_params(at) } + + async fn claim_queue(&self, at: Hash) -> Result>, ApiError> { + self.client.runtime_api().claim_queue(at) + } +} + +impl HeaderBackend for DefaultSubsystemClient +where + Client: HeaderBackend, + Block: sp_runtime::traits::Block, +{ + fn header( + &self, + hash: Block::Hash, + ) -> sc_client_api::blockchain::Result> { + self.client.header(hash) + } + + fn info(&self) -> Info { + self.client.info() + } + + fn status(&self, hash: Block::Hash) -> sc_client_api::blockchain::Result { + self.client.status(hash) + } + + fn number( + &self, + hash: Block::Hash, + ) -> sc_client_api::blockchain::Result::Header as HeaderT>::Number>> { + self.client.number(hash) + } + + fn hash( + &self, + number: NumberFor, + ) -> sc_client_api::blockchain::Result> { + self.client.hash(number) + } +} + +impl AuxStore for DefaultSubsystemClient +where + Client: AuxStore, +{ + fn insert_aux< + 'a, + 'b: 'a, + 'c: 'a, + I: IntoIterator, + D: IntoIterator, + >( + &self, + insert: I, + delete: D, + ) -> sp_blockchain::Result<()> { + self.client.insert_aux(insert, delete) + } + + fn get_aux(&self, key: &[u8]) -> sp_blockchain::Result>> { + self.client.get_aux(key) + } } diff --git a/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs b/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs index c7b91bffb3d705b27ee4c611985328dd1b1b7e77..d38d838fedefac643253528ab99d0035eeae55da 100644 --- a/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs +++ b/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs @@ -78,7 +78,7 @@ /// /// 2. The root fragment is invalid under the new constraints because it has been subsumed by /// the relay-chain. In this case, we can discard the root and split & re-root the fragment -/// tree under its descendents and compare to the new constraints again. This is the +/// tree under its descendants and compare to the new constraints again. This is the /// "prediction came true" case. /// /// 3. The root fragment is invalid under the new constraints because a competing parachain diff --git a/polkadot/node/subsystem-util/src/lib.rs b/polkadot/node/subsystem-util/src/lib.rs index f13beb3502fc22dcb768fb5548965eb7727f46fd..6ff09ed5f220086cdadaf83092b88b90f1fca388 100644 --- a/polkadot/node/subsystem-util/src/lib.rs +++ b/polkadot/node/subsystem-util/src/lib.rs @@ -30,7 +30,7 @@ use polkadot_node_subsystem::{ messages::{RuntimeApiMessage, RuntimeApiRequest, RuntimeApiSender}, overseer, SubsystemSender, }; -use polkadot_primitives::{slashing, ExecutorParams}; +use polkadot_primitives::{slashing, CoreIndex, ExecutorParams}; pub use overseer::{ gen::{OrchestraError as OverseerError, Timeout}, @@ -53,7 +53,10 @@ pub use rand; use sp_application_crypto::AppCrypto; use sp_core::ByteArray; use sp_keystore::{Error as KeystoreError, KeystorePtr}; -use std::time::Duration; +use std::{ + collections::{BTreeMap, VecDeque}, + time::Duration, +}; use thiserror::Error; use vstaging::get_disabled_validators_with_fallback; @@ -304,6 +307,7 @@ specialize_requests! { fn request_submit_report_dispute_lost(dp: slashing::DisputeProof, okop: slashing::OpaqueKeyOwnershipProof) -> Option<()>; SubmitReportDisputeLost; fn request_disabled_validators() -> Vec; DisabledValidators; fn request_async_backing_params() -> AsyncBackingParams; AsyncBackingParams; + fn request_claim_queue() -> BTreeMap>; ClaimQueue; } /// Requests executor parameters from the runtime effective at given relay-parent. First obtains @@ -378,7 +382,7 @@ pub fn signing_key_and_index<'a>( /// Sign the given data with the given validator ID. /// -/// Returns `Ok(None)` if the private key that correponds to that validator ID is not found in the +/// Returns `Ok(None)` if the private key that corresponds to that validator ID is not found in the /// given keystore. Returns an error if the key could not be used for signing. pub fn sign( keystore: &KeystorePtr, diff --git a/polkadot/node/test/service/src/chain_spec.rs b/polkadot/node/test/service/src/chain_spec.rs index 0295090b9521551533d012addeda064595dbeac3..f14fa9fde58b4426690cd1bd0e16129f095c34ec 100644 --- a/polkadot/node/test/service/src/chain_spec.rs +++ b/polkadot/node/test/service/src/chain_spec.rs @@ -19,7 +19,9 @@ use babe_primitives::AuthorityId as BabeId; use grandpa::AuthorityId as GrandpaId; use pallet_staking::Forcing; -use polkadot_primitives::{AccountId, AssignmentId, ValidatorId, MAX_CODE_SIZE, MAX_POV_SIZE}; +use polkadot_primitives::{ + vstaging::SchedulerParams, AccountId, AssignmentId, ValidatorId, MAX_CODE_SIZE, MAX_POV_SIZE, +}; use polkadot_service::chain_spec::{get_account_id_from_seed, get_from_seed, Extensions}; use polkadot_test_runtime::BABE_GENESIS_EPOCH_CONFIG; use sc_chain_spec::{ChainSpec, ChainType}; @@ -165,10 +167,14 @@ fn polkadot_testnet_genesis( max_code_size: MAX_CODE_SIZE, max_pov_size: MAX_POV_SIZE, max_head_data_size: 32 * 1024, - group_rotation_frequency: 20, - paras_availability_period: 4, no_show_slots: 10, minimum_validation_upgrade_delay: 5, + max_downward_message_size: 1024, + scheduler_params: SchedulerParams { + group_rotation_frequency: 20, + paras_availability_period: 4, + ..Default::default() + }, ..Default::default() }, } diff --git a/polkadot/node/test/service/src/lib.rs b/polkadot/node/test/service/src/lib.rs index ca904bae28db2dcc2f0a2e50e1cf92528a135003..eed11e62c21e4b811de7f286c2757a35ed5f19cf 100644 --- a/polkadot/node/test/service/src/lib.rs +++ b/polkadot/node/test/service/src/lib.rs @@ -410,7 +410,7 @@ pub fn construct_extrinsic( UncheckedExtrinsic::new_signed( function.clone(), polkadot_test_runtime::Address::Id(caller.public().into()), - polkadot_primitives::Signature::Sr25519(signature.clone()), + polkadot_primitives::Signature::Sr25519(signature), extra.clone(), ) } diff --git a/polkadot/parachain/test-parachains/adder/collator/Cargo.toml b/polkadot/parachain/test-parachains/adder/collator/Cargo.toml index 8ce4ceb47c245b621dcbf0199b7a73ad29b1a6f5..cb283c271191f80a0f29275fa44eb883586aeb8b 100644 --- a/polkadot/parachain/test-parachains/adder/collator/Cargo.toml +++ b/polkadot/parachain/test-parachains/adder/collator/Cargo.toml @@ -16,7 +16,7 @@ path = "src/main.rs" [dependencies] parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive"] } -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } futures = "0.3.21" futures-timer = "3.0.2" log = { workspace = true, default-features = true } diff --git a/polkadot/parachain/test-parachains/undying/collator/Cargo.toml b/polkadot/parachain/test-parachains/undying/collator/Cargo.toml index 25fdbfa74ea08ae3a2b389927073aeadd066cb7d..238b98a66801c7b6ec9ef1bda9248621a5f4332c 100644 --- a/polkadot/parachain/test-parachains/undying/collator/Cargo.toml +++ b/polkadot/parachain/test-parachains/undying/collator/Cargo.toml @@ -16,7 +16,7 @@ path = "src/main.rs" [dependencies] parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive"] } -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } futures = "0.3.21" futures-timer = "3.0.2" log = { workspace = true, default-features = true } diff --git a/polkadot/primitives/Cargo.toml b/polkadot/primitives/Cargo.toml index 58d3d10016107b73ad422f669ba054d668a81781..e63fb621c7880c03fe0282a5500df69d20528f99 100644 --- a/polkadot/primitives/Cargo.toml +++ b/polkadot/primitives/Cargo.toml @@ -14,6 +14,7 @@ bitvec = { version = "1.0.0", default-features = false, features = ["alloc", "se hex-literal = "0.4.1" parity-scale-codec = { version = "3.6.1", default-features = false, features = ["bit-vec", "derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["bit-vec", "derive", "serde"] } +log = { workspace = true, default-features = false } serde = { features = ["alloc", "derive"], workspace = true } application-crypto = { package = "sp-application-crypto", path = "../../substrate/primitives/application-crypto", default-features = false, features = ["serde"] } @@ -38,6 +39,7 @@ std = [ "application-crypto/std", "bitvec/std", "inherents/std", + "log/std", "parity-scale-codec/std", "polkadot-core-primitives/std", "polkadot-parachain-primitives/std", diff --git a/polkadot/primitives/src/lib.rs b/polkadot/primitives/src/lib.rs index 2ddd9b58dfe45d6560e9dc5c62f3d047ad8d9850..745195ce092ab1e4678b54e594901275260dc7da 100644 --- a/polkadot/primitives/src/lib.rs +++ b/polkadot/primitives/src/lib.rs @@ -58,7 +58,8 @@ pub use v6::{ ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, ValidityAttestation, ValidityError, ASSIGNMENT_KEY_TYPE_ID, LEGACY_MIN_BACKING_VOTES, LOWEST_PUBLIC_ID, MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE, MAX_POV_SIZE, MIN_CODE_SIZE, - ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, PARACHAINS_INHERENT_IDENTIFIER, PARACHAIN_KEY_TYPE_ID, + ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, ON_DEMAND_MAX_QUEUE_MAX_SIZE, PARACHAINS_INHERENT_IDENTIFIER, + PARACHAIN_KEY_TYPE_ID, }; #[cfg(feature = "std")] diff --git a/polkadot/primitives/src/runtime_api.rs b/polkadot/primitives/src/runtime_api.rs index d661005e32ffc9ce146e96ad984fb3f60025ca4c..6dca33f88234bfb064ade5f519dec378dc01ce71 100644 --- a/polkadot/primitives/src/runtime_api.rs +++ b/polkadot/primitives/src/runtime_api.rs @@ -117,14 +117,18 @@ use crate::{ async_backing, slashing, vstaging::{self, ApprovalVotingParams}, AsyncBackingParams, BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, - OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, - SessionIndex, SessionInfo, ValidatorId, ValidatorIndex, ValidatorSignature, + CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, + GroupRotationInfo, Hash, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, + ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidatorId, ValidatorIndex, + ValidatorSignature, }; use polkadot_core_primitives as pcp; use polkadot_parachain_primitives::primitives as ppp; -use sp_std::{collections::btree_map::BTreeMap, prelude::*}; +use sp_std::{ + collections::{btree_map::BTreeMap, vec_deque::VecDeque}, + prelude::*, +}; sp_api::decl_runtime_apis! { /// The API for querying the state of parachains on-chain. @@ -281,5 +285,10 @@ sp_api::decl_runtime_apis! { /// Approval voting configuration parameters #[api_version(10)] fn approval_voting_params() -> ApprovalVotingParams; + + /***** Added in v11 *****/ + /// Claim queue + #[api_version(11)] + fn claim_queue() -> BTreeMap>; } } diff --git a/polkadot/primitives/src/v6/executor_params.rs b/polkadot/primitives/src/v6/executor_params.rs index 112a529f62b0570e9270d1f09e397e6b9dc358b0..1e19f3b23fec9b9889d5976380f52ab6b66072b4 100644 --- a/polkadot/primitives/src/v6/executor_params.rs +++ b/polkadot/primitives/src/v6/executor_params.rs @@ -159,7 +159,9 @@ impl sp_std::fmt::LowerHex for ExecutorParamsHash { // into individual fields of the structure. Thus, complex migrations shall be avoided when adding // new entries and removing old ones. At the moment, there's no mandatory parameters defined. If // they show up, they must be clearly documented as mandatory ones. -#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo, Serialize, Deserialize)] +#[derive( + Clone, Debug, Default, Encode, Decode, PartialEq, Eq, TypeInfo, Serialize, Deserialize, +)] pub struct ExecutorParams(Vec); impl ExecutorParams { @@ -334,9 +336,3 @@ impl From<&[ExecutorParam]> for ExecutorParams { ExecutorParams(arr.to_vec()) } } - -impl Default for ExecutorParams { - fn default() -> Self { - ExecutorParams(vec![]) - } -} diff --git a/polkadot/primitives/src/v6/mod.rs b/polkadot/primitives/src/v6/mod.rs index 4938d20d2d1bd5c4aad82a99704eb715bbc66197..21cee753265246272a31d0ec41d82ad1203206eb 100644 --- a/polkadot/primitives/src/v6/mod.rs +++ b/polkadot/primitives/src/v6/mod.rs @@ -16,7 +16,7 @@ //! `V6` Primitives. -use bitvec::vec::BitVec; +use bitvec::{field::BitField, slice::BitSlice, vec::BitVec}; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_std::{ @@ -72,6 +72,7 @@ pub use metrics::{ /// The key type ID for a collator key. pub const COLLATOR_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"coll"); +const LOG_TARGET: &str = "runtime::primitives"; mod collator_app { use application_crypto::{app_crypto, sr25519}; @@ -398,6 +399,13 @@ pub const MAX_POV_SIZE: u32 = 5 * 1024 * 1024; /// Can be adjusted in configuration. pub const ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE: u32 = 10_000; +/// Maximum for maximum queue size. +/// +/// Setting `on_demand_queue_max_size` to a value higher than this is unsound. This is more a +/// theoretical limit, just below enough what the target type supports, so comparisons are possible +/// even with indices that are overflowing the underyling type. +pub const ON_DEMAND_MAX_QUEUE_MAX_SIZE: u32 = 1_000_000_000; + /// Backing votes threshold used from the host prior to runtime API version 6 and from the runtime /// prior to v9 configuration migration. pub const LEGACY_MIN_BACKING_VOTES: u32 = 2; @@ -532,18 +540,6 @@ impl CandidateReceipt { } } -/// All data pertaining to the execution of a para candidate. -#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] -pub struct FullCandidateReceipt { - /// The inner candidate receipt. - pub inner: CandidateReceipt, - /// The validation data derived from the relay-chain state at that - /// point. The hash of the persisted validation data should - /// match the `persisted_validation_data_hash` in the descriptor - /// of the receipt. - pub validation_data: PersistedValidationData, -} - /// A candidate-receipt with commitments directly included. #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] #[cfg_attr(feature = "std", derive(Hash))] @@ -706,19 +702,50 @@ pub type UncheckedSignedAvailabilityBitfields = Vec { /// The candidate referred to. - pub candidate: CommittedCandidateReceipt, + candidate: CommittedCandidateReceipt, /// The validity votes themselves, expressed as signatures. - pub validity_votes: Vec, - /// The indices of the validators within the group, expressed as a bitfield. - pub validator_indices: BitVec, + validity_votes: Vec, + /// The indices of the validators within the group, expressed as a bitfield. May be extended + /// beyond the backing group size to contain the assigned core index, if ElasticScalingMVP is + /// enabled. + validator_indices: BitVec, } impl BackedCandidate { - /// Get a reference to the descriptor of the para. + /// Constructor + pub fn new( + candidate: CommittedCandidateReceipt, + validity_votes: Vec, + validator_indices: BitVec, + core_index: Option, + ) -> Self { + let mut instance = Self { candidate, validity_votes, validator_indices }; + if let Some(core_index) = core_index { + instance.inject_core_index(core_index); + } + instance + } + + /// Get a reference to the descriptor of the candidate. pub fn descriptor(&self) -> &CandidateDescriptor { &self.candidate.descriptor } + /// Get a reference to the committed candidate receipt of the candidate. + pub fn candidate(&self) -> &CommittedCandidateReceipt { + &self.candidate + } + + /// Get a reference to the validity votes of the candidate. + pub fn validity_votes(&self) -> &[ValidityAttestation] { + &self.validity_votes + } + + /// Get a mutable reference to validity votes of the para. + pub fn validity_votes_mut(&mut self) -> &mut Vec { + &mut self.validity_votes + } + /// Compute this candidate's hash. pub fn hash(&self) -> CandidateHash where @@ -734,6 +761,48 @@ impl BackedCandidate { { self.candidate.to_plain() } + + /// Get a copy of the validator indices and the assumed core index, if any. + pub fn validator_indices_and_core_index( + &self, + core_index_enabled: bool, + ) -> (&BitSlice, Option) { + // This flag tells us if the block producers must enable Elastic Scaling MVP hack. + // It extends `BackedCandidate::validity_indices` to store a 8 bit core index. + if core_index_enabled { + let core_idx_offset = self.validator_indices.len().saturating_sub(8); + if core_idx_offset > 0 { + let (validator_indices_slice, core_idx_slice) = + self.validator_indices.split_at(core_idx_offset); + return ( + validator_indices_slice, + Some(CoreIndex(core_idx_slice.load::() as u32)), + ); + } + } + + (&self.validator_indices, None) + } + + /// Inject a core index in the validator_indices bitvec. + fn inject_core_index(&mut self, core_index: CoreIndex) { + let core_index_to_inject: BitVec = + BitVec::from_vec(vec![core_index.0 as u8]); + self.validator_indices.extend(core_index_to_inject); + } + + /// Update the validator indices and core index in the candidate. + pub fn set_validator_indices_and_core_index( + &mut self, + new_indices: BitVec, + maybe_core_index: Option, + ) { + self.validator_indices = new_indices; + + if let Some(core_index) = maybe_core_index { + self.inject_core_index(core_index); + } + } } /// Verify the backing of the given candidate. @@ -746,43 +815,65 @@ impl BackedCandidate { /// /// Returns either an error, indicating that one of the signatures was invalid or that the index /// was out-of-bounds, or the number of signatures checked. -pub fn check_candidate_backing + Clone + Encode>( - backed: &BackedCandidate, +pub fn check_candidate_backing + Clone + Encode + core::fmt::Debug>( + candidate_hash: CandidateHash, + validity_votes: &[ValidityAttestation], + validator_indices: &BitSlice, signing_context: &SigningContext, group_len: usize, validator_lookup: impl Fn(usize) -> Option, ) -> Result { - if backed.validator_indices.len() != group_len { + if validator_indices.len() != group_len { + log::debug!( + target: LOG_TARGET, + "Check candidate backing: indices mismatch: group_len = {} , indices_len = {}", + group_len, + validator_indices.len(), + ); return Err(()) } - if backed.validity_votes.len() > group_len { + if validity_votes.len() > group_len { + log::debug!( + target: LOG_TARGET, + "Check candidate backing: Too many votes, expected: {}, found: {}", + group_len, + validity_votes.len(), + ); return Err(()) } - // this is known, even in runtime, to be blake2-256. - let hash = backed.candidate.hash(); - let mut signed = 0; - for ((val_in_group_idx, _), attestation) in backed - .validator_indices + for ((val_in_group_idx, _), attestation) in validator_indices .iter() .enumerate() .filter(|(_, signed)| **signed) - .zip(backed.validity_votes.iter()) + .zip(validity_votes.iter()) { let validator_id = validator_lookup(val_in_group_idx).ok_or(())?; - let payload = attestation.signed_payload(hash, signing_context); + let payload = attestation.signed_payload(candidate_hash, signing_context); let sig = attestation.signature(); if sig.verify(&payload[..], &validator_id) { signed += 1; } else { + log::debug!( + target: LOG_TARGET, + "Check candidate backing: Invalid signature. validator_id = {:?}, validator_index = {} ", + validator_id, + val_in_group_idx, + ); return Err(()) } } - if signed != backed.validity_votes.len() { + if signed != validity_votes.len() { + log::error!( + target: LOG_TARGET, + "Check candidate backing: Too many signatures, expected = {}, found = {}", + validity_votes.len(), + signed, + ); return Err(()) } @@ -1366,7 +1457,7 @@ pub enum ValidDisputeStatementKind { #[codec(index = 3)] ApprovalChecking, /// An approval vote from the new version. - /// We can't create this version untill all nodes + /// We can't create this version until all nodes /// have been updated to support it and max_approval_coalesce_count /// is set to more than 1. #[codec(index = 4)] @@ -1513,7 +1604,7 @@ impl ValidityAttestation { pub fn to_compact_statement(&self, candidate_hash: CandidateHash) -> CompactStatement { // Explicit and implicit map directly from // `ValidityVote::Valid` and `ValidityVote::Issued`, and hence there is a - // `1:1` relationshow which enables the conversion. + // `1:1` relationship which enables the conversion. match *self { ValidityAttestation::Implicit(_) => CompactStatement::Seconded(candidate_hash), ValidityAttestation::Explicit(_) => CompactStatement::Valid(candidate_hash), @@ -1859,6 +1950,34 @@ pub enum PvfExecKind { #[cfg(test)] mod tests { use super::*; + use bitvec::bitvec; + use primitives::sr25519; + + pub fn dummy_committed_candidate_receipt() -> CommittedCandidateReceipt { + let zeros = Hash::zero(); + + CommittedCandidateReceipt { + descriptor: CandidateDescriptor { + para_id: 0.into(), + relay_parent: zeros, + collator: CollatorId::from(sr25519::Public::default()), + persisted_validation_data_hash: zeros, + pov_hash: zeros, + erasure_root: zeros, + signature: CollatorSignature::from(sr25519::Signature::default()), + para_head: zeros, + validation_code_hash: ValidationCode(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).hash(), + }, + commitments: CandidateCommitments { + head_data: HeadData(vec![]), + upward_messages: vec![].try_into().expect("empty vec fits within bounds"), + new_validation_code: None, + horizontal_messages: vec![].try_into().expect("empty vec fits within bounds"), + processed_downward_messages: 0, + hrmp_watermark: 0_u32, + }, + } + } #[test] fn group_rotation_info_calculations() { @@ -1933,4 +2052,73 @@ mod tests { assert!(zero_b.leading_zeros() >= zero_u.leading_zeros()); } + + #[test] + fn test_backed_candidate_injected_core_index() { + let initial_validator_indices = bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1]; + let mut candidate = BackedCandidate::new( + dummy_committed_candidate_receipt(), + vec![], + initial_validator_indices.clone(), + None, + ); + + // No core index supplied, ElasticScalingMVP is off. + let (validator_indices, core_index) = candidate.validator_indices_and_core_index(false); + assert_eq!(validator_indices, initial_validator_indices.as_bitslice()); + assert!(core_index.is_none()); + + // No core index supplied, ElasticScalingMVP is on. Still, decoding will be ok if backing + // group size is <= 8, to give a chance to parachains that don't have multiple cores + // assigned. + let (validator_indices, core_index) = candidate.validator_indices_and_core_index(true); + assert_eq!(validator_indices, initial_validator_indices.as_bitslice()); + assert!(core_index.is_none()); + + let encoded_validator_indices = candidate.validator_indices.clone(); + candidate.set_validator_indices_and_core_index(validator_indices.into(), core_index); + assert_eq!(candidate.validator_indices, encoded_validator_indices); + + // No core index supplied, ElasticScalingMVP is on. Decoding is corrupted if backing group + // size larger than 8. + let candidate = BackedCandidate::new( + dummy_committed_candidate_receipt(), + vec![], + bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1, 0, 1, 0, 1, 0], + None, + ); + let (validator_indices, core_index) = candidate.validator_indices_and_core_index(true); + assert_eq!(validator_indices, bitvec![u8, bitvec::order::Lsb0; 0].as_bitslice()); + assert!(core_index.is_some()); + + // Core index supplied, ElasticScalingMVP is off. Core index will be treated as normal + // validator indices. Runtime will check against this. + let candidate = BackedCandidate::new( + dummy_committed_candidate_receipt(), + vec![], + bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1], + Some(CoreIndex(10)), + ); + let (validator_indices, core_index) = candidate.validator_indices_and_core_index(false); + assert_eq!( + validator_indices, + bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0] + ); + assert!(core_index.is_none()); + + // Core index supplied, ElasticScalingMVP is on. + let mut candidate = BackedCandidate::new( + dummy_committed_candidate_receipt(), + vec![], + bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1], + Some(CoreIndex(10)), + ); + let (validator_indices, core_index) = candidate.validator_indices_and_core_index(true); + assert_eq!(validator_indices, bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1]); + assert_eq!(core_index, Some(CoreIndex(10))); + + let encoded_validator_indices = candidate.validator_indices.clone(); + candidate.set_validator_indices_and_core_index(validator_indices.into(), core_index); + assert_eq!(candidate.validator_indices, encoded_validator_indices); + } } diff --git a/polkadot/primitives/src/v6/slashing.rs b/polkadot/primitives/src/v6/slashing.rs index efffb932cdc0d997e08f9d195e2c42b857a7c8f5..bcd7d0c2fc4455e2ebf01fab027dbf0618f63dba 100644 --- a/polkadot/primitives/src/v6/slashing.rs +++ b/polkadot/primitives/src/v6/slashing.rs @@ -54,7 +54,7 @@ impl DisputesTimeSlot { /// is required to identify and verify it. #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug)] pub struct DisputeProof { - /// Time slot when the dispute occured. + /// Time slot when the dispute occurred. pub time_slot: DisputesTimeSlot, /// The dispute outcome. pub kind: SlashingOffenceKind, diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 630bcf8679ad3046ce734042a1557058ea440110..94e9e89202989c6d3ffe09d937c37c2b9ccf58ac 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -23,6 +23,7 @@ use sp_std::prelude::*; use parity_scale_codec::{Decode, Encode}; use primitives::RuntimeDebug; use scale_info::TypeInfo; +use sp_arithmetic::Perbill; /// Approval voting configuration parameters #[derive( @@ -50,6 +51,81 @@ impl Default for ApprovalVotingParams { } } +/// Scheduler configuration parameters. All coretime/ondemand parameters are here. +#[derive( + RuntimeDebug, + Copy, + Clone, + PartialEq, + Encode, + Decode, + TypeInfo, + serde::Serialize, + serde::Deserialize, +)] +pub struct SchedulerParams { + /// How often parachain groups should be rotated across parachains. + /// + /// Must be non-zero. + pub group_rotation_frequency: BlockNumber, + /// Availability timeout for a block on a core, measured in blocks. + /// + /// This is the maximum amount of blocks after a core became occupied that validators have time + /// to make the block available. + /// + /// This value only has effect on group rotations. If backers backed something at the end of + /// their rotation, the occupied core affects the backing group that comes afterwards. We limit + /// the effect one backing group can have on the next to `paras_availability_period` blocks. + /// + /// Within a group rotation there is no timeout as backers are only affecting themselves. + /// + /// Must be at least 1. With a value of 1, the previous group will not be able to negatively + /// affect the following group at the expense of a tight availability timeline at group + /// rotation boundaries. + pub paras_availability_period: BlockNumber, + /// The maximum number of validators to have per core. + /// + /// `None` means no maximum. + pub max_validators_per_core: Option, + /// The amount of blocks ahead to schedule paras. + pub lookahead: u32, + /// How many cores are managed by the coretime chain. + pub num_cores: u32, + /// The max number of times a claim can time out in availability. + pub max_availability_timeouts: u32, + /// The maximum queue size of the pay as you go module. + pub on_demand_queue_max_size: u32, + /// The target utilization of the spot price queue in percentages. + pub on_demand_target_queue_utilization: Perbill, + /// How quickly the fee rises in reaction to increased utilization. + /// The lower the number the slower the increase. + pub on_demand_fee_variability: Perbill, + /// The minimum amount needed to claim a slot in the spot pricing queue. + pub on_demand_base_fee: Balance, + /// The number of blocks a claim stays in the scheduler's claim queue before getting cleared. + /// This number should go reasonably higher than the number of blocks in the async backing + /// lookahead. + pub ttl: BlockNumber, +} + +impl> Default for SchedulerParams { + fn default() -> Self { + Self { + group_rotation_frequency: 1u32.into(), + paras_availability_period: 1u32.into(), + max_validators_per_core: Default::default(), + lookahead: 1, + num_cores: Default::default(), + max_availability_timeouts: Default::default(), + on_demand_queue_max_size: ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, + on_demand_target_queue_utilization: Perbill::from_percent(25), + on_demand_fee_variability: Perbill::from_percent(3), + on_demand_base_fee: 10_000_000u128, + ttl: 5u32.into(), + } + } +} + use bitvec::vec::BitVec; /// Bit indices in the `HostConfiguration.node_features` that correspond to different node features. @@ -57,16 +133,20 @@ pub type NodeFeatures = BitVec; /// Module containing feature-specific bit indices into the `NodeFeatures` bitvec. pub mod node_features { - /// A feature index used to indentify a bit into the node_features array stored + /// A feature index used to identify a bit into the node_features array stored /// in the HostConfiguration. #[repr(u8)] pub enum FeatureIndex { /// Tells if tranch0 assignments could be sent in a single certificate. /// Reserved for: `` EnableAssignmentsV2 = 0, + /// This feature enables the extension of `BackedCandidate::validator_indices` by 8 bits. + /// The value stored there represents the assumed core index where the candidates + /// are backed. This is needed for the elastic scaling MVP. + ElasticScalingMVP = 1, /// First unassigned feature bit. /// Every time a new feature flag is assigned it should take this value. /// and this should be incremented. - FirstUnassigned = 1, + FirstUnassigned = 2, } } diff --git a/polkadot/primitives/test-helpers/src/lib.rs b/polkadot/primitives/test-helpers/src/lib.rs index c0118f5960a47eabe205c4d2cf6bcf6d295ce347..d43cf3317e573adffb21b8fdc077eeebf993d40d 100644 --- a/polkadot/primitives/test-helpers/src/lib.rs +++ b/polkadot/primitives/test-helpers/src/lib.rs @@ -136,17 +136,17 @@ pub fn dummy_head_data() -> HeadData { /// Create a meaningless collator id. pub fn dummy_collator() -> CollatorId { - CollatorId::from(sr25519::Public::from_raw([0; 32])) + CollatorId::from(sr25519::Public::default()) } /// Create a meaningless validator id. pub fn dummy_validator() -> ValidatorId { - ValidatorId::from(sr25519::Public::from_raw([0; 32])) + ValidatorId::from(sr25519::Public::default()) } /// Create a meaningless collator signature. pub fn dummy_collator_signature() -> CollatorSignature { - CollatorSignature::from(sr25519::Signature([0u8; 64])) + CollatorSignature::from(sr25519::Signature::default()) } /// Create a meaningless persisted validation data. @@ -249,7 +249,7 @@ pub fn resign_candidate_descriptor_with_collator>( descriptor.signature = signature; } -/// Extracts validators's public keus (`ValidatorId`) from `Sr25519Keyring` +/// Extracts validators's public keys (`ValidatorId`) from `Sr25519Keyring` pub fn validator_pubkeys(val_ids: &[Sr25519Keyring]) -> Vec { val_ids.iter().map(|v| v.public().into()).collect() } diff --git a/polkadot/roadmap/implementers-guide/src/disputes-flow.md b/polkadot/roadmap/implementers-guide/src/disputes-flow.md index b5cc5611c6ff897826e128d240c38761991e0a5c..540b3c45bad4b3328437ad5e6177227ef13a4f22 100644 --- a/polkadot/roadmap/implementers-guide/src/disputes-flow.md +++ b/polkadot/roadmap/implementers-guide/src/disputes-flow.md @@ -74,7 +74,7 @@ The set of validators eligible to vote consists of the validators that had duty votes by the backing validators. If a validator receives an initial dispute message (a set of votes where there are at least two opposing votes -contained), and the PoV or Code are hence not reconstructable from local storage, that validator must request the +contained), and the PoV or Code are hence not reconstructible from local storage, that validator must request the required data from its peers. The dispute availability message must contain code, persisted validation data, and the proof of validity. diff --git a/polkadot/roadmap/implementers-guide/src/node/approval/approval-distribution.md b/polkadot/roadmap/implementers-guide/src/node/approval/approval-distribution.md index ce71de6f76b8f8550e815849ba80002f890a9b85..c987b7fe5beaa366c23c2fc58a179a916bbb8a21 100644 --- a/polkadot/roadmap/implementers-guide/src/node/approval/approval-distribution.md +++ b/polkadot/roadmap/implementers-guide/src/node/approval/approval-distribution.md @@ -101,7 +101,7 @@ struct State { } enum MessageFingerprint { - Assigment(Hash, u32, ValidatorIndex), + Assignment(Hash, u32, ValidatorIndex), Approval(Hash, u32, ValidatorIndex), } @@ -203,7 +203,7 @@ For all peers: * Compute `view_intersection` as the intersection of the peer's view blocks with the hashes of the new blocks. * Invoke `unify_with_peer(peer, view_intersection)`. -#### `ApprovalDistributionMessage::DistributeAsignment` +#### `ApprovalDistributionMessage::DistributeAssignment` Call `import_and_circulate_assignment` with `MessageSource::Local`. diff --git a/polkadot/roadmap/implementers-guide/src/node/availability/availability-recovery.md b/polkadot/roadmap/implementers-guide/src/node/availability/availability-recovery.md index e3bb14db3a55499dfc1546494c8e83d5e6cb6ed7..c57c4589244e79f6cd54a87f823abc466ed8eb43 100644 --- a/polkadot/roadmap/implementers-guide/src/node/availability/availability-recovery.md +++ b/polkadot/roadmap/implementers-guide/src/node/availability/availability-recovery.md @@ -115,7 +115,7 @@ On `Conclude`, shut down the subsystem. Launch the source as a background task running `run(recovery_task)`. -#### `run(recovery_task) -> Result` +#### `run(recovery_task) -> Result` ```rust // How many parallel requests to have going at once. diff --git a/polkadot/roadmap/implementers-guide/src/node/disputes/dispute-coordinator.md b/polkadot/roadmap/implementers-guide/src/node/disputes/dispute-coordinator.md index e0738e219d1b6f02e20d5bd360b9ed9f72b3cbd7..90b29249f3e5bdf2170d0f56e95e99db1cfd1b83 100644 --- a/polkadot/roadmap/implementers-guide/src/node/disputes/dispute-coordinator.md +++ b/polkadot/roadmap/implementers-guide/src/node/disputes/dispute-coordinator.md @@ -76,7 +76,7 @@ dispute is raised reconsider their vote and send an explicit invalid vote. If th recorded, then they could avoid a slash. This is not a problem for our basic security assumptions: The backers are the ones to be supposed to have skin in the -game, so we are not too woried about colluding approval voters getting away slash free as the gambler's ruin property is +game, so we are not too worried about colluding approval voters getting away slash free as the gambler's ruin property is maintained anyway. There is however a separate problem, from colluding approval-voters, that is "lazy" approval voters. If it were easy and reliable for approval-voters to reconsider their vote, in case of an actual dispute, then they don't have a direct incentive (apart from playing a part in securing the network) to properly run the validation function at @@ -331,13 +331,13 @@ off-chain. The reason for this is a dispute can be raised for a candidate in a p validator that is going to be slashed for it might not even be in the current active set. That means it can't be disabled on-chain. We need a way to prevent someone from disputing all valid candidates in the previous era. We do this by keeping track of the validators who lost a dispute in the past few sessions and use that list in addition to the -on-chain disabled validators state. In addition to past session misbehavior, this also heps in case a slash is delayed. +on-chain disabled validators state. In addition to past session misbehavior, this also helps in case a slash is delayed. When we receive a dispute statements set, we do the following: 1. Take the on-chain state of disabled validators at the relay parent block. 1. Take a list of those who lost a dispute in that session in the order that prioritizes the biggest and newest offence. 1. Combine the two lists and take the first byzantine threshold validators from it. -1. If the dispute is unconfimed, check if all votes against the candidate are from disabled validators. +1. If the dispute is unconfirmed, check if all votes against the candidate are from disabled validators. If so, we don't participate in the dispute, but record the votes. ### Backing Votes @@ -591,7 +591,7 @@ Initiates processing via the `Participation` module and updates the internal sta ### On `MuxedMessage::Participation` -This message is sent from `Participatuion` module and indicates a processed dispute participation. It's the result of +This message is sent from `Participation` module and indicates a processed dispute participation. It's the result of the processing job initiated with `OverseerSignal::ActiveLeaves`. The subsystem issues a `DisputeMessage` with the result. diff --git a/polkadot/roadmap/implementers-guide/src/node/overseer.md b/polkadot/roadmap/implementers-guide/src/node/overseer.md index 53a1153081013b684d0cab419fb3eaa0be5a15c1..960539b84998455ab52350a78f432dd9d399ab89 100644 --- a/polkadot/roadmap/implementers-guide/src/node/overseer.md +++ b/polkadot/roadmap/implementers-guide/src/node/overseer.md @@ -108,11 +108,11 @@ way that the receiving subsystem can further address the communication to one of This communication prevents a certain class of race conditions. When the Overseer determines that it is time for subsystems to begin working on top of a particular relay-parent, it will dispatch a `ActiveLeavesUpdate` message to all subsystems to do so, and those messages will be handled asynchronously by those subsystems. Some subsystems will receive -those messsages before others, and it is important that a message sent by subsystem A after receiving +those messages before others, and it is important that a message sent by subsystem A after receiving `ActiveLeavesUpdate` message will arrive at subsystem B after its `ActiveLeavesUpdate` message. If subsystem A maintained an independent channel with subsystem B to communicate, it would be possible for subsystem B to handle the side message before the `ActiveLeavesUpdate` message, but it wouldn't have any logical course of action to take with the -side message - leading to it being discarded or improperly handled. Well-architectured state machines should have a +side message - leading to it being discarded or improperly handled. Well-architected state machines should have a single source of inputs, so that is what we do here. One exception is reasonable to make for responses to requests. A request should be made via the overseer in order to diff --git a/polkadot/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md b/polkadot/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md index e0984bd58d1dd8ea22b145a15b4a3bab8779de80..39a317a07c847aa734e78f85de524f5541c8ddf6 100644 --- a/polkadot/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md +++ b/polkadot/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md @@ -125,6 +125,14 @@ execution request: reason, which may or may not be independent of the candidate or PVF. 5. **Internal errors:** See "Internal Errors" section. In this case, after the retry we abstain from voting. +6. **RuntimeConstruction** error. The precheck handles a general case of a wrong + artifact but doesn't guarantee its consistency between the preparation and + the execution. If something happened with the artifact between + the preparation of the artifact and its execution (e.g. the artifact was + corrupted on disk or a dirty node upgrade happened when the prepare worker + has a wasmtime version different from the execute worker's wasmtime version). + We treat such an error as possibly transient due to local issues and retry + one time. ### Preparation timeouts diff --git a/polkadot/roadmap/implementers-guide/src/node/utility/pvf-prechecker.md b/polkadot/roadmap/implementers-guide/src/node/utility/pvf-prechecker.md index 7f6fef7ddf631f10eadd7c1cc5f4d7d4b7f9cd04..f8e88a67fcb813cae22fc43abd273f020b91620f 100644 --- a/polkadot/roadmap/implementers-guide/src/node/utility/pvf-prechecker.md +++ b/polkadot/roadmap/implementers-guide/src/node/utility/pvf-prechecker.md @@ -8,7 +8,7 @@ pre-checking. Head over to [overview] for the PVF pre-checking process overview. There is no dedicated input mechanism for PVF pre-checker. Instead, PVF pre-checker looks on the `ActiveLeavesUpdate` event stream for work. -This subsytem does not produce any output messages either. The subsystem will, however, send messages to the +This subsystem does not produce any output messages either. The subsystem will, however, send messages to the [Runtime API] subsystem to query for the pending PVFs and to submit votes. In addition to that, it will also communicate with [Candidate Validation] Subsystem to request PVF pre-check. diff --git a/polkadot/roadmap/implementers-guide/src/runtime/README.md b/polkadot/roadmap/implementers-guide/src/runtime/README.md index 459f0e6b69d98bddb19a9ccea85830ed8dc01150..10eedb49ec38233af5b17992134f1f14dfcbaa74 100644 --- a/polkadot/roadmap/implementers-guide/src/runtime/README.md +++ b/polkadot/roadmap/implementers-guide/src/runtime/README.md @@ -89,7 +89,7 @@ struct SessionChangeNotification { prev_config: HostConfiguration, // The configuration after handling the session change. new_config: HostConfiguration, - // A secure randomn seed for the session, gathered from BABE. + // A secure random seed for the session, gathered from BABE. random_seed: [u8; 32], // The session index of the beginning session. session_index: SessionIndex, diff --git a/polkadot/roadmap/implementers-guide/src/runtime/hrmp.md b/polkadot/roadmap/implementers-guide/src/runtime/hrmp.md index 69d33ca8670db942ecd8f5cb564f8d973ef6e47b..ed765634b592cdabf0b1f971ce34e7edbc9f069c 100644 --- a/polkadot/roadmap/implementers-guide/src/runtime/hrmp.md +++ b/polkadot/roadmap/implementers-guide/src/runtime/hrmp.md @@ -62,7 +62,7 @@ HRMP related storage layout HrmpOpenChannelRequests: map HrmpChannelId => Option; HrmpOpenChannelRequestsList: Vec; -/// This mapping tracks how many open channel requests are inititated by a given sender para. +/// This mapping tracks how many open channel requests are initiated by a given sender para. /// Invariant: `HrmpOpenChannelRequests` should contain the same number of items that has `(X, _)` /// as the number of `HrmpOpenChannelRequestCount` for `X`. HrmpOpenChannelRequestCount: map ParaId => u32; @@ -233,7 +233,7 @@ executed the message. 1. Send a downward message to the opposite party notifying about the channel closing. * The DM is sent using `queue_downward_message`. * The DM is represented by the `HrmpChannelClosing` XCM message with: - * `initator` is set to `origin`, + * `initiator` is set to `origin`, * `sender` is set to `ch.sender`, * `recipient` is set to `ch.recipient`. * The opposite party is `ch.sender` if `origin` is `ch.recipient` and `ch.recipient` if `origin` is `ch.sender`. diff --git a/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md b/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md index f6a32a01d5025e2fb824b94f804fd1e89fd0392b..fd74f33253b72bd629313b5763e2cbbed75a6314 100644 --- a/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md +++ b/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md @@ -147,15 +147,16 @@ All failed checks should lead to an unrecoverable error making the block invalid // return a vector of cleaned-up core IDs. } ``` -* `force_enact(ParaId)`: Forcibly enact the candidate with the given ID as though it had been deemed available by - bitfields. Is a no-op if there is no candidate pending availability for this para-id. This should generally not be - used but it is useful during execution of Runtime APIs, where the changes to the state are expected to be discarded - directly after. +* `force_enact(ParaId)`: Forcibly enact the pending candidates of the given paraid as though they had been deemed + available by bitfields. Is a no-op if there is no candidate pending availability for this para-id. + If there are multiple candidates pending availability for this para-id, it will enact all of + them. This should generally not be used but it is useful during execution of Runtime APIs, + where the changes to the state are expected to be discarded directly after. * `candidate_pending_availability(ParaId) -> Option`: returns the `CommittedCandidateReceipt` pending availability for the para provided, if any. * `pending_availability(ParaId) -> Option`: returns the metadata around the candidate pending availability for the para, if any. -* `collect_disputed(disputed: Vec) -> Vec`: Sweeps through all paras pending availability. If +* `free_disputed(disputed: Vec) -> Vec`: Sweeps through all paras pending availability. If the candidate hash is one of the disputed candidates, then clean up the corresponding storage for that candidate and the commitments. Return a vector of cleaned-up core IDs. diff --git a/polkadot/roadmap/implementers-guide/src/runtime/parainherent.md b/polkadot/roadmap/implementers-guide/src/runtime/parainherent.md index 5419ddae83d4a58222bc405e41a58ca0fd8315f3..7972c706b9ee1ebce2132a49ccf62a194d6c1e58 100644 --- a/polkadot/roadmap/implementers-guide/src/runtime/parainherent.md +++ b/polkadot/roadmap/implementers-guide/src/runtime/parainherent.md @@ -17,7 +17,7 @@ There are a couple of important notes to the operations in this inherent as they this fork. 1. When disputes are initiated, we remove the block from pending availability. This allows us to roll back chains to the block before blocks are included as opposed to backing. It's important to do this before processing bitfields. -1. `Inclusion::collect_disputed` is kind of expensive so it's important to gate this on whether there are actually any +1. `Inclusion::free_disputed` is kind of expensive so it's important to gate this on whether there are actually any new disputes. Which should be never. 1. And we don't accept parablocks that have open disputes or disputes that have concluded against the candidate. It's important to import dispute statements before backing, but this is already the case as disputes are imported before @@ -88,7 +88,7 @@ to `sanitize_bitfields` function for implementation details. Backed candidates sanitization removes malformed ones, candidates which have got concluded invalid disputes against them or candidates produced by unassigned cores. Furthermore any backing votes from disabled validators for a candidate are dropped. This is part of the validator disabling strategy. After filtering the statements from disabled validators a -backed candidate may end up with votes count less than `minimum_backing_votes` (a parameter from `HostConfiguiration`). +backed candidate may end up with votes count less than `minimum_backing_votes` (a parameter from `HostConfiguration`). In this case the whole candidate is dropped otherwise it will be rejected by `process_candidates` from pallet inclusion. All checks related to backed candidates are implemented in `sanitize_backed_candidates` and `filter_backed_statements_from_disabled_validators`. diff --git a/polkadot/roadmap/implementers-guide/src/runtime/scheduler.md b/polkadot/roadmap/implementers-guide/src/runtime/scheduler.md index 32a7fe652dbcb0ee60aa7d828d3a8226dcb80730..04b221a83e587e1de20b6e0088d88aa731354ba3 100644 --- a/polkadot/roadmap/implementers-guide/src/runtime/scheduler.md +++ b/polkadot/roadmap/implementers-guide/src/runtime/scheduler.md @@ -285,7 +285,6 @@ No finalization routine runs for this module. - This clears them from `Scheduled` and marks each corresponding `core` in the `AvailabilityCores` as occupied. - Since both the availability cores and the newly-occupied cores lists are sorted ascending, this method can be implemented efficiently. -- `core_para(CoreIndex) -> ParaId`: return the currently-scheduled or occupied ParaId for the given core. - `group_validators(GroupIndex) -> Option>`: return all validators in a given group, if the group index is valid for this session. - `availability_timeout_predicate() -> Option bool>`: returns an optional predicate diff --git a/polkadot/roadmap/implementers-guide/src/types/README.md b/polkadot/roadmap/implementers-guide/src/types/README.md index 87092bf3a005353b09d375b89f599c2918295ef5..5fd3050f94906f04e876e9018698edb1a5218e2f 100644 --- a/polkadot/roadmap/implementers-guide/src/types/README.md +++ b/polkadot/roadmap/implementers-guide/src/types/README.md @@ -55,17 +55,6 @@ digraph { CandidateCommitmentsHash [label = "Hash", shape="doublecircle", fill="gray90"] CandidateCommitmentsHash -> CandidateCommitments:name - FullCandidateReceipt [label = < - - - - -
FullCandidateReceipt<H = Hash, N = BlockNumber>
innerCandidateReceipt<H>
validation_dataValidationData<N>
- >] - - FullCandidateReceipt:inner -> CandidateReceipt:name - FullCandidateReceipt:validation_data -> ValidationData:name - CommittedCandidateReceipt [label = < diff --git a/polkadot/roadmap/implementers-guide/src/types/approval.md b/polkadot/roadmap/implementers-guide/src/types/approval.md index c19ffa53762a5fc9e2ac0766604efc22cf20ef4d..29d973ca0ab8c3717bfbaa1077bd5f9873cf7a94 100644 --- a/polkadot/roadmap/implementers-guide/src/types/approval.md +++ b/polkadot/roadmap/implementers-guide/src/types/approval.md @@ -39,7 +39,7 @@ enum AssignmentCertKindV2 { /// The core index chosen in this cert. core_index: CoreIndex, }, - /// Deprectated assignment. Soon to be removed. + /// Deprecated assignment. Soon to be removed. /// /// An assignment story based on the VRF that authorized the relay-chain block where the /// candidate was included combined with a sample number. @@ -117,7 +117,7 @@ struct IndirectSignedApprovalVote { ## `CheckedAssignmentCert` An assignment cert which has checked both the VRF and the validity of the implied assignment according to the selection -criteria rules of the protocol. This type should be declared in such a way as to be instantiatable only when the checks +criteria rules of the protocol. This type should be declared in such a way as to be instantiable only when the checks have actually been done. Fields should be accessible via getters, not direct struct access. ```rust diff --git a/polkadot/roadmap/implementers-guide/src/types/candidate.md b/polkadot/roadmap/implementers-guide/src/types/candidate.md index 00176229e5a4356a9f88ae91c16eb46617b47216..399d7854ac4a057f49435702adb1a815c960c593 100644 --- a/polkadot/roadmap/implementers-guide/src/types/candidate.md +++ b/polkadot/roadmap/implementers-guide/src/types/candidate.md @@ -20,12 +20,8 @@ struct ParaId(u32); ## Candidate Receipt -Much info in a [`FullCandidateReceipt`](#full-candidate-receipt) is duplicated from the relay-chain state. When the -corresponding relay-chain state is considered widely available, the Candidate Receipt should be favored over the -`FullCandidateReceipt`. - -Examples of situations where the state is readily available includes within the scope of work done by subsystems working -on a given relay-parent, or within the logic of the runtime importing a backed candidate. +Compact representation of the result of a validation. This is what validators +receive from collators, together with the PoV. ```rust /// A candidate-receipt. @@ -37,24 +33,6 @@ struct CandidateReceipt { } ``` -## Full Candidate Receipt - -This is the full receipt type. The `PersistedValidationData` are technically redundant with the `inner.relay_parent`, -which uniquely describes the block in the blockchain from whose state these values are derived. The -[`CandidateReceipt`](#candidate-receipt) variant is often used instead for this reason. - -However, the Full Candidate Receipt type is useful as a means of avoiding the implicit dependency on availability of old -blockchain state. In situations such as availability and approval, having the full description of the candidate within a -self-contained struct is convenient. - -```rust -/// All data pertaining to the execution of a para candidate. -struct FullCandidateReceipt { - inner: CandidateReceipt, - validation_data: PeristedValidationData, -} -``` - ## Committed Candidate Receipt This is a variant of the candidate receipt which includes the commitments of the candidate receipt alongside the diff --git a/polkadot/roadmap/implementers-guide/src/types/disputes.md b/polkadot/roadmap/implementers-guide/src/types/disputes.md index c49e0fea262510ef2e1585f9aa8433dc02883b50..ac09084c48a2f3170939387473ed4c772ee1dcf1 100644 --- a/polkadot/roadmap/implementers-guide/src/types/disputes.md +++ b/polkadot/roadmap/implementers-guide/src/types/disputes.md @@ -82,7 +82,7 @@ struct DisputeState { struct ScrapedOnChainVotes { /// The session index at which the block was included. session: SessionIndex, - /// The backing and seconding validity attestations for all candidates, provigind the full candidate receipt. + /// The backing and seconding validity attestations for all candidates, providing the full candidate receipt. backing_validators_per_candidate: Vec<(CandidateReceipt, Vec<(ValidatorIndex, ValidityAttestation)>)> /// Set of concluded disputes that were recorded /// on chain within the inherent. diff --git a/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md b/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md index 54cdc2edd12d2537c1b80d9f2e973562375fc150..e011afb97089aaf59d685b8d4bf9998d21390146 100644 --- a/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md +++ b/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md @@ -22,7 +22,7 @@ All subsystems have their own message types; all of them need to be able to list are currently two proposals for how to handle that with unified communication channels: 1. Retaining the `OverseerSignal` definition above, add `enum FromOrchestra {Signal(OverseerSignal), Message(T)}`. -1. Add a generic varint to `OverseerSignal`: `Message(T)`. +1. Add a generic variant to `OverseerSignal`: `Message(T)`. Either way, there will be some top-level type encapsulating messages from the overseer to each subsystem. @@ -340,9 +340,15 @@ enum BitfieldSigningMessage { } ```rust enum CandidateBackingMessage { /// Requests a set of backable candidates attested by the subsystem. - /// - /// Each pair is (candidate_hash, candidate_relay_parent). - GetBackedCandidates(Vec<(CandidateHash, Hash)>, oneshot::Sender>), + /// The order of candidates of the same para must be preserved in the response. + /// If a backed candidate of a para cannot be retrieved, the response should not contain any + /// candidates of the same para that follow it in the input vector. In other words, assuming + /// candidates are supplied in dependency order, we must ensure that this dependency order is + /// preserved. + GetBackedCandidates( + HashMap>, + oneshot::Sender>>, + ), /// Note that the Candidate Backing subsystem should second the given candidate in the context of the /// given relay-parent (ref. by hash). This candidate must be validated using the provided PoV. /// The PoV is expected to match the `pov_hash` in the descriptor. diff --git a/polkadot/roadmap/implementers-guide/src/types/runtime.md b/polkadot/roadmap/implementers-guide/src/types/runtime.md index 4b97409f8df3e4f3581161ef47713bfdfbcb9654..1dfabd96db7cdb929c416201eeeb233af11f5dbb 100644 --- a/polkadot/roadmap/implementers-guide/src/types/runtime.md +++ b/polkadot/roadmap/implementers-guide/src/types/runtime.md @@ -4,106 +4,24 @@ Types used within the runtime exclusively and pervasively. ## Host Configuration -The internal-to-runtime configuration of the parachain host. This is expected to be altered only by governance procedures. - -```rust -struct HostConfiguration { - /// The minimum period, in blocks, between which parachains can update their validation code. - pub validation_upgrade_cooldown: BlockNumber, - /// The delay, in blocks, before a validation upgrade is applied. - pub validation_upgrade_delay: BlockNumber, - /// How long to keep code on-chain, in blocks. This should be sufficiently long that disputes - /// have concluded. - pub code_retention_period: BlockNumber, - /// The maximum validation code size, in bytes. - pub max_code_size: u32, - /// The maximum head-data size, in bytes. - pub max_head_data_size: u32, - /// The amount of availability cores to dedicate to parathreads (on-demand parachains). - pub parathread_cores: u32, - /// The number of retries that a parathread (on-demand parachain) author has to submit their block. - pub parathread_retries: u32, - /// How often parachain groups should be rotated across parachains. - pub group_rotation_frequency: BlockNumber, - /// The availability period, in blocks, for parachains. This is the amount of blocks - /// after inclusion that validators have to make the block available and signal its availability to - /// the chain. Must be at least 1. - pub chain_availability_period: BlockNumber, - /// The availability period, in blocks, for parathreads (on-demand parachains). Same as the `chain_availability_period`, - /// but a differing timeout due to differing requirements. Must be at least 1. - pub thread_availability_period: BlockNumber, - /// The amount of blocks ahead to schedule on-demand parachains. - pub scheduling_lookahead: u32, - /// The maximum number of validators to have per core. `None` means no maximum. - pub max_validators_per_core: Option, - /// The maximum number of validators to use for parachains, in total. `None` means no maximum. - pub max_validators: Option, - /// The amount of sessions to keep for disputes. - pub dispute_period: SessionIndex, - /// How long after dispute conclusion to accept statements. - pub dispute_post_conclusion_acceptance_period: BlockNumber, - /// The maximum number of dispute spam slots - pub dispute_max_spam_slots: u32, - /// The amount of consensus slots that must pass between submitting an assignment and - /// submitting an approval vote before a validator is considered a no-show. - /// Must be at least 1. - pub no_show_slots: u32, - /// The number of delay tranches in total. - pub n_delay_tranches: u32, - /// The width of the zeroth delay tranche for approval assignments. This many delay tranches - /// beyond 0 are all consolidated to form a wide 0 tranche. - pub zeroth_delay_tranche_width: u32, - /// The number of validators needed to approve a block. - pub needed_approvals: u32, - /// The number of samples to use in `RelayVRFModulo` or `RelayVRFModuloCompact` approval assignment criterions. - pub relay_vrf_modulo_samples: u32, - /// Total number of individual messages allowed in the parachain -> relay-chain message queue. - pub max_upward_queue_count: u32, - /// Total size of messages allowed in the parachain -> relay-chain message queue before which - /// no further messages may be added to it. If it exceeds this then the queue may contain only - /// a single message. - pub max_upward_queue_size: u32, - /// The maximum size of an upward message that can be sent by a candidate. - /// - /// This parameter affects the upper bound of size of `CandidateCommitments`. - pub max_upward_message_size: u32, - /// The maximum number of messages that a candidate can contain. - /// - /// This parameter affects the upper bound of size of `CandidateCommitments`. - pub max_upward_message_num_per_candidate: u32, - /// The maximum size of a message that can be put in a downward message queue. - /// - /// Since we require receiving at least one DMP message the obvious upper bound of the size is - /// the PoV size. Of course, there is a lot of other different things that a parachain may - /// decide to do with its PoV so this value in practice will be picked as a fraction of the PoV - /// size. - pub max_downward_message_size: u32, - /// The deposit that the sender should provide for opening an HRMP channel. - pub hrmp_sender_deposit: u32, - /// The deposit that the recipient should provide for accepting opening an HRMP channel. - pub hrmp_recipient_deposit: u32, - /// The maximum number of messages allowed in an HRMP channel at once. - pub hrmp_channel_max_capacity: u32, - /// The maximum total size of messages in bytes allowed in an HRMP channel at once. - pub hrmp_channel_max_total_size: u32, - /// The maximum number of inbound HRMP channels a parachain is allowed to accept. - pub hrmp_max_parachain_inbound_channels: u32, - /// The maximum number of inbound HRMP channels a parathread (on-demand parachain) is allowed to accept. - pub hrmp_max_parathread_inbound_channels: u32, - /// The maximum size of a message that could ever be put into an HRMP channel. - /// - /// This parameter affects the upper bound of size of `CandidateCommitments`. - pub hrmp_channel_max_message_size: u32, - /// The maximum number of outbound HRMP channels a parachain is allowed to open. - pub hrmp_max_parachain_outbound_channels: u32, - /// The maximum number of outbound HRMP channels a parathread (on-demand parachain) is allowed to open. - pub hrmp_max_parathread_outbound_channels: u32, - /// The maximum number of outbound HRMP messages can be sent by a candidate. - /// - /// This parameter affects the upper bound of size of `CandidateCommitments`. - pub hrmp_max_message_num_per_candidate: u32, -} -``` +The internal-to-runtime configuration of the parachain host is kept in `struct HostConfiguration`. This is expected to +be altered only by governance procedures or via migrations from the Polkadot-SDK codebase. The latest definition of +`HostConfiguration` can be found in the project repo +[here](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/runtime/parachains/src/configuration.rs). Each +parameter has got a doc comment so for any details please refer to the code. + +Some related parameters in `HostConfiguration` are grouped together so that they can be managed easily. These are: +* `async_backing_params` in `struct AsyncBackingParams` +* `executor_params` in `struct ExecutorParams` +* `approval_voting_params` in `struct ApprovalVotingParams` +* `scheduler_params` in `struct SchedulerParams` + +Check the definitions of these structs for further details. + +### Configuration migrations +Modifying `HostConfiguration` requires a storage migration. These migrations are located in the +[`migrations`](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/runtime/parachains/src/configuration.rs) +subfolder of Polkadot-SDK repo. ## ParaInherentData diff --git a/polkadot/roadmap/phase-1.toml b/polkadot/roadmap/phase-1.toml index 3a5f0d752debee41c51a2124f8e91f3e03dc9d68..9b9374d234bdc735a601ad93f94899ab086c2c06 100644 --- a/polkadot/roadmap/phase-1.toml +++ b/polkadot/roadmap/phase-1.toml @@ -34,7 +34,7 @@ requires = ["two-phase-inclusion"] items = [ { label = "Submit secondary checks to runtime", port = "submitsecondary", requires = ["secondary-checking"] }, { label = "Track all candidates within the slash period as well as their session" }, - { label = "Track reports and attestatations for candidates" }, + { label = "Track reports and attestations for candidates" }, ] [[group]] diff --git a/polkadot/runtime/common/Cargo.toml b/polkadot/runtime/common/Cargo.toml index 2467d123f02f37a63d035e44a0c12441a905a5a8..eae5d4fb2ef90a0acb515b863dfb0013c45bac89 100644 --- a/polkadot/runtime/common/Cargo.toml +++ b/polkadot/runtime/common/Cargo.toml @@ -58,8 +58,6 @@ runtime-parachains = { package = "polkadot-runtime-parachains", path = "../parac slot-range-helper = { path = "slot_range_helper", default-features = false } xcm = { package = "staging-xcm", path = "../../xcm", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../xcm/xcm-executor", default-features = false, optional = true } - -pallet-xcm-benchmarks = { path = "../../xcm/pallet-xcm-benchmarks", default-features = false, optional = true } xcm-builder = { package = "staging-xcm-builder", path = "../../xcm/xcm-builder", default-features = false } [dev-dependencies] @@ -99,7 +97,6 @@ std = [ "pallet-transaction-payment/std", "pallet-treasury/std", "pallet-vesting/std", - "pallet-xcm-benchmarks/std", "parity-scale-codec/std", "primitives/std", "runtime-parachains/std", @@ -137,7 +134,6 @@ runtime-benchmarks = [ "pallet-timestamp/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", - "pallet-xcm-benchmarks/runtime-benchmarks", "primitives/runtime-benchmarks", "runtime-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", diff --git a/polkadot/runtime/common/src/assigned_slots/migration.rs b/polkadot/runtime/common/src/assigned_slots/migration.rs index ba3108c0aa38b9b9a2dabba0559d74ec3c97d0ed..def6bad692a235d3282774b0db3fabdaf33c89b3 100644 --- a/polkadot/runtime/common/src/assigned_slots/migration.rs +++ b/polkadot/runtime/common/src/assigned_slots/migration.rs @@ -29,14 +29,14 @@ pub mod v1 { impl OnRuntimeUpgrade for VersionUncheckedMigrateToV1 { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { - let onchain_version = Pallet::::on_chain_storage_version(); - ensure!(onchain_version < 1, "assigned_slots::MigrateToV1 migration can be deleted"); + let on_chain_version = Pallet::::on_chain_storage_version(); + ensure!(on_chain_version < 1, "assigned_slots::MigrateToV1 migration can be deleted"); Ok(Default::default()) } fn on_runtime_upgrade() -> frame_support::weights::Weight { - let onchain_version = Pallet::::on_chain_storage_version(); - if onchain_version < 1 { + let on_chain_version = Pallet::::on_chain_storage_version(); + if on_chain_version < 1 { const MAX_PERMANENT_SLOTS: u32 = 100; const MAX_TEMPORARY_SLOTS: u32 = 100; @@ -52,8 +52,8 @@ pub mod v1 { #[cfg(feature = "try-runtime")] fn post_upgrade(_state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { - let onchain_version = Pallet::::on_chain_storage_version(); - ensure!(onchain_version == 1, "assigned_slots::MigrateToV1 needs to be run"); + let on_chain_version = Pallet::::on_chain_storage_version(); + ensure!(on_chain_version == 1, "assigned_slots::MigrateToV1 needs to be run"); assert_eq!(>::get(), 100); assert_eq!(>::get(), 100); Ok(()) diff --git a/polkadot/runtime/common/src/assigned_slots/mod.rs b/polkadot/runtime/common/src/assigned_slots/mod.rs index 3419e3497f7d446f7bea02bf8134932c0a0cdbf6..5f1bc73db96397426040cc0b873e5f660577a793 100644 --- a/polkadot/runtime/common/src/assigned_slots/mod.rs +++ b/polkadot/runtime/common/src/assigned_slots/mod.rs @@ -107,7 +107,7 @@ type LeasePeriodOf = <::Leaser as Leaser>>::Le pub mod pallet { use super::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] @@ -680,7 +680,7 @@ mod tests { pub const BlockHashCount: u32 = 250; } - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/polkadot/runtime/common/src/auctions.rs b/polkadot/runtime/common/src/auctions.rs index 46ab673a7a0cb9c685b486c89599c2d9dd324a0d..6914fef99d5264c41d93c8a4d3559b31caf621b0 100644 --- a/polkadot/runtime/common/src/auctions.rs +++ b/polkadot/runtime/common/src/auctions.rs @@ -707,7 +707,7 @@ mod tests { pub const BlockHashCount: u32 = 250; } - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/polkadot/runtime/common/src/claims.rs b/polkadot/runtime/common/src/claims.rs index 86550ea8b4ebf97054ce6713fffcdd0dd2cf628e..49ac4eb894108bd4ab4dc28209deb08c505f42cb 100644 --- a/polkadot/runtime/common/src/claims.rs +++ b/polkadot/runtime/common/src/claims.rs @@ -702,7 +702,6 @@ mod tests { use secp_utils::*; use parity_scale_codec::Encode; - use sp_core::H256; // The testing primitives are very useful for avoiding having to work with signatures // or public keys. `u64` is used as the `AccountId` and no `Signature`s are required. use crate::claims; @@ -715,11 +714,8 @@ mod tests { }; use pallet_balances; use sp_runtime::{ - traits::{BlakeTwo256, Identity, IdentityLookup}, - transaction_validity::TransactionLongevity, - BuildStorage, - DispatchError::BadOrigin, - TokenError, + traits::Identity, transaction_validity::TransactionLongevity, BuildStorage, + DispatchError::BadOrigin, TokenError, }; type Block = frame_system::mocking::MockBlock; @@ -734,34 +730,13 @@ mod tests { } ); - parameter_types! { - pub const BlockHashCount: u32 = 250; - } - - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; type RuntimeEvent = RuntimeEvent; - type BlockHashCount = BlockHashCount; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); type MaxConsumers = frame_support::traits::ConstU32<16>; } diff --git a/polkadot/runtime/common/src/crowdloan/migration.rs b/polkadot/runtime/common/src/crowdloan/migration.rs index 5133c14ada9276a56ae91f38e8a67b4846979866..3afd6b3fbc94b5b30bad162cfcf8236a029b77da 100644 --- a/polkadot/runtime/common/src/crowdloan/migration.rs +++ b/polkadot/runtime/common/src/crowdloan/migration.rs @@ -24,9 +24,9 @@ use frame_support::{ pub struct MigrateToTrackInactiveV2(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToTrackInactiveV2 { fn on_runtime_upgrade() -> Weight { - let onchain_version = Pallet::::on_chain_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); - if onchain_version == 1 { + if on_chain_version == 1 { let mut translated = 0u64; for item in Funds::::iter_values() { let b = diff --git a/polkadot/runtime/common/src/crowdloan/mod.rs b/polkadot/runtime/common/src/crowdloan/mod.rs index 7d1b892dfa7fc6c4057a7da8731e39a9a4b2d3e8..d405278411b7e4aa1f1133fb7356bf6929a370e6 100644 --- a/polkadot/runtime/common/src/crowdloan/mod.rs +++ b/polkadot/runtime/common/src/crowdloan/mod.rs @@ -60,7 +60,7 @@ use frame_support::{ pallet_prelude::{DispatchResult, Weight}, storage::{child, ChildTriePrefixIterator}, traits::{ - Currency, + Currency, Defensive, ExistenceRequirement::{self, AllowDeath, KeepAlive}, Get, ReservableCurrency, }, @@ -180,7 +180,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::{ensure_root, ensure_signed, pallet_prelude::*}; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); #[pallet::pallet] @@ -563,6 +563,7 @@ pub mod pallet { let who = ensure_signed(origin)?; let fund = Self::funds(index).ok_or(Error::::InvalidParaId)?; + let pot = Self::fund_account_id(fund.fund_index); let now = frame_system::Pallet::::block_number(); // Only allow dissolution when the raised funds goes to zero, @@ -576,7 +577,10 @@ pub mod pallet { // can take care of that. debug_assert!(Self::contribution_iterator(fund.fund_index).count().is_zero()); - frame_system::Pallet::::dec_providers(&Self::fund_account_id(fund.fund_index))?; + // Crowdloan over, burn all funds. + let _imba = CurrencyOf::::make_free_balance_be(&pot, Zero::zero()); + let _ = frame_system::Pallet::::dec_providers(&pot).defensive(); + CurrencyOf::::unreserve(&fund.depositor, fund.deposit); Funds::::remove(index); Self::deposit_event(Event::::Dissolved { para_id: index }); @@ -900,7 +904,7 @@ mod tests { type BlockNumber = u64; - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); @@ -1609,6 +1613,7 @@ mod tests { new_test_ext().execute_with(|| { let para = new_para(); let index = NextFundIndex::::get(); + let issuance = Balances::total_issuance(); // Set up a crowdloan assert_ok!(Crowdloan::create(RuntimeOrigin::signed(1), para, 1000, 1, 1, 9, None)); @@ -1629,9 +1634,10 @@ mod tests { // Some funds are left over assert_eq!(Balances::free_balance(&account_id), 10); - // They wil be left in the account at the end + // Remaining funds will be burned assert_ok!(Crowdloan::dissolve(RuntimeOrigin::signed(1), para)); - assert_eq!(Balances::free_balance(&account_id), 10); + assert_eq!(Balances::free_balance(&account_id), 0); + assert_eq!(Balances::total_issuance(), issuance - 10); }); } @@ -1740,6 +1746,41 @@ mod tests { }); } + // Regression test to check that a pot account with just one provider can be dissolved. + #[test] + fn dissolve_provider_refs_total_issuance_works() { + new_test_ext().execute_with(|| { + let para = new_para(); + let issuance = Balances::total_issuance(); + + // Set up a crowdloan + assert_ok!(Crowdloan::create(RuntimeOrigin::signed(1), para, 1000, 1, 1, 9, None)); + assert_ok!(Crowdloan::contribute(RuntimeOrigin::signed(2), para, 100, None)); + assert_ok!(Crowdloan::contribute(RuntimeOrigin::signed(3), para, 50, None)); + + run_to_block(10); + + // We test the historic case where crowdloan accounts only have one provider: + { + let fund = Crowdloan::funds(para).unwrap(); + let pot = Crowdloan::fund_account_id(fund.fund_index); + System::dec_providers(&pot).unwrap(); + assert_eq!(System::providers(&pot), 1); + } + + // All funds are refunded + assert_ok!(Crowdloan::refund(RuntimeOrigin::signed(2), para)); + + // Now that `fund.raised` is zero, it can be dissolved. + assert_ok!(Crowdloan::dissolve(RuntimeOrigin::signed(1), para)); + + assert_eq!(Balances::free_balance(1), 1000); + assert_eq!(Balances::free_balance(2), 2000); + assert_eq!(Balances::free_balance(3), 3000); + assert_eq!(Balances::total_issuance(), issuance); + }); + } + #[test] fn dissolve_works() { new_test_ext().execute_with(|| { diff --git a/polkadot/runtime/common/src/identity_migrator.rs b/polkadot/runtime/common/src/identity_migrator.rs index 0dfb03b06ba32b65941165ec993bc694b84a1389..bf334a63e9588411f57ecc8b6ee2fca72861d7aa 100644 --- a/polkadot/runtime/common/src/identity_migrator.rs +++ b/polkadot/runtime/common/src/identity_migrator.rs @@ -31,7 +31,7 @@ use pallet_identity; use sp_core::Get; #[cfg(feature = "runtime-benchmarks")] -use frame_benchmarking::{account, impl_benchmark_test_suite, v2::*, BenchmarkError}; +use frame_benchmarking::{account, v2::*, BenchmarkError}; pub trait WeightInfo { fn reap_identity(r: u32, s: u32) -> Weight; diff --git a/polkadot/runtime/common/src/impls.rs b/polkadot/runtime/common/src/impls.rs index 4ba85a3c8353565ad17cfb5db29c3d4e1dfa8a44..acf5a701a62d0e7a5b60eee00d85528f5d9214b5 100644 --- a/polkadot/runtime/common/src/impls.rs +++ b/polkadot/runtime/common/src/impls.rs @@ -264,7 +264,7 @@ mod tests { pub const AvailableBlockRatio: Perbill = Perbill::one(); } - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type RuntimeOrigin = RuntimeOrigin; diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 3aa291f0f1f3075670cea40cee25645456057bdb..dfd0d356638c0b5525368bb81b268baada334f22 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -115,7 +115,7 @@ parameter_types! { ); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = BlockWeights; diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 5450c4309e40745f36ae405a8614fe9e8ad2f534..5b2098388d81cbc0028f9a1062b5ec9302a802ec 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -106,7 +106,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] @@ -757,7 +757,7 @@ mod tests { limits::BlockLength::max_with_normal_ratio(4 * 1024 * 1024, NORMAL_RATIO); } - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type RuntimeOrigin = RuntimeOrigin; diff --git a/polkadot/runtime/common/src/purchase.rs b/polkadot/runtime/common/src/purchase.rs index 301f1f21fbce90d311c9b06ae376f2c99931ef27..b90bbb3a7cfb87c8753114de74d12119ad8b6c56 100644 --- a/polkadot/runtime/common/src/purchase.rs +++ b/polkadot/runtime/common/src/purchase.rs @@ -425,8 +425,8 @@ pub mod pallet { impl Pallet { fn verify_signature(who: &T::AccountId, signature: &[u8]) -> Result<(), DispatchError> { // sr25519 always expects a 64 byte signature. - let signature: AnySignature = sr25519::Signature::from_slice(signature) - .ok_or(Error::::InvalidSignature)? + let signature: AnySignature = sr25519::Signature::try_from(signature) + .map_err(|_| Error::::InvalidSignature)? .into(); // In Polkadot, the AccountId is always the same as the 32 byte public key. @@ -512,7 +512,7 @@ mod tests { pub const BlockHashCount: u32 = 250; } - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/polkadot/runtime/common/src/slots/mod.rs b/polkadot/runtime/common/src/slots/mod.rs index 51bd0ba4debe6531da9cca604bb6aa069ad016f3..a54aaa53f8c9255752587f741fac794eaf0d47f5 100644 --- a/polkadot/runtime/common/src/slots/mod.rs +++ b/polkadot/runtime/common/src/slots/mod.rs @@ -530,7 +530,7 @@ mod tests { pub const BlockHashCount: u32 = 250; } - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/polkadot/runtime/common/src/xcm_sender.rs b/polkadot/runtime/common/src/xcm_sender.rs index 7f1100a13619a8c5e1048afd4a34d2d977aa8914..4bbf6a14a40a8a797a1fd184e7387251af7e1ec2 100644 --- a/polkadot/runtime/common/src/xcm_sender.rs +++ b/polkadot/runtime/common/src/xcm_sender.rs @@ -136,10 +136,10 @@ where } } -/// Implementation of `pallet_xcm_benchmarks::EnsureDelivery` which helps to ensure delivery to the +/// Implementation of `xcm_builder::EnsureDelivery` which helps to ensure delivery to the /// `ParaId` parachain (sibling or child). Deposits existential deposit for origin (if needed). /// Deposits estimated fee to the origin account (if needed). -/// Allows to trigger additional logic for specific `ParaId` (e.g. open HRMP channel) (if neeeded). +/// Allows to trigger additional logic for specific `ParaId` (e.g. open HRMP channel) (if needed). #[cfg(feature = "runtime-benchmarks")] pub struct ToParachainDeliveryHelper< XcmConfig, @@ -164,7 +164,7 @@ impl< PriceForDelivery: PriceForMessageDelivery, Parachain: Get, ToParachainHelper: EnsureForParachain, - > pallet_xcm_benchmarks::EnsureDelivery + > xcm_builder::EnsureDelivery for ToParachainDeliveryHelper< XcmConfig, ExistentialDeposit, @@ -175,7 +175,7 @@ impl< { fn ensure_successful_delivery( origin_ref: &Location, - _dest: &Location, + dest: &Location, fee_reason: xcm_executor::traits::FeeReason, ) -> (Option, Option) { use xcm_executor::{ @@ -183,6 +183,15 @@ impl< FeesMode, }; + // check if the destination matches the expected `Parachain`. + if let Some(Parachain(para_id)) = dest.first_interior() { + if ParaId::from(*para_id) != Parachain::get().into() { + return (None, None) + } + } else { + return (None, None) + } + let mut fees_mode = None; if !XcmConfig::FeeManager::is_waived(Some(origin_ref), fee_reason) { // if not waived, we need to set up accounts for paying and receiving fees diff --git a/polkadot/runtime/parachains/Cargo.toml b/polkadot/runtime/parachains/Cargo.toml index 311a62b6c91714cef58a18260f0e9cd35174da41..6e693b83ae138d3b9a0a3cb580485f3560b2d50c 100644 --- a/polkadot/runtime/parachains/Cargo.toml +++ b/polkadot/runtime/parachains/Cargo.toml @@ -15,7 +15,7 @@ bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] } parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive", "max-encoded-len"] } log = { workspace = true } rustc-hex = { version = "2.1.0", default-features = false } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.11.0", default-features = false, features = ["derive"] } serde = { features = ["alloc", "derive"], workspace = true } derive_more = "0.99.17" bitflags = "1.3.2" @@ -69,6 +69,7 @@ sp-tracing = { path = "../../../substrate/primitives/tracing" } sp-crypto-hashing = { path = "../../../substrate/primitives/crypto/hashing" } thousands = "0.2.0" assert_matches = "1" +rstest = "0.18.2" serde_json = { workspace = true, default-features = true } [features] diff --git a/polkadot/runtime/parachains/src/assigner_coretime/mock_helpers.rs b/polkadot/runtime/parachains/src/assigner_coretime/mock_helpers.rs index 71c3f1fa39f7c6c127ac954b8299fb16a323e72d..e2ba0b4f7ea5d5ca2eb49c903afb764229e0491f 100644 --- a/polkadot/runtime/parachains/src/assigner_coretime/mock_helpers.rs +++ b/polkadot/runtime/parachains/src/assigner_coretime/mock_helpers.rs @@ -64,11 +64,12 @@ impl GenesisConfigBuilder { pub(super) fn build(self) -> MockGenesisConfig { let mut genesis = default_genesis_config(); let config = &mut genesis.configuration.config; - config.coretime_cores = self.on_demand_cores; - config.on_demand_base_fee = self.on_demand_base_fee; - config.on_demand_fee_variability = self.on_demand_fee_variability; - config.on_demand_queue_max_size = self.on_demand_max_queue_size; - config.on_demand_target_queue_utilization = self.on_demand_target_queue_utilization; + config.scheduler_params.num_cores = self.on_demand_cores; + config.scheduler_params.on_demand_base_fee = self.on_demand_base_fee; + config.scheduler_params.on_demand_fee_variability = self.on_demand_fee_variability; + config.scheduler_params.on_demand_queue_max_size = self.on_demand_max_queue_size; + config.scheduler_params.on_demand_target_queue_utilization = + self.on_demand_target_queue_utilization; let paras = &mut genesis.paras.paras; for para_id in self.onboarded_on_demand_chains { diff --git a/polkadot/runtime/parachains/src/assigner_coretime/mod.rs b/polkadot/runtime/parachains/src/assigner_coretime/mod.rs index 9da81dc816cabeb7019e44b8f88c0f526582830d..e2da89fadd4800f5de32650178766e1ed85bbd17 100644 --- a/polkadot/runtime/parachains/src/assigner_coretime/mod.rs +++ b/polkadot/runtime/parachains/src/assigner_coretime/mod.rs @@ -30,7 +30,7 @@ mod tests; use crate::{ assigner_on_demand, configuration, paras::AssignCoretime, - scheduler::common::{Assignment, AssignmentProvider, AssignmentProviderConfig}, + scheduler::common::{Assignment, AssignmentProvider}, ParaId, }; @@ -316,14 +316,6 @@ impl AssignmentProvider> for Pallet { } } - fn get_provider_config(_core_idx: CoreIndex) -> AssignmentProviderConfig> { - let config = >::config(); - AssignmentProviderConfig { - max_availability_timeouts: config.on_demand_retries, - ttl: config.on_demand_ttl, - } - } - #[cfg(any(feature = "runtime-benchmarks", test))] fn get_mock_assignment(_: CoreIndex, para_id: primitives::Id) -> Assignment { // Given that we are not tracking anything in `Bulk` assignments, it is safe to always @@ -333,7 +325,7 @@ impl AssignmentProvider> for Pallet { fn session_core_count() -> u32 { let config = >::config(); - config.coretime_cores + config.scheduler_params.num_cores } } @@ -482,8 +474,8 @@ impl AssignCoretime for Pallet { // Add a new core and assign the para to it. let mut config = >::config(); - let core = config.coretime_cores; - config.coretime_cores.saturating_inc(); + let core = config.scheduler_params.num_cores; + config.scheduler_params.num_cores.saturating_inc(); // `assign_coretime` is only called at genesis or by root, so setting the active // config here is fine. diff --git a/polkadot/runtime/parachains/src/assigner_coretime/tests.rs b/polkadot/runtime/parachains/src/assigner_coretime/tests.rs index 998e39670f97d6f6e48d079e4544e547d702509b..5d42a9d0c8eec877292b2d5d6ca318e7df8df5b4 100644 --- a/polkadot/runtime/parachains/src/assigner_coretime/tests.rs +++ b/polkadot/runtime/parachains/src/assigner_coretime/tests.rs @@ -622,7 +622,7 @@ fn assignment_proportions_in_core_state_work() { ); } - // Case 2: Current assignment remaning < step after pop + // Case 2: Current assignment remaining < step after pop { assert_eq!( CoretimeAssigner::pop_assignment_for_core(core_idx), diff --git a/polkadot/runtime/parachains/src/assigner_on_demand/benchmarking.rs b/polkadot/runtime/parachains/src/assigner_on_demand/benchmarking.rs index 5a6060cd2b4eab88867088dee30b6fb1047bdf20..779d6f04e39638d5dfe2552c983de4715ee089d7 100644 --- a/polkadot/runtime/parachains/src/assigner_on_demand/benchmarking.rs +++ b/polkadot/runtime/parachains/src/assigner_on_demand/benchmarking.rs @@ -43,7 +43,7 @@ where { ParasShared::::set_session_index(SESSION_INDEX); let mut config = HostConfiguration::default(); - config.coretime_cores = 1; + config.scheduler_params.num_cores = 1; ConfigurationPallet::::force_set_active_config(config); let mut parachains = ParachainsCache::new(); ParasPallet::::initialize_para_now( @@ -70,11 +70,7 @@ mod benchmarks { let para_id = ParaId::from(111u32); init_parathread::(para_id); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - let order = EnqueuedOrder::new(para_id); - - for _ in 0..s { - Pallet::::add_on_demand_order(order.clone(), QueuePushDirection::Back).unwrap(); - } + Pallet::::populate_queue(para_id, s); #[extrinsic_call] _(RawOrigin::Signed(caller.into()), BalanceOf::::max_value(), para_id) @@ -87,11 +83,8 @@ mod benchmarks { let para_id = ParaId::from(111u32); init_parathread::(para_id); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - let order = EnqueuedOrder::new(para_id); - for _ in 0..s { - Pallet::::add_on_demand_order(order.clone(), QueuePushDirection::Back).unwrap(); - } + Pallet::::populate_queue(para_id, s); #[extrinsic_call] _(RawOrigin::Signed(caller.into()), BalanceOf::::max_value(), para_id) diff --git a/polkadot/runtime/parachains/src/assigner_on_demand/migration.rs b/polkadot/runtime/parachains/src/assigner_on_demand/migration.rs new file mode 100644 index 0000000000000000000000000000000000000000..5071653377d49cb80d9af73375ead355e81ade01 --- /dev/null +++ b/polkadot/runtime/parachains/src/assigner_on_demand/migration.rs @@ -0,0 +1,181 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! A module that is responsible for migration of storage. +use super::*; +use frame_support::{ + migrations::VersionedMigration, pallet_prelude::ValueQuery, storage_alias, + traits::OnRuntimeUpgrade, weights::Weight, +}; + +mod v0 { + use super::*; + use sp_std::collections::vec_deque::VecDeque; + + #[derive(Encode, Decode, TypeInfo, Debug, PartialEq, Clone)] + pub(super) struct EnqueuedOrder { + pub para_id: ParaId, + } + + /// Keeps track of the multiplier used to calculate the current spot price for the on demand + /// assigner. + /// NOTE: Ignoring the `OnEmpty` field for the migration. + #[storage_alias] + pub(super) type SpotTraffic = StorageValue, FixedU128, ValueQuery>; + + /// The order storage entry. Uses a VecDeque to be able to push to the front of the + /// queue from the scheduler on session boundaries. + /// NOTE: Ignoring the `OnEmpty` field for the migration. + #[storage_alias] + pub(super) type OnDemandQueue = + StorageValue, VecDeque, ValueQuery>; +} + +mod v1 { + use super::*; + + use crate::assigner_on_demand::LOG_TARGET; + + /// Migration to V1 + pub struct UncheckedMigrateToV1(sp_std::marker::PhantomData); + impl OnRuntimeUpgrade for UncheckedMigrateToV1 { + fn on_runtime_upgrade() -> Weight { + let mut weight: Weight = Weight::zero(); + + // Migrate the current traffic value + let config = >::config(); + QueueStatus::::mutate(|mut queue_status| { + Pallet::::update_spot_traffic(&config, &mut queue_status); + + let v0_queue = v0::OnDemandQueue::::take(); + // Process the v0 queue into v1. + v0_queue.into_iter().for_each(|enqueued_order| { + // Readding the old orders will use the new systems. + Pallet::::add_on_demand_order( + queue_status, + enqueued_order.para_id, + QueuePushDirection::Back, + ); + }); + }); + + // Remove the old storage. + v0::OnDemandQueue::::kill(); // 1 write + v0::SpotTraffic::::kill(); // 1 write + + // Config read + weight.saturating_accrue(T::DbWeight::get().reads(1)); + // QueueStatus read write (update_spot_traffic) + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + // Kill x 2 + weight.saturating_accrue(T::DbWeight::get().writes(2)); + + log::info!(target: LOG_TARGET, "Migrated on demand assigner storage to v1"); + weight + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + let n: u32 = v0::OnDemandQueue::::get().len() as u32; + + log::info!( + target: LOG_TARGET, + "Number of orders waiting in the queue before: {n}", + ); + + Ok(n.encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + log::info!(target: LOG_TARGET, "Running post_upgrade()"); + + ensure!( + v0::OnDemandQueue::::get().is_empty(), + "OnDemandQueue should be empty after the migration" + ); + + let expected_len = u32::decode(&mut &state[..]).unwrap(); + let queue_status_size = QueueStatus::::get().size(); + ensure!( + expected_len == queue_status_size, + "Number of orders should be the same before and after migration" + ); + + let n_affinity_entries: u32 = + AffinityEntries::::iter().map(|(_index, heap)| heap.len() as u32).sum(); + let n_para_id_affinity: u32 = ParaIdAffinity::::iter() + .map(|(_para_id, affinity)| affinity.count as u32) + .sum(); + ensure!( + n_para_id_affinity == n_affinity_entries, + "Number of affinity entries should be the same as the counts in ParaIdAffinity" + ); + + Ok(()) + } + } +} + +/// Migrate `V0` to `V1` of the storage format. +pub type MigrateV0ToV1 = VersionedMigration< + 0, + 1, + v1::UncheckedMigrateToV1, + Pallet, + ::DbWeight, +>; + +#[cfg(test)] +mod tests { + use super::{v0, v1, OnRuntimeUpgrade, Weight}; + use crate::mock::{new_test_ext, MockGenesisConfig, OnDemandAssigner, Test}; + use primitives::Id as ParaId; + + #[test] + fn migration_to_v1_preserves_queue_ordering() { + new_test_ext(MockGenesisConfig::default()).execute_with(|| { + // Place orders for paraids 1..5 + for i in 1..=5 { + v0::OnDemandQueue::::mutate(|queue| { + queue.push_back(v0::EnqueuedOrder { para_id: ParaId::new(i) }) + }); + } + + // Queue has 5 orders + let old_queue = v0::OnDemandQueue::::get(); + assert_eq!(old_queue.len(), 5); + // New queue has 0 orders + assert_eq!(OnDemandAssigner::get_queue_status().size(), 0); + + // For tests, db weight is zero. + assert_eq!( + as OnRuntimeUpgrade>::on_runtime_upgrade(), + Weight::zero() + ); + + // New queue has 5 orders + assert_eq!(OnDemandAssigner::get_queue_status().size(), 5); + + // Compare each entry from the old queue with the entry in the new queue. + old_queue.iter().zip(OnDemandAssigner::get_free_entries().iter()).for_each( + |(old_enq, new_enq)| { + assert_eq!(old_enq.para_id, new_enq.para_id); + }, + ); + }); + } +} diff --git a/polkadot/runtime/parachains/src/assigner_on_demand/mock_helpers.rs b/polkadot/runtime/parachains/src/assigner_on_demand/mock_helpers.rs index de30330ac84e0a7715799d71d26fb42ce48efff8..f8d1a894f0e4d5c619377ba4c34158c8f6d60597 100644 --- a/polkadot/runtime/parachains/src/assigner_on_demand/mock_helpers.rs +++ b/polkadot/runtime/parachains/src/assigner_on_demand/mock_helpers.rs @@ -63,11 +63,12 @@ impl GenesisConfigBuilder { pub(super) fn build(self) -> MockGenesisConfig { let mut genesis = default_genesis_config(); let config = &mut genesis.configuration.config; - config.coretime_cores = self.on_demand_cores; - config.on_demand_base_fee = self.on_demand_base_fee; - config.on_demand_fee_variability = self.on_demand_fee_variability; - config.on_demand_queue_max_size = self.on_demand_max_queue_size; - config.on_demand_target_queue_utilization = self.on_demand_target_queue_utilization; + config.scheduler_params.num_cores = self.on_demand_cores; + config.scheduler_params.on_demand_base_fee = self.on_demand_base_fee; + config.scheduler_params.on_demand_fee_variability = self.on_demand_fee_variability; + config.scheduler_params.on_demand_queue_max_size = self.on_demand_max_queue_size; + config.scheduler_params.on_demand_target_queue_utilization = + self.on_demand_target_queue_utilization; let paras = &mut genesis.paras.paras; for para_id in self.onboarded_on_demand_chains { diff --git a/polkadot/runtime/parachains/src/assigner_on_demand/mod.rs b/polkadot/runtime/parachains/src/assigner_on_demand/mod.rs index 1b746e88694c9f105db119d351a76e336fd3fdba..26951f34252cb61d4c9a436ad981164003064895 100644 --- a/polkadot/runtime/parachains/src/assigner_on_demand/mod.rs +++ b/polkadot/runtime/parachains/src/assigner_on_demand/mod.rs @@ -16,22 +16,32 @@ //! The parachain on demand assignment module. //! -//! Implements a mechanism for taking in orders for pay as you go (PAYG) or on demand -//! parachain (previously parathreads) assignments. This module is not handled by the -//! initializer but is instead instantiated in the `construct_runtime` macro. +//! Implements a mechanism for taking in orders for on-demand parachain (previously parathreads) +//! assignments. This module is not handled by the initializer but is instead instantiated in the +//! `construct_runtime` macro. //! //! The module currently limits parallel execution of blocks from the same `ParaId` via //! a core affinity mechanism. As long as there exists an affinity for a `CoreIndex` for //! a specific `ParaId`, orders for blockspace for that `ParaId` will only be assigned to -//! that `CoreIndex`. This affinity mechanism can be removed if it can be shown that parallel -//! execution is valid. +//! that `CoreIndex`. +//! +//! NOTE: Once we have elastic scaling implemented we might want to extend this module to support +//! ignoring core affinity up to a certain extend. This should be opt-in though as the parachain +//! needs to support multiple cores in the same block. If we want to enable a single parachain +//! occupying multiple cores in on-demand, we will likely add a separate order type, where the +//! intent can be made explicit. mod benchmarking; +pub mod migration; mod mock_helpers; +extern crate alloc; + #[cfg(test)] mod tests; +use core::mem::take; + use crate::{configuration, paras, scheduler::common::Assignment}; use frame_support::{ @@ -43,13 +53,17 @@ use frame_support::{ }, }; use frame_system::pallet_prelude::*; -use primitives::{CoreIndex, Id as ParaId}; +use primitives::{CoreIndex, Id as ParaId, ON_DEMAND_MAX_QUEUE_MAX_SIZE}; use sp_runtime::{ traits::{One, SaturatedConversion}, FixedPointNumber, FixedPointOperand, FixedU128, Perbill, Saturating, }; -use sp_std::{collections::vec_deque::VecDeque, prelude::*}; +use alloc::collections::BinaryHeap; +use sp_std::{ + cmp::{Ord, Ordering, PartialOrd}, + prelude::*, +}; const LOG_TARGET: &str = "runtime::parachains::assigner-on-demand"; @@ -73,17 +87,116 @@ impl WeightInfo for TestWeightInfo { } } +/// Meta data for full queue. +/// +/// This includes elements with affinity and free entries. +/// +/// The actual queue is implemented via multiple priority queues. One for each core, for entries +/// which currently have a core affinity and one free queue, with entries without any affinity yet. +/// +/// The design aims to have most queue accessess be O(1) or O(log(N)). Absolute worst case is O(N). +/// Importantly this includes all accessess that happen in a single block. Even with 50 cores, the +/// total complexity of all operations in the block should maintain above complexities. In +/// particular O(N) stays O(N), it should never be O(N*cores). +/// +/// More concrete rundown on complexity: +/// +/// - insert: O(1) for placing an order, O(log(N)) for push backs. +/// - pop_assignment_for_core: O(log(N)), O(N) worst case: Can only happen for one core, next core +/// is already less work. +/// - report_processed & push back: If affinity dropped to 0, then O(N) in the worst case. Again +/// this divides per core. +/// +/// Reads still exist, also improved slightly, but worst case we fetch all entries. +#[derive(Encode, Decode, TypeInfo)] +struct QueueStatusType { + /// Last calculated traffic value. + traffic: FixedU128, + /// The next index to use. + next_index: QueueIndex, + /// Smallest index still in use. + /// + /// In case of a completely empty queue (free + affinity queues), `next_index - smallest_index + /// == 0`. + smallest_index: QueueIndex, + /// Indices that have been freed already. + /// + /// But have a hole to `smallest_index`, so we can not yet bump `smallest_index`. This binary + /// heap is roughly bounded in the number of on demand cores: + /// + /// For a single core, elements will always be processed in order. With each core added, a + /// level of out of order execution is added. + freed_indices: BinaryHeap, +} + +impl Default for QueueStatusType { + fn default() -> QueueStatusType { + QueueStatusType { + traffic: FixedU128::default(), + next_index: QueueIndex(0), + smallest_index: QueueIndex(0), + freed_indices: BinaryHeap::new(), + } + } +} + +impl QueueStatusType { + /// How many orders are queued in total? + /// + /// This includes entries which have core affinity. + fn size(&self) -> u32 { + self.next_index + .0 + .overflowing_sub(self.smallest_index.0) + .0 + .saturating_sub(self.freed_indices.len() as u32) + } + + /// Get current next index + /// + /// to use for an element newly pushed to the back of the queue. + fn push_back(&mut self) -> QueueIndex { + let QueueIndex(next_index) = self.next_index; + self.next_index = QueueIndex(next_index.overflowing_add(1).0); + QueueIndex(next_index) + } + + /// Push something to the front of the queue + fn push_front(&mut self) -> QueueIndex { + self.smallest_index = QueueIndex(self.smallest_index.0.overflowing_sub(1).0); + self.smallest_index + } + + /// The given index is no longer part of the queue. + /// + /// This updates `smallest_index` if need be. + fn consume_index(&mut self, removed_index: QueueIndex) { + if removed_index != self.smallest_index { + self.freed_indices.push(removed_index.reverse()); + return + } + let mut index = self.smallest_index.0.overflowing_add(1).0; + // Even more to advance? + while self.freed_indices.peek() == Some(&ReverseQueueIndex(index)) { + index = index.overflowing_add(1).0; + self.freed_indices.pop(); + } + self.smallest_index = QueueIndex(index); + } +} + /// Keeps track of how many assignments a scheduler currently has at a specific `CoreIndex` for a /// specific `ParaId`. #[derive(Encode, Decode, Default, Clone, Copy, TypeInfo)] #[cfg_attr(test, derive(PartialEq, RuntimeDebug))] -pub struct CoreAffinityCount { - core_idx: CoreIndex, +struct CoreAffinityCount { + core_index: CoreIndex, count: u32, } /// An indicator as to which end of the `OnDemandQueue` an assignment will be placed. -pub enum QueuePushDirection { +#[cfg_attr(test, derive(RuntimeDebug))] +enum QueuePushDirection { Back, Front, } @@ -93,9 +206,8 @@ type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; /// Errors that can happen during spot traffic calculation. -#[derive(PartialEq)] -#[cfg_attr(feature = "std", derive(Debug))] -pub enum SpotTrafficCalculationErr { +#[derive(PartialEq, RuntimeDebug)] +enum SpotTrafficCalculationErr { /// The order queue capacity is at 0. QueueCapacityIsZero, /// The queue size is larger than the queue capacity. @@ -104,15 +216,85 @@ pub enum SpotTrafficCalculationErr { Division, } +/// Type used for priority indices. +// NOTE: The `Ord` implementation for this type is unsound in the general case. +// Do not use it for anything but it's intended purpose. +#[derive(Encode, Decode, TypeInfo, Debug, PartialEq, Clone, Eq, Copy)] +struct QueueIndex(u32); + +/// QueueIndex with reverse ordering. +/// +/// Same as `Reverse(QueueIndex)`, but with all the needed traits implemented. +#[derive(Encode, Decode, TypeInfo, Debug, PartialEq, Clone, Eq, Copy)] +struct ReverseQueueIndex(u32); + +impl QueueIndex { + fn reverse(self) -> ReverseQueueIndex { + ReverseQueueIndex(self.0) + } +} + +impl Ord for QueueIndex { + fn cmp(&self, other: &Self) -> Ordering { + let diff = self.0.overflowing_sub(other.0).0; + if diff == 0 { + Ordering::Equal + } else if diff <= ON_DEMAND_MAX_QUEUE_MAX_SIZE { + Ordering::Greater + } else { + Ordering::Less + } + } +} + +impl PartialOrd for QueueIndex { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ReverseQueueIndex { + fn cmp(&self, other: &Self) -> Ordering { + QueueIndex(other.0).cmp(&QueueIndex(self.0)) + } +} +impl PartialOrd for ReverseQueueIndex { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(&other)) + } +} + /// Internal representation of an order after it has been enqueued already. -#[derive(Encode, Decode, TypeInfo, Debug, PartialEq, Clone)] -pub(super) struct EnqueuedOrder { - pub para_id: ParaId, +/// +/// This data structure is provided for a min BinaryHeap (Ord compares in reverse order with regards +/// to its elements) +#[derive(Encode, Decode, TypeInfo, Debug, PartialEq, Clone, Eq)] +struct EnqueuedOrder { + para_id: ParaId, + idx: QueueIndex, } impl EnqueuedOrder { - pub fn new(para_id: ParaId) -> Self { - Self { para_id } + fn new(idx: QueueIndex, para_id: ParaId) -> Self { + Self { idx, para_id } + } +} + +impl PartialOrd for EnqueuedOrder { + fn partial_cmp(&self, other: &Self) -> Option { + match other.idx.partial_cmp(&self.idx) { + Some(Ordering::Equal) => other.para_id.partial_cmp(&self.para_id), + o => o, + } + } +} + +impl Ord for EnqueuedOrder { + fn cmp(&self, other: &Self) -> Ordering { + match other.idx.cmp(&self.idx) { + Ordering::Equal => other.para_id.cmp(&self.para_id), + o => o, + } } } @@ -121,8 +303,11 @@ pub mod pallet { use super::*; + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + #[pallet::pallet] #[pallet::without_storage_info] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); #[pallet::config] @@ -141,36 +326,44 @@ pub mod pallet { type TrafficDefaultValue: Get; } - /// Creates an empty spot traffic value if one isn't present in storage already. + /// Creates an empty queue status for an empty queue with initial traffic value. #[pallet::type_value] - pub fn SpotTrafficOnEmpty() -> FixedU128 { - T::TrafficDefaultValue::get() + pub(super) fn QueueStatusOnEmpty() -> QueueStatusType { + QueueStatusType { traffic: T::TrafficDefaultValue::get(), ..Default::default() } } - /// Creates an empty on demand queue if one isn't present in storage already. #[pallet::type_value] - pub(super) fn OnDemandQueueOnEmpty() -> VecDeque { - VecDeque::new() + pub(super) fn EntriesOnEmpty() -> BinaryHeap { + BinaryHeap::new() } - /// Keeps track of the multiplier used to calculate the current spot price for the on demand - /// assigner. - #[pallet::storage] - pub(super) type SpotTraffic = - StorageValue<_, FixedU128, ValueQuery, SpotTrafficOnEmpty>; - - /// The order storage entry. Uses a VecDeque to be able to push to the front of the - /// queue from the scheduler on session boundaries. - #[pallet::storage] - pub(super) type OnDemandQueue = - StorageValue<_, VecDeque, ValueQuery, OnDemandQueueOnEmpty>; - /// Maps a `ParaId` to `CoreIndex` and keeps track of how many assignments the scheduler has in /// it's lookahead. Keeping track of this affinity prevents parallel execution of the same /// `ParaId` on two or more `CoreIndex`es. #[pallet::storage] pub(super) type ParaIdAffinity = - StorageMap<_, Twox256, ParaId, CoreAffinityCount, OptionQuery>; + StorageMap<_, Twox64Concat, ParaId, CoreAffinityCount, OptionQuery>; + + /// Overall status of queue (both free + affinity entries) + #[pallet::storage] + pub(super) type QueueStatus = + StorageValue<_, QueueStatusType, ValueQuery, QueueStatusOnEmpty>; + + /// Priority queue for all orders which don't yet (or not any more) have any core affinity. + #[pallet::storage] + pub(super) type FreeEntries = + StorageValue<_, BinaryHeap, ValueQuery, EntriesOnEmpty>; + + /// Queue entries that are currently bound to a particular core due to core affinity. + #[pallet::storage] + pub(super) type AffinityEntries = StorageMap< + _, + Twox64Concat, + CoreIndex, + BinaryHeap, + ValueQuery, + EntriesOnEmpty, + >; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -183,9 +376,6 @@ pub mod pallet { #[pallet::error] pub enum Error { - /// The `ParaId` supplied to the `place_order` call is not a valid `ParaThread`, making the - /// call is invalid. - InvalidParaId, /// The order queue is full, `place_order` will not continue. QueueFull, /// The current spot price is higher than the max amount specified in the `place_order` @@ -197,45 +387,14 @@ pub mod pallet { impl Hooks> for Pallet { fn on_initialize(_now: BlockNumberFor) -> Weight { let config = >::config(); - // Calculate spot price multiplier and store it. - let old_traffic = SpotTraffic::::get(); - match Self::calculate_spot_traffic( - old_traffic, - config.on_demand_queue_max_size, - Self::queue_size(), - config.on_demand_target_queue_utilization, - config.on_demand_fee_variability, - ) { - Ok(new_traffic) => { - // Only update storage on change - if new_traffic != old_traffic { - SpotTraffic::::set(new_traffic); - Pallet::::deposit_event(Event::::SpotTrafficSet { - traffic: new_traffic, - }); - return T::DbWeight::get().reads_writes(2, 1) - } - }, - Err(SpotTrafficCalculationErr::QueueCapacityIsZero) => { - log::debug!( - target: LOG_TARGET, - "Error calculating spot traffic: The order queue capacity is at 0." - ); - }, - Err(SpotTrafficCalculationErr::QueueSizeLargerThanCapacity) => { - log::debug!( - target: LOG_TARGET, - "Error calculating spot traffic: The queue size is larger than the queue capacity." - ); - }, - Err(SpotTrafficCalculationErr::Division) => { - log::debug!( - target: LOG_TARGET, - "Error calculating spot traffic: Arithmetic error during division, either division by 0 or over/underflow." - ); - }, - }; - T::DbWeight::get().reads_writes(2, 0) + // We need to update the spot traffic on block initialize in order to account for idle + // blocks. + QueueStatus::::mutate(|queue_status| { + Self::update_spot_traffic(&config, queue_status); + }); + + // 2 reads in config and queuestatus, at maximum 1 write to queuestatus. + T::DbWeight::get().reads_writes(2, 1) } } @@ -258,7 +417,7 @@ pub mod pallet { /// Events: /// - `SpotOrderPlaced` #[pallet::call_index(0)] - #[pallet::weight(::WeightInfo::place_order_allow_death(OnDemandQueue::::get().len() as u32))] + #[pallet::weight(::WeightInfo::place_order_allow_death(QueueStatus::::get().size()))] pub fn place_order_allow_death( origin: OriginFor, max_amount: BalanceOf, @@ -285,7 +444,7 @@ pub mod pallet { /// Events: /// - `SpotOrderPlaced` #[pallet::call_index(1)] - #[pallet::weight(::WeightInfo::place_order_keep_alive(OnDemandQueue::::get().len() as u32))] + #[pallet::weight(::WeightInfo::place_order_keep_alive(QueueStatus::::get().size()))] pub fn place_order_keep_alive( origin: OriginFor, max_amount: BalanceOf, @@ -297,10 +456,78 @@ pub mod pallet { } } +// Internal functions and interface to scheduler/wrapping assignment provider. impl Pallet where BalanceOf: FixedPointOperand, { + /// Take the next queued entry that is available for a given core index. + /// + /// Parameters: + /// - `core_index`: The core index + pub fn pop_assignment_for_core(core_index: CoreIndex) -> Option { + let entry: Result = QueueStatus::::try_mutate(|queue_status| { + AffinityEntries::::try_mutate(core_index, |affinity_entries| { + let free_entry = FreeEntries::::try_mutate(|free_entries| { + let affinity_next = affinity_entries.peek(); + let free_next = free_entries.peek(); + let pick_free = match (affinity_next, free_next) { + (None, _) => true, + (Some(_), None) => false, + (Some(a), Some(f)) => f < a, + }; + if pick_free { + let entry = free_entries.pop().ok_or(())?; + let (mut affinities, free): (BinaryHeap<_>, BinaryHeap<_>) = + take(free_entries) + .into_iter() + .partition(|e| e.para_id == entry.para_id); + affinity_entries.append(&mut affinities); + *free_entries = free; + Ok(entry) + } else { + Err(()) + } + }); + let entry = free_entry.or_else(|()| affinity_entries.pop().ok_or(()))?; + queue_status.consume_index(entry.idx); + Ok(entry) + }) + }); + + let assignment = entry.map(|e| Assignment::Pool { para_id: e.para_id, core_index }).ok()?; + + Pallet::::increase_affinity(assignment.para_id(), core_index); + Some(assignment) + } + + /// Report that the `para_id` & `core_index` combination was processed. + /// + /// This should be called once it is clear that the assignment won't get pushed back anymore. + /// + /// In other words for each `pop_assignment_for_core` a call to this function or + /// `push_back_assignment` must follow, but only one. + pub fn report_processed(para_id: ParaId, core_index: CoreIndex) { + Pallet::::decrease_affinity_update_queue(para_id, core_index); + } + + /// Push an assignment back to the front of the queue. + /// + /// The assignment has not been processed yet. Typically used on session boundaries. + /// + /// NOTE: We are not checking queue size here. So due to push backs it is possible that we + /// exceed the maximum queue size slightly. + /// + /// Parameters: + /// - `para_id`: The para that did not make it. + /// - `core_index`: The core the para was scheduled on. + pub fn push_back_assignment(para_id: ParaId, core_index: CoreIndex) { + Pallet::::decrease_affinity_update_queue(para_id, core_index); + QueueStatus::::mutate(|queue_status| { + Pallet::::add_on_demand_order(queue_status, para_id, QueuePushDirection::Front); + }); + } + /// Helper function for `place_order_*` calls. Used to differentiate between placing orders /// with a keep alive check or to allow the account to be reaped. /// @@ -326,33 +553,62 @@ where ) -> DispatchResult { let config = >::config(); - // Traffic always falls back to 1.0 - let traffic = SpotTraffic::::get(); - - // Calculate spot price - let spot_price: BalanceOf = - traffic.saturating_mul_int(config.on_demand_base_fee.saturated_into::>()); - - // Is the current price higher than `max_amount` - ensure!(spot_price.le(&max_amount), Error::::SpotPriceHigherThanMaxAmount); - - // Charge the sending account the spot price - let _ = T::Currency::withdraw( - &sender, - spot_price, - WithdrawReasons::FEE, - existence_requirement, - )?; - - let order = EnqueuedOrder::new(para_id); - - let res = Pallet::::add_on_demand_order(order, QueuePushDirection::Back); - - if res.is_ok() { - Pallet::::deposit_event(Event::::OnDemandOrderPlaced { para_id, spot_price }); - } + QueueStatus::::mutate(|queue_status| { + Self::update_spot_traffic(&config, queue_status); + let traffic = queue_status.traffic; + + // Calculate spot price + let spot_price: BalanceOf = traffic.saturating_mul_int( + config.scheduler_params.on_demand_base_fee.saturated_into::>(), + ); + + // Is the current price higher than `max_amount` + ensure!(spot_price.le(&max_amount), Error::::SpotPriceHigherThanMaxAmount); + + // Charge the sending account the spot price + let _ = T::Currency::withdraw( + &sender, + spot_price, + WithdrawReasons::FEE, + existence_requirement, + )?; + + ensure!( + queue_status.size() < config.scheduler_params.on_demand_queue_max_size, + Error::::QueueFull + ); + Pallet::::add_on_demand_order(queue_status, para_id, QueuePushDirection::Back); + Ok(()) + }) + } - res + /// Calculate and update spot traffic. + fn update_spot_traffic( + config: &configuration::HostConfiguration>, + queue_status: &mut QueueStatusType, + ) { + let old_traffic = queue_status.traffic; + match Self::calculate_spot_traffic( + old_traffic, + config.scheduler_params.on_demand_queue_max_size, + queue_status.size(), + config.scheduler_params.on_demand_target_queue_utilization, + config.scheduler_params.on_demand_fee_variability, + ) { + Ok(new_traffic) => { + // Only update storage on change + if new_traffic != old_traffic { + queue_status.traffic = new_traffic; + Pallet::::deposit_event(Event::::SpotTrafficSet { traffic: new_traffic }); + } + }, + Err(err) => { + log::debug!( + target: LOG_TARGET, + "Error calculating spot traffic: {:?}", err + ); + }, + }; } /// The spot price multiplier. This is based on the transaction fee calculations defined in: @@ -377,7 +633,7 @@ where /// - `SpotTrafficCalculationErr::QueueCapacityIsZero` /// - `SpotTrafficCalculationErr::QueueSizeLargerThanCapacity` /// - `SpotTrafficCalculationErr::Division` - pub(crate) fn calculate_spot_traffic( + fn calculate_spot_traffic( traffic: FixedU128, queue_capacity: u32, queue_size: u32, @@ -428,173 +684,141 @@ where /// Adds an order to the on demand queue. /// - /// Paramenters: - /// - `order`: The `EnqueuedOrder` to add to the queue. + /// Parameters: /// - `location`: Whether to push this entry to the back or the front of the queue. Pushing an /// entry to the front of the queue is only used when the scheduler wants to push back an /// entry it has already popped. - /// Returns: - /// - The unit type on success. - /// - /// Errors: - /// - `InvalidParaId` - /// - `QueueFull` fn add_on_demand_order( - order: EnqueuedOrder, + queue_status: &mut QueueStatusType, + para_id: ParaId, location: QueuePushDirection, - ) -> Result<(), DispatchError> { - // Only parathreads are valid paraids for on the go parachains. - ensure!(>::is_parathread(order.para_id), Error::::InvalidParaId); - - let config = >::config(); - - OnDemandQueue::::try_mutate(|queue| { - // Abort transaction if queue is too large - ensure!(Self::queue_size() < config.on_demand_queue_max_size, Error::::QueueFull); - match location { - QueuePushDirection::Back => queue.push_back(order), - QueuePushDirection::Front => queue.push_front(order), - }; - Ok(()) - }) + ) { + let idx = match location { + QueuePushDirection::Back => queue_status.push_back(), + QueuePushDirection::Front => queue_status.push_front(), + }; + + let affinity = ParaIdAffinity::::get(para_id); + let order = EnqueuedOrder::new(idx, para_id); + #[cfg(test)] + log::debug!(target: LOG_TARGET, "add_on_demand_order, order: {:?}, affinity: {:?}, direction: {:?}", order, affinity, location); + + match affinity { + None => FreeEntries::::mutate(|entries| entries.push(order)), + Some(affinity) => + AffinityEntries::::mutate(affinity.core_index, |entries| entries.push(order)), + } } - /// Get the size of the on demand queue. + /// Decrease core affinity for para and update queue /// - /// Returns: - /// - The size of the on demand queue. - fn queue_size() -> u32 { - let config = >::config(); - match OnDemandQueue::::get().len().try_into() { - Ok(size) => return size, - Err(_) => { - log::debug!( - target: LOG_TARGET, - "Failed to fetch the on demand queue size, returning the max size." - ); - return config.on_demand_queue_max_size - }, + /// if affinity dropped to 0, moving entries back to `FreeEntries`. + fn decrease_affinity_update_queue(para_id: ParaId, core_index: CoreIndex) { + let affinity = Pallet::::decrease_affinity(para_id, core_index); + #[cfg(not(test))] + debug_assert_ne!( + affinity, None, + "Decreased affinity for a para that has not been served on a core?" + ); + if affinity != Some(0) { + return } - } - - /// Getter for the order queue. - #[cfg(test)] - fn get_queue() -> VecDeque { - OnDemandQueue::::get() - } - - /// Getter for the affinity tracker. - pub fn get_affinity_map(para_id: ParaId) -> Option { - ParaIdAffinity::::get(para_id) + // No affinity more for entries on this core, free any entries: + // + // This is necessary to ensure them being served as the core might no longer exist at all. + AffinityEntries::::mutate(core_index, |affinity_entries| { + FreeEntries::::mutate(|free_entries| { + let (mut freed, affinities): (BinaryHeap<_>, BinaryHeap<_>) = + take(affinity_entries).into_iter().partition(|e| e.para_id == para_id); + free_entries.append(&mut freed); + *affinity_entries = affinities; + }) + }); } /// Decreases the affinity of a `ParaId` to a specified `CoreIndex`. - /// Subtracts from the count of the `CoreAffinityCount` if an entry is found and the core_idx + /// + /// Subtracts from the count of the `CoreAffinityCount` if an entry is found and the core_index /// matches. When the count reaches 0, the entry is removed. /// A non-existant entry is a no-op. - fn decrease_affinity(para_id: ParaId, core_idx: CoreIndex) { + /// + /// Returns: The new affinity of the para on that core. `None` if there is no affinity on this + /// core. + fn decrease_affinity(para_id: ParaId, core_index: CoreIndex) -> Option { ParaIdAffinity::::mutate(para_id, |maybe_affinity| { - if let Some(affinity) = maybe_affinity { - if affinity.core_idx == core_idx { - let new_count = affinity.count.saturating_sub(1); - if new_count > 0 { - *maybe_affinity = Some(CoreAffinityCount { core_idx, count: new_count }); - } else { - *maybe_affinity = None; - } + let affinity = maybe_affinity.as_mut()?; + if affinity.core_index == core_index { + let new_count = affinity.count.saturating_sub(1); + if new_count > 0 { + *maybe_affinity = Some(CoreAffinityCount { core_index, count: new_count }); + } else { + *maybe_affinity = None; } + return Some(new_count) + } else { + None } - }); + }) } /// Increases the affinity of a `ParaId` to a specified `CoreIndex`. - /// Adds to the count of the `CoreAffinityCount` if an entry is found and the core_idx matches. - /// A non-existant entry will be initialized with a count of 1 and uses the supplied + /// Adds to the count of the `CoreAffinityCount` if an entry is found and the core_index + /// matches. A non-existent entry will be initialized with a count of 1 and uses the supplied /// `CoreIndex`. - fn increase_affinity(para_id: ParaId, core_idx: CoreIndex) { + fn increase_affinity(para_id: ParaId, core_index: CoreIndex) { ParaIdAffinity::::mutate(para_id, |maybe_affinity| match maybe_affinity { Some(affinity) => - if affinity.core_idx == core_idx { + if affinity.core_index == core_index { *maybe_affinity = Some(CoreAffinityCount { - core_idx, + core_index, count: affinity.count.saturating_add(1), }); }, None => { - *maybe_affinity = Some(CoreAffinityCount { core_idx, count: 1 }); + *maybe_affinity = Some(CoreAffinityCount { core_index, count: 1 }); }, }) } -} -impl Pallet { - /// Take the next queued entry that is available for a given core index. - /// Invalidates and removes orders with a `para_id` that is not `ParaLifecycle::Parathread` - /// but only in [0..P] range slice of the order queue, where P is the element that is - /// removed from the order queue. - /// - /// Parameters: - /// - `core_idx`: The core index - pub fn pop_assignment_for_core(core_idx: CoreIndex) -> Option { - let mut queue: VecDeque = OnDemandQueue::::get(); - - let mut invalidated_para_id_indexes: Vec = vec![]; - - // Get the position of the next `ParaId`. Select either a valid `ParaId` that has an - // affinity to the same `CoreIndex` as the scheduler asks for or a valid `ParaId` with no - // affinity at all. - let pos = queue.iter().enumerate().position(|(index, assignment)| { - if >::is_parathread(assignment.para_id) { - match ParaIdAffinity::::get(&assignment.para_id) { - Some(affinity) => return affinity.core_idx == core_idx, - None => return true, - } - } - // Record no longer valid para_ids. - invalidated_para_id_indexes.push(index); - return false - }); + /// Getter for the affinity tracker. + #[cfg(test)] + fn get_affinity_map(para_id: ParaId) -> Option { + ParaIdAffinity::::get(para_id) + } - // Collect the popped value. - let popped = pos.and_then(|p: usize| { - if let Some(assignment) = queue.remove(p) { - Pallet::::increase_affinity(assignment.para_id, core_idx); - return Some(assignment) - }; - None - }); + /// Getter for the affinity entries. + #[cfg(test)] + fn get_affinity_entries(core_index: CoreIndex) -> BinaryHeap { + AffinityEntries::::get(core_index) + } - // Only remove the invalid indexes *after* using the index. - // Removed in reverse order so that the indexes don't shift. - invalidated_para_id_indexes.iter().rev().for_each(|idx| { - queue.remove(*idx); - }); + /// Getter for the free entries. + #[cfg(test)] + fn get_free_entries() -> BinaryHeap { + FreeEntries::::get() + } - // Write changes to storage. - OnDemandQueue::::set(queue); + #[cfg(feature = "runtime-benchmarks")] + pub fn populate_queue(para_id: ParaId, num: u32) { + QueueStatus::::mutate(|queue_status| { + for _ in 0..num { + Pallet::::add_on_demand_order(queue_status, para_id, QueuePushDirection::Back); + } + }); + } - popped.map(|p| Assignment::Pool { para_id: p.para_id, core_index: core_idx }) + #[cfg(test)] + fn set_queue_status(new_status: QueueStatusType) { + QueueStatus::::set(new_status); } - /// Report that the `para_id` & `core_index` combination was processed. - pub fn report_processed(para_id: ParaId, core_index: CoreIndex) { - Pallet::::decrease_affinity(para_id, core_index) + #[cfg(test)] + fn get_queue_status() -> QueueStatusType { + QueueStatus::::get() } - /// Push an assignment back to the front of the queue. - /// - /// The assignment has not been processed yet. Typically used on session boundaries. - /// Parameters: - /// - `assignment`: The on demand assignment. - pub fn push_back_assignment(para_id: ParaId, core_index: CoreIndex) { - Pallet::::decrease_affinity(para_id, core_index); - // Skip the queue on push backs from scheduler - match Pallet::::add_on_demand_order( - EnqueuedOrder::new(para_id), - QueuePushDirection::Front, - ) { - Ok(_) => {}, - Err(_) => {}, - } + #[cfg(test)] + fn get_traffic_default_value() -> FixedU128 { + ::TrafficDefaultValue::get() } } diff --git a/polkadot/runtime/parachains/src/assigner_on_demand/tests.rs b/polkadot/runtime/parachains/src/assigner_on_demand/tests.rs index 8404700780c84e493d6436c5f3174f814c1082ef..982efe77b939cf64d355e1111606c3856c4a0aea 100644 --- a/polkadot/runtime/parachains/src/assigner_on_demand/tests.rs +++ b/polkadot/runtime/parachains/src/assigner_on_demand/tests.rs @@ -73,11 +73,24 @@ fn run_to_block( Paras::initializer_initialize(b + 1); Scheduler::initializer_initialize(b + 1); + // We need to update the spot traffic on every block. + OnDemandAssigner::on_initialize(b + 1); + // In the real runtime this is expected to be called by the `InclusionInherent` pallet. Scheduler::free_cores_and_fill_claimqueue(BTreeMap::new(), b + 1); } } +fn place_order(para_id: ParaId) { + let alice = 100u64; + let amt = 10_000_000u128; + + Balances::make_free_balance_be(&alice, amt); + + run_to_block(101, |n| if n == 101 { Some(Default::default()) } else { None }); + OnDemandAssigner::place_order_allow_death(RuntimeOrigin::signed(alice), amt, para_id).unwrap() +} + #[test] fn spot_traffic_capacity_zero_returns_none() { match OnDemandAssigner::calculate_spot_traffic( @@ -201,6 +214,42 @@ fn spot_traffic_decreases_over_time() { assert_eq!(traffic, FixedU128::from_inner(3_125_000_000_000_000_000u128)) } +#[test] +fn spot_traffic_decreases_between_idle_blocks() { + // Testing spot traffic assumptions, but using the mock runtime and default on demand + // configuration values. Ensuring that blocks with no on demand activity (idle) + // decrease traffic. + + let para_id = ParaId::from(111); + + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + // Initialize the parathread and wait for it to be ready. + schedule_blank_para(para_id, ParaKind::Parathread); + assert!(!Paras::is_parathread(para_id)); + run_to_block(100, |n| if n == 100 { Some(Default::default()) } else { None }); + assert!(Paras::is_parathread(para_id)); + + // Set the spot traffic to a large number + OnDemandAssigner::set_queue_status(QueueStatusType { + traffic: FixedU128::from_u32(10), + ..Default::default() + }); + + assert_eq!(OnDemandAssigner::get_queue_status().traffic, FixedU128::from_u32(10)); + + // Run to block 101 and ensure that the traffic decreases. + run_to_block(101, |n| if n == 100 { Some(Default::default()) } else { None }); + assert!(OnDemandAssigner::get_queue_status().traffic < FixedU128::from_u32(10)); + + // Run to block 102 and observe that we've hit the default traffic value. + run_to_block(102, |n| if n == 100 { Some(Default::default()) } else { None }); + assert_eq!( + OnDemandAssigner::get_queue_status().traffic, + OnDemandAssigner::get_traffic_default_value() + ); + }) +} + #[test] fn place_order_works() { let alice = 1u64; @@ -278,74 +327,6 @@ fn place_order_keep_alive_keeps_alive() { }); } -#[test] -fn add_on_demand_order_works() { - let para_a = ParaId::from(111); - let order = EnqueuedOrder::new(para_a); - - let mut genesis = GenesisConfigBuilder::default(); - genesis.on_demand_max_queue_size = 1; - new_test_ext(genesis.build()).execute_with(|| { - // Initialize the parathread and wait for it to be ready. - schedule_blank_para(para_a, ParaKind::Parathread); - - // `para_a` is not onboarded as a parathread yet. - assert_noop!( - OnDemandAssigner::add_on_demand_order(order.clone(), QueuePushDirection::Back), - Error::::InvalidParaId - ); - - assert!(!Paras::is_parathread(para_a)); - run_to_block(100, |n| if n == 100 { Some(Default::default()) } else { None }); - assert!(Paras::is_parathread(para_a)); - - // `para_a` is now onboarded as a valid parathread. - assert_ok!(OnDemandAssigner::add_on_demand_order(order.clone(), QueuePushDirection::Back)); - - // Max queue size is 1, queue should be full. - assert_noop!( - OnDemandAssigner::add_on_demand_order(order, QueuePushDirection::Back), - Error::::QueueFull - ); - }); -} - -#[test] -fn spotqueue_push_directions() { - new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { - let para_a = ParaId::from(111); - let para_b = ParaId::from(222); - let para_c = ParaId::from(333); - - schedule_blank_para(para_a, ParaKind::Parathread); - schedule_blank_para(para_b, ParaKind::Parathread); - schedule_blank_para(para_c, ParaKind::Parathread); - - run_to_block(11, |n| if n == 11 { Some(Default::default()) } else { None }); - - let order_a = EnqueuedOrder::new(para_a); - let order_b = EnqueuedOrder::new(para_b); - let order_c = EnqueuedOrder::new(para_c); - - assert_ok!(OnDemandAssigner::add_on_demand_order( - order_a.clone(), - QueuePushDirection::Front - )); - assert_ok!(OnDemandAssigner::add_on_demand_order( - order_b.clone(), - QueuePushDirection::Front - )); - - assert_ok!(OnDemandAssigner::add_on_demand_order( - order_c.clone(), - QueuePushDirection::Back - )); - - assert_eq!(OnDemandAssigner::queue_size(), 3); - assert_eq!(OnDemandAssigner::get_queue(), VecDeque::from(vec![order_b, order_a, order_c])) - }); -} - #[test] fn pop_assignment_for_core_works() { new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { @@ -356,51 +337,32 @@ fn pop_assignment_for_core_works() { run_to_block(11, |n| if n == 11 { Some(Default::default()) } else { None }); - let order_a = EnqueuedOrder::new(para_a); - let order_b = EnqueuedOrder::new(para_b); - let assignment_a = Assignment::Pool { para_id: para_a, core_index: CoreIndex(0) }; - let assignment_b = Assignment::Pool { para_id: para_b, core_index: CoreIndex(1) }; - // Pop should return none with empty queue assert_eq!(OnDemandAssigner::pop_assignment_for_core(CoreIndex(0)), None); // Add enough assignments to the order queue. for _ in 0..2 { - OnDemandAssigner::add_on_demand_order(order_a.clone(), QueuePushDirection::Back) - .expect("Invalid paraid or queue full"); - - OnDemandAssigner::add_on_demand_order(order_b.clone(), QueuePushDirection::Back) - .expect("Invalid paraid or queue full"); - } - - // Queue should contain orders a, b, a, b - { - let queue: Vec = OnDemandQueue::::get().into_iter().collect(); - assert_eq!( - queue, - vec![order_a.clone(), order_b.clone(), order_a.clone(), order_b.clone()] - ); + place_order(para_a); + place_order(para_b); } // Popped assignments should be for the correct paras and cores assert_eq!( - OnDemandAssigner::pop_assignment_for_core(CoreIndex(0)), - Some(assignment_a.clone()) + OnDemandAssigner::pop_assignment_for_core(CoreIndex(0)).map(|a| a.para_id()), + Some(para_a) ); assert_eq!( - OnDemandAssigner::pop_assignment_for_core(CoreIndex(1)), - Some(assignment_b.clone()) + OnDemandAssigner::pop_assignment_for_core(CoreIndex(1)).map(|a| a.para_id()), + Some(para_b) ); assert_eq!( - OnDemandAssigner::pop_assignment_for_core(CoreIndex(0)), - Some(assignment_a.clone()) + OnDemandAssigner::pop_assignment_for_core(CoreIndex(0)).map(|a| a.para_id()), + Some(para_a) + ); + assert_eq!( + OnDemandAssigner::pop_assignment_for_core(CoreIndex(1)).map(|a| a.para_id()), + Some(para_b) ); - - // Queue should contain one left over order - { - let queue: Vec = OnDemandQueue::::get().into_iter().collect(); - assert_eq!(queue, vec![order_b.clone(),]); - } }); } @@ -414,28 +376,19 @@ fn push_back_assignment_works() { run_to_block(11, |n| if n == 11 { Some(Default::default()) } else { None }); - let order_a = EnqueuedOrder::new(para_a); - let order_b = EnqueuedOrder::new(para_b); - // Add enough assignments to the order queue. - OnDemandAssigner::add_on_demand_order(order_a.clone(), QueuePushDirection::Back) - .expect("Invalid paraid or queue full"); - - OnDemandAssigner::add_on_demand_order(order_b.clone(), QueuePushDirection::Back) - .expect("Invalid paraid or queue full"); + place_order(para_a); + place_order(para_b); // Pop order a - OnDemandAssigner::pop_assignment_for_core(CoreIndex(0)); + assert_eq!( + OnDemandAssigner::pop_assignment_for_core(CoreIndex(0)).unwrap().para_id(), + para_a + ); // Para a should have affinity for core 0 assert_eq!(OnDemandAssigner::get_affinity_map(para_a).unwrap().count, 1); - assert_eq!(OnDemandAssigner::get_affinity_map(para_a).unwrap().core_idx, CoreIndex(0)); - - // Queue should still contain order b - { - let queue: Vec = OnDemandQueue::::get().into_iter().collect(); - assert_eq!(queue, vec![order_b.clone()]); - } + assert_eq!(OnDemandAssigner::get_affinity_map(para_a).unwrap().core_index, CoreIndex(0)); // Push back order a OnDemandAssigner::push_back_assignment(para_a, CoreIndex(0)); @@ -444,10 +397,82 @@ fn push_back_assignment_works() { assert_eq!(OnDemandAssigner::get_affinity_map(para_a).is_none(), true); // Queue should contain orders a, b. A in front of b. - { - let queue: Vec = OnDemandQueue::::get().into_iter().collect(); - assert_eq!(queue, vec![order_a.clone(), order_b.clone()]); + assert_eq!( + OnDemandAssigner::pop_assignment_for_core(CoreIndex(0)).unwrap().para_id(), + para_a + ); + assert_eq!( + OnDemandAssigner::pop_assignment_for_core(CoreIndex(0)).unwrap().para_id(), + para_b + ); + }); +} + +#[test] +fn affinity_prohibits_parallel_scheduling() { + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + let para_a = ParaId::from(111); + let para_b = ParaId::from(222); + + schedule_blank_para(para_a, ParaKind::Parathread); + schedule_blank_para(para_b, ParaKind::Parathread); + + run_to_block(11, |n| if n == 11 { Some(Default::default()) } else { None }); + + // There should be no affinity before starting. + assert!(OnDemandAssigner::get_affinity_map(para_a).is_none()); + assert!(OnDemandAssigner::get_affinity_map(para_b).is_none()); + + // Add 2 assignments for para_a for every para_b. + place_order(para_a); + place_order(para_a); + place_order(para_b); + + // Approximate having 1 core. + for _ in 0..3 { + assert!(OnDemandAssigner::pop_assignment_for_core(CoreIndex(0)).is_some()); } + assert!(OnDemandAssigner::pop_assignment_for_core(CoreIndex(0)).is_none()); + + // Affinity on one core is meaningless. + assert_eq!(OnDemandAssigner::get_affinity_map(para_a).unwrap().count, 2); + assert_eq!(OnDemandAssigner::get_affinity_map(para_b).unwrap().count, 1); + assert_eq!( + OnDemandAssigner::get_affinity_map(para_a).unwrap().core_index, + OnDemandAssigner::get_affinity_map(para_b).unwrap().core_index, + ); + + // Clear affinity + OnDemandAssigner::report_processed(para_a, 0.into()); + OnDemandAssigner::report_processed(para_a, 0.into()); + OnDemandAssigner::report_processed(para_b, 0.into()); + + // Add 2 assignments for para_a for every para_b. + place_order(para_a); + place_order(para_a); + place_order(para_b); + + // Approximate having 3 cores. CoreIndex 2 should be unable to obtain an assignment + for _ in 0..3 { + OnDemandAssigner::pop_assignment_for_core(CoreIndex(0)); + OnDemandAssigner::pop_assignment_for_core(CoreIndex(1)); + assert!(OnDemandAssigner::pop_assignment_for_core(CoreIndex(2)).is_none()); + } + + // Affinity should be the same as before, but on different cores. + assert_eq!(OnDemandAssigner::get_affinity_map(para_a).unwrap().count, 2); + assert_eq!(OnDemandAssigner::get_affinity_map(para_b).unwrap().count, 1); + assert_eq!(OnDemandAssigner::get_affinity_map(para_a).unwrap().core_index, CoreIndex(0)); + assert_eq!(OnDemandAssigner::get_affinity_map(para_b).unwrap().core_index, CoreIndex(1)); + + // Clear affinity + OnDemandAssigner::report_processed(para_a, CoreIndex(0)); + OnDemandAssigner::report_processed(para_a, CoreIndex(0)); + OnDemandAssigner::report_processed(para_b, CoreIndex(1)); + + // There should be no affinity after clearing. + assert!(OnDemandAssigner::get_affinity_map(para_a).is_none()); + assert!(OnDemandAssigner::get_affinity_map(para_b).is_none()); }); } @@ -458,7 +483,6 @@ fn affinity_changes_work() { let core_index = CoreIndex(0); schedule_blank_para(para_a, ParaKind::Parathread); - let order_a = EnqueuedOrder::new(para_a); run_to_block(11, |n| if n == 11 { Some(Default::default()) } else { None }); // There should be no affinity before starting. @@ -466,8 +490,7 @@ fn affinity_changes_work() { // Add enough assignments to the order queue. for _ in 0..10 { - OnDemandAssigner::add_on_demand_order(order_a.clone(), QueuePushDirection::Front) - .expect("Invalid paraid or queue full"); + place_order(para_a); } // There should be no affinity before the scheduler pops. @@ -483,7 +506,6 @@ fn affinity_changes_work() { // Affinity count is 1 after popping with a previous para. assert_eq!(OnDemandAssigner::get_affinity_map(para_a).unwrap().count, 1); - assert_eq!(OnDemandAssigner::queue_size(), 8); for _ in 0..3 { OnDemandAssigner::pop_assignment_for_core(core_index); @@ -491,147 +513,197 @@ fn affinity_changes_work() { // Affinity count is 4 after popping 3 times without a previous para. assert_eq!(OnDemandAssigner::get_affinity_map(para_a).unwrap().count, 4); - assert_eq!(OnDemandAssigner::queue_size(), 5); for _ in 0..5 { OnDemandAssigner::report_processed(para_a, 0.into()); - OnDemandAssigner::pop_assignment_for_core(core_index); + assert!(OnDemandAssigner::pop_assignment_for_core(core_index).is_some()); } // Affinity count should still be 4 but queue should be empty. + assert!(OnDemandAssigner::pop_assignment_for_core(core_index).is_none()); assert_eq!(OnDemandAssigner::get_affinity_map(para_a).unwrap().count, 4); - assert_eq!(OnDemandAssigner::queue_size(), 0); // Pop 4 times and get to exactly 0 (None) affinity. for _ in 0..4 { OnDemandAssigner::report_processed(para_a, 0.into()); - OnDemandAssigner::pop_assignment_for_core(core_index); + assert!(OnDemandAssigner::pop_assignment_for_core(core_index).is_none()); } assert!(OnDemandAssigner::get_affinity_map(para_a).is_none()); // Decreasing affinity beyond 0 should still be None. OnDemandAssigner::report_processed(para_a, 0.into()); - OnDemandAssigner::pop_assignment_for_core(core_index); + assert!(OnDemandAssigner::pop_assignment_for_core(core_index).is_none()); assert!(OnDemandAssigner::get_affinity_map(para_a).is_none()); }); } #[test] -fn affinity_prohibits_parallel_scheduling() { - new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { - let para_a = ParaId::from(111); - let para_b = ParaId::from(222); +fn new_affinity_for_a_core_must_come_from_free_entries() { + // If affinity count for a core was zero before, and is 1 now, then the entry + // must have come from free_entries. + let parachains = + vec![ParaId::from(111), ParaId::from(222), ParaId::from(333), ParaId::from(444)]; + let core_indices = vec![CoreIndex(0), CoreIndex(1), CoreIndex(2), CoreIndex(3)]; - schedule_blank_para(para_a, ParaKind::Parathread); - schedule_blank_para(para_b, ParaKind::Parathread); + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + parachains.iter().for_each(|chain| { + schedule_blank_para(*chain, ParaKind::Parathread); + }); run_to_block(11, |n| if n == 11 { Some(Default::default()) } else { None }); - let order_a = EnqueuedOrder::new(para_a); - let order_b = EnqueuedOrder::new(para_b); - - // There should be no affinity before starting. - assert!(OnDemandAssigner::get_affinity_map(para_a).is_none()); - assert!(OnDemandAssigner::get_affinity_map(para_b).is_none()); - - // Add 2 assignments for para_a for every para_b. - OnDemandAssigner::add_on_demand_order(order_a.clone(), QueuePushDirection::Back) - .expect("Invalid paraid or queue full"); - - OnDemandAssigner::add_on_demand_order(order_a.clone(), QueuePushDirection::Back) - .expect("Invalid paraid or queue full"); - - OnDemandAssigner::add_on_demand_order(order_b.clone(), QueuePushDirection::Back) - .expect("Invalid paraid or queue full"); - - assert_eq!(OnDemandAssigner::queue_size(), 3); + // Place orders for all chains. + parachains.iter().for_each(|chain| { + place_order(*chain); + }); + + // There are 4 entries in free_entries. + let start_free_entries = OnDemandAssigner::get_free_entries().len(); + assert_eq!(start_free_entries, 4); + + // Pop assignments on all cores. + core_indices.iter().enumerate().for_each(|(n, core_index)| { + // There is no affinity on the core prior to popping. + assert!(OnDemandAssigner::get_affinity_entries(*core_index).is_empty()); + + // There's always an order to be popped for each core. + let free_entries = OnDemandAssigner::get_free_entries(); + let next_order = free_entries.peek(); + + // There is no affinity on the paraid prior to popping. + assert!(OnDemandAssigner::get_affinity_map(next_order.unwrap().para_id).is_none()); + + match OnDemandAssigner::pop_assignment_for_core(*core_index) { + Some(assignment) => { + // The popped assignment came from free entries. + assert_eq!( + start_free_entries - 1 - n, + OnDemandAssigner::get_free_entries().len() + ); + // The popped assignment has the same para id as the next order. + assert_eq!(assignment.para_id(), next_order.unwrap().para_id); + }, + None => panic!("Should not happen"), + } + }); - // Approximate having 1 core. - for _ in 0..3 { - OnDemandAssigner::pop_assignment_for_core(CoreIndex(0)); - } + // All entries have been removed from free_entries. + assert!(OnDemandAssigner::get_free_entries().is_empty()); - // Affinity on one core is meaningless. - assert_eq!(OnDemandAssigner::get_affinity_map(para_a).unwrap().count, 2); - assert_eq!(OnDemandAssigner::get_affinity_map(para_b).unwrap().count, 1); - assert_eq!( - OnDemandAssigner::get_affinity_map(para_a).unwrap().core_idx, - OnDemandAssigner::get_affinity_map(para_b).unwrap().core_idx - ); - - // Clear affinity - OnDemandAssigner::report_processed(para_a, 0.into()); - OnDemandAssigner::report_processed(para_a, 0.into()); - OnDemandAssigner::report_processed(para_b, 0.into()); - - // Add 2 assignments for para_a for every para_b. - OnDemandAssigner::add_on_demand_order(order_a.clone(), QueuePushDirection::Back) - .expect("Invalid paraid or queue full"); + // All chains have an affinity count of 1. + parachains.iter().for_each(|chain| { + assert_eq!(OnDemandAssigner::get_affinity_map(*chain).unwrap().count, 1); + }); + }); +} - OnDemandAssigner::add_on_demand_order(order_a.clone(), QueuePushDirection::Back) - .expect("Invalid paraid or queue full"); +#[test] +#[should_panic] +fn queue_index_ordering_is_unsound_over_max_size() { + // NOTE: Unsoundness proof. If the number goes sufficiently over the max_queue_max_size + // the overflow will cause an opposite comparison to what would be expected. + let max_num = u32::MAX - ON_DEMAND_MAX_QUEUE_MAX_SIZE; + // 0 < some large number. + assert_eq!(QueueIndex(0).cmp(&QueueIndex(max_num + 1)), Ordering::Less); +} - OnDemandAssigner::add_on_demand_order(order_b.clone(), QueuePushDirection::Back) - .expect("Invalid paraid or queue full"); +#[test] +fn queue_index_ordering_works() { + // The largest accepted queue size. + let max_num = ON_DEMAND_MAX_QUEUE_MAX_SIZE; + + // 0 == 0 + assert_eq!(QueueIndex(0).cmp(&QueueIndex(0)), Ordering::Equal); + // 0 < 1 + assert_eq!(QueueIndex(0).cmp(&QueueIndex(1)), Ordering::Less); + // 1 > 0 + assert_eq!(QueueIndex(1).cmp(&QueueIndex(0)), Ordering::Greater); + // 0 < max_num + assert_eq!(QueueIndex(0).cmp(&QueueIndex(max_num)), Ordering::Less); + // 0 > max_num + 1 + assert_eq!(QueueIndex(0).cmp(&QueueIndex(max_num + 1)), Ordering::Less); + + // Ordering within the bounds of ON_DEMAND_MAX_QUEUE_MAX_SIZE works. + let mut v = vec![3, 6, 2, 1, 5, 4]; + v.sort_by_key(|&num| QueueIndex(num)); + assert_eq!(v, vec![1, 2, 3, 4, 5, 6]); + + v = vec![max_num, 4, 5, 1, 6]; + v.sort_by_key(|&num| QueueIndex(num)); + assert_eq!(v, vec![1, 4, 5, 6, max_num]); + + // Ordering with an element outside of the bounds of the max size also works. + v = vec![max_num + 2, 0, 6, 2, 1, 5, 4]; + v.sort_by_key(|&num| QueueIndex(num)); + assert_eq!(v, vec![0, 1, 2, 4, 5, 6, max_num + 2]); + + // Numbers way above the max size will overflow + v = vec![u32::MAX - 1, u32::MAX, 6, 2, 1, 5, 4]; + v.sort_by_key(|&num| QueueIndex(num)); + assert_eq!(v, vec![u32::MAX - 1, u32::MAX, 1, 2, 4, 5, 6]); +} - // Approximate having 3 cores. CoreIndex 2 should be unable to obtain an assignment - for _ in 0..3 { - OnDemandAssigner::pop_assignment_for_core(CoreIndex(0)); - OnDemandAssigner::pop_assignment_for_core(CoreIndex(1)); - assert_eq!(None, OnDemandAssigner::pop_assignment_for_core(CoreIndex(2))); - } +#[test] +fn reverse_queue_index_does_reverse() { + let mut v = vec![1, 2, 3, 4, 5, 6]; - // Affinity should be the same as before, but on different cores. - assert_eq!(OnDemandAssigner::get_affinity_map(para_a).unwrap().count, 2); - assert_eq!(OnDemandAssigner::get_affinity_map(para_b).unwrap().count, 1); - assert_eq!(OnDemandAssigner::get_affinity_map(para_a).unwrap().core_idx, CoreIndex(0)); - assert_eq!(OnDemandAssigner::get_affinity_map(para_b).unwrap().core_idx, CoreIndex(1)); + // Basic reversal of a vector. + v.sort_by_key(|&num| ReverseQueueIndex(num)); + assert_eq!(v, vec![6, 5, 4, 3, 2, 1]); - // Clear affinity - OnDemandAssigner::report_processed(para_a, 0.into()); - OnDemandAssigner::report_processed(para_a, 0.into()); - OnDemandAssigner::report_processed(para_b, 1.into()); + // Example from rust docs on `Reverse`. Should work identically. + v.sort_by_key(|&num| (num > 3, ReverseQueueIndex(num))); + assert_eq!(v, vec![3, 2, 1, 6, 5, 4]); - // There should be no affinity after clearing. - assert!(OnDemandAssigner::get_affinity_map(para_a).is_none()); - assert!(OnDemandAssigner::get_affinity_map(para_b).is_none()); - }); + let mut v2 = vec![1, 2, u32::MAX]; + v2.sort_by_key(|&num| ReverseQueueIndex(num)); + assert_eq!(v2, vec![2, 1, u32::MAX]); } #[test] -fn on_demand_orders_cannot_be_popped_if_lifecycle_changes() { - let para_id = ParaId::from(10); - let core_index = CoreIndex(0); - let order = EnqueuedOrder::new(para_id); +fn queue_status_size_fn_works() { + // Add orders to the on demand queue, and make sure that they are properly represented + // by the QueueStatusType::size fn. + let parachains = vec![ParaId::from(111), ParaId::from(222), ParaId::from(333)]; + let core_indices = vec![CoreIndex(0), CoreIndex(1)]; new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { - // Register the para_id as a parathread - schedule_blank_para(para_id, ParaKind::Parathread); - - assert!(!Paras::is_parathread(para_id)); - run_to_block(10, |n| if n == 10 { Some(Default::default()) } else { None }); - assert!(Paras::is_parathread(para_id)); + parachains.iter().for_each(|chain| { + schedule_blank_para(*chain, ParaKind::Parathread); + }); - // Add two assignments for a para_id with a valid lifecycle. - assert_ok!(OnDemandAssigner::add_on_demand_order(order.clone(), QueuePushDirection::Back)); - assert_ok!(OnDemandAssigner::add_on_demand_order(order.clone(), QueuePushDirection::Back)); + assert_eq!(OnDemandAssigner::get_queue_status().size(), 0); - // First pop is fine - assert!( - OnDemandAssigner::pop_assignment_for_core(core_index) == - Some(Assignment::Pool { para_id, core_index }) - ); + run_to_block(11, |n| if n == 11 { Some(Default::default()) } else { None }); - // Deregister para - assert_ok!(Paras::schedule_para_cleanup(para_id)); + // Place orders for all chains. + parachains.iter().for_each(|chain| { + // 2 per chain for a total of 6 + place_order(*chain); + place_order(*chain); + }); - // Run to new session and verify that para_id is no longer a valid parathread. - assert!(Paras::is_parathread(para_id)); - run_to_block(20, |n| if n == 20 { Some(Default::default()) } else { None }); - assert!(!Paras::is_parathread(para_id)); + // 6 orders in free entries + assert_eq!(OnDemandAssigner::get_free_entries().len(), 6); + // 6 orders via queue status size + assert_eq!( + OnDemandAssigner::get_free_entries().len(), + OnDemandAssigner::get_queue_status().size() as usize + ); - // Second pop should be None. - OnDemandAssigner::report_processed(para_id, core_index); - assert_eq!(OnDemandAssigner::pop_assignment_for_core(core_index), None); + core_indices.iter().for_each(|core_index| { + OnDemandAssigner::pop_assignment_for_core(*core_index); + }); + + // There should be 2 orders in the scheduler's claimqueue, + // 2 in assorted AffinityMaps and 2 in free. + // ParaId 111 + assert_eq!(OnDemandAssigner::get_affinity_entries(core_indices[0]).len(), 1); + // ParaId 222 + assert_eq!(OnDemandAssigner::get_affinity_entries(core_indices[1]).len(), 1); + // Free entries are from ParaId 333 + assert_eq!(OnDemandAssigner::get_free_entries().len(), 2); + // For a total size of 4. + assert_eq!(OnDemandAssigner::get_queue_status().size(), 4) }); } diff --git a/polkadot/runtime/parachains/src/assigner_parachains.rs b/polkadot/runtime/parachains/src/assigner_parachains.rs index 34b5d3c1ec51811e8e4c50255592b1e9344014d8..b5f342563e9763e9dba0fa8cbc7afd8e84dc59da 100644 --- a/polkadot/runtime/parachains/src/assigner_parachains.rs +++ b/polkadot/runtime/parachains/src/assigner_parachains.rs @@ -27,7 +27,7 @@ use primitives::CoreIndex; use crate::{ configuration, paras, - scheduler::common::{Assignment, AssignmentProvider, AssignmentProviderConfig}, + scheduler::common::{Assignment, AssignmentProvider}, }; pub use pallet::*; @@ -58,16 +58,6 @@ impl AssignmentProvider> for Pallet { /// this is a no-op in the case of a bulk assignment slot. fn push_back_assignment(_: Assignment) {} - fn get_provider_config(_core_idx: CoreIndex) -> AssignmentProviderConfig> { - AssignmentProviderConfig { - // The next assignment already goes to the same [`ParaId`], no timeout tracking needed. - max_availability_timeouts: 0, - // The next assignment already goes to the same [`ParaId`], this can be any number - // that's high enough to clear the time it takes to clear backing/availability. - ttl: 10u32.into(), - } - } - #[cfg(any(feature = "runtime-benchmarks", test))] fn get_mock_assignment(_: CoreIndex, para_id: primitives::Id) -> Assignment { Assignment::Bulk(para_id) diff --git a/polkadot/runtime/parachains/src/assigner_parachains/mock_helpers.rs b/polkadot/runtime/parachains/src/assigner_parachains/mock_helpers.rs index e6e9fb074aa97e87e6fe92819cc57e5bfa2ca656..a46e114daeaf458fb6bc985a5bb2ebad951d7e3b 100644 --- a/polkadot/runtime/parachains/src/assigner_parachains/mock_helpers.rs +++ b/polkadot/runtime/parachains/src/assigner_parachains/mock_helpers.rs @@ -60,11 +60,12 @@ impl GenesisConfigBuilder { pub(super) fn build(self) -> MockGenesisConfig { let mut genesis = default_genesis_config(); let config = &mut genesis.configuration.config; - config.coretime_cores = self.on_demand_cores; - config.on_demand_base_fee = self.on_demand_base_fee; - config.on_demand_fee_variability = self.on_demand_fee_variability; - config.on_demand_queue_max_size = self.on_demand_max_queue_size; - config.on_demand_target_queue_utilization = self.on_demand_target_queue_utilization; + config.scheduler_params.num_cores = self.on_demand_cores; + config.scheduler_params.on_demand_base_fee = self.on_demand_base_fee; + config.scheduler_params.on_demand_fee_variability = self.on_demand_fee_variability; + config.scheduler_params.on_demand_queue_max_size = self.on_demand_max_queue_size; + config.scheduler_params.on_demand_target_queue_utilization = + self.on_demand_target_queue_utilization; let paras = &mut genesis.paras.paras; for para_id in self.onboarded_on_demand_chains { diff --git a/polkadot/runtime/parachains/src/builder.rs b/polkadot/runtime/parachains/src/builder.rs index 016b3fca589a5b845110d9f25199cc8b8aef5bfe..73617010f6dece72af1ffbd4ccc3fc0d91e70f12 100644 --- a/polkadot/runtime/parachains/src/builder.rs +++ b/polkadot/runtime/parachains/src/builder.rs @@ -18,23 +18,20 @@ use crate::{ configuration, inclusion, initializer, paras, paras::ParaKind, paras_inherent, - scheduler::{ - self, - common::{AssignmentProvider, AssignmentProviderConfig}, - CoreOccupied, ParasEntry, - }, + scheduler::{self, common::AssignmentProvider, CoreOccupied, ParasEntry}, session_info, shared, }; use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec}; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use primitives::{ - collator_signature_payload, AvailabilityBitfield, BackedCandidate, CandidateCommitments, - CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, CommittedCandidateReceipt, - CompactStatement, CoreIndex, DisputeStatement, DisputeStatementSet, GroupIndex, HeadData, - Id as ParaId, IndexedVec, InherentData as ParachainsInherentData, InvalidDisputeStatementKind, - PersistedValidationData, SessionIndex, SigningContext, UncheckedSigned, - ValidDisputeStatementKind, ValidationCode, ValidatorId, ValidatorIndex, ValidityAttestation, + collator_signature_payload, vstaging::node_features::FeatureIndex, AvailabilityBitfield, + BackedCandidate, CandidateCommitments, CandidateDescriptor, CandidateHash, CollatorId, + CollatorSignature, CommittedCandidateReceipt, CompactStatement, CoreIndex, DisputeStatement, + DisputeStatementSet, GroupIndex, HeadData, Id as ParaId, IndexedVec, + InherentData as ParachainsInherentData, InvalidDisputeStatementKind, PersistedValidationData, + SessionIndex, SigningContext, UncheckedSigned, ValidDisputeStatementKind, ValidationCode, + ValidatorId, ValidatorIndex, ValidityAttestation, }; use sp_core::{sr25519, H256}; use sp_runtime::{ @@ -42,7 +39,11 @@ use sp_runtime::{ traits::{Header as HeaderT, One, TrailingZeroInput, Zero}, RuntimeAppPublic, }; -use sp_std::{collections::btree_map::BTreeMap, prelude::Vec, vec}; +use sp_std::{ + collections::{btree_map::BTreeMap, btree_set::BTreeSet, vec_deque::VecDeque}, + prelude::Vec, + vec, +}; fn mock_validation_code() -> ValidationCode { ValidationCode(vec![1, 2, 3]) @@ -86,18 +87,25 @@ pub(crate) struct BenchBuilder { /// Optionally set the number of dispute statements for each candidate. dispute_statements: BTreeMap, /// Session index of for each dispute. Index of slice corresponds to a core, - /// which is offset by the number of entries for `backed_and_concluding_cores`. I.E. if - /// `backed_and_concluding_cores` has 3 entries, the first index of `dispute_sessions` + /// which is offset by the number of entries for `backed_and_concluding_paras`. I.E. if + /// `backed_and_concluding_paras` has 3 entries, the first index of `dispute_sessions` /// will correspond to core index 3. There must be one entry for each core with a dispute /// statement set. dispute_sessions: Vec, - /// Map from core seed to number of validity votes. - backed_and_concluding_cores: BTreeMap, + /// Map from para id to number of validity votes. Core indices are generated based on + /// `elastic_paras` configuration. Each para id in `elastic_paras` gets the + /// specified amount of consecutive cores assigned to it. If a para id is not present + /// in `elastic_paras` it get assigned to a single core. + backed_and_concluding_paras: BTreeMap, + /// Map from para id (seed) to number of chained candidates. + elastic_paras: BTreeMap, /// Make every candidate include a code upgrade by setting this to `Some` where the interior /// value is the byte length of the new code. code_upgrade: Option, /// Specifies whether the claimqueue should be filled. fill_claimqueue: bool, + /// Cores which should not be available when being populated with pending candidates. + unavailable_cores: Vec, _phantom: sp_std::marker::PhantomData, } @@ -109,6 +117,7 @@ pub(crate) struct Bench { pub(crate) _block_number: BlockNumberFor, } +#[allow(dead_code)] impl BenchBuilder { /// Create a new `BenchBuilder` with some opinionated values that should work with the rest /// of the functions in this implementation. @@ -122,9 +131,11 @@ impl BenchBuilder { max_validators: None, dispute_statements: BTreeMap::new(), dispute_sessions: Default::default(), - backed_and_concluding_cores: Default::default(), + backed_and_concluding_paras: Default::default(), + elastic_paras: Default::default(), code_upgrade: None, fill_claimqueue: true, + unavailable_cores: vec![], _phantom: sp_std::marker::PhantomData::, } } @@ -132,7 +143,7 @@ impl BenchBuilder { /// Set the session index for each dispute statement set (in other words, set the session the /// the dispute statement set's relay chain block is from). Indexes of `dispute_sessions` /// correspond to a core, which is offset by the number of entries for - /// `backed_and_concluding_cores`. I.E. if `backed_and_concluding_cores` cores has 3 entries, + /// `backed_and_concluding_paras`. I.E. if `backed_and_concluding_paras` cores has 3 entries, /// the first index of `dispute_sessions` will correspond to core index 3. /// /// Note that there must be an entry for each core with a dispute statement set. @@ -141,12 +152,24 @@ impl BenchBuilder { self } - /// Set a map from core/para id seed to number of validity votes. - pub(crate) fn set_backed_and_concluding_cores( + /// Set the cores which should not be available when being populated with pending candidates. + pub(crate) fn set_unavailable_cores(mut self, unavailable_cores: Vec) -> Self { + self.unavailable_cores = unavailable_cores; + self + } + + /// Set a map from para id seed to number of validity votes. + pub(crate) fn set_backed_and_concluding_paras( mut self, - backed_and_concluding_cores: BTreeMap, + backed_and_concluding_paras: BTreeMap, ) -> Self { - self.backed_and_concluding_cores = backed_and_concluding_cores; + self.backed_and_concluding_paras = backed_and_concluding_paras; + self + } + + /// Set a map from para id seed to number of cores assigned to it. + pub(crate) fn set_elastic_paras(mut self, elastic_paras: BTreeMap) -> Self { + self.elastic_paras = elastic_paras; self } @@ -197,7 +220,10 @@ impl BenchBuilder { /// Maximum number of validators per core (a.k.a. max validators per group). This value is used /// if none is explicitly set on the builder. pub(crate) fn fallback_max_validators_per_core() -> u32 { - configuration::Pallet::::config().max_validators_per_core.unwrap_or(5) + configuration::Pallet::::config() + .scheduler_params + .max_validators_per_core + .unwrap_or(5) } /// Specify a mapping of core index/ para id to the number of dispute statements for the @@ -241,16 +267,6 @@ impl BenchBuilder { (Self::fallback_max_validators() / 2) + 1 } - /// Create para id, core index, and grab the associated group index from the scheduler pallet. - fn create_indexes(&self, seed: u32) -> (ParaId, CoreIndex, GroupIndex) { - let para_id = ParaId::from(seed); - let core_idx = CoreIndex(seed); - let group_idx = - scheduler::Pallet::::group_assigned_to_core(core_idx, self.block_number).unwrap(); - - (para_id, core_idx, group_idx) - } - fn mock_head_data() -> HeadData { let max_head_size = configuration::Pallet::::config().max_head_data_size; HeadData(vec![0xFF; max_head_size as usize]) @@ -264,7 +280,7 @@ impl BenchBuilder { persisted_validation_data_hash: Default::default(), pov_hash: Default::default(), erasure_root: Default::default(), - signature: CollatorSignature::from(sr25519::Signature([42u8; 64])), + signature: CollatorSignature::from(sr25519::Signature::from_raw([42u8; 64])), para_head: Default::default(), validation_code_hash: mock_validation_code().hash(), } @@ -276,11 +292,13 @@ impl BenchBuilder { core_idx: CoreIndex, candidate_hash: CandidateHash, availability_votes: BitVec, + commitments: CandidateCommitments, ) -> inclusion::CandidatePendingAvailability> { inclusion::CandidatePendingAvailability::>::new( core_idx, // core candidate_hash, // hash Self::candidate_descriptor_mock(), // candidate descriptor + commitments, // commitments availability_votes, // availability votes Default::default(), // backers Zero::zero(), // relay parent @@ -301,12 +319,6 @@ impl BenchBuilder { availability_votes: BitVec, candidate_hash: CandidateHash, ) { - let candidate_availability = Self::candidate_availability_mock( - group_idx, - core_idx, - candidate_hash, - availability_votes, - ); let commitments = CandidateCommitments:: { upward_messages: Default::default(), horizontal_messages: Default::default(), @@ -315,16 +327,29 @@ impl BenchBuilder { processed_downward_messages: 0, hrmp_watermark: 0u32.into(), }; - inclusion::PendingAvailability::::insert(para_id, candidate_availability); - inclusion::PendingAvailabilityCommitments::::insert(¶_id, commitments); + let candidate_availability = Self::candidate_availability_mock( + group_idx, + core_idx, + candidate_hash, + availability_votes, + commitments, + ); + inclusion::PendingAvailability::::mutate(para_id, |maybe_andidates| { + if let Some(candidates) = maybe_andidates { + candidates.push_back(candidate_availability); + } else { + *maybe_andidates = + Some([candidate_availability].into_iter().collect::>()); + } + }); } /// Create an `AvailabilityBitfield` where `concluding` is a map where each key is a core index /// that is concluding and `cores` is the total number of cores in the system. - fn availability_bitvec(concluding: &BTreeMap, cores: u32) -> AvailabilityBitfield { + fn availability_bitvec(concluding_cores: &BTreeSet, cores: usize) -> AvailabilityBitfield { let mut bitfields = bitvec::bitvec![u8, bitvec::order::Lsb0; 0; 0]; for i in 0..cores { - if concluding.get(&(i as u32)).is_some() { + if concluding_cores.contains(&(i as u32)) { bitfields.push(true); } else { bitfields.push(false) @@ -348,13 +373,13 @@ impl BenchBuilder { } } - /// Register `cores` count of parachains. + /// Register `n_paras` count of parachains. /// /// Note that this must be called at least 2 sessions before the target session as there is a /// n+2 session delay for the scheduled actions to take effect. - fn setup_para_ids(cores: u32) { + fn setup_para_ids(n_paras: usize) { // make sure parachains exist prior to session change. - for i in 0..cores { + for i in 0..n_paras { let para_id = ParaId::from(i as u32); let validation_code = mock_validation_code(); @@ -407,7 +432,10 @@ impl BenchBuilder { mut self, target_session: SessionIndex, validators: Vec<(T::AccountId, ValidatorId)>, - total_cores: u32, + // Total cores used in the scenario + total_cores: usize, + // Additional cores for elastic parachains + extra_cores: usize, ) -> Self { let mut block = 1; for session in 0..=target_session { @@ -442,7 +470,8 @@ impl BenchBuilder { self.validators = Some(validators_shuffled); self.block_number = block_number; self.session = target_session; - assert_eq!(paras::Pallet::::parachains().len(), total_cores as usize); + + assert_eq!(paras::Pallet::::parachains().len(), total_cores - extra_cores); self } @@ -453,13 +482,42 @@ impl BenchBuilder { /// to the cores successfully being freed from the candidates being marked as available. fn create_availability_bitfields( &self, - concluding_cores: &BTreeMap, - total_cores: u32, + concluding_paras: &BTreeMap, + elastic_paras: &BTreeMap, + total_cores: usize, ) -> Vec> { let validators = self.validators.as_ref().expect("must have some validators prior to calling"); - let availability_bitvec = Self::availability_bitvec(concluding_cores, total_cores); + let mut current_core_idx = 0u32; + let mut concluding_cores = BTreeSet::new(); + + for (seed, _) in concluding_paras.iter() { + // make sure the candidates that will be concluding are marked as pending availability. + let para_id = ParaId::from(*seed); + + for _chain_idx in 0..elastic_paras.get(&seed).cloned().unwrap_or(1) { + let core_idx = CoreIndex::from(current_core_idx); + let group_idx = + scheduler::Pallet::::group_assigned_to_core(core_idx, self.block_number) + .unwrap(); + + Self::add_availability( + para_id, + core_idx, + group_idx, + // No validators have made this candidate available yet. + bitvec::bitvec![u8, bitvec::order::Lsb0; 0; validators.len()], + CandidateHash(H256::from(byte32_slice_from(current_core_idx))), + ); + if !self.unavailable_cores.contains(¤t_core_idx) { + concluding_cores.insert(current_core_idx); + } + current_core_idx += 1; + } + } + + let availability_bitvec = Self::availability_bitvec(&concluding_cores, total_cores); let bitfields: Vec> = validators .iter() @@ -476,122 +534,149 @@ impl BenchBuilder { }) .collect(); - for (seed, _) in concluding_cores.iter() { - // make sure the candidates that will be concluding are marked as pending availability. - let (para_id, core_idx, group_idx) = self.create_indexes(*seed); - Self::add_availability( - para_id, - core_idx, - group_idx, - Self::validator_availability_votes_yes(validators.len()), - CandidateHash(H256::from(byte32_slice_from(*seed))), - ); - } - bitfields } /// Create backed candidates for `cores_with_backed_candidates`. You need these cores to be /// scheduled _within_ paras inherent, which requires marking the available bitfields as fully /// available. - /// - `cores_with_backed_candidates` Mapping of `para_id`/`core_idx`/`group_idx` seed to number - /// of + /// - `cores_with_backed_candidates` Mapping of `para_id` seed to number of /// validity votes. fn create_backed_candidates( &self, - cores_with_backed_candidates: &BTreeMap, + paras_with_backed_candidates: &BTreeMap, + elastic_paras: &BTreeMap, includes_code_upgrade: Option, ) -> Vec> { let validators = self.validators.as_ref().expect("must have some validators prior to calling"); let config = configuration::Pallet::::config(); - cores_with_backed_candidates + let mut current_core_idx = 0u32; + paras_with_backed_candidates .iter() - .map(|(seed, num_votes)| { + .flat_map(|(seed, num_votes)| { assert!(*num_votes <= validators.len() as u32); - let (para_id, _core_idx, group_idx) = self.create_indexes(*seed); - - // This generates a pair and adds it to the keystore, returning just the public. - let collator_public = CollatorId::generate_pair(None); - let header = Self::header(self.block_number); - let relay_parent = header.hash(); - let head_data = Self::mock_head_data(); - let persisted_validation_data_hash = PersistedValidationData:: { - parent_head: head_data.clone(), - relay_parent_number: self.relay_parent_number(), - relay_parent_storage_root: Default::default(), - max_pov_size: config.max_pov_size, - } - .hash(); - - let pov_hash = Default::default(); - let validation_code_hash = mock_validation_code().hash(); - let payload = collator_signature_payload( - &relay_parent, - ¶_id, - &persisted_validation_data_hash, - &pov_hash, - &validation_code_hash, - ); - let signature = collator_public.sign(&payload).unwrap(); - - // Set the head data so it can be used while validating the signatures on the - // candidate receipt. - paras::Pallet::::heads_insert(¶_id, head_data.clone()); - - let mut past_code_meta = paras::ParaPastCodeMeta::>::default(); - past_code_meta.note_replacement(0u32.into(), 0u32.into()); - - let group_validators = scheduler::Pallet::::group_validators(group_idx).unwrap(); - - let candidate = CommittedCandidateReceipt:: { - descriptor: CandidateDescriptor:: { - para_id, - relay_parent, - collator: collator_public, - persisted_validation_data_hash, - pov_hash, - erasure_root: Default::default(), - signature, - para_head: head_data.hash(), - validation_code_hash, - }, - commitments: CandidateCommitments:: { - upward_messages: Default::default(), - horizontal_messages: Default::default(), - new_validation_code: includes_code_upgrade - .map(|v| ValidationCode(vec![42u8; v as usize])), - head_data, - processed_downward_messages: 0, - hrmp_watermark: self.relay_parent_number(), - }, - }; - - let candidate_hash = candidate.hash(); - - let validity_votes: Vec<_> = group_validators - .iter() - .take(*num_votes as usize) - .map(|val_idx| { - let public = validators.get(*val_idx).unwrap(); - let sig = UncheckedSigned::::benchmark_sign( - public, - CompactStatement::Valid(candidate_hash), - &self.signing_context(), - *val_idx, + + let para_id = ParaId::from(*seed); + let mut prev_head = None; + // How many chained candidates we want to build ? + (0..elastic_paras.get(&seed).cloned().unwrap_or(1)) + .map(|chain_idx| { + let core_idx = CoreIndex::from(current_core_idx); + // Advance core index. + current_core_idx += 1; + let group_idx = scheduler::Pallet::::group_assigned_to_core( + core_idx, + self.block_number, ) - .benchmark_signature(); + .unwrap(); - ValidityAttestation::Explicit(sig.clone()) - }) - .collect(); + // This generates a pair and adds it to the keystore, returning just the + // public. + let collator_public = CollatorId::generate_pair(None); + let header = Self::header(self.block_number); + let relay_parent = header.hash(); - BackedCandidate:: { - candidate, - validity_votes, - validator_indices: bitvec::bitvec![u8, bitvec::order::Lsb0; 1; group_validators.len()], - } + // Set the head data so it can be used while validating the signatures on + // the candidate receipt. + let mut head_data = Self::mock_head_data(); + + if chain_idx == 0 { + // Only first parahead of the chain needs to be set in storage. + paras::Pallet::::heads_insert(¶_id, head_data.clone()); + } else { + // Make each candidate head data unique to avoid cycles. + head_data.0[0] = chain_idx; + } + + let persisted_validation_data_hash = PersistedValidationData:: { + // To form a chain we set parent head to previous block if any, or + // default to what is in storage already setup. + parent_head: prev_head.take().unwrap_or(head_data.clone()), + relay_parent_number: self.relay_parent_number(), + relay_parent_storage_root: Default::default(), + max_pov_size: config.max_pov_size, + } + .hash(); + + prev_head = Some(head_data.clone()); + + let pov_hash = Default::default(); + let validation_code_hash = mock_validation_code().hash(); + let payload = collator_signature_payload( + &relay_parent, + ¶_id, + &persisted_validation_data_hash, + &pov_hash, + &validation_code_hash, + ); + let signature = collator_public.sign(&payload).unwrap(); + + let mut past_code_meta = + paras::ParaPastCodeMeta::>::default(); + past_code_meta.note_replacement(0u32.into(), 0u32.into()); + + let group_validators = + scheduler::Pallet::::group_validators(group_idx).unwrap(); + + let candidate = CommittedCandidateReceipt:: { + descriptor: CandidateDescriptor:: { + para_id, + relay_parent, + collator: collator_public, + persisted_validation_data_hash, + pov_hash, + erasure_root: Default::default(), + signature, + para_head: head_data.hash(), + validation_code_hash, + }, + commitments: CandidateCommitments:: { + upward_messages: Default::default(), + horizontal_messages: Default::default(), + new_validation_code: includes_code_upgrade + .map(|v| ValidationCode(vec![42u8; v as usize])), + head_data, + processed_downward_messages: 0, + hrmp_watermark: self.relay_parent_number(), + }, + }; + + let candidate_hash = candidate.hash(); + + let validity_votes: Vec<_> = group_validators + .iter() + .take(*num_votes as usize) + .map(|val_idx| { + let public = validators.get(*val_idx).unwrap(); + let sig = UncheckedSigned::::benchmark_sign( + public, + CompactStatement::Valid(candidate_hash), + &self.signing_context(), + *val_idx, + ) + .benchmark_signature(); + + ValidityAttestation::Explicit(sig.clone()) + }) + .collect(); + + // Check if the elastic scaling bit is set, if so we need to supply the core + // index in the generated candidate. + let core_idx = configuration::Pallet::::config() + .node_features + .get(FeatureIndex::ElasticScalingMVP as usize) + .map(|_the_bit| core_idx); + + BackedCandidate::::new( + candidate, + validity_votes, + bitvec::bitvec![u8, bitvec::order::Lsb0; 1; group_validators.len()], + core_idx, + ) + }) + .collect::>() }) .collect() } @@ -608,6 +693,8 @@ impl BenchBuilder { self.validators.as_ref().expect("must have some validators prior to calling"); let dispute_sessions = dispute_sessions.as_ref(); + let mut current_core_idx = start; + (start..last) .map(|seed| { let dispute_session_idx = (seed - start) as usize; @@ -616,7 +703,14 @@ impl BenchBuilder { .cloned() .unwrap_or(self.target_session); - let (para_id, core_idx, group_idx) = self.create_indexes(seed); + let para_id = ParaId::from(seed); + let core_idx = CoreIndex::from(current_core_idx); + current_core_idx +=1; + + let group_idx = + scheduler::Pallet::::group_assigned_to_core(core_idx, self.block_number) + .unwrap(); + let candidate_hash = CandidateHash(H256::from(byte32_slice_from(seed))); let relay_parent = H256::from(byte32_slice_from(seed)); @@ -660,87 +754,130 @@ impl BenchBuilder { /// Build a scenario for testing or benchmarks. /// - /// Note that this API only allows building scenarios where the `backed_and_concluding_cores` + /// Note that this API only allows building scenarios where the `backed_and_concluding_paras` /// are mutually exclusive with the cores for disputes. So - /// `backed_and_concluding_cores.len() + dispute_sessions.len()` must be less than the max + /// `backed_and_concluding_paras.len() + dispute_sessions.len()` must be less than the max /// number of cores. pub(crate) fn build(self) -> Bench { // Make sure relevant storage is cleared. This is just to get the asserts to work when // running tests because it seems the storage is not cleared in between. #[allow(deprecated)] - inclusion::PendingAvailabilityCommitments::::remove_all(None); - #[allow(deprecated)] inclusion::PendingAvailability::::remove_all(None); // We don't allow a core to have both disputes and be marked fully available at this block. - let max_cores = self.max_cores(); + let max_cores = self.max_cores() as usize; + + let extra_cores = self + .elastic_paras + .values() + .map(|count| *count as usize) + .sum::() + .saturating_sub(self.elastic_paras.len() as usize); + let used_cores = - (self.dispute_sessions.len() + self.backed_and_concluding_cores.len()) as u32; + self.dispute_sessions.len() + self.backed_and_concluding_paras.len() + extra_cores; + assert!(used_cores <= max_cores); let fill_claimqueue = self.fill_claimqueue; // NOTE: there is an n+2 session delay for these actions to take effect. // We are currently in Session 0, so these changes will take effect in Session 2. - Self::setup_para_ids(used_cores); + Self::setup_para_ids(used_cores - extra_cores); configuration::ActiveConfig::::mutate(|c| { - c.coretime_cores = used_cores; + c.scheduler_params.num_cores = used_cores as u32; }); let validator_ids = Self::generate_validator_pairs(self.max_validators()); let target_session = SessionIndex::from(self.target_session); - let builder = self.setup_session(target_session, validator_ids, used_cores); + let builder = self.setup_session(target_session, validator_ids, used_cores, extra_cores); - let bitfields = - builder.create_availability_bitfields(&builder.backed_and_concluding_cores, used_cores); - let backed_candidates = builder - .create_backed_candidates(&builder.backed_and_concluding_cores, builder.code_upgrade); + let bitfields = builder.create_availability_bitfields( + &builder.backed_and_concluding_paras, + &builder.elastic_paras, + used_cores, + ); + let backed_candidates = builder.create_backed_candidates( + &builder.backed_and_concluding_paras, + &builder.elastic_paras, + builder.code_upgrade, + ); let disputes = builder.create_disputes( - builder.backed_and_concluding_cores.len() as u32, - used_cores, + builder.backed_and_concluding_paras.len() as u32, + (used_cores - extra_cores) as u32, builder.dispute_sessions.as_slice(), ); + let mut disputed_cores = (builder.backed_and_concluding_paras.len() as u32.. + ((used_cores - extra_cores) as u32)) + .into_iter() + .map(|idx| (idx, 0)) + .collect::>(); - assert_eq!( - inclusion::PendingAvailabilityCommitments::::iter().count(), - used_cores as usize, - ); - assert_eq!(inclusion::PendingAvailability::::iter().count(), used_cores as usize,); + let mut all_cores = builder.backed_and_concluding_paras.clone(); + all_cores.append(&mut disputed_cores); + + assert_eq!(inclusion::PendingAvailability::::iter().count(), used_cores - extra_cores); // Mark all the used cores as occupied. We expect that there are - // `backed_and_concluding_cores` that are pending availability and that there are - // `used_cores - backed_and_concluding_cores ` which are about to be disputed. + // `backed_and_concluding_paras` that are pending availability and that there are + // `used_cores - backed_and_concluding_paras ` which are about to be disputed. let now = >::block_number() + One::one(); - let cores = (0..used_cores) - .into_iter() - .map(|i| { - let AssignmentProviderConfig { ttl, .. } = - scheduler::Pallet::::assignment_provider_config(CoreIndex(i)); - // Load an assignment into provider so that one is present to pop - let assignment = ::AssignmentProvider::get_mock_assignment( - CoreIndex(i), - ParaId::from(i), - ); - CoreOccupied::Paras(ParasEntry::new(assignment, now + ttl)) + + let mut core_idx = 0u32; + let elastic_paras = &builder.elastic_paras; + // Assign potentially multiple cores to same parachains, + let cores = all_cores + .iter() + .flat_map(|(para_id, _)| { + (0..elastic_paras.get(¶_id).cloned().unwrap_or(1)) + .map(|_para_local_core_idx| { + let ttl = configuration::Pallet::::config().scheduler_params.ttl; + // Load an assignment into provider so that one is present to pop + let assignment = + ::AssignmentProvider::get_mock_assignment( + CoreIndex(core_idx), + ParaId::from(*para_id), + ); + core_idx += 1; + CoreOccupied::Paras(ParasEntry::new(assignment, now + ttl)) + }) + .collect::>>() }) - .collect(); + .collect::>>(); + scheduler::AvailabilityCores::::set(cores); + + core_idx = 0u32; if fill_claimqueue { - // Add items to claim queue as well: - let cores = (0..used_cores) - .into_iter() - .map(|i| { - let AssignmentProviderConfig { ttl, .. } = - scheduler::Pallet::::assignment_provider_config(CoreIndex(i)); - // Load an assignment into provider so that one is present to pop - let assignment = - ::AssignmentProvider::get_mock_assignment( - CoreIndex(i), - ParaId::from(i), - ); - (CoreIndex(i), [ParasEntry::new(assignment, now + ttl)].into()) + let cores = all_cores + .keys() + .flat_map(|para_id| { + (0..elastic_paras.get(¶_id).cloned().unwrap_or(1)) + .filter_map(|_para_local_core_idx| { + let ttl = configuration::Pallet::::config().scheduler_params.ttl; + // Load an assignment into provider so that one is present to pop + let assignment = + ::AssignmentProvider::get_mock_assignment( + CoreIndex(core_idx), + ParaId::from(*para_id), + ); + + let entry = ( + CoreIndex(core_idx), + [ParasEntry::new(assignment, now + ttl)].into(), + ); + let res = if builder.unavailable_cores.contains(&core_idx) { + None + } else { + Some(entry) + }; + core_idx += 1; + res + }) + .collect::>)>>() }) - .collect(); + .collect::>>>(); + scheduler::ClaimQueue::::set(cores); } diff --git a/polkadot/runtime/parachains/src/configuration.rs b/polkadot/runtime/parachains/src/configuration.rs index 7cc5b31fc8fd1dfbed7a02b293eca2dab4b95cf4..b7635dcd7b227993c38475a6ca13fd9a7fe53cb5 100644 --- a/polkadot/runtime/parachains/src/configuration.rs +++ b/polkadot/runtime/parachains/src/configuration.rs @@ -29,7 +29,7 @@ use primitives::{ vstaging::{ApprovalVotingParams, NodeFeatures}, AsyncBackingParams, Balance, ExecutorParamError, ExecutorParams, SessionIndex, LEGACY_MIN_BACKING_VOTES, MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE, MAX_POV_SIZE, - ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, + ON_DEMAND_MAX_QUEUE_MAX_SIZE, }; use sp_runtime::{traits::Zero, Perbill}; use sp_std::prelude::*; @@ -43,6 +43,7 @@ mod benchmarking; pub mod migration; pub use pallet::*; +use primitives::vstaging::SchedulerParams; const LOG_TARGET: &str = "runtime::configuration"; @@ -118,9 +119,9 @@ pub struct HostConfiguration { /// been completed. /// /// Note, there are situations in which `expected_at` in the past. For example, if - /// [`paras_availability_period`](Self::paras_availability_period) is less than the delay set - /// by this field or if PVF pre-check took more time than the delay. In such cases, the upgrade - /// is further at the earliest possible time determined by + /// [`paras_availability_period`](SchedulerParams::paras_availability_period) is less than the + /// delay set by this field or if PVF pre-check took more time than the delay. In such cases, + /// the upgrade is further at the earliest possible time determined by /// [`minimum_validation_upgrade_delay`](Self::minimum_validation_upgrade_delay). /// /// The rationale for this delay has to do with relay-chain reversions. In case there is an @@ -172,48 +173,7 @@ pub struct HostConfiguration { /// How long to keep code on-chain, in blocks. This should be sufficiently long that disputes /// have concluded. pub code_retention_period: BlockNumber, - /// How many cores are managed by the coretime chain. - pub coretime_cores: u32, - /// The number of retries that a on demand author has to submit their block. - pub on_demand_retries: u32, - /// The maximum queue size of the pay as you go module. - pub on_demand_queue_max_size: u32, - /// The target utilization of the spot price queue in percentages. - pub on_demand_target_queue_utilization: Perbill, - /// How quickly the fee rises in reaction to increased utilization. - /// The lower the number the slower the increase. - pub on_demand_fee_variability: Perbill, - /// The minimum amount needed to claim a slot in the spot pricing queue. - pub on_demand_base_fee: Balance, - /// The number of blocks an on demand claim stays in the scheduler's claimqueue before getting - /// cleared. This number should go reasonably higher than the number of blocks in the async - /// backing lookahead. - pub on_demand_ttl: BlockNumber, - /// How often parachain groups should be rotated across parachains. - /// - /// Must be non-zero. - pub group_rotation_frequency: BlockNumber, - /// The minimum availability period, in blocks. - /// - /// This is the minimum amount of blocks after a core became occupied that validators have time - /// to make the block available. - /// - /// This value only has effect on group rotations. If backers backed something at the end of - /// their rotation, the occupied core affects the backing group that comes afterwards. We limit - /// the effect one backing group can have on the next to `paras_availability_period` blocks. - /// - /// Within a group rotation there is no timeout as backers are only affecting themselves. - /// - /// Must be at least 1. With a value of 1, the previous group will not be able to negatively - /// affect the following group at the expense of a tight availability timeline at group - /// rotation boundaries. - pub paras_availability_period: BlockNumber, - /// The amount of blocks ahead to schedule paras. - pub scheduling_lookahead: u32, - /// The maximum number of validators to have per core. - /// - /// `None` means no maximum. - pub max_validators_per_core: Option, + /// The maximum number of validators to use for parachain consensus, period. /// /// `None` means no maximum. @@ -257,7 +217,7 @@ pub struct HostConfiguration { /// scheduled. This number is controlled by this field. /// /// This value should be greater than - /// [`paras_availability_period`](Self::paras_availability_period). + /// [`paras_availability_period`](SchedulerParams::paras_availability_period). pub minimum_validation_upgrade_delay: BlockNumber, /// The minimum number of valid backing statements required to consider a parachain candidate /// backable. @@ -266,6 +226,8 @@ pub struct HostConfiguration { pub node_features: NodeFeatures, /// Params used by approval-voting pub approval_voting_params: ApprovalVotingParams, + /// Scheduler parameters + pub scheduler_params: SchedulerParams, } impl> Default for HostConfiguration { @@ -275,8 +237,6 @@ impl> Default for HostConfiguration> Default for HostConfiguration> Default for HostConfiguration { ZeroMinimumBackingVotes, /// `executor_params` are inconsistent. InconsistentExecutorParams { inner: ExecutorParamError }, + /// TTL should be bigger than lookahead + LookaheadExceedsTTL, + /// Passed in queue size for on-demand was too large. + OnDemandQueueSizeTooLarge, } impl HostConfiguration @@ -373,11 +329,11 @@ where pub fn check_consistency(&self) -> Result<(), InconsistentError> { use InconsistentError::*; - if self.group_rotation_frequency.is_zero() { + if self.scheduler_params.group_rotation_frequency.is_zero() { return Err(ZeroGroupRotationFrequency) } - if self.paras_availability_period.is_zero() { + if self.scheduler_params.paras_availability_period.is_zero() { return Err(ZeroParasAvailabilityPeriod) } @@ -399,10 +355,11 @@ where return Err(MaxPovSizeExceedHardLimit { max_pov_size: self.max_pov_size }) } - if self.minimum_validation_upgrade_delay <= self.paras_availability_period { + if self.minimum_validation_upgrade_delay <= self.scheduler_params.paras_availability_period + { return Err(MinimumValidationUpgradeDelayLessThanChainAvailabilityPeriod { minimum_validation_upgrade_delay: self.minimum_validation_upgrade_delay.clone(), - paras_availability_period: self.paras_availability_period.clone(), + paras_availability_period: self.scheduler_params.paras_availability_period.clone(), }) } @@ -447,6 +404,14 @@ where return Err(InconsistentExecutorParams { inner }) } + if self.scheduler_params.ttl < self.scheduler_params.lookahead.into() { + return Err(LookaheadExceedsTTL) + } + + if self.scheduler_params.on_demand_queue_max_size > ON_DEMAND_MAX_QUEUE_MAX_SIZE { + return Err(OnDemandQueueSizeTooLarge) + } + Ok(()) } @@ -471,6 +436,7 @@ pub trait WeightInfo { fn set_config_with_executor_params() -> Weight; fn set_config_with_perbill() -> Weight; fn set_node_feature() -> Weight; + fn set_config_with_scheduler_params() -> Weight; } pub struct TestWeightInfo; @@ -499,13 +465,16 @@ impl WeightInfo for TestWeightInfo { fn set_node_feature() -> Weight { Weight::MAX } + fn set_config_with_scheduler_params() -> Weight { + Weight::MAX + } } #[frame_support::pallet] pub mod pallet { use super::*; - /// The current storage version. + /// The in-code storage version. /// /// v0-v1: /// v1-v2: @@ -520,7 +489,8 @@ pub mod pallet { /// v8-v9: /// v9-v10: /// v10-11: - const STORAGE_VERSION: StorageVersion = StorageVersion::new(11); + /// v11-12: + const STORAGE_VERSION: StorageVersion = StorageVersion::new(12); #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -667,7 +637,7 @@ pub mod pallet { /// Set the number of coretime execution cores. /// - /// Note that this configuration is managed by the coretime chain. Only manually change + /// NOTE: that this configuration is managed by the coretime chain. Only manually change /// this, if you really know what you are doing! #[pallet::call_index(6)] #[pallet::weight(( @@ -679,16 +649,16 @@ pub mod pallet { Self::set_coretime_cores_unchecked(new) } - /// Set the number of retries for a particular on demand. + /// Set the max number of times a claim may timeout on a core before it is abandoned #[pallet::call_index(7)] #[pallet::weight(( T::WeightInfo::set_config_with_u32(), DispatchClass::Operational, ))] - pub fn set_on_demand_retries(origin: OriginFor, new: u32) -> DispatchResult { + pub fn set_max_availability_timeouts(origin: OriginFor, new: u32) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.on_demand_retries = new; + config.scheduler_params.max_availability_timeouts = new; }) } @@ -704,7 +674,7 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.group_rotation_frequency = new; + config.scheduler_params.group_rotation_frequency = new; }) } @@ -720,7 +690,7 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.paras_availability_period = new; + config.scheduler_params.paras_availability_period = new; }) } @@ -733,7 +703,7 @@ pub mod pallet { pub fn set_scheduling_lookahead(origin: OriginFor, new: u32) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.scheduling_lookahead = new; + config.scheduler_params.lookahead = new; }) } @@ -749,7 +719,7 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.max_validators_per_core = new; + config.scheduler_params.max_validators_per_core = new; }) } @@ -1141,7 +1111,7 @@ pub mod pallet { pub fn set_on_demand_base_fee(origin: OriginFor, new: Balance) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.on_demand_base_fee = new; + config.scheduler_params.on_demand_base_fee = new; }) } @@ -1154,7 +1124,7 @@ pub mod pallet { pub fn set_on_demand_fee_variability(origin: OriginFor, new: Perbill) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.on_demand_fee_variability = new; + config.scheduler_params.on_demand_fee_variability = new; }) } @@ -1167,9 +1137,10 @@ pub mod pallet { pub fn set_on_demand_queue_max_size(origin: OriginFor, new: u32) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.on_demand_queue_max_size = new; + config.scheduler_params.on_demand_queue_max_size = new; }) } + /// Set the on demand (parathreads) fee variability. #[pallet::call_index(50)] #[pallet::weight(( @@ -1182,7 +1153,7 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.on_demand_target_queue_utilization = new; + config.scheduler_params.on_demand_target_queue_utilization = new; }) } /// Set the on demand (parathreads) ttl in the claimqueue. @@ -1194,7 +1165,7 @@ pub mod pallet { pub fn set_on_demand_ttl(origin: OriginFor, new: BlockNumberFor) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.on_demand_ttl = new; + config.scheduler_params.ttl = new; }) } @@ -1244,6 +1215,22 @@ pub mod pallet { config.approval_voting_params = new; }) } + + /// Set scheduler-params. + #[pallet::call_index(55)] + #[pallet::weight(( + T::WeightInfo::set_config_with_scheduler_params(), + DispatchClass::Operational, + ))] + pub fn set_scheduler_params( + origin: OriginFor, + new: SchedulerParams>, + ) -> DispatchResult { + ensure_root(origin)?; + Self::schedule_config_update(|config| { + config.scheduler_params = new; + }) + } } impl Pallet { @@ -1252,7 +1239,7 @@ pub mod pallet { /// To be used if authorization is checked otherwise. pub fn set_coretime_cores_unchecked(new: u32) -> DispatchResult { Self::schedule_config_update(|config| { - config.coretime_cores = new; + config.scheduler_params.num_cores = new; }) } } @@ -1393,7 +1380,7 @@ impl Pallet { let base_config_consistent = base_config.check_consistency().is_ok(); // Now, we need to decide what the new configuration should be. - // We also move the `base_config` to `new_config` to empahsize that the base config was + // We also move the `base_config` to `new_config` to emphasize that the base config was // destroyed by the `updater`. updater(&mut base_config); let new_config = base_config; diff --git a/polkadot/runtime/parachains/src/configuration/benchmarking.rs b/polkadot/runtime/parachains/src/configuration/benchmarking.rs index 67daf1c459884d42378056b6528605da29578c95..882b5aab096ad2f8227aa7ad0efaddfd2078c3ad 100644 --- a/polkadot/runtime/parachains/src/configuration/benchmarking.rs +++ b/polkadot/runtime/parachains/src/configuration/benchmarking.rs @@ -51,6 +51,8 @@ benchmarks! { set_node_feature{}: set_node_feature(RawOrigin::Root, 255, true) + set_config_with_scheduler_params {} : set_scheduler_params(RawOrigin::Root, SchedulerParams::default()) + impl_benchmark_test_suite!( Pallet, crate::mock::new_test_ext(Default::default()), diff --git a/polkadot/runtime/parachains/src/configuration/migration.rs b/polkadot/runtime/parachains/src/configuration/migration.rs index 2838b73092dbab4a029a684948a85123aa489906..87b30b177e735bdc1ec618a6e403c4c74a801533 100644 --- a/polkadot/runtime/parachains/src/configuration/migration.rs +++ b/polkadot/runtime/parachains/src/configuration/migration.rs @@ -18,6 +18,7 @@ pub mod v10; pub mod v11; +pub mod v12; pub mod v6; pub mod v7; pub mod v8; diff --git a/polkadot/runtime/parachains/src/configuration/migration/v11.rs b/polkadot/runtime/parachains/src/configuration/migration/v11.rs index a69061ac1381e253cdbaef6d4681511973f83e86..f6e0e0431640ba271f592759308cc0d07efcfa71 100644 --- a/polkadot/runtime/parachains/src/configuration/migration/v11.rs +++ b/polkadot/runtime/parachains/src/configuration/migration/v11.rs @@ -21,13 +21,122 @@ use frame_support::{ migrations::VersionedMigration, pallet_prelude::*, traits::Defensive, weights::Weight, }; use frame_system::pallet_prelude::BlockNumberFor; -use primitives::{vstaging::ApprovalVotingParams, SessionIndex}; +use primitives::{ + vstaging::ApprovalVotingParams, AsyncBackingParams, ExecutorParams, SessionIndex, + LEGACY_MIN_BACKING_VOTES, ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, +}; use sp_std::vec::Vec; use frame_support::traits::OnRuntimeUpgrade; +use polkadot_core_primitives::Balance; +use primitives::vstaging::NodeFeatures; +use sp_arithmetic::Perbill; use super::v10::V10HostConfiguration; -type V11HostConfiguration = configuration::HostConfiguration; + +#[derive(Clone, Encode, PartialEq, Decode, Debug)] +pub struct V11HostConfiguration { + pub max_code_size: u32, + pub max_head_data_size: u32, + pub max_upward_queue_count: u32, + pub max_upward_queue_size: u32, + pub max_upward_message_size: u32, + pub max_upward_message_num_per_candidate: u32, + pub hrmp_max_message_num_per_candidate: u32, + pub validation_upgrade_cooldown: BlockNumber, + pub validation_upgrade_delay: BlockNumber, + pub async_backing_params: AsyncBackingParams, + pub max_pov_size: u32, + pub max_downward_message_size: u32, + pub hrmp_max_parachain_outbound_channels: u32, + pub hrmp_sender_deposit: Balance, + pub hrmp_recipient_deposit: Balance, + pub hrmp_channel_max_capacity: u32, + pub hrmp_channel_max_total_size: u32, + pub hrmp_max_parachain_inbound_channels: u32, + pub hrmp_channel_max_message_size: u32, + pub executor_params: ExecutorParams, + pub code_retention_period: BlockNumber, + pub coretime_cores: u32, + pub on_demand_retries: u32, + pub on_demand_queue_max_size: u32, + pub on_demand_target_queue_utilization: Perbill, + pub on_demand_fee_variability: Perbill, + pub on_demand_base_fee: Balance, + pub on_demand_ttl: BlockNumber, + pub group_rotation_frequency: BlockNumber, + pub paras_availability_period: BlockNumber, + pub scheduling_lookahead: u32, + pub max_validators_per_core: Option, + pub max_validators: Option, + pub dispute_period: SessionIndex, + pub dispute_post_conclusion_acceptance_period: BlockNumber, + pub no_show_slots: u32, + pub n_delay_tranches: u32, + pub zeroth_delay_tranche_width: u32, + pub needed_approvals: u32, + pub relay_vrf_modulo_samples: u32, + pub pvf_voting_ttl: SessionIndex, + pub minimum_validation_upgrade_delay: BlockNumber, + pub minimum_backing_votes: u32, + pub node_features: NodeFeatures, + pub approval_voting_params: ApprovalVotingParams, +} + +impl> Default for V11HostConfiguration { + fn default() -> Self { + Self { + async_backing_params: AsyncBackingParams { + max_candidate_depth: 0, + allowed_ancestry_len: 0, + }, + group_rotation_frequency: 1u32.into(), + paras_availability_period: 1u32.into(), + no_show_slots: 1u32.into(), + validation_upgrade_cooldown: Default::default(), + validation_upgrade_delay: 2u32.into(), + code_retention_period: Default::default(), + max_code_size: Default::default(), + max_pov_size: Default::default(), + max_head_data_size: Default::default(), + coretime_cores: Default::default(), + on_demand_retries: Default::default(), + scheduling_lookahead: 1, + max_validators_per_core: Default::default(), + max_validators: None, + dispute_period: 6, + dispute_post_conclusion_acceptance_period: 100.into(), + n_delay_tranches: Default::default(), + zeroth_delay_tranche_width: Default::default(), + needed_approvals: Default::default(), + relay_vrf_modulo_samples: Default::default(), + max_upward_queue_count: Default::default(), + max_upward_queue_size: Default::default(), + max_downward_message_size: Default::default(), + max_upward_message_size: Default::default(), + max_upward_message_num_per_candidate: Default::default(), + hrmp_sender_deposit: Default::default(), + hrmp_recipient_deposit: Default::default(), + hrmp_channel_max_capacity: Default::default(), + hrmp_channel_max_total_size: Default::default(), + hrmp_max_parachain_inbound_channels: Default::default(), + hrmp_channel_max_message_size: Default::default(), + hrmp_max_parachain_outbound_channels: Default::default(), + hrmp_max_message_num_per_candidate: Default::default(), + pvf_voting_ttl: 2u32.into(), + minimum_validation_upgrade_delay: 2.into(), + executor_params: Default::default(), + approval_voting_params: ApprovalVotingParams { max_approval_coalesce_count: 1 }, + on_demand_queue_max_size: ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, + on_demand_base_fee: 10_000_000u128, + on_demand_fee_variability: Perbill::from_percent(3), + on_demand_target_queue_utilization: Perbill::from_percent(25), + on_demand_ttl: 5u32.into(), + minimum_backing_votes: LEGACY_MIN_BACKING_VOTES, + node_features: NodeFeatures::EMPTY, + } + } +} mod v10 { use super::*; diff --git a/polkadot/runtime/parachains/src/configuration/migration/v12.rs b/polkadot/runtime/parachains/src/configuration/migration/v12.rs new file mode 100644 index 0000000000000000000000000000000000000000..4295a79893e8ee52632d54772bd108b4de37cc3f --- /dev/null +++ b/polkadot/runtime/parachains/src/configuration/migration/v12.rs @@ -0,0 +1,349 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! A module that is responsible for migration of storage. + +use crate::configuration::{self, migration::v11::V11HostConfiguration, Config, Pallet}; +use frame_support::{ + migrations::VersionedMigration, + pallet_prelude::*, + traits::{Defensive, OnRuntimeUpgrade}, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use primitives::vstaging::SchedulerParams; +use sp_core::Get; +use sp_staking::SessionIndex; +use sp_std::vec::Vec; + +type V12HostConfiguration = configuration::HostConfiguration; + +mod v11 { + use super::*; + + #[frame_support::storage_alias] + pub(crate) type ActiveConfig = + StorageValue, V11HostConfiguration>, OptionQuery>; + + #[frame_support::storage_alias] + pub(crate) type PendingConfigs = StorageValue< + Pallet, + Vec<(SessionIndex, V11HostConfiguration>)>, + OptionQuery, + >; +} + +mod v12 { + use super::*; + + #[frame_support::storage_alias] + pub(crate) type ActiveConfig = + StorageValue, V12HostConfiguration>, OptionQuery>; + + #[frame_support::storage_alias] + pub(crate) type PendingConfigs = StorageValue< + Pallet, + Vec<(SessionIndex, V12HostConfiguration>)>, + OptionQuery, + >; +} + +pub type MigrateToV12 = VersionedMigration< + 11, + 12, + UncheckedMigrateToV12, + Pallet, + ::DbWeight, +>; + +pub struct UncheckedMigrateToV12(sp_std::marker::PhantomData); + +impl OnRuntimeUpgrade for UncheckedMigrateToV12 { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + log::trace!(target: crate::configuration::LOG_TARGET, "Running pre_upgrade() for HostConfiguration MigrateToV12"); + Ok(Vec::new()) + } + + fn on_runtime_upgrade() -> Weight { + log::info!(target: configuration::LOG_TARGET, "HostConfiguration MigrateToV12 started"); + let weight_consumed = migrate_to_v12::(); + + log::info!(target: configuration::LOG_TARGET, "HostConfiguration MigrateToV12 executed successfully"); + + weight_consumed + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + log::trace!(target: crate::configuration::LOG_TARGET, "Running post_upgrade() for HostConfiguration MigrateToV12"); + ensure!( + StorageVersion::get::>() >= 12, + "Storage version should be >= 12 after the migration" + ); + + Ok(()) + } +} + +fn migrate_to_v12() -> Weight { + // Unusual formatting is justified: + // - make it easier to verify that fields assign what they supposed to assign. + // - this code is transient and will be removed after all migrations are done. + // - this code is important enough to optimize for legibility sacrificing consistency. + #[rustfmt::skip] + let translate = + |pre: V11HostConfiguration>| -> + V12HostConfiguration> + { + V12HostConfiguration { + max_code_size : pre.max_code_size, + max_head_data_size : pre.max_head_data_size, + max_upward_queue_count : pre.max_upward_queue_count, + max_upward_queue_size : pre.max_upward_queue_size, + max_upward_message_size : pre.max_upward_message_size, + max_upward_message_num_per_candidate : pre.max_upward_message_num_per_candidate, + hrmp_max_message_num_per_candidate : pre.hrmp_max_message_num_per_candidate, + validation_upgrade_cooldown : pre.validation_upgrade_cooldown, + validation_upgrade_delay : pre.validation_upgrade_delay, + max_pov_size : pre.max_pov_size, + max_downward_message_size : pre.max_downward_message_size, + hrmp_sender_deposit : pre.hrmp_sender_deposit, + hrmp_recipient_deposit : pre.hrmp_recipient_deposit, + hrmp_channel_max_capacity : pre.hrmp_channel_max_capacity, + hrmp_channel_max_total_size : pre.hrmp_channel_max_total_size, + hrmp_max_parachain_inbound_channels : pre.hrmp_max_parachain_inbound_channels, + hrmp_max_parachain_outbound_channels : pre.hrmp_max_parachain_outbound_channels, + hrmp_channel_max_message_size : pre.hrmp_channel_max_message_size, + code_retention_period : pre.code_retention_period, + max_validators : pre.max_validators, + dispute_period : pre.dispute_period, + dispute_post_conclusion_acceptance_period: pre.dispute_post_conclusion_acceptance_period, + no_show_slots : pre.no_show_slots, + n_delay_tranches : pre.n_delay_tranches, + zeroth_delay_tranche_width : pre.zeroth_delay_tranche_width, + needed_approvals : pre.needed_approvals, + relay_vrf_modulo_samples : pre.relay_vrf_modulo_samples, + pvf_voting_ttl : pre.pvf_voting_ttl, + minimum_validation_upgrade_delay : pre.minimum_validation_upgrade_delay, + async_backing_params : pre.async_backing_params, + executor_params : pre.executor_params, + minimum_backing_votes : pre.minimum_backing_votes, + node_features : pre.node_features, + approval_voting_params : pre.approval_voting_params, + scheduler_params: SchedulerParams { + group_rotation_frequency : pre.group_rotation_frequency, + paras_availability_period : pre.paras_availability_period, + max_validators_per_core : pre.max_validators_per_core, + lookahead : pre.scheduling_lookahead, + num_cores : pre.coretime_cores, + max_availability_timeouts : pre.on_demand_retries, + on_demand_queue_max_size : pre.on_demand_queue_max_size, + on_demand_target_queue_utilization : pre.on_demand_target_queue_utilization, + on_demand_fee_variability : pre.on_demand_fee_variability, + on_demand_base_fee : pre.on_demand_base_fee, + ttl : pre.on_demand_ttl, + } + } + }; + + let v11 = v11::ActiveConfig::::get() + .defensive_proof("Could not decode old config") + .unwrap_or_default(); + let v12 = translate(v11); + v12::ActiveConfig::::set(Some(v12)); + + // Allowed to be empty. + let pending_v11 = v11::PendingConfigs::::get().unwrap_or_default(); + let mut pending_v12 = Vec::new(); + + for (session, v11) in pending_v11.into_iter() { + let v12 = translate(v11); + pending_v12.push((session, v12)); + } + v12::PendingConfigs::::set(Some(pending_v12.clone())); + + let num_configs = (pending_v12.len() + 1) as u64; + T::DbWeight::get().reads_writes(num_configs, num_configs) +} + +#[cfg(test)] +mod tests { + use primitives::LEGACY_MIN_BACKING_VOTES; + use sp_arithmetic::Perbill; + + use super::*; + use crate::mock::{new_test_ext, Test}; + + #[test] + fn v12_deserialized_from_actual_data() { + // Example how to get new `raw_config`: + // We'll obtain the raw_config at a specified a block + // Steps: + // 1. Go to Polkadot.js -> Developer -> Chain state -> Storage: https://polkadot.js.org/apps/#/chainstate + // 2. Set these parameters: + // 2.1. selected state query: configuration; activeConfig(): + // PolkadotRuntimeParachainsConfigurationHostConfiguration + // 2.2. blockhash to query at: + // 0xf89d3ab5312c5f70d396dc59612f0aa65806c798346f9db4b35278baed2e0e53 (the hash of + // the block) + // 2.3. Note the value of encoded storage key -> + // 0x06de3d8a54d27e44a9d5ce189618f22db4b49d95320d9021994c850f25b8e385 for the + // referenced block. + // 2.4. You'll also need the decoded values to update the test. + // 3. Go to Polkadot.js -> Developer -> Chain state -> Raw storage + // 3.1 Enter the encoded storage key and you get the raw config. + + // This exceeds the maximal line width length, but that's fine, since this is not code and + // doesn't need to be read and also leaving it as one line allows to easily copy it. + let raw_config = + hex_literal::hex![ + "0000300000800000080000000000100000c8000005000000050000000200000002000000000000000000000000005000000010000400000000000000000000000000000000000000000000000000000000000000000000000800000000200000040000000000100000b004000000060000006400000002000000190000000000000002000000020000000200000005000000020000000001000000140000000400000001010000000100000001000000000000001027000080b2e60e80c3c9018096980000000000000000000000000005000000" + ]; + + let v12 = + V12HostConfiguration::::decode(&mut &raw_config[..]).unwrap(); + + // We check only a sample of the values here. If we missed any fields or messed up data + // types that would skew all the fields coming after. + assert_eq!(v12.max_code_size, 3_145_728); + assert_eq!(v12.validation_upgrade_cooldown, 2); + assert_eq!(v12.max_pov_size, 5_242_880); + assert_eq!(v12.hrmp_channel_max_message_size, 1_048_576); + assert_eq!(v12.n_delay_tranches, 25); + assert_eq!(v12.minimum_validation_upgrade_delay, 5); + assert_eq!(v12.minimum_backing_votes, LEGACY_MIN_BACKING_VOTES); + assert_eq!(v12.approval_voting_params.max_approval_coalesce_count, 1); + assert_eq!(v12.scheduler_params.group_rotation_frequency, 20); + assert_eq!(v12.scheduler_params.paras_availability_period, 4); + assert_eq!(v12.scheduler_params.lookahead, 1); + assert_eq!(v12.scheduler_params.num_cores, 1); + assert_eq!(v12.scheduler_params.max_availability_timeouts, 0); + assert_eq!(v12.scheduler_params.on_demand_queue_max_size, 10_000); + assert_eq!( + v12.scheduler_params.on_demand_target_queue_utilization, + Perbill::from_percent(25) + ); + assert_eq!(v12.scheduler_params.on_demand_fee_variability, Perbill::from_percent(3)); + assert_eq!(v12.scheduler_params.on_demand_base_fee, 10_000_000); + assert_eq!(v12.scheduler_params.ttl, 5); + } + + #[test] + fn test_migrate_to_v12() { + // Host configuration has lots of fields. However, in this migration we only add one + // field. The most important part to check are a couple of the last fields. We also pick + // extra fields to check arbitrarily, e.g. depending on their position (i.e. the middle) and + // also their type. + // + // We specify only the picked fields and the rest should be provided by the `Default` + // implementation. That implementation is copied over between the two types and should work + // fine. + let v11 = V11HostConfiguration:: { + needed_approvals: 69, + paras_availability_period: 55, + hrmp_recipient_deposit: 1337, + max_pov_size: 1111, + minimum_validation_upgrade_delay: 20, + on_demand_ttl: 3, + on_demand_retries: 10, + ..Default::default() + }; + + let mut pending_configs = Vec::new(); + pending_configs.push((100, v11.clone())); + pending_configs.push((300, v11.clone())); + + new_test_ext(Default::default()).execute_with(|| { + // Implant the v10 version in the state. + v11::ActiveConfig::::set(Some(v11.clone())); + v11::PendingConfigs::::set(Some(pending_configs)); + + migrate_to_v12::(); + + let v12 = v12::ActiveConfig::::get().unwrap(); + assert_eq!(v12.approval_voting_params.max_approval_coalesce_count, 1); + + let mut configs_to_check = v12::PendingConfigs::::get().unwrap(); + configs_to_check.push((0, v12.clone())); + + for (_, v12) in configs_to_check { + #[rustfmt::skip] + { + assert_eq!(v11.max_code_size , v12.max_code_size); + assert_eq!(v11.max_head_data_size , v12.max_head_data_size); + assert_eq!(v11.max_upward_queue_count , v12.max_upward_queue_count); + assert_eq!(v11.max_upward_queue_size , v12.max_upward_queue_size); + assert_eq!(v11.max_upward_message_size , v12.max_upward_message_size); + assert_eq!(v11.max_upward_message_num_per_candidate , v12.max_upward_message_num_per_candidate); + assert_eq!(v11.hrmp_max_message_num_per_candidate , v12.hrmp_max_message_num_per_candidate); + assert_eq!(v11.validation_upgrade_cooldown , v12.validation_upgrade_cooldown); + assert_eq!(v11.validation_upgrade_delay , v12.validation_upgrade_delay); + assert_eq!(v11.max_pov_size , v12.max_pov_size); + assert_eq!(v11.max_downward_message_size , v12.max_downward_message_size); + assert_eq!(v11.hrmp_max_parachain_outbound_channels , v12.hrmp_max_parachain_outbound_channels); + assert_eq!(v11.hrmp_sender_deposit , v12.hrmp_sender_deposit); + assert_eq!(v11.hrmp_recipient_deposit , v12.hrmp_recipient_deposit); + assert_eq!(v11.hrmp_channel_max_capacity , v12.hrmp_channel_max_capacity); + assert_eq!(v11.hrmp_channel_max_total_size , v12.hrmp_channel_max_total_size); + assert_eq!(v11.hrmp_max_parachain_inbound_channels , v12.hrmp_max_parachain_inbound_channels); + assert_eq!(v11.hrmp_channel_max_message_size , v12.hrmp_channel_max_message_size); + assert_eq!(v11.code_retention_period , v12.code_retention_period); + assert_eq!(v11.max_validators , v12.max_validators); + assert_eq!(v11.dispute_period , v12.dispute_period); + assert_eq!(v11.no_show_slots , v12.no_show_slots); + assert_eq!(v11.n_delay_tranches , v12.n_delay_tranches); + assert_eq!(v11.zeroth_delay_tranche_width , v12.zeroth_delay_tranche_width); + assert_eq!(v11.needed_approvals , v12.needed_approvals); + assert_eq!(v11.relay_vrf_modulo_samples , v12.relay_vrf_modulo_samples); + assert_eq!(v11.pvf_voting_ttl , v12.pvf_voting_ttl); + assert_eq!(v11.minimum_validation_upgrade_delay , v12.minimum_validation_upgrade_delay); + assert_eq!(v11.async_backing_params.allowed_ancestry_len, v12.async_backing_params.allowed_ancestry_len); + assert_eq!(v11.async_backing_params.max_candidate_depth , v12.async_backing_params.max_candidate_depth); + assert_eq!(v11.executor_params , v12.executor_params); + assert_eq!(v11.minimum_backing_votes , v12.minimum_backing_votes); + assert_eq!(v11.group_rotation_frequency , v12.scheduler_params.group_rotation_frequency); + assert_eq!(v11.paras_availability_period , v12.scheduler_params.paras_availability_period); + assert_eq!(v11.max_validators_per_core , v12.scheduler_params.max_validators_per_core); + assert_eq!(v11.scheduling_lookahead , v12.scheduler_params.lookahead); + assert_eq!(v11.coretime_cores , v12.scheduler_params.num_cores); + assert_eq!(v11.on_demand_retries , v12.scheduler_params.max_availability_timeouts); + assert_eq!(v11.on_demand_queue_max_size , v12.scheduler_params.on_demand_queue_max_size); + assert_eq!(v11.on_demand_target_queue_utilization , v12.scheduler_params.on_demand_target_queue_utilization); + assert_eq!(v11.on_demand_fee_variability , v12.scheduler_params.on_demand_fee_variability); + assert_eq!(v11.on_demand_base_fee , v12.scheduler_params.on_demand_base_fee); + assert_eq!(v11.on_demand_ttl , v12.scheduler_params.ttl); + }; // ; makes this a statement. `rustfmt::skip` cannot be put on an expression. + } + }); + } + + // Test that migration doesn't panic in case there are no pending configurations upgrades in + // pallet's storage. + #[test] + fn test_migrate_to_v12_no_pending() { + let v11 = V11HostConfiguration::::default(); + + new_test_ext(Default::default()).execute_with(|| { + // Implant the v10 version in the state. + v11::ActiveConfig::::set(Some(v11)); + // Ensure there are no pending configs. + v12::PendingConfigs::::set(None); + + // Shouldn't fail. + migrate_to_v12::(); + }); + } +} diff --git a/polkadot/runtime/parachains/src/configuration/tests.rs b/polkadot/runtime/parachains/src/configuration/tests.rs index f1570017dd7ba75db429eac4f00f26fa277de2d6..254511231cac1729981706775bb9f844ca9f1c5c 100644 --- a/polkadot/runtime/parachains/src/configuration/tests.rs +++ b/polkadot/runtime/parachains/src/configuration/tests.rs @@ -226,8 +226,11 @@ fn invariants() { ); ActiveConfig::::put(HostConfiguration { - paras_availability_period: 10, minimum_validation_upgrade_delay: 11, + scheduler_params: SchedulerParams { + paras_availability_period: 10, + ..Default::default() + }, ..Default::default() }); assert_err!( @@ -283,12 +286,6 @@ fn setting_pending_config_members() { max_code_size: 100_000, max_pov_size: 1024, max_head_data_size: 1_000, - coretime_cores: 2, - on_demand_retries: 5, - group_rotation_frequency: 20, - paras_availability_period: 10, - scheduling_lookahead: 3, - max_validators_per_core: None, max_validators: None, dispute_period: 239, dispute_post_conclusion_acceptance_period: 10, @@ -314,13 +311,21 @@ fn setting_pending_config_members() { minimum_validation_upgrade_delay: 20, executor_params: Default::default(), approval_voting_params: ApprovalVotingParams { max_approval_coalesce_count: 1 }, - on_demand_queue_max_size: 10_000u32, - on_demand_base_fee: 10_000_000u128, - on_demand_fee_variability: Perbill::from_percent(3), - on_demand_target_queue_utilization: Perbill::from_percent(25), - on_demand_ttl: 5u32, minimum_backing_votes: 5, node_features: bitvec![u8, Lsb0; 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], + scheduler_params: SchedulerParams { + group_rotation_frequency: 20, + paras_availability_period: 10, + max_validators_per_core: None, + lookahead: 3, + num_cores: 2, + max_availability_timeouts: 5, + on_demand_queue_max_size: 10_000u32, + on_demand_base_fee: 10_000_000u128, + on_demand_fee_variability: Perbill::from_percent(3), + on_demand_target_queue_utilization: Perbill::from_percent(25), + ttl: 5u32, + }, }; Configuration::set_validation_upgrade_cooldown( @@ -342,13 +347,19 @@ fn setting_pending_config_members() { Configuration::set_max_pov_size(RuntimeOrigin::root(), new_config.max_pov_size).unwrap(); Configuration::set_max_head_data_size(RuntimeOrigin::root(), new_config.max_head_data_size) .unwrap(); - Configuration::set_coretime_cores(RuntimeOrigin::root(), new_config.coretime_cores) - .unwrap(); - Configuration::set_on_demand_retries(RuntimeOrigin::root(), new_config.on_demand_retries) - .unwrap(); + Configuration::set_coretime_cores( + RuntimeOrigin::root(), + new_config.scheduler_params.num_cores, + ) + .unwrap(); + Configuration::set_max_availability_timeouts( + RuntimeOrigin::root(), + new_config.scheduler_params.max_availability_timeouts, + ) + .unwrap(); Configuration::set_group_rotation_frequency( RuntimeOrigin::root(), - new_config.group_rotation_frequency, + new_config.scheduler_params.group_rotation_frequency, ) .unwrap(); // This comes out of order to satisfy the validity criteria for the chain and thread @@ -360,17 +371,17 @@ fn setting_pending_config_members() { .unwrap(); Configuration::set_paras_availability_period( RuntimeOrigin::root(), - new_config.paras_availability_period, + new_config.scheduler_params.paras_availability_period, ) .unwrap(); Configuration::set_scheduling_lookahead( RuntimeOrigin::root(), - new_config.scheduling_lookahead, + new_config.scheduler_params.lookahead, ) .unwrap(); Configuration::set_max_validators_per_core( RuntimeOrigin::root(), - new_config.max_validators_per_core, + new_config.scheduler_params.max_validators_per_core, ) .unwrap(); Configuration::set_max_validators(RuntimeOrigin::root(), new_config.max_validators) diff --git a/polkadot/runtime/parachains/src/coretime/migration.rs b/polkadot/runtime/parachains/src/coretime/migration.rs index 9bc0a20ef5b4afce7d63da9bb1d231d39bf445ab..193a5e46b999a8286cea232e0891e7810b60d35f 100644 --- a/polkadot/runtime/parachains/src/coretime/migration.rs +++ b/polkadot/runtime/parachains/src/coretime/migration.rs @@ -114,7 +114,7 @@ mod v_coretime { let legacy_paras = paras::Parachains::::get(); let config = >::config(); - let total_core_count = config.coretime_cores + legacy_paras.len() as u32; + let total_core_count = config.scheduler_params.num_cores + legacy_paras.len() as u32; let dmp_queue_size = crate::dmp::Pallet::::dmq_contents(T::BrokerId::get().into()).len() as u32; @@ -150,7 +150,7 @@ mod v_coretime { // Migrate to Coretime. // - // NOTE: Also migrates coretime_cores config value in configuration::ActiveConfig. + // NOTE: Also migrates `num_cores` config value in configuration::ActiveConfig. fn migrate_to_coretime< T: Config, SendXcm: xcm::v4::SendXcm, @@ -176,8 +176,8 @@ mod v_coretime { } let config = >::config(); - // coretime_cores was on_demand_cores until now: - for on_demand in 0..config.coretime_cores { + // num_cores was on_demand_cores until now: + for on_demand in 0..config.scheduler_params.num_cores { let core = CoreIndex(legacy_count.saturating_add(on_demand as _)); let r = assigner_coretime::Pallet::::assign_core( core, @@ -189,9 +189,9 @@ mod v_coretime { log::error!("Creating assignment for existing on-demand core, failed: {:?}", err); } } - let total_cores = config.coretime_cores + legacy_count; + let total_cores = config.scheduler_params.num_cores + legacy_count; configuration::ActiveConfig::::mutate(|c| { - c.coretime_cores = total_cores; + c.scheduler_params.num_cores = total_cores; }); if let Err(err) = migrate_send_assignments_to_coretime_chain::() { @@ -200,7 +200,9 @@ mod v_coretime { let single_weight = ::WeightInfo::assign_core(1); single_weight - .saturating_mul(u64::from(legacy_count.saturating_add(config.coretime_cores))) + .saturating_mul(u64::from( + legacy_count.saturating_add(config.scheduler_params.num_cores), + )) // Second read from sending assignments to the coretime chain. .saturating_add(T::DbWeight::get().reads_writes(2, 1)) } @@ -236,7 +238,7 @@ mod v_coretime { return None }, }; - // We assume the coretime chain set this parameter to the recommened value in RFC-1: + // We assume the coretime chain set this parameter to the recommended value in RFC-1: const TIME_SLICE_PERIOD: u32 = 80; let round_up = if valid_until % TIME_SLICE_PERIOD > 0 { 1 } else { 0 }; let time_slice = valid_until / TIME_SLICE_PERIOD + TIME_SLICE_PERIOD * round_up; @@ -244,7 +246,8 @@ mod v_coretime { Some(mk_coretime_call(crate::coretime::CoretimeCalls::SetLease(p.into(), time_slice))) }); - let core_count: u16 = configuration::Pallet::::config().coretime_cores.saturated_into(); + let core_count: u16 = + configuration::Pallet::::config().scheduler_params.num_cores.saturated_into(); let set_core_count = iter::once(mk_coretime_call( crate::coretime::CoretimeCalls::NotifyCoreCount(core_count), )); diff --git a/polkadot/runtime/parachains/src/coretime/mod.rs b/polkadot/runtime/parachains/src/coretime/mod.rs index 531f5c2e4e470095989bbb73429cc2180ff4f321..9095cd90ae0cfea6a72fde2d4730cad28cf97d29 100644 --- a/polkadot/runtime/parachains/src/coretime/mod.rs +++ b/polkadot/runtime/parachains/src/coretime/mod.rs @@ -83,6 +83,8 @@ enum CoretimeCalls { SetLease(pallet_broker::TaskId, pallet_broker::Timeslice), #[codec(index = 19)] NotifyCoreCount(u16), + #[codec(index = 99)] + SwapLeases(ParaId, ParaId), } #[frame_support::pallet] @@ -214,8 +216,8 @@ impl Pallet { } pub fn initializer_on_new_session(notification: &SessionChangeNotification>) { - let old_core_count = notification.prev_config.coretime_cores; - let new_core_count = notification.new_config.coretime_cores; + let old_core_count = notification.prev_config.scheduler_params.num_cores; + let new_core_count = notification.new_config.scheduler_params.num_cores; if new_core_count != old_core_count { let core_count: u16 = new_core_count.saturated_into(); let message = Xcm(vec![ @@ -233,6 +235,24 @@ impl Pallet { } } } + + // Handle legacy swaps in coretime. Notifies broker parachain that a lease swap has occurred via + // XCM message. This function is meant to be used in an implementation of `OnSwap` trait. + pub fn on_legacy_lease_swap(one: ParaId, other: ParaId) { + let message = Xcm(vec![ + Instruction::UnpaidExecution { + weight_limit: WeightLimit::Unlimited, + check_origin: None, + }, + mk_coretime_call(crate::coretime::CoretimeCalls::SwapLeases(one, other)), + ]); + if let Err(err) = send_xcm::( + Location::new(0, [Junction::Parachain(T::BrokerId::get())]), + message, + ) { + log::error!("Sending `SwapLeases` to coretime chain failed: {:?}", err); + } + } } impl OnNewSession> for Pallet { diff --git a/polkadot/runtime/parachains/src/disputes.rs b/polkadot/runtime/parachains/src/disputes.rs index c2383dad3053882b7abec959446b23d149aa4a5f..cffad42e0ec99c54c48773bc84e7dc4430ed2e5c 100644 --- a/polkadot/runtime/parachains/src/disputes.rs +++ b/polkadot/runtime/parachains/src/disputes.rs @@ -181,7 +181,7 @@ pub trait DisputesHandler { fn is_frozen() -> bool; /// Remove dispute statement duplicates and sort the non-duplicates based on - /// local (lower indicies) vs remotes (higher indices) and age (older with lower indices). + /// local (lower indices) vs remotes (higher indices) and age (older with lower indices). /// /// Returns `Ok(())` if no duplicates were present, `Err(())` otherwise. /// @@ -379,7 +379,7 @@ pub mod pallet { type WeightInfo: WeightInfo; } - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] diff --git a/polkadot/runtime/parachains/src/disputes/slashing.rs b/polkadot/runtime/parachains/src/disputes/slashing.rs index 9b2b7a48dc8b3ace0b4feea937b7efbe98e4950e..9f8fa1239187c0c95bca38a1a4a9d46dcd645c45 100644 --- a/polkadot/runtime/parachains/src/disputes/slashing.rs +++ b/polkadot/runtime/parachains/src/disputes/slashing.rs @@ -643,7 +643,7 @@ fn is_known_offence( } } -/// Actual `HandleReports` implemention. +/// Actual `HandleReports` implementation. /// /// When configured properly, should be instantiated with /// `T::KeyOwnerIdentification, Offences, ReportLongevity` parameters. diff --git a/polkadot/runtime/parachains/src/hrmp.rs b/polkadot/runtime/parachains/src/hrmp.rs index 42592d9d9f1496c882e0dd2d98e53cf06fcefcc1..d62533dc919b8157253be249e092c505861f1a8e 100644 --- a/polkadot/runtime/parachains/src/hrmp.rs +++ b/polkadot/runtime/parachains/src/hrmp.rs @@ -229,12 +229,12 @@ impl fmt::Debug for OutboundHrmpAcceptanceErr { ), TotalSizeExceeded { idx, total_size, limit } => write!( fmt, - "sending the HRMP message at index {} would exceed the neogitiated channel total size ({} > {})", + "sending the HRMP message at index {} would exceed the negotiated channel total size ({} > {})", idx, total_size, limit, ), CapacityExceeded { idx, count, limit } => write!( fmt, - "sending the HRMP message at index {} would exceed the neogitiated channel capacity ({} > {})", + "sending the HRMP message at index {} would exceed the negotiated channel capacity ({} > {})", idx, count, limit, ), } @@ -790,7 +790,7 @@ pub mod pallet { .ok_or(ArithmeticError::Underflow)?; T::Currency::unreserve( &channel_id.sender.into_account_truncating(), - // The difference should always be convertable into `Balance`, but be + // The difference should always be convertible into `Balance`, but be // paranoid and do nothing in case. amount.try_into().unwrap_or(Zero::zero()), ); diff --git a/polkadot/runtime/parachains/src/hrmp/benchmarking.rs b/polkadot/runtime/parachains/src/hrmp/benchmarking.rs index 2cb49c88d437cb3e0a4c01a40d343a179316ec9d..c6baf2f30cf58279b58b644df76e5ea255f7ec4f 100644 --- a/polkadot/runtime/parachains/src/hrmp/benchmarking.rs +++ b/polkadot/runtime/parachains/src/hrmp/benchmarking.rs @@ -22,7 +22,7 @@ use crate::{ paras::{Pallet as Paras, ParaKind, ParachainsCache}, shared::Pallet as Shared, }; -use frame_benchmarking::{impl_benchmark_test_suite, v2::*, whitelisted_caller}; +use frame_benchmarking::{v2::*, whitelisted_caller}; use frame_support::{assert_ok, traits::Currency}; type BalanceOf = diff --git a/polkadot/runtime/parachains/src/hrmp/tests.rs b/polkadot/runtime/parachains/src/hrmp/tests.rs index 7e7b67c8059dbf4ed9e2c4fc1cebb04efd44c89d..162c1412160139ce66f6b03c0fde9b6d55e95401 100644 --- a/polkadot/runtime/parachains/src/hrmp/tests.rs +++ b/polkadot/runtime/parachains/src/hrmp/tests.rs @@ -702,7 +702,7 @@ fn verify_externally_accessible() { sp_io::storage::get(&well_known_keys::hrmp_ingress_channel_index(para_b)) .expect("the ingress index must be present for para_b"); let ingress_index = >::decode(&mut &raw_ingress_index[..]) - .expect("ingress indexx should be decodable as a list of para ids"); + .expect("ingress index should be decodable as a list of para ids"); assert_eq!(ingress_index, vec![para_a]); // Now, verify that we can access and decode the egress index. diff --git a/polkadot/runtime/parachains/src/inclusion/migration.rs b/polkadot/runtime/parachains/src/inclusion/migration.rs new file mode 100644 index 0000000000000000000000000000000000000000..1e63b209f4e78de48ef349d90b6ef40d21afe124 --- /dev/null +++ b/polkadot/runtime/parachains/src/inclusion/migration.rs @@ -0,0 +1,317 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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. + +pub use v1::MigrateToV1; + +pub mod v0 { + use crate::inclusion::{Config, Pallet}; + use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec}; + use frame_support::{storage_alias, Twox64Concat}; + use frame_system::pallet_prelude::BlockNumberFor; + use parity_scale_codec::{Decode, Encode}; + use primitives::{ + AvailabilityBitfield, CandidateCommitments, CandidateDescriptor, CandidateHash, CoreIndex, + GroupIndex, Id as ParaId, ValidatorIndex, + }; + use scale_info::TypeInfo; + + #[derive(Encode, Decode, PartialEq, TypeInfo, Clone, Debug)] + pub struct CandidatePendingAvailability { + pub core: CoreIndex, + pub hash: CandidateHash, + pub descriptor: CandidateDescriptor, + pub availability_votes: BitVec, + pub backers: BitVec, + pub relay_parent_number: N, + pub backed_in_number: N, + pub backing_group: GroupIndex, + } + + #[derive(Encode, Decode, TypeInfo, Debug, PartialEq)] + pub struct AvailabilityBitfieldRecord { + pub bitfield: AvailabilityBitfield, + pub submitted_at: N, + } + + #[storage_alias] + pub type PendingAvailability = StorageMap< + Pallet, + Twox64Concat, + ParaId, + CandidatePendingAvailability<::Hash, BlockNumberFor>, + >; + + #[storage_alias] + pub type PendingAvailabilityCommitments = + StorageMap, Twox64Concat, ParaId, CandidateCommitments>; + + #[storage_alias] + pub type AvailabilityBitfields = StorageMap< + Pallet, + Twox64Concat, + ValidatorIndex, + AvailabilityBitfieldRecord>, + >; +} + +mod v1 { + use super::v0::{ + AvailabilityBitfields, PendingAvailability as V0PendingAvailability, + PendingAvailabilityCommitments as V0PendingAvailabilityCommitments, + }; + use crate::inclusion::{ + CandidatePendingAvailability as V1CandidatePendingAvailability, Config, Pallet, + PendingAvailability as V1PendingAvailability, + }; + use frame_support::{traits::OnRuntimeUpgrade, weights::Weight}; + use sp_core::Get; + use sp_std::{collections::vec_deque::VecDeque, vec::Vec}; + + #[cfg(feature = "try-runtime")] + use frame_support::{ + ensure, + traits::{GetStorageVersion, StorageVersion}, + }; + #[cfg(feature = "try-runtime")] + use parity_scale_codec::{Decode, Encode}; + + pub struct VersionUncheckedMigrateToV1(sp_std::marker::PhantomData); + + impl OnRuntimeUpgrade for VersionUncheckedMigrateToV1 { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + log::trace!(target: crate::inclusion::LOG_TARGET, "Running pre_upgrade() for inclusion MigrateToV1"); + let candidates_before_upgrade = V0PendingAvailability::::iter().count(); + let commitments_before_upgrade = V0PendingAvailabilityCommitments::::iter().count(); + + if candidates_before_upgrade != commitments_before_upgrade { + log::warn!( + target: crate::inclusion::LOG_TARGET, + "Number of pending candidates differ from the number of pending commitments. {} vs {}", + candidates_before_upgrade, + commitments_before_upgrade + ); + } + + Ok((candidates_before_upgrade as u32).encode()) + } + + fn on_runtime_upgrade() -> Weight { + let mut weight: Weight = Weight::zero(); + + let v0_candidates: Vec<_> = V0PendingAvailability::::drain().collect(); + + for (para_id, candidate) in v0_candidates { + let commitments = V0PendingAvailabilityCommitments::::take(para_id); + // One write for each removal (one candidate and one commitment). + weight = weight.saturating_add(T::DbWeight::get().writes(2)); + + if let Some(commitments) = commitments { + let mut per_para = VecDeque::new(); + per_para.push_back(V1CandidatePendingAvailability { + core: candidate.core, + hash: candidate.hash, + descriptor: candidate.descriptor, + availability_votes: candidate.availability_votes, + backers: candidate.backers, + relay_parent_number: candidate.relay_parent_number, + backed_in_number: candidate.backed_in_number, + backing_group: candidate.backing_group, + commitments, + }); + V1PendingAvailability::::insert(para_id, per_para); + + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + } + } + + // should've already been drained by the above for loop, but as a sanity check, in case + // there are more commitments than candidates. + // V0PendingAvailabilityCommitments should not contain too many keys so removing + // everything at once should be safe + let res = V0PendingAvailabilityCommitments::::clear(u32::MAX, None); + weight = weight.saturating_add( + T::DbWeight::get().reads_writes(res.loops as u64, res.backend as u64), + ); + + // AvailabilityBitfields should not contain too many keys so removing everything at once + // should be safe. + let res = AvailabilityBitfields::::clear(u32::MAX, None); + weight = weight.saturating_add( + T::DbWeight::get().reads_writes(res.loops as u64, res.backend as u64), + ); + + weight + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + log::trace!(target: crate::inclusion::LOG_TARGET, "Running post_upgrade() for inclusion MigrateToV1"); + ensure!( + Pallet::::on_chain_storage_version() >= StorageVersion::new(1), + "Storage version should be >= 1 after the migration" + ); + + let candidates_before_upgrade = + u32::decode(&mut &state[..]).expect("Was properly encoded") as usize; + let candidates_after_upgrade = V1PendingAvailability::::iter().fold( + 0usize, + |mut acc, (_paraid, para_candidates)| { + acc += para_candidates.len(); + acc + }, + ); + + ensure!( + candidates_before_upgrade == candidates_after_upgrade, + "Number of pending candidates should be the same as the one before the upgrade." + ); + ensure!( + V0PendingAvailability::::iter().next() == None, + "Pending availability candidates storage v0 should have been removed" + ); + ensure!( + V0PendingAvailabilityCommitments::::iter().next() == None, + "Pending availability commitments storage should have been removed" + ); + ensure!( + AvailabilityBitfields::::iter().next() == None, + "Availability bitfields storage should have been removed" + ); + + Ok(()) + } + } + + /// Migrate to v1 inclusion module storage. + /// - merges the `PendingAvailabilityCommitments` into the `CandidatePendingAvailability` + /// storage + /// - removes the `AvailabilityBitfields` storage, which was never read. + pub type MigrateToV1 = frame_support::migrations::VersionedMigration< + 0, + 1, + VersionUncheckedMigrateToV1, + Pallet, + ::DbWeight, + >; +} + +#[cfg(test)] +mod tests { + use super::{v1::VersionUncheckedMigrateToV1, *}; + use crate::{ + inclusion::{ + CandidatePendingAvailability as V1CandidatePendingAvailability, + PendingAvailability as V1PendingAvailability, *, + }, + mock::{new_test_ext, MockGenesisConfig, Test}, + }; + use frame_support::traits::OnRuntimeUpgrade; + use primitives::{AvailabilityBitfield, Id as ParaId}; + use test_helpers::{dummy_candidate_commitments, dummy_candidate_descriptor, dummy_hash}; + + #[test] + fn migrate_to_v1() { + new_test_ext(MockGenesisConfig::default()).execute_with(|| { + // No data to migrate. + assert_eq!( + as OnRuntimeUpgrade>::on_runtime_upgrade(), + Weight::zero() + ); + assert!(V1PendingAvailability::::iter().next().is_none()); + + let mut expected = vec![]; + + for i in 1..5 { + let descriptor = dummy_candidate_descriptor(dummy_hash()); + v0::PendingAvailability::::insert( + ParaId::from(i), + v0::CandidatePendingAvailability { + core: CoreIndex(i), + descriptor: descriptor.clone(), + relay_parent_number: i, + hash: CandidateHash(dummy_hash()), + availability_votes: Default::default(), + backed_in_number: i, + backers: Default::default(), + backing_group: GroupIndex(i), + }, + ); + v0::PendingAvailabilityCommitments::::insert( + ParaId::from(i), + dummy_candidate_commitments(HeadData(vec![i as _])), + ); + + v0::AvailabilityBitfields::::insert( + ValidatorIndex(i), + v0::AvailabilityBitfieldRecord { + bitfield: AvailabilityBitfield(Default::default()), + submitted_at: i, + }, + ); + + expected.push(( + ParaId::from(i), + [V1CandidatePendingAvailability { + core: CoreIndex(i), + descriptor, + relay_parent_number: i, + hash: CandidateHash(dummy_hash()), + availability_votes: Default::default(), + backed_in_number: i, + backers: Default::default(), + backing_group: GroupIndex(i), + commitments: dummy_candidate_commitments(HeadData(vec![i as _])), + }] + .into_iter() + .collect::>(), + )); + } + // add some wrong data also, candidates without commitments or commitments without + // candidates. + v0::PendingAvailability::::insert( + ParaId::from(6), + v0::CandidatePendingAvailability { + core: CoreIndex(6), + descriptor: dummy_candidate_descriptor(dummy_hash()), + relay_parent_number: 6, + hash: CandidateHash(dummy_hash()), + availability_votes: Default::default(), + backed_in_number: 6, + backers: Default::default(), + backing_group: GroupIndex(6), + }, + ); + v0::PendingAvailabilityCommitments::::insert( + ParaId::from(7), + dummy_candidate_commitments(HeadData(vec![7 as _])), + ); + + // For tests, db weight is zero. + assert_eq!( + as OnRuntimeUpgrade>::on_runtime_upgrade(), + Weight::zero() + ); + + assert_eq!(v0::PendingAvailabilityCommitments::::iter().next(), None); + assert_eq!(v0::PendingAvailability::::iter().next(), None); + assert_eq!(v0::AvailabilityBitfields::::iter().next(), None); + + let mut actual = V1PendingAvailability::::iter().collect::>(); + actual.sort_by(|(id1, _), (id2, _)| id1.cmp(id2)); + expected.sort_by(|(id1, _), (id2, _)| id1.cmp(id2)); + + assert_eq!(actual, expected); + }); + } +} diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index 90af9cde00a8faaec13101b490ea3ba92f4a827c..e77f8d15b40d13deaaa96d47c04ef378a4a2a73c 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -23,32 +23,33 @@ use crate::{ configuration::{self, HostConfiguration}, disputes, dmp, hrmp, paras::{self, SetGoAhead}, - scheduler::{self, AvailabilityTimeoutStatus}, + scheduler, shared::{self, AllowedRelayParentsTracker}, + util::make_persisted_validation_data_with_parent, }; use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec}; use frame_support::{ defensive, pallet_prelude::*, - traits::{Defensive, EnqueueMessage, Footprint, QueueFootprint}, + traits::{EnqueueMessage, Footprint, QueueFootprint}, BoundedSlice, }; use frame_system::pallet_prelude::*; use pallet_message_queue::OnQueueChanged; use parity_scale_codec::{Decode, Encode}; use primitives::{ - effective_minimum_backing_votes, supermajority_threshold, well_known_keys, - AvailabilityBitfield, BackedCandidate, CandidateCommitments, CandidateDescriptor, - CandidateHash, CandidateReceipt, CommittedCandidateReceipt, CoreIndex, GroupIndex, Hash, - HeadData, Id as ParaId, SignedAvailabilityBitfields, SigningContext, UpwardMessage, - ValidatorId, ValidatorIndex, ValidityAttestation, + effective_minimum_backing_votes, supermajority_threshold, well_known_keys, BackedCandidate, + CandidateCommitments, CandidateDescriptor, CandidateHash, CandidateReceipt, + CommittedCandidateReceipt, CoreIndex, GroupIndex, Hash, HeadData, Id as ParaId, + SignedAvailabilityBitfields, SigningContext, UpwardMessage, ValidatorId, ValidatorIndex, + ValidityAttestation, }; use scale_info::TypeInfo; use sp_runtime::{traits::One, DispatchError, SaturatedConversion, Saturating}; #[cfg(feature = "std")] use sp_std::fmt; use sp_std::{ - collections::{btree_map::BTreeMap, btree_set::BTreeSet}, + collections::{btree_map::BTreeMap, btree_set::BTreeSet, vec_deque::VecDeque}, prelude::*, }; @@ -60,6 +61,8 @@ pub(crate) mod tests; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; +pub mod migration; + pub trait WeightInfo { fn receive_upward_messages(i: u32) -> Weight; } @@ -83,20 +86,8 @@ impl WeightInfo for () { /// `configuration` pallet to check these values before setting. pub const MAX_UPWARD_MESSAGE_SIZE_BOUND: u32 = 128 * 1024; -/// A bitfield signed by a validator indicating that it is keeping its piece of the erasure-coding -/// for any backed candidates referred to by a `1` bit available. -/// -/// The bitfield's signature should be checked at the point of submission. Afterwards it can be -/// dropped. -#[derive(Encode, Decode, TypeInfo)] -#[cfg_attr(test, derive(Debug))] -pub struct AvailabilityBitfieldRecord { - bitfield: AvailabilityBitfield, // one bit per core. - submitted_at: N, // for accounting, as meaning of bits may change over time. -} - /// A backed candidate pending availability. -#[derive(Encode, Decode, PartialEq, TypeInfo)] +#[derive(Encode, Decode, PartialEq, TypeInfo, Clone)] #[cfg_attr(test, derive(Debug))] pub struct CandidatePendingAvailability { /// The availability core this is assigned to. @@ -105,6 +96,8 @@ pub struct CandidatePendingAvailability { hash: CandidateHash, /// The candidate descriptor. descriptor: CandidateDescriptor, + /// The candidate commitments. + commitments: CandidateCommitments, /// The received availability votes. One bit per validator. availability_votes: BitVec, /// The backers of the candidate pending availability. @@ -124,8 +117,11 @@ impl CandidatePendingAvailability { } /// Get the relay-chain block number this was backed in. - pub(crate) fn backed_in_number(&self) -> &N { - &self.backed_in_number + pub(crate) fn backed_in_number(&self) -> N + where + N: Clone, + { + self.backed_in_number.clone() } /// Get the core index. @@ -143,6 +139,11 @@ impl CandidatePendingAvailability { &self.descriptor } + /// Get the candidate commitments. + pub(crate) fn candidate_commitments(&self) -> &CandidateCommitments { + &self.commitments + } + /// Get the candidate's relay parent's number. pub(crate) fn relay_parent_number(&self) -> N where @@ -151,11 +152,22 @@ impl CandidatePendingAvailability { self.relay_parent_number.clone() } + /// Get the candidate backing group. + pub(crate) fn backing_group(&self) -> GroupIndex { + self.backing_group + } + + /// Get the candidate's backers. + pub(crate) fn backers(&self) -> &BitVec { + &self.backers + } + #[cfg(any(feature = "runtime-benchmarks", test))] pub(crate) fn new( core: CoreIndex, hash: CandidateHash, descriptor: CandidateDescriptor, + commitments: CandidateCommitments, availability_votes: BitVec, backers: BitVec, relay_parent_number: N, @@ -166,6 +178,7 @@ impl CandidatePendingAvailability { core, hash, descriptor, + commitments, availability_votes, backers, relay_parent_number, @@ -256,8 +269,10 @@ pub type MaxUmpMessageLenOf = pub mod pallet { use super::*; + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] #[pallet::without_storage_info] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); #[pallet::config] @@ -300,30 +315,10 @@ pub mod pallet { #[pallet::error] pub enum Error { - /// Validator indices are out of order or contains duplicates. - UnsortedOrDuplicateValidatorIndices, - /// Dispute statement sets are out of order or contain duplicates. - UnsortedOrDuplicateDisputeStatementSet, - /// Backed candidates are out of order (core index) or contain duplicates. - UnsortedOrDuplicateBackedCandidates, - /// A different relay parent was provided compared to the on-chain stored one. - UnexpectedRelayParent, - /// Availability bitfield has unexpected size. - WrongBitfieldSize, - /// Bitfield consists of zeros only. - BitfieldAllZeros, - /// Multiple bitfields submitted by same validator or validators out of order by index. - BitfieldDuplicateOrUnordered, /// Validator index out of bounds. ValidatorIndexOutOfBounds, - /// Invalid signature - InvalidBitfieldSignature, /// Candidate submitted but para not scheduled. UnscheduledCandidate, - /// Candidate scheduled despite pending candidate already existing for the para. - CandidateScheduledBeforeParaFree, - /// Scheduled cores out of order. - ScheduledOutOfOrder, /// Head data exceeds the configured maximum. HeadDataTooLarge, /// Code upgrade prematurely. @@ -359,31 +354,22 @@ pub mod pallet { /// The `para_head` hash in the candidate descriptor doesn't match the hash of the actual /// para head in the commitments. ParaHeadMismatch, - /// A bitfield that references a freed core, - /// either intentionally or as part of a concluded - /// invalid dispute. - BitfieldReferencesFreedCore, } - /// The latest bitfield for each validator, referred to by their index in the validator set. - #[pallet::storage] - pub(crate) type AvailabilityBitfields = - StorageMap<_, Twox64Concat, ValidatorIndex, AvailabilityBitfieldRecord>>; - - /// Candidates pending availability by `ParaId`. + /// Candidates pending availability by `ParaId`. They form a chain starting from the latest + /// included head of the para. + /// Use a different prefix post-migration to v1, since the v0 `PendingAvailability` storage + /// would otherwise have the exact same prefix which could cause undefined behaviour when doing + /// the migration. #[pallet::storage] + #[pallet::storage_prefix = "V1"] pub(crate) type PendingAvailability = StorageMap< _, Twox64Concat, ParaId, - CandidatePendingAvailability>, + VecDeque>>, >; - /// The commitments of candidates pending availability, by `ParaId`. - #[pallet::storage] - pub(crate) type PendingAvailabilityCommitments = - StorageMap<_, Twox64Concat, ParaId, CandidateCommitments>; - #[pallet::call] impl Pallet {} } @@ -472,9 +458,7 @@ impl Pallet { ) { // unlike most drain methods, drained elements are not cleared on `Drop` of the iterator // and require consumption. - for _ in >::drain() {} for _ in >::drain() {} - for _ in >::drain() {} Self::cleanup_outgoing_ump_dispatch_queues(outgoing_paras); } @@ -493,27 +477,18 @@ impl Pallet { /// /// Bitfields are expected to have been sanitized already. E.g. via `sanitize_bitfields`! /// - /// Updates storage items `PendingAvailability` and `AvailabilityBitfields`. + /// Updates storage items `PendingAvailability`. /// /// Returns a `Vec` of `CandidateHash`es and their respective `AvailabilityCore`s that became /// available, and cores free. - pub(crate) fn update_pending_availability_and_get_freed_cores( - expected_bits: usize, + pub(crate) fn update_pending_availability_and_get_freed_cores( validators: &[ValidatorId], signed_bitfields: SignedAvailabilityBitfields, - core_lookup: F, - ) -> Vec<(CoreIndex, CandidateHash)> - where - F: Fn(CoreIndex) -> Option, - { - let mut assigned_paras_record = (0..expected_bits) - .map(|bit_index| core_lookup(CoreIndex::from(bit_index as u32))) - .map(|opt_para_id| { - opt_para_id.map(|para_id| (para_id, PendingAvailability::::get(¶_id))) - }) - .collect::>(); + ) -> Vec<(CoreIndex, CandidateHash)> { + let threshold = availability_threshold(validators.len()); + + let mut votes_per_core: BTreeMap> = BTreeMap::new(); - let now = >::block_number(); for (checked_bitfield, validator_index) in signed_bitfields.into_iter().map(|signed_bitfield| { let validator_idx = signed_bitfield.validator_index(); @@ -521,298 +496,275 @@ impl Pallet { (checked_bitfield, validator_idx) }) { for (bit_idx, _) in checked_bitfield.0.iter().enumerate().filter(|(_, is_av)| **is_av) { - let pending_availability = if let Some((_, pending_availability)) = - assigned_paras_record[bit_idx].as_mut() - { - pending_availability - } else { - // For honest validators, this happens in case of unoccupied cores, - // which in turn happens in case of a disputed candidate. - // A malicious one might include arbitrary indices, but they are represented - // by `None` values and will be sorted out in the next if case. - continue - }; - - // defensive check - this is constructed by loading the availability bitfield - // record, which is always `Some` if the core is occupied - that's why we're here. - let validator_index = validator_index.0 as usize; - if let Some(mut bit) = - pending_availability.as_mut().and_then(|candidate_pending_availability| { - candidate_pending_availability.availability_votes.get_mut(validator_index) - }) { - *bit = true; - } + let core_index = CoreIndex(bit_idx as u32); + votes_per_core + .entry(core_index) + .or_insert_with(|| BTreeSet::new()) + .insert(validator_index); } - - let record = - AvailabilityBitfieldRecord { bitfield: checked_bitfield, submitted_at: now }; - - >::insert(&validator_index, record); } - let threshold = availability_threshold(validators.len()); + let mut freed_cores = vec![]; + + let pending_paraids: Vec<_> = >::iter_keys().collect(); + for paraid in pending_paraids { + >::mutate(paraid, |candidates| { + if let Some(candidates) = candidates { + let mut last_enacted_index: Option = None; + + for (candidate_index, candidate) in candidates.iter_mut().enumerate() { + if let Some(validator_indices) = votes_per_core.remove(&candidate.core) { + for validator_index in validator_indices.iter() { + // defensive check - this is constructed by loading the + // availability bitfield record, which is always `Some` if + // the core is occupied - that's why we're here. + if let Some(mut bit) = + candidate.availability_votes.get_mut(validator_index.0 as usize) + { + *bit = true; + } + } + } + + // We check for the candidate's availability even if we didn't get any new + // bitfields for its core, as it may have already been available at a + // previous block but wasn't enacted due to its predecessors not being + // available. + if candidate.availability_votes.count_ones() >= threshold { + // We can only enact a candidate if we've enacted all of its + // predecessors already. + let can_enact = if candidate_index == 0 { + last_enacted_index == None + } else { + let prev_candidate_index = usize::try_from(candidate_index - 1) + .expect("Previous `if` would have caught a 0 candidate index."); + matches!(last_enacted_index, Some(old_index) if old_index == prev_candidate_index) + }; + + if can_enact { + last_enacted_index = Some(candidate_index); + } + } + } - let mut freed_cores = Vec::with_capacity(expected_bits); - for (para_id, pending_availability) in assigned_paras_record - .into_iter() - .flatten() - .filter_map(|(id, p)| p.map(|p| (id, p))) - { - if pending_availability.availability_votes.count_ones() >= threshold { - >::remove(¶_id); - let commitments = match PendingAvailabilityCommitments::::take(¶_id) { - Some(commitments) => commitments, - None => { - log::warn!( - target: LOG_TARGET, - "Inclusion::process_bitfields: PendingAvailability and PendingAvailabilityCommitments - are out of sync, did someone mess with the storage?", - ); - continue - }, - }; - - let receipt = CommittedCandidateReceipt { - descriptor: pending_availability.descriptor, - commitments, - }; - let _weight = Self::enact_candidate( - pending_availability.relay_parent_number, - receipt, - pending_availability.backers, - pending_availability.availability_votes, - pending_availability.core, - pending_availability.backing_group, - ); - - freed_cores.push((pending_availability.core, pending_availability.hash)); - } else { - >::insert(¶_id, &pending_availability); - } + // Trim the pending availability candidates storage and enact candidates of this + // para now. + if let Some(last_enacted_index) = last_enacted_index { + let evicted_candidates = candidates.drain(0..=last_enacted_index); + for candidate in evicted_candidates { + freed_cores.push((candidate.core, candidate.hash)); + + let receipt = CommittedCandidateReceipt { + descriptor: candidate.descriptor, + commitments: candidate.commitments, + }; + let _weight = Self::enact_candidate( + candidate.relay_parent_number, + receipt, + candidate.backers, + candidate.availability_votes, + candidate.core, + candidate.backing_group, + ); + } + } + } + }); } freed_cores } - /// Process candidates that have been backed. Provide the relay storage root, a set of - /// candidates and scheduled cores. + /// Process candidates that have been backed. Provide a set of + /// candidates along with their scheduled cores. /// - /// Both should be sorted ascending by core index, and the candidates should be a subset of - /// scheduled cores. If these conditions are not met, the execution of the function fails. + /// Candidates of the same paraid should be sorted according to their dependency order (they + /// should form a chain). If this condition is not met, this function will return an error. + /// (This really should not happen here, if the candidates were properly sanitised in + /// paras_inherent). pub(crate) fn process_candidates( allowed_relay_parents: &AllowedRelayParentsTracker>, - candidates: Vec>, - scheduled: &BTreeMap, + candidates: &BTreeMap, CoreIndex)>>, group_validators: GV, + core_index_enabled: bool, ) -> Result, DispatchError> where GV: Fn(GroupIndex) -> Option>, { - let now = >::block_number(); - - ensure!(candidates.len() <= scheduled.len(), Error::::UnscheduledCandidate); - - if scheduled.is_empty() { + if candidates.is_empty() { return Ok(ProcessedCandidates::default()) } - let minimum_backing_votes = configuration::Pallet::::config().minimum_backing_votes; + let now = >::block_number(); let validators = shared::Pallet::::active_validator_keys(); // Collect candidate receipts with backers. let mut candidate_receipt_with_backing_validator_indices = Vec::with_capacity(candidates.len()); + let mut core_indices = Vec::with_capacity(candidates.len()); - // Do all checks before writing storage. - let core_indices_and_backers = { - let mut core_indices_and_backers = Vec::with_capacity(candidates.len()); - let mut last_core = None; - - let mut check_assignment_in_order = |core_idx| -> DispatchResult { - ensure!( - last_core.map_or(true, |core| core_idx > core), - Error::::ScheduledOutOfOrder, - ); - - last_core = Some(core_idx); - Ok(()) + for (para_id, para_candidates) in candidates { + let mut latest_head_data = match Self::para_latest_head_data(para_id) { + None => { + defensive!("Latest included head data for paraid {:?} is None", para_id); + continue + }, + Some(latest_head_data) => latest_head_data, }; - // We combine an outer loop over candidates with an inner loop over the scheduled, - // where each iteration of the outer loop picks up at the position - // in scheduled just after the past iteration left off. - // - // If the candidates appear in the same order as they appear in `scheduled`, - // then they should always be found. If the end of `scheduled` is reached, - // then the candidate was either not scheduled or out-of-order. - // - // In the meantime, we do certain sanity checks on the candidates and on the scheduled - // list. - for (candidate_idx, backed_candidate) in candidates.iter().enumerate() { - let relay_parent_hash = backed_candidate.descriptor().relay_parent; - let para_id = backed_candidate.descriptor().para_id; - - let prev_context = >::para_most_recent_context(para_id); - - let check_ctx = CandidateCheckContext::::new(prev_context); - let signing_context = SigningContext { - parent_hash: relay_parent_hash, - session_index: shared::Pallet::::session_index(), - }; - - let relay_parent_number = match check_ctx.verify_backed_candidate( + for (candidate, core) in para_candidates.iter() { + let candidate_hash = candidate.candidate().hash(); + + let check_ctx = CandidateCheckContext::::new(None); + let relay_parent_number = check_ctx.verify_backed_candidate( &allowed_relay_parents, - candidate_idx, - backed_candidate, - )? { - Err(FailedToCreatePVD) => { - log::debug!( - target: LOG_TARGET, - "Failed to create PVD for candidate {}", - candidate_idx, - ); - // We don't want to error out here because it will - // brick the relay-chain. So we return early without - // doing anything. - return Ok(ProcessedCandidates::default()) - }, - Ok(rpn) => rpn, - }; - - let para_id = backed_candidate.descriptor().para_id; - let mut backers = bitvec::bitvec![u8, BitOrderLsb0; 0; validators.len()]; - - let core_idx = *scheduled.get(¶_id).ok_or(Error::::UnscheduledCandidate)?; - check_assignment_in_order(core_idx)?; - ensure!( - >::get(¶_id).is_none() && - >::get(¶_id).is_none(), - Error::::CandidateScheduledBeforeParaFree, - ); - - // The candidate based upon relay parent `N` should be backed by a group - // assigned to core at block `N + 1`. Thus, `relay_parent_number + 1` - // will always land in the current session. + candidate.candidate(), + latest_head_data.clone(), + )?; + + // The candidate based upon relay parent `N` should be backed by a + // group assigned to core at block `N + 1`. Thus, + // `relay_parent_number + 1` will always land in the current + // session. let group_idx = >::group_assigned_to_core( - core_idx, + *core, relay_parent_number + One::one(), ) .ok_or_else(|| { log::warn!( target: LOG_TARGET, - "Failed to compute group index for candidate {}", - candidate_idx + "Failed to compute group index for candidate {:?}", + candidate_hash ); Error::::InvalidAssignment })?; let group_vals = group_validators(group_idx).ok_or_else(|| Error::::InvalidGroupIndex)?; - // check the signatures in the backing and that it is a majority. - { - let maybe_amount_validated = primitives::check_candidate_backing( - &backed_candidate, - &signing_context, - group_vals.len(), - |intra_group_vi| { - group_vals - .get(intra_group_vi) - .and_then(|vi| validators.get(vi.0 as usize)) - .map(|v| v.clone()) - }, - ); - - match maybe_amount_validated { - Ok(amount_validated) => ensure!( - amount_validated >= - effective_minimum_backing_votes( - group_vals.len(), - minimum_backing_votes - ), - Error::::InsufficientBacking, - ), - Err(()) => { - Err(Error::::InvalidBacking)?; - }, - } - - let mut backer_idx_and_attestation = - Vec::<(ValidatorIndex, ValidityAttestation)>::with_capacity( - backed_candidate.validator_indices.count_ones(), - ); - let candidate_receipt = backed_candidate.receipt(); - - for ((bit_idx, _), attestation) in backed_candidate - .validator_indices - .iter() - .enumerate() - .filter(|(_, signed)| **signed) - .zip(backed_candidate.validity_votes.iter().cloned()) - { - let val_idx = - group_vals.get(bit_idx).expect("this query succeeded above; qed"); - backer_idx_and_attestation.push((*val_idx, attestation)); - - backers.set(val_idx.0 as _, true); + // Check backing vote count and validity. + let (backers, backer_idx_and_attestation) = Self::check_backing_votes( + candidate, + &validators, + group_vals, + core_index_enabled, + )?; + + // Found a valid candidate. + latest_head_data = candidate.candidate().commitments.head_data.clone(); + candidate_receipt_with_backing_validator_indices + .push((candidate.receipt(), backer_idx_and_attestation)); + core_indices.push((*core, *para_id)); + + // Update storage now + >::mutate(¶_id, |pending_availability| { + let new_candidate = CandidatePendingAvailability { + core: *core, + hash: candidate_hash, + descriptor: candidate.candidate().descriptor.clone(), + commitments: candidate.candidate().commitments.clone(), + // initialize all availability votes to 0. + availability_votes: bitvec::bitvec![u8, BitOrderLsb0; 0; validators.len()], + relay_parent_number, + backers: backers.to_bitvec(), + backed_in_number: now, + backing_group: group_idx, + }; + + if let Some(pending_availability) = pending_availability { + pending_availability.push_back(new_candidate); + } else { + *pending_availability = + Some([new_candidate].into_iter().collect::>()) } - candidate_receipt_with_backing_validator_indices - .push((candidate_receipt, backer_idx_and_attestation)); - } + }); - core_indices_and_backers.push(( - (core_idx, para_id), - backers, + // Deposit backed event. + Self::deposit_event(Event::::CandidateBacked( + candidate.candidate().to_plain(), + candidate.candidate().commitments.head_data.clone(), + *core, group_idx, - relay_parent_number, )); } + } - core_indices_and_backers - }; + Ok(ProcessedCandidates:: { + core_indices, + candidate_receipt_with_backing_validator_indices, + }) + } - // one more sweep for actually writing to storage. - let core_indices = core_indices_and_backers.iter().map(|(c, ..)| *c).collect(); - for (candidate, (core, backers, group, relay_parent_number)) in - candidates.into_iter().zip(core_indices_and_backers) - { - let para_id = candidate.descriptor().para_id; + // Get the latest backed output head data of this para. + pub(crate) fn para_latest_head_data(para_id: &ParaId) -> Option { + match >::get(para_id).and_then(|pending_candidates| { + pending_candidates.back().map(|x| x.commitments.head_data.clone()) + }) { + Some(head_data) => Some(head_data), + None => >::para_head(para_id), + } + } - // initialize all availability votes to 0. - let availability_votes: BitVec = - bitvec::bitvec![u8, BitOrderLsb0; 0; validators.len()]; + fn check_backing_votes( + backed_candidate: &BackedCandidate, + validators: &[ValidatorId], + group_vals: Vec, + core_index_enabled: bool, + ) -> Result<(BitVec, Vec<(ValidatorIndex, ValidityAttestation)>), Error> { + let minimum_backing_votes = configuration::Pallet::::config().minimum_backing_votes; - Self::deposit_event(Event::::CandidateBacked( - candidate.candidate.to_plain(), - candidate.candidate.commitments.head_data.clone(), - core.0, - group, - )); + let mut backers = bitvec::bitvec![u8, BitOrderLsb0; 0; validators.len()]; + let signing_context = SigningContext { + parent_hash: backed_candidate.descriptor().relay_parent, + session_index: shared::Pallet::::session_index(), + }; - let candidate_hash = candidate.candidate.hash(); - - let (descriptor, commitments) = - (candidate.candidate.descriptor, candidate.candidate.commitments); - - >::insert( - ¶_id, - CandidatePendingAvailability { - core: core.0, - hash: candidate_hash, - descriptor, - availability_votes, - relay_parent_number, - backers: backers.to_bitvec(), - backed_in_number: now, - backing_group: group, - }, + let (validator_indices, _) = + backed_candidate.validator_indices_and_core_index(core_index_enabled); + + // check the signatures in the backing and that it is a majority. + let maybe_amount_validated = primitives::check_candidate_backing( + backed_candidate.candidate().hash(), + backed_candidate.validity_votes(), + validator_indices, + &signing_context, + group_vals.len(), + |intra_group_vi| { + group_vals + .get(intra_group_vi) + .and_then(|vi| validators.get(vi.0 as usize)) + .map(|v| v.clone()) + }, + ); + + match maybe_amount_validated { + Ok(amount_validated) => ensure!( + amount_validated >= + effective_minimum_backing_votes(group_vals.len(), minimum_backing_votes), + Error::::InsufficientBacking, + ), + Err(()) => { + Err(Error::::InvalidBacking)?; + }, + } + + let mut backer_idx_and_attestation = + Vec::<(ValidatorIndex, ValidityAttestation)>::with_capacity( + validator_indices.count_ones(), ); - >::insert(¶_id, commitments); + + for ((bit_idx, _), attestation) in validator_indices + .iter() + .enumerate() + .filter(|(_, signed)| **signed) + .zip(backed_candidate.validity_votes().iter().cloned()) + { + let val_idx = group_vals.get(bit_idx).expect("this query succeeded above; qed"); + backer_idx_and_attestation.push((*val_idx, attestation)); + + backers.set(val_idx.0 as _, true); } - Ok(ProcessedCandidates:: { - core_indices, - candidate_receipt_with_backing_validator_indices, - }) + Ok((backers, backer_idx_and_attestation)) } /// Run the acceptance criteria checks on the given candidate commitments. @@ -1019,110 +971,155 @@ impl Pallet { weight } - /// Cleans up all paras pending availability that the predicate returns true for. - /// - /// The predicate accepts the index of the core and the block number the core has been occupied - /// since (i.e. the block number the candidate was backed at in this fork of the relay chain). + /// Cleans up all timed out candidates as well as their descendant candidates. /// /// Returns a vector of cleaned-up core IDs. - pub(crate) fn collect_pending( - pred: impl Fn(BlockNumberFor) -> AvailabilityTimeoutStatus>, - ) -> Vec { - let mut cleaned_up_ids = Vec::new(); - let mut cleaned_up_cores = Vec::new(); - - for (para_id, pending_record) in >::iter() { - if pred(pending_record.backed_in_number).timed_out { - cleaned_up_ids.push(para_id); - cleaned_up_cores.push(pending_record.core); - } - } - - for para_id in cleaned_up_ids { - let pending = >::take(¶_id); - let commitments = >::take(¶_id); - - if let (Some(pending), Some(commitments)) = (pending, commitments) { - // defensive: this should always be true. - let candidate = CandidateReceipt { - descriptor: pending.descriptor, - commitments_hash: commitments.hash(), - }; + pub(crate) fn free_timedout() -> Vec { + let timeout_pred = >::availability_timeout_predicate(); + + let timed_out: Vec<_> = Self::free_failed_cores( + |candidate| timeout_pred(candidate.backed_in_number).timed_out, + None, + ) + .collect(); + + let mut timed_out_cores = Vec::with_capacity(timed_out.len()); + for candidate in timed_out.iter() { + timed_out_cores.push(candidate.core); + + let receipt = CandidateReceipt { + descriptor: candidate.descriptor.clone(), + commitments_hash: candidate.commitments.hash(), + }; - Self::deposit_event(Event::::CandidateTimedOut( - candidate, - commitments.head_data, - pending.core, - )); - } + Self::deposit_event(Event::::CandidateTimedOut( + receipt, + candidate.commitments.head_data.clone(), + candidate.core, + )); } - cleaned_up_cores + timed_out_cores } - /// Cleans up all paras pending availability that are in the given list of disputed candidates. + /// Cleans up all cores pending availability occupied by one of the disputed candidates or which + /// are descendants of a disputed candidate. /// - /// Returns a vector of cleaned-up core IDs. - pub(crate) fn collect_disputed(disputed: &BTreeSet) -> Vec { - let mut cleaned_up_ids = Vec::new(); - let mut cleaned_up_cores = Vec::new(); - - for (para_id, pending_record) in >::iter() { - if disputed.contains(&pending_record.hash) { - cleaned_up_ids.push(para_id); - cleaned_up_cores.push(pending_record.core); + /// Returns a vector of cleaned-up core IDs, along with the evicted candidate hashes. + pub(crate) fn free_disputed( + disputed: &BTreeSet, + ) -> Vec<(CoreIndex, CandidateHash)> { + Self::free_failed_cores( + |candidate| disputed.contains(&candidate.hash), + Some(disputed.len()), + ) + .map(|candidate| (candidate.core, candidate.hash)) + .collect() + } + + // Clean up cores whose candidates are deemed as failed by the predicate. `pred` returns true if + // a candidate is considered failed. + // A failed candidate also frees all subsequent cores which hold descendants of said candidate. + fn free_failed_cores< + P: Fn(&CandidatePendingAvailability>) -> bool, + >( + pred: P, + capacity_hint: Option, + ) -> impl Iterator>> { + let mut earliest_dropped_indices: BTreeMap = BTreeMap::new(); + + for (para_id, pending_candidates) in >::iter() { + // We assume that pending candidates are stored in dependency order. So we need to store + // the earliest dropped candidate. All others that follow will get freed as well. + let mut earliest_dropped_idx = None; + for (index, candidate) in pending_candidates.iter().enumerate() { + if pred(candidate) { + earliest_dropped_idx = Some(index); + // Since we're looping the candidates in dependency order, we've found the + // earliest failed index for this paraid. + break; + } + } + + if let Some(earliest_dropped_idx) = earliest_dropped_idx { + earliest_dropped_indices.insert(para_id, earliest_dropped_idx); } } - for para_id in cleaned_up_ids { - let _ = >::take(¶_id); - let _ = >::take(¶_id); + let mut cleaned_up_cores = + if let Some(capacity) = capacity_hint { Vec::with_capacity(capacity) } else { vec![] }; + + for (para_id, earliest_dropped_idx) in earliest_dropped_indices { + // Do cleanups and record the cleaned up cores + >::mutate(¶_id, |record| { + if let Some(record) = record { + let cleaned_up = record.drain(earliest_dropped_idx..); + cleaned_up_cores.extend(cleaned_up); + } + }); } - cleaned_up_cores + cleaned_up_cores.into_iter() } - /// Forcibly enact the candidate with the given ID as though it had been deemed available - /// by bitfields. + /// Forcibly enact the pending candidates of the given paraid as though they had been deemed + /// available by bitfields. /// /// Is a no-op if there is no candidate pending availability for this para-id. - /// This should generally not be used but it is useful during execution of Runtime APIs, + /// If there are multiple candidates pending availability for this para-id, it will enact all of + /// them. This should generally not be used but it is useful during execution of Runtime APIs, /// where the changes to the state are expected to be discarded directly after. pub(crate) fn force_enact(para: ParaId) { - let pending = >::take(¶); - let commitments = >::take(¶); - - if let (Some(pending), Some(commitments)) = (pending, commitments) { - let candidate = - CommittedCandidateReceipt { descriptor: pending.descriptor, commitments }; - - Self::enact_candidate( - pending.relay_parent_number, - candidate, - pending.backers, - pending.availability_votes, - pending.core, - pending.backing_group, - ); - } + >::mutate(¶, |candidates| { + if let Some(candidates) = candidates { + for candidate in candidates.drain(..) { + let receipt = CommittedCandidateReceipt { + descriptor: candidate.descriptor, + commitments: candidate.commitments, + }; + + Self::enact_candidate( + candidate.relay_parent_number, + receipt, + candidate.backers, + candidate.availability_votes, + candidate.core, + candidate.backing_group, + ); + } + } + }); } - /// Returns the `CommittedCandidateReceipt` pending availability for the para provided, if any. + /// Returns the first `CommittedCandidateReceipt` pending availability for the para provided, if + /// any. pub(crate) fn candidate_pending_availability( para: ParaId, ) -> Option> { - >::get(¶) - .map(|p| p.descriptor) - .and_then(|d| >::get(¶).map(move |c| (d, c))) - .map(|(d, c)| CommittedCandidateReceipt { descriptor: d, commitments: c }) + >::get(¶).and_then(|p| { + p.get(0).map(|p| CommittedCandidateReceipt { + descriptor: p.descriptor.clone(), + commitments: p.commitments.clone(), + }) + }) } - /// Returns the metadata around the candidate pending availability for the + /// Returns the metadata around the first candidate pending availability for the /// para provided, if any. pub(crate) fn pending_availability( para: ParaId, + ) -> Option>> { + >::get(¶).and_then(|p| p.get(0).cloned()) + } + + /// Returns the metadata around the candidate pending availability occupying the supplied core, + /// if any. + pub(crate) fn pending_availability_with_core( + para: ParaId, + core: CoreIndex, ) -> Option>> { >::get(¶) + .and_then(|p| p.iter().find(|c| c.core == core).cloned()) } } @@ -1173,10 +1170,6 @@ pub(crate) struct CandidateCheckContext { prev_context: Option>, } -/// An error indicating that creating Persisted Validation Data failed -/// while checking a candidate's validity. -pub(crate) struct FailedToCreatePVD; - impl CandidateCheckContext { pub(crate) fn new(prev_context: Option>) -> Self { Self { config: >::config(), prev_context } @@ -1194,11 +1187,11 @@ impl CandidateCheckContext { pub(crate) fn verify_backed_candidate( &self, allowed_relay_parents: &AllowedRelayParentsTracker>, - candidate_idx: usize, - backed_candidate: &BackedCandidate<::Hash>, - ) -> Result, FailedToCreatePVD>, Error> { - let para_id = backed_candidate.descriptor().para_id; - let relay_parent = backed_candidate.descriptor().relay_parent; + backed_candidate_receipt: &CommittedCandidateReceipt<::Hash>, + parent_head_data: HeadData, + ) -> Result, Error> { + let para_id = backed_candidate_receipt.descriptor().para_id; + let relay_parent = backed_candidate_receipt.descriptor().relay_parent; // Check that the relay-parent is one of the allowed relay-parents. let (relay_parent_storage_root, relay_parent_number) = { @@ -1209,27 +1202,22 @@ impl CandidateCheckContext { }; { - let persisted_validation_data = match crate::util::make_persisted_validation_data::( - para_id, + let persisted_validation_data = make_persisted_validation_data_with_parent::( relay_parent_number, relay_parent_storage_root, - ) - .defensive_proof("the para is registered") - { - Some(l) => l, - None => return Ok(Err(FailedToCreatePVD)), - }; + parent_head_data, + ); let expected = persisted_validation_data.hash(); ensure!( - expected == backed_candidate.descriptor().persisted_validation_data_hash, + expected == backed_candidate_receipt.descriptor().persisted_validation_data_hash, Error::::ValidationDataHashMismatch, ); } ensure!( - backed_candidate.descriptor().check_collator_signature().is_ok(), + backed_candidate_receipt.descriptor().check_collator_signature().is_ok(), Error::::NotCollatorSigned, ); @@ -1237,35 +1225,35 @@ impl CandidateCheckContext { // A candidate for a parachain without current validation code is not scheduled. .ok_or_else(|| Error::::UnscheduledCandidate)?; ensure!( - backed_candidate.descriptor().validation_code_hash == validation_code_hash, + backed_candidate_receipt.descriptor().validation_code_hash == validation_code_hash, Error::::InvalidValidationCodeHash, ); ensure!( - backed_candidate.descriptor().para_head == - backed_candidate.candidate.commitments.head_data.hash(), + backed_candidate_receipt.descriptor().para_head == + backed_candidate_receipt.commitments.head_data.hash(), Error::::ParaHeadMismatch, ); if let Err(err) = self.check_validation_outputs( para_id, relay_parent_number, - &backed_candidate.candidate.commitments.head_data, - &backed_candidate.candidate.commitments.new_validation_code, - backed_candidate.candidate.commitments.processed_downward_messages, - &backed_candidate.candidate.commitments.upward_messages, - BlockNumberFor::::from(backed_candidate.candidate.commitments.hrmp_watermark), - &backed_candidate.candidate.commitments.horizontal_messages, + &backed_candidate_receipt.commitments.head_data, + &backed_candidate_receipt.commitments.new_validation_code, + backed_candidate_receipt.commitments.processed_downward_messages, + &backed_candidate_receipt.commitments.upward_messages, + BlockNumberFor::::from(backed_candidate_receipt.commitments.hrmp_watermark), + &backed_candidate_receipt.commitments.horizontal_messages, ) { log::debug!( target: LOG_TARGET, - "Validation outputs checking during inclusion of a candidate {} for parachain `{}` failed", - candidate_idx, + "Validation outputs checking during inclusion of a candidate {:?} for parachain `{}` failed", + backed_candidate_receipt.hash(), u32::from(para_id), ); Err(err.strip_into_dispatch_err::())?; }; - Ok(Ok(relay_parent_number)) + Ok(relay_parent_number) } /// Check the given outputs after candidate validation on whether it passes the acceptance diff --git a/polkadot/runtime/parachains/src/inclusion/tests.rs b/polkadot/runtime/parachains/src/inclusion/tests.rs index 557b7b71a9e326af0ab455619fbfe4fddb34d599..5ab3a13324d29c3d78456f5287de1163530e6f39 100644 --- a/polkadot/runtime/parachains/src/inclusion/tests.rs +++ b/polkadot/runtime/parachains/src/inclusion/tests.rs @@ -27,7 +27,7 @@ use crate::{ shared::AllowedRelayParentsTracker, }; use primitives::{ - effective_minimum_backing_votes, SignedAvailabilityBitfields, + effective_minimum_backing_votes, AvailabilityBitfield, SignedAvailabilityBitfields, UncheckedSignedAvailabilityBitfields, }; @@ -47,10 +47,10 @@ use test_helpers::{dummy_collator, dummy_collator_signature, dummy_validation_co fn default_config() -> HostConfiguration { let mut config = HostConfiguration::default(); - config.coretime_cores = 1; + config.scheduler_params.num_cores = 1; config.max_code_size = 0b100000; config.max_head_data_size = 0b100000; - config.group_rotation_frequency = u32::MAX; + config.scheduler_params.group_rotation_frequency = u32::MAX; config } @@ -120,6 +120,7 @@ pub(crate) fn back_candidate( keystore: &KeystorePtr, signing_context: &SigningContext, kind: BackingKind, + core_index: Option, ) -> BackedCandidate { let mut validator_indices = bitvec::bitvec![u8, BitOrderLsb0; 0; group.len()]; let threshold = effective_minimum_backing_votes( @@ -155,15 +156,20 @@ pub(crate) fn back_candidate( validity_votes.push(ValidityAttestation::Explicit(signature).into()); } - let backed = BackedCandidate { candidate, validity_votes, validator_indices }; + let backed = + BackedCandidate::new(candidate, validity_votes, validator_indices.clone(), core_index); - let successfully_backed = - primitives::check_candidate_backing(&backed, signing_context, group.len(), |i| { - Some(validators[group[i].0 as usize].public().into()) - }) - .ok() - .unwrap_or(0) >= - threshold; + let successfully_backed = primitives::check_candidate_backing( + backed.candidate().hash(), + backed.validity_votes(), + validator_indices.as_bitslice(), + signing_context, + group.len(), + |i| Some(validators[group[i].0 as usize].public().into()), + ) + .ok() + .unwrap_or(0) >= + threshold; match kind { BackingKind::Unanimous | BackingKind::Threshold => assert!(successfully_backed), @@ -218,7 +224,7 @@ pub(crate) fn run_to_block( } pub(crate) fn expected_bits() -> usize { - Paras::parachains().len() + Configuration::config().coretime_cores as usize + Paras::parachains().len() + Configuration::config().scheduler_params.num_cores as usize } fn default_bitfield() -> AvailabilityBitfield { @@ -354,81 +360,288 @@ fn simple_sanitize_bitfields( } /// Process a set of already sanitized bitfields. pub(crate) fn process_bitfields( - expected_bits: usize, signed_bitfields: SignedAvailabilityBitfields, - core_lookup: impl Fn(CoreIndex) -> Option, ) -> Vec<(CoreIndex, CandidateHash)> { let validators = shared::Pallet::::active_validator_keys(); - ParaInclusion::update_pending_availability_and_get_freed_cores::<_>( - expected_bits, + ParaInclusion::update_pending_availability_and_get_freed_cores( &validators[..], signed_bitfields, - core_lookup, ) } #[test] -fn collect_pending_cleans_up_pending() { +fn free_timedout() { let chain_a = ParaId::from(1_u32); let chain_b = ParaId::from(2_u32); - let thread_a = ParaId::from(3_u32); + let chain_c = ParaId::from(3_u32); + let chain_d = ParaId::from(4_u32); + let chain_e = ParaId::from(5_u32); + let chain_f = ParaId::from(6_u32); + let thread_a = ParaId::from(7_u32); let paras = vec![ (chain_a, ParaKind::Parachain), (chain_b, ParaKind::Parachain), + (chain_c, ParaKind::Parachain), + (chain_d, ParaKind::Parachain), + (chain_e, ParaKind::Parachain), + (chain_f, ParaKind::Parachain), (thread_a, ParaKind::Parathread), ]; let mut config = genesis_config(paras); - config.configuration.config.group_rotation_frequency = 3; + config.configuration.config.scheduler_params.group_rotation_frequency = 3; new_test_ext(config).execute_with(|| { - let default_candidate = TestCandidateBuilder::default().build(); - >::insert( - chain_a, + let timed_out_cores = ParaInclusion::free_timedout(); + assert!(timed_out_cores.is_empty()); + + let make_candidate = |core_index: u32, timed_out: bool| { + let default_candidate = TestCandidateBuilder::default().build(); + let backed_in_number = if timed_out { 0 } else { 5 }; + CandidatePendingAvailability { - core: CoreIndex::from(0), + core: CoreIndex::from(core_index), hash: default_candidate.hash(), descriptor: default_candidate.descriptor.clone(), availability_votes: default_availability_votes(), relay_parent_number: 0, - backed_in_number: 0, + backed_in_number, backers: default_backing_bitfield(), - backing_group: GroupIndex::from(0), - }, - ); - PendingAvailabilityCommitments::::insert( + backing_group: GroupIndex::from(core_index), + commitments: default_candidate.commitments.clone(), + } + }; + + >::insert( chain_a, - default_candidate.commitments.clone(), + [make_candidate(0, true)].into_iter().collect::>(), ); >::insert( &chain_b, + [make_candidate(1, false)].into_iter().collect::>(), + ); + + // 2 chained candidates. The first one is timed out. The other will be evicted also. + let mut c_candidates = VecDeque::new(); + c_candidates.push_back(make_candidate(2, true)); + c_candidates.push_back(make_candidate(3, false)); + + >::insert(&chain_c, c_candidates); + + // 2 chained candidates. All are timed out. + let mut d_candidates = VecDeque::new(); + d_candidates.push_back(make_candidate(4, true)); + d_candidates.push_back(make_candidate(5, true)); + + >::insert(&chain_d, d_candidates); + + // 3 chained candidates. The second one is timed out. The first one will remain in place. + // With the current time out predicate this scenario is impossible. But this is not a + // concern for this module. + let mut e_candidates = VecDeque::new(); + e_candidates.push_back(make_candidate(6, false)); + e_candidates.push_back(make_candidate(7, true)); + e_candidates.push_back(make_candidate(8, false)); + + >::insert(&chain_e, e_candidates); + + // 3 chained candidates, none are timed out. + let mut f_candidates = VecDeque::new(); + f_candidates.push_back(make_candidate(9, false)); + f_candidates.push_back(make_candidate(10, false)); + f_candidates.push_back(make_candidate(11, false)); + + >::insert(&chain_f, f_candidates); + + run_to_block(5, |_| None); + + assert_eq!(>::get(&chain_a).unwrap().len(), 1); + assert_eq!(>::get(&chain_b).unwrap().len(), 1); + assert_eq!(>::get(&chain_c).unwrap().len(), 2); + assert_eq!(>::get(&chain_d).unwrap().len(), 2); + assert_eq!(>::get(&chain_e).unwrap().len(), 3); + assert_eq!(>::get(&chain_f).unwrap().len(), 3); + + let timed_out_cores = ParaInclusion::free_timedout(); + + assert_eq!( + timed_out_cores, + vec![ + CoreIndex(0), + CoreIndex(2), + CoreIndex(3), + CoreIndex(4), + CoreIndex(5), + CoreIndex(7), + CoreIndex(8), + ] + ); + + assert!(>::get(&chain_a).unwrap().is_empty()); + assert_eq!(>::get(&chain_b).unwrap().len(), 1); + assert!(>::get(&chain_c).unwrap().is_empty()); + assert!(>::get(&chain_d).unwrap().is_empty()); + assert_eq!( + >::get(&chain_e) + .unwrap() + .into_iter() + .map(|c| c.core) + .collect::>(), + vec![CoreIndex(6)] + ); + assert_eq!( + >::get(&chain_f) + .unwrap() + .into_iter() + .map(|c| c.core) + .collect::>(), + vec![CoreIndex(9), CoreIndex(10), CoreIndex(11)] + ); + }); +} + +#[test] +fn free_disputed() { + let chain_a = ParaId::from(1_u32); + let chain_b = ParaId::from(2_u32); + let chain_c = ParaId::from(3_u32); + let chain_d = ParaId::from(4_u32); + let chain_e = ParaId::from(5_u32); + let chain_f = ParaId::from(6_u32); + let thread_a = ParaId::from(7_u32); + + let paras = vec![ + (chain_a, ParaKind::Parachain), + (chain_b, ParaKind::Parachain), + (chain_c, ParaKind::Parachain), + (chain_d, ParaKind::Parachain), + (chain_e, ParaKind::Parachain), + (chain_f, ParaKind::Parachain), + (thread_a, ParaKind::Parathread), + ]; + let mut config = genesis_config(paras); + config.configuration.config.scheduler_params.group_rotation_frequency = 3; + new_test_ext(config).execute_with(|| { + let disputed_cores = ParaInclusion::free_disputed(&BTreeSet::new()); + assert!(disputed_cores.is_empty()); + + let disputed_cores = ParaInclusion::free_disputed( + &[CandidateHash::default()].into_iter().collect::>(), + ); + assert!(disputed_cores.is_empty()); + + let make_candidate = |core_index: u32| { + let default_candidate = TestCandidateBuilder::default().build(); + CandidatePendingAvailability { - core: CoreIndex::from(1), - hash: default_candidate.hash(), - descriptor: default_candidate.descriptor, + core: CoreIndex::from(core_index), + hash: CandidateHash(Hash::from_low_u64_be(core_index as _)), + descriptor: default_candidate.descriptor.clone(), availability_votes: default_availability_votes(), relay_parent_number: 0, - backed_in_number: 5, + backed_in_number: 0, backers: default_backing_bitfield(), - backing_group: GroupIndex::from(1), - }, + backing_group: GroupIndex::from(core_index), + commitments: default_candidate.commitments.clone(), + } + }; + + // Disputed + >::insert( + chain_a, + [make_candidate(0)].into_iter().collect::>(), + ); + + // Not disputed. + >::insert( + &chain_b, + [make_candidate(1)].into_iter().collect::>(), ); - PendingAvailabilityCommitments::::insert(chain_b, default_candidate.commitments); + + // 2 chained candidates. The first one is disputed. The other will be evicted also. + let mut c_candidates = VecDeque::new(); + c_candidates.push_back(make_candidate(2)); + c_candidates.push_back(make_candidate(3)); + + >::insert(&chain_c, c_candidates); + + // 2 chained candidates. All are disputed. + let mut d_candidates = VecDeque::new(); + d_candidates.push_back(make_candidate(4)); + d_candidates.push_back(make_candidate(5)); + + >::insert(&chain_d, d_candidates); + + // 3 chained candidates. The second one is disputed. The first one will remain in place. + let mut e_candidates = VecDeque::new(); + e_candidates.push_back(make_candidate(6)); + e_candidates.push_back(make_candidate(7)); + e_candidates.push_back(make_candidate(8)); + + >::insert(&chain_e, e_candidates); + + // 3 chained candidates, none are disputed. + let mut f_candidates = VecDeque::new(); + f_candidates.push_back(make_candidate(9)); + f_candidates.push_back(make_candidate(10)); + f_candidates.push_back(make_candidate(11)); + + >::insert(&chain_f, f_candidates); run_to_block(5, |_| None); - assert!(>::get(&chain_a).is_some()); - assert!(>::get(&chain_b).is_some()); - assert!(>::get(&chain_a).is_some()); - assert!(>::get(&chain_b).is_some()); + assert_eq!(>::get(&chain_a).unwrap().len(), 1); + assert_eq!(>::get(&chain_b).unwrap().len(), 1); + assert_eq!(>::get(&chain_c).unwrap().len(), 2); + assert_eq!(>::get(&chain_d).unwrap().len(), 2); + assert_eq!(>::get(&chain_e).unwrap().len(), 3); + assert_eq!(>::get(&chain_f).unwrap().len(), 3); + + let disputed_candidates = [ + CandidateHash(Hash::from_low_u64_be(0)), + CandidateHash(Hash::from_low_u64_be(2)), + CandidateHash(Hash::from_low_u64_be(4)), + CandidateHash(Hash::from_low_u64_be(5)), + CandidateHash(Hash::from_low_u64_be(7)), + ] + .into_iter() + .collect::>(); + let disputed_cores = ParaInclusion::free_disputed(&disputed_candidates); - ParaInclusion::collect_pending(Scheduler::availability_timeout_predicate()); + assert_eq!( + disputed_cores.into_iter().map(|(core, _)| core).collect::>(), + vec![ + CoreIndex(0), + CoreIndex(2), + CoreIndex(3), + CoreIndex(4), + CoreIndex(5), + CoreIndex(7), + CoreIndex(8), + ] + ); - assert!(>::get(&chain_a).is_none()); - assert!(>::get(&chain_b).is_some()); - assert!(>::get(&chain_a).is_none()); - assert!(>::get(&chain_b).is_some()); + assert!(>::get(&chain_a).unwrap().is_empty()); + assert_eq!(>::get(&chain_b).unwrap().len(), 1); + assert!(>::get(&chain_c).unwrap().is_empty()); + assert!(>::get(&chain_d).unwrap().is_empty()); + assert_eq!( + >::get(&chain_e) + .unwrap() + .into_iter() + .map(|c| c.core) + .collect::>(), + vec![CoreIndex(6)] + ); + assert_eq!( + >::get(&chain_f) + .unwrap() + .into_iter() + .map(|c| c.core) + .collect::>(), + vec![CoreIndex(9), CoreIndex(10), CoreIndex(11)] + ); }); } @@ -468,14 +681,6 @@ fn bitfield_checks() { let signing_context = SigningContext { parent_hash: System::parent_hash(), session_index: 5 }; - let core_lookup = |core| match core { - core if core == CoreIndex::from(0) => Some(chain_a), - core if core == CoreIndex::from(1) => Some(chain_b), - core if core == CoreIndex::from(2) => Some(thread_a), - core if core == CoreIndex::from(3) => None, // for the expected_cores() + 1 test below. - _ => panic!("out of bounds for testing"), - }; - // too many bits in bitfield { let mut bare_bitfield = default_bitfield(); @@ -544,7 +749,7 @@ fn bitfield_checks() { ); assert_eq!(checked_bitfields.len(), 1, "No bitfields should have been filtered!"); - let x = process_bitfields(expected_bits(), checked_bitfields, core_lookup); + let x = process_bitfields(checked_bitfields); assert!(x.is_empty(), "No core should be freed."); } @@ -565,7 +770,7 @@ fn bitfield_checks() { ); assert_eq!(checked_bitfields.len(), 1, "No bitfields should have been filtered!"); - let x = process_bitfields(expected_bits(), checked_bitfields, core_lookup); + let x = process_bitfields(checked_bitfields); assert!(x.is_empty(), "No core should be freed."); } @@ -573,12 +778,10 @@ fn bitfield_checks() { { let mut bare_bitfield = default_bitfield(); - assert_eq!(core_lookup(CoreIndex::from(0)), Some(chain_a)); - let default_candidate = TestCandidateBuilder::default().build(); >::insert( chain_a, - CandidatePendingAvailability { + [CandidatePendingAvailability { core: CoreIndex::from(0), hash: default_candidate.hash(), descriptor: default_candidate.descriptor, @@ -587,9 +790,11 @@ fn bitfield_checks() { backed_in_number: 0, backers: default_backing_bitfield(), backing_group: GroupIndex::from(0), - }, + commitments: default_candidate.commitments, + }] + .into_iter() + .collect::>(), ); - PendingAvailabilityCommitments::::insert(chain_a, default_candidate.commitments); *bare_bitfield.0.get_mut(0).unwrap() = true; let signed = sign_bitfield( @@ -607,53 +812,10 @@ fn bitfield_checks() { ); assert_eq!(checked_bitfields.len(), 1, "No bitfields should have been filtered!"); - let x = process_bitfields(expected_bits(), checked_bitfields, core_lookup); + let x = process_bitfields(checked_bitfields); assert!(x.is_empty(), "No core should be freed."); >::remove(chain_a); - PendingAvailabilityCommitments::::remove(chain_a); - } - - // bitfield signed with pending bit signed, but no commitments. - { - let mut bare_bitfield = default_bitfield(); - - assert_eq!(core_lookup(CoreIndex::from(0)), Some(chain_a)); - - let default_candidate = TestCandidateBuilder::default().build(); - >::insert( - chain_a, - CandidatePendingAvailability { - core: CoreIndex::from(0), - hash: default_candidate.hash(), - descriptor: default_candidate.descriptor, - availability_votes: default_availability_votes(), - relay_parent_number: 0, - backed_in_number: 0, - backers: default_backing_bitfield(), - backing_group: GroupIndex::from(0), - }, - ); - - *bare_bitfield.0.get_mut(0).unwrap() = true; - let signed = sign_bitfield( - &keystore, - &validators[0], - ValidatorIndex(0), - bare_bitfield, - &signing_context, - ); - - let checked_bitfields = simple_sanitize_bitfields( - vec![signed.into()], - DisputedBitfield::zeros(expected_bits()), - expected_bits(), - ); - assert_eq!(checked_bitfields.len(), 1, "No bitfields should have been filtered!"); - - let x = process_bitfields(expected_bits(), checked_bitfields, core_lookup); - // no core is freed - assert!(x.is_empty(), "No core should be freed."); } }); } @@ -667,13 +829,17 @@ fn availability_threshold_is_supermajority() { #[test] fn supermajority_bitfields_trigger_availability() { - let chain_a = ParaId::from(1_u32); - let chain_b = ParaId::from(2_u32); - let thread_a = ParaId::from(3_u32); + let chain_a = ParaId::from(0_u32); + let chain_b = ParaId::from(1_u32); + let chain_c = ParaId::from(2_u32); + let chain_d = ParaId::from(3_u32); + let thread_a = ParaId::from(4_u32); let paras = vec![ (chain_a, ParaKind::Parachain), (chain_b, ParaKind::Parachain), + (chain_c, ParaKind::Parachain), + (chain_d, ParaKind::Parachain), (thread_a, ParaKind::Parathread), ]; let validators = vec![ @@ -682,6 +848,8 @@ fn supermajority_bitfields_trigger_availability() { Sr25519Keyring::Charlie, Sr25519Keyring::Dave, Sr25519Keyring::Ferdie, + Sr25519Keyring::One, + Sr25519Keyring::Two, ]; let keystore: KeystorePtr = Arc::new(LocalKeystore::in_memory()); for validator in validators.iter() { @@ -701,13 +869,7 @@ fn supermajority_bitfields_trigger_availability() { let signing_context = SigningContext { parent_hash: System::parent_hash(), session_index: 5 }; - let core_lookup = |core| match core { - core if core == CoreIndex::from(0) => Some(chain_a), - core if core == CoreIndex::from(1) => Some(chain_b), - core if core == CoreIndex::from(2) => Some(thread_a), - _ => panic!("Core out of bounds for 2 parachains and 1 parathread core."), - }; - + // Chain A only has one candidate pending availability. It will be made available now. let candidate_a = TestCandidateBuilder { para_id: chain_a, head_data: vec![1, 2, 3, 4].into(), @@ -717,7 +879,7 @@ fn supermajority_bitfields_trigger_availability() { >::insert( chain_a, - CandidatePendingAvailability { + [CandidatePendingAvailability { core: CoreIndex::from(0), hash: candidate_a.hash(), descriptor: candidate_a.clone().descriptor, @@ -726,10 +888,13 @@ fn supermajority_bitfields_trigger_availability() { backed_in_number: 0, backers: backing_bitfield(&[3, 4]), backing_group: GroupIndex::from(0), - }, + commitments: candidate_a.clone().commitments, + }] + .into_iter() + .collect::>(), ); - PendingAvailabilityCommitments::::insert(chain_a, candidate_a.clone().commitments); + // Chain B only has one candidate pending availability. It won't be made available now. let candidate_b = TestCandidateBuilder { para_id: chain_b, head_data: vec![5, 6, 7, 8].into(), @@ -739,7 +904,7 @@ fn supermajority_bitfields_trigger_availability() { >::insert( chain_b, - CandidatePendingAvailability { + [CandidatePendingAvailability { core: CoreIndex::from(1), hash: candidate_b.hash(), descriptor: candidate_b.descriptor, @@ -748,40 +913,99 @@ fn supermajority_bitfields_trigger_availability() { backed_in_number: 0, backers: backing_bitfield(&[0, 2]), backing_group: GroupIndex::from(1), - }, + commitments: candidate_b.commitments, + }] + .into_iter() + .collect::>(), ); - PendingAvailabilityCommitments::::insert(chain_b, candidate_b.commitments); - // this bitfield signals that a and b are available. - let a_and_b_available = { - let mut bare_bitfield = default_bitfield(); - *bare_bitfield.0.get_mut(0).unwrap() = true; - *bare_bitfield.0.get_mut(1).unwrap() = true; + // Chain C has three candidates pending availability. The first and third candidates will be + // made available. Only the first candidate will be evicted from the core and enacted. + let candidate_c_1 = TestCandidateBuilder { + para_id: chain_c, + head_data: vec![7, 8].into(), + ..Default::default() + } + .build(); + let candidate_c_2 = TestCandidateBuilder { + para_id: chain_c, + head_data: vec![9, 10].into(), + ..Default::default() + } + .build(); + let candidate_c_3 = TestCandidateBuilder { + para_id: chain_c, + head_data: vec![11, 12].into(), + ..Default::default() + } + .build(); - bare_bitfield - }; + let mut c_candidates = VecDeque::new(); + c_candidates.push_back(CandidatePendingAvailability { + core: CoreIndex::from(2), + hash: candidate_c_1.hash(), + descriptor: candidate_c_1.descriptor.clone(), + availability_votes: default_availability_votes(), + relay_parent_number: 0, + backed_in_number: 0, + backers: backing_bitfield(&[1]), + backing_group: GroupIndex::from(2), + commitments: candidate_c_1.commitments.clone(), + }); + c_candidates.push_back(CandidatePendingAvailability { + core: CoreIndex::from(3), + hash: candidate_c_2.hash(), + descriptor: candidate_c_2.descriptor.clone(), + availability_votes: default_availability_votes(), + relay_parent_number: 0, + backed_in_number: 0, + backers: backing_bitfield(&[5]), + backing_group: GroupIndex::from(3), + commitments: candidate_c_2.commitments.clone(), + }); + c_candidates.push_back(CandidatePendingAvailability { + core: CoreIndex::from(4), + hash: candidate_c_3.hash(), + descriptor: candidate_c_3.descriptor.clone(), + availability_votes: default_availability_votes(), + relay_parent_number: 0, + backed_in_number: 0, + backers: backing_bitfield(&[6]), + backing_group: GroupIndex::from(4), + commitments: candidate_c_3.commitments.clone(), + }); - // this bitfield signals that only a is available. - let a_available = { + >::insert(chain_c, c_candidates); + + // this bitfield signals that a and b are available. + let all_available = { let mut bare_bitfield = default_bitfield(); - *bare_bitfield.0.get_mut(0).unwrap() = true; + for bit in 0..=4 { + *bare_bitfield.0.get_mut(bit).unwrap() = true; + } bare_bitfield }; let threshold = availability_threshold(validators.len()); - // 4 of 5 first value >= 2/3 - assert_eq!(threshold, 4); + // 5 of 7 first value >= 2/3 + assert_eq!(threshold, 5); let signed_bitfields = validators .iter() .enumerate() .filter_map(|(i, key)| { - let to_sign = if i < 3 { - a_and_b_available.clone() - } else if i < 4 { - a_available.clone() + let to_sign = if i < 4 { + all_available.clone() + } else if i < 5 { + // this bitfield signals that only a, c1 and c3 are available. + let mut bare_bitfield = default_bitfield(); + *bare_bitfield.0.get_mut(0).unwrap() = true; + *bare_bitfield.0.get_mut(2).unwrap() = true; + *bare_bitfield.0.get_mut(4).unwrap() = true; + + bare_bitfield } else { // sign nothing. return None @@ -808,46 +1032,129 @@ fn supermajority_bitfields_trigger_availability() { ); assert_eq!(checked_bitfields.len(), old_len, "No bitfields should have been filtered!"); - // only chain A's core is freed. - let v = process_bitfields(expected_bits(), checked_bitfields, core_lookup); - assert_eq!(vec![(CoreIndex(0), candidate_a.hash())], v); - - // chain A had 4 signing off, which is >= threshold. - // chain B has 3 signing off, which is < threshold. - assert!(>::get(&chain_a).is_none()); - assert!(>::get(&chain_a).is_none()); - assert!(>::get(&chain_b).is_some()); - assert_eq!(>::get(&chain_b).unwrap().availability_votes, { - // check that votes from first 3 were tracked. + // only chain A's core and candidate's C1 core are freed. + let v = process_bitfields(checked_bitfields); + assert_eq!( + vec![(CoreIndex(2), candidate_c_1.hash()), (CoreIndex(0), candidate_a.hash())], + v + ); + let votes = |bits: &[usize]| { let mut votes = default_availability_votes(); - *votes.get_mut(0).unwrap() = true; - *votes.get_mut(1).unwrap() = true; - *votes.get_mut(2).unwrap() = true; + for bit in bits { + *votes.get_mut(*bit).unwrap() = true; + } votes - }); + }; + + assert!(>::get(&chain_a).unwrap().is_empty()); + assert_eq!( + >::get(&chain_b) + .unwrap() + .pop_front() + .unwrap() + .availability_votes, + votes(&[0, 1, 2, 3]) + ); + let mut pending_c = >::get(&chain_c).unwrap(); + assert_eq!(pending_c.pop_front().unwrap().availability_votes, votes(&[0, 1, 2, 3])); + assert_eq!(pending_c.pop_front().unwrap().availability_votes, votes(&[0, 1, 2, 3, 4])); + assert!(pending_c.is_empty()); - // and check that chain head was enacted. + // and check that chain heads. assert_eq!(Paras::para_head(&chain_a), Some(vec![1, 2, 3, 4].into())); + assert_ne!(Paras::para_head(&chain_b), Some(vec![5, 6, 7, 8].into())); + assert_eq!(Paras::para_head(&chain_c), Some(vec![7, 8].into())); // Check that rewards are applied. { let rewards = crate::mock::availability_rewards(); - assert_eq!(rewards.len(), 4); - assert_eq!(rewards.get(&ValidatorIndex(0)).unwrap(), &1); - assert_eq!(rewards.get(&ValidatorIndex(1)).unwrap(), &1); - assert_eq!(rewards.get(&ValidatorIndex(2)).unwrap(), &1); + assert_eq!(rewards.len(), 5); + assert_eq!(rewards.get(&ValidatorIndex(0)).unwrap(), &2); + assert_eq!(rewards.get(&ValidatorIndex(1)).unwrap(), &2); + assert_eq!(rewards.get(&ValidatorIndex(2)).unwrap(), &2); + assert_eq!(rewards.get(&ValidatorIndex(3)).unwrap(), &2); + assert_eq!(rewards.get(&ValidatorIndex(4)).unwrap(), &2); + } + + { + let rewards = crate::mock::backing_rewards(); + + assert_eq!(rewards.len(), 3); assert_eq!(rewards.get(&ValidatorIndex(3)).unwrap(), &1); + assert_eq!(rewards.get(&ValidatorIndex(4)).unwrap(), &1); + assert_eq!(rewards.get(&ValidatorIndex(1)).unwrap(), &1); + } + + // Add a new bitfield which will make candidate C2 available also. This will also evict and + // enact C3. + let signed_bitfields = vec![sign_bitfield( + &keystore, + &validators[5], + ValidatorIndex(5), + { + let mut bare_bitfield = default_bitfield(); + *bare_bitfield.0.get_mut(3).unwrap() = true; + bare_bitfield + }, + &signing_context, + ) + .into()]; + + let old_len = signed_bitfields.len(); + let checked_bitfields = simple_sanitize_bitfields( + signed_bitfields, + DisputedBitfield::zeros(expected_bits()), + expected_bits(), + ); + assert_eq!(checked_bitfields.len(), old_len, "No bitfields should have been filtered!"); + + let v = process_bitfields(checked_bitfields); + assert_eq!( + vec![(CoreIndex(3), candidate_c_2.hash()), (CoreIndex(4), candidate_c_3.hash())], + v + ); + + assert!(>::get(&chain_a).unwrap().is_empty()); + assert_eq!( + >::get(&chain_b) + .unwrap() + .pop_front() + .unwrap() + .availability_votes, + votes(&[0, 1, 2, 3]) + ); + assert!(>::get(&chain_c).unwrap().is_empty()); + + // and check that chain heads. + assert_eq!(Paras::para_head(&chain_a), Some(vec![1, 2, 3, 4].into())); + assert_ne!(Paras::para_head(&chain_b), Some(vec![5, 6, 7, 8].into())); + assert_eq!(Paras::para_head(&chain_c), Some(vec![11, 12].into())); + + // Check that rewards are applied. + { + let rewards = crate::mock::availability_rewards(); + + assert_eq!(rewards.len(), 6); + assert_eq!(rewards.get(&ValidatorIndex(0)).unwrap(), &4); + assert_eq!(rewards.get(&ValidatorIndex(1)).unwrap(), &4); + assert_eq!(rewards.get(&ValidatorIndex(2)).unwrap(), &4); + assert_eq!(rewards.get(&ValidatorIndex(3)).unwrap(), &4); + assert_eq!(rewards.get(&ValidatorIndex(4)).unwrap(), &3); + assert_eq!(rewards.get(&ValidatorIndex(5)).unwrap(), &1); } { let rewards = crate::mock::backing_rewards(); - assert_eq!(rewards.len(), 2); + assert_eq!(rewards.len(), 5); assert_eq!(rewards.get(&ValidatorIndex(3)).unwrap(), &1); assert_eq!(rewards.get(&ValidatorIndex(4)).unwrap(), &1); + assert_eq!(rewards.get(&ValidatorIndex(1)).unwrap(), &1); + assert_eq!(rewards.get(&ValidatorIndex(5)).unwrap(), &1); + assert_eq!(rewards.get(&ValidatorIndex(6)).unwrap(), &1); } }); } @@ -872,6 +1179,7 @@ fn candidate_checks() { Sr25519Keyring::Charlie, Sr25519Keyring::Dave, Sr25519Keyring::Ferdie, + Sr25519Keyring::One, ]; let keystore: KeystorePtr = Arc::new(LocalKeystore::in_memory()); for validator in validators.iter() { @@ -898,7 +1206,8 @@ fn candidate_checks() { group_index if group_index == GroupIndex::from(0) => Some(vec![0, 1]), group_index if group_index == GroupIndex::from(1) => Some(vec![2, 3]), group_index if group_index == GroupIndex::from(2) => Some(vec![4]), - _ => panic!("Group index out of bounds for 2 parachains and 1 parathread core"), + group_index if group_index == GroupIndex::from(3) => Some(vec![5]), + _ => panic!("Group index out of bounds"), } .map(|m| m.into_iter().map(ValidatorIndex).collect::>()) }; @@ -908,51 +1217,29 @@ fn candidate_checks() { vec![ValidatorIndex(0), ValidatorIndex(1)], vec![ValidatorIndex(2), ValidatorIndex(3)], vec![ValidatorIndex(4)], + vec![ValidatorIndex(5)], ]; Scheduler::set_validator_groups(validator_groups); let thread_collator: CollatorId = Sr25519Keyring::Two.public().into(); let chain_a_assignment = (chain_a, CoreIndex::from(0)); - let chain_b_assignment = (chain_b, CoreIndex::from(1)); let thread_a_assignment = (thread_a, CoreIndex::from(2)); let allowed_relay_parents = default_allowed_relay_parent_tracker(); - // unscheduled candidate. - { - let mut candidate = TestCandidateBuilder { - para_id: chain_a, - relay_parent: System::parent_hash(), - pov_hash: Hash::repeat_byte(1), - persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(), - hrmp_watermark: RELAY_PARENT_NUM, - ..Default::default() - } - .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - - let backed = back_candidate( - candidate, - &validators, - group_validators(GroupIndex::from(0)).unwrap().as_ref(), - &keystore, - &signing_context, - BackingKind::Threshold, - ); - - assert_noop!( - ParaInclusion::process_candidates( - &allowed_relay_parents, - vec![backed], - &[chain_b_assignment].into_iter().collect(), - &group_validators, - ), - Error::::UnscheduledCandidate - ); - } + // no candidates. + assert_eq!( + ParaInclusion::process_candidates( + &allowed_relay_parents, + &BTreeMap::new(), + &group_validators, + false + ), + Ok(ProcessedCandidates::default()) + ); - // candidates out of order. + // Check candidate ordering { let mut candidate_a = TestCandidateBuilder { para_id: chain_a, @@ -963,19 +1250,37 @@ fn candidate_checks() { ..Default::default() } .build(); - let mut candidate_b = TestCandidateBuilder { + let mut candidate_b_1 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(2), persisted_validation_data_hash: make_vdata_hash(chain_b).unwrap(), hrmp_watermark: RELAY_PARENT_NUM, + head_data: HeadData(vec![1, 2, 3]), ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); - - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b); + // Make candidate b2 a child of b1. + let mut candidate_b_2 = TestCandidateBuilder { + para_id: chain_b, + relay_parent: System::parent_hash(), + pov_hash: Hash::repeat_byte(3), + persisted_validation_data_hash: make_persisted_validation_data_with_parent::( + RELAY_PARENT_NUM, + Default::default(), + candidate_b_1.commitments.head_data.clone(), + ) + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + head_data: HeadData(vec![5, 6, 7]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); + collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b_1); + collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b_2); let backed_a = back_candidate( candidate_a, @@ -984,26 +1289,105 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); - let backed_b = back_candidate( - candidate_b, + let backed_b_1 = back_candidate( + candidate_b_1.clone(), + &validators, + group_validators(GroupIndex::from(2)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + None, + ); + + let backed_b_2 = back_candidate( + candidate_b_2, &validators, group_validators(GroupIndex::from(1)).unwrap().as_ref(), &keystore, &signing_context, BackingKind::Threshold, + None, + ); + + // candidates are required to be sorted in dependency order. + assert_noop!( + ParaInclusion::process_candidates( + &allowed_relay_parents, + &vec![( + chain_b, + vec![ + (backed_b_2.clone(), CoreIndex(1)), + (backed_b_1.clone(), CoreIndex(2)) + ] + ),] + .into_iter() + .collect(), + &group_validators, + false + ), + Error::::ValidationDataHashMismatch + ); + + // candidates are no longer required to be sorted by core index. + ParaInclusion::process_candidates( + &allowed_relay_parents, + &vec![ + ( + chain_b, + vec![ + (backed_b_1.clone(), CoreIndex(2)), + (backed_b_2.clone(), CoreIndex(1)), + ], + ), + (chain_a_assignment.0, vec![(backed_a.clone(), chain_a_assignment.1)]), + ] + .into_iter() + .collect(), + &group_validators, + false, + ) + .unwrap(); + + // candidate does not build on top of the latest unincluded head + + let mut candidate_b_3 = TestCandidateBuilder { + para_id: chain_b, + relay_parent: System::parent_hash(), + pov_hash: Hash::repeat_byte(4), + persisted_validation_data_hash: make_persisted_validation_data_with_parent::( + RELAY_PARENT_NUM, + Default::default(), + candidate_b_1.commitments.head_data.clone(), + ) + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + head_data: HeadData(vec![8, 9]), + ..Default::default() + } + .build(); + collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b_3); + + let backed_b_3 = back_candidate( + candidate_b_3, + &validators, + group_validators(GroupIndex::from(3)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + None, ); - // out-of-order manifests as unscheduled. assert_noop!( ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed_b, backed_a], - &[chain_a_assignment, chain_b_assignment].into_iter().collect(), + &vec![(chain_b, vec![(backed_b_3, CoreIndex(3))])].into_iter().collect(), &group_validators, + false ), - Error::::ScheduledOutOfOrder + Error::::ValidationDataHashMismatch ); } @@ -1020,24 +1404,51 @@ fn candidate_checks() { .build(); collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + // Insufficient backing. let backed = back_candidate( - candidate, + candidate.clone(), &validators, group_validators(GroupIndex::from(0)).unwrap().as_ref(), &keystore, &signing_context, BackingKind::Lacking, + None, ); assert_noop!( ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed], - &[chain_a_assignment].into_iter().collect(), + &vec![(chain_a_assignment.0, vec![(backed, chain_a_assignment.1)])] + .into_iter() + .collect(), &group_validators, + false ), Error::::InsufficientBacking ); + + // Wrong backing group. + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(1)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + None, + ); + + assert_noop!( + ParaInclusion::process_candidates( + &allowed_relay_parents, + &vec![(chain_a_assignment.0, vec![(backed, chain_a_assignment.1)])] + .into_iter() + .collect(), + &group_validators, + false + ), + Error::::InvalidBacking + ); } // one of candidates is not based on allowed relay parent. @@ -1075,6 +1486,7 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); let backed_b = back_candidate( @@ -1084,14 +1496,20 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); assert_noop!( ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed_b, backed_a], - &[chain_a_assignment, chain_b_assignment].into_iter().collect(), + &vec![ + (chain_b_assignment.0, vec![(backed_b, chain_b_assignment.1)]), + (chain_a_assignment.0, vec![(backed_a, chain_a_assignment.1)]) + ] + .into_iter() + .collect(), &group_validators, + false ), Error::::DisallowedRelayParent ); @@ -1122,111 +1540,22 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); assert_noop!( ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed], - &[thread_a_assignment].into_iter().collect(), + &vec![(thread_a_assignment.0, vec![(backed, thread_a_assignment.1)])] + .into_iter() + .collect(), &group_validators, + false ), Error::::NotCollatorSigned ); } - // para occupied - reject. - { - let mut candidate = TestCandidateBuilder { - para_id: chain_a, - relay_parent: System::parent_hash(), - pov_hash: Hash::repeat_byte(1), - persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(), - hrmp_watermark: RELAY_PARENT_NUM, - ..Default::default() - } - .build(); - - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - - let backed = back_candidate( - candidate, - &validators, - group_validators(GroupIndex::from(0)).unwrap().as_ref(), - &keystore, - &signing_context, - BackingKind::Threshold, - ); - - let candidate = TestCandidateBuilder::default().build(); - >::insert( - &chain_a, - CandidatePendingAvailability { - core: CoreIndex::from(0), - hash: candidate.hash(), - descriptor: candidate.descriptor, - availability_votes: default_availability_votes(), - relay_parent_number: 3, - backed_in_number: 4, - backers: default_backing_bitfield(), - backing_group: GroupIndex::from(0), - }, - ); - >::insert(&chain_a, candidate.commitments); - - assert_noop!( - ParaInclusion::process_candidates( - &allowed_relay_parents, - vec![backed], - &[chain_a_assignment].into_iter().collect(), - &group_validators, - ), - Error::::CandidateScheduledBeforeParaFree - ); - - >::remove(&chain_a); - >::remove(&chain_a); - } - - // messed up commitments storage - do not panic - reject. - { - let mut candidate = TestCandidateBuilder { - para_id: chain_a, - relay_parent: System::parent_hash(), - pov_hash: Hash::repeat_byte(1), - persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(), - hrmp_watermark: RELAY_PARENT_NUM, - ..Default::default() - } - .build(); - - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - - // this is not supposed to happen - >::insert(&chain_a, candidate.commitments.clone()); - - let backed = back_candidate( - candidate, - &validators, - group_validators(GroupIndex::from(0)).unwrap().as_ref(), - &keystore, - &signing_context, - BackingKind::Threshold, - ); - - assert_noop!( - ParaInclusion::process_candidates( - &allowed_relay_parents, - vec![backed], - &[chain_a_assignment].into_iter().collect(), - &group_validators, - ), - Error::::CandidateScheduledBeforeParaFree - ); - - >::remove(&chain_a); - } - // interfering code upgrade - reject { let mut candidate = TestCandidateBuilder { @@ -1249,6 +1578,7 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); { @@ -1267,9 +1597,11 @@ fn candidate_checks() { assert_noop!( ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed], - &[chain_a_assignment].into_iter().collect(), + &vec![(chain_a_assignment.0, vec![(backed, chain_a_assignment.1)])] + .into_iter() + .collect(), &group_validators, + false ), Error::::PrematureCodeUpgrade ); @@ -1296,16 +1628,19 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); - assert_eq!( + assert_noop!( ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed], - &[chain_a_assignment].into_iter().collect(), + &vec![(chain_a_assignment.0, vec![(backed, chain_a_assignment.1)])] + .into_iter() + .collect(), &group_validators, + false, ), - Err(Error::::ValidationDataHashMismatch.into()), + Error::::ValidationDataHashMismatch ); } @@ -1331,14 +1666,17 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); assert_noop!( ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed], - &[chain_a_assignment].into_iter().collect(), + &vec![(chain_a_assignment.0, vec![(backed, chain_a_assignment.1)])] + .into_iter() + .collect(), &group_validators, + false ), Error::::InvalidValidationCodeHash ); @@ -1366,14 +1704,17 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); assert_noop!( ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed], - &[chain_a_assignment].into_iter().collect(), + &vec![(chain_a_assignment.0, vec![(backed, chain_a_assignment.1)])] + .into_iter() + .collect(), &group_validators, + false ), Error::::ParaHeadMismatch ); @@ -1486,6 +1827,7 @@ fn backing_works() { &keystore, &signing_context, BackingKind::Threshold, + None, ); let backed_b = back_candidate( @@ -1495,6 +1837,7 @@ fn backing_works() { &keystore, &signing_context, BackingKind::Threshold, + None, ); let backed_c = back_candidate( @@ -1504,15 +1847,25 @@ fn backing_works() { &keystore, &signing_context, BackingKind::Threshold, + None, ); - let backed_candidates = vec![backed_a.clone(), backed_b.clone(), backed_c]; + let backed_candidates = vec![ + (chain_a_assignment.0, vec![(backed_a, chain_a_assignment.1)]), + (chain_b_assignment.0, vec![(backed_b, chain_b_assignment.1)]), + (thread_a_assignment.0, vec![(backed_c, thread_a_assignment.1)]), + ] + .into_iter() + .collect::>(); + let get_backing_group_idx = { // the order defines the group implicitly for this test case let backed_candidates_with_groups = backed_candidates - .iter() + .values() .enumerate() - .map(|(idx, backed_candidate)| (backed_candidate.hash(), GroupIndex(idx as _))) + .map(|(idx, backed_candidates)| { + (backed_candidates.iter().next().unwrap().0.hash(), GroupIndex(idx as _)) + }) .collect::>(); move |candidate_hash_x: CandidateHash| -> Option { @@ -1531,11 +1884,9 @@ fn backing_works() { candidate_receipt_with_backing_validator_indices, } = ParaInclusion::process_candidates( &allowed_relay_parents, - backed_candidates.clone(), - &[chain_a_assignment, chain_b_assignment, thread_a_assignment] - .into_iter() - .collect(), + &backed_candidates, &group_validators, + false, ) .expect("candidates scheduled, in order, and backed"); @@ -1554,22 +1905,23 @@ fn backing_works() { CandidateHash, (CandidateReceipt, Vec<(ValidatorIndex, ValidityAttestation)>), >::new(); - backed_candidates.into_iter().for_each(|backed_candidate| { + backed_candidates.values().for_each(|backed_candidates| { + let backed_candidate = backed_candidates.iter().next().unwrap().0.clone(); let candidate_receipt_with_backers = intermediate .entry(backed_candidate.hash()) .or_insert_with(|| (backed_candidate.receipt(), Vec::new())); - - assert_eq!( - backed_candidate.validity_votes.len(), - backed_candidate.validator_indices.count_ones() - ); + let (validator_indices, None) = + backed_candidate.validator_indices_and_core_index(false) + else { + panic!("Expected no injected core index") + }; + assert_eq!(backed_candidate.validity_votes().len(), validator_indices.count_ones()); candidate_receipt_with_backers.1.extend( - backed_candidate - .validator_indices + validator_indices .iter() .enumerate() .filter(|(_, signed)| **signed) - .zip(backed_candidate.validity_votes.iter().cloned()) + .zip(backed_candidate.validity_votes().iter().cloned()) .filter_map(|((validator_index_within_group, _), attestation)| { let grp_idx = get_backing_group_idx(backed_candidate.hash()).unwrap(); group_validators(grp_idx).map(|validator_indices| { @@ -1605,20 +1957,21 @@ fn backing_works() { }; assert_eq!( >::get(&chain_a), - Some(CandidatePendingAvailability { - core: CoreIndex::from(0), - hash: candidate_a.hash(), - descriptor: candidate_a.descriptor, - availability_votes: default_availability_votes(), - relay_parent_number: System::block_number() - 1, - backed_in_number: System::block_number(), - backers, - backing_group: GroupIndex::from(0), - }) - ); - assert_eq!( - >::get(&chain_a), - Some(candidate_a.commitments), + Some( + [CandidatePendingAvailability { + core: CoreIndex::from(0), + hash: candidate_a.hash(), + descriptor: candidate_a.descriptor, + availability_votes: default_availability_votes(), + relay_parent_number: System::block_number() - 1, + backed_in_number: System::block_number(), + backers, + backing_group: GroupIndex::from(0), + commitments: candidate_a.commitments, + }] + .into_iter() + .collect::>() + ) ); let backers = { @@ -1630,38 +1983,321 @@ fn backing_works() { }; assert_eq!( >::get(&chain_b), - Some(CandidatePendingAvailability { - core: CoreIndex::from(1), - hash: candidate_b.hash(), - descriptor: candidate_b.descriptor, - availability_votes: default_availability_votes(), - relay_parent_number: System::block_number() - 1, - backed_in_number: System::block_number(), - backers, - backing_group: GroupIndex::from(1), - }) + Some( + [CandidatePendingAvailability { + core: CoreIndex::from(1), + hash: candidate_b.hash(), + descriptor: candidate_b.descriptor, + availability_votes: default_availability_votes(), + relay_parent_number: System::block_number() - 1, + backed_in_number: System::block_number(), + backers, + backing_group: GroupIndex::from(1), + commitments: candidate_b.commitments, + }] + .into_iter() + .collect::>() + ) + ); + + assert_eq!( + >::get(&thread_a), + Some( + [CandidatePendingAvailability { + core: CoreIndex::from(2), + hash: candidate_c.hash(), + descriptor: candidate_c.descriptor, + availability_votes: default_availability_votes(), + relay_parent_number: System::block_number() - 1, + backed_in_number: System::block_number(), + backers: backing_bitfield(&[4]), + backing_group: GroupIndex::from(2), + commitments: candidate_c.commitments + }] + .into_iter() + .collect::>() + ) + ); + }); +} + +#[test] +fn backing_works_with_elastic_scaling_mvp() { + let chain_a = ParaId::from(1_u32); + let chain_b = ParaId::from(2_u32); + let thread_a = ParaId::from(3_u32); + + // The block number of the relay-parent for testing. + const RELAY_PARENT_NUM: BlockNumber = 4; + + let paras = vec![ + (chain_a, ParaKind::Parachain), + (chain_b, ParaKind::Parachain), + (thread_a, ParaKind::Parathread), + ]; + let validators = vec![ + Sr25519Keyring::Alice, + Sr25519Keyring::Bob, + Sr25519Keyring::Charlie, + Sr25519Keyring::Dave, + Sr25519Keyring::Ferdie, + ]; + let keystore: KeystorePtr = Arc::new(LocalKeystore::in_memory()); + for validator in validators.iter() { + Keystore::sr25519_generate_new( + &*keystore, + PARACHAIN_KEY_TYPE_ID, + Some(&validator.to_seed()), + ) + .unwrap(); + } + let validator_public = validator_pubkeys(&validators); + + new_test_ext(genesis_config(paras)).execute_with(|| { + shared::Pallet::::set_active_validators_ascending(validator_public.clone()); + shared::Pallet::::set_session_index(5); + + run_to_block(5, |_| None); + + let signing_context = + SigningContext { parent_hash: System::parent_hash(), session_index: 5 }; + + let group_validators = |group_index: GroupIndex| { + match group_index { + group_index if group_index == GroupIndex::from(0) => Some(vec![0, 1]), + group_index if group_index == GroupIndex::from(1) => Some(vec![2, 3]), + group_index if group_index == GroupIndex::from(2) => Some(vec![4]), + _ => panic!("Group index out of bounds for 2 parachains and 1 parathread core"), + } + .map(|vs| vs.into_iter().map(ValidatorIndex).collect::>()) + }; + + // When processing candidates, we compute the group index from scheduler. + let validator_groups = vec![ + vec![ValidatorIndex(0), ValidatorIndex(1)], + vec![ValidatorIndex(2), ValidatorIndex(3)], + vec![ValidatorIndex(4)], + ]; + Scheduler::set_validator_groups(validator_groups); + + let allowed_relay_parents = default_allowed_relay_parent_tracker(); + + let mut candidate_a = TestCandidateBuilder { + para_id: chain_a, + relay_parent: System::parent_hash(), + pov_hash: Hash::repeat_byte(1), + persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(), + hrmp_watermark: RELAY_PARENT_NUM, + ..Default::default() + } + .build(); + collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); + + let mut candidate_b_1 = TestCandidateBuilder { + para_id: chain_b, + relay_parent: System::parent_hash(), + pov_hash: Hash::repeat_byte(2), + persisted_validation_data_hash: make_vdata_hash(chain_b).unwrap(), + hrmp_watermark: RELAY_PARENT_NUM, + ..Default::default() + } + .build(); + collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b_1); + + // Make candidate b2 a child of b1. + let mut candidate_b_2 = TestCandidateBuilder { + para_id: chain_b, + relay_parent: System::parent_hash(), + pov_hash: Hash::repeat_byte(3), + persisted_validation_data_hash: make_persisted_validation_data_with_parent::( + RELAY_PARENT_NUM, + Default::default(), + candidate_b_1.commitments.head_data.clone(), + ) + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + ..Default::default() + } + .build(); + collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b_2); + + let backed_a = back_candidate( + candidate_a.clone(), + &validators, + group_validators(GroupIndex::from(0)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + None, ); + + let backed_b_1 = back_candidate( + candidate_b_1.clone(), + &validators, + group_validators(GroupIndex::from(1)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + Some(CoreIndex(1)), + ); + + let backed_b_2 = back_candidate( + candidate_b_2.clone(), + &validators, + group_validators(GroupIndex::from(2)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + Some(CoreIndex(2)), + ); + + let mut backed_candidates = BTreeMap::new(); + backed_candidates.insert(chain_a, vec![(backed_a, CoreIndex(0))]); + backed_candidates + .insert(chain_b, vec![(backed_b_1, CoreIndex(1)), (backed_b_2, CoreIndex(2))]); + + let get_backing_group_idx = { + // the order defines the group implicitly for this test case + let backed_candidates_with_groups = backed_candidates + .values() + .enumerate() + .map(|(idx, backed_candidates)| { + backed_candidates + .iter() + .enumerate() + .map(|(i, c)| (c.0.hash(), GroupIndex((idx + i) as _))) + .collect() + }) + .collect::>>() + .concat(); + + move |candidate_hash_x: CandidateHash| -> Option { + backed_candidates_with_groups.iter().find_map(|(candidate_hash, grp)| { + if *candidate_hash == candidate_hash_x { + Some(*grp) + } else { + None + } + }) + } + }; + + let ProcessedCandidates { + core_indices: occupied_cores, + candidate_receipt_with_backing_validator_indices, + } = ParaInclusion::process_candidates( + &allowed_relay_parents, + &backed_candidates, + &group_validators, + true, + ) + .expect("candidates scheduled, in order, and backed"); + + // Both b candidates will be backed. assert_eq!( - >::get(&chain_b), - Some(candidate_b.commitments), + occupied_cores, + vec![ + (CoreIndex::from(0), chain_a), + (CoreIndex::from(1), chain_b), + (CoreIndex::from(2), chain_b), + ] ); + // Transform the votes into the setup we expect + let mut expected = std::collections::HashMap::< + CandidateHash, + (CandidateReceipt, Vec<(ValidatorIndex, ValidityAttestation)>), + >::new(); + backed_candidates.values().for_each(|backed_candidates| { + for backed_candidate in backed_candidates { + let backed_candidate = backed_candidate.0.clone(); + let candidate_receipt_with_backers = expected + .entry(backed_candidate.hash()) + .or_insert_with(|| (backed_candidate.receipt(), Vec::new())); + let (validator_indices, _maybe_core_index) = + backed_candidate.validator_indices_and_core_index(true); + assert_eq!(backed_candidate.validity_votes().len(), validator_indices.count_ones()); + candidate_receipt_with_backers.1.extend( + validator_indices + .iter() + .enumerate() + .filter(|(_, signed)| **signed) + .zip(backed_candidate.validity_votes().iter().cloned()) + .filter_map(|((validator_index_within_group, _), attestation)| { + let grp_idx = get_backing_group_idx(backed_candidate.hash()).unwrap(); + group_validators(grp_idx).map(|validator_indices| { + (validator_indices[validator_index_within_group], attestation) + }) + }), + ); + } + }); + assert_eq!( - >::get(&thread_a), - Some(CandidatePendingAvailability { - core: CoreIndex::from(2), - hash: candidate_c.hash(), - descriptor: candidate_c.descriptor, - availability_votes: default_availability_votes(), - relay_parent_number: System::block_number() - 1, - backed_in_number: System::block_number(), - backers: backing_bitfield(&[4]), - backing_group: GroupIndex::from(2), - }) + expected, + candidate_receipt_with_backing_validator_indices + .into_iter() + .map(|c| (c.0.hash(), c)) + .collect() + ); + + let backers = { + let num_backers = effective_minimum_backing_votes( + group_validators(GroupIndex(0)).unwrap().len(), + configuration::Pallet::::config().minimum_backing_votes, + ); + backing_bitfield(&(0..num_backers).collect::>()) + }; + assert_eq!( + >::get(&chain_a), + Some( + [CandidatePendingAvailability { + core: CoreIndex::from(0), + hash: candidate_a.hash(), + descriptor: candidate_a.descriptor, + availability_votes: default_availability_votes(), + relay_parent_number: System::block_number() - 1, + backed_in_number: System::block_number(), + backers, + backing_group: GroupIndex::from(0), + commitments: candidate_a.commitments + }] + .into_iter() + .collect::>() + ) ); + + // Both candidates of b will be recorded on chain. assert_eq!( - >::get(&thread_a), - Some(candidate_c.commitments), + >::get(&chain_b), + Some( + [ + CandidatePendingAvailability { + core: CoreIndex::from(1), + hash: candidate_b_1.hash(), + descriptor: candidate_b_1.descriptor, + availability_votes: default_availability_votes(), + relay_parent_number: System::block_number() - 1, + backed_in_number: System::block_number(), + backers: backing_bitfield(&[2, 3]), + backing_group: GroupIndex::from(1), + commitments: candidate_b_1.commitments + }, + CandidatePendingAvailability { + core: CoreIndex::from(2), + hash: candidate_b_2.hash(), + descriptor: candidate_b_2.descriptor, + availability_votes: default_availability_votes(), + relay_parent_number: System::block_number() - 1, + backed_in_number: System::block_number(), + backers: backing_bitfield(&[4]), + backing_group: GroupIndex::from(2), + commitments: candidate_b_2.commitments + } + ] + .into_iter() + .collect::>() + ) ); }); } @@ -1740,14 +2376,17 @@ fn can_include_candidate_with_ok_code_upgrade() { &keystore, &signing_context, BackingKind::Threshold, + None, ); let ProcessedCandidates { core_indices: occupied_cores, .. } = ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed_a], - &[chain_a_assignment].into_iter().collect(), - &group_validators, + &vec![(chain_a_assignment.0, vec![(backed_a, chain_a_assignment.1)])] + .into_iter() + .collect::>(), + group_validators, + false, ) .expect("candidates scheduled, in order, and backed"); @@ -1762,20 +2401,21 @@ fn can_include_candidate_with_ok_code_upgrade() { }; assert_eq!( >::get(&chain_a), - Some(CandidatePendingAvailability { - core: CoreIndex::from(0), - hash: candidate_a.hash(), - descriptor: candidate_a.descriptor, - availability_votes: default_availability_votes(), - relay_parent_number: System::block_number() - 1, - backed_in_number: System::block_number(), - backers, - backing_group: GroupIndex::from(0), - }) - ); - assert_eq!( - >::get(&chain_a), - Some(candidate_a.commitments), + Some( + [CandidatePendingAvailability { + core: CoreIndex::from(0), + hash: candidate_a.hash(), + descriptor: candidate_a.descriptor, + availability_votes: default_availability_votes(), + relay_parent_number: System::block_number() - 1, + backed_in_number: System::block_number(), + backers, + backing_group: GroupIndex::from(0), + commitments: candidate_a.commitments + }] + .into_iter() + .collect::>() + ) ); }); } @@ -1809,7 +2449,7 @@ fn check_allowed_relay_parents() { } let validator_public = validator_pubkeys(&validators); let mut config = genesis_config(paras); - config.configuration.config.group_rotation_frequency = 1; + config.configuration.config.scheduler_params.group_rotation_frequency = 1; new_test_ext(config).execute_with(|| { shared::Pallet::::set_active_validators_ascending(validator_public.clone()); @@ -1932,6 +2572,7 @@ fn check_allowed_relay_parents() { &keystore, &signing_context_a, BackingKind::Threshold, + None, ); let backed_b = back_candidate( @@ -1941,6 +2582,7 @@ fn check_allowed_relay_parents() { &keystore, &signing_context_b, BackingKind::Threshold, + None, ); let backed_c = back_candidate( @@ -1950,17 +2592,22 @@ fn check_allowed_relay_parents() { &keystore, &signing_context_c, BackingKind::Threshold, + None, ); - let backed_candidates = vec![backed_a, backed_b, backed_c]; + let backed_candidates = vec![ + (chain_a_assignment.0, vec![(backed_a, chain_a_assignment.1)]), + (chain_b_assignment.0, vec![(backed_b, chain_b_assignment.1)]), + (thread_a_assignment.0, vec![(backed_c, thread_a_assignment.1)]), + ] + .into_iter() + .collect::>(); ParaInclusion::process_candidates( &allowed_relay_parents, - backed_candidates.clone(), - &[chain_a_assignment, chain_b_assignment, thread_a_assignment] - .into_iter() - .collect(), + &backed_candidates, &group_validators, + false, ) .expect("candidates scheduled, in order, and backed"); }); @@ -2006,25 +2653,10 @@ fn session_change_wipes() { run_to_block(10, |_| None); - >::insert( - &ValidatorIndex(0), - AvailabilityBitfieldRecord { bitfield: default_bitfield(), submitted_at: 9 }, - ); - - >::insert( - &ValidatorIndex(1), - AvailabilityBitfieldRecord { bitfield: default_bitfield(), submitted_at: 9 }, - ); - - >::insert( - &ValidatorIndex(4), - AvailabilityBitfieldRecord { bitfield: default_bitfield(), submitted_at: 9 }, - ); - let candidate = TestCandidateBuilder::default().build(); >::insert( &chain_a, - CandidatePendingAvailability { + [CandidatePendingAvailability { core: CoreIndex::from(0), hash: candidate.hash(), descriptor: candidate.descriptor.clone(), @@ -2033,13 +2665,15 @@ fn session_change_wipes() { backed_in_number: 6, backers: default_backing_bitfield(), backing_group: GroupIndex::from(0), - }, + commitments: candidate.commitments.clone(), + }] + .into_iter() + .collect::>(), ); - >::insert(&chain_a, candidate.commitments.clone()); >::insert( &chain_b, - CandidatePendingAvailability { + [CandidatePendingAvailability { core: CoreIndex::from(1), hash: candidate.hash(), descriptor: candidate.descriptor, @@ -2048,22 +2682,18 @@ fn session_change_wipes() { backed_in_number: 7, backers: default_backing_bitfield(), backing_group: GroupIndex::from(1), - }, + commitments: candidate.commitments, + }] + .into_iter() + .collect::>(), ); - >::insert(&chain_b, candidate.commitments); run_to_block(11, |_| None); assert_eq!(shared::Pallet::::session_index(), 5); - assert!(>::get(&ValidatorIndex(0)).is_some()); - assert!(>::get(&ValidatorIndex(1)).is_some()); - assert!(>::get(&ValidatorIndex(4)).is_some()); - assert!(>::get(&chain_a).is_some()); assert!(>::get(&chain_b).is_some()); - assert!(>::get(&chain_a).is_some()); - assert!(>::get(&chain_b).is_some()); run_to_block(12, |n| match n { 12 => Some(SessionChangeNotification { @@ -2079,18 +2709,7 @@ fn session_change_wipes() { assert_eq!(shared::Pallet::::session_index(), 6); - assert!(>::get(&ValidatorIndex(0)).is_none()); - assert!(>::get(&ValidatorIndex(1)).is_none()); - assert!(>::get(&ValidatorIndex(4)).is_none()); - - assert!(>::get(&chain_a).is_none()); - assert!(>::get(&chain_b).is_none()); - assert!(>::get(&chain_a).is_none()); - assert!(>::get(&chain_b).is_none()); - - assert!(>::iter().collect::>().is_empty()); assert!(>::iter().collect::>().is_empty()); - assert!(>::iter().collect::>().is_empty()); }); } @@ -2162,11 +2781,6 @@ fn para_upgrade_delay_scheduled_from_inclusion() { ]]; Scheduler::set_validator_groups(validator_groups); - let core_lookup = |core| match core { - core if core == CoreIndex::from(0) => Some(chain_a), - _ => None, - }; - let allowed_relay_parents = default_allowed_relay_parent_tracker(); let chain_a_assignment = (chain_a, CoreIndex::from(0)); @@ -2189,14 +2803,17 @@ fn para_upgrade_delay_scheduled_from_inclusion() { &keystore, &signing_context, BackingKind::Threshold, + None, ); let ProcessedCandidates { core_indices: occupied_cores, .. } = ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed_a], - &[chain_a_assignment].into_iter().collect(), + &vec![(chain_a_assignment.0, vec![(backed_a, chain_a_assignment.1)])] + .into_iter() + .collect::>(), &group_validators, + false, ) .expect("candidates scheduled, in order, and backed"); @@ -2229,11 +2846,10 @@ fn para_upgrade_delay_scheduled_from_inclusion() { expected_bits(), ); - let v = process_bitfields(expected_bits(), checked_bitfields, core_lookup); + let v = process_bitfields(checked_bitfields); assert_eq!(vec![(CoreIndex(0), candidate_a.hash())], v); - assert!(>::get(&chain_a).is_none()); - assert!(>::get(&chain_a).is_none()); + assert!(>::get(&chain_a).unwrap().is_empty()); let active_vote_state = paras::Pallet::::active_vote_state(&new_validation_code_hash) .expect("prechecking must be initiated"); diff --git a/polkadot/runtime/parachains/src/mock.rs b/polkadot/runtime/parachains/src/mock.rs index 1925ca19501accce8148e23d5e34c8f6625097a2..7ed62a392e4e532b244b628d7f02a0dea2209c34 100644 --- a/polkadot/runtime/parachains/src/mock.rs +++ b/polkadot/runtime/parachains/src/mock.rs @@ -23,7 +23,7 @@ use crate::{ initializer, origin, paras, paras::ParaKind, paras_inherent, scheduler, - scheduler::common::{AssignmentProvider, AssignmentProviderConfig}, + scheduler::common::AssignmentProvider, session_info, shared, ParaId, }; use frame_support::pallet_prelude::*; @@ -103,7 +103,7 @@ parameter_types! { pub type AccountId = u64; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = BlockWeights; @@ -463,10 +463,6 @@ pub mod mock_assigner { pub(super) type MockAssignmentQueue = StorageValue<_, VecDeque, ValueQuery>; - #[pallet::storage] - pub(super) type MockProviderConfig = - StorageValue<_, AssignmentProviderConfig, OptionQuery>; - #[pallet::storage] pub(super) type MockCoreCount = StorageValue<_, u32, OptionQuery>; } @@ -478,12 +474,6 @@ pub mod mock_assigner { MockAssignmentQueue::::mutate(|queue| queue.push_back(assignment)); } - // This configuration needs to be customized to service `get_provider_config` in - // scheduler tests. - pub fn set_assignment_provider_config(config: AssignmentProviderConfig) { - MockProviderConfig::::set(Some(config)); - } - // Allows for customized core count in scheduler tests, rather than a core count // derived from on-demand config + parachain count. pub fn set_core_count(count: u32) { @@ -512,17 +502,6 @@ pub mod mock_assigner { // in the mock assigner. fn push_back_assignment(_assignment: Assignment) {} - // Gets the provider config we set earlier using `set_assignment_provider_config`, falling - // back to the on demand parachain configuration if none was set. - fn get_provider_config(_core_idx: CoreIndex) -> AssignmentProviderConfig { - match MockProviderConfig::::get() { - Some(config) => config, - None => AssignmentProviderConfig { - max_availability_timeouts: 1, - ttl: BlockNumber::from(5u32), - }, - } - } #[cfg(any(feature = "runtime-benchmarks", test))] fn get_mock_assignment(_: CoreIndex, para_id: ParaId) -> Assignment { Assignment::Bulk(para_id) diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index 39371391b1f09cc57d919ea85a7591689e2e04d6..017cd87f13b883b29fbe032f19ff38ce56bc47ae 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -1374,7 +1374,7 @@ impl Pallet { outgoing } - // note replacement of the code of para with given `id`, which occured in the + // note replacement of the code of para with given `id`, which occurred in the // context of the given relay-chain block number. provide the replaced code. // // `at` for para-triggered replacement is the block number of the relay-chain @@ -1756,7 +1756,7 @@ impl Pallet { // // - Empty value is treated as the current code is already inserted during the onboarding. // - // This is only an intermediate solution and should be fixed in foreseable future. + // This is only an intermediate solution and should be fixed in foreseeable future. // // [soaking issue]: https://github.com/paritytech/polkadot/issues/3918 let validation_code = diff --git a/polkadot/runtime/parachains/src/paras/tests.rs b/polkadot/runtime/parachains/src/paras/tests.rs index 24ea919ec8754a50d6fb9507739a48c8a3eb1120..39abd2367b72313d1542fa8d956dae7622abf783 100644 --- a/polkadot/runtime/parachains/src/paras/tests.rs +++ b/polkadot/runtime/parachains/src/paras/tests.rs @@ -17,7 +17,7 @@ use super::*; use frame_support::{assert_err, assert_ok, assert_storage_noop}; use keyring::Sr25519Keyring; -use primitives::{BlockNumber, PARACHAIN_KEY_TYPE_ID}; +use primitives::{vstaging::SchedulerParams, BlockNumber, PARACHAIN_KEY_TYPE_ID}; use sc_keystore::LocalKeystore; use sp_keystore::{Keystore, KeystorePtr}; use std::sync::Arc; @@ -909,7 +909,10 @@ fn full_parachain_cleanup_storage() { minimum_validation_upgrade_delay: 2, // Those are not relevant to this test. However, HostConfiguration is still a // subject for the consistency check. - paras_availability_period: 1, + scheduler_params: SchedulerParams { + paras_availability_period: 1, + ..Default::default() + }, ..Default::default() }, }, @@ -1207,7 +1210,7 @@ fn code_hash_at_returns_up_to_end_of_code_retention_period() { assert_eq!(Paras::past_code_meta(¶_id).upgrade_times, vec![upgrade_at(4, 10)]); assert_eq!(Paras::current_code(¶_id), Some(new_code.clone())); - // Make sure that the old code is available **before** the code retion period passes. + // Make sure that the old code is available **before** the code retention period passes. run_to_block(10 + code_retention_period, None); assert_eq!(Paras::code_by_hash(&old_code.hash()), Some(old_code.clone())); assert_eq!(Paras::code_by_hash(&new_code.hash()), Some(new_code.clone())); @@ -1714,7 +1717,7 @@ fn poke_unused_validation_code_doesnt_remove_code_with_users() { #[test] fn increase_code_ref_doesnt_have_allergy_on_add_trusted_validation_code() { - // Verify that accidential calling of increase_code_ref or decrease_code_ref does not lead + // Verify that accidental calling of increase_code_ref or decrease_code_ref does not lead // to a disaster. // NOTE that this test is extra paranoid, as it is not really possible to hit // `decrease_code_ref` without calling `increase_code_ref` first. diff --git a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs index 0f6b23ae1b39213f8afbd37798f8f7f31a024cf9..1b07acffb1549f60caa6a5172b46eca3c957cf4e 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs @@ -65,7 +65,7 @@ benchmarks! { .collect(); let scenario = BenchBuilder::::new() - .set_backed_and_concluding_cores(cores_with_backed) + .set_backed_and_concluding_paras(cores_with_backed) .build(); let mut benchmark = scenario.data.clone(); @@ -110,7 +110,7 @@ benchmarks! { .collect(); let scenario = BenchBuilder::::new() - .set_backed_and_concluding_cores(cores_with_backed.clone()) + .set_backed_and_concluding_paras(cores_with_backed.clone()) .build(); let mut benchmark = scenario.data.clone(); @@ -120,7 +120,7 @@ benchmarks! { // with `v` validity votes. // let votes = v as usize; let votes = min(scheduler::Pallet::::group_validators(GroupIndex::from(0)).unwrap().len(), v as usize); - assert_eq!(benchmark.backed_candidates.get(0).unwrap().validity_votes.len(), votes); + assert_eq!(benchmark.backed_candidates.get(0).unwrap().validity_votes().len(), votes); benchmark.bitfields.clear(); benchmark.disputes.clear(); @@ -145,10 +145,6 @@ benchmarks! { assert_eq!(backing_validators.1.len(), votes); } - assert_eq!( - inclusion::PendingAvailabilityCommitments::::iter().count(), - cores_with_backed.len() - ); assert_eq!( inclusion::PendingAvailability::::iter().count(), cores_with_backed.len() @@ -165,7 +161,7 @@ benchmarks! { .collect(); let scenario = BenchBuilder::::new() - .set_backed_and_concluding_cores(cores_with_backed.clone()) + .set_backed_and_concluding_paras(cores_with_backed.clone()) .set_code_upgrade(v) .build(); @@ -177,7 +173,7 @@ benchmarks! { // There is 1 backed assert_eq!(benchmark.backed_candidates.len(), 1); assert_eq!( - benchmark.backed_candidates.get(0).unwrap().validity_votes.len(), + benchmark.backed_candidates.get(0).unwrap().validity_votes().len(), votes, ); @@ -209,10 +205,6 @@ benchmarks! { ); } - assert_eq!( - inclusion::PendingAvailabilityCommitments::::iter().count(), - cores_with_backed.len() - ); assert_eq!( inclusion::PendingAvailability::::iter().count(), cores_with_backed.len() diff --git a/polkadot/runtime/parachains/src/paras_inherent/mod.rs b/polkadot/runtime/parachains/src/paras_inherent/mod.rs index 81e092f0a991cd216bac3c1d8e48ae8bcea59144..6a20a10a8d755efa8a0bed6843a5d3329a45b5c4 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/mod.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/mod.rs @@ -24,8 +24,7 @@ use crate::{ configuration, disputes::DisputesHandler, - inclusion, - inclusion::CandidateCheckContext, + inclusion::{self, CandidateCheckContext}, initializer, metrics::METRICS, paras, @@ -35,6 +34,7 @@ use crate::{ }; use bitvec::prelude::BitVec; use frame_support::{ + defensive, dispatch::{DispatchErrorWithPostInfo, PostDispatchInfo}, inherent::{InherentData, InherentIdentifier, MakeFatalError, ProvideInherent}, pallet_prelude::*, @@ -43,15 +43,14 @@ use frame_support::{ use frame_system::pallet_prelude::*; use pallet_babe::{self, ParentBlockRandomness}; use primitives::{ - effective_minimum_backing_votes, BackedCandidate, CandidateHash, CandidateReceipt, - CheckedDisputeStatementSet, CheckedMultiDisputeStatementSet, CoreIndex, DisputeStatementSet, - InherentData as ParachainsInherentData, MultiDisputeStatementSet, ScrapedOnChainVotes, - SessionIndex, SignedAvailabilityBitfields, SigningContext, UncheckedSignedAvailabilityBitfield, - UncheckedSignedAvailabilityBitfields, ValidatorId, ValidatorIndex, ValidityAttestation, - PARACHAINS_INHERENT_IDENTIFIER, + effective_minimum_backing_votes, vstaging::node_features::FeatureIndex, BackedCandidate, + CandidateHash, CandidateReceipt, CheckedDisputeStatementSet, CheckedMultiDisputeStatementSet, + CoreIndex, DisputeStatementSet, HeadData, InherentData as ParachainsInherentData, + MultiDisputeStatementSet, ScrapedOnChainVotes, SessionIndex, SignedAvailabilityBitfields, + SigningContext, UncheckedSignedAvailabilityBitfield, UncheckedSignedAvailabilityBitfields, + ValidatorId, ValidatorIndex, ValidityAttestation, PARACHAINS_INHERENT_IDENTIFIER, }; use rand::{seq::SliceRandom, SeedableRng}; - use scale_info::TypeInfo; use sp_runtime::traits::{Header as HeaderT, One}; use sp_std::{ @@ -135,16 +134,13 @@ pub mod pallet { /// The hash of the submitted parent header doesn't correspond to the saved block hash of /// the parent. InvalidParentHeader, - /// Disputed candidate that was concluded invalid. - CandidateConcludedInvalid, /// The data given to the inherent will result in an overweight block. InherentOverweight, - /// The ordering of dispute statements was invalid. - DisputeStatementsUnsortedOrDuplicates, - /// A dispute statement was invalid. - DisputeInvalid, - /// A candidate was backed by a disabled validator - BackedByDisabled, + /// A candidate was filtered during inherent execution. This should have only been done + /// during creation. + CandidatesFilteredDuringExecution, + /// Too many candidates supplied. + UnscheduledCandidate, } /// Whether the paras inherent was included within this block. @@ -232,35 +228,6 @@ pub mod pallet { } } - /// Collect all freed cores based on storage data. (i.e. append cores freed from timeouts to - /// the given `freed_concluded`). - /// - /// The parameter `freed_concluded` contains all core indicies that became - /// free due to candidate that became available. - pub(crate) fn collect_all_freed_cores( - freed_concluded: I, - ) -> BTreeMap - where - I: core::iter::IntoIterator, - T: Config, - { - // Handle timeouts for any availability core work. - let freed_timeout = if >::availability_timeout_check_required() { - let pred = >::availability_timeout_predicate(); - >::collect_pending(pred) - } else { - Vec::new() - }; - - // Schedule paras again, given freed cores, and reasons for freeing. - let freed = freed_concluded - .into_iter() - .map(|(c, _hash)| (c, FreedReason::Concluded)) - .chain(freed_timeout.into_iter().map(|c| (c, FreedReason::TimedOut))) - .collect::>(); - freed - } - #[pallet::call] impl Pallet { /// Enter the paras inherent. This will process bitfields and backed candidates. @@ -316,7 +283,7 @@ impl Pallet { /// Process inherent data. /// /// The given inherent data is processed and state is altered accordingly. If any data could - /// not be applied (inconsitencies, weight limit, ...) it is removed. + /// not be applied (inconsistencies, weight limit, ...) it is removed. /// /// When called from `create_inherent` the `context` must be set to /// `ProcessInherentDataContext::ProvideInherent` so it guarantees the invariant that inherent @@ -523,7 +490,7 @@ impl Pallet { // Contains the disputes that are concluded in the current session only, // since these are the only ones that are relevant for the occupied cores - // and lightens the load on `collect_disputed` significantly. + // and lightens the load on `free_disputed` significantly. // Cores can't be occupied with candidates of the previous sessions, and only // things with new votes can have just concluded. We only need to collect // cores with disputes that conclude just now, because disputes that @@ -539,21 +506,17 @@ impl Pallet { .map(|(_session, candidate)| candidate) .collect::>(); - let freed_disputed: BTreeMap = - >::collect_disputed(¤t_concluded_invalid_disputes) + // Get the cores freed as a result of concluded invalid candidates. + let (freed_disputed, concluded_invalid_hashes): (Vec, BTreeSet) = + >::free_disputed(¤t_concluded_invalid_disputes) .into_iter() - .map(|core| (core, FreedReason::Concluded)) - .collect(); + .unzip(); // Create a bit index from the set of core indices where each index corresponds to // a core index that was freed due to a dispute. // // I.e. 010100 would indicate, the candidates on Core 1 and 3 would be disputed. - let disputed_bitfield = create_disputed_bitfield(expected_bits, freed_disputed.keys()); - - if !freed_disputed.is_empty() { - >::free_cores_and_fill_claimqueue(freed_disputed.clone(), now); - } + let disputed_bitfield = create_disputed_bitfield(expected_bits, freed_disputed.iter()); let bitfields = sanitize_bitfields::( bitfields, @@ -568,11 +531,9 @@ impl Pallet { // Process new availability bitfields, yielding any availability cores whose // work has now concluded. let freed_concluded = - >::update_pending_availability_and_get_freed_cores::<_>( - expected_bits, + >::update_pending_availability_and_get_freed_cores( &validator_public[..], bitfields.clone(), - >::core_para, ); // Inform the disputes module of all included candidates. @@ -582,48 +543,64 @@ impl Pallet { METRICS.on_candidates_included(freed_concluded.len() as u64); - let freed = collect_all_freed_cores::(freed_concluded.iter().cloned()); + // Get the timed out candidates + let freed_timeout = if >::availability_timeout_check_required() { + >::free_timedout() + } else { + Vec::new() + }; + + if !freed_timeout.is_empty() { + log::debug!(target: LOG_TARGET, "Evicted timed out cores: {:?}", freed_timeout); + } + // We'll schedule paras again, given freed cores, and reasons for freeing. + let freed = freed_concluded + .into_iter() + .map(|(c, _hash)| (c, FreedReason::Concluded)) + .chain(freed_disputed.into_iter().map(|core| (core, FreedReason::Concluded))) + .chain(freed_timeout.into_iter().map(|c| (c, FreedReason::TimedOut))) + .collect::>(); >::free_cores_and_fill_claimqueue(freed, now); - let scheduled = >::scheduled_paras() - .map(|(core_idx, para_id)| (para_id, core_idx)) - .collect(); METRICS.on_candidates_processed_total(backed_candidates.len() as u64); - let SanitizedBackedCandidates { backed_candidates, votes_from_disabled_were_dropped } = - sanitize_backed_candidates::( - backed_candidates, - &allowed_relay_parents, - |candidate_idx: usize, - backed_candidate: &BackedCandidate<::Hash>| - -> bool { - let para_id = backed_candidate.descriptor().para_id; - let prev_context = >::para_most_recent_context(para_id); - let check_ctx = CandidateCheckContext::::new(prev_context); - - // never include a concluded-invalid candidate - current_concluded_invalid_disputes.contains(&backed_candidate.hash()) || - // Instead of checking the candidates with code upgrades twice - // move the checking up here and skip it in the training wheels fallback. - // That way we avoid possible duplicate checks while assuring all - // backed candidates fine to pass on. - // - // NOTE: this is the only place where we check the relay-parent. - check_ctx - .verify_backed_candidate(&allowed_relay_parents, candidate_idx, backed_candidate) - .is_err() - }, - &scheduled, - ); + let core_index_enabled = configuration::Pallet::::config() + .node_features + .get(FeatureIndex::ElasticScalingMVP as usize) + .map(|b| *b) + .unwrap_or(false); + + let mut scheduled: BTreeMap> = BTreeMap::new(); + let mut total_scheduled_cores = 0; + + for (core_idx, para_id) in >::scheduled_paras() { + total_scheduled_cores += 1; + scheduled.entry(para_id).or_default().insert(core_idx); + } + + let initial_candidate_count = backed_candidates.len(); + let backed_candidates_with_core = sanitize_backed_candidates::( + backed_candidates, + &allowed_relay_parents, + concluded_invalid_hashes, + scheduled, + core_index_enabled, + ); + let count = count_backed_candidates(&backed_candidates_with_core); + + ensure!(count <= total_scheduled_cores, Error::::UnscheduledCandidate); - METRICS.on_candidates_sanitized(backed_candidates.len() as u64); + METRICS.on_candidates_sanitized(count as u64); - // In `Enter` context (invoked during execution) there should be no backing votes from - // disabled validators because they should have been filtered out during inherent data - // preparation (`ProvideInherent` context). Abort in such cases. + // In `Enter` context (invoked during execution) no more candidates should be filtered, + // because they have already been filtered during `ProvideInherent` context. Abort in such + // cases. if context == ProcessInherentDataContext::Enter { - ensure!(!votes_from_disabled_were_dropped, Error::::BackedByDisabled); + ensure!( + initial_candidate_count == count, + Error::::CandidatesFilteredDuringExecution + ); } // Process backed candidates according to scheduled cores. @@ -632,9 +609,9 @@ impl Pallet { candidate_receipt_with_backing_validator_indices, } = >::process_candidates( &allowed_relay_parents, - backed_candidates.clone(), - &scheduled, + &backed_candidates_with_core, >::group_validators, + core_index_enabled, )?; // Note which of the scheduled cores were actually occupied by a backed candidate. >::occupied(occupied.into_iter().map(|e| (e.0, e.1)).collect()); @@ -651,8 +628,18 @@ impl Pallet { let bitfields = bitfields.into_iter().map(|v| v.into_unchecked()).collect(); - let processed = - ParachainsInherentData { bitfields, backed_candidates, disputes, parent_header }; + let processed = ParachainsInherentData { + bitfields, + backed_candidates: backed_candidates_with_core.into_iter().fold( + Vec::with_capacity(count), + |mut acc, (_id, candidates)| { + acc.extend(candidates.into_iter().map(|(c, _)| c)); + acc + }, + ), + disputes, + parent_header, + }; Ok((processed, Some(all_weight_after).into())) } } @@ -746,12 +733,12 @@ fn random_sel Weight>( /// are preferred. And for disputes, local and older disputes are preferred (see /// `limit_and_sanitize_disputes`). for backed candidates, since with a increasing number of /// parachains their chances of inclusion become slim. All backed candidates are checked -/// beforehands in `fn create_inherent_inner` which guarantees sanity. +/// beforehand in `fn create_inherent_inner` which guarantees sanity. /// /// Assumes disputes are already filtered by the time this is called. /// /// Returns the total weight consumed by `bitfields` and `candidates`. -fn apply_weight_limit( +pub(crate) fn apply_weight_limit( candidates: &mut Vec::Hash>>, bitfields: &mut UncheckedSignedAvailabilityBitfields, max_consumable_weight: Weight, @@ -768,35 +755,71 @@ fn apply_weight_limit( return total } - // Prefer code upgrades, they tend to be large and hence stand no chance to be picked - // late while maintaining the weight bounds. - let preferred_indices = candidates + // Invariant: block author provides candidate in the order in which they form a chain + // wrt elastic scaling. If the invariant is broken, we'd fail later when filtering candidates + // which are unchained. + + let mut chained_candidates: Vec> = Vec::new(); + let mut current_para_id = None; + + for candidate in sp_std::mem::take(candidates).into_iter() { + let candidate_para_id = candidate.descriptor().para_id; + if Some(candidate_para_id) == current_para_id { + let chain = chained_candidates + .last_mut() + .expect("if the current_para_id is Some, then vec is not empty; qed"); + chain.push(candidate); + } else { + current_para_id = Some(candidate_para_id); + chained_candidates.push(vec![candidate]); + } + } + + // Elastic scaling: we prefer chains that have a code upgrade among the candidates, + // as the candidates containing the upgrade tend to be large and hence stand no chance to + // be picked late while maintaining the weight bounds. + // + // Limitations: For simplicity if total weight of a chain of candidates is larger than + // the remaining weight, the chain will still not be included while it could still be possible + // to include part of that chain. + let preferred_chain_indices = chained_candidates .iter() .enumerate() - .filter_map(|(idx, candidate)| { - candidate.candidate.commitments.new_validation_code.as_ref().map(|_code| idx) + .filter_map(|(idx, candidates)| { + // Check if any of the candidate in chain contains a code upgrade. + if candidates + .iter() + .any(|candidate| candidate.candidate().commitments.new_validation_code.is_some()) + { + Some(idx) + } else { + None + } }) .collect::>(); - // There is weight remaining to be consumed by a subset of candidates + // There is weight remaining to be consumed by a subset of chained candidates // which are going to be picked now. if let Some(max_consumable_by_candidates) = max_consumable_weight.checked_sub(&total_bitfields_weight) { - let (acc_candidate_weight, indices) = - random_sel::::Hash>, _>( + let (acc_candidate_weight, chained_indices) = + random_sel::::Hash>>, _>( rng, - &candidates, - preferred_indices, - |c| backed_candidate_weight::(c), + &chained_candidates, + preferred_chain_indices, + |candidates| backed_candidates_weight::(&candidates), max_consumable_by_candidates, ); - log::debug!(target: LOG_TARGET, "Indices Candidates: {:?}, size: {}", indices, candidates.len()); - candidates.indexed_retain(|idx, _backed_candidate| indices.binary_search(&idx).is_ok()); + log::debug!(target: LOG_TARGET, "Indices Candidates: {:?}, size: {}", chained_indices, candidates.len()); + chained_candidates + .indexed_retain(|idx, _backed_candidates| chained_indices.binary_search(&idx).is_ok()); // pick all bitfields, and // fill the remaining space with candidates let total_consumed = acc_candidate_weight.saturating_add(total_bitfields_weight); + *candidates = chained_candidates.into_iter().flatten().collect::>(); + return total_consumed } @@ -913,77 +936,86 @@ pub(crate) fn sanitize_bitfields( bitfields } -// Result from `sanitize_backed_candidates` -#[derive(Debug, PartialEq)] -struct SanitizedBackedCandidates { - // Sanitized backed candidates. The `Vec` is sorted according to the occupied core index. - backed_candidates: Vec>, - // Set to true if any votes from disabled validators were dropped from the input. - votes_from_disabled_were_dropped: bool, -} - -/// Filter out: -/// 1. any candidates that have a concluded invalid dispute -/// 2. all backing votes from disabled validators -/// 3. any candidates that end up with less than `effective_minimum_backing_votes` backing votes -/// -/// `scheduled` follows the same naming scheme as provided in the -/// guide: Currently `free` but might become `occupied`. -/// For the filtering here the relevant part is only the current `free` -/// state. +/// Performs various filtering on the backed candidates inherent data. +/// Must maintain the invariant that the returned candidate collection contains the candidates +/// sorted in dependency order for each para. When doing any filtering, we must therefore drop any +/// subsequent candidates after the filtered one. /// -/// `candidate_has_concluded_invalid_dispute` must return `true` if the candidate -/// is disputed, false otherwise. The passed `usize` is the candidate index. +/// Filter out: +/// 1. any candidates which don't form a chain with the other candidates of the paraid (even if they +/// do form a chain but are not in the right order). +/// 2. any candidates that have a concluded invalid dispute or who are descendants of a concluded +/// invalid candidate. +/// 3. any unscheduled candidates, as well as candidates whose paraid has multiple cores assigned +/// but have no injected core index. +/// 4. all backing votes from disabled validators +/// 5. any candidates that end up with less than `effective_minimum_backing_votes` backing votes /// -/// Returns struct `SanitizedBackedCandidates` where `backed_candidates` are sorted according to the -/// occupied core index. -fn sanitize_backed_candidates< - T: crate::inclusion::Config, - F: FnMut(usize, &BackedCandidate) -> bool, ->( - mut backed_candidates: Vec>, +/// Returns the scheduled +/// backed candidates which passed filtering, mapped by para id and in the right dependency order. +fn sanitize_backed_candidates( + backed_candidates: Vec>, allowed_relay_parents: &AllowedRelayParentsTracker>, - mut candidate_has_concluded_invalid_dispute_or_is_invalid: F, - scheduled: &BTreeMap, -) -> SanitizedBackedCandidates { - // Remove any candidates that were concluded invalid. - // This does not assume sorting. - backed_candidates.indexed_retain(move |candidate_idx, backed_candidate| { - !candidate_has_concluded_invalid_dispute_or_is_invalid(candidate_idx, backed_candidate) - }); + concluded_invalid_with_descendants: BTreeSet, + scheduled: BTreeMap>, + core_index_enabled: bool, +) -> BTreeMap, CoreIndex)>> { + // Map the candidates to the right paraids, while making sure that the order between candidates + // of the same para is preserved. + let mut candidates_per_para: BTreeMap> = BTreeMap::new(); + for candidate in backed_candidates { + candidates_per_para + .entry(candidate.descriptor().para_id) + .or_default() + .push(candidate); + } - // Assure the backed candidate's `ParaId`'s core is free. - // This holds under the assumption that `Scheduler::schedule` is called _before_. - // We don't check the relay-parent because this is done in the closure when - // constructing the inherent and during actual processing otherwise. + // Check that candidates pertaining to the same para form a chain. Drop the ones that + // don't, along with the rest of candidates which follow them in the input vector. + filter_unchained_candidates::(&mut candidates_per_para, allowed_relay_parents); - backed_candidates.retain(|backed_candidate| { - let desc = backed_candidate.descriptor(); + // Remove any candidates that were concluded invalid or who are descendants of concluded invalid + // candidates (along with their descendants). + retain_candidates::(&mut candidates_per_para, |_, candidate| { + let keep = !concluded_invalid_with_descendants.contains(&candidate.candidate().hash()); - scheduled.get(&desc.para_id).is_some() + if !keep { + log::debug!( + target: LOG_TARGET, + "Found backed candidate {:?} which was concluded invalid or is a descendant of a concluded invalid candidate, for paraid {:?}.", + candidate.candidate().hash(), + candidate.descriptor().para_id + ); + } + keep }); - // Filter out backing statements from disabled validators - let dropped_disabled = filter_backed_statements_from_disabled_validators::( - &mut backed_candidates, + // Map candidates to scheduled cores. Filter out any unscheduled candidates along with their + // descendants. + let mut backed_candidates_with_core = map_candidates_to_cores::( &allowed_relay_parents, scheduled, + core_index_enabled, + candidates_per_para, ); - // Sort the `Vec` last, once there is a guarantee that these - // `BackedCandidates` references the expected relay chain parent, - // but more importantly are scheduled for a free core. - // This both avoids extra work for obviously invalid candidates, - // but also allows this to be done in place. - backed_candidates.sort_by(|x, y| { - // Never panics, since we filtered all panic arguments out in the previous `fn retain`. - scheduled[&x.descriptor().para_id].cmp(&scheduled[&y.descriptor().para_id]) - }); + // Filter out backing statements from disabled validators. If by that we render a candidate with + // less backing votes than required, filter that candidate also. As all the other filtering + // operations above, we drop the descendants of the dropped candidates also. + filter_backed_statements_from_disabled_validators::( + &mut backed_candidates_with_core, + &allowed_relay_parents, + core_index_enabled, + ); - SanitizedBackedCandidates { - backed_candidates, - votes_from_disabled_were_dropped: dropped_disabled, - } + backed_candidates_with_core +} + +fn count_backed_candidates(backed_candidates: &BTreeMap>) -> usize { + backed_candidates.iter().fold(0, |mut count, (_id, candidates)| { + count += candidates.len(); + count + }) } /// Derive entropy from babe provided per block randomness. @@ -1038,7 +1070,7 @@ fn limit_and_sanitize_disputes< log::debug!(target: LOG_TARGET, "Above max consumable weight: {}/{}", disputes_weight, max_consumable_weight); let mut checked_acc = Vec::::with_capacity(disputes.len()); - // Accumualated weight of all disputes picked, that passed the checks. + // Accumulated weight of all disputes picked, that passed the checks. let mut weight_acc = Weight::zero(); // Select disputes in-order until the remaining weight is attained @@ -1067,56 +1099,87 @@ fn limit_and_sanitize_disputes< } } -// Filters statements from disabled validators in `BackedCandidate`, non-scheduled candidates and -// few more sanity checks. Returns `true` if at least one statement is removed and `false` -// otherwise. -fn filter_backed_statements_from_disabled_validators( - backed_candidates: &mut Vec::Hash>>, +// Helper function for filtering candidates which don't pass the given predicate. When/if the first +// candidate which failes the predicate is found, all the other candidates that follow are dropped. +fn retain_candidates< + T: inclusion::Config + paras::Config + inclusion::Config, + F: FnMut(ParaId, &mut C) -> bool, + C, +>( + candidates_per_para: &mut BTreeMap>, + mut pred: F, +) { + for (para_id, candidates) in candidates_per_para.iter_mut() { + let mut latest_valid_idx = None; + + for (idx, candidate) in candidates.iter_mut().enumerate() { + if pred(*para_id, candidate) { + // Found a valid candidate. + latest_valid_idx = Some(idx); + } else { + break + } + } + + if let Some(latest_valid_idx) = latest_valid_idx { + candidates.truncate(latest_valid_idx + 1); + } else { + candidates.clear(); + } + } + + candidates_per_para.retain(|_, c| !c.is_empty()); +} + +// Filters statements from disabled validators in `BackedCandidate` and does a few more sanity +// checks. +fn filter_backed_statements_from_disabled_validators< + T: shared::Config + scheduler::Config + inclusion::Config, +>( + backed_candidates_with_core: &mut BTreeMap< + ParaId, + Vec<(BackedCandidate<::Hash>, CoreIndex)>, + >, allowed_relay_parents: &AllowedRelayParentsTracker>, - scheduled: &BTreeMap, -) -> bool { + core_index_enabled: bool, +) { let disabled_validators = BTreeSet::<_>::from_iter(shared::Pallet::::disabled_validators().into_iter()); if disabled_validators.is_empty() { // No disabled validators - nothing to do - return false + return } - let backed_len_before = backed_candidates.len(); - - // Flag which will be returned. Set to `true` if at least one vote is filtered. - let mut filtered = false; - let minimum_backing_votes = configuration::Pallet::::config().minimum_backing_votes; // Process all backed candidates. `validator_indices` in `BackedCandidates` are indices within // the validator group assigned to the parachain. To obtain this group we need: // 1. Core index assigned to the parachain which has produced the candidate // 2. The relay chain block number of the candidate - backed_candidates.retain_mut(|bc| { - // Get `core_idx` assigned to the `para_id` of the candidate - let core_idx = match scheduled.get(&bc.descriptor().para_id) { - Some(core_idx) => *core_idx, - None => { - log::debug!(target: LOG_TARGET, "Can't get core idx of a backed candidate for para id {:?}. Dropping the candidate.", bc.descriptor().para_id); - return false - } - }; - - // Get relay parent block number of the candidate. We need this to get the group index assigned to this core at this block number - let relay_parent_block_number = match allowed_relay_parents - .acquire_info(bc.descriptor().relay_parent, None) { + retain_candidates::(backed_candidates_with_core, |para_id, (bc, core_idx)| { + let (validator_indices, maybe_core_index) = + bc.validator_indices_and_core_index(core_index_enabled); + let mut validator_indices = BitVec::<_>::from(validator_indices); + + // Get relay parent block number of the candidate. We need this to get the group index + // assigned to this core at this block number + let relay_parent_block_number = + match allowed_relay_parents.acquire_info(bc.descriptor().relay_parent, None) { Some((_, block_num)) => block_num, None => { - log::debug!(target: LOG_TARGET, "Relay parent {:?} for candidate is not in the allowed relay parents. Dropping the candidate.", bc.descriptor().relay_parent); + log::debug!( + target: LOG_TARGET, + "Relay parent {:?} for candidate is not in the allowed relay parents. Dropping the candidate.", + bc.descriptor().relay_parent + ); return false - } + }, }; // Get the group index for the core let group_idx = match >::group_assigned_to_core( - core_idx, + *core_idx, relay_parent_block_number + One::one(), ) { Some(group_idx) => group_idx, @@ -1132,39 +1195,301 @@ fn filter_backed_statements_from_disabled_validators { log::debug!(target: LOG_TARGET, "Can't get the validators from group {:?}. Dropping the candidate.", group_idx); return false - } + }, }; // Bitmask with the disabled indices within the validator group - let disabled_indices = BitVec::::from_iter(validator_group.iter().map(|idx| disabled_validators.contains(idx))); - // The indices of statements from disabled validators in `BackedCandidate`. We have to drop these. - let indices_to_drop = disabled_indices.clone() & &bc.validator_indices; + let disabled_indices = BitVec::::from_iter( + validator_group.iter().map(|idx| disabled_validators.contains(idx)), + ); + // The indices of statements from disabled validators in `BackedCandidate`. We have to drop + // these. + let indices_to_drop = disabled_indices.clone() & &validator_indices; // Apply the bitmask to drop the disabled validator from `validator_indices` - bc.validator_indices &= !disabled_indices; + validator_indices &= !disabled_indices; + // Update the backed candidate + bc.set_validator_indices_and_core_index(validator_indices, maybe_core_index); + // Remove the corresponding votes from `validity_votes` for idx in indices_to_drop.iter_ones().rev() { - bc.validity_votes.remove(idx); - } - - // If at least one statement was dropped we need to return `true` - if indices_to_drop.count_ones() > 0 { - filtered = true; + bc.validity_votes_mut().remove(idx); } // By filtering votes we might render the candidate invalid and cause a failure in // [`process_candidates`]. To avoid this we have to perform a sanity check here. If there // are not enough backing votes after filtering we will remove the whole candidate. - if bc.validity_votes.len() < effective_minimum_backing_votes( - validator_group.len(), - minimum_backing_votes + if bc.validity_votes().len() < + effective_minimum_backing_votes(validator_group.len(), minimum_backing_votes) + { + log::debug!( + target: LOG_TARGET, + "Dropping candidate {:?} of paraid {:?} because it was left with too few backing votes after votes from disabled validators were filtered.", + bc.candidate().hash(), + para_id + ); - ) { return false } true }); +} + +// Check that candidates pertaining to the same para form a chain. Drop the ones that +// don't, along with the rest of candidates which follow them in the input vector. +// In the process, duplicated candidates will also be dropped (even if they form a valid cycle; +// cycles are not allowed if they entail backing duplicated candidates). +fn filter_unchained_candidates( + candidates: &mut BTreeMap>>, + allowed_relay_parents: &AllowedRelayParentsTracker>, +) { + let mut para_latest_head_data: BTreeMap = BTreeMap::new(); + for para_id in candidates.keys() { + let latest_head_data = match >::para_latest_head_data(¶_id) { + None => { + defensive!("Latest included head data for paraid {:?} is None", para_id); + continue + }, + Some(latest_head_data) => latest_head_data, + }; + para_latest_head_data.insert(*para_id, latest_head_data); + } + + let mut para_visited_candidates: BTreeMap> = BTreeMap::new(); + + retain_candidates::(candidates, |para_id, candidate| { + let Some(latest_head_data) = para_latest_head_data.get(¶_id) else { return false }; + let candidate_hash = candidate.candidate().hash(); + + let visited_candidates = + para_visited_candidates.entry(para_id).or_insert_with(|| BTreeSet::new()); + if visited_candidates.contains(&candidate_hash) { + log::debug!( + target: LOG_TARGET, + "Found duplicate candidates for paraid {:?}. Dropping the candidates with hash {:?}", + para_id, + candidate_hash + ); + + // If we got a duplicate candidate, stop. + return false + } else { + visited_candidates.insert(candidate_hash); + } - // Also return `true` if a whole candidate was dropped from the set - filtered || backed_len_before != backed_candidates.len() + let prev_context = >::para_most_recent_context(para_id); + let check_ctx = CandidateCheckContext::::new(prev_context); + + let res = match check_ctx.verify_backed_candidate( + &allowed_relay_parents, + candidate.candidate(), + latest_head_data.clone(), + ) { + Ok(_) => true, + Err(err) => { + log::debug!( + target: LOG_TARGET, + "Backed candidate verification for candidate {:?} of paraid {:?} failed with {:?}", + candidate_hash, + para_id, + err + ); + false + }, + }; + + if res { + para_latest_head_data + .insert(para_id, candidate.candidate().commitments.head_data.clone()); + } + + res + }); +} + +/// Map candidates to scheduled cores. +/// If the para only has one scheduled core and one candidate supplied, map the candidate to the +/// single core. If the para has multiple cores scheduled, only map the candidates which have a +/// proper core injected. Filter out the rest. +/// Also returns whether or not we dropped any candidates. +/// When dropping a candidate of a para, we must drop all subsequent candidates from that para +/// (because they form a chain). +fn map_candidates_to_cores( + allowed_relay_parents: &AllowedRelayParentsTracker>, + mut scheduled: BTreeMap>, + core_index_enabled: bool, + candidates: BTreeMap>>, +) -> BTreeMap, CoreIndex)>> { + let mut backed_candidates_with_core = BTreeMap::new(); + + for (para_id, backed_candidates) in candidates.into_iter() { + if backed_candidates.len() == 0 { + defensive!("Backed candidates for paraid {} is empty.", para_id); + continue + } + + let scheduled_cores = scheduled.get_mut(¶_id); + + // ParaIds without scheduled cores are silently filtered out. + if let Some(scheduled_cores) = scheduled_cores { + if scheduled_cores.len() == 0 { + log::debug!( + target: LOG_TARGET, + "Paraid: {:?} has no scheduled cores but {} candidates were supplied.", + para_id, + backed_candidates.len() + ); + + // Non-elastic scaling case. One core per para. + } else if scheduled_cores.len() == 1 && !core_index_enabled { + backed_candidates_with_core.insert( + para_id, + vec![( + // We need the first one here, as we assume candidates of a para are in + // dependency order. + backed_candidates.into_iter().next().expect("Length is at least 1"), + scheduled_cores.pop_first().expect("Length is 1"), + )], + ); + continue; + + // Elastic scaling case. We only allow candidates which have the right core + // indices injected. + } else if scheduled_cores.len() >= 1 && core_index_enabled { + // We must preserve the dependency order given in the input. + let mut temp_backed_candidates = Vec::with_capacity(scheduled_cores.len()); + + for candidate in backed_candidates { + if scheduled_cores.len() == 0 { + // We've got candidates for all of this para's assigned cores. Move on to + // the next para. + log::debug!( + target: LOG_TARGET, + "Found enough candidates for paraid: {:?}.", + candidate.descriptor().para_id + ); + break; + } + let maybe_injected_core_index: Option = + get_injected_core_index::(allowed_relay_parents, &candidate); + + if let Some(core_index) = maybe_injected_core_index { + if scheduled_cores.remove(&core_index) { + temp_backed_candidates.push((candidate, core_index)); + } else { + // if we got a candidate for a core index which is not scheduled, stop + // the work for this para. the already processed candidate chain in + // temp_backed_candidates is still fine though. + log::debug!( + target: LOG_TARGET, + "Found a backed candidate {:?} with injected core index {}, which is not scheduled for paraid {:?}.", + candidate.candidate().hash(), + core_index.0, + candidate.descriptor().para_id + ); + + break; + } + } else { + // if we got a candidate which does not contain its core index, stop the + // work for this para. the already processed candidate chain in + // temp_backed_candidates is still fine though. + + log::debug!( + target: LOG_TARGET, + "Found a backed candidate {:?} with no injected core index, for paraid {:?} which has multiple scheduled cores.", + candidate.candidate().hash(), + candidate.descriptor().para_id + ); + + break; + } + } + + if !temp_backed_candidates.is_empty() { + backed_candidates_with_core + .entry(para_id) + .or_insert_with(|| vec![]) + .extend(temp_backed_candidates); + } + } else { + log::warn!( + target: LOG_TARGET, + "Found a paraid {:?} which has multiple scheduled cores but ElasticScalingMVP feature is not enabled: {:?}", + para_id, + scheduled_cores + ); + } + } else { + log::debug!( + target: LOG_TARGET, + "Paraid: {:?} has no scheduled cores but {} candidates were supplied.", + para_id, + backed_candidates.len() + ); + } + } + + backed_candidates_with_core +} + +fn get_injected_core_index( + allowed_relay_parents: &AllowedRelayParentsTracker>, + candidate: &BackedCandidate, +) -> Option { + // After stripping the 8 bit extensions, the `validator_indices` field length is expected + // to be equal to backing group size. If these don't match, the `CoreIndex` is badly encoded, + // or not supported. + let (validator_indices, maybe_core_idx) = candidate.validator_indices_and_core_index(true); + + let Some(core_idx) = maybe_core_idx else { return None }; + + let relay_parent_block_number = + match allowed_relay_parents.acquire_info(candidate.descriptor().relay_parent, None) { + Some((_, block_num)) => block_num, + None => { + log::debug!( + target: LOG_TARGET, + "Relay parent {:?} for candidate {:?} is not in the allowed relay parents.", + candidate.descriptor().relay_parent, + candidate.candidate().hash(), + ); + return None + }, + }; + + // Get the backing group of the candidate backed at `core_idx`. + let group_idx = match >::group_assigned_to_core( + core_idx, + relay_parent_block_number + One::one(), + ) { + Some(group_idx) => group_idx, + None => { + log::debug!( + target: LOG_TARGET, + "Can't get the group index for core idx {:?}.", + core_idx, + ); + return None + }, + }; + + let group_validators = match >::group_validators(group_idx) { + Some(validators) => validators, + None => return None, + }; + + if group_validators.len() == validator_indices.len() { + Some(core_idx) + } else { + log::debug!( + target: LOG_TARGET, + "Expected validator_indices count different than the real one: {}, {} for candidate {:?}", + group_validators.len(), + validator_indices.len(), + candidate.candidate().hash() + ); + + None + } } diff --git a/polkadot/runtime/parachains/src/paras_inherent/tests.rs b/polkadot/runtime/parachains/src/paras_inherent/tests.rs index 6f3eac35685a82b32c69b27e5379620988c956b7..c5e65622c76efe44084c52154b1dfe27645f85d4 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/tests.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/tests.rs @@ -16,21 +16,48 @@ use super::*; +use crate::{ + configuration::{self, HostConfiguration}, + mock::MockGenesisConfig, +}; +use primitives::vstaging::SchedulerParams; + +fn default_config() -> MockGenesisConfig { + MockGenesisConfig { + configuration: configuration::GenesisConfig { + config: HostConfiguration { + max_head_data_size: 0b100000, + scheduler_params: SchedulerParams { + group_rotation_frequency: u32::MAX, + ..Default::default() + }, + ..Default::default() + }, + }, + ..Default::default() + } +} + // In order to facilitate benchmarks as tests we have a benchmark feature gated `WeightInfo` impl // that uses 0 for all the weights. Because all the weights are 0, the tests that rely on // weights for limiting data will fail, so we don't run them when using the benchmark feature. #[cfg(not(feature = "runtime-benchmarks"))] mod enter { - - use super::*; + use super::{inclusion::tests::TestCandidateBuilder, *}; use crate::{ builder::{Bench, BenchBuilder}, - mock::{mock_assigner, new_test_ext, BlockLength, BlockWeights, MockGenesisConfig, Test}, - scheduler::common::Assignment, + mock::{mock_assigner, new_test_ext, BlockLength, BlockWeights, RuntimeOrigin, Test}, + scheduler::{ + common::{Assignment, AssignmentProvider}, + ParasEntry, + }, + session_info, }; use assert_matches::assert_matches; + use core::panic; use frame_support::assert_ok; use frame_system::limits; + use primitives::{vstaging::SchedulerParams, AvailabilityBitfield, UncheckedSigned}; use sp_runtime::Perbill; use sp_std::collections::btree_map::BTreeMap; @@ -41,6 +68,8 @@ mod enter { num_validators_per_core: u32, code_upgrade: Option, fill_claimqueue: bool, + elastic_paras: BTreeMap, + unavailable_cores: Vec, } fn make_inherent_data( @@ -51,25 +80,39 @@ mod enter { num_validators_per_core, code_upgrade, fill_claimqueue, + elastic_paras, + unavailable_cores, }: TestConfig, ) -> Bench { + let extra_cores = elastic_paras + .values() + .map(|count| *count as usize) + .sum::() + .saturating_sub(elastic_paras.len() as usize); + let total_cores = dispute_sessions.len() + backed_and_concluding.len() + extra_cores; + let builder = BenchBuilder::::new() - .set_max_validators( - (dispute_sessions.len() + backed_and_concluding.len()) as u32 * - num_validators_per_core, - ) + .set_max_validators((total_cores) as u32 * num_validators_per_core) + .set_elastic_paras(elastic_paras.clone()) .set_max_validators_per_core(num_validators_per_core) .set_dispute_statements(dispute_statements) - .set_backed_and_concluding_cores(backed_and_concluding) + .set_backed_and_concluding_paras(backed_and_concluding.clone()) .set_dispute_sessions(&dispute_sessions[..]) - .set_fill_claimqueue(fill_claimqueue); + .set_fill_claimqueue(fill_claimqueue) + .set_unavailable_cores(unavailable_cores); // Setup some assignments as needed: mock_assigner::Pallet::::set_core_count(builder.max_cores()); - for core_index in 0..builder.max_cores() { - // Core index == para_id in this case - mock_assigner::Pallet::::add_test_assignment(Assignment::Bulk(core_index.into())); - } + + (0..(builder.max_cores() as usize - extra_cores)).for_each(|para_id| { + (0..elastic_paras.get(&(para_id as u32)).cloned().unwrap_or(1)).for_each( + |_para_local_core_idx| { + mock_assigner::Pallet::::add_test_assignment(Assignment::Bulk( + para_id.into(), + )); + }, + ); + }); if let Some(code_size) = code_upgrade { builder.set_code_upgrade(code_size).build() @@ -84,7 +127,7 @@ mod enter { // `create_inherent` and will not cause `enter` to early. fn include_backed_candidates() { let config = MockGenesisConfig::default(); - assert!(config.configuration.config.scheduling_lookahead > 0); + assert!(config.configuration.config.scheduler_params.lookahead > 0); new_test_ext(config).execute_with(|| { let dispute_statements = BTreeMap::new(); @@ -100,6 +143,8 @@ mod enter { num_validators_per_core: 1, code_upgrade: None, fill_claimqueue: false, + elastic_paras: BTreeMap::new(), + unavailable_cores: vec![], }); // We expect the scenario to have cores 0 & 1 with pending availability. The backed @@ -141,6 +186,305 @@ mod enter { Pallet::::on_chain_votes().unwrap().session, 2 ); + + assert_eq!( + inclusion::PendingAvailability::::get(ParaId::from(0)) + .unwrap() + .into_iter() + .map(|c| c.core_occupied()) + .collect::>(), + vec![CoreIndex(0)] + ); + assert_eq!( + inclusion::PendingAvailability::::get(ParaId::from(1)) + .unwrap() + .into_iter() + .map(|c| c.core_occupied()) + .collect::>(), + vec![CoreIndex(1)] + ); + }); + } + + #[test] + fn include_backed_candidates_elastic_scaling() { + // ParaId 0 has one pending candidate on core 0. + // ParaId 1 has one pending candidate on core 1. + // ParaId 2 has three pending candidates on cores 2, 3 and 4. + // All of them are being made available in this block. Propose 5 more candidates (one for + // each core) and check that they're successfully backed and the old ones enacted. + let config = default_config(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + new_test_ext(config).execute_with(|| { + // Set the elastic scaling MVP feature. + >::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::ElasticScalingMVP as u8, + true, + ) + .unwrap(); + + let dispute_statements = BTreeMap::new(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + backed_and_concluding.insert(2, 1); + + let scenario = make_inherent_data(TestConfig { + dispute_statements, + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 1, + code_upgrade: None, + fill_claimqueue: false, + elastic_paras: [(2, 3)].into_iter().collect(), + unavailable_cores: vec![], + }); + + let expected_para_inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (5 validators) + assert_eq!(expected_para_inherent_data.bitfields.len(), 5); + // * 1 backed candidate per core (5 cores) + assert_eq!(expected_para_inherent_data.backed_candidates.len(), 5); + // * 0 disputes. + assert_eq!(expected_para_inherent_data.disputes.len(), 0); + let mut inherent_data = InherentData::new(); + inherent_data + .put_data(PARACHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data) + .unwrap(); + + // The current schedule is empty prior to calling `create_inherent_enter`. + assert!(>::claimqueue_is_empty()); + + assert!(Pallet::::on_chain_votes().is_none()); + + // Nothing is filtered out (including the backed candidates.) + assert_eq!( + Pallet::::create_inherent_inner(&inherent_data.clone()).unwrap(), + expected_para_inherent_data + ); + + assert_eq!( + // The length of this vec is equal to the number of candidates, so we know our 5 + // backed candidates did not get filtered out + Pallet::::on_chain_votes().unwrap().backing_validators_per_candidate.len(), + 5 + ); + + assert_eq!( + // The session of the on chain votes should equal the current session, which is 2 + Pallet::::on_chain_votes().unwrap().session, + 2 + ); + + assert_eq!( + inclusion::PendingAvailability::::get(ParaId::from(0)) + .unwrap() + .into_iter() + .map(|c| c.core_occupied()) + .collect::>(), + vec![CoreIndex(0)] + ); + assert_eq!( + inclusion::PendingAvailability::::get(ParaId::from(1)) + .unwrap() + .into_iter() + .map(|c| c.core_occupied()) + .collect::>(), + vec![CoreIndex(1)] + ); + assert_eq!( + inclusion::PendingAvailability::::get(ParaId::from(2)) + .unwrap() + .into_iter() + .map(|c| c.core_occupied()) + .collect::>(), + vec![CoreIndex(2), CoreIndex(3), CoreIndex(4)] + ); + }); + + // ParaId 0 has one pending candidate on core 0. + // ParaId 1 has one pending candidate on core 1. + // ParaId 2 has 4 pending candidates on cores 2, 3, 4 and 5. + // Cores 1, 2 and 3 are being made available in this block. Propose 6 more candidates (one + // for each core) and check that the right ones are successfully backed and the old ones + // enacted. + let config = default_config(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + new_test_ext(config).execute_with(|| { + // Set the elastic scaling MVP feature. + >::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::ElasticScalingMVP as u8, + true, + ) + .unwrap(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + backed_and_concluding.insert(2, 1); + + // Modify the availability bitfields so that cores 0, 4 and 5 are not being made + // available. + let unavailable_cores = vec![0, 4, 5]; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 1, + code_upgrade: None, + fill_claimqueue: true, + elastic_paras: [(2, 4)].into_iter().collect(), + unavailable_cores: unavailable_cores.clone(), + }); + + let mut expected_para_inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (6 validators) + assert_eq!(expected_para_inherent_data.bitfields.len(), 6); + // * 1 backed candidate per core (6 cores) + assert_eq!(expected_para_inherent_data.backed_candidates.len(), 6); + // * 0 disputes. + assert_eq!(expected_para_inherent_data.disputes.len(), 0); + assert!(Pallet::::on_chain_votes().is_none()); + + expected_para_inherent_data.backed_candidates = expected_para_inherent_data + .backed_candidates + .into_iter() + .filter(|candidate| { + let (_, Some(core_index)) = candidate.validator_indices_and_core_index(true) + else { + panic!("Core index must have been injected"); + }; + !unavailable_cores.contains(&core_index.0) + }) + .collect(); + + let mut inherent_data = InherentData::new(); + inherent_data.put_data(PARACHAINS_INHERENT_IDENTIFIER, &scenario.data).unwrap(); + + assert!(!>::claimqueue_is_empty()); + + // The right candidates have been filtered out (the ones for cores 0,4,5) + assert_eq!( + Pallet::::create_inherent_inner(&inherent_data.clone()).unwrap(), + expected_para_inherent_data + ); + + // 3 candidates have been backed (for cores 1,2 and 3) + assert_eq!( + Pallet::::on_chain_votes().unwrap().backing_validators_per_candidate.len(), + 3 + ); + + assert_eq!( + // The session of the on chain votes should equal the current session, which is 2 + Pallet::::on_chain_votes().unwrap().session, + 2 + ); + + assert_eq!( + inclusion::PendingAvailability::::get(ParaId::from(1)) + .unwrap() + .into_iter() + .map(|c| c.core_occupied()) + .collect::>(), + vec![CoreIndex(1)] + ); + assert_eq!( + inclusion::PendingAvailability::::get(ParaId::from(2)) + .unwrap() + .into_iter() + .map(|c| c.core_occupied()) + .collect::>(), + vec![CoreIndex(4), CoreIndex(5), CoreIndex(2), CoreIndex(3)] + ); + + let expected_heads = (0..=2) + .map(|id| { + inclusion::PendingAvailability::::get(ParaId::from(id)) + .unwrap() + .back() + .unwrap() + .candidate_commitments() + .head_data + .clone() + }) + .collect::>(); + + // Now just make all candidates available. + let mut data = scenario.data.clone(); + let validators = session_info::Pallet::::session_info(2).unwrap().validators; + let signing_context = SigningContext { + parent_hash: BenchBuilder::::header(4).hash(), + session_index: 2, + }; + + data.backed_candidates.clear(); + + data.bitfields.iter_mut().enumerate().for_each(|(i, bitfield)| { + let unchecked_signed = UncheckedSigned::::benchmark_sign( + validators.get(ValidatorIndex(i as u32)).unwrap(), + bitvec::bitvec![u8, bitvec::order::Lsb0; 1; 6].into(), + &signing_context, + ValidatorIndex(i as u32), + ); + *bitfield = unchecked_signed; + }); + let mut inherent_data = InherentData::new(); + inherent_data.put_data(PARACHAINS_INHERENT_IDENTIFIER, &data).unwrap(); + + // Nothing has been filtered out. + assert_eq!( + Pallet::::create_inherent_inner(&inherent_data.clone()).unwrap(), + data + ); + + // No more candidates have been backed + assert!(Pallet::::on_chain_votes() + .unwrap() + .backing_validators_per_candidate + .is_empty()); + + // No more pending availability candidates + assert_eq!( + inclusion::PendingAvailability::::get(ParaId::from(0)) + .unwrap() + .into_iter() + .map(|c| c.core_occupied()) + .collect::>(), + vec![] + ); + assert_eq!( + inclusion::PendingAvailability::::get(ParaId::from(1)) + .unwrap() + .into_iter() + .map(|c| c.core_occupied()) + .collect::>(), + vec![] + ); + assert_eq!( + inclusion::PendingAvailability::::get(ParaId::from(2)) + .unwrap() + .into_iter() + .map(|c| c.core_occupied()) + .collect::>(), + vec![] + ); + + // Paras have the right on-chain heads now + expected_heads.into_iter().enumerate().for_each(|(id, head)| { + assert_eq!( + paras::Pallet::::para_head(ParaId::from(id as u32)).unwrap(), + head + ); + }); }); } @@ -251,6 +595,8 @@ mod enter { num_validators_per_core: 5, code_upgrade: None, fill_claimqueue: false, + elastic_paras: BTreeMap::new(), + unavailable_cores: vec![], }); let expected_para_inherent_data = scenario.data.clone(); @@ -322,6 +668,8 @@ mod enter { num_validators_per_core: 6, code_upgrade: None, fill_claimqueue: false, + elastic_paras: BTreeMap::new(), + unavailable_cores: vec![], }); let expected_para_inherent_data = scenario.data.clone(); @@ -391,6 +739,8 @@ mod enter { num_validators_per_core: 4, code_upgrade: None, fill_claimqueue: false, + elastic_paras: BTreeMap::new(), + unavailable_cores: vec![], }); let expected_para_inherent_data = scenario.data.clone(); @@ -476,6 +826,8 @@ mod enter { num_validators_per_core: 5, code_upgrade: None, fill_claimqueue: false, + elastic_paras: BTreeMap::new(), + unavailable_cores: vec![], }); let expected_para_inherent_data = scenario.data.clone(); @@ -561,6 +913,8 @@ mod enter { num_validators_per_core: 5, code_upgrade: None, fill_claimqueue: false, + elastic_paras: BTreeMap::new(), + unavailable_cores: vec![], }); let expected_para_inherent_data = scenario.data.clone(); @@ -622,7 +976,7 @@ mod enter { #[test] fn limit_candidates_over_weight_1() { let config = MockGenesisConfig::default(); - assert!(config.configuration.config.scheduling_lookahead > 0); + assert!(config.configuration.config.scheduler_params.lookahead > 0); new_test_ext(config).execute_with(|| { // Create the inherent data for this block @@ -645,6 +999,8 @@ mod enter { num_validators_per_core: 5, code_upgrade: None, fill_claimqueue: false, + elastic_paras: BTreeMap::new(), + unavailable_cores: vec![], }); let expected_para_inherent_data = scenario.data.clone(); @@ -697,6 +1053,25 @@ mod enter { 2 ); + // One core was scheduled. We should put the assignment back, before calling enter(). + let now = >::block_number() + 1; + let used_cores = 5; + let cores = (0..used_cores) + .into_iter() + .map(|i| { + let SchedulerParams { ttl, .. } = + >::config().scheduler_params; + // Load an assignment into provider so that one is present to pop + let assignment = + ::AssignmentProvider::get_mock_assignment( + CoreIndex(i), + ParaId::from(i), + ); + (CoreIndex(i), [ParasEntry::new(assignment, now + ttl)].into()) + }) + .collect(); + scheduler::ClaimQueue::::set(cores); + assert_ok!(Pallet::::enter( frame_system::RawOrigin::None.into(), limit_inherent_data, @@ -731,6 +1106,8 @@ mod enter { num_validators_per_core: 5, code_upgrade: None, fill_claimqueue: false, + elastic_paras: BTreeMap::new(), + unavailable_cores: vec![], }); let expected_para_inherent_data = scenario.data.clone(); @@ -797,6 +1174,8 @@ mod enter { num_validators_per_core: 5, code_upgrade: None, fill_claimqueue: false, + elastic_paras: BTreeMap::new(), + unavailable_cores: vec![], }); let expected_para_inherent_data = scenario.data.clone(); @@ -861,6 +1240,8 @@ mod enter { num_validators_per_core: 5, code_upgrade: None, fill_claimqueue: false, + elastic_paras: BTreeMap::new(), + unavailable_cores: vec![], }); let expected_para_inherent_data = scenario.data.clone(); @@ -902,6 +1283,131 @@ mod enter { }); } + // Helper fn that builds chained dummy candidates for elastic scaling tests + fn build_backed_candidate_chain( + para_id: ParaId, + len: usize, + start_core_index: usize, + code_upgrade_index: Option, + ) -> Vec { + if let Some(code_upgrade_index) = code_upgrade_index { + assert!(code_upgrade_index < len, "Code upgrade index out of bounds"); + } + + (0..len) + .into_iter() + .map(|idx| { + let mut builder = TestCandidateBuilder::default(); + builder.para_id = para_id; + let mut ccr = builder.build(); + + if Some(idx) == code_upgrade_index { + ccr.commitments.new_validation_code = Some(vec![1, 2, 3, 4].into()); + } + + ccr.commitments.processed_downward_messages = idx as u32; + let core_index = start_core_index + idx; + + BackedCandidate::new( + ccr.into(), + Default::default(), + Default::default(), + Some(CoreIndex(core_index as u32)), + ) + }) + .collect::>() + } + + // Ensure that overweight parachain inherents are always rejected by the runtime. + // Runtime should panic and return `InherentOverweight` error. + #[test] + fn test_backed_candidates_apply_weight_works_for_elastic_scaling() { + new_test_ext(MockGenesisConfig::default()).execute_with(|| { + let seed = [ + 1, 0, 52, 0, 0, 0, 0, 0, 1, 0, 10, 0, 22, 32, 0, 0, 2, 0, 55, 49, 0, 11, 0, 0, 3, + 0, 0, 0, 0, 0, 2, 92, + ]; + let mut rng = rand_chacha::ChaChaRng::from_seed(seed); + + // Create an overweight inherent and oversized block + let mut backed_and_concluding = BTreeMap::new(); + + for i in 0..30 { + backed_and_concluding.insert(i, i); + } + + let scenario = make_inherent_data(TestConfig { + dispute_statements: Default::default(), + dispute_sessions: vec![], // 3 cores with disputes + backed_and_concluding, + num_validators_per_core: 5, + code_upgrade: None, + fill_claimqueue: false, + elastic_paras: BTreeMap::new(), + unavailable_cores: vec![], + }); + + let mut para_inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (5 validators per core, 30 backed candidates, 0 disputes + // => 5*30 = 150) + assert_eq!(para_inherent_data.bitfields.len(), 150); + // * 30 backed candidates + assert_eq!(para_inherent_data.backed_candidates.len(), 30); + + let mut input_candidates = + build_backed_candidate_chain(ParaId::from(1000), 3, 0, Some(1)); + let chained_candidates_weight = backed_candidates_weight::(&input_candidates); + + input_candidates.append(&mut para_inherent_data.backed_candidates); + let input_bitfields = para_inherent_data.bitfields; + + // Test if weight insufficient even for 1 candidate (which doesn't contain a code + // upgrade). + let max_weight = backed_candidate_weight::(&input_candidates[0]) + + signed_bitfields_weight::(&input_bitfields); + let mut backed_candidates = input_candidates.clone(); + let mut bitfields = input_bitfields.clone(); + apply_weight_limit::( + &mut backed_candidates, + &mut bitfields, + max_weight, + &mut rng, + ); + + // The chained candidates are not picked, instead a single other candidate is picked + assert_eq!(backed_candidates.len(), 1); + assert_ne!(backed_candidates[0].descriptor().para_id, ParaId::from(1000)); + + // All bitfields are kept. + assert_eq!(bitfields.len(), 150); + + // Test if para_id 1000 chained candidates make it if there is enough room for its 3 + // candidates. + let max_weight = + chained_candidates_weight + signed_bitfields_weight::(&input_bitfields); + let mut backed_candidates = input_candidates.clone(); + let mut bitfields = input_bitfields.clone(); + apply_weight_limit::( + &mut backed_candidates, + &mut bitfields, + max_weight, + &mut rng, + ); + + // Only the chained candidates should pass filter. + assert_eq!(backed_candidates.len(), 3); + // Check the actual candidates + assert_eq!(backed_candidates[0].descriptor().para_id, ParaId::from(1000)); + assert_eq!(backed_candidates[1].descriptor().para_id, ParaId::from(1000)); + assert_eq!(backed_candidates[2].descriptor().para_id, ParaId::from(1000)); + + // All bitfields are kept. + assert_eq!(bitfields.len(), 150); + }); + } + // Ensure that overweight parachain inherents are always rejected by the runtime. // Runtime should panic and return `InherentOverweight` error. #[test] @@ -926,6 +1432,8 @@ mod enter { num_validators_per_core: 5, code_upgrade: None, fill_claimqueue: false, + elastic_paras: BTreeMap::new(), + unavailable_cores: vec![], }); let expected_para_inherent_data = scenario.data.clone(); @@ -973,13 +1481,14 @@ mod sanitizers { inclusion::tests::{ back_candidate, collator_sign_candidate, BackingKind, TestCandidateBuilder, }, - mock::{new_test_ext, MockGenesisConfig}, + mock::new_test_ext, }; use bitvec::order::Lsb0; use primitives::{ AvailabilityBitfield, GroupIndex, Hash, Id as ParaId, SignedAvailabilityBitfield, ValidatorIndex, }; + use rstest::rstest; use sp_core::crypto::UncheckedFrom; use crate::mock::Test; @@ -1228,9 +1737,11 @@ mod sanitizers { mod candidates { use crate::{ - mock::set_disabled_validators, + mock::{set_disabled_validators, RuntimeOrigin}, scheduler::{common::Assignment, ParasEntry}, + util::{make_persisted_validation_data, make_persisted_validation_data_with_parent}, }; + use primitives::ValidationCode; use sp_std::collections::vec_deque::VecDeque; use super::*; @@ -1238,12 +1749,14 @@ mod sanitizers { // Backed candidates and scheduled parachains used for `sanitize_backed_candidates` testing struct TestData { backed_candidates: Vec, - scheduled_paras: BTreeMap, + expected_backed_candidates_with_core: + BTreeMap>, + scheduled_paras: BTreeMap>, } - // Generate test data for the candidates and assert that the evnironment is set as expected + // Generate test data for the candidates and assert that the environment is set as expected // (check the comments for details) - fn get_test_data() -> TestData { + fn get_test_data_one_core_per_para(core_index_enabled: bool) -> TestData { const RELAY_PARENT_NUM: u32 = 3; // Add the relay parent to `shared` pallet. Otherwise some code (e.g. filtering backing @@ -1285,9 +1798,14 @@ mod sanitizers { shared::Pallet::::set_active_validators_ascending(validator_ids); // Two scheduled parachains - ParaId(1) on CoreIndex(0) and ParaId(2) on CoreIndex(1) - let scheduled = (0_usize..2) + let scheduled: BTreeMap> = (0_usize..2) .into_iter() - .map(|idx| (ParaId::from(1_u32 + idx as u32), CoreIndex::from(idx as u32))) + .map(|idx| { + ( + ParaId::from(1_u32 + idx as u32), + [CoreIndex::from(idx as u32)].into_iter().collect(), + ) + }) .collect::>(); // Set the validator groups in `scheduler` @@ -1301,7 +1819,7 @@ mod sanitizers { ( CoreIndex::from(0), VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(1) }, + Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(0) }, RELAY_PARENT_NUM, )]), ), @@ -1314,17 +1832,35 @@ mod sanitizers { ), ])); + // Set the on-chain included head data for paras. + paras::Pallet::::set_current_head(ParaId::from(1), HeadData(vec![1])); + paras::Pallet::::set_current_head(ParaId::from(2), HeadData(vec![2])); + + // Set the current_code_hash + paras::Pallet::::force_set_current_code( + RuntimeOrigin::root(), + ParaId::from(1), + ValidationCode(vec![1]), + ) + .unwrap(); + paras::Pallet::::force_set_current_code( + RuntimeOrigin::root(), + ParaId::from(2), + ValidationCode(vec![2]), + ) + .unwrap(); + // Callback used for backing candidates let group_validators = |group_index: GroupIndex| { match group_index { group_index if group_index == GroupIndex::from(0) => Some(vec![0, 1]), group_index if group_index == GroupIndex::from(1) => Some(vec![2, 3]), - _ => panic!("Group index out of bounds for 2 parachains and 1 parathread core"), + _ => panic!("Group index out of bounds"), } .map(|m| m.into_iter().map(ValidatorIndex).collect::>()) }; - // Two backed candidates from each parachain + // One backed candidate from each parachain let backed_candidates = (0_usize..2) .into_iter() .map(|idx0| { @@ -1333,8 +1869,15 @@ mod sanitizers { para_id: ParaId::from(idx1), relay_parent, pov_hash: Hash::repeat_byte(idx1 as u8), - persisted_validation_data_hash: [42u8; 32].into(), + persisted_validation_data_hash: make_persisted_validation_data::( + ParaId::from(idx1), + RELAY_PARENT_NUM, + Default::default(), + ) + .unwrap() + .hash(), hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![idx1 as u8]), ..Default::default() } .build(); @@ -1348,6 +1891,7 @@ mod sanitizers { &keystore, &signing_context, BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(idx0 as u32)), ); backed }) @@ -1369,116 +1913,1321 @@ mod sanitizers { ] ); - TestData { backed_candidates, scheduled_paras: scheduled } - } - - #[test] - fn happy_path() { - new_test_ext(MockGenesisConfig::default()).execute_with(|| { - let TestData { backed_candidates, scheduled_paras: scheduled } = get_test_data(); + let mut expected_backed_candidates_with_core = BTreeMap::new(); - let has_concluded_invalid = - |_idx: usize, _backed_candidate: &BackedCandidate| -> bool { false }; + for candidate in backed_candidates.iter() { + let para_id = candidate.descriptor().para_id; - assert_eq!( - sanitize_backed_candidates::( - backed_candidates.clone(), - &>::allowed_relay_parents(), - has_concluded_invalid, - &scheduled - ), - SanitizedBackedCandidates { - backed_candidates, - votes_from_disabled_were_dropped: false - } - ); + expected_backed_candidates_with_core.entry(para_id).or_insert(vec![]).push(( + candidate.clone(), + scheduled.get(¶_id).unwrap().first().copied().unwrap(), + )); + } - {} - }); + TestData { + backed_candidates, + scheduled_paras: scheduled, + expected_backed_candidates_with_core, + } } - // nothing is scheduled, so no paraids match, thus all backed candidates are skipped - #[test] - fn nothing_scheduled() { - new_test_ext(MockGenesisConfig::default()).execute_with(|| { - let TestData { backed_candidates, scheduled_paras: _ } = get_test_data(); - let scheduled = &BTreeMap::new(); - let has_concluded_invalid = - |_idx: usize, _backed_candidate: &BackedCandidate| -> bool { false }; - - let SanitizedBackedCandidates { - backed_candidates: sanitized_backed_candidates, - votes_from_disabled_were_dropped, - } = sanitize_backed_candidates::( - backed_candidates.clone(), - &>::allowed_relay_parents(), - has_concluded_invalid, - &scheduled, - ); + // Generate test data for the candidates and assert that the environment is set as expected + // (check the comments for details) + // Para 1 scheduled on core 0 and core 1. Two candidates are supplied. + // Para 2 scheduled on cores 2 and 3. One candidate supplied. + // Para 3 scheduled on core 4. One candidate supplied. + // Para 4 scheduled on core 5. Two candidates supplied. + // Para 5 scheduled on core 6. No candidates supplied. + // Para 6 is not scheduled. One candidate supplied. + // Para 7 is scheduled on core 7 and 8, but the candidate contains the wrong core index. + // Para 8 is scheduled on core 9, but the candidate contains the wrong core index. + fn get_test_data_multiple_cores_per_para(core_index_enabled: bool) -> TestData { + const RELAY_PARENT_NUM: u32 = 3; - assert!(sanitized_backed_candidates.is_empty()); - assert!(!votes_from_disabled_were_dropped); - }); - } + // Add the relay parent to `shared` pallet. Otherwise some code (e.g. filtering backing + // votes) won't behave correctly + shared::Pallet::::add_allowed_relay_parent( + default_header().hash(), + Default::default(), + RELAY_PARENT_NUM, + 1, + ); - // candidates that have concluded as invalid are filtered out - #[test] - fn invalid_are_filtered_out() { - new_test_ext(MockGenesisConfig::default()).execute_with(|| { - let TestData { backed_candidates, scheduled_paras: scheduled } = get_test_data(); + let header = default_header(); + let relay_parent = header.hash(); + let session_index = SessionIndex::from(0_u32); - // mark every second one as concluded invalid - let set = { - let mut set = std::collections::HashSet::new(); - for (idx, backed_candidate) in backed_candidates.iter().enumerate() { - if idx & 0x01 == 0 { - set.insert(backed_candidate.hash()); - } - } - set - }; - let has_concluded_invalid = - |_idx: usize, candidate: &BackedCandidate| set.contains(&candidate.hash()); - let SanitizedBackedCandidates { - backed_candidates: sanitized_backed_candidates, - votes_from_disabled_were_dropped, - } = sanitize_backed_candidates::( - backed_candidates.clone(), - &>::allowed_relay_parents(), - has_concluded_invalid, - &scheduled, + let keystore = LocalKeystore::in_memory(); + let keystore = Arc::new(keystore) as KeystorePtr; + let signing_context = SigningContext { parent_hash: relay_parent, session_index }; + + let validators = vec![ + keyring::Sr25519Keyring::Alice, + keyring::Sr25519Keyring::Bob, + keyring::Sr25519Keyring::Charlie, + keyring::Sr25519Keyring::Dave, + keyring::Sr25519Keyring::Eve, + keyring::Sr25519Keyring::Ferdie, + keyring::Sr25519Keyring::One, + keyring::Sr25519Keyring::Two, + ]; + for validator in validators.iter() { + Keystore::sr25519_generate_new( + &*keystore, + PARACHAIN_KEY_TYPE_ID, + Some(&validator.to_seed()), + ) + .unwrap(); + } + + // Set active validators in `shared` pallet + let validator_ids = + validators.iter().map(|v| v.public().into()).collect::>(); + shared::Pallet::::set_active_validators_ascending(validator_ids); + + // Set the validator groups in `scheduler` + scheduler::Pallet::::set_validator_groups(vec![ + vec![ValidatorIndex(0)], + vec![ValidatorIndex(1)], + vec![ValidatorIndex(2)], + vec![ValidatorIndex(3)], + vec![ValidatorIndex(4)], + vec![ValidatorIndex(5)], + vec![ValidatorIndex(6)], + vec![ValidatorIndex(7)], + ]); + + // Update scheduler's claimqueue with the parachains + scheduler::Pallet::::set_claimqueue(BTreeMap::from([ + ( + CoreIndex::from(0), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(0) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(1), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(1) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(2), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(2) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(3), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(3) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(4), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 3.into(), core_index: CoreIndex(4) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(5), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 4.into(), core_index: CoreIndex(5) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(6), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 5.into(), core_index: CoreIndex(6) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(7), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 7.into(), core_index: CoreIndex(7) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(8), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 7.into(), core_index: CoreIndex(8) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(9), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 8.into(), core_index: CoreIndex(9) }, + RELAY_PARENT_NUM, + )]), + ), + ])); + + // Set the on-chain included head data and current code hash. + for id in 1..=8u32 { + paras::Pallet::::set_current_head(ParaId::from(id), HeadData(vec![id as u8])); + paras::Pallet::::force_set_current_code( + RuntimeOrigin::root(), + ParaId::from(id), + ValidationCode(vec![id as u8]), + ) + .unwrap(); + } + + // Callback used for backing candidates + let group_validators = |group_index: GroupIndex| { + match group_index { + group_index if group_index == GroupIndex::from(0) => Some(vec![0]), + group_index if group_index == GroupIndex::from(1) => Some(vec![1]), + group_index if group_index == GroupIndex::from(2) => Some(vec![2]), + group_index if group_index == GroupIndex::from(3) => Some(vec![3]), + group_index if group_index == GroupIndex::from(4) => Some(vec![4]), + group_index if group_index == GroupIndex::from(5) => Some(vec![5]), + group_index if group_index == GroupIndex::from(6) => Some(vec![6]), + group_index if group_index == GroupIndex::from(7) => Some(vec![7]), + + _ => panic!("Group index out of bounds"), + } + .map(|m| m.into_iter().map(ValidatorIndex).collect::>()) + }; + + let mut backed_candidates = vec![]; + let mut expected_backed_candidates_with_core = BTreeMap::new(); + + // Para 1 + { + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(1), + relay_parent, + pov_hash: Hash::repeat_byte(1 as u8), + persisted_validation_data_hash: make_persisted_validation_data::( + ParaId::from(1), + RELAY_PARENT_NUM, + Default::default(), + ) + .unwrap() + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + head_data: HeadData(vec![1, 1]), + validation_code: ValidationCode(vec![1]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let prev_candidate = candidate.clone(); + let backed: BackedCandidate = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(0 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(0 as u32)), + ); + backed_candidates.push(backed.clone()); + if core_index_enabled { + expected_backed_candidates_with_core + .entry(ParaId::from(1)) + .or_insert(vec![]) + .push((backed, CoreIndex(0))); + } + + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(1), + relay_parent, + pov_hash: Hash::repeat_byte(2 as u8), + persisted_validation_data_hash: make_persisted_validation_data_with_parent::< + Test, + >( + RELAY_PARENT_NUM, + Default::default(), + prev_candidate.commitments.head_data, + ) + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![1]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(1 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(1 as u32)), + ); + backed_candidates.push(backed.clone()); + if core_index_enabled { + expected_backed_candidates_with_core + .entry(ParaId::from(1)) + .or_insert(vec![]) + .push((backed, CoreIndex(1))); + } + } + + // Para 2 + { + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(2), + relay_parent, + pov_hash: Hash::repeat_byte(3 as u8), + persisted_validation_data_hash: make_persisted_validation_data::( + ParaId::from(2), + RELAY_PARENT_NUM, + Default::default(), + ) + .unwrap() + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![2]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(2 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(2 as u32)), + ); + backed_candidates.push(backed.clone()); + if core_index_enabled { + expected_backed_candidates_with_core + .entry(ParaId::from(2)) + .or_insert(vec![]) + .push((backed, CoreIndex(2))); + } + } + + // Para 3 + { + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(3), + relay_parent, + pov_hash: Hash::repeat_byte(4 as u8), + persisted_validation_data_hash: make_persisted_validation_data::( + ParaId::from(3), + RELAY_PARENT_NUM, + Default::default(), + ) + .unwrap() + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![3]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(4 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(4 as u32)), + ); + backed_candidates.push(backed.clone()); + expected_backed_candidates_with_core + .entry(ParaId::from(3)) + .or_insert(vec![]) + .push((backed, CoreIndex(4))); + } + + // Para 4 + { + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(4), + relay_parent, + pov_hash: Hash::repeat_byte(5 as u8), + persisted_validation_data_hash: make_persisted_validation_data::( + ParaId::from(4), + RELAY_PARENT_NUM, + Default::default(), + ) + .unwrap() + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![4]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let prev_candidate = candidate.clone(); + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(5 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(5 as u32)), + ); + backed_candidates.push(backed.clone()); + expected_backed_candidates_with_core + .entry(ParaId::from(4)) + .or_insert(vec![]) + .push((backed, CoreIndex(5))); + + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(4), + relay_parent, + pov_hash: Hash::repeat_byte(6 as u8), + persisted_validation_data_hash: make_persisted_validation_data_with_parent::< + Test, + >( + RELAY_PARENT_NUM, + Default::default(), + prev_candidate.commitments.head_data, + ) + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![4]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(5 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(5 as u32)), + ); + backed_candidates.push(backed.clone()); + } + + // No candidate for para 5. + + // Para 6. + { + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(6), + relay_parent, + pov_hash: Hash::repeat_byte(3 as u8), + persisted_validation_data_hash: make_persisted_validation_data::( + ParaId::from(6), + RELAY_PARENT_NUM, + Default::default(), + ) + .unwrap() + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![6]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(6 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(6 as u32)), + ); + backed_candidates.push(backed.clone()); + } + + // Para 7. + { + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(7), + relay_parent, + pov_hash: Hash::repeat_byte(3 as u8), + persisted_validation_data_hash: make_persisted_validation_data::( + ParaId::from(7), + RELAY_PARENT_NUM, + Default::default(), + ) + .unwrap() + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![7]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(6 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(6 as u32)), + ); + backed_candidates.push(backed.clone()); + } + + // Para 8. + { + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(8), + relay_parent, + pov_hash: Hash::repeat_byte(3 as u8), + persisted_validation_data_hash: make_persisted_validation_data::( + ParaId::from(8), + RELAY_PARENT_NUM, + Default::default(), + ) + .unwrap() + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![8]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(6 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(7 as u32)), + ); + backed_candidates.push(backed.clone()); + if !core_index_enabled { + expected_backed_candidates_with_core + .entry(ParaId::from(8)) + .or_insert(vec![]) + .push((backed, CoreIndex(9))); + } + } + + // State sanity checks + assert_eq!( + >::scheduled_paras().collect::>(), + vec![ + (CoreIndex(0), ParaId::from(1)), + (CoreIndex(1), ParaId::from(1)), + (CoreIndex(2), ParaId::from(2)), + (CoreIndex(3), ParaId::from(2)), + (CoreIndex(4), ParaId::from(3)), + (CoreIndex(5), ParaId::from(4)), + (CoreIndex(6), ParaId::from(5)), + (CoreIndex(7), ParaId::from(7)), + (CoreIndex(8), ParaId::from(7)), + (CoreIndex(9), ParaId::from(8)), + ] + ); + let mut scheduled: BTreeMap> = BTreeMap::new(); + for (core_idx, para_id) in >::scheduled_paras() { + scheduled.entry(para_id).or_default().insert(core_idx); + } + + assert_eq!( + shared::Pallet::::active_validator_indices(), + vec![ + ValidatorIndex(0), + ValidatorIndex(1), + ValidatorIndex(2), + ValidatorIndex(3), + ValidatorIndex(4), + ValidatorIndex(5), + ValidatorIndex(6), + ValidatorIndex(7), + ] + ); + + TestData { + backed_candidates, + scheduled_paras: scheduled, + expected_backed_candidates_with_core, + } + } + + // Para 1 scheduled on core 0 and core 1. Two candidates are supplied. They form a chain but + // in the wrong order. + // Para 2 scheduled on core 2, core 3 and core 4. Three candidates are supplied. The second + // one is not part of the chain. + // Para 3 scheduled on core 5 and 6. Two candidates are supplied and they all form a chain. + // Para 4 scheduled on core 7 and 8. Duplicated candidates. + fn get_test_data_for_order_checks(core_index_enabled: bool) -> TestData { + const RELAY_PARENT_NUM: u32 = 3; + + // Add the relay parent to `shared` pallet. Otherwise some code (e.g. filtering backing + // votes) won't behave correctly + shared::Pallet::::add_allowed_relay_parent( + default_header().hash(), + Default::default(), + RELAY_PARENT_NUM, + 1, + ); + + let header = default_header(); + let relay_parent = header.hash(); + let session_index = SessionIndex::from(0_u32); + + let keystore = LocalKeystore::in_memory(); + let keystore = Arc::new(keystore) as KeystorePtr; + let signing_context = SigningContext { parent_hash: relay_parent, session_index }; + + let validators = vec![ + keyring::Sr25519Keyring::Alice, + keyring::Sr25519Keyring::Bob, + keyring::Sr25519Keyring::Charlie, + keyring::Sr25519Keyring::Dave, + keyring::Sr25519Keyring::Eve, + keyring::Sr25519Keyring::Ferdie, + keyring::Sr25519Keyring::One, + keyring::Sr25519Keyring::Two, + keyring::Sr25519Keyring::AliceStash, + ]; + for validator in validators.iter() { + Keystore::sr25519_generate_new( + &*keystore, + PARACHAIN_KEY_TYPE_ID, + Some(&validator.to_seed()), + ) + .unwrap(); + } + + // Set active validators in `shared` pallet + let validator_ids = + validators.iter().map(|v| v.public().into()).collect::>(); + shared::Pallet::::set_active_validators_ascending(validator_ids); + + // Set the validator groups in `scheduler` + scheduler::Pallet::::set_validator_groups(vec![ + vec![ValidatorIndex(0)], + vec![ValidatorIndex(1)], + vec![ValidatorIndex(2)], + vec![ValidatorIndex(3)], + vec![ValidatorIndex(4)], + vec![ValidatorIndex(5)], + vec![ValidatorIndex(6)], + vec![ValidatorIndex(7)], + vec![ValidatorIndex(8)], + ]); + + // Update scheduler's claimqueue with the parachains + scheduler::Pallet::::set_claimqueue(BTreeMap::from([ + ( + CoreIndex::from(0), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(0) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(1), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(1) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(2), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(2) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(3), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(3) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(4), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(4) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(5), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 3.into(), core_index: CoreIndex(5) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(6), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 3.into(), core_index: CoreIndex(6) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(7), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 4.into(), core_index: CoreIndex(7) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(8), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 4.into(), core_index: CoreIndex(8) }, + RELAY_PARENT_NUM, + )]), + ), + ])); + + // Set the on-chain included head data and current code hash. + for id in 1..=4u32 { + paras::Pallet::::set_current_head(ParaId::from(id), HeadData(vec![id as u8])); + paras::Pallet::::force_set_current_code( + RuntimeOrigin::root(), + ParaId::from(id), + ValidationCode(vec![id as u8]), + ) + .unwrap(); + } + + // Callback used for backing candidates + let group_validators = |group_index: GroupIndex| { + match group_index { + group_index if group_index == GroupIndex::from(0) => Some(vec![0]), + group_index if group_index == GroupIndex::from(1) => Some(vec![1]), + group_index if group_index == GroupIndex::from(2) => Some(vec![2]), + group_index if group_index == GroupIndex::from(3) => Some(vec![3]), + group_index if group_index == GroupIndex::from(4) => Some(vec![4]), + group_index if group_index == GroupIndex::from(5) => Some(vec![5]), + group_index if group_index == GroupIndex::from(6) => Some(vec![6]), + group_index if group_index == GroupIndex::from(7) => Some(vec![7]), + group_index if group_index == GroupIndex::from(8) => Some(vec![8]), + + _ => panic!("Group index out of bounds"), + } + .map(|m| m.into_iter().map(ValidatorIndex).collect::>()) + }; + + let mut backed_candidates = vec![]; + let mut expected_backed_candidates_with_core = BTreeMap::new(); + + // Para 1 + { + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(1), + relay_parent, + pov_hash: Hash::repeat_byte(1 as u8), + persisted_validation_data_hash: make_persisted_validation_data::( + ParaId::from(1), + RELAY_PARENT_NUM, + Default::default(), + ) + .unwrap() + .hash(), + head_data: HeadData(vec![1, 1]), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![1]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let prev_candidate = candidate.clone(); + let prev_backed: BackedCandidate = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(0 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(0 as u32)), + ); + + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(1), + relay_parent, + pov_hash: Hash::repeat_byte(2 as u8), + persisted_validation_data_hash: make_persisted_validation_data_with_parent::< + Test, + >( + RELAY_PARENT_NUM, + Default::default(), + prev_candidate.commitments.head_data, + ) + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![1]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(1 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(1 as u32)), + ); + backed_candidates.push(backed.clone()); + backed_candidates.push(prev_backed.clone()); + } + + // Para 2. + { + let mut candidate_1 = TestCandidateBuilder { + para_id: ParaId::from(2), + relay_parent, + pov_hash: Hash::repeat_byte(3 as u8), + persisted_validation_data_hash: make_persisted_validation_data::( + ParaId::from(2), + RELAY_PARENT_NUM, + Default::default(), + ) + .unwrap() + .hash(), + head_data: HeadData(vec![2, 2]), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![2]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate_1); + + let backed_1: BackedCandidate = back_candidate( + candidate_1, + &validators, + group_validators(GroupIndex::from(2 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(2 as u32)), + ); + + backed_candidates.push(backed_1.clone()); + if core_index_enabled { + expected_backed_candidates_with_core + .entry(ParaId::from(2)) + .or_insert(vec![]) + .push((backed_1, CoreIndex(2))); + } + + let mut candidate_2 = TestCandidateBuilder { + para_id: ParaId::from(2), + relay_parent, + pov_hash: Hash::repeat_byte(4 as u8), + persisted_validation_data_hash: make_persisted_validation_data::( + ParaId::from(2), + RELAY_PARENT_NUM, + Default::default(), + ) + .unwrap() + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![2]), + head_data: HeadData(vec![3, 3]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate_2); + + let backed_2 = back_candidate( + candidate_2.clone(), + &validators, + group_validators(GroupIndex::from(3 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(3 as u32)), + ); + backed_candidates.push(backed_2.clone()); + + let mut candidate_3 = TestCandidateBuilder { + para_id: ParaId::from(2), + relay_parent, + pov_hash: Hash::repeat_byte(5 as u8), + persisted_validation_data_hash: make_persisted_validation_data_with_parent::< + Test, + >( + RELAY_PARENT_NUM, + Default::default(), + candidate_2.commitments.head_data, + ) + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![2]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate_3); + + let backed_3 = back_candidate( + candidate_3, + &validators, + group_validators(GroupIndex::from(4 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(4 as u32)), + ); + backed_candidates.push(backed_3.clone()); + } + + // Para 3 + { + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(3), + relay_parent, + pov_hash: Hash::repeat_byte(6 as u8), + persisted_validation_data_hash: make_persisted_validation_data::( + ParaId::from(3), + RELAY_PARENT_NUM, + Default::default(), + ) + .unwrap() + .hash(), + head_data: HeadData(vec![3, 3]), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![3]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let prev_candidate = candidate.clone(); + let backed: BackedCandidate = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(5 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(5 as u32)), + ); + backed_candidates.push(backed.clone()); + if core_index_enabled { + expected_backed_candidates_with_core + .entry(ParaId::from(3)) + .or_insert(vec![]) + .push((backed, CoreIndex(5))); + } + + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(3), + relay_parent, + pov_hash: Hash::repeat_byte(6 as u8), + persisted_validation_data_hash: make_persisted_validation_data_with_parent::< + Test, + >( + RELAY_PARENT_NUM, + Default::default(), + prev_candidate.commitments.head_data, + ) + .hash(), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![3]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(6 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(6 as u32)), + ); + backed_candidates.push(backed.clone()); + if core_index_enabled { + expected_backed_candidates_with_core + .entry(ParaId::from(3)) + .or_insert(vec![]) + .push((backed, CoreIndex(6))); + } + } + + // Para 4 + { + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(4), + relay_parent, + pov_hash: Hash::repeat_byte(8 as u8), + persisted_validation_data_hash: make_persisted_validation_data::( + ParaId::from(4), + RELAY_PARENT_NUM, + Default::default(), + ) + .unwrap() + .hash(), + head_data: HeadData(vec![4]), + hrmp_watermark: RELAY_PARENT_NUM, + validation_code: ValidationCode(vec![4]), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed: BackedCandidate = back_candidate( + candidate.clone(), + &validators, + group_validators(GroupIndex::from(7 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(7 as u32)), + ); + backed_candidates.push(backed.clone()); + if core_index_enabled { + expected_backed_candidates_with_core + .entry(ParaId::from(4)) + .or_insert(vec![]) + .push((backed, CoreIndex(7))); + } + + let backed: BackedCandidate = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(7 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(8 as u32)), + ); + backed_candidates.push(backed.clone()); + } + + // State sanity checks + assert_eq!( + >::scheduled_paras().collect::>(), + vec![ + (CoreIndex(0), ParaId::from(1)), + (CoreIndex(1), ParaId::from(1)), + (CoreIndex(2), ParaId::from(2)), + (CoreIndex(3), ParaId::from(2)), + (CoreIndex(4), ParaId::from(2)), + (CoreIndex(5), ParaId::from(3)), + (CoreIndex(6), ParaId::from(3)), + (CoreIndex(7), ParaId::from(4)), + (CoreIndex(8), ParaId::from(4)), + ] + ); + let mut scheduled: BTreeMap> = BTreeMap::new(); + for (core_idx, para_id) in >::scheduled_paras() { + scheduled.entry(para_id).or_default().insert(core_idx); + } + + assert_eq!( + shared::Pallet::::active_validator_indices(), + vec![ + ValidatorIndex(0), + ValidatorIndex(1), + ValidatorIndex(2), + ValidatorIndex(3), + ValidatorIndex(4), + ValidatorIndex(5), + ValidatorIndex(6), + ValidatorIndex(7), + ValidatorIndex(8), + ] + ); + + TestData { + backed_candidates, + scheduled_paras: scheduled, + expected_backed_candidates_with_core, + } + } + + #[rstest] + #[case(false)] + #[case(true)] + fn happy_path_one_core_per_para(#[case] core_index_enabled: bool) { + new_test_ext(default_config()).execute_with(|| { + let TestData { + backed_candidates, + expected_backed_candidates_with_core, + scheduled_paras: scheduled, + } = get_test_data_one_core_per_para(core_index_enabled); + + assert_eq!( + sanitize_backed_candidates::( + backed_candidates.clone(), + &>::allowed_relay_parents(), + BTreeSet::new(), + scheduled, + core_index_enabled + ), + expected_backed_candidates_with_core, + ); + }); + } + + #[rstest] + #[case(false)] + #[case(true)] + fn test_with_multiple_cores_per_para(#[case] core_index_enabled: bool) { + new_test_ext(default_config()).execute_with(|| { + let TestData { + backed_candidates, + expected_backed_candidates_with_core, + scheduled_paras: scheduled, + } = get_test_data_multiple_cores_per_para(core_index_enabled); + + assert_eq!( + sanitize_backed_candidates::( + backed_candidates.clone(), + &>::allowed_relay_parents(), + BTreeSet::new(), + scheduled, + core_index_enabled + ), + expected_backed_candidates_with_core, + ); + }); + } + + #[rstest] + #[case(false)] + #[case(true)] + fn test_candidate_ordering(#[case] core_index_enabled: bool) { + new_test_ext(default_config()).execute_with(|| { + let TestData { + backed_candidates, + scheduled_paras: scheduled, + expected_backed_candidates_with_core, + } = get_test_data_for_order_checks(core_index_enabled); + + assert_eq!( + sanitize_backed_candidates::( + backed_candidates.clone(), + &>::allowed_relay_parents(), + BTreeSet::new(), + scheduled, + core_index_enabled, + ), + expected_backed_candidates_with_core + ); + }); + } + + // nothing is scheduled, so no paraids match, thus all backed candidates are skipped + #[rstest] + #[case(false, false)] + #[case(true, true)] + #[case(false, true)] + #[case(true, false)] + fn nothing_scheduled( + #[case] core_index_enabled: bool, + #[case] multiple_cores_per_para: bool, + ) { + new_test_ext(default_config()).execute_with(|| { + let TestData { backed_candidates, .. } = if multiple_cores_per_para { + get_test_data_multiple_cores_per_para(core_index_enabled) + } else { + get_test_data_one_core_per_para(core_index_enabled) + }; + let scheduled = BTreeMap::new(); + + let sanitized_backed_candidates = sanitize_backed_candidates::( + backed_candidates.clone(), + &>::allowed_relay_parents(), + BTreeSet::new(), + scheduled, + core_index_enabled, + ); + + assert!(sanitized_backed_candidates.is_empty()); + }); + } + + // candidates that have concluded as invalid are filtered out + #[rstest] + #[case(false)] + #[case(true)] + fn concluded_invalid_are_filtered_out_single_core_per_para( + #[case] core_index_enabled: bool, + ) { + new_test_ext(default_config()).execute_with(|| { + let TestData { backed_candidates, scheduled_paras: scheduled, .. } = + get_test_data_one_core_per_para(core_index_enabled); + + // mark every second one as concluded invalid + let set = { + let mut set = std::collections::BTreeSet::new(); + for (idx, backed_candidate) in backed_candidates.iter().enumerate() { + if idx & 0x01 == 0 { + set.insert(backed_candidate.hash()); + } + } + set + }; + let sanitized_backed_candidates: BTreeMap< + ParaId, + Vec<(BackedCandidate<_>, CoreIndex)>, + > = sanitize_backed_candidates::( + backed_candidates.clone(), + &>::allowed_relay_parents(), + set, + scheduled, + core_index_enabled, ); assert_eq!(sanitized_backed_candidates.len(), backed_candidates.len() / 2); - assert!(!votes_from_disabled_were_dropped); }); } + // candidates that have concluded as invalid are filtered out, as well as their descendants. #[test] - fn disabled_non_signing_validator_doesnt_get_filtered() { - new_test_ext(MockGenesisConfig::default()).execute_with(|| { - let TestData { mut backed_candidates, scheduled_paras } = get_test_data(); + fn concluded_invalid_are_filtered_out_multiple_cores_per_para() { + // Mark the first candidate of paraid 1 as invalid. Its descendant should also + // be dropped. Also mark the candidate of paraid 3 as invalid. + new_test_ext(default_config()).execute_with(|| { + let TestData { + backed_candidates, + scheduled_paras: scheduled, + mut expected_backed_candidates_with_core, + .. + } = get_test_data_multiple_cores_per_para(true); + + let mut invalid_set = std::collections::BTreeSet::new(); + + for (idx, backed_candidate) in backed_candidates.iter().enumerate() { + if backed_candidate.descriptor().para_id == ParaId::from(1) && idx == 0 { + invalid_set.insert(backed_candidate.hash()); + } else if backed_candidate.descriptor().para_id == ParaId::from(3) { + invalid_set.insert(backed_candidate.hash()); + } + } + let sanitized_backed_candidates: BTreeMap< + ParaId, + Vec<(BackedCandidate<_>, CoreIndex)>, + > = sanitize_backed_candidates::( + backed_candidates.clone(), + &>::allowed_relay_parents(), + invalid_set, + scheduled, + true, + ); + + // We'll be left with candidates from paraid 2 and 4. + + expected_backed_candidates_with_core.remove(&ParaId::from(1)).unwrap(); + expected_backed_candidates_with_core.remove(&ParaId::from(3)).unwrap(); + + assert_eq!(sanitized_backed_candidates, sanitized_backed_candidates); + }); + + // Mark the second candidate of paraid 1 as invalid. Its predecessor should be left + // in place. + new_test_ext(default_config()).execute_with(|| { + let TestData { + backed_candidates, + scheduled_paras: scheduled, + mut expected_backed_candidates_with_core, + .. + } = get_test_data_multiple_cores_per_para(true); + + let mut invalid_set = std::collections::BTreeSet::new(); + + for (idx, backed_candidate) in backed_candidates.iter().enumerate() { + if backed_candidate.descriptor().para_id == ParaId::from(1) && idx == 1 { + invalid_set.insert(backed_candidate.hash()); + } + } + let sanitized_backed_candidates: BTreeMap< + ParaId, + Vec<(BackedCandidate<_>, CoreIndex)>, + > = sanitize_backed_candidates::( + backed_candidates.clone(), + &>::allowed_relay_parents(), + invalid_set, + scheduled, + true, + ); + + // Only the second candidate of paraid 1 should be removed. + expected_backed_candidates_with_core + .get_mut(&ParaId::from(1)) + .unwrap() + .remove(1); + + // We'll be left with candidates from paraid 1, 2, 3 and 4. + assert_eq!(sanitized_backed_candidates, expected_backed_candidates_with_core); + }); + } + + #[rstest] + #[case(false)] + #[case(true)] + fn disabled_non_signing_validator_doesnt_get_filtered(#[case] core_index_enabled: bool) { + new_test_ext(default_config()).execute_with(|| { + let TestData { mut expected_backed_candidates_with_core, .. } = + get_test_data_one_core_per_para(core_index_enabled); // Disable Eve set_disabled_validators(vec![4]); - let before = backed_candidates.clone(); + let before = expected_backed_candidates_with_core.clone(); // Eve is disabled but no backing statement is signed by it so nothing should be // filtered - assert!(!filter_backed_statements_from_disabled_validators::( - &mut backed_candidates, + filter_backed_statements_from_disabled_validators::( + &mut expected_backed_candidates_with_core, &>::allowed_relay_parents(), - &scheduled_paras - )); - assert_eq!(backed_candidates, before); + core_index_enabled, + ); + assert_eq!(expected_backed_candidates_with_core, before); }); } - #[test] - fn drop_statements_from_disabled_without_dropping_candidate() { - new_test_ext(MockGenesisConfig::default()).execute_with(|| { - let TestData { mut backed_candidates, scheduled_paras } = get_test_data(); + #[rstest] + #[case(false)] + #[case(true)] + fn drop_statements_from_disabled_without_dropping_candidate( + #[case] core_index_enabled: bool, + ) { + new_test_ext(default_config()).execute_with(|| { + let TestData { mut expected_backed_candidates_with_core, .. } = + get_test_data_one_core_per_para(core_index_enabled); // Disable Alice set_disabled_validators(vec![0]); @@ -1491,62 +3240,197 @@ mod sanitizers { configuration::Pallet::::force_set_active_config(hc); // Verify the initial state is as expected - assert_eq!(backed_candidates.get(0).unwrap().validity_votes.len(), 2); - assert_eq!( - backed_candidates.get(0).unwrap().validator_indices.get(0).unwrap(), - true - ); assert_eq!( - backed_candidates.get(0).unwrap().validator_indices.get(1).unwrap(), - true + expected_backed_candidates_with_core + .get(&ParaId::from(1)) + .unwrap() + .iter() + .next() + .unwrap() + .0 + .validity_votes() + .len(), + 2 ); - let untouched = backed_candidates.get(1).unwrap().clone(); + let (validator_indices, maybe_core_index) = expected_backed_candidates_with_core + .get(&ParaId::from(1)) + .unwrap() + .iter() + .next() + .unwrap() + .0 + .validator_indices_and_core_index(core_index_enabled); + if core_index_enabled { + assert!(maybe_core_index.is_some()); + } else { + assert!(maybe_core_index.is_none()); + } - assert!(filter_backed_statements_from_disabled_validators::( - &mut backed_candidates, + assert_eq!(validator_indices.get(0).unwrap(), true); + assert_eq!(validator_indices.get(1).unwrap(), true); + let untouched = expected_backed_candidates_with_core + .get(&ParaId::from(2)) + .unwrap() + .iter() + .next() + .unwrap() + .0 + .clone(); + + let before = expected_backed_candidates_with_core.clone(); + filter_backed_statements_from_disabled_validators::( + &mut expected_backed_candidates_with_core, &>::allowed_relay_parents(), - &scheduled_paras - )); + core_index_enabled, + ); + assert_eq!(before.len(), expected_backed_candidates_with_core.len()); + + let (validator_indices, maybe_core_index) = expected_backed_candidates_with_core + .get(&ParaId::from(1)) + .unwrap() + .iter() + .next() + .unwrap() + .0 + .validator_indices_and_core_index(core_index_enabled); + if core_index_enabled { + assert!(maybe_core_index.is_some()); + } else { + assert!(maybe_core_index.is_none()); + } // there should still be two backed candidates - assert_eq!(backed_candidates.len(), 2); + assert_eq!(expected_backed_candidates_with_core.len(), 2); // but the first one should have only one validity vote - assert_eq!(backed_candidates.get(0).unwrap().validity_votes.len(), 1); - // Validator 0 vote should be dropped, validator 1 - retained assert_eq!( - backed_candidates.get(0).unwrap().validator_indices.get(0).unwrap(), - false + expected_backed_candidates_with_core + .get(&ParaId::from(1)) + .unwrap() + .iter() + .next() + .unwrap() + .0 + .validity_votes() + .len(), + 1 ); + // Validator 0 vote should be dropped, validator 1 - retained + assert_eq!(validator_indices.get(0).unwrap(), false); + assert_eq!(validator_indices.get(1).unwrap(), true); + // the second candidate shouldn't be modified assert_eq!( - backed_candidates.get(0).unwrap().validator_indices.get(1).unwrap(), - true + expected_backed_candidates_with_core + .get(&ParaId::from(2)) + .unwrap() + .iter() + .next() + .unwrap() + .0, + untouched ); - // the second candidate shouldn't be modified - assert_eq!(*backed_candidates.get(1).unwrap(), untouched); }); } - #[test] - fn drop_candidate_if_all_statements_are_from_disabled() { - new_test_ext(MockGenesisConfig::default()).execute_with(|| { - let TestData { mut backed_candidates, scheduled_paras } = get_test_data(); + #[rstest] + #[case(false)] + #[case(true)] + fn drop_candidate_if_all_statements_are_from_disabled_single_core_per_para( + #[case] core_index_enabled: bool, + ) { + new_test_ext(default_config()).execute_with(|| { + let TestData { mut expected_backed_candidates_with_core, .. } = + get_test_data_one_core_per_para(core_index_enabled); // Disable Alice and Bob set_disabled_validators(vec![0, 1]); // Verify the initial state is as expected - assert_eq!(backed_candidates.get(0).unwrap().validity_votes.len(), 2); - let untouched = backed_candidates.get(1).unwrap().clone(); + assert_eq!( + expected_backed_candidates_with_core + .get(&ParaId::from(1)) + .unwrap() + .iter() + .next() + .unwrap() + .0 + .validity_votes() + .len(), + 2 + ); + let untouched = expected_backed_candidates_with_core + .get(&ParaId::from(2)) + .unwrap() + .iter() + .next() + .unwrap() + .0 + .clone(); + + filter_backed_statements_from_disabled_validators::( + &mut expected_backed_candidates_with_core, + &>::allowed_relay_parents(), + core_index_enabled, + ); + + assert_eq!(expected_backed_candidates_with_core.len(), 1); + assert_eq!( + expected_backed_candidates_with_core + .get(&ParaId::from(2)) + .unwrap() + .iter() + .next() + .unwrap() + .0, + untouched + ); + assert_eq!(expected_backed_candidates_with_core.get(&ParaId::from(1)), None); + }); + } + + #[test] + fn drop_candidate_if_all_statements_are_from_disabled_multiple_cores_per_para() { + // Disable Bob, only the second candidate of paraid 1 should be removed. + new_test_ext(default_config()).execute_with(|| { + let TestData { mut expected_backed_candidates_with_core, .. } = + get_test_data_multiple_cores_per_para(true); + + set_disabled_validators(vec![1]); - assert!(filter_backed_statements_from_disabled_validators::( - &mut backed_candidates, + let mut untouched = expected_backed_candidates_with_core.clone(); + + filter_backed_statements_from_disabled_validators::( + &mut expected_backed_candidates_with_core, &>::allowed_relay_parents(), - &scheduled_paras - )); + true, + ); - assert_eq!(backed_candidates.len(), 1); - assert_eq!(*backed_candidates.get(0).unwrap(), untouched); + untouched.get_mut(&ParaId::from(1)).unwrap().remove(1); + + assert_eq!(expected_backed_candidates_with_core, untouched); }); + + // Disable Alice or disable both Alice and Bob, all candidates of paraid 1 should be + // removed. + for disabled in [vec![0], vec![0, 1]] { + new_test_ext(default_config()).execute_with(|| { + let TestData { mut expected_backed_candidates_with_core, .. } = + get_test_data_multiple_cores_per_para(true); + + set_disabled_validators(disabled); + + let mut untouched = expected_backed_candidates_with_core.clone(); + + filter_backed_statements_from_disabled_validators::( + &mut expected_backed_candidates_with_core, + &>::allowed_relay_parents(), + true, + ); + + untouched.remove(&ParaId::from(1)).unwrap(); + + assert_eq!(expected_backed_candidates_with_core, untouched); + }); + } } } } diff --git a/polkadot/runtime/parachains/src/paras_inherent/weights.rs b/polkadot/runtime/parachains/src/paras_inherent/weights.rs index 05cc53fae04652c188d62b57fa5281aa6bb88e22..0f4e5be572a66d16c1476ada7671c495a27bbc83 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/weights.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/weights.rs @@ -149,11 +149,11 @@ pub fn backed_candidate_weight( candidate: &BackedCandidate, ) -> Weight { set_proof_size_to_tx_size( - if candidate.candidate.commitments.new_validation_code.is_some() { + if candidate.candidate().commitments.new_validation_code.is_some() { <::WeightInfo as WeightInfo>::enter_backed_candidate_code_upgrade() } else { <::WeightInfo as WeightInfo>::enter_backed_candidates_variable( - candidate.validity_votes.len() as u32, + candidate.validity_votes().len() as u32, ) }, candidate, diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/v7.rs b/polkadot/runtime/parachains/src/runtime_api_impl/v7.rs index 1bbd4dfb716f1f541b40bca3e3a434b21c5a7a81..171f3f746a82f16d0929f60100fdec5b00b9dab7 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/v7.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/v7.rs @@ -22,6 +22,7 @@ use crate::{ scheduler::{self, CoreOccupied}, session_info, shared, }; +use frame_support::traits::{GetStorageVersion, StorageVersion}; use frame_system::pallet_prelude::*; use primitives::{ async_backing::{ @@ -92,16 +93,41 @@ pub fn availability_cores() -> Vec { - let pending_availability = - >::pending_availability(entry.para_id()) - .expect("Occupied core always has pending availability; qed"); - - let backed_in_number = *pending_availability.backed_in_number(); + // Due to https://github.com/paritytech/polkadot-sdk/issues/64, using the new storage types would cause + // this runtime API to panic. We explicitly handle the storage for version 0 to + // prevent that. When removing the inclusion v0 -> v1 migration, this bit of code + // can also be removed. + let pending_availability = if >::on_chain_storage_version() == + StorageVersion::new(0) + { + inclusion::migration::v0::PendingAvailability::::get(entry.para_id()) + .expect("Occupied core always has pending availability; qed") + } else { + let candidate = >::pending_availability_with_core( + entry.para_id(), + CoreIndex(i as u32), + ) + .expect("Occupied core always has pending availability; qed"); + + // Translate to the old candidate format, as we don't need the commitments now. + inclusion::migration::v0::CandidatePendingAvailability { + core: candidate.core_occupied(), + hash: candidate.candidate_hash(), + descriptor: candidate.candidate_descriptor().clone(), + availability_votes: candidate.availability_votes().clone(), + backers: candidate.backers().clone(), + relay_parent_number: candidate.relay_parent_number(), + backed_in_number: candidate.backed_in_number(), + backing_group: candidate.backing_group(), + } + }; + + let backed_in_number = pending_availability.backed_in_number; // Use the same block number for determining the responsible group as what the // backing subsystem would use when it calls validator_groups api. let backing_group_allocation_time = - pending_availability.relay_parent_number() + One::one(); + pending_availability.relay_parent_number + One::one(); CoreState::Occupied(OccupiedCore { next_up_on_available: >::next_up_on_available(CoreIndex( i as u32, @@ -111,13 +137,13 @@ pub fn availability_cores() -> Vec>::next_up_on_time_out(CoreIndex( i as u32, )), - availability: pending_availability.availability_votes().clone(), + availability: pending_availability.availability_votes.clone(), group_responsible: group_responsible_for( backing_group_allocation_time, - pending_availability.core_occupied(), + pending_availability.core, ), - candidate_hash: pending_availability.candidate_hash(), - candidate_descriptor: pending_availability.candidate_descriptor().clone(), + candidate_hash: pending_availability.hash, + candidate_descriptor: pending_availability.descriptor, }) }, CoreOccupied::Free => { @@ -200,8 +226,8 @@ pub fn assumed_validation_data( }; let persisted_validation_data = make_validation_data().or_else(|| { - // Try again with force enacting the core. This check only makes sense if - // the core is occupied. + // Try again with force enacting the pending candidates. This check only makes sense if + // there are any pending candidates. >::pending_availability(para_id).and_then(|_| { >::force_enact(para_id); make_validation_data() @@ -465,27 +491,23 @@ pub fn backing_state( }; let pending_availability = { - // Note: the API deals with a `Vec` as it is future-proof for cases - // where there may be multiple candidates pending availability at a time. - // But at the moment only one candidate can be pending availability per - // parachain. crate::inclusion::PendingAvailability::::get(¶_id) - .and_then(|pending| { - let commitments = - crate::inclusion::PendingAvailabilityCommitments::::get(¶_id); - commitments.map(move |c| (pending, c)) - }) - .map(|(pending, commitments)| { - CandidatePendingAvailability { - candidate_hash: pending.candidate_hash(), - descriptor: pending.candidate_descriptor().clone(), - commitments, - relay_parent_number: pending.relay_parent_number(), - max_pov_size: constraints.max_pov_size, // assume always same in session. - } + .map(|pending_candidates| { + pending_candidates + .into_iter() + .map(|candidate| { + CandidatePendingAvailability { + candidate_hash: candidate.candidate_hash(), + descriptor: candidate.candidate_descriptor().clone(), + commitments: candidate.candidate_commitments().clone(), + relay_parent_number: candidate.relay_parent_number(), + max_pov_size: constraints.max_pov_size, /* assume always same in + * session. */ + } + }) + .collect() }) - .into_iter() - .collect() + .unwrap_or_else(|| vec![]) }; Some(BackingState { constraints, pending_availability }) diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs b/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs index 1fee1a4097d8ee465317e32274dcc6f14075a6ee..296b872e8d41bb212bf1ec484056a7b5762e0134 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs @@ -16,12 +16,15 @@ //! Put implementations of functions from staging APIs here. -use crate::{configuration, initializer, shared}; +use crate::{configuration, initializer, scheduler, shared}; use primitives::{ vstaging::{ApprovalVotingParams, NodeFeatures}, - ValidatorIndex, + CoreIndex, Id as ParaId, ValidatorIndex, +}; +use sp_std::{ + collections::{btree_map::BTreeMap, vec_deque::VecDeque}, + prelude::Vec, }; -use sp_std::prelude::Vec; /// Implementation for `DisabledValidators` // CAVEAT: this should only be called on the node side @@ -38,8 +41,18 @@ pub fn node_features() -> NodeFeatures { >::config().node_features } -/// Approval voting subsystem configuration parameteres +/// Approval voting subsystem configuration parameters pub fn approval_voting_params() -> ApprovalVotingParams { let config = >::config(); config.approval_voting_params } + +/// Returns the claimqueue from the scheduler +pub fn claim_queue() -> BTreeMap> { + >::claimqueue() + .into_iter() + .map(|(core_index, entries)| { + (core_index, entries.into_iter().map(|e| e.para_id()).collect()) + }) + .collect() +} diff --git a/polkadot/runtime/parachains/src/scheduler.rs b/polkadot/runtime/parachains/src/scheduler.rs index 3dbb050fb4e5e40dc6f7b512bf55c45368a3ac5d..25840d9707dcc596233e7c19b20cf2f20360dcc5 100644 --- a/polkadot/runtime/parachains/src/scheduler.rs +++ b/polkadot/runtime/parachains/src/scheduler.rs @@ -37,7 +37,7 @@ //! availability cores over time. use crate::{configuration, initializer::SessionChangeNotification, paras}; -use frame_support::pallet_prelude::*; +use frame_support::{pallet_prelude::*, traits::Defensive}; use frame_system::pallet_prelude::BlockNumberFor; pub use polkadot_core_primitives::v2::BlockNumber; use primitives::{ @@ -51,7 +51,7 @@ use sp_std::{ pub mod common; -use common::{Assignment, AssignmentProvider, AssignmentProviderConfig}; +use common::{Assignment, AssignmentProvider}; pub use pallet::*; @@ -59,6 +59,7 @@ pub use pallet::*; mod tests; const LOG_TARGET: &str = "runtime::parachains::scheduler"; + pub mod migration; #[frame_support::pallet] @@ -88,10 +89,8 @@ pub mod pallet { #[pallet::getter(fn validator_groups)] pub(crate) type ValidatorGroups = StorageValue<_, Vec>, ValueQuery>; - /// One entry for each availability core. Entries are `None` if the core is not currently - /// occupied. Can be temporarily `Some` if scheduled but not occupied. - /// The i'th parachain belongs to the i'th core, with the remaining cores all being - /// parathread-multiplexers. + /// One entry for each availability core. The i'th parachain belongs to the i'th core, with the + /// remaining cores all being on demand parachain multiplexers. /// /// Bounded by the maximum of either of these two values: /// * The number of parachains and parathread multiplexers @@ -146,7 +145,7 @@ pub mod pallet { /// One entry for each availability core. The `VecDeque` represents the assignments to be /// scheduled on that core. The value contained here will not be valid after the end of - /// a block. Runtime APIs should be used to determine scheduled cores/ for the upcoming block. + /// a block. Runtime APIs should be used to determine scheduled cores for the upcoming block. #[pallet::storage] #[pallet::getter(fn claimqueue)] pub(crate) type ClaimQueue = @@ -222,7 +221,7 @@ impl Pallet { let n_cores = core::cmp::max( T::AssignmentProvider::session_core_count(), - match config.max_validators_per_core { + match config.scheduler_params.max_validators_per_core { Some(x) if x != 0 => validators.len() as u32 / x, _ => 0, }, @@ -236,8 +235,16 @@ impl Pallet { if n_cores == 0 || validators.is_empty() { ValidatorGroups::::set(Vec::new()); } else { - let group_base_size = validators.len() / n_cores as usize; - let n_larger_groups = validators.len() % n_cores as usize; + let group_base_size = validators + .len() + .checked_div(n_cores as usize) + .defensive_proof("n_cores should not be 0") + .unwrap_or(0); + let n_larger_groups = validators + .len() + .checked_rem(n_cores as usize) + .defensive_proof("n_cores should not be 0") + .unwrap_or(0); // Groups contain indices into the validators from the session change notification, // which are already shuffled. @@ -350,6 +357,7 @@ impl Pallet { fn drop_expired_claims_from_claimqueue() { let now = >::block_number(); let availability_cores = AvailabilityCores::::get(); + let ttl = >::config().scheduler_params.ttl; ClaimQueue::::mutate(|cq| { for (idx, _) in (0u32..).zip(availability_cores) { @@ -382,8 +390,6 @@ impl Pallet { if let Some(assignment) = T::AssignmentProvider::pop_assignment_for_core(core_idx) { - let AssignmentProviderConfig { ttl, .. } = - T::AssignmentProvider::get_provider_config(core_idx); core_claimqueue.push_back(ParasEntry::new(assignment, now + ttl)); } } @@ -392,16 +398,6 @@ impl Pallet { }); } - /// Get the para (chain or thread) ID assigned to a particular core or index, if any. Core - /// indices out of bounds will return `None`, as will indices of unassigned cores. - pub(crate) fn core_para(core_index: CoreIndex) -> Option { - let cores = AvailabilityCores::::get(); - match cores.get(core_index.0 as usize) { - None | Some(CoreOccupied::Free) => None, - Some(CoreOccupied::Paras(entry)) => Some(entry.para_id()), - } - } - /// Get the validators in the given group, if the group index is valid for this session. pub(crate) fn group_validators(group_index: GroupIndex) -> Option> { ValidatorGroups::::get().get(group_index.0 as usize).map(|g| g.clone()) @@ -428,7 +424,7 @@ impl Pallet { } let rotations_since_session_start: BlockNumberFor = - (at - session_start_block) / config.group_rotation_frequency; + (at - session_start_block) / config.scheduler_params.group_rotation_frequency; let rotations_since_session_start = as TryInto>::try_into(rotations_since_session_start) @@ -460,9 +456,9 @@ impl Pallet { // Note: blocks backed in this rotation will never time out here as backed_in + // config.paras_availability_period will always be > now for these blocks, as // otherwise above condition would not be true. - pending_since + config.paras_availability_period + pending_since + config.scheduler_params.paras_availability_period } else { - next_rotation + config.paras_availability_period + next_rotation + config.scheduler_params.paras_availability_period }; AvailabilityTimeoutStatus { timed_out: time_out_at <= now, live_until: time_out_at } @@ -478,7 +474,8 @@ impl Pallet { let now = >::block_number() + One::one(); let rotation_info = Self::group_rotation_info(now); - let current_window = rotation_info.last_rotation_at() + config.paras_availability_period; + let current_window = + rotation_info.last_rotation_at() + config.scheduler_params.paras_availability_period; now < current_window } @@ -488,7 +485,7 @@ impl Pallet { ) -> GroupRotationInfo> { let session_start_block = Self::session_start_block(); let group_rotation_frequency = - >::config().group_rotation_frequency; + >::config().scheduler_params.group_rotation_frequency; GroupRotationInfo { session_start_block, now, group_rotation_frequency } } @@ -508,6 +505,8 @@ impl Pallet { /// Return the next thing that will be scheduled on this core assuming it is currently /// occupied and the candidate occupying it times out. pub(crate) fn next_up_on_time_out(core: CoreIndex) -> Option { + let max_availability_timeouts = + >::config().scheduler_params.max_availability_timeouts; Self::next_up_on_available(core).or_else(|| { // Or, if none, the claim currently occupying the core, // as it would be put back on the queue after timing out if number of retries is not at @@ -515,16 +514,12 @@ impl Pallet { let cores = AvailabilityCores::::get(); cores.get(core.0 as usize).and_then(|c| match c { CoreOccupied::Free => None, - CoreOccupied::Paras(pe) => { - let AssignmentProviderConfig { max_availability_timeouts, .. } = - T::AssignmentProvider::get_provider_config(core); - + CoreOccupied::Paras(pe) => if pe.availability_timeouts < max_availability_timeouts { Some(Self::paras_entry_to_scheduled_core(pe)) } else { None - } - }, + }, }) }) } @@ -566,7 +561,7 @@ impl Pallet { // ClaimQueue related functions // fn claimqueue_lookahead() -> u32 { - >::config().scheduling_lookahead + >::config().scheduler_params.lookahead } /// Frees cores and fills the free claimqueue spots by popping from the `AssignmentProvider`. @@ -585,15 +580,15 @@ impl Pallet { let n_lookahead = Self::claimqueue_lookahead().max(1); let n_session_cores = T::AssignmentProvider::session_core_count(); let cq = ClaimQueue::::get(); - let ttl = >::config().on_demand_ttl; + let config = >::config(); + let max_availability_timeouts = config.scheduler_params.max_availability_timeouts; + let ttl = config.scheduler_params.ttl; for core_idx in 0..n_session_cores { let core_idx = CoreIndex::from(core_idx); // add previously timedout paras back into the queue if let Some(mut entry) = timedout_paras.remove(&core_idx) { - let AssignmentProviderConfig { max_availability_timeouts, .. } = - T::AssignmentProvider::get_provider_config(core_idx); if entry.availability_timeouts < max_availability_timeouts { // Increment the timeout counter. entry.availability_timeouts += 1; @@ -668,13 +663,6 @@ impl Pallet { .filter_map(|(core_idx, v)| v.front().map(|e| (core_idx, e.assignment.para_id()))) } - #[cfg(any(feature = "runtime-benchmarks", test))] - pub(crate) fn assignment_provider_config( - core_idx: CoreIndex, - ) -> AssignmentProviderConfig> { - T::AssignmentProvider::get_provider_config(core_idx) - } - #[cfg(any(feature = "try-runtime", test))] fn claimqueue_len() -> usize { ClaimQueue::::get().iter().map(|la_vec| la_vec.1.len()).sum() diff --git a/polkadot/runtime/parachains/src/scheduler/common.rs b/polkadot/runtime/parachains/src/scheduler/common.rs index 2eb73385803c6e62a88fc55526e3ba18218cf06d..66a4e6d30be0830650295d5311b7d7e1fc3b1d20 100644 --- a/polkadot/runtime/parachains/src/scheduler/common.rs +++ b/polkadot/runtime/parachains/src/scheduler/common.rs @@ -48,22 +48,10 @@ impl Assignment { } } -#[derive(Encode, Decode, TypeInfo)] -/// A set of variables required by the scheduler in order to operate. -pub struct AssignmentProviderConfig { - /// How many times a collation can time out on availability. - /// Zero timeouts still means that a collation can be provided as per the slot auction - /// assignment provider. - pub max_availability_timeouts: u32, - - /// How long the collator has to provide a collation to the backing group before being dropped. - pub ttl: BlockNumber, -} - pub trait AssignmentProvider { /// Pops an [`Assignment`] from the provider for a specified [`CoreIndex`]. /// - /// This is where assignments come into existance. + /// This is where assignments come into existence. fn pop_assignment_for_core(core_idx: CoreIndex) -> Option; /// A previously popped `Assignment` has been fully processed. @@ -77,14 +65,11 @@ pub trait AssignmentProvider { /// Push back a previously popped assignment. /// /// If the assignment could not be processed within the current session, it can be pushed back - /// to the assignment provider in order to be poppped again later. + /// to the assignment provider in order to be popped again later. /// /// This is the second way the life of an assignment can come to an end. fn push_back_assignment(assignment: Assignment); - /// Returns a set of variables needed by the scheduler - fn get_provider_config(core_idx: CoreIndex) -> AssignmentProviderConfig; - /// Push some assignment for mocking/benchmarks purposes. /// /// Useful for benchmarks and testing. The returned assignment is "valid" and can if need be diff --git a/polkadot/runtime/parachains/src/scheduler/migration.rs b/polkadot/runtime/parachains/src/scheduler/migration.rs index 4c0a07d73674205dbc4fc80090f1331a8858633e..c47fbab046fecd4ee56a52b976f1d9e6e861f217 100644 --- a/polkadot/runtime/parachains/src/scheduler/migration.rs +++ b/polkadot/runtime/parachains/src/scheduler/migration.rs @@ -24,7 +24,7 @@ use frame_support::{ /// Old/legacy assignment representation (v0). /// -/// `Assignment` used to be a concrete type with the same layout V0Assignment, idential on all +/// `Assignment` used to be a concrete type with the same layout V0Assignment, identical on all /// assignment providers. This can be removed once storage has been migrated. #[derive(Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Clone)] struct V0Assignment { diff --git a/polkadot/runtime/parachains/src/scheduler/tests.rs b/polkadot/runtime/parachains/src/scheduler/tests.rs index 9af23ce64bd67ab0901dd1a03e849d51cfffe342..720f645e3bdc148819b3eab00b0f54f706367f9d 100644 --- a/polkadot/runtime/parachains/src/scheduler/tests.rs +++ b/polkadot/runtime/parachains/src/scheduler/tests.rs @@ -18,7 +18,9 @@ use super::*; use frame_support::assert_ok; use keyring::Sr25519Keyring; -use primitives::{BlockNumber, SessionIndex, ValidationCode, ValidatorId}; +use primitives::{ + vstaging::SchedulerParams, BlockNumber, SessionIndex, ValidationCode, ValidatorId, +}; use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; use crate::{ @@ -103,15 +105,19 @@ fn run_to_end_of_block( fn default_config() -> HostConfiguration { HostConfiguration { - coretime_cores: 3, - group_rotation_frequency: 10, - paras_availability_period: 3, - scheduling_lookahead: 2, // This field does not affect anything that scheduler does. However, `HostConfiguration` // is still a subject to consistency test. It requires that // `minimum_validation_upgrade_delay` is greater than `chain_availability_period` and // `thread_availability_period`. minimum_validation_upgrade_delay: 6, + scheduler_params: SchedulerParams { + group_rotation_frequency: 10, + paras_availability_period: 3, + lookahead: 2, + num_cores: 3, + max_availability_timeouts: 1, + ..Default::default() + }, ..Default::default() } } @@ -155,7 +161,7 @@ fn scheduled_entries() -> impl Iterator(vec![para_id])); // Add a claim on core 0 with a ttl == now (17) let paras_entry_non_expired = ParasEntry::new(Assignment::Bulk(para_id), now); @@ -217,7 +223,7 @@ fn claimqueue_ttl_drop_fn_works() { Scheduler::add_to_claimqueue(core_idx, paras_entry_expired.clone()); Scheduler::add_to_claimqueue(core_idx, paras_entry_non_expired.clone()); let cq = Scheduler::claimqueue(); - assert!(cq.get(&core_idx).unwrap().len() == 3); + assert_eq!(cq.get(&core_idx).unwrap().len(), 3); // Add a claim to the test assignment provider. let assignment = Assignment::Bulk(para_id); @@ -229,8 +235,9 @@ fn claimqueue_ttl_drop_fn_works() { let cq = Scheduler::claimqueue(); let cqc = cq.get(&core_idx).unwrap(); - // Same number of claims - assert!(cqc.len() == 3); + // Same number of claims, because a new claim is popped from `MockAssigner` instead of the + // expired one + assert_eq!(cqc.len(), 3); // The first 2 claims in the queue should have a ttl of 17, // being the ones set up prior in this test as claims 1 and 3. @@ -290,7 +297,7 @@ fn session_change_shuffles_validators() { fn session_change_takes_only_max_per_core() { let config = { let mut config = default_config(); - config.max_validators_per_core = Some(1); + config.scheduler_params.max_validators_per_core = Some(1); config }; @@ -330,7 +337,8 @@ fn session_change_takes_only_max_per_core() { #[test] fn fill_claimqueue_fills() { - let genesis_config = genesis_config(&default_config()); + let config = default_config(); + let genesis_config = genesis_config(&config); let para_a = ParaId::from(3_u32); let para_b = ParaId::from(4_u32); @@ -342,22 +350,20 @@ fn fill_claimqueue_fills() { new_test_ext(genesis_config).execute_with(|| { MockAssigner::set_core_count(2); - let AssignmentProviderConfig { ttl: config_ttl, .. } = - MockAssigner::get_provider_config(CoreIndex(0)); + let coretime_ttl = config.scheduler_params.ttl; // Add 3 paras schedule_blank_para(para_a); schedule_blank_para(para_b); schedule_blank_para(para_c); - // start a new session to activate, 3 validators for 3 cores. + // start a new session to activate, 2 validators for 2 cores. run_to_block(1, |number| match number { 1 => Some(SessionChangeNotification { new_config: default_config(), validators: vec![ ValidatorId::from(Sr25519Keyring::Alice.public()), ValidatorId::from(Sr25519Keyring::Bob.public()), - ValidatorId::from(Sr25519Keyring::Charlie.public()), ], ..Default::default() }), @@ -381,7 +387,7 @@ fn fill_claimqueue_fills() { &ParasEntry { assignment: assignment_a.clone(), availability_timeouts: 0, - ttl: 2 + config_ttl + ttl: 2 + coretime_ttl }, ); // Sits on the same core as `para_a` @@ -390,7 +396,7 @@ fn fill_claimqueue_fills() { ParasEntry { assignment: assignment_b.clone(), availability_timeouts: 0, - ttl: 2 + config_ttl + ttl: 2 + coretime_ttl } ); assert_eq!( @@ -398,7 +404,7 @@ fn fill_claimqueue_fills() { &ParasEntry { assignment: assignment_c.clone(), availability_timeouts: 0, - ttl: 2 + config_ttl + ttl: 2 + coretime_ttl }, ); } @@ -410,7 +416,7 @@ fn schedule_schedules_including_just_freed() { let mut config = default_config(); // NOTE: This test expects on demand cores to each get slotted on to a different core // and not fill up the claimqueue of each core first. - config.scheduling_lookahead = 1; + config.scheduler_params.lookahead = 1; let genesis_config = genesis_config(&config); let para_a = ParaId::from(3_u32); @@ -479,7 +485,7 @@ fn schedule_schedules_including_just_freed() { // All `core_queue`s should be empty Scheduler::claimqueue() .iter() - .for_each(|(_core_idx, core_queue)| assert!(core_queue.len() == 0)) + .for_each(|(_core_idx, core_queue)| assert_eq!(core_queue.len(), 0)) } // add a couple more para claims - the claim on `b` will go to the 3rd core @@ -557,7 +563,7 @@ fn schedule_schedules_including_just_freed() { #[test] fn schedule_clears_availability_cores() { let mut config = default_config(); - config.scheduling_lookahead = 1; + config.scheduler_params.lookahead = 1; let genesis_config = genesis_config(&config); let para_a = ParaId::from(1_u32); @@ -659,11 +665,11 @@ fn schedule_clears_availability_cores() { fn schedule_rotates_groups() { let config = { let mut config = default_config(); - config.scheduling_lookahead = 1; + config.scheduler_params.lookahead = 1; config }; - let rotation_frequency = config.group_rotation_frequency; + let rotation_frequency = config.scheduler_params.group_rotation_frequency; let on_demand_cores = 2; let genesis_config = genesis_config(&config); @@ -742,9 +748,12 @@ fn schedule_rotates_groups() { #[test] fn on_demand_claims_are_pruned_after_timing_out() { - let max_retries = 20; + let max_timeouts = 20; let mut config = default_config(); - config.scheduling_lookahead = 1; + config.scheduler_params.lookahead = 1; + // Need more timeouts for this test + config.scheduler_params.max_availability_timeouts = max_timeouts; + config.scheduler_params.ttl = BlockNumber::from(5u32); let genesis_config = genesis_config(&config); let para_a = ParaId::from(1_u32); @@ -753,11 +762,6 @@ fn on_demand_claims_are_pruned_after_timing_out() { new_test_ext(genesis_config).execute_with(|| { MockAssigner::set_core_count(2); - // Need more timeouts for this test - MockAssigner::set_assignment_provider_config(AssignmentProviderConfig { - max_availability_timeouts: max_retries, - ttl: BlockNumber::from(5u32), - }); schedule_blank_para(para_a); // #1 @@ -794,7 +798,7 @@ fn on_demand_claims_are_pruned_after_timing_out() { // Run to block #n over the max_retries value. // In this case, both validator groups with time out on availability and // the assignment will be dropped. - for n in now..=(now + max_retries + 1) { + for n in now..=(now + max_timeouts + 1) { // #n run_to_block(n, |_| None); // Time out on core 0. @@ -806,7 +810,7 @@ fn on_demand_claims_are_pruned_after_timing_out() { Scheduler::free_cores_and_fill_claimqueue(just_updated, now); // ParaId a exists in the claim queue until max_retries is reached. - if n < max_retries + now { + if n < max_timeouts + now { assert!(claimqueue_contains_para_ids::(vec![para_a])); } else { assert!(!claimqueue_contains_para_ids::(vec![para_a])); @@ -822,7 +826,7 @@ fn on_demand_claims_are_pruned_after_timing_out() { assert!(!availability_cores_contains_para_ids::(vec![para_a])); // #25 - now += max_retries + 2; + now += max_timeouts + 2; // Add assignment back to the mix. MockAssigner::add_test_assignment(assignment_a.clone()); @@ -832,8 +836,8 @@ fn on_demand_claims_are_pruned_after_timing_out() { // #26 now += 1; - // Run to block #n but this time have group 1 conclude the availabilty. - for n in now..=(now + max_retries + 1) { + // Run to block #n but this time have group 1 conclude the availability. + for n in now..=(now + max_timeouts + 1) { // #n run_to_block(n, |_| None); // Time out core 0 if group 0 is assigned to it, if group 1 is assigned, conclude. @@ -874,10 +878,8 @@ fn on_demand_claims_are_pruned_after_timing_out() { fn availability_predicate_works() { let genesis_config = genesis_config(&default_config()); - let HostConfiguration { group_rotation_frequency, paras_availability_period, .. } = - default_config(); - - assert!(paras_availability_period < group_rotation_frequency); + let SchedulerParams { group_rotation_frequency, paras_availability_period, .. } = + default_config().scheduler_params; new_test_ext(genesis_config).execute_with(|| { run_to_block(1 + paras_availability_period, |_| None); @@ -1044,7 +1046,7 @@ fn next_up_on_time_out_reuses_claim_if_nothing_queued() { #[test] fn session_change_requires_reschedule_dropping_removed_paras() { let mut config = default_config(); - config.scheduling_lookahead = 1; + config.scheduler_params.lookahead = 1; let genesis_config = genesis_config(&config); let para_a = ParaId::from(1_u32); @@ -1056,7 +1058,7 @@ fn session_change_requires_reschedule_dropping_removed_paras() { new_test_ext(genesis_config).execute_with(|| { // Setting explicit core count MockAssigner::set_core_count(5); - let assignment_provider_ttl = MockAssigner::get_provider_config(CoreIndex::from(0)).ttl; + let coretime_ttl = >::config().scheduler_params.ttl; schedule_blank_para(para_a); schedule_blank_para(para_b); @@ -1120,7 +1122,7 @@ fn session_change_requires_reschedule_dropping_removed_paras() { vec![ParasEntry::new( Assignment::Bulk(para_a), // At end of block 2 - assignment_provider_ttl + 2 + coretime_ttl + 2 )] .into_iter() .collect() @@ -1169,7 +1171,7 @@ fn session_change_requires_reschedule_dropping_removed_paras() { vec![ParasEntry::new( Assignment::Bulk(para_a), // At block 3 - assignment_provider_ttl + 3 + coretime_ttl + 3 )] .into_iter() .collect() @@ -1179,7 +1181,7 @@ fn session_change_requires_reschedule_dropping_removed_paras() { vec![ParasEntry::new( Assignment::Bulk(para_b), // At block 3 - assignment_provider_ttl + 3 + coretime_ttl + 3 )] .into_iter() .collect() diff --git a/polkadot/runtime/parachains/src/session_info.rs b/polkadot/runtime/parachains/src/session_info.rs index 9e1b3d05842fd7b35f87de5c297958f5f3f36bf3..4ca5a73d42b99378838927d9a67a60ea83c05a07 100644 --- a/polkadot/runtime/parachains/src/session_info.rs +++ b/polkadot/runtime/parachains/src/session_info.rs @@ -158,7 +158,7 @@ impl Pallet { for idx in old_earliest_stored_session..new_earliest_stored_session { Sessions::::remove(&idx); // Idx will be missing for a few sessions after the runtime upgrade. - // But it shouldn'be be a problem. + // But it shouldn't be a problem. AccountKeys::::remove(&idx); SessionExecutorParams::::remove(&idx); } diff --git a/polkadot/runtime/parachains/src/session_info/migration.rs b/polkadot/runtime/parachains/src/session_info/migration.rs index 228c1e3bb2515b14f5fe59a89578b03be065955d..ea6f81834b5db6eb7336521d3d471a6c3df49985 100644 --- a/polkadot/runtime/parachains/src/session_info/migration.rs +++ b/polkadot/runtime/parachains/src/session_info/migration.rs @@ -18,5 +18,5 @@ use frame_support::traits::StorageVersion; -/// The current storage version. +/// The in-code storage version. pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); diff --git a/polkadot/runtime/parachains/src/session_info/tests.rs b/polkadot/runtime/parachains/src/session_info/tests.rs index 92a50575deda8413abb18f892680d693c148d0cb..a5bfeae0745599c00de718684420fc377a9184f1 100644 --- a/polkadot/runtime/parachains/src/session_info/tests.rs +++ b/polkadot/runtime/parachains/src/session_info/tests.rs @@ -25,7 +25,7 @@ use crate::{ util::take_active_subset, }; use keyring::Sr25519Keyring; -use primitives::{BlockNumber, ValidatorId, ValidatorIndex}; +use primitives::{vstaging::SchedulerParams, BlockNumber, ValidatorId, ValidatorIndex}; fn run_to_block( to: BlockNumber, @@ -62,9 +62,9 @@ fn run_to_block( fn default_config() -> HostConfiguration { HostConfiguration { - coretime_cores: 1, dispute_period: 2, needed_approvals: 3, + scheduler_params: SchedulerParams { num_cores: 1, ..Default::default() }, ..Default::default() } } diff --git a/polkadot/runtime/parachains/src/ump_tests.rs b/polkadot/runtime/parachains/src/ump_tests.rs index 2ed1d64336b31e42d1fd0363143a7d17a66c7b97..5867a8fca66387743618ff1bebfebe19a14a0c41 100644 --- a/polkadot/runtime/parachains/src/ump_tests.rs +++ b/polkadot/runtime/parachains/src/ump_tests.rs @@ -456,7 +456,7 @@ fn verify_relay_dispatch_queue_size_is_externally_accessible() { fn assert_queue_size(para: ParaId, count: u32, size: u32) { #[allow(deprecated)] let raw_queue_size = sp_io::storage::get(&well_known_keys::relay_dispatch_queue_size(para)).expect( - "enqueing a message should create the dispatch queue\ + "enqueuing a message should create the dispatch queue\ and it should be accessible via the well known keys", ); let (c, s) = <(u32, u32)>::decode(&mut &raw_queue_size[..]) @@ -466,7 +466,7 @@ fn assert_queue_size(para: ParaId, count: u32, size: u32) { // Test the deprecated but at least type-safe `relay_dispatch_queue_size_typed`: #[allow(deprecated)] let (c, s) = well_known_keys::relay_dispatch_queue_size_typed(para).get().expect( - "enqueing a message should create the dispatch queue\ + "enqueuing a message should create the dispatch queue\ and it should be accessible via the well known keys", ); assert_eq!((c, s), (count, size)); diff --git a/polkadot/runtime/parachains/src/util.rs b/polkadot/runtime/parachains/src/util.rs index aa07ef0800554e9676ff67df8efb91f6643f502b..493a9d055efd6eb3260edc68e965365303474082 100644 --- a/polkadot/runtime/parachains/src/util.rs +++ b/polkadot/runtime/parachains/src/util.rs @@ -18,7 +18,7 @@ //! on all modules. use frame_system::pallet_prelude::BlockNumberFor; -use primitives::{Id as ParaId, PersistedValidationData, ValidatorIndex}; +use primitives::{HeadData, Id as ParaId, PersistedValidationData, ValidatorIndex}; use sp_std::{collections::btree_set::BTreeSet, vec::Vec}; use crate::{configuration, hrmp, paras}; @@ -42,6 +42,23 @@ pub fn make_persisted_validation_data( }) } +/// Make the persisted validation data for a particular parachain, a specified relay-parent, its +/// storage root and parent head data. +pub fn make_persisted_validation_data_with_parent( + relay_parent_number: BlockNumberFor, + relay_parent_storage_root: T::Hash, + parent_head: HeadData, +) -> PersistedValidationData> { + let config = >::config(); + + PersistedValidationData { + parent_head, + relay_parent_number, + relay_parent_storage_root, + max_pov_size: config.max_pov_size, + } +} + /// Take an active subset of a set containing all validators. /// /// First item in pair will be all items in set have indices found in the `active` indices set (in diff --git a/polkadot/runtime/rococo/Cargo.toml b/polkadot/runtime/rococo/Cargo.toml index 3dc59cc172817409bd607f5b68e52163a7b9afdb..6f63a93cebe50ddc18dd1abc1acfcd35bd3a6b54 100644 --- a/polkadot/runtime/rococo/Cargo.toml +++ b/polkadot/runtime/rococo/Cargo.toml @@ -104,6 +104,7 @@ polkadot-parachain-primitives = { path = "../../parachain", default-features = f xcm = { package = "staging-xcm", path = "../../xcm", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../xcm/xcm-executor", default-features = false } xcm-builder = { package = "staging-xcm-builder", path = "../../xcm/xcm-builder", default-features = false } +xcm-fee-payment-runtime-api = { path = "../../xcm/xcm-fee-payment-runtime-api", default-features = false } [dev-dependencies] tiny-keccak = { version = "2.0.2", features = ["keccak"] } @@ -208,6 +209,7 @@ std = [ "tx-pool-api/std", "xcm-builder/std", "xcm-executor/std", + "xcm-fee-payment-runtime-api/std", "xcm/std", ] runtime-benchmarks = [ diff --git a/polkadot/runtime/rococo/README.md b/polkadot/runtime/rococo/README.md index 5b2c296f0ced9a053de46bac5d8207d7a1d835d8..c19c3654fe4d35717283a5ad5fe1f2546a0d0f3a 100644 --- a/polkadot/runtime/rococo/README.md +++ b/polkadot/runtime/rococo/README.md @@ -4,7 +4,7 @@ Rococo is a testnet runtime with no stability guarantees. ## How to build `rococo` runtime `EpochDurationInBlocks` parameter is configurable via `ROCOCO_EPOCH_DURATION` environment variable. To build wasm -runtime blob with customized epoch duration the following command shall be exectuted: +runtime blob with customized epoch duration the following command shall be executed: ```bash ROCOCO_EPOCH_DURATION=10 ./polkadot/scripts/build-only-wasm.sh rococo-runtime /path/to/output/directory/ ``` diff --git a/polkadot/runtime/rococo/src/impls.rs b/polkadot/runtime/rococo/src/impls.rs index ac7100d7858377dca5991e0d0308dc64577b9350..cf364b6ac7942753b6fd5f47dbf621d70c7ebcf7 100644 --- a/polkadot/runtime/rococo/src/impls.rs +++ b/polkadot/runtime/rococo/src/impls.rs @@ -167,11 +167,16 @@ where }, ]); + let encoded_versioned_xcm = + VersionedXcm::V4(program).encode().try_into().map_err(|error| { + log::error!(target: "runtime::on_reap_identity", "XCM too large, error: {:?}", error); + pallet_xcm::Error::::XcmTooLarge + })?; // send - let _ = >::send( + let _ = >::send_blob( RawOrigin::Root.into(), Box::new(VersionedLocation::V4(destination)), - Box::new(VersionedXcm::V4(program)), + encoded_versioned_xcm, )?; Ok(()) } diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 03e96ab388b72c19a95bcc0c4af509fe4e56d36c..8c8abe97ede22d3df445742ffc72a0696df6c91f 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -38,7 +38,7 @@ use runtime_common::{ LocatableAssetConverter, ToAuthor, VersionedLocatableAsset, VersionedLocationConverter, }, paras_registrar, paras_sudo_wrapper, prod_or_fast, slots, - traits::Leaser, + traits::{Leaser, OnSwap}, BlockHashCount, BlockLength, SlowAdjustingFeeUpdate, }; use scale_info::TypeInfo; @@ -46,9 +46,8 @@ use sp_std::{cmp::Ordering, collections::btree_map::BTreeMap, prelude::*}; use runtime_parachains::{ assigner_coretime as parachains_assigner_coretime, - assigner_on_demand as parachains_assigner_on_demand, - assigner_parachains as parachains_assigner_parachains, - configuration as parachains_configuration, coretime, disputes as parachains_disputes, + assigner_on_demand as parachains_assigner_on_demand, configuration as parachains_configuration, + coretime, disputes as parachains_disputes, disputes::slashing as parachains_slashing, dmp as parachains_dmp, hrmp as parachains_hrmp, inclusion as parachains_inclusion, inclusion::{AggregateMessageOrigin, UmpQueueId}, @@ -76,7 +75,7 @@ use frame_support::{ InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, PrivilegeCmp, ProcessMessage, ProcessMessageError, StorageMapShim, WithdrawReasons, }, - weights::{ConstantMultiplier, WeightMeter}, + weights::{ConstantMultiplier, WeightMeter, WeightToFee as _}, PalletId, }; use frame_system::{EnsureRoot, EnsureSigned}; @@ -99,7 +98,10 @@ use sp_staking::SessionIndex; #[cfg(any(feature = "std", test))] use sp_version::NativeVersion; use sp_version::RuntimeVersion; -use xcm::{latest::prelude::*, VersionedLocation}; +use xcm::{ + latest::prelude::*, IntoVersion, VersionedAssetId, VersionedAssets, VersionedLocation, + VersionedXcm, +}; use xcm_builder::PayOverXcm; pub use frame_system::Call as SystemCall; @@ -124,6 +126,7 @@ use governance::{ pallet_custom_origins, AuctionAdmin, Fellows, GeneralAdmin, LeaseAdmin, Treasurer, TreasurySpender, }; +use xcm_fee_payment_runtime_api::Error as XcmPaymentApiError; #[cfg(test)] mod tests; @@ -150,7 +153,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("rococo"), impl_name: create_runtime_str!("parity-rococo-v2.0"), authoring_version: 0, - spec_version: 1_007_000, + spec_version: 1_009_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 24, @@ -186,7 +189,7 @@ parameter_types! { pub const SS58Prefix: u8 = 42; } -#[derive_impl(frame_system::config_preludes::RelayChainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::RelayChainDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = EverythingBut; type BlockWeights = BlockWeights; @@ -217,7 +220,7 @@ pub struct OriginPrivilegeCmp; impl PrivilegeCmp for OriginPrivilegeCmp { fn cmp_privilege(left: &OriginCaller, right: &OriginCaller) -> Option { if left == right { - return Some(Ordering::Equal) + return Some(Ordering::Equal); } match (left, right) { @@ -1038,8 +1041,6 @@ impl parachains_assigner_on_demand::Config for Runtime { type WeightInfo = weights::runtime_parachains_assigner_on_demand::WeightInfo; } -impl parachains_assigner_parachains::Config for Runtime {} - impl parachains_assigner_coretime::Config for Runtime {} impl parachains_initializer::Config for Runtime { @@ -1081,7 +1082,7 @@ impl paras_registrar::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type OnSwap = (Crowdloan, Slots); + type OnSwap = (Crowdloan, Slots, SwapLeases); type ParaDeposit = ParaDeposit; type DataDepositPerByte = DataDepositPerByte; type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; @@ -1309,6 +1310,14 @@ impl pallet_asset_rate::Config for Runtime { type BenchmarkHelper = runtime_common::impls::benchmarks::AssetRateArguments; } +// Notify `coretime` pallet when a lease swap occurs +pub struct SwapLeases; +impl OnSwap for SwapLeases { + fn on_swap(one: ParaId, other: ParaId) { + coretime::Pallet::::on_legacy_lease_swap(one, other); + } +} + construct_runtime! { pub enum Runtime { @@ -1401,7 +1410,6 @@ construct_runtime! { ParasSlashing: parachains_slashing = 63, MessageQueue: pallet_message_queue = 64, OnDemandAssignmentProvider: parachains_assigner_on_demand = 66, - ParachainsAssignmentProvider: parachains_assigner_parachains = 67, CoretimeAssignmentProvider: parachains_assigner_coretime = 68, // Parachain Onboarding Pallets. Start indices at 70 to leave room. @@ -1489,11 +1497,11 @@ pub mod migrations { let now = frame_system::Pallet::::block_number(); let lease = slots::Pallet::::lease(para); if lease.is_empty() { - return None + return None; } // Lease not yet started, ignore: if lease.iter().any(Option::is_none) { - return None + return None; } let (index, _) = as Leaser>::lease_period_index(now)?; @@ -1555,7 +1563,7 @@ pub mod migrations { fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC { log::warn!(target: "runtime::session_keys", "Skipping session keys migration pre-upgrade check due to spec version (already applied?)"); - return Ok(Vec::new()) + return Ok(Vec::new()); } log::info!(target: "runtime::session_keys", "Collecting pre-upgrade session keys state"); @@ -1584,7 +1592,7 @@ pub mod migrations { fn on_runtime_upgrade() -> Weight { if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC { log::info!("Skipping session keys upgrade: already applied"); - return ::DbWeight::get().reads(1) + return ::DbWeight::get().reads(1); } log::trace!("Upgrading session keys"); Session::upgrade_keys::(transform_session_keys); @@ -1597,7 +1605,7 @@ pub mod migrations { ) -> Result<(), sp_runtime::TryRuntimeError> { if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC { log::warn!(target: "runtime::session_keys", "Skipping session keys migration post-upgrade check due to spec version (already applied?)"); - return Ok(()) + return Ok(()); } let key_ids = SessionKeys::key_ids(); @@ -1662,9 +1670,13 @@ pub mod migrations { parachains_configuration::migration::v11::MigrateToV11, // This needs to come after the `parachains_configuration` above as we are reading the configuration. coretime::migration::MigrateToCoretime, + parachains_configuration::migration::v12::MigrateToV12, + parachains_assigner_on_demand::migration::MigrateV0ToV1, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, + + parachains_inclusion::migration::MigrateToV1, ); } @@ -1772,11 +1784,42 @@ sp_api::impl_runtime_apis! { Executive::execute_block(block); } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } + impl xcm_fee_payment_runtime_api::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + if !matches!(xcm_version, 3 | 4) { + return Err(XcmPaymentApiError::UnhandledXcmVersion); + } + Ok([VersionedAssetId::V4(xcm_config::TokenLocation::get().into())] + .into_iter() + .filter_map(|asset| asset.into_version(xcm_version).ok()) + .collect()) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + let local_asset = VersionedAssetId::V4(xcm_config::TokenLocation::get().into()); + let asset = asset + .into_version(4) + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; + + if asset != local_asset { return Err(XcmPaymentApiError::AssetNotFound); } + + Ok(WeightToFee::weight_to_fee(&weight)) + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + XcmPallet::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + XcmPallet::query_delivery_fees(destination, message) + } + } + impl sp_api::Metadata for Runtime { fn metadata() -> OpaqueMetadata { OpaqueMetadata::new(Runtime::metadata().into()) @@ -1994,7 +2037,7 @@ sp_api::impl_runtime_apis! { #[api_version(3)] impl beefy_primitives::BeefyApi for Runtime { fn beefy_genesis() -> Option { - Beefy::genesis_block() + pallet_beefy::GenesisBlock::::get() } fn validator_set() -> Option> { @@ -2032,11 +2075,11 @@ sp_api::impl_runtime_apis! { #[api_version(2)] impl mmr::MmrApi for Runtime { fn mmr_root() -> Result { - Ok(Mmr::mmr_root()) + Ok(pallet_mmr::RootHash::::get()) } fn mmr_leaf_count() -> Result { - Ok(Mmr::mmr_leaves()) + Ok(pallet_mmr::NumberOfLeaves::::get()) } fn generate_proof( @@ -2276,12 +2319,30 @@ sp_api::impl_runtime_apis! { TokenLocation::get(), ExistentialDeposit::get() ).into()); - pub ToParachain: ParaId = rococo_runtime_constants::system_parachain::ASSET_HUB_ID.into(); + pub AssetHubParaId: ParaId = rococo_runtime_constants::system_parachain::ASSET_HUB_ID.into(); + pub const RandomParaId: ParaId = ParaId::new(43211234); } impl frame_system_benchmarking::Config for Runtime {} impl frame_benchmarking::baseline::Config for Runtime {} impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = ( + runtime_common::xcm_sender::ToParachainDeliveryHelper< + XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForChildParachainDelivery, + AssetHubParaId, + (), + >, + runtime_common::xcm_sender::ToParachainDeliveryHelper< + XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForChildParachainDelivery, + RandomParaId, + (), + > + ); + fn reachable_dest() -> Option { Some(crate::xcm_config::AssetHub::get()) } @@ -2290,7 +2351,7 @@ sp_api::impl_runtime_apis! { // Relay/native token can be teleported to/from AH. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Here.into()) }, crate::xcm_config::AssetHub::get(), @@ -2301,10 +2362,10 @@ sp_api::impl_runtime_apis! { // Relay can reserve transfer native token to some random parachain. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Here.into()) }, - Parachain(43211234).into(), + Parachain(RandomParaId::get().into()).into(), )) } @@ -2321,6 +2382,13 @@ sp_api::impl_runtime_apis! { dest ) } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::here()), + fun: Fungible(ExistentialDeposit::get()), + } + } } impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = XcmConfig; @@ -2329,7 +2397,7 @@ sp_api::impl_runtime_apis! { XcmConfig, ExistentialDepositAsset, xcm_config::PriceForChildParachainDelivery, - ToParachain, + AssetHubParaId, (), >; fn valid_destination() -> Result { @@ -2460,7 +2528,7 @@ mod remote_tests { #[tokio::test] async fn run_migrations() { if var("RUN_MIGRATION_TESTS").is_err() { - return + return; } sp_tracing::try_init_simple(); diff --git a/polkadot/runtime/rococo/src/weights/pallet_xcm.rs b/polkadot/runtime/rococo/src/weights/pallet_xcm.rs index 177407ef7088b5c9bdcc460f36cb7d6485743a71..42972baa1c830baa820fc5c72b35f346daaf27d0 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_xcm.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_xcm.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-yprdrvc7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -48,10 +48,6 @@ use core::marker::PhantomData; /// Weight functions for `pallet_xcm`. pub struct WeightInfo(PhantomData); impl pallet_xcm::WeightInfo for WeightInfo { - fn transfer_assets() -> Weight { - // TODO: run benchmarks - Weight::zero() - } /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) @@ -62,36 +58,106 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn send() -> Weight { // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 27_328_000 picoseconds. - Weight::from_parts(27_976_000, 0) - .saturating_add(Weight::from_parts(0, 3607)) + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 24_724_000 picoseconds. + Weight::from_parts(25_615_000, 0) + .saturating_add(Weight::from_parts(0, 3645)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 24_709_000 picoseconds. + Weight::from_parts(25_326_000, 0) + .saturating_add(Weight::from_parts(0, 3645)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn teleport_assets() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 16_280_000 picoseconds. - Weight::from_parts(16_904_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 106_600_000 picoseconds. + Weight::from_parts(110_781_000, 0) + .saturating_add(Weight::from_parts(0, 3645)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn reserve_transfer_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `232` + // Estimated: `3697` + // Minimum execution time: 103_030_000 picoseconds. + Weight::from_parts(106_018_000, 0) + .saturating_add(Weight::from_parts(0, 3697)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn transfer_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 107_017_000 picoseconds. + Weight::from_parts(109_214_000, 0) + .saturating_add(Weight::from_parts(0, 3645)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + fn execute() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 15_869_000 picoseconds. - Weight::from_parts(16_264_000, 0) + // Minimum execution time: 6_864_000 picoseconds. + Weight::from_parts(7_135_000, 0) .saturating_add(Weight::from_parts(0, 0)) } - fn execute() -> Weight { + fn execute_blob() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_923_000 picoseconds. - Weight::from_parts(7_432_000, 0) + // Minimum execution time: 6_955_000 picoseconds. + Weight::from_parts(7_165_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `XcmPallet::SupportedVersion` (r:0 w:1) @@ -100,8 +166,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_333_000 picoseconds. - Weight::from_parts(7_566_000, 0) + // Minimum execution time: 6_827_000 picoseconds. + Weight::from_parts(7_211_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -109,8 +175,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_219_000 picoseconds. - Weight::from_parts(2_375_000, 0) + // Minimum execution time: 1_788_000 picoseconds. + Weight::from_parts(2_021_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `XcmPallet::VersionNotifiers` (r:1 w:1) @@ -129,11 +195,11 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `XcmPallet::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_subscribe_version_notify() -> Weight { // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 30_650_000 picoseconds. - Weight::from_parts(31_683_000, 0) - .saturating_add(Weight::from_parts(0, 3607)) + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 30_627_000 picoseconds. + Weight::from_parts(31_350_000, 0) + .saturating_add(Weight::from_parts(0, 3645)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -151,11 +217,11 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `XcmPallet::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_unsubscribe_version_notify() -> Weight { // Proof Size summary in bytes: - // Measured: `322` - // Estimated: `3787` - // Minimum execution time: 37_666_000 picoseconds. - Weight::from_parts(38_920_000, 0) - .saturating_add(Weight::from_parts(0, 3787)) + // Measured: `360` + // Estimated: `3825` + // Minimum execution time: 36_688_000 picoseconds. + Weight::from_parts(37_345_000, 0) + .saturating_add(Weight::from_parts(0, 3825)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -165,45 +231,45 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_244_000 picoseconds. - Weight::from_parts(2_425_000, 0) + // Minimum execution time: 1_829_000 picoseconds. + Weight::from_parts(1_986_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `XcmPallet::SupportedVersion` (r:4 w:2) + /// Storage: `XcmPallet::SupportedVersion` (r:5 w:2) /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `26` - // Estimated: `10916` - // Minimum execution time: 14_710_000 picoseconds. - Weight::from_parts(15_156_000, 0) - .saturating_add(Weight::from_parts(0, 10916)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `22` + // Estimated: `13387` + // Minimum execution time: 16_104_000 picoseconds. + Weight::from_parts(16_464_000, 0) + .saturating_add(Weight::from_parts(0, 13387)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `XcmPallet::VersionNotifiers` (r:4 w:2) + /// Storage: `XcmPallet::VersionNotifiers` (r:5 w:2) /// Proof: `XcmPallet::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `30` - // Estimated: `10920` - // Minimum execution time: 14_630_000 picoseconds. - Weight::from_parts(15_290_000, 0) - .saturating_add(Weight::from_parts(0, 10920)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `26` + // Estimated: `13391` + // Minimum execution time: 16_267_000 picoseconds. + Weight::from_parts(16_675_000, 0) + .saturating_add(Weight::from_parts(0, 13391)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `XcmPallet::VersionNotifyTargets` (r:5 w:0) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:6 w:0) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `40` - // Estimated: `13405` - // Minimum execution time: 16_686_000 picoseconds. - Weight::from_parts(17_332_000, 0) - .saturating_add(Weight::from_parts(0, 13405)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `15880` + // Minimum execution time: 18_487_000 picoseconds. + Weight::from_parts(19_102_000, 0) + .saturating_add(Weight::from_parts(0, 15880)) + .saturating_add(T::DbWeight::get().reads(6)) } /// Storage: `XcmPallet::VersionNotifyTargets` (r:2 w:1) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -217,38 +283,38 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_current_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `178` - // Estimated: `6118` - // Minimum execution time: 30_180_000 picoseconds. - Weight::from_parts(31_351_000, 0) - .saturating_add(Weight::from_parts(0, 6118)) + // Measured: `216` + // Estimated: `6156` + // Minimum execution time: 29_603_000 picoseconds. + Weight::from_parts(31_002_000, 0) + .saturating_add(Weight::from_parts(0, 6156)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: `XcmPallet::VersionNotifyTargets` (r:3 w:0) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:4 w:0) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `69` - // Estimated: `8484` - // Minimum execution time: 9_624_000 picoseconds. - Weight::from_parts(10_029_000, 0) - .saturating_add(Weight::from_parts(0, 8484)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `10959` + // Minimum execution time: 12_183_000 picoseconds. + Weight::from_parts(12_587_000, 0) + .saturating_add(Weight::from_parts(0, 10959)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: `XcmPallet::VersionNotifyTargets` (r:4 w:2) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:5 w:2) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `37` - // Estimated: `10927` - // Minimum execution time: 15_139_000 picoseconds. - Weight::from_parts(15_575_000, 0) - .saturating_add(Weight::from_parts(0, 10927)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `33` + // Estimated: `13398` + // Minimum execution time: 16_372_000 picoseconds. + Weight::from_parts(16_967_000, 0) + .saturating_add(Weight::from_parts(0, 13398)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `XcmPallet::VersionNotifyTargets` (r:4 w:2) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:5 w:2) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -260,12 +326,12 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `182` - // Estimated: `11072` - // Minimum execution time: 37_871_000 picoseconds. - Weight::from_parts(38_940_000, 0) - .saturating_add(Weight::from_parts(0, 11072)) - .saturating_add(T::DbWeight::get().reads(8)) + // Measured: `216` + // Estimated: `13581` + // Minimum execution time: 38_904_000 picoseconds. + Weight::from_parts(39_983_000, 0) + .saturating_add(Weight::from_parts(0, 13581)) + .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `XcmPallet::QueryCounter` (r:1 w:1) @@ -276,8 +342,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1485` - // Minimum execution time: 2_732_000 picoseconds. - Weight::from_parts(2_892_000, 0) + // Minimum execution time: 2_067_000 picoseconds. + Weight::from_parts(2_195_000, 0) .saturating_add(Weight::from_parts(0, 1485)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -288,10 +354,22 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7576` // Estimated: `11041` - // Minimum execution time: 23_813_000 picoseconds. - Weight::from_parts(24_201_000, 0) + // Minimum execution time: 23_982_000 picoseconds. + Weight::from_parts(24_409_000, 0) .saturating_add(Weight::from_parts(0, 11041)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `XcmPallet::AssetTraps` (r:1 w:1) + /// Proof: `XcmPallet::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `23` + // Estimated: `3488` + // Minimum execution time: 33_430_000 picoseconds. + Weight::from_parts(34_433_000, 0) + .saturating_add(Weight::from_parts(0, 3488)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_assigner_on_demand.rs b/polkadot/runtime/rococo/src/weights/runtime_parachains_assigner_on_demand.rs index ac0f05301b486dbdbb8c0ca004e195ab47171ff3..dba9e7904c79065c33c9fffc9851171c72c2e66a 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_parachains_assigner_on_demand.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_parachains_assigner_on_demand.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `runtime_parachains::assigner_on_demand` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-08-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-18, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-fljshgub-project-163-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -31,11 +31,11 @@ // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot/.git/.artifacts/bench.json +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json // --pallet=runtime_parachains::assigner_on_demand // --chain=rococo-dev -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -48,44 +48,44 @@ use core::marker::PhantomData; /// Weight functions for `runtime_parachains::assigner_on_demand`. pub struct WeightInfo(PhantomData); impl runtime_parachains::assigner_on_demand::WeightInfo for WeightInfo { - /// Storage: `OnDemandAssignmentProvider::SpotTraffic` (r:1 w:0) - /// Proof: `OnDemandAssignmentProvider::SpotTraffic` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Paras::ParaLifecycles` (r:1 w:0) - /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `OnDemandAssignmentProvider::OnDemandQueue` (r:1 w:1) - /// Proof: `OnDemandAssignmentProvider::OnDemandQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `OnDemandAssignmentProvider::QueueStatus` (r:1 w:1) + /// Proof: `OnDemandAssignmentProvider::QueueStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `OnDemandAssignmentProvider::ParaIdAffinity` (r:1 w:0) + /// Proof: `OnDemandAssignmentProvider::ParaIdAffinity` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `OnDemandAssignmentProvider::FreeEntries` (r:1 w:1) + /// Proof: `OnDemandAssignmentProvider::FreeEntries` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `s` is `[1, 9999]`. fn place_order_keep_alive(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `297 + s * (4 ±0)` - // Estimated: `3762 + s * (4 ±0)` - // Minimum execution time: 33_522_000 picoseconds. - Weight::from_parts(35_436_835, 0) - .saturating_add(Weight::from_parts(0, 3762)) - // Standard Error: 129 - .saturating_add(Weight::from_parts(14_041, 0).saturating_mul(s.into())) + // Measured: `218 + s * (8 ±0)` + // Estimated: `3681 + s * (8 ±0)` + // Minimum execution time: 21_053_000 picoseconds. + Weight::from_parts(17_291_897, 0) + .saturating_add(Weight::from_parts(0, 3681)) + // Standard Error: 104 + .saturating_add(Weight::from_parts(18_779, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) - .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(Weight::from_parts(0, 8).saturating_mul(s.into())) } - /// Storage: `OnDemandAssignmentProvider::SpotTraffic` (r:1 w:0) - /// Proof: `OnDemandAssignmentProvider::SpotTraffic` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Paras::ParaLifecycles` (r:1 w:0) - /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `OnDemandAssignmentProvider::OnDemandQueue` (r:1 w:1) - /// Proof: `OnDemandAssignmentProvider::OnDemandQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `OnDemandAssignmentProvider::QueueStatus` (r:1 w:1) + /// Proof: `OnDemandAssignmentProvider::QueueStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `OnDemandAssignmentProvider::ParaIdAffinity` (r:1 w:0) + /// Proof: `OnDemandAssignmentProvider::ParaIdAffinity` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `OnDemandAssignmentProvider::FreeEntries` (r:1 w:1) + /// Proof: `OnDemandAssignmentProvider::FreeEntries` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `s` is `[1, 9999]`. fn place_order_allow_death(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `297 + s * (4 ±0)` - // Estimated: `3762 + s * (4 ±0)` - // Minimum execution time: 33_488_000 picoseconds. - Weight::from_parts(34_848_934, 0) - .saturating_add(Weight::from_parts(0, 3762)) - // Standard Error: 143 - .saturating_add(Weight::from_parts(14_215, 0).saturating_mul(s.into())) + // Measured: `218 + s * (8 ±0)` + // Estimated: `3681 + s * (8 ±0)` + // Minimum execution time: 20_843_000 picoseconds. + Weight::from_parts(16_881_986, 0) + .saturating_add(Weight::from_parts(0, 3681)) + // Standard Error: 104 + .saturating_add(Weight::from_parts(18_788, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) - .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(Weight::from_parts(0, 8).saturating_mul(s.into())) } } diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_configuration.rs b/polkadot/runtime/rococo/src/weights/runtime_parachains_configuration.rs index 34541b83597e6284a401a171e703b200366114a0..ca0575cb1b64689b7b016d63a2cfc5080dc874a5 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_parachains_configuration.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_parachains_configuration.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `runtime_parachains::configuration` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-yprdrvc7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -58,8 +58,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_793_000 picoseconds. - Weight::from_parts(8_192_000, 0) + // Minimum execution time: 7_789_000 picoseconds. + Weight::from_parts(8_269_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -74,8 +74,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_819_000 picoseconds. - Weight::from_parts(8_004_000, 0) + // Minimum execution time: 7_851_000 picoseconds. + Weight::from_parts(8_152_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -90,8 +90,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_760_000 picoseconds. - Weight::from_parts(8_174_000, 0) + // Minimum execution time: 7_960_000 picoseconds. + Weight::from_parts(8_276_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -116,8 +116,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_814_000 picoseconds. - Weight::from_parts(8_098_000, 0) + // Minimum execution time: 7_912_000 picoseconds. + Weight::from_parts(8_164_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -132,8 +132,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 10_028_000 picoseconds. - Weight::from_parts(10_386_000, 0) + // Minimum execution time: 9_782_000 picoseconds. + Weight::from_parts(10_373_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -148,8 +148,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_867_000 picoseconds. - Weight::from_parts(8_191_000, 0) + // Minimum execution time: 7_870_000 picoseconds. + Weight::from_parts(8_274_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -164,8 +164,24 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 10_158_000 picoseconds. - Weight::from_parts(10_430_000, 0) + // Minimum execution time: 9_960_000 picoseconds. + Weight::from_parts(10_514_000, 0) + .saturating_add(Weight::from_parts(0, 1636)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Configuration::PendingConfigs` (r:1 w:1) + /// Proof: `Configuration::PendingConfigs` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Configuration::BypassConsistencyCheck` (r:1 w:0) + /// Proof: `Configuration::BypassConsistencyCheck` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_config_with_scheduler_params() -> Weight { + // Proof Size summary in bytes: + // Measured: `151` + // Estimated: `1636` + // Minimum execution time: 7_913_000 picoseconds. + Weight::from_parts(8_338_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_paras_inherent.rs b/polkadot/runtime/rococo/src/weights/runtime_parachains_paras_inherent.rs index a102d1903b2f2b4a757edf3bb96809c982f31383..c250c86665be0eebf54e570e73290a3ebdbdab82 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_parachains_paras_inherent.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_parachains_paras_inherent.rs @@ -13,161 +13,322 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . + //! Autogenerated weights for `runtime_parachains::paras_inherent` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2021-11-20, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 128 +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-18, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 // Executed Command: -// target/release/polkadot +// target/production/polkadot // benchmark -// --chain=rococo-dev +// pallet // --steps=50 // --repeat=20 -// --pallet=runtime_parachains::paras_inherent // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./runtime/rococo/src/weights/runtime_parachains_paras_inherent.rs -// --header=./file_header.txt +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=runtime_parachains::paras_inherent +// --chain=rococo-dev +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::Weight}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions for `runtime_parachains::paras_inherent`. pub struct WeightInfo(PhantomData); impl runtime_parachains::paras_inherent::WeightInfo for WeightInfo { - // Storage: ParaInherent Included (r:1 w:1) - // Storage: System ParentHash (r:1 w:0) - // Storage: ParaScheduler AvailabilityCores (r:1 w:1) - // Storage: ParasShared CurrentSessionIndex (r:1 w:0) - // Storage: Configuration ActiveConfig (r:1 w:0) - // Storage: ParaSessionInfo Sessions (r:1 w:0) - // Storage: ParasDisputes Disputes (r:1 w:1) - // Storage: ParasDisputes Included (r:1 w:1) - // Storage: ParasDisputes SpamSlots (r:1 w:1) - // Storage: ParasDisputes Frozen (r:1 w:0) - // Storage: ParaInclusion PendingAvailability (r:2 w:1) - // Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - // Storage: Paras Parachains (r:1 w:0) - // Storage: ParaInclusion PendingAvailabilityCommitments (r:1 w:1) - // Storage: Dmp DownwardMessageQueues (r:1 w:1) - // Storage: Hrmp HrmpChannelDigests (r:1 w:1) - // Storage: Paras FutureCodeUpgrades (r:1 w:0) - // Storage: ParaScheduler SessionStartBlock (r:1 w:0) - // Storage: ParaScheduler ParathreadQueue (r:1 w:1) - // Storage: ParaScheduler Scheduled (r:1 w:1) - // Storage: ParaScheduler ValidatorGroups (r:1 w:0) - // Storage: Ump NeedsDispatch (r:1 w:1) - // Storage: Ump NextDispatchRoundStartWith (r:1 w:1) - // Storage: ParaInherent OnChainVotes (r:0 w:1) - // Storage: Hrmp HrmpWatermarks (r:0 w:1) - // Storage: Paras Heads (r:0 w:1) + /// Storage: `ParaInherent::Included` (r:1 w:1) + /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::ParentHash` (r:1 w:0) + /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) + /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) + /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) + /// Proof: `Babe::AuthorVrfRandomness` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) + /// Storage: `ParaSessionInfo::Sessions` (r:1 w:0) + /// Proof: `ParaSessionInfo::Sessions` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Disputes` (r:1 w:1) + /// Proof: `ParasDisputes::Disputes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::BackersOnDisputes` (r:1 w:1) + /// Proof: `ParasDisputes::BackersOnDisputes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Included` (r:1 w:1) + /// Proof: `ParasDisputes::Included` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaInherent::OnChainVotes` (r:1 w:1) + /// Proof: `ParaInherent::OnChainVotes` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Frozen` (r:1 w:0) + /// Proof: `ParasDisputes::Frozen` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaInclusion::V1` (r:2 w:1) + /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:1) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannelDigests` (r:1 w:1) + /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) + /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::Paras` (r:1 w:0) + /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) + /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorIndices` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::DisabledValidators` (r:1 w:0) + /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) + /// Proof: `Hrmp::HrmpWatermarks` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::Heads` (r:0 w:1) + /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeGoAheadSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::MostRecentContext` (r:0 w:1) + /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `v` is `[10, 200]`. fn enter_variable_disputes(v: u32, ) -> Weight { - Weight::from_parts(352_590_000 as u64, 0) - // Standard Error: 13_000 - .saturating_add(Weight::from_parts(49_254_000 as u64, 0).saturating_mul(v as u64)) - .saturating_add(T::DbWeight::get().reads(24 as u64)) - .saturating_add(T::DbWeight::get().writes(16 as u64)) + // Proof Size summary in bytes: + // Measured: `67785` + // Estimated: `73725 + v * (23 ±0)` + // Minimum execution time: 949_716_000 picoseconds. + Weight::from_parts(482_361_515, 0) + .saturating_add(Weight::from_parts(0, 73725)) + // Standard Error: 17_471 + .saturating_add(Weight::from_parts(50_100_764, 0).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(25)) + .saturating_add(T::DbWeight::get().writes(15)) + .saturating_add(Weight::from_parts(0, 23).saturating_mul(v.into())) } - // Storage: ParaInherent Included (r:1 w:1) - // Storage: System ParentHash (r:1 w:0) - // Storage: ParaScheduler AvailabilityCores (r:1 w:1) - // Storage: ParasShared CurrentSessionIndex (r:1 w:0) - // Storage: Configuration ActiveConfig (r:1 w:0) - // Storage: ParasDisputes Frozen (r:1 w:0) - // Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - // Storage: Paras Parachains (r:1 w:0) - // Storage: ParaInclusion PendingAvailability (r:2 w:1) - // Storage: ParaInclusion PendingAvailabilityCommitments (r:1 w:1) - // Storage: Dmp DownwardMessageQueues (r:1 w:1) - // Storage: Hrmp HrmpChannelDigests (r:1 w:1) - // Storage: Paras FutureCodeUpgrades (r:1 w:0) - // Storage: ParasDisputes Disputes (r:1 w:0) - // Storage: ParaScheduler SessionStartBlock (r:1 w:0) - // Storage: ParaScheduler ParathreadQueue (r:1 w:1) - // Storage: ParaScheduler Scheduled (r:1 w:1) - // Storage: ParaScheduler ValidatorGroups (r:1 w:0) - // Storage: Ump NeedsDispatch (r:1 w:1) - // Storage: Ump NextDispatchRoundStartWith (r:1 w:1) - // Storage: ParaInclusion AvailabilityBitfields (r:0 w:1) - // Storage: ParaInherent OnChainVotes (r:0 w:1) - // Storage: ParasDisputes Included (r:0 w:1) - // Storage: Hrmp HrmpWatermarks (r:0 w:1) - // Storage: Paras Heads (r:0 w:1) + /// Storage: `ParaInherent::Included` (r:1 w:1) + /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::ParentHash` (r:1 w:0) + /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) + /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) + /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) + /// Proof: `Babe::AuthorVrfRandomness` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) + /// Storage: `ParaInherent::OnChainVotes` (r:1 w:1) + /// Proof: `ParaInherent::OnChainVotes` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Frozen` (r:1 w:0) + /// Proof: `ParasDisputes::Frozen` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaInclusion::V1` (r:2 w:1) + /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:1) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannelDigests` (r:1 w:1) + /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) + /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::Paras` (r:1 w:0) + /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Disputes` (r:1 w:0) + /// Proof: `ParasDisputes::Disputes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) + /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorIndices` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::DisabledValidators` (r:1 w:0) + /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Included` (r:0 w:1) + /// Proof: `ParasDisputes::Included` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) + /// Proof: `Hrmp::HrmpWatermarks` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::Heads` (r:0 w:1) + /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeGoAheadSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::MostRecentContext` (r:0 w:1) + /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) fn enter_bitfields() -> Weight { - Weight::from_parts(299_878_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(21 as u64)) - .saturating_add(T::DbWeight::get().writes(15 as u64)) + // Proof Size summary in bytes: + // Measured: `42757` + // Estimated: `48697` + // Minimum execution time: 437_627_000 picoseconds. + Weight::from_parts(460_975_000, 0) + .saturating_add(Weight::from_parts(0, 48697)) + .saturating_add(T::DbWeight::get().reads(23)) + .saturating_add(T::DbWeight::get().writes(15)) } - // Storage: ParaInherent Included (r:1 w:1) - // Storage: System ParentHash (r:1 w:0) - // Storage: ParaScheduler AvailabilityCores (r:1 w:1) - // Storage: ParasShared CurrentSessionIndex (r:1 w:0) - // Storage: Configuration ActiveConfig (r:1 w:0) - // Storage: ParasDisputes Frozen (r:1 w:0) - // Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - // Storage: Paras Parachains (r:1 w:0) - // Storage: ParaInclusion PendingAvailability (r:2 w:1) - // Storage: ParaInclusion PendingAvailabilityCommitments (r:1 w:1) - // Storage: Dmp DownwardMessageQueues (r:1 w:1) - // Storage: Hrmp HrmpChannelDigests (r:1 w:1) - // Storage: Paras FutureCodeUpgrades (r:1 w:0) - // Storage: ParasDisputes Disputes (r:2 w:0) - // Storage: ParaScheduler SessionStartBlock (r:1 w:0) - // Storage: ParaScheduler ParathreadQueue (r:1 w:1) - // Storage: ParaScheduler Scheduled (r:1 w:1) - // Storage: ParaScheduler ValidatorGroups (r:1 w:0) - // Storage: Paras PastCodeMeta (r:1 w:0) - // Storage: Paras CurrentCodeHash (r:1 w:0) - // Storage: Ump RelayDispatchQueueSize (r:1 w:0) - // Storage: Ump NeedsDispatch (r:1 w:1) - // Storage: Ump NextDispatchRoundStartWith (r:1 w:1) - // Storage: ParaInherent OnChainVotes (r:0 w:1) - // Storage: ParasDisputes Included (r:0 w:1) - // Storage: Hrmp HrmpWatermarks (r:0 w:1) - // Storage: Paras Heads (r:0 w:1) - fn enter_backed_candidates_variable(_v: u32) -> Weight { - Weight::from_parts(442_472_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(25 as u64)) - .saturating_add(T::DbWeight::get().writes(14 as u64)) + /// Storage: `ParaInherent::Included` (r:1 w:1) + /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::ParentHash` (r:1 w:0) + /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) + /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) + /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) + /// Proof: `Babe::AuthorVrfRandomness` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) + /// Storage: `ParaInherent::OnChainVotes` (r:1 w:1) + /// Proof: `ParaInherent::OnChainVotes` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Frozen` (r:1 w:0) + /// Proof: `ParasDisputes::Frozen` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaInclusion::V1` (r:2 w:1) + /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:1) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannelDigests` (r:1 w:1) + /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) + /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::Paras` (r:1 w:0) + /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Disputes` (r:1 w:0) + /// Proof: `ParasDisputes::Disputes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) + /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CurrentCodeHash` (r:1 w:0) + /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:0) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:0) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `ParasShared::ActiveValidatorIndices` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::DisabledValidators` (r:1 w:0) + /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Included` (r:0 w:1) + /// Proof: `ParasDisputes::Included` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) + /// Proof: `Hrmp::HrmpWatermarks` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::Heads` (r:0 w:1) + /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeGoAheadSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::MostRecentContext` (r:0 w:1) + /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `v` is `[101, 200]`. + fn enter_backed_candidates_variable(v: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `42829` + // Estimated: `48769` + // Minimum execution time: 1_305_254_000 picoseconds. + Weight::from_parts(1_347_160_667, 0) + .saturating_add(Weight::from_parts(0, 48769)) + // Standard Error: 22_128 + .saturating_add(Weight::from_parts(57_229, 0).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(26)) + .saturating_add(T::DbWeight::get().writes(15)) } - // Storage: ParaInherent Included (r:1 w:1) - // Storage: System ParentHash (r:1 w:0) - // Storage: ParaScheduler AvailabilityCores (r:1 w:1) - // Storage: ParasShared CurrentSessionIndex (r:1 w:0) - // Storage: Configuration ActiveConfig (r:1 w:0) - // Storage: ParasDisputes Frozen (r:1 w:0) - // Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - // Storage: Paras Parachains (r:1 w:0) - // Storage: ParaInclusion PendingAvailability (r:2 w:1) - // Storage: ParaInclusion PendingAvailabilityCommitments (r:1 w:1) - // Storage: Dmp DownwardMessageQueues (r:1 w:1) - // Storage: Hrmp HrmpChannelDigests (r:1 w:1) - // Storage: Paras FutureCodeUpgrades (r:1 w:0) - // Storage: ParasDisputes Disputes (r:2 w:0) - // Storage: ParaScheduler SessionStartBlock (r:1 w:0) - // Storage: ParaScheduler ParathreadQueue (r:1 w:1) - // Storage: ParaScheduler Scheduled (r:1 w:1) - // Storage: ParaScheduler ValidatorGroups (r:1 w:0) - // Storage: Paras PastCodeMeta (r:1 w:0) - // Storage: Paras CurrentCodeHash (r:1 w:0) - // Storage: Ump RelayDispatchQueueSize (r:1 w:0) - // Storage: Ump NeedsDispatch (r:1 w:1) - // Storage: Ump NextDispatchRoundStartWith (r:1 w:1) - // Storage: ParaInherent OnChainVotes (r:0 w:1) - // Storage: ParasDisputes Included (r:0 w:1) - // Storage: Hrmp HrmpWatermarks (r:0 w:1) - // Storage: Paras Heads (r:0 w:1) + /// Storage: `ParaInherent::Included` (r:1 w:1) + /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::ParentHash` (r:1 w:0) + /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) + /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) + /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) + /// Proof: `Babe::AuthorVrfRandomness` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) + /// Storage: `ParaInherent::OnChainVotes` (r:1 w:1) + /// Proof: `ParaInherent::OnChainVotes` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Frozen` (r:1 w:0) + /// Proof: `ParasDisputes::Frozen` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaInclusion::V1` (r:2 w:1) + /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:1) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannelDigests` (r:1 w:1) + /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) + /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::Paras` (r:1 w:0) + /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Disputes` (r:1 w:0) + /// Proof: `ParasDisputes::Disputes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) + /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CurrentCodeHash` (r:1 w:0) + /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::FutureCodeHash` (r:1 w:0) + /// Proof: `Paras::FutureCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeRestrictionSignal` (r:1 w:0) + /// Proof: `Paras::UpgradeRestrictionSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:0) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:0) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `ParasShared::ActiveValidatorIndices` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::DisabledValidators` (r:1 w:0) + /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Included` (r:0 w:1) + /// Proof: `ParasDisputes::Included` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) + /// Proof: `Hrmp::HrmpWatermarks` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::Heads` (r:0 w:1) + /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeGoAheadSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::MostRecentContext` (r:0 w:1) + /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) fn enter_backed_candidate_code_upgrade() -> Weight { - Weight::from_parts(36_903_411_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(25 as u64)) - .saturating_add(T::DbWeight::get().writes(14 as u64)) + // Proof Size summary in bytes: + // Measured: `42842` + // Estimated: `48782` + // Minimum execution time: 38_637_547_000 picoseconds. + Weight::from_parts(41_447_412_000, 0) + .saturating_add(Weight::from_parts(0, 48782)) + .saturating_add(T::DbWeight::get().reads(28)) + .saturating_add(T::DbWeight::get().writes(15)) } } diff --git a/polkadot/runtime/rococo/src/xcm_config.rs b/polkadot/runtime/rococo/src/xcm_config.rs index af8981ddcc146573bdf2179c9baae856269e4b93..c7063bd7ad616c2e5cb7b2e3f8c397087e522c33 100644 --- a/polkadot/runtime/rococo/src/xcm_config.rs +++ b/polkadot/runtime/rococo/src/xcm_config.rs @@ -39,7 +39,7 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, - ChildParachainConvertsVia, DescribeBodyTerminal, DescribeFamily, FixedWeightBounds, + ChildParachainConvertsVia, DescribeAllTerminal, DescribeFamily, FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, HashedDescription, IsChildSystemParachain, IsConcrete, MintLocation, OriginToPluralityVoice, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, @@ -63,8 +63,8 @@ pub type LocationConverter = ( ChildParachainConvertsVia, // We can directly alias an `AccountId32` into a local account. AccountId32Aliases, - // Allow governance body to be used as a sovereign account. - HashedDescription>, + // Foreign locations alias into accounts according to a hash of their standard description. + HashedDescription>, ); /// Our asset transactor. This is what allows us to interest with the runtime facilities from the @@ -221,6 +221,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } parameter_types! { @@ -256,11 +259,9 @@ pub type LocalPalletOriginToLocation = ( impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; - // We only allow the root, fellows and the staking admin to send messages. - // This is basically safe to enable for everyone (safe the possibility of someone spamming the - // parachain if they're willing to pay the KSM to send from the Relay-chain), but it's useless - // until we bring in XCM v3 which will make `DescendOrigin` a bit more useful. - type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + // Note that this configuration of `SendXcmOrigin` is different from the one present in + // production. + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; type XcmRouter = XcmRouter; // Anyone can execute XCM messages locally. type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index b74def5de8aadd718df48b27aff1a7ca324389c3..8a3cd9309dbd84bb9f6c92d9ef0dabb13a36439a 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -139,7 +139,7 @@ parameter_types! { pub const SS58Prefix: u8 = 42; } -#[derive_impl(frame_system::config_preludes::RelayChainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::RelayChainDefaultConfig)] impl frame_system::Config for Runtime { type BlockWeights = BlockWeights; type BlockLength = BlockLength; @@ -767,7 +767,7 @@ sp_api::impl_runtime_apis! { Executive::execute_block(block); } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } diff --git a/polkadot/runtime/test-runtime/src/xcm_config.rs b/polkadot/runtime/test-runtime/src/xcm_config.rs index a81d35f4d788f1628cf20cf5cbd0e366828101ff..8411b79f2529d8e2d26815bd9ab806457e4ca99b 100644 --- a/polkadot/runtime/test-runtime/src/xcm_config.rs +++ b/polkadot/runtime/test-runtime/src/xcm_config.rs @@ -16,14 +16,16 @@ use frame_support::{ parameter_types, - traits::{Everything, Nothing}, + traits::{Everything, Get, Nothing}, weights::Weight, }; use frame_system::EnsureRoot; +use polkadot_runtime_parachains::FeeTracker; +use runtime_common::xcm_sender::{ChildParachainRouter, PriceForMessageDelivery}; use xcm::latest::prelude::*; use xcm_builder::{ AllowUnpaidExecutionFrom, EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, - SignedAccountId32AsNative, SignedToAccountId32, + SignedAccountId32AsNative, SignedToAccountId32, WithUniqueTopic, }; use xcm_executor::{ traits::{TransactAsset, WeightTrader}, @@ -36,6 +38,8 @@ parameter_types! { pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 16; pub const UniversalLocation: xcm::latest::InteriorLocation = xcm::latest::Junctions::Here; + pub TokenLocation: Location = Here.into_location(); + pub FeeAssetId: AssetId = AssetId(TokenLocation::get()); } /// Type to convert an `Origin` type value into a `Location` value which represents an interior @@ -45,17 +49,43 @@ pub type LocalOriginToLocation = ( SignedToAccountId32, ); -pub struct DoNothingRouter; -impl SendXcm for DoNothingRouter { - type Ticket = (); - fn validate(_dest: &mut Option, _msg: &mut Option>) -> SendResult<()> { - Ok(((), Assets::new())) - } - fn deliver(_: ()) -> Result { - Ok([0; 32]) +/// Implementation of [`PriceForMessageDelivery`], returning a different price +/// based on whether a message contains a reanchored asset or not. +/// This implementation ensures that messages with non-reanchored assets return higher +/// prices than messages with reanchored assets. +/// Useful for `deposit_reserve_asset_works_for_any_xcm_sender` integration test. +pub struct TestDeliveryPrice(sp_std::marker::PhantomData<(A, F)>); +impl, F: FeeTracker> PriceForMessageDelivery for TestDeliveryPrice { + type Id = F::Id; + + fn price_for_delivery(_: Self::Id, msg: &Xcm<()>) -> Assets { + let base_fee: super::Balance = 1_000_000; + + let parents = msg.iter().find_map(|xcm| match xcm { + ReserveAssetDeposited(assets) => { + let AssetId(location) = &assets.inner().first().unwrap().id; + Some(location.parents) + }, + _ => None, + }); + + // If no asset is found, price defaults to `base_fee`. + let amount = base_fee + .saturating_add(base_fee.saturating_mul(parents.unwrap_or(0) as super::Balance)); + + (A::get(), amount).into() } } +pub type PriceForChildParachainDelivery = TestDeliveryPrice; + +/// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our +/// individual routers. +pub type XcmRouter = WithUniqueTopic< + // Only one router so far - use DMP to communicate with child parachains. + ChildParachainRouter, +>; + pub type Barrier = AllowUnpaidExecutionFrom; pub struct DummyAssetTransactor; @@ -99,7 +129,7 @@ type OriginConverter = ( pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = super::RuntimeCall; - type XcmSender = DoNothingRouter; + type XcmSender = XcmRouter; type AssetTransactor = DummyAssetTransactor; type OriginConverter = OriginConverter; type IsReserve = (); @@ -123,6 +153,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } impl pallet_xcm::Config for crate::Runtime { @@ -133,7 +166,7 @@ impl pallet_xcm::Config for crate::Runtime { type UniversalLocation = UniversalLocation; type SendXcmOrigin = EnsureXcmOrigin; type Weigher = FixedWeightBounds; - type XcmRouter = DoNothingRouter; + type XcmRouter = XcmRouter; type XcmExecuteFilter = Everything; type XcmExecutor = xcm_executor::XcmExecutor; type XcmTeleportFilter = Everything; diff --git a/polkadot/runtime/westend/Cargo.toml b/polkadot/runtime/westend/Cargo.toml index 4180828bcfb14d497d9da06bc74da91fa53f8d5f..587a6c9a590513fe9c3840c96fbc5af3ba5750d6 100644 --- a/polkadot/runtime/westend/Cargo.toml +++ b/polkadot/runtime/westend/Cargo.toml @@ -45,7 +45,7 @@ sp-npos-elections = { path = "../../../substrate/primitives/npos-elections", def frame-election-provider-support = { path = "../../../substrate/frame/election-provider-support", default-features = false } frame-executive = { path = "../../../substrate/frame/executive", default-features = false } -frame-support = { path = "../../../substrate/frame/support", default-features = false, features = ["tuples-96"] } +frame-support = { path = "../../../substrate/frame/support", default-features = false, features = ["experimental", "tuples-96"] } frame-system = { path = "../../../substrate/frame/system", default-features = false } frame-system-rpc-runtime-api = { path = "../../../substrate/frame/system/rpc/runtime-api", default-features = false } westend-runtime-constants = { package = "westend-runtime-constants", path = "constants", default-features = false } @@ -114,6 +114,7 @@ runtime-parachains = { package = "polkadot-runtime-parachains", path = "../parac xcm = { package = "staging-xcm", path = "../../xcm", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../xcm/xcm-executor", default-features = false } xcm-builder = { package = "staging-xcm-builder", path = "../../xcm/xcm-builder", default-features = false } +xcm-fee-payment-runtime-api = { path = "../../xcm/xcm-fee-payment-runtime-api", default-features = false } [dev-dependencies] hex-literal = "0.4.1" @@ -227,6 +228,7 @@ std = [ "westend-runtime-constants/std", "xcm-builder/std", "xcm-executor/std", + "xcm-fee-payment-runtime-api/std", "xcm/std", ] runtime-benchmarks = [ diff --git a/polkadot/runtime/westend/constants/src/lib.rs b/polkadot/runtime/westend/constants/src/lib.rs index 848cccd559dc3e50cbd2a50c4df6a643910fe35c..c98f4b114fd88241dea1e2e5ffcf694848448007 100644 --- a/polkadot/runtime/westend/constants/src/lib.rs +++ b/polkadot/runtime/westend/constants/src/lib.rs @@ -107,6 +107,8 @@ pub mod system_parachain { pub const COLLECTIVES_ID: u32 = 1001; /// BridgeHub parachain ID. pub const BRIDGE_HUB_ID: u32 = 1002; + /// Encointer parachain ID. + pub const ENCOINTER_ID: u32 = 1003; /// People Chain parachain ID. pub const PEOPLE_ID: u32 = 1004; /// Brokerage parachain ID. @@ -128,6 +130,8 @@ pub mod xcm { const ROOT_INDEX: u32 = 0; // The bodies corresponding to the Polkadot OpenGov Origins. pub const FELLOWSHIP_ADMIN_INDEX: u32 = 1; + #[deprecated = "Will be removed after August 2024; Use `xcm::latest::BodyId::Treasury` \ + instead"] pub const TREASURER_INDEX: u32 = 2; } } diff --git a/polkadot/runtime/westend/src/impls.rs b/polkadot/runtime/westend/src/impls.rs index 71e6b696a20a0feb89e669067d02b12e6eeb89fd..d8741c939a507d49c181b3620d322c08692d730b 100644 --- a/polkadot/runtime/westend/src/impls.rs +++ b/polkadot/runtime/westend/src/impls.rs @@ -167,11 +167,16 @@ where }, ]); + let encoded_versioned_xcm = + VersionedXcm::V4(program).encode().try_into().map_err(|error| { + log::error!(target: "runtime::on_reap_identity", "XCM too large, error: {:?}", error); + pallet_xcm::Error::::XcmTooLarge + })?; // send - let _ = >::send( + let _ = >::send_blob( RawOrigin::Root.into(), Box::new(VersionedLocation::V4(destination)), - Box::new(VersionedXcm::V4(program)), + encoded_versioned_xcm, )?; Ok(()) } diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index bbb010f60bffe8a8c99050ceb79d11f13143e0c6..02397f35368e91e4af202409b120811012fb51a5 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -17,7 +17,7 @@ //! The Westend runtime. This can be compiled with `#[no_std]`, ready for Wasm. #![cfg_attr(not(feature = "std"), no_std)] -// `construct_runtime!` does a lot of recursion and requires us to increase the limit. +// `#[frame_support::runtime]!` does a lot of recursion and requires us to increase the limit. #![recursion_limit = "512"] use authority_discovery_primitives::AuthorityId as AuthorityDiscoveryId; @@ -27,7 +27,7 @@ use beefy_primitives::{ }; use frame_election_provider_support::{bounds::ElectionBoundsBuilder, onchain, SequentialPhragmen}; use frame_support::{ - construct_runtime, derive_impl, + derive_impl, genesis_builder_helper::{build_config, create_default_config}, parameter_types, traits::{ @@ -35,7 +35,7 @@ use frame_support::{ InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, ProcessMessage, ProcessMessageError, WithdrawReasons, }, - weights::{ConstantMultiplier, WeightMeter}, + weights::{ConstantMultiplier, WeightMeter, WeightToFee as _}, PalletId, }; use frame_system::{EnsureRoot, EnsureSigned}; @@ -62,15 +62,14 @@ use runtime_common::{ LocatableAssetConverter, ToAuthor, VersionedLocatableAsset, VersionedLocationConverter, }, paras_registrar, paras_sudo_wrapper, prod_or_fast, slots, - traits::Leaser, + traits::{Leaser, OnSwap}, BalanceToU256, BlockHashCount, BlockLength, CurrencyToVote, SlowAdjustingFeeUpdate, U256ToBalance, }; use runtime_parachains::{ assigner_coretime as parachains_assigner_coretime, - assigner_on_demand as parachains_assigner_on_demand, - assigner_parachains as parachains_assigner_parachains, - configuration as parachains_configuration, coretime, disputes as parachains_disputes, + assigner_on_demand as parachains_assigner_on_demand, configuration as parachains_configuration, + coretime, disputes as parachains_disputes, disputes::slashing as parachains_slashing, dmp as parachains_dmp, hrmp as parachains_hrmp, inclusion as parachains_inclusion, inclusion::{AggregateMessageOrigin, UmpQueueId}, @@ -102,11 +101,13 @@ use sp_std::{collections::btree_map::BTreeMap, prelude::*}; use sp_version::NativeVersion; use sp_version::RuntimeVersion; use xcm::{ - latest::{InteriorLocation, Junction, Junction::PalletInstance}, - VersionedLocation, + latest::prelude::*, IntoVersion, VersionedAssetId, VersionedAssets, VersionedLocation, + VersionedXcm, }; use xcm_builder::PayOverXcm; +use xcm_fee_payment_runtime_api::Error as XcmPaymentApiError; + pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; pub use pallet_election_provider_multi_phase::{Call as EPMCall, GeometricDepositBase}; @@ -151,7 +152,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("westend"), impl_name: create_runtime_str!("parity-westend"), authoring_version: 2, - spec_version: 1_007_001, + spec_version: 1_009_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 24, @@ -187,7 +188,7 @@ parameter_types! { pub const SS58Prefix: u8 = 42; } -#[derive_impl(frame_system::config_preludes::RelayChainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::RelayChainDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = EverythingBut; type BlockWeights = BlockWeights; @@ -1243,8 +1244,6 @@ impl parachains_assigner_on_demand::Config for Runtime { type WeightInfo = weights::runtime_parachains_assigner_on_demand::WeightInfo; } -impl parachains_assigner_parachains::Config for Runtime {} - impl parachains_assigner_coretime::Config for Runtime {} impl parachains_initializer::Config for Runtime { @@ -1305,7 +1304,7 @@ impl paras_registrar::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type OnSwap = (Crowdloan, Slots); + type OnSwap = (Crowdloan, Slots, SwapLeases); type ParaDeposit = ParaDeposit; type DataDepositPerByte = RegistrarDataDepositPerByte; type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; @@ -1417,131 +1416,211 @@ impl pallet_asset_rate::Config for Runtime { type BenchmarkHelper = runtime_common::impls::benchmarks::AssetRateArguments; } -construct_runtime! { - pub enum Runtime - { - // Basic stuff; balances is uncallable initially. - System: frame_system = 0, - - // Babe must be before session. - Babe: pallet_babe = 1, - - Timestamp: pallet_timestamp = 2, - Indices: pallet_indices = 3, - Balances: pallet_balances = 4, - TransactionPayment: pallet_transaction_payment = 26, - - // Consensus support. - // Authorship must be before session in order to note author in the correct session and era. - Authorship: pallet_authorship = 5, - Staking: pallet_staking = 6, - Offences: pallet_offences = 7, - Historical: session_historical = 27, - - Session: pallet_session = 8, - Grandpa: pallet_grandpa = 10, - AuthorityDiscovery: pallet_authority_discovery = 12, - - // Utility module. - Utility: pallet_utility = 16, - - // Less simple identity module. - Identity: pallet_identity = 17, - - // Social recovery module. - Recovery: pallet_recovery = 18, - - // Vesting. Usable initially, but removed once all vesting is finished. - Vesting: pallet_vesting = 19, - - // System scheduler. - Scheduler: pallet_scheduler = 20, - - // Preimage registrar. - Preimage: pallet_preimage = 28, - - // Sudo. - Sudo: pallet_sudo = 21, - - // Proxy module. Late addition. - Proxy: pallet_proxy = 22, - - // Multisig module. Late addition. - Multisig: pallet_multisig = 23, - - // Election pallet. Only works with staking, but placed here to maintain indices. - ElectionProviderMultiPhase: pallet_election_provider_multi_phase = 24, - - // Provides a semi-sorted list of nominators for staking. - VoterList: pallet_bags_list:: = 25, - - // Nomination pools for staking. - NominationPools: pallet_nomination_pools = 29, - - // Fast unstake pallet: extension to staking. - FastUnstake: pallet_fast_unstake = 30, - - // OpenGov - ConvictionVoting: pallet_conviction_voting = 31, - Referenda: pallet_referenda = 32, - Origins: pallet_custom_origins = 35, - Whitelist: pallet_whitelist = 36, - - // Treasury - Treasury: pallet_treasury = 37, - - // Parachains pallets. Start indices at 40 to leave room. - ParachainsOrigin: parachains_origin = 41, - Configuration: parachains_configuration = 42, - ParasShared: parachains_shared = 43, - ParaInclusion: parachains_inclusion = 44, - ParaInherent: parachains_paras_inherent = 45, - ParaScheduler: parachains_scheduler = 46, - Paras: parachains_paras = 47, - Initializer: parachains_initializer = 48, - Dmp: parachains_dmp = 49, - // RIP Ump 50 - Hrmp: parachains_hrmp = 51, - ParaSessionInfo: parachains_session_info = 52, - ParasDisputes: parachains_disputes = 53, - ParasSlashing: parachains_slashing = 54, - ParachainsAssignmentProvider: parachains_assigner_parachains = 55, - OnDemandAssignmentProvider: parachains_assigner_on_demand = 56, - CoretimeAssignmentProvider: parachains_assigner_coretime = 57, - - // Parachain Onboarding Pallets. Start indices at 60 to leave room. - Registrar: paras_registrar = 60, - Slots: slots = 61, - ParasSudoWrapper: paras_sudo_wrapper = 62, - Auctions: auctions = 63, - Crowdloan: crowdloan = 64, - AssignedSlots: assigned_slots = 65, - Coretime: coretime = 66, - - // Pallet for sending XCM. - XcmPallet: pallet_xcm = 99, - - // Generalized message queue - MessageQueue: pallet_message_queue = 100, - - // Asset rate. - AssetRate: pallet_asset_rate = 101, - - // Root testing pallet. - RootTesting: pallet_root_testing = 102, - - // BEEFY Bridges support. - Beefy: pallet_beefy = 200, - // MMR leaf construction must be after session in order to have a leaf's next_auth_set - // refer to block. See issue polkadot-fellows/runtimes#160 for details. - Mmr: pallet_mmr = 201, - BeefyMmrLeaf: pallet_beefy_mmr = 202, - - // Pallet for migrating Identity to a parachain. To be removed post-migration. - IdentityMigrator: identity_migrator = 248, +// Notify `coretime` pallet when a lease swap occurs +pub struct SwapLeases; +impl OnSwap for SwapLeases { + fn on_swap(one: ParaId, other: ParaId) { + coretime::Pallet::::on_legacy_lease_swap(one, other); } } +#[frame_support::runtime(legacy_ordering)] +mod runtime { + #[runtime::runtime] + #[runtime::derive( + RuntimeCall, + RuntimeEvent, + RuntimeError, + RuntimeOrigin, + RuntimeFreezeReason, + RuntimeHoldReason, + RuntimeSlashReason, + RuntimeLockId, + RuntimeTask + )] + pub struct Runtime; + + // Basic stuff; balances is uncallable initially. + #[runtime::pallet_index(0)] + pub type System = frame_system; + + // Babe must be before session. + #[runtime::pallet_index(1)] + pub type Babe = pallet_babe; + + #[runtime::pallet_index(2)] + pub type Timestamp = pallet_timestamp; + #[runtime::pallet_index(3)] + pub type Indices = pallet_indices; + #[runtime::pallet_index(4)] + pub type Balances = pallet_balances; + #[runtime::pallet_index(26)] + pub type TransactionPayment = pallet_transaction_payment; + + // Consensus support. + // Authorship must be before session in order to note author in the correct session and era. + #[runtime::pallet_index(5)] + pub type Authorship = pallet_authorship; + #[runtime::pallet_index(6)] + pub type Staking = pallet_staking; + #[runtime::pallet_index(7)] + pub type Offences = pallet_offences; + #[runtime::pallet_index(27)] + pub type Historical = session_historical; + + #[runtime::pallet_index(8)] + pub type Session = pallet_session; + #[runtime::pallet_index(10)] + pub type Grandpa = pallet_grandpa; + #[runtime::pallet_index(12)] + pub type AuthorityDiscovery = pallet_authority_discovery; + + // Utility module. + #[runtime::pallet_index(16)] + pub type Utility = pallet_utility; + + // Less simple identity module. + #[runtime::pallet_index(17)] + pub type Identity = pallet_identity; + + // Social recovery module. + #[runtime::pallet_index(18)] + pub type Recovery = pallet_recovery; + + // Vesting. Usable initially, but removed once all vesting is finished. + #[runtime::pallet_index(19)] + pub type Vesting = pallet_vesting; + + // System scheduler. + #[runtime::pallet_index(20)] + pub type Scheduler = pallet_scheduler; + + // Preimage registrar. + #[runtime::pallet_index(28)] + pub type Preimage = pallet_preimage; + + // Sudo. + #[runtime::pallet_index(21)] + pub type Sudo = pallet_sudo; + + // Proxy module. Late addition. + #[runtime::pallet_index(22)] + pub type Proxy = pallet_proxy; + + // Multisig module. Late addition. + #[runtime::pallet_index(23)] + pub type Multisig = pallet_multisig; + + // Election pallet. Only works with staking, but placed here to maintain indices. + #[runtime::pallet_index(24)] + pub type ElectionProviderMultiPhase = pallet_election_provider_multi_phase; + + // Provides a semi-sorted list of nominators for staking. + #[runtime::pallet_index(25)] + pub type VoterList = pallet_bags_list; + + // Nomination pools for staking. + #[runtime::pallet_index(29)] + pub type NominationPools = pallet_nomination_pools; + + // Fast unstake pallet = extension to staking. + #[runtime::pallet_index(30)] + pub type FastUnstake = pallet_fast_unstake; + + // OpenGov + #[runtime::pallet_index(31)] + pub type ConvictionVoting = pallet_conviction_voting; + #[runtime::pallet_index(32)] + pub type Referenda = pallet_referenda; + #[runtime::pallet_index(35)] + pub type Origins = pallet_custom_origins; + #[runtime::pallet_index(36)] + pub type Whitelist = pallet_whitelist; + + // Treasury + #[runtime::pallet_index(37)] + pub type Treasury = pallet_treasury; + + // Parachains pallets. Start indices at 40 to leave room. + #[runtime::pallet_index(41)] + pub type ParachainsOrigin = parachains_origin; + #[runtime::pallet_index(42)] + pub type Configuration = parachains_configuration; + #[runtime::pallet_index(43)] + pub type ParasShared = parachains_shared; + #[runtime::pallet_index(44)] + pub type ParaInclusion = parachains_inclusion; + #[runtime::pallet_index(45)] + pub type ParaInherent = parachains_paras_inherent; + #[runtime::pallet_index(46)] + pub type ParaScheduler = parachains_scheduler; + #[runtime::pallet_index(47)] + pub type Paras = parachains_paras; + #[runtime::pallet_index(48)] + pub type Initializer = parachains_initializer; + #[runtime::pallet_index(49)] + pub type Dmp = parachains_dmp; + // RIP Ump 50 + #[runtime::pallet_index(51)] + pub type Hrmp = parachains_hrmp; + #[runtime::pallet_index(52)] + pub type ParaSessionInfo = parachains_session_info; + #[runtime::pallet_index(53)] + pub type ParasDisputes = parachains_disputes; + #[runtime::pallet_index(54)] + pub type ParasSlashing = parachains_slashing; + #[runtime::pallet_index(56)] + pub type OnDemandAssignmentProvider = parachains_assigner_on_demand; + #[runtime::pallet_index(57)] + pub type CoretimeAssignmentProvider = parachains_assigner_coretime; + + // Parachain Onboarding Pallets. Start indices at 60 to leave room. + #[runtime::pallet_index(60)] + pub type Registrar = paras_registrar; + #[runtime::pallet_index(61)] + pub type Slots = slots; + #[runtime::pallet_index(62)] + pub type ParasSudoWrapper = paras_sudo_wrapper; + #[runtime::pallet_index(63)] + pub type Auctions = auctions; + #[runtime::pallet_index(64)] + pub type Crowdloan = crowdloan; + #[runtime::pallet_index(65)] + pub type AssignedSlots = assigned_slots; + #[runtime::pallet_index(66)] + pub type Coretime = coretime; + + // Pallet for sending XCM. + #[runtime::pallet_index(99)] + pub type XcmPallet = pallet_xcm; + + // Generalized message queue + #[runtime::pallet_index(100)] + pub type MessageQueue = pallet_message_queue; + + // Asset rate. + #[runtime::pallet_index(101)] + pub type AssetRate = pallet_asset_rate; + + // Root testing pallet. + #[runtime::pallet_index(102)] + pub type RootTesting = pallet_root_testing; + + // BEEFY Bridges support. + #[runtime::pallet_index(200)] + pub type Beefy = pallet_beefy; + // MMR leaf construction must be after session in order to have a leaf's next_auth_set + // refer to block. See issue polkadot-fellows/runtimes#160 for details. + #[runtime::pallet_index(201)] + pub type Mmr = pallet_mmr; + #[runtime::pallet_index(202)] + pub type BeefyMmrLeaf = pallet_beefy_mmr; + + // Pallet for migrating Identity to a parachain. To be removed post-migration. + #[runtime::pallet_index(248)] + pub type IdentityMigrator = identity_migrator; +} + /// The address format for describing accounts. pub type Address = sp_runtime::MultiAddress; /// Block header type as expected by this runtime. @@ -1590,11 +1669,11 @@ pub mod migrations { let now = frame_system::Pallet::::block_number(); let lease = slots::Pallet::::lease(para); if lease.is_empty() { - return None + return None; } // Lease not yet started, ignore: if lease.iter().any(Option::is_none) { - return None + return None; } let (index, _) = as Leaser>::lease_period_index(now)?; @@ -1616,7 +1695,7 @@ pub mod migrations { fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC { log::warn!(target: "runtime::session_keys", "Skipping session keys migration pre-upgrade check due to spec version (already applied?)"); - return Ok(Vec::new()) + return Ok(Vec::new()); } log::info!(target: "runtime::session_keys", "Collecting pre-upgrade session keys state"); @@ -1645,7 +1724,7 @@ pub mod migrations { fn on_runtime_upgrade() -> Weight { if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC { log::warn!("Skipping session keys upgrade: already applied"); - return ::DbWeight::get().reads(1) + return ::DbWeight::get().reads(1); } log::info!("Upgrading session keys"); Session::upgrade_keys::(transform_session_keys); @@ -1658,7 +1737,7 @@ pub mod migrations { ) -> Result<(), sp_runtime::TryRuntimeError> { if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC { log::warn!(target: "runtime::session_keys", "Skipping session keys migration post-upgrade check due to spec version (already applied?)"); - return Ok(()) + return Ok(()); } let key_ids = SessionKeys::key_ids(); @@ -1703,6 +1782,7 @@ pub mod migrations { // Migrate Identity pallet for Usernames pallet_identity::migration::versioned::V0ToV1, parachains_configuration::migration::v11::MigrateToV11, + parachains_configuration::migration::v12::MigrateToV12, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, // Migrate from legacy lease to coretime. Needs to run after configuration v11 @@ -1711,6 +1791,7 @@ pub mod migrations { crate::xcm_config::XcmRouter, GetLegacyLeaseImpl, >, + parachains_inclusion::migration::MigrateToV1, ); } @@ -1797,7 +1878,7 @@ sp_api::impl_runtime_apis! { Executive::execute_block(block); } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -2018,7 +2099,7 @@ sp_api::impl_runtime_apis! { impl beefy_primitives::BeefyApi for Runtime { fn beefy_genesis() -> Option { - Beefy::genesis_block() + pallet_beefy::GenesisBlock::::get() } fn validator_set() -> Option> { @@ -2055,11 +2136,11 @@ sp_api::impl_runtime_apis! { impl mmr::MmrApi for Runtime { fn mmr_root() -> Result { - Ok(Mmr::mmr_root()) + Ok(pallet_mmr::RootHash::::get()) } fn mmr_leaf_count() -> Result { - Ok(Mmr::mmr_leaves()) + Ok(pallet_mmr::NumberOfLeaves::::get()) } fn generate_proof( @@ -2253,6 +2334,37 @@ sp_api::impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + if !matches!(xcm_version, 3 | 4) { + return Err(XcmPaymentApiError::UnhandledXcmVersion); + } + Ok([VersionedAssetId::V4(xcm_config::TokenLocation::get().into())] + .into_iter() + .filter_map(|asset| asset.into_version(xcm_version).ok()) + .collect()) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + let local_asset = VersionedAssetId::V4(xcm_config::TokenLocation::get().into()); + let asset = asset + .into_version(4) + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; + + if asset != local_asset { return Err(XcmPaymentApiError::AssetNotFound); } + + Ok(WeightToFee::weight_to_fee(&weight)) + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + XcmPallet::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + XcmPallet::query_delivery_fees(destination, message) + } + } + impl pallet_nomination_pools_runtime_api::NominationPoolsApi< Block, AccountId, @@ -2348,7 +2460,36 @@ sp_api::impl_runtime_apis! { impl pallet_session_benchmarking::Config for Runtime {} impl pallet_offences_benchmarking::Config for Runtime {} impl pallet_election_provider_support_benchmarking::Config for Runtime {} + + use xcm_config::{AssetHub, TokenLocation}; + + parameter_types! { + pub ExistentialDepositAsset: Option = Some(( + TokenLocation::get(), + ExistentialDeposit::get() + ).into()); + pub AssetHubParaId: ParaId = westend_runtime_constants::system_parachain::ASSET_HUB_ID.into(); + pub const RandomParaId: ParaId = ParaId::new(43211234); + } + impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = ( + runtime_common::xcm_sender::ToParachainDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForChildParachainDelivery, + AssetHubParaId, + (), + >, + runtime_common::xcm_sender::ToParachainDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForChildParachainDelivery, + RandomParaId, + (), + > + ); + fn reachable_dest() -> Option { Some(crate::xcm_config::AssetHub::get()) } @@ -2356,7 +2497,7 @@ sp_api::impl_runtime_apis! { fn teleportable_asset_and_dest() -> Option<(Asset, Location)> { // Relay/native token can be teleported to/from AH. Some(( - Asset { fun: Fungible(EXISTENTIAL_DEPOSIT), id: AssetId(Here.into()) }, + Asset { fun: Fungible(ExistentialDeposit::get()), id: AssetId(Here.into()) }, crate::xcm_config::AssetHub::get(), )) } @@ -2365,10 +2506,10 @@ sp_api::impl_runtime_apis! { // Relay can reserve transfer native token to some random parachain. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Here.into()) }, - crate::Junction::Parachain(43211234).into(), + crate::Junction::Parachain(RandomParaId::get().into()).into(), )) } @@ -2386,6 +2527,13 @@ sp_api::impl_runtime_apis! { dest ) } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::here()), + fun: Fungible(ExistentialDeposit::get()), + } + } } impl frame_system_benchmarking::Config for Runtime {} impl pallet_nomination_pools_benchmarking::Config for Runtime {} @@ -2395,15 +2543,6 @@ sp_api::impl_runtime_apis! { AssetId, Fungibility::*, InteriorLocation, Junction, Junctions::*, Asset, Assets, Location, NetworkId, Response, }; - use xcm_config::{AssetHub, TokenLocation}; - - parameter_types! { - pub ExistentialDepositAsset: Option = Some(( - TokenLocation::get(), - ExistentialDeposit::get() - ).into()); - pub ToParachain: ParaId = westend_runtime_constants::system_parachain::ASSET_HUB_ID.into(); - } impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; @@ -2412,7 +2551,7 @@ sp_api::impl_runtime_apis! { xcm_config::XcmConfig, ExistentialDepositAsset, xcm_config::PriceForChildParachainDelivery, - ToParachain, + AssetHubParaId, (), >; fn valid_destination() -> Result { @@ -2544,7 +2683,7 @@ mod remote_tests { #[tokio::test] async fn run_migrations() { if var("RUN_MIGRATION_TESTS").is_err() { - return + return; } sp_tracing::try_init_simple(); diff --git a/polkadot/runtime/westend/src/tests.rs b/polkadot/runtime/westend/src/tests.rs index 9f99631605903c805ac42089453e0fb8e5aa0485..bdd599d2b752461186c70c21222e76349af2ee8f 100644 --- a/polkadot/runtime/westend/src/tests.rs +++ b/polkadot/runtime/westend/src/tests.rs @@ -21,7 +21,6 @@ use std::collections::HashSet; use crate::*; use frame_support::traits::WhitelistedStorageKeys; use sp_core::hexdisplay::HexDisplay; -use xcm::latest::prelude::*; #[test] fn remove_keys_weight_is_sensible() { diff --git a/polkadot/runtime/westend/src/weights/pallet_xcm.rs b/polkadot/runtime/westend/src/weights/pallet_xcm.rs index 493acd0f9e7bdfbfd1e716b7e82a474643f1050b..80bc551ba1e264f9703f5fd307ec7075642bc3bd 100644 --- a/polkadot/runtime/westend/src/weights/pallet_xcm.rs +++ b/polkadot/runtime/westend/src/weights/pallet_xcm.rs @@ -16,26 +16,24 @@ //! Autogenerated weights for `pallet_xcm` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-yprdrvc7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot +// target/production/polkadot // benchmark // pallet -// --chain=westend-dev // --steps=50 // --repeat=20 -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --pallet=pallet_xcm // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm +// --chain=westend-dev // --header=./polkadot/file_header.txt // --output=./polkadot/runtime/westend/src/weights/ @@ -50,10 +48,6 @@ use core::marker::PhantomData; /// Weight functions for `pallet_xcm`. pub struct WeightInfo(PhantomData); impl pallet_xcm::WeightInfo for WeightInfo { - fn transfer_assets() -> Weight { - // TODO: run benchmarks - Weight::zero() - } /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) @@ -64,33 +58,105 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn send() -> Weight { // Proof Size summary in bytes: - // Measured: `109` - // Estimated: `3574` - // Minimum execution time: 28_098_000 picoseconds. - Weight::from_parts(28_887_000, 0) - .saturating_add(Weight::from_parts(0, 3574)) + // Measured: `147` + // Estimated: `3612` + // Minimum execution time: 24_535_000 picoseconds. + Weight::from_parts(25_618_000, 0) + .saturating_add(Weight::from_parts(0, 3612)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn send_blob() -> Weight { + // Proof Size summary in bytes: + // Measured: `147` + // Estimated: `3612` + // Minimum execution time: 25_376_000 picoseconds. + Weight::from_parts(26_180_000, 0) + .saturating_add(Weight::from_parts(0, 3612)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn teleport_assets() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 17_609_000 picoseconds. - Weight::from_parts(18_000_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `250` + // Estimated: `6196` + // Minimum execution time: 108_786_000 picoseconds. + Weight::from_parts(112_208_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) } + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn reserve_transfer_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `302` + // Estimated: `6196` + // Minimum execution time: 105_190_000 picoseconds. + Weight::from_parts(107_140_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn transfer_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `250` + // Estimated: `6196` + // Minimum execution time: 109_027_000 picoseconds. + Weight::from_parts(111_404_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn execute() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 17_007_000 picoseconds. - Weight::from_parts(17_471_000, 0) + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `Benchmark::Override` (r:0 w:0) /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn execute() -> Weight { + fn execute_blob() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` @@ -104,8 +170,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_444_000 picoseconds. - Weight::from_parts(7_671_000, 0) + // Minimum execution time: 6_668_000 picoseconds. + Weight::from_parts(7_013_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -113,8 +179,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_126_000 picoseconds. - Weight::from_parts(2_253_000, 0) + // Minimum execution time: 1_740_000 picoseconds. + Weight::from_parts(1_884_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `XcmPallet::VersionNotifiers` (r:1 w:1) @@ -133,11 +199,11 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `XcmPallet::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_subscribe_version_notify() -> Weight { // Proof Size summary in bytes: - // Measured: `109` - // Estimated: `3574` - // Minimum execution time: 31_318_000 picoseconds. - Weight::from_parts(32_413_000, 0) - .saturating_add(Weight::from_parts(0, 3574)) + // Measured: `147` + // Estimated: `3612` + // Minimum execution time: 30_200_000 picoseconds. + Weight::from_parts(30_768_000, 0) + .saturating_add(Weight::from_parts(0, 3612)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -155,11 +221,11 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `XcmPallet::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_unsubscribe_version_notify() -> Weight { // Proof Size summary in bytes: - // Measured: `289` - // Estimated: `3754` - // Minimum execution time: 35_282_000 picoseconds. - Weight::from_parts(35_969_000, 0) - .saturating_add(Weight::from_parts(0, 3754)) + // Measured: `327` + // Estimated: `3792` + // Minimum execution time: 33_928_000 picoseconds. + Weight::from_parts(35_551_000, 0) + .saturating_add(Weight::from_parts(0, 3792)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -169,45 +235,45 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_247_000 picoseconds. - Weight::from_parts(2_381_000, 0) + // Minimum execution time: 1_759_000 picoseconds. + Weight::from_parts(1_880_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `XcmPallet::SupportedVersion` (r:4 w:2) + /// Storage: `XcmPallet::SupportedVersion` (r:5 w:2) /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `26` - // Estimated: `10916` - // Minimum execution time: 14_512_000 picoseconds. - Weight::from_parts(15_042_000, 0) - .saturating_add(Weight::from_parts(0, 10916)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `22` + // Estimated: `13387` + // Minimum execution time: 16_507_000 picoseconds. + Weight::from_parts(17_219_000, 0) + .saturating_add(Weight::from_parts(0, 13387)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `XcmPallet::VersionNotifiers` (r:4 w:2) + /// Storage: `XcmPallet::VersionNotifiers` (r:5 w:2) /// Proof: `XcmPallet::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `30` - // Estimated: `10920` - // Minimum execution time: 14_659_000 picoseconds. - Weight::from_parts(15_164_000, 0) - .saturating_add(Weight::from_parts(0, 10920)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `26` + // Estimated: `13391` + // Minimum execution time: 16_633_000 picoseconds. + Weight::from_parts(16_889_000, 0) + .saturating_add(Weight::from_parts(0, 13391)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `XcmPallet::VersionNotifyTargets` (r:5 w:0) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:6 w:0) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `40` - // Estimated: `13405` - // Minimum execution time: 16_261_000 picoseconds. - Weight::from_parts(16_986_000, 0) - .saturating_add(Weight::from_parts(0, 13405)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `15880` + // Minimum execution time: 19_297_000 picoseconds. + Weight::from_parts(19_820_000, 0) + .saturating_add(Weight::from_parts(0, 15880)) + .saturating_add(T::DbWeight::get().reads(6)) } /// Storage: `XcmPallet::VersionNotifyTargets` (r:2 w:1) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -221,38 +287,38 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_current_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `145` - // Estimated: `6085` - // Minimum execution time: 30_539_000 picoseconds. - Weight::from_parts(31_117_000, 0) - .saturating_add(Weight::from_parts(0, 6085)) + // Measured: `183` + // Estimated: `6123` + // Minimum execution time: 30_364_000 picoseconds. + Weight::from_parts(31_122_000, 0) + .saturating_add(Weight::from_parts(0, 6123)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: `XcmPallet::VersionNotifyTargets` (r:3 w:0) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:4 w:0) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `69` - // Estimated: `8484` - // Minimum execution time: 9_463_000 picoseconds. - Weight::from_parts(9_728_000, 0) - .saturating_add(Weight::from_parts(0, 8484)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `10959` + // Minimum execution time: 11_997_000 picoseconds. + Weight::from_parts(12_392_000, 0) + .saturating_add(Weight::from_parts(0, 10959)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: `XcmPallet::VersionNotifyTargets` (r:4 w:2) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:5 w:2) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `37` - // Estimated: `10927` - // Minimum execution time: 15_169_000 picoseconds. - Weight::from_parts(15_694_000, 0) - .saturating_add(Weight::from_parts(0, 10927)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `33` + // Estimated: `13398` + // Minimum execution time: 16_894_000 picoseconds. + Weight::from_parts(17_452_000, 0) + .saturating_add(Weight::from_parts(0, 13398)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `XcmPallet::VersionNotifyTargets` (r:4 w:2) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:5 w:2) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -264,12 +330,12 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `149` - // Estimated: `11039` - // Minimum execution time: 37_549_000 picoseconds. - Weight::from_parts(38_203_000, 0) - .saturating_add(Weight::from_parts(0, 11039)) - .saturating_add(T::DbWeight::get().reads(8)) + // Measured: `183` + // Estimated: `13548` + // Minimum execution time: 39_864_000 picoseconds. + Weight::from_parts(40_859_000, 0) + .saturating_add(Weight::from_parts(0, 13548)) + .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `XcmPallet::QueryCounter` (r:1 w:1) @@ -280,8 +346,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1485` - // Minimum execution time: 2_947_000 picoseconds. - Weight::from_parts(3_117_000, 0) + // Minimum execution time: 2_363_000 picoseconds. + Weight::from_parts(2_519_000, 0) .saturating_add(Weight::from_parts(0, 1485)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -292,10 +358,22 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7576` // Estimated: `11041` - // Minimum execution time: 24_595_000 picoseconds. - Weight::from_parts(24_907_000, 0) + // Minimum execution time: 22_409_000 picoseconds. + Weight::from_parts(22_776_000, 0) .saturating_add(Weight::from_parts(0, 11041)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `XcmPallet::AssetTraps` (r:1 w:1) + /// Proof: `XcmPallet::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `23` + // Estimated: `3488` + // Minimum execution time: 33_551_000 picoseconds. + Weight::from_parts(34_127_000, 0) + .saturating_add(Weight::from_parts(0, 3488)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_assigner_on_demand.rs b/polkadot/runtime/westend/src/weights/runtime_parachains_assigner_on_demand.rs index ac0f05301b486dbdbb8c0ca004e195ab47171ff3..acd1834f79ed8c35ed83bd56d75c0907b02f7f5d 100644 --- a/polkadot/runtime/westend/src/weights/runtime_parachains_assigner_on_demand.rs +++ b/polkadot/runtime/westend/src/weights/runtime_parachains_assigner_on_demand.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_parachains::assigner_on_demand` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-08-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-18, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-fljshgub-project-163-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: // target/production/polkadot @@ -31,11 +31,11 @@ // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot/.git/.artifacts/bench.json +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json // --pallet=runtime_parachains::assigner_on_demand -// --chain=rococo-dev -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --chain=westend-dev +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -48,44 +48,44 @@ use core::marker::PhantomData; /// Weight functions for `runtime_parachains::assigner_on_demand`. pub struct WeightInfo(PhantomData); impl runtime_parachains::assigner_on_demand::WeightInfo for WeightInfo { - /// Storage: `OnDemandAssignmentProvider::SpotTraffic` (r:1 w:0) - /// Proof: `OnDemandAssignmentProvider::SpotTraffic` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Paras::ParaLifecycles` (r:1 w:0) - /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `OnDemandAssignmentProvider::OnDemandQueue` (r:1 w:1) - /// Proof: `OnDemandAssignmentProvider::OnDemandQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `OnDemandAssignmentProvider::QueueStatus` (r:1 w:1) + /// Proof: `OnDemandAssignmentProvider::QueueStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `OnDemandAssignmentProvider::ParaIdAffinity` (r:1 w:0) + /// Proof: `OnDemandAssignmentProvider::ParaIdAffinity` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `OnDemandAssignmentProvider::FreeEntries` (r:1 w:1) + /// Proof: `OnDemandAssignmentProvider::FreeEntries` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `s` is `[1, 9999]`. fn place_order_keep_alive(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `297 + s * (4 ±0)` - // Estimated: `3762 + s * (4 ±0)` - // Minimum execution time: 33_522_000 picoseconds. - Weight::from_parts(35_436_835, 0) - .saturating_add(Weight::from_parts(0, 3762)) - // Standard Error: 129 - .saturating_add(Weight::from_parts(14_041, 0).saturating_mul(s.into())) + // Measured: `218 + s * (8 ±0)` + // Estimated: `3681 + s * (8 ±0)` + // Minimum execution time: 21_396_000 picoseconds. + Weight::from_parts(20_585_695, 0) + .saturating_add(Weight::from_parts(0, 3681)) + // Standard Error: 127 + .saturating_add(Weight::from_parts(20_951, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) - .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(Weight::from_parts(0, 8).saturating_mul(s.into())) } - /// Storage: `OnDemandAssignmentProvider::SpotTraffic` (r:1 w:0) - /// Proof: `OnDemandAssignmentProvider::SpotTraffic` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Paras::ParaLifecycles` (r:1 w:0) - /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `OnDemandAssignmentProvider::OnDemandQueue` (r:1 w:1) - /// Proof: `OnDemandAssignmentProvider::OnDemandQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `OnDemandAssignmentProvider::QueueStatus` (r:1 w:1) + /// Proof: `OnDemandAssignmentProvider::QueueStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `OnDemandAssignmentProvider::ParaIdAffinity` (r:1 w:0) + /// Proof: `OnDemandAssignmentProvider::ParaIdAffinity` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `OnDemandAssignmentProvider::FreeEntries` (r:1 w:1) + /// Proof: `OnDemandAssignmentProvider::FreeEntries` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `s` is `[1, 9999]`. fn place_order_allow_death(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `297 + s * (4 ±0)` - // Estimated: `3762 + s * (4 ±0)` - // Minimum execution time: 33_488_000 picoseconds. - Weight::from_parts(34_848_934, 0) - .saturating_add(Weight::from_parts(0, 3762)) - // Standard Error: 143 - .saturating_add(Weight::from_parts(14_215, 0).saturating_mul(s.into())) + // Measured: `218 + s * (8 ±0)` + // Estimated: `3681 + s * (8 ±0)` + // Minimum execution time: 21_412_000 picoseconds. + Weight::from_parts(19_731_554, 0) + .saturating_add(Weight::from_parts(0, 3681)) + // Standard Error: 128 + .saturating_add(Weight::from_parts(21_055, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) - .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(Weight::from_parts(0, 8).saturating_mul(s.into())) } } diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_configuration.rs b/polkadot/runtime/westend/src/weights/runtime_parachains_configuration.rs index 3a4813b667c68ea6400c9ad58cea8fd1bfece5d0..8fa3207c6446082a97877f488913d9c063114fca 100644 --- a/polkadot/runtime/westend/src/weights/runtime_parachains_configuration.rs +++ b/polkadot/runtime/westend/src/weights/runtime_parachains_configuration.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `runtime_parachains::configuration` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-yprdrvc7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -58,8 +58,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 8_065_000 picoseconds. - Weight::from_parts(8_389_000, 0) + // Minimum execution time: 7_775_000 picoseconds. + Weight::from_parts(8_036_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -74,8 +74,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 8_038_000 picoseconds. - Weight::from_parts(8_463_000, 0) + // Minimum execution time: 7_708_000 picoseconds. + Weight::from_parts(7_971_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -90,8 +90,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_843_000 picoseconds. - Weight::from_parts(8_216_000, 0) + // Minimum execution time: 7_746_000 picoseconds. + Weight::from_parts(8_028_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -116,8 +116,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_969_000 picoseconds. - Weight::from_parts(8_362_000, 0) + // Minimum execution time: 7_729_000 picoseconds. + Weight::from_parts(7_954_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -132,8 +132,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 10_084_000 picoseconds. - Weight::from_parts(10_451_000, 0) + // Minimum execution time: 9_871_000 picoseconds. + Weight::from_parts(10_075_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -148,8 +148,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_948_000 picoseconds. - Weight::from_parts(8_268_000, 0) + // Minimum execution time: 7_869_000 picoseconds. + Weight::from_parts(8_000_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -164,8 +164,24 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 10_257_000 picoseconds. - Weight::from_parts(10_584_000, 0) + // Minimum execution time: 9_797_000 picoseconds. + Weight::from_parts(10_373_000, 0) + .saturating_add(Weight::from_parts(0, 1636)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Configuration::PendingConfigs` (r:1 w:1) + /// Proof: `Configuration::PendingConfigs` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Configuration::BypassConsistencyCheck` (r:1 w:0) + /// Proof: `Configuration::BypassConsistencyCheck` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_config_with_scheduler_params() -> Weight { + // Proof Size summary in bytes: + // Measured: `151` + // Estimated: `1636` + // Minimum execution time: 7_718_000 picoseconds. + Weight::from_parts(7_984_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_paras_inherent.rs b/polkadot/runtime/westend/src/weights/runtime_parachains_paras_inherent.rs index 0dd64f054d009ac1aa01e251b01652ea2dd909c4..aa99ac9438c404aad48715370badd821207f669c 100644 --- a/polkadot/runtime/westend/src/weights/runtime_parachains_paras_inherent.rs +++ b/polkadot/runtime/westend/src/weights/runtime_parachains_paras_inherent.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_parachains::paras_inherent` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-18, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-xerhrdyb-project-163-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: // target/production/polkadot @@ -29,14 +29,13 @@ // --steps=50 // --repeat=20 // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot/.git/.artifacts/bench.json +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json // --pallet=runtime_parachains::paras_inherent // --chain=westend-dev -// --header=./file_header.txt -// --output=./runtime/westend/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -49,297 +48,311 @@ use core::marker::PhantomData; /// Weight functions for `runtime_parachains::paras_inherent`. pub struct WeightInfo(PhantomData); impl runtime_parachains::paras_inherent::WeightInfo for WeightInfo { - /// Storage: ParaInherent Included (r:1 w:1) - /// Proof Skipped: ParaInherent Included (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: System ParentHash (r:1 w:0) - /// Proof: System ParentHash (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaScheduler AvailabilityCores (r:1 w:1) - /// Proof Skipped: ParaScheduler AvailabilityCores (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Babe AuthorVrfRandomness (r:1 w:0) - /// Proof: Babe AuthorVrfRandomness (max_values: Some(1), max_size: Some(33), added: 528, mode: MaxEncodedLen) - /// Storage: ParaSessionInfo Sessions (r:1 w:0) - /// Proof Skipped: ParaSessionInfo Sessions (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasDisputes Disputes (r:1 w:1) - /// Proof Skipped: ParasDisputes Disputes (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasDisputes BackersOnDisputes (r:1 w:1) - /// Proof Skipped: ParasDisputes BackersOnDisputes (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasDisputes Included (r:1 w:1) - /// Proof Skipped: ParasDisputes Included (max_values: None, max_size: None, mode: Measured) - /// Storage: ParaSessionInfo AccountKeys (r:1 w:0) - /// Proof Skipped: ParaSessionInfo AccountKeys (max_values: None, max_size: None, mode: Measured) - /// Storage: Session Validators (r:1 w:0) - /// Proof Skipped: Session Validators (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Staking ActiveEra (r:1 w:0) - /// Proof: Staking ActiveEra (max_values: Some(1), max_size: Some(13), added: 508, mode: MaxEncodedLen) - /// Storage: Staking ErasRewardPoints (r:1 w:1) - /// Proof Skipped: Staking ErasRewardPoints (max_values: None, max_size: None, mode: Measured) - /// Storage: ParaInherent OnChainVotes (r:1 w:1) - /// Proof Skipped: ParaInherent OnChainVotes (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasDisputes Frozen (r:1 w:0) - /// Proof Skipped: ParasDisputes Frozen (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaInclusion PendingAvailability (r:2 w:1) - /// Proof Skipped: ParaInclusion PendingAvailability (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras Parachains (r:1 w:0) - /// Proof Skipped: Paras Parachains (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaInclusion PendingAvailabilityCommitments (r:1 w:1) - /// Proof Skipped: ParaInclusion PendingAvailabilityCommitments (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DeliveryFeeFactor (r:1 w:1) - /// Proof Skipped: Dmp DeliveryFeeFactor (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpChannelDigests (r:1 w:1) - /// Proof Skipped: Hrmp HrmpChannelDigests (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras FutureCodeUpgrades (r:1 w:0) - /// Proof Skipped: Paras FutureCodeUpgrades (max_values: None, max_size: None, mode: Measured) - /// Storage: ParaScheduler SessionStartBlock (r:1 w:0) - /// Proof Skipped: ParaScheduler SessionStartBlock (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaScheduler ParathreadQueue (r:1 w:1) - /// Proof Skipped: ParaScheduler ParathreadQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaScheduler Scheduled (r:1 w:1) - /// Proof Skipped: ParaScheduler Scheduled (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaScheduler ValidatorGroups (r:1 w:0) - /// Proof Skipped: ParaScheduler ValidatorGroups (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Hrmp HrmpWatermarks (r:0 w:1) - /// Proof Skipped: Hrmp HrmpWatermarks (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras Heads (r:0 w:1) - /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpgradeGoAheadSignal (r:0 w:1) - /// Proof Skipped: Paras UpgradeGoAheadSignal (max_values: None, max_size: None, mode: Measured) + /// Storage: `ParaInherent::Included` (r:1 w:1) + /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::ParentHash` (r:1 w:0) + /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) + /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) + /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) + /// Proof: `Babe::AuthorVrfRandomness` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) + /// Storage: `ParaSessionInfo::Sessions` (r:1 w:0) + /// Proof: `ParaSessionInfo::Sessions` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Disputes` (r:1 w:1) + /// Proof: `ParasDisputes::Disputes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::BackersOnDisputes` (r:1 w:1) + /// Proof: `ParasDisputes::BackersOnDisputes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Included` (r:1 w:1) + /// Proof: `ParasDisputes::Included` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaSessionInfo::AccountKeys` (r:1 w:0) + /// Proof: `ParaSessionInfo::AccountKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Session::Validators` (r:1 w:0) + /// Proof: `Session::Validators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Staking::ActiveEra` (r:1 w:0) + /// Proof: `Staking::ActiveEra` (`max_values`: Some(1), `max_size`: Some(13), added: 508, mode: `MaxEncodedLen`) + /// Storage: `Staking::ErasRewardPoints` (r:1 w:1) + /// Proof: `Staking::ErasRewardPoints` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaInherent::OnChainVotes` (r:1 w:1) + /// Proof: `ParaInherent::OnChainVotes` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Frozen` (r:1 w:0) + /// Proof: `ParasDisputes::Frozen` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaInclusion::V1` (r:2 w:1) + /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:1) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannelDigests` (r:1 w:1) + /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) + /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) + /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorIndices` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::DisabledValidators` (r:1 w:0) + /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) + /// Proof: `Hrmp::HrmpWatermarks` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::Heads` (r:0 w:1) + /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeGoAheadSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::MostRecentContext` (r:0 w:1) + /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `v` is `[10, 200]`. fn enter_variable_disputes(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `50518` - // Estimated: `56458 + v * (23 ±0)` - // Minimum execution time: 998_338_000 picoseconds. - Weight::from_parts(468_412_001, 0) - .saturating_add(Weight::from_parts(0, 56458)) - // Standard Error: 20_559 - .saturating_add(Weight::from_parts(56_965_025, 0).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(27)) - .saturating_add(T::DbWeight::get().writes(15)) + // Measured: `67518` + // Estimated: `73458 + v * (23 ±0)` + // Minimum execution time: 844_022_000 picoseconds. + Weight::from_parts(456_682_337, 0) + .saturating_add(Weight::from_parts(0, 73458)) + // Standard Error: 16_403 + .saturating_add(Weight::from_parts(41_871_245, 0).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(28)) + .saturating_add(T::DbWeight::get().writes(16)) .saturating_add(Weight::from_parts(0, 23).saturating_mul(v.into())) } - /// Storage: ParaInherent Included (r:1 w:1) - /// Proof Skipped: ParaInherent Included (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: System ParentHash (r:1 w:0) - /// Proof: System ParentHash (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaScheduler AvailabilityCores (r:1 w:1) - /// Proof Skipped: ParaScheduler AvailabilityCores (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Babe AuthorVrfRandomness (r:1 w:0) - /// Proof: Babe AuthorVrfRandomness (max_values: Some(1), max_size: Some(33), added: 528, mode: MaxEncodedLen) - /// Storage: ParaInherent OnChainVotes (r:1 w:1) - /// Proof Skipped: ParaInherent OnChainVotes (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasDisputes Frozen (r:1 w:0) - /// Proof Skipped: ParasDisputes Frozen (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaInclusion PendingAvailability (r:2 w:1) - /// Proof Skipped: ParaInclusion PendingAvailability (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras Parachains (r:1 w:0) - /// Proof Skipped: Paras Parachains (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaInclusion PendingAvailabilityCommitments (r:1 w:1) - /// Proof Skipped: ParaInclusion PendingAvailabilityCommitments (max_values: None, max_size: None, mode: Measured) - /// Storage: ParaSessionInfo AccountKeys (r:1 w:0) - /// Proof Skipped: ParaSessionInfo AccountKeys (max_values: None, max_size: None, mode: Measured) - /// Storage: Session Validators (r:1 w:0) - /// Proof Skipped: Session Validators (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Staking ActiveEra (r:1 w:0) - /// Proof: Staking ActiveEra (max_values: Some(1), max_size: Some(13), added: 508, mode: MaxEncodedLen) - /// Storage: Staking ErasRewardPoints (r:1 w:1) - /// Proof Skipped: Staking ErasRewardPoints (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DeliveryFeeFactor (r:1 w:1) - /// Proof Skipped: Dmp DeliveryFeeFactor (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpChannelDigests (r:1 w:1) - /// Proof Skipped: Hrmp HrmpChannelDigests (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras FutureCodeUpgrades (r:1 w:0) - /// Proof Skipped: Paras FutureCodeUpgrades (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasDisputes Disputes (r:1 w:0) - /// Proof Skipped: ParasDisputes Disputes (max_values: None, max_size: None, mode: Measured) - /// Storage: ParaScheduler SessionStartBlock (r:1 w:0) - /// Proof Skipped: ParaScheduler SessionStartBlock (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaScheduler ParathreadQueue (r:1 w:1) - /// Proof Skipped: ParaScheduler ParathreadQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaScheduler Scheduled (r:1 w:1) - /// Proof Skipped: ParaScheduler Scheduled (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaScheduler ValidatorGroups (r:1 w:0) - /// Proof Skipped: ParaScheduler ValidatorGroups (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaInclusion AvailabilityBitfields (r:0 w:1) - /// Proof Skipped: ParaInclusion AvailabilityBitfields (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasDisputes Included (r:0 w:1) - /// Proof Skipped: ParasDisputes Included (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpWatermarks (r:0 w:1) - /// Proof Skipped: Hrmp HrmpWatermarks (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras Heads (r:0 w:1) - /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpgradeGoAheadSignal (r:0 w:1) - /// Proof Skipped: Paras UpgradeGoAheadSignal (max_values: None, max_size: None, mode: Measured) + /// Storage: `ParaInherent::Included` (r:1 w:1) + /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::ParentHash` (r:1 w:0) + /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) + /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) + /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) + /// Proof: `Babe::AuthorVrfRandomness` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) + /// Storage: `ParaInherent::OnChainVotes` (r:1 w:1) + /// Proof: `ParaInherent::OnChainVotes` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Frozen` (r:1 w:0) + /// Proof: `ParasDisputes::Frozen` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaInclusion::V1` (r:2 w:1) + /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaSessionInfo::AccountKeys` (r:1 w:0) + /// Proof: `ParaSessionInfo::AccountKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Session::Validators` (r:1 w:0) + /// Proof: `Session::Validators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Staking::ActiveEra` (r:1 w:0) + /// Proof: `Staking::ActiveEra` (`max_values`: Some(1), `max_size`: Some(13), added: 508, mode: `MaxEncodedLen`) + /// Storage: `Staking::ErasRewardPoints` (r:1 w:1) + /// Proof: `Staking::ErasRewardPoints` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:1) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannelDigests` (r:1 w:1) + /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) + /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Disputes` (r:1 w:0) + /// Proof: `ParasDisputes::Disputes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) + /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorIndices` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::DisabledValidators` (r:1 w:0) + /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Included` (r:0 w:1) + /// Proof: `ParasDisputes::Included` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) + /// Proof: `Hrmp::HrmpWatermarks` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::Heads` (r:0 w:1) + /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeGoAheadSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::MostRecentContext` (r:0 w:1) + /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) fn enter_bitfields() -> Weight { // Proof Size summary in bytes: - // Measured: `42352` - // Estimated: `48292` - // Minimum execution time: 457_404_000 picoseconds. - Weight::from_parts(485_416_000, 0) - .saturating_add(Weight::from_parts(0, 48292)) - .saturating_add(T::DbWeight::get().reads(25)) + // Measured: `43196` + // Estimated: `49136` + // Minimum execution time: 438_637_000 picoseconds. + Weight::from_parts(458_342_000, 0) + .saturating_add(Weight::from_parts(0, 49136)) + .saturating_add(T::DbWeight::get().reads(26)) .saturating_add(T::DbWeight::get().writes(16)) } - /// Storage: ParaInherent Included (r:1 w:1) - /// Proof Skipped: ParaInherent Included (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: System ParentHash (r:1 w:0) - /// Proof: System ParentHash (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaScheduler AvailabilityCores (r:1 w:1) - /// Proof Skipped: ParaScheduler AvailabilityCores (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Babe AuthorVrfRandomness (r:1 w:0) - /// Proof: Babe AuthorVrfRandomness (max_values: Some(1), max_size: Some(33), added: 528, mode: MaxEncodedLen) - /// Storage: ParaInherent OnChainVotes (r:1 w:1) - /// Proof Skipped: ParaInherent OnChainVotes (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasDisputes Frozen (r:1 w:0) - /// Proof Skipped: ParasDisputes Frozen (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaInclusion PendingAvailability (r:2 w:1) - /// Proof Skipped: ParaInclusion PendingAvailability (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras Parachains (r:1 w:0) - /// Proof Skipped: Paras Parachains (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaInclusion PendingAvailabilityCommitments (r:1 w:1) - /// Proof Skipped: ParaInclusion PendingAvailabilityCommitments (max_values: None, max_size: None, mode: Measured) - /// Storage: ParaSessionInfo AccountKeys (r:1 w:0) - /// Proof Skipped: ParaSessionInfo AccountKeys (max_values: None, max_size: None, mode: Measured) - /// Storage: Session Validators (r:1 w:0) - /// Proof Skipped: Session Validators (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Staking ActiveEra (r:1 w:0) - /// Proof: Staking ActiveEra (max_values: Some(1), max_size: Some(13), added: 508, mode: MaxEncodedLen) - /// Storage: Staking ErasRewardPoints (r:1 w:1) - /// Proof Skipped: Staking ErasRewardPoints (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DeliveryFeeFactor (r:1 w:1) - /// Proof Skipped: Dmp DeliveryFeeFactor (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpChannelDigests (r:1 w:1) - /// Proof Skipped: Hrmp HrmpChannelDigests (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras FutureCodeUpgrades (r:1 w:0) - /// Proof Skipped: Paras FutureCodeUpgrades (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasDisputes Disputes (r:1 w:0) - /// Proof Skipped: ParasDisputes Disputes (max_values: None, max_size: None, mode: Measured) - /// Storage: ParaScheduler SessionStartBlock (r:1 w:0) - /// Proof Skipped: ParaScheduler SessionStartBlock (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaScheduler ParathreadQueue (r:1 w:1) - /// Proof Skipped: ParaScheduler ParathreadQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaScheduler Scheduled (r:1 w:1) - /// Proof Skipped: ParaScheduler Scheduled (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaScheduler ValidatorGroups (r:1 w:0) - /// Proof Skipped: ParaScheduler ValidatorGroups (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras CurrentCodeHash (r:1 w:0) - /// Proof Skipped: Paras CurrentCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:1 w:0) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: MessageQueue BookStateFor (r:1 w:0) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) - /// Storage: ParasDisputes Included (r:0 w:1) - /// Proof Skipped: ParasDisputes Included (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpWatermarks (r:0 w:1) - /// Proof Skipped: Hrmp HrmpWatermarks (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras Heads (r:0 w:1) - /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpgradeGoAheadSignal (r:0 w:1) - /// Proof Skipped: Paras UpgradeGoAheadSignal (max_values: None, max_size: None, mode: Measured) + /// Storage: `ParaInherent::Included` (r:1 w:1) + /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::ParentHash` (r:1 w:0) + /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) + /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) + /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) + /// Proof: `Babe::AuthorVrfRandomness` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) + /// Storage: `ParaInherent::OnChainVotes` (r:1 w:1) + /// Proof: `ParaInherent::OnChainVotes` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Frozen` (r:1 w:0) + /// Proof: `ParasDisputes::Frozen` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaInclusion::V1` (r:2 w:1) + /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaSessionInfo::AccountKeys` (r:1 w:0) + /// Proof: `ParaSessionInfo::AccountKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Session::Validators` (r:1 w:0) + /// Proof: `Session::Validators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Staking::ActiveEra` (r:1 w:0) + /// Proof: `Staking::ActiveEra` (`max_values`: Some(1), `max_size`: Some(13), added: 508, mode: `MaxEncodedLen`) + /// Storage: `Staking::ErasRewardPoints` (r:1 w:1) + /// Proof: `Staking::ErasRewardPoints` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:1) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannelDigests` (r:1 w:1) + /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) + /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Disputes` (r:1 w:0) + /// Proof: `ParasDisputes::Disputes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) + /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CurrentCodeHash` (r:1 w:0) + /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:0) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:0) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `ParasShared::ActiveValidatorIndices` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::DisabledValidators` (r:1 w:0) + /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Included` (r:0 w:1) + /// Proof: `ParasDisputes::Included` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) + /// Proof: `Hrmp::HrmpWatermarks` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::Heads` (r:0 w:1) + /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeGoAheadSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::MostRecentContext` (r:0 w:1) + /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `v` is `[101, 200]`. fn enter_backed_candidates_variable(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `42387` - // Estimated: `48327` - // Minimum execution time: 6_864_029_000 picoseconds. - Weight::from_parts(1_237_704_892, 0) - .saturating_add(Weight::from_parts(0, 48327)) - // Standard Error: 33_413 - .saturating_add(Weight::from_parts(56_199_819, 0).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(28)) - .saturating_add(T::DbWeight::get().writes(15)) + // Measured: `43269` + // Estimated: `49209` + // Minimum execution time: 5_955_361_000 picoseconds. + Weight::from_parts(1_285_398_956, 0) + .saturating_add(Weight::from_parts(0, 49209)) + // Standard Error: 57_369 + .saturating_add(Weight::from_parts(47_073_853, 0).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(29)) + .saturating_add(T::DbWeight::get().writes(16)) } - /// Storage: ParaInherent Included (r:1 w:1) - /// Proof Skipped: ParaInherent Included (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: System ParentHash (r:1 w:0) - /// Proof: System ParentHash (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaScheduler AvailabilityCores (r:1 w:1) - /// Proof Skipped: ParaScheduler AvailabilityCores (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Babe AuthorVrfRandomness (r:1 w:0) - /// Proof: Babe AuthorVrfRandomness (max_values: Some(1), max_size: Some(33), added: 528, mode: MaxEncodedLen) - /// Storage: ParaInherent OnChainVotes (r:1 w:1) - /// Proof Skipped: ParaInherent OnChainVotes (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasDisputes Frozen (r:1 w:0) - /// Proof Skipped: ParasDisputes Frozen (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaInclusion PendingAvailability (r:2 w:1) - /// Proof Skipped: ParaInclusion PendingAvailability (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras Parachains (r:1 w:0) - /// Proof Skipped: Paras Parachains (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaInclusion PendingAvailabilityCommitments (r:1 w:1) - /// Proof Skipped: ParaInclusion PendingAvailabilityCommitments (max_values: None, max_size: None, mode: Measured) - /// Storage: ParaSessionInfo AccountKeys (r:1 w:0) - /// Proof Skipped: ParaSessionInfo AccountKeys (max_values: None, max_size: None, mode: Measured) - /// Storage: Session Validators (r:1 w:0) - /// Proof Skipped: Session Validators (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Staking ActiveEra (r:1 w:0) - /// Proof: Staking ActiveEra (max_values: Some(1), max_size: Some(13), added: 508, mode: MaxEncodedLen) - /// Storage: Staking ErasRewardPoints (r:1 w:1) - /// Proof Skipped: Staking ErasRewardPoints (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DeliveryFeeFactor (r:1 w:1) - /// Proof Skipped: Dmp DeliveryFeeFactor (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpChannelDigests (r:1 w:1) - /// Proof Skipped: Hrmp HrmpChannelDigests (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras FutureCodeUpgrades (r:1 w:0) - /// Proof Skipped: Paras FutureCodeUpgrades (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasDisputes Disputes (r:1 w:0) - /// Proof Skipped: ParasDisputes Disputes (max_values: None, max_size: None, mode: Measured) - /// Storage: ParaScheduler SessionStartBlock (r:1 w:0) - /// Proof Skipped: ParaScheduler SessionStartBlock (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaScheduler ParathreadQueue (r:1 w:1) - /// Proof Skipped: ParaScheduler ParathreadQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaScheduler Scheduled (r:1 w:1) - /// Proof Skipped: ParaScheduler Scheduled (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParaScheduler ValidatorGroups (r:1 w:0) - /// Proof Skipped: ParaScheduler ValidatorGroups (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras CurrentCodeHash (r:1 w:0) - /// Proof Skipped: Paras CurrentCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras FutureCodeHash (r:1 w:0) - /// Proof Skipped: Paras FutureCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpgradeRestrictionSignal (r:1 w:0) - /// Proof Skipped: Paras UpgradeRestrictionSignal (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:1 w:0) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: MessageQueue BookStateFor (r:1 w:0) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) - /// Storage: ParasDisputes Included (r:0 w:1) - /// Proof Skipped: ParasDisputes Included (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpWatermarks (r:0 w:1) - /// Proof Skipped: Hrmp HrmpWatermarks (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras Heads (r:0 w:1) - /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpgradeGoAheadSignal (r:0 w:1) - /// Proof Skipped: Paras UpgradeGoAheadSignal (max_values: None, max_size: None, mode: Measured) + /// Storage: `ParaInherent::Included` (r:1 w:1) + /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::ParentHash` (r:1 w:0) + /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) + /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) + /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) + /// Proof: `Babe::AuthorVrfRandomness` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) + /// Storage: `ParaInherent::OnChainVotes` (r:1 w:1) + /// Proof: `ParaInherent::OnChainVotes` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Frozen` (r:1 w:0) + /// Proof: `ParasDisputes::Frozen` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaInclusion::V1` (r:2 w:1) + /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaSessionInfo::AccountKeys` (r:1 w:0) + /// Proof: `ParaSessionInfo::AccountKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Session::Validators` (r:1 w:0) + /// Proof: `Session::Validators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Staking::ActiveEra` (r:1 w:0) + /// Proof: `Staking::ActiveEra` (`max_values`: Some(1), `max_size`: Some(13), added: 508, mode: `MaxEncodedLen`) + /// Storage: `Staking::ErasRewardPoints` (r:1 w:1) + /// Proof: `Staking::ErasRewardPoints` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:1) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannelDigests` (r:1 w:1) + /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) + /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Disputes` (r:1 w:0) + /// Proof: `ParasDisputes::Disputes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) + /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CurrentCodeHash` (r:1 w:0) + /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::FutureCodeHash` (r:1 w:0) + /// Proof: `Paras::FutureCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeRestrictionSignal` (r:1 w:0) + /// Proof: `Paras::UpgradeRestrictionSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:0) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:0) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `ParasShared::ActiveValidatorIndices` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::DisabledValidators` (r:1 w:0) + /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Included` (r:0 w:1) + /// Proof: `ParasDisputes::Included` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) + /// Proof: `Hrmp::HrmpWatermarks` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::Heads` (r:0 w:1) + /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeGoAheadSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::MostRecentContext` (r:0 w:1) + /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) fn enter_backed_candidate_code_upgrade() -> Weight { // Proof Size summary in bytes: - // Measured: `42414` - // Estimated: `48354` - // Minimum execution time: 43_320_529_000 picoseconds. - Weight::from_parts(45_622_613_000, 0) - .saturating_add(Weight::from_parts(0, 48354)) - .saturating_add(T::DbWeight::get().reads(30)) - .saturating_add(T::DbWeight::get().writes(15)) + // Measured: `43282` + // Estimated: `49222` + // Minimum execution time: 42_128_606_000 picoseconds. + Weight::from_parts(42_822_806_000, 0) + .saturating_add(Weight::from_parts(0, 49222)) + .saturating_add(T::DbWeight::get().reads(31)) + .saturating_add(T::DbWeight::get().writes(16)) } } diff --git a/polkadot/runtime/westend/src/weights/xcm/mod.rs b/polkadot/runtime/westend/src/weights/xcm/mod.rs index 0162012825ff7e4d47c43868ac94f894b38514c7..09e883a9f7af5820832e7db52a8eeba5148d3a48 100644 --- a/polkadot/runtime/westend/src/weights/xcm/mod.rs +++ b/polkadot/runtime/westend/src/weights/xcm/mod.rs @@ -142,7 +142,7 @@ impl XcmWeightInfo for WestendXcmWeight { fn descend_origin(_who: &InteriorLocation) -> Weight { XcmGeneric::::descend_origin() } - fn report_error(_query_repsonse_info: &QueryResponseInfo) -> Weight { + fn report_error(_query_response_info: &QueryResponseInfo) -> Weight { XcmGeneric::::report_error() } diff --git a/polkadot/runtime/westend/src/xcm_config.rs b/polkadot/runtime/westend/src/xcm_config.rs index 06124d896fbb31ecca032149fb5604008f51f4b7..96d2a124ff9a343364696025ebae9393baa6dc6c 100644 --- a/polkadot/runtime/westend/src/xcm_config.rs +++ b/polkadot/runtime/westend/src/xcm_config.rs @@ -34,19 +34,18 @@ use runtime_common::{ }; use sp_core::ConstU32; use westend_runtime_constants::{ - currency::CENTS, - system_parachain::*, - xcm::body::{FELLOWSHIP_ADMIN_INDEX, TREASURER_INDEX}, + currency::CENTS, system_parachain::*, xcm::body::FELLOWSHIP_ADMIN_INDEX, }; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, - ChildParachainConvertsVia, DescribeBodyTerminal, DescribeFamily, FrameTransactionalProcessor, - FungibleAdapter, HashedDescription, IsConcrete, MintLocation, OriginToPluralityVoice, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, XcmFeeToAccount, + ChildParachainConvertsVia, DescribeAllTerminal, DescribeFamily, FrameTransactionalProcessor, + FungibleAdapter, HashedDescription, IsChildSystemParachain, IsConcrete, MintLocation, + OriginToPluralityVoice, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, + XcmFeeToAccount, }; use xcm_executor::XcmExecutor; @@ -69,8 +68,8 @@ pub type LocationConverter = ( ChildParachainConvertsVia, // We can directly alias an `AccountId32` into a local account. AccountId32Aliases, - // Allow governance body to be used as a sovereign account. - HashedDescription>, + // Foreign locations alias into accounts according to a hash of their standard description. + HashedDescription>, ); pub type LocalAssetTransactor = FungibleAdapter< @@ -114,12 +113,14 @@ parameter_types! { pub AssetHub: Location = Parachain(ASSET_HUB_ID).into_location(); pub Collectives: Location = Parachain(COLLECTIVES_ID).into_location(); pub BridgeHub: Location = Parachain(BRIDGE_HUB_ID).into_location(); + pub Encointer: Location = Parachain(ENCOINTER_ID).into_location(); pub People: Location = Parachain(PEOPLE_ID).into_location(); pub Broker: Location = Parachain(BROKER_ID).into_location(); pub Wnd: AssetFilter = Wild(AllOf { fun: WildFungible, id: AssetId(TokenLocation::get()) }); pub WndForAssetHub: (AssetFilter, Location) = (Wnd::get(), AssetHub::get()); pub WndForCollectives: (AssetFilter, Location) = (Wnd::get(), Collectives::get()); pub WndForBridgeHub: (AssetFilter, Location) = (Wnd::get(), BridgeHub::get()); + pub WndForEncointer: (AssetFilter, Location) = (Wnd::get(), Encointer::get()); pub WndForPeople: (AssetFilter, Location) = (Wnd::get(), People::get()); pub WndForBroker: (AssetFilter, Location) = (Wnd::get(), Broker::get()); pub MaxInstructions: u32 = 100; @@ -130,6 +131,7 @@ pub type TrustedTeleporters = ( xcm_builder::Case, xcm_builder::Case, xcm_builder::Case, + xcm_builder::Case, xcm_builder::Case, xcm_builder::Case, ); @@ -141,13 +143,12 @@ impl Contains for OnlyParachains { } } -pub struct CollectivesOrFellows; -impl Contains for CollectivesOrFellows { +pub struct Fellows; +impl Contains for Fellows { fn contains(location: &Location) -> bool { matches!( location.unpack(), - (0, [Parachain(COLLECTIVES_ID)]) | - (0, [Parachain(COLLECTIVES_ID), Plurality { id: BodyId::Technical, .. }]) + (0, [Parachain(COLLECTIVES_ID), Plurality { id: BodyId::Technical, .. }]) ) } } @@ -171,8 +172,8 @@ pub type Barrier = TrailingSetTopicAsId<( AllowTopLevelPaidExecutionFrom, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, - // Collectives and Fellows plurality get free execution. - AllowExplicitUnpaidExecutionFrom, + // Messages from system parachains or the Fellows plurality need not pay for execution. + AllowExplicitUnpaidExecutionFrom<(IsChildSystemParachain, Fellows)>, ), UniversalLocation, ConstU32<8>, @@ -218,6 +219,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } parameter_types! { @@ -228,7 +232,7 @@ parameter_types! { // FellowshipAdmin pluralistic body. pub const FellowshipAdminBodyId: BodyId = BodyId::Index(FELLOWSHIP_ADMIN_INDEX); // `Treasurer` pluralistic body. - pub const TreasurerBodyId: BodyId = BodyId::Index(TREASURER_INDEX); + pub const TreasurerBodyId: BodyId = BodyId::Treasury; } /// Type to convert the `GeneralAdmin` origin to a Plurality `Location` value. @@ -262,13 +266,15 @@ pub type LocalPalletOriginToLocation = ( StakingAdminToPlurality, // FellowshipAdmin origin to be used in XCM as a corresponding Plurality `Location` value. FellowshipAdminToPlurality, - // `Treasurer` origin to be used in XCM as a corresponding Plurality `MultiLocation` value. + // `Treasurer` origin to be used in XCM as a corresponding Plurality `Location` value. TreasurerToPlurality, ); impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + // Note that this configuration of `SendXcmOrigin` is different from the one present in + // production. + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; type XcmRouter = XcmRouter; // Anyone can execute XCM messages locally... type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; diff --git a/polkadot/statement-table/Cargo.toml b/polkadot/statement-table/Cargo.toml index 6403b822ed9b1ad6879698b3909f0f48daf1e765..37b8a99d640a2d23e0d7a532adc0c43b04bba1f2 100644 --- a/polkadot/statement-table/Cargo.toml +++ b/polkadot/statement-table/Cargo.toml @@ -13,3 +13,4 @@ workspace = true parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive"] } sp-core = { path = "../../substrate/primitives/core" } primitives = { package = "polkadot-primitives", path = "../primitives" } +gum = { package = "tracing-gum", path = "../node/gum" } diff --git a/polkadot/statement-table/src/generic.rs b/polkadot/statement-table/src/generic.rs index 22bffde5acc11b8fdbea565d054949caf40d788f..2ee6f6a4f781842866728e2105d6c23e2fa1cd70 100644 --- a/polkadot/statement-table/src/generic.rs +++ b/polkadot/statement-table/src/generic.rs @@ -36,6 +36,7 @@ use primitives::{ }; use parity_scale_codec::{Decode, Encode}; +const LOG_TARGET: &str = "parachain::statement-table"; /// Context for the statement table. pub trait Context { @@ -53,9 +54,6 @@ pub trait Context { /// get the digest of a candidate. fn candidate_digest(candidate: &Self::Candidate) -> Self::Digest; - /// get the group of a candidate. - fn candidate_group(candidate: &Self::Candidate) -> Self::GroupId; - /// Whether a authority is a member of a group. /// Members are meant to submit candidates and vote on validity. fn is_member_of(&self, authority: &Self::AuthorityId, group: &Self::GroupId) -> bool; @@ -342,13 +340,13 @@ impl Table { pub fn import_statement( &mut self, context: &Ctx, + group_id: Ctx::GroupId, statement: SignedStatement, ) -> Option> { let SignedStatement { statement, signature, sender: signer } = statement; - let res = match statement { Statement::Seconded(candidate) => - self.import_candidate(context, signer.clone(), candidate, signature), + self.import_candidate(context, signer.clone(), candidate, signature, group_id), Statement::Valid(digest) => self.validity_vote(context, signer.clone(), digest, ValidityVote::Valid(signature)), }; @@ -387,9 +385,10 @@ impl Table { authority: Ctx::AuthorityId, candidate: Ctx::Candidate, signature: Ctx::Signature, + group: Ctx::GroupId, ) -> ImportResult { - let group = Ctx::candidate_group(&candidate); if !context.is_member_of(&authority, &group) { + gum::debug!(target: LOG_TARGET, authority = ?authority, group = ?group, "New `Misbehavior::UnauthorizedStatement`, candidate backed by validator that doesn't belong to expected group" ); return Err(Misbehavior::UnauthorizedStatement(UnauthorizedStatement { statement: SignedStatement { signature, @@ -634,10 +633,6 @@ mod tests { Digest(candidate.1) } - fn candidate_group(candidate: &Candidate) -> GroupId { - GroupId(candidate.0) - } - fn is_member_of(&self, authority: &AuthorityId, group: &GroupId) -> bool { self.authorities.get(authority).map(|v| v == group).unwrap_or(false) } @@ -675,10 +670,10 @@ mod tests { sender: AuthorityId(1), }; - table.import_statement(&context, statement_a); + table.import_statement(&context, GroupId(2), statement_a); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1))); - table.import_statement(&context, statement_b); + table.import_statement(&context, GroupId(2), statement_b); assert_eq!( table.detected_misbehavior[&AuthorityId(1)][0], Misbehavior::MultipleCandidates(MultipleCandidates { @@ -711,10 +706,10 @@ mod tests { sender: AuthorityId(1), }; - table.import_statement(&context, statement_a); + table.import_statement(&context, GroupId(2), statement_a); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1))); - table.import_statement(&context, statement_b); + table.import_statement(&context, GroupId(2), statement_b); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1))); } @@ -735,7 +730,7 @@ mod tests { sender: AuthorityId(1), }; - table.import_statement(&context, statement); + table.import_statement(&context, GroupId(2), statement); assert_eq!( table.detected_misbehavior[&AuthorityId(1)][0], @@ -769,7 +764,7 @@ mod tests { }; let candidate_a_digest = Digest(100); - table.import_statement(&context, candidate_a); + table.import_statement(&context, GroupId(2), candidate_a); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1))); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(2))); @@ -779,7 +774,7 @@ mod tests { signature: Signature(2), sender: AuthorityId(2), }; - table.import_statement(&context, bad_validity_vote); + table.import_statement(&context, GroupId(3), bad_validity_vote); assert_eq!( table.detected_misbehavior[&AuthorityId(2)][0], @@ -811,7 +806,7 @@ mod tests { sender: AuthorityId(1), }; - table.import_statement(&context, statement); + table.import_statement(&context, GroupId(2), statement); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1))); let invalid_statement = SignedStatement { @@ -820,7 +815,7 @@ mod tests { sender: AuthorityId(1), }; - table.import_statement(&context, invalid_statement); + table.import_statement(&context, GroupId(2), invalid_statement); assert!(table.detected_misbehavior.contains_key(&AuthorityId(1))); } @@ -842,7 +837,7 @@ mod tests { }; let candidate_digest = Digest(100); - table.import_statement(&context, statement); + table.import_statement(&context, GroupId(2), statement); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1))); let extra_vote = SignedStatement { @@ -851,7 +846,7 @@ mod tests { sender: AuthorityId(1), }; - table.import_statement(&context, extra_vote); + table.import_statement(&context, GroupId(2), extra_vote); assert_eq!( table.detected_misbehavior[&AuthorityId(1)][0], Misbehavior::ValidityDoubleVote(ValidityDoubleVote::IssuedAndValidity( @@ -910,7 +905,7 @@ mod tests { }; let candidate_digest = Digest(100); - table.import_statement(&context, statement); + table.import_statement(&context, GroupId(2), statement); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1))); assert!(table.attested_candidate(&candidate_digest, &context, 2).is_none()); @@ -921,7 +916,7 @@ mod tests { sender: AuthorityId(2), }; - table.import_statement(&context, vote); + table.import_statement(&context, GroupId(2), vote); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(2))); assert!(table.attested_candidate(&candidate_digest, &context, 2).is_some()); } @@ -944,7 +939,7 @@ mod tests { }; let summary = table - .import_statement(&context, statement) + .import_statement(&context, GroupId(2), statement) .expect("candidate import to give summary"); assert_eq!(summary.candidate, Digest(100)); @@ -971,7 +966,7 @@ mod tests { }; let candidate_digest = Digest(100); - table.import_statement(&context, statement); + table.import_statement(&context, GroupId(2), statement); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1))); let vote = SignedStatement { @@ -980,8 +975,9 @@ mod tests { sender: AuthorityId(2), }; - let summary = - table.import_statement(&context, vote).expect("candidate vote to give summary"); + let summary = table + .import_statement(&context, GroupId(2), vote) + .expect("candidate vote to give summary"); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(2))); diff --git a/polkadot/statement-table/src/lib.rs b/polkadot/statement-table/src/lib.rs index d4629330ac01512e2dc2b0f441d2302f0fd38624..3740d15cc4f326962b962557e61ca14e9163de7d 100644 --- a/polkadot/statement-table/src/lib.rs +++ b/polkadot/statement-table/src/lib.rs @@ -35,8 +35,8 @@ pub use generic::{Config, Context, Table}; pub mod v2 { use crate::generic; use primitives::{ - CandidateHash, CommittedCandidateReceipt, CompactStatement as PrimitiveStatement, Id, - ValidatorIndex, ValidatorSignature, + CandidateHash, CommittedCandidateReceipt, CompactStatement as PrimitiveStatement, + CoreIndex, ValidatorIndex, ValidatorSignature, }; /// Statements about candidates on the network. @@ -59,7 +59,7 @@ pub mod v2 { >; /// A summary of import of a statement. - pub type Summary = generic::Summary; + pub type Summary = generic::Summary; impl<'a> From<&'a Statement> for PrimitiveStatement { fn from(s: &'a Statement) -> PrimitiveStatement { diff --git a/polkadot/tests/common.rs b/polkadot/tests/common.rs index 15721c990e01b30b97db32151b0899c1e5a3888b..dbee2d3650347e228db0b3663a696f67f5a2240a 100644 --- a/polkadot/tests/common.rs +++ b/polkadot/tests/common.rs @@ -48,8 +48,8 @@ pub async fn wait_n_finalized_blocks(n: usize, url: &str) { /// Read the WS address from the output. /// -/// This is hack to get the actual binded sockaddr because -/// polkadot assigns a random port if the specified port was already binded. +/// This is hack to get the actual bound sockaddr because +/// polkadot assigns a random port if the specified port was already bound. /// /// You must call /// `Command::new("cmd").stdout(process::Stdio::piped()).stderr(process::Stdio::piped())` diff --git a/polkadot/tests/running_the_node_and_interrupt.rs b/polkadot/tests/running_the_node_and_interrupt.rs index 079c34e0421e8a1936359b03bc46f3fea87f4b8b..85c073d3023a8e28417a2f0eb5047e2f04100820 100644 --- a/polkadot/tests/running_the_node_and_interrupt.rs +++ b/polkadot/tests/running_the_node_and_interrupt.rs @@ -32,7 +32,7 @@ async fn running_the_node_works_and_can_be_interrupted() { }; async fn run_command_and_kill(signal: Signal) { - let tmpdir = tempdir().expect("coult not create temp dir"); + let tmpdir = tempdir().expect("could not create temp dir"); let mut cmd = Command::new(cargo_bin("polkadot")) .stdout(process::Stdio::piped()) diff --git a/polkadot/utils/generate-bags/Cargo.toml b/polkadot/utils/generate-bags/Cargo.toml index c0e9bd332df7ccca511bd199221405ce4d43d025..ad6d7259d2483e11e9df8c121bab75461a71c525 100644 --- a/polkadot/utils/generate-bags/Cargo.toml +++ b/polkadot/utils/generate-bags/Cargo.toml @@ -10,7 +10,7 @@ description = "CLI to generate voter bags for Polkadot runtimes" workspace = true [dependencies] -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } generate-bags = { path = "../../../substrate/utils/frame/generate-bags" } sp-io = { path = "../../../substrate/primitives/io" } diff --git a/polkadot/utils/remote-ext-tests/bags-list/Cargo.toml b/polkadot/utils/remote-ext-tests/bags-list/Cargo.toml index ddc5af97a166af253e660d0523b7ac6e357c367c..20e4130f888bcb1f826dab81fb7e5a721a53a225 100644 --- a/polkadot/utils/remote-ext-tests/bags-list/Cargo.toml +++ b/polkadot/utils/remote-ext-tests/bags-list/Cargo.toml @@ -18,6 +18,6 @@ sp-tracing = { path = "../../../../substrate/primitives/tracing" } frame-system = { path = "../../../../substrate/frame/system" } sp-core = { path = "../../../../substrate/primitives/core" } -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } log = { workspace = true, default-features = true } tokio = { version = "1.24.2", features = ["macros"] } diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs index 637446832fdca7d836f4df6b8565a46682f96213..c831cd024659135fd3fd5d85a4406351945d887f 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs @@ -46,7 +46,7 @@ parameter_types! { frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1024, u64::MAX)); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = Everything; type BlockWeights = (); @@ -77,7 +77,7 @@ parameter_types! { pub const ExistentialDeposit: u64 = 7; } -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for Test { type ReserveIdentifier = [u8; 8]; type AccountStore = System; @@ -145,6 +145,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } impl crate::Config for Test { diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs index c84f062a8d169b2ca25704e13b08449573a97006..534f7d85ea2e9aa9759aca4bda9e4759dbf49517 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs @@ -51,7 +51,7 @@ parameter_types! { frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1024, u64::MAX)); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = Everything; type BlockWeights = (); @@ -135,13 +135,16 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Aliasers; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } parameter_types! { pub const ExistentialDeposit: u64 = 7; } -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for Test { type ReserveIdentifier = [u8; 8]; type AccountStore = System; diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs index 6ce8d3e99e8e54114048ff35d2c2a7c38b01b9c4..63ed0ac0ca736450077a4677d29d65a81e9eaf3b 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs @@ -22,10 +22,8 @@ use codec::Encode; use frame_benchmarking::{account, BenchmarkError}; use sp_std::prelude::*; use xcm::latest::prelude::*; -use xcm_executor::{ - traits::{ConvertLocation, FeeReason}, - Config as XcmConfig, FeesMode, -}; +use xcm_builder::EnsureDelivery; +use xcm_executor::{traits::ConvertLocation, Config as XcmConfig}; pub mod fungible; pub mod generic; @@ -114,29 +112,3 @@ pub fn account_and_location(index: u32) -> (T::AccountId, Location) { (account, location) } - -/// Trait for a type which ensures all requirements for successful delivery with XCM transport -/// layers. -pub trait EnsureDelivery { - /// Prepare all requirements for successful `XcmSender: SendXcm` passing (accounts, balances, - /// channels ...). Returns: - /// - possible `FeesMode` which is expected to be set to executor - /// - possible `Assets` which are expected to be subsume to the Holding Register - fn ensure_successful_delivery( - origin_ref: &Location, - dest: &Location, - fee_reason: FeeReason, - ) -> (Option, Option); -} - -/// `()` implementation does nothing which means no special requirements for environment. -impl EnsureDelivery for () { - fn ensure_successful_delivery( - _origin_ref: &Location, - _dest: &Location, - _fee_reason: FeeReason, - ) -> (Option, Option) { - // doing nothing - (None, None) - } -} diff --git a/polkadot/xcm/pallet-xcm/Cargo.toml b/polkadot/xcm/pallet-xcm/Cargo.toml index 4840b6127f55425de91eea85099649d4dfda0881..08307c34f8a9909beb6bbaa68ba9e843d1dfa53a 100644 --- a/polkadot/xcm/pallet-xcm/Cargo.toml +++ b/polkadot/xcm/pallet-xcm/Cargo.toml @@ -26,6 +26,7 @@ sp-std = { path = "../../../substrate/primitives/std", default-features = false xcm = { package = "staging-xcm", path = "..", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../xcm-executor", default-features = false } xcm-builder = { package = "staging-xcm-builder", path = "../xcm-builder", default-features = false } +xcm-fee-payment-runtime-api = { path = "../xcm-fee-payment-runtime-api", default-features = false } # marked optional, used in benchmarking frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } @@ -54,6 +55,7 @@ std = [ "sp-std/std", "xcm-builder/std", "xcm-executor/std", + "xcm-fee-payment-runtime-api/std", "xcm/std", ] runtime-benchmarks = [ diff --git a/polkadot/xcm/pallet-xcm/src/benchmarking.rs b/polkadot/xcm/pallet-xcm/src/benchmarking.rs index c7d8fb24e9df320f8439616591c467f3f6d7aa1e..e2903d592dc18aeafc7ef15d5ccc8d3fade1bcef 100644 --- a/polkadot/xcm/pallet-xcm/src/benchmarking.rs +++ b/polkadot/xcm/pallet-xcm/src/benchmarking.rs @@ -16,22 +16,28 @@ use super::*; use bounded_collections::{ConstU32, WeakBoundedVec}; +use codec::Encode; use frame_benchmarking::{benchmarks, whitelisted_caller, BenchmarkError, BenchmarkResult}; -use frame_support::{traits::Currency, weights::Weight}; +use frame_support::{ + traits::fungible::{Inspect, Mutate}, + weights::Weight, +}; use frame_system::RawOrigin; use sp_std::prelude::*; use xcm::{latest::prelude::*, v2}; +use xcm_builder::EnsureDelivery; +use xcm_executor::traits::FeeReason; type RuntimeOrigin = ::RuntimeOrigin; -// existential deposit multiplier -const ED_MULTIPLIER: u32 = 100; - /// Pallet we're benchmarking here. pub struct Pallet(crate::Pallet); /// Trait that must be implemented by runtime to be able to benchmark pallet properly. pub trait Config: crate::Config { + /// Helper that ensures successful delivery for extrinsics/benchmarks which need `SendXcm`. + type DeliveryHelper: EnsureDelivery; + /// A `Location` that can be reached via `XcmRouter`. Used only in benchmarks. /// /// If `None`, the benchmarks that depend on a reachable destination will be skipped. @@ -74,6 +80,13 @@ pub trait Config: crate::Config { fn set_up_complex_asset_transfer() -> Option<(Assets, u32, Location, Box)> { None } + + /// Gets an asset that can be handled by the AssetTransactor. + /// + /// Used only in benchmarks. + /// + /// Used, for example, in the benchmark for `claim_assets`. + fn get_asset() -> Asset; } benchmarks! { @@ -96,6 +109,21 @@ benchmarks! { let versioned_msg = VersionedXcm::from(msg); }: _>(send_origin, Box::new(versioned_dest), Box::new(versioned_msg)) + send_blob { + let send_origin = + T::SendXcmOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + if T::SendXcmOrigin::try_origin(send_origin.clone()).is_err() { + return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))) + } + let msg = Xcm::<()>(vec![ClearOrigin]); + let versioned_dest: VersionedLocation = T::reachable_dest().ok_or( + BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)), + )? + .into(); + let versioned_msg = VersionedXcm::from(msg); + let encoded_versioned_msg = versioned_msg.encode().try_into().unwrap(); + }: _>(send_origin, Box::new(versioned_dest), encoded_versioned_msg) + teleport_assets { let (asset, destination) = T::teleportable_asset_and_dest().ok_or( BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)), @@ -107,23 +135,29 @@ benchmarks! { }.into(); let assets: Assets = asset.into(); - let existential_deposit = T::ExistentialDeposit::get(); - let caller = whitelisted_caller(); - - // Give some multiple of the existential deposit - let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - assert!(balance >= transferred_amount); - let _ = as Currency<_>>::make_free_balance_be(&caller, balance); - // verify initial balance - assert_eq!(pallet_balances::Pallet::::free_balance(&caller), balance); - + let caller: T::AccountId = whitelisted_caller(); let send_origin = RawOrigin::Signed(caller.clone()); let origin_location = T::ExecuteXcmOrigin::try_origin(send_origin.clone().into()) .map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?; - if !T::XcmTeleportFilter::contains(&(origin_location, assets.clone().into_inner())) { + if !T::XcmTeleportFilter::contains(&(origin_location.clone(), assets.clone().into_inner())) { return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))) } + // Ensure that origin can send to destination (e.g. setup delivery fees, ensure router setup, ...) + let (_, _) = T::DeliveryHelper::ensure_successful_delivery( + &origin_location, + &destination, + FeeReason::ChargeFees, + ); + + // Actual balance (e.g. `ensure_successful_delivery` could drip delivery fees, ...) + let balance = as Inspect<_>>::balance(&caller); + // Add transferred_amount to origin + as Mutate<_>>::mint_into(&caller, transferred_amount)?; + // verify initial balance + let balance = balance + transferred_amount; + assert_eq!( as Inspect<_>>::balance(&caller), balance); + let recipient = [0u8; 32]; let versioned_dest: VersionedLocation = destination.into(); let versioned_beneficiary: VersionedLocation = @@ -132,7 +166,7 @@ benchmarks! { }: _>(send_origin.into(), Box::new(versioned_dest), Box::new(versioned_beneficiary), Box::new(versioned_assets), 0) verify { // verify balance after transfer, decreased by transferred amount (+ maybe XCM delivery fees) - assert!(pallet_balances::Pallet::::free_balance(&caller) <= balance - transferred_amount); + assert!( as Inspect<_>>::balance(&caller) <= balance - transferred_amount); } reserve_transfer_assets { @@ -146,23 +180,29 @@ benchmarks! { }.into(); let assets: Assets = asset.into(); - let existential_deposit = T::ExistentialDeposit::get(); - let caller = whitelisted_caller(); - - // Give some multiple of the existential deposit - let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - assert!(balance >= transferred_amount); - let _ = as Currency<_>>::make_free_balance_be(&caller, balance); - // verify initial balance - assert_eq!(pallet_balances::Pallet::::free_balance(&caller), balance); - + let caller: T::AccountId = whitelisted_caller(); let send_origin = RawOrigin::Signed(caller.clone()); let origin_location = T::ExecuteXcmOrigin::try_origin(send_origin.clone().into()) .map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?; - if !T::XcmReserveTransferFilter::contains(&(origin_location, assets.clone().into_inner())) { + if !T::XcmReserveTransferFilter::contains(&(origin_location.clone(), assets.clone().into_inner())) { return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))) } + // Ensure that origin can send to destination (e.g. setup delivery fees, ensure router setup, ...) + let (_, _) = T::DeliveryHelper::ensure_successful_delivery( + &origin_location, + &destination, + FeeReason::ChargeFees, + ); + + // Actual balance (e.g. `ensure_successful_delivery` could drip delivery fees, ...) + let balance = as Inspect<_>>::balance(&caller); + // Add transferred_amount to origin + as Mutate<_>>::mint_into(&caller, transferred_amount)?; + // verify initial balance + let balance = balance + transferred_amount; + assert_eq!( as Inspect<_>>::balance(&caller), balance); + let recipient = [0u8; 32]; let versioned_dest: VersionedLocation = destination.into(); let versioned_beneficiary: VersionedLocation = @@ -171,7 +211,7 @@ benchmarks! { }: _>(send_origin.into(), Box::new(versioned_dest), Box::new(versioned_beneficiary), Box::new(versioned_assets), 0) verify { // verify balance after transfer, decreased by transferred amount (+ maybe XCM delivery fees) - assert!(pallet_balances::Pallet::::free_balance(&caller) <= balance - transferred_amount); + assert!( as Inspect<_>>::balance(&caller) <= balance - transferred_amount); } transfer_assets { @@ -203,6 +243,19 @@ benchmarks! { let versioned_msg = VersionedXcm::from(msg); }: _>(execute_origin, Box::new(versioned_msg), Weight::MAX) + execute_blob { + let execute_origin = + T::ExecuteXcmOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let origin_location = T::ExecuteXcmOrigin::try_origin(execute_origin.clone()) + .map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?; + let msg = Xcm(vec![ClearOrigin]); + if !T::XcmExecuteFilter::contains(&(origin_location, msg.clone())) { + return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))) + } + let versioned_msg = VersionedXcm::from(msg); + let encoded_versioned_msg = versioned_msg.encode().try_into().unwrap(); + }: _>(execute_origin, encoded_versioned_msg, Weight::MAX) + force_xcm_version { let loc = T::reachable_dest().ok_or( BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)), @@ -324,11 +377,23 @@ benchmarks! { u32::MAX, ).unwrap()).collect::>(); crate::Pallet::::expect_response(query_id, Response::PalletsInfo(infos.try_into().unwrap())); - }: { as QueryHandler>::take_response(query_id); } + claim_assets { + let claim_origin = RawOrigin::Signed(whitelisted_caller()); + let claim_location = T::ExecuteXcmOrigin::try_origin(claim_origin.clone().into()).map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?; + let asset: Asset = T::get_asset(); + // Trap assets for claiming later + crate::Pallet::::drop_assets( + &claim_location, + asset.clone().into(), + &XcmContext { origin: None, message_id: [0u8; 32], topic: None } + ); + let versioned_assets = VersionedAssets::V4(asset.into()); + }: _>(claim_origin.into(), Box::new(versioned_assets), Box::new(VersionedLocation::V4(claim_location))) + impl_benchmark_test_suite!( Pallet, crate::mock::new_test_ext_with_balances(Vec::new()), diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index 5e1a3e55f9b62b67bb3cedb6a279a1c4c9046405..29b61988f73c884dbcdf1ce0eae0572175ef7f14 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -50,8 +50,8 @@ use sp_runtime::{ use sp_std::{boxed::Box, marker::PhantomData, prelude::*, result::Result, vec}; use xcm::{latest::QueryResponseInfo, prelude::*}; use xcm_builder::{ - ExecuteController, ExecuteControllerWeightInfo, QueryController, QueryControllerWeightInfo, - SendController, SendControllerWeightInfo, + ExecuteController, ExecuteControllerWeightInfo, MaxXcmEncodedSize, QueryController, + QueryControllerWeightInfo, SendController, SendControllerWeightInfo, }; use xcm_executor::{ traits::{ @@ -61,6 +61,7 @@ use xcm_executor::{ }, AssetsInHolding, }; +use xcm_fee_payment_runtime_api::Error as FeePaymentError; #[cfg(any(feature = "try-runtime", test))] use sp_runtime::TryRuntimeError; @@ -85,6 +86,9 @@ pub trait WeightInfo { fn migrate_and_notify_old_targets() -> Weight; fn new_query() -> Weight; fn take_response() -> Weight; + fn claim_assets() -> Weight; + fn execute_blob() -> Weight; + fn send_blob() -> Weight; } /// fallback implementation @@ -165,6 +169,18 @@ impl WeightInfo for TestWeightInfo { fn take_response() -> Weight { Weight::from_parts(100_000_000, 0) } + + fn claim_assets() -> Weight { + Weight::from_parts(100_000_000, 0) + } + + fn execute_blob() -> Weight { + Weight::from_parts(100_000_000, 0) + } + + fn send_blob() -> Weight { + Weight::from_parts(100_000_000, 0) + } } #[frame_support::pallet] @@ -280,76 +296,49 @@ pub mod pallet { } impl ExecuteControllerWeightInfo for Pallet { - fn execute() -> Weight { - T::WeightInfo::execute() + fn execute_blob() -> Weight { + T::WeightInfo::execute_blob() } } impl ExecuteController, ::RuntimeCall> for Pallet { type WeightInfo = Self; - fn execute( + fn execute_blob( origin: OriginFor, - message: Box::RuntimeCall>>, + encoded_message: BoundedVec, max_weight: Weight, ) -> Result { - log::trace!(target: "xcm::pallet_xcm::execute", "message {:?}, max_weight {:?}", message, max_weight); - let outcome = (|| { - let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; - let mut hash = message.using_encoded(sp_io::hashing::blake2_256); - let message = (*message).try_into().map_err(|()| Error::::BadVersion)?; - let value = (origin_location, message); - ensure!(T::XcmExecuteFilter::contains(&value), Error::::Filtered); - let (origin_location, message) = value; - Ok(T::XcmExecutor::prepare_and_execute( - origin_location, - message, - &mut hash, - max_weight, - max_weight, - )) - })() - .map_err(|e: DispatchError| { - e.with_weight(::execute()) - })?; - - Self::deposit_event(Event::Attempted { outcome: outcome.clone() }); - let weight_used = outcome.weight_used(); - outcome.ensure_complete().map_err(|error| { - log::error!(target: "xcm::pallet_xcm::execute", "XCM execution failed with error {:?}", error); - Error::::LocalExecutionIncomplete.with_weight( - weight_used.saturating_add( - ::execute(), - ), - ) - })?; - Ok(weight_used) + let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; + let message = + VersionedXcm::<::RuntimeCall>::decode(&mut &encoded_message[..]) + .map_err(|error| { + log::error!(target: "xcm::execute_blob", "Unable to decode XCM, error: {:?}", error); + Error::::UnableToDecode + })?; + Self::execute_base(origin_location, Box::new(message), max_weight) } } impl SendControllerWeightInfo for Pallet { - fn send() -> Weight { - T::WeightInfo::send() + fn send_blob() -> Weight { + T::WeightInfo::send_blob() } } impl SendController> for Pallet { type WeightInfo = Self; - fn send( + fn send_blob( origin: OriginFor, dest: Box, - message: Box>, + encoded_message: BoundedVec, ) -> Result { let origin_location = T::SendXcmOrigin::ensure_origin(origin)?; - let interior: Junctions = - origin_location.clone().try_into().map_err(|_| Error::::InvalidOrigin)?; - let dest = Location::try_from(*dest).map_err(|()| Error::::BadVersion)?; - let message: Xcm<()> = (*message).try_into().map_err(|()| Error::::BadVersion)?; - - let message_id = Self::send_xcm(interior, dest.clone(), message.clone()) - .map_err(Error::::from)?; - let e = Event::Sent { origin: origin_location, destination: dest, message, message_id }; - Self::deposit_event(e); - Ok(message_id) + let message = + VersionedXcm::<()>::decode(&mut &encoded_message[..]).map_err(|error| { + log::error!(target: "xcm::send_blob", "Unable to decode XCM, error: {:?}", error); + Error::::UnableToDecode + })?; + Self::send_base(origin_location, dest, Box::new(message)) } } @@ -369,7 +358,7 @@ pub mod pallet { origin: OriginFor, timeout: BlockNumberFor, match_querier: VersionedLocation, - ) -> Result { + ) -> Result { let responder = ::ExecuteXcmOrigin::ensure_origin(origin)?; let query_id = ::new_query( responder, @@ -556,6 +545,11 @@ pub mod pallet { TooManyReserves, /// Local XCM execution incomplete. LocalExecutionIncomplete, + /// Could not decode XCM. + UnableToDecode, + /// XCM encoded length is too large. + /// Returned when an XCM encoded length is larger than `MaxXcmEncodedSize`. + XcmTooLarge, } impl From for Error { @@ -893,8 +887,64 @@ pub mod pallet { } } + impl Pallet { + /// Underlying logic for both [`execute_blob`] and [`execute`]. + fn execute_base( + origin_location: Location, + message: Box::RuntimeCall>>, + max_weight: Weight, + ) -> Result { + log::trace!(target: "xcm::pallet_xcm::execute", "message {:?}, max_weight {:?}", message, max_weight); + let outcome = (|| { + let mut hash = message.using_encoded(sp_io::hashing::blake2_256); + let message = (*message).try_into().map_err(|()| Error::::BadVersion)?; + let value = (origin_location, message); + ensure!(T::XcmExecuteFilter::contains(&value), Error::::Filtered); + let (origin_location, message) = value; + Ok(T::XcmExecutor::prepare_and_execute( + origin_location, + message, + &mut hash, + max_weight, + max_weight, + )) + })() + .map_err(|e: DispatchError| e.with_weight(T::WeightInfo::execute()))?; + + Self::deposit_event(Event::Attempted { outcome: outcome.clone() }); + let weight_used = outcome.weight_used(); + outcome.ensure_complete().map_err(|error| { + log::error!(target: "xcm::pallet_xcm::execute", "XCM execution failed with error {:?}", error); + Error::::LocalExecutionIncomplete + .with_weight(weight_used.saturating_add(T::WeightInfo::execute())) + })?; + Ok(weight_used) + } + + /// Underlying logic for both [`send_blob`] and [`send`]. + fn send_base( + origin_location: Location, + dest: Box, + message: Box>, + ) -> Result { + let interior: Junctions = + origin_location.clone().try_into().map_err(|_| Error::::InvalidOrigin)?; + let dest = Location::try_from(*dest).map_err(|()| Error::::BadVersion)?; + let message: Xcm<()> = (*message).try_into().map_err(|()| Error::::BadVersion)?; + + let message_id = Self::send_xcm(interior, dest.clone(), message.clone()) + .map_err(Error::::from)?; + let e = Event::Sent { origin: origin_location, destination: dest, message, message_id }; + Self::deposit_event(e); + Ok(message_id) + } + } + #[pallet::call] impl Pallet { + /// WARNING: DEPRECATED. `send` will be removed after June 2024. Use `send_blob` instead. + #[allow(deprecated)] + #[deprecated(note = "`send` will be removed after June 2024. Use `send_blob` instead.")] #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::send())] pub fn send( @@ -902,7 +952,8 @@ pub mod pallet { dest: Box, message: Box>, ) -> DispatchResult { - >::send(origin, dest, message)?; + let origin_location = T::SendXcmOrigin::ensure_origin(origin)?; + Self::send_base(origin_location, dest, message)?; Ok(()) } @@ -1025,6 +1076,13 @@ pub mod pallet { /// No more than `max_weight` will be used in its attempted execution. If this is less than /// the maximum amount of weight that the message could take to be executed, then no /// execution attempt will be made. + /// + /// WARNING: DEPRECATED. `execute` will be removed after June 2024. Use `execute_blob` + /// instead. + #[allow(deprecated)] + #[deprecated( + note = "`execute` will be removed after June 2024. Use `execute_blob` instead." + )] #[pallet::call_index(3)] #[pallet::weight(max_weight.saturating_add(T::WeightInfo::execute()))] pub fn execute( @@ -1032,8 +1090,8 @@ pub mod pallet { message: Box::RuntimeCall>>, max_weight: Weight, ) -> DispatchResultWithPostInfo { - let weight_used = - >::execute(origin, message, max_weight)?; + let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; + let weight_used = Self::execute_base(origin_location, message, max_weight)?; Ok(Some(weight_used.saturating_add(T::WeightInfo::execute())).into()) } @@ -1386,6 +1444,106 @@ pub mod pallet { weight_limit, ) } + + /// Claims assets trapped on this pallet because of leftover assets during XCM execution. + /// + /// - `origin`: Anyone can call this extrinsic. + /// - `assets`: The exact assets that were trapped. Use the version to specify what version + /// was the latest when they were trapped. + /// - `beneficiary`: The location/account where the claimed assets will be deposited. + #[pallet::call_index(12)] + #[pallet::weight({ + let assets_version = assets.identify_version(); + let maybe_assets: Result = (*assets.clone()).try_into(); + let maybe_beneficiary: Result = (*beneficiary.clone()).try_into(); + match (maybe_assets, maybe_beneficiary) { + (Ok(assets), Ok(beneficiary)) => { + let ticket: Location = GeneralIndex(assets_version as u128).into(); + let mut message = Xcm(vec![ + ClaimAsset { assets: assets.clone(), ticket }, + DepositAsset { assets: AllCounted(assets.len() as u32).into(), beneficiary }, + ]); + T::Weigher::weight(&mut message).map_or(Weight::MAX, |w| T::WeightInfo::claim_assets().saturating_add(w)) + } + _ => Weight::MAX + } + })] + pub fn claim_assets( + origin: OriginFor, + assets: Box, + beneficiary: Box, + ) -> DispatchResult { + let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; + log::debug!(target: "xcm::pallet_xcm::claim_assets", "origin: {:?}, assets: {:?}, beneficiary: {:?}", origin_location, assets, beneficiary); + // Extract version from `assets`. + let assets_version = assets.identify_version(); + let assets: Assets = (*assets).try_into().map_err(|()| Error::::BadVersion)?; + let number_of_assets = assets.len() as u32; + let beneficiary: Location = + (*beneficiary).try_into().map_err(|()| Error::::BadVersion)?; + let ticket: Location = GeneralIndex(assets_version as u128).into(); + let mut message = Xcm(vec![ + ClaimAsset { assets, ticket }, + DepositAsset { assets: AllCounted(number_of_assets).into(), beneficiary }, + ]); + let weight = + T::Weigher::weight(&mut message).map_err(|()| Error::::UnweighableMessage)?; + let mut hash = message.using_encoded(sp_io::hashing::blake2_256); + let outcome = T::XcmExecutor::prepare_and_execute( + origin_location, + message, + &mut hash, + weight, + weight, + ); + outcome.ensure_complete().map_err(|error| { + log::error!(target: "xcm::pallet_xcm::claim_assets", "XCM execution failed with error: {:?}", error); + Error::::LocalExecutionIncomplete + })?; + Ok(()) + } + + /// Execute an XCM from a local, signed, origin. + /// + /// An event is deposited indicating whether the message could be executed completely + /// or only partially. + /// + /// No more than `max_weight` will be used in its attempted execution. If this is less than + /// the maximum amount of weight that the message could take to be executed, then no + /// execution attempt will be made. + /// + /// The message is passed in encoded. It needs to be decodable as a [`VersionedXcm`]. + #[pallet::call_index(13)] + #[pallet::weight(T::WeightInfo::execute_blob())] + pub fn execute_blob( + origin: OriginFor, + encoded_message: BoundedVec, + max_weight: Weight, + ) -> DispatchResultWithPostInfo { + let weight_used = >::execute_blob( + origin, + encoded_message, + max_weight, + )?; + Ok(Some(weight_used.saturating_add(T::WeightInfo::execute_blob())).into()) + } + + /// Send an XCM from a local, signed, origin. + /// + /// The destination, `dest`, will receive this message with a `DescendOrigin` instruction + /// that makes the origin of the message be the origin on this system. + /// + /// The message is passed in encoded. It needs to be decodable as a [`VersionedXcm`]. + #[pallet::call_index(14)] + #[pallet::weight(T::WeightInfo::send_blob())] + pub fn send_blob( + origin: OriginFor, + dest: Box, + encoded_message: BoundedVec, + ) -> DispatchResult { + >::send_blob(origin, dest, encoded_message)?; + Ok(()) + } } } @@ -1415,7 +1573,6 @@ impl sp_std::fmt::Debug for FeesHandling { } impl QueryHandler for Pallet { - type QueryId = u64; type BlockNumber = BlockNumberFor; type Error = XcmError; type UniversalLocation = T::UniversalLocation; @@ -1425,7 +1582,7 @@ impl QueryHandler for Pallet { responder: impl Into, timeout: BlockNumberFor, match_querier: impl Into, - ) -> Self::QueryId { + ) -> QueryId { Self::do_new_query(responder, None, timeout, match_querier) } @@ -1435,7 +1592,7 @@ impl QueryHandler for Pallet { message: &mut Xcm<()>, responder: impl Into, timeout: Self::BlockNumber, - ) -> Result { + ) -> Result { let responder = responder.into(); let destination = Self::UniversalLocation::get() .invert_target(&responder) @@ -1448,7 +1605,7 @@ impl QueryHandler for Pallet { } /// Removes response when ready and emits [Event::ResponseTaken] event. - fn take_response(query_id: Self::QueryId) -> QueryResponseStatus { + fn take_response(query_id: QueryId) -> QueryResponseStatus { match Queries::::get(query_id) { Some(QueryStatus::Ready { response, at }) => match response.try_into() { Ok(response) => { @@ -1465,7 +1622,7 @@ impl QueryHandler for Pallet { } #[cfg(feature = "runtime-benchmarks")] - fn expect_response(id: Self::QueryId, response: Response) { + fn expect_response(id: QueryId, response: Response) { let response = response.into(); Queries::::insert( id, @@ -1928,6 +2085,7 @@ impl Pallet { ]); Ok(Xcm(vec![ WithdrawAsset(assets.into()), + SetFeesMode { jit_withdraw: true }, InitiateReserveWithdraw { assets: Wild(AllCounted(max_assets)), reserve, @@ -2300,6 +2458,37 @@ impl Pallet { AccountIdConversion::::into_account_truncating(&ID) } + pub fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + let message = + Xcm::<()>::try_from(message).map_err(|_| FeePaymentError::VersionedConversionFailed)?; + + T::Weigher::weight(&mut message.into()).map_err(|()| { + log::error!(target: "xcm::pallet_xcm::query_xcm_weight", "Error when querying XCM weight"); + FeePaymentError::WeightNotComputable + }) + } + + pub fn query_delivery_fees( + destination: VersionedLocation, + message: VersionedXcm<()>, + ) -> Result { + let result_version = destination.identify_version().max(message.identify_version()); + + let destination = + destination.try_into().map_err(|_| FeePaymentError::VersionedConversionFailed)?; + + let message = message.try_into().map_err(|_| FeePaymentError::VersionedConversionFailed)?; + + let (_, fees) = validate_send::(destination, message).map_err(|error| { + log::error!(target: "xcm::pallet_xcm::query_delivery_fees", "Error when querying delivery fees: {:?}", error); + FeePaymentError::Unroutable + })?; + + VersionedAssets::from(fees) + .into_version(result_version) + .map_err(|_| FeePaymentError::VersionedConversionFailed) + } + /// Create a new expectation of a query response with the querier being here. fn do_new_query( responder: impl Into, diff --git a/polkadot/xcm/pallet-xcm/src/mock.rs b/polkadot/xcm/pallet-xcm/src/mock.rs index 004a73ca6ab23ff0bcf59748ba477fce2fdb341d..2cc228476ba8e18dace2fc950d7253cba61cd8a4 100644 --- a/polkadot/xcm/pallet-xcm/src/mock.rs +++ b/polkadot/xcm/pallet-xcm/src/mock.rs @@ -172,7 +172,7 @@ impl SendXcm for TestSendXcm { msg: &mut Option>, ) -> SendResult<(Location, Xcm<()>)> { if FAIL_SEND_XCM.with(|q| *q.borrow()) { - return Err(SendError::Transport("Intentional send failure used in tests")) + return Err(SendError::Transport("Intentional send failure used in tests")); } let pair = (dest.take().unwrap(), msg.take().unwrap()); Ok((pair, Assets::new())) @@ -241,7 +241,7 @@ parameter_types! { pub const BlockHashCount: u64 = 250; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; @@ -361,6 +361,10 @@ parameter_types! { 0, [Parachain(FOREIGN_ASSET_RESERVE_PARA_ID)] ); + pub PaidParaForeignReserveLocation: Location = Location::new( + 0, + [Parachain(Para3000::get())] + ); pub ForeignAsset: Asset = Asset { fun: Fungible(10), id: AssetId(Location::new( @@ -368,6 +372,13 @@ parameter_types! { [Parachain(FOREIGN_ASSET_RESERVE_PARA_ID), FOREIGN_ASSET_INNER_JUNCTION], )), }; + pub PaidParaForeignAsset: Asset = Asset { + fun: Fungible(10), + id: AssetId(Location::new( + 0, + [Parachain(Para3000::get())], + )), + }; pub UsdcReserveLocation: Location = Location::new( 0, [Parachain(USDC_RESERVE_PARA_ID)] @@ -450,6 +461,8 @@ parameter_types! { pub TrustedFilteredTeleport: (AssetFilter, Location) = (FilteredTeleportAsset::get().into(), FilteredTeleportLocation::get()); pub TeleportUsdtToForeign: (AssetFilter, Location) = (Usdt::get().into(), ForeignReserveLocation::get()); pub TrustedForeign: (AssetFilter, Location) = (ForeignAsset::get().into(), ForeignReserveLocation::get()); + pub TrustedPaidParaForeign: (AssetFilter, Location) = (PaidParaForeignAsset::get().into(), PaidParaForeignReserveLocation::get()); + pub TrustedUsdc: (AssetFilter, Location) = (Usdc::get().into(), UsdcReserveLocation::get()); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; @@ -483,7 +496,7 @@ impl xcm_executor::Config for XcmConfig { type XcmSender = XcmRouter; type AssetTransactor = AssetTransactors; type OriginConverter = LocalOriginConverter; - type IsReserve = (Case, Case); + type IsReserve = (Case, Case, Case); type IsTeleporter = ( Case, Case, @@ -513,6 +526,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } pub type LocalOriginToLocation = SignedToAccountId32; @@ -563,8 +579,30 @@ impl pallet_test_notifier::Config for Test { type RuntimeCall = RuntimeCall; } +#[cfg(feature = "runtime-benchmarks")] +pub struct TestDeliveryHelper; +#[cfg(feature = "runtime-benchmarks")] +impl xcm_builder::EnsureDelivery for TestDeliveryHelper { + fn ensure_successful_delivery( + origin_ref: &Location, + _dest: &Location, + _fee_reason: xcm_executor::traits::FeeReason, + ) -> (Option, Option) { + use xcm_executor::traits::ConvertLocation; + let account = SovereignAccountOf::convert_location(origin_ref).expect("Valid location"); + // Give the existential deposit at least + let balance = ExistentialDeposit::get(); + let _ = >::make_free_balance_be( + &account, balance, + ); + (None, None) + } +} + #[cfg(feature = "runtime-benchmarks")] impl super::benchmarking::Config for Test { + type DeliveryHelper = TestDeliveryHelper; + fn reachable_dest() -> Option { Some(Parachain(1000).into()) } @@ -632,6 +670,10 @@ impl super::benchmarking::Config for Test { }); Some((assets, fee_index as u32, dest, verify)) } + + fn get_asset() -> Asset { + Asset { id: AssetId(Location::here()), fun: Fungible(ExistentialDeposit::get()) } + } } pub(crate) fn last_event() -> RuntimeEvent { diff --git a/polkadot/xcm/pallet-xcm/src/tests/assets_transfer.rs b/polkadot/xcm/pallet-xcm/src/tests/assets_transfer.rs index 27be5cce145859a999c9e45ec2ca4aa32de3c641..92d23cfd281739ccc968be7229d032f6c1a3687a 100644 --- a/polkadot/xcm/pallet-xcm/src/tests/assets_transfer.rs +++ b/polkadot/xcm/pallet-xcm/src/tests/assets_transfer.rs @@ -248,7 +248,7 @@ fn reserve_transfer_assets_with_paid_router_works() { pub(crate) fn set_up_foreign_asset( reserve_para_id: u32, inner_junction: Option, - benficiary: AccountId, + beneficiary: AccountId, initial_amount: u128, is_sufficient: bool, ) -> (Location, AccountId, Location) { @@ -276,7 +276,7 @@ pub(crate) fn set_up_foreign_asset( assert_ok!(AssetsPallet::mint( RuntimeOrigin::signed(BOB), foreign_asset_id_location.clone(), - benficiary, + beneficiary, initial_amount )); @@ -2604,3 +2604,174 @@ fn teleport_assets_using_destination_reserve_fee_disallowed() { expected_result, ); } + +/// Test `tested_call` transferring single asset using remote reserve. +/// +/// Transferring Para3000 asset (`Para3000` reserve) to +/// `OTHER_PARA_ID` (no teleport trust), therefore triggering remote reserve. +/// Using the same asset asset (Para3000 reserve) for fees. +/// +/// Asserts that the sender's balance is decreased and the beneficiary's balance +/// is increased. Verifies the correct message is sent and event is emitted. +/// +/// Verifies that XCM router fees (`SendXcm::validate` -> `Assets`) are withdrawn from correct +/// user account and deposited to a correct target account (`XcmFeesTargetAccount`). +/// Verifies `expected_result`. +fn remote_asset_reserve_and_remote_fee_reserve_paid_call( + tested_call: Call, + expected_result: DispatchResult, +) where + Call: FnOnce( + OriginFor, + Box, + Box, + Box, + u32, + WeightLimit, + ) -> DispatchResult, +{ + let weight = BaseXcmWeight::get() * 3; + let user_account = AccountId::from(XCM_FEES_NOT_WAIVED_USER_ACCOUNT); + let xcm_router_fee_amount = Para3000PaymentAmount::get(); + let paid_para_id = Para3000::get(); + let balances = vec![ + (user_account.clone(), INITIAL_BALANCE), + (ParaId::from(paid_para_id).into_account_truncating(), INITIAL_BALANCE), + (XcmFeesTargetAccount::get(), INITIAL_BALANCE), + ]; + let beneficiary: Location = Junction::AccountId32 { network: None, id: ALICE.into() }.into(); + new_test_ext_with_balances(balances).execute_with(|| { + // create sufficient foreign asset BLA + let foreign_initial_amount = 142; + let (reserve_location, _, foreign_asset_id_location) = set_up_foreign_asset( + paid_para_id, + None, + user_account.clone(), + foreign_initial_amount, + true, + ); + + // transfer destination is another chain that is not the reserve location + // the goal is to trigger the remoteReserve case + let dest = RelayLocation::get().pushed_with_interior(Parachain(OTHER_PARA_ID)).unwrap(); + + let transferred_asset: Assets = (foreign_asset_id_location.clone(), SEND_AMOUNT).into(); + + // balances checks before + assert_eq!( + AssetsPallet::balance(foreign_asset_id_location.clone(), user_account.clone()), + foreign_initial_amount + ); + assert_eq!(Balances::free_balance(user_account.clone()), INITIAL_BALANCE); + + // do the transfer + let result = tested_call( + RuntimeOrigin::signed(user_account.clone()), + Box::new(dest.clone().into()), + Box::new(beneficiary.clone().into()), + Box::new(transferred_asset.into()), + 0 as u32, + Unlimited, + ); + assert_eq!(result, expected_result); + if expected_result.is_err() { + // short-circuit here for tests where we expect failure + return; + } + + let mut last_events = last_events(7).into_iter(); + // asset events + // forceCreate + last_events.next().unwrap(); + // mint tokens + last_events.next().unwrap(); + // burn tokens + last_events.next().unwrap(); + // balance events + // burn delivery fee + last_events.next().unwrap(); + // mint delivery fee + last_events.next().unwrap(); + assert_eq!( + last_events.next().unwrap(), + RuntimeEvent::XcmPallet(crate::Event::Attempted { + outcome: Outcome::Complete { used: weight } + }) + ); + + // user account spent (transferred) amount + assert_eq!( + AssetsPallet::balance(foreign_asset_id_location.clone(), user_account.clone()), + foreign_initial_amount - SEND_AMOUNT + ); + + // user account spent delivery fees + assert_eq!(Balances::free_balance(user_account), INITIAL_BALANCE - xcm_router_fee_amount); + + // XcmFeesTargetAccount where should lend xcm_router_fee_amount + assert_eq!( + Balances::free_balance(XcmFeesTargetAccount::get()), + INITIAL_BALANCE + xcm_router_fee_amount + ); + + // Verify total and active issuance of foreign BLA have decreased (burned on + // reserve-withdraw) + let expected_issuance = foreign_initial_amount - SEND_AMOUNT; + assert_eq!( + AssetsPallet::total_issuance(foreign_asset_id_location.clone()), + expected_issuance + ); + assert_eq!( + AssetsPallet::active_issuance(foreign_asset_id_location.clone()), + expected_issuance + ); + + let context = UniversalLocation::get(); + let foreign_id_location_reanchored = + foreign_asset_id_location.reanchored(&dest, &context).unwrap(); + let dest_reanchored = dest.reanchored(&reserve_location, &context).unwrap(); + + // Verify sent XCM program + assert_eq!( + sent_xcm(), + vec![( + reserve_location, + // `assets` are burned on source and withdrawn from SA in remote reserve chain + Xcm(vec![ + WithdrawAsset((Location::here(), SEND_AMOUNT).into()), + ClearOrigin, + buy_execution((Location::here(), SEND_AMOUNT / 2)), + DepositReserveAsset { + assets: Wild(AllCounted(1)), + // final destination is `dest` as seen by `reserve` + dest: dest_reanchored, + // message sent onward to `dest` + xcm: Xcm(vec![ + buy_execution((foreign_id_location_reanchored, SEND_AMOUNT / 2)), + DepositAsset { assets: AllCounted(1).into(), beneficiary } + ]) + } + ]) + )] + ); + }); +} +/// Test `transfer_assets` with remote asset reserve and remote fee reserve. +#[test] +fn transfer_assets_with_remote_asset_reserve_and_remote_asset_fee_reserve_paid_works() { + let expected_result = Ok(()); + remote_asset_reserve_and_remote_fee_reserve_paid_call( + XcmPallet::transfer_assets, + expected_result, + ); +} +/// Test `limited_reserve_transfer_assets` with remote asset reserve and remote fee reserve. +#[test] +fn limited_reserve_transfer_assets_with_remote_asset_reserve_and_remote_asset_fee_reserve_paid_works( +) { + let expected_result = Ok(()); + remote_asset_reserve_and_remote_fee_reserve_paid_call( + XcmPallet::limited_reserve_transfer_assets, + expected_result, + ); +} diff --git a/polkadot/xcm/pallet-xcm/src/tests/mod.rs b/polkadot/xcm/pallet-xcm/src/tests/mod.rs index 28c7d197443b070423bfb694593c6feb0d471534..763d768e154a50428e085c6699a89805c29b402c 100644 --- a/polkadot/xcm/pallet-xcm/src/tests/mod.rs +++ b/polkadot/xcm/pallet-xcm/src/tests/mod.rs @@ -20,10 +20,10 @@ pub(crate) mod assets_transfer; use crate::{ mock::*, pallet::SupportedVersion, AssetTraps, Config, CurrentMigration, Error, - ExecuteControllerWeightInfo, LatestVersionedLocation, Pallet, Queries, QueryStatus, - VersionDiscoveryQueue, VersionMigrationStage, VersionNotifiers, VersionNotifyTargets, - WeightInfo, + LatestVersionedLocation, Pallet, Queries, QueryStatus, VersionDiscoveryQueue, + VersionMigrationStage, VersionNotifiers, VersionNotifyTargets, WeightInfo, }; +use codec::Encode; use frame_support::{ assert_err_ignore_postinfo, assert_noop, assert_ok, traits::{Currency, Hooks}, @@ -305,11 +305,12 @@ fn send_works() { ]); let versioned_dest = Box::new(RelayLocation::get().into()); - let versioned_message = Box::new(VersionedXcm::from(message.clone())); - assert_ok!(XcmPallet::send( + let versioned_message = VersionedXcm::from(message.clone()); + let encoded_versioned_message = versioned_message.encode().try_into().unwrap(); + assert_ok!(XcmPallet::send_blob( RuntimeOrigin::signed(ALICE), versioned_dest, - versioned_message + encoded_versioned_message )); let sent_message = Xcm(Some(DescendOrigin(sender.clone().try_into().unwrap())) .into_iter() @@ -341,16 +342,16 @@ fn send_fails_when_xcm_router_blocks() { ]; new_test_ext_with_balances(balances).execute_with(|| { let sender: Location = Junction::AccountId32 { network: None, id: ALICE.into() }.into(); - let message = Xcm(vec![ + let message = Xcm::<()>(vec![ ReserveAssetDeposited((Parent, SEND_AMOUNT).into()), buy_execution((Parent, SEND_AMOUNT)), DepositAsset { assets: AllCounted(1).into(), beneficiary: sender }, ]); assert_noop!( - XcmPallet::send( + XcmPallet::send_blob( RuntimeOrigin::signed(ALICE), Box::new(Location::ancestor(8).into()), - Box::new(VersionedXcm::from(message.clone())), + VersionedXcm::from(message.clone()).encode().try_into().unwrap(), ), crate::Error::::SendFailure ); @@ -371,13 +372,16 @@ fn execute_withdraw_to_deposit_works() { let weight = BaseXcmWeight::get() * 3; let dest: Location = Junction::AccountId32 { network: None, id: BOB.into() }.into(); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); - assert_ok!(XcmPallet::execute( + assert_ok!(XcmPallet::execute_blob( RuntimeOrigin::signed(ALICE), - Box::new(VersionedXcm::from(Xcm(vec![ + VersionedXcm::from(Xcm::(vec![ WithdrawAsset((Here, SEND_AMOUNT).into()), buy_execution((Here, SEND_AMOUNT)), DepositAsset { assets: AllCounted(1).into(), beneficiary: dest }, - ]))), + ])) + .encode() + .try_into() + .unwrap(), weight )); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT); @@ -399,18 +403,21 @@ fn trapped_assets_can_be_claimed() { let weight = BaseXcmWeight::get() * 6; let dest: Location = Junction::AccountId32 { network: None, id: BOB.into() }.into(); - assert_ok!(XcmPallet::execute( + assert_ok!(XcmPallet::execute_blob( RuntimeOrigin::signed(ALICE), - Box::new(VersionedXcm::from(Xcm(vec![ + VersionedXcm::from(Xcm(vec![ WithdrawAsset((Here, SEND_AMOUNT).into()), buy_execution((Here, SEND_AMOUNT)), // Don't propagated the error into the result. - SetErrorHandler(Xcm(vec![ClearError])), + SetErrorHandler(Xcm::(vec![ClearError])), // This will make an error. Trap(0), // This would succeed, but we never get to it. DepositAsset { assets: AllCounted(1).into(), beneficiary: dest.clone() }, - ]))), + ])) + .encode() + .try_into() + .unwrap(), weight )); let source: Location = Junction::AccountId32 { network: None, id: ALICE.into() }.into(); @@ -437,13 +444,16 @@ fn trapped_assets_can_be_claimed() { assert_eq!(trapped, expected); let weight = BaseXcmWeight::get() * 3; - assert_ok!(XcmPallet::execute( + assert_ok!(XcmPallet::execute_blob( RuntimeOrigin::signed(ALICE), - Box::new(VersionedXcm::from(Xcm(vec![ + VersionedXcm::from(Xcm::(vec![ ClaimAsset { assets: (Here, SEND_AMOUNT).into(), ticket: Here.into() }, buy_execution((Here, SEND_AMOUNT)), DepositAsset { assets: AllCounted(1).into(), beneficiary: dest.clone() }, - ]))), + ])) + .encode() + .try_into() + .unwrap(), weight )); @@ -453,13 +463,16 @@ fn trapped_assets_can_be_claimed() { // Can't claim twice. assert_err_ignore_postinfo!( - XcmPallet::execute( + XcmPallet::execute_blob( RuntimeOrigin::signed(ALICE), - Box::new(VersionedXcm::from(Xcm(vec![ + VersionedXcm::from(Xcm::(vec![ ClaimAsset { assets: (Here, SEND_AMOUNT).into(), ticket: Here.into() }, buy_execution((Here, SEND_AMOUNT)), DepositAsset { assets: AllCounted(1).into(), beneficiary: dest }, - ]))), + ])) + .encode() + .try_into() + .unwrap(), weight ), Error::::LocalExecutionIncomplete @@ -467,6 +480,58 @@ fn trapped_assets_can_be_claimed() { }); } +// Like `trapped_assets_can_be_claimed` but using the `claim_assets` extrinsic. +#[test] +fn claim_assets_works() { + let balances = vec![(ALICE, INITIAL_BALANCE)]; + new_test_ext_with_balances(balances).execute_with(|| { + // First trap some assets. + let trapping_program = Xcm::::builder_unsafe() + .withdraw_asset((Here, SEND_AMOUNT).into()) + .build(); + // Even though assets are trapped, the extrinsic returns success. + assert_ok!(XcmPallet::execute_blob( + RuntimeOrigin::signed(ALICE), + VersionedXcm::V4(trapping_program).encode().try_into().unwrap(), + BaseXcmWeight::get() * 2, + )); + assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT); + + // Expected `AssetsTrapped` event info. + let source: Location = Junction::AccountId32 { network: None, id: ALICE.into() }.into(); + let versioned_assets = VersionedAssets::V4(Assets::from((Here, SEND_AMOUNT))); + let hash = BlakeTwo256::hash_of(&(source.clone(), versioned_assets.clone())); + + // Assets were indeed trapped. + assert_eq!( + last_events(2), + vec![ + RuntimeEvent::XcmPallet(crate::Event::AssetsTrapped { + hash, + origin: source, + assets: versioned_assets + }), + RuntimeEvent::XcmPallet(crate::Event::Attempted { + outcome: Outcome::Complete { used: BaseXcmWeight::get() * 1 } + }) + ], + ); + let trapped = AssetTraps::::iter().collect::>(); + assert_eq!(trapped, vec![(hash, 1)]); + + // Now claim them with the extrinsic. + assert_ok!(XcmPallet::claim_assets( + RuntimeOrigin::signed(ALICE), + Box::new(VersionedAssets::V4((Here, SEND_AMOUNT).into())), + Box::new(VersionedLocation::V4( + AccountId32 { network: None, id: ALICE.clone().into() }.into() + )), + )); + assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); + assert_eq!(AssetTraps::::iter().collect::>(), vec![]); + }); +} + /// Test failure to complete execution reverts intermediate side-effects. /// /// XCM program will withdraw and deposit some assets, then fail execution of a further withdraw. @@ -480,9 +545,9 @@ fn incomplete_execute_reverts_side_effects() { assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); let amount_to_send = INITIAL_BALANCE - ExistentialDeposit::get(); let assets: Assets = (Here, amount_to_send).into(); - let result = XcmPallet::execute( + let result = XcmPallet::execute_blob( RuntimeOrigin::signed(ALICE), - Box::new(VersionedXcm::from(Xcm(vec![ + VersionedXcm::from(Xcm::(vec![ // Withdraw + BuyExec + Deposit should work WithdrawAsset(assets.clone()), buy_execution(assets.inner()[0].clone()), @@ -490,7 +555,10 @@ fn incomplete_execute_reverts_side_effects() { // Withdrawing once more will fail because of InsufficientBalance, and we expect to // revert the effects of the above instructions as well WithdrawAsset(assets), - ]))), + ])) + .encode() + .try_into() + .unwrap(), weight, ); // all effects are reverted and balances unchanged for either sender or receiver @@ -502,7 +570,7 @@ fn incomplete_execute_reverts_side_effects() { Err(sp_runtime::DispatchErrorWithPostInfo { post_info: frame_support::dispatch::PostDispatchInfo { actual_weight: Some( - as ExecuteControllerWeightInfo>::execute() + weight + <::WeightInfo>::execute_blob() + weight ), pays_fee: frame_support::dispatch::Pays::Yes, }, diff --git a/polkadot/xcm/src/lib.rs b/polkadot/xcm/src/lib.rs index c90ad25c0d83d0bf155a766cf341d5b646e76800..e836486e86e3068b82c3f3a5905836c7704e09f7 100644 --- a/polkadot/xcm/src/lib.rs +++ b/polkadot/xcm/src/lib.rs @@ -48,6 +48,9 @@ mod tests; /// Maximum nesting level for XCM decoding. pub const MAX_XCM_DECODE_DEPTH: u32 = 8; +/// Maximum encoded size. +/// See `decoding_respects_limit` test for more reasoning behind this value. +pub const MAX_XCM_ENCODED_SIZE: u32 = 12402; /// A version of XCM. pub type Version = u32; @@ -165,6 +168,15 @@ macro_rules! versioned_type { <$v3>::max_encoded_len() } } + impl IdentifyVersion for $n { + fn identify_version(&self) -> Version { + use $n::*; + match self { + V3(_) => v3::VERSION, + V4(_) => v4::VERSION, + } + } + } }; ($(#[$attr:meta])* pub enum $n:ident { @@ -287,6 +299,16 @@ macro_rules! versioned_type { <$v3>::max_encoded_len() } } + impl IdentifyVersion for $n { + fn identify_version(&self) -> Version { + use $n::*; + match self { + V2(_) => v2::VERSION, + V3(_) => v3::VERSION, + V4(_) => v4::VERSION, + } + } + } }; } @@ -424,6 +446,16 @@ impl IntoVersion for VersionedXcm { } } +impl IdentifyVersion for VersionedXcm { + fn identify_version(&self) -> Version { + match self { + Self::V2(_) => v2::VERSION, + Self::V3(_) => v3::VERSION, + Self::V4(_) => v4::VERSION, + } + } +} + impl From> for VersionedXcm { fn from(x: v2::Xcm) -> Self { VersionedXcm::V2(x) @@ -493,6 +525,12 @@ pub trait WrapVersion { ) -> Result, ()>; } +/// Used to get the version out of a versioned type. +// TODO(XCMv5): This could be `GetVersion` and we change the current one to `GetVersionFor`. +pub trait IdentifyVersion { + fn identify_version(&self) -> Version; +} + /// Check and return the `Version` that should be used for the `Xcm` datum for the destination /// `Location`, which will interpret it. pub trait GetVersion { @@ -572,9 +610,9 @@ pub type AlwaysLts = AlwaysV4; pub mod prelude { pub use super::{ latest::prelude::*, AlwaysLatest, AlwaysLts, AlwaysV2, AlwaysV3, AlwaysV4, GetVersion, - IntoVersion, Unsupported, Version as XcmVersion, VersionedAsset, VersionedAssetId, - VersionedAssets, VersionedInteriorLocation, VersionedLocation, VersionedResponse, - VersionedXcm, WrapVersion, + IdentifyVersion, IntoVersion, Unsupported, Version as XcmVersion, VersionedAsset, + VersionedAssetId, VersionedAssets, VersionedInteriorLocation, VersionedLocation, + VersionedResponse, VersionedXcm, WrapVersion, }; } diff --git a/polkadot/xcm/src/v2/multilocation.rs b/polkadot/xcm/src/v2/multilocation.rs index 60aa1f6ceadf0fefb991660393dc0cdc91f07700..ac98da8d08c910160a099e92eeef3eacd7e56513 100644 --- a/polkadot/xcm/src/v2/multilocation.rs +++ b/polkadot/xcm/src/v2/multilocation.rs @@ -176,7 +176,7 @@ impl MultiLocation { } /// Consumes `self` and returns a `MultiLocation` suffixed with `new`, or an `Err` with - /// theoriginal value of `self` in case of overflow. + /// the original value of `self` in case of overflow. pub fn pushed_with_interior(self, new: Junction) -> result::Result { match self.interior.pushed_with(new) { Ok(i) => Ok(MultiLocation { interior: i, parents: self.parents }), diff --git a/polkadot/xcm/src/v3/multilocation.rs b/polkadot/xcm/src/v3/multilocation.rs index c588b924ac70842b72678d141a67e4279563f30a..18fe01ec8fa7da6eb1db44ce961098c00a2ac13b 100644 --- a/polkadot/xcm/src/v3/multilocation.rs +++ b/polkadot/xcm/src/v3/multilocation.rs @@ -205,7 +205,7 @@ impl MultiLocation { } /// Consumes `self` and returns a `MultiLocation` suffixed with `new`, or an `Err` with - /// theoriginal value of `self` in case of overflow. + /// the original value of `self` in case of overflow. pub fn pushed_with_interior( self, new: impl Into, diff --git a/polkadot/xcm/src/v4/location.rs b/polkadot/xcm/src/v4/location.rs index db55c3d3034ce3f09b7f23c8acd8a69efbc6afc2..0e7c69864fa33c3ecca592bcfcc4d250b1b16a95 100644 --- a/polkadot/xcm/src/v4/location.rs +++ b/polkadot/xcm/src/v4/location.rs @@ -210,7 +210,7 @@ impl Location { } /// Consumes `self` and returns a `Location` suffixed with `new`, or an `Err` with - /// theoriginal value of `self` in case of overflow. + /// the original value of `self` in case of overflow. pub fn pushed_with_interior( self, new: impl Into, diff --git a/polkadot/xcm/src/v4/mod.rs b/polkadot/xcm/src/v4/mod.rs index 30ee485589a28191e61fc351eda0f91d2773d589..6635408282e4849861a62be955e52d2fc4dd1b87 100644 --- a/polkadot/xcm/src/v4/mod.rs +++ b/polkadot/xcm/src/v4/mod.rs @@ -1488,7 +1488,21 @@ mod tests { let encoded = big_xcm.encode(); assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err()); - let nested_xcm = Xcm::<()>(vec![ + let mut many_assets = Assets::new(); + for index in 0..MAX_ITEMS_IN_ASSETS { + many_assets.push((GeneralIndex(index as u128), 1u128).into()); + } + + let full_xcm_pass = + Xcm::<()>(vec![ + TransferAsset { assets: many_assets, beneficiary: Here.into() }; + MAX_INSTRUCTIONS_TO_DECODE as usize + ]); + let encoded = full_xcm_pass.encode(); + assert_eq!(encoded.len(), 12402); + assert!(Xcm::<()>::decode(&mut &encoded[..]).is_ok()); + + let nested_xcm_fail = Xcm::<()>(vec![ DepositReserveAsset { assets: All.into(), dest: Here.into(), @@ -1496,10 +1510,10 @@ mod tests { }; (MAX_INSTRUCTIONS_TO_DECODE / 2) as usize ]); - let encoded = nested_xcm.encode(); + let encoded = nested_xcm_fail.encode(); assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err()); - let even_more_nested_xcm = Xcm::<()>(vec![SetAppendix(nested_xcm); 64]); + let even_more_nested_xcm = Xcm::<()>(vec![SetAppendix(nested_xcm_fail); 64]); let encoded = even_more_nested_xcm.encode(); assert_eq!(encoded.len(), 342530); // This should not decode since the limit is 100 diff --git a/polkadot/xcm/xcm-builder/src/barriers.rs b/polkadot/xcm/xcm-builder/src/barriers.rs index 80411ab5a2246bf8c7ba96c215b685f5e90aadaf..b8923a4d5c6e88d9034536fa4dacf21c47e9108b 100644 --- a/polkadot/xcm/xcm-builder/src/barriers.rs +++ b/polkadot/xcm/xcm-builder/src/barriers.rs @@ -142,7 +142,7 @@ impl> ShouldExecute for AllowTopLevelPaidExecutionFrom /// In the above example, `AllowUnpaidExecutionFrom` appears once underneath /// `WithComputedOrigin`. This is in order to distinguish between messages which are notionally /// from a derivative location of `ParentLocation` but that just happened to be sent via -/// `ParentLocaction` rather than messages that were sent by the parent. +/// `ParentLocation` rather than messages that were sent by the parent. /// /// Similarly `AllowTopLevelPaidExecutionFrom` appears twice: once inside of `WithComputedOrigin` /// where we provide the list of origins which are derivative origins, and then secondly outside diff --git a/polkadot/xcm/xcm-builder/src/controller.rs b/polkadot/xcm/xcm-builder/src/controller.rs index ba2b1fb44b8eeb0916fdec24d97e36121bc18c8a..6bdde2a967de9195f62fa59544fc3c254b51796b 100644 --- a/polkadot/xcm/xcm-builder/src/controller.rs +++ b/polkadot/xcm/xcm-builder/src/controller.rs @@ -21,6 +21,7 @@ use frame_support::{ dispatch::{DispatchErrorWithPostInfo, WithPostDispatchInfo}, pallet_prelude::DispatchError, + parameter_types, BoundedVec, }; use sp_std::boxed::Box; use xcm::prelude::*; @@ -41,8 +42,12 @@ impl Controller f /// Weight functions needed for [`ExecuteController`]. pub trait ExecuteControllerWeightInfo { - /// Weight for [`ExecuteController::execute`] - fn execute() -> Weight; + /// Weight for [`ExecuteController::execute_blob`] + fn execute_blob() -> Weight; +} + +parameter_types! { + pub const MaxXcmEncodedSize: u32 = xcm::MAX_XCM_ENCODED_SIZE; } /// Execute an XCM locally, for a given origin. @@ -61,19 +66,19 @@ pub trait ExecuteController { /// # Parameters /// /// - `origin`: the origin of the call. - /// - `message`: the XCM program to be executed. + /// - `msg`: the encoded XCM to be executed, should be decodable as a [`VersionedXcm`] /// - `max_weight`: the maximum weight that can be consumed by the execution. - fn execute( + fn execute_blob( origin: Origin, - message: Box>, + message: BoundedVec, max_weight: Weight, ) -> Result; } /// Weight functions needed for [`SendController`]. pub trait SendControllerWeightInfo { - /// Weight for [`SendController::send`] - fn send() -> Weight; + /// Weight for [`SendController::send_blob`] + fn send_blob() -> Weight; } /// Send an XCM from a given origin. @@ -93,11 +98,11 @@ pub trait SendController { /// /// - `origin`: the origin of the call. /// - `dest`: the destination of the message. - /// - `msg`: the XCM to be sent. - fn send( + /// - `msg`: the encoded XCM to be sent, should be decodable as a [`VersionedXcm`] + fn send_blob( origin: Origin, dest: Box, - message: Box>, + message: BoundedVec, ) -> Result; } @@ -132,40 +137,40 @@ pub trait QueryController: QueryHandler { origin: Origin, timeout: Timeout, match_querier: VersionedLocation, - ) -> Result; + ) -> Result; } impl ExecuteController for () { type WeightInfo = (); - fn execute( + fn execute_blob( _origin: Origin, - _message: Box>, + _message: BoundedVec, _max_weight: Weight, ) -> Result { - Err(DispatchError::Other("ExecuteController::execute not implemented") + Err(DispatchError::Other("ExecuteController::execute_blob not implemented") .with_weight(Weight::zero())) } } impl ExecuteControllerWeightInfo for () { - fn execute() -> Weight { + fn execute_blob() -> Weight { Weight::zero() } } impl SendController for () { type WeightInfo = (); - fn send( + fn send_blob( _origin: Origin, _dest: Box, - _message: Box>, + _message: BoundedVec, ) -> Result { Ok(Default::default()) } } impl SendControllerWeightInfo for () { - fn send() -> Weight { + fn send_blob() -> Weight { Weight::zero() } } @@ -186,7 +191,7 @@ impl QueryController for () { _origin: Origin, _timeout: Timeout, _match_querier: VersionedLocation, - ) -> Result { + ) -> Result { Ok(Default::default()) } } diff --git a/polkadot/xcm/xcm-builder/src/currency_adapter.rs b/polkadot/xcm/xcm-builder/src/currency_adapter.rs index fe26b7319bb18b2d4ad33774d389afd0f834eb50..24261ac06583c4ca284f52071a4ee80afae2a6ec 100644 --- a/polkadot/xcm/xcm-builder/src/currency_adapter.rs +++ b/polkadot/xcm/xcm-builder/src/currency_adapter.rs @@ -170,7 +170,7 @@ impl< } fn can_check_out(_dest: &Location, what: &Asset, _context: &XcmContext) -> Result { - log::trace!(target: "xcm::currency_adapter", "check_out dest: {:?}, what: {:?}", _dest, what); + log::trace!(target: "xcm::currency_adapter", "can_check_out dest: {:?}, what: {:?}", _dest, what); let amount = Matcher::matches_fungible(what).ok_or(Error::AssetNotHandled)?; match CheckedAccount::get() { Some((checked_account, MintLocation::Local)) => diff --git a/polkadot/xcm/xcm-builder/src/fungible_adapter.rs b/polkadot/xcm/xcm-builder/src/fungible_adapter.rs index 33b766c8560cef798b61d564f1910ff77b2c10eb..21c828922b333e85e9332d16c768aef0d34cbd9f 100644 --- a/polkadot/xcm/xcm-builder/src/fungible_adapter.rs +++ b/polkadot/xcm/xcm-builder/src/fungible_adapter.rs @@ -151,7 +151,7 @@ impl< fn can_check_out(_dest: &Location, what: &Asset, _context: &XcmContext) -> XcmResult { log::trace!( target: "xcm::fungible_adapter", - "check_out dest: {:?}, what: {:?}", + "can_check_out dest: {:?}, what: {:?}", _dest, what ); diff --git a/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs b/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs index 4574d5ed4c682c7bb62b6f20b6f9397dc37e4098..b4c418ebf1c9cf7d9d26540e998f966665993585 100644 --- a/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs +++ b/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs @@ -235,7 +235,7 @@ impl< fn can_check_out(_origin: &Location, what: &Asset, _context: &XcmContext) -> XcmResult { log::trace!( target: "xcm::fungibles_adapter", - "can_check_in origin: {:?}, what: {:?}", + "can_check_out origin: {:?}, what: {:?}", _origin, what ); // Check we handle this asset. diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs index 42522c64d8a4da77cc676e6a338d9f019ef707a5..46d0ad227bfdf8e5e77188165fc259b0c1aec585 100644 --- a/polkadot/xcm/xcm-builder/src/lib.rs +++ b/polkadot/xcm/xcm-builder/src/lib.rs @@ -43,7 +43,7 @@ pub use barriers::{ mod controller; pub use controller::{ - Controller, ExecuteController, ExecuteControllerWeightInfo, QueryController, + Controller, ExecuteController, ExecuteControllerWeightInfo, MaxXcmEncodedSize, QueryController, QueryControllerWeightInfo, QueryHandler, SendController, SendControllerWeightInfo, }; @@ -117,7 +117,7 @@ mod process_xcm_message; pub use process_xcm_message::ProcessXcmMessage; mod routing; -pub use routing::{WithTopicSource, WithUniqueTopic}; +pub use routing::{EnsureDelivery, WithTopicSource, WithUniqueTopic}; mod transactional; pub use transactional::FrameTransactionalProcessor; diff --git a/polkadot/xcm/xcm-builder/src/pay.rs b/polkadot/xcm/xcm-builder/src/pay.rs index 6b466483cfad70e52e841b39ef44ebb7362b17cf..35b624b041539e3d89fdb98a56d0910d5033417f 100644 --- a/polkadot/xcm/xcm-builder/src/pay.rs +++ b/polkadot/xcm/xcm-builder/src/pay.rs @@ -88,7 +88,7 @@ impl< type Beneficiary = Beneficiary; type AssetKind = AssetKind; type Balance = u128; - type Id = Querier::QueryId; + type Id = QueryId; type Error = xcm::latest::Error; fn pay( diff --git a/polkadot/xcm/xcm-builder/src/routing.rs b/polkadot/xcm/xcm-builder/src/routing.rs index 9c0302baee06b81ec662bc3a0e25eb9308fc0a3a..529ef80c15ff11d3c0d2629aeb4fa9506cb37a28 100644 --- a/polkadot/xcm/xcm-builder/src/routing.rs +++ b/polkadot/xcm/xcm-builder/src/routing.rs @@ -20,6 +20,7 @@ use frame_system::unique; use parity_scale_codec::Encode; use sp_std::{marker::PhantomData, result::Result}; use xcm::prelude::*; +use xcm_executor::{traits::FeeReason, FeesMode}; /// Wrapper router which, if the message does not already end with a `SetTopic` instruction, /// appends one to the message filled with a universally unique ID. This ID is returned from a @@ -104,3 +105,37 @@ impl SendXcm for WithTopicSource (Option, Option); +} + +/// Tuple implementation for `EnsureDelivery`. +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl EnsureDelivery for Tuple { + fn ensure_successful_delivery( + origin_ref: &Location, + dest: &Location, + fee_reason: FeeReason, + ) -> (Option, Option) { + for_tuples!( #( + // If the implementation returns something, we're done; if not, let others try. + match Tuple::ensure_successful_delivery(origin_ref, dest, fee_reason.clone()) { + r @ (Some(_), Some(_)) | r @ (Some(_), None) | r @ (None, Some(_)) => return r, + (None, None) => (), + } + )* ); + // doing nothing + (None, None) + } +} diff --git a/polkadot/xcm/xcm-builder/src/tests/mock.rs b/polkadot/xcm/xcm-builder/src/tests/mock.rs index 4521d5e92a42ba3c370e0de6c05b1fa6da158ce6..3d03ab054248d907c89fe0f7a9d2fbb21896ace2 100644 --- a/polkadot/xcm/xcm-builder/src/tests/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/mock.rs @@ -38,7 +38,7 @@ pub use sp_std::{ collections::{btree_map::BTreeMap, btree_set::BTreeSet}, fmt::Debug, }; -pub use xcm::latest::{prelude::*, Weight}; +pub use xcm::latest::{prelude::*, QueryId, Weight}; use xcm_executor::traits::{Properties, QueryHandler, QueryResponseStatus}; pub use xcm_executor::{ traits::{ @@ -414,7 +414,6 @@ pub struct TestQueryHandler(core::marker::PhantomData<(T, BlockN impl QueryHandler for TestQueryHandler { - type QueryId = u64; type BlockNumber = BlockNumber; type Error = XcmError; type UniversalLocation = T::UniversalLocation; @@ -423,7 +422,7 @@ impl QueryHandler responder: impl Into, _timeout: Self::BlockNumber, _match_querier: impl Into, - ) -> Self::QueryId { + ) -> QueryId { let query_id = 1; expect_response(query_id, responder.into()); query_id @@ -433,7 +432,7 @@ impl QueryHandler message: &mut Xcm<()>, responder: impl Into, timeout: Self::BlockNumber, - ) -> Result { + ) -> Result { let responder = responder.into(); let destination = Self::UniversalLocation::get() .invert_target(&responder) @@ -445,7 +444,7 @@ impl QueryHandler Ok(query_id) } - fn take_response(query_id: Self::QueryId) -> QueryResponseStatus { + fn take_response(query_id: QueryId) -> QueryResponseStatus { QUERIES .with(|q| { q.borrow().get(&query_id).and_then(|v| match v { @@ -460,7 +459,7 @@ impl QueryHandler } #[cfg(feature = "runtime-benchmarks")] - fn expect_response(_id: Self::QueryId, _response: xcm::latest::Response) { + fn expect_response(_id: QueryId, _response: xcm::latest::Response) { // Unnecessary since it's only a test implementation } } @@ -743,6 +742,9 @@ impl Config for TestConfig { type SafeCallFilter = Everything; type Aliasers = AliasForeignAccountId32; type TransactionalProcessor = (); + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } pub fn fungible_multi_asset(location: Location, amount: u128) -> Asset { diff --git a/polkadot/xcm/xcm-builder/src/tests/origins.rs b/polkadot/xcm/xcm-builder/src/tests/origins.rs index c717d1e2af8a2f09e910dbf83fe51e6b821a6872..b6a1a9f1052a4757e7596927e10e1af73bd7b187 100644 --- a/polkadot/xcm/xcm-builder/src/tests/origins.rs +++ b/polkadot/xcm/xcm-builder/src/tests/origins.rs @@ -80,8 +80,8 @@ fn universal_origin_should_work() { fn export_message_should_work() { // Bridge chain (assumed to be Relay) lets Parachain #1 have message execution for free. AllowUnpaidFrom::set(vec![[Parachain(1)].into()]); - // Local parachain #1 issues a transfer asset on Polkadot Relay-chain, transfering 100 Planck to - // Polkadot parachain #2. + // Local parachain #1 issues a transfer asset on Polkadot Relay-chain, transferring 100 Planck + // to Polkadot parachain #2. let expected_message = Xcm(vec![TransferAsset { assets: (Here, 100u128).into(), beneficiary: Parachain(2).into(), diff --git a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs index 9892c500f2eebb7cd4a3cfd88f86c6669cca4111..019113a12b2fb127142c9e59d00ef0faca84b688 100644 --- a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs @@ -49,7 +49,7 @@ construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; @@ -218,6 +218,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = (); + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } parameter_types! { diff --git a/polkadot/xcm/xcm-builder/tests/mock/mod.rs b/polkadot/xcm/xcm-builder/tests/mock/mod.rs index 06cedb9c35775898e264a58e175ebe81e06d10a4..f3cf5ab264902df83a40948fa3ad28c40571cd3f 100644 --- a/polkadot/xcm/xcm-builder/tests/mock/mod.rs +++ b/polkadot/xcm/xcm-builder/tests/mock/mod.rs @@ -76,7 +76,7 @@ parameter_types! { pub const BlockHashCount: u64 = 250; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; @@ -204,6 +204,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = (); + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/polkadot/xcm/xcm-builder/tests/scenarios.rs b/polkadot/xcm/xcm-builder/tests/scenarios.rs index db37f85acdbbac2edfaa278d33ac7654b0efcee5..ee1aeffbb4e71d5c42cb92376395961d4d95de81 100644 --- a/polkadot/xcm/xcm-builder/tests/scenarios.rs +++ b/polkadot/xcm/xcm-builder/tests/scenarios.rs @@ -132,7 +132,7 @@ fn report_holding_works() { assets: AllCounted(1).into(), beneficiary: OnlyChild.into(), // invalid destination }, - // is not triggered becasue the deposit fails + // is not triggered because the deposit fails ReportHolding { response_info: response_info.clone(), assets: All.into() }, ]); let mut hash = fake_message_hash(&message); diff --git a/polkadot/xcm/xcm-executor/integration-tests/Cargo.toml b/polkadot/xcm/xcm-executor/integration-tests/Cargo.toml index cafe12dc587f883a31ac3539aced38e8de29a89e..1e572e6210a27a7469f827bbe5d076c01c84f032 100644 --- a/polkadot/xcm/xcm-executor/integration-tests/Cargo.toml +++ b/polkadot/xcm/xcm-executor/integration-tests/Cargo.toml @@ -20,6 +20,7 @@ pallet-xcm = { path = "../../pallet-xcm" } polkadot-test-client = { path = "../../../node/test/client" } polkadot-test-runtime = { path = "../../../runtime/test-runtime" } polkadot-test-service = { path = "../../../node/test/service" } +polkadot-service = { path = "../../../node/service" } sp-consensus = { path = "../../../../substrate/primitives/consensus/common" } sp-keyring = { path = "../../../../substrate/primitives/keyring" } sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } @@ -27,6 +28,7 @@ sp-state-machine = { path = "../../../../substrate/primitives/state-machine" } xcm = { package = "staging-xcm", path = "../..", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = ".." } sp-tracing = { path = "../../../../substrate/primitives/tracing" } +sp-core = { path = "../../../../substrate/primitives/core" } [features] default = ["std"] diff --git a/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs b/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs index 79d6cb1c411b12da2a9f6b81a01b93a44c97ebe9..1d1ee40d092c6d251b200439971986b9e0d6abd7 100644 --- a/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs +++ b/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs @@ -19,12 +19,14 @@ use codec::Encode; use frame_support::{dispatch::GetDispatchInfo, weights::Weight}; +use polkadot_service::chain_spec::get_account_id_from_seed; use polkadot_test_client::{ BlockBuilderExt, ClientBlockImportExt, DefaultTestClientBuilderExt, InitPolkadotBlockBuilder, TestClientBuilder, TestClientBuilderExt, }; use polkadot_test_runtime::{pallet_test_notifier, xcm_config::XcmConfig}; use polkadot_test_service::construct_extrinsic; +use sp_core::sr25519; use sp_runtime::traits::Block; use sp_state_machine::InspectState; use xcm::{latest::prelude::*, VersionedResponse, VersionedXcm}; @@ -323,3 +325,84 @@ fn query_response_elicits_handler() { ))); }); } + +/// Simulates a cross-chain message from Parachain to Parachain through Relay Chain +/// that deposits assets into the reserve of the destination. +/// Regression test for `DepositReserveAsset` changes in +/// +#[test] +fn deposit_reserve_asset_works_for_any_xcm_sender() { + sp_tracing::try_init_simple(); + let mut client = TestClientBuilder::new().build(); + + // Init values for the simulated origin Parachain + let amount_to_send: u128 = 1_000_000_000_000; + let assets: Assets = (Parent, amount_to_send).into(); + let fee_asset_item = 0; + let max_assets = assets.len() as u32; + let fees = assets.get(fee_asset_item as usize).unwrap().clone(); + let weight_limit = Unlimited; + let reserve = Location::parent(); + let dest = Location::new(1, [Parachain(2000)]); + let beneficiary_id = get_account_id_from_seed::("Alice"); + let beneficiary = Location::new(0, [AccountId32 { network: None, id: beneficiary_id.into() }]); + + // spends up to half of fees for execution on reserve and other half for execution on + // destination + let fee1 = amount_to_send.saturating_div(2); + let fee2 = amount_to_send.saturating_sub(fee1); + let fees_half_1 = Asset::from((fees.id.clone(), Fungible(fee1))); + let fees_half_2 = Asset::from((fees.id.clone(), Fungible(fee2))); + + let reserve_context = ::UniversalLocation::get(); + // identifies fee item as seen by `reserve` - to be used at reserve chain + let reserve_fees = fees_half_1.reanchored(&reserve, &reserve_context).unwrap(); + // identifies fee item as seen by `dest` - to be used at destination chain + let dest_fees = fees_half_2.reanchored(&dest, &reserve_context).unwrap(); + // identifies assets as seen by `reserve` - to be used at reserve chain + let assets_reanchored = assets.reanchored(&reserve, &reserve_context).unwrap(); + // identifies `dest` as seen by `reserve` + let dest = dest.reanchored(&reserve, &reserve_context).unwrap(); + // xcm to be executed at dest + let xcm_on_dest = Xcm(vec![ + BuyExecution { fees: dest_fees, weight_limit: weight_limit.clone() }, + DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }, + ]); + // xcm to be executed at reserve + let msg = Xcm(vec![ + WithdrawAsset(assets_reanchored), + ClearOrigin, + BuyExecution { fees: reserve_fees, weight_limit }, + DepositReserveAsset { assets: Wild(AllCounted(max_assets)), dest, xcm: xcm_on_dest }, + ]); + + let mut block_builder = client.init_polkadot_block_builder(); + + // Simulate execution of an incoming XCM message at the reserve chain + let execute = construct_extrinsic( + &client, + polkadot_test_runtime::RuntimeCall::Xcm(pallet_xcm::Call::execute { + message: Box::new(VersionedXcm::from(msg)), + max_weight: Weight::from_parts(1_000_000_000, 1024 * 1024), + }), + sp_keyring::Sr25519Keyring::Alice, + 0, + ); + + block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic"); + + let block = block_builder.build().expect("Finalizes the block").block; + let block_hash = block.hash(); + + futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block)) + .expect("imports the block"); + + client.state_at(block_hash).expect("state should exist").inspect_state(|| { + assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!( + r.event, + polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::Attempted { + outcome: Outcome::Complete { .. } + }), + ))); + }); +} diff --git a/polkadot/xcm/xcm-executor/src/config.rs b/polkadot/xcm/xcm-executor/src/config.rs index ebe532a42fd396c64d64590bfb18e6778995bd4a..b296d32ca2adb6761f815aa74397211e3bc17d5f 100644 --- a/polkadot/xcm/xcm-executor/src/config.rs +++ b/polkadot/xcm/xcm-executor/src/config.rs @@ -16,7 +16,8 @@ use crate::traits::{ AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, - FeeManager, OnResponse, ProcessTransaction, ShouldExecute, TransactAsset, + FeeManager, HandleHrmpChannelAccepted, HandleHrmpChannelClosing, + HandleHrmpNewChannelOpenRequest, OnResponse, ProcessTransaction, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, }; use frame_support::{ @@ -114,4 +115,11 @@ pub trait Config { /// Transactional processor for XCM instructions. type TransactionalProcessor: ProcessTransaction; + + /// Allows optional logic execution for the `HrmpNewChannelOpenRequest` XCM notification. + type HrmpNewChannelOpenRequestHandler: HandleHrmpNewChannelOpenRequest; + /// Allows optional logic execution for the `HrmpChannelAccepted` XCM notification. + type HrmpChannelAcceptedHandler: HandleHrmpChannelAccepted; + /// Allows optional logic execution for the `HrmpChannelClosing` XCM notification. + type HrmpChannelClosingHandler: HandleHrmpChannelClosing; } diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index b26779f3ae9da206d76823027dc193dc771cc215..81b81fe6a17154c74e03ebbddf8bfd81e4f7181c 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -31,7 +31,8 @@ use xcm::latest::prelude::*; pub mod traits; use traits::{ validate_export, AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin, - DropAssets, Enact, ExportXcm, FeeManager, FeeReason, OnResponse, ProcessTransaction, + DropAssets, Enact, ExportXcm, FeeManager, FeeReason, HandleHrmpChannelAccepted, + HandleHrmpChannelClosing, HandleHrmpNewChannelOpenRequest, OnResponse, ProcessTransaction, Properties, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, XcmAssetTransfers, }; @@ -826,9 +827,9 @@ impl XcmExecutor { // be weighed let to_weigh = self.holding.saturating_take(assets.clone()); self.holding.subsume_assets(to_weigh.clone()); - + let to_weigh_reanchored = Self::reanchored(to_weigh, &dest, None); let mut message_to_weigh = - vec![ReserveAssetDeposited(to_weigh.into()), ClearOrigin]; + vec![ReserveAssetDeposited(to_weigh_reanchored), ClearOrigin]; message_to_weigh.extend(xcm.0.clone().into_iter()); let (_, fee) = validate_send::(dest.clone(), Xcm(message_to_weigh))?; @@ -1212,9 +1213,21 @@ impl XcmExecutor { ); Ok(()) }, - HrmpNewChannelOpenRequest { .. } => Err(XcmError::Unimplemented), - HrmpChannelAccepted { .. } => Err(XcmError::Unimplemented), - HrmpChannelClosing { .. } => Err(XcmError::Unimplemented), + HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => + Config::TransactionalProcessor::process(|| { + Config::HrmpNewChannelOpenRequestHandler::handle( + sender, + max_message_size, + max_capacity, + ) + }), + HrmpChannelAccepted { recipient } => Config::TransactionalProcessor::process(|| { + Config::HrmpChannelAcceptedHandler::handle(recipient) + }), + HrmpChannelClosing { initiator, sender, recipient } => + Config::TransactionalProcessor::process(|| { + Config::HrmpChannelClosingHandler::handle(initiator, sender, recipient) + }), } } } diff --git a/polkadot/xcm/xcm-executor/src/traits/hrmp.rs b/polkadot/xcm/xcm-executor/src/traits/hrmp.rs new file mode 100644 index 0000000000000000000000000000000000000000..b6bbb9316d7537559e895b379b855bf10f0d303d --- /dev/null +++ b/polkadot/xcm/xcm-executor/src/traits/hrmp.rs @@ -0,0 +1,56 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +use xcm::latest::Result as XcmResult; + +/// Executes logic when a `HrmpNewChannelOpenRequest` XCM notification is received. +pub trait HandleHrmpNewChannelOpenRequest { + fn handle(sender: u32, max_message_size: u32, max_capacity: u32) -> XcmResult; +} + +/// Executes optional logic when a `HrmpChannelAccepted` XCM notification is received. +pub trait HandleHrmpChannelAccepted { + fn handle(recipient: u32) -> XcmResult; +} + +/// Executes optional logic when a `HrmpChannelClosing` XCM notification is received. +pub trait HandleHrmpChannelClosing { + fn handle(initiator: u32, sender: u32, recipient: u32) -> XcmResult; +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl HandleHrmpNewChannelOpenRequest for Tuple { + fn handle(sender: u32, max_message_size: u32, max_capacity: u32) -> XcmResult { + for_tuples!( #( Tuple::handle(sender, max_message_size, max_capacity)?; )* ); + Ok(()) + } +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl HandleHrmpChannelAccepted for Tuple { + fn handle(recipient: u32) -> XcmResult { + for_tuples!( #( Tuple::handle(recipient)?; )* ); + Ok(()) + } +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl HandleHrmpChannelClosing for Tuple { + fn handle(initiator: u32, sender: u32, recipient: u32) -> XcmResult { + for_tuples!( #( Tuple::handle(initiator, sender, recipient)?; )* ); + Ok(()) + } +} diff --git a/polkadot/xcm/xcm-executor/src/traits/mod.rs b/polkadot/xcm/xcm-executor/src/traits/mod.rs index b445e84d39120bd7970dbfff599a061f863ab2d9..aa3f0d26e3025781eacefcf4b6cfdfe140cb39a5 100644 --- a/polkadot/xcm/xcm-executor/src/traits/mod.rs +++ b/polkadot/xcm/xcm-executor/src/traits/mod.rs @@ -45,6 +45,10 @@ mod should_execute; pub use should_execute::{CheckSuspension, Properties, ShouldExecute}; mod transact_asset; pub use transact_asset::TransactAsset; +mod hrmp; +pub use hrmp::{ + HandleHrmpChannelAccepted, HandleHrmpChannelClosing, HandleHrmpNewChannelOpenRequest, +}; mod weight; #[deprecated = "Use `sp_runtime::traits::` instead"] pub use sp_runtime::traits::{Identity, TryConvertInto as JustTry}; diff --git a/polkadot/xcm/xcm-executor/src/traits/on_response.rs b/polkadot/xcm/xcm-executor/src/traits/on_response.rs index 952bd2d0040aca89f21d7a1eabbb6667ccb8a68c..1049bacdca5f9eee964cf810835c69b7ba1ba1ea 100644 --- a/polkadot/xcm/xcm-executor/src/traits/on_response.rs +++ b/polkadot/xcm/xcm-executor/src/traits/on_response.rs @@ -16,11 +16,8 @@ use crate::{Junctions::Here, Xcm}; use core::result; -use frame_support::{ - pallet_prelude::{Get, TypeInfo}, - parameter_types, -}; -use parity_scale_codec::{Decode, Encode, FullCodec, MaxEncodedLen}; +use frame_support::{pallet_prelude::Get, parameter_types}; +use parity_scale_codec::{Decode, Encode}; use sp_arithmetic::traits::Zero; use sp_std::fmt::Debug; use xcm::latest::{ @@ -115,15 +112,6 @@ pub enum QueryResponseStatus { /// Provides methods to expect responses from XCMs and query their status. pub trait QueryHandler { - type QueryId: From - + FullCodec - + MaxEncodedLen - + TypeInfo - + Clone - + Eq - + PartialEq - + Debug - + Copy; type BlockNumber: Zero + Encode; type Error; type UniversalLocation: Get; @@ -151,14 +139,14 @@ pub trait QueryHandler { message: &mut Xcm<()>, responder: impl Into, timeout: Self::BlockNumber, - ) -> result::Result; + ) -> result::Result; /// Attempt to remove and return the response of query with ID `query_id`. - fn take_response(id: Self::QueryId) -> QueryResponseStatus; + fn take_response(id: QueryId) -> QueryResponseStatus; /// Makes sure to expect a response with the given id. #[cfg(feature = "runtime-benchmarks")] - fn expect_response(id: Self::QueryId, response: Response); + fn expect_response(id: QueryId, response: Response); } parameter_types! { @@ -168,17 +156,16 @@ parameter_types! { impl QueryHandler for () { type BlockNumber = u64; type Error = (); - type QueryId = u64; type UniversalLocation = UniversalLocation; - fn take_response(_query_id: Self::QueryId) -> QueryResponseStatus { + fn take_response(_query_id: QueryId) -> QueryResponseStatus { QueryResponseStatus::NotFound } fn new_query( _responder: impl Into, _timeout: Self::BlockNumber, _match_querier: impl Into, - ) -> Self::QueryId { + ) -> QueryId { 0u64 } @@ -186,10 +173,10 @@ impl QueryHandler for () { _message: &mut Xcm<()>, _responder: impl Into, _timeout: Self::BlockNumber, - ) -> Result { + ) -> Result { Err(()) } #[cfg(feature = "runtime-benchmarks")] - fn expect_response(_id: Self::QueryId, _response: crate::Response) {} + fn expect_response(_id: QueryId, _response: crate::Response) {} } diff --git a/polkadot/xcm/xcm-executor/src/traits/should_execute.rs b/polkadot/xcm/xcm-executor/src/traits/should_execute.rs index 449e82b5a6e2c3ecaf43b35608222b88ae6c8bcc..12e8fd6b87f1004da2da250a62cf7480bbb178af 100644 --- a/polkadot/xcm/xcm-executor/src/traits/should_execute.rs +++ b/polkadot/xcm/xcm-executor/src/traits/should_execute.rs @@ -18,7 +18,7 @@ use frame_support::traits::ProcessMessageError; use sp_std::result::Result; use xcm::latest::{Instruction, Location, Weight, XcmHash}; -/// Properyies of an XCM message and its imminent execution. +/// Properties of an XCM message and its imminent execution. #[derive(Clone, Eq, PartialEq, Debug)] pub struct Properties { /// The amount of weight that the system has determined this diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..682642d13c3a73de5c24bdedccdcf63002e60b55 --- /dev/null +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "xcm-fee-payment-runtime-api" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +repository.workspace = true +description = "XCM fee payment runtime API" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ + "derive", +] } + +sp-api = { path = "../../../substrate/primitives/api", default-features = false } +scale-info = { version = "2.10.0", default-features = false, features = [ + "derive", + "serde", +] } +sp-std = { path = "../../../substrate/primitives/std", default-features = false } +sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } +sp-weights = { path = "../../../substrate/primitives/weights", default-features = false } +xcm = { package = "staging-xcm", path = "../", default-features = false } +frame-support = { path = "../../../substrate/frame/support", default-features = false } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "scale-info/std", + "sp-api/std", + "sp-runtime/std", + "sp-std/std", + "sp-weights/std", + "xcm/std", +] diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..20bf9236f1fbbb46ca50a1b42d6dc4e519a5ae1a --- /dev/null +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs @@ -0,0 +1,99 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// 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 Polkadot. If not, see . + +//! Runtime API definition for xcm transaction payment. + +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Decode, Encode}; +use frame_support::pallet_prelude::TypeInfo; +use sp_std::vec::Vec; +use sp_weights::Weight; +use xcm::{Version, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}; + +sp_api::decl_runtime_apis! { + /// A trait of XCM payment API. + /// + /// API provides functionality for obtaining: + /// + /// * the weight required to execute an XCM message, + /// * a list of acceptable `AssetId`s for message execution payment, + /// * the cost of the weight in the specified acceptable `AssetId`. + /// * the fees for an XCM message delivery. + /// + /// To determine the execution weight of the calls required for + /// [`xcm::latest::Instruction::Transact`] instruction, `TransactionPaymentCallApi` can be used. + pub trait XcmPaymentApi { + /// Returns a list of acceptable payment assets. + /// + /// # Arguments + /// + /// * `xcm_version`: Version. + fn query_acceptable_payment_assets(xcm_version: Version) -> Result, Error>; + + /// Returns a weight needed to execute a XCM. + /// + /// # Arguments + /// + /// * `message`: `VersionedXcm`. + fn query_xcm_weight(message: VersionedXcm<()>) -> Result; + + /// Converts a weight into a fee for the specified `AssetId`. + /// + /// # Arguments + /// + /// * `weight`: convertible `Weight`. + /// * `asset`: `VersionedAssetId`. + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result; + + /// Get delivery fees for sending a specific `message` to a `destination`. + /// These always come in a specific asset, defined by the chain. + /// + /// # Arguments + /// * `message`: The message that'll be sent, necessary because most delivery fees are based on the + /// size of the message. + /// * `destination`: The destination to send the message to. Different destinations may use + /// different senders that charge different fees. + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result; + } +} + +#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)] +pub enum Error { + /// An API part is unsupported. + #[codec(index = 0)] + Unimplemented, + + /// Converting a versioned data structure from one version to another failed. + #[codec(index = 1)] + VersionedConversionFailed, + + /// XCM message weight calculation failed. + #[codec(index = 2)] + WeightNotComputable, + + /// XCM version not able to be handled. + #[codec(index = 3)] + UnhandledXcmVersion, + + /// The given asset is not handled as a fee asset. + #[codec(index = 4)] + AssetNotFound, + + /// Destination is known to be unroutable. + #[codec(index = 5)] + Unroutable, +} diff --git a/polkadot/xcm/xcm-simulator/example/src/lib.rs b/polkadot/xcm/xcm-simulator/example/src/lib.rs index d134957fbc1c58a2fa59cfb73860d93d7ba74d5b..13210179e9100c12ac701f70873ea07d1b6bd733 100644 --- a/polkadot/xcm/xcm-simulator/example/src/lib.rs +++ b/polkadot/xcm/xcm-simulator/example/src/lib.rs @@ -424,7 +424,7 @@ mod tests { /// Scenario: /// The relay-chain transfers an NFT into a parachain's sovereign account, who then mints a - /// trustless-backed-derivated locally. + /// trustless-backed-derived locally. /// /// Asserts that the parachain accounts are updated as expected. #[test] @@ -479,7 +479,7 @@ mod tests { assert_ok!(ParachainPalletXcm::send_xcm(alice, Parent, message)); }); ParaA::execute_with(|| { - log::debug!(target: "xcm-exceutor", "Hello"); + log::debug!(target: "xcm-executor", "Hello"); assert_eq!( parachain::ForeignUniques::owner((Parent, GeneralIndex(2)).into(), 69u32.into()), Some(ALICE), diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain.rs b/polkadot/xcm/xcm-simulator/example/src/parachain.rs index 64333b4d5815018381f86eca10199c0d90feb6e1..86401d756af3b7d86c34e394ae89c677684c0fb8 100644 --- a/polkadot/xcm/xcm-simulator/example/src/parachain.rs +++ b/polkadot/xcm/xcm-simulator/example/src/parachain.rs @@ -63,7 +63,7 @@ parameter_types! { pub const BlockHashCount: u64 = 250; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; @@ -251,6 +251,9 @@ impl Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } #[frame_support::pallet] diff --git a/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs index 54c5657c00d7f6c38ade3aadaf38056504abcc76..377c77f30a473d178b5fa3c56a79abe7e2c2766a 100644 --- a/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs @@ -49,7 +49,7 @@ parameter_types! { pub const BlockHashCount: u64 = 250; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; @@ -198,6 +198,9 @@ impl Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs b/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs index a20390b64f946db2633ae2538b4a869b53fa338a..cadfc1e7200c075433ebf295982cc49f8a0d06b1 100644 --- a/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs +++ b/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs @@ -63,7 +63,7 @@ parameter_types! { pub const BlockHashCount: u32 = 250; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type AccountId = AccountId; type Lookup = AccountIdLookup; @@ -156,6 +156,9 @@ impl Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } #[frame_support::pallet] diff --git a/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs b/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs index 5bf65fa9f9ac407ead13a008919aa2796867ccdd..3224df66cbe56fd50ecdc5931274f41332143134 100644 --- a/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs +++ b/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs @@ -62,7 +62,7 @@ parameter_types! { pub const BlockHashCount: u32 = 250; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type AccountId = AccountId; type Lookup = sp_runtime::traits::AccountIdLookup; @@ -157,6 +157,9 @@ impl Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/polkadot/zombienet_tests/functional/0002-parachains-disputes.toml b/polkadot/zombienet_tests/functional/0002-parachains-disputes.toml index f6bdfeb4877e1dbd5f293f8c0ec864f4f7f1b80d..2561661de1f8408b2ed2808f1f5e41b4286ce97e 100644 --- a/polkadot/zombienet_tests/functional/0002-parachains-disputes.toml +++ b/polkadot/zombienet_tests/functional/0002-parachains-disputes.toml @@ -2,9 +2,11 @@ timeout = 1000 [relaychain.genesis.runtimeGenesis.patch.configuration.config] - max_validators_per_core = 5 needed_approvals = 8 +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + max_validators_per_core = 5 + [relaychain.genesis.runtimeGenesis.patch.configuration.config.approval_voting_params] max_approval_coalesce_count = 5 diff --git a/polkadot/zombienet_tests/functional/0004-parachains-garbage-candidate.toml b/polkadot/zombienet_tests/functional/0004-parachains-garbage-candidate.toml index 5d6f299d46133e52c645bd128fbdcc8171490d4e..a2a2621f8426d2e8deb32f524d23e666ab1b5c00 100644 --- a/polkadot/zombienet_tests/functional/0004-parachains-garbage-candidate.toml +++ b/polkadot/zombienet_tests/functional/0004-parachains-garbage-candidate.toml @@ -2,8 +2,10 @@ timeout = 1000 bootnode = true -[relaychain.genesis.runtimeGenesis.patch.configuration.config] +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] max_validators_per_core = 1 + +[relaychain.genesis.runtimeGenesis.patch.configuration.config] needed_approvals = 2 [relaychain] diff --git a/polkadot/zombienet_tests/functional/0005-parachains-disputes-past-session.toml b/polkadot/zombienet_tests/functional/0005-parachains-disputes-past-session.toml index e2fbec079b1a81ec50054a30ef89d049bb717482..a3bbc82e74ba6d9d5c442742afc2dd18f31c7305 100644 --- a/polkadot/zombienet_tests/functional/0005-parachains-disputes-past-session.toml +++ b/polkadot/zombienet_tests/functional/0005-parachains-disputes-past-session.toml @@ -3,8 +3,10 @@ timeout = 1000 bootnode = true [relaychain.genesis.runtimeGenesis.patch.configuration.config] - max_validators_per_core = 1 needed_approvals = 2 + +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + max_validators_per_core = 1 group_rotation_frequency = 2 [relaychain] diff --git a/polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.toml b/polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.toml index bef54cb8ca416fb3210849fb9801c44a31e846a4..858f87b9cfe52c10c9cff9885dbeb57b0dcba2f5 100644 --- a/polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.toml +++ b/polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.toml @@ -3,10 +3,12 @@ timeout = 1000 bootnode = true [relaychain.genesis.runtimeGenesis.patch.configuration.config] - max_validators_per_core = 1 needed_approvals = 7 relay_vrf_modulo_samples = 5 +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + max_validators_per_core = 1 + [relaychain] default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" chain = "rococo-local" diff --git a/polkadot/zombienet_tests/functional/0007-dispute-freshly-finalized.toml b/polkadot/zombienet_tests/functional/0007-dispute-freshly-finalized.toml index 69eb0804d8cb70c58fbc70abdb091af3a197d2fb..573ccf961385fa2c721caa0bb67ed97b94deb5a1 100644 --- a/polkadot/zombienet_tests/functional/0007-dispute-freshly-finalized.toml +++ b/polkadot/zombienet_tests/functional/0007-dispute-freshly-finalized.toml @@ -2,9 +2,11 @@ timeout = 1000 [relaychain.genesis.runtimeGenesis.patch.configuration.config] - max_validators_per_core = 1 needed_approvals = 1 +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + max_validators_per_core = 1 + [relaychain] default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" chain = "rococo-local" diff --git a/polkadot/zombienet_tests/functional/0008-dispute-old-finalized.toml b/polkadot/zombienet_tests/functional/0008-dispute-old-finalized.toml index 1ea385c3a42ee8fdf89b7595151a98c26d9b011b..ea1c93a1403fb837b45a6da01d0920c516e43ae6 100644 --- a/polkadot/zombienet_tests/functional/0008-dispute-old-finalized.toml +++ b/polkadot/zombienet_tests/functional/0008-dispute-old-finalized.toml @@ -2,9 +2,11 @@ timeout = 1000 [relaychain.genesis.runtimeGenesis.patch.configuration.config] - max_validators_per_core = 1 needed_approvals = 1 +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + max_validators_per_core = 1 + [relaychain] default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" chain = "rococo-local" diff --git a/polkadot/zombienet_tests/functional/0010-validator-disabling.toml b/polkadot/zombienet_tests/functional/0010-validator-disabling.toml index 6701d60d74d147d517fc55dd3f1253171f6b807f..c9d79c5f8f236918cf409fd684b7d0b8d9792d33 100644 --- a/polkadot/zombienet_tests/functional/0010-validator-disabling.toml +++ b/polkadot/zombienet_tests/functional/0010-validator-disabling.toml @@ -3,8 +3,10 @@ timeout = 1000 bootnode = true [relaychain.genesis.runtimeGenesis.patch.configuration.config] - max_validators_per_core = 1 needed_approvals = 2 + +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + max_validators_per_core = 1 group_rotation_frequency = 10 [relaychain] diff --git a/polkadot/zombienet_tests/functional/0011-async-backing-6-seconds-rate.toml b/polkadot/zombienet_tests/functional/0011-async-backing-6-seconds-rate.toml index 5a6832b149be21a48ca17fe6e84f75cfc3324ed0..b776622fdce33df2e9a78debc31ee3e62ae4805d 100644 --- a/polkadot/zombienet_tests/functional/0011-async-backing-6-seconds-rate.toml +++ b/polkadot/zombienet_tests/functional/0011-async-backing-6-seconds-rate.toml @@ -8,13 +8,16 @@ chain = "rococo-local" [relaychain.genesis.runtimeGenesis.patch.configuration.config] needed_approvals = 4 relay_vrf_modulo_samples = 6 - scheduling_lookahead = 2 - group_rotation_frequency = 4 [relaychain.genesis.runtimeGenesis.patch.configuration.config.async_backing_params] max_candidate_depth = 3 allowed_ancestry_len = 2 +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + lookahead = 2 + group_rotation_frequency = 4 + + [relaychain.default_resources] limits = { memory = "4G", cpu = "2" } requests = { memory = "2G", cpu = "1" } diff --git a/polkadot/zombienet_tests/functional/0012-elastic-scaling-mvp.toml b/polkadot/zombienet_tests/functional/0012-elastic-scaling-mvp.toml new file mode 100644 index 0000000000000000000000000000000000000000..9b3576eaa3c212bd9490095c12ae4bca65f6fa54 --- /dev/null +++ b/polkadot/zombienet_tests/functional/0012-elastic-scaling-mvp.toml @@ -0,0 +1,40 @@ +[settings] +timeout = 1000 +bootnode = true + +[relaychain.genesis.runtimeGenesis.patch.configuration.config] + needed_approvals = 4 + +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + max_validators_per_core = 2 + num_cores = 2 + +[relaychain] +default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" +chain = "rococo-local" +default_command = "polkadot" + +[relaychain.default_resources] +limits = { memory = "4G", cpu = "2" } +requests = { memory = "2G", cpu = "1" } + + [[relaychain.nodes]] + name = "alice" + validator = "true" + + [[relaychain.node_groups]] + name = "validator" + count = 3 + args = [ "-lparachain=debug,runtime=debug"] + +[[parachains]] +id = 2000 +default_command = "polkadot-parachain" +add_to_genesis = false +register_para = true +onboard_as_parachain = false + + [parachains.collator] + name = "collator2000" + command = "polkadot-parachain" + args = [ "-lparachain=debug" ] diff --git a/polkadot/zombienet_tests/functional/0012-elastic-scaling-mvp.zndsl b/polkadot/zombienet_tests/functional/0012-elastic-scaling-mvp.zndsl new file mode 100644 index 0000000000000000000000000000000000000000..edc87c802a0965b6fd685db44ccbf50e807f1ed3 --- /dev/null +++ b/polkadot/zombienet_tests/functional/0012-elastic-scaling-mvp.zndsl @@ -0,0 +1,19 @@ +Description: Test that a paraid acquiring multiple cores does not brick itself if ElasticScalingMVP feature is enabled in genesis +Network: ./0012-elastic-scaling-mvp.toml +Creds: config + +# Check authority status. +validator: reports node_roles is 4 + +validator: reports substrate_block_height{status="finalized"} is at least 10 within 100 seconds + +# Ensure parachain was able to make progress. +validator: parachain 2000 block height is at least 10 within 200 seconds + +# Register the second core assigned to this parachain. +alice: js-script ./0012-register-para.js return is 0 within 600 seconds + +validator: reports substrate_block_height{status="finalized"} is at least 35 within 100 seconds + +# Ensure parachain is now making progress. +validator: parachain 2000 block height is at least 30 within 200 seconds diff --git a/polkadot/zombienet_tests/functional/0012-register-para.js b/polkadot/zombienet_tests/functional/0012-register-para.js new file mode 100644 index 0000000000000000000000000000000000000000..25c7e4f5ffddcf087edab4df37e696af92fe6d3f --- /dev/null +++ b/polkadot/zombienet_tests/functional/0012-register-para.js @@ -0,0 +1,37 @@ +async function run(nodeName, networkInfo, _jsArgs) { + const { wsUri, userDefinedTypes } = networkInfo.nodesByName[nodeName]; + const api = await zombie.connect(wsUri, userDefinedTypes); + + await zombie.util.cryptoWaitReady(); + + // account to submit tx + const keyring = new zombie.Keyring({ type: "sr25519" }); + const alice = keyring.addFromUri("//Alice"); + + await new Promise(async (resolve, reject) => { + const unsub = await api.tx.sudo + .sudo(api.tx.coretime.assignCore(0, 35, [[{ task: 2000 }, 57600]], null)) + .signAndSend(alice, ({ status, isError }) => { + if (status.isInBlock) { + console.log( + `Transaction included at blockhash ${status.asInBlock}`, + ); + } else if (status.isFinalized) { + console.log( + `Transaction finalized at blockHash ${status.asFinalized}`, + ); + unsub(); + return resolve(); + } else if (isError) { + console.log(`Transaction error`); + reject(`Transaction error`); + } + }); + }); + + + + return 0; +} + +module.exports = { run }; diff --git a/polkadot/zombienet_tests/misc/0001-paritydb.toml b/polkadot/zombienet_tests/misc/0001-paritydb.toml index 399f848d3ac49e7e9950617c170c13d5c63593dd..b3ce2081b1119ec237c1b3d6e14e1e4aed8322c5 100644 --- a/polkadot/zombienet_tests/misc/0001-paritydb.toml +++ b/polkadot/zombienet_tests/misc/0001-paritydb.toml @@ -3,9 +3,11 @@ timeout = 1000 bootnode = true [relaychain.genesis.runtimeGenesis.patch.configuration.config] - max_validators_per_core = 1 needed_approvals = 3 +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + max_validators_per_core = 1 + [relaychain] default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" chain = "rococo-local" diff --git a/polkadot/zombienet_tests/misc/0002-upgrade-node.zndsl b/polkadot/zombienet_tests/misc/0002-upgrade-node.zndsl index db0a60ac1df617e5c89dc6a1385c4c106c1ead05..5fe1b2ad2f1a7589a28f8b1b8da05d6f35d3d59b 100644 --- a/polkadot/zombienet_tests/misc/0002-upgrade-node.zndsl +++ b/polkadot/zombienet_tests/misc/0002-upgrade-node.zndsl @@ -10,9 +10,8 @@ dave: parachain 2001 block height is at least 10 within 200 seconds # POLKADOT_PR_ARTIFACTS_URL=https://gitlab.parity.io/parity/mirrors/polkadot/-/jobs/1842869/artifacts/raw/artifacts # with the version of polkadot you want to download. -# avg 30s in our infra -alice: run ./0002-download-polkadot-from-pr.sh with "{{POLKADOT_PR_ARTIFACTS_URL}}" within 60 seconds -bob: run ./0002-download-polkadot-from-pr.sh with "{{POLKADOT_PR_ARTIFACTS_URL}}" within 60 seconds +alice: run ./0002-download-polkadot-from-pr.sh with "{{POLKADOT_PR_ARTIFACTS_URL}}" within 240 seconds +bob: run ./0002-download-polkadot-from-pr.sh with "{{POLKADOT_PR_ARTIFACTS_URL}}" within 240 seconds # update the cmd to add the flag '--insecure-validator-i-know-what-i-do' # once the base image include the version with this flag we can remove this logic. alice: run ./0002-update-cmd.sh within 60 seconds diff --git a/prdoc/.template.prdoc b/prdoc/.template.prdoc index 097741f388c4f2be73c9aed9d2b1dcc7dde32cf2..03a458876dfaaab5f9bc2fe61ab349c0903988e1 100644 --- a/prdoc/.template.prdoc +++ b/prdoc/.template.prdoc @@ -4,7 +4,7 @@ title: ... doc: - - audience: Node Dev + - audience: ... description: | ... diff --git a/prdoc/1.4.0/pr_1246.prdoc b/prdoc/1.4.0/pr_1246.prdoc index a4d270c45cb5915760ddfbd60e5e4b3b7c08cd4a..3b5c2017f22acfba25471595c50015ae14c3e5cd 100644 --- a/prdoc/1.4.0/pr_1246.prdoc +++ b/prdoc/1.4.0/pr_1246.prdoc @@ -11,7 +11,7 @@ migrations: description: "Messages from the DMP dispatch queue will be moved over to the MQ pallet via `on_initialize`. This happens over multiple blocks and emits a `Completed` event at the end. The pallet can be un-deployed and deleted afterwards. Note that the migration reverses the order of messages, which should be acceptable as a one-off." crates: - - name: cumulus_pallet_xcmp_queue + - name: cumulus-pallet-xcmp-queue note: Pallet config must be altered according to the MR description. host_functions: [] diff --git a/prdoc/1.6.0/pr_2689.prdoc b/prdoc/1.6.0/pr_2689.prdoc index 847c3e8026cef9dfdf63f044a1b773be85b12921..5d3081e3a4ce71d872c8eb8a96fda5b2e273b684 100644 --- a/prdoc/1.6.0/pr_2689.prdoc +++ b/prdoc/1.6.0/pr_2689.prdoc @@ -1,7 +1,7 @@ # Schema: Parity PR Documentation Schema (prdoc) # See doc at https://github.com/paritytech/prdoc -title: BEEFY: Support compatibility with Warp Sync - Allow Warp Sync for Validators +title: "BEEFY: Support compatibility with Warp Sync - Allow Warp Sync for Validators" doc: - audience: Node Operator diff --git a/prdoc/1.6.0/pr_2771.prdoc b/prdoc/1.6.0/pr_2771.prdoc index 1b49162e4392ba1ad1a77d61e5b2289474b0ffbe..50fb99556ecddf39adbcece77d9b83e5d98651b4 100644 --- a/prdoc/1.6.0/pr_2771.prdoc +++ b/prdoc/1.6.0/pr_2771.prdoc @@ -6,4 +6,4 @@ doc: Enable better req-response protocol versioning, by allowing for fallback requests on different protocols. crates: - - name: sc_network + - name: sc-network diff --git a/prdoc/pr_1660.prdoc b/prdoc/1.8.0/pr_1660.prdoc similarity index 100% rename from prdoc/pr_1660.prdoc rename to prdoc/1.8.0/pr_1660.prdoc diff --git a/prdoc/pr_2061.prdoc b/prdoc/1.8.0/pr_2061.prdoc similarity index 100% rename from prdoc/pr_2061.prdoc rename to prdoc/1.8.0/pr_2061.prdoc diff --git a/prdoc/pr_2290.prdoc b/prdoc/1.8.0/pr_2290.prdoc similarity index 100% rename from prdoc/pr_2290.prdoc rename to prdoc/1.8.0/pr_2290.prdoc diff --git a/prdoc/pr_2903.prdoc b/prdoc/1.8.0/pr_2903.prdoc similarity index 100% rename from prdoc/pr_2903.prdoc rename to prdoc/1.8.0/pr_2903.prdoc diff --git a/prdoc/pr_2949-async-backing-on-all-testnet-system-chains.prdoc b/prdoc/1.8.0/pr_2949-async-backing-on-all-testnet-system-chains.prdoc similarity index 100% rename from prdoc/pr_2949-async-backing-on-all-testnet-system-chains.prdoc rename to prdoc/1.8.0/pr_2949-async-backing-on-all-testnet-system-chains.prdoc diff --git a/prdoc/pr_3007.prdoc b/prdoc/1.8.0/pr_3007.prdoc similarity index 100% rename from prdoc/pr_3007.prdoc rename to prdoc/1.8.0/pr_3007.prdoc diff --git a/prdoc/pr_3052.prdoc b/prdoc/1.8.0/pr_3052.prdoc similarity index 100% rename from prdoc/pr_3052.prdoc rename to prdoc/1.8.0/pr_3052.prdoc diff --git a/prdoc/pr_3060.prdoc b/prdoc/1.8.0/pr_3060.prdoc similarity index 100% rename from prdoc/pr_3060.prdoc rename to prdoc/1.8.0/pr_3060.prdoc diff --git a/prdoc/pr_3079.prdoc b/prdoc/1.8.0/pr_3079.prdoc similarity index 100% rename from prdoc/pr_3079.prdoc rename to prdoc/1.8.0/pr_3079.prdoc diff --git a/prdoc/pr_3154.prdoc b/prdoc/1.8.0/pr_3154.prdoc similarity index 100% rename from prdoc/pr_3154.prdoc rename to prdoc/1.8.0/pr_3154.prdoc diff --git a/prdoc/pr_3160.prdoc b/prdoc/1.8.0/pr_3160.prdoc similarity index 100% rename from prdoc/pr_3160.prdoc rename to prdoc/1.8.0/pr_3160.prdoc diff --git a/prdoc/pr_3166.prdoc b/prdoc/1.8.0/pr_3166.prdoc similarity index 100% rename from prdoc/pr_3166.prdoc rename to prdoc/1.8.0/pr_3166.prdoc diff --git a/prdoc/pr_3184.prdoc b/prdoc/1.8.0/pr_3184.prdoc similarity index 100% rename from prdoc/pr_3184.prdoc rename to prdoc/1.8.0/pr_3184.prdoc diff --git a/prdoc/pr_3212.prdoc b/prdoc/1.8.0/pr_3212.prdoc similarity index 100% rename from prdoc/pr_3212.prdoc rename to prdoc/1.8.0/pr_3212.prdoc diff --git a/prdoc/pr_3225.prdoc b/prdoc/1.8.0/pr_3225.prdoc similarity index 100% rename from prdoc/pr_3225.prdoc rename to prdoc/1.8.0/pr_3225.prdoc diff --git a/prdoc/pr_3230.prdoc b/prdoc/1.8.0/pr_3230.prdoc similarity index 100% rename from prdoc/pr_3230.prdoc rename to prdoc/1.8.0/pr_3230.prdoc diff --git a/prdoc/pr_3232.prdoc b/prdoc/1.8.0/pr_3232.prdoc similarity index 100% rename from prdoc/pr_3232.prdoc rename to prdoc/1.8.0/pr_3232.prdoc diff --git a/prdoc/pr_3243.prdoc b/prdoc/1.8.0/pr_3243.prdoc similarity index 100% rename from prdoc/pr_3243.prdoc rename to prdoc/1.8.0/pr_3243.prdoc diff --git a/prdoc/pr_3244.prdoc b/prdoc/1.8.0/pr_3244.prdoc similarity index 100% rename from prdoc/pr_3244.prdoc rename to prdoc/1.8.0/pr_3244.prdoc diff --git a/prdoc/pr_3272.prdoc b/prdoc/1.8.0/pr_3272.prdoc similarity index 100% rename from prdoc/pr_3272.prdoc rename to prdoc/1.8.0/pr_3272.prdoc diff --git a/prdoc/pr_3301.prdoc b/prdoc/1.8.0/pr_3301.prdoc similarity index 100% rename from prdoc/pr_3301.prdoc rename to prdoc/1.8.0/pr_3301.prdoc diff --git a/prdoc/pr_3308.prdoc b/prdoc/1.8.0/pr_3308.prdoc similarity index 100% rename from prdoc/pr_3308.prdoc rename to prdoc/1.8.0/pr_3308.prdoc diff --git a/prdoc/pr_3319.prdoc b/prdoc/1.8.0/pr_3319.prdoc similarity index 100% rename from prdoc/pr_3319.prdoc rename to prdoc/1.8.0/pr_3319.prdoc diff --git a/prdoc/pr_3325.prdoc b/prdoc/1.8.0/pr_3325.prdoc similarity index 100% rename from prdoc/pr_3325.prdoc rename to prdoc/1.8.0/pr_3325.prdoc diff --git a/prdoc/pr_3358.prdoc b/prdoc/1.8.0/pr_3358.prdoc similarity index 100% rename from prdoc/pr_3358.prdoc rename to prdoc/1.8.0/pr_3358.prdoc diff --git a/prdoc/1.8.0/pr_3361.prdoc b/prdoc/1.8.0/pr_3361.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..65baa9e94a0e38a4af16bf6f69aab7526c4ad920 --- /dev/null +++ b/prdoc/1.8.0/pr_3361.prdoc @@ -0,0 +1,10 @@ +title: Fix double charge of host function weight + +doc: + - audience: Runtime Dev + description: | + Fixed a double charge which can lead to quadratic gas consumption of + the `call` and `instantiate` host functions. + +crates: + - name: pallet-contracts diff --git a/prdoc/pr_3364.prdoc b/prdoc/1.8.0/pr_3364.prdoc similarity index 100% rename from prdoc/pr_3364.prdoc rename to prdoc/1.8.0/pr_3364.prdoc diff --git a/prdoc/pr_3370.prdoc b/prdoc/1.8.0/pr_3370.prdoc similarity index 100% rename from prdoc/pr_3370.prdoc rename to prdoc/1.8.0/pr_3370.prdoc diff --git a/prdoc/pr_3384.prdoc b/prdoc/1.8.0/pr_3384.prdoc similarity index 100% rename from prdoc/pr_3384.prdoc rename to prdoc/1.8.0/pr_3384.prdoc diff --git a/prdoc/1.8.0/pr_3395.prdoc b/prdoc/1.8.0/pr_3395.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..a10fb84fd7f56211602fadaf656a25ea5d382255 --- /dev/null +++ b/prdoc/1.8.0/pr_3395.prdoc @@ -0,0 +1,14 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "`benchmarking-cli` `pallet` subcommand: refactor `--list` and add `--all` option" + +doc: + - audience: Node Dev + description: | + `pallet` subcommand's `--list` now accepts two values: "all" and "pallets". The former will list all available benchmarks, the latter will list only pallets. + Also adds `--all` to run all the available benchmarks and `--no-csv-header` to omit the csv-style header in the output. + NOTE: changes are backward compatible. + +crates: + - name: frame-benchmarking-cli diff --git a/prdoc/1.8.0/pr_3415.prdoc b/prdoc/1.8.0/pr_3415.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c56a5d3ffaa129d4aba36e1e11905e9d72b936cb --- /dev/null +++ b/prdoc/1.8.0/pr_3415.prdoc @@ -0,0 +1,9 @@ +title: "[pallet-contracts] Add APIVersion to the config." + +doc: + - audience: Runtime Dev + description: | + Add `APIVersion` to the config to communicate the state of the Host functions exposed by the pallet. + +crates: + - name: pallet-contracts diff --git a/prdoc/1.8.0/pr_3435.prdoc b/prdoc/1.8.0/pr_3435.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..7d7896bff7d405a0a1dd34f09bc2aff0834b9ff2 --- /dev/null +++ b/prdoc/1.8.0/pr_3435.prdoc @@ -0,0 +1,16 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix BEEFY-related gossip messages error logs + +doc: + - audience: Node Operator + description: | + Added logic to pump the gossip engine while waiting for other things + to make sure gossiped messages get consumed (practically discarded + until worker is fully initialized). + This fixes an issue where node operators saw error logs, and fixes + potential RAM bloat when BEEFY initialization takes a long time + (i.e. during clean sync). +crates: + - name: sc-consensus-beefy diff --git a/prdoc/1.8.0/pr_3477.prdoc b/prdoc/1.8.0/pr_3477.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..26e96d3635b14634ca523797d741ddaf224aa9ae --- /dev/null +++ b/prdoc/1.8.0/pr_3477.prdoc @@ -0,0 +1,11 @@ +title: Allow parachain which acquires multiple coretime cores to make progress + +doc: + - audience: Node Operator + description: | + Adds the needed changes so that parachains which acquire multiple coretime cores can still make progress. + Only one of the cores will be able to be occupied at a time. + Only works if the ElasticScalingMVP node feature is enabled in the runtime and the block author validator is + updated to include this change. + +crates: [ ] diff --git a/prdoc/1.9.0/pr_1378.prdoc b/prdoc/1.9.0/pr_1378.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..6533dcb663030718fdcd3ffc353b2ae8b2a92407 --- /dev/null +++ b/prdoc/1.9.0/pr_1378.prdoc @@ -0,0 +1,27 @@ +title: Construct Runtime V2 - An outer macro approach to define the runtime + +doc: + - audience: Runtime Dev + description: | + Introduces `#[frame_support::runtime]` that can be attached to a mod to define a runtime. The items + in this mod can be attached to the following attributes to define the key components of the runtime. + 1. `#[runtime::runtime]` attached to a struct defines the main runtime + 2. `#[runtime::derive]` attached to the runtime struct defines the types generated by the runtime + 3. `#[runtime::pallet_index]` must be attached to a pallet to define its index + 4. `#[runtime::disable_call]` can be optionally attached to a pallet to disable its calls + 5. `#[runtime::disable_unsigned]` can be optionally attached to a pallet to disable unsigned calls + 6. A pallet instance can be defined as `TemplateModule: pallet_template` + An optional attribute can be defined as `#[frame_support::runtime(legacy_ordering)]` to ensure that + the order of hooks is same as the order of pallets (and not based on the pallet_index). This is to support + legacy runtimes and should be avoided for new ones. + +migrations: + db: [] + + runtime: [] + +crates: + - name: frame-support + - name: frame-support-procedural + +host_functions: [] diff --git a/prdoc/1.9.0/pr_1554.prdoc b/prdoc/1.9.0/pr_1554.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..bfce7c5edafd9df39ff9235f18536591b1d47158 --- /dev/null +++ b/prdoc/1.9.0/pr_1554.prdoc @@ -0,0 +1,14 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Runtime Upgrade ref docs and Single Block Migration example pallet + +doc: + - audience: Runtime Dev + description: | + `frame_support::traits::GetStorageVersion::current_storage_version` has been renamed `frame_support::traits::GetStorageVersion::in_code_storage_version`. + A simple find-replace is sufficient to handle this change. + +crates: + - name: "frame-support" + diff --git a/prdoc/1.9.0/pr_1781.prdoc b/prdoc/1.9.0/pr_1781.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e3560842d15ab249539ac2de172dc5914e92cc19 --- /dev/null +++ b/prdoc/1.9.0/pr_1781.prdoc @@ -0,0 +1,45 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Multi-Block-Migrations, `poll` hook and new System Callbacks" + +doc: + - audience: Runtime Dev + description: | + The major things that this MR touches are: + + **Multi-Block-Migrations**: `pallet-migrations` is introduced that can be configured in the + `System` of a runtime to act as multi-block migrator. The `migrations` pallet then in turn + receives the list of MBMs as config parameter. The list of migrations can be an aggregated + tuple of `SteppedMigration` trait implementation. + It is paramount that the `migrations` pallet is configured in `System` once it is deployed. A + test is in place to double check this. + + To integrate this into your runtime, it is only necessary to change the return type of + `initialize_block` to `RuntimeExecutiveMode`. For extended info please see + https://github.com/paritytech/polkadot-sdk/pull/1781. + + **poll**: a new pallet hook named `poll` is added. This can be used for places where the code + that should be executed is not deadline critical. Runtime devs are advised to skim their usage + of `on_initialize` and `on_finalize` to see whether they can be replace with `poll`. `poll` is + not guaranteed to be called each block. In fact it will not be called when MBMs are ongoing. + + **System Callbacks**: The `system` pallet gets five new config items - all of which can be + safely set to `()` as default. They are: + - `SingleBlockMigrations`: replaces the `Executive` now for configuring migrations. + - `MultiBlockMigrator`: the `pallet-migrations` would be set here, if deployed. + - `PreInherents`: a hook that runs before any inherent. + - `PostInherents`: a hook to run between inherents and `poll`/MBM logic. + - `PostTransactions`: a hook to run after all transactions but before `on_idle`. + +crates: + - name: frame-executive + - name: frame-system + - name: frame-support + - name: frame-support-procedural + - name: pallet-migrations + - name: sc-basic-authorship + - name: sc-block-builder + - name: sp-api + - name: sp-api-proc-macro + - name: sp-runtime diff --git a/prdoc/1.9.0/pr_2393.prdoc b/prdoc/1.9.0/pr_2393.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..708d017fafd50d103c0470997c3a7571e0ff30c1 --- /dev/null +++ b/prdoc/1.9.0/pr_2393.prdoc @@ -0,0 +1,16 @@ +title: "[XCMP] Use the number of 'ready' pages in XCMP suspend logic" + +doc: + - audience: Runtime Dev + description: | + Semantics of the suspension logic in the XCMP queue pallet change from using the number of + total pages to the number of 'ready' pages. The number of ready pages is now also exposed by + the `MessageQueue` pallet to downstream via the queue `footprint`. + +crates: + - name: cumulus-pallet-xcmp-queue + bump: patch + - name: pallet-message-queue + bump: patch + - name: frame-support + bump: major diff --git a/prdoc/1.9.0/pr_3002.prdoc b/prdoc/1.9.0/pr_3002.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..511a07e39c47d5055bd161fda0e748f96e4a9997 --- /dev/null +++ b/prdoc/1.9.0/pr_3002.prdoc @@ -0,0 +1,29 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: PoV Reclaim Runtime Side +author: skunert +topic: runtime +doc: + - audience: Runtime Dev + description: | + Adds a mechanism to reclaim proof size weight. + 1. Introduces a new `SignedExtension` that reclaims the difference + between benchmarked proof size weight and actual consumed proof size weight. + 2. Introduces a manual mechanism, `StorageWeightReclaimer`, to reclaim excess storage weight for situations + that require manual weight management. The most prominent case is the `on_idle` hook. + 3. Adds the `storage_proof_size` host function to the PVF. Parachain nodes should add it to ensure compatibility. + + To enable proof size reclaiming, add the host `storage_proof_size` host function to the parachain node. Add the + `StorageWeightReclaim` `SignedExtension` to your runtime and enable proof recording during block import. + + +crates: + - name: "cumulus-primitives-storage-weight-reclaim" +host_functions: + - name: "storage_proof_size" + description: | + This host function is used to pass the current size of the storage proof to the runtime. + It was introduced before but becomes relevant now. + Note: This host function is intended to be used through `cumulus_primitives_storage_weight_reclaim::get_proof_size`. + Direct usage is not recommended. diff --git a/prdoc/1.9.0/pr_3187.prdoc b/prdoc/1.9.0/pr_3187.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..bda41142d86c8f9c1af8398e0cf21cdfeebebfa7 --- /dev/null +++ b/prdoc/1.9.0/pr_3187.prdoc @@ -0,0 +1,16 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Retrying an execution on failed runtime construction + +doc: + - audience: Node Dev + description: | + If a runtime construction error happened during the execution request, then the artifact is re-prepared + and the execution request is retried at most once. See also the related issue. + +crates: + - name: polkadot-node-core-candidate-validation + - name: polkadot-node-core-pvf + - name: polkadot-node-core-pvf-execute-worker + - name: polkadot-node-core-pvf-common diff --git a/prdoc/1.9.0/pr_3231.prdoc b/prdoc/1.9.0/pr_3231.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..26e96d3635b14634ca523797d741ddaf224aa9ae --- /dev/null +++ b/prdoc/1.9.0/pr_3231.prdoc @@ -0,0 +1,11 @@ +title: Allow parachain which acquires multiple coretime cores to make progress + +doc: + - audience: Node Operator + description: | + Adds the needed changes so that parachains which acquire multiple coretime cores can still make progress. + Only one of the cores will be able to be occupied at a time. + Only works if the ElasticScalingMVP node feature is enabled in the runtime and the block author validator is + updated to include this change. + +crates: [ ] diff --git a/prdoc/1.9.0/pr_3233.prdoc b/prdoc/1.9.0/pr_3233.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..ed4e8cce31f75b4bd98f91f7d15ec3fae0761354 --- /dev/null +++ b/prdoc/1.9.0/pr_3233.prdoc @@ -0,0 +1,12 @@ +title: "provisioner: allow multiple cores assigned to the same para" + +topic: Node + +doc: + - audience: Node Dev + description: | + Enable supplying multiple backable candidates to the paras_inherent pallet for the same paraid. + +crates: + - name: polkadot-node-core-prospective-parachains + - name: polkadot-node-core-provisioner diff --git a/prdoc/1.9.0/pr_3324.prdoc b/prdoc/1.9.0/pr_3324.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..0425fbf317c8620125e9fe64f96a0eb99e4595f0 --- /dev/null +++ b/prdoc/1.9.0/pr_3324.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Fix weight calculation and event emission in pallet-membership" + +doc: + - audience: Runtime Dev + description: | + Bug fix for the membership pallet to use correct weights. Also no event will be emitted + anymore when `change_key` is called with identical accounts. + +crates: + - name: pallet-membership diff --git a/prdoc/1.9.0/pr_3371.prdoc b/prdoc/1.9.0/pr_3371.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..605d540772f0b249962088463237dda2e6690d6c --- /dev/null +++ b/prdoc/1.9.0/pr_3371.prdoc @@ -0,0 +1,19 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: removed `pallet::getter` from example pallets + +doc: + - audience: Runtime Dev + description: | + This PR removes all the `pallet::getter` usages from the template pallets found in the Substrate and Cumulus template nodes, and from the Substrate example pallets. + The purpose is to discourage developers to use this macro, that is currently being removed and soon will be deprecated. + +crates: + - name: pallet-template + - name: pallet-parachain-template + - name: pallet-example-basic + - name: pallet-example-kitchensink + - name: pallet-example-offchain-worker + - name: pallet-example-split + diff --git a/prdoc/1.9.0/pr_3377.prdoc b/prdoc/1.9.0/pr_3377.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..8e5b3935512b1b2dd12836e940a5d95040df8cf2 --- /dev/null +++ b/prdoc/1.9.0/pr_3377.prdoc @@ -0,0 +1,14 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Permissioned contract deployment + +doc: + - audience: Runtime Dev + description: | + This PR introduces two new config types that specify the origins allowed to + upload and instantiate contract code. However, this check is not enforced when + a contract instantiates another contract. + +crates: +- name: pallet-contracts diff --git a/prdoc/1.9.0/pr_3378.prdoc b/prdoc/1.9.0/pr_3378.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..2470fc158519e31e297a7cbefcae3bc54eb8f708 --- /dev/null +++ b/prdoc/1.9.0/pr_3378.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Remove deprecated GenesisConfig + +doc: + - audience: Runtime Dev + description: | + Removes deprecated type `GenesisConfig`, it was replaced by `RuntimeGenesisConfig` on May 24 of 2023. + The type `GenesisConfig` was deprecated on May 24 of 2023 [#14210](https://github.com/paritytech/substrate/pull/14210) + +crates: + - name: frame-support-procedural \ No newline at end of file diff --git a/prdoc/1.9.0/pr_3403.prdoc b/prdoc/1.9.0/pr_3403.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..086e769da3c39c4d3def42791c18a82948676386 --- /dev/null +++ b/prdoc/1.9.0/pr_3403.prdoc @@ -0,0 +1,22 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Add `claim_assets` extrinsic to `pallet-xcm`" + +doc: + - audience: Runtime User + description: | + There's a new extrinsic in `pallet-xcm` for claiming assets. + This means that if your assets ever get trapped while teleporting or doing reserve asset transfers, + you can easily claim them by calling this new extrinsic. + - audience: Runtime Dev + description: | + There's a new extrinsic in `pallet-xcm` that needs a new configuration item for its benchmarks. + It's a simple function in `pallet_xcm::benchmarking::Config`, `get_asset`, that returns a valid asset + handled by the AssetTransactor of the chain. + If you're already using `pallet-xcm-benchmarks`, then you already have this function there and can + just copy and paste it. + +crates: + - name: pallet-xcm + - name: staging-xcm diff --git a/prdoc/1.9.0/pr_3411.prdoc b/prdoc/1.9.0/pr_3411.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..d847d6756ac7f019cf12e4d9e7993ebeda1273ab --- /dev/null +++ b/prdoc/1.9.0/pr_3411.prdoc @@ -0,0 +1,11 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: add Encointer as trusted teleporter for Westend + +doc: + - audience: Runtime Dev + description: | + add Encointer as trusted teleporter for Westend with ParaId 1003 + +crates: [ ] diff --git a/prdoc/1.9.0/pr_3412.prdoc b/prdoc/1.9.0/pr_3412.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1ee6edfeb837f81646d32633ed009d2cded4dcfe --- /dev/null +++ b/prdoc/1.9.0/pr_3412.prdoc @@ -0,0 +1,17 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "[FRAME] Add genesis test and remove some checks" + +doc: + - audience: Runtime Dev + description: | + The construct_runtime macro now generates a test to assert that all `GenesisConfig`s of all + pallets can be build within the runtime. This ensures that the `BuildGenesisConfig` runtime + API works. + Further, some checks from a few pallets were removed to make this pass. + +crates: + - name: pallet-babe + - name: cumulus-pallet-aura-ext + - name: pallet-session diff --git a/prdoc/1.9.0/pr_3447.prdoc b/prdoc/1.9.0/pr_3447.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1d8d4f409f77cf29247d35d196bccff390896881 --- /dev/null +++ b/prdoc/1.9.0/pr_3447.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Use generic hash for runtime wasm in resolve_state_version_from_wasm + +doc: + - audience: Node Dev + description: | + Changes the runtime hash algorithm used in resolve_state_version_from_wasm from DefaultHasher to a caller-provided + one (usually HashingFor). Fixes a bug where the runtime wasm was being compiled again when it was not + needed, because the hash did not match +crates: + - name: sc-chain-spec diff --git a/prdoc/1.9.0/pr_3453.prdoc b/prdoc/1.9.0/pr_3453.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1f01440f722fb9984231f690a20c517b9d4bf0a1 --- /dev/null +++ b/prdoc/1.9.0/pr_3453.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "[pallet-nomination-pools]: `chill` is permissionless if depositor's stake is less than `min_nominator_bond`" + +doc: + - audience: Runtime Dev + description: | + Nomination pools currently have an issue whereby member funds cannot be unbonded if the depositor bonded amount is less than `MinNominatorBond`. + This PR makes the `chill` function permissionless if this condition is met. + Consequently, `nominate` function also checks for `depositor` to have at least `MinNominatorBond`, and returns `MinimumBondNotMet` error if not. +crates: + - name: pallet-nomination-pools diff --git a/prdoc/1.9.0/pr_3454.prdoc b/prdoc/1.9.0/pr_3454.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..7f564258f23a57f3784c2ee055176153f006734f --- /dev/null +++ b/prdoc/1.9.0/pr_3454.prdoc @@ -0,0 +1,19 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Introduce storage attr macro #[disable_try_decode_storage] and set it on System::Events and ParachainSystem::HostConfiguration" + +doc: + - audience: Runtime Dev + description: | + Allows marking storage items with \#[disable_try_decode_storage], which disables that storage item from being decoded + during try_decode_entire_state calls. + + Applied the attribute to System::Events to close https://github.com/paritytech/polkadot-sdk/issues/2560. + Applied the attribute to ParachainSystem::HostConfiguration to resolve periodic issues with it. + +crates: + - name: frame-support-procedural + - name: frame-system + - name: cumulus-pallet-parachain-system + diff --git a/prdoc/1.9.0/pr_3456.prdoc b/prdoc/1.9.0/pr_3456.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c7327e17e57d74614fd1b593cc222faf4a87192d --- /dev/null +++ b/prdoc/1.9.0/pr_3456.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Removed `pallet::getter` usage from `pallet-collective` + +doc: + - audience: Runtime Dev + description: | + This PR removes `pallet::getter` usage from `pallet-collective`, and updates dependant code accordingly. + The syntax `StorageItem::::get()` should be used instead. + +crates: + - name: pallet-collective + - name: kitchensink-runtime + - name: collectives-westend-runtime diff --git a/prdoc/1.9.0/pr_3460.prdoc b/prdoc/1.9.0/pr_3460.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1f16fe0890836dd25bfcaa75d5fc647688230dd8 --- /dev/null +++ b/prdoc/1.9.0/pr_3460.prdoc @@ -0,0 +1,26 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Repot all templates + +doc: + - audience: Runtime Dev + description: | + This PR moves all templates into a single folder in the polkadot-sdk repo (`/templates`) and + unifies their crate names as well. Most notably, the crate name for what was formerly known + as `node-template` is no `solochain-template-node`. The other two crates in the template are + consequently called: `solochain-runtime-template` and `pallet-solochain-template`. + The other two template crate names follow a similar patter, just replacing `solochain` with + `parachain` or `minimal`. + + This PR is part of a bigger step toward automating the template repositories, see the + following: https://github.com/paritytech/polkadot-sdk/issues/3155 + +# the following crates are removed and renamed, although none are released. +crates: + - name: minimal-template-runtime # formerly called minimal-runtime + - name: minimal-template-node # formerly called minimal-node + - name: solochain-template-node # formerly called node-template + - name: solochain-template-runtime # formerly called node-template-runtime + - name: parachain-template-runtime # formerly called parachain-runtime + - name: parachain-template-runtime # formerly called parachain-node diff --git a/prdoc/1.9.0/pr_3491.prdoc b/prdoc/1.9.0/pr_3491.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e36afb916a6226c3609b1e2eb8aacf6af8127d32 --- /dev/null +++ b/prdoc/1.9.0/pr_3491.prdoc @@ -0,0 +1,12 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Remove Deprecated OldWeight + +doc: + - audience: Runtime Dev + description: | + Removed deprecated sp_weights::OldWeight type. Use [`weight_v2::Weight`] instead. + +crates: + - name: frame-support diff --git a/prdoc/1.9.0/pr_3504.prdoc b/prdoc/1.9.0/pr_3504.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..90a8ddd38b8f7f8159199ac6423ffee3dc645291 --- /dev/null +++ b/prdoc/1.9.0/pr_3504.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: add prometheus label "is_rate_limited" to rpc calls + +doc: + - audience: Node Operator + description: | + This PR adds a label "is_rate_limited" to the prometheus metrics "substrate_rpc_calls_time" and "substrate_rpc_calls_finished" + than can be used to distinguish rate-limited RPC calls from other RPC calls. Because rate-limited RPC calls may take + tens of seconds. + +crates: [ ] diff --git a/prdoc/1.9.0/pr_3505.prdoc b/prdoc/1.9.0/pr_3505.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..b55972927c33002c918aa4b1cd147e9ee01a3e52 --- /dev/null +++ b/prdoc/1.9.0/pr_3505.prdoc @@ -0,0 +1,36 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Removes `as [disambiguation_path]` from the required syntax in `derive_impl` + +doc: + - audience: Runtime Dev + description: | + This PR removes the need to specify `as [disambiguation_path]` for cases where the trait + definition resides within the same scope as default impl path. + + For example, in the following macro invocation + ```rust + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] + impl frame_system::Config for Runtime { + ... + } + ``` + the trait `DefaultConfig` lies within the `frame_system` scope and `TestDefaultConfig` impls + the `DefaultConfig` trait. + + Using this information, we can compute the disambiguation path internally, thus removing the + need of an explicit specification: + ```rust + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] + impl frame_system::Config for Runtime { + ... + } + ``` + + In cases where the trait lies outside this scope, we would still need to specify it explicitly, + but this should take care of most (if not all) uses of `derive_impl` within FRAME's context. + +crates: + - name: frame-support-procedural + - name: pallet-default-config-example diff --git a/prdoc/1.9.0/pr_3510.prdoc b/prdoc/1.9.0/pr_3510.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..6ee2f9a81f750ef0c64a2b8d8197e1e44d7e9f0c --- /dev/null +++ b/prdoc/1.9.0/pr_3510.prdoc @@ -0,0 +1,13 @@ +title: "Fix multi-collator parachain transition to async backing" + +doc: + - audience: Node Operator + description: | + The dynamic Aura slot duration, introduced in PR#3211, didn't take the block import pipeline + into account. The result was the parachain backed by multiple collators not being able to + keep producing blocks after its runtime was upgraded to support async backing, requiring to + restart all the collator nodes. This change fixes the issue, introducing the dynamic Aura + slot duration into the block import pipeline. + +crates: + - name: "polkadot-parachain-bin" diff --git a/prdoc/1.9.0/pr_3513.prdoc b/prdoc/1.9.0/pr_3513.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e1f2afe280a5bc2e8d341f9ab5ac73315f1676e8 --- /dev/null +++ b/prdoc/1.9.0/pr_3513.prdoc @@ -0,0 +1,18 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix call enum's metadata regression + +doc: + - audience: Runtime Dev + - audience: Runtime User + description: | + This PR fixes an issue where in the metadata of all FRAME-based chains, the documentation for + all extrinsic/call/transactions has been replaced by a single line saying "see Pallet::name". + + Many wallets display the metadata of transactions to the user before signing, and this bug + might have affected this process. + +crates: + - name: frame + - name: frame-support diff --git a/prdoc/1.9.0/pr_3523.prdoc b/prdoc/1.9.0/pr_3523.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c31d9d096dc00797254f7946fd44984636cb1547 --- /dev/null +++ b/prdoc/1.9.0/pr_3523.prdoc @@ -0,0 +1,19 @@ +title: Fix crash of synced parachain node run with `--sync=warp` + +doc: + - audience: Node Operator + description: | + Fix crash of `SyncingEngine` when an already synced parachain node is run with `--sync=warp` + (issue https://github.com/paritytech/polkadot-sdk/issues/3496). + The issue manifests itself by errors in the logs: + ``` + [Parachain] Cannot set warp sync target block: no warp sync strategy is active. + [Parachain] Failed to set warp sync target block header, terminating `SyncingEngine`. + ``` + Followed by a stream of messages: + ``` + [Parachain] Protocol command streams have been shut down + ``` + +crates: + - name: sc-network-sync diff --git a/prdoc/1.9.0/pr_3532.prdoc b/prdoc/1.9.0/pr_3532.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..d47c8d1d49156aca4ac1b7683ee7fbd9d8e9e690 --- /dev/null +++ b/prdoc/1.9.0/pr_3532.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Remove deprecated `trait Store` + +doc: + - audience: Runtime Dev + description: | + The deprecated `trait Store` feature has been removed from the codebase. Please remove usages of `generate_store` + macro from your pallets and access the storage through generics. For example, + `::StoredRange::mutate` will need to be updated to `StoredRange::::mutate`. + +crates: + - name: frame-support + - name: frame \ No newline at end of file diff --git a/prdoc/1.9.0/pr_3540.prdoc b/prdoc/1.9.0/pr_3540.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..d0a91882b6bf4e357fde899c2d939ddb7de62f34 --- /dev/null +++ b/prdoc/1.9.0/pr_3540.prdoc @@ -0,0 +1,10 @@ +title: "[pallet-contracts] Only allow non-deterministic code to be uploaded with Determinism::Relaxed" + +doc: + - audience: Runtime Dev + description: | + The `upload_code` extrinsic, will now only allow non-deterministic code to be uploaded with the `Determinism::Relaxed` flag. + This prevent an attacker from uploading "deterministic" code with the `Determinism::Relaxed` flag, preventing the code to be instantiated for on-chain execution. + +crates: + - name: pallet-contracts diff --git a/prdoc/1.9.0/pr_3574.prdoc b/prdoc/1.9.0/pr_3574.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..99868ea8a132a6440b9daff0d9f7deaa329ca4b3 --- /dev/null +++ b/prdoc/1.9.0/pr_3574.prdoc @@ -0,0 +1,23 @@ +title: Generate test functions for each benchmark with benchmarking v2 + +doc: + - audience: Runtime Dev + description: | + This PR fixes an issue where using `impl_benchmark_test_suite` macro + within modules that use the benchmarking v2 macros (`#[benchmarks]` + and `#[instance_benchmarks]`) always produced a single test called + `test_benchmarks` instead of a separate benchmark test for every + benchmark (noted with the `#[benchmark]` macro). + + By using this macro from now on, new tests will be created named + `test_benchmark_{name}` where `name` is the name of the benchmark + function. Those tests will be nested inside the module intended for + benchmark functions. + + Also, when using `impl_benchmark_test_suite` inside the module, + the import of such marco will not be necessary, so any explicit + import of it will be marked as unused, the same way it works for + v1 macros so far. + +crates: + - name: frame-support-procedural diff --git a/prdoc/1.9.0/pr_3589.prdoc b/prdoc/1.9.0/pr_3589.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..98b9b1039ab49f1dc62580d56327bf7944ed3372 --- /dev/null +++ b/prdoc/1.9.0/pr_3589.prdoc @@ -0,0 +1,10 @@ +title: Removed `pallet::getter` usage from `pallet-im-online` + +doc: + - audience: Runtime Dev + description: | + This PR removes `pallet::getter` usage from `pallet-im-online`, and updates dependant code accordingly. + The syntax `StorageItem::::get()` should be used instead. + +crates: + - name: pallet-im-online diff --git a/prdoc/1.9.0/pr_3606.prdoc b/prdoc/1.9.0/pr_3606.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..18b71de9477ea516de2bcd5ecfab52a6a5479afb --- /dev/null +++ b/prdoc/1.9.0/pr_3606.prdoc @@ -0,0 +1,9 @@ +title: "[pallet_contracts] mark lock/unlock_delegate_dependency as stable" + +doc: + - audience: Runtime Dev + description: | + Lock and unlock delegate dependency are stable now, so we can mark them as such. + +crates: + - name: pallet-contracts diff --git a/prdoc/1.9.0/pr_3636.prdoc b/prdoc/1.9.0/pr_3636.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..934158ac1022dc14309696bea43492db8f7a0e88 --- /dev/null +++ b/prdoc/1.9.0/pr_3636.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "[pallet_broker] Fix `Linear::adapt_price` behavior at zero" + +doc: + - audience: Runtime Dev + description: | + This fixes the behaviour of `Linear` which is the default implementation of the `AdaptPrice` + trait in the broker pallet. Previously if cores were offered but not sold in only one sale, + the price would be set to zero and due to the logic being purely multiplicative, the price + would stay at 0 indefinitely. + +crates: + - name: pallet-broker diff --git a/prdoc/1.9.0/pr_3639.prdoc b/prdoc/1.9.0/pr_3639.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..46e3f6f4d76394ded0c7c76458e1a71148ea5cb4 --- /dev/null +++ b/prdoc/1.9.0/pr_3639.prdoc @@ -0,0 +1,19 @@ +title: Prevents staking controllers from becoming stashes of different ledgers; Ensures that no ledger in bad state is mutated. + +doc: + - audience: Runtime User + description: | + This PR introduces a fix to the staking logic which prevents an existing controller from bonding as a stash of another ledger, which + lead to staking ledger inconsistencies down the line. In addition, it adds a few (temporary) gates to prevent ledgers that are already + in a bad state from mutating its state. + + In summary: + * Checks if stash is already a controller when calling `Call::bond` and fails if that's the case; + * Ensures that all fetching ledgers from storage are done through the `StakingLedger` API; + * Ensures that a `Error::BadState` is returned if the ledger bonding is in a bad state. This prevents bad ledgers from mutating (e.g. + `bond_extra`, `set_controller`, etc) its state and avoid further data inconsistencies. + * Prevents stashes which are controllers or another ledger from calling `set_controller`, since that may lead to a bad state. + * Adds further try-state runtime checks that check if there are ledgers in a bad state based on their bonded metadata. + +crates: +- name: pallet-staking diff --git a/prdoc/1.9.0/pr_3643.prdoc b/prdoc/1.9.0/pr_3643.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..6e85af8d99a07f397ce03d4b7c936f7560f36c8f --- /dev/null +++ b/prdoc/1.9.0/pr_3643.prdoc @@ -0,0 +1,12 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix weight refund for `pallet_collator_selection::set_candidacy_bond` + +doc: + - audience: Runtime Dev + description: | + This PR implements the weight refund of `pallet_collator_selection::set_candidacy_bond` to + account for no iterations over the candidate list when the candidacy bond is decreased. +crates: + - name: pallet-collator-selection diff --git a/prdoc/1.9.0/pr_3665.prdoc b/prdoc/1.9.0/pr_3665.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..67725d24d185502871baf41ba65b856449c4dfc3 --- /dev/null +++ b/prdoc/1.9.0/pr_3665.prdoc @@ -0,0 +1,11 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Revert "FRAME Create TransactionExtension as a replacement for SignedExtension (#2280)" + +doc: + - audience: Runtime Dev + description: | + This PR reverts the PR which introduced `TransactionExtension` to replace `SignedExtension`. + +crates: [ ] diff --git a/prdoc/pr_3190.prdoc b/prdoc/pr_3190.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..2f7a89a0b1aba611b10625f0d7833b3f3dfd4e6e --- /dev/null +++ b/prdoc/pr_3190.prdoc @@ -0,0 +1,17 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix algorithmic complexity of the on-demand scheduler. + +doc: + - audience: Runtime Dev + description: | + Improves on demand performance by a significant factor. Previously, having many on-demand cores + would cause really poor blocktimes due to the fact that for each core the full order queue was + processed. This allows for increasing the max size of the on-demand queue if needed. + + At the same time, the spot price for on-demand is now checked prior to every order, ensuring + that economic backpressure will be applied. + +crates: + - name: polkadot-runtime-parachains diff --git a/prdoc/pr_3302.prdoc b/prdoc/pr_3302.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..a2d93fc607355542c39785742231e6da705e27da --- /dev/null +++ b/prdoc/pr_3302.prdoc @@ -0,0 +1,15 @@ +title: Collator protocol changes for elastic scaling + +doc: + - audience: Node Dev + description: | + This PR introduces changes to the collator protocol to support elastic scaling. + Namely, a new variant added to the collation response to include parent head-data + along with the collation. Currently, the new variant is not being used. + - audience: Node Operator + description: | + Validators are required to upgrade to this version before collators in order to + support the elastic scaling of parachains. + +crates: + - name: polkadot-collator-protocol diff --git a/prdoc/pr_3350.prdoc b/prdoc/pr_3350.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1171614d67f65e8d75a2d3b4f6545a87bfb0ac0e --- /dev/null +++ b/prdoc/pr_3350.prdoc @@ -0,0 +1,31 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: removed `pallet::getter` from Pallet AURA + +doc: + - audience: Runtime Dev + description: | + This PR removes all the declarations of macro `pallet::getter` in the Pallet AURA and replaces the use of storage getter functions authorities() & current_slot() with `StorageItem::get()` API across the crates as listed bellow. + The purpose is to discourage developers to use this macro, that is currently being removed and soon will be deprecated. + +crates: + - name: pallet-aura + - name: solochain-template-runtime + - name: cumulus-pallet-aura-ext + - name: parachain-template-runtime + - name: people-westend-runtime + - name: people-rococo-runtime + - name: bridge-hub-rococo-runtime + - name: bridge-hub-westend-runtime + - name: rococo-parachain-runtime + - name: penpal-runtime + - name: glutton-westend-runtime + - name: shell-runtime + - name: seedling-runtime + - name: collectives-westend-runtime + - name: asset-hub-rococo-runtime + - name: asset-hub-westend-runtime + - name: contracts-rococo-runtime + - name: coretime-westend-runtime + - name: coretime-rococo-runtime diff --git a/prdoc/pr_3471.prdoc b/prdoc/pr_3471.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..0c16587fc9086c6173e6917cdbf9c434599cdcd6 --- /dev/null +++ b/prdoc/pr_3471.prdoc @@ -0,0 +1,33 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: removed `pallet::getter` from cumulus pallets + +doc: + - audience: Runtime Dev + description: | + This PR removes all the `pallet::getter` usages from cumulus pallets, and updates depdendant runtimes accordingly. + The ParaId can be retrieved using `>::get()`. + For other storage items, the syntax `StorageItem::::get()` should be used instead. + +crates: + - name: cumulus-pallet-aura-ext + - name: pallet-collator-selection + - name: cumulus-pallet-parachain-system + - name: staging-parachain-info + - name: parachain-template-runtime + - name: asset-hub-rococo-runtime + - name: asset-hub-westend-runtime + - name: bridge-hub-rococo-runtime + - name: bridge-hub-westend-runtime + - name: collectives-westend-runtime + - name: contracts-rococo-runtime + - name: coretime-rococo-runtime + - name: coretime-westend-runtime + - name: glutton-westend-runtime + - name: people-rococo-runtime + - name: people-westend-runtime + - name: shell-runtime + - name: penpal-runtime + - name: rococo-parachain-runtime + diff --git a/prdoc/pr_3479.prdoc b/prdoc/pr_3479.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1e44ce5646b92b5e893b33a56648100df45ac449 --- /dev/null +++ b/prdoc/pr_3479.prdoc @@ -0,0 +1,8 @@ +title: "Elastic scaling: runtime dependency tracking and enactment" + +doc: + - audience: Node Dev + description: | + Adds support in the inclusion and paras_inherent runtime modules for backing and including multiple candidates of the same para if they form a chain. + +crates: [ ] diff --git a/prdoc/pr_3521.prdoc b/prdoc/pr_3521.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..4ad3f03bf0c5eea885769285fd29ef76d3214111 --- /dev/null +++ b/prdoc/pr_3521.prdoc @@ -0,0 +1,12 @@ +title: Collator side changes for elastic scaling + +doc: + - audience: Node Dev + description: | + Parachain teams wishing to utilize the benefits of + elastic scaling will need to upgrade their collator + code to include these changes. + +crates: +- name: polkadot-collator-protocol + bump: minor diff --git a/prdoc/pr_3580.prdoc b/prdoc/pr_3580.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..042fcf7a1a849f3a704a6fe81506071dbc57df36 --- /dev/null +++ b/prdoc/pr_3580.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Expose `ClaimQueue` via a runtime api and consume it in `collation-generation` + +doc: + - audience: Node Dev + description: | + Creates a new runtime api exposing the `ClaimQueue` from `scheduler` pallet. Consume the api + in collation generation (if available) by getting what's scheduled on a core from the + `ClaimQueue` instead of from `next_up_on_available` (from `AvailabilityCores` runtime api). + +crates: [ ] diff --git a/prdoc/pr_3607.prdoc b/prdoc/pr_3607.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1a69b25ad255ca1d2e2f717a75db763360e7f4fc --- /dev/null +++ b/prdoc/pr_3607.prdoc @@ -0,0 +1,26 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "XCM fee payment API" + +doc: + - audience: Runtime Dev + description: | + A runtime API was added for estimating the fees required for XCM execution and delivery. + This is the basic building block needed for UIs to accurately estimate fees. + An example implementation is shown in the PR. Ideally it's simple to implement, you only need to call existing parts of your XCM config. + The API looks like so: + ```rust + fn query_acceptable_payment_assets(xcm_version: Version) -> Result, Error>; + fn query_xcm_weight(message: VersionedXcm) -> Result; + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result; + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result; + ``` + The first three relate to XCM execution fees, given an XCM, you can query its weight, then which assets are acceptable for buying weight and convert weight to a number of those assets. + The last one takes in a destination and a message you want to send from the runtime you're executing this on, it will give you the delivery fees. + +crates: + - name: xcm-fee-payment-runtime-api + - name: rococo-runtime + - name: westend-runtime + diff --git a/prdoc/pr_3616.prdoc b/prdoc/pr_3616.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..fcf068dcd1739365f9cf04782efea3503327f3d4 --- /dev/null +++ b/prdoc/pr_3616.prdoc @@ -0,0 +1,28 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Benchmarking pallet V2 syntax extension: pov_mode attribute" + +doc: + - audience: Runtime Dev + description: | + Adds the `pov_mode` attribute from the V1 benchmarking syntax to the V2 syntax. This allows to + override the default PoV mode (`MaxEncodedLen`) to either `Measured` or `Ignored`. It can be + overridden for a whole benchmark, a key prefix of a specific key itself. + + Example syntax looks like this: + ```rust + #[benchmark(pov_mode = Measured { + Pallet: Measured, + Pallet::Storage: MaxEncodedLen, + })] + fn do_some() { + .. + } + ``` + +crates: + - name: frame-support-procedural + bump: minor + - name: frame-support + bump: minor diff --git a/prdoc/pr_3654.prdoc b/prdoc/pr_3654.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..de4a68d23569d130e5df0d0400edbe498e3875e4 --- /dev/null +++ b/prdoc/pr_3654.prdoc @@ -0,0 +1,14 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Remove `experimental` feature from `pallet-aura` + +doc: + - audience: Runtime Dev + description: | + The `experimental` feature in `pallet-aura`, previously used to gate different experimental + changes, became redundant with the introduction of the async backing which relies on the + mentioned changes, therefore, it is removed. + +crates: + - name: pallet-aura diff --git a/prdoc/pr_3696.prdoc b/prdoc/pr_3696.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..f3371d1734adf3e9281a83f900950129817ccc46 --- /dev/null +++ b/prdoc/pr_3696.prdoc @@ -0,0 +1,17 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Add HRMP notification handlers to the xcm-executor + +doc: + - audience: Runtime Dev + description: | + Adds optional HRMP notification handlers to the xcm-executor. These handlers are 3 new config types on the xcm-executor `Config` trait: + - `HrmpNewChannelOpenRequestHandler` + - `HrmpChannelAcceptedHandler` + - `HrmpChannelClosingHandler` + + The traits of these config types are implemented on tuples, and on `()` for the default case. + +crates: + - name: staging-xcm-executor diff --git a/prdoc/pr_3714.prdoc b/prdoc/pr_3714.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e276d0d2d3731c2cf626a54f4812b5167269bca6 --- /dev/null +++ b/prdoc/pr_3714.prdoc @@ -0,0 +1,10 @@ +title: Handle legacy lease swaps on coretime + +doc: + - audience: Runtime Dev + description: | + When a `registar::swap` extrinsic is executed it swaps two leases on the relay chain but the + broker chain never knows about this swap. This change notifies the broker chain via a XCM + message for a swap so that it can update its state. +crates: + - name: pallet-broker diff --git a/prdoc/pr_3722.prdoc b/prdoc/pr_3722.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..7e2d7d38795b70213a4fb737a7c7cf4703035272 --- /dev/null +++ b/prdoc/pr_3722.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix kusama 0 backing rewards when entering active set + +doc: + - audience: Runtime Dev + description: | + This PR fixes getting 0 backing rewards the first session when + a node enters the active set. + +crates: + - name: pallet-authority-discovery diff --git a/prdoc/pr_3740.prdoc b/prdoc/pr_3740.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..03df8ec5fea0282136ca9d5b1912f1257de7ed1e --- /dev/null +++ b/prdoc/pr_3740.prdoc @@ -0,0 +1,18 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Removed `pallet::getter` usage from Beefy and MMR pallets + +doc: + - audience: Runtime Dev + description: | + This PR removes `pallet::getter` usage from `pallet-beefy`, `pallet-beefy-mmr` and `pallet-mmr`, and updates dependant code and runtimes accordingly. + The syntax `StorageItem::::get()` should be used instead. + +crates: + - name: pallet-beefy + - name: pallet-beefy-mmr + - name: pallet-mmr + - name: kitchensink-runtime + - name: rococo-runtime + - name: westend-runtime diff --git a/prdoc/pr_3749.prdoc b/prdoc/pr_3749.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1ebde9670e0f374c8dd079f9d66ec84894b71f6b --- /dev/null +++ b/prdoc/pr_3749.prdoc @@ -0,0 +1,47 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "pallet-xcm: deprecate execute and send in favor of execute_blob and send_blob" + +doc: + - audience: Runtime Dev + description: | + pallet-xcm's extrinsics `execute` and `send` have been marked as deprecated. + Please change their usage to the new `execute_blob` and `send_blob`. + The migration from the old extrinsic to the new is very simple. + If you have your message `xcm: VersionedXcm`, then instead of passing in + `Box::new(xcm)` to both `execute` and `send`, you would pass in + `xcm.encode().try_into()` and handle the potential error of its encoded length + being bigger than `MAX_XCM_ENCODED_SIZE`. + + pallet-contracts takes the XCM encoded now as well. It follows the same API as + `execute_blob` and `send_blob`. + - audience: Runtime User + description: | + pallet-xcm has a new pair of extrinsics, `execute_blob` and `send_blob`. + These are meant to be used instead of `execute` and `send`, which are now deprecated + and will be removed eventually. + These new extrinsics just require you to input the encoded XCM. + There's a new utility in PolkadotJS Apps for encoding XCMs you can use: + https://polkadot.js.org/apps/#/utilities/xcm + Just pass in the encoded XCM to the new extrinsics and you're done. + + pallet-contracts takes the XCM encoded now as well. It follows the same API as + `execute_blob` and `send_blob`. + +crates: +- name: pallet-xcm +- name: staging-xcm +- name: staging-xcm-builder +- name: pallet-contracts +- name: asset-hub-rococo-runtime +- name: asset-hub-westend-runtime +- name: bridge-hub-rococo-runtime +- name: bridge-hub-westend-runtime +- name: collectives-westend-runtime +- name: coretime-rococo-runtime +- name: coretime-westend-runtime +- name: people-rococo-runtime +- name: people-westend-runtime +- name: rococo-runtime +- name: westend-runtime diff --git a/prdoc/pr_3754.prdoc b/prdoc/pr_3754.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..94ea6d566088309cbad05d5eed3f248af716c3bc --- /dev/null +++ b/prdoc/pr_3754.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Migrates Westend to Runtime V2 + +doc: + - audience: Runtime Dev + description: | + This PR migrates Westend from `construct_runtime` to Runtime V2 + as introduced in https://github.com/paritytech/polkadot-sdk/pull/1378 + +crates: + - name: westend-runtime diff --git a/prdoc/pr_3792.prdoc b/prdoc/pr_3792.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..cbcdc29a9c6e434403b5d2548a44eb468e6ba30e --- /dev/null +++ b/prdoc/pr_3792.prdoc @@ -0,0 +1,19 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "[pallet-xcm] fix transport fees for remote reserve transfers" + +doc: + - audience: Runtime Dev + description: | + This PR fixes `pallet_xcm::transfer_assets` and + `pallet_xcm::limited_reserve_transfer_assets` extrinsics for transfers + that need to go through remote reserves. The fix is adding a + `SetFeesMode { jit_withdraw: true }` instruction before local execution of + `InitiateReserveWithdraw` so that delivery fees are correctly charged by + the xcm-executor. Without this change, a runtime that has implemented + delivery fees would not be able to execute remote reserve transfers using + these extrinsics. + +crates: + - name: pallet-xcm diff --git a/prdoc/pr_3808.prdoc b/prdoc/pr_3808.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9b50f721df335891130f9ef63c0bf144792beebb --- /dev/null +++ b/prdoc/pr_3808.prdoc @@ -0,0 +1,20 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix spelling mistakes in source code + +doc: + - audience: Node Operator + description: | + Some spelling mistakes in log output, error messages and tracing (prometheus/grafana) have been fixed. + - audience: Runtime Dev + description: | + Public crate changes: + - The public trait `RuntimeParameterStore` in `substrate/frame/support` had the associated type renamed from `AggregratedKeyValue` to `AggregatedKeyValue`. + - The public trait `AggregratedKeyValue` in `substrate/frame/support` was similarly renamed to `AggregatedKeyValue`. + - The public methods `test_versioning` and `test_versioning_register_only` of the `TestApi` trait in `substrate/primitives/runtime-interface/test-wasm` had the spelling of `versionning` changed to `versioning`. + - The public functions `read_trie_first_descendant_value` and `read_child_trie_first_descendant_value` in `substrate/primitives/trie` had the spelling of `descedant` changed to `descendant`. +crates: + - name: frame-support + - name: sp-runtime-interface-test-wasm + - name: sp-trie diff --git a/prdoc/schema_user.json b/prdoc/schema_user.json index 82215d51866b35895b5e840a8f3a900b161a9cf6..1bd0b3b93ee45ea75d9781ef1485898dd86c7ff3 100644 --- a/prdoc/schema_user.json +++ b/prdoc/schema_user.json @@ -3,9 +3,8 @@ "$id": "https://raw.githubusercontent.com/paritytech/prdoc/master/prdoc_schema_user.json", "version": { "major": 1, - "minor": 0, - "patch": 0, - "timestamp": 20230817152351 + "minor": 1, + "patch": 0 }, "title": "Polkadot SDK PRDoc Schema", "description": "JSON Schema definition for the Polkadot SDK PR documentation", @@ -125,10 +124,16 @@ "name": { "type": "string" }, + "bump": { + "$ref": "#/$defs/semver_bump" + }, "note": { "type": "string" } - } + }, + "required": [ + "name" + ] }, "migration_db": { "type": "object", @@ -165,6 +170,26 @@ "description" ] }, + "semver_bump": { + "description": "The type of bump to apply to the crate version according to Cargo SemVer: https://doc.rust-lang.org/cargo/reference/semver.html. Please check docs/RELEASE.md for more information.", + "oneOf": [ + { + "const": "major", + "title": "Major", + "description": "A bump to the leftmost non-zero digit of the version number." + }, + { + "const": "minor", + "title": "Minor", + "description": "A bump to the second leftmost non-zero digit of the version number." + }, + { + "const": "patch", + "title": "Patch", + "description": "A bump to the third leftmost non-zero digit of the version number." + } + ] + }, "doc": { "type": "object", "description": "You have the the option to provide different description of your PR for different audiences.", diff --git a/scripts/bridges_update_subtree.sh b/scripts/bridges_update_subtree.sh index 5c5c7a322a163d5db9f7fa4e714c4951f296e1df..2cd6d968d2b24894cc5a8faec02c96618f258d4b 100755 --- a/scripts/bridges_update_subtree.sh +++ b/scripts/bridges_update_subtree.sh @@ -1,6 +1,6 @@ #!/bin/bash -# A script to udpate bridges repo as subtree to Cumulus +# A script to update bridges repo as subtree to Cumulus # Usage: # ./scripts/bridges_update_subtree.sh fetch # ./scripts/bridges_update_subtree.sh patch diff --git a/scripts/release/build-changelogs.sh b/scripts/release/build-changelogs.sh index a9275f45a50c479d27ff3cfffcb5bd82f0b815cf..cbfb7ad0e48f133ed9064f49e2be48bb50b0bea3 100755 --- a/scripts/release/build-changelogs.sh +++ b/scripts/release/build-changelogs.sh @@ -48,6 +48,7 @@ for audience in "${audiences[@]}"; do echo "Processing audience: $audience ($audience_id)" export TARGET_AUDIENCE=$audience tera -t "${TEMPLATE_AUDIENCE}" --env --env-key env "${CONTEXT_JSON}" > "$OUTPUT/relnote_${audience_id}.md" + cat "$OUTPUT/relnote_${audience_id}.md" >> "$OUTPUT/relnote_combined.md" done # Show the files diff --git a/scripts/release/templates/audience.md.tera b/scripts/release/templates/audience.md.tera index dc507053dd5a1fb0f56c788e0f2b02408ba8221c..0b47850e3a37026a5aa5562cd685cbca48bde6f0 100644 --- a/scripts/release/templates/audience.md.tera +++ b/scripts/release/templates/audience.md.tera @@ -1,11 +1,9 @@ -## Release {{ env.PRODUCT }} {{ env.VERSION }} - -Changelog for `{{ env.TARGET_AUDIENCE }}`. +### Changelog for `{{ env.TARGET_AUDIENCE }}` {% for file in prdoc -%} -#### PR #{{file.doc_filename.number}}: {{ file.content.title }} {% for doc_item in file.content.doc %} {%- if doc_item.audience == env.TARGET_AUDIENCE %} +#### [#{{file.doc_filename.number}}]: {{ file.content.title }} {{ doc_item.description }} {% endif -%} diff --git a/scripts/snowbridge_update_subtree.sh b/scripts/snowbridge_update_subtree.sh index 2276bb35469f1e13cd58085e36fa49069123bcef..c572eaa18faa8594d4457f5d87cd00dfb790292a 100755 --- a/scripts/snowbridge_update_subtree.sh +++ b/scripts/snowbridge_update_subtree.sh @@ -1,6 +1,6 @@ #!/bin/bash -# A script to udpate bridges repo as subtree to Cumulus +# A script to update bridges repo as subtree to Cumulus # Usage: # ./scripts/update_subtree_snowbridge.sh fetch # ./scripts/update_subtree_snowbridge.sh patch diff --git a/substrate/bin/minimal/node/Cargo.toml b/substrate/bin/minimal/node/Cargo.toml deleted file mode 100644 index 65644861c9acaca95c6b152e7d237badbed89891..0000000000000000000000000000000000000000 --- a/substrate/bin/minimal/node/Cargo.toml +++ /dev/null @@ -1,60 +0,0 @@ -[package] -name = "minimal-node" -version = "4.0.0-dev" -description = "A fresh FRAME-based Substrate node, ready for hacking." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io/" -edition = "2021" -license = "MIT-0" -publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" -build = "build.rs" - -[lints] -workspace = true - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[[bin]] -name = "minimal-node" - -[dependencies] -clap = { version = "4.5.1", features = ["derive"] } -futures = { version = "0.3.21", features = ["thread-pool"] } -futures-timer = "3.0.1" -jsonrpsee = { version = "0.22", features = ["server"] } -serde_json = { workspace = true, default-features = true } - -sc-cli = { path = "../../../client/cli" } -sc-executor = { path = "../../../client/executor" } -sc-network = { path = "../../../client/network" } -sc-service = { path = "../../../client/service" } -sc-telemetry = { path = "../../../client/telemetry" } -sc-transaction-pool = { path = "../../../client/transaction-pool" } -sc-transaction-pool-api = { path = "../../../client/transaction-pool/api" } -sc-consensus = { path = "../../../client/consensus/common" } -sc-consensus-manual-seal = { path = "../../../client/consensus/manual-seal" } -sc-rpc-api = { path = "../../../client/rpc-api" } -sc-basic-authorship = { path = "../../../client/basic-authorship" } -sc-offchain = { path = "../../../client/offchain" } -sc-client-api = { path = "../../../client/api" } - -sp-timestamp = { path = "../../../primitives/timestamp" } -sp-keyring = { path = "../../../primitives/keyring" } -sp-api = { path = "../../../primitives/api" } -sp-blockchain = { path = "../../../primitives/blockchain" } -sp-block-builder = { path = "../../../primitives/block-builder" } -sp-io = { path = "../../../primitives/io" } -sp-runtime = { path = "../../../primitives/runtime" } - -substrate-frame-rpc-system = { path = "../../../utils/frame/rpc/system" } - -frame = { path = "../../../frame", features = ["experimental", "runtime"] } -runtime = { package = "minimal-runtime", path = "../runtime" } - -[build-dependencies] -substrate-build-script-utils = { path = "../../../utils/build-script-utils" } - -[features] -default = [] diff --git a/substrate/bin/minimal/runtime/Cargo.toml b/substrate/bin/minimal/runtime/Cargo.toml deleted file mode 100644 index 296106544bbfdbbb1f7f76b86d5c8ddefb54f917..0000000000000000000000000000000000000000 --- a/substrate/bin/minimal/runtime/Cargo.toml +++ /dev/null @@ -1,50 +0,0 @@ -[package] -name = "minimal-runtime" -version = "0.1.0" -authors.workspace = true -description = "A minimal Substrate example runtime" -edition.workspace = true -repository.workspace = true -license.workspace = true -publish = false - -[lints] -workspace = true - -[dependencies] -parity-scale-codec = { version = "3.0.0", default-features = false } -scale-info = { version = "2.6.0", default-features = false } - -# this is a frame-based runtime, thus importing `frame` with runtime feature enabled. -frame = { path = "../../../frame", default-features = false, features = ["experimental", "runtime"] } -frame-support = { path = "../../../frame/support", default-features = false } - -# pallets that we want to use -pallet-balances = { path = "../../../frame/balances", default-features = false } -pallet-sudo = { path = "../../../frame/sudo", default-features = false } -pallet-timestamp = { path = "../../../frame/timestamp", default-features = false } -pallet-transaction-payment = { path = "../../../frame/transaction-payment", default-features = false } -pallet-transaction-payment-rpc-runtime-api = { path = "../../../frame/transaction-payment/rpc/runtime-api", default-features = false } - -# genesis builder that allows us to interacto with runtime genesis config -sp-genesis-builder = { path = "../../../primitives/genesis-builder", default-features = false } - - -[build-dependencies] -substrate-wasm-builder = { path = "../../../utils/wasm-builder", optional = true } - -[features] -default = ["std"] -std = [ - "frame-support/std", - "frame/std", - "pallet-balances/std", - "pallet-sudo/std", - "pallet-timestamp/std", - "pallet-transaction-payment-rpc-runtime-api/std", - "pallet-transaction-payment/std", - "parity-scale-codec/std", - "scale-info/std", - "sp-genesis-builder/std", - "substrate-wasm-builder", -] diff --git a/substrate/bin/node-template/.editorconfig b/substrate/bin/node-template/.editorconfig deleted file mode 100644 index 5adac74ca24b3422222b2cb886b2a50cd22b6d1b..0000000000000000000000000000000000000000 --- a/substrate/bin/node-template/.editorconfig +++ /dev/null @@ -1,16 +0,0 @@ -root = true - -[*] -indent_style=space -indent_size=2 -tab_width=2 -end_of_line=lf -charset=utf-8 -trim_trailing_whitespace=true -insert_final_newline = true - -[*.{rs,toml}] -indent_style=tab -indent_size=tab -tab_width=4 -max_line_length=100 diff --git a/substrate/bin/node-template/env-setup/.envrc b/substrate/bin/node-template/env-setup/.envrc deleted file mode 100644 index 3550a30f2de389e537ee40ca5e64a77dc185c79b..0000000000000000000000000000000000000000 --- a/substrate/bin/node-template/env-setup/.envrc +++ /dev/null @@ -1 +0,0 @@ -use flake diff --git a/substrate/bin/node-template/node/Cargo.toml b/substrate/bin/node-template/node/Cargo.toml deleted file mode 100644 index 9cba2b5a36602cae3687b8d5afaa71510cd0d926..0000000000000000000000000000000000000000 --- a/substrate/bin/node-template/node/Cargo.toml +++ /dev/null @@ -1,92 +0,0 @@ -[package] -name = "node-template" -version = "4.0.0-dev" -description = "A fresh FRAME-based Substrate node, ready for hacking." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io/" -edition.workspace = true -license = "MIT-0" -publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" -build = "build.rs" - -[lints] -workspace = true - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[[bin]] -name = "node-template" - -[dependencies] -clap = { version = "4.5.1", features = ["derive"] } -futures = { version = "0.3.21", features = ["thread-pool"] } -serde_json = { workspace = true, default-features = true } - -sc-cli = { path = "../../../client/cli" } -sp-core = { path = "../../../primitives/core" } -sc-executor = { path = "../../../client/executor" } -sc-network = { path = "../../../client/network" } -sc-service = { path = "../../../client/service" } -sc-telemetry = { path = "../../../client/telemetry" } -sc-transaction-pool = { path = "../../../client/transaction-pool" } -sc-transaction-pool-api = { path = "../../../client/transaction-pool/api" } -sc-offchain = { path = "../../../client/offchain" } -sc-consensus-aura = { path = "../../../client/consensus/aura" } -sp-consensus-aura = { path = "../../../primitives/consensus/aura" } -sc-consensus = { path = "../../../client/consensus/common" } -sc-consensus-grandpa = { path = "../../../client/consensus/grandpa" } -sp-consensus-grandpa = { path = "../../../primitives/consensus/grandpa" } -sc-client-api = { path = "../../../client/api" } -sp-runtime = { path = "../../../primitives/runtime" } -sp-io = { path = "../../../primitives/io" } -sp-timestamp = { path = "../../../primitives/timestamp" } -sp-inherents = { path = "../../../primitives/inherents" } -sp-keyring = { path = "../../../primitives/keyring" } -frame-system = { path = "../../../frame/system" } -pallet-transaction-payment = { path = "../../../frame/transaction-payment", default-features = false } - -# These dependencies are used for the node template's RPCs -jsonrpsee = { version = "0.22", features = ["server"] } -sp-api = { path = "../../../primitives/api" } -sc-rpc-api = { path = "../../../client/rpc-api" } -sp-blockchain = { path = "../../../primitives/blockchain" } -sp-block-builder = { path = "../../../primitives/block-builder" } -sc-basic-authorship = { path = "../../../client/basic-authorship" } -substrate-frame-rpc-system = { path = "../../../utils/frame/rpc/system" } -pallet-transaction-payment-rpc = { path = "../../../frame/transaction-payment/rpc" } - -# These dependencies are used for runtime benchmarking -frame-benchmarking = { path = "../../../frame/benchmarking" } -frame-benchmarking-cli = { path = "../../../utils/frame/benchmarking-cli" } - -# Local Dependencies -node-template-runtime = { path = "../runtime" } - -# CLI-specific dependencies -try-runtime-cli = { path = "../../../utils/frame/try-runtime/cli", optional = true } - -[build-dependencies] -substrate-build-script-utils = { path = "../../../utils/build-script-utils" } - -[features] -default = [] -# Dependencies that are only required if runtime benchmarking should be build. -runtime-benchmarks = [ - "frame-benchmarking-cli/runtime-benchmarks", - "frame-benchmarking/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "node-template-runtime/runtime-benchmarks", - "sc-service/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -# Enable features that allow the runtime to be tried and debugged. Name might be subject to change -# in the near future. -try-runtime = [ - "frame-system/try-runtime", - "node-template-runtime/try-runtime", - "pallet-transaction-payment/try-runtime", - "sp-runtime/try-runtime", - "try-runtime-cli/try-runtime", -] diff --git a/substrate/bin/node-template/runtime/Cargo.toml b/substrate/bin/node-template/runtime/Cargo.toml deleted file mode 100644 index 86bab0ca375a84cad0fc9ac2faf5fbc105c0b001..0000000000000000000000000000000000000000 --- a/substrate/bin/node-template/runtime/Cargo.toml +++ /dev/null @@ -1,125 +0,0 @@ -[package] -name = "node-template-runtime" -version = "4.0.0-dev" -description = "A fresh FRAME-based Substrate node, ready for hacking." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io/" -edition.workspace = true -license = "MIT-0" -publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" - -[lints] -workspace = true - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -scale-info = { version = "2.10.0", default-features = false, features = ["derive", "serde"] } - -pallet-aura = { path = "../../../frame/aura", default-features = false } -pallet-balances = { path = "../../../frame/balances", default-features = false } -frame-support = { path = "../../../frame/support", default-features = false } -pallet-grandpa = { path = "../../../frame/grandpa", default-features = false } -pallet-sudo = { path = "../../../frame/sudo", default-features = false } -frame-system = { path = "../../../frame/system", default-features = false } -frame-try-runtime = { path = "../../../frame/try-runtime", default-features = false, optional = true } -pallet-timestamp = { path = "../../../frame/timestamp", default-features = false } -pallet-transaction-payment = { path = "../../../frame/transaction-payment", default-features = false } -frame-executive = { path = "../../../frame/executive", default-features = false } -sp-api = { path = "../../../primitives/api", default-features = false } -sp-block-builder = { path = "../../../primitives/block-builder", default-features = false } -sp-consensus-aura = { path = "../../../primitives/consensus/aura", default-features = false, features = ["serde"] } -sp-consensus-grandpa = { path = "../../../primitives/consensus/grandpa", default-features = false, features = ["serde"] } -sp-core = { path = "../../../primitives/core", default-features = false, features = ["serde"] } -sp-inherents = { path = "../../../primitives/inherents", default-features = false } -sp-offchain = { path = "../../../primitives/offchain", default-features = false } -sp-runtime = { path = "../../../primitives/runtime", default-features = false, features = ["serde"] } -sp-session = { path = "../../../primitives/session", default-features = false } -sp-std = { path = "../../../primitives/std", default-features = false } -sp-storage = { path = "../../../primitives/storage", default-features = false } -sp-transaction-pool = { path = "../../../primitives/transaction-pool", default-features = false } -sp-version = { path = "../../../primitives/version", default-features = false, features = ["serde"] } -serde_json = { features = ["alloc"], workspace = true } -sp-genesis-builder = { default-features = false, path = "../../../primitives/genesis-builder" } - -# Used for the node template's RPCs -frame-system-rpc-runtime-api = { path = "../../../frame/system/rpc/runtime-api", default-features = false } -pallet-transaction-payment-rpc-runtime-api = { path = "../../../frame/transaction-payment/rpc/runtime-api", default-features = false } - -# Used for runtime benchmarking -frame-benchmarking = { path = "../../../frame/benchmarking", default-features = false, optional = true } -frame-system-benchmarking = { path = "../../../frame/system/benchmarking", default-features = false, optional = true } - -# Local Dependencies -pallet-template = { path = "../pallets/template", default-features = false } - -[build-dependencies] -substrate-wasm-builder = { path = "../../../utils/wasm-builder", optional = true } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-executive/std", - "frame-support/std", - "frame-system-benchmarking?/std", - "frame-system-rpc-runtime-api/std", - "frame-system/std", - "frame-try-runtime?/std", - "pallet-aura/std", - "pallet-balances/std", - "pallet-grandpa/std", - "pallet-sudo/std", - "pallet-template/std", - "pallet-timestamp/std", - "pallet-transaction-payment-rpc-runtime-api/std", - "pallet-transaction-payment/std", - "scale-info/std", - "serde_json/std", - "sp-api/std", - "sp-block-builder/std", - "sp-consensus-aura/std", - "sp-consensus-grandpa/std", - "sp-core/std", - "sp-genesis-builder/std", - "sp-inherents/std", - "sp-offchain/std", - "sp-runtime/std", - "sp-session/std", - "sp-std/std", - "sp-storage/std", - "sp-transaction-pool/std", - "sp-version/std", - "substrate-wasm-builder", -] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system-benchmarking/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "pallet-grandpa/runtime-benchmarks", - "pallet-sudo/runtime-benchmarks", - "pallet-template/runtime-benchmarks", - "pallet-timestamp/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-executive/try-runtime", - "frame-support/try-runtime", - "frame-system/try-runtime", - "frame-try-runtime/try-runtime", - "pallet-aura/try-runtime", - "pallet-balances/try-runtime", - "pallet-grandpa/try-runtime", - "pallet-sudo/try-runtime", - "pallet-template/try-runtime", - "pallet-timestamp/try-runtime", - "pallet-transaction-payment/try-runtime", - "sp-runtime/try-runtime", -] -experimental = ["pallet-aura/experimental"] diff --git a/substrate/bin/node/bench/Cargo.toml b/substrate/bin/node/bench/Cargo.toml index 8e0f7fb93b3904ac20902662326a7cba067fbf21..d5de1dbe6c44e16980d79a75c54cec8ae42a4a7f 100644 --- a/substrate/bin/node/bench/Cargo.toml +++ b/substrate/bin/node/bench/Cargo.toml @@ -16,7 +16,7 @@ workspace = true [dependencies] array-bytes = "6.1" -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } log = { workspace = true, default-features = true } node-primitives = { path = "../primitives" } node-testing = { path = "../testing" } diff --git a/substrate/bin/node/cli/Cargo.toml b/substrate/bin/node/cli/Cargo.toml index abe284c41da1f21c064cebcf05ee23c9f4f540f0..8bddbbe04828f19ae437db60c8a6f5480c4e847f 100644 --- a/substrate/bin/node/cli/Cargo.toml +++ b/substrate/bin/node/cli/Cargo.toml @@ -41,7 +41,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] # third-party dependencies array-bytes = "6.1" -clap = { version = "4.5.1", features = ["derive"], optional = true } +clap = { version = "4.5.3", features = ["derive"], optional = true } codec = { package = "parity-scale-codec", version = "3.6.1" } serde = { features = ["derive"], workspace = true, default-features = true } jsonrpsee = { version = "0.22", features = ["server"] } @@ -165,7 +165,7 @@ sp-trie = { path = "../../../primitives/trie" } sp-state-machine = { path = "../../../primitives/state-machine" } [build-dependencies] -clap = { version = "4.5.1", optional = true } +clap = { version = "4.5.3", optional = true } clap_complete = { version = "4.0.2", optional = true } node-inspect = { package = "staging-node-inspect", path = "../inspect", optional = true } frame-benchmarking-cli = { path = "../../../utils/frame/benchmarking-cli", optional = true } diff --git a/substrate/bin/node/cli/benches/block_production.rs b/substrate/bin/node/cli/benches/block_production.rs index 23a62cc0bd243ebd1c8ade712b3ded2361f9e4c0..d04780d5f9535791f5e904bbc89405355cbbd0ce 100644 --- a/substrate/bin/node/cli/benches/block_production.rs +++ b/substrate/bin/node/cli/benches/block_production.rs @@ -145,7 +145,7 @@ fn prepare_benchmark(client: &FullClient) -> (usize, Vec) { let src = Sr25519Keyring::Alice.pair(); let dst: MultiAddress = Sr25519Keyring::Bob.to_account_id().into(); - // Add as many tranfer extrinsics as possible into a single block. + // Add as many transfer extrinsics as possible into a single block. for nonce in 0.. { let extrinsic: OpaqueExtrinsic = create_extrinsic( client, @@ -179,7 +179,7 @@ fn block_production(c: &mut Criterion) { let node = new_node(tokio_handle.clone()); let client = &*node.client; - // Buliding the very first block is around ~30x slower than any subsequent one, + // Building the very first block is around ~30x slower than any subsequent one, // so let's make sure it's built and imported before we benchmark anything. let mut block_builder = BlockBuilderBuilder::new(client) .on_parent_block(client.chain_info().best_hash) diff --git a/substrate/bin/node/cli/src/cli.rs b/substrate/bin/node/cli/src/cli.rs index f3c0435fd32dcaa71f9f94b78dcae01ff07c8b81..56fbed51f8a453b702349f815bc58a07abbd0370 100644 --- a/substrate/bin/node/cli/src/cli.rs +++ b/substrate/bin/node/cli/src/cli.rs @@ -49,7 +49,7 @@ pub struct Cli { /// Possible subcommands of the main binary. #[derive(Debug, clap::Subcommand)] pub enum Subcommand { - /// The custom inspect subcommmand for decoding blocks and extrinsics. + /// The custom inspect subcommand for decoding blocks and extrinsics. #[command( name = "inspect", about = "Decode given block or extrinsic using current native runtime." @@ -63,7 +63,7 @@ pub enum Subcommand { /// Try-runtime has migrated to a standalone CLI /// (). The subcommand exists as a stub and - /// deprecation notice. It will be removed entirely some time after Janurary 2024. + /// deprecation notice. It will be removed entirely some time after January 2024. TryRuntime, /// Key management cli utilities diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index 8f2aba6b44cd0a980771ec7d84eb383421551a6b..dddb261a71d1b2c8d49c1ad3a9f86b70917727a8 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -53,7 +53,7 @@ pub type HostFunctions = ( frame_benchmarking::benchmarking::HostFunctions, ); -/// A specialized `WasmExecutor` intended to use accross substrate node. It provides all required +/// A specialized `WasmExecutor` intended to use across substrate node. It provides all required /// HostFunctions. pub type RuntimeExecutor = sc_executor::WasmExecutor; @@ -422,6 +422,7 @@ pub fn new_full_base( let shared_voter_state = rpc_setup; let auth_disc_publish_non_global_ips = config.network.allow_non_globals_in_dht; + let auth_disc_public_addresses = config.network.public_addresses.clone(); let mut net_config = sc_network::config::FullNetworkConfiguration::new(&config.network); let genesis_hash = client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"); @@ -610,6 +611,7 @@ pub fn new_full_base( sc_authority_discovery::new_worker_and_service_with_config( sc_authority_discovery::WorkerConfig { publish_non_global_ips: auth_disc_publish_non_global_ips, + public_addresses: auth_disc_public_addresses, ..Default::default() }, client.clone(), diff --git a/substrate/bin/node/cli/tests/fees.rs b/substrate/bin/node/cli/tests/fees.rs index 8c7b3c873157770a8f156a019aa7e43e66460bae..69c96bf63a6d8d7b7c24746cdf5c3c48b526c12e 100644 --- a/substrate/bin/node/cli/tests/fees.rs +++ b/substrate/bin/node/cli/tests/fees.rs @@ -135,7 +135,7 @@ fn transaction_fee_is_correct() { // if weight of the cheapest weight would be 10^7, this would be 10^9, which is: // - 1 MILLICENTS in substrate node. // - 1 milli-dot based on current polkadot runtime. - // (this baed on assigning 0.1 CENT to the cheapest tx with `weight = 100`) + // (this based on assigning 0.1 CENT to the cheapest tx with `weight = 100`) let mut t = new_test_ext(compact_code_unwrap()); t.insert(>::hashed_key_for(alice()), new_account_info(100)); t.insert(>::hashed_key_for(bob()), new_account_info(10)); diff --git a/substrate/bin/node/cli/tests/res/default_genesis_config.json b/substrate/bin/node/cli/tests/res/default_genesis_config.json index 1465a6497cadb3d79f75910dc2e084c168559c62..e21fbb47da8c4619e0923c85bf3470828cd80b23 100644 --- a/substrate/bin/node/cli/tests/res/default_genesis_config.json +++ b/substrate/bin/node/cli/tests/res/default_genesis_config.json @@ -2,7 +2,13 @@ "system": {}, "babe": { "authorities": [], - "epochConfig": null + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 1, + 4 + ] + } }, "indices": { "indices": [] diff --git a/substrate/bin/node/inspect/Cargo.toml b/substrate/bin/node/inspect/Cargo.toml index 7db6e3efd3a0fccc7ddd624e6ecb029c82637c03..8453aa3cdeb18715afede5d93fb52f3c6b39be7e 100644 --- a/substrate/bin/node/inspect/Cargo.toml +++ b/substrate/bin/node/inspect/Cargo.toml @@ -15,7 +15,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.6.1" } thiserror = { workspace = true } sc-cli = { path = "../../../client/cli" } diff --git a/substrate/bin/node/runtime/Cargo.toml b/substrate/bin/node/runtime/Cargo.toml index aa13c3d292be6af6d6dba12e5b043a9069eab9a3..4d342ceb460a3f3a5c12b502915f2b679c2b469c 100644 --- a/substrate/bin/node/runtime/Cargo.toml +++ b/substrate/bin/node/runtime/Cargo.toml @@ -58,7 +58,7 @@ sp-io = { path = "../../../primitives/io", default-features = false } frame-executive = { path = "../../../frame/executive", default-features = false } frame-benchmarking = { path = "../../../frame/benchmarking", default-features = false } frame-benchmarking-pallet-pov = { path = "../../../frame/benchmarking/pov", default-features = false } -frame-support = { path = "../../../frame/support", default-features = false, features = ["tuples-96"] } +frame-support = { path = "../../../frame/support", default-features = false, features = ["experimental", "tuples-96"] } frame-system = { path = "../../../frame/system", default-features = false } frame-system-benchmarking = { path = "../../../frame/system/benchmarking", default-features = false, optional = true } frame-election-provider-support = { path = "../../../frame/election-provider-support", default-features = false } @@ -88,6 +88,7 @@ pallet-election-provider-support-benchmarking = { path = "../../../frame/electio pallet-elections-phragmen = { path = "../../../frame/elections-phragmen", default-features = false } pallet-example-tasks = { path = "../../../frame/examples/tasks", default-features = false } pallet-fast-unstake = { path = "../../../frame/fast-unstake", default-features = false } +pallet-migrations = { path = "../../../frame/migrations", default-features = false } pallet-nis = { path = "../../../frame/nis", default-features = false } pallet-grandpa = { path = "../../../frame/grandpa", default-features = false } pallet-im-online = { path = "../../../frame/im-online", default-features = false } @@ -198,6 +199,7 @@ std = [ "pallet-lottery/std", "pallet-membership/std", "pallet-message-queue/std", + "pallet-migrations/std", "pallet-mixnet/std", "pallet-mmr/std", "pallet-multisig/std", @@ -302,6 +304,7 @@ runtime-benchmarks = [ "pallet-lottery/runtime-benchmarks", "pallet-membership/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", + "pallet-migrations/runtime-benchmarks", "pallet-mixnet/runtime-benchmarks", "pallet-mmr/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", @@ -381,6 +384,7 @@ try-runtime = [ "pallet-lottery/try-runtime", "pallet-membership/try-runtime", "pallet-message-queue/try-runtime", + "pallet-migrations/try-runtime", "pallet-mixnet/try-runtime", "pallet-mmr/try-runtime", "pallet-multisig/try-runtime", diff --git a/substrate/bin/node/runtime/src/impls.rs b/substrate/bin/node/runtime/src/impls.rs index 7ff52a758b3dd756dc4536df5928d3b2bb68a148..34f043b33a4edfe2d8cdbe154896e937826110ca 100644 --- a/substrate/bin/node/runtime/src/impls.rs +++ b/substrate/bin/node/runtime/src/impls.rs @@ -30,8 +30,8 @@ use pallet_identity::legacy::IdentityField; use sp_std::prelude::*; use crate::{ - AccountId, AllianceMotion, Assets, Authorship, Balances, Hash, NegativeImbalance, Runtime, - RuntimeCall, + AccountId, AllianceCollective, AllianceMotion, Assets, Authorship, Balances, Hash, + NegativeImbalance, Runtime, RuntimeCall, }; pub struct Author; @@ -107,7 +107,7 @@ impl ProposalProvider for AllianceProposalProvider } fn proposal_of(proposal_hash: Hash) -> Option { - AllianceMotion::proposal_of(proposal_hash) + pallet_collective::ProposalOf::::get(proposal_hash) } } @@ -276,7 +276,7 @@ mod multiplier_tests { let next = runtime_multiplier_update(fm); fm = next; if fm == min_multiplier() { - break + break; } iterations += 1; } diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 24c8f6f48e96ea38078def12fcf8fe17eca3a336..ca7e14f6eb169f4edac300272c2455e5fff0975f 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -28,7 +28,7 @@ use frame_election_provider_support::{ onchain, BalancingConfig, ElectionDataProvider, SequentialPhragmen, VoteWeight, }; use frame_support::{ - construct_runtime, derive_impl, + derive_impl, dispatch::DispatchClass, dynamic_params::{dynamic_pallet_params, dynamic_params}, genesis_builder_helper::{build_config, create_default_config}, @@ -293,7 +293,7 @@ impl pallet_safe_mode::Config for Runtime { type WeightInfo = pallet_safe_mode::weights::SubstrateWeight; } -#[derive_impl(frame_system::config_preludes::SolochainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = InsideBoth; type BlockWeights = RuntimeBlockWeights; @@ -310,6 +310,7 @@ impl frame_system::Config for Runtime { type SystemWeightInfo = frame_system::weights::SubstrateWeight; type SS58Prefix = ConstU16<42>; type MaxConsumers = ConstU32<16>; + type MultiBlockMigrator = MultiBlockMigrations; } impl pallet_insecure_randomness_collective_flip::Config for Runtime {} @@ -1357,6 +1358,8 @@ impl pallet_contracts::Config for Runtime { type MaxCodeLen = ConstU32<{ 123 * 1024 }>; type MaxStorageKeyLen = ConstU32<128>; type UnsafeUnstableInterface = ConstBool; + type UploadOrigin = EnsureSigned; + type InstantiateOrigin = EnsureSigned; type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; type RuntimeHoldReason = RuntimeHoldReason; #[cfg(not(feature = "runtime-benchmarks"))] @@ -1367,6 +1370,7 @@ impl pallet_contracts::Config for Runtime { type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; type Debug = (); type Environment = (); + type ApiVersion = (); type Xcm = (); } @@ -2006,6 +2010,25 @@ impl pallet_statement::Config for Runtime { type MaxAllowedBytes = MaxAllowedBytes; } +parameter_types! { + pub MbmServiceWeight: Weight = Perbill::from_percent(80) * RuntimeBlockWeights::get().max_block; +} + +impl pallet_migrations::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + #[cfg(not(feature = "runtime-benchmarks"))] + type Migrations = (); + // Benchmarks need mocked migrations to guarantee that they succeed. + #[cfg(feature = "runtime-benchmarks")] + type Migrations = pallet_migrations::mock_helpers::MockedMigrations; + type CursorMaxLen = ConstU32<65_536>; + type IdentifierMaxLen = ConstU32<256>; + type MigrationStatusHandler = (); + type FailedMigrationHandler = frame_support::migrations::FreezeChainOnFailedMigration; + type MaxServiceWeight = MbmServiceWeight; + type WeightInfo = pallet_migrations::weights::SubstrateWeight; +} + parameter_types! { pub const BrokerPalletId: PalletId = PalletId(*b"py/broke"); } @@ -2162,91 +2185,260 @@ impl pallet_parameters::Config for Runtime { type WeightInfo = (); } -construct_runtime!( - pub enum Runtime { - System: frame_system, - Utility: pallet_utility, - Babe: pallet_babe, - Timestamp: pallet_timestamp, - // Authorship must be before session in order to note author in the correct session and era - // for im-online and staking. - Authorship: pallet_authorship, - Indices: pallet_indices, - Balances: pallet_balances, - TransactionPayment: pallet_transaction_payment, - AssetTxPayment: pallet_asset_tx_payment, - AssetConversionTxPayment: pallet_asset_conversion_tx_payment, - ElectionProviderMultiPhase: pallet_election_provider_multi_phase, - Staking: pallet_staking, - Session: pallet_session, - Democracy: pallet_democracy, - Council: pallet_collective::, - TechnicalCommittee: pallet_collective::, - Elections: pallet_elections_phragmen, - TechnicalMembership: pallet_membership::, - Grandpa: pallet_grandpa, - Treasury: pallet_treasury, - AssetRate: pallet_asset_rate, - Contracts: pallet_contracts, - Sudo: pallet_sudo, - ImOnline: pallet_im_online, - AuthorityDiscovery: pallet_authority_discovery, - Offences: pallet_offences, - Historical: pallet_session_historical, - RandomnessCollectiveFlip: pallet_insecure_randomness_collective_flip, - Identity: pallet_identity, - Society: pallet_society, - Recovery: pallet_recovery, - Vesting: pallet_vesting, - Scheduler: pallet_scheduler, - Glutton: pallet_glutton, - Preimage: pallet_preimage, - Proxy: pallet_proxy, - Multisig: pallet_multisig, - Bounties: pallet_bounties, - Tips: pallet_tips, - Assets: pallet_assets::, - PoolAssets: pallet_assets::, - Beefy: pallet_beefy, - // MMR leaf construction must be after session in order to have a leaf's next_auth_set - // refer to block. See issue polkadot-fellows/runtimes#160 for details. - Mmr: pallet_mmr, - MmrLeaf: pallet_beefy_mmr, - Lottery: pallet_lottery, - Nis: pallet_nis, - Uniques: pallet_uniques, - Nfts: pallet_nfts, - NftFractionalization: pallet_nft_fractionalization, - Salary: pallet_salary, - CoreFellowship: pallet_core_fellowship, - TransactionStorage: pallet_transaction_storage, - VoterList: pallet_bags_list::, - StateTrieMigration: pallet_state_trie_migration, - ChildBounties: pallet_child_bounties, - Referenda: pallet_referenda, - Remark: pallet_remark, - RootTesting: pallet_root_testing, - ConvictionVoting: pallet_conviction_voting, - Whitelist: pallet_whitelist, - AllianceMotion: pallet_collective::, - Alliance: pallet_alliance, - NominationPools: pallet_nomination_pools, - RankedPolls: pallet_referenda::, - RankedCollective: pallet_ranked_collective, - AssetConversion: pallet_asset_conversion, - FastUnstake: pallet_fast_unstake, - MessageQueue: pallet_message_queue, - Pov: frame_benchmarking_pallet_pov, - TxPause: pallet_tx_pause, - SafeMode: pallet_safe_mode, - Statement: pallet_statement, - Broker: pallet_broker, - TasksExample: pallet_example_tasks, - Mixnet: pallet_mixnet, - Parameters: pallet_parameters, - SkipFeelessPayment: pallet_skip_feeless_payment, - } -); +#[frame_support::runtime] +mod runtime { + #[runtime::runtime] + #[runtime::derive( + RuntimeCall, + RuntimeEvent, + RuntimeError, + RuntimeOrigin, + RuntimeFreezeReason, + RuntimeHoldReason, + RuntimeSlashReason, + RuntimeLockId, + RuntimeTask + )] + pub struct Runtime; + + #[runtime::pallet_index(0)] + pub type System = frame_system; + + #[runtime::pallet_index(1)] + pub type Utility = pallet_utility; + + #[runtime::pallet_index(2)] + pub type Babe = pallet_babe; + + #[runtime::pallet_index(3)] + pub type Timestamp = pallet_timestamp; + + // Authorship must be before session in order to note author in the correct session and era + // for im-online and staking. + #[runtime::pallet_index(4)] + pub type Authorship = pallet_authorship; + + #[runtime::pallet_index(5)] + pub type Indices = pallet_indices; + + #[runtime::pallet_index(6)] + pub type Balances = pallet_balances; + + #[runtime::pallet_index(7)] + pub type TransactionPayment = pallet_transaction_payment; + + #[runtime::pallet_index(8)] + pub type AssetTxPayment = pallet_asset_tx_payment; + + #[runtime::pallet_index(9)] + pub type AssetConversionTxPayment = pallet_asset_conversion_tx_payment; + + #[runtime::pallet_index(10)] + pub type ElectionProviderMultiPhase = pallet_election_provider_multi_phase; + + #[runtime::pallet_index(11)] + pub type Staking = pallet_staking; + + #[runtime::pallet_index(12)] + pub type Session = pallet_session; + + #[runtime::pallet_index(13)] + pub type Democracy = pallet_democracy; + + #[runtime::pallet_index(14)] + pub type Council = pallet_collective; + + #[runtime::pallet_index(15)] + pub type TechnicalCommittee = pallet_collective; + + #[runtime::pallet_index(16)] + pub type Elections = pallet_elections_phragmen; + + #[runtime::pallet_index(17)] + pub type TechnicalMembership = pallet_membership; + + #[runtime::pallet_index(18)] + pub type Grandpa = pallet_grandpa; + + #[runtime::pallet_index(19)] + pub type Treasury = pallet_treasury; + + #[runtime::pallet_index(20)] + pub type AssetRate = pallet_asset_rate; + + #[runtime::pallet_index(21)] + pub type Contracts = pallet_contracts; + + #[runtime::pallet_index(22)] + pub type Sudo = pallet_sudo; + + #[runtime::pallet_index(23)] + pub type ImOnline = pallet_im_online; + + #[runtime::pallet_index(24)] + pub type AuthorityDiscovery = pallet_authority_discovery; + + #[runtime::pallet_index(25)] + pub type Offences = pallet_offences; + + #[runtime::pallet_index(26)] + pub type Historical = pallet_session_historical; + + #[runtime::pallet_index(27)] + pub type RandomnessCollectiveFlip = pallet_insecure_randomness_collective_flip; + + #[runtime::pallet_index(28)] + pub type Identity = pallet_identity; + + #[runtime::pallet_index(29)] + pub type Society = pallet_society; + + #[runtime::pallet_index(30)] + pub type Recovery = pallet_recovery; + + #[runtime::pallet_index(31)] + pub type Vesting = pallet_vesting; + + #[runtime::pallet_index(32)] + pub type Scheduler = pallet_scheduler; + + #[runtime::pallet_index(33)] + pub type Glutton = pallet_glutton; + + #[runtime::pallet_index(34)] + pub type Preimage = pallet_preimage; + + #[runtime::pallet_index(35)] + pub type Proxy = pallet_proxy; + + #[runtime::pallet_index(36)] + pub type Multisig = pallet_multisig; + + #[runtime::pallet_index(37)] + pub type Bounties = pallet_bounties; + + #[runtime::pallet_index(38)] + pub type Tips = pallet_tips; + + #[runtime::pallet_index(39)] + pub type Assets = pallet_assets; + + #[runtime::pallet_index(40)] + pub type PoolAssets = pallet_assets; + + #[runtime::pallet_index(41)] + pub type Beefy = pallet_beefy; + + // MMR leaf construction must be after session in order to have a leaf's next_auth_set + // refer to block. See issue polkadot-fellows/runtimes#160 for details. + #[runtime::pallet_index(42)] + pub type Mmr = pallet_mmr; + + #[runtime::pallet_index(43)] + pub type MmrLeaf = pallet_beefy_mmr; + + #[runtime::pallet_index(44)] + pub type Lottery = pallet_lottery; + + #[runtime::pallet_index(45)] + pub type Nis = pallet_nis; + + #[runtime::pallet_index(46)] + pub type Uniques = pallet_uniques; + + #[runtime::pallet_index(47)] + pub type Nfts = pallet_nfts; + + #[runtime::pallet_index(48)] + pub type NftFractionalization = pallet_nft_fractionalization; + + #[runtime::pallet_index(49)] + pub type Salary = pallet_salary; + + #[runtime::pallet_index(50)] + pub type CoreFellowship = pallet_core_fellowship; + + #[runtime::pallet_index(51)] + pub type TransactionStorage = pallet_transaction_storage; + + #[runtime::pallet_index(52)] + pub type VoterList = pallet_bags_list; + + #[runtime::pallet_index(53)] + pub type StateTrieMigration = pallet_state_trie_migration; + + #[runtime::pallet_index(54)] + pub type ChildBounties = pallet_child_bounties; + + #[runtime::pallet_index(55)] + pub type Referenda = pallet_referenda; + + #[runtime::pallet_index(56)] + pub type Remark = pallet_remark; + + #[runtime::pallet_index(57)] + pub type RootTesting = pallet_root_testing; + + #[runtime::pallet_index(58)] + pub type ConvictionVoting = pallet_conviction_voting; + + #[runtime::pallet_index(59)] + pub type Whitelist = pallet_whitelist; + + #[runtime::pallet_index(60)] + pub type AllianceMotion = pallet_collective; + + #[runtime::pallet_index(61)] + pub type Alliance = pallet_alliance; + + #[runtime::pallet_index(62)] + pub type NominationPools = pallet_nomination_pools; + + #[runtime::pallet_index(63)] + pub type RankedPolls = pallet_referenda; + + #[runtime::pallet_index(64)] + pub type RankedCollective = pallet_ranked_collective; + + #[runtime::pallet_index(65)] + pub type AssetConversion = pallet_asset_conversion; + + #[runtime::pallet_index(66)] + pub type FastUnstake = pallet_fast_unstake; + + #[runtime::pallet_index(67)] + pub type MessageQueue = pallet_message_queue; + + #[runtime::pallet_index(68)] + pub type Pov = frame_benchmarking_pallet_pov; + + #[runtime::pallet_index(69)] + pub type TxPause = pallet_tx_pause; + + #[runtime::pallet_index(70)] + pub type SafeMode = pallet_safe_mode; + + #[runtime::pallet_index(71)] + pub type Statement = pallet_statement; + + #[runtime::pallet_index(72)] + pub type MultiBlockMigrations = pallet_migrations; + + #[runtime::pallet_index(73)] + pub type Broker = pallet_broker; + + #[runtime::pallet_index(74)] + pub type TasksExample = pallet_example_tasks; + + #[runtime::pallet_index(75)] + pub type Mixnet = pallet_mixnet; + + #[runtime::pallet_index(76)] + pub type Parameters = pallet_parameters; + + #[runtime::pallet_index(77)] + pub type SkipFeelessPayment = pallet_skip_feeless_payment; +} /// The address format for describing accounts. pub type Address = sp_runtime::MultiAddress; @@ -2371,6 +2563,7 @@ mod benches { [pallet_lottery, Lottery] [pallet_membership, TechnicalMembership] [pallet_message_queue, MessageQueue] + [pallet_migrations, MultiBlockMigrations] [pallet_mmr, Mmr] [pallet_multisig, Multisig] [pallet_nomination_pools, NominationPoolsBench::] @@ -2416,7 +2609,7 @@ impl_runtime_apis! { Executive::execute_block(block); } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -2786,7 +2979,7 @@ impl_runtime_apis! { #[api_version(3)] impl sp_consensus_beefy::BeefyApi for Runtime { fn beefy_genesis() -> Option { - Beefy::genesis_block() + pallet_beefy::GenesisBlock::::get() } fn validator_set() -> Option> { @@ -2825,11 +3018,11 @@ impl_runtime_apis! { BlockNumber, > for Runtime { fn mmr_root() -> Result { - Ok(Mmr::mmr_root()) + Ok(pallet_mmr::RootHash::::get()) } fn mmr_leaf_count() -> Result { - Ok(Mmr::mmr_leaves()) + Ok(pallet_mmr::NumberOfLeaves::::get()) } fn generate_proof( diff --git a/substrate/bin/node/testing/src/bench.rs b/substrate/bin/node/testing/src/bench.rs index df302a6453b9ffbef603afc36fc860a1535a7ca2..e5c2563905e9ea59901db1564f2b0f34f6bd4aec 100644 --- a/substrate/bin/node/testing/src/bench.rs +++ b/substrate/bin/node/testing/src/bench.rs @@ -84,7 +84,7 @@ impl BenchPair { /// Drop system cache. /// -/// Will panic if cache drop is impossbile. +/// Will panic if cache drop is impossible. pub fn drop_system_cache() { #[cfg(target_os = "windows")] { @@ -173,7 +173,7 @@ impl Clone for BenchDb { // We clear system cache after db clone but before any warmups. // This populates system cache with some data unrelated to actual - // data we will be quering further under benchmark (like what + // data we will be querying further under benchmark (like what // would have happened in real system that queries random entries // from database). drop_system_cache(); @@ -443,7 +443,7 @@ impl BenchDb { BlockContentIterator::new(content, &self.keyring, client) } - /// Get cliet for this database operations. + /// Get client for this database operations. pub fn client(&mut self) -> Client { let (client, _backend, _task_executor) = Self::bench_client(self.database_type, self.directory_guard.path(), &self.keyring); diff --git a/substrate/bin/node/testing/src/genesis.rs b/substrate/bin/node/testing/src/genesis.rs index 6ec21fbe09342d1f704a4ed1a6f79b6160c27a01..c79612d68444c8bd64ad18c3b0a74ceed176eff8 100644 --- a/substrate/bin/node/testing/src/genesis.rs +++ b/substrate/bin/node/testing/src/genesis.rs @@ -20,9 +20,8 @@ use crate::keyring::*; use kitchensink_runtime::{ - constants::currency::*, AccountId, AssetsConfig, BabeConfig, BalancesConfig, GluttonConfig, - GrandpaConfig, IndicesConfig, RuntimeGenesisConfig, SessionConfig, SocietyConfig, StakerStatus, - StakingConfig, BABE_GENESIS_EPOCH_CONFIG, + constants::currency::*, AccountId, AssetsConfig, BalancesConfig, IndicesConfig, + RuntimeGenesisConfig, SessionConfig, SocietyConfig, StakerStatus, StakingConfig, }; use sp_keyring::Ed25519Keyring; use sp_runtime::Perbill; @@ -47,7 +46,6 @@ pub fn config_endowed(extra_endowed: Vec) -> RuntimeGenesisConfig { endowed.extend(extra_endowed.into_iter().map(|endowed| (endowed, 100 * DOLLARS))); RuntimeGenesisConfig { - system: Default::default(), indices: IndicesConfig { indices: vec![] }, balances: BalancesConfig { balances: endowed }, session: SessionConfig { @@ -69,39 +67,8 @@ pub fn config_endowed(extra_endowed: Vec) -> RuntimeGenesisConfig { invulnerables: vec![alice(), bob(), charlie()], ..Default::default() }, - babe: BabeConfig { - authorities: vec![], - epoch_config: Some(BABE_GENESIS_EPOCH_CONFIG), - ..Default::default() - }, - grandpa: GrandpaConfig { authorities: vec![], _config: Default::default() }, - beefy: Default::default(), - im_online: Default::default(), - authority_discovery: Default::default(), - democracy: Default::default(), - council: Default::default(), - technical_committee: Default::default(), - technical_membership: Default::default(), - elections: Default::default(), - sudo: Default::default(), - treasury: Default::default(), society: SocietyConfig { pot: 0 }, - vesting: Default::default(), assets: AssetsConfig { assets: vec![(9, alice(), true, 1)], ..Default::default() }, - pool_assets: Default::default(), - transaction_storage: Default::default(), - transaction_payment: Default::default(), - alliance: Default::default(), - alliance_motion: Default::default(), - nomination_pools: Default::default(), - safe_mode: Default::default(), - tx_pause: Default::default(), - glutton: GluttonConfig { - compute: Default::default(), - storage: Default::default(), - trash_data_count: Default::default(), - ..Default::default() - }, - mixnet: Default::default(), + ..Default::default() } } diff --git a/substrate/bin/utils/chain-spec-builder/Cargo.toml b/substrate/bin/utils/chain-spec-builder/Cargo.toml index 996372c8a0f8638ad4420dc8e21c8f1d418bb60e..c126a76b763f28cbc29b844b0435d9d68858f283 100644 --- a/substrate/bin/utils/chain-spec-builder/Cargo.toml +++ b/substrate/bin/utils/chain-spec-builder/Cargo.toml @@ -23,7 +23,7 @@ name = "chain-spec-builder" crate-type = ["rlib"] [dependencies] -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } log = { workspace = true, default-features = true } sc-chain-spec = { path = "../../../client/chain-spec" } serde_json = { workspace = true, default-features = true } diff --git a/substrate/bin/utils/chain-spec-builder/src/lib.rs b/substrate/bin/utils/chain-spec-builder/src/lib.rs index 8c78030c885484b7d1f9ba3213dbfa76faa36c52..dbd0437921ff3b0dd83612cfae1457e729daf37e 100644 --- a/substrate/bin/utils/chain-spec-builder/src/lib.rs +++ b/substrate/bin/utils/chain-spec-builder/src/lib.rs @@ -185,7 +185,7 @@ pub struct ConvertToRawCmd { /// Verifies the provided input chain spec. /// /// Silently checks if given input chain spec can be converted to raw. It allows to check if all -/// RuntimeGenesisConfig fiels are properly initialized and if the json does not contain invalid +/// RuntimeGenesisConfig fields are properly initialized and if the json does not contain invalid /// fields. #[derive(Parser, Debug, Clone)] pub struct VerifyCmd { diff --git a/substrate/bin/utils/subkey/Cargo.toml b/substrate/bin/utils/subkey/Cargo.toml index 93b1368ca757ad8f383a4c0945c69d0055a8c1ff..8dc4bf254b2d44e1f6b5c96ca16a2e8586e0333c 100644 --- a/substrate/bin/utils/subkey/Cargo.toml +++ b/substrate/bin/utils/subkey/Cargo.toml @@ -20,5 +20,5 @@ path = "src/main.rs" name = "subkey" [dependencies] -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } sc-cli = { path = "../../../client/cli" } diff --git a/substrate/bin/utils/subkey/README.md b/substrate/bin/utils/subkey/README.md index a5f27cfd3707d6278ac9f78b727021a305a18457..fc1053e232d70d746cf315d203a51dfd09e8447c 100644 --- a/substrate/bin/utils/subkey/README.md +++ b/substrate/bin/utils/subkey/README.md @@ -77,7 +77,7 @@ below can be derived from those secrets. The output above also show the **public key** and the **Account ID**. Those are the independent from the network where you will use the key. -The **SS58 address** (or **Public Address**) of a new account is a reprensentation of the public keys of an account for +The **SS58 address** (or **Public Address**) of a new account is a representation of the public keys of an account for a given network (for instance Kusama or Polkadot). You can read more about the [SS58 format in the Substrate Docs](https://docs.substrate.io/reference/address-formats/) @@ -143,7 +143,7 @@ Secret phrase `soup lyrics media market way crouch elevator put moon useful ques SS58 Address: 5He5pZpc7AJ8evPuab37vJF6KkFDqq9uDq2WXh877Qw6iaVC ``` -Using the `inspect` command (see more details below), we see that knowning only the **secret seed** is no longer +Using the `inspect` command (see more details below), we see that knowing only the **secret seed** is no longer sufficient to recover the account: ```bash diff --git a/substrate/bin/utils/subkey/src/lib.rs b/substrate/bin/utils/subkey/src/lib.rs index f3023acde4047a715c0756f26ef2e3944c8206da..33f28ef46a5e7805b2c00b047e413f049169f6ef 100644 --- a/substrate/bin/utils/subkey/src/lib.rs +++ b/substrate/bin/utils/subkey/src/lib.rs @@ -94,10 +94,10 @@ //! seed** (also called **Private Key**). Those 2 secrets are the pieces of information you MUST //! keep safe and secret. All the other information below can be derived from those secrets. //! -//! The output above also show the **public key** and the **Account ID**. Those are the independant +//! The output above also show the **public key** and the **Account ID**. Those are the independent //! from the network where you will use the key. //! -//! The **SS58 address** (or **Public Address**) of a new account is a reprensentation of the public +//! The **SS58 address** (or **Public Address**) of a new account is a representation of the public //! keys of an account for a given network (for instance Kusama or Polkadot). //! //! You can read more about the [SS58 format in the Substrate Docs](https://docs.substrate.io/reference/address-formats/) and see the list of reserved prefixes in the [SS58 Registry](https://github.com/paritytech/ss58-registry). @@ -110,7 +110,7 @@ //! //! ### Json output //! -//! `subkey` can calso generate the output as *json*. This is useful for automation. +//! `subkey` can also generate the output as *json*. This is useful for automation. //! //! command: //! @@ -163,7 +163,7 @@ //! SS58 Address: 5He5pZpc7AJ8evPuab37vJF6KkFDqq9uDq2WXh877Qw6iaVC //! ``` //! -//! Using the `inspect` command (see more details below), we see that knowning only the **secret +//! Using the `inspect` command (see more details below), we see that knowing only the **secret //! seed** is no longer sufficient to recover the account: //! //! ```bash @@ -184,7 +184,7 @@ //! //! ### Inspecting a key //! -//! If you have *some data* about a key, `subkey inpsect` will help you discover more information +//! If you have *some data* about a key, `subkey inspect` will help you discover more information //! about it. //! //! If you have **secrets** that you would like to verify for instance, you can use: diff --git a/substrate/client/api/src/client.rs b/substrate/client/api/src/client.rs index 46232c74539c6c230652a753b5fcd4b6761600d8..2de09840e4dfdc15e1f1d1acaa5f3e438de0caca 100644 --- a/substrate/client/api/src/client.rs +++ b/substrate/client/api/src/client.rs @@ -278,7 +278,7 @@ impl fmt::Display for UsageInfo { pub struct UnpinHandleInner { /// Hash of the block pinned by this handle hash: Block::Hash, - unpin_worker_sender: TracingUnboundedSender, + unpin_worker_sender: TracingUnboundedSender>, } impl Debug for UnpinHandleInner { @@ -291,7 +291,7 @@ impl UnpinHandleInner { /// Create a new [`UnpinHandleInner`] pub fn new( hash: Block::Hash, - unpin_worker_sender: TracingUnboundedSender, + unpin_worker_sender: TracingUnboundedSender>, ) -> Self { Self { hash, unpin_worker_sender } } @@ -299,12 +299,25 @@ impl UnpinHandleInner { impl Drop for UnpinHandleInner { fn drop(&mut self) { - if let Err(err) = self.unpin_worker_sender.unbounded_send(self.hash) { + if let Err(err) = + self.unpin_worker_sender.unbounded_send(UnpinWorkerMessage::Unpin(self.hash)) + { log::debug!(target: "db", "Unable to unpin block with hash: {}, error: {:?}", self.hash, err); }; } } +/// Message that signals notification-based pinning actions to the pinning-worker. +/// +/// When the notification is dropped, an `Unpin` message should be sent to the worker. +#[derive(Debug)] +pub enum UnpinWorkerMessage { + /// Should be sent when a import or finality notification is created. + AnnouncePin(Block::Hash), + /// Should be sent when a import or finality notification is dropped. + Unpin(Block::Hash), +} + /// Keeps a specific block pinned while the handle is alive. /// Once the last handle instance for a given block is dropped, the /// block is unpinned in the [`Backend`](crate::backend::Backend::unpin_block). @@ -315,7 +328,7 @@ impl UnpinHandle { /// Create a new [`UnpinHandle`] pub fn new( hash: Block::Hash, - unpin_worker_sender: TracingUnboundedSender, + unpin_worker_sender: TracingUnboundedSender>, ) -> UnpinHandle { UnpinHandle(Arc::new(UnpinHandleInner::new(hash, unpin_worker_sender))) } @@ -353,7 +366,7 @@ impl BlockImportNotification { header: Block::Header, is_new_best: bool, tree_route: Option>>, - unpin_worker_sender: TracingUnboundedSender, + unpin_worker_sender: TracingUnboundedSender>, ) -> Self { Self { hash, @@ -412,7 +425,7 @@ impl FinalityNotification { /// Create finality notification from finality summary. pub fn from_summary( mut summary: FinalizeSummary, - unpin_worker_sender: TracingUnboundedSender, + unpin_worker_sender: TracingUnboundedSender>, ) -> FinalityNotification { let hash = summary.finalized.pop().unwrap_or_default(); FinalityNotification { @@ -436,7 +449,7 @@ impl BlockImportNotification { /// Create finality notification from finality summary. pub fn from_summary( summary: ImportSummary, - unpin_worker_sender: TracingUnboundedSender, + unpin_worker_sender: TracingUnboundedSender>, ) -> BlockImportNotification { let hash = summary.hash; BlockImportNotification { diff --git a/substrate/client/authority-discovery/Cargo.toml b/substrate/client/authority-discovery/Cargo.toml index cdd4052f0b0998f283ede885f7e7de7509ecd169..26580064b3c453fb3a6ef77ff8d45413877f1d8d 100644 --- a/substrate/client/authority-discovery/Cargo.toml +++ b/substrate/client/authority-discovery/Cargo.toml @@ -29,6 +29,7 @@ multihash = { version = "0.18.1", default-features = false, features = [ "sha2", "std", ] } +linked_hash_set = "0.1.4" log = { workspace = true, default-features = true } prost = "0.12" rand = "0.8.5" diff --git a/substrate/client/authority-discovery/src/interval.rs b/substrate/client/authority-discovery/src/interval.rs index 23c7ce266e3bf8c13485f78dac6d4bb848ef4612..0eee0d159cd86bc781aeed0f3c68f28a75c02f46 100644 --- a/substrate/client/authority-discovery/src/interval.rs +++ b/substrate/client/authority-discovery/src/interval.rs @@ -28,6 +28,7 @@ use std::{ /// /// Doubles interval duration on each tick until the configured maximum is reached. pub struct ExpIncInterval { + start: Duration, max: Duration, next: Duration, delay: Delay, @@ -37,14 +38,29 @@ impl ExpIncInterval { /// Create a new [`ExpIncInterval`]. pub fn new(start: Duration, max: Duration) -> Self { let delay = Delay::new(start); - Self { max, next: start * 2, delay } + Self { start, max, next: start * 2, delay } } - /// Fast forward the exponentially increasing interval to the configured maximum. + /// Fast forward the exponentially increasing interval to the configured maximum, if not already + /// set. pub fn set_to_max(&mut self) { + if self.next == self.max { + return; + } + self.next = self.max; self.delay = Delay::new(self.next); } + + /// Rewind the exponentially increasing interval to the configured start, if not already set. + pub fn set_to_start(&mut self) { + if self.next == self.start * 2 { + return; + } + + self.next = self.start * 2; + self.delay = Delay::new(self.start); + } } impl Stream for ExpIncInterval { diff --git a/substrate/client/authority-discovery/src/lib.rs b/substrate/client/authority-discovery/src/lib.rs index 6bb12804cada34d1b3f87b4249fc466fcf253a9a..281188de14324ed38793a8968341f6d450d3f8c0 100644 --- a/substrate/client/authority-discovery/src/lib.rs +++ b/substrate/client/authority-discovery/src/lib.rs @@ -80,6 +80,10 @@ pub struct WorkerConfig { /// Defaults to `true` to avoid the surprise factor. pub publish_non_global_ips: bool, + /// Public addresses set by the node operator to always publish first in the authority + /// discovery DHT record. + pub public_addresses: Vec, + /// Reject authority discovery records that are not signed by their network identity (PeerId) /// /// Defaults to `false` to provide compatibility with old versions @@ -104,6 +108,7 @@ impl Default for WorkerConfig { // `authority_discovery_dht_event_received`. max_query_interval: Duration::from_secs(10 * 60), publish_non_global_ips: true, + public_addresses: Vec::new(), strict_record_validation: false, } } diff --git a/substrate/client/authority-discovery/src/tests.rs b/substrate/client/authority-discovery/src/tests.rs index 4fbc196c5ecd1bb4c4eafd3850a8e2754de91963..edd50d073c8d79729676e55300f37fd0f8ef0064 100644 --- a/substrate/client/authority-discovery/src/tests.rs +++ b/substrate/client/authority-discovery/src/tests.rs @@ -100,7 +100,7 @@ fn cryptos_are_compatible() { let sp_core_signature = sp_core_secret.sign(message); // no error expected... assert!(sp_core::ed25519::Pair::verify( - &sp_core::ed25519::Signature::from_slice(&libp2p_signature).unwrap(), + &sp_core::ed25519::Signature::try_from(libp2p_signature.as_slice()).unwrap(), message, &sp_core_public )); diff --git a/substrate/client/authority-discovery/src/worker.rs b/substrate/client/authority-discovery/src/worker.rs index 6db25416dee78323a1531f5868be2601af3eaee0..4ad7db5f7da94c8676a25f2090970c54fc09c05b 100644 --- a/substrate/client/authority-discovery/src/worker.rs +++ b/substrate/client/authority-discovery/src/worker.rs @@ -35,6 +35,7 @@ use addr_cache::AddrCache; use codec::{Decode, Encode}; use ip_network::IpNetwork; use libp2p::{core::multiaddr, identity::PublicKey, multihash::Multihash, Multiaddr, PeerId}; +use linked_hash_set::LinkedHashSet; use multihash_codetable::{Code, MultihashDigest}; use log::{debug, error, log_enabled}; @@ -120,14 +121,25 @@ pub struct Worker { /// Interval to be proactive, publishing own addresses. publish_interval: ExpIncInterval, + /// Pro-actively publish our own addresses at this interval, if the keys in the keystore /// have changed. publish_if_changed_interval: ExpIncInterval, + /// List of keys onto which addresses have been published at the latest publication. /// Used to check whether they have changed. latest_published_keys: HashSet, + /// List of the kademlia keys that have been published at the latest publication. + /// Used to associate DHT events with our published records. + latest_published_kad_keys: HashSet, + /// Same value as in the configuration. publish_non_global_ips: bool, + + /// Public addresses set by the node operator to always publish first in the authority + /// discovery DHT record. + public_addresses: LinkedHashSet, + /// Same value as in the configuration. strict_record_validation: bool, @@ -136,6 +148,7 @@ pub struct Worker { /// Queue of throttled lookups pending to be passed to the network. pending_lookups: Vec, + /// Set of in-flight lookups. in_flight_lookups: HashMap, @@ -224,6 +237,29 @@ where None => None, }; + let public_addresses = { + let local_peer_id: Multihash = network.local_peer_id().into(); + + config + .public_addresses + .into_iter() + .map(|mut address| { + if let Some(multiaddr::Protocol::P2p(peer_id)) = address.iter().last() { + if peer_id != local_peer_id { + error!( + target: LOG_TARGET, + "Discarding invalid local peer ID in public address {address}.", + ); + } + // Always discard `/p2p/...` protocol for proper address comparison (local + // peer id will be added before publishing). + address.pop(); + } + address + }) + .collect() + }; + Worker { from_service: from_service.fuse(), client, @@ -232,7 +268,9 @@ where publish_interval, publish_if_changed_interval, latest_published_keys: HashSet::new(), + latest_published_kad_keys: HashSet::new(), publish_non_global_ips: config.publish_non_global_ips, + public_addresses, strict_record_validation: config.strict_record_validation, query_interval, pending_lookups: Vec::new(), @@ -304,17 +342,30 @@ where } fn addresses_to_publish(&self) -> impl Iterator { - let peer_id: Multihash = self.network.local_peer_id().into(); let publish_non_global_ips = self.publish_non_global_ips; - self.network - .external_addresses() + let addresses = self + .public_addresses + .clone() .into_iter() - .filter(move |a| { + .chain(self.network.external_addresses().into_iter().filter_map(|mut address| { + // Make sure the reported external address does not contain `/p2p/...` protocol. + if let Some(multiaddr::Protocol::P2p(_)) = address.iter().last() { + address.pop(); + } + + if self.public_addresses.contains(&address) { + // Already added above. + None + } else { + Some(address) + } + })) + .filter(move |address| { if publish_non_global_ips { return true } - a.iter().all(|p| match p { + address.iter().all(|protocol| match protocol { // The `ip_network` library is used because its `is_global()` method is stable, // while `is_global()` in the standard library currently isn't. multiaddr::Protocol::Ip4(ip) if !IpNetwork::from(ip).is_global() => false, @@ -322,13 +373,17 @@ where _ => true, }) }) - .map(move |a| { - if a.iter().any(|p| matches!(p, multiaddr::Protocol::P2p(_))) { - a - } else { - a.with(multiaddr::Protocol::P2p(peer_id)) - } - }) + .collect::>(); + + let peer_id = self.network.local_peer_id(); + debug!( + target: LOG_TARGET, + "Authority DHT record peer_id='{peer_id}' addresses='{addresses:?}'", + ); + + // The address must include the peer id. + let peer_id: Multihash = peer_id.into(); + addresses.into_iter().map(move |a| a.with(multiaddr::Protocol::P2p(peer_id))) } /// Publish own public addresses. @@ -346,8 +401,17 @@ where self.client.as_ref(), ).await?.into_iter().collect::>(); - if only_if_changed && keys == self.latest_published_keys { - return Ok(()) + if only_if_changed { + // If the authority keys did not change and the `publish_if_changed_interval` was + // triggered then do nothing. + if keys == self.latest_published_keys { + return Ok(()) + } + + // We have detected a change in the authority keys, reset the timers to + // publish and gather data faster. + self.publish_interval.set_to_start(); + self.query_interval.set_to_start(); } let addresses = serialize_addresses(self.addresses_to_publish()); @@ -371,6 +435,8 @@ where keys_vec, )?; + self.latest_published_kad_keys = kv_pairs.iter().map(|(k, _)| k.clone()).collect(); + for (key, value) in kv_pairs.into_iter() { self.network.put_value(key, value); } @@ -472,6 +538,10 @@ where } }, DhtEvent::ValuePut(hash) => { + if !self.latest_published_kad_keys.contains(&hash) { + return; + } + // Fast forward the exponentially increasing interval to the configured maximum. In // case this was the first successful address publishing there is no need for a // timely retry. @@ -484,6 +554,11 @@ where debug!(target: LOG_TARGET, "Successfully put hash '{:?}' on Dht.", hash) }, DhtEvent::ValuePutFailed(hash) => { + if !self.latest_published_kad_keys.contains(&hash) { + // Not a value we have published or received multiple times. + return; + } + if let Some(metrics) = &self.metrics { metrics.dht_event_received.with_label_values(&["value_put_failed"]).inc(); } diff --git a/substrate/client/basic-authorship/src/basic_authorship.rs b/substrate/client/basic-authorship/src/basic_authorship.rs index c07f3e639c3e15b572dbb470c1518b9ddfddae32..1519c76c42c0efab1fa59c7e53bed41c5e104f9b 100644 --- a/substrate/client/basic-authorship/src/basic_authorship.rs +++ b/substrate/client/basic-authorship/src/basic_authorship.rs @@ -38,7 +38,7 @@ use sp_core::traits::SpawnNamed; use sp_inherents::InherentData; use sp_runtime::{ traits::{BlakeTwo256, Block as BlockT, Hash as HashT, Header as HeaderT}, - Digest, Percent, SaturatedConversion, + Digest, ExtrinsicInclusionMode, Percent, SaturatedConversion, }; use std::{marker::PhantomData, pin::Pin, sync::Arc, time}; @@ -87,6 +87,22 @@ pub struct ProposerFactory { _phantom: PhantomData, } +impl Clone for ProposerFactory { + fn clone(&self) -> Self { + Self { + spawn_handle: self.spawn_handle.clone(), + client: self.client.clone(), + transaction_pool: self.transaction_pool.clone(), + metrics: self.metrics.clone(), + default_block_size_limit: self.default_block_size_limit, + soft_deadline_percent: self.soft_deadline_percent, + telemetry: self.telemetry.clone(), + include_proof_in_block_size_estimation: self.include_proof_in_block_size_estimation, + _phantom: self._phantom, + } + } +} + impl ProposerFactory { /// Create a new proposer factory. /// @@ -164,7 +180,7 @@ impl ProposerFactory { /// The soft deadline indicates where we should stop attempting to add transactions /// to the block, which exhaust resources. After soft deadline is reached, /// we switch to a fixed-amount mode, in which after we see `MAX_SKIPPED_TRANSACTIONS` - /// transactions which exhaust resrouces, we will conclude that the block is full. + /// transactions which exhaust resources, we will conclude that the block is full. /// /// Setting the value too low will significantly limit the amount of transactions /// we try in case they exhaust resources. Setting the value too high can @@ -319,11 +335,12 @@ where self.apply_inherents(&mut block_builder, inherent_data)?; - // TODO call `after_inherents` and check if we should apply extrinsincs here - // - - let end_reason = - self.apply_extrinsics(&mut block_builder, deadline, block_size_limit).await?; + let mode = block_builder.extrinsic_inclusion_mode(); + let end_reason = match mode { + ExtrinsicInclusionMode::AllExtrinsics => + self.apply_extrinsics(&mut block_builder, deadline, block_size_limit).await?, + ExtrinsicInclusionMode::OnlyInherents => EndProposingReason::TransactionForbidden, + }; let (block, storage_changes, proof) = block_builder.build()?.into_inner(); let block_took = block_timer.elapsed(); diff --git a/substrate/client/block-builder/src/lib.rs b/substrate/client/block-builder/src/lib.rs index 258e39d962b2de2397d85a54223c155e821ddfa3..2f22cd42591fc60bf1665d00a1c5b2b548ad3aea 100644 --- a/substrate/client/block-builder/src/lib.rs +++ b/substrate/client/block-builder/src/lib.rs @@ -37,7 +37,7 @@ use sp_core::traits::CallContext; use sp_runtime::{ legacy, traits::{Block as BlockT, Hash, HashingFor, Header as HeaderT, NumberFor, One}, - Digest, + Digest, ExtrinsicInclusionMode, }; use std::marker::PhantomData; @@ -198,10 +198,12 @@ pub struct BlockBuilder<'a, Block: BlockT, C: ProvideRuntimeApi + 'a> { extrinsics: Vec, api: ApiRef<'a, C::Api>, call_api_at: &'a C, + /// Version of the [`BlockBuilderApi`] runtime API. version: u32, parent_hash: Block::Hash, /// The estimated size of the block header. estimated_header_size: usize, + extrinsic_inclusion_mode: ExtrinsicInclusionMode, } impl<'a, Block, C> BlockBuilder<'a, Block, C> @@ -244,9 +246,19 @@ where api.set_call_context(CallContext::Onchain); - api.initialize_block(parent_hash, &header)?; + let core_version = api + .api_version::>(parent_hash)? + .ok_or_else(|| Error::VersionInvalid("Core".to_string()))?; - let version = api + let extrinsic_inclusion_mode = if core_version >= 5 { + api.initialize_block(parent_hash, &header)? + } else { + #[allow(deprecated)] + api.initialize_block_before_version_5(parent_hash, &header)?; + ExtrinsicInclusionMode::AllExtrinsics + }; + + let bb_version = api .api_version::>(parent_hash)? .ok_or_else(|| Error::VersionInvalid("BlockBuilderApi".to_string()))?; @@ -254,12 +266,18 @@ where parent_hash, extrinsics: Vec::new(), api, - version, + version: bb_version, estimated_header_size, call_api_at, + extrinsic_inclusion_mode, }) } + /// The extrinsic inclusion mode of the runtime for this block. + pub fn extrinsic_inclusion_mode(&self) -> ExtrinsicInclusionMode { + self.extrinsic_inclusion_mode + } + /// Push onto the block's list of extrinsics. /// /// This will ensure the extrinsic can be validly executed (by executing it). diff --git a/substrate/client/chain-spec/src/chain_spec.rs b/substrate/client/chain-spec/src/chain_spec.rs index fe8fcfda216e1fa86215daf93add2aa6adc78bd9..4ec8527de264cc8df49ce67ff534d6cedef8e641 100644 --- a/substrate/client/chain-spec/src/chain_spec.rs +++ b/substrate/client/chain-spec/src/chain_spec.rs @@ -52,7 +52,7 @@ enum GenesisSource { File(PathBuf), Binary(Cow<'static, [u8]>), /// factory function + code - //Factory and G type parameter shall be removed togheter with `ChainSpec::from_genesis` + //Factory and G type parameter shall be removed together with `ChainSpec::from_genesis` Factory(Arc G + Send + Sync>, Vec), Storage(Storage), /// build action + code @@ -264,7 +264,7 @@ struct RuntimeInnerWrapper { enum Genesis { /// (Deprecated) Contains the JSON representation of G (the native type representing the /// runtime's `RuntimeGenesisConfig` struct) (will be removed with `ChainSpec::from_genesis`) - /// without the runtime code. It is required to deserialize the legacy chainspecs genereted + /// without the runtime code. It is required to deserialize the legacy chainspecs generated /// with `ChainsSpec::from_genesis` method. Runtime(G), /// (Deprecated) Contains the JSON representation of G (the native type representing the @@ -276,13 +276,13 @@ enum Genesis { Raw(RawGenesis), /// State root hash of the genesis storage. StateRootHash(StorageData), - /// Represents the runtime genesis config in JSON format toghether with runtime code. + /// Represents the runtime genesis config in JSON format together with runtime code. RuntimeGenesis(RuntimeGenesisInner), } /// A configuration of a client. Does not include runtime storage initialization. /// Note: `genesis` field is ignored due to way how the chain specification is serialized into -/// JSON file. Refer to [`ChainSpecJsonContainer`], which flattens [`ClientSpec`] and denies uknown +/// JSON file. Refer to [`ChainSpecJsonContainer`], which flattens [`ClientSpec`] and denies unknown /// fields. #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "camelCase")] @@ -508,7 +508,7 @@ impl ChainSpec { self.client_spec.fork_id.as_deref() } - /// Additional loosly-typed properties of the chain. + /// Additional loosely-typed properties of the chain. /// /// Returns an empty JSON object if 'properties' not defined in config pub fn properties(&self) -> Properties { @@ -1237,15 +1237,7 @@ mod tests { "TestName", "test", ChainType::Local, - move || substrate_test_runtime::RuntimeGenesisConfig { - babe: substrate_test_runtime::BabeConfig { - epoch_config: Some( - substrate_test_runtime::TEST_RUNTIME_BABE_EPOCH_CONFIGURATION, - ), - ..Default::default() - }, - ..Default::default() - }, + || Default::default(), Vec::new(), None, None, diff --git a/substrate/client/chain-spec/src/extension.rs b/substrate/client/chain-spec/src/extension.rs index f2939741535f730787846d2a641a2721f5cec9e5..d1f03539349686cf22b00b362907eb0c3f67f406 100644 --- a/substrate/client/chain-spec/src/extension.rs +++ b/substrate/client/chain-spec/src/extension.rs @@ -284,7 +284,7 @@ where } } -/// A subset of the `Extension` trait that only allows for quering extensions. +/// A subset of the `Extension` trait that only allows for querying extensions. pub trait GetExtension { /// Get an extension of specific type. fn get_any(&self, t: TypeId) -> &dyn Any; diff --git a/substrate/client/chain-spec/src/genesis_block.rs b/substrate/client/chain-spec/src/genesis_block.rs index 6aa156a620a7933034643f8a7bdbf8e0d617ad97..3c7b9f64dcd6bc0babff662747571f9d31853eec 100644 --- a/substrate/client/chain-spec/src/genesis_block.rs +++ b/substrate/client/chain-spec/src/genesis_block.rs @@ -18,23 +18,25 @@ //! Tool for creating the genesis block. -use std::{collections::hash_map::DefaultHasher, marker::PhantomData, sync::Arc}; +use std::{marker::PhantomData, sync::Arc}; +use codec::Encode; use sc_client_api::{backend::Backend, BlockImportOperation}; use sc_executor::RuntimeVersionOf; use sp_core::storage::{well_known_keys, StateVersion, Storage}; use sp_runtime::{ - traits::{Block as BlockT, Hash as HashT, Header as HeaderT, Zero}, + traits::{Block as BlockT, Hash as HashT, HashingFor, Header as HeaderT, Zero}, BuildStorage, }; /// Return the state version given the genesis storage and executor. -pub fn resolve_state_version_from_wasm( +pub fn resolve_state_version_from_wasm( storage: &Storage, executor: &E, ) -> sp_blockchain::Result where E: RuntimeVersionOf, + H: HashT, { if let Some(wasm) = storage.top.get(well_known_keys::CODE) { let mut ext = sp_state_machine::BasicExternalities::new_empty(); // just to read runtime version. @@ -43,12 +45,7 @@ where let runtime_code = sp_core::traits::RuntimeCode { code_fetcher: &code_fetcher, heap_pages: None, - hash: { - use std::hash::{Hash, Hasher}; - let mut state = DefaultHasher::new(); - wasm.hash(&mut state); - state.finish().to_le_bytes().to_vec() - }, + hash: ::hash(wasm).encode(), }; let runtime_version = RuntimeVersionOf::runtime_version(executor, &mut ext, &runtime_code) .map_err(|e| sp_blockchain::Error::VersionInvalid(e.to_string()))?; @@ -129,7 +126,8 @@ impl, E: RuntimeVersionOf> BuildGenesisBlock sp_blockchain::Result<(Block, Self::BlockImportOperation)> { let Self { genesis_storage, commit_genesis_state, backend, executor, _phantom } = self; - let genesis_state_version = resolve_state_version_from_wasm(&genesis_storage, &executor)?; + let genesis_state_version = + resolve_state_version_from_wasm::<_, HashingFor>(&genesis_storage, &executor)?; let mut op = backend.begin_operation()?; let state_root = op.set_genesis_state(genesis_storage, commit_genesis_state, genesis_state_version)?; diff --git a/substrate/client/chain-spec/src/genesis_config_builder.rs b/substrate/client/chain-spec/src/genesis_config_builder.rs index 8766dd5c5ad28c9263f7538d255e03a898244026..61f065e213cb1aca50e1b34d84a855cec0ee33ca 100644 --- a/substrate/client/chain-spec/src/genesis_config_builder.rs +++ b/substrate/client/chain-spec/src/genesis_config_builder.rs @@ -33,7 +33,7 @@ use std::borrow::Cow; /// A utility that facilitates calling the GenesisBuilder API from the runtime wasm code blob. /// /// `EHF` type allows to specify the extended host function required for building runtime's genesis -/// config. The type will be compbined with default `sp_io::SubstrateHostFunctions`. +/// config. The type will be combined with default `sp_io::SubstrateHostFunctions`. pub struct GenesisConfigBuilderRuntimeCaller<'a, EHF = ()> where EHF: HostFunctions, @@ -81,7 +81,8 @@ where .0 } - /// Returns the default `GenesisConfig` provided by the `runtime`. + /// Returns a json representation of the default `RuntimeGenesisConfig` provided by the + /// `runtime`. /// /// Calls [`GenesisBuilder::create_default_config`](sp_genesis_builder::GenesisBuilder::create_default_config) in the `runtime`. pub fn get_default_config(&self) -> core::result::Result { @@ -94,7 +95,7 @@ where Ok(from_slice(&default_config[..]).expect("returned value is json. qed.")) } - /// Build the given `GenesisConfig` and returns the genesis state. + /// Builds `RuntimeGenesisConfig` from given json blob and returns the genesis state. /// /// Calls [`GenesisBuilder::build_config`](sp_genesis_builder::GenesisBuilder::build_config) /// provided by the `runtime`. @@ -111,25 +112,26 @@ where Ok(ext.into_storages()) } - /// Creates the genesis state by patching the default `GenesisConfig` and applying it. + /// Creates the genesis state by patching the default `RuntimeGenesisConfig`. /// - /// This function generates the `GenesisConfig` for the runtime by applying a provided JSON - /// patch. The patch modifies the default `GenesisConfig` allowing customization of the specific - /// keys. The resulting `GenesisConfig` is then deserialized from the patched JSON - /// representation and stored in the storage. + /// This function generates the `RuntimeGenesisConfig` for the runtime by applying a provided + /// JSON patch. The patch modifies the default `RuntimeGenesisConfig` allowing customization of + /// the specific keys. The resulting `RuntimeGenesisConfig` is then deserialized from the + /// patched JSON representation and stored in the storage. /// /// If the provided JSON patch is incorrect or the deserialization fails the error will be /// returned. /// - /// The patching process modifies the default `GenesisConfig` according to the following rules: + /// The patching process modifies the default `RuntimeGenesisConfig` according to the following + /// rules: /// 1. Existing keys in the default configuration will be overridden by the corresponding values /// in the patch. /// 2. If a key exists in the patch but not in the default configuration, it will be added to - /// the resulting `GenesisConfig`. + /// the resulting `RuntimeGenesisConfig`. /// 3. Keys in the default configuration that have null values in the patch will be removed from - /// the resulting `GenesisConfig`. This is helpful for changing enum variant value. + /// the resulting `RuntimeGenesisConfig`. This is helpful for changing enum variant value. /// - /// Please note that the patch may contain full `GenesisConfig`. + /// Please note that the patch may contain full `RuntimeGenesisConfig`. pub fn get_storage_for_patch(&self, patch: Value) -> core::result::Result { let mut config = self.get_default_config()?; crate::json_patch::merge(&mut config, patch); @@ -149,7 +151,7 @@ mod tests { ::new(substrate_test_runtime::wasm_binary_unwrap()) .get_default_config() .unwrap(); - let expected = r#"{"system":{},"babe":{"authorities":[],"epochConfig":null},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#; + let expected = r#"{"babe": {"authorities": [], "epochConfig": {"allowed_slots": "PrimaryAndSecondaryVRFSlots", "c": [1, 4]}}, "balances": {"balances": []}, "substrateTest": {"authorities": []}, "system": {}}"#; assert_eq!(from_str::(expected).unwrap(), config); } diff --git a/substrate/client/chain-spec/src/lib.rs b/substrate/client/chain-spec/src/lib.rs index eab5f789f29a0589d68c3c0aa86952c75f112afa..e8b87a6040422aab2d6b5ad3e2a90c7b386aa318 100644 --- a/substrate/client/chain-spec/src/lib.rs +++ b/substrate/client/chain-spec/src/lib.rs @@ -393,7 +393,7 @@ pub trait ChainSpec: BuildStorage + Send + Sync { fn protocol_id(&self) -> Option<&str>; /// Optional network fork identifier. `None` by default. fn fork_id(&self) -> Option<&str>; - /// Additional loosly-typed properties of the chain. + /// Additional loosely-typed properties of the chain. /// /// Returns an empty JSON object if 'properties' not defined in config fn properties(&self) -> Properties; diff --git a/substrate/client/cli/Cargo.toml b/substrate/client/cli/Cargo.toml index 0582018283c6e49882c71106d36f73d6ba7cedb2..47b29555e6603e9e72f8dca634f7beb20069b5e7 100644 --- a/substrate/client/cli/Cargo.toml +++ b/substrate/client/cli/Cargo.toml @@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = "6.1" chrono = "0.4.31" -clap = { version = "4.5.1", features = ["derive", "string", "wrap_help"] } +clap = { version = "4.5.3", features = ["derive", "string", "wrap_help"] } fdlimit = "0.3.0" futures = "0.3.21" itertools = "0.10.3" @@ -32,7 +32,8 @@ rpassword = "7.0.0" serde = { workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } thiserror = { workspace = true } -bip39 = "2.0.0" +# personal fork here as workaround for: https://github.com/rust-bitcoin/rust-bip39/pull/64 +bip39 = { package = "parity-bip39", version = "2.0.1", features = ["rand"] } tokio = { version = "1.22.0", features = ["parking_lot", "rt-multi-thread", "signal"] } sc-client-api = { path = "../api" } sc-client-db = { path = "../db", default-features = false } diff --git a/substrate/client/cli/src/commands/generate.rs b/substrate/client/cli/src/commands/generate.rs index c465bcc85a47be16a5217677311499ce42573895..94769279e21940993785b2a4eeb6fed5d08ddeb6 100644 --- a/substrate/client/cli/src/commands/generate.rs +++ b/substrate/client/cli/src/commands/generate.rs @@ -64,7 +64,7 @@ impl GenerateCmd { let password = self.keystore_params.read_password()?; let output = self.output_scheme.output_type; - let phrase = mnemonic.word_iter().join(" "); + let phrase = mnemonic.words().join(" "); with_crypto_scheme!( self.crypto_scheme.scheme, diff --git a/substrate/client/cli/src/commands/vanity.rs b/substrate/client/cli/src/commands/vanity.rs index ce75161329893d281868c659bbc6656aa9ece194..330a59493efc9536cb1d78f2d97b40a11c94d4b0 100644 --- a/substrate/client/cli/src/commands/vanity.rs +++ b/substrate/client/cli/src/commands/vanity.rs @@ -51,7 +51,7 @@ pub struct VanityCmd { impl VanityCmd { /// Run the command pub fn run(&self) -> error::Result<()> { - let formated_seed = with_crypto_scheme!( + let formatted_seed = with_crypto_scheme!( self.crypto_scheme.scheme, generate_key( &self.pattern, @@ -62,7 +62,7 @@ impl VanityCmd { with_crypto_scheme!( self.crypto_scheme.scheme, print_from_uri( - &formated_seed, + &formatted_seed, None, self.network_scheme.network, self.output_scheme.output_type, diff --git a/substrate/client/cli/src/params/network_params.rs b/substrate/client/cli/src/params/network_params.rs index 12f19df2a68549be318bedf1cc86875a292ee86b..94efb4280912041dcea512096226824012114b9b 100644 --- a/substrate/client/cli/src/params/network_params.rs +++ b/substrate/client/cli/src/params/network_params.rs @@ -310,7 +310,7 @@ mod tests { } #[test] - fn sync_ingores_case() { + fn sync_ignores_case() { let params = Cli::try_parse_from(["", "--sync", "wArP"]).expect("Parses network params"); assert_eq!(SyncMode::Warp, params.network_params.sync); diff --git a/substrate/client/consensus/aura/src/lib.rs b/substrate/client/consensus/aura/src/lib.rs index 1be7be8eeeaa60a75ae8b6ef95fd8cbbd5b3998e..e220aaac508df50c1c6d440cfe50ee3b4015d64a 100644 --- a/substrate/client/consensus/aura/src/lib.rs +++ b/substrate/client/consensus/aura/src/lib.rs @@ -82,7 +82,7 @@ pub enum CompatibilityMode { None, /// Call `initialize_block` before doing any runtime calls. /// - /// Previously the node would execute `initialize_block` before fetchting the authorities + /// Previously the node would execute `initialize_block` before fetching the authorities /// from the runtime. This behaviour changed in: /// /// By calling `initialize_block` before fetching the authorities, on a block that diff --git a/substrate/client/consensus/babe/rpc/src/lib.rs b/substrate/client/consensus/babe/rpc/src/lib.rs index 8b183e7dfdcd3888ef618e1506c5d57be89403f5..a3e811baecffd6de1b9f94952be40d00d8b61de3 100644 --- a/substrate/client/consensus/babe/rpc/src/lib.rs +++ b/substrate/client/consensus/babe/rpc/src/lib.rs @@ -258,7 +258,7 @@ mod tests { let request = r#"{"jsonrpc":"2.0","method":"babe_epochAuthorship","params": [],"id":1}"#; let (response, _) = api.raw_json_request(request, 1).await.unwrap(); - let expected = r#"{"jsonrpc":"2.0","result":{"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY":{"primary":[0],"secondary":[1,2,4],"secondary_vrf":[]}},"id":1}"#; + let expected = r#"{"jsonrpc":"2.0","result":{"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY":{"primary":[0],"secondary":[],"secondary_vrf":[1,2,4]}},"id":1}"#; assert_eq!(response, expected); } diff --git a/substrate/client/consensus/babe/src/authorship.rs b/substrate/client/consensus/babe/src/authorship.rs index 11f5233abc6b384ef034acaa1f26a1833aee124a..57ee706a04f6bfc80ff29b6e0bbf91a4874991b1 100644 --- a/substrate/client/consensus/babe/src/authorship.rs +++ b/substrate/client/consensus/babe/src/authorship.rs @@ -59,7 +59,7 @@ pub(super) fn calculate_primary_threshold( assert!(theta > 0.0, "authority with weight 0."); // NOTE: in the equation `p = 1 - (1 - c)^theta` the value of `p` is always - // capped by `c`. For all pratical purposes `c` should always be set to a + // capped by `c`. For all practical purposes `c` should always be set to a // value < 0.5, as such in the computations below we should never be near // edge cases like `0.999999`. diff --git a/substrate/client/consensus/babe/src/lib.rs b/substrate/client/consensus/babe/src/lib.rs index ccf72939631accae68c9e473cf30461c2fca65dc..d10bdd8c7e4c4d108b78ef3286f475ba85e16a8a 100644 --- a/substrate/client/consensus/babe/src/lib.rs +++ b/substrate/client/consensus/babe/src/lib.rs @@ -1418,7 +1418,7 @@ where // Skip babe logic if block already in chain or importing blocks during initial sync, // otherwise the check for epoch changes will error because trying to re-import an - // epoch change or because of missing epoch data in the tree, respectivelly. + // epoch change or because of missing epoch data in the tree, respectively. if info.block_gap.map_or(false, |(s, e)| s <= number && number <= e) || block_status == BlockStatus::InChain { diff --git a/substrate/client/consensus/babe/src/migration.rs b/substrate/client/consensus/babe/src/migration.rs index bec2d0a61f4b6d4f55236f69042bd764629b5f92..5f1ece3ec0e208306a935f269b47667cdb8b4863 100644 --- a/substrate/client/consensus/babe/src/migration.rs +++ b/substrate/client/consensus/babe/src/migration.rs @@ -64,7 +64,7 @@ impl EpochT for EpochV0 { // Implement From for Epoch impl EpochV0 { - /// Migrate the sturct to current epoch version. + /// Migrate the struct to current epoch version. pub fn migrate(self, config: &BabeConfiguration) -> Epoch { sp_consensus_babe::Epoch { epoch_index: self.epoch_index, diff --git a/substrate/client/consensus/beefy/README.md b/substrate/client/consensus/beefy/README.md index 13f88303a972ac35f2bbcb11a443619b603149a7..a7956cfcd42efab055ea7cc5d0efc14f54ea20ba 100644 --- a/substrate/client/consensus/beefy/README.md +++ b/substrate/client/consensus/beefy/README.md @@ -297,7 +297,7 @@ periodically on the global topic. Let's now dive into description of the message - Justification is considered worthwhile to gossip when: - It is for a recent (implementation specific) round or the latest mandatory round. - All signatures are valid and there is at least `2/3rd + 1` of them. - - Signatorees are part of the current validator set. + - Signatories are part of the current validator set. - Mandatory justifications should be announced periodically. ## Misbehavior diff --git a/substrate/client/consensus/beefy/src/aux_schema.rs b/substrate/client/consensus/beefy/src/aux_schema.rs index 944a00f8372fa8b0643dd4ddff8f8ce684e9f325..534f668ae69c2996064bef086e2958b23f48caf0 100644 --- a/substrate/client/consensus/beefy/src/aux_schema.rs +++ b/substrate/client/consensus/beefy/src/aux_schema.rs @@ -20,7 +20,7 @@ use crate::{error::Error, worker::PersistedState, LOG_TARGET}; use codec::{Decode, Encode}; -use log::{info, trace}; +use log::{debug, trace}; use sc_client_api::{backend::AuxStore, Backend}; use sp_runtime::traits::Block as BlockT; @@ -30,7 +30,7 @@ const WORKER_STATE_KEY: &[u8] = b"beefy_voter_state"; const CURRENT_VERSION: u32 = 4; pub(crate) fn write_current_version(backend: &BE) -> Result<(), Error> { - info!(target: LOG_TARGET, "🥩 write aux schema version {:?}", CURRENT_VERSION); + debug!(target: LOG_TARGET, "🥩 write aux schema version {:?}", CURRENT_VERSION); AuxStore::insert_aux(backend, &[(VERSION_KEY, CURRENT_VERSION.encode().as_slice())], &[]) .map_err(|e| Error::Backend(e.to_string())) } diff --git a/substrate/client/consensus/beefy/src/communication/gossip.rs b/substrate/client/consensus/beefy/src/communication/gossip.rs index 8a0b0a74308f1a6d6bf3bae327b87fac6fb6cdde..eb43c9173d751bf75bc4973aca248d80fb68ca48 100644 --- a/substrate/client/consensus/beefy/src/communication/gossip.rs +++ b/substrate/client/consensus/beefy/src/communication/gossip.rs @@ -56,6 +56,8 @@ pub(super) enum Action { Keep(H, ReputationChange), // discard, applying cost/benefit to originator. Discard(ReputationChange), + // ignore, no cost/benefit applied to originator. + DiscardNoReport, } /// An outcome of examining a message. @@ -68,7 +70,7 @@ enum Consider { /// Message is from the future. Reject. RejectFuture, /// Message cannot be evaluated. Reject. - RejectOutOfScope, + CannotEvaluate, } /// BEEFY gossip message type that gets encoded and sent on the network. @@ -168,18 +170,14 @@ impl Filter { .as_ref() .map(|f| // only from current set and only [filter.start, filter.end] - if set_id < f.validator_set.id() { + if set_id < f.validator_set.id() || round < f.start { Consider::RejectPast - } else if set_id > f.validator_set.id() { - Consider::RejectFuture - } else if round < f.start { - Consider::RejectPast - } else if round > f.end { + } else if set_id > f.validator_set.id() || round > f.end { Consider::RejectFuture } else { Consider::Accept }) - .unwrap_or(Consider::RejectOutOfScope) + .unwrap_or(Consider::CannotEvaluate) } /// Return true if `round` is >= than `max(session_start, best_beefy)`, @@ -199,7 +197,7 @@ impl Filter { Consider::Accept } ) - .unwrap_or(Consider::RejectOutOfScope) + .unwrap_or(Consider::CannotEvaluate) } /// Add new _known_ `round` to the set of seen valid justifications. @@ -244,7 +242,7 @@ where pub(crate) fn new( known_peers: Arc>>, ) -> (GossipValidator, TracingUnboundedReceiver) { - let (tx, rx) = tracing_unbounded("mpsc_beefy_gossip_validator", 10_000); + let (tx, rx) = tracing_unbounded("mpsc_beefy_gossip_validator", 100_000); let val = GossipValidator { votes_topic: votes_topic::(), justifs_topic: proofs_topic::(), @@ -289,7 +287,9 @@ where match filter.consider_vote(round, set_id) { Consider::RejectPast => return Action::Discard(cost::OUTDATED_MESSAGE), Consider::RejectFuture => return Action::Discard(cost::FUTURE_MESSAGE), - Consider::RejectOutOfScope => return Action::Discard(cost::OUT_OF_SCOPE_MESSAGE), + // When we can't evaluate, it's our fault (e.g. filter not initialized yet), we + // discard the vote without punishing or rewarding the sending peer. + Consider::CannotEvaluate => return Action::DiscardNoReport, Consider::Accept => {}, } @@ -330,7 +330,9 @@ where match guard.consider_finality_proof(round, set_id) { Consider::RejectPast => return Action::Discard(cost::OUTDATED_MESSAGE), Consider::RejectFuture => return Action::Discard(cost::FUTURE_MESSAGE), - Consider::RejectOutOfScope => return Action::Discard(cost::OUT_OF_SCOPE_MESSAGE), + // When we can't evaluate, it's our fault (e.g. filter not initialized yet), we + // discard the proof without punishing or rewarding the sending peer. + Consider::CannotEvaluate => return Action::DiscardNoReport, Consider::Accept => {}, } @@ -357,7 +359,9 @@ where Action::Keep(self.justifs_topic, benefit::VALIDATED_PROOF) } }) - .unwrap_or(Action::Discard(cost::OUT_OF_SCOPE_MESSAGE)) + // When we can't evaluate, it's our fault (e.g. filter not initialized yet), we + // discard the proof without punishing or rewarding the sending peer. + .unwrap_or(Action::DiscardNoReport) }; if matches!(action, Action::Keep(_, _)) { self.gossip_filter.write().mark_round_as_proven(round); @@ -404,6 +408,7 @@ where self.report(*sender, cb); ValidationResult::Discard }, + Action::DiscardNoReport => ValidationResult::Discard, } } @@ -579,8 +584,8 @@ pub(crate) mod tests { // filter not initialized let res = gv.validate(&mut context, &sender, &encoded); assert!(matches!(res, ValidationResult::Discard)); - expected_report.cost_benefit = cost::OUT_OF_SCOPE_MESSAGE; - assert_eq!(report_stream.try_recv().unwrap(), expected_report); + // nothing reported + assert!(report_stream.try_recv().is_err()); gv.update_filter(GossipFilterCfg { start: 0, end: 10, validator_set: &validator_set }); // nothing in cache first time diff --git a/substrate/client/consensus/beefy/src/communication/mod.rs b/substrate/client/consensus/beefy/src/communication/mod.rs index 3827559057dde856d201ebb9c9ff71a1d7d11f47..09c540e3b8a8459826e7e338c6dc0ee83aa1823a 100644 --- a/substrate/client/consensus/beefy/src/communication/mod.rs +++ b/substrate/client/consensus/beefy/src/communication/mod.rs @@ -90,8 +90,6 @@ mod cost { pub(super) const BAD_SIGNATURE: Rep = Rep::new(-100, "BEEFY: Bad signature"); // Message received with vote from voter not in validator set. pub(super) const UNKNOWN_VOTER: Rep = Rep::new(-150, "BEEFY: Unknown voter"); - // A message received that cannot be evaluated relative to our current state. - pub(super) const OUT_OF_SCOPE_MESSAGE: Rep = Rep::new(-500, "BEEFY: Out-of-scope message"); // Message containing invalid proof. pub(super) const INVALID_PROOF: Rep = Rep::new(-5000, "BEEFY: Invalid commit"); // Reputation cost per signature checked for invalid proof. @@ -101,7 +99,7 @@ mod cost { // On-demand request was refused by peer. pub(super) const REFUSAL_RESPONSE: Rep = Rep::new(-100, "BEEFY: Proof request refused"); // On-demand request for a proof that can't be found in the backend. - pub(super) const UNKOWN_PROOF_REQUEST: Rep = Rep::new(-150, "BEEFY: Unknown proof request"); + pub(super) const UNKNOWN_PROOF_REQUEST: Rep = Rep::new(-150, "BEEFY: Unknown proof request"); } // benefit scalars for reporting peers. diff --git a/substrate/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs b/substrate/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs index d856e9748a101b4ad71a39957579c41c0c75aaf4..ce184769fa7748368acb25db3228e4547ade4a5b 100644 --- a/substrate/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs +++ b/substrate/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs @@ -170,7 +170,7 @@ where .flatten() .and_then(|hash| self.client.justifications(hash).ok().flatten()) .and_then(|justifs| justifs.get(BEEFY_ENGINE_ID).cloned()) - .ok_or_else(|| reputation_changes.push(cost::UNKOWN_PROOF_REQUEST)); + .ok_or_else(|| reputation_changes.push(cost::UNKNOWN_PROOF_REQUEST)); request .pending_response .send(netconfig::OutgoingResponse { diff --git a/substrate/client/consensus/beefy/src/import.rs b/substrate/client/consensus/beefy/src/import.rs index fc19ecc301422d74d5da5a289ed641810b1022f1..ed8ed68c4e8d0d378728ba87d1ffe726f6c4c11a 100644 --- a/substrate/client/consensus/beefy/src/import.rs +++ b/substrate/client/consensus/beefy/src/import.rs @@ -159,7 +159,7 @@ where // The proof is valid and the block is imported and final, we can import. debug!( target: LOG_TARGET, - "🥩 import justif {:?} for block number {:?}.", proof, number + "🥩 import justif {} for block number {:?}.", proof, number ); // Send the justification to the BEEFY voter for processing. self.justification_sender diff --git a/substrate/client/consensus/beefy/src/keystore.rs b/substrate/client/consensus/beefy/src/keystore.rs index 2ddc938fbc6ce33aa74c62d051cf77a519c634ee..9582c2661c30b431a5934e2bcc592b4a56faf3b3 100644 --- a/substrate/client/consensus/beefy/src/keystore.rs +++ b/substrate/client/consensus/beefy/src/keystore.rs @@ -404,7 +404,7 @@ pub mod tests { let store: BeefyKeystore = Some(store).into(); - let msg = b"are you involved or commited?"; + let msg = b"are you involved or committed?"; let sig1 = store.sign(&alice, msg).unwrap(); let sig2 = Keyring::::Alice.sign(msg); @@ -440,7 +440,7 @@ pub mod tests { let alice = Keyring::Alice.public(); - let msg = b"are you involved or commited?"; + let msg = b"are you involved or committed?"; let sig = store.sign(&alice, msg).err().unwrap(); let err = Error::Signature(expected_error_message.to_string()); @@ -463,7 +463,7 @@ pub mod tests { let store: BeefyKeystore = None.into(); let alice = Keyring::Alice.public(); - let msg = b"are you involved or commited"; + let msg = b"are you involved or committed"; let sig = store.sign(&alice, msg).err().unwrap(); let err = Error::Keystore("no Keystore".to_string()); @@ -487,7 +487,7 @@ pub mod tests { let alice = Keyring::Alice.public(); // `msg` and `sig` match - let msg = b"are you involved or commited?"; + let msg = b"are you involved or committed?"; let sig = store.sign(&alice, msg).unwrap(); assert!(BeefyKeystore::verify(&alice, &sig, msg)); diff --git a/substrate/client/consensus/beefy/src/lib.rs b/substrate/client/consensus/beefy/src/lib.rs index 1f10d8099d836b55fab2b13a60dcc2faf0f37320..323af1bc8305c38bf7a34b4690b212954b7b0864 100644 --- a/substrate/client/consensus/beefy/src/lib.rs +++ b/substrate/client/consensus/beefy/src/lib.rs @@ -31,7 +31,7 @@ use crate::{ import::BeefyBlockImport, metrics::register_metrics, }; -use futures::{stream::Fuse, StreamExt}; +use futures::{stream::Fuse, FutureExt, StreamExt}; use log::{debug, error, info, warn}; use parking_lot::Mutex; use prometheus::Registry; @@ -40,17 +40,21 @@ use sc_consensus::BlockImport; use sc_network::{NetworkRequest, NotificationService, ProtocolName}; use sc_network_gossip::{GossipEngine, Network as GossipNetwork, Syncing as GossipSyncing}; use sp_api::ProvideRuntimeApi; -use sp_blockchain::{ - Backend as BlockchainBackend, Error as ClientError, HeaderBackend, Result as ClientResult, -}; +use sp_blockchain::{Backend as BlockchainBackend, HeaderBackend}; use sp_consensus::{Error as ConsensusError, SyncOracle}; use sp_consensus_beefy::{ - ecdsa_crypto::AuthorityId, BeefyApi, MmrRootHash, PayloadProvider, ValidatorSet, + ecdsa_crypto::AuthorityId, BeefyApi, ConsensusLog, MmrRootHash, PayloadProvider, ValidatorSet, + BEEFY_ENGINE_ID, }; use sp_keystore::KeystorePtr; use sp_mmr_primitives::MmrApi; use sp_runtime::traits::{Block, Header as HeaderT, NumberFor, Zero}; -use std::{collections::BTreeMap, marker::PhantomData, sync::Arc, time::Duration}; +use std::{ + collections::{BTreeMap, VecDeque}, + marker::PhantomData, + sync::Arc, + time::Duration, +}; mod aux_schema; mod error; @@ -63,9 +67,19 @@ pub mod communication; pub mod import; pub mod justification; +use crate::{ + communication::{gossip::GossipValidator, peers::PeerReport}, + justification::BeefyVersionedFinalityProof, + keystore::BeefyKeystore, + metrics::VoterMetrics, + round::Rounds, + worker::{BeefyWorker, PersistedState}, +}; pub use communication::beefy_protocol_name::{ gossip_protocol_name, justifications_protocol_name as justifs_protocol_name, }; +use sc_utils::mpsc::TracingUnboundedReceiver; +use sp_runtime::generic::OpaqueDigestItemId; #[cfg(test)] mod tests; @@ -209,6 +223,247 @@ pub struct BeefyParams { /// Handler for incoming BEEFY justifications requests from a remote peer. pub on_demand_justifications_handler: BeefyJustifsRequestHandler, } +/// Helper object holding BEEFY worker communication/gossip components. +/// +/// These are created once, but will be reused if worker is restarted/reinitialized. +pub(crate) struct BeefyComms { + pub gossip_engine: GossipEngine, + pub gossip_validator: Arc>, + pub gossip_report_stream: TracingUnboundedReceiver, + pub on_demand_justifications: OnDemandJustificationsEngine, +} + +/// Helper builder object for building [worker::BeefyWorker]. +/// +/// It has to do it in two steps: initialization and build, because the first step can sleep waiting +/// for certain chain and backend conditions, and while sleeping we still need to pump the +/// GossipEngine. Once initialization is done, the GossipEngine (and other pieces) are added to get +/// the complete [worker::BeefyWorker] object. +pub(crate) struct BeefyWorkerBuilder { + // utilities + backend: Arc, + runtime: Arc, + key_store: BeefyKeystore, + // voter metrics + metrics: Option, + persisted_state: PersistedState, +} + +impl BeefyWorkerBuilder +where + B: Block + codec::Codec, + BE: Backend, + R: ProvideRuntimeApi, + R::Api: BeefyApi, +{ + /// This will wait for the chain to enable BEEFY (if not yet enabled) and also wait for the + /// backend to sync all headers required by the voter to build a contiguous chain of mandatory + /// justifications. Then it builds the initial voter state using a combination of previously + /// persisted state in AUX DB and latest chain information/progress. + /// + /// Returns a sane `BeefyWorkerBuilder` that can build the `BeefyWorker`. + pub async fn async_initialize( + backend: Arc, + runtime: Arc, + key_store: BeefyKeystore, + metrics: Option, + min_block_delta: u32, + gossip_validator: Arc>, + finality_notifications: &mut Fuse>, + ) -> Result { + // Wait for BEEFY pallet to be active before starting voter. + let (beefy_genesis, best_grandpa) = + wait_for_runtime_pallet(&*runtime, finality_notifications).await?; + + let persisted_state = Self::load_or_init_state( + beefy_genesis, + best_grandpa, + min_block_delta, + backend.clone(), + runtime.clone(), + &key_store, + &metrics, + ) + .await?; + // Update the gossip validator with the right starting round and set id. + persisted_state + .gossip_filter_config() + .map(|f| gossip_validator.update_filter(f))?; + + Ok(BeefyWorkerBuilder { backend, runtime, key_store, metrics, persisted_state }) + } + + /// Takes rest of missing pieces as params and builds the `BeefyWorker`. + pub fn build( + self, + payload_provider: P, + sync: Arc, + comms: BeefyComms, + links: BeefyVoterLinks, + pending_justifications: BTreeMap, BeefyVersionedFinalityProof>, + ) -> BeefyWorker { + BeefyWorker { + backend: self.backend, + runtime: self.runtime, + key_store: self.key_store, + metrics: self.metrics, + persisted_state: self.persisted_state, + payload_provider, + sync, + comms, + links, + pending_justifications, + } + } + + // If no persisted state present, walk back the chain from first GRANDPA notification to either: + // - latest BEEFY finalized block, or if none found on the way, + // - BEEFY pallet genesis; + // Enqueue any BEEFY mandatory blocks (session boundaries) found on the way, for voter to + // finalize. + async fn init_state( + beefy_genesis: NumberFor, + best_grandpa: ::Header, + min_block_delta: u32, + backend: Arc, + runtime: Arc, + ) -> Result, Error> { + let blockchain = backend.blockchain(); + + let beefy_genesis = runtime + .runtime_api() + .beefy_genesis(best_grandpa.hash()) + .ok() + .flatten() + .filter(|genesis| *genesis == beefy_genesis) + .ok_or_else(|| Error::Backend("BEEFY pallet expected to be active.".into()))?; + // Walk back the imported blocks and initialize voter either, at the last block with + // a BEEFY justification, or at pallet genesis block; voter will resume from there. + let mut sessions = VecDeque::new(); + let mut header = best_grandpa.clone(); + let state = loop { + if let Some(true) = blockchain + .justifications(header.hash()) + .ok() + .flatten() + .map(|justifs| justifs.get(BEEFY_ENGINE_ID).is_some()) + { + debug!( + target: LOG_TARGET, + "🥩 Initialize BEEFY voter at last BEEFY finalized block: {:?}.", + *header.number() + ); + let best_beefy = *header.number(); + // If no session boundaries detected so far, just initialize new rounds here. + if sessions.is_empty() { + let active_set = + expect_validator_set(runtime.as_ref(), backend.as_ref(), &header).await?; + let mut rounds = Rounds::new(best_beefy, active_set); + // Mark the round as already finalized. + rounds.conclude(best_beefy); + sessions.push_front(rounds); + } + let state = PersistedState::checked_new( + best_grandpa, + best_beefy, + sessions, + min_block_delta, + beefy_genesis, + ) + .ok_or_else(|| Error::Backend("Invalid BEEFY chain".into()))?; + break state + } + + if *header.number() == beefy_genesis { + // We've reached BEEFY genesis, initialize voter here. + let genesis_set = + expect_validator_set(runtime.as_ref(), backend.as_ref(), &header).await?; + info!( + target: LOG_TARGET, + "🥩 Loading BEEFY voter state from genesis on what appears to be first startup. \ + Starting voting rounds at block {:?}, genesis validator set {:?}.", + beefy_genesis, + genesis_set, + ); + + sessions.push_front(Rounds::new(beefy_genesis, genesis_set)); + break PersistedState::checked_new( + best_grandpa, + Zero::zero(), + sessions, + min_block_delta, + beefy_genesis, + ) + .ok_or_else(|| Error::Backend("Invalid BEEFY chain".into()))? + } + + if let Some(active) = find_authorities_change::(&header) { + debug!( + target: LOG_TARGET, + "🥩 Marking block {:?} as BEEFY Mandatory.", + *header.number() + ); + sessions.push_front(Rounds::new(*header.number(), active)); + } + + // Move up the chain. + header = wait_for_parent_header(blockchain, header, HEADER_SYNC_DELAY).await?; + }; + + aux_schema::write_current_version(backend.as_ref())?; + aux_schema::write_voter_state(backend.as_ref(), &state)?; + Ok(state) + } + + async fn load_or_init_state( + beefy_genesis: NumberFor, + best_grandpa: ::Header, + min_block_delta: u32, + backend: Arc, + runtime: Arc, + key_store: &BeefyKeystore, + metrics: &Option, + ) -> Result, Error> { + // Initialize voter state from AUX DB if compatible. + if let Some(mut state) = crate::aux_schema::load_persistent(backend.as_ref())? + // Verify state pallet genesis matches runtime. + .filter(|state| state.pallet_genesis() == beefy_genesis) + { + // Overwrite persisted state with current best GRANDPA block. + state.set_best_grandpa(best_grandpa.clone()); + // Overwrite persisted data with newly provided `min_block_delta`. + state.set_min_block_delta(min_block_delta); + debug!(target: LOG_TARGET, "🥩 Loading BEEFY voter state from db: {:?}.", state); + + // Make sure that all the headers that we need have been synced. + let mut new_sessions = vec![]; + let mut header = best_grandpa.clone(); + while *header.number() > state.best_beefy() { + if state.voting_oracle().can_add_session(*header.number()) { + if let Some(active) = find_authorities_change::(&header) { + new_sessions.push((active, *header.number())); + } + } + header = + wait_for_parent_header(backend.blockchain(), header, HEADER_SYNC_DELAY).await?; + } + + // Make sure we didn't miss any sessions during node restart. + for (validator_set, new_session_start) in new_sessions.drain(..).rev() { + debug!( + target: LOG_TARGET, + "🥩 Handling missed BEEFY session after node restart: {:?}.", + new_session_start + ); + state.init_session_at(new_session_start, validator_set, key_store, metrics); + } + return Ok(state) + } + + // No valid voter-state persisted, re-initialize from pallet genesis. + Self::init_state(beefy_genesis, best_grandpa, min_block_delta, backend, runtime).await + } +} /// Start the BEEFY gadget. /// @@ -277,7 +532,7 @@ pub async fn start_beefy_gadget( known_peers, prometheus_registry.clone(), ); - let mut beefy_comms = worker::BeefyComms { + let mut beefy_comms = BeefyComms { gossip_engine, gossip_validator, gossip_report_stream, @@ -287,57 +542,45 @@ pub async fn start_beefy_gadget( // We re-create and re-run the worker in this loop in order to quickly reinit and resume after // select recoverable errors. loop { - // Wait for BEEFY pallet to be active before starting voter. - let (beefy_genesis, best_grandpa) = match wait_for_runtime_pallet( - &*runtime, - &mut beefy_comms.gossip_engine, - &mut finality_notifications, - ) - .await - { - Ok(res) => res, - Err(e) => { - error!(target: LOG_TARGET, "Error: {:?}. Terminating.", e); - return - }, - }; - - let mut worker_base = worker::BeefyWorkerBase { - backend: backend.clone(), - runtime: runtime.clone(), - key_store: key_store.clone().into(), - metrics: metrics.clone(), - _phantom: Default::default(), - }; - - let persisted_state = match worker_base - .load_or_init_state(beefy_genesis, best_grandpa, min_block_delta) - .await - { - Ok(state) => state, - Err(e) => { - error!(target: LOG_TARGET, "Error: {:?}. Terminating.", e); - return - }, + // Make sure to pump gossip engine while waiting for initialization conditions. + let worker_builder = loop { + futures::select! { + builder_init_result = BeefyWorkerBuilder::async_initialize( + backend.clone(), + runtime.clone(), + key_store.clone().into(), + metrics.clone(), + min_block_delta, + beefy_comms.gossip_validator.clone(), + &mut finality_notifications, + ).fuse() => { + match builder_init_result { + Ok(builder) => break builder, + Err(e) => { + error!(target: LOG_TARGET, "🥩 Error: {:?}. Terminating.", e); + return + }, + } + }, + // Pump peer reports + _ = &mut beefy_comms.gossip_report_stream.next() => { + continue + }, + // Pump gossip engine. + _ = &mut beefy_comms.gossip_engine => { + error!(target: LOG_TARGET, "🥩 Gossip engine has unexpectedly terminated."); + return + } + } }; - // Update the gossip validator with the right starting round and set id. - if let Err(e) = persisted_state - .gossip_filter_config() - .map(|f| beefy_comms.gossip_validator.update_filter(f)) - { - error!(target: LOG_TARGET, "Error: {:?}. Terminating.", e); - return - } - let worker = worker::BeefyWorker { - base: worker_base, - payload_provider: payload_provider.clone(), - sync: sync.clone(), - comms: beefy_comms, - links: links.clone(), - pending_justifications: BTreeMap::new(), - persisted_state, - }; + let worker = worker_builder.build( + payload_provider.clone(), + sync.clone(), + beefy_comms, + links.clone(), + BTreeMap::new(), + ); match futures::future::select( Box::pin(worker.run(&mut block_import_justif, &mut finality_notifications)), @@ -404,9 +647,8 @@ where /// Should be called only once during worker initialization. async fn wait_for_runtime_pallet( runtime: &R, - mut gossip_engine: &mut GossipEngine, finality: &mut Fuse>, -) -> ClientResult<(NumberFor, ::Header)> +) -> Result<(NumberFor, ::Header), Error> where B: Block, R: ProvideRuntimeApi, @@ -414,33 +656,24 @@ where { info!(target: LOG_TARGET, "🥩 BEEFY gadget waiting for BEEFY pallet to become available..."); loop { - futures::select! { - notif = finality.next() => { - let notif = match notif { - Some(notif) => notif, - None => break - }; - let at = notif.header.hash(); - if let Some(start) = runtime.runtime_api().beefy_genesis(at).ok().flatten() { - if *notif.header.number() >= start { - // Beefy pallet available, return header for best grandpa at the time. - info!( - target: LOG_TARGET, - "🥩 BEEFY pallet available: block {:?} beefy genesis {:?}", - notif.header.number(), start - ); - return Ok((start, notif.header)) - } - } - }, - _ = gossip_engine => { - break + let notif = finality.next().await.ok_or_else(|| { + let err_msg = "🥩 Finality stream has unexpectedly terminated.".into(); + error!(target: LOG_TARGET, "{}", err_msg); + Error::Backend(err_msg) + })?; + let at = notif.header.hash(); + if let Some(start) = runtime.runtime_api().beefy_genesis(at).ok().flatten() { + if *notif.header.number() >= start { + // Beefy pallet available, return header for best grandpa at the time. + info!( + target: LOG_TARGET, + "🥩 BEEFY pallet available: block {:?} beefy genesis {:?}", + notif.header.number(), start + ); + return Ok((start, notif.header)) } } } - let err_msg = "🥩 Gossip engine has unexpectedly terminated.".into(); - error!(target: LOG_TARGET, "{}", err_msg); - Err(ClientError::Backend(err_msg)) } /// Provides validator set active `at_header`. It tries to get it from state, otherwise falls @@ -460,17 +693,21 @@ where R::Api: BeefyApi, { let blockchain = backend.blockchain(); - // Walk up the chain looking for the validator set active at 'at_header'. Process both state and // header digests. - debug!(target: LOG_TARGET, "🥩 Trying to find validator set active at header: {:?}", at_header); + debug!( + target: LOG_TARGET, + "🥩 Trying to find validator set active at header(number {:?}, hash {:?})", + at_header.number(), + at_header.hash() + ); let mut header = at_header.clone(); loop { debug!(target: LOG_TARGET, "🥩 Looking for auth set change at block number: {:?}", *header.number()); if let Ok(Some(active)) = runtime.runtime_api().validator_set(header.hash()) { return Ok(active) } else { - match worker::find_authorities_change::(&header) { + match find_authorities_change::(&header) { Some(active) => return Ok(active), // Move up the chain. Ultimately we'll get it from chain genesis state, or error out // there. @@ -482,3 +719,18 @@ where } } } + +/// Scan the `header` digest log for a BEEFY validator set change. Return either the new +/// validator set or `None` in case no validator set change has been signaled. +pub(crate) fn find_authorities_change(header: &B::Header) -> Option> +where + B: Block, +{ + let id = OpaqueDigestItemId::Consensus(&BEEFY_ENGINE_ID); + + let filter = |log: ConsensusLog| match log { + ConsensusLog::AuthoritiesChange(validator_set) => Some(validator_set), + _ => None, + }; + header.digest().convert_first(|l| l.try_to(id).and_then(filter)) +} diff --git a/substrate/client/consensus/beefy/src/tests.rs b/substrate/client/consensus/beefy/src/tests.rs index 0e6ff210b158c18334a749992d096e2d54c77a01..d106c9dcd88165f929085972483e090dbf78c559 100644 --- a/substrate/client/consensus/beefy/src/tests.rs +++ b/substrate/client/consensus/beefy/src/tests.rs @@ -32,8 +32,8 @@ use crate::{ gossip_protocol_name, justification::*, wait_for_runtime_pallet, - worker::{BeefyWorkerBase, PersistedState}, - BeefyRPCLinks, BeefyVoterLinks, KnownPeers, + worker::PersistedState, + BeefyRPCLinks, BeefyVoterLinks, BeefyWorkerBuilder, KnownPeers, }; use futures::{future, stream::FuturesUnordered, Future, FutureExt, StreamExt}; use parking_lot::Mutex; @@ -368,27 +368,19 @@ async fn voter_init_setup( api: &TestApi, ) -> Result, Error> { let backend = net.peer(0).client().as_backend(); - let known_peers = Arc::new(Mutex::new(KnownPeers::new())); - let (gossip_validator, _) = GossipValidator::new(known_peers); - let gossip_validator = Arc::new(gossip_validator); - let mut gossip_engine = sc_network_gossip::GossipEngine::new( - net.peer(0).network_service().clone(), - net.peer(0).sync_service().clone(), - net.peer(0).take_notification_service(&beefy_gossip_proto_name()).unwrap(), - "/beefy/whatever", - gossip_validator, - None, - ); - let (beefy_genesis, best_grandpa) = - wait_for_runtime_pallet(api, &mut gossip_engine, finality).await.unwrap(); - let mut worker_base = BeefyWorkerBase { + let (beefy_genesis, best_grandpa) = wait_for_runtime_pallet(api, finality).await.unwrap(); + let key_store = None.into(); + let metrics = None; + BeefyWorkerBuilder::load_or_init_state( + beefy_genesis, + best_grandpa, + 1, backend, - runtime: Arc::new(api.clone()), - key_store: None.into(), - metrics: None, - _phantom: Default::default(), - }; - worker_base.load_or_init_state(beefy_genesis, best_grandpa, 1).await + Arc::new(api.clone()), + &key_store, + &metrics, + ) + .await } // Spawns beefy voters. Returns a future to spawn on the runtime. @@ -1065,32 +1057,7 @@ async fn should_initialize_voter_at_custom_genesis() { net.peer(0).client().as_client().finalize_block(hashes[8], None).unwrap(); // load persistent state - nothing in DB, should init at genesis - // - // NOTE: code from `voter_init_setup()` is moved here because the new network event system - // doesn't allow creating a new `GossipEngine` as the notification handle is consumed by the - // first `GossipEngine` - let known_peers = Arc::new(Mutex::new(KnownPeers::new())); - let (gossip_validator, _) = GossipValidator::new(known_peers); - let gossip_validator = Arc::new(gossip_validator); - let mut gossip_engine = sc_network_gossip::GossipEngine::new( - net.peer(0).network_service().clone(), - net.peer(0).sync_service().clone(), - net.peer(0).take_notification_service(&beefy_gossip_proto_name()).unwrap(), - "/beefy/whatever", - gossip_validator, - None, - ); - let (beefy_genesis, best_grandpa) = - wait_for_runtime_pallet(&api, &mut gossip_engine, &mut finality).await.unwrap(); - let mut worker_base = BeefyWorkerBase { - backend: backend.clone(), - runtime: Arc::new(api), - key_store: None.into(), - metrics: None, - _phantom: Default::default(), - }; - let persisted_state = - worker_base.load_or_init_state(beefy_genesis, best_grandpa, 1).await.unwrap(); + let persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); // Test initialization at session boundary. // verify voter initialized with single session starting at block `custom_pallet_genesis` (7) @@ -1120,18 +1087,7 @@ async fn should_initialize_voter_at_custom_genesis() { net.peer(0).client().as_client().finalize_block(hashes[10], None).unwrap(); // load persistent state - state preset in DB, but with different pallet genesis - // the network state persists and uses the old `GossipEngine` initialized for `peer(0)` - let (beefy_genesis, best_grandpa) = - wait_for_runtime_pallet(&api, &mut gossip_engine, &mut finality).await.unwrap(); - let mut worker_base = BeefyWorkerBase { - backend: backend.clone(), - runtime: Arc::new(api), - key_store: None.into(), - metrics: None, - _phantom: Default::default(), - }; - let new_persisted_state = - worker_base.load_or_init_state(beefy_genesis, best_grandpa, 1).await.unwrap(); + let new_persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); // verify voter initialized with single session starting at block `new_pallet_genesis` (10) let sessions = new_persisted_state.voting_oracle().sessions(); @@ -1322,32 +1278,7 @@ async fn should_catch_up_when_loading_saved_voter_state() { let api = TestApi::with_validator_set(&validator_set); // load persistent state - nothing in DB, should init at genesis - // - // NOTE: code from `voter_init_setup()` is moved here because the new network event system - // doesn't allow creating a new `GossipEngine` as the notification handle is consumed by the - // first `GossipEngine` - let known_peers = Arc::new(Mutex::new(KnownPeers::new())); - let (gossip_validator, _) = GossipValidator::new(known_peers); - let gossip_validator = Arc::new(gossip_validator); - let mut gossip_engine = sc_network_gossip::GossipEngine::new( - net.peer(0).network_service().clone(), - net.peer(0).sync_service().clone(), - net.peer(0).take_notification_service(&beefy_gossip_proto_name()).unwrap(), - "/beefy/whatever", - gossip_validator, - None, - ); - let (beefy_genesis, best_grandpa) = - wait_for_runtime_pallet(&api, &mut gossip_engine, &mut finality).await.unwrap(); - let mut worker_base = BeefyWorkerBase { - backend: backend.clone(), - runtime: Arc::new(api.clone()), - key_store: None.into(), - metrics: None, - _phantom: Default::default(), - }; - let persisted_state = - worker_base.load_or_init_state(beefy_genesis, best_grandpa, 1).await.unwrap(); + let persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); // Test initialization at session boundary. // verify voter initialized with two sessions starting at blocks 1 and 10 @@ -1374,18 +1305,7 @@ async fn should_catch_up_when_loading_saved_voter_state() { // finalize 25 without justifications net.peer(0).client().as_client().finalize_block(hashes[25], None).unwrap(); // load persistent state - state preset in DB - // the network state persists and uses the old `GossipEngine` initialized for `peer(0)` - let (beefy_genesis, best_grandpa) = - wait_for_runtime_pallet(&api, &mut gossip_engine, &mut finality).await.unwrap(); - let mut worker_base = BeefyWorkerBase { - backend: backend.clone(), - runtime: Arc::new(api), - key_store: None.into(), - metrics: None, - _phantom: Default::default(), - }; - let persisted_state = - worker_base.load_or_init_state(beefy_genesis, best_grandpa, 1).await.unwrap(); + let persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); // Verify voter initialized with old sessions plus a new one starting at block 20. // There shouldn't be any duplicates. diff --git a/substrate/client/consensus/beefy/src/worker.rs b/substrate/client/consensus/beefy/src/worker.rs index 19a24b9bc1a548936483537f7ea057cba1592d8c..7a47f286ef755973f418e02db16affb045d381c4 100644 --- a/substrate/client/consensus/beefy/src/worker.rs +++ b/substrate/client/consensus/beefy/src/worker.rs @@ -17,46 +17,42 @@ // along with this program. If not, see . use crate::{ - aux_schema, communication::{ - gossip::{proofs_topic, votes_topic, GossipFilterCfg, GossipMessage, GossipValidator}, + gossip::{proofs_topic, votes_topic, GossipFilterCfg, GossipMessage}, peers::PeerReport, - request_response::outgoing_requests_engine::{OnDemandJustificationsEngine, ResponseInfo}, + request_response::outgoing_requests_engine::ResponseInfo, }, error::Error, - expect_validator_set, + find_authorities_change, justification::BeefyVersionedFinalityProof, keystore::BeefyKeystore, metric_inc, metric_set, metrics::VoterMetrics, round::{Rounds, VoteImportResult}, - wait_for_parent_header, BeefyVoterLinks, HEADER_SYNC_DELAY, LOG_TARGET, + BeefyComms, BeefyVoterLinks, LOG_TARGET, }; use codec::{Codec, Decode, DecodeAll, Encode}; use futures::{stream::Fuse, FutureExt, StreamExt}; use log::{debug, error, info, log_enabled, trace, warn}; use sc_client_api::{Backend, FinalityNotification, FinalityNotifications, HeaderBackend}; -use sc_network_gossip::GossipEngine; -use sc_utils::{mpsc::TracingUnboundedReceiver, notification::NotificationReceiver}; +use sc_utils::notification::NotificationReceiver; use sp_api::ProvideRuntimeApi; use sp_arithmetic::traits::{AtLeast32Bit, Saturating}; -use sp_blockchain::Backend as BlockchainBackend; use sp_consensus::SyncOracle; use sp_consensus_beefy::{ check_equivocation_proof, ecdsa_crypto::{AuthorityId, Signature}, - BeefyApi, BeefySignatureHasher, Commitment, ConsensusLog, EquivocationProof, PayloadProvider, - ValidatorSet, VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID, + BeefyApi, BeefySignatureHasher, Commitment, EquivocationProof, PayloadProvider, ValidatorSet, + VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID, }; use sp_runtime::{ - generic::{BlockId, OpaqueDigestItemId}, + generic::BlockId, traits::{Block, Header, NumberFor, Zero}, SaturatedConversion, }; use std::{ collections::{BTreeMap, BTreeSet, VecDeque}, fmt::Debug, - marker::PhantomData, sync::Arc, }; @@ -80,13 +76,13 @@ pub(crate) enum RoundAction { pub(crate) struct VoterOracle { /// Queue of known sessions. Keeps track of voting rounds (block numbers) within each session. /// - /// There are three voter states coresponding to three queue states: + /// There are three voter states corresponding to three queue states: /// 1. voter uninitialized: queue empty, /// 2. up-to-date - all mandatory blocks leading up to current GRANDPA finalized: queue has ONE /// element, the 'current session' where `mandatory_done == true`, /// 3. lagging behind GRANDPA: queue has [1, N] elements, where all `mandatory_done == false`. - /// In this state, everytime a session gets its mandatory block BEEFY finalized, it's popped - /// off the queue, eventually getting to state `2. up-to-date`. + /// In this state, every time a session gets its mandatory block BEEFY finalized, it's + /// popped off the queue, eventually getting to state `2. up-to-date`. sessions: VecDeque>, /// Min delta in block numbers between two blocks, BEEFY should vote on. min_block_delta: u32, @@ -180,8 +176,8 @@ impl VoterOracle { } } - // Check if an observed session can be added to the Oracle. - fn can_add_session(&self, session_start: NumberFor) -> bool { + /// Check if an observed session can be added to the Oracle. + pub fn can_add_session(&self, session_start: NumberFor) -> bool { let latest_known_session_start = self.sessions.back().map(|session| session.session_start()); Some(session_start) > latest_known_session_start @@ -319,229 +315,28 @@ impl PersistedState { self.voting_oracle.best_grandpa_block_header = best_grandpa; } + pub fn voting_oracle(&self) -> &VoterOracle { + &self.voting_oracle + } + pub(crate) fn gossip_filter_config(&self) -> Result, Error> { let (start, end) = self.voting_oracle.accepted_interval()?; let validator_set = self.voting_oracle.current_validator_set()?; Ok(GossipFilterCfg { start, end, validator_set }) } -} - -/// Helper object holding BEEFY worker communication/gossip components. -/// -/// These are created once, but will be reused if worker is restarted/reinitialized. -pub(crate) struct BeefyComms { - pub gossip_engine: GossipEngine, - pub gossip_validator: Arc>, - pub gossip_report_stream: TracingUnboundedReceiver, - pub on_demand_justifications: OnDemandJustificationsEngine, -} - -pub(crate) struct BeefyWorkerBase { - // utilities - pub backend: Arc, - pub runtime: Arc, - pub key_store: BeefyKeystore, - - /// BEEFY client metrics. - pub metrics: Option, - - pub _phantom: PhantomData, -} - -impl BeefyWorkerBase -where - B: Block + Codec, - BE: Backend, - R: ProvideRuntimeApi, - R::Api: BeefyApi, -{ - // If no persisted state present, walk back the chain from first GRANDPA notification to either: - // - latest BEEFY finalized block, or if none found on the way, - // - BEEFY pallet genesis; - // Enqueue any BEEFY mandatory blocks (session boundaries) found on the way, for voter to - // finalize. - async fn init_state( - &self, - beefy_genesis: NumberFor, - best_grandpa: ::Header, - min_block_delta: u32, - ) -> Result, Error> { - let blockchain = self.backend.blockchain(); - - let beefy_genesis = self - .runtime - .runtime_api() - .beefy_genesis(best_grandpa.hash()) - .ok() - .flatten() - .filter(|genesis| *genesis == beefy_genesis) - .ok_or_else(|| Error::Backend("BEEFY pallet expected to be active.".into()))?; - // Walk back the imported blocks and initialize voter either, at the last block with - // a BEEFY justification, or at pallet genesis block; voter will resume from there. - let mut sessions = VecDeque::new(); - let mut header = best_grandpa.clone(); - let state = loop { - if let Some(true) = blockchain - .justifications(header.hash()) - .ok() - .flatten() - .map(|justifs| justifs.get(BEEFY_ENGINE_ID).is_some()) - { - info!( - target: LOG_TARGET, - "🥩 Initialize BEEFY voter at last BEEFY finalized block: {:?}.", - *header.number() - ); - let best_beefy = *header.number(); - // If no session boundaries detected so far, just initialize new rounds here. - if sessions.is_empty() { - let active_set = - expect_validator_set(self.runtime.as_ref(), self.backend.as_ref(), &header) - .await?; - let mut rounds = Rounds::new(best_beefy, active_set); - // Mark the round as already finalized. - rounds.conclude(best_beefy); - sessions.push_front(rounds); - } - let state = PersistedState::checked_new( - best_grandpa, - best_beefy, - sessions, - min_block_delta, - beefy_genesis, - ) - .ok_or_else(|| Error::Backend("Invalid BEEFY chain".into()))?; - break state - } - - if *header.number() == beefy_genesis { - // We've reached BEEFY genesis, initialize voter here. - let genesis_set = - expect_validator_set(self.runtime.as_ref(), self.backend.as_ref(), &header) - .await?; - info!( - target: LOG_TARGET, - "🥩 Loading BEEFY voter state from genesis on what appears to be first startup. \ - Starting voting rounds at block {:?}, genesis validator set {:?}.", - beefy_genesis, - genesis_set, - ); - - sessions.push_front(Rounds::new(beefy_genesis, genesis_set)); - break PersistedState::checked_new( - best_grandpa, - Zero::zero(), - sessions, - min_block_delta, - beefy_genesis, - ) - .ok_or_else(|| Error::Backend("Invalid BEEFY chain".into()))? - } - - if let Some(active) = find_authorities_change::(&header) { - info!( - target: LOG_TARGET, - "🥩 Marking block {:?} as BEEFY Mandatory.", - *header.number() - ); - sessions.push_front(Rounds::new(*header.number(), active)); - } - - // Move up the chain. - header = wait_for_parent_header(blockchain, header, HEADER_SYNC_DELAY).await?; - }; - - aux_schema::write_current_version(self.backend.as_ref())?; - aux_schema::write_voter_state(self.backend.as_ref(), &state)?; - Ok(state) - } - - pub async fn load_or_init_state( - &mut self, - beefy_genesis: NumberFor, - best_grandpa: ::Header, - min_block_delta: u32, - ) -> Result, Error> { - // Initialize voter state from AUX DB if compatible. - if let Some(mut state) = crate::aux_schema::load_persistent(self.backend.as_ref())? - // Verify state pallet genesis matches runtime. - .filter(|state| state.pallet_genesis() == beefy_genesis) - { - // Overwrite persisted state with current best GRANDPA block. - state.set_best_grandpa(best_grandpa.clone()); - // Overwrite persisted data with newly provided `min_block_delta`. - state.set_min_block_delta(min_block_delta); - info!(target: LOG_TARGET, "🥩 Loading BEEFY voter state from db: {:?}.", state); - - // Make sure that all the headers that we need have been synced. - let mut new_sessions = vec![]; - let mut header = best_grandpa.clone(); - while *header.number() > state.best_beefy() { - if state.voting_oracle.can_add_session(*header.number()) { - if let Some(active) = find_authorities_change::(&header) { - new_sessions.push((active, *header.number())); - } - } - header = - wait_for_parent_header(self.backend.blockchain(), header, HEADER_SYNC_DELAY) - .await?; - } - - // Make sure we didn't miss any sessions during node restart. - for (validator_set, new_session_start) in new_sessions.drain(..).rev() { - info!( - target: LOG_TARGET, - "🥩 Handling missed BEEFY session after node restart: {:?}.", - new_session_start - ); - self.init_session_at(&mut state, validator_set, new_session_start); - } - return Ok(state) - } - - // No valid voter-state persisted, re-initialize from pallet genesis. - self.init_state(beefy_genesis, best_grandpa, min_block_delta).await - } - - /// Verify `active` validator set for `block` against the key store - /// - /// We want to make sure that we have _at least one_ key in our keystore that - /// is part of the validator set, that's because if there are no local keys - /// then we can't perform our job as a validator. - /// - /// Note that for a non-authority node there will be no keystore, and we will - /// return an error and don't check. The error can usually be ignored. - fn verify_validator_set( - &self, - block: &NumberFor, - active: &ValidatorSet, - ) -> Result<(), Error> { - let active: BTreeSet<&AuthorityId> = active.validators().iter().collect(); - - let public_keys = self.key_store.public_keys()?; - let store: BTreeSet<&AuthorityId> = public_keys.iter().collect(); - - if store.intersection(&active).count() == 0 { - let msg = "no authority public key found in store".to_string(); - debug!(target: LOG_TARGET, "🥩 for block {:?} {}", block, msg); - metric_inc!(self.metrics, beefy_no_authority_found_in_store); - Err(Error::Keystore(msg)) - } else { - Ok(()) - } - } /// Handle session changes by starting new voting round for mandatory blocks. - fn init_session_at( + pub fn init_session_at( &mut self, - persisted_state: &mut PersistedState, - validator_set: ValidatorSet, new_session_start: NumberFor, + validator_set: ValidatorSet, + key_store: &BeefyKeystore, + metrics: &Option, ) { debug!(target: LOG_TARGET, "🥩 New active validator set: {:?}", validator_set); // BEEFY should finalize a mandatory block during each session. - if let Ok(active_session) = persisted_state.voting_oracle.active_rounds() { + if let Ok(active_session) = self.voting_oracle.active_rounds() { if !active_session.mandatory_done() { debug!( target: LOG_TARGET, @@ -549,20 +344,20 @@ where validator_set.id(), active_session.validator_set_id(), ); - metric_inc!(self.metrics, beefy_lagging_sessions); + metric_inc!(metrics, beefy_lagging_sessions); } } if log_enabled!(target: LOG_TARGET, log::Level::Debug) { // verify the new validator set - only do it if we're also logging the warning - let _ = self.verify_validator_set(&new_session_start, &validator_set); + if verify_validator_set::(&new_session_start, &validator_set, key_store).is_err() { + metric_inc!(metrics, beefy_no_authority_found_in_store); + } } let id = validator_set.id(); - persisted_state - .voting_oracle - .add_session(Rounds::new(new_session_start, validator_set)); - metric_set!(self.metrics, beefy_validator_set_id, id); + self.voting_oracle.add_session(Rounds::new(new_session_start, validator_set)); + metric_set!(metrics, beefy_validator_set_id, id); info!( target: LOG_TARGET, "🥩 New Rounds for validator set id: {:?} with session_start {:?}", @@ -574,9 +369,10 @@ where /// A BEEFY worker/voter that follows the BEEFY protocol pub(crate) struct BeefyWorker { - pub base: BeefyWorkerBase, - - // utils + // utilities + pub backend: Arc, + pub runtime: Arc, + pub key_store: BeefyKeystore, pub payload_provider: P, pub sync: Arc, @@ -592,6 +388,8 @@ pub(crate) struct BeefyWorker { pub pending_justifications: BTreeMap, BeefyVersionedFinalityProof>, /// Persisted voter state. pub persisted_state: PersistedState, + /// BEEFY voter metrics + pub metrics: Option, } impl BeefyWorker @@ -622,24 +420,28 @@ where validator_set: ValidatorSet, new_session_start: NumberFor, ) { - self.base - .init_session_at(&mut self.persisted_state, validator_set, new_session_start); + self.persisted_state.init_session_at( + new_session_start, + validator_set, + &self.key_store, + &self.metrics, + ); } fn handle_finality_notification( &mut self, notification: &FinalityNotification, ) -> Result<(), Error> { + let header = ¬ification.header; debug!( target: LOG_TARGET, - "🥩 Finality notification: header {:?} tree_route {:?}", - notification.header, + "🥩 Finality notification: header(number {:?}, hash {:?}) tree_route {:?}", + header.number(), + header.hash(), notification.tree_route, ); - let header = ¬ification.header; - self.base - .runtime + self.runtime .runtime_api() .beefy_genesis(header.hash()) .ok() @@ -653,7 +455,7 @@ where self.persisted_state.set_best_grandpa(header.clone()); // Check all (newly) finalized blocks for new session(s). - let backend = self.base.backend.clone(); + let backend = self.backend.clone(); for header in notification .tree_route .iter() @@ -672,7 +474,7 @@ where } if new_session_added { - crate::aux_schema::write_voter_state(&*self.base.backend, &self.persisted_state) + crate::aux_schema::write_voter_state(&*self.backend, &self.persisted_state) .map_err(|e| Error::Backend(e.to_string()))?; } @@ -706,7 +508,7 @@ where true, ); }, - RoundAction::Drop => metric_inc!(self.base.metrics, beefy_stale_votes), + RoundAction::Drop => metric_inc!(self.metrics, beefy_stale_votes), RoundAction::Enqueue => error!(target: LOG_TARGET, "🥩 unexpected vote: {:?}.", vote), }; Ok(()) @@ -726,23 +528,23 @@ where match self.voting_oracle().triage_round(block_num)? { RoundAction::Process => { debug!(target: LOG_TARGET, "🥩 Process justification for round: {:?}.", block_num); - metric_inc!(self.base.metrics, beefy_imported_justifications); + metric_inc!(self.metrics, beefy_imported_justifications); self.finalize(justification)? }, RoundAction::Enqueue => { debug!(target: LOG_TARGET, "🥩 Buffer justification for round: {:?}.", block_num); if self.pending_justifications.len() < MAX_BUFFERED_JUSTIFICATIONS { self.pending_justifications.entry(block_num).or_insert(justification); - metric_inc!(self.base.metrics, beefy_buffered_justifications); + metric_inc!(self.metrics, beefy_buffered_justifications); } else { - metric_inc!(self.base.metrics, beefy_buffered_justifications_dropped); + metric_inc!(self.metrics, beefy_buffered_justifications_dropped); warn!( target: LOG_TARGET, "🥩 Buffer justification dropped for round: {:?}.", block_num ); } }, - RoundAction::Drop => metric_inc!(self.base.metrics, beefy_stale_justifications), + RoundAction::Drop => metric_inc!(self.metrics, beefy_stale_justifications), }; Ok(()) } @@ -757,14 +559,14 @@ where match rounds.add_vote(vote) { VoteImportResult::RoundConcluded(signed_commitment) => { let finality_proof = VersionedFinalityProof::V1(signed_commitment); - info!( + debug!( target: LOG_TARGET, "🥩 Round #{} concluded, finality_proof: {:?}.", block_number, finality_proof ); // We created the `finality_proof` and know to be valid. // New state is persisted after finalization. self.finalize(finality_proof.clone())?; - metric_inc!(self.base.metrics, beefy_good_votes_processed); + metric_inc!(self.metrics, beefy_good_votes_processed); return Ok(Some(finality_proof)) }, VoteImportResult::Ok => { @@ -775,20 +577,17 @@ where .map(|(mandatory_num, _)| mandatory_num == block_number) .unwrap_or(false) { - crate::aux_schema::write_voter_state( - &*self.base.backend, - &self.persisted_state, - ) - .map_err(|e| Error::Backend(e.to_string()))?; + crate::aux_schema::write_voter_state(&*self.backend, &self.persisted_state) + .map_err(|e| Error::Backend(e.to_string()))?; } - metric_inc!(self.base.metrics, beefy_good_votes_processed); + metric_inc!(self.metrics, beefy_good_votes_processed); }, VoteImportResult::Equivocation(proof) => { - metric_inc!(self.base.metrics, beefy_equivocation_votes); + metric_inc!(self.metrics, beefy_equivocation_votes); self.report_equivocation(proof)?; }, - VoteImportResult::Invalid => metric_inc!(self.base.metrics, beefy_invalid_votes), - VoteImportResult::Stale => metric_inc!(self.base.metrics, beefy_stale_votes), + VoteImportResult::Invalid => metric_inc!(self.metrics, beefy_invalid_votes), + VoteImportResult::Stale => metric_inc!(self.metrics, beefy_stale_votes), }; Ok(None) } @@ -815,15 +614,14 @@ where // Set new best BEEFY block number. self.persisted_state.set_best_beefy(block_num); - crate::aux_schema::write_voter_state(&*self.base.backend, &self.persisted_state) + crate::aux_schema::write_voter_state(&*self.backend, &self.persisted_state) .map_err(|e| Error::Backend(e.to_string()))?; - metric_set!(self.base.metrics, beefy_best_block, block_num); + metric_set!(self.metrics, beefy_best_block, block_num); self.comms.on_demand_justifications.cancel_requests_older_than(block_num); if let Err(e) = self - .base .backend .blockchain() .expect_block_hash_from_id(&BlockId::Number(block_num)) @@ -833,8 +631,7 @@ where .notify(|| Ok::<_, ()>(hash)) .expect("forwards closure result; the closure always returns Ok; qed."); - self.base - .backend + self.backend .append_justification(hash, (BEEFY_ENGINE_ID, finality_proof.encode())) }) { debug!( @@ -871,13 +668,13 @@ where for (num, justification) in justifs_to_process.into_iter() { debug!(target: LOG_TARGET, "🥩 Handle buffered justification for: {:?}.", num); - metric_inc!(self.base.metrics, beefy_imported_justifications); + metric_inc!(self.metrics, beefy_imported_justifications); if let Err(err) = self.finalize(justification) { error!(target: LOG_TARGET, "🥩 Error finalizing block: {}", err); } } metric_set!( - self.base.metrics, + self.metrics, beefy_buffered_justifications, self.pending_justifications.len() ); @@ -889,7 +686,7 @@ where fn try_to_vote(&mut self) -> Result<(), Error> { // Vote if there's now a new vote target. if let Some(target) = self.voting_oracle().voting_target() { - metric_set!(self.base.metrics, beefy_should_vote_on, target); + metric_set!(self.metrics, beefy_should_vote_on, target); if target > self.persisted_state.best_voted { self.do_vote(target)?; } @@ -909,7 +706,6 @@ where self.persisted_state.voting_oracle.best_grandpa_block_header.clone() } else { let hash = self - .base .backend .blockchain() .expect_block_hash_from_id(&BlockId::Number(target_number)) @@ -921,7 +717,7 @@ where Error::Backend(err_msg) })?; - self.base.backend.blockchain().expect_header(hash).map_err(|err| { + self.backend.blockchain().expect_header(hash).map_err(|err| { let err_msg = format!( "Couldn't get header for block #{:?} ({:?}) (error: {:?}), skipping vote..", target_number, hash, err @@ -941,7 +737,7 @@ where let rounds = self.persisted_state.voting_oracle.active_rounds_mut()?; let (validators, validator_set_id) = (rounds.validators(), rounds.validator_set_id()); - let authority_id = if let Some(id) = self.base.key_store.authority_id(validators) { + let authority_id = if let Some(id) = self.key_store.authority_id(validators) { debug!(target: LOG_TARGET, "🥩 Local authority id: {:?}", id); id } else { @@ -955,7 +751,7 @@ where let commitment = Commitment { payload, block_number: target_number, validator_set_id }; let encoded_commitment = commitment.encode(); - let signature = match self.base.key_store.sign(&authority_id, &encoded_commitment) { + let signature = match self.key_store.sign(&authority_id, &encoded_commitment) { Ok(sig) => sig, Err(err) => { warn!(target: LOG_TARGET, "🥩 Error signing commitment: {:?}", err); @@ -980,7 +776,7 @@ where .gossip_engine .gossip_message(proofs_topic::(), encoded_proof, true); } else { - metric_inc!(self.base.metrics, beefy_votes_sent); + metric_inc!(self.metrics, beefy_votes_sent); debug!(target: LOG_TARGET, "🥩 Sent vote message: {:?}", vote); let encoded_vote = GossipMessage::::Vote(vote).encode(); self.comms.gossip_engine.gossip_message(votes_topic::(), encoded_vote, false); @@ -988,8 +784,8 @@ where // Persist state after vote to avoid double voting in case of voter restarts. self.persisted_state.best_voted = target_number; - metric_set!(self.base.metrics, beefy_best_voted, target_number); - crate::aux_schema::write_voter_state(&*self.base.backend, &self.persisted_state) + metric_set!(self.metrics, beefy_best_voted, target_number); + crate::aux_schema::write_voter_state(&*self.backend, &self.persisted_state) .map_err(|e| Error::Backend(e.to_string())) } @@ -1163,16 +959,15 @@ where if !check_equivocation_proof::<_, _, BeefySignatureHasher>(&proof) { debug!(target: LOG_TARGET, "🥩 Skip report for bad equivocation {:?}", proof); return Ok(()) - } else if let Some(local_id) = self.base.key_store.authority_id(validators) { + } else if let Some(local_id) = self.key_store.authority_id(validators) { if offender_id == local_id { - debug!(target: LOG_TARGET, "🥩 Skip equivocation report for own equivocation"); + warn!(target: LOG_TARGET, "🥩 Skip equivocation report for own equivocation"); return Ok(()) } } let number = *proof.round_number(); let hash = self - .base .backend .blockchain() .expect_block_hash_from_id(&BlockId::Number(number)) @@ -1183,7 +978,7 @@ where ); Error::Backend(err_msg) })?; - let runtime_api = self.base.runtime.runtime_api(); + let runtime_api = self.runtime.runtime_api(); // generate key ownership proof at that block let key_owner_proof = match runtime_api .generate_key_ownership_proof(hash, validator_set_id, offender_id) @@ -1200,7 +995,7 @@ where }; // submit equivocation report at **best** block - let best_block_hash = self.base.backend.blockchain().info().best_hash; + let best_block_hash = self.backend.blockchain().info().best_hash; runtime_api .submit_report_equivocation_unsigned_extrinsic(best_block_hash, proof, key_owner_proof) .map_err(Error::RuntimeApi)?; @@ -1209,21 +1004,6 @@ where } } -/// Scan the `header` digest log for a BEEFY validator set change. Return either the new -/// validator set or `None` in case no validator set change has been signaled. -pub(crate) fn find_authorities_change(header: &B::Header) -> Option> -where - B: Block, -{ - let id = OpaqueDigestItemId::Consensus(&BEEFY_ENGINE_ID); - - let filter = |log: ConsensusLog| match log { - ConsensusLog::AuthoritiesChange(validator_set) => Some(validator_set), - _ => None, - }; - header.digest().convert_first(|l| l.try_to(id).and_then(filter)) -} - /// Calculate next block number to vote on. /// /// Return `None` if there is no votable target yet. @@ -1234,7 +1014,7 @@ where // if the mandatory block (session_start) does not have a beefy justification yet, // we vote on it let target = if best_beefy < session_start { - debug!(target: LOG_TARGET, "🥩 vote target - mandatory block: #{:?}", session_start,); + debug!(target: LOG_TARGET, "🥩 vote target - mandatory block: #{:?}", session_start); session_start } else { let diff = best_grandpa.saturating_sub(best_beefy) + 1u32.into(); @@ -1260,11 +1040,42 @@ where } } +/// Verify `active` validator set for `block` against the key store +/// +/// We want to make sure that we have _at least one_ key in our keystore that +/// is part of the validator set, that's because if there are no local keys +/// then we can't perform our job as a validator. +/// +/// Note that for a non-authority node there will be no keystore, and we will +/// return an error and don't check. The error can usually be ignored. +fn verify_validator_set( + block: &NumberFor, + active: &ValidatorSet, + key_store: &BeefyKeystore, +) -> Result<(), Error> { + let active: BTreeSet<&AuthorityId> = active.validators().iter().collect(); + + let public_keys = key_store.public_keys()?; + let store: BTreeSet<&AuthorityId> = public_keys.iter().collect(); + + if store.intersection(&active).count() == 0 { + let msg = "no authority public key found in store".to_string(); + debug!(target: LOG_TARGET, "🥩 for block {:?} {}", block, msg); + Err(Error::Keystore(msg)) + } else { + Ok(()) + } +} + #[cfg(test)] pub(crate) mod tests { use super::*; use crate::{ - communication::notification::{BeefyBestBlockStream, BeefyVersionedFinalityProofStream}, + communication::{ + gossip::GossipValidator, + notification::{BeefyBestBlockStream, BeefyVersionedFinalityProofStream}, + request_response::outgoing_requests_engine::OnDemandJustificationsEngine, + }, tests::{ create_beefy_keystore, get_beefy_streams, make_beefy_ids, BeefyPeer, BeefyTestNet, TestApi, @@ -1274,6 +1085,7 @@ pub(crate) mod tests { use futures::{future::poll_fn, task::Poll}; use parking_lot::Mutex; use sc_client_api::{Backend as BackendT, HeaderBackend}; + use sc_network_gossip::GossipEngine; use sc_network_sync::SyncingService; use sc_network_test::TestNetFactory; use sp_blockchain::Backend as BlockchainBackendT; @@ -1282,7 +1094,7 @@ pub(crate) mod tests { known_payloads::MMR_ROOT_ID, mmr::MmrRootProvider, test_utils::{generate_equivocation_proof, Keyring}, - Payload, SignedCommitment, + ConsensusLog, Payload, SignedCommitment, }; use sp_runtime::traits::{Header as HeaderT, One}; use substrate_test_runtime_client::{ @@ -1291,10 +1103,6 @@ pub(crate) mod tests { }; impl PersistedState { - pub fn voting_oracle(&self) -> &VoterOracle { - &self.voting_oracle - } - pub fn active_round(&self) -> Result<&Rounds, Error> { self.voting_oracle.active_rounds() } @@ -1390,13 +1198,10 @@ pub(crate) mod tests { on_demand_justifications, }; BeefyWorker { - base: BeefyWorkerBase { - backend, - runtime: api, - key_store: Some(keystore).into(), - metrics, - _phantom: Default::default(), - }, + backend, + runtime: api, + key_store: Some(keystore).into(), + metrics, payload_provider, sync: Arc::new(sync), links, @@ -1674,19 +1479,22 @@ pub(crate) mod tests { let mut worker = create_beefy_worker(net.peer(0), &keys[0], 1, validator_set.clone()); // keystore doesn't contain other keys than validators' - assert_eq!(worker.base.verify_validator_set(&1, &validator_set), Ok(())); + assert_eq!(verify_validator_set::(&1, &validator_set, &worker.key_store), Ok(())); // unknown `Bob` key let keys = &[Keyring::Bob]; let validator_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap(); let err_msg = "no authority public key found in store".to_string(); let expected = Err(Error::Keystore(err_msg)); - assert_eq!(worker.base.verify_validator_set(&1, &validator_set), expected); + assert_eq!(verify_validator_set::(&1, &validator_set, &worker.key_store), expected); // worker has no keystore - worker.base.key_store = None.into(); + worker.key_store = None.into(); let expected_err = Err(Error::Keystore("no Keystore".into())); - assert_eq!(worker.base.verify_validator_set(&1, &validator_set), expected_err); + assert_eq!( + verify_validator_set::(&1, &validator_set, &worker.key_store), + expected_err + ); } #[tokio::test] @@ -1838,7 +1646,7 @@ pub(crate) mod tests { let mut net = BeefyTestNet::new(1); let mut worker = create_beefy_worker(net.peer(0), &keys[0], 1, validator_set.clone()); - worker.base.runtime = api_alice.clone(); + worker.runtime = api_alice.clone(); // let there be a block with num = 1: let _ = net.peer(0).push_blocks(1, false); diff --git a/substrate/client/consensus/common/Cargo.toml b/substrate/client/consensus/common/Cargo.toml index 7f36dfc09ef8451927eb32ebd651258647abee3b..f691e84717d186046e53588eeb0fec9c816eb527 100644 --- a/substrate/client/consensus/common/Cargo.toml +++ b/substrate/client/consensus/common/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.io" repository.workspace = true -description = "Collection of common consensus specific imlementations for Substrate (client)" +description = "Collection of common consensus specific implementations for Substrate (client)" readme = "README.md" [lints] diff --git a/substrate/client/consensus/common/src/import_queue.rs b/substrate/client/consensus/common/src/import_queue.rs index 39d5bf8ed35d17468a8d8819a423f22a0d80667c..062e244a912ef824b3a85574c5195889e2556a89 100644 --- a/substrate/client/consensus/common/src/import_queue.rs +++ b/substrate/client/consensus/common/src/import_queue.rs @@ -133,7 +133,7 @@ pub trait ImportQueue: Send { /// Start asynchronous runner for import queue. /// /// Takes an object implementing [`Link`] which allows the import queue to - /// influece the synchronization process. + /// influence the synchronization process. async fn run(self, link: Box>); } diff --git a/substrate/client/consensus/common/src/import_queue/basic_queue.rs b/substrate/client/consensus/common/src/import_queue/basic_queue.rs index 1cc7ec26fd1930100ebe45f9c1e8ba643bd5be37..125d4f104c1918bf5a54e5a13f67b4603a3ae302 100644 --- a/substrate/client/consensus/common/src/import_queue/basic_queue.rs +++ b/substrate/client/consensus/common/src/import_queue/basic_queue.rs @@ -187,7 +187,7 @@ impl ImportQueue for BasicQueue { /// Start asynchronous runner for import queue. /// /// Takes an object implementing [`Link`] which allows the import queue to - /// influece the synchronization process. + /// influence the synchronization process. async fn run(mut self, mut link: Box>) { loop { if let Err(_) = self.result_port.next_action(&mut *link).await { @@ -198,7 +198,7 @@ impl ImportQueue for BasicQueue { } } -/// Messages destinated to the background worker. +/// Messages designated to the background worker. mod worker_messages { use super::*; diff --git a/substrate/client/consensus/epochs/src/lib.rs b/substrate/client/consensus/epochs/src/lib.rs index 29bb18e147c2b8aef2a172130e753bf0d2f7193d..bbc143b7bd302aec2f45f868c48ab9f7dd7ff58f 100644 --- a/substrate/client/consensus/epochs/src/lib.rs +++ b/substrate/client/consensus/epochs/src/lib.rs @@ -326,7 +326,7 @@ impl AsRef for IncrementedEpoch { /// /// The first epoch, epoch_0, is special cased by saying that it starts at /// slot number of the first block in the chain. When bootstrapping a chain, -/// there can be multiple competing block #1s, so we have to ensure that the overlayed +/// there can be multiple competing block #1s, so we have to ensure that the overlaid /// DAG doesn't get confused. /// /// The first block of every epoch should be producing a descriptor for the next @@ -655,7 +655,7 @@ where /// Revert to a specified block given its `hash` and `number`. /// This removes all the epoch changes information that were announced by - /// all the given block descendents. + /// all the given block descendants. pub fn revert>( &mut self, descendent_of_builder: D, diff --git a/substrate/client/consensus/grandpa/src/communication/mod.rs b/substrate/client/consensus/grandpa/src/communication/mod.rs index 5c7e1276297a988b3109ae650e121331f57f930d..6e87d6bf9a28924811efab920b98da7c51480481 100644 --- a/substrate/client/consensus/grandpa/src/communication/mod.rs +++ b/substrate/client/consensus/grandpa/src/communication/mod.rs @@ -222,13 +222,13 @@ pub(crate) struct NetworkBridge, S: Syncing> { neighbor_sender: periodic::NeighborPacketSender, /// `NeighborPacketWorker` processing packets sent through the `NeighborPacketSender`. - // `NetworkBridge` is required to be cloneable, thus one needs to be able to clone its + // `NetworkBridge` is required to be clonable, thus one needs to be able to clone its // children, thus one has to wrap `neighbor_packet_worker` with an `Arc` `Mutex`. neighbor_packet_worker: Arc>>, /// Receiver side of the peer report stream populated by the gossip validator, forwarded to the /// gossip engine. - // `NetworkBridge` is required to be cloneable, thus one needs to be able to clone its + // `NetworkBridge` is required to be clonable, thus one needs to be able to clone its // children, thus one has to wrap gossip_validator_report_stream with an `Arc` `Mutex`. Given // that it is just an `UnboundedReceiver`, one could also switch to a // multi-producer-*multi*-consumer channel implementation. @@ -766,7 +766,7 @@ impl Sink> for OutgoingMessages { ) .ok_or_else(|| { Error::Signing(format!( - "Failed to sign GRANDPA vote for round {} targetting {:?}", + "Failed to sign GRANDPA vote for round {} targeting {:?}", self.round, target_hash )) })?; diff --git a/substrate/client/consensus/grandpa/src/communication/periodic.rs b/substrate/client/consensus/grandpa/src/communication/periodic.rs index daa752920287971ee986ae5c65fb995b52187495..9d0e76b7c80bd2911d49d14ddd3923f16dd6b211 100644 --- a/substrate/client/consensus/grandpa/src/communication/periodic.rs +++ b/substrate/client/consensus/grandpa/src/communication/periodic.rs @@ -106,7 +106,7 @@ impl Stream for NeighborPacketWorker { // Make sure the underlying task is scheduled for wake-up. // - // Note: In case poll_unpin is called after the resetted delay fires again, this + // Note: In case poll_unpin is called after the reset delay fires again, this // will drop one tick. Deemed as very unlikely and also not critical. while this.delay.poll_unpin(cx).is_ready() {} diff --git a/substrate/client/consensus/grandpa/src/tests.rs b/substrate/client/consensus/grandpa/src/tests.rs index 7e42c2d45c733b8cbc32a599680202399fadaed6..14708cc89e890bd3fe7963589010896f78bb6ab1 100644 --- a/substrate/client/consensus/grandpa/src/tests.rs +++ b/substrate/client/consensus/grandpa/src/tests.rs @@ -1805,7 +1805,7 @@ async fn grandpa_environment_checks_if_best_block_is_descendent_of_finality_targ ); // best block is higher than finality target and it's on the same fork, - // the best block passed to the voting rule should not be overriden + // the best block passed to the voting rule should not be overridden select_chain.set_best_chain(client.expect_header(hashof10_a).unwrap()); select_chain.set_finality_target(client.expect_header(hashof5_a).unwrap().hash()); voting_rule.set_expected_best_block(hashof10_a); @@ -1940,7 +1940,7 @@ async fn justification_with_equivocation() { precommits.push(precommit); } - // we create an equivocation for the 67th validator targetting blocks #1 and #2. + // we create an equivocation for the 67th validator targeting blocks #1 and #2. // this should be accounted as "voting for all blocks" and therefore block #3 will // have 67/100 votes, reaching finality threshold. { diff --git a/substrate/client/consensus/grandpa/src/until_imported.rs b/substrate/client/consensus/grandpa/src/until_imported.rs index 14f32ecc883662d9d690b75611657cef0aa797e6..f3874086b58143d185f87ad9c6e0eb009acf16e9 100644 --- a/substrate/client/consensus/grandpa/src/until_imported.rs +++ b/substrate/client/consensus/grandpa/src/until_imported.rs @@ -1002,7 +1002,7 @@ mod tests { } #[test] - fn block_global_message_wait_completed_return_none_on_block_number_missmatch() { + fn block_global_message_wait_completed_return_none_on_block_number_mismatch() { let msg_inner = test_catch_up(); let waiting_block_1 = diff --git a/substrate/client/consensus/grandpa/src/voting_rule.rs b/substrate/client/consensus/grandpa/src/voting_rule.rs index e09780739c7353418a81488a25060eba0231ebfc..c37596d20f68625b21f443e16db3c03fc08357da 100644 --- a/substrate/client/consensus/grandpa/src/voting_rule.rs +++ b/substrate/client/consensus/grandpa/src/voting_rule.rs @@ -196,7 +196,7 @@ where target_header = backend .header(target_hash) .ok()? - .expect("Header known to exist due to the existence of one of its descendents; qed"); + .expect("Header known to exist due to the existence of one of its descendants; qed"); } } diff --git a/substrate/client/consensus/grandpa/src/warp_proof.rs b/substrate/client/consensus/grandpa/src/warp_proof.rs index 29111712ec382ec6758fddcfa670d4edd2a9a7eb..7169a424c14a7726efa9531b80ef13f829bc9529 100644 --- a/substrate/client/consensus/grandpa/src/warp_proof.rs +++ b/substrate/client/consensus/grandpa/src/warp_proof.rs @@ -249,7 +249,7 @@ impl> NetworkProvider: BlockNumberOps, { - /// Create a new istance for a given backend and authority set. + /// Create a new instance for a given backend and authority set. pub fn new( backend: Arc, authority_set: SharedAuthoritySet>, diff --git a/substrate/client/consensus/manual-seal/src/consensus/babe.rs b/substrate/client/consensus/manual-seal/src/consensus/babe.rs index 26fa81459808c39635a1970b0fef7b36774803a9..bc56ce0227142fee2c001c39ce8d31cd9e6fb9b5 100644 --- a/substrate/client/consensus/manual-seal/src/consensus/babe.rs +++ b/substrate/client/consensus/manual-seal/src/consensus/babe.rs @@ -82,7 +82,7 @@ pub struct BabeVerifier { } impl BabeVerifier { - /// create a nrew verifier + /// create a new verifier pub fn new(epoch_changes: SharedEpochChanges, client: Arc) -> BabeVerifier { BabeVerifier { epoch_changes, client } } diff --git a/substrate/client/consensus/manual-seal/src/lib.rs b/substrate/client/consensus/manual-seal/src/lib.rs index e3608f6716c2631e5079c08e59ef991903b12d73..c3d360f071974ab38c6f1d112c23d6705f937bd3 100644 --- a/substrate/client/consensus/manual-seal/src/lib.rs +++ b/substrate/client/consensus/manual-seal/src/lib.rs @@ -429,7 +429,9 @@ mod tests { sender, } }); - let future = run_manual_seal(ManualSealParams { + + // spawn the background authorship task + tokio::spawn(run_manual_seal(ManualSealParams { block_import: client.clone(), env, client: client.clone(), @@ -438,12 +440,8 @@ mod tests { select_chain, create_inherent_data_providers: |_, _| async { Ok(()) }, consensus_data_provider: None, - }); - std::thread::spawn(|| { - let rt = tokio::runtime::Runtime::new().unwrap(); - // spawn the background authorship task - rt.block_on(future); - }); + })); + // submit a transaction to pool. let result = pool.submit_one(genesis_hash, SOURCE, uxt(Alice, 0)).await; // assert that it was successfully imported @@ -469,7 +467,10 @@ mod tests { assert_eq!(client.header(created_block.hash).unwrap().unwrap().number, 1) } - #[tokio::test] + // TODO: enable once the flakiness is fixed + // See https://github.com/paritytech/polkadot-sdk/issues/3603 + //#[tokio::test] + #[allow(unused)] async fn instant_seal_delayed_finalize() { let builder = TestClientBuilder::new(); let (client, select_chain) = builder.build_with_longest_chain(); @@ -507,7 +508,8 @@ mod tests { } }); - let future_instant_seal = run_manual_seal(ManualSealParams { + // spawn the background authorship task + tokio::spawn(run_manual_seal(ManualSealParams { block_import: client.clone(), commands_stream, env, @@ -516,24 +518,16 @@ mod tests { select_chain, create_inherent_data_providers: |_, _| async { Ok(()) }, consensus_data_provider: None, - }); - std::thread::spawn(|| { - let rt = tokio::runtime::Runtime::new().unwrap(); - // spawn the background authorship task - rt.block_on(future_instant_seal); - }); + })); let delay_sec = 5; - let future_delayed_finalize = run_delayed_finalize(DelayedFinalizeParams { + + // spawn the background finality task + tokio::spawn(run_delayed_finalize(DelayedFinalizeParams { client: client.clone(), delay_sec, spawn_handle: spawner, - }); - std::thread::spawn(|| { - let rt = tokio::runtime::Runtime::new().unwrap(); - // spawn the background authorship task - rt.block_on(future_delayed_finalize); - }); + })); let mut finality_stream = client.finality_notification_stream(); // submit a transaction to pool. @@ -589,7 +583,9 @@ mod tests { // this test checks that blocks are created as soon as an engine command is sent over the // stream. let (mut sink, commands_stream) = futures::channel::mpsc::channel(1024); - let future = run_manual_seal(ManualSealParams { + + // spawn the background authorship task + tokio::spawn(run_manual_seal(ManualSealParams { block_import: client.clone(), env, client: client.clone(), @@ -598,12 +594,8 @@ mod tests { select_chain, consensus_data_provider: None, create_inherent_data_providers: |_, _| async { Ok(()) }, - }); - std::thread::spawn(|| { - let rt = tokio::runtime::Runtime::new().unwrap(); - // spawn the background authorship task - rt.block_on(future); - }); + })); + // submit a transaction to pool. let result = pool.submit_one(genesis_hash, SOURCE, uxt(Alice, 0)).await; // assert that it was successfully imported @@ -675,7 +667,9 @@ mod tests { // this test checks that blocks are created as soon as an engine command is sent over the // stream. let (mut sink, commands_stream) = futures::channel::mpsc::channel(1024); - let future = run_manual_seal(ManualSealParams { + + // spawn the background authorship task + tokio::spawn(run_manual_seal(ManualSealParams { block_import: client.clone(), env, client: client.clone(), @@ -684,12 +678,8 @@ mod tests { select_chain, consensus_data_provider: None, create_inherent_data_providers: |_, _| async { Ok(()) }, - }); - std::thread::spawn(|| { - let rt = tokio::runtime::Runtime::new().unwrap(); - // spawn the background authorship task - rt.block_on(future); - }); + })); + // submit a transaction to pool. let result = pool.submit_one(genesis_hash, SOURCE, uxt(Alice, 0)).await; // assert that it was successfully imported @@ -781,7 +771,9 @@ mod tests { let env = ProposerFactory::new(spawner.clone(), client.clone(), pool.clone(), None, None); let (mut sink, commands_stream) = futures::channel::mpsc::channel(1024); - let future = run_manual_seal(ManualSealParams { + + // spawn the background authorship task + tokio::spawn(run_manual_seal(ManualSealParams { block_import: client.clone(), env, client: client.clone(), @@ -791,11 +783,8 @@ mod tests { // use a provider that pushes some post digest data consensus_data_provider: Some(Box::new(TestDigestProvider { _client: client.clone() })), create_inherent_data_providers: |_, _| async { Ok(()) }, - }); - std::thread::spawn(|| { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(future); - }); + })); + let (tx, rx) = futures::channel::oneshot::channel(); sink.send(EngineCommand::SealNewBlock { parent_hash: None, diff --git a/substrate/client/consensus/slots/src/lib.rs b/substrate/client/consensus/slots/src/lib.rs index 12636aae7a4412228ab1da5755c9f82669c469e5..d9d792005312503f48bb2d628235794952104ccd 100644 --- a/substrate/client/consensus/slots/src/lib.rs +++ b/substrate/client/consensus/slots/src/lib.rs @@ -555,7 +555,7 @@ impl SlotProportion { Self(inner.clamp(0.0, 1.0)) } - /// Returns the inner that is guaranted to be in the range `[0,1]`. + /// Returns the inner that is guaranteed to be in the range `[0,1]`. pub fn get(&self) -> f32 { self.0 } @@ -648,7 +648,7 @@ pub fn proposing_remaining_duration( } /// Calculate a slot duration lenience based on the number of missed slots from current -/// to parent. If the number of skipped slots is greated than 0 this method will apply +/// to parent. If the number of skipped slots is greater than 0 this method will apply /// an exponential backoff of at most `2^7 * slot_duration`, if no slots were skipped /// this method will return `None.` pub fn slot_lenience_exponential( @@ -680,7 +680,7 @@ pub fn slot_lenience_exponential( } /// Calculate a slot duration lenience based on the number of missed slots from current -/// to parent. If the number of skipped slots is greated than 0 this method will apply +/// to parent. If the number of skipped slots is greater than 0 this method will apply /// a linear backoff of at most `20 * slot_duration`, if no slots were skipped /// this method will return `None.` pub fn slot_lenience_linear( diff --git a/substrate/client/db/src/lib.rs b/substrate/client/db/src/lib.rs index 870b4150b9df71ff63e5b5be7467b5369caa97c0..0faa90dfc4f925db8776c6914fcf864813b790a5 100644 --- a/substrate/client/db/src/lib.rs +++ b/substrate/client/db/src/lib.rs @@ -2531,7 +2531,7 @@ impl sc_client_api::backend::Backend for Backend { self.storage.state_db.pin(&hash, number.saturated_into::(), hint).map_err( |_| { sp_blockchain::Error::UnknownBlock(format!( - "State already discarded for `{:?}`", + "Unable to pin: state already discarded for `{:?}`", hash )) }, diff --git a/substrate/client/db/src/pinned_blocks_cache.rs b/substrate/client/db/src/pinned_blocks_cache.rs index 46c9287fb19ac1361153dbb65f4f0b353170042f..ac4aad07765cfbaa4d9c66efa4b44730b0b0a04c 100644 --- a/substrate/client/db/src/pinned_blocks_cache.rs +++ b/substrate/client/db/src/pinned_blocks_cache.rs @@ -20,7 +20,7 @@ use schnellru::{Limiter, LruMap}; use sp_runtime::{traits::Block as BlockT, Justifications}; const LOG_TARGET: &str = "db::pin"; -const PINNING_CACHE_SIZE: usize = 1024; +const PINNING_CACHE_SIZE: usize = 2048; /// Entry for pinned blocks cache. struct PinnedBlockCacheEntry { diff --git a/substrate/client/db/src/upgrade.rs b/substrate/client/db/src/upgrade.rs index f1e503867dfc3e4a9ad3ddd5135f5d426e9c221c..475220d9991931e5ed03f1be9ba812bb5a6b1ad4 100644 --- a/substrate/client/db/src/upgrade.rs +++ b/substrate/client/db/src/upgrade.rs @@ -79,7 +79,7 @@ impl fmt::Display for UpgradeError { write!(f, "Database version comes from future version of the client: {}", version) }, UpgradeError::DecodingJustificationBlock => { - write!(f, "Decodoning justification block failed") + write!(f, "Decoding justification block failed") }, UpgradeError::Io(err) => write!(f, "Io error: {}", err), } diff --git a/substrate/client/db/src/utils.rs b/substrate/client/db/src/utils.rs index abf9c4629cee47e31d18a18c827212b48fe31511..b532e0d4666273648a7cef073a11ab21eb3a2554 100644 --- a/substrate/client/db/src/utils.rs +++ b/substrate/client/db/src/utils.rs @@ -338,7 +338,7 @@ fn open_kvdb_rocksdb( db_config.memory_budget = memory_budget; let db = kvdb_rocksdb::Database::open(&db_config, path)?; - // write database version only after the database is succesfully opened + // write database version only after the database is successfully opened crate::upgrade::update_version(path)?; Ok(sp_database::as_database(db)) } diff --git a/substrate/client/executor/Cargo.toml b/substrate/client/executor/Cargo.toml index 4e36de3c4f47c9f4f98f28d694ec3f23cbd81add..7fad7e3a2a0ebaf3a8ecde19ff9517e47ff28e75 100644 --- a/substrate/client/executor/Cargo.toml +++ b/substrate/client/executor/Cargo.toml @@ -23,6 +23,7 @@ tracing = "0.1.29" codec = { package = "parity-scale-codec", version = "3.6.1" } sc-executor-common = { path = "common" } +sc-executor-polkavm = { path = "polkavm" } sc-executor-wasmtime = { path = "wasmtime" } sp-api = { path = "../../primitives/api" } sp-core = { path = "../../primitives/core" } diff --git a/substrate/client/executor/common/Cargo.toml b/substrate/client/executor/common/Cargo.toml index bfd1fa6b74014bfbac893bd4e503e70bd76d9cf0..8ff34c3709a5e486fb9036a638788a532c6f296c 100644 --- a/substrate/client/executor/common/Cargo.toml +++ b/substrate/client/executor/common/Cargo.toml @@ -22,6 +22,7 @@ wasm-instrument = "0.4" sc-allocator = { path = "../../allocator" } sp-maybe-compressed-blob = { path = "../../../primitives/maybe-compressed-blob" } sp-wasm-interface = { path = "../../../primitives/wasm-interface" } +polkavm = { workspace = true } [features] default = [] diff --git a/substrate/client/executor/common/src/error.rs b/substrate/client/executor/common/src/error.rs index 2a0dc364b4103ebc53ee767300bf25cc7211fe8b..9d489eaae4200035b6e0ffc0b1bafb045b2ce2b7 100644 --- a/substrate/client/executor/common/src/error.rs +++ b/substrate/client/executor/common/src/error.rs @@ -145,11 +145,29 @@ pub enum WasmError { #[error("{0}")] Instantiation(String), - /// Other error happenend. + /// Other error happened. #[error("Other error happened while constructing the runtime: {0}")] Other(String), } +impl From for WasmError { + fn from(error: polkavm::ProgramParseError) -> Self { + WasmError::Other(error.to_string()) + } +} + +impl From for WasmError { + fn from(error: polkavm::Error) -> Self { + WasmError::Other(error.to_string()) + } +} + +impl From for Error { + fn from(error: polkavm::Error) -> Self { + Error::Other(error.to_string()) + } +} + /// An error message with an attached backtrace. #[derive(Debug)] pub struct MessageWithBacktrace { diff --git a/substrate/client/executor/common/src/lib.rs b/substrate/client/executor/common/src/lib.rs index 751801fb30da18e48e05daedbde644ad8699ce10..ff9fab2c8889fbbc9040740cd5f5a49f158d618c 100644 --- a/substrate/client/executor/common/src/lib.rs +++ b/substrate/client/executor/common/src/lib.rs @@ -25,3 +25,7 @@ pub mod error; pub mod runtime_blob; pub mod util; pub mod wasm_runtime; + +pub(crate) fn is_polkavm_enabled() -> bool { + std::env::var_os("SUBSTRATE_ENABLE_POLKAVM").map_or(false, |value| value == "1") +} diff --git a/substrate/client/executor/common/src/runtime_blob/runtime_blob.rs b/substrate/client/executor/common/src/runtime_blob/runtime_blob.rs index becf9e219b0b7a60f97bb08b4044897b143bd006..d689083b2f85cd7f3fc16f220c1b45a474dd87f5 100644 --- a/substrate/client/executor/common/src/runtime_blob/runtime_blob.rs +++ b/substrate/client/executor/common/src/runtime_blob/runtime_blob.rs @@ -17,23 +17,23 @@ // along with this program. If not, see . use crate::{error::WasmError, wasm_runtime::HeapAllocStrategy}; -use wasm_instrument::{ - export_mutable_globals, - parity_wasm::elements::{ - deserialize_buffer, serialize, ExportEntry, External, Internal, MemorySection, MemoryType, - Module, Section, - }, +use wasm_instrument::parity_wasm::elements::{ + deserialize_buffer, serialize, ExportEntry, External, Internal, MemorySection, MemoryType, + Module, Section, }; -/// A bunch of information collected from a WebAssembly module. +/// A program blob containing a Substrate runtime. #[derive(Clone)] -pub struct RuntimeBlob { - raw_module: Module, +pub struct RuntimeBlob(BlobKind); + +#[derive(Clone)] +enum BlobKind { + WebAssembly(Module), + PolkaVM(polkavm::ProgramBlob<'static>), } impl RuntimeBlob { - /// Create `RuntimeBlob` from the given wasm code. Will attempt to decompress the code before - /// deserializing it. + /// Create `RuntimeBlob` from the given WASM or PolkaVM compressed program blob. /// /// See [`sp_maybe_compressed_blob`] for details about decompression. pub fn uncompress_if_needed(wasm_code: &[u8]) -> Result { @@ -43,31 +43,26 @@ impl RuntimeBlob { Self::new(&wasm_code) } - /// Create `RuntimeBlob` from the given wasm code. + /// Create `RuntimeBlob` from the given WASM or PolkaVM program blob. /// - /// Returns `Err` if the wasm code cannot be deserialized. - pub fn new(wasm_code: &[u8]) -> Result { - let raw_module: Module = deserialize_buffer(wasm_code) - .map_err(|e| WasmError::Other(format!("cannot deserialize module: {:?}", e)))?; - Ok(Self { raw_module }) - } - - /// The number of globals defined in locally in this module. - pub fn declared_globals_count(&self) -> u32 { - self.raw_module - .global_section() - .map(|gs| gs.entries().len() as u32) - .unwrap_or(0) - } - - /// The number of imports of globals. - pub fn imported_globals_count(&self) -> u32 { - self.raw_module.import_section().map(|is| is.globals() as u32).unwrap_or(0) - } + /// Returns `Err` if the blob cannot be deserialized. + /// + /// Will only accept a PolkaVM program if the `SUBSTRATE_ENABLE_POLKAVM` environment + /// variable is set to `1`. + pub fn new(raw_blob: &[u8]) -> Result { + if raw_blob.starts_with(b"PVM\0") { + if crate::is_polkavm_enabled() { + return Ok(Self(BlobKind::PolkaVM( + polkavm::ProgramBlob::parse(raw_blob)?.into_owned(), + ))); + } else { + return Err(WasmError::Other("expected a WASM runtime blob, found a PolkaVM runtime blob; set the 'SUBSTRATE_ENABLE_POLKAVM' environment variable to enable the experimental PolkaVM-based executor".to_string())); + } + } - /// Perform an instrumentation that makes sure that the mutable globals are exported. - pub fn expose_mutable_globals(&mut self) { - export_mutable_globals(&mut self.raw_module, "exported_internal_global"); + let raw_module: Module = deserialize_buffer(raw_blob) + .map_err(|e| WasmError::Other(format!("cannot deserialize module: {:?}", e)))?; + Ok(Self(BlobKind::WebAssembly(raw_module))) } /// Run a pass that instrument this module so as to introduce a deterministic stack height @@ -80,26 +75,16 @@ impl RuntimeBlob { /// /// The stack cost of a function is computed based on how much locals there are and the maximum /// depth of the wasm operand stack. + /// + /// Only valid for WASM programs; will return an error if the blob is a PolkaVM program. pub fn inject_stack_depth_metering(self, stack_depth_limit: u32) -> Result { let injected_module = - wasm_instrument::inject_stack_limiter(self.raw_module, stack_depth_limit).map_err( - |e| WasmError::Other(format!("cannot inject the stack limiter: {:?}", e)), - )?; - - Ok(Self { raw_module: injected_module }) - } + wasm_instrument::inject_stack_limiter(self.into_webassembly_blob()?, stack_depth_limit) + .map_err(|e| { + WasmError::Other(format!("cannot inject the stack limiter: {:?}", e)) + })?; - /// Perform an instrumentation that makes sure that a specific function `entry_point` is - /// exported - pub fn entry_point_exists(&self, entry_point: &str) -> bool { - self.raw_module - .export_section() - .map(|e| { - e.entries().iter().any(|e| { - matches!(e.internal(), Internal::Function(_)) && e.field() == entry_point - }) - }) - .unwrap_or_default() + Ok(Self(BlobKind::WebAssembly(injected_module))) } /// Converts a WASM memory import into a memory section and exports it. @@ -107,8 +92,11 @@ impl RuntimeBlob { /// Does nothing if there's no memory import. /// /// May return an error in case the WASM module is invalid. + /// + /// Only valid for WASM programs; will return an error if the blob is a PolkaVM program. pub fn convert_memory_import_into_export(&mut self) -> Result<(), WasmError> { - let import_section = match self.raw_module.import_section_mut() { + let raw_module = self.as_webassembly_blob_mut()?; + let import_section = match raw_module.import_section_mut() { Some(import_section) => import_section, None => return Ok(()), }; @@ -124,7 +112,7 @@ impl RuntimeBlob { let memory_name = entry.field().to_owned(); import_entries.remove(index); - self.raw_module + raw_module .insert_section(Section::Memory(MemorySection::with_entries(vec![memory_ty]))) .map_err(|error| { WasmError::Other(format!( @@ -133,14 +121,14 @@ impl RuntimeBlob { )) })?; - if self.raw_module.export_section_mut().is_none() { + if raw_module.export_section_mut().is_none() { // A module without an export section is somewhat unrealistic, but let's do this // just in case to cover all of our bases. - self.raw_module + raw_module .insert_section(Section::Export(Default::default())) .expect("an export section can be always inserted if it doesn't exist; qed"); } - self.raw_module + raw_module .export_section_mut() .expect("export section already existed or we just added it above, so it always exists; qed") .entries_mut() @@ -156,12 +144,14 @@ impl RuntimeBlob { /// /// Will return an error in case there is no memory section present, /// or if the memory section is empty. + /// + /// Only valid for WASM programs; will return an error if the blob is a PolkaVM program. pub fn setup_memory_according_to_heap_alloc_strategy( &mut self, heap_alloc_strategy: HeapAllocStrategy, ) -> Result<(), WasmError> { - let memory_section = self - .raw_module + let raw_module = self.as_webassembly_blob_mut()?; + let memory_section = raw_module .memory_section_mut() .ok_or_else(|| WasmError::Other("no memory section found".into()))?; @@ -187,8 +177,11 @@ impl RuntimeBlob { /// Scans the wasm blob for the first section with the name that matches the given. Returns the /// contents of the custom section if found or `None` otherwise. + /// + /// Only valid for WASM programs; will return an error if the blob is a PolkaVM program. pub fn custom_section_contents(&self, section_name: &str) -> Option<&[u8]> { - self.raw_module + self.as_webassembly_blob() + .ok()? .custom_sections() .find(|cs| cs.name() == section_name) .map(|cs| cs.payload()) @@ -196,11 +189,45 @@ impl RuntimeBlob { /// Consumes this runtime blob and serializes it. pub fn serialize(self) -> Vec { - serialize(self.raw_module).expect("serializing into a vec should succeed; qed") + match self.0 { + BlobKind::WebAssembly(raw_module) => + serialize(raw_module).expect("serializing into a vec should succeed; qed"), + BlobKind::PolkaVM(ref blob) => blob.as_bytes().to_vec(), + } + } + + fn as_webassembly_blob(&self) -> Result<&Module, WasmError> { + match self.0 { + BlobKind::WebAssembly(ref raw_module) => Ok(raw_module), + BlobKind::PolkaVM(..) => Err(WasmError::Other( + "expected a WebAssembly program; found a PolkaVM program blob".into(), + )), + } } - /// Destructure this structure into the underlying parity-wasm Module. - pub fn into_inner(self) -> Module { - self.raw_module + fn as_webassembly_blob_mut(&mut self) -> Result<&mut Module, WasmError> { + match self.0 { + BlobKind::WebAssembly(ref mut raw_module) => Ok(raw_module), + BlobKind::PolkaVM(..) => Err(WasmError::Other( + "expected a WebAssembly program; found a PolkaVM program blob".into(), + )), + } + } + + fn into_webassembly_blob(self) -> Result { + match self.0 { + BlobKind::WebAssembly(raw_module) => Ok(raw_module), + BlobKind::PolkaVM(..) => Err(WasmError::Other( + "expected a WebAssembly program; found a PolkaVM program blob".into(), + )), + } + } + + /// Gets a reference to the inner PolkaVM program blob, if this is a PolkaVM program. + pub fn as_polkavm_blob(&self) -> Option<&polkavm::ProgramBlob> { + match self.0 { + BlobKind::WebAssembly(..) => None, + BlobKind::PolkaVM(ref blob) => Some(blob), + } } } diff --git a/substrate/client/executor/common/src/wasm_runtime.rs b/substrate/client/executor/common/src/wasm_runtime.rs index d8e142b9d55905e3e31d2247f5a48e8e2b0f987a..e8f429a3dbb2a0451769d427a81bdf5eaef492ff 100644 --- a/substrate/client/executor/common/src/wasm_runtime.rs +++ b/substrate/client/executor/common/src/wasm_runtime.rs @@ -19,7 +19,6 @@ //! Definitions for a wasm runtime. use crate::error::Error; -use sp_wasm_interface::Value; pub use sc_allocator::AllocationStats; @@ -30,46 +29,6 @@ pub const DEFAULT_HEAP_ALLOC_STRATEGY: HeapAllocStrategy = /// Default heap allocation pages. pub const DEFAULT_HEAP_ALLOC_PAGES: u32 = 2048; -/// A method to be used to find the entrypoint when calling into the runtime -/// -/// Contains variants on how to resolve wasm function that will be invoked. -pub enum InvokeMethod<'a> { - /// Call function exported with this name. - /// - /// Located function should have (u32, u32) -> u64 signature. - Export(&'a str), - /// Call a function found in the exported table found under the given index. - /// - /// Located function should have (u32, u32) -> u64 signature. - Table(u32), - /// Call function by reference from table through a wrapper. - /// - /// Invoked function (`dispatcher_ref`) function - /// should have (u32, u32, u32) -> u64 signature. - /// - /// `func` will be passed to the invoked function as a first argument. - TableWithWrapper { - /// Wrapper for the call. - /// - /// Function pointer, index into runtime exported table. - dispatcher_ref: u32, - /// Extra argument for dispatch. - /// - /// Common usage would be to use it as an actual wasm function pointer - /// that should be invoked, but can be used as any extra argument on the - /// callee side. - /// - /// This is typically generated and invoked by the runtime itself. - func: u32, - }, -} - -impl<'a> From<&'a str> for InvokeMethod<'a> { - fn from(val: &'a str) -> InvokeMethod<'a> { - InvokeMethod::Export(val) - } -} - /// A trait that defines an abstract WASM runtime module. /// /// This can be implemented by an execution engine. @@ -87,7 +46,7 @@ pub trait WasmInstance: Send { /// Before execution, instance is reset. /// /// Returns the encoded result on success. - fn call(&mut self, method: InvokeMethod, data: &[u8]) -> Result, Error> { + fn call(&mut self, method: &str, data: &[u8]) -> Result, Error> { self.call_with_allocation_stats(method, data).0 } @@ -98,7 +57,7 @@ pub trait WasmInstance: Send { /// Returns the encoded result on success. fn call_with_allocation_stats( &mut self, - method: InvokeMethod, + method: &str, data: &[u8], ) -> (Result, Error>, Option); @@ -110,11 +69,6 @@ pub trait WasmInstance: Send { fn call_export(&mut self, method: &str, data: &[u8]) -> Result, Error> { self.call(method.into(), data) } - - /// Get the value from a global with the given `name`. - /// - /// This method is only suitable for getting immutable globals. - fn get_global_const(&mut self, name: &str) -> Result, Error>; } /// Defines the heap pages allocation strategy the wasm runtime should use. diff --git a/substrate/client/executor/polkavm/Cargo.toml b/substrate/client/executor/polkavm/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..9d0eb8ccf0ee072c86195068a589309edd132ba4 --- /dev/null +++ b/substrate/client/executor/polkavm/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "sc-executor-polkavm" +version = "0.29.0" +authors.workspace = true +edition.workspace = true +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +homepage = "https://substrate.io" +repository.workspace = true +description = "PolkaVM executor for Substrate" +readme = "README.md" + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +log = { workspace = true } +polkavm = { workspace = true } + +sc-executor-common = { path = "../common" } +sp-wasm-interface = { path = "../../../primitives/wasm-interface" } diff --git a/substrate/client/executor/polkavm/README.md b/substrate/client/executor/polkavm/README.md new file mode 100644 index 0000000000000000000000000000000000000000..64fc2fa0c28485566fc60329ad8cfc0057112bc7 --- /dev/null +++ b/substrate/client/executor/polkavm/README.md @@ -0,0 +1 @@ +License: GPL-3.0-or-later WITH Classpath-exception-2.0 diff --git a/substrate/client/executor/polkavm/src/lib.rs b/substrate/client/executor/polkavm/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..1bd72eb33d309997832de5fca9250ca651090ce7 --- /dev/null +++ b/substrate/client/executor/polkavm/src/lib.rs @@ -0,0 +1,261 @@ +// This file is part of Substrate. + +// Copyright (C) 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 . + +use polkavm::{Caller, Reg}; +use sc_executor_common::{ + error::{Error, WasmError}, + wasm_runtime::{AllocationStats, WasmInstance, WasmModule}, +}; +use sp_wasm_interface::{ + Function, FunctionContext, HostFunctions, Pointer, Value, ValueType, WordSize, +}; + +#[repr(transparent)] +pub struct InstancePre(polkavm::InstancePre<()>); + +#[repr(transparent)] +pub struct Instance(polkavm::Instance<()>); + +impl WasmModule for InstancePre { + fn new_instance(&self) -> Result, Error> { + Ok(Box::new(Instance(self.0.instantiate()?))) + } +} + +impl WasmInstance for Instance { + fn call_with_allocation_stats( + &mut self, + name: &str, + raw_data: &[u8], + ) -> (Result, Error>, Option) { + let Some(method_index) = self.0.module().lookup_export(name) else { + return ( + Err(format!("cannot call into the runtime: export not found: '{name}'").into()), + None, + ); + }; + + let Ok(raw_data_length) = u32::try_from(raw_data.len()) else { + return ( + Err(format!("cannot call runtime method '{name}': input payload is too big").into()), + None, + ); + }; + + // TODO: This will leak guest memory; find a better solution. + let mut state_args = polkavm::StateArgs::new(); + + // Make sure the memory is cleared... + state_args.reset_memory(true); + // ...and allocate space for the input payload. + state_args.sbrk(raw_data_length); + + match self.0.update_state(state_args) { + Ok(()) => {}, + Err(polkavm::ExecutionError::Trap(trap)) => { + return (Err(format!("call into the runtime method '{name}' failed: failed to prepare the guest's memory: {trap}").into()), None); + }, + Err(polkavm::ExecutionError::Error(error)) => { + return (Err(format!("call into the runtime method '{name}' failed: failed to prepare the guest's memory: {error}").into()), None); + }, + Err(polkavm::ExecutionError::OutOfGas) => unreachable!("gas metering is never enabled"), + } + + // Grab the address of where the guest's heap starts; that's where we've just allocated + // the memory for the input payload. + let data_pointer = self.0.module().memory_map().heap_base(); + + if let Err(error) = self.0.write_memory(data_pointer, raw_data) { + return (Err(format!("call into the runtime method '{name}': failed to write the input payload into guest memory: {error}").into()), None); + } + + let mut state = (); + let mut call_args = polkavm::CallArgs::new(&mut state, method_index); + call_args.args_untyped(&[data_pointer, raw_data_length]); + + match self.0.call(Default::default(), call_args) { + Ok(()) => {}, + Err(polkavm::ExecutionError::Trap(trap)) => { + return ( + Err(format!("call into the runtime method '{name}' failed: {trap}").into()), + None, + ); + }, + Err(polkavm::ExecutionError::Error(error)) => { + return ( + Err(format!("call into the runtime method '{name}' failed: {error}").into()), + None, + ); + }, + Err(polkavm::ExecutionError::OutOfGas) => unreachable!("gas metering is never enabled"), + } + + let result_pointer = self.0.get_reg(Reg::A0); + let result_length = self.0.get_reg(Reg::A1); + let output = match self.0.read_memory_into_vec(result_pointer, result_length) { + Ok(output) => output, + Err(error) => { + return (Err(format!("call into the runtime method '{name}' failed: failed to read the return payload: {error}").into()), None) + }, + }; + + (Ok(output), None) + } +} + +struct Context<'r, 'a>(&'r mut polkavm::Caller<'a, ()>); + +impl<'r, 'a> FunctionContext for Context<'r, 'a> { + fn read_memory_into( + &self, + address: Pointer, + dest: &mut [u8], + ) -> sp_wasm_interface::Result<()> { + self.0 + .read_memory_into_slice(u32::from(address), dest) + .map_err(|error| error.to_string()) + .map(|_| ()) + } + + fn write_memory(&mut self, address: Pointer, data: &[u8]) -> sp_wasm_interface::Result<()> { + self.0.write_memory(u32::from(address), data).map_err(|error| error.to_string()) + } + + fn allocate_memory(&mut self, size: WordSize) -> sp_wasm_interface::Result> { + let pointer = self.0.sbrk(0).expect("fetching the current heap pointer never fails"); + + // TODO: This will leak guest memory; find a better solution. + self.0.sbrk(size).ok_or_else(|| String::from("allocation failed"))?; + + Ok(Pointer::new(pointer)) + } + + fn deallocate_memory(&mut self, _ptr: Pointer) -> sp_wasm_interface::Result<()> { + // This is only used by the allocator host function, which is unused under PolkaVM. + unimplemented!("'deallocate_memory' is never used when running under PolkaVM"); + } + + fn register_panic_error_message(&mut self, _message: &str) { + unimplemented!("'register_panic_error_message' is never used when running under PolkaVM"); + } +} + +fn call_host_function( + caller: &mut Caller<()>, + function: &dyn Function, +) -> Result<(), polkavm::Trap> { + let mut args = [Value::I64(0); Reg::ARG_REGS.len()]; + let mut nth_reg = 0; + for (nth_arg, kind) in function.signature().args.iter().enumerate() { + match kind { + ValueType::I32 => { + args[nth_arg] = Value::I32(caller.get_reg(Reg::ARG_REGS[nth_reg]) as i32); + nth_reg += 1; + }, + ValueType::F32 => { + args[nth_arg] = Value::F32(caller.get_reg(Reg::ARG_REGS[nth_reg])); + nth_reg += 1; + }, + ValueType::I64 => { + let value_lo = caller.get_reg(Reg::ARG_REGS[nth_reg]); + nth_reg += 1; + + let value_hi = caller.get_reg(Reg::ARG_REGS[nth_reg]); + nth_reg += 1; + + args[nth_arg] = + Value::I64((u64::from(value_lo) | (u64::from(value_hi) << 32)) as i64); + }, + ValueType::F64 => { + let value_lo = caller.get_reg(Reg::ARG_REGS[nth_reg]); + nth_reg += 1; + + let value_hi = caller.get_reg(Reg::ARG_REGS[nth_reg]); + nth_reg += 1; + + args[nth_arg] = Value::F64(u64::from(value_lo) | (u64::from(value_hi) << 32)); + }, + } + } + + log::trace!( + "Calling host function: '{}', args = {:?}", + function.name(), + &args[..function.signature().args.len()] + ); + + let value = match function + .execute(&mut Context(caller), &mut args.into_iter().take(function.signature().args.len())) + { + Ok(value) => value, + Err(error) => { + log::warn!("Call into the host function '{}' failed: {error}", function.name()); + return Err(polkavm::Trap::default()); + }, + }; + + if let Some(value) = value { + match value { + Value::I32(value) => { + caller.set_reg(Reg::A0, value as u32); + }, + Value::F32(value) => { + caller.set_reg(Reg::A0, value); + }, + Value::I64(value) => { + caller.set_reg(Reg::A0, value as u32); + caller.set_reg(Reg::A1, (value >> 32) as u32); + }, + Value::F64(value) => { + caller.set_reg(Reg::A0, value as u32); + caller.set_reg(Reg::A1, (value >> 32) as u32); + }, + } + } + + Ok(()) +} + +pub fn create_runtime(blob: &polkavm::ProgramBlob) -> Result, WasmError> +where + H: HostFunctions, +{ + static ENGINE: std::sync::OnceLock> = + std::sync::OnceLock::new(); + + let engine = ENGINE.get_or_init(|| { + let config = polkavm::Config::from_env()?; + polkavm::Engine::new(&config) + }); + + let engine = match engine { + Ok(ref engine) => engine, + Err(ref error) => { + return Err(WasmError::Other(error.to_string())); + }, + }; + + let module = polkavm::Module::from_blob(&engine, &polkavm::ModuleConfig::default(), blob)?; + let mut linker = polkavm::Linker::new(&engine); + for function in H::host_functions() { + linker.func_new(function.name(), |mut caller| call_host_function(&mut caller, function))?; + } + + let instance_pre = linker.instantiate_pre(&module)?; + Ok(Box::new(InstancePre(instance_pre))) +} diff --git a/substrate/client/executor/runtime-test/src/lib.rs b/substrate/client/executor/runtime-test/src/lib.rs index ec9b2378d4d463b36fdd7370e013ab4de6849226..40683fbb664aadb6daf68595befcc68881322f05 100644 --- a/substrate/client/executor/runtime-test/src/lib.rs +++ b/substrate/client/executor/runtime-test/src/lib.rs @@ -181,7 +181,7 @@ sp_core::wasm_export_functions! { sig.copy_from_slice(&input[32..96]); let msg = b"all ok!"; - ed25519_verify(&ed25519::Signature(sig), &msg[..], &ed25519::Public(pubkey)) + ed25519_verify(&ed25519::Signature::from(sig), &msg[..], &ed25519::Public::from(pubkey)) } fn test_sr25519_verify(input: Vec) -> bool { @@ -192,7 +192,7 @@ sp_core::wasm_export_functions! { sig.copy_from_slice(&input[32..96]); let msg = b"all ok!"; - sr25519_verify(&sr25519::Signature(sig), &msg[..], &sr25519::Public(pubkey)) + sr25519_verify(&sr25519::Signature::from(sig), &msg[..], &sr25519::Public::from(pubkey)) } fn test_ordered_trie_root() -> Vec { diff --git a/substrate/client/executor/src/executor.rs b/substrate/client/executor/src/executor.rs index 499bb704b16990de49d2b3311c48fa8ee2c1813e..d56a3b389ef42868d0543303c5fbd63ea8d2d884 100644 --- a/substrate/client/executor/src/executor.rs +++ b/substrate/client/executor/src/executor.rs @@ -518,7 +518,7 @@ where runtime_code, ext, heap_alloc_strategy, - |_, mut instance, _onchain_version, mut ext| { + |_, mut instance, _on_chain_version, mut ext| { with_externalities_safe(&mut **ext, move || instance.call_export(method, data)) }, ); @@ -682,18 +682,18 @@ impl CodeExecutor for NativeElseWasmExecut runtime_code, ext, heap_alloc_strategy, - |_, mut instance, onchain_version, mut ext| { - let onchain_version = - onchain_version.ok_or_else(|| Error::ApiError("Unknown version".into()))?; + |_, mut instance, on_chain_version, mut ext| { + let on_chain_version = + on_chain_version.ok_or_else(|| Error::ApiError("Unknown version".into()))?; let can_call_with = - onchain_version.can_call_with(&self.native_version.runtime_version); + on_chain_version.can_call_with(&self.native_version.runtime_version); if use_native && can_call_with { tracing::trace!( target: "executor", native = %self.native_version.runtime_version, - chain = %onchain_version, + chain = %on_chain_version, "Request for native execution succeeded", ); @@ -705,7 +705,7 @@ impl CodeExecutor for NativeElseWasmExecut tracing::trace!( target: "executor", native = %self.native_version.runtime_version, - chain = %onchain_version, + chain = %on_chain_version, "Request for native execution failed", ); } diff --git a/substrate/client/executor/src/wasm_runtime.rs b/substrate/client/executor/src/wasm_runtime.rs index 501279a312cc09bdaada09b64895a1e6c5a2c87a..be8344ba79b7d78f8d49880ea73754788f33ff3e 100644 --- a/substrate/client/executor/src/wasm_runtime.rs +++ b/substrate/client/executor/src/wasm_runtime.rs @@ -297,6 +297,10 @@ pub fn create_wasm_runtime_with_code( where H: HostFunctions, { + if let Some(blob) = blob.as_polkavm_blob() { + return sc_executor_polkavm::create_runtime::(blob); + } + match wasm_method { WasmExecutionMethod::Compiled { instantiation_strategy } => sc_executor_wasmtime::create_runtime::( diff --git a/substrate/client/executor/wasmtime/Cargo.toml b/substrate/client/executor/wasmtime/Cargo.toml index 75cc76a235430fa33b735e8b234493a24a3e2961..f3fef4046914128dff2ad0904d445e89cba11c6a 100644 --- a/substrate/client/executor/wasmtime/Cargo.toml +++ b/substrate/client/executor/wasmtime/Cargo.toml @@ -30,7 +30,7 @@ wasmtime = { version = "8.0.1", default-features = false, features = [ "parallel-compilation", "pooling-allocator", ] } -anyhow = "1.0.68" +anyhow = "1.0.81" sc-allocator = { path = "../../allocator" } sc-executor-common = { path = "../common" } sp-runtime-interface = { path = "../../../primitives/runtime-interface" } diff --git a/substrate/client/executor/wasmtime/src/instance_wrapper.rs b/substrate/client/executor/wasmtime/src/instance_wrapper.rs index 8852532adbcaa40293f2d7dfd0a326572d3c1ca4..4f67d1df9c5f3193d508b408c48c5c1230de2b54 100644 --- a/substrate/client/executor/wasmtime/src/instance_wrapper.rs +++ b/substrate/client/executor/wasmtime/src/instance_wrapper.rs @@ -22,36 +22,12 @@ use std::sync::Arc; use crate::runtime::{InstanceCounter, ReleaseInstanceHandle, Store, StoreData}; -use sc_executor_common::{ - error::{Backtrace, Error, MessageWithBacktrace, Result, WasmError}, - wasm_runtime::InvokeMethod, -}; -use sp_wasm_interface::{Pointer, Value, WordSize}; -use wasmtime::{ - AsContext, AsContextMut, Engine, Extern, Instance, InstancePre, Memory, Table, Val, -}; - -/// Invoked entrypoint format. -pub enum EntryPointType { - /// Direct call. - /// - /// Call is made by providing only payload reference and length. - Direct { entrypoint: wasmtime::TypedFunc<(u32, u32), u64> }, - /// Indirect call. - /// - /// Call is made by providing payload reference and length, and extra argument - /// for advanced routing. - Wrapped { - /// The extra argument passed to the runtime. It is typically a wasm function pointer. - func: u32, - dispatcher: wasmtime::TypedFunc<(u32, u32, u32), u64>, - }, -} +use sc_executor_common::error::{Backtrace, Error, MessageWithBacktrace, Result, WasmError}; +use sp_wasm_interface::{Pointer, WordSize}; +use wasmtime::{AsContext, AsContextMut, Engine, Instance, InstancePre, Memory}; /// Wasm blob entry point. -pub struct EntryPoint { - call_type: EntryPointType, -} +pub struct EntryPoint(wasmtime::TypedFunc<(u32, u32), u64>); impl EntryPoint { /// Call this entry point. @@ -64,13 +40,7 @@ impl EntryPoint { let data_ptr = u32::from(data_ptr); let data_len = u32::from(data_len); - match self.call_type { - EntryPointType::Direct { ref entrypoint } => - entrypoint.call(&mut *store, (data_ptr, data_len)), - EntryPointType::Wrapped { func, ref dispatcher } => - dispatcher.call(&mut *store, (func, data_ptr, data_len)), - } - .map_err(|trap| { + self.0.call(&mut *store, (data_ptr, data_len)).map_err(|trap| { let host_state = store .data_mut() .host_state @@ -99,18 +69,7 @@ impl EntryPoint { let entrypoint = func .typed::<(u32, u32), u64>(ctx) .map_err(|_| "Invalid signature for direct entry point")?; - Ok(Self { call_type: EntryPointType::Direct { entrypoint } }) - } - - pub fn wrapped( - dispatcher: wasmtime::Func, - func: u32, - ctx: impl AsContext, - ) -> std::result::Result { - let dispatcher = dispatcher - .typed::<(u32, u32, u32), u64>(ctx) - .map_err(|_| "Invalid signature for wrapped entry point")?; - Ok(Self { call_type: EntryPointType::Wrapped { func, dispatcher } }) + Ok(Self(entrypoint)) } } @@ -178,10 +137,8 @@ impl InstanceWrapper { })?; let memory = get_linear_memory(&instance, &mut store)?; - let table = get_table(&instance, &mut store); store.data_mut().memory = Some(memory); - store.data_mut().table = table; Ok(InstanceWrapper { instance, store, _release_instance_handle }) } @@ -190,61 +147,17 @@ impl InstanceWrapper { /// /// An entrypoint must have a signature `(i32, i32) -> i64`, otherwise this function will return /// an error. - pub fn resolve_entrypoint(&mut self, method: InvokeMethod) -> Result { - Ok(match method { - InvokeMethod::Export(method) => { - // Resolve the requested method and verify that it has a proper signature. - let export = - self.instance.get_export(&mut self.store, method).ok_or_else(|| { - Error::from(format!("Exported method {} is not found", method)) - })?; - let func = export - .into_func() - .ok_or_else(|| Error::from(format!("Export {} is not a function", method)))?; - EntryPoint::direct(func, &self.store).map_err(|_| { - Error::from(format!("Exported function '{}' has invalid signature.", method)) - })? - }, - InvokeMethod::Table(func_ref) => { - let table = self - .instance - .get_table(&mut self.store, "__indirect_function_table") - .ok_or(Error::NoTable)?; - let val = table - .get(&mut self.store, func_ref) - .ok_or(Error::NoTableEntryWithIndex(func_ref))?; - let func = val - .funcref() - .ok_or(Error::TableElementIsNotAFunction(func_ref))? - .ok_or(Error::FunctionRefIsNull(func_ref))?; - - EntryPoint::direct(*func, &self.store).map_err(|_| { - Error::from(format!( - "Function @{} in exported table has invalid signature for direct call.", - func_ref, - )) - })? - }, - InvokeMethod::TableWithWrapper { dispatcher_ref, func } => { - let table = self - .instance - .get_table(&mut self.store, "__indirect_function_table") - .ok_or(Error::NoTable)?; - let val = table - .get(&mut self.store, dispatcher_ref) - .ok_or(Error::NoTableEntryWithIndex(dispatcher_ref))?; - let dispatcher = val - .funcref() - .ok_or(Error::TableElementIsNotAFunction(dispatcher_ref))? - .ok_or(Error::FunctionRefIsNull(dispatcher_ref))?; - - EntryPoint::wrapped(*dispatcher, func, &self.store).map_err(|_| { - Error::from(format!( - "Function @{} in exported table has invalid signature for wrapped call.", - dispatcher_ref, - )) - })? - }, + pub fn resolve_entrypoint(&mut self, method: &str) -> Result { + // Resolve the requested method and verify that it has a proper signature. + let export = self + .instance + .get_export(&mut self.store, method) + .ok_or_else(|| Error::from(format!("Exported method {} is not found", method)))?; + let func = export + .into_func() + .ok_or_else(|| Error::from(format!("Export {} is not a function", method)))?; + EntryPoint::direct(func, &self.store).map_err(|_| { + Error::from(format!("Exported function '{}' has invalid signature.", method)) }) } @@ -268,24 +181,6 @@ impl InstanceWrapper { Ok(heap_base as u32) } - - /// Get the value from a global with the given `name`. - pub fn get_global_val(&mut self, name: &str) -> Result> { - let global = match self.instance.get_export(&mut self.store, name) { - Some(global) => global, - None => return Ok(None), - }; - - let global = global.into_global().ok_or_else(|| format!("`{}` is not a global", name))?; - - match global.get(&mut self.store) { - Val::I32(val) => Ok(Some(Value::I32(val))), - Val::I64(val) => Ok(Some(Value::I64(val))), - Val::F32(val) => Ok(Some(Value::F32(val))), - Val::F64(val) => Ok(Some(Value::F64(val))), - _ => Err("Unknown value type".into()), - } - } } /// Extract linear memory instance from the given instance. @@ -301,15 +196,6 @@ fn get_linear_memory(instance: &Instance, ctx: impl AsContextMut) -> Result Option
CommittedCandidateReceipt<H = Hash>
{ - instance - .get_export(ctx, "__indirect_function_table") - .as_ref() - .cloned() - .and_then(Extern::into_table) -} - /// Functions related to memory. impl InstanceWrapper { pub(crate) fn store(&self) -> &Store { diff --git a/substrate/client/executor/wasmtime/src/runtime.rs b/substrate/client/executor/wasmtime/src/runtime.rs index ac88663f4e7915e49262ee8186bfaf7d92f6064d..286d134ecd1715cdd876e3412c37958acfc93b71 100644 --- a/substrate/client/executor/wasmtime/src/runtime.rs +++ b/substrate/client/executor/wasmtime/src/runtime.rs @@ -30,10 +30,10 @@ use sc_executor_common::{ error::{Error, Result, WasmError}, runtime_blob::RuntimeBlob, util::checked_range, - wasm_runtime::{HeapAllocStrategy, InvokeMethod, WasmInstance, WasmModule}, + wasm_runtime::{HeapAllocStrategy, WasmInstance, WasmModule}, }; use sp_runtime_interface::unpack_ptr_and_len; -use sp_wasm_interface::{HostFunctions, Pointer, Value, WordSize}; +use sp_wasm_interface::{HostFunctions, Pointer, WordSize}; use std::{ path::{Path, PathBuf}, sync::{ @@ -41,7 +41,7 @@ use std::{ Arc, }, }; -use wasmtime::{AsContext, Engine, Memory, Table}; +use wasmtime::{AsContext, Engine, Memory}; const MAX_INSTANCE_COUNT: u32 = 64; @@ -51,8 +51,6 @@ pub(crate) struct StoreData { pub(crate) host_state: Option, /// This will be always set once the store is initialized. pub(crate) memory: Option, - /// This will be set only if the runtime actually contains a table. - pub(crate) table: Option
, } impl StoreData { @@ -164,7 +162,7 @@ pub struct WasmtimeInstance { impl WasmtimeInstance { fn call_impl( &mut self, - method: InvokeMethod, + method: &str, data: &[u8], allocation_stats: &mut Option, ) -> Result> { @@ -184,20 +182,13 @@ impl WasmtimeInstance { impl WasmInstance for WasmtimeInstance { fn call_with_allocation_stats( &mut self, - method: InvokeMethod, + method: &str, data: &[u8], ) -> (Result>, Option) { let mut allocation_stats = None; let result = self.call_impl(method, data, &mut allocation_stats); (result, allocation_stats) } - - fn get_global_const(&mut self, name: &str) -> Result> { - match &mut self.strategy { - Strategy::RecreateInstance(ref mut instance_creator) => - instance_creator.instantiate()?.get_global_val(name), - } - } } /// Prepare a directory structure and a config file to enable wasmtime caching. @@ -471,7 +462,7 @@ pub struct Semantics { pub struct Config { /// The WebAssembly standard requires all imports of an instantiated module to be resolved, /// otherwise, the instantiation fails. If this option is set to `true`, then this behavior is - /// overriden and imports that are requested by the module and not provided by the host + /// overridden and imports that are requested by the module and not provided by the host /// functions will be resolved using stubs. These stubs will trap upon a call. pub allow_missing_func_imports: bool, diff --git a/substrate/client/executor/wasmtime/src/tests.rs b/substrate/client/executor/wasmtime/src/tests.rs index 1c06da1e3c142f58d4fa3ec64cf87577190218f8..f86a4275769401712622112c6a2c587d11e1adc4 100644 --- a/substrate/client/executor/wasmtime/src/tests.rs +++ b/substrate/client/executor/wasmtime/src/tests.rs @@ -254,7 +254,7 @@ fn test_nan_canonicalization(instantiation_strategy: InstantiationStrategy) { /// A NaN with canonical payload bits. const CANONICAL_NAN_BITS: u32 = 0x7fc00000; - /// A NaN value with an abitrary payload. + /// A NaN value with an arbitrary payload. const ARBITRARY_NAN_BITS: u32 = 0x7f812345; // This test works like this: we essentially do @@ -272,7 +272,7 @@ fn test_nan_canonicalization(instantiation_strategy: InstantiationStrategy) { // However, with the `canonicalize_nans` option turned on above, we expect that the output will // be a canonical NaN. // - // We exterpolate the results of this tests so that we assume that all intermediate computations + // We extrapolate the results of this tests so that we assume that all intermediate computations // that involve floats are sanitized and cannot produce a non-deterministic NaN. let params = (u32::to_le_bytes(ARBITRARY_NAN_BITS), u32::to_le_bytes(1)).encode(); diff --git a/substrate/client/network/Cargo.toml b/substrate/client/network/Cargo.toml index cbf74440dc1a83933d1e994294a7d37609c72331..c6f17647166c4c406bd83003344177395599e67b 100644 --- a/substrate/client/network/Cargo.toml +++ b/substrate/client/network/Cargo.toml @@ -29,7 +29,7 @@ futures = "0.3.21" futures-timer = "3.0.2" ip_network = "0.4.1" libp2p = { version = "0.51.4", features = ["dns", "identify", "kad", "macros", "mdns", "noise", "ping", "request-response", "tcp", "tokio", "websocket", "yamux"] } -linked_hash_set = "0.1.3" +linked_hash_set = "0.1.4" log = { workspace = true, default-features = true } mockall = "0.11.3" parking_lot = "0.12.1" diff --git a/substrate/client/network/bitswap/src/lib.rs b/substrate/client/network/bitswap/src/lib.rs index 0586354d6a0dd4c576d4d326d04bacd69069bcb9..1ba95e30bad101e92adc2291b5d0928896f33269 100644 --- a/substrate/client/network/bitswap/src/lib.rs +++ b/substrate/client/network/bitswap/src/lib.rs @@ -301,7 +301,7 @@ mod tests { use substrate_test_runtime_client::{self, prelude::*, TestClientBuilder}; #[tokio::test] - async fn undecodeable_message() { + async fn undecodable_message() { let client = substrate_test_runtime_client::new(); let (bitswap, config) = BitswapRequestHandler::new(Arc::new(client)); diff --git a/substrate/client/network/src/discovery.rs b/substrate/client/network/src/discovery.rs index 77c26266aac4691cf36b440e6d06c792e57a311e..4e2121c5540d6c7c6d895e2756b266f6b5a945e8 100644 --- a/substrate/client/network/src/discovery.rs +++ b/substrate/client/network/src/discovery.rs @@ -72,6 +72,7 @@ use libp2p::{ }, PeerId, }; +use linked_hash_set::LinkedHashSet; use log::{debug, info, trace, warn}; use sp_core::hexdisplay::HexDisplay; use std::{ @@ -550,14 +551,20 @@ impl NetworkBehaviour for DiscoveryBehaviour { ) -> Result, ConnectionDenied> { let Some(peer_id) = maybe_peer else { return Ok(Vec::new()) }; - let mut list = self + // Collect addresses into [`LinkedHashSet`] to eliminate duplicate entries preserving the + // order of addresses. Give priority to `permanent_addresses` (used with reserved nodes) and + // `ephemeral_addresses` (used for addresses discovered from other sources, like authority + // discovery DHT records). + let mut list: LinkedHashSet<_> = self .permanent_addresses .iter() .filter_map(|(p, a)| (*p == peer_id).then_some(a.clone())) - .collect::>(); + .collect(); if let Some(ephemeral_addresses) = self.ephemeral_addresses.get(&peer_id) { - list.extend(ephemeral_addresses.clone()); + ephemeral_addresses.iter().for_each(|address| { + list.insert_if_absent(address.clone()); + }); } { @@ -583,12 +590,14 @@ impl NetworkBehaviour for DiscoveryBehaviour { }); } - list.extend(list_to_filter); + list_to_filter.into_iter().for_each(|address| { + list.insert_if_absent(address); + }); } trace!(target: "sub-libp2p", "Addresses of {:?}: {:?}", peer_id, list); - Ok(list) + Ok(list.into_iter().collect()) } fn on_swarm_event(&mut self, event: FromSwarm) { diff --git a/substrate/client/network/src/protocol/notifications/behaviour.rs b/substrate/client/network/src/protocol/notifications/behaviour.rs index 9ad41e376e824835cdb0be883b3c0f344218ed11..b945d4bfc6043cccbeb41219d2f25edd33c5d510 100644 --- a/substrate/client/network/src/protocol/notifications/behaviour.rs +++ b/substrate/client/network/src/protocol/notifications/behaviour.rs @@ -2668,7 +2668,7 @@ mod tests { // // there is not straight-forward way of adding backoff to `PeerState::Disabled` // so manually adjust the value in order to progress on to the next stage. - // This modification together with `ConnectionClosed` will conver the peer + // This modification together with `ConnectionClosed` will convert the peer // state into `PeerState::Backoff`. if let Some(PeerState::Disabled { ref mut backoff_until, .. }) = notif.peers.get_mut(&(peer, set_id)) diff --git a/substrate/client/network/src/protocol/notifications/handler.rs b/substrate/client/network/src/protocol/notifications/handler.rs index 28662be29feede2ef67f150d6fa1b67ff313ae36..391252c3ffe71cdf3f7e9b5d1efc0ee5ad0ffcc6 100644 --- a/substrate/client/network/src/protocol/notifications/handler.rs +++ b/substrate/client/network/src/protocol/notifications/handler.rs @@ -211,7 +211,7 @@ enum State { /// consequently trying to open the various notifications substreams. /// /// A [`NotifsHandlerOut::OpenResultOk`] or a [`NotifsHandlerOut::OpenResultErr`] event must - /// be emitted when transitionning to respectively [`State::Open`] or [`State::Closed`]. + /// be emitted when transitioning to respectively [`State::Open`] or [`State::Closed`]. Opening { /// Substream opened by the remote. If `Some`, has been accepted. in_substream: Option>, diff --git a/substrate/client/network/src/protocol/notifications/service/mod.rs b/substrate/client/network/src/protocol/notifications/service/mod.rs index 62e6d88a3d5a6ae36b46728a37dc3ff5c31177db..6d9873f45d53425379cfb0de1220e2fa7da58636 100644 --- a/substrate/client/network/src/protocol/notifications/service/mod.rs +++ b/substrate/client/network/src/protocol/notifications/service/mod.rs @@ -54,7 +54,7 @@ const COMMAND_QUEUE_SIZE: usize = 64; /// Type representing subscribers of a notification protocol. type Subscribers = Arc>>>; -/// Type represending a distributable message sink. +/// Type representing a distributable message sink. /// Detached message sink must carry the protocol name for registering metrics. /// /// See documentation for [`PeerContext`] for more details. @@ -175,11 +175,11 @@ pub enum NotificationCommand { /// and an additional, distributable `NotificationsSink` which the protocol may acquire /// if it wishes to send notifications through `NotificationsSink` directly. /// -/// The distributable `NoticationsSink` is wrapped in an `Arc>` to allow +/// The distributable `NotificationsSink` is wrapped in an `Arc>` to allow /// `NotificationsService` to swap the underlying sink in case it's replaced. #[derive(Debug, Clone)] struct PeerContext { - /// Sink for sending notificaitons. + /// Sink for sending notifications. sink: NotificationsSink, /// Distributable notification sink. diff --git a/substrate/client/network/src/protocol/notifications/service/tests.rs b/substrate/client/network/src/protocol/notifications/service/tests.rs index 02ba9e1711c3856df095c8a31688e3388bf0bb00..238e0ccf566898d9c19431a0d00bc14284d449e4 100644 --- a/substrate/client/network/src/protocol/notifications/service/tests.rs +++ b/substrate/client/network/src/protocol/notifications/service/tests.rs @@ -437,7 +437,7 @@ async fn peer_disconnects_then_async_notification_is_sent() { notif.send_async_notification(&peer_id, vec![1, 3, 3, 7]).await { } else { - panic!("invalid state after calling `send_async_notificatio()` on closed connection") + panic!("invalid state after calling `send_async_notification()` on closed connection") } } diff --git a/substrate/client/network/src/protocol/notifications/upgrade/notifications.rs b/substrate/client/network/src/protocol/notifications/upgrade/notifications.rs index 4e1c033f33b68e551fbd98e9c06edcbdd7173b5c..85209a888cd9d935ea6533b64c003e5ee407eeca 100644 --- a/substrate/client/network/src/protocol/notifications/upgrade/notifications.rs +++ b/substrate/client/network/src/protocol/notifications/upgrade/notifications.rs @@ -188,7 +188,7 @@ where } } -/// Yielded by the [`NotificationsIn`] after a successfuly upgrade. +/// Yielded by the [`NotificationsIn`] after a successfully upgrade. pub struct NotificationsInOpen { /// Handshake sent by the remote. pub handshake: Vec, @@ -415,7 +415,7 @@ where } } -/// Yielded by the [`NotificationsOut`] after a successfuly upgrade. +/// Yielded by the [`NotificationsOut`] after a successfully upgrade. pub struct NotificationsOutOpen { /// Handshake returned by the remote. pub handshake: Vec, diff --git a/substrate/client/network/src/protocol_controller.rs b/substrate/client/network/src/protocol_controller.rs index 4c8f119baa203b3942f4156e2f92efb72007c77f..7f851fd8e9c99c10ff4b93e2f3ca6836ffc6de80 100644 --- a/substrate/client/network/src/protocol_controller.rs +++ b/substrate/client/network/src/protocol_controller.rs @@ -448,7 +448,7 @@ impl ProtocolController { self.peer_store.report_disconnect(peer_id); } - /// Ask `Peerset` if the peer has a reputation value not sufficent for connection with it. + /// Ask `Peerset` if the peer has a reputation value not sufficient for connection with it. fn is_banned(&self, peer_id: &PeerId) -> bool { self.peer_store.is_banned(peer_id) } @@ -2020,7 +2020,7 @@ mod tests { ProtocolController::new(SetId::from(0), config, tx, Box::new(peer_store)); assert!(matches!(controller.reserved_nodes.get(&reserved1), Some(PeerState::NotConnected))); - // Initiate connectios + // Initiate connections controller.alloc_slots(); assert!(matches!(controller.reserved_nodes.get(&reserved1), Some(PeerState::NotConnected))); assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty); diff --git a/substrate/client/network/sync/src/engine.rs b/substrate/client/network/sync/src/engine.rs index c1a7009eeb01745ca5a7069eb2a258768ad2c49c..ff40ae95624e198c4eb5af64c5af85f9cd180c9a 100644 --- a/substrate/client/network/sync/src/engine.rs +++ b/substrate/client/network/sync/src/engine.rs @@ -110,7 +110,7 @@ const INACTIVITY_EVICT_THRESHOLD: Duration = Duration::from_secs(30); /// Parachain collator may incorrectly get evicted because it's waiting to receive a number of /// relaychain blocks before it can start creating parachain blocks. During this wait, /// `SyncingEngine` still counts it as active and as the peer is not sending blocks, it may get -/// evicted if a block is not received within the first 30 secons since the peer connected. +/// evicted if a block is not received within the first 30 seconds since the peer connected. /// /// To prevent this from happening, define a threshold for how long `SyncingEngine` should wait /// before it starts evicting peers. @@ -424,7 +424,7 @@ where .expect("Genesis block exists; qed"), ); - // Split warp sync params into warp sync config and a channel to retreive target block + // Split warp sync params into warp sync config and a channel to retrieve target block // header. let (warp_sync_config, warp_sync_target_block_header_rx) = warp_sync_params.map_or((None, None), |params| { @@ -656,6 +656,8 @@ where Some(event) => self.process_notification_event(event), None => return, }, + // TODO: setting of warp sync target block should be moved to the initialization of + // `SyncingEngine`, see https://github.com/paritytech/polkadot-sdk/issues/3537. warp_target_block_header = &mut self.warp_sync_target_block_header_rx_fused => { if let Err(_) = self.pass_warp_sync_target_block_header(warp_target_block_header) { error!( @@ -1055,7 +1057,7 @@ where // still be under validation. If the peer has different genesis than the // local node the validation fails but the peer cannot be reported in // `validate_connection()` as that is also called by - // `ValiateInboundSubstream` which means that the peer is still being + // `ValidateInboundSubstream` which means that the peer is still being // validated and banning the peer when handling that event would // result in peer getting dropped twice. // diff --git a/substrate/client/network/sync/src/lib.rs b/substrate/client/network/sync/src/lib.rs index e23a23e735d3e25b60b6b5bda8dd4beadcfe9377..9f6c0f45d089ce415f9e8c0ae792de1d7a327407 100644 --- a/substrate/client/network/sync/src/lib.rs +++ b/substrate/client/network/sync/src/lib.rs @@ -28,7 +28,7 @@ mod justification_requests; mod pending_responses; mod request_metrics; mod schema; -mod types; +pub mod types; pub mod block_relay_protocol; pub mod block_request_handler; diff --git a/substrate/client/network/sync/src/strategy.rs b/substrate/client/network/sync/src/strategy.rs index 7d6e6a8d3b8b79e9e926261d298e4fcacf7c714e..610fd7c65606b18d7eb9dc018636f1fac3ff35f5 100644 --- a/substrate/client/network/sync/src/strategy.rs +++ b/substrate/client/network/sync/src/strategy.rs @@ -30,7 +30,7 @@ use crate::{ }; use chain_sync::{ChainSync, ChainSyncAction, ChainSyncMode}; use libp2p::PeerId; -use log::{debug, error, info}; +use log::{debug, error, info, warn}; use prometheus_endpoint::Registry; use sc_client_api::{BlockBackend, ProofProvider}; use sc_consensus::{BlockImportError, BlockImportStatus, IncomingBlock}; @@ -159,7 +159,7 @@ impl From> for SyncingAction { /// Proxy to specific syncing strategies. pub struct SyncingStrategy { - /// Syncing configuration. + /// Initial syncing configuration. config: SyncingConfig, /// Client used by syncing strategies. client: Arc, @@ -185,7 +185,7 @@ where + Sync + 'static, { - /// Initialize a new syncing startegy. + /// Initialize a new syncing strategy. pub fn new( config: SyncingConfig, client: Arc, @@ -418,7 +418,7 @@ where self.state.is_some() || match self.chain_sync { Some(ref s) => s.status().state.is_major_syncing(), - None => unreachable!("At least one syncing startegy is active; qed"), + None => unreachable!("At least one syncing strategy is active; qed"), } } @@ -429,7 +429,7 @@ where /// Returns the current sync status. pub fn status(&self) -> SyncStatus { - // This function presumes that startegies are executed serially and must be refactored + // This function presumes that strategies are executed serially and must be refactored // once we have parallel strategies. if let Some(ref warp) = self.warp { warp.status() @@ -438,7 +438,7 @@ where } else if let Some(ref chain_sync) = self.chain_sync { chain_sync.status() } else { - unreachable!("At least one syncing startegy is always active; qed") + unreachable!("At least one syncing strategy is always active; qed") } } @@ -466,15 +466,27 @@ where &mut self, target_header: B::Header, ) -> Result<(), ()> { - match self.warp { - Some(ref mut warp) => { - warp.set_target_block(target_header); - Ok(()) + match self.config.mode { + SyncMode::Warp => match self.warp { + Some(ref mut warp) => { + warp.set_target_block(target_header); + Ok(()) + }, + None => { + // As mode is set to warp sync, but no warp sync strategy is active, this means + // that warp sync has already finished / was skipped. + warn!( + target: LOG_TARGET, + "Discarding warp sync target, as warp sync was seemingly skipped due \ + to node being (partially) synced.", + ); + Ok(()) + }, }, - None => { + _ => { error!( target: LOG_TARGET, - "Cannot set warp sync target block: no warp sync strategy is active." + "Cannot set warp sync target block: not in warp sync mode." ); debug_assert!(false); Err(()) @@ -506,7 +518,7 @@ where /// Proceed with the next strategy if the active one finished. pub fn proceed_to_next(&mut self) -> Result<(), ClientError> { - // The strategies are switched as `WarpSync` -> `StateStartegy` -> `ChainSync`. + // The strategies are switched as `WarpSync` -> `StateStrategy` -> `ChainSync`. if let Some(ref mut warp) = self.warp { match warp.take_result() { Some(res) => { @@ -557,7 +569,7 @@ where }, } } else if let Some(state) = &self.state { - if state.is_succeded() { + if state.is_succeeded() { info!(target: LOG_TARGET, "State sync is complete, continuing with block sync."); } else { error!(target: LOG_TARGET, "State sync failed. Falling back to full sync."); @@ -587,3 +599,63 @@ where } } } + +#[cfg(test)] +mod test { + use super::*; + use futures::executor::block_on; + use sc_block_builder::BlockBuilderBuilder; + use substrate_test_runtime_client::{ + ClientBlockImportExt, ClientExt, DefaultTestClientBuilderExt, TestClientBuilder, + TestClientBuilderExt, + }; + + /// Regression test for crash when starting already synced parachain node with `--sync=warp`. + /// We must remove this after setting of warp sync target block is moved to initialization of + /// `SyncingEngine` (issue https://github.com/paritytech/polkadot-sdk/issues/3537). + #[test] + fn set_target_block_finished_warp_sync() { + // Populate database with finalized state. + let mut client = Arc::new(TestClientBuilder::new().build()); + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; + block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); + let just = (*b"TEST", Vec::new()); + client.finalize_block(block.hash(), Some(just)).unwrap(); + let target_block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; + + // Initialize syncing strategy. + let config = SyncingConfig { + mode: SyncMode::Warp, + max_parallel_downloads: 3, + max_blocks_per_request: 64, + metrics_registry: None, + }; + let mut strategy = + SyncingStrategy::new(config, client, Some(WarpSyncConfig::WaitForTarget)).unwrap(); + + // Warp sync instantly finishes as we have finalized state in DB. + let actions = strategy.actions().unwrap(); + assert_eq!(actions.len(), 1); + assert!(matches!(actions[0], SyncingAction::Finished)); + assert!(strategy.warp.is_none()); + + // Try setting the target block. We mustn't crash. + strategy + .set_warp_sync_target_block_header(target_block.header().clone()) + .unwrap(); + } +} diff --git a/substrate/client/network/sync/src/strategy/chain_sync.rs b/substrate/client/network/sync/src/strategy/chain_sync.rs index ad0c75363e78ac948b4e10352328c64cefb792a1..da04bbbeccc889250f57818e9a14e4f25400093d 100644 --- a/substrate/client/network/sync/src/strategy/chain_sync.rs +++ b/substrate/client/network/sync/src/strategy/chain_sync.rs @@ -117,7 +117,7 @@ mod rep { /// Reputation change for peers which send us a block with bad justifications. pub const BAD_JUSTIFICATION: Rep = Rep::new(-(1 << 16), "Bad justification"); - /// Reputation change when a peer sent us invlid ancestry result. + /// Reputation change when a peer sent us invalid ancestry result. pub const UNKNOWN_ANCESTOR: Rep = Rep::new(-(1 << 16), "DB Error"); /// Peer response data does not have requested bits. @@ -1334,7 +1334,7 @@ where PeerSyncState::DownloadingJustification(_) => { // Peers that were downloading justifications // should be kept in that state. - // We make sure our commmon number is at least something we have. + // We make sure our common number is at least something we have. trace!( target: LOG_TARGET, "Keeping peer {} after restart, updating common number from={} => to={} (our best).", diff --git a/substrate/client/network/sync/src/strategy/chain_sync/test.rs b/substrate/client/network/sync/src/strategy/chain_sync/test.rs index 127b6862f0e0dca6c01a88bc6ee83b1f57e7c4dc..cd955113542be598e18c94057d3ad056c95a6002 100644 --- a/substrate/client/network/sync/src/strategy/chain_sync/test.rs +++ b/substrate/client/network/sync/src/strategy/chain_sync/test.rs @@ -189,7 +189,7 @@ fn restart_doesnt_affect_peers_downloading_finality_data() { assert_eq!(sync.peers.get(&peer_id3).unwrap().common_number, 50); } -/// Send a block annoucnement for the given `header`. +/// Send a block announcement for the given `header`. fn send_block_announce(header: Header, peer_id: PeerId, sync: &mut ChainSync) { let announce = BlockAnnounce { header: header.clone(), @@ -278,7 +278,7 @@ fn unwrap_from_block_number(from: FromBlock) -> u64 { /// announcement from this node in its sync process. Meaning our common number didn't change. It /// is now expected that we start an ancestor search to find the common number. #[test] -fn do_ancestor_search_when_common_block_to_best_qeued_gap_is_to_big() { +fn do_ancestor_search_when_common_block_to_best_queued_gap_is_to_big() { sp_tracing::try_init_simple(); let blocks = { @@ -472,7 +472,7 @@ fn can_sync_huge_fork() { let actions = sync.take_actions().collect::>(); request = if actions.is_empty() { - // We found the ancenstor + // We found the ancestor break } else { assert_eq!(actions.len(), 1); @@ -607,7 +607,7 @@ fn syncs_fork_without_duplicate_requests() { let actions = sync.take_actions().collect::>(); request = if actions.is_empty() { - // We found the ancenstor + // We found the ancestor break } else { assert_eq!(actions.len(), 1); diff --git a/substrate/client/network/sync/src/strategy/state.rs b/substrate/client/network/sync/src/strategy/state.rs index 12d36ff9e01a9c16bfee482c5e9e641b13bb3b87..6d3b215f7f3210073c2ab61a2e57b0abf9652150 100644 --- a/substrate/client/network/sync/src/strategy/state.rs +++ b/substrate/client/network/sync/src/strategy/state.rs @@ -79,7 +79,7 @@ pub struct StateStrategy { state_sync: Box>, peers: HashMap>, actions: Vec>, - succeded: bool, + succeeded: bool, } impl StateStrategy { @@ -110,7 +110,7 @@ impl StateStrategy { )), peers, actions: Vec::new(), - succeded: false, + succeeded: false, } } @@ -129,7 +129,7 @@ impl StateStrategy { }) .collect(), actions: Vec::new(), - succeded: false, + succeeded: false, } } @@ -260,7 +260,7 @@ impl StateStrategy { "Failed to import target block with state: {e:?}." ); }); - self.succeded |= results.into_iter().any(|result| result.is_ok()); + self.succeeded |= results.into_iter().any(|result| result.is_ok()); self.actions.push(StateStrategyAction::Finished); } } @@ -342,10 +342,10 @@ impl StateStrategy { std::mem::take(&mut self.actions).into_iter() } - /// Check if state sync has succeded. + /// Check if state sync has succeeded. #[must_use] - pub fn is_succeded(&self) -> bool { - self.succeded + pub fn is_succeeded(&self) -> bool { + self.succeeded } } @@ -669,7 +669,7 @@ mod test { } #[test] - fn succesfully_importing_target_block_finishes_strategy() { + fn successfully_importing_target_block_finishes_strategy() { let target_hash = Hash::random(); let mut state_sync_provider = MockStateSync::::new(); state_sync_provider.expect_target_hash().return_const(target_hash); diff --git a/substrate/client/network/sync/src/strategy/warp.rs b/substrate/client/network/sync/src/strategy/warp.rs index 7935b5f29b68df1ac259e325e66522afd9f55e4c..c7b79228efeef37c5579462bc3fd9ba1978b78cb 100644 --- a/substrate/client/network/sync/src/strategy/warp.rs +++ b/substrate/client/network/sync/src/strategy/warp.rs @@ -968,7 +968,7 @@ mod test { warp_sync.on_warp_proof_response(&request_peer_id, EncodedProof(Vec::new())); - // We only interested in alredy generated actions, not new requests. + // We only interested in already generated actions, not new requests. let actions = std::mem::take(&mut warp_sync.actions); assert_eq!(actions.len(), 1); assert!(matches!( diff --git a/substrate/client/network/sync/src/warp_request_handler.rs b/substrate/client/network/sync/src/warp_request_handler.rs index 39cf1c5d8067e154d0e414fc42069f1f7500b097..eda67cac95ffae2f423ba062308192371c1a2f20 100644 --- a/substrate/client/network/sync/src/warp_request_handler.rs +++ b/substrate/client/network/sync/src/warp_request_handler.rs @@ -57,7 +57,7 @@ pub fn generate_request_response_config>( } } -/// Generate the grandpa warp sync protocol name from the genesi hash and fork id. +/// Generate the grandpa warp sync protocol name from the genesis hash and fork id. fn generate_protocol_name>(genesis_hash: Hash, fork_id: Option<&str>) -> String { let genesis_hash = genesis_hash.as_ref(); if let Some(fork_id) = fork_id { diff --git a/substrate/client/network/test/src/sync.rs b/substrate/client/network/test/src/sync.rs index c025a8262f0e53e15200749f30ba22696930c52f..f1c1b7414303202346a35aaf335d162997b504c3 100644 --- a/substrate/client/network/test/src/sync.rs +++ b/substrate/client/network/test/src/sync.rs @@ -749,7 +749,7 @@ async fn sync_blocks_when_block_announce_validator_says_it_is_new_best() { } } -/// Waits for some time until the validation is successfull. +/// Waits for some time until the validation is successful. struct DeferredBlockAnnounceValidator; impl BlockAnnounceValidator for DeferredBlockAnnounceValidator { diff --git a/substrate/client/offchain/src/api/http.rs b/substrate/client/offchain/src/api/http.rs index 7ca5e3fd13af788c7dee986202471a4ad2a9de6e..46f573341c57948539ef969f6f238923cd2fff22 100644 --- a/substrate/client/offchain/src/api/http.rs +++ b/substrate/client/offchain/src/api/http.rs @@ -604,7 +604,7 @@ enum WorkerToApi { /// because we don't want the `HttpApi` to have to drive the reading. /// Instead, reading an item from the channel will notify the worker task, which will push /// the next item. - /// Can also be used to send an error, in case an error happend on the HTTP socket. After + /// Can also be used to send an error, in case an error happened on the HTTP socket. After /// an error is sent, the channel will close. body: mpsc::Receiver>, }, diff --git a/substrate/client/proposer-metrics/src/lib.rs b/substrate/client/proposer-metrics/src/lib.rs index 012e8ca769a96cb51141c5a13ca7ba04d76a8123..2856300cf8027b45bb0ef32b8781c533ae2e340c 100644 --- a/substrate/client/proposer-metrics/src/lib.rs +++ b/substrate/client/proposer-metrics/src/lib.rs @@ -44,11 +44,14 @@ impl MetricsLink { } /// The reason why proposing a block ended. +#[derive(Clone, Copy, PartialEq, Eq)] pub enum EndProposingReason { NoMoreTransactions, HitDeadline, HitBlockSizeLimit, HitBlockWeightLimit, + /// No transactions are allowed in the block. + TransactionForbidden, } /// Authorship metrics. @@ -112,6 +115,7 @@ impl Metrics { EndProposingReason::NoMoreTransactions => "no_more_transactions", EndProposingReason::HitBlockSizeLimit => "hit_block_size_limit", EndProposingReason::HitBlockWeightLimit => "hit_block_weight_limit", + EndProposingReason::TransactionForbidden => "transactions_forbidden", }; self.end_proposing_reason.with_label_values(&[reason]).inc(); diff --git a/substrate/client/rpc-servers/Cargo.toml b/substrate/client/rpc-servers/Cargo.toml index 7f7a86799e0cc6510fc4ab7f0d3e1c2c3070b85f..3adc81c57d594a355212a88f83682a0441bc7acc 100644 --- a/substrate/client/rpc-servers/Cargo.toml +++ b/substrate/client/rpc-servers/Cargo.toml @@ -26,5 +26,4 @@ tower = { version = "0.4.13", features = ["util"] } http = "0.2.8" hyper = "0.14.27" futures = "0.3.29" -pin-project = "1.1.3" governor = "0.6.0" diff --git a/substrate/client/rpc-servers/src/lib.rs b/substrate/client/rpc-servers/src/lib.rs index e65d954bed5266746f2c163e865613f255356c44..ad4b444c7ff425ea84ba25e756d61854c3a4569e 100644 --- a/substrate/client/rpc-servers/src/lib.rs +++ b/substrate/client/rpc-servers/src/lib.rs @@ -49,7 +49,7 @@ pub use jsonrpsee::{ }, server::{middleware::rpc::RpcServiceBuilder, BatchRequestConfig}, }; -pub use middleware::{MetricsLayer, RateLimitLayer, RpcMetrics}; +pub use middleware::{Metrics, MiddlewareLayer, RpcMetrics}; const MEGABYTE: u32 = 1024 * 1024; @@ -173,13 +173,22 @@ where let is_websocket = ws::is_upgrade_request(&req); let transport_label = if is_websocket { "ws" } else { "http" }; - let metrics = metrics.map(|m| MetricsLayer::new(m, transport_label)); - let rate_limit = rate_limit.map(|r| RateLimitLayer::per_minute(r)); + let middleware_layer = match (metrics, rate_limit) { + (None, None) => None, + (Some(metrics), None) => Some( + MiddlewareLayer::new().with_metrics(Metrics::new(metrics, transport_label)), + ), + (None, Some(rate_limit)) => + Some(MiddlewareLayer::new().with_rate_limit_per_minute(rate_limit)), + (Some(metrics), Some(rate_limit)) => Some( + MiddlewareLayer::new() + .with_metrics(Metrics::new(metrics, transport_label)) + .with_rate_limit_per_minute(rate_limit), + ), + }; - // NOTE: The metrics needs to run first to include rate-limited calls in the - // metrics. let rpc_middleware = - RpcServiceBuilder::new().option_layer(metrics.clone()).option_layer(rate_limit); + RpcServiceBuilder::new().option_layer(middleware_layer.clone()); let mut svc = service_builder.set_rpc_middleware(rpc_middleware).build(methods, stop_handle); @@ -191,9 +200,9 @@ where // Spawn a task to handle when the connection is closed. tokio_handle.spawn(async move { let now = std::time::Instant::now(); - metrics.as_ref().map(|m| m.ws_connect()); + middleware_layer.as_ref().map(|m| m.ws_connect()); on_disconnect.await; - metrics.as_ref().map(|m| m.ws_disconnect(now)); + middleware_layer.as_ref().map(|m| m.ws_disconnect(now)); }); } diff --git a/substrate/client/rpc-servers/src/middleware/metrics.rs b/substrate/client/rpc-servers/src/middleware/metrics.rs index c2d1956c3b3985e8e071ad5316dcc2a150410415..688c3c2a9fc42a8dddfcc2487b921ffca8616292 100644 --- a/substrate/client/rpc-servers/src/middleware/metrics.rs +++ b/substrate/client/rpc-servers/src/middleware/metrics.rs @@ -18,15 +18,9 @@ //! RPC middleware to collect prometheus metrics on RPC calls. -use std::{ - future::Future, - pin::Pin, - task::{Context, Poll}, - time::Instant, -}; +use std::time::Instant; -use jsonrpsee::{server::middleware::rpc::RpcServiceT, types::Request, MethodResponse}; -use pin_project::pin_project; +use jsonrpsee::{types::Request, MethodResponse}; use prometheus_endpoint::{ register, Counter, CounterVec, HistogramOpts, HistogramVec, Opts, PrometheusError, Registry, U64, @@ -77,7 +71,7 @@ impl RpcMetrics { "Total time [μs] of processed RPC calls", ) .buckets(HISTOGRAM_BUCKETS.to_vec()), - &["protocol", "method"], + &["protocol", "method", "is_rate_limited"], )?, metrics_registry, )?, @@ -97,7 +91,7 @@ impl RpcMetrics { "substrate_rpc_calls_finished", "Number of processed RPC calls (unique un-batched requests)", ), - &["protocol", "method", "is_error"], + &["protocol", "method", "is_error", "is_rate_limited"], )?, metrics_registry, )?, @@ -144,138 +138,90 @@ impl RpcMetrics { self.ws_sessions_closed.as_ref().map(|counter| counter.inc()); self.ws_sessions_time.with_label_values(&["ws"]).observe(micros as _); } -} - -/// Metrics layer. -#[derive(Clone)] -pub struct MetricsLayer { - inner: RpcMetrics, - transport_label: &'static str, -} - -impl MetricsLayer { - /// Create a new [`MetricsLayer`]. - pub fn new(metrics: RpcMetrics, transport_label: &'static str) -> Self { - Self { inner: metrics, transport_label } - } - - pub(crate) fn ws_connect(&self) { - self.inner.ws_connect(); - } - - pub(crate) fn ws_disconnect(&self, now: Instant) { - self.inner.ws_disconnect(now) - } -} - -impl tower::Layer for MetricsLayer { - type Service = Metrics; - - fn layer(&self, inner: S) -> Self::Service { - Metrics::new(inner, self.inner.clone(), self.transport_label) - } -} - -/// Metrics middleware. -#[derive(Clone)] -pub struct Metrics { - service: S, - metrics: RpcMetrics, - transport_label: &'static str, -} - -impl Metrics { - /// Create a new metrics middleware. - pub fn new(service: S, metrics: RpcMetrics, transport_label: &'static str) -> Metrics { - Metrics { service, metrics, transport_label } - } -} - -impl<'a, S> RpcServiceT<'a> for Metrics -where - S: Send + Sync + RpcServiceT<'a>, -{ - type Future = ResponseFuture<'a, S::Future>; - - fn call(&self, req: Request<'a>) -> Self::Future { - let now = Instant::now(); + pub(crate) fn on_call(&self, req: &Request, transport_label: &'static str) { log::trace!( target: "rpc_metrics", - "[{}] on_call name={} params={:?}", - self.transport_label, + "[{transport_label}] on_call name={} params={:?}", req.method_name(), req.params(), ); - self.metrics - .calls_started - .with_label_values(&[self.transport_label, req.method_name()]) + + self.calls_started + .with_label_values(&[transport_label, req.method_name()]) .inc(); + } - ResponseFuture { - fut: self.service.call(req.clone()), - metrics: self.metrics.clone(), - req, - now, - transport_label: self.transport_label, - } + pub(crate) fn on_response( + &self, + req: &Request, + rp: &MethodResponse, + is_rate_limited: bool, + transport_label: &'static str, + now: Instant, + ) { + log::trace!(target: "rpc_metrics", "[{transport_label}] on_response started_at={:?}", now); + log::trace!(target: "rpc_metrics::extra", "[{transport_label}] result={}", rp.as_result()); + + let micros = now.elapsed().as_micros(); + log::debug!( + target: "rpc_metrics", + "[{transport_label}] {} call took {} μs", + req.method_name(), + micros, + ); + self.calls_time + .with_label_values(&[ + transport_label, + req.method_name(), + if is_rate_limited { "true" } else { "false" }, + ]) + .observe(micros as _); + self.calls_finished + .with_label_values(&[ + transport_label, + req.method_name(), + // the label "is_error", so `success` should be regarded as false + // and vice-versa to be registered correctly. + if rp.is_success() { "false" } else { "true" }, + if is_rate_limited { "true" } else { "false" }, + ]) + .inc(); } } -/// Response future for metrics. -#[pin_project] -pub struct ResponseFuture<'a, F> { - #[pin] - fut: F, - metrics: RpcMetrics, - req: Request<'a>, - now: Instant, - transport_label: &'static str, +/// Metrics with transport label. +#[derive(Clone, Debug)] +pub struct Metrics { + pub(crate) inner: RpcMetrics, + pub(crate) transport_label: &'static str, } -impl<'a, F> std::fmt::Debug for ResponseFuture<'a, F> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("ResponseFuture") +impl Metrics { + /// Create a new [`Metrics`]. + pub fn new(metrics: RpcMetrics, transport_label: &'static str) -> Self { + Self { inner: metrics, transport_label } } -} -impl<'a, F: Future> Future for ResponseFuture<'a, F> { - type Output = F::Output; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.project(); + pub(crate) fn ws_connect(&self) { + self.inner.ws_connect(); + } - let res = this.fut.poll(cx); - if let Poll::Ready(rp) = &res { - let method_name = this.req.method_name(); - let transport_label = &this.transport_label; - let now = this.now; - let metrics = &this.metrics; + pub(crate) fn ws_disconnect(&self, now: Instant) { + self.inner.ws_disconnect(now) + } - log::trace!(target: "rpc_metrics", "[{transport_label}] on_response started_at={:?}", now); - log::trace!(target: "rpc_metrics::extra", "[{transport_label}] result={:?}", rp); + pub(crate) fn on_call(&self, req: &Request) { + self.inner.on_call(req, self.transport_label) + } - let micros = now.elapsed().as_micros(); - log::debug!( - target: "rpc_metrics", - "[{transport_label}] {method_name} call took {} μs", - micros, - ); - metrics - .calls_time - .with_label_values(&[transport_label, method_name]) - .observe(micros as _); - metrics - .calls_finished - .with_label_values(&[ - transport_label, - method_name, - // the label "is_error", so `success` should be regarded as false - // and vice-versa to be registrered correctly. - if rp.is_success() { "false" } else { "true" }, - ]) - .inc(); - } - res + pub(crate) fn on_response( + &self, + req: &Request, + rp: &MethodResponse, + is_rate_limited: bool, + now: Instant, + ) { + self.inner.on_response(req, rp, is_rate_limited, self.transport_label, now) } } diff --git a/substrate/client/rpc-servers/src/middleware/mod.rs b/substrate/client/rpc-servers/src/middleware/mod.rs index cac516913d327f46ccf6ba1f81c7fb447873509b..88ed8b2f433580fa2d87f1a70eacc32019027f2a 100644 --- a/substrate/client/rpc-servers/src/middleware/mod.rs +++ b/substrate/client/rpc-servers/src/middleware/mod.rs @@ -18,10 +18,131 @@ //! JSON-RPC specific middleware. -/// Grafana metrics middleware. -pub mod metrics; -/// Rate limit middleware. -pub mod rate_limit; +use std::{ + num::NonZeroU32, + time::{Duration, Instant}, +}; + +use futures::future::{BoxFuture, FutureExt}; +use governor::{clock::Clock, Jitter}; +use jsonrpsee::{ + server::middleware::rpc::RpcServiceT, + types::{ErrorObject, Id, Request}, + MethodResponse, +}; + +mod metrics; +mod rate_limit; pub use metrics::*; pub use rate_limit::*; + +const MAX_JITTER: Duration = Duration::from_millis(50); +const MAX_RETRIES: usize = 10; + +/// JSON-RPC middleware layer. +#[derive(Debug, Clone, Default)] +pub struct MiddlewareLayer { + rate_limit: Option, + metrics: Option, +} + +impl MiddlewareLayer { + /// Create an empty MiddlewareLayer. + pub fn new() -> Self { + Self::default() + } + + /// Enable new rate limit middleware enforced per minute. + pub fn with_rate_limit_per_minute(self, n: NonZeroU32) -> Self { + Self { rate_limit: Some(RateLimit::per_minute(n)), metrics: self.metrics } + } + + /// Enable metrics middleware. + pub fn with_metrics(self, metrics: Metrics) -> Self { + Self { rate_limit: self.rate_limit, metrics: Some(metrics) } + } + + /// Register a new websocket connection. + pub fn ws_connect(&self) { + self.metrics.as_ref().map(|m| m.ws_connect()); + } + + /// Register that a websocket connection was closed. + pub fn ws_disconnect(&self, now: Instant) { + self.metrics.as_ref().map(|m| m.ws_disconnect(now)); + } +} + +impl tower::Layer for MiddlewareLayer { + type Service = Middleware; + + fn layer(&self, service: S) -> Self::Service { + Middleware { service, rate_limit: self.rate_limit.clone(), metrics: self.metrics.clone() } + } +} + +/// JSON-RPC middleware that handles metrics +/// and rate-limiting. +/// +/// These are part of the same middleware +/// because the metrics needs to know whether +/// a call was rate-limited or not because +/// it will impact the roundtrip for a call. +pub struct Middleware { + service: S, + rate_limit: Option, + metrics: Option, +} + +impl<'a, S> RpcServiceT<'a> for Middleware +where + S: Send + Sync + RpcServiceT<'a> + Clone + 'static, +{ + type Future = BoxFuture<'a, MethodResponse>; + + fn call(&self, req: Request<'a>) -> Self::Future { + let now = Instant::now(); + + self.metrics.as_ref().map(|m| m.on_call(&req)); + + let service = self.service.clone(); + let rate_limit = self.rate_limit.clone(); + let metrics = self.metrics.clone(); + + async move { + let mut is_rate_limited = false; + + if let Some(limit) = rate_limit.as_ref() { + let mut attempts = 0; + let jitter = Jitter::up_to(MAX_JITTER); + + loop { + if attempts >= MAX_RETRIES { + return reject_too_many_calls(req.id); + } + + if let Err(rejected) = limit.inner.check() { + tokio::time::sleep(jitter + rejected.wait_time_from(limit.clock.now())) + .await; + } else { + break; + } + + is_rate_limited = true; + attempts += 1; + } + } + + let rp = service.call(req.clone()).await; + metrics.as_ref().map(|m| m.on_response(&req, &rp, is_rate_limited, now)); + + rp + } + .boxed() + } +} + +fn reject_too_many_calls(id: Id) -> MethodResponse { + MethodResponse::error(id, ErrorObject::owned(-32999, "RPC rate limit exceeded", None::<()>)) +} diff --git a/substrate/client/rpc-servers/src/middleware/rate_limit.rs b/substrate/client/rpc-servers/src/middleware/rate_limit.rs index cdcc3ebf66f7d0ab9d2ba515b0c128b55281f590..23335eb37686c30d6bf148a2fd38f75013aef606 100644 --- a/substrate/client/rpc-servers/src/middleware/rate_limit.rs +++ b/substrate/client/rpc-servers/src/middleware/rate_limit.rs @@ -16,92 +16,32 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! RPC rate limiting middleware. +//! RPC rate limit. -use std::{num::NonZeroU32, sync::Arc, time::Duration}; - -use futures::future::{BoxFuture, FutureExt}; use governor::{ - clock::{Clock, DefaultClock, QuantaClock}, + clock::{DefaultClock, QuantaClock}, middleware::NoOpMiddleware, state::{InMemoryState, NotKeyed}, - Jitter, -}; -use jsonrpsee::{ - server::middleware::rpc::RpcServiceT, - types::{ErrorObject, Id, Request}, - MethodResponse, + Quota, }; +use std::{num::NonZeroU32, sync::Arc}; type RateLimitInner = governor::RateLimiter; -const MAX_JITTER: Duration = Duration::from_millis(50); -const MAX_RETRIES: usize = 10; - -/// JSON-RPC rate limit middleware layer. +/// Rate limit. #[derive(Debug, Clone)] -pub struct RateLimitLayer(governor::Quota); - -impl RateLimitLayer { - /// Create new rate limit enforced per minute. - pub fn per_minute(n: NonZeroU32) -> Self { - Self(governor::Quota::per_minute(n)) - } +pub struct RateLimit { + pub(crate) inner: Arc, + pub(crate) clock: QuantaClock, } -/// JSON-RPC rate limit middleware -pub struct RateLimit { - service: S, - rate_limit: Arc, - clock: QuantaClock, -} - -impl tower::Layer for RateLimitLayer { - type Service = RateLimit; - - fn layer(&self, service: S) -> Self::Service { +impl RateLimit { + /// Create a new `RateLimit` per minute. + pub fn per_minute(n: NonZeroU32) -> Self { let clock = QuantaClock::default(); - RateLimit { - service, - rate_limit: Arc::new(RateLimitInner::direct_with_clock(self.0, &clock)), + Self { + inner: Arc::new(RateLimitInner::direct_with_clock(Quota::per_minute(n), &clock)), clock, } } } - -impl<'a, S> RpcServiceT<'a> for RateLimit -where - S: Send + Sync + RpcServiceT<'a> + Clone + 'static, -{ - type Future = BoxFuture<'a, MethodResponse>; - - fn call(&self, req: Request<'a>) -> Self::Future { - let service = self.service.clone(); - let rate_limit = self.rate_limit.clone(); - let clock = self.clock.clone(); - - async move { - let mut attempts = 0; - let jitter = Jitter::up_to(MAX_JITTER); - - loop { - if attempts >= MAX_RETRIES { - break reject_too_many_calls(req.id); - } - - if let Err(rejected) = rate_limit.check() { - tokio::time::sleep(jitter + rejected.wait_time_from(clock.now())).await; - } else { - break service.call(req).await; - } - - attempts += 1; - } - } - .boxed() - } -} - -fn reject_too_many_calls(id: Id) -> MethodResponse { - MethodResponse::error(id, ErrorObject::owned(-32999, "RPC rate limit exceeded", None::<()>)) -} diff --git a/substrate/client/rpc-spec-v2/src/archive/tests.rs b/substrate/client/rpc-spec-v2/src/archive/tests.rs index 1803ffa3a3183628a8eff81a10c05efdddc5e962..de71ed82a128845d98ef5b6f96e0896de1a71ef1 100644 --- a/substrate/client/rpc-spec-v2/src/archive/tests.rs +++ b/substrate/client/rpc-spec-v2/src/archive/tests.rs @@ -435,7 +435,7 @@ async fn archive_storage_closest_merkle_value() { /// The core of this test. /// - /// Checks keys that are exact match, keys with descedant and keys that should not return + /// Checks keys that are exact match, keys with descendant and keys that should not return /// values. /// /// Returns (key, merkle value) pairs. @@ -459,7 +459,7 @@ async fn archive_storage_closest_merkle_value() { query_type: StorageQueryType::ClosestDescendantMerkleValue, pagination_start_key: None, }, - // Key with descedent. + // Key with descendant. PaginatedStorageQuery { key: hex_string(b":A"), query_type: StorageQueryType::ClosestDescendantMerkleValue, diff --git a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs index e94374aebd912b40074c194f656a1951fb097510..afa99f3aa1648823aee646304c7c2b2cd71da6e2 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs @@ -42,7 +42,14 @@ use sp_blockchain::{ Backend as BlockChainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata, Info, }; use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor}; -use std::{collections::HashSet, sync::Arc}; +use std::{ + collections::{HashSet, VecDeque}, + sync::Arc, +}; + +/// The maximum number of finalized blocks provided by the +/// `Initialized` event. +const MAX_FINALIZED_BLOCKS: usize = 16; use super::subscription::InsertedSubscriptionData; @@ -95,6 +102,8 @@ struct InitialBlocks { /// /// It is a tuple of (block hash, parent hash). finalized_block_descendants: Vec<(Block::Hash, Block::Hash)>, + /// Hashes of the last finalized blocks + finalized_block_hashes: VecDeque, /// Blocks that should not be reported as pruned by the `Finalized` event. /// /// Substrate database will perform the pruning of height N at @@ -178,13 +187,14 @@ where } /// Get the in-memory blocks of the client, starting from the provided finalized hash. + /// + /// The reported blocks are pinned by this function. fn get_init_blocks_with_forks( &self, - startup_point: &StartupPoint, + finalized: Block::Hash, ) -> Result, SubscriptionManagementError> { let blockchain = self.backend.blockchain(); let leaves = blockchain.leaves()?; - let finalized = startup_point.finalized_hash; let mut pruned_forks = HashSet::new(); let mut finalized_block_descendants = Vec::new(); let mut unique_descendants = HashSet::new(); @@ -198,17 +208,47 @@ where // Ensure a `NewBlock` event is generated for all children of the // finalized block. Describe the tree route as (child_node, parent_node) // Note: the order of elements matters here. - let parents = std::iter::once(finalized).chain(blocks.clone()); + let mut parent = finalized; + for child in blocks { + let pair = (child, parent); - for pair in blocks.zip(parents) { if unique_descendants.insert(pair) { + // The finalized block is pinned below. + self.sub_handle.pin_block(&self.sub_id, child)?; finalized_block_descendants.push(pair); } + + parent = child; } } } - Ok(InitialBlocks { finalized_block_descendants, pruned_forks }) + let mut current_block = finalized; + // The header of the finalized block must not be pruned. + let Some(header) = blockchain.header(current_block)? else { + return Err(SubscriptionManagementError::BlockHeaderAbsent); + }; + + // Report at most `MAX_FINALIZED_BLOCKS`. Note: The node might not have that many blocks. + let mut finalized_block_hashes = VecDeque::with_capacity(MAX_FINALIZED_BLOCKS); + + // Pin the finalized block. + self.sub_handle.pin_block(&self.sub_id, current_block)?; + finalized_block_hashes.push_front(current_block); + current_block = *header.parent_hash(); + + for _ in 0..MAX_FINALIZED_BLOCKS - 1 { + let Ok(Some(header)) = blockchain.header(current_block) else { break }; + // Block cannot be reported if pinning fails. + if self.sub_handle.pin_block(&self.sub_id, current_block).is_err() { + break + }; + + finalized_block_hashes.push_front(current_block); + current_block = *header.parent_hash(); + } + + Ok(InitialBlocks { finalized_block_descendants, finalized_block_hashes, pruned_forks }) } /// Generate the initial events reported by the RPC `follow` method. @@ -220,18 +260,17 @@ where startup_point: &StartupPoint, ) -> Result<(Vec>, HashSet), SubscriptionManagementError> { - let init = self.get_init_blocks_with_forks(startup_point)?; + let init = self.get_init_blocks_with_forks(startup_point.finalized_hash)?; + // The initialized event is the first one sent. let initial_blocks = init.finalized_block_descendants; + let finalized_block_hashes = init.finalized_block_hashes; - // The initialized event is the first one sent. let finalized_block_hash = startup_point.finalized_hash; - self.sub_handle.pin_block(&self.sub_id, finalized_block_hash)?; - let finalized_block_runtime = self.generate_runtime_event(finalized_block_hash, None); let initialized_event = FollowEvent::Initialized(Initialized { - finalized_block_hash, + finalized_block_hashes: finalized_block_hashes.into(), finalized_block_runtime, with_runtime: self.with_runtime, }); @@ -240,8 +279,6 @@ where finalized_block_descendants.push(initialized_event); for (child, parent) in initial_blocks.into_iter() { - self.sub_handle.pin_block(&self.sub_id, child)?; - let new_runtime = self.generate_runtime_event(child, Some(parent)); let event = FollowEvent::NewBlock(NewBlock { diff --git a/substrate/client/rpc-spec-v2/src/chain_head/event.rs b/substrate/client/rpc-spec-v2/src/chain_head/event.rs index 560ab87eab405968e9b7348bfd37792307b87a8f..bd9863060910542b1fdd2785d69fc9bb3472879b 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/event.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/event.rs @@ -111,8 +111,8 @@ impl From for RuntimeEvent { #[derive(Debug, Clone, PartialEq, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Initialized { - /// The hash of the latest finalized block. - pub finalized_block_hash: Hash, + /// The hash of the latest finalized blocks. + pub finalized_block_hashes: Vec, /// The runtime version of the finalized block. /// /// # Note @@ -135,12 +135,12 @@ impl Serialize for Initialized { { if self.with_runtime { let mut state = serializer.serialize_struct("Initialized", 2)?; - state.serialize_field("finalizedBlockHash", &self.finalized_block_hash)?; + state.serialize_field("finalizedBlockHashes", &self.finalized_block_hashes)?; state.serialize_field("finalizedBlockRuntime", &self.finalized_block_runtime)?; state.end() } else { let mut state = serializer.serialize_struct("Initialized", 1)?; - state.serialize_field("finalizedBlockHash", &self.finalized_block_hash)?; + state.serialize_field("finalizedBlockHashes", &self.finalized_block_hashes)?; state.end() } } @@ -315,7 +315,7 @@ pub enum FollowEvent { Stop, } -/// The method respose of `chainHead_body`, `chainHead_call` and `chainHead_storage`. +/// The method response of `chainHead_body`, `chainHead_call` and `chainHead_storage`. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] #[serde(tag = "result")] @@ -348,13 +348,13 @@ mod tests { fn follow_initialized_event_no_updates() { // Runtime flag is false. let event: FollowEvent = FollowEvent::Initialized(Initialized { - finalized_block_hash: "0x1".into(), + finalized_block_hashes: vec!["0x1".into()], finalized_block_runtime: None, with_runtime: false, }); let ser = serde_json::to_string(&event).unwrap(); - let exp = r#"{"event":"initialized","finalizedBlockHash":"0x1"}"#; + let exp = r#"{"event":"initialized","finalizedBlockHashes":["0x1"]}"#; assert_eq!(ser, exp); let event_dec: FollowEvent = serde_json::from_str(exp).unwrap(); @@ -373,7 +373,7 @@ mod tests { let runtime_event = RuntimeEvent::Valid(RuntimeVersionEvent { spec: runtime.into() }); let mut initialized = Initialized { - finalized_block_hash: "0x1".into(), + finalized_block_hashes: vec!["0x1".into()], finalized_block_runtime: Some(runtime_event), with_runtime: true, }; @@ -381,7 +381,7 @@ mod tests { let ser = serde_json::to_string(&event).unwrap(); let exp = concat!( - r#"{"event":"initialized","finalizedBlockHash":"0x1","#, + r#"{"event":"initialized","finalizedBlockHashes":["0x1"],"#, r#""finalizedBlockRuntime":{"type":"valid","spec":{"specName":"ABC","implName":"Impl","#, r#""specVersion":1,"implVersion":0,"apis":{},"transactionVersion":0}}}"#, ); diff --git a/substrate/client/rpc-spec-v2/src/chain_head/test_utils.rs b/substrate/client/rpc-spec-v2/src/chain_head/test_utils.rs index d63a98a5cb0d93b3633864bd3405ad38c2b6e799..e81bd4bfa0b04aa3ad9e25e47e83e9ace1c7f985 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/test_utils.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/test_utils.rs @@ -63,7 +63,7 @@ impl ChainHeadMockClient { BlockImportNotification::new(header.hash(), BlockOrigin::Own, header, true, None, sink); for sink in self.import_sinks.lock().iter_mut() { - sink.unbounded_send(notification.clone()).unwrap(); + let _ = sink.unbounded_send(notification.clone()); } } @@ -83,7 +83,7 @@ impl ChainHeadMockClient { let notification = FinalityNotification::from_summary(summary, sink); for sink in self.finality_sinks.lock().iter_mut() { - sink.unbounded_send(notification.clone()).unwrap(); + let _ = sink.unbounded_send(notification.clone()); } } } diff --git a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs index 9544736d84c8e634613d0a7e363e9398f1aa0b9d..30152efb5b623d664845453c64873dc45cdbb278 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -173,7 +173,7 @@ async fn follow_subscription_produces_blocks() { // Initialized must always be reported first. let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::Initialized(Initialized { - finalized_block_hash: format!("{:?}", finalized_hash), + finalized_block_hashes: vec![format!("{:?}", finalized_hash)], finalized_block_runtime: None, with_runtime: false, }); @@ -242,12 +242,12 @@ async fn follow_with_runtime() { let event: FollowEvent = get_next_event(&mut sub).await; // it is basically json-encoded substrate_test_runtime_client::runtime::VERSION - let runtime_str = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":0,\ - \"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",4],\ + let runtime_str = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\ + \"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",5],\ [\"0x37e397fc7c91f5e4\",2],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\ [\"0xbc9d89904f5b923f\",1],[\"0xc6e9a76309f39b09\",2],[\"0xdd718d5cc53262d4\",1],\ [\"0xcbca25e39f142387\",2],[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],\ - [\"0xed99c5acb25eedf5\",3],[\"0xfbc577b9d747efd6\",1]],\"transactionVersion\":1,\"stateVersion\":0}"; + [\"0xed99c5acb25eedf5\",3],[\"0xfbc577b9d747efd6\",1]],\"transactionVersion\":1,\"stateVersion\":1}"; let runtime: RuntimeVersion = serde_json::from_str(runtime_str).unwrap(); @@ -255,7 +255,7 @@ async fn follow_with_runtime() { Some(RuntimeEvent::Valid(RuntimeVersionEvent { spec: runtime.clone().into() })); // Runtime must always be reported with the first event. let expected = FollowEvent::Initialized(Initialized { - finalized_block_hash: format!("{:?}", finalized_hash), + finalized_block_hashes: vec![format!("{:?}", finalized_hash)], finalized_block_runtime, with_runtime: false, }); @@ -1344,7 +1344,7 @@ async fn follow_generates_initial_blocks() { // Initialized must always be reported first. let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::Initialized(Initialized { - finalized_block_hash: format!("{:?}", finalized_hash), + finalized_block_hashes: vec![format!("{:?}", finalized_hash)], finalized_block_runtime: None, with_runtime: false, }); @@ -1896,7 +1896,7 @@ async fn follow_prune_best_block() { // Initialized must always be reported first. let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::Initialized(Initialized { - finalized_block_hash: format!("{:?}", finalized_hash), + finalized_block_hashes: vec![format!("{:?}", finalized_hash)], finalized_block_runtime: None, with_runtime: false, }); @@ -2081,6 +2081,7 @@ async fn follow_forks_pruned_block() { // ^^^ finalized // -> block 1 -> block 2_f -> block 3_f // + let finalized_hash = client.info().finalized_hash; let block_1 = BlockBuilderBuilder::new(&*client) .on_parent_block(client.chain_info().genesis_hash) @@ -2090,6 +2091,7 @@ async fn follow_forks_pruned_block() { .build() .unwrap() .block; + let block_1_hash = block_1.header.hash(); client.import(BlockOrigin::Own, block_1.clone()).await.unwrap(); let block_2 = BlockBuilderBuilder::new(&*client) @@ -2100,6 +2102,7 @@ async fn follow_forks_pruned_block() { .build() .unwrap() .block; + let block_2_hash = block_2.header.hash(); client.import(BlockOrigin::Own, block_2.clone()).await.unwrap(); let block_3 = BlockBuilderBuilder::new(&*client) @@ -2156,7 +2159,12 @@ async fn follow_forks_pruned_block() { // Initialized must always be reported first. let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::Initialized(Initialized { - finalized_block_hash: format!("{:?}", block_3_hash), + finalized_block_hashes: vec![ + format!("{:?}", finalized_hash), + format!("{:?}", block_1_hash), + format!("{:?}", block_2_hash), + format!("{:?}", block_3_hash), + ], finalized_block_runtime: None, with_runtime: false, }); @@ -2310,7 +2318,7 @@ async fn follow_report_multiple_pruned_block() { // Initialized must always be reported first. let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::Initialized(Initialized { - finalized_block_hash: format!("{:?}", finalized_hash), + finalized_block_hashes: vec![format!("{:?}", finalized_hash)], finalized_block_runtime: None, with_runtime: false, }); @@ -2632,7 +2640,7 @@ async fn follow_finalized_before_new_block() { let finalized_hash = client.info().finalized_hash; let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::Initialized(Initialized { - finalized_block_hash: format!("{:?}", finalized_hash), + finalized_block_hashes: vec![format!("{:?}", finalized_hash)], finalized_block_runtime: None, with_runtime: false, }); @@ -2778,7 +2786,7 @@ async fn ensure_operation_limits_works() { FollowEvent::OperationStorageDone(done) if done.operation_id == operation_id ); - // The storage is finished and capactiy must be released. + // The storage is finished and capacity must be released. let alice_id = AccountKeyring::Alice.to_account_id(); // Hex encoded scale encoded bytes representing the call parameters. let call_parameters = hex_string(&alice_id.encode()); @@ -3105,7 +3113,7 @@ async fn storage_closest_merkle_value() { /// The core of this test. /// - /// Checks keys that are exact match, keys with descedant and keys that should not return + /// Checks keys that are exact match, keys with descendant and keys that should not return /// values. /// /// Returns (key, merkle value) pairs. @@ -3131,7 +3139,7 @@ async fn storage_closest_merkle_value() { key: hex_string(b":AAAB"), query_type: StorageQueryType::ClosestDescendantMerkleValue }, - // Key with descedent. + // Key with descendant. StorageQuery { key: hex_string(b":A"), query_type: StorageQueryType::ClosestDescendantMerkleValue diff --git a/substrate/client/rpc-spec-v2/src/transaction/event.rs b/substrate/client/rpc-spec-v2/src/transaction/event.rs index 8b80fcda17beb22cece35c928f10d72175fa9458..882ac8490b07c00432c4296964b7f995329df3dc 100644 --- a/substrate/client/rpc-spec-v2/src/transaction/event.rs +++ b/substrate/client/rpc-spec-v2/src/transaction/event.rs @@ -20,23 +20,6 @@ use serde::{Deserialize, Serialize}; -/// The transaction was broadcasted to a number of peers. -/// -/// # Note -/// -/// The RPC does not guarantee that the peers have received the -/// transaction. -/// -/// When the number of peers is zero, the event guarantees that -/// shutting down the local node will lead to the transaction -/// not being included in the chain. -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct TransactionBroadcasted { - /// The number of peers the transaction was broadcasted to. - pub num_peers: usize, -} - /// The transaction was included in a block of the chain. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -59,9 +42,6 @@ pub struct TransactionError { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TransactionDropped { - /// True if the transaction was broadcasted to other peers and - /// may still be included in the block. - pub broadcasted: bool, /// Reason of the event. pub error: String, } @@ -70,20 +50,17 @@ pub struct TransactionDropped { /// /// The status events can be grouped based on their kinds as: /// -/// 1. Runtime validated the transaction: +/// 1. Runtime validated the transaction and it entered the pool: /// - `Validated` /// -/// 2. Inside the `Ready` queue: -/// - `Broadcast` -/// -/// 3. Leaving the pool: +/// 2. Leaving the pool: /// - `BestChainBlockIncluded` /// - `Invalid` /// -/// 4. Block finalized: +/// 3. Block finalized: /// - `Finalized` /// -/// 5. At any time: +/// 4. At any time: /// - `Dropped` /// - `Error` /// @@ -101,8 +78,6 @@ pub struct TransactionDropped { pub enum TransactionEvent { /// The transaction was validated by the runtime. Validated, - /// The transaction was broadcasted to a number of peers. - Broadcasted(TransactionBroadcasted), /// The transaction was included in a best block of the chain. /// /// # Note @@ -159,7 +134,6 @@ enum TransactionEventBlockIR { #[serde(tag = "event")] enum TransactionEventNonBlockIR { Validated, - Broadcasted(TransactionBroadcasted), Error(TransactionError), Invalid(TransactionError), Dropped(TransactionDropped), @@ -186,8 +160,6 @@ impl From> for TransactionEventIR { match value { TransactionEvent::Validated => TransactionEventIR::NonBlock(TransactionEventNonBlockIR::Validated), - TransactionEvent::Broadcasted(event) => - TransactionEventIR::NonBlock(TransactionEventNonBlockIR::Broadcasted(event)), TransactionEvent::BestChainBlockIncluded(event) => TransactionEventIR::Block(TransactionEventBlockIR::BestChainBlockIncluded(event)), TransactionEvent::Finalized(event) => @@ -207,8 +179,6 @@ impl From> for TransactionEvent { match value { TransactionEventIR::NonBlock(status) => match status { TransactionEventNonBlockIR::Validated => TransactionEvent::Validated, - TransactionEventNonBlockIR::Broadcasted(event) => - TransactionEvent::Broadcasted(event), TransactionEventNonBlockIR::Error(event) => TransactionEvent::Error(event), TransactionEventNonBlockIR::Invalid(event) => TransactionEvent::Invalid(event), TransactionEventNonBlockIR::Dropped(event) => TransactionEvent::Dropped(event), @@ -239,19 +209,6 @@ mod tests { assert_eq!(event_dec, event); } - #[test] - fn broadcasted_event() { - let event: TransactionEvent<()> = - TransactionEvent::Broadcasted(TransactionBroadcasted { num_peers: 2 }); - let ser = serde_json::to_string(&event).unwrap(); - - let exp = r#"{"event":"broadcasted","numPeers":2}"#; - assert_eq!(ser, exp); - - let event_dec: TransactionEvent<()> = serde_json::from_str(exp).unwrap(); - assert_eq!(event_dec, event); - } - #[test] fn best_chain_event() { let event: TransactionEvent<()> = TransactionEvent::BestChainBlockIncluded(None); @@ -320,13 +277,11 @@ mod tests { #[test] fn dropped_event() { - let event: TransactionEvent<()> = TransactionEvent::Dropped(TransactionDropped { - broadcasted: true, - error: "abc".to_string(), - }); + let event: TransactionEvent<()> = + TransactionEvent::Dropped(TransactionDropped { error: "abc".to_string() }); let ser = serde_json::to_string(&event).unwrap(); - let exp = r#"{"event":"dropped","broadcasted":true,"error":"abc"}"#; + let exp = r#"{"event":"dropped","error":"abc"}"#; assert_eq!(ser, exp); let event_dec: TransactionEvent<()> = serde_json::from_str(exp).unwrap(); diff --git a/substrate/client/rpc-spec-v2/src/transaction/mod.rs b/substrate/client/rpc-spec-v2/src/transaction/mod.rs index 74268a5372a37a7f40d79e50d2ecf6659315d534..514ccf047dc28570c78543755577661a6a7791af 100644 --- a/substrate/client/rpc-spec-v2/src/transaction/mod.rs +++ b/substrate/client/rpc-spec-v2/src/transaction/mod.rs @@ -35,9 +35,6 @@ pub mod transaction; pub mod transaction_broadcast; pub use api::{TransactionApiServer, TransactionBroadcastApiServer}; -pub use event::{ - TransactionBlock, TransactionBroadcasted, TransactionDropped, TransactionError, - TransactionEvent, -}; +pub use event::{TransactionBlock, TransactionDropped, TransactionError, TransactionEvent}; pub use transaction::Transaction; pub use transaction_broadcast::TransactionBroadcast; diff --git a/substrate/client/rpc-spec-v2/src/transaction/tests.rs b/substrate/client/rpc-spec-v2/src/transaction/tests.rs deleted file mode 100644 index 382f5adeae19ebd7da366036b5092b0208b62568..0000000000000000000000000000000000000000 --- a/substrate/client/rpc-spec-v2/src/transaction/tests.rs +++ /dev/null @@ -1,238 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 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 . - -use super::*; -use crate::{ - chain_head::test_utils::ChainHeadMockClient, hex_string, - transaction::TransactionBroadcast as RpcTransactionBroadcast, -}; -use assert_matches::assert_matches; -use codec::Encode; -use futures::Future; -use jsonrpsee::{rpc_params, MethodsError as Error, RpcModule}; -use sc_transaction_pool::*; -use sc_transaction_pool_api::{ChainEvent, MaintainedTransactionPool, TransactionPool}; -use sp_core::{testing::TaskExecutor, traits::SpawnNamed}; -use std::{pin::Pin, sync::Arc, time::Duration}; -use substrate_test_runtime_client::{prelude::*, AccountKeyring::*, Client}; -use substrate_test_runtime_transaction_pool::{uxt, TestApi}; -use tokio::sync::mpsc; - -type Block = substrate_test_runtime_client::runtime::Block; - -/// Wrap the `TaskExecutor` to know when the broadcast future is dropped. -#[derive(Clone)] -struct TaskExecutorBroadcast { - executor: TaskExecutor, - sender: mpsc::UnboundedSender<()>, -} - -/// The channel that receives events when the broadcast futures are dropped. -type TaskExecutorRecv = mpsc::UnboundedReceiver<()>; - -impl TaskExecutorBroadcast { - /// Construct a new `TaskExecutorBroadcast` and a receiver to know when the broadcast futures - /// are dropped. - fn new() -> (Self, TaskExecutorRecv) { - let (sender, recv) = mpsc::unbounded_channel(); - - (Self { executor: TaskExecutor::new(), sender }, recv) - } -} - -impl SpawnNamed for TaskExecutorBroadcast { - fn spawn( - &self, - name: &'static str, - group: Option<&'static str>, - future: futures::future::BoxFuture<'static, ()>, - ) { - let sender = self.sender.clone(); - let future = Box::pin(async move { - future.await; - let _ = sender.send(()); - }); - - self.executor.spawn(name, group, future) - } - - fn spawn_blocking( - &self, - name: &'static str, - group: Option<&'static str>, - future: futures::future::BoxFuture<'static, ()>, - ) { - let sender = self.sender.clone(); - let future = Box::pin(async move { - future.await; - let _ = sender.send(()); - }); - - self.executor.spawn_blocking(name, group, future) - } -} - -/// Initial Alice account nonce. -const ALICE_NONCE: u64 = 209; - -fn create_basic_pool_with_genesis( - test_api: Arc, -) -> (BasicPool, Pin + Send>>) { - let genesis_hash = { - test_api - .chain() - .read() - .block_by_number - .get(&0) - .map(|blocks| blocks[0].0.header.hash()) - .expect("there is block 0. qed") - }; - BasicPool::new_test(test_api, genesis_hash, genesis_hash) -} - -fn maintained_pool() -> (BasicPool, Arc, futures::executor::ThreadPool) { - let api = Arc::new(TestApi::with_alice_nonce(ALICE_NONCE)); - let (pool, background_task) = create_basic_pool_with_genesis(api.clone()); - - let thread_pool = futures::executor::ThreadPool::new().unwrap(); - thread_pool.spawn_ok(background_task); - (pool, api, thread_pool) -} - -fn setup_api() -> ( - Arc, - Arc>, - Arc>>, - RpcModule< - TransactionBroadcast, ChainHeadMockClient>>, - >, - TaskExecutorRecv, -) { - let (pool, api, _) = maintained_pool(); - let pool = Arc::new(pool); - - let builder = TestClientBuilder::new(); - let client = Arc::new(builder.build()); - let client_mock = Arc::new(ChainHeadMockClient::new(client.clone())); - - let (task_executor, executor_recv) = TaskExecutorBroadcast::new(); - - let tx_api = - RpcTransactionBroadcast::new(client_mock.clone(), pool.clone(), Arc::new(task_executor)) - .into_rpc(); - - (api, pool, client_mock, tx_api, executor_recv) -} - -#[tokio::test] -async fn tx_broadcast_enters_pool() { - let (api, pool, client_mock, tx_api, _) = setup_api(); - - // Start at block 1. - let block_1_header = api.push_block(1, vec![], true); - - let uxt = uxt(Alice, ALICE_NONCE); - let xt = hex_string(&uxt.encode()); - - let operation_id: String = - tx_api.call("transaction_unstable_broadcast", rpc_params![&xt]).await.unwrap(); - - // Announce block 1 to `transaction_unstable_broadcast`. - client_mock.trigger_import_stream(block_1_header).await; - - // Ensure the tx propagated from `transaction_unstable_broadcast` to the transaction pool. - - // TODO: Improve testability by extending the `transaction_unstable_broadcast` with - // a middleware trait that intercepts the transaction status for testing. - let mut num_retries = 12; - while num_retries > 0 && pool.status().ready != 1 { - tokio::time::sleep(Duration::from_secs(5)).await; - num_retries -= 1; - } - assert_eq!(1, pool.status().ready); - assert_eq!(uxt.encode().len(), pool.status().ready_bytes); - - // Import block 2 with the transaction included. - let block_2_header = api.push_block(2, vec![uxt.clone()], true); - let block_2 = block_2_header.hash(); - - // Announce block 2 to the pool. - let event = ChainEvent::NewBestBlock { hash: block_2, tree_route: None }; - pool.maintain(event).await; - - assert_eq!(0, pool.status().ready); - - // Stop call can still be made. - let _: () = tx_api - .call("transaction_unstable_stop", rpc_params![&operation_id]) - .await - .unwrap(); -} - -#[tokio::test] -async fn tx_broadcast_invalid_tx() { - let (_, pool, _, tx_api, mut exec_recv) = setup_api(); - - // Invalid parameters. - let err = tx_api - .call::<_, serde_json::Value>("transaction_unstable_broadcast", [1u8]) - .await - .unwrap_err(); - assert_matches!(err, - Error::JsonRpc(err) if err.code() == super::error::json_rpc_spec::INVALID_PARAM_ERROR && err.message() == "Invalid params" - ); - - assert_eq!(0, pool.status().ready); - - // Invalid transaction that cannot be decoded. The broadcast silently exits. - let xt = "0xdeadbeef"; - let operation_id: String = - tx_api.call("transaction_unstable_broadcast", rpc_params![&xt]).await.unwrap(); - - assert_eq!(0, pool.status().ready); - - // Await the broadcast future to exit. - // Without this we'd be subject to races, where we try to call the stop before the tx is - // dropped. - exec_recv.recv().await.unwrap(); - - // The broadcast future was dropped, and the operation is no longer active. - // When the operation is not active, either from the tx being finalized or a - // terminal error; the stop method should return an error. - let err = tx_api - .call::<_, serde_json::Value>("transaction_unstable_stop", rpc_params![&operation_id]) - .await - .unwrap_err(); - assert_matches!(err, - Error::JsonRpc(err) if err.code() == super::error::json_rpc_spec::INVALID_PARAM_ERROR && err.message() == "Invalid operation id" - ); -} - -#[tokio::test] -async fn tx_invalid_stop() { - let (_, _, _, tx_api, _) = setup_api(); - - // Make an invalid stop call. - let err = tx_api - .call::<_, serde_json::Value>("transaction_unstable_stop", ["invalid_operation_id"]) - .await - .unwrap_err(); - assert_matches!(err, - Error::JsonRpc(err) if err.code() == super::error::json_rpc_spec::INVALID_PARAM_ERROR && err.message() == "Invalid operation id" - ); -} diff --git a/substrate/client/rpc-spec-v2/src/transaction/tests/executor.rs b/substrate/client/rpc-spec-v2/src/transaction/tests/executor.rs new file mode 100644 index 0000000000000000000000000000000000000000..ff9aca79887c3f29025697f9f5832c914735e0d6 --- /dev/null +++ b/substrate/client/rpc-spec-v2/src/transaction/tests/executor.rs @@ -0,0 +1,100 @@ +// This file is part of Substrate. + +// Copyright (C) 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 . + +use sp_core::{testing::TaskExecutor, traits::SpawnNamed}; +use std::sync::{atomic::AtomicUsize, Arc}; +use tokio::sync::mpsc; + +/// Wrap the `TaskExecutor` to know when the broadcast future is dropped. +#[derive(Clone)] +pub struct TaskExecutorBroadcast { + executor: TaskExecutor, + sender: mpsc::UnboundedSender<()>, + num_tasks: Arc, +} + +/// The channel that receives events when the broadcast futures are dropped. +pub type TaskExecutorRecv = mpsc::UnboundedReceiver<()>; + +/// The state of the `TaskExecutorBroadcast`. +pub struct TaskExecutorState { + pub recv: TaskExecutorRecv, + pub num_tasks: Arc, +} + +impl TaskExecutorState { + pub fn num_tasks(&self) -> usize { + self.num_tasks.load(std::sync::atomic::Ordering::Acquire) + } +} + +impl TaskExecutorBroadcast { + /// Construct a new `TaskExecutorBroadcast` and a receiver to know when the broadcast futures + /// are dropped. + pub fn new() -> (Self, TaskExecutorState) { + let (sender, recv) = mpsc::unbounded_channel(); + let num_tasks = Arc::new(AtomicUsize::new(0)); + + ( + Self { executor: TaskExecutor::new(), sender, num_tasks: num_tasks.clone() }, + TaskExecutorState { recv, num_tasks }, + ) + } +} + +impl SpawnNamed for TaskExecutorBroadcast { + fn spawn( + &self, + name: &'static str, + group: Option<&'static str>, + future: futures::future::BoxFuture<'static, ()>, + ) { + let sender = self.sender.clone(); + let num_tasks = self.num_tasks.clone(); + + let future = Box::pin(async move { + num_tasks.fetch_add(1, std::sync::atomic::Ordering::AcqRel); + future.await; + num_tasks.fetch_sub(1, std::sync::atomic::Ordering::AcqRel); + + let _ = sender.send(()); + }); + + self.executor.spawn(name, group, future) + } + + fn spawn_blocking( + &self, + name: &'static str, + group: Option<&'static str>, + future: futures::future::BoxFuture<'static, ()>, + ) { + let sender = self.sender.clone(); + let num_tasks = self.num_tasks.clone(); + + let future = Box::pin(async move { + num_tasks.fetch_add(1, std::sync::atomic::Ordering::AcqRel); + future.await; + num_tasks.fetch_sub(1, std::sync::atomic::Ordering::AcqRel); + + let _ = sender.send(()); + }); + + self.executor.spawn_blocking(name, group, future) + } +} diff --git a/substrate/client/rpc-spec-v2/src/transaction/tests/middleware_pool.rs b/substrate/client/rpc-spec-v2/src/transaction/tests/middleware_pool.rs new file mode 100644 index 0000000000000000000000000000000000000000..aa8ac572dec9d8d6de9212fcb94c97ce2b804da4 --- /dev/null +++ b/substrate/client/rpc-spec-v2/src/transaction/tests/middleware_pool.rs @@ -0,0 +1,187 @@ +// This file is part of Substrate. + +// Copyright (C) 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 . + +use codec::Encode; +use futures::Future; +use sc_transaction_pool::BasicPool; +use sc_transaction_pool_api::{ + ImportNotificationStream, PoolFuture, PoolStatus, ReadyTransactions, TransactionFor, + TransactionPool, TransactionSource, TransactionStatusStreamFor, TxHash, +}; + +use crate::hex_string; +use futures::{FutureExt, StreamExt}; + +use sp_runtime::traits::{Block as BlockT, NumberFor}; +use std::{collections::HashMap, pin::Pin, sync::Arc}; +use substrate_test_runtime_transaction_pool::TestApi; +use tokio::sync::mpsc; + +pub type Block = substrate_test_runtime_client::runtime::Block; + +pub type TxTestPool = MiddlewarePool; +pub type TxStatusType = sc_transaction_pool_api::TransactionStatus< + sc_transaction_pool_api::TxHash, + sc_transaction_pool_api::BlockHash, +>; +pub type TxStatusTypeTest = TxStatusType; + +/// The type of the event that the middleware captures. +#[derive(Debug, PartialEq)] +pub enum MiddlewarePoolEvent { + TransactionStatus { + transaction: String, + status: sc_transaction_pool_api::TransactionStatus< + ::Hash, + ::Hash, + >, + }, + PoolError { + transaction: String, + err: String, + }, +} + +/// The channel that receives events when the broadcast futures are dropped. +pub type MiddlewarePoolRecv = mpsc::UnboundedReceiver; + +/// Add a middleware to the transaction pool. +/// +/// This wraps the `submit_and_watch` to gain access to the events. +pub struct MiddlewarePool { + pub inner_pool: Arc>, + /// Send the middleware events to the test. + sender: mpsc::UnboundedSender, +} + +impl MiddlewarePool { + /// Construct a new [`MiddlewarePool`]. + pub fn new(pool: Arc>) -> (Self, MiddlewarePoolRecv) { + let (sender, recv) = mpsc::unbounded_channel(); + (MiddlewarePool { inner_pool: pool, sender }, recv) + } +} + +impl TransactionPool for MiddlewarePool { + type Block = as TransactionPool>::Block; + type Hash = as TransactionPool>::Hash; + type InPoolTransaction = as TransactionPool>::InPoolTransaction; + type Error = as TransactionPool>::Error; + + fn submit_at( + &self, + at: ::Hash, + source: TransactionSource, + xts: Vec>, + ) -> PoolFuture, Self::Error>>, Self::Error> { + self.inner_pool.submit_at(at, source, xts) + } + + fn submit_one( + &self, + at: ::Hash, + source: TransactionSource, + xt: TransactionFor, + ) -> PoolFuture, Self::Error> { + self.inner_pool.submit_one(at, source, xt) + } + + fn submit_and_watch( + &self, + at: ::Hash, + source: TransactionSource, + xt: TransactionFor, + ) -> PoolFuture>>, Self::Error> { + let pool = self.inner_pool.clone(); + let sender = self.sender.clone(); + let transaction = hex_string(&xt.encode()); + + async move { + let watcher = match pool.submit_and_watch(at, source, xt).await { + Ok(watcher) => watcher, + Err(err) => { + let _ = sender.send(MiddlewarePoolEvent::PoolError { + transaction: transaction.clone(), + err: err.to_string(), + }); + return Err(err); + }, + }; + + let watcher = watcher.map(move |status| { + let sender = sender.clone(); + let transaction = transaction.clone(); + + let _ = sender.send(MiddlewarePoolEvent::TransactionStatus { + transaction, + status: status.clone(), + }); + + status + }); + + Ok(watcher.boxed()) + } + .boxed() + } + + fn remove_invalid(&self, hashes: &[TxHash]) -> Vec> { + self.inner_pool.remove_invalid(hashes) + } + + fn status(&self) -> PoolStatus { + self.inner_pool.status() + } + + fn import_notification_stream(&self) -> ImportNotificationStream> { + self.inner_pool.import_notification_stream() + } + + fn hash_of(&self, xt: &TransactionFor) -> TxHash { + self.inner_pool.hash_of(xt) + } + + fn on_broadcasted(&self, propagations: HashMap, Vec>) { + self.inner_pool.on_broadcasted(propagations) + } + + fn ready_transaction(&self, hash: &TxHash) -> Option> { + self.inner_pool.ready_transaction(hash) + } + + fn ready_at( + &self, + at: NumberFor, + ) -> Pin< + Box< + dyn Future< + Output = Box> + Send>, + > + Send, + >, + > { + self.inner_pool.ready_at(at) + } + + fn ready(&self) -> Box> + Send> { + self.inner_pool.ready() + } + + fn futures(&self) -> Vec { + self.inner_pool.futures() + } +} diff --git a/substrate/client/rpc-spec-v2/src/transaction/tests/mod.rs b/substrate/client/rpc-spec-v2/src/transaction/tests/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..ab0caaf906fd08049ccf6c9f421d3134d3689322 --- /dev/null +++ b/substrate/client/rpc-spec-v2/src/transaction/tests/mod.rs @@ -0,0 +1,24 @@ +// This file is part of Substrate. + +// Copyright (C) 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 . + +mod executor; +mod middleware_pool; +#[macro_use] +mod setup; + +mod transaction_broadcast_tests; diff --git a/substrate/client/rpc-spec-v2/src/transaction/tests/setup.rs b/substrate/client/rpc-spec-v2/src/transaction/tests/setup.rs new file mode 100644 index 0000000000000000000000000000000000000000..04ee7b9b4c94c4f3eac763ad06ba9b8964c2287e --- /dev/null +++ b/substrate/client/rpc-spec-v2/src/transaction/tests/setup.rs @@ -0,0 +1,120 @@ +// This file is part of Substrate. + +// Copyright (C) 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 . + +use crate::{ + chain_head::test_utils::ChainHeadMockClient, + transaction::{ + api::TransactionBroadcastApiServer, + tests::executor::{TaskExecutorBroadcast, TaskExecutorState}, + TransactionBroadcast as RpcTransactionBroadcast, + }, +}; +use futures::Future; +use jsonrpsee::RpcModule; +use sc_transaction_pool::*; +use std::{pin::Pin, sync::Arc}; +use substrate_test_runtime_client::{prelude::*, Client}; +use substrate_test_runtime_transaction_pool::TestApi; + +use crate::transaction::tests::middleware_pool::{MiddlewarePool, MiddlewarePoolRecv}; + +pub type Block = substrate_test_runtime_client::runtime::Block; + +/// Initial Alice account nonce. +pub const ALICE_NONCE: u64 = 209; + +fn create_basic_pool_with_genesis( + test_api: Arc, + options: Options, +) -> (BasicPool, Pin + Send>>) { + let genesis_hash = { + test_api + .chain() + .read() + .block_by_number + .get(&0) + .map(|blocks| blocks[0].0.header.hash()) + .expect("there is block 0. qed") + }; + BasicPool::new_test(test_api, genesis_hash, genesis_hash, options) +} + +fn maintained_pool( + options: Options, +) -> (BasicPool, Arc, futures::executor::ThreadPool) { + let api = Arc::new(TestApi::with_alice_nonce(ALICE_NONCE)); + let (pool, background_task) = create_basic_pool_with_genesis(api.clone(), options); + + let thread_pool = futures::executor::ThreadPool::new().unwrap(); + thread_pool.spawn_ok(background_task); + (pool, api, thread_pool) +} + +pub fn setup_api( + options: Options, +) -> ( + Arc, + Arc, + Arc>>, + RpcModule>>>, + TaskExecutorState, + MiddlewarePoolRecv, +) { + let (pool, api, _) = maintained_pool(options); + let (pool, pool_state) = MiddlewarePool::new(Arc::new(pool).clone()); + let pool = Arc::new(pool); + + let builder = TestClientBuilder::new(); + let client = Arc::new(builder.build()); + let client_mock = Arc::new(ChainHeadMockClient::new(client.clone())); + + let (task_executor, executor_recv) = TaskExecutorBroadcast::new(); + + let tx_api = + RpcTransactionBroadcast::new(client_mock.clone(), pool.clone(), Arc::new(task_executor)) + .into_rpc(); + + (api, pool, client_mock, tx_api, executor_recv, pool_state) +} + +/// Get the next event from the provided middleware in at most 5 seconds. +macro_rules! get_next_event { + ($middleware:expr) => { + tokio::time::timeout(std::time::Duration::from_secs(5), $middleware.recv()) + .await + .unwrap() + .unwrap() + }; +} + +/// Collect the next number of transaction events from the provided middleware. +macro_rules! get_next_tx_events { + ($middleware:expr, $num:expr) => {{ + let mut events = std::collections::HashMap::new(); + for _ in 0..$num { + let event = get_next_event!($middleware); + match event { + crate::transaction::tests::middleware_pool::MiddlewarePoolEvent::TransactionStatus { transaction, status } => { + events.entry(transaction).or_insert_with(|| vec![]).push(status); + }, + other => panic!("Expected TransactionStatus, received {:?}", other), + }; + } + events + }}; +} diff --git a/substrate/client/rpc-spec-v2/src/transaction/tests/transaction_broadcast_tests.rs b/substrate/client/rpc-spec-v2/src/transaction/tests/transaction_broadcast_tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..77a28968aedf8cfeefc7964ca29b972a856d618e --- /dev/null +++ b/substrate/client/rpc-spec-v2/src/transaction/tests/transaction_broadcast_tests.rs @@ -0,0 +1,523 @@ +// This file is part of Substrate. + +// Copyright (C) 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 . + +use crate::{hex_string, transaction::error::json_rpc_spec}; +use assert_matches::assert_matches; +use codec::Encode; +use jsonrpsee::{rpc_params, MethodsError as Error}; +use sc_transaction_pool::{Options, PoolLimit}; +use sc_transaction_pool_api::{ChainEvent, MaintainedTransactionPool, TransactionPool}; +use std::sync::Arc; +use substrate_test_runtime_client::AccountKeyring::*; +use substrate_test_runtime_transaction_pool::uxt; + +// Test helpers. +use crate::transaction::tests::{ + middleware_pool::{MiddlewarePoolEvent, TxStatusTypeTest}, + setup::{setup_api, ALICE_NONCE}, +}; + +#[tokio::test] +async fn tx_broadcast_enters_pool() { + let (api, pool, client_mock, tx_api, mut exec_middleware, mut pool_middleware) = + setup_api(Default::default()); + + // Start at block 1. + let block_1_header = api.push_block(1, vec![], true); + + let uxt = uxt(Alice, ALICE_NONCE); + let xt = hex_string(&uxt.encode()); + + let operation_id: String = + tx_api.call("transaction_unstable_broadcast", rpc_params![&xt]).await.unwrap(); + + // Announce block 1 to `transaction_unstable_broadcast`. + client_mock.trigger_import_stream(block_1_header).await; + + // Ensure the tx propagated from `transaction_unstable_broadcast` to the transaction pool. + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::Ready + } + ); + + assert_eq!(1, pool.inner_pool.status().ready); + assert_eq!(uxt.encode().len(), pool.inner_pool.status().ready_bytes); + + // Import block 2 with the transaction included. + let block_2_header = api.push_block(2, vec![uxt.clone()], true); + let block_2 = block_2_header.hash(); + + // Announce block 2 to the pool. + let event = ChainEvent::NewBestBlock { hash: block_2, tree_route: None }; + pool.inner_pool.maintain(event).await; + assert_eq!(0, pool.inner_pool.status().ready); + + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::InBlock((block_2, 0)) + } + ); + + // The future broadcast awaits for the finalized status to be reached. + // Force the future to exit by calling stop. + let _: () = tx_api + .call("transaction_unstable_stop", rpc_params![&operation_id]) + .await + .unwrap(); + + // Ensure the broadcast future finishes. + let _ = get_next_event!(&mut exec_middleware.recv); + assert_eq!(0, exec_middleware.num_tasks()); +} + +#[tokio::test] +async fn tx_broadcast_invalid_tx() { + let (_, pool, _, tx_api, mut exec_middleware, _) = setup_api(Default::default()); + + // Invalid parameters. + let err = tx_api + .call::<_, serde_json::Value>("transaction_unstable_broadcast", [1u8]) + .await + .unwrap_err(); + assert_matches!(err, + Error::JsonRpc(err) if err.code() == json_rpc_spec::INVALID_PARAM_ERROR && err.message() == "Invalid params" + ); + + assert_eq!(0, pool.status().ready); + + // Invalid transaction that cannot be decoded. The broadcast silently exits. + let xt = "0xdeadbeef"; + let operation_id: String = + tx_api.call("transaction_unstable_broadcast", rpc_params![&xt]).await.unwrap(); + + assert_eq!(0, pool.status().ready); + + // Await the broadcast future to exit. + // Without this we'd be subject to races, where we try to call the stop before the tx is + // dropped. + let _ = get_next_event!(&mut exec_middleware.recv); + assert_eq!(0, exec_middleware.num_tasks()); + + // The broadcast future was dropped, and the operation is no longer active. + // When the operation is not active, either from the tx being finalized or a + // terminal error; the stop method should return an error. + let err = tx_api + .call::<_, serde_json::Value>("transaction_unstable_stop", rpc_params![&operation_id]) + .await + .unwrap_err(); + assert_matches!(err, + Error::JsonRpc(err) if err.code() == json_rpc_spec::INVALID_PARAM_ERROR && err.message() == "Invalid operation id" + ); +} + +#[tokio::test] +async fn tx_stop_with_invalid_operation_id() { + let (_, _, _, tx_api, _, _) = setup_api(Default::default()); + + // Make an invalid stop call. + let err = tx_api + .call::<_, serde_json::Value>("transaction_unstable_stop", ["invalid_operation_id"]) + .await + .unwrap_err(); + assert_matches!(err, + Error::JsonRpc(err) if err.code() == json_rpc_spec::INVALID_PARAM_ERROR && err.message() == "Invalid operation id" + ); +} + +#[tokio::test] +async fn tx_broadcast_resubmits_future_nonce_tx() { + let (api, pool, client_mock, tx_api, mut exec_middleware, mut pool_middleware) = + setup_api(Default::default()); + + // Start at block 1. + let block_1_header = api.push_block(1, vec![], true); + let block_1 = block_1_header.hash(); + + let current_uxt = uxt(Alice, ALICE_NONCE); + let current_xt = hex_string(¤t_uxt.encode()); + // This lives in the future. + let future_uxt = uxt(Alice, ALICE_NONCE + 1); + let future_xt = hex_string(&future_uxt.encode()); + + let future_operation_id: String = tx_api + .call("transaction_unstable_broadcast", rpc_params![&future_xt]) + .await + .unwrap(); + + // Announce block 1 to `transaction_unstable_broadcast`. + client_mock.trigger_import_stream(block_1_header).await; + + // Ensure the tx propagated from `transaction_unstable_broadcast` to the transaction pool. + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: future_xt.clone(), + status: TxStatusTypeTest::Future + } + ); + + let event = ChainEvent::NewBestBlock { hash: block_1, tree_route: None }; + pool.inner_pool.maintain(event).await; + assert_eq!(0, pool.inner_pool.status().ready); + // Ensure the tx is in the future. + assert_eq!(1, pool.inner_pool.status().future); + + let block_2_header = api.push_block(2, vec![], true); + let block_2 = block_2_header.hash(); + + let operation_id: String = tx_api + .call("transaction_unstable_broadcast", rpc_params![¤t_xt]) + .await + .unwrap(); + assert_ne!(future_operation_id, operation_id); + + // Announce block 2 to `transaction_unstable_broadcast`. + client_mock.trigger_import_stream(block_2_header).await; + + // Collect the events of both transactions. + let events = get_next_tx_events!(&mut pool_middleware, 2); + // Transactions entered the ready queue. + assert_eq!(events.get(¤t_xt).unwrap(), &vec![TxStatusTypeTest::Ready]); + assert_eq!(events.get(&future_xt).unwrap(), &vec![TxStatusTypeTest::Ready]); + + let event = ChainEvent::NewBestBlock { hash: block_2, tree_route: None }; + pool.inner_pool.maintain(event).await; + assert_eq!(2, pool.inner_pool.status().ready); + assert_eq!(0, pool.inner_pool.status().future); + + // Finalize transactions. + let block_3_header = api.push_block(3, vec![current_uxt, future_uxt], true); + let block_3 = block_3_header.hash(); + client_mock.trigger_import_stream(block_3_header).await; + + let event = ChainEvent::Finalized { hash: block_3, tree_route: Arc::from(vec![]) }; + pool.inner_pool.maintain(event).await; + assert_eq!(0, pool.inner_pool.status().ready); + assert_eq!(0, pool.inner_pool.status().future); + + let events = get_next_tx_events!(&mut pool_middleware, 4); + assert_eq!( + events.get(¤t_xt).unwrap(), + &vec![TxStatusTypeTest::InBlock((block_3, 0)), TxStatusTypeTest::Finalized((block_3, 0))] + ); + assert_eq!( + events.get(&future_xt).unwrap(), + &vec![TxStatusTypeTest::InBlock((block_3, 1)), TxStatusTypeTest::Finalized((block_3, 1))] + ); + + // Both broadcast futures must exit. + let _ = get_next_event!(&mut exec_middleware.recv); + let _ = get_next_event!(&mut exec_middleware.recv); + assert_eq!(0, exec_middleware.num_tasks()); +} + +/// This test is similar to `tx_broadcast_enters_pool` +/// However the last block is announced as finalized to force the +/// broadcast future to exit before the `stop` is called. +#[tokio::test] +async fn tx_broadcast_stop_after_broadcast_finishes() { + let (api, pool, client_mock, tx_api, mut exec_middleware, mut pool_middleware) = + setup_api(Default::default()); + + // Start at block 1. + let block_1_header = api.push_block(1, vec![], true); + + let uxt = uxt(Alice, ALICE_NONCE); + let xt = hex_string(&uxt.encode()); + + let operation_id: String = + tx_api.call("transaction_unstable_broadcast", rpc_params![&xt]).await.unwrap(); + + // Announce block 1 to `transaction_unstable_broadcast`. + client_mock.trigger_import_stream(block_1_header).await; + + // Ensure the tx propagated from `transaction_unstable_broadcast` to the transaction + // pool.inner_pool. + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::Ready + } + ); + + assert_eq!(1, pool.inner_pool.status().ready); + assert_eq!(uxt.encode().len(), pool.inner_pool.status().ready_bytes); + + // Import block 2 with the transaction included. + let block_2_header = api.push_block(2, vec![uxt.clone()], true); + let block_2 = block_2_header.hash(); + + // Announce block 2 to the pool.inner_pool. + let event = ChainEvent::Finalized { hash: block_2, tree_route: Arc::from(vec![]) }; + pool.inner_pool.maintain(event).await; + + assert_eq!(0, pool.inner_pool.status().ready); + + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::InBlock((block_2, 0)) + } + ); + + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::Finalized((block_2, 0)) + } + ); + + // Ensure the broadcast future terminated properly. + let _ = get_next_event!(&mut exec_middleware.recv); + assert_eq!(0, exec_middleware.num_tasks()); + + // The operation ID is no longer valid, check that the broadcast future + // cleared out the inner state of the operation. + let err = tx_api + .call::<_, serde_json::Value>("transaction_unstable_stop", rpc_params![&operation_id]) + .await + .unwrap_err(); + assert_matches!(err, + Error::JsonRpc(err) if err.code() == json_rpc_spec::INVALID_PARAM_ERROR && err.message() == "Invalid operation id" + ); +} + +#[tokio::test] +async fn tx_broadcast_resubmits_invalid_tx() { + let limits = PoolLimit { count: 8192, total_bytes: 20 * 1024 * 1024 }; + let options = Options { + ready: limits.clone(), + future: limits, + reject_future_transactions: false, + // This ensures that a transaction is not banned. + ban_time: std::time::Duration::ZERO, + }; + + let (api, pool, client_mock, tx_api, mut exec_middleware, mut pool_middleware) = + setup_api(options); + + let uxt = uxt(Alice, ALICE_NONCE); + let xt = hex_string(&uxt.encode()); + let _operation_id: String = + tx_api.call("transaction_unstable_broadcast", rpc_params![&xt]).await.unwrap(); + + let block_1_header = api.push_block(1, vec![], true); + let block_1 = block_1_header.hash(); + // Announce block 1 to `transaction_unstable_broadcast`. + client_mock.trigger_import_stream(block_1_header).await; + + // Ensure the tx propagated from `transaction_unstable_broadcast` to the transaction pool. + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::Ready, + } + ); + assert_eq!(1, pool.inner_pool.status().ready); + assert_eq!(uxt.encode().len(), pool.inner_pool.status().ready_bytes); + + // Mark the transaction as invalid from the API, causing a temporary ban. + api.add_invalid(&uxt); + + // Push an event to the pool to ensure the transaction is excluded. + let event = ChainEvent::NewBestBlock { hash: block_1, tree_route: None }; + pool.inner_pool.maintain(event).await; + assert_eq!(1, pool.inner_pool.status().ready); + + // Ensure the `transaction_unstable_broadcast` is aware of the invalid transaction. + let event = get_next_event!(&mut pool_middleware); + // Because we have received an `Invalid` status, we try to broadcast the transaction with the + // next announced block. + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::Invalid + } + ); + + // Import block 2. + let block_2_header = api.push_block(2, vec![], true); + client_mock.trigger_import_stream(block_2_header).await; + + // Ensure we propagate the temporary ban error to `submit_and_watch`. + // This ensures we'll loop again with the next announced block and try to resubmit the + // transaction. The transaction remains temporarily banned until the pool is maintained. + let event = get_next_event!(&mut pool_middleware); + assert_matches!(event, MiddlewarePoolEvent::PoolError { transaction, err } if transaction == xt && err.contains("Transaction temporarily Banned")); + + // Import block 3. + let block_3_header = api.push_block(3, vec![], true); + let block_3 = block_3_header.hash(); + // Remove the invalid transaction from the pool to allow it to pass through. + api.remove_invalid(&uxt); + let event = ChainEvent::NewBestBlock { hash: block_3, tree_route: None }; + // We have to maintain the pool to ensure the transaction is no longer invalid. + // This clears out the banned transactions. + pool.inner_pool.maintain(event).await; + assert_eq!(0, pool.inner_pool.status().ready); + + // Announce block to `transaction_unstable_broadcast`. + client_mock.trigger_import_stream(block_3_header).await; + + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::Ready, + } + ); + assert_eq!(1, pool.inner_pool.status().ready); + + let block_4_header = api.push_block(4, vec![uxt], true); + let block_4 = block_4_header.hash(); + let event = ChainEvent::Finalized { hash: block_4, tree_route: Arc::from(vec![]) }; + pool.inner_pool.maintain(event).await; + + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::InBlock((block_4, 0)), + } + ); + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::Finalized((block_4, 0)), + } + ); + + // Ensure the broadcast future terminated properly. + let _ = get_next_event!(&mut exec_middleware.recv); + assert_eq!(0, exec_middleware.num_tasks()); +} + +/// This is similar to `tx_broadcast_resubmits_invalid_tx`. +/// However, it forces the tx to be resubmitted because of the pool +/// limits. Which is a different code path than the invalid tx. +#[tokio::test] +async fn tx_broadcast_resubmits_dropped_tx() { + let limits = PoolLimit { count: 1, total_bytes: 1000 }; + let options = Options { + ready: limits.clone(), + future: limits, + reject_future_transactions: false, + // This ensures that a transaction is not banned. + ban_time: std::time::Duration::ZERO, + }; + + let (api, pool, client_mock, tx_api, _, mut pool_middleware) = setup_api(options); + + let current_uxt = uxt(Alice, ALICE_NONCE); + let current_xt = hex_string(¤t_uxt.encode()); + // This lives in the future. + let future_uxt = uxt(Alice, ALICE_NONCE + 1); + let future_xt = hex_string(&future_uxt.encode()); + + // By default the `validate_transaction` mock uses priority 1 for + // transactions. Bump the priority to ensure other transactions + // are immediately dropped. + api.set_priority(¤t_uxt, 10); + + let current_operation_id: String = tx_api + .call("transaction_unstable_broadcast", rpc_params![¤t_xt]) + .await + .unwrap(); + + // Announce block 1 to `transaction_unstable_broadcast`. + let block_1_header = api.push_block(1, vec![], true); + let event = + ChainEvent::Finalized { hash: block_1_header.hash(), tree_route: Arc::from(vec![]) }; + pool.inner_pool.maintain(event).await; + client_mock.trigger_import_stream(block_1_header).await; + + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: current_xt.clone(), + status: TxStatusTypeTest::Ready, + } + ); + assert_eq!(1, pool.inner_pool.status().ready); + + // The future tx has priority 2, smaller than the current 10. + api.set_priority(&future_uxt, 2); + let future_operation_id: String = tx_api + .call("transaction_unstable_broadcast", rpc_params![&future_xt]) + .await + .unwrap(); + assert_ne!(current_operation_id, future_operation_id); + + let block_2_header = api.push_block(2, vec![], true); + let event = + ChainEvent::Finalized { hash: block_2_header.hash(), tree_route: Arc::from(vec![]) }; + pool.inner_pool.maintain(event).await; + client_mock.trigger_import_stream(block_2_header).await; + + // We must have at most 1 transaction in the pool, as per limits above. + assert_eq!(1, pool.inner_pool.status().ready); + + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::PoolError { + transaction: future_xt.clone(), + err: "Transaction couldn't enter the pool because of the limit".into() + } + ); + + let block_3_header = api.push_block(3, vec![current_uxt], true); + let event = + ChainEvent::Finalized { hash: block_3_header.hash(), tree_route: Arc::from(vec![]) }; + pool.inner_pool.maintain(event).await; + client_mock.trigger_import_stream(block_3_header.clone()).await; + + // The first tx is in a finalized block; the future tx must enter the pool. + let events = get_next_tx_events!(&mut pool_middleware, 3); + assert_eq!( + events.get(¤t_xt).unwrap(), + &vec![ + TxStatusTypeTest::InBlock((block_3_header.hash(), 0)), + TxStatusTypeTest::Finalized((block_3_header.hash(), 0)) + ] + ); + // The dropped transaction was resubmitted. + assert_eq!(events.get(&future_xt).unwrap(), &vec![TxStatusTypeTest::Ready]); +} diff --git a/substrate/client/rpc-spec-v2/src/transaction/transaction.rs b/substrate/client/rpc-spec-v2/src/transaction/transaction.rs index 17889b3bad2a55794cc191514106fe02274512b6..d44006392dca4a05a6c9f5bbf872c75bc7d5ee52 100644 --- a/substrate/client/rpc-spec-v2/src/transaction/transaction.rs +++ b/substrate/client/rpc-spec-v2/src/transaction/transaction.rs @@ -22,10 +22,7 @@ use crate::{ transaction::{ api::TransactionApiServer, error::Error, - event::{ - TransactionBlock, TransactionBroadcasted, TransactionDropped, TransactionError, - TransactionEvent, - }, + event::{TransactionBlock, TransactionDropped, TransactionError, TransactionEvent}, }, SubscriptionTaskExecutor, }; @@ -113,9 +110,7 @@ where match submit.await { Ok(stream) => { - let mut state = TransactionState::new(); - let stream = - stream.filter_map(move |event| async move { state.handle_event(event) }); + let stream = stream.filter_map(move |event| async move { handle_event(event) }); pipe_from_stream(pending, stream.boxed()).await; }, Err(err) => { @@ -131,66 +126,34 @@ where } } -/// The transaction's state that needs to be preserved between -/// multiple events generated by the transaction-pool. -/// -/// # Note -/// -/// In the future, the RPC server can submit only the last event when multiple -/// identical events happen in a row. -#[derive(Clone, Copy)] -struct TransactionState { - /// True if the transaction was previously broadcasted. - broadcasted: bool, -} - -impl TransactionState { - /// Construct a new [`TransactionState`]. - pub fn new() -> Self { - TransactionState { broadcasted: false } - } - - /// Handle events generated by the transaction-pool and convert them - /// to the new API expected state. - #[inline] - pub fn handle_event( - &mut self, - event: TransactionStatus, - ) -> Option> { - match event { - TransactionStatus::Ready | TransactionStatus::Future => - Some(TransactionEvent::::Validated), - TransactionStatus::Broadcast(peers) => { - // Set the broadcasted flag once if we submitted the transaction to - // at least one peer. - self.broadcasted = self.broadcasted || !peers.is_empty(); - - Some(TransactionEvent::Broadcasted(TransactionBroadcasted { - num_peers: peers.len(), - })) - }, - TransactionStatus::InBlock((hash, index)) => - Some(TransactionEvent::BestChainBlockIncluded(Some(TransactionBlock { - hash, - index, - }))), - TransactionStatus::Retracted(_) => Some(TransactionEvent::BestChainBlockIncluded(None)), - TransactionStatus::FinalityTimeout(_) => - Some(TransactionEvent::Dropped(TransactionDropped { - broadcasted: self.broadcasted, - error: "Maximum number of finality watchers has been reached".into(), - })), - TransactionStatus::Finalized((hash, index)) => - Some(TransactionEvent::Finalized(TransactionBlock { hash, index })), - TransactionStatus::Usurped(_) => Some(TransactionEvent::Invalid(TransactionError { - error: "Extrinsic was rendered invalid by another extrinsic".into(), - })), - TransactionStatus::Dropped => Some(TransactionEvent::Invalid(TransactionError { - error: "Extrinsic dropped from the pool due to exceeding limits".into(), - })), - TransactionStatus::Invalid => Some(TransactionEvent::Invalid(TransactionError { - error: "Extrinsic marked as invalid".into(), +/// Handle events generated by the transaction-pool and convert them +/// to the new API expected state. +#[inline] +pub fn handle_event( + event: TransactionStatus, +) -> Option> { + match event { + TransactionStatus::Ready | TransactionStatus::Future => + Some(TransactionEvent::::Validated), + TransactionStatus::InBlock((hash, index)) => + Some(TransactionEvent::BestChainBlockIncluded(Some(TransactionBlock { hash, index }))), + TransactionStatus::Retracted(_) => Some(TransactionEvent::BestChainBlockIncluded(None)), + TransactionStatus::FinalityTimeout(_) => + Some(TransactionEvent::Dropped(TransactionDropped { + error: "Maximum number of finality watchers has been reached".into(), })), - } + TransactionStatus::Finalized((hash, index)) => + Some(TransactionEvent::Finalized(TransactionBlock { hash, index })), + TransactionStatus::Usurped(_) => Some(TransactionEvent::Invalid(TransactionError { + error: "Extrinsic was rendered invalid by another extrinsic".into(), + })), + TransactionStatus::Dropped => Some(TransactionEvent::Invalid(TransactionError { + error: "Extrinsic dropped from the pool due to exceeding limits".into(), + })), + TransactionStatus::Invalid => Some(TransactionEvent::Invalid(TransactionError { + error: "Extrinsic marked as invalid".into(), + })), + // These are the events that are not supported by the new API. + TransactionStatus::Broadcast(_) => None, } } diff --git a/substrate/client/rpc-spec-v2/src/transaction/transaction_broadcast.rs b/substrate/client/rpc-spec-v2/src/transaction/transaction_broadcast.rs index 92c838261874a816335b6d7e82c7df7667e2c235..6eaf50d6b2e2822d766f4f131f4bd6f1ba04d5cd 100644 --- a/substrate/client/rpc-spec-v2/src/transaction/transaction_broadcast.rs +++ b/substrate/client/rpc-spec-v2/src/transaction/transaction_broadcast.rs @@ -44,7 +44,7 @@ pub struct TransactionBroadcast { pool: Arc, /// Executor to spawn subscriptions. executor: SubscriptionTaskExecutor, - /// The brodcast operation IDs. + /// The broadcast operation IDs. broadcast_ids: Arc>>, } @@ -200,7 +200,7 @@ where } } -/// Returns the last element of the providided stream, or `None` if the stream is closed. +/// Returns the last element of the provided stream, or `None` if the stream is closed. async fn last_stream_element(stream: &mut S) -> Option where S: Stream + Unpin, diff --git a/substrate/client/rpc/src/state/tests.rs b/substrate/client/rpc/src/state/tests.rs index 96f4c1be960faa5f15508f19a9dd5665d8b684c1..dd866e671c5095dca9b5851111340a4e32f71e67 100644 --- a/substrate/client/rpc/src/state/tests.rs +++ b/substrate/client/rpc/src/state/tests.rs @@ -475,7 +475,7 @@ async fn should_return_runtime_version() { // it is basically json-encoded substrate_test_runtime_client::runtime::VERSION let result = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\ - \"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",4],\ + \"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",5],\ [\"0x37e397fc7c91f5e4\",2],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\ [\"0xbc9d89904f5b923f\",1],[\"0xc6e9a76309f39b09\",2],[\"0xdd718d5cc53262d4\",1],\ [\"0xcbca25e39f142387\",2],[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],\ diff --git a/substrate/client/rpc/src/statement/mod.rs b/substrate/client/rpc/src/statement/mod.rs index b4f432bbbb0e3485a67447fb34669bf980602868..e99135aec38c6fb61dc667535b6fe9e0f458f6e3 100644 --- a/substrate/client/rpc/src/statement/mod.rs +++ b/substrate/client/rpc/src/statement/mod.rs @@ -89,7 +89,7 @@ impl StatementApiServer for StatementStore { fn submit(&self, encoded: Bytes) -> RpcResult<()> { let statement = Decode::decode(&mut &*encoded) - .map_err(|e| Error::StatementStore(format!("Eror decoding statement: {:?}", e)))?; + .map_err(|e| Error::StatementStore(format!("Error decoding statement: {:?}", e)))?; match self.store.submit(statement, StatementSource::Local) { SubmitResult::New(_) | SubmitResult::Known => Ok(()), // `KnownExpired` should not happen. Expired statements submitted with diff --git a/substrate/client/service/Cargo.toml b/substrate/client/service/Cargo.toml index 73edceb2ef3608c2c6da65806c1caf665bcfddd9..bbf67d1fbd0af6dceada3b73178004558044d0b2 100644 --- a/substrate/client/service/Cargo.toml +++ b/substrate/client/service/Cargo.toml @@ -84,6 +84,7 @@ tokio = { version = "1.22.0", features = ["parking_lot", "rt-multi-thread", "tim tempfile = "3.1.0" directories = "5.0.1" static_init = "1.0.3" +schnellru = "0.2.1" [dev-dependencies] substrate-test-runtime-client = { path = "../../test-utils/runtime/client" } diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs index 31d63c6a81d3a4df2e6eca32260a64f577712aa9..e71313428daf5acbe6d9837697191aa4d792a39d 100644 --- a/substrate/client/service/src/builder.rs +++ b/substrate/client/service/src/builder.rs @@ -64,7 +64,9 @@ use sc_rpc::{ DenyUnsafe, SubscriptionTaskExecutor, }; use sc_rpc_spec_v2::{ - archive::ArchiveApiServer, chain_head::ChainHeadApiServer, transaction::TransactionApiServer, + archive::ArchiveApiServer, + chain_head::ChainHeadApiServer, + transaction::{TransactionApiServer, TransactionBroadcastApiServer}, }; use sc_telemetry::{telemetry, ConnectionMessage, Telemetry, TelemetryHandle, SUBSTRATE_INFO}; use sc_transaction_pool_api::{MaintainedTransactionPool, TransactionPool}; @@ -653,6 +655,13 @@ where (chain, state, child_state) }; + let transaction_broadcast_rpc_v2 = sc_rpc_spec_v2::transaction::TransactionBroadcast::new( + client.clone(), + transaction_pool.clone(), + task_executor.clone(), + ) + .into_rpc(); + let transaction_v2 = sc_rpc_spec_v2::transaction::Transaction::new( client.clone(), transaction_pool.clone(), @@ -708,6 +717,9 @@ where // Part of the RPC v2 spec. rpc_api.merge(transaction_v2).map_err(|e| Error::Application(e.into()))?; + rpc_api + .merge(transaction_broadcast_rpc_v2) + .map_err(|e| Error::Application(e.into()))?; rpc_api.merge(chain_head_v2).map_err(|e| Error::Application(e.into()))?; // Part of the old RPC spec. diff --git a/substrate/client/service/src/chain_ops/import_blocks.rs b/substrate/client/service/src/chain_ops/import_blocks.rs index 34f7669d0106e2065c3b08f7816a002c9d1175ae..661fc09a8f19e55c38bec280b7ce399a8dbe2e29 100644 --- a/substrate/client/service/src/chain_ops/import_blocks.rs +++ b/substrate/client/service/src/chain_ops/import_blocks.rs @@ -73,7 +73,7 @@ where reader: CodecIoReader, }, Json { - // Nubmer of blocks we have decoded thus far. + // Number of blocks we have decoded thus far. read_block_count: u64, // Stream to the data, used for decoding new blocks. reader: StreamDeserializer<'static, JsonIoRead, SignedBlock>, diff --git a/substrate/client/service/src/client/client.rs b/substrate/client/service/src/client/client.rs index aa9c1b80a29a95bd77efbda35620c132b624bd9b..35e8b53a09cf2d802a0c4a873f7ecb8099764e83 100644 --- a/substrate/client/service/src/client/client.rs +++ b/substrate/client/service/src/client/client.rs @@ -19,8 +19,8 @@ //! Substrate Client use super::block_rules::{BlockRules, LookupResult as BlockLookupResult}; -use futures::{FutureExt, StreamExt}; -use log::{error, info, trace, warn}; +use crate::client::notification_pinning::NotificationPinningWorker; +use log::{debug, info, trace, warn}; use parking_lot::{Mutex, RwLock}; use prometheus_endpoint::Registry; use rand::Rng; @@ -38,7 +38,7 @@ use sc_client_api::{ execution_extensions::ExecutionExtensions, notifications::{StorageEventStream, StorageNotifications}, CallExecutor, ExecutorProvider, KeysIter, OnFinalityAction, OnImportAction, PairsIter, - ProofProvider, UsageProvider, + ProofProvider, UnpinWorkerMessage, UsageProvider, }; use sc_consensus::{ BlockCheckParams, BlockImportParams, ForkChoiceStrategy, ImportResult, StateAction, @@ -114,7 +114,7 @@ where block_rules: BlockRules, config: ClientConfig, telemetry: Option, - unpin_worker_sender: TracingUnboundedSender, + unpin_worker_sender: TracingUnboundedSender>, _phantom: PhantomData, } @@ -326,19 +326,35 @@ where // dropped, the block will be unpinned automatically. if let Some(ref notification) = finality_notification { if let Err(err) = self.backend.pin_block(notification.hash) { - error!( + debug!( "Unable to pin block for finality notification. hash: {}, Error: {}", notification.hash, err ); - }; + } else { + let _ = self + .unpin_worker_sender + .unbounded_send(UnpinWorkerMessage::AnnouncePin(notification.hash)) + .map_err(|e| { + log::error!( + "Unable to send AnnouncePin worker message for finality: {e}" + ) + }); + } } if let Some(ref notification) = import_notification { if let Err(err) = self.backend.pin_block(notification.hash) { - error!( + debug!( "Unable to pin block for import notification. hash: {}, Error: {}", notification.hash, err ); + } else { + let _ = self + .unpin_worker_sender + .unbounded_send(UnpinWorkerMessage::AnnouncePin(notification.hash)) + .map_err(|e| { + log::error!("Unable to send AnnouncePin worker message for import: {e}") + }); }; } @@ -416,25 +432,12 @@ where backend.commit_operation(op)?; } - let (unpin_worker_sender, mut rx) = - tracing_unbounded::("unpin-worker-channel", 10_000); - let task_backend = Arc::downgrade(&backend); - spawn_handle.spawn( - "unpin-worker", - None, - async move { - while let Some(message) = rx.next().await { - if let Some(backend) = task_backend.upgrade() { - backend.unpin_block(message); - } else { - log::debug!("Terminating unpin-worker, backend reference was dropped."); - return - } - } - log::debug!("Terminating unpin-worker, stream terminated.") - } - .boxed(), + let (unpin_worker_sender, rx) = tracing_unbounded::>( + "notification-pinning-worker-channel", + 10_000, ); + let unpin_worker = NotificationPinningWorker::new(rx, backend.clone()); + spawn_handle.spawn("notification-pinning-worker", None, Box::pin(unpin_worker.run())); Ok(Client { backend, @@ -675,8 +678,10 @@ where // This is use by fast sync for runtime version to be resolvable from // changes. - let state_version = - resolve_state_version_from_wasm(&storage, &self.executor)?; + let state_version = resolve_state_version_from_wasm::<_, HashingFor>( + &storage, + &self.executor, + )?; let state_root = operation.op.reset_storage(storage, state_version)?; if state_root != *import_headers.post().state_root() { // State root mismatch when importing state. This should not happen in diff --git a/substrate/client/service/src/client/mod.rs b/substrate/client/service/src/client/mod.rs index a13fd4317e1553d379ea068c516e74072d3c8c95..0703cc2b47d144d4e67418cfb9966cd1cd209392 100644 --- a/substrate/client/service/src/client/mod.rs +++ b/substrate/client/service/src/client/mod.rs @@ -47,6 +47,7 @@ mod block_rules; mod call_executor; mod client; +mod notification_pinning; mod wasm_override; mod wasm_substitutes; diff --git a/substrate/client/service/src/client/notification_pinning.rs b/substrate/client/service/src/client/notification_pinning.rs new file mode 100644 index 0000000000000000000000000000000000000000..80de91c02f1ae0273d42edc79d442aa312d09356 --- /dev/null +++ b/substrate/client/service/src/client/notification_pinning.rs @@ -0,0 +1,353 @@ +// This file is part of Substrate. + +// Copyright (C) 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 . + +//! Notification pinning related logic. +//! +//! This file contains a worker that should be started when a new client instance is created. +//! The goal is to avoid pruning of blocks that have active notifications in the node. Every +//! recipient of notifications should receive the chance to act upon them. In addition, notification +//! listeners can hold onto a [`sc_client_api::UnpinHandle`] to keep a block pinned. Once the handle +//! is dropped, a message is sent and the worker unpins the respective block. +use std::{ + marker::PhantomData, + sync::{Arc, Weak}, +}; + +use futures::StreamExt; +use sc_client_api::{Backend, UnpinWorkerMessage}; + +use sc_utils::mpsc::TracingUnboundedReceiver; +use schnellru::Limiter; +use sp_runtime::traits::Block as BlockT; + +const LOG_TARGET: &str = "db::notification_pinning"; +const NOTIFICATION_PINNING_LIMIT: usize = 1024; + +/// A limiter which automatically unpins blocks that leave the data structure. +#[derive(Clone, Debug)] +struct UnpinningByLengthLimiter> { + max_length: usize, + backend: Weak, + _phantom: PhantomData, +} + +impl> UnpinningByLengthLimiter { + /// Creates a new length limiter with a given `max_length`. + pub fn new(max_length: usize, backend: Weak) -> UnpinningByLengthLimiter { + UnpinningByLengthLimiter { max_length, backend, _phantom: PhantomData::::default() } + } +} + +impl> Limiter + for UnpinningByLengthLimiter +{ + type KeyToInsert<'a> = Block::Hash; + type LinkType = usize; + + fn is_over_the_limit(&self, length: usize) -> bool { + length > self.max_length + } + + fn on_insert( + &mut self, + _length: usize, + key: Self::KeyToInsert<'_>, + value: u32, + ) -> Option<(Block::Hash, u32)> { + log::debug!(target: LOG_TARGET, "Pinning block based on notification. hash = {key}"); + if self.max_length > 0 { + Some((key, value)) + } else { + None + } + } + + fn on_replace( + &mut self, + _length: usize, + _old_key: &mut Block::Hash, + _new_key: Block::Hash, + _old_value: &mut u32, + _new_value: &mut u32, + ) -> bool { + true + } + + fn on_removed(&mut self, key: &mut Block::Hash, references: &mut u32) { + // If reference count was larger than 0 on removal, + // the item was removed due to capacity limitations. + // Since the cache should be large enough for pinned items, + // we want to know about these evictions. + if *references > 0 { + log::warn!( + target: LOG_TARGET, + "Notification block pinning limit reached. Unpinning block with hash = {key:?}" + ); + if let Some(backend) = self.backend.upgrade() { + (0..*references).for_each(|_| backend.unpin_block(*key)); + } + } else { + log::trace!( + target: LOG_TARGET, + "Unpinned block. hash = {key:?}", + ) + } + } + + fn on_cleared(&mut self) {} + + fn on_grow(&mut self, _new_memory_usage: usize) -> bool { + true + } +} + +/// Worker for the handling of notification pinning. +/// +/// It receives messages from a receiver and pins/unpins based on the incoming messages. +/// All notification related unpinning should go through this worker. If the maximum number of +/// notification pins is reached, the block from the oldest notification is unpinned. +pub struct NotificationPinningWorker> { + unpin_message_rx: TracingUnboundedReceiver>, + task_backend: Weak, + pinned_blocks: schnellru::LruMap>, +} + +impl> NotificationPinningWorker { + /// Creates a new `NotificationPinningWorker`. + pub fn new( + unpin_message_rx: TracingUnboundedReceiver>, + task_backend: Arc, + ) -> Self { + let pinned_blocks = + schnellru::LruMap::>::new( + UnpinningByLengthLimiter::new( + NOTIFICATION_PINNING_LIMIT, + Arc::downgrade(&task_backend), + ), + ); + Self { unpin_message_rx, task_backend: Arc::downgrade(&task_backend), pinned_blocks } + } + + fn handle_announce_message(&mut self, hash: Block::Hash) { + if let Some(entry) = self.pinned_blocks.get_or_insert(hash, Default::default) { + *entry = *entry + 1; + } + } + + fn handle_unpin_message(&mut self, hash: Block::Hash) -> Result<(), ()> { + if let Some(refcount) = self.pinned_blocks.peek_mut(&hash) { + *refcount = *refcount - 1; + if *refcount == 0 { + self.pinned_blocks.remove(&hash); + } + if let Some(backend) = self.task_backend.upgrade() { + log::debug!(target: LOG_TARGET, "Reducing pinning refcount for block hash = {hash:?}"); + backend.unpin_block(hash); + } else { + log::debug!(target: LOG_TARGET, "Terminating unpin-worker, backend reference was dropped."); + return Err(()) + } + } else { + log::debug!(target: LOG_TARGET, "Received unpin message for already unpinned block. hash = {hash:?}"); + } + Ok(()) + } + + /// Start working on the received messages. + /// + /// The worker maintains a map which keeps track of the pinned blocks and their reference count. + /// Depending upon the received message, it acts to pin/unpin the block. + pub async fn run(mut self) { + while let Some(message) = self.unpin_message_rx.next().await { + match message { + UnpinWorkerMessage::AnnouncePin(hash) => self.handle_announce_message(hash), + UnpinWorkerMessage::Unpin(hash) => + if self.handle_unpin_message(hash).is_err() { + return + }, + } + } + log::debug!(target: LOG_TARGET, "Terminating unpin-worker, stream terminated.") + } +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use sc_client_api::{Backend, UnpinWorkerMessage}; + use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver}; + use sp_core::H256; + use sp_runtime::traits::Block as BlockT; + + type Block = substrate_test_runtime_client::runtime::Block; + + use super::{NotificationPinningWorker, UnpinningByLengthLimiter}; + + impl> NotificationPinningWorker { + fn new_with_limit( + unpin_message_rx: TracingUnboundedReceiver>, + task_backend: Arc, + limit: usize, + ) -> Self { + let pinned_blocks = + schnellru::LruMap::>::new( + UnpinningByLengthLimiter::new(limit, Arc::downgrade(&task_backend)), + ); + Self { unpin_message_rx, task_backend: Arc::downgrade(&task_backend), pinned_blocks } + } + + fn lru( + &self, + ) -> &schnellru::LruMap> { + &self.pinned_blocks + } + } + + #[test] + fn pinning_worker_handles_base_case() { + let (_tx, rx) = tracing_unbounded("testing", 1000); + + let backend = Arc::new(sc_client_api::in_mem::Backend::::new()); + + let hash = H256::random(); + + let mut worker = NotificationPinningWorker::new(rx, backend.clone()); + + // Block got pinned and unpin message should unpin in the backend. + let _ = backend.pin_block(hash); + assert_eq!(backend.pin_refs(&hash), Some(1)); + + worker.handle_announce_message(hash); + assert_eq!(worker.lru().len(), 1); + + let _ = worker.handle_unpin_message(hash); + + assert_eq!(backend.pin_refs(&hash), Some(0)); + assert!(worker.lru().is_empty()); + } + + #[test] + fn pinning_worker_handles_multiple_pins() { + let (_tx, rx) = tracing_unbounded("testing", 1000); + + let backend = Arc::new(sc_client_api::in_mem::Backend::::new()); + + let hash = H256::random(); + + let mut worker = NotificationPinningWorker::new(rx, backend.clone()); + // Block got pinned multiple times. + let _ = backend.pin_block(hash); + let _ = backend.pin_block(hash); + let _ = backend.pin_block(hash); + assert_eq!(backend.pin_refs(&hash), Some(3)); + + worker.handle_announce_message(hash); + worker.handle_announce_message(hash); + worker.handle_announce_message(hash); + assert_eq!(worker.lru().len(), 1); + + let _ = worker.handle_unpin_message(hash); + assert_eq!(backend.pin_refs(&hash), Some(2)); + let _ = worker.handle_unpin_message(hash); + assert_eq!(backend.pin_refs(&hash), Some(1)); + let _ = worker.handle_unpin_message(hash); + assert_eq!(backend.pin_refs(&hash), Some(0)); + assert!(worker.lru().is_empty()); + + let _ = worker.handle_unpin_message(hash); + assert_eq!(backend.pin_refs(&hash), Some(0)); + } + + #[test] + fn pinning_worker_handles_too_many_unpins() { + let (_tx, rx) = tracing_unbounded("testing", 1000); + + let backend = Arc::new(sc_client_api::in_mem::Backend::::new()); + + let hash = H256::random(); + let hash2 = H256::random(); + + let mut worker = NotificationPinningWorker::new(rx, backend.clone()); + // Block was announced once but unpinned multiple times. The worker should ignore the + // additional unpins. + let _ = backend.pin_block(hash); + let _ = backend.pin_block(hash); + let _ = backend.pin_block(hash); + assert_eq!(backend.pin_refs(&hash), Some(3)); + + worker.handle_announce_message(hash); + assert_eq!(worker.lru().len(), 1); + + let _ = worker.handle_unpin_message(hash); + assert_eq!(backend.pin_refs(&hash), Some(2)); + let _ = worker.handle_unpin_message(hash); + assert_eq!(backend.pin_refs(&hash), Some(2)); + assert!(worker.lru().is_empty()); + + let _ = worker.handle_unpin_message(hash2); + assert!(worker.lru().is_empty()); + assert_eq!(backend.pin_refs(&hash2), None); + } + + #[test] + fn pinning_worker_should_evict_when_limit_reached() { + let (_tx, rx) = tracing_unbounded("testing", 1000); + + let backend = Arc::new(sc_client_api::in_mem::Backend::::new()); + + let hash1 = H256::random(); + let hash2 = H256::random(); + let hash3 = H256::random(); + let hash4 = H256::random(); + + // Only two items fit into the cache. + let mut worker = NotificationPinningWorker::new_with_limit(rx, backend.clone(), 2); + + // Multiple blocks are announced but the cache size is too small. We expect that blocks + // are evicted by the cache and unpinned in the backend. + let _ = backend.pin_block(hash1); + let _ = backend.pin_block(hash2); + let _ = backend.pin_block(hash3); + assert_eq!(backend.pin_refs(&hash1), Some(1)); + assert_eq!(backend.pin_refs(&hash2), Some(1)); + assert_eq!(backend.pin_refs(&hash3), Some(1)); + + worker.handle_announce_message(hash1); + assert!(worker.lru().peek(&hash1).is_some()); + worker.handle_announce_message(hash2); + assert!(worker.lru().peek(&hash2).is_some()); + worker.handle_announce_message(hash3); + assert!(worker.lru().peek(&hash3).is_some()); + assert!(worker.lru().peek(&hash2).is_some()); + assert_eq!(worker.lru().len(), 2); + + // Hash 1 should have gotten unpinned, since its oldest. + assert_eq!(backend.pin_refs(&hash1), Some(0)); + assert_eq!(backend.pin_refs(&hash2), Some(1)); + assert_eq!(backend.pin_refs(&hash3), Some(1)); + + // Hash 2 is getting bumped. + worker.handle_announce_message(hash2); + assert_eq!(worker.lru().peek(&hash2), Some(&2)); + + // Since hash 2 was accessed, evict hash 3. + worker.handle_announce_message(hash4); + assert_eq!(worker.lru().peek(&hash3), None); + } +} diff --git a/substrate/client/service/src/config.rs b/substrate/client/service/src/config.rs index 35262ff493b44f07a94f1d35f1a3e765251a1897..59e307d7f93b529f37ab457dd6bfe0b312065444 100644 --- a/substrate/client/service/src/config.rs +++ b/substrate/client/service/src/config.rs @@ -236,7 +236,7 @@ impl Configuration { ProtocolId::from(protocol_id_full) } - /// Returns true if the genesis state writting will be skipped while initializing the genesis + /// Returns true if the genesis state writing will be skipped while initializing the genesis /// block. pub fn no_genesis(&self) -> bool { matches!(self.network.sync_mode, SyncMode::LightState { .. } | SyncMode::Warp { .. }) diff --git a/substrate/client/service/test/src/client/mod.rs b/substrate/client/service/test/src/client/mod.rs index ba83bec8276eb3b4f7b09ff9a5717343b3f5e59e..51dcc4966e58838a4055f59ff033e16010c1d585 100644 --- a/substrate/client/service/test/src/client/mod.rs +++ b/substrate/client/service/test/src/client/mod.rs @@ -2226,11 +2226,11 @@ fn reorg_triggers_a_notification_even_for_sources_that_should_not_trigger_notifi #[test] fn use_dalek_ext_works() { fn zero_ed_pub() -> sp_core::ed25519::Public { - sp_core::ed25519::Public([0u8; 32]) + sp_core::ed25519::Public::default() } fn zero_ed_sig() -> sp_core::ed25519::Signature { - sp_core::ed25519::Signature::from_raw([0u8; 64]) + sp_core::ed25519::Signature::default() } let mut client = TestClientBuilder::new().build(); diff --git a/substrate/client/state-db/src/lib.rs b/substrate/client/state-db/src/lib.rs index 41c231c31aafbc6453fa4343640c27b733174c28..6c37c87a611b1288c6aecc4b79b4a3e159e2b9dd 100644 --- a/substrate/client/state-db/src/lib.rs +++ b/substrate/client/state-db/src/lib.rs @@ -872,7 +872,7 @@ mod tests { fn check_stored_and_requested_mode_compatibility( mode_when_created: Option, mode_when_reopened: Option, - expected_effective_mode_when_reopenned: Result, + expected_effective_mode_when_reopened: Result, ) { let mut db = make_db(&[]); let (state_db_init, state_db) = @@ -883,7 +883,7 @@ mod tests { let state_db_reopen_result = StateDb::::open(db.clone(), mode_when_reopened, false, false); - if let Ok(expected_mode) = expected_effective_mode_when_reopenned { + if let Ok(expected_mode) = expected_effective_mode_when_reopened { let (state_db_init, state_db_reopened) = state_db_reopen_result.unwrap(); db.commit(&state_db_init); assert_eq!(state_db_reopened.pruning_mode(), expected_mode,) diff --git a/substrate/client/state-db/src/noncanonical.rs b/substrate/client/state-db/src/noncanonical.rs index bdbe8318371ce8239158f4f728a7313e107db879..4492a7bd077df02c6feb111051273742ef79604b 100644 --- a/substrate/client/state-db/src/noncanonical.rs +++ b/substrate/client/state-db/src/noncanonical.rs @@ -46,26 +46,26 @@ pub struct NonCanonicalOverlay { #[cfg_attr(test, derive(PartialEq, Debug))] struct OverlayLevel { blocks: Vec>, - used_indicies: u64, // Bitmask of available journal indicies. + used_indices: u64, // Bitmask of available journal indices. } impl OverlayLevel { fn push(&mut self, overlay: BlockOverlay) { - self.used_indicies |= 1 << overlay.journal_index; + self.used_indices |= 1 << overlay.journal_index; self.blocks.push(overlay) } fn available_index(&self) -> u64 { - self.used_indicies.trailing_ones() as u64 + self.used_indices.trailing_ones() as u64 } fn remove(&mut self, index: usize) -> BlockOverlay { - self.used_indicies &= !(1 << self.blocks[index].journal_index); + self.used_indices &= !(1 << self.blocks[index].journal_index); self.blocks.remove(index) } fn new() -> OverlayLevel { - OverlayLevel { blocks: Vec::new(), used_indicies: 0 } + OverlayLevel { blocks: Vec::new(), used_indices: 0 } } } diff --git a/substrate/client/storage-monitor/Cargo.toml b/substrate/client/storage-monitor/Cargo.toml index b2120b3efc4396801bdbcaf0a7867ab6692908d3..5248ebdf9a650ba2e1fd1a34f18dbbc0b16781fe 100644 --- a/substrate/client/storage-monitor/Cargo.toml +++ b/substrate/client/storage-monitor/Cargo.toml @@ -12,7 +12,7 @@ homepage = "https://substrate.io" workspace = true [dependencies] -clap = { version = "4.5.1", features = ["derive", "string"] } +clap = { version = "4.5.3", features = ["derive", "string"] } log = { workspace = true, default-features = true } fs4 = "0.7.0" sp-core = { path = "../../primitives/core" } diff --git a/substrate/client/telemetry/src/lib.rs b/substrate/client/telemetry/src/lib.rs index 113d8303a20f6fedcf8f3c75108896bbbb8f8756..7e3a4ee8639308bd4678288db583e31c5085af5b 100644 --- a/substrate/client/telemetry/src/lib.rs +++ b/substrate/client/telemetry/src/lib.rs @@ -413,7 +413,7 @@ impl Telemetry { .map_err(|_| Error::TelemetryWorkerDropped) } - /// Make a new cloneable handle to this [`Telemetry`]. This is used for reporting telemetries. + /// Make a new clonable handle to this [`Telemetry`]. This is used for reporting telemetries. pub fn handle(&self) -> TelemetryHandle { TelemetryHandle { message_sender: Arc::new(Mutex::new(self.message_sender.clone())), diff --git a/substrate/client/transaction-pool/README.md b/substrate/client/transaction-pool/README.md index b55dc6482d64eb5342e684a82989f0ab2732b784..7a53727d576103800375a99d0544b3b67efad79e 100644 --- a/substrate/client/transaction-pool/README.md +++ b/substrate/client/transaction-pool/README.md @@ -171,7 +171,7 @@ This parameter instructs the pool propagate/gossip a transaction to node peers. By default this should be `true`, however in some cases it might be undesirable to propagate transactions further. Examples might include heavy transactions produced by block authors in offchain workers (DoS) or risking being front -runned by someone else after finding some non trivial solution or equivocation, +ran by someone else after finding some non trivial solution or equivocation, etc. ### 'TransactionSource` diff --git a/substrate/client/transaction-pool/src/lib.rs b/substrate/client/transaction-pool/src/lib.rs index faa3f455a580c8713ced21b14818da874fd972a7..64b301e6bf36f4f4fc88abf15a36c5b1ef30b04b 100644 --- a/substrate/client/transaction-pool/src/lib.rs +++ b/substrate/client/transaction-pool/src/lib.rs @@ -164,8 +164,9 @@ where pool_api: Arc, best_block_hash: Block::Hash, finalized_hash: Block::Hash, + options: graph::Options, ) -> (Self, Pin + Send>>) { - let pool = Arc::new(graph::Pool::new(Default::default(), true.into(), pool_api.clone())); + let pool = Arc::new(graph::Pool::new(options, true.into(), pool_api.clone())); let (revalidation_queue, background_task) = revalidation::RevalidationQueue::new_background( pool_api.clone(), pool.clone(), diff --git a/substrate/client/transaction-pool/tests/pool.rs b/substrate/client/transaction-pool/tests/pool.rs index 6b1a197440c11e69805465ed4c7195f07eaafc0f..49bd2203c12b48454844e1bc3ce09c2c446fc9b6 100644 --- a/substrate/client/transaction-pool/tests/pool.rs +++ b/substrate/client/transaction-pool/tests/pool.rs @@ -73,7 +73,7 @@ fn create_basic_pool_with_genesis( .map(|blocks| blocks[0].0.header.hash()) .expect("there is block 0. qed") }; - BasicPool::new_test(test_api, genesis_hash, genesis_hash) + BasicPool::new_test(test_api, genesis_hash, genesis_hash, Default::default()) } fn create_basic_pool(test_api: TestApi) -> BasicPool { @@ -994,11 +994,12 @@ fn import_notification_to_pool_maintain_works() { )), best_hash, finalized_hash, + Default::default(), ) .0, ); - // Prepare the extrisic, push it to the pool and check that it was added. + // Prepare the extrinsic, push it to the pool and check that it was added. let xt = uxt(Alice, 0); block_on(pool.submit_one( pool.api().block_id_to_hash(&BlockId::Number(0)).unwrap().unwrap(), diff --git a/substrate/client/utils/src/notification.rs b/substrate/client/utils/src/notification.rs index dabb85d613cc97e1a95133a5de130b505f5050e4..3f606aabe3a156071ad0fbfffc8c5c8fd55728e2 100644 --- a/substrate/client/utils/src/notification.rs +++ b/substrate/client/utils/src/notification.rs @@ -44,7 +44,7 @@ mod tests; /// and identify the mpsc channels. pub trait TracingKeyStr { /// Const `str` representing the "tracing key" used to tag and identify - /// the mpsc channels owned by the object implemeting this trait. + /// the mpsc channels owned by the object implementing this trait. const TRACING_KEY: &'static str; } diff --git a/substrate/frame/Cargo.toml b/substrate/frame/Cargo.toml index 3f148bf4c83bfcbcf1892bdb0ebb83abb70b9cce..6746723e72f79498cf764199866936b93373858e 100644 --- a/substrate/frame/Cargo.toml +++ b/substrate/frame/Cargo.toml @@ -19,8 +19,12 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # external deps -parity-scale-codec = { version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } +parity-scale-codec = { version = "3.2.2", default-features = false, features = [ + "derive", +] } +scale-info = { version = "2.6.0", default-features = false, features = [ + "derive", +] } # primitive deps, used for developing FRAME pallets. sp-runtime = { default-features = false, path = "../primitives/runtime" } @@ -57,8 +61,6 @@ pallet-examples = { path = "./examples" } default = ["runtime", "std"] experimental = ["frame-support/experimental"] runtime = [ - "frame-executive", - "frame-system-rpc-runtime-api", "sp-api", "sp-block-builder", "sp-consensus-aura", @@ -68,6 +70,9 @@ runtime = [ "sp-session", "sp-transaction-pool", "sp-version", + + "frame-executive", + "frame-system-rpc-runtime-api", ] std = [ "frame-executive?/std", diff --git a/substrate/frame/alliance/README.md b/substrate/frame/alliance/README.md index 9930008e2d63558a9f83d906da92ecbf29f11a7a..16335a98f59f67a06b9ed6f18fe2c41e43124a34 100644 --- a/substrate/frame/alliance/README.md +++ b/substrate/frame/alliance/README.md @@ -58,7 +58,7 @@ to update the Alliance's rule and make announcements. - `add_unscrupulous_items` - Add some items, either accounts or websites, to the list of unscrupulous items. - `remove_unscrupulous_items` - Remove some items from the list of unscrupulous items. -- `abdicate_fellow_status` - Abdicate one's voting rights, demoting themself to Ally. +- `abdicate_fellow_status` - Abdicate one's voting rights, demoting themselves to Ally. #### Root Calls diff --git a/substrate/frame/alliance/src/benchmarking.rs b/substrate/frame/alliance/src/benchmarking.rs index 9fe0e29b42cd9526a1e221531dd5946846b17b90..4ccd0fc08f6ac1c8ac7e6009b555a470eb4a2779 100644 --- a/substrate/frame/alliance/src/benchmarking.rs +++ b/substrate/frame/alliance/src/benchmarking.rs @@ -26,7 +26,7 @@ use core::{ }; use sp_runtime::traits::{Bounded, Hash, StaticLookup}; -use frame_benchmarking::{account, impl_benchmark_test_suite, v2::*, BenchmarkError}; +use frame_benchmarking::{account, v2::*, BenchmarkError}; use frame_support::traits::{EnsureOrigin, Get, UnfilteredDispatchable}; use frame_system::{pallet_prelude::BlockNumberFor, Pallet as System, RawOrigin as SystemOrigin}; diff --git a/substrate/frame/alliance/src/lib.rs b/substrate/frame/alliance/src/lib.rs index d4703db68dbba34988516ba08b5efcebcb79e5e9..414d550c53a90e839e24d4f54dd218397bf1a1fe 100644 --- a/substrate/frame/alliance/src/lib.rs +++ b/substrate/frame/alliance/src/lib.rs @@ -505,10 +505,10 @@ pub mod pallet { proposal: Box<>::Proposal>, #[pallet::compact] length_bound: u32, ) -> DispatchResult { - let proposor = ensure_signed(origin)?; - ensure!(Self::has_voting_rights(&proposor), Error::::NoVotingRights); + let proposer = ensure_signed(origin)?; + ensure!(Self::has_voting_rights(&proposer), Error::::NoVotingRights); - T::ProposalProvider::propose_proposal(proposor, threshold, proposal, length_bound)?; + T::ProposalProvider::propose_proposal(proposer, threshold, proposal, length_bound)?; Ok(()) } diff --git a/substrate/frame/alliance/src/migration.rs b/substrate/frame/alliance/src/migration.rs index e3a44a7887e976cfa6c5d0cfd48c251348edb26b..432f09a16f47772ead741a0f312eefeb795e2d69 100644 --- a/substrate/frame/alliance/src/migration.rs +++ b/substrate/frame/alliance/src/migration.rs @@ -19,19 +19,19 @@ use crate::{Config, Pallet, Weight, LOG_TARGET}; use frame_support::{pallet_prelude::*, storage::migration, traits::OnRuntimeUpgrade}; use log; -/// The current storage version. +/// The in-code storage version. pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); /// Wrapper for all migrations of this pallet. pub fn migrate, I: 'static>() -> Weight { - let onchain_version = Pallet::::on_chain_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); let mut weight: Weight = Weight::zero(); - if onchain_version < 1 { + if on_chain_version < 1 { weight = weight.saturating_add(v0_to_v1::migrate::()); } - if onchain_version < 2 { + if on_chain_version < 2 { weight = weight.saturating_add(v1_to_v2::migrate::()); } diff --git a/substrate/frame/alliance/src/mock.rs b/substrate/frame/alliance/src/mock.rs index 4a65485ed8f6643e2ab38e49150eb3901ded7559..b183e412bed779291ef2bf3d94c0790295420e02 100644 --- a/substrate/frame/alliance/src/mock.rs +++ b/substrate/frame/alliance/src/mock.rs @@ -48,7 +48,7 @@ parameter_types! { frame_system::limits::BlockWeights::simple_max(Weight::MAX); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; @@ -208,7 +208,7 @@ impl ProposalProvider for AllianceProposalProvider } fn proposal_of(proposal_hash: H256) -> Option { - AllianceMotion::proposal_of(proposal_hash) + pallet_collective::ProposalOf::::get(proposal_hash) } } diff --git a/substrate/frame/alliance/src/tests.rs b/substrate/frame/alliance/src/tests.rs index 8011627b237af15e8d30379bf14afc2564421050..c65f10228e779ba71991189ec07e9d743f8f3de9 100644 --- a/substrate/frame/alliance/src/tests.rs +++ b/substrate/frame/alliance/src/tests.rs @@ -26,7 +26,7 @@ use crate::mock::*; type AllianceMotionEvent = pallet_collective::Event; fn assert_powerless(user: RuntimeOrigin, user_is_member: bool) { - //vote / veto with a valid propsal + //vote / veto with a valid proposal let cid = test_cid(); let (proposal, _, _) = make_kick_member_proposal(42); @@ -187,8 +187,8 @@ fn propose_works() { Box::new(proposal.clone()), proposal_len )); - assert_eq!(*AllianceMotion::proposals(), vec![hash]); - assert_eq!(AllianceMotion::proposal_of(&hash), Some(proposal)); + assert_eq!(*pallet_collective::Proposals::::get(), vec![hash]); + assert_eq!(pallet_collective::ProposalOf::::get(&hash), Some(proposal)); assert_eq!( System::events(), vec![EventRecord { diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index f13d40d3e7e2c74edc805ed2d4cb8154bb507480..0bf73e8809cfb3cb305716b8561846ff9a1ed631 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -205,7 +205,7 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - /// A successful call of the `CretaPool` extrinsic will create this event. + /// A successful call of the `CreatePool` extrinsic will create this event. PoolCreated { /// The account that created the pool. creator: T::AccountId, @@ -413,6 +413,11 @@ pub mod pallet { /// Params `amount1_min`/`amount2_min` represent that. /// `mint_to` will be sent the liquidity tokens that represent this share of the pool. /// + /// NOTE: when encountering an incorrect exchange rate and non-withdrawable pool liquidity, + /// batch an atomic call with [`Pallet::add_liquidity`] and + /// [`Pallet::swap_exact_tokens_for_tokens`] or [`Pallet::swap_tokens_for_exact_tokens`] + /// calls to render the liquidity withdrawable and rectify the exchange rate. + /// /// Once liquidity is added, someone may successfully call /// [`Pallet::swap_exact_tokens_for_tokens`] successfully. #[pallet::call_index(1)] diff --git a/substrate/frame/asset-conversion/src/mock.rs b/substrate/frame/asset-conversion/src/mock.rs index 870538a68cc7105802672972bdf394e36f7bbc87..4591b87c1867862e87a16e86c0d96a060bd1b7ff 100644 --- a/substrate/frame/asset-conversion/src/mock.rs +++ b/substrate/frame/asset-conversion/src/mock.rs @@ -53,7 +53,7 @@ construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type AccountId = u128; type Lookup = IdentityLookup; diff --git a/substrate/frame/asset-rate/src/lib.rs b/substrate/frame/asset-rate/src/lib.rs index befabfe54aa0e766d498eccf2201de4e9c0f2fc9..69f8267a4f25fe2fa82113eb6d9673b05b4ab6aa 100644 --- a/substrate/frame/asset-rate/src/lib.rs +++ b/substrate/frame/asset-rate/src/lib.rs @@ -112,7 +112,7 @@ pub mod pallet { /// The origin permissioned to remove an existing conversion rate for an asset. type RemoveOrigin: EnsureOrigin; - /// The origin permissioned to update an existiing conversion rate for an asset. + /// The origin permissioned to update an existing conversion rate for an asset. type UpdateOrigin: EnsureOrigin; /// The currency mechanism for this pallet. diff --git a/substrate/frame/asset-rate/src/mock.rs b/substrate/frame/asset-rate/src/mock.rs index 5981b05676414e9ec5251be0bcf2e554d589107a..d01996dab193d6d6e21a14efed7f14ff2ef8c931 100644 --- a/substrate/frame/asset-rate/src/mock.rs +++ b/substrate/frame/asset-rate/src/mock.rs @@ -32,7 +32,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; diff --git a/substrate/frame/assets/src/lib.rs b/substrate/frame/assets/src/lib.rs index cafe7bb1a3b53df4b48d2a1b9a45cdf14c18cea8..c5468e4237df1fd5d936ef9cca2f96336758241f 100644 --- a/substrate/frame/assets/src/lib.rs +++ b/substrate/frame/assets/src/lib.rs @@ -183,7 +183,7 @@ pub use weights::WeightInfo; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; const LOG_TARGET: &str = "runtime::assets"; -/// Trait with callbacks that are executed after successfull asset creation or destruction. +/// Trait with callbacks that are executed after successful asset creation or destruction. pub trait AssetsCallback { /// Indicates that asset with `id` was successfully created by the `owner` fn created(_id: &AssetId, _owner: &AccountId) -> Result<(), ()> { @@ -208,7 +208,7 @@ pub mod pallet { }; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] @@ -226,10 +226,42 @@ pub mod pallet { } } - #[pallet::config] + /// Default implementations of [`DefaultConfig`], which can be used to implement [`Config`]. + pub mod config_preludes { + use super::*; + use frame_support::{derive_impl, traits::ConstU64}; + pub struct TestDefaultConfig; + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)] + impl frame_system::DefaultConfig for TestDefaultConfig {} + + #[frame_support::register_default_impl(TestDefaultConfig)] + impl DefaultConfig for TestDefaultConfig { + #[inject_runtime_type] + type RuntimeEvent = (); + type Balance = u64; + type RemoveItemsLimit = ConstU32<5>; + type AssetId = u32; + type AssetIdParameter = u32; + type AssetDeposit = ConstU64<1>; + type AssetAccountDeposit = ConstU64<10>; + type MetadataDepositBase = ConstU64<1>; + type MetadataDepositPerByte = ConstU64<1>; + type ApprovalDeposit = ConstU64<1>; + type StringLimit = ConstU32<50>; + type Extra = (); + type CallbackHandle = (); + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); + } + } + + #[pallet::config(with_default)] /// The module configuration trait. pub trait Config: frame_system::Config { /// The overarching event type. + #[pallet::no_default_bounds] type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -262,10 +294,12 @@ pub mod pallet { type AssetIdParameter: Parameter + From + Into + MaxEncodedLen; /// The currency mechanism. + #[pallet::no_default] type Currency: ReservableCurrency; /// Standard asset class creation is only allowed if the origin attempting it and the /// asset class are in this set. + #[pallet::no_default] type CreateOrigin: EnsureOriginWithArg< Self::RuntimeOrigin, Self::AssetId, @@ -274,28 +308,34 @@ pub mod pallet { /// The origin which may forcibly create or destroy an asset or otherwise alter privileged /// attributes. + #[pallet::no_default] type ForceOrigin: EnsureOrigin; /// The basic amount of funds that must be reserved for an asset. #[pallet::constant] + #[pallet::no_default_bounds] type AssetDeposit: Get>; /// The amount of funds that must be reserved for a non-provider asset account to be /// maintained. #[pallet::constant] + #[pallet::no_default_bounds] type AssetAccountDeposit: Get>; /// The basic amount of funds that must be reserved when adding metadata to your asset. #[pallet::constant] + #[pallet::no_default_bounds] type MetadataDepositBase: Get>; /// The additional funds that must be reserved for the number of bytes you store in your /// metadata. #[pallet::constant] + #[pallet::no_default_bounds] type MetadataDepositPerByte: Get>; /// The amount of funds that must be reserved when creating a new approval. #[pallet::constant] + #[pallet::no_default_bounds] type ApprovalDeposit: Get>; /// The maximum length of a name or symbol stored on-chain. @@ -304,6 +344,7 @@ pub mod pallet { /// A hook to allow a per-asset, per-account minimum balance to be enforced. This must be /// respected in all permissionless operations. + #[pallet::no_default] type Freezer: FrozenBalance; /// Additional data to be stored with an account's asset balance. diff --git a/substrate/frame/assets/src/migration.rs b/substrate/frame/assets/src/migration.rs index ff0ffbff0d362fbd630e22dcb293f28e2b75469c..dd7c12293e80f410301bb47cca013ae4f013e72f 100644 --- a/substrate/frame/assets/src/migration.rs +++ b/substrate/frame/assets/src/migration.rs @@ -67,9 +67,9 @@ pub mod v1 { pub struct MigrateToV1(core::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { fn on_runtime_upgrade() -> Weight { - let current_version = Pallet::::current_storage_version(); - let onchain_version = Pallet::::on_chain_storage_version(); - if onchain_version == 0 && current_version == 1 { + let in_code_version = Pallet::::in_code_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); + if on_chain_version == 0 && in_code_version == 1 { let mut translated = 0u64; Asset::::translate::< OldAssetDetails>, @@ -78,12 +78,12 @@ pub mod v1 { translated.saturating_inc(); Some(old_value.migrate_to_v1()) }); - current_version.put::>(); + in_code_version.put::>(); log::info!( target: LOG_TARGET, "Upgraded {} pools, storage to version {:?}", translated, - current_version + in_code_version ); T::DbWeight::get().reads_writes(translated + 1, translated + 1) } else { @@ -116,13 +116,13 @@ pub mod v1 { "the asset count before and after the migration should be the same" ); - let current_version = Pallet::::current_storage_version(); - let onchain_version = Pallet::::on_chain_storage_version(); + let in_code_version = Pallet::::in_code_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); - frame_support::ensure!(current_version == 1, "must_upgrade"); + frame_support::ensure!(in_code_version == 1, "must_upgrade"); ensure!( - current_version == onchain_version, - "after migration, the current_version and onchain_version should be the same" + in_code_version == on_chain_version, + "after migration, the in_code_version and on_chain_version should be the same" ); Asset::::iter().try_for_each(|(_id, asset)| -> Result<(), TryRuntimeError> { diff --git a/substrate/frame/assets/src/mock.rs b/substrate/frame/assets/src/mock.rs index e1722200c35d4c3674db8a56dbee092f1f7ff8a8..f6173a451fffaee41f66dc6f697a40cd68e3648b 100644 --- a/substrate/frame/assets/src/mock.rs +++ b/substrate/frame/assets/src/mock.rs @@ -42,7 +42,7 @@ construct_runtime!( type AccountId = u64; type AssetId = u32; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; @@ -108,27 +108,13 @@ impl AssetsCallbackHandle { } } +#[derive_impl(crate::config_preludes::TestDefaultConfig)] impl Config for Test { - type RuntimeEvent = RuntimeEvent; - type Balance = u64; - type AssetId = u32; - type AssetIdParameter = u32; type Currency = Balances; type CreateOrigin = AsEnsureOriginWithArg>; type ForceOrigin = frame_system::EnsureRoot; - type AssetDeposit = ConstU64<1>; - type AssetAccountDeposit = ConstU64<10>; - type MetadataDepositBase = ConstU64<1>; - type MetadataDepositPerByte = ConstU64<1>; - type ApprovalDeposit = ConstU64<1>; - type StringLimit = ConstU32<50>; type Freezer = TestFreezer; - type WeightInfo = (); type CallbackHandle = AssetsCallbackHandle; - type Extra = (); - type RemoveItemsLimit = ConstU32<5>; - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = (); } use std::collections::HashMap; diff --git a/substrate/frame/assets/src/tests.rs b/substrate/frame/assets/src/tests.rs index e09648a51eccd09408b4549cc28315c8052d9ae5..c7021bcad531046c8881a8a9ef8932f4bd54c011 100644 --- a/substrate/frame/assets/src/tests.rs +++ b/substrate/frame/assets/src/tests.rs @@ -1453,7 +1453,7 @@ fn force_asset_status_should_work() { )); assert_eq!(Assets::balance(0, 1), 50); - // account can recieve assets for balance < min_balance + // account can receive assets for balance < min_balance assert_ok!(Assets::transfer(RuntimeOrigin::signed(2), 0, 1, 1)); assert_eq!(Assets::balance(0, 1), 51); diff --git a/substrate/frame/atomic-swap/src/tests.rs b/substrate/frame/atomic-swap/src/tests.rs index 4b444d888ed5e228a4951258092b0bdbdcf6e597..9f51f04208aaccca281398b0382718f2165d8a0e 100644 --- a/substrate/frame/atomic-swap/src/tests.rs +++ b/substrate/frame/atomic-swap/src/tests.rs @@ -37,7 +37,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; diff --git a/substrate/frame/aura/src/lib.rs b/substrate/frame/aura/src/lib.rs index f7506db05d1d82f32a4ec27a20096bb2aaf7548a..3ca1444aaae9b61511a33ed6a9dc9075a2e8c864 100644 --- a/substrate/frame/aura/src/lib.rs +++ b/substrate/frame/aura/src/lib.rs @@ -66,9 +66,6 @@ const LOG_TARGET: &str = "runtime::aura"; /// /// This was the default behavior of the Aura pallet and may be used for /// backwards compatibility. -/// -/// Note that this type is likely not useful without the `experimental` -/// feature. pub struct MinimumPeriodTimesTwo(sp_std::marker::PhantomData); impl Get for MinimumPeriodTimesTwo { @@ -117,10 +114,7 @@ pub mod pallet { /// The effective value of this type should not change while the chain is running. /// /// For backwards compatibility either use [`MinimumPeriodTimesTwo`] or a const. - /// - /// This associated type is only present when compiled with the `experimental` - /// feature. - #[cfg(feature = "experimental")] + #[pallet::constant] type SlotDuration: Get<::Moment>; } @@ -168,16 +162,14 @@ pub mod pallet { /// The current authority set. #[pallet::storage] - #[pallet::getter(fn authorities)] - pub(super) type Authorities = + pub type Authorities = StorageValue<_, BoundedVec, ValueQuery>; /// The current slot of this block. /// /// This will be set in `on_initialize`. #[pallet::storage] - #[pallet::getter(fn current_slot)] - pub(super) type CurrentSlot = StorageValue<_, Slot, ValueQuery>; + pub type CurrentSlot = StorageValue<_, Slot, ValueQuery>; #[pallet::genesis_config] #[derive(frame_support::DefaultNoBound)] @@ -250,17 +242,7 @@ impl Pallet { /// Determine the Aura slot-duration based on the Timestamp module configuration. pub fn slot_duration() -> T::Moment { - #[cfg(feature = "experimental")] - { - T::SlotDuration::get() - } - - #[cfg(not(feature = "experimental"))] - { - // we double the minimum block-period so each author can always propose within - // the majority of its slot. - ::MinimumPeriod::get().saturating_mul(2u32.into()) - } + T::SlotDuration::get() } /// Ensure the correctness of the state of this pallet. @@ -335,7 +317,7 @@ impl OneSessionHandler for Pallet { // instant changes if changed { let next_authorities = validators.map(|(_, k)| k).collect::>(); - let last_authorities = Self::authorities(); + let last_authorities = Authorities::::get(); if last_authorities != next_authorities { if next_authorities.len() as u32 > T::MaxAuthorities::get() { log::warn!( @@ -391,7 +373,7 @@ impl> FindAuthor { let i = Inner::find_author(digests)?; - let validators = >::authorities(); + let validators = Authorities::::get(); validators.get(i as usize).cloned() } } @@ -401,7 +383,7 @@ pub type AuraAuthorId = FindAccountFromAuthorIndex>; impl IsMember for Pallet { fn is_member(authority_id: &T::AuthorityId) -> bool { - Self::authorities().iter().any(|id| id == authority_id) + Authorities::::get().iter().any(|id| id == authority_id) } } diff --git a/substrate/frame/aura/src/mock.rs b/substrate/frame/aura/src/mock.rs index 8bc3e407158317164730e839bf954fd5f8e5e597..786e6e2efafb5186cbb0228476c0003c74245039 100644 --- a/substrate/frame/aura/src/mock.rs +++ b/substrate/frame/aura/src/mock.rs @@ -40,7 +40,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } @@ -84,8 +84,6 @@ impl pallet_aura::Config for Test { type DisabledValidators = MockDisabledValidators; type MaxAuthorities = ConstU32<10>; type AllowMultipleBlocksPerSlot = AllowMultipleBlocksPerSlot; - - #[cfg(feature = "experimental")] type SlotDuration = ConstU64; } diff --git a/substrate/frame/aura/src/tests.rs b/substrate/frame/aura/src/tests.rs index b3a5e144fad8e300059159e61ad8412e84e38d2d..5374105a2f3b2f286e7ad9afa835a260d7a7520b 100644 --- a/substrate/frame/aura/src/tests.rs +++ b/substrate/frame/aura/src/tests.rs @@ -19,7 +19,8 @@ #![cfg(test)] -use crate::mock::{build_ext_and_execute_test, Aura, MockDisabledValidators, System}; +use super::pallet; +use crate::mock::{build_ext_and_execute_test, Aura, MockDisabledValidators, System, Test}; use codec::Encode; use frame_support::traits::OnInitialize; use sp_consensus_aura::{Slot, AURA_ENGINE_ID}; @@ -28,8 +29,8 @@ use sp_runtime::{Digest, DigestItem}; #[test] fn initial_values() { build_ext_and_execute_test(vec![0, 1, 2, 3], || { - assert_eq!(Aura::current_slot(), 0u64); - assert_eq!(Aura::authorities().len(), Aura::authorities_len()); + assert_eq!(pallet::CurrentSlot::::get(), 0u64); + assert_eq!(pallet::Authorities::::get().len(), Aura::authorities_len()); assert_eq!(Aura::authorities_len(), 4); }); } diff --git a/substrate/frame/authority-discovery/src/lib.rs b/substrate/frame/authority-discovery/src/lib.rs index 2b4dfaf1aea8c804af7b4893be7057c53c347896..ed9240d99e8d5bb2b814fe80e0211c601378651c 100644 --- a/substrate/frame/authority-discovery/src/lib.rs +++ b/substrate/frame/authority-discovery/src/lib.rs @@ -144,19 +144,21 @@ impl OneSessionHandler for Pallet { ); Keys::::put(bounded_keys); + } - let next_keys = queued_validators.map(|x| x.1).collect::>(); + // `changed` represents if queued_validators changed in the previous session not in the + // current one. + let next_keys = queued_validators.map(|x| x.1).collect::>(); - let next_bounded_keys = WeakBoundedVec::<_, T::MaxAuthorities>::force_from( - next_keys, - Some( - "Warning: The session has more queued validators than expected. \ - A runtime configuration adjustment may be needed.", - ), - ); + let next_bounded_keys = WeakBoundedVec::<_, T::MaxAuthorities>::force_from( + next_keys, + Some( + "Warning: The session has more queued validators than expected. \ + A runtime configuration adjustment may be needed.", + ), + ); - NextKeys::::put(next_bounded_keys); - } + NextKeys::::put(next_bounded_keys); } fn on_disabled(_i: u32) { @@ -222,7 +224,7 @@ mod tests { pub const Offset: BlockNumber = 0; } - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type AccountId = AuthorityId; type Lookup = IdentityLookup; @@ -270,7 +272,7 @@ mod tests { .map(|id| (&account_id, id)) .collect::>(); - let mut third_authorities: Vec = vec![4, 5] + let third_authorities: Vec = vec![4, 5] .into_iter() .map(|i| AuthorityPair::from_seed_slice(vec![i; 32].as_ref()).unwrap().public()) .map(AuthorityId::from) @@ -282,6 +284,18 @@ mod tests { .map(|id| (&account_id, id)) .collect::>(); + let mut fourth_authorities: Vec = vec![6, 7] + .into_iter() + .map(|i| AuthorityPair::from_seed_slice(vec![i; 32].as_ref()).unwrap().public()) + .map(AuthorityId::from) + .collect(); + // Needed for `pallet_session::OneSessionHandler::on_new_session`. + let fourth_authorities_and_account_ids = fourth_authorities + .clone() + .into_iter() + .map(|id| (&account_id, id)) + .collect::>(); + // Build genesis. let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); @@ -310,25 +324,33 @@ mod tests { third_authorities_and_account_ids.clone().into_iter(), ); let authorities_returned = AuthorityDiscovery::authorities(); + let mut first_and_third_authorities = first_authorities + .iter() + .chain(third_authorities.iter()) + .cloned() + .collect::>(); + first_and_third_authorities.sort(); + assert_eq!( - first_authorities, authorities_returned, + first_and_third_authorities, authorities_returned, "Expected authority set not to change as `changed` was set to false.", ); // When `changed` set to true, the authority set should be updated. AuthorityDiscovery::on_new_session( true, - second_authorities_and_account_ids.into_iter(), - third_authorities_and_account_ids.clone().into_iter(), + third_authorities_and_account_ids.into_iter(), + fourth_authorities_and_account_ids.clone().into_iter(), ); - let mut second_and_third_authorities = second_authorities + + let mut third_and_fourth_authorities = third_authorities .iter() - .chain(third_authorities.iter()) + .chain(fourth_authorities.iter()) .cloned() .collect::>(); - second_and_third_authorities.sort(); + third_and_fourth_authorities.sort(); assert_eq!( - second_and_third_authorities, + third_and_fourth_authorities, AuthorityDiscovery::authorities(), "Expected authority set to contain both the authorities of the new as well as the \ next session." @@ -337,12 +359,12 @@ mod tests { // With overlapping authority sets, `authorities()` should return a deduplicated set. AuthorityDiscovery::on_new_session( true, - third_authorities_and_account_ids.clone().into_iter(), - third_authorities_and_account_ids.clone().into_iter(), + fourth_authorities_and_account_ids.clone().into_iter(), + fourth_authorities_and_account_ids.clone().into_iter(), ); - third_authorities.sort(); + fourth_authorities.sort(); assert_eq!( - third_authorities, + fourth_authorities, AuthorityDiscovery::authorities(), "Expected authority set to be deduplicated." ); diff --git a/substrate/frame/authorship/src/lib.rs b/substrate/frame/authorship/src/lib.rs index 8b38a58d8e738d58dc0f7ff4565279034c3de099..d8f1baab23c8b11579c22a986dc65286f2e2508a 100644 --- a/substrate/frame/authorship/src/lib.rs +++ b/substrate/frame/authorship/src/lib.rs @@ -113,7 +113,7 @@ mod tests { } ); - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } diff --git a/substrate/frame/babe/src/benchmarking.rs b/substrate/frame/babe/src/benchmarking.rs index 92f55665913e24b8f254cf0d1610bc87c737a6e4..6b0e31e84718f4311421eec18755d562c7b24db9 100644 --- a/substrate/frame/babe/src/benchmarking.rs +++ b/substrate/frame/babe/src/benchmarking.rs @@ -31,7 +31,7 @@ benchmarks! { // NOTE: generated with the test below `test_generate_equivocation_report_blob`. // the output is not deterministic since keys are generated randomly (and therefore // signature content changes). it should not affect the benchmark. - // with the current benchmark setup it is not possible to generate this programatically + // with the current benchmark setup it is not possible to generate this programmatically // from the benchmark setup. const EQUIVOCATION_PROOF_BLOB: [u8; 416] = [ 222, 241, 46, 66, 243, 228, 135, 233, 177, 64, 149, 170, 141, 92, 193, 106, 51, 73, 31, diff --git a/substrate/frame/babe/src/lib.rs b/substrate/frame/babe/src/lib.rs index a6e44390dbc534e15e3a1658513c6ce9fa25088e..5fb107dde3bad06e6f4812d27ff1159a95194442 100644 --- a/substrate/frame/babe/src/lib.rs +++ b/substrate/frame/babe/src/lib.rs @@ -323,7 +323,7 @@ pub mod pallet { #[pallet::genesis_config] pub struct GenesisConfig { pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>, - pub epoch_config: Option, + pub epoch_config: BabeEpochConfiguration, #[serde(skip)] pub _config: sp_std::marker::PhantomData, } @@ -333,9 +333,7 @@ pub mod pallet { fn build(&self) { SegmentIndex::::put(0); Pallet::::initialize_genesis_authorities(&self.authorities); - EpochConfig::::put( - self.epoch_config.clone().expect("epoch_config must not be None"), - ); + EpochConfig::::put(&self.epoch_config); } } diff --git a/substrate/frame/babe/src/mock.rs b/substrate/frame/babe/src/mock.rs index b693f4fce9bde662c8badee8f0e675648597ea58..ec54275278eb625d9648b9fd744a3f7aab0a646a 100644 --- a/substrate/frame/babe/src/mock.rs +++ b/substrate/frame/babe/src/mock.rs @@ -63,7 +63,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; diff --git a/substrate/frame/bags-list/src/list/tests.rs b/substrate/frame/bags-list/src/list/tests.rs index fd4ad8f893af38aa29fe0573eb4c22d13be97037..cd39b083172670f0f8f7450ffc546b6fda187313 100644 --- a/substrate/frame/bags-list/src/list/tests.rs +++ b/substrate/frame/bags-list/src/list/tests.rs @@ -431,7 +431,7 @@ mod list { #[test] fn insert_at_unchecked_at_is_only_node() { // Note that this `insert_at_unchecked` test should fail post checks because node 42 does - // not get re-assigned the correct bagu pper. This is because `insert_at_unchecked` assumes + // not get re-assigned the correct bag upper. This is because `insert_at_unchecked` assumes // both nodes are already in the same bag with the correct bag upper. ExtBuilder::default().build_and_execute_no_post_check(|| { // given diff --git a/substrate/frame/bags-list/src/mock.rs b/substrate/frame/bags-list/src/mock.rs index 4282120983b1b366ef392eb1d6992876d0722ab8..ea677cb9e73e37b36cee813b372efe809b47375d 100644 --- a/substrate/frame/bags-list/src/mock.rs +++ b/substrate/frame/bags-list/src/mock.rs @@ -48,7 +48,7 @@ impl frame_election_provider_support::ScoreProvider for StakingMock { } } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type Block = Block; type AccountData = pallet_balances::AccountData; diff --git a/substrate/frame/balances/src/lib.rs b/substrate/frame/balances/src/lib.rs index 7dd087eabd6343df653905856c078d3f6f2a3908..80278752207dee2f54972b60fb2062e6e69ace0e 100644 --- a/substrate/frame/balances/src/lib.rs +++ b/substrate/frame/balances/src/lib.rs @@ -219,7 +219,7 @@ pub mod pallet { pub struct TestDefaultConfig; - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig, no_aggregated_types)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)] impl frame_system::DefaultConfig for TestDefaultConfig {} #[frame_support::register_default_impl(TestDefaultConfig)] @@ -320,7 +320,7 @@ pub mod pallet { type MaxFreezes: Get; } - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: frame_support::traits::StorageVersion = frame_support::traits::StorageVersion::new(1); @@ -677,7 +677,7 @@ pub mod pallet { /// /// This will waive the transaction fee if at least all but 10% of the accounts needed to /// be upgraded. (We let some not have to be upgraded just in order to allow for the - /// possibililty of churn). + /// possibility of churn). #[pallet::call_index(6)] #[pallet::weight(T::WeightInfo::upgrade_accounts(who.len() as u32))] pub fn upgrade_accounts( @@ -905,14 +905,14 @@ pub mod pallet { Self::try_mutate_account(who, |a, _| -> Result { Ok(f(a)) }) } - /// Returns `true` when `who` has some providers or `insecure_zero_ed` feature is disnabled. + /// Returns `true` when `who` has some providers or `insecure_zero_ed` feature is disabled. /// Returns `false` otherwise. #[cfg(not(feature = "insecure_zero_ed"))] fn have_providers_or_no_zero_ed(_: &T::AccountId) -> bool { true } - /// Returns `true` when `who` has some providers or `insecure_zero_ed` feature is disnabled. + /// Returns `true` when `who` has some providers or `insecure_zero_ed` feature is disabled. /// Returns `false` otherwise. #[cfg(feature = "insecure_zero_ed")] fn have_providers_or_no_zero_ed(who: &T::AccountId) -> bool { diff --git a/substrate/frame/balances/src/migration.rs b/substrate/frame/balances/src/migration.rs index ba6819ec6e814da69807c8717a088df2451b6cb5..38d9c07ff7e002f6a7a57189b7ebce6830b52c75 100644 --- a/substrate/frame/balances/src/migration.rs +++ b/substrate/frame/balances/src/migration.rs @@ -22,9 +22,9 @@ use frame_support::{ }; fn migrate_v0_to_v1, I: 'static>(accounts: &[T::AccountId]) -> Weight { - let onchain_version = Pallet::::on_chain_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); - if onchain_version == 0 { + if on_chain_version == 0 { let total = accounts .iter() .map(|a| Pallet::::total_balance(a)) @@ -76,9 +76,9 @@ impl, A: Get>, I: 'static> OnRuntimeUpgrade pub struct ResetInactive(PhantomData<(T, I)>); impl, I: 'static> OnRuntimeUpgrade for ResetInactive { fn on_runtime_upgrade() -> Weight { - let onchain_version = Pallet::::on_chain_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); - if onchain_version == 1 { + if on_chain_version == 1 { // Remove the old `StorageVersion` type. frame_support::storage::unhashed::kill(&frame_support::storage::storage_prefix( Pallet::::name().as_bytes(), diff --git a/substrate/frame/balances/src/tests/currency_tests.rs b/substrate/frame/balances/src/tests/currency_tests.rs index 46a4c4caefc3d72ad45adf58243f3430344d4813..bd4ff762c748bead206dd792e9b8ab891945494c 100644 --- a/substrate/frame/balances/src/tests/currency_tests.rs +++ b/substrate/frame/balances/src/tests/currency_tests.rs @@ -1024,7 +1024,7 @@ fn slash_consumed_slash_partial_works() { } #[test] -fn slash_on_non_existant_works() { +fn slash_on_non_existent_works() { ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { // Slash on non-existent account is okay. assert_eq!(Balances::slash(&12345, 1_300), (NegativeImbalance::new(0), 1300)); @@ -1071,7 +1071,7 @@ fn slash_reserved_overslash_does_not_touch_free_balance() { } #[test] -fn slash_reserved_on_non_existant_works() { +fn slash_reserved_on_non_existent_works() { ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { // Slash on non-existent account is okay. assert_eq!(Balances::slash_reserved(&12345, 1_300), (NegativeImbalance::new(0), 1300)); diff --git a/substrate/frame/balances/src/tests/mod.rs b/substrate/frame/balances/src/tests/mod.rs index 599909fa94351b748ce7ea0514cc574347ea6a7a..f2f107d8bd64ea4115b45fc29a84abfaff34ba92 100644 --- a/substrate/frame/balances/src/tests/mod.rs +++ b/substrate/frame/balances/src/tests/mod.rs @@ -90,12 +90,13 @@ parameter_types! { pub static ExistentialDeposit: u64 = 1; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = super::AccountData; } +#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)] impl pallet_transaction_payment::Config for Test { type RuntimeEvent = RuntimeEvent; type OnChargeTransaction = CurrencyAdapter, ()>; diff --git a/substrate/frame/balances/src/tests/reentrancy_tests.rs b/substrate/frame/balances/src/tests/reentrancy_tests.rs index 1afbe82c7e2a7b2c9eec65e0ad2f34a23aa025a3..717f04978577f1d82f1c743cbac988a6820f90bc 100644 --- a/substrate/frame/balances/src/tests/reentrancy_tests.rs +++ b/substrate/frame/balances/src/tests/reentrancy_tests.rs @@ -44,7 +44,7 @@ fn transfer_dust_removal_tst1_should_work() { assert_eq!(Balances::free_balance(&2), 0); // As expected beneficiary account 3 - // received the transfered fund. + // received the transferred fund. assert_eq!(Balances::free_balance(&3), 450); // Dust balance is deposited to account 1 @@ -123,7 +123,7 @@ fn repatriating_reserved_balance_dust_removal_should_work() { // Reserve a value on account 2, // Such that free balance is lower than - // Exestintial deposit. + // Existential deposit. assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(2), 1, 450)); // Since free balance of account 2 is lower than diff --git a/substrate/frame/beefy-mmr/src/lib.rs b/substrate/frame/beefy-mmr/src/lib.rs index fa3caba7977d31767253763aac1f1bbba5c7d67e..e423f1b342f2fc4faa9095c1cecf32556d0fd3cd 100644 --- a/substrate/frame/beefy-mmr/src/lib.rs +++ b/substrate/frame/beefy-mmr/src/lib.rs @@ -68,7 +68,7 @@ where ::BeefyId, >::MmrRoot(*root)), ); - >::deposit_log(digest); + frame_system::Pallet::::deposit_log(digest); } } @@ -126,7 +126,6 @@ pub mod pallet { /// Details of current BEEFY authority set. #[pallet::storage] - #[pallet::getter(fn beefy_authorities)] pub type BeefyAuthorities = StorageValue<_, BeefyAuthoritySet>, ValueQuery>; @@ -134,7 +133,6 @@ pub mod pallet { /// /// This storage entry is used as cache for calls to `update_beefy_next_authority_set`. #[pallet::storage] - #[pallet::getter(fn beefy_next_authorities)] pub type BeefyNextAuthorities = StorageValue<_, BeefyNextAuthoritySet>, ValueQuery>; } @@ -152,7 +150,7 @@ impl LeafDataProvider for Pallet { version: T::LeafVersion::get(), parent_number_and_hash: ParentNumberAndHash::::leaf_data(), leaf_extra: T::BeefyDataProvider::extra_data(), - beefy_next_authority_set: Pallet::::beefy_next_authorities(), + beefy_next_authority_set: BeefyNextAuthorities::::get(), } } } @@ -177,12 +175,12 @@ where impl Pallet { /// Return the currently active BEEFY authority set proof. pub fn authority_set_proof() -> BeefyAuthoritySet> { - Pallet::::beefy_authorities() + BeefyAuthorities::::get() } /// Return the next/queued BEEFY authority set proof. pub fn next_authority_set_proof() -> BeefyNextAuthoritySet> { - Pallet::::beefy_next_authorities() + BeefyNextAuthorities::::get() } /// Returns details of a BEEFY authority set. diff --git a/substrate/frame/beefy-mmr/src/mock.rs b/substrate/frame/beefy-mmr/src/mock.rs index 94149ac67765d79a378feb51d14c77649ea99bb0..9d1ece7a1d8ecf433f6d244e55fb1f6e6211e77e 100644 --- a/substrate/frame/beefy-mmr/src/mock.rs +++ b/substrate/frame/beefy-mmr/src/mock.rs @@ -57,7 +57,7 @@ construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } diff --git a/substrate/frame/beefy-mmr/src/tests.rs b/substrate/frame/beefy-mmr/src/tests.rs index ec756f83dffa41b51245dea38affc3dda8b871b5..fac799bf64e430c0908602697cc8f97f0a8c76c6 100644 --- a/substrate/frame/beefy-mmr/src/tests.rs +++ b/substrate/frame/beefy-mmr/src/tests.rs @@ -107,7 +107,7 @@ fn should_contain_valid_leaf_data() { let mut ext = new_test_ext(vec![1, 2, 3, 4]); let parent_hash = ext.execute_with(|| { init_block(1); - >::parent_hash() + frame_system::Pallet::::parent_hash() }); let mmr_leaf = read_mmr_leaf(&mut ext, node_offchain_key(0, parent_hash)); @@ -132,7 +132,7 @@ fn should_contain_valid_leaf_data() { // build second block on top let parent_hash = ext.execute_with(|| { init_block(2); - >::parent_hash() + frame_system::Pallet::::parent_hash() }); let mmr_leaf = read_mmr_leaf(&mut ext, node_offchain_key(1, parent_hash)); diff --git a/substrate/frame/beefy/src/equivocation.rs b/substrate/frame/beefy/src/equivocation.rs index 0a7ede327c9e672a6b455f724c6ed9852df21984..bbc6eae6af29d8b7beb41d903e6f9bf52af05763 100644 --- a/substrate/frame/beefy/src/equivocation.rs +++ b/substrate/frame/beefy/src/equivocation.rs @@ -190,7 +190,7 @@ where evidence: EquivocationEvidenceFor, ) -> Result<(), DispatchError> { let (equivocation_proof, key_owner_proof) = evidence; - let reporter = reporter.or_else(|| >::author()); + let reporter = reporter.or_else(|| pallet_authorship::Pallet::::author()); let offender = equivocation_proof.offender_id().clone(); // We check the equivocation within the context of its set id (and diff --git a/substrate/frame/beefy/src/lib.rs b/substrate/frame/beefy/src/lib.rs index 0760446753a688516a2c4ce75727ebb4b3cd06a8..87304eba8bab27a022d946c0e7d403ec9346dac4 100644 --- a/substrate/frame/beefy/src/lib.rs +++ b/substrate/frame/beefy/src/lib.rs @@ -120,20 +120,17 @@ pub mod pallet { /// The current authorities set #[pallet::storage] - #[pallet::getter(fn authorities)] - pub(super) type Authorities = + pub type Authorities = StorageValue<_, BoundedVec, ValueQuery>; /// The current validator set id #[pallet::storage] - #[pallet::getter(fn validator_set_id)] - pub(super) type ValidatorSetId = + pub type ValidatorSetId = StorageValue<_, sp_consensus_beefy::ValidatorSetId, ValueQuery>; /// Authorities set scheduled to be used with the next session #[pallet::storage] - #[pallet::getter(fn next_authorities)] - pub(super) type NextAuthorities = + pub type NextAuthorities = StorageValue<_, BoundedVec, ValueQuery>; /// A mapping from BEEFY set ID to the index of the *most recent* session for which its @@ -147,17 +144,14 @@ pub mod pallet { /// /// TWOX-NOTE: `ValidatorSetId` is not under user control. #[pallet::storage] - #[pallet::getter(fn session_for_set)] - pub(super) type SetIdSession = + pub type SetIdSession = StorageMap<_, Twox64Concat, sp_consensus_beefy::ValidatorSetId, SessionIndex>; /// Block number where BEEFY consensus is enabled/started. /// By changing this (through privileged `set_new_genesis()`), BEEFY consensus is effectively /// restarted from the newly set block number. #[pallet::storage] - #[pallet::getter(fn genesis_block)] - pub(super) type GenesisBlock = - StorageValue<_, Option>, ValueQuery>; + pub type GenesisBlock = StorageValue<_, Option>, ValueQuery>; #[pallet::genesis_config] pub struct GenesisConfig { @@ -186,7 +180,7 @@ pub mod pallet { // we panic here as runtime maintainers can simply reconfigure genesis and restart // the chain easily .expect("Authorities vec too big"); - >::put(&self.genesis_block); + GenesisBlock::::put(&self.genesis_block); } } @@ -303,8 +297,8 @@ pub mod pallet { impl Pallet { /// Return the current active BEEFY validator set. pub fn validator_set() -> Option> { - let validators: BoundedVec = Self::authorities(); - let id: sp_consensus_beefy::ValidatorSetId = Self::validator_set_id(); + let validators: BoundedVec = Authorities::::get(); + let id: sp_consensus_beefy::ValidatorSetId = ValidatorSetId::::get(); ValidatorSet::::new(validators, id) } @@ -326,19 +320,19 @@ impl Pallet { new: BoundedVec, queued: BoundedVec, ) { - >::put(&new); + Authorities::::put(&new); - let new_id = Self::validator_set_id() + 1u64; - >::put(new_id); + let new_id = ValidatorSetId::::get() + 1u64; + ValidatorSetId::::put(new_id); - >::put(&queued); + NextAuthorities::::put(&queued); if let Some(validator_set) = ValidatorSet::::new(new, new_id) { let log = DigestItem::Consensus( BEEFY_ENGINE_ID, ConsensusLog::AuthoritiesChange(validator_set.clone()).encode(), ); - >::deposit_log(log); + frame_system::Pallet::::deposit_log(log); let next_id = new_id + 1; if let Some(next_validator_set) = ValidatorSet::::new(queued, next_id) { @@ -355,7 +349,7 @@ impl Pallet { return Ok(()) } - if !>::get().is_empty() { + if !Authorities::::get().is_empty() { return Err(()) } @@ -364,10 +358,10 @@ impl Pallet { .map_err(|_| ())?; let id = GENESIS_AUTHORITY_SET_ID; - >::put(bounded_authorities); - >::put(id); + Authorities::::put(bounded_authorities); + ValidatorSetId::::put(id); // Like `pallet_session`, initialize the next validator set as well. - >::put(bounded_authorities); + NextAuthorities::::put(bounded_authorities); if let Some(validator_set) = ValidatorSet::::new(authorities.clone(), id) { let next_id = id + 1; @@ -442,9 +436,9 @@ where // We want to have at least one BEEFY mandatory block per session. Self::change_authorities(bounded_next_authorities, bounded_next_queued_authorities); - let validator_set_id = Self::validator_set_id(); + let validator_set_id = ValidatorSetId::::get(); // Update the mapping for the new set id that corresponds to the latest session (i.e. now). - let session_index = >::current_index(); + let session_index = pallet_session::Pallet::::current_index(); SetIdSession::::insert(validator_set_id, &session_index); // Prune old entry if limit reached. let max_set_id_session_entries = T::MaxSetIdSessionEntries::get().max(1); @@ -459,13 +453,13 @@ where ConsensusLog::::OnDisabled(i as AuthorityIndex).encode(), ); - >::deposit_log(log); + frame_system::Pallet::::deposit_log(log); } } impl IsMember for Pallet { fn is_member(authority_id: &T::BeefyId) -> bool { - Self::authorities().iter().any(|id| id == authority_id) + Authorities::::get().iter().any(|id| id == authority_id) } } diff --git a/substrate/frame/beefy/src/mock.rs b/substrate/frame/beefy/src/mock.rs index 9cce479890a48f7753be76b809f13abc1baa045d..fccc63bd1b45a9aded2cfcfa7e38222174aa2ad8 100644 --- a/substrate/frame/beefy/src/mock.rs +++ b/substrate/frame/beefy/src/mock.rs @@ -62,7 +62,7 @@ construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; diff --git a/substrate/frame/beefy/src/tests.rs b/substrate/frame/beefy/src/tests.rs index 453cf19a4fe156a76ed13a7da4fe59d95d4260f0..2950264e0c317f59cb2638a1851660865fe2e71a 100644 --- a/substrate/frame/beefy/src/tests.rs +++ b/substrate/frame/beefy/src/tests.rs @@ -31,7 +31,7 @@ use sp_consensus_beefy::{ }; use sp_runtime::DigestItem; -use crate::{mock::*, Call, Config, Error, Weight, WeightInfo}; +use crate::{self as beefy, mock::*, Call, Config, Error, Weight, WeightInfo}; fn init_block(block: u64) { System::set_block_number(block); @@ -48,15 +48,15 @@ fn genesis_session_initializes_authorities() { let want = authorities.clone(); new_test_ext_raw_authorities(authorities).execute_with(|| { - let authorities = Beefy::authorities(); + let authorities = beefy::Authorities::::get(); assert_eq!(authorities.len(), 4); assert_eq!(want[0], authorities[0]); assert_eq!(want[1], authorities[1]); - assert!(Beefy::validator_set_id() == 0); + assert!(beefy::ValidatorSetId::::get() == 0); - let next_authorities = Beefy::next_authorities(); + let next_authorities = beefy::NextAuthorities::::get(); assert_eq!(next_authorities.len(), 4); assert_eq!(want[0], next_authorities[0]); @@ -70,11 +70,11 @@ fn session_change_updates_authorities() { let want_validators = authorities.clone(); new_test_ext(vec![1, 2, 3, 4]).execute_with(|| { - assert!(0 == Beefy::validator_set_id()); + assert!(0 == beefy::ValidatorSetId::::get()); init_block(1); - assert!(1 == Beefy::validator_set_id()); + assert!(1 == beefy::ValidatorSetId::::get()); let want = beefy_log(ConsensusLog::AuthoritiesChange( ValidatorSet::new(want_validators, 1).unwrap(), @@ -85,7 +85,7 @@ fn session_change_updates_authorities() { init_block(2); - assert!(2 == Beefy::validator_set_id()); + assert!(2 == beefy::ValidatorSetId::::get()); let want = beefy_log(ConsensusLog::AuthoritiesChange( ValidatorSet::new(vec![mock_beefy_id(2), mock_beefy_id(4)], 2).unwrap(), @@ -101,7 +101,7 @@ fn session_change_updates_next_authorities() { let want = vec![mock_beefy_id(1), mock_beefy_id(2), mock_beefy_id(3), mock_beefy_id(4)]; new_test_ext(vec![1, 2, 3, 4]).execute_with(|| { - let next_authorities = Beefy::next_authorities(); + let next_authorities = beefy::NextAuthorities::::get(); assert_eq!(next_authorities.len(), 4); assert_eq!(want[0], next_authorities[0]); @@ -111,7 +111,7 @@ fn session_change_updates_next_authorities() { init_block(1); - let next_authorities = Beefy::next_authorities(); + let next_authorities = beefy::NextAuthorities::::get(); assert_eq!(next_authorities.len(), 2); assert_eq!(want[1], next_authorities[0]); @@ -177,7 +177,7 @@ fn cleans_up_old_set_id_session_mappings() { // we should have a session id mapping for all the set ids from // `max_set_id_session_entries` eras we have observed for i in 1..=max_set_id_session_entries { - assert!(Beefy::session_for_set(i as u64).is_some()); + assert!(beefy::SetIdSession::::get(i as u64).is_some()); } // go through another `max_set_id_session_entries` sessions @@ -185,12 +185,12 @@ fn cleans_up_old_set_id_session_mappings() { // we should keep tracking the new mappings for new sessions for i in max_set_id_session_entries + 1..=max_set_id_session_entries * 2 { - assert!(Beefy::session_for_set(i as u64).is_some()); + assert!(beefy::SetIdSession::::get(i as u64).is_some()); } // but the old ones should have been pruned by now for i in 1..=max_set_id_session_entries { - assert!(Beefy::session_for_set(i as u64).is_none()); + assert!(beefy::SetIdSession::::get(i as u64).is_none()); } }); } @@ -804,7 +804,7 @@ fn set_new_genesis_works() { assert_ok!(Beefy::set_new_genesis(RuntimeOrigin::root(), new_genesis_delay,)); let expected = System::block_number() + new_genesis_delay; // verify new genesis was set - assert_eq!(Beefy::genesis_block(), Some(expected)); + assert_eq!(beefy::GenesisBlock::::get(), Some(expected)); // setting delay < 1 should fail assert_err!( diff --git a/substrate/frame/benchmarking/pov/src/benchmarking.rs b/substrate/frame/benchmarking/pov/src/benchmarking.rs index 84d81890b1756fcb8f86298526ffece69e74b22a..7e6aa8e6bf605859fe3cc6bf0cd0b4bd3d56ccc1 100644 --- a/substrate/frame/benchmarking/pov/src/benchmarking.rs +++ b/substrate/frame/benchmarking/pov/src/benchmarking.rs @@ -21,54 +21,78 @@ use super::*; +use frame_benchmarking::v2::*; use frame_support::traits::UnfilteredDispatchable; use frame_system::{Pallet as System, RawOrigin}; use sp_runtime::traits::Hash; -frame_benchmarking::benchmarks! { - storage_single_value_read { +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn storage_single_value_read() { Value::::put(123); - }: { - assert_eq!(Value::::get(), Some(123)); + + #[block] + { + assert_eq!(Value::::get(), Some(123)); + } } - #[pov_mode = Ignored] - storage_single_value_ignored_read { + #[benchmark(pov_mode = Ignored)] + fn storage_single_value_ignored_read() { Value::::put(123); - }: { - assert_eq!(Value::::get(), Some(123)); + #[block] + { + assert_eq!(Value::::get(), Some(123)); + } } - #[pov_mode = MaxEncodedLen { + #[benchmark(pov_mode = MaxEncodedLen { Pov::Value2: Ignored - }] - storage_single_value_ignored_some_read { + })] + fn storage_single_value_ignored_some_read() { Value::::put(123); Value2::::put(123); - }: { - assert_eq!(Value::::get(), Some(123)); - assert_eq!(Value2::::get(), Some(123)); + + #[block] + { + assert_eq!(Value::::get(), Some(123)); + assert_eq!(Value2::::get(), Some(123)); + } } - storage_single_value_read_twice { + #[benchmark] + fn storage_single_value_read_twice() { Value::::put(123); - }: { - assert_eq!(Value::::get(), Some(123)); - assert_eq!(Value::::get(), Some(123)); + + #[block] + { + assert_eq!(Value::::get(), Some(123)); + assert_eq!(Value::::get(), Some(123)); + } } - storage_single_value_write { - }: { - Value::::put(123); - } verify { + #[benchmark] + fn storage_single_value_write() { + #[block] + { + Value::::put(123); + } + assert_eq!(Value::::get(), Some(123)); } - storage_single_value_kill { + #[benchmark] + fn storage_single_value_kill() { Value::::put(123); - }: { - Value::::kill(); - } verify { + + #[block] + { + Value::::kill(); + } + assert!(!Value::::exists()); } @@ -78,263 +102,297 @@ frame_benchmarking::benchmarks! { // created. Then the one value is read from the map. This demonstrates that the number of other // nodes in the Trie influences the proof size. The number of inserted nodes can be interpreted // as the number of `StorageMap`/`StorageValue` in the whole runtime. - #[pov_mode = Measured] - storage_1m_map_read_one_value_two_additional_layers { - (0..(1<<10)).for_each(|i| Map1M::::insert(i, i)); + #[benchmark(pov_mode = Measured)] + fn storage_1m_map_read_one_value_two_additional_layers() { + (0..(1 << 10)).for_each(|i| Map1M::::insert(i, i)); // Assume there are 16-256 other storage items. - (0..(1u32<<4)).for_each(|i| { + (0..(1u32 << 4)).for_each(|i| { let k = T::Hashing::hash(&i.to_be_bytes()); frame_support::storage::unhashed::put(k.as_ref(), &i); }); - }: { - assert_eq!(Map1M::::get(1<<9), Some(1<<9)); + + #[block] + { + assert_eq!(Map1M::::get(1 << 9), Some(1 << 9)); + } } - #[pov_mode = Measured] - storage_1m_map_read_one_value_three_additional_layers { - (0..(1<<10)).for_each(|i| Map1M::::insert(i, i)); + #[benchmark(pov_mode = Measured)] + fn storage_1m_map_read_one_value_three_additional_layers() { + (0..(1 << 10)).for_each(|i| Map1M::::insert(i, i)); // Assume there are 256-4096 other storage items. - (0..(1u32<<8)).for_each(|i| { + (0..(1u32 << 8)).for_each(|i| { let k = T::Hashing::hash(&i.to_be_bytes()); frame_support::storage::unhashed::put(k.as_ref(), &i); }); - }: { - assert_eq!(Map1M::::get(1<<9), Some(1<<9)); + + #[block] + { + assert_eq!(Map1M::::get(1 << 9), Some(1 << 9)); + } } - #[pov_mode = Measured] - storage_1m_map_read_one_value_four_additional_layers { - (0..(1<<10)).for_each(|i| Map1M::::insert(i, i)); + #[benchmark(pov_mode = Measured)] + fn storage_1m_map_read_one_value_four_additional_layers() { + (0..(1 << 10)).for_each(|i| Map1M::::insert(i, i)); // Assume there are 4096-65536 other storage items. - (0..(1u32<<12)).for_each(|i| { + (0..(1u32 << 12)).for_each(|i| { let k = T::Hashing::hash(&i.to_be_bytes()); frame_support::storage::unhashed::put(k.as_ref(), &i); }); - }: { - assert_eq!(Map1M::::get(1<<9), Some(1<<9)); + + #[block] + { + assert_eq!(Map1M::::get(1 << 9), Some(1 << 9)); + } } // Reads from both storage maps each `n` and `m` times. Should result in two linear components. - storage_map_read_per_component { - let n in 0 .. 100; - let m in 0 .. 100; + #[benchmark] + fn storage_map_read_per_component(n: Linear<0, 100>, m: Linear<0, 100>) { + (0..m * 10).for_each(|i| Map1M::::insert(i, i)); + (0..n * 10).for_each(|i| Map16M::::insert(i, i)); - (0..m*10).for_each(|i| Map1M::::insert(i, i)); - (0..n*10).for_each(|i| Map16M::::insert(i, i)); - }: { - (0..m).for_each(|i| - assert_eq!(Map1M::::get(i*10), Some(i*10))); - (0..n).for_each(|i| - assert_eq!(Map16M::::get(i*10), Some(i*10))); + #[block] + { + (0..m).for_each(|i| assert_eq!(Map1M::::get(i * 10), Some(i * 10))); + (0..n).for_each(|i| assert_eq!(Map16M::::get(i * 10), Some(i * 10))); + } } - #[pov_mode = MaxEncodedLen { + #[benchmark(pov_mode = MaxEncodedLen { Pov::Map1M: Ignored - }] - storage_map_read_per_component_one_ignored { - let n in 0 .. 100; - let m in 0 .. 100; + })] + fn storage_map_read_per_component_one_ignored(n: Linear<0, 100>, m: Linear<0, 100>) { + (0..m * 10).for_each(|i| Map1M::::insert(i, i)); + (0..n * 10).for_each(|i| Map16M::::insert(i, i)); - (0..m*10).for_each(|i| Map1M::::insert(i, i)); - (0..n*10).for_each(|i| Map16M::::insert(i, i)); - }: { - (0..m).for_each(|i| - assert_eq!(Map1M::::get(i*10), Some(i*10))); - (0..n).for_each(|i| - assert_eq!(Map16M::::get(i*10), Some(i*10))); + #[block] + { + (0..m).for_each(|i| assert_eq!(Map1M::::get(i * 10), Some(i * 10))); + (0..n).for_each(|i| assert_eq!(Map16M::::get(i * 10), Some(i * 10))); + } } // Reads the same value from a storage map. Should not result in a component. - storage_1m_map_one_entry_repeated_read { - let n in 0 .. 100; + #[benchmark] + fn storage_1m_map_one_entry_repeated_read(n: Linear<0, 100>) { Map1M::::insert(0, 0); - }: { - (0..n).for_each(|i| - assert_eq!(Map1M::::get(0), Some(0))); + + #[block] + { + (0..n).for_each(|_| assert_eq!(Map1M::::get(0), Some(0))); + } } // Reads the same values from a storage map. Should result in a `1x` linear component. - storage_1m_map_multiple_entry_repeated_read { - let n in 0 .. 100; + #[benchmark] + fn storage_1m_map_multiple_entry_repeated_read(n: Linear<0, 100>) { (0..n).for_each(|i| Map1M::::insert(i, i)); - }: { - (0..n).for_each(|i| { - // Reading the same value 10 times does nothing. - (0..10).for_each(|j| - assert_eq!(Map1M::::get(i), Some(i))); - }); + + #[block] + { + (0..n).for_each(|i| { + // Reading the same value 10 times does nothing. + (0..10).for_each(|_| assert_eq!(Map1M::::get(i), Some(i))); + }); + } } - storage_1m_double_map_read_per_component { - let n in 0 .. 1024; - (0..(1<<10)).for_each(|i| DoubleMap1M::::insert(i, i, i)); - }: { - (0..n).for_each(|i| - assert_eq!(DoubleMap1M::::get(i, i), Some(i))); + #[benchmark] + fn storage_1m_double_map_read_per_component(n: Linear<0, 1024>) { + (0..(1 << 10)).for_each(|i| DoubleMap1M::::insert(i, i, i)); + + #[block] + { + (0..n).for_each(|i| assert_eq!(DoubleMap1M::::get(i, i), Some(i))); + } } - storage_value_bounded_read { - }: { - assert!(BoundedValue::::get().is_none()); + #[benchmark] + fn storage_value_bounded_read() { + #[block] + { + assert!(BoundedValue::::get().is_none()); + } } // Reading unbounded values will produce no mathematical worst case PoV size for this component. - storage_value_unbounded_read { - }: { - assert!(UnboundedValue::::get().is_none()); + #[benchmark] + fn storage_value_unbounded_read() { + #[block] + { + assert!(UnboundedValue::::get().is_none()); + } } - #[pov_mode = Ignored] - storage_value_unbounded_ignored_read { - }: { - assert!(UnboundedValue::::get().is_none()); + #[benchmark(pov_mode = Ignored)] + fn storage_value_unbounded_ignored_read() { + #[block] + { + assert!(UnboundedValue::::get().is_none()); + } } // Same as above, but we still expect a mathematical worst case PoV size for the bounded one. - storage_value_bounded_and_unbounded_read { + #[benchmark] + fn storage_value_bounded_and_unbounded_read() { (0..1024).for_each(|i| Map1M::::insert(i, i)); - }: { - assert!(UnboundedValue::::get().is_none()); - assert!(BoundedValue::::get().is_none()); + #[block] + { + assert!(UnboundedValue::::get().is_none()); + assert!(BoundedValue::::get().is_none()); + } } - #[pov_mode = Measured] - measured_storage_value_read_linear_size { - let l in 0 .. 1<<22; + #[benchmark(pov_mode = Measured)] + fn measured_storage_value_read_linear_size(l: Linear<0, { 1 << 22 }>) { let v: sp_runtime::BoundedVec = sp_std::vec![0u8; l as usize].try_into().unwrap(); LargeValue::::put(&v); - }: { - assert!(LargeValue::::get().is_some()); + #[block] + { + assert!(LargeValue::::get().is_some()); + } } - #[pov_mode = MaxEncodedLen] - mel_storage_value_read_linear_size { - let l in 0 .. 1<<22; + #[benchmark(pov_mode = MaxEncodedLen)] + fn mel_storage_value_read_linear_size(l: Linear<0, { 1 << 22 }>) { let v: sp_runtime::BoundedVec = sp_std::vec![0u8; l as usize].try_into().unwrap(); LargeValue::::put(&v); - }: { - assert!(LargeValue::::get().is_some()); + #[block] + { + assert!(LargeValue::::get().is_some()); + } } - #[pov_mode = Measured] - measured_storage_double_value_read_linear_size { - let l in 0 .. 1<<22; + #[benchmark(pov_mode = Measured)] + fn measured_storage_double_value_read_linear_size(l: Linear<0, { 1 << 22 }>) { let v: sp_runtime::BoundedVec = sp_std::vec![0u8; l as usize].try_into().unwrap(); LargeValue::::put(&v); LargeValue2::::put(&v); - }: { - assert!(LargeValue::::get().is_some()); - assert!(LargeValue2::::get().is_some()); + #[block] + { + assert!(LargeValue::::get().is_some()); + assert!(LargeValue2::::get().is_some()); + } } - #[pov_mode = MaxEncodedLen] - mel_storage_double_value_read_linear_size { - let l in 0 .. 1<<22; + #[benchmark(pov_mode = MaxEncodedLen)] + fn mel_storage_double_value_read_linear_size(l: Linear<0, { 1 << 22 }>) { let v: sp_runtime::BoundedVec = sp_std::vec![0u8; l as usize].try_into().unwrap(); LargeValue::::put(&v); LargeValue2::::put(&v); - }: { - assert!(LargeValue::::get().is_some()); - assert!(LargeValue2::::get().is_some()); + #[block] + { + assert!(LargeValue::::get().is_some()); + assert!(LargeValue2::::get().is_some()); + } } - #[pov_mode = MaxEncodedLen { + #[benchmark(pov_mode = MaxEncodedLen { Pov::LargeValue2: Measured - }] - mel_mixed_storage_double_value_read_linear_size { - let l in 0 .. 1<<22; + })] + fn mel_mixed_storage_double_value_read_linear_size(l: Linear<0, { 1 << 22 }>) { let v: sp_runtime::BoundedVec = sp_std::vec![0u8; l as usize].try_into().unwrap(); LargeValue::::put(&v); LargeValue2::::put(&v); - }: { - assert!(LargeValue::::get().is_some()); - assert!(LargeValue2::::get().is_some()); + #[block] + { + assert!(LargeValue::::get().is_some()); + assert!(LargeValue2::::get().is_some()); + } } - #[pov_mode = Measured { + #[benchmark(pov_mode = Measured { Pov::LargeValue2: MaxEncodedLen - }] - measured_mixed_storage_double_value_read_linear_size { - let l in 0 .. 1<<22; + })] + fn measured_mixed_storage_double_value_read_linear_size(l: Linear<0, { 1 << 22 }>) { let v: sp_runtime::BoundedVec = sp_std::vec![0u8; l as usize].try_into().unwrap(); LargeValue::::put(&v); LargeValue2::::put(&v); - }: { - assert!(LargeValue::::get().is_some()); - assert!(LargeValue2::::get().is_some()); + #[block] + { + assert!(LargeValue::::get().is_some()); + assert!(LargeValue2::::get().is_some()); + } } - #[pov_mode = Measured] - storage_map_unbounded_both_measured_read { - let i in 0 .. 1000; - + #[benchmark(pov_mode = Measured)] + fn storage_map_unbounded_both_measured_read(i: Linear<0, 1000>) { UnboundedMap::::insert(i, sp_std::vec![0; i as usize]); UnboundedMap2::::insert(i, sp_std::vec![0; i as usize]); - }: { - assert!(UnboundedMap::::get(i).is_some()); - assert!(UnboundedMap2::::get(i).is_some()); + #[block] + { + assert!(UnboundedMap::::get(i).is_some()); + assert!(UnboundedMap2::::get(i).is_some()); + } } - #[pov_mode = MaxEncodedLen { + #[benchmark(pov_mode = MaxEncodedLen { Pov::UnboundedMap: Measured - }] - storage_map_partial_unbounded_read { - let i in 0 .. 1000; - + })] + fn storage_map_partial_unbounded_read(i: Linear<0, 1000>) { Map1M::::insert(i, 0); UnboundedMap::::insert(i, sp_std::vec![0; i as usize]); - }: { - assert!(Map1M::::get(i).is_some()); - assert!(UnboundedMap::::get(i).is_some()); + #[block] + { + assert!(Map1M::::get(i).is_some()); + assert!(UnboundedMap::::get(i).is_some()); + } } - #[pov_mode = MaxEncodedLen { + #[benchmark(pov_mode = MaxEncodedLen { Pov::UnboundedMap: Ignored - }] - storage_map_partial_unbounded_ignored_read { - let i in 0 .. 1000; - + })] + fn storage_map_partial_unbounded_ignored_read(i: Linear<0, 1000>) { Map1M::::insert(i, 0); UnboundedMap::::insert(i, sp_std::vec![0; i as usize]); - }: { - assert!(Map1M::::get(i).is_some()); - assert!(UnboundedMap::::get(i).is_some()); + #[block] + { + assert!(Map1M::::get(i).is_some()); + assert!(UnboundedMap::::get(i).is_some()); + } } // Emitting an event will not incur any PoV. - emit_event { + #[benchmark] + fn emit_event() { // Emit a single event. - let call = Call::::emit_event { }; - }: { call.dispatch_bypass_filter(RawOrigin::Root.into()).unwrap(); } - verify { + let call = Call::::emit_event {}; + #[block] + { + call.dispatch_bypass_filter(RawOrigin::Root.into()).unwrap(); + } assert_eq!(System::::events().len(), 1); } // A No-OP will not incur any PoV. - noop { - let call = Call::::noop { }; - }: { - call.dispatch_bypass_filter(RawOrigin::Root.into()).unwrap(); + #[benchmark] + fn noop() { + let call = Call::::noop {}; + #[block] + { + call.dispatch_bypass_filter(RawOrigin::Root.into()).unwrap(); + } } - storage_iteration { + #[benchmark] + fn storage_iteration() { for i in 0..65000 { UnboundedMapTwox::::insert(i, sp_std::vec![0; 64]); } - }: { - for (key, value) in UnboundedMapTwox::::iter() { - unsafe { - core::ptr::read_volatile(&key); - core::ptr::read_volatile(value.as_ptr()); + #[block] + { + for (key, value) in UnboundedMapTwox::::iter() { + unsafe { + core::ptr::read_volatile(&key); + core::ptr::read_volatile(value.as_ptr()); + } } } } - impl_benchmark_test_suite!( - Pallet, - mock::new_test_ext(), - mock::Test, - ); + impl_benchmark_test_suite!(Pallet, super::mock::new_test_ext(), super::mock::Test,); } #[cfg(test)] @@ -355,7 +413,7 @@ mod mock { } ); - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/frame/benchmarking/pov/src/tests.rs b/substrate/frame/benchmarking/pov/src/tests.rs index 7fa2fb97dea1b2e710e62a1d9950abe617798ad9..cec42057454f687cde26652db3e6506a2ae80d13 100644 --- a/substrate/frame/benchmarking/pov/src/tests.rs +++ b/substrate/frame/benchmarking/pov/src/tests.rs @@ -175,7 +175,7 @@ mod mock { } ); - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/frame/benchmarking/pov/src/weights.rs b/substrate/frame/benchmarking/pov/src/weights.rs index d84ac88c98f022d77a286c512f979eadb5c2d7a0..c4fc03d1dd937bba6ea0f83eec89c88704a6f31e 100644 --- a/substrate/frame/benchmarking/pov/src/weights.rs +++ b/substrate/frame/benchmarking/pov/src/weights.rs @@ -15,38 +15,36 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for frame_benchmarking_pallet_pov +//! Autogenerated weights for `frame_benchmarking_pallet_pov` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-12, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `i9`, CPU: `13th Gen Intel(R) Core(TM) i9-13900K` -//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 +//! HOSTNAME: `Olivers-MBP`, CPU: `` +//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: -// ./target/release/substrate +// target/release/substrate-node // benchmark // pallet -// --dev // --pallet // frame-benchmarking-pallet-pov // --extrinsic // -// --steps -// 50 -// --repeat -// 20 -// --template=.maintain/frame-weight-template.hbs -// --output=frame/benchmarking/pov/src/weights.rs +// --output +// substrate/frame/benchmarking/pov/src/weights.rs +// --template +// substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for frame_benchmarking_pallet_pov. +/// Weight functions needed for `frame_benchmarking_pallet_pov`. pub trait WeightInfo { fn storage_single_value_read() -> Weight; fn storage_single_value_ignored_read() -> Weight; @@ -80,361 +78,361 @@ pub trait WeightInfo { fn storage_iteration() -> Weight; } -/// Weights for frame_benchmarking_pallet_pov using the Substrate node and recommended hardware. +/// Weights for `frame_benchmarking_pallet_pov` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Pov Value (r:1 w:0) - /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `Pov::Value` (r:1 w:0) + /// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn storage_single_value_read() -> Weight { // Proof Size summary in bytes: // Measured: `136` // Estimated: `1489` - // Minimum execution time: 1_706_000 picoseconds. - Weight::from_parts(1_788_000, 1489) + // Minimum execution time: 3_000_000 picoseconds. + Weight::from_parts(3_000_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) } - /// Storage: Pov Value (r:1 w:0) - /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: Ignored) + /// Storage: `Pov::Value` (r:1 w:0) + /// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `Ignored`) fn storage_single_value_ignored_read() -> Weight { // Proof Size summary in bytes: // Measured: `136` // Estimated: `0` - // Minimum execution time: 1_661_000 picoseconds. - Weight::from_parts(1_718_000, 0) + // Minimum execution time: 3_000_000 picoseconds. + Weight::from_parts(3_000_000, 0) .saturating_add(T::DbWeight::get().reads(1_u64)) } - /// Storage: Pov Value (r:1 w:0) - /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Pov Value2 (r:1 w:0) - /// Proof: Pov Value2 (max_values: Some(1), max_size: Some(4), added: 499, mode: Ignored) + /// Storage: `Pov::Value` (r:1 w:0) + /// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Pov::Value2` (r:1 w:0) + /// Proof: `Pov::Value2` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `Ignored`) fn storage_single_value_ignored_some_read() -> Weight { // Proof Size summary in bytes: // Measured: `160` // Estimated: `1489` - // Minimum execution time: 2_226_000 picoseconds. - Weight::from_parts(2_365_000, 1489) + // Minimum execution time: 4_000_000 picoseconds. + Weight::from_parts(4_000_000, 1489) .saturating_add(T::DbWeight::get().reads(2_u64)) } - /// Storage: Pov Value (r:1 w:0) - /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `Pov::Value` (r:1 w:0) + /// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn storage_single_value_read_twice() -> Weight { // Proof Size summary in bytes: // Measured: `136` // Estimated: `1489` - // Minimum execution time: 1_785_000 picoseconds. - Weight::from_parts(1_980_000, 1489) + // Minimum execution time: 3_000_000 picoseconds. + Weight::from_parts(4_000_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) } - /// Storage: Pov Value (r:0 w:1) - /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `Pov::Value` (r:0 w:1) + /// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn storage_single_value_write() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 254_000 picoseconds. - Weight::from_parts(326_000, 0) + // Minimum execution time: 0_000 picoseconds. + Weight::from_parts(1_000_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Pov Value (r:0 w:1) - /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `Pov::Value` (r:0 w:1) + /// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn storage_single_value_kill() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 239_000 picoseconds. - Weight::from_parts(277_000, 0) + // Minimum execution time: 0_000 picoseconds. + Weight::from_parts(1_000_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Pov Map1M (r:1 w:0) - /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Measured) + /// Storage: `Pov::Map1M` (r:1 w:0) + /// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `Measured`) fn storage_1m_map_read_one_value_two_additional_layers() -> Weight { // Proof Size summary in bytes: // Measured: `1275` // Estimated: `4740` - // Minimum execution time: 4_760_000 picoseconds. - Weight::from_parts(5_051_000, 4740) + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(7_000_000, 4740) .saturating_add(T::DbWeight::get().reads(1_u64)) } - /// Storage: Pov Map1M (r:1 w:0) - /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Measured) + /// Storage: `Pov::Map1M` (r:1 w:0) + /// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `Measured`) fn storage_1m_map_read_one_value_three_additional_layers() -> Weight { // Proof Size summary in bytes: // Measured: `1544` // Estimated: `5009` - // Minimum execution time: 5_490_000 picoseconds. - Weight::from_parts(5_703_000, 5009) + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(8_000_000, 5009) .saturating_add(T::DbWeight::get().reads(1_u64)) } - /// Storage: Pov Map1M (r:1 w:0) - /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Measured) + /// Storage: `Pov::Map1M` (r:1 w:0) + /// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `Measured`) fn storage_1m_map_read_one_value_four_additional_layers() -> Weight { // Proof Size summary in bytes: // Measured: `2044` // Estimated: `5509` - // Minimum execution time: 6_397_000 picoseconds. - Weight::from_parts(7_084_000, 5509) + // Minimum execution time: 9_000_000 picoseconds. + Weight::from_parts(10_000_000, 5509) .saturating_add(T::DbWeight::get().reads(1_u64)) } - /// Storage: Pov Map1M (r:100 w:0) - /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) - /// Storage: Pov Map16M (r:100 w:0) - /// Proof: Pov Map16M (max_values: Some(16000000), max_size: Some(36), added: 3006, mode: MaxEncodedLen) + /// Storage: `Pov::Map1M` (r:100 w:0) + /// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `Pov::Map16M` (r:100 w:0) + /// Proof: `Pov::Map16M` (`max_values`: Some(16000000), `max_size`: Some(36), added: 3006, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 100]`. /// The range of component `m` is `[0, 100]`. fn storage_map_read_per_component(n: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `515 + m * (188 ±0) + n * (188 ±0)` // Estimated: `990 + m * (2511 ±0) + n * (3006 ±0)` - // Minimum execution time: 181_481_000 picoseconds. - Weight::from_parts(129_275_141, 990) - // Standard Error: 13_049 - .saturating_add(Weight::from_parts(787_667, 0).saturating_mul(n.into())) - // Standard Error: 13_049 - .saturating_add(Weight::from_parts(830_378, 0).saturating_mul(m.into())) + // Minimum execution time: 342_000_000 picoseconds. + Weight::from_parts(179_688_624, 990) + // Standard Error: 26_526 + .saturating_add(Weight::from_parts(2_061_828, 0).saturating_mul(n.into())) + // Standard Error: 26_526 + .saturating_add(Weight::from_parts(1_825_923, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(Weight::from_parts(0, 2511).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 3006).saturating_mul(n.into())) } - /// Storage: Pov Map1M (r:100 w:0) - /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Ignored) - /// Storage: Pov Map16M (r:100 w:0) - /// Proof: Pov Map16M (max_values: Some(16000000), max_size: Some(36), added: 3006, mode: MaxEncodedLen) + /// Storage: `Pov::Map1M` (r:100 w:0) + /// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `Ignored`) + /// Storage: `Pov::Map16M` (r:100 w:0) + /// Proof: `Pov::Map16M` (`max_values`: Some(16000000), `max_size`: Some(36), added: 3006, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 100]`. /// The range of component `m` is `[0, 100]`. fn storage_map_read_per_component_one_ignored(n: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `515 + m * (188 ±0) + n * (188 ±0)` // Estimated: `1685 + m * (189 ±0) + n * (3006 ±0)` - // Minimum execution time: 181_925_000 picoseconds. - Weight::from_parts(134_416_814, 1685) - // Standard Error: 15_678 - .saturating_add(Weight::from_parts(827_168, 0).saturating_mul(n.into())) - // Standard Error: 15_678 - .saturating_add(Weight::from_parts(813_655, 0).saturating_mul(m.into())) + // Minimum execution time: 342_000_000 picoseconds. + Weight::from_parts(204_945_396, 1685) + // Standard Error: 25_217 + .saturating_add(Weight::from_parts(1_827_513, 0).saturating_mul(n.into())) + // Standard Error: 25_217 + .saturating_add(Weight::from_parts(1_661_271, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(Weight::from_parts(0, 189).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 3006).saturating_mul(n.into())) } - /// Storage: Pov Map1M (r:1 w:0) - /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: `Pov::Map1M` (r:1 w:0) + /// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 100]`. fn storage_1m_map_one_entry_repeated_read(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `170` // Estimated: `3501` - // Minimum execution time: 20_000 picoseconds. - Weight::from_parts(2_006_399, 3501) - // Standard Error: 808 - .saturating_add(Weight::from_parts(263_609, 0).saturating_mul(n.into())) + // Minimum execution time: 0_000 picoseconds. + Weight::from_parts(3_387_064, 3501) + // Standard Error: 1_445 + .saturating_add(Weight::from_parts(1_143_678, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) } - /// Storage: Pov Map1M (r:100 w:0) - /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: `Pov::Map1M` (r:100 w:0) + /// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 100]`. fn storage_1m_map_multiple_entry_repeated_read(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `147 + n * (40 ±0)` // Estimated: `990 + n * (2511 ±0)` - // Minimum execution time: 21_000 picoseconds. - Weight::from_parts(3_940_044, 990) - // Standard Error: 4_906 - .saturating_add(Weight::from_parts(3_454_882, 0).saturating_mul(n.into())) + // Minimum execution time: 0_000 picoseconds. + Weight::from_parts(1_323_684, 990) + // Standard Error: 10_546 + .saturating_add(Weight::from_parts(13_101_864, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2511).saturating_mul(n.into())) } - /// Storage: Pov DoubleMap1M (r:1024 w:0) - /// Proof: Pov DoubleMap1M (max_values: Some(1000000), max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: `Pov::DoubleMap1M` (r:1024 w:0) + /// Proof: `Pov::DoubleMap1M` (`max_values`: Some(1000000), `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1024]`. fn storage_1m_double_map_read_per_component(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `21938 + n * (57 ±0)` // Estimated: `990 + n * (2543 ±0)` - // Minimum execution time: 28_000 picoseconds. - Weight::from_parts(20_674_869, 990) - // Standard Error: 3_035 - .saturating_add(Weight::from_parts(1_995_730, 0).saturating_mul(n.into())) + // Minimum execution time: 0_000 picoseconds. + Weight::from_parts(39_703_963, 990) + // Standard Error: 10_589 + .saturating_add(Weight::from_parts(3_718_040, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2543).saturating_mul(n.into())) } - /// Storage: Pov BoundedValue (r:1 w:0) - /// Proof: Pov BoundedValue (max_values: Some(1), max_size: Some(33), added: 528, mode: MaxEncodedLen) + /// Storage: `Pov::BoundedValue` (r:1 w:0) + /// Proof: `Pov::BoundedValue` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) fn storage_value_bounded_read() -> Weight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1518` - // Minimum execution time: 1_091_000 picoseconds. - Weight::from_parts(1_181_000, 1518) + // Minimum execution time: 2_000_000 picoseconds. + Weight::from_parts(2_000_000, 1518) .saturating_add(T::DbWeight::get().reads(1_u64)) } - /// Storage: Pov UnboundedValue (r:1 w:0) - /// Proof Skipped: Pov UnboundedValue (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Pov::UnboundedValue` (r:1 w:0) + /// Proof: `Pov::UnboundedValue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn storage_value_unbounded_read() -> Weight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 1_079_000 picoseconds. - Weight::from_parts(1_176_000, 1594) + // Minimum execution time: 2_000_000 picoseconds. + Weight::from_parts(2_000_000, 1594) .saturating_add(T::DbWeight::get().reads(1_u64)) } - /// Storage: Pov UnboundedValue (r:1 w:0) - /// Proof Skipped: Pov UnboundedValue (max_values: Some(1), max_size: None, mode: Ignored) + /// Storage: `Pov::UnboundedValue` (r:1 w:0) + /// Proof: `Pov::UnboundedValue` (`max_values`: Some(1), `max_size`: None, mode: `Ignored`) fn storage_value_unbounded_ignored_read() -> Weight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `0` - // Minimum execution time: 1_101_000 picoseconds. - Weight::from_parts(1_160_000, 0) + // Minimum execution time: 2_000_000 picoseconds. + Weight::from_parts(2_000_000, 0) .saturating_add(T::DbWeight::get().reads(1_u64)) } - /// Storage: Pov UnboundedValue (r:1 w:0) - /// Proof Skipped: Pov UnboundedValue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Pov BoundedValue (r:1 w:0) - /// Proof: Pov BoundedValue (max_values: Some(1), max_size: Some(33), added: 528, mode: MaxEncodedLen) + /// Storage: `Pov::UnboundedValue` (r:1 w:0) + /// Proof: `Pov::UnboundedValue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Pov::BoundedValue` (r:1 w:0) + /// Proof: `Pov::BoundedValue` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) fn storage_value_bounded_and_unbounded_read() -> Weight { // Proof Size summary in bytes: // Measured: `147` // Estimated: `1632` - // Minimum execution time: 2_143_000 picoseconds. - Weight::from_parts(2_280_000, 1632) + // Minimum execution time: 3_000_000 picoseconds. + Weight::from_parts(5_000_000, 1632) .saturating_add(T::DbWeight::get().reads(2_u64)) } - /// Storage: Pov LargeValue (r:1 w:0) - /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) + /// Storage: `Pov::LargeValue` (r:1 w:0) + /// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`) /// The range of component `l` is `[0, 4194304]`. fn measured_storage_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `142 + l * (1 ±0)` // Estimated: `1626 + l * (1 ±0)` - // Minimum execution time: 1_665_000 picoseconds. - Weight::from_parts(1_725_000, 1626) - // Standard Error: 3 - .saturating_add(Weight::from_parts(376, 0).saturating_mul(l.into())) + // Minimum execution time: 3_000_000 picoseconds. + Weight::from_parts(3_000_000, 1626) + // Standard Error: 1 + .saturating_add(Weight::from_parts(393, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(l.into())) } - /// Storage: Pov LargeValue (r:1 w:0) - /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) + /// Storage: `Pov::LargeValue` (r:1 w:0) + /// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 4194304]`. fn mel_storage_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `142 + l * (1 ±0)` // Estimated: `4195793` - // Minimum execution time: 1_640_000 picoseconds. - Weight::from_parts(1_724_000, 4195793) - // Standard Error: 4 - .saturating_add(Weight::from_parts(395, 0).saturating_mul(l.into())) + // Minimum execution time: 3_000_000 picoseconds. + Weight::from_parts(3_000_000, 4195793) + // Standard Error: 1 + .saturating_add(Weight::from_parts(394, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) } - /// Storage: Pov LargeValue (r:1 w:0) - /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) - /// Storage: Pov LargeValue2 (r:1 w:0) - /// Proof: Pov LargeValue2 (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) + /// Storage: `Pov::LargeValue` (r:1 w:0) + /// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`) + /// Storage: `Pov::LargeValue2` (r:1 w:0) + /// Proof: `Pov::LargeValue2` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`) /// The range of component `l` is `[0, 4194304]`. fn measured_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `171 + l * (2 ±0)` // Estimated: `1655 + l * (2 ±0)` - // Minimum execution time: 2_263_000 picoseconds. - Weight::from_parts(2_358_000, 1655) - // Standard Error: 8 - .saturating_add(Weight::from_parts(737, 0).saturating_mul(l.into())) + // Minimum execution time: 4_000_000 picoseconds. + Weight::from_parts(4_000_000, 1655) + // Standard Error: 2 + .saturating_add(Weight::from_parts(655, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into())) } - /// Storage: Pov LargeValue (r:1 w:0) - /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) - /// Storage: Pov LargeValue2 (r:1 w:0) - /// Proof: Pov LargeValue2 (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) + /// Storage: `Pov::LargeValue` (r:1 w:0) + /// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`) + /// Storage: `Pov::LargeValue2` (r:1 w:0) + /// Proof: `Pov::LargeValue2` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 4194304]`. fn mel_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `171 + l * (2 ±0)` // Estimated: `4195793` - // Minimum execution time: 2_161_000 picoseconds. - Weight::from_parts(2_233_000, 4195793) - // Standard Error: 5 - .saturating_add(Weight::from_parts(639, 0).saturating_mul(l.into())) + // Minimum execution time: 4_000_000 picoseconds. + Weight::from_parts(4_000_000, 4195793) + // Standard Error: 2 + .saturating_add(Weight::from_parts(660, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) } - /// Storage: Pov LargeValue (r:1 w:0) - /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) - /// Storage: Pov LargeValue2 (r:1 w:0) - /// Proof: Pov LargeValue2 (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) + /// Storage: `Pov::LargeValue` (r:1 w:0) + /// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`) + /// Storage: `Pov::LargeValue2` (r:1 w:0) + /// Proof: `Pov::LargeValue2` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`) /// The range of component `l` is `[0, 4194304]`. fn mel_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `171 + l * (2 ±0)` // Estimated: `4195793 + l * (2 ±0)` - // Minimum execution time: 2_149_000 picoseconds. - Weight::from_parts(2_256_000, 4195793) - // Standard Error: 6 - .saturating_add(Weight::from_parts(677, 0).saturating_mul(l.into())) + // Minimum execution time: 4_000_000 picoseconds. + Weight::from_parts(4_000_000, 4195793) + // Standard Error: 4 + .saturating_add(Weight::from_parts(691, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into())) } - /// Storage: Pov LargeValue (r:1 w:0) - /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) - /// Storage: Pov LargeValue2 (r:1 w:0) - /// Proof: Pov LargeValue2 (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) + /// Storage: `Pov::LargeValue` (r:1 w:0) + /// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`) + /// Storage: `Pov::LargeValue2` (r:1 w:0) + /// Proof: `Pov::LargeValue2` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 4194304]`. fn measured_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `171 + l * (2 ±0)` // Estimated: `4195793 + l * (2 ±0)` - // Minimum execution time: 2_254_000 picoseconds. - Weight::from_parts(2_319_000, 4195793) - // Standard Error: 5 - .saturating_add(Weight::from_parts(664, 0).saturating_mul(l.into())) + // Minimum execution time: 4_000_000 picoseconds. + Weight::from_parts(4_000_000, 4195793) + // Standard Error: 4 + .saturating_add(Weight::from_parts(691, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into())) } - /// Storage: Pov UnboundedMap (r:1 w:0) - /// Proof Skipped: Pov UnboundedMap (max_values: None, max_size: None, mode: Measured) - /// Storage: Pov UnboundedMap2 (r:1 w:0) - /// Proof Skipped: Pov UnboundedMap2 (max_values: None, max_size: None, mode: Measured) + /// Storage: `Pov::UnboundedMap` (r:1 w:0) + /// Proof: `Pov::UnboundedMap` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Pov::UnboundedMap2` (r:1 w:0) + /// Proof: `Pov::UnboundedMap2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `i` is `[0, 1000]`. fn storage_map_unbounded_both_measured_read(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `229 + i * (8 ±0)` // Estimated: `3693 + i * (8 ±0)` - // Minimum execution time: 3_071_000 picoseconds. - Weight::from_parts(3_487_712, 3693) - // Standard Error: 26 - .saturating_add(Weight::from_parts(748, 0).saturating_mul(i.into())) + // Minimum execution time: 6_000_000 picoseconds. + Weight::from_parts(7_274_226, 3693) + // Standard Error: 280 + .saturating_add(Weight::from_parts(3_282, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(i.into())) } - /// Storage: Pov Map1M (r:1 w:0) - /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) - /// Storage: Pov UnboundedMap (r:1 w:0) - /// Proof Skipped: Pov UnboundedMap (max_values: None, max_size: None, mode: Measured) + /// Storage: `Pov::Map1M` (r:1 w:0) + /// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `Pov::UnboundedMap` (r:1 w:0) + /// Proof: `Pov::UnboundedMap` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `i` is `[0, 1000]`. fn storage_map_partial_unbounded_read(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `228 + i * (4 ±0)` // Estimated: `3692 + i * (4 ±0)` - // Minimum execution time: 3_150_000 picoseconds. - Weight::from_parts(3_582_963, 3692) - // Standard Error: 18 - .saturating_add(Weight::from_parts(380, 0).saturating_mul(i.into())) + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(7_507_333, 3692) + // Standard Error: 64 + .saturating_add(Weight::from_parts(982, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 4).saturating_mul(i.into())) } - /// Storage: Pov Map1M (r:1 w:0) - /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) - /// Storage: Pov UnboundedMap (r:1 w:0) - /// Proof Skipped: Pov UnboundedMap (max_values: None, max_size: None, mode: Ignored) + /// Storage: `Pov::Map1M` (r:1 w:0) + /// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `Pov::UnboundedMap` (r:1 w:0) + /// Proof: `Pov::UnboundedMap` (`max_values`: None, `max_size`: None, mode: `Ignored`) /// The range of component `i` is `[0, 1000]`. fn storage_map_partial_unbounded_ignored_read(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `228 + i * (4 ±0)` // Estimated: `3501 + i * (4 ±0)` - // Minimum execution time: 3_092_000 picoseconds. - Weight::from_parts(3_595_328, 3501) - // Standard Error: 20 - .saturating_add(Weight::from_parts(243, 0).saturating_mul(i.into())) + // Minimum execution time: 6_000_000 picoseconds. + Weight::from_parts(7_285_011, 3501) + // Standard Error: 80 + .saturating_add(Weight::from_parts(1_395, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 4).saturating_mul(i.into())) } @@ -442,382 +440,382 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_705_000 picoseconds. - Weight::from_parts(1_818_000, 0) + // Minimum execution time: 4_000_000 picoseconds. + Weight::from_parts(5_000_000, 0) } fn noop() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 533_000 picoseconds. - Weight::from_parts(587_000, 0) + // Minimum execution time: 2_000_000 picoseconds. + Weight::from_parts(2_000_000, 0) } - /// Storage: Pov UnboundedMapTwox (r:65001 w:0) - /// Proof Skipped: Pov UnboundedMapTwox (max_values: None, max_size: None, mode: Measured) + /// Storage: `Pov::UnboundedMapTwox` (r:65001 w:0) + /// Proof: `Pov::UnboundedMapTwox` (`max_values`: None, `max_size`: None, mode: `Measured`) fn storage_iteration() -> Weight { // Proof Size summary in bytes: // Measured: `17985289` // Estimated: `178863754` - // Minimum execution time: 118_753_057_000 picoseconds. - Weight::from_parts(121_396_503_000, 178863754) + // Minimum execution time: 218_275_000_000 picoseconds. + Weight::from_parts(222_603_000_000, 178863754) .saturating_add(T::DbWeight::get().reads(65001_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Pov Value (r:1 w:0) - /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `Pov::Value` (r:1 w:0) + /// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn storage_single_value_read() -> Weight { // Proof Size summary in bytes: // Measured: `136` // Estimated: `1489` - // Minimum execution time: 1_706_000 picoseconds. - Weight::from_parts(1_788_000, 1489) + // Minimum execution time: 3_000_000 picoseconds. + Weight::from_parts(3_000_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) } - /// Storage: Pov Value (r:1 w:0) - /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: Ignored) + /// Storage: `Pov::Value` (r:1 w:0) + /// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `Ignored`) fn storage_single_value_ignored_read() -> Weight { // Proof Size summary in bytes: // Measured: `136` // Estimated: `0` - // Minimum execution time: 1_661_000 picoseconds. - Weight::from_parts(1_718_000, 0) + // Minimum execution time: 3_000_000 picoseconds. + Weight::from_parts(3_000_000, 0) .saturating_add(RocksDbWeight::get().reads(1_u64)) } - /// Storage: Pov Value (r:1 w:0) - /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Pov Value2 (r:1 w:0) - /// Proof: Pov Value2 (max_values: Some(1), max_size: Some(4), added: 499, mode: Ignored) + /// Storage: `Pov::Value` (r:1 w:0) + /// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Pov::Value2` (r:1 w:0) + /// Proof: `Pov::Value2` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `Ignored`) fn storage_single_value_ignored_some_read() -> Weight { // Proof Size summary in bytes: // Measured: `160` // Estimated: `1489` - // Minimum execution time: 2_226_000 picoseconds. - Weight::from_parts(2_365_000, 1489) + // Minimum execution time: 4_000_000 picoseconds. + Weight::from_parts(4_000_000, 1489) .saturating_add(RocksDbWeight::get().reads(2_u64)) } - /// Storage: Pov Value (r:1 w:0) - /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `Pov::Value` (r:1 w:0) + /// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn storage_single_value_read_twice() -> Weight { // Proof Size summary in bytes: // Measured: `136` // Estimated: `1489` - // Minimum execution time: 1_785_000 picoseconds. - Weight::from_parts(1_980_000, 1489) + // Minimum execution time: 3_000_000 picoseconds. + Weight::from_parts(4_000_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) } - /// Storage: Pov Value (r:0 w:1) - /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `Pov::Value` (r:0 w:1) + /// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn storage_single_value_write() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 254_000 picoseconds. - Weight::from_parts(326_000, 0) + // Minimum execution time: 0_000 picoseconds. + Weight::from_parts(1_000_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Pov Value (r:0 w:1) - /// Proof: Pov Value (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `Pov::Value` (r:0 w:1) + /// Proof: `Pov::Value` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn storage_single_value_kill() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 239_000 picoseconds. - Weight::from_parts(277_000, 0) + // Minimum execution time: 0_000 picoseconds. + Weight::from_parts(1_000_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Pov Map1M (r:1 w:0) - /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Measured) + /// Storage: `Pov::Map1M` (r:1 w:0) + /// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `Measured`) fn storage_1m_map_read_one_value_two_additional_layers() -> Weight { // Proof Size summary in bytes: // Measured: `1275` // Estimated: `4740` - // Minimum execution time: 4_760_000 picoseconds. - Weight::from_parts(5_051_000, 4740) + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(7_000_000, 4740) .saturating_add(RocksDbWeight::get().reads(1_u64)) } - /// Storage: Pov Map1M (r:1 w:0) - /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Measured) + /// Storage: `Pov::Map1M` (r:1 w:0) + /// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `Measured`) fn storage_1m_map_read_one_value_three_additional_layers() -> Weight { // Proof Size summary in bytes: // Measured: `1544` // Estimated: `5009` - // Minimum execution time: 5_490_000 picoseconds. - Weight::from_parts(5_703_000, 5009) + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(8_000_000, 5009) .saturating_add(RocksDbWeight::get().reads(1_u64)) } - /// Storage: Pov Map1M (r:1 w:0) - /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Measured) + /// Storage: `Pov::Map1M` (r:1 w:0) + /// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `Measured`) fn storage_1m_map_read_one_value_four_additional_layers() -> Weight { // Proof Size summary in bytes: // Measured: `2044` // Estimated: `5509` - // Minimum execution time: 6_397_000 picoseconds. - Weight::from_parts(7_084_000, 5509) + // Minimum execution time: 9_000_000 picoseconds. + Weight::from_parts(10_000_000, 5509) .saturating_add(RocksDbWeight::get().reads(1_u64)) } - /// Storage: Pov Map1M (r:100 w:0) - /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) - /// Storage: Pov Map16M (r:100 w:0) - /// Proof: Pov Map16M (max_values: Some(16000000), max_size: Some(36), added: 3006, mode: MaxEncodedLen) + /// Storage: `Pov::Map1M` (r:100 w:0) + /// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `Pov::Map16M` (r:100 w:0) + /// Proof: `Pov::Map16M` (`max_values`: Some(16000000), `max_size`: Some(36), added: 3006, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 100]`. /// The range of component `m` is `[0, 100]`. fn storage_map_read_per_component(n: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `515 + m * (188 ±0) + n * (188 ±0)` // Estimated: `990 + m * (2511 ±0) + n * (3006 ±0)` - // Minimum execution time: 181_481_000 picoseconds. - Weight::from_parts(129_275_141, 990) - // Standard Error: 13_049 - .saturating_add(Weight::from_parts(787_667, 0).saturating_mul(n.into())) - // Standard Error: 13_049 - .saturating_add(Weight::from_parts(830_378, 0).saturating_mul(m.into())) + // Minimum execution time: 342_000_000 picoseconds. + Weight::from_parts(179_688_624, 990) + // Standard Error: 26_526 + .saturating_add(Weight::from_parts(2_061_828, 0).saturating_mul(n.into())) + // Standard Error: 26_526 + .saturating_add(Weight::from_parts(1_825_923, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(Weight::from_parts(0, 2511).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 3006).saturating_mul(n.into())) } - /// Storage: Pov Map1M (r:100 w:0) - /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: Ignored) - /// Storage: Pov Map16M (r:100 w:0) - /// Proof: Pov Map16M (max_values: Some(16000000), max_size: Some(36), added: 3006, mode: MaxEncodedLen) + /// Storage: `Pov::Map1M` (r:100 w:0) + /// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `Ignored`) + /// Storage: `Pov::Map16M` (r:100 w:0) + /// Proof: `Pov::Map16M` (`max_values`: Some(16000000), `max_size`: Some(36), added: 3006, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 100]`. /// The range of component `m` is `[0, 100]`. fn storage_map_read_per_component_one_ignored(n: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `515 + m * (188 ±0) + n * (188 ±0)` // Estimated: `1685 + m * (189 ±0) + n * (3006 ±0)` - // Minimum execution time: 181_925_000 picoseconds. - Weight::from_parts(134_416_814, 1685) - // Standard Error: 15_678 - .saturating_add(Weight::from_parts(827_168, 0).saturating_mul(n.into())) - // Standard Error: 15_678 - .saturating_add(Weight::from_parts(813_655, 0).saturating_mul(m.into())) + // Minimum execution time: 342_000_000 picoseconds. + Weight::from_parts(204_945_396, 1685) + // Standard Error: 25_217 + .saturating_add(Weight::from_parts(1_827_513, 0).saturating_mul(n.into())) + // Standard Error: 25_217 + .saturating_add(Weight::from_parts(1_661_271, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(Weight::from_parts(0, 189).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 3006).saturating_mul(n.into())) } - /// Storage: Pov Map1M (r:1 w:0) - /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: `Pov::Map1M` (r:1 w:0) + /// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 100]`. fn storage_1m_map_one_entry_repeated_read(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `170` // Estimated: `3501` - // Minimum execution time: 20_000 picoseconds. - Weight::from_parts(2_006_399, 3501) - // Standard Error: 808 - .saturating_add(Weight::from_parts(263_609, 0).saturating_mul(n.into())) + // Minimum execution time: 0_000 picoseconds. + Weight::from_parts(3_387_064, 3501) + // Standard Error: 1_445 + .saturating_add(Weight::from_parts(1_143_678, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) } - /// Storage: Pov Map1M (r:100 w:0) - /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: `Pov::Map1M` (r:100 w:0) + /// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 100]`. fn storage_1m_map_multiple_entry_repeated_read(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `147 + n * (40 ±0)` // Estimated: `990 + n * (2511 ±0)` - // Minimum execution time: 21_000 picoseconds. - Weight::from_parts(3_940_044, 990) - // Standard Error: 4_906 - .saturating_add(Weight::from_parts(3_454_882, 0).saturating_mul(n.into())) + // Minimum execution time: 0_000 picoseconds. + Weight::from_parts(1_323_684, 990) + // Standard Error: 10_546 + .saturating_add(Weight::from_parts(13_101_864, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2511).saturating_mul(n.into())) } - /// Storage: Pov DoubleMap1M (r:1024 w:0) - /// Proof: Pov DoubleMap1M (max_values: Some(1000000), max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: `Pov::DoubleMap1M` (r:1024 w:0) + /// Proof: `Pov::DoubleMap1M` (`max_values`: Some(1000000), `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1024]`. fn storage_1m_double_map_read_per_component(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `21938 + n * (57 ±0)` // Estimated: `990 + n * (2543 ±0)` - // Minimum execution time: 28_000 picoseconds. - Weight::from_parts(20_674_869, 990) - // Standard Error: 3_035 - .saturating_add(Weight::from_parts(1_995_730, 0).saturating_mul(n.into())) + // Minimum execution time: 0_000 picoseconds. + Weight::from_parts(39_703_963, 990) + // Standard Error: 10_589 + .saturating_add(Weight::from_parts(3_718_040, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2543).saturating_mul(n.into())) } - /// Storage: Pov BoundedValue (r:1 w:0) - /// Proof: Pov BoundedValue (max_values: Some(1), max_size: Some(33), added: 528, mode: MaxEncodedLen) + /// Storage: `Pov::BoundedValue` (r:1 w:0) + /// Proof: `Pov::BoundedValue` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) fn storage_value_bounded_read() -> Weight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1518` - // Minimum execution time: 1_091_000 picoseconds. - Weight::from_parts(1_181_000, 1518) + // Minimum execution time: 2_000_000 picoseconds. + Weight::from_parts(2_000_000, 1518) .saturating_add(RocksDbWeight::get().reads(1_u64)) } - /// Storage: Pov UnboundedValue (r:1 w:0) - /// Proof Skipped: Pov UnboundedValue (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Pov::UnboundedValue` (r:1 w:0) + /// Proof: `Pov::UnboundedValue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn storage_value_unbounded_read() -> Weight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 1_079_000 picoseconds. - Weight::from_parts(1_176_000, 1594) + // Minimum execution time: 2_000_000 picoseconds. + Weight::from_parts(2_000_000, 1594) .saturating_add(RocksDbWeight::get().reads(1_u64)) } - /// Storage: Pov UnboundedValue (r:1 w:0) - /// Proof Skipped: Pov UnboundedValue (max_values: Some(1), max_size: None, mode: Ignored) + /// Storage: `Pov::UnboundedValue` (r:1 w:0) + /// Proof: `Pov::UnboundedValue` (`max_values`: Some(1), `max_size`: None, mode: `Ignored`) fn storage_value_unbounded_ignored_read() -> Weight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `0` - // Minimum execution time: 1_101_000 picoseconds. - Weight::from_parts(1_160_000, 0) + // Minimum execution time: 2_000_000 picoseconds. + Weight::from_parts(2_000_000, 0) .saturating_add(RocksDbWeight::get().reads(1_u64)) } - /// Storage: Pov UnboundedValue (r:1 w:0) - /// Proof Skipped: Pov UnboundedValue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Pov BoundedValue (r:1 w:0) - /// Proof: Pov BoundedValue (max_values: Some(1), max_size: Some(33), added: 528, mode: MaxEncodedLen) + /// Storage: `Pov::UnboundedValue` (r:1 w:0) + /// Proof: `Pov::UnboundedValue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Pov::BoundedValue` (r:1 w:0) + /// Proof: `Pov::BoundedValue` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) fn storage_value_bounded_and_unbounded_read() -> Weight { // Proof Size summary in bytes: // Measured: `147` // Estimated: `1632` - // Minimum execution time: 2_143_000 picoseconds. - Weight::from_parts(2_280_000, 1632) + // Minimum execution time: 3_000_000 picoseconds. + Weight::from_parts(5_000_000, 1632) .saturating_add(RocksDbWeight::get().reads(2_u64)) } - /// Storage: Pov LargeValue (r:1 w:0) - /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) + /// Storage: `Pov::LargeValue` (r:1 w:0) + /// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`) /// The range of component `l` is `[0, 4194304]`. fn measured_storage_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `142 + l * (1 ±0)` // Estimated: `1626 + l * (1 ±0)` - // Minimum execution time: 1_665_000 picoseconds. - Weight::from_parts(1_725_000, 1626) - // Standard Error: 3 - .saturating_add(Weight::from_parts(376, 0).saturating_mul(l.into())) + // Minimum execution time: 3_000_000 picoseconds. + Weight::from_parts(3_000_000, 1626) + // Standard Error: 1 + .saturating_add(Weight::from_parts(393, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(l.into())) } - /// Storage: Pov LargeValue (r:1 w:0) - /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) + /// Storage: `Pov::LargeValue` (r:1 w:0) + /// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 4194304]`. fn mel_storage_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `142 + l * (1 ±0)` // Estimated: `4195793` - // Minimum execution time: 1_640_000 picoseconds. - Weight::from_parts(1_724_000, 4195793) - // Standard Error: 4 - .saturating_add(Weight::from_parts(395, 0).saturating_mul(l.into())) + // Minimum execution time: 3_000_000 picoseconds. + Weight::from_parts(3_000_000, 4195793) + // Standard Error: 1 + .saturating_add(Weight::from_parts(394, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) } - /// Storage: Pov LargeValue (r:1 w:0) - /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) - /// Storage: Pov LargeValue2 (r:1 w:0) - /// Proof: Pov LargeValue2 (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) + /// Storage: `Pov::LargeValue` (r:1 w:0) + /// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`) + /// Storage: `Pov::LargeValue2` (r:1 w:0) + /// Proof: `Pov::LargeValue2` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`) /// The range of component `l` is `[0, 4194304]`. fn measured_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `171 + l * (2 ±0)` // Estimated: `1655 + l * (2 ±0)` - // Minimum execution time: 2_263_000 picoseconds. - Weight::from_parts(2_358_000, 1655) - // Standard Error: 8 - .saturating_add(Weight::from_parts(737, 0).saturating_mul(l.into())) + // Minimum execution time: 4_000_000 picoseconds. + Weight::from_parts(4_000_000, 1655) + // Standard Error: 2 + .saturating_add(Weight::from_parts(655, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into())) } - /// Storage: Pov LargeValue (r:1 w:0) - /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) - /// Storage: Pov LargeValue2 (r:1 w:0) - /// Proof: Pov LargeValue2 (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) + /// Storage: `Pov::LargeValue` (r:1 w:0) + /// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`) + /// Storage: `Pov::LargeValue2` (r:1 w:0) + /// Proof: `Pov::LargeValue2` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 4194304]`. fn mel_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `171 + l * (2 ±0)` // Estimated: `4195793` - // Minimum execution time: 2_161_000 picoseconds. - Weight::from_parts(2_233_000, 4195793) - // Standard Error: 5 - .saturating_add(Weight::from_parts(639, 0).saturating_mul(l.into())) + // Minimum execution time: 4_000_000 picoseconds. + Weight::from_parts(4_000_000, 4195793) + // Standard Error: 2 + .saturating_add(Weight::from_parts(660, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) } - /// Storage: Pov LargeValue (r:1 w:0) - /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) - /// Storage: Pov LargeValue2 (r:1 w:0) - /// Proof: Pov LargeValue2 (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) + /// Storage: `Pov::LargeValue` (r:1 w:0) + /// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`) + /// Storage: `Pov::LargeValue2` (r:1 w:0) + /// Proof: `Pov::LargeValue2` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`) /// The range of component `l` is `[0, 4194304]`. fn mel_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `171 + l * (2 ±0)` // Estimated: `4195793 + l * (2 ±0)` - // Minimum execution time: 2_149_000 picoseconds. - Weight::from_parts(2_256_000, 4195793) - // Standard Error: 6 - .saturating_add(Weight::from_parts(677, 0).saturating_mul(l.into())) + // Minimum execution time: 4_000_000 picoseconds. + Weight::from_parts(4_000_000, 4195793) + // Standard Error: 4 + .saturating_add(Weight::from_parts(691, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into())) } - /// Storage: Pov LargeValue (r:1 w:0) - /// Proof: Pov LargeValue (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: Measured) - /// Storage: Pov LargeValue2 (r:1 w:0) - /// Proof: Pov LargeValue2 (max_values: Some(1), max_size: Some(4194308), added: 4194803, mode: MaxEncodedLen) + /// Storage: `Pov::LargeValue` (r:1 w:0) + /// Proof: `Pov::LargeValue` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `Measured`) + /// Storage: `Pov::LargeValue2` (r:1 w:0) + /// Proof: `Pov::LargeValue2` (`max_values`: Some(1), `max_size`: Some(4194308), added: 4194803, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 4194304]`. fn measured_mixed_storage_double_value_read_linear_size(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `171 + l * (2 ±0)` // Estimated: `4195793 + l * (2 ±0)` - // Minimum execution time: 2_254_000 picoseconds. - Weight::from_parts(2_319_000, 4195793) - // Standard Error: 5 - .saturating_add(Weight::from_parts(664, 0).saturating_mul(l.into())) + // Minimum execution time: 4_000_000 picoseconds. + Weight::from_parts(4_000_000, 4195793) + // Standard Error: 4 + .saturating_add(Weight::from_parts(691, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 2).saturating_mul(l.into())) } - /// Storage: Pov UnboundedMap (r:1 w:0) - /// Proof Skipped: Pov UnboundedMap (max_values: None, max_size: None, mode: Measured) - /// Storage: Pov UnboundedMap2 (r:1 w:0) - /// Proof Skipped: Pov UnboundedMap2 (max_values: None, max_size: None, mode: Measured) + /// Storage: `Pov::UnboundedMap` (r:1 w:0) + /// Proof: `Pov::UnboundedMap` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Pov::UnboundedMap2` (r:1 w:0) + /// Proof: `Pov::UnboundedMap2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `i` is `[0, 1000]`. fn storage_map_unbounded_both_measured_read(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `229 + i * (8 ±0)` // Estimated: `3693 + i * (8 ±0)` - // Minimum execution time: 3_071_000 picoseconds. - Weight::from_parts(3_487_712, 3693) - // Standard Error: 26 - .saturating_add(Weight::from_parts(748, 0).saturating_mul(i.into())) + // Minimum execution time: 6_000_000 picoseconds. + Weight::from_parts(7_274_226, 3693) + // Standard Error: 280 + .saturating_add(Weight::from_parts(3_282, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(i.into())) } - /// Storage: Pov Map1M (r:1 w:0) - /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) - /// Storage: Pov UnboundedMap (r:1 w:0) - /// Proof Skipped: Pov UnboundedMap (max_values: None, max_size: None, mode: Measured) + /// Storage: `Pov::Map1M` (r:1 w:0) + /// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `Pov::UnboundedMap` (r:1 w:0) + /// Proof: `Pov::UnboundedMap` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `i` is `[0, 1000]`. fn storage_map_partial_unbounded_read(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `228 + i * (4 ±0)` // Estimated: `3692 + i * (4 ±0)` - // Minimum execution time: 3_150_000 picoseconds. - Weight::from_parts(3_582_963, 3692) - // Standard Error: 18 - .saturating_add(Weight::from_parts(380, 0).saturating_mul(i.into())) + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(7_507_333, 3692) + // Standard Error: 64 + .saturating_add(Weight::from_parts(982, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 4).saturating_mul(i.into())) } - /// Storage: Pov Map1M (r:1 w:0) - /// Proof: Pov Map1M (max_values: Some(1000000), max_size: Some(36), added: 2511, mode: MaxEncodedLen) - /// Storage: Pov UnboundedMap (r:1 w:0) - /// Proof Skipped: Pov UnboundedMap (max_values: None, max_size: None, mode: Ignored) + /// Storage: `Pov::Map1M` (r:1 w:0) + /// Proof: `Pov::Map1M` (`max_values`: Some(1000000), `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `Pov::UnboundedMap` (r:1 w:0) + /// Proof: `Pov::UnboundedMap` (`max_values`: None, `max_size`: None, mode: `Ignored`) /// The range of component `i` is `[0, 1000]`. fn storage_map_partial_unbounded_ignored_read(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `228 + i * (4 ±0)` // Estimated: `3501 + i * (4 ±0)` - // Minimum execution time: 3_092_000 picoseconds. - Weight::from_parts(3_595_328, 3501) - // Standard Error: 20 - .saturating_add(Weight::from_parts(243, 0).saturating_mul(i.into())) + // Minimum execution time: 6_000_000 picoseconds. + Weight::from_parts(7_285_011, 3501) + // Standard Error: 80 + .saturating_add(Weight::from_parts(1_395, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(Weight::from_parts(0, 4).saturating_mul(i.into())) } @@ -825,24 +823,24 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_705_000 picoseconds. - Weight::from_parts(1_818_000, 0) + // Minimum execution time: 4_000_000 picoseconds. + Weight::from_parts(5_000_000, 0) } fn noop() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 533_000 picoseconds. - Weight::from_parts(587_000, 0) + // Minimum execution time: 2_000_000 picoseconds. + Weight::from_parts(2_000_000, 0) } - /// Storage: Pov UnboundedMapTwox (r:65001 w:0) - /// Proof Skipped: Pov UnboundedMapTwox (max_values: None, max_size: None, mode: Measured) + /// Storage: `Pov::UnboundedMapTwox` (r:65001 w:0) + /// Proof: `Pov::UnboundedMapTwox` (`max_values`: None, `max_size`: None, mode: `Measured`) fn storage_iteration() -> Weight { // Proof Size summary in bytes: // Measured: `17985289` // Estimated: `178863754` - // Minimum execution time: 118_753_057_000 picoseconds. - Weight::from_parts(121_396_503_000, 178863754) + // Minimum execution time: 218_275_000_000 picoseconds. + Weight::from_parts(222_603_000_000, 178863754) .saturating_add(RocksDbWeight::get().reads(65001_u64)) } } diff --git a/substrate/frame/benchmarking/src/analysis.rs b/substrate/frame/benchmarking/src/analysis.rs index 5fc3abb5a27f0f2c2bcaadc2c6e71fbd603c0470..987078ff79a8b432c1ca801ab79578cd2c27fc9d 100644 --- a/substrate/frame/benchmarking/src/analysis.rs +++ b/substrate/frame/benchmarking/src/analysis.rs @@ -41,7 +41,7 @@ pub enum BenchmarkSelector { /// Multiplies the value by 1000 and converts it into an u128. fn mul_1000_into_u128(value: f64) -> u128 { - // This is slighly more precise than the alternative of `(value * 1000.0) as u128`. + // This is slightly more precise than the alternative of `(value * 1000.0) as u128`. (value as u128) .saturating_mul(1000) .saturating_add((value.fract() * 1000.0) as u128) diff --git a/substrate/frame/benchmarking/src/baseline.rs b/substrate/frame/benchmarking/src/baseline.rs index 2fd3b634ae72d9c50d7d20ba14c625402e0b2565..e76d5aed7b8d3539810ec7c7cd2f83acb51479eb 100644 --- a/substrate/frame/benchmarking/src/baseline.rs +++ b/substrate/frame/benchmarking/src/baseline.rs @@ -125,7 +125,7 @@ pub mod mock { } ); - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/frame/benchmarking/src/lib.rs b/substrate/frame/benchmarking/src/lib.rs index f79582d03e51caaaf395048fa224484b026d32f9..d4ee0abbecce5be341881d73004c59bf71d9db97 100644 --- a/substrate/frame/benchmarking/src/lib.rs +++ b/substrate/frame/benchmarking/src/lib.rs @@ -203,7 +203,7 @@ pub use v1::*; /// ## Where Clause /// /// Some pallets require a where clause specifying constraints on their generics to make -/// writing benchmarks feasible. To accomodate this situation, you can provide such a where +/// writing benchmarks feasible. To accommodate this situation, you can provide such a where /// clause as the (only) argument to the `#[benchmarks]` or `#[instance_benchmarks]` attribute /// macros. Below is an example of this taken from the `message-queue` pallet. /// diff --git a/substrate/frame/benchmarking/src/tests.rs b/substrate/frame/benchmarking/src/tests.rs index f47a3d9f96a9fded1702d9b6e36d6ceda14a7598..206ae515aac6170b030942e4c7612272e2a40709 100644 --- a/substrate/frame/benchmarking/src/tests.rs +++ b/substrate/frame/benchmarking/src/tests.rs @@ -75,7 +75,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/frame/benchmarking/src/tests_instance.rs b/substrate/frame/benchmarking/src/tests_instance.rs index c28aa694a130b9d0c29b86a3b06166b11bed0f3a..d6e1cf99ef73f33820fbea31c8f31a560ec487b6 100644 --- a/substrate/frame/benchmarking/src/tests_instance.rs +++ b/substrate/frame/benchmarking/src/tests_instance.rs @@ -85,7 +85,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/frame/benchmarking/src/v1.rs b/substrate/frame/benchmarking/src/v1.rs index 4ad8cc0edd46ccf554493aac274ca79c679eb642..b2449db3d67d0d336c765becbb8845b85a1a35d3 100644 --- a/substrate/frame/benchmarking/src/v1.rs +++ b/substrate/frame/benchmarking/src/v1.rs @@ -874,7 +874,7 @@ macro_rules! impl_bench_name_tests { $crate::BenchmarkError::Override(_) => { // This is still considered a success condition. $crate::__private::log::error!( - "WARNING: benchmark error overrided - {}", + "WARNING: benchmark error overridden - {}", stringify!($name), ); }, @@ -1704,7 +1704,7 @@ macro_rules! impl_test_function { $crate::BenchmarkError::Override(_) => { // This is still considered a success condition. $crate::__private::log::error!( - "WARNING: benchmark error overrided - {}", + "WARNING: benchmark error overridden - {}", $crate::__private::str::from_utf8(benchmark_name) .expect("benchmark name is always a valid string!"), ); @@ -1851,7 +1851,7 @@ macro_rules! add_benchmark { Err($crate::BenchmarkError::Override(mut result)) => { // Insert override warning as the first storage key. $crate::__private::log::error!( - "WARNING: benchmark error overrided - {}", + "WARNING: benchmark error overridden - {}", $crate::__private::str::from_utf8(benchmark) .expect("benchmark name is always a valid string!") ); diff --git a/substrate/frame/bounties/src/tests.rs b/substrate/frame/bounties/src/tests.rs index da6596617dac4cc893cf27dfd9806436822cf516..de747db53749902ad6164265e3cad139ddefa7da 100644 --- a/substrate/frame/bounties/src/tests.rs +++ b/substrate/frame/bounties/src/tests.rs @@ -58,7 +58,7 @@ parameter_types! { type Balance = u64; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type AccountId = u128; // u64 is not enough to hold bytes used to generate bounty account type Lookup = IdentityLookup; diff --git a/substrate/frame/broker/src/adapt_price.rs b/substrate/frame/broker/src/adapt_price.rs index 8266625687a23073a335bcab58db6ef7c33c8d80..fbcd7afdf0da059fb7023de3422f15b2344d5407 100644 --- a/substrate/frame/broker/src/adapt_price.rs +++ b/substrate/frame/broker/src/adapt_price.rs @@ -19,6 +19,7 @@ use crate::CoreIndex; use sp_arithmetic::{traits::One, FixedU64}; +use sp_runtime::Saturating; /// Type for determining how to set price. pub trait AdaptPrice { @@ -49,14 +50,24 @@ impl AdaptPrice for () { pub struct Linear; impl AdaptPrice for Linear { fn leadin_factor_at(when: FixedU64) -> FixedU64 { - FixedU64::from(2) - when + FixedU64::from(2).saturating_sub(when) } fn adapt_price(sold: CoreIndex, target: CoreIndex, limit: CoreIndex) -> FixedU64 { if sold <= target { - FixedU64::from_rational(sold.into(), target.into()) + // Range of [0.5, 1.0]. + FixedU64::from_rational(1, 2).saturating_add(FixedU64::from_rational( + sold.into(), + target.saturating_mul(2).into(), + )) } else { - FixedU64::one() + - FixedU64::from_rational((sold - target).into(), (limit - target).into()) + // Range of (1.0, 2]. + + // Unchecked math: In this branch we know that sold > target. The limit must be >= sold + // by construction, and thus target must be < limit. + FixedU64::one().saturating_add(FixedU64::from_rational( + (sold - target).into(), + (limit - target).into(), + )) } } } @@ -81,4 +92,23 @@ mod tests { } } } + + #[test] + fn linear_bound_check() { + // Using constraints from pallet implementation i.e. `limit >= sold`. + // Check extremes + let limit = 10; + let target = 5; + + // Maximally sold: `sold == limit` + assert_eq!(Linear::adapt_price(limit, target, limit), FixedU64::from_float(2.0)); + // Ideally sold: `sold == target` + assert_eq!(Linear::adapt_price(target, target, limit), FixedU64::one()); + // Minimally sold: `sold == 0` + assert_eq!(Linear::adapt_price(0, target, limit), FixedU64::from_float(0.5)); + // Optimistic target: `target == limit` + assert_eq!(Linear::adapt_price(limit, limit, limit), FixedU64::one()); + // Pessimistic target: `target == 0` + assert_eq!(Linear::adapt_price(limit, 0, limit), FixedU64::from_float(2.0)); + } } diff --git a/substrate/frame/broker/src/benchmarking.rs b/substrate/frame/broker/src/benchmarking.rs index 70f488e998cc8d2b06d26f2250ee8a585ab44add..98ac074ca91778cfd2538cc0e89f90ca79a62ad5 100644 --- a/substrate/frame/broker/src/benchmarking.rs +++ b/substrate/frame/broker/src/benchmarking.rs @@ -918,6 +918,23 @@ mod benches { Ok(()) } + #[benchmark] + fn swap_leases() -> Result<(), BenchmarkError> { + let admin_origin = + T::AdminOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + + // Add two leases in `Leases` + let n = (T::MaxLeasedCores::get() / 2) as usize; + let mut leases = vec![LeaseRecordItem { task: 1, until: 10u32.into() }; n]; + leases.extend(vec![LeaseRecordItem { task: 2, until: 20u32.into() }; n]); + Leases::::put(BoundedVec::try_from(leases).unwrap()); + + #[extrinsic_call] + _(admin_origin as T::RuntimeOrigin, 1, 2); + + Ok(()) + } + // Implements a test for each benchmark. Execute with: // `cargo test -p pallet-broker --features runtime-benchmarks`. impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/substrate/frame/broker/src/dispatchable_impls.rs b/substrate/frame/broker/src/dispatchable_impls.rs index f2451013251fba144270e4009bae8cb180653b2d..74cda9c4f4cb48cfe1aa8f819cd3094eb2ca6023 100644 --- a/substrate/frame/broker/src/dispatchable_impls.rs +++ b/substrate/frame/broker/src/dispatchable_impls.rs @@ -437,4 +437,22 @@ impl Pallet { Self::deposit_event(Event::AllowedRenewalDropped { core, when }); Ok(()) } + + pub(crate) fn do_swap_leases(id: TaskId, other: TaskId) -> DispatchResult { + let mut id_leases_count = 0; + let mut other_leases_count = 0; + Leases::::mutate(|leases| { + leases.iter_mut().for_each(|lease| { + if lease.task == id { + lease.task = other; + id_leases_count += 1; + } else if lease.task == other { + lease.task = id; + other_leases_count += 1; + } + }) + }); + + Ok(()) + } } diff --git a/substrate/frame/broker/src/lib.rs b/substrate/frame/broker/src/lib.rs index a669463aa02d97e40c4221107bf15d1d3d3525bb..b9b5e309ca91e9ba4ccc2cd6e4f83c6d12bd542e 100644 --- a/substrate/frame/broker/src/lib.rs +++ b/substrate/frame/broker/src/lib.rs @@ -683,7 +683,7 @@ pub mod pallet { /// - `origin`: Must be a Signed origin of the account which owns the Region `region_id`. /// - `region_id`: The Region which was assigned to the Pool. /// - `max_timeslices`: The maximum number of timeslices which should be processed. This may - /// effect the weight of the call but should be ideally made equivalant to the length of + /// effect the weight of the call but should be ideally made equivalent to the length of /// the Region `region_id`. If it is less than this, then further dispatches will be /// required with the `region_id` which makes up any remainders of the region to be /// collected. @@ -786,5 +786,13 @@ pub mod pallet { Self::do_notify_core_count(core_count)?; Ok(()) } + + #[pallet::call_index(99)] + #[pallet::weight(T::WeightInfo::swap_leases())] + pub fn swap_leases(origin: OriginFor, id: TaskId, other: TaskId) -> DispatchResult { + T::AdminOrigin::ensure_origin_or_root(origin)?; + Self::do_swap_leases(id, other)?; + Ok(()) + } } } diff --git a/substrate/frame/broker/src/mock.rs b/substrate/frame/broker/src/mock.rs index ac327c4143e7b51e5350a0065ab861b1618ca0f5..c7205058c9720b15838de3684a33303a45cb8c68 100644 --- a/substrate/frame/broker/src/mock.rs +++ b/substrate/frame/broker/src/mock.rs @@ -47,7 +47,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } diff --git a/substrate/frame/broker/src/types.rs b/substrate/frame/broker/src/types.rs index 7e9f351723a5d312fcd52dcf57d845ccea34a05a..e8119d29ef5d38e25060514d624713a6eedd11a8 100644 --- a/substrate/frame/broker/src/types.rs +++ b/substrate/frame/broker/src/types.rs @@ -55,7 +55,7 @@ pub enum Finality { pub struct RegionId { /// The timeslice at which this Region begins. pub begin: Timeslice, - /// The index of the Polakdot Core on which this Region will be scheduled. + /// The index of the Polkadot Core on which this Region will be scheduled. pub core: CoreIndex, /// The regularity parts in which this Region will be scheduled. pub mask: CoreMask, @@ -198,7 +198,7 @@ pub struct PoolIoRecord { /// The total change of the portion of the pool supplied by purchased Bulk Coretime, measured /// in Core Mask Bits. pub private: SignedCoreMaskBitCount, - /// The total change of the portion of the pool supplied by the Polkaot System, measured in + /// The total change of the portion of the pool supplied by the Polkadot System, measured in /// Core Mask Bits. pub system: SignedCoreMaskBitCount, } diff --git a/substrate/frame/broker/src/weights.rs b/substrate/frame/broker/src/weights.rs index a8f50eeee6e6ceaf284c1764a68411956753cfff..a8b9fb598b8b4b941ae27e1a18b4a5c61d3b2248 100644 --- a/substrate/frame/broker/src/weights.rs +++ b/substrate/frame/broker/src/weights.rs @@ -17,10 +17,10 @@ //! Autogenerated weights for `pallet_broker` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-09-04, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-pzhd7p6z-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: @@ -76,6 +76,7 @@ pub trait WeightInfo { fn request_revenue_info_at() -> Weight; fn notify_core_count() -> Weight; fn do_tick_base() -> Weight; + fn swap_leases() -> Weight; } /// Weights for `pallet_broker` using the Substrate node and recommended hardware. @@ -87,8 +88,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_040_000 picoseconds. - Weight::from_parts(3_344_000, 0) + // Minimum execution time: 2_865_000 picoseconds. + Weight::from_parts(3_061_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Broker::Reservations` (r:1 w:1) @@ -97,8 +98,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `5016` // Estimated: `7496` - // Minimum execution time: 21_259_000 picoseconds. - Weight::from_parts(22_110_000, 7496) + // Minimum execution time: 18_431_000 picoseconds. + Weight::from_parts(19_558_000, 7496) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -108,8 +109,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `6218` // Estimated: `7496` - // Minimum execution time: 20_330_000 picoseconds. - Weight::from_parts(20_826_000, 7496) + // Minimum execution time: 17_724_000 picoseconds. + Weight::from_parts(18_688_000, 7496) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -119,8 +120,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `239` // Estimated: `1526` - // Minimum execution time: 13_411_000 picoseconds. - Weight::from_parts(13_960_000, 1526) + // Minimum execution time: 10_513_000 picoseconds. + Weight::from_parts(11_138_000, 1526) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -139,14 +140,12 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Broker::Workplan` (r:0 w:10) /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. - fn start_sales(n: u32, ) -> Weight { + fn start_sales(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `6330` // Estimated: `8499` - // Minimum execution time: 57_770_000 picoseconds. - Weight::from_parts(61_047_512, 8499) - // Standard Error: 165 - .saturating_add(Weight::from_parts(3, 0).saturating_mul(n.into())) + // Minimum execution time: 50_864_000 picoseconds. + Weight::from_parts(54_000_280, 8499) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(16_u64)) } @@ -162,10 +161,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn purchase() -> Weight { // Proof Size summary in bytes: - // Measured: `568` - // Estimated: `2053` - // Minimum execution time: 51_196_000 picoseconds. - Weight::from_parts(52_382_000, 2053) + // Measured: `635` + // Estimated: `2120` + // Minimum execution time: 43_630_000 picoseconds. + Weight::from_parts(44_622_000, 2120) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -185,10 +184,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) fn renew() -> Weight { // Proof Size summary in bytes: - // Measured: `686` + // Measured: `753` // Estimated: `4698` - // Minimum execution time: 71_636_000 picoseconds. - Weight::from_parts(73_679_000, 4698) + // Minimum execution time: 62_453_000 picoseconds. + Weight::from_parts(63_882_000, 4698) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -198,8 +197,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3550` - // Minimum execution time: 19_182_000 picoseconds. - Weight::from_parts(19_775_000, 3550) + // Minimum execution time: 17_237_000 picoseconds. + Weight::from_parts(17_757_000, 3550) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -209,21 +208,21 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3550` - // Minimum execution time: 20_688_000 picoseconds. - Weight::from_parts(21_557_000, 3550) + // Minimum execution time: 18_504_000 picoseconds. + Weight::from_parts(19_273_000, 3550) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: `Broker::Regions` (r:1 w:2) + /// Storage: `Broker::Regions` (r:1 w:3) /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn interlace() -> Weight { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3550` - // Minimum execution time: 21_190_000 picoseconds. - Weight::from_parts(22_215_000, 3550) + // Minimum execution time: 20_477_000 picoseconds. + Weight::from_parts(21_328_000, 3550) .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Broker::Configuration` (r:1 w:0) /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) @@ -237,8 +236,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `740` // Estimated: `4681` - // Minimum execution time: 34_591_000 picoseconds. - Weight::from_parts(36_227_000, 4681) + // Minimum execution time: 31_815_000 picoseconds. + Weight::from_parts(32_700_000, 4681) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -256,8 +255,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `775` // Estimated: `5996` - // Minimum execution time: 40_346_000 picoseconds. - Weight::from_parts(41_951_000, 5996) + // Minimum execution time: 38_313_000 picoseconds. + Weight::from_parts(38_985_000, 5996) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -272,10 +271,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `859` // Estimated: `6196 + m * (2520 ±0)` - // Minimum execution time: 75_734_000 picoseconds. - Weight::from_parts(78_168_395, 6196) - // Standard Error: 63_180 - .saturating_add(Weight::from_parts(1_076_259, 0).saturating_mul(m.into())) + // Minimum execution time: 70_170_000 picoseconds. + Weight::from_parts(71_245_388, 6196) + // Standard Error: 54_382 + .saturating_add(Weight::from_parts(1_488_794, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) @@ -287,8 +286,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 46_383_000 picoseconds. - Weight::from_parts(47_405_000, 3593) + // Minimum execution time: 43_414_000 picoseconds. + Weight::from_parts(44_475_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -300,8 +299,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `603` // Estimated: `3550` - // Minimum execution time: 30_994_000 picoseconds. - Weight::from_parts(31_979_000, 3550) + // Minimum execution time: 31_327_000 picoseconds. + Weight::from_parts(32_050_000, 3550) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -315,8 +314,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `601` // Estimated: `3533` - // Minimum execution time: 37_584_000 picoseconds. - Weight::from_parts(44_010_000, 3533) + // Minimum execution time: 41_315_000 picoseconds. + Weight::from_parts(42_421_000, 3533) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -330,10 +329,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn drop_history() -> Weight { // Proof Size summary in bytes: - // Measured: `830` + // Measured: `995` // Estimated: `3593` - // Minimum execution time: 45_266_000 picoseconds. - Weight::from_parts(48_000_000, 3593) + // Minimum execution time: 49_707_000 picoseconds. + Weight::from_parts(51_516_000, 3593) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -343,10 +342,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Broker::AllowedRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) fn drop_renewal() -> Weight { // Proof Size summary in bytes: - // Measured: `525` + // Measured: `661` // Estimated: `4698` - // Minimum execution time: 25_365_000 picoseconds. - Weight::from_parts(26_920_000, 4698) + // Minimum execution time: 26_207_000 picoseconds. + Weight::from_parts(27_227_000, 4698) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -355,22 +354,22 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_519_000 picoseconds. - Weight::from_parts(7_098_698, 0) - // Standard Error: 20 - .saturating_add(Weight::from_parts(8, 0).saturating_mul(n.into())) + // Minimum execution time: 4_670_000 picoseconds. + Weight::from_parts(5_170_450, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(37, 0).saturating_mul(n.into())) } - /// Storage: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) + /// Storage: `Broker::CoreCountInbox` (r:1 w:1) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. fn process_core_count(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `98` - // Estimated: `3563` - // Minimum execution time: 7_608_000 picoseconds. - Weight::from_parts(8_157_815, 3563) - // Standard Error: 26 - .saturating_add(Weight::from_parts(48, 0).saturating_mul(n.into())) + // Measured: `404` + // Estimated: `1487` + // Minimum execution time: 6_916_000 picoseconds. + Weight::from_parts(7_485_053, 1487) + // Standard Error: 23 + .saturating_add(Weight::from_parts(30, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -386,10 +385,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn process_revenue() -> Weight { // Proof Size summary in bytes: - // Measured: `905` - // Estimated: `4370` - // Minimum execution time: 59_993_000 picoseconds. - Weight::from_parts(61_752_000, 4370) + // Measured: `972` + // Estimated: `4437` + // Minimum execution time: 50_987_000 picoseconds. + Weight::from_parts(52_303_000, 4437) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -408,10 +407,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `6281` // Estimated: `8499` - // Minimum execution time: 41_863_000 picoseconds. - Weight::from_parts(44_033_031, 8499) - // Standard Error: 116 - .saturating_add(Weight::from_parts(764, 0).saturating_mul(n.into())) + // Minimum execution time: 38_334_000 picoseconds. + Weight::from_parts(40_517_609, 8499) + // Standard Error: 90 + .saturating_add(Weight::from_parts(338, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(15_u64)) } @@ -423,8 +422,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3493` - // Minimum execution time: 9_588_000 picoseconds. - Weight::from_parts(9_925_000, 3493) + // Minimum execution time: 7_850_000 picoseconds. + Weight::from_parts(8_157_000, 3493) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -436,8 +435,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1423` // Estimated: `4681` - // Minimum execution time: 19_308_000 picoseconds. - Weight::from_parts(20_482_000, 4681) + // Minimum execution time: 17_313_000 picoseconds. + Weight::from_parts(17_727_000, 4681) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -445,28 +444,46 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 147_000 picoseconds. - Weight::from_parts(184_000, 0) + // Minimum execution time: 171_000 picoseconds. + Weight::from_parts(196_000, 0) } + /// Storage: `Broker::CoreCountInbox` (r:0 w:1) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) fn notify_core_count() -> Weight { - T::DbWeight::get().reads_writes(1, 1) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_413_000 picoseconds. + Weight::from_parts(2_587_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Broker::Status` (r:1 w:1) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) /// Storage: `Broker::Configuration` (r:1 w:0) /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) - /// Storage: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) + /// Storage: `Broker::CoreCountInbox` (r:1 w:0) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) /// Proof: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) fn do_tick_base() -> Weight { // Proof Size summary in bytes: - // Measured: `699` - // Estimated: `4164` - // Minimum execution time: 19_824_000 picoseconds. - Weight::from_parts(20_983_000, 4164) + // Measured: `603` + // Estimated: `4068` + // Minimum execution time: 13_121_000 picoseconds. + Weight::from_parts(13_685_000, 4068) .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Broker::Leases` (r:1 w:1) + /// Proof: `Broker::Leases` (`max_values`: Some(1), `max_size`: Some(41), added: 536, mode: `MaxEncodedLen`) + fn swap_leases() -> Weight { + // Proof Size summary in bytes: + // Measured: `239` + // Estimated: `1526` + // Minimum execution time: 6_847_000 picoseconds. + Weight::from_parts(7_185_000, 1526) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } } @@ -478,8 +495,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_040_000 picoseconds. - Weight::from_parts(3_344_000, 0) + // Minimum execution time: 2_865_000 picoseconds. + Weight::from_parts(3_061_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Broker::Reservations` (r:1 w:1) @@ -488,8 +505,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `5016` // Estimated: `7496` - // Minimum execution time: 21_259_000 picoseconds. - Weight::from_parts(22_110_000, 7496) + // Minimum execution time: 18_431_000 picoseconds. + Weight::from_parts(19_558_000, 7496) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -499,8 +516,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `6218` // Estimated: `7496` - // Minimum execution time: 20_330_000 picoseconds. - Weight::from_parts(20_826_000, 7496) + // Minimum execution time: 17_724_000 picoseconds. + Weight::from_parts(18_688_000, 7496) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -510,8 +527,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `239` // Estimated: `1526` - // Minimum execution time: 13_411_000 picoseconds. - Weight::from_parts(13_960_000, 1526) + // Minimum execution time: 10_513_000 picoseconds. + Weight::from_parts(11_138_000, 1526) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -530,14 +547,12 @@ impl WeightInfo for () { /// Storage: `Broker::Workplan` (r:0 w:10) /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. - fn start_sales(n: u32, ) -> Weight { + fn start_sales(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `6330` // Estimated: `8499` - // Minimum execution time: 57_770_000 picoseconds. - Weight::from_parts(61_047_512, 8499) - // Standard Error: 165 - .saturating_add(Weight::from_parts(3, 0).saturating_mul(n.into())) + // Minimum execution time: 50_864_000 picoseconds. + Weight::from_parts(54_000_280, 8499) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(16_u64)) } @@ -553,10 +568,10 @@ impl WeightInfo for () { /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn purchase() -> Weight { // Proof Size summary in bytes: - // Measured: `568` - // Estimated: `2053` - // Minimum execution time: 51_196_000 picoseconds. - Weight::from_parts(52_382_000, 2053) + // Measured: `635` + // Estimated: `2120` + // Minimum execution time: 43_630_000 picoseconds. + Weight::from_parts(44_622_000, 2120) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -576,10 +591,10 @@ impl WeightInfo for () { /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) fn renew() -> Weight { // Proof Size summary in bytes: - // Measured: `686` + // Measured: `753` // Estimated: `4698` - // Minimum execution time: 71_636_000 picoseconds. - Weight::from_parts(73_679_000, 4698) + // Minimum execution time: 62_453_000 picoseconds. + Weight::from_parts(63_882_000, 4698) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -589,8 +604,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3550` - // Minimum execution time: 19_182_000 picoseconds. - Weight::from_parts(19_775_000, 3550) + // Minimum execution time: 17_237_000 picoseconds. + Weight::from_parts(17_757_000, 3550) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -600,21 +615,21 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3550` - // Minimum execution time: 20_688_000 picoseconds. - Weight::from_parts(21_557_000, 3550) + // Minimum execution time: 18_504_000 picoseconds. + Weight::from_parts(19_273_000, 3550) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: `Broker::Regions` (r:1 w:2) + /// Storage: `Broker::Regions` (r:1 w:3) /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn interlace() -> Weight { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3550` - // Minimum execution time: 21_190_000 picoseconds. - Weight::from_parts(22_215_000, 3550) + // Minimum execution time: 20_477_000 picoseconds. + Weight::from_parts(21_328_000, 3550) .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Broker::Configuration` (r:1 w:0) /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) @@ -628,8 +643,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `740` // Estimated: `4681` - // Minimum execution time: 34_591_000 picoseconds. - Weight::from_parts(36_227_000, 4681) + // Minimum execution time: 31_815_000 picoseconds. + Weight::from_parts(32_700_000, 4681) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -647,8 +662,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `775` // Estimated: `5996` - // Minimum execution time: 40_346_000 picoseconds. - Weight::from_parts(41_951_000, 5996) + // Minimum execution time: 38_313_000 picoseconds. + Weight::from_parts(38_985_000, 5996) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -663,10 +678,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `859` // Estimated: `6196 + m * (2520 ±0)` - // Minimum execution time: 75_734_000 picoseconds. - Weight::from_parts(78_168_395, 6196) - // Standard Error: 63_180 - .saturating_add(Weight::from_parts(1_076_259, 0).saturating_mul(m.into())) + // Minimum execution time: 70_170_000 picoseconds. + Weight::from_parts(71_245_388, 6196) + // Standard Error: 54_382 + .saturating_add(Weight::from_parts(1_488_794, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) @@ -678,8 +693,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 46_383_000 picoseconds. - Weight::from_parts(47_405_000, 3593) + // Minimum execution time: 43_414_000 picoseconds. + Weight::from_parts(44_475_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -691,8 +706,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `603` // Estimated: `3550` - // Minimum execution time: 30_994_000 picoseconds. - Weight::from_parts(31_979_000, 3550) + // Minimum execution time: 31_327_000 picoseconds. + Weight::from_parts(32_050_000, 3550) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -706,8 +721,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `601` // Estimated: `3533` - // Minimum execution time: 37_584_000 picoseconds. - Weight::from_parts(44_010_000, 3533) + // Minimum execution time: 41_315_000 picoseconds. + Weight::from_parts(42_421_000, 3533) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -721,10 +736,10 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn drop_history() -> Weight { // Proof Size summary in bytes: - // Measured: `830` + // Measured: `995` // Estimated: `3593` - // Minimum execution time: 45_266_000 picoseconds. - Weight::from_parts(48_000_000, 3593) + // Minimum execution time: 49_707_000 picoseconds. + Weight::from_parts(51_516_000, 3593) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -734,10 +749,10 @@ impl WeightInfo for () { /// Proof: `Broker::AllowedRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) fn drop_renewal() -> Weight { // Proof Size summary in bytes: - // Measured: `525` + // Measured: `661` // Estimated: `4698` - // Minimum execution time: 25_365_000 picoseconds. - Weight::from_parts(26_920_000, 4698) + // Minimum execution time: 26_207_000 picoseconds. + Weight::from_parts(27_227_000, 4698) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -746,22 +761,22 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_519_000 picoseconds. - Weight::from_parts(7_098_698, 0) - // Standard Error: 20 - .saturating_add(Weight::from_parts(8, 0).saturating_mul(n.into())) + // Minimum execution time: 4_670_000 picoseconds. + Weight::from_parts(5_170_450, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(37, 0).saturating_mul(n.into())) } - /// Storage: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) + /// Storage: `Broker::CoreCountInbox` (r:1 w:1) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. fn process_core_count(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `98` - // Estimated: `3563` - // Minimum execution time: 7_608_000 picoseconds. - Weight::from_parts(8_157_815, 3563) - // Standard Error: 26 - .saturating_add(Weight::from_parts(48, 0).saturating_mul(n.into())) + // Measured: `404` + // Estimated: `1487` + // Minimum execution time: 6_916_000 picoseconds. + Weight::from_parts(7_485_053, 1487) + // Standard Error: 23 + .saturating_add(Weight::from_parts(30, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -777,10 +792,10 @@ impl WeightInfo for () { /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn process_revenue() -> Weight { // Proof Size summary in bytes: - // Measured: `905` - // Estimated: `4370` - // Minimum execution time: 59_993_000 picoseconds. - Weight::from_parts(61_752_000, 4370) + // Measured: `972` + // Estimated: `4437` + // Minimum execution time: 50_987_000 picoseconds. + Weight::from_parts(52_303_000, 4437) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -799,10 +814,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `6281` // Estimated: `8499` - // Minimum execution time: 41_863_000 picoseconds. - Weight::from_parts(44_033_031, 8499) - // Standard Error: 116 - .saturating_add(Weight::from_parts(764, 0).saturating_mul(n.into())) + // Minimum execution time: 38_334_000 picoseconds. + Weight::from_parts(40_517_609, 8499) + // Standard Error: 90 + .saturating_add(Weight::from_parts(338, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(15_u64)) } @@ -814,8 +829,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3493` - // Minimum execution time: 9_588_000 picoseconds. - Weight::from_parts(9_925_000, 3493) + // Minimum execution time: 7_850_000 picoseconds. + Weight::from_parts(8_157_000, 3493) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -827,8 +842,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1423` // Estimated: `4681` - // Minimum execution time: 19_308_000 picoseconds. - Weight::from_parts(20_482_000, 4681) + // Minimum execution time: 17_313_000 picoseconds. + Weight::from_parts(17_727_000, 4681) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -836,28 +851,45 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 147_000 picoseconds. - Weight::from_parts(184_000, 0) + // Minimum execution time: 171_000 picoseconds. + Weight::from_parts(196_000, 0) } + /// Storage: `Broker::CoreCountInbox` (r:0 w:1) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) fn notify_core_count() -> Weight { - RocksDbWeight::get().reads(1) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_413_000 picoseconds. + Weight::from_parts(2_587_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Broker::Status` (r:1 w:1) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) /// Storage: `Broker::Configuration` (r:1 w:0) /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) - /// Storage: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) + /// Storage: `Broker::CoreCountInbox` (r:1 w:0) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) /// Proof: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) fn do_tick_base() -> Weight { // Proof Size summary in bytes: - // Measured: `699` - // Estimated: `4164` - // Minimum execution time: 19_824_000 picoseconds. - Weight::from_parts(20_983_000, 4164) + // Measured: `603` + // Estimated: `4068` + // Minimum execution time: 13_121_000 picoseconds. + Weight::from_parts(13_685_000, 4068) .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `Broker::Leases` (r:1 w:1) + /// Proof: `Broker::Leases` (`max_values`: Some(1), `max_size`: Some(41), added: 536, mode: `MaxEncodedLen`) + fn swap_leases() -> Weight { + // Proof Size summary in bytes: + // Measured: `239` + // Estimated: `1526` + // Minimum execution time: 6_847_000 picoseconds. + Weight::from_parts(7_185_000, 1526) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/substrate/frame/child-bounties/src/tests.rs b/substrate/frame/child-bounties/src/tests.rs index 276a90a3e29cf78c91e0dc141d57e90c41df7d20..30601f821e4384827059ffc694ab0108a347bf8b 100644 --- a/substrate/frame/child-bounties/src/tests.rs +++ b/substrate/frame/child-bounties/src/tests.rs @@ -61,7 +61,7 @@ parameter_types! { type Balance = u64; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type AccountId = u128; type Lookup = IdentityLookup; diff --git a/substrate/frame/collective/README.md b/substrate/frame/collective/README.md index 444927e51da22898c893b668328521606ba8a9a4..e860edbd484bb872bd241e13cf80f4928a278307 100644 --- a/substrate/frame/collective/README.md +++ b/substrate/frame/collective/README.md @@ -9,7 +9,7 @@ calculations, but enforces this neither in `set_members` nor in `change_members_ A "prime" member may be set to help determine the default vote behavior based on chain config. If `PrimeDefaultVote` is used, the prime vote acts as the default vote in case of any abstentions after the voting period. If `MoreThanMajorityThenPrimeDefaultVote` is used, then -abstentations will first follow the majority of the collective voting, and then the prime +abstentions will first follow the majority of the collective voting, and then the prime member. Voting happens through motions comprising a proposal (i.e. a dispatchable) plus a diff --git a/substrate/frame/collective/src/benchmarking.rs b/substrate/frame/collective/src/benchmarking.rs index af10eae5b673d53b03298b3e0cba48c885dec69b..7b5df17b60a696a17b706dd01f05f2a01d3e787e 100644 --- a/substrate/frame/collective/src/benchmarking.rs +++ b/substrate/frame/collective/src/benchmarking.rs @@ -105,7 +105,7 @@ benchmarks_instance_pallet! { }: _(SystemOrigin::Root, new_members.clone(), new_members.last().cloned(), T::MaxMembers::get()) verify { new_members.sort(); - assert_eq!(Collective::::members(), new_members); + assert_eq!(Members::::get(), new_members); } execute { @@ -199,14 +199,14 @@ benchmarks_instance_pallet! { )?; } - assert_eq!(Collective::::proposals().len(), (p - 1) as usize); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(p, b as usize) }.into(); }: propose(SystemOrigin::Signed(caller.clone()), threshold, Box::new(proposal.clone()), bytes_in_storage) verify { // New proposal is recorded - assert_eq!(Collective::::proposals().len(), p as usize); + assert_eq!(Proposals::::get().len(), p as usize); let proposal_hash = T::Hashing::hash_of(&proposal); assert_last_event::(Event::Proposed { account: caller, proposal_index: p - 1, proposal_hash, threshold }.into()); } @@ -269,7 +269,7 @@ benchmarks_instance_pallet! { approve, )?; - assert_eq!(Collective::::proposals().len(), p as usize); + assert_eq!(Proposals::::get().len(), p as usize); // Voter switches vote to nay, but does not kill the vote, just updates + inserts let approve = false; @@ -280,8 +280,8 @@ benchmarks_instance_pallet! { }: _(SystemOrigin::Signed(voter), last_hash, index, approve) verify { // All proposals exist and the last proposal has just been updated. - assert_eq!(Collective::::proposals().len(), p as usize); - let voting = Collective::::voting(&last_hash).ok_or("Proposal Missing")?; + assert_eq!(Proposals::::get().len(), p as usize); + let voting = Voting::::get(&last_hash).ok_or("Proposal Missing")?; assert_eq!(voting.ayes.len(), (m - 3) as usize); assert_eq!(voting.nays.len(), 1); } @@ -344,7 +344,7 @@ benchmarks_instance_pallet! { approve, )?; - assert_eq!(Collective::::proposals().len(), p as usize); + assert_eq!(Proposals::::get().len(), p as usize); // Voter switches vote to nay, which kills the vote let approve = false; @@ -361,7 +361,7 @@ benchmarks_instance_pallet! { }: close(SystemOrigin::Signed(voter), last_hash, index, Weight::MAX, bytes_in_storage) verify { // The last proposal is removed. - assert_eq!(Collective::::proposals().len(), (p - 1) as usize); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); assert_last_event::(Event::Disapproved { proposal_hash: last_hash }.into()); } @@ -428,7 +428,7 @@ benchmarks_instance_pallet! { true, )?; - assert_eq!(Collective::::proposals().len(), p as usize); + assert_eq!(Proposals::::get().len(), p as usize); // Caller switches vote to aye, which passes the vote let index = p - 1; @@ -442,7 +442,7 @@ benchmarks_instance_pallet! { }: close(SystemOrigin::Signed(caller), last_hash, index, Weight::MAX, bytes_in_storage) verify { // The last proposal is removed. - assert_eq!(Collective::::proposals().len(), (p - 1) as usize); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); assert_last_event::(Event::Executed { proposal_hash: last_hash, result: Ok(()) }.into()); } @@ -519,12 +519,12 @@ benchmarks_instance_pallet! { )?; System::::set_block_number(BlockNumberFor::::max_value()); - assert_eq!(Collective::::proposals().len(), p as usize); + assert_eq!(Proposals::::get().len(), p as usize); // Prime nay will close it as disapproved }: close(SystemOrigin::Signed(caller), last_hash, index, Weight::MAX, bytes_in_storage) verify { - assert_eq!(Collective::::proposals().len(), (p - 1) as usize); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); assert_last_event::(Event::Disapproved { proposal_hash: last_hash }.into()); } @@ -591,12 +591,12 @@ benchmarks_instance_pallet! { // caller is prime, prime already votes aye by creating the proposal System::::set_block_number(BlockNumberFor::::max_value()); - assert_eq!(Collective::::proposals().len(), p as usize); + assert_eq!(Proposals::::get().len(), p as usize); // Prime aye will close it as approved }: close(SystemOrigin::Signed(caller), last_hash, p - 1, Weight::MAX, bytes_in_storage) verify { - assert_eq!(Collective::::proposals().len(), (p - 1) as usize); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); assert_last_event::(Event::Executed { proposal_hash: last_hash, result: Ok(()) }.into()); } @@ -640,11 +640,11 @@ benchmarks_instance_pallet! { } System::::set_block_number(BlockNumberFor::::max_value()); - assert_eq!(Collective::::proposals().len(), p as usize); + assert_eq!(Proposals::::get().len(), p as usize); }: _(SystemOrigin::Root, last_hash) verify { - assert_eq!(Collective::::proposals().len(), (p - 1) as usize); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); assert_last_event::(Event::Disapproved { proposal_hash: last_hash }.into()); } diff --git a/substrate/frame/collective/src/lib.rs b/substrate/frame/collective/src/lib.rs index c084784e0a9bca9914cbbf879c746bfdda1774fb..d0009d02f68c2f0d26e5847221c85ede39e9e633 100644 --- a/substrate/frame/collective/src/lib.rs +++ b/substrate/frame/collective/src/lib.rs @@ -176,7 +176,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] @@ -261,36 +261,30 @@ pub mod pallet { /// The hashes of the active proposals. #[pallet::storage] - #[pallet::getter(fn proposals)] pub type Proposals, I: 'static = ()> = StorageValue<_, BoundedVec, ValueQuery>; /// Actual proposal for a given hash, if it's current. #[pallet::storage] - #[pallet::getter(fn proposal_of)] pub type ProposalOf, I: 'static = ()> = StorageMap<_, Identity, T::Hash, >::Proposal, OptionQuery>; /// Votes on a given proposal, if it is ongoing. #[pallet::storage] - #[pallet::getter(fn voting)] pub type Voting, I: 'static = ()> = StorageMap<_, Identity, T::Hash, Votes>, OptionQuery>; /// Proposals so far. #[pallet::storage] - #[pallet::getter(fn proposal_count)] pub type ProposalCount, I: 'static = ()> = StorageValue<_, u32, ValueQuery>; /// The current members of the collective. This is stored sorted (just by value). #[pallet::storage] - #[pallet::getter(fn members)] pub type Members, I: 'static = ()> = StorageValue<_, Vec, ValueQuery>; - /// The prime member that helps determine the default vote behavior in case of absentations. + /// The prime member that helps determine the default vote behavior in case of abstentions. #[pallet::storage] - #[pallet::getter(fn prime)] pub type Prime, I: 'static = ()> = StorageValue<_, T::AccountId, OptionQuery>; #[pallet::event] @@ -459,7 +453,7 @@ pub mod pallet { #[pallet::compact] length_bound: u32, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - let members = Self::members(); + let members = Members::::get(); ensure!(members.contains(&who), Error::::NotMember); let proposal_len = proposal.encoded_size(); ensure!(proposal_len <= length_bound as usize, Error::::WrongProposalLength); @@ -519,7 +513,7 @@ pub mod pallet { #[pallet::compact] length_bound: u32, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - let members = Self::members(); + let members = Members::::get(); ensure!(members.contains(&who), Error::::NotMember); if threshold < 2 { @@ -565,7 +559,7 @@ pub mod pallet { approve: bool, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - let members = Self::members(); + let members = Members::::get(); ensure!(members.contains(&who), Error::::NotMember); // Detects first vote of the member in the motion @@ -669,7 +663,7 @@ impl, I: 'static> Pallet { pub fn is_member(who: &T::AccountId) -> bool { // Note: The dispatchables *do not* use this to check membership so make sure // to update those if this is changed. - Self::members().contains(who) + Members::::get().contains(who) } /// Execute immediately when adding a new proposal. @@ -688,7 +682,7 @@ impl, I: 'static> Pallet { let proposal_hash = T::Hashing::hash_of(&proposal); ensure!(!>::contains_key(proposal_hash), Error::::DuplicateProposal); - let seats = Self::members().len() as MemberCount; + let seats = Members::::get().len() as MemberCount; let result = proposal.dispatch(RawOrigin::Members(1, seats).into()); Self::deposit_event(Event::Executed { proposal_hash, @@ -721,7 +715,7 @@ impl, I: 'static> Pallet { Ok(proposals.len()) })?; - let index = Self::proposal_count(); + let index = ProposalCount::::get(); >::mutate(|i| *i += 1); >::insert(proposal_hash, proposal); let votes = { @@ -747,7 +741,7 @@ impl, I: 'static> Pallet { index: ProposalIndex, approve: bool, ) -> Result { - let mut voting = Self::voting(&proposal).ok_or(Error::::ProposalMissing)?; + let mut voting = Voting::::get(&proposal).ok_or(Error::::ProposalMissing)?; ensure!(voting.index == index, Error::::WrongIndex); let position_yes = voting.ayes.iter().position(|a| a == &who); @@ -798,12 +792,12 @@ impl, I: 'static> Pallet { proposal_weight_bound: Weight, length_bound: u32, ) -> DispatchResultWithPostInfo { - let voting = Self::voting(&proposal_hash).ok_or(Error::::ProposalMissing)?; + let voting = Voting::::get(&proposal_hash).ok_or(Error::::ProposalMissing)?; ensure!(voting.index == index, Error::::WrongIndex); let mut no_votes = voting.nays.len() as MemberCount; let mut yes_votes = voting.ayes.len() as MemberCount; - let seats = Self::members().len() as MemberCount; + let seats = Members::::get().len() as MemberCount; let approved = yes_votes >= voting.threshold; let disapproved = seats.saturating_sub(no_votes) < voting.threshold; // Allow (dis-)approving the proposal as soon as there are enough votes. @@ -837,7 +831,7 @@ impl, I: 'static> Pallet { // Only allow actual closing of the proposal after the voting period has ended. ensure!(frame_system::Pallet::::block_number() >= voting.end, Error::::TooEarly); - let prime_vote = Self::prime().map(|who| voting.ayes.iter().any(|a| a == &who)); + let prime_vote = Prime::::get().map(|who| voting.ayes.iter().any(|a| a == &who)); // default voting strategy. let default = T::DefaultVote::default_vote(prime_vote, yes_votes, no_votes, seats); @@ -978,29 +972,28 @@ impl, I: 'static> Pallet { /// * The prime account must be a member of the collective. #[cfg(any(feature = "try-runtime", test))] fn do_try_state() -> Result<(), TryRuntimeError> { - Self::proposals() - .into_iter() - .try_for_each(|proposal| -> Result<(), TryRuntimeError> { + Proposals::::get().into_iter().try_for_each( + |proposal| -> Result<(), TryRuntimeError> { ensure!( - Self::proposal_of(proposal).is_some(), + ProposalOf::::get(proposal).is_some(), "Proposal hash from `Proposals` is not found inside the `ProposalOf` mapping." ); Ok(()) - })?; + }, + )?; ensure!( - Self::proposals().into_iter().count() <= Self::proposal_count() as usize, + Proposals::::get().into_iter().count() <= ProposalCount::::get() as usize, "The actual number of proposals is greater than `ProposalCount`" ); ensure!( - Self::proposals().into_iter().count() == >::iter_keys().count(), + Proposals::::get().into_iter().count() == >::iter_keys().count(), "Proposal count inside `Proposals` is not equal to the proposal count in `ProposalOf`" ); - Self::proposals() - .into_iter() - .try_for_each(|proposal| -> Result<(), TryRuntimeError> { - if let Some(votes) = Self::voting(proposal) { + Proposals::::get().into_iter().try_for_each( + |proposal| -> Result<(), TryRuntimeError> { + if let Some(votes) = Voting::::get(proposal) { let ayes = votes.ayes.len(); let nays = votes.nays.len(); @@ -1010,13 +1003,13 @@ impl, I: 'static> Pallet { ); } Ok(()) - })?; + }, + )?; let mut proposal_indices = vec![]; - Self::proposals() - .into_iter() - .try_for_each(|proposal| -> Result<(), TryRuntimeError> { - if let Some(votes) = Self::voting(proposal) { + Proposals::::get().into_iter().try_for_each( + |proposal| -> Result<(), TryRuntimeError> { + if let Some(votes) = Voting::::get(proposal) { let proposal_index = votes.index; ensure!( !proposal_indices.contains(&proposal_index), @@ -1025,12 +1018,13 @@ impl, I: 'static> Pallet { proposal_indices.push(proposal_index); } Ok(()) - })?; + }, + )?; >::iter_keys().try_for_each( |proposal_hash| -> Result<(), TryRuntimeError> { ensure!( - Self::proposals().contains(&proposal_hash), + Proposals::::get().contains(&proposal_hash), "`Proposals` doesn't contain the proposal hash from the `Voting` storage map." ); Ok(()) @@ -1038,17 +1032,17 @@ impl, I: 'static> Pallet { )?; ensure!( - Self::members().len() <= T::MaxMembers::get() as usize, + Members::::get().len() <= T::MaxMembers::get() as usize, "The member count is greater than `MaxMembers`." ); ensure!( - Self::members().windows(2).all(|members| members[0] <= members[1]), + Members::::get().windows(2).all(|members| members[0] <= members[1]), "The members are not sorted by value." ); - if let Some(prime) = Self::prime() { - ensure!(Self::members().contains(&prime), "Prime account is not a member."); + if let Some(prime) = Prime::::get() { + ensure!(Members::::get().contains(&prime), "Prime account is not a member."); } Ok(()) @@ -1082,7 +1076,7 @@ impl, I: 'static> ChangeMembers for Pallet { // remove accounts from all current voting in motions. let mut outgoing = outgoing.to_vec(); outgoing.sort(); - for h in Self::proposals().into_iter() { + for h in Proposals::::get().into_iter() { >::mutate(h, |v| { if let Some(mut votes) = v.take() { votes.ayes = votes diff --git a/substrate/frame/collective/src/tests.rs b/substrate/frame/collective/src/tests.rs index aae17b7ffc27bb6d9e200dee18a44c7ec0aa7dcc..5240dc215ff7b19b774dc32e2a33092e137d6d28 100644 --- a/substrate/frame/collective/src/tests.rs +++ b/substrate/frame/collective/src/tests.rs @@ -87,7 +87,7 @@ parameter_types! { pub static MaxProposalWeight: Weight = default_max_proposal_weight(); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } @@ -193,8 +193,8 @@ fn default_max_proposal_weight() -> Weight { #[test] fn motions_basic_environment_works() { ExtBuilder::default().build_and_execute(|| { - assert_eq!(Collective::members(), vec![1, 2, 3]); - assert_eq!(*Collective::proposals(), Vec::::new()); + assert_eq!(Members::::get(), vec![1, 2, 3]); + assert_eq!(*Proposals::::get(), Vec::::new()); }); } @@ -205,7 +205,7 @@ fn initialize_members_sorts_members() { ExtBuilder::default() .set_collective_members(unsorted_members) .build_and_execute(|| { - assert_eq!(Collective::members(), expected_members); + assert_eq!(Members::::get(), expected_members); }); } @@ -219,8 +219,8 @@ fn set_members_with_prime_works() { Some(3), MaxMembers::get() )); - assert_eq!(Collective::members(), members.clone()); - assert_eq!(Collective::prime(), Some(3)); + assert_eq!(Members::::get(), members.clone()); + assert_eq!(Prime::::get(), Some(3)); assert_noop!( Collective::set_members(RuntimeOrigin::root(), members, Some(4), MaxMembers::get()), Error::::PrimeAccountNotMember @@ -632,12 +632,12 @@ fn removal_of_old_voters_votes_works() { assert_ok!(Collective::vote(RuntimeOrigin::signed(1), hash, 0, true)); assert_ok!(Collective::vote(RuntimeOrigin::signed(2), hash, 0, true)); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 0, threshold: 3, ayes: vec![1, 2], nays: vec![], end }) ); Collective::change_members_sorted(&[4], &[1], &[2, 3, 4]); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 0, threshold: 3, ayes: vec![2], nays: vec![], end }) ); @@ -653,12 +653,12 @@ fn removal_of_old_voters_votes_works() { assert_ok!(Collective::vote(RuntimeOrigin::signed(2), hash, 1, true)); assert_ok!(Collective::vote(RuntimeOrigin::signed(3), hash, 1, false)); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![3], end }) ); Collective::change_members_sorted(&[], &[3], &[2, 4]); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![], end }) ); }); @@ -680,7 +680,7 @@ fn removal_of_old_voters_votes_works_with_set_members() { assert_ok!(Collective::vote(RuntimeOrigin::signed(1), hash, 0, true)); assert_ok!(Collective::vote(RuntimeOrigin::signed(2), hash, 0, true)); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 0, threshold: 3, ayes: vec![1, 2], nays: vec![], end }) ); assert_ok!(Collective::set_members( @@ -690,7 +690,7 @@ fn removal_of_old_voters_votes_works_with_set_members() { MaxMembers::get() )); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 0, threshold: 3, ayes: vec![2], nays: vec![], end }) ); @@ -706,7 +706,7 @@ fn removal_of_old_voters_votes_works_with_set_members() { assert_ok!(Collective::vote(RuntimeOrigin::signed(2), hash, 1, true)); assert_ok!(Collective::vote(RuntimeOrigin::signed(3), hash, 1, false)); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![3], end }) ); assert_ok!(Collective::set_members( @@ -716,7 +716,7 @@ fn removal_of_old_voters_votes_works_with_set_members() { MaxMembers::get() )); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![], end }) ); }); @@ -735,10 +735,10 @@ fn propose_works() { Box::new(proposal.clone()), proposal_len )); - assert_eq!(*Collective::proposals(), vec![hash]); - assert_eq!(Collective::proposal_of(&hash), Some(proposal)); + assert_eq!(*Proposals::::get(), vec![hash]); + assert_eq!(ProposalOf::::get(&hash), Some(proposal)); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 0, threshold: 3, ayes: vec![], nays: vec![], end }) ); @@ -898,13 +898,13 @@ fn motions_vote_after_works() { )); // Initially there a no votes when the motion is proposed. assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 0, threshold: 2, ayes: vec![], nays: vec![], end }) ); // Cast first aye vote. assert_ok!(Collective::vote(RuntimeOrigin::signed(1), hash, 0, true)); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 0, threshold: 2, ayes: vec![1], nays: vec![], end }) ); // Try to cast a duplicate aye vote. @@ -915,7 +915,7 @@ fn motions_vote_after_works() { // Cast a nay vote. assert_ok!(Collective::vote(RuntimeOrigin::signed(1), hash, 0, false)); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 0, threshold: 2, ayes: vec![], nays: vec![1], end }) ); // Try to cast a duplicate nay vote. @@ -966,7 +966,7 @@ fn motions_all_first_vote_free_works() { proposal_len, )); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 0, threshold: 2, ayes: vec![], nays: vec![], end }) ); @@ -995,7 +995,7 @@ fn motions_all_first_vote_free_works() { Collective::vote(RuntimeOrigin::signed(3), hash, 0, false); assert_eq!(vote_rval.unwrap().pays_fee, Pays::Yes); - // Test close() Extrincis | Check DispatchResultWithPostInfo with Pay Info + // Test close() Extrinsics | Check DispatchResultWithPostInfo with Pay Info let proposal_weight = proposal.get_dispatch_info().weight; let close_rval: DispatchResultWithPostInfo = @@ -1031,14 +1031,14 @@ fn motions_reproposing_disapproved_works() { proposal_weight, proposal_len )); - assert_eq!(*Collective::proposals(), vec![]); + assert_eq!(*Proposals::::get(), vec![]); assert_ok!(Collective::propose( RuntimeOrigin::signed(1), 2, Box::new(proposal.clone()), proposal_len )); - assert_eq!(*Collective::proposals(), vec![hash]); + assert_eq!(*Proposals::::get(), vec![hash]); }); } diff --git a/substrate/frame/contracts/CHANGELOG.md b/substrate/frame/contracts/CHANGELOG.md deleted file mode 100644 index aca94e5b149152d5c598102c633048baa17d1276..0000000000000000000000000000000000000000 --- a/substrate/frame/contracts/CHANGELOG.md +++ /dev/null @@ -1,116 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -The semantic versioning guarantees cover the interface to the Substrate runtime which -includes this pallet as a dependency. This module will also add storage migrations whenever -changes require it. Stability with regard to offchain tooling is explicitly excluded from -this guarantee: For example adding a new field to an in-storage data structure will require -changes to frontends to properly display it. However, those changes will still be regarded -as a minor version bump. - -The interface provided to smart contracts will adhere to semver with one exception: Even -major version bumps will be backwards compatible with regard to already deployed contracts. -In other words: Upgrading this pallet will not break pre-existing contracts. - -## [Unreleased] - -### Added - -- Forbid calling back to contracts after switching to runtime -[#13443](https://github.com/paritytech/substrate/pull/13443) - -- Allow contracts to dispatch calls into the runtime (**unstable**) -[#9276](https://github.com/paritytech/substrate/pull/9276) - -- New version of `seal_call` that offers more features. -[#8909](https://github.com/paritytech/substrate/pull/8909) - -- New `instantiate` RPC that allows clients to dry-run contract instantiation. -[#8451](https://github.com/paritytech/substrate/pull/8451) - -- New version of `seal_random` which exposes additional information. -[#8329](https://github.com/paritytech/substrate/pull/8329) - -### Changed - -- Replaced storage rent with automatic storage deposits -[#9669](https://github.com/paritytech/substrate/pull/9669) -[#10082](https://github.com/paritytech/substrate/pull/10082) - -- Replaced `seal_println` with the `seal_debug_message` API which allows outputting debug -messages to the console and RPC clients. -[#8773](https://github.com/paritytech/substrate/pull/8773) -[#9550](https://github.com/paritytech/substrate/pull/9550) - -- Make storage and fields of `Schedule` private to the crate. -[#8359](https://github.com/paritytech/substrate/pull/8359) - -### Fixed - -- Remove pre-charging which caused wrongly estimated weights -[#8976](https://github.com/paritytech/substrate/pull/8976) - -## [v3.0.0] 2021-02-25 - -This version constitutes the first release that brings any stability guarantees (see above). - -### Added - -- Emit an event when a contract terminates (self-destructs). -[#8014](https://github.com/paritytech/substrate/pull/8014) - -- Charge rent for code stored on the chain in addition to the already existing -rent that is paid for data storage. -[#7935](https://github.com/paritytech/substrate/pull/7935) - -- Allow the runtime to configure per storage item costs in addition -to the already existing per byte costs. -[#7819](https://github.com/paritytech/substrate/pull/7819) - -- Contracts are now deleted lazily so that the user who removes a contract -does not need to pay for the deletion of the contract storage. -[#7740](https://github.com/paritytech/substrate/pull/7740) - -- Allow runtime authors to define chain extensions in order to provide custom -functionality to contracts. -[#7548](https://github.com/paritytech/substrate/pull/7548) -[#8003](https://github.com/paritytech/substrate/pull/8003) - -- Proper weights which are fully automated by benchmarking. -[#6715](https://github.com/paritytech/substrate/pull/6715) -[#7017](https://github.com/paritytech/substrate/pull/7017) -[#7361](https://github.com/paritytech/substrate/pull/7361) - -### Changed - -- Collect the rent for one block during instantiation. -[#7847](https://github.com/paritytech/substrate/pull/7847) - -- Instantiation takes a `salt` argument to allow for easier instantion of the -same code by the same sender. -[#7482](https://github.com/paritytech/substrate/pull/7482) - -- Improve the information returned by the `contracts_call` RPC. -[#7468](https://github.com/paritytech/substrate/pull/7468) - -- Simplify the node configuration necessary to add this module. -[#7409](https://github.com/paritytech/substrate/pull/7409) - -### Fixed - -- Consider the code size of a contract in the weight that is charged for -loading a contract from storage. -[#8086](https://github.com/paritytech/substrate/pull/8086) - -- Fix possible overflow in storage size calculation -[#7885](https://github.com/paritytech/substrate/pull/7885) - -- Cap the surcharge reward that can be claimed. -[#7870](https://github.com/paritytech/substrate/pull/7870) - -- Fix a possible DoS vector where contracts could allocate too large buffers. -[#7818](https://github.com/paritytech/substrate/pull/7818) diff --git a/substrate/frame/contracts/README.md b/substrate/frame/contracts/README.md index 6e817292e66c50d5766d5406cca6c053a25c1362..09dc770300ca8491af062bbde9e820deccdd1341 100644 --- a/substrate/frame/contracts/README.md +++ b/substrate/frame/contracts/README.md @@ -59,7 +59,7 @@ In general, a contract execution needs to be deterministic so that all nodes com it. To that end we disallow any instructions that could cause indeterminism. Most notable are any floating point arithmetic. That said, sometimes contracts are executed off-chain and hence are not subject to consensus. If code is only executed by a single node and implicitly trusted by other actors is such a case. Trusted execution environments -come to mind. To that end we allow the execution of indeterminstic code for off-chain usages with the following +come to mind. To that end we allow the execution of indeterministic code for off-chain usages with the following constraints: 1. No contract can ever be instantiated from an indeterministic code. The only way to execute the code is to use a diff --git a/substrate/frame/contracts/fixtures/Cargo.toml b/substrate/frame/contracts/fixtures/Cargo.toml index 7fdf56a91fcc7c114b0cadd622e9d43e47088f33..8c93c6f16f66fa2c4a6955607fbd9797c0eda925 100644 --- a/substrate/frame/contracts/fixtures/Cargo.toml +++ b/substrate/frame/contracts/fixtures/Cargo.toml @@ -11,18 +11,17 @@ description = "Fixtures for testing contracts pallet." workspace = true [dependencies] -wat = "1" frame-system = { path = "../../system" } sp-runtime = { path = "../../../primitives/runtime" } -anyhow = "1.0.0" +anyhow = "1.0.81" [build-dependencies] parity-wasm = "0.45.0" tempfile = "3.8.1" toml = "0.8.2" twox-hash = "1.6.3" -polkavm-linker = { version = "0.5.0", optional = true } -anyhow = "1.0.0" +polkavm-linker = { workspace = true, optional = true } +anyhow = "1.0.81" [features] riscv = ["polkavm-linker"] diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 12a7805294b673480b6a350b9f242a1de9830e56..19aff37c1601430011966768d54a475ef0bbc1d2 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -121,9 +121,11 @@ fn collect_entries(contracts_dir: &Path, out_dir: &Path) -> Vec { /// Create a `Cargo.toml` to compile the given contract entries. fn create_cargo_toml<'a>( fixtures_dir: &Path, + root_cargo_toml: &Path, entries: impl Iterator, output_dir: &Path, ) -> Result<()> { + let root_toml: toml::Value = toml::from_str(&fs::read_to_string(root_cargo_toml)?)?; let mut cargo_toml: toml::Value = toml::from_str(include_str!("./build/Cargo.toml"))?; let mut set_dep = |name, path| -> Result<()> { cargo_toml["dependencies"][name]["path"] = toml::Value::String( @@ -133,6 +135,8 @@ fn create_cargo_toml<'a>( }; set_dep("uapi", "../uapi")?; set_dep("common", "./contracts/common")?; + cargo_toml["dependencies"]["polkavm-derive"]["version"] = + root_toml["workspace"]["dependencies"]["polkavm-derive"].clone(); cargo_toml["bin"] = toml::Value::Array( entries @@ -324,6 +328,7 @@ fn main() -> Result<()> { let contracts_dir = fixtures_dir.join("contracts"); let out_dir: PathBuf = env::var("OUT_DIR")?.into(); let workspace_root = find_workspace_root(&fixtures_dir).expect("workspace root exists; qed"); + let root_cargo_toml = workspace_root.join("Cargo.toml"); let entries = collect_entries(&contracts_dir, &out_dir); if entries.is_empty() { @@ -333,7 +338,7 @@ fn main() -> Result<()> { let tmp_dir = tempfile::tempdir()?; let tmp_dir_path = tmp_dir.path(); - create_cargo_toml(&fixtures_dir, entries.iter(), tmp_dir.path())?; + create_cargo_toml(&fixtures_dir, &root_cargo_toml, entries.iter(), tmp_dir.path())?; invoke_cargo_fmt( &workspace_root.join(".rustfmt.toml"), entries.iter().map(|entry| &entry.path as _), diff --git a/substrate/frame/contracts/fixtures/build/Cargo.toml b/substrate/frame/contracts/fixtures/build/Cargo.toml index d524dbff1ce1b7af5d4dcf404f61154c5f2d1e6f..ba487a2bb5ca68f8533dec7ae32541dd0f429756 100644 --- a/substrate/frame/contracts/fixtures/build/Cargo.toml +++ b/substrate/frame/contracts/fixtures/build/Cargo.toml @@ -6,11 +6,11 @@ edition = "2021" # Binary targets are injected dynamically by the build script. [[bin]] -# local path are injected dynamically by the build script. +# All paths or versions are injected dynamically by the build script. [dependencies] uapi = { package = 'pallet-contracts-uapi', path = "", default-features = false } common = { package = 'pallet-contracts-fixtures-common', path = "" } -polkavm-derive = '0.5.0' +polkavm-derive = { version = "" } [profile.release] opt-level = 3 diff --git a/substrate/frame/contracts/fixtures/contracts/caller_is_origin_n.rs b/substrate/frame/contracts/fixtures/contracts/caller_is_origin_n.rs new file mode 100644 index 0000000000000000000000000000000000000000..fd6f59802fa08408105070f37ae64569a32d38a7 --- /dev/null +++ b/substrate/frame/contracts/fixtures/contracts/caller_is_origin_n.rs @@ -0,0 +1,38 @@ +// 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. + +//! This fixture calls caller_is_origin `n` times. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(n: u32, ); + + for _ in 0..n { + let _ = api::caller_is_origin(); + } +} diff --git a/substrate/frame/contracts/fixtures/contracts/multi_store.rs b/substrate/frame/contracts/fixtures/contracts/multi_store.rs index b83f3995a42b4905383f8629c04d25902e03374e..a78115f0148888d601893322fe04a4ae07fce269 100644 --- a/substrate/frame/contracts/fixtures/contracts/multi_store.rs +++ b/substrate/frame/contracts/fixtures/contracts/multi_store.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Does two stores to two seperate storage items +//! Does two stores to two separate storage items #![no_std] #![no_main] diff --git a/substrate/frame/contracts/fixtures/contracts/recurse.rs b/substrate/frame/contracts/fixtures/contracts/recurse.rs new file mode 100644 index 0000000000000000000000000000000000000000..b1ded608c2fc417323580257b74835a0aaee9aa3 --- /dev/null +++ b/substrate/frame/contracts/fixtures/contracts/recurse.rs @@ -0,0 +1,53 @@ +// 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. + +//! This fixture calls itself as many times as passed as argument. + +#![no_std] +#![no_main] + +use common::{input, output}; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(calls_left: u32, ); + + // own address + output!(addr, [0u8; 32], api::address,); + + if calls_left == 0 { + return + } + + api::call_v2( + uapi::CallFlags::ALLOW_REENTRY, + addr, + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much deposit_limit to devote for the execution. 0 = all. + None, // No deposit limit. + &0u64.to_le_bytes(), // Value transferred to the contract. + &(calls_left - 1).to_le_bytes(), + None, + ) + .unwrap(); +} diff --git a/substrate/frame/contracts/fixtures/data/invalid_contract_no_memory.wat b/substrate/frame/contracts/fixtures/data/invalid_contract_no_memory.wat deleted file mode 100644 index 0aeefbcb7ebfe27f55fb84de0f14a3c7ec12b05c..0000000000000000000000000000000000000000 --- a/substrate/frame/contracts/fixtures/data/invalid_contract_no_memory.wat +++ /dev/null @@ -1,5 +0,0 @@ -;; A valid contract which does nothing at all -(module - (func (export "deploy")) - (func (export "call")) -) diff --git a/substrate/frame/contracts/fixtures/data/invalid_module.wat b/substrate/frame/contracts/fixtures/data/invalid_module.wat deleted file mode 100644 index e4a72f74273f9350420f737e0974516b3e5c4132..0000000000000000000000000000000000000000 --- a/substrate/frame/contracts/fixtures/data/invalid_module.wat +++ /dev/null @@ -1,8 +0,0 @@ -;; An invalid module -(module - (func (export "deploy")) - (func (export "call") - ;; imbalanced stack - (i32.const 7) - ) -) diff --git a/substrate/frame/contracts/fixtures/data/run_out_of_gas_start_fn.wat b/substrate/frame/contracts/fixtures/data/run_out_of_gas_start_fn.wat deleted file mode 100644 index 6591d7ede78c20e0d19c7c60c183b4c58e98d2dc..0000000000000000000000000000000000000000 --- a/substrate/frame/contracts/fixtures/data/run_out_of_gas_start_fn.wat +++ /dev/null @@ -1,10 +0,0 @@ -(module - (import "env" "memory" (memory 1 1)) - (start $start) - (func $start - (loop $inf (br $inf)) ;; just run out of gas - (unreachable) - ) - (func (export "call")) - (func (export "deploy")) -) diff --git a/substrate/frame/contracts/fixtures/data/seal_input_noop.wat b/substrate/frame/contracts/fixtures/data/seal_input_noop.wat deleted file mode 100644 index 7b5a1e32af4d61ba8dfecdc978e5f4d3a09e1480..0000000000000000000000000000000000000000 --- a/substrate/frame/contracts/fixtures/data/seal_input_noop.wat +++ /dev/null @@ -1,14 +0,0 @@ -;; Everything prepared for the host function call, but no call is performed. -(module - (import "seal0" "seal_input" (func $seal_input (param i32 i32))) - (import "env" "memory" (memory 1 1)) - - ;; [0, 8) buffer to write input - - ;; [8, 12) size of the input buffer - (data (i32.const 8) "\04") - - (func (export "call")) - - (func (export "deploy")) -) diff --git a/substrate/frame/contracts/fixtures/data/seal_input_once.wat b/substrate/frame/contracts/fixtures/data/seal_input_once.wat deleted file mode 100644 index 919a03a9b6903df3ff4917347b9c40a2d260b8f1..0000000000000000000000000000000000000000 --- a/substrate/frame/contracts/fixtures/data/seal_input_once.wat +++ /dev/null @@ -1,22 +0,0 @@ -;; Stores a value of the passed size. The host function is called once. -(module - (import "seal0" "seal_input" (func $seal_input (param i32 i32))) - (import "env" "memory" (memory 1 1)) - - ;; [0, 8) buffer to write input - - ;; [8, 12) size of the input buffer - (data (i32.const 8) "\04") - - (func (export "call") - ;; instructions to consume engine fuel - (drop - (i32.const 42) - ) - - (call $seal_input (i32.const 0) (i32.const 8)) - - ) - - (func (export "deploy")) -) diff --git a/substrate/frame/contracts/fixtures/data/seal_input_twice.wat b/substrate/frame/contracts/fixtures/data/seal_input_twice.wat deleted file mode 100644 index 3a8be814efb045078494294961686931c4cd7ab4..0000000000000000000000000000000000000000 --- a/substrate/frame/contracts/fixtures/data/seal_input_twice.wat +++ /dev/null @@ -1,28 +0,0 @@ -;; Stores a value of the passed size. The host function is called twice. -(module - (import "seal0" "seal_input" (func $seal_input (param i32 i32))) - (import "env" "memory" (memory 1 1)) - - ;; [0, 8) buffer to write input - - ;; [8, 12) size of the input buffer - (data (i32.const 8) "\04") - - (func (export "call") - ;; instructions to consume engine fuel - (drop - (i32.const 42) - ) - - (call $seal_input (i32.const 0) (i32.const 8)) - - ;; instructions to consume engine fuel - (drop - (i32.const 42) - ) - - (call $seal_input (i32.const 0) (i32.const 8)) - ) - - (func (export "deploy")) -) diff --git a/substrate/frame/contracts/fixtures/src/lib.rs b/substrate/frame/contracts/fixtures/src/lib.rs index e0d9d4f8bd5b1512f7998d497adacf695c5092e1..56a8e2321c5c3cc2181cbfeb676700993e3f0a77 100644 --- a/substrate/frame/contracts/fixtures/src/lib.rs +++ b/substrate/frame/contracts/fixtures/src/lib.rs @@ -16,34 +16,7 @@ // limitations under the License. use sp_runtime::traits::Hash; -use std::{env::var, fs, path::PathBuf}; - -fn wat_root_dir() -> PathBuf { - match (var("CARGO_MANIFEST_DIR"), var("CARGO_PKG_NAME")) { - // When `CARGO_MANIFEST_DIR` is not set, Rust resolves relative paths from the root folder - (Err(_), _) => "substrate/frame/contracts/fixtures/data".into(), - (Ok(path), Ok(s)) if s == "pallet-contracts" => PathBuf::from(path).join("fixtures/data"), - (Ok(path), Ok(s)) if s == "pallet-contracts-mock-network" => - PathBuf::from(path).parent().unwrap().join("fixtures/data"), - (Ok(_), pkg_name) => panic!("Failed to resolve fixture dir for tests from {pkg_name:?}."), - } -} - -/// Load a given wasm module represented by a .wat file and returns a wasm binary contents along -/// with it's hash. -/// -/// The fixture files are located under the `fixtures/` directory. -fn legacy_compile_module( - fixture_name: &str, -) -> anyhow::Result<(Vec, ::Output)> -where - T: frame_system::Config, -{ - let fixture_path = wat_root_dir().join(format!("{fixture_name}.wat")); - let wasm_binary = wat::parse_file(fixture_path)?; - let code_hash = T::Hashing::hash(&wasm_binary); - Ok((wasm_binary, code_hash)) -} +use std::{fs, path::PathBuf}; /// Load a given wasm module and returns a wasm binary contents along with it's hash. /// Use the legacy compile_module as fallback, if the rust fixture does not exist yet. @@ -53,15 +26,11 @@ pub fn compile_module( where T: frame_system::Config, { - let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); + let out_dir: PathBuf = env!("OUT_DIR").into(); let fixture_path = out_dir.join(format!("{fixture_name}.wasm")); - match fs::read(fixture_path) { - Ok(wasm_binary) => { - let code_hash = T::Hashing::hash(&wasm_binary); - Ok((wasm_binary, code_hash)) - }, - Err(_) => legacy_compile_module::(fixture_name), - } + let binary = fs::read(fixture_path)?; + let code_hash = T::Hashing::hash(&binary); + Ok((binary, code_hash)) } #[cfg(test)] diff --git a/substrate/frame/contracts/mock-network/src/parachain.rs b/substrate/frame/contracts/mock-network/src/parachain.rs index 7a60a66b3145dd7268f20924cd53ec81f2d32447..d4ad47581d16740df732459b3faee2d0b02306b4 100644 --- a/substrate/frame/contracts/mock-network/src/parachain.rs +++ b/substrate/frame/contracts/mock-network/src/parachain.rs @@ -53,7 +53,7 @@ parameter_types! { pub const BlockHashCount: u64 = 250; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; @@ -282,6 +282,9 @@ impl Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } impl mock_msg_queue::Config for Runtime { diff --git a/substrate/frame/contracts/mock-network/src/parachain/contracts_config.rs b/substrate/frame/contracts/mock-network/src/parachain/contracts_config.rs index dadba394e26453366d0b90ce8ff18931ab00743f..3c06131dd6088a2b044b68cfbf3b4a918eb37e3e 100644 --- a/substrate/frame/contracts/mock-network/src/parachain/contracts_config.rs +++ b/substrate/frame/contracts/mock-network/src/parachain/contracts_config.rs @@ -25,7 +25,7 @@ use frame_support::{ traits::{ConstBool, ConstU32, Contains, Randomness}, weights::Weight, }; -use frame_system::pallet_prelude::BlockNumberFor; +use frame_system::{pallet_prelude::BlockNumberFor, EnsureSigned}; use pallet_xcm::BalanceOf; use sp_runtime::{traits::Convert, Perbill}; @@ -90,9 +90,12 @@ impl pallet_contracts::Config for Runtime { type Schedule = Schedule; type Time = super::Timestamp; type UnsafeUnstableInterface = ConstBool; + type UploadOrigin = EnsureSigned; + type InstantiateOrigin = EnsureSigned; type WeightInfo = (); type WeightPrice = Self; type Debug = (); type Environment = (); + type ApiVersion = (); type Xcm = pallet_xcm::Pallet; } diff --git a/substrate/frame/contracts/mock-network/src/relay_chain.rs b/substrate/frame/contracts/mock-network/src/relay_chain.rs index 6eb9b4e53855c0c62e65392a1dd0a63adc239b0b..e2a8d3d1337bb6901d4f0b24d16198595accba1b 100644 --- a/substrate/frame/contracts/mock-network/src/relay_chain.rs +++ b/substrate/frame/contracts/mock-network/src/relay_chain.rs @@ -47,7 +47,7 @@ parameter_types! { pub const BlockHashCount: u64 = 250; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; @@ -182,6 +182,9 @@ impl Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/substrate/frame/contracts/mock-network/src/tests.rs b/substrate/frame/contracts/mock-network/src/tests.rs index d22221fe8ee051182471249182d54b02b7f47c2c..39aa9bebc0f5943627ae40a109dab357ae19dd07 100644 --- a/substrate/frame/contracts/mock-network/src/tests.rs +++ b/substrate/frame/contracts/mock-network/src/tests.rs @@ -23,7 +23,6 @@ use crate::{ }; use codec::{Decode, Encode}; use frame_support::{ - assert_err, pallet_prelude::Weight, traits::{fungibles::Mutate, Currency}, }; @@ -102,7 +101,7 @@ fn test_xcm_execute() { 0, Weight::MAX, None, - VersionedXcm::V4(message).encode(), + VersionedXcm::V4(message).encode().encode(), DebugInfo::UnsafeDebug, CollectEvents::UnsafeCollect, Determinism::Enforced, @@ -146,7 +145,7 @@ fn test_xcm_execute_incomplete() { 0, Weight::MAX, None, - VersionedXcm::V4(message).encode(), + VersionedXcm::V4(message).encode().encode(), DebugInfo::UnsafeDebug, CollectEvents::UnsafeCollect, Determinism::Enforced, @@ -160,37 +159,6 @@ fn test_xcm_execute_incomplete() { }); } -#[test] -fn test_xcm_execute_filtered_call() { - MockNet::reset(); - - let contract_addr = instantiate_test_contract("xcm_execute"); - - ParaA::execute_with(|| { - // `remark` should be rejected, as it is not allowed by our CallFilter. - let call = parachain::RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); - let message: Xcm = Xcm(vec![Transact { - origin_kind: OriginKind::Native, - require_weight_at_most: Weight::MAX, - call: call.encode().into(), - }]); - - let result = ParachainContracts::bare_call( - ALICE, - contract_addr.clone(), - 0, - Weight::MAX, - None, - VersionedXcm::V4(message).encode(), - DebugInfo::UnsafeDebug, - CollectEvents::UnsafeCollect, - Determinism::Enforced, - ); - - assert_err!(result.result, frame_system::Error::::CallFiltered); - }); -} - #[test] fn test_xcm_execute_reentrant_call() { MockNet::reset(); @@ -222,7 +190,7 @@ fn test_xcm_execute_reentrant_call() { 0, Weight::MAX, None, - VersionedXcm::V4(message).encode(), + VersionedXcm::V4(message).encode().encode(), DebugInfo::UnsafeDebug, CollectEvents::UnsafeCollect, Determinism::Enforced, @@ -258,7 +226,7 @@ fn test_xcm_send() { 0, Weight::MAX, None, - (dest, message).encode(), + (dest, message.encode()).encode(), DebugInfo::UnsafeDebug, CollectEvents::UnsafeCollect, Determinism::Enforced, diff --git a/substrate/frame/contracts/proc-macro/src/lib.rs b/substrate/frame/contracts/proc-macro/src/lib.rs index 403db15ac2cbcf796280a29ba4c7a91e3047b21e..1794d09d5ad28e08bf9579bed4f311f469be832c 100644 --- a/substrate/frame/contracts/proc-macro/src/lib.rs +++ b/substrate/frame/contracts/proc-macro/src/lib.rs @@ -497,9 +497,14 @@ fn expand_docs(def: &EnvDef) -> TokenStream2 { fn expand_env(def: &EnvDef, docs: bool) -> TokenStream2 { let impls = expand_impls(def); let docs = docs.then_some(expand_docs(def)).unwrap_or(TokenStream2::new()); + let stable_api_count = def.host_funcs.iter().filter(|f| f.is_stable).count(); quote! { pub struct Env; + + #[cfg(test)] + pub const STABLE_API_COUNT: usize = #stable_api_count; + #impls /// Documentation of the API (host functions) available to contracts. /// @@ -638,37 +643,34 @@ fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2) }; let sync_gas_before = if expand_blocks { quote! { - // Gas left in the gas meter right before switching to engine execution. - let __gas_before__ = { - let engine_consumed_total = + // Write gas from wasmi into pallet-contracts before entering the host function. + let __gas_left_before__ = { + let executor_total = __caller__.fuel_consumed().expect("Fuel metering is enabled; qed"); - let gas_meter = __caller__.data_mut().ext().gas_meter_mut(); - gas_meter - .charge_fuel(engine_consumed_total) + __caller__ + .data_mut() + .ext() + .gas_meter_mut() + .sync_from_executor(executor_total) .map_err(TrapReason::from) .map_err(#into_host)? - .ref_time() }; } } else { quote! { } }; - // Gas left in the gas meter right after returning from engine execution. + // Write gas from pallet-contracts into wasmi after leaving the host function. let sync_gas_after = if expand_blocks { quote! { - let mut gas_after = __caller__.data_mut().ext().gas_meter().gas_left().ref_time(); - let mut host_consumed = __gas_before__.saturating_sub(gas_after); - // Possible undercharge of at max 1 fuel here, if host consumed less than `instruction_weights.base` - // Not a problem though, as soon as host accounts its spent gas properly. - let fuel_consumed = host_consumed - .checked_div(__caller__.data_mut().ext().schedule().instruction_weights.base as u64) - .ok_or(Error::::InvalidSchedule) - .map_err(TrapReason::from) - .map_err(#into_host)?; + let fuel_consumed = __caller__ + .data_mut() + .ext() + .gas_meter_mut() + .sync_to_executor(__gas_left_before__) + .map_err(TrapReason::from)?; __caller__ - .consume_fuel(fuel_consumed) - .map_err(|_| TrapReason::from(Error::::OutOfGas)) - .map_err(#into_host)?; + .consume_fuel(fuel_consumed.into()) + .map_err(|_| TrapReason::from(Error::::OutOfGas))?; } } else { quote! { } diff --git a/substrate/frame/contracts/src/benchmarking/code.rs b/substrate/frame/contracts/src/benchmarking/code.rs index 644c2abf0a8d5a49443f26e38b8869c134a7805f..b97cf168e26d4562e4f4a3b7f07ad243174e7a98 100644 --- a/substrate/frame/contracts/src/benchmarking/code.rs +++ b/substrate/frame/contracts/src/benchmarking/code.rs @@ -26,7 +26,7 @@ use crate::Config; use frame_support::traits::Get; -use sp_runtime::traits::Hash; +use sp_runtime::{traits::Hash, Saturating}; use sp_std::{borrow::ToOwned, prelude::*}; use wasm_instrument::parity_wasm::{ builder, @@ -164,7 +164,7 @@ impl From for WasmModule { // Grant access to linear memory. // Every contract module is required to have an imported memory. - // If no memory is specified in the passed ModuleDefenition, then + // If no memory is specified in the passed ModuleDefinition, then // default to (1, 1). let (init, max) = if let Some(memory) = &def.memory { (memory.min_pages, Some(memory.max_pages)) @@ -262,22 +262,25 @@ impl WasmModule { /// `instantiate_with_code` for different sizes of wasm modules. The generated module maximizes /// instrumentation runtime by nesting blocks as deeply as possible given the byte budget. /// `code_location`: Whether to place the code into `deploy` or `call`. - pub fn sized(target_bytes: u32, code_location: Location) -> Self { + pub fn sized(target_bytes: u32, code_location: Location, use_float: bool) -> Self { use self::elements::Instruction::{End, GetLocal, If, Return}; // Base size of a contract is 63 bytes and each expansion adds 6 bytes. // We do one expansion less to account for the code section and function body // size fields inside the binary wasm module representation which are leb128 encoded // and therefore grow in size when the contract grows. We are not allowed to overshoot // because of the maximum code size that is enforced by `instantiate_with_code`. - let expansions = (target_bytes.saturating_sub(63) / 6).saturating_sub(1); + let mut expansions = (target_bytes.saturating_sub(63) / 6).saturating_sub(1); const EXPANSION: [Instruction; 4] = [GetLocal(0), If(BlockType::NoResult), Return, End]; + let mut locals = vec![Local::new(1, ValueType::I32)]; + if use_float { + locals.push(Local::new(1, ValueType::F32)); + locals.push(Local::new(2, ValueType::F32)); + locals.push(Local::new(3, ValueType::F32)); + expansions.saturating_dec(); + } let mut module = ModuleDefinition { memory: Some(ImportedMemory::max::()), ..Default::default() }; - let body = Some(body::repeated_with_locals( - &[Local::new(1, ValueType::I32)], - expansions, - &EXPANSION, - )); + let body = Some(body::repeated_with_locals(&locals, expansions, &EXPANSION)); match code_location { Location::Call => module.call_body = body, Location::Deploy => module.deploy_body = body, diff --git a/substrate/frame/contracts/src/benchmarking/mod.rs b/substrate/frame/contracts/src/benchmarking/mod.rs index 4b6ff63c6f5d44dfb6a985a222535829a1a4439c..9fb107537ba07945dbfaebdce48555dc1103864f 100644 --- a/substrate/frame/contracts/src/benchmarking/mod.rs +++ b/substrate/frame/contracts/src/benchmarking/mod.rs @@ -16,7 +16,6 @@ // limitations under the License. //! Benchmarks for the contracts pallet - #![cfg(feature = "runtime-benchmarks")] mod code; @@ -36,7 +35,7 @@ use crate::{ Pallet as Contracts, *, }; use codec::{Encode, MaxEncodedLen}; -use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v2::*; use frame_support::{ self, pallet_prelude::StorageVersion, @@ -184,172 +183,215 @@ fn caller_funding() -> BalanceOf { BalanceOf::::max_value() / 10_000u32.into() } -benchmarks! { - where_clause { where +#[benchmarks( + where as codec::HasCompact>::Type: Clone + Eq + PartialEq + sp_std::fmt::Debug + scale_info::TypeInfo + codec::Encode, T: Config + pallet_balances::Config, BalanceOf: From< as Currency>::Balance>, as Currency>::Balance: From>, - } +)] +mod benchmarks { + use super::*; // The base weight consumed on processing contracts deletion queue. - #[pov_mode = Measured] - on_process_deletion_queue_batch {}: { - ContractInfo::::process_deletion_queue_batch(Weight::MAX) + #[benchmark(pov_mode = Measured)] + fn on_process_deletion_queue_batch() { + #[block] + { + ContractInfo::::process_deletion_queue_batch(Weight::MAX); + } } - #[skip_meta] - #[pov_mode = Measured] - on_initialize_per_trie_key { - let k in 0..1024; - let instance = Contract::::with_storage(WasmModule::dummy(), k, T::Schedule::get().limits.payload_len)?; + #[benchmark(skip_meta, pov_mode = Measured)] + fn on_initialize_per_trie_key(k: Linear<0, 1024>) -> Result<(), BenchmarkError> { + let instance = Contract::::with_storage( + WasmModule::dummy(), + k, + T::Schedule::get().limits.payload_len, + )?; instance.info()?.queue_trie_for_deletion(); - }: { - ContractInfo::::process_deletion_queue_batch(Weight::MAX) + + #[block] + { + ContractInfo::::process_deletion_queue_batch(Weight::MAX); + } + + Ok(()) } // This benchmarks the v9 migration step (update codeStorage). - #[pov_mode = Measured] - v9_migration_step { - let c in 0 .. T::MaxCodeLen::get(); + #[benchmark(pov_mode = Measured)] + fn v9_migration_step(c: Linear<0, { T::MaxCodeLen::get() }>) { v09::store_old_dummy_code::(c as usize); let mut m = v09::Migration::::default(); - }: { - m.step(); + #[block] + { + m.step(); + } } // This benchmarks the v10 migration step (use dedicated deposit_account). - #[pov_mode = Measured] - v10_migration_step { - let contract = >::with_caller( - whitelisted_caller(), WasmModule::dummy(), vec![], - )?; - - v10::store_old_contract_info::>(contract.account_id.clone(), contract.info()?); + #[benchmark(pov_mode = Measured)] + fn v10_migration_step() -> Result<(), BenchmarkError> { + let contract = + >::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?; + + v10::store_old_contract_info::>( + contract.account_id.clone(), + contract.info()?, + ); let mut m = v10::Migration::>::default(); - }: { - m.step(); + + #[block] + { + m.step(); + } + + Ok(()) } - // This benchmarks the v11 migration step (Don't rely on reserved balances keeping an account alive). - #[pov_mode = Measured] - v11_migration_step { - let k in 0 .. 1024; + // This benchmarks the v11 migration step (Don't rely on reserved balances keeping an account + // alive). + #[benchmark(pov_mode = Measured)] + fn v11_migration_step(k: Linear<0, 1024>) { v11::fill_old_queue::(k as usize); let mut m = v11::Migration::::default(); - }: { - m.step(); + + #[block] + { + m.step(); + } } // This benchmarks the v12 migration step (Move `OwnerInfo` to `CodeInfo`, // add `determinism` field to the latter, clear `CodeStorage` // and repay deposits). - #[pov_mode = Measured] - v12_migration_step { - let c in 0 .. T::MaxCodeLen::get(); - v12::store_old_dummy_code::< - T, - pallet_balances::Pallet - >(c as usize, account::("account", 0, 0)); + #[benchmark(pov_mode = Measured)] + fn v12_migration_step(c: Linear<0, { T::MaxCodeLen::get() }>) { + v12::store_old_dummy_code::>( + c as usize, + account::("account", 0, 0), + ); let mut m = v12::Migration::>::default(); - }: { - m.step(); + + #[block] + { + m.step(); + } } // This benchmarks the v13 migration step (Add delegate_dependencies field). - #[pov_mode = Measured] - v13_migration_step { - let contract = >::with_caller( - whitelisted_caller(), WasmModule::dummy(), vec![], - )?; + #[benchmark(pov_mode = Measured)] + fn v13_migration_step() -> Result<(), BenchmarkError> { + let contract = + >::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?; v13::store_old_contract_info::(contract.account_id.clone(), contract.info()?); let mut m = v13::Migration::::default(); - }: { - m.step(); + + #[block] + { + m.step(); + } + Ok(()) } - // This benchmarks the v14 migration step (Move code owners' reserved balance to be held instead). - #[pov_mode = Measured] - v14_migration_step { + // This benchmarks the v14 migration step (Move code owners' reserved balance to be held + // instead). + #[benchmark(pov_mode = Measured)] + fn v14_migration_step() { let account = account::("account", 0, 0); T::Currency::set_balance(&account, caller_funding::()); v14::store_dummy_code::>(account); let mut m = v14::Migration::>::default(); - }: { - m.step(); + + #[block] + { + m.step(); + } } // This benchmarks the v15 migration step (remove deposit account). - #[pov_mode = Measured] - v15_migration_step { - let contract = >::with_caller( - whitelisted_caller(), WasmModule::dummy(), vec![], - )?; + #[benchmark(pov_mode = Measured)] + fn v15_migration_step() -> Result<(), BenchmarkError> { + let contract = + >::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?; v15::store_old_contract_info::(contract.account_id.clone(), contract.info()?); let mut m = v15::Migration::::default(); - }: { - m.step(); + + #[block] + { + m.step(); + } + + Ok(()) } // This benchmarks the weight of executing Migration::migrate to execute a noop migration. - #[pov_mode = Measured] - migration_noop { + #[benchmark(pov_mode = Measured)] + fn migration_noop() { let version = LATEST_MIGRATION_VERSION; assert_eq!(StorageVersion::get::>(), version); - }: { - Migration::::migrate(Weight::MAX) - } verify { + #[block] + { + Migration::::migrate(Weight::MAX); + } assert_eq!(StorageVersion::get::>(), version); } - // This benchmarks the weight of dispatching migrate to execute 1 `NoopMigraton` - #[pov_mode = Measured] - migrate { + // This benchmarks the weight of dispatching migrate to execute 1 `NoopMigration` + #[benchmark(pov_mode = Measured)] + fn migrate() { let latest_version = LATEST_MIGRATION_VERSION; StorageVersion::new(latest_version - 2).put::>(); - as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade(); - let caller: T::AccountId = whitelisted_caller(); - let origin = RawOrigin::Signed(caller.clone()); - }: _(origin, Weight::MAX) - verify { + as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade(); + + #[extrinsic_call] + _(RawOrigin::Signed(whitelisted_caller()), Weight::MAX); + assert_eq!(StorageVersion::get::>(), latest_version - 1); } - // This benchmarks the weight of running on_runtime_upgrade when there are no migration in progress. - #[pov_mode = Measured] - on_runtime_upgrade_noop { + // This benchmarks the weight of running on_runtime_upgrade when there are no migration in + // progress. + #[benchmark(pov_mode = Measured)] + fn on_runtime_upgrade_noop() { let latest_version = LATEST_MIGRATION_VERSION; assert_eq!(StorageVersion::get::>(), latest_version); - }: { - as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade() - } verify { + #[block] + { + as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade(); + } assert!(MigrationInProgress::::get().is_none()); } - // This benchmarks the weight of running on_runtime_upgrade when there is a migration in progress. - #[pov_mode = Measured] - on_runtime_upgrade_in_progress { + // This benchmarks the weight of running on_runtime_upgrade when there is a migration in + // progress. + #[benchmark(pov_mode = Measured)] + fn on_runtime_upgrade_in_progress() { let latest_version = LATEST_MIGRATION_VERSION; StorageVersion::new(latest_version - 2).put::>(); let v = vec![42u8].try_into().ok(); MigrationInProgress::::set(v.clone()); - }: { - as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade() - } verify { + #[block] + { + as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade(); + } assert!(MigrationInProgress::::get().is_some()); assert_eq!(MigrationInProgress::::get(), v); } - // This benchmarks the weight of running on_runtime_upgrade when there is a migration to process. - #[pov_mode = Measured] - on_runtime_upgrade { + // This benchmarks the weight of running on_runtime_upgrade when there is a migration to + // process. + #[benchmark(pov_mode = Measured)] + fn on_runtime_upgrade() { let latest_version = LATEST_MIGRATION_VERSION; StorageVersion::new(latest_version - 2).put::>(); - }: { - as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade() - } verify { + #[block] + { + as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade(); + } assert!(MigrationInProgress::::get().is_some()); } @@ -357,17 +399,25 @@ benchmarks! { // the sandbox. This does **not** include the actual execution for which the gas meter // is responsible. This is achieved by generating all code to the `deploy` function // which is in the wasm module but not executed on `call`. - // The results are supposed to be used as `call_with_code_per_byte(c) - call_with_code_per_byte(0)`. - #[pov_mode = Measured] - call_with_code_per_byte { - let c in 0 .. T::MaxCodeLen::get(); + // The results are supposed to be used as `call_with_code_per_byte(c) - + // call_with_code_per_byte(0)`. + #[benchmark(pov_mode = Measured)] + fn call_with_code_per_byte( + c: Linear<0, { T::MaxCodeLen::get() }>, + ) -> Result<(), BenchmarkError> { let instance = Contract::::with_caller( - whitelisted_caller(), WasmModule::sized(c, Location::Deploy), vec![], + whitelisted_caller(), + WasmModule::sized(c, Location::Deploy, false), + vec![], )?; let value = Pallet::::min_balance(); - let origin = RawOrigin::Signed(instance.caller.clone()); let callee = instance.addr; - }: call(origin, callee, value, Weight::MAX, None, vec![]) + + #[extrinsic_call] + call(RawOrigin::Signed(instance.caller.clone()), callee, value, Weight::MAX, None, vec![]); + + Ok(()) + } // This constructs a contract that is maximal expensive to instrument. // It creates a maximum number of metering blocks per byte. @@ -379,24 +429,28 @@ benchmarks! { // `c`: Size of the code in bytes. // `i`: Size of the input in bytes. // `s`: Size of the salt in bytes. - #[pov_mode = Measured] - instantiate_with_code { - let c in 0 .. T::MaxCodeLen::get(); - let i in 0 .. code::max_pages::() * 64 * 1024; - let s in 0 .. code::max_pages::() * 64 * 1024; + #[benchmark(pov_mode = Measured)] + fn instantiate_with_code( + c: Linear<0, { T::MaxCodeLen::get() }>, + i: Linear<0, { code::max_pages::() * 64 * 1024 }>, + s: Linear<0, { code::max_pages::() * 64 * 1024 }>, + ) { let input = vec![42u8; i as usize]; let salt = vec![42u8; s as usize]; let value = Pallet::::min_balance(); let caller = whitelisted_caller(); T::Currency::set_balance(&caller, caller_funding::()); - let WasmModule { code, hash, .. } = WasmModule::::sized(c, Location::Call); + let WasmModule { code, hash, .. } = WasmModule::::sized(c, Location::Call, false); let origin = RawOrigin::Signed(caller.clone()); let addr = Contracts::::contract_address(&caller, &hash, &input, &salt); - }: _(origin, value, Weight::MAX, None, code, input, salt) - verify { - let deposit = T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr); + #[extrinsic_call] + _(origin, value, Weight::MAX, None, code, input, salt); + + let deposit = + T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr); // uploading the code reserves some balance in the callers account - let code_deposit = T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &caller); + let code_deposit = + T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &caller); assert_eq!( T::Currency::balance(&caller), caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), @@ -408,22 +462,25 @@ benchmarks! { // Instantiate uses a dummy contract constructor to measure the overhead of the instantiate. // `i`: Size of the input in bytes. // `s`: Size of the salt in bytes. - #[pov_mode = Measured] - instantiate { - let i in 0 .. code::max_pages::() * 64 * 1024; - let s in 0 .. code::max_pages::() * 64 * 1024; + #[benchmark(pov_mode = Measured)] + fn instantiate( + i: Linear<0, { code::max_pages::() * 64 * 1024 }>, + s: Linear<0, { code::max_pages::() * 64 * 1024 }>, + ) -> Result<(), BenchmarkError> { let input = vec![42u8; i as usize]; let salt = vec![42u8; s as usize]; let value = Pallet::::min_balance(); let caller = whitelisted_caller(); T::Currency::set_balance(&caller, caller_funding::()); let WasmModule { code, hash, .. } = WasmModule::::dummy(); - let origin = RawOrigin::Signed(caller.clone()); let addr = Contracts::::contract_address(&caller, &hash, &input, &salt); Contracts::::store_code_raw(code, caller.clone())?; - }: _(origin, value, Weight::MAX, None, hash, input, salt) - verify { - let deposit = T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), value, Weight::MAX, None, hash, input, salt); + + let deposit = + T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr); // value was removed from the caller assert_eq!( T::Currency::balance(&caller), @@ -431,6 +488,8 @@ benchmarks! { ); // contract has the full value assert_eq!(T::Currency::balance(&addr), value + Pallet::::min_balance()); + + Ok(()) } // We just call a dummy contract to measure the overhead of the call extrinsic. @@ -440,19 +499,21 @@ benchmarks! { // part of `seal_input`. The costs for invoking a contract of a specific size are not part // of this benchmark because we cannot know the size of the contract when issuing a call // transaction. See `call_with_code_per_byte` for this. - #[pov_mode = Measured] - call { + #[benchmark(pov_mode = Measured)] + fn call() -> Result<(), BenchmarkError> { let data = vec![42u8; 1024]; - let instance = Contract::::with_caller( - whitelisted_caller(), WasmModule::dummy(), vec![], - )?; + let instance = + Contract::::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?; let value = Pallet::::min_balance(); let origin = RawOrigin::Signed(instance.caller.clone()); let callee = instance.addr.clone(); let before = T::Currency::balance(&instance.account_id); - }: _(origin, callee, value, Weight::MAX, None, data) - verify { - let deposit = T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &instance.account_id); + #[extrinsic_call] + _(origin, callee, value, Weight::MAX, None, data); + let deposit = T::Currency::balance_on_hold( + &HoldReason::StorageDepositReserve.into(), + &instance.account_id, + ); // value and value transferred via call should be removed from the caller assert_eq!( T::Currency::balance(&instance.caller), @@ -462,75 +523,93 @@ benchmarks! { assert_eq!(T::Currency::balance(&instance.account_id), before + value); // contract should still exist instance.info()?; + + Ok(()) } // This constructs a contract that is maximal expensive to instrument. // It creates a maximum number of metering blocks per byte. // `c`: Size of the code in bytes. - #[pov_mode = Measured] - upload_code { - let c in 0 .. T::MaxCodeLen::get(); + #[benchmark(pov_mode = Measured)] + fn upload_code_determinism_enforced(c: Linear<0, { T::MaxCodeLen::get() }>) { let caller = whitelisted_caller(); T::Currency::set_balance(&caller, caller_funding::()); - let WasmModule { code, hash, .. } = WasmModule::::sized(c, Location::Call); + let WasmModule { code, hash, .. } = WasmModule::::sized(c, Location::Call, false); let origin = RawOrigin::Signed(caller.clone()); - }: _(origin, code, None, Determinism::Enforced) - verify { + #[extrinsic_call] + upload_code(origin, code, None, Determinism::Enforced); // uploading the code reserves some balance in the callers account assert!(T::Currency::total_balance_on_hold(&caller) > 0u32.into()); assert!(>::code_exists(&hash)); } + // Uploading code with [`Determinism::Relaxed`] should be more expensive than uploading code + // with [`Determinism::Enforced`], as we always try to save the code with + // [`Determinism::Enforced`] first. + #[benchmark(pov_mode = Measured)] + fn upload_code_determinism_relaxed(c: Linear<0, { T::MaxCodeLen::get() }>) { + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + let WasmModule { code, hash, .. } = WasmModule::::sized(c, Location::Call, true); + let origin = RawOrigin::Signed(caller.clone()); + #[extrinsic_call] + upload_code(origin, code, None, Determinism::Relaxed); + assert!(T::Currency::total_balance_on_hold(&caller) > 0u32.into()); + assert!(>::code_exists(&hash)); + // Ensure that the benchmark follows the most expensive path, i.e., the code is saved with + assert_eq!(CodeInfoOf::::get(&hash).unwrap().determinism(), Determinism::Relaxed); + } + // Removing code does not depend on the size of the contract because all the information // needed to verify the removal claim (refcount, owner) is stored in a separate storage // item (`CodeInfoOf`). - #[pov_mode = Measured] - remove_code { + #[benchmark(pov_mode = Measured)] + fn remove_code() -> Result<(), BenchmarkError> { let caller = whitelisted_caller(); T::Currency::set_balance(&caller, caller_funding::()); let WasmModule { code, hash, .. } = WasmModule::::dummy(); let origin = RawOrigin::Signed(caller.clone()); - let uploaded = >::bare_upload_code(caller.clone(), code, None, Determinism::Enforced)?; + let uploaded = + >::bare_upload_code(caller.clone(), code, None, Determinism::Enforced)?; assert_eq!(uploaded.code_hash, hash); assert_eq!(uploaded.deposit, T::Currency::total_balance_on_hold(&caller)); assert!(>::code_exists(&hash)); - }: _(origin, hash) - verify { + #[extrinsic_call] + _(origin, hash); // removing the code should have unreserved the deposit assert_eq!(T::Currency::total_balance_on_hold(&caller), 0u32.into()); assert!(>::code_removed(&hash)); + Ok(()) } - #[pov_mode = Measured] - set_code { - let instance = >::with_caller( - whitelisted_caller(), WasmModule::dummy(), vec![], - )?; + #[benchmark(pov_mode = Measured)] + fn set_code() -> Result<(), BenchmarkError> { + let instance = + >::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?; // we just add some bytes so that the code hash is different let WasmModule { code, hash, .. } = >::dummy_with_bytes(128); >::store_code_raw(code, instance.caller.clone())?; let callee = instance.addr.clone(); assert_ne!(instance.info()?.code_hash, hash); - }: _(RawOrigin::Root, callee, hash) - verify { + #[extrinsic_call] + _(RawOrigin::Root, callee, hash); assert_eq!(instance.info()?.code_hash, hash); + Ok(()) } - #[pov_mode = Measured] - seal_caller { - let r in 0 .. API_BENCHMARK_RUNS; - let instance = Contract::::new(WasmModule::getter( - "seal0", "seal_caller", r - ), vec![])?; + #[benchmark(pov_mode = Measured)] + fn seal_caller(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { + let instance = Contract::::new(WasmModule::getter("seal0", "seal_caller", r), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); - #[pov_mode = Measured] - seal_is_contract { - let r in 0 .. API_BENCHMARK_RUNS; - let accounts = (0 .. r) - .map(|n| account::("account", n, 0)) - .collect::>(); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn seal_is_contract(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { + let accounts = (0..r).map(|n| account::("account", n, 0)).collect::>(); let account_len = accounts.get(0).map(|i| i.encode().len()).unwrap_or(0); let accounts_bytes = accounts.iter().flat_map(|a| a.encode()).collect::>(); let code = WasmModule::::from(ModuleDefinition { @@ -541,18 +620,16 @@ benchmarks! { params: vec![ValueType::I32], return_type: Some(ValueType::I32), }], - data_segments: vec![ - DataSegment { - offset: 0, - value: accounts_bytes - }, - ], - call_body: Some(body::repeated_dyn(r, vec![ - Counter(0, account_len as u32), // address_ptr - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), - ])), - .. Default::default() + data_segments: vec![DataSegment { offset: 0, value: accounts_bytes }], + call_body: Some(body::repeated_dyn( + r, + vec![ + Counter(0, account_len as u32), // address_ptr + Regular(Instruction::Call(0)), + Regular(Instruction::Drop), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; @@ -561,18 +638,17 @@ benchmarks! { >::insert(acc, info.clone()); } let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); - #[pov_mode = Measured] - seal_code_hash { - let r in 0 .. API_BENCHMARK_RUNS; - let accounts = (0 .. r) - .map(|n| account::("account", n, 0)) - .collect::>(); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn seal_code_hash(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { + let accounts = (0..r).map(|n| account::("account", n, 0)).collect::>(); let account_len = accounts.get(0).map(|i| i.encode().len()).unwrap_or(0); let accounts_bytes = accounts.iter().flat_map(|a| a.encode()).collect::>(); - let accounts_len = accounts_bytes.len(); - let pages = code::max_pages::(); let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -586,19 +662,19 @@ benchmarks! { offset: 0, value: 32u32.to_le_bytes().to_vec(), // output length }, - DataSegment { - offset: 36, - value: accounts_bytes, - }, + DataSegment { offset: 36, value: accounts_bytes }, ], - call_body: Some(body::repeated_dyn(r, vec![ - Counter(36, account_len as u32), // address_ptr - Regular(Instruction::I32Const(4)), // ptr to output data - Regular(Instruction::I32Const(0)), // ptr to output length - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), - ])), - .. Default::default() + call_body: Some(body::repeated_dyn( + r, + vec![ + Counter(36, account_len as u32), // address_ptr + Regular(Instruction::I32Const(4)), // ptr to output data + Regular(Instruction::I32Const(0)), // ptr to output length + Regular(Instruction::Call(0)), + Regular(Instruction::Drop), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; @@ -607,20 +683,23 @@ benchmarks! { >::insert(acc, info.clone()); } let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - - #[pov_mode = Measured] - seal_own_code_hash { - let r in 0 .. API_BENCHMARK_RUNS; - let instance = Contract::::new(WasmModule::getter( - "seal0", "seal_own_code_hash", r - ), vec![])?; + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn seal_own_code_hash(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { + let instance = + Contract::::new(WasmModule::getter("seal0", "seal_own_code_hash", r), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - #[pov_mode = Measured] - seal_caller_is_origin { - let r in 0 .. API_BENCHMARK_RUNS; + #[benchmark(pov_mode = Measured)] + fn seal_caller_is_origin(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -629,20 +708,18 @@ benchmarks! { params: vec![], return_type: Some(ValueType::I32), }], - call_body: Some(body::repeated(r, &[ - Instruction::Call(0), - Instruction::Drop, - ])), - .. Default::default() + call_body: Some(body::repeated(r, &[Instruction::Call(0), Instruction::Drop])), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - - #[pov_mode = Measured] - seal_caller_is_root { - let r in 0 .. API_BENCHMARK_RUNS; + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } + #[benchmark(pov_mode = Measured)] + fn seal_caller_is_root(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -651,82 +728,84 @@ benchmarks! { params: vec![], return_type: Some(ValueType::I32), }], - call_body: Some(body::repeated(r, &[ - Instruction::Call(0), - Instruction::Drop, - ])), - .. Default::default() + call_body: Some(body::repeated(r, &[Instruction::Call(0), Instruction::Drop])), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Root; - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - - #[pov_mode = Measured] - seal_address { - let r in 0 .. API_BENCHMARK_RUNS; - let instance = Contract::::new(WasmModule::getter( - "seal0", "seal_address", r - ), vec![])?; + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn seal_address(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { + let instance = Contract::::new(WasmModule::getter("seal0", "seal_address", r), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - - #[pov_mode = Measured] - seal_gas_left { - let r in 0 .. API_BENCHMARK_RUNS; - let instance = Contract::::new(WasmModule::getter( - "seal1", "gas_left", r - ), vec![])?; + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn seal_gas_left(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { + let instance = Contract::::new(WasmModule::getter("seal1", "gas_left", r), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - - #[pov_mode = Measured] - seal_balance { - let r in 0 .. API_BENCHMARK_RUNS; - let instance = Contract::::new(WasmModule::getter( - "seal0", "seal_balance", r - ), vec![])?; + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn seal_balance(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { + let instance = Contract::::new(WasmModule::getter("seal0", "seal_balance", r), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - - #[pov_mode = Measured] - seal_value_transferred { - let r in 0 .. API_BENCHMARK_RUNS; - let instance = Contract::::new(WasmModule::getter( - "seal0", "seal_value_transferred", r - ), vec![])?; + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn seal_value_transferred(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { + let instance = + Contract::::new(WasmModule::getter("seal0", "seal_value_transferred", r), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - - #[pov_mode = Measured] - seal_minimum_balance { - let r in 0 .. API_BENCHMARK_RUNS; - let instance = Contract::::new(WasmModule::getter( - "seal0", "seal_minimum_balance", r - ), vec![])?; + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn seal_minimum_balance(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { + let instance = + Contract::::new(WasmModule::getter("seal0", "seal_minimum_balance", r), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - - #[pov_mode = Measured] - seal_block_number { - let r in 0 .. API_BENCHMARK_RUNS; - let instance = Contract::::new(WasmModule::getter( - "seal0", "seal_block_number", r - ), vec![])?; + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn seal_block_number(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { + let instance = + Contract::::new(WasmModule::getter("seal0", "seal_block_number", r), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - - #[pov_mode = Measured] - seal_now { - let r in 0 .. API_BENCHMARK_RUNS; - let instance = Contract::::new(WasmModule::getter( - "seal0", "seal_now", r - ), vec![])?; + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn seal_now(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { + let instance = Contract::::new(WasmModule::getter("seal0", "seal_now", r), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - #[pov_mode = Measured] - seal_weight_to_fee { - let r in 0 .. API_BENCHMARK_RUNS; + #[benchmark(pov_mode = Measured)] + fn seal_weight_to_fee(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { let pages = code::max_pages::(); let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), @@ -740,22 +819,27 @@ benchmarks! { offset: 0, value: (pages * 64 * 1024 - 4).to_le_bytes().to_vec(), }], - call_body: Some(body::repeated(r, &[ - Instruction::I64Const(500_000), - Instruction::I64Const(300_000), - Instruction::I32Const(4), - Instruction::I32Const(0), - Instruction::Call(0), - ])), - .. Default::default() + call_body: Some(body::repeated( + r, + &[ + Instruction::I64Const(500_000), + Instruction::I64Const(300_000), + Instruction::I32Const(4), + Instruction::I32Const(0), + Instruction::Call(0), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - #[pov_mode = Measured] - seal_input { - let r in 0 .. API_BENCHMARK_RUNS; + #[benchmark(pov_mode = Measured)] + fn seal_input(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -764,26 +848,28 @@ benchmarks! { params: vec![ValueType::I32, ValueType::I32], return_type: None, }], - data_segments: vec![ - DataSegment { - offset: 0, - value: 0u32.to_le_bytes().to_vec(), - }, - ], - call_body: Some(body::repeated(r, &[ - Instruction::I32Const(4), // ptr where to store output - Instruction::I32Const(0), // ptr to length - Instruction::Call(0), - ])), - .. Default::default() + data_segments: vec![DataSegment { offset: 0, value: 0u32.to_le_bytes().to_vec() }], + call_body: Some(body::repeated( + r, + &[ + Instruction::I32Const(4), // ptr where to store output + Instruction::I32Const(0), // ptr to length + Instruction::Call(0), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - #[pov_mode = Measured] - seal_input_per_byte { - let n in 0 .. code::max_pages::() * 64 * 1024; + #[benchmark(pov_mode = Measured)] + fn seal_input_per_byte( + n: Linear<0, { code::max_pages::() * 64 * 1024 }>, + ) -> Result<(), BenchmarkError> { let buffer_size = code::max_pages::() * 64 * 1024 - 4; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), @@ -793,31 +879,31 @@ benchmarks! { params: vec![ValueType::I32, ValueType::I32], return_type: None, }], - data_segments: vec![ - DataSegment { - offset: 0, - value: buffer_size.to_le_bytes().to_vec(), - }, - ], + data_segments: vec![DataSegment { + offset: 0, + value: buffer_size.to_le_bytes().to_vec(), + }], call_body: Some(body::plain(vec![ Instruction::I32Const(4), // ptr where to store output Instruction::I32Const(0), // ptr to length Instruction::Call(0), Instruction::End, ])), - .. Default::default() + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let data = vec![42u8; n.min(buffer_size) as usize]; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, data) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, data); + Ok(()) + } // We cannot call `seal_return` multiple times. Therefore our weight determination is not // as precise as with other APIs. Because this function can only be called once per // contract it cannot be used as an attack vector. - #[pov_mode = Measured] - seal_return { - let r in 0 .. 1; + #[benchmark(pov_mode = Measured)] + fn seal_return(r: Linear<0, 1>) -> Result<(), BenchmarkError> { let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -826,21 +912,28 @@ benchmarks! { params: vec![ValueType::I32, ValueType::I32, ValueType::I32], return_type: None, }], - call_body: Some(body::repeated(r, &[ - Instruction::I32Const(0), // flags - Instruction::I32Const(0), // data_ptr - Instruction::I32Const(0), // data_len - Instruction::Call(0), - ])), - .. Default::default() + call_body: Some(body::repeated( + r, + &[ + Instruction::I32Const(0), // flags + Instruction::I32Const(0), // data_ptr + Instruction::I32Const(0), // data_len + Instruction::Call(0), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - #[pov_mode = Measured] - seal_return_per_byte { - let n in 0 .. code::max_pages::() * 64 * 1024; + #[benchmark(pov_mode = Measured)] + fn seal_return_per_byte( + n: Linear<0, { code::max_pages::() * 64 * 1024 }>, + ) -> Result<(), BenchmarkError> { let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -850,22 +943,24 @@ benchmarks! { return_type: None, }], call_body: Some(body::plain(vec![ - Instruction::I32Const(0), // flags - Instruction::I32Const(0), // data_ptr + Instruction::I32Const(0), // flags + Instruction::I32Const(0), // data_ptr Instruction::I32Const(n as i32), // data_len Instruction::Call(0), Instruction::End, ])), - .. Default::default() + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // The same argument as for `seal_return` is true here. - #[pov_mode = Measured] - seal_terminate { - let r in 0 .. 1; + #[benchmark(pov_mode = Measured)] + fn seal_terminate(r: Linear<0, 1>) -> Result<(), BenchmarkError> { let beneficiary = account::("beneficiary", 0, 0); let beneficiary_bytes = beneficiary.encode(); let beneficiary_len = beneficiary_bytes.len(); @@ -898,49 +993,74 @@ benchmarks! { name: "lock_delegate_dependency", params: vec![ValueType::I32], return_type: None, - } + }, ], data_segments: vec![ - DataSegment { - offset: 0, - value: beneficiary_bytes, - }, - DataSegment { - offset: beneficiary_len as u32, - value: code_hashes_bytes, - }, + DataSegment { offset: 0, value: beneficiary_bytes }, + DataSegment { offset: beneficiary_len as u32, value: code_hashes_bytes }, ], - deploy_body: Some(body::repeated_dyn(r, vec![ - Counter(beneficiary_len as u32, code_hash_len as u32), // code_hash_ptr - Regular(Instruction::Call(1)), - ])), - call_body: Some(body::repeated(r, &[ - Instruction::I32Const(0), // beneficiary_ptr - Instruction::I32Const(beneficiary_len as i32), // beneficiary_len - Instruction::Call(0), - ])), - .. Default::default() + deploy_body: Some(body::repeated_dyn( + T::MaxDelegateDependencies::get(), + vec![ + Counter(beneficiary_len as u32, code_hash_len as u32), // code_hash_ptr + Regular(Instruction::Call(1)), + ], + )), + call_body: Some(body::repeated( + r, + &[ + Instruction::I32Const(0), // beneficiary_ptr + Instruction::I32Const(beneficiary_len as i32), // beneficiary_len + Instruction::Call(0), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); assert_eq!(T::Currency::total_balance(&beneficiary), 0u32.into()); - assert_eq!(T::Currency::balance(&instance.account_id), Pallet::::min_balance() * 2u32.into()); - assert_ne!(T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &instance.account_id), 0u32.into()); - }: call(origin, instance.addr.clone(), 0u32.into(), Weight::MAX, None, vec![]) - verify { + assert_eq!( + T::Currency::balance(&instance.account_id), + Pallet::::min_balance() * 2u32.into() + ); + assert_ne!( + T::Currency::balance_on_hold( + &HoldReason::StorageDepositReserve.into(), + &instance.account_id + ), + 0u32.into() + ); + assert_eq!( + ContractInfoOf::::get(&instance.account_id) + .unwrap() + .delegate_dependencies_count() as u32, + T::MaxDelegateDependencies::get() + ); + #[extrinsic_call] + call(origin, instance.addr.clone(), 0u32.into(), Weight::MAX, None, vec![]); + if r > 0 { assert_eq!(T::Currency::total_balance(&instance.account_id), 0u32.into()); - assert_eq!(T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &instance.account_id), 0u32.into()); - assert_eq!(T::Currency::total_balance(&beneficiary), Pallet::::min_balance() * 2u32.into()); + assert_eq!( + T::Currency::balance_on_hold( + &HoldReason::StorageDepositReserve.into(), + &instance.account_id + ), + 0u32.into() + ); + assert_eq!( + T::Currency::total_balance(&beneficiary), + Pallet::::min_balance() * 2u32.into() + ); } + Ok(()) } // We benchmark only for the maximum subject length. We assume that this is some lowish // number (< 1 KB). Therefore we are not overcharging too much in case a smaller subject is // used. - #[pov_mode = Measured] - seal_random { - let r in 0 .. API_BENCHMARK_RUNS; + #[benchmark(pov_mode = Measured)] + fn seal_random(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { let pages = code::max_pages::(); let subject_len = T::Schedule::get().limits.subject_len; assert!(subject_len < 1024); @@ -952,30 +1072,33 @@ benchmarks! { params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32], return_type: None, }], - data_segments: vec![ - DataSegment { - offset: 0, - value: (pages * 64 * 1024 - subject_len - 4).to_le_bytes().to_vec(), - }, - ], - call_body: Some(body::repeated(r, &[ - Instruction::I32Const(4), // subject_ptr - Instruction::I32Const(subject_len as i32), // subject_len - Instruction::I32Const((subject_len + 4) as i32), // out_ptr - Instruction::I32Const(0), // out_len_ptr - Instruction::Call(0), - ])), - .. Default::default() + data_segments: vec![DataSegment { + offset: 0, + value: (pages * 64 * 1024 - subject_len - 4).to_le_bytes().to_vec(), + }], + call_body: Some(body::repeated( + r, + &[ + Instruction::I32Const(4), // subject_ptr + Instruction::I32Const(subject_len as i32), // subject_len + Instruction::I32Const((subject_len + 4) as i32), // out_ptr + Instruction::I32Const(0), // out_len_ptr + Instruction::Call(0), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // Overhead of calling the function without any topic. // We benchmark for the worst case (largest event). - #[pov_mode = Measured] - seal_deposit_event { - let r in 0 .. API_BENCHMARK_RUNS; + #[benchmark(pov_mode = Measured)] + fn seal_deposit_event(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -984,26 +1107,33 @@ benchmarks! { params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32], return_type: None, }], - call_body: Some(body::repeated(r, &[ - Instruction::I32Const(0), // topics_ptr - Instruction::I32Const(0), // topics_len - Instruction::I32Const(0), // data_ptr - Instruction::I32Const(0), // data_len - Instruction::Call(0), - ])), - .. Default::default() + call_body: Some(body::repeated( + r, + &[ + Instruction::I32Const(0), // topics_ptr + Instruction::I32Const(0), // topics_len + Instruction::I32Const(0), // data_ptr + Instruction::I32Const(0), // data_len + Instruction::Call(0), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // Benchmark the overhead that topics generate. // `t`: Number of topics // `n`: Size of event payload in bytes - #[pov_mode = Measured] - seal_deposit_event_per_topic_and_byte { - let t in 0 .. T::Schedule::get().limits.event_topics; - let n in 0 .. T::Schedule::get().limits.payload_len; + #[benchmark(pov_mode = Measured)] + fn seal_deposit_event_per_topic_and_byte( + t: Linear<0, { T::Schedule::get().limits.event_topics }>, + n: Linear<0, { T::Schedule::get().limits.payload_len }>, + ) -> Result<(), BenchmarkError> { let topics = (0..t).map(|i| T::Hashing::hash_of(&i)).collect::>().encode(); let topics_len = topics.len(); let code = WasmModule::::from(ModuleDefinition { @@ -1014,32 +1144,29 @@ benchmarks! { params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32], return_type: None, }], - data_segments: vec![ - DataSegment { - offset: 0, - value: topics, - }, - ], + data_segments: vec![DataSegment { offset: 0, value: topics }], call_body: Some(body::plain(vec![ - Instruction::I32Const(0), // topics_ptr + Instruction::I32Const(0), // topics_ptr Instruction::I32Const(topics_len as i32), // topics_len - Instruction::I32Const(0), // data_ptr - Instruction::I32Const(n as i32), // data_len + Instruction::I32Const(0), // data_ptr + Instruction::I32Const(n as i32), // data_len Instruction::Call(0), Instruction::End, ])), - .. Default::default() + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // Benchmark debug_message call with zero input data. // Whereas this function is used in RPC mode only, it still should be secured // against an excessive use. - #[pov_mode = Measured] - seal_debug_message { - let r in 0 .. API_BENCHMARK_RUNS; + #[benchmark(pov_mode = Measured)] + fn seal_debug_message(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory { min_pages: 1, max_pages: 1 }), imported_functions: vec![ImportedFunction { @@ -1048,38 +1175,56 @@ benchmarks! { params: vec![ValueType::I32, ValueType::I32], return_type: Some(ValueType::I32), }], - call_body: Some(body::repeated(r, &[ - Instruction::I32Const(0), // value_ptr - Instruction::I32Const(0), // value_len - Instruction::Call(0), - Instruction::Drop, - ])), - .. Default::default() + call_body: Some(body::repeated( + r, + &[ + Instruction::I32Const(0), // value_ptr + Instruction::I32Const(0), // value_len + Instruction::Call(0), + Instruction::Drop, + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; - }: { - >::bare_call( - instance.caller, - instance.account_id, - 0u32.into(), - Weight::MAX, - None, - vec![], - DebugInfo::UnsafeDebug, - CollectEvents::Skip, - Determinism::Enforced, - ) - .result?; + + #[block] + { + >::bare_call( + instance.caller, + instance.account_id, + 0u32.into(), + Weight::MAX, + None, + vec![], + DebugInfo::UnsafeDebug, + CollectEvents::Skip, + Determinism::Enforced, + ) + .result?; + } + Ok(()) } - seal_debug_message_per_byte { - // Vary size of input in bytes up to maximum allowed contract memory - // or maximum allowed debug buffer size, whichever is less. - let i in 0 .. (T::Schedule::get().limits.memory_pages * 64 * 1024).min(T::MaxDebugBufferLen::get()); + // Vary size of input in bytes up to maximum allowed contract memory + // or maximum allowed debug buffer size, whichever is less. + #[benchmark] + fn seal_debug_message_per_byte( + i: Linear< + 0, + { + (T::Schedule::get().limits.memory_pages * 64 * 1024) + .min(T::MaxDebugBufferLen::get()) + }, + >, + ) -> Result<(), BenchmarkError> { // We benchmark versus messages containing printable ASCII codes. // About 1Kb goes to the contract code instructions, // whereas all the space left we use for the initialization of the debug messages data. - let message = (0 .. T::MaxCodeLen::get() - 1024).zip((32..127).cycle()).map(|i| i.1).collect::>(); + let message = (0..T::MaxCodeLen::get() - 1024) + .zip((32..127).cycle()) + .map(|i| i.1) + .collect::>(); let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory { min_pages: T::Schedule::get().limits.memory_pages, @@ -1090,15 +1235,10 @@ benchmarks! { name: "seal_debug_message", params: vec![ValueType::I32, ValueType::I32], return_type: Some(ValueType::I32), - }], - data_segments: vec![ - DataSegment { - offset: 0, - value: message, - }, - ], + }], + data_segments: vec![DataSegment { offset: 0, value: message }], call_body: Some(body::plain(vec![ - Instruction::I32Const(0), // value_ptr + Instruction::I32Const(0), // value_ptr Instruction::I32Const(i as i32), // value_len Instruction::Call(0), Instruction::Drop, @@ -1107,19 +1247,22 @@ benchmarks! { ..Default::default() }); let instance = Contract::::new(code, vec![])?; - }: { - >::bare_call( - instance.caller, - instance.account_id, - 0u32.into(), - Weight::MAX, - None, - vec![], - DebugInfo::UnsafeDebug, - CollectEvents::Skip, - Determinism::Enforced, - ) - .result?; + #[block] + { + >::bare_call( + instance.caller, + instance.account_id, + 0u32.into(), + Weight::MAX, + None, + vec![], + DebugInfo::UnsafeDebug, + CollectEvents::Skip, + Determinism::Enforced, + ) + .result?; + } + Ok(()) } // Only the overhead of calling the function itself with minimal arguments. @@ -1133,15 +1276,16 @@ benchmarks! { // // We need to use a smaller `r` because the keys are big and writing them all into the wasm // might exceed the code size. - #[skip_meta] - #[pov_mode = Measured] - seal_set_storage { - let r in 0 .. API_BENCHMARK_RUNS/2; + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_set_storage(r: Linear<0, { API_BENCHMARK_RUNS / 2 }>) -> Result<(), BenchmarkError> { let max_key_len = T::MaxStorageKeyLen::get(); - let keys = (0 .. r) - .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); - h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); h }) - .collect::>(); + let keys = (0..r) + .map(|n| { + let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); + h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); + h + }) + .collect::>(); let keys_bytes = keys.iter().flatten().cloned().collect::>(); let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), @@ -1151,27 +1295,25 @@ benchmarks! { params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32], return_type: Some(ValueType::I32), }], - data_segments: vec![ - DataSegment { - offset: 0, - value: keys_bytes, - }, - ], - call_body: Some(body::repeated_dyn(r, vec![ - Counter(0, max_key_len as u32), // key_ptr - Regular(Instruction::I32Const(max_key_len as i32)), // key_len - Regular(Instruction::I32Const(0)), // value_ptr - Regular(Instruction::I32Const(0)), // value_len - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), - ])), - .. Default::default() + data_segments: vec![DataSegment { offset: 0, value: keys_bytes }], + call_body: Some(body::repeated_dyn( + r, + vec![ + Counter(0, max_key_len as u32), // key_ptr + Regular(Instruction::I32Const(max_key_len as i32)), // key_len + Regular(Instruction::I32Const(0)), // value_ptr + Regular(Instruction::I32Const(0)), // value_len + Regular(Instruction::Call(0)), + Regular(Instruction::Drop), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; for key in keys { info.write( - &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|_| "Key has wrong length")?, Some(vec![]), None, false, @@ -1179,12 +1321,15 @@ benchmarks! { .map_err(|_| "Failed to write to storage during setup.")?; } let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - #[skip_meta] - #[pov_mode = Measured] - seal_set_storage_per_new_byte { - let n in 0 .. T::Schedule::get().limits.payload_len; + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_set_storage_per_new_byte( + n: Linear<0, { T::Schedule::get().limits.payload_len }>, + ) -> Result<(), BenchmarkError> { let max_key_len = T::MaxStorageKeyLen::get(); let key = vec![0u8; max_key_len as usize]; let code = WasmModule::::from(ModuleDefinition { @@ -1195,39 +1340,37 @@ benchmarks! { params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32], return_type: Some(ValueType::I32), }], - data_segments: vec![ - DataSegment { - offset: 0, - value: key.clone(), - }, - ], + data_segments: vec![DataSegment { offset: 0, value: key.clone() }], call_body: Some(body::plain(vec![ - Instruction::I32Const(0), // key_ptr + Instruction::I32Const(0), // key_ptr Instruction::I32Const(max_key_len as i32), // key_len - Instruction::I32Const(0), // value_ptr - Instruction::I32Const(n as i32), // value_len + Instruction::I32Const(0), // value_ptr + Instruction::I32Const(n as i32), // value_len Instruction::Call(0), Instruction::Drop, Instruction::End, ])), - .. Default::default() + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; info.write( - &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|_| "Key has wrong length")?, Some(vec![]), None, false, ) .map_err(|_| "Failed to write to storage during setup.")?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - #[skip_meta] - #[pov_mode = Measured] - seal_set_storage_per_old_byte { - let n in 0 .. T::Schedule::get().limits.payload_len; + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_set_storage_per_old_byte( + n: Linear<0, { T::Schedule::get().limits.payload_len }>, + ) -> Result<(), BenchmarkError> { let max_key_len = T::MaxStorageKeyLen::get(); let key = vec![0u8; max_key_len as usize]; let code = WasmModule::::from(ModuleDefinition { @@ -1238,48 +1381,48 @@ benchmarks! { params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32], return_type: Some(ValueType::I32), }], - data_segments: vec![ - DataSegment { - offset: 0, - value: key.clone(), - }, - ], + data_segments: vec![DataSegment { offset: 0, value: key.clone() }], call_body: Some(body::plain(vec![ - Instruction::I32Const(0), // key_ptr + Instruction::I32Const(0), // key_ptr Instruction::I32Const(max_key_len as i32), // key_len - Instruction::I32Const(0), // value_ptr - Instruction::I32Const(0), // value_len is 0 as testing vs pre-existing value len + Instruction::I32Const(0), // value_ptr + Instruction::I32Const(0), /* value_len is 0 as testing vs + * pre-existing value len */ Instruction::Call(0), Instruction::Drop, Instruction::End, ])), - .. Default::default() + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; info.write( - &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|_| "Key has wrong length")?, Some(vec![42u8; n as usize]), None, false, ) .map_err(|_| "Failed to write to storage during setup.")?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // Similar to seal_set_storage. We store all the keys that we are about to // delete beforehand in order to prevent any optimizations that could occur when // deleting a non existing key. We generate keys of a maximum length, and have to // the amount of runs in order to make resulting contract code size less than MaxCodeLen. - #[skip_meta] - #[pov_mode = Measured] - seal_clear_storage { - let r in 0 .. API_BENCHMARK_RUNS/2; + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_clear_storage(r: Linear<0, { API_BENCHMARK_RUNS / 2 }>) -> Result<(), BenchmarkError> { let max_key_len = T::MaxStorageKeyLen::get(); - let keys = (0 .. r) - .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); - h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); h }) - .collect::>(); + let keys = (0..r) + .map(|n| { + let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); + h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); + h + }) + .collect::>(); let key_bytes = keys.iter().flatten().cloned().collect::>(); let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), @@ -1289,25 +1432,23 @@ benchmarks! { params: vec![ValueType::I32, ValueType::I32], return_type: Some(ValueType::I32), }], - data_segments: vec![ - DataSegment { - offset: 0, - value: key_bytes, - }, - ], - call_body: Some(body::repeated_dyn(r, vec![ - Counter(0, max_key_len as u32), // key_ptr - Regular(Instruction::I32Const(max_key_len as i32)), // key_len - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), - ])), - .. Default::default() + data_segments: vec![DataSegment { offset: 0, value: key_bytes }], + call_body: Some(body::repeated_dyn( + r, + vec![ + Counter(0, max_key_len as u32), // key_ptr + Regular(Instruction::I32Const(max_key_len as i32)), // key_len + Regular(Instruction::Call(0)), + Regular(Instruction::Drop), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; for key in keys { info.write( - &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|_| "Key has wrong length")?, Some(vec![]), None, false, @@ -1316,12 +1457,15 @@ benchmarks! { } >::insert(&instance.account_id, info); let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - #[skip_meta] - #[pov_mode = Measured] - seal_clear_storage_per_byte { - let n in 0 .. T::Schedule::get().limits.payload_len; + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_clear_storage_per_byte( + n: Linear<0, { T::Schedule::get().limits.payload_len }>, + ) -> Result<(), BenchmarkError> { let max_key_len = T::MaxStorageKeyLen::get(); let key = vec![0u8; max_key_len as usize]; let code = WasmModule::::from(ModuleDefinition { @@ -1332,43 +1476,42 @@ benchmarks! { params: vec![ValueType::I32, ValueType::I32], return_type: Some(ValueType::I32), }], - data_segments: vec![ - DataSegment { - offset: 0, - value: key.clone(), - }, - ], + data_segments: vec![DataSegment { offset: 0, value: key.clone() }], call_body: Some(body::plain(vec![ - Instruction::I32Const(0), // key_ptr + Instruction::I32Const(0), // key_ptr Instruction::I32Const(max_key_len as i32), // key_len Instruction::Call(0), Instruction::Drop, Instruction::End, ])), - .. Default::default() + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; info.write( - &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|_| "Key has wrong length")?, Some(vec![42u8; n as usize]), None, false, ) .map_err(|_| "Failed to write to storage during setup.")?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // We make sure that all storage accesses are to unique keys. - #[skip_meta] - #[pov_mode = Measured] - seal_get_storage { - let r in 0 .. API_BENCHMARK_RUNS/2; + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_get_storage(r: Linear<0, { API_BENCHMARK_RUNS / 2 }>) -> Result<(), BenchmarkError> { let max_key_len = T::MaxStorageKeyLen::get(); - let keys = (0 .. r) - .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); - h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); h }) - .collect::>(); + let keys = (0..r) + .map(|n| { + let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); + h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); + h + }) + .collect::>(); let key_bytes = keys.iter().flatten().cloned().collect::>(); let key_bytes_len = key_bytes.len(); let code = WasmModule::::from(ModuleDefinition { @@ -1380,30 +1523,30 @@ benchmarks! { return_type: Some(ValueType::I32), }], data_segments: vec![ - DataSegment { - offset: 0, - value: key_bytes, - }, + DataSegment { offset: 0, value: key_bytes }, DataSegment { offset: key_bytes_len as u32, value: T::Schedule::get().limits.payload_len.to_le_bytes().into(), }, ], - call_body: Some(body::repeated_dyn(r, vec![ - Counter(0, max_key_len), // key_ptr - Regular(Instruction::I32Const(max_key_len as i32)), // key_len - Regular(Instruction::I32Const((key_bytes_len + 4) as i32)), // out_ptr - Regular(Instruction::I32Const(key_bytes_len as i32)), // out_len_ptr - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), - ])), - .. Default::default() + call_body: Some(body::repeated_dyn( + r, + vec![ + Counter(0, max_key_len), // key_ptr + Regular(Instruction::I32Const(max_key_len as i32)), // key_len + Regular(Instruction::I32Const((key_bytes_len + 4) as i32)), // out_ptr + Regular(Instruction::I32Const(key_bytes_len as i32)), // out_len_ptr + Regular(Instruction::Call(0)), + Regular(Instruction::Drop), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; for key in keys { info.write( - &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|_| "Key has wrong length")?, Some(vec![]), None, false, @@ -1412,12 +1555,15 @@ benchmarks! { } >::insert(&instance.account_id, info); let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - #[skip_meta] - #[pov_mode = Measured] - seal_get_storage_per_byte { - let n in 0 .. T::Schedule::get().limits.payload_len; + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_get_storage_per_byte( + n: Linear<0, { T::Schedule::get().limits.payload_len }>, + ) -> Result<(), BenchmarkError> { let max_key_len = T::MaxStorageKeyLen::get(); let key = vec![0u8; max_key_len as usize]; let code = WasmModule::::from(ModuleDefinition { @@ -1429,30 +1575,27 @@ benchmarks! { return_type: Some(ValueType::I32), }], data_segments: vec![ - DataSegment { - offset: 0, - value: key.clone(), - }, + DataSegment { offset: 0, value: key.clone() }, DataSegment { offset: max_key_len, value: T::Schedule::get().limits.payload_len.to_le_bytes().into(), }, ], call_body: Some(body::plain(vec![ - Instruction::I32Const(0), // key_ptr - Instruction::I32Const(max_key_len as i32), // key_len + Instruction::I32Const(0), // key_ptr + Instruction::I32Const(max_key_len as i32), // key_len Instruction::I32Const((max_key_len + 4) as i32), // out_ptr - Instruction::I32Const(max_key_len as i32), // out_len_ptr + Instruction::I32Const(max_key_len as i32), // out_len_ptr Instruction::Call(0), Instruction::Drop, Instruction::End, ])), - .. Default::default() + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; info.write( - &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|_| "Key has wrong length")?, Some(vec![42u8; n as usize]), None, false, @@ -1460,20 +1603,25 @@ benchmarks! { .map_err(|_| "Failed to write to storage during setup.")?; >::insert(&instance.account_id, info); let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // We make sure that all storage accesses are to unique keys. - #[skip_meta] - #[pov_mode = Measured] - seal_contains_storage { - let r in 0 .. API_BENCHMARK_RUNS/2; + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_contains_storage( + r: Linear<0, { API_BENCHMARK_RUNS / 2 }>, + ) -> Result<(), BenchmarkError> { let max_key_len = T::MaxStorageKeyLen::get(); - let keys = (0 .. r) - .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); - h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); h }) - .collect::>(); + let keys = (0..r) + .map(|n| { + let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); + h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); + h + }) + .collect::>(); let key_bytes = keys.iter().flatten().cloned().collect::>(); - let key_bytes_len = key_bytes.len(); let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -1482,25 +1630,23 @@ benchmarks! { params: vec![ValueType::I32, ValueType::I32], return_type: Some(ValueType::I32), }], - data_segments: vec![ - DataSegment { - offset: 0, - value: key_bytes, - }, - ], - call_body: Some(body::repeated_dyn(r, vec![ - Counter(0, max_key_len as u32), // key_ptr - Regular(Instruction::I32Const(max_key_len as i32)), // key_len - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), - ])), - .. Default::default() + data_segments: vec![DataSegment { offset: 0, value: key_bytes }], + call_body: Some(body::repeated_dyn( + r, + vec![ + Counter(0, max_key_len as u32), // key_ptr + Regular(Instruction::I32Const(max_key_len as i32)), // key_len + Regular(Instruction::Call(0)), + Regular(Instruction::Drop), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; for key in keys { info.write( - &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|_| "Key has wrong length")?, Some(vec![]), None, false, @@ -1509,12 +1655,15 @@ benchmarks! { } >::insert(&instance.account_id, info); let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - #[skip_meta] - #[pov_mode = Measured] - seal_contains_storage_per_byte { - let n in 0 .. T::Schedule::get().limits.payload_len; + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_contains_storage_per_byte( + n: Linear<0, { T::Schedule::get().limits.payload_len }>, + ) -> Result<(), BenchmarkError> { let max_key_len = T::MaxStorageKeyLen::get(); let key = vec![0u8; max_key_len as usize]; let code = WasmModule::::from(ModuleDefinition { @@ -1525,25 +1674,20 @@ benchmarks! { params: vec![ValueType::I32, ValueType::I32], return_type: Some(ValueType::I32), }], - data_segments: vec![ - DataSegment { - offset: 0, - value: key.clone(), - }, - ], + data_segments: vec![DataSegment { offset: 0, value: key.clone() }], call_body: Some(body::plain(vec![ - Instruction::I32Const(0), // key_ptr + Instruction::I32Const(0), // key_ptr Instruction::I32Const(max_key_len as i32), // key_len Instruction::Call(0), Instruction::Drop, Instruction::End, ])), - .. Default::default() + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; info.write( - &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|_| "Key has wrong length")?, Some(vec![42u8; n as usize]), None, false, @@ -1551,17 +1695,21 @@ benchmarks! { .map_err(|_| "Failed to write to storage during setup.")?; >::insert(&instance.account_id, info); let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - #[skip_meta] - #[pov_mode = Measured] - seal_take_storage { - let r in 0 .. API_BENCHMARK_RUNS/2; + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_take_storage(r: Linear<0, { API_BENCHMARK_RUNS / 2 }>) -> Result<(), BenchmarkError> { let max_key_len = T::MaxStorageKeyLen::get(); - let keys = (0 .. r) - .map(|n| { let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); - h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); h }) - .collect::>(); + let keys = (0..r) + .map(|n| { + let mut h = T::Hashing::hash_of(&n).as_ref().to_vec(); + h.resize(max_key_len.try_into().unwrap(), n.to_le_bytes()[0]); + h + }) + .collect::>(); let key_bytes = keys.iter().flatten().cloned().collect::>(); let key_bytes_len = key_bytes.len(); let code = WasmModule::::from(ModuleDefinition { @@ -1573,30 +1721,30 @@ benchmarks! { return_type: Some(ValueType::I32), }], data_segments: vec![ - DataSegment { - offset: 0, - value: key_bytes, - }, + DataSegment { offset: 0, value: key_bytes }, DataSegment { offset: key_bytes_len as u32, value: T::Schedule::get().limits.payload_len.to_le_bytes().into(), }, ], - call_body: Some(body::repeated_dyn(r, vec![ - Counter(0, max_key_len as u32), // key_ptr - Regular(Instruction::I32Const(max_key_len as i32)), // key_len - Regular(Instruction::I32Const((key_bytes_len + 4) as i32)), // out_ptr - Regular(Instruction::I32Const(key_bytes_len as i32)), // out_len_ptr - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), - ])), - .. Default::default() + call_body: Some(body::repeated_dyn( + r, + vec![ + Counter(0, max_key_len as u32), // key_ptr + Regular(Instruction::I32Const(max_key_len as i32)), // key_len + Regular(Instruction::I32Const((key_bytes_len + 4) as i32)), // out_ptr + Regular(Instruction::I32Const(key_bytes_len as i32)), // out_len_ptr + Regular(Instruction::Call(0)), + Regular(Instruction::Drop), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; for key in keys { info.write( - &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|_| "Key has wrong length")?, Some(vec![]), None, false, @@ -1605,12 +1753,15 @@ benchmarks! { } >::insert(&instance.account_id, info); let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - #[skip_meta] - #[pov_mode = Measured] - seal_take_storage_per_byte { - let n in 0 .. T::Schedule::get().limits.payload_len; + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_take_storage_per_byte( + n: Linear<0, { T::Schedule::get().limits.payload_len }>, + ) -> Result<(), BenchmarkError> { let max_key_len = T::MaxStorageKeyLen::get(); let key = vec![0u8; max_key_len as usize]; let code = WasmModule::::from(ModuleDefinition { @@ -1622,30 +1773,27 @@ benchmarks! { return_type: Some(ValueType::I32), }], data_segments: vec![ - DataSegment { - offset: 0, - value: key.clone(), - }, + DataSegment { offset: 0, value: key.clone() }, DataSegment { offset: max_key_len, value: T::Schedule::get().limits.payload_len.to_le_bytes().into(), }, ], call_body: Some(body::plain(vec![ - Instruction::I32Const(0), // key_ptr - Instruction::I32Const(max_key_len as i32), // key_len + Instruction::I32Const(0), // key_ptr + Instruction::I32Const(max_key_len as i32), // key_len Instruction::I32Const((max_key_len + 4) as i32), // out_ptr - Instruction::I32Const(max_key_len as i32), // out_len_ptr + Instruction::I32Const(max_key_len as i32), // out_len_ptr Instruction::Call(0), Instruction::Drop, Instruction::End, ])), - .. Default::default() + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let info = instance.info()?; info.write( - &Key::::try_from_var(key).map_err(|e| "Key has wrong length")?, + &Key::::try_from_var(key).map_err(|_| "Key has wrong length")?, Some(vec![42u8; n as usize]), None, false, @@ -1653,15 +1801,16 @@ benchmarks! { .map_err(|_| "Failed to write to storage during setup.")?; >::insert(&instance.account_id, info); let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // We transfer to unique accounts. - #[pov_mode = Measured] - seal_transfer { - let r in 0 .. API_BENCHMARK_RUNS; - let accounts = (0..r) - .map(|i| account::("receiver", i, 0)) - .collect::>(); + #[benchmark(pov_mode = Measured)] + fn seal_transfer(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { + let accounts = + (0..r).map(|i| account::("receiver", i, 0)).collect::>(); let account_len = accounts.get(0).map(|i| i.encode().len()).unwrap_or(0); let account_bytes = accounts.iter().flat_map(|x| x.encode()).collect(); let value = Pallet::::min_balance(); @@ -1677,24 +1826,21 @@ benchmarks! { return_type: Some(ValueType::I32), }], data_segments: vec![ - DataSegment { - offset: 0, - value: value_bytes, - }, - DataSegment { - offset: value_len as u32, - value: account_bytes, - }, + DataSegment { offset: 0, value: value_bytes }, + DataSegment { offset: value_len as u32, value: account_bytes }, ], - call_body: Some(body::repeated_dyn(r, vec![ - Counter(value_len as u32, account_len as u32), // account_ptr - Regular(Instruction::I32Const(account_len as i32)), // account_len - Regular(Instruction::I32Const(0)), // value_ptr - Regular(Instruction::I32Const(value_len as i32)), // value_len - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), - ])), - .. Default::default() + call_body: Some(body::repeated_dyn( + r, + vec![ + Counter(value_len as u32, account_len as u32), // account_ptr + Regular(Instruction::I32Const(account_len as i32)), // account_len + Regular(Instruction::I32Const(0)), // value_ptr + Regular(Instruction::I32Const(value_len as i32)), // value_len + Regular(Instruction::Call(0)), + Regular(Instruction::Drop), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; instance.set_balance(value * (r + 1).into()); @@ -1702,19 +1848,20 @@ benchmarks! { for account in &accounts { assert_eq!(T::Currency::total_balance(account), 0u32.into()); } - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - verify { + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + for account in &accounts { assert_eq!(T::Currency::total_balance(account), value); } + Ok(()) } // We call unique accounts. // - // This is a slow call: We redeuce the number of runs. - #[pov_mode = Measured] - seal_call { - let r in 0 .. API_BENCHMARK_RUNS / 2; + // This is a slow call: We reduce the number of runs. + #[benchmark(pov_mode = Measured)] + fn seal_call(r: Linear<0, { API_BENCHMARK_RUNS / 2 }>) -> Result<(), BenchmarkError> { let dummy_code = WasmModule::::dummy_with_bytes(0); let callees = (0..r) .map(|i| Contract::with_index(i + 1, dummy_code.clone(), vec![])) @@ -1727,7 +1874,7 @@ benchmarks! { // Set an own limit every 2nd call let own_limit = (u32::MAX - 100).into(); let deposits = (0..r) - .map(|i| if i % 2 == 0 { 0u32.into() } else { own_limit } ) + .map(|i| if i % 2 == 0 { 0u32.into() } else { own_limit }) .collect::>>(); let deposits_bytes: Vec = deposits.iter().flat_map(|i| i.encode()).collect(); let deposits_len = deposits_bytes.len() as u32; @@ -1753,43 +1900,46 @@ benchmarks! { return_type: Some(ValueType::I32), }], data_segments: vec![ - DataSegment { - offset: 0, - value: value_bytes, - }, - DataSegment { - offset: value_len, - value: deposits_bytes, - }, - DataSegment { - offset: callee_offset, - value: callee_bytes, - }, + DataSegment { offset: 0, value: value_bytes }, + DataSegment { offset: value_len, value: deposits_bytes }, + DataSegment { offset: callee_offset, value: callee_bytes }, ], - call_body: Some(body::repeated_dyn(r, vec![ - Regular(Instruction::I32Const(0)), // flags - Counter(callee_offset, callee_len as u32), // callee_ptr - Regular(Instruction::I64Const(0)), // ref_time weight - Regular(Instruction::I64Const(0)), // proof_size weight - Counter(value_len, deposit_len as u32), // deposit_limit_ptr - Regular(Instruction::I32Const(0)), // value_ptr - Regular(Instruction::I32Const(0)), // input_data_ptr - Regular(Instruction::I32Const(0)), // input_data_len - Regular(Instruction::I32Const(SENTINEL as i32)), // output_ptr - Regular(Instruction::I32Const(0)), // output_len_ptr - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), - ])), - .. Default::default() + call_body: Some(body::repeated_dyn( + r, + vec![ + Regular(Instruction::I32Const(0)), // flags + Counter(callee_offset, callee_len as u32), // callee_ptr + Regular(Instruction::I64Const(0)), // ref_time weight + Regular(Instruction::I64Const(0)), // proof_size weight + Counter(value_len, deposit_len as u32), // deposit_limit_ptr + Regular(Instruction::I32Const(0)), // value_ptr + Regular(Instruction::I32Const(0)), // input_data_ptr + Regular(Instruction::I32Const(0)), // input_data_len + Regular(Instruction::I32Const(SENTINEL as i32)), // output_ptr + Regular(Instruction::I32Const(0)), // output_len_ptr + Regular(Instruction::Call(0)), + Regular(Instruction::Drop), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, Some(BalanceOf::::from(u32::MAX.into()).into()), vec![]) + #[extrinsic_call] + call( + origin, + instance.addr, + 0u32.into(), + Weight::MAX, + Some(BalanceOf::::from(u32::MAX.into()).into()), + vec![], + ); + Ok(()) + } - // This is a slow call: We redeuce the number of runs. - #[pov_mode = Measured] - seal_delegate_call { - let r in 0 .. API_BENCHMARK_RUNS / 2; + // This is a slow call: We reduce the number of runs. + #[benchmark(pov_mode = Measured)] + fn seal_delegate_call(r: Linear<0, { API_BENCHMARK_RUNS / 2 }>) -> Result<(), BenchmarkError> { let hashes = (0..r) .map(|i| { let code = WasmModule::::dummy_with_bytes(i); @@ -1801,7 +1951,6 @@ benchmarks! { .collect::, &'static str>>()?; let hash_len = hashes.get(0).map(|x| x.encode().len()).unwrap_or(0); let hashes_bytes = hashes.iter().flat_map(|x| x.encode()).collect::>(); - let hashes_len = hashes_bytes.len(); let hashes_offset = 0; let code = WasmModule::::from(ModuleDefinition { @@ -1819,33 +1968,35 @@ benchmarks! { ], return_type: Some(ValueType::I32), }], - data_segments: vec![ - DataSegment { - offset: hashes_offset as u32, - value: hashes_bytes, - }, - ], - call_body: Some(body::repeated_dyn(r, vec![ - Regular(Instruction::I32Const(0)), // flags - Counter(hashes_offset as u32, hash_len as u32), // code_hash_ptr - Regular(Instruction::I32Const(0)), // input_data_ptr - Regular(Instruction::I32Const(0)), // input_data_len - Regular(Instruction::I32Const(u32::max_value() as i32)), // output_ptr - Regular(Instruction::I32Const(0)), // output_len_ptr - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), - ])), - .. Default::default() + data_segments: vec![DataSegment { offset: hashes_offset as u32, value: hashes_bytes }], + call_body: Some(body::repeated_dyn( + r, + vec![ + Regular(Instruction::I32Const(0)), // flags + Counter(hashes_offset as u32, hash_len as u32), // code_hash_ptr + Regular(Instruction::I32Const(0)), // input_data_ptr + Regular(Instruction::I32Const(0)), // input_data_len + Regular(Instruction::I32Const(u32::max_value() as i32)), // output_ptr + Regular(Instruction::I32Const(0)), // output_len_ptr + Regular(Instruction::Call(0)), + Regular(Instruction::Drop), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let callee = instance.addr.clone(); let origin = RawOrigin::Signed(instance.caller); - }: call(origin, callee, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, callee, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - #[pov_mode = Measured] - seal_call_per_transfer_clone_byte { - let t in 0 .. 1; - let c in 0 .. code::max_pages::() * 64 * 1024; + #[benchmark(pov_mode = Measured)] + fn seal_call_per_transfer_clone_byte( + t: Linear<0, { 1 }>, + c: Linear<0, { code::max_pages::() * 64 * 1024 }>, + ) -> Result<(), BenchmarkError> { let callee = Contract::with_index(5, >::dummy(), vec![])?; let value: BalanceOf = t.into(); let value_bytes = value.encode(); @@ -1868,40 +2019,36 @@ benchmarks! { return_type: Some(ValueType::I32), }], data_segments: vec![ - DataSegment { - offset: 0, - value: value_bytes, - }, - DataSegment { - offset: value_len as u32, - value: callee.account_id.encode(), - }, + DataSegment { offset: 0, value: value_bytes }, + DataSegment { offset: value_len as u32, value: callee.account_id.encode() }, ], call_body: Some(body::plain(vec![ Instruction::I32Const(CallFlags::CLONE_INPUT.bits() as i32), // flags - Instruction::I32Const(value_len as i32), // callee_ptr - Instruction::I64Const(0), // gas - Instruction::I32Const(0), // value_ptr - Instruction::I32Const(0), // input_data_ptr - Instruction::I32Const(0), // input_data_len - Instruction::I32Const(SENTINEL as i32), // output_ptr - Instruction::I32Const(0), // output_len_ptr + Instruction::I32Const(value_len as i32), // callee_ptr + Instruction::I64Const(0), // gas + Instruction::I32Const(0), // value_ptr + Instruction::I32Const(0), // input_data_ptr + Instruction::I32Const(0), // input_data_len + Instruction::I32Const(SENTINEL as i32), // output_ptr + Instruction::I32Const(0), // output_len_ptr Instruction::Call(0), Instruction::Drop, Instruction::End, ])), - .. Default::default() + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); let bytes = vec![42; c as usize]; - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, bytes) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, bytes); + Ok(()) + } // We assume that every instantiate sends at least the minimum balance. // This is a slow call: we reduce the number of runs. - #[pov_mode = Measured] - seal_instantiate { - let r in 1 .. API_BENCHMARK_RUNS / 2; + #[benchmark(pov_mode = Measured)] + fn seal_instantiate(r: Linear<1, { API_BENCHMARK_RUNS / 2 }>) -> Result<(), BenchmarkError> { let hashes = (0..r) .map(|i| { let code = WasmModule::::from(ModuleDefinition { @@ -1913,7 +2060,7 @@ benchmarks! { Instruction::Drop, Instruction::End, ])), - .. Default::default() + ..Default::default() }); let caller = whitelisted_caller(); T::Currency::set_balance(&caller, caller_funding::()); @@ -1956,37 +2103,35 @@ benchmarks! { return_type: Some(ValueType::I32), }], data_segments: vec![ - DataSegment { - offset: 0, - value: value_bytes, - }, - DataSegment { - offset: hashes_offset as u32, - value: hashes_bytes, - }, + DataSegment { offset: 0, value: value_bytes }, + DataSegment { offset: hashes_offset as u32, value: hashes_bytes }, DataSegment { offset: addr_len_offset as u32, value: addr_len.to_le_bytes().into(), }, ], - call_body: Some(body::repeated_dyn(r, vec![ - Counter(hashes_offset as u32, hash_len as u32), // code_hash_ptr - Regular(Instruction::I64Const(0)), // ref_time weight - Regular(Instruction::I64Const(0)), // proof_size weight - Regular(Instruction::I32Const(SENTINEL as i32)), // deposit limit ptr: use parent's limit - Regular(Instruction::I32Const(0)), // value_ptr - Regular(Instruction::I32Const(0)), // input_data_ptr - Regular(Instruction::I32Const(0)), // input_data_len - Regular(Instruction::I32Const(addr_offset as i32)), // address_ptr - Regular(Instruction::I32Const(addr_len_offset as i32)), // address_len_ptr - Regular(Instruction::I32Const(SENTINEL as i32)), // output_ptr - Regular(Instruction::I32Const(0)), // output_len_ptr - Regular(Instruction::I32Const(0)), // salt_ptr - Regular(Instruction::I32Const(0)), // salt_len_ptr - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), - ])), - .. Default::default() + call_body: Some(body::repeated_dyn( + r, + vec![ + Counter(hashes_offset as u32, hash_len as u32), // code_hash_ptr + Regular(Instruction::I64Const(0)), // ref_time weight + Regular(Instruction::I64Const(0)), // proof_size weight + Regular(Instruction::I32Const(SENTINEL as i32)), /* deposit limit ptr: use + * parent's limit */ + Regular(Instruction::I32Const(0)), // value_ptr + Regular(Instruction::I32Const(0)), // input_data_ptr + Regular(Instruction::I32Const(0)), // input_data_len + Regular(Instruction::I32Const(addr_offset as i32)), // address_ptr + Regular(Instruction::I32Const(addr_len_offset as i32)), // address_len_ptr + Regular(Instruction::I32Const(SENTINEL as i32)), // output_ptr + Regular(Instruction::I32Const(0)), // output_len_ptr + Regular(Instruction::I32Const(0)), // salt_ptr + Regular(Instruction::I32Const(0)), // salt_len_ptr + Regular(Instruction::Call(0)), + Regular(Instruction::Drop), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; instance.set_balance((value + Pallet::::min_balance()) * (r + 1).into()); @@ -1994,9 +2139,7 @@ benchmarks! { let callee = instance.addr.clone(); let addresses = hashes .iter() - .map(|hash| Contracts::::contract_address( - &instance.account_id, hash, &[], &[], - )) + .map(|hash| Contracts::::contract_address(&instance.account_id, hash, &[], &[])) .collect::>(); for addr in &addresses { @@ -2004,27 +2147,27 @@ benchmarks! { return Err("Expected that contract does not exist at this point.".into()); } } - }: call(origin, callee, 0u32.into(), Weight::MAX, None, vec![]) - verify { + #[extrinsic_call] + call(origin, callee, 0u32.into(), Weight::MAX, None, vec![]); for addr in &addresses { - ContractInfoOf::::get(&addr) - .ok_or("Contract should have been instantiated")?; + ContractInfoOf::::get(&addr).ok_or("Contract should have been instantiated")?; } + Ok(()) } - #[pov_mode = Measured] - seal_instantiate_per_transfer_input_salt_byte { - let t in 0 .. 1; - let i in 0 .. (code::max_pages::() - 1) * 64 * 1024; - let s in 0 .. (code::max_pages::() - 1) * 64 * 1024; + #[benchmark(pov_mode = Measured)] + fn seal_instantiate_per_transfer_input_salt_byte( + t: Linear<0, 1>, + i: Linear<0, { (code::max_pages::() - 1) * 64 * 1024 }>, + s: Linear<0, { (code::max_pages::() - 1) * 64 * 1024 }>, + ) -> Result<(), BenchmarkError> { let callee_code = WasmModule::::dummy(); - let hash = callee_code.hash; let hash_bytes = callee_code.hash.encode(); let hash_len = hash_bytes.len(); let caller = whitelisted_caller(); T::Currency::set_balance(&caller, caller_funding::()); Contracts::::store_code_raw(callee_code.code, caller)?; - let value: BalanceOf = t.into(); + let value: BalanceOf = t.into(); let value_bytes = value.encode(); let code = WasmModule::::from(ModuleDefinition { @@ -2048,27 +2191,21 @@ benchmarks! { return_type: Some(ValueType::I32), }], data_segments: vec![ - DataSegment { - offset: 0, - value: hash_bytes, - }, - DataSegment { - offset: hash_len as u32, - value: value_bytes, - }, + DataSegment { offset: 0, value: hash_bytes }, + DataSegment { offset: hash_len as u32, value: value_bytes }, ], call_body: Some(body::plain(vec![ - Instruction::I32Const(0 as i32), // code_hash_ptr - Instruction::I64Const(0), // gas + Instruction::I32Const(0 as i32), // code_hash_ptr + Instruction::I64Const(0), // gas Instruction::I32Const(hash_len as i32), // value_ptr - Instruction::I32Const(0 as i32), // input_data_ptr - Instruction::I32Const(i as i32), // input_data_len + Instruction::I32Const(0 as i32), // input_data_ptr + Instruction::I32Const(i as i32), // input_data_len Instruction::I32Const(SENTINEL as i32), // address_ptr - Instruction::I32Const(0), // address_len_ptr + Instruction::I32Const(0), // address_len_ptr Instruction::I32Const(SENTINEL as i32), // output_ptr - Instruction::I32Const(0), // output_len_ptr - Instruction::I32Const(0 as i32), // salt_ptr - Instruction::I32Const(s as i32), // salt_len + Instruction::I32Const(0), // output_len_ptr + Instruction::I32Const(0 as i32), // salt_ptr + Instruction::I32Const(s as i32), // salt_len Instruction::Call(0), Instruction::I32Eqz, Instruction::If(BlockType::NoResult), @@ -2078,105 +2215,123 @@ benchmarks! { Instruction::End, Instruction::End, ])), - .. Default::default() + ..Default::default() }); let instance = Contract::::new(code, vec![])?; instance.set_balance(value + (Pallet::::min_balance() * 2u32.into())); let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // Only the overhead of calling the function itself with minimal arguments. - #[pov_mode = Measured] - seal_hash_sha2_256 { - let r in 0 .. API_BENCHMARK_RUNS; - let instance = Contract::::new(WasmModule::hasher( - "seal_hash_sha2_256", r, 0, - ), vec![])?; + #[benchmark(pov_mode = Measured)] + fn seal_hash_sha2_256(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { + let instance = Contract::::new(WasmModule::hasher("seal_hash_sha2_256", r, 0), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // `n`: Input to hash in bytes - #[pov_mode = Measured] - seal_hash_sha2_256_per_byte { - let n in 0 .. code::max_pages::() * 64 * 1024; - let instance = Contract::::new(WasmModule::hasher( - "seal_hash_sha2_256", 1, n, - ), vec![])?; + #[benchmark(pov_mode = Measured)] + fn seal_hash_sha2_256_per_byte( + n: Linear<0, { code::max_pages::() * 64 * 1024 }>, + ) -> Result<(), BenchmarkError> { + let instance = Contract::::new(WasmModule::hasher("seal_hash_sha2_256", 1, n), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // Only the overhead of calling the function itself with minimal arguments. - #[pov_mode = Measured] - seal_hash_keccak_256 { - let r in 0 .. API_BENCHMARK_RUNS; - let instance = Contract::::new(WasmModule::hasher( - "seal_hash_keccak_256", r, 0, - ), vec![])?; + #[benchmark(pov_mode = Measured)] + fn seal_hash_keccak_256(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { + let instance = + Contract::::new(WasmModule::hasher("seal_hash_keccak_256", r, 0), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // `n`: Input to hash in bytes - #[pov_mode = Measured] - seal_hash_keccak_256_per_byte { - let n in 0 .. code::max_pages::() * 64 * 1024; - let instance = Contract::::new(WasmModule::hasher( - "seal_hash_keccak_256", 1, n, - ), vec![])?; + #[benchmark(pov_mode = Measured)] + fn seal_hash_keccak_256_per_byte( + n: Linear<0, { code::max_pages::() * 64 * 1024 }>, + ) -> Result<(), BenchmarkError> { + let instance = + Contract::::new(WasmModule::hasher("seal_hash_keccak_256", 1, n), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // Only the overhead of calling the function itself with minimal arguments. - #[pov_mode = Measured] - seal_hash_blake2_256 { - let r in 0 .. API_BENCHMARK_RUNS; - let instance = Contract::::new(WasmModule::hasher( - "seal_hash_blake2_256", r, 0, - ), vec![])?; + #[benchmark(pov_mode = Measured)] + fn seal_hash_blake2_256(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { + let instance = + Contract::::new(WasmModule::hasher("seal_hash_blake2_256", r, 0), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // `n`: Input to hash in bytes - #[pov_mode = Measured] - seal_hash_blake2_256_per_byte { - let n in 0 .. code::max_pages::() * 64 * 1024; - let instance = Contract::::new(WasmModule::hasher( - "seal_hash_blake2_256", 1, n, - ), vec![])?; + #[benchmark(pov_mode = Measured)] + fn seal_hash_blake2_256_per_byte( + n: Linear<0, { code::max_pages::() * 64 * 1024 }>, + ) -> Result<(), BenchmarkError> { + let instance = + Contract::::new(WasmModule::hasher("seal_hash_blake2_256", 1, n), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // Only the overhead of calling the function itself with minimal arguments. - #[pov_mode = Measured] - seal_hash_blake2_128 { - let r in 0 .. API_BENCHMARK_RUNS; - let instance = Contract::::new(WasmModule::hasher( - "seal_hash_blake2_128", r, 0, - ), vec![])?; + #[benchmark(pov_mode = Measured)] + fn seal_hash_blake2_128(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { + let instance = + Contract::::new(WasmModule::hasher("seal_hash_blake2_128", r, 0), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // `n`: Input to hash in bytes - #[pov_mode = Measured] - seal_hash_blake2_128_per_byte { - let n in 0 .. code::max_pages::() * 64 * 1024; - let instance = Contract::::new(WasmModule::hasher( - "seal_hash_blake2_128", 1, n, - ), vec![])?; + #[benchmark(pov_mode = Measured)] + fn seal_hash_blake2_128_per_byte( + n: Linear<0, { code::max_pages::() * 64 * 1024 }>, + ) -> Result<(), BenchmarkError> { + let instance = + Contract::::new(WasmModule::hasher("seal_hash_blake2_128", 1, n), vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // `n`: Message input length to verify in bytes. - #[pov_mode = Measured] - seal_sr25519_verify_per_byte { - let n in 0 .. T::MaxCodeLen::get() - 255; // need some buffer so the code size does not - // exceed the max code size. - + // need some buffer so the code size does not exceed the max code size. + #[benchmark(pov_mode = Measured)] + fn seal_sr25519_verify_per_byte( + n: Linear<0, { T::MaxCodeLen::get() - 255 }>, + ) -> Result<(), BenchmarkError> { let message = (0..n).zip((32u8..127u8).cycle()).map(|(_, c)| c).collect::>(); let message_len = message.len() as i32; let key_type = sp_core::crypto::KeyTypeId(*b"code"); let pub_key = sp_io::crypto::sr25519_generate(key_type, None); - let sig = sp_io::crypto::sr25519_sign(key_type, &pub_key, &message).expect("Generates signature"); + let sig = + sp_io::crypto::sr25519_sign(key_type, &pub_key, &message).expect("Generates signature"); let sig = AsRef::<[u8; 64]>::as_ref(&sig).to_vec(); let code = WasmModule::::from(ModuleDefinition { @@ -2188,50 +2343,48 @@ benchmarks! { return_type: Some(ValueType::I32), }], data_segments: vec![ - DataSegment { - offset: 0, - value: sig, - }, - DataSegment { - offset: 64, - value: pub_key.to_vec(), - }, - DataSegment { - offset: 96, - value: message, - }, + DataSegment { offset: 0, value: sig }, + DataSegment { offset: 64, value: pub_key.to_vec() }, + DataSegment { offset: 96, value: message }, ], call_body: Some(body::plain(vec![ - Instruction::I32Const(0), // signature_ptr - Instruction::I32Const(64), // pub_key_ptr + Instruction::I32Const(0), // signature_ptr + Instruction::I32Const(64), // pub_key_ptr Instruction::I32Const(message_len), // message_len - Instruction::I32Const(96), // message_ptr + Instruction::I32Const(96), // message_ptr Instruction::Call(0), Instruction::Drop, Instruction::End, ])), - .. Default::default() + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // Only calling the function itself with valid arguments. // It generates different private keys and signatures for the message "Hello world". // This is a slow call: We reduce the number of runs. - #[pov_mode = Measured] - seal_sr25519_verify { - let r in 0 .. API_BENCHMARK_RUNS / 10; - + #[benchmark(pov_mode = Measured)] + fn seal_sr25519_verify( + r: Linear<0, { API_BENCHMARK_RUNS / 10 }>, + ) -> Result<(), BenchmarkError> { let message = b"Hello world".to_vec(); let message_len = message.len() as i32; let key_type = sp_core::crypto::KeyTypeId(*b"code"); let sig_params = (0..r) - .flat_map(|i| { + .flat_map(|_| { let pub_key = sp_io::crypto::sr25519_generate(key_type, None); - let sig = sp_io::crypto::sr25519_sign(key_type, &pub_key, &message).expect("Generates signature"); - let data: [u8; 96] = [AsRef::<[u8]>::as_ref(&sig), AsRef::<[u8]>::as_ref(&pub_key)].concat().try_into().unwrap(); + let sig = sp_io::crypto::sr25519_sign(key_type, &pub_key, &message) + .expect("Generates signature"); + let data: [u8; 96] = [AsRef::<[u8]>::as_ref(&sig), AsRef::<[u8]>::as_ref(&pub_key)] + .concat() + .try_into() + .unwrap(); data }) .collect::>(); @@ -2246,42 +2399,41 @@ benchmarks! { return_type: Some(ValueType::I32), }], data_segments: vec![ - DataSegment { - offset: 0, - value: sig_params - }, - DataSegment { - offset: sig_params_len as u32, - value: message, - }, + DataSegment { offset: 0, value: sig_params }, + DataSegment { offset: sig_params_len as u32, value: message }, ], - call_body: Some(body::repeated_dyn(r, vec![ - Counter(0, 96), // signature_ptr - Counter(64, 96), // pub_key_ptr - Regular(Instruction::I32Const(message_len)), // message_len - Regular(Instruction::I32Const(sig_params_len)), // message_ptr - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), - ])), - .. Default::default() + call_body: Some(body::repeated_dyn( + r, + vec![ + Counter(0, 96), // signature_ptr + Counter(64, 96), // pub_key_ptr + Regular(Instruction::I32Const(message_len)), // message_len + Regular(Instruction::I32Const(sig_params_len)), // message_ptr + Regular(Instruction::Call(0)), + Regular(Instruction::Drop), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // Only calling the function itself with valid arguments. // It generates different private keys and signatures for the message "Hello world". // This is a slow call: We reduce the number of runs. - #[pov_mode = Measured] - seal_ecdsa_recover { - let r in 0 .. API_BENCHMARK_RUNS / 10; - + #[benchmark(pov_mode = Measured)] + fn seal_ecdsa_recover(r: Linear<0, { API_BENCHMARK_RUNS / 10 }>) -> Result<(), BenchmarkError> { let message_hash = sp_io::hashing::blake2_256("Hello world".as_bytes()); let key_type = sp_core::crypto::KeyTypeId(*b"code"); let signatures = (0..r) - .map(|i| { + .map(|_| { let pub_key = sp_io::crypto::ecdsa_generate(key_type, None); - let sig = sp_io::crypto::ecdsa_sign_prehashed(key_type, &pub_key, &message_hash).expect("Generates signature"); + let sig = sp_io::crypto::ecdsa_sign_prehashed(key_type, &pub_key, &message_hash) + .expect("Generates signature"); AsRef::<[u8; 65]>::as_ref(&sig).to_vec() }) .collect::>(); @@ -2297,40 +2449,39 @@ benchmarks! { return_type: Some(ValueType::I32), }], data_segments: vec![ - DataSegment { - offset: 0, - value: message_hash[..].to_vec(), - }, - DataSegment { - offset: 32, - value: signatures, - }, + DataSegment { offset: 0, value: message_hash[..].to_vec() }, + DataSegment { offset: 32, value: signatures }, ], - call_body: Some(body::repeated_dyn(r, vec![ - Counter(32, 65), // signature_ptr - Regular(Instruction::I32Const(0)), // message_hash_ptr - Regular(Instruction::I32Const(signatures_bytes_len + 32)), // output_len_ptr - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), - ])), - .. Default::default() + call_body: Some(body::repeated_dyn( + r, + vec![ + Counter(32, 65), // signature_ptr + Regular(Instruction::I32Const(0)), // message_hash_ptr + Regular(Instruction::I32Const(signatures_bytes_len + 32)), // output_len_ptr + Regular(Instruction::Call(0)), + Regular(Instruction::Drop), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // Only calling the function itself for the list of // generated different ECDSA keys. - // This is a slow call: We redeuce the number of runs. - #[pov_mode = Measured] - seal_ecdsa_to_eth_address { - let r in 0 .. API_BENCHMARK_RUNS / 10; + // This is a slow call: We reduce the number of runs. + #[benchmark(pov_mode = Measured)] + fn seal_ecdsa_to_eth_address( + r: Linear<0, { API_BENCHMARK_RUNS / 10 }>, + ) -> Result<(), BenchmarkError> { let key_type = sp_core::crypto::KeyTypeId(*b"code"); let pub_keys_bytes = (0..r) - .flat_map(|_| { - sp_io::crypto::ecdsa_generate(key_type, None).0 - }) - .collect::>(); + .flat_map(|_| sp_io::crypto::ecdsa_generate(key_type, None).0) + .collect::>(); let pub_keys_bytes_len = pub_keys_bytes.len() as i32; let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), @@ -2340,27 +2491,27 @@ benchmarks! { params: vec![ValueType::I32, ValueType::I32], return_type: Some(ValueType::I32), }], - data_segments: vec![ - DataSegment { - offset: 0, - value: pub_keys_bytes, - }, - ], - call_body: Some(body::repeated_dyn(r, vec![ - Counter(0, 33), // pub_key_ptr - Regular(Instruction::I32Const(pub_keys_bytes_len)), // out_ptr - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), - ])), - .. Default::default() + data_segments: vec![DataSegment { offset: 0, value: pub_keys_bytes }], + call_body: Some(body::repeated_dyn( + r, + vec![ + Counter(0, 33), // pub_key_ptr + Regular(Instruction::I32Const(pub_keys_bytes_len)), // out_ptr + Regular(Instruction::Call(0)), + Regular(Instruction::Drop), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - #[pov_mode = Measured] - seal_set_code_hash { - let r in 0 .. API_BENCHMARK_RUNS; + #[benchmark(pov_mode = Measured)] + fn seal_set_code_hash(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { let code_hashes = (0..r) .map(|i| { let new_code = WasmModule::::dummy_with_bytes(i); @@ -2372,38 +2523,37 @@ benchmarks! { .collect::, &'static str>>()?; let code_hash_len = code_hashes.get(0).map(|x| x.encode().len()).unwrap_or(0); let code_hashes_bytes = code_hashes.iter().flat_map(|x| x.encode()).collect::>(); - let code_hashes_len = code_hashes_bytes.len(); let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { module: "seal0", name: "seal_set_code_hash", - params: vec![ - ValueType::I32, - ], + params: vec![ValueType::I32], return_type: Some(ValueType::I32), }], - data_segments: vec![ - DataSegment { - offset: 0, - value: code_hashes_bytes, - }, - ], - call_body: Some(body::repeated_dyn(r, vec![ - Counter(0, code_hash_len as u32), // code_hash_ptr - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), - ])), - .. Default::default() + data_segments: vec![DataSegment { offset: 0, value: code_hashes_bytes }], + call_body: Some(body::repeated_dyn( + r, + vec![ + Counter(0, code_hash_len as u32), // code_hash_ptr + Regular(Instruction::Call(0)), + Regular(Instruction::Drop), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - #[pov_mode = Measured] - lock_delegate_dependency { - let r in 0 .. T::MaxDelegateDependencies::get(); + #[benchmark(pov_mode = Measured)] + fn lock_delegate_dependency( + r: Linear<0, { T::MaxDelegateDependencies::get() }>, + ) -> Result<(), BenchmarkError> { let code_hashes = (0..r) .map(|i| { let new_code = WasmModule::::dummy_with_bytes(65 + i); @@ -2424,24 +2574,27 @@ benchmarks! { params: vec![ValueType::I32], return_type: None, }], - data_segments: vec![ - DataSegment { - offset: 0, - value: code_hashes_bytes, - }, - ], - call_body: Some(body::repeated_dyn(r, vec![ - Counter(0, code_hash_len as u32), // code_hash_ptr - Regular(Instruction::Call(0)), - ])), - .. Default::default() + data_segments: vec![DataSegment { offset: 0, value: code_hashes_bytes }], + call_body: Some(body::repeated_dyn( + r, + vec![ + Counter(0, code_hash_len as u32), // code_hash_ptr + Regular(Instruction::Call(0)), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - unlock_delegate_dependency { - let r in 0 .. T::MaxDelegateDependencies::get(); + #[benchmark] + fn unlock_delegate_dependency( + r: Linear<0, { T::MaxDelegateDependencies::get() }>, + ) -> Result<(), BenchmarkError> { let code_hashes = (0..r) .map(|i| { let new_code = WasmModule::::dummy_with_bytes(65 + i); @@ -2457,40 +2610,46 @@ benchmarks! { let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), - imported_functions: vec![ImportedFunction { - module: "seal0", - name: "unlock_delegate_dependency", - params: vec![ValueType::I32], - return_type: None, - }, ImportedFunction { - module: "seal0", - name: "lock_delegate_dependency", - params: vec![ValueType::I32], - return_type: None - }], - data_segments: vec![ - DataSegment { - offset: 0, - value: code_hashes_bytes, + imported_functions: vec![ + ImportedFunction { + module: "seal0", + name: "unlock_delegate_dependency", + params: vec![ValueType::I32], + return_type: None, + }, + ImportedFunction { + module: "seal0", + name: "lock_delegate_dependency", + params: vec![ValueType::I32], + return_type: None, }, ], - deploy_body: Some(body::repeated_dyn(r, vec![ - Counter(0, code_hash_len as u32), // code_hash_ptr - Regular(Instruction::Call(1)), - ])), - call_body: Some(body::repeated_dyn(r, vec![ - Counter(0, code_hash_len as u32), // code_hash_ptr - Regular(Instruction::Call(0)), - ])), - .. Default::default() + data_segments: vec![DataSegment { offset: 0, value: code_hashes_bytes }], + deploy_body: Some(body::repeated_dyn( + r, + vec![ + Counter(0, code_hash_len as u32), // code_hash_ptr + Regular(Instruction::Call(1)), + ], + )), + call_body: Some(body::repeated_dyn( + r, + vec![ + Counter(0, code_hash_len as u32), // code_hash_ptr + Regular(Instruction::Call(0)), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - #[pov_mode = Measured] - seal_reentrance_count { - let r in 0 .. API_BENCHMARK_RUNS; + #[benchmark(pov_mode = Measured)] + fn seal_reentrance_count(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -2499,19 +2658,20 @@ benchmarks! { params: vec![], return_type: Some(ValueType::I32), }], - call_body: Some(body::repeated(r, &[ - Instruction::Call(0), - Instruction::Drop, - ])), - .. Default::default() + call_body: Some(body::repeated(r, &[Instruction::Call(0), Instruction::Drop])), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - #[pov_mode = Measured] - seal_account_reentrance_count { - let r in 0 .. API_BENCHMARK_RUNS; + #[benchmark(pov_mode = Measured)] + fn seal_account_reentrance_count( + r: Linear<0, API_BENCHMARK_RUNS>, + ) -> Result<(), BenchmarkError> { let dummy_code = WasmModule::::dummy_with_bytes(0); let accounts = (0..r) .map(|i| Contract::with_index(i + 1, dummy_code.clone(), vec![])) @@ -2526,26 +2686,26 @@ benchmarks! { params: vec![ValueType::I32], return_type: Some(ValueType::I32), }], - data_segments: vec![ - DataSegment { - offset: 0, - value: account_id_bytes, - }, - ], - call_body: Some(body::repeated_dyn(r, vec![ - Counter(0, account_id_len as u32), // account_ptr - Regular(Instruction::Call(0)), - Regular(Instruction::Drop), - ])), - .. Default::default() + data_segments: vec![DataSegment { offset: 0, value: account_id_bytes }], + call_body: Some(body::repeated_dyn( + r, + vec![ + Counter(0, account_id_len as u32), // account_ptr + Regular(Instruction::Call(0)), + Regular(Instruction::Drop), + ], + )), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } - #[pov_mode = Measured] - seal_instantiation_nonce { - let r in 0 .. API_BENCHMARK_RUNS; + #[benchmark(pov_mode = Measured)] + fn seal_instantiation_nonce(r: Linear<0, API_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { let code = WasmModule::::from(ModuleDefinition { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { @@ -2554,29 +2714,27 @@ benchmarks! { params: vec![], return_type: Some(ValueType::I64), }], - call_body: Some(body::repeated(r, &[ - Instruction::Call(0), - Instruction::Drop, - ])), - .. Default::default() + call_body: Some(body::repeated(r, &[Instruction::Call(0), Instruction::Drop])), + ..Default::default() }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) + #[extrinsic_call] + call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]); + Ok(()) + } // We load `i64` values from random linear memory locations and store the loaded // values back into yet another random linear memory location. - // The random addresses are uniformely distributed across the entire span of the linear memory. + // The random addresses are uniformly distributed across the entire span of the linear memory. // We do this to enforce random memory accesses which are particularly expensive. // // The combination of this computation is our weight base `w_base`. - #[pov_mode = Ignored] - instr_i64_load_store { - let r in 0 .. INSTR_BENCHMARK_RUNS; - + #[benchmark(pov_mode = Ignored)] + fn instr_i64_load_store(r: Linear<0, INSTR_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> { use rand::prelude::*; - // We do not need to be secure here. Fixed seed allows for determinstic results. + // We do not need to be secure here. Fixed seed allows for deterministic results. let mut rng = rand_pcg::Pcg32::seed_from_u64(8446744073709551615); let memory = ImportedMemory::max::(); @@ -2596,24 +2754,27 @@ benchmarks! { [ Instruction::I32Const(c0), // address for `i64.load_8s` Instruction::I64Load8S(0, 0), - Instruction::SetLocal(0), // temporarily store value loaded in `i64.load_8s` + Instruction::SetLocal(0), /* temporarily store value loaded in + * `i64.load_8s` */ Instruction::I32Const(c1), // address for `i64.store8` Instruction::GetLocal(0), // value to be stores in `i64.store8` Instruction::I64Store8(0, 0), ] - } + }, )), - .. Default::default() + ..Default::default() })); - }: { - sbox.invoke(); + #[block] + { + sbox.invoke(); + } + Ok(()) } // This is no benchmark. It merely exist to have an easy way to pretty print the currently // configured `Schedule` during benchmark development. Check the README on how to print this. - #[extra] - #[pov_mode = Ignored] - print_schedule { + #[benchmark(extra, pov_mode = Ignored)] + fn print_schedule() -> Result<(), BenchmarkError> { let max_weight = ::BlockWeights::get().max_block; let (weight_per_key, key_budget) = ContractInfo::::deletion_budget(max_weight); let schedule = T::Schedule::get(); @@ -2623,11 +2784,15 @@ benchmarks! { Lazy deletion weight per key: {weight_per_key} Lazy deletion keys per block: {key_budget} "); - }: {} + #[block] + {} + + Err(BenchmarkError::Skip) + } impl_benchmark_test_suite!( Contracts, crate::tests::ExtBuilder::default().build(), crate::tests::Test, - ) + ); } diff --git a/substrate/frame/contracts/src/exec.rs b/substrate/frame/contracts/src/exec.rs index 7da321af547d5b8f5539673f7540c328299945b6..41a0383811fdb09afe7655d0418039c714cff1a5 100644 --- a/substrate/frame/contracts/src/exec.rs +++ b/substrate/frame/contracts/src/exec.rs @@ -531,9 +531,9 @@ enum FrameArgs<'a, T: Config, E> { nonce: u64, /// The executable whose `deploy` function is run. executable: E, - /// A salt used in the contract address deriviation of the new contract. + /// A salt used in the contract address derivation of the new contract. salt: &'a [u8], - /// The input data is used in the contract address deriviation of the new contract. + /// The input data is used in the contract address derivation of the new contract. input_data: &'a [u8], }, } @@ -1466,14 +1466,14 @@ where fn sr25519_verify(&self, signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> bool { sp_io::crypto::sr25519_verify( - &SR25519Signature(*signature), + &SR25519Signature::from(*signature), message, - &SR25519Public(*pub_key), + &SR25519Public::from(*pub_key), ) } fn ecdsa_to_eth_address(&self, pk: &[u8; 33]) -> Result<[u8; 20], ()> { - ECDSAPublic(*pk).to_eth_address() + ECDSAPublic::from(*pk).to_eth_address() } #[cfg(test)] diff --git a/substrate/frame/contracts/src/gas.rs b/substrate/frame/contracts/src/gas.rs index 9271b615d00263c809e17dd68761790ea2fa9e7c..32fad2140f1463b454a3b8df1cb49b53e79e0c7b 100644 --- a/substrate/frame/contracts/src/gas.rs +++ b/substrate/frame/contracts/src/gas.rs @@ -23,7 +23,7 @@ use frame_support::{ DefaultNoBound, }; use sp_core::Get; -use sp_runtime::{traits::Zero, DispatchError}; +use sp_runtime::{traits::Zero, DispatchError, Saturating}; #[cfg(test)] use std::{any::Any, fmt::Debug}; @@ -37,6 +37,24 @@ impl ChargedAmount { } } +/// Used to capture the gas left before entering a host function. +/// +/// Has to be consumed in order to sync back the gas after leaving the host function. +#[must_use] +pub struct RefTimeLeft(u64); + +/// Resource that needs to be synced to the executor. +/// +/// Wrapped to make sure that the resource will be synced back the the executor. +#[must_use] +pub struct Syncable(u64); + +impl From for u64 { + fn from(from: Syncable) -> u64 { + from.0 + } +} + #[cfg(not(test))] pub trait TestAuxiliaries {} #[cfg(not(test))] @@ -84,8 +102,13 @@ pub struct GasMeter { gas_left: Weight, /// Due to `adjust_gas` and `nested` the `gas_left` can temporarily dip below its final value. gas_left_lowest: Weight, - /// Amount of fuel consumed by the engine from the last host function call. - engine_consumed: u64, + /// The amount of resources that was consumed by the execution engine. + /// + /// This should be equivalent to `self.gas_consumed().ref_time()` but expressed in whatever + /// unit the execution engine uses to track resource consumption. We have to track it + /// separately in order to avoid the loss of precision that happens when converting from + /// ref_time to the execution engine unit. + executor_consumed: u64, _phantom: PhantomData, #[cfg(test)] tokens: Vec, @@ -97,7 +120,7 @@ impl GasMeter { gas_limit, gas_left: gas_limit, gas_left_lowest: gas_limit, - engine_consumed: Default::default(), + executor_consumed: 0, _phantom: PhantomData, #[cfg(test)] tokens: Vec::new(), @@ -172,32 +195,41 @@ impl GasMeter { self.gas_left = self.gas_left.saturating_add(adjustment).min(self.gas_limit); } - /// This method is used for gas syncs with the engine. + /// Hand over the gas metering responsibility from the executor to this meter. /// - /// Updates internal `engine_comsumed` tracker of engine fuel consumption. + /// Needs to be called when entering a host function to update this meter with the + /// gas that was tracked by the executor. It tracks the latest seen total value + /// in order to compute the delta that needs to be charged. + pub fn sync_from_executor( + &mut self, + executor_total: u64, + ) -> Result { + let chargable_reftime = executor_total + .saturating_sub(self.executor_consumed) + .saturating_mul(u64::from(T::Schedule::get().instruction_weights.base)); + self.executor_consumed = executor_total; + self.gas_left + .checked_reduce(Weight::from_parts(chargable_reftime, 0)) + .ok_or_else(|| Error::::OutOfGas)?; + Ok(RefTimeLeft(self.gas_left.ref_time())) + } + + /// Hand over the gas metering responsibility from this meter to the executor. /// - /// Charges self with the `ref_time` Weight corresponding to wasmi fuel consumed on the engine - /// side since last sync. Passed value is scaled by multiplying it by the weight of a basic - /// operation, as such an operation in wasmi engine costs 1. + /// Needs to be called when leaving a host function in order to calculate how much + /// gas needs to be charged from the **executor**. It updates the last seen executor + /// total value so that it is correct when `sync_from_executor` is called the next time. /// - /// Returns the updated `gas_left` `Weight` value from the meter. - /// Normally this would never fail, as engine should fail first when out of gas. - pub fn charge_fuel(&mut self, wasmi_fuel_total: u64) -> Result { - // Take the part consumed since the last update. - let wasmi_fuel = wasmi_fuel_total.saturating_sub(self.engine_consumed); - if !wasmi_fuel.is_zero() { - self.engine_consumed = wasmi_fuel_total; - let reftime_consumed = - wasmi_fuel.saturating_mul(T::Schedule::get().instruction_weights.base as u64); - let ref_time_left = self - .gas_left - .ref_time() - .checked_sub(reftime_consumed) - .ok_or_else(|| Error::::OutOfGas)?; - - *(self.gas_left.ref_time_mut()) = ref_time_left; - } - Ok(self.gas_left) + /// It is important that this does **not** actually sync with the executor. That has + /// to be done by the caller. + pub fn sync_to_executor(&mut self, before: RefTimeLeft) -> Result { + let chargable_executor_resource = before + .0 + .saturating_sub(self.gas_left().ref_time()) + .checked_div(u64::from(T::Schedule::get().instruction_weights.base)) + .ok_or(Error::::InvalidSchedule)?; + self.executor_consumed.saturating_accrue(chargable_executor_resource); + Ok(Syncable(chargable_executor_resource)) } /// Returns the amount of gas that is required to run the same call. @@ -320,7 +352,7 @@ mod tests { assert!(gas_meter.charge(SimpleToken(1)).is_err()); } - // Make sure that the gas meter does not charge in case of overcharger + // Make sure that the gas meter does not charge in case of overcharge #[test] fn overcharge_does_not_charge() { let mut gas_meter = GasMeter::::new(Weight::from_parts(200, 0)); diff --git a/substrate/frame/contracts/src/lib.rs b/substrate/frame/contracts/src/lib.rs index 943bf5f85d4ef3f75b8d1384c5895a9941bbb79d..e14a4b8bcb874015e58747867462c05280e3e52a 100644 --- a/substrate/frame/contracts/src/lib.rs +++ b/substrate/frame/contracts/src/lib.rs @@ -214,6 +214,27 @@ pub struct Environment { block_number: EnvironmentType>, } +/// Defines the current version of the HostFn APIs. +/// This is used to communicate the available APIs in pallet-contracts. +/// +/// The version is bumped any time a new HostFn is added or stabilized. +#[derive(Encode, Decode, TypeInfo)] +pub struct ApiVersion(u16); +impl Default for ApiVersion { + fn default() -> Self { + Self(2) + } +} + +#[test] +fn api_version_is_up_to_date() { + assert_eq!( + 109, + crate::wasm::STABLE_API_COUNT, + "Stable API count has changed. Bump the returned value of ApiVersion::default() and update the test." + ); +} + #[frame_support::pallet] pub mod pallet { use super::*; @@ -222,7 +243,7 @@ pub mod pallet { use frame_system::pallet_prelude::*; use sp_runtime::Perbill; - /// The current storage version. + /// The in-code storage version. pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(15); #[pallet::pallet] @@ -277,6 +298,9 @@ pub mod pallet { /// Therefore please make sure to be restrictive about which dispatchables are allowed /// in order to not introduce a new DoS vector like memory allocation patterns that can /// be exploited to drive the runtime into a panic. + /// + /// This filter does not apply to XCM transact calls. To impose restrictions on XCM transact + /// calls, you must configure them separately within the XCM pallet itself. type CallFilter: Contains<::RuntimeCall>; /// Used to answer contracts' queries regarding the current weight price. This is **not** @@ -367,6 +391,24 @@ pub mod pallet { #[pallet::constant] type MaxDebugBufferLen: Get; + /// Origin allowed to upload code. + /// + /// By default, it is safe to set this to `EnsureSigned`, allowing anyone to upload contract + /// code. + type UploadOrigin: EnsureOrigin; + + /// Origin allowed to instantiate code. + /// + /// # Note + /// + /// This is not enforced when a contract instantiates another contract. The + /// [`Self::UploadOrigin`] should make sure that no code is deployed that does unwanted + /// instantiations. + /// + /// By default, it is safe to set this to `EnsureSigned`, allowing anyone to instantiate + /// contract code. + type InstantiateOrigin: EnsureOrigin; + /// Overarching hold reason. type RuntimeHoldReason: From; @@ -402,6 +444,12 @@ pub mod pallet { #[pallet::constant] type Environment: Get>; + /// The version of the HostFn APIs that are available in the runtime. + /// + /// Only valid value is `()`. + #[pallet::constant] + type ApiVersion: Get; + /// A type that exposes XCM APIs, allowing contracts to interact with other parachains, and /// execute XCM programs. type Xcm: xcm_builder::Controller< @@ -609,8 +657,17 @@ pub mod pallet { /// To avoid this situation a constructor could employ access control so that it can /// only be instantiated by permissioned entities. The same is true when uploading /// through [`Self::instantiate_with_code`]. + /// + /// Use [`Determinism::Relaxed`] exclusively for non-deterministic code. If the uploaded + /// code is deterministic, specifying [`Determinism::Relaxed`] will be disregarded and + /// result in higher gas costs. #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::upload_code(code.len() as u32))] + #[pallet::weight( + match determinism { + Determinism::Enforced => T::WeightInfo::upload_code_determinism_enforced(code.len() as u32), + Determinism::Relaxed => T::WeightInfo::upload_code_determinism_relaxed(code.len() as u32), + } + )] pub fn upload_code( origin: OriginFor, code: Vec, @@ -618,7 +675,7 @@ pub mod pallet { determinism: Determinism, ) -> DispatchResult { Migration::::ensure_migrated()?; - let origin = ensure_signed(origin)?; + let origin = T::UploadOrigin::ensure_origin(origin)?; Self::bare_upload_code(origin, code, storage_deposit_limit.map(Into::into), determinism) .map(|_| ()) } @@ -767,11 +824,17 @@ pub mod pallet { salt: Vec, ) -> DispatchResultWithPostInfo { Migration::::ensure_migrated()?; - let origin = ensure_signed(origin)?; + + // These two origins will usually be the same; however, we treat them as separate since + // it is possible for the `Success` value of `UploadOrigin` and `InstantiateOrigin` to + // differ. + let upload_origin = T::UploadOrigin::ensure_origin(origin.clone())?; + let instantiate_origin = T::InstantiateOrigin::ensure_origin(origin)?; + let code_len = code.len() as u32; let (module, upload_deposit) = Self::try_upload_code( - origin.clone(), + upload_origin, code, storage_deposit_limit.clone().map(Into::into), Determinism::Enforced, @@ -785,7 +848,7 @@ pub mod pallet { let data_len = data.len() as u32; let salt_len = salt.len() as u32; let common = CommonInput { - origin: Origin::from_account_id(origin), + origin: Origin::from_account_id(instantiate_origin), value, data, gas_limit, @@ -826,10 +889,11 @@ pub mod pallet { salt: Vec, ) -> DispatchResultWithPostInfo { Migration::::ensure_migrated()?; + let origin = T::InstantiateOrigin::ensure_origin(origin)?; let data_len = data.len() as u32; let salt_len = salt.len() as u32; let common = CommonInput { - origin: Origin::from_runtime_origin(origin)?, + origin: Origin::from_account_id(origin), value, data, gas_limit, @@ -1043,7 +1107,7 @@ pub mod pallet { /// A more detailed error can be found on the node console if debug messages are enabled /// by supplying `-lruntime::contracts=debug`. CodeRejected, - /// An indetermistic code was used in a context where this is not permitted. + /// An indeterministic code was used in a context where this is not permitted. Indeterministic, /// A pending migration needs to complete before the extrinsic can be called. MigrationInProgress, diff --git a/substrate/frame/contracts/src/migration.rs b/substrate/frame/contracts/src/migration.rs index 6d61cb6b1e1af158b9d4942efabc73267f8e44f7..f30ae1ebfadee55eeca000e74b4aac76f93f7551 100644 --- a/substrate/frame/contracts/src/migration.rs +++ b/substrate/frame/contracts/src/migration.rs @@ -263,10 +263,10 @@ impl Migration { impl OnRuntimeUpgrade for Migration { fn on_runtime_upgrade() -> Weight { let name = >::name(); - let current_version = >::current_storage_version(); + let in_code_version = >::in_code_storage_version(); let on_chain_version = >::on_chain_storage_version(); - if on_chain_version == current_version { + if on_chain_version == in_code_version { log::warn!( target: LOG_TARGET, "{name}: No Migration performed storage_version = latest_version = {:?}", @@ -289,7 +289,7 @@ impl OnRuntimeUpgrade for Migration OnRuntimeUpgrade for Migration>::on_chain_storage_version(); - let current_version = >::current_storage_version(); + let in_code_version = >::in_code_storage_version(); - if on_chain_version == current_version { + if on_chain_version == in_code_version { return Ok(Default::default()) } log::debug!( target: LOG_TARGET, - "Requested migration of {} from {:?}(on-chain storage version) to {:?}(current storage version)", - >::name(), on_chain_version, current_version + "Requested migration of {} from {:?}(on-chain storage version) to {:?}(in-code storage version)", + >::name(), on_chain_version, in_code_version ); ensure!( - T::Migrations::is_upgrade_supported(on_chain_version, current_version), - "Unsupported upgrade: VERSION_RANGE should be (on-chain storage version + 1, current storage version)" + T::Migrations::is_upgrade_supported(on_chain_version, in_code_version), + "Unsupported upgrade: VERSION_RANGE should be (on-chain storage version + 1, in-code storage version)" ); Ok(Default::default()) @@ -421,7 +421,7 @@ impl Migration { }, StepResult::Completed { steps_done } => { in_progress_version.put::>(); - if >::current_storage_version() != in_progress_version { + if >::in_code_storage_version() != in_progress_version { log::info!( target: LOG_TARGET, "{name}: Next migration is {:?},", diff --git a/substrate/frame/contracts/src/migration/v09.rs b/substrate/frame/contracts/src/migration/v09.rs index 98fcccc2c0becedccd4bd15eed98b36fd52662ee..8e718871ecb1844849e18e6480ad1d6d0b7ba5f1 100644 --- a/substrate/frame/contracts/src/migration/v09.rs +++ b/substrate/frame/contracts/src/migration/v09.rs @@ -28,7 +28,7 @@ use frame_support::{pallet_prelude::*, storage_alias, DefaultNoBound, Identity}; use sp_runtime::TryRuntimeError; use sp_std::prelude::*; -mod old { +mod v8 { use super::*; #[derive(Encode, Decode)] @@ -50,14 +50,14 @@ mod old { #[cfg(feature = "runtime-benchmarks")] pub fn store_old_dummy_code(len: usize) { use sp_runtime::traits::Hash; - let module = old::PrefabWasmModule { + let module = v8::PrefabWasmModule { instruction_weights_version: 0, initial: 0, maximum: 0, code: vec![42u8; len], }; let hash = T::Hashing::hash(&module.code); - old::CodeStorage::::insert(hash, module); + v8::CodeStorage::::insert(hash, module); } #[derive(Encode, Decode)] @@ -89,9 +89,9 @@ impl MigrationStep for Migration { fn step(&mut self) -> (IsFinished, Weight) { let mut iter = if let Some(last_key) = self.last_code_hash.take() { - old::CodeStorage::::iter_from(old::CodeStorage::::hashed_key_for(last_key)) + v8::CodeStorage::::iter_from(v8::CodeStorage::::hashed_key_for(last_key)) } else { - old::CodeStorage::::iter() + v8::CodeStorage::::iter() }; if let Some((key, old)) = iter.next() { @@ -115,7 +115,7 @@ impl MigrationStep for Migration { #[cfg(feature = "try-runtime")] fn pre_upgrade_step() -> Result, TryRuntimeError> { - let sample: Vec<_> = old::CodeStorage::::iter().take(100).collect(); + let sample: Vec<_> = v8::CodeStorage::::iter().take(100).collect(); log::debug!(target: LOG_TARGET, "Taking sample of {} contract codes", sample.len()); Ok(sample.encode()) @@ -123,7 +123,7 @@ impl MigrationStep for Migration { #[cfg(feature = "try-runtime")] fn post_upgrade_step(state: Vec) -> Result<(), TryRuntimeError> { - let sample = , old::PrefabWasmModule)> as Decode>::decode(&mut &state[..]) + let sample = , v8::PrefabWasmModule)> as Decode>::decode(&mut &state[..]) .expect("pre_upgrade_step provides a valid state; qed"); log::debug!(target: LOG_TARGET, "Validating sample of {} contract codes", sample.len()); @@ -131,7 +131,7 @@ impl MigrationStep for Migration { let module = CodeStorage::::get(&code_hash).unwrap(); ensure!( module.instruction_weights_version == old.instruction_weights_version, - "invalid isntruction weights version" + "invalid instruction weights version" ); ensure!(module.determinism == Determinism::Enforced, "invalid determinism"); ensure!(module.initial == old.initial, "invalid initial"); diff --git a/substrate/frame/contracts/src/migration/v10.rs b/substrate/frame/contracts/src/migration/v10.rs index d64673aac7d268df80adb955ff3d5c5fd16c5681..1bee86e6a773a9b0d7720438644a4ae84d9bd332 100644 --- a/substrate/frame/contracts/src/migration/v10.rs +++ b/substrate/frame/contracts/src/migration/v10.rs @@ -47,7 +47,7 @@ use sp_runtime::{ }; use sp_std::prelude::*; -mod old { +mod v9 { use super::*; pub type BalanceOf = ( ) where OldCurrency: ReservableCurrency<::AccountId> + 'static, { - let info = old::ContractInfo { + let info = v9::ContractInfo { trie_id: info.trie_id, code_hash: info.code_hash, storage_bytes: Default::default(), @@ -94,7 +94,7 @@ pub fn store_old_contract_info( storage_item_deposit: Default::default(), storage_base_deposit: Default::default(), }; - old::ContractInfoOf::::insert(account, info); + v9::ContractInfoOf::::insert(account, info); } #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen)] @@ -120,9 +120,9 @@ where pub code_hash: CodeHash, storage_bytes: u32, storage_items: u32, - pub storage_byte_deposit: old::BalanceOf, - storage_item_deposit: old::BalanceOf, - storage_base_deposit: old::BalanceOf, + pub storage_byte_deposit: v9::BalanceOf, + storage_item_deposit: v9::BalanceOf, + storage_base_deposit: v9::BalanceOf, } #[derive(Encode, Decode, MaxEncodedLen, DefaultNoBound)] @@ -152,7 +152,7 @@ fn deposit_address( impl MigrationStep for Migration where OldCurrency: ReservableCurrency<::AccountId> - + Inspect<::AccountId, Balance = old::BalanceOf>, + + Inspect<::AccountId, Balance = v9::BalanceOf>, { const VERSION: u16 = 10; @@ -162,11 +162,11 @@ where fn step(&mut self) -> (IsFinished, Weight) { let mut iter = if let Some(last_account) = self.last_account.take() { - old::ContractInfoOf::::iter_from( - old::ContractInfoOf::::hashed_key_for(last_account), + v9::ContractInfoOf::::iter_from( + v9::ContractInfoOf::::hashed_key_for(last_account), ) } else { - old::ContractInfoOf::::iter() + v9::ContractInfoOf::::iter() }; if let Some((account, contract)) = iter.next() { @@ -276,7 +276,7 @@ where #[cfg(feature = "try-runtime")] fn pre_upgrade_step() -> Result, TryRuntimeError> { - let sample: Vec<_> = old::ContractInfoOf::::iter().take(10).collect(); + let sample: Vec<_> = v9::ContractInfoOf::::iter().take(10).collect(); log::debug!(target: LOG_TARGET, "Taking sample of {} contracts", sample.len()); Ok(sample.encode()) @@ -284,7 +284,7 @@ where #[cfg(feature = "try-runtime")] fn post_upgrade_step(state: Vec) -> Result<(), TryRuntimeError> { - let sample = )> as Decode>::decode( + let sample = )> as Decode>::decode( &mut &state[..], ) .expect("pre_upgrade_step provides a valid state; qed"); diff --git a/substrate/frame/contracts/src/migration/v11.rs b/substrate/frame/contracts/src/migration/v11.rs index a5b11f6e08977fbbeb737bc2650ad318ddac97c8..9bfbb25edfb44a8072d2d0073ec4cdb4d27cc360 100644 --- a/substrate/frame/contracts/src/migration/v11.rs +++ b/substrate/frame/contracts/src/migration/v11.rs @@ -29,7 +29,7 @@ use sp_runtime::TryRuntimeError; use codec::{Decode, Encode}; use frame_support::{pallet_prelude::*, storage_alias, DefaultNoBound}; use sp_std::{marker::PhantomData, prelude::*}; -mod old { +mod v10 { use super::*; #[derive(Encode, Decode, TypeInfo, MaxEncodedLen)] @@ -51,11 +51,11 @@ pub struct DeletionQueueManager { #[cfg(any(feature = "runtime-benchmarks", feature = "try-runtime"))] pub fn fill_old_queue(len: usize) { - let queue: Vec = - core::iter::repeat_with(|| old::DeletedContract { trie_id: Default::default() }) + let queue: Vec = + core::iter::repeat_with(|| v10::DeletedContract { trie_id: Default::default() }) .take(len) .collect(); - old::DeletionQueue::::set(Some(queue)); + v10::DeletionQueue::::set(Some(queue)); } #[storage_alias] @@ -80,7 +80,7 @@ impl MigrationStep for Migration { } fn step(&mut self) -> (IsFinished, Weight) { - let Some(old_queue) = old::DeletionQueue::::take() else { + let Some(old_queue) = v10::DeletionQueue::::take() else { return (IsFinished::Yes, Weight::zero()) }; let len = old_queue.len(); @@ -106,7 +106,7 @@ impl MigrationStep for Migration { #[cfg(feature = "try-runtime")] fn pre_upgrade_step() -> Result, TryRuntimeError> { - let old_queue = old::DeletionQueue::::take().unwrap_or_default(); + let old_queue = v10::DeletionQueue::::take().unwrap_or_default(); if old_queue.is_empty() { let len = 10u32; diff --git a/substrate/frame/contracts/src/migration/v12.rs b/substrate/frame/contracts/src/migration/v12.rs index 7dee31503101ba753b0e807d11621be711d4b58b..d9128286df389f84b451660e8d00f4a886b629d4 100644 --- a/substrate/frame/contracts/src/migration/v12.rs +++ b/substrate/frame/contracts/src/migration/v12.rs @@ -34,7 +34,7 @@ use sp_runtime::TryRuntimeError; use sp_runtime::{traits::Zero, FixedPointNumber, FixedU128, Saturating}; use sp_std::prelude::*; -mod old { +mod v11 { use super::*; pub type BalanceOf = , #[codec(compact)] - deposit: old::BalanceOf, + deposit: v11::BalanceOf, #[codec(compact)] refcount: u64, determinism: Determinism, @@ -112,17 +112,17 @@ where let hash = T::Hashing::hash(&code); PristineCode::::insert(hash, code.clone()); - let module = old::PrefabWasmModule { + let module = v11::PrefabWasmModule { instruction_weights_version: Default::default(), initial: Default::default(), maximum: Default::default(), code, determinism: Determinism::Enforced, }; - old::CodeStorage::::insert(hash, module); + v11::CodeStorage::::insert(hash, module); - let info = old::OwnerInfo { owner: account, deposit: u32::MAX.into(), refcount: u64::MAX }; - old::OwnerInfoOf::::insert(hash, info); + let info = v11::OwnerInfo { owner: account, deposit: u32::MAX.into(), refcount: u64::MAX }; + v11::OwnerInfoOf::::insert(hash, info); } #[derive(Encode, Decode, MaxEncodedLen, DefaultNoBound)] @@ -148,16 +148,16 @@ where fn step(&mut self) -> (IsFinished, Weight) { let mut iter = if let Some(last_key) = self.last_code_hash.take() { - old::OwnerInfoOf::::iter_from( - old::OwnerInfoOf::::hashed_key_for(last_key), + v11::OwnerInfoOf::::iter_from( + v11::OwnerInfoOf::::hashed_key_for(last_key), ) } else { - old::OwnerInfoOf::::iter() + v11::OwnerInfoOf::::iter() }; if let Some((hash, old_info)) = iter.next() { log::debug!(target: LOG_TARGET, "Migrating OwnerInfo for code_hash {:?}", hash); - let module = old::CodeStorage::::take(hash) + let module = v11::CodeStorage::::take(hash) .expect(format!("No PrefabWasmModule found for code_hash: {:?}", hash).as_str()); let code_len = module.code.len(); @@ -184,7 +184,7 @@ where let bytes_before = module .encoded_size() .saturating_add(code_len) - .saturating_add(old::OwnerInfo::::max_encoded_len()) + .saturating_add(v11::OwnerInfo::::max_encoded_len()) as u32; let items_before = 3u32; let deposit_expected_before = price_per_byte @@ -241,10 +241,10 @@ where fn pre_upgrade_step() -> Result, TryRuntimeError> { let len = 100; log::debug!(target: LOG_TARGET, "Taking sample of {} OwnerInfo(s)", len); - let sample: Vec<_> = old::OwnerInfoOf::::iter() + let sample: Vec<_> = v11::OwnerInfoOf::::iter() .take(len) .map(|(k, v)| { - let module = old::CodeStorage::::get(k) + let module = v11::CodeStorage::::get(k) .expect("No PrefabWasmModule found for code_hash: {:?}"); let info: CodeInfo = CodeInfo { determinism: module.determinism, @@ -258,9 +258,9 @@ where .collect(); let storage: u32 = - old::CodeStorage::::iter().map(|(_k, v)| v.encoded_size() as u32).sum(); - let mut deposit: old::BalanceOf = Default::default(); - old::OwnerInfoOf::::iter().for_each(|(_k, v)| deposit += v.deposit); + v11::CodeStorage::::iter().map(|(_k, v)| v.encoded_size() as u32).sum(); + let mut deposit: v11::BalanceOf = Default::default(); + v11::OwnerInfoOf::::iter().for_each(|(_k, v)| deposit += v.deposit); Ok((sample, deposit, storage).encode()) } @@ -269,7 +269,7 @@ where fn post_upgrade_step(state: Vec) -> Result<(), TryRuntimeError> { let state = <( Vec<(CodeHash, CodeInfo)>, - old::BalanceOf, + v11::BalanceOf, u32, ) as Decode>::decode(&mut &state[..]) .unwrap(); @@ -283,7 +283,7 @@ where ensure!(info.refcount == old.refcount, "invalid refcount"); } - if let Some((k, _)) = old::CodeStorage::::iter().next() { + if let Some((k, _)) = v11::CodeStorage::::iter().next() { log::warn!( target: LOG_TARGET, "CodeStorage is still NOT empty, found code_hash: {:?}", @@ -292,7 +292,7 @@ where } else { log::debug!(target: LOG_TARGET, "CodeStorage is empty."); } - if let Some((k, _)) = old::OwnerInfoOf::::iter().next() { + if let Some((k, _)) = v11::OwnerInfoOf::::iter().next() { log::warn!( target: LOG_TARGET, "OwnerInfoOf is still NOT empty, found code_hash: {:?}", @@ -302,7 +302,7 @@ where log::debug!(target: LOG_TARGET, "OwnerInfoOf is empty."); } - let mut deposit: old::BalanceOf = Default::default(); + let mut deposit: v11::BalanceOf = Default::default(); let mut items = 0u32; let mut storage_info = 0u32; CodeInfoOf::::iter().for_each(|(_k, v)| { diff --git a/substrate/frame/contracts/src/migration/v13.rs b/substrate/frame/contracts/src/migration/v13.rs index dd2eb12eb62a5daf92cec8b5910c03f6d81ae0c0..498c44d53abce9730e9833fcbd27e1b2dbb2fe38 100644 --- a/substrate/frame/contracts/src/migration/v13.rs +++ b/substrate/frame/contracts/src/migration/v13.rs @@ -28,7 +28,7 @@ use frame_support::{pallet_prelude::*, storage_alias, DefaultNoBound}; use sp_runtime::BoundedBTreeMap; use sp_std::prelude::*; -mod old { +mod v12 { use super::*; #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] @@ -59,7 +59,7 @@ pub fn store_old_contract_info(account: T::AccountId, info: crate::Co let entropy = (b"contract_depo_v1", account.clone()).using_encoded(T::Hashing::hash); let deposit_account = Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref())) .expect("infinite length input; no invalid inputs for type; qed"); - let info = old::ContractInfo { + let info = v12::ContractInfo { trie_id: info.trie_id.clone(), deposit_account, code_hash: info.code_hash, @@ -69,7 +69,7 @@ pub fn store_old_contract_info(account: T::AccountId, info: crate::Co storage_item_deposit: Default::default(), storage_base_deposit: Default::default(), }; - old::ContractInfoOf::::insert(account, info); + v12::ContractInfoOf::::insert(account, info); } #[storage_alias] @@ -104,11 +104,11 @@ impl MigrationStep for Migration { fn step(&mut self) -> (IsFinished, Weight) { let mut iter = if let Some(last_account) = self.last_account.take() { - old::ContractInfoOf::::iter_from(old::ContractInfoOf::::hashed_key_for( + v12::ContractInfoOf::::iter_from(v12::ContractInfoOf::::hashed_key_for( last_account, )) } else { - old::ContractInfoOf::::iter() + v12::ContractInfoOf::::iter() }; if let Some((key, old)) = iter.next() { diff --git a/substrate/frame/contracts/src/migration/v14.rs b/substrate/frame/contracts/src/migration/v14.rs index 94534d05fdf889d05227149efd57ede584ecb013..09da09e5bcfb1f9ab4396a651e67519a75e09df2 100644 --- a/substrate/frame/contracts/src/migration/v14.rs +++ b/substrate/frame/contracts/src/migration/v14.rs @@ -44,7 +44,7 @@ use sp_runtime::{traits::Zero, Saturating}; #[cfg(feature = "try-runtime")] use sp_std::collections::btree_map::BTreeMap; -mod old { +mod v13 { use super::*; pub type BalanceOf = , #[codec(compact)] - pub deposit: old::BalanceOf, + pub deposit: v13::BalanceOf, #[codec(compact)] pub refcount: u64, pub determinism: Determinism, @@ -86,14 +86,14 @@ where let code = vec![42u8; len as usize]; let hash = T::Hashing::hash(&code); - let info = old::CodeInfo { + let info = v13::CodeInfo { owner: account, deposit: 10_000u32.into(), refcount: u64::MAX, determinism: Determinism::Enforced, code_len: len, }; - old::CodeInfoOf::::insert(hash, info); + v13::CodeInfoOf::::insert(hash, info); } #[cfg(feature = "try-runtime")] @@ -105,9 +105,9 @@ where OldCurrency: ReservableCurrency<::AccountId>, { /// Total reserved balance as code upload deposit for the owner. - reserved: old::BalanceOf, + reserved: v13::BalanceOf, /// Total balance of the owner. - total: old::BalanceOf, + total: v13::BalanceOf, } #[derive(Encode, Decode, MaxEncodedLen, DefaultNoBound)] @@ -134,11 +134,11 @@ where fn step(&mut self) -> (IsFinished, Weight) { let mut iter = if let Some(last_hash) = self.last_code_hash.take() { - old::CodeInfoOf::::iter_from( - old::CodeInfoOf::::hashed_key_for(last_hash), + v13::CodeInfoOf::::iter_from( + v13::CodeInfoOf::::hashed_key_for(last_hash), ) } else { - old::CodeInfoOf::::iter() + v13::CodeInfoOf::::iter() }; if let Some((hash, code_info)) = iter.next() { @@ -194,7 +194,7 @@ where #[cfg(feature = "try-runtime")] fn pre_upgrade_step() -> Result, TryRuntimeError> { - let info: Vec<_> = old::CodeInfoOf::::iter().collect(); + let info: Vec<_> = v13::CodeInfoOf::::iter().collect(); let mut owner_balance_allocation = BTreeMap::, BalanceAllocation>::new(); diff --git a/substrate/frame/contracts/src/migration/v15.rs b/substrate/frame/contracts/src/migration/v15.rs index 180fe855ca66728ba18b17555363eeac11a340ba..c77198d6fea0f6b7c305d40b981330cbb50e355d 100644 --- a/substrate/frame/contracts/src/migration/v15.rs +++ b/substrate/frame/contracts/src/migration/v15.rs @@ -46,7 +46,7 @@ use sp_runtime::{traits::Zero, Saturating}; #[cfg(feature = "try-runtime")] use sp_std::vec::Vec; -mod old { +mod v14 { use super::*; #[derive( @@ -81,7 +81,7 @@ pub fn store_old_contract_info(account: T::AccountId, info: crate::Co let entropy = (b"contract_depo_v1", account.clone()).using_encoded(T::Hashing::hash); let deposit_account = Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref())) .expect("infinite length input; no invalid inputs for type; qed"); - let info = old::ContractInfo { + let info = v14::ContractInfo { trie_id: info.trie_id.clone(), deposit_account, code_hash: info.code_hash, @@ -92,7 +92,7 @@ pub fn store_old_contract_info(account: T::AccountId, info: crate::Co storage_base_deposit: info.storage_base_deposit(), delegate_dependencies: info.delegate_dependencies().clone(), }; - old::ContractInfoOf::::insert(account, info); + v14::ContractInfoOf::::insert(account, info); } #[derive(Encode, Decode, CloneNoBound, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] @@ -127,11 +127,11 @@ impl MigrationStep for Migration { fn step(&mut self) -> (IsFinished, Weight) { let mut iter = if let Some(last_account) = self.last_account.take() { - old::ContractInfoOf::::iter_from(old::ContractInfoOf::::hashed_key_for( + v14::ContractInfoOf::::iter_from(v14::ContractInfoOf::::hashed_key_for( last_account, )) } else { - old::ContractInfoOf::::iter() + v14::ContractInfoOf::::iter() }; if let Some((account, old_contract)) = iter.next() { @@ -243,11 +243,11 @@ impl MigrationStep for Migration { #[cfg(feature = "try-runtime")] fn pre_upgrade_step() -> Result, TryRuntimeError> { - let sample: Vec<_> = old::ContractInfoOf::::iter().take(100).collect(); + let sample: Vec<_> = v14::ContractInfoOf::::iter().take(100).collect(); log::debug!(target: LOG_TARGET, "Taking sample of {} contracts", sample.len()); - let state: Vec<(T::AccountId, old::ContractInfo, BalanceOf, BalanceOf)> = sample + let state: Vec<(T::AccountId, v14::ContractInfo, BalanceOf, BalanceOf)> = sample .iter() .map(|(account, contract)| { ( @@ -265,7 +265,7 @@ impl MigrationStep for Migration { #[cfg(feature = "try-runtime")] fn post_upgrade_step(state: Vec) -> Result<(), TryRuntimeError> { let sample = - , BalanceOf, BalanceOf)> as Decode>::decode( + , BalanceOf, BalanceOf)> as Decode>::decode( &mut &state[..], ) .expect("pre_upgrade_step provides a valid state; qed"); diff --git a/substrate/frame/contracts/src/schedule.rs b/substrate/frame/contracts/src/schedule.rs index b2e3801deaec1a36aef589427a6bf9dd324183b8..06a7c2005aa5e46e814d6eff192775ca63881fae 100644 --- a/substrate/frame/contracts/src/schedule.rs +++ b/substrate/frame/contracts/src/schedule.rs @@ -193,7 +193,7 @@ pub struct HostFnWeights { /// Weight of calling `seal_set_storage`. pub set_storage: Weight, - /// Weight per written byten of an item stored with `seal_set_storage`. + /// Weight per written byte of an item stored with `seal_set_storage`. pub set_storage_per_new_byte: Weight, /// Weight per overwritten byte of an item stored with `seal_set_storage`. diff --git a/substrate/frame/contracts/src/storage.rs b/substrate/frame/contracts/src/storage.rs index 3304166607d28af88ebd381e46bfd18f3c3b293c..e99afd5d42ee4684adc874d68bdba325feb58358 100644 --- a/substrate/frame/contracts/src/storage.rs +++ b/substrate/frame/contracts/src/storage.rs @@ -108,6 +108,11 @@ impl ContractInfo { Ok(contract) } + /// Returns the number of locked delegate dependencies. + pub fn delegate_dependencies_count(&self) -> usize { + self.delegate_dependencies.len() + } + /// Associated child trie unique id is built from the hash part of the trie id. pub fn child_trie_info(&self) -> ChildInfo { ChildInfo::new_default(self.trie_id.as_ref()) @@ -322,7 +327,8 @@ impl ContractInfo { KillStorageResult::SomeRemaining(_) => return weight_limit, KillStorageResult::AllRemoved(keys_removed) => { entry.remove(); - remaining_key_budget = remaining_key_budget.saturating_sub(keys_removed); + // charge at least one key even if none were removed. + remaining_key_budget = remaining_key_budget.saturating_sub(keys_removed.max(1)); }, }; } diff --git a/substrate/frame/contracts/src/tests.rs b/substrate/frame/contracts/src/tests.rs index cd4e43ed4c270b890d23026ab201d751b77bcc92..ed486fc4a67d521b170469db35986720ba0e1935 100644 --- a/substrate/frame/contracts/src/tests.rs +++ b/substrate/frame/contracts/src/tests.rs @@ -35,8 +35,8 @@ use crate::{ tests::test_utils::{get_contract, get_contract_checked}, wasm::{Determinism, ReturnErrorCode as RuntimeReturnCode}, weights::WeightInfo, - BalanceOf, Code, CodeHash, CodeInfoOf, CollectEvents, Config, ContractInfo, ContractInfoOf, - DebugInfo, DefaultAddressGenerator, DeletionQueueCounter, Error, HoldReason, + Array, BalanceOf, Code, CodeHash, CodeInfoOf, CollectEvents, Config, ContractInfo, + ContractInfoOf, DebugInfo, DefaultAddressGenerator, DeletionQueueCounter, Error, HoldReason, MigrationInProgress, Origin, Pallet, PristineCode, Schedule, }; use assert_matches::assert_matches; @@ -45,6 +45,7 @@ use frame_support::{ assert_err, assert_err_ignore_postinfo, assert_err_with_weight, assert_noop, assert_ok, derive_impl, dispatch::{DispatchErrorWithPostInfo, PostDispatchInfo}, + pallet_prelude::EnsureOrigin, parameter_types, storage::child, traits::{ @@ -330,7 +331,7 @@ parameter_types! { pub static ExistentialDeposit: u64 = 1; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type AccountId = AccountId32; type Lookup = IdentityLookup; @@ -434,6 +435,33 @@ impl Contains for TestFilter { } } +parameter_types! { + pub static UploadAccount: Option<::AccountId> = None; + pub static InstantiateAccount: Option<::AccountId> = None; +} + +pub struct EnsureAccount(sp_std::marker::PhantomData<(T, A)>); +impl>>> + EnsureOrigin<::RuntimeOrigin> for EnsureAccount +where + ::AccountId: From, +{ + type Success = T::AccountId; + + fn try_origin(o: T::RuntimeOrigin) -> Result { + let who = as EnsureOrigin<_>>::try_origin(o.clone())?; + if matches!(A::get(), Some(a) if who != a) { + return Err(o) + } + + Ok(who) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + Err(()) + } +} parameter_types! { pub static UnstableInterface: bool = true; } @@ -458,6 +486,8 @@ impl Config for Test { type MaxCodeLen = ConstU32<{ 123 * 1024 }>; type MaxStorageKeyLen = ConstU32<128>; type UnsafeUnstableInterface = UnstableInterface; + type UploadOrigin = EnsureAccount; + type InstantiateOrigin = EnsureAccount; type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; type RuntimeHoldReason = RuntimeHoldReason; type Migrations = crate::migration::codegen::BenchMigrations; @@ -465,6 +495,7 @@ impl Config for Test { type MaxDelegateDependencies = MaxDelegateDependencies; type Debug = TestDebug; type Environment = (); + type ApiVersion = (); type Xcm = (); } @@ -821,27 +852,6 @@ fn deposit_event_max_value_limit() { }); } -// Fail out of fuel (ref_time weight) inside the start function. -#[test] -fn run_out_of_fuel_start_fun() { - let (wasm, _code_hash) = compile_module::("run_out_of_gas_start_fn").unwrap(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let _ = ::Currency::set_balance(&ALICE, 1_000_000); - assert_err_ignore_postinfo!( - Contracts::instantiate_with_code( - RuntimeOrigin::signed(ALICE), - 0, - Weight::from_parts(1_000_000_000_000, u64::MAX), - None, - wasm, - vec![], - vec![], - ), - Error::::OutOfGas, - ); - }); -} - // Fail out of fuel (ref_time weight) in the engine. #[test] fn run_out_of_fuel_engine() { @@ -925,50 +935,15 @@ fn run_out_of_fuel_host() { #[test] fn gas_syncs_work() { - let (wasm0, _code_hash) = compile_module::("seal_input_noop").unwrap(); - let (wasm1, _code_hash) = compile_module::("seal_input_once").unwrap(); - let (wasm2, _code_hash) = compile_module::("seal_input_twice").unwrap(); + let (code, _code_hash) = compile_module::("caller_is_origin_n").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - // Instantiate noop contract. - let addr0 = Contracts::bare_instantiate( - ALICE, - 0, - GAS_LIMIT, - None, - Code::Upload(wasm0), - vec![], - vec![], - DebugInfo::Skip, - CollectEvents::Skip, - ) - .result - .unwrap() - .account_id; - - // Instantiate 1st contract. - let addr1 = Contracts::bare_instantiate( - ALICE, - 0, - GAS_LIMIT, - None, - Code::Upload(wasm1), - vec![], - vec![], - DebugInfo::Skip, - CollectEvents::Skip, - ) - .result - .unwrap() - .account_id; - - // Instantiate 2nd contract. - let addr2 = Contracts::bare_instantiate( + let addr = Contracts::bare_instantiate( ALICE, 0, GAS_LIMIT, None, - Code::Upload(wasm2), + Code::Upload(code), vec![], vec![], DebugInfo::Skip, @@ -980,11 +955,11 @@ fn gas_syncs_work() { let result = Contracts::bare_call( ALICE, - addr0, + addr.clone(), 0, GAS_LIMIT, None, - 1u8.to_le_bytes().to_vec(), + 0u32.encode(), DebugInfo::Skip, CollectEvents::Skip, Determinism::Enforced, @@ -994,27 +969,28 @@ fn gas_syncs_work() { let result = Contracts::bare_call( ALICE, - addr1, + addr.clone(), 0, GAS_LIMIT, None, - 1u8.to_le_bytes().to_vec(), + 1u32.encode(), DebugInfo::Skip, CollectEvents::Skip, Determinism::Enforced, ); assert_ok!(result.result); let gas_consumed_once = result.gas_consumed.ref_time(); - let host_consumed_once = ::Schedule::get().host_fn_weights.input.ref_time(); + let host_consumed_once = + ::Schedule::get().host_fn_weights.caller_is_origin.ref_time(); let engine_consumed_once = gas_consumed_once - host_consumed_once - engine_consumed_noop; let result = Contracts::bare_call( ALICE, - addr2, + addr, 0, GAS_LIMIT, None, - 1u8.to_le_bytes().to_vec(), + 2u32.encode(), DebugInfo::Skip, CollectEvents::Skip, Determinism::Enforced, @@ -1371,7 +1347,7 @@ fn transfer_expendable_cannot_kill_account() { } #[test] -fn cannot_self_destruct_through_draning() { +fn cannot_self_destruct_through_draining() { let (wasm, _code_hash) = compile_module::("drain").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -1686,7 +1662,7 @@ fn cannot_self_destruct_in_constructor() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - // Fail to instantiate the BOB because the contructor calls seal_terminate. + // Fail to instantiate the BOB because the constructor calls seal_terminate. assert_err_ignore_postinfo!( Contracts::instantiate_with_code( RuntimeOrigin::signed(ALICE), @@ -4422,96 +4398,6 @@ fn contract_reverted() { }); } -#[test] -fn code_rejected_error_works() { - ExtBuilder::default().existential_deposit(200).build().execute_with(|| { - let _ = ::Currency::set_balance(&ALICE, 1_000_000); - - let (wasm, _) = compile_module::("invalid_module").unwrap(); - assert_noop!( - Contracts::upload_code( - RuntimeOrigin::signed(ALICE), - wasm.clone(), - None, - Determinism::Enforced - ), - >::CodeRejected, - ); - let result = Contracts::bare_instantiate( - ALICE, - 0, - GAS_LIMIT, - None, - Code::Upload(wasm), - vec![], - vec![], - DebugInfo::UnsafeDebug, - CollectEvents::Skip, - ); - assert_err!(result.result, >::CodeRejected); - assert_eq!( - std::str::from_utf8(&result.debug_message).unwrap(), - "Can't load the module into wasmi!" - ); - - let (wasm, _) = compile_module::("invalid_contract_no_call").unwrap(); - assert_noop!( - Contracts::upload_code( - RuntimeOrigin::signed(ALICE), - wasm.clone(), - None, - Determinism::Enforced - ), - >::CodeRejected, - ); - - let result = Contracts::bare_instantiate( - ALICE, - 0, - GAS_LIMIT, - None, - Code::Upload(wasm), - vec![], - vec![], - DebugInfo::UnsafeDebug, - CollectEvents::Skip, - ); - assert_err!(result.result, >::CodeRejected); - assert_eq!( - std::str::from_utf8(&result.debug_message).unwrap(), - "call function isn't exported" - ); - - let (wasm, _) = compile_module::("invalid_contract_no_memory").unwrap(); - assert_noop!( - Contracts::upload_code( - RuntimeOrigin::signed(ALICE), - wasm.clone(), - None, - Determinism::Enforced - ), - >::CodeRejected, - ); - - let result = Contracts::bare_instantiate( - ALICE, - 0, - GAS_LIMIT, - None, - Code::Upload(wasm), - vec![], - vec![], - DebugInfo::UnsafeDebug, - CollectEvents::Skip, - ); - assert_err!(result.result, >::CodeRejected); - assert_eq!( - std::str::from_utf8(&result.debug_message).unwrap(), - "No memory import found in the module" - ); - }); -} - #[test] fn set_code_hash() { let (wasm, code_hash) = compile_module::("set_code_hash").unwrap(); @@ -5154,6 +5040,26 @@ fn deposit_limit_honors_min_leftover() { }); } +#[test] +fn upload_should_enforce_deterministic_mode_when_possible() { + let upload = |fixture, determinism| { + let (wasm, code_hash) = compile_module::(fixture).unwrap(); + ExtBuilder::default() + .build() + .execute_with(|| -> Result { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + Contracts::bare_upload_code(ALICE, wasm, None, determinism)?; + let info = CodeInfoOf::::get(code_hash).unwrap(); + Ok(info.determinism()) + }) + }; + + assert_eq!(upload("dummy", Determinism::Enforced), Ok(Determinism::Enforced)); + assert_eq!(upload("dummy", Determinism::Relaxed), Ok(Determinism::Enforced)); + assert_eq!(upload("float_instruction", Determinism::Relaxed), Ok(Determinism::Relaxed)); + assert!(upload("float_instruction", Determinism::Enforced).is_err()); +} + #[test] fn cannot_instantiate_indeterministic_code() { let (wasm, code_hash) = compile_module::("float_instruction").unwrap(); @@ -5190,7 +5096,7 @@ fn cannot_instantiate_indeterministic_code() { >::CodeRejected, ); - // Try to upload a non deterministic code as deterministic + // Try to upload a non-deterministic code as deterministic assert_err!( Contracts::upload_code( RuntimeOrigin::signed(ALICE), @@ -5270,7 +5176,7 @@ fn cannot_instantiate_indeterministic_code() { >::Indeterministic, ); - // Instantiations are not allowed even in non determinism mode + // Instantiations are not allowed even in non-determinism mode assert_err!( >::bare_call( ALICE, @@ -5296,7 +5202,7 @@ fn cannot_set_code_indeterministic_code() { ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - // Put the non deterministic contract on-chain + // Put the non-deterministic contract on-chain assert_ok!(Contracts::upload_code( RuntimeOrigin::signed(ALICE), wasm, @@ -5320,7 +5226,7 @@ fn cannot_set_code_indeterministic_code() { .unwrap() .account_id; - // We do not allow to set the code hash to a non determinstic wasm + // We do not allow to set the code hash to a non-deterministic wasm assert_err!( >::bare_call( ALICE, @@ -5346,7 +5252,7 @@ fn delegate_call_indeterministic_code() { ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - // Put the non deterministic contract on-chain + // Put the non-deterministic contract on-chain assert_ok!(Contracts::upload_code( RuntimeOrigin::signed(ALICE), wasm, @@ -5387,7 +5293,7 @@ fn delegate_call_indeterministic_code() { >::Indeterministic, ); - // The delegate call will work on non deterministic mode + // The delegate call will work on non-deterministic mode assert_ok!( >::bare_call( ALICE, @@ -5523,7 +5429,7 @@ fn locking_delegate_dependency_works() { contract.storage_base_deposit() - ED ); - // Removing an unexisting dependency should fail. + // Removing a nonexistent dependency should fail. assert_err!( call(&addr_caller, &unlock_delegate_dependency_input).result, Error::::DelegateDependencyNotFound @@ -5923,7 +5829,106 @@ fn root_cannot_instantiate() { vec![], vec![], ), - DispatchError::RootNotAllowed + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn only_upload_origin_can_upload() { + let (wasm, _) = compile_module::("dummy").unwrap(); + UploadAccount::set(Some(ALICE)); + ExtBuilder::default().build().execute_with(|| { + let _ = Balances::set_balance(&ALICE, 1_000_000); + let _ = Balances::set_balance(&BOB, 1_000_000); + + assert_err!( + Contracts::upload_code( + RuntimeOrigin::root(), + wasm.clone(), + None, + Determinism::Enforced, + ), + DispatchError::BadOrigin + ); + + assert_err!( + Contracts::upload_code( + RuntimeOrigin::signed(BOB), + wasm.clone(), + None, + Determinism::Enforced, + ), + DispatchError::BadOrigin + ); + + // Only alice is allowed to upload contract code. + assert_ok!(Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + wasm.clone(), + None, + Determinism::Enforced, + )); + }); +} + +#[test] +fn only_instantiation_origin_can_instantiate() { + let (code, code_hash) = compile_module::("dummy").unwrap(); + InstantiateAccount::set(Some(ALICE)); + ExtBuilder::default().build().execute_with(|| { + let _ = Balances::set_balance(&ALICE, 1_000_000); + let _ = Balances::set_balance(&BOB, 1_000_000); + + assert_err_ignore_postinfo!( + Contracts::instantiate_with_code( + RuntimeOrigin::root(), + 0, + GAS_LIMIT, + None, + code.clone(), + vec![], + vec![], + ), + DispatchError::BadOrigin + ); + + assert_err_ignore_postinfo!( + Contracts::instantiate_with_code( + RuntimeOrigin::signed(BOB), + 0, + GAS_LIMIT, + None, + code.clone(), + vec![], + vec![], + ), + DispatchError::BadOrigin + ); + + // Only Alice can instantiate + assert_ok!(Contracts::instantiate_with_code( + RuntimeOrigin::signed(ALICE), + 0, + GAS_LIMIT, + None, + code, + vec![], + vec![], + ),); + + // Bob cannot instantiate with either `instantiate_with_code` or `instantiate`. + assert_err_ignore_postinfo!( + Contracts::instantiate( + RuntimeOrigin::signed(BOB), + 0, + GAS_LIMIT, + None, + code_hash, + vec![], + vec![], + ), + DispatchError::BadOrigin ); }); } @@ -5977,3 +5982,53 @@ fn balance_api_returns_free_balance() { ); }); } + +#[test] +fn gas_consumed_is_linear_for_nested_calls() { + let (code, _code_hash) = compile_module::("recurse").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + let addr = Contracts::bare_instantiate( + ALICE, + 0, + GAS_LIMIT, + None, + Code::Upload(code), + vec![], + vec![], + DebugInfo::Skip, + CollectEvents::Skip, + ) + .result + .unwrap() + .account_id; + + let max_call_depth = ::CallStack::size() as u32; + let [gas_0, gas_1, gas_2, gas_max] = { + [0u32, 1u32, 2u32, max_call_depth] + .iter() + .map(|i| { + let result = Contracts::bare_call( + ALICE, + addr.clone(), + 0, + GAS_LIMIT, + None, + i.encode(), + DebugInfo::Skip, + CollectEvents::Skip, + Determinism::Enforced, + ); + assert_ok!(result.result); + result.gas_consumed + }) + .collect::>() + .try_into() + .unwrap() + }; + + let gas_per_recursion = gas_2.checked_sub(&gas_1).unwrap(); + assert_eq!(gas_max, gas_0 + gas_per_recursion * max_call_depth as u64); + }); +} diff --git a/substrate/frame/contracts/src/wasm/mod.rs b/substrate/frame/contracts/src/wasm/mod.rs index 5386f4d0ffdf6bfea00c6544c5bae171e936b8f7..e6af62c72891d4cfb42a4b7d65127391e0eff9a0 100644 --- a/substrate/frame/contracts/src/wasm/mod.rs +++ b/substrate/frame/contracts/src/wasm/mod.rs @@ -21,6 +21,9 @@ mod prepare; mod runtime; +#[cfg(test)] +pub use runtime::STABLE_API_COUNT; + #[cfg(doc)] pub use crate::wasm::runtime::api_doc; @@ -313,6 +316,11 @@ impl CodeInfo { } } + /// Returns the determinism of the module. + pub fn determinism(&self) -> Determinism { + self.determinism + } + /// Returns reference count of the module. pub fn refcount(&self) -> u64 { self.refcount @@ -386,7 +394,7 @@ impl Executable for WasmBlob { let engine_consumed_total = store.fuel_consumed().expect("Fuel metering is enabled; qed"); let gas_meter = store.data_mut().ext().gas_meter_mut(); - gas_meter.charge_fuel(engine_consumed_total)?; + let _ = gas_meter.sync_from_executor(engine_consumed_total)?; store.into_data().to_execution_result(result) }; @@ -1417,7 +1425,7 @@ mod tests { #[test] fn contract_ecdsa_to_eth_address() { - /// calls `seal_ecdsa_to_eth_address` for the contstant and ensures the result equals the + /// calls `seal_ecdsa_to_eth_address` for the constant and ensures the result equals the /// expected one. const CODE_ECDSA_TO_ETH_ADDRESS: &str = r#" (module @@ -1821,17 +1829,6 @@ mod tests { assert!(weight_left.all_gt(actual_left), "gas_left must be greater than final"); } - /// Test that [`frame_support::weights::OldWeight`] en/decodes the same as our - /// [`crate::OldWeight`]. - #[test] - fn old_weight_decode() { - #![allow(deprecated)] - let sp = frame_support::weights::OldWeight(42).encode(); - let our = crate::OldWeight::decode(&mut &*sp).unwrap(); - - assert_eq!(our, 42); - } - const CODE_VALUE_TRANSFERRED: &str = r#" (module (import "seal0" "seal_value_transferred" (func $seal_value_transferred (param i32 i32))) @@ -3448,4 +3445,22 @@ mod tests { runtime.read_sandbox_memory_as(&memory, 0u32).unwrap(); assert_eq!(decoded.into_inner(), data); } + + #[test] + fn run_out_of_gas_in_start_fn() { + const CODE: &str = r#" +(module + (import "env" "memory" (memory 1 1)) + (start $start) + (func $start + (loop $inf (br $inf)) ;; just run out of gas + (unreachable) + ) + (func (export "call")) + (func (export "deploy")) +) +"#; + let mut mock_ext = MockExt::default(); + assert_err!(execute(&CODE, vec![], &mut mock_ext), >::OutOfGas); + } } diff --git a/substrate/frame/contracts/src/wasm/prepare.rs b/substrate/frame/contracts/src/wasm/prepare.rs index 5cdf5600fcdc6d08b55cd68685a532494b4583a7..5efea8c3a23b319ff86fa38089ef04e1dcdc6681 100644 --- a/substrate/frame/contracts/src/wasm/prepare.rs +++ b/substrate/frame/contracts/src/wasm/prepare.rs @@ -79,7 +79,10 @@ impl LoadedModule { } let engine = Engine::new(&config); - let module = Module::new(&engine, code).map_err(|_| "Can't load the module into wasmi!")?; + let module = Module::new(&engine, code).map_err(|err| { + log::debug!(target: LOG_TARGET, "Module creation failed: {:?}", err); + "Can't load the module into wasmi!" + })?; // Return a `LoadedModule` instance with // __valid__ module. @@ -220,7 +223,7 @@ impl LoadedModule { fn validate( code: &[u8], schedule: &Schedule, - determinism: Determinism, + determinism: &mut Determinism, ) -> Result<(), (DispatchError, &'static str)> where E: Environment<()>, @@ -229,7 +232,17 @@ where (|| { // We check that the module is generally valid, // and does not have restricted WebAssembly features, here. - let contract_module = LoadedModule::new::(code, determinism, None)?; + let contract_module = match *determinism { + Determinism::Relaxed => + if let Ok(module) = LoadedModule::new::(code, Determinism::Enforced, None) { + *determinism = Determinism::Enforced; + module + } else { + LoadedModule::new::(code, Determinism::Relaxed, None)? + }, + Determinism::Enforced => LoadedModule::new::(code, Determinism::Enforced, None)?, + }; + // The we check that module satisfies constraints the pallet puts on contracts. contract_module.scan_exports()?; contract_module.scan_imports::(schedule)?; @@ -252,7 +265,7 @@ where &code, (), schedule, - determinism, + *determinism, stack_limits, AllowDeprecatedInterface::No, ) @@ -276,13 +289,13 @@ pub fn prepare( code: CodeVec, schedule: &Schedule, owner: AccountIdOf, - determinism: Determinism, + mut determinism: Determinism, ) -> Result, (DispatchError, &'static str)> where E: Environment<()>, T: Config, { - validate::(code.as_ref(), schedule, determinism)?; + validate::(code.as_ref(), schedule, &mut determinism)?; // Calculate deposit for storing contract code and `code_info` in two different storage items. let code_len = code.len() as u32; diff --git a/substrate/frame/contracts/src/wasm/runtime.rs b/substrate/frame/contracts/src/wasm/runtime.rs index f440c818166d865a3b884eea03107c663a985046..28a08ab0224ddf4603c881b50c9359d4a6f54d46 100644 --- a/substrate/frame/contracts/src/wasm/runtime.rs +++ b/substrate/frame/contracts/src/wasm/runtime.rs @@ -25,12 +25,8 @@ use crate::{ }; use codec::{Decode, DecodeLimit, Encode, MaxEncodedLen}; use frame_support::{ - dispatch::DispatchInfo, - ensure, - pallet_prelude::{DispatchResult, DispatchResultWithPostInfo}, - parameter_types, - traits::Get, - weights::Weight, + dispatch::DispatchInfo, ensure, pallet_prelude::DispatchResultWithPostInfo, parameter_types, + traits::Get, weights::Weight, }; use pallet_contracts_proc_macro::define_env; use pallet_contracts_uapi::{CallFlags, ReturnFlags}; @@ -41,9 +37,6 @@ use sp_runtime::{ }; use sp_std::{fmt, prelude::*}; use wasmi::{core::HostError, errors::LinkerError, Linker, Memory, Store}; -use xcm::VersionedXcm; - -type CallOf = ::RuntimeCall; /// The maximum nesting depth a contract can use when encoding types. const MAX_DECODE_NESTING: u32 = 256; @@ -245,7 +238,7 @@ pub enum RuntimeCosts { /// Weight of calling `account_reentrance_count` AccountEntranceCount, /// Weight of calling `instantiation_nonce` - InstantationNonce, + InstantiationNonce, /// Weight of calling `lock_delegate_dependency` LockDelegateDependency, /// Weight of calling `unlock_delegate_dependency` @@ -337,7 +330,7 @@ impl Token for RuntimeCosts { EcdsaToEthAddress => s.ecdsa_to_eth_address, ReentrantCount => s.reentrance_count, AccountEntranceCount => s.account_reentrance_count, - InstantationNonce => s.instantiation_nonce, + InstantiationNonce => s.instantiation_nonce, LockDelegateDependency => s.lock_delegate_dependency, UnlockDelegateDependency => s.unlock_delegate_dependency, } @@ -378,29 +371,6 @@ fn already_charged(_: u32) -> Option { None } -/// Ensure that the XCM program is executable, by checking that it does not contain any [`Transact`] -/// instruction with a call that is not allowed by the CallFilter. -fn ensure_executable(message: &VersionedXcm>) -> DispatchResult { - use frame_support::traits::Contains; - use xcm::prelude::{Transact, Xcm}; - - let mut message: Xcm> = - message.clone().try_into().map_err(|_| Error::::XCMDecodeFailed)?; - - message.iter_mut().try_for_each(|inst| -> DispatchResult { - let Transact { ref mut call, .. } = inst else { return Ok(()) }; - let call = call.ensure_decoded().map_err(|_| Error::::XCMDecodeFailed)?; - - if !::CallFilter::contains(call) { - return Err(frame_system::Error::::CallFiltered.into()) - } - - Ok(()) - })?; - - Ok(()) -} - /// Can only be used for one call. pub struct Runtime<'a, E: Ext + 'a> { ext: &'a mut E, @@ -987,7 +957,6 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { // for every function. #[define_env(doc)] pub mod env { - /// Set the value at the given key in the contract storage. /// See [`pallet_contracts_uapi::HostFn::set_storage`] #[prefixed_alias] @@ -2113,16 +2082,13 @@ pub mod env { msg_len: u32, ) -> Result { use frame_support::dispatch::DispatchInfo; - use xcm::VersionedXcm; use xcm_builder::{ExecuteController, ExecuteControllerWeightInfo}; ctx.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?; - let message: VersionedXcm> = - ctx.read_sandbox_memory_as_unbounded(memory, msg_ptr, msg_len)?; - ensure_executable::(&message)?; + let message = ctx.read_sandbox_memory_as_unbounded(memory, msg_ptr, msg_len)?; let execute_weight = - <::Xcm as ExecuteController<_, _>>::WeightInfo::execute(); + <::Xcm as ExecuteController<_, _>>::WeightInfo::execute_blob(); let weight = ctx.ext.gas_meter().gas_left().max(execute_weight); let dispatch_info = DispatchInfo { weight, ..Default::default() }; @@ -2131,9 +2097,9 @@ pub mod env { RuntimeCosts::CallXcmExecute, |ctx| { let origin = crate::RawOrigin::Signed(ctx.ext.address().clone()).into(); - let weight_used = <::Xcm>::execute( + let weight_used = <::Xcm>::execute_blob( origin, - Box::new(message), + message, weight.saturating_sub(execute_weight), )?; @@ -2153,19 +2119,18 @@ pub mod env { msg_len: u32, output_ptr: u32, ) -> Result { - use xcm::{VersionedLocation, VersionedXcm}; + use xcm::VersionedLocation; use xcm_builder::{SendController, SendControllerWeightInfo}; ctx.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?; let dest: VersionedLocation = ctx.read_sandbox_memory_as(memory, dest_ptr)?; - let message: VersionedXcm<()> = - ctx.read_sandbox_memory_as_unbounded(memory, msg_ptr, msg_len)?; - let weight = <::Xcm as SendController<_>>::WeightInfo::send(); + let message = ctx.read_sandbox_memory_as_unbounded(memory, msg_ptr, msg_len)?; + let weight = <::Xcm as SendController<_>>::WeightInfo::send_blob(); ctx.charge_gas(RuntimeCosts::CallRuntime(weight))?; let origin = crate::RawOrigin::Signed(ctx.ext.address().clone()).into(); - match <::Xcm>::send(origin, dest.into(), message.into()) { + match <::Xcm>::send_blob(origin, dest.into(), message) { Ok(message_id) => { ctx.write_sandbox_memory(memory, output_ptr, &message_id.encode())?; Ok(ReturnErrorCode::Success) @@ -2299,13 +2264,12 @@ pub mod env { /// Returns a nonce that is unique per contract instantiation. /// See [`pallet_contracts_uapi::HostFn::instantiation_nonce`]. fn instantiation_nonce(ctx: _, _memory: _) -> Result { - ctx.charge_gas(RuntimeCosts::InstantationNonce)?; + ctx.charge_gas(RuntimeCosts::InstantiationNonce)?; Ok(ctx.ext.nonce()) } /// Adds a new delegate dependency to the contract. /// See [`pallet_contracts_uapi::HostFn::lock_delegate_dependency`]. - #[unstable] fn lock_delegate_dependency(ctx: _, memory: _, code_hash_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::LockDelegateDependency)?; let code_hash = ctx.read_sandbox_memory_as(memory, code_hash_ptr)?; @@ -2315,7 +2279,6 @@ pub mod env { /// Removes the delegate dependency from the contract. /// see [`pallet_contracts_uapi::HostFn::unlock_delegate_dependency`]. - #[unstable] fn unlock_delegate_dependency(ctx: _, memory: _, code_hash_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::UnlockDelegateDependency)?; let code_hash = ctx.read_sandbox_memory_as(memory, code_hash_ptr)?; diff --git a/substrate/frame/contracts/src/weights.rs b/substrate/frame/contracts/src/weights.rs index 962591290b3149d6b72ea737871b98f38f5c07a3..aa75ca8b29e36bcdf0d6f4bc17678e7d63b16176 100644 --- a/substrate/frame/contracts/src/weights.rs +++ b/substrate/frame/contracts/src/weights.rs @@ -17,10 +17,10 @@ //! Autogenerated weights for `pallet_contracts` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-01-19, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-19, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-j8vvqcjr-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: @@ -67,7 +67,8 @@ pub trait WeightInfo { fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight; fn instantiate(i: u32, s: u32, ) -> Weight; fn call() -> Weight; - fn upload_code(c: u32, ) -> Weight; + fn upload_code_determinism_enforced(c: u32, ) -> Weight; + fn upload_code_determinism_relaxed(c: u32, ) -> Weight; fn remove_code() -> Weight; fn set_code() -> Weight; fn seal_caller(r: u32, ) -> Weight; @@ -141,8 +142,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `142` // Estimated: `1627` - // Minimum execution time: 1_997_000 picoseconds. - Weight::from_parts(2_130_000, 1627) + // Minimum execution time: 2_130_000 picoseconds. + Weight::from_parts(2_247_000, 1627) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -152,10 +153,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `452 + k * (69 ±0)` // Estimated: `442 + k * (70 ±0)` - // Minimum execution time: 12_276_000 picoseconds. - Weight::from_parts(1_593_881, 442) - // Standard Error: 1_135 - .saturating_add(Weight::from_parts(1_109_302, 0).saturating_mul(k.into())) + // Minimum execution time: 12_748_000 picoseconds. + Weight::from_parts(13_001_000, 442) + // Standard Error: 1_206 + .saturating_add(Weight::from_parts(1_146_159, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -169,10 +170,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `211 + c * (1 ±0)` // Estimated: `6149 + c * (1 ±0)` - // Minimum execution time: 8_176_000 picoseconds. - Weight::from_parts(8_555_388, 6149) + // Minimum execution time: 8_636_000 picoseconds. + Weight::from_parts(8_664_917, 6149) // Standard Error: 1 - .saturating_add(Weight::from_parts(1_184, 0).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(1_188, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) @@ -185,8 +186,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `510` // Estimated: `6450` - // Minimum execution time: 16_270_000 picoseconds. - Weight::from_parts(16_779_000, 6450) + // Minimum execution time: 16_838_000 picoseconds. + Weight::from_parts(17_400_000, 6450) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -199,10 +200,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `171 + k * (1 ±0)` // Estimated: `3635 + k * (1 ±0)` - // Minimum execution time: 3_572_000 picoseconds. - Weight::from_parts(1_950_905, 3635) - // Standard Error: 1_597 - .saturating_add(Weight::from_parts(1_123_190, 0).saturating_mul(k.into())) + // Minimum execution time: 3_876_000 picoseconds. + Weight::from_parts(2_654_935, 3635) + // Standard Error: 2_001 + .saturating_add(Weight::from_parts(1_258_085, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -212,6 +213,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553053f13fd319a03c211337c76e0fe776df` (r:2 w:0) /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:1 w:1) /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:1 w:1) + /// Storage: `Parameters::Parameters` (r:2 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:0 w:1) @@ -219,13 +222,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 125952]`. fn v12_migration_step(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `325 + c * (1 ±0)` - // Estimated: `6263 + c * (1 ±0)` - // Minimum execution time: 16_873_000 picoseconds. - Weight::from_parts(16_790_402, 6263) + // Measured: `328 + c * (1 ±0)` + // Estimated: `6266 + c * (1 ±0)` + // Minimum execution time: 21_038_000 picoseconds. + Weight::from_parts(20_890_548, 6266) // Standard Error: 1 - .saturating_add(Weight::from_parts(396, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(Weight::from_parts(435, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) } @@ -235,8 +238,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `440` // Estimated: `6380` - // Minimum execution time: 11_904_000 picoseconds. - Weight::from_parts(12_785_000, 6380) + // Minimum execution time: 12_579_000 picoseconds. + Weight::from_parts(13_486_000, 6380) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -245,13 +248,13 @@ impl WeightInfo for SubstrateWeight { /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:0) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) fn v14_migration_step() -> Weight { // Proof Size summary in bytes: // Measured: `352` // Estimated: `6292` - // Minimum execution time: 44_920_000 picoseconds. - Weight::from_parts(46_163_000, 6292) + // Minimum execution time: 47_123_000 picoseconds. + Weight::from_parts(48_284_000, 6292) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -263,8 +266,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `594` // Estimated: `6534` - // Minimum execution time: 53_864_000 picoseconds. - Weight::from_parts(55_139_000, 6534) + // Minimum execution time: 55_237_000 picoseconds. + Weight::from_parts(57_996_000, 6534) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -274,8 +277,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `142` // Estimated: `1627` - // Minimum execution time: 2_375_000 picoseconds. - Weight::from_parts(2_487_000, 1627) + // Minimum execution time: 2_766_000 picoseconds. + Weight::from_parts(2_807_000, 1627) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -287,8 +290,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `166` // Estimated: `3631` - // Minimum execution time: 11_580_000 picoseconds. - Weight::from_parts(11_980_000, 3631) + // Minimum execution time: 12_243_000 picoseconds. + Weight::from_parts(12_890_000, 3631) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -298,8 +301,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `142` // Estimated: `3607` - // Minimum execution time: 4_557_000 picoseconds. - Weight::from_parts(4_807_000, 3607) + // Minimum execution time: 4_951_000 picoseconds. + Weight::from_parts(5_232_000, 3607) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) @@ -310,8 +313,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `167` // Estimated: `3632` - // Minimum execution time: 6_253_000 picoseconds. - Weight::from_parts(6_479_000, 3632) + // Minimum execution time: 6_530_000 picoseconds. + Weight::from_parts(6_726_000, 3632) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) @@ -322,13 +325,15 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `142` // Estimated: `3607` - // Minimum execution time: 6_166_000 picoseconds. - Weight::from_parts(6_545_000, 3607) + // Minimum execution time: 6_227_000 picoseconds. + Weight::from_parts(6_708_000, 3607) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -344,22 +349,24 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 125952]`. fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `801 + c * (1 ±0)` - // Estimated: `6739 + c * (1 ±0)` - // Minimum execution time: 282_232_000 picoseconds. - Weight::from_parts(266_148_573, 6739) - // Standard Error: 69 - .saturating_add(Weight::from_parts(34_592, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `804 + c * (1 ±0)` + // Estimated: `9217 + c * (1 ±0)` + // Minimum execution time: 309_889_000 picoseconds. + Weight::from_parts(277_084_159, 9217) + // Standard Error: 71 + .saturating_add(Weight::from_parts(33_471, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) /// Storage: `Balances::Holds` (r:2 w:2) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// Storage: `System::EventTopics` (r:3 w:3) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Contracts::Nonce` (r:1 w:1) @@ -377,17 +384,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 1048576]`. fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `323` - // Estimated: `8737` - // Minimum execution time: 3_760_879_000 picoseconds. - Weight::from_parts(794_812_431, 8737) - // Standard Error: 149 - .saturating_add(Weight::from_parts(101_881, 0).saturating_mul(c.into())) - // Standard Error: 18 - .saturating_add(Weight::from_parts(1_404, 0).saturating_mul(i.into())) - // Standard Error: 18 - .saturating_add(Weight::from_parts(1_544, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(11_u64)) + // Measured: `326` + // Estimated: `8740` + // Minimum execution time: 3_909_680_000 picoseconds. + Weight::from_parts(446_471_160, 8740) + // Standard Error: 159 + .saturating_add(Weight::from_parts(101_085, 0).saturating_mul(c.into())) + // Standard Error: 19 + .saturating_add(Weight::from_parts(1_598, 0).saturating_mul(i.into())) + // Standard Error: 19 + .saturating_add(Weight::from_parts(1_879, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(14_u64)) .saturating_add(T::DbWeight::get().writes(10_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) @@ -396,6 +403,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) /// Storage: `Contracts::PristineCode` (r:1 w:0) /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::Nonce` (r:1 w:1) /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) @@ -407,24 +416,26 @@ impl WeightInfo for SubstrateWeight { /// Storage: `System::EventTopics` (r:2 w:2) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// The range of component `i` is `[0, 1048576]`. /// The range of component `s` is `[0, 1048576]`. fn instantiate(i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `560` - // Estimated: `6504` - // Minimum execution time: 1_953_162_000 picoseconds. - Weight::from_parts(374_252_840, 6504) - // Standard Error: 7 - .saturating_add(Weight::from_parts(1_630, 0).saturating_mul(i.into())) - // Standard Error: 7 - .saturating_add(Weight::from_parts(1_650, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(10_u64)) + // Measured: `563` + // Estimated: `8982` + // Minimum execution time: 1_968_545_000 picoseconds. + Weight::from_parts(420_048_028, 8982) + // Standard Error: 20 + .saturating_add(Weight::from_parts(1_685, 0).saturating_mul(i.into())) + // Standard Error: 20 + .saturating_add(Weight::from_parts(1_645, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -439,33 +450,59 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `826` - // Estimated: `6766` - // Minimum execution time: 187_899_000 picoseconds. - Weight::from_parts(195_510_000, 6766) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `829` + // Estimated: `9244` + // Minimum execution time: 207_564_000 picoseconds. + Weight::from_parts(216_983_000, 9244) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:2 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// Storage: `System::EventTopics` (r:1 w:1) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Contracts::PristineCode` (r:0 w:1) /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) /// The range of component `c` is `[0, 125952]`. - fn upload_code(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 254_800_000 picoseconds. - Weight::from_parts(285_603_050, 3607) - // Standard Error: 62 - .saturating_add(Weight::from_parts(66_212, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) + fn upload_code_determinism_enforced(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `6085` + // Minimum execution time: 273_555_000 picoseconds. + Weight::from_parts(257_517_935, 6085) + // Standard Error: 148 + .saturating_add(Weight::from_parts(64_488, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:2 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// Storage: `System::EventTopics` (r:1 w:1) + /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:0 w:1) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + fn upload_code_determinism_relaxed(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `6085` + // Minimum execution time: 289_672_000 picoseconds. + Weight::from_parts(297_020_278, 6085) + // Standard Error: 86 + .saturating_add(Weight::from_parts(64_340, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) @@ -473,7 +510,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// Storage: `System::EventTopics` (r:1 w:1) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Contracts::PristineCode` (r:0 w:1) @@ -482,8 +519,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `315` // Estimated: `3780` - // Minimum execution time: 43_553_000 picoseconds. - Weight::from_parts(45_036_000, 3780) + // Minimum execution time: 45_930_000 picoseconds. + Weight::from_parts(47_288_000, 3780) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -499,8 +536,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `552` // Estimated: `8967` - // Minimum execution time: 33_223_000 picoseconds. - Weight::from_parts(34_385_000, 8967) + // Minimum execution time: 35_421_000 picoseconds. + Weight::from_parts(36_909_000, 8967) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -508,6 +545,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -521,13 +560,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_caller(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `866 + r * (6 ±0)` - // Estimated: `6806 + r * (6 ±0)` - // Minimum execution time: 254_213_000 picoseconds. - Weight::from_parts(273_464_980, 6806) - // Standard Error: 1_362 - .saturating_add(Weight::from_parts(322_619, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `869 + r * (6 ±0)` + // Estimated: `9284 + r * (6 ±0)` + // Minimum execution time: 275_531_000 picoseconds. + Weight::from_parts(292_269_656, 9284) + // Standard Error: 672 + .saturating_add(Weight::from_parts(339_728, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -535,6 +574,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1601 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -548,13 +589,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_is_contract(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `922 + r * (209 ±0)` - // Estimated: `6826 + r * (2684 ±0)` - // Minimum execution time: 250_273_000 picoseconds. - Weight::from_parts(122_072_782, 6826) - // Standard Error: 5_629 - .saturating_add(Weight::from_parts(3_490_256, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `925 + r * (209 ±0)` + // Estimated: `9304 + r * (2684 ±0)` + // Minimum execution time: 275_829_000 picoseconds. + Weight::from_parts(124_543_289, 9304) + // Standard Error: 6_085 + .saturating_add(Weight::from_parts(3_702_964, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2684).saturating_mul(r.into())) @@ -563,6 +604,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1601 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -576,13 +619,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (213 ±0)` - // Estimated: `6830 + r * (2688 ±0)` - // Minimum execution time: 255_187_000 picoseconds. - Weight::from_parts(118_082_505, 6830) - // Standard Error: 6_302 - .saturating_add(Weight::from_parts(4_246_968, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `924 + r * (213 ±0)` + // Estimated: `9308 + r * (2688 ±0)` + // Minimum execution time: 276_666_000 picoseconds. + Weight::from_parts(96_951_288, 9308) + // Standard Error: 8_876 + .saturating_add(Weight::from_parts(4_604_699, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2688).saturating_mul(r.into())) @@ -591,6 +634,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -604,13 +649,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_own_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `873 + r * (6 ±0)` - // Estimated: `6815 + r * (6 ±0)` - // Minimum execution time: 256_833_000 picoseconds. - Weight::from_parts(273_330_216, 6815) - // Standard Error: 881 - .saturating_add(Weight::from_parts(400_105, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `876 + r * (6 ±0)` + // Estimated: `9293 + r * (6 ±0)` + // Minimum execution time: 271_301_000 picoseconds. + Weight::from_parts(284_126_054, 9293) + // Standard Error: 886 + .saturating_add(Weight::from_parts(437_127, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -618,6 +663,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -631,18 +678,20 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_caller_is_origin(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863 + r * (3 ±0)` - // Estimated: `6804 + r * (3 ±0)` - // Minimum execution time: 244_193_000 picoseconds. - Weight::from_parts(271_221_908, 6804) - // Standard Error: 442 - .saturating_add(Weight::from_parts(176_480, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `866 + r * (3 ±0)` + // Estimated: `9282 + r * (3 ±0)` + // Minimum execution time: 274_778_000 picoseconds. + Weight::from_parts(289_355_269, 9282) + // Standard Error: 382 + .saturating_add(Weight::from_parts(175_342, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -656,13 +705,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_caller_is_root(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `753 + r * (3 ±0)` - // Estimated: `6693 + r * (3 ±0)` - // Minimum execution time: 232_603_000 picoseconds. - Weight::from_parts(260_577_368, 6693) - // Standard Error: 365 - .saturating_add(Weight::from_parts(158_126, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(7_u64)) + // Measured: `756 + r * (3 ±0)` + // Estimated: `9171 + r * (3 ±0)` + // Minimum execution time: 257_310_000 picoseconds. + Weight::from_parts(276_410_847, 9171) + // Standard Error: 733 + .saturating_add(Weight::from_parts(160_094, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } @@ -670,6 +719,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -683,13 +734,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `867 + r * (6 ±0)` - // Estimated: `6807 + r * (6 ±0)` - // Minimum execution time: 247_564_000 picoseconds. - Weight::from_parts(275_108_914, 6807) - // Standard Error: 505 - .saturating_add(Weight::from_parts(315_065, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `870 + r * (6 ±0)` + // Estimated: `9285 + r * (6 ±0)` + // Minimum execution time: 278_305_000 picoseconds. + Weight::from_parts(282_372_935, 9285) + // Standard Error: 1_154 + .saturating_add(Weight::from_parts(343_382, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -697,6 +748,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -710,13 +763,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_gas_left(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863 + r * (6 ±0)` - // Estimated: `6806 + r * (6 ±0)` - // Minimum execution time: 258_799_000 picoseconds. - Weight::from_parts(274_338_256, 6806) - // Standard Error: 632 - .saturating_add(Weight::from_parts(355_032, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `866 + r * (6 ±0)` + // Estimated: `9284 + r * (6 ±0)` + // Minimum execution time: 280_349_000 picoseconds. + Weight::from_parts(294_864_875, 9284) + // Standard Error: 1_010 + .saturating_add(Weight::from_parts(368_740, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -724,6 +777,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:2 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -737,13 +792,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1007 + r * (6 ±0)` - // Estimated: `6931 + r * (6 ±0)` - // Minimum execution time: 253_335_000 picoseconds. - Weight::from_parts(273_013_859, 6931) - // Standard Error: 2_007 - .saturating_add(Weight::from_parts(1_540_735, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `1010 + r * (6 ±0)` + // Estimated: `9409 + r * (6 ±0)` + // Minimum execution time: 274_578_000 picoseconds. + Weight::from_parts(313_285_034, 9409) + // Standard Error: 2_800 + .saturating_add(Weight::from_parts(1_653_468, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -751,6 +806,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -764,13 +821,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_value_transferred(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `877 + r * (6 ±0)` - // Estimated: `6823 + r * (6 ±0)` - // Minimum execution time: 252_325_000 picoseconds. - Weight::from_parts(274_733_944, 6823) - // Standard Error: 603 - .saturating_add(Weight::from_parts(314_467, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `880 + r * (6 ±0)` + // Estimated: `9301 + r * (6 ±0)` + // Minimum execution time: 287_127_000 picoseconds. + Weight::from_parts(290_489_909, 9301) + // Standard Error: 864 + .saturating_add(Weight::from_parts(332_977, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -778,6 +835,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -791,13 +850,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_minimum_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `875 + r * (6 ±0)` - // Estimated: `6816 + r * (6 ±0)` - // Minimum execution time: 250_698_000 picoseconds. - Weight::from_parts(271_707_578, 6816) - // Standard Error: 952 - .saturating_add(Weight::from_parts(318_412, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `878 + r * (6 ±0)` + // Estimated: `9294 + r * (6 ±0)` + // Minimum execution time: 273_291_000 picoseconds. + Weight::from_parts(293_650_716, 9294) + // Standard Error: 725 + .saturating_add(Weight::from_parts(323_281, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -805,6 +864,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -818,13 +879,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_block_number(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872 + r * (6 ±0)` - // Estimated: `6819 + r * (6 ±0)` - // Minimum execution time: 251_854_000 picoseconds. - Weight::from_parts(272_002_212, 6819) - // Standard Error: 622 - .saturating_add(Weight::from_parts(313_353, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `875 + r * (6 ±0)` + // Estimated: `9297 + r * (6 ±0)` + // Minimum execution time: 282_061_000 picoseconds. + Weight::from_parts(291_729_751, 9297) + // Standard Error: 929 + .saturating_add(Weight::from_parts(324_683, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -832,6 +893,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -845,13 +908,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_now(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863 + r * (6 ±0)` - // Estimated: `6804 + r * (6 ±0)` - // Minimum execution time: 252_010_000 picoseconds. - Weight::from_parts(270_387_000, 6804) - // Standard Error: 659 - .saturating_add(Weight::from_parts(325_856, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `866 + r * (6 ±0)` + // Estimated: `9282 + r * (6 ±0)` + // Minimum execution time: 264_505_000 picoseconds. + Weight::from_parts(293_440_286, 9282) + // Standard Error: 704 + .saturating_add(Weight::from_parts(329_851, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -859,6 +922,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -874,13 +939,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_weight_to_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `937 + r * (14 ±0)` - // Estimated: `6872 + r * (14 ±0)` - // Minimum execution time: 247_933_000 picoseconds. - Weight::from_parts(281_550_162, 6872) - // Standard Error: 660 - .saturating_add(Weight::from_parts(1_090_869, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `940 + r * (14 ±0)` + // Estimated: `9350 + r * (14 ±0)` + // Minimum execution time: 277_208_000 picoseconds. + Weight::from_parts(304_294_691, 9350) + // Standard Error: 1_083 + .saturating_add(Weight::from_parts(824_245, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 14).saturating_mul(r.into())) } @@ -888,6 +953,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -901,13 +968,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_input(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `865 + r * (6 ±0)` - // Estimated: `6807 + r * (6 ±0)` - // Minimum execution time: 251_158_000 picoseconds. - Weight::from_parts(274_623_152, 6807) - // Standard Error: 491 - .saturating_add(Weight::from_parts(263_916, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `868 + r * (6 ±0)` + // Estimated: `9285 + r * (6 ±0)` + // Minimum execution time: 278_293_000 picoseconds. + Weight::from_parts(289_743_005, 9285) + // Standard Error: 672 + .saturating_add(Weight::from_parts(267_553, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -915,6 +982,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -928,19 +997,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1048576]`. fn seal_input_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `869` - // Estimated: `6809` - // Minimum execution time: 263_205_000 picoseconds. - Weight::from_parts(216_792_893, 6809) + // Measured: `872` + // Estimated: `9287` + // Minimum execution time: 279_495_000 picoseconds. + Weight::from_parts(232_736_994, 9287) // Standard Error: 23 - .saturating_add(Weight::from_parts(989, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(Weight::from_parts(1_008, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -954,11 +1025,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_return(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `853 + r * (45 ±0)` - // Estimated: `6793 + r * (45 ±0)` - // Minimum execution time: 239_663_000 picoseconds. - Weight::from_parts(266_124_565, 6793) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `856 + r * (45 ±0)` + // Estimated: `9271 + r * (45 ±0)` + // Minimum execution time: 257_920_000 picoseconds. + Weight::from_parts(282_276_265, 9271) + // Standard Error: 948_490 + .saturating_add(Weight::from_parts(2_408_134, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 45).saturating_mul(r.into())) } @@ -966,6 +1039,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -979,22 +1054,24 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1048576]`. fn seal_return_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863` - // Estimated: `6810` - // Minimum execution time: 241_763_000 picoseconds. - Weight::from_parts(266_535_552, 6810) + // Measured: `866` + // Estimated: `9288` + // Minimum execution time: 278_149_000 picoseconds. + Weight::from_parts(287_020_337, 9288) // Standard Error: 0 - .saturating_add(Weight::from_parts(320, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(Weight::from_parts(321, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:3 w:3) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:1 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:2 w:2) + /// Storage: `Contracts::CodeInfoOf` (r:33 w:33) /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) /// Storage: `Contracts::PristineCode` (r:1 w:0) /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) @@ -1005,28 +1082,30 @@ impl WeightInfo for SubstrateWeight { /// Storage: `System::EventTopics` (r:4 w:4) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// Storage: `Contracts::DeletionQueue` (r:0 w:1) /// Proof: `Contracts::DeletionQueue` (`max_values`: None, `max_size`: Some(142), added: 2617, mode: `Measured`) /// The range of component `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2972 + r * (316 ±0)` - // Estimated: `8912 + r * (5266 ±0)` - // Minimum execution time: 265_888_000 picoseconds. - Weight::from_parts(291_232_232, 8912) - // Standard Error: 845_475 - .saturating_add(Weight::from_parts(104_398_867, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) - .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(r.into()))) + // Measured: `4805 + r * (2121 ±0)` + // Estimated: `13220 + r * (81321 ±0)` + // Minimum execution time: 307_763_000 picoseconds. + Weight::from_parts(323_648_618, 13220) + // Standard Error: 879_890 + .saturating_add(Weight::from_parts(249_045_481, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) + .saturating_add(T::DbWeight::get().reads((36_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(T::DbWeight::get().writes((10_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 5266).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().writes((41_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 81321).saturating_mul(r.into())) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1042,13 +1121,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_random(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `944 + r * (10 ±0)` - // Estimated: `6885 + r * (10 ±0)` - // Minimum execution time: 248_500_000 picoseconds. - Weight::from_parts(282_353_053, 6885) - // Standard Error: 1_144 - .saturating_add(Weight::from_parts(1_193_841, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `947 + r * (10 ±0)` + // Estimated: `9363 + r * (10 ±0)` + // Minimum execution time: 278_400_000 picoseconds. + Weight::from_parts(293_743_000, 9363) + // Standard Error: 1_686 + .saturating_add(Weight::from_parts(1_288_603, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) } @@ -1056,6 +1135,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1069,13 +1150,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_deposit_event(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863 + r * (10 ±0)` - // Estimated: `6805 + r * (10 ±0)` - // Minimum execution time: 248_130_000 picoseconds. - Weight::from_parts(279_583_178, 6805) - // Standard Error: 971 - .saturating_add(Weight::from_parts(1_987_941, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `866 + r * (10 ±0)` + // Estimated: `9283 + r * (10 ±0)` + // Minimum execution time: 272_110_000 picoseconds. + Weight::from_parts(295_620_726, 9283) + // Standard Error: 5_481 + .saturating_add(Weight::from_parts(2_031_955, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) } @@ -1083,6 +1164,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1097,15 +1180,15 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_deposit_event_per_topic_and_byte(t: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `880 + t * (32 ±0)` - // Estimated: `6825 + t * (2508 ±0)` - // Minimum execution time: 258_594_000 picoseconds. - Weight::from_parts(276_734_422, 6825) - // Standard Error: 102_093 - .saturating_add(Weight::from_parts(2_559_383, 0).saturating_mul(t.into())) - // Standard Error: 28 - .saturating_add(Weight::from_parts(501, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `883 + t * (32 ±0)` + // Estimated: `9303 + t * (2508 ±0)` + // Minimum execution time: 277_409_000 picoseconds. + Weight::from_parts(293_838_037, 9303) + // Standard Error: 87_977 + .saturating_add(Weight::from_parts(2_911_340, 0).saturating_mul(t.into())) + // Standard Error: 24 + .saturating_add(Weight::from_parts(531, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) @@ -1115,6 +1198,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1128,13 +1213,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_debug_message(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `862 + r * (7 ±0)` - // Estimated: `6807 + r * (7 ±0)` - // Minimum execution time: 154_564_000 picoseconds. - Weight::from_parts(168_931_365, 6807) - // Standard Error: 349 - .saturating_add(Weight::from_parts(226_848, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `865 + r * (7 ±0)` + // Estimated: `9285 + r * (7 ±0)` + // Minimum execution time: 172_634_000 picoseconds. + Weight::from_parts(183_322_840, 9285) + // Standard Error: 384 + .saturating_add(Weight::from_parts(226_007, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 7).saturating_mul(r.into())) } @@ -1142,6 +1227,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `MaxEncodedLen`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1155,13 +1242,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 1048576]`. fn seal_debug_message_per_byte(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `125813` - // Estimated: `131755` - // Minimum execution time: 394_382_000 picoseconds. - Weight::from_parts(376_780_500, 131755) + // Measured: `125816` + // Estimated: `131758` + // Minimum execution time: 425_713_000 picoseconds. + Weight::from_parts(394_260_924, 131758) // Standard Error: 12 - .saturating_add(Weight::from_parts(1_026, 0).saturating_mul(i.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(Weight::from_parts(1_032, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -1169,13 +1256,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 800]`. fn seal_set_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `924 + r * (292 ±0)` - // Estimated: `926 + r * (293 ±0)` - // Minimum execution time: 249_757_000 picoseconds. - Weight::from_parts(177_324_374, 926) - // Standard Error: 9_512 - .saturating_add(Weight::from_parts(6_176_717, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `927 + r * (292 ±0)` + // Estimated: `929 + r * (293 ±0)` + // Minimum execution time: 268_128_000 picoseconds. + Weight::from_parts(186_787_113, 929) + // Standard Error: 10_796 + .saturating_add(Weight::from_parts(6_454_780, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -1186,13 +1273,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_set_storage_per_new_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1447` - // Estimated: `1430` - // Minimum execution time: 267_564_000 picoseconds. - Weight::from_parts(328_701_080, 1430) - // Standard Error: 61 - .saturating_add(Weight::from_parts(576, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(12_u64)) + // Measured: `1450` + // Estimated: `1433` + // Minimum execution time: 286_565_000 picoseconds. + Weight::from_parts(349_504_932, 1433) + // Standard Error: 70 + .saturating_add(Weight::from_parts(530, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(15_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -1200,13 +1287,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_set_storage_per_old_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1253 + n * (1 ±0)` - // Estimated: `1253 + n * (1 ±0)` - // Minimum execution time: 266_347_000 picoseconds. - Weight::from_parts(289_824_718, 1253) + // Measured: `1256 + n * (1 ±0)` + // Estimated: `1256 + n * (1 ±0)` + // Minimum execution time: 282_478_000 picoseconds. + Weight::from_parts(303_448_260, 1256) // Standard Error: 34 - .saturating_add(Weight::from_parts(184, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(Weight::from_parts(712, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1215,13 +1302,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 800]`. fn seal_clear_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (288 ±0)` - // Estimated: `927 + r * (289 ±0)` - // Minimum execution time: 247_207_000 picoseconds. - Weight::from_parts(179_856_075, 927) - // Standard Error: 9_383 - .saturating_add(Weight::from_parts(6_053_198, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `924 + r * (288 ±0)` + // Estimated: `930 + r * (289 ±0)` + // Minimum execution time: 271_793_000 picoseconds. + Weight::from_parts(179_158_648, 930) + // Standard Error: 11_868 + .saturating_add(Weight::from_parts(6_397_986, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -1232,13 +1319,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_clear_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1249 + n * (1 ±0)` - // Estimated: `1249 + n * (1 ±0)` - // Minimum execution time: 262_655_000 picoseconds. - Weight::from_parts(289_482_543, 1249) - // Standard Error: 35 - .saturating_add(Weight::from_parts(92, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `1252 + n * (1 ±0)` + // Estimated: `1252 + n * (1 ±0)` + // Minimum execution time: 273_945_000 picoseconds. + Weight::from_parts(299_855_996, 1252) + // Standard Error: 31 + .saturating_add(Weight::from_parts(309, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1247,13 +1334,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 800]`. fn seal_get_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (296 ±0)` - // Estimated: `923 + r * (297 ±0)` - // Minimum execution time: 247_414_000 picoseconds. - Weight::from_parts(203_317_182, 923) - // Standard Error: 7_191 - .saturating_add(Weight::from_parts(4_925_154, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `924 + r * (296 ±0)` + // Estimated: `926 + r * (297 ±0)` + // Minimum execution time: 275_285_000 picoseconds. + Weight::from_parts(207_735_572, 926) + // Standard Error: 9_736 + .saturating_add(Weight::from_parts(5_162_837, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 297).saturating_mul(r.into())) @@ -1263,13 +1350,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_get_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1265 + n * (1 ±0)` - // Estimated: `1265 + n * (1 ±0)` - // Minimum execution time: 258_910_000 picoseconds. - Weight::from_parts(283_086_514, 1265) - // Standard Error: 39 - .saturating_add(Weight::from_parts(980, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `1268 + n * (1 ±0)` + // Estimated: `1268 + n * (1 ±0)` + // Minimum execution time: 278_929_000 picoseconds. + Weight::from_parts(302_251_674, 1268) + // Standard Error: 34 + .saturating_add(Weight::from_parts(583, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1278,13 +1365,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 800]`. fn seal_contains_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `932 + r * (288 ±0)` - // Estimated: `929 + r * (289 ±0)` - // Minimum execution time: 252_410_000 picoseconds. - Weight::from_parts(201_227_879, 929) - // Standard Error: 6_899 - .saturating_add(Weight::from_parts(4_774_983, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `935 + r * (288 ±0)` + // Estimated: `932 + r * (289 ±0)` + // Minimum execution time: 258_476_000 picoseconds. + Weight::from_parts(209_578_051, 932) + // Standard Error: 8_255 + .saturating_add(Weight::from_parts(4_942_572, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 289).saturating_mul(r.into())) @@ -1294,13 +1381,11 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_contains_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1252 + n * (1 ±0)` - // Estimated: `1252 + n * (1 ±0)` - // Minimum execution time: 259_053_000 picoseconds. - Weight::from_parts(283_392_084, 1252) - // Standard Error: 41 - .saturating_add(Weight::from_parts(213, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `1255 + n * (1 ±0)` + // Estimated: `1255 + n * (1 ±0)` + // Minimum execution time: 273_089_000 picoseconds. + Weight::from_parts(302_452_604, 1255) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1309,13 +1394,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 800]`. fn seal_take_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `914 + r * (296 ±0)` - // Estimated: `919 + r * (297 ±0)` - // Minimum execution time: 251_371_000 picoseconds. - Weight::from_parts(177_119_717, 919) - // Standard Error: 9_421 - .saturating_add(Weight::from_parts(6_226_005, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `917 + r * (296 ±0)` + // Estimated: `922 + r * (297 ±0)` + // Minimum execution time: 274_301_000 picoseconds. + Weight::from_parts(172_245_469, 922) + // Standard Error: 11_306 + .saturating_add(Weight::from_parts(6_526_825, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -1326,13 +1411,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_take_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1266 + n * (1 ±0)` - // Estimated: `1266 + n * (1 ±0)` - // Minimum execution time: 263_350_000 picoseconds. - Weight::from_parts(284_323_917, 1266) - // Standard Error: 31 - .saturating_add(Weight::from_parts(921, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `1269 + n * (1 ±0)` + // Estimated: `1269 + n * (1 ±0)` + // Minimum execution time: 280_399_000 picoseconds. + Weight::from_parts(305_970_974, 1269) + // Standard Error: 36 + .saturating_add(Weight::from_parts(568, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1340,6 +1425,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1602 w:1601) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1353,13 +1440,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_transfer(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1415 + r * (45 ±0)` - // Estimated: `7307 + r * (2520 ±0)` - // Minimum execution time: 248_701_000 picoseconds. - Weight::from_parts(17_811_969, 7307) - // Standard Error: 35_154 - .saturating_add(Weight::from_parts(31_809_738, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `1418 + r * (45 ±0)` + // Estimated: `9785 + r * (2520 ±0)` + // Minimum execution time: 258_452_000 picoseconds. + Weight::from_parts(276_401_000, 9785) + // Standard Error: 65_648 + .saturating_add(Weight::from_parts(33_890_852, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -1369,6 +1456,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:801 w:801) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:2 w:0) @@ -1382,13 +1471,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 800]`. fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1260 + r * (245 ±0)` - // Estimated: `9440 + r * (2721 ±0)` - // Minimum execution time: 247_335_000 picoseconds. - Weight::from_parts(264_025_000, 9440) - // Standard Error: 121_299 - .saturating_add(Weight::from_parts(234_770_827, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(11_u64)) + // Measured: `1263 + r * (245 ±0)` + // Estimated: `9635 + r * (2721 ±0)` + // Minimum execution time: 281_394_000 picoseconds. + Weight::from_parts(286_475_000, 9635) + // Standard Error: 156_302 + .saturating_add(Weight::from_parts(250_370_283, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(14_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(r.into()))) @@ -1398,6 +1487,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:736 w:0) @@ -1412,12 +1503,12 @@ impl WeightInfo for SubstrateWeight { fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (576 ±0)` - // Estimated: `6812 + r * (2637 ±3)` - // Minimum execution time: 261_011_000 picoseconds. - Weight::from_parts(264_554_000, 6812) - // Standard Error: 104_415 - .saturating_add(Weight::from_parts(231_627_084, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Estimated: `9290 + r * (2637 ±10)` + // Minimum execution time: 278_193_000 picoseconds. + Weight::from_parts(280_814_000, 9290) + // Standard Error: 164_401 + .saturating_add(Weight::from_parts(251_272_834, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -1427,6 +1518,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:3 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:2 w:2) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:2 w:0) @@ -1441,15 +1534,15 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 1048576]`. fn seal_call_per_transfer_clone_byte(t: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1307 + t * (277 ±0)` - // Estimated: `12197 + t * (5227 ±0)` - // Minimum execution time: 445_561_000 picoseconds. - Weight::from_parts(62_287_490, 12197) - // Standard Error: 11_797_697 - .saturating_add(Weight::from_parts(357_530_529, 0).saturating_mul(t.into())) + // Measured: `1310 + t * (277 ±0)` + // Estimated: `12200 + t * (5227 ±0)` + // Minimum execution time: 476_812_000 picoseconds. + Weight::from_parts(70_715_306, 12200) + // Standard Error: 12_232_109 + .saturating_add(Weight::from_parts(374_277_042, 0).saturating_mul(t.into())) // Standard Error: 17 - .saturating_add(Weight::from_parts(970, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(13_u64)) + .saturating_add(Weight::from_parts(1_022, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(16_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(t.into()))) @@ -1459,6 +1552,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:802 w:802) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:801 w:801) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:801 w:800) @@ -1472,17 +1567,17 @@ impl WeightInfo for SubstrateWeight { /// Storage: `System::EventTopics` (r:803 w:803) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Balances::Holds` (r:800 w:800) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// The range of component `r` is `[1, 800]`. fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1278 + r * (255 ±0)` - // Estimated: `9620 + r * (2731 ±0)` - // Minimum execution time: 621_897_000 picoseconds. - Weight::from_parts(631_687_000, 9620) - // Standard Error: 215_241 - .saturating_add(Weight::from_parts(350_527_831, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(11_u64)) + // Measured: `1281 + r * (255 ±0)` + // Estimated: `9623 + r * (2731 ±0)` + // Minimum execution time: 656_480_000 picoseconds. + Weight::from_parts(668_579_000, 9623) + // Standard Error: 365_458 + .saturating_add(Weight::from_parts(379_238_223, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(14_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(7_u64)) .saturating_add(T::DbWeight::get().writes((5_u64).saturating_mul(r.into()))) @@ -1492,6 +1587,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:3 w:3) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:2 w:2) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:2 w:1) @@ -1505,23 +1602,21 @@ impl WeightInfo for SubstrateWeight { /// Storage: `System::EventTopics` (r:4 w:4) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// The range of component `t` is `[0, 1]`. /// The range of component `i` is `[0, 983040]`. /// The range of component `s` is `[0, 983040]`. fn seal_instantiate_per_transfer_input_salt_byte(t: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1303 + t * (104 ±0)` - // Estimated: `12211 + t * (2549 ±1)` - // Minimum execution time: 2_181_184_000 picoseconds. - Weight::from_parts(1_194_190_111, 12211) - // Standard Error: 11_578_766 - .saturating_add(Weight::from_parts(6_361_884, 0).saturating_mul(t.into())) - // Standard Error: 18 - .saturating_add(Weight::from_parts(1_025, 0).saturating_mul(i.into())) - // Standard Error: 18 - .saturating_add(Weight::from_parts(1_158, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(16_u64)) + // Measured: `1306 + t * (104 ±0)` + // Estimated: `12214 + t * (2549 ±1)` + // Minimum execution time: 2_148_964_000 picoseconds. + Weight::from_parts(1_557_685_999, 12214) + // Standard Error: 36 + .saturating_add(Weight::from_parts(864, 0).saturating_mul(i.into())) + // Standard Error: 36 + .saturating_add(Weight::from_parts(1_092, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(19_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(11_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) @@ -1531,6 +1626,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1544,13 +1641,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_hash_sha2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `862 + r * (8 ±0)` - // Estimated: `6801 + r * (8 ±0)` - // Minimum execution time: 241_609_000 picoseconds. - Weight::from_parts(268_716_874, 6801) - // Standard Error: 617 - .saturating_add(Weight::from_parts(377_753, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `865 + r * (8 ±0)` + // Estimated: `9279 + r * (8 ±0)` + // Minimum execution time: 279_377_000 picoseconds. + Weight::from_parts(287_951_287, 9279) + // Standard Error: 659 + .saturating_add(Weight::from_parts(376_476, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } @@ -1558,6 +1655,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1571,19 +1670,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_sha2_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `870` - // Estimated: `6808` - // Minimum execution time: 261_296_000 picoseconds. - Weight::from_parts(255_531_654, 6808) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_081, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `873` + // Estimated: `9286` + // Minimum execution time: 276_151_000 picoseconds. + Weight::from_parts(267_656_959, 9286) + // Standard Error: 2 + .saturating_add(Weight::from_parts(1_108, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1597,13 +1698,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_hash_keccak_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `864 + r * (8 ±0)` - // Estimated: `6806 + r * (8 ±0)` - // Minimum execution time: 243_583_000 picoseconds. - Weight::from_parts(270_025_058, 6806) - // Standard Error: 560 - .saturating_add(Weight::from_parts(767_519, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `867 + r * (8 ±0)` + // Estimated: `9284 + r * (8 ±0)` + // Minimum execution time: 275_247_000 picoseconds. + Weight::from_parts(286_675_317, 9284) + // Standard Error: 601 + .saturating_add(Weight::from_parts(788_160, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } @@ -1611,6 +1712,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1624,19 +1727,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_keccak_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872` - // Estimated: `6814` - // Minimum execution time: 253_798_000 picoseconds. - Weight::from_parts(265_542_351, 6814) - // Standard Error: 0 - .saturating_add(Weight::from_parts(3_343, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `875` + // Estimated: `9292` + // Minimum execution time: 281_585_000 picoseconds. + Weight::from_parts(287_637_844, 9292) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_351, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1650,13 +1755,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_hash_blake2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `864 + r * (8 ±0)` - // Estimated: `6808 + r * (8 ±0)` - // Minimum execution time: 247_332_000 picoseconds. - Weight::from_parts(269_183_656, 6808) - // Standard Error: 665 - .saturating_add(Weight::from_parts(443_386, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `867 + r * (8 ±0)` + // Estimated: `9286 + r * (8 ±0)` + // Minimum execution time: 273_678_000 picoseconds. + Weight::from_parts(289_879_306, 9286) + // Standard Error: 607 + .saturating_add(Weight::from_parts(439_482, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } @@ -1664,6 +1769,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1677,19 +1784,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_blake2_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872` - // Estimated: `6813` - // Minimum execution time: 250_855_000 picoseconds. - Weight::from_parts(258_752_975, 6813) + // Measured: `875` + // Estimated: `9291` + // Minimum execution time: 275_126_000 picoseconds. + Weight::from_parts(276_684_594, 9291) // Standard Error: 1 .saturating_add(Weight::from_parts(1_202, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1703,13 +1812,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_hash_blake2_128(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `864 + r * (8 ±0)` - // Estimated: `6805 + r * (8 ±0)` - // Minimum execution time: 240_733_000 picoseconds. - Weight::from_parts(269_134_358, 6805) - // Standard Error: 512 - .saturating_add(Weight::from_parts(440_043, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `867 + r * (8 ±0)` + // Estimated: `9283 + r * (8 ±0)` + // Minimum execution time: 273_229_000 picoseconds. + Weight::from_parts(287_793_841, 9283) + // Standard Error: 451 + .saturating_add(Weight::from_parts(447_922, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } @@ -1717,6 +1826,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1730,19 +1841,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_blake2_128_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872` - // Estimated: `6811` - // Minimum execution time: 247_377_000 picoseconds. - Weight::from_parts(261_077_322, 6811) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_195, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `875` + // Estimated: `9289` + // Minimum execution time: 277_843_000 picoseconds. + Weight::from_parts(279_900_099, 9289) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_204, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1756,13 +1869,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 125697]`. fn seal_sr25519_verify_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `997 + n * (1 ±0)` - // Estimated: `6934 + n * (1 ±0)` - // Minimum execution time: 307_337_000 picoseconds. - Weight::from_parts(326_710_473, 6934) - // Standard Error: 9 - .saturating_add(Weight::from_parts(5_765, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `1000 + n * (1 ±0)` + // Estimated: `9412 + n * (1 ±0)` + // Minimum execution time: 331_840_000 picoseconds. + Weight::from_parts(338_767_191, 9412) + // Standard Error: 11 + .saturating_add(Weight::from_parts(5_971, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1770,6 +1883,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1783,13 +1898,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 160]`. fn seal_sr25519_verify(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `805 + r * (112 ±0)` - // Estimated: `6748 + r * (112 ±0)` - // Minimum execution time: 245_432_000 picoseconds. - Weight::from_parts(294_206_377, 6748) - // Standard Error: 7_229 - .saturating_add(Weight::from_parts(41_480_485, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `808 + r * (112 ±0)` + // Estimated: `9226 + r * (112 ±0)` + // Minimum execution time: 277_912_000 picoseconds. + Weight::from_parts(344_538_960, 9226) + // Standard Error: 13_422 + .saturating_add(Weight::from_parts(41_592_887, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 112).saturating_mul(r.into())) } @@ -1797,6 +1912,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1810,13 +1927,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 160]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `907 + r * (76 ±0)` - // Estimated: `6802 + r * (77 ±0)` - // Minimum execution time: 247_788_000 picoseconds. - Weight::from_parts(303_940_062, 6802) - // Standard Error: 10_671 - .saturating_add(Weight::from_parts(45_730_772, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `910 + r * (76 ±0)` + // Estimated: `9279 + r * (77 ±0)` + // Minimum execution time: 280_383_000 picoseconds. + Weight::from_parts(348_542_377, 9279) + // Standard Error: 13_985 + .saturating_add(Weight::from_parts(45_983_827, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 77).saturating_mul(r.into())) } @@ -1824,6 +1941,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1837,13 +1956,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 160]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `877 + r * (42 ±0)` - // Estimated: `6816 + r * (42 ±0)` - // Minimum execution time: 248_825_000 picoseconds. - Weight::from_parts(286_832_225, 6816) - // Standard Error: 5_274 - .saturating_add(Weight::from_parts(11_889_262, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `880 + r * (42 ±0)` + // Estimated: `9294 + r * (42 ±0)` + // Minimum execution time: 277_764_000 picoseconds. + Weight::from_parts(320_288_180, 9294) + // Standard Error: 10_140 + .saturating_add(Weight::from_parts(12_046_137, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 42).saturating_mul(r.into())) } @@ -1851,6 +1970,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1536 w:1536) @@ -1865,12 +1986,12 @@ impl WeightInfo for SubstrateWeight { fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (965 ±0)` - // Estimated: `6807 + r * (3090 ±7)` - // Minimum execution time: 244_982_000 picoseconds. - Weight::from_parts(265_297_000, 6807) - // Standard Error: 39_895 - .saturating_add(Weight::from_parts(22_435_888, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Estimated: `9285 + r * (3090 ±7)` + // Minimum execution time: 271_356_000 picoseconds. + Weight::from_parts(282_924_000, 9285) + // Standard Error: 60_493 + .saturating_add(Weight::from_parts(28_319_267, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(r.into()))) @@ -1880,6 +2001,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:33 w:32) @@ -1893,22 +2016,24 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 32]`. fn lock_delegate_dependency(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `928 + r * (131 ±0)` - // Estimated: `6878 + r * (2606 ±0)` - // Minimum execution time: 246_455_000 picoseconds. - Weight::from_parts(275_334_919, 6878) - // Standard Error: 20_911 - .saturating_add(Weight::from_parts(6_427_525, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `937 + r * (131 ±0)` + // Estimated: `9346 + r * (2607 ±0)` + // Minimum execution time: 269_698_000 picoseconds. + Weight::from_parts(294_325_127, 9346) + // Standard Error: 22_352 + .saturating_add(Weight::from_parts(6_744_117, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 2606).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2607).saturating_mul(r.into())) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `MaxEncodedLen`) /// Storage: `Contracts::CodeInfoOf` (r:33 w:32) @@ -1922,13 +2047,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 32]`. fn unlock_delegate_dependency(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `969 + r * (183 ±0)` + // Measured: `972 + r * (184 ±0)` // Estimated: `129453 + r * (2568 ±0)` - // Minimum execution time: 254_472_000 picoseconds. - Weight::from_parts(280_657_909, 129453) - // Standard Error: 20_131 - .saturating_add(Weight::from_parts(5_644_006, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Minimum execution time: 261_226_000 picoseconds. + Weight::from_parts(294_299_527, 129453) + // Standard Error: 27_898 + .saturating_add(Weight::from_parts(6_031_601, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -1938,6 +2063,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1951,13 +2078,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `858 + r * (3 ±0)` - // Estimated: `6804 + r * (3 ±0)` - // Minimum execution time: 250_535_000 picoseconds. - Weight::from_parts(270_318_376, 6804) - // Standard Error: 386 - .saturating_add(Weight::from_parts(174_627, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `861 + r * (3 ±0)` + // Estimated: `9282 + r * (3 ±0)` + // Minimum execution time: 270_729_000 picoseconds. + Weight::from_parts(289_622_807, 9282) + // Standard Error: 394 + .saturating_add(Weight::from_parts(167_010, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } @@ -1965,6 +2092,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1978,13 +2107,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_account_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2109 + r * (39 ±0)` - // Estimated: `7899 + r * (40 ±0)` - // Minimum execution time: 248_174_000 picoseconds. - Weight::from_parts(301_826_520, 7899) - // Standard Error: 801 - .saturating_add(Weight::from_parts(248_479, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `2112 + r * (39 ±0)` + // Estimated: `10377 + r * (40 ±0)` + // Minimum execution time: 272_228_000 picoseconds. + Weight::from_parts(351_059_276, 10377) + // Standard Error: 1_761 + .saturating_add(Weight::from_parts(312_269, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) } @@ -1992,6 +2121,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2007,13 +2138,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_instantiation_nonce(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `861 + r * (3 ±0)` - // Estimated: `6801 + r * (3 ±0)` - // Minimum execution time: 246_540_000 picoseconds. - Weight::from_parts(268_913_509, 6801) - // Standard Error: 378 - .saturating_add(Weight::from_parts(154_950, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `864 + r * (3 ±0)` + // Estimated: `9279 + r * (3 ±0)` + // Minimum execution time: 272_497_000 picoseconds. + Weight::from_parts(288_213_060, 9279) + // Standard Error: 469 + .saturating_add(Weight::from_parts(155_530, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } @@ -2022,10 +2153,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_777_000 picoseconds. - Weight::from_parts(1_707_601, 0) - // Standard Error: 14 - .saturating_add(Weight::from_parts(15_392, 0).saturating_mul(r.into())) + // Minimum execution time: 1_990_000 picoseconds. + Weight::from_parts(1_778_221, 0) + // Standard Error: 26 + .saturating_add(Weight::from_parts(14_888, 0).saturating_mul(r.into())) } } @@ -2037,8 +2168,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `142` // Estimated: `1627` - // Minimum execution time: 1_997_000 picoseconds. - Weight::from_parts(2_130_000, 1627) + // Minimum execution time: 2_130_000 picoseconds. + Weight::from_parts(2_247_000, 1627) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -2048,10 +2179,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `452 + k * (69 ±0)` // Estimated: `442 + k * (70 ±0)` - // Minimum execution time: 12_276_000 picoseconds. - Weight::from_parts(1_593_881, 442) - // Standard Error: 1_135 - .saturating_add(Weight::from_parts(1_109_302, 0).saturating_mul(k.into())) + // Minimum execution time: 12_748_000 picoseconds. + Weight::from_parts(13_001_000, 442) + // Standard Error: 1_206 + .saturating_add(Weight::from_parts(1_146_159, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -2065,10 +2196,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `211 + c * (1 ±0)` // Estimated: `6149 + c * (1 ±0)` - // Minimum execution time: 8_176_000 picoseconds. - Weight::from_parts(8_555_388, 6149) + // Minimum execution time: 8_636_000 picoseconds. + Weight::from_parts(8_664_917, 6149) // Standard Error: 1 - .saturating_add(Weight::from_parts(1_184, 0).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(1_188, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) @@ -2081,8 +2212,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `510` // Estimated: `6450` - // Minimum execution time: 16_270_000 picoseconds. - Weight::from_parts(16_779_000, 6450) + // Minimum execution time: 16_838_000 picoseconds. + Weight::from_parts(17_400_000, 6450) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -2095,10 +2226,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `171 + k * (1 ±0)` // Estimated: `3635 + k * (1 ±0)` - // Minimum execution time: 3_572_000 picoseconds. - Weight::from_parts(1_950_905, 3635) - // Standard Error: 1_597 - .saturating_add(Weight::from_parts(1_123_190, 0).saturating_mul(k.into())) + // Minimum execution time: 3_876_000 picoseconds. + Weight::from_parts(2_654_935, 3635) + // Standard Error: 2_001 + .saturating_add(Weight::from_parts(1_258_085, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -2108,6 +2239,8 @@ impl WeightInfo for () { /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553053f13fd319a03c211337c76e0fe776df` (r:2 w:0) /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:1 w:1) /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:1 w:1) + /// Storage: `Parameters::Parameters` (r:2 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:0 w:1) @@ -2115,13 +2248,13 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 125952]`. fn v12_migration_step(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `325 + c * (1 ±0)` - // Estimated: `6263 + c * (1 ±0)` - // Minimum execution time: 16_873_000 picoseconds. - Weight::from_parts(16_790_402, 6263) + // Measured: `328 + c * (1 ±0)` + // Estimated: `6266 + c * (1 ±0)` + // Minimum execution time: 21_038_000 picoseconds. + Weight::from_parts(20_890_548, 6266) // Standard Error: 1 - .saturating_add(Weight::from_parts(396, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(Weight::from_parts(435, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) } @@ -2131,8 +2264,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `440` // Estimated: `6380` - // Minimum execution time: 11_904_000 picoseconds. - Weight::from_parts(12_785_000, 6380) + // Minimum execution time: 12_579_000 picoseconds. + Weight::from_parts(13_486_000, 6380) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -2141,13 +2274,13 @@ impl WeightInfo for () { /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:0) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) fn v14_migration_step() -> Weight { // Proof Size summary in bytes: // Measured: `352` // Estimated: `6292` - // Minimum execution time: 44_920_000 picoseconds. - Weight::from_parts(46_163_000, 6292) + // Minimum execution time: 47_123_000 picoseconds. + Weight::from_parts(48_284_000, 6292) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -2159,8 +2292,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `594` // Estimated: `6534` - // Minimum execution time: 53_864_000 picoseconds. - Weight::from_parts(55_139_000, 6534) + // Minimum execution time: 55_237_000 picoseconds. + Weight::from_parts(57_996_000, 6534) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -2170,8 +2303,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `142` // Estimated: `1627` - // Minimum execution time: 2_375_000 picoseconds. - Weight::from_parts(2_487_000, 1627) + // Minimum execution time: 2_766_000 picoseconds. + Weight::from_parts(2_807_000, 1627) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -2183,8 +2316,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `166` // Estimated: `3631` - // Minimum execution time: 11_580_000 picoseconds. - Weight::from_parts(11_980_000, 3631) + // Minimum execution time: 12_243_000 picoseconds. + Weight::from_parts(12_890_000, 3631) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -2194,8 +2327,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `142` // Estimated: `3607` - // Minimum execution time: 4_557_000 picoseconds. - Weight::from_parts(4_807_000, 3607) + // Minimum execution time: 4_951_000 picoseconds. + Weight::from_parts(5_232_000, 3607) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) @@ -2206,8 +2339,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `167` // Estimated: `3632` - // Minimum execution time: 6_253_000 picoseconds. - Weight::from_parts(6_479_000, 3632) + // Minimum execution time: 6_530_000 picoseconds. + Weight::from_parts(6_726_000, 3632) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) @@ -2218,13 +2351,15 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `142` // Estimated: `3607` - // Minimum execution time: 6_166_000 picoseconds. - Weight::from_parts(6_545_000, 3607) + // Minimum execution time: 6_227_000 picoseconds. + Weight::from_parts(6_708_000, 3607) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2240,22 +2375,24 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 125952]`. fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `801 + c * (1 ±0)` - // Estimated: `6739 + c * (1 ±0)` - // Minimum execution time: 282_232_000 picoseconds. - Weight::from_parts(266_148_573, 6739) - // Standard Error: 69 - .saturating_add(Weight::from_parts(34_592, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `804 + c * (1 ±0)` + // Estimated: `9217 + c * (1 ±0)` + // Minimum execution time: 309_889_000 picoseconds. + Weight::from_parts(277_084_159, 9217) + // Standard Error: 71 + .saturating_add(Weight::from_parts(33_471, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) /// Storage: `Balances::Holds` (r:2 w:2) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// Storage: `System::EventTopics` (r:3 w:3) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Contracts::Nonce` (r:1 w:1) @@ -2273,17 +2410,17 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 1048576]`. fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `323` - // Estimated: `8737` - // Minimum execution time: 3_760_879_000 picoseconds. - Weight::from_parts(794_812_431, 8737) - // Standard Error: 149 - .saturating_add(Weight::from_parts(101_881, 0).saturating_mul(c.into())) - // Standard Error: 18 - .saturating_add(Weight::from_parts(1_404, 0).saturating_mul(i.into())) - // Standard Error: 18 - .saturating_add(Weight::from_parts(1_544, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(11_u64)) + // Measured: `326` + // Estimated: `8740` + // Minimum execution time: 3_909_680_000 picoseconds. + Weight::from_parts(446_471_160, 8740) + // Standard Error: 159 + .saturating_add(Weight::from_parts(101_085, 0).saturating_mul(c.into())) + // Standard Error: 19 + .saturating_add(Weight::from_parts(1_598, 0).saturating_mul(i.into())) + // Standard Error: 19 + .saturating_add(Weight::from_parts(1_879, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(14_u64)) .saturating_add(RocksDbWeight::get().writes(10_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) @@ -2292,6 +2429,8 @@ impl WeightInfo for () { /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) /// Storage: `Contracts::PristineCode` (r:1 w:0) /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::Nonce` (r:1 w:1) /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) @@ -2303,24 +2442,26 @@ impl WeightInfo for () { /// Storage: `System::EventTopics` (r:2 w:2) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// The range of component `i` is `[0, 1048576]`. /// The range of component `s` is `[0, 1048576]`. fn instantiate(i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `560` - // Estimated: `6504` - // Minimum execution time: 1_953_162_000 picoseconds. - Weight::from_parts(374_252_840, 6504) - // Standard Error: 7 - .saturating_add(Weight::from_parts(1_630, 0).saturating_mul(i.into())) - // Standard Error: 7 - .saturating_add(Weight::from_parts(1_650, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(10_u64)) + // Measured: `563` + // Estimated: `8982` + // Minimum execution time: 1_968_545_000 picoseconds. + Weight::from_parts(420_048_028, 8982) + // Standard Error: 20 + .saturating_add(Weight::from_parts(1_685, 0).saturating_mul(i.into())) + // Standard Error: 20 + .saturating_add(Weight::from_parts(1_645, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2335,33 +2476,59 @@ impl WeightInfo for () { /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `826` - // Estimated: `6766` - // Minimum execution time: 187_899_000 picoseconds. - Weight::from_parts(195_510_000, 6766) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `829` + // Estimated: `9244` + // Minimum execution time: 207_564_000 picoseconds. + Weight::from_parts(216_983_000, 9244) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:2 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// Storage: `System::EventTopics` (r:1 w:1) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Contracts::PristineCode` (r:0 w:1) /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) /// The range of component `c` is `[0, 125952]`. - fn upload_code(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 254_800_000 picoseconds. - Weight::from_parts(285_603_050, 3607) - // Standard Error: 62 - .saturating_add(Weight::from_parts(66_212, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + fn upload_code_determinism_enforced(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `6085` + // Minimum execution time: 273_555_000 picoseconds. + Weight::from_parts(257_517_935, 6085) + // Standard Error: 148 + .saturating_add(Weight::from_parts(64_488, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:2 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// Storage: `System::EventTopics` (r:1 w:1) + /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:0 w:1) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + fn upload_code_determinism_relaxed(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `6085` + // Minimum execution time: 289_672_000 picoseconds. + Weight::from_parts(297_020_278, 6085) + // Standard Error: 86 + .saturating_add(Weight::from_parts(64_340, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) @@ -2369,7 +2536,7 @@ impl WeightInfo for () { /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// Storage: `System::EventTopics` (r:1 w:1) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Contracts::PristineCode` (r:0 w:1) @@ -2378,8 +2545,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `315` // Estimated: `3780` - // Minimum execution time: 43_553_000 picoseconds. - Weight::from_parts(45_036_000, 3780) + // Minimum execution time: 45_930_000 picoseconds. + Weight::from_parts(47_288_000, 3780) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2395,8 +2562,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `552` // Estimated: `8967` - // Minimum execution time: 33_223_000 picoseconds. - Weight::from_parts(34_385_000, 8967) + // Minimum execution time: 35_421_000 picoseconds. + Weight::from_parts(36_909_000, 8967) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2404,6 +2571,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2417,13 +2586,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_caller(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `866 + r * (6 ±0)` - // Estimated: `6806 + r * (6 ±0)` - // Minimum execution time: 254_213_000 picoseconds. - Weight::from_parts(273_464_980, 6806) - // Standard Error: 1_362 - .saturating_add(Weight::from_parts(322_619, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `869 + r * (6 ±0)` + // Estimated: `9284 + r * (6 ±0)` + // Minimum execution time: 275_531_000 picoseconds. + Weight::from_parts(292_269_656, 9284) + // Standard Error: 672 + .saturating_add(Weight::from_parts(339_728, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2431,6 +2600,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1601 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2444,13 +2615,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_is_contract(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `922 + r * (209 ±0)` - // Estimated: `6826 + r * (2684 ±0)` - // Minimum execution time: 250_273_000 picoseconds. - Weight::from_parts(122_072_782, 6826) - // Standard Error: 5_629 - .saturating_add(Weight::from_parts(3_490_256, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `925 + r * (209 ±0)` + // Estimated: `9304 + r * (2684 ±0)` + // Minimum execution time: 275_829_000 picoseconds. + Weight::from_parts(124_543_289, 9304) + // Standard Error: 6_085 + .saturating_add(Weight::from_parts(3_702_964, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2684).saturating_mul(r.into())) @@ -2459,6 +2630,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1601 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2472,13 +2645,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (213 ±0)` - // Estimated: `6830 + r * (2688 ±0)` - // Minimum execution time: 255_187_000 picoseconds. - Weight::from_parts(118_082_505, 6830) - // Standard Error: 6_302 - .saturating_add(Weight::from_parts(4_246_968, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `924 + r * (213 ±0)` + // Estimated: `9308 + r * (2688 ±0)` + // Minimum execution time: 276_666_000 picoseconds. + Weight::from_parts(96_951_288, 9308) + // Standard Error: 8_876 + .saturating_add(Weight::from_parts(4_604_699, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2688).saturating_mul(r.into())) @@ -2487,6 +2660,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2500,13 +2675,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_own_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `873 + r * (6 ±0)` - // Estimated: `6815 + r * (6 ±0)` - // Minimum execution time: 256_833_000 picoseconds. - Weight::from_parts(273_330_216, 6815) - // Standard Error: 881 - .saturating_add(Weight::from_parts(400_105, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `876 + r * (6 ±0)` + // Estimated: `9293 + r * (6 ±0)` + // Minimum execution time: 271_301_000 picoseconds. + Weight::from_parts(284_126_054, 9293) + // Standard Error: 886 + .saturating_add(Weight::from_parts(437_127, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2514,6 +2689,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2527,18 +2704,20 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_caller_is_origin(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863 + r * (3 ±0)` - // Estimated: `6804 + r * (3 ±0)` - // Minimum execution time: 244_193_000 picoseconds. - Weight::from_parts(271_221_908, 6804) - // Standard Error: 442 - .saturating_add(Weight::from_parts(176_480, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `866 + r * (3 ±0)` + // Estimated: `9282 + r * (3 ±0)` + // Minimum execution time: 274_778_000 picoseconds. + Weight::from_parts(289_355_269, 9282) + // Standard Error: 382 + .saturating_add(Weight::from_parts(175_342, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2552,13 +2731,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_caller_is_root(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `753 + r * (3 ±0)` - // Estimated: `6693 + r * (3 ±0)` - // Minimum execution time: 232_603_000 picoseconds. - Weight::from_parts(260_577_368, 6693) - // Standard Error: 365 - .saturating_add(Weight::from_parts(158_126, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(7_u64)) + // Measured: `756 + r * (3 ±0)` + // Estimated: `9171 + r * (3 ±0)` + // Minimum execution time: 257_310_000 picoseconds. + Weight::from_parts(276_410_847, 9171) + // Standard Error: 733 + .saturating_add(Weight::from_parts(160_094, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } @@ -2566,6 +2745,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2579,13 +2760,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `867 + r * (6 ±0)` - // Estimated: `6807 + r * (6 ±0)` - // Minimum execution time: 247_564_000 picoseconds. - Weight::from_parts(275_108_914, 6807) - // Standard Error: 505 - .saturating_add(Weight::from_parts(315_065, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `870 + r * (6 ±0)` + // Estimated: `9285 + r * (6 ±0)` + // Minimum execution time: 278_305_000 picoseconds. + Weight::from_parts(282_372_935, 9285) + // Standard Error: 1_154 + .saturating_add(Weight::from_parts(343_382, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2593,6 +2774,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2606,13 +2789,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_gas_left(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863 + r * (6 ±0)` - // Estimated: `6806 + r * (6 ±0)` - // Minimum execution time: 258_799_000 picoseconds. - Weight::from_parts(274_338_256, 6806) - // Standard Error: 632 - .saturating_add(Weight::from_parts(355_032, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `866 + r * (6 ±0)` + // Estimated: `9284 + r * (6 ±0)` + // Minimum execution time: 280_349_000 picoseconds. + Weight::from_parts(294_864_875, 9284) + // Standard Error: 1_010 + .saturating_add(Weight::from_parts(368_740, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2620,6 +2803,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:2 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2633,13 +2818,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1007 + r * (6 ±0)` - // Estimated: `6931 + r * (6 ±0)` - // Minimum execution time: 253_335_000 picoseconds. - Weight::from_parts(273_013_859, 6931) - // Standard Error: 2_007 - .saturating_add(Weight::from_parts(1_540_735, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `1010 + r * (6 ±0)` + // Estimated: `9409 + r * (6 ±0)` + // Minimum execution time: 274_578_000 picoseconds. + Weight::from_parts(313_285_034, 9409) + // Standard Error: 2_800 + .saturating_add(Weight::from_parts(1_653_468, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2647,6 +2832,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2660,13 +2847,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_value_transferred(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `877 + r * (6 ±0)` - // Estimated: `6823 + r * (6 ±0)` - // Minimum execution time: 252_325_000 picoseconds. - Weight::from_parts(274_733_944, 6823) - // Standard Error: 603 - .saturating_add(Weight::from_parts(314_467, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `880 + r * (6 ±0)` + // Estimated: `9301 + r * (6 ±0)` + // Minimum execution time: 287_127_000 picoseconds. + Weight::from_parts(290_489_909, 9301) + // Standard Error: 864 + .saturating_add(Weight::from_parts(332_977, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2674,6 +2861,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2687,13 +2876,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_minimum_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `875 + r * (6 ±0)` - // Estimated: `6816 + r * (6 ±0)` - // Minimum execution time: 250_698_000 picoseconds. - Weight::from_parts(271_707_578, 6816) - // Standard Error: 952 - .saturating_add(Weight::from_parts(318_412, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `878 + r * (6 ±0)` + // Estimated: `9294 + r * (6 ±0)` + // Minimum execution time: 273_291_000 picoseconds. + Weight::from_parts(293_650_716, 9294) + // Standard Error: 725 + .saturating_add(Weight::from_parts(323_281, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2701,6 +2890,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2714,13 +2905,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_block_number(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872 + r * (6 ±0)` - // Estimated: `6819 + r * (6 ±0)` - // Minimum execution time: 251_854_000 picoseconds. - Weight::from_parts(272_002_212, 6819) - // Standard Error: 622 - .saturating_add(Weight::from_parts(313_353, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `875 + r * (6 ±0)` + // Estimated: `9297 + r * (6 ±0)` + // Minimum execution time: 282_061_000 picoseconds. + Weight::from_parts(291_729_751, 9297) + // Standard Error: 929 + .saturating_add(Weight::from_parts(324_683, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2728,6 +2919,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2741,13 +2934,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_now(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863 + r * (6 ±0)` - // Estimated: `6804 + r * (6 ±0)` - // Minimum execution time: 252_010_000 picoseconds. - Weight::from_parts(270_387_000, 6804) - // Standard Error: 659 - .saturating_add(Weight::from_parts(325_856, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `866 + r * (6 ±0)` + // Estimated: `9282 + r * (6 ±0)` + // Minimum execution time: 264_505_000 picoseconds. + Weight::from_parts(293_440_286, 9282) + // Standard Error: 704 + .saturating_add(Weight::from_parts(329_851, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2755,6 +2948,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2770,13 +2965,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_weight_to_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `937 + r * (14 ±0)` - // Estimated: `6872 + r * (14 ±0)` - // Minimum execution time: 247_933_000 picoseconds. - Weight::from_parts(281_550_162, 6872) - // Standard Error: 660 - .saturating_add(Weight::from_parts(1_090_869, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `940 + r * (14 ±0)` + // Estimated: `9350 + r * (14 ±0)` + // Minimum execution time: 277_208_000 picoseconds. + Weight::from_parts(304_294_691, 9350) + // Standard Error: 1_083 + .saturating_add(Weight::from_parts(824_245, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 14).saturating_mul(r.into())) } @@ -2784,6 +2979,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2797,13 +2994,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_input(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `865 + r * (6 ±0)` - // Estimated: `6807 + r * (6 ±0)` - // Minimum execution time: 251_158_000 picoseconds. - Weight::from_parts(274_623_152, 6807) - // Standard Error: 491 - .saturating_add(Weight::from_parts(263_916, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `868 + r * (6 ±0)` + // Estimated: `9285 + r * (6 ±0)` + // Minimum execution time: 278_293_000 picoseconds. + Weight::from_parts(289_743_005, 9285) + // Standard Error: 672 + .saturating_add(Weight::from_parts(267_553, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2811,6 +3008,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2824,19 +3023,21 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1048576]`. fn seal_input_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `869` - // Estimated: `6809` - // Minimum execution time: 263_205_000 picoseconds. - Weight::from_parts(216_792_893, 6809) + // Measured: `872` + // Estimated: `9287` + // Minimum execution time: 279_495_000 picoseconds. + Weight::from_parts(232_736_994, 9287) // Standard Error: 23 - .saturating_add(Weight::from_parts(989, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(Weight::from_parts(1_008, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2850,11 +3051,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_return(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `853 + r * (45 ±0)` - // Estimated: `6793 + r * (45 ±0)` - // Minimum execution time: 239_663_000 picoseconds. - Weight::from_parts(266_124_565, 6793) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `856 + r * (45 ±0)` + // Estimated: `9271 + r * (45 ±0)` + // Minimum execution time: 257_920_000 picoseconds. + Weight::from_parts(282_276_265, 9271) + // Standard Error: 948_490 + .saturating_add(Weight::from_parts(2_408_134, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 45).saturating_mul(r.into())) } @@ -2862,6 +3065,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2875,22 +3080,24 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1048576]`. fn seal_return_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863` - // Estimated: `6810` - // Minimum execution time: 241_763_000 picoseconds. - Weight::from_parts(266_535_552, 6810) + // Measured: `866` + // Estimated: `9288` + // Minimum execution time: 278_149_000 picoseconds. + Weight::from_parts(287_020_337, 9288) // Standard Error: 0 - .saturating_add(Weight::from_parts(320, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(Weight::from_parts(321, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:3 w:3) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:1 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:2 w:2) + /// Storage: `Contracts::CodeInfoOf` (r:33 w:33) /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) /// Storage: `Contracts::PristineCode` (r:1 w:0) /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) @@ -2901,28 +3108,30 @@ impl WeightInfo for () { /// Storage: `System::EventTopics` (r:4 w:4) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// Storage: `Contracts::DeletionQueue` (r:0 w:1) /// Proof: `Contracts::DeletionQueue` (`max_values`: None, `max_size`: Some(142), added: 2617, mode: `Measured`) /// The range of component `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2972 + r * (316 ±0)` - // Estimated: `8912 + r * (5266 ±0)` - // Minimum execution time: 265_888_000 picoseconds. - Weight::from_parts(291_232_232, 8912) - // Standard Error: 845_475 - .saturating_add(Weight::from_parts(104_398_867, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) - .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(r.into()))) + // Measured: `4805 + r * (2121 ±0)` + // Estimated: `13220 + r * (81321 ±0)` + // Minimum execution time: 307_763_000 picoseconds. + Weight::from_parts(323_648_618, 13220) + // Standard Error: 879_890 + .saturating_add(Weight::from_parts(249_045_481, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) + .saturating_add(RocksDbWeight::get().reads((36_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(RocksDbWeight::get().writes((10_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 5266).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().writes((41_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 81321).saturating_mul(r.into())) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2938,13 +3147,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_random(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `944 + r * (10 ±0)` - // Estimated: `6885 + r * (10 ±0)` - // Minimum execution time: 248_500_000 picoseconds. - Weight::from_parts(282_353_053, 6885) - // Standard Error: 1_144 - .saturating_add(Weight::from_parts(1_193_841, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `947 + r * (10 ±0)` + // Estimated: `9363 + r * (10 ±0)` + // Minimum execution time: 278_400_000 picoseconds. + Weight::from_parts(293_743_000, 9363) + // Standard Error: 1_686 + .saturating_add(Weight::from_parts(1_288_603, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) } @@ -2952,6 +3161,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2965,13 +3176,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_deposit_event(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863 + r * (10 ±0)` - // Estimated: `6805 + r * (10 ±0)` - // Minimum execution time: 248_130_000 picoseconds. - Weight::from_parts(279_583_178, 6805) - // Standard Error: 971 - .saturating_add(Weight::from_parts(1_987_941, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `866 + r * (10 ±0)` + // Estimated: `9283 + r * (10 ±0)` + // Minimum execution time: 272_110_000 picoseconds. + Weight::from_parts(295_620_726, 9283) + // Standard Error: 5_481 + .saturating_add(Weight::from_parts(2_031_955, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) } @@ -2979,6 +3190,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2993,15 +3206,15 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_deposit_event_per_topic_and_byte(t: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `880 + t * (32 ±0)` - // Estimated: `6825 + t * (2508 ±0)` - // Minimum execution time: 258_594_000 picoseconds. - Weight::from_parts(276_734_422, 6825) - // Standard Error: 102_093 - .saturating_add(Weight::from_parts(2_559_383, 0).saturating_mul(t.into())) - // Standard Error: 28 - .saturating_add(Weight::from_parts(501, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `883 + t * (32 ±0)` + // Estimated: `9303 + t * (2508 ±0)` + // Minimum execution time: 277_409_000 picoseconds. + Weight::from_parts(293_838_037, 9303) + // Standard Error: 87_977 + .saturating_add(Weight::from_parts(2_911_340, 0).saturating_mul(t.into())) + // Standard Error: 24 + .saturating_add(Weight::from_parts(531, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) @@ -3011,6 +3224,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3024,13 +3239,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_debug_message(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `862 + r * (7 ±0)` - // Estimated: `6807 + r * (7 ±0)` - // Minimum execution time: 154_564_000 picoseconds. - Weight::from_parts(168_931_365, 6807) - // Standard Error: 349 - .saturating_add(Weight::from_parts(226_848, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `865 + r * (7 ±0)` + // Estimated: `9285 + r * (7 ±0)` + // Minimum execution time: 172_634_000 picoseconds. + Weight::from_parts(183_322_840, 9285) + // Standard Error: 384 + .saturating_add(Weight::from_parts(226_007, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 7).saturating_mul(r.into())) } @@ -3038,6 +3253,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `MaxEncodedLen`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3051,13 +3268,13 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 1048576]`. fn seal_debug_message_per_byte(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `125813` - // Estimated: `131755` - // Minimum execution time: 394_382_000 picoseconds. - Weight::from_parts(376_780_500, 131755) + // Measured: `125816` + // Estimated: `131758` + // Minimum execution time: 425_713_000 picoseconds. + Weight::from_parts(394_260_924, 131758) // Standard Error: 12 - .saturating_add(Weight::from_parts(1_026, 0).saturating_mul(i.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(Weight::from_parts(1_032, 0).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -3065,13 +3282,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 800]`. fn seal_set_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `924 + r * (292 ±0)` - // Estimated: `926 + r * (293 ±0)` - // Minimum execution time: 249_757_000 picoseconds. - Weight::from_parts(177_324_374, 926) - // Standard Error: 9_512 - .saturating_add(Weight::from_parts(6_176_717, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `927 + r * (292 ±0)` + // Estimated: `929 + r * (293 ±0)` + // Minimum execution time: 268_128_000 picoseconds. + Weight::from_parts(186_787_113, 929) + // Standard Error: 10_796 + .saturating_add(Weight::from_parts(6_454_780, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -3082,13 +3299,13 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_set_storage_per_new_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1447` - // Estimated: `1430` - // Minimum execution time: 267_564_000 picoseconds. - Weight::from_parts(328_701_080, 1430) - // Standard Error: 61 - .saturating_add(Weight::from_parts(576, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(12_u64)) + // Measured: `1450` + // Estimated: `1433` + // Minimum execution time: 286_565_000 picoseconds. + Weight::from_parts(349_504_932, 1433) + // Standard Error: 70 + .saturating_add(Weight::from_parts(530, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(15_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -3096,13 +3313,13 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_set_storage_per_old_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1253 + n * (1 ±0)` - // Estimated: `1253 + n * (1 ±0)` - // Minimum execution time: 266_347_000 picoseconds. - Weight::from_parts(289_824_718, 1253) + // Measured: `1256 + n * (1 ±0)` + // Estimated: `1256 + n * (1 ±0)` + // Minimum execution time: 282_478_000 picoseconds. + Weight::from_parts(303_448_260, 1256) // Standard Error: 34 - .saturating_add(Weight::from_parts(184, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(Weight::from_parts(712, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -3111,13 +3328,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 800]`. fn seal_clear_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (288 ±0)` - // Estimated: `927 + r * (289 ±0)` - // Minimum execution time: 247_207_000 picoseconds. - Weight::from_parts(179_856_075, 927) - // Standard Error: 9_383 - .saturating_add(Weight::from_parts(6_053_198, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `924 + r * (288 ±0)` + // Estimated: `930 + r * (289 ±0)` + // Minimum execution time: 271_793_000 picoseconds. + Weight::from_parts(179_158_648, 930) + // Standard Error: 11_868 + .saturating_add(Weight::from_parts(6_397_986, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -3128,13 +3345,13 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_clear_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1249 + n * (1 ±0)` - // Estimated: `1249 + n * (1 ±0)` - // Minimum execution time: 262_655_000 picoseconds. - Weight::from_parts(289_482_543, 1249) - // Standard Error: 35 - .saturating_add(Weight::from_parts(92, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `1252 + n * (1 ±0)` + // Estimated: `1252 + n * (1 ±0)` + // Minimum execution time: 273_945_000 picoseconds. + Weight::from_parts(299_855_996, 1252) + // Standard Error: 31 + .saturating_add(Weight::from_parts(309, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -3143,13 +3360,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 800]`. fn seal_get_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (296 ±0)` - // Estimated: `923 + r * (297 ±0)` - // Minimum execution time: 247_414_000 picoseconds. - Weight::from_parts(203_317_182, 923) - // Standard Error: 7_191 - .saturating_add(Weight::from_parts(4_925_154, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `924 + r * (296 ±0)` + // Estimated: `926 + r * (297 ±0)` + // Minimum execution time: 275_285_000 picoseconds. + Weight::from_parts(207_735_572, 926) + // Standard Error: 9_736 + .saturating_add(Weight::from_parts(5_162_837, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 297).saturating_mul(r.into())) @@ -3159,13 +3376,13 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_get_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1265 + n * (1 ±0)` - // Estimated: `1265 + n * (1 ±0)` - // Minimum execution time: 258_910_000 picoseconds. - Weight::from_parts(283_086_514, 1265) - // Standard Error: 39 - .saturating_add(Weight::from_parts(980, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `1268 + n * (1 ±0)` + // Estimated: `1268 + n * (1 ±0)` + // Minimum execution time: 278_929_000 picoseconds. + Weight::from_parts(302_251_674, 1268) + // Standard Error: 34 + .saturating_add(Weight::from_parts(583, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -3174,13 +3391,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 800]`. fn seal_contains_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `932 + r * (288 ±0)` - // Estimated: `929 + r * (289 ±0)` - // Minimum execution time: 252_410_000 picoseconds. - Weight::from_parts(201_227_879, 929) - // Standard Error: 6_899 - .saturating_add(Weight::from_parts(4_774_983, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `935 + r * (288 ±0)` + // Estimated: `932 + r * (289 ±0)` + // Minimum execution time: 258_476_000 picoseconds. + Weight::from_parts(209_578_051, 932) + // Standard Error: 8_255 + .saturating_add(Weight::from_parts(4_942_572, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 289).saturating_mul(r.into())) @@ -3190,13 +3407,11 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_contains_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1252 + n * (1 ±0)` - // Estimated: `1252 + n * (1 ±0)` - // Minimum execution time: 259_053_000 picoseconds. - Weight::from_parts(283_392_084, 1252) - // Standard Error: 41 - .saturating_add(Weight::from_parts(213, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `1255 + n * (1 ±0)` + // Estimated: `1255 + n * (1 ±0)` + // Minimum execution time: 273_089_000 picoseconds. + Weight::from_parts(302_452_604, 1255) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -3205,13 +3420,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 800]`. fn seal_take_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `914 + r * (296 ±0)` - // Estimated: `919 + r * (297 ±0)` - // Minimum execution time: 251_371_000 picoseconds. - Weight::from_parts(177_119_717, 919) - // Standard Error: 9_421 - .saturating_add(Weight::from_parts(6_226_005, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `917 + r * (296 ±0)` + // Estimated: `922 + r * (297 ±0)` + // Minimum execution time: 274_301_000 picoseconds. + Weight::from_parts(172_245_469, 922) + // Standard Error: 11_306 + .saturating_add(Weight::from_parts(6_526_825, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -3222,13 +3437,13 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_take_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1266 + n * (1 ±0)` - // Estimated: `1266 + n * (1 ±0)` - // Minimum execution time: 263_350_000 picoseconds. - Weight::from_parts(284_323_917, 1266) - // Standard Error: 31 - .saturating_add(Weight::from_parts(921, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `1269 + n * (1 ±0)` + // Estimated: `1269 + n * (1 ±0)` + // Minimum execution time: 280_399_000 picoseconds. + Weight::from_parts(305_970_974, 1269) + // Standard Error: 36 + .saturating_add(Weight::from_parts(568, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -3236,6 +3451,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1602 w:1601) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3249,13 +3466,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_transfer(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1415 + r * (45 ±0)` - // Estimated: `7307 + r * (2520 ±0)` - // Minimum execution time: 248_701_000 picoseconds. - Weight::from_parts(17_811_969, 7307) - // Standard Error: 35_154 - .saturating_add(Weight::from_parts(31_809_738, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `1418 + r * (45 ±0)` + // Estimated: `9785 + r * (2520 ±0)` + // Minimum execution time: 258_452_000 picoseconds. + Weight::from_parts(276_401_000, 9785) + // Standard Error: 65_648 + .saturating_add(Weight::from_parts(33_890_852, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -3265,6 +3482,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:801 w:801) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:2 w:0) @@ -3278,13 +3497,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 800]`. fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1260 + r * (245 ±0)` - // Estimated: `9440 + r * (2721 ±0)` - // Minimum execution time: 247_335_000 picoseconds. - Weight::from_parts(264_025_000, 9440) - // Standard Error: 121_299 - .saturating_add(Weight::from_parts(234_770_827, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(11_u64)) + // Measured: `1263 + r * (245 ±0)` + // Estimated: `9635 + r * (2721 ±0)` + // Minimum execution time: 281_394_000 picoseconds. + Weight::from_parts(286_475_000, 9635) + // Standard Error: 156_302 + .saturating_add(Weight::from_parts(250_370_283, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(14_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(r.into()))) @@ -3294,6 +3513,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:736 w:0) @@ -3308,12 +3529,12 @@ impl WeightInfo for () { fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (576 ±0)` - // Estimated: `6812 + r * (2637 ±3)` - // Minimum execution time: 261_011_000 picoseconds. - Weight::from_parts(264_554_000, 6812) - // Standard Error: 104_415 - .saturating_add(Weight::from_parts(231_627_084, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Estimated: `9290 + r * (2637 ±10)` + // Minimum execution time: 278_193_000 picoseconds. + Weight::from_parts(280_814_000, 9290) + // Standard Error: 164_401 + .saturating_add(Weight::from_parts(251_272_834, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -3323,6 +3544,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:3 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:2 w:2) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:2 w:0) @@ -3337,15 +3560,15 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 1048576]`. fn seal_call_per_transfer_clone_byte(t: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1307 + t * (277 ±0)` - // Estimated: `12197 + t * (5227 ±0)` - // Minimum execution time: 445_561_000 picoseconds. - Weight::from_parts(62_287_490, 12197) - // Standard Error: 11_797_697 - .saturating_add(Weight::from_parts(357_530_529, 0).saturating_mul(t.into())) + // Measured: `1310 + t * (277 ±0)` + // Estimated: `12200 + t * (5227 ±0)` + // Minimum execution time: 476_812_000 picoseconds. + Weight::from_parts(70_715_306, 12200) + // Standard Error: 12_232_109 + .saturating_add(Weight::from_parts(374_277_042, 0).saturating_mul(t.into())) // Standard Error: 17 - .saturating_add(Weight::from_parts(970, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(13_u64)) + .saturating_add(Weight::from_parts(1_022, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(16_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(t.into()))) @@ -3355,6 +3578,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:802 w:802) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:801 w:801) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:801 w:800) @@ -3368,17 +3593,17 @@ impl WeightInfo for () { /// Storage: `System::EventTopics` (r:803 w:803) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Balances::Holds` (r:800 w:800) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// The range of component `r` is `[1, 800]`. fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1278 + r * (255 ±0)` - // Estimated: `9620 + r * (2731 ±0)` - // Minimum execution time: 621_897_000 picoseconds. - Weight::from_parts(631_687_000, 9620) - // Standard Error: 215_241 - .saturating_add(Weight::from_parts(350_527_831, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(11_u64)) + // Measured: `1281 + r * (255 ±0)` + // Estimated: `9623 + r * (2731 ±0)` + // Minimum execution time: 656_480_000 picoseconds. + Weight::from_parts(668_579_000, 9623) + // Standard Error: 365_458 + .saturating_add(Weight::from_parts(379_238_223, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(14_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(7_u64)) .saturating_add(RocksDbWeight::get().writes((5_u64).saturating_mul(r.into()))) @@ -3388,6 +3613,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:3 w:3) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:2 w:2) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:2 w:1) @@ -3401,23 +3628,21 @@ impl WeightInfo for () { /// Storage: `System::EventTopics` (r:4 w:4) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// The range of component `t` is `[0, 1]`. /// The range of component `i` is `[0, 983040]`. /// The range of component `s` is `[0, 983040]`. fn seal_instantiate_per_transfer_input_salt_byte(t: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1303 + t * (104 ±0)` - // Estimated: `12211 + t * (2549 ±1)` - // Minimum execution time: 2_181_184_000 picoseconds. - Weight::from_parts(1_194_190_111, 12211) - // Standard Error: 11_578_766 - .saturating_add(Weight::from_parts(6_361_884, 0).saturating_mul(t.into())) - // Standard Error: 18 - .saturating_add(Weight::from_parts(1_025, 0).saturating_mul(i.into())) - // Standard Error: 18 - .saturating_add(Weight::from_parts(1_158, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(16_u64)) + // Measured: `1306 + t * (104 ±0)` + // Estimated: `12214 + t * (2549 ±1)` + // Minimum execution time: 2_148_964_000 picoseconds. + Weight::from_parts(1_557_685_999, 12214) + // Standard Error: 36 + .saturating_add(Weight::from_parts(864, 0).saturating_mul(i.into())) + // Standard Error: 36 + .saturating_add(Weight::from_parts(1_092, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(19_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(11_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) @@ -3427,6 +3652,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3440,13 +3667,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_hash_sha2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `862 + r * (8 ±0)` - // Estimated: `6801 + r * (8 ±0)` - // Minimum execution time: 241_609_000 picoseconds. - Weight::from_parts(268_716_874, 6801) - // Standard Error: 617 - .saturating_add(Weight::from_parts(377_753, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `865 + r * (8 ±0)` + // Estimated: `9279 + r * (8 ±0)` + // Minimum execution time: 279_377_000 picoseconds. + Weight::from_parts(287_951_287, 9279) + // Standard Error: 659 + .saturating_add(Weight::from_parts(376_476, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } @@ -3454,6 +3681,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3467,19 +3696,21 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_sha2_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `870` - // Estimated: `6808` - // Minimum execution time: 261_296_000 picoseconds. - Weight::from_parts(255_531_654, 6808) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_081, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `873` + // Estimated: `9286` + // Minimum execution time: 276_151_000 picoseconds. + Weight::from_parts(267_656_959, 9286) + // Standard Error: 2 + .saturating_add(Weight::from_parts(1_108, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3493,13 +3724,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_hash_keccak_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `864 + r * (8 ±0)` - // Estimated: `6806 + r * (8 ±0)` - // Minimum execution time: 243_583_000 picoseconds. - Weight::from_parts(270_025_058, 6806) - // Standard Error: 560 - .saturating_add(Weight::from_parts(767_519, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `867 + r * (8 ±0)` + // Estimated: `9284 + r * (8 ±0)` + // Minimum execution time: 275_247_000 picoseconds. + Weight::from_parts(286_675_317, 9284) + // Standard Error: 601 + .saturating_add(Weight::from_parts(788_160, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } @@ -3507,6 +3738,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3520,19 +3753,21 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_keccak_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872` - // Estimated: `6814` - // Minimum execution time: 253_798_000 picoseconds. - Weight::from_parts(265_542_351, 6814) - // Standard Error: 0 - .saturating_add(Weight::from_parts(3_343, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `875` + // Estimated: `9292` + // Minimum execution time: 281_585_000 picoseconds. + Weight::from_parts(287_637_844, 9292) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_351, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3546,13 +3781,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_hash_blake2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `864 + r * (8 ±0)` - // Estimated: `6808 + r * (8 ±0)` - // Minimum execution time: 247_332_000 picoseconds. - Weight::from_parts(269_183_656, 6808) - // Standard Error: 665 - .saturating_add(Weight::from_parts(443_386, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `867 + r * (8 ±0)` + // Estimated: `9286 + r * (8 ±0)` + // Minimum execution time: 273_678_000 picoseconds. + Weight::from_parts(289_879_306, 9286) + // Standard Error: 607 + .saturating_add(Weight::from_parts(439_482, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } @@ -3560,6 +3795,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3573,19 +3810,21 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_blake2_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872` - // Estimated: `6813` - // Minimum execution time: 250_855_000 picoseconds. - Weight::from_parts(258_752_975, 6813) + // Measured: `875` + // Estimated: `9291` + // Minimum execution time: 275_126_000 picoseconds. + Weight::from_parts(276_684_594, 9291) // Standard Error: 1 .saturating_add(Weight::from_parts(1_202, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3599,13 +3838,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_hash_blake2_128(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `864 + r * (8 ±0)` - // Estimated: `6805 + r * (8 ±0)` - // Minimum execution time: 240_733_000 picoseconds. - Weight::from_parts(269_134_358, 6805) - // Standard Error: 512 - .saturating_add(Weight::from_parts(440_043, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `867 + r * (8 ±0)` + // Estimated: `9283 + r * (8 ±0)` + // Minimum execution time: 273_229_000 picoseconds. + Weight::from_parts(287_793_841, 9283) + // Standard Error: 451 + .saturating_add(Weight::from_parts(447_922, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } @@ -3613,6 +3852,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3626,19 +3867,21 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_blake2_128_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872` - // Estimated: `6811` - // Minimum execution time: 247_377_000 picoseconds. - Weight::from_parts(261_077_322, 6811) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_195, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `875` + // Estimated: `9289` + // Minimum execution time: 277_843_000 picoseconds. + Weight::from_parts(279_900_099, 9289) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_204, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3652,13 +3895,13 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 125697]`. fn seal_sr25519_verify_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `997 + n * (1 ±0)` - // Estimated: `6934 + n * (1 ±0)` - // Minimum execution time: 307_337_000 picoseconds. - Weight::from_parts(326_710_473, 6934) - // Standard Error: 9 - .saturating_add(Weight::from_parts(5_765, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `1000 + n * (1 ±0)` + // Estimated: `9412 + n * (1 ±0)` + // Minimum execution time: 331_840_000 picoseconds. + Weight::from_parts(338_767_191, 9412) + // Standard Error: 11 + .saturating_add(Weight::from_parts(5_971, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -3666,6 +3909,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3679,13 +3924,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 160]`. fn seal_sr25519_verify(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `805 + r * (112 ±0)` - // Estimated: `6748 + r * (112 ±0)` - // Minimum execution time: 245_432_000 picoseconds. - Weight::from_parts(294_206_377, 6748) - // Standard Error: 7_229 - .saturating_add(Weight::from_parts(41_480_485, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `808 + r * (112 ±0)` + // Estimated: `9226 + r * (112 ±0)` + // Minimum execution time: 277_912_000 picoseconds. + Weight::from_parts(344_538_960, 9226) + // Standard Error: 13_422 + .saturating_add(Weight::from_parts(41_592_887, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 112).saturating_mul(r.into())) } @@ -3693,6 +3938,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3706,13 +3953,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 160]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `907 + r * (76 ±0)` - // Estimated: `6802 + r * (77 ±0)` - // Minimum execution time: 247_788_000 picoseconds. - Weight::from_parts(303_940_062, 6802) - // Standard Error: 10_671 - .saturating_add(Weight::from_parts(45_730_772, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `910 + r * (76 ±0)` + // Estimated: `9279 + r * (77 ±0)` + // Minimum execution time: 280_383_000 picoseconds. + Weight::from_parts(348_542_377, 9279) + // Standard Error: 13_985 + .saturating_add(Weight::from_parts(45_983_827, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 77).saturating_mul(r.into())) } @@ -3720,6 +3967,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3733,13 +3982,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 160]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `877 + r * (42 ±0)` - // Estimated: `6816 + r * (42 ±0)` - // Minimum execution time: 248_825_000 picoseconds. - Weight::from_parts(286_832_225, 6816) - // Standard Error: 5_274 - .saturating_add(Weight::from_parts(11_889_262, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `880 + r * (42 ±0)` + // Estimated: `9294 + r * (42 ±0)` + // Minimum execution time: 277_764_000 picoseconds. + Weight::from_parts(320_288_180, 9294) + // Standard Error: 10_140 + .saturating_add(Weight::from_parts(12_046_137, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 42).saturating_mul(r.into())) } @@ -3747,6 +3996,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1536 w:1536) @@ -3761,12 +4012,12 @@ impl WeightInfo for () { fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (965 ±0)` - // Estimated: `6807 + r * (3090 ±7)` - // Minimum execution time: 244_982_000 picoseconds. - Weight::from_parts(265_297_000, 6807) - // Standard Error: 39_895 - .saturating_add(Weight::from_parts(22_435_888, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Estimated: `9285 + r * (3090 ±7)` + // Minimum execution time: 271_356_000 picoseconds. + Weight::from_parts(282_924_000, 9285) + // Standard Error: 60_493 + .saturating_add(Weight::from_parts(28_319_267, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(r.into()))) @@ -3776,6 +4027,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:33 w:32) @@ -3789,22 +4042,24 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 32]`. fn lock_delegate_dependency(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `928 + r * (131 ±0)` - // Estimated: `6878 + r * (2606 ±0)` - // Minimum execution time: 246_455_000 picoseconds. - Weight::from_parts(275_334_919, 6878) - // Standard Error: 20_911 - .saturating_add(Weight::from_parts(6_427_525, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `937 + r * (131 ±0)` + // Estimated: `9346 + r * (2607 ±0)` + // Minimum execution time: 269_698_000 picoseconds. + Weight::from_parts(294_325_127, 9346) + // Standard Error: 22_352 + .saturating_add(Weight::from_parts(6_744_117, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 2606).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2607).saturating_mul(r.into())) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `MaxEncodedLen`) /// Storage: `Contracts::CodeInfoOf` (r:33 w:32) @@ -3818,13 +4073,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 32]`. fn unlock_delegate_dependency(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `969 + r * (183 ±0)` + // Measured: `972 + r * (184 ±0)` // Estimated: `129453 + r * (2568 ±0)` - // Minimum execution time: 254_472_000 picoseconds. - Weight::from_parts(280_657_909, 129453) - // Standard Error: 20_131 - .saturating_add(Weight::from_parts(5_644_006, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Minimum execution time: 261_226_000 picoseconds. + Weight::from_parts(294_299_527, 129453) + // Standard Error: 27_898 + .saturating_add(Weight::from_parts(6_031_601, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -3834,6 +4089,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3847,13 +4104,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `858 + r * (3 ±0)` - // Estimated: `6804 + r * (3 ±0)` - // Minimum execution time: 250_535_000 picoseconds. - Weight::from_parts(270_318_376, 6804) - // Standard Error: 386 - .saturating_add(Weight::from_parts(174_627, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `861 + r * (3 ±0)` + // Estimated: `9282 + r * (3 ±0)` + // Minimum execution time: 270_729_000 picoseconds. + Weight::from_parts(289_622_807, 9282) + // Standard Error: 394 + .saturating_add(Weight::from_parts(167_010, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } @@ -3861,6 +4118,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3874,13 +4133,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_account_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2109 + r * (39 ±0)` - // Estimated: `7899 + r * (40 ±0)` - // Minimum execution time: 248_174_000 picoseconds. - Weight::from_parts(301_826_520, 7899) - // Standard Error: 801 - .saturating_add(Weight::from_parts(248_479, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `2112 + r * (39 ±0)` + // Estimated: `10377 + r * (40 ±0)` + // Minimum execution time: 272_228_000 picoseconds. + Weight::from_parts(351_059_276, 10377) + // Standard Error: 1_761 + .saturating_add(Weight::from_parts(312_269, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) } @@ -3888,6 +4147,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3903,13 +4164,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_instantiation_nonce(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `861 + r * (3 ±0)` - // Estimated: `6801 + r * (3 ±0)` - // Minimum execution time: 246_540_000 picoseconds. - Weight::from_parts(268_913_509, 6801) - // Standard Error: 378 - .saturating_add(Weight::from_parts(154_950, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `864 + r * (3 ±0)` + // Estimated: `9279 + r * (3 ±0)` + // Minimum execution time: 272_497_000 picoseconds. + Weight::from_parts(288_213_060, 9279) + // Standard Error: 469 + .saturating_add(Weight::from_parts(155_530, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } @@ -3918,9 +4179,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_777_000 picoseconds. - Weight::from_parts(1_707_601, 0) - // Standard Error: 14 - .saturating_add(Weight::from_parts(15_392, 0).saturating_mul(r.into())) + // Minimum execution time: 1_990_000 picoseconds. + Weight::from_parts(1_778_221, 0) + // Standard Error: 26 + .saturating_add(Weight::from_parts(14_888, 0).saturating_mul(r.into())) } } diff --git a/substrate/frame/contracts/uapi/Cargo.toml b/substrate/frame/contracts/uapi/Cargo.toml index a5081af2a2d280cc1956dcf2dbb69de18588ccd4..12bb6b8fc2c0bddffee1cfbf7a3df0243ff421a6 100644 --- a/substrate/frame/contracts/uapi/Cargo.toml +++ b/substrate/frame/contracts/uapi/Cargo.toml @@ -21,7 +21,7 @@ scale = { package = "parity-scale-codec", version = "3.6.1", default-features = ], optional = true } [target.'cfg(target_arch = "riscv32")'.dependencies] -polkavm-derive = '0.5.0' +polkavm-derive = { workspace = true } [package.metadata.docs.rs] default-target = ["wasm32-unknown-unknown"] diff --git a/substrate/frame/contracts/uapi/src/host.rs b/substrate/frame/contracts/uapi/src/host.rs index c25be4479cefaac81e8cb25c6b56502f9e2fd83a..459cb59bead94fc5af18fb43955621f0e72d7e75 100644 --- a/substrate/frame/contracts/uapi/src/host.rs +++ b/substrate/frame/contracts/uapi/src/host.rs @@ -180,7 +180,7 @@ pub trait HostFn { flags: CallFlags, callee: &[u8], ref_time_limit: u64, - proof_time_limit: u64, + proof_size_limit: u64, deposit: Option<&[u8]>, value: &[u8], input_data: &[u8], @@ -790,7 +790,7 @@ pub trait HostFn { /// /// # Parameters /// - /// - `dest`: The XCM destination, should be decodable as [VersionedMultiLocation](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedMultiLocation.html), + /// - `dest`: The XCM destination, should be decodable as [MultiLocation](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedLocation.html), /// traps otherwise. /// - `msg`: The message, should be decodable as a [VersionedXcm](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedXcm.html), /// traps otherwise. diff --git a/substrate/frame/contracts/uapi/src/host/riscv32.rs b/substrate/frame/contracts/uapi/src/host/riscv32.rs index dbd5abc42409741af2679d99274d1cdf48f2c757..561ab28747df9e55d105d6db09f4776d5e453e28 100644 --- a/substrate/frame/contracts/uapi/src/host/riscv32.rs +++ b/substrate/frame/contracts/uapi/src/host/riscv32.rs @@ -130,7 +130,7 @@ impl HostFn for HostFnImpl { flags: CallFlags, callee: &[u8], ref_time_limit: u64, - proof_time_limit: u64, + proof_size_limit: u64, deposit: Option<&[u8]>, value: &[u8], input_data: &[u8], diff --git a/substrate/frame/contracts/uapi/src/host/wasm32.rs b/substrate/frame/contracts/uapi/src/host/wasm32.rs index 9651aa73d6f9bd3364432ed9135e460f62aa0b6c..bc697238061ab16e47d304bd9583f51f7eb99c0b 100644 --- a/substrate/frame/contracts/uapi/src/host/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/host/wasm32.rs @@ -223,7 +223,7 @@ mod sys { pub fn weight_to_fee( ref_time_limit: u64, - proof_time_limit: u64, + proof_size_limit: u64, output_ptr: *mut u8, output_len_ptr: *mut u32, ); @@ -239,7 +239,7 @@ mod sys { flags: u32, callee_ptr: *const u8, ref_time_limit: u64, - proof_time_limit: u64, + proof_size_limit: u64, deposit_ptr: *const u8, transferred_value_ptr: *const u8, input_data_ptr: *const u8, @@ -251,7 +251,7 @@ mod sys { pub fn instantiate( code_hash_ptr: *const u8, ref_time_limit: u64, - proof_time_limit: u64, + proof_size_limit: u64, deposit_ptr: *const u8, value_ptr: *const u8, input_ptr: *const u8, @@ -301,6 +301,7 @@ macro_rules! impl_wrapper_for { unsafe { $( $mod )::*::$name(output.as_mut_ptr(), &mut output_len); } + extract_from_slice(output, output_len as usize) } } }; @@ -487,7 +488,7 @@ impl HostFn for HostFnImpl { flags: CallFlags, callee: &[u8], ref_time_limit: u64, - proof_time_limit: u64, + proof_size_limit: u64, deposit: Option<&[u8]>, value: &[u8], input_data: &[u8], @@ -501,7 +502,7 @@ impl HostFn for HostFnImpl { flags.bits(), callee.as_ptr(), ref_time_limit, - proof_time_limit, + proof_size_limit, deposit_ptr, value.as_ptr(), input_data.as_ptr(), diff --git a/substrate/frame/conviction-voting/src/tests.rs b/substrate/frame/conviction-voting/src/tests.rs index dbcd643b60ff7c5ac2ec0a0a351650c69c200e37..74baeace898b09084a20b5e8e431efb2973c149f 100644 --- a/substrate/frame/conviction-voting/src/tests.rs +++ b/substrate/frame/conviction-voting/src/tests.rs @@ -39,7 +39,7 @@ frame_support::construct_runtime!( } ); -// Test that a fitlered call can be dispatched. +// Test that a filtered call can be dispatched. pub struct BaseFilter; impl Contains for BaseFilter { fn contains(call: &RuntimeCall) -> bool { @@ -47,7 +47,7 @@ impl Contains for BaseFilter { } } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; diff --git a/substrate/frame/core-fellowship/src/tests/integration.rs b/substrate/frame/core-fellowship/src/tests/integration.rs index 6f177ba66db37fa791b06eb3f9b7c10998af0b98..d3bbac158056e7edbbf1e08b255d63450fa1c5c0 100644 --- a/substrate/frame/core-fellowship/src/tests/integration.rs +++ b/substrate/frame/core-fellowship/src/tests/integration.rs @@ -51,7 +51,7 @@ parameter_types! { frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1_000_000, u64::max_value())); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } @@ -251,7 +251,7 @@ fn swap_exhaustive_works() { }); assert_eq!(root_add, root_swap); - // Ensure that we dont compare trivial stuff like `()` from a type error above. + // Ensure that we don't compare trivial stuff like `()` from a type error above. assert_eq!(root_add.len(), 32); }); } diff --git a/substrate/frame/core-fellowship/src/tests/unit.rs b/substrate/frame/core-fellowship/src/tests/unit.rs index de8cd858bdfc05d4881ff31331745906e001dc95..669517d61a4a963d3e021191ed3f9e561021b908 100644 --- a/substrate/frame/core-fellowship/src/tests/unit.rs +++ b/substrate/frame/core-fellowship/src/tests/unit.rs @@ -47,7 +47,7 @@ parameter_types! { frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1_000_000, u64::max_value())); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } diff --git a/substrate/frame/democracy/src/lib.rs b/substrate/frame/democracy/src/lib.rs index 089556191cd14eee735d12976ba6b0ed471a4eac..f3d33a72f3ad1a088d4dc050d1e637acd162db7d 100644 --- a/substrate/frame/democracy/src/lib.rs +++ b/substrate/frame/democracy/src/lib.rs @@ -211,7 +211,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] @@ -484,7 +484,7 @@ pub mod pallet { Blacklisted { proposal_hash: T::Hash }, /// An account has voted in a referendum Voted { voter: T::AccountId, ref_index: ReferendumIndex, vote: AccountVote> }, - /// An account has secconded a proposal + /// An account has seconded a proposal Seconded { seconder: T::AccountId, prop_index: PropIndex }, /// A proposal got canceled. ProposalCanceled { prop_index: PropIndex }, diff --git a/substrate/frame/democracy/src/migrations/unlock_and_unreserve_all_funds.rs b/substrate/frame/democracy/src/migrations/unlock_and_unreserve_all_funds.rs index 188c475f64d0ef3e55e6daf3f86f9423e93a4efa..1cb50a157b12bb5ea68fc870e870cda6536fd524 100644 --- a/substrate/frame/democracy/src/migrations/unlock_and_unreserve_all_funds.rs +++ b/substrate/frame/democracy/src/migrations/unlock_and_unreserve_all_funds.rs @@ -321,40 +321,40 @@ mod test { } #[test] - fn unreserve_works_for_depositer() { - let depositer_0 = 10; - let depositer_1 = 11; + fn unreserve_works_for_depositor() { + let depositor_0 = 10; + let depositor_1 = 11; let deposit = 25; - let depositer_0_initial_reserved = 0; - let depositer_1_initial_reserved = 15; + let depositor_0_initial_reserved = 0; + let depositor_1_initial_reserved = 15; let initial_balance = 100_000; new_test_ext().execute_with(|| { // Set up initial state. - ::Currency::make_free_balance_be(&depositer_0, initial_balance); - ::Currency::make_free_balance_be(&depositer_1, initial_balance); + ::Currency::make_free_balance_be(&depositor_0, initial_balance); + ::Currency::make_free_balance_be(&depositor_1, initial_balance); assert_ok!(::Currency::reserve( - &depositer_0, - depositer_0_initial_reserved + deposit + &depositor_0, + depositor_0_initial_reserved + deposit )); assert_ok!(::Currency::reserve( - &depositer_1, - depositer_1_initial_reserved + deposit + &depositor_1, + depositor_1_initial_reserved + deposit )); let depositors = BoundedVec::<_, ::MaxDeposits>::truncate_from(vec![ - depositer_0, - depositer_1, + depositor_0, + depositor_1, ]); DepositOf::::insert(0, (depositors, deposit)); // Sanity check: ensure initial reserved balance was set correctly. assert_eq!( - ::Currency::reserved_balance(&depositer_0), - depositer_0_initial_reserved + deposit + ::Currency::reserved_balance(&depositor_0), + depositor_0_initial_reserved + deposit ); assert_eq!( - ::Currency::reserved_balance(&depositer_1), - depositer_1_initial_reserved + deposit + ::Currency::reserved_balance(&depositor_1), + depositor_1_initial_reserved + deposit ); // Run the migration. @@ -365,12 +365,12 @@ mod test { // Assert the reserved balance was reduced by the expected amount. assert_eq!( - ::Currency::reserved_balance(&depositer_0), - depositer_0_initial_reserved + ::Currency::reserved_balance(&depositor_0), + depositor_0_initial_reserved ); assert_eq!( - ::Currency::reserved_balance(&depositer_1), - depositer_1_initial_reserved + ::Currency::reserved_balance(&depositor_1), + depositor_1_initial_reserved ); }); } diff --git a/substrate/frame/democracy/src/tests.rs b/substrate/frame/democracy/src/tests.rs index 973e0c28eb2f7307d8e18ff3470c7df8d106a609..e2946ba98156ad3480f85ae4e86ba4a2036ff59c 100644 --- a/substrate/frame/democracy/src/tests.rs +++ b/substrate/frame/democracy/src/tests.rs @@ -62,7 +62,7 @@ frame_support::construct_runtime!( } ); -// Test that a fitlered call can be dispatched. +// Test that a filtered call can be dispatched. pub struct BaseFilter; impl Contains for BaseFilter { fn contains(call: &RuntimeCall) -> bool { @@ -77,7 +77,7 @@ parameter_types! { ); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = BaseFilter; type Block = Block; diff --git a/substrate/frame/democracy/src/tests/cancellation.rs b/substrate/frame/democracy/src/tests/cancellation.rs index 4384fe6a1641aa2f51723c70caeac7093dcb1951..b4c42f9c79053ad6db1641e7e431dc4be7661e9f 100644 --- a/substrate/frame/democracy/src/tests/cancellation.rs +++ b/substrate/frame/democracy/src/tests/cancellation.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! The tests for cancelation functionality. +//! The tests for cancellation functionality. use super::*; diff --git a/substrate/frame/election-provider-multi-phase/src/helpers.rs b/substrate/frame/election-provider-multi-phase/src/helpers.rs index 57d580e93016cf9be6e1dede6ebbeb95c1609826..a3f27fc18f07730ebbe9907ddd897a17ccb876e2 100644 --- a/substrate/frame/election-provider-multi-phase/src/helpers.rs +++ b/substrate/frame/election-provider-multi-phase/src/helpers.rs @@ -160,7 +160,7 @@ pub fn target_index_fn_linear( } /// Create a function that can map a voter index ([`SolutionVoterIndexOf`]) to the actual voter -/// account using a linearly indexible snapshot. +/// account using a linearly indexable snapshot. pub fn voter_at_fn( snapshot: &Vec>, ) -> impl Fn(SolutionVoterIndexOf) -> Option + '_ { @@ -172,7 +172,7 @@ pub fn voter_at_fn( } /// Create a function that can map a target index ([`SolutionTargetIndexOf`]) to the actual target -/// account using a linearly indexible snapshot. +/// account using a linearly indexable snapshot. pub fn target_at_fn( snapshot: &Vec, ) -> impl Fn(SolutionTargetIndexOf) -> Option + '_ { diff --git a/substrate/frame/election-provider-multi-phase/src/lib.rs b/substrate/frame/election-provider-multi-phase/src/lib.rs index 4f43f89abed222f963156c2f5afaebb8288bf45a..11577cd3526233aca7efcc1da4f55d194785e300 100644 --- a/substrate/frame/election-provider-multi-phase/src/lib.rs +++ b/substrate/frame/election-provider-multi-phase/src/lib.rs @@ -586,10 +586,8 @@ pub mod pallet { type EstimateCallFee: EstimateCallFee, BalanceOf>; /// Duration of the unsigned phase. - #[pallet::constant] type UnsignedPhase: Get>; /// Duration of the signed phase. - #[pallet::constant] type SignedPhase: Get>; /// The minimum amount of improvement to the solution score that defines a solution as @@ -1134,7 +1132,7 @@ pub mod pallet { /// A solution was stored with the given compute. /// /// The `origin` indicates the origin of the solution. If `origin` is `Some(AccountId)`, - /// the stored solution was submited in the signed phase by a miner with the `AccountId`. + /// the stored solution was submitted in the signed phase by a miner with the `AccountId`. /// Otherwise, the solution was stored either during the unsigned phase or by /// `T::ForceOrigin`. The `bool` is `true` when a previous solution was ejected to make /// room for this one. @@ -1192,7 +1190,7 @@ pub mod pallet { BoundNotMet, /// Submitted solution has too many winners TooManyWinners, - /// Sumission was prepared for a different round. + /// Submission was prepared for a different round. PreDispatchDifferentRound, } @@ -1343,7 +1341,7 @@ pub mod pallet { #[pallet::getter(fn minimum_untrusted_score)] pub type MinimumUntrustedScore = StorageValue<_, ElectionScore>; - /// The current storage version. + /// The in-code storage version. /// /// v1: https://github.com/paritytech/substrate/pull/12237/ const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); diff --git a/substrate/frame/election-provider-multi-phase/src/migrations.rs b/substrate/frame/election-provider-multi-phase/src/migrations.rs index 50b821e6db6ae8c7a2cc68192f9dd8cb39e9f460..156f1c02e27cd23e3e37014650fdff66115cd18f 100644 --- a/substrate/frame/election-provider-multi-phase/src/migrations.rs +++ b/substrate/frame/election-provider-multi-phase/src/migrations.rs @@ -27,12 +27,12 @@ pub mod v1 { pub struct MigrateToV1(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); + let current = Pallet::::in_code_storage_version(); let onchain = Pallet::::on_chain_storage_version(); log!( info, - "Running migration with current storage version {:?} / onchain {:?}", + "Running migration with in-code storage version {:?} / onchain {:?}", current, onchain ); diff --git a/substrate/frame/election-provider-multi-phase/src/mock.rs b/substrate/frame/election-provider-multi-phase/src/mock.rs index 18dcd7061c1fc35c7cc64134c33363b746e9baf8..92b87d92e99b16c959e9d719e91251da94fa7576 100644 --- a/substrate/frame/election-provider-multi-phase/src/mock.rs +++ b/substrate/frame/election-provider-multi-phase/src/mock.rs @@ -208,7 +208,7 @@ pub fn witness() -> SolutionOrSnapshotSize { .unwrap_or_default() } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type SS58Prefix = (); type BaseCallFilter = frame_support::traits::Everything; diff --git a/substrate/frame/election-provider-multi-phase/src/unsigned.rs b/substrate/frame/election-provider-multi-phase/src/unsigned.rs index 94348181334061a3c2de81b90742f53c4390ddc9..8b25815eca13ec72491bdb1cb9135a69c540001f 100644 --- a/substrate/frame/election-provider-multi-phase/src/unsigned.rs +++ b/substrate/frame/election-provider-multi-phase/src/unsigned.rs @@ -1445,7 +1445,7 @@ mod tests { ) .unwrap(); let solution = RawSolution { solution: raw, score, round: MultiPhase::round() }; - // 12 is not better than 12. We need score of atleast 13 to be accepted. + // 12 is not better than 12. We need score of at least 13 to be accepted. assert_eq!(solution.score.minimal_stake, 12); // submitting this will panic. assert_noop!( @@ -1483,7 +1483,7 @@ mod tests { )); // trial 4: a solution who's minimal stake is 17, i.e. 4 better than the last - // soluton. + // solution. let result = ElectionResult { winners: vec![(10, 12)], assignments: vec![ diff --git a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs index 53bff50f7482fd61fb2c77594fa9c3a8957c0a8a..83083c912094bc4b39287f5105021fe2e44a4bb0 100644 --- a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs +++ b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs @@ -208,8 +208,8 @@ fn enters_emergency_phase_after_forcing_before_elect() { /// active validators. Thus, slashing a percentage of the current validators that is lower than /// `OffendingValidatorsThreshold` will never force a new era. However, as the slashes progress, if /// the subsequent elections do not meet the minimum election untrusted score, the election will -/// fail and enter in emenergency mode. -fn continous_slashes_below_offending_threshold() { +/// fail and enter in emergency mode. +fn continuous_slashes_below_offending_threshold() { let staking_builder = StakingExtBuilder::default().validator_count(10); let epm_builder = EpmExtBuilder::default().disable_emergency_throttling(); @@ -323,7 +323,7 @@ fn set_validation_intention_after_chilled() { } #[test] -/// Active ledger balance may fall below ED if account chills before unbounding. +/// Active ledger balance may fall below ED if account chills before unbonding. /// /// Unbonding call fails if the remaining ledger's stash balance falls below the existential /// deposit. However, if the stash is chilled before unbonding, the ledger's active balance may @@ -350,7 +350,7 @@ fn ledger_consistency_active_balance_below_ed() { // however, chilling works as expected. assert_ok!(Staking::chill(RuntimeOrigin::signed(11))); - // now unbonding the full active balance works, since remainer of the active balance is + // now unbonding the full active balance works, since remainder of the active balance is // not enforced to be below `MinNominatorBond` if the stash has been chilled. assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 1000)); @@ -479,7 +479,7 @@ fn automatic_unbonding_pools() { staking_events(), [ // auto-withdraw happened as expected to release 2's unbonding funds, but the funds - // were not transfered to 2 and stay in the pool's tranferrable balance instead. + // were not transferred to 2 and stay in the pool's transferrable balance instead. pallet_staking::Event::Withdrawn { stash: 7939698191839293293, amount: 10 }, pallet_staking::Event::Unbonded { stash: 7939698191839293293, amount: 10 } ] diff --git a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs index 882b894bb22fcdc38ab26c21301f34344b1c1093..7efcc4701e27eadf6ff64caa3e7fea8671c68114 100644 --- a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs +++ b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs @@ -86,7 +86,7 @@ pub(crate) type VoterIndex = u16; pub(crate) type TargetIndex = u16; pub(crate) type Moment = u32; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type Block = Block; type AccountData = pallet_balances::AccountData; @@ -167,7 +167,7 @@ parameter_types! { pub static SignedPhase: BlockNumber = 10; pub static UnsignedPhase: BlockNumber = 10; // we expect a minimum of 3 blocks in signed phase and unsigned phases before trying - // enetering in emergency phase after the election failed. + // entering in emergency phase after the election failed. pub static MinBlocksBeforeEmergency: BlockNumber = 3; pub static MaxActiveValidators: u32 = 1000; pub static OffchainRepeat: u32 = 5; @@ -661,7 +661,7 @@ pub fn roll_to(n: BlockNumber, delay_solution: bool) { Session::on_initialize(b); Timestamp::set_timestamp(System::block_number() * BLOCK_TIME + INIT_TIMESTAMP); - // TODO(gpestana): implement a realistic OCW worker insted of simulating it + // TODO(gpestana): implement a realistic OCW worker instead of simulating it // https://github.com/paritytech/substrate/issues/13589 // if there's no solution queued and the solution should not be delayed, try mining and // queue a solution. diff --git a/substrate/frame/election-provider-support/solution-type/fuzzer/Cargo.toml b/substrate/frame/election-provider-support/solution-type/fuzzer/Cargo.toml index 8a73dd38fa2df251fcd0ef5826f51b6cf56e0160..a27f0f7d4dd480fc8d6b10cd16c52c0acb617009 100644 --- a/substrate/frame/election-provider-support/solution-type/fuzzer/Cargo.toml +++ b/substrate/frame/election-provider-support/solution-type/fuzzer/Cargo.toml @@ -16,7 +16,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } honggfuzz = "0.5" rand = { version = "0.8", features = ["small_rng", "std"] } diff --git a/substrate/frame/election-provider-support/src/bounds.rs b/substrate/frame/election-provider-support/src/bounds.rs index b9ae21e49ca70b3700ad43603fbac38f217f75de..6b2423b7fece6bfd1273bab51578bb32032dff31 100644 --- a/substrate/frame/election-provider-support/src/bounds.rs +++ b/substrate/frame/election-provider-support/src/bounds.rs @@ -62,7 +62,7 @@ use sp_runtime::traits::Zero; /// Encapsulates the counting of things that can be bounded in an election, such as voters, /// targets or anything else. /// -/// This struct is defined mostly to prevent callers from mistankingly using `CountBound` instead of +/// This struct is defined mostly to prevent callers from mistakenly using `CountBound` instead of /// `SizeBound` and vice-versa. #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct CountBound(pub u32); @@ -96,7 +96,7 @@ impl Zero for CountBound { /// logic and implementation, but it most likely will represent bytes in SCALE encoding in this /// context. /// -/// This struct is defined mostly to prevent callers from mistankingly using `CountBound` instead of +/// This struct is defined mostly to prevent callers from mistakenly using `CountBound` instead of /// `SizeBound` and vice-versa. #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct SizeBound(pub u32); diff --git a/substrate/frame/election-provider-support/src/onchain.rs b/substrate/frame/election-provider-support/src/onchain.rs index d937f42cb405f5726307be734db6b33a3f702d66..ee4f6992a085e677aeb8dc2cafbc5637c135ebfd 100644 --- a/substrate/frame/election-provider-support/src/onchain.rs +++ b/substrate/frame/election-provider-support/src/onchain.rs @@ -199,7 +199,7 @@ mod tests { } ); - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type SS58Prefix = (); type BaseCallFilter = frame_support::traits::Everything; diff --git a/substrate/frame/elections-phragmen/src/lib.rs b/substrate/frame/elections-phragmen/src/lib.rs index a078361a5f713b5d267fdc1002ac361f95a3faef..b4be07030efbbf3d52405320830f86f524c9b0ad 100644 --- a/substrate/frame/elections-phragmen/src/lib.rs +++ b/substrate/frame/elections-phragmen/src/lib.rs @@ -188,7 +188,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] @@ -1316,7 +1316,7 @@ mod tests { use sp_runtime::{testing::Header, BuildStorage}; use substrate_test_utils::assert_eq_uvec; - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; diff --git a/substrate/frame/examples/Cargo.toml b/substrate/frame/examples/Cargo.toml index eb6355edd312a1ff5f865c235ffd034f085a3027..45c7440eb89135eca98cf00a4aaf5d6ad2c094ca 100644 --- a/substrate/frame/examples/Cargo.toml +++ b/substrate/frame/examples/Cargo.toml @@ -23,6 +23,7 @@ pallet-example-frame-crate = { path = "frame-crate", default-features = false } pallet-example-kitchensink = { path = "kitchensink", default-features = false } pallet-example-offchain-worker = { path = "offchain-worker", default-features = false } pallet-example-split = { path = "split", default-features = false } +pallet-example-single-block-migrations = { path = "single-block-migrations", default-features = false } pallet-example-tasks = { path = "tasks", default-features = false } [features] @@ -34,6 +35,7 @@ std = [ "pallet-example-frame-crate/std", "pallet-example-kitchensink/std", "pallet-example-offchain-worker/std", + "pallet-example-single-block-migrations/std", "pallet-example-split/std", "pallet-example-tasks/std", ] @@ -43,6 +45,7 @@ try-runtime = [ "pallet-example-basic/try-runtime", "pallet-example-kitchensink/try-runtime", "pallet-example-offchain-worker/try-runtime", + "pallet-example-single-block-migrations/try-runtime", "pallet-example-split/try-runtime", "pallet-example-tasks/try-runtime", ] diff --git a/substrate/frame/examples/basic/src/benchmarking.rs b/substrate/frame/examples/basic/src/benchmarking.rs index 4b2ebb41fbda1d6d8a0d12a65b32391510f1a488..65ca3089aba4247cae65932cd4bca9b68d520b75 100644 --- a/substrate/frame/examples/basic/src/benchmarking.rs +++ b/substrate/frame/examples/basic/src/benchmarking.rs @@ -48,7 +48,7 @@ mod benchmarks { set_dummy(RawOrigin::Root, value); // The execution phase is just running `set_dummy` extrinsic call // This is the optional benchmark verification phase, asserting certain states. - assert_eq!(Pallet::::dummy(), Some(value)) + assert_eq!(Dummy::::get(), Some(value)) } // An example method that returns a Result that can be called within a benchmark diff --git a/substrate/frame/examples/basic/src/lib.rs b/substrate/frame/examples/basic/src/lib.rs index dad4d01978f95391e88cd99dce82f86f9d942e87..12cadc969fd74288fc2ca99ac014fa98a603d56d 100644 --- a/substrate/frame/examples/basic/src/lib.rs +++ b/substrate/frame/examples/basic/src/lib.rs @@ -286,9 +286,7 @@ pub mod pallet { let _sender = ensure_signed(origin)?; // Read the value of dummy from storage. - // let dummy = Self::dummy(); - // Will also work using the `::get` on the storage item type itself: - // let dummy = >::get(); + // let dummy = Dummy::::get(); // Calculate the new value. // let new_dummy = dummy.map_or(increase_by, |dummy| dummy + increase_by); @@ -381,20 +379,14 @@ pub mod pallet { // - `Foo::put(1); Foo::get()` returns `1`; // - `Foo::kill(); Foo::get()` returns `0` (u32::default()). #[pallet::storage] - // The getter attribute generate a function on `Pallet` placeholder: - // `fn getter_name() -> Type` for basic value items or - // `fn getter_name(key: KeyType) -> ValueType` for map items. - #[pallet::getter(fn dummy)] pub(super) type Dummy = StorageValue<_, T::Balance>; // A map that has enumerable entries. #[pallet::storage] - #[pallet::getter(fn bar)] pub(super) type Bar = StorageMap<_, Blake2_128Concat, T::AccountId, T::Balance>; // this one uses the query kind: `ValueQuery`, we'll demonstrate the usage of 'mutate' API. #[pallet::storage] - #[pallet::getter(fn foo)] pub(super) type Foo = StorageValue<_, T::Balance, ValueQuery>; #[pallet::storage] @@ -433,10 +425,10 @@ impl Pallet { fn accumulate_foo(origin: T::RuntimeOrigin, increase_by: T::Balance) -> DispatchResult { let _sender = ensure_signed(origin)?; - let prev = >::get(); + let prev = Foo::::get(); // Because Foo has 'default', the type of 'foo' in closure is the raw type instead of an // Option<> type. - let result = >::mutate(|foo| { + let result = Foo::::mutate(|foo| { *foo = foo.saturating_add(increase_by); *foo }); diff --git a/substrate/frame/examples/basic/src/tests.rs b/substrate/frame/examples/basic/src/tests.rs index 9434ace35ffe35467a9c513d67f0450e5b6ade6e..de37bcf75569575b5eb42336424af2e3a917bf1d 100644 --- a/substrate/frame/examples/basic/src/tests.rs +++ b/substrate/frame/examples/basic/src/tests.rs @@ -45,7 +45,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); @@ -119,25 +119,25 @@ fn it_works_for_optional_value() { // Check that GenesisBuilder works properly. let val1 = 42; let val2 = 27; - assert_eq!(Example::dummy(), Some(val1)); + assert_eq!(Dummy::::get(), Some(val1)); // Check that accumulate works when we have Some value in Dummy already. assert_ok!(Example::accumulate_dummy(RuntimeOrigin::signed(1), val2)); - assert_eq!(Example::dummy(), Some(val1 + val2)); + assert_eq!(Dummy::::get(), Some(val1 + val2)); // Check that accumulate works when we Dummy has None in it. >::on_initialize(2); assert_ok!(Example::accumulate_dummy(RuntimeOrigin::signed(1), val1)); - assert_eq!(Example::dummy(), Some(val1 + val2 + val1)); + assert_eq!(Dummy::::get(), Some(val1 + val2 + val1)); }); } #[test] fn it_works_for_default_value() { new_test_ext().execute_with(|| { - assert_eq!(Example::foo(), 24); + assert_eq!(Foo::::get(), 24); assert_ok!(Example::accumulate_foo(RuntimeOrigin::signed(1), 1)); - assert_eq!(Example::foo(), 25); + assert_eq!(Foo::::get(), 25); }); } @@ -146,7 +146,7 @@ fn set_dummy_works() { new_test_ext().execute_with(|| { let test_val = 133; assert_ok!(Example::set_dummy(RuntimeOrigin::root(), test_val.into())); - assert_eq!(Example::dummy(), Some(test_val)); + assert_eq!(Dummy::::get(), Some(test_val)); }); } diff --git a/substrate/frame/examples/default-config/src/lib.rs b/substrate/frame/examples/default-config/src/lib.rs index cd1653e6c764358165c8306d4810c1d4c9bea3b4..5b66c78e06283a2d9155dd76a653a7ca4649bbbf 100644 --- a/substrate/frame/examples/default-config/src/lib.rs +++ b/substrate/frame/examples/default-config/src/lib.rs @@ -87,12 +87,11 @@ pub mod pallet { // This will help use not need to disambiguate anything when using `derive_impl`. use super::*; use frame_support::derive_impl; - use frame_system::config_preludes::TestDefaultConfig as SystemTestDefaultConfig; /// A type providing default configurations for this pallet in testing environment. pub struct TestDefaultConfig; - #[derive_impl(SystemTestDefaultConfig as frame_system::DefaultConfig, no_aggregated_types)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)] impl frame_system::DefaultConfig for TestDefaultConfig {} #[frame_support::register_default_impl(TestDefaultConfig)] @@ -108,13 +107,13 @@ pub mod pallet { } /// A type providing default configurations for this pallet in another environment. Examples - /// could be a parachain, or a solo-chain. + /// could be a parachain, or a solochain. /// /// Appropriate derive for `frame_system::DefaultConfig` needs to be provided. In this /// example, we simple derive `frame_system::config_preludes::TestDefaultConfig` again. pub struct OtherDefaultConfig; - #[derive_impl(SystemTestDefaultConfig as frame_system::DefaultConfig, no_aggregated_types)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)] impl frame_system::DefaultConfig for OtherDefaultConfig {} #[frame_support::register_default_impl(OtherDefaultConfig)] @@ -149,7 +148,7 @@ pub mod tests { } ); - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { // these items are defined by frame-system as `no_default`, so we must specify them here. type Block = Block; diff --git a/substrate/frame/examples/dev-mode/src/tests.rs b/substrate/frame/examples/dev-mode/src/tests.rs index c13152533fdbb9bbbcf624ae9ed2c8fa9617242d..1c79b5f5fa6050758f5158bdcbede75e914eaf97 100644 --- a/substrate/frame/examples/dev-mode/src/tests.rs +++ b/substrate/frame/examples/dev-mode/src/tests.rs @@ -39,7 +39,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/frame/examples/frame-crate/src/lib.rs b/substrate/frame/examples/frame-crate/src/lib.rs index 7e286df1e32ced104d4f5e41a6cd63645770c117..781cba5658d778f991c990634ceff52838be559f 100644 --- a/substrate/frame/examples/frame-crate/src/lib.rs +++ b/substrate/frame/examples/frame-crate/src/lib.rs @@ -55,7 +55,7 @@ mod tests { } ); - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type Block = MockBlock; } diff --git a/substrate/frame/examples/kitchensink/src/benchmarking.rs b/substrate/frame/examples/kitchensink/src/benchmarking.rs index 24da581fc967b8e8c8586bf3b41a4452a89a4dcd..5f1d378e06fe67ee3978124ce7b557ad39b89e0a 100644 --- a/substrate/frame/examples/kitchensink/src/benchmarking.rs +++ b/substrate/frame/examples/kitchensink/src/benchmarking.rs @@ -51,7 +51,7 @@ mod benchmarks { set_foo(RawOrigin::Root, value, 10u128); // The execution phase is just running `set_foo` extrinsic call // This is the optional benchmark verification phase, asserting certain states. - assert_eq!(Pallet::::foo(), Some(value)) + assert_eq!(Foo::::get(), Some(value)) } // This line generates test cases for benchmarking, and could be run by: diff --git a/substrate/frame/examples/kitchensink/src/lib.rs b/substrate/frame/examples/kitchensink/src/lib.rs index 18429bc967d7c1e1f7b181e708ca3a86ef6f251c..b7425b0c0846afc3e9488a490144a89e7b647301 100644 --- a/substrate/frame/examples/kitchensink/src/lib.rs +++ b/substrate/frame/examples/kitchensink/src/lib.rs @@ -125,7 +125,6 @@ pub mod pallet { #[pallet::storage] #[pallet::unbounded] // optional #[pallet::storage_prefix = "OtherFoo"] // optional - #[pallet::getter(fn foo)] // optional pub type Foo = StorageValue; #[pallet::type_value] diff --git a/substrate/frame/examples/kitchensink/src/tests.rs b/substrate/frame/examples/kitchensink/src/tests.rs index 7f62671893087119a4aff817689f54e5066d7384..1205fefc422983d2b6f621b601a96b786353f83a 100644 --- a/substrate/frame/examples/kitchensink/src/tests.rs +++ b/substrate/frame/examples/kitchensink/src/tests.rs @@ -37,7 +37,7 @@ frame_support::construct_runtime!( /// Using a default config for [`frame_system`] in tests. See `default-config` example for more /// details. -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; diff --git a/substrate/frame/examples/offchain-worker/src/lib.rs b/substrate/frame/examples/offchain-worker/src/lib.rs index 6c1fa6ea8ec42dbee21875a0610b3724aadff802..0a90e896188ebe111baafd46b265d7e8d8790d9e 100644 --- a/substrate/frame/examples/offchain-worker/src/lib.rs +++ b/substrate/frame/examples/offchain-worker/src/lib.rs @@ -332,7 +332,6 @@ pub mod pallet { /// /// This is used to calculate average price, should have bounded size. #[pallet::storage] - #[pallet::getter(fn prices)] pub(super) type Prices = StorageValue<_, BoundedVec, ValueQuery>; /// Defines the block when next unsigned transaction will be accepted. @@ -341,7 +340,6 @@ pub mod pallet { /// we only allow one transaction every `T::UnsignedInterval` blocks. /// This storage entry defines when new transaction is going to be accepted. #[pallet::storage] - #[pallet::getter(fn next_unsigned_at)] pub(super) type NextUnsignedAt = StorageValue<_, BlockNumberFor, ValueQuery>; } @@ -479,7 +477,7 @@ impl Pallet { ) -> Result<(), &'static str> { // Make sure we don't fetch the price if unsigned transaction is going to be rejected // anyway. - let next_unsigned_at = >::get(); + let next_unsigned_at = NextUnsignedAt::::get(); if next_unsigned_at > block_number { return Err("Too early to send unsigned transaction") } @@ -497,7 +495,7 @@ impl Pallet { // Here we showcase two ways to send an unsigned transaction / unsigned payload (raw) // // By default unsigned transactions are disallowed, so we need to whitelist this case - // by writing `UnsignedValidator`. Note that it's EXTREMELY important to carefuly + // by writing `UnsignedValidator`. Note that it's EXTREMELY important to carefully // implement unsigned validation logic, as any mistakes can lead to opening DoS or spam // attack vectors. See validation logic docs for more details. // @@ -513,7 +511,7 @@ impl Pallet { ) -> Result<(), &'static str> { // Make sure we don't fetch the price if unsigned transaction is going to be rejected // anyway. - let next_unsigned_at = >::get(); + let next_unsigned_at = NextUnsignedAt::::get(); if next_unsigned_at > block_number { return Err("Too early to send unsigned transaction") } @@ -543,7 +541,7 @@ impl Pallet { ) -> Result<(), &'static str> { // Make sure we don't fetch the price if unsigned transaction is going to be rejected // anyway. - let next_unsigned_at = >::get(); + let next_unsigned_at = NextUnsignedAt::::get(); if next_unsigned_at > block_number { return Err("Too early to send unsigned transaction") } @@ -664,7 +662,7 @@ impl Pallet { /// Calculate current average price. fn average_price() -> Option { - let prices = >::get(); + let prices = Prices::::get(); if prices.is_empty() { None } else { @@ -677,7 +675,7 @@ impl Pallet { new_price: &u32, ) -> TransactionValidity { // Now let's check if the transaction has any chance to succeed. - let next_unsigned_at = >::get(); + let next_unsigned_at = NextUnsignedAt::::get(); if &next_unsigned_at > block_number { return InvalidTransaction::Stale.into() } diff --git a/substrate/frame/examples/offchain-worker/src/tests.rs b/substrate/frame/examples/offchain-worker/src/tests.rs index ea37a2da493d69ae6550c8d999fe2eb9dcfefad1..3525b3b67edf1569f00600e1e210e426c5f858dc 100644 --- a/substrate/frame/examples/offchain-worker/src/tests.rs +++ b/substrate/frame/examples/offchain-worker/src/tests.rs @@ -46,7 +46,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/frame/examples/single-block-migrations/Cargo.toml b/substrate/frame/examples/single-block-migrations/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..613742a6787c9aa91b2d4dffa55b25afdd675c7c --- /dev/null +++ b/substrate/frame/examples/single-block-migrations/Cargo.toml @@ -0,0 +1,61 @@ +[package] +name = "pallet-example-single-block-migrations" +version = "0.0.1" +authors.workspace = true +edition.workspace = true +license = "MIT-0" +homepage = "https://substrate.io" +repository.workspace = true +description = "FRAME example pallet demonstrating best-practices for writing storage migrations." +publish = false + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +docify = { version = "0.2.3", default-features = false } +log = { version = "0.4.21", default-features = false } +codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +frame-support = { path = "../../support", default-features = false } +frame-executive = { path = "../../executive", default-features = false } +frame-system = { path = "../../system", default-features = false } +frame-try-runtime = { path = "../../try-runtime", default-features = false, optional = true } +pallet-balances = { path = "../../balances", default-features = false } +sp-std = { path = "../../../primitives/std", default-features = false } +sp-runtime = { path = "../../../primitives/runtime", default-features = false } +sp-core = { path = "../../../primitives/core", default-features = false } +sp-io = { path = "../../../primitives/io", default-features = false } +sp-version = { path = "../../../primitives/version", default-features = false } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-executive/std", + "frame-support/std", + "frame-system/std", + "frame-try-runtime/std", + "log/std", + "pallet-balances/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "sp-version/std", +] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "frame-executive/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "frame-try-runtime/try-runtime", + "pallet-balances/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/examples/single-block-migrations/src/lib.rs b/substrate/frame/examples/single-block-migrations/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..b36d52622678d85659692e6bbd5b439899e383f9 --- /dev/null +++ b/substrate/frame/examples/single-block-migrations/src/lib.rs @@ -0,0 +1,213 @@ +// 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. + +//! # Single Block Migration Example Pallet +//! +//! An example pallet demonstrating best-practices for writing single-block migrations in the +//! context of upgrading pallet storage. +//! +//! ## Forewarning +//! +//! Single block migrations **MUST** execute in a single block, therefore when executed on a +//! parachain are only appropriate when guaranteed to not exceed block weight limits. If a +//! parachain submits a block that exceeds the block weight limit it will **brick the chain**! +//! +//! If weight is a concern or you are not sure which type of migration to use, you should probably +//! use a multi-block migration. +//! +//! TODO: Link above to multi-block migration example. +//! +//! ## Pallet Overview +//! +//! This example pallet contains a single storage item [`Value`](pallet::Value), which may be set by +//! any signed origin by calling the [`set_value`](crate::Call::set_value) extrinsic. +//! +//! For the purposes of this exercise, we imagine that in [`StorageVersion`] V0 of this pallet +//! [`Value`](pallet::Value) is a `u32`, and this what is currently stored on-chain. +//! +//! ```ignore +//! // (Old) Storage Version V0 representation of `Value` +//! #[pallet::storage] +//! pub type Value = StorageValue<_, u32>; +//! ``` +//! +//! In [`StorageVersion`] V1 of the pallet a new struct [`CurrentAndPreviousValue`] is introduced: +#![doc = docify::embed!("src/lib.rs", CurrentAndPreviousValue)] +//! and [`Value`](pallet::Value) is updated to store this new struct instead of a `u32`: +#![doc = docify::embed!("src/lib.rs", Value)] +//! +//! In StorageVersion V1 of the pallet when [`set_value`](crate::Call::set_value) is called, the +//! new value is stored in the `current` field of [`CurrentAndPreviousValue`], and the previous +//! value (if it exists) is stored in the `previous` field. +#![doc = docify::embed!("src/lib.rs", pallet_calls)] +//! +//! ## Why a migration is necessary +//! +//! Without a migration, there will be a discrepancy between the on-chain storage for [`Value`] (in +//! V0 it is a `u32`) and the current storage for [`Value`] (in V1 it was changed to a +//! [`CurrentAndPreviousValue`] struct). +//! +//! The on-chain storage for [`Value`] would be a `u32` but the runtime would try to read it as a +//! [`CurrentAndPreviousValue`]. This would result in unacceptable undefined behavior. +//! +//! ## Adding a migration module +//! +//! Writing a pallets migrations in a separate module is strongly recommended. +//! +//! Here's how the migration module is defined for this pallet: +//! +//! ```text +//! substrate/frame/examples/single-block-migrations/src/ +//! ├── lib.rs <-- pallet definition +//! ├── Cargo.toml <-- pallet manifest +//! └── migrations/ +//! ├── mod.rs <-- migrations module definition +//! └── v1.rs <-- migration logic for the V0 to V1 transition +//! ``` +//! +//! This structure allows keeping migration logic separate from the pallet logic and +//! easily adding new migrations in the future. +//! +//! ## Writing the Migration +//! +//! All code related to the migration can be found under +//! [`v1.rs`](migrations::v1). +//! +//! See the migration source code for detailed comments. +//! +//! To keep the migration logic organised, it is split across additional modules: +//! +//! ### `mod v0` +//! +//! Here we define a [`storage_alias`](frame_support::storage_alias) for the old v0 [`Value`] +//! format. +//! +//! This allows reading the old v0 value from storage during the migration. +//! +//! ### `mod version_unchecked` +//! +//! Here we define our raw migration logic, +//! `version_unchecked::MigrateV0ToV1` which implements the [`OnRuntimeUpgrade`] trait. +//! +//! Importantly, it is kept in a private module so that it cannot be accidentally used in a runtime. +//! +//! Private modules cannot be referenced in docs, so please read the code directly. +//! +//! #### Standalone Struct or Pallet Hook? +//! +//! Note that the storage migration logic is attached to a standalone struct implementing +//! [`OnRuntimeUpgrade`], rather than implementing the +//! [`Hooks::on_runtime_upgrade`](frame_support::traits::Hooks::on_runtime_upgrade) hook directly on +//! the pallet. The pallet hook is better suited for special types of logic that need to execute on +//! every runtime upgrade, but not so much for one-off storage migrations. +//! +//! ### `pub mod versioned` +//! +//! Here, `version_unchecked::MigrateV0ToV1` is wrapped in a +//! [`VersionedMigration`] to define +//! [`versioned::MigrateV0ToV1`](crate::migrations::v1::versioned::MigrateV0ToV1), which may be used +//! in runtimes. +//! +//! Using [`VersionedMigration`] ensures that +//! - The migration only runs once when the on-chain storage version is `0` +//! - The on-chain storage version is updated to `1` after the migration executes +//! - Reads and writes from checking and setting the on-chain storage version are accounted for in +//! the final [`Weight`](frame_support::weights::Weight) +//! +//! This is the only public module exported from `v1`. +//! +//! ### `mod test` +//! +//! Here basic unit tests are defined for the migration. +//! +//! When writing migration tests, don't forget to check: +//! - `on_runtime_upgrade` returns the expected weight +//! - `post_upgrade` succeeds when given the bytes returned by `pre_upgrade` +//! - Pallet storage is in the expected state after the migration +//! +//! [`VersionedMigration`]: frame_support::migrations::VersionedMigration +//! [`GetStorageVersion`]: frame_support::traits::GetStorageVersion +//! [`OnRuntimeUpgrade`]: frame_support::traits::OnRuntimeUpgrade +//! [`MigrateV0ToV1`]: crate::migrations::v1::versioned::MigrationV0ToV1 + +// We make sure this pallet uses `no_std` for compiling to Wasm. +#![cfg_attr(not(feature = "std"), no_std)] +// allow non-camel-case names for storage version V0 value +#![allow(non_camel_case_types)] + +// Re-export pallet items so that they can be accessed from the crate namespace. +pub use pallet::*; + +// Export migrations so they may be used in the runtime. +pub mod migrations; +#[doc(hidden)] +mod mock; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::traits::StorageVersion; +use sp_runtime::RuntimeDebug; + +/// Example struct holding the most recently set [`u32`] and the +/// second most recently set [`u32`] (if one existed). +#[docify::export] +#[derive( + Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, scale_info::TypeInfo, MaxEncodedLen, +)] +pub struct CurrentAndPreviousValue { + /// The most recently set value. + pub current: u32, + /// The previous value, if one existed. + pub previous: Option, +} + +// Pallet for demonstrating storage migrations. +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + /// Define the current [`StorageVersion`] of the pallet. + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config {} + + /// [`StorageVersion`] V1 of [`Value`]. + /// + /// Currently used. + #[docify::export] + #[pallet::storage] + pub type Value = StorageValue<_, CurrentAndPreviousValue>; + + #[docify::export(pallet_calls)] + #[pallet::call] + impl Pallet { + pub fn set_value(origin: OriginFor, value: u32) -> DispatchResult { + ensure_signed(origin)?; + + let previous = Value::::get().map(|v| v.current); + let new_struct = CurrentAndPreviousValue { current: value, previous }; + >::put(new_struct); + + Ok(()) + } + } +} diff --git a/substrate/frame/examples/single-block-migrations/src/migrations/mod.rs b/substrate/frame/examples/single-block-migrations/src/migrations/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..80a33f69941aa1a49563971ab0c9e13a88ecda19 --- /dev/null +++ b/substrate/frame/examples/single-block-migrations/src/migrations/mod.rs @@ -0,0 +1,20 @@ +// 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. + +/// Module containing all logic associated with the example migration from +/// [`StorageVersion`](frame_support::traits::StorageVersion) V0 to V1. +pub mod v1; diff --git a/substrate/frame/examples/single-block-migrations/src/migrations/v1.rs b/substrate/frame/examples/single-block-migrations/src/migrations/v1.rs new file mode 100644 index 0000000000000000000000000000000000000000..b46640a320207551ab55b1dc793574ce68c4c26c --- /dev/null +++ b/substrate/frame/examples/single-block-migrations/src/migrations/v1.rs @@ -0,0 +1,222 @@ +// 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. + +use frame_support::{ + storage_alias, + traits::{Get, OnRuntimeUpgrade}, +}; + +#[cfg(feature = "try-runtime")] +use sp_std::vec::Vec; + +/// Collection of storage item formats from the previous storage version. +/// +/// Required so we can read values in the v0 storage format during the migration. +mod v0 { + use super::*; + + /// V0 type for [`crate::Value`]. + #[storage_alias] + pub type Value = StorageValue, u32>; +} + +/// Private module containing *version unchecked* migration logic. +/// +/// Should only be used by the [`VersionedMigration`](frame_support::migrations::VersionedMigration) +/// type in this module to create something to export. +/// +/// The unversioned migration should be kept private so the unversioned migration cannot +/// accidentally be used in any runtimes. +/// +/// For more about this pattern of keeping items private, see +/// - +/// - +mod version_unchecked { + use super::*; + + /// Implements [`OnRuntimeUpgrade`], migrating the state of this pallet from V0 to V1. + /// + /// In V0 of the template [`crate::Value`] is just a `u32`. In V1, it has been upgraded to + /// contain the struct [`crate::CurrentAndPreviousValue`]. + /// + /// In this migration, update the on-chain storage for the pallet to reflect the new storage + /// layout. + pub struct MigrateV0ToV1(sp_std::marker::PhantomData); + + impl OnRuntimeUpgrade for MigrateV0ToV1 { + /// Return the existing [`crate::Value`] so we can check that it was correctly set in + /// `version_unchecked::MigrateV0ToV1::post_upgrade`. + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + use codec::Encode; + + // Access the old value using the `storage_alias` type + let old_value = v0::Value::::get(); + // Return it as an encoded `Vec` + Ok(old_value.encode()) + } + + /// Migrate the storage from V0 to V1. + /// + /// - If the value doesn't exist, there is nothing to do. + /// - If the value exists, it is read and then written back to storage inside a + /// [`crate::CurrentAndPreviousValue`]. + fn on_runtime_upgrade() -> frame_support::weights::Weight { + // Read the old value from storage + if let Some(old_value) = v0::Value::::take() { + // Write the new value to storage + let new = crate::CurrentAndPreviousValue { current: old_value, previous: None }; + crate::Value::::put(new); + // One read for the old value, one write for the new value + T::DbWeight::get().reads_writes(1, 1) + } else { + // One read for trying to access the old value + T::DbWeight::get().reads(1) + } + } + + /// Verifies the storage was migrated correctly. + /// + /// - If there was no old value, the new value should not be set. + /// - If there was an old value, the new value should be a + /// [`crate::CurrentAndPreviousValue`]. + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + use codec::Decode; + use frame_support::ensure; + + let maybe_old_value = Option::::decode(&mut &state[..]).map_err(|_| { + sp_runtime::TryRuntimeError::Other("Failed to decode old value from storage") + })?; + + match maybe_old_value { + Some(old_value) => { + let expected_new_value = + crate::CurrentAndPreviousValue { current: old_value, previous: None }; + let actual_new_value = crate::Value::::get(); + + ensure!(actual_new_value.is_some(), "New value not set"); + ensure!( + actual_new_value == Some(expected_new_value), + "New value not set correctly" + ); + }, + None => { + ensure!(crate::Value::::get().is_none(), "New value unexpectedly set"); + }, + }; + Ok(()) + } + } +} + +/// Public module containing *version checked* migration logic. +/// +/// This is the only module that should be exported from this module. +/// +/// See [`VersionedMigration`](frame_support::migrations::VersionedMigration) docs for more about +/// how it works. +pub mod versioned { + use super::*; + + /// `version_unchecked::MigrateV0ToV1` wrapped in a + /// [`VersionedMigration`](frame_support::migrations::VersionedMigration), which ensures that: + /// - The migration only runs once when the on-chain storage version is 0 + /// - The on-chain storage version is updated to `1` after the migration executes + /// - Reads/Writes from checking/settings the on-chain storage version are accounted for + pub type MigrateV0ToV1 = frame_support::migrations::VersionedMigration< + 0, // The migration will only execute when the on-chain storage version is 0 + 1, // The on-chain storage version will be set to 1 after the migration is complete + version_unchecked::MigrateV0ToV1, + crate::pallet::Pallet, + ::DbWeight, + >; +} + +/// Tests for our migration. +/// +/// When writing migration tests, it is important to check: +/// 1. `on_runtime_upgrade` returns the expected weight +/// 2. `post_upgrade` succeeds when given the bytes returned by `pre_upgrade` +/// 3. The storage is in the expected state after the migration +#[cfg(any(all(feature = "try-runtime", test), doc))] +mod test { + use super::*; + use crate::mock::{new_test_ext, MockRuntime}; + use frame_support::assert_ok; + use version_unchecked::MigrateV0ToV1; + + #[test] + fn handles_no_existing_value() { + new_test_ext().execute_with(|| { + // By default, no value should be set. Verify this assumption. + assert!(crate::Value::::get().is_none()); + assert!(v0::Value::::get().is_none()); + + // Get the pre_upgrade bytes + let bytes = match MigrateV0ToV1::::pre_upgrade() { + Ok(bytes) => bytes, + Err(e) => panic!("pre_upgrade failed: {:?}", e), + }; + + // Execute the migration + let weight = MigrateV0ToV1::::on_runtime_upgrade(); + + // Verify post_upgrade succeeds + assert_ok!(MigrateV0ToV1::::post_upgrade(bytes)); + + // The weight should be just 1 read for trying to access the old value. + assert_eq!(weight, ::DbWeight::get().reads(1)); + + // After the migration, no value should have been set. + assert!(crate::Value::::get().is_none()); + }) + } + + #[test] + fn handles_existing_value() { + new_test_ext().execute_with(|| { + // Set up an initial value + let initial_value = 42; + v0::Value::::put(initial_value); + + // Get the pre_upgrade bytes + let bytes = match MigrateV0ToV1::::pre_upgrade() { + Ok(bytes) => bytes, + Err(e) => panic!("pre_upgrade failed: {:?}", e), + }; + + // Execute the migration + let weight = MigrateV0ToV1::::on_runtime_upgrade(); + + // Verify post_upgrade succeeds + assert_ok!(MigrateV0ToV1::::post_upgrade(bytes)); + + // The weight used should be 1 read for the old value, and 1 write for the new + // value. + assert_eq!( + weight, + ::DbWeight::get().reads_writes(1, 1) + ); + + // After the migration, the new value should be set as the `current` value. + let expected_new_value = + crate::CurrentAndPreviousValue { current: initial_value, previous: None }; + assert_eq!(crate::Value::::get(), Some(expected_new_value)); + }) + } +} diff --git a/substrate/frame/examples/single-block-migrations/src/mock.rs b/substrate/frame/examples/single-block-migrations/src/mock.rs new file mode 100644 index 0000000000000000000000000000000000000000..68594cc4ad727a18f97b5d8b55381d36d1e7aeed --- /dev/null +++ b/substrate/frame/examples/single-block-migrations/src/mock.rs @@ -0,0 +1,69 @@ +// 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. + +#![cfg(any(all(feature = "try-runtime", test), doc))] + +use crate::*; +use frame_support::{derive_impl, traits::ConstU64, weights::constants::ParityDbWeight}; + +// Re-export crate as its pallet name for construct_runtime. +use crate as pallet_example_storage_migration; + +type Block = frame_system::mocking::MockBlock; + +// For testing the pallet, we construct a mock runtime. +frame_support::construct_runtime!( + pub struct MockRuntime { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Example: pallet_example_storage_migration::{Pallet, Call, Storage}, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for MockRuntime { + type Block = Block; + type AccountData = pallet_balances::AccountData; + type DbWeight = ParityDbWeight; +} + +impl pallet_balances::Config for MockRuntime { + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = u64; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); +} + +impl Config for MockRuntime {} + +pub fn new_test_ext() -> sp_io::TestExternalities { + use sp_runtime::BuildStorage; + + let t = RuntimeGenesisConfig { system: Default::default(), balances: Default::default() } + .build_storage() + .unwrap(); + t.into() +} diff --git a/substrate/frame/examples/split/Cargo.toml b/substrate/frame/examples/split/Cargo.toml index d140fc3eef43b04ca7b9175efbdae2830e2d6282..230dc980b1a665199b389104efbed45a2621eee3 100644 --- a/substrate/frame/examples/split/Cargo.toml +++ b/substrate/frame/examples/split/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true license = "MIT-0" homepage = "https://substrate.io" repository.workspace = true -description = "FRAME example splitted pallet" +description = "FRAME example split pallet" readme = "README.md" [lints] diff --git a/substrate/frame/examples/split/src/lib.rs b/substrate/frame/examples/split/src/lib.rs index 74d2e0cc24b7bf5789da19f2657c5e7d2514bbfe..5245d90e390cff1ccab79ecc279fe57188cde030 100644 --- a/substrate/frame/examples/split/src/lib.rs +++ b/substrate/frame/examples/split/src/lib.rs @@ -107,7 +107,7 @@ pub mod pallet { let _who = ensure_signed(origin)?; // Read a value from storage. - match >::get() { + match Something::::get() { // Return an error if the value has not been set. None => return Err(Error::::NoneValue.into()), Some(old) => { diff --git a/substrate/frame/examples/split/src/mock.rs b/substrate/frame/examples/split/src/mock.rs index caab4f1ae902f9be5bee10d324af96304db3a63f..5bf414ee24133c6ca86ed3ff6708e0b2e4f252ee 100644 --- a/substrate/frame/examples/split/src/mock.rs +++ b/substrate/frame/examples/split/src/mock.rs @@ -31,7 +31,7 @@ frame_support::construct_runtime!( /// Using a default config for [`frame_system`] in tests. See `default-config` example for more /// details. -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } diff --git a/substrate/frame/examples/src/lib.rs b/substrate/frame/examples/src/lib.rs index f38bbe52dc114900e4b497cb17da6deb4406512c..dee23a41379fc8b732252b9fe4db39bf809699fd 100644 --- a/substrate/frame/examples/src/lib.rs +++ b/substrate/frame/examples/src/lib.rs @@ -43,6 +43,9 @@ //! - [`pallet_example_frame_crate`]: Example pallet showcasing how one can be //! built using only the `frame` umbrella crate. //! +//! - [`pallet_example_single_block_migrations`]: An example pallet demonstrating best-practices for +//! writing storage migrations. +//! //! - [`pallet_example_tasks`]: This pallet demonstrates the use of `Tasks` to execute service work. //! //! **Tip**: Use `cargo doc --package --open` to view each pallet's documentation. diff --git a/substrate/frame/examples/tasks/Cargo.toml b/substrate/frame/examples/tasks/Cargo.toml index 41521114366a298861c883b4d6f14b0eaf6c4276..4d14bf313d7bec60d260aa1ed5e8249abc8ab842 100644 --- a/substrate/frame/examples/tasks/Cargo.toml +++ b/substrate/frame/examples/tasks/Cargo.toml @@ -5,7 +5,7 @@ authors.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -description = "Pallet to demonstrate the usage of Tasks to recongnize and execute service work" +description = "Pallet to demonstrate the usage of Tasks to recognize and execute service work" [lints] workspace = true diff --git a/substrate/frame/examples/tasks/src/mock.rs b/substrate/frame/examples/tasks/src/mock.rs index 5ad104b0dfae4f32945182e2e79773ba3f470d9a..76ac9e76bff8a79dbedfd6f6a4746a452a685eba 100644 --- a/substrate/frame/examples/tasks/src/mock.rs +++ b/substrate/frame/examples/tasks/src/mock.rs @@ -32,7 +32,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type Block = Block; } diff --git a/substrate/frame/executive/Cargo.toml b/substrate/frame/executive/Cargo.toml index a4ca265f6178218791bf83f6b8f259821e086a98..63285e4cb4939c10db3342b5d59eeffc63103d48 100644 --- a/substrate/frame/executive/Cargo.toml +++ b/substrate/frame/executive/Cargo.toml @@ -16,6 +16,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] +aquamarine = "0.3.2" codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ "derive", ] } @@ -44,6 +45,7 @@ default = ["std"] with-tracing = ["sp-tracing/with-tracing"] std = [ "codec/std", + "frame-support/experimental", "frame-support/std", "frame-system/std", "frame-try-runtime/std", diff --git a/substrate/frame/executive/src/lib.rs b/substrate/frame/executive/src/lib.rs index 48ff675f8082ddd0e7779c946b32dde66b171078..3028eaf318e0881c1b6f4d4ea6ac17adfe99a75f 100644 --- a/substrate/frame/executive/src/lib.rs +++ b/substrate/frame/executive/src/lib.rs @@ -15,6 +15,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![cfg_attr(not(feature = "std"), no_std)] + //! # Executive Module //! //! The Executive module acts as the orchestration layer for the runtime. It dispatches incoming @@ -35,6 +37,8 @@ //! - Finalize a block. //! - Start an off-chain worker. //! +//! The flow of their application in a block is explained in the [block flowchart](block_flowchart). +//! //! ### Implementations //! //! The Executive module provides the following implementations: @@ -114,17 +118,51 @@ //! pub type Executive = executive::Executive; //! ``` -#![cfg_attr(not(feature = "std"), no_std)] +#[cfg(doc)] +#[cfg_attr(doc, aquamarine::aquamarine)] +/// # Block Execution +/// +/// These are the steps of block execution as done by [`Executive::execute_block`]. A block is +/// invalid if any of them fail. +/// +/// ```mermaid +/// flowchart TD +/// Executive::execute_block --> on_runtime_upgrade +/// on_runtime_upgrade --> System::initialize +/// Executive::initialize_block --> System::initialize +/// System::initialize --> on_initialize +/// on_initialize --> PreInherents[System::PreInherents] +/// PreInherents --> Inherents[Apply Inherents] +/// Inherents --> PostInherents[System::PostInherents] +/// PostInherents --> Check{MBM ongoing?} +/// Check -->|No| poll +/// Check -->|Yes| post_transactions_2[System::PostTransaction] +/// post_transactions_2 --> Step[MBMs::step] +/// Step --> on_finalize +/// poll --> transactions[Apply Transactions] +/// transactions --> post_transactions_1[System::PostTransaction] +/// post_transactions_1 --> CheckIdle{Weight remaining?} +/// CheckIdle -->|Yes| on_idle +/// CheckIdle -->|No| on_finalize +/// on_idle --> on_finalize +/// ``` +pub mod block_flowchart {} + +#[cfg(test)] +mod tests; use codec::{Codec, Encode}; use frame_support::{ + defensive_assert, dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, PostDispatchInfo}, + migrations::MultiStepMigrator, pallet_prelude::InvalidTransaction, traits::{ BeforeAllRuntimeMigrations, EnsureInherentsAreFirst, ExecuteBlock, OffchainWorker, - OnFinalize, OnIdle, OnInitialize, OnRuntimeUpgrade, + OnFinalize, OnIdle, OnInitialize, OnPoll, OnRuntimeUpgrade, PostInherents, + PostTransactions, PreInherents, }, - weights::Weight, + weights::{Weight, WeightMeter}, }; use frame_system::pallet_prelude::BlockNumberFor; use sp_runtime::{ @@ -134,7 +172,7 @@ use sp_runtime::{ ValidateUnsigned, Zero, }, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, + ApplyExtrinsicResult, ExtrinsicInclusionMode, }; use sp_std::{marker::PhantomData, prelude::*}; @@ -198,7 +236,8 @@ impl< + OnInitialize> + OnIdle> + OnFinalize> - + OffchainWorker>, + + OffchainWorker> + + OnPoll>, COnRuntimeUpgrade: OnRuntimeUpgrade, > ExecuteBlock for Executive @@ -237,6 +276,7 @@ impl< + OnIdle> + OnFinalize> + OffchainWorker> + + OnPoll> + TryState> + TryDecodeEntireStorage, COnRuntimeUpgrade: OnRuntimeUpgrade, @@ -272,36 +312,50 @@ where select, ); - Self::initialize_block(block.header()); - Self::initial_checks(&block); - + let mode = Self::initialize_block(block.header()); + let num_inherents = Self::initial_checks(&block) as usize; let (header, extrinsics) = block.deconstruct(); + // Check if there are any forbidden non-inherents in the block. + if mode == ExtrinsicInclusionMode::OnlyInherents && extrinsics.len() > num_inherents { + return Err("Only inherents allowed".into()) + } + let try_apply_extrinsic = |uxt: Block::Extrinsic| -> ApplyExtrinsicResult { sp_io::init_tracing(); let encoded = uxt.encode(); let encoded_len = encoded.len(); + let is_inherent = System::is_inherent(&uxt); // skip signature verification. let xt = if signature_check { uxt.check(&Default::default()) } else { uxt.unchecked_into_checked_i_know_what_i_am_doing(&Default::default()) }?; - >::note_extrinsic(encoded); let dispatch_info = xt.get_dispatch_info(); + if !is_inherent && !>::inherents_applied() { + Self::inherents_applied(); + } + + >::note_extrinsic(encoded); let r = Applyable::apply::(xt, &dispatch_info, encoded_len)?; + if r.is_err() && dispatch_info.class == DispatchClass::Mandatory { + return Err(InvalidTransaction::BadMandatory.into()) + } + >::note_applied_extrinsic(&r, dispatch_info); Ok(r.map(|_| ()).map_err(|e| e.error)) }; - for e in extrinsics { + // Apply extrinsics: + for e in extrinsics.iter() { if let Err(err) = try_apply_extrinsic(e.clone()) { log::error!( - target: LOG_TARGET, "executing transaction {:?} failed due to {:?}. Aborting the rest of the block execution.", + target: LOG_TARGET, "transaction {:?} failed due to {:?}. Aborting the rest of the block execution.", e, err, ); @@ -309,9 +363,17 @@ where } } + // In this case there were no transactions to trigger this state transition: + if !>::inherents_applied() { + Self::inherents_applied(); + } + // post-extrinsics book-keeping >::note_finished_extrinsics(); - Self::idle_and_finalize_hook(*header.number()); + ::PostTransactions::post_transactions(); + + Self::on_idle_hook(*header.number()); + Self::on_finalize_hook(*header.number()); // run the try-state checks of all pallets, ensuring they don't alter any state. let _guard = frame_support::StorageNoopGuard::default(); @@ -449,7 +511,8 @@ impl< + OnInitialize> + OnIdle> + OnFinalize> - + OffchainWorker>, + + OffchainWorker> + + OnPoll>, COnRuntimeUpgrade: OnRuntimeUpgrade, > Executive where @@ -464,16 +527,36 @@ where pub fn execute_on_runtime_upgrade() -> Weight { let before_all_weight = ::before_all_runtime_migrations(); - <(COnRuntimeUpgrade, AllPalletsWithSystem) as OnRuntimeUpgrade>::on_runtime_upgrade() - .saturating_add(before_all_weight) + + let runtime_upgrade_weight = <( + COnRuntimeUpgrade, + ::SingleBlockMigrations, + // We want to run the migrations before we call into the pallets as they may + // access any state that would then not be migrated. + AllPalletsWithSystem, + ) as OnRuntimeUpgrade>::on_runtime_upgrade(); + + before_all_weight.saturating_add(runtime_upgrade_weight) } /// Start the execution of a particular block. - pub fn initialize_block(header: &frame_system::pallet_prelude::HeaderFor) { + pub fn initialize_block( + header: &frame_system::pallet_prelude::HeaderFor, + ) -> ExtrinsicInclusionMode { sp_io::init_tracing(); sp_tracing::enter_span!(sp_tracing::Level::TRACE, "init_block"); let digests = Self::extract_pre_digest(header); Self::initialize_block_impl(header.number(), header.parent_hash(), &digests); + + Self::extrinsic_mode() + } + + fn extrinsic_mode() -> ExtrinsicInclusionMode { + if ::MultiBlockMigrator::ongoing() { + ExtrinsicInclusionMode::OnlyInherents + } else { + ExtrinsicInclusionMode::AllExtrinsics + } } fn extract_pre_digest(header: &frame_system::pallet_prelude::HeaderFor) -> Digest { @@ -519,6 +602,7 @@ where ); frame_system::Pallet::::note_finished_initialize(); + ::PreInherents::pre_inherents(); } /// Returns if the runtime has been upgraded, based on [`frame_system::LastRuntimeUpgrade`]. @@ -529,7 +613,8 @@ where last.map(|v| v.was_upgraded(¤t)).unwrap_or(true) } - fn initial_checks(block: &Block) { + /// Returns the number of inherents in the block. + fn initial_checks(block: &Block) -> u32 { sp_tracing::enter_span!(sp_tracing::Level::TRACE, "initial_checks"); let header = block.header(); @@ -542,8 +627,9 @@ where "Parent hash should be valid.", ); - if let Err(i) = System::ensure_inherents_are_first(block) { - panic!("Invalid inherent position for extrinsic at index {}", i); + match System::ensure_inherents_are_first(block) { + Ok(num) => num, + Err(i) => panic!("Invalid inherent position for extrinsic at index {}", i), } } @@ -552,53 +638,90 @@ where sp_io::init_tracing(); sp_tracing::within_span! { sp_tracing::info_span!("execute_block", ?block); + // Execute `on_runtime_upgrade` and `on_initialize`. + let mode = Self::initialize_block(block.header()); + let num_inherents = Self::initial_checks(&block) as usize; + let (header, extrinsics) = block.deconstruct(); + let num_extrinsics = extrinsics.len(); - Self::initialize_block(block.header()); + if mode == ExtrinsicInclusionMode::OnlyInherents && num_extrinsics > num_inherents { + // Invalid block + panic!("Only inherents are allowed in this block") + } - // any initial checks - Self::initial_checks(&block); + Self::apply_extrinsics(extrinsics.into_iter()); - // execute extrinsics - let (header, extrinsics) = block.deconstruct(); - Self::execute_extrinsics_with_book_keeping(extrinsics, *header.number()); + // In this case there were no transactions to trigger this state transition: + if !>::inherents_applied() { + defensive_assert!(num_inherents == num_extrinsics); + Self::inherents_applied(); + } - // any final checks + >::note_finished_extrinsics(); + ::PostTransactions::post_transactions(); + + Self::on_idle_hook(*header.number()); + Self::on_finalize_hook(*header.number()); Self::final_checks(&header); } } - /// Execute given extrinsics and take care of post-extrinsics book-keeping. - fn execute_extrinsics_with_book_keeping( - extrinsics: Vec, - block_number: NumberFor, - ) { + /// Logic that runs directly after inherent application. + /// + /// It advances the Multi-Block-Migrations or runs the `on_poll` hook. + pub fn inherents_applied() { + >::note_inherents_applied(); + ::PostInherents::post_inherents(); + + if ::MultiBlockMigrator::ongoing() { + let used_weight = ::MultiBlockMigrator::step(); + >::register_extra_weight_unchecked( + used_weight, + DispatchClass::Mandatory, + ); + } else { + let block_number = >::block_number(); + Self::on_poll_hook(block_number); + } + } + + /// Execute given extrinsics. + fn apply_extrinsics(extrinsics: impl Iterator) { extrinsics.into_iter().for_each(|e| { if let Err(e) = Self::apply_extrinsic(e) { let err: &'static str = e.into(); panic!("{}", err) } }); - - // post-extrinsics book-keeping - >::note_finished_extrinsics(); - - Self::idle_and_finalize_hook(block_number); } /// Finalize the block - it is up the caller to ensure that all header fields are valid /// except state-root. + // Note: Only used by the block builder - not Executive itself. pub fn finalize_block() -> frame_system::pallet_prelude::HeaderFor { sp_io::init_tracing(); sp_tracing::enter_span!(sp_tracing::Level::TRACE, "finalize_block"); - >::note_finished_extrinsics(); - let block_number = >::block_number(); - Self::idle_and_finalize_hook(block_number); + // In this case there were no transactions to trigger this state transition: + if !>::inherents_applied() { + Self::inherents_applied(); + } + >::note_finished_extrinsics(); + ::PostTransactions::post_transactions(); + let block_number = >::block_number(); + Self::on_idle_hook(block_number); + Self::on_finalize_hook(block_number); >::finalize() } - fn idle_and_finalize_hook(block_number: NumberFor) { + /// Run the `on_idle` hook of all pallet, but only if there is weight remaining and there are no + /// ongoing MBMs. + fn on_idle_hook(block_number: NumberFor) { + if ::MultiBlockMigrator::ongoing() { + return + } + let weight = >::block_weight(); let max_weight = >::get().max_block; let remaining_weight = max_weight.saturating_sub(weight.total()); @@ -613,7 +736,33 @@ where DispatchClass::Mandatory, ); } + } + + fn on_poll_hook(block_number: NumberFor) { + defensive_assert!( + !::MultiBlockMigrator::ongoing(), + "on_poll should not be called during migrations" + ); + let weight = >::block_weight(); + let max_weight = >::get().max_block; + let remaining = max_weight.saturating_sub(weight.total()); + + if remaining.all_gt(Weight::zero()) { + let mut meter = WeightMeter::with_limit(remaining); + >>::on_poll( + block_number, + &mut meter, + ); + >::register_extra_weight_unchecked( + meter.consumed(), + DispatchClass::Mandatory, + ); + } + } + + /// Run the `on_finalize` hook of all pallet. + fn on_finalize_hook(block_number: NumberFor) { >>::on_finalize(block_number); } @@ -627,8 +776,18 @@ where let encoded_len = encoded.len(); sp_tracing::enter_span!(sp_tracing::info_span!("apply_extrinsic", ext=?sp_core::hexdisplay::HexDisplay::from(&encoded))); + + // We use the dedicated `is_inherent` check here, since just relying on `Mandatory` dispatch + // class does not capture optional inherents. + let is_inherent = System::is_inherent(&uxt); + // Verify that the signature is good. let xt = uxt.check(&Default::default())?; + let dispatch_info = xt.get_dispatch_info(); + + if !is_inherent && !>::inherents_applied() { + Self::inherents_applied(); + } // We don't need to make sure to `note_extrinsic` only after we know it's going to be // executed to prevent it from leaking in storage since at this point, it will either @@ -637,8 +796,6 @@ where // AUDIT: Under no circumstances may this function panic from here onwards. - // Decode parameters and dispatch - let dispatch_info = xt.get_dispatch_info(); let r = Applyable::apply::(xt, &dispatch_info, encoded_len)?; // Mandatory(inherents) are not allowed to fail. @@ -745,956 +902,3 @@ where ) } } - -#[cfg(test)] -mod tests { - use super::*; - - use sp_core::H256; - use sp_runtime::{ - generic::{DigestItem, Era}, - testing::{Block, Digest, Header}, - traits::{BlakeTwo256, Block as BlockT, Header as HeaderT, IdentityLookup}, - transaction_validity::{ - InvalidTransaction, TransactionValidityError, UnknownTransaction, ValidTransaction, - }, - BuildStorage, DispatchError, - }; - - use frame_support::{ - assert_err, derive_impl, parameter_types, - traits::{fungible, ConstU32, ConstU64, ConstU8, Currency}, - weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight, WeightToFee}, - }; - use frame_system::{ChainContext, LastRuntimeUpgrade, LastRuntimeUpgradeInfo}; - use pallet_balances::Call as BalancesCall; - use pallet_transaction_payment::CurrencyAdapter; - - const TEST_KEY: &[u8] = b":test:key:"; - - #[frame_support::pallet(dev_mode)] - mod custom { - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::hooks] - impl Hooks> for Pallet { - // module hooks. - // one with block number arg and one without - fn on_initialize(n: BlockNumberFor) -> Weight { - println!("on_initialize({})", n); - Weight::from_parts(175, 0) - } - - fn on_idle(n: BlockNumberFor, remaining_weight: Weight) -> Weight { - println!("on_idle{}, {})", n, remaining_weight); - Weight::from_parts(175, 0) - } - - fn on_finalize(n: BlockNumberFor) { - println!("on_finalize({})", n); - } - - fn on_runtime_upgrade() -> Weight { - sp_io::storage::set(super::TEST_KEY, "module".as_bytes()); - Weight::from_parts(200, 0) - } - - fn offchain_worker(n: BlockNumberFor) { - assert_eq!(BlockNumberFor::::from(1u32), n); - } - } - - #[pallet::call] - impl Pallet { - pub fn some_function(origin: OriginFor) -> DispatchResult { - // NOTE: does not make any different. - frame_system::ensure_signed(origin)?; - Ok(()) - } - - #[pallet::weight((200, DispatchClass::Operational))] - pub fn some_root_operation(origin: OriginFor) -> DispatchResult { - frame_system::ensure_root(origin)?; - Ok(()) - } - - pub fn some_unsigned_message(origin: OriginFor) -> DispatchResult { - frame_system::ensure_none(origin)?; - Ok(()) - } - - pub fn allowed_unsigned(origin: OriginFor) -> DispatchResult { - frame_system::ensure_root(origin)?; - Ok(()) - } - - pub fn unallowed_unsigned(origin: OriginFor) -> DispatchResult { - frame_system::ensure_root(origin)?; - Ok(()) - } - - #[pallet::weight((0, DispatchClass::Mandatory))] - pub fn inherent_call(origin: OriginFor) -> DispatchResult { - frame_system::ensure_none(origin)?; - Ok(()) - } - - pub fn calculate_storage_root(_origin: OriginFor) -> DispatchResult { - let root = sp_io::storage::root(sp_runtime::StateVersion::V1); - sp_io::storage::set("storage_root".as_bytes(), &root); - Ok(()) - } - } - - #[pallet::inherent] - impl ProvideInherent for Pallet { - type Call = Call; - - type Error = sp_inherents::MakeFatalError<()>; - - const INHERENT_IDENTIFIER: [u8; 8] = *b"test1234"; - - fn create_inherent(_data: &InherentData) -> Option { - None - } - - fn is_inherent(call: &Self::Call) -> bool { - *call == Call::::inherent_call {} - } - } - - #[pallet::validate_unsigned] - impl ValidateUnsigned for Pallet { - type Call = Call; - - // Inherent call is accepted for being dispatched - fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> { - match call { - Call::allowed_unsigned { .. } => Ok(()), - Call::inherent_call { .. } => Ok(()), - _ => Err(UnknownTransaction::NoUnsignedValidator.into()), - } - } - - // Inherent call is not validated as unsigned - fn validate_unsigned( - _source: TransactionSource, - call: &Self::Call, - ) -> TransactionValidity { - match call { - Call::allowed_unsigned { .. } => Ok(Default::default()), - _ => UnknownTransaction::NoUnsignedValidator.into(), - } - } - } - } - - frame_support::construct_runtime!( - pub enum Runtime { - System: frame_system, - Balances: pallet_balances, - TransactionPayment: pallet_transaction_payment, - Custom: custom, - } - ); - - parameter_types! { - pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::builder() - .base_block(Weight::from_parts(10, 0)) - .for_class(DispatchClass::all(), |weights| weights.base_extrinsic = Weight::from_parts(5, 0)) - .for_class(DispatchClass::non_mandatory(), |weights| weights.max_total = Weight::from_parts(1024, u64::MAX).into()) - .build_or_panic(); - pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { - read: 10, - write: 100, - }; - } - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] - impl frame_system::Config for Runtime { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = BlockWeights; - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = sp_core::H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Block = TestBlock; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = RuntimeVersion; - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; - } - - type Balance = u64; - impl pallet_balances::Config for Runtime { - type Balance = Balance; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ConstU64<1>; - type AccountStore = System; - type MaxLocks = (); - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type WeightInfo = (); - type FreezeIdentifier = (); - type MaxFreezes = ConstU32<1>; - type RuntimeHoldReason = (); - type RuntimeFreezeReason = (); - } - - parameter_types! { - pub const TransactionByteFee: Balance = 0; - } - impl pallet_transaction_payment::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type OnChargeTransaction = CurrencyAdapter; - type OperationalFeeMultiplier = ConstU8<5>; - type WeightToFee = IdentityFee; - type LengthToFee = ConstantMultiplier; - type FeeMultiplierUpdate = (); - } - impl custom::Config for Runtime {} - - pub struct RuntimeVersion; - impl frame_support::traits::Get for RuntimeVersion { - fn get() -> sp_version::RuntimeVersion { - RuntimeVersionTestValues::get().clone() - } - } - - parameter_types! { - pub static RuntimeVersionTestValues: sp_version::RuntimeVersion = - Default::default(); - } - - type SignedExtra = ( - frame_system::CheckEra, - frame_system::CheckNonce, - frame_system::CheckWeight, - pallet_transaction_payment::ChargeTransactionPayment, - ); - type TestXt = sp_runtime::testing::TestXt; - type TestBlock = Block; - - // Will contain `true` when the custom runtime logic was called. - const CUSTOM_ON_RUNTIME_KEY: &[u8] = b":custom:on_runtime"; - - struct CustomOnRuntimeUpgrade; - impl OnRuntimeUpgrade for CustomOnRuntimeUpgrade { - fn on_runtime_upgrade() -> Weight { - sp_io::storage::set(TEST_KEY, "custom_upgrade".as_bytes()); - sp_io::storage::set(CUSTOM_ON_RUNTIME_KEY, &true.encode()); - System::deposit_event(frame_system::Event::CodeUpdated); - - assert_eq!(0, System::last_runtime_upgrade_spec_version()); - - Weight::from_parts(100, 0) - } - } - - type Executive = super::Executive< - Runtime, - Block, - ChainContext, - Runtime, - AllPalletsWithSystem, - CustomOnRuntimeUpgrade, - >; - - fn extra(nonce: u64, fee: Balance) -> SignedExtra { - ( - frame_system::CheckEra::from(Era::Immortal), - frame_system::CheckNonce::from(nonce), - frame_system::CheckWeight::new(), - pallet_transaction_payment::ChargeTransactionPayment::from(fee), - ) - } - - fn sign_extra(who: u64, nonce: u64, fee: Balance) -> Option<(u64, SignedExtra)> { - Some((who, extra(nonce, fee))) - } - - fn call_transfer(dest: u64, value: u64) -> RuntimeCall { - RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value }) - } - - #[test] - fn balance_transfer_dispatch_works() { - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - pallet_balances::GenesisConfig:: { balances: vec![(1, 211)] } - .assimilate_storage(&mut t) - .unwrap(); - let xt = TestXt::new(call_transfer(2, 69), sign_extra(1, 0, 0)); - let weight = xt.get_dispatch_info().weight + - ::BlockWeights::get() - .get(DispatchClass::Normal) - .base_extrinsic; - let fee: Balance = - ::WeightToFee::weight_to_fee(&weight); - let mut t = sp_io::TestExternalities::new(t); - t.execute_with(|| { - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - let r = Executive::apply_extrinsic(xt); - assert!(r.is_ok()); - assert_eq!(>::total_balance(&1), 142 - fee); - assert_eq!(>::total_balance(&2), 69); - }); - } - - fn new_test_ext(balance_factor: Balance) -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - pallet_balances::GenesisConfig:: { balances: vec![(1, 111 * balance_factor)] } - .assimilate_storage(&mut t) - .unwrap(); - t.into() - } - - fn new_test_ext_v0(balance_factor: Balance) -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - pallet_balances::GenesisConfig:: { balances: vec![(1, 111 * balance_factor)] } - .assimilate_storage(&mut t) - .unwrap(); - (t, sp_runtime::StateVersion::V0).into() - } - - #[test] - fn block_import_works() { - block_import_works_inner( - new_test_ext_v0(1), - array_bytes::hex_n_into_unchecked( - "65e953676859e7a33245908af7ad3637d6861eb90416d433d485e95e2dd174a1", - ), - ); - block_import_works_inner( - new_test_ext(1), - array_bytes::hex_n_into_unchecked( - "5a19b3d6fdb7241836349fdcbe2d9df4d4f945b949d979e31ad50bff1cbcd1c2", - ), - ); - } - fn block_import_works_inner(mut ext: sp_io::TestExternalities, state_root: H256) { - ext.execute_with(|| { - Executive::execute_block(Block { - header: Header { - parent_hash: [69u8; 32].into(), - number: 1, - state_root, - extrinsics_root: array_bytes::hex_n_into_unchecked( - "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314", - ), - digest: Digest { logs: vec![] }, - }, - extrinsics: vec![], - }); - }); - } - - #[test] - #[should_panic] - fn block_import_of_bad_state_root_fails() { - new_test_ext(1).execute_with(|| { - Executive::execute_block(Block { - header: Header { - parent_hash: [69u8; 32].into(), - number: 1, - state_root: [0u8; 32].into(), - extrinsics_root: array_bytes::hex_n_into_unchecked( - "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314", - ), - digest: Digest { logs: vec![] }, - }, - extrinsics: vec![], - }); - }); - } - - #[test] - #[should_panic] - fn block_import_of_bad_extrinsic_root_fails() { - new_test_ext(1).execute_with(|| { - Executive::execute_block(Block { - header: Header { - parent_hash: [69u8; 32].into(), - number: 1, - state_root: array_bytes::hex_n_into_unchecked( - "75e7d8f360d375bbe91bcf8019c01ab6362448b4a89e3b329717eb9d910340e5", - ), - extrinsics_root: [0u8; 32].into(), - digest: Digest { logs: vec![] }, - }, - extrinsics: vec![], - }); - }); - } - - #[test] - fn bad_extrinsic_not_inserted() { - let mut t = new_test_ext(1); - // bad nonce check! - let xt = TestXt::new(call_transfer(33, 69), sign_extra(1, 30, 0)); - t.execute_with(|| { - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - assert_err!( - Executive::apply_extrinsic(xt), - TransactionValidityError::Invalid(InvalidTransaction::Future) - ); - assert_eq!(>::extrinsic_index(), Some(0)); - }); - } - - #[test] - fn block_weight_limit_enforced() { - let mut t = new_test_ext(10000); - // given: TestXt uses the encoded len as fixed Len: - let xt = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, 0, 0), - ); - let encoded = xt.encode(); - let encoded_len = encoded.len() as u64; - // on_initialize weight + base block execution weight - let block_weights = ::BlockWeights::get(); - let base_block_weight = Weight::from_parts(175, 0) + block_weights.base_block; - let limit = block_weights.get(DispatchClass::Normal).max_total.unwrap() - base_block_weight; - let num_to_exhaust_block = limit.ref_time() / (encoded_len + 5); - t.execute_with(|| { - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - // Base block execution weight + `on_initialize` weight from the custom module. - assert_eq!(>::block_weight().total(), base_block_weight); - - for nonce in 0..=num_to_exhaust_block { - let xt = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer_allow_death { - dest: 33, - value: 0, - }), - sign_extra(1, nonce.into(), 0), - ); - let res = Executive::apply_extrinsic(xt); - if nonce != num_to_exhaust_block { - assert!(res.is_ok()); - assert_eq!( - >::block_weight().total(), - //--------------------- on_initialize + block_execution + extrinsic_base weight - Weight::from_parts((encoded_len + 5) * (nonce + 1), 0) + base_block_weight, - ); - assert_eq!( - >::extrinsic_index(), - Some(nonce as u32 + 1) - ); - } else { - assert_eq!(res, Err(InvalidTransaction::ExhaustsResources.into())); - } - } - }); - } - - #[test] - fn block_weight_and_size_is_stored_per_tx() { - let xt = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, 0, 0), - ); - let x1 = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, 1, 0), - ); - let x2 = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, 2, 0), - ); - let len = xt.clone().encode().len() as u32; - let mut t = new_test_ext(1); - t.execute_with(|| { - // Block execution weight + on_initialize weight from custom module - let base_block_weight = Weight::from_parts(175, 0) + - ::BlockWeights::get().base_block; - - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - assert_eq!(>::block_weight().total(), base_block_weight); - assert_eq!(>::all_extrinsics_len(), 0); - - assert!(Executive::apply_extrinsic(xt.clone()).unwrap().is_ok()); - assert!(Executive::apply_extrinsic(x1.clone()).unwrap().is_ok()); - assert!(Executive::apply_extrinsic(x2.clone()).unwrap().is_ok()); - - // default weight for `TestXt` == encoded length. - let extrinsic_weight = Weight::from_parts(len as u64, 0) + - ::BlockWeights::get() - .get(DispatchClass::Normal) - .base_extrinsic; - assert_eq!( - >::block_weight().total(), - base_block_weight + 3u64 * extrinsic_weight, - ); - assert_eq!(>::all_extrinsics_len(), 3 * len); - - let _ = >::finalize(); - // All extrinsics length cleaned on `System::finalize` - assert_eq!(>::all_extrinsics_len(), 0); - - // New Block - Executive::initialize_block(&Header::new( - 2, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - // Block weight cleaned up on `System::initialize` - assert_eq!(>::block_weight().total(), base_block_weight); - }); - } - - #[test] - fn validate_unsigned() { - let valid = TestXt::new(RuntimeCall::Custom(custom::Call::allowed_unsigned {}), None); - let invalid = TestXt::new(RuntimeCall::Custom(custom::Call::unallowed_unsigned {}), None); - let mut t = new_test_ext(1); - - t.execute_with(|| { - assert_eq!( - Executive::validate_transaction( - TransactionSource::InBlock, - valid.clone(), - Default::default(), - ), - Ok(ValidTransaction::default()), - ); - assert_eq!( - Executive::validate_transaction( - TransactionSource::InBlock, - invalid.clone(), - Default::default(), - ), - Err(TransactionValidityError::Unknown(UnknownTransaction::NoUnsignedValidator)), - ); - assert_eq!(Executive::apply_extrinsic(valid), Ok(Err(DispatchError::BadOrigin))); - assert_eq!( - Executive::apply_extrinsic(invalid), - Err(TransactionValidityError::Unknown(UnknownTransaction::NoUnsignedValidator)) - ); - }); - } - - #[test] - fn can_not_pay_for_tx_fee_on_full_lock() { - let mut t = new_test_ext(1); - t.execute_with(|| { - as fungible::MutateFreeze>::set_freeze( - &(), - &1, - 110, - ) - .unwrap(); - let xt = TestXt::new( - RuntimeCall::System(frame_system::Call::remark { remark: vec![1u8] }), - sign_extra(1, 0, 0), - ); - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - assert_eq!(Executive::apply_extrinsic(xt), Err(InvalidTransaction::Payment.into()),); - assert_eq!(>::total_balance(&1), 111); - }); - } - - #[test] - fn block_hooks_weight_is_stored() { - new_test_ext(1).execute_with(|| { - Executive::initialize_block(&Header::new_from_number(1)); - Executive::finalize_block(); - // NOTE: might need updates over time if new weights are introduced. - // For now it only accounts for the base block execution weight and - // the `on_initialize` weight defined in the custom test module. - assert_eq!( - >::block_weight().total(), - Weight::from_parts(175 + 175 + 10, 0) - ); - }) - } - - #[test] - fn runtime_upgraded_should_work() { - new_test_ext(1).execute_with(|| { - RuntimeVersionTestValues::mutate(|v| *v = Default::default()); - // It should be added at genesis - assert!(LastRuntimeUpgrade::::exists()); - assert!(!Executive::runtime_upgraded()); - - RuntimeVersionTestValues::mutate(|v| { - *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } - }); - assert!(Executive::runtime_upgraded()); - - RuntimeVersionTestValues::mutate(|v| { - *v = sp_version::RuntimeVersion { - spec_version: 1, - spec_name: "test".into(), - ..Default::default() - } - }); - assert!(Executive::runtime_upgraded()); - - RuntimeVersionTestValues::mutate(|v| { - *v = sp_version::RuntimeVersion { - spec_version: 0, - impl_version: 2, - ..Default::default() - } - }); - assert!(!Executive::runtime_upgraded()); - - LastRuntimeUpgrade::::take(); - assert!(Executive::runtime_upgraded()); - }) - } - - #[test] - fn last_runtime_upgrade_was_upgraded_works() { - let test_data = vec![ - (0, "", 1, "", true), - (1, "", 1, "", false), - (1, "", 1, "test", true), - (1, "", 0, "", false), - (1, "", 0, "test", true), - ]; - - for (spec_version, spec_name, c_spec_version, c_spec_name, result) in test_data { - let current = sp_version::RuntimeVersion { - spec_version: c_spec_version, - spec_name: c_spec_name.into(), - ..Default::default() - }; - - let last = LastRuntimeUpgradeInfo { - spec_version: spec_version.into(), - spec_name: spec_name.into(), - }; - - assert_eq!(result, last.was_upgraded(¤t)); - } - } - - #[test] - fn custom_runtime_upgrade_is_called_before_modules() { - new_test_ext(1).execute_with(|| { - // Make sure `on_runtime_upgrade` is called. - RuntimeVersionTestValues::mutate(|v| { - *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } - }); - - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - assert_eq!(&sp_io::storage::get(TEST_KEY).unwrap()[..], *b"module"); - assert_eq!(sp_io::storage::get(CUSTOM_ON_RUNTIME_KEY).unwrap(), true.encode()); - assert_eq!( - Some(RuntimeVersionTestValues::get().into()), - LastRuntimeUpgrade::::get(), - ) - }); - } - - #[test] - fn event_from_runtime_upgrade_is_included() { - new_test_ext(1).execute_with(|| { - // Make sure `on_runtime_upgrade` is called. - RuntimeVersionTestValues::mutate(|v| { - *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } - }); - - // set block number to non zero so events are not excluded - System::set_block_number(1); - - Executive::initialize_block(&Header::new( - 2, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - System::assert_last_event(frame_system::Event::::CodeUpdated.into()); - }); - } - - /// Regression test that ensures that the custom on runtime upgrade is called when executive is - /// used through the `ExecuteBlock` trait. - #[test] - fn custom_runtime_upgrade_is_called_when_using_execute_block_trait() { - let xt = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, 0, 0), - ); - - let header = new_test_ext(1).execute_with(|| { - // Make sure `on_runtime_upgrade` is called. - RuntimeVersionTestValues::mutate(|v| { - *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } - }); - - // Let's build some fake block. - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - Executive::apply_extrinsic(xt.clone()).unwrap().unwrap(); - - Executive::finalize_block() - }); - - // Reset to get the correct new genesis below. - RuntimeVersionTestValues::mutate(|v| { - *v = sp_version::RuntimeVersion { spec_version: 0, ..Default::default() } - }); - - new_test_ext(1).execute_with(|| { - // Make sure `on_runtime_upgrade` is called. - RuntimeVersionTestValues::mutate(|v| { - *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } - }); - - >>::execute_block(Block::new(header, vec![xt])); - - assert_eq!(&sp_io::storage::get(TEST_KEY).unwrap()[..], *b"module"); - assert_eq!(sp_io::storage::get(CUSTOM_ON_RUNTIME_KEY).unwrap(), true.encode()); - }); - } - - #[test] - fn all_weights_are_recorded_correctly() { - // Reset to get the correct new genesis below. - RuntimeVersionTestValues::take(); - - new_test_ext(1).execute_with(|| { - // Make sure `on_runtime_upgrade` is called for maximum complexity - RuntimeVersionTestValues::mutate(|v| { - *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } - }); - - let block_number = 1; - - Executive::initialize_block(&Header::new( - block_number, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - // Reset the last runtime upgrade info, to make the second call to `on_runtime_upgrade` - // succeed. - LastRuntimeUpgrade::::take(); - - // All weights that show up in the `initialize_block_impl` - let custom_runtime_upgrade_weight = CustomOnRuntimeUpgrade::on_runtime_upgrade(); - let runtime_upgrade_weight = - ::on_runtime_upgrade(); - let on_initialize_weight = - >::on_initialize(block_number); - let base_block_weight = - ::BlockWeights::get().base_block; - - // Weights are recorded correctly - assert_eq!( - frame_system::Pallet::::block_weight().total(), - custom_runtime_upgrade_weight + - runtime_upgrade_weight + - on_initialize_weight + base_block_weight, - ); - }); - } - - #[test] - fn offchain_worker_works_as_expected() { - new_test_ext(1).execute_with(|| { - let parent_hash = sp_core::H256::from([69u8; 32]); - let mut digest = Digest::default(); - digest.push(DigestItem::Seal([1, 2, 3, 4], vec![5, 6, 7, 8])); - - let header = - Header::new(1, H256::default(), H256::default(), parent_hash, digest.clone()); - - Executive::offchain_worker(&header); - - assert_eq!(digest, System::digest()); - assert_eq!(parent_hash, System::block_hash(0)); - assert_eq!(header.hash(), System::block_hash(1)); - }); - } - - #[test] - fn calculating_storage_root_twice_works() { - let call = RuntimeCall::Custom(custom::Call::calculate_storage_root {}); - let xt = TestXt::new(call, sign_extra(1, 0, 0)); - - let header = new_test_ext(1).execute_with(|| { - // Let's build some fake block. - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - Executive::apply_extrinsic(xt.clone()).unwrap().unwrap(); - - Executive::finalize_block() - }); - - new_test_ext(1).execute_with(|| { - Executive::execute_block(Block::new(header, vec![xt])); - }); - } - - #[test] - #[should_panic(expected = "Invalid inherent position for extrinsic at index 1")] - fn invalid_inherent_position_fail() { - let xt1 = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, 0, 0), - ); - let xt2 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent_call {}), None); - - let header = new_test_ext(1).execute_with(|| { - // Let's build some fake block. - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); - Executive::apply_extrinsic(xt2.clone()).unwrap().unwrap(); - - Executive::finalize_block() - }); - - new_test_ext(1).execute_with(|| { - Executive::execute_block(Block::new(header, vec![xt1, xt2])); - }); - } - - #[test] - fn valid_inherents_position_works() { - let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent_call {}), None); - let xt2 = TestXt::new(call_transfer(33, 0), sign_extra(1, 0, 0)); - - let header = new_test_ext(1).execute_with(|| { - // Let's build some fake block. - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); - Executive::apply_extrinsic(xt2.clone()).unwrap().unwrap(); - - Executive::finalize_block() - }); - - new_test_ext(1).execute_with(|| { - Executive::execute_block(Block::new(header, vec![xt1, xt2])); - }); - } - - #[test] - #[should_panic(expected = "A call was labelled as mandatory, but resulted in an Error.")] - fn invalid_inherents_fail_block_execution() { - let xt1 = - TestXt::new(RuntimeCall::Custom(custom::Call::inherent_call {}), sign_extra(1, 0, 0)); - - new_test_ext(1).execute_with(|| { - Executive::execute_block(Block::new( - Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - ), - vec![xt1], - )); - }); - } - - // Inherents are created by the runtime and don't need to be validated. - #[test] - fn inherents_fail_validate_block() { - let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent_call {}), None); - - new_test_ext(1).execute_with(|| { - assert_eq!( - Executive::validate_transaction(TransactionSource::External, xt1, H256::random()) - .unwrap_err(), - InvalidTransaction::MandatoryValidation.into() - ); - }) - } -} diff --git a/substrate/frame/executive/src/tests.rs b/substrate/frame/executive/src/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..204889a292fd720aa89cb343435849be0ca8e442 --- /dev/null +++ b/substrate/frame/executive/src/tests.rs @@ -0,0 +1,1389 @@ +// 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. + +//! Test the `frame-executive` crate. + +use super::*; + +use sp_core::H256; +use sp_runtime::{ + generic::{DigestItem, Era}, + testing::{Block, Digest, Header}, + traits::{Block as BlockT, Header as HeaderT}, + transaction_validity::{ + InvalidTransaction, TransactionValidityError, UnknownTransaction, ValidTransaction, + }, + BuildStorage, DispatchError, +}; + +use frame_support::{ + assert_err, assert_ok, derive_impl, + migrations::MultiStepMigrator, + pallet_prelude::*, + parameter_types, + traits::{fungible, ConstU8, Currency, IsInherent}, + weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight, WeightMeter, WeightToFee}, +}; +use frame_system::{pallet_prelude::*, ChainContext, LastRuntimeUpgrade, LastRuntimeUpgradeInfo}; +use pallet_balances::Call as BalancesCall; +use pallet_transaction_payment::CurrencyAdapter; + +const TEST_KEY: &[u8] = b":test:key:"; + +#[frame_support::pallet(dev_mode)] +mod custom { + use super::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::hooks] + impl Hooks> for Pallet { + // module hooks. + // one with block number arg and one without + fn on_initialize(_: BlockNumberFor) -> Weight { + Weight::from_parts(175, 0) + } + + fn on_idle(_: BlockNumberFor, _: Weight) -> Weight { + Weight::from_parts(175, 0) + } + + fn on_poll(_: BlockNumberFor, _: &mut WeightMeter) {} + + fn on_finalize(_: BlockNumberFor) {} + + fn on_runtime_upgrade() -> Weight { + sp_io::storage::set(super::TEST_KEY, "module".as_bytes()); + Weight::from_parts(200, 0) + } + + fn offchain_worker(n: BlockNumberFor) { + assert_eq!(BlockNumberFor::::from(1u32), n); + } + } + + #[pallet::call] + impl Pallet { + pub fn some_function(origin: OriginFor) -> DispatchResult { + // NOTE: does not make any difference. + frame_system::ensure_signed(origin)?; + Ok(()) + } + + #[pallet::weight((200, DispatchClass::Operational))] + pub fn some_root_operation(origin: OriginFor) -> DispatchResult { + frame_system::ensure_root(origin)?; + Ok(()) + } + + pub fn some_unsigned_message(origin: OriginFor) -> DispatchResult { + frame_system::ensure_none(origin)?; + Ok(()) + } + + pub fn allowed_unsigned(origin: OriginFor) -> DispatchResult { + frame_system::ensure_root(origin)?; + Ok(()) + } + + pub fn unallowed_unsigned(origin: OriginFor) -> DispatchResult { + frame_system::ensure_root(origin)?; + Ok(()) + } + + #[pallet::weight((0, DispatchClass::Mandatory))] + pub fn inherent(origin: OriginFor) -> DispatchResult { + frame_system::ensure_none(origin)?; + Ok(()) + } + + pub fn calculate_storage_root(_origin: OriginFor) -> DispatchResult { + let root = sp_io::storage::root(sp_runtime::StateVersion::V1); + sp_io::storage::set("storage_root".as_bytes(), &root); + Ok(()) + } + } + + #[pallet::inherent] + impl ProvideInherent for Pallet { + type Call = Call; + + type Error = sp_inherents::MakeFatalError<()>; + + const INHERENT_IDENTIFIER: [u8; 8] = *b"test1234"; + + fn create_inherent(_data: &InherentData) -> Option { + None + } + + fn is_inherent(call: &Self::Call) -> bool { + *call == Call::::inherent {} + } + } + + #[pallet::validate_unsigned] + impl ValidateUnsigned for Pallet { + type Call = Call; + + // Inherent call is accepted for being dispatched + fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> { + match call { + Call::allowed_unsigned { .. } => Ok(()), + Call::inherent { .. } => Ok(()), + _ => Err(UnknownTransaction::NoUnsignedValidator.into()), + } + } + + // Inherent call is not validated as unsigned + fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity { + match call { + Call::allowed_unsigned { .. } => Ok(Default::default()), + _ => UnknownTransaction::NoUnsignedValidator.into(), + } + } + } +} + +#[frame_support::pallet(dev_mode)] +mod custom2 { + use super::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::hooks] + impl Hooks> for Pallet { + // module hooks. + // one with block number arg and one without + fn on_initialize(_: BlockNumberFor) -> Weight { + assert!( + !MockedSystemCallbacks::pre_inherent_called(), + "Pre inherent hook goes after on_initialize" + ); + + Weight::from_parts(0, 0) + } + + fn on_idle(_: BlockNumberFor, _: Weight) -> Weight { + assert!( + MockedSystemCallbacks::post_transactions_called(), + "Post transactions hook goes before after on_idle" + ); + Weight::from_parts(0, 0) + } + + fn on_finalize(_: BlockNumberFor) { + assert!( + MockedSystemCallbacks::post_transactions_called(), + "Post transactions hook goes before after on_finalize" + ); + } + + fn on_runtime_upgrade() -> Weight { + sp_io::storage::set(super::TEST_KEY, "module".as_bytes()); + Weight::from_parts(0, 0) + } + } + + #[pallet::call] + impl Pallet { + pub fn allowed_unsigned(origin: OriginFor) -> DispatchResult { + frame_system::ensure_root(origin)?; + Ok(()) + } + + pub fn some_call(_: OriginFor) -> DispatchResult { + assert!(MockedSystemCallbacks::post_inherent_called()); + assert!(!MockedSystemCallbacks::post_transactions_called()); + assert!(System::inherents_applied()); + + Ok(()) + } + + #[pallet::weight({0})] + pub fn optional_inherent(origin: OriginFor) -> DispatchResult { + frame_system::ensure_none(origin)?; + + assert!(MockedSystemCallbacks::pre_inherent_called()); + assert!(!MockedSystemCallbacks::post_inherent_called(), "Should not already be called"); + assert!(!System::inherents_applied()); + + Ok(()) + } + + #[pallet::weight((0, DispatchClass::Mandatory))] + pub fn inherent(origin: OriginFor) -> DispatchResult { + frame_system::ensure_none(origin)?; + + assert!(MockedSystemCallbacks::pre_inherent_called()); + assert!(!MockedSystemCallbacks::post_inherent_called(), "Should not already be called"); + assert!(!System::inherents_applied()); + + Ok(()) + } + } + + #[pallet::inherent] + impl ProvideInherent for Pallet { + type Call = Call; + + type Error = sp_inherents::MakeFatalError<()>; + + const INHERENT_IDENTIFIER: [u8; 8] = *b"test1235"; + + fn create_inherent(_data: &InherentData) -> Option { + None + } + + fn is_inherent(call: &Self::Call) -> bool { + matches!(call, Call::::inherent {} | Call::::optional_inherent {}) + } + } + + #[pallet::validate_unsigned] + impl ValidateUnsigned for Pallet { + type Call = Call; + + // Inherent call is accepted for being dispatched + fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> { + match call { + Call::allowed_unsigned { .. } | + Call::optional_inherent { .. } | + Call::inherent { .. } => Ok(()), + _ => Err(UnknownTransaction::NoUnsignedValidator.into()), + } + } + + // Inherent call is not validated as unsigned + fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity { + match call { + Call::allowed_unsigned { .. } => Ok(Default::default()), + _ => UnknownTransaction::NoUnsignedValidator.into(), + } + } + } +} + +frame_support::construct_runtime!( + pub struct Runtime + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + Custom: custom::{Pallet, Call, ValidateUnsigned, Inherent}, + Custom2: custom2::{Pallet, Call, ValidateUnsigned, Inherent}, + } +); + +parameter_types! { + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::builder() + .base_block(Weight::from_parts(10, 0)) + .for_class(DispatchClass::all(), |weights| weights.base_extrinsic = Weight::from_parts(5, 0)) + .for_class(DispatchClass::non_mandatory(), |weights| weights.max_total = Weight::from_parts(1024, u64::MAX).into()) + .build_or_panic(); + pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { + read: 10, + write: 100, + }; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Runtime { + type BlockWeights = BlockWeights; + type RuntimeOrigin = RuntimeOrigin; + type Nonce = u64; + type RuntimeCall = RuntimeCall; + type Block = TestBlock; + type RuntimeEvent = RuntimeEvent; + type Version = RuntimeVersion; + type AccountData = pallet_balances::AccountData; + type PreInherents = MockedSystemCallbacks; + type PostInherents = MockedSystemCallbacks; + type PostTransactions = MockedSystemCallbacks; + type MultiBlockMigrator = MockedModeGetter; +} + +type Balance = u64; + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Runtime { + type Balance = Balance; + type AccountStore = System; +} + +parameter_types! { + pub const TransactionByteFee: Balance = 0; +} +impl pallet_transaction_payment::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = CurrencyAdapter; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = IdentityFee; + type LengthToFee = ConstantMultiplier; + type FeeMultiplierUpdate = (); +} + +impl custom::Config for Runtime {} +impl custom2::Config for Runtime {} + +pub struct RuntimeVersion; +impl frame_support::traits::Get for RuntimeVersion { + fn get() -> sp_version::RuntimeVersion { + RuntimeVersionTestValues::get().clone() + } +} + +parameter_types! { + pub static RuntimeVersionTestValues: sp_version::RuntimeVersion = + Default::default(); +} + +type SignedExtra = ( + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); +type TestXt = sp_runtime::testing::TestXt; +type TestBlock = Block; + +// Will contain `true` when the custom runtime logic was called. +const CUSTOM_ON_RUNTIME_KEY: &[u8] = b":custom:on_runtime"; + +struct CustomOnRuntimeUpgrade; +impl OnRuntimeUpgrade for CustomOnRuntimeUpgrade { + fn on_runtime_upgrade() -> Weight { + sp_io::storage::set(TEST_KEY, "custom_upgrade".as_bytes()); + sp_io::storage::set(CUSTOM_ON_RUNTIME_KEY, &true.encode()); + System::deposit_event(frame_system::Event::CodeUpdated); + + assert_eq!(0, System::last_runtime_upgrade_spec_version()); + + Weight::from_parts(100, 0) + } +} + +type Executive = super::Executive< + Runtime, + Block, + ChainContext, + Runtime, + AllPalletsWithSystem, + CustomOnRuntimeUpgrade, +>; + +parameter_types! { + pub static SystemCallbacksCalled: u32 = 0; +} + +pub struct MockedSystemCallbacks; +impl PreInherents for MockedSystemCallbacks { + fn pre_inherents() { + assert_eq!(SystemCallbacksCalled::get(), 0); + SystemCallbacksCalled::set(1); + // Change the storage to modify the root hash: + frame_support::storage::unhashed::put(b":pre_inherent", b"0"); + } +} + +impl PostInherents for MockedSystemCallbacks { + fn post_inherents() { + assert_eq!(SystemCallbacksCalled::get(), 1); + SystemCallbacksCalled::set(2); + // Change the storage to modify the root hash: + frame_support::storage::unhashed::put(b":post_inherent", b"0"); + } +} + +impl PostTransactions for MockedSystemCallbacks { + fn post_transactions() { + assert_eq!(SystemCallbacksCalled::get(), 2); + SystemCallbacksCalled::set(3); + // Change the storage to modify the root hash: + frame_support::storage::unhashed::put(b":post_transaction", b"0"); + } +} + +impl MockedSystemCallbacks { + fn pre_inherent_called() -> bool { + SystemCallbacksCalled::get() >= 1 + } + + fn post_inherent_called() -> bool { + SystemCallbacksCalled::get() >= 2 + } + + fn post_transactions_called() -> bool { + SystemCallbacksCalled::get() >= 3 + } + + fn reset() { + SystemCallbacksCalled::set(0); + frame_support::storage::unhashed::kill(b":pre_inherent"); + frame_support::storage::unhashed::kill(b":post_inherent"); + frame_support::storage::unhashed::kill(b":post_transaction"); + } +} + +parameter_types! { + pub static MbmActive: bool = false; +} + +pub struct MockedModeGetter; +impl MultiStepMigrator for MockedModeGetter { + fn ongoing() -> bool { + MbmActive::get() + } + + fn step() -> Weight { + Weight::zero() + } +} + +fn extra(nonce: u64, fee: Balance) -> SignedExtra { + ( + frame_system::CheckEra::from(Era::Immortal), + frame_system::CheckNonce::from(nonce), + frame_system::CheckWeight::new(), + pallet_transaction_payment::ChargeTransactionPayment::from(fee), + ) +} + +fn sign_extra(who: u64, nonce: u64, fee: Balance) -> Option<(u64, SignedExtra)> { + Some((who, extra(nonce, fee))) +} + +fn call_transfer(dest: u64, value: u64) -> RuntimeCall { + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value }) +} + +#[test] +fn balance_transfer_dispatch_works() { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + pallet_balances::GenesisConfig:: { balances: vec![(1, 211)] } + .assimilate_storage(&mut t) + .unwrap(); + let xt = TestXt::new(call_transfer(2, 69), sign_extra(1, 0, 0)); + let weight = xt.get_dispatch_info().weight + + ::BlockWeights::get() + .get(DispatchClass::Normal) + .base_extrinsic; + let fee: Balance = + ::WeightToFee::weight_to_fee(&weight); + let mut t = sp_io::TestExternalities::new(t); + t.execute_with(|| { + Executive::initialize_block(&Header::new_from_number(1)); + let r = Executive::apply_extrinsic(xt); + assert!(r.is_ok()); + assert_eq!(>::total_balance(&1), 142 - fee); + assert_eq!(>::total_balance(&2), 69); + }); +} + +fn new_test_ext(balance_factor: Balance) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + pallet_balances::GenesisConfig:: { balances: vec![(1, 111 * balance_factor)] } + .assimilate_storage(&mut t) + .unwrap(); + let mut ext: sp_io::TestExternalities = t.into(); + ext.execute_with(|| { + SystemCallbacksCalled::set(0); + }); + ext +} + +fn new_test_ext_v0(balance_factor: Balance) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + pallet_balances::GenesisConfig:: { balances: vec![(1, 111 * balance_factor)] } + .assimilate_storage(&mut t) + .unwrap(); + (t, sp_runtime::StateVersion::V0).into() +} + +#[test] +fn block_import_works() { + block_import_works_inner( + new_test_ext_v0(1), + array_bytes::hex_n_into_unchecked( + "4826d3bdf87dbbc883d2ab274cbe272f58ed94a904619b59953e48294d1142d2", + ), + ); + block_import_works_inner( + new_test_ext(1), + array_bytes::hex_n_into_unchecked( + "d6b465f5a50c9f8d5a6edc0f01d285a6b19030f097d3aaf1649b7be81649f118", + ), + ); +} +fn block_import_works_inner(mut ext: sp_io::TestExternalities, state_root: H256) { + ext.execute_with(|| { + Executive::execute_block(Block { + header: Header { + parent_hash: [69u8; 32].into(), + number: 1, + state_root, + extrinsics_root: array_bytes::hex_n_into_unchecked( + "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314", + ), + digest: Digest { logs: vec![] }, + }, + extrinsics: vec![], + }); + }); +} + +#[test] +#[should_panic] +fn block_import_of_bad_state_root_fails() { + new_test_ext(1).execute_with(|| { + Executive::execute_block(Block { + header: Header { + parent_hash: [69u8; 32].into(), + number: 1, + state_root: [0u8; 32].into(), + extrinsics_root: array_bytes::hex_n_into_unchecked( + "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314", + ), + digest: Digest { logs: vec![] }, + }, + extrinsics: vec![], + }); + }); +} + +#[test] +#[should_panic] +fn block_import_of_bad_extrinsic_root_fails() { + new_test_ext(1).execute_with(|| { + Executive::execute_block(Block { + header: Header { + parent_hash: [69u8; 32].into(), + number: 1, + state_root: array_bytes::hex_n_into_unchecked( + "75e7d8f360d375bbe91bcf8019c01ab6362448b4a89e3b329717eb9d910340e5", + ), + extrinsics_root: [0u8; 32].into(), + digest: Digest { logs: vec![] }, + }, + extrinsics: vec![], + }); + }); +} + +#[test] +fn bad_extrinsic_not_inserted() { + let mut t = new_test_ext(1); + // bad nonce check! + let xt = TestXt::new(call_transfer(33, 69), sign_extra(1, 30, 0)); + t.execute_with(|| { + Executive::initialize_block(&Header::new_from_number(1)); + assert_err!( + Executive::apply_extrinsic(xt), + TransactionValidityError::Invalid(InvalidTransaction::Future) + ); + assert_eq!(>::extrinsic_index(), Some(0)); + }); +} + +#[test] +fn block_weight_limit_enforced() { + let mut t = new_test_ext(10000); + // given: TestXt uses the encoded len as fixed Len: + let xt = TestXt::new( + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), + sign_extra(1, 0, 0), + ); + let encoded = xt.encode(); + let encoded_len = encoded.len() as u64; + // on_initialize weight + base block execution weight + let block_weights = ::BlockWeights::get(); + let base_block_weight = Weight::from_parts(175, 0) + block_weights.base_block; + let limit = block_weights.get(DispatchClass::Normal).max_total.unwrap() - base_block_weight; + let num_to_exhaust_block = limit.ref_time() / (encoded_len + 5); + t.execute_with(|| { + Executive::initialize_block(&Header::new_from_number(1)); + // Base block execution weight + `on_initialize` weight from the custom module. + assert_eq!(>::block_weight().total(), base_block_weight); + + for nonce in 0..=num_to_exhaust_block { + let xt = TestXt::new( + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), + sign_extra(1, nonce.into(), 0), + ); + let res = Executive::apply_extrinsic(xt); + if nonce != num_to_exhaust_block { + assert!(res.is_ok()); + assert_eq!( + >::block_weight().total(), + //--------------------- on_initialize + block_execution + extrinsic_base weight + Weight::from_parts((encoded_len + 5) * (nonce + 1), 0) + base_block_weight, + ); + assert_eq!( + >::extrinsic_index(), + Some(nonce as u32 + 1) + ); + } else { + assert_eq!(res, Err(InvalidTransaction::ExhaustsResources.into())); + } + } + }); +} + +#[test] +fn block_weight_and_size_is_stored_per_tx() { + let xt = TestXt::new( + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), + sign_extra(1, 0, 0), + ); + let x1 = TestXt::new( + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), + sign_extra(1, 1, 0), + ); + let x2 = TestXt::new( + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), + sign_extra(1, 2, 0), + ); + let len = xt.clone().encode().len() as u32; + let mut t = new_test_ext(1); + t.execute_with(|| { + // Block execution weight + on_initialize weight from custom module + let base_block_weight = Weight::from_parts(175, 0) + + ::BlockWeights::get().base_block; + + Executive::initialize_block(&Header::new_from_number(1)); + + assert_eq!(>::block_weight().total(), base_block_weight); + assert_eq!(>::all_extrinsics_len(), 0); + + assert!(Executive::apply_extrinsic(xt.clone()).unwrap().is_ok()); + assert!(Executive::apply_extrinsic(x1.clone()).unwrap().is_ok()); + assert!(Executive::apply_extrinsic(x2.clone()).unwrap().is_ok()); + + // default weight for `TestXt` == encoded length. + let extrinsic_weight = Weight::from_parts(len as u64, 0) + + ::BlockWeights::get() + .get(DispatchClass::Normal) + .base_extrinsic; + assert_eq!( + >::block_weight().total(), + base_block_weight + 3u64 * extrinsic_weight, + ); + assert_eq!(>::all_extrinsics_len(), 3 * len); + + let _ = >::finalize(); + // All extrinsics length cleaned on `System::finalize` + assert_eq!(>::all_extrinsics_len(), 0); + + // Reset to a new block. + SystemCallbacksCalled::take(); + Executive::initialize_block(&Header::new_from_number(2)); + + // Block weight cleaned up on `System::initialize` + assert_eq!(>::block_weight().total(), base_block_weight); + }); +} + +#[test] +fn validate_unsigned() { + let valid = TestXt::new(RuntimeCall::Custom(custom::Call::allowed_unsigned {}), None); + let invalid = TestXt::new(RuntimeCall::Custom(custom::Call::unallowed_unsigned {}), None); + let mut t = new_test_ext(1); + + t.execute_with(|| { + assert_eq!( + Executive::validate_transaction( + TransactionSource::InBlock, + valid.clone(), + Default::default(), + ), + Ok(ValidTransaction::default()), + ); + assert_eq!( + Executive::validate_transaction( + TransactionSource::InBlock, + invalid.clone(), + Default::default(), + ), + Err(TransactionValidityError::Unknown(UnknownTransaction::NoUnsignedValidator)), + ); + // Need to initialize the block before applying extrinsics for the `MockedSystemCallbacks` + // check. + Executive::initialize_block(&Header::new_from_number(1)); + assert_eq!(Executive::apply_extrinsic(valid), Ok(Err(DispatchError::BadOrigin))); + assert_eq!( + Executive::apply_extrinsic(invalid), + Err(TransactionValidityError::Unknown(UnknownTransaction::NoUnsignedValidator)) + ); + }); +} + +#[test] +fn can_not_pay_for_tx_fee_on_full_lock() { + let mut t = new_test_ext(1); + t.execute_with(|| { + as fungible::MutateFreeze>::set_freeze(&(), &1, 110) + .unwrap(); + let xt = TestXt::new( + RuntimeCall::System(frame_system::Call::remark { remark: vec![1u8] }), + sign_extra(1, 0, 0), + ); + Executive::initialize_block(&Header::new_from_number(1)); + + assert_eq!(Executive::apply_extrinsic(xt), Err(InvalidTransaction::Payment.into()),); + assert_eq!(>::total_balance(&1), 111); + }); +} + +#[test] +fn block_hooks_weight_is_stored() { + new_test_ext(1).execute_with(|| { + Executive::initialize_block(&Header::new_from_number(1)); + Executive::finalize_block(); + // NOTE: might need updates over time if new weights are introduced. + // For now it only accounts for the base block execution weight and + // the `on_initialize` weight defined in the custom test module. + assert_eq!( + >::block_weight().total(), + Weight::from_parts(175 + 175 + 10, 0) + ); + }) +} + +#[test] +fn runtime_upgraded_should_work() { + new_test_ext(1).execute_with(|| { + RuntimeVersionTestValues::mutate(|v| *v = Default::default()); + // It should be added at genesis + assert!(LastRuntimeUpgrade::::exists()); + assert!(!Executive::runtime_upgraded()); + + RuntimeVersionTestValues::mutate(|v| { + *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } + }); + assert!(Executive::runtime_upgraded()); + + RuntimeVersionTestValues::mutate(|v| { + *v = sp_version::RuntimeVersion { + spec_version: 1, + spec_name: "test".into(), + ..Default::default() + } + }); + assert!(Executive::runtime_upgraded()); + + RuntimeVersionTestValues::mutate(|v| { + *v = sp_version::RuntimeVersion { + spec_version: 0, + impl_version: 2, + ..Default::default() + } + }); + assert!(!Executive::runtime_upgraded()); + + LastRuntimeUpgrade::::take(); + assert!(Executive::runtime_upgraded()); + }) +} + +#[test] +fn last_runtime_upgrade_was_upgraded_works() { + let test_data = vec![ + (0, "", 1, "", true), + (1, "", 1, "", false), + (1, "", 1, "test", true), + (1, "", 0, "", false), + (1, "", 0, "test", true), + ]; + + for (spec_version, spec_name, c_spec_version, c_spec_name, result) in test_data { + let current = sp_version::RuntimeVersion { + spec_version: c_spec_version, + spec_name: c_spec_name.into(), + ..Default::default() + }; + + let last = LastRuntimeUpgradeInfo { + spec_version: spec_version.into(), + spec_name: spec_name.into(), + }; + + assert_eq!(result, last.was_upgraded(¤t)); + } +} + +#[test] +fn custom_runtime_upgrade_is_called_before_modules() { + new_test_ext(1).execute_with(|| { + // Make sure `on_runtime_upgrade` is called. + RuntimeVersionTestValues::mutate(|v| { + *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } + }); + + Executive::initialize_block(&Header::new_from_number(1)); + + assert_eq!(&sp_io::storage::get(TEST_KEY).unwrap()[..], *b"module"); + assert_eq!(sp_io::storage::get(CUSTOM_ON_RUNTIME_KEY).unwrap(), true.encode()); + assert_eq!( + Some(RuntimeVersionTestValues::get().into()), + LastRuntimeUpgrade::::get(), + ) + }); +} + +#[test] +fn event_from_runtime_upgrade_is_included() { + new_test_ext(1).execute_with(|| { + // Make sure `on_runtime_upgrade` is called. + RuntimeVersionTestValues::mutate(|v| { + *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } + }); + + // set block number to non zero so events are not excluded + System::set_block_number(1); + + Executive::initialize_block(&Header::new_from_number(2)); + System::assert_last_event(frame_system::Event::::CodeUpdated.into()); + }); +} + +/// Regression test that ensures that the custom on runtime upgrade is called when executive is +/// used through the `ExecuteBlock` trait. +#[test] +fn custom_runtime_upgrade_is_called_when_using_execute_block_trait() { + let xt = TestXt::new( + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), + sign_extra(1, 0, 0), + ); + + let header = new_test_ext(1).execute_with(|| { + // Make sure `on_runtime_upgrade` is called. + RuntimeVersionTestValues::mutate(|v| { + *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } + }); + + // Let's build some fake block. + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(xt.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + // Reset to get the correct new genesis below. + RuntimeVersionTestValues::mutate(|v| { + *v = sp_version::RuntimeVersion { spec_version: 0, ..Default::default() } + }); + + new_test_ext(1).execute_with(|| { + // Make sure `on_runtime_upgrade` is called. + RuntimeVersionTestValues::mutate(|v| { + *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } + }); + + >>::execute_block(Block::new(header, vec![xt])); + + assert_eq!(&sp_io::storage::get(TEST_KEY).unwrap()[..], *b"module"); + assert_eq!(sp_io::storage::get(CUSTOM_ON_RUNTIME_KEY).unwrap(), true.encode()); + }); +} + +#[test] +fn all_weights_are_recorded_correctly() { + // Reset to get the correct new genesis below. + RuntimeVersionTestValues::take(); + + new_test_ext(1).execute_with(|| { + // Make sure `on_runtime_upgrade` is called for maximum complexity + RuntimeVersionTestValues::mutate(|v| { + *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } + }); + + let block_number = 1; + + Executive::initialize_block(&Header::new_from_number(block_number)); + + // Reset the last runtime upgrade info, to make the second call to `on_runtime_upgrade` + // succeed. + LastRuntimeUpgrade::::take(); + MockedSystemCallbacks::reset(); + + // All weights that show up in the `initialize_block_impl` + let custom_runtime_upgrade_weight = CustomOnRuntimeUpgrade::on_runtime_upgrade(); + let runtime_upgrade_weight = + ::on_runtime_upgrade(); + let on_initialize_weight = + >::on_initialize(block_number); + let base_block_weight = ::BlockWeights::get().base_block; + + // Weights are recorded correctly + assert_eq!( + frame_system::Pallet::::block_weight().total(), + custom_runtime_upgrade_weight + + runtime_upgrade_weight + + on_initialize_weight + + base_block_weight, + ); + }); +} + +#[test] +fn offchain_worker_works_as_expected() { + new_test_ext(1).execute_with(|| { + let parent_hash = sp_core::H256::from([69u8; 32]); + let mut digest = Digest::default(); + digest.push(DigestItem::Seal([1, 2, 3, 4], vec![5, 6, 7, 8])); + + let header = Header::new(1, H256::default(), H256::default(), parent_hash, digest.clone()); + + Executive::offchain_worker(&header); + + assert_eq!(digest, System::digest()); + assert_eq!(parent_hash, System::block_hash(0)); + assert_eq!(header.hash(), System::block_hash(1)); + }); +} + +#[test] +fn calculating_storage_root_twice_works() { + let call = RuntimeCall::Custom(custom::Call::calculate_storage_root {}); + let xt = TestXt::new(call, sign_extra(1, 0, 0)); + + let header = new_test_ext(1).execute_with(|| { + // Let's build some fake block. + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(xt.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + new_test_ext(1).execute_with(|| { + Executive::execute_block(Block::new(header, vec![xt])); + }); +} + +#[test] +#[should_panic(expected = "Invalid inherent position for extrinsic at index 1")] +fn invalid_inherent_position_fail() { + let xt1 = TestXt::new( + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), + sign_extra(1, 0, 0), + ); + let xt2 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None); + + let header = new_test_ext(1).execute_with(|| { + // Let's build some fake block. + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); + Executive::apply_extrinsic(xt2.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + new_test_ext(1).execute_with(|| { + Executive::execute_block(Block::new(header, vec![xt1, xt2])); + }); +} + +#[test] +fn valid_inherents_position_works() { + let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None); + let xt2 = TestXt::new(call_transfer(33, 0), sign_extra(1, 0, 0)); + + let header = new_test_ext(1).execute_with(|| { + // Let's build some fake block. + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); + Executive::apply_extrinsic(xt2.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + new_test_ext(1).execute_with(|| { + Executive::execute_block(Block::new(header, vec![xt1, xt2])); + }); +} + +#[test] +#[should_panic(expected = "A call was labelled as mandatory, but resulted in an Error.")] +fn invalid_inherents_fail_block_execution() { + let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), sign_extra(1, 0, 0)); + + new_test_ext(1).execute_with(|| { + Executive::execute_block(Block::new( + Header::new(1, H256::default(), H256::default(), [69u8; 32].into(), Digest::default()), + vec![xt1], + )); + }); +} + +// Inherents are created by the runtime and don't need to be validated. +#[test] +fn inherents_fail_validate_block() { + let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None); + + new_test_ext(1).execute_with(|| { + assert_eq!( + Executive::validate_transaction(TransactionSource::External, xt1, H256::random()) + .unwrap_err(), + InvalidTransaction::MandatoryValidation.into() + ); + }) +} + +/// Inherents still work while `initialize_block` forbids transactions. +#[test] +fn inherents_ok_while_exts_forbidden_works() { + let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None); + + let header = new_test_ext(1).execute_with(|| { + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); + // This is not applied: + Executive::finalize_block() + }); + + new_test_ext(1).execute_with(|| { + // Tell `initialize_block` to forbid extrinsics: + Executive::execute_block(Block::new(header, vec![xt1])); + }); +} + +/// Refuses to import blocks with transactions during `OnlyInherents` mode. +#[test] +#[should_panic = "Only inherents are allowed in this block"] +fn transactions_in_only_inherents_block_errors() { + let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None); + let xt2 = TestXt::new(call_transfer(33, 0), sign_extra(1, 0, 0)); + + let header = new_test_ext(1).execute_with(|| { + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); + Executive::apply_extrinsic(xt2.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + new_test_ext(1).execute_with(|| { + MbmActive::set(true); + Executive::execute_block(Block::new(header, vec![xt1, xt2])); + }); +} + +/// Same as above but no error. +#[test] +fn transactions_in_normal_block_works() { + let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None); + let xt2 = TestXt::new(call_transfer(33, 0), sign_extra(1, 0, 0)); + + let header = new_test_ext(1).execute_with(|| { + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); + Executive::apply_extrinsic(xt2.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + new_test_ext(1).execute_with(|| { + // Tell `initialize_block` to forbid extrinsics: + Executive::execute_block(Block::new(header, vec![xt1, xt2])); + }); +} + +#[test] +#[cfg(feature = "try-runtime")] +fn try_execute_block_works() { + let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None); + let xt2 = TestXt::new(call_transfer(33, 0), sign_extra(1, 0, 0)); + + let header = new_test_ext(1).execute_with(|| { + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); + Executive::apply_extrinsic(xt2.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + new_test_ext(1).execute_with(|| { + Executive::try_execute_block( + Block::new(header, vec![xt1, xt2]), + true, + true, + frame_try_runtime::TryStateSelect::All, + ) + .unwrap(); + }); +} + +/// Same as `extrinsic_while_exts_forbidden_errors` but using the try-runtime function. +#[test] +#[cfg(feature = "try-runtime")] +#[should_panic = "Only inherents allowed"] +fn try_execute_tx_forbidden_errors() { + let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None); + let xt2 = TestXt::new(call_transfer(33, 0), sign_extra(1, 0, 0)); + + let header = new_test_ext(1).execute_with(|| { + // Let's build some fake block. + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); + Executive::apply_extrinsic(xt2.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + new_test_ext(1).execute_with(|| { + MbmActive::set(true); + Executive::try_execute_block( + Block::new(header, vec![xt1, xt2]), + true, + true, + frame_try_runtime::TryStateSelect::All, + ) + .unwrap(); + }); +} + +/// Check that `ensure_inherents_are_first` reports the correct indices. +#[test] +fn ensure_inherents_are_first_works() { + let in1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None); + let in2 = TestXt::new(RuntimeCall::Custom2(custom2::Call::inherent {}), None); + let xt2 = TestXt::new(call_transfer(33, 0), sign_extra(1, 0, 0)); + + // Mocked empty header: + let header = new_test_ext(1).execute_with(|| { + Executive::initialize_block(&Header::new_from_number(1)); + Executive::finalize_block() + }); + + new_test_ext(1).execute_with(|| { + assert_ok!(Runtime::ensure_inherents_are_first(&Block::new(header.clone(), vec![]),), 0); + assert_ok!( + Runtime::ensure_inherents_are_first(&Block::new(header.clone(), vec![xt2.clone()]),), + 0 + ); + assert_ok!( + Runtime::ensure_inherents_are_first(&Block::new(header.clone(), vec![in1.clone()])), + 1 + ); + assert_ok!( + Runtime::ensure_inherents_are_first(&Block::new( + header.clone(), + vec![in1.clone(), xt2.clone()] + ),), + 1 + ); + assert_ok!( + Runtime::ensure_inherents_are_first(&Block::new( + header.clone(), + vec![in2.clone(), in1.clone(), xt2.clone()] + ),), + 2 + ); + + assert_eq!( + Runtime::ensure_inherents_are_first(&Block::new( + header.clone(), + vec![xt2.clone(), in1.clone()] + ),), + Err(1) + ); + assert_eq!( + Runtime::ensure_inherents_are_first(&Block::new( + header.clone(), + vec![xt2.clone(), xt2.clone(), in1.clone()] + ),), + Err(2) + ); + assert_eq!( + Runtime::ensure_inherents_are_first(&Block::new( + header.clone(), + vec![xt2.clone(), xt2.clone(), xt2.clone(), in2.clone()] + ),), + Err(3) + ); + }); +} + +/// Check that block execution rejects blocks with transactions in them while MBMs are active and +/// also that all the system callbacks are called correctly. +#[test] +fn callbacks_in_block_execution_works() { + callbacks_in_block_execution_works_inner(false); + callbacks_in_block_execution_works_inner(true); +} + +fn callbacks_in_block_execution_works_inner(mbms_active: bool) { + MbmActive::set(mbms_active); + + for (n_in, n_tx) in (0..10usize).zip(0..10usize) { + let mut extrinsics = Vec::new(); + + let header = new_test_ext(10).execute_with(|| { + MockedSystemCallbacks::reset(); + Executive::initialize_block(&Header::new_from_number(1)); + assert_eq!(SystemCallbacksCalled::get(), 1); + + for i in 0..n_in { + let xt = if i % 2 == 0 { + TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None) + } else { + TestXt::new(RuntimeCall::Custom2(custom2::Call::optional_inherent {}), None) + }; + Executive::apply_extrinsic(xt.clone()).unwrap().unwrap(); + extrinsics.push(xt); + } + + for t in 0..n_tx { + let xt = TestXt::new( + RuntimeCall::Custom2(custom2::Call::some_call {}), + sign_extra(1, t as u64, 0), + ); + // Extrinsics can be applied even when MBMs are active. Only the `execute_block` + // will reject it. + Executive::apply_extrinsic(xt.clone()).unwrap().unwrap(); + extrinsics.push(xt); + } + + Executive::finalize_block() + }); + assert_eq!(SystemCallbacksCalled::get(), 3); + + new_test_ext(10).execute_with(|| { + let header = std::panic::catch_unwind(|| { + Executive::execute_block(Block::new(header, extrinsics)); + }); + + match header { + Err(e) => { + let err = e.downcast::<&str>().unwrap(); + assert_eq!(*err, "Only inherents are allowed in this block"); + assert!( + MbmActive::get() && n_tx > 0, + "Transactions should be rejected when MBMs are active" + ); + }, + Ok(_) => { + assert_eq!(SystemCallbacksCalled::get(), 3); + assert!( + !MbmActive::get() || n_tx == 0, + "MBMs should be deactivated after finalization" + ); + }, + } + }); + } +} + +#[test] +fn post_inherent_called_after_all_inherents() { + let in1 = TestXt::new(RuntimeCall::Custom2(custom2::Call::inherent {}), None); + let xt1 = TestXt::new(RuntimeCall::Custom2(custom2::Call::some_call {}), sign_extra(1, 0, 0)); + + let header = new_test_ext(1).execute_with(|| { + // Let's build some fake block. + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(in1.clone()).unwrap().unwrap(); + Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + #[cfg(feature = "try-runtime")] + new_test_ext(1).execute_with(|| { + Executive::try_execute_block( + Block::new(header.clone(), vec![in1.clone(), xt1.clone()]), + true, + true, + frame_try_runtime::TryStateSelect::All, + ) + .unwrap(); + assert!(MockedSystemCallbacks::post_transactions_called()); + }); + + new_test_ext(1).execute_with(|| { + MockedSystemCallbacks::reset(); + Executive::execute_block(Block::new(header, vec![in1, xt1])); + assert!(MockedSystemCallbacks::post_transactions_called()); + }); +} + +/// Regression test for AppSec finding #40. +#[test] +fn post_inherent_called_after_all_optional_inherents() { + let in1 = TestXt::new(RuntimeCall::Custom2(custom2::Call::optional_inherent {}), None); + let xt1 = TestXt::new(RuntimeCall::Custom2(custom2::Call::some_call {}), sign_extra(1, 0, 0)); + + let header = new_test_ext(1).execute_with(|| { + // Let's build some fake block. + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(in1.clone()).unwrap().unwrap(); + Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + #[cfg(feature = "try-runtime")] + new_test_ext(1).execute_with(|| { + Executive::try_execute_block( + Block::new(header.clone(), vec![in1.clone(), xt1.clone()]), + true, + true, + frame_try_runtime::TryStateSelect::All, + ) + .unwrap(); + assert!(MockedSystemCallbacks::post_transactions_called()); + }); + + new_test_ext(1).execute_with(|| { + MockedSystemCallbacks::reset(); + Executive::execute_block(Block::new(header, vec![in1, xt1])); + assert!(MockedSystemCallbacks::post_transactions_called()); + }); +} + +#[test] +fn is_inherent_works() { + let ext = TestXt::new(RuntimeCall::Custom2(custom2::Call::inherent {}), None); + assert!(Runtime::is_inherent(&ext)); + let ext = TestXt::new(RuntimeCall::Custom2(custom2::Call::optional_inherent {}), None); + assert!(Runtime::is_inherent(&ext)); + + let ext = TestXt::new(call_transfer(33, 0), sign_extra(1, 0, 0)); + assert!(!Runtime::is_inherent(&ext)); + + let ext = TestXt::new(RuntimeCall::Custom2(custom2::Call::allowed_unsigned {}), None); + assert!(!Runtime::is_inherent(&ext), "Unsigned ext are not automatically inherents"); +} diff --git a/substrate/frame/fast-unstake/src/migrations.rs b/substrate/frame/fast-unstake/src/migrations.rs index 564388407045e0ae86b87c7823f3f07c5613e012..97ad86bfff42bf6ee1e258f5663d9bb53144373c 100644 --- a/substrate/frame/fast-unstake/src/migrations.rs +++ b/substrate/frame/fast-unstake/src/migrations.rs @@ -33,12 +33,12 @@ pub mod v1 { pub struct MigrateToV1(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); + let current = Pallet::::in_code_storage_version(); let onchain = Pallet::::on_chain_storage_version(); log!( info, - "Running migration with current storage version {:?} / onchain {:?}", + "Running migration with in-code storage version {:?} / onchain {:?}", current, onchain ); diff --git a/substrate/frame/fast-unstake/src/mock.rs b/substrate/frame/fast-unstake/src/mock.rs index 78d881965a5531dea8a56a2f94015cf39afa2d90..b731cb822f336d6dd2da8e0d5ca501090909a31a 100644 --- a/substrate/frame/fast-unstake/src/mock.rs +++ b/substrate/frame/fast-unstake/src/mock.rs @@ -43,7 +43,7 @@ parameter_types! { ); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type Block = Block; type AccountData = pallet_balances::AccountData; diff --git a/substrate/frame/fast-unstake/src/tests.rs b/substrate/frame/fast-unstake/src/tests.rs index b19fe3b8c463ba54571d471b0672d8b7f7d88426..77128872f285f379237153290e394f3a461226e8 100644 --- a/substrate/frame/fast-unstake/src/tests.rs +++ b/substrate/frame/fast-unstake/src/tests.rs @@ -110,7 +110,7 @@ fn cannot_register_if_head() { stashes: bounded_vec![(1, Deposit::get())], checked: bounded_vec![], }); - // Controller attempts to regsiter + // Controller attempts to register assert_noop!( FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1)), Error::::AlreadyHead diff --git a/substrate/frame/glutton/src/mock.rs b/substrate/frame/glutton/src/mock.rs index 0049800d95298386a9ed63e23c608d4a2938c758..132ef5cfbcbbabb077b7968cc0ae1451bcd06bc7 100644 --- a/substrate/frame/glutton/src/mock.rs +++ b/substrate/frame/glutton/src/mock.rs @@ -31,7 +31,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } diff --git a/substrate/frame/grandpa/src/benchmarking.rs b/substrate/frame/grandpa/src/benchmarking.rs index 7a87f0c4b07881864aefd8eb5aad55ea41fc713d..c89592b3b3590cc6c99b2a9e546eb7a280e843c5 100644 --- a/substrate/frame/grandpa/src/benchmarking.rs +++ b/substrate/frame/grandpa/src/benchmarking.rs @@ -29,7 +29,7 @@ benchmarks! { // NOTE: generated with the test below `test_generate_equivocation_report_blob`. // the output should be deterministic since the keys we use are static. // with the current benchmark setup it is not possible to generate this - // programatically from the benchmark setup. + // programmatically from the benchmark setup. const EQUIVOCATION_PROOF_BLOB: [u8; 257] = [ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 136, 220, 52, 23, 213, 5, 142, 196, 180, 80, 62, 12, 18, 234, 26, 10, 137, 190, 32, diff --git a/substrate/frame/grandpa/src/lib.rs b/substrate/frame/grandpa/src/lib.rs index 0b9f2b3582792e9d93695d34656912a8d70a131f..90bcd8721dfa1f6d091edc34da883c2172d9f6ca 100644 --- a/substrate/frame/grandpa/src/lib.rs +++ b/substrate/frame/grandpa/src/lib.rs @@ -73,7 +73,7 @@ pub mod pallet { use frame_support::{dispatch::DispatchResult, pallet_prelude::*}; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(5); #[pallet::pallet] diff --git a/substrate/frame/grandpa/src/mock.rs b/substrate/frame/grandpa/src/mock.rs index 5d48f974c31476f74a876d3ed8a74ff4b2bdf8d3..4a21da655e5b3d4b219caed097619fa3268eca57 100644 --- a/substrate/frame/grandpa/src/mock.rs +++ b/substrate/frame/grandpa/src/mock.rs @@ -66,7 +66,7 @@ impl_opaque_keys! { } } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; diff --git a/substrate/frame/grandpa/src/tests.rs b/substrate/frame/grandpa/src/tests.rs index 993d72af6d41062a24362bbbdaf394cb651dd2c7..8b12d63adaadb27c3835f29d8345bd6e6a5b6997 100644 --- a/substrate/frame/grandpa/src/tests.rs +++ b/substrate/frame/grandpa/src/tests.rs @@ -634,7 +634,7 @@ fn report_equivocation_invalid_equivocation_proof() { (1, H256::zero(), 10, &equivocation_keyring), )); - // votes targetting different rounds, there is no equivocation. + // votes targeting different rounds, there is no equivocation. assert_invalid_equivocation_proof(generate_equivocation_proof( set_id, (1, H256::random(), 10, &equivocation_keyring), diff --git a/substrate/frame/identity/src/benchmarking.rs b/substrate/frame/identity/src/benchmarking.rs index fe2fb0b048933c13425c0127b752bf773e00bea2..cdcdb9522615d0439f4d8370551d73b710edc685 100644 --- a/substrate/frame/identity/src/benchmarking.rs +++ b/substrate/frame/identity/src/benchmarking.rs @@ -23,9 +23,7 @@ use super::*; use crate::Pallet as Identity; use codec::Encode; -use frame_benchmarking::{ - account, impl_benchmark_test_suite, v2::*, whitelisted_caller, BenchmarkError, -}; +use frame_benchmarking::{account, v2::*, whitelisted_caller, BenchmarkError}; use frame_support::{ assert_ok, ensure, traits::{EnsureOrigin, Get, OnFinalize, OnInitialize}, @@ -673,10 +671,10 @@ mod benchmarks { let username = bounded_username::(bench_username(), bench_suffix()); Identity::::queue_acceptance(&caller, username.clone()); - let expected_exiration = + let expected_expiration = frame_system::Pallet::::block_number() + T::PendingUsernameExpiration::get(); - run_to_block::(expected_exiration + One::one()); + run_to_block::(expected_expiration + One::one()); #[extrinsic_call] _(RawOrigin::Signed(caller.clone()), username); diff --git a/substrate/frame/identity/src/lib.rs b/substrate/frame/identity/src/lib.rs index 78d59180b3f2f8ca684dc52d9e218de8914575b8..4a977880b3150cf9ffa7e2b4032e258483a6d242 100644 --- a/substrate/frame/identity/src/lib.rs +++ b/substrate/frame/identity/src/lib.rs @@ -161,7 +161,7 @@ pub mod pallet { /// Structure holding information about an identity. type IdentityInformation: IdentityInformationProvider; - /// Maxmimum number of registrars allowed in the system. Needed to bound the complexity + /// Maximum number of registrars allowed in the system. Needed to bound the complexity /// of, e.g., updating judgements. #[pallet::constant] type MaxRegistrars: Get; diff --git a/substrate/frame/identity/src/tests.rs b/substrate/frame/identity/src/tests.rs index 60866f12baa61783aa27333f66a07193059ce727..0a9464256ce3fbf7bf84d0a463b9c00ad87bef29 100644 --- a/substrate/frame/identity/src/tests.rs +++ b/substrate/frame/identity/src/tests.rs @@ -53,7 +53,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type AccountId = AccountId; type Lookup = IdentityLookup; diff --git a/substrate/frame/im-online/src/lib.rs b/substrate/frame/im-online/src/lib.rs index 1de89dd00c8121c64a19f8b45f24fc6500126dce..239b47834d1f8b1625a186e4a8f90e9861bfa303 100644 --- a/substrate/frame/im-online/src/lib.rs +++ b/substrate/frame/im-online/src/lib.rs @@ -251,7 +251,7 @@ type OffchainResult = Result>>; pub mod pallet { use super::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] @@ -338,26 +338,22 @@ pub mod pallet { /// progress estimate from `NextSessionRotation`, as those estimates should be /// more accurate then the value we calculate for `HeartbeatAfter`. #[pallet::storage] - #[pallet::getter(fn heartbeat_after)] - pub(super) type HeartbeatAfter = StorageValue<_, BlockNumberFor, ValueQuery>; + pub type HeartbeatAfter = StorageValue<_, BlockNumberFor, ValueQuery>; /// The current set of keys that may issue a heartbeat. #[pallet::storage] - #[pallet::getter(fn keys)] - pub(super) type Keys = + pub type Keys = StorageValue<_, WeakBoundedVec, ValueQuery>; /// For each session index, we keep a mapping of `SessionIndex` and `AuthIndex`. #[pallet::storage] - #[pallet::getter(fn received_heartbeats)] - pub(super) type ReceivedHeartbeats = + pub type ReceivedHeartbeats = StorageDoubleMap<_, Twox64Concat, SessionIndex, Twox64Concat, AuthIndex, bool>; /// For each session index, we keep a mapping of `ValidatorId` to the /// number of blocks authored by the given authority. #[pallet::storage] - #[pallet::getter(fn authored_blocks)] - pub(super) type AuthoredBlocks = StorageDoubleMap< + pub type AuthoredBlocks = StorageDoubleMap< _, Twox64Concat, SessionIndex, diff --git a/substrate/frame/im-online/src/migration.rs b/substrate/frame/im-online/src/migration.rs index 0d2c0a055b6d88855bc8783b65e06f0427197208..754a2e672e6cfaec32ed0e44c9af54469fddbb04 100644 --- a/substrate/frame/im-online/src/migration.rs +++ b/substrate/frame/im-online/src/migration.rs @@ -72,7 +72,7 @@ pub mod v1 { if StorageVersion::get::>() != 0 { log::warn!( target: TARGET, - "Skipping migration because current storage version is not 0" + "Skipping migration because in-code storage version is not 0" ); return weight } diff --git a/substrate/frame/im-online/src/mock.rs b/substrate/frame/im-online/src/mock.rs index 9dad148b10fa1b0942e93f1c5d183264ab282f05..cc448dc1ae10bcc80a4389348b5969042bc02caf 100644 --- a/substrate/frame/im-online/src/mock.rs +++ b/substrate/frame/im-online/src/mock.rs @@ -112,7 +112,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { result } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/frame/im-online/src/tests.rs b/substrate/frame/im-online/src/tests.rs index 79036760c2d427c2895163b150f3806bf17222f2..f9959593494a0b94c4093f4c06f0e45c1e3e4f59 100644 --- a/substrate/frame/im-online/src/tests.rs +++ b/substrate/frame/im-online/src/tests.rs @@ -26,10 +26,7 @@ use sp_core::offchain::{ testing::{TestOffchainExt, TestTransactionPoolExt}, OffchainDbExt, OffchainWorkerExt, TransactionPoolExt, }; -use sp_runtime::{ - testing::UintAuthorityId, - transaction_validity::{InvalidTransaction, TransactionValidityError}, -}; +use sp_runtime::testing::UintAuthorityId; #[test] fn test_unresponsiveness_slash_fraction() { @@ -267,14 +264,14 @@ fn should_cleanup_received_heartbeats_on_session_end() { let _ = heartbeat(1, 2, 0, 1.into(), Session::validators()).unwrap(); // the heartbeat is stored - assert!(!ImOnline::received_heartbeats(&2, &0).is_none()); + assert!(!super::pallet::ReceivedHeartbeats::::get(2, 0).is_none()); advance_session(); // after the session has ended we have already processed the heartbeat // message, so any messages received on the previous session should have // been pruned. - assert!(ImOnline::received_heartbeats(&2, &0).is_none()); + assert!(super::pallet::ReceivedHeartbeats::::get(2, 0).is_none()); }); } @@ -419,7 +416,7 @@ fn should_handle_non_linear_session_progress() { Session::rotate_session(); - // if we don't have valid results for the current session progres then + // if we don't have valid results for the current session progress then // we'll fallback to `HeartbeatAfter` and only heartbeat on block 5. MockCurrentSessionProgress::mutate(|p| *p = Some(None)); assert_eq!(ImOnline::send_heartbeats(2).err(), Some(OffchainErr::TooEarly)); diff --git a/substrate/frame/indices/src/mock.rs b/substrate/frame/indices/src/mock.rs index 5cf82305178d419d0542d283db914eb8dc481aa7..9f8bf8c3758832d203d088616b4cb5247a116805 100644 --- a/substrate/frame/indices/src/mock.rs +++ b/substrate/frame/indices/src/mock.rs @@ -38,7 +38,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/frame/insecure-randomness-collective-flip/src/lib.rs b/substrate/frame/insecure-randomness-collective-flip/src/lib.rs index 04f8cda6541dd37685b86b7a25f7b84d5880de45..bdb089a14200c679b4ad9d5c5404fffbf8d19a8e 100644 --- a/substrate/frame/insecure-randomness-collective-flip/src/lib.rs +++ b/substrate/frame/insecure-randomness-collective-flip/src/lib.rs @@ -186,7 +186,7 @@ mod tests { ::max(2 * 1024); } - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } diff --git a/substrate/frame/lottery/src/benchmarking.rs b/substrate/frame/lottery/src/benchmarking.rs index 1510d250dbeaf6814a35f1262f08ba727b340ee2..123b425b976f3b5b2842e748498513611dca5bc2 100644 --- a/substrate/frame/lottery/src/benchmarking.rs +++ b/substrate/frame/lottery/src/benchmarking.rs @@ -23,7 +23,6 @@ use super::*; use crate::Pallet as Lottery; use frame_benchmarking::{ - impl_benchmark_test_suite, v1::{account, whitelisted_caller, BenchmarkError}, v2::*, }; diff --git a/substrate/frame/lottery/src/mock.rs b/substrate/frame/lottery/src/mock.rs index 563ce7202ec39c5b16af1efc58c0cde077400db7..596e1a9d837d16486e8a2017b0ab679a02bc2dd6 100644 --- a/substrate/frame/lottery/src/mock.rs +++ b/substrate/frame/lottery/src/mock.rs @@ -43,7 +43,7 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; diff --git a/substrate/frame/membership/src/lib.rs b/substrate/frame/membership/src/lib.rs index 19b5e54d308cb7a1546136ff1266de67ec6d80fa..426fc985a52f3fcd29c1a6802ee16ef4f34d9884 100644 --- a/substrate/frame/membership/src/lib.rs +++ b/substrate/frame/membership/src/lib.rs @@ -27,7 +27,7 @@ use frame_support::{ traits::{ChangeMembers, Contains, Get, InitializeMembers, SortedMembers}, BoundedVec, }; -use sp_runtime::traits::StaticLookup; +use sp_runtime::traits::{StaticLookup, UniqueSaturatedInto}; use sp_std::prelude::*; pub mod migrations; @@ -46,7 +46,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] @@ -163,12 +163,16 @@ pub mod pallet { /// /// May only be called from `T::AddOrigin`. #[pallet::call_index(0)] - #[pallet::weight({50_000_000})] - pub fn add_member(origin: OriginFor, who: AccountIdLookupOf) -> DispatchResult { + #[pallet::weight(T::WeightInfo::add_member(T::MaxMembers::get()))] + pub fn add_member( + origin: OriginFor, + who: AccountIdLookupOf, + ) -> DispatchResultWithPostInfo { T::AddOrigin::ensure_origin(origin)?; let who = T::Lookup::lookup(who)?; let mut members = >::get(); + let init_length = members.len(); let location = members.binary_search(&who).err().ok_or(Error::::AlreadyMember)?; members .try_insert(location, who.clone()) @@ -179,19 +183,24 @@ pub mod pallet { T::MembershipChanged::change_members_sorted(&[who], &[], &members[..]); Self::deposit_event(Event::MemberAdded); - Ok(()) + + Ok(Some(T::WeightInfo::add_member(init_length as u32)).into()) } /// Remove a member `who` from the set. /// /// May only be called from `T::RemoveOrigin`. #[pallet::call_index(1)] - #[pallet::weight({50_000_000})] - pub fn remove_member(origin: OriginFor, who: AccountIdLookupOf) -> DispatchResult { + #[pallet::weight(T::WeightInfo::remove_member(T::MaxMembers::get()))] + pub fn remove_member( + origin: OriginFor, + who: AccountIdLookupOf, + ) -> DispatchResultWithPostInfo { T::RemoveOrigin::ensure_origin(origin)?; let who = T::Lookup::lookup(who)?; let mut members = >::get(); + let init_length = members.len(); let location = members.binary_search(&who).ok().ok_or(Error::::NotMember)?; members.remove(location); @@ -201,7 +210,7 @@ pub mod pallet { Self::rejig_prime(&members); Self::deposit_event(Event::MemberRemoved); - Ok(()) + Ok(Some(T::WeightInfo::remove_member(init_length as u32)).into()) } /// Swap out one member `remove` for another `add`. @@ -210,18 +219,18 @@ pub mod pallet { /// /// Prime membership is *not* passed from `remove` to `add`, if extant. #[pallet::call_index(2)] - #[pallet::weight({50_000_000})] + #[pallet::weight(T::WeightInfo::swap_member(T::MaxMembers::get()))] pub fn swap_member( origin: OriginFor, remove: AccountIdLookupOf, add: AccountIdLookupOf, - ) -> DispatchResult { + ) -> DispatchResultWithPostInfo { T::SwapOrigin::ensure_origin(origin)?; let remove = T::Lookup::lookup(remove)?; let add = T::Lookup::lookup(add)?; if remove == add { - return Ok(()) + return Ok(().into()); } let mut members = >::get(); @@ -236,7 +245,7 @@ pub mod pallet { Self::rejig_prime(&members); Self::deposit_event(Event::MembersSwapped); - Ok(()) + Ok(Some(T::WeightInfo::swap_member(members.len() as u32)).into()) } /// Change the membership to a new set, disregarding the existing membership. Be nice and @@ -244,7 +253,7 @@ pub mod pallet { /// /// May only be called from `T::ResetOrigin`. #[pallet::call_index(3)] - #[pallet::weight({50_000_000})] + #[pallet::weight(T::WeightInfo::reset_members(members.len().unique_saturated_into()))] pub fn reset_members(origin: OriginFor, members: Vec) -> DispatchResult { T::ResetOrigin::ensure_origin(origin)?; @@ -267,56 +276,65 @@ pub mod pallet { /// /// Prime membership is passed from the origin account to `new`, if extant. #[pallet::call_index(4)] - #[pallet::weight({50_000_000})] - pub fn change_key(origin: OriginFor, new: AccountIdLookupOf) -> DispatchResult { + #[pallet::weight(T::WeightInfo::change_key(T::MaxMembers::get()))] + pub fn change_key( + origin: OriginFor, + new: AccountIdLookupOf, + ) -> DispatchResultWithPostInfo { let remove = ensure_signed(origin)?; let new = T::Lookup::lookup(new)?; - if remove != new { - let mut members = >::get(); - let location = - members.binary_search(&remove).ok().ok_or(Error::::NotMember)?; - let _ = members.binary_search(&new).err().ok_or(Error::::AlreadyMember)?; - members[location] = new.clone(); - members.sort(); - - >::put(&members); - - T::MembershipChanged::change_members_sorted( - &[new.clone()], - &[remove.clone()], - &members[..], - ); - - if Prime::::get() == Some(remove) { - Prime::::put(&new); - T::MembershipChanged::set_prime(Some(new)); - } + if remove == new { + return Ok(().into()); + } + + let mut members = >::get(); + let members_length = members.len() as u32; + let location = members.binary_search(&remove).ok().ok_or(Error::::NotMember)?; + let _ = members.binary_search(&new).err().ok_or(Error::::AlreadyMember)?; + members[location] = new.clone(); + members.sort(); + + >::put(&members); + + T::MembershipChanged::change_members_sorted( + &[new.clone()], + &[remove.clone()], + &members[..], + ); + + if Prime::::get() == Some(remove) { + Prime::::put(&new); + T::MembershipChanged::set_prime(Some(new)); } Self::deposit_event(Event::KeyChanged); - Ok(()) + Ok(Some(T::WeightInfo::change_key(members_length)).into()) } /// Set the prime member. Must be a current member. /// /// May only be called from `T::PrimeOrigin`. #[pallet::call_index(5)] - #[pallet::weight({50_000_000})] - pub fn set_prime(origin: OriginFor, who: AccountIdLookupOf) -> DispatchResult { + #[pallet::weight(T::WeightInfo::set_prime(T::MaxMembers::get()))] + pub fn set_prime( + origin: OriginFor, + who: AccountIdLookupOf, + ) -> DispatchResultWithPostInfo { T::PrimeOrigin::ensure_origin(origin)?; let who = T::Lookup::lookup(who)?; - Self::members().binary_search(&who).ok().ok_or(Error::::NotMember)?; + let members = Self::members(); + members.binary_search(&who).ok().ok_or(Error::::NotMember)?; Prime::::put(&who); T::MembershipChanged::set_prime(Some(who)); - Ok(()) + Ok(Some(T::WeightInfo::set_prime(members.len() as u32)).into()) } /// Remove the prime member if it exists. /// /// May only be called from `T::PrimeOrigin`. #[pallet::call_index(6)] - #[pallet::weight({50_000_000})] + #[pallet::weight(T::WeightInfo::clear_prime())] pub fn clear_prime(origin: OriginFor) -> DispatchResult { T::PrimeOrigin::ensure_origin(origin)?; Prime::::kill(); @@ -442,7 +460,7 @@ mod benchmark { } // er keep the prime common between incoming and outgoing to make sure it is rejigged. - reset_member { + reset_members { let m in 1 .. T::MaxMembers::get(); let members = (1..m+1).map(|i| account("member", i, SEED)).collect::>(); @@ -500,8 +518,7 @@ mod benchmark { } clear_prime { - let m in 1 .. T::MaxMembers::get(); - let members = (0..m).map(|i| account("member", i, SEED)).collect::>(); + let members = (0..T::MaxMembers::get()).map(|i| account("member", i, SEED)).collect::>(); let prime = members.last().cloned().unwrap(); set_members::(members, None); }: { @@ -526,7 +543,8 @@ mod tests { use sp_runtime::{bounded_vec, traits::BadOrigin, BuildStorage}; use frame_support::{ - assert_noop, assert_ok, derive_impl, ord_parameter_types, parameter_types, + assert_noop, assert_ok, assert_storage_noop, derive_impl, ord_parameter_types, + parameter_types, traits::{ConstU32, StorageVersion}, }; use frame_system::EnsureSignedBy; @@ -546,7 +564,7 @@ mod tests { pub static Prime: Option = None; } - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } @@ -716,6 +734,17 @@ mod tests { }); } + #[test] + fn swap_member_with_identical_arguments_changes_nothing() { + new_test_ext().execute_with(|| { + assert_storage_noop!(assert_ok!(Membership::swap_member( + RuntimeOrigin::signed(3), + 10, + 10 + ))); + }); + } + #[test] fn change_key_works() { new_test_ext().execute_with(|| { @@ -745,6 +774,13 @@ mod tests { }); } + #[test] + fn change_key_with_same_caller_as_argument_changes_nothing() { + new_test_ext().execute_with(|| { + assert_storage_noop!(assert_ok!(Membership::change_key(RuntimeOrigin::signed(10), 10))); + }); + } + #[test] fn reset_members_works() { new_test_ext().execute_with(|| { diff --git a/substrate/frame/membership/src/weights.rs b/substrate/frame/membership/src/weights.rs index 18ea7fcb315a3134747743f1a913856d084e5ad6..2d18848b89ab5b55c22235282fb78b514f7ed937 100644 --- a/substrate/frame/membership/src/weights.rs +++ b/substrate/frame/membership/src/weights.rs @@ -55,10 +55,10 @@ pub trait WeightInfo { fn add_member(m: u32, ) -> Weight; fn remove_member(m: u32, ) -> Weight; fn swap_member(m: u32, ) -> Weight; - fn reset_member(m: u32, ) -> Weight; + fn reset_members(m: u32, ) -> Weight; fn change_key(m: u32, ) -> Weight; fn set_prime(m: u32, ) -> Weight; - fn clear_prime(m: u32, ) -> Weight; + fn clear_prime() -> Weight; } /// Weights for pallet_membership using the Substrate node and recommended hardware. @@ -142,7 +142,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: TechnicalCommittee Prime (r:0 w:1) /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[1, 100]`. - fn reset_member(m: u32, ) -> Weight { + fn reset_members(m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `312 + m * (64 ±0)` // Estimated: `4687 + m * (64 ±0)` @@ -200,15 +200,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) /// Storage: TechnicalCommittee Prime (r:0 w:1) /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `m` is `[1, 100]`. - fn clear_prime(m: u32, ) -> Weight { + fn clear_prime() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 3_373_000 picoseconds. Weight::from_parts(3_750_452, 0) - // Standard Error: 142 - .saturating_add(Weight::from_parts(505, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().writes(2_u64)) } } @@ -293,7 +290,7 @@ impl WeightInfo for () { /// Storage: TechnicalCommittee Prime (r:0 w:1) /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) /// The range of component `m` is `[1, 100]`. - fn reset_member(m: u32, ) -> Weight { + fn reset_members(m: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `312 + m * (64 ±0)` // Estimated: `4687 + m * (64 ±0)` @@ -351,15 +348,12 @@ impl WeightInfo for () { /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) /// Storage: TechnicalCommittee Prime (r:0 w:1) /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `m` is `[1, 100]`. - fn clear_prime(m: u32, ) -> Weight { + fn clear_prime() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 3_373_000 picoseconds. Weight::from_parts(3_750_452, 0) - // Standard Error: 142 - .saturating_add(Weight::from_parts(505, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/substrate/frame/merkle-mountain-range/src/lib.rs b/substrate/frame/merkle-mountain-range/src/lib.rs index 664f4bc73901b39f852e9fccf5685b48e402ec4b..7b6edb37b7f7bc8608a417aa9eff6f0713edf9df 100644 --- a/substrate/frame/merkle-mountain-range/src/lib.rs +++ b/substrate/frame/merkle-mountain-range/src/lib.rs @@ -89,7 +89,7 @@ mod tests; /// is not available (since the block is not finished yet), /// we use the `parent_hash` here along with parent block number. pub struct ParentNumberAndHash { - _phanthom: sp_std::marker::PhantomData, + _phantom: sp_std::marker::PhantomData, } impl LeafDataProvider for ParentNumberAndHash { @@ -183,7 +183,6 @@ pub mod pallet { /// Latest MMR Root hash. #[pallet::storage] - #[pallet::getter(fn mmr_root_hash)] pub type RootHash, I: 'static = ()> = StorageValue<_, HashOf, ValueQuery>; /// Current size of the MMR (number of leaves). @@ -204,7 +203,7 @@ pub mod pallet { impl, I: 'static> Hooks> for Pallet { fn on_initialize(_n: BlockNumberFor) -> Weight { use primitives::LeafDataProvider; - let leaves = Self::mmr_leaves(); + let leaves = NumberOfLeaves::::get(); let peaks_before = sp_mmr_primitives::utils::NodesUtils::new(leaves).number_of_peaks(); let data = T::LeafData::leaf_data(); @@ -225,8 +224,8 @@ pub mod pallet { }; >::on_new_root(&root); - >::put(leaves); - >::put(root); + NumberOfLeaves::::put(leaves); + RootHash::::put(root); let peaks_after = sp_mmr_primitives::utils::NodesUtils::new(leaves).number_of_peaks(); @@ -301,7 +300,7 @@ impl, I: 'static> Pallet { { let first_mmr_block = utils::first_mmr_block_num::>( >::block_number(), - Self::mmr_leaves(), + NumberOfLeaves::::get(), )?; utils::block_num_to_leaf_index::>(block_num, first_mmr_block) @@ -341,7 +340,7 @@ impl, I: 'static> Pallet { /// Return the on-chain MMR root hash. pub fn mmr_root() -> HashOf { - Self::mmr_root_hash() + RootHash::::get() } /// Verify MMR proof for given `leaves`. @@ -354,7 +353,7 @@ impl, I: 'static> Pallet { leaves: Vec>, proof: primitives::Proof>, ) -> Result<(), primitives::Error> { - if proof.leaf_count > Self::mmr_leaves() || + if proof.leaf_count > NumberOfLeaves::::get() || proof.leaf_count == 0 || (proof.items.len().saturating_add(leaves.len())) as u64 > proof.leaf_count { diff --git a/substrate/frame/merkle-mountain-range/src/mmr/storage.rs b/substrate/frame/merkle-mountain-range/src/mmr/storage.rs index 03039be83ac1a13383313f7bc09c175705e6d02a..96a20c3445eedea0bfa3d02e99ed07022d30da80 100644 --- a/substrate/frame/merkle-mountain-range/src/mmr/storage.rs +++ b/substrate/frame/merkle-mountain-range/src/mmr/storage.rs @@ -111,7 +111,7 @@ where L: primitives::FullLeaf, { fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result>> { - Ok(>::get(pos).map(Node::Hash)) + Ok(Nodes::::get(pos).map(Node::Hash)) } fn append(&mut self, pos: NodeIndex, elems: Vec>) -> mmr_lib::Result<()> { @@ -147,7 +147,7 @@ where for elem in elems { // On-chain we are going to only store new peaks. if peaks_to_store.next_if_eq(&node_index).is_some() { - >::insert(node_index, elem.hash()); + Nodes::::insert(node_index, elem.hash()); } // We are storing full node off-chain (using indexing API). Self::store_to_offchain(node_index, parent_hash, &elem); @@ -164,7 +164,7 @@ where // And remove all remaining items from `peaks_before` collection. for pos in peaks_to_prune { - >::remove(pos); + Nodes::::remove(pos); } Ok(()) diff --git a/substrate/frame/merkle-mountain-range/src/mock.rs b/substrate/frame/merkle-mountain-range/src/mock.rs index b3b5127db024ffd9687d14d20d702ccc86647815..212012a052a027cd0582b90e3a29a409980c4e66 100644 --- a/substrate/frame/merkle-mountain-range/src/mock.rs +++ b/substrate/frame/merkle-mountain-range/src/mock.rs @@ -33,7 +33,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } diff --git a/substrate/frame/merkle-mountain-range/src/tests.rs b/substrate/frame/merkle-mountain-range/src/tests.rs index 429df75182eeeefb3411d5eb6aeb8c9b62968186..88de7511c9f280b6996419f07ac1f5c458d8a395 100644 --- a/substrate/frame/merkle-mountain-range/src/tests.rs +++ b/substrate/frame/merkle-mountain-range/src/tests.rs @@ -608,9 +608,9 @@ fn verification_should_be_stateless() { let mut ext = new_test_ext(); let (root_6, root_7) = ext.execute_with(|| { add_blocks(6); - let root_6 = crate::Pallet::::mmr_root_hash(); + let root_6 = crate::Pallet::::mmr_root(); add_blocks(1); - let root_7 = crate::Pallet::::mmr_root_hash(); + let root_7 = crate::Pallet::::mmr_root(); (root_6, root_7) }); ext.persist_offchain_overlay(); @@ -656,9 +656,9 @@ fn should_verify_batch_proof_statelessly() { let mut ext = new_test_ext(); let (root_6, root_7) = ext.execute_with(|| { add_blocks(6); - let root_6 = crate::Pallet::::mmr_root_hash(); + let root_6 = crate::Pallet::::mmr_root(); add_blocks(1); - let root_7 = crate::Pallet::::mmr_root_hash(); + let root_7 = crate::Pallet::::mmr_root(); (root_6, root_7) }); ext.persist_offchain_overlay(); diff --git a/substrate/frame/message-queue/src/integration_test.rs b/substrate/frame/message-queue/src/integration_test.rs index cc3da6ebdc669e5710867aee63252a4bf5ca6889..26a330cc88e8ed2a53c2d19b2d3b90272c7e7778 100644 --- a/substrate/frame/message-queue/src/integration_test.rs +++ b/substrate/frame/message-queue/src/integration_test.rs @@ -52,7 +52,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } @@ -330,6 +330,11 @@ fn process_some_messages(num_msgs: u32) { ServiceWeight::set(Some(weight)); let consumed = next_block(); + for origin in BookStateFor::::iter_keys() { + let fp = MessageQueue::footprint(origin); + assert_eq!(fp.pages, fp.ready_pages); + } + assert_eq!(consumed, weight, "\n{}", MessageQueue::debug_info()); assert_eq!(NumMessagesProcessed::take(), num_msgs as usize); } diff --git a/substrate/frame/message-queue/src/lib.rs b/substrate/frame/message-queue/src/lib.rs index 07eb0041985342522a113339769b98f834ef4a17..93cd760eeb96c74ef0f64fdde0705ec00835a45b 100644 --- a/substrate/frame/message-queue/src/lib.rs +++ b/substrate/frame/message-queue/src/lib.rs @@ -208,8 +208,9 @@ use frame_support::{ defensive, pallet_prelude::*, traits::{ - Defensive, DefensiveTruncateFrom, EnqueueMessage, ExecuteOverweightError, Footprint, - ProcessMessage, ProcessMessageError, QueueFootprint, QueuePausedQuery, ServiceQueues, + Defensive, DefensiveSaturating, DefensiveTruncateFrom, EnqueueMessage, + ExecuteOverweightError, Footprint, ProcessMessage, ProcessMessageError, QueueFootprint, + QueuePausedQuery, ServiceQueues, }, BoundedSlice, CloneNoBound, DefaultNoBound, }; @@ -442,6 +443,7 @@ impl From> for QueueFootprint { fn from(book: BookState) -> Self { QueueFootprint { pages: book.count, + ready_pages: book.end.defensive_saturating_sub(book.begin), storage: Footprint { count: book.message_count, size: book.size }, } } @@ -1281,6 +1283,9 @@ impl Pallet { ensure!(book.message_count < 1 << 30, "Likely overflow or corruption"); ensure!(book.size < 1 << 30, "Likely overflow or corruption"); ensure!(book.count < 1 << 30, "Likely overflow or corruption"); + + let fp: QueueFootprint = book.into(); + ensure!(fp.ready_pages <= fp.pages, "There cannot be more ready than total pages"); } //loop around this origin @@ -1455,7 +1460,7 @@ impl Pallet { /// Run a closure that errors on re-entrance. Meant to be used by anything that services queues. pub(crate) fn with_service_mutex R, R>(f: F) -> Result { - // Holds the singelton token instance. + // Holds the singleton token instance. environmental::environmental!(token: Option<()>); token::using_once(&mut Some(()), || { diff --git a/substrate/frame/message-queue/src/mock.rs b/substrate/frame/message-queue/src/mock.rs index a46fa31df3e20d302650e68a26d8bcb96e5496a2..f22f318b8ef12511e840f21d874b0d3c2fa31519 100644 --- a/substrate/frame/message-queue/src/mock.rs +++ b/substrate/frame/message-queue/src/mock.rs @@ -37,7 +37,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } @@ -355,8 +355,8 @@ pub fn num_overweight_enqueued_events() -> u32 { .count() as u32 } -pub fn fp(pages: u32, count: u64, size: u64) -> QueueFootprint { - QueueFootprint { storage: Footprint { count, size }, pages } +pub fn fp(pages: u32, ready_pages: u32, count: u64, size: u64) -> QueueFootprint { + QueueFootprint { storage: Footprint { count, size }, pages, ready_pages } } /// A random seed that can be overwritten with `MQ_SEED`. diff --git a/substrate/frame/message-queue/src/tests.rs b/substrate/frame/message-queue/src/tests.rs index 86a8b79fe8bd01fc5f906ef42e22339c669e57b5..1f6e7777f0125a949cd5c1cec545337d7d716140 100644 --- a/substrate/frame/message-queue/src/tests.rs +++ b/substrate/frame/message-queue/src/tests.rs @@ -90,7 +90,7 @@ fn queue_priority_retains() { MessageQueue::enqueue_message(msg("d"), Everywhere(2)); assert_ring(&[Everywhere(1), Everywhere(2), Everywhere(3)]); // service head is 1, it will process a, leaving service head at 2. it also processes b but - // doees not empty queue 2, so service head will end at 2. + // does not empty queue 2, so service head will end at 2. assert_eq!(MessageQueue::service_queues(2.into_weight()), 2.into_weight()); assert_eq!( MessagesProcessed::take(), @@ -1064,13 +1064,13 @@ fn footprint_num_pages_works() { MessageQueue::enqueue_message(msg("weight=2"), Here); MessageQueue::enqueue_message(msg("weight=3"), Here); - assert_eq!(MessageQueue::footprint(Here), fp(2, 2, 16)); + assert_eq!(MessageQueue::footprint(Here), fp(2, 2, 2, 16)); // Mark the messages as overweight. assert_eq!(MessageQueue::service_queues(1.into_weight()), 0.into_weight()); assert_eq!(System::events().len(), 2); - // Overweight does not change the footprint. - assert_eq!(MessageQueue::footprint(Here), fp(2, 2, 16)); + // `ready_pages` decreases but `page` count does not. + assert_eq!(MessageQueue::footprint(Here), fp(2, 0, 2, 16)); // Now execute the second message. assert_eq!( @@ -1078,7 +1078,7 @@ fn footprint_num_pages_works() { .unwrap(), 3.into_weight() ); - assert_eq!(MessageQueue::footprint(Here), fp(1, 1, 8)); + assert_eq!(MessageQueue::footprint(Here), fp(1, 0, 1, 8)); // And the first one: assert_eq!( ::execute_overweight(2.into_weight(), (Here, 0, 0)) @@ -1086,6 +1086,11 @@ fn footprint_num_pages_works() { 2.into_weight() ); assert_eq!(MessageQueue::footprint(Here), Default::default()); + assert_eq!(MessageQueue::footprint(Here), fp(0, 0, 0, 0)); + + // `ready_pages` and normal `pages` increases again: + MessageQueue::enqueue_message(msg("weight=3"), Here); + assert_eq!(MessageQueue::footprint(Here), fp(1, 1, 1, 8)); }) } diff --git a/substrate/frame/migrations/Cargo.toml b/substrate/frame/migrations/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..40059fb9ec4356b819cb8cbeda1889e1bdd29e25 --- /dev/null +++ b/substrate/frame/migrations/Cargo.toml @@ -0,0 +1,64 @@ +[package] +name = "pallet-migrations" +version = "1.0.0" +description = "FRAME pallet to execute multi-block migrations." +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true + +[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"] } +docify = "0.1.14" +impl-trait-for-tuples = "0.2.2" +log = "0.4.21" +scale-info = { version = "2.0.0", default-features = false, features = ["derive"] } + +frame-benchmarking = { default-features = false, optional = true, path = "../benchmarking" } +frame-support = { default-features = false, path = "../support" } +frame-system = { default-features = false, path = "../system" } +sp-core = { path = "../../primitives/core", default-features = false } +sp-std = { path = "../../primitives/std", default-features = false } +sp-runtime = { path = "../../primitives/runtime", default-features = false } + +[dev-dependencies] +frame-executive = { path = "../executive" } +sp-api = { path = "../../primitives/api", features = ["std"] } +sp-block-builder = { path = "../../primitives/block-builder", features = ["std"] } +sp-io = { path = "../../primitives/io", features = ["std"] } +sp-tracing = { path = "../../primitives/tracing", features = ["std"] } +sp-version = { path = "../../primitives/version", features = ["std"] } + +pretty_assertions = "1.3.0" + +[features] +default = ["std"] + +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] + +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] + +try-runtime = [ + "frame-executive/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/migrations/src/benchmarking.rs b/substrate/frame/migrations/src/benchmarking.rs new file mode 100644 index 0000000000000000000000000000000000000000..8ad1fa50d14985842051c9ac6fb3f95e30bdb030 --- /dev/null +++ b/substrate/frame/migrations/src/benchmarking.rs @@ -0,0 +1,222 @@ +// 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. + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; + +use frame_benchmarking::{v2::*, BenchmarkError}; +use frame_system::{Pallet as System, RawOrigin}; +use sp_runtime::traits::One; + +fn assert_last_event(generic_event: ::RuntimeEvent) { + frame_system::Pallet::::assert_last_event(generic_event.into()); +} + +#[benchmarks] +mod benches { + use super::*; + use frame_support::traits::Hooks; + + #[benchmark] + fn onboard_new_mbms() { + T::Migrations::set_fail_after(0); // Should not be called anyway. + assert!(!Cursor::::exists()); + + #[block] + { + Pallet::::onboard_new_mbms(); + } + + assert_last_event::(Event::UpgradeStarted { migrations: 1 }.into()); + } + + #[benchmark] + fn progress_mbms_none() { + T::Migrations::set_fail_after(0); // Should not be called anyway. + assert!(!Cursor::::exists()); + + #[block] + { + Pallet::::progress_mbms(One::one()); + } + } + + /// All migrations completed. + #[benchmark] + fn exec_migration_completed() -> Result<(), BenchmarkError> { + T::Migrations::set_fail_after(0); // Should not be called anyway. + assert_eq!(T::Migrations::len(), 1, "Setup failed"); + let c = ActiveCursor { index: 1, inner_cursor: None, started_at: 0u32.into() }; + let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get()); + System::::set_block_number(1u32.into()); + + #[block] + { + Pallet::::exec_migration(c, false, &mut meter); + } + + assert_last_event::(Event::UpgradeCompleted {}.into()); + + Ok(()) + } + + /// No migration runs since it is skipped as historic. + #[benchmark] + fn exec_migration_skipped_historic() -> Result<(), BenchmarkError> { + T::Migrations::set_fail_after(0); // Should not be called anyway. + assert_eq!(T::Migrations::len(), 1, "Setup failed"); + let c = ActiveCursor { index: 0, inner_cursor: None, started_at: 0u32.into() }; + + let id: IdentifierOf = T::Migrations::nth_id(0).unwrap().try_into().unwrap(); + Historic::::insert(id, ()); + + let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get()); + System::::set_block_number(1u32.into()); + + #[block] + { + Pallet::::exec_migration(c, false, &mut meter); + } + + assert_last_event::(Event::MigrationSkipped { index: 0 }.into()); + + Ok(()) + } + + /// Advance a migration by one step. + #[benchmark] + fn exec_migration_advance() -> Result<(), BenchmarkError> { + T::Migrations::set_success_after(1); + assert_eq!(T::Migrations::len(), 1, "Setup failed"); + let c = ActiveCursor { index: 0, inner_cursor: None, started_at: 0u32.into() }; + let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get()); + System::::set_block_number(1u32.into()); + + #[block] + { + Pallet::::exec_migration(c, false, &mut meter); + } + + assert_last_event::(Event::MigrationAdvanced { index: 0, took: One::one() }.into()); + + Ok(()) + } + + /// Successfully complete a migration. + #[benchmark] + fn exec_migration_complete() -> Result<(), BenchmarkError> { + T::Migrations::set_success_after(0); + assert_eq!(T::Migrations::len(), 1, "Setup failed"); + let c = ActiveCursor { index: 0, inner_cursor: None, started_at: 0u32.into() }; + let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get()); + System::::set_block_number(1u32.into()); + + #[block] + { + Pallet::::exec_migration(c, false, &mut meter); + } + + assert_last_event::(Event::MigrationCompleted { index: 0, took: One::one() }.into()); + + Ok(()) + } + + #[benchmark] + fn exec_migration_fail() -> Result<(), BenchmarkError> { + T::Migrations::set_fail_after(0); + assert_eq!(T::Migrations::len(), 1, "Setup failed"); + let c = ActiveCursor { index: 0, inner_cursor: None, started_at: 0u32.into() }; + let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get()); + System::::set_block_number(1u32.into()); + + #[block] + { + Pallet::::exec_migration(c, false, &mut meter); + } + + assert_last_event::(Event::UpgradeFailed {}.into()); + + Ok(()) + } + + #[benchmark] + fn on_init_loop() { + T::Migrations::set_fail_after(0); // Should not be called anyway. + System::::set_block_number(1u32.into()); + Pallet::::on_runtime_upgrade(); + + #[block] + { + Pallet::::on_initialize(1u32.into()); + } + } + + #[benchmark] + fn force_set_cursor() { + #[extrinsic_call] + _(RawOrigin::Root, Some(cursor::())); + } + + #[benchmark] + fn force_set_active_cursor() { + #[extrinsic_call] + _(RawOrigin::Root, 0, None, None); + } + + #[benchmark] + fn force_onboard_mbms() { + #[extrinsic_call] + _(RawOrigin::Root); + } + + #[benchmark] + fn clear_historic(n: Linear<0, { DEFAULT_HISTORIC_BATCH_CLEAR_SIZE * 2 }>) { + let id_max_len = ::IdentifierMaxLen::get(); + assert!(id_max_len >= 4, "Precondition violated"); + + for i in 0..DEFAULT_HISTORIC_BATCH_CLEAR_SIZE * 2 { + let id = IdentifierOf::::truncate_from( + i.encode().into_iter().cycle().take(id_max_len as usize).collect::>(), + ); + + Historic::::insert(&id, ()); + } + + #[extrinsic_call] + _( + RawOrigin::Root, + HistoricCleanupSelector::Wildcard { limit: n.into(), previous_cursor: None }, + ); + } + + fn cursor() -> CursorOf { + // Note: The weight of a function can depend on the weight of reading the `inner_cursor`. + // `Cursor` is a user provided type. Now instead of requiring something like `Cursor: + // From`, we instead rely on the fact that it is MEL and the PoV benchmarking will + // therefore already take the MEL bound, even when the cursor in storage is `None`. + MigrationCursor::Active(ActiveCursor { + index: u32::MAX, + inner_cursor: None, + started_at: 0u32.into(), + }) + } + + // Implements a test for each benchmark. Execute with: + // `cargo test -p pallet-migrations --features runtime-benchmarks`. + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/substrate/frame/migrations/src/lib.rs b/substrate/frame/migrations/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..cd57d89f440f79d39f98a3da81ed57b6cb5bfcf5 --- /dev/null +++ b/substrate/frame/migrations/src/lib.rs @@ -0,0 +1,746 @@ +// 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. + +#![deny(missing_docs)] +#![deny(rustdoc::broken_intra_doc_links)] + +//! # `pallet-migrations` +//! +//! Provides multi block migrations for FRAME runtimes. +//! +//! ## Overview +//! +//! The pallet takes care of executing a batch of multi-step migrations over multiple blocks. The +//! process starts on each runtime upgrade. Normal and operational transactions are paused while +//! migrations are on-going. +//! +//! ### Example +//! +//! This example demonstrates a simple mocked walk through of a basic success scenario. The pallet +//! is configured with two migrations: one succeeding after just one step, and the second one +//! succeeding after two steps. A runtime upgrade is then enacted and the block number is advanced +//! until all migrations finish executing. Afterwards, the recorded historic migrations are +//! checked and events are asserted. +#![doc = docify::embed!("substrate/frame/migrations/src/tests.rs", simple_works)] +//! +//! ## Pallet API +//! +//! See the [`pallet`] module for more information about the interfaces this pallet exposes, +//! including its configuration trait, dispatchables, storage items, events and errors. +//! +//! Otherwise noteworthy API of this pallet include its implementation of the +//! [`MultiStepMigrator`] trait. This must be plugged into +//! [`frame_system::Config::MultiBlockMigrator`] for proper function. +//! +//! The API contains some calls for emergency management. They are all prefixed with `force_` and +//! should normally not be needed. Pay special attention prior to using them. +//! +//! ### Design Goals +//! +//! 1. Must automatically execute migrations over multiple blocks. +//! 2. Must expose information about whether migrations are ongoing. +//! 3. Must respect pessimistic weight bounds of migrations. +//! 4. Must execute migrations in order. Skipping is not allowed; migrations are run on a +//! all-or-nothing basis. +//! 5. Must prevent re-execution of past migrations. +//! 6. Must provide transactional storage semantics for migrations. +//! 7. Must guarantee progress. +//! +//! ### Design +//! +//! Migrations are provided to the pallet through the associated type [`Config::Migrations`] of type +//! [`SteppedMigrations`]. This allows multiple migrations to be aggregated through a tuple. It +//! simplifies the trait bounds since all associated types of the trait must be provided by the +//! pallet. The actual progress of the pallet is stored in the [`Cursor`] storage item. This can +//! either be [`MigrationCursor::Active`] or [`MigrationCursor::Stuck`]. In the active case it +//! points to the currently active migration and stores its inner cursor. The inner cursor can then +//! be used by the migration to store its inner state and advance. Each time when the migration +//! returns `Some(cursor)`, it signals the pallet that it is not done yet. +//! The cursor is reset on each runtime upgrade. This ensures that it starts to execute at the +//! first migration in the vector. The pallets cursor is only ever incremented or set to `Stuck` +//! once it encounters an error (Goal 4). Once in the stuck state, the pallet will stay stuck until +//! it is fixed through manual governance intervention. +//! As soon as the cursor of the pallet becomes `Some(_)`; [`MultiStepMigrator::ongoing`] returns +//! `true` (Goal 2). This can be used by upstream code to possibly pause transactions. +//! In `on_initialize` the pallet will load the current migration and check whether it was already +//! executed in the past by checking for membership of its ID in the [`Historic`] set. Historic +//! migrations are skipped without causing an error. Each successfully executed migration is added +//! to this set (Goal 5). +//! This proceeds until no more migrations remain. At that point, the event `UpgradeCompleted` is +//! emitted (Goal 1). +//! The execution of each migration happens by calling [`SteppedMigration::transactional_step`]. +//! This function wraps the inner `step` function into a transactional layer to allow rollback in +//! the error case (Goal 6). +//! Weight limits must be checked by the migration itself. The pallet provides a [`WeightMeter`] for +//! that purpose. The pallet may return [`SteppedMigrationError::InsufficientWeight`] at any point. +//! In that scenario, one of two things will happen: if that migration was exclusively executed +//! in this block, and therefore required more than the maximum amount of weight possible, the +//! process becomes `Stuck`. Otherwise, one re-attempt is executed with the same logic in the next +//! block (Goal 3). Progress through the migrations is guaranteed by providing a timeout for each +//! migration via [`SteppedMigration::max_steps`]. The pallet **ONLY** guarantees progress if this +//! is set to sensible limits (Goal 7). +//! +//! ### Scenario: Governance cleanup +//! +//! Every now and then, governance can make use of the [`clear_historic`][Pallet::clear_historic] +//! call. This ensures that no old migrations pile up in the [`Historic`] set. This can be done very +//! rarely, since the storage should not grow quickly and the lookup weight does not suffer much. +//! Another possibility would be to have a synchronous single-block migration perpetually deployed +//! that cleans them up before the MBMs start. +//! +//! ### Scenario: Successful upgrade +//! +//! The standard procedure for a successful runtime upgrade can look like this: +//! 1. Migrations are configured in the `Migrations` config item. All migrations expose +//! [`max_steps`][SteppedMigration::max_steps], are error tolerant, check their weight bounds and +//! have a unique identifier. +//! 2. The runtime upgrade is enacted. An `UpgradeStarted` event is +//! followed by lots of `MigrationAdvanced` and `MigrationCompleted` events. Finally +//! `UpgradeCompleted` is emitted. +//! 3. Cleanup as described in the governance scenario be executed at any time after the migrations +//! completed. +//! +//! ### Advice: Failed upgrades +//! +//! Failed upgrades cannot be recovered from automatically and require governance intervention. Set +//! up monitoring for `UpgradeFailed` events to be made aware of any failures. The hook +//! [`FailedMigrationHandler::failed`] should be setup in a way that it allows governance to act, +//! but still prevent other transactions from interacting with the inconsistent storage state. Note +//! that this is paramount, since the inconsistent state might contain a faulty balance amount or +//! similar that could cause great harm if user transactions don't remain suspended. One way to +//! implement this would be to use the `SafeMode` or `TxPause` pallets that can prevent most user +//! interactions but still allow a whitelisted set of governance calls. +//! +//! ### Remark: Failed migrations +//! +//! Failed migrations are not added to the `Historic` set. This means that an erroneous +//! migration must be removed and fixed manually. This already applies, even before considering the +//! historic set. +//! +//! ### Remark: Transactional processing +//! +//! You can see the transactional semantics for migration steps as mostly useless, since in the +//! stuck case the state is already messed up. This just prevents it from becoming even more messed +//! up, but doesn't prevent it in the first place. + +#![cfg_attr(not(feature = "std"), no_std)] + +mod benchmarking; +mod mock; +pub mod mock_helpers; +mod tests; +pub mod weights; + +pub use pallet::*; +pub use weights::WeightInfo; + +use codec::{Decode, Encode, MaxEncodedLen}; +use core::ops::ControlFlow; +use frame_support::{ + defensive, defensive_assert, + migrations::*, + traits::Get, + weights::{Weight, WeightMeter}, + BoundedVec, +}; +use frame_system::{pallet_prelude::BlockNumberFor, Pallet as System}; +use sp_runtime::Saturating; +use sp_std::vec::Vec; + +/// Points to the next migration to execute. +#[derive(Debug, Clone, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo, MaxEncodedLen)] +pub enum MigrationCursor { + /// Points to the currently active migration and its inner cursor. + Active(ActiveCursor), + + /// Migration got stuck and cannot proceed. This is bad. + Stuck, +} + +impl MigrationCursor { + /// Try to return self as an [`ActiveCursor`]. + pub fn as_active(&self) -> Option<&ActiveCursor> { + match self { + MigrationCursor::Active(active) => Some(active), + MigrationCursor::Stuck => None, + } + } +} + +impl From> + for MigrationCursor +{ + fn from(active: ActiveCursor) -> Self { + MigrationCursor::Active(active) + } +} + +/// Points to the currently active migration and its inner cursor. +#[derive(Debug, Clone, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo, MaxEncodedLen)] +pub struct ActiveCursor { + /// The index of the migration in the MBM tuple. + pub index: u32, + /// The cursor of the migration that is referenced by `index`. + pub inner_cursor: Option, + /// The block number that the migration started at. + /// + /// This is used to calculate how many blocks it took. + pub started_at: BlockNumber, +} + +impl ActiveCursor { + /// Advance the overarching cursor to the next migration. + pub(crate) fn goto_next_migration(&mut self, current_block: BlockNumber) { + self.index.saturating_inc(); + self.inner_cursor = None; + self.started_at = current_block; + } +} + +/// How to clear the records of historic migrations. +#[derive(Debug, Clone, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo)] +pub enum HistoricCleanupSelector { + /// Clear exactly these entries. + /// + /// This is the advised way of doing it. + Specific(Vec), + + /// Clear up to this many entries + Wildcard { + /// How many should be cleared in this call at most. + limit: Option, + /// The cursor that was emitted from any previous `HistoricCleared`. + /// + /// Does not need to be passed when clearing the first batch. + previous_cursor: Option>, + }, +} + +/// The default number of entries that should be cleared by a `HistoricCleanupSelector::Wildcard`. +/// +/// The caller can explicitly specify a higher amount. Benchmarks are run with twice this value. +const DEFAULT_HISTORIC_BATCH_CLEAR_SIZE: u32 = 128; + +impl HistoricCleanupSelector { + /// The maximal number of entries that this will remove. + /// + /// Needed for weight calculation. + pub fn limit(&self) -> u32 { + match self { + Self::Specific(ids) => ids.len() as u32, + Self::Wildcard { limit, .. } => limit.unwrap_or(DEFAULT_HISTORIC_BATCH_CLEAR_SIZE), + } + } +} + +/// Convenience alias for [`MigrationCursor`]. +pub type CursorOf = MigrationCursor, BlockNumberFor>; + +/// Convenience alias for the raw inner cursor of a migration. +pub type RawCursorOf = BoundedVec::CursorMaxLen>; + +/// Convenience alias for the identifier of a migration. +pub type IdentifierOf = BoundedVec::IdentifierMaxLen>; + +/// Convenience alias for [`ActiveCursor`]. +pub type ActiveCursorOf = ActiveCursor, BlockNumberFor>; + +/// Trait for a tuple of No-OP migrations with one element. +pub trait MockedMigrations: SteppedMigrations { + /// The migration should fail after `n` steps. + fn set_fail_after(n: u32); + /// The migration should succeed after `n` steps. + fn set_success_after(n: u32); +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type of the runtime. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// All the multi-block migrations to run. + /// + /// Should only be updated in a runtime-upgrade once all the old migrations have completed. + /// (Check that [`Cursor`] is `None`). + #[cfg(not(feature = "runtime-benchmarks"))] + type Migrations: SteppedMigrations; + + /// Mocked migrations for benchmarking only. + /// + /// Should be configured to [`crate::mock_helpers::MockedMigrations`] in benchmarks. + #[cfg(feature = "runtime-benchmarks")] + type Migrations: MockedMigrations; + + /// The maximal length of an encoded cursor. + /// + /// A good default needs to selected such that no migration will ever have a cursor with MEL + /// above this limit. This is statically checked in `integrity_test`. + #[pallet::constant] + type CursorMaxLen: Get; + + /// The maximal length of an encoded identifier. + /// + /// A good default needs to selected such that no migration will ever have an identifier + /// with MEL above this limit. This is statically checked in `integrity_test`. + #[pallet::constant] + type IdentifierMaxLen: Get; + + /// Notifications for status updates of a runtime upgrade. + /// + /// Could be used to pause XCM etc. + type MigrationStatusHandler: MigrationStatusHandler; + + /// Handler for failed migrations. + type FailedMigrationHandler: FailedMigrationHandler; + + /// The maximum weight to spend each block to execute migrations. + type MaxServiceWeight: Get; + + /// Weight information for the calls and functions of this pallet. + type WeightInfo: WeightInfo; + } + + /// The currently active migration to run and its cursor. + /// + /// `None` indicates that no migration is running. + #[pallet::storage] + pub type Cursor = StorageValue<_, CursorOf, OptionQuery>; + + /// Set of all successfully executed migrations. + /// + /// This is used as blacklist, to not re-execute migrations that have not been removed from the + /// codebase yet. Governance can regularly clear this out via `clear_historic`. + #[pallet::storage] + pub type Historic = StorageMap<_, Twox64Concat, IdentifierOf, (), OptionQuery>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// A Runtime upgrade started. + /// + /// Its end is indicated by `UpgradeCompleted` or `UpgradeFailed`. + UpgradeStarted { + /// The number of migrations that this upgrade contains. + /// + /// This can be used to design a progress indicator in combination with counting the + /// `MigrationCompleted` and `MigrationSkipped` events. + migrations: u32, + }, + /// The current runtime upgrade completed. + /// + /// This implies that all of its migrations completed successfully as well. + UpgradeCompleted, + /// Runtime upgrade failed. + /// + /// This is very bad and will require governance intervention. + UpgradeFailed, + /// A migration was skipped since it was already executed in the past. + MigrationSkipped { + /// The index of the skipped migration within the [`Config::Migrations`] list. + index: u32, + }, + /// A migration progressed. + MigrationAdvanced { + /// The index of the migration within the [`Config::Migrations`] list. + index: u32, + /// The number of blocks that this migration took so far. + took: BlockNumberFor, + }, + /// A Migration completed. + MigrationCompleted { + /// The index of the migration within the [`Config::Migrations`] list. + index: u32, + /// The number of blocks that this migration took so far. + took: BlockNumberFor, + }, + /// A Migration failed. + /// + /// This implies that the whole upgrade failed and governance intervention is required. + MigrationFailed { + /// The index of the migration within the [`Config::Migrations`] list. + index: u32, + /// The number of blocks that this migration took so far. + took: BlockNumberFor, + }, + /// The set of historical migrations has been cleared. + HistoricCleared { + /// Should be passed to `clear_historic` in a successive call. + next_cursor: Option>, + }, + } + + #[pallet::error] + pub enum Error { + /// The operation cannot complete since some MBMs are ongoing. + Ongoing, + } + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_runtime_upgrade() -> Weight { + Self::onboard_new_mbms() + } + + #[cfg(feature = "std")] + fn integrity_test() { + // Check that the migrations tuple is legit. + frame_support::assert_ok!(T::Migrations::integrity_test()); + + // Very important! Ensure that the pallet is configured in `System::Config`. + { + assert!(!Cursor::::exists(), "Externalities storage should be clean"); + assert!(!::MultiBlockMigrator::ongoing()); + + Cursor::::put(MigrationCursor::Stuck); + assert!(::MultiBlockMigrator::ongoing()); + + Cursor::::kill(); + } + + // The per-block service weight is sane. + #[cfg(not(test))] + { + let want = T::MaxServiceWeight::get(); + let max = ::BlockWeights::get().max_block; + + assert!(want.all_lte(max), "Service weight is larger than a block: {want} > {max}",); + } + + // Cursor MEL + { + let mel = T::Migrations::cursor_max_encoded_len(); + let max_mel = T::CursorMaxLen::get() as usize; + assert!( + mel <= max_mel, + "A Cursor is not guaranteed to fit into the storage: {mel} > {max_mel}", + ); + } + + // Identifier MEL + { + let mel = T::Migrations::identifier_max_encoded_len(); + let max_mel = T::IdentifierMaxLen::get() as usize; + assert!( + mel <= max_mel, + "An Identifier is not guaranteed to fit into the storage: {mel} > {max_mel}", + ); + } + } + } + + #[pallet::call(weight = T::WeightInfo)] + impl Pallet { + /// Allows root to set a cursor to forcefully start, stop or forward the migration process. + /// + /// Should normally not be needed and is only in place as emergency measure. Note that + /// restarting the migration process in this manner will not call the + /// [`MigrationStatusHandler::started`] hook or emit an `UpgradeStarted` event. + #[pallet::call_index(0)] + pub fn force_set_cursor( + origin: OriginFor, + cursor: Option>, + ) -> DispatchResult { + ensure_root(origin)?; + + Cursor::::set(cursor); + + Ok(()) + } + + /// Allows root to set an active cursor to forcefully start/forward the migration process. + /// + /// This is an edge-case version of [`Self::force_set_cursor`] that allows to set the + /// `started_at` value to the next block number. Otherwise this would not be possible, since + /// `force_set_cursor` takes an absolute block number. Setting `started_at` to `None` + /// indicates that the current block number plus one should be used. + #[pallet::call_index(1)] + pub fn force_set_active_cursor( + origin: OriginFor, + index: u32, + inner_cursor: Option>, + started_at: Option>, + ) -> DispatchResult { + ensure_root(origin)?; + + let started_at = started_at.unwrap_or( + System::::block_number().saturating_add(sp_runtime::traits::One::one()), + ); + Cursor::::put(MigrationCursor::Active(ActiveCursor { + index, + inner_cursor, + started_at, + })); + + Ok(()) + } + + /// Forces the onboarding of the migrations. + /// + /// This process happens automatically on a runtime upgrade. It is in place as an emergency + /// measurement. The cursor needs to be `None` for this to succeed. + #[pallet::call_index(2)] + pub fn force_onboard_mbms(origin: OriginFor) -> DispatchResult { + ensure_root(origin)?; + + ensure!(!Cursor::::exists(), Error::::Ongoing); + Self::onboard_new_mbms(); + + Ok(()) + } + + /// Clears the `Historic` set. + /// + /// `map_cursor` must be set to the last value that was returned by the + /// `HistoricCleared` event. The first time `None` can be used. `limit` must be chosen in a + /// way that will result in a sensible weight. + #[pallet::call_index(3)] + #[pallet::weight(T::WeightInfo::clear_historic(selector.limit()))] + pub fn clear_historic( + origin: OriginFor, + selector: HistoricCleanupSelector>, + ) -> DispatchResult { + ensure_root(origin)?; + + match &selector { + HistoricCleanupSelector::Specific(ids) => { + for id in ids { + Historic::::remove(id); + } + Self::deposit_event(Event::HistoricCleared { next_cursor: None }); + }, + HistoricCleanupSelector::Wildcard { previous_cursor, .. } => { + let next = Historic::::clear(selector.limit(), previous_cursor.as_deref()); + Self::deposit_event(Event::HistoricCleared { next_cursor: next.maybe_cursor }); + }, + } + + Ok(()) + } + } +} + +impl Pallet { + /// Onboard all new Multi-Block-Migrations and start the process of executing them. + /// + /// Should only be called once all previous migrations completed. + fn onboard_new_mbms() -> Weight { + if let Some(cursor) = Cursor::::get() { + log::error!("Ongoing migrations interrupted - chain stuck"); + + let maybe_index = cursor.as_active().map(|c| c.index); + Self::upgrade_failed(maybe_index); + return T::WeightInfo::onboard_new_mbms() + } + + let migrations = T::Migrations::len(); + log::debug!("Onboarding {migrations} new MBM migrations"); + + if migrations > 0 { + // Set the cursor to the first migration: + Cursor::::set(Some( + ActiveCursor { + index: 0, + inner_cursor: None, + started_at: System::::block_number(), + } + .into(), + )); + Self::deposit_event(Event::UpgradeStarted { migrations }); + T::MigrationStatusHandler::started(); + } + + T::WeightInfo::onboard_new_mbms() + } + + /// Tries to make progress on the Multi-Block-Migrations process. + fn progress_mbms(n: BlockNumberFor) -> Weight { + let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get()); + meter.consume(T::WeightInfo::progress_mbms_none()); + + let mut cursor = match Cursor::::get() { + None => { + log::trace!("[Block {n:?}] Waiting for cursor to become `Some`."); + return meter.consumed() + }, + Some(MigrationCursor::Active(cursor)) => { + log::debug!("Progressing MBM #{}", cursor.index); + cursor + }, + Some(MigrationCursor::Stuck) => { + log::error!("Migration stuck. Governance intervention required."); + return meter.consumed() + }, + }; + debug_assert!(Self::ongoing()); + + // The limit here is a defensive measure to prevent an infinite loop. It expresses that we + // allow no more than 8 MBMs to finish in a single block. This should be harmless, since we + // generally expect *Multi*-Block-Migrations to take *multiple* blocks. + for i in 0..8 { + match Self::exec_migration(cursor, i == 0, &mut meter) { + None => return meter.consumed(), + Some(ControlFlow::Continue(next_cursor)) => { + cursor = next_cursor; + }, + Some(ControlFlow::Break(last_cursor)) => { + cursor = last_cursor; + break + }, + } + } + + Cursor::::set(Some(cursor.into())); + + meter.consumed() + } + + /// Try to make progress on the current migration. + /// + /// Returns whether processing should continue or break for this block. The return value means: + /// - `None`: The migration process is completely finished. + /// - `ControlFlow::Break`: Continue in the *next* block with the given cursor. + /// - `ControlFlow::Continue`: Continue in the *current* block with the given cursor. + fn exec_migration( + mut cursor: ActiveCursorOf, + is_first: bool, + meter: &mut WeightMeter, + ) -> Option, ActiveCursorOf>> { + // The differences between the single branches' weights is not that big. And since we do + // only one step per block, we can just use the maximum instead of more precise accounting. + if meter.try_consume(Self::exec_migration_max_weight()).is_err() { + defensive_assert!(!is_first, "There should be enough weight to do this at least once"); + return Some(ControlFlow::Break(cursor)) + } + + let Some(id) = T::Migrations::nth_id(cursor.index) else { + // No more migrations in the tuple - we are done. + defensive_assert!(cursor.index == T::Migrations::len(), "Inconsistent MBMs tuple"); + Self::deposit_event(Event::UpgradeCompleted); + Cursor::::kill(); + T::MigrationStatusHandler::completed(); + return None; + }; + + let Ok(bounded_id): Result, _> = id.try_into() else { + defensive!("integrity_test ensures that all identifiers' MEL bounds fit into CursorMaxLen; qed."); + Self::upgrade_failed(Some(cursor.index)); + return None + }; + + if Historic::::contains_key(&bounded_id) { + Self::deposit_event(Event::MigrationSkipped { index: cursor.index }); + cursor.goto_next_migration(System::::block_number()); + return Some(ControlFlow::Continue(cursor)) + } + + let max_steps = T::Migrations::nth_max_steps(cursor.index); + let next_cursor = T::Migrations::nth_transactional_step( + cursor.index, + cursor.inner_cursor.clone().map(|c| c.into_inner()), + meter, + ); + let Some((max_steps, next_cursor)) = max_steps.zip(next_cursor) else { + defensive!("integrity_test ensures that the tuple is valid; qed"); + Self::upgrade_failed(Some(cursor.index)); + return None + }; + + let took = System::::block_number().saturating_sub(cursor.started_at); + match next_cursor { + Ok(Some(next_cursor)) => { + let Ok(bound_next_cursor) = next_cursor.try_into() else { + defensive!("The integrity check ensures that all cursors' MEL bound fits into CursorMaxLen; qed"); + Self::upgrade_failed(Some(cursor.index)); + return None + }; + + Self::deposit_event(Event::MigrationAdvanced { index: cursor.index, took }); + cursor.inner_cursor = Some(bound_next_cursor); + + if max_steps.map_or(false, |max| took > max.into()) { + Self::deposit_event(Event::MigrationFailed { index: cursor.index, took }); + Self::upgrade_failed(Some(cursor.index)); + None + } else { + // A migration cannot progress more than one step per block, we therefore break. + Some(ControlFlow::Break(cursor)) + } + }, + Ok(None) => { + // A migration is done when it returns cursor `None`. + Self::deposit_event(Event::MigrationCompleted { index: cursor.index, took }); + Historic::::insert(&bounded_id, ()); + cursor.goto_next_migration(System::::block_number()); + Some(ControlFlow::Continue(cursor)) + }, + Err(SteppedMigrationError::InsufficientWeight { required }) => { + if is_first || required.any_gt(meter.limit()) { + Self::deposit_event(Event::MigrationFailed { index: cursor.index, took }); + Self::upgrade_failed(Some(cursor.index)); + None + } else { + // Retry and hope that there is more weight in the next block. + Some(ControlFlow::Break(cursor)) + } + }, + Err(SteppedMigrationError::InvalidCursor | SteppedMigrationError::Failed) => { + Self::deposit_event(Event::MigrationFailed { index: cursor.index, took }); + Self::upgrade_failed(Some(cursor.index)); + None + }, + } + } + + /// Fail the current runtime upgrade, caused by `migration`. + fn upgrade_failed(migration: Option) { + use FailedMigrationHandling::*; + Self::deposit_event(Event::UpgradeFailed); + + match T::FailedMigrationHandler::failed(migration) { + KeepStuck => Cursor::::set(Some(MigrationCursor::Stuck)), + ForceUnstuck => Cursor::::kill(), + Ignore => {}, + } + } + + fn exec_migration_max_weight() -> Weight { + T::WeightInfo::exec_migration_complete() + .max(T::WeightInfo::exec_migration_completed()) + .max(T::WeightInfo::exec_migration_skipped_historic()) + .max(T::WeightInfo::exec_migration_advance()) + .max(T::WeightInfo::exec_migration_fail()) + } +} + +impl MultiStepMigrator for Pallet { + fn ongoing() -> bool { + Cursor::::exists() + } + + fn step() -> Weight { + Self::progress_mbms(System::::block_number()) + } +} diff --git a/substrate/frame/migrations/src/mock.rs b/substrate/frame/migrations/src/mock.rs new file mode 100644 index 0000000000000000000000000000000000000000..bcd6a189c5bf08fbeaed7eddeecb162e74c16ed7 --- /dev/null +++ b/substrate/frame/migrations/src/mock.rs @@ -0,0 +1,163 @@ +// 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. + +//! Mocked runtime for testing the migrations pallet. + +#![cfg(test)] + +use crate::{mock_helpers::*, Event, Historic}; + +use frame_support::{ + derive_impl, + migrations::*, + traits::{OnFinalize, OnInitialize}, + weights::Weight, +}; +use frame_system::EventRecord; +use sp_core::{ConstU32, H256}; + +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test { + System: frame_system, + Migrations: crate, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Test { + type Block = Block; + type PalletInfo = PalletInfo; + type MultiBlockMigrator = Migrations; +} + +frame_support::parameter_types! { + pub const MaxServiceWeight: Weight = Weight::MAX.div(10); +} + +impl crate::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Migrations = MockedMigrations; + type CursorMaxLen = ConstU32<65_536>; + type IdentifierMaxLen = ConstU32<256>; + type MigrationStatusHandler = MockedMigrationStatusHandler; + type FailedMigrationHandler = MockedFailedMigrationHandler; + type MaxServiceWeight = MaxServiceWeight; + type WeightInfo = (); +} + +frame_support::parameter_types! { + /// The number of started upgrades. + pub static UpgradesStarted: u32 = 0; + /// The number of completed upgrades. + pub static UpgradesCompleted: u32 = 0; + /// The migrations that failed. + pub static UpgradesFailed: Vec> = vec![]; + /// Return value of [`MockedFailedMigrationHandler::failed`]. + pub static FailedUpgradeResponse: FailedMigrationHandling = FailedMigrationHandling::KeepStuck; +} + +/// Records all started and completed upgrades in `UpgradesStarted` and `UpgradesCompleted`. +pub struct MockedMigrationStatusHandler; +impl MigrationStatusHandler for MockedMigrationStatusHandler { + fn started() { + log::info!("MigrationStatusHandler started"); + UpgradesStarted::mutate(|v| *v += 1); + } + + fn completed() { + log::info!("MigrationStatusHandler completed"); + UpgradesCompleted::mutate(|v| *v += 1); + } +} + +/// Records all failed upgrades in `UpgradesFailed`. +pub struct MockedFailedMigrationHandler; +impl FailedMigrationHandler for MockedFailedMigrationHandler { + fn failed(migration: Option) -> FailedMigrationHandling { + UpgradesFailed::mutate(|v| v.push(migration)); + let res = FailedUpgradeResponse::get(); + log::error!("FailedMigrationHandler failed at: {migration:?}, handling as {res:?}"); + res + } +} + +/// Returns the number of `(started, completed, failed)` upgrades and resets their numbers. +pub fn upgrades_started_completed_failed() -> (u32, u32, u32) { + (UpgradesStarted::take(), UpgradesCompleted::take(), UpgradesFailed::take().len() as u32) +} + +/// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + sp_io::TestExternalities::new(Default::default()) +} + +/// Run this closure in test externalities. +pub fn test_closure(f: impl FnOnce() -> R) -> R { + let mut ext = new_test_ext(); + ext.execute_with(f) +} + +pub fn run_to_block(n: u32) { + while System::block_number() < n as u64 { + log::debug!("Block {}", System::block_number()); + System::set_block_number(System::block_number() + 1); + System::on_initialize(System::block_number()); + Migrations::on_initialize(System::block_number()); + // Executive calls this: + ::step(); + + Migrations::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); + } +} + +/// Returns the historic migrations, sorted by their identifier. +pub fn historic() -> Vec { + let mut historic = Historic::::iter_keys().collect::>(); + historic.sort(); + historic +} + +// Traits to make using events less insufferable: +pub trait IntoRecord { + fn into_record(self) -> EventRecord<::RuntimeEvent, H256>; +} + +impl IntoRecord for Event { + fn into_record(self) -> EventRecord<::RuntimeEvent, H256> { + let re: ::RuntimeEvent = self.into(); + EventRecord { phase: frame_system::Phase::Initialization, event: re, topics: vec![] } + } +} + +pub trait IntoRecords { + fn into_records(self) -> Vec::RuntimeEvent, H256>>; +} + +impl IntoRecords for Vec { + fn into_records(self) -> Vec::RuntimeEvent, H256>> { + self.into_iter().map(|e| e.into_record()).collect() + } +} + +pub fn assert_events(events: Vec) { + pretty_assertions::assert_eq!(events.into_records(), System::events()); + System::reset_events(); +} diff --git a/substrate/frame/migrations/src/mock_helpers.rs b/substrate/frame/migrations/src/mock_helpers.rs new file mode 100644 index 0000000000000000000000000000000000000000..995ec0a922ccbce7745446bc5af73d9c2f20b044 --- /dev/null +++ b/substrate/frame/migrations/src/mock_helpers.rs @@ -0,0 +1,142 @@ +// 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. + +//! Test helpers for internal and external usage. + +#![allow(missing_docs)] + +use codec::{Decode, Encode}; +use frame_support::{ + migrations::*, + weights::{Weight, WeightMeter}, +}; +use sp_core::ConstU32; +use sp_runtime::BoundedVec; +use sp_std::{vec, vec::Vec}; + +/// Opaque identifier of a migration. +pub type MockedIdentifier = BoundedVec>; + +/// How a mocked migration should behave. +#[derive(Debug, Clone, Copy, Encode, Decode)] +pub enum MockedMigrationKind { + /// Succeed after its number of steps elapsed. + SucceedAfter, + /// Fail after its number of steps elapsed. + FailAfter, + /// Never terminate. + TimeoutAfter, + /// Cause an [`SteppedMigrationError::InsufficientWeight`] error after its number of steps + /// elapsed. + HighWeightAfter(Weight), +} +use MockedMigrationKind::*; // C style + +/// Creates a migration identifier with a specific `kind` and `steps`. +pub fn mocked_id(kind: MockedMigrationKind, steps: u32) -> MockedIdentifier { + (b"MockedMigration", kind, steps).encode().try_into().unwrap() +} + +frame_support::parameter_types! { + /// The configs for the migrations to run. + storage MIGRATIONS: Vec<(MockedMigrationKind, u32)> = vec![]; +} + +/// Allows to set the migrations to run at runtime instead of compile-time. +/// +/// It achieves this by using the storage to store the migrations to run. +pub struct MockedMigrations; +impl SteppedMigrations for MockedMigrations { + fn len() -> u32 { + MIGRATIONS::get().len() as u32 + } + + fn nth_id(n: u32) -> Option> { + let k = MIGRATIONS::get().get(n as usize).copied(); + k.map(|(kind, steps)| mocked_id(kind, steps).into_inner()) + } + + fn nth_step( + n: u32, + cursor: Option>, + _meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>> { + let (kind, steps) = MIGRATIONS::get()[n as usize]; + + let mut count: u32 = + cursor.as_ref().and_then(|c| Decode::decode(&mut &c[..]).ok()).unwrap_or(0); + log::debug!("MockedMigration: Step {}", count); + if count != steps || matches!(kind, TimeoutAfter) { + count += 1; + return Some(Ok(Some(count.encode()))) + } + + Some(match kind { + SucceedAfter => { + log::debug!("MockedMigration: Succeeded after {} steps", count); + Ok(None) + }, + HighWeightAfter(required) => { + log::debug!("MockedMigration: Not enough weight after {} steps", count); + Err(SteppedMigrationError::InsufficientWeight { required }) + }, + FailAfter => { + log::debug!("MockedMigration: Failed after {} steps", count); + Err(SteppedMigrationError::Failed) + }, + TimeoutAfter => unreachable!(), + }) + } + + fn nth_transactional_step( + n: u32, + cursor: Option>, + meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>> { + // This is a hack but should be fine. We don't need it in testing. + Self::nth_step(n, cursor, meter) + } + + fn nth_max_steps(n: u32) -> Option> { + MIGRATIONS::get().get(n as usize).map(|(_, s)| Some(*s)) + } + + fn cursor_max_encoded_len() -> usize { + 65_536 + } + + fn identifier_max_encoded_len() -> usize { + 256 + } +} + +impl MockedMigrations { + /// Set the migrations to run. + pub fn set(migrations: Vec<(MockedMigrationKind, u32)>) { + MIGRATIONS::set(&migrations); + } +} + +impl crate::MockedMigrations for MockedMigrations { + fn set_fail_after(steps: u32) { + MIGRATIONS::set(&vec![(FailAfter, steps)]); + } + + fn set_success_after(steps: u32) { + MIGRATIONS::set(&vec![(SucceedAfter, steps)]); + } +} diff --git a/substrate/frame/migrations/src/tests.rs b/substrate/frame/migrations/src/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..9c9043d37a62e5f1df3d12da979b68251621c9d0 --- /dev/null +++ b/substrate/frame/migrations/src/tests.rs @@ -0,0 +1,335 @@ +// 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. + +#![cfg(test)] + +use crate::{ + mock::{Test as T, *}, + mock_helpers::{MockedMigrationKind::*, *}, + Cursor, Event, FailedMigrationHandling, MigrationCursor, +}; +use frame_support::{pallet_prelude::Weight, traits::OnRuntimeUpgrade}; + +#[docify::export] +#[test] +fn simple_works() { + use Event::*; + test_closure(|| { + // Add three migrations, each taking one block longer than the previous. + MockedMigrations::set(vec![(SucceedAfter, 0), (SucceedAfter, 1), (SucceedAfter, 2)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(10); + + // Check that the executed migrations are recorded in `Historical`. + assert_eq!( + historic(), + vec![ + mocked_id(SucceedAfter, 0), + mocked_id(SucceedAfter, 1), + mocked_id(SucceedAfter, 2), + ] + ); + + // Check that we got all events. + assert_events(vec![ + UpgradeStarted { migrations: 3 }, + MigrationCompleted { index: 0, took: 1 }, + MigrationAdvanced { index: 1, took: 0 }, + MigrationCompleted { index: 1, took: 1 }, + MigrationAdvanced { index: 2, took: 0 }, + MigrationAdvanced { index: 2, took: 1 }, + MigrationCompleted { index: 2, took: 2 }, + UpgradeCompleted, + ]); + }); +} + +#[test] +fn failing_migration_sets_cursor_to_stuck() { + test_closure(|| { + FailedUpgradeResponse::set(FailedMigrationHandling::KeepStuck); + MockedMigrations::set(vec![(FailAfter, 2)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(10); + + // Failed migrations are not recorded in `Historical`. + assert!(historic().is_empty()); + // Check that we got all events. + assert_events(vec![ + Event::UpgradeStarted { migrations: 1 }, + Event::MigrationAdvanced { index: 0, took: 1 }, + Event::MigrationAdvanced { index: 0, took: 2 }, + Event::MigrationFailed { index: 0, took: 3 }, + Event::UpgradeFailed, + ]); + + // Check that the handler was called correctly. + assert_eq!(UpgradesStarted::take(), 1); + assert_eq!(UpgradesCompleted::take(), 0); + assert_eq!(UpgradesFailed::take(), vec![Some(0)]); + + assert_eq!(Cursor::::get(), Some(MigrationCursor::Stuck), "Must stuck the chain"); + }); +} + +#[test] +fn failing_migration_force_unstuck_works() { + test_closure(|| { + FailedUpgradeResponse::set(FailedMigrationHandling::ForceUnstuck); + MockedMigrations::set(vec![(FailAfter, 2)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(10); + + // Failed migrations are not recorded in `Historical`. + assert!(historic().is_empty()); + // Check that we got all events. + assert_events(vec![ + Event::UpgradeStarted { migrations: 1 }, + Event::MigrationAdvanced { index: 0, took: 1 }, + Event::MigrationAdvanced { index: 0, took: 2 }, + Event::MigrationFailed { index: 0, took: 3 }, + Event::UpgradeFailed, + ]); + + // Check that the handler was called correctly. + assert_eq!(UpgradesStarted::take(), 1); + assert_eq!(UpgradesCompleted::take(), 0); + assert_eq!(UpgradesFailed::take(), vec![Some(0)]); + + assert!(Cursor::::get().is_none(), "Must unstuck the chain"); + }); +} + +/// A migration that reports not getting enough weight errors if it is the first one to run in that +/// block. +#[test] +fn high_weight_migration_singular_fails() { + test_closure(|| { + MockedMigrations::set(vec![(HighWeightAfter(Weight::zero()), 2)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(10); + + // Failed migrations are not recorded in `Historical`. + assert!(historic().is_empty()); + // Check that we got all events. + assert_events(vec![ + Event::UpgradeStarted { migrations: 1 }, + Event::MigrationAdvanced { index: 0, took: 1 }, + Event::MigrationAdvanced { index: 0, took: 2 }, + Event::MigrationFailed { index: 0, took: 3 }, + Event::UpgradeFailed, + ]); + + // Check that the handler was called correctly. + assert_eq!(upgrades_started_completed_failed(), (1, 0, 1)); + assert_eq!(Cursor::::get(), Some(MigrationCursor::Stuck)); + }); +} + +/// A migration that reports of not getting enough weight is retried once, if it is not the first +/// one to run in a block. +#[test] +fn high_weight_migration_retries_once() { + test_closure(|| { + MockedMigrations::set(vec![(SucceedAfter, 0), (HighWeightAfter(Weight::zero()), 0)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(10); + + assert_eq!(historic(), vec![mocked_id(SucceedAfter, 0)]); + // Check that we got all events. + assert_events::>(vec![ + Event::UpgradeStarted { migrations: 2 }, + Event::MigrationCompleted { index: 0, took: 1 }, + // `took=1` means that it was retried once. + Event::MigrationFailed { index: 1, took: 1 }, + Event::UpgradeFailed, + ]); + + // Check that the handler was called correctly. + assert_eq!(upgrades_started_completed_failed(), (1, 0, 1)); + assert_eq!(Cursor::::get(), Some(MigrationCursor::Stuck)); + }); +} + +/// If a migration uses more weight than the limit, then it will not retry but fail even when it is +/// not the first one in the block. +// Note: Same as `high_weight_migration_retries_once` but with different required weight for the +// migration. +#[test] +fn high_weight_migration_permanently_overweight_fails() { + test_closure(|| { + MockedMigrations::set(vec![(SucceedAfter, 0), (HighWeightAfter(Weight::MAX), 0)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(10); + + assert_eq!(historic(), vec![mocked_id(SucceedAfter, 0)]); + // Check that we got all events. + assert_events::>(vec![ + Event::UpgradeStarted { migrations: 2 }, + Event::MigrationCompleted { index: 0, took: 1 }, + // `blocks=0` means that it was not retried. + Event::MigrationFailed { index: 1, took: 0 }, + Event::UpgradeFailed, + ]); + + // Check that the handler was called correctly. + assert_eq!(upgrades_started_completed_failed(), (1, 0, 1)); + assert_eq!(Cursor::::get(), Some(MigrationCursor::Stuck)); + }); +} + +#[test] +fn historic_skipping_works() { + test_closure(|| { + MockedMigrations::set(vec![ + (SucceedAfter, 0), + (SucceedAfter, 0), // duplicate + (SucceedAfter, 1), + (SucceedAfter, 2), + (SucceedAfter, 1), // duplicate + ]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(10); + + // Just three historical ones, since two were added twice. + assert_eq!( + historic(), + vec![ + mocked_id(SucceedAfter, 0), + mocked_id(SucceedAfter, 1), + mocked_id(SucceedAfter, 2), + ] + ); + // Events received. + assert_events(vec![ + Event::UpgradeStarted { migrations: 5 }, + Event::MigrationCompleted { index: 0, took: 1 }, + Event::MigrationSkipped { index: 1 }, + Event::MigrationAdvanced { index: 2, took: 0 }, + Event::MigrationCompleted { index: 2, took: 1 }, + Event::MigrationAdvanced { index: 3, took: 0 }, + Event::MigrationAdvanced { index: 3, took: 1 }, + Event::MigrationCompleted { index: 3, took: 2 }, + Event::MigrationSkipped { index: 4 }, + Event::UpgradeCompleted, + ]); + assert_eq!(upgrades_started_completed_failed(), (1, 1, 0)); + + // Now go for another upgrade; just to make sure that it wont execute again. + System::reset_events(); + Migrations::on_runtime_upgrade(); + run_to_block(20); + + // Same historical ones as before. + assert_eq!( + historic(), + vec![ + mocked_id(SucceedAfter, 0), + mocked_id(SucceedAfter, 1), + mocked_id(SucceedAfter, 2), + ] + ); + + // Everything got skipped. + assert_events(vec![ + Event::UpgradeStarted { migrations: 5 }, + Event::MigrationSkipped { index: 0 }, + Event::MigrationSkipped { index: 1 }, + Event::MigrationSkipped { index: 2 }, + Event::MigrationSkipped { index: 3 }, + Event::MigrationSkipped { index: 4 }, + Event::UpgradeCompleted, + ]); + assert_eq!(upgrades_started_completed_failed(), (1, 1, 0)); + }); +} + +/// When another upgrade happens while a migration is still running, it should set the cursor to +/// stuck. +#[test] +fn upgrade_fails_when_migration_active() { + test_closure(|| { + MockedMigrations::set(vec![(SucceedAfter, 10)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(3); + + // Events received. + assert_events(vec![ + Event::UpgradeStarted { migrations: 1 }, + Event::MigrationAdvanced { index: 0, took: 1 }, + Event::MigrationAdvanced { index: 0, took: 2 }, + ]); + assert_eq!(upgrades_started_completed_failed(), (1, 0, 0)); + + // Upgrade again. + Migrations::on_runtime_upgrade(); + // -- Defensive path -- + assert_eq!(Cursor::::get(), Some(MigrationCursor::Stuck)); + assert_events(vec![Event::UpgradeFailed]); + assert_eq!(upgrades_started_completed_failed(), (0, 0, 1)); + }); +} + +#[test] +fn migration_timeout_errors() { + test_closure(|| { + MockedMigrations::set(vec![(TimeoutAfter, 3)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(5); + + // Times out after taking more than 3 steps. + assert_events(vec![ + Event::UpgradeStarted { migrations: 1 }, + Event::MigrationAdvanced { index: 0, took: 1 }, + Event::MigrationAdvanced { index: 0, took: 2 }, + Event::MigrationAdvanced { index: 0, took: 3 }, + Event::MigrationAdvanced { index: 0, took: 4 }, + Event::MigrationFailed { index: 0, took: 4 }, + Event::UpgradeFailed, + ]); + assert_eq!(upgrades_started_completed_failed(), (1, 0, 1)); + + // Failed migrations are not black-listed. + assert!(historic().is_empty()); + assert_eq!(Cursor::::get(), Some(MigrationCursor::Stuck)); + + Migrations::on_runtime_upgrade(); + run_to_block(6); + + assert_events(vec![Event::UpgradeFailed]); + assert_eq!(Cursor::::get(), Some(MigrationCursor::Stuck)); + assert_eq!(upgrades_started_completed_failed(), (0, 0, 1)); + }); +} diff --git a/substrate/frame/migrations/src/weights.rs b/substrate/frame/migrations/src/weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..c9b63258c44b7948f113161be6452b9a0f7348cc --- /dev/null +++ b/substrate/frame/migrations/src/weights.rs @@ -0,0 +1,358 @@ +// 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. + +//! Autogenerated weights for `pallet_migrations` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-04, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `loud1`, CPU: `AMD EPYC 7282 16-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// target/release/substrate-node +// benchmark +// pallet +// --chain +// dev +// --pallet +// pallet-migrations +// --extrinsic +// +// --output +// weight.rs +// --template +// ../../polkadot-sdk/substrate/.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for `pallet_migrations`. +pub trait WeightInfo { + fn onboard_new_mbms() -> Weight; + fn progress_mbms_none() -> Weight; + fn exec_migration_completed() -> Weight; + fn exec_migration_skipped_historic() -> Weight; + fn exec_migration_advance() -> Weight; + fn exec_migration_complete() -> Weight; + fn exec_migration_fail() -> Weight; + fn on_init_loop() -> Weight; + fn force_set_cursor() -> Weight; + fn force_set_active_cursor() -> Weight; + fn force_onboard_mbms() -> Weight; + fn clear_historic(n: u32, ) -> Weight; +} + +/// Weights for `pallet_migrations` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + fn onboard_new_mbms() -> Weight { + // Proof Size summary in bytes: + // Measured: `243` + // Estimated: `67035` + // Minimum execution time: 13_980_000 picoseconds. + Weight::from_parts(14_290_000, 67035) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn progress_mbms_none() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `67035` + // Minimum execution time: 3_770_000 picoseconds. + Weight::from_parts(4_001_000, 67035) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn exec_migration_completed() -> Weight { + // Proof Size summary in bytes: + // Measured: `134` + // Estimated: `3599` + // Minimum execution time: 10_900_000 picoseconds. + Weight::from_parts(11_251_000, 3599) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_skipped_historic() -> Weight { + // Proof Size summary in bytes: + // Measured: `297` + // Estimated: `3762` + // Minimum execution time: 17_891_000 picoseconds. + Weight::from_parts(18_501_000, 3762) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_advance() -> Weight { + // Proof Size summary in bytes: + // Measured: `243` + // Estimated: `3731` + // Minimum execution time: 18_271_000 picoseconds. + Weight::from_parts(18_740_000, 3731) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:1) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_complete() -> Weight { + // Proof Size summary in bytes: + // Measured: `243` + // Estimated: `3731` + // Minimum execution time: 21_241_000 picoseconds. + Weight::from_parts(21_911_000, 3731) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn exec_migration_fail() -> Weight { + // Proof Size summary in bytes: + // Measured: `243` + // Estimated: `3731` + // Minimum execution time: 22_740_000 picoseconds. + Weight::from_parts(23_231_000, 3731) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn on_init_loop() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 440_000 picoseconds. + Weight::from_parts(500_000, 0) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn force_set_cursor() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_751_000 picoseconds. + Weight::from_parts(5_950_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn force_set_active_cursor() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_350_000 picoseconds. + Weight::from_parts(6_560_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + fn force_onboard_mbms() -> Weight { + // Proof Size summary in bytes: + // Measured: `218` + // Estimated: `67035` + // Minimum execution time: 11_121_000 picoseconds. + Weight::from_parts(11_530_000, 67035) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `MultiBlockMigrations::Historic` (r:256 w:256) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 256]`. + fn clear_historic(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1089 + n * (271 ±0)` + // Estimated: `3834 + n * (2740 ±0)` + // Minimum execution time: 21_891_000 picoseconds. + Weight::from_parts(18_572_306, 3834) + // Standard Error: 3_236 + .saturating_add(Weight::from_parts(1_648_429, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2740).saturating_mul(n.into())) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + fn onboard_new_mbms() -> Weight { + // Proof Size summary in bytes: + // Measured: `243` + // Estimated: `67035` + // Minimum execution time: 13_980_000 picoseconds. + Weight::from_parts(14_290_000, 67035) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn progress_mbms_none() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `67035` + // Minimum execution time: 3_770_000 picoseconds. + Weight::from_parts(4_001_000, 67035) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn exec_migration_completed() -> Weight { + // Proof Size summary in bytes: + // Measured: `134` + // Estimated: `3599` + // Minimum execution time: 10_900_000 picoseconds. + Weight::from_parts(11_251_000, 3599) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_skipped_historic() -> Weight { + // Proof Size summary in bytes: + // Measured: `297` + // Estimated: `3762` + // Minimum execution time: 17_891_000 picoseconds. + Weight::from_parts(18_501_000, 3762) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_advance() -> Weight { + // Proof Size summary in bytes: + // Measured: `243` + // Estimated: `3731` + // Minimum execution time: 18_271_000 picoseconds. + Weight::from_parts(18_740_000, 3731) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:1) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_complete() -> Weight { + // Proof Size summary in bytes: + // Measured: `243` + // Estimated: `3731` + // Minimum execution time: 21_241_000 picoseconds. + Weight::from_parts(21_911_000, 3731) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn exec_migration_fail() -> Weight { + // Proof Size summary in bytes: + // Measured: `243` + // Estimated: `3731` + // Minimum execution time: 22_740_000 picoseconds. + Weight::from_parts(23_231_000, 3731) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + fn on_init_loop() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 440_000 picoseconds. + Weight::from_parts(500_000, 0) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn force_set_cursor() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_751_000 picoseconds. + Weight::from_parts(5_950_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn force_set_active_cursor() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_350_000 picoseconds. + Weight::from_parts(6_560_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + fn force_onboard_mbms() -> Weight { + // Proof Size summary in bytes: + // Measured: `218` + // Estimated: `67035` + // Minimum execution time: 11_121_000 picoseconds. + Weight::from_parts(11_530_000, 67035) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } + /// Storage: `MultiBlockMigrations::Historic` (r:256 w:256) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 256]`. + fn clear_historic(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1089 + n * (271 ±0)` + // Estimated: `3834 + n * (2740 ±0)` + // Minimum execution time: 21_891_000 picoseconds. + Weight::from_parts(18_572_306, 3834) + // Standard Error: 3_236 + .saturating_add(Weight::from_parts(1_648_429, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2740).saturating_mul(n.into())) + } +} diff --git a/substrate/frame/multisig/src/lib.rs b/substrate/frame/multisig/src/lib.rs index e4426c64b4125fe267a1e30a42a7196274b05e4e..a83b78e316f500ddc9c615420c2ac627b90ee7e9 100644 --- a/substrate/frame/multisig/src/lib.rs +++ b/substrate/frame/multisig/src/lib.rs @@ -168,7 +168,7 @@ pub mod pallet { type WeightInfo: WeightInfo; } - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] diff --git a/substrate/frame/multisig/src/migrations.rs b/substrate/frame/multisig/src/migrations.rs index d03e42a66a5b5af07248ee7b11061d5f26b66c98..e6402600d0d368783e30f43e0afc3adad4d0a1ba 100644 --- a/substrate/frame/multisig/src/migrations.rs +++ b/substrate/frame/multisig/src/migrations.rs @@ -51,7 +51,7 @@ pub mod v1 { fn on_runtime_upgrade() -> Weight { use sp_runtime::Saturating; - let current = Pallet::::current_storage_version(); + let current = Pallet::::in_code_storage_version(); let onchain = Pallet::::on_chain_storage_version(); if onchain > 0 { diff --git a/substrate/frame/multisig/src/tests.rs b/substrate/frame/multisig/src/tests.rs index 887c7f8bebc47a8c7d4f9b5b4defb560874cfab6..0d73e3db66153f6c5fe16dd4974567c3584a2372 100644 --- a/substrate/frame/multisig/src/tests.rs +++ b/substrate/frame/multisig/src/tests.rs @@ -38,7 +38,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type BlockHashCount = ConstU32<250>; @@ -47,7 +47,7 @@ impl frame_system::Config for Test { type BaseCallFilter = TestBaseCallFilter; } -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for Test { type ReserveIdentifier = [u8; 8]; type AccountStore = System; diff --git a/substrate/frame/nft-fractionalization/src/mock.rs b/substrate/frame/nft-fractionalization/src/mock.rs index a41386150091de0d77153593dfe6ce68a07cfe18..82a608816260041d6887af80334b423de1c4f744 100644 --- a/substrate/frame/nft-fractionalization/src/mock.rs +++ b/substrate/frame/nft-fractionalization/src/mock.rs @@ -49,7 +49,7 @@ construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type AccountId = AccountId; type Lookup = IdentityLookup; diff --git a/substrate/frame/nfts/src/lib.rs b/substrate/frame/nfts/src/lib.rs index a7d505e2e397dbca5f547ed9ed1a7c3cc4235aa5..615720268fed611bbf7effa1f4b71eca0df4a7e7 100644 --- a/substrate/frame/nfts/src/lib.rs +++ b/substrate/frame/nfts/src/lib.rs @@ -76,7 +76,7 @@ pub mod pallet { use frame_support::{pallet_prelude::*, traits::ExistenceRequirement}; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] @@ -874,8 +874,8 @@ pub mod pallet { ), ); Self::deposit_event(Event::PalletAttributeSet { - collection, - item: Some(item), + collection: collection_id, + item: Some(owned_item), attribute: pallet_attribute, value: attribute_value, }); diff --git a/substrate/frame/nfts/src/migration.rs b/substrate/frame/nfts/src/migration.rs index d4cdf7e820d15b7df1d8f36807baf34b81c34bd3..8f82e092262fb2d63cf1dda563afb0dd11c6f6ac 100644 --- a/substrate/frame/nfts/src/migration.rs +++ b/substrate/frame/nfts/src/migration.rs @@ -54,17 +54,17 @@ pub mod v1 { pub struct MigrateToV1(core::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { fn on_runtime_upgrade() -> Weight { - let current_version = Pallet::::current_storage_version(); - let onchain_version = Pallet::::on_chain_storage_version(); + let in_code_version = Pallet::::in_code_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); log::info!( target: LOG_TARGET, - "Running migration with current storage version {:?} / onchain {:?}", - current_version, - onchain_version + "Running migration with in-code storage version {:?} / onchain {:?}", + in_code_version, + on_chain_version ); - if onchain_version == 0 && current_version == 1 { + if on_chain_version == 0 && in_code_version == 1 { let mut translated = 0u64; let mut configs_iterated = 0u64; Collection::::translate::< @@ -77,13 +77,13 @@ pub mod v1 { Some(old_value.migrate_to_v1(item_configs)) }); - current_version.put::>(); + in_code_version.put::>(); log::info!( target: LOG_TARGET, "Upgraded {} records, storage to version {:?}", translated, - current_version + in_code_version ); T::DbWeight::get().reads_writes(translated + configs_iterated + 1, translated + 1) } else { diff --git a/substrate/frame/nfts/src/mock.rs b/substrate/frame/nfts/src/mock.rs index e86fafd07e965ab995a04e512bd9794833735d4e..51cfd5f244bcdb14fcdb1f66cff245df9030185f 100644 --- a/substrate/frame/nfts/src/mock.rs +++ b/substrate/frame/nfts/src/mock.rs @@ -45,7 +45,7 @@ pub type Signature = MultiSignature; pub type AccountPublic = ::Signer; pub type AccountId = ::AccountId; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type AccountId = AccountId; type Lookup = IdentityLookup; diff --git a/substrate/frame/nfts/src/tests.rs b/substrate/frame/nfts/src/tests.rs index 9e521537534fab31c4626c21ecf06e3df366c01b..6bf9427f4e6cda9ef2eecaf9abed2721e7127ecf 100644 --- a/substrate/frame/nfts/src/tests.rs +++ b/substrate/frame/nfts/src/tests.rs @@ -440,6 +440,12 @@ fn mint_should_work() { account(2), Some(MintWitness { owned_item: Some(43), ..Default::default() }) )); + assert!(events().contains(&Event::::PalletAttributeSet { + collection: 0, + item: Some(43), + attribute: PalletAttributes::<::CollectionId>::UsedToClaim(1), + value: Nfts::construct_attribute_value(vec![]).unwrap(), + })); // can't mint twice assert_noop!( diff --git a/substrate/frame/nis/README.md b/substrate/frame/nis/README.md index 032df7d01868f8643284985985fc98b3f6ccc0ac..8a1a30f17e18a20168e81995e2b64e2f87603968 100644 --- a/substrate/frame/nis/README.md +++ b/substrate/frame/nis/README.md @@ -1,5 +1,5 @@ # NIS Module -Provides a non-interactiove variant of staking. +Provides a non-interactive variant of staking. License: Apache-2.0 diff --git a/substrate/frame/nis/src/lib.rs b/substrate/frame/nis/src/lib.rs index 5e547b63e5474ea36bdcbdb699200f162ca3ac2e..7655cd1a82433215552ed9e7d08a7ec50354a573 100644 --- a/substrate/frame/nis/src/lib.rs +++ b/substrate/frame/nis/src/lib.rs @@ -426,7 +426,7 @@ pub mod pallet { }, /// An automatic funding of the deficit was made. Funded { deficit: BalanceOf }, - /// A receipt was transfered. + /// A receipt was transferred. Transferred { from: T::AccountId, to: T::AccountId, index: ReceiptIndex }, } @@ -457,7 +457,7 @@ pub mod pallet { AlreadyFunded, /// The thaw throttle has been reached for this period. Throttled, - /// The operation would result in a receipt worth an insignficant value. + /// The operation would result in a receipt worth an insignificant value. MakesDust, /// The receipt is already communal. AlreadyCommunal, diff --git a/substrate/frame/nis/src/mock.rs b/substrate/frame/nis/src/mock.rs index 03976bc66c4df6844c81232fef7f912cb02b5292..33464db34c30de36e900b08613f840104757558d 100644 --- a/substrate/frame/nis/src/mock.rs +++ b/substrate/frame/nis/src/mock.rs @@ -44,7 +44,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; diff --git a/substrate/frame/nis/src/tests.rs b/substrate/frame/nis/src/tests.rs index 7350da97dc60a65747dc6b195510781ea149b73b..01724999ae7e9fb346f940be776e8b1273ea98f4 100644 --- a/substrate/frame/nis/src/tests.rs +++ b/substrate/frame/nis/src/tests.rs @@ -414,7 +414,7 @@ fn thaw_respects_transfers() { assert_eq!(Balances::reserved_balance(&1), 0); assert_eq!(Balances::reserved_balance(&2), 40); - // Transfering the receipt... + // Transferring the receipt... assert_noop!(Nis::thaw_private(signed(1), 0, None), Error::::NotOwner); // ...and thawing is possible. diff --git a/substrate/frame/node-authorization/src/lib.rs b/substrate/frame/node-authorization/src/lib.rs index 8a823d29f2355bc6b3cbf4fe8e25906448f2888b..9019a863ad8124fdb8aacac6a8e532d860861730 100644 --- a/substrate/frame/node-authorization/src/lib.rs +++ b/substrate/frame/node-authorization/src/lib.rs @@ -18,7 +18,7 @@ //! # Node authorization pallet //! //! This pallet manages a configurable set of nodes for a permissioned network. -//! Each node is dentified by a PeerId (i.e. `Vec`). It provides two ways to +//! Each node is identified by a PeerId (i.e. `Vec`). It provides two ways to //! authorize a node, //! //! - a set of well known nodes across different organizations in which the @@ -102,7 +102,7 @@ pub mod pallet { #[pallet::getter(fn owners)] pub type Owners = StorageMap<_, Blake2_128Concat, PeerId, T::AccountId>; - /// The additional adapative connections of each node. + /// The additional adaptive connections of each node. #[pallet::storage] #[pallet::getter(fn additional_connection)] pub type AdditionalConnections = @@ -161,7 +161,7 @@ pub mod pallet { NotClaimed, /// You are not the owner of the node. NotOwner, - /// No permisson to perform specific operation. + /// No permission to perform specific operation. PermissionDenied, } @@ -377,7 +377,7 @@ pub mod pallet { /// Add additional connections to a given node. /// /// - `node`: identifier of the node. - /// - `connections`: additonal nodes from which the connections are allowed. + /// - `connections`: additional nodes from which the connections are allowed. #[pallet::call_index(7)] #[pallet::weight(T::WeightInfo::add_connections())] pub fn add_connections( @@ -412,7 +412,7 @@ pub mod pallet { /// Remove additional connections of a given node. /// /// - `node`: identifier of the node. - /// - `connections`: additonal nodes from which the connections are not allowed anymore. + /// - `connections`: additional nodes from which the connections are not allowed anymore. #[pallet::call_index(8)] #[pallet::weight(T::WeightInfo::remove_connections())] pub fn remove_connections( diff --git a/substrate/frame/node-authorization/src/mock.rs b/substrate/frame/node-authorization/src/mock.rs index 84ca4d7eff701118e8ec4d140d164e709cc1c8f6..656d2bfa39ad3d4b2ac22c135691c66dd9079b4a 100644 --- a/substrate/frame/node-authorization/src/mock.rs +++ b/substrate/frame/node-authorization/src/mock.rs @@ -34,7 +34,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } diff --git a/substrate/frame/nomination-pools/benchmarking/src/mock.rs b/substrate/frame/nomination-pools/benchmarking/src/mock.rs index 4e57c00849f2d54a140deb36d7b60109aa59e9c9..1c513a1007cbca1f707cb0e40110050e51409cea 100644 --- a/substrate/frame/nomination-pools/benchmarking/src/mock.rs +++ b/substrate/frame/nomination-pools/benchmarking/src/mock.rs @@ -28,7 +28,7 @@ type Nonce = u32; type BlockNumber = u64; type Balance = u128; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 074d59931ade733a3199e227a99c6dff819be793..f29a49a2b1b3de3acd24c497cd6d36134d1687c7 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -451,7 +451,7 @@ enum AccountType { /// The permission a pool member can set for other accounts to claim rewards on their behalf. #[derive(Encode, Decode, MaxEncodedLen, Clone, Copy, Debug, PartialEq, Eq, TypeInfo)] pub enum ClaimPermission { - /// Only the pool member themself can claim their rewards. + /// Only the pool member themselves can claim their rewards. Permissioned, /// Anyone can compound rewards on a pool member's behalf. PermissionlessCompound, @@ -1576,7 +1576,7 @@ pub mod pallet { use frame_system::{ensure_signed, pallet_prelude::*}; use sp_runtime::Perbill; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(8); #[pallet::pallet] @@ -2066,7 +2066,7 @@ pub mod pallet { /// The member will earn rewards pro rata based on the members stake vs the sum of the /// members in the pools stake. Rewards do not "expire". /// - /// See `claim_payout_other` to caim rewards on bahalf of some `other` pool member. + /// See `claim_payout_other` to claim rewards on behalf of some `other` pool member. #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::claim_payout())] pub fn claim_payout(origin: OriginFor) -> DispatchResult { @@ -2401,6 +2401,11 @@ pub mod pallet { /// /// This directly forward the call to the staking pallet, on behalf of the pool bonded /// account. + /// + /// # Note + /// + /// In addition to a `root` or `nominator` role of `origin`, pool's depositor needs to have + /// at least `depositor_min_bond` in the pool to start nominating. #[pallet::call_index(8)] #[pallet::weight(T::WeightInfo::nominate(validators.len() as u32))] pub fn nominate( @@ -2411,6 +2416,16 @@ pub mod pallet { let who = ensure_signed(origin)?; let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_nominate(&who), Error::::NotNominator); + + let depositor_points = PoolMembers::::get(&bonded_pool.roles.depositor) + .ok_or(Error::::PoolMemberNotFound)? + .active_points(); + + ensure!( + bonded_pool.points_to_balance(depositor_points) >= Self::depositor_min_bond(), + Error::::MinimumBondNotMet + ); + T::Staking::nominate(&bonded_pool.bonded_account(), validators) } @@ -2573,17 +2588,37 @@ pub mod pallet { /// Chill on behalf of the pool. /// - /// The dispatch origin of this call must be signed by the pool nominator or the pool + /// The dispatch origin of this call can be signed by the pool nominator or the pool /// root role, same as [`Pallet::nominate`]. /// + /// Under certain conditions, this call can be dispatched permissionlessly (i.e. by any + /// account). + /// + /// # Conditions for a permissionless dispatch: + /// * When pool depositor has less than `MinNominatorBond` staked, otherwise pool members + /// are unable to unbond. + /// + /// # Conditions for permissioned dispatch: + /// * The caller has a nominator or root role of the pool. /// This directly forward the call to the staking pallet, on behalf of the pool bonded /// account. #[pallet::call_index(13)] #[pallet::weight(T::WeightInfo::chill())] pub fn chill(origin: OriginFor, pool_id: PoolId) -> DispatchResult { let who = ensure_signed(origin)?; + let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; - ensure!(bonded_pool.can_nominate(&who), Error::::NotNominator); + + let depositor_points = PoolMembers::::get(&bonded_pool.roles.depositor) + .ok_or(Error::::PoolMemberNotFound)? + .active_points(); + + if bonded_pool.points_to_balance(depositor_points) >= + T::Staking::minimum_nominator_bond() + { + ensure!(bonded_pool.can_nominate(&who), Error::::NotNominator); + } + T::Staking::chill(&bonded_pool.bonded_account()) } @@ -2762,7 +2797,7 @@ pub mod pallet { /// Set or remove a pool's commission claim permission. /// /// Determines who can claim the pool's pending commission. Only the `Root` role of the pool - /// is able to conifigure commission claim permissions. + /// is able to configure commission claim permissions. #[pallet::call_index(22)] #[pallet::weight(T::WeightInfo::set_commission_claim_permission())] pub fn set_commission_claim_permission( @@ -2801,7 +2836,7 @@ pub mod pallet { assert!( T::Staking::bonding_duration() < TotalUnbondingPools::::get(), "There must be more unbonding pools then the bonding duration / - so a slash can be applied to relevant unboding pools. (We assume / + so a slash can be applied to relevant unbonding pools. (We assume / the bonding duration > slash deffer duration.", ); } diff --git a/substrate/frame/nomination-pools/src/migration.rs b/substrate/frame/nomination-pools/src/migration.rs index 6887fcfa7ecad36172c17e36d55713df27865482..ca9c0874a83c45068b475358b6f3e9f5374b19b2 100644 --- a/substrate/frame/nomination-pools/src/migration.rs +++ b/substrate/frame/nomination-pools/src/migration.rs @@ -70,7 +70,7 @@ pub mod unversioned { fn on_runtime_upgrade() -> Weight { let migrated = BondedPools::::count(); - // recalcuate the `TotalValueLocked` to compare with the current on-chain TVL which may + // recalculate the `TotalValueLocked` to compare with the current on-chain TVL which may // be out of sync. let tvl: BalanceOf = helpers::calculate_tvl_by_total_stake::(); let onchain_tvl = TotalValueLocked::::get(); @@ -342,25 +342,25 @@ pub mod v5 { pub struct MigrateToV5(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV5 { fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); + let in_code = Pallet::::in_code_storage_version(); let onchain = Pallet::::on_chain_storage_version(); log!( info, - "Running migration with current storage version {:?} / onchain {:?}", - current, + "Running migration with in-code storage version {:?} / onchain {:?}", + in_code, onchain ); - if current == 5 && onchain == 4 { + if in_code == 5 && onchain == 4 { let mut translated = 0u64; RewardPools::::translate::, _>(|_id, old_value| { translated.saturating_inc(); Some(old_value.migrate_to_v5()) }); - current.put::>(); - log!(info, "Upgraded {} pools, storage to version {:?}", translated, current); + in_code.put::>(); + log!(info, "Upgraded {} pools, storage to version {:?}", translated, in_code); // reads: translated + onchain version. // writes: translated + current.put. @@ -498,12 +498,12 @@ pub mod v4 { #[allow(deprecated)] impl> OnRuntimeUpgrade for MigrateToV4 { fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); + let current = Pallet::::in_code_storage_version(); let onchain = Pallet::::on_chain_storage_version(); log!( info, - "Running migration with current storage version {:?} / onchain {:?}", + "Running migration with in-code storage version {:?} / onchain {:?}", current, onchain ); @@ -579,13 +579,13 @@ pub mod v3 { pub struct MigrateToV3(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV3 { fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); + let current = Pallet::::in_code_storage_version(); let onchain = Pallet::::on_chain_storage_version(); if onchain == 2 { log!( info, - "Running migration with current storage version {:?} / onchain {:?}", + "Running migration with in-code storage version {:?} / onchain {:?}", current, onchain ); @@ -859,12 +859,12 @@ pub mod v2 { impl OnRuntimeUpgrade for MigrateToV2 { fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); + let current = Pallet::::in_code_storage_version(); let onchain = Pallet::::on_chain_storage_version(); log!( info, - "Running migration with current storage version {:?} / onchain {:?}", + "Running migration with in-code storage version {:?} / onchain {:?}", current, onchain ); @@ -976,12 +976,12 @@ pub mod v1 { pub struct MigrateToV1(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); + let current = Pallet::::in_code_storage_version(); let onchain = Pallet::::on_chain_storage_version(); log!( info, - "Running migration with current storage version {:?} / onchain {:?}", + "Running migration with in-code storage version {:?} / onchain {:?}", current, onchain ); diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs index f982b72c63564c0cff6106ded6ad934fec10f48c..686759604c238042c56e007ba2243eed23b0c285 100644 --- a/substrate/frame/nomination-pools/src/mock.rs +++ b/substrate/frame/nomination-pools/src/mock.rs @@ -222,7 +222,7 @@ impl sp_staking::StakingInterface for StakingMock { } } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type SS58Prefix = (); type BaseCallFilter = frame_support::traits::Everything; diff --git a/substrate/frame/nomination-pools/src/tests.rs b/substrate/frame/nomination-pools/src/tests.rs index 13298fa64f54c0439384fcb8fba7b512678ff687..8fb2b41b88a190df444162a58d0a134936d81272 100644 --- a/substrate/frame/nomination-pools/src/tests.rs +++ b/substrate/frame/nomination-pools/src/tests.rs @@ -4848,6 +4848,18 @@ mod nominate { Error::::NotNominator ); + // if `depositor` stake is less than the `MinimumNominatorBond`, they can't nominate + StakingMinBond::set(20); + + // Can't nominate if depositor's stake is less than the `MinimumNominatorBond` + assert_noop!( + Pools::nominate(RuntimeOrigin::signed(900), 1, vec![21]), + Error::::MinimumBondNotMet + ); + + // restore `MinimumNominatorBond` + StakingMinBond::set(10); + // Root can nominate assert_ok!(Pools::nominate(RuntimeOrigin::signed(900), 1, vec![21])); assert_eq!(Nominations::get().unwrap(), vec![21]); @@ -7338,3 +7350,33 @@ mod slash { }); } } + +mod chill { + use super::*; + + #[test] + fn chill_works() { + ExtBuilder::default().build_and_execute(|| { + // only nominator or root can chill + assert_noop!( + Pools::chill(RuntimeOrigin::signed(10), 1), + Error::::NotNominator + ); + + // root can chill and re-nominate + assert_ok!(Pools::chill(RuntimeOrigin::signed(900), 1)); + assert_ok!(Pools::nominate(RuntimeOrigin::signed(900), 1, vec![31])); + + // nominator can chill and re-nominate + assert_ok!(Pools::chill(RuntimeOrigin::signed(901), 1)); + assert_ok!(Pools::nominate(RuntimeOrigin::signed(901), 1, vec![31])); + + // if `depositor` stake is less than the `MinimumNominatorBond`, then this call + // becomes permissionless; + StakingMinBond::set(20); + + // any account can chill + assert_ok!(Pools::chill(RuntimeOrigin::signed(10), 1)); + }) + } +} diff --git a/substrate/frame/nomination-pools/src/weights.rs b/substrate/frame/nomination-pools/src/weights.rs index 047a17c3f9a278ac9564562edf318f13ecc2f7ca..0b8c1d22fa19872711fead9fd7441b26d97e19d6 100644 --- a/substrate/frame/nomination-pools/src/weights.rs +++ b/substrate/frame/nomination-pools/src/weights.rs @@ -17,14 +17,15 @@ //! Autogenerated weights for `pallet_nomination_pools` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-yprdrvc7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-p5qp1txx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: // target/production/substrate-node +// target/production/substrate-node // benchmark // pallet // --steps=50 @@ -35,8 +36,12 @@ // --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json // --pallet=pallet_nomination_pools // --chain=dev +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_nomination_pools +// --chain=dev // --header=./substrate/HEADER-APACHE2 // --output=./substrate/frame/nomination-pools/src/weights.rs +// --output=./substrate/frame/nomination-pools/src/weights.rs // --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -112,8 +117,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3425` // Estimated: `8877` - // Minimum execution time: 184_295_000 picoseconds. - Weight::from_parts(188_860_000, 8877) + // Minimum execution time: 182_643_000 picoseconds. + Weight::from_parts(186_106_000, 8877) .saturating_add(T::DbWeight::get().reads(20_u64)) .saturating_add(T::DbWeight::get().writes(13_u64)) } @@ -145,8 +150,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3435` // Estimated: `8877` - // Minimum execution time: 188_777_000 picoseconds. - Weight::from_parts(192_646_000, 8877) + // Minimum execution time: 181_464_000 picoseconds. + Weight::from_parts(184_672_000, 8877) .saturating_add(T::DbWeight::get().reads(17_u64)) .saturating_add(T::DbWeight::get().writes(13_u64)) } @@ -180,8 +185,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3500` // Estimated: `8877` - // Minimum execution time: 221_728_000 picoseconds. - Weight::from_parts(227_569_000, 8877) + // Minimum execution time: 216_182_000 picoseconds. + Weight::from_parts(218_448_000, 8877) .saturating_add(T::DbWeight::get().reads(18_u64)) .saturating_add(T::DbWeight::get().writes(14_u64)) } @@ -201,8 +206,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1172` // Estimated: `3719` - // Minimum execution time: 75_310_000 picoseconds. - Weight::from_parts(77_709_000, 3719) + // Minimum execution time: 73_830_000 picoseconds. + Weight::from_parts(75_271_000, 3719) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -242,8 +247,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3622` // Estimated: `27847` - // Minimum execution time: 170_656_000 picoseconds. - Weight::from_parts(174_950_000, 27847) + // Minimum execution time: 166_099_000 picoseconds. + Weight::from_parts(170_355_000, 27847) .saturating_add(T::DbWeight::get().reads(20_u64)) .saturating_add(T::DbWeight::get().writes(13_u64)) } @@ -266,11 +271,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1817` // Estimated: `4764` - // Minimum execution time: 68_866_000 picoseconds. - Weight::from_parts(72_312_887, 4764) - // Standard Error: 1_635 - .saturating_add(Weight::from_parts(41_679, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(7_u64)) + // Minimum execution time: 64_787_000 picoseconds. + Weight::from_parts(67_920_914, 4764) + // Standard Error: 1_482 + .saturating_add(Weight::from_parts(43_264, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `NominationPools::PoolMembers` (r:1 w:1) @@ -302,11 +307,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2207` // Estimated: `27847` - // Minimum execution time: 131_383_000 picoseconds. - Weight::from_parts(136_595_971, 27847) - // Standard Error: 2_715 - .saturating_add(Weight::from_parts(52_351, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(11_u64)) + // Minimum execution time: 124_990_000 picoseconds. + Weight::from_parts(129_041_398, 27847) + // Standard Error: 2_335 + .saturating_add(Weight::from_parts(67_889, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(9_u64)) } /// Storage: `NominationPools::PoolMembers` (r:1 w:1) @@ -356,12 +361,14 @@ impl WeightInfo for SubstrateWeight { /// Storage: `NominationPools::ClaimPermissions` (r:0 w:1) /// Proof: `NominationPools::ClaimPermissions` (`max_values`: None, `max_size`: Some(41), added: 2516, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(_s: u32, ) -> Weight { + fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `2525` // Estimated: `27847` - // Minimum execution time: 233_314_000 picoseconds. - Weight::from_parts(241_694_316, 27847) + // Minimum execution time: 221_955_000 picoseconds. + Weight::from_parts(230_244_437, 27847) + // Standard Error: 4_059 + .saturating_add(Weight::from_parts(7_522, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(24_u64)) .saturating_add(T::DbWeight::get().writes(20_u64)) } @@ -413,19 +420,25 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1169` // Estimated: `8538` - // Minimum execution time: 171_465_000 picoseconds. - Weight::from_parts(176_478_000, 8538) + // Minimum execution time: 165_358_000 picoseconds. + Weight::from_parts(169_683_000, 8538) .saturating_add(T::DbWeight::get().reads(23_u64)) .saturating_add(T::DbWeight::get().writes(17_u64)) } /// Storage: `NominationPools::BondedPools` (r:1 w:0) /// Proof: `NominationPools::BondedPools` (`max_values`: None, `max_size`: Some(254), added: 2729, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::PoolMembers` (r:1 w:0) + /// Proof: `NominationPools::PoolMembers` (`max_values`: None, `max_size`: Some(237), added: 2712, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) /// Storage: `Staking::MinNominatorBond` (r:1 w:0) /// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::MinCreateBond` (r:1 w:0) + /// Proof: `NominationPools::MinCreateBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::MinJoinBond` (r:1 w:0) + /// Proof: `NominationPools::MinJoinBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxNominatorsCount` (r:1 w:0) @@ -445,13 +458,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1808` + // Measured: `1976` // Estimated: `4556 + n * (2520 ±0)` - // Minimum execution time: 63_588_000 picoseconds. - Weight::from_parts(64_930_584, 4556) - // Standard Error: 9_167 - .saturating_add(Weight::from_parts(1_595_779, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(12_u64)) + // Minimum execution time: 74_483_000 picoseconds. + Weight::from_parts(76_611_288, 4556) + // Standard Error: 9_013 + .saturating_add(Weight::from_parts(1_475_128, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(15_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(n.into())) @@ -466,8 +479,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1434` // Estimated: `4556` - // Minimum execution time: 32_899_000 picoseconds. - Weight::from_parts(33_955_000, 4556) + // Minimum execution time: 32_598_000 picoseconds. + Weight::from_parts(33_350_000, 4556) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -482,10 +495,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `532` // Estimated: `3735` - // Minimum execution time: 13_778_000 picoseconds. - Weight::from_parts(14_770_006, 3735) - // Standard Error: 151 - .saturating_add(Weight::from_parts(1_900, 0).saturating_mul(n.into())) + // Minimum execution time: 13_705_000 picoseconds. + Weight::from_parts(14_594_211, 3735) + // Standard Error: 141 + .saturating_add(Weight::from_parts(2_119, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -505,8 +518,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_550_000 picoseconds. - Weight::from_parts(4_935_000, 0) + // Minimum execution time: 4_222_000 picoseconds. + Weight::from_parts(4_527_000, 0) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: `NominationPools::BondedPools` (r:1 w:1) @@ -515,17 +528,21 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `532` // Estimated: `3719` - // Minimum execution time: 16_759_000 picoseconds. - Weight::from_parts(17_346_000, 3719) + // Minimum execution time: 16_106_000 picoseconds. + Weight::from_parts(17_365_000, 3719) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `NominationPools::BondedPools` (r:1 w:0) /// Proof: `NominationPools::BondedPools` (`max_values`: None, `max_size`: Some(254), added: 2729, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::PoolMembers` (r:1 w:0) + /// Proof: `NominationPools::PoolMembers` (`max_values`: None, `max_size`: Some(237), added: 2712, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::MinNominatorBond` (r:1 w:0) + /// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::Validators` (r:1 w:0) /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) @@ -540,11 +557,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn chill() -> Weight { // Proof Size summary in bytes: - // Measured: `1971` + // Measured: `2143` // Estimated: `4556` - // Minimum execution time: 61_970_000 picoseconds. - Weight::from_parts(63_738_000, 4556) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Minimum execution time: 71_301_000 picoseconds. + Weight::from_parts(73_626_000, 4556) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } /// Storage: `NominationPools::BondedPools` (r:1 w:1) @@ -559,8 +576,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `804` // Estimated: `3719` - // Minimum execution time: 31_950_000 picoseconds. - Weight::from_parts(33_190_000, 3719) + // Minimum execution time: 32_363_000 picoseconds. + Weight::from_parts(33_400_000, 3719) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -572,8 +589,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `572` // Estimated: `3719` - // Minimum execution time: 16_807_000 picoseconds. - Weight::from_parts(17_733_000, 3719) + // Minimum execution time: 16_686_000 picoseconds. + Weight::from_parts(17_294_000, 3719) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -583,8 +600,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `532` // Estimated: `3719` - // Minimum execution time: 16_710_000 picoseconds. - Weight::from_parts(17_563_000, 3719) + // Minimum execution time: 16_417_000 picoseconds. + Weight::from_parts(17_028_000, 3719) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -594,8 +611,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `532` // Estimated: `3719` - // Minimum execution time: 16_493_000 picoseconds. - Weight::from_parts(17_022_000, 3719) + // Minimum execution time: 16_350_000 picoseconds. + Weight::from_parts(16_905_000, 3719) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -607,8 +624,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `542` // Estimated: `3702` - // Minimum execution time: 14_248_000 picoseconds. - Weight::from_parts(15_095_000, 3702) + // Minimum execution time: 14_081_000 picoseconds. + Weight::from_parts(14_608_000, 3702) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -624,8 +641,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1002` // Estimated: `3719` - // Minimum execution time: 61_969_000 picoseconds. - Weight::from_parts(63_965_000, 3719) + // Minimum execution time: 62_178_000 picoseconds. + Weight::from_parts(64_039_000, 3719) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -641,8 +658,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `901` // Estimated: `4764` - // Minimum execution time: 65_462_000 picoseconds. - Weight::from_parts(67_250_000, 4764) + // Minimum execution time: 64_880_000 picoseconds. + Weight::from_parts(66_541_000, 4764) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -686,8 +703,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3425` // Estimated: `8877` - // Minimum execution time: 184_295_000 picoseconds. - Weight::from_parts(188_860_000, 8877) + // Minimum execution time: 182_643_000 picoseconds. + Weight::from_parts(186_106_000, 8877) .saturating_add(RocksDbWeight::get().reads(20_u64)) .saturating_add(RocksDbWeight::get().writes(13_u64)) } @@ -719,8 +736,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3435` // Estimated: `8877` - // Minimum execution time: 188_777_000 picoseconds. - Weight::from_parts(192_646_000, 8877) + // Minimum execution time: 181_464_000 picoseconds. + Weight::from_parts(184_672_000, 8877) .saturating_add(RocksDbWeight::get().reads(17_u64)) .saturating_add(RocksDbWeight::get().writes(13_u64)) } @@ -754,8 +771,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3500` // Estimated: `8877` - // Minimum execution time: 221_728_000 picoseconds. - Weight::from_parts(227_569_000, 8877) + // Minimum execution time: 216_182_000 picoseconds. + Weight::from_parts(218_448_000, 8877) .saturating_add(RocksDbWeight::get().reads(18_u64)) .saturating_add(RocksDbWeight::get().writes(14_u64)) } @@ -775,8 +792,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1172` // Estimated: `3719` - // Minimum execution time: 75_310_000 picoseconds. - Weight::from_parts(77_709_000, 3719) + // Minimum execution time: 73_830_000 picoseconds. + Weight::from_parts(75_271_000, 3719) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -816,8 +833,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3622` // Estimated: `27847` - // Minimum execution time: 170_656_000 picoseconds. - Weight::from_parts(174_950_000, 27847) + // Minimum execution time: 166_099_000 picoseconds. + Weight::from_parts(170_355_000, 27847) .saturating_add(RocksDbWeight::get().reads(20_u64)) .saturating_add(RocksDbWeight::get().writes(13_u64)) } @@ -840,11 +857,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1817` // Estimated: `4764` - // Minimum execution time: 68_866_000 picoseconds. - Weight::from_parts(72_312_887, 4764) - // Standard Error: 1_635 - .saturating_add(Weight::from_parts(41_679, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(7_u64)) + // Minimum execution time: 64_787_000 picoseconds. + Weight::from_parts(67_920_914, 4764) + // Standard Error: 1_482 + .saturating_add(Weight::from_parts(43_264, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `NominationPools::PoolMembers` (r:1 w:1) @@ -876,11 +893,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2207` // Estimated: `27847` - // Minimum execution time: 131_383_000 picoseconds. - Weight::from_parts(136_595_971, 27847) - // Standard Error: 2_715 - .saturating_add(Weight::from_parts(52_351, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(11_u64)) + // Minimum execution time: 124_990_000 picoseconds. + Weight::from_parts(129_041_398, 27847) + // Standard Error: 2_335 + .saturating_add(Weight::from_parts(67_889, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(9_u64)) } /// Storage: `NominationPools::PoolMembers` (r:1 w:1) @@ -930,12 +947,14 @@ impl WeightInfo for () { /// Storage: `NominationPools::ClaimPermissions` (r:0 w:1) /// Proof: `NominationPools::ClaimPermissions` (`max_values`: None, `max_size`: Some(41), added: 2516, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(_s: u32, ) -> Weight { + fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `2525` // Estimated: `27847` - // Minimum execution time: 233_314_000 picoseconds. - Weight::from_parts(241_694_316, 27847) + // Minimum execution time: 221_955_000 picoseconds. + Weight::from_parts(230_244_437, 27847) + // Standard Error: 4_059 + .saturating_add(Weight::from_parts(7_522, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(24_u64)) .saturating_add(RocksDbWeight::get().writes(20_u64)) } @@ -987,19 +1006,25 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1169` // Estimated: `8538` - // Minimum execution time: 171_465_000 picoseconds. - Weight::from_parts(176_478_000, 8538) + // Minimum execution time: 165_358_000 picoseconds. + Weight::from_parts(169_683_000, 8538) .saturating_add(RocksDbWeight::get().reads(23_u64)) .saturating_add(RocksDbWeight::get().writes(17_u64)) } /// Storage: `NominationPools::BondedPools` (r:1 w:0) /// Proof: `NominationPools::BondedPools` (`max_values`: None, `max_size`: Some(254), added: 2729, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::PoolMembers` (r:1 w:0) + /// Proof: `NominationPools::PoolMembers` (`max_values`: None, `max_size`: Some(237), added: 2712, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) /// Storage: `Staking::MinNominatorBond` (r:1 w:0) /// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::MinCreateBond` (r:1 w:0) + /// Proof: `NominationPools::MinCreateBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::MinJoinBond` (r:1 w:0) + /// Proof: `NominationPools::MinJoinBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxNominatorsCount` (r:1 w:0) @@ -1019,13 +1044,13 @@ impl WeightInfo for () { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1808` + // Measured: `1976` // Estimated: `4556 + n * (2520 ±0)` - // Minimum execution time: 63_588_000 picoseconds. - Weight::from_parts(64_930_584, 4556) - // Standard Error: 9_167 - .saturating_add(Weight::from_parts(1_595_779, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(12_u64)) + // Minimum execution time: 74_483_000 picoseconds. + Weight::from_parts(76_611_288, 4556) + // Standard Error: 9_013 + .saturating_add(Weight::from_parts(1_475_128, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(15_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(n.into())) @@ -1040,8 +1065,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1434` // Estimated: `4556` - // Minimum execution time: 32_899_000 picoseconds. - Weight::from_parts(33_955_000, 4556) + // Minimum execution time: 32_598_000 picoseconds. + Weight::from_parts(33_350_000, 4556) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1056,10 +1081,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `532` // Estimated: `3735` - // Minimum execution time: 13_778_000 picoseconds. - Weight::from_parts(14_770_006, 3735) - // Standard Error: 151 - .saturating_add(Weight::from_parts(1_900, 0).saturating_mul(n.into())) + // Minimum execution time: 13_705_000 picoseconds. + Weight::from_parts(14_594_211, 3735) + // Standard Error: 141 + .saturating_add(Weight::from_parts(2_119, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1079,8 +1104,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_550_000 picoseconds. - Weight::from_parts(4_935_000, 0) + // Minimum execution time: 4_222_000 picoseconds. + Weight::from_parts(4_527_000, 0) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: `NominationPools::BondedPools` (r:1 w:1) @@ -1089,17 +1114,21 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `532` // Estimated: `3719` - // Minimum execution time: 16_759_000 picoseconds. - Weight::from_parts(17_346_000, 3719) + // Minimum execution time: 16_106_000 picoseconds. + Weight::from_parts(17_365_000, 3719) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `NominationPools::BondedPools` (r:1 w:0) /// Proof: `NominationPools::BondedPools` (`max_values`: None, `max_size`: Some(254), added: 2729, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::PoolMembers` (r:1 w:0) + /// Proof: `NominationPools::PoolMembers` (`max_values`: None, `max_size`: Some(237), added: 2712, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::MinNominatorBond` (r:1 w:0) + /// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::Validators` (r:1 w:0) /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) @@ -1114,11 +1143,11 @@ impl WeightInfo for () { /// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn chill() -> Weight { // Proof Size summary in bytes: - // Measured: `1971` + // Measured: `2143` // Estimated: `4556` - // Minimum execution time: 61_970_000 picoseconds. - Weight::from_parts(63_738_000, 4556) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Minimum execution time: 71_301_000 picoseconds. + Weight::from_parts(73_626_000, 4556) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } /// Storage: `NominationPools::BondedPools` (r:1 w:1) @@ -1133,8 +1162,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `804` // Estimated: `3719` - // Minimum execution time: 31_950_000 picoseconds. - Weight::from_parts(33_190_000, 3719) + // Minimum execution time: 32_363_000 picoseconds. + Weight::from_parts(33_400_000, 3719) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1146,8 +1175,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `572` // Estimated: `3719` - // Minimum execution time: 16_807_000 picoseconds. - Weight::from_parts(17_733_000, 3719) + // Minimum execution time: 16_686_000 picoseconds. + Weight::from_parts(17_294_000, 3719) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1157,8 +1186,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `532` // Estimated: `3719` - // Minimum execution time: 16_710_000 picoseconds. - Weight::from_parts(17_563_000, 3719) + // Minimum execution time: 16_417_000 picoseconds. + Weight::from_parts(17_028_000, 3719) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1168,8 +1197,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `532` // Estimated: `3719` - // Minimum execution time: 16_493_000 picoseconds. - Weight::from_parts(17_022_000, 3719) + // Minimum execution time: 16_350_000 picoseconds. + Weight::from_parts(16_905_000, 3719) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1181,8 +1210,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `542` // Estimated: `3702` - // Minimum execution time: 14_248_000 picoseconds. - Weight::from_parts(15_095_000, 3702) + // Minimum execution time: 14_081_000 picoseconds. + Weight::from_parts(14_608_000, 3702) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1198,8 +1227,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1002` // Estimated: `3719` - // Minimum execution time: 61_969_000 picoseconds. - Weight::from_parts(63_965_000, 3719) + // Minimum execution time: 62_178_000 picoseconds. + Weight::from_parts(64_039_000, 3719) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1215,8 +1244,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `901` // Estimated: `4764` - // Minimum execution time: 65_462_000 picoseconds. - Weight::from_parts(67_250_000, 4764) + // Minimum execution time: 64_880_000 picoseconds. + Weight::from_parts(66_541_000, 4764) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/substrate/frame/nomination-pools/test-staking/src/lib.rs b/substrate/frame/nomination-pools/test-staking/src/lib.rs index 865b7a71e68840d00b42c7cf11e6916c8debf5ad..98bff537c90819be34625d099cb55abcaafb10ae 100644 --- a/substrate/frame/nomination-pools/test-staking/src/lib.rs +++ b/substrate/frame/nomination-pools/test-staking/src/lib.rs @@ -22,10 +22,12 @@ mod mock; use frame_support::{assert_noop, assert_ok, traits::Currency}; use mock::*; use pallet_nomination_pools::{ - BondedPools, Error as PoolsError, Event as PoolsEvent, LastPoolId, PoolMember, PoolMembers, - PoolState, + BondExtra, BondedPools, Error as PoolsError, Event as PoolsEvent, LastPoolId, PoolMember, + PoolMembers, PoolState, +}; +use pallet_staking::{ + CurrentEra, Error as StakingError, Event as StakingEvent, Payee, RewardDestination, }; -use pallet_staking::{CurrentEra, Event as StakingEvent, Payee, RewardDestination}; use sp_runtime::{bounded_btree_map, traits::Zero}; #[test] @@ -191,6 +193,131 @@ fn pool_lifecycle_e2e() { }) } +#[test] +fn pool_chill_e2e() { + new_test_ext().execute_with(|| { + assert_eq!(Balances::minimum_balance(), 5); + assert_eq!(Staking::current_era(), None); + + // create the pool, we know this has id 1. + assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10)); + assert_eq!(LastPoolId::::get(), 1); + + // have the pool nominate. + assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3])); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 50 }] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Created { depositor: 10, pool_id: 1 }, + PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 50, joined: true }, + ] + ); + + // have two members join + assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); + assert_ok!(Pools::join(RuntimeOrigin::signed(21), 10, 1)); + + assert_eq!( + staking_events_since_last_call(), + vec![ + StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, + StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, + ] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, + PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: 10, joined: true }, + ] + ); + + // in case depositor does not have more than `MinNominatorBond` staked, we can end up in + // situation where a member unbonding would cause pool balance to drop below + // `MinNominatorBond` and hence not allowed. This can happen if the `MinNominatorBond` is + // increased after the pool is created. + assert_ok!(Staking::set_staking_configs( + RuntimeOrigin::root(), + pallet_staking::ConfigOp::Set(55), // minimum nominator bond + pallet_staking::ConfigOp::Noop, + pallet_staking::ConfigOp::Noop, + pallet_staking::ConfigOp::Noop, + pallet_staking::ConfigOp::Noop, + pallet_staking::ConfigOp::Noop, + pallet_staking::ConfigOp::Noop, + )); + + // members can unbond as long as total stake of the pool is above min nominator bond + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10),); + assert_eq!(PoolMembers::::get(20).unwrap().unbonding_eras.len(), 1); + assert_eq!(PoolMembers::::get(20).unwrap().points, 0); + + // this member cannot unbond since it will cause `pool stake < MinNominatorBond` + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(21), 21, 10), + StakingError::::InsufficientBond, + ); + + // members can call `chill` permissionlessly now + assert_ok!(Pools::chill(RuntimeOrigin::signed(20), 1)); + + // now another member can unbond. + assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, 10)); + assert_eq!(PoolMembers::::get(21).unwrap().unbonding_eras.len(), 1); + assert_eq!(PoolMembers::::get(21).unwrap().points, 0); + + // nominator can not resume nomination until depositor have enough stake + assert_noop!( + Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]), + PoolsError::::MinimumBondNotMet, + ); + + // other members joining pool does not affect the depositor's ability to resume nomination + assert_ok!(Pools::join(RuntimeOrigin::signed(22), 10, 1)); + + assert_noop!( + Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]), + PoolsError::::MinimumBondNotMet, + ); + + // depositor can bond extra stake + assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10))); + + // `chill` can not be called permissionlessly anymore + assert_noop!( + Pools::chill(RuntimeOrigin::signed(20), 1), + PoolsError::::NotNominator, + ); + + // now nominator can resume nomination + assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3])); + + // skip to make the unbonding period end. + CurrentEra::::set(Some(BondingDuration::get())); + + // members can now withdraw. + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0)); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(21), 21, 0)); + + assert_eq!( + staking_events_since_last_call(), + vec![ + StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, + StakingEvent::Chilled { stash: POOL1_BONDED }, + StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, + StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, // other member bonding + StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, // depositor bond extra + StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 20 }, + ] + ); + }) +} + #[test] fn pool_slash_e2e() { new_test_ext().execute_with(|| { diff --git a/substrate/frame/nomination-pools/test-staking/src/mock.rs b/substrate/frame/nomination-pools/test-staking/src/mock.rs index ce97e13d640b63ed7da25ad36f8d2625e58d3c50..22939ff5e23862141f2bd557e9ff96ae140b5b50 100644 --- a/substrate/frame/nomination-pools/test-staking/src/mock.rs +++ b/substrate/frame/nomination-pools/test-staking/src/mock.rs @@ -38,7 +38,7 @@ pub(crate) type T = Runtime; pub(crate) const POOL1_BONDED: AccountId = 20318131474730217858575332831085u128; pub(crate) const POOL1_REWARD: AccountId = 20397359637244482196168876781421u128; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/frame/offences/benchmarking/src/mock.rs b/substrate/frame/offences/benchmarking/src/mock.rs index 01ad8d64f100672dc36929d28af22e41f8a88c60..ea2e9e93ed68aabf1b30de0a1cbe5a235acdbcad 100644 --- a/substrate/frame/offences/benchmarking/src/mock.rs +++ b/substrate/frame/offences/benchmarking/src/mock.rs @@ -40,7 +40,7 @@ type AccountId = u64; type Nonce = u32; type Balance = u64; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/frame/offences/src/mock.rs b/substrate/frame/offences/src/mock.rs index d97be99def1cccb3a0dbd5762b552d20000034d2..31d5f805f3e4894407cd245db8bd7856d73c57cc 100644 --- a/substrate/frame/offences/src/mock.rs +++ b/substrate/frame/offences/src/mock.rs @@ -74,7 +74,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/frame/offences/src/tests.rs b/substrate/frame/offences/src/tests.rs index d525c7c3ab1d920e07273f26168d3711517e5827..4897b78f3e4d37ccabaad6a5fa1b3c6acbac45b2 100644 --- a/substrate/frame/offences/src/tests.rs +++ b/substrate/frame/offences/src/tests.rs @@ -204,7 +204,7 @@ fn reports_if_an_offence_is_dup() { assert_eq!(Offences::report_offence(vec![], test_offence.clone()), Ok(())); // creating a new offence for the same authorities on the next slot - // should be considered a new offence and thefore not known + // should be considered a new offence and therefore not known let test_offence_next_slot = offence(time_slot + 1, vec![0, 1]); assert!(!>::is_known_offence( &test_offence_next_slot.offenders, diff --git a/substrate/frame/paged-list/src/mock.rs b/substrate/frame/paged-list/src/mock.rs index 37bdc4f157cb9861eb0e387f7b0954c4068fa13d..5d06170aae7f991ae1e0b77c530272cbd5ef14dd 100644 --- a/substrate/frame/paged-list/src/mock.rs +++ b/substrate/frame/paged-list/src/mock.rs @@ -41,7 +41,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/frame/paged-list/src/paged_list.rs b/substrate/frame/paged-list/src/paged_list.rs index 75467f3ceeb582e04c8e02eb7629637358a760e1..eecc728cd62a1dcd29c4db1d48a642102421be9c 100644 --- a/substrate/frame/paged-list/src/paged_list.rs +++ b/substrate/frame/paged-list/src/paged_list.rs @@ -190,7 +190,7 @@ impl Page { let values = sp_io::storage::get(&key) .and_then(|raw| sp_std::vec::Vec::::decode(&mut &raw[..]).ok())?; if values.is_empty() { - // Dont create empty pages. + // Don't create empty pages. return None } let values = values.into_iter().skip(value_index as usize); diff --git a/substrate/frame/parameters/src/lib.rs b/substrate/frame/parameters/src/lib.rs index 91cf10ba93f7f9ebf03363cfe3bf8a9b4ff89172..55a6f1ff91ded0222e2596c4ded133a5a5bc0719 100644 --- a/substrate/frame/parameters/src/lib.rs +++ b/substrate/frame/parameters/src/lib.rs @@ -122,7 +122,7 @@ use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use frame_support::traits::{ - dynamic_params::{AggregratedKeyValue, IntoKey, Key, RuntimeParameterStore, TryIntoKey}, + dynamic_params::{AggregatedKeyValue, IntoKey, Key, RuntimeParameterStore, TryIntoKey}, EnsureOriginWithArg, }; @@ -135,10 +135,10 @@ pub use pallet::*; pub use weights::WeightInfo; /// The key type of a parameter. -type KeyOf = <::RuntimeParameters as AggregratedKeyValue>::Key; +type KeyOf = <::RuntimeParameters as AggregatedKeyValue>::Key; /// The value type of a parameter. -type ValueOf = <::RuntimeParameters as AggregratedKeyValue>::Value; +type ValueOf = <::RuntimeParameters as AggregatedKeyValue>::Value; #[frame_support::pallet] pub mod pallet { @@ -154,7 +154,7 @@ pub mod pallet { /// /// Usually created by [`frame_support::dynamic_params`] or equivalent. #[pallet::no_default_bounds] - type RuntimeParameters: AggregratedKeyValue; + type RuntimeParameters: AggregatedKeyValue; /// The origin which may update a parameter. /// @@ -175,11 +175,11 @@ pub mod pallet { /// Is also emitted when the value was not changed. Updated { /// The key that was updated. - key: ::Key, + key: ::Key, /// The old value before this call. - old_value: Option<::Value>, + old_value: Option<::Value>, /// The new value after this call. - new_value: Option<::Value>, + new_value: Option<::Value>, }, } @@ -225,7 +225,7 @@ pub mod pallet { /// A configuration for testing. pub struct TestDefaultConfig; - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig, no_aggregated_types)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)] impl frame_system::DefaultConfig for TestDefaultConfig {} #[frame_support::register_default_impl(TestDefaultConfig)] @@ -245,23 +245,23 @@ pub mod pallet { } impl RuntimeParameterStore for Pallet { - type AggregratedKeyValue = T::RuntimeParameters; + type AggregatedKeyValue = T::RuntimeParameters; fn get(key: K) -> Option where - KV: AggregratedKeyValue, - K: Key + Into<::Key>, - ::Key: IntoKey< - <::AggregratedKeyValue as AggregratedKeyValue>::Key, + KV: AggregatedKeyValue, + K: Key + Into<::Key>, + ::Key: IntoKey< + <::AggregatedKeyValue as AggregatedKeyValue>::Key, >, - <::AggregratedKeyValue as AggregratedKeyValue>::Value: - TryIntoKey<::Value>, - ::Value: TryInto, + <::AggregatedKeyValue as AggregatedKeyValue>::Value: + TryIntoKey<::Value>, + ::Value: TryInto, { - let key: ::Key = key.into(); + let key: ::Key = key.into(); let val = Parameters::::get(key.into_key()); val.and_then(|v| { - let val: ::Value = v.try_into_key().ok()?; + let val: ::Value = v.try_into_key().ok()?; let val: K::WrappedValue = val.try_into().ok()?; let val = val.into(); Some(val) diff --git a/substrate/frame/parameters/src/tests/mock.rs b/substrate/frame/parameters/src/tests/mock.rs index 98612dc6a6d959c91b78570f562bce9074c43f61..4c7dda639a9ae762c94b355174e7e4ca59869afe 100644 --- a/substrate/frame/parameters/src/tests/mock.rs +++ b/substrate/frame/parameters/src/tests/mock.rs @@ -28,13 +28,13 @@ use frame_support::{ use crate as pallet_parameters; use crate::*; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type Block = frame_system::mocking::MockBlock; type AccountData = pallet_balances::AccountData<::Balance>; } -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for Runtime { type ReserveIdentifier = [u8; 8]; type AccountStore = System; @@ -112,7 +112,7 @@ mod custom_origin { } #[docify::export(impl_config)] -#[derive_impl(pallet_parameters::config_preludes::TestDefaultConfig as pallet_parameters::DefaultConfig)] +#[derive_impl(pallet_parameters::config_preludes::TestDefaultConfig)] impl Config for Runtime { type AdminOrigin = custom_origin::ParamsManager; // RuntimeParameters is injected by the `derive_impl` macro. diff --git a/substrate/frame/parameters/src/tests/test_renamed.rs b/substrate/frame/parameters/src/tests/test_renamed.rs index b2e0c1fd9661b4122663a4633e1d4500ac6fad1c..cfc870fbe1096aeeab930d548e6455427962d62a 100644 --- a/substrate/frame/parameters/src/tests/test_renamed.rs +++ b/substrate/frame/parameters/src/tests/test_renamed.rs @@ -31,13 +31,13 @@ use crate::*; use dynamic_params::*; use RuntimeParametersRenamed::*; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type Block = frame_system::mocking::MockBlock; type AccountData = pallet_balances::AccountData<::Balance>; } -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for Runtime { type ReserveIdentifier = [u8; 8]; type AccountStore = System; @@ -80,7 +80,7 @@ impl Default for RuntimeParametersRenamed { } } -#[derive_impl(pallet_parameters::config_preludes::TestDefaultConfig as pallet_parameters::DefaultConfig)] +#[derive_impl(pallet_parameters::config_preludes::TestDefaultConfig)] impl Config for Runtime { type AdminOrigin = AsEnsureOriginWithArg>; type RuntimeParameters = RuntimeParametersRenamed; diff --git a/substrate/frame/parameters/src/tests/unit.rs b/substrate/frame/parameters/src/tests/unit.rs index d3f11ba96403514fbe77ba89f57872e85cbf655a..d811a83564657d75759f652a849402f2b912422f 100644 --- a/substrate/frame/parameters/src/tests/unit.rs +++ b/substrate/frame/parameters/src/tests/unit.rs @@ -25,7 +25,7 @@ use crate::tests::mock::{ RuntimeParametersValue, }; use codec::Encode; -use frame_support::{assert_noop, assert_ok, traits::dynamic_params::AggregratedKeyValue}; +use frame_support::{assert_noop, assert_ok, traits::dynamic_params::AggregatedKeyValue}; use sp_core::Get; use sp_runtime::DispatchError; diff --git a/substrate/frame/preimage/src/lib.rs b/substrate/frame/preimage/src/lib.rs index e344bdfe2d8feff5a25ed686d7301f462fa41189..4e474685166631ba41eed644d3754a332a290663 100644 --- a/substrate/frame/preimage/src/lib.rs +++ b/substrate/frame/preimage/src/lib.rs @@ -102,7 +102,7 @@ pub const MAX_HASH_UPGRADE_BULK_COUNT: u32 = 1024; pub mod pallet { use super::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::config] diff --git a/substrate/frame/preimage/src/mock.rs b/substrate/frame/preimage/src/mock.rs index a43e8347d76bf219c2f2fe3a36e4505f1d48c4a0..903c34596aebacfd7116f1b172d54f9ed26f6426 100644 --- a/substrate/frame/preimage/src/mock.rs +++ b/substrate/frame/preimage/src/mock.rs @@ -42,7 +42,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; diff --git a/substrate/frame/proxy/src/lib.rs b/substrate/frame/proxy/src/lib.rs index 4d4da0433afa015a6c33e8ee68e1e1c90851ce97..2b3fac5f59e4b1fd48f92bffc314cdbad5ec9c22 100644 --- a/substrate/frame/proxy/src/lib.rs +++ b/substrate/frame/proxy/src/lib.rs @@ -119,7 +119,7 @@ pub mod pallet { /// The currency mechanism. type Currency: ReservableCurrency; - /// A kind of proxy; specified with the proxy and passed in to the `IsProxyable` fitler. + /// A kind of proxy; specified with the proxy and passed in to the `IsProxyable` filter. /// The instance filter determines whether a given call may be proxied under this type. /// /// IMPORTANT: `Default` must be provided and MUST BE the the *most permissive* value. diff --git a/substrate/frame/proxy/src/tests.rs b/substrate/frame/proxy/src/tests.rs index 67a662e01b2bf4345b8fff1501a63b0f7ed68e99..3ed61fbedaaa93ca3bbf9976a7303195537ae671 100644 --- a/substrate/frame/proxy/src/tests.rs +++ b/substrate/frame/proxy/src/tests.rs @@ -42,14 +42,14 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type BaseCallFilter = BaseFilter; type AccountData = pallet_balances::AccountData; } -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for Test { type ReserveIdentifier = [u8; 8]; type AccountStore = System; diff --git a/substrate/frame/ranked-collective/src/tests.rs b/substrate/frame/ranked-collective/src/tests.rs index 31add52d90afa9c8756be88b425a08b453b60892..ad8b7d2a8018bb0566f9b851cf64433791a1c1ce 100644 --- a/substrate/frame/ranked-collective/src/tests.rs +++ b/substrate/frame/ranked-collective/src/tests.rs @@ -45,7 +45,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } diff --git a/substrate/frame/recovery/src/mock.rs b/substrate/frame/recovery/src/mock.rs index 89374527e069c4eac7eac1173502768d1cef684d..bec7e02c128bbfc2c9975d881ac5210cd31aa56f 100644 --- a/substrate/frame/recovery/src/mock.rs +++ b/substrate/frame/recovery/src/mock.rs @@ -37,7 +37,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; diff --git a/substrate/frame/referenda/src/lib.rs b/substrate/frame/referenda/src/lib.rs index c5bf2266e672542b77c79bee71176f8e526018d4..e616056c3022abe796cd494e91fa4e9565843e60 100644 --- a/substrate/frame/referenda/src/lib.rs +++ b/substrate/frame/referenda/src/lib.rs @@ -143,7 +143,7 @@ pub mod pallet { use frame_support::{pallet_prelude::*, traits::EnsureOriginWithArg}; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] diff --git a/substrate/frame/referenda/src/migration.rs b/substrate/frame/referenda/src/migration.rs index a80897242eec67aad194f8deaf1cd6527533eab9..631eb7340e567e8115e1755d513980fb148700c5 100644 --- a/substrate/frame/referenda/src/migration.rs +++ b/substrate/frame/referenda/src/migration.rs @@ -109,16 +109,16 @@ pub mod v1 { } fn on_runtime_upgrade() -> Weight { - let current_version = Pallet::::current_storage_version(); - let onchain_version = Pallet::::on_chain_storage_version(); + let in_code_version = Pallet::::in_code_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); let mut weight = T::DbWeight::get().reads(1); log::info!( target: TARGET, - "running migration with current storage version {:?} / onchain {:?}.", - current_version, - onchain_version + "running migration with in-code storage version {:?} / onchain {:?}.", + in_code_version, + on_chain_version ); - if onchain_version != 0 { + if on_chain_version != 0 { log::warn!(target: TARGET, "skipping migration from v0 to v1."); return weight } @@ -149,8 +149,8 @@ pub mod v1 { #[cfg(feature = "try-runtime")] fn post_upgrade(state: Vec) -> Result<(), TryRuntimeError> { - let onchain_version = Pallet::::on_chain_storage_version(); - ensure!(onchain_version == 1, "must upgrade from version 0 to 1."); + let on_chain_version = Pallet::::on_chain_storage_version(); + ensure!(on_chain_version == 1, "must upgrade from version 0 to 1."); let pre_referendum_count: u32 = Decode::decode(&mut &state[..]) .expect("failed to decode the state from pre-upgrade."); let post_referendum_count = ReferendumInfoFor::::iter().count() as u32; diff --git a/substrate/frame/referenda/src/mock.rs b/substrate/frame/referenda/src/mock.rs index bfafc107c28bc4ba422d6409b861f60a6c1d26d9..135476d7cb1373ea1e7d3164e9df9d5b9dfc36ee 100644 --- a/substrate/frame/referenda/src/mock.rs +++ b/substrate/frame/referenda/src/mock.rs @@ -58,7 +58,7 @@ impl Contains for BaseFilter { parameter_types! { pub MaxWeight: Weight = Weight::from_parts(2_000_000_000_000, u64::MAX); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = BaseFilter; type Block = Block; diff --git a/substrate/frame/remark/src/mock.rs b/substrate/frame/remark/src/mock.rs index d89583513b60d16a79aa076fb963fdc0e1459b76..3eaac4ab43964cd16c1cd4849648a2192b23d8f8 100644 --- a/substrate/frame/remark/src/mock.rs +++ b/substrate/frame/remark/src/mock.rs @@ -32,7 +32,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } diff --git a/substrate/frame/root-offences/src/mock.rs b/substrate/frame/root-offences/src/mock.rs index 1f7cce27769f17b9965774300070d62e78bcaabd..626db138c2bf955b5d8b6b018f28f7e01acd753a 100644 --- a/substrate/frame/root-offences/src/mock.rs +++ b/substrate/frame/root-offences/src/mock.rs @@ -78,7 +78,7 @@ impl sp_runtime::BoundToRuntimeAppPublic for OtherSessionHandler { type Public = UintAuthorityId; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; diff --git a/substrate/frame/root-testing/src/lib.rs b/substrate/frame/root-testing/src/lib.rs index 51fd835409aef4bfe58f8f97c4df88e130c965a5..98e1f5c5b668d04f7638087bda9be9d81bc12484 100644 --- a/substrate/frame/root-testing/src/lib.rs +++ b/substrate/frame/root-testing/src/lib.rs @@ -17,7 +17,7 @@ //! # Root Testing Pallet //! -//! Pallet that contains extrinsics that can be usefull in testing. +//! Pallet that contains extrinsics that can be useful in testing. //! //! NOTE: This pallet should only be used for testing purposes and should not be used in production //! runtimes! diff --git a/substrate/frame/safe-mode/src/mock.rs b/substrate/frame/safe-mode/src/mock.rs index b4d7a624ea21751fe0dc88d7059ccecc130036b4..fbfc16f4aa2881b25db9b27c3be3c53973a925ab 100644 --- a/substrate/frame/safe-mode/src/mock.rs +++ b/substrate/frame/safe-mode/src/mock.rs @@ -33,7 +33,7 @@ use sp_runtime::{ BuildStorage, }; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = InsideBoth; type BlockWeights = (); diff --git a/substrate/frame/salary/src/tests/integration.rs b/substrate/frame/salary/src/tests/integration.rs index a49b5637b8ae42b6618fe98b12ceba77f35fe89b..124ab38c5651b1e3c57d0457952eda8cd5b92e98 100644 --- a/substrate/frame/salary/src/tests/integration.rs +++ b/substrate/frame/salary/src/tests/integration.rs @@ -50,7 +50,7 @@ parameter_types! { frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1_000_000, 0)); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } @@ -250,7 +250,7 @@ fn swap_exhaustive_works() { }); assert_eq!(root_add, root_swap); - // Ensure that we dont compare trivial stuff like `()` from a type error above. + // Ensure that we don't compare trivial stuff like `()` from a type error above. assert_eq!(root_add.len(), 32); }); } diff --git a/substrate/frame/salary/src/tests/unit.rs b/substrate/frame/salary/src/tests/unit.rs index b3fd00ec76b9b018a8949e5dc6a36385d99ad8d5..db1c8b947ef57c7c1919568f5bf0545f0449994d 100644 --- a/substrate/frame/salary/src/tests/unit.rs +++ b/substrate/frame/salary/src/tests/unit.rs @@ -46,7 +46,7 @@ parameter_types! { frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1_000_000, 0)); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } @@ -508,7 +508,7 @@ fn zero_payment_fails() { } #[test] -fn unregistered_bankrupcy_fails_gracefully() { +fn unregistered_bankruptcy_fails_gracefully() { new_test_ext().execute_with(|| { assert_ok!(Salary::init(RuntimeOrigin::signed(1))); set_rank(1, 2); @@ -532,7 +532,7 @@ fn unregistered_bankrupcy_fails_gracefully() { } #[test] -fn registered_bankrupcy_fails_gracefully() { +fn registered_bankruptcy_fails_gracefully() { new_test_ext().execute_with(|| { assert_ok!(Salary::init(RuntimeOrigin::signed(1))); set_rank(1, 2); @@ -561,7 +561,7 @@ fn registered_bankrupcy_fails_gracefully() { } #[test] -fn mixed_bankrupcy_fails_gracefully() { +fn mixed_bankruptcy_fails_gracefully() { new_test_ext().execute_with(|| { assert_ok!(Salary::init(RuntimeOrigin::signed(1))); set_rank(1, 2); @@ -589,7 +589,7 @@ fn mixed_bankrupcy_fails_gracefully() { } #[test] -fn other_mixed_bankrupcy_fails_gracefully() { +fn other_mixed_bankruptcy_fails_gracefully() { new_test_ext().execute_with(|| { assert_ok!(Salary::init(RuntimeOrigin::signed(1))); set_rank(1, 2); diff --git a/substrate/frame/sassafras/src/benchmarking.rs b/substrate/frame/sassafras/src/benchmarking.rs index 921f2f0793d3ce9072aa762deeb97ad7b00799d5..2b2467c6f84ddf431425605834a7f6b006795a95 100644 --- a/substrate/frame/sassafras/src/benchmarking.rs +++ b/substrate/frame/sassafras/src/benchmarking.rs @@ -84,7 +84,7 @@ mod benchmarks { // - load the full ring context. // - recompute the ring verifier. // - sorting the epoch tickets in one shot - // (here we account for the very unluky scenario where we haven't done any sort work yet) + // (here we account for the very unlucky scenario where we haven't done any sort work yet) // - pending epoch change config. // // For this bench we assume a redundancy factor of 2 (suggested value to be used in prod). @@ -139,8 +139,8 @@ mod benchmarks { TicketsIds::::insert((epoch_tag as u8, i), id); let body = TicketBody { attempt_idx: i, - erased_public: EphemeralPublic([i as u8; 32]), - revealed_public: EphemeralPublic([i as u8; 32]), + erased_public: EphemeralPublic::from([i as u8; 32]), + revealed_public: EphemeralPublic::from([i as u8; 32]), }; TicketsData::::set(id, Some(body)); }); @@ -236,8 +236,8 @@ mod benchmarks { .map(|i| { let body = TicketBody { attempt_idx: i, - erased_public: EphemeralPublic([i as u8; 32]), - revealed_public: EphemeralPublic([i as u8; 32]), + erased_public: EphemeralPublic::from([i as u8; 32]), + revealed_public: EphemeralPublic::from([i as u8; 32]), }; let id_bytes = crate::hashing::blake2_128(&i.to_le_bytes()); let id = TicketId::from_le_bytes(id_bytes); diff --git a/substrate/frame/sassafras/src/lib.rs b/substrate/frame/sassafras/src/lib.rs index 0ee8657489b7f7321d24b209dfddcd086cf1cf37..8cbf1e47e3203a208578abb1b8b8431cbfd22ebb 100644 --- a/substrate/frame/sassafras/src/lib.rs +++ b/substrate/frame/sassafras/src/lib.rs @@ -234,7 +234,7 @@ pub mod pallet { /// Epoch X first N-th ticket has key (X mod 2, N) /// /// Note that the ticket's index doesn't directly correspond to the slot index within the epoch. - /// The assigment is computed dynamically using an *outside-in* strategy. + /// The assignment is computed dynamically using an *outside-in* strategy. /// /// Be aware that entries within this map are never removed, only overwritten. /// Last element index should be fetched from the [`TicketsMeta`] value. @@ -465,7 +465,7 @@ pub mod pallet { /// Plan an epoch configuration change. /// - /// The epoch configuration change is recorded and will be announced at the begining + /// The epoch configuration change is recorded and will be announced at the beginning /// of the next epoch together with next epoch authorities information. /// In other words, the configuration will be enacted one epoch later. /// @@ -758,11 +758,11 @@ impl Pallet { let randomness = hashing::blake2_256(buf.as_slice()); RandomnessAccumulator::::put(randomness); - let next_randoness = Self::update_epoch_randomness(1); + let next_randomness = Self::update_epoch_randomness(1); // Deposit a log as this is the first block in first epoch. let next_epoch = NextEpochDescriptor { - randomness: next_randoness, + randomness: next_randomness, authorities: Self::next_authorities().into_inner(), config: None, }; diff --git a/substrate/frame/sassafras/src/mock.rs b/substrate/frame/sassafras/src/mock.rs index 5e5909fcb0d6e0d4db41ce96035fa9322cf65bbd..f145bffa3a05c1c9b05087490792245e58c2a352 100644 --- a/substrate/frame/sassafras/src/mock.rs +++ b/substrate/frame/sassafras/src/mock.rs @@ -43,7 +43,7 @@ const LOG_TARGET: &str = "sassafras::tests"; const EPOCH_LENGTH: u32 = 10; const MAX_AUTHORITIES: u32 = 100; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = frame_system::mocking::MockBlock; } diff --git a/substrate/frame/scheduler/README.md b/substrate/frame/scheduler/README.md index 6aec2ddb0e4325e8ec4bbe3987ad2ee16a12cd52..5e233fdbdb0e9babd08dd73c8ca4942c718c4a58 100644 --- a/substrate/frame/scheduler/README.md +++ b/substrate/frame/scheduler/README.md @@ -16,7 +16,7 @@ for the origin: namely `frame_system::Config::BaseCallFilter` for all origin except root which will get no filter. And not the filter contained in origin use to call `fn schedule`. -If a call is scheduled using proxy or whatever mecanism which adds filter, +If a call is scheduled using proxy or whatever mechanism which adds filter, then those filter will not be used when dispatching the schedule call. ## Interface diff --git a/substrate/frame/scheduler/src/benchmarking.rs b/substrate/frame/scheduler/src/benchmarking.rs index 18441d54b39a2244f1eb9677d38be2576e141d94..884f78000384cf66696b22de40238bbd2e5bef95 100644 --- a/substrate/frame/scheduler/src/benchmarking.rs +++ b/substrate/frame/scheduler/src/benchmarking.rs @@ -211,7 +211,7 @@ benchmarks! { } verify { } - // `execute_dispatch` when the origin is `Signed`, not counting the dispatable's weight. + // `execute_dispatch` when the origin is `Signed`, not counting the dispatchable's weight. execute_dispatch_signed { let mut counter = WeightMeter::new(); let origin = make_origin::(true); @@ -222,7 +222,7 @@ benchmarks! { verify { } - // `execute_dispatch` when the origin is not `Signed`, not counting the dispatable's weight. + // `execute_dispatch` when the origin is not `Signed`, not counting the dispatchable's weight. execute_dispatch_unsigned { let mut counter = WeightMeter::new(); let origin = make_origin::(false); diff --git a/substrate/frame/scheduler/src/lib.rs b/substrate/frame/scheduler/src/lib.rs index daebebdee9956a2317ca6d755999797a52ac42ce..62417b8d2cc28fcae514a9eefdcef34beb88260b 100644 --- a/substrate/frame/scheduler/src/lib.rs +++ b/substrate/frame/scheduler/src/lib.rs @@ -46,7 +46,7 @@ //! 1. Scheduling a runtime call at a specific block. #![doc = docify::embed!("src/tests.rs", basic_scheduling_works)] //! -//! 2. Scheduling a preimage hash of a runtime call at a specifc block +//! 2. Scheduling a preimage hash of a runtime call at a specific block #![doc = docify::embed!("src/tests.rs", scheduling_with_preimages_works)] //! @@ -229,7 +229,7 @@ pub mod pallet { use frame_support::{dispatch::PostDispatchInfo, pallet_prelude::*}; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] diff --git a/substrate/frame/scheduler/src/mock.rs b/substrate/frame/scheduler/src/mock.rs index bf7dac0d53ae2596f6e9672e1753d96879b7c2be..8d36ca1c42e3ad916a5cd38e2ef3fa7bcfea5483 100644 --- a/substrate/frame/scheduler/src/mock.rs +++ b/substrate/frame/scheduler/src/mock.rs @@ -137,7 +137,7 @@ parameter_types! { ); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl system::Config for Test { type BaseCallFilter = BaseFilter; type Block = Block; diff --git a/substrate/frame/scheduler/src/tests.rs b/substrate/frame/scheduler/src/tests.rs index 1ed2ca9e2f36d0b3bf4b3a02983ab1ab7539c30b..bb02320ad751e0a40063434921bd00c9eac6687c 100644 --- a/substrate/frame/scheduler/src/tests.rs +++ b/substrate/frame/scheduler/src/tests.rs @@ -1119,7 +1119,7 @@ fn reschedule_named_works() { } #[test] -fn reschedule_named_perodic_works() { +fn reschedule_named_periodic_works() { new_test_ext().execute_with(|| { let call = RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) }); @@ -2980,7 +2980,7 @@ fn reschedule_named_last_task_removes_agenda() { }); } -/// Ensures that an unvailable call sends an event. +/// Ensures that an unavailable call sends an event. #[test] fn unavailable_call_is_detected() { use frame_support::traits::schedule::v3::Named; diff --git a/substrate/frame/scored-pool/src/mock.rs b/substrate/frame/scored-pool/src/mock.rs index 6fba1bb3d5376ba13273b1133691537234791dca..9d2f5eb1099f819ad0db2ac1c6d1878a5d2df2da 100644 --- a/substrate/frame/scored-pool/src/mock.rs +++ b/substrate/frame/scored-pool/src/mock.rs @@ -46,7 +46,7 @@ ord_parameter_types! { pub const ScoreOrigin: u64 = 3; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; diff --git a/substrate/frame/session/benchmarking/src/mock.rs b/substrate/frame/session/benchmarking/src/mock.rs index 2ef8989f4b057da5baa34c5b6ec606fd0575861a..81052141fd8650106a2bbc68a5a67f2dbf457545 100644 --- a/substrate/frame/session/benchmarking/src/mock.rs +++ b/substrate/frame/session/benchmarking/src/mock.rs @@ -45,7 +45,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/frame/session/src/historical/mod.rs b/substrate/frame/session/src/historical/mod.rs index d74e9dd0b7c54042840c62feff0693359f97c6c9..b9cecea1a7f7144fb7f846548b827455dce0a1e5 100644 --- a/substrate/frame/session/src/historical/mod.rs +++ b/substrate/frame/session/src/historical/mod.rs @@ -58,7 +58,7 @@ pub mod pallet { use super::*; use frame_support::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] diff --git a/substrate/frame/session/src/lib.rs b/substrate/frame/session/src/lib.rs index 178d43f596b2742af69d35437592d78bd030a63d..17b6aa7a4640ce764919c7e20a07cb7ba1261486 100644 --- a/substrate/frame/session/src/lib.rs +++ b/substrate/frame/session/src/lib.rs @@ -285,7 +285,11 @@ pub trait SessionHandler { /// before initialization of your pallet. /// /// `changed` is true whenever any of the session keys or underlying economic - /// identities or weightings behind those keys has changed. + /// identities or weightings behind `validators` keys has changed. `queued_validators` + /// could change without `validators` changing. Example of possible sequent calls: + /// Session N: on_new_session(false, unchanged_validators, unchanged_queued_validators) + /// Session N + 1: on_new_session(false, unchanged_validators, new_queued_validators) + /// Session N + 2: on_new_session(true, new_queued_validators, new_queued_validators) fn on_new_session( changed: bool, validators: &[(ValidatorId, Ks)], @@ -368,7 +372,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); #[pallet::pallet] @@ -460,27 +464,13 @@ pub mod pallet { ); self.keys.iter().map(|x| x.1.clone()).collect() }); - assert!( - !initial_validators_0.is_empty(), - "Empty validator set for session 0 in genesis block!" - ); let initial_validators_1 = T::SessionManager::new_session_genesis(1) .unwrap_or_else(|| initial_validators_0.clone()); - assert!( - !initial_validators_1.is_empty(), - "Empty validator set for session 1 in genesis block!" - ); let queued_keys: Vec<_> = initial_validators_1 - .iter() - .cloned() - .map(|v| { - ( - v.clone(), - Pallet::::load_keys(&v).expect("Validator in session 1 missing keys!"), - ) - }) + .into_iter() + .filter_map(|v| Pallet::::load_keys(&v).map(|k| (v, k))) .collect(); // Tell everyone about the genesis session keys diff --git a/substrate/frame/session/src/mock.rs b/substrate/frame/session/src/mock.rs index 89804f72cd6268314b28724329229882bf9a2103..25b81668cc084cbc226b283a60ec9b3184e9cc34 100644 --- a/substrate/frame/session/src/mock.rs +++ b/substrate/frame/session/src/mock.rs @@ -224,7 +224,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { sp_io::TestExternalities::new(t) } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } diff --git a/substrate/frame/society/src/lib.rs b/substrate/frame/society/src/lib.rs index 99dd580a4035ed138b494d827956435e597ebe1c..5bce245f73f17e02d1301ee9242a356ad291b3ee 100644 --- a/substrate/frame/society/src/lib.rs +++ b/substrate/frame/society/src/lib.rs @@ -1509,7 +1509,7 @@ impl, I: 'static> Pallet { // the Founder. if MemberCount::::get() > 2 { let defender = next_defender - .or_else(|| Self::pick_defendent(rng)) + .or_else(|| Self::pick_defendant(rng)) .expect("exited if members empty; qed"); let skeptic = Self::pick_member_except(rng, &defender).expect("exited if members empty; qed"); @@ -1871,7 +1871,7 @@ impl, I: 'static> Pallet { /// /// If only the Founder and Head members exist (or the state is inconsistent), then `None` /// may be returned. - fn pick_defendent(rng: &mut impl RngCore) -> Option { + fn pick_defendant(rng: &mut impl RngCore) -> Option { let member_count = MemberCount::::get(); if member_count <= 2 { return None @@ -1976,7 +1976,7 @@ impl, I: 'static> Pallet { /// Transfer some `amount` from the main account into the payouts account and reduce the Pot /// by this amount. fn reserve_payout(amount: BalanceOf) { - // Tramsfer payout from the Pot into the payouts account. + // Transfer payout from the Pot into the payouts account. Pot::::mutate(|pot| pot.saturating_reduce(amount)); // this should never fail since we ensure we can afford the payouts in a previous @@ -1988,7 +1988,7 @@ impl, I: 'static> Pallet { /// Transfer some `amount` from the main account into the payouts account and increase the Pot /// by this amount. fn unreserve_payout(amount: BalanceOf) { - // Tramsfer payout from the Pot into the payouts account. + // Transfer payout from the Pot into the payouts account. Pot::::mutate(|pot| pot.saturating_accrue(amount)); // this should never fail since we ensure we can afford the payouts in a previous diff --git a/substrate/frame/society/src/migrations.rs b/substrate/frame/society/src/migrations.rs index dafb1e0b9e5e99706de94b1a4fdd1808343af29d..8fd87b1163a4877c41c4b8ea30ace460e2916867 100644 --- a/substrate/frame/society/src/migrations.rs +++ b/substrate/frame/society/src/migrations.rs @@ -40,11 +40,11 @@ impl< { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { - let current = Pallet::::current_storage_version(); - let onchain = Pallet::::on_chain_storage_version(); - ensure!(onchain == 0 && current == 2, "pallet_society: invalid version"); + let in_code = Pallet::::in_code_storage_version(); + let on_chain = Pallet::::on_chain_storage_version(); + ensure!(on_chain == 0 && in_code == 2, "pallet_society: invalid version"); - Ok((old::Candidates::::get(), old::Members::::get()).encode()) + Ok((v0::Candidates::::get(), v0::Members::::get()).encode()) } fn on_runtime_upgrade() -> Weight { @@ -103,7 +103,7 @@ pub type MigrateToV2 = frame_support::migrations::VersionedMi ::DbWeight, >; -pub(crate) mod old { +pub(crate) mod v0 { use super::*; use frame_support::storage_alias; @@ -230,37 +230,37 @@ pub fn assert_internal_consistency, I: Instance + 'static>() { } // We don't use these - make sure they don't exist. - assert_eq!(old::SuspendedCandidates::::iter().count(), 0); - assert_eq!(old::Strikes::::iter().count(), 0); - assert_eq!(old::Vouching::::iter().count(), 0); - assert!(!old::Defender::::exists()); - assert!(!old::Members::::exists()); + assert_eq!(v0::SuspendedCandidates::::iter().count(), 0); + assert_eq!(v0::Strikes::::iter().count(), 0); + assert_eq!(v0::Vouching::::iter().count(), 0); + assert!(!v0::Defender::::exists()); + assert!(!v0::Members::::exists()); } pub fn from_original, I: Instance + 'static>( past_payouts: &mut [(::AccountId, BalanceOf)], ) -> Result { - // Migrate Bids from old::Bids (just a trunctation). - Bids::::put(BoundedVec::<_, T::MaxBids>::truncate_from(old::Bids::::take())); + // Migrate Bids from old::Bids (just a truncation). + Bids::::put(BoundedVec::<_, T::MaxBids>::truncate_from(v0::Bids::::take())); // Initialise round counter. RoundCount::::put(0); // Migrate Candidates from old::Candidates - for Bid { who: candidate, kind, value } in old::Candidates::::take().into_iter() { + for Bid { who: candidate, kind, value } in v0::Candidates::::take().into_iter() { let mut tally = Tally::default(); // Migrate Votes from old::Votes // No need to drain, since we're overwriting values. - for (voter, vote) in old::Votes::::iter_prefix(&candidate) { + for (voter, vote) in v0::Votes::::iter_prefix(&candidate) { Votes::::insert( &candidate, &voter, - Vote { approve: vote == old::Vote::Approve, weight: 1 }, + Vote { approve: vote == v0::Vote::Approve, weight: 1 }, ); match vote { - old::Vote::Approve => tally.approvals.saturating_inc(), - old::Vote::Reject => tally.rejections.saturating_inc(), - old::Vote::Skeptic => Skeptic::::put(&voter), + v0::Vote::Approve => tally.approvals.saturating_inc(), + v0::Vote::Reject => tally.rejections.saturating_inc(), + v0::Vote::Skeptic => Skeptic::::put(&voter), } } Candidates::::insert( @@ -271,9 +271,9 @@ pub fn from_original, I: Instance + 'static>( // Migrate Members from old::Members old::Strikes old::Vouching let mut member_count = 0; - for member in old::Members::::take() { - let strikes = old::Strikes::::take(&member); - let vouching = old::Vouching::::take(&member); + for member in v0::Members::::take() { + let strikes = v0::Strikes::::take(&member); + let vouching = v0::Vouching::::take(&member); let record = MemberRecord { index: member_count, rank: 0, strikes, vouching }; Members::::insert(&member, record); MemberByIndex::::insert(member_count, &member); @@ -287,13 +287,13 @@ pub fn from_original, I: Instance + 'static>( .defensive_ok_or("member_count > 0, we must have at least 1 member")?; // Swap the founder with the first member in MemberByIndex. MemberByIndex::::swap(0, member_count); - // Update the indicies of the swapped member MemberRecords. + // Update the indices of the swapped member MemberRecords. Members::::mutate(&member, |m| { if let Some(member) = m { member.index = 0; } else { frame_support::defensive!( - "Member somehow disapeared from storage after it was inserted" + "Member somehow disappeared from storage after it was inserted" ); } }); @@ -302,7 +302,7 @@ pub fn from_original, I: Instance + 'static>( member.index = member_count; } else { frame_support::defensive!( - "Member somehow disapeared from storage after it was queried" + "Member somehow disappeared from storage after it was queried" ); } }); @@ -314,7 +314,7 @@ pub fn from_original, I: Instance + 'static>( // Migrate Payouts from: old::Payouts and raw info (needed since we can't query old chain // state). past_payouts.sort(); - for (who, mut payouts) in old::Payouts::::iter() { + for (who, mut payouts) in v0::Payouts::::iter() { payouts.truncate(T::MaxPayouts::get() as usize); // ^^ Safe since we already truncated. let paid = past_payouts @@ -329,19 +329,19 @@ pub fn from_original, I: Instance + 'static>( } // Migrate SuspendedMembers from old::SuspendedMembers old::Strikes old::Vouching. - for who in old::SuspendedMembers::::iter_keys() { - let strikes = old::Strikes::::take(&who); - let vouching = old::Vouching::::take(&who); + for who in v0::SuspendedMembers::::iter_keys() { + let strikes = v0::Strikes::::take(&who); + let vouching = v0::Vouching::::take(&who); let record = MemberRecord { index: 0, rank: 0, strikes, vouching }; SuspendedMembers::::insert(&who, record); } // Any suspended candidates remaining are rejected. - let _ = old::SuspendedCandidates::::clear(u32::MAX, None); + let _ = v0::SuspendedCandidates::::clear(u32::MAX, None); // We give the current defender the benefit of the doubt. - old::Defender::::kill(); - let _ = old::DefenderVotes::::clear(u32::MAX, None); + v0::Defender::::kill(); + let _ = v0::DefenderVotes::::clear(u32::MAX, None); Ok(T::BlockWeights::get().max_block) } diff --git a/substrate/frame/society/src/mock.rs b/substrate/frame/society/src/mock.rs index 04f3e85f539aa94dc78142d7e9e5b27736dbc2af..3c27c08a10610f44ebaf2fc72678f52be2f06507 100644 --- a/substrate/frame/society/src/mock.rs +++ b/substrate/frame/society/src/mock.rs @@ -54,7 +54,7 @@ ord_parameter_types! { pub const MaxBids: u32 = 10; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type AccountId = u128; type Block = Block; @@ -62,7 +62,7 @@ impl frame_system::Config for Test { type Lookup = IdentityLookup; } -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for Test { type ReserveIdentifier = [u8; 8]; type AccountStore = System; diff --git a/substrate/frame/society/src/tests.rs b/substrate/frame/society/src/tests.rs index 940643168fb41fd723905497935b7cb543b7f7b7..5f8ecc9a7c52b5095b561db1fa93c749a8efd206 100644 --- a/substrate/frame/society/src/tests.rs +++ b/substrate/frame/society/src/tests.rs @@ -18,7 +18,7 @@ //! Tests for the module. use super::*; -use migrations::old; +use migrations::v0; use mock::*; use frame_support::{assert_noop, assert_ok}; @@ -32,41 +32,41 @@ use RuntimeOrigin as Origin; #[test] fn migration_works() { EnvBuilder::new().founded(false).execute(|| { - use old::Vote::*; + use v0::Vote::*; // Initialise the old storage items. Founder::::put(10); Head::::put(30); - old::Members::::put(vec![10, 20, 30]); - old::Vouching::::insert(30, Vouching); - old::Vouching::::insert(40, Banned); - old::Strikes::::insert(20, 1); - old::Strikes::::insert(30, 2); - old::Strikes::::insert(40, 5); - old::Payouts::::insert(20, vec![(1, 1)]); - old::Payouts::::insert( + v0::Members::::put(vec![10, 20, 30]); + v0::Vouching::::insert(30, Vouching); + v0::Vouching::::insert(40, Banned); + v0::Strikes::::insert(20, 1); + v0::Strikes::::insert(30, 2); + v0::Strikes::::insert(40, 5); + v0::Payouts::::insert(20, vec![(1, 1)]); + v0::Payouts::::insert( 30, (0..=::MaxPayouts::get()) .map(|i| (i as u64, i as u64)) .collect::>(), ); - old::SuspendedMembers::::insert(40, true); + v0::SuspendedMembers::::insert(40, true); - old::Defender::::put(20); - old::DefenderVotes::::insert(10, Approve); - old::DefenderVotes::::insert(20, Approve); - old::DefenderVotes::::insert(30, Reject); + v0::Defender::::put(20); + v0::DefenderVotes::::insert(10, Approve); + v0::DefenderVotes::::insert(20, Approve); + v0::DefenderVotes::::insert(30, Reject); - old::SuspendedCandidates::::insert(50, (10, Deposit(100))); + v0::SuspendedCandidates::::insert(50, (10, Deposit(100))); - old::Candidates::::put(vec![ + v0::Candidates::::put(vec![ Bid { who: 60, kind: Deposit(100), value: 200 }, Bid { who: 70, kind: Vouch(30, 30), value: 100 }, ]); - old::Votes::::insert(60, 10, Approve); - old::Votes::::insert(70, 10, Reject); - old::Votes::::insert(70, 20, Approve); - old::Votes::::insert(70, 30, Approve); + v0::Votes::::insert(60, 10, Approve); + v0::Votes::::insert(70, 10, Reject); + v0::Votes::::insert(70, 20, Approve); + v0::Votes::::insert(70, 30, Approve); let bids = (0..=::MaxBids::get()) .map(|i| Bid { @@ -75,7 +75,7 @@ fn migration_works() { value: 10u64 + i as u64, }) .collect::>(); - old::Bids::::put(bids); + v0::Bids::::put(bids); migrations::from_original::(&mut [][..]).expect("migration failed"); migrations::assert_internal_consistency::(); @@ -541,7 +541,7 @@ fn suspended_candidate_rejected_works() { assert_ok!(Society::vote(Origin::signed(30), x, true)); } - // Voting continues, as no canidate is clearly accepted yet and the founder chooses not to + // Voting continues, as no candidate is clearly accepted yet and the founder chooses not to // act. conclude_intake(false, None); assert_eq!(members(), vec![10, 20, 30]); diff --git a/substrate/frame/src/lib.rs b/substrate/frame/src/lib.rs index a1715ba4900102595ac6ec926a0aeeb2a2be1650..52db7c34bfdc46efcb4008c04ae4f8b185fb3ffb 100644 --- a/substrate/frame/src/lib.rs +++ b/substrate/frame/src/lib.rs @@ -163,6 +163,12 @@ pub mod runtime { ConstU32, ConstU64, ConstU8, }; + /// Primary types used to parameterize `EnsureOrigin` and `EnsureRootWithArg`. + pub use frame_system::{ + EnsureNever, EnsureNone, EnsureRoot, EnsureRootWithSuccess, EnsureSigned, + EnsureSignedBy, + }; + /// Types to define your runtime version. pub use sp_version::{create_runtime_str, runtime_version, RuntimeVersion}; @@ -191,7 +197,7 @@ pub mod runtime { // Types often used in the runtime APIs. pub use sp_core::OpaqueMetadata; pub use sp_inherents::{CheckInherentsResult, InherentData}; - pub use sp_runtime::ApplyExtrinsicResult; + pub use sp_runtime::{ApplyExtrinsicResult, ExtrinsicInclusionMode}; pub use frame_system_rpc_runtime_api::*; pub use sp_api::{self, *}; diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index 5947adb9028b3a63b9f5b75eff4190c83749895f..9461daefed65e74c778a248a0bbaeeedd3ee6222 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -32,8 +32,8 @@ //! state consistency. use frame_support::{ - defensive, - traits::{LockableCurrency, WithdrawReasons}, + defensive, ensure, + traits::{Defensive, LockableCurrency, WithdrawReasons}, }; use sp_staking::StakingAccount; use sp_std::prelude::*; @@ -106,18 +106,39 @@ impl StakingLedger { /// This getter can be called with either a controller or stash account, provided that the /// account is properly wrapped in the respective [`StakingAccount`] variant. This is meant to /// abstract the concept of controller/stash accounts from the caller. + /// + /// Returns [`Error::BadState`] when a bond is in "bad state". A bond is in a bad state when a + /// stash has a controller which is bonding a ledger associated with another stash. pub(crate) fn get(account: StakingAccount) -> Result, Error> { - let controller = match account { - StakingAccount::Stash(stash) => >::get(stash).ok_or(Error::::NotStash), - StakingAccount::Controller(controller) => Ok(controller), - }?; + let (stash, controller) = match account.clone() { + StakingAccount::Stash(stash) => + (stash.clone(), >::get(&stash).ok_or(Error::::NotStash)?), + StakingAccount::Controller(controller) => ( + Ledger::::get(&controller) + .map(|l| l.stash) + .ok_or(Error::::NotController)?, + controller, + ), + }; - >::get(&controller) + let ledger = >::get(&controller) .map(|mut ledger| { ledger.controller = Some(controller.clone()); ledger }) - .ok_or(Error::::NotController) + .ok_or(Error::::NotController)?; + + // if ledger bond is in a bad state, return error to prevent applying operations that may + // further spoil the ledger's state. A bond is in bad state when the bonded controller is + // associated with a different ledger (i.e. a ledger with a different stash). + // + // See for more details. + ensure!( + Bonded::::get(&stash) == Some(controller) && ledger.stash == stash, + Error::::BadState + ); + + Ok(ledger) } /// Returns the reward destination of a staking ledger, stored in [`Payee`]. @@ -201,6 +222,30 @@ impl StakingLedger { } } + /// Sets the ledger controller to its stash. + pub(crate) fn set_controller_to_stash(self) -> Result<(), Error> { + let controller = self.controller.as_ref() + .defensive_proof("Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.") + .ok_or(Error::::NotController)?; + + ensure!(self.stash != *controller, Error::::AlreadyPaired); + + // check if the ledger's stash is a controller of another ledger. + if let Some(bonded_ledger) = Ledger::::get(&self.stash) { + // there is a ledger bonded by the stash. In this case, the stash of the bonded ledger + // should be the same as the ledger's stash. Otherwise fail to prevent data + // inconsistencies. See for more + // details. + ensure!(bonded_ledger.stash == self.stash, Error::::BadState); + } + + >::remove(&controller); + >::insert(&self.stash, &self); + >::insert(&self.stash, &self.stash); + + Ok(()) + } + /// Clears all data related to a staking ledger and its bond in both [`Ledger`] and [`Bonded`] /// storage items and updates the stash staking lock. pub(crate) fn kill(stash: &T::AccountId) -> Result<(), Error> { diff --git a/substrate/frame/staking/src/migrations.rs b/substrate/frame/staking/src/migrations.rs index 98984be9920d782bcfbe4bd1dec733294dc604e7..d5b18421d5b67fbeaac27cbbdecde174fa3d024b 100644 --- a/substrate/frame/staking/src/migrations.rs +++ b/substrate/frame/staking/src/migrations.rs @@ -30,7 +30,7 @@ use frame_support::ensure; #[cfg(feature = "try-runtime")] use sp_runtime::TryRuntimeError; -/// Used for release versioning upto v12. +/// Used for release versioning up to v12. /// /// Obsolete from v13. Keeping around to make encoding/decoding of old migration code easier. #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] @@ -67,11 +67,11 @@ pub mod v14 { pub struct MigrateToV14(core::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV14 { fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); + let in_code = Pallet::::in_code_storage_version(); let on_chain = Pallet::::on_chain_storage_version(); - if current == 14 && on_chain == 13 { - current.put::>(); + if in_code == 14 && on_chain == 13 { + in_code.put::>(); log!(info, "v14 applied successfully."); T::DbWeight::get().reads_writes(1, 1) @@ -108,12 +108,12 @@ pub mod v13 { } fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); + let in_code = Pallet::::in_code_storage_version(); let onchain = StorageVersion::::get(); - if current == 13 && onchain == ObsoleteReleases::V12_0_0 { + if in_code == 13 && onchain == ObsoleteReleases::V12_0_0 { StorageVersion::::kill(); - current.put::>(); + in_code.put::>(); log!(info, "v13 applied successfully"); T::DbWeight::get().reads_writes(1, 2) diff --git a/substrate/frame/staking/src/mock.rs b/substrate/frame/staking/src/mock.rs index 24311cb9e78266240f2ded70fcacfe3b4f265d3f..6c2ea225ff1e116c2621d6c6a9bed01eb2ea04b2 100644 --- a/substrate/frame/staking/src/mock.rs +++ b/substrate/frame/staking/src/mock.rs @@ -118,7 +118,7 @@ parameter_types! { pub static MaxControllersInDeprecationBatch: u32 = 5900; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type DbWeight = RocksDbWeight; type Block = Block; @@ -786,6 +786,57 @@ pub(crate) fn bond_controller_stash(controller: AccountId, stash: AccountId) -> Ok(()) } +pub(crate) fn setup_double_bonded_ledgers() { + assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 10, RewardDestination::Staked)); + assert_ok!(Staking::bond(RuntimeOrigin::signed(2), 20, RewardDestination::Staked)); + assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 20, RewardDestination::Staked)); + // not relevant to the test case, but ensures try-runtime checks pass. + [1, 2, 3] + .iter() + .for_each(|s| Payee::::insert(s, RewardDestination::Staked)); + + // we want to test the case where a controller can also be a stash of another ledger. + // for that, we change the controller/stash bonding so that: + // * 2 becomes controller of 1. + // * 3 becomes controller of 2. + // * 4 becomes controller of 3. + let ledger_1 = Ledger::::get(1).unwrap(); + let ledger_2 = Ledger::::get(2).unwrap(); + let ledger_3 = Ledger::::get(3).unwrap(); + + // 4 becomes controller of 3. + Bonded::::mutate(3, |controller| *controller = Some(4)); + Ledger::::insert(4, ledger_3); + + // 3 becomes controller of 2. + Bonded::::mutate(2, |controller| *controller = Some(3)); + Ledger::::insert(3, ledger_2); + + // 2 becomes controller of 1 + Bonded::::mutate(1, |controller| *controller = Some(2)); + Ledger::::insert(2, ledger_1); + // 1 is not controller anymore. + Ledger::::remove(1); + + // checks. now we have: + // * 3 ledgers + assert_eq!(Ledger::::iter().count(), 3); + // * stash 1 has controller 2. + assert_eq!(Bonded::::get(1), Some(2)); + assert_eq!(StakingLedger::::paired_account(StakingAccount::Stash(1)), Some(2)); + assert_eq!(Ledger::::get(2).unwrap().stash, 1); + + // * stash 2 has controller 3. + assert_eq!(Bonded::::get(2), Some(3)); + assert_eq!(StakingLedger::::paired_account(StakingAccount::Stash(2)), Some(3)); + assert_eq!(Ledger::::get(3).unwrap().stash, 2); + + // * stash 3 has controller 4. + assert_eq!(Bonded::::get(3), Some(4)); + assert_eq!(StakingLedger::::paired_account(StakingAccount::Stash(3)), Some(4)); + assert_eq!(Ledger::::get(4).unwrap().stash, 3); +} + #[macro_export] macro_rules! assert_session_era { ($session:expr, $era:expr) => { diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 757c46f4faf93d0e5ac79c21741774b2e36d2a89..407b301fad2a009aee62a13af8daa77cfaf30d81 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -165,7 +165,8 @@ impl Pallet { let controller = Self::bonded(&validator_stash).ok_or_else(|| { Error::::NotStash.with_weight(T::WeightInfo::payout_stakers_alive_staked(0)) })?; - let ledger = >::get(&controller).ok_or(Error::::NotController)?; + + let ledger = Self::ledger(StakingAccount::Controller(controller))?; let page = EraInfo::::get_next_claimable_page(era, &validator_stash, &ledger) .ok_or_else(|| { Error::::AlreadyClaimed @@ -1728,7 +1729,7 @@ impl StakingInterface for Pallet { ) -> Result { let ctrl = Self::bonded(&who).ok_or(Error::::NotStash)?; Self::withdraw_unbonded(RawOrigin::Signed(ctrl.clone()).into(), num_slashing_spans) - .map(|_| !Ledger::::contains_key(&ctrl)) + .map(|_| !StakingLedger::::is_bonded(StakingAccount::Controller(ctrl))) .map_err(|with_post| with_post.error) } @@ -1836,6 +1837,7 @@ impl Pallet { "VoterList contains non-staker" ); + Self::check_bonded_consistency()?; Self::check_payees()?; Self::check_nominators()?; Self::check_exposures()?; @@ -1844,9 +1846,67 @@ impl Pallet { Self::check_count() } + /// Invariants: + /// * A controller should not be associated with more than one ledger. + /// * A bonded (stash, controller) pair should have only one associated ledger. I.e. if the + /// ledger is bonded by stash, the controller account must not bond a different ledger. + /// * A bonded (stash, controller) pair must have an associated ledger. + /// NOTE: these checks result in warnings only. Once + /// is resolved, turn warns into check + /// failures. + fn check_bonded_consistency() -> Result<(), TryRuntimeError> { + use sp_std::collections::btree_set::BTreeSet; + + let mut count_controller_double = 0; + let mut count_double = 0; + let mut count_none = 0; + // sanity check to ensure that each controller in Bonded storage is associated with only one + // ledger. + let mut controllers = BTreeSet::new(); + + for (stash, controller) in >::iter() { + if !controllers.insert(controller.clone()) { + count_controller_double += 1; + } + + match (>::get(&stash), >::get(&controller)) { + (Some(_), Some(_)) => + // if stash == controller, it means that the ledger has migrated to + // post-controller. If no migration happened, we expect that the (stash, + // controller) pair has only one associated ledger. + if stash != controller { + count_double += 1; + }, + (None, None) => { + count_none += 1; + }, + _ => {}, + }; + } + + if count_controller_double != 0 { + log!( + warn, + "a controller is associated with more than one ledger ({} occurrences)", + count_controller_double + ); + }; + + if count_double != 0 { + log!(warn, "single tuple of (stash, controller) pair bonds more than one ledger ({} occurrences)", count_double); + } + + if count_none != 0 { + log!(warn, "inconsistent bonded state: (stash, controller) pair missing associated ledger ({} occurrences)", count_none); + } + + Ok(()) + } + /// Invariants: /// * A bonded ledger should always have an assigned `Payee`. /// * The number of entries in `Payee` and of bonded staking ledgers *must* match. + /// * The stash account in the ledger must match that of the bonded account. fn check_payees() -> Result<(), TryRuntimeError> { for (stash, _) in Bonded::::iter() { ensure!(Payee::::get(&stash).is_some(), "bonded ledger does not have payee set"); @@ -1861,6 +1921,11 @@ impl Pallet { Ok(()) } + /// Invariants: + /// * Number of voters in `VoterList` match that of the number of Nominators and Validators in + /// the system (validator is both voter and target). + /// * Number of targets in `TargetList` matches the number of validators in the system. + /// * Current validator count is bounded by the election provider's max winners. fn check_count() -> Result<(), TryRuntimeError> { ensure!( ::VoterList::count() == @@ -1879,6 +1944,11 @@ impl Pallet { Ok(()) } + /// Invariants: + /// * `ledger.controller` is not stored in the storage (but populated at retrieval). + /// * Stake consistency: ledger.total == ledger.active + sum(ledger.unlocking). + /// * The controller keying the ledger and the ledger stash matches the state of the `Bonded` + /// storage. fn check_ledgers() -> Result<(), TryRuntimeError> { Bonded::::iter() .map(|(stash, ctrl)| { @@ -1896,8 +1966,10 @@ impl Pallet { Ok(()) } + /// Invariants: + /// * For each era exposed validator, check if the exposure total is sane (exposure.total = + /// exposure.own + exposure.own). fn check_exposures() -> Result<(), TryRuntimeError> { - // a check per validator to ensure the exposure struct is always sane. let era = Self::active_era().unwrap().index; ErasStakers::::iter_prefix_values(era) .map(|expo| { @@ -1915,6 +1987,10 @@ impl Pallet { .collect::>() } + /// Invariants: + /// * For each paged era exposed validator, check if the exposure total is sane (exposure.total + /// = exposure.own + exposure.own). + /// * Paged exposures metadata (`ErasStakersOverview`) matches the paged exposures state. fn check_paged_exposures() -> Result<(), TryRuntimeError> { use sp_staking::PagedExposureMetadata; use sp_std::collections::btree_map::BTreeMap; @@ -1979,6 +2055,8 @@ impl Pallet { .collect::>() } + /// Invariants: + /// * Checks that each nominator has its entire stake correctly distributed. fn check_nominators() -> Result<(), TryRuntimeError> { // a check per nominator to ensure their entire stake is correctly distributed. Will only // kick-in if the nomination was submitted before the current era. diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index e0213efd507fd46d6d7d8800b6d02c89229a13af..6afbf12032d9198b387527a6d81aaaff514d5c4a 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -54,7 +54,7 @@ use crate::{ }; // The speculative number of spans are used as an input of the weight annotation of -// [`Call::unbond`], as the post dipatch weight may depend on the number of slashing span on the +// [`Call::unbond`], as the post dispatch weight may depend on the number of slashing span on the // account which is not provided as an input. The value set should be conservative but sensible. pub(crate) const SPECULATIVE_NUM_SPANS: u32 = 32; @@ -66,7 +66,7 @@ pub mod pallet { use super::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(14); #[pallet::pallet] @@ -791,6 +791,8 @@ pub mod pallet { SnapshotTargetsSizeExceeded { size: u32 }, /// A new force era mode was set. ForceEra { mode: Forcing }, + /// Report of a controller batch deprecation. + ControllerBatchDeprecated { failures: u32 }, } #[pallet::error] @@ -935,6 +937,11 @@ pub mod pallet { return Err(Error::::AlreadyBonded.into()) } + // An existing controller cannot become a stash. + if StakingLedger::::is_bonded(StakingAccount::Controller(stash.clone())) { + return Err(Error::::AlreadyPaired.into()) + } + // Reject a bond which is considered to be _dust_. if value < T::Currency::minimum_balance() { return Err(Error::::InsufficientBond.into()) @@ -975,7 +982,6 @@ pub mod pallet { #[pallet::compact] max_additional: BalanceOf, ) -> DispatchResult { let stash = ensure_signed(origin)?; - let mut ledger = Self::ledger(StakingAccount::Stash(stash.clone()))?; let stash_balance = T::Currency::free_balance(&stash); @@ -1128,7 +1134,7 @@ pub mod pallet { /// this call results in a complete removal of all the data related to the stash account. /// In this case, the `num_slashing_spans` must be larger or equal to the number of /// slashing spans associated with the stash account in the [`SlashingSpans`] storage type, - /// otherwise the call will fail. The call weight is directly propotional to + /// otherwise the call will fail. The call weight is directly proportional to /// `num_slashing_spans`. /// /// ## Complexity @@ -1332,8 +1338,6 @@ pub mod pallet { pub fn set_controller(origin: OriginFor) -> DispatchResult { let stash = ensure_signed(origin)?; - // The bonded map and ledger are mutated directly as this extrinsic is related to a - // (temporary) passive migration. Self::ledger(StakingAccount::Stash(stash.clone())).map(|ledger| { let controller = ledger.controller() .defensive_proof("Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.") @@ -1343,9 +1347,8 @@ pub mod pallet { // Stash is already its own controller. return Err(Error::::AlreadyPaired.into()) } - >::remove(controller); - >::insert(&stash, &stash); - >::insert(&stash, ledger); + + let _ = ledger.set_controller_to_stash()?; Ok(()) })? } @@ -1373,7 +1376,7 @@ pub mod pallet { Ok(()) } - /// Increments the ideal number of validators upto maximum of + /// Increments the ideal number of validators up to maximum of /// `ElectionProviderBase::MaxWinners`. /// /// The dispatch origin must be Root. @@ -1398,7 +1401,7 @@ pub mod pallet { Ok(()) } - /// Scale up the ideal number of validators by a factor upto maximum of + /// Scale up the ideal number of validators by a factor up to maximum of /// `ElectionProviderBase::MaxWinners`. /// /// The dispatch origin must be Root. @@ -1960,7 +1963,7 @@ pub mod pallet { }; if ledger.stash != *controller && !payee_deprecated { - Some((controller.clone(), ledger)) + Some(ledger) } else { None } @@ -1969,13 +1972,12 @@ pub mod pallet { .collect(); // Update unique pairs. - for (controller, ledger) in filtered_batch_with_ledger { - let stash = ledger.stash.clone(); - - >::insert(&stash, &stash); - >::remove(controller); - >::insert(stash, ledger); + let mut failures = 0; + for ledger in filtered_batch_with_ledger { + let _ = ledger.clone().set_controller_to_stash().map_err(|_| failures += 1); } + Self::deposit_event(Event::::ControllerBatchDeprecated { failures }); + Ok(Some(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32)).into()) } } diff --git a/substrate/frame/staking/src/tests.rs b/substrate/frame/staking/src/tests.rs index 3f4e28b1f6af0cdfac939cdc4e11ef5dcc1fd908..ef156e1955276814269669e758ae572a251e3989 100644 --- a/substrate/frame/staking/src/tests.rs +++ b/substrate/frame/staking/src/tests.rs @@ -270,7 +270,7 @@ fn change_controller_works() { assert_eq!(ledger.controller(), Some(stash)); // the raw storage ledger's controller is always `None`. however, we can still fetch the - // correct controller with `ledger.controler()`. + // correct controller with `ledger.controller()`. let raw_ledger = >::get(&stash).unwrap(); assert_eq!(raw_ledger.controller, None); @@ -1248,6 +1248,23 @@ fn bond_extra_works() { }); } +#[test] +fn bond_extra_controller_bad_state_works() { + ExtBuilder::default().try_state(false).build_and_execute(|| { + assert_eq!(StakingLedger::::get(StakingAccount::Stash(31)).unwrap().stash, 31); + + // simulate ledger in bad state: the controller 41 is associated to the stash 31 and 41. + Bonded::::insert(31, 41); + + // we confirm that the ledger is in bad state: 31 has 41 as controller and when fetching + // the ledger associated with the controller 41, its stash is 41 (and not 31). + assert_eq!(Ledger::::get(41).unwrap().stash, 41); + + // if the ledger is in this bad state, the `bond_extra` should fail. + assert_noop!(Staking::bond_extra(RuntimeOrigin::signed(31), 10), Error::::BadState); + }) +} + #[test] fn bond_extra_and_withdraw_unbonded_works() { // @@ -1798,7 +1815,7 @@ fn max_staked_rewards_works() { let total_payout = treasury_payout + validators_payout; // max stakers payout (without max staked rewards cap applied) is larger than the final - // validator rewards. The final payment and remainder should be adjusted by redestributing + // validator rewards. The final payment and remainder should be adjusted by redistributing // the era inflation to apply the cap... assert!(max_stakers_payout > validators_payout); @@ -4669,7 +4686,7 @@ fn bond_during_era_does_not_populate_legacy_claimed_rewards() { } ); - // make sure only era upto history depth is stored + // make sure only era up to history depth is stored let current_era = 99; mock::start_active_era(current_era); bond_validator(13, 1000); @@ -5395,7 +5412,7 @@ mod election_data_provider { Event::SnapshotVotersSizeExceeded { size: 75 } ); - // however, if the election voter size bounds were largers, the snapshot would + // however, if the election voter size bounds were larger, the snapshot would // include the electing voters of 70. let bounds = ElectionBoundsBuilder::default().voters_size(1_000.into()).build(); assert_eq!( @@ -6910,6 +6927,49 @@ mod ledger { }) } + #[test] + fn get_ledger_bad_state_fails() { + ExtBuilder::default().has_stakers(false).try_state(false).build_and_execute(|| { + setup_double_bonded_ledgers(); + + // Case 1: double bonded but not corrupted: + // stash 2 has controller 3: + assert_eq!(Bonded::::get(2), Some(3)); + assert_eq!(Ledger::::get(3).unwrap().stash, 2); + + // stash 2 is also a controller of 1: + assert_eq!(Bonded::::get(1), Some(2)); + assert_eq!(StakingLedger::::paired_account(StakingAccount::Stash(1)), Some(2)); + assert_eq!(Ledger::::get(2).unwrap().stash, 1); + + // although 2 is double bonded (it is a controller and a stash of different ledgers), + // we can safely retrieve the ledger and mutate it since the correct ledger is + // returned. + let ledger_result = StakingLedger::::get(StakingAccount::Stash(2)); + assert_eq!(ledger_result.unwrap().stash, 2); // correct ledger. + + let ledger_result = StakingLedger::::get(StakingAccount::Controller(2)); + assert_eq!(ledger_result.unwrap().stash, 1); // correct ledger. + + // fetching ledger 1 by its stash works. + let ledger_result = StakingLedger::::get(StakingAccount::Stash(1)); + assert_eq!(ledger_result.unwrap().stash, 1); + + // Case 2: corrupted ledger bonding. + // in this case, we simulate what happens when fetching a ledger by stash returns a + // ledger with a different stash. when this happens, we return an error instead of the + // ledger to prevent ledger mutations. + let mut ledger = Ledger::::get(2).unwrap(); + assert_eq!(ledger.stash, 1); + ledger.stash = 2; + Ledger::::insert(2, ledger); + + // now, we are prevented from fetching the ledger by stash from 1. It's associated + // controller (2) is now bonding a ledger with a different stash (2, not 1). + assert!(StakingLedger::::get(StakingAccount::Stash(1)).is_err()); + }) + } + #[test] fn bond_works() { ExtBuilder::default().build_and_execute(|| { @@ -6933,6 +6993,28 @@ mod ledger { }) } + #[test] + fn bond_controller_cannot_be_stash_works() { + ExtBuilder::default().build_and_execute(|| { + let (stash, controller) = testing_utils::create_unique_stash_controller::( + 0, + 10, + RewardDestination::Staked, + false, + ) + .unwrap(); + + assert_eq!(Bonded::::get(stash), Some(controller)); + assert_eq!(Ledger::::get(controller).map(|l| l.stash), Some(stash)); + + // existing controller should not be able become a stash. + assert_noop!( + Staking::bond(RuntimeOrigin::signed(controller), 10, RewardDestination::Staked), + Error::::AlreadyPaired, + ); + }) + } + #[test] fn is_bonded_works() { ExtBuilder::default().build_and_execute(|| { @@ -7161,4 +7243,81 @@ mod ledger { assert_eq!(ledger_updated.stash, stash); }) } + + #[test] + fn deprecate_controller_batch_with_bad_state_ok() { + ExtBuilder::default().has_stakers(false).nominate(false).build_and_execute(|| { + setup_double_bonded_ledgers(); + + // now let's deprecate all the controllers for all the existing ledgers. + let bounded_controllers: BoundedVec< + _, + ::MaxControllersInDeprecationBatch, + > = BoundedVec::try_from(vec![1, 2, 3, 4]).unwrap(); + + assert_ok!(Staking::deprecate_controller_batch( + RuntimeOrigin::root(), + bounded_controllers + )); + + assert_eq!( + *staking_events().last().unwrap(), + Event::ControllerBatchDeprecated { failures: 0 } + ); + }) + } + + #[test] + fn deprecate_controller_batch_with_bad_state_failures() { + ExtBuilder::default().has_stakers(false).try_state(false).build_and_execute(|| { + setup_double_bonded_ledgers(); + + // now let's deprecate all the controllers for all the existing ledgers. + let bounded_controllers: BoundedVec< + _, + ::MaxControllersInDeprecationBatch, + > = BoundedVec::try_from(vec![4, 3, 2, 1]).unwrap(); + + assert_ok!(Staking::deprecate_controller_batch( + RuntimeOrigin::root(), + bounded_controllers + )); + + assert_eq!( + *staking_events().last().unwrap(), + Event::ControllerBatchDeprecated { failures: 2 } + ); + }) + } + + #[test] + fn set_controller_with_bad_state_ok() { + ExtBuilder::default().has_stakers(false).nominate(false).build_and_execute(|| { + setup_double_bonded_ledgers(); + + // in this case, setting controller works due to the ordering of the calls. + assert_ok!(Staking::set_controller(RuntimeOrigin::signed(1))); + assert_ok!(Staking::set_controller(RuntimeOrigin::signed(2))); + assert_ok!(Staking::set_controller(RuntimeOrigin::signed(3))); + }) + } + + #[test] + fn set_controller_with_bad_state_fails() { + ExtBuilder::default().has_stakers(false).try_state(false).build_and_execute(|| { + setup_double_bonded_ledgers(); + + // setting the controller of ledger associated with stash 3 fails since its stash is a + // controller of another ledger. + assert_noop!( + Staking::set_controller(RuntimeOrigin::signed(3)), + Error::::BadState + ); + assert_noop!( + Staking::set_controller(RuntimeOrigin::signed(2)), + Error::::BadState + ); + assert_ok!(Staking::set_controller(RuntimeOrigin::signed(1))); + }) + } } diff --git a/substrate/frame/state-trie-migration/src/lib.rs b/substrate/frame/state-trie-migration/src/lib.rs index 6b3aa9934e070046ee694409b20faebf3934663f..6e1de865ab4f799a2c1ee1a8acb3184b0553435c 100644 --- a/substrate/frame/state-trie-migration/src/lib.rs +++ b/substrate/frame/state-trie-migration/src/lib.rs @@ -452,7 +452,7 @@ pub mod pallet { pub struct TestDefaultConfig; - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig, no_aggregated_types)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)] impl frame_system::DefaultConfig for TestDefaultConfig {} #[frame_support::register_default_impl(TestDefaultConfig)] @@ -1131,7 +1131,7 @@ mod mock { pub const SS58Prefix: u8 = 42; } - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type BlockHashCount = ConstU32<250>; @@ -1144,7 +1144,7 @@ mod mock { pub const MigrationMaxKeyLen: u32 = 512; } - #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] + #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for Test { type ReserveIdentifier = [u8; 8]; type AccountStore = System; @@ -1177,7 +1177,7 @@ mod mock { } } - #[derive_impl(super::config_preludes::TestDefaultConfig as pallet_state_trie_migration::DefaultConfig)] + #[derive_impl(super::config_preludes::TestDefaultConfig)] impl pallet_state_trie_migration::Config for Test { type ControlOrigin = EnsureRoot; type Currency = Balances; diff --git a/substrate/frame/statement/src/mock.rs b/substrate/frame/statement/src/mock.rs index 4ab9cf9e0f96a33fe70de6c019234b9a3829eb7e..35d51e7a27bfc82e82d9b0b19220c31816c27e37 100644 --- a/substrate/frame/statement/src/mock.rs +++ b/substrate/frame/statement/src/mock.rs @@ -43,7 +43,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type AccountId = AccountId32; type Lookup = IdentityLookup; diff --git a/substrate/frame/sudo/src/lib.rs b/substrate/frame/sudo/src/lib.rs index 2ebe4cb015712dbf8df630a7114e6fc32c247e0d..63b68e694307ecb4ee3986118f780df1ae49f609 100644 --- a/substrate/frame/sudo/src/lib.rs +++ b/substrate/frame/sudo/src/lib.rs @@ -38,7 +38,7 @@ //! In Substrate blockchains, pallets may contain dispatchable calls that can only be called at //! the system level of the chain (i.e. dispatchables that require a `Root` origin). //! Setting a privileged account, called the _sudo key_, allows you to make such calls as an -//! extrinisic. +//! extrinsic. //! //! Here's an example of a privileged function in another pallet: //! @@ -156,7 +156,7 @@ pub mod pallet { /// Default prelude sensible to be used in a testing environment. pub struct TestDefaultConfig; - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig, no_aggregated_types)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)] impl frame_system::DefaultConfig for TestDefaultConfig {} #[frame_support::register_default_impl(TestDefaultConfig)] diff --git a/substrate/frame/sudo/src/mock.rs b/substrate/frame/sudo/src/mock.rs index 3b907a27168366c4b4b56ceabde60d78ff9e42fd..a3a786c4af3924a4477823018a6400090b316c57 100644 --- a/substrate/frame/sudo/src/mock.rs +++ b/substrate/frame/sudo/src/mock.rs @@ -104,7 +104,7 @@ impl Contains for BlockEverything { } } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } diff --git a/substrate/frame/support/Cargo.toml b/substrate/frame/support/Cargo.toml index 72e2d0bfa56938f1353a196f2fbfc95a0e68b48d..be60068f1220b701e15f440b11b9775a5bcbaaf1 100644 --- a/substrate/frame/support/Cargo.toml +++ b/substrate/frame/support/Cargo.toml @@ -18,13 +18,24 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = { version = "6.1", default-features = false } serde = { features = ["alloc", "derive"], workspace = true } -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive", "max-encoded-len"] } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -frame-metadata = { version = "16.0.0", default-features = false, features = ["current"] } -sp-api = { path = "../../primitives/api", default-features = false, features = ["frame-metadata"] } +codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ + "derive", + "max-encoded-len", +] } +scale-info = { version = "2.10.0", default-features = false, features = [ + "derive", +] } +frame-metadata = { version = "16.0.0", default-features = false, features = [ + "current", +] } +sp-api = { path = "../../primitives/api", default-features = false, features = [ + "frame-metadata", +] } sp-std = { path = "../../primitives/std", default-features = false } sp-io = { path = "../../primitives/io", default-features = false } -sp-runtime = { path = "../../primitives/runtime", default-features = false, features = ["serde"] } +sp-runtime = { path = "../../primitives/runtime", default-features = false, features = [ + "serde", +] } sp-tracing = { path = "../../primitives/tracing", default-features = false } sp-core = { path = "../../primitives/core", default-features = false } sp-arithmetic = { path = "../../primitives/arithmetic", default-features = false } @@ -55,6 +66,7 @@ aquamarine = { version = "0.5.0" } [dev-dependencies] assert_matches = "1.3.0" pretty_assertions = "1.2.1" +sp-timestamp = { path = "../../primitives/timestamp", default-features = false } frame-system = { path = "../system" } sp-crypto-hashing = { path = "../../primitives/crypto/hashing" } @@ -83,6 +95,7 @@ std = [ "sp-staking/std", "sp-state-machine/std", "sp-std/std", + "sp-timestamp/std", "sp-tracing/std", "sp-weights/std", ] @@ -96,7 +109,9 @@ try-runtime = [ "sp-debug-derive/force-debug", "sp-runtime/try-runtime", ] -experimental = [] +experimental = [ + "frame-support-procedural/experimental", +] # By default some types have documentation, `no-metadata-docs` allows to reduce the documentation # in the metadata. no-metadata-docs = [ diff --git a/substrate/frame/support/procedural/Cargo.toml b/substrate/frame/support/procedural/Cargo.toml index 859475038020aba9afa0a9aa110c51d699cc5358..dd0688f2ad06a94332bda9f5401a5578aae04e28 100644 --- a/substrate/frame/support/procedural/Cargo.toml +++ b/substrate/frame/support/procedural/Cargo.toml @@ -38,6 +38,7 @@ regex = "1" default = ["std"] std = ["sp-crypto-hashing/std"] no-metadata-docs = [] +experimental = [] # Generate impl-trait for tuples with the given number of tuples. Will be needed as the number of # pallets in a runtime grows. Does increase the compile time! tuples-96 = [] diff --git a/substrate/frame/support/procedural/src/benchmark.rs b/substrate/frame/support/procedural/src/benchmark.rs index 6ded82d91aa5cd9bb9cceb4594ae9768d4948483..0a62c3f92a6eeef38a84a6b2e915f1103d395fa1 100644 --- a/substrate/frame/support/procedural/src/benchmark.rs +++ b/substrate/frame/support/procedural/src/benchmark.rs @@ -40,10 +40,14 @@ mod keywords { custom_keyword!(benchmarks); custom_keyword!(block); custom_keyword!(extra); + custom_keyword!(pov_mode); custom_keyword!(extrinsic_call); custom_keyword!(skip_meta); custom_keyword!(BenchmarkError); custom_keyword!(Result); + custom_keyword!(MaxEncodedLen); + custom_keyword!(Measured); + custom_keyword!(Ignored); pub const BENCHMARK_TOKEN: &str = stringify!(benchmark); pub const BENCHMARKS_TOKEN: &str = stringify!(benchmarks); @@ -65,6 +69,7 @@ struct RangeArgs { start: syn::GenericArgument, _comma: Comma, end: syn::GenericArgument, + _trailing_comma: Option, _gt_token: Gt, } @@ -72,51 +77,158 @@ struct RangeArgs { struct BenchmarkAttrs { skip_meta: bool, extra: bool, + pov_mode: Option, } /// Represents a single benchmark option -enum BenchmarkAttrKeyword { +enum BenchmarkAttr { Extra, SkipMeta, + /// How the PoV should be measured. + PoV(PovModeAttr), } -impl syn::parse::Parse for BenchmarkAttrKeyword { +impl syn::parse::Parse for PovModeAttr { + fn parse(input: ParseStream) -> Result { + let _pov: keywords::pov_mode = input.parse()?; + let _eq: Token![=] = input.parse()?; + let root = PovEstimationMode::parse(input)?; + + let mut maybe_content = None; + let _ = || -> Result<()> { + let content; + syn::braced!(content in input); + maybe_content = Some(content); + Ok(()) + }(); + + let per_key = match maybe_content { + Some(content) => { + let per_key = Punctuated::::parse_terminated(&content)?; + per_key.into_iter().collect() + }, + None => Vec::new(), + }; + + Ok(Self { root, per_key }) + } +} + +impl syn::parse::Parse for BenchmarkAttr { fn parse(input: ParseStream) -> Result { let lookahead = input.lookahead1(); if lookahead.peek(keywords::extra) { let _extra: keywords::extra = input.parse()?; - return Ok(BenchmarkAttrKeyword::Extra) + Ok(BenchmarkAttr::Extra) } else if lookahead.peek(keywords::skip_meta) { let _skip_meta: keywords::skip_meta = input.parse()?; - return Ok(BenchmarkAttrKeyword::SkipMeta) + Ok(BenchmarkAttr::SkipMeta) + } else if lookahead.peek(keywords::pov_mode) { + PovModeAttr::parse(input).map(BenchmarkAttr::PoV) + } else { + Err(lookahead.error()) + } + } +} + +/// A `#[pov_mode = .. { .. }]` attribute. +#[derive(Debug, Clone)] +struct PovModeAttr { + /// The root mode for this benchmarks. + root: PovEstimationMode, + /// The pov-mode for a specific key. This overwrites `root` for this key. + per_key: Vec, +} + +/// A single key-value pair inside the `{}` of a `#[pov_mode = .. { .. }]` attribute. +#[derive(Debug, Clone, derive_syn_parse::Parse)] +struct PovModeKeyAttr { + /// A specific storage key for which to set the PoV mode. + key: Path, + _underscore: Token![:], + /// The PoV mode for this key. + mode: PovEstimationMode, +} + +/// How the PoV should be estimated. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum PovEstimationMode { + /// Use the maximal encoded length as provided by [`codec::MaxEncodedLen`]. + MaxEncodedLen, + /// Measure the accessed value size in the pallet benchmarking and add some trie overhead. + Measured, + /// Do not estimate the PoV size for this storage item or benchmark. + Ignored, +} + +impl syn::parse::Parse for PovEstimationMode { + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(keywords::MaxEncodedLen) { + let _max_encoded_len: keywords::MaxEncodedLen = input.parse()?; + return Ok(PovEstimationMode::MaxEncodedLen) + } else if lookahead.peek(keywords::Measured) { + let _measured: keywords::Measured = input.parse()?; + return Ok(PovEstimationMode::Measured) + } else if lookahead.peek(keywords::Ignored) { + let _ignored: keywords::Ignored = input.parse()?; + return Ok(PovEstimationMode::Ignored) } else { return Err(lookahead.error()) } } } +impl ToString for PovEstimationMode { + fn to_string(&self) -> String { + match self { + PovEstimationMode::MaxEncodedLen => "MaxEncodedLen".into(), + PovEstimationMode::Measured => "Measured".into(), + PovEstimationMode::Ignored => "Ignored".into(), + } + } +} + +impl quote::ToTokens for PovEstimationMode { + fn to_tokens(&self, tokens: &mut TokenStream2) { + match self { + PovEstimationMode::MaxEncodedLen => tokens.extend(quote!(MaxEncodedLen)), + PovEstimationMode::Measured => tokens.extend(quote!(Measured)), + PovEstimationMode::Ignored => tokens.extend(quote!(Ignored)), + } + } +} + impl syn::parse::Parse for BenchmarkAttrs { fn parse(input: ParseStream) -> syn::Result { let mut extra = false; let mut skip_meta = false; - let args = Punctuated::::parse_terminated(&input)?; + let mut pov_mode = None; + let args = Punctuated::::parse_terminated(&input)?; + for arg in args.into_iter() { match arg { - BenchmarkAttrKeyword::Extra => { + BenchmarkAttr::Extra => { if extra { return Err(input.error("`extra` can only be specified once")) } extra = true; }, - BenchmarkAttrKeyword::SkipMeta => { + BenchmarkAttr::SkipMeta => { if skip_meta { return Err(input.error("`skip_meta` can only be specified once")) } skip_meta = true; }, + BenchmarkAttr::PoV(mode) => { + if pov_mode.is_some() { + return Err(input.error("`pov_mode` can only be specified once")) + } + pov_mode = Some(mode); + }, } } - Ok(BenchmarkAttrs { extra, skip_meta }) + Ok(BenchmarkAttrs { extra, skip_meta, pov_mode }) } } @@ -137,7 +249,7 @@ impl BenchmarkCallDef { } } -/// Represents a parsed `#[benchmark]` or `#[instance_banchmark]` item. +/// Represents a parsed `#[benchmark]` or `#[instance_benchmark]` item. #[derive(Clone)] struct BenchmarkDef { params: Vec, @@ -343,6 +455,7 @@ pub fn benchmarks( tokens: TokenStream, instance: bool, ) -> syn::Result { + let krate = generate_access_from_frame_or_crate("frame-benchmarking")?; // gather module info let module: ItemMod = syn::parse(tokens)?; let mod_span = module.span(); @@ -353,7 +466,7 @@ pub fn benchmarks( let mod_vis = module.vis; let mod_name = module.ident; - // consume #[benchmarks] attribute by exclusing it from mod_attrs + // consume #[benchmarks] attribute by excluding it from mod_attrs let mod_attrs: Vec<&Attribute> = module .attrs .iter() @@ -363,6 +476,8 @@ pub fn benchmarks( let mut benchmark_names: Vec = Vec::new(); let mut extra_benchmark_names: Vec = Vec::new(); let mut skip_meta_benchmark_names: Vec = Vec::new(); + // Map benchmarks to PoV modes. + let mut pov_modes = Vec::new(); let (_brace, mut content) = module.content.ok_or(syn::Error::new(mod_span, "Module cannot be empty!"))?; @@ -399,6 +514,25 @@ pub fn benchmarks( } else if benchmark_attrs.skip_meta { skip_meta_benchmark_names.push(name.clone()); } + + if let Some(mode) = benchmark_attrs.pov_mode { + let mut modes = Vec::new(); + // We cannot expand strings here since it is no-std, but syn does not expand bytes. + let name = name.to_string(); + let m = mode.root.to_string(); + modes.push(quote!(("ALL".as_bytes().to_vec(), #m.as_bytes().to_vec()))); + + for attr in mode.per_key.iter() { + // syn always puts spaces in quoted paths: + let key = attr.key.clone().into_token_stream().to_string().replace(" ", ""); + let mode = attr.mode.to_string(); + modes.push(quote!((#key.as_bytes().to_vec(), #mode.as_bytes().to_vec()))); + } + + pov_modes.push( + quote!((#name.as_bytes().to_vec(), #krate::__private::vec![#(#modes),*])), + ); + } } // expand benchmark @@ -418,7 +552,6 @@ pub fn benchmarks( true => quote!(T: Config, I: 'static), }; - let krate = generate_access_from_frame_or_crate("frame-benchmarking")?; let frame_system = generate_access_from_frame_or_crate("frame-system")?; // benchmark name variables @@ -431,7 +564,7 @@ pub fn benchmarks( let mut benchmarks_by_name_mappings: Vec = Vec::new(); let test_idents: Vec = benchmark_names_str .iter() - .map(|n| Ident::new(format!("test_{}", n).as_str(), Span::call_site())) + .map(|n| Ident::new(format!("test_benchmark_{}", n).as_str(), Span::call_site())) .collect(); for i in 0..benchmark_names.len() { let name_ident = &benchmark_names[i]; @@ -441,6 +574,37 @@ pub fn benchmarks( benchmarks_by_name_mappings.push(quote!(#name_str => Self::#test_ident())) } + let impl_test_function = content + .iter_mut() + .find_map(|item| { + let Item::Macro(item_macro) = item else { + return None; + }; + + if !item_macro + .mac + .path + .segments + .iter() + .any(|s| s.ident == "impl_benchmark_test_suite") + { + return None; + } + + let tokens = item_macro.mac.tokens.clone(); + *item = Item::Verbatim(quote! {}); + + Some(quote! { + impl_test_function!( + (#( {} #benchmark_names )*) + (#( #extra_benchmark_names )*) + (#( #skip_meta_benchmark_names )*) + #tokens + ); + }) + }) + .unwrap_or(quote! {}); + // emit final quoted tokens let res = quote! { #(#mod_attrs) @@ -505,6 +669,16 @@ pub fn benchmarks( ]; all_names.retain(|x| !extra.contains(x)); } + let pov_modes: + #krate::__private::Vec<( + #krate::__private::Vec, + #krate::__private::Vec<( + #krate::__private::Vec, + #krate::__private::Vec + )>, + )> = #krate::__private::vec![ + #( #pov_modes ),* + ]; all_names.into_iter().map(|benchmark| { let selected_benchmark = match benchmark { #(#selected_benchmark_mappings), @@ -512,12 +686,13 @@ pub fn benchmarks( _ => panic!("all benchmarks should be selectable") }; let components = >::components(&selected_benchmark); + let name = benchmark.as_bytes().to_vec(); + let modes = pov_modes.iter().find(|p| p.0 == name).map(|p| p.1.clone()); + #krate::BenchmarkMetadata { name: benchmark.as_bytes().to_vec(), components, - // TODO: Not supported by V2 syntax as of yet. - // https://github.com/paritytech/substrate/issues/13132 - pov_modes: #krate::__private::vec![], + pov_modes: modes.unwrap_or_default(), } }).collect::<#krate::__private::Vec<_>>() } @@ -676,6 +851,8 @@ pub fn benchmarks( } } } + + #impl_test_function } #mod_vis use #mod_name::*; }; @@ -733,7 +910,8 @@ fn expand_benchmark( let setup_stmts = benchmark_def.setup_stmts; let verify_stmts = benchmark_def.verify_stmts; let last_stmt = benchmark_def.last_stmt; - let test_ident = Ident::new(format!("test_{}", name.to_string()).as_str(), Span::call_site()); + let test_ident = + Ident::new(format!("test_benchmark_{}", name.to_string()).as_str(), Span::call_site()); // unroll params (prepare for quoting) let unrolled = UnrolledParams::from(&benchmark_def.params); diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/config.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/config.rs index ffe55bceb80ef96bc9e7814cfbd8bec6c62ab562..dbbe6ba6e6c32ec09a8514033108617883075243 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/config.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/config.rs @@ -76,10 +76,6 @@ pub fn expand_outer_config( #fields } - #[cfg(any(feature = "std", test))] - #[deprecated(note = "GenesisConfig is planned to be removed in December 2023. Use `RuntimeGenesisConfig` instead.")] - pub type GenesisConfig = RuntimeGenesisConfig; - #[cfg(any(feature = "std", test))] impl #scrate::sp_runtime::BuildStorage for RuntimeGenesisConfig { fn assimilate_storage( @@ -99,6 +95,17 @@ pub fn expand_outer_config( ::on_genesis(); } } + + /// Test the `Default` derive impl of the `RuntimeGenesisConfig`. + #[cfg(test)] + #[test] + fn test_genesis_config_builds() { + #scrate::__private::sp_io::TestExternalities::default().execute_with(|| { + ::build( + &RuntimeGenesisConfig::default() + ); + }); + } } } diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/inherent.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/inherent.rs index 34b9d21d8ce84fafb7958278227a3f572d8d2f74..da483fa6cf0b6aaa724792f724482e88027fa696 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/inherent.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/inherent.rs @@ -204,47 +204,50 @@ pub fn expand_outer_inherent( } } + impl #scrate::traits::IsInherent<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic> for #runtime { + fn is_inherent(ext: &<#block as #scrate::sp_runtime::traits::Block>::Extrinsic) -> bool { + use #scrate::inherent::ProvideInherent; + use #scrate::traits::{IsSubType, ExtrinsicCall}; + + if #scrate::sp_runtime::traits::Extrinsic::is_signed(ext).unwrap_or(false) { + // Signed extrinsics are never inherents. + return false + } + + #( + #pallet_attrs + { + let call = <#unchecked_extrinsic as ExtrinsicCall>::call(ext); + if let Some(call) = IsSubType::<_>::is_sub_type(call) { + if <#pallet_names as ProvideInherent>::is_inherent(&call) { + return true; + } + } + } + )* + false + } + } + impl #scrate::traits::EnsureInherentsAreFirst<#block> for #runtime { - fn ensure_inherents_are_first(block: &#block) -> Result<(), u32> { + fn ensure_inherents_are_first(block: &#block) -> Result { use #scrate::inherent::ProvideInherent; use #scrate::traits::{IsSubType, ExtrinsicCall}; use #scrate::sp_runtime::traits::Block as _; - let mut first_signed_observed = false; + let mut num_inherents = 0u32; for (i, xt) in block.extrinsics().iter().enumerate() { - let is_signed = #scrate::sp_runtime::traits::Extrinsic::is_signed(xt) - .unwrap_or(false); - - let is_inherent = if is_signed { - // Signed extrinsics are not inherents. - false - } else { - let mut is_inherent = false; - #( - #pallet_attrs - { - let call = <#unchecked_extrinsic as ExtrinsicCall>::call(xt); - if let Some(call) = IsSubType::<_>::is_sub_type(call) { - if #pallet_names::is_inherent(&call) { - is_inherent = true; - } - } - } - )* - is_inherent - }; - - if !is_inherent { - first_signed_observed = true; - } + if >::is_inherent(xt) { + if num_inherents != i as u32 { + return Err(i as u32); + } - if first_signed_observed && is_inherent { - return Err(i as u32) + num_inherents += 1; // Safe since we are in an `enumerate` loop. } } - Ok(()) + Ok(num_inherents) } } } diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs index b421d2aaffabe077450d81e3396976f3067b264a..83049919d01c34f05ce7be6725ab8bc60731f47b 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs @@ -104,7 +104,7 @@ pub fn expand_outer_origin( #[doc = #doc_string] #[derive(Clone)] pub struct RuntimeOrigin { - caller: OriginCaller, + pub caller: OriginCaller, filter: #scrate::__private::sp_std::rc::Rc::RuntimeCall) -> bool>>, } diff --git a/substrate/frame/support/procedural/src/construct_runtime/mod.rs b/substrate/frame/support/procedural/src/construct_runtime/mod.rs index 54ed15f7b1d36d6ee5eb724381152af03bef60c8..b083abbb2a8db65e1abd1f0cc2e4a15dbd922e2d 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/mod.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/mod.rs @@ -24,7 +24,7 @@ //! - Implicitly: `System: frame_system` //! - Explicitly: `System: frame_system::{Pallet, Call}` //! -//! The `construct_runtime` transitions from the implicit definition to the explict one. +//! The `construct_runtime` transitions from the implicit definition to the explicit one. //! From the explicit state, Substrate expands the pallets with additional information //! that is to be included in the runtime metadata. This expansion makes visible some extra //! parts of the pallets, mainly the `Error` if defined. The expanded state looks like @@ -55,7 +55,7 @@ //! +----------+ +------------------+ //! ``` //! -//! When all pallet parts are implcit, then the `construct_runtime!` macro expands to its final +//! When all pallet parts are implicit, then the `construct_runtime!` macro expands to its final //! state, the `ExplicitExpanded`. Otherwise, all implicit parts are converted to an explicit //! expanded part allow the `construct_runtime!` to expand any remaining explicit parts to an //! explicit expanded part. @@ -202,14 +202,14 @@ //! Similarly to the previous transition, the macro expansion transforms `System: //! frame_system::{Pallet, Call}` into `System: frame_system expanded::{Error} ::{Pallet, Call}`. //! The `expanded` section adds extra parts that the Substrate would like to expose for each pallet -//! by default. This is done to expose the approprite types for metadata construction. +//! by default. This is done to expose the appropriate types for metadata construction. //! //! This time, instead of calling `tt_default_parts` we are using the `tt_extra_parts` macro. //! This macro returns the ` :: expanded { Error }` list of additional parts we would like to //! expose. -mod expand; -mod parse; +pub(crate) mod expand; +pub(crate) mod parse; use crate::pallet::parse::helper::two128_str; use cfg_expr::Predicate; @@ -233,25 +233,38 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream { let input_copy = input.clone(); let definition = syn::parse_macro_input!(input as RuntimeDeclaration); - let res = match definition { - RuntimeDeclaration::Implicit(implicit_def) => - check_pallet_number(input_copy.clone().into(), implicit_def.pallets.len()).and_then( - |_| construct_runtime_implicit_to_explicit(input_copy.into(), implicit_def), - ), - RuntimeDeclaration::Explicit(explicit_decl) => check_pallet_number( - input_copy.clone().into(), - explicit_decl.pallets.len(), - ) - .and_then(|_| { - construct_runtime_explicit_to_explicit_expanded(input_copy.into(), explicit_decl) - }), - RuntimeDeclaration::ExplicitExpanded(explicit_decl) => - check_pallet_number(input_copy.into(), explicit_decl.pallets.len()) - .and_then(|_| construct_runtime_final_expansion(explicit_decl)), + let (check_pallet_number_res, res) = match definition { + RuntimeDeclaration::Implicit(implicit_def) => ( + check_pallet_number(input_copy.clone().into(), implicit_def.pallets.len()), + construct_runtime_implicit_to_explicit(input_copy.into(), implicit_def), + ), + RuntimeDeclaration::Explicit(explicit_decl) => ( + check_pallet_number(input_copy.clone().into(), explicit_decl.pallets.len()), + construct_runtime_explicit_to_explicit_expanded(input_copy.into(), explicit_decl), + ), + RuntimeDeclaration::ExplicitExpanded(explicit_decl) => ( + check_pallet_number(input_copy.into(), explicit_decl.pallets.len()), + construct_runtime_final_expansion(explicit_decl), + ), }; let res = res.unwrap_or_else(|e| e.to_compile_error()); + // We want to provide better error messages to the user and thus, handle the error here + // separately. If there is an error, we print the error and still generate all of the code to + // get in overall less errors for the user. + let res = if let Err(error) = check_pallet_number_res { + let error = error.to_compile_error(); + + quote! { + #error + + #res + } + } else { + res + }; + let res = expander::Expander::new("construct_runtime") .dry(std::env::var("EXPAND_MACROS").is_err()) .verbose(true) @@ -502,7 +515,7 @@ fn construct_runtime_final_expansion( Ok(res) } -fn decl_all_pallets<'a>( +pub(crate) fn decl_all_pallets<'a>( runtime: &'a Ident, pallet_declarations: impl Iterator, features: &HashSet<&str>, @@ -611,7 +624,8 @@ fn decl_all_pallets<'a>( #( #all_pallets_without_system )* ) } -fn decl_pallet_runtime_setup( + +pub(crate) fn decl_pallet_runtime_setup( runtime: &Ident, pallet_declarations: &[Pallet], scrate: &TokenStream2, @@ -717,7 +731,7 @@ fn decl_pallet_runtime_setup( ) } -fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 { +pub(crate) fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 { quote!( #[cfg(test)] mod __construct_runtime_integrity_test { @@ -732,7 +746,7 @@ fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 { ) } -fn decl_static_assertions( +pub(crate) fn decl_static_assertions( runtime: &Ident, pallet_decls: &[Pallet], scrate: &TokenStream2, @@ -763,7 +777,7 @@ fn decl_static_assertions( } } -fn check_pallet_number(input: TokenStream2, pallet_num: usize) -> Result<()> { +pub(crate) fn check_pallet_number(input: TokenStream2, pallet_num: usize) -> Result<()> { let max_pallet_num = { if cfg!(feature = "tuples-96") { 96 diff --git a/substrate/frame/support/procedural/src/construct_runtime/parse.rs b/substrate/frame/support/procedural/src/construct_runtime/parse.rs index 88f3f14dc86c541ef949beced9feaf222e9a3146..31866c787b0f339d6d83b810476e840216f70bbc 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/parse.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/parse.rs @@ -322,7 +322,7 @@ impl Parse for PalletDeclaration { /// A struct representing a path to a pallet. `PalletPath` is almost identical to the standard /// Rust path with a few restrictions: /// - No leading colons allowed -/// - Path segments can only consist of identifers separated by colons +/// - Path segments can only consist of identifiers separated by colons #[derive(Debug, Clone)] pub struct PalletPath { pub inner: Path, @@ -595,7 +595,7 @@ pub struct Pallet { pub is_expanded: bool, /// The name of the pallet, e.g.`System` in `System: frame_system`. pub name: Ident, - /// Either automatically infered, or defined (e.g. `MyPallet ... = 3,`). + /// Either automatically inferred, or defined (e.g. `MyPallet ... = 3,`). pub index: u8, /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. pub path: PalletPath, @@ -634,7 +634,7 @@ impl Pallet { /// +----------+ +----------+ +------------------+ /// ``` enum PalletsConversion { - /// Pallets implicitely declare parts. + /// Pallets implicitly declare parts. /// /// `System: frame_system`. Implicit(Vec), @@ -648,7 +648,7 @@ enum PalletsConversion { /// Pallets explicitly declare parts that are fully expanded. /// /// This is the end state that contains extra parts included by - /// default by Subtrate. + /// default by Substrate. /// /// `System: frame_system expanded::{Error} ::{Pallet, Call}` /// @@ -660,7 +660,7 @@ enum PalletsConversion { /// /// Check if all pallet have explicit declaration of their parts, if so then assign index to each /// pallet using same rules as rust for fieldless enum. I.e. implicit are assigned number -/// incrementedly from last explicit or 0. +/// incrementally from last explicit or 0. fn convert_pallets(pallets: Vec) -> syn::Result { if pallets.iter().any(|pallet| pallet.pallet_parts.is_none()) { return Ok(PalletsConversion::Implicit(pallets)) diff --git a/substrate/frame/support/procedural/src/derive_impl.rs b/substrate/frame/support/procedural/src/derive_impl.rs index d6d5bf68efd5689af2e96250e24cef3cd7faf47b..54755f1163a1de279cad99b16f9500ccb39956eb 100644 --- a/substrate/frame/support/procedural/src/derive_impl.rs +++ b/substrate/frame/support/procedural/src/derive_impl.rs @@ -172,6 +172,33 @@ fn combine_impls( final_impl } +/// Computes the disambiguation path for the `derive_impl` attribute macro. +/// +/// When specified explicitly using `as [disambiguation_path]` in the macro attr, the +/// disambiguation is used as is. If not, we infer the disambiguation path from the +/// `foreign_impl_path` and the computed scope. +fn compute_disambiguation_path( + disambiguation_path: Option, + foreign_impl: ItemImpl, + default_impl_path: Path, +) -> Result { + match (disambiguation_path, foreign_impl.clone().trait_) { + (Some(disambiguation_path), _) => Ok(disambiguation_path), + (None, Some((_, foreign_impl_path, _))) => + if default_impl_path.segments.len() > 1 { + let scope = default_impl_path.segments.first(); + Ok(parse_quote!(#scope :: #foreign_impl_path)) + } else { + Ok(foreign_impl_path) + }, + _ => Err(syn::Error::new( + default_impl_path.span(), + "Impl statement must have a defined type being implemented \ + for a defined type such as `impl A for B`", + )), + } +} + /// Internal implementation behind [`#[derive_impl(..)]`](`macro@crate::derive_impl`). /// /// `default_impl_path`: the module path of the external `impl` statement whose tokens we are @@ -194,18 +221,11 @@ pub fn derive_impl( let foreign_impl = parse2::(foreign_tokens)?; let default_impl_path = parse2::(default_impl_path)?; - // have disambiguation_path default to the item being impl'd in the foreign impl if we - // don't specify an `as [disambiguation_path]` in the macro attr - let disambiguation_path = match (disambiguation_path, foreign_impl.clone().trait_) { - (Some(disambiguation_path), _) => disambiguation_path, - (None, Some((_, foreign_impl_path, _))) => foreign_impl_path, - _ => - return Err(syn::Error::new( - foreign_impl.span(), - "Impl statement must have a defined type being implemented \ - for a defined type such as `impl A for B`", - )), - }; + let disambiguation_path = compute_disambiguation_path( + disambiguation_path, + foreign_impl.clone(), + default_impl_path.clone(), + )?; // generate the combined impl let combined_impl = combine_impls( @@ -257,3 +277,27 @@ fn test_runtime_type_with_doc() { } } } + +#[test] +fn test_disambiguation_path() { + let foreign_impl: ItemImpl = parse_quote!(impl SomeTrait for SomeType {}); + let default_impl_path: Path = parse_quote!(SomeScope::SomeType); + + // disambiguation path is specified + let disambiguation_path = compute_disambiguation_path( + Some(parse_quote!(SomeScope::SomePath)), + foreign_impl.clone(), + default_impl_path.clone(), + ); + assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeScope::SomePath)); + + // disambiguation path is not specified and the default_impl_path has more than one segment + let disambiguation_path = + compute_disambiguation_path(None, foreign_impl.clone(), default_impl_path.clone()); + assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeScope::SomeTrait)); + + // disambiguation path is not specified and the default_impl_path has only one segment + let disambiguation_path = + compute_disambiguation_path(None, foreign_impl.clone(), parse_quote!(SomeType)); + assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeTrait)); +} diff --git a/substrate/frame/support/procedural/src/dynamic_params.rs b/substrate/frame/support/procedural/src/dynamic_params.rs index b718ccbc9558480587a1f50f4e85a992e5c750da..29399a885bc671ddfeefe3d8eefba3d235ad4899 100644 --- a/substrate/frame/support/procedural/src/dynamic_params.rs +++ b/substrate/frame/support/procedural/src/dynamic_params.rs @@ -147,8 +147,8 @@ fn ensure_codec_index(attrs: &Vec, span: Span) -> Result<()> { /// Used to inject arguments into the inner `#[dynamic_pallet_params(..)]` attribute. /// -/// This allows the outer `#[dynamic_params(..)]` attribute to specify some arguments that dont need -/// to be repeated every time. +/// This allows the outer `#[dynamic_params(..)]` attribute to specify some arguments that don't +/// need to be repeated every time. struct MacroInjectArgs { runtime_params: syn::Ident, params_pallet: syn::Type, @@ -311,7 +311,7 @@ impl ToTokens for DynamicPalletParamAttr { )* } - impl #scrate::traits::dynamic_params::AggregratedKeyValue for Parameters { + impl #scrate::traits::dynamic_params::AggregatedKeyValue for Parameters { type Key = #key_ident; type Value = #value_ident; @@ -497,7 +497,7 @@ impl ToTokens for DynamicParamAggregatedEnum { #vis enum #params_key_ident { #( #(#attributes)* - #param_names(<#param_types as #scrate::traits::dynamic_params::AggregratedKeyValue>::Key), + #param_names(<#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Key), )* } @@ -515,11 +515,11 @@ impl ToTokens for DynamicParamAggregatedEnum { #vis enum #params_value_ident { #( #(#attributes)* - #param_names(<#param_types as #scrate::traits::dynamic_params::AggregratedKeyValue>::Value), + #param_names(<#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Value), )* } - impl #scrate::traits::dynamic_params::AggregratedKeyValue for #name { + impl #scrate::traits::dynamic_params::AggregatedKeyValue for #name { type Key = #params_key_ident; type Value = #params_value_ident; @@ -536,13 +536,13 @@ impl ToTokens for DynamicParamAggregatedEnum { } #( - impl ::core::convert::From<<#param_types as #scrate::traits::dynamic_params::AggregratedKeyValue>::Key> for #params_key_ident { - fn from(key: <#param_types as #scrate::traits::dynamic_params::AggregratedKeyValue>::Key) -> Self { + impl ::core::convert::From<<#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Key> for #params_key_ident { + fn from(key: <#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Key) -> Self { #params_key_ident::#param_names(key) } } - impl ::core::convert::TryFrom<#params_value_ident> for <#param_types as #scrate::traits::dynamic_params::AggregratedKeyValue>::Value { + impl ::core::convert::TryFrom<#params_value_ident> for <#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Value { type Error = (); fn try_from(value: #params_value_ident) -> Result { diff --git a/substrate/frame/support/procedural/src/lib.rs b/substrate/frame/support/procedural/src/lib.rs index 20b8d74310f3e4aca7a5cae61fe3e5d72ec0626c..bc62c0509b05b2c8cc1f2bf518c58748baaea0de 100644 --- a/substrate/frame/support/procedural/src/lib.rs +++ b/substrate/frame/support/procedural/src/lib.rs @@ -31,6 +31,7 @@ mod match_and_insert; mod no_bound; mod pallet; mod pallet_error; +mod runtime; mod storage_alias; mod transactional; mod tt_macro; @@ -136,7 +137,7 @@ fn counter_prefix(prefix: &str) -> String { /// - `Call` - If the pallet has callable functions /// - `Storage` - If the pallet uses storage /// - `Event` or `Event` (if the event is generic) - If the pallet emits events -/// - `Origin` or `Origin` (if the origin is generic) - If the pallet has instanciable origins +/// - `Origin` or `Origin` (if the origin is generic) - If the pallet has instantiable origins /// - `Config` or `Config` (if the config is generic) - If the pallet builds the genesis /// storage with `GenesisConfig` /// - `Inherent` - If the pallet provides/can check inherents. @@ -165,7 +166,7 @@ fn counter_prefix(prefix: &str) -> String { /// and `Event` are encoded, and to define the ModuleToIndex value. /// /// if `= $n` is not given, then index is resolved in the same way as fieldless enum in Rust -/// (i.e. incrementedly from previous index): +/// (i.e. incrementally from previous index): /// ```nocompile /// pallet1 .. = 2, /// pallet2 .., // Here pallet2 is given index 3 @@ -188,133 +189,11 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream { construct_runtime::construct_runtime(input) } -/// The pallet struct placeholder `#[pallet::pallet]` is mandatory and allows you to specify -/// pallet information. /// -/// The struct must be defined as follows: -/// ```ignore -/// #[pallet::pallet] -/// pub struct Pallet(_); -/// ``` -/// I.e. a regular struct definition named `Pallet`, with generic T and no where clause. -/// -/// ## Macro expansion: -/// -/// The macro adds this attribute to the struct definition: -/// ```ignore -/// #[derive( -/// frame_support::CloneNoBound, -/// frame_support::EqNoBound, -/// frame_support::PartialEqNoBound, -/// frame_support::RuntimeDebugNoBound, -/// )] -/// ``` -/// and replaces the type `_` with `PhantomData`. It also implements on the pallet: -/// * `GetStorageVersion` -/// * `OnGenesis`: contains some logic to write the pallet version into storage. -/// * `PalletErrorTypeInfo`: provides the type information for the pallet error, if defined. -/// -/// It declares `type Module` type alias for `Pallet`, used by `construct_runtime`. -/// -/// It implements `PalletInfoAccess` on `Pallet` to ease access to pallet information given by -/// `frame_support::traits::PalletInfo`. (The implementation uses the associated type -/// `frame_system::Config::PalletInfo`). -/// -/// It implements `StorageInfoTrait` on `Pallet` which give information about all storages. -/// -/// If the attribute `generate_store` is set then the macro creates the trait `Store` and -/// implements it on `Pallet`. -/// -/// If the attribute `set_storage_max_encoded_len` is set then the macro calls -/// `StorageInfoTrait` for each storage in the implementation of `StorageInfoTrait` for the -/// pallet. Otherwise it implements `StorageInfoTrait` for the pallet using the -/// `PartialStorageInfoTrait` implementation of storages. -/// -/// ## Dev Mode (`#[pallet(dev_mode)]`) -/// -/// Specifying the argument `dev_mode` will allow you to enable dev mode for a pallet. The aim -/// of dev mode is to loosen some of the restrictions and requirements placed on production -/// pallets for easy tinkering and development. Dev mode pallets should not be used in -/// production. Enabling dev mode has the following effects: -/// -/// * Weights no longer need to be specified on every `#[pallet::call]` declaration. By default, dev -/// mode pallets will assume a weight of zero (`0`) if a weight is not specified. This is -/// equivalent to specifying `#[weight(0)]` on all calls that do not specify a weight. -/// * Call indices no longer need to be specified on every `#[pallet::call]` declaration. By -/// default, dev mode pallets will assume a call index based on the order of the call. -/// * All storages are marked as unbounded, meaning you do not need to implement `MaxEncodedLen` on -/// storage types. This is equivalent to specifying `#[pallet::unbounded]` on all storage type -/// definitions. -/// * Storage hashers no longer need to be specified and can be replaced by `_`. In dev mode, these -/// will be replaced by `Blake2_128Concat`. In case of explicit key-binding, `Hasher` can simply -/// be ignored when in `dev_mode`. -/// -/// Note that the `dev_mode` argument can only be supplied to the `#[pallet]` or -/// `#[frame_support::pallet]` attribute macro that encloses your pallet module. This argument -/// cannot be specified anywhere else, including but not limited to the `#[pallet::pallet]` -/// attribute macro. -/// -///
-/// WARNING:
-/// You should not deploy or use dev mode pallets in production. Doing so can break your chain
-/// and therefore should never be done. Once you are done tinkering, you should remove the
-/// 'dev_mode' argument from your #[pallet] declaration and fix any compile errors before
-/// attempting to use your pallet in a production scenario.
-/// 
-/// -/// See `frame_support::pallet` docs for more info. -/// -/// ## Runtime Metadata Documentation -/// -/// The documentation added to this pallet is included in the runtime metadata. -/// -/// The documentation can be defined in the following ways: -/// -/// ```ignore -/// #[pallet::pallet] -/// /// Documentation for pallet 1 -/// #[doc = "Documentation for pallet 2"] -/// #[doc = include_str!("../README.md")] -/// #[pallet_doc("../doc1.md")] -/// #[pallet_doc("../doc2.md")] -/// pub mod pallet {} -/// ``` -/// -/// The runtime metadata for this pallet contains the following -/// - " Documentation for pallet 1" (captured from `///`) -/// - "Documentation for pallet 2" (captured from `#[doc]`) -/// - content of ../README.md (captured from `#[doc]` with `include_str!`) -/// - content of "../doc1.md" (captured from `pallet_doc`) -/// - content of "../doc2.md" (captured from `pallet_doc`) -/// -/// ### `doc` attribute -/// -/// The value of the `doc` attribute is included in the runtime metadata, as well as -/// expanded on the pallet module. The previous example is expanded to: -/// -/// ```ignore -/// /// Documentation for pallet 1 -/// /// Documentation for pallet 2 -/// /// Content of README.md -/// pub mod pallet {} -/// ``` -/// -/// If you want to specify the file from which the documentation is loaded, you can use the -/// `include_str` macro. However, if you only want the documentation to be included in the -/// runtime metadata, use the `pallet_doc` attribute. -/// -/// ### `pallet_doc` attribute -/// -/// Unlike the `doc` attribute, the documentation provided to the `pallet_doc` attribute is -/// not inserted on the module. -/// -/// The `pallet_doc` attribute can only be provided with one argument, -/// which is the file path that holds the documentation to be added to the metadata. +/// --- /// -/// This approach is beneficial when you use the `include_str` macro at the beginning of the file -/// and want that documentation to extend to the runtime metadata, without reiterating the -/// documentation on the pallet module itself. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet`. #[proc_macro_attribute] pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream { pallet::pallet(attr, item) @@ -408,19 +287,28 @@ pub fn transactional(attr: TokenStream, input: TokenStream) -> TokenStream { transactional::transactional(attr, input).unwrap_or_else(|e| e.to_compile_error().into()) } +/// +/// --- +/// +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::require_transactional`. #[proc_macro_attribute] pub fn require_transactional(attr: TokenStream, input: TokenStream) -> TokenStream { transactional::require_transactional(attr, input) .unwrap_or_else(|e| e.to_compile_error().into()) } -/// Derive [`Clone`] but do not bound any generic. Docs are at `frame_support::CloneNoBound`. +/// Derive [`Clone`] but do not bound any generic. +/// +/// Docs at `frame_support::CloneNoBound`. #[proc_macro_derive(CloneNoBound)] pub fn derive_clone_no_bound(input: TokenStream) -> TokenStream { no_bound::clone::derive_clone_no_bound(input) } -/// Derive [`Debug`] but do not bound any generics. Docs are at `frame_support::DebugNoBound`. +/// Derive [`Debug`] but do not bound any generics. +/// +/// Docs at `frame_support::DebugNoBound`. #[proc_macro_derive(DebugNoBound)] pub fn derive_debug_no_bound(input: TokenStream) -> TokenStream { no_bound::debug::derive_debug_no_bound(input) @@ -452,14 +340,17 @@ pub fn derive_runtime_debug_no_bound(input: TokenStream) -> TokenStream { } } -/// Derive [`PartialEq`] but do not bound any generic. Docs are at -/// `frame_support::PartialEqNoBound`. +/// Derive [`PartialEq`] but do not bound any generic. +/// +/// Docs at `frame_support::PartialEqNoBound`. #[proc_macro_derive(PartialEqNoBound)] pub fn derive_partial_eq_no_bound(input: TokenStream) -> TokenStream { no_bound::partial_eq::derive_partial_eq_no_bound(input) } -/// Derive [`Eq`] but do no bound any generic. Docs are at `frame_support::EqNoBound`. +/// DeriveEq but do no bound any generic. +/// +/// Docs at `frame_support::EqNoBound`. #[proc_macro_derive(EqNoBound)] pub fn derive_eq_no_bound(input: TokenStream) -> TokenStream { let input = syn::parse_macro_input!(input as syn::DeriveInput); @@ -494,6 +385,7 @@ pub fn derive_default_no_bound(input: TokenStream) -> TokenStream { no_bound::default::derive_default_no_bound(input) } +/// Macro used internally in FRAME to generate the crate version for a pallet. #[proc_macro] pub fn crate_to_crate_version(input: TokenStream) -> TokenStream { crate_version::crate_to_crate_version(input) @@ -555,6 +447,11 @@ pub fn __create_tt_macro(input: TokenStream) -> TokenStream { tt_macro::create_tt_return_macro(input) } +/// +/// --- +/// +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::storage_alias`. #[proc_macro_attribute] pub fn storage_alias(attributes: TokenStream, input: TokenStream) -> TokenStream { storage_alias::storage_alias(attributes.into(), input.into()) @@ -563,7 +460,7 @@ pub fn storage_alias(attributes: TokenStream, input: TokenStream) -> TokenStream } /// This attribute can be used to derive a full implementation of a trait based on a local partial -/// impl and an external impl containing defaults that can be overriden in the local impl. +/// impl and an external impl containing defaults that can be overridden in the local impl. /// /// For a full end-to-end example, see [below](#use-case-auto-derive-test-pallet-config-traits). /// @@ -603,6 +500,11 @@ pub fn storage_alias(attributes: TokenStream, input: TokenStream) -> TokenStream /// default to `A` from the `impl A for B` part of the default impl. This is useful for scenarios /// where all of the relevant types are already in scope via `use` statements. /// +/// In case the `default_impl_path` is scoped to a different module such as +/// `some::path::TestTraitImpl`, the same scope is assumed for the `disambiguation_path`, i.e. +/// `some::A`. This enables the use of `derive_impl` attribute without having to specify the +/// `disambiguation_path` in most (if not all) uses within FRAME's context. +/// /// Conversely, the `default_impl_path` argument is required and cannot be omitted. /// /// Optionally, `no_aggregated_types` can be specified as follows: @@ -785,24 +687,21 @@ pub fn derive_impl(attrs: TokenStream, input: TokenStream) -> TokenStream { .into() } -/// The optional attribute `#[pallet::no_default]` can be attached to trait items within a -/// `Config` trait impl that has [`#[pallet::config(with_default)]`](`macro@config`) attached. /// -/// Attaching this attribute to a trait item ensures that that trait item will not be used as a -/// default with the [`#[derive_impl(..)]`](`macro@derive_impl`) attribute macro. +/// --- +/// +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::no_default`. #[proc_macro_attribute] pub fn no_default(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The optional attribute `#[pallet::no_default_bounds]` can be attached to trait items within a -/// `Config` trait impl that has [`#[pallet::config(with_default)]`](`macro@config`) attached. /// -/// Attaching this attribute to a trait item ensures that the generated trait `DefaultConfig` -/// will not have any bounds for this trait item. +/// --- /// -/// As an example, if you have a trait item `type AccountId: SomeTrait;` in your `Config` trait, -/// the generated `DefaultConfig` will only have `type AccountId;` with no trait bound. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::no_default_bounds`. #[proc_macro_attribute] pub fn no_default_bounds(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() @@ -882,6 +781,11 @@ pub fn register_default_impl(attrs: TokenStream, tokens: TokenStream) -> TokenSt } } +/// +/// --- +/// +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_prelude::inject_runtime_type`. #[proc_macro_attribute] pub fn inject_runtime_type(_: TokenStream, tokens: TokenStream) -> TokenStream { let item = tokens.clone(); @@ -915,75 +819,11 @@ fn pallet_macro_stub() -> TokenStream { .into() } -/// The mandatory attribute `#[pallet::config]` defines the configurable options for the pallet. -/// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::config] -/// pub trait Config: frame_system::Config + $optionally_some_other_supertraits -/// $optional_where_clause -/// { -/// ... -/// } -/// ``` -/// -/// I.e. a regular trait definition named `Config`, with the supertrait -/// `frame_system::pallet::Config`, and optionally other supertraits and a where clause. -/// (Specifying other supertraits here is known as [tight -/// coupling](https://docs.substrate.io/reference/how-to-guides/pallet-design/use-tight-coupling/)) -/// -/// The associated type `RuntimeEvent` is reserved. If defined, it must have the bounds -/// `From` and `IsType<::RuntimeEvent>`. -/// -/// [`pallet::event`](`macro@event`) must be present if `RuntimeEvent` exists as a config item -/// in your `#[pallet::config]`. -/// -/// ## Optional: `with_default` /// -/// An optional `with_default` argument may also be specified. Doing so will automatically -/// generate a `DefaultConfig` trait inside your pallet which is suitable for use with -/// [`[#[derive_impl(..)]`](`macro@derive_impl`) to derive a default testing config: -/// -/// ```ignore -/// #[pallet::config(with_default)] -/// pub trait Config: frame_system::Config { -/// type RuntimeEvent: Parameter -/// + Member -/// + From> -/// + Debug -/// + IsType<::RuntimeEvent>; -/// -/// #[pallet::no_default] -/// type BaseCallFilter: Contains; -/// // ... -/// } -/// ``` -/// -/// As shown above, you may also attach the [`#[pallet::no_default]`](`macro@no_default`) -/// attribute to specify that a particular trait item _cannot_ be used as a default when a test -/// `Config` is derived using the [`#[derive_impl(..)]`](`macro@derive_impl`) attribute macro. -/// This will cause that particular trait item to simply not appear in default testing configs -/// based on this config (the trait item will not be included in `DefaultConfig`). -/// -/// ### `DefaultConfig` Caveats -/// -/// The auto-generated `DefaultConfig` trait: -/// - is always a _subset_ of your pallet's `Config` trait. -/// - can only contain items that don't rely on externalities, such as `frame_system::Config`. -/// -/// Trait items that _do_ rely on externalities should be marked with -/// [`#[pallet::no_default]`](`macro@no_default`) -/// -/// Consequently: -/// - Any items that rely on externalities _must_ be marked with -/// [`#[pallet::no_default]`](`macro@no_default`) or your trait will fail to compile when used -/// with [`derive_impl`](`macro@derive_impl`). -/// - Items marked with [`#[pallet::no_default]`](`macro@no_default`) are entirely excluded from the -/// `DefaultConfig` trait, and therefore any impl of `DefaultConfig` doesn't need to implement -/// such items. +/// --- /// -/// For more information, see [`macro@derive_impl`]. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::config`. #[proc_macro_attribute] pub fn config(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() @@ -992,7 +832,7 @@ pub fn config(_: TokenStream, _: TokenStream) -> TokenStream { /// /// --- /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in +/// Rust-Analyzer Users: Documentation for this macro can be found at /// `frame_support::pallet_macros::constant`. #[proc_macro_attribute] pub fn constant(_: TokenStream, _: TokenStream) -> TokenStream { @@ -1002,106 +842,48 @@ pub fn constant(_: TokenStream, _: TokenStream) -> TokenStream { /// /// --- /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in +/// Rust-Analyzer Users: Documentation for this macro can be found at /// `frame_support::pallet_macros::constant_name`. #[proc_macro_attribute] pub fn constant_name(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// To bypass the `frame_system::Config` supertrait check, use the attribute -/// `pallet::disable_frame_system_supertrait_check`, e.g.: /// -/// ```ignore -/// #[pallet::config] -/// #[pallet::disable_frame_system_supertrait_check] -/// pub trait Config: pallet_timestamp::Config {} -/// ``` +/// --- /// -/// NOTE: Bypassing the `frame_system::Config` supertrait check is typically desirable when you -/// want to write an alternative to the `frame_system` pallet. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::disable_frame_system_supertrait_check`. #[proc_macro_attribute] pub fn disable_frame_system_supertrait_check(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// To generate a `Store` trait associating all storages, annotate your `Pallet` struct with -/// the attribute `#[pallet::generate_store($vis trait Store)]`, e.g.: /// -/// ```ignore -/// #[pallet::pallet] -/// #[pallet::generate_store(pub(super) trait Store)] -/// pub struct Pallet(_); -/// ``` -/// More precisely, the `Store` trait contains an associated type for each storage. It is -/// implemented for `Pallet` allowing access to the storage from pallet struct. -/// -/// Thus when defining a storage named `Foo`, it can later be accessed from `Pallet` using -/// `::Foo`. -/// -/// NOTE: this attribute is only valid when applied _directly_ to your `Pallet` struct -/// definition. -#[proc_macro_attribute] -pub fn generate_store(_: TokenStream, _: TokenStream) -> TokenStream { - pallet_macro_stub() -} - -/// Because the `pallet::pallet` macro implements `GetStorageVersion`, the current storage -/// version needs to be communicated to the macro. This can be done by using the -/// `pallet::storage_version` attribute: -/// -/// ```ignore -/// const STORAGE_VERSION: StorageVersion = StorageVersion::new(5); -/// -/// #[pallet::pallet] -/// #[pallet::storage_version(STORAGE_VERSION)] -/// pub struct Pallet(_); -/// ``` +/// --- /// -/// If not present, the current storage version is set to the default value. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::storage_version`. #[proc_macro_attribute] pub fn storage_version(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The `#[pallet::hooks]` attribute allows you to specify a `Hooks` implementation for -/// `Pallet` that specifies pallet-specific logic. -/// -/// The item the attribute attaches to must be defined as follows: -/// ```ignore -/// #[pallet::hooks] -/// impl Hooks> for Pallet $optional_where_clause { -/// ... -/// } -/// ``` -/// I.e. a regular trait implementation with generic bound: `T: Config`, for the trait -/// `Hooks>` (they are defined in preludes), for the type `Pallet` and -/// with an optional where clause. -/// -/// If no `#[pallet::hooks]` exists, then the following default implementation is -/// automatically generated: -/// ```ignore -/// #[pallet::hooks] -/// impl Hooks> for Pallet {} -/// ``` -/// -/// ## Macro expansion -/// -/// The macro implements the traits `OnInitialize`, `OnIdle`, `OnFinalize`, `OnRuntimeUpgrade`, -/// `OffchainWorker`, and `IntegrityTest` using the provided `Hooks` implementation. /// -/// NOTE: `OnRuntimeUpgrade` is implemented with `Hooks::on_runtime_upgrade` and some -/// additional logic. E.g. logic to write the pallet version into storage. +/// --- /// -/// NOTE: The macro also adds some tracing logic when implementing the above traits. The -/// following hooks emit traces: `on_initialize`, `on_finalize` and `on_runtime_upgrade`. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::hooks`. #[proc_macro_attribute] pub fn hooks(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// Each dispatchable needs to define a weight with `#[pallet::weight($expr)]` attribute, the -/// first argument must be `origin: OriginFor`. +/// +/// --- +/// +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::weight`. #[proc_macro_attribute] pub fn weight(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() @@ -1110,8 +892,8 @@ pub fn weight(_: TokenStream, _: TokenStream) -> TokenStream { /// /// --- /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in -/// [`frame_support::pallet_macros::call`](../../frame_support/pallet_macros/attr.call.html). +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::compact`. #[proc_macro_attribute] pub fn compact(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() @@ -1120,8 +902,8 @@ pub fn compact(_: TokenStream, _: TokenStream) -> TokenStream { /// /// --- /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in -/// [`frame_support::pallet_macros::call`](../../frame_support/pallet_macros/attr.call.html). +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::call`. #[proc_macro_attribute] pub fn call(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() @@ -1132,164 +914,60 @@ pub fn call(_: TokenStream, _: TokenStream) -> TokenStream { /// /// --- /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in -/// [`frame_support::pallet_macros::call`](../../frame_support/pallet_macros/attr.call.html). +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::call_index`. #[proc_macro_attribute] pub fn call_index(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// Each dispatchable may be annotated with the `#[pallet::feeless_if($closure)]` attribute, -/// which explicitly defines the condition for the dispatchable to be feeless. /// -/// The arguments for the closure must be the referenced arguments of the dispatchable function. -/// -/// The closure must return `bool`. -/// -/// ### Example -/// ```ignore -/// #[pallet::feeless_if(|_origin: &OriginFor, something: &u32| -> bool { -/// *something == 0 -/// })] -/// pub fn do_something(origin: OriginFor, something: u32) -> DispatchResult { -/// .... -/// } -/// ``` -/// -/// Please note that this only works for signed dispatchables and requires a signed extension -/// such as `SkipCheckIfFeeless` as defined in `pallet-skip-feeless-payment` to wrap the existing -/// payment extension. Else, this is completely ignored and the dispatchable is still charged. +/// --- /// -/// ### Macro expansion +/// Rust-Analyzer Users: Documentation for this macro can be found at /// -/// The macro implements the `CheckIfFeeless` trait on the dispatchable and calls the corresponding -/// closure in the implementation. +/// `frame_support::pallet_macros::feeless_if`. #[proc_macro_attribute] pub fn feeless_if(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// Allows you to define some extra constants to be added into constant metadata. -/// -/// Item must be defined as: /// -/// ```ignore -/// #[pallet::extra_constants] -/// impl Pallet where $optional_where_clause { -/// /// $some_doc -/// $vis fn $fn_name() -> $some_return_type { -/// ... -/// } -/// ... -/// } -/// ``` -/// I.e. a regular rust `impl` block with some optional where clause and functions with 0 args, -/// 0 generics, and some return type. +/// --- /// -/// ## Macro expansion +/// Rust-Analyzer Users: Documentation for this macro can be found at /// -/// The macro add some extra constants to pallet constant metadata. +/// `frame_support::pallet_macros::extra_constants`. #[proc_macro_attribute] pub fn extra_constants(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The `#[pallet::error]` attribute allows you to define an error enum that will be returned -/// from the dispatchable when an error occurs. The information for this error type is then -/// stored in metadata. -/// -/// Item must be defined as: /// -/// ```ignore -/// #[pallet::error] -/// pub enum Error { -/// /// $some_optional_doc -/// $SomeFieldLessVariant, -/// /// $some_more_optional_doc -/// $SomeVariantWithOneField(FieldType), -/// ... -/// } -/// ``` -/// I.e. a regular enum named `Error`, with generic `T` and fieldless or multiple-field -/// variants. -/// -/// Any field type in the enum variants must implement `TypeInfo` in order to be properly used -/// in the metadata, and its encoded size should be as small as possible, preferably 1 byte in -/// size in order to reduce storage size. The error enum itself has an absolute maximum encoded -/// size specified by `MAX_MODULE_ERROR_ENCODED_SIZE`. -/// -/// (1 byte can still be 256 different errors. The more specific the error, the easier it is to -/// diagnose problems and give a better experience to the user. Don't skimp on having lots of -/// individual error conditions.) -/// -/// Field types in enum variants must also implement `PalletError`, otherwise the pallet will -/// fail to compile. Rust primitive types have already implemented the `PalletError` trait -/// along with some commonly used stdlib types such as [`Option`] and `PhantomData`, and hence -/// in most use cases, a manual implementation is not necessary and is discouraged. -/// -/// The generic `T` must not bound anything and a `where` clause is not allowed. That said, -/// bounds and/or a where clause should not needed for any use-case. -/// -/// ## Macro expansion -/// -/// The macro implements the [`Debug`] trait and functions `as_u8` using variant position, and -/// `as_str` using variant doc. +/// --- /// -/// The macro also implements `From>` for `&'static str` and `From>` for -/// `DispatchError`. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::error`. #[proc_macro_attribute] pub fn error(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The `#[pallet::event]` attribute allows you to define pallet events. Pallet events are -/// stored under the `system` / `events` key when the block is applied (and then replaced when -/// the next block writes it's events). -/// -/// The Event enum must be defined as follows: -/// -/// ```ignore -/// #[pallet::event] -/// #[pallet::generate_deposit($visibility fn deposit_event)] // Optional -/// pub enum Event<$some_generic> $optional_where_clause { -/// /// Some doc -/// $SomeName($SomeType, $YetanotherType, ...), -/// ... -/// } -/// ``` /// -/// I.e. an enum (with named or unnamed fields variant), named `Event`, with generic: none or -/// `T` or `T: Config`, and optional w here clause. +/// --- /// -/// Each field must implement [`Clone`], [`Eq`], [`PartialEq`], `Encode`, `Decode`, and -/// [`Debug`] (on std only). For ease of use, bound by the trait `Member`, available in -/// `frame_support::pallet_prelude`. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::event`. #[proc_macro_attribute] pub fn event(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The attribute `#[pallet::generate_deposit($visibility fn deposit_event)]` generates a -/// helper function on `Pallet` that handles deposit events. -/// -/// NOTE: For instantiable pallets, the event must be generic over `T` and `I`. -/// -/// ## Macro expansion /// -/// The macro will add on enum `Event` the attributes: -/// * `#[derive(frame_support::CloneNoBound)]` -/// * `#[derive(frame_support::EqNoBound)]` -/// * `#[derive(frame_support::PartialEqNoBound)]` -/// * `#[derive(frame_support::RuntimeDebugNoBound)]` -/// * `#[derive(codec::Encode)]` -/// * `#[derive(codec::Decode)]` -/// -/// The macro implements `From>` for (). -/// -/// The macro implements a metadata function on `Event` returning the `EventMetadata`. +/// --- /// -/// If `#[pallet::generate_deposit]` is present then the macro implements `fn deposit_event` on -/// `Pallet`. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::generate_deposit`. #[proc_macro_attribute] pub fn generate_deposit(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() @@ -1298,105 +976,68 @@ pub fn generate_deposit(_: TokenStream, _: TokenStream) -> TokenStream { /// /// --- /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in +/// Rust-Analyzer Users: Documentation for this macro can be found at /// `frame_support::pallet_macros::storage`. #[proc_macro_attribute] pub fn storage(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The optional attribute `#[pallet::getter(fn $my_getter_fn_name)]` allows you to define a -/// getter function on `Pallet`. /// -/// Also see [`pallet::storage`](`macro@storage`) +/// --- +/// +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::getter`. #[proc_macro_attribute] pub fn getter(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The optional attribute `#[pallet::storage_prefix = "SomeName"]` allows you to define the -/// storage prefix to use. This is helpful if you wish to rename the storage field but don't -/// want to perform a migration. /// -/// E.g: -/// -/// ```ignore -/// #[pallet::storage] -/// #[pallet::storage_prefix = "foo"] -/// #[pallet::getter(fn my_storage)] -/// pub(super) type MyStorage = StorageMap; -/// ``` -/// -/// or +/// --- /// -/// ```ignore -/// #[pallet::storage] -/// #[pallet::getter(fn my_storage)] -/// pub(super) type MyStorage = StorageMap<_, Blake2_128Concat, u32, u32>; -/// ``` +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::storage_prefix`. #[proc_macro_attribute] pub fn storage_prefix(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The optional attribute `#[pallet::unbounded]` declares the storage as unbounded. When -/// implementating the storage info (when `#[pallet::generate_storage_info]` is specified on -/// the pallet struct placeholder), the size of the storage will be declared as unbounded. This -/// can be useful for storage which can never go into PoV (Proof of Validity). +/// +/// --- +/// +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::unbounded`. #[proc_macro_attribute] pub fn unbounded(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The optional attribute `#[pallet::whitelist_storage]` will declare the -/// storage as whitelisted from benchmarking. Doing so will exclude reads of -/// that value's storage key from counting towards weight calculations during -/// benchmarking. -/// -/// This attribute should only be attached to storages that are known to be -/// read/used in every block. This will result in a more accurate benchmarking weight. /// -/// ### Example -/// ```ignore -/// #[pallet::storage] -/// #[pallet::whitelist_storage] -/// pub(super) type Number = StorageValue<_, frame_system::pallet_prelude::BlockNumberFor::, ValueQuery>; -/// ``` +/// --- /// -/// NOTE: As with all `pallet::*` attributes, this one _must_ be written as -/// `#[pallet::whitelist_storage]` and can only be placed inside a `pallet` module in order for -/// it to work properly. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::whitelist_storage`. #[proc_macro_attribute] pub fn whitelist_storage(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The `#[pallet::type_value]` attribute lets you define a struct implementing the `Get` trait -/// to ease the use of storage types. This attribute is meant to be used alongside -/// [`#[pallet::storage]`](`macro@storage`) to define a storage's default value. This attribute -/// can be used multiple times. -/// -/// Item must be defined as: /// -/// ```ignore -/// #[pallet::type_value] -/// fn $MyDefaultName<$some_generic>() -> $default_type $optional_where_clause { $expr } -/// ``` -/// -/// I.e.: a function definition with generics none or `T: Config` and a returned type. -/// -/// E.g.: +/// --- /// -/// ```ignore -/// #[pallet::type_value] -/// fn MyDefault() -> T::Balance { 3.into() } -/// ``` +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::disable_try_decode_storage`. +#[proc_macro_attribute] +pub fn disable_try_decode_storage(_: TokenStream, _: TokenStream) -> TokenStream { + pallet_macro_stub() +} + /// -/// ## Macro expansion +/// --- /// -/// The macro renames the function to some internal name, generates a struct with the original -/// name of the function and its generic, and implements `Get<$ReturnType>` by calling the user -/// defined function. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::type_value`. #[proc_macro_attribute] pub fn type_value(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() @@ -1405,7 +1046,7 @@ pub fn type_value(_: TokenStream, _: TokenStream) -> TokenStream { /// /// --- /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in +/// Rust-Analyzer Users: Documentation for this macro can be found at /// `frame_support::pallet_macros::genesis_config`. #[proc_macro_attribute] pub fn genesis_config(_: TokenStream, _: TokenStream) -> TokenStream { @@ -1415,115 +1056,48 @@ pub fn genesis_config(_: TokenStream, _: TokenStream) -> TokenStream { /// /// --- /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in +/// Rust-Analyzer Users: Documentation for this macro can be found at /// `frame_support::pallet_macros::genesis_build`. #[proc_macro_attribute] pub fn genesis_build(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The `#[pallet::inherent]` attribute allows the pallet to provide some -/// [inherent](https://docs.substrate.io/fundamentals/transaction-types/#inherent-transactions). -/// An inherent is some piece of data that is inserted by a block authoring node at block -/// creation time and can either be accepted or rejected by validators based on whether the -/// data falls within an acceptable range. -/// -/// The most common inherent is the `timestamp` that is inserted into every block. Since there -/// is no way to validate timestamps, validators simply check that the timestamp reported by -/// the block authoring node falls within an acceptable range. -/// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::inherent] -/// impl ProvideInherent for Pallet { -/// // ... regular trait implementation -/// } -/// ``` /// -/// I.e. a trait implementation with bound `T: Config`, of trait `ProvideInherent` for type -/// `Pallet`, and some optional where clause. -/// -/// ## Macro expansion +/// --- /// -/// The macro currently makes no use of this information, but it might use this information in -/// the future to give information directly to `construct_runtime`. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::inherent`. #[proc_macro_attribute] pub fn inherent(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The `#[pallet::validate_unsigned]` attribute allows the pallet to validate some unsigned -/// transaction: -/// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::validate_unsigned] -/// impl ValidateUnsigned for Pallet { -/// // ... regular trait implementation -/// } -/// ``` -/// -/// I.e. a trait implementation with bound `T: Config`, of trait `ValidateUnsigned` for type -/// `Pallet`, and some optional where clause. /// -/// NOTE: There is also the `sp_runtime::traits::SignedExtension` trait that can be used to add -/// some specific logic for transaction validation. -/// -/// ## Macro expansion +/// --- /// -/// The macro currently makes no use of this information, but it might use this information in -/// the future to give information directly to `construct_runtime`. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::validate_unsigned`. #[proc_macro_attribute] pub fn validate_unsigned(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The `#[pallet::origin]` attribute allows you to define some origin for the pallet. -/// -/// Item must be either a type alias, an enum, or a struct. It needs to be public. -/// -/// E.g.: -/// -/// ```ignore -/// #[pallet::origin] -/// pub struct Origin(PhantomData<(T)>); -/// ``` /// -/// **WARNING**: modifying origin changes the outer runtime origin. This outer runtime origin -/// can be stored on-chain (e.g. in `pallet-scheduler`), thus any change must be done with care -/// as it might require some migration. +/// --- /// -/// NOTE: for instantiable pallets, the origin must be generic over `T` and `I`. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::origin`. #[proc_macro_attribute] pub fn origin(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The `#[pallet::composite_enum]` attribute allows you to define an enum that gets composed as an -/// aggregate enum by `construct_runtime`. This is similar in principle with `#[pallet::event]` and -/// `#[pallet::error]`. -/// -/// The attribute currently only supports enum definitions, and identifiers that are named -/// `FreezeReason`, `HoldReason`, `LockId` or `SlashReason`. Arbitrary identifiers for the enum are -/// not supported. The aggregate enum generated by `construct_runtime` will have the name of -/// `RuntimeFreezeReason`, `RuntimeHoldReason`, `RuntimeLockId` and `RuntimeSlashReason` -/// respectively. /// -/// NOTE: The aggregate enum generated by `construct_runtime` generates a conversion function from -/// the pallet enum to the aggregate enum, and automatically derives the following traits: -/// -/// ```ignore -/// Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, TypeInfo, -/// RuntimeDebug -/// ``` +/// --- /// -/// For ease of usage, when no `#[derive]` attributes are found for the enum under -/// `#[pallet::composite_enum]`, the aforementioned traits are automatically derived for it. The -/// inverse is also true: if there are any `#[derive]` attributes found for the enum, then no traits -/// will automatically be derived for it (this implies that you need to provide the -/// `frame_support::traits::VariantCount` implementation). +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::composite_enum`. #[proc_macro_attribute] pub fn composite_enum(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() @@ -1579,25 +1153,11 @@ pub fn task_index(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// Can be attached to a module. Doing so will declare that module as importable into a pallet -/// via [`#[import_section]`](`macro@import_section`). -/// -/// Note that sections are imported by their module name/ident, and should be referred to by -/// their _full path_ from the perspective of the target pallet. Do not attempt to make use -/// of `use` statements to bring pallet sections into scope, as this will not work (unless -/// you do so as part of a wildcard import, in which case it will work). -/// -/// ## Naming Logistics /// -/// Also note that because of how `#[pallet_section]` works, pallet section names must be -/// globally unique _within the crate in which they are defined_. For more information on -/// why this must be the case, see macro_magic's -/// [`#[export_tokens]`](https://docs.rs/macro_magic/latest/macro_magic/attr.export_tokens.html) macro. +/// --- /// -/// Optionally, you may provide an argument to `#[pallet_section]` such as -/// `#[pallet_section(some_ident)]`, in the event that there is another pallet section in -/// same crate with the same ident/name. The ident you specify can then be used instead of -/// the module's ident name when you go to import it via `#[import_section]`. +/// **Rust-Analyzer users**: See the documentation of the Rust item in +/// `frame_support::pallet_macros::pallet_section`. #[proc_macro_attribute] pub fn pallet_section(attr: TokenStream, tokens: TokenStream) -> TokenStream { let tokens_clone = tokens.clone(); @@ -1611,39 +1171,11 @@ pub fn pallet_section(attr: TokenStream, tokens: TokenStream) -> TokenStream { } } -/// An attribute macro that can be attached to a module declaration. Doing so will -/// Imports the contents of the specified external pallet section that was defined -/// previously using [`#[pallet_section]`](`macro@pallet_section`). /// -/// ## Example -/// ```ignore -/// #[import_section(some_section)] -/// #[pallet] -/// pub mod pallet { -/// // ... -/// } -/// ``` -/// where `some_section` was defined elsewhere via: -/// ```ignore -/// #[pallet_section] -/// pub mod some_section { -/// // ... -/// } -/// ``` -/// -/// This will result in the contents of `some_section` being _verbatim_ imported into -/// the pallet above. Note that since the tokens for `some_section` are essentially -/// copy-pasted into the target pallet, you cannot refer to imports that don't also -/// exist in the target pallet, but this is easily resolved by including all relevant -/// `use` statements within your pallet section, so they are imported as well, or by -/// otherwise ensuring that you have the same imports on the target pallet. -/// -/// It is perfectly permissible to import multiple pallet sections into the same pallet, -/// which can be done by having multiple `#[import_section(something)]` attributes -/// attached to the pallet. +/// --- /// -/// Note that sections are imported by their module name/ident, and should be referred to by -/// their _full path_ from the perspective of the target pallet. +/// **Rust-Analyzer users**: See the documentation of the Rust item in +/// `frame_support::pallet_macros::import_section`. #[import_tokens_attr { format!( "{}::macro_magic", @@ -1689,6 +1221,73 @@ pub fn import_section(attr: TokenStream, tokens: TokenStream) -> TokenStream { .into() } +/// Construct a runtime, with the given name and the given pallets. +/// +/// # Example: +/// +/// ```ignore +/// #[frame_support::runtime] +/// mod runtime { +/// // The main runtime +/// #[runtime::runtime] +/// // Runtime Types to be generated +/// #[runtime::derive( +/// RuntimeCall, +/// RuntimeEvent, +/// RuntimeError, +/// RuntimeOrigin, +/// RuntimeFreezeReason, +/// RuntimeHoldReason, +/// RuntimeSlashReason, +/// RuntimeLockId, +/// RuntimeTask, +/// )] +/// pub struct Runtime; +/// +/// #[runtime::pallet_index(0)] +/// pub type System = frame_system; +/// +/// #[runtime::pallet_index(1)] +/// pub type Test = path::to::test; +/// +/// // Pallet with instance. +/// #[runtime::pallet_index(2)] +/// pub type Test2_Instance1 = test2; +/// +/// // Pallet with calls disabled. +/// #[runtime::pallet_index(3)] +/// #[runtime::disable_call] +/// pub type Test3 = test3; +/// +/// // Pallet with unsigned extrinsics disabled. +/// #[runtime::pallet_index(4)] +/// #[runtime::disable_unsigned] +/// pub type Test4 = test4; +/// } +/// ``` +/// +/// # Legacy Ordering +/// +/// An optional attribute can be defined as #[frame_support::runtime(legacy_ordering)] to +/// ensure that the order of hooks is same as the order of pallets (and not based on the +/// pallet_index). This is to support legacy runtimes and should be avoided for new ones. +/// +/// # Note +/// +/// The population of the genesis storage depends on the order of pallets. So, if one of your +/// pallets depends on another pallet, the pallet that is depended upon needs to come before +/// the pallet depending on it. +/// +/// # Type definitions +/// +/// * The macro generates a type alias for each pallet to their `Pallet`. E.g. `type System = +/// frame_system::Pallet` +#[cfg(feature = "experimental")] +#[proc_macro_attribute] +pub fn runtime(attr: TokenStream, item: TokenStream) -> TokenStream { + runtime::runtime(attr, item) +} + /// Mark a module that contains dynamic parameters. /// /// See the `pallet_parameters` for a full example. diff --git a/substrate/frame/support/procedural/src/pallet/expand/call.rs b/substrate/frame/support/procedural/src/pallet/expand/call.rs index f43faba1ee0c8c14cb808b2f64f58446ebe7ffae..f395872c8a80a6b9a2ddd391ea106284140d53ba 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/call.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/call.rs @@ -18,7 +18,7 @@ use crate::{ pallet::{ expand::warnings::{weight_constant_warning, weight_witness_warning}, - parse::call::{CallVariantDef, CallWeightDef}, + parse::call::CallWeightDef, Def, }, COUNTER, @@ -112,22 +112,7 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { } debug_assert_eq!(fn_weight.len(), methods.len()); - let map_fn_docs = if !def.dev_mode { - // Emit the [`Pallet::method`] documentation only for non-dev modes. - |method: &CallVariantDef| { - let reference = format!("See [`Pallet::{}`].", method.name); - quote!(#reference) - } - } else { - // For the dev-mode do not provide a documenation link as it will break the - // `cargo doc` if the pallet is private inside a test. - |method: &CallVariantDef| { - let reference = format!("See `Pallet::{}`.", method.name); - quote!(#reference) - } - }; - - let fn_doc = methods.iter().map(map_fn_docs).collect::>(); + let fn_doc = methods.iter().map(|method| &method.docs).collect::>(); let args_name = methods .iter() @@ -309,7 +294,7 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { ), #( #cfg_attrs - #[doc = #fn_doc] + #( #[doc = #fn_doc] )* #[codec(index = #call_index)] #fn_name { #( diff --git a/substrate/frame/support/procedural/src/pallet/expand/hooks.rs b/substrate/frame/support/procedural/src/pallet/expand/hooks.rs index 5044d4285bb64aceeccd14959743a494f3c94f85..3623b595268d081ee7b5750a5431f208383f8517 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/hooks.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/hooks.rs @@ -42,7 +42,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { >::name::().unwrap_or("") }; - let initialize_on_chain_storage_version = if let Some(current_version) = + let initialize_on_chain_storage_version = if let Some(in_code_version) = &def.pallet_struct.storage_version { quote::quote! { @@ -50,9 +50,9 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { target: #frame_support::LOG_TARGET, "🐥 New pallet {:?} detected in the runtime. Initializing the on-chain storage version to match the storage version defined in the pallet: {:?}", #pallet_name, - #current_version + #in_code_version ); - #current_version.put::(); + #in_code_version.put::(); } } else { quote::quote! { @@ -73,10 +73,10 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { #frame_support::__private::log::info!( target: #frame_support::LOG_TARGET, "⚠️ {} declares internal migrations (which *might* execute). \ - On-chain `{:?}` vs current storage version `{:?}`", + On-chain `{:?}` vs in-code storage version `{:?}`", #pallet_name, ::on_chain_storage_version(), - ::current_storage_version(), + ::in_code_storage_version(), ); } } else { @@ -102,23 +102,23 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { }; // If a storage version is set, we should ensure that the storage version on chain matches the - // current storage version. This assumes that `Executive` is running custom migrations before + // in-code storage version. This assumes that `Executive` is running custom migrations before // the pallets are called. let post_storage_version_check = if def.pallet_struct.storage_version.is_some() { quote::quote! { let on_chain_version = ::on_chain_storage_version(); - let current_version = ::current_storage_version(); + let in_code_version = ::in_code_storage_version(); - if on_chain_version != current_version { + if on_chain_version != in_code_version { #frame_support::__private::log::error!( target: #frame_support::LOG_TARGET, - "{}: On chain storage version {:?} doesn't match current storage version {:?}.", + "{}: On chain storage version {:?} doesn't match in-code storage version {:?}.", #pallet_name, on_chain_version, - current_version, + in_code_version, ); - return Err("On chain and current storage version do not match. Missing runtime upgrade?".into()); + return Err("On chain and in-code storage version do not match. Missing runtime upgrade?".into()); } } } else { @@ -175,6 +175,22 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { } } + impl<#type_impl_gen> + #frame_support::traits::OnPoll<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_poll( + n: #frame_system::pallet_prelude::BlockNumberFor::, + weight: &mut #frame_support::weights::WeightMeter + ) { + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_poll(n, weight); + } + } + impl<#type_impl_gen> #frame_support::traits::OnInitialize<#frame_system::pallet_prelude::BlockNumberFor::> for #pallet_ident<#type_use_gen> #where_clause diff --git a/substrate/frame/support/procedural/src/pallet/expand/mod.rs b/substrate/frame/support/procedural/src/pallet/expand/mod.rs index 3da7d9293c7cc1a1676e5cb0e4afe9ab08b25948..067839c2846353cb995d4c160ac2f38313c19d01 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/mod.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/mod.rs @@ -31,7 +31,6 @@ mod instances; mod origin; mod pallet_struct; mod storage; -mod store_trait; mod tasks; mod tt_default_parts; mod type_value; @@ -68,7 +67,6 @@ pub fn expand(mut def: Def) -> proc_macro2::TokenStream { let storages = storage::expand_storages(&mut def); let inherents = inherent::expand_inherents(&mut def); let instances = instances::expand_instances(&mut def); - let store_trait = store_trait::expand_store_trait(&mut def); let hooks = hooks::expand_hooks(&mut def); let genesis_build = genesis_build::expand_genesis_build(&mut def); let genesis_config = genesis_config::expand_genesis_config(&mut def); @@ -110,7 +108,6 @@ storage item. Otherwise, all storage items are listed among [*Type Definitions*] #storages #inherents #instances - #store_trait #hooks #genesis_build #genesis_config diff --git a/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs b/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs index c2102f0284dbeeadb08fc9122e5fe824032dfb82..7cdf6bde9de87d0bd4eed70de93173bc9bf4e3ff 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs @@ -160,7 +160,7 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { } ); - let (storage_version, current_storage_version_ty) = + let (storage_version, in_code_storage_version_ty) = if let Some(v) = def.pallet_struct.storage_version.as_ref() { (quote::quote! { #v }, quote::quote! { #frame_support::traits::StorageVersion }) } else { @@ -203,9 +203,9 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { for #pallet_ident<#type_use_gen> #config_where_clause { - type CurrentStorageVersion = #current_storage_version_ty; + type InCodeStorageVersion = #in_code_storage_version_ty; - fn current_storage_version() -> Self::CurrentStorageVersion { + fn in_code_storage_version() -> Self::InCodeStorageVersion { #storage_version } diff --git a/substrate/frame/support/procedural/src/pallet/expand/storage.rs b/substrate/frame/support/procedural/src/pallet/expand/storage.rs index 96c2c8e3120b8f8fb76e8ee10b067ce3f13f62e9..937b068cfabd14e04b05177be2f0242eccd3abec 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/storage.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/storage.rs @@ -834,7 +834,10 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { .storages .iter() .filter_map(|storage| { - if storage.cfg_attrs.is_empty() { + // A little hacky; don't generate for cfg gated storages to not get compile errors + // when building "frame-feature-testing" gated storages in the "frame-support-test" + // crate. + if storage.try_decode && storage.cfg_attrs.is_empty() { let ident = &storage.ident; let gen = &def.type_use_generics(storage.attr_span); Some(quote::quote_spanned!(storage.attr_span => #ident<#gen> )) diff --git a/substrate/frame/support/procedural/src/pallet/expand/store_trait.rs b/substrate/frame/support/procedural/src/pallet/expand/store_trait.rs deleted file mode 100644 index 6635adc988157361a1478a5ec06589f7988b9676..0000000000000000000000000000000000000000 --- a/substrate/frame/support/procedural/src/pallet/expand/store_trait.rs +++ /dev/null @@ -1,67 +0,0 @@ -// 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. - -use crate::pallet::Def; -use syn::spanned::Spanned; - -/// If attribute `#[pallet::generate_store(..)]` is defined then: -/// * generate Store trait with all storages, -/// * implement Store trait for Pallet. -pub fn expand_store_trait(def: &mut Def) -> proc_macro2::TokenStream { - let (trait_vis, trait_store, attribute_span) = - if let Some(store) = &def.pallet_struct.store { store } else { return Default::default() }; - - let type_impl_gen = &def.type_impl_generics(trait_store.span()); - let type_use_gen = &def.type_use_generics(trait_store.span()); - let pallet_ident = &def.pallet_struct.pallet; - - let mut where_clauses = vec![&def.config.where_clause]; - where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause)); - let completed_where_clause = super::merge_where_clauses(&where_clauses); - - let storage_names = &def.storages.iter().map(|storage| &storage.ident).collect::>(); - let storage_cfg_attrs = - &def.storages.iter().map(|storage| &storage.cfg_attrs).collect::>(); - let warnig_struct_name = syn::Ident::new("Store", *attribute_span); - let warning: syn::ItemStruct = syn::parse_quote!( - #[deprecated(note = r" - Use of `#[pallet::generate_store(pub(super) trait Store)]` will be removed after July 2023. - Check https://github.com/paritytech/substrate/pull/13535 for more details.")] - struct #warnig_struct_name; - ); - - quote::quote_spanned!(trait_store.span() => - const _:() = { - #warning - const _: Option<#warnig_struct_name> = None; - }; - #trait_vis trait #trait_store { - #( - #(#storage_cfg_attrs)* - type #storage_names; - )* - } - impl<#type_impl_gen> #trait_store for #pallet_ident<#type_use_gen> - #completed_where_clause - { - #( - #(#storage_cfg_attrs)* - type #storage_names = #storage_names<#type_use_gen>; - )* - } - ) -} diff --git a/substrate/frame/support/procedural/src/pallet/expand/tt_default_parts.rs b/substrate/frame/support/procedural/src/pallet/expand/tt_default_parts.rs index 7cc1415dfddf1514cf398555c650f4892097aefd..99364aaa96cd7808def45563392be207604151d7 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/tt_default_parts.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/tt_default_parts.rs @@ -28,6 +28,8 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream { syn::Ident::new(&format!("__tt_default_parts_{}", count), def.item.span()); let extra_parts_unique_id = syn::Ident::new(&format!("__tt_extra_parts_{}", count), def.item.span()); + let default_parts_unique_id_v2 = + syn::Ident::new(&format!("__tt_default_parts_v2_{}", count), def.item.span()); let call_part = def.call.as_ref().map(|_| quote::quote!(Call,)); @@ -81,6 +83,58 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream { .any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_))) .then_some(quote::quote!(SlashReason,)); + let call_part_v2 = def.call.as_ref().map(|_| quote::quote!(+ Call)); + + let task_part_v2 = def.task_enum.as_ref().map(|_| quote::quote!(+ Task)); + + let storage_part_v2 = (!def.storages.is_empty()).then(|| quote::quote!(+ Storage)); + + let event_part_v2 = def.event.as_ref().map(|event| { + let gen = event.gen_kind.is_generic().then(|| quote::quote!()); + quote::quote!(+ Event #gen) + }); + + let error_part_v2 = def.error.as_ref().map(|_| quote::quote!(+ Error)); + + let origin_part_v2 = def.origin.as_ref().map(|origin| { + let gen = origin.is_generic.then(|| quote::quote!()); + quote::quote!(+ Origin #gen) + }); + + let config_part_v2 = def.genesis_config.as_ref().map(|genesis_config| { + let gen = genesis_config.gen_kind.is_generic().then(|| quote::quote!()); + quote::quote!(+ Config #gen) + }); + + let inherent_part_v2 = def.inherent.as_ref().map(|_| quote::quote!(+ Inherent)); + + let validate_unsigned_part_v2 = + def.validate_unsigned.as_ref().map(|_| quote::quote!(+ ValidateUnsigned)); + + let freeze_reason_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_))) + .then_some(quote::quote!(+ FreezeReason)); + + let hold_reason_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_))) + .then_some(quote::quote!(+ HoldReason)); + + let lock_id_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_))) + .then_some(quote::quote!(+ LockId)); + + let slash_reason_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_))) + .then_some(quote::quote!(+ SlashReason)); + quote::quote!( // This macro follows the conventions as laid out by the `tt-call` crate. It does not // accept any arguments and simply returns the pallet parts, separated by commas, then @@ -138,5 +192,25 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream { } pub use #extra_parts_unique_id as tt_extra_parts; + + #[macro_export] + #[doc(hidden)] + macro_rules! #default_parts_unique_id_v2 { + { + $caller:tt + frame_support = [{ $($frame_support:ident)::* }] + } => { + $($frame_support)*::__private::tt_return! { + $caller + tokens = [{ + + Pallet #call_part_v2 #storage_part_v2 #event_part_v2 #error_part_v2 #origin_part_v2 #config_part_v2 + #inherent_part_v2 #validate_unsigned_part_v2 #freeze_reason_part_v2 #task_part_v2 + #hold_reason_part_v2 #lock_id_part_v2 #slash_reason_part_v2 + }] + } + }; + } + + pub use #default_parts_unique_id_v2 as tt_default_parts_v2; ) } diff --git a/substrate/frame/support/procedural/src/pallet/parse/helper.rs b/substrate/frame/support/procedural/src/pallet/parse/helper.rs index 538226a8745fe4d0792e36b9aaad9cbed9d4f88e..3187c9139c8f46c7664e96edb6870f68979146ae 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/helper.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/helper.rs @@ -148,6 +148,12 @@ impl MutItemAttrs for syn::ImplItemFn { } } +impl MutItemAttrs for syn::ItemType { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } +} + /// Parse for `()` struct Unit; impl syn::parse::Parse for Unit { diff --git a/substrate/frame/support/procedural/src/pallet/parse/mod.rs b/substrate/frame/support/procedural/src/pallet/parse/mod.rs index e1efdbcc2027975d89a00b1037bd5d0af68998a3..6e12774611ddfd0b4cdf53468df074dc7315e382 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/mod.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/mod.rs @@ -321,7 +321,7 @@ impl Def { Ok(()) } - /// Tries to locate a manual tasks impl (an impl impling a trait whose last path segment is + /// Tries to locate a manual tasks impl (an impl implementing a trait whose last path segment is /// `Task`) in the event that one has not been found already via the attribute macro pub fn resolve_manual_tasks_impl( tasks: &mut Option, @@ -558,8 +558,6 @@ mod keyword { syn::custom_keyword!(validate_unsigned); syn::custom_keyword!(type_value); syn::custom_keyword!(pallet); - syn::custom_keyword!(generate_store); - syn::custom_keyword!(Store); syn::custom_keyword!(extra_constants); syn::custom_keyword!(composite_enum); } diff --git a/substrate/frame/support/procedural/src/pallet/parse/pallet_struct.rs b/substrate/frame/support/procedural/src/pallet/parse/pallet_struct.rs index f4af86aa3e9936a3e77036f062ba8d4bf1a6c601..b645760998fe1c27e48c724dcde7cfcc658bb594 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/pallet_struct.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/pallet_struct.rs @@ -23,10 +23,8 @@ use syn::spanned::Spanned; mod keyword { syn::custom_keyword!(pallet); syn::custom_keyword!(Pallet); - syn::custom_keyword!(generate_store); syn::custom_keyword!(without_storage_info); syn::custom_keyword!(storage_version); - syn::custom_keyword!(Store); } /// Definition of the pallet pallet. @@ -37,23 +35,19 @@ pub struct PalletStructDef { pub instances: Vec, /// The keyword Pallet used (contains span). pub pallet: keyword::Pallet, - /// Whether the trait `Store` must be generated. - pub store: Option<(syn::Visibility, keyword::Store, proc_macro2::Span)>, /// The span of the pallet::pallet attribute. pub attr_span: proc_macro2::Span, /// Whether to specify the storages max encoded len when implementing `StorageInfoTrait`. /// Contains the span of the attribute. pub without_storage_info: Option, - /// The current storage version of the pallet. + /// The in-code storage version of the pallet. pub storage_version: Option, } /// Parse for one variant of: -/// * `#[pallet::generate_store($vis trait Store)]` /// * `#[pallet::without_storage_info]` /// * `#[pallet::storage_version(STORAGE_VERSION)]` pub enum PalletStructAttr { - GenerateStore { span: proc_macro2::Span, vis: syn::Visibility, keyword: keyword::Store }, WithoutStorageInfoTrait(proc_macro2::Span), StorageVersion { storage_version: syn::Path, span: proc_macro2::Span }, } @@ -61,9 +55,7 @@ pub enum PalletStructAttr { impl PalletStructAttr { fn span(&self) -> proc_macro2::Span { match self { - Self::GenerateStore { span, .. } | - Self::WithoutStorageInfoTrait(span) | - Self::StorageVersion { span, .. } => *span, + Self::WithoutStorageInfoTrait(span) | Self::StorageVersion { span, .. } => *span, } } } @@ -77,16 +69,7 @@ impl syn::parse::Parse for PalletStructAttr { content.parse::()?; let lookahead = content.lookahead1(); - if lookahead.peek(keyword::generate_store) { - content.parse::()?; - let generate_content; - syn::parenthesized!(generate_content in content); - let vis = generate_content.parse::()?; - generate_content.parse::()?; - let keyword = generate_content.parse::()?; - let span = content.span(); - Ok(Self::GenerateStore { vis, keyword, span }) - } else if lookahead.peek(keyword::without_storage_info) { + if lookahead.peek(keyword::without_storage_info) { let span = content.parse::()?.span(); Ok(Self::WithoutStorageInfoTrait(span)) } else if lookahead.peek(keyword::storage_version) { @@ -116,16 +99,12 @@ impl PalletStructDef { return Err(syn::Error::new(item.span(), msg)) }; - let mut store = None; let mut without_storage_info = None; let mut storage_version_found = None; let struct_attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; for attr in struct_attrs { match attr { - PalletStructAttr::GenerateStore { vis, keyword, span } if store.is_none() => { - store = Some((vis, keyword, span)); - }, PalletStructAttr::WithoutStorageInfoTrait(span) if without_storage_info.is_none() => { @@ -162,7 +141,6 @@ impl PalletStructDef { index, instances, pallet, - store, attr_span, without_storage_info, storage_version: storage_version_found, diff --git a/substrate/frame/support/procedural/src/pallet/parse/storage.rs b/substrate/frame/support/procedural/src/pallet/parse/storage.rs index d1c7ba2e5e3c6788827577dfa843205d0be69174..9d96a18b56943db4715fabd30e8bc0f1c0aa7d28 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/storage.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/storage.rs @@ -29,6 +29,7 @@ mod keyword { syn::custom_keyword!(storage_prefix); syn::custom_keyword!(unbounded); syn::custom_keyword!(whitelist_storage); + syn::custom_keyword!(disable_try_decode_storage); syn::custom_keyword!(OptionQuery); syn::custom_keyword!(ResultQuery); syn::custom_keyword!(ValueQuery); @@ -39,11 +40,13 @@ mod keyword { /// * `#[pallet::storage_prefix = "CustomName"]` /// * `#[pallet::unbounded]` /// * `#[pallet::whitelist_storage] +/// * `#[pallet::disable_try_decode_storage]` pub enum PalletStorageAttr { Getter(syn::Ident, proc_macro2::Span), StorageName(syn::LitStr, proc_macro2::Span), Unbounded(proc_macro2::Span), WhitelistStorage(proc_macro2::Span), + DisableTryDecodeStorage(proc_macro2::Span), } impl PalletStorageAttr { @@ -53,6 +56,7 @@ impl PalletStorageAttr { Self::StorageName(_, span) | Self::Unbounded(span) | Self::WhitelistStorage(span) => *span, + Self::DisableTryDecodeStorage(span) => *span, } } } @@ -93,6 +97,9 @@ impl syn::parse::Parse for PalletStorageAttr { } else if lookahead.peek(keyword::whitelist_storage) { content.parse::()?; Ok(Self::WhitelistStorage(attr_span)) + } else if lookahead.peek(keyword::disable_try_decode_storage) { + content.parse::()?; + Ok(Self::DisableTryDecodeStorage(attr_span)) } else { Err(lookahead.error()) } @@ -104,6 +111,7 @@ struct PalletStorageAttrInfo { rename_as: Option, unbounded: bool, whitelisted: bool, + try_decode: bool, } impl PalletStorageAttrInfo { @@ -112,6 +120,7 @@ impl PalletStorageAttrInfo { let mut rename_as = None; let mut unbounded = false; let mut whitelisted = false; + let mut disable_try_decode_storage = false; for attr in attrs { match attr { PalletStorageAttr::Getter(ident, ..) if getter.is_none() => getter = Some(ident), @@ -119,6 +128,8 @@ impl PalletStorageAttrInfo { rename_as = Some(name), PalletStorageAttr::Unbounded(..) if !unbounded => unbounded = true, PalletStorageAttr::WhitelistStorage(..) if !whitelisted => whitelisted = true, + PalletStorageAttr::DisableTryDecodeStorage(..) if !disable_try_decode_storage => + disable_try_decode_storage = true, attr => return Err(syn::Error::new( attr.attr_span(), @@ -127,7 +138,13 @@ impl PalletStorageAttrInfo { } } - Ok(PalletStorageAttrInfo { getter, rename_as, unbounded, whitelisted }) + Ok(PalletStorageAttrInfo { + getter, + rename_as, + unbounded, + whitelisted, + try_decode: !disable_try_decode_storage, + }) } } @@ -186,6 +203,8 @@ pub struct StorageDef { pub unbounded: bool, /// Whether or not reads to this storage key will be ignored by benchmarking pub whitelisted: bool, + /// Whether or not to try to decode the storage key when running try-runtime checks. + pub try_decode: bool, /// Whether or not a default hasher is allowed to replace `_` pub use_default_hasher: bool, } @@ -775,7 +794,7 @@ impl StorageDef { }; let attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; - let PalletStorageAttrInfo { getter, rename_as, mut unbounded, whitelisted } = + let PalletStorageAttrInfo { getter, rename_as, mut unbounded, whitelisted, try_decode } = PalletStorageAttrInfo::from_attrs(attrs)?; // set all storages to be unbounded if dev_mode is enabled @@ -921,6 +940,7 @@ impl StorageDef { named_generics, unbounded, whitelisted, + try_decode, use_default_hasher, }) } diff --git a/substrate/frame/support/procedural/src/runtime/expand/mod.rs b/substrate/frame/support/procedural/src/runtime/expand/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..93c88fce94b73665efa7912b36056b661b12be1e --- /dev/null +++ b/substrate/frame/support/procedural/src/runtime/expand/mod.rs @@ -0,0 +1,320 @@ +// 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. + +use super::parse::runtime_types::RuntimeType; +use crate::{ + construct_runtime::{ + check_pallet_number, decl_all_pallets, decl_integrity_test, decl_pallet_runtime_setup, + decl_static_assertions, expand, + }, + runtime::{ + parse::{ + AllPalletsDeclaration, ExplicitAllPalletsDeclaration, ImplicitAllPalletsDeclaration, + }, + Def, + }, +}; +use cfg_expr::Predicate; +use frame_support_procedural_tools::{ + generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes, +}; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use std::collections::HashSet; +use syn::{Ident, Result}; + +/// The fixed name of the system pallet. +const SYSTEM_PALLET_NAME: &str = "System"; + +pub fn expand(def: Def, legacy_ordering: bool) -> TokenStream2 { + let input = def.input; + + let (check_pallet_number_res, res) = match def.pallets { + AllPalletsDeclaration::Implicit(ref decl) => ( + check_pallet_number(input.clone(), decl.pallet_count), + construct_runtime_implicit_to_explicit(input.into(), decl.clone(), legacy_ordering), + ), + AllPalletsDeclaration::Explicit(ref decl) => ( + check_pallet_number(input, decl.pallets.len()), + construct_runtime_final_expansion( + def.runtime_struct.ident.clone(), + decl.clone(), + def.runtime_types.clone(), + legacy_ordering, + ), + ), + }; + + let res = res.unwrap_or_else(|e| e.to_compile_error()); + + // We want to provide better error messages to the user and thus, handle the error here + // separately. If there is an error, we print the error and still generate all of the code to + // get in overall less errors for the user. + let res = if let Err(error) = check_pallet_number_res { + let error = error.to_compile_error(); + + quote! { + #error + + #res + } + } else { + res + }; + + let res = expander::Expander::new("construct_runtime") + .dry(std::env::var("FRAME_EXPAND").is_err()) + .verbose(true) + .write_to_out_dir(res) + .expect("Does not fail because of IO in OUT_DIR; qed"); + + res.into() +} + +fn construct_runtime_implicit_to_explicit( + input: TokenStream2, + definition: ImplicitAllPalletsDeclaration, + legacy_ordering: bool, +) -> Result { + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + let attr = if legacy_ordering { quote!((legacy_ordering)) } else { quote!() }; + let mut expansion = quote::quote!( + #[frame_support::runtime #attr] + #input + ); + for pallet in definition.pallet_decls.iter() { + let pallet_path = &pallet.path; + let pallet_name = &pallet.name; + let pallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(<#instance>)); + expansion = quote::quote!( + #frame_support::__private::tt_call! { + macro = [{ #pallet_path::tt_default_parts_v2 }] + frame_support = [{ #frame_support }] + ~~> #frame_support::match_and_insert! { + target = [{ #expansion }] + pattern = [{ #pallet_name = #pallet_path #pallet_instance }] + } + } + ); + } + + Ok(expansion) +} + +fn construct_runtime_final_expansion( + name: Ident, + definition: ExplicitAllPalletsDeclaration, + runtime_types: Vec, + legacy_ordering: bool, +) -> Result { + let ExplicitAllPalletsDeclaration { mut pallets, name: pallets_name } = definition; + + if !legacy_ordering { + // Ensure that order of hooks is based on the pallet index + pallets.sort_by_key(|p| p.index); + } + + let system_pallet = + pallets.iter().find(|decl| decl.name == SYSTEM_PALLET_NAME).ok_or_else(|| { + syn::Error::new( + pallets_name.span(), + "`System` pallet declaration is missing. \ + Please add this line: `pub type System = frame_system;`", + ) + })?; + if !system_pallet.cfg_pattern.is_empty() { + return Err(syn::Error::new( + system_pallet.name.span(), + "`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes", + )) + } + + let features = pallets + .iter() + .filter_map(|decl| { + (!decl.cfg_pattern.is_empty()).then(|| { + decl.cfg_pattern.iter().flat_map(|attr| { + attr.predicates().filter_map(|pred| match pred { + Predicate::Feature(feat) => Some(feat), + Predicate::Test => Some("test"), + _ => None, + }) + }) + }) + }) + .flatten() + .collect::>(); + + let hidden_crate_name = "construct_runtime"; + let scrate = generate_crate_access(hidden_crate_name, "frame-support"); + let scrate_decl = generate_hidden_includes(hidden_crate_name, "frame-support"); + + let frame_system = generate_access_from_frame_or_crate("frame-system")?; + let block = quote!(<#name as #frame_system::Config>::Block); + let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic); + + let mut dispatch = None; + let mut outer_event = None; + let mut outer_error = None; + let mut outer_origin = None; + let mut freeze_reason = None; + let mut hold_reason = None; + let mut slash_reason = None; + let mut lock_id = None; + let mut task = None; + + for runtime_type in runtime_types.iter() { + match runtime_type { + RuntimeType::RuntimeCall(_) => { + dispatch = + Some(expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate)); + }, + RuntimeType::RuntimeEvent(_) => { + outer_event = Some(expand::expand_outer_enum( + &name, + &pallets, + &scrate, + expand::OuterEnumType::Event, + )?); + }, + RuntimeType::RuntimeError(_) => { + outer_error = Some(expand::expand_outer_enum( + &name, + &pallets, + &scrate, + expand::OuterEnumType::Error, + )?); + }, + RuntimeType::RuntimeOrigin(_) => { + outer_origin = + Some(expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?); + }, + RuntimeType::RuntimeFreezeReason(_) => { + freeze_reason = Some(expand::expand_outer_freeze_reason(&pallets, &scrate)); + }, + RuntimeType::RuntimeHoldReason(_) => { + hold_reason = Some(expand::expand_outer_hold_reason(&pallets, &scrate)); + }, + RuntimeType::RuntimeSlashReason(_) => { + slash_reason = Some(expand::expand_outer_slash_reason(&pallets, &scrate)); + }, + RuntimeType::RuntimeLockId(_) => { + lock_id = Some(expand::expand_outer_lock_id(&pallets, &scrate)); + }, + RuntimeType::RuntimeTask(_) => { + task = Some(expand::expand_outer_task(&name, &pallets, &scrate)); + }, + } + } + + let all_pallets = decl_all_pallets(&name, pallets.iter(), &features); + let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate); + + let metadata = expand::expand_runtime_metadata( + &name, + &pallets, + &scrate, + &unchecked_extrinsic, + &system_pallet.path, + ); + let outer_config = expand::expand_outer_config(&name, &pallets, &scrate); + let inherent = + expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate); + let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate); + let integrity_test = decl_integrity_test(&scrate); + let static_assertions = decl_static_assertions(&name, &pallets, &scrate); + + let res = quote!( + #scrate_decl + + // Prevent UncheckedExtrinsic to print unused warning. + const _: () = { + #[allow(unused)] + type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic; + }; + + #[derive( + Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + pub struct #name; + impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name { + type RuntimeBlock = #block; + } + + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `runtime` may be used without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro runtime). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. + // `InternalConstructRuntime` is implemented by the `runtime` for Runtime references (`& Runtime`), + // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the `runtime` + // is used. + + #[doc(hidden)] + trait InternalConstructRuntime { + #[inline(always)] + fn runtime_metadata(&self) -> #scrate::__private::sp_std::vec::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> { + Default::default() + } + } + #[doc(hidden)] + impl InternalConstructRuntime for &#name {} + + #outer_event + + #outer_error + + #outer_origin + + #all_pallets + + #pallet_to_index + + #dispatch + + #task + + #metadata + + #outer_config + + #inherent + + #validate_unsigned + + #freeze_reason + + #hold_reason + + #lock_id + + #slash_reason + + #integrity_test + + #static_assertions + ); + + Ok(res) +} diff --git a/substrate/frame/support/procedural/src/runtime/mod.rs b/substrate/frame/support/procedural/src/runtime/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..aaae579eb086638f1bfacf02105a8fcd0e3d01b5 --- /dev/null +++ b/substrate/frame/support/procedural/src/runtime/mod.rs @@ -0,0 +1,236 @@ +// 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. + +//! Implementation of `runtime`. +//! +//! `runtime` implementation is recursive and can generate code which will call itself +//! in order to get all the pallet parts for each pallet. +//! +//! Pallets can define their parts: +//! - Implicitly: `pub type System = frame_system;` +//! - Explicitly: `pub type System = frame_system + Pallet + Call;` +//! +//! The `runtime` transitions from the implicit definition to the explicit one. +//! From the explicit state, Substrate expands the pallets with additional information +//! that is to be included in the runtime metadata. +//! +//! Pallets must provide the `tt_default_parts_v2` macro for these transitions. +//! These are automatically implemented by the `#[pallet::pallet]` macro. +//! +//! This macro also generates the following enums for ease of decoding if the respective type +//! is defined inside `#[runtime::derive]`: +//! - `enum RuntimeCall`: This type contains the information needed to decode extrinsics. +//! - `enum RuntimeEvent`: This type contains the information needed to decode events. +//! - `enum RuntimeError`: While this cannot be used directly to decode `sp_runtime::DispatchError` +//! from the chain, it contains the information needed to decode the +//! `sp_runtime::DispatchError::Module`. +//! +//! # State Transitions +//! +//! ```ignore +//! +----------+ +//! | Implicit | +//! +----------+ +//! | +//! v +//! +----------+ +//! | Explicit | +//! +----------+ +//! ``` +//! +//! The `runtime` macro transforms the implicit declaration of each pallet +//! `System: frame_system` to an explicit one `System: frame_system + Pallet + Call` using the +//! `tt_default_parts_v2` macro. +//! +//! The `tt_default_parts_v2` macro exposes a plus separated list of pallet parts. For example, the +//! `Event` part is exposed only if the pallet implements an event via `#[pallet::event]` macro. +//! The tokens generated by this macro are `+ Pallet + Call` for our example. +//! +//! The `match_and_insert` macro takes in 3 arguments: +//! - target: This is the `TokenStream` that contains the `runtime` macro. +//! - pattern: The pattern to match against in the target stream. +//! - tokens: The tokens to added after the pattern match. +//! +//! The `runtime` macro uses the `tt_call` to get the default pallet parts via +//! the `tt_default_parts_v2` macro defined by each pallet. The pallet parts are then returned as +//! input to the `match_and_replace` macro. +//! The `match_and_replace` then will modify the `runtime` to expand the implicit +//! definition to the explicit one. +//! +//! For example, +//! +//! ```ignore +//! #[frame_support::runtime] +//! mod runtime { +//! //... +//! +//! #[runtime::pallet_index(0)] +//! pub type System = frame_system; // Implicit definition of parts +//! +//! #[runtime::pallet_index(1)] +//! pub type Balances = pallet_balances; // Implicit definition of parts +//! } +//! ``` +//! This call has some implicit pallet parts, thus it will expand to: +//! ```ignore +//! frame_support::__private::tt_call! { +//! macro = [{ pallet_balances::tt_default_parts_v2 }] +//! ~~> frame_support::match_and_insert! { +//! target = [{ +//! frame_support::__private::tt_call! { +//! macro = [{ frame_system::tt_default_parts_v2 }] +//! ~~> frame_support::match_and_insert! { +//! target = [{ +//! #[frame_support::runtime] +//! mod runtime { +//! //... +//! +//! #[runtime::pallet_index(0)] +//! pub type System = frame_system; +//! +//! #[runtime::pallet_index(1)] +//! pub type Balances = pallet_balances; +//! } +//! }] +//! pattern = [{ System = frame_system }] +//! } +//! } +//! }] +//! pattern = [{ Balances = pallet_balances }] +//! } +//! } +//! ``` +//! `tt_default_parts_v2` must be defined. It returns the pallet parts inside some tokens, and +//! then `tt_call` will pipe the returned pallet parts into the input of `match_and_insert`. +//! Thus `match_and_insert` will initially receive the following inputs: +//! ```ignore +//! frame_support::match_and_insert! { +//! target = [{ +//! frame_support::match_and_insert! { +//! target = [{ +//! #[frame_support::runtime] +//! mod runtime { +//! //... +//! +//! #[runtime::pallet_index(0)] +//! pub type System = frame_system; +//! +//! #[runtime::pallet_index(1)] +//! pub type Balances = pallet_balances; +//! } +//! }] +//! pattern = [{ System = frame_system }] +//! tokens = [{ ::{+ Pallet + Call} }] +//! } +//! }] +//! pattern = [{ Balances = pallet_balances }] +//! tokens = [{ ::{+ Pallet + Call} }] +//! } +//! ``` +//! After dealing with `pallet_balances`, the inner `match_and_insert` will expand to: +//! ```ignore +//! frame_support::match_and_insert! { +//! target = [{ +//! #[frame_support::runtime] +//! mod runtime { +//! //... +//! +//! #[runtime::pallet_index(0)] +//! pub type System = frame_system; // Implicit definition of parts +//! +//! #[runtime::pallet_index(1)] +//! pub type Balances = pallet_balances + Pallet + Call; // Explicit definition of parts +//! } +//! }] +//! pattern = [{ System = frame_system }] +//! tokens = [{ ::{+ Pallet + Call} }] +//! } +//! ``` +//! +//! Which will then finally expand to the following: +//! ```ignore +//! #[frame_support::runtime] +//! mod runtime { +//! //... +//! +//! #[runtime::pallet_index(0)] +//! pub type System = frame_system + Pallet + Call; +//! +//! #[runtime::pallet_index(1)] +//! pub type Balances = pallet_balances + Pallet + Call; +//! } +//! ``` +//! +//! This call has no implicit pallet parts, thus it will expand to the runtime construction: +//! ```ignore +//! pub struct Runtime { ... } +//! pub struct Call { ... } +//! impl Call ... +//! pub enum Origin { ... } +//! ... +//! ``` +//! +//! Visualizing the entire flow of `#[frame_support::runtime]`, it would look like the following: +//! +//! ```ignore +//! +----------------------+ +------------------------+ +-------------------+ +//! | | | (defined in pallet) | | | +//! | runtime | --> | tt_default_parts_v2! | --> | match_and_insert! | +//! | w/ no pallet parts | | | | | +//! +----------------------+ +------------------------+ +-------------------+ +//! +//! +----------------------+ +//! | | +//! --> | runtime | +//! | w/ pallet parts | +//! +----------------------+ +//! ``` + +#![cfg(feature = "experimental")] + +pub use parse::Def; +use proc_macro::TokenStream; +use syn::spanned::Spanned; + +mod expand; +mod parse; + +mod keyword { + syn::custom_keyword!(legacy_ordering); +} + +pub fn runtime(attr: TokenStream, tokens: TokenStream) -> TokenStream { + let mut legacy_ordering = false; + if !attr.is_empty() { + if let Ok(_) = syn::parse::(attr.clone()) { + legacy_ordering = true; + } else { + let msg = "Invalid runtime macro call: unexpected attribute. Macro call must be \ + bare, such as `#[frame_support::runtime]` or `#[runtime]`, or must specify the \ + `legacy_ordering` attribute, such as `#[frame_support::runtime(legacy_ordering)]` or \ + #[runtime(legacy_ordering)]."; + let span = proc_macro2::TokenStream::from(attr).span(); + return syn::Error::new(span, msg).to_compile_error().into() + } + } + + let item = syn::parse_macro_input!(tokens as syn::ItemMod); + match parse::Def::try_from(item) { + Ok(def) => expand::expand(def, legacy_ordering).into(), + Err(e) => e.to_compile_error().into(), + } +} diff --git a/substrate/frame/support/procedural/src/runtime/parse/helper.rs b/substrate/frame/support/procedural/src/runtime/parse/helper.rs new file mode 100644 index 0000000000000000000000000000000000000000..f05395f9b7abd55201cdfd49f80debac32fde9f4 --- /dev/null +++ b/substrate/frame/support/procedural/src/runtime/parse/helper.rs @@ -0,0 +1,37 @@ +// 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. + +use crate::pallet::parse::helper::MutItemAttrs; +use quote::ToTokens; + +pub(crate) fn take_first_item_runtime_attr( + item: &mut impl MutItemAttrs, +) -> syn::Result> +where + Attr: syn::parse::Parse, +{ + let attrs = if let Some(attrs) = item.mut_item_attrs() { attrs } else { return Ok(None) }; + + if let Some(index) = attrs.iter().position(|attr| { + attr.path().segments.first().map_or(false, |segment| segment.ident == "runtime") + }) { + let runtime_attr = attrs.remove(index); + Ok(Some(syn::parse2(runtime_attr.into_token_stream())?)) + } else { + Ok(None) + } +} diff --git a/substrate/frame/support/procedural/src/runtime/parse/mod.rs b/substrate/frame/support/procedural/src/runtime/parse/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..893cb4726e2b6016fa2e728add86990e24087c42 --- /dev/null +++ b/substrate/frame/support/procedural/src/runtime/parse/mod.rs @@ -0,0 +1,266 @@ +// 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. + +pub mod helper; +pub mod pallet; +pub mod pallet_decl; +pub mod runtime_struct; +pub mod runtime_types; + +use crate::construct_runtime::parse::Pallet; +use pallet_decl::PalletDeclaration; +use proc_macro2::TokenStream as TokenStream2; +use quote::ToTokens; +use std::collections::HashMap; +use syn::{spanned::Spanned, Ident, Token}; + +use frame_support_procedural_tools::syn_ext as ext; +use runtime_types::RuntimeType; + +mod keyword { + use syn::custom_keyword; + + custom_keyword!(runtime); + custom_keyword!(derive); + custom_keyword!(pallet_index); + custom_keyword!(disable_call); + custom_keyword!(disable_unsigned); +} + +enum RuntimeAttr { + Runtime(proc_macro2::Span), + Derive(proc_macro2::Span, Vec), + PalletIndex(proc_macro2::Span, u8), + DisableCall(proc_macro2::Span), + DisableUnsigned(proc_macro2::Span), +} + +impl RuntimeAttr { + fn span(&self) -> proc_macro2::Span { + match self { + Self::Runtime(span) => *span, + Self::Derive(span, _) => *span, + Self::PalletIndex(span, _) => *span, + Self::DisableCall(span) => *span, + Self::DisableUnsigned(span) => *span, + } + } +} + +impl syn::parse::Parse for RuntimeAttr { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::runtime) { + Ok(RuntimeAttr::Runtime(content.parse::()?.span())) + } else if lookahead.peek(keyword::derive) { + let _ = content.parse::(); + let derive_content; + syn::parenthesized!(derive_content in content); + let runtime_types = + derive_content.parse::>()?; + let runtime_types = runtime_types.inner.into_iter().collect(); + Ok(RuntimeAttr::Derive(derive_content.span(), runtime_types)) + } else if lookahead.peek(keyword::pallet_index) { + let _ = content.parse::(); + let pallet_index_content; + syn::parenthesized!(pallet_index_content in content); + let pallet_index = pallet_index_content.parse::()?; + if !pallet_index.suffix().is_empty() { + let msg = "Number literal must not have a suffix"; + return Err(syn::Error::new(pallet_index.span(), msg)) + } + Ok(RuntimeAttr::PalletIndex(pallet_index.span(), pallet_index.base10_parse()?)) + } else if lookahead.peek(keyword::disable_call) { + Ok(RuntimeAttr::DisableCall(content.parse::()?.span())) + } else if lookahead.peek(keyword::disable_unsigned) { + Ok(RuntimeAttr::DisableUnsigned(content.parse::()?.span())) + } else { + Err(lookahead.error()) + } + } +} + +#[derive(Debug, Clone)] +pub enum AllPalletsDeclaration { + Implicit(ImplicitAllPalletsDeclaration), + Explicit(ExplicitAllPalletsDeclaration), +} + +/// Declaration of a runtime with some pallet with implicit declaration of parts. +#[derive(Debug, Clone)] +pub struct ImplicitAllPalletsDeclaration { + pub name: Ident, + pub pallet_decls: Vec, + pub pallet_count: usize, +} + +/// Declaration of a runtime with all pallet having explicit declaration of parts. +#[derive(Debug, Clone)] +pub struct ExplicitAllPalletsDeclaration { + pub name: Ident, + pub pallets: Vec, +} + +pub struct Def { + pub input: TokenStream2, + pub item: syn::ItemMod, + pub runtime_struct: runtime_struct::RuntimeStructDef, + pub pallets: AllPalletsDeclaration, + pub runtime_types: Vec, +} + +impl Def { + pub fn try_from(mut item: syn::ItemMod) -> syn::Result { + let input: TokenStream2 = item.to_token_stream().into(); + let item_span = item.span(); + let items = &mut item + .content + .as_mut() + .ok_or_else(|| { + let msg = "Invalid runtime definition, expected mod to be inlined."; + syn::Error::new(item_span, msg) + })? + .1; + + let mut runtime_struct = None; + let mut runtime_types = None; + + let mut indices = HashMap::new(); + let mut names = HashMap::new(); + + let mut pallet_decls = vec![]; + let mut pallets = vec![]; + + for item in items.iter_mut() { + let mut pallet_item = None; + let mut pallet_index = 0; + + let mut disable_call = false; + let mut disable_unsigned = false; + + while let Some(runtime_attr) = + helper::take_first_item_runtime_attr::(item)? + { + match runtime_attr { + RuntimeAttr::Runtime(span) if runtime_struct.is_none() => { + let p = runtime_struct::RuntimeStructDef::try_from(span, item)?; + runtime_struct = Some(p); + }, + RuntimeAttr::Derive(_, types) if runtime_types.is_none() => { + runtime_types = Some(types); + }, + RuntimeAttr::PalletIndex(span, index) => { + pallet_index = index; + pallet_item = if let syn::Item::Type(item) = item { + Some(item.clone()) + } else { + let msg = "Invalid runtime::pallet_index, expected type definition"; + return Err(syn::Error::new(span, msg)) + }; + }, + RuntimeAttr::DisableCall(_) => disable_call = true, + RuntimeAttr::DisableUnsigned(_) => disable_unsigned = true, + attr => { + let msg = "Invalid duplicated attribute"; + return Err(syn::Error::new(attr.span(), msg)) + }, + } + } + + if let Some(pallet_item) = pallet_item { + match *pallet_item.ty.clone() { + syn::Type::Path(ref path) => { + let pallet_decl = + PalletDeclaration::try_from(item.span(), &pallet_item, path)?; + + if let Some(used_pallet) = + names.insert(pallet_decl.name.clone(), pallet_decl.name.span()) + { + let msg = "Two pallets with the same name!"; + + let mut err = syn::Error::new(used_pallet, &msg); + err.combine(syn::Error::new(pallet_decl.name.span(), &msg)); + return Err(err) + } + + pallet_decls.push(pallet_decl); + }, + syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) => { + let pallet = Pallet::try_from( + item.span(), + &pallet_item, + pallet_index, + disable_call, + disable_unsigned, + &bounds, + )?; + + if let Some(used_pallet) = indices.insert(pallet.index, pallet.name.clone()) + { + let msg = format!( + "Pallet indices are conflicting: Both pallets {} and {} are at index {}", + used_pallet, pallet.name, pallet.index, + ); + let mut err = syn::Error::new(used_pallet.span(), &msg); + err.combine(syn::Error::new(pallet.name.span(), msg)); + return Err(err) + } + + pallets.push(pallet); + }, + _ => continue, + } + } + } + + let name = item.ident.clone(); + let decl_count = pallet_decls.len(); + let pallets = if decl_count > 0 { + AllPalletsDeclaration::Implicit(ImplicitAllPalletsDeclaration { + name, + pallet_decls, + pallet_count: decl_count.saturating_add(pallets.len()), + }) + } else { + AllPalletsDeclaration::Explicit(ExplicitAllPalletsDeclaration { name, pallets }) + }; + + let def = Def { + input, + item, + runtime_struct: runtime_struct.ok_or_else(|| { + syn::Error::new(item_span, + "Missing Runtime. Please add a struct inside the module and annotate it with `#[runtime::runtime]`" + ) + })?, + pallets, + runtime_types: runtime_types.ok_or_else(|| { + syn::Error::new(item_span, + "Missing Runtime Types. Please annotate the runtime struct with `#[runtime::derive]`" + ) + })?, + }; + + Ok(def) + } +} diff --git a/substrate/frame/support/procedural/src/runtime/parse/pallet.rs b/substrate/frame/support/procedural/src/runtime/parse/pallet.rs new file mode 100644 index 0000000000000000000000000000000000000000..d2f1857fb2b4feec841224f28d691076c8853427 --- /dev/null +++ b/substrate/frame/support/procedural/src/runtime/parse/pallet.rs @@ -0,0 +1,99 @@ +// 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. + +use crate::construct_runtime::parse::{Pallet, PalletPart, PalletPartKeyword, PalletPath}; +use quote::ToTokens; +use syn::{punctuated::Punctuated, spanned::Spanned, token, Error, Ident, PathArguments}; + +impl Pallet { + pub fn try_from( + attr_span: proc_macro2::Span, + item: &syn::ItemType, + pallet_index: u8, + disable_call: bool, + disable_unsigned: bool, + bounds: &Punctuated, + ) -> syn::Result { + let name = item.ident.clone(); + + let mut pallet_path = None; + let mut pallet_parts = vec![]; + + for (index, bound) in bounds.into_iter().enumerate() { + if let syn::TypeParamBound::Trait(syn::TraitBound { path, .. }) = bound { + if index == 0 { + pallet_path = Some(PalletPath { inner: path.clone() }); + } else { + let pallet_part = syn::parse2::(bound.into_token_stream())?; + pallet_parts.push(pallet_part); + } + } else { + return Err(Error::new( + attr_span, + "Invalid pallet declaration, expected a path or a trait object", + )) + }; + } + + let mut path = pallet_path.ok_or(Error::new( + attr_span, + "Invalid pallet declaration, expected a path or a trait object", + ))?; + + let mut instance = None; + if let Some(segment) = path.inner.segments.iter_mut().find(|seg| !seg.arguments.is_empty()) + { + if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { + args, .. + }) = segment.arguments.clone() + { + if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() { + instance = + Some(Ident::new(&arg_path.to_token_stream().to_string(), arg_path.span())); + segment.arguments = PathArguments::None; + } + } + } + + pallet_parts = pallet_parts + .into_iter() + .filter(|part| { + if let (true, &PalletPartKeyword::Call(_)) = (disable_call, &part.keyword) { + false + } else if let (true, &PalletPartKeyword::ValidateUnsigned(_)) = + (disable_unsigned, &part.keyword) + { + false + } else { + true + } + }) + .collect(); + + let cfg_pattern = vec![]; + + Ok(Pallet { + is_expanded: true, + name, + index: pallet_index, + path, + instance, + cfg_pattern, + pallet_parts, + }) + } +} diff --git a/substrate/frame/support/procedural/src/runtime/parse/pallet_decl.rs b/substrate/frame/support/procedural/src/runtime/parse/pallet_decl.rs new file mode 100644 index 0000000000000000000000000000000000000000..437a163cfbc44973e753075d4a139fca5df81af8 --- /dev/null +++ b/substrate/frame/support/procedural/src/runtime/parse/pallet_decl.rs @@ -0,0 +1,60 @@ +// 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. + +use quote::ToTokens; +use syn::{spanned::Spanned, Attribute, Ident, PathArguments}; + +/// The declaration of a pallet. +#[derive(Debug, Clone)] +pub struct PalletDeclaration { + /// The name of the pallet, e.g.`System` in `System: frame_system`. + pub name: Ident, + /// Optional attributes tagged right above a pallet declaration. + pub attrs: Vec, + /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. + pub path: syn::Path, + /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. + pub instance: Option, +} + +impl PalletDeclaration { + pub fn try_from( + _attr_span: proc_macro2::Span, + item: &syn::ItemType, + path: &syn::TypePath, + ) -> syn::Result { + let name = item.ident.clone(); + + let mut path = path.path.clone(); + + let mut instance = None; + if let Some(segment) = path.segments.iter_mut().find(|seg| !seg.arguments.is_empty()) { + if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { + args, .. + }) = segment.arguments.clone() + { + if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() { + instance = + Some(Ident::new(&arg_path.to_token_stream().to_string(), arg_path.span())); + segment.arguments = PathArguments::None; + } + } + } + + Ok(Self { name, path, instance, attrs: item.attrs.clone() }) + } +} diff --git a/substrate/frame/support/procedural/src/runtime/parse/runtime_struct.rs b/substrate/frame/support/procedural/src/runtime/parse/runtime_struct.rs new file mode 100644 index 0000000000000000000000000000000000000000..8fa746ee80727d7164d5dadb0f728fc7991362f7 --- /dev/null +++ b/substrate/frame/support/procedural/src/runtime/parse/runtime_struct.rs @@ -0,0 +1,35 @@ +// 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. + +use syn::spanned::Spanned; +pub struct RuntimeStructDef { + pub ident: syn::Ident, + pub attr_span: proc_macro2::Span, +} + +impl RuntimeStructDef { + pub fn try_from(attr_span: proc_macro2::Span, item: &mut syn::Item) -> syn::Result { + let item = if let syn::Item::Struct(item) = item { + item + } else { + let msg = "Invalid runtime::runtime, expected struct definition"; + return Err(syn::Error::new(item.span(), msg)) + }; + + Ok(Self { ident: item.ident.clone(), attr_span }) + } +} diff --git a/substrate/frame/support/procedural/src/runtime/parse/runtime_types.rs b/substrate/frame/support/procedural/src/runtime/parse/runtime_types.rs new file mode 100644 index 0000000000000000000000000000000000000000..a4480e2a1fd32622bea3a7f20294b4c2bee88309 --- /dev/null +++ b/substrate/frame/support/procedural/src/runtime/parse/runtime_types.rs @@ -0,0 +1,76 @@ +// 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. + +use syn::{ + parse::{Parse, ParseStream}, + Result, +}; + +mod keyword { + use syn::custom_keyword; + + custom_keyword!(RuntimeCall); + custom_keyword!(RuntimeEvent); + custom_keyword!(RuntimeError); + custom_keyword!(RuntimeOrigin); + custom_keyword!(RuntimeFreezeReason); + custom_keyword!(RuntimeHoldReason); + custom_keyword!(RuntimeSlashReason); + custom_keyword!(RuntimeLockId); + custom_keyword!(RuntimeTask); +} + +#[derive(Debug, Clone, PartialEq)] +pub enum RuntimeType { + RuntimeCall(keyword::RuntimeCall), + RuntimeEvent(keyword::RuntimeEvent), + RuntimeError(keyword::RuntimeError), + RuntimeOrigin(keyword::RuntimeOrigin), + RuntimeFreezeReason(keyword::RuntimeFreezeReason), + RuntimeHoldReason(keyword::RuntimeHoldReason), + RuntimeSlashReason(keyword::RuntimeSlashReason), + RuntimeLockId(keyword::RuntimeLockId), + RuntimeTask(keyword::RuntimeTask), +} + +impl Parse for RuntimeType { + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + + if lookahead.peek(keyword::RuntimeCall) { + Ok(Self::RuntimeCall(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeEvent) { + Ok(Self::RuntimeEvent(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeError) { + Ok(Self::RuntimeError(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeOrigin) { + Ok(Self::RuntimeOrigin(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeFreezeReason) { + Ok(Self::RuntimeFreezeReason(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeHoldReason) { + Ok(Self::RuntimeHoldReason(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeSlashReason) { + Ok(Self::RuntimeSlashReason(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeLockId) { + Ok(Self::RuntimeLockId(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeTask) { + Ok(Self::RuntimeTask(input.parse()?)) + } else { + Err(lookahead.error()) + } + } +} diff --git a/substrate/frame/support/src/dispatch_context.rs b/substrate/frame/support/src/dispatch_context.rs index 608187b72206399913e337a5fa9f5c1e42277978..254302c8f14d2db1987353155ab7cacde65a5bb3 100644 --- a/substrate/frame/support/src/dispatch_context.rs +++ b/substrate/frame/support/src/dispatch_context.rs @@ -18,7 +18,7 @@ //! Provides functions to interact with the dispatch context. //! //! A Dispatch context is created by calling [`run_in_context`] and then the given closure will be -//! executed in this dispatch context. Everyting run in this `closure` will have access to the same +//! executed in this dispatch context. Everything run in this `closure` will have access to the same //! dispatch context. This also applies to nested calls of [`run_in_context`]. The dispatch context //! can be used to store and retrieve information locally in this context. The dispatch context can //! be accessed by using [`with_context`]. This function will execute the given closure and give it @@ -51,7 +51,7 @@ //! //! run_in_context(|| { //! with_context::(|v| { -//! // Intitialize the value to the default value. +//! // Initialize the value to the default value. //! assert_eq!(0, v.or_default().0); //! v.or_default().0 = 10; //! }); diff --git a/substrate/frame/support/src/instances.rs b/substrate/frame/support/src/instances.rs index 396018d5cbd5289db9c7c6e89d861adee4b2df9a..ecb356af50b6fd2dded7bd4d7a5536f2ce7bf069 100644 --- a/substrate/frame/support/src/instances.rs +++ b/substrate/frame/support/src/instances.rs @@ -31,83 +31,83 @@ //! NOTE: [`frame_support::pallet`] will reexport them inside the module, in order to make them //! accessible to [`frame_support::construct_runtime`]. -/// `Instance1` to be used for instantiable palllets defined with the +/// `Instance1` to be used for instantiable pallets defined with the /// [`#[pallet]`](`frame_support::pallet`) macro. Instances 2-16 are also available but are hidden /// from docs. #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance1; -/// `Instance2` to be used for instantiable palllets defined with the `#[pallet]` macro. +/// `Instance2` to be used for instantiable pallets defined with the `#[pallet]` macro. #[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance2; -/// `Instance3` to be used for instantiable palllets defined with the `#[pallet]` macro. +/// `Instance3` to be used for instantiable pallets defined with the `#[pallet]` macro. #[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance3; -/// `Instance4` to be used for instantiable palllets defined with the `#[pallet]` macro. +/// `Instance4` to be used for instantiable pallets defined with the `#[pallet]` macro. #[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance4; -/// `Instance5` to be used for instantiable palllets defined with the `#[pallet]` macro. +/// `Instance5` to be used for instantiable pallets defined with the `#[pallet]` macro. #[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance5; -/// `Instance6` to be used for instantiable palllets defined with the `#[pallet]` macro. +/// `Instance6` to be used for instantiable pallets defined with the `#[pallet]` macro. #[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance6; -/// `Instance7` to be used for instantiable palllets defined with the `#[pallet]` macro. +/// `Instance7` to be used for instantiable pallets defined with the `#[pallet]` macro. #[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance7; -/// `Instance8` to be used for instantiable palllets defined with the `#[pallet]` macro. +/// `Instance8` to be used for instantiable pallets defined with the `#[pallet]` macro. #[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance8; -/// `Instance9` to be used for instantiable palllets defined with the `#[pallet]` macro. +/// `Instance9` to be used for instantiable pallets defined with the `#[pallet]` macro. #[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance9; -/// `Instance10` to be used for instantiable palllets defined with the `#[pallet]` macro. +/// `Instance10` to be used for instantiable pallets defined with the `#[pallet]` macro. #[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance10; -/// `Instance11` to be used for instantiable palllets defined with the `#[pallet]` macro. +/// `Instance11` to be used for instantiable pallets defined with the `#[pallet]` macro. #[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance11; -/// `Instance12` to be used for instantiable palllets defined with the `#[pallet]` macro. +/// `Instance12` to be used for instantiable pallets defined with the `#[pallet]` macro. #[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance12; -/// `Instance13` to be used for instantiable palllets defined with the `#[pallet]` macro. +/// `Instance13` to be used for instantiable pallets defined with the `#[pallet]` macro. #[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance13; -/// `Instance14` to be used for instantiable palllets defined with the `#[pallet]` macro. +/// `Instance14` to be used for instantiable pallets defined with the `#[pallet]` macro. #[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance14; -/// `Instance15` to be used for instantiable palllets defined with the `#[pallet]` macro. +/// `Instance15` to be used for instantiable pallets defined with the `#[pallet]` macro. #[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance15; -/// `Instance16` to be used for instantiable palllets defined with the `#[pallet]` macro. +/// `Instance16` to be used for instantiable pallets defined with the `#[pallet]` macro. #[doc(hidden)] #[derive(Clone, Copy, PartialEq, Eq, crate::RuntimeDebugNoBound)] pub struct Instance16; diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index 8935acf4e2bb64dc0087c492e2174741cbb40d80..895215d364e3dfccad33a805c6db34857c014ae8 100644 --- a/substrate/frame/support/src/lib.rs +++ b/substrate/frame/support/src/lib.rs @@ -145,7 +145,7 @@ impl TypeId for PalletId { /// # Examples /// /// There are different ways to declare the `prefix` to use. The `prefix` type can either be -/// declared explicetly by passing it to the macro as an attribute or by letting the macro +/// declared explicitly by passing it to the macro as an attribute or by letting the macro /// guess on what the `prefix` type is. The `prefix` is always passed as the first generic /// argument to the type declaration. When using [`#[pallet::storage]`](pallet_macros::storage) /// this first generic argument is always `_`. Besides declaring the `prefix`, the rest of the @@ -508,6 +508,9 @@ pub use frame_support_procedural::{ construct_runtime, match_and_insert, transactional, PalletError, RuntimeDebugNoBound, }; +#[cfg(feature = "experimental")] +pub use frame_support_procedural::runtime; + #[doc(hidden)] pub use frame_support_procedural::{__create_tt_macro, __generate_dummy_part_checker}; @@ -700,7 +703,7 @@ pub use frame_support_procedural::crate_to_crate_version; #[macro_export] macro_rules! fail { ( $y:expr ) => {{ - return Err($y.into()) + return Err($y.into()); }}; } @@ -780,7 +783,7 @@ macro_rules! assert_err_with_weight { $crate::assert_err!($call.map(|_| ()).map_err(|e| e.error), $err); assert_eq!(dispatch_err_with_post.post_info.actual_weight, $weight); } else { - panic!("expected Err(_), got Ok(_).") + ::core::panic!("expected Err(_), got Ok(_).") } }; } @@ -900,18 +903,20 @@ pub mod pallet_prelude { }; pub use codec::{Decode, Encode, MaxEncodedLen}; pub use frame_support::pallet_macros::*; + /// The optional attribute `#[inject_runtime_type]` can be attached to `RuntimeCall`, /// `RuntimeEvent`, `RuntimeOrigin` or `PalletInfo` in an impl statement that has /// `#[register_default_impl]` attached to indicate that this item is generated by /// `construct_runtime`. /// /// Attaching this attribute to such an item ensures that the combined impl generated via - /// [`#[derive_impl(..)]`](`macro@super::derive_impl`) will use the correct type - /// auto-generated by `construct_runtime!`. + /// [`#[derive_impl(..)]`](`frame_support::derive_impl`) will use the correct + /// type auto-generated by + /// `construct_runtime!`. #[doc = docify::embed!("src/tests/inject_runtime_type.rs", derive_impl_works_with_runtime_type_injection)] /// /// However, if `no_aggregated_types` is specified while using - /// `[`#[derive_impl(..)]`](`macro@super::derive_impl`)`, then these items are attached + /// `[`#[derive_impl(..)]`](`frame_support::derive_impl`)`, then these items are attached /// verbatim to the combined impl. #[doc = docify::embed!("src/tests/inject_runtime_type.rs", derive_impl_works_with_no_aggregated_types)] pub use frame_support_procedural::inject_runtime_type; @@ -931,129 +936,26 @@ pub mod pallet_prelude { pub use sp_weights::Weight; } -/// The `pallet` attribute macro defines a pallet that can be used with -/// [`construct_runtime!`]. It must be attached to a module named `pallet` as follows: +/// The pallet struct placeholder `#[pallet::pallet]` is mandatory and allows you to +/// specify pallet information. /// -/// ```ignore -/// #[pallet] -/// pub mod pallet { -/// ... -/// } +/// The struct must be defined as follows: /// ``` +/// #[frame_support::pallet] +/// mod pallet { +/// #[pallet::pallet] // <- the macro +/// pub struct Pallet(_); // <- the struct definition /// -/// Note that various types can be automatically imported using -/// [`frame_support::pallet_prelude`] and `frame_system::pallet_prelude`: -/// -/// ```ignore -/// #[pallet] -/// pub mod pallet { -/// use frame_support::pallet_prelude::*; -/// use frame_system::pallet_prelude::*; -/// ... +/// #[pallet::config] +/// pub trait Config: frame_system::Config {} /// } /// ``` -/// -/// # pallet::* Attributes -/// -/// The `pallet` macro will parse any items within your `pallet` module that are annotated with -/// `#[pallet::*]` attributes. Some of these attributes are mandatory and some are optional, -/// and they can attach to different types of items within your pallet depending on the -/// attribute in question. The full list of `#[pallet::*]` attributes is shown below in the -/// order in which they are mentioned in this document: -/// -/// * [`pallet::pallet`](#pallet-struct-placeholder-palletpallet-mandatory) -/// * [`pallet::config`](#config-trait-palletconfig-mandatory) -/// * [`pallet::constant`](#palletconstant) -/// * [`pallet::disable_frame_system_supertrait_check`](#disable_supertrait_check) -/// * [`pallet::generate_store($vis trait Store)`](#palletgenerate_storevis-trait-store) -/// * [`pallet::storage_version`](#palletstorage_version) -/// * [`pallet::hooks`](#hooks-pallethooks-optional) -/// * [`pallet::call`](#call-palletcall-optional) -/// * [`pallet::weight($expr)`](#palletweightexpr) -/// * [`pallet::compact`](#palletcompact-some_arg-some_type) -/// * [`pallet::call_index($idx)`](#palletcall_indexidx) -/// * [`pallet::extra_constants`](#extra-constants-palletextra_constants-optional) -/// * [`pallet::error`](#error-palleterror-optional) -/// * [`pallet::event`](#event-palletevent-optional) -/// * [`pallet::generate_deposit($visibility fn -/// deposit_event)`](#palletgenerate_depositvisibility-fn-deposit_event) -/// * [`pallet::storage`](#storage-palletstorage-optional) -/// * [`pallet::getter(fn $my_getter_fn_name)`](#palletgetterfn-my_getter_fn_name-optional) -/// * [`pallet::storage_prefix = "SomeName"`](#palletstorage_prefix--somename-optional) -/// * [`pallet::unbounded`](#palletunbounded-optional) -/// * [`pallet::whitelist_storage`](#palletwhitelist_storage-optional) -/// * [`cfg(..)`](#cfg-for-storage) (on storage items) -/// * [`pallet::type_value`](#type-value-pallettype_value-optional) -/// * [`pallet::genesis_config`](#genesis-config-palletgenesis_config-optional) -/// * [`pallet::genesis_build`](#genesis-build-palletgenesis_build-optional) -/// * [`pallet::inherent`](#inherent-palletinherent-optional) -/// * [`pallet::validate_unsigned`](#validate-unsigned-palletvalidate_unsigned-optional) -/// * [`pallet::origin`](#origin-palletorigin-optional) -/// * [`pallet::composite_enum`](#composite-enum-palletcomposite_enum-optional) -/// -/// Note that at compile-time, the `#[pallet]` macro will analyze and expand all of these -/// attributes, ultimately removing their AST nodes before they can be parsed as real -/// attribute macro calls. This means that technically we do not need attribute macro -/// definitions for any of these attributes, however, for consistency and discoverability -/// reasons, we still maintain stub attribute macro definitions for all of these attributes in -/// the [`pallet_macros`] module which is automatically included in all pallets as part of the -/// pallet prelude. The actual "work" for all of these attribute macros can be found in the -/// macro expansion for `#[pallet]`. -/// -/// Also note that in this document, pallet attributes are explained using the syntax of -/// non-instantiable pallets. For an example of an instantiable pallet, see [this -/// example](#example-of-an-instantiable-pallet). -/// -/// # Dev Mode (`#[pallet(dev_mode)]`) -/// -/// Specifying the argument `dev_mode` on the `#[pallet]` or `#[frame_support::pallet]` -/// attribute attached to your pallet module will allow you to enable dev mode for a pallet. -/// The aim of dev mode is to loosen some of the restrictions and requirements placed on -/// production pallets for easy tinkering and development. Dev mode pallets should not be used -/// in production. Enabling dev mode has the following effects: -/// -/// * Weights no longer need to be specified on every `#[pallet::call]` declaration. By -/// default, dev mode pallets will assume a weight of zero (`0`) if a weight is not -/// specified. This is equivalent to specifying `#[weight(0)]` on all calls that do not -/// specify a weight. -/// * Call indices no longer need to be specified on every `#[pallet::call]` declaration. By -/// default, dev mode pallets will assume a call index based on the order of the call. -/// * All storages are marked as unbounded, meaning you do not need to implement -/// `MaxEncodedLen` on storage types. This is equivalent to specifying `#[pallet::unbounded]` -/// on all storage type definitions. -/// * Storage hashers no longer need to be specified and can be replaced by `_`. In dev mode, -/// these will be replaced by `Blake2_128Concat`. In case of explicit key-binding, `Hasher` -/// can simply be ignored when in `dev_mode`. -/// -/// Note that the `dev_mode` argument can only be supplied to the `#[pallet]` or -/// `#[frame_support::pallet]` attribute macro that encloses your pallet module. This argument -/// cannot be specified anywhere else, including but not limited to the `#[pallet::pallet]` -/// attribute macro. -/// -///
-/// WARNING:
-/// You should not deploy or use dev mode pallets in production. Doing so can break your chain
-/// and therefore should never be done. Once you are done tinkering, you should remove the
-/// 'dev_mode' argument from your #[pallet] declaration and fix any compile errors before
-/// attempting to use your pallet in a production scenario.
-/// 
-/// -/// # Pallet struct placeholder: `#[pallet::pallet]` (mandatory) -/// -/// The pallet struct placeholder `#[pallet::pallet]` is mandatory and allows you to specify -/// pallet information. -/// -/// The struct must be defined as follows: -/// ```ignore -/// #[pallet::pallet] -/// pub struct Pallet(_); -/// ``` +// /// I.e. a regular struct definition named `Pallet`, with generic T and no where clause. /// /// ## Macro expansion: /// -/// The macro adds this attribute to the struct definition: +/// The macro adds this attribute to the Pallet struct definition: /// ```ignore /// #[derive( /// frame_support::CloneNoBound, @@ -1062,1225 +964,926 @@ pub mod pallet_prelude { /// frame_support::RuntimeDebugNoBound, /// )] /// ``` -/// and replaces the type `_` with `PhantomData`. It also implements on the pallet: -/// * [`GetStorageVersion`](`traits::GetStorageVersion`) -/// * [`OnGenesis`](`traits::OnGenesis`): contains some logic to write the pallet version into -/// storage. -/// * `PalletErrorTypeInfo`: provides the type information for the pallet error, if defined. -/// -/// It declares `type Module` type alias for `Pallet`, used by `construct_runtime`. -/// -/// It implements [`PalletInfoAccess`](`traits::PalletInfoAccess') on `Pallet` to ease access -/// to pallet information given by [`frame_support::traits::PalletInfo`]. (The implementation -/// uses the associated type `frame_system::Config::PalletInfo`). +/// and replaces the type `_` with `PhantomData`. /// -/// It implements [`StorageInfoTrait`](`traits::StorageInfoTrait`) on `Pallet` which give -/// information about all storages. +/// It also implements on the pallet: /// -/// If the attribute `generate_store` is set then the macro creates the trait `Store` and -/// implements it on `Pallet`. +/// * [`GetStorageVersion`](frame_support::traits::GetStorageVersion) +/// * [`OnGenesis`](frame_support::traits::OnGenesis): contains some logic to write the pallet +/// version into storage. +/// * [`PalletInfoAccess`](frame_support::traits::PalletInfoAccess) to ease access to pallet +/// information given by [`frame_support::traits::PalletInfo`]. (The implementation uses the +/// associated type [`frame_support::traits::PalletInfo`]). +/// * [`StorageInfoTrait`](frame_support::traits::StorageInfoTrait) to give information about +/// storages. /// /// If the attribute `set_storage_max_encoded_len` is set then the macro calls -/// [`StorageInfoTrait`](`traits::StorageInfoTrait`) for each storage in the implementation of -/// [`StorageInfoTrait`](`traits::StorageInfoTrait`) for the pallet. Otherwise it implements -/// [`StorageInfoTrait`](`traits::StorageInfoTrait`) for the pallet using the -/// [`PartialStorageInfoTrait`](`traits::PartialStorageInfoTrait`) implementation of storages. -/// -/// # Config trait: `#[pallet::config]` (mandatory) -/// -/// The mandatory attribute `#[pallet::config]` defines the configurable options for the -/// pallet. -/// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::config] -/// pub trait Config: frame_system::Config + $optionally_some_other_supertraits -/// $optional_where_clause -/// { -/// ... -/// } -/// ``` -/// -/// I.e. a regular trait definition named `Config`, with the supertrait -/// `frame_system::pallet::Config`, and optionally other supertraits and a where clause. -/// (Specifying other supertraits here is known as [tight -/// coupling](https://docs.substrate.io/reference/how-to-guides/pallet-design/use-tight-coupling/)) -/// -/// The associated type `RuntimeEvent` is reserved. If defined, it must have the bounds -/// `From` and `IsType<::RuntimeEvent>`. -/// -/// [`pallet::event`](`frame_support::pallet_macros::event`) must be present if `RuntimeEvent` -/// exists as a config item in your `#[pallet::config]`. -/// -/// Also see [`pallet::config`](`frame_support::pallet_macros::config`) -/// -/// ## `pallet::constant` -/// -/// The `#[pallet::constant]` attribute can be used to add an associated type trait bounded by -/// [`Get`](crate::traits::Get) from [`pallet::config`](#palletconfig) into metadata, e.g.: -/// -/// ```ignore -/// #[pallet::config] -/// pub trait Config: frame_system::Config { -/// #[pallet::constant] -/// type Foo: Get; -/// } -/// ``` -/// -/// Also see [`pallet::constant`](`frame_support::pallet_macros::constant`) -/// -/// ## `pallet::disable_frame_system_supertrait_check` -/// -/// -/// To bypass the `frame_system::Config` supertrait check, use the attribute -/// `pallet::disable_frame_system_supertrait_check`, e.g.: -/// -/// ```ignore -/// #[pallet::config] -/// #[pallet::disable_frame_system_supertrait_check] -/// pub trait Config: pallet_timestamp::Config {} -/// ``` -/// -/// NOTE: Bypassing the `frame_system::Config` supertrait check is typically desirable when you -/// want to write an alternative to the `frame_system` pallet. -/// -/// Also see -/// [`pallet::disable_frame_system_supertrait_check`](`frame_support::pallet_macros::disable_frame_system_supertrait_check`) -/// -/// ## Macro expansion: -/// -/// The macro expands pallet constant metadata with the information given by -/// `#[pallet::constant]`. -/// -/// # `pallet::generate_store($vis trait Store)` -/// -/// To generate a `Store` trait associating all storages, annotate your `Pallet` struct with -/// the attribute `#[pallet::generate_store($vis trait Store)]`, e.g.: -/// -/// ```ignore -/// #[pallet::pallet] -/// #[pallet::generate_store(pub(super) trait Store)] -/// pub struct Pallet(_); -/// ``` -/// More precisely, the `Store` trait contains an associated type for each storage. It is -/// implemented for `Pallet` allowing access to the storage from pallet struct. -/// -/// Thus when defining a storage named `Foo`, it can later be accessed from `Pallet` using -/// `::Foo`. -/// -/// NOTE: this attribute is only valid when applied _directly_ to your `Pallet` struct -/// definition. -/// -/// Also see [`pallet::generate_store`](`frame_support::pallet_macros::generate_store`). -/// -/// # `pallet::storage_version` -/// -/// Because the [`pallet::pallet`](#pallet-struct-placeholder-palletpallet-mandatory) macro -/// implements [`traits::GetStorageVersion`], the current storage version needs to be -/// communicated to the macro. This can be done by using the `pallet::storage_version` -/// attribute: -/// -/// ```ignore -/// const STORAGE_VERSION: StorageVersion = StorageVersion::new(5); -/// -/// #[pallet::pallet] -/// #[pallet::storage_version(STORAGE_VERSION)] -/// pub struct Pallet(_); -/// ``` -/// -/// If not present, the current storage version is set to the default value. -/// -/// Also see [`pallet::storage_version`](`frame_support::pallet_macros::storage_version`) -/// -/// # Hooks: `#[pallet::hooks]` (optional) -/// -/// The `pallet::hooks` attribute allows you to specify a `Hooks` implementation for `Pallet` -/// that specifies pallet-specific logic. -/// -/// The item the attribute attaches to must be defined as follows: -/// ```ignore -/// #[pallet::hooks] -/// impl Hooks> for Pallet $optional_where_clause { -/// ... -/// } -/// ``` -/// I.e. a regular trait implementation with generic bound: `T: Config`, for the trait -/// `Hooks>` (they are defined in preludes), for the type `Pallet` and -/// with an optional where clause. -/// -/// If no `#[pallet::hooks]` exists, then the following default implementation is -/// automatically generated: -/// ```ignore -/// #[pallet::hooks] -/// impl Hooks> for Pallet {} -/// ``` -/// -/// Also see [`pallet::hooks`](`frame_support::pallet_macros::hooks`) -/// -/// # Call: `#[pallet::call]` (optional) -/// -/// Implementation of pallet dispatchables. -/// -/// Item must be defined as: -/// ```ignore -/// #[pallet::call] -/// impl Pallet { -/// /// $some_doc -/// #[pallet::weight($ExpressionResultingInWeight)] -/// pub fn $fn_name( -/// origin: OriginFor, -/// $some_arg: $some_type, -/// // or with compact attribute: #[pallet::compact] $some_arg: $some_type, -/// ... -/// ) -> DispatchResultWithPostInfo { // or `-> DispatchResult` -/// ... -/// } -/// ... -/// } -/// ``` -/// I.e. a regular type implementation, with generic `T: Config`, on type `Pallet`, with -/// an optional where clause. -/// -/// ## `#[pallet::weight($expr)]` -/// -/// Each dispatchable needs to define a weight with `#[pallet::weight($expr)]` attribute, the -/// first argument must be `origin: OriginFor`. -/// -/// Also see [`pallet::weight`](`frame_support::pallet_macros::weight`) -/// -/// ### `#[pallet::compact] $some_arg: $some_type` -/// -/// Compact encoding for arguments can be achieved via `#[pallet::compact]`. The function must -/// return a `DispatchResultWithPostInfo` or `DispatchResult`. -/// -/// Also see [`pallet::compact`](`frame_support::pallet_macros::compact`) -/// -/// ## `#[pallet::call_index($idx)]` -/// -/// Each dispatchable may also be annotated with the `#[pallet::call_index($idx)]` attribute, -/// which explicitly defines the codec index for the dispatchable function in the `Call` enum. -/// -/// All call indexes start from 0, until it encounters a dispatchable function with a defined -/// call index. The dispatchable function that lexically follows the function with a defined -/// call index will have that call index, but incremented by 1, e.g. if there are 3 -/// dispatchable functions `fn foo`, `fn bar` and `fn qux` in that order, and only `fn bar` -/// has a call index of 10, then `fn qux` will have an index of 11, instead of 1. -/// -/// **WARNING**: modifying dispatchables, changing their order, removing some, etc., must be -/// done with care. Indeed this will change the outer runtime call type (which is an enum with -/// one variant per pallet), this outer runtime call can be stored on-chain (e.g. in -/// `pallet-scheduler`). Thus migration might be needed. To mitigate against some of this, the -/// `#[pallet::call_index($idx)]` attribute can be used to fix the order of the dispatchable so -/// that the `Call` enum encoding does not change after modification. As a general rule of -/// thumb, it is therefore adventageous to always add new calls to the end so you can maintain -/// the existing order of calls. -/// -/// Also see [`pallet::call_index`](`frame_support::pallet_macros::call_index`) -/// -/// # Extra constants: `#[pallet::extra_constants]` (optional) -/// -/// Allows you to define some extra constants to be added into constant metadata. -/// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::extra_constants] -/// impl Pallet where $optional_where_clause { -/// /// $some_doc -/// $vis fn $fn_name() -> $some_return_type { -/// ... -/// } -/// ... -/// } -/// ``` -/// I.e. a regular rust `impl` block with some optional where clause and functions with 0 args, -/// 0 generics, and some return type. -/// -/// ## Macro expansion -/// -/// The macro add some extra constants to pallet constant metadata. -/// -/// Also see: [`pallet::extra_constants`](`frame_support::pallet_macros::extra_constants`) -/// -/// # Error: `#[pallet::error]` (optional) -/// -/// The `#[pallet::error]` attribute allows you to define an error enum that will be returned -/// from the dispatchable when an error occurs. The information for this error type is then -/// stored in metadata. -/// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::error] -/// pub enum Error { -/// /// $some_optional_doc -/// $SomeFieldLessVariant, -/// /// $some_more_optional_doc -/// $SomeVariantWithOneField(FieldType), -/// ... -/// } -/// ``` -/// I.e. a regular enum named `Error`, with generic `T` and fieldless or multiple-field -/// variants. -/// -/// Any field type in the enum variants must implement [`scale_info::TypeInfo`] in order to be -/// properly used in the metadata, and its encoded size should be as small as possible, -/// preferably 1 byte in size in order to reduce storage size. The error enum itself has an -/// absolute maximum encoded size specified by [`MAX_MODULE_ERROR_ENCODED_SIZE`]. -/// -/// (1 byte can still be 256 different errors. The more specific the error, the easier it is to -/// diagnose problems and give a better experience to the user. Don't skimp on having lots of -/// individual error conditions.) -/// -/// Field types in enum variants must also implement [`PalletError`](traits::PalletError), -/// otherwise the pallet will fail to compile. Rust primitive types have already implemented -/// the [`PalletError`](traits::PalletError) trait along with some commonly used stdlib types -/// such as [`Option`] and -/// [`PhantomData`](`frame_support::__private::sp_std::marker::PhantomData`), and hence in most -/// use cases, a manual implementation is not necessary and is discouraged. -/// -/// The generic `T` must not bound anything and a `where` clause is not allowed. That said, -/// bounds and/or a where clause should not needed for any use-case. -/// -/// Also see: [`pallet::error`](`frame_support::pallet_macros::error`) -/// -/// # Event: `#[pallet::event]` (optional) -/// -/// Allows you to define pallet events. Pallet events are stored under the `system` / `events` -/// key when the block is applied (and then replaced when the next block writes it's events). -/// -/// The Event enum must be defined as follows: -/// -/// ```ignore -/// #[pallet::event] -/// #[pallet::generate_deposit($visibility fn deposit_event)] // Optional -/// pub enum Event<$some_generic> $optional_where_clause { -/// /// Some doc -/// $SomeName($SomeType, $YetanotherType, ...), -/// ... -/// } -/// ``` -/// -/// I.e. an enum (with named or unnamed fields variant), named `Event`, with generic: none or -/// `T` or `T: Config`, and optional w here clause. -/// -/// Each field must implement [`Clone`], [`Eq`], [`PartialEq`], [`Encode`], [`Decode`], and -/// [`Debug`] (on std only). For ease of use, bound by the trait -/// [`Member`](`frame_support::pallet_prelude::Member`), available in -/// frame_support::pallet_prelude. -/// -/// Also see [`pallet::event`](`frame_support::pallet_macros::event`) -/// -/// ## `#[pallet::generate_deposit($visibility fn deposit_event)]` -/// -/// The attribute `#[pallet::generate_deposit($visibility fn deposit_event)]` generates a -/// helper function on `Pallet` that handles deposit events. -/// -/// NOTE: For instantiable pallets, the event must be generic over `T` and `I`. -/// -/// Also see [`pallet::generate_deposit`](`frame_support::pallet_macros::generate_deposit`) -/// -/// # Storage: `#[pallet::storage]` (optional) -/// -/// The `#[pallet::storage]` attribute lets you define some abstract storage inside of runtime -/// storage and also set its metadata. This attribute can be used multiple times. -/// -/// Item should be defined as: -/// -/// ```ignore -/// #[pallet::storage] -/// #[pallet::getter(fn $getter_name)] // optional -/// $vis type $StorageName<$some_generic> $optional_where_clause -/// = $StorageType<$generic_name = $some_generics, $other_name = $some_other, ...>; -/// ``` -/// -/// or with unnamed generic: -/// -/// ```ignore -/// #[pallet::storage] -/// #[pallet::getter(fn $getter_name)] // optional -/// $vis type $StorageName<$some_generic> $optional_where_clause -/// = $StorageType<_, $some_generics, ...>; -/// ``` -/// -/// I.e. it must be a type alias, with generics: `T` or `T: Config`. The aliased type must be -/// one of [`StorageValue`](`pallet_prelude::StorageValue`), -/// [`StorageMap`](`pallet_prelude::StorageMap`) or -/// [`StorageDoubleMap`](`pallet_prelude::StorageDoubleMap`). The generic arguments of the -/// storage type can be given in two manners: named and unnamed. For named generic arguments, -/// the name for each argument should match the name defined for it on the storage struct: -/// * [`StorageValue`](`pallet_prelude::StorageValue`) expects `Value` and optionally -/// `QueryKind` and `OnEmpty`, -/// * [`StorageMap`](`pallet_prelude::StorageMap`) expects `Hasher`, `Key`, `Value` and -/// optionally `QueryKind` and `OnEmpty`, -/// * [`CountedStorageMap`](`pallet_prelude::CountedStorageMap`) expects `Hasher`, `Key`, -/// `Value` and optionally `QueryKind` and `OnEmpty`, -/// * [`StorageDoubleMap`](`pallet_prelude::StorageDoubleMap`) expects `Hasher1`, `Key1`, -/// `Hasher2`, `Key2`, `Value` and optionally `QueryKind` and `OnEmpty`. -/// -/// For unnamed generic arguments: Their first generic must be `_` as it is replaced by the -/// macro and other generic must declared as a normal generic type declaration. -/// -/// The `Prefix` generic written by the macro is generated using -/// `PalletInfo::name::>()` and the name of the storage type. E.g. if runtime names -/// the pallet "MyExample" then the storage `type Foo = ...` should use the prefix: -/// `Twox128(b"MyExample") ++ Twox128(b"Foo")`. -/// -/// For the [`CountedStorageMap`](`pallet_prelude::CountedStorageMap`) variant, the `Prefix` -/// also implements -/// [`CountedStorageMapInstance`](`frame_support::storage::types::CountedStorageMapInstance`). -/// It also associates a [`CounterPrefix`](`pallet_prelude::CounterPrefix'), which is -/// implemented the same as above, but the storage prefix is prepend with `"CounterFor"`. E.g. -/// if runtime names the pallet "MyExample" then the storage `type Foo = -/// CountedStorageaMap<...>` will store its counter at the prefix: `Twox128(b"MyExample") ++ -/// Twox128(b"CounterForFoo")`. -/// -/// E.g: -/// -/// ```ignore -/// #[pallet::storage] -/// pub(super) type MyStorage = StorageMap; -/// ``` -/// -/// In this case the final prefix used by the map is `Twox128(b"MyExample") ++ -/// Twox128(b"OtherName")`. -/// -/// Also see [`pallet::storage`](`frame_support::pallet_macros::storage`) -/// -/// ## `#[pallet::getter(fn $my_getter_fn_name)]` (optional) -/// -/// The optional attribute `#[pallet::getter(fn $my_getter_fn_name)]` allows you to define a -/// getter function on `Pallet`. -/// -/// Also see [`pallet::getter`](`frame_support::pallet_macros::getter`) -/// -/// ## `#[pallet::storage_prefix = "SomeName"]` (optional) -/// -/// The optional attribute `#[pallet::storage_prefix = "SomeName"]` allows you to define the -/// storage prefix to use, see how `Prefix` generic is implemented above. This is helpful if -/// you wish to rename the storage field but don't want to perform a migration. -/// -/// E.g: -/// -/// ```ignore -/// #[pallet::storage] -/// #[pallet::storage_prefix = "foo"] -/// #[pallet::getter(fn my_storage)] -/// pub(super) type MyStorage = StorageMap; -/// ``` -/// -/// or -/// -/// ```ignore -/// #[pallet::storage] -/// #[pallet::getter(fn my_storage)] -/// pub(super) type MyStorage = StorageMap<_, Blake2_128Concat, u32, u32>; -/// ``` -/// -/// Also see [`pallet::storage_prefix`](`frame_support::pallet_macros::storage_prefix`) -/// -/// ## `#[pallet::unbounded]` (optional) -/// -/// The optional attribute `#[pallet::unbounded]` declares the storage as unbounded. When -/// implementating the storage info (when `#[pallet::generate_storage_info]` is specified on -/// the pallet struct placeholder), the size of the storage will be declared as unbounded. This -/// can be useful for storage which can never go into PoV (Proof of Validity). -/// -/// Also see [`pallet::unbounded`](`frame_support::pallet_macros::unbounded`) -/// -/// ## `#[pallet::whitelist_storage]` (optional) -/// -/// The optional attribute `#[pallet::whitelist_storage]` will declare the storage as -/// whitelisted from benchmarking. -/// -/// See -/// [`pallet::whitelist_storage`](frame_support::pallet_macros::whitelist_storage) -/// for more info. -/// -/// ## `#[cfg(..)]` (for storage) -/// The optional attributes `#[cfg(..)]` allow conditional compilation for the storage. -/// -/// E.g: -/// -/// ```ignore -/// #[cfg(feature = "my-feature")] -/// #[pallet::storage] -/// pub(super) type MyStorage = StorageValue; -/// ``` -/// -/// All the `cfg` attributes are automatically copied to the items generated for the storage, -/// i.e. the getter, storage prefix, and the metadata element etc. -/// -/// Any type placed as the `QueryKind` parameter must implement -/// [`frame_support::storage::types::QueryKindTrait`]. There are 3 implementations of this -/// trait by default: -/// -/// 1. [`OptionQuery`](`frame_support::storage::types::OptionQuery`), the default `QueryKind` -/// used when this type parameter is omitted. Specifying this as the `QueryKind` would cause -/// storage map APIs that return a `QueryKind` to instead return an [`Option`], returning -/// `Some` when a value does exist under a specified storage key, and `None` otherwise. -/// 2. [`ValueQuery`](`frame_support::storage::types::ValueQuery`) causes storage map APIs that -/// return a `QueryKind` to instead return the value type. In cases where a value does not -/// exist under a specified storage key, the `OnEmpty` type parameter on `QueryKindTrait` is -/// used to return an appropriate value. -/// 3. [`ResultQuery`](`frame_support::storage::types::ResultQuery`) causes storage map APIs -/// that return a `QueryKind` to instead return a `Result`, with `T` being the value -/// type and `E` being the pallet error type specified by the `#[pallet::error]` attribute. -/// In cases where a value does not exist under a specified storage key, an `Err` with the -/// specified pallet error variant is returned. -/// -/// NOTE: If the `QueryKind` generic parameter is still generic at this stage or is using some -/// type alias then the generation of the getter might fail. In this case the getter can be -/// implemented manually. -/// -/// NOTE: The generic `Hasher` must implement the [`StorageHasher`] trait (or the type is not -/// usable at all). We use [`StorageHasher::METADATA`] for the metadata of the hasher of the -/// storage item. Thus generic hasher is supported. -/// -/// ## Macro expansion -/// -/// For each storage item the macro generates a struct named -/// `_GeneratedPrefixForStorage$NameOfStorage`, and implements -/// [`StorageInstance`](traits::StorageInstance) on it using the pallet and storage name. It -/// then uses it as the first generic of the aliased type. For -/// [`CountedStorageMap`](`pallet_prelude::CountedStorageMap`), -/// [`CountedStorageMapInstance`](`frame_support::storage::types::CountedStorageMapInstance`) -/// is implemented, and another similar struct is generated. -/// -/// For a named generic, the macro will reorder the generics, and remove the names. -/// -/// The macro implements the function `storage_metadata` on the `Pallet` implementing the -/// metadata for all storage items based on their kind: -/// * for a storage value, the type of the value is copied into the metadata -/// * for a storage map, the type of the values and the key's type is copied into the metadata -/// * for a storage double map, the type of the values, and the types of `key1` and `key2` are -/// copied into the metadata. -/// -/// # Type value: `#[pallet::type_value]` (optional) -/// -/// The `#[pallet::type_value]` attribute lets you define a struct implementing the -/// [`Get`](crate::traits::Get) trait to ease use of storage types. This attribute is meant to -/// be used alongside [`#[pallet::storage]`](#storage-palletstorage-optional) to define a -/// storage's default value. This attribute can be used multiple times. -/// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::type_value] -/// fn $MyDefaultName<$some_generic>() -> $default_type $optional_where_clause { $expr } -/// ``` -/// -/// I.e.: a function definition with generics none or `T: Config` and a returned type. -/// -/// E.g.: -/// -/// ```ignore -/// #[pallet::type_value] -/// fn MyDefault() -> T::Balance { 3.into() } -/// ``` -/// -/// Also see [`pallet::type_value`](`frame_support::pallet_macros::type_value`) -/// -/// # Genesis config: `#[pallet::genesis_config]` (optional) +/// [`StorageInfoTrait`](frame_support::traits::StorageInfoTrait) for each storage in the +/// implementation of [`StorageInfoTrait`](frame_support::traits::StorageInfoTrait) for the +/// pallet. Otherwise it implements +/// [`StorageInfoTrait`](frame_support::traits::StorageInfoTrait) for the pallet using the +/// [`PartialStorageInfoTrait`](frame_support::traits::PartialStorageInfoTrait) +/// implementation of storages. /// -/// The `#[pallet::genesis_config]` attribute allows you to define the genesis configuration -/// for the pallet. +/// ## Dev Mode (`#[pallet(dev_mode)]`) /// -/// Item is defined as either an enum or a struct. It needs to be public and implement the -/// trait [`BuildGenesisConfig`](`traits::BuildGenesisConfig`) with -/// [`#[pallet::genesis_build]`](#genesis-build-palletgenesis_build-optional). The type -/// generics are constrained to be either none, or `T` or `T: Config`. +/// Specifying the argument `dev_mode` will allow you to enable dev mode for a pallet. The +/// aim of dev mode is to loosen some of the restrictions and requirements placed on +/// production pallets for easy tinkering and development. Dev mode pallets should not be +/// used in production. Enabling dev mode has the following effects: /// -/// E.g: -/// -/// ```ignore -/// #[pallet::genesis_config] -/// pub struct GenesisConfig { -/// _myfield: BalanceOf, -/// } -/// ``` -/// -/// Also see [`pallet::genesis_config`](`frame_support::pallet_macros::genesis_config`) -/// -/// # Genesis build: `#[pallet::genesis_build]` (optional) -/// -/// The `#[pallet::genesis_build]` attribute allows you to define how `genesis_configuration` -/// is built. This takes as input the `GenesisConfig` type (as `self`) and constructs the -/// pallet's initial state. -/// -/// The impl must be defined as: -/// -/// ```ignore -/// #[pallet::genesis_build] -/// impl GenesisBuild for GenesisConfig<$maybe_generics> { -/// fn build(&self) { $expr } -/// } -/// ``` -/// -/// I.e. a trait implementation with generic `T: Config`, of trait `GenesisBuild` on -/// type `GenesisConfig` with generics none or `T`. -/// -/// E.g.: -/// -/// ```ignore -/// #[pallet::genesis_build] -/// impl GenesisBuild for GenesisConfig { -/// fn build(&self) {} -/// } -/// ``` -/// -/// Also see [`pallet::genesis_build`](`frame_support::pallet_macros::genesis_build`) -/// -/// # Inherent: `#[pallet::inherent]` (optional) -/// -/// The `#[pallet::inherent]` attribute allows the pallet to provide some -/// [inherent](https://docs.substrate.io/fundamentals/transaction-types/#inherent-transactions). -/// An inherent is some piece of data that is inserted by a block authoring node at block -/// creation time and can either be accepted or rejected by validators based on whether the -/// data falls within an acceptable range. -/// -/// The most common inherent is the `timestamp` that is inserted into every block. Since there -/// is no way to validate timestamps, validators simply check that the timestamp reported by -/// the block authoring node falls within an acceptable range. -/// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::inherent] -/// impl ProvideInherent for Pallet { -/// // ... regular trait implementation -/// } -/// ``` -/// -/// I.e. a trait implementation with bound `T: Config`, of trait -/// [`ProvideInherent`](`pallet_prelude::ProvideInherent`) for type `Pallet`, and some -/// optional where clause. -/// -/// Also see [`pallet::inherent`](`frame_support::pallet_macros::inherent`) -/// -/// # Validate unsigned: `#[pallet::validate_unsigned]` (optional) -/// -/// The `#[pallet::validate_unsigned]` attribute allows the pallet to validate some unsigned -/// transaction: -/// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::validate_unsigned] -/// impl ValidateUnsigned for Pallet { -/// // ... regular trait implementation -/// } -/// ``` -/// -/// I.e. a trait implementation with bound `T: Config`, of trait -/// [`ValidateUnsigned`](`pallet_prelude::ValidateUnsigned`) for type `Pallet`, and some -/// optional where clause. -/// -/// NOTE: There is also the [`sp_runtime::traits::SignedExtension`] trait that can be used to -/// add some specific logic for transaction validation. -/// -/// Also see [`pallet::validate_unsigned`](`frame_support::pallet_macros::validate_unsigned`) -/// -/// # Origin: `#[pallet::origin]` (optional) -/// -/// The `#[pallet::origin]` attribute allows you to define some origin for the pallet. -/// -/// Item must be either a type alias, an enum, or a struct. It needs to be public. -/// -/// E.g.: -/// -/// ```ignore -/// #[pallet::origin] -/// pub struct Origin(PhantomData<(T)>); -/// ``` -/// -/// **WARNING**: modifying origin changes the outer runtime origin. This outer runtime origin -/// can be stored on-chain (e.g. in `pallet-scheduler`), thus any change must be done with care -/// as it might require some migration. -/// -/// NOTE: for instantiable pallets, the origin must be generic over `T` and `I`. -/// -/// Also see [`pallet::origin`](`frame_support::pallet_macros::origin`) -/// -/// # Composite enum `#[pallet::composite_enum]` (optional) -/// -/// The `#[pallet::composite_enum]` attribute allows you to define an enum on the pallet which -/// will then instruct `construct_runtime` to amalgamate all similarly-named enums from other -/// pallets into an aggregate enum. This is similar in principle with how the aggregate enum is -/// generated for `#[pallet::event]` or `#[pallet::error]`. -/// -/// The item tagged with `#[pallet::composite_enum]` MUST be an enum declaration, and can ONLY -/// be the following identifiers: `FreezeReason`, `HoldReason`, `LockId` or `SlashReason`. -/// Custom identifiers are not supported. -/// -/// NOTE: For ease of usage, when no `#[derive]` attributes are detected, the -/// `#[pallet::composite_enum]` attribute will automatically derive the following traits for -/// the enum: -/// -/// ```ignore -/// Copy, Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebug -/// ``` -/// -/// The inverse is also true: if there are any #[derive] attributes present for the enum, then -/// the attribute will not automatically derive any of the traits described above. -/// -/// # General notes on instantiable pallets -/// -/// An instantiable pallet is one where Config is generic, i.e. `Config`. This allows -/// runtime to implement multiple instances of the pallet, by using different types for the -/// generic. This is the sole purpose of the generic `I`, but because -/// [`PalletInfo`](`traits::PalletInfo`) requires the `Pallet` placeholder to be static, it is -/// important to bound by `'static` whenever [`PalletInfo`](`traits::PalletInfo`) can be used. -/// Additionally, in order to make an instantiable pallet usable as a regular pallet without an -/// instance, it is important to bound by `= ()` on every type. -/// -/// Thus impl bound looks like `impl, I: 'static>`, and types look like -/// `SomeType` or `SomeType, I: 'static = ()>`. -/// -/// # Example of a non-instantiable pallet -/// -/// ``` -/// pub use pallet::*; // reexport in crate namespace for `construct_runtime!` -/// -/// #[frame_support::pallet] -/// // NOTE: The name of the pallet is provided by `construct_runtime` and is used as -/// // the unique identifier for the pallet's storage. It is not defined in the pallet itself. -/// pub mod pallet { -/// use frame_support::pallet_prelude::*; // Import various types used in the pallet definition -/// use frame_system::pallet_prelude::*; // Import some system helper types. -/// -/// type BalanceOf = ::Balance; -/// -/// // Define the generic parameter of the pallet -/// // The macro parses `#[pallet::constant]` attributes and uses them to generate metadata -/// // for the pallet's constants. -/// #[pallet::config] -/// pub trait Config: frame_system::Config { -/// #[pallet::constant] // put the constant in metadata -/// type MyGetParam: Get; -/// type Balance: Parameter + MaxEncodedLen + From; -/// type RuntimeEvent: From> + IsType<::RuntimeEvent>; -/// } -/// -/// // Define some additional constant to put into the constant metadata. -/// #[pallet::extra_constants] -/// impl Pallet { -/// /// Some description -/// fn exra_constant_name() -> u128 { 4u128 } -/// } -/// -/// // Define the pallet struct placeholder, various pallet function are implemented on it. -/// #[pallet::pallet] -/// #[pallet::generate_store(pub(super) trait Store)] -/// pub struct Pallet(_); -/// -/// // Implement the pallet hooks. -/// #[pallet::hooks] -/// impl Hooks> for Pallet { -/// fn on_initialize(_n: BlockNumberFor) -> Weight { -/// unimplemented!(); -/// } -/// -/// // can implement also: on_finalize, on_runtime_upgrade, offchain_worker, ... -/// // see `Hooks` trait -/// } -/// -/// // Declare Call struct and implement dispatchables. -/// // -/// // WARNING: Each parameter used in functions must implement: Clone, Debug, Eq, PartialEq, -/// // Codec. -/// // -/// // The macro parses `#[pallet::compact]` attributes on function arguments and implements -/// // the `Call` encoding/decoding accordingly. -/// #[pallet::call] -/// impl Pallet { -/// /// Doc comment put in metadata -/// #[pallet::weight(0)] // Defines weight for call (function parameters are in scope) -/// pub fn toto( -/// origin: OriginFor, -/// #[pallet::compact] _foo: u32, -/// ) -> DispatchResultWithPostInfo { -/// let _ = origin; -/// unimplemented!(); -/// } -/// } -/// -/// // Declare the pallet `Error` enum (this is optional). -/// // The macro generates error metadata using the doc comment on each variant. -/// #[pallet::error] -/// pub enum Error { -/// /// doc comment put into metadata -/// InsufficientProposersBalance, -/// } -/// -/// // Declare pallet Event enum (this is optional). -/// // -/// // WARNING: Each type used in variants must implement: Clone, Debug, Eq, PartialEq, Codec. -/// // -/// // The macro generates event metadata, and derive Clone, Debug, Eq, PartialEq and Codec -/// #[pallet::event] -/// // Generate a funciton on Pallet to deposit an event. -/// #[pallet::generate_deposit(pub(super) fn deposit_event)] -/// pub enum Event { -/// /// doc comment put in metadata -/// // `::AccountId` is not defined in metadata list, the last -/// // Thus the metadata is `::AccountId`. -/// Proposed(::AccountId), -/// /// doc -/// // here metadata will be `Balance` as define in metadata list -/// Spending(BalanceOf), -/// // here metadata will be `Other` as define in metadata list -/// Something(u32), -/// } -/// -/// // Define a struct which implements `frame_support::traits::Get` (optional). -/// #[pallet::type_value] -/// pub(super) fn MyDefault() -> T::Balance { 3.into() } -/// -/// // Declare a storage item. Any amount of storage items can be declared (optional). -/// // -/// // Is expected either `StorageValue`, `StorageMap` or `StorageDoubleMap`. -/// // The macro generates the prefix type and replaces the first generic `_`. -/// // -/// // The macro expands the metadata for the storage item with the type used: -/// // * for a storage value the type of the value is copied into the metadata -/// // * for a storage map the type of the values and the type of the key is copied into the metadata -/// // * for a storage double map the types of the values and keys are copied into the -/// // metadata. -/// // -/// // NOTE: The generic `Hasher` must implement the `StorageHasher` trait (or the type is not -/// // usable at all). We use [`StorageHasher::METADATA`] for the metadata of the hasher of the -/// // storage item. Thus generic hasher is supported. -/// #[pallet::storage] -/// pub(super) type MyStorageValue = -/// StorageValue>; -/// -/// // Another storage declaration -/// #[pallet::storage] -/// #[pallet::getter(fn my_storage)] -/// #[pallet::storage_prefix = "SomeOtherName"] -/// pub(super) type MyStorage = -/// StorageMap; -/// -/// // Declare the genesis config (optional). -/// // -/// // The macro accepts either a struct or an enum; it checks that generics are consistent. -/// // -/// // Type must implement the `Default` trait. -/// #[pallet::genesis_config] -/// #[derive(frame_support::DefaultNoBound)] -/// pub struct GenesisConfig { -/// _config: sp_std::marker::PhantomData, -/// _myfield: u32, -/// } -/// -/// // Declare genesis builder. (This is need only if GenesisConfig is declared) -/// #[pallet::genesis_build] -/// impl BuildGenesisConfig for GenesisConfig { -/// fn build(&self) {} -/// } -/// -/// // Declare a pallet origin (this is optional). -/// // -/// // The macro accept type alias or struct or enum, it checks generics are consistent. -/// #[pallet::origin] -/// pub struct Origin(PhantomData); -/// -/// // Declare a hold reason (this is optional). -/// // -/// // Creates a hold reason for this pallet that is aggregated by `construct_runtime`. -/// // A similar enum can be defined for `FreezeReason`, `LockId` or `SlashReason`. -/// #[pallet::composite_enum] -/// pub enum HoldReason { -/// SomeHoldReason -/// } -/// -/// // Declare validate_unsigned implementation (this is optional). -/// #[pallet::validate_unsigned] -/// impl ValidateUnsigned for Pallet { -/// type Call = Call; -/// fn validate_unsigned( -/// source: TransactionSource, -/// call: &Self::Call -/// ) -> TransactionValidity { -/// Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) -/// } -/// } -/// -/// // Declare inherent provider for pallet (this is optional). -/// #[pallet::inherent] -/// impl ProvideInherent for Pallet { -/// type Call = Call; -/// type Error = InherentError; -/// -/// const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; -/// -/// fn create_inherent(_data: &InherentData) -> Option { -/// unimplemented!(); -/// } -/// -/// fn is_inherent(_call: &Self::Call) -> bool { -/// unimplemented!(); -/// } -/// } -/// -/// // Regular rust code needed for implementing ProvideInherent trait -/// -/// #[derive(codec::Encode, sp_runtime::RuntimeDebug)] -/// #[cfg_attr(feature = "std", derive(codec::Decode))] -/// pub enum InherentError { -/// } -/// -/// impl sp_inherents::IsFatalError for InherentError { -/// fn is_fatal_error(&self) -> bool { -/// unimplemented!(); -/// } -/// } -/// -/// pub const INHERENT_IDENTIFIER: sp_inherents::InherentIdentifier = *b"testpall"; -/// } -/// ``` -/// -/// # Example of an instantiable pallet -/// -/// ``` -/// pub use pallet::*; -/// -/// #[frame_support::pallet] -/// pub mod pallet { -/// use frame_support::pallet_prelude::*; -/// use frame_system::pallet_prelude::*; -/// -/// type BalanceOf = >::Balance; +/// * Weights no longer need to be specified on every `#[pallet::call]` declaration. By +/// default, dev mode pallets will assume a weight of zero (`0`) if a weight is not +/// specified. This is equivalent to specifying `#[weight(0)]` on all calls that do not +/// specify a weight. +/// * Call indices no longer need to be specified on every `#[pallet::call]` declaration. By +/// default, dev mode pallets will assume a call index based on the order of the call. +/// * All storages are marked as unbounded, meaning you do not need to implement +/// [`MaxEncodedLen`](frame_support::pallet_prelude::MaxEncodedLen) on storage types. This is +/// equivalent to specifying `#[pallet::unbounded]` on all storage type definitions. +/// * Storage hashers no longer need to be specified and can be replaced by `_`. In dev mode, +/// these will be replaced by `Blake2_128Concat`. In case of explicit key-binding, `Hasher` +/// can simply be ignored when in `dev_mode`. /// -/// #[pallet::config] -/// pub trait Config: frame_system::Config { -/// #[pallet::constant] -/// type MyGetParam: Get; -/// type Balance: Parameter + MaxEncodedLen + From; -/// type RuntimeEvent: From> + IsType<::RuntimeEvent>; -/// } -/// -/// #[pallet::extra_constants] -/// impl, I: 'static> Pallet { -/// /// Some description -/// fn extra_constant_name() -> u128 { 4u128 } -/// } -/// -/// #[pallet::pallet] -/// #[pallet::generate_store(pub(super) trait Store)] -/// pub struct Pallet(PhantomData<(T, I)>); -/// -/// #[pallet::hooks] -/// impl, I: 'static> Hooks> for Pallet { -/// } -/// -/// #[pallet::call] -/// impl, I: 'static> Pallet { -/// /// Doc comment put in metadata -/// #[pallet::weight(0)] -/// pub fn toto(origin: OriginFor, #[pallet::compact] _foo: u32) -> DispatchResultWithPostInfo { -/// let _ = origin; -/// unimplemented!(); -/// } -/// } -/// -/// #[pallet::error] -/// pub enum Error { -/// /// doc comment put into metadata -/// InsufficientProposersBalance, -/// } -/// -/// #[pallet::event] -/// #[pallet::generate_deposit(pub(super) fn deposit_event)] -/// pub enum Event, I: 'static = ()> { -/// /// doc comment put in metadata -/// Proposed(::AccountId), -/// /// doc -/// Spending(BalanceOf), -/// Something(u32), -/// } -/// -/// #[pallet::type_value] -/// pub(super) fn MyDefault, I: 'static>() -> T::Balance { 3.into() } -/// -/// #[pallet::storage] -/// pub(super) type MyStorageValue, I: 'static = ()> = -/// StorageValue>; -/// -/// #[pallet::storage] -/// #[pallet::getter(fn my_storage)] -/// #[pallet::storage_prefix = "SomeOtherName"] -/// pub(super) type MyStorage = -/// StorageMap; -/// -/// #[pallet::genesis_config] -/// #[derive(frame_support::DefaultNoBound)] -/// pub struct GenesisConfig, I: 'static = ()> { -/// _config: sp_std::marker::PhantomData<(T,I)>, -/// _myfield: u32, -/// } -/// -/// #[pallet::genesis_build] -/// impl, I: 'static> BuildGenesisConfig for GenesisConfig { -/// fn build(&self) {} -/// } -/// -/// #[pallet::origin] -/// pub struct Origin(PhantomData<(T, I)>); -/// -/// #[pallet::composite_enum] -/// pub enum HoldReason { -/// SomeHoldReason -/// } -/// -/// #[pallet::validate_unsigned] -/// impl, I: 'static> ValidateUnsigned for Pallet { -/// type Call = Call; -/// fn validate_unsigned( -/// source: TransactionSource, -/// call: &Self::Call -/// ) -> TransactionValidity { -/// Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) -/// } -/// } -/// -/// #[pallet::inherent] -/// impl, I: 'static> ProvideInherent for Pallet { -/// type Call = Call; -/// type Error = InherentError; -/// -/// const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; -/// -/// fn create_inherent(_data: &InherentData) -> Option { -/// unimplemented!(); -/// } -/// -/// fn is_inherent(_call: &Self::Call) -> bool { -/// unimplemented!(); -/// } -/// } -/// -/// // Regular rust code needed for implementing ProvideInherent trait -/// -/// #[derive(codec::Encode, sp_runtime::RuntimeDebug)] -/// #[cfg_attr(feature = "std", derive(codec::Decode))] -/// pub enum InherentError { -/// } -/// -/// impl sp_inherents::IsFatalError for InherentError { -/// fn is_fatal_error(&self) -> bool { -/// unimplemented!(); -/// } -/// } -/// -/// pub const INHERENT_IDENTIFIER: sp_inherents::InherentIdentifier = *b"testpall"; -/// } -/// ``` +/// Note that the `dev_mode` argument can only be supplied to the `#[pallet]` or +/// `#[frame_support::pallet]` attribute macro that encloses your pallet module. This +/// argument cannot be specified anywhere else, including but not limited to the +/// `#[pallet::pallet]` attribute macro. /// -/// # Upgrade guidelines -/// -/// 1. Export the metadata of the pallet for later checks -/// - run your node with the pallet active -/// - query the metadata using the `state_getMetadata` RPC and curl, or use `subsee -p -/// > meta.json` -/// 2. Generate the template upgrade for the pallet provided by `decl_storage` with the -/// environment variable `PRINT_PALLET_UPGRADE`: `PRINT_PALLET_UPGRADE=1 cargo check -p -/// my_pallet`. This template can be used as it contains all information for storages, -/// genesis config and genesis build. -/// 3. Reorganize the pallet to have the trait `Config`, `decl_*` macros, -/// [`ValidateUnsigned`](`pallet_prelude::ValidateUnsigned`), -/// [`ProvideInherent`](`pallet_prelude::ProvideInherent`), and Origin` all together in one -/// file. Suggested order: -/// * `Config`, -/// * `decl_module`, -/// * `decl_event`, -/// * `decl_error`, -/// * `decl_storage`, -/// * `origin`, -/// * `validate_unsigned`, -/// * `provide_inherent`, so far it should compile and all be correct. -/// 4. start writing the new pallet module -/// ```ignore -/// pub use pallet::*; -/// -/// #[frame_support::pallet] -/// pub mod pallet { -/// use frame_support::pallet_prelude::*; -/// use frame_system::pallet_prelude::*; -/// use super::*; -/// -/// #[pallet::pallet] -/// #[pallet::generate_store($visibility_of_trait_store trait Store)] -/// // NOTE: if the visibility of trait store is private but you want to make it available -/// // in super, then use `pub(super)` or `pub(crate)` to make it available in crate. -/// pub struct Pallet(_); -/// // pub struct Pallet(PhantomData); // for instantiable pallet -/// } -/// ``` -/// 5. **migrate Config**: move trait into the module with -/// * all const in `decl_module` to [`#[pallet::constant]`](#palletconstant) -/// * add the bound `IsType<::RuntimeEvent>` to `type -/// RuntimeEvent` -/// 7. **migrate decl_module**: write: -/// ```ignore -/// #[pallet::hooks] -/// impl Hooks for Pallet { -/// } -/// ``` -/// and write inside `on_initialize`, `on_finalize`, `on_runtime_upgrade`, -/// `offchain_worker`, and `integrity_test`. -/// -/// then write: -/// ```ignore -/// #[pallet::call] -/// impl Pallet { -/// } -/// ``` -/// and write inside all the calls in `decl_module` with a few changes in the signature: -/// - origin must now be written completely, e.g. `origin: OriginFor` -/// - result type must be `DispatchResultWithPostInfo`, you need to write it and also you -/// might need to put `Ok(().into())` at the end or the function. -/// - `#[compact]` must now be written -/// [`#[pallet::compact]`](#palletcompact-some_arg-some_type) -/// - `#[weight = ..]` must now be written [`#[pallet::weight(..)]`](#palletweightexpr) -/// -/// 7. **migrate event**: rewrite as a simple enum with the attribute -/// [`#[pallet::event]`](#event-palletevent-optional), use [`#[pallet::generate_deposit($vis -/// fn deposit_event)]`](#event-palletevent-optional) to generate `deposit_event`, -/// 8. **migrate error**: rewrite it with attribute -/// [`#[pallet::error]`](#error-palleterror-optional). -/// 9. **migrate storage**: `decl_storage` provide an upgrade template (see 3.). All storages, -/// genesis config, genesis build and default implementation of genesis config can be taken -/// from it directly. -/// -/// Otherwise here is the manual process: -/// -/// first migrate the genesis logic. write: -/// ```ignore -/// #[pallet::genesis_config] -/// struct GenesisConfig { -/// // fields of add_extra_genesis -/// } -/// impl Default for GenesisConfig { -/// // type default or default provided for fields -/// } -/// #[pallet::genesis_build] -/// impl GenesisBuild for GenesisConfig { -/// // for instantiable pallet: -/// // `impl GenesisBuild for GenesisConfig { -/// fn build() { -/// // The add_extra_genesis build logic -/// } -/// } -/// ``` -/// for each storage, if it contains `config(..)` then add fields, and make it default to -/// the value in `= ..;` or the type default if none, if it contains no build then also add -/// the logic to build the value. for each storage if it contains `build(..)` then add the -/// logic to `genesis_build`. -/// -/// NOTE: within `decl_storage`: the individual config is executed first, followed by the -/// build and finally the `add_extra_genesis` build. -/// -/// Once this is done you can migrate storages individually, a few notes: -/// - for private storage use `pub(crate) type ` or `pub(super) type` or nothing, -/// - for storages with `get(fn ..)` use [`#[pallet::getter(fn -/// ...)]`](#palletgetterfn-my_getter_fn_name-optional) -/// - for storages with value being `Option<$something>` make generic `Value` being -/// `$something` and generic `QueryKind` being `OptionQuery` (note: this is default). -/// Otherwise make `Value` the complete value type and `QueryKind` being `ValueQuery`. -/// - for storages with default value: `= $expr;` provide some specific `OnEmpty` generic. -/// To do so use of `#[pallet::type_value]` to generate the wanted struct to put. -/// example: `MyStorage: u32 = 3u32` would be written: -/// -/// ```ignore -/// #[pallet::type_value] fn MyStorageOnEmpty() -> u32 { 3u32 } -/// #[pallet::storage] -/// pub(super) type MyStorage = StorageValue<_, u32, ValueQuery, MyStorageOnEmpty>; -/// ``` -/// -/// NOTE: `decl_storage` also generates the functions `assimilate_storage` and -/// `build_storage` directly on `GenesisConfig`, and these are sometimes used in tests. -/// In order not to break they can be implemented manually, one can implement those -/// functions by calling the `GenesisBuild` implementation. -/// 10. **migrate origin**: move the origin to the pallet module to be under a -/// [`#[pallet::origin]`](#origin-palletorigin-optional) attribute -/// 11. **migrate validate_unsigned**: move the -/// [`ValidateUnsigned`](`pallet_prelude::ValidateUnsigned`) implementation to the pallet -/// module under a -/// [`#[pallet::validate_unsigned]`](#validate-unsigned-palletvalidate_unsigned-optional) -/// attribute -/// 12. **migrate provide_inherent**: move the -/// [`ProvideInherent`](`pallet_prelude::ProvideInherent`) implementation to the pallet -/// module under a [`#[pallet::inherent]`](#inherent-palletinherent-optional) attribute -/// 13. rename the usage of `Module` to `Pallet` inside the crate. -/// 14. migration is done, now double check the migration with the checking migration -/// guidelines shown below. -/// -/// # Checking upgrade guidelines: -/// -/// * compare metadata. Use [subsee](https://github.com/ascjones/subsee) to fetch the metadata -/// and do a diff of the resulting json before and after migration. This checks for: -/// * call, names, signature, docs -/// * event names, docs -/// * error names, docs -/// * storage names, hasher, prefixes, default value -/// * error, error, constant -/// * manually check that: -/// * `Origin` was moved inside the macro under -/// [`#[pallet::origin]`](#origin-palletorigin-optional) if it exists -/// * [`ValidateUnsigned`](`pallet_prelude::ValidateUnsigned`) was moved inside the macro -/// under -/// [`#[pallet::validate_unsigned)]`](#validate-unsigned-palletvalidate_unsigned-optional) -/// if it exists -/// * [`ProvideInherent`](`pallet_prelude::ProvideInherent`) was moved inside the macro -/// under [`#[pallet::inherent)]`](#inherent-palletinherent-optional) if it exists -/// * `on_initialize` / `on_finalize` / `on_runtime_upgrade` / `offchain_worker` were moved -/// to the `Hooks` implementation -/// * storages with `config(..)` were converted to `GenesisConfig` field, and their default -/// is `= $expr;` if the storage has a default value -/// * storages with `build($expr)` or `config(..)` were built in `GenesisBuild::build` -/// * `add_extra_genesis` fields were converted to `GenesisConfig` field with their correct -/// default if specified -/// * `add_extra_genesis` build was written into `GenesisBuild::build` -/// * storage items defined with [`pallet`] use the name of the pallet provided by -/// [`traits::PalletInfo::name`] as `pallet_prefix` (in `decl_storage`, storage items used -/// the `pallet_prefix` given as input of `decl_storage` with the syntax `as Example`). Thus -/// a runtime using the pallet must be careful with this change. To handle this change: -/// * either ensure that the name of the pallet given to `construct_runtime!` is the same -/// as the name the pallet was giving to `decl_storage`, -/// * or do a storage migration from the old prefix used to the new prefix used. -/// -/// NOTE: The prefixes used by storage items are in metadata. Thus, ensuring the metadata -/// hasn't changed ensures that the `pallet_prefix`s used by the storage items haven't changed. -/// -/// # Notes when macro fails to show proper error message spans: -/// -/// Rustc loses span for some macro input. Some tips to fix it: -/// * do not use inner attribute: -/// ```ignore -/// #[pallet] -/// pub mod pallet { -/// //! This inner attribute will make span fail -/// .. -/// } -/// ``` -/// * use the newest nightly possible. +///
+/// WARNING:
+/// You should not deploy or use dev mode pallets in production. Doing so can break your
+/// chain and therefore should never be done. Once you are done tinkering, you should
+/// remove the 'dev_mode' argument from your #[pallet] declaration and fix any compile
+/// errors before attempting to use your pallet in a production scenario.
+/// 
pub use frame_support_procedural::pallet; -/// Contains macro stubs for all of the pallet:: macros -pub mod pallet_macros { - pub use frame_support_procedural::{ - composite_enum, config, disable_frame_system_supertrait_check, error, event, - extra_constants, feeless_if, generate_deposit, generate_store, getter, hooks, - import_section, inherent, no_default, no_default_bounds, origin, pallet_section, - storage_prefix, storage_version, type_value, unbounded, validate_unsigned, weight, - whitelist_storage, - }; +/// Contains macro stubs for all of the `pallet::` macros +pub mod pallet_macros { + /// Declare the storage as whitelisted from benchmarking. + /// + /// Doing so will exclude reads of that value's storage key from counting towards weight + /// calculations during benchmarking. + /// + /// This attribute should only be attached to storages that are known to be + /// read/used in every block. This will result in a more accurate benchmarking weight. + /// + /// ### Example + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::storage] + /// #[pallet::whitelist_storage] + /// pub type MyStorage = StorageValue<_, u32>; + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + pub use frame_support_procedural::whitelist_storage; + + /// Allows specifying the weight of a call. + /// + /// Each dispatchable needs to define a weight with the `#[pallet::weight($expr)]` + /// attribute. The first argument must be `origin: OriginFor`. + /// + /// ## Example + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # use frame_system::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::call] + /// impl Pallet { + /// #[pallet::weight({0})] // <- set actual weight here + /// #[pallet::call_index(0)] + /// pub fn something( + /// _: OriginFor, + /// foo: u32, + /// ) -> DispatchResult { + /// unimplemented!() + /// } + /// } + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + pub use frame_support_procedural::weight; + + /// Allows whitelisting a storage item from decoding during try-runtime checks. + /// + /// The optional attribute `#[pallet::disable_try_decode_storage]` will declare the + /// storage as whitelisted from decoding during try-runtime checks. This should only be + /// attached to transient storage which cannot be migrated during runtime upgrades. + /// + /// ### Example + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::storage] + /// #[pallet::disable_try_decode_storage] + /// pub type MyStorage = StorageValue<_, u32>; + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + pub use frame_support_procedural::disable_try_decode_storage; + + /// Declares a storage as unbounded in potential size. + /// + /// When implementing the storage info (when `#[pallet::generate_storage_info]` is + /// specified on the pallet struct placeholder), the size of the storage will be declared + /// as unbounded. This can be useful for storage which can never go into PoV (Proof of + /// Validity). + /// + /// ## Example + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::storage] + /// #[pallet::unbounded] + /// pub type MyStorage = StorageValue<_, u32>; + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + pub use frame_support_procedural::unbounded; + + /// Defines what storage prefix to use for a storage item when building the trie. + /// + /// This is helpful if you wish to rename the storage field but don't want to perform a + /// migration. + /// + /// ## Example + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::storage] + /// #[pallet::storage_prefix = "foo"] + /// pub type MyStorage = StorageValue<_, u32>; + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + pub use frame_support_procedural::storage_prefix; + + /// Ensures the generated `DefaultConfig` will not have any bounds for + /// that trait item. + /// + /// Attaching this attribute to a trait item ensures that the generated trait + /// `DefaultConfig` will not have any bounds for this trait item. + /// + /// As an example, if you have a trait item `type AccountId: SomeTrait;` in your `Config` + /// trait, the generated `DefaultConfig` will only have `type AccountId;` with no trait + /// bound. + pub use frame_support_procedural::no_default_bounds; + + /// Ensures the trait item will not be used as a default with the + /// `#[derive_impl(..)]` attribute macro. + /// + /// The optional attribute `#[pallet::no_default]` can be attached to trait items within a + /// `Config` trait impl that has [`#[pallet::config(with_default)]`](`config`) + /// attached. + pub use frame_support_procedural::no_default; + + /// Declares a module as importable into a pallet via + /// [`#[import_section]`](`import_section`). + /// + /// Note that sections are imported by their module name/ident, and should be referred to + /// by their _full path_ from the perspective of the target pallet. Do not attempt to make + /// use of `use` statements to bring pallet sections into scope, as this will not work + /// (unless you do so as part of a wildcard import, in which case it will work). + /// + /// ## Naming Logistics + /// + /// Also note that because of how `#[pallet_section]` works, pallet section names must be + /// globally unique _within the crate in which they are defined_. For more information on + /// why this must be the case, see macro_magic's + /// [`#[export_tokens]`](https://docs.rs/macro_magic/latest/macro_magic/attr.export_tokens.html) macro. + /// + /// Optionally, you may provide an argument to `#[pallet_section]` such as + /// `#[pallet_section(some_ident)]`, in the event that there is another pallet section in + /// same crate with the same ident/name. The ident you specify can then be used instead of + /// the module's ident name when you go to import it via + /// [`#[import_section]`](`import_section`). + pub use frame_support_procedural::pallet_section; + + /// The `#[pallet::inherent]` attribute allows the pallet to provide + /// [inherents](https://docs.substrate.io/fundamentals/transaction-types/#inherent-transactions). + /// + /// An inherent is some piece of data that is inserted by a block authoring node at block + /// creation time and can either be accepted or rejected by validators based on whether the + /// data falls within an acceptable range. + /// + /// The most common inherent is the `timestamp` that is inserted into every block. Since + /// there is no way to validate timestamps, validators simply check that the timestamp + /// reported by the block authoring node falls within an acceptable range. + /// + /// Example usage: + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # use frame_support::inherent::IsFatalError; + /// # use sp_timestamp::InherentError; + /// # use sp_std::result; + /// # + /// // Example inherent identifier + /// pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"timstap0"; + /// + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::inherent] + /// impl ProvideInherent for Pallet { + /// type Call = Call; + /// type Error = InherentError; + /// const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; + /// + /// fn create_inherent(data: &InherentData) -> Option { + /// unimplemented!() + /// } + /// + /// fn check_inherent( + /// call: &Self::Call, + /// data: &InherentData, + /// ) -> result::Result<(), Self::Error> { + /// unimplemented!() + /// } + /// + /// fn is_inherent(call: &Self::Call) -> bool { + /// unimplemented!() + /// } + /// } + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + /// + /// I.e. a trait implementation with bound `T: Config`, of trait `ProvideInherent` for type + /// `Pallet`, and some optional where clause. + /// + /// ## Macro expansion + /// + /// The macro currently makes no use of this information, but it might use this information + /// in the future to give information directly to `construct_runtime`. + pub use frame_support_procedural::inherent; + + /// Splits a pallet declaration into multiple parts. + /// + /// An attribute macro that can be attached to a module declaration. Doing so will + /// import the contents of the specified external pallet section that is defined + /// elsewhere using [`#[pallet_section]`](`pallet_section`). + /// + /// ## Example + /// ``` + /// # use frame_support::pallet_macros::pallet_section; + /// # use frame_support::pallet_macros::import_section; + /// # + /// /// A [`pallet_section`] that defines the events for a pallet. + /// /// This can later be imported into the pallet using [`import_section`]. + /// #[pallet_section] + /// mod events { + /// #[pallet::event] + /// #[pallet::generate_deposit(pub(super) fn deposit_event)] + /// pub enum Event { + /// /// Event documentation should end with an array that provides descriptive names for event + /// /// parameters. [something, who] + /// SomethingStored { something: u32, who: T::AccountId }, + /// } + /// } + /// + /// #[import_section(events)] + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config { + /// # type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// # } + /// } + /// ``` + /// + /// This will result in the contents of `some_section` being _verbatim_ imported into + /// the pallet above. Note that since the tokens for `some_section` are essentially + /// copy-pasted into the target pallet, you cannot refer to imports that don't also + /// exist in the target pallet, but this is easily resolved by including all relevant + /// `use` statements within your pallet section, so they are imported as well, or by + /// otherwise ensuring that you have the same imports on the target pallet. + /// + /// It is perfectly permissible to import multiple pallet sections into the same pallet, + /// which can be done by having multiple `#[import_section(something)]` attributes + /// attached to the pallet. + /// + /// Note that sections are imported by their module name/ident, and should be referred to + /// by their _full path_ from the perspective of the target pallet. + pub use frame_support_procedural::import_section; + + /// Allows defining getter functions on `Pallet` storage. + /// + /// ## Example + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::storage] + /// #[pallet::getter(fn my_getter_fn_name)] + /// pub type MyStorage = StorageValue<_, u32>; + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + /// + /// See [`pallet::storage`](`frame_support::pallet_macros::storage`) for more info. + pub use frame_support_procedural::getter; + + /// Defines constants that are added to the constant field of + /// [`PalletMetadata`](frame_metadata::v15::PalletMetadata) struct for this pallet. + /// + /// Must be defined like: + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// # + /// #[pallet::extra_constants] + /// impl Pallet // $optional_where_clause + /// { + /// #[pallet::constant_name(SomeU32ConstantName)] + /// /// Some doc + /// fn some_u32_constant() -> u32 { + /// 100u32 + /// } + /// } + /// } + /// ``` + /// + /// I.e. a regular rust `impl` block with some optional where clause and functions with 0 + /// args, 0 generics, and some return type. + pub use frame_support_procedural::extra_constants; + + #[rustfmt::skip] + /// Allows bypassing the `frame_system::Config` supertrait check. + /// + /// To bypass the syntactic `frame_system::Config` supertrait check, use the attribute + /// `pallet::disable_frame_system_supertrait_check`. + /// + /// Note this bypass is purely syntactic, and does not actually remove the requirement that your + /// pallet implements `frame_system::Config`. When using this check, your config is still required to implement + /// `frame_system::Config` either via + /// - Implementing a trait that itself implements `frame_system::Config` + /// - Tightly coupling it with another pallet which itself implements `frame_system::Config` + /// + /// e.g. + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # use frame_system::pallet_prelude::*; + /// trait OtherTrait: frame_system::Config {} + /// + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::config] + /// #[pallet::disable_frame_system_supertrait_check] + /// pub trait Config: OtherTrait {} + /// } + /// ``` + /// + /// To learn more about supertraits, see the + /// [trait_based_programming](../../polkadot_sdk_docs/reference_docs/trait_based_programming/index.html) + /// reference doc. + pub use frame_support_procedural::disable_frame_system_supertrait_check; + + /// The mandatory attribute allowing definition of configurable types for the pallet. + /// + /// Item must be defined as: + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::config] + /// pub trait Config: frame_system::Config // + $optionally_some_other_supertraits + /// // $optional_where_clause + /// { + /// // config items here + /// } + /// } + /// ``` + /// + /// I.e. a regular trait definition named `Config`, with the supertrait + /// [`frame_system::pallet::Config`](../../frame_system/pallet/trait.Config.html), and + /// optionally other supertraits and a where clause. (Specifying other supertraits here is + /// known as [tight coupling](https://docs.substrate.io/reference/how-to-guides/pallet-design/use-tight-coupling/)) + /// + /// The associated type `RuntimeEvent` is reserved. If defined, it must have the bounds + /// `From` and `IsType<::RuntimeEvent>`. + /// + /// [`#[pallet::event]`](`event`) must be present if `RuntimeEvent` + /// exists as a config item in your `#[pallet::config]`. + /// + /// ## Optional: `with_default` + /// + /// An optional `with_default` argument may also be specified. Doing so will automatically + /// generate a `DefaultConfig` trait inside your pallet which is suitable for use with + /// [`#[derive_impl(..)`](`frame_support::derive_impl`) to derive a default testing + /// config: + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # use frame_system::pallet_prelude::*; + /// # use core::fmt::Debug; + /// # use frame_support::traits::Contains; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::config(with_default)] // <- with_default is optional + /// pub trait Config: frame_system::Config { + /// /// The overarching event type. + /// #[pallet::no_default_bounds] // Default is not supported for RuntimeEvent + /// type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// + /// // ...other config items get default + /// } + /// + /// #[pallet::event] + /// pub enum Event { + /// SomeEvent(u16, u32), + /// } + /// } + /// ``` + /// + /// As shown above, you may also attach the [`#[pallet::no_default]`](`no_default`) + /// attribute to specify that a particular trait item _cannot_ be used as a default when a + /// test `Config` is derived using the [`#[derive_impl(..)]`](`frame_support::derive_impl`) + /// attribute macro. This will cause that particular trait item to simply not appear in + /// default testing configs based on this config (the trait item will not be included in + /// `DefaultConfig`). + /// + /// ### `DefaultConfig` Caveats + /// + /// The auto-generated `DefaultConfig` trait: + /// - is always a _subset_ of your pallet's `Config` trait. + /// - can only contain items that don't rely on externalities, such as + /// `frame_system::Config`. + /// + /// Trait items that _do_ rely on externalities should be marked with + /// [`#[pallet::no_default]`](`no_default`) + /// + /// Consequently: + /// - Any items that rely on externalities _must_ be marked with + /// [`#[pallet::no_default]`](`no_default`) or your trait will fail to compile when used + /// with [`derive_impl`](`frame_support::derive_impl`). + /// - Items marked with [`#[pallet::no_default]`](`no_default`) are entirely excluded from + /// the `DefaultConfig` trait, and therefore any impl of `DefaultConfig` doesn't need to + /// implement such items. + /// + /// For more information, see [`frame_support::derive_impl`]. + pub use frame_support_procedural::config; + + /// Allows defining an enum that gets composed as an aggregate enum by `construct_runtime`. + /// + /// The `#[pallet::composite_enum]` attribute allows you to define an enum that gets + /// composed as an aggregate enum by `construct_runtime`. This is similar in principle with + /// [frame_support_procedural::event] and [frame_support_procedural::error]. + /// + /// The attribute currently only supports enum definitions, and identifiers that are named + /// `FreezeReason`, `HoldReason`, `LockId` or `SlashReason`. Arbitrary identifiers for the + /// enum are not supported. The aggregate enum generated by + /// [`frame_support::construct_runtime`] will have the name of `RuntimeFreezeReason`, + /// `RuntimeHoldReason`, `RuntimeLockId` and `RuntimeSlashReason` respectively. + /// + /// NOTE: The aggregate enum generated by `construct_runtime` generates a conversion + /// function from the pallet enum to the aggregate enum, and automatically derives the + /// following traits: + /// + /// ```ignore + /// Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, TypeInfo, + /// RuntimeDebug + /// ``` + /// + /// For ease of usage, when no `#[derive]` attributes are found for the enum under + /// [`#[pallet::composite_enum]`](composite_enum), the aforementioned traits are + /// automatically derived for it. The inverse is also true: if there are any `#[derive]` + /// attributes found for the enum, then no traits will automatically be derived for it. + /// + /// e.g, defining `HoldReason` in a pallet + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::composite_enum] + /// pub enum HoldReason { + /// /// The NIS Pallet has reserved it for a non-fungible receipt. + /// #[codec(index = 0)] + /// SomeHoldReason, + /// #[codec(index = 1)] + /// SomeOtherHoldReason, + /// } + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + pub use frame_support_procedural::composite_enum; + + /// Allows the pallet to validate unsigned transactions. + /// + /// Item must be defined as: + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::validate_unsigned] + /// impl sp_runtime::traits::ValidateUnsigned for Pallet { + /// type Call = Call; + /// + /// fn validate_unsigned(_source: TransactionSource, _call: &Self::Call) -> TransactionValidity { + /// // Your implementation details here + /// unimplemented!() + /// } + /// } + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + /// + /// I.e. a trait implementation with bound `T: Config`, of trait + /// [`ValidateUnsigned`](frame_support::pallet_prelude::ValidateUnsigned) for + /// type `Pallet`, and some optional where clause. + /// + /// NOTE: There is also the [`sp_runtime::traits::SignedExtension`] trait that can be used + /// to add some specific logic for transaction validation. + /// + /// ## Macro expansion + /// + /// The macro currently makes no use of this information, but it might use this information + /// in the future to give information directly to [`frame_support::construct_runtime`]. + pub use frame_support_procedural::validate_unsigned; + + /// Allows defining a struct implementing the [`Get`](frame_support::traits::Get) trait to + /// ease the use of storage types. + /// + /// This attribute is meant to be used alongside [`#[pallet::storage]`](`storage`) to + /// define a storage's default value. This attribute can be used multiple times. + /// + /// Item must be defined as: + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use sp_runtime::FixedU128; + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::storage] + /// pub(super) type SomeStorage = + /// StorageValue<_, FixedU128, ValueQuery, DefaultForSomeValue>; + /// + /// // Define default for ParachainId + /// #[pallet::type_value] + /// pub fn DefaultForSomeValue() -> FixedU128 { + /// FixedU128::from_u32(1) + /// } + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + /// + /// ## Macro expansion + /// + /// The macro renames the function to some internal name, generates a struct with the + /// original name of the function and its generic, and implements `Get<$ReturnType>` by + /// calling the user defined function. + pub use frame_support_procedural::type_value; + + /// Allows defining a storage version for the pallet. + /// + /// Because the `pallet::pallet` macro implements + /// [`GetStorageVersion`](frame_support::traits::GetStorageVersion), the current storage + /// version needs to be communicated to the macro. This can be done by using the + /// `pallet::storage_version` attribute: + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::StorageVersion; + /// # use frame_support::traits::GetStorageVersion; + /// # + /// const STORAGE_VERSION: StorageVersion = StorageVersion::new(5); + /// + /// #[pallet::pallet] + /// #[pallet::storage_version(STORAGE_VERSION)] + /// pub struct Pallet(_); + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + /// + /// If not present, the current storage version is set to the default value. + pub use frame_support_procedural::storage_version; + + /// The `#[pallet::hooks]` attribute allows you to specify a + /// [`frame_support::traits::Hooks`] implementation for `Pallet` that specifies + /// pallet-specific logic. + /// + /// The item the attribute attaches to must be defined as follows: + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # use frame_system::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::hooks] + /// impl Hooks> for Pallet { + /// // Implement hooks here + /// } + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + /// I.e. a regular trait implementation with generic bound: `T: Config`, for the trait + /// `Hooks>` (they are defined in preludes), for the type `Pallet`. + /// + /// Optionally, you could add a where clause. + /// + /// ## Macro expansion + /// + /// The macro implements the traits + /// [`OnInitialize`](frame_support::traits::OnInitialize), + /// [`OnIdle`](frame_support::traits::OnIdle), + /// [`OnFinalize`](frame_support::traits::OnFinalize), + /// [`OnRuntimeUpgrade`](frame_support::traits::OnRuntimeUpgrade), + /// [`OffchainWorker`](frame_support::traits::OffchainWorker), and + /// [`IntegrityTest`](frame_support::traits::IntegrityTest) using + /// the provided [`Hooks`](frame_support::traits::Hooks) implementation. + /// + /// NOTE: `OnRuntimeUpgrade` is implemented with `Hooks::on_runtime_upgrade` and some + /// additional logic. E.g. logic to write the pallet version into storage. + /// + /// NOTE: The macro also adds some tracing logic when implementing the above traits. The + /// following hooks emit traces: `on_initialize`, `on_finalize` and `on_runtime_upgrade`. + pub use frame_support_procedural::hooks; + + /// Generates a helper function on `Pallet` that handles deposit events. + /// + /// NOTE: For instantiable pallets, the event must be generic over `T` and `I`. + /// + /// ## Macro expansion + /// + /// The macro will add on enum `Event` the attributes: + /// * `#[derive(`[`frame_support::CloneNoBound`]`)]` + /// * `#[derive(`[`frame_support::EqNoBound`]`)]` + /// * `#[derive(`[`frame_support::PartialEqNoBound`]`)]` + /// * `#[derive(`[`frame_support::RuntimeDebugNoBound`]`)]` + /// * `#[derive(`[`codec::Encode`]`)]` + /// * `#[derive(`[`codec::Decode`]`)]` + /// + /// The macro implements `From>` for (). + /// + /// The macro implements a metadata function on `Event` returning the `EventMetadata`. + /// + /// If `#[pallet::generate_deposit]` is present then the macro implements `fn + /// deposit_event` on `Pallet`. + pub use frame_support_procedural::generate_deposit; + + /// Allows defining logic to make an extrinsic call feeless. + /// + /// Each dispatchable may be annotated with the `#[pallet::feeless_if($closure)]` + /// attribute, which explicitly defines the condition for the dispatchable to be feeless. + /// + /// The arguments for the closure must be the referenced arguments of the dispatchable + /// function. + /// + /// The closure must return `bool`. + /// + /// ### Example + /// + /// ``` + /// #[frame_support::pallet(dev_mode)] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # use frame_system::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::call] + /// impl Pallet { + /// #[pallet::call_index(0)] + /// /// Marks this call as feeless if `foo` is zero. + /// #[pallet::feeless_if(|_origin: &OriginFor, foo: &u32| -> bool { + /// *foo == 0 + /// })] + /// pub fn something( + /// _: OriginFor, + /// foo: u32, + /// ) -> DispatchResult { + /// unimplemented!() + /// } + /// } + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + /// + /// Please note that this only works for signed dispatchables and requires a signed + /// extension such as [`pallet_skip_feeless_payment::SkipCheckIfFeeless`] to wrap the + /// existing payment extension. Else, this is completely ignored and the dispatchable is + /// still charged. + /// + /// ### Macro expansion + /// + /// The macro implements the [`pallet_skip_feeless_payment::CheckIfFeeless`] trait on the + /// dispatchable and calls the corresponding closure in the implementation. + /// + /// [`pallet_skip_feeless_payment::SkipCheckIfFeeless`]: ../../pallet_skip_feeless_payment/struct.SkipCheckIfFeeless.html + /// [`pallet_skip_feeless_payment::CheckIfFeeless`]: ../../pallet_skip_feeless_payment/struct.SkipCheckIfFeeless.html + pub use frame_support_procedural::feeless_if; + + /// Allows defining an error enum that will be returned from the dispatchable when an error + /// occurs. + /// + /// The information for this error type is then stored in runtime metadata. + /// + /// Item must be defined as so: + /// + /// ``` + /// #[frame_support::pallet(dev_mode)] + /// mod pallet { + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::error] + /// pub enum Error { + /// /// SomeFieldLessVariant doc + /// SomeFieldLessVariant, + /// /// SomeVariantWithOneField doc + /// SomeVariantWithOneField(u32), + /// } + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + /// I.e. a regular enum named `Error`, with generic `T` and fieldless or multiple-field + /// variants. + /// + /// Any field type in the enum variants must implement [`scale_info::TypeInfo`] in order to + /// be properly used in the metadata, and its encoded size should be as small as possible, + /// preferably 1 byte in size in order to reduce storage size. The error enum itself has an + /// absolute maximum encoded size specified by + /// [`frame_support::MAX_MODULE_ERROR_ENCODED_SIZE`]. + /// + /// (1 byte can still be 256 different errors. The more specific the error, the easier it + /// is to diagnose problems and give a better experience to the user. Don't skimp on having + /// lots of individual error conditions.) + /// + /// Field types in enum variants must also implement [`frame_support::PalletError`], + /// otherwise the pallet will fail to compile. Rust primitive types have already + /// implemented the [`frame_support::PalletError`] trait along with some commonly used + /// stdlib types such as [`Option`] and [`sp_std::marker::PhantomData`], and hence + /// in most use cases, a manual implementation is not necessary and is discouraged. + /// + /// The generic `T` must not bound anything and a `where` clause is not allowed. That said, + /// bounds and/or a where clause should not needed for any use-case. + /// + /// ## Macro expansion + /// + /// The macro implements the [`Debug`] trait and functions `as_u8` using variant position, + /// and `as_str` using variant doc. + /// + /// The macro also implements `From>` for `&'static str` and `From>` for + /// `DispatchError`. + pub use frame_support_procedural::error; + + /// Allows defining pallet events. + /// + /// Pallet events are stored under the `system` / `events` key when the block is applied + /// (and then replaced when the next block writes it's events). + /// + /// The Event enum can be defined as follows: + /// + /// ``` + /// #[frame_support::pallet(dev_mode)] + /// mod pallet { + /// # use frame_support::pallet_prelude::IsType; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::event] + /// #[pallet::generate_deposit(fn deposit_event)] // Optional + /// pub enum Event { + /// /// SomeEvent doc + /// SomeEvent(u16, u32), // SomeEvent with two fields + /// } + /// + /// #[pallet::config] + /// pub trait Config: frame_system::Config { + /// /// The overarching runtime event type. + /// type RuntimeEvent: From> + /// + IsType<::RuntimeEvent>; + /// } + /// } + /// ``` + /// + /// I.e. an enum (with named or unnamed fields variant), named `Event`, with generic: none + /// or `T` or `T: Config`, and optional w here clause. + /// + /// `RuntimeEvent` must be defined in the `Config`, as shown in the example. + /// + /// Each field must implement [`Clone`], [`Eq`], [`PartialEq`], [`codec::Encode`], + /// [`codec::Decode`], and [`Debug`] (on std only). For ease of use, bound by the trait + /// `Member`, available in [`frame_support::pallet_prelude`]. + pub use frame_support_procedural::event; - /// Allows a pallet to declare a set of functions as a *dispatchable extrinsic*. In - /// slightly simplified terms, this macro declares the set of "transactions" of a pallet. + /// Allows a pallet to declare a set of functions as a *dispatchable extrinsic*. + /// + /// In slightly simplified terms, this macro declares the set of "transactions" of a + /// pallet. /// /// > The exact definition of **extrinsic** can be found in /// > [`sp_runtime::generic::UncheckedExtrinsic`]. @@ -2330,7 +1933,7 @@ pub mod pallet_macros { /// # use frame_support::__private::TestExternalities; /// # use frame_support::traits::UnfilteredDispatchable; /// # impl custom_pallet::Config for Runtime {} - /// # #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + /// # #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] /// # impl frame_system::Config for Runtime { /// # type Block = frame_system::mocking::MockBlock; /// # } @@ -2382,14 +1985,24 @@ pub mod pallet_macros { /// If no `#[pallet::call]` exists, then a default implementation corresponding to the /// following code is automatically generated: /// - /// ```ignore - /// #[pallet::call] - /// impl Pallet {} + /// ``` + /// #[frame_support::pallet(dev_mode)] + /// mod pallet { + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::call] // <- automatically generated + /// impl Pallet {} // <- automatically generated + /// + /// #[pallet::config] + /// pub trait Config: frame_system::Config {} + /// } /// ``` pub use frame_support_procedural::call; - /// Enforce the index of a variant in the generated `enum Call`. See [`call`] for more - /// information. + /// Enforce the index of a variant in the generated `enum Call`. + /// + /// See [`call`] for more information. /// /// All call indexes start from 0, until it encounters a dispatchable function with a /// defined call index. The dispatchable function that lexically follows the function with @@ -2399,7 +2012,9 @@ pub mod pallet_macros { pub use frame_support_procedural::call_index; /// Declares the arguments of a [`call`] function to be encoded using - /// [`codec::Compact`]. This will results in smaller extrinsic encoding. + /// [`codec::Compact`]. + /// + /// This will results in smaller extrinsic encoding. /// /// A common example of `compact` is for numeric values that are often times far far away /// from their theoretical maximum. For example, in the context of a crypto-currency, the @@ -2495,8 +2110,8 @@ pub mod pallet_macros { /// ``` pub use frame_support_procedural::genesis_build; - /// The `#[pallet::constant]` attribute can be used to add an associated type trait bounded - /// by [`Get`](frame_support::pallet_prelude::Get) from [`pallet::config`](`macro@config`) + /// Allows adding an associated type trait bounded by + /// [`Get`](frame_support::pallet_prelude::Get) from [`pallet::config`](`macro@config`) /// into metadata. /// /// ## Example @@ -2517,9 +2132,10 @@ pub mod pallet_macros { /// ``` pub use frame_support_procedural::constant; - /// Declares a type alias as a storage item. Storage items are pointers to data stored - /// on-chain (the *blockchain state*), under a specific key. The exact key is dependent on - /// the type of the storage. + /// Declares a type alias as a storage item. + /// + /// Storage items are pointers to data stored on-chain (the *blockchain state*), under a + /// specific key. The exact key is dependent on the type of the storage. /// /// > From the perspective of this pallet, the entire blockchain state is abstracted behind /// > a key-value api, namely [`sp_io::storage`]. @@ -2699,6 +2315,8 @@ pub mod pallet_macros { /// * [`macro@getter`]: Creates a custom getter function. /// * [`macro@storage_prefix`]: Overrides the default prefix of the storage item. /// * [`macro@unbounded`]: Declares the storage item as unbounded. + /// * [`macro@disable_try_decode_storage`]: Declares that try-runtime checks should not + /// attempt to decode the storage item. /// /// #### Example /// ``` @@ -2714,11 +2332,15 @@ pub mod pallet_macros { /// #[pallet::getter(fn foo)] /// #[pallet::storage_prefix = "OtherFoo"] /// #[pallet::unbounded] + /// #[pallet::disable_try_decode_storage] /// pub type Foo = StorageValue<_, u32, ValueQuery>; /// } /// ``` pub use frame_support_procedural::storage; - /// This attribute is attached to a function inside an `impl` block annoated with + + /// Allows defining conditions for a task to run. + /// + /// This attribute is attached to a function inside an `impl` block annotated with /// [`pallet::tasks_experimental`](`tasks_experimental`) to define the conditions for a /// given work item to be valid. /// @@ -2726,29 +2348,39 @@ pub mod pallet_macros { /// should have the same signature as the function it is attached to, except that it should /// return a `bool` instead. pub use frame_support_procedural::task_condition; - /// This attribute is attached to a function inside an `impl` block annoated with + + /// Allows defining an index for a task. + /// + /// This attribute is attached to a function inside an `impl` block annotated with /// [`pallet::tasks_experimental`](`tasks_experimental`) to define the index of a given /// work item. /// /// It takes an integer literal as input, which is then used to define the index. This /// index should be unique for each function in the `impl` block. pub use frame_support_procedural::task_index; - /// This attribute is attached to a function inside an `impl` block annoated with - /// [`pallet::tasks_experimental`](`tasks_experimental`) to define an iterator over the - /// available work items for a task. + + /// Allows defining an iterator over available work items for a task. + /// + /// This attribute is attached to a function inside an `impl` block annotated with + /// [`pallet::tasks_experimental`](`tasks_experimental`). /// /// It takes an iterator as input that yields a tuple with same types as the function /// arguments. pub use frame_support_procedural::task_list; - /// This attribute is attached to a function inside an `impl` block annoated with + + /// Allows defining the weight of a task. + /// + /// This attribute is attached to a function inside an `impl` block annotated with /// [`pallet::tasks_experimental`](`tasks_experimental`) define the weight of a given work /// item. /// /// It takes a closure as input, which should return a `Weight` value. pub use frame_support_procedural::task_weight; + /// Allows you to define some service work that can be recognized by a script or an - /// off-chain worker. Such a script can then create and submit all such work items at any - /// given time. + /// off-chain worker. + /// + /// Such a script can then create and submit all such work items at any given time. /// /// These work items are defined as instances of the [`Task`](frame_support::traits::Task) /// trait. [`pallet:tasks_experimental`](`tasks_experimental`) when attached to an `impl` @@ -2773,6 +2405,64 @@ pub mod pallet_macros { /// Now, this can be executed as follows: #[doc = docify::embed!("src/tests/tasks.rs", tasks_work)] pub use frame_support_procedural::tasks_experimental; + + /// Allows a pallet to declare a type as an origin. + /// + /// If defined as such, this type will be amalgamated at the runtime level into + /// `RuntimeOrigin`, very similar to [`call`], [`error`] and [`event`]. See + /// [`composite_enum`] for similar cases. + /// + /// Origin is a complex FRAME topics and is further explained in `polkadot_sdk_docs`. + /// + /// ## Syntax Variants + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// # #[pallet::pallet] + /// # pub struct Pallet(_); + /// /// On the spot declaration. + /// #[pallet::origin] + /// #[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)] + /// pub enum Origin { + /// Foo, + /// Bar, + /// } + /// } + /// ``` + /// + /// Or, more commonly used: + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// # #[pallet::pallet] + /// # pub struct Pallet(_); + /// #[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)] + /// pub enum RawOrigin { + /// Foo, + /// Bar, + /// } + /// + /// #[pallet::origin] + /// pub type Origin = RawOrigin; + /// } + /// ``` + /// + /// ## Warning + /// + /// Modifying any pallet's origin type will cause the runtime level origin type to also + /// change in encoding. If stored anywhere on-chain, this will require a data migration. + /// + /// Read more about origins at the [Origin Reference + /// Docs](../../polkadot_sdk_docs/reference_docs/frame_origin/index.html). + pub use frame_support_procedural::origin; } #[deprecated(note = "Will be removed after July 2023; Use `sp_runtime::traits` directly instead.")] diff --git a/substrate/frame/support/src/migrations.rs b/substrate/frame/support/src/migrations.rs index d059a992a8664cd8c3583f7f6fd1bff6af102770..2ceab44cb16bd4bcf356caa07a3506aa76ba79db 100644 --- a/substrate/frame/support/src/migrations.rs +++ b/substrate/frame/support/src/migrations.rs @@ -16,13 +16,21 @@ // limitations under the License. use crate::{ - traits::{GetStorageVersion, NoStorageVersionSet, PalletInfoAccess, StorageVersion}, - weights::{RuntimeDbWeight, Weight}, + defensive, + storage::transactional::with_transaction_opaque_err, + traits::{ + Defensive, GetStorageVersion, NoStorageVersionSet, PalletInfoAccess, SafeMode, + StorageVersion, + }, + weights::{RuntimeDbWeight, Weight, WeightMeter}, }; +use codec::{Decode, Encode, MaxEncodedLen}; use impl_trait_for_tuples::impl_for_tuples; +use sp_arithmetic::traits::Bounded; use sp_core::Get; use sp_io::{hashing::twox_128, storage::clear_prefix, KillStorageResult}; -use sp_std::marker::PhantomData; +use sp_runtime::traits::Zero; +use sp_std::{marker::PhantomData, vec::Vec}; /// Handles storage migration pallet versioning. /// @@ -43,12 +51,30 @@ use sp_std::marker::PhantomData; /// Otherwise, a warning is logged notifying the developer that the upgrade was a noop and should /// probably be removed. /// +/// It is STRONGLY RECOMMENDED to write the unversioned migration logic in a private module and +/// only export the versioned migration logic to prevent accidentally using the unversioned +/// migration in any runtimes. +/// /// ### Examples /// ```ignore /// // In file defining migrations -/// pub struct VersionUncheckedMigrateV5ToV6(sp_std::marker::PhantomData); -/// impl OnRuntimeUpgrade for VersionUncheckedMigrateV5ToV6 { -/// // OnRuntimeUpgrade implementation... +/// +/// /// Private module containing *version unchecked* migration logic. +/// /// +/// /// Should only be used by the [`VersionedMigration`] type in this module to create something to +/// /// export. +/// /// +/// /// We keep this private so the unversioned migration cannot accidentally be used in any runtimes. +/// /// +/// /// For more about this pattern of keeping items private, see +/// /// - https://github.com/rust-lang/rust/issues/30905 +/// /// - https://internals.rust-lang.org/t/lang-team-minutes-private-in-public-rules/4504/40 +/// mod version_unchecked { +/// use super::*; +/// pub struct MigrateV5ToV6(sp_std::marker::PhantomData); +/// impl OnRuntimeUpgrade for VersionUncheckedMigrateV5ToV6 { +/// // OnRuntimeUpgrade implementation... +/// } /// } /// /// pub type MigrateV5ToV6 = @@ -73,7 +99,7 @@ pub struct VersionedMigration), @@ -91,7 +117,7 @@ impl< const FROM: u16, const TO: u16, Inner: crate::traits::OnRuntimeUpgrade, - Pallet: GetStorageVersion + PalletInfoAccess, + Pallet: GetStorageVersion + PalletInfoAccess, DbWeight: Get, > crate::traits::OnRuntimeUpgrade for VersionedMigration { @@ -100,7 +126,6 @@ impl< /// migration ran or not. #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { - use codec::Encode; let on_chain_version = Pallet::on_chain_storage_version(); if on_chain_version == FROM { Ok(VersionedPostUpgradeData::MigrationExecuted(Inner::pre_upgrade()?).encode()) @@ -163,25 +188,25 @@ impl< } } -/// Can store the current pallet version in storage. -pub trait StoreCurrentStorageVersion { - /// Write the current storage version to the storage. - fn store_current_storage_version(); +/// Can store the in-code pallet version on-chain. +pub trait StoreInCodeStorageVersion { + /// Write the in-code storage version on-chain. + fn store_in_code_storage_version(); } -impl + PalletInfoAccess> - StoreCurrentStorageVersion for StorageVersion +impl + PalletInfoAccess> + StoreInCodeStorageVersion for StorageVersion { - fn store_current_storage_version() { - let version = ::current_storage_version(); + fn store_in_code_storage_version() { + let version = ::in_code_storage_version(); version.put::(); } } -impl + PalletInfoAccess> - StoreCurrentStorageVersion for NoStorageVersionSet +impl + PalletInfoAccess> + StoreInCodeStorageVersion for NoStorageVersionSet { - fn store_current_storage_version() { + fn store_in_code_storage_version() { StorageVersion::default().put::(); } } @@ -193,7 +218,7 @@ pub trait PalletVersionToStorageVersionHelper { impl PalletVersionToStorageVersionHelper for T where - T::CurrentStorageVersion: StoreCurrentStorageVersion, + T::InCodeStorageVersion: StoreInCodeStorageVersion, { fn migrate(db_weight: &RuntimeDbWeight) -> Weight { const PALLET_VERSION_STORAGE_KEY_POSTFIX: &[u8] = b":__PALLET_VERSION__:"; @@ -204,8 +229,7 @@ where sp_io::storage::clear(&pallet_version_key(::name())); - >::store_current_storage_version( - ); + >::store_in_code_storage_version(); db_weight.writes(2) } @@ -226,7 +250,7 @@ impl PalletVersionToStorageVersionHelper for T { /// Migrate from the `PalletVersion` struct to the new [`StorageVersion`] struct. /// -/// This will remove all `PalletVersion's` from the state and insert the current storage version. +/// This will remove all `PalletVersion's` from the state and insert the in-code storage version. pub fn migrate_from_pallet_version_to_storage_version< Pallets: PalletVersionToStorageVersionHelper, >( @@ -344,3 +368,622 @@ impl, DbWeight: Get> frame_support::traits Ok(()) } } + +/// A migration that can proceed in multiple steps. +pub trait SteppedMigration { + /// The cursor type that stores the progress (aka. state) of this migration. + type Cursor: codec::FullCodec + codec::MaxEncodedLen; + + /// The unique identifier type of this migration. + type Identifier: codec::FullCodec + codec::MaxEncodedLen; + + /// The unique identifier of this migration. + /// + /// If two migrations have the same identifier, then they are assumed to be identical. + fn id() -> Self::Identifier; + + /// The maximum number of steps that this migration can take. + /// + /// This can be used to enforce progress and prevent migrations becoming stuck forever. A + /// migration that exceeds its max steps is treated as failed. `None` means that there is no + /// limit. + fn max_steps() -> Option { + None + } + + /// Try to migrate as much as possible with the given weight. + /// + /// **ANY STORAGE CHANGES MUST BE ROLLED-BACK BY THE CALLER UPON ERROR.** This is necessary + /// since the caller cannot return a cursor in the error case. [`Self::transactional_step`] is + /// provided as convenience for a caller. A cursor of `None` implies that the migration is at + /// its end. A migration that once returned `Nonce` is guaranteed to never be called again. + fn step( + cursor: Option, + meter: &mut WeightMeter, + ) -> Result, SteppedMigrationError>; + + /// Same as [`Self::step`], but rolls back pending changes in the error case. + fn transactional_step( + mut cursor: Option, + meter: &mut WeightMeter, + ) -> Result, SteppedMigrationError> { + with_transaction_opaque_err(move || match Self::step(cursor, meter) { + Ok(new_cursor) => { + cursor = new_cursor; + sp_runtime::TransactionOutcome::Commit(Ok(cursor)) + }, + Err(err) => sp_runtime::TransactionOutcome::Rollback(Err(err)), + }) + .map_err(|()| SteppedMigrationError::Failed)? + } +} + +/// Error that can occur during a [`SteppedMigration`]. +#[derive(Debug, Encode, Decode, MaxEncodedLen, scale_info::TypeInfo)] +pub enum SteppedMigrationError { + // Transient errors: + /// The remaining weight is not enough to do anything. + /// + /// Can be resolved by calling with at least `required` weight. Note that calling it with + /// exactly `required` weight could cause it to not make any progress. + InsufficientWeight { + /// Amount of weight required to make progress. + required: Weight, + }, + // Permanent errors: + /// The migration cannot decode its cursor and therefore not proceed. + /// + /// This should not happen unless (1) the migration itself returned an invalid cursor in a + /// previous iteration, (2) the storage got corrupted or (3) there is a bug in the caller's + /// code. + InvalidCursor, + /// The migration encountered a permanent error and cannot continue. + Failed, +} + +/// Notification handler for status updates regarding Multi-Block-Migrations. +#[impl_trait_for_tuples::impl_for_tuples(8)] +pub trait MigrationStatusHandler { + /// Notifies of the start of a runtime migration. + fn started() {} + + /// Notifies of the completion of a runtime migration. + fn completed() {} +} + +/// Handles a failed runtime migration. +/// +/// This should never happen, but is here for completeness. +pub trait FailedMigrationHandler { + /// Infallibly handle a failed runtime migration. + /// + /// Gets passed in the optional index of the migration in the batch that caused the failure. + /// Returning `None` means that no automatic handling should take place and the callee decides + /// in the implementation what to do. + fn failed(migration: Option) -> FailedMigrationHandling; +} + +/// Do now allow any transactions to be processed after a runtime upgrade failed. +/// +/// This is **not a sane default**, since it prevents governance intervention. +pub struct FreezeChainOnFailedMigration; + +impl FailedMigrationHandler for FreezeChainOnFailedMigration { + fn failed(_migration: Option) -> FailedMigrationHandling { + FailedMigrationHandling::KeepStuck + } +} + +/// Enter safe mode on a failed runtime upgrade. +/// +/// This can be very useful to manually intervene and fix the chain state. `Else` is used in case +/// that the safe mode could not be entered. +pub struct EnterSafeModeOnFailedMigration( + PhantomData<(SM, Else)>, +); + +impl FailedMigrationHandler + for EnterSafeModeOnFailedMigration +where + ::BlockNumber: Bounded, +{ + fn failed(migration: Option) -> FailedMigrationHandling { + let entered = if SM::is_entered() { + SM::extend(Bounded::max_value()) + } else { + SM::enter(Bounded::max_value()) + }; + + // If we could not enter or extend safe mode (for whatever reason), then we try the next. + if entered.is_err() { + Else::failed(migration) + } else { + FailedMigrationHandling::KeepStuck + } + } +} + +/// How to proceed after a runtime upgrade failed. +/// +/// There is NO SANE DEFAULT HERE. All options are very dangerous and should be used with care. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum FailedMigrationHandling { + /// Resume extrinsic processing of the chain. This will not resume the upgrade. + /// + /// This should be supplemented with additional measures to ensure that the broken chain state + /// does not get further messed up by user extrinsics. + ForceUnstuck, + /// Set the cursor to `Stuck` and keep blocking extrinsics. + KeepStuck, + /// Don't do anything with the cursor and let the handler decide. + /// + /// This can be useful in cases where the other two options would overwrite any changes that + /// were done by the handler to the cursor. + Ignore, +} + +/// Something that can do multi step migrations. +pub trait MultiStepMigrator { + /// Hint for whether [`Self::step`] should be called. + fn ongoing() -> bool; + + /// Do the next step in the MBM process. + /// + /// Must gracefully handle the case that it is currently not upgrading. + fn step() -> Weight; +} + +impl MultiStepMigrator for () { + fn ongoing() -> bool { + false + } + + fn step() -> Weight { + Weight::zero() + } +} + +/// Multiple [`SteppedMigration`]. +pub trait SteppedMigrations { + /// The number of migrations that `Self` aggregates. + fn len() -> u32; + + /// The `n`th [`SteppedMigration::id`]. + /// + /// Is guaranteed to return `Some` if `n < Self::len()`. + fn nth_id(n: u32) -> Option>; + + /// The [`SteppedMigration::max_steps`] of the `n`th migration. + /// + /// Is guaranteed to return `Some` if `n < Self::len()`. + fn nth_max_steps(n: u32) -> Option>; + + /// Do a [`SteppedMigration::step`] on the `n`th migration. + /// + /// Is guaranteed to return `Some` if `n < Self::len()`. + fn nth_step( + n: u32, + cursor: Option>, + meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>>; + + /// Do a [`SteppedMigration::transactional_step`] on the `n`th migration. + /// + /// Is guaranteed to return `Some` if `n < Self::len()`. + fn nth_transactional_step( + n: u32, + cursor: Option>, + meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>>; + + /// The maximal encoded length across all cursors. + fn cursor_max_encoded_len() -> usize; + + /// The maximal encoded length across all identifiers. + fn identifier_max_encoded_len() -> usize; + + /// Assert the integrity of the migrations. + /// + /// Should be executed as part of a test prior to runtime usage. May or may not need + /// externalities. + #[cfg(feature = "std")] + fn integrity_test() -> Result<(), &'static str> { + use crate::ensure; + let l = Self::len(); + + for n in 0..l { + ensure!(Self::nth_id(n).is_some(), "id is None"); + ensure!(Self::nth_max_steps(n).is_some(), "steps is None"); + + // The cursor that we use does not matter. Hence use empty. + ensure!( + Self::nth_step(n, Some(vec![]), &mut WeightMeter::new()).is_some(), + "steps is None" + ); + ensure!( + Self::nth_transactional_step(n, Some(vec![]), &mut WeightMeter::new()).is_some(), + "steps is None" + ); + } + + Ok(()) + } +} + +impl SteppedMigrations for () { + fn len() -> u32 { + 0 + } + + fn nth_id(_n: u32) -> Option> { + None + } + + fn nth_max_steps(_n: u32) -> Option> { + None + } + + fn nth_step( + _n: u32, + _cursor: Option>, + _meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>> { + None + } + + fn nth_transactional_step( + _n: u32, + _cursor: Option>, + _meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>> { + None + } + + fn cursor_max_encoded_len() -> usize { + 0 + } + + fn identifier_max_encoded_len() -> usize { + 0 + } +} + +// A collection consisting of only a single migration. +impl SteppedMigrations for T { + fn len() -> u32 { + 1 + } + + fn nth_id(_n: u32) -> Option> { + Some(T::id().encode()) + } + + fn nth_max_steps(n: u32) -> Option> { + // It should be generally fine to call with n>0, but the code should not attempt to. + n.is_zero() + .then_some(T::max_steps()) + .defensive_proof("nth_max_steps should only be called with n==0") + } + + fn nth_step( + _n: u32, + cursor: Option>, + meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>> { + if !_n.is_zero() { + defensive!("nth_step should only be called with n==0"); + return None + } + + let cursor = match cursor { + Some(cursor) => match T::Cursor::decode(&mut &cursor[..]) { + Ok(cursor) => Some(cursor), + Err(_) => return Some(Err(SteppedMigrationError::InvalidCursor)), + }, + None => None, + }; + + Some(T::step(cursor, meter).map(|cursor| cursor.map(|cursor| cursor.encode()))) + } + + fn nth_transactional_step( + n: u32, + cursor: Option>, + meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>> { + if n != 0 { + defensive!("nth_transactional_step should only be called with n==0"); + return None + } + + let cursor = match cursor { + Some(cursor) => match T::Cursor::decode(&mut &cursor[..]) { + Ok(cursor) => Some(cursor), + Err(_) => return Some(Err(SteppedMigrationError::InvalidCursor)), + }, + None => None, + }; + + Some( + T::transactional_step(cursor, meter).map(|cursor| cursor.map(|cursor| cursor.encode())), + ) + } + + fn cursor_max_encoded_len() -> usize { + T::Cursor::max_encoded_len() + } + + fn identifier_max_encoded_len() -> usize { + T::Identifier::max_encoded_len() + } +} + +#[impl_trait_for_tuples::impl_for_tuples(1, 30)] +impl SteppedMigrations for Tuple { + fn len() -> u32 { + for_tuples!( #( Tuple::len() )+* ) + } + + fn nth_id(n: u32) -> Option> { + let mut i = 0; + + for_tuples!( #( + if (i + Tuple::len()) > n { + return Tuple::nth_id(n - i) + } + + i += Tuple::len(); + )* ); + + None + } + + fn nth_step( + n: u32, + cursor: Option>, + meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>> { + let mut i = 0; + + for_tuples!( #( + if (i + Tuple::len()) > n { + return Tuple::nth_step(n - i, cursor, meter) + } + + i += Tuple::len(); + )* ); + + None + } + + fn nth_transactional_step( + n: u32, + cursor: Option>, + meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>> { + let mut i = 0; + + for_tuples! ( #( + if (i + Tuple::len()) > n { + return Tuple::nth_transactional_step(n - i, cursor, meter) + } + + i += Tuple::len(); + )* ); + + None + } + + fn nth_max_steps(n: u32) -> Option> { + let mut i = 0; + + for_tuples!( #( + if (i + Tuple::len()) > n { + return Tuple::nth_max_steps(n - i) + } + + i += Tuple::len(); + )* ); + + None + } + + fn cursor_max_encoded_len() -> usize { + let mut max_len = 0; + + for_tuples!( #( + max_len = max_len.max(Tuple::cursor_max_encoded_len()); + )* ); + + max_len + } + + fn identifier_max_encoded_len() -> usize { + let mut max_len = 0; + + for_tuples!( #( + max_len = max_len.max(Tuple::identifier_max_encoded_len()); + )* ); + + max_len + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{assert_ok, storage::unhashed}; + + #[derive(Decode, Encode, MaxEncodedLen, Eq, PartialEq)] + pub enum Either { + Left(L), + Right(R), + } + + pub struct M0; + impl SteppedMigration for M0 { + type Cursor = (); + type Identifier = u8; + + fn id() -> Self::Identifier { + 0 + } + + fn step( + _cursor: Option, + _meter: &mut WeightMeter, + ) -> Result, SteppedMigrationError> { + log::info!("M0"); + unhashed::put(&[0], &()); + Ok(None) + } + } + + pub struct M1; + impl SteppedMigration for M1 { + type Cursor = (); + type Identifier = u8; + + fn id() -> Self::Identifier { + 1 + } + + fn step( + _cursor: Option, + _meter: &mut WeightMeter, + ) -> Result, SteppedMigrationError> { + log::info!("M1"); + unhashed::put(&[1], &()); + Ok(None) + } + + fn max_steps() -> Option { + Some(1) + } + } + + pub struct M2; + impl SteppedMigration for M2 { + type Cursor = (); + type Identifier = u8; + + fn id() -> Self::Identifier { + 2 + } + + fn step( + _cursor: Option, + _meter: &mut WeightMeter, + ) -> Result, SteppedMigrationError> { + log::info!("M2"); + unhashed::put(&[2], &()); + Ok(None) + } + + fn max_steps() -> Option { + Some(2) + } + } + + pub struct F0; + impl SteppedMigration for F0 { + type Cursor = (); + type Identifier = u8; + + fn id() -> Self::Identifier { + 3 + } + + fn step( + _cursor: Option, + _meter: &mut WeightMeter, + ) -> Result, SteppedMigrationError> { + log::info!("F0"); + unhashed::put(&[3], &()); + Err(SteppedMigrationError::Failed) + } + } + + // Three migrations combined to execute in order: + type Triple = (M0, (M1, M2)); + // Six migrations, just concatenating the ones from before: + type Hextuple = (Triple, Triple); + + #[test] + fn singular_migrations_work() { + assert_eq!(M0::max_steps(), None); + assert_eq!(M1::max_steps(), Some(1)); + assert_eq!(M2::max_steps(), Some(2)); + + assert_eq!(<(M0, M1)>::nth_max_steps(0), Some(None)); + assert_eq!(<(M0, M1)>::nth_max_steps(1), Some(Some(1))); + assert_eq!(<(M0, M1, M2)>::nth_max_steps(2), Some(Some(2))); + + assert_eq!(<(M0, M1)>::nth_max_steps(2), None); + } + + #[test] + fn tuple_migrations_work() { + assert_eq!(<() as SteppedMigrations>::len(), 0); + assert_eq!(<((), ((), ())) as SteppedMigrations>::len(), 0); + assert_eq!(::len(), 3); + assert_eq!(::len(), 6); + + // Check the IDs. The index specific functions all return an Option, + // to account for the out-of-range case. + assert_eq!(::nth_id(0), Some(0u8.encode())); + assert_eq!(::nth_id(1), Some(1u8.encode())); + assert_eq!(::nth_id(2), Some(2u8.encode())); + + sp_io::TestExternalities::default().execute_with(|| { + for n in 0..3 { + ::nth_step( + n, + Default::default(), + &mut WeightMeter::new(), + ); + } + }); + } + + #[test] + fn integrity_test_works() { + sp_io::TestExternalities::default().execute_with(|| { + assert_ok!(<() as SteppedMigrations>::integrity_test()); + assert_ok!(::integrity_test()); + assert_ok!(::integrity_test()); + assert_ok!(::integrity_test()); + assert_ok!(::integrity_test()); + assert_ok!(::integrity_test()); + }); + } + + #[test] + fn transactional_rollback_works() { + sp_io::TestExternalities::default().execute_with(|| { + assert_ok!(<(M0, F0) as SteppedMigrations>::nth_transactional_step( + 0, + Default::default(), + &mut WeightMeter::new() + ) + .unwrap()); + assert!(unhashed::exists(&[0])); + + let _g = crate::StorageNoopGuard::new(); + assert!(<(M0, F0) as SteppedMigrations>::nth_transactional_step( + 1, + Default::default(), + &mut WeightMeter::new() + ) + .unwrap() + .is_err()); + assert!(<(F0, M1) as SteppedMigrations>::nth_transactional_step( + 0, + Default::default(), + &mut WeightMeter::new() + ) + .unwrap() + .is_err()); + }); + } +} diff --git a/substrate/frame/support/src/storage/migration.rs b/substrate/frame/support/src/storage/migration.rs index 568c475bdc69d6adc9f59bc0b059b48987bd04e3..252625cf4f7d02aa82418ca50b45334c53692766 100644 --- a/substrate/frame/support/src/storage/migration.rs +++ b/substrate/frame/support/src/storage/migration.rs @@ -303,11 +303,11 @@ pub fn take_storage_item /// Move a storage from a pallet prefix to another pallet prefix. /// /// Keys used in pallet storages always start with: -/// `concat(twox_128(pallet_name), towx_128(storage_name))`. +/// `concat(twox_128(pallet_name), twox_128(storage_name))`. /// /// This function will remove all value for which the key start with -/// `concat(twox_128(old_pallet_name), towx_128(storage_name))` and insert them at the key with -/// the start replaced by `concat(twox_128(new_pallet_name), towx_128(storage_name))`. +/// `concat(twox_128(old_pallet_name), twox_128(storage_name))` and insert them at the key with +/// the start replaced by `concat(twox_128(new_pallet_name), twox_128(storage_name))`. /// /// # Example /// @@ -339,7 +339,7 @@ pub fn move_storage_from_pallet( /// Move all storages from a pallet prefix to another pallet prefix. /// /// Keys used in pallet storages always start with: -/// `concat(twox_128(pallet_name), towx_128(storage_name))`. +/// `concat(twox_128(pallet_name), twox_128(storage_name))`. /// /// This function will remove all value for which the key start with `twox_128(old_pallet_name)` /// and insert them at the key with the start replaced by `twox_128(new_pallet_name)`. diff --git a/substrate/frame/support/src/storage/mod.rs b/substrate/frame/support/src/storage/mod.rs index 8ebe7b31da80d3511b4a0003637d1f45b52d5b11..f7d7447482d06ccd64e59c3b8e49aec18696fdee 100644 --- a/substrate/frame/support/src/storage/mod.rs +++ b/substrate/frame/support/src/storage/mod.rs @@ -159,7 +159,7 @@ pub trait StorageValue { /// /// # Warning /// - /// `None` does not mean that `get()` does not return a value. The default value is completly + /// `None` does not mean that `get()` does not return a value. The default value is completely /// ignored by this function. fn decode_len() -> Option where @@ -363,7 +363,7 @@ pub trait StorageMap { /// /// # Warning /// - /// `None` does not mean that `get()` does not return a value. The default value is completly + /// `None` does not mean that `get()` does not return a value. The default value is completely /// ignored by this function. fn decode_len>(key: KeyArg) -> Option where @@ -381,7 +381,8 @@ pub trait StorageMap { /// /// # Warning /// - /// - `None` does not mean that `get()` does not return a value. The default value is completly + /// - `None` does not mean that `get()` does not return a value. The default value is + /// completely /// ignored by this function. /// /// - The value returned is the non-deduplicated length of the underlying Vector in storage.This @@ -410,7 +411,7 @@ pub trait StorageMap { pub trait IterableStorageMap: StorageMap { /// The type that iterates over all `(key, value)`. type Iterator: Iterator; - /// The type that itereates over all `key`s. + /// The type that iterates over all `key`s. type KeyIterator: Iterator; /// Enumerate all elements in the map in lexicographical order of the encoded key. If you @@ -777,7 +778,7 @@ pub trait StorageDoubleMap { /// /// # Warning /// - /// `None` does not mean that `get()` does not return a value. The default value is completly + /// `None` does not mean that `get()` does not return a value. The default value is completely /// ignored by this function. fn decode_len(key1: KArg1, key2: KArg2) -> Option where @@ -798,7 +799,7 @@ pub trait StorageDoubleMap { /// /// # Warning /// - /// `None` does not mean that `get()` does not return a value. The default value is completly + /// `None` does not mean that `get()` does not return a value. The default value is completely /// ignored by this function. fn decode_non_dedup_len(key1: KArg1, key2: KArg2) -> Option where @@ -980,7 +981,7 @@ pub trait StorageNMap { /// /// # Warning /// - /// `None` does not mean that `get()` does not return a value. The default value is completly + /// `None` does not mean that `get()` does not return a value. The default value is completely /// ignored by this function. fn decode_len + TupleToEncodedIter>(key: KArg) -> Option where @@ -1488,8 +1489,8 @@ pub trait StorageDecodeLength: private::Sealed + codec::DecodeLength { } } -/// It is expected that the length is at the beginning of the encoded objectand that the length is a -/// `Compact`. +/// It is expected that the length is at the beginning of the encoded object and that the length is +/// a `Compact`. /// /// # Note /// The length returned by this trait is not deduplicated, i.e. it is the length of the underlying @@ -1790,7 +1791,7 @@ mod test { }); } - // This test ensures that the Digest encoding does not change without being noticied. + // This test ensures that the Digest encoding does not change without being noticed. #[test] fn digest_storage_append_works_as_expected() { TestExternalities::default().execute_with(|| { diff --git a/substrate/frame/support/src/storage/stream_iter.rs b/substrate/frame/support/src/storage/stream_iter.rs index 2205601938b880f88bcdbc20680e7d6254badc8b..529b2f387c71cc86b52f648f8630bd9f2339dd69 100644 --- a/substrate/frame/support/src/storage/stream_iter.rs +++ b/substrate/frame/support/src/storage/stream_iter.rs @@ -217,7 +217,7 @@ const STORAGE_INPUT_BUFFER_CAPACITY: usize = 2 * 1024; /// Implementation of [`codec::Input`] using [`sp_io::storage::read`]. /// /// Keeps an internal buffer with a size of [`STORAGE_INPUT_BUFFER_CAPACITY`]. All read accesses -/// are tried to be served by this buffer. If the buffer doesn't hold enough bytes to fullfill the +/// are tried to be served by this buffer. If the buffer doesn't hold enough bytes to fulfill the /// current read access, the buffer is re-filled from the state. A read request that is bigger than /// the internal buffer is directly forwarded to the state to reduce the number of reads from the /// state. diff --git a/substrate/frame/support/src/storage/transactional.rs b/substrate/frame/support/src/storage/transactional.rs index d42e1809e91292a8e0ad367af4e203b8b1b17f87..0671db4a3a86bc76f7706df6eefe28a8c34c0701 100644 --- a/substrate/frame/support/src/storage/transactional.rs +++ b/substrate/frame/support/src/storage/transactional.rs @@ -127,6 +127,22 @@ where } } +/// Same as [`with_transaction`] but casts any internal error to `()`. +/// +/// This rids `E` of the `From` bound that is required by `with_transaction`. +pub fn with_transaction_opaque_err(f: F) -> Result, ()> +where + F: FnOnce() -> TransactionOutcome>, +{ + with_transaction(move || -> TransactionOutcome, DispatchError>> { + match f() { + TransactionOutcome::Commit(res) => TransactionOutcome::Commit(Ok(res)), + TransactionOutcome::Rollback(res) => TransactionOutcome::Rollback(Ok(res)), + } + }) + .map_err(|_| ()) +} + /// Same as [`with_transaction`] but without a limit check on nested transactional layers. /// /// This is mostly for backwards compatibility before there was a transactional layer limit. diff --git a/substrate/frame/support/src/storage/types/counted_map.rs b/substrate/frame/support/src/storage/types/counted_map.rs index 04e69751c16a00e4b89fdf5907bec5be64305014..0444e269928ab68e6da1ebada37425d77bdcc288 100644 --- a/substrate/frame/support/src/storage/types/counted_map.rs +++ b/substrate/frame/support/src/storage/types/counted_map.rs @@ -310,7 +310,7 @@ where /// /// # Warning /// - /// `None` does not mean that `get()` does not return a value. The default value is completly + /// `None` does not mean that `get()` does not return a value. The default value is completely /// ignored by this function. pub fn decode_len>(key: KeyArg) -> Option where @@ -775,13 +775,13 @@ mod test { assert_eq!(A::try_get(1), Err(())); assert_eq!(A::count(), 3); - // Take exsisting. + // Take existing. assert_eq!(A::take(4), 10); assert_eq!(A::try_get(4), Err(())); assert_eq!(A::count(), 2); - // Take non-exsisting. + // Take non-existing. assert_eq!(A::take(4), ADefault::get()); assert_eq!(A::try_get(4), Err(())); @@ -1022,13 +1022,13 @@ mod test { assert_eq!(B::try_get(1), Err(())); assert_eq!(B::count(), 3); - // Take exsisting. + // Take existing. assert_eq!(B::take(4), Some(10)); assert_eq!(B::try_get(4), Err(())); assert_eq!(B::count(), 2); - // Take non-exsisting. + // Take non-existing. assert_eq!(B::take(4), None); assert_eq!(B::try_get(4), Err(())); diff --git a/substrate/frame/support/src/storage/types/counted_nmap.rs b/substrate/frame/support/src/storage/types/counted_nmap.rs index 279894ee97363b38462f83e011f8fee9f0fc233b..51cde93f28c01d8dab45da49fddb0b5eeb1c54d1 100644 --- a/substrate/frame/support/src/storage/types/counted_nmap.rs +++ b/substrate/frame/support/src/storage/types/counted_nmap.rs @@ -378,7 +378,7 @@ where /// /// # Warning /// - /// `None` does not mean that `get()` does not return a value. The default value is completly + /// `None` does not mean that `get()` does not return a value. The default value is completely /// ignored by this function. pub fn decode_len + TupleToEncodedIter>( key: KArg, diff --git a/substrate/frame/support/src/storage/types/double_map.rs b/substrate/frame/support/src/storage/types/double_map.rs index cb9479d491cff7206eb36996097e231439fd1769..2a7af7a984633ebcb79572e30595359758b50154 100644 --- a/substrate/frame/support/src/storage/types/double_map.rs +++ b/substrate/frame/support/src/storage/types/double_map.rs @@ -445,7 +445,7 @@ where /// /// # Warning /// - /// `None` does not mean that `get()` does not return a value. The default value is completly + /// `None` does not mean that `get()` does not return a value. The default value is completely /// ignored by this function. pub fn decode_len(key1: KArg1, key2: KArg2) -> Option where @@ -465,7 +465,8 @@ where /// /// # Warning /// - /// - `None` does not mean that `get()` does not return a value. The default value is completly + /// - `None` does not mean that `get()` does not return a value. The default value is + /// completely /// ignored by this function. /// /// - The value returned is the non-deduplicated length of the underlying Vector in storage.This diff --git a/substrate/frame/support/src/storage/types/map.rs b/substrate/frame/support/src/storage/types/map.rs index ee5db74583b03f8646553d7fc8c1c75042e7f68a..b79a6ae9b8482090c3f3ebe42b7b23aa8824b657 100644 --- a/substrate/frame/support/src/storage/types/map.rs +++ b/substrate/frame/support/src/storage/types/map.rs @@ -277,7 +277,7 @@ where /// /// # Warning /// - /// `None` does not mean that `get()` does not return a value. The default value is completly + /// `None` does not mean that `get()` does not return a value. The default value is completely /// ignored by this function. pub fn decode_len>(key: KeyArg) -> Option where @@ -295,7 +295,8 @@ where /// /// # Warning /// - /// - `None` does not mean that `get()` does not return a value. The default value is completly + /// - `None` does not mean that `get()` does not return a value. The default value is + /// completely /// ignored by this function. /// /// - The value returned is the non-deduplicated length of the underlying Vector in storage.This diff --git a/substrate/frame/support/src/storage/types/mod.rs b/substrate/frame/support/src/storage/types/mod.rs index 9dd6f4066e432068b97899e94017bb06fe0dc014..631410f425d17a169f16cd0380a3ffa3f22f6611 100644 --- a/substrate/frame/support/src/storage/types/mod.rs +++ b/substrate/frame/support/src/storage/types/mod.rs @@ -195,7 +195,7 @@ mod test { // result query returns error assert_eq!(C::get(), Err(())); - // value query with custom onempty returns 42 + // value query with custom on empty returns 42 assert_eq!(D::get(), 42); }); } diff --git a/substrate/frame/support/src/storage/types/nmap.rs b/substrate/frame/support/src/storage/types/nmap.rs index 0723db68900273ff729e8d7cceb4e408e18a00e3..253f02a14f0796f7ecba7e81af8e5be7b7ec0e42 100755 --- a/substrate/frame/support/src/storage/types/nmap.rs +++ b/substrate/frame/support/src/storage/types/nmap.rs @@ -348,7 +348,7 @@ where /// /// # Warning /// - /// `None` does not mean that `get()` does not return a value. The default value is completly + /// `None` does not mean that `get()` does not return a value. The default value is completely /// ignored by this function. pub fn decode_len + TupleToEncodedIter>( key: KArg, diff --git a/substrate/frame/support/src/storage/types/value.rs b/substrate/frame/support/src/storage/types/value.rs index 263091dd25237da759c54a0a7668306e5850aec9..a2d93a6a165ffca205f1597dfa0952c9c4900313 100644 --- a/substrate/frame/support/src/storage/types/value.rs +++ b/substrate/frame/support/src/storage/types/value.rs @@ -225,7 +225,7 @@ where /// /// # Warning /// - /// `None` does not mean that `get()` does not return a value. The default value is completly + /// `None` does not mean that `get()` does not return a value. The default value is completely /// ignored by this function. pub fn decode_len() -> Option where @@ -243,7 +243,8 @@ where /// /// # Warning /// - /// - `None` does not mean that `get()` does not return a value. The default value is completly + /// - `None` does not mean that `get()` does not return a value. The default value is + /// completely /// ignored by this function. /// /// - The value returned is the non-deduplicated length of the underlying Vector in storage.This diff --git a/substrate/frame/support/src/tests/mod.rs b/substrate/frame/support/src/tests/mod.rs index c63bfb181c3ff0ae56197432046dc381761f4dae..88afa243f0932e797516192f0fd1495f00cd69bf 100644 --- a/substrate/frame/support/src/tests/mod.rs +++ b/substrate/frame/support/src/tests/mod.rs @@ -227,7 +227,7 @@ crate::construct_runtime!( } ); -#[crate::derive_impl(self::frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[crate::derive_impl(self::frame_system::config_preludes::TestDefaultConfig as self::frame_system::DefaultConfig)] impl Config for Runtime { type Block = Block; type AccountId = AccountId; diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs index 3d0429f71b11d6c74f87ef33521634f46b0d591c..1997d8fc223efe7ff1d1be17e2bc10fb973d2cb9 100644 --- a/substrate/frame/support/src/traits.rs +++ b/substrate/frame/support/src/traits.rs @@ -59,10 +59,10 @@ pub use misc::{ AccountTouch, Backing, ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, ConstU16, ConstU32, ConstU64, ConstU8, DefensiveMax, DefensiveMin, DefensiveSaturating, DefensiveTruncateFrom, EnsureInherentsAreFirst, EqualPrivilegeOnly, EstimateCallFee, - ExecuteBlock, ExtrinsicCall, Get, GetBacking, GetDefault, HandleLifetime, IsSubType, IsType, - Len, OffchainWorker, OnKilledAccount, OnNewAccount, PrivilegeCmp, SameOrOther, Time, - TryCollect, TryDrop, TypedGet, UnixTime, VariantCount, VariantCountOf, WrapperKeepOpaque, - WrapperOpaque, + ExecuteBlock, ExtrinsicCall, Get, GetBacking, GetDefault, HandleLifetime, IsInherent, + IsSubType, IsType, Len, OffchainWorker, OnKilledAccount, OnNewAccount, PrivilegeCmp, + SameOrOther, Time, TryCollect, TryDrop, TypedGet, UnixTime, VariantCount, VariantCountOf, + WrapperKeepOpaque, WrapperOpaque, }; #[allow(deprecated)] pub use misc::{PreimageProvider, PreimageRecipient}; @@ -86,7 +86,8 @@ mod hooks; pub use hooks::GenesisBuild; pub use hooks::{ BeforeAllRuntimeMigrations, BuildGenesisConfig, Hooks, IntegrityTest, OnFinalize, OnGenesis, - OnIdle, OnInitialize, OnRuntimeUpgrade, OnTimestampSet, + OnIdle, OnInitialize, OnPoll, OnRuntimeUpgrade, OnTimestampSet, PostInherents, + PostTransactions, PreInherents, }; pub mod schedule; diff --git a/substrate/frame/support/src/traits/dispatch.rs b/substrate/frame/support/src/traits/dispatch.rs index de50ce7a26c211190ddcb81976d39b9779540756..7dc8d3e4f5a6e954c5eb726b257f1d2d73a29dc6 100644 --- a/substrate/frame/support/src/traits/dispatch.rs +++ b/substrate/frame/support/src/traits/dispatch.rs @@ -540,7 +540,7 @@ pub trait OriginTrait: Sized { }) } - /// Extract a reference to the sytsem origin, if that's what the caller is. + /// Extract a reference to the system origin, if that's what the caller is. fn as_system_ref(&self) -> Option<&RawOrigin> { self.caller().as_system_ref() } diff --git a/substrate/frame/support/src/traits/dynamic_params.rs b/substrate/frame/support/src/traits/dynamic_params.rs index 8881df04141cc13a97f79bc6df3ed41b8db0323d..32dae6799eaf7c9061851896ada9fe4e8a1a3e0d 100644 --- a/substrate/frame/support/src/traits/dynamic_params.rs +++ b/substrate/frame/support/src/traits/dynamic_params.rs @@ -25,30 +25,30 @@ use frame_support::Parameter; /// A dynamic parameter store across an aggregated KV type. pub trait RuntimeParameterStore { - type AggregratedKeyValue: AggregratedKeyValue; + type AggregatedKeyValue: AggregatedKeyValue; /// Get the value of a parametrized key. /// /// Should return `None` if no explicit value was set instead of a default. fn get(key: K) -> Option where - KV: AggregratedKeyValue, - K: Key + Into<::Key>, - ::Key: IntoKey< - <::AggregratedKeyValue as AggregratedKeyValue>::Key, + KV: AggregatedKeyValue, + K: Key + Into<::Key>, + ::Key: IntoKey< + <::AggregatedKeyValue as AggregatedKeyValue>::Key, >, - <::AggregratedKeyValue as AggregratedKeyValue>::Value: - TryIntoKey<::Value>, - ::Value: TryInto; + <::AggregatedKeyValue as AggregatedKeyValue>::Value: + TryIntoKey<::Value>, + ::Value: TryInto; } /// A dynamic parameter store across a concrete KV type. -pub trait ParameterStore { +pub trait ParameterStore { /// Get the value of a parametrized key. fn get(key: K) -> Option where - K: Key + Into<::Key>, - ::Value: TryInto; + K: Key + Into<::Key>, + ::Value: TryInto; } /// Key of a dynamic parameter. @@ -61,7 +61,7 @@ pub trait Key { } /// The aggregated key-value type of a dynamic parameter store. -pub trait AggregratedKeyValue: Parameter { +pub trait AggregatedKeyValue: Parameter { /// The aggregated key type. type Key: Parameter + MaxEncodedLen; @@ -72,7 +72,7 @@ pub trait AggregratedKeyValue: Parameter { fn into_parts(self) -> (Self::Key, Option); } -impl AggregratedKeyValue for () { +impl AggregatedKeyValue for () { type Key = (); type Value = (); @@ -90,17 +90,17 @@ pub struct ParameterStoreAdapter(sp_std::marker::PhantomData<(PS, KV)>); impl ParameterStore for ParameterStoreAdapter where PS: RuntimeParameterStore, - KV: AggregratedKeyValue, - ::Key: - IntoKey<<::AggregratedKeyValue as AggregratedKeyValue>::Key>, - ::Value: TryFromKey< - <::AggregratedKeyValue as AggregratedKeyValue>::Value, + KV: AggregatedKeyValue, + ::Key: + IntoKey<<::AggregatedKeyValue as AggregatedKeyValue>::Key>, + ::Value: TryFromKey< + <::AggregatedKeyValue as AggregatedKeyValue>::Value, >, { fn get(key: K) -> Option where - K: Key + Into<::Key>, - ::Value: TryInto, + K: Key + Into<::Key>, + ::Value: TryInto, { PS::get::(key) } diff --git a/substrate/frame/support/src/traits/hooks.rs b/substrate/frame/support/src/traits/hooks.rs index 20788ce932d8b397990767c6ae8a0d82016cccbd..d83e270474581ce2ca0aee93112a3b535a9271e3 100644 --- a/substrate/frame/support/src/traits/hooks.rs +++ b/substrate/frame/support/src/traits/hooks.rs @@ -25,10 +25,74 @@ use crate::weights::Weight; use impl_trait_for_tuples::impl_for_tuples; use sp_runtime::traits::AtLeast32BitUnsigned; use sp_std::prelude::*; +use sp_weights::WeightMeter; #[cfg(feature = "try-runtime")] use sp_runtime::TryRuntimeError; +/// Provides a callback to execute logic before the all inherents. +pub trait PreInherents { + /// Called before all inherents were applied but after `on_initialize`. + fn pre_inherents() {} +} + +#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))] +#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))] +#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))] +impl PreInherents for Tuple { + fn pre_inherents() { + for_tuples!( #( Tuple::pre_inherents(); )* ); + } +} + +/// Provides a callback to execute logic after the all inherents. +pub trait PostInherents { + /// Called after all inherents were applied. + fn post_inherents() {} +} + +#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))] +#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))] +#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))] +impl PostInherents for Tuple { + fn post_inherents() { + for_tuples!( #( Tuple::post_inherents(); )* ); + } +} + +/// Provides a callback to execute logic before the all transactions. +pub trait PostTransactions { + /// Called after all transactions were applied but before `on_finalize`. + fn post_transactions() {} +} + +#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))] +#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))] +#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))] +impl PostTransactions for Tuple { + fn post_transactions() { + for_tuples!( #( Tuple::post_transactions(); )* ); + } +} + +/// Periodically executes logic. Is not guaranteed to run within a specific timeframe and should +/// only be used on logic that has no deadline. +pub trait OnPoll { + /// Code to execute every now and then at the beginning of the block after inherent application. + /// + /// The remaining weight limit must be respected. + fn on_poll(_n: BlockNumber, _weight: &mut WeightMeter) {} +} + +#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))] +#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))] +#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))] +impl OnPoll for Tuple { + fn on_poll(n: BlockNumber, weight: &mut WeightMeter) { + for_tuples!( #( Tuple::on_poll(n.clone(), weight); )* ); + } +} + /// See [`Hooks::on_initialize`]. pub trait OnInitialize { /// See [`Hooks::on_initialize`]. @@ -90,7 +154,7 @@ impl OnIdle for Tuple { /// /// Implementing this trait for a pallet let's you express operations that should /// happen at genesis. It will be called in an externalities provided environment and -/// will see the genesis state after all pallets have written their genesis state. +/// will set the genesis state after all pallets have written their genesis state. #[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))] #[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))] #[cfg_attr(feature = "tuples-128", impl_for_tuples(128))] @@ -103,7 +167,7 @@ pub trait OnGenesis { /// /// This hook is intended to be used internally in FRAME and not be exposed to FRAME developers. /// -/// It is defined as a seperate trait from [`OnRuntimeUpgrade`] precisely to not pollute the public +/// It is defined as a separate trait from [`OnRuntimeUpgrade`] precisely to not pollute the public /// API. pub trait BeforeAllRuntimeMigrations { /// Something that should happen before runtime migrations are executed. @@ -306,19 +370,23 @@ pub trait IntegrityTest { /// end /// ``` /// -/// * `OnRuntimeUpgrade` is only executed before everything else if a code -/// * `OnRuntimeUpgrade` is mandatorily at the beginning of the block body (extrinsics) being -/// processed. change is detected. -/// * Extrinsics start with inherents, and continue with other signed or unsigned extrinsics. -/// * `OnIdle` optionally comes after extrinsics. -/// `OnFinalize` mandatorily comes after `OnIdle`. +/// * [`OnRuntimeUpgrade`](Hooks::OnRuntimeUpgrade) hooks are only executed when a code change is +/// detected. +/// * [`OnRuntimeUpgrade`](Hooks::OnRuntimeUpgrade) hooks are mandatorily executed at the very +/// beginning of the block body, before any extrinsics are processed. +/// * [`Inherents`](sp_inherents) are always executed before any other other signed or unsigned +/// extrinsics. +/// * [`OnIdle`](Hooks::OnIdle) hooks are executed after extrinsics if there is weight remaining in +/// the block. +/// * [`OnFinalize`](Hooks::OnFinalize) hooks are mandatorily executed after +/// [`OnIdle`](Hooks::OnIdle). /// -/// > `OffchainWorker` is not part of this flow, as it is not really part of the consensus/main -/// > block import path, and is called optionally, and in other circumstances. See -/// > [`crate::traits::misc::OffchainWorker`] for more information. +/// > [`OffchainWorker`](crate::traits::misc::OffchainWorker) hooks are not part of this flow, +/// > because they are not part of the consensus/main block building logic. See +/// > [`OffchainWorker`](crate::traits::misc::OffchainWorker) for more information. /// -/// To learn more about the execution of hooks see `frame-executive` as this component is is charge -/// of dispatching extrinsics and placing the hooks in the correct order. +/// To learn more about the execution of hooks see the FRAME `Executive` pallet which is in charge +/// of dispatching extrinsics and calling hooks in the correct order. pub trait Hooks { /// Block initialization hook. This is called at the very beginning of block execution. /// @@ -370,30 +438,44 @@ pub trait Hooks { Weight::zero() } - /// Hook executed when a code change (aka. a "runtime upgrade") is detected by FRAME. + /// A hook to run logic after inherent application. + /// + /// Is not guaranteed to execute in a block and should therefore only be used in no-deadline + /// scenarios. + fn on_poll(_n: BlockNumber, _weight: &mut WeightMeter) {} + + /// Hook executed when a code change (aka. a "runtime upgrade") is detected by the FRAME + /// `Executive` pallet. /// /// Be aware that this is called before [`Hooks::on_initialize`] of any pallet; therefore, a lot /// of the critical storage items such as `block_number` in system pallet might have not been - /// set. + /// set yet. /// - /// Vert similar to [`Hooks::on_initialize`], any code in this block is mandatory and MUST - /// execute. Use with care. + /// Similar to [`Hooks::on_initialize`], any code in this block is mandatory and MUST execute. + /// It is strongly recommended to dry-run the execution of these hooks using + /// [try-runtime-cli](https://github.com/paritytech/try-runtime-cli) to ensure they will not + /// produce and overweight block which can brick your chain. Use with care! /// - /// ## Implementation Note: Versioning + /// ## Implementation Note: Standalone Migrations /// - /// 1. An implementation of this should typically follow a pattern where the version of the - /// pallet is checked against the onchain version, and a decision is made about what needs to be - /// done. This is helpful to prevent accidental repetitive execution of this hook, which can be - /// catastrophic. + /// Additional migrations can be created by directly implementing [`OnRuntimeUpgrade`] on + /// structs and passing them to `Executive`. /// - /// Alternatively, [`frame_support::migrations::VersionedMigration`] can be used to assist with - /// this. + /// ## Implementation Note: Pallet Versioning /// - /// ## Implementation Note: Runtime Level Migration + /// Implementations of this hook are typically wrapped in + /// [`crate::migrations::VersionedMigration`] to ensure the migration is executed exactly + /// once and only when it is supposed to. /// - /// Additional "upgrade hooks" can be created by pallets by a manual implementation of - /// [`Hooks::on_runtime_upgrade`] which can be passed on to `Executive` at the top level - /// runtime. + /// Alternatively, developers can manually implement version checks. + /// + /// Failure to adequately check storage versions can result in accidental repetitive execution + /// of the hook, which can be catastrophic. + /// + /// ## Implementation Note: Weight + /// + /// Typically, implementations of this method are simple enough that weights can be calculated + /// manually. However, if required, a benchmark can also be used. fn on_runtime_upgrade() -> Weight { Weight::zero() } @@ -403,7 +485,7 @@ pub trait Hooks { /// It should focus on certain checks to ensure that the state is sensible. This is never /// executed in a consensus code-path, therefore it can consume as much weight as it needs. /// - /// This hook should not alter any storage. + /// This hook must not alter any storage. #[cfg(feature = "try-runtime")] fn try_state(_n: BlockNumber) -> Result<(), TryRuntimeError> { Ok(()) @@ -415,7 +497,7 @@ pub trait Hooks { /// which will be passed to `post_upgrade` after upgrading for post-check. An empty vector /// should be returned if there is no such need. /// - /// This hook is never meant to be executed on-chain but is meant to be used by testing tools. + /// This hook is never executed on-chain but instead used by testing tools. #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { Ok(Vec::new()) @@ -434,7 +516,7 @@ pub trait Hooks { } /// Implementing this function on a pallet allows you to perform long-running tasks that are - /// dispatched as separate threads, and entirely independent of the main wasm runtime. + /// dispatched as separate threads, and entirely independent of the main blockchain execution. /// /// This function can freely read from the state, but any change it makes to the state is /// meaningless. Writes can be pushed back to the chain by submitting extrinsics from the diff --git a/substrate/frame/support/src/traits/messages.rs b/substrate/frame/support/src/traits/messages.rs index 995ac4f717911195e4dba202d350c6cbc09ae340..f3d893bcc1d899fce06ef00a38d687bfb3ea0181 100644 --- a/substrate/frame/support/src/traits/messages.rs +++ b/substrate/frame/support/src/traits/messages.rs @@ -123,6 +123,8 @@ impl ServiceQueues for NoopServiceQueues { pub struct QueueFootprint { /// The number of pages in the queue (including overweight pages). pub pages: u32, + /// The number of pages that are ready (not yet processed and also not overweight). + pub ready_pages: u32, /// The storage footprint of the queue (including overweight messages). pub storage: Footprint, } diff --git a/substrate/frame/support/src/traits/metadata.rs b/substrate/frame/support/src/traits/metadata.rs index 586af20511a89f82caf33297177a12f6d0988bf4..8bda4186bc967b29a6342ea96dd0a1cdc5072438 100644 --- a/substrate/frame/support/src/traits/metadata.rs +++ b/substrate/frame/support/src/traits/metadata.rs @@ -261,41 +261,64 @@ impl Add for StorageVersion { } } -/// Special marker struct if no storage version is set for a pallet. +/// Special marker struct used when [`storage_version`](crate::pallet_macros::storage_version) is +/// not defined for a pallet. /// /// If you (the reader) end up here, it probably means that you tried to compare /// [`GetStorageVersion::on_chain_storage_version`] against -/// [`GetStorageVersion::current_storage_version`]. This basically means that the -/// [`storage_version`](crate::pallet_macros::storage_version) is missing in the pallet where the -/// mentioned functions are being called. +/// [`GetStorageVersion::in_code_storage_version`]. This basically means that the +/// [`storage_version`](crate::pallet_macros::storage_version) is missing from the pallet where the +/// mentioned functions are being called, and needs to be defined. #[derive(Debug, Default)] pub struct NoStorageVersionSet; -/// Provides information about the storage version of a pallet. +/// Provides information about a pallet's storage versions. /// -/// It differentiates between current and on-chain storage version. Both should be only out of sync -/// when a new runtime upgrade was applied and the runtime migrations did not yet executed. -/// Otherwise it means that the pallet works with an unsupported storage version and unforeseen -/// stuff can happen. +/// Every pallet has two storage versions: +/// 1. An in-code storage version +/// 2. An on-chain storage version /// -/// The current storage version is the version of the pallet as supported at runtime. The active -/// storage version is the version of the pallet in the storage. +/// The in-code storage version is the version of the pallet as defined in the runtime blob, and the +/// on-chain storage version is the version of the pallet stored on-chain. /// -/// It is required to update the on-chain storage version manually when a migration was applied. +/// Storage versions should be only ever be out of sync when a pallet has been updated to a new +/// version and the in-code version is incremented, but the migration has not yet been executed +/// on-chain as part of a runtime upgrade. +/// +/// It is the responsibility of the developer to ensure that the on-chain storage version is set +/// correctly during a migration so that it matches the in-code storage version. pub trait GetStorageVersion { - /// This will be filled out by the [`pallet`](crate::pallet) macro. + /// This type is generated by the [`pallet`](crate::pallet) macro. + /// + /// If the [`storage_version`](crate::pallet_macros::storage_version) attribute isn't specified, + /// this is set to [`NoStorageVersionSet`] to signify that it is missing. + /// + /// If the [`storage_version`](crate::pallet_macros::storage_version) attribute is specified, + /// this is be set to a [`StorageVersion`] corresponding to the attribute. /// - /// If the [`storage_version`](crate::pallet_macros::storage_version) attribute isn't given - /// this is set to [`NoStorageVersionSet`] to inform the user that the attribute is missing. - /// This should prevent that the user forgets to set a storage version when required. However, - /// this will only work when the user actually tries to call [`Self::current_storage_version`] - /// to compare it against the [`Self::on_chain_storage_version`]. If the attribute is given, - /// this will be set to [`StorageVersion`]. - type CurrentStorageVersion; - - /// Returns the current storage version as supported by the pallet. - fn current_storage_version() -> Self::CurrentStorageVersion; - /// Returns the on-chain storage version of the pallet as stored in the storage. + /// The intention of using [`NoStorageVersionSet`] instead of defaulting to a [`StorageVersion`] + /// of zero is to prevent developers from forgetting to set + /// [`storage_version`](crate::pallet_macros::storage_version) when it is required, like in the + /// case that they wish to compare the in-code storage version to the on-chain storage version. + type InCodeStorageVersion; + + #[deprecated( + note = "This method has been renamed to `in_code_storage_version` and will be removed after March 2024." + )] + /// DEPRECATED: Use [`Self::current_storage_version`] instead. + /// + /// Returns the in-code storage version as specified in the + /// [`storage_version`](crate::pallet_macros::storage_version) attribute, or + /// [`NoStorageVersionSet`] if the attribute is missing. + fn current_storage_version() -> Self::InCodeStorageVersion { + Self::in_code_storage_version() + } + + /// Returns the in-code storage version as specified in the + /// [`storage_version`](crate::pallet_macros::storage_version) attribute, or + /// [`NoStorageVersionSet`] if the attribute is missing. + fn in_code_storage_version() -> Self::InCodeStorageVersion; + /// Returns the storage version of the pallet as last set in the actual on-chain storage. fn on_chain_storage_version() -> StorageVersion; } diff --git a/substrate/frame/support/src/traits/misc.rs b/substrate/frame/support/src/traits/misc.rs index eafd9c8abdd2dfc4b4174723678d94aa963ad738..bc7407a7be6248f25866a78f3b95bb0107ff19ae 100644 --- a/substrate/frame/support/src/traits/misc.rs +++ b/substrate/frame/support/src/traits/misc.rs @@ -23,6 +23,7 @@ use impl_trait_for_tuples::impl_for_tuples; use scale_info::{build::Fields, meta_type, Path, Type, TypeInfo, TypeParameter}; use sp_arithmetic::traits::{CheckedAdd, CheckedMul, CheckedSub, One, Saturating}; use sp_core::bounded::bounded_vec::TruncateFrom; + #[doc(hidden)] pub use sp_runtime::traits::{ ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, ConstU16, ConstU32, @@ -895,11 +896,21 @@ pub trait GetBacking { /// A trait to ensure the inherent are before non-inherent in a block. /// /// This is typically implemented on runtime, through `construct_runtime!`. -pub trait EnsureInherentsAreFirst { +pub trait EnsureInherentsAreFirst: + IsInherent<::Extrinsic> +{ /// Ensure the position of inherent is correct, i.e. they are before non-inherents. /// - /// On error return the index of the inherent with invalid position (counting from 0). - fn ensure_inherents_are_first(block: &Block) -> Result<(), u32>; + /// On error return the index of the inherent with invalid position (counting from 0). On + /// success it returns the index of the last inherent. `0` therefore means that there are no + /// inherents. + fn ensure_inherents_are_first(block: &Block) -> Result; +} + +/// A trait to check if an extrinsic is an inherent. +pub trait IsInherent { + /// Whether this extrinsic is an inherent. + fn is_inherent(ext: &Extrinsic) -> bool; } /// An extrinsic on which we can get access to call. @@ -1413,7 +1424,7 @@ mod test { assert_eq!(>::max_encoded_len(), 2usize.pow(14) + 4); let data = 4u64; - // Ensure that we check that the `Vec` is consumed completly on decode. + // Ensure that we check that the `Vec` is consumed completely on decode. assert!(WrapperOpaque::::decode(&mut &data.encode().encode()[..]).is_err()); } diff --git a/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/unbalanced.rs b/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/unbalanced.rs index e7fcc15472e05b0600f5bfda2f6a1d524a8afbc3..a522073689879385941cfaeebe25675d3b253127 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/unbalanced.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/conformance_tests/regular/unbalanced.rs @@ -94,7 +94,7 @@ where ); assert_eq!(T::balance(&account_0), account_0_initial_balance - amount); - // Decreasing the balance below funds avalibale should fail when Precision::Exact + // Decreasing the balance below funds available should fail when Precision::Exact let balance_before = T::balance(&account_0); assert_eq!( T::decrease_balance( diff --git a/substrate/frame/support/src/traits/tokens/imbalance/split_two_ways.rs b/substrate/frame/support/src/traits/tokens/imbalance/split_two_ways.rs index c1afac35fc93c4ab9263a190c0adca44adad3ded..59a582389ba61f3eb65fbc7f3710b1edba0a56ab 100644 --- a/substrate/frame/support/src/traits/tokens/imbalance/split_two_ways.rs +++ b/substrate/frame/support/src/traits/tokens/imbalance/split_two_ways.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Means for splitting an imbalance into two and hanlding them differently. +//! Means for splitting an imbalance into two and handling them differently. use super::super::imbalance::{Imbalance, OnUnbalanced}; use sp_runtime::traits::Saturating; diff --git a/substrate/frame/support/src/traits/tokens/misc.rs b/substrate/frame/support/src/traits/tokens/misc.rs index fd497bc4eda6f0b66127b4fc87edacd9be76d56f..a4dd5e4914283e6da028aac038cecee501a2228f 100644 --- a/substrate/frame/support/src/traits/tokens/misc.rs +++ b/substrate/frame/support/src/traits/tokens/misc.rs @@ -130,7 +130,7 @@ impl WithdrawConsequence { pub enum DepositConsequence { /// Deposit couldn't happen due to the amount being too low. This is usually because the /// account doesn't yet exist and the deposit wouldn't bring it to at least the minimum needed - /// for existance. + /// for existence. BelowMinimum, /// Deposit cannot happen since the account cannot be created (usually because it's a consumer /// and there exists no provider reference). diff --git a/substrate/frame/support/test/Cargo.toml b/substrate/frame/support/test/Cargo.toml index ae2c56a531fd428e712a0825153afc7c89185c78..2f12cc00ed9e08560870e6f62b1d1930bad90832 100644 --- a/substrate/frame/support/test/Cargo.toml +++ b/substrate/frame/support/test/Cargo.toml @@ -24,7 +24,7 @@ sp-api = { path = "../../../primitives/api", default-features = false } sp-arithmetic = { path = "../../../primitives/arithmetic", default-features = false } sp-io = { path = "../../../primitives/io", default-features = false } sp-state-machine = { path = "../../../primitives/state-machine", optional = true } -frame-support = { path = "..", default-features = false } +frame-support = { path = "..", default-features = false, features = ["experimental"] } frame-benchmarking = { path = "../../benchmarking", default-features = false } sp-runtime = { path = "../../../primitives/runtime", default-features = false } sp-core = { path = "../../../primitives/core", default-features = false } diff --git a/substrate/frame/support/test/compile_pass/src/lib.rs b/substrate/frame/support/test/compile_pass/src/lib.rs index 575322df7604fe54f1194269c570653f5f141cbc..07d2f7d9ecdbe803139f6017d71f06bc278adc6a 100644 --- a/substrate/frame/support/test/compile_pass/src/lib.rs +++ b/substrate/frame/support/test/compile_pass/src/lib.rs @@ -51,7 +51,7 @@ parameter_types! { pub const Version: RuntimeVersion = VERSION; } -#[derive_impl(renamed_frame_system::config_preludes::TestDefaultConfig as renamed_frame_system::DefaultConfig)] +#[derive_impl(renamed_frame_system::config_preludes::TestDefaultConfig)] impl renamed_frame_system::Config for Runtime { type BaseCallFilter = Everything; type BlockWeights = (); diff --git a/substrate/frame/support/test/stg_frame_crate/src/lib.rs b/substrate/frame/support/test/stg_frame_crate/src/lib.rs index 59a66851f237c19ea8f95b851bf36518914b49bb..dc5fff65510e6532b8c8a9e22d82b369e00aa775 100644 --- a/substrate/frame/support/test/stg_frame_crate/src/lib.rs +++ b/substrate/frame/support/test/stg_frame_crate/src/lib.rs @@ -60,7 +60,7 @@ mod tests { impl crate::pallet::Config for Runtime {} - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type Block = Block; } diff --git a/substrate/frame/contracts/fixtures/contracts/invalid_contract_no_call.rs b/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_1.rs similarity index 80% rename from substrate/frame/contracts/fixtures/contracts/invalid_contract_no_call.rs rename to substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_1.rs index 13af3eb22b1f5fd10c3e2b60061d3f7a6cc37c94..40ef884bf8577aeace86fe8f6225d18f0efde727 100644 --- a/substrate/frame/contracts/fixtures/contracts/invalid_contract_no_call.rs +++ b/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_1.rs @@ -14,12 +14,18 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -//! Valid module but missing the call function -#![no_std] -#![no_main] -extern crate common; +use frame_benchmarking::v2::*; -#[no_mangle] -#[polkavm_derive::polkavm_export] -pub extern "C" fn deploy() {} +#[benchmarks] +mod benches { + use super::*; + + #[benchmark(pov_mode = Wrong)] + fn bench() { + #[block] + {} + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_1.stderr b/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_1.stderr new file mode 100644 index 0000000000000000000000000000000000000000..add80da63070e47b1ce46aba23359e2e9cbd4645 --- /dev/null +++ b/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_1.stderr @@ -0,0 +1,5 @@ +error: expected one of: `MaxEncodedLen`, `Measured`, `Ignored` + --> tests/benchmark_ui/bad_attr_pov_mode_1.rs:24:25 + | +24 | #[benchmark(pov_mode = Wrong)] + | ^^^^^ diff --git a/substrate/frame/support/test/tests/pallet_ui/deprecated_store_attr.rs b/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_2.rs similarity index 76% rename from substrate/frame/support/test/tests/pallet_ui/deprecated_store_attr.rs rename to substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_2.rs index 72ad7896dfec572014cb6b10dcc39bc417a275b6..151bb931e9204b17bb3789869df0e4ab96594848 100644 --- a/substrate/frame/support/test/tests/pallet_ui/deprecated_store_attr.rs +++ b/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_2.rs @@ -15,14 +15,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -#[frame_support::pallet] -mod pallet { - #[pallet::config] - pub trait Config: frame_system::Config {} +use frame_benchmarking::v2::*; - #[pallet::pallet] - #[pallet::generate_store(trait Store)] - pub struct Pallet(core::marker::PhantomData); +#[benchmarks] +mod benches { + use super::*; + + #[benchmark(pov_mode = Measured { + Key: Wrong + })] + fn bench() { + #[block] + {} + } } fn main() {} diff --git a/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_2.stderr b/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_2.stderr new file mode 100644 index 0000000000000000000000000000000000000000..0f9961afd89fad0d7e7022f890a89db90ff69582 --- /dev/null +++ b/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_2.stderr @@ -0,0 +1,5 @@ +error: expected one of: `MaxEncodedLen`, `Measured`, `Ignored` + --> tests/benchmark_ui/bad_attr_pov_mode_2.rs:25:8 + | +25 | Key: Wrong + | ^^^^^ diff --git a/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_3.rs b/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_3.rs new file mode 100644 index 0000000000000000000000000000000000000000..9c5e3801b1a30d5b88662e9f691d18eccf1349ef --- /dev/null +++ b/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_3.rs @@ -0,0 +1,31 @@ +// 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. + +use frame_benchmarking::v2::*; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark(pov_mode)] + fn bench() { + #[block] + {} + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_3.stderr b/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_3.stderr new file mode 100644 index 0000000000000000000000000000000000000000..f28a993989a35a22e191c9b75cb4dc37725ffb99 --- /dev/null +++ b/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_3.stderr @@ -0,0 +1,5 @@ +error: expected `=` + --> tests/benchmark_ui/bad_attr_pov_mode_3.rs:24:22 + | +24 | #[benchmark(pov_mode)] + | ^ diff --git a/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_4.rs b/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_4.rs new file mode 100644 index 0000000000000000000000000000000000000000..11ec5124d289dc83a1d9526e7b21df3bcffebf41 --- /dev/null +++ b/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_4.rs @@ -0,0 +1,31 @@ +// 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. + +use frame_benchmarking::v2::*; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark(pov_mode =)] + fn bench() { + #[block] + {} + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_4.stderr b/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_4.stderr new file mode 100644 index 0000000000000000000000000000000000000000..572b6b0815dc1f93590b8b1505a94f2450a39953 --- /dev/null +++ b/substrate/frame/support/test/tests/benchmark_ui/bad_attr_pov_mode_4.stderr @@ -0,0 +1,5 @@ +error: unexpected end of input, expected one of: `MaxEncodedLen`, `Measured`, `Ignored` + --> tests/benchmark_ui/bad_attr_pov_mode_4.rs:24:24 + | +24 | #[benchmark(pov_mode =)] + | ^ diff --git a/substrate/frame/support/test/tests/benchmark_ui/dup_attr_pov_mode.rs b/substrate/frame/support/test/tests/benchmark_ui/dup_attr_pov_mode.rs new file mode 100644 index 0000000000000000000000000000000000000000..f49636d181a5b07c4e51e0bd9096fd2465fbcea9 --- /dev/null +++ b/substrate/frame/support/test/tests/benchmark_ui/dup_attr_pov_mode.rs @@ -0,0 +1,32 @@ +// 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. + +use frame_benchmarking::v2::*; +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark(pov_mode = Measured, pov_mode = MaxEncodedLen)] + fn bench() { + #[block] + {} + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/benchmark_ui/dup_attr_pov_mode.stderr b/substrate/frame/support/test/tests/benchmark_ui/dup_attr_pov_mode.stderr new file mode 100644 index 0000000000000000000000000000000000000000..aab91d271a694309ad7ea293eb1adc19ff7a03b5 --- /dev/null +++ b/substrate/frame/support/test/tests/benchmark_ui/dup_attr_pov_mode.stderr @@ -0,0 +1,14 @@ +error: unexpected end of input, `pov_mode` can only be specified once + --> tests/benchmark_ui/dup_attr_pov_mode.rs:25:59 + | +25 | #[benchmark(pov_mode = Measured, pov_mode = MaxEncodedLen)] + | ^ + +error: unused import: `frame_support_test::Config` + --> tests/benchmark_ui/dup_attr_pov_mode.rs:19:5 + | +19 | use frame_support_test::Config; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D unused-imports` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unused_imports)]` diff --git a/substrate/frame/support/test/tests/benchmark_ui/pass/valid_attr_pov_mode.rs b/substrate/frame/support/test/tests/benchmark_ui/pass/valid_attr_pov_mode.rs new file mode 100644 index 0000000000000000000000000000000000000000..35fa1e76ae5ad8852686d7e8810f061675895e2e --- /dev/null +++ b/substrate/frame/support/test/tests/benchmark_ui/pass/valid_attr_pov_mode.rs @@ -0,0 +1,74 @@ +// 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. + +use frame_benchmarking::v2::*; +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark(skip_meta, extra, pov_mode = Measured)] + fn bench1() { + #[block] + {} + } + + #[benchmark(pov_mode = Measured, extra, skip_meta)] + fn bench2() { + #[block] + {} + } + + #[benchmark(extra, pov_mode = Measured { + Pallet: Measured, + Pallet::Storage: MaxEncodedLen, + }, skip_meta)] + fn bench3() { + #[block] + {} + } + + #[benchmark(skip_meta, extra, pov_mode = Measured { + Pallet::Storage: MaxEncodedLen, + Pallet::StorageSubKey: Measured, + })] + fn bench4() { + #[block] + {} + } + + #[benchmark(pov_mode = MaxEncodedLen { + Pallet::Storage: Measured, + Pallet::StorageSubKey: Measured + }, extra, skip_meta)] + fn bench5() { + #[block] + {} + } + + #[benchmark(pov_mode = MaxEncodedLen { + Pallet::Storage: Measured, + Pallet::Storage::Nested: Ignored + }, extra, skip_meta)] + fn bench6() { + #[block] + {} + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs b/substrate/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs index 126cee8fa6c535f5651e083e181d58ffa13c1e73..5899eb3562a2a1791f05be7ec6e77d3d01aa2cd1 100644 --- a/substrate/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs +++ b/substrate/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs @@ -22,7 +22,7 @@ use frame_support_test::Config; mod benches { use super::*; - #[benchmark(skip_meta, extra)] + #[benchmark(skip_meta, pov_mode = Measured, extra)] fn bench() { let a = 2 + 2; #[block] diff --git a/substrate/frame/support/test/tests/benchmark_ui/unrecognized_option.stderr b/substrate/frame/support/test/tests/benchmark_ui/unrecognized_option.stderr index bea770b634e25495a9207be51d41c363848fdc1f..2eb06e396a8582c01df93e84572c51d001fe434e 100644 --- a/substrate/frame/support/test/tests/benchmark_ui/unrecognized_option.stderr +++ b/substrate/frame/support/test/tests/benchmark_ui/unrecognized_option.stderr @@ -1,4 +1,4 @@ -error: expected `extra` or `skip_meta` +error: expected one of: `extra`, `skip_meta`, `pov_mode` --> tests/benchmark_ui/unrecognized_option.rs:26:32 | 26 | #[benchmark(skip_meta, extra, bad)] diff --git a/substrate/frame/support/test/tests/composite_enum.rs b/substrate/frame/support/test/tests/composite_enum.rs index b9e9c23c4bca8ffb1e1a93d4800d040e010bbb13..1f937705823b38da77a989b2ba14d27570556145 100644 --- a/substrate/frame/support/test/tests/composite_enum.rs +++ b/substrate/frame/support/test/tests/composite_enum.rs @@ -114,7 +114,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type Block = Block; } diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/deprecated_where_block.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/deprecated_where_block.stderr index 61cfc07caedc37e1a8427e8fadc61406813c52e6..09c4d290ef5c3d49d244648242e6324d4cf760e5 100644 --- a/substrate/frame/support/test/tests/construct_runtime_ui/deprecated_where_block.stderr +++ b/substrate/frame/support/test/tests/construct_runtime_ui/deprecated_where_block.stderr @@ -347,7 +347,7 @@ error[E0277]: the trait bound `Runtime: Config` is not satisfied 26 | System: frame_system::{Pallet, Call, Storage, Config, Event}, | ^^^^^^ the trait `Config` is not implemented for `Runtime` | -note: required by a bound in `frame_system::GenesisConfig` +note: required by a bound in `GenesisConfig` --> $WORKSPACE/substrate/frame/system/src/lib.rs | | pub struct GenesisConfig { diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs b/substrate/frame/support/test/tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs index 78ae6f57f087c4a652a75b8636c4cbd15ab7b35f..26110810250c77ca7aaa87b881524ac2752319b7 100644 --- a/substrate/frame/support/test/tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs +++ b/substrate/frame/support/test/tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs @@ -36,7 +36,7 @@ pub type UncheckedExtrinsic = generic::UncheckedExtrinsic tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:35:64 - | -35 | pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; - | ^^^^^^^^^^^ not found in this scope - | -help: you might be missing a type parameter - | -35 | pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; - | +++++++++++++ - -error[E0412]: cannot find type `Runtime` in this scope - --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:37:25 - | -37 | impl pallet::Config for Runtime {} - | ^^^^^^^ not found in this scope - -error[E0412]: cannot find type `Runtime` in this scope - --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:40:31 - | -40 | impl frame_system::Config for Runtime { - | ^^^^^^^ not found in this scope - -error[E0412]: cannot find type `RuntimeOrigin` in this scope - --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:42:23 - | -42 | type RuntimeOrigin = RuntimeOrigin; - | ^^^^^^^^^^^^^ - | -help: you might have meant to use the associated type - | -42 | type RuntimeOrigin = Self::RuntimeOrigin; - | ++++++ - -error[E0412]: cannot find type `RuntimeCall` in this scope - --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:44:21 - | -44 | type RuntimeCall = RuntimeCall; - | ^^^^^^^^^^^ - | -help: you might have meant to use the associated type - | -44 | type RuntimeCall = Self::RuntimeCall; - | ++++++ - -error[E0412]: cannot find type `RuntimeEvent` in this scope - --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:50:22 - | -50 | type RuntimeEvent = RuntimeEvent; - | ^^^^^^^^^^^^ - | -help: you might have meant to use the associated type - | -50 | type RuntimeEvent = Self::RuntimeEvent; - | ++++++ - -error[E0412]: cannot find type `PalletInfo` in this scope - --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:56:20 - | -56 | type PalletInfo = PalletInfo; - | ^^^^^^^^^^ - | -help: you might have meant to use the associated type - | -56 | type PalletInfo = Self::PalletInfo; - | ++++++ -help: consider importing one of these items - | -18 + use frame_benchmarking::__private::traits::PalletInfo; - | -18 + use frame_support::traits::PalletInfo; - | - -error[E0412]: cannot find type `RuntimeTask` in this scope - --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:39:1 - | -39 | #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `frame_system::config_preludes::TestDefaultConfig` which comes from the expansion of the macro `frame_support::macro_magic::forward_tokens_verbatim` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you might have meant to use the associated type - --> $WORKSPACE/substrate/frame/system/src/lib.rs - | - | type Self::RuntimeTask = (); - | ++++++ +error: recursion limit reached while expanding `frame_support::__private::tt_return!` + --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:22:1 + | +22 | / #[frame_support::pallet] +23 | | mod pallet { +24 | | #[pallet::config] +25 | | pub trait Config: frame_system::Config {} +... | +66 | |/ construct_runtime! { +67 | || pub struct Runtime +68 | || { +69 | || System: frame_system::{Pallet, Call, Storage, Config, Event}, +... || +180 | || } +181 | || } + | ||_^ + | |_| + | in this macro invocation + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`$CRATE`) + = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/pallet_error_too_large.rs b/substrate/frame/support/test/tests/construct_runtime_ui/pallet_error_too_large.rs index d3e519af55150818c6073d7b35f845226ae1edf3..3ec1d5669618fc1a76afe195dbce9cac135e1035 100644 --- a/substrate/frame/support/test/tests/construct_runtime_ui/pallet_error_too_large.rs +++ b/substrate/frame/support/test/tests/construct_runtime_ui/pallet_error_too_large.rs @@ -61,7 +61,7 @@ pub type UncheckedExtrinsic = generic::UncheckedExtrinsic` | = help: items from traits can only be used if the trait is implemented and in scope - = note: the following trait defines an item `is_inherent`, perhaps you need to implement it: + = note: the following traits define an item `is_inherent`, perhaps you need to implement one of them: candidate #1: `ProvideInherent` + candidate #2: `IsInherent` = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0599]: no function or associated item named `check_inherent` found for struct `pallet::Pallet` in the current scope @@ -119,3 +120,23 @@ error[E0599]: no function or associated item named `is_inherent_required` found = note: the following trait defines an item `is_inherent_required`, perhaps you need to implement it: candidate #1: `ProvideInherent` = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `pallet::Pallet: ProvideInherent` is not satisfied + --> tests/construct_runtime_ui/undefined_inherent_part.rs:70:3 + | +70 | Pallet: pallet expanded::{}::{Pallet, Inherent}, + | ^^^^^^ the trait `ProvideInherent` is not implemented for `pallet::Pallet` + +error[E0277]: the trait bound `pallet::Pallet: ProvideInherent` is not satisfied + --> tests/construct_runtime_ui/undefined_inherent_part.rs:66:1 + | +66 | / construct_runtime! { +67 | | pub struct Runtime +68 | | { +69 | | System: frame_system expanded::{}::{Pallet, Call, Storage, Config, Event}, +70 | | Pallet: pallet expanded::{}::{Pallet, Inherent}, +71 | | } +72 | | } + | |_^ the trait `ProvideInherent` is not implemented for `pallet::Pallet` + | + = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.rs b/substrate/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.rs index 40a4a1ebcb5d2003a549d5197578a4154c233ea6..b113b3c79bb848cfa6dd63e4861939b564de5fd5 100644 --- a/substrate/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.rs +++ b/substrate/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.rs @@ -36,7 +36,7 @@ pub type UncheckedExtrinsic = generic::UncheckedExtrinsic::Locomotion, (Swims, RunsOnFourLegs)); -assert_type_eq_all!(::Environment, (Land, Sea)); -assert_type_eq_all!(::Diet, Omnivore); -assert_type_eq_all!(::SleepingStrategy, Diurnal); +assert_type_eq_all!(::Locomotion, (Swims, RunsOnFourLegs)); +assert_type_eq_all!(::Environment, (Land, Sea)); +assert_type_eq_all!(::Diet, Omnivore); +assert_type_eq_all!(::SleepingStrategy, Diurnal); pub struct Lion {} diff --git a/substrate/frame/support/test/tests/final_keys.rs b/substrate/frame/support/test/tests/final_keys.rs index 38240830daa2b05f0a8426f4a2d28b3f3df3d865..a777c20a1e98f7a38c4a4cc4dead81e5565b6ebc 100644 --- a/substrate/frame/support/test/tests/final_keys.rs +++ b/substrate/frame/support/test/tests/final_keys.rs @@ -209,7 +209,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type Block = Block; diff --git a/substrate/frame/support/test/tests/genesisconfig.rs b/substrate/frame/support/test/tests/genesisconfig.rs index c6781220692a942adfb445f8620c522b40e4e45a..a82425cf6befe3c014543943ffb6a72e9f00770b 100644 --- a/substrate/frame/support/test/tests/genesisconfig.rs +++ b/substrate/frame/support/test/tests/genesisconfig.rs @@ -79,7 +79,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type Block = Block; diff --git a/substrate/frame/support/test/tests/instance.rs b/substrate/frame/support/test/tests/instance.rs index 7a888e5685d11748ba618c166d5ccd2d91febc28..332f5725e055eb2aa9ca70dff987f021a76d41ba 100644 --- a/substrate/frame/support/test/tests/instance.rs +++ b/substrate/frame/support/test/tests/instance.rs @@ -289,7 +289,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type Block = Block; diff --git a/substrate/frame/support/test/tests/issue2219.rs b/substrate/frame/support/test/tests/issue2219.rs index 4c384f432762343b952e3ebd31dbc7d3b655741f..1542c4a6c434cec98547c533ba3f3530a5aff853 100644 --- a/substrate/frame/support/test/tests/issue2219.rs +++ b/substrate/frame/support/test/tests/issue2219.rs @@ -161,7 +161,7 @@ pub type Header = generic::Header; pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; pub type Block = generic::Block; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type Block = Block; diff --git a/substrate/frame/support/test/tests/origin.rs b/substrate/frame/support/test/tests/origin.rs index 5682bb500c7e366ba6f6da1cd6e0b70569c4a6a7..a25c575cc5177a4511dec8a8b24918ffde6da8d3 100644 --- a/substrate/frame/support/test/tests/origin.rs +++ b/substrate/frame/support/test/tests/origin.rs @@ -170,7 +170,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for RuntimeOriginTest { type BaseCallFilter = BaseCallFilter; type Block = Block; diff --git a/substrate/frame/support/test/tests/pallet.rs b/substrate/frame/support/test/tests/pallet.rs index 9b4381c2f82bd4baa78fc8d175079a0e90480da3..f41e606ad7c3e927e9719afccde3d5e0266509e0 100644 --- a/substrate/frame/support/test/tests/pallet.rs +++ b/substrate/frame/support/test/tests/pallet.rs @@ -209,7 +209,7 @@ pub mod pallet { where T::AccountId: From + From + SomeAssociation1, { - /// Doc comment put in metadata + /// call foo doc comment put in metadata #[pallet::call_index(0)] #[pallet::weight(Weight::from_parts(*foo as u64, 0))] pub fn foo( @@ -225,7 +225,7 @@ pub mod pallet { Ok(().into()) } - /// Doc comment put in metadata + /// call foo_storage_layer doc comment put in metadata #[pallet::call_index(1)] #[pallet::weight({1})] pub fn foo_storage_layer( @@ -270,7 +270,7 @@ pub mod pallet { #[pallet::error] #[derive(PartialEq, Eq)] pub enum Error { - /// doc comment put into metadata + /// error doc comment put in metadata InsufficientProposersBalance, NonExistentStorageValue, Code(u8), @@ -287,9 +287,8 @@ pub mod pallet { where T::AccountId: SomeAssociation1 + From, { - /// doc comment put in metadata + /// event doc comment put in metadata Proposed(::AccountId), - /// doc Spending(BalanceOf), Something(u32), SomethingElse(::_1), @@ -590,7 +589,7 @@ pub mod pallet2 { Self::deposit_event(Event::Something(31)); if UpdateStorageVersion::get() { - Self::current_storage_version().put::(); + Self::in_code_storage_version().put::(); } Weight::zero() @@ -694,7 +693,7 @@ frame_support::parameter_types!( pub const MyGetParam3: u32 = 12; ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type RuntimeOrigin = RuntimeOrigin; @@ -750,8 +749,7 @@ pub type UncheckedExtrinsic = sp_runtime::testing::TestXt>; frame_support::construct_runtime!( - pub struct Runtime - { + pub struct Runtime { // Exclude part `Storage` in order not to check its metadata in tests. System: frame_system exclude_parts { Pallet, Storage }, Example: pallet, @@ -772,6 +770,14 @@ fn _ensure_call_is_correctly_excluded_and_included(call: RuntimeCall) { } } +fn maybe_docs(doc: Vec<&'static str>) -> Vec<&'static str> { + if cfg!(feature = "no-metadata-docs") { + vec![] + } else { + doc + } +} + #[test] fn transactional_works() { TestExternalities::default().execute_with(|| { @@ -1310,7 +1316,7 @@ fn pallet_on_genesis() { assert_eq!(pallet::Pallet::::on_chain_storage_version(), StorageVersion::new(0)); pallet::Pallet::::on_genesis(); assert_eq!( - pallet::Pallet::::current_storage_version(), + pallet::Pallet::::in_code_storage_version(), pallet::Pallet::::on_chain_storage_version(), ); }) @@ -1362,19 +1368,47 @@ fn migrate_from_pallet_version_to_storage_version() { }); } +#[test] +fn pallet_item_docs_in_metadata() { + // call + let call_variants = match meta_type::>().type_info().type_def { + scale_info::TypeDef::Variant(variants) => variants.variants, + _ => unreachable!(), + }; + + assert_eq!(call_variants[0].docs, maybe_docs(vec!["call foo doc comment put in metadata"])); + assert_eq!( + call_variants[1].docs, + maybe_docs(vec!["call foo_storage_layer doc comment put in metadata"]) + ); + assert!(call_variants[2].docs.is_empty()); + + // event + let event_variants = match meta_type::>().type_info().type_def { + scale_info::TypeDef::Variant(variants) => variants.variants, + _ => unreachable!(), + }; + + assert_eq!(event_variants[0].docs, maybe_docs(vec!["event doc comment put in metadata"])); + assert!(event_variants[1].docs.is_empty()); + + // error + let error_variants = match meta_type::>().type_info().type_def { + scale_info::TypeDef::Variant(variants) => variants.variants, + _ => unreachable!(), + }; + + assert_eq!(error_variants[0].docs, maybe_docs(vec!["error doc comment put in metadata"])); + assert!(error_variants[1].docs.is_empty()); + + // storage is already covered in the main `fn metadata` test. +} + #[test] fn metadata() { use codec::Decode; use frame_metadata::{v15::*, *}; - fn maybe_docs(doc: Vec<&'static str>) -> Vec<&'static str> { - if cfg!(feature = "no-metadata-docs") { - vec![] - } else { - doc - } - } - let readme = "Support code for the runtime.\n\nLicense: Apache-2.0\n"; let expected_pallet_doc = vec![" Pallet documentation", readme, readme]; @@ -2257,10 +2291,10 @@ fn pallet_on_chain_storage_version_initializes_correctly() { AllPalletsWithSystem, >; - // Simple example of a pallet with current version 10 being added to the runtime for the first + // Simple example of a pallet with in-code version 10 being added to the runtime for the first // time. TestExternalities::default().execute_with(|| { - let current_version = Example::current_storage_version(); + let in_code_version = Example::in_code_storage_version(); // Check the pallet has no storage items set. let pallet_hashed_prefix = twox_128(Example::name().as_bytes()); @@ -2271,14 +2305,14 @@ fn pallet_on_chain_storage_version_initializes_correctly() { // version. Executive::execute_on_runtime_upgrade(); - // Check that the storage version was initialized to the current version + // Check that the storage version was initialized to the in-code version let on_chain_version_after = StorageVersion::get::(); - assert_eq!(on_chain_version_after, current_version); + assert_eq!(on_chain_version_after, in_code_version); }); - // Pallet with no current storage version should have the on-chain version initialized to 0. + // Pallet with no in-code storage version should have the on-chain version initialized to 0. TestExternalities::default().execute_with(|| { - // Example4 current_storage_version is NoStorageVersionSet. + // Example4 in_code_storage_version is NoStorageVersionSet. // Check the pallet has no storage items set. let pallet_hashed_prefix = twox_128(Example4::name().as_bytes()); @@ -2308,7 +2342,7 @@ fn post_runtime_upgrade_detects_storage_version_issues() { impl OnRuntimeUpgrade for CustomUpgrade { fn on_runtime_upgrade() -> Weight { - Example2::current_storage_version().put::(); + Example2::in_code_storage_version().put::(); Default::default() } @@ -2351,14 +2385,14 @@ fn post_runtime_upgrade_detects_storage_version_issues() { >; TestExternalities::default().execute_with(|| { - // Set the on-chain version to one less than the current version for `Example`, simulating a + // Set the on-chain version to one less than the in-code version for `Example`, simulating a // forgotten migration StorageVersion::new(9).put::(); // The version isn't changed, we should detect it. assert!( Executive::try_runtime_upgrade(UpgradeCheckSelect::PreAndPost).unwrap_err() == - "On chain and current storage version do not match. Missing runtime upgrade?" + "On chain and in-code storage version do not match. Missing runtime upgrade?" .into() ); }); diff --git a/substrate/frame/support/test/tests/pallet_instance.rs b/substrate/frame/support/test/tests/pallet_instance.rs index f8cc97623b8de219900e5d436a2b00d8f3c6fa91..c79cdf93e97dc5a2f257ab9ba62a5d26e078b53f 100644 --- a/substrate/frame/support/test/tests/pallet_instance.rs +++ b/substrate/frame/support/test/tests/pallet_instance.rs @@ -296,7 +296,7 @@ pub mod pallet2 { } } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type RuntimeOrigin = RuntimeOrigin; diff --git a/substrate/frame/support/test/tests/pallet_outer_enums_explicit.rs b/substrate/frame/support/test/tests/pallet_outer_enums_explicit.rs index 79e9d6786717a5a27717cda754575c2ecd478aa3..6c71b544426512c54a4320452204b0a4b62e7764 100644 --- a/substrate/frame/support/test/tests/pallet_outer_enums_explicit.rs +++ b/substrate/frame/support/test/tests/pallet_outer_enums_explicit.rs @@ -25,7 +25,7 @@ pub type Header = sp_runtime::generic::Header; pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type Block = Block; @@ -82,7 +82,7 @@ fn module_error_outer_enum_expand_explicit() { // Check that all error types are propagated match RuntimeError::Example(pallet::Error::InsufficientProposersBalance) { - // Error passed implicitely to the pallet system. + // Error passed implicitly to the pallet system. RuntimeError::System(system) => match system { frame_system::Error::InvalidSpecName => (), frame_system::Error::SpecVersionNeedsToIncrease => (), @@ -90,6 +90,7 @@ fn module_error_outer_enum_expand_explicit() { frame_system::Error::NonDefaultComposite => (), frame_system::Error::NonZeroRefCount => (), frame_system::Error::CallFiltered => (), + frame_system::Error::MultiBlockMigrationsOngoing => (), #[cfg(feature = "experimental")] frame_system::Error::InvalidTask => (), #[cfg(feature = "experimental")] diff --git a/substrate/frame/support/test/tests/pallet_outer_enums_implicit.rs b/substrate/frame/support/test/tests/pallet_outer_enums_implicit.rs index 4bd8ee0bb39a574b2ff3f59b571c1209fc675da4..79828119742c106763b97096ffaed85acf7381da 100644 --- a/substrate/frame/support/test/tests/pallet_outer_enums_implicit.rs +++ b/substrate/frame/support/test/tests/pallet_outer_enums_implicit.rs @@ -25,7 +25,7 @@ pub type Header = sp_runtime::generic::Header; pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type Block = Block; @@ -62,11 +62,11 @@ frame_support::construct_runtime!( // Exclude part `Storage` in order not to check its metadata in tests. System: frame_system exclude_parts { Storage }, - // Pallet exposes `Error` implicitely. + // Pallet exposes `Error` implicitly. Example: common::outer_enums::pallet, Instance1Example: common::outer_enums::pallet::, - // Pallet exposes `Error` implicitely. + // Pallet exposes `Error` implicitly. Example2: common::outer_enums::pallet2, Instance1Example2: common::outer_enums::pallet2::, @@ -82,7 +82,7 @@ fn module_error_outer_enum_expand_implicit() { // Check that all error types are propagated match RuntimeError::Example(pallet::Error::InsufficientProposersBalance) { - // Error passed implicitely to the pallet system. + // Error passed implicitly to the pallet system. RuntimeError::System(system) => match system { frame_system::Error::InvalidSpecName => (), frame_system::Error::SpecVersionNeedsToIncrease => (), @@ -90,6 +90,7 @@ fn module_error_outer_enum_expand_implicit() { frame_system::Error::NonDefaultComposite => (), frame_system::Error::NonZeroRefCount => (), frame_system::Error::CallFiltered => (), + frame_system::Error::MultiBlockMigrationsOngoing => (), #[cfg(feature = "experimental")] frame_system::Error::InvalidTask => (), #[cfg(feature = "experimental")] diff --git a/substrate/frame/support/test/tests/pallet_ui/compare_unset_storage_version.rs b/substrate/frame/support/test/tests/pallet_ui/compare_unset_storage_version.rs index 840a6dee20ccc7a836619a1ef334cca6e22e7887..d2ca9fc80991a87dbb6f20323f07d8adcd29449d 100644 --- a/substrate/frame/support/test/tests/pallet_ui/compare_unset_storage_version.rs +++ b/substrate/frame/support/test/tests/pallet_ui/compare_unset_storage_version.rs @@ -29,7 +29,7 @@ mod pallet { #[pallet::hooks] impl Hooks> for Pallet { fn on_runtime_upgrade() -> Weight { - if Self::current_storage_version() != Self::on_chain_storage_version() { + if Self::in_code_storage_version() != Self::on_chain_storage_version() { } diff --git a/substrate/frame/support/test/tests/pallet_ui/compare_unset_storage_version.stderr b/substrate/frame/support/test/tests/pallet_ui/compare_unset_storage_version.stderr index 1b48197cc9ed1049ea0f52bb011fbc95654c59c0..3256e69528a2d28527ccfe1fa4d77dadfc77baa1 100644 --- a/substrate/frame/support/test/tests/pallet_ui/compare_unset_storage_version.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/compare_unset_storage_version.stderr @@ -1,7 +1,7 @@ error[E0369]: binary operation `!=` cannot be applied to type `NoStorageVersionSet` --> tests/pallet_ui/compare_unset_storage_version.rs:32:39 | -32 | if Self::current_storage_version() != Self::on_chain_storage_version() { +32 | if Self::in_code_storage_version() != Self::on_chain_storage_version() { | ------------------------------- ^^ -------------------------------- StorageVersion | | | NoStorageVersionSet diff --git a/substrate/frame/support/test/tests/pallet_ui/deprecated_store_attr.stderr b/substrate/frame/support/test/tests/pallet_ui/deprecated_store_attr.stderr deleted file mode 100644 index e227033d3646bac74b23267531730e0a59ce06fb..0000000000000000000000000000000000000000 --- a/substrate/frame/support/test/tests/pallet_ui/deprecated_store_attr.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: use of deprecated struct `pallet::_::Store`: - Use of `#[pallet::generate_store(pub(super) trait Store)]` will be removed after July 2023. - Check https://github.com/paritytech/substrate/pull/13535 for more details. - --> tests/pallet_ui/deprecated_store_attr.rs:24:3 - | -24 | #[pallet::generate_store(trait Store)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D deprecated` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(deprecated)]` diff --git a/substrate/frame/support/test/tests/pallet_ui/duplicate_store_attr.rs b/substrate/frame/support/test/tests/pallet_ui/duplicate_store_attr.rs deleted file mode 100644 index 334fd8a46af2e14ef175acfbbdbc6a895b890595..0000000000000000000000000000000000000000 --- a/substrate/frame/support/test/tests/pallet_ui/duplicate_store_attr.rs +++ /dev/null @@ -1,42 +0,0 @@ -// 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. - -#[frame_support::pallet] -mod pallet { - use frame_support::pallet_prelude::Hooks; - use frame_system::pallet_prelude::BlockNumberFor; - use frame_support::pallet_prelude::StorageValue; - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - #[pallet::generate_store(trait Store)] - #[pallet::generate_store(trait Store)] - pub struct Pallet(core::marker::PhantomData); - - #[pallet::hooks] - impl Hooks> for Pallet {} - - #[pallet::call] - impl Pallet {} - - #[pallet::storage] - type Foo = StorageValue<_, u8>; -} - -fn main() {} diff --git a/substrate/frame/support/test/tests/pallet_ui/duplicate_store_attr.stderr b/substrate/frame/support/test/tests/pallet_ui/duplicate_store_attr.stderr deleted file mode 100644 index 864b399326e19ea96afa8d76ccf9815b057ceef6..0000000000000000000000000000000000000000 --- a/substrate/frame/support/test/tests/pallet_ui/duplicate_store_attr.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: Unexpected duplicated attribute - --> tests/pallet_ui/duplicate_store_attr.rs:29:3 - | -29 | #[pallet::generate_store(trait Store)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/substrate/frame/support/test/tests/pallet_ui/pallet_struct_invalid_attr.stderr b/substrate/frame/support/test/tests/pallet_ui/pallet_struct_invalid_attr.stderr index 33a2d1da78608c7d97e433d03c403cf4559a79b9..6f7f5617f7e5a8b40dc5e14f8684ccc256be1994 100644 --- a/substrate/frame/support/test/tests/pallet_ui/pallet_struct_invalid_attr.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/pallet_struct_invalid_attr.stderr @@ -1,4 +1,4 @@ -error: expected one of: `generate_store`, `without_storage_info`, `storage_version` +error: expected `without_storage_info` or `storage_version` --> tests/pallet_ui/pallet_struct_invalid_attr.rs:24:12 | 24 | #[pallet::generate_storage_info] // invalid diff --git a/substrate/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs b/substrate/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs index 4dc33991b1260d3afb5969bcd37d070f192a0268..e4ea094d0692e2f39167da014e3c3185d997ac08 100644 --- a/substrate/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs +++ b/substrate/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs @@ -70,7 +70,7 @@ pub mod pallet { impl Pallet {} } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type RuntimeOrigin = RuntimeOrigin; diff --git a/substrate/frame/support/test/tests/pallet_ui/pass/inherited_call_weight3.rs b/substrate/frame/support/test/tests/pallet_ui/pass/inherited_call_weight3.rs index f40d1040858f5ba47c2ffb5817a02a1035b3a87b..ddccd0b3e192c4d89bc841bfbb2bf2412d82631d 100644 --- a/substrate/frame/support/test/tests/pallet_ui/pass/inherited_call_weight3.rs +++ b/substrate/frame/support/test/tests/pallet_ui/pass/inherited_call_weight3.rs @@ -18,7 +18,7 @@ use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; -// If, for whatever reason, you dont to not use a `WeightInfo` trait - it will still work. +// If, for whatever reason, you don't to not use a `WeightInfo` trait - it will still work. struct Impl; impl Impl { diff --git a/substrate/frame/support/test/tests/pallet_ui/pass/no_std_genesis_config.rs b/substrate/frame/support/test/tests/pallet_ui/pass/no_std_genesis_config.rs index 7e67193cc76c56fe1df8d0f2fc85e6f79d841c1e..23e411e1a0946bb26f310546c88d4d7e020ad68b 100644 --- a/substrate/frame/support/test/tests/pallet_ui/pass/no_std_genesis_config.rs +++ b/substrate/frame/support/test/tests/pallet_ui/pass/no_std_genesis_config.rs @@ -27,7 +27,7 @@ pub type UncheckedExtrinsic = generic::UncheckedExtrinsic - Rc frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes + Rc Arc = note: required for `Bar` to implement `Decode` = note: required for `Bar` to implement `FullCodec` @@ -65,8 +65,8 @@ error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied bytes::bytes::Bytes Cow<'a, T> parity_scale_codec::Ref<'a, T, U> - Rc frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes + Rc Arc Vec and $N others @@ -106,8 +106,8 @@ error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied | = help: the following other types implement trait `WrapperTypeDecode`: Box - Rc frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes + Rc Arc = note: required for `Bar` to implement `Decode` = note: required for `Bar` to implement `FullCodec` @@ -148,8 +148,8 @@ error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied bytes::bytes::Bytes Cow<'a, T> parity_scale_codec::Ref<'a, T, U> - Rc frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes + Rc Arc Vec and $N others @@ -168,8 +168,8 @@ error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied | = help: the following other types implement trait `WrapperTypeDecode`: Box - Rc frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes + Rc Arc = note: required for `Bar` to implement `Decode` = note: required for `Bar` to implement `FullCodec` @@ -210,8 +210,8 @@ error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied bytes::bytes::Bytes Cow<'a, T> parity_scale_codec::Ref<'a, T, U> - Rc frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes + Rc Arc Vec and $N others diff --git a/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr b/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr index 855d289d0a160e08e45fa0c3b9b50616fb9c8ade..13d761d65d2012d124279cdd857aac35fcec9ac0 100644 --- a/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr @@ -13,8 +13,8 @@ error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied | = help: the following other types implement trait `WrapperTypeDecode`: Box - Rc frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes + Rc Arc = note: required for `Bar` to implement `Decode` = note: required for `Bar` to implement `FullCodec` @@ -65,8 +65,8 @@ error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied bytes::bytes::Bytes Cow<'a, T> parity_scale_codec::Ref<'a, T, U> - Rc frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes + Rc Arc Vec and $N others @@ -106,8 +106,8 @@ error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied | = help: the following other types implement trait `WrapperTypeDecode`: Box - Rc frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes + Rc Arc = note: required for `Bar` to implement `Decode` = note: required for `Bar` to implement `FullCodec` @@ -148,8 +148,8 @@ error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied bytes::bytes::Bytes Cow<'a, T> parity_scale_codec::Ref<'a, T, U> - Rc frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes + Rc Arc Vec and $N others @@ -168,8 +168,8 @@ error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied | = help: the following other types implement trait `WrapperTypeDecode`: Box - Rc frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes + Rc Arc = note: required for `Bar` to implement `Decode` = note: required for `Bar` to implement `FullCodec` @@ -210,8 +210,8 @@ error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied bytes::bytes::Bytes Cow<'a, T> parity_scale_codec::Ref<'a, T, U> - Rc frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes + Rc Arc Vec and $N others diff --git a/substrate/frame/support/test/tests/pallet_ui/storage_invalid_attribute.stderr b/substrate/frame/support/test/tests/pallet_ui/storage_invalid_attribute.stderr index 7f125526edf26abddf82f2228da9a510200f0735..519fadaa6049cb26430719379d3f9847b81efb62 100644 --- a/substrate/frame/support/test/tests/pallet_ui/storage_invalid_attribute.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/storage_invalid_attribute.stderr @@ -1,4 +1,4 @@ -error: expected one of: `getter`, `storage_prefix`, `unbounded`, `whitelist_storage` +error: expected one of: `getter`, `storage_prefix`, `unbounded`, `whitelist_storage`, `disable_try_decode_storage` --> tests/pallet_ui/storage_invalid_attribute.rs:33:12 | 33 | #[pallet::generate_store(pub trait Store)] diff --git a/substrate/frame/support/test/tests/pallet_ui/store_trait_leak_private.stderr b/substrate/frame/support/test/tests/pallet_ui/store_trait_leak_private.stderr deleted file mode 100644 index ccb55122e8169e84f776dd41504738ab0f1772f5..0000000000000000000000000000000000000000 --- a/substrate/frame/support/test/tests/pallet_ui/store_trait_leak_private.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error: use of deprecated struct `pallet::_::Store`: - Use of `#[pallet::generate_store(pub(super) trait Store)]` will be removed after July 2023. - Check https://github.com/paritytech/substrate/pull/13535 for more details. - --> tests/pallet_ui/store_trait_leak_private.rs:28:3 - | -28 | #[pallet::generate_store(pub trait Store)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D deprecated` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(deprecated)]` - -error[E0446]: private type `_GeneratedPrefixForStorageFoo` in public interface - --> tests/pallet_ui/store_trait_leak_private.rs:28:37 - | -28 | #[pallet::generate_store(pub trait Store)] - | ^^^^^ can't leak private type -... -37 | #[pallet::storage] - | ------- `_GeneratedPrefixForStorageFoo` declared as private diff --git a/substrate/frame/support/test/tests/runtime.rs b/substrate/frame/support/test/tests/runtime.rs new file mode 100644 index 0000000000000000000000000000000000000000..7c2a8139a1345a4903766970a13bbf955aeef7c8 --- /dev/null +++ b/substrate/frame/support/test/tests/runtime.rs @@ -0,0 +1,993 @@ +// 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. + +//! General tests for construct_runtime macro, test for: +//! * error declared with decl_error works +//! * integrity test is generated + +#![recursion_limit = "128"] + +use codec::MaxEncodedLen; +use frame_support::{ + derive_impl, parameter_types, traits::PalletInfo as _, weights::RuntimeDbWeight, +}; +use frame_system::limits::{BlockLength, BlockWeights}; +use scale_info::TypeInfo; +use sp_core::{sr25519, ConstU64}; +use sp_runtime::{ + generic, + traits::{BlakeTwo256, ValidateUnsigned, Verify}, + DispatchError, ModuleError, +}; +use sp_version::RuntimeVersion; + +parameter_types! { + pub static IntegrityTestExec: u32 = 0; +} + +#[frame_support::pallet(dev_mode)] +mod module1 { + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + } + + #[pallet::call] + impl, I: 'static> Pallet { + pub fn fail(_origin: OriginFor) -> DispatchResult { + Err(Error::::Something.into()) + } + } + + #[pallet::origin] + #[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] + #[scale_info(skip_type_params(I))] + pub struct Origin(pub PhantomData<(T, I)>); + + #[pallet::event] + pub enum Event, I: 'static = ()> { + A(::AccountId), + } + + #[pallet::error] + pub enum Error { + Something, + } +} + +#[frame_support::pallet(dev_mode)] +mod module2 { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + #[pallet::hooks] + impl Hooks> for Pallet { + fn integrity_test() { + IntegrityTestExec::mutate(|i| *i += 1); + } + } + + #[pallet::call] + impl Pallet { + pub fn fail(_origin: OriginFor) -> DispatchResult { + Err(Error::::Something.into()) + } + } + + #[pallet::origin] + #[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] + pub struct Origin; + + #[pallet::event] + pub enum Event { + A, + } + + #[pallet::error] + pub enum Error { + Something, + } +} + +mod nested { + use super::*; + + #[frame_support::pallet(dev_mode)] + pub mod module3 { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + } + + #[pallet::hooks] + impl Hooks> for Pallet { + fn integrity_test() { + IntegrityTestExec::mutate(|i| *i += 1); + } + } + + #[pallet::call] + impl Pallet { + pub fn fail(_origin: OriginFor) -> DispatchResult { + Err(Error::::Something.into()) + } + } + + #[pallet::origin] + #[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] + pub struct Origin; + + #[pallet::event] + pub enum Event { + A, + } + + #[pallet::error] + pub enum Error { + Something, + } + + #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] + pub struct GenesisConfig { + #[serde(skip)] + pub _config: sp_std::marker::PhantomData, + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) {} + } + + #[pallet::validate_unsigned] + impl ValidateUnsigned for Pallet { + type Call = Call; + fn validate_unsigned( + _source: TransactionSource, + _call: &Self::Call, + ) -> TransactionValidity { + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) + } + } + } +} + +#[frame_support::pallet(dev_mode)] +pub mod module3 { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + #[pallet::call] + impl Pallet { + pub fn fail(_origin: OriginFor) -> DispatchResult { + Err(Error::::Something.into()) + } + pub fn aux_1(_origin: OriginFor, #[pallet::compact] _data: u32) -> DispatchResult { + unreachable!() + } + pub fn aux_2( + _origin: OriginFor, + _data: i32, + #[pallet::compact] _data2: u32, + ) -> DispatchResult { + unreachable!() + } + #[pallet::weight(0)] + pub fn aux_3(_origin: OriginFor, _data: i32, _data2: String) -> DispatchResult { + unreachable!() + } + #[pallet::weight(3)] + pub fn aux_4(_origin: OriginFor) -> DispatchResult { + unreachable!() + } + #[pallet::weight((5, DispatchClass::Operational))] + pub fn operational(_origin: OriginFor) -> DispatchResult { + unreachable!() + } + } + + #[pallet::origin] + #[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] + pub struct Origin(pub PhantomData); + + #[pallet::event] + pub enum Event { + A, + } + + #[pallet::error] + pub enum Error { + Something, + } + + #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] + pub struct GenesisConfig { + #[serde(skip)] + pub _config: sp_std::marker::PhantomData, + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) {} + } + + #[pallet::storage] + pub type Storage = StorageValue<_, u32>; + + #[pallet::validate_unsigned] + impl ValidateUnsigned for Pallet { + type Call = Call; + fn validate_unsigned( + _source: TransactionSource, + _call: &Self::Call, + ) -> TransactionValidity { + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) + } + } +} + +pub type BlockNumber = u64; +pub type Signature = sr25519::Signature; +pub type AccountId = ::Signer; +pub type Header = generic::Header; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +pub type Block = generic::Block; + +#[frame_support::runtime] +mod runtime { + #[runtime::runtime] + #[runtime::derive( + RuntimeCall, + RuntimeEvent, + RuntimeError, + RuntimeOrigin, + RuntimeFreezeReason, + RuntimeHoldReason, + RuntimeSlashReason, + RuntimeLockId, + RuntimeTask + )] + pub struct Runtime; + + #[runtime::pallet_index(30)] + pub type System = frame_system + Pallet + Call + Event + Origin; + + #[runtime::pallet_index(31)] + pub type Module1_1 = module1; + + #[runtime::pallet_index(32)] + pub type Module2 = module2; + + #[runtime::pallet_index(33)] + pub type Module1_2 = module1; + + #[runtime::pallet_index(34)] + pub type NestedModule3 = nested::module3; + + #[runtime::pallet_index(35)] + #[runtime::disable_unsigned] + pub type Module3 = self::module3; + + #[runtime::pallet_index(6)] + #[runtime::disable_call] + pub type Module1_3 = module1; + + #[runtime::pallet_index(3)] + pub type Module1_4 = module1; + + #[runtime::pallet_index(4)] + #[runtime::disable_call] + pub type Module1_5 = module1; + + #[runtime::pallet_index(1)] + pub type Module1_6 = module1; + + #[runtime::pallet_index(2)] + pub type Module1_7 = module1; + + #[runtime::pallet_index(12)] + pub type Module1_8 = module1; + + #[runtime::pallet_index(13)] + pub type Module1_9 = module1; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Runtime { + type AccountId = AccountId; + type Lookup = sp_runtime::traits::IdentityLookup; + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type PalletInfo = PalletInfo; + type OnSetCode = (); + type Block = Block; + type BlockHashCount = ConstU64<10>; +} + +impl module1::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module1::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module1::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module1::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module1::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module1::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module1::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module1::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module1::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module2::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl nested::module3::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} +impl module3::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +fn test_pub() -> AccountId { + AccountId::from_raw([0; 32]) +} + +#[test] +fn check_modules_error_type() { + sp_io::TestExternalities::default().execute_with(|| { + assert_eq!( + Module1_1::fail(frame_system::Origin::::Root.into()), + Err(DispatchError::Module(ModuleError { + index: 31, + error: [0; 4], + message: Some("Something") + })), + ); + assert_eq!( + Module2::fail(frame_system::Origin::::Root.into()), + Err(DispatchError::Module(ModuleError { + index: 32, + error: [0; 4], + message: Some("Something") + })), + ); + assert_eq!( + Module1_2::fail(frame_system::Origin::::Root.into()), + Err(DispatchError::Module(ModuleError { + index: 33, + error: [0; 4], + message: Some("Something") + })), + ); + assert_eq!( + NestedModule3::fail(frame_system::Origin::::Root.into()), + Err(DispatchError::Module(ModuleError { + index: 34, + error: [0; 4], + message: Some("Something") + })), + ); + assert_eq!( + Module1_3::fail(frame_system::Origin::::Root.into()), + Err(DispatchError::Module(ModuleError { + index: 6, + error: [0; 4], + message: Some("Something") + })), + ); + assert_eq!( + Module1_4::fail(frame_system::Origin::::Root.into()), + Err(DispatchError::Module(ModuleError { + index: 3, + error: [0; 4], + message: Some("Something") + })), + ); + assert_eq!( + Module1_5::fail(frame_system::Origin::::Root.into()), + Err(DispatchError::Module(ModuleError { + index: 4, + error: [0; 4], + message: Some("Something") + })), + ); + assert_eq!( + Module1_6::fail(frame_system::Origin::::Root.into()), + Err(DispatchError::Module(ModuleError { + index: 1, + error: [0; 4], + message: Some("Something") + })), + ); + assert_eq!( + Module1_7::fail(frame_system::Origin::::Root.into()), + Err(DispatchError::Module(ModuleError { + index: 2, + error: [0; 4], + message: Some("Something") + })), + ); + assert_eq!( + Module1_8::fail(frame_system::Origin::::Root.into()), + Err(DispatchError::Module(ModuleError { + index: 12, + error: [0; 4], + message: Some("Something") + })), + ); + assert_eq!( + Module1_9::fail(frame_system::Origin::::Root.into()), + Err(DispatchError::Module(ModuleError { + index: 13, + error: [0; 4], + message: Some("Something") + })), + ); + }) +} + +#[test] +fn integrity_test_works() { + __construct_runtime_integrity_test::runtime_integrity_tests(); + assert_eq!(IntegrityTestExec::get(), 2); +} + +#[test] +fn origin_codec() { + use codec::Encode; + + let origin = OriginCaller::system(frame_system::RawOrigin::None); + assert_eq!(origin.encode()[0], 30); + + let origin = OriginCaller::Module1_1(module1::Origin(Default::default())); + assert_eq!(origin.encode()[0], 31); + + let origin = OriginCaller::Module2(module2::Origin); + assert_eq!(origin.encode()[0], 32); + + let origin = OriginCaller::Module1_2(module1::Origin(Default::default())); + assert_eq!(origin.encode()[0], 33); + + let origin = OriginCaller::NestedModule3(nested::module3::Origin); + assert_eq!(origin.encode()[0], 34); + + let origin = OriginCaller::Module3(module3::Origin(Default::default())); + assert_eq!(origin.encode()[0], 35); + + let origin = OriginCaller::Module1_6(module1::Origin(Default::default())); + assert_eq!(origin.encode()[0], 1); + + let origin = OriginCaller::Module1_7(module1::Origin(Default::default())); + assert_eq!(origin.encode()[0], 2); + + let origin = OriginCaller::Module1_8(module1::Origin(Default::default())); + assert_eq!(origin.encode()[0], 12); + + let origin = OriginCaller::Module1_9(module1::Origin(Default::default())); + assert_eq!(origin.encode()[0], 13); +} + +#[test] +fn event_codec() { + use codec::Encode; + + let event = + frame_system::Event::::ExtrinsicSuccess { dispatch_info: Default::default() }; + assert_eq!(RuntimeEvent::from(event).encode()[0], 30); + + let event = module1::Event::::A(test_pub()); + assert_eq!(RuntimeEvent::from(event).encode()[0], 31); + + let event = module2::Event::A; + assert_eq!(RuntimeEvent::from(event).encode()[0], 32); + + let event = module1::Event::::A(test_pub()); + assert_eq!(RuntimeEvent::from(event).encode()[0], 33); + + let event = nested::module3::Event::A; + assert_eq!(RuntimeEvent::from(event).encode()[0], 34); + + let event = module3::Event::A; + assert_eq!(RuntimeEvent::from(event).encode()[0], 35); + + let event = module1::Event::::A(test_pub()); + assert_eq!(RuntimeEvent::from(event).encode()[0], 4); + + let event = module1::Event::::A(test_pub()); + assert_eq!(RuntimeEvent::from(event).encode()[0], 1); + + let event = module1::Event::::A(test_pub()); + assert_eq!(RuntimeEvent::from(event).encode()[0], 2); + + let event = module1::Event::::A(test_pub()); + assert_eq!(RuntimeEvent::from(event).encode()[0], 12); + + let event = module1::Event::::A(test_pub()); + assert_eq!(RuntimeEvent::from(event).encode()[0], 13); +} + +#[test] +fn call_codec() { + use codec::Encode; + assert_eq!(RuntimeCall::System(frame_system::Call::remark { remark: vec![1] }).encode()[0], 30); + assert_eq!(RuntimeCall::Module1_1(module1::Call::fail {}).encode()[0], 31); + assert_eq!(RuntimeCall::Module2(module2::Call::fail {}).encode()[0], 32); + assert_eq!(RuntimeCall::Module1_2(module1::Call::fail {}).encode()[0], 33); + assert_eq!(RuntimeCall::NestedModule3(nested::module3::Call::fail {}).encode()[0], 34); + assert_eq!(RuntimeCall::Module3(module3::Call::fail {}).encode()[0], 35); + assert_eq!(RuntimeCall::Module1_4(module1::Call::fail {}).encode()[0], 3); + assert_eq!(RuntimeCall::Module1_6(module1::Call::fail {}).encode()[0], 1); + assert_eq!(RuntimeCall::Module1_7(module1::Call::fail {}).encode()[0], 2); + assert_eq!(RuntimeCall::Module1_8(module1::Call::fail {}).encode()[0], 12); + assert_eq!(RuntimeCall::Module1_9(module1::Call::fail {}).encode()[0], 13); +} + +#[test] +fn call_compact_attr() { + use codec::Encode; + let call: module3::Call = module3::Call::aux_1 { data: 1 }; + let encoded = call.encode(); + assert_eq!(2, encoded.len()); + assert_eq!(vec![1, 4], encoded); + + let call: module3::Call = module3::Call::aux_2 { data: 1, data2: 2 }; + let encoded = call.encode(); + assert_eq!(6, encoded.len()); + assert_eq!(vec![2, 1, 0, 0, 0, 8], encoded); +} + +#[test] +fn call_encode_is_correct_and_decode_works() { + use codec::{Decode, Encode}; + let call: module3::Call = module3::Call::fail {}; + let encoded = call.encode(); + assert_eq!(vec![0], encoded); + let decoded = module3::Call::::decode(&mut &encoded[..]).unwrap(); + assert_eq!(decoded, call); + + let call: module3::Call = module3::Call::aux_3 { data: 32, data2: "hello".into() }; + let encoded = call.encode(); + assert_eq!(vec![3, 32, 0, 0, 0, 20, 104, 101, 108, 108, 111], encoded); + let decoded = module3::Call::::decode(&mut &encoded[..]).unwrap(); + assert_eq!(decoded, call); +} + +#[test] +fn call_weight_should_attach_to_call_enum() { + use frame_support::{ + dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays}, + weights::Weight, + }; + // operational. + assert_eq!( + module3::Call::::operational {}.get_dispatch_info(), + DispatchInfo { + weight: Weight::from_parts(5, 0), + class: DispatchClass::Operational, + pays_fee: Pays::Yes + }, + ); + // custom basic + assert_eq!( + module3::Call::::aux_4 {}.get_dispatch_info(), + DispatchInfo { + weight: Weight::from_parts(3, 0), + class: DispatchClass::Normal, + pays_fee: Pays::Yes + }, + ); +} + +#[test] +fn call_name() { + use frame_support::traits::GetCallName; + let name = module3::Call::::aux_4 {}.get_call_name(); + assert_eq!("aux_4", name); +} + +#[test] +fn call_metadata() { + use frame_support::traits::{CallMetadata, GetCallMetadata}; + let call = RuntimeCall::Module3(module3::Call::::aux_4 {}); + let metadata = call.get_call_metadata(); + let expected = CallMetadata { function_name: "aux_4".into(), pallet_name: "Module3".into() }; + assert_eq!(metadata, expected); +} + +#[test] +fn get_call_names() { + use frame_support::traits::GetCallName; + let call_names = module3::Call::::get_call_names(); + assert_eq!(["fail", "aux_1", "aux_2", "aux_3", "aux_4", "operational"], call_names); +} + +#[test] +fn get_module_names() { + use frame_support::traits::GetCallMetadata; + let module_names = RuntimeCall::get_module_names(); + assert_eq!( + [ + "Module1_6", + "Module1_7", + "Module1_4", + "Module1_8", + "Module1_9", + "System", + "Module1_1", + "Module2", + "Module1_2", + "NestedModule3", + "Module3", + ], + module_names + ); +} + +#[test] +fn call_subtype_conversion() { + use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; + let call = RuntimeCall::Module3(module3::Call::::fail {}); + let subcall: Option<&CallableCallFor> = call.is_sub_type(); + let subcall_none: Option<&CallableCallFor> = call.is_sub_type(); + assert_eq!(Some(&module3::Call::::fail {}), subcall); + assert_eq!(None, subcall_none); + + let from = RuntimeCall::from(subcall.unwrap().clone()); + assert_eq!(from, call); +} + +#[test] +fn test_metadata() { + use frame_metadata::{ + v14::{StorageEntryType::Plain, *}, + *, + }; + use scale_info::meta_type; + use sp_core::Encode; + use sp_metadata_ir::StorageEntryModifierIR::Optional; + + fn maybe_docs(doc: Vec<&'static str>) -> Vec<&'static str> { + if cfg!(feature = "no-metadata-docs") { + vec![] + } else { + doc + } + } + + let pallets = vec![ + PalletMetadata { + name: "Module1_6", + storage:None, + calls: Some(meta_type::>().into()), + event: Some(meta_type::>().into()), + constants: vec![], + error: Some(meta_type::>().into()), + index: 1, + }, + PalletMetadata { + name: "Module1_7", + storage: None, + calls: Some(meta_type::>().into()), + event: Some(meta_type::>().into()), + constants: vec![], + error: Some(meta_type::>().into()), + index: 2, + }, + PalletMetadata { + name: "Module1_4", + storage: None, + calls: Some(meta_type::>().into()), + event: Some(meta_type::>().into()), + constants: vec![], + error: Some(meta_type::>().into()), + index: 3, + }, + PalletMetadata { + name: "Module1_5", + storage: None, + calls: None, + event: Some(meta_type::>().into()), + constants: vec![], + error: Some(meta_type::>().into()), + index: 4, + }, + PalletMetadata { + name: "Module1_3", + storage: None, + calls: None, + event: Some(meta_type::>().into()), + constants: vec![], + error: Some(meta_type::>().into()), + index: 6, + }, + PalletMetadata { + name: "Module1_8", + storage: None, + calls: Some(meta_type::>().into()), + event: Some(meta_type::>().into()), + constants: vec![], + error: Some(meta_type::>().into()), + index: 12, + }, + PalletMetadata { + name: "Module1_9", + storage: None, + calls: Some(meta_type::>().into()), + event: Some(meta_type::>().into()), + constants: vec![], + error: Some(meta_type::>().into()), + index: 13, + }, + PalletMetadata { + name: "System", + storage: None, + calls: Some(meta_type::>().into()), + event: Some(meta_type::>().into()), + constants: vec![ + PalletConstantMetadata { + name: "BlockWeights", + ty: meta_type::(), + value: BlockWeights::default().encode(), + docs: maybe_docs(vec![" Block & extrinsics weights: base values and limits."]), + }, + PalletConstantMetadata { + name: "BlockLength", + ty: meta_type::(), + value: BlockLength::default().encode(), + docs: maybe_docs(vec![" The maximum length of a block (in bytes)."]), + }, + PalletConstantMetadata { + name: "BlockHashCount", + ty: meta_type::(), + value: 10u64.encode(), + docs: maybe_docs(vec![" Maximum number of block number to block hash mappings to keep (oldest pruned first)."]), + }, + PalletConstantMetadata { + name: "DbWeight", + ty: meta_type::(), + value: RuntimeDbWeight::default().encode(), + docs: maybe_docs(vec![" The weight of runtime database operations the runtime can invoke.",]), + }, + PalletConstantMetadata { + name: "Version", + ty: meta_type::(), + value: RuntimeVersion::default().encode(), + docs: maybe_docs(vec![ " Get the chain's in-code version."]), + }, + PalletConstantMetadata { + name: "SS58Prefix", + ty: meta_type::(), + value: 0u16.encode(), + docs: maybe_docs(vec![ + " The designated SS58 prefix of this chain.", + "", + " This replaces the \"ss58Format\" property declared in the chain spec. Reason is", + " that the runtime should know about the prefix in order to make use of it as", + " an identifier of the chain.", + ]), + }, + ], + error: Some(meta_type::>().into()), + index: 30, + }, + PalletMetadata { + name: "Module1_1", + storage: None, + calls: Some(meta_type::>().into()), + event: Some(meta_type::>().into()), + constants: vec![], + error: Some(meta_type::>().into()), + index: 31, + }, + PalletMetadata { + name: "Module2", + storage: None, + calls: Some(meta_type::>().into()), + event: Some(meta_type::>().into()), + constants: vec![], + error: Some(meta_type::>().into()), + index: 32, + }, + PalletMetadata { + name: "Module1_2", + storage: None, + calls: Some(meta_type::>().into()), + event: Some(meta_type::>().into()), + constants: vec![], + error: Some(meta_type::>().into()), + index: 33, + }, + PalletMetadata { + name: "NestedModule3", + storage: None, + calls: Some(meta_type::>().into()), + event: Some(meta_type::>().into()), + constants: vec![], + error: Some(meta_type::>().into()), + index: 34, + }, + PalletMetadata { + name: "Module3", + storage: Some(PalletStorageMetadata { + prefix: "Module3", + entries: vec![ + StorageEntryMetadata { + name: "Storage", + modifier: Optional.into(), + ty: Plain(meta_type::().into()), + default: vec![0], + docs: vec![], + }, + ] + }), + calls: Some(meta_type::>().into()), + event: Some(meta_type::>().into()), + constants: vec![], + error: Some(meta_type::>().into()), + index: 35, + }, + ]; + + let extrinsic = ExtrinsicMetadata { + ty: meta_type::(), + version: 4, + signed_extensions: vec![SignedExtensionMetadata { + identifier: "UnitSignedExtension", + ty: meta_type::<()>(), + additional_signed: meta_type::<()>(), + }], + }; + + let expected_metadata: RuntimeMetadataPrefixed = + RuntimeMetadataLastVersion::new(pallets, extrinsic, meta_type::()).into(); + let actual_metadata = Runtime::metadata(); + + pretty_assertions::assert_eq!(actual_metadata, expected_metadata); +} + +#[test] +fn pallet_in_runtime_is_correct() { + assert_eq!(PalletInfo::index::().unwrap(), 30); + assert_eq!(PalletInfo::name::().unwrap(), "System"); + assert_eq!(PalletInfo::module_name::().unwrap(), "frame_system"); + assert!(PalletInfo::crate_version::().is_some()); + + assert_eq!(PalletInfo::index::().unwrap(), 31); + assert_eq!(PalletInfo::name::().unwrap(), "Module1_1"); + assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); + assert!(PalletInfo::crate_version::().is_some()); + + assert_eq!(PalletInfo::index::().unwrap(), 32); + assert_eq!(PalletInfo::name::().unwrap(), "Module2"); + assert_eq!(PalletInfo::module_name::().unwrap(), "module2"); + assert!(PalletInfo::crate_version::().is_some()); + + assert_eq!(PalletInfo::index::().unwrap(), 33); + assert_eq!(PalletInfo::name::().unwrap(), "Module1_2"); + assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); + assert!(PalletInfo::crate_version::().is_some()); + + assert_eq!(PalletInfo::index::().unwrap(), 34); + assert_eq!(PalletInfo::name::().unwrap(), "NestedModule3"); + assert_eq!(PalletInfo::module_name::().unwrap(), "nested::module3"); + assert!(PalletInfo::crate_version::().is_some()); + + assert_eq!(PalletInfo::index::().unwrap(), 35); + assert_eq!(PalletInfo::name::().unwrap(), "Module3"); + assert_eq!(PalletInfo::module_name::().unwrap(), "self::module3"); + assert!(PalletInfo::crate_version::().is_some()); + + assert_eq!(PalletInfo::index::().unwrap(), 6); + assert_eq!(PalletInfo::name::().unwrap(), "Module1_3"); + assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); + assert!(PalletInfo::crate_version::().is_some()); + + assert_eq!(PalletInfo::index::().unwrap(), 3); + assert_eq!(PalletInfo::name::().unwrap(), "Module1_4"); + assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); + assert!(PalletInfo::crate_version::().is_some()); + + assert_eq!(PalletInfo::index::().unwrap(), 4); + assert_eq!(PalletInfo::name::().unwrap(), "Module1_5"); + assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); + assert!(PalletInfo::crate_version::().is_some()); + + assert_eq!(PalletInfo::index::().unwrap(), 1); + assert_eq!(PalletInfo::name::().unwrap(), "Module1_6"); + assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); + assert!(PalletInfo::crate_version::().is_some()); + + assert_eq!(PalletInfo::index::().unwrap(), 2); + assert_eq!(PalletInfo::name::().unwrap(), "Module1_7"); + assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); + assert!(PalletInfo::crate_version::().is_some()); + + assert_eq!(PalletInfo::index::().unwrap(), 12); + assert_eq!(PalletInfo::name::().unwrap(), "Module1_8"); + assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); + assert!(PalletInfo::crate_version::().is_some()); + + assert_eq!(PalletInfo::index::().unwrap(), 13); + assert_eq!(PalletInfo::name::().unwrap(), "Module1_9"); + assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); + assert!(PalletInfo::crate_version::().is_some()); +} + +#[test] +fn test_validate_unsigned() { + use frame_support::pallet_prelude::*; + + let call = RuntimeCall::NestedModule3(nested::module3::Call::fail {}); + let validity = Runtime::validate_unsigned(TransactionSource::Local, &call).unwrap_err(); + assert_eq!(validity, TransactionValidityError::Invalid(InvalidTransaction::Call)); + + let call = RuntimeCall::Module3(module3::Call::fail {}); + let validity = Runtime::validate_unsigned(TransactionSource::Local, &call).unwrap_err(); + assert_eq!( + validity, + TransactionValidityError::Unknown(UnknownTransaction::NoUnsignedValidator) + ); +} diff --git a/substrate/frame/support/test/tests/construct_runtime.rs b/substrate/frame/support/test/tests/runtime_legacy_ordering.rs similarity index 89% rename from substrate/frame/support/test/tests/construct_runtime.rs rename to substrate/frame/support/test/tests/runtime_legacy_ordering.rs index b8341b25cb0985915706ccb68b870474118d33cb..4c7012dca14979f0ed7f560bdc339146a676caa3 100644 --- a/substrate/frame/support/test/tests/construct_runtime.rs +++ b/substrate/frame/support/test/tests/runtime_legacy_ordering.rs @@ -30,7 +30,7 @@ use scale_info::TypeInfo; use sp_core::{sr25519, ConstU64}; use sp_runtime::{ generic, - traits::{BlakeTwo256, Verify}, + traits::{BlakeTwo256, ValidateUnsigned, Verify}, DispatchError, ModuleError, }; use sp_version::RuntimeVersion; @@ -176,6 +176,17 @@ mod nested { impl BuildGenesisConfig for GenesisConfig { fn build(&self) {} } + + #[pallet::validate_unsigned] + impl ValidateUnsigned for Pallet { + type Call = Call; + fn validate_unsigned( + _source: TransactionSource, + _call: &Self::Call, + ) -> TransactionValidity { + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) + } + } } } @@ -247,6 +258,20 @@ pub mod module3 { impl BuildGenesisConfig for GenesisConfig { fn build(&self) {} } + + #[pallet::storage] + pub type Storage = StorageValue<_, u32>; + + #[pallet::validate_unsigned] + impl ValidateUnsigned for Pallet { + type Call = Call; + fn validate_unsigned( + _source: TransactionSource, + _call: &Self::Call, + ) -> TransactionValidity { + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) + } + } } pub type BlockNumber = u64; @@ -256,24 +281,64 @@ pub type Header = generic::Header; pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; pub type Block = generic::Block; -frame_support::construct_runtime!( - pub struct Runtime - { - System: frame_system::{Pallet, Call, Event, Origin} = 30, - Module1_1: module1::::{Pallet, Call, Storage, Event, Origin}, - Module2: module2::{Pallet, Call, Storage, Event, Origin}, - Module1_2: module1::::{Pallet, Call, Storage, Event, Origin}, - NestedModule3: nested::module3::{Pallet, Call, Config, Storage, Event, Origin}, - Module3: self::module3::{Pallet, Call, Config, Storage, Event, Origin}, - Module1_3: module1::::{Pallet, Storage, Event } = 6, - Module1_4: module1::::{Pallet, Call, Event } = 3, - Module1_5: module1::::{Pallet, Event}, - Module1_6: module1::::{Pallet, Call, Storage, Event, Origin} = 1, - Module1_7: module1::::{Pallet, Call, Storage, Event, Origin}, - Module1_8: module1::::{Pallet, Call, Storage, Event, Origin} = 12, - Module1_9: module1::::{Pallet, Call, Storage, Event, Origin}, - } -); +#[frame_support::runtime(legacy_ordering)] +mod runtime { + #[runtime::runtime] + #[runtime::derive( + RuntimeCall, + RuntimeEvent, + RuntimeError, + RuntimeOrigin, + RuntimeFreezeReason, + RuntimeHoldReason, + RuntimeSlashReason, + RuntimeLockId, + RuntimeTask + )] + pub struct Runtime; + + #[runtime::pallet_index(30)] + pub type System = frame_system + Pallet + Call + Event + Origin; + + #[runtime::pallet_index(31)] + pub type Module1_1 = module1; + + #[runtime::pallet_index(32)] + pub type Module2 = module2; + + #[runtime::pallet_index(33)] + pub type Module1_2 = module1; + + #[runtime::pallet_index(34)] + pub type NestedModule3 = nested::module3; + + #[runtime::pallet_index(35)] + #[runtime::disable_unsigned] + pub type Module3 = self::module3; + + #[runtime::pallet_index(6)] + #[runtime::disable_call] + pub type Module1_3 = module1; + + #[runtime::pallet_index(3)] + pub type Module1_4 = module1; + + #[runtime::pallet_index(4)] + #[runtime::disable_call] + pub type Module1_5 = module1; + + #[runtime::pallet_index(1)] + pub type Module1_6 = module1; + + #[runtime::pallet_index(2)] + pub type Module1_7 = module1; + + #[runtime::pallet_index(12)] + pub type Module1_8 = module1; + + #[runtime::pallet_index(13)] + pub type Module1_9 = module1; +} #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Runtime { @@ -636,9 +701,13 @@ fn call_subtype_conversion() { #[test] fn test_metadata() { - use frame_metadata::{v14::*, *}; + use frame_metadata::{ + v14::{StorageEntryType::Plain, *}, + *, + }; use scale_info::meta_type; use sp_core::Encode; + use sp_metadata_ir::StorageEntryModifierIR::Optional; fn maybe_docs(doc: Vec<&'static str>) -> Vec<&'static str> { if cfg!(feature = "no-metadata-docs") { @@ -683,7 +752,7 @@ fn test_metadata() { name: "Version", ty: meta_type::(), value: RuntimeVersion::default().encode(), - docs: maybe_docs(vec![ " Get the chain's current version."]), + docs: maybe_docs(vec![ " Get the chain's in-code version."]), }, PalletConstantMetadata { name: "SS58Prefix", @@ -703,7 +772,7 @@ fn test_metadata() { }, PalletMetadata { name: "Module1_1", - storage: Some(PalletStorageMetadata { prefix: "Module1_1", entries: vec![] }), + storage: None, calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], @@ -712,7 +781,7 @@ fn test_metadata() { }, PalletMetadata { name: "Module2", - storage: Some(PalletStorageMetadata { prefix: "Module2", entries: vec![] }), + storage: None, calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], @@ -721,7 +790,7 @@ fn test_metadata() { }, PalletMetadata { name: "Module1_2", - storage: Some(PalletStorageMetadata { prefix: "Module1_2", entries: vec![] }), + storage: None, calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], @@ -730,7 +799,7 @@ fn test_metadata() { }, PalletMetadata { name: "NestedModule3", - storage: Some(PalletStorageMetadata { prefix: "NestedModule3", entries: vec![] }), + storage: None, calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], @@ -739,7 +808,18 @@ fn test_metadata() { }, PalletMetadata { name: "Module3", - storage: Some(PalletStorageMetadata { prefix: "Module3", entries: vec![] }), + storage: Some(PalletStorageMetadata { + prefix: "Module3", + entries: vec![ + StorageEntryMetadata { + name: "Storage", + modifier: Optional.into(), + ty: Plain(meta_type::().into()), + default: vec![0], + docs: vec![], + }, + ] + }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], @@ -748,7 +828,7 @@ fn test_metadata() { }, PalletMetadata { name: "Module1_3", - storage: Some(PalletStorageMetadata { prefix: "Module1_3", entries: vec![] }), + storage: None, calls: None, event: Some(meta_type::>().into()), constants: vec![], @@ -775,7 +855,7 @@ fn test_metadata() { }, PalletMetadata { name: "Module1_6", - storage: Some(PalletStorageMetadata { prefix: "Module1_6", entries: vec![] }), + storage:None, calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], @@ -784,7 +864,7 @@ fn test_metadata() { }, PalletMetadata { name: "Module1_7", - storage: Some(PalletStorageMetadata { prefix: "Module1_7", entries: vec![] }), + storage: None, calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], @@ -793,7 +873,7 @@ fn test_metadata() { }, PalletMetadata { name: "Module1_8", - storage: Some(PalletStorageMetadata { prefix: "Module1_8", entries: vec![] }), + storage: None, calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], @@ -802,7 +882,7 @@ fn test_metadata() { }, PalletMetadata { name: "Module1_9", - storage: Some(PalletStorageMetadata { prefix: "Module1_9", entries: vec![] }), + storage: None, calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], @@ -895,3 +975,19 @@ fn pallet_in_runtime_is_correct() { assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); } + +#[test] +fn test_validate_unsigned() { + use frame_support::pallet_prelude::*; + + let call = RuntimeCall::NestedModule3(nested::module3::Call::fail {}); + let validity = Runtime::validate_unsigned(TransactionSource::Local, &call).unwrap_err(); + assert_eq!(validity, TransactionValidityError::Invalid(InvalidTransaction::Call)); + + let call = RuntimeCall::Module3(module3::Call::fail {}); + let validity = Runtime::validate_unsigned(TransactionSource::Local, &call).unwrap_err(); + assert_eq!( + validity, + TransactionValidityError::Unknown(UnknownTransaction::NoUnsignedValidator) + ); +} diff --git a/substrate/frame/support/test/tests/runtime_metadata.rs b/substrate/frame/support/test/tests/runtime_metadata.rs index bb7f7d2822e7cf1a15c41de1ca47686dd8440bfc..819ec176d2b13c3fb5c90290339ef328a4c523ff 100644 --- a/substrate/frame/support/test/tests/runtime_metadata.rs +++ b/substrate/frame/support/test/tests/runtime_metadata.rs @@ -27,7 +27,7 @@ pub type Header = sp_runtime::generic::Header; pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BlockWeights = (); type BlockLength = (); @@ -101,7 +101,7 @@ sp_api::impl_runtime_apis! { fn execute_block(_: Block) { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } @@ -200,8 +200,8 @@ fn runtime_metadata() { name: "header", ty: meta_type::<&::Header>(), }], - output: meta_type::<()>(), - docs: maybe_docs(vec![" Initialize a block with the given header."]), + output: meta_type::(), + docs: maybe_docs(vec![" Initialize a block with the given header and return the runtime executive mode."]), }, ], docs: maybe_docs(vec![ diff --git a/substrate/frame/support/test/tests/runtime_ui.rs b/substrate/frame/support/test/tests/runtime_ui.rs new file mode 100644 index 0000000000000000000000000000000000000000..dbe150f38ed6a3b6a45ac58e288b65c397c671f9 --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui.rs @@ -0,0 +1,36 @@ +// 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. + +#[rustversion::attr(not(stable), ignore)] +#[cfg(not(feature = "disable-ui-tests"))] +#[test] +fn ui() { + // Only run the ui tests when `RUN_UI_TESTS` is set. + if std::env::var("RUN_UI_TESTS").is_err() { + return + } + + // As trybuild is using `cargo check`, we don't need the real WASM binaries. + std::env::set_var("SKIP_WASM_BUILD", "1"); + + // Deny all warnings since we emit warnings as part of a Runtime's UI. + std::env::set_var("RUSTFLAGS", "--deny warnings"); + + let t = trybuild::TestCases::new(); + t.compile_fail("tests/runtime_ui/*.rs"); + t.pass("tests/runtime_ui/pass/*.rs"); +} diff --git a/substrate/frame/support/test/tests/runtime_ui/can_only_be_attached_to_mod.rs b/substrate/frame/support/test/tests/runtime_ui/can_only_be_attached_to_mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..eb5868090418a3e10320cc4394c565d054926710 --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/can_only_be_attached_to_mod.rs @@ -0,0 +1,21 @@ +// 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. + +#[frame_support::runtime] +fn construct_runtime() {} + +fn main() {} diff --git a/substrate/frame/support/test/tests/runtime_ui/can_only_be_attached_to_mod.stderr b/substrate/frame/support/test/tests/runtime_ui/can_only_be_attached_to_mod.stderr new file mode 100644 index 0000000000000000000000000000000000000000..7c2f0d1f1699502c8847f308e7978d988552cc47 --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/can_only_be_attached_to_mod.stderr @@ -0,0 +1,5 @@ +error: expected `mod` + --> tests/runtime_ui/can_only_be_attached_to_mod.rs:19:1 + | +19 | fn construct_runtime() {} + | ^^ diff --git a/substrate/frame/support/test/tests/pallet_ui/store_trait_leak_private.rs b/substrate/frame/support/test/tests/runtime_ui/conflicting_pallet_index.rs similarity index 58% rename from substrate/frame/support/test/tests/pallet_ui/store_trait_leak_private.rs rename to substrate/frame/support/test/tests/runtime_ui/conflicting_pallet_index.rs index 55dd315fb297cdf55802e25f48b53364b394dd3d..648812b52c4fe8b307b93a4e702b1ff98547e613 100644 --- a/substrate/frame/support/test/tests/pallet_ui/store_trait_leak_private.rs +++ b/substrate/frame/support/test/tests/runtime_ui/conflicting_pallet_index.rs @@ -17,26 +17,27 @@ #[frame_support::pallet] mod pallet { - use frame_support::pallet_prelude::Hooks; - use frame_system::pallet_prelude::BlockNumberFor; - use frame_support::pallet_prelude::StorageValue; + #[pallet::config] + pub trait Config: frame_system::Config {} - #[pallet::config] - pub trait Config: frame_system::Config {} + #[pallet::pallet] + pub struct Pallet(_); - #[pallet::pallet] - #[pallet::generate_store(pub trait Store)] - pub struct Pallet(core::marker::PhantomData); + #[pallet::call] + impl Pallet {} +} - #[pallet::hooks] - impl Hooks> for Pallet {} +#[frame_support::runtime] +mod runtime { + #[runtime::runtime] + #[runtime::derive(RuntimeCall)] + pub struct Runtime; - #[pallet::call] - impl Pallet {} + #[runtime::pallet_index(0)] + pub type System = frame_system; - #[pallet::storage] - type Foo = StorageValue<_, u8>; + #[runtime::pallet_index(0)] + pub type Pallet = pallet; } -fn main() { -} +fn main() {} diff --git a/substrate/frame/support/test/tests/runtime_ui/conflicting_pallet_index.stderr b/substrate/frame/support/test/tests/runtime_ui/conflicting_pallet_index.stderr new file mode 100644 index 0000000000000000000000000000000000000000..8963c5d3f265d1efb6be5fb1b820285f86d4245b --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/conflicting_pallet_index.stderr @@ -0,0 +1,11 @@ +error: Pallet indices are conflicting: Both pallets System and Pallet are at index 0 + --> tests/runtime_ui/conflicting_pallet_index.rs:37:14 + | +37 | pub type System = frame_system; + | ^^^^^^ + +error: Pallet indices are conflicting: Both pallets System and Pallet are at index 0 + --> tests/runtime_ui/conflicting_pallet_index.rs:40:14 + | +40 | pub type Pallet = pallet; + | ^^^^^^ diff --git a/substrate/frame/support/test/tests/runtime_ui/conflicting_pallet_name.rs b/substrate/frame/support/test/tests/runtime_ui/conflicting_pallet_name.rs new file mode 100644 index 0000000000000000000000000000000000000000..c68f9a8082b47fcd3bf306187c8f511392baf95b --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/conflicting_pallet_name.rs @@ -0,0 +1,43 @@ +// 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. + +#[frame_support::pallet] +mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::call] + impl Pallet {} +} + +#[frame_support::runtime] +mod runtime { + #[runtime::runtime] + #[runtime::derive(RuntimeCall)] + pub struct Runtime; + + #[runtime::pallet_index(0)] + pub type System = frame_system; + + #[runtime::pallet_index(1)] + pub type System = pallet; +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/runtime_ui/conflicting_pallet_name.stderr b/substrate/frame/support/test/tests/runtime_ui/conflicting_pallet_name.stderr new file mode 100644 index 0000000000000000000000000000000000000000..c250c24e3e704136e380f91b0e74eb3c4fd4d7b7 --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/conflicting_pallet_name.stderr @@ -0,0 +1,11 @@ +error: Two pallets with the same name! + --> tests/runtime_ui/conflicting_pallet_name.rs:37:14 + | +37 | pub type System = frame_system; + | ^^^^^^ + +error: Two pallets with the same name! + --> tests/runtime_ui/conflicting_pallet_name.rs:40:14 + | +40 | pub type System = pallet; + | ^^^^^^ diff --git a/substrate/frame/support/test/tests/runtime_ui/invalid_attribute.rs b/substrate/frame/support/test/tests/runtime_ui/invalid_attribute.rs new file mode 100644 index 0000000000000000000000000000000000000000..5df2491cb24ba15000324efdb853e7e38af1b774 --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/invalid_attribute.rs @@ -0,0 +1,23 @@ +// 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. + +#[frame_support::runtime(dummy)] +mod runtime { + +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/runtime_ui/invalid_attribute.stderr b/substrate/frame/support/test/tests/runtime_ui/invalid_attribute.stderr new file mode 100644 index 0000000000000000000000000000000000000000..e13552413c00a094bf37cb54f0f21db4e0030231 --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/invalid_attribute.stderr @@ -0,0 +1,5 @@ +error: Invalid runtime macro call: unexpected attribute. Macro call must be bare, such as `#[frame_support::runtime]` or `#[runtime]`, or must specify the `legacy_ordering` attribute, such as `#[frame_support::runtime(legacy_ordering)]` or #[runtime(legacy_ordering)]. + --> tests/runtime_ui/invalid_attribute.rs:18:26 + | +18 | #[frame_support::runtime(dummy)] + | ^^^^^ diff --git a/substrate/frame/support/test/tests/runtime_ui/invalid_pallet_index.rs b/substrate/frame/support/test/tests/runtime_ui/invalid_pallet_index.rs new file mode 100644 index 0000000000000000000000000000000000000000..de89bb60224fd8fd77ac7c68f62353c6e51aafe6 --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/invalid_pallet_index.rs @@ -0,0 +1,28 @@ +// 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. + +#[frame_support::runtime] +mod runtime { + #[runtime::runtime] + #[runtime::derive(RuntimeCall)] + pub struct Runtime; + + #[runtime::pallet_index("0")] + pub type System = frame_system; +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/runtime_ui/invalid_pallet_index.stderr b/substrate/frame/support/test/tests/runtime_ui/invalid_pallet_index.stderr new file mode 100644 index 0000000000000000000000000000000000000000..1fbd086ddbe7e243dff03df1a7e91dc9d911964d --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/invalid_pallet_index.stderr @@ -0,0 +1,5 @@ +error: expected integer literal + --> tests/runtime_ui/invalid_pallet_index.rs:24:29 + | +24 | #[runtime::pallet_index("0")] + | ^^^ diff --git a/substrate/frame/support/test/tests/runtime_ui/invalid_runtime_type_derive.rs b/substrate/frame/support/test/tests/runtime_ui/invalid_runtime_type_derive.rs new file mode 100644 index 0000000000000000000000000000000000000000..89ba2f23bb1afbde0ff2936acd7910b2b761fff7 --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/invalid_runtime_type_derive.rs @@ -0,0 +1,25 @@ +// 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. + +#[frame_support::runtime] +mod runtime { + #[runtime::runtime] + #[runtime::derive(RuntimeInfo)] + pub struct Runtime; +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/runtime_ui/invalid_runtime_type_derive.stderr b/substrate/frame/support/test/tests/runtime_ui/invalid_runtime_type_derive.stderr new file mode 100644 index 0000000000000000000000000000000000000000..0b128c3dd4579ba93f7950f42f0ff97440eadd38 --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/invalid_runtime_type_derive.stderr @@ -0,0 +1,5 @@ +error: expected one of: `RuntimeCall`, `RuntimeEvent`, `RuntimeError`, `RuntimeOrigin`, `RuntimeFreezeReason`, `RuntimeHoldReason`, `RuntimeSlashReason`, `RuntimeLockId`, `RuntimeTask` + --> tests/runtime_ui/invalid_runtime_type_derive.rs:21:23 + | +21 | #[runtime::derive(RuntimeInfo)] + | ^^^^^^^^^^^ diff --git a/substrate/frame/support/test/tests/runtime_ui/missing_runtime.rs b/substrate/frame/support/test/tests/runtime_ui/missing_runtime.rs new file mode 100644 index 0000000000000000000000000000000000000000..c5febbe52876008e8a0537fece2b85806d6b323d --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/missing_runtime.rs @@ -0,0 +1,21 @@ +// 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. + +#[frame_support::runtime] +mod runtime {} + +fn main() {} diff --git a/substrate/frame/support/test/tests/runtime_ui/missing_runtime.stderr b/substrate/frame/support/test/tests/runtime_ui/missing_runtime.stderr new file mode 100644 index 0000000000000000000000000000000000000000..9790c5dbc1a9a3f16269c4adea0b2c558bf13c08 --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/missing_runtime.stderr @@ -0,0 +1,5 @@ +error: Missing Runtime. Please add a struct inside the module and annotate it with `#[runtime::runtime]` + --> tests/runtime_ui/missing_runtime.rs:19:1 + | +19 | mod runtime {} + | ^^^ diff --git a/substrate/frame/support/test/tests/runtime_ui/missing_runtime_types_derive.rs b/substrate/frame/support/test/tests/runtime_ui/missing_runtime_types_derive.rs new file mode 100644 index 0000000000000000000000000000000000000000..c3fb0fd454f023fda6b442a5411087caea172c17 --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/missing_runtime_types_derive.rs @@ -0,0 +1,24 @@ +// 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. + +#[frame_support::runtime] +mod runtime { + #[runtime::runtime] + pub struct Runtime; +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/runtime_ui/missing_runtime_types_derive.stderr b/substrate/frame/support/test/tests/runtime_ui/missing_runtime_types_derive.stderr new file mode 100644 index 0000000000000000000000000000000000000000..1a8deebe5497c9c463f52140b8893b9c12529e52 --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/missing_runtime_types_derive.stderr @@ -0,0 +1,5 @@ +error: Missing Runtime Types. Please annotate the runtime struct with `#[runtime::derive]` + --> tests/runtime_ui/missing_runtime_types_derive.rs:19:1 + | +19 | mod runtime { + | ^^^ diff --git a/substrate/frame/support/test/tests/runtime_ui/missing_system_pallet.rs b/substrate/frame/support/test/tests/runtime_ui/missing_system_pallet.rs new file mode 100644 index 0000000000000000000000000000000000000000..f9a15fcf992a459313495562316cfa7c0ff01fde --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/missing_system_pallet.rs @@ -0,0 +1,25 @@ +// 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. + +#[frame_support::runtime] +mod runtime { + #[runtime::runtime] + #[runtime::derive(RuntimeCall)] + pub struct Runtime; +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/runtime_ui/missing_system_pallet.stderr b/substrate/frame/support/test/tests/runtime_ui/missing_system_pallet.stderr new file mode 100644 index 0000000000000000000000000000000000000000..dc3ac4cd5d560d208da05556f256498fd838da37 --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/missing_system_pallet.stderr @@ -0,0 +1,5 @@ +error: `System` pallet declaration is missing. Please add this line: `pub type System = frame_system;` + --> tests/runtime_ui/missing_system_pallet.rs:19:5 + | +19 | mod runtime { + | ^^^^^^^ diff --git a/substrate/frame/support/test/tests/runtime_ui/pass/basic.rs b/substrate/frame/support/test/tests/runtime_ui/pass/basic.rs new file mode 100644 index 0000000000000000000000000000000000000000..514f150180153692caf55ba9b3ecb171ca4e1a2a --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/pass/basic.rs @@ -0,0 +1,37 @@ +// 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. + +use frame_support::derive_impl; + +pub type Block = frame_system::mocking::MockBlock; + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Runtime { + type Block = Block; +} + +#[frame_support::runtime] +mod runtime { + #[runtime::runtime] + #[runtime::derive(RuntimeCall, RuntimeEvent, RuntimeOrigin, RuntimeError, RuntimeTask)] + pub struct Runtime; + + #[runtime::pallet_index(0)] + pub type System = frame_system; +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/runtime_ui/runtime_struct.rs b/substrate/frame/support/test/tests/runtime_ui/runtime_struct.rs new file mode 100644 index 0000000000000000000000000000000000000000..bfa2fb75f04b8ce04d5aabdc83e36414f2dd3c00 --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/runtime_struct.rs @@ -0,0 +1,24 @@ +// 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. + +#[frame_support::runtime] +mod runtime { + #[runtime::runtime] + pub enum Runtime {} +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/runtime_ui/runtime_struct.stderr b/substrate/frame/support/test/tests/runtime_ui/runtime_struct.stderr new file mode 100644 index 0000000000000000000000000000000000000000..04192bcf7f192c833893f8a57536961fcd9b32ca --- /dev/null +++ b/substrate/frame/support/test/tests/runtime_ui/runtime_struct.stderr @@ -0,0 +1,5 @@ +error: Invalid runtime::runtime, expected struct definition + --> tests/runtime_ui/runtime_struct.rs:21:5 + | +21 | pub enum Runtime {} + | ^^^ diff --git a/substrate/frame/support/test/tests/storage_layers.rs b/substrate/frame/support/test/tests/storage_layers.rs index 88edd7de6ca23fd3001640daf1a62d2daa48c757..caa125153e9dc597f8ca859e2e5dd6ac16618992 100644 --- a/substrate/frame/support/test/tests/storage_layers.rs +++ b/substrate/frame/support/test/tests/storage_layers.rs @@ -64,7 +64,7 @@ pub type Header = sp_runtime::generic::Header; pub type Block = sp_runtime::generic::Block; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/frame/support/test/tests/storage_transaction.rs b/substrate/frame/support/test/tests/storage_transaction.rs index c47743308609850ae480f3194773a76c36bbba58..a5bbfd24ab09a8084f58ad47e342174454133ef2 100644 --- a/substrate/frame/support/test/tests/storage_transaction.rs +++ b/substrate/frame/support/test/tests/storage_transaction.rs @@ -41,7 +41,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::generate_store(pub (super) trait Store)] pub struct Pallet(_); #[pallet::config] @@ -88,7 +87,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type Block = Block; diff --git a/substrate/frame/support/test/tests/versioned_migration.rs b/substrate/frame/support/test/tests/versioned_migration.rs index dab5e06dc858f8ff5f7b38d2af411362a15c3442..3fdfb902129ef8b16d82f7d6e4a16000ce0c61e3 100644 --- a/substrate/frame/support/test/tests/versioned_migration.rs +++ b/substrate/frame/support/test/tests/versioned_migration.rs @@ -71,7 +71,7 @@ construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type Block = Block; diff --git a/substrate/frame/system/benches/bench.rs b/substrate/frame/system/benches/bench.rs index a0c567bf85273cb4c6c7839ebd8e509e92b6c9e4..87c5581b2a3409f511f0a8f83a7c7aeae7e7b2c6 100644 --- a/substrate/frame/system/benches/bench.rs +++ b/substrate/frame/system/benches/bench.rs @@ -60,7 +60,7 @@ frame_support::parameter_types! { ); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/frame/system/benchmarking/src/lib.rs b/substrate/frame/system/benchmarking/src/lib.rs index 18bfb85f52dfd4c2b6a5c8a57e78494d97fbdeeb..29100faa75142a8b945bf15f1870e9c3d685d35e 100644 --- a/substrate/frame/system/benchmarking/src/lib.rs +++ b/substrate/frame/system/benchmarking/src/lib.rs @@ -21,7 +21,7 @@ #![cfg(feature = "runtime-benchmarks")] use codec::Encode; -use frame_benchmarking::{impl_benchmark_test_suite, v2::*}; +use frame_benchmarking::v2::*; use frame_support::{dispatch::DispatchClass, storage, traits::Get}; use frame_system::{Call, Pallet as System, RawOrigin}; use sp_core::storage::well_known_keys; diff --git a/substrate/frame/system/benchmarking/src/mock.rs b/substrate/frame/system/benchmarking/src/mock.rs index edbf74a9a51ac9bd90ee65f208c7d96be08430e3..39a64ff6177c38bee487fde66cbf4f7b0df77117 100644 --- a/substrate/frame/system/benchmarking/src/mock.rs +++ b/substrate/frame/system/benchmarking/src/mock.rs @@ -35,7 +35,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/frame/system/src/lib.rs b/substrate/frame/system/src/lib.rs index 069217bcee46b4663c836a750bfd90a568c1a4d2..184f27b61ed2a78fa298e4883db6d4eb39d8a184 100644 --- a/substrate/frame/system/src/lib.rs +++ b/substrate/frame/system/src/lib.rs @@ -130,11 +130,13 @@ use frame_support::{ DispatchResult, DispatchResultWithPostInfo, PerDispatchClass, PostDispatchInfo, }, ensure, impl_ensure_origin_with_arg_ignoring_arg, + migrations::MultiStepMigrator, pallet_prelude::Pays, storage::{self, StorageStreamIter}, traits::{ ConstU32, Contains, EnsureOrigin, EnsureOriginWithArg, Get, HandleLifetime, - OnKilledAccount, OnNewAccount, OriginTrait, PalletInfo, SortedMembers, StoredMap, TypedGet, + OnKilledAccount, OnNewAccount, OnRuntimeUpgrade, OriginTrait, PalletInfo, SortedMembers, + StoredMap, TypedGet, }, Parameter, }; @@ -148,6 +150,7 @@ use sp_io::TestExternalities; pub mod limits; #[cfg(test)] pub(crate) mod mock; + pub mod offchain; mod extensions; @@ -168,6 +171,7 @@ pub use extensions::{ // Backward compatible re-export. pub use extensions::check_mortality::CheckMortality as CheckEra; pub use frame_support::dispatch::RawOrigin; +use frame_support::traits::{PostInherents, PostTransactions, PreInherents}; pub use weights::WeightInfo; const LOG_TARGET: &str = "runtime::system"; @@ -298,9 +302,14 @@ pub mod pallet { type BaseCallFilter = frame_support::traits::Everything; type BlockHashCount = frame_support::traits::ConstU64<10>; type OnSetCode = (); + type SingleBlockMigrations = (); + type MultiBlockMigrator = (); + type PreInherents = (); + type PostInherents = (); + type PostTransactions = (); } - /// Default configurations of this pallet in a solo-chain environment. + /// Default configurations of this pallet in a solochain environment. /// /// ## Considerations: /// @@ -392,6 +401,11 @@ pub mod pallet { /// The set code logic, just the default since we're not a parachain. type OnSetCode = (); + type SingleBlockMigrations = (); + type MultiBlockMigrator = (); + type PreInherents = (); + type PostInherents = (); + type PostTransactions = (); } /// Default configurations of this pallet in a relay-chain environment. @@ -451,6 +465,7 @@ pub mod pallet { + Clone + OriginTrait; + #[docify::export(system_runtime_call)] /// The aggregated `RuntimeCall` type. #[pallet::no_default_bounds] type RuntimeCall: Parameter @@ -523,7 +538,7 @@ pub mod pallet { #[pallet::constant] type DbWeight: Get; - /// Get the chain's current version. + /// Get the chain's in-code version. #[pallet::constant] type Version: Get; @@ -570,6 +585,35 @@ pub mod pallet { /// The maximum number of consumers allowed on a single account. type MaxConsumers: ConsumerLimits; + + /// All migrations that should run in the next runtime upgrade. + /// + /// These used to be formerly configured in `Executive`. Parachains need to ensure that + /// running all these migrations in one block will not overflow the weight limit of a block. + /// The migrations are run *before* the pallet `on_runtime_upgrade` hooks, just like the + /// `OnRuntimeUpgrade` migrations. + type SingleBlockMigrations: OnRuntimeUpgrade; + + /// The migrator that is used to run Multi-Block-Migrations. + /// + /// Can be set to [`pallet-migrations`] or an alternative implementation of the interface. + /// The diagram in `frame_executive::block_flowchart` explains when it runs. + type MultiBlockMigrator: MultiStepMigrator; + + /// A callback that executes in *every block* directly before all inherents were applied. + /// + /// See `frame_executive::block_flowchart` for a in-depth explanation when it runs. + type PreInherents: PreInherents; + + /// A callback that executes in *every block* directly after all inherents were applied. + /// + /// See `frame_executive::block_flowchart` for a in-depth explanation when it runs. + type PostInherents: PostInherents; + + /// A callback that executes in *every block* directly after all transactions were applied. + /// + /// See `frame_executive::block_flowchart` for a in-depth explanation when it runs. + type PostTransactions: PostTransactions; } #[pallet::pallet] @@ -617,6 +661,9 @@ pub mod pallet { } /// Set the new runtime code without doing any checks of the given `code`. + /// + /// Note that runtime upgrades will not run if this is called with a not-increasing spec + /// version! #[pallet::call_index(3)] #[pallet::weight((T::SystemWeightInfo::set_code(), DispatchClass::Operational))] pub fn set_code_without_checks( @@ -812,6 +859,8 @@ pub mod pallet { NonZeroRefCount, /// The origin filter prevent the call to be dispatched. CallFiltered, + /// A multi-block migration is ongoing and prevents the current code from being replaced. + MultiBlockMigrationsOngoing, #[cfg(feature = "experimental")] /// The specified [`Task`] is not valid. InvalidTask, @@ -843,11 +892,15 @@ pub mod pallet { #[pallet::storage] pub(super) type ExtrinsicCount = StorageValue<_, u32>; + /// Whether all inherents have been applied. + #[pallet::storage] + pub type InherentsApplied = StorageValue<_, bool, ValueQuery>; + /// The current weight for the block. #[pallet::storage] #[pallet::whitelist_storage] #[pallet::getter(fn block_weight)] - pub(super) type BlockWeight = StorageValue<_, ConsumedWeight, ValueQuery>; + pub type BlockWeight = StorageValue<_, ConsumedWeight, ValueQuery>; /// Total length (in bytes) for all extrinsics put together, for the current block. #[pallet::storage] @@ -892,6 +945,7 @@ pub mod pallet { /// just in case someone still reads them from within the runtime. #[pallet::storage] #[pallet::whitelist_storage] + #[pallet::disable_try_decode_storage] #[pallet::unbounded] pub(super) type Events = StorageValue<_, Vec>>, ValueQuery>; @@ -1370,6 +1424,19 @@ impl Pallet { Self::deposit_event(Event::CodeUpdated); } + /// Whether all inherents have been applied. + pub fn inherents_applied() -> bool { + InherentsApplied::::get() + } + + /// Note that all inherents have been applied. + /// + /// Should be called immediately after all inherents have been applied. Must be called at least + /// once per block. + pub fn note_inherents_applied() { + InherentsApplied::::put(true); + } + /// Increment the reference counter on an account. #[deprecated = "Use `inc_consumers` instead"] pub fn inc_ref(who: &T::AccountId) { @@ -1689,6 +1756,7 @@ impl Pallet { >::put(digest); >::put(parent_hash); >::insert(*number - One::one(), parent_hash); + >::kill(); // Remove previous block data from storage BlockWeight::::kill(); @@ -1735,6 +1803,7 @@ impl Pallet { ExecutionPhase::::kill(); AllExtrinsicsLen::::kill(); storage::unhashed::kill(well_known_keys::INTRABLOCK_ENTROPY); + InherentsApplied::::kill(); // The following fields // @@ -1978,10 +2047,14 @@ impl Pallet { /// Determine whether or not it is possible to update the code. /// - /// Checks the given code if it is a valid runtime wasm blob by instantianting + /// Checks the given code if it is a valid runtime wasm blob by instantiating /// it and extracting the runtime version of it. It checks that the runtime version /// of the old and new runtime has the same spec name and that the spec version is increasing. pub fn can_set_code(code: &[u8]) -> Result<(), sp_runtime::DispatchError> { + if T::MultiBlockMigrator::ongoing() { + return Err(Error::::MultiBlockMigrationsOngoing.into()) + } + let current_version = T::Version::get(); let new_version = sp_io::misc::runtime_version(code) .and_then(|v| RuntimeVersion::decode(&mut &v[..]).ok()) diff --git a/substrate/frame/system/src/limits.rs b/substrate/frame/system/src/limits.rs index 5fd7a5af87571ff3a77442c1588a6f1fa5bee686..ab5a98a6b9745c044bb5d22847fb6db16cd4d573 100644 --- a/substrate/frame/system/src/limits.rs +++ b/substrate/frame/system/src/limits.rs @@ -378,7 +378,7 @@ impl BlockWeightsBuilder { /// class, based on the allowance. /// /// This is to make sure that extrinsics don't stay forever in the pool, - /// because they could seamingly fit the block (since they are below `max_block`), + /// because they could seemingly fit the block (since they are below `max_block`), /// but the cost of calling `on_initialize` always prevents them from being included. pub fn avg_block_initialization(mut self, init_cost: Perbill) -> Self { self.init_cost = Some(init_cost); diff --git a/substrate/frame/system/src/mock.rs b/substrate/frame/system/src/mock.rs index c4108099e39ffdf306d7a9e5e0af8a619336eaea..e1959e572e99cae45dcdf4960139298defbf81b2 100644 --- a/substrate/frame/system/src/mock.rs +++ b/substrate/frame/system/src/mock.rs @@ -78,7 +78,7 @@ impl OnKilledAccount for RecordKilled { } } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl Config for Test { type BlockWeights = RuntimeBlockWeights; type BlockLength = RuntimeBlockLength; @@ -86,6 +86,22 @@ impl Config for Test { type Version = Version; type AccountData = u32; type OnKilledAccount = RecordKilled; + type MultiBlockMigrator = MockedMigrator; +} + +parameter_types! { + pub static Ongoing: bool = false; +} + +pub struct MockedMigrator; +impl frame_support::migrations::MultiStepMigrator for MockedMigrator { + fn ongoing() -> bool { + Ongoing::get() + } + + fn step() -> Weight { + Weight::zero() + } } pub type SysEvent = frame_system::Event; diff --git a/substrate/frame/system/src/offchain.rs b/substrate/frame/system/src/offchain.rs index a019cfd666e8cb3ddccc02aa706692cbe6cee2e3..a64b326196403cb87e0970c061ac09e42136d4e1 100644 --- a/substrate/frame/system/src/offchain.rs +++ b/substrate/frame/system/src/offchain.rs @@ -22,7 +22,7 @@ //! This module provides transaction related helpers to: //! - Submit a raw unsigned transaction //! - Submit an unsigned transaction with a signed payload -//! - Submit a signed transction. +//! - Submit a signed transaction. //! //! ## Usage //! @@ -384,7 +384,7 @@ where /// /// // runtime-specific public key /// type Public = MultiSigner: From; -/// type Signature = MulitSignature: From; +/// type Signature = MultiSignature: From; /// ``` // TODO [#5662] Potentially use `IsWrappedBy` types, or find some other way to make it easy to // obtain unwrapped crypto (and wrap it back). @@ -444,7 +444,7 @@ pub trait SigningTypes: crate::Config { /// A public key that is capable of identifying `AccountId`s. /// /// Usually that's either a raw crypto public key (e.g. `sr25519::Public`) or - /// an aggregate type for multiple crypto public keys, like `MulitSigner`. + /// an aggregate type for multiple crypto public keys, like `MultiSigner`. type Public: Clone + PartialEq + IdentifyAccount diff --git a/substrate/frame/system/src/tests.rs b/substrate/frame/system/src/tests.rs index e437e7f9f39b0845d9f7eef33e3c90546c8dbbd2..b889b5ca046efe557e7706870c11febce8a358b2 100644 --- a/substrate/frame/system/src/tests.rs +++ b/substrate/frame/system/src/tests.rs @@ -675,6 +675,28 @@ fn set_code_with_real_wasm_blob() { }); } +#[test] +fn set_code_rejects_during_mbm() { + Ongoing::set(true); + + let executor = substrate_test_runtime_client::new_native_or_wasm_executor(); + let mut ext = new_test_ext(); + ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(executor)); + ext.execute_with(|| { + System::set_block_number(1); + let res = System::set_code( + RawOrigin::Root.into(), + substrate_test_runtime_client::runtime::wasm_binary_unwrap().to_vec(), + ); + assert_eq!( + res, + Err(DispatchErrorWithPostInfo::from(Error::::MultiBlockMigrationsOngoing)) + ); + + assert!(System::events().is_empty()); + }); +} + #[test] fn set_code_via_authorization_works() { let executor = substrate_test_runtime_client::new_native_or_wasm_executor(); diff --git a/substrate/frame/timestamp/src/lib.rs b/substrate/frame/timestamp/src/lib.rs index a62ac6d633d043d5a62cbd9f27639fc6ba0ac4bf..5269f17eca6b2151a787f91a1126c822aaf83879 100644 --- a/substrate/frame/timestamp/src/lib.rs +++ b/substrate/frame/timestamp/src/lib.rs @@ -154,7 +154,7 @@ pub mod pallet { /// Default prelude sensible to be used in a testing environment. pub struct TestDefaultConfig; - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig, no_aggregated_types)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)] impl frame_system::DefaultConfig for TestDefaultConfig {} #[frame_support::register_default_impl(TestDefaultConfig)] diff --git a/substrate/frame/timestamp/src/mock.rs b/substrate/frame/timestamp/src/mock.rs index 244b66a4bb2c64428011b1e90fea0ec76d5ba6f2..eb4fdbe71fa7fb54fecb26bd99e977cc57455883 100644 --- a/substrate/frame/timestamp/src/mock.rs +++ b/substrate/frame/timestamp/src/mock.rs @@ -35,7 +35,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } diff --git a/substrate/frame/tips/src/lib.rs b/substrate/frame/tips/src/lib.rs index 4c7cfc3028a9f04c87022ffc89892b59ce3c5610..8c360fb57d72488553529ab4264eb7f8b444c064 100644 --- a/substrate/frame/tips/src/lib.rs +++ b/substrate/frame/tips/src/lib.rs @@ -122,7 +122,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] diff --git a/substrate/frame/tips/src/migrations/mod.rs b/substrate/frame/tips/src/migrations/mod.rs index 9cdd01c17fbf6009e306caff31079ca0eb7a6ea1..a7917bfce16fb0057c184b2b736ee5788f3a2fe5 100644 --- a/substrate/frame/tips/src/migrations/mod.rs +++ b/substrate/frame/tips/src/migrations/mod.rs @@ -17,7 +17,7 @@ /// Version 4. /// -/// For backward compatability reasons, pallet-tips uses `Treasury` for storage module prefix +/// For backward compatibility reasons, pallet-tips uses `Treasury` for storage module prefix /// before calling this migration. After calling this migration, it will get replaced with /// own storage identifier. pub mod v4; diff --git a/substrate/frame/tips/src/tests.rs b/substrate/frame/tips/src/tests.rs index 0e7ea1f47817c5ae14dace2dce7ce7e555a68a03..78df3736815a11dcc8a766e6eb88c4972157a7b3 100644 --- a/substrate/frame/tips/src/tests.rs +++ b/substrate/frame/tips/src/tests.rs @@ -57,7 +57,7 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type AccountId = u128; // u64 is not enough to hold bytes used to generate bounty account type Lookup = IdentityLookup; diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs index c8bf2eb8f440fd1203796fd89d9265403b3fdad0..bd9565fe1887c3b1a117c62fae25a2744586318a 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs @@ -84,7 +84,7 @@ parameter_types! { pub static TransactionByteFee: u64 = 1; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = BlockWeights; @@ -168,6 +168,7 @@ impl OnUnbalanced> for DealWithFees } } +#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)] impl pallet_transaction_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type OnChargeTransaction = CurrencyAdapter; diff --git a/substrate/frame/transaction-payment/asset-tx-payment/src/mock.rs b/substrate/frame/transaction-payment/asset-tx-payment/src/mock.rs index 1f335b4f6c4ab5cc59a4815a2160328aa7f584df..4387f319c3b542a84902d24faabd4fc955b9b031 100644 --- a/substrate/frame/transaction-payment/asset-tx-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/asset-tx-payment/src/mock.rs @@ -71,7 +71,7 @@ parameter_types! { pub static TransactionByteFee: u64 = 1; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = BlockWeights; @@ -136,6 +136,7 @@ impl WeightToFeeT for TransactionByteFee { } } +#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)] impl pallet_transaction_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type OnChargeTransaction = CurrencyAdapter; diff --git a/substrate/frame/transaction-payment/skip-feeless-payment/src/mock.rs b/substrate/frame/transaction-payment/skip-feeless-payment/src/mock.rs index 17c4c773997eb591c1f8aac06a607c3e35df8db0..5f26680af375473e3868babfaea59dfb730f4d84 100644 --- a/substrate/frame/transaction-payment/skip-feeless-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/skip-feeless-payment/src/mock.rs @@ -22,7 +22,7 @@ use frame_system as system; type Block = frame_system::mocking::MockBlock; type AccountId = u64; -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type Block = Block; } diff --git a/substrate/frame/transaction-payment/src/lib.rs b/substrate/frame/transaction-payment/src/lib.rs index efadfd60bdd30126f8627fccea7c02f4c42c68a3..0e440ee4e9ff5f503086a0e5c139a89fdf01a5be 100644 --- a/substrate/frame/transaction-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/src/lib.rs @@ -326,7 +326,7 @@ pub mod pallet { /// Default prelude sensible to be used in a testing environment. pub struct TestDefaultConfig; - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig, no_aggregated_types)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)] impl frame_system::DefaultConfig for TestDefaultConfig {} #[frame_support::register_default_impl(TestDefaultConfig)] diff --git a/substrate/frame/transaction-payment/src/mock.rs b/substrate/frame/transaction-payment/src/mock.rs index 1ca2e3d734720455f8a2fdc9453c712d1f199796..67ad1caa02ff8d05acd4a33d671aa7e26daae253 100644 --- a/substrate/frame/transaction-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/src/mock.rs @@ -70,7 +70,7 @@ parameter_types! { pub static OperationalFeeMultiplier: u8 = 5; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = BlockWeights; diff --git a/substrate/frame/transaction-payment/src/tests.rs b/substrate/frame/transaction-payment/src/tests.rs index d3a1721ccb9909b2f2a705d9bb9ddc9ebc7cb5ee..bc0efd2d64a3b27f5e280a0318f1e5822d583e47 100644 --- a/substrate/frame/transaction-payment/src/tests.rs +++ b/substrate/frame/transaction-payment/src/tests.rs @@ -384,7 +384,7 @@ fn query_call_info_and_fee_details_works() { adjusted_weight_fee: info .weight .min(BlockWeights::get().max_block) - .ref_time() as u64 * 2 * 3 / 2 /* weight * weight_fee * multipler */ + .ref_time() as u64 * 2 * 3 / 2 /* weight * weight_fee * multiplier */ }), tip: 0, }, diff --git a/substrate/frame/transaction-storage/README.md b/substrate/frame/transaction-storage/README.md index 1066968469d4b7c629ac7243b9e7e20febdbdbab..b173c0a84d5a0b387a14abe0415733c603a7010a 100644 --- a/substrate/frame/transaction-storage/README.md +++ b/substrate/frame/transaction-storage/README.md @@ -79,7 +79,7 @@ ipfs block get /ipfs/ > kitten.jpeg ``` To renew data and prevent it from being disposed after the storage period, use `transactionStorage.renew(block, index)` -where `block` is the block number of the previous store or renew transction, and index is the index of that transaction +where `block` is the block number of the previous store or renew transaction, and index is the index of that transaction in the block. diff --git a/substrate/frame/transaction-storage/src/lib.rs b/substrate/frame/transaction-storage/src/lib.rs index d32cfe169ce0b3f3ef76040ea596673e7d02d2c7..398cb350c501ee18dd25073b4047b5045b69fa49 100644 --- a/substrate/frame/transaction-storage/src/lib.rs +++ b/substrate/frame/transaction-storage/src/lib.rs @@ -137,7 +137,7 @@ pub mod pallet { InvalidProof, /// Missing storage proof. MissingProof, - /// Unable to verify proof becasue state data is missing. + /// Unable to verify proof because state data is missing. MissingStateData, /// Double proof check in the block. DoubleCheck, diff --git a/substrate/frame/transaction-storage/src/mock.rs b/substrate/frame/transaction-storage/src/mock.rs index 31f539327cda2e5a8bd4593d3cbbbecc004ae296..f1e9e0591f6f521bad4fda1cb4ae4aa879efc483 100644 --- a/substrate/frame/transaction-storage/src/mock.rs +++ b/substrate/frame/transaction-storage/src/mock.rs @@ -39,7 +39,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; @@ -48,7 +48,7 @@ impl frame_system::Config for Test { type Lookup = IdentityLookup; } -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for Test { type AccountStore = System; } diff --git a/substrate/frame/transaction-storage/src/tests.rs b/substrate/frame/transaction-storage/src/tests.rs index e17b3ca3bebd596f1d7071c45c986820169c4f94..621f74804eccae4b06f2d1f96e238334d038e68d 100644 --- a/substrate/frame/transaction-storage/src/tests.rs +++ b/substrate/frame/transaction-storage/src/tests.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Tests for transction-storage pallet. +//! Tests for transaction-storage pallet. use super::{Pallet as TransactionStorage, *}; use crate::mock::*; @@ -53,8 +53,8 @@ fn discards_data() { }; run_to_block(11, proof_provider); assert!(Transactions::::get(1).is_some()); - let transctions = Transactions::::get(1).unwrap(); - assert_eq!(transctions.len(), 2); + let transactions = Transactions::::get(1).unwrap(); + assert_eq!(transactions.len(), 2); assert_eq!(ChunkCount::::get(1), 16); run_to_block(12, proof_provider); assert!(Transactions::::get(1).is_none()); diff --git a/substrate/frame/treasury/src/lib.rs b/substrate/frame/treasury/src/lib.rs index 5e429d3914bd0c60ea24eb53825c4da4b6a07fe5..d569ae406ea39aa9734d145968924ba1309e2f60 100644 --- a/substrate/frame/treasury/src/lib.rs +++ b/substrate/frame/treasury/src/lib.rs @@ -621,7 +621,7 @@ pub mod pallet { with_context::>, _>(|v| { let context = v.or_default(); - // We group based on `max_amount`, to dinstinguish between different kind of + // We group based on `max_amount`, to distinguish between different kind of // origins. (assumes that all origins have different `max_amount`) // // Worst case is that we reject some "valid" request. @@ -1042,7 +1042,7 @@ impl, I: 'static> Pallet { /// ### Invariants of proposal storage items /// /// 1. [`ProposalCount`] >= Number of elements in [`Proposals`]. - /// 2. Each entry in [`Proposals`] should be saved under a key stricly less than current + /// 2. Each entry in [`Proposals`] should be saved under a key strictly less than current /// [`ProposalCount`]. /// 3. Each [`ProposalIndex`] contained in [`Approvals`] should exist in [`Proposals`]. /// Note, that this automatically implies [`Approvals`].count() <= [`Proposals`].count(). @@ -1078,7 +1078,7 @@ impl, I: 'static> Pallet { /// ## Invariants of spend storage items /// /// 1. [`SpendCount`] >= Number of elements in [`Spends`]. - /// 2. Each entry in [`Spends`] should be saved under a key stricly less than current + /// 2. Each entry in [`Spends`] should be saved under a key strictly less than current /// [`SpendCount`]. /// 3. For each spend entry contained in [`Spends`] we should have spend.expire_at /// > spend.valid_from. diff --git a/substrate/frame/treasury/src/tests.rs b/substrate/frame/treasury/src/tests.rs index b488300de99d5d0c77d8496b597f71c03935b61e..67d81cb5c30224e68a2b72bcda3255273304b14a 100644 --- a/substrate/frame/treasury/src/tests.rs +++ b/substrate/frame/treasury/src/tests.rs @@ -53,7 +53,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type AccountId = u128; // u64 is not enough to hold bytes used to generate bounty account type Lookup = IdentityLookup; diff --git a/substrate/frame/tx-pause/src/mock.rs b/substrate/frame/tx-pause/src/mock.rs index 8ccdc43a46cdb1b2865c768a81dc69453820d9d3..5206023838b95290d1a153b37074dccb0b5635d9 100644 --- a/substrate/frame/tx-pause/src/mock.rs +++ b/substrate/frame/tx-pause/src/mock.rs @@ -36,7 +36,7 @@ use sp_runtime::{ parameter_types! { pub const BlockHashCount: u64 = 250; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = InsideBoth; type BlockWeights = (); diff --git a/substrate/frame/uniques/src/mock.rs b/substrate/frame/uniques/src/mock.rs index eae125971635ec7d3b6ed7a8559e0a5e018592f7..9fd7f87e159bb29bb7206fcba05b573ccc140115 100644 --- a/substrate/frame/uniques/src/mock.rs +++ b/substrate/frame/uniques/src/mock.rs @@ -37,7 +37,7 @@ construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; diff --git a/substrate/frame/uniques/src/tests.rs b/substrate/frame/uniques/src/tests.rs index 351dac09f7f202a62df508fe71d26bb24662d5bc..afd0352bf90eb27164f3a714393e33d1274da1f9 100644 --- a/substrate/frame/uniques/src/tests.rs +++ b/substrate/frame/uniques/src/tests.rs @@ -289,7 +289,7 @@ fn transfer_owner_should_work() { assert_eq!(Balances::reserved_balance(&2), 0); assert_eq!(Balances::reserved_balance(&3), 45); - // 2's acceptence from before is reset when it became owner, so it cannot be transfered + // 2's acceptance from before is reset when it became owner, so it cannot be transferred // without a fresh acceptance. assert_noop!( Uniques::transfer_ownership(RuntimeOrigin::signed(3), 0, 2), @@ -692,7 +692,7 @@ fn approved_account_gets_reset_after_transfer() { assert_ok!(Uniques::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3)); assert_ok!(Uniques::transfer(RuntimeOrigin::signed(2), 0, 42, 5)); - // this shouldn't work because we have just transfered the item to another account. + // this shouldn't work because we have just transferred the item to another account. assert_noop!( Uniques::transfer(RuntimeOrigin::signed(3), 0, 42, 4), Error::::NoPermission diff --git a/substrate/frame/utility/README.md b/substrate/frame/utility/README.md index 00fff76cd626bcb119f971edb910c033024b54c8..0a6769ae1c7c14a8480cd58a43c6a2e495a10906 100644 --- a/substrate/frame/utility/README.md +++ b/substrate/frame/utility/README.md @@ -17,7 +17,7 @@ This module contains two basic pieces of functionality: need multiple distinct accounts (e.g. as controllers for many staking accounts), but where it's perfectly fine to have each of them controlled by the same underlying keypair. Derivative accounts are, for the purposes of proxy filtering considered exactly the same as - the oigin and are thus hampered with the origin's filters. + the origin and are thus hampered with the origin's filters. Since proxy filters are respected in all dispatches of this module, it should never need to be filtered by any proxy. diff --git a/substrate/frame/utility/src/tests.rs b/substrate/frame/utility/src/tests.rs index 8742513be95043186dbe840540247fe7160fdfaa..9bcbec99f3b441669eaf68433f0e88b3f5117502 100644 --- a/substrate/frame/utility/src/tests.rs +++ b/substrate/frame/utility/src/tests.rs @@ -143,7 +143,7 @@ parameter_types! { pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::simple_max(Weight::MAX); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = TestBaseCallFilter; type BlockWeights = BlockWeights; diff --git a/substrate/frame/vesting/src/migrations.rs b/substrate/frame/vesting/src/migrations.rs index cac3c90b403ab4b1e037e140c0598aa83d69816e..6fe82312b637d550bfe164c62155bb9261e75801 100644 --- a/substrate/frame/vesting/src/migrations.rs +++ b/substrate/frame/vesting/src/migrations.rs @@ -29,7 +29,7 @@ pub mod v1 { log::debug!( target: "runtime::vesting", - "migration: Vesting storage version v1 PRE migration checks succesful!" + "migration: Vesting storage version v1 PRE migration checks successful!" ); Ok(()) diff --git a/substrate/frame/vesting/src/mock.rs b/substrate/frame/vesting/src/mock.rs index 8a0cd1351253663983774a7a79d17dd60583b3f5..674a6f6e2a83677eae30fae445c05def44741e12 100644 --- a/substrate/frame/vesting/src/mock.rs +++ b/substrate/frame/vesting/src/mock.rs @@ -35,7 +35,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type AccountData = pallet_balances::AccountData; type Block = Block; diff --git a/substrate/frame/whitelist/src/benchmarking.rs b/substrate/frame/whitelist/src/benchmarking.rs index 9d356f09a9d2ec411c6af5ae73940741319ab590..7fb5632fc00247648be3238775735d0b2fdc2241 100644 --- a/substrate/frame/whitelist/src/benchmarking.rs +++ b/substrate/frame/whitelist/src/benchmarking.rs @@ -20,58 +20,57 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::v1::{benchmarks, BenchmarkError}; -use frame_support::{ensure, traits::EnsureOrigin}; +use frame_benchmarking::v2::*; +use frame_support::traits::EnsureOrigin; #[cfg(test)] use crate::Pallet as Whitelist; -benchmarks! { - whitelist_call { +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn whitelist_call() -> Result<(), BenchmarkError> { let origin = T::WhitelistOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call_hash = Default::default(); - }: _(origin, call_hash) - verify { - ensure!( - WhitelistedCall::::contains_key(call_hash), - "call not whitelisted" - ); - ensure!( - T::Preimages::is_requested(&call_hash), - "preimage not requested" - ); + + #[extrinsic_call] + _(origin as T::RuntimeOrigin, call_hash); + + ensure!(WhitelistedCall::::contains_key(call_hash), "call not whitelisted"); + ensure!(T::Preimages::is_requested(&call_hash), "preimage not requested"); + Ok(()) } - remove_whitelisted_call { + #[benchmark] + fn remove_whitelisted_call() -> Result<(), BenchmarkError> { let origin = T::WhitelistOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call_hash = Default::default(); Pallet::::whitelist_call(origin.clone(), call_hash) .expect("whitelisting call must be successful"); - }: _(origin, call_hash) - verify { - ensure!( - !WhitelistedCall::::contains_key(call_hash), - "whitelist not removed" - ); - ensure!( - !T::Preimages::is_requested(&call_hash), - "preimage still requested" - ); + + #[extrinsic_call] + _(origin as T::RuntimeOrigin, call_hash); + + ensure!(!WhitelistedCall::::contains_key(call_hash), "whitelist not removed"); + ensure!(!T::Preimages::is_requested(&call_hash), "preimage still requested"); + Ok(()) } // We benchmark with the maximum possible size for a call. // If the resulting weight is too big, maybe it worth having a weight which depends // on the size of the call, with a new witness in parameter. - #[pov_mode = MaxEncodedLen { + #[benchmark(pov_mode = MaxEncodedLen { // Use measured PoV size for the Preimages since we pass in a length witness. Preimage::PreimageFor: Measured - }] - dispatch_whitelisted_call { - // NOTE: we remove `10` because we need some bytes to encode the variants and vec length - let n in 1 .. T::Preimages::MAX_LENGTH as u32 - 10; - + })] + // NOTE: we remove `10` because we need some bytes to encode the variants and vec length + fn dispatch_whitelisted_call( + n: Linear<1, { T::Preimages::MAX_LENGTH as u32 - 10 }>, + ) -> Result<(), BenchmarkError> { let origin = T::DispatchWhitelistedOrigin::try_successful_origin() .map_err(|_| BenchmarkError::Weightless)?; let remark = sp_std::vec![1u8; n as usize]; @@ -86,21 +85,16 @@ benchmarks! { T::Preimages::note(encoded_call.into()).unwrap(); - }: _(origin, call_hash, call_encoded_len, call_weight) - verify { - ensure!( - !WhitelistedCall::::contains_key(call_hash), - "whitelist not removed" - ); - ensure!( - !T::Preimages::is_requested(&call_hash), - "preimage still requested" - ); - } + #[extrinsic_call] + _(origin as T::RuntimeOrigin, call_hash, call_encoded_len, call_weight); - dispatch_whitelisted_call_with_preimage { - let n in 1 .. 10_000; + ensure!(!WhitelistedCall::::contains_key(call_hash), "whitelist not removed"); + ensure!(!T::Preimages::is_requested(&call_hash), "preimage still requested"); + Ok(()) + } + #[benchmark] + fn dispatch_whitelisted_call_with_preimage(n: Linear<1, 10_000>) -> Result<(), BenchmarkError> { let origin = T::DispatchWhitelistedOrigin::try_successful_origin() .map_err(|_| BenchmarkError::Weightless)?; let remark = sp_std::vec![1u8; n as usize]; @@ -110,16 +104,13 @@ benchmarks! { Pallet::::whitelist_call(origin.clone(), call_hash) .expect("whitelisting call must be successful"); - }: _(origin, Box::new(call)) - verify { - ensure!( - !WhitelistedCall::::contains_key(call_hash), - "whitelist not removed" - ); - ensure!( - !T::Preimages::is_requested(&call_hash), - "preimage still requested" - ); + + #[extrinsic_call] + _(origin as T::RuntimeOrigin, Box::new(call)); + + ensure!(!WhitelistedCall::::contains_key(call_hash), "whitelist not removed"); + ensure!(!T::Preimages::is_requested(&call_hash), "preimage still requested"); + Ok(()) } impl_benchmark_test_suite!(Whitelist, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/substrate/frame/whitelist/src/mock.rs b/substrate/frame/whitelist/src/mock.rs index e323e806b8152b3765f60af46a59ec151f147154..6fb8711057ef0cd53cc388e07cf90347c0e13000 100644 --- a/substrate/frame/whitelist/src/mock.rs +++ b/substrate/frame/whitelist/src/mock.rs @@ -37,7 +37,7 @@ construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; type AccountData = pallet_balances::AccountData; diff --git a/substrate/primitives/api/proc-macro/src/decl_runtime_apis.rs b/substrate/primitives/api/proc-macro/src/decl_runtime_apis.rs index 2b1e65ec88524a0236a464005fb2d1215c1157fe..e34e4c0e7672164afb41702ab3bae87082df9062 100644 --- a/substrate/primitives/api/proc-macro/src/decl_runtime_apis.rs +++ b/substrate/primitives/api/proc-macro/src/decl_runtime_apis.rs @@ -456,6 +456,7 @@ impl<'a> ToClientSideDecl<'a> { |err| #crate_::ApiError::FailedToDecodeReturnValue { function: #function_name, error: err, + raw: r.clone(), } ) ) diff --git a/substrate/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs b/substrate/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs index c1339ff6621b389e32c2f9ca2a7f19079c2d269e..1761e0ac9dbf450c15adae2659119c918f300334 100644 --- a/substrate/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs +++ b/substrate/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs @@ -158,7 +158,7 @@ fn implement_common_api_traits(block_type: TypePath, self_ty: Type) -> Result::Hash, _: &<#block_type as #crate_::BlockT>::Header, - ) -> std::result::Result<(), #crate_::ApiError> { + ) -> std::result::Result<#crate_::__private::ExtrinsicInclusionMode, #crate_::ApiError> { unimplemented!("`Core::initialize_block` not implemented for runtime api mocks") } } diff --git a/substrate/primitives/api/src/lib.rs b/substrate/primitives/api/src/lib.rs index 190de1ab3fdee0611f27071b9c769a164c3aca9d..a945b9f21f3cff60d6a9d5e24aa07704a1dc8d31 100644 --- a/substrate/primitives/api/src/lib.rs +++ b/substrate/primitives/api/src/lib.rs @@ -101,7 +101,7 @@ pub mod __private { generic::BlockId, traits::{Block as BlockT, Hash as HashT, HashingFor, Header as HeaderT, NumberFor}, transaction_validity::TransactionValidity, - RuntimeString, TransactionOutcome, + ExtrinsicInclusionMode, RuntimeString, TransactionOutcome, }; pub use sp_std::{mem, slice, vec}; pub use sp_version::{create_apis_vec, ApiId, ApisVec, RuntimeVersion}; @@ -115,11 +115,11 @@ pub use sp_core::traits::CallContext; use sp_core::OpaqueMetadata; #[cfg(feature = "std")] use sp_externalities::{Extension, Extensions}; -use sp_runtime::traits::Block as BlockT; #[cfg(feature = "std")] use sp_runtime::traits::HashingFor; #[cfg(feature = "std")] pub use sp_runtime::TransactionOutcome; +use sp_runtime::{traits::Block as BlockT, ExtrinsicInclusionMode}; #[cfg(feature = "std")] pub use sp_state_machine::StorageProof; #[cfg(feature = "std")] @@ -280,7 +280,7 @@ pub use sp_api_proc_macro::decl_runtime_apis; /// ```rust /// use sp_version::create_runtime_str; /// # -/// # use sp_runtime::traits::Block as BlockT; +/// # use sp_runtime::{ExtrinsicInclusionMode, traits::Block as BlockT}; /// # use sp_test_primitives::Block; /// # /// # /// The declaration of the `Runtime` type is done by the `construct_runtime!` macro @@ -307,7 +307,9 @@ pub use sp_api_proc_macro::decl_runtime_apis; /// # unimplemented!() /// # } /// # fn execute_block(_block: Block) {} -/// # fn initialize_block(_header: &::Header) {} +/// # fn initialize_block(_header: &::Header) -> ExtrinsicInclusionMode { +/// # unimplemented!() +/// # } /// # } /// /// impl self::Balance for Runtime { @@ -540,11 +542,12 @@ pub fn init_runtime_logger() { #[cfg(feature = "std")] #[derive(Debug, thiserror::Error)] pub enum ApiError { - #[error("Failed to decode return value of {function}")] + #[error("Failed to decode return value of {function}: {error} raw data: {raw:?}")] FailedToDecodeReturnValue { function: &'static str, #[source] error: codec::Error, + raw: Vec, }, #[error("Failed to convert return value from runtime to node of {function}")] FailedToConvertReturnValue { @@ -800,15 +803,18 @@ pub fn deserialize_runtime_api_info(bytes: [u8; RUNTIME_API_INFO_SIZE]) -> ([u8; decl_runtime_apis! { /// The `Core` runtime api that every Substrate runtime needs to implement. #[core_trait] - #[api_version(4)] + #[api_version(5)] pub trait Core { /// Returns the version of the runtime. fn version() -> RuntimeVersion; /// Execute the given block. fn execute_block(block: Block); /// Initialize a block with the given header. + #[changed_in(5)] #[renamed("initialise_block", 2)] fn initialize_block(header: &::Header); + /// Initialize a block with the given header and return the runtime executive mode. + fn initialize_block(header: &::Header) -> ExtrinsicInclusionMode; } /// The `Metadata` api trait that returns metadata for the runtime. diff --git a/substrate/primitives/api/test/tests/decl_and_impl.rs b/substrate/primitives/api/test/tests/decl_and_impl.rs index d68470551d20061aa09d277a9999db81654ff47d..211a08561fd4bcf601d9508d96447fc07f82657f 100644 --- a/substrate/primitives/api/test/tests/decl_and_impl.rs +++ b/substrate/primitives/api/test/tests/decl_and_impl.rs @@ -139,7 +139,7 @@ impl_runtime_apis! { fn execute_block(_: Block) { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/substrate/primitives/api/test/tests/ui/impl_incorrect_method_signature.rs b/substrate/primitives/api/test/tests/ui/impl_incorrect_method_signature.rs index 43718e4cd04a3f25fcc805edec3c14283f1f5f56..262a874213a5662c0c87a79989b620c661e707c7 100644 --- a/substrate/primitives/api/test/tests/ui/impl_incorrect_method_signature.rs +++ b/substrate/primitives/api/test/tests/ui/impl_incorrect_method_signature.rs @@ -40,7 +40,7 @@ sp_api::impl_runtime_apis! { fn execute_block(_: Block) { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/substrate/primitives/api/test/tests/ui/impl_missing_version.rs b/substrate/primitives/api/test/tests/ui/impl_missing_version.rs index 560257b5168c921ce112a56fcbd7e9c303363c77..58850ab343fbb7bcc2c04b01c7aec9cb881023d0 100644 --- a/substrate/primitives/api/test/tests/ui/impl_missing_version.rs +++ b/substrate/primitives/api/test/tests/ui/impl_missing_version.rs @@ -45,7 +45,7 @@ sp_api::impl_runtime_apis! { fn execute_block(_: Block) { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/substrate/primitives/api/test/tests/ui/missing_versioned_method.rs b/substrate/primitives/api/test/tests/ui/missing_versioned_method.rs index 6ead545f85a11e8f0382331e77145acb4a053d5e..70f75d065154afcb807e1712c95fc24eca5267b1 100644 --- a/substrate/primitives/api/test/tests/ui/missing_versioned_method.rs +++ b/substrate/primitives/api/test/tests/ui/missing_versioned_method.rs @@ -44,7 +44,7 @@ sp_api::impl_runtime_apis! { fn execute_block(_: Block) { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/substrate/primitives/api/test/tests/ui/missing_versioned_method_multiple_vers.rs b/substrate/primitives/api/test/tests/ui/missing_versioned_method_multiple_vers.rs index 8eebc1d79babcbd6fb95876a530a920397c035a6..63032000040b7d19f8e31fc4ac3221369aef9e7e 100644 --- a/substrate/primitives/api/test/tests/ui/missing_versioned_method_multiple_vers.rs +++ b/substrate/primitives/api/test/tests/ui/missing_versioned_method_multiple_vers.rs @@ -47,7 +47,7 @@ sp_api::impl_runtime_apis! { fn execute_block(_: Block) { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/substrate/primitives/api/test/tests/ui/positive_cases/custom_where_bound.rs b/substrate/primitives/api/test/tests/ui/positive_cases/custom_where_bound.rs index 594556d57be50dbd151de486dc22a63a733b933b..0858813bc99941be005e0c0141b2defc3a96a3d3 100644 --- a/substrate/primitives/api/test/tests/ui/positive_cases/custom_where_bound.rs +++ b/substrate/primitives/api/test/tests/ui/positive_cases/custom_where_bound.rs @@ -51,7 +51,7 @@ sp_api::impl_runtime_apis! { fn execute_block(_: Block) { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/substrate/primitives/api/test/tests/ui/positive_cases/default_impls.rs b/substrate/primitives/api/test/tests/ui/positive_cases/default_impls.rs index ae573238ffe144d26b7e5dd4528e8a9e5ab88a9e..3e0cb79156c8bf5c218e29e3024d181a69fc4da6 100644 --- a/substrate/primitives/api/test/tests/ui/positive_cases/default_impls.rs +++ b/substrate/primitives/api/test/tests/ui/positive_cases/default_impls.rs @@ -46,7 +46,7 @@ sp_api::impl_runtime_apis! { fn execute_block(_: Block) { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/substrate/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.rs b/substrate/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.rs index 921bf0d04351dac4ddcdd18030619f1ebcf15008..b2caea7ab7e44d910628d0ac834017e66e7e5d2e 100644 --- a/substrate/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.rs +++ b/substrate/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.rs @@ -42,7 +42,7 @@ sp_api::impl_runtime_apis! { fn execute_block(_: Block) { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/substrate/primitives/application-crypto/check-features-variants.sh b/substrate/primitives/application-crypto/check-features-variants.sh new file mode 100755 index 0000000000000000000000000000000000000000..dd45a212bae097beb40171295e254a601674ff00 --- /dev/null +++ b/substrate/primitives/application-crypto/check-features-variants.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env -S bash -eux + +export RUSTFLAGS="-Cdebug-assertions=y -Dwarnings" +T=wasm32-unknown-unknown +cargo check --release +cargo check --release --target=$T --no-default-features +cargo check --release --target=$T --no-default-features --features="full_crypto" +cargo check --release --target=$T --no-default-features --features="serde" +cargo check --release --target=$T --no-default-features --features="serde,full_crypto" +cargo check --release --target=$T --no-default-features --features="bandersnatch-experimental" +cargo check --release --target=$T --no-default-features --features="bls-experimental" +cargo check --release --target=$T --no-default-features --features="bls-experimental,full_crypto" diff --git a/substrate/primitives/application-crypto/src/bls377.rs b/substrate/primitives/application-crypto/src/bls377.rs index ee17060564fa8bec54888c1123b7c86af13863d3..3bd01de139c9496d35b143ddeb07de1285154946 100644 --- a/substrate/primitives/application-crypto/src/bls377.rs +++ b/substrate/primitives/application-crypto/src/bls377.rs @@ -19,14 +19,13 @@ use crate::{KeyTypeId, RuntimePublic}; pub use sp_core::bls::bls377::*; +use sp_std::vec::Vec; mod app { crate::app_crypto!(super, sp_core::testing::BLS377); } -#[cfg(feature = "full_crypto")] -pub use app::Pair as AppPair; -pub use app::{Public as AppPublic, Signature as AppSignature}; +pub use app::{Pair as AppPair, Public as AppPublic, Signature as AppSignature}; impl RuntimePublic for Public { type Signature = Signature; diff --git a/substrate/primitives/application-crypto/src/ecdsa.rs b/substrate/primitives/application-crypto/src/ecdsa.rs index 27ffe12579f5591cf71a0f22cd5bb2702ce83afb..439b51dc604509788bd16b25aea2dbf6201471bd 100644 --- a/substrate/primitives/application-crypto/src/ecdsa.rs +++ b/substrate/primitives/application-crypto/src/ecdsa.rs @@ -27,9 +27,7 @@ mod app { crate::app_crypto!(super, sp_core::testing::ECDSA); } -#[cfg(feature = "full_crypto")] -pub use app::Pair as AppPair; -pub use app::{Public as AppPublic, Signature as AppSignature}; +pub use app::{Pair as AppPair, Public as AppPublic, Signature as AppSignature}; impl RuntimePublic for Public { type Signature = Signature; diff --git a/substrate/primitives/application-crypto/src/ecdsa_bls377.rs b/substrate/primitives/application-crypto/src/ecdsa_bls377.rs index 70940587cedaf6312dbc75834d2180131ca157b4..8dee73095fb2e2fcbeb968edef2aad667112767a 100644 --- a/substrate/primitives/application-crypto/src/ecdsa_bls377.rs +++ b/substrate/primitives/application-crypto/src/ecdsa_bls377.rs @@ -18,6 +18,7 @@ //! ECDSA and BLS12-377 paired crypto applications. use crate::{KeyTypeId, RuntimePublic}; +use sp_std::vec::Vec; pub use sp_core::paired_crypto::ecdsa_bls377::*; diff --git a/substrate/primitives/application-crypto/src/ed25519.rs b/substrate/primitives/application-crypto/src/ed25519.rs index bc05018370edb16c41ed1dddef2dafd7d785cc32..addefe7daf6431415f1c06727b88edd1b3bd9158 100644 --- a/substrate/primitives/application-crypto/src/ed25519.rs +++ b/substrate/primitives/application-crypto/src/ed25519.rs @@ -27,9 +27,7 @@ mod app { crate::app_crypto!(super, sp_core::testing::ED25519); } -#[cfg(feature = "full_crypto")] -pub use app::Pair as AppPair; -pub use app::{Public as AppPublic, Signature as AppSignature}; +pub use app::{Pair as AppPair, Public as AppPublic, Signature as AppSignature}; impl RuntimePublic for Public { type Signature = Signature; diff --git a/substrate/primitives/application-crypto/src/lib.rs b/substrate/primitives/application-crypto/src/lib.rs index 686b486f335301e8750253cb2e306a0f5cb222d4..2355f1ba527d5227a804fc30ae2deccc44df2152 100644 --- a/substrate/primitives/application-crypto/src/lib.rs +++ b/substrate/primitives/application-crypto/src/lib.rs @@ -20,16 +20,13 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] -pub use sp_core::crypto::{key_types, CryptoTypeId, KeyTypeId}; +pub use sp_core::crypto::{key_types, CryptoTypeId, DeriveJunction, KeyTypeId, Ss58Codec}; #[doc(hidden)] -#[cfg(feature = "full_crypto")] pub use sp_core::crypto::{DeriveError, Pair, SecretStringError}; -#[cfg(any(feature = "full_crypto", feature = "serde"))] -pub use sp_core::crypto::{DeriveJunction, Ss58Codec}; #[doc(hidden)] pub use sp_core::{ self, - crypto::{ByteArray, CryptoType, Derive, IsWrappedBy, Public, UncheckedFrom, Wraps}, + crypto::{ByteArray, CryptoType, Derive, IsWrappedBy, Public, Signature, UncheckedFrom, Wraps}, RuntimeDebug, }; @@ -85,7 +82,7 @@ macro_rules! app_crypto { $module::CRYPTO_ID ); $crate::app_crypto_signature_common!($module::Signature, $key_type); - $crate::app_crypto_pair!($module::Pair, $key_type, $module::CRYPTO_ID); + $crate::app_crypto_pair_common!($module::Pair, $key_type, $module::CRYPTO_ID); }; } @@ -116,13 +113,15 @@ macro_rules! app_crypto { $module::CRYPTO_ID ); $crate::app_crypto_signature_common!($module::Signature, $key_type); + $crate::app_crypto_pair_common!($module::Pair, $key_type, $module::CRYPTO_ID); }; } /// Declares `Pair` type which is functionally equivalent to `$pair`, but is /// new application-specific type whose identifier is `$key_type`. +/// It is a common part shared between full_crypto and non full_crypto environments. #[macro_export] -macro_rules! app_crypto_pair { +macro_rules! app_crypto_pair_common { ($pair:ty, $key_type:expr, $crypto_type:expr) => { $crate::wrap! { /// A generic `AppPublic` wrapper type over $pair crypto; this has no specific App. @@ -140,7 +139,14 @@ macro_rules! app_crypto_pair { type Signature = Signature; $crate::app_crypto_pair_functions_if_std!($pair); + $crate::app_crypto_pair_functions_if_full_crypto!($pair); + fn from_phrase( + phrase: &str, + password: Option<&str>, + ) -> Result<(Self, Self::Seed), $crate::SecretStringError> { + <$pair>::from_phrase(phrase, password).map(|r| (Self(r.0), r.1)) + } fn derive>( &self, path: Iter, @@ -154,9 +160,6 @@ macro_rules! app_crypto_pair { fn from_seed_slice(seed: &[u8]) -> Result { <$pair>::from_seed_slice(seed).map(Self) } - fn sign(&self, msg: &[u8]) -> Self::Signature { - Signature(self.0.sign(msg)) - } fn verify>( sig: &Self::Signature, message: M, @@ -203,13 +206,6 @@ macro_rules! app_crypto_pair_functions_if_std { let r = <$pair>::generate_with_phrase(password); (Self(r.0), r.1, r.2) } - - fn from_phrase( - phrase: &str, - password: Option<&str>, - ) -> Result<(Self, Self::Seed), $crate::SecretStringError> { - <$pair>::from_phrase(phrase, password).map(|r| (Self(r.0), r.1)) - } }; } @@ -220,6 +216,25 @@ macro_rules! app_crypto_pair_functions_if_std { ($pair:ty) => {}; } +/// Implements functions for the `Pair` trait when `feature = "full_crypto"` is enabled. +#[doc(hidden)] +#[cfg(feature = "full_crypto")] +#[macro_export] +macro_rules! app_crypto_pair_functions_if_full_crypto { + ($pair:ty) => { + fn sign(&self, msg: &[u8]) -> Self::Signature { + Signature(self.0.sign(msg)) + } + }; +} + +#[doc(hidden)] +#[cfg(not(feature = "full_crypto"))] +#[macro_export] +macro_rules! app_crypto_pair_functions_if_full_crypto { + ($pair:ty) => {}; +} + /// Declares `Public` type which is functionally equivalent to `$public` but is /// new application-specific type whose identifier is `$key_type`. /// For full functionality, `app_crypto_public_common!` must be called too. @@ -267,7 +282,7 @@ macro_rules! app_crypto_public_not_full_crypto { $crate::wrap! { /// A generic `AppPublic` wrapper type over $public crypto; this has no specific App. #[derive( - Clone, Eq, PartialEq, Ord, PartialOrd, + Clone, Eq, Hash, PartialEq, Ord, PartialOrd, $crate::codec::Encode, $crate::codec::Decode, $crate::RuntimeDebug, @@ -277,10 +292,13 @@ macro_rules! app_crypto_public_not_full_crypto { pub struct Public($public); } - impl $crate::CryptoType for Public {} + impl $crate::CryptoType for Public { + type Pair = Pair; + } impl $crate::AppCrypto for Public { type Public = Public; + type Pair = Pair; type Signature = Signature; const ID: $crate::KeyTypeId = $key_type; const CRYPTO_ID: $crate::CryptoTypeId = $crypto_type; @@ -452,10 +470,13 @@ macro_rules! app_crypto_signature_not_full_crypto { pub struct Signature($sig); } - impl $crate::CryptoType for Signature {} + impl $crate::CryptoType for Signature { + type Pair = Pair; + } impl $crate::AppCrypto for Signature { type Public = Public; + type Pair = Pair; type Signature = Signature; const ID: $crate::KeyTypeId = $key_type; const CRYPTO_ID: $crate::CryptoTypeId = $crypto_type; @@ -484,6 +505,12 @@ macro_rules! app_crypto_signature_common { } } + impl AsMut<[u8]> for Signature { + fn as_mut(&mut self) -> &mut [u8] { + self.0.as_mut() + } + } + impl $crate::AppSignature for Signature { type Generic = $sig; } @@ -504,6 +531,12 @@ macro_rules! app_crypto_signature_common { } } + impl $crate::Signature for Signature {} + + impl $crate::ByteArray for Signature { + const LEN: usize = <$sig>::LEN; + } + impl Signature { /// Convert into wrapped generic signature type. pub fn into_inner(self) -> $sig { diff --git a/substrate/primitives/application-crypto/src/sr25519.rs b/substrate/primitives/application-crypto/src/sr25519.rs index 7c91bfa7bb5ffe221a618769f2cb3de3e054d042..d411cc253c0d84fd0bd7f366a7dec86f4c9acc91 100644 --- a/substrate/primitives/application-crypto/src/sr25519.rs +++ b/substrate/primitives/application-crypto/src/sr25519.rs @@ -27,9 +27,7 @@ mod app { crate::app_crypto!(super, sp_core::testing::SR25519); } -#[cfg(feature = "full_crypto")] -pub use app::Pair as AppPair; -pub use app::{Public as AppPublic, Signature as AppSignature}; +pub use app::{Pair as AppPair, Public as AppPublic, Signature as AppSignature}; impl RuntimePublic for Public { type Signature = Signature; diff --git a/substrate/primitives/application-crypto/src/traits.rs b/substrate/primitives/application-crypto/src/traits.rs index e9b1080f63d9ce96ced35989cfba56887f03feb9..0b59abf272dc7761f08c8604db33762b07e31584 100644 --- a/substrate/primitives/application-crypto/src/traits.rs +++ b/substrate/primitives/application-crypto/src/traits.rs @@ -18,9 +18,7 @@ use codec::Codec; use scale_info::TypeInfo; -#[cfg(feature = "full_crypto")] -use sp_core::crypto::Pair; -use sp_core::crypto::{CryptoType, CryptoTypeId, IsWrappedBy, KeyTypeId, Public}; +use sp_core::crypto::{CryptoType, CryptoTypeId, IsWrappedBy, KeyTypeId, Pair, Public}; use sp_std::{fmt::Debug, vec::Vec}; /// Application-specific cryptographic object. @@ -45,24 +43,14 @@ pub trait AppCrypto: 'static + Sized + CryptoType { type Signature: AppSignature; /// The corresponding key pair type in this application scheme. - #[cfg(feature = "full_crypto")] type Pair: AppPair; } /// Type which implements Hash in std, not when no-std (std variant). -#[cfg(any(feature = "std", feature = "full_crypto"))] pub trait MaybeHash: sp_std::hash::Hash {} -#[cfg(any(feature = "std", feature = "full_crypto"))] impl MaybeHash for T {} -/// Type which implements Hash in std, not when no-std (no-std variant). -#[cfg(all(not(feature = "std"), not(feature = "full_crypto")))] -pub trait MaybeHash {} -#[cfg(all(not(feature = "std"), not(feature = "full_crypto")))] -impl MaybeHash for T {} - /// Application-specific key pair. -#[cfg(feature = "full_crypto")] pub trait AppPair: AppCrypto + Pair::Public, Signature = ::Signature> { diff --git a/substrate/primitives/arithmetic/Cargo.toml b/substrate/primitives/arithmetic/Cargo.toml index 301821ad6893117661e439921f23d4d5d48f16b5..120edd06a66099c18efc4334f28f7031cb6a1bed 100644 --- a/substrate/primitives/arithmetic/Cargo.toml +++ b/substrate/primitives/arithmetic/Cargo.toml @@ -27,6 +27,7 @@ scale-info = { version = "2.10.0", default-features = false, features = ["derive serde = { features = ["alloc", "derive"], optional = true, workspace = true } static_assertions = "1.1.0" sp-std = { path = "../std", default-features = false } +docify = "0.2.7" [dev-dependencies] criterion = "0.4.0" diff --git a/substrate/primitives/arithmetic/fuzzer/src/fixed_point.rs b/substrate/primitives/arithmetic/fuzzer/src/fixed_point.rs index e76dd1503e39fddb5dbfde601d5a86d28c5b1b88..e2d31065635ebc4d98edb5c6e5df39893ae65b9d 100644 --- a/substrate/primitives/arithmetic/fuzzer/src/fixed_point.rs +++ b/substrate/primitives/arithmetic/fuzzer/src/fixed_point.rs @@ -66,7 +66,7 @@ fn main() { let c = FixedI64::saturating_from_integer(x.saturating_add(y)); assert_eq!(a.saturating_add(b), c); - // Check substraction. + // Check subtraction. let a = FixedI64::saturating_from_integer(x); let b = FixedI64::saturating_from_integer(y); let c = FixedI64::saturating_from_integer(x.saturating_sub(y)); diff --git a/substrate/primitives/arithmetic/src/biguint.rs b/substrate/primitives/arithmetic/src/biguint.rs index d92b08c8eca96121ab1e9c6d54f9fca318815861..164ec67a603f7e61c6e25de1a2861822da06dcf0 100644 --- a/substrate/primitives/arithmetic/src/biguint.rs +++ b/substrate/primitives/arithmetic/src/biguint.rs @@ -17,9 +17,10 @@ //! Infinite precision unsigned integer for substrate runtime. +use alloc::{vec, vec::Vec}; use codec::{Decode, Encode}; +use core::{cell::RefCell, cmp::Ordering, ops}; use num_traits::{One, Zero}; -use sp_std::{cell::RefCell, cmp::Ordering, ops, prelude::*, vec}; // A sensible value for this would be half of the dword size of the host machine. Since the // runtime is compiled to 32bit webassembly, using 32 and 64 for single and double respectively @@ -35,7 +36,7 @@ const SHIFT: usize = 32; const B: Double = Single::max_value() as Double + 1; static_assertions::const_assert!( - sp_std::mem::size_of::() - sp_std::mem::size_of::() == SHIFT / 8 + core::mem::size_of::() - core::mem::size_of::() == SHIFT / 8 ); /// Splits a [`Double`] limb number into a tuple of two [`Single`] limb numbers. @@ -438,9 +439,9 @@ impl BigUint { } } -impl sp_std::fmt::Debug for BigUint { +impl core::fmt::Debug for BigUint { #[cfg(feature = "std")] - fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!( f, "BigUint {{ {:?} ({:?})}}", @@ -450,7 +451,7 @@ impl sp_std::fmt::Debug for BigUint { } #[cfg(not(feature = "std"))] - fn fmt(&self, _: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { Ok(()) } } diff --git a/substrate/primitives/arithmetic/src/fixed_point.rs b/substrate/primitives/arithmetic/src/fixed_point.rs index ce14d2957b5e216325a2af02fbb1d68af2b756bb..c4e9259c5fc94e3494a4df2bacc36fcdb562e006 100644 --- a/substrate/primitives/arithmetic/src/fixed_point.rs +++ b/substrate/primitives/arithmetic/src/fixed_point.rs @@ -16,6 +16,33 @@ // limitations under the License. //! Decimal Fixed Point implementations for Substrate runtime. +//! Similar to types that implement [`PerThing`](crate::per_things), these are also +//! fixed-point types, however, they are able to represent larger fractions: +#![doc = docify::embed!("./src/lib.rs", fixed_u64)] +//! +//! ### Fixed Point Types in Practice +//! +//! If one needs to exceed the value of one (1), then +//! [`FixedU64`](FixedU64) (and its signed and `u128` counterparts) can be utilized. +//! Take for example this very rudimentary pricing mechanism, where we wish to calculate the demand +//! / supply to get a price for some on-chain compute: +#![doc = docify::embed!( + "./src/lib.rs", + fixed_u64_block_computation_example +)] +//! +//! For a much more comprehensive example, be sure to look at the source for broker (the "coretime") +//! pallet. +//! +//! #### Fixed Point Types in Practice +//! +//! Just as with [`PerThing`](PerThing), you can also perform regular mathematical +//! expressions: +#![doc = docify::embed!( + "./src/lib.rs", + fixed_u64_operation_example +)] +//! use crate::{ helpers_128bit::{multiply_by_rational_with_rounding, sqrt}, @@ -26,17 +53,16 @@ use crate::{ PerThing, Perbill, Rounding, SignedRounding, }; use codec::{CompactAs, Decode, Encode}; -use sp_std::{ +use core::{ fmt::Debug, ops::{self, Add, Div, Mul, Sub}, - prelude::*, }; #[cfg(feature = "serde")] use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; #[cfg(all(not(feature = "std"), feature = "serde"))] -use sp_std::alloc::string::{String, ToString}; +use alloc::string::{String, ToString}; /// Integer types that can be used to interact with `FixedPointNumber` implementations. pub trait FixedPointOperand: @@ -542,7 +568,7 @@ macro_rules! implement_fixed { let v = self.0 as u128; // Want x' = sqrt(x) where x = n/D and x' = n'/D (D is fixed) - // Our prefered way is: + // Our preferred way is: // sqrt(n/D) = sqrt(nD / D^2) = sqrt(nD)/sqrt(D^2) = sqrt(nD)/D // ergo n' = sqrt(nD) // but this requires nD to fit into our type. @@ -899,9 +925,9 @@ macro_rules! implement_fixed { } } - impl sp_std::fmt::Debug for $name { + impl ::core::fmt::Debug for $name { #[cfg(feature = "std")] - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { let integral = { let int = self.0 / Self::accuracy(); let signum_for_zero = if int == 0 && self.is_negative() { "-" } else { "" }; @@ -917,7 +943,7 @@ macro_rules! implement_fixed { } #[cfg(not(feature = "std"))] - fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + fn fmt(&self, _: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { Ok(()) } } @@ -933,13 +959,13 @@ macro_rules! implement_fixed { } } - impl sp_std::fmt::Display for $name { - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + impl ::core::fmt::Display for $name { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { write!(f, "{}", self.0) } } - impl sp_std::str::FromStr for $name { + impl ::core::str::FromStr for $name { type Err = &'static str; fn from_str(s: &str) -> Result { @@ -969,7 +995,7 @@ macro_rules! implement_fixed { where D: Deserializer<'de>, { - use sp_std::str::FromStr; + use ::core::str::FromStr; let s = String::deserialize(deserializer)?; $name::from_str(&s).map_err(de::Error::custom) } diff --git a/substrate/primitives/arithmetic/src/lib.rs b/substrate/primitives/arithmetic/src/lib.rs index 900f0b75c3bf4f4e45524db7b3f0991828f03a09..01c403a7c4af284a2e6eaec65a5e5e820be9163e 100644 --- a/substrate/primitives/arithmetic/src/lib.rs +++ b/substrate/primitives/arithmetic/src/lib.rs @@ -19,6 +19,8 @@ #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + /// Copied from `sp-runtime` and documented there. #[macro_export] macro_rules! assert_eq_error_rate { @@ -49,7 +51,8 @@ pub use per_things::{ }; pub use rational::{MultiplyRational, Rational128, RationalInfinite}; -use sp_std::{cmp::Ordering, fmt::Debug, prelude::*}; +use alloc::vec::Vec; +use core::{cmp::Ordering, fmt::Debug}; use traits::{BaseArithmetic, One, SaturatedConversion, Unsigned, Zero}; use codec::{Decode, Encode, MaxEncodedLen}; @@ -98,7 +101,7 @@ where fn tcmp(&self, other: &T, threshold: T) -> Ordering { // early exit. if threshold.is_zero() { - return self.cmp(other) + return self.cmp(other); } let upper_bound = other.saturating_add(threshold); @@ -203,12 +206,12 @@ where // Nothing to do here. if count.is_zero() { - return Ok(Vec::::new()) + return Ok(Vec::::new()); } let diff = targeted_sum.max(sum) - targeted_sum.min(sum); if diff.is_zero() { - return Ok(input.to_vec()) + return Ok(input.to_vec()); } let needs_bump = targeted_sum > sum; @@ -251,7 +254,7 @@ where min_index += 1; min_index %= count; } - leftover -= One::one() + leftover -= One::one(); } } else { // must decrease the stakes a bit. decrement from the max element. index of maximum is now @@ -285,7 +288,7 @@ where if output_with_idx[max_index].1 <= threshold { max_index = max_index.checked_sub(1).unwrap_or(count - 1); } - leftover -= One::one() + leftover -= One::one(); } else { max_index = max_index.checked_sub(1).unwrap_or(count - 1); } @@ -297,7 +300,7 @@ where targeted_sum, "sum({:?}) != {:?}", output_with_idx, - targeted_sum, + targeted_sum ); // sort again based on the original index. @@ -353,7 +356,7 @@ mod normalize_tests { vec![ Perbill::from_parts(333333334), Perbill::from_parts(333333333), - Perbill::from_parts(333333333), + Perbill::from_parts(333333333) ] ); @@ -364,7 +367,7 @@ mod normalize_tests { vec![ Perbill::from_parts(316666668), Perbill::from_parts(383333332), - Perbill::from_parts(300000000), + Perbill::from_parts(300000000) ] ); } @@ -375,13 +378,13 @@ mod normalize_tests { // could have a situation where the sum cannot be calculated in the inner type. Calculating // using the upper type of the per_thing should assure this to be okay. assert_eq!( - vec![PerU16::from_percent(40), PerU16::from_percent(40), PerU16::from_percent(40),] + vec![PerU16::from_percent(40), PerU16::from_percent(40), PerU16::from_percent(40)] .normalize(PerU16::one()) .unwrap(), vec![ PerU16::from_parts(21845), // 33% PerU16::from_parts(21845), // 33% - PerU16::from_parts(21845), // 33% + PerU16::from_parts(21845) // 33% ] ); } @@ -425,11 +428,93 @@ mod normalize_tests { } } +#[cfg(test)] +mod per_and_fixed_examples { + use super::*; + + #[docify::export] + #[test] + fn percent_mult() { + let percent = Percent::from_rational(5u32, 100u32); // aka, 5% + let five_percent_of_100 = percent * 100u32; // 5% of 100 is 5. + assert_eq!(five_percent_of_100, 5) + } + #[docify::export] + #[test] + fn perbill_example() { + let p = Perbill::from_percent(80); + // 800000000 bil, or a representative of 0.800000000. + // Precision is in the billions place. + assert_eq!(p.deconstruct(), 800000000); + } + + #[docify::export] + #[test] + fn percent_example() { + let percent = Percent::from_rational(190u32, 400u32); + assert_eq!(percent.deconstruct(), 47); + } + + #[docify::export] + #[test] + fn fixed_u64_block_computation_example() { + // Calculate a very rudimentary on-chain price from supply / demand + // Supply: Cores available per block + // Demand: Cores being ordered per block + let price = FixedU64::from_rational(5u128, 10u128); + + // 0.5 DOT per core + assert_eq!(price, FixedU64::from_float(0.5)); + + // Now, the story has changed - lots of demand means we buy as many cores as there + // available. This also means that price goes up! For the sake of simplicity, we don't care + // about who gets a core - just about our very simple price model + + // Calculate a very rudimentary on-chain price from supply / demand + // Supply: Cores available per block + // Demand: Cores being ordered per block + let price = FixedU64::from_rational(19u128, 10u128); + + // 1.9 DOT per core + assert_eq!(price, FixedU64::from_float(1.9)); + } + + #[docify::export] + #[test] + fn fixed_u64() { + // The difference between this and perthings is perthings operates within the relam of [0, + // 1] In cases where we need > 1, we can used fixed types such as FixedU64 + + let rational_1 = FixedU64::from_rational(10, 5); //" 200%" aka 2. + let rational_2 = FixedU64::from_rational_with_rounding(5, 10, Rounding::Down); // "50%" aka 0.50... + + assert_eq!(rational_1, (2u64).into()); + assert_eq!(rational_2.into_perbill(), Perbill::from_float(0.5)); + } + + #[docify::export] + #[test] + fn fixed_u64_operation_example() { + let rational_1 = FixedU64::from_rational(10, 5); // "200%" aka 2. + let rational_2 = FixedU64::from_rational(8, 5); // "160%" aka 1.6. + + let addition = rational_1 + rational_2; + let multiplication = rational_1 * rational_2; + let division = rational_1 / rational_2; + let subtraction = rational_1 - rational_2; + + assert_eq!(addition, FixedU64::from_float(3.6)); + assert_eq!(multiplication, FixedU64::from_float(3.2)); + assert_eq!(division, FixedU64::from_float(1.25)); + assert_eq!(subtraction, FixedU64::from_float(0.4)); + } +} + #[cfg(test)] mod threshold_compare_tests { use super::*; use crate::traits::Saturating; - use sp_std::cmp::Ordering; + use core::cmp::Ordering; #[test] fn epsilon_ord_works() { @@ -437,15 +522,15 @@ mod threshold_compare_tests { let e = Perbill::from_percent(10).mul_ceil(b); // [115 - 11,5 (103,5), 115 + 11,5 (126,5)] is all equal - assert_eq!(103u32.tcmp(&b, e), Ordering::Equal); - assert_eq!(104u32.tcmp(&b, e), Ordering::Equal); - assert_eq!(115u32.tcmp(&b, e), Ordering::Equal); - assert_eq!(120u32.tcmp(&b, e), Ordering::Equal); - assert_eq!(126u32.tcmp(&b, e), Ordering::Equal); - assert_eq!(127u32.tcmp(&b, e), Ordering::Equal); - - assert_eq!(128u32.tcmp(&b, e), Ordering::Greater); - assert_eq!(102u32.tcmp(&b, e), Ordering::Less); + assert_eq!((103u32).tcmp(&b, e), Ordering::Equal); + assert_eq!((104u32).tcmp(&b, e), Ordering::Equal); + assert_eq!((115u32).tcmp(&b, e), Ordering::Equal); + assert_eq!((120u32).tcmp(&b, e), Ordering::Equal); + assert_eq!((126u32).tcmp(&b, e), Ordering::Equal); + assert_eq!((127u32).tcmp(&b, e), Ordering::Equal); + + assert_eq!((128u32).tcmp(&b, e), Ordering::Greater); + assert_eq!((102u32).tcmp(&b, e), Ordering::Less); } #[test] @@ -455,15 +540,15 @@ mod threshold_compare_tests { let e = Perbill::from_parts(100) * b; // [115 - 11,5 (103,5), 115 + 11,5 (126,5)] is all equal - assert_eq!(103u32.tcmp(&b, e), 103u32.cmp(&b)); - assert_eq!(104u32.tcmp(&b, e), 104u32.cmp(&b)); - assert_eq!(115u32.tcmp(&b, e), 115u32.cmp(&b)); - assert_eq!(120u32.tcmp(&b, e), 120u32.cmp(&b)); - assert_eq!(126u32.tcmp(&b, e), 126u32.cmp(&b)); - assert_eq!(127u32.tcmp(&b, e), 127u32.cmp(&b)); - - assert_eq!(128u32.tcmp(&b, e), 128u32.cmp(&b)); - assert_eq!(102u32.tcmp(&b, e), 102u32.cmp(&b)); + assert_eq!((103u32).tcmp(&b, e), (103u32).cmp(&b)); + assert_eq!((104u32).tcmp(&b, e), (104u32).cmp(&b)); + assert_eq!((115u32).tcmp(&b, e), (115u32).cmp(&b)); + assert_eq!((120u32).tcmp(&b, e), (120u32).cmp(&b)); + assert_eq!((126u32).tcmp(&b, e), (126u32).cmp(&b)); + assert_eq!((127u32).tcmp(&b, e), (127u32).cmp(&b)); + + assert_eq!((128u32).tcmp(&b, e), (128u32).cmp(&b)); + assert_eq!((102u32).tcmp(&b, e), (102u32).cmp(&b)); } #[test] diff --git a/substrate/primitives/arithmetic/src/per_things.rs b/substrate/primitives/arithmetic/src/per_things.rs index fe88b72e24c22158d1d35db50aebf68dec8ff191..f73dbe30cec17ea56b2c349d057df92b76edf724 100644 --- a/substrate/primitives/arithmetic/src/per_things.rs +++ b/substrate/primitives/arithmetic/src/per_things.rs @@ -15,6 +15,42 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Types that implement [`PerThing`](PerThing) can be used as a floating-point alternative for +//! numbers that operate within the realm of `[0, 1]`. The primary types may you encounter in +//! Substrate would be the following: +//! - [`Percent`](Percent) - parts of one hundred. +//! - [`Permill`](Permill) - parts of a million. +//! - [`Perbill`](Perbill) - parts of a billion. +//! +//! In use, you may see them being used as follows: +//! +//! > **[`Perbill`](Perbill), parts of a billion** +#![doc = docify::embed!("./src/lib.rs", perbill_example)] +//! > **[`Percent`](Percent), parts of a hundred** +#![doc = docify::embed!("./src/lib.rs", percent_example)] +//! +//! Note that `Percent` is represented as a _rounded down_, fixed point +//! number (see the example above). Unlike primitive types, types that implement +//! [`PerThing`](PerThing) will also not overflow, and are therefore safe to use. +//! They adopt the same behavior that a saturated calculation would provide, meaning that if one is +//! to go over "100%", it wouldn't overflow, but simply stop at the upper or lower bound. +//! +//! For use cases which require precision beyond the range of `[0, 1]`, there are fixed-point types +//! which can be used. +//! +//! Each of these can be used to construct and represent ratios within our runtime. +//! You will find types like [`Perbill`](Perbill) being used often in pallet +//! development. `pallet_referenda` is a good example of a pallet which makes good use of fixed +//! point arithmetic, as it relies on representing various curves and thresholds relating to +//! governance. +//! +//! #### Fixed Point Arithmetic with [`PerThing`](PerThing) +//! +//! As stated, one can also perform mathematics using these types directly. For example, finding the +//! percentage of a particular item: + +#![doc = docify::embed!("./src/lib.rs", percent_mult)] + #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -23,12 +59,11 @@ use crate::traits::{ Saturating, UniqueSaturatedInto, Unsigned, Zero, }; use codec::{CompactAs, Encode}; -use num_traits::{Pow, SaturatingAdd, SaturatingSub}; -use sp_std::{ +use core::{ fmt, ops, ops::{Add, Sub}, - prelude::*, }; +use num_traits::{Pow, SaturatingAdd, SaturatingSub}; /// Get the inner type of a `PerThing`. pub type InnerOf

=

::Inner; @@ -414,7 +449,7 @@ pub trait PerThing: } /// The rounding method to use for unsigned quantities. -#[derive(Copy, Clone, sp_std::fmt::Debug)] +#[derive(Copy, Clone, core::fmt::Debug)] pub enum Rounding { // Towards infinity. Up, @@ -427,7 +462,7 @@ pub enum Rounding { } /// The rounding method to use. -#[derive(Copy, Clone, sp_std::fmt::Debug)] +#[derive(Copy, Clone, core::fmt::Debug)] pub enum SignedRounding { // Towards positive infinity. High, @@ -580,8 +615,8 @@ macro_rules! implement_per_thing { } #[cfg(feature = "std")] - impl sp_std::fmt::Debug for $name { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + impl core::fmt::Debug for $name { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { if $max == <$type>::max_value() { // Not a power of ten: show as N/D and approx % let pc = (self.0 as f64) / (self.0 as f64) * 100f64; @@ -606,8 +641,8 @@ macro_rules! implement_per_thing { } #[cfg(not(feature = "std"))] - impl sp_std::fmt::Debug for $name { - fn fmt(&self, fmt: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + impl core::fmt::Debug for $name { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { if $max == <$type>::max_value() { // Not a power of ten: show as N/D and approx % write!(fmt, "{}/{}", self.0, $max) diff --git a/substrate/primitives/arithmetic/src/rational.rs b/substrate/primitives/arithmetic/src/rational.rs index ebd89c615a38b3efc717fbb7051fe12ccfb22ea1..2ec83e6fd868e1b67f3c2374dc9a3171165bc8e0 100644 --- a/substrate/primitives/arithmetic/src/rational.rs +++ b/substrate/primitives/arithmetic/src/rational.rs @@ -16,8 +16,8 @@ // limitations under the License. use crate::{biguint::BigUint, helpers_128bit, Rounding}; +use core::cmp::Ordering; use num_traits::{Bounded, One, Zero}; -use sp_std::{cmp::Ordering, prelude::*}; /// A wrapper for any rational number with infinitely large numerator and denominator. /// @@ -92,15 +92,15 @@ impl From for RationalInfinite { pub struct Rational128(u128, u128); #[cfg(feature = "std")] -impl sp_std::fmt::Debug for Rational128 { - fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { +impl core::fmt::Debug for Rational128 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "Rational128({} / {} ≈ {:.8})", self.0, self.1, self.0 as f64 / self.1 as f64) } } #[cfg(not(feature = "std"))] -impl sp_std::fmt::Debug for Rational128 { - fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { +impl core::fmt::Debug for Rational128 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "Rational128({} / {})", self.0, self.1) } } diff --git a/substrate/primitives/authority-discovery/Cargo.toml b/substrate/primitives/authority-discovery/Cargo.toml index 70f00897cdd6e804c98363016a8baabc9edde415..8ee8bb94ed97bf4283f2853cc28a8525a6448da6 100644 --- a/substrate/primitives/authority-discovery/Cargo.toml +++ b/substrate/primitives/authority-discovery/Cargo.toml @@ -21,7 +21,6 @@ scale-info = { version = "2.10.0", default-features = false, features = ["derive sp-api = { path = "../api", default-features = false } sp-application-crypto = { path = "../application-crypto", default-features = false } sp-runtime = { path = "../runtime", default-features = false } -sp-std = { path = "../std", default-features = false } [features] default = ["std"] @@ -31,7 +30,6 @@ std = [ "sp-api/std", "sp-application-crypto/std", "sp-runtime/std", - "sp-std/std", ] serde = [ "scale-info/serde", diff --git a/substrate/primitives/authority-discovery/src/lib.rs b/substrate/primitives/authority-discovery/src/lib.rs index 3b25e39d4045e872d2892aa7e992d4140950479e..5aba76a3abb66f11d650ad50486f7a92ed4d6051 100644 --- a/substrate/primitives/authority-discovery/src/lib.rs +++ b/substrate/primitives/authority-discovery/src/lib.rs @@ -19,7 +19,9 @@ #![cfg_attr(not(feature = "std"), no_std)] -use sp_std::vec::Vec; +extern crate alloc; + +use alloc::vec::Vec; mod app { use sp_application_crypto::{app_crypto, key_types::AUTHORITY_DISCOVERY, sr25519}; diff --git a/substrate/primitives/block-builder/Cargo.toml b/substrate/primitives/block-builder/Cargo.toml index c1317facd7fc532267c8039d71b2d78b3d3f61c4..cc4b10851544d601ef9caea512280d49b77cd443 100644 --- a/substrate/primitives/block-builder/Cargo.toml +++ b/substrate/primitives/block-builder/Cargo.toml @@ -19,8 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"] sp-api = { path = "../api", default-features = false } sp-inherents = { path = "../inherents", default-features = false } sp-runtime = { path = "../runtime", default-features = false } -sp-std = { path = "../std", default-features = false } [features] default = ["std"] -std = ["sp-api/std", "sp-inherents/std", "sp-runtime/std", "sp-std/std"] +std = ["sp-api/std", "sp-inherents/std", "sp-runtime/std"] diff --git a/substrate/primitives/block-builder/src/lib.rs b/substrate/primitives/block-builder/src/lib.rs index 29e04857f463ee04c8f39b6c885437992e0d911c..9d03aa4d7a013b1d0ba71da628405a25be95ce38 100644 --- a/substrate/primitives/block-builder/src/lib.rs +++ b/substrate/primitives/block-builder/src/lib.rs @@ -19,6 +19,8 @@ #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + use sp_inherents::{CheckInherentsResult, InherentData}; use sp_runtime::{traits::Block as BlockT, ApplyExtrinsicResult}; @@ -44,7 +46,7 @@ sp_api::decl_runtime_apis! { /// Generate inherent extrinsics. The inherent data will vary from chain to chain. fn inherent_extrinsics( inherent: InherentData, - ) -> sp_std::vec::Vec<::Extrinsic>; + ) -> alloc::vec::Vec<::Extrinsic>; /// Check that the inherents are valid. The inherent data will vary from chain to chain. fn check_inherents(block: Block, data: InherentData) -> CheckInherentsResult; diff --git a/substrate/primitives/blockchain/src/backend.rs b/substrate/primitives/blockchain/src/backend.rs index 8208f9128e714511c2a3604d7d754e4d87d62530..7a09865f858d3faa4ec1ee1fd5a2beb84e9267d7 100644 --- a/substrate/primitives/blockchain/src/backend.rs +++ b/substrate/primitives/blockchain/src/backend.rs @@ -187,7 +187,7 @@ pub trait Backend: /// a block with the given `base_hash`. /// /// The search space is always limited to blocks which are in the finalized - /// chain or descendents of it. + /// chain or descendants of it. /// /// Returns `Ok(None)` if `base_hash` is not found in search space. // TODO: document time complexity of this, see [#1444](https://github.com/paritytech/substrate/issues/1444) diff --git a/substrate/primitives/blockchain/src/error.rs b/substrate/primitives/blockchain/src/error.rs index 74a2ed3fba50d1b349370a831a92af5d8e78950f..e8ac148d7511ec9cbbf6564015fd63e12d7ec06c 100644 --- a/substrate/primitives/blockchain/src/error.rs +++ b/substrate/primitives/blockchain/src/error.rs @@ -34,7 +34,7 @@ pub enum ApplyExtrinsicFailed { /// The transaction cannot be included into the current block. /// /// This doesn't necessary mean that the transaction itself is invalid, but it might be just - /// unappliable onto the current block. + /// unapplicable onto the current block. #[error("Extrinsic is not valid: {0:?}")] Validity(#[from] TransactionValidityError), diff --git a/substrate/primitives/blockchain/src/header_metadata.rs b/substrate/primitives/blockchain/src/header_metadata.rs index 08b3c9ab3dfbda6acc7a0ad6b575c518dc97ecc3..ccd640c0567afc6c9f9341fca754471a15a2829c 100644 --- a/substrate/primitives/blockchain/src/header_metadata.rs +++ b/substrate/primitives/blockchain/src/header_metadata.rs @@ -178,7 +178,7 @@ pub struct TreeRoute { impl TreeRoute { /// Creates a new `TreeRoute`. /// - /// To preserve the structure safety invariats it is required that `pivot < route.len()`. + /// To preserve the structure safety invariants it is required that `pivot < route.len()`. pub fn new(route: Vec>, pivot: usize) -> Result { if pivot < route.len() { Ok(TreeRoute { route, pivot }) @@ -212,7 +212,7 @@ impl TreeRoute { ) } - /// Get a slice of enacted blocks (descendents of the common ancestor) + /// Get a slice of enacted blocks (descendants of the common ancestor) pub fn enacted(&self) -> &[HashAndNumber] { &self.route[self.pivot + 1..] } diff --git a/substrate/primitives/consensus/aura/Cargo.toml b/substrate/primitives/consensus/aura/Cargo.toml index 52f6bc22ba4080fbe1fccde71cf06ffa2c1a5327..0cedc59ea8fb1d06c7fc791561d1b3674a378300 100644 --- a/substrate/primitives/consensus/aura/Cargo.toml +++ b/substrate/primitives/consensus/aura/Cargo.toml @@ -24,7 +24,6 @@ sp-application-crypto = { path = "../../application-crypto", default-features = sp-consensus-slots = { path = "../slots", default-features = false } sp-inherents = { path = "../../inherents", default-features = false } sp-runtime = { path = "../../runtime", default-features = false } -sp-std = { path = "../../std", default-features = false } sp-timestamp = { path = "../../timestamp", default-features = false } [features] @@ -38,7 +37,6 @@ std = [ "sp-consensus-slots/std", "sp-inherents/std", "sp-runtime/std", - "sp-std/std", "sp-timestamp/std", ] diff --git a/substrate/primitives/consensus/aura/src/lib.rs b/substrate/primitives/consensus/aura/src/lib.rs index 78409e84e93a377c90c4318e185cd5d4d8324781..5173d5516cfa605f7248ac0ca354b2283227d870 100644 --- a/substrate/primitives/consensus/aura/src/lib.rs +++ b/substrate/primitives/consensus/aura/src/lib.rs @@ -19,9 +19,11 @@ #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + +use alloc::vec::Vec; use codec::{Codec, Decode, Encode}; use sp_runtime::ConsensusEngineId; -use sp_std::vec::Vec; pub mod digests; pub mod inherents; diff --git a/substrate/primitives/consensus/babe/Cargo.toml b/substrate/primitives/consensus/babe/Cargo.toml index 8b3006f79a7ffc87a69a7eb2921f4b4f0533d1a1..724b9fd3e28925c3453cc86e2c1b7bea061676d0 100644 --- a/substrate/primitives/consensus/babe/Cargo.toml +++ b/substrate/primitives/consensus/babe/Cargo.toml @@ -26,7 +26,6 @@ sp-consensus-slots = { path = "../slots", default-features = false } sp-core = { path = "../../core", default-features = false } sp-inherents = { path = "../../inherents", default-features = false } sp-runtime = { path = "../../runtime", default-features = false } -sp-std = { path = "../../std", default-features = false } sp-timestamp = { path = "../../timestamp", optional = true } [features] @@ -42,7 +41,6 @@ std = [ "sp-core/std", "sp-inherents/std", "sp-runtime/std", - "sp-std/std", "sp-timestamp/std", ] diff --git a/substrate/primitives/consensus/babe/src/digests.rs b/substrate/primitives/consensus/babe/src/digests.rs index afc967e3af391dd165e6be88ffdb42918b57b130..e7af8c5763a0f93223c749cea6b21313f46ba430 100644 --- a/substrate/primitives/consensus/babe/src/digests.rs +++ b/substrate/primitives/consensus/babe/src/digests.rs @@ -22,9 +22,10 @@ use super::{ BabeEpochConfiguration, Randomness, Slot, BABE_ENGINE_ID, }; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; use sp_core::sr25519::vrf::VrfSignature; use sp_runtime::{DigestItem, RuntimeDebug}; -use sp_std::vec::Vec; use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; diff --git a/substrate/primitives/consensus/babe/src/inherents.rs b/substrate/primitives/consensus/babe/src/inherents.rs index 909769f3031be658e758087c4323ed7ec80fdcce..54b7b64401673927602a3e816cc9d5dcb9ede14a 100644 --- a/substrate/primitives/consensus/babe/src/inherents.rs +++ b/substrate/primitives/consensus/babe/src/inherents.rs @@ -17,7 +17,6 @@ //! Inherents for BABE -use core::result::Result; use sp_inherents::{Error, InherentData, InherentIdentifier}; /// The BABE inherent identifier. diff --git a/substrate/primitives/consensus/babe/src/lib.rs b/substrate/primitives/consensus/babe/src/lib.rs index d6b2cdd55e0daddf7e49a132d48bbc8c1741736a..ee07da6829f52933424a838e36a2c1246bf9aeb7 100644 --- a/substrate/primitives/consensus/babe/src/lib.rs +++ b/substrate/primitives/consensus/babe/src/lib.rs @@ -20,15 +20,18 @@ #![forbid(unsafe_code, missing_docs, unused_variables, unused_imports)] #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + pub mod digests; pub mod inherents; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use sp_runtime::{traits::Header, ConsensusEngineId, RuntimeDebug}; -use sp_std::vec::Vec; use crate::digests::{NextConfigDescriptor, NextEpochDescriptor}; @@ -256,8 +259,14 @@ pub struct BabeEpochConfiguration { pub allowed_slots: AllowedSlots, } +impl Default for BabeEpochConfiguration { + fn default() -> Self { + Self { c: (1, 4), allowed_slots: AllowedSlots::PrimaryAndSecondaryVRFSlots } + } +} + /// Verifies the equivocation proof by making sure that: both headers have -/// different hashes, are targetting the same slot, and have valid signatures by +/// different hashes, are targeting the same slot, and have valid signatures by /// the same authority. pub fn check_equivocation_proof(proof: EquivocationProof) -> bool where @@ -289,7 +298,7 @@ where let first_pre_digest = find_pre_digest(&proof.first_header)?; let second_pre_digest = find_pre_digest(&proof.second_header)?; - // both headers must be targetting the same slot and it must + // both headers must be targeting the same slot and it must // be the same as the one in the proof. if proof.slot != first_pre_digest.slot() || first_pre_digest.slot() != second_pre_digest.slot() diff --git a/substrate/primitives/consensus/beefy/Cargo.toml b/substrate/primitives/consensus/beefy/Cargo.toml index 8ab817d52ef93469959c4de17a459a319b07b4e0..fbcc6e0c1048a55f405e479ed9a195154f6fe823 100644 --- a/substrate/primitives/consensus/beefy/Cargo.toml +++ b/substrate/primitives/consensus/beefy/Cargo.toml @@ -26,7 +26,6 @@ sp-io = { path = "../../io", default-features = false } sp-mmr-primitives = { path = "../../merkle-mountain-range", default-features = false } sp-runtime = { path = "../../runtime", default-features = false } sp-keystore = { path = "../../keystore", default-features = false } -sp-std = { path = "../../std", default-features = false } strum = { version = "0.24.1", features = ["derive"], default-features = false } lazy_static = { version = "1.4.0", optional = true } @@ -49,7 +48,6 @@ std = [ "sp-keystore/std", "sp-mmr-primitives/std", "sp-runtime/std", - "sp-std/std", "strum/std", ] diff --git a/substrate/primitives/consensus/beefy/src/commitment.rs b/substrate/primitives/consensus/beefy/src/commitment.rs index 1f0fb34ebf10b3e357429bf9bb501677dbea4695..4fd9e1b0a6ed14ea51ef30e5d4c6748a129d5249 100644 --- a/substrate/primitives/consensus/beefy/src/commitment.rs +++ b/substrate/primitives/consensus/beefy/src/commitment.rs @@ -15,9 +15,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +use alloc::{vec, vec::Vec}; use codec::{Decode, Encode, Error, Input}; +use core::cmp; use scale_info::TypeInfo; -use sp_std::{cmp, prelude::*}; use crate::{Payload, ValidatorSetId}; @@ -97,6 +98,19 @@ pub struct SignedCommitment { pub signatures: Vec>, } +impl core::fmt::Display + for SignedCommitment +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let signatures_count = self.signatures.iter().filter(|s| s.is_some()).count(); + write!( + f, + "SignedCommitment(commitment: {:?}, signatures_count: {})", + self.commitment, signatures_count + ) + } +} + impl SignedCommitment { /// Return the number of collected signatures. pub fn no_of_signatures(&self) -> usize { @@ -241,6 +255,14 @@ pub enum VersionedFinalityProof { V1(SignedCommitment), } +impl core::fmt::Display for VersionedFinalityProof { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + VersionedFinalityProof::V1(sc) => write!(f, "VersionedFinalityProof::V1({})", sc), + } + } +} + impl From> for VersionedFinalityProof { fn from(commitment: SignedCommitment) -> Self { VersionedFinalityProof::V1(commitment) diff --git a/substrate/primitives/consensus/beefy/src/lib.rs b/substrate/primitives/consensus/beefy/src/lib.rs index 1c3801e3a506bc9c04976713e1fae9cf02511a3d..70978ca559dd2b50041c45d9cab3c874e8bcc852 100644 --- a/substrate/primitives/consensus/beefy/src/lib.rs +++ b/substrate/primitives/consensus/beefy/src/lib.rs @@ -31,6 +31,8 @@ //! it will use a different set of keys. For Polkadot use case we plan to use `secp256k1` for BEEFY, //! while GRANDPA uses `ed25519`. +extern crate alloc; + mod commitment; mod payload; @@ -44,13 +46,13 @@ pub mod test_utils; pub use commitment::{Commitment, SignedCommitment, VersionedFinalityProof}; pub use payload::{known_payloads, BeefyPayloadId, Payload, PayloadProvider}; +use alloc::vec::Vec; use codec::{Codec, Decode, Encode}; use core::fmt::{Debug, Display}; use scale_info::TypeInfo; use sp_application_crypto::{AppCrypto, AppPublic, ByteArray, RuntimeAppPublic}; use sp_core::H256; use sp_runtime::traits::{Hash, Keccak256, NumberFor}; -use sp_std::prelude::*; /// Key type for BEEFY module. pub const KEY_TYPE: sp_core::crypto::KeyTypeId = sp_application_crypto::key_types::BEEFY; @@ -198,7 +200,7 @@ pub mod ecdsa_bls_crypto { fn verify(&self, signature: &::Signature, msg: &[u8]) -> bool { // We can not simply call // `EcdsaBlsPair::verify(signature.as_inner_ref(), msg, self.as_inner_ref())` - // because that invokes ECDSA default verification which perfoms Blake2b hash + // because that invokes ECDSA default verification which performs Blake2b hash // which we don't want. This is because ECDSA signatures are meant to be verified // on Ethereum network where Keccak hasher is significantly cheaper than Blake2b. // See Figure 3 of [OnSc21](https://www.scitepress.org/Papers/2021/106066/106066.pdf) diff --git a/substrate/primitives/consensus/beefy/src/mmr.rs b/substrate/primitives/consensus/beefy/src/mmr.rs index 1b9a45f86878fa6d35fa0d150146e6c94c7154fe..0bc303d51c01409253ac2de1a34430fd085b7f1a 100644 --- a/substrate/primitives/consensus/beefy/src/mmr.rs +++ b/substrate/primitives/consensus/beefy/src/mmr.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! BEEFY + MMR utilties. +//! BEEFY + MMR utilities. //! //! While BEEFY can be used completely independently as an additional consensus gadget, //! it is designed around a main use case of bridging standalone networks together. @@ -26,7 +26,8 @@ //! but we imagine they will be useful for other chains that either want to bridge with Polkadot //! or are completely standalone, but heavily inspired by Polkadot. -use crate::{ecdsa_crypto::AuthorityId, ConsensusLog, MmrRootHash, Vec, BEEFY_ENGINE_ID}; +use crate::{ecdsa_crypto::AuthorityId, ConsensusLog, MmrRootHash, BEEFY_ENGINE_ID}; +use alloc::vec::Vec; use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime::{ @@ -76,7 +77,7 @@ pub struct MmrLeaf { /// /// Given that adding new struct elements in SCALE is backward compatible (i.e. old format can be /// still decoded, the new fields will simply be ignored). We expect the major version to be bumped -/// very rarely (hopefuly never). +/// very rarely (hopefully never). #[derive(Debug, Default, PartialEq, Eq, Clone, Encode, Decode, TypeInfo)] pub struct MmrLeafVersion(u8); impl MmrLeafVersion { @@ -150,10 +151,11 @@ pub use mmr_root_provider::MmrRootProvider; mod mmr_root_provider { use super::*; use crate::{known_payloads, payload::PayloadProvider, Payload}; + use alloc::sync::Arc; + use core::marker::PhantomData; use sp_api::ProvideRuntimeApi; use sp_mmr_primitives::MmrApi; use sp_runtime::traits::NumberFor; - use sp_std::{marker::PhantomData, sync::Arc}; /// A [`crate::Payload`] provider where payload is Merkle Mountain Range root hash. /// diff --git a/substrate/primitives/consensus/beefy/src/payload.rs b/substrate/primitives/consensus/beefy/src/payload.rs index d520de445c95aed210c61e67f7e09be8b676442b..1a06e620e7ad400ed10c5451d453f403da1f3688 100644 --- a/substrate/primitives/consensus/beefy/src/payload.rs +++ b/substrate/primitives/consensus/beefy/src/payload.rs @@ -15,10 +15,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +use alloc::{vec, vec::Vec}; use codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_runtime::traits::Block; -use sp_std::prelude::*; /// Id of different payloads in the [`crate::Commitment`] data. pub type BeefyPayloadId = [u8; 2]; @@ -43,7 +43,7 @@ pub mod known_payloads { pub struct Payload(Vec<(BeefyPayloadId, Vec)>); impl Payload { - /// Construct a new payload given an initial vallue + /// Construct a new payload given an initial value pub fn from_single_entry(id: BeefyPayloadId, value: Vec) -> Self { Self(vec![(id, value)]) } diff --git a/substrate/primitives/consensus/beefy/src/witness.rs b/substrate/primitives/consensus/beefy/src/witness.rs index b633453340bbee211f49bc5afcdf5867570cf7c0..cfffc94254a4c6caa9c7748b5e48b71fd71792fe 100644 --- a/substrate/primitives/consensus/beefy/src/witness.rs +++ b/substrate/primitives/consensus/beefy/src/witness.rs @@ -23,9 +23,8 @@ //! verification. This allows lowering the data and computation cost of verifying the //! signed commitment. -use sp_std::prelude::*; - use crate::commitment::{Commitment, SignedCommitment}; +use alloc::vec::Vec; /// A light form of [SignedCommitment]. /// diff --git a/substrate/primitives/consensus/common/src/lib.rs b/substrate/primitives/consensus/common/src/lib.rs index 6505d005deb8df42c04252be85afd7e7b55924b9..01d3b7a24f9c143201f07092c7484e72f513d8f0 100644 --- a/substrate/primitives/consensus/common/src/lib.rs +++ b/substrate/primitives/consensus/common/src/lib.rs @@ -182,7 +182,7 @@ pub trait Proposer { + Send + Unpin + 'static; - /// The supported proof recording by the implementator of this trait. See [`ProofRecording`] + /// The supported proof recording by the implementor of this trait. See [`ProofRecording`] /// for more information. type ProofRecording: self::ProofRecording + Send + Sync + 'static; /// The proof type used by [`Self::ProofRecording`]. diff --git a/substrate/primitives/consensus/grandpa/Cargo.toml b/substrate/primitives/consensus/grandpa/Cargo.toml index b06208a4308b355031adde70de72e0e136debda5..1f2da55c5a1668b10902e71297e1b12a121d6f41 100644 --- a/substrate/primitives/consensus/grandpa/Cargo.toml +++ b/substrate/primitives/consensus/grandpa/Cargo.toml @@ -27,7 +27,6 @@ sp-application-crypto = { path = "../../application-crypto", default-features = sp-core = { path = "../../core", default-features = false } sp-keystore = { path = "../../keystore", default-features = false, optional = true } sp-runtime = { path = "../../runtime", default-features = false } -sp-std = { path = "../../std", default-features = false } [features] default = ["std"] @@ -42,7 +41,6 @@ std = [ "sp-core/std", "sp-keystore/std", "sp-runtime/std", - "sp-std/std", ] # Serde support without relying on std features. diff --git a/substrate/primitives/consensus/grandpa/src/lib.rs b/substrate/primitives/consensus/grandpa/src/lib.rs index 1cf5504c5e7d1b0f17e248e6be45829e6ef15655..75ed81894c259ad7102b9b98bb3e6e29bea4cdcd 100644 --- a/substrate/primitives/consensus/grandpa/src/lib.rs +++ b/substrate/primitives/consensus/grandpa/src/lib.rs @@ -19,9 +19,12 @@ #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + #[cfg(feature = "serde")] use serde::Serialize; +use alloc::vec::Vec; use codec::{Codec, Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "std")] @@ -30,7 +33,6 @@ use sp_runtime::{ traits::{Header as HeaderT, NumberFor}, ConsensusEngineId, RuntimeDebug, }; -use sp_std::vec::Vec; /// The log target to be used by client code. pub const CLIENT_LOG_TARGET: &str = "grandpa"; diff --git a/substrate/primitives/consensus/pow/Cargo.toml b/substrate/primitives/consensus/pow/Cargo.toml index 8147b063f6d3324b1e942d825c272f94720854c0..7a884f865fbeea0d6e7c0fd6a6699e0a5b513769 100644 --- a/substrate/primitives/consensus/pow/Cargo.toml +++ b/substrate/primitives/consensus/pow/Cargo.toml @@ -20,7 +20,6 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = sp-api = { path = "../../api", default-features = false } sp-core = { path = "../../core", default-features = false } sp-runtime = { path = "../../runtime", default-features = false } -sp-std = { path = "../../std", default-features = false } [features] default = ["std"] @@ -29,5 +28,4 @@ std = [ "sp-api/std", "sp-core/std", "sp-runtime/std", - "sp-std/std", ] diff --git a/substrate/primitives/consensus/pow/src/lib.rs b/substrate/primitives/consensus/pow/src/lib.rs index f37aae1c5c012900b4220e2cdf9e1ab5edd188b0..c14d23cf068f23e48d03544e5a16b775245f451c 100644 --- a/substrate/primitives/consensus/pow/src/lib.rs +++ b/substrate/primitives/consensus/pow/src/lib.rs @@ -19,9 +19,12 @@ #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; use codec::Decode; use sp_runtime::ConsensusEngineId; -use sp_std::vec::Vec; /// The `ConsensusEngineId` of PoW. pub const POW_ENGINE_ID: ConsensusEngineId = [b'p', b'o', b'w', b'_']; diff --git a/substrate/primitives/consensus/sassafras/Cargo.toml b/substrate/primitives/consensus/sassafras/Cargo.toml index b707ad18b5b9c6d4649a31a0c8f6a4dd4eeaf1f8..085709d4c8b5be184a3eac016d20b626db6a8528 100644 --- a/substrate/primitives/consensus/sassafras/Cargo.toml +++ b/substrate/primitives/consensus/sassafras/Cargo.toml @@ -26,7 +26,6 @@ sp-application-crypto = { path = "../../application-crypto", default-features = sp-consensus-slots = { path = "../slots", default-features = false } sp-core = { path = "../../core", default-features = false, features = ["bandersnatch-experimental"] } sp-runtime = { path = "../../runtime", default-features = false } -sp-std = { path = "../../std", default-features = false } [features] default = ["std"] @@ -39,7 +38,6 @@ std = [ "sp-consensus-slots/std", "sp-core/std", "sp-runtime/std", - "sp-std/std", ] # Serde support without relying on std features. diff --git a/substrate/primitives/consensus/sassafras/src/digests.rs b/substrate/primitives/consensus/sassafras/src/digests.rs index 5274f1309d8251977fe3bf30f2209bf8538f5e19..64190a41ce1c95d5251f25f0991daf2bed4cc802 100644 --- a/substrate/primitives/consensus/sassafras/src/digests.rs +++ b/substrate/primitives/consensus/sassafras/src/digests.rs @@ -25,8 +25,9 @@ use crate::{ use scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; use sp_runtime::{DigestItem, RuntimeDebug}; -use sp_std::vec::Vec; /// Epoch slot claim digest entry. /// diff --git a/substrate/primitives/consensus/sassafras/src/lib.rs b/substrate/primitives/consensus/sassafras/src/lib.rs index 1752f76588635f5a80fddabc86023a439361643c..c1fea74d04522896b249979caac7658cb5f8a6a8 100644 --- a/substrate/primitives/consensus/sassafras/src/lib.rs +++ b/substrate/primitives/consensus/sassafras/src/lib.rs @@ -21,11 +21,13 @@ #![forbid(unsafe_code, missing_docs, unused_variables, unused_imports)] #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + +use alloc::vec::Vec; use scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_core::crypto::KeyTypeId; use sp_runtime::{ConsensusEngineId, RuntimeDebug}; -use sp_std::vec::Vec; pub use sp_consensus_slots::{Slot, SlotDuration}; diff --git a/substrate/primitives/consensus/sassafras/src/ticket.rs b/substrate/primitives/consensus/sassafras/src/ticket.rs index dc0a61990d3ea7248a4bbbbf69a6eed72942719f..345de99be28d88ec5125067f0eaaa838b13373fd 100644 --- a/substrate/primitives/consensus/sassafras/src/ticket.rs +++ b/substrate/primitives/consensus/sassafras/src/ticket.rs @@ -115,7 +115,7 @@ mod tests { let threshold = ticket_id_threshold(redundancy, slots, attempts, validators); let threshold = threshold as f64 / TicketId::MAX as f64; - // We expect that the total number of tickets allowed to be submited + // We expect that the total number of tickets allowed to be submitted // is slots*redundancy let avt = ((attempts * validators) as f64 * threshold) as u32; assert_eq!(avt, slots * redundancy); diff --git a/substrate/primitives/consensus/sassafras/src/vrf.rs b/substrate/primitives/consensus/sassafras/src/vrf.rs index 5deacd8e9945bf9dbcc05a0664029e3a2c57c1c0..537cff52ab6fb7e42c0309431e5dc4b5a767716a 100644 --- a/substrate/primitives/consensus/sassafras/src/vrf.rs +++ b/substrate/primitives/consensus/sassafras/src/vrf.rs @@ -18,9 +18,10 @@ //! Utilities related to VRF input, pre-output and signatures. use crate::{Randomness, TicketBody, TicketId}; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; use scale_codec::Encode; use sp_consensus_slots::Slot; -use sp_std::vec::Vec; pub use sp_core::bandersnatch::{ ring_vrf::{RingProver, RingVerifier, RingVerifierData, RingVrfSignature}, @@ -100,7 +101,7 @@ pub fn make_ticket_id(input: &VrfInput, pre_output: &VrfPreOutput) -> TicketId { u128::from_le_bytes(bytes) } -/// Make revealed key seed from a given VRF input and pre-ouput. +/// Make revealed key seed from a given VRF input and pre-output. /// /// Input should have been obtained via [`revealed_key_input`]. /// Pre-output should have been obtained from the input directly using the vrf diff --git a/substrate/primitives/consensus/slots/Cargo.toml b/substrate/primitives/consensus/slots/Cargo.toml index 8372b2b04a6b662cd76a07b6d7245ba9ba46b251..94c02dba203dc938062546dabe3cebd32411dbde 100644 --- a/substrate/primitives/consensus/slots/Cargo.toml +++ b/substrate/primitives/consensus/slots/Cargo.toml @@ -19,7 +19,6 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } serde = { features = ["alloc", "derive"], optional = true, workspace = true } -sp-std = { path = "../../std", default-features = false } sp-timestamp = { path = "../../timestamp", default-features = false } [features] @@ -28,7 +27,6 @@ std = [ "codec/std", "scale-info/std", "serde/std", - "sp-std/std", "sp-timestamp/std", ] diff --git a/substrate/primitives/core/Cargo.toml b/substrate/primitives/core/Cargo.toml index 8fcabfeb238459940f58357a9b6f803535c9a177..908f2498de5330c307e164f35c6f9ccfc1aa7b76 100644 --- a/substrate/primitives/core/Cargo.toml +++ b/substrate/primitives/core/Cargo.toml @@ -27,10 +27,11 @@ hash-db = { version = "0.16.0", default-features = false } hash256-std-hasher = { version = "0.15.2", default-features = false } bs58 = { version = "0.5.0", default-features = false, optional = true } rand = { version = "0.8.5", features = ["small_rng"], optional = true } -substrate-bip39 = { version = "0.4.5", optional = true } -bip39 = { version = "2.0.0", default-features = false } +substrate-bip39 = { path = "../../utils/substrate-bip39", default-features = false } +# personal fork here as workaround for: https://github.com/rust-bitcoin/rust-bip39/pull/64 +bip39 = { package = "parity-bip39", version = "2.0.1", default-features = false, features = ["alloc"] } zeroize = { version = "1.4.3", default-features = false } -secrecy = { version = "0.8.0", default-features = false } +secrecy = { version = "0.8.0", default-features = false, features = ["alloc"] } parking_lot = { version = "0.12.1", optional = true } ss58-registry = { version = "1.34.0", default-features = false } sp-std = { path = "../std", default-features = false } @@ -46,15 +47,18 @@ paste = "1.0.7" itertools = { version = "0.10.3", optional = true } # full crypto -array-bytes = { version = "6.1", optional = true } -ed25519-zebra = { version = "3.1.0", default-features = false, optional = true } +array-bytes = { version = "6.1" } +ed25519-zebra = { version = "3.1.0", default-features = false } blake2 = { version = "0.10.4", default-features = false, optional = true } -libsecp256k1 = { version = "0.7", default-features = false, features = ["static-context"], optional = true } +libsecp256k1 = { version = "0.7", default-features = false, features = ["static-context"] } schnorrkel = { version = "0.11.4", features = ["preaudit_deprecated"], default-features = false } merlin = { version = "3.0", default-features = false } -secp256k1 = { version = "0.28.0", default-features = false, features = ["alloc", "recovery"], optional = true } -sp-crypto-hashing = { path = "../crypto/hashing", default-features = false, optional = true } +sp-crypto-hashing = { path = "../crypto/hashing", default-features = false } sp-runtime-interface = { path = "../runtime-interface", default-features = false } +# k256 crate, better portability, intended to be used in substrate-runtimes (no-std) +k256 = { version = "0.13.3", features = ["alloc", "ecdsa"], default-features = false } +# secp256k1 crate, better performance, intended to be used on host side (std) +secp256k1 = { version = "0.28.0", default-features = false, features = ["alloc", "recovery"], optional = true } # bls crypto w3f-bls = { version = "0.1.3", default-features = false, optional = true } @@ -76,8 +80,8 @@ bench = false [features] default = ["std"] + std = [ - "array-bytes", "bandersnatch_vrfs?/std", "bip39/rand", "bip39/std", @@ -94,6 +98,7 @@ std = [ "hash256-std-hasher/std", "impl-serde/std", "itertools", + "k256/std", "libsecp256k1/std", "log/std", "merlin/std", @@ -107,7 +112,6 @@ std = [ "schnorrkel/std", "secp256k1/global-context", "secp256k1/std", - "secrecy/alloc", "serde/std", "sp-crypto-hashing/std", "sp-debug-derive/std", @@ -116,7 +120,7 @@ std = [ "sp-std/std", "sp-storage/std", "ss58-registry/std", - "substrate-bip39", + "substrate-bip39/std", "thiserror", "tracing", "w3f-bls?/std", @@ -126,16 +130,14 @@ std = [ # Serde support without relying on std features. serde = [ - "array-bytes", "blake2", "bounded-collections/serde", "bs58/alloc", "dep:serde", "impl-serde", + "k256/serde", "primitive-types/serde_no_std", "scale-info/serde", - "secrecy/alloc", - "sp-crypto-hashing", "sp-storage/serde", ] @@ -143,12 +145,7 @@ serde = [ # or Intel SGX. # For the regular wasm runtime builds this should not be used. full_crypto = [ - "array-bytes", "blake2", - "ed25519-zebra", - "libsecp256k1", - "secp256k1", - "sp-crypto-hashing", "sp-runtime-interface/disable_target_static_assertions", ] diff --git a/substrate/primitives/core/check-features-variants.sh b/substrate/primitives/core/check-features-variants.sh new file mode 100755 index 0000000000000000000000000000000000000000..6d28212065a623096202f9da483aead71806c513 --- /dev/null +++ b/substrate/primitives/core/check-features-variants.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env -S bash -eux + +export RUSTFLAGS="-Cdebug-assertions=y -Dwarnings" +T=wasm32-unknown-unknown + +cargo check --target=$T --release --no-default-features --features="bls-experimental" +cargo check --target=$T --release --no-default-features --features="full_crypto,bls-experimental" +cargo check --target=$T --release --no-default-features --features="bandersnatch-experimental" +cargo check --target=$T --release --no-default-features --features="full_crypto,serde,bandersnatch-experimental" +cargo check --target=$T --release --no-default-features --features="full_crypto,serde" +cargo check --target=$T --release --no-default-features --features="full_crypto" +cargo check --target=$T --release --no-default-features --features="serde" +cargo check --target=$T --release --no-default-features diff --git a/substrate/primitives/core/src/address_uri.rs b/substrate/primitives/core/src/address_uri.rs index 211d47c0093d8800646c9a47273128af18edbad5..d44f3c0c87c40dd447ca0e3123b74f9464408666 100644 --- a/substrate/primitives/core/src/address_uri.rs +++ b/substrate/primitives/core/src/address_uri.rs @@ -17,7 +17,7 @@ //! Little util for parsing an address URI. Replaces regular expressions. -#[cfg(all(not(feature = "std"), any(feature = "serde", feature = "full_crypto")))] +#[cfg(not(feature = "std"))] use sp_std::{ alloc::string::{String, ToString}, vec::Vec, @@ -85,7 +85,7 @@ impl Error { /// Complementary error information. /// -/// Strucutre contains complementary information about parsing address URI string. +/// Structure contains complementary information about parsing address URI string. /// String contains a copy of an original URI string, 0-based integer indicates position of invalid /// character. #[derive(Debug, PartialEq, Eq, Clone)] diff --git a/substrate/primitives/core/src/bandersnatch.rs b/substrate/primitives/core/src/bandersnatch.rs index 61e7162544a602ba2c5577807cfbc94d143dad22..71ee2da5383489a42f1c3a961089b031e2764964 100644 --- a/substrate/primitives/core/src/bandersnatch.rs +++ b/substrate/primitives/core/src/bandersnatch.rs @@ -20,209 +20,67 @@ //! //! The primitive can operate both as a regular VRF or as an anonymized Ring VRF. -#[cfg(feature = "serde")] -use crate::crypto::Ss58Codec; +#[cfg(feature = "full_crypto")] +use crate::crypto::VrfSecret; use crate::crypto::{ - ByteArray, CryptoType, CryptoTypeId, Derive, Public as TraitPublic, UncheckedFrom, VrfPublic, + ByteArray, CryptoType, CryptoTypeId, DeriveError, DeriveJunction, Pair as TraitPair, + PublicBytes, SecretStringError, SignatureBytes, UncheckedFrom, VrfPublic, }; -#[cfg(feature = "full_crypto")] -use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError, VrfSecret}; -#[cfg(feature = "serde")] -use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; -#[cfg(all(not(feature = "std"), feature = "serde"))] -use sp_std::alloc::{format, string::String}; -use bandersnatch_vrfs::CanonicalSerialize; -#[cfg(feature = "full_crypto")] -use bandersnatch_vrfs::SecretKey; +use bandersnatch_vrfs::{CanonicalSerialize, SecretKey}; use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; use scale_info::TypeInfo; -use sp_runtime_interface::pass_by::PassByInner; use sp_std::{vec, vec::Vec}; /// Identifier used to match public keys against bandersnatch-vrf keys. pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"band"); /// Context used to produce a plain signature without any VRF input/output. -#[cfg(feature = "full_crypto")] pub const SIGNING_CTX: &[u8] = b"BandersnatchSigningContext"; -#[cfg(feature = "full_crypto")] -const SEED_SERIALIZED_SIZE: usize = 32; +/// The byte length of secret key seed. +pub const SEED_SERIALIZED_SIZE: usize = 32; -const PUBLIC_SERIALIZED_SIZE: usize = 33; -const SIGNATURE_SERIALIZED_SIZE: usize = 65; -const PREOUT_SERIALIZED_SIZE: usize = 33; +/// The byte length of serialized public key. +pub const PUBLIC_SERIALIZED_SIZE: usize = 33; -/// Bandersnatch public key. -#[cfg_attr(feature = "full_crypto", derive(Hash))] -#[derive( - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Encode, - Decode, - PassByInner, - MaxEncodedLen, - TypeInfo, -)] -pub struct Public(pub [u8; PUBLIC_SERIALIZED_SIZE]); - -impl UncheckedFrom<[u8; PUBLIC_SERIALIZED_SIZE]> for Public { - fn unchecked_from(raw: [u8; PUBLIC_SERIALIZED_SIZE]) -> Self { - Public(raw) - } -} +/// The byte length of serialized signature. +pub const SIGNATURE_SERIALIZED_SIZE: usize = 65; -impl AsRef<[u8; PUBLIC_SERIALIZED_SIZE]> for Public { - fn as_ref(&self) -> &[u8; PUBLIC_SERIALIZED_SIZE] { - &self.0 - } -} - -impl AsRef<[u8]> for Public { - fn as_ref(&self) -> &[u8] { - &self.0[..] - } -} +/// The byte length of serialized pre-output. +pub const PREOUT_SERIALIZED_SIZE: usize = 33; -impl AsMut<[u8]> for Public { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0[..] - } -} +#[doc(hidden)] +pub struct BandersnatchTag; -impl TryFrom<&[u8]> for Public { - type Error = (); - - fn try_from(data: &[u8]) -> Result { - if data.len() != PUBLIC_SERIALIZED_SIZE { - return Err(()) - } - let mut r = [0u8; PUBLIC_SERIALIZED_SIZE]; - r.copy_from_slice(data); - Ok(Self::unchecked_from(r)) - } -} - -impl ByteArray for Public { - const LEN: usize = PUBLIC_SERIALIZED_SIZE; -} - -impl TraitPublic for Public {} +/// Bandersnatch public key. +pub type Public = PublicBytes; impl CryptoType for Public { - #[cfg(feature = "full_crypto")] type Pair = Pair; } -impl Derive for Public {} - -impl sp_std::fmt::Debug for Public { - #[cfg(feature = "std")] - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - let s = self.to_ss58check(); - write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.as_ref()), &s[0..8]) - } - - #[cfg(not(feature = "std"))] - fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - Ok(()) - } -} - -#[cfg(feature = "serde")] -impl Serialize for Public { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_str(&self.to_ss58check()) - } -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for Public { - fn deserialize>(deserializer: D) -> Result { - Public::from_ss58check(&String::deserialize(deserializer)?) - .map_err(|e| de::Error::custom(format!("{:?}", e))) - } -} - /// Bandersnatch signature. /// /// The signature is created via the [`VrfSecret::vrf_sign`] using [`SIGNING_CTX`] as transcript /// `label`. -#[cfg_attr(feature = "full_crypto", derive(Hash))] -#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, PassByInner, MaxEncodedLen, TypeInfo)] -pub struct Signature([u8; SIGNATURE_SERIALIZED_SIZE]); - -impl UncheckedFrom<[u8; SIGNATURE_SERIALIZED_SIZE]> for Signature { - fn unchecked_from(raw: [u8; SIGNATURE_SERIALIZED_SIZE]) -> Self { - Signature(raw) - } -} - -impl AsRef<[u8]> for Signature { - fn as_ref(&self) -> &[u8] { - &self.0[..] - } -} - -impl AsMut<[u8]> for Signature { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0[..] - } -} - -impl TryFrom<&[u8]> for Signature { - type Error = (); - - fn try_from(data: &[u8]) -> Result { - if data.len() != SIGNATURE_SERIALIZED_SIZE { - return Err(()) - } - let mut r = [0u8; SIGNATURE_SERIALIZED_SIZE]; - r.copy_from_slice(data); - Ok(Self::unchecked_from(r)) - } -} - -impl ByteArray for Signature { - const LEN: usize = SIGNATURE_SERIALIZED_SIZE; -} +pub type Signature = SignatureBytes; impl CryptoType for Signature { - #[cfg(feature = "full_crypto")] type Pair = Pair; } -impl sp_std::fmt::Debug for Signature { - #[cfg(feature = "std")] - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - write!(f, "{}", crate::hexdisplay::HexDisplay::from(&self.0)) - } - - #[cfg(not(feature = "std"))] - fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - Ok(()) - } -} - /// The raw secret seed, which can be used to reconstruct the secret [`Pair`]. -#[cfg(feature = "full_crypto")] type Seed = [u8; SEED_SERIALIZED_SIZE]; /// Bandersnatch secret key. -#[cfg(feature = "full_crypto")] #[derive(Clone)] pub struct Pair { secret: SecretKey, seed: Seed, } -#[cfg(feature = "full_crypto")] impl Pair { /// Get the key seed. pub fn seed(&self) -> Seed { @@ -230,7 +88,6 @@ impl Pair { } } -#[cfg(feature = "full_crypto")] impl TraitPair for Pair { type Seed = Seed; type Public = Public; @@ -287,6 +144,7 @@ impl TraitPair for Pair { /// the constant label [`SIGNING_CTX`] and `data` without any additional data. /// /// See [`vrf::VrfSignData`] for additional details. + #[cfg(feature = "full_crypto")] fn sign(&self, data: &[u8]) -> Signature { let data = vrf::VrfSignData::new_unchecked(SIGNING_CTX, &[data], None); self.vrf_sign(&data).signature @@ -305,7 +163,6 @@ impl TraitPair for Pair { } } -#[cfg(feature = "full_crypto")] impl CryptoType for Pair { type Pair = Pair; } @@ -392,7 +249,7 @@ pub mod vrf { /// /// The `transcript` summarizes a set of messages which are defining a particular /// protocol by automating the Fiat-Shamir transform for challenge generation. - /// A good explaination of the topic can be found in Merlin [docs](https://merlin.cool/) + /// A good explanation of the topic can be found in Merlin [docs](https://merlin.cool/) /// /// The `inputs` is a sequence of [`VrfInput`]s which, during the signing procedure, are /// first transformed to [`VrfPreOutput`]s. Both inputs and pre-outputs are then appended to @@ -550,8 +407,7 @@ pub mod vrf { thin_signature.preouts.into_iter().map(VrfPreOutput).collect(); let pre_outputs = VrfIosVec::truncate_from(pre_outputs); - let mut signature = - VrfSignature { signature: Signature([0; SIGNATURE_SERIALIZED_SIZE]), pre_outputs }; + let mut signature = VrfSignature { signature: Signature::default(), pre_outputs }; thin_signature .proof @@ -590,7 +446,7 @@ pub mod vrf { // This is another hack used because backend signature type is generic over // the number of ios. let Ok(proof) = ThinVrfSignature::<0>::deserialize_compressed_unchecked( - signature.signature.as_ref(), + signature.signature.as_slice(), ) .map(|s| s.proof) else { return false diff --git a/substrate/primitives/core/src/bls.rs b/substrate/primitives/core/src/bls.rs index 0c84d0ba8e6c0fd295690ca5fddf48440f083379..bb04babb3f180f3728c3a0e8159b725aeb7a2d47 100644 --- a/substrate/primitives/core/src/bls.rs +++ b/substrate/primitives/core/src/bls.rs @@ -23,29 +23,17 @@ //! Chaum-Pedersen proof uses the same hash-to-field specified in RFC 9380 for the field of the BLS //! curve. -#[cfg(feature = "serde")] -use crate::crypto::Ss58Codec; -use crate::crypto::{ByteArray, CryptoType, Derive, Public as TraitPublic, UncheckedFrom}; -#[cfg(feature = "full_crypto")] -use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError}; +use crate::crypto::{ + CryptoType, DeriveError, DeriveJunction, Pair as TraitPair, PublicBytes, SecretStringError, + SignatureBytes, UncheckedFrom, +}; -#[cfg(feature = "full_crypto")] use sp_std::vec::Vec; -use codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; - -#[cfg(feature = "serde")] -use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; -#[cfg(all(not(feature = "std"), feature = "serde"))] -use sp_std::alloc::{format, string::String}; - -use w3f_bls::{DoublePublicKey, DoubleSignature, EngineBLS, SerializableToBytes, TinyBLS381}; -#[cfg(feature = "full_crypto")] -use w3f_bls::{DoublePublicKeyScheme, Keypair, Message, SecretKey}; - -use sp_runtime_interface::pass_by::{self, PassBy, PassByInner}; -use sp_std::{convert::TryFrom, marker::PhantomData, ops::Deref}; +use w3f_bls::{ + DoublePublicKey, DoublePublicKeyScheme, DoubleSignature, EngineBLS, Keypair, Message, + SecretKey, SerializableToBytes, TinyBLS381, +}; /// BLS-377 specialized types pub mod bls377 { @@ -56,8 +44,10 @@ pub mod bls377 { /// An identifier used to match public keys against BLS12-377 keys pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"bls7"); + #[doc(hidden)] + pub type Bls377Tag = TinyBLS377; + /// BLS12-377 key pair. - #[cfg(feature = "full_crypto")] pub type Pair = super::Pair; /// BLS12-377 public key. pub type Public = super::Public; @@ -79,7 +69,6 @@ pub mod bls381 { pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"bls8"); /// BLS12-381 key pair. - #[cfg(feature = "full_crypto")] pub type Pair = super::Pair; /// BLS12-381 public key. pub type Public = super::Public; @@ -96,7 +85,6 @@ trait BlsBound: EngineBLS + HardJunctionId + Send + Sync + 'static {} impl BlsBound for T {} /// Secret key serialized size -#[cfg(feature = "full_crypto")] const SECRET_KEY_SERIALIZED_SIZE: usize = as SerializableToBytes>::SERIALIZED_BYTES_SIZE; @@ -113,314 +101,28 @@ pub const SIGNATURE_SERIALIZED_SIZE: usize = /// It's not called a "secret key" because ring doesn't expose the secret keys /// of the key pair (yeah, dumb); as such we're forced to remember the seed manually if we /// will need it later (such as for HDKD). -#[cfg(feature = "full_crypto")] type Seed = [u8; SECRET_KEY_SERIALIZED_SIZE]; -/// A public key. -#[derive(Copy, Encode, Decode, MaxEncodedLen, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct Public { - inner: [u8; PUBLIC_KEY_SERIALIZED_SIZE], - _phantom: PhantomData T>, -} - -impl Clone for Public { - fn clone(&self) -> Self { - Self { inner: self.inner, _phantom: PhantomData } - } -} - -impl PartialEq for Public { - fn eq(&self, other: &Self) -> bool { - self.inner == other.inner - } -} - -impl Eq for Public {} - -impl PartialOrd for Public { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Public { - fn cmp(&self, other: &Self) -> sp_std::cmp::Ordering { - self.inner.cmp(&other.inner) - } -} +#[doc(hidden)] +pub struct BlsTag; -#[cfg(feature = "full_crypto")] -impl sp_std::hash::Hash for Public { - fn hash(&self, state: &mut H) { - self.inner.hash(state) - } -} - -impl ByteArray for Public { - const LEN: usize = PUBLIC_KEY_SERIALIZED_SIZE; -} - -impl PassByInner for Public { - type Inner = [u8; PUBLIC_KEY_SERIALIZED_SIZE]; - - fn into_inner(self) -> Self::Inner { - self.inner - } - - fn inner(&self) -> &Self::Inner { - &self.inner - } - - fn from_inner(inner: Self::Inner) -> Self { - Self { inner, _phantom: PhantomData } - } -} - -impl PassBy for Public { - type PassBy = pass_by::Inner; -} - -impl AsRef<[u8; PUBLIC_KEY_SERIALIZED_SIZE]> for Public { - fn as_ref(&self) -> &[u8; PUBLIC_KEY_SERIALIZED_SIZE] { - &self.inner - } -} - -impl AsRef<[u8]> for Public { - fn as_ref(&self) -> &[u8] { - &self.inner[..] - } -} - -impl AsMut<[u8]> for Public { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.inner[..] - } -} - -impl Deref for Public { - type Target = [u8]; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl TryFrom<&[u8]> for Public { - type Error = (); - - fn try_from(data: &[u8]) -> Result { - if data.len() != PUBLIC_KEY_SERIALIZED_SIZE { - return Err(()) - } - let mut r = [0u8; PUBLIC_KEY_SERIALIZED_SIZE]; - r.copy_from_slice(data); - Ok(Self::unchecked_from(r)) - } -} - -impl From> for [u8; PUBLIC_KEY_SERIALIZED_SIZE] { - fn from(x: Public) -> Self { - x.inner - } -} - -#[cfg(feature = "full_crypto")] -impl From> for Public { - fn from(x: Pair) -> Self { - x.public() - } -} - -impl UncheckedFrom<[u8; PUBLIC_KEY_SERIALIZED_SIZE]> for Public { - fn unchecked_from(data: [u8; PUBLIC_KEY_SERIALIZED_SIZE]) -> Self { - Public { inner: data, _phantom: PhantomData } - } -} - -#[cfg(feature = "std")] -impl std::str::FromStr for Public { - type Err = crate::crypto::PublicError; - - fn from_str(s: &str) -> Result { - Self::from_ss58check(s) - } -} - -#[cfg(feature = "std")] -impl std::fmt::Display for Public { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self.to_ss58check()) - } -} - -#[cfg(feature = "std")] -impl sp_std::fmt::Debug for Public { - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - let s = self.to_ss58check(); - write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.inner), &s[0..8]) - } -} - -#[cfg(not(feature = "std"))] -impl sp_std::fmt::Debug for Public { - fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - Ok(()) - } -} - -#[cfg(feature = "serde")] -impl Serialize for Public { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.to_ss58check()) - } -} - -#[cfg(feature = "serde")] -impl<'de, T: BlsBound> Deserialize<'de> for Public { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Public::from_ss58check(&String::deserialize(deserializer)?) - .map_err(|e| de::Error::custom(format!("{:?}", e))) - } -} - -impl TraitPublic for Public {} - -impl Derive for Public {} +/// A public key. +pub type Public = PublicBytes; impl CryptoType for Public { - #[cfg(feature = "full_crypto")] type Pair = Pair; } /// A generic BLS signature. -#[derive(Copy, Encode, Decode, MaxEncodedLen, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct Signature { - inner: [u8; SIGNATURE_SERIALIZED_SIZE], - _phantom: PhantomData T>, -} - -impl Clone for Signature { - fn clone(&self) -> Self { - Self { inner: self.inner, _phantom: PhantomData } - } -} - -impl PartialEq for Signature { - fn eq(&self, other: &Self) -> bool { - self.inner == other.inner - } -} - -impl Eq for Signature {} - -#[cfg(feature = "full_crypto")] -impl sp_std::hash::Hash for Signature { - fn hash(&self, state: &mut H) { - self.inner.hash(state) - } -} - -impl ByteArray for Signature { - const LEN: usize = SIGNATURE_SERIALIZED_SIZE; -} - -impl TryFrom<&[u8]> for Signature { - type Error = (); - - fn try_from(data: &[u8]) -> Result { - if data.len() != SIGNATURE_SERIALIZED_SIZE { - return Err(()) - } - let mut inner = [0u8; SIGNATURE_SERIALIZED_SIZE]; - inner.copy_from_slice(data); - Ok(Signature::unchecked_from(inner)) - } -} - -#[cfg(feature = "serde")] -impl Serialize for Signature { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&array_bytes::bytes2hex("", self)) - } -} - -#[cfg(feature = "serde")] -impl<'de, T> Deserialize<'de> for Signature { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let signature_hex = array_bytes::hex2bytes(&String::deserialize(deserializer)?) - .map_err(|e| de::Error::custom(format!("{:?}", e)))?; - Signature::try_from(signature_hex.as_ref()) - .map_err(|e| de::Error::custom(format!("{:?}", e))) - } -} - -impl From> for [u8; SIGNATURE_SERIALIZED_SIZE] { - fn from(signature: Signature) -> [u8; SIGNATURE_SERIALIZED_SIZE] { - signature.inner - } -} - -impl AsRef<[u8; SIGNATURE_SERIALIZED_SIZE]> for Signature { - fn as_ref(&self) -> &[u8; SIGNATURE_SERIALIZED_SIZE] { - &self.inner - } -} - -impl AsRef<[u8]> for Signature { - fn as_ref(&self) -> &[u8] { - &self.inner[..] - } -} - -impl AsMut<[u8]> for Signature { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.inner[..] - } -} - -impl sp_std::fmt::Debug for Signature { - #[cfg(feature = "std")] - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - write!(f, "{}", crate::hexdisplay::HexDisplay::from(&self.inner)) - } - - #[cfg(not(feature = "std"))] - fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - Ok(()) - } -} - -impl UncheckedFrom<[u8; SIGNATURE_SERIALIZED_SIZE]> for Signature { - fn unchecked_from(data: [u8; SIGNATURE_SERIALIZED_SIZE]) -> Self { - Signature { inner: data, _phantom: PhantomData } - } -} +pub type Signature = SignatureBytes; impl CryptoType for Signature { - #[cfg(feature = "full_crypto")] type Pair = Pair; } /// A key pair. -#[cfg(feature = "full_crypto")] pub struct Pair(Keypair); -#[cfg(feature = "full_crypto")] impl Clone for Pair { fn clone(&self) -> Self { Pair(self.0.clone()) @@ -432,15 +134,13 @@ trait HardJunctionId { } /// Derive a single hard junction. -#[cfg(feature = "full_crypto")] fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed { + use codec::Encode; (T::ID, secret_seed, cc).using_encoded(sp_crypto_hashing::blake2_256) } -#[cfg(feature = "full_crypto")] impl Pair {} -#[cfg(feature = "full_crypto")] impl TraitPair for Pair { type Seed = Seed; type Public = Public; @@ -480,6 +180,7 @@ impl TraitPair for Pair { Self::Public::unchecked_from(raw) } + #[cfg(feature = "full_crypto")] fn sign(&self, message: &[u8]) -> Self::Signature { let mut mutable_self = self.clone(); let r: [u8; SIGNATURE_SERIALIZED_SIZE] = @@ -501,7 +202,7 @@ impl TraitPair for Pair { Err(_) => return false, }; - let sig_array = match sig.inner[..].try_into() { + let sig_array = match sig.0[..].try_into() { Ok(s) => s, Err(_) => return false, }; @@ -523,15 +224,16 @@ impl TraitPair for Pair { } } -#[cfg(feature = "full_crypto")] impl CryptoType for Pair { type Pair = Pair; } // Test set exercising the BLS12-377 implementation #[cfg(test)] -mod test { +mod tests { use super::*; + #[cfg(feature = "serde")] + use crate::crypto::Ss58Codec; use crate::crypto::DEV_PHRASE; use bls377::{Pair, Signature}; diff --git a/substrate/primitives/core/src/crypto.rs b/substrate/primitives/core/src/crypto.rs index 2a8be2a2ba8503d7dca2d324e405f6a1af840247..b13899fff5176eb76defa5ef32f840ad2c8c0ceb 100644 --- a/substrate/primitives/core/src/crypto.rs +++ b/substrate/primitives/core/src/crypto.rs @@ -18,7 +18,6 @@ //! Cryptographic utilities. use crate::{ed25519, sr25519}; -#[cfg(feature = "std")] use bip39::{Language, Mnemonic}; use codec::{Decode, Encode, MaxEncodedLen}; #[cfg(feature = "std")] @@ -26,7 +25,6 @@ use itertools::Itertools; #[cfg(feature = "std")] use rand::{rngs::OsRng, RngCore}; use scale_info::TypeInfo; -#[cfg(feature = "std")] pub use secrecy::{ExposeSecret, SecretString}; use sp_runtime_interface::pass_by::PassByInner; #[doc(hidden)] @@ -41,10 +39,10 @@ pub use ss58_registry::{from_known_address_format, Ss58AddressFormat, Ss58Addres /// Trait to zeroize a memory buffer. pub use zeroize::Zeroize; -#[cfg(feature = "std")] -pub use crate::address_uri::AddressUri; -#[cfg(any(feature = "std", feature = "full_crypto"))] -pub use crate::address_uri::Error as AddressUriError; +pub use crate::{ + address_uri::{AddressUri, Error as AddressUriError}, + crypto_bytes::{CryptoBytes, PublicBytes, SignatureBytes}, +}; /// The root phrase for our publicly known keys. pub const DEV_PHRASE: &str = @@ -82,7 +80,6 @@ impl> UncheckedInto for S { /// An error with the interpretation of a secret. #[cfg_attr(feature = "std", derive(thiserror::Error))] #[derive(Debug, Clone, PartialEq, Eq)] -#[cfg(feature = "full_crypto")] pub enum SecretStringError { /// The overall format was invalid (e.g. the seed phrase contained symbols). #[cfg_attr(feature = "std", error("Invalid format {0}"))] @@ -104,7 +101,6 @@ pub enum SecretStringError { InvalidPath, } -#[cfg(any(feature = "std", feature = "full_crypto"))] impl From for SecretStringError { fn from(e: AddressUriError) -> Self { Self::InvalidFormat(e) @@ -114,7 +110,6 @@ impl From for SecretStringError { /// An error when deriving a key. #[cfg_attr(feature = "std", derive(thiserror::Error))] #[derive(Debug, Clone, PartialEq, Eq)] -#[cfg(feature = "full_crypto")] pub enum DeriveError { /// A soft key was found in the path (and is unsupported). #[cfg_attr(feature = "std", error("Soft key in path"))] @@ -125,7 +120,6 @@ pub enum DeriveError { /// a new secret key from an existing secret key and, in the case of `SoftRaw` and `SoftIndex` /// a new public key from an existing public key. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Encode, Decode)] -#[cfg(any(feature = "full_crypto", feature = "serde"))] pub enum DeriveJunction { /// Soft (vanilla) derivation. Public keys have a correspondent derivation. Soft([u8; JUNCTION_ID_LEN]), @@ -133,7 +127,6 @@ pub enum DeriveJunction { Hard([u8; JUNCTION_ID_LEN]), } -#[cfg(any(feature = "full_crypto", feature = "serde"))] impl DeriveJunction { /// Consume self to return a soft derive junction with the same chain code. pub fn soften(self) -> Self { @@ -192,7 +185,6 @@ impl DeriveJunction { } } -#[cfg(any(feature = "full_crypto", feature = "serde"))] impl> From for DeriveJunction { fn from(j: T) -> DeriveJunction { let j = j.as_ref(); @@ -493,8 +485,11 @@ pub trait ByteArray: AsRef<[u8]> + AsMut<[u8]> + for<'a> TryFrom<&'a [u8], Error } } -/// Trait suitable for typical cryptographic key public type. -pub trait Public: CryptoType + ByteArray + Derive + PartialEq + Eq + Clone + Send + Sync {} +/// Trait suitable for cryptographic public keys. +pub trait Public: CryptoType + ByteArray + PartialEq + Eq + Clone + Send + Sync + Derive {} + +/// Trait suitable for cryptographic signatures. +pub trait Signature: CryptoType + ByteArray + PartialEq + Eq + Clone + Send + Sync {} /// An opaque 32-byte cryptographic identifier. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, TypeInfo)] @@ -656,32 +651,11 @@ pub use self::dummy::*; mod dummy { use super::*; - /// Dummy cryptography. Doesn't do anything. - #[derive(Clone, Hash, Default, Eq, PartialEq)] - pub struct Dummy; - - impl AsRef<[u8]> for Dummy { - fn as_ref(&self) -> &[u8] { - &b""[..] - } - } + #[doc(hidden)] + pub struct DummyTag; - impl AsMut<[u8]> for Dummy { - fn as_mut(&mut self) -> &mut [u8] { - unsafe { - #[allow(mutable_transmutes)] - sp_std::mem::transmute::<_, &'static mut [u8]>(&b""[..]) - } - } - } - - impl<'a> TryFrom<&'a [u8]> for Dummy { - type Error = (); - - fn try_from(_: &'a [u8]) -> Result { - Ok(Self) - } - } + /// Dummy cryptography. Doesn't do anything. + pub type Dummy = CryptoBytes<0, DummyTag>; impl CryptoType for Dummy { type Pair = Dummy; @@ -689,21 +663,10 @@ mod dummy { impl Derive for Dummy {} - impl ByteArray for Dummy { - const LEN: usize = 0; - fn from_slice(_: &[u8]) -> Result { - Ok(Self) - } - #[cfg(feature = "std")] - fn to_raw_vec(&self) -> Vec { - vec![] - } - fn as_slice(&self) -> &[u8] { - b"" - } - } impl Public for Dummy {} + impl Signature for Dummy {} + impl Pair for Dummy { type Public = Dummy; type Seed = Dummy; @@ -724,15 +687,15 @@ mod dummy { _: Iter, _: Option, ) -> Result<(Self, Option), DeriveError> { - Ok((Self, None)) + Ok((Self::default(), None)) } fn from_seed_slice(_: &[u8]) -> Result { - Ok(Self) + Ok(Self::default()) } fn sign(&self, _: &[u8]) -> Self::Signature { - Self + Self::default() } fn verify>(_: &Self::Signature, _: M, _: &Self::Public) -> bool { @@ -740,11 +703,11 @@ mod dummy { } fn public(&self) -> Self::Public { - Self + Self::default() } fn to_raw_vec(&self) -> Vec { - vec![] + Default::default() } } } @@ -812,7 +775,6 @@ mod dummy { /// assert_eq!("0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a", suri.phrase.expose_secret()); /// assert!(suri.password.is_none()); /// ``` -#[cfg(feature = "std")] pub struct SecretUri { /// The phrase to derive the private key. /// @@ -824,7 +786,6 @@ pub struct SecretUri { pub junctions: Vec, } -#[cfg(feature = "std")] impl sp_std::str::FromStr for SecretUri { type Err = SecretStringError; @@ -845,7 +806,6 @@ impl sp_std::str::FromStr for SecretUri { /// Trait suitable for typical cryptographic PKI key pair type. /// /// For now it just specifies how to create a key from a phrase and derivation path. -#[cfg(feature = "full_crypto")] pub trait Pair: CryptoType + Sized { /// The type which is used to encode a public key. type Public: Public + Hash; @@ -856,7 +816,7 @@ pub trait Pair: CryptoType + Sized { /// The type used to represent a signature. Can be created from a key pair and a message /// and verified with the message and a public key. - type Signature: AsRef<[u8]>; + type Signature: Signature; /// Generate new secure (random) key pair. /// @@ -878,21 +838,19 @@ pub trait Pair: CryptoType + Sized { #[cfg(feature = "std")] fn generate_with_phrase(password: Option<&str>) -> (Self, String, Self::Seed) { let mnemonic = Mnemonic::generate(12).expect("Mnemonic generation always works; qed"); - let phrase = mnemonic.word_iter().join(" "); + let phrase = mnemonic.words().join(" "); let (pair, seed) = Self::from_phrase(&phrase, password) .expect("All phrases generated by Mnemonic are valid; qed"); (pair, phrase.to_owned(), seed) } /// Returns the KeyPair from the English BIP39 seed `phrase`, or an error if it's invalid. - #[cfg(feature = "std")] fn from_phrase( phrase: &str, password: Option<&str>, ) -> Result<(Self, Self::Seed), SecretStringError> { let mnemonic = Mnemonic::parse_in(Language::English, phrase) .map_err(|_| SecretStringError::InvalidPhrase)?; - let (entropy, entropy_len) = mnemonic.to_entropy_array(); let big_seed = substrate_bip39::seed_from_entropy(&entropy[0..entropy_len], password.unwrap_or("")) @@ -928,6 +886,7 @@ pub trait Pair: CryptoType + Sized { fn from_seed_slice(seed: &[u8]) -> Result; /// Sign a message. + #[cfg(feature = "full_crypto")] fn sign(&self, message: &[u8]) -> Self::Signature; /// Verify a signature on a message. Returns true if the signature is good. @@ -962,7 +921,6 @@ pub trait Pair: CryptoType + Sized { /// Notably, integer junction indices may be legally prefixed with arbitrary number of zeros. /// Similarly an empty password (ending the SURI with `///`) is perfectly valid and will /// generally be equivalent to no password at all. - #[cfg(feature = "std")] fn from_string_with_seed( s: &str, password_override: Option<&str>, @@ -996,7 +954,6 @@ pub trait Pair: CryptoType + Sized { /// Interprets the string `s` in order to generate a key pair. /// /// See [`from_string_with_seed`](Pair::from_string_with_seed) for more extensive documentation. - #[cfg(feature = "std")] fn from_string(s: &str, password_override: Option<&str>) -> Result { Self::from_string_with_seed(s, password_override).map(|x| x.0) } @@ -1054,7 +1011,6 @@ where /// Type which has a particular kind of crypto associated with it. pub trait CryptoType { /// The pair key type of this crypto. - #[cfg(feature = "full_crypto")] type Pair: Pair; } @@ -1233,6 +1189,8 @@ mod tests { use super::*; use crate::DeriveJunction; + struct TestCryptoTag; + #[derive(Clone, Eq, PartialEq, Debug)] enum TestPair { Generated, @@ -1241,59 +1199,33 @@ mod tests { Standard { phrase: String, password: Option, path: Vec }, Seed(Vec), } + impl Default for TestPair { fn default() -> Self { TestPair::Generated } } + impl CryptoType for TestPair { type Pair = Self; } - #[derive(Clone, PartialEq, Eq, Hash, Default)] - struct TestPublic; - impl AsRef<[u8]> for TestPublic { - fn as_ref(&self) -> &[u8] { - &[] - } - } - impl AsMut<[u8]> for TestPublic { - fn as_mut(&mut self) -> &mut [u8] { - &mut [] - } - } - impl<'a> TryFrom<&'a [u8]> for TestPublic { - type Error = (); + type TestPublic = PublicBytes<0, TestCryptoTag>; - fn try_from(data: &'a [u8]) -> Result { - Self::from_slice(data) - } - } impl CryptoType for TestPublic { type Pair = TestPair; } - impl Derive for TestPublic {} - impl ByteArray for TestPublic { - const LEN: usize = 0; - fn from_slice(bytes: &[u8]) -> Result { - if bytes.is_empty() { - Ok(Self) - } else { - Err(()) - } - } - fn as_slice(&self) -> &[u8] { - &[] - } - fn to_raw_vec(&self) -> Vec { - vec![] - } + + type TestSignature = SignatureBytes<0, TestCryptoTag>; + + impl CryptoType for TestSignature { + type Pair = TestPair; } - impl Public for TestPublic {} + impl Pair for TestPair { type Public = TestPublic; type Seed = [u8; 8]; - type Signature = [u8; 0]; + type Signature = TestSignature; fn generate() -> (Self, ::Seed) { (TestPair::Generated, [0u8; 8]) @@ -1342,7 +1274,7 @@ mod tests { } fn sign(&self, _message: &[u8]) -> Self::Signature { - [] + TestSignature::default() } fn verify>(_: &Self::Signature, _: M, _: &Self::Public) -> bool { @@ -1350,7 +1282,7 @@ mod tests { } fn public(&self) -> Self::Public { - TestPublic + TestPublic::default() } fn from_seed_slice(seed: &[u8]) -> Result { diff --git a/substrate/primitives/core/src/crypto_bytes.rs b/substrate/primitives/core/src/crypto_bytes.rs new file mode 100644 index 0000000000000000000000000000000000000000..ee5f3482f743a6a63b3465c75fcdb6d2d82ab6e7 --- /dev/null +++ b/substrate/primitives/core/src/crypto_bytes.rs @@ -0,0 +1,379 @@ +// 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. + +//! Generic byte array which can be specialized with a marker type. + +use crate::{ + crypto::{CryptoType, Derive, FromEntropy, Public, Signature, UncheckedFrom}, + hash::{H256, H512}, +}; + +use codec::{Decode, Encode, MaxEncodedLen}; +use core::marker::PhantomData; +use scale_info::TypeInfo; + +use sp_runtime_interface::pass_by::{self, PassBy, PassByInner}; + +#[cfg(feature = "serde")] +use crate::crypto::Ss58Codec; +#[cfg(feature = "serde")] +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +#[cfg(all(not(feature = "std"), feature = "serde"))] +use sp_std::alloc::{format, string::String}; + +pub use public_bytes::*; +pub use signature_bytes::*; + +/// Generic byte array holding some crypto-related raw data. +/// +/// The type is generic over a constant length `N` and a "tag" `T` which +/// can be used to specialize the byte array without requiring newtypes. +/// +/// The tag `T` is held in a `PhantomDataT>`, a trick allowing +/// `CryptoBytes` to be `Send` and `Sync` regardless of `T` properties +/// ([ref](https://doc.rust-lang.org/nomicon/phantom-data.html#table-of-phantomdata-patterns)). +#[derive(Encode, Decode, MaxEncodedLen)] +#[repr(transparent)] +pub struct CryptoBytes(pub [u8; N], PhantomData T>); + +impl Copy for CryptoBytes {} + +impl Clone for CryptoBytes { + fn clone(&self) -> Self { + Self(self.0, PhantomData) + } +} + +impl TypeInfo for CryptoBytes { + type Identity = [u8; N]; + + fn type_info() -> scale_info::Type { + Self::Identity::type_info() + } +} + +impl PartialOrd for CryptoBytes { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl Ord for CryptoBytes { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.0.cmp(&other.0) + } +} + +impl PartialEq for CryptoBytes { + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) + } +} + +impl core::hash::Hash for CryptoBytes { + fn hash(&self, state: &mut H) { + self.0.hash(state) + } +} + +impl Eq for CryptoBytes {} + +impl Default for CryptoBytes { + fn default() -> Self { + Self([0_u8; N], PhantomData) + } +} + +impl PassByInner for CryptoBytes { + type Inner = [u8; N]; + + fn into_inner(self) -> Self::Inner { + self.0 + } + + fn inner(&self) -> &Self::Inner { + &self.0 + } + + fn from_inner(inner: Self::Inner) -> Self { + Self(inner, PhantomData) + } +} + +impl PassBy for CryptoBytes { + type PassBy = pass_by::Inner; +} + +impl AsRef<[u8]> for CryptoBytes { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} + +impl AsMut<[u8]> for CryptoBytes { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0[..] + } +} + +impl From> for [u8; N] { + fn from(v: CryptoBytes) -> [u8; N] { + v.0 + } +} + +impl AsRef<[u8; N]> for CryptoBytes { + fn as_ref(&self) -> &[u8; N] { + &self.0 + } +} + +impl AsMut<[u8; N]> for CryptoBytes { + fn as_mut(&mut self) -> &mut [u8; N] { + &mut self.0 + } +} + +impl From<[u8; N]> for CryptoBytes { + fn from(value: [u8; N]) -> Self { + Self::from_raw(value) + } +} + +impl TryFrom<&[u8]> for CryptoBytes { + type Error = (); + + fn try_from(data: &[u8]) -> Result { + if data.len() != N { + return Err(()) + } + let mut r = [0u8; N]; + r.copy_from_slice(data); + Ok(Self::from_raw(r)) + } +} + +impl UncheckedFrom<[u8; N]> for CryptoBytes { + fn unchecked_from(data: [u8; N]) -> Self { + Self::from_raw(data) + } +} + +impl core::ops::Deref for CryptoBytes { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl CryptoBytes { + /// Construct from raw array. + pub fn from_raw(inner: [u8; N]) -> Self { + Self(inner, PhantomData) + } + + /// Construct from raw array. + pub fn to_raw(self) -> [u8; N] { + self.0 + } + + /// Return a slice filled with raw data. + pub fn as_array_ref(&self) -> &[u8; N] { + &self.0 + } +} + +impl crate::ByteArray for CryptoBytes { + const LEN: usize = N; +} + +impl FromEntropy for CryptoBytes { + fn from_entropy(input: &mut impl codec::Input) -> Result { + let mut result = Self::default(); + input.read(result.as_mut())?; + Ok(result) + } +} + +impl From> for H256 { + fn from(x: CryptoBytes<32, T>) -> H256 { + H256::from(x.0) + } +} + +impl From> for H512 { + fn from(x: CryptoBytes<64, T>) -> H512 { + H512::from(x.0) + } +} + +impl UncheckedFrom for CryptoBytes<32, T> { + fn unchecked_from(x: H256) -> Self { + Self::from_h256(x) + } +} + +impl CryptoBytes<32, T> { + /// A new instance from an H256. + pub fn from_h256(x: H256) -> Self { + Self::from_raw(x.into()) + } +} + +impl CryptoBytes<64, T> { + /// A new instance from an H512. + pub fn from_h512(x: H512) -> Self { + Self::from_raw(x.into()) + } +} + +mod public_bytes { + use super::*; + + /// Tag used for generic public key bytes. + pub struct PublicTag; + + /// Generic encoded public key. + pub type PublicBytes = CryptoBytes; + + impl Derive for PublicBytes where Self: CryptoType {} + + impl Public for PublicBytes where Self: CryptoType {} + + impl sp_std::fmt::Debug for PublicBytes + where + Self: CryptoType, + { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let s = self.to_ss58check(); + write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.as_ref()), &s[0..8]) + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + Ok(()) + } + } + + #[cfg(feature = "std")] + impl std::fmt::Display for PublicBytes + where + Self: CryptoType, + { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.to_ss58check()) + } + } + + #[cfg(feature = "std")] + impl std::str::FromStr for PublicBytes + where + Self: CryptoType, + { + type Err = crate::crypto::PublicError; + + fn from_str(s: &str) -> Result { + Self::from_ss58check(s) + } + } + + #[cfg(feature = "serde")] + impl Serialize for PublicBytes + where + Self: CryptoType, + { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_ss58check()) + } + } + + #[cfg(feature = "serde")] + impl<'de, const N: usize, SubTag> Deserialize<'de> for PublicBytes + where + Self: CryptoType, + { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Self::from_ss58check(&String::deserialize(deserializer)?) + .map_err(|e| de::Error::custom(format!("{:?}", e))) + } + } +} + +mod signature_bytes { + use super::*; + + /// Tag used for generic signature bytes. + pub struct SignatureTag; + + /// Generic encoded signature. + pub type SignatureBytes = CryptoBytes; + + impl Signature for SignatureBytes where Self: CryptoType {} + + #[cfg(feature = "serde")] + impl Serialize for SignatureBytes + where + Self: CryptoType, + { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&array_bytes::bytes2hex("", self)) + } + } + + #[cfg(feature = "serde")] + impl<'de, const N: usize, SubTag> Deserialize<'de> for SignatureBytes + where + Self: CryptoType, + { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let signature_hex = array_bytes::hex2bytes(&String::deserialize(deserializer)?) + .map_err(|e| de::Error::custom(format!("{:?}", e)))?; + Self::try_from(signature_hex.as_ref()) + .map_err(|e| de::Error::custom(format!("{:?}", e))) + } + } + + impl sp_std::fmt::Debug for SignatureBytes + where + Self: CryptoType, + { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + write!(f, "{}", crate::hexdisplay::HexDisplay::from(&&self.0[..])) + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + Ok(()) + } + } +} diff --git a/substrate/primitives/core/src/ecdsa.rs b/substrate/primitives/core/src/ecdsa.rs index f172b3a7d02c7a1adb1ff2fc37933d3cf47a165f..9cba8cc3d352a0b60317f570827015f4c11d5313 100644 --- a/substrate/primitives/core/src/ecdsa.rs +++ b/substrate/primitives/core/src/ecdsa.rs @@ -17,31 +17,19 @@ //! Simple ECDSA secp256k1 API. -use codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_runtime_interface::pass_by::PassByInner; - -#[cfg(feature = "serde")] -use crate::crypto::Ss58Codec; use crate::crypto::{ - ByteArray, CryptoType, CryptoTypeId, Derive, Public as TraitPublic, UncheckedFrom, + CryptoType, CryptoTypeId, DeriveError, DeriveJunction, Pair as TraitPair, PublicBytes, + SecretStringError, SignatureBytes, }; -#[cfg(feature = "full_crypto")] -use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError}; -#[cfg(all(feature = "full_crypto", not(feature = "std")))] -use secp256k1::Secp256k1; + +#[cfg(not(feature = "std"))] +use k256::ecdsa::{SigningKey as SecretKey, VerifyingKey}; #[cfg(feature = "std")] -use secp256k1::SECP256K1; -#[cfg(feature = "full_crypto")] use secp256k1::{ ecdsa::{RecoverableSignature, RecoveryId}, - Message, PublicKey, SecretKey, + Message, PublicKey, SecretKey, SECP256K1, }; -#[cfg(feature = "serde")] -use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; -#[cfg(all(not(feature = "std"), feature = "serde"))] -use sp_std::alloc::{format, string::String}; -#[cfg(feature = "full_crypto")] +#[cfg(not(feature = "std"))] use sp_std::vec::Vec; /// An identifier used to match public keys against ecdsa keys @@ -53,94 +41,51 @@ pub const PUBLIC_KEY_SERIALIZED_SIZE: usize = 33; /// The byte length of signature pub const SIGNATURE_SERIALIZED_SIZE: usize = 65; -/// A secret seed (which is bytewise essentially equivalent to a SecretKey). +#[doc(hidden)] +pub struct EcdsaTag; + +/// The secret seed. /// -/// We need it as a different type because `Seed` is expected to be AsRef<[u8]>. -#[cfg(feature = "full_crypto")] +/// The raw secret seed, which can be used to create the `Pair`. type Seed = [u8; 32]; /// The ECDSA compressed public key. -#[cfg_attr(feature = "full_crypto", derive(Hash))] -#[derive( - Clone, - Copy, - Encode, - Decode, - PassByInner, - MaxEncodedLen, - TypeInfo, - Eq, - PartialEq, - PartialOrd, - Ord, -)] -pub struct Public(pub [u8; PUBLIC_KEY_SERIALIZED_SIZE]); - -impl crate::crypto::FromEntropy for Public { - fn from_entropy(input: &mut impl codec::Input) -> Result { - let mut result = Self([0u8; PUBLIC_KEY_SERIALIZED_SIZE]); - input.read(&mut result.0[..])?; - Ok(result) - } -} +pub type Public = PublicBytes; impl Public { - /// A new instance from the given 33-byte `data`. - /// - /// NOTE: No checking goes on to ensure this is a real public key. Only use it if - /// you are certain that the array actually is a pubkey. GIGO! - pub fn from_raw(data: [u8; PUBLIC_KEY_SERIALIZED_SIZE]) -> Self { - Self(data) - } - /// Create a new instance from the given full public key. /// /// This will convert the full public key into the compressed format. - #[cfg(feature = "std")] pub fn from_full(full: &[u8]) -> Result { - let pubkey = if full.len() == 64 { + let mut tagged_full = [0u8; 65]; + let full = if full.len() == 64 { // Tag it as uncompressed public key. - let mut tagged_full = [0u8; 65]; tagged_full[0] = 0x04; tagged_full[1..].copy_from_slice(full); - secp256k1::PublicKey::from_slice(&tagged_full) + &tagged_full } else { - secp256k1::PublicKey::from_slice(full) + full }; - pubkey.map(|k| Self(k.serialize())).map_err(|_| ()) - } -} - -impl ByteArray for Public { - const LEN: usize = PUBLIC_KEY_SERIALIZED_SIZE; -} - -impl TraitPublic for Public {} - -impl Derive for Public {} - -impl AsRef<[u8]> for Public { - fn as_ref(&self) -> &[u8] { - &self.0[..] + #[cfg(feature = "std")] + let pubkey = PublicKey::from_slice(&full); + #[cfg(not(feature = "std"))] + let pubkey = VerifyingKey::from_sec1_bytes(&full); + pubkey.map(|k| k.into()).map_err(|_| ()) } } -impl AsMut<[u8]> for Public { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0[..] +#[cfg(feature = "std")] +impl From for Public { + fn from(pubkey: PublicKey) -> Self { + Self::from(pubkey.serialize()) } } -impl TryFrom<&[u8]> for Public { - type Error = (); - - fn try_from(data: &[u8]) -> Result { - if data.len() != Self::LEN { - return Err(()) - } - let mut r = [0u8; Self::LEN]; - r.copy_from_slice(data); - Ok(Self::unchecked_from(r)) +#[cfg(not(feature = "std"))] +impl From for Public { + fn from(pubkey: VerifyingKey) -> Self { + Self::try_from(&pubkey.to_sec1_bytes()[..]) + .expect("Valid key is serializable to [u8; 33]. qed.") } } @@ -151,203 +96,46 @@ impl From for Public { } } -impl UncheckedFrom<[u8; PUBLIC_KEY_SERIALIZED_SIZE]> for Public { - fn unchecked_from(x: [u8; PUBLIC_KEY_SERIALIZED_SIZE]) -> Self { - Public(x) - } -} - -#[cfg(feature = "std")] -impl std::fmt::Display for Public { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self.to_ss58check()) - } -} - -impl sp_std::fmt::Debug for Public { - #[cfg(feature = "std")] - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - let s = self.to_ss58check(); - write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.as_ref()), &s[0..8]) - } - - #[cfg(not(feature = "std"))] - fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - Ok(()) - } -} - -#[cfg(feature = "serde")] -impl Serialize for Public { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.to_ss58check()) - } -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for Public { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Public::from_ss58check(&String::deserialize(deserializer)?) - .map_err(|e| de::Error::custom(format!("{:?}", e))) - } -} - /// A signature (a 512-bit value, plus 8 bits for recovery ID). -#[cfg_attr(feature = "full_crypto", derive(Hash))] -#[derive(Encode, Decode, MaxEncodedLen, PassByInner, TypeInfo, PartialEq, Eq)] -pub struct Signature(pub [u8; SIGNATURE_SERIALIZED_SIZE]); - -impl ByteArray for Signature { - const LEN: usize = SIGNATURE_SERIALIZED_SIZE; -} - -impl TryFrom<&[u8]> for Signature { - type Error = (); - - fn try_from(data: &[u8]) -> Result { - if data.len() == SIGNATURE_SERIALIZED_SIZE { - let mut inner = [0u8; SIGNATURE_SERIALIZED_SIZE]; - inner.copy_from_slice(data); - Ok(Signature(inner)) - } else { - Err(()) - } - } -} - -#[cfg(feature = "serde")] -impl Serialize for Signature { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&array_bytes::bytes2hex("", self)) - } -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for Signature { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let signature_hex = array_bytes::hex2bytes(&String::deserialize(deserializer)?) - .map_err(|e| de::Error::custom(format!("{:?}", e)))?; - Signature::try_from(signature_hex.as_ref()) - .map_err(|e| de::Error::custom(format!("{:?}", e))) - } -} - -impl Clone for Signature { - fn clone(&self) -> Self { - let mut r = [0u8; SIGNATURE_SERIALIZED_SIZE]; - r.copy_from_slice(&self.0[..]); - Signature(r) - } -} - -impl Default for Signature { - fn default() -> Self { - Signature([0u8; SIGNATURE_SERIALIZED_SIZE]) - } -} - -impl From for [u8; SIGNATURE_SERIALIZED_SIZE] { - fn from(v: Signature) -> [u8; SIGNATURE_SERIALIZED_SIZE] { - v.0 - } -} - -impl AsRef<[u8; SIGNATURE_SERIALIZED_SIZE]> for Signature { - fn as_ref(&self) -> &[u8; SIGNATURE_SERIALIZED_SIZE] { - &self.0 - } -} - -impl AsRef<[u8]> for Signature { - fn as_ref(&self) -> &[u8] { - &self.0[..] - } -} - -impl AsMut<[u8]> for Signature { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0[..] - } -} - -impl sp_std::fmt::Debug for Signature { - #[cfg(feature = "std")] - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - write!(f, "{}", crate::hexdisplay::HexDisplay::from(&self.0)) - } - - #[cfg(not(feature = "std"))] - fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - Ok(()) - } -} - -impl UncheckedFrom<[u8; SIGNATURE_SERIALIZED_SIZE]> for Signature { - fn unchecked_from(data: [u8; SIGNATURE_SERIALIZED_SIZE]) -> Signature { - Signature(data) - } -} +pub type Signature = SignatureBytes; impl Signature { - /// A new instance from the given 65-byte `data`. - /// - /// NOTE: No checking goes on to ensure this is a real signature. Only use it if - /// you are certain that the array actually is a signature. GIGO! - pub fn from_raw(data: [u8; SIGNATURE_SERIALIZED_SIZE]) -> Signature { - Signature(data) - } - - /// A new instance from the given slice that should be 65 bytes long. - /// - /// NOTE: No checking goes on to ensure this is a real signature. Only use it if - /// you are certain that the array actually is a signature. GIGO! - pub fn from_slice(data: &[u8]) -> Option { - if data.len() != SIGNATURE_SERIALIZED_SIZE { - return None - } - let mut r = [0u8; SIGNATURE_SERIALIZED_SIZE]; - r.copy_from_slice(data); - Some(Signature(r)) - } - /// Recover the public key from this signature and a message. - #[cfg(feature = "full_crypto")] pub fn recover>(&self, message: M) -> Option { self.recover_prehashed(&sp_crypto_hashing::blake2_256(message.as_ref())) } /// Recover the public key from this signature and a pre-hashed message. - #[cfg(feature = "full_crypto")] pub fn recover_prehashed(&self, message: &[u8; 32]) -> Option { - let rid = RecoveryId::from_i32(self.0[64] as i32).ok()?; - let sig = RecoverableSignature::from_compact(&self.0[..64], rid).ok()?; - let message = Message::from_digest_slice(message).expect("Message is 32 bytes; qed"); - #[cfg(feature = "std")] - let context = SECP256K1; + { + let rid = RecoveryId::from_i32(self.0[64] as i32).ok()?; + let sig = RecoverableSignature::from_compact(&self.0[..64], rid).ok()?; + let message = + Message::from_digest_slice(message).expect("Message is a 32 bytes hash; qed"); + SECP256K1.recover_ecdsa(&message, &sig).ok().map(Public::from) + } + #[cfg(not(feature = "std"))] - let context = Secp256k1::verification_only(); + { + let rid = k256::ecdsa::RecoveryId::from_byte(self.0[64])?; + let sig = k256::ecdsa::Signature::from_bytes((&self.0[..64]).into()).ok()?; + VerifyingKey::recover_from_prehash(message, &sig, rid).map(Public::from).ok() + } + } +} - context - .recover_ecdsa(&message, &sig) - .ok() - .map(|pubkey| Public(pubkey.serialize())) +#[cfg(not(feature = "std"))] +impl From<(k256::ecdsa::Signature, k256::ecdsa::RecoveryId)> for Signature { + fn from(recsig: (k256::ecdsa::Signature, k256::ecdsa::RecoveryId)) -> Signature { + let mut r = Self::default(); + r.0[..64].copy_from_slice(&recsig.0.to_bytes()); + r.0[64] = recsig.1.to_byte(); + r } } -#[cfg(feature = "full_crypto")] +#[cfg(feature = "std")] impl From for Signature { fn from(recsig: RecoverableSignature) -> Signature { let mut r = Self::default(); @@ -360,20 +148,18 @@ impl From for Signature { } /// Derive a single hard junction. -#[cfg(feature = "full_crypto")] fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed { + use codec::Encode; ("Secp256k1HDKD", secret_seed, cc).using_encoded(sp_crypto_hashing::blake2_256) } /// A key pair. -#[cfg(feature = "full_crypto")] #[derive(Clone)] pub struct Pair { public: Public, secret: SecretKey, } -#[cfg(feature = "full_crypto")] impl TraitPair for Pair { type Public = Public; type Seed = Seed; @@ -384,17 +170,19 @@ impl TraitPair for Pair { /// /// You should never need to use this; generate(), generate_with_phrase fn from_seed_slice(seed_slice: &[u8]) -> Result { - let secret = - SecretKey::from_slice(seed_slice).map_err(|_| SecretStringError::InvalidSeedLength)?; - #[cfg(feature = "std")] - let context = SECP256K1; - #[cfg(not(feature = "std"))] - let context = Secp256k1::signing_only(); + { + let secret = SecretKey::from_slice(seed_slice) + .map_err(|_| SecretStringError::InvalidSeedLength)?; + Ok(Pair { public: PublicKey::from_secret_key(&SECP256K1, &secret).into(), secret }) + } - let public = PublicKey::from_secret_key(&context, &secret); - let public = Public(public.serialize()); - Ok(Pair { public, secret }) + #[cfg(not(feature = "std"))] + { + let secret = SecretKey::from_slice(seed_slice) + .map_err(|_| SecretStringError::InvalidSeedLength)?; + Ok(Pair { public: VerifyingKey::from(&secret).into(), secret }) + } } /// Derive a child key from a series of given junctions. @@ -419,6 +207,7 @@ impl TraitPair for Pair { } /// Sign a message. + #[cfg(feature = "full_crypto")] fn sign(&self, message: &[u8]) -> Signature { self.sign_prehashed(&sp_crypto_hashing::blake2_256(message)) } @@ -434,11 +223,17 @@ impl TraitPair for Pair { } } -#[cfg(feature = "full_crypto")] impl Pair { /// Get the seed for this key. pub fn seed(&self) -> Seed { - self.secret.secret_bytes() + #[cfg(feature = "std")] + { + self.secret.secret_bytes() + } + #[cfg(not(feature = "std"))] + { + self.secret.to_bytes().into() + } } /// Exactly as `from_string` except that if no matches are found then, the the first 32 @@ -454,15 +249,24 @@ impl Pair { } /// Sign a pre-hashed message + #[cfg(feature = "full_crypto")] pub fn sign_prehashed(&self, message: &[u8; 32]) -> Signature { - let message = Message::from_digest_slice(message).expect("Message is 32 bytes; qed"); - #[cfg(feature = "std")] - let context = SECP256K1; - #[cfg(not(feature = "std"))] - let context = Secp256k1::signing_only(); + { + let message = + Message::from_digest_slice(message).expect("Message is a 32 bytes hash; qed"); + SECP256K1.sign_ecdsa_recoverable(&message, &self.secret).into() + } - context.sign_ecdsa_recoverable(&message, &self.secret).into() + #[cfg(not(feature = "std"))] + { + // Signing fails only if the `message` number of bytes is less than the field length + // (unfallible as we're using a fixed message length of 32). + self.secret + .sign_prehash_recoverable(message) + .expect("Signing can't fail when using 32 bytes message hash. qed.") + .into() + } } /// Verify a signature on a pre-hashed message. Return `true` if the signature is valid @@ -503,7 +307,7 @@ impl Pair { // NOTE: this solution is not effective when `Pair` is moved around memory. // The very same problem affects other cryptographic backends that are just using // `zeroize`for their secrets. -#[cfg(feature = "full_crypto")] +#[cfg(feature = "std")] impl Drop for Pair { fn drop(&mut self) { self.secret.non_secure_erase() @@ -511,16 +315,13 @@ impl Drop for Pair { } impl CryptoType for Public { - #[cfg(feature = "full_crypto")] type Pair = Pair; } impl CryptoType for Signature { - #[cfg(feature = "full_crypto")] type Pair = Pair; } -#[cfg(feature = "full_crypto")] impl CryptoType for Pair { type Pair = Pair; } @@ -530,7 +331,7 @@ mod test { use super::*; use crate::crypto::{ set_default_ss58_version, PublicError, Ss58AddressFormat, Ss58AddressFormatRegistry, - DEV_PHRASE, + Ss58Codec, DEV_PHRASE, }; use serde_json; @@ -770,8 +571,18 @@ mod test { let msg = [0u8; 32]; let sig1 = pair.sign_prehashed(&msg); let sig2: Signature = { - let message = Message::from_digest_slice(&msg).unwrap(); - SECP256K1.sign_ecdsa_recoverable(&message, &pair.secret).into() + #[cfg(feature = "std")] + { + let message = Message::from_digest_slice(&msg).unwrap(); + SECP256K1.sign_ecdsa_recoverable(&message, &pair.secret).into() + } + #[cfg(not(feature = "std"))] + { + pair.secret + .sign_prehash_recoverable(&msg) + .expect("signing may not fail (???). qed.") + .into() + } }; assert_eq!(sig1, sig2); diff --git a/substrate/primitives/core/src/ed25519.rs b/substrate/primitives/core/src/ed25519.rs index 60ebd93e12d43d6d331c6758508f843145031e74..a9494f2860b4c018d465eecb9eafe825e169292c 100644 --- a/substrate/primitives/core/src/ed25519.rs +++ b/substrate/primitives/core/src/ed25519.rs @@ -15,367 +15,53 @@ // See the License for the specific language governing permissions and // limitations under the License. -// tag::description[] //! Simple Ed25519 API. -// end::description[] -#[cfg(feature = "full_crypto")] -use sp_std::vec::Vec; - -use crate::{ - crypto::ByteArray, - hash::{H256, H512}, -}; -use codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; - -#[cfg(feature = "serde")] -use crate::crypto::Ss58Codec; use crate::crypto::{ - CryptoType, CryptoTypeId, Derive, FromEntropy, Public as TraitPublic, UncheckedFrom, + ByteArray, CryptoType, CryptoTypeId, DeriveError, DeriveJunction, Pair as TraitPair, + PublicBytes, SecretStringError, SignatureBytes, }; -#[cfg(feature = "full_crypto")] -use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError}; -#[cfg(feature = "full_crypto")] -use core::convert::TryFrom; -#[cfg(feature = "full_crypto")] + use ed25519_zebra::{SigningKey, VerificationKey}; -#[cfg(feature = "serde")] -use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; -use sp_runtime_interface::pass_by::PassByInner; -#[cfg(all(not(feature = "std"), feature = "serde"))] -use sp_std::alloc::{format, string::String}; -use sp_std::ops::Deref; + +use sp_std::vec::Vec; /// An identifier used to match public keys against ed25519 keys pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"ed25"); +/// The byte length of public key +pub const PUBLIC_KEY_SERIALIZED_SIZE: usize = 32; + +/// The byte length of signature +pub const SIGNATURE_SERIALIZED_SIZE: usize = 64; + /// A secret seed. It's not called a "secret key" because ring doesn't expose the secret keys /// of the key pair (yeah, dumb); as such we're forced to remember the seed manually if we /// will need it later (such as for HDKD). -#[cfg(feature = "full_crypto")] type Seed = [u8; 32]; +#[doc(hidden)] +pub struct Ed25519Tag; + /// A public key. -#[cfg_attr(feature = "full_crypto", derive(Hash))] -#[derive( - PartialEq, - Eq, - PartialOrd, - Ord, - Clone, - Copy, - Encode, - Decode, - PassByInner, - MaxEncodedLen, - TypeInfo, -)] -pub struct Public(pub [u8; 32]); +pub type Public = PublicBytes; + +/// A signature. +pub type Signature = SignatureBytes; /// A key pair. -#[cfg(feature = "full_crypto")] #[derive(Copy, Clone)] pub struct Pair { public: VerificationKey, secret: SigningKey, } -impl FromEntropy for Public { - fn from_entropy(input: &mut impl codec::Input) -> Result { - let mut result = Self([0u8; 32]); - input.read(&mut result.0[..])?; - Ok(result) - } -} - -impl AsRef<[u8; 32]> for Public { - fn as_ref(&self) -> &[u8; 32] { - &self.0 - } -} - -impl AsRef<[u8]> for Public { - fn as_ref(&self) -> &[u8] { - &self.0[..] - } -} - -impl AsMut<[u8]> for Public { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0[..] - } -} - -impl Deref for Public { - type Target = [u8]; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl TryFrom<&[u8]> for Public { - type Error = (); - - fn try_from(data: &[u8]) -> Result { - if data.len() != Self::LEN { - return Err(()) - } - let mut r = [0u8; Self::LEN]; - r.copy_from_slice(data); - Ok(Self::unchecked_from(r)) - } -} - -impl From for [u8; 32] { - fn from(x: Public) -> Self { - x.0 - } -} - -#[cfg(feature = "full_crypto")] -impl From for Public { - fn from(x: Pair) -> Self { - x.public() - } -} - -impl From for H256 { - fn from(x: Public) -> Self { - x.0.into() - } -} - -#[cfg(feature = "std")] -impl std::str::FromStr for Public { - type Err = crate::crypto::PublicError; - - fn from_str(s: &str) -> Result { - Self::from_ss58check(s) - } -} - -impl UncheckedFrom<[u8; 32]> for Public { - fn unchecked_from(x: [u8; 32]) -> Self { - Public::from_raw(x) - } -} - -impl UncheckedFrom for Public { - fn unchecked_from(x: H256) -> Self { - Public::from_h256(x) - } -} - -#[cfg(feature = "std")] -impl std::fmt::Display for Public { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self.to_ss58check()) - } -} - -impl sp_std::fmt::Debug for Public { - #[cfg(feature = "std")] - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - let s = self.to_ss58check(); - write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.0), &s[0..8]) - } - - #[cfg(not(feature = "std"))] - fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - Ok(()) - } -} - -#[cfg(feature = "serde")] -impl Serialize for Public { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.to_ss58check()) - } -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for Public { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Public::from_ss58check(&String::deserialize(deserializer)?) - .map_err(|e| de::Error::custom(format!("{:?}", e))) - } -} - -/// A signature (a 512-bit value). -#[cfg_attr(feature = "full_crypto", derive(Hash))] -#[derive(Encode, Decode, MaxEncodedLen, PassByInner, TypeInfo, PartialEq, Eq)] -pub struct Signature(pub [u8; 64]); - -impl TryFrom<&[u8]> for Signature { - type Error = (); - - fn try_from(data: &[u8]) -> Result { - if data.len() == 64 { - let mut inner = [0u8; 64]; - inner.copy_from_slice(data); - Ok(Signature(inner)) - } else { - Err(()) - } - } -} - -#[cfg(feature = "serde")] -impl Serialize for Signature { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&array_bytes::bytes2hex("", self)) - } -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for Signature { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let signature_hex = array_bytes::hex2bytes(&String::deserialize(deserializer)?) - .map_err(|e| de::Error::custom(format!("{:?}", e)))?; - Signature::try_from(signature_hex.as_ref()) - .map_err(|e| de::Error::custom(format!("{:?}", e))) - } -} - -impl Clone for Signature { - fn clone(&self) -> Self { - let mut r = [0u8; 64]; - r.copy_from_slice(&self.0[..]); - Signature(r) - } -} - -impl From for H512 { - fn from(v: Signature) -> H512 { - H512::from(v.0) - } -} - -impl From for [u8; 64] { - fn from(v: Signature) -> [u8; 64] { - v.0 - } -} - -impl AsRef<[u8; 64]> for Signature { - fn as_ref(&self) -> &[u8; 64] { - &self.0 - } -} - -impl AsRef<[u8]> for Signature { - fn as_ref(&self) -> &[u8] { - &self.0[..] - } -} - -impl AsMut<[u8]> for Signature { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0[..] - } -} - -impl sp_std::fmt::Debug for Signature { - #[cfg(feature = "std")] - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - write!(f, "{}", crate::hexdisplay::HexDisplay::from(&self.0)) - } - - #[cfg(not(feature = "std"))] - fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - Ok(()) - } -} - -impl UncheckedFrom<[u8; 64]> for Signature { - fn unchecked_from(data: [u8; 64]) -> Signature { - Signature(data) - } -} - -impl Signature { - /// A new instance from the given 64-byte `data`. - /// - /// NOTE: No checking goes on to ensure this is a real signature. Only use it if - /// you are certain that the array actually is a signature. GIGO! - pub fn from_raw(data: [u8; 64]) -> Signature { - Signature(data) - } - - /// A new instance from the given slice that should be 64 bytes long. - /// - /// NOTE: No checking goes on to ensure this is a real signature. Only use it if - /// you are certain that the array actually is a signature. GIGO! - pub fn from_slice(data: &[u8]) -> Option { - if data.len() != 64 { - return None - } - let mut r = [0u8; 64]; - r.copy_from_slice(data); - Some(Signature(r)) - } - - /// A new instance from an H512. - /// - /// NOTE: No checking goes on to ensure this is a real signature. Only use it if - /// you are certain that the array actually is a signature. GIGO! - pub fn from_h512(v: H512) -> Signature { - Signature(v.into()) - } -} - -impl Public { - /// A new instance from the given 32-byte `data`. - /// - /// NOTE: No checking goes on to ensure this is a real public key. Only use it if - /// you are certain that the array actually is a pubkey. GIGO! - pub fn from_raw(data: [u8; 32]) -> Self { - Public(data) - } - - /// A new instance from an H256. - /// - /// NOTE: No checking goes on to ensure this is a real public key. Only use it if - /// you are certain that the array actually is a pubkey. GIGO! - pub fn from_h256(x: H256) -> Self { - Public(x.into()) - } - - /// Return a slice filled with raw data. - pub fn as_array_ref(&self) -> &[u8; 32] { - self.as_ref() - } -} - -impl ByteArray for Public { - const LEN: usize = 32; -} - -impl TraitPublic for Public {} - -impl Derive for Public {} - /// Derive a single hard junction. -#[cfg(feature = "full_crypto")] fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed { + use codec::Encode; ("Ed25519HDKD", secret_seed, cc).using_encoded(sp_crypto_hashing::blake2_256) } -#[cfg(feature = "full_crypto")] impl TraitPair for Pair { type Public = Public; type Seed = Seed; @@ -410,10 +96,11 @@ impl TraitPair for Pair { /// Get the public key. fn public(&self) -> Public { - Public(self.public.into()) + Public::from_raw(self.public.into()) } /// Sign a message. + #[cfg(feature = "full_crypto")] fn sign(&self, message: &[u8]) -> Signature { Signature::from_raw(self.secret.sign(message).into()) } @@ -433,7 +120,6 @@ impl TraitPair for Pair { } } -#[cfg(feature = "full_crypto")] impl Pair { /// Get the seed for this key. pub fn seed(&self) -> Seed { @@ -454,23 +140,22 @@ impl Pair { } impl CryptoType for Public { - #[cfg(feature = "full_crypto")] type Pair = Pair; } impl CryptoType for Signature { - #[cfg(feature = "full_crypto")] type Pair = Pair; } -#[cfg(feature = "full_crypto")] impl CryptoType for Pair { type Pair = Pair; } #[cfg(test)] -mod test { +mod tests { use super::*; + #[cfg(feature = "serde")] + use crate::crypto::Ss58Codec; use crate::crypto::DEV_PHRASE; use serde_json; diff --git a/substrate/primitives/core/src/lib.rs b/substrate/primitives/core/src/lib.rs index 0d43eea99629915715f452da409d53522d199e53..098bd135bfebba0d83bb2f58e72b4779bdf441b7 100644 --- a/substrate/primitives/core/src/lib.rs +++ b/substrate/primitives/core/src/lib.rs @@ -46,7 +46,6 @@ pub use sp_debug_derive::RuntimeDebug; #[cfg(feature = "serde")] pub use impl_serde::serialize as bytes; -#[cfg(feature = "full_crypto")] #[deprecated( since = "27.0.0", note = "`sp-crypto-hashing` re-exports will be removed after June 2024. Use `sp-crypto-hashing` instead." @@ -57,27 +56,27 @@ pub mod const_hex2array; pub mod crypto; pub mod hexdisplay; pub use paste; - -#[cfg(any(feature = "full_crypto", feature = "std"))] mod address_uri; -#[cfg(feature = "bandersnatch-experimental")] -pub mod bandersnatch; -#[cfg(feature = "bls-experimental")] -pub mod bls; pub mod defer; -pub mod ecdsa; -pub mod ed25519; pub mod hash; #[cfg(feature = "std")] mod hasher; pub mod offchain; -pub mod paired_crypto; -pub mod sr25519; pub mod testing; #[cfg(feature = "std")] pub mod traits; pub mod uint; +#[cfg(feature = "bandersnatch-experimental")] +pub mod bandersnatch; +#[cfg(feature = "bls-experimental")] +pub mod bls; +pub mod crypto_bytes; +pub mod ecdsa; +pub mod ed25519; +pub mod paired_crypto; +pub mod sr25519; + #[cfg(feature = "bls-experimental")] pub use bls::{bls377, bls381}; #[cfg(feature = "bls-experimental")] @@ -87,7 +86,6 @@ pub use self::{ hash::{convert_hash, H160, H256, H512}, uint::{U256, U512}, }; -#[cfg(feature = "full_crypto")] pub use crypto::{ByteArray, DeriveJunction, Pair, Public}; #[cfg(feature = "std")] diff --git a/substrate/primitives/core/src/paired_crypto.rs b/substrate/primitives/core/src/paired_crypto.rs index 20b32c339bd763d4847f4d36626635a4226f80d9..260e86b6ff9c4d759fd3794c6a91cc77aebd8dc0 100644 --- a/substrate/primitives/core/src/paired_crypto.rs +++ b/substrate/primitives/core/src/paired_crypto.rs @@ -17,34 +17,23 @@ //! API for using a pair of crypto schemes together. -#[cfg(feature = "serde")] -use crate::crypto::Ss58Codec; -use crate::crypto::{ByteArray, CryptoType, Derive, Public as PublicT, UncheckedFrom}; -#[cfg(feature = "full_crypto")] -use crate::crypto::{DeriveError, DeriveJunction, Pair as PairT, SecretStringError}; +use core::marker::PhantomData; -#[cfg(feature = "full_crypto")] -use sp_std::vec::Vec; - -use codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -#[cfg(feature = "serde")] -use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; -#[cfg(all(not(feature = "std"), feature = "serde"))] -use sp_std::alloc::{format, string::String}; +use crate::crypto::{ + ByteArray, CryptoType, DeriveError, DeriveJunction, Pair as PairT, Public as PublicT, + PublicBytes, SecretStringError, Signature as SignatureT, SignatureBytes, UncheckedFrom, +}; -use sp_runtime_interface::pass_by::{self, PassBy, PassByInner}; -use sp_std::convert::TryFrom; +use sp_std::vec::Vec; /// ECDSA and BLS12-377 paired crypto scheme #[cfg(feature = "bls-experimental")] pub mod ecdsa_bls377 { + use crate::{bls377, crypto::CryptoTypeId, ecdsa}; #[cfg(feature = "full_crypto")] - use crate::Hasher; use crate::{ - bls377, - crypto::{CryptoTypeId, Pair as PairT, UncheckedFrom}, - ecdsa, + crypto::{Pair as PairT, UncheckedFrom}, + Hasher, }; /// An identifier used to match public keys against BLS12-377 keys @@ -55,32 +44,36 @@ pub mod ecdsa_bls377 { const SIGNATURE_LEN: usize = ecdsa::SIGNATURE_SERIALIZED_SIZE + bls377::SIGNATURE_SERIALIZED_SIZE; + #[doc(hidden)] + pub struct EcdsaBls377Tag(ecdsa::EcdsaTag, bls377::Bls377Tag); + + impl super::PairedCryptoSubTagBound for EcdsaBls377Tag {} + /// (ECDSA,BLS12-377) key-pair pair. - #[cfg(feature = "full_crypto")] - pub type Pair = super::Pair; + pub type Pair = + super::Pair; + /// (ECDSA,BLS12-377) public key pair. - pub type Public = super::Public; + pub type Public = super::Public; + /// (ECDSA,BLS12-377) signature pair. - pub type Signature = super::Signature; + pub type Signature = super::Signature; impl super::CryptoType for Public { - #[cfg(feature = "full_crypto")] type Pair = Pair; } impl super::CryptoType for Signature { - #[cfg(feature = "full_crypto")] type Pair = Pair; } - #[cfg(feature = "full_crypto")] impl super::CryptoType for Pair { type Pair = Pair; } #[cfg(feature = "full_crypto")] impl Pair { - /// Hashes the `message` with the specified [`Hasher`] before signing sith the ECDSA secret + /// Hashes the `message` with the specified [`Hasher`] before signing with the ECDSA secret /// component. /// /// The hasher does not affect the BLS12-377 component. This generates BLS12-377 Signature @@ -115,7 +108,7 @@ pub mod ecdsa_bls377 { let Ok(left_pub) = public.0[..ecdsa::PUBLIC_KEY_SERIALIZED_SIZE].try_into() else { return false }; - let Ok(left_sig) = sig.0[0..ecdsa::SIGNATURE_SERIALIZED_SIZE].try_into() else { + let Ok(left_sig) = sig.0[..ecdsa::SIGNATURE_SERIALIZED_SIZE].try_into() else { return false }; if !ecdsa::Pair::verify_prehashed(&left_sig, &msg_hash, &left_pub) { @@ -136,7 +129,6 @@ pub mod ecdsa_bls377 { /// Secure seed length. /// /// Currently only supporting sub-schemes whose seed is a 32-bytes array. -#[cfg(feature = "full_crypto")] const SECURE_SEED_LEN: usize = 32; /// A secret seed. @@ -144,303 +136,83 @@ const SECURE_SEED_LEN: usize = 32; /// It's not called a "secret key" because ring doesn't expose the secret keys /// of the key pair (yeah, dumb); as such we're forced to remember the seed manually if we /// will need it later (such as for HDKD). -#[cfg(feature = "full_crypto")] type Seed = [u8; SECURE_SEED_LEN]; -/// A public key. -#[derive(Clone, Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Eq, PartialOrd, Ord)] -pub struct Public([u8; LEFT_PLUS_RIGHT_LEN]); - -#[cfg(feature = "full_crypto")] -impl sp_std::hash::Hash for Public { - fn hash(&self, state: &mut H) { - self.0.hash(state); - } -} - -impl ByteArray for Public { - const LEN: usize = LEFT_PLUS_RIGHT_LEN; -} - -impl TryFrom<&[u8]> for Public { - type Error = (); - - fn try_from(data: &[u8]) -> Result { - if data.len() != LEFT_PLUS_RIGHT_LEN { - return Err(()) - } - let mut inner = [0u8; LEFT_PLUS_RIGHT_LEN]; - inner.copy_from_slice(data); - Ok(Public(inner)) - } -} - -impl AsRef<[u8; LEFT_PLUS_RIGHT_LEN]> - for Public -{ - fn as_ref(&self) -> &[u8; LEFT_PLUS_RIGHT_LEN] { - &self.0 - } -} - -impl AsRef<[u8]> for Public { - fn as_ref(&self) -> &[u8] { - &self.0[..] - } -} - -impl AsMut<[u8]> for Public { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0[..] - } -} - -impl PassByInner for Public { - type Inner = [u8; LEFT_PLUS_RIGHT_LEN]; +#[doc(hidden)] +pub trait PairedCryptoSubTagBound {} +#[doc(hidden)] +pub struct PairedCryptoTag; - fn into_inner(self) -> Self::Inner { - self.0 - } - - fn inner(&self) -> &Self::Inner { - &self.0 - } - - fn from_inner(inner: Self::Inner) -> Self { - Self(inner) - } -} - -impl PassBy for Public { - type PassBy = pass_by::Inner; -} +/// A public key. +pub type Public = + PublicBytes; -#[cfg(feature = "full_crypto")] impl< LeftPair: PairT, RightPair: PairT, const LEFT_PLUS_RIGHT_PUBLIC_LEN: usize, const SIGNATURE_LEN: usize, - > From> - for Public + SubTag: PairedCryptoSubTagBound, + > From> + for Public where - Pair: - PairT>, + Pair: + PairT>, { - fn from(x: Pair) -> Self { + fn from( + x: Pair, + ) -> Self { x.public() } } -impl UncheckedFrom<[u8; LEFT_PLUS_RIGHT_LEN]> - for Public -{ - fn unchecked_from(data: [u8; LEFT_PLUS_RIGHT_LEN]) -> Self { - Public(data) - } -} - -#[cfg(feature = "std")] -impl std::fmt::Display for Public -where - Public: CryptoType, -{ - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self.to_ss58check()) - } -} - -impl sp_std::fmt::Debug for Public -where - Public: CryptoType, - [u8; LEFT_PLUS_RIGHT_LEN]: crate::hexdisplay::AsBytesRef, -{ - #[cfg(feature = "std")] - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - let s = self.to_ss58check(); - write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.0), &s[0..8]) - } - - #[cfg(not(feature = "std"))] - fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - Ok(()) - } -} - -#[cfg(feature = "serde")] -impl Serialize for Public -where - Public: CryptoType, -{ - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.to_ss58check()) - } -} - -#[cfg(feature = "serde")] -impl<'de, const LEFT_PLUS_RIGHT_LEN: usize> Deserialize<'de> for Public -where - Public: CryptoType, -{ - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Public::from_ss58check(&String::deserialize(deserializer)?) - .map_err(|e| de::Error::custom(format!("{:?}", e))) - } -} - -impl PublicT for Public where - Public: CryptoType -{ -} - -impl Derive for Public {} - -/// Trait characterizing a signature which could be used as individual component of an -/// `paired_crypto:Signature` pair. -pub trait SignatureBound: ByteArray {} - -impl SignatureBound for T {} - /// A pair of signatures of different types -#[derive(Clone, Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Eq)] -pub struct Signature([u8; LEFT_PLUS_RIGHT_LEN]); - -#[cfg(feature = "full_crypto")] -impl sp_std::hash::Hash for Signature { - fn hash(&self, state: &mut H) { - self.0.hash(state); - } -} - -impl ByteArray for Signature { - const LEN: usize = LEFT_PLUS_RIGHT_LEN; -} - -impl TryFrom<&[u8]> for Signature { - type Error = (); - - fn try_from(data: &[u8]) -> Result { - if data.len() != LEFT_PLUS_RIGHT_LEN { - return Err(()) - } - let mut inner = [0u8; LEFT_PLUS_RIGHT_LEN]; - inner.copy_from_slice(data); - Ok(Signature(inner)) - } -} - -impl AsMut<[u8]> for Signature { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0[..] - } -} - -impl AsRef<[u8; LEFT_PLUS_RIGHT_LEN]> - for Signature -{ - fn as_ref(&self) -> &[u8; LEFT_PLUS_RIGHT_LEN] { - &self.0 - } -} - -impl AsRef<[u8]> for Signature { - fn as_ref(&self) -> &[u8] { - &self.0[..] - } -} - -#[cfg(feature = "serde")] -impl Serialize for Signature { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&array_bytes::bytes2hex("", self)) - } -} - -#[cfg(feature = "serde")] -impl<'de, const LEFT_PLUS_RIGHT_LEN: usize> Deserialize<'de> for Signature { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let bytes = array_bytes::hex2bytes(&String::deserialize(deserializer)?) - .map_err(|e| de::Error::custom(format!("{:?}", e)))?; - Signature::::try_from(bytes.as_ref()).map_err(|e| { - de::Error::custom(format!("Error converting deserialized data into signature: {:?}", e)) - }) - } -} - -impl From> - for [u8; LEFT_PLUS_RIGHT_LEN] -{ - fn from(signature: Signature) -> [u8; LEFT_PLUS_RIGHT_LEN] { - signature.0 - } -} - -impl sp_std::fmt::Debug for Signature -where - [u8; LEFT_PLUS_RIGHT_LEN]: crate::hexdisplay::AsBytesRef, -{ - #[cfg(feature = "std")] - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - write!(f, "{}", crate::hexdisplay::HexDisplay::from(&self.0)) - } - - #[cfg(not(feature = "std"))] - fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - Ok(()) - } -} - -impl UncheckedFrom<[u8; LEFT_PLUS_RIGHT_LEN]> - for Signature -{ - fn unchecked_from(data: [u8; LEFT_PLUS_RIGHT_LEN]) -> Self { - Signature(data) - } -} +pub type Signature = + SignatureBytes; /// A key pair. -#[cfg(feature = "full_crypto")] -#[derive(Clone)] pub struct Pair< LeftPair: PairT, RightPair: PairT, const PUBLIC_KEY_LEN: usize, const SIGNATURE_LEN: usize, + SubTag, > { left: LeftPair, right: RightPair, + _phantom: PhantomData SubTag>, +} + +impl< + LeftPair: PairT + Clone, + RightPair: PairT + Clone, + const PUBLIC_KEY_LEN: usize, + const SIGNATURE_LEN: usize, + SubTag, + > Clone for Pair +{ + fn clone(&self) -> Self { + Self { left: self.left.clone(), right: self.right.clone(), _phantom: PhantomData } + } } -#[cfg(feature = "full_crypto")] impl< LeftPair: PairT, RightPair: PairT, const PUBLIC_KEY_LEN: usize, const SIGNATURE_LEN: usize, - > PairT for Pair + SubTag: PairedCryptoSubTagBound, + > PairT for Pair where - Pair: CryptoType, - LeftPair::Signature: SignatureBound, - RightPair::Signature: SignatureBound, - Public: CryptoType, + Pair: CryptoType, + Public: PublicT, + Signature: SignatureT, LeftPair::Seed: From + Into, RightPair::Seed: From + Into, { type Seed = Seed; - type Public = Public; - type Signature = Signature; + type Public = Public; + type Signature = Signature; fn from_seed_slice(seed_slice: &[u8]) -> Result { if seed_slice.len() != SECURE_SEED_LEN { @@ -448,7 +220,7 @@ where } let left = LeftPair::from_seed_slice(&seed_slice)?; let right = RightPair::from_seed_slice(&seed_slice)?; - Ok(Pair { left, right }) + Ok(Pair { left, right, _phantom: PhantomData }) } /// Derive a child key from a series of given junctions. @@ -471,7 +243,7 @@ where _ => None, }; - Ok((Self { left: left.0, right: right.0 }, seed)) + Ok((Self { left: left.0, right: right.0, _phantom: PhantomData }, seed)) } fn public(&self) -> Self::Public { @@ -483,6 +255,7 @@ where Self::Public::unchecked_from(raw) } + #[cfg(feature = "full_crypto")] fn sign(&self, message: &[u8]) -> Self::Signature { let mut raw: [u8; SIGNATURE_LEN] = [0u8; SIGNATURE_LEN]; raw[..LeftPair::Signature::LEN].copy_from_slice(self.left.sign(message).as_ref()); @@ -490,16 +263,18 @@ where Self::Signature::unchecked_from(raw) } - fn verify>(sig: &Self::Signature, message: M, public: &Self::Public) -> bool { + fn verify>( + sig: &Self::Signature, + message: Msg, + public: &Self::Public, + ) -> bool { let Ok(left_pub) = public.0[..LeftPair::Public::LEN].try_into() else { return false }; let Ok(left_sig) = sig.0[0..LeftPair::Signature::LEN].try_into() else { return false }; if !LeftPair::verify(&left_sig, message.as_ref(), &left_pub) { return false } - let Ok(right_pub) = public.0[LeftPair::Public::LEN..PUBLIC_KEY_LEN].try_into() else { - return false - }; + let Ok(right_pub) = public.0[LeftPair::Public::LEN..].try_into() else { return false }; let Ok(right_sig) = sig.0[LeftPair::Signature::LEN..].try_into() else { return false }; RightPair::verify(&right_sig, message.as_ref(), &right_pub) } @@ -514,13 +289,14 @@ where // Test set exercising the (ECDSA,BLS12-377) implementation #[cfg(all(test, feature = "bls-experimental"))] -mod test { +mod tests { use super::*; - use crate::{crypto::DEV_PHRASE, KeccakHasher}; + #[cfg(feature = "serde")] + use crate::crypto::Ss58Codec; + use crate::{bls377, crypto::DEV_PHRASE, ecdsa, KeccakHasher}; + use codec::{Decode, Encode}; use ecdsa_bls377::{Pair, Signature}; - use crate::{bls377, ecdsa}; - #[test] fn test_length_of_paired_ecdsa_and_bls377_public_key_and_signature_is_correct() { assert_eq!( diff --git a/substrate/primitives/core/src/sr25519.rs b/substrate/primitives/core/src/sr25519.rs index 7c02afc3cd5ff8fe378613d88c088c95ceb805c8..54b9a98db3d2ca2b69af5ab77701e4e8c0b70a83 100644 --- a/substrate/primitives/core/src/sr25519.rs +++ b/substrate/primitives/core/src/sr25519.rs @@ -19,124 +19,68 @@ //! //! Note: `CHAIN_CODE_LENGTH` must be equal to `crate::crypto::JUNCTION_ID_LEN` //! for this to work. -#[cfg(any(feature = "full_crypto", feature = "serde"))] -use crate::crypto::DeriveJunction; + #[cfg(feature = "serde")] use crate::crypto::Ss58Codec; -#[cfg(feature = "full_crypto")] -use crate::crypto::{DeriveError, Pair as TraitPair, SecretStringError}; -#[cfg(feature = "full_crypto")] -use schnorrkel::{ - derive::CHAIN_CODE_LENGTH, signing_context, ExpansionMode, Keypair, MiniSecretKey, SecretKey, +use crate::crypto::{ + CryptoBytes, DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError, }; -#[cfg(any(feature = "full_crypto", feature = "serde"))] +#[cfg(feature = "full_crypto")] +use schnorrkel::signing_context; use schnorrkel::{ - derive::{ChainCode, Derivation}, - PublicKey, + derive::{ChainCode, Derivation, CHAIN_CODE_LENGTH}, + ExpansionMode, Keypair, MiniSecretKey, PublicKey, SecretKey, }; use sp_std::vec::Vec; -use crate::{ - crypto::{ - ByteArray, CryptoType, CryptoTypeId, Derive, FromEntropy, Public as TraitPublic, - UncheckedFrom, - }, - hash::{H256, H512}, -}; +use crate::crypto::{CryptoType, CryptoTypeId, Derive, Public as TraitPublic, SignatureBytes}; use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; -use sp_std::ops::Deref; -#[cfg(feature = "full_crypto")] use schnorrkel::keys::{MINI_SECRET_KEY_LENGTH, SECRET_KEY_LENGTH}; #[cfg(feature = "serde")] use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; +#[cfg(feature = "std")] use sp_runtime_interface::pass_by::PassByInner; #[cfg(all(not(feature = "std"), feature = "serde"))] use sp_std::alloc::{format, string::String}; // signing context -#[cfg(feature = "full_crypto")] const SIGNING_CTX: &[u8] = b"substrate"; /// An identifier used to match public keys against sr25519 keys pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"sr25"); -/// An Schnorrkel/Ristretto x25519 ("sr25519") public key. -#[cfg_attr(feature = "full_crypto", derive(Hash))] -#[derive( - PartialEq, - Eq, - PartialOrd, - Ord, - Clone, - Copy, - Encode, - Decode, - PassByInner, - MaxEncodedLen, - TypeInfo, -)] -pub struct Public(pub [u8; 32]); - -/// An Schnorrkel/Ristretto x25519 ("sr25519") key pair. -#[cfg(feature = "full_crypto")] -pub struct Pair(Keypair); - -#[cfg(feature = "full_crypto")] -impl Clone for Pair { - fn clone(&self) -> Self { - Pair(schnorrkel::Keypair { - public: self.0.public, - secret: schnorrkel::SecretKey::from_bytes(&self.0.secret.to_bytes()[..]) - .expect("key is always the correct size; qed"), - }) - } -} - -impl FromEntropy for Public { - fn from_entropy(input: &mut impl codec::Input) -> Result { - let mut result = Self([0u8; 32]); - input.read(&mut result.0[..])?; - Ok(result) - } -} - -impl AsRef<[u8; 32]> for Public { - fn as_ref(&self) -> &[u8; 32] { - &self.0 - } -} - -impl AsRef<[u8]> for Public { - fn as_ref(&self) -> &[u8] { - &self.0[..] - } -} +/// The byte length of public key +pub const PUBLIC_KEY_SERIALIZED_SIZE: usize = 32; -impl AsMut<[u8]> for Public { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0[..] - } -} +/// The byte length of signature +pub const SIGNATURE_SERIALIZED_SIZE: usize = 64; -impl Deref for Public { - type Target = [u8]; +#[doc(hidden)] +pub struct Sr25519Tag; +#[doc(hidden)] +pub struct Sr25519PublicTag; - fn deref(&self) -> &Self::Target { - &self.0 - } -} +/// An Schnorrkel/Ristretto x25519 ("sr25519") public key. +pub type Public = CryptoBytes; -impl From for [u8; 32] { - fn from(x: Public) -> [u8; 32] { - x.0 - } -} +impl TraitPublic for Public {} -impl From for H256 { - fn from(x: Public) -> H256 { - x.0.into() +impl Derive for Public { + /// Derive a child key from a series of given junctions. + /// + /// `None` if there are any hard junctions in there. + #[cfg(feature = "serde")] + fn derive>(&self, path: Iter) -> Option { + let mut acc = PublicKey::from_bytes(self.as_ref()).ok()?; + for j in path { + match j { + DeriveJunction::Soft(cc) => acc = acc.derived_key_simple(ChainCode(cc), &[]).0, + DeriveJunction::Hard(_cc) => return None, + } + } + Some(Self::from(acc.to_bytes())) } } @@ -149,31 +93,6 @@ impl std::str::FromStr for Public { } } -impl TryFrom<&[u8]> for Public { - type Error = (); - - fn try_from(data: &[u8]) -> Result { - if data.len() != Self::LEN { - return Err(()) - } - let mut r = [0u8; 32]; - r.copy_from_slice(data); - Ok(Self::unchecked_from(r)) - } -} - -impl UncheckedFrom<[u8; 32]> for Public { - fn unchecked_from(x: [u8; 32]) -> Self { - Public::from_raw(x) - } -} - -impl UncheckedFrom for Public { - fn unchecked_from(x: H256) -> Self { - Public::from_h256(x) - } -} - #[cfg(feature = "std")] impl std::fmt::Display for Public { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -185,7 +104,7 @@ impl sp_std::fmt::Debug for Public { #[cfg(feature = "std")] fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { let s = self.to_ss58check(); - write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.0), &s[0..8]) + write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(self.inner()), &s[0..8]) } #[cfg(not(feature = "std"))] @@ -216,189 +135,28 @@ impl<'de> Deserialize<'de> for Public { } /// An Schnorrkel/Ristretto x25519 ("sr25519") signature. -#[cfg_attr(feature = "full_crypto", derive(Hash))] -#[derive(Encode, Decode, MaxEncodedLen, PassByInner, TypeInfo, PartialEq, Eq)] -pub struct Signature(pub [u8; 64]); - -impl TryFrom<&[u8]> for Signature { - type Error = (); - - fn try_from(data: &[u8]) -> Result { - if data.len() == 64 { - let mut inner = [0u8; 64]; - inner.copy_from_slice(data); - Ok(Signature(inner)) - } else { - Err(()) - } - } -} - -#[cfg(feature = "serde")] -impl Serialize for Signature { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&array_bytes::bytes2hex("", self)) - } -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for Signature { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let signature_hex = array_bytes::hex2bytes(&String::deserialize(deserializer)?) - .map_err(|e| de::Error::custom(format!("{:?}", e)))?; - Signature::try_from(signature_hex.as_ref()) - .map_err(|e| de::Error::custom(format!("{:?}", e))) - } -} - -impl Clone for Signature { - fn clone(&self) -> Self { - let mut r = [0u8; 64]; - r.copy_from_slice(&self.0[..]); - Signature(r) - } -} - -impl From for [u8; 64] { - fn from(v: Signature) -> [u8; 64] { - v.0 - } -} - -impl From for H512 { - fn from(v: Signature) -> H512 { - H512::from(v.0) - } -} - -impl AsRef<[u8; 64]> for Signature { - fn as_ref(&self) -> &[u8; 64] { - &self.0 - } -} - -impl AsRef<[u8]> for Signature { - fn as_ref(&self) -> &[u8] { - &self.0[..] - } -} - -impl AsMut<[u8]> for Signature { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0[..] - } -} +pub type Signature = SignatureBytes; #[cfg(feature = "full_crypto")] impl From for Signature { fn from(s: schnorrkel::Signature) -> Signature { - Signature(s.to_bytes()) - } -} - -impl sp_std::fmt::Debug for Signature { - #[cfg(feature = "std")] - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - write!(f, "{}", crate::hexdisplay::HexDisplay::from(&self.0)) - } - - #[cfg(not(feature = "std"))] - fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - Ok(()) - } -} - -impl UncheckedFrom<[u8; 64]> for Signature { - fn unchecked_from(data: [u8; 64]) -> Signature { - Signature(data) + Signature::from(s.to_bytes()) } } -impl Signature { - /// A new instance from the given 64-byte `data`. - /// - /// NOTE: No checking goes on to ensure this is a real signature. Only use - /// it if you are certain that the array actually is a signature, or if you - /// immediately verify the signature. All functions that verify signatures - /// will fail if the `Signature` is not actually a valid signature. - pub fn from_raw(data: [u8; 64]) -> Signature { - Signature(data) - } - - /// A new instance from the given slice that should be 64 bytes long. - /// - /// NOTE: No checking goes on to ensure this is a real signature. Only use it if - /// you are certain that the array actually is a signature. GIGO! - pub fn from_slice(data: &[u8]) -> Option { - if data.len() != 64 { - return None - } - let mut r = [0u8; 64]; - r.copy_from_slice(data); - Some(Signature(r)) - } - - /// A new instance from an H512. - /// - /// NOTE: No checking goes on to ensure this is a real signature. Only use it if - /// you are certain that the array actually is a signature. GIGO! - pub fn from_h512(v: H512) -> Signature { - Signature(v.into()) - } -} - -impl Derive for Public { - /// Derive a child key from a series of given junctions. - /// - /// `None` if there are any hard junctions in there. - #[cfg(feature = "serde")] - fn derive>(&self, path: Iter) -> Option { - let mut acc = PublicKey::from_bytes(self.as_ref()).ok()?; - for j in path { - match j { - DeriveJunction::Soft(cc) => acc = acc.derived_key_simple(ChainCode(cc), &[]).0, - DeriveJunction::Hard(_cc) => return None, - } - } - Some(Self(acc.to_bytes())) - } -} - -impl Public { - /// A new instance from the given 32-byte `data`. - /// - /// NOTE: No checking goes on to ensure this is a real public key. Only use it if - /// you are certain that the array actually is a pubkey. GIGO! - pub fn from_raw(data: [u8; 32]) -> Self { - Public(data) - } - - /// A new instance from an H256. - /// - /// NOTE: No checking goes on to ensure this is a real public key. Only use it if - /// you are certain that the array actually is a pubkey. GIGO! - pub fn from_h256(x: H256) -> Self { - Public(x.into()) - } +/// An Schnorrkel/Ristretto x25519 ("sr25519") key pair. +pub struct Pair(Keypair); - /// Return a slice filled with raw data. - pub fn as_array_ref(&self) -> &[u8; 32] { - self.as_ref() +impl Clone for Pair { + fn clone(&self) -> Self { + Pair(schnorrkel::Keypair { + public: self.0.public, + secret: schnorrkel::SecretKey::from_bytes(&self.0.secret.to_bytes()[..]) + .expect("key is always the correct size; qed"), + }) } } -impl ByteArray for Public { - const LEN: usize = 32; -} - -impl TraitPublic for Public {} - #[cfg(feature = "std")] impl From for Pair { fn from(sec: MiniSecretKey) -> Pair { @@ -435,16 +193,13 @@ impl AsRef for Pair { } /// Derive a single hard junction. -#[cfg(feature = "full_crypto")] fn derive_hard_junction(secret: &SecretKey, cc: &[u8; CHAIN_CODE_LENGTH]) -> MiniSecretKey { secret.hard_derive_mini_secret_key(Some(ChainCode(*cc)), b"").0 } /// The raw secret seed, which can be used to recreate the `Pair`. -#[cfg(feature = "full_crypto")] type Seed = [u8; MINI_SECRET_KEY_LENGTH]; -#[cfg(feature = "full_crypto")] impl TraitPair for Pair { type Public = Public; type Seed = Seed; @@ -452,9 +207,7 @@ impl TraitPair for Pair { /// Get the public key. fn public(&self) -> Public { - let mut pk = [0u8; 32]; - pk.copy_from_slice(&self.0.public.to_bytes()); - Public(pk) + Public::from(self.0.public.to_bytes()) } /// Make a new key pair from raw secret seed material. @@ -499,6 +252,7 @@ impl TraitPair for Pair { Ok((Self(result.into()), seed.map(|s| MiniSecretKey::to_bytes(&s)))) } + #[cfg(feature = "full_crypto")] fn sign(&self, message: &[u8]) -> Signature { let context = signing_context(SIGNING_CTX); self.0.sign(context.bytes(message)).into() @@ -533,16 +287,13 @@ impl Pair { } impl CryptoType for Public { - #[cfg(feature = "full_crypto")] type Pair = Pair; } impl CryptoType for Signature { - #[cfg(feature = "full_crypto")] type Pair = Pair; } -#[cfg(feature = "full_crypto")] impl CryptoType for Pair { type Pair = Pair; } @@ -736,7 +487,7 @@ pub mod vrf { impl VrfPublic for Public { fn vrf_verify(&self, data: &Self::VrfSignData, signature: &Self::VrfSignature) -> bool { let do_verify = || { - let public = schnorrkel::PublicKey::from_bytes(self)?; + let public = schnorrkel::PublicKey::from_bytes(&self.0)?; let inout = signature.pre_output.0.attach_input_hash(&public, data.transcript.0.clone())?; @@ -836,7 +587,10 @@ pub mod vrf { #[cfg(test)] mod tests { use super::{vrf::*, *}; - use crate::crypto::{Ss58Codec, VrfPublic, VrfSecret, DEV_ADDRESS, DEV_PHRASE}; + use crate::{ + crypto::{Ss58Codec, VrfPublic, VrfSecret, DEV_ADDRESS, DEV_PHRASE}, + ByteArray as _, + }; use serde_json; #[test] @@ -1000,10 +754,10 @@ mod tests { let (pair, _) = Pair::generate(); let public = pair.public(); let message = b"Signed payload"; - let Signature(mut bytes) = pair.sign(&message[..]); + let mut signature = pair.sign(&message[..]); + let bytes = &mut signature.0; bytes[0] = !bytes[0]; bytes[2] = !bytes[2]; - let signature = Signature(bytes); assert!(!Pair::verify(&signature, &message[..], &public)); } diff --git a/substrate/primitives/crypto/ec-utils/Cargo.toml b/substrate/primitives/crypto/ec-utils/Cargo.toml index 43daad0892187ff34c94c1fbc657534f071dc6f2..142a5abf9b30d660077d39c12808a26d47bd41b6 100644 --- a/substrate/primitives/crypto/ec-utils/Cargo.toml +++ b/substrate/primitives/crypto/ec-utils/Cargo.toml @@ -28,7 +28,6 @@ ark-ed-on-bls12-377-ext = { version = "0.4.1", default-features = false, optiona ark-ed-on-bls12-377 = { version = "0.4.0", default-features = false, optional = true } ark-scale = { version = "0.0.12", default-features = false, features = ["hazmat"], optional = true } sp-runtime-interface = { path = "../../runtime-interface", default-features = false, optional = true } -sp-std = { path = "../../std", default-features = false, optional = true } [features] default = ["std"] @@ -47,9 +46,8 @@ std = [ "ark-ed-on-bls12-381-bandersnatch?/std", "ark-scale?/std", "sp-runtime-interface?/std", - "sp-std?/std", ] -common = ["ark-ec", "ark-scale", "sp-runtime-interface", "sp-std"] +common = ["ark-ec", "ark-scale", "sp-runtime-interface"] bls12-377 = ["ark-bls12-377", "ark-bls12-377-ext", "common"] bls12-381 = ["ark-bls12-381", "ark-bls12-381-ext", "common"] bw6-761 = ["ark-bw6-761", "ark-bw6-761-ext", "common"] diff --git a/substrate/primitives/crypto/ec-utils/src/bls12_377.rs b/substrate/primitives/crypto/ec-utils/src/bls12_377.rs index 8f19a2c4a1911f0d1045d0eebf98958236830e0f..a1ea5dbbf935a48ebc5fc971517657c8210c8deb 100644 --- a/substrate/primitives/crypto/ec-utils/src/bls12_377.rs +++ b/substrate/primitives/crypto/ec-utils/src/bls12_377.rs @@ -18,10 +18,10 @@ //! *BLS12-377* types and host functions. use crate::utils; +use alloc::vec::Vec; use ark_bls12_377_ext::CurveHooks; use ark_ec::{pairing::Pairing, CurveConfig}; use sp_runtime_interface::runtime_interface; -use sp_std::vec::Vec; /// First pairing group definitions. pub mod g1 { diff --git a/substrate/primitives/crypto/ec-utils/src/bls12_381.rs b/substrate/primitives/crypto/ec-utils/src/bls12_381.rs index 99a0289b7ad26b09bcb39e469ec8bf5203e47cb2..5e02862ed7ba53fcf3c25d037683bad0059496d3 100644 --- a/substrate/primitives/crypto/ec-utils/src/bls12_381.rs +++ b/substrate/primitives/crypto/ec-utils/src/bls12_381.rs @@ -18,10 +18,10 @@ //! *BLS12-381* types and host functions. use crate::utils; +use alloc::vec::Vec; use ark_bls12_381_ext::CurveHooks; use ark_ec::{pairing::Pairing, CurveConfig}; use sp_runtime_interface::runtime_interface; -use sp_std::vec::Vec; /// First pairing group definitions. pub mod g1 { diff --git a/substrate/primitives/crypto/ec-utils/src/bw6_761.rs b/substrate/primitives/crypto/ec-utils/src/bw6_761.rs index a68abf6e43e0384a1afe6beafdd99963a6fc48ff..4ee1035f670f8a9607df4fc9a513abdab73d454a 100644 --- a/substrate/primitives/crypto/ec-utils/src/bw6_761.rs +++ b/substrate/primitives/crypto/ec-utils/src/bw6_761.rs @@ -18,10 +18,10 @@ //! *BW6-761* types and host functions. use crate::utils; +use alloc::vec::Vec; use ark_bw6_761_ext::CurveHooks; use ark_ec::{pairing::Pairing, CurveConfig}; use sp_runtime_interface::runtime_interface; -use sp_std::vec::Vec; /// First pairing group definitions. pub mod g1 { diff --git a/substrate/primitives/crypto/ec-utils/src/ed_on_bls12_377.rs b/substrate/primitives/crypto/ec-utils/src/ed_on_bls12_377.rs index a03be41b854284d5a02956f5ae099f1b2c21d4ed..e068507b3473e68671f00be314a6167a874a2ce0 100644 --- a/substrate/primitives/crypto/ec-utils/src/ed_on_bls12_377.rs +++ b/substrate/primitives/crypto/ec-utils/src/ed_on_bls12_377.rs @@ -18,10 +18,10 @@ //! *Ed-on-BLS12-377* types and host functions. use crate::utils; +use alloc::vec::Vec; use ark_ec::CurveConfig; use ark_ed_on_bls12_377_ext::CurveHooks; use sp_runtime_interface::runtime_interface; -use sp_std::vec::Vec; /// Curve hooks jumping into [`host_calls`] host functions. #[derive(Copy, Clone)] diff --git a/substrate/primitives/crypto/ec-utils/src/ed_on_bls12_381_bandersnatch.rs b/substrate/primitives/crypto/ec-utils/src/ed_on_bls12_381_bandersnatch.rs index 9d63f35876521091fd757c9e7ca15a446fd1f1e9..487ad98dac657e5ed5ea01f045c82f60185f549c 100644 --- a/substrate/primitives/crypto/ec-utils/src/ed_on_bls12_381_bandersnatch.rs +++ b/substrate/primitives/crypto/ec-utils/src/ed_on_bls12_381_bandersnatch.rs @@ -19,10 +19,10 @@ //! computationally expensive operations. use crate::utils; +use alloc::vec::Vec; use ark_ec::CurveConfig; use ark_ed_on_bls12_381_bandersnatch_ext::CurveHooks; use sp_runtime_interface::runtime_interface; -use sp_std::vec::Vec; /// Curve hooks jumping into [`host_calls`] host functions. #[derive(Copy, Clone)] diff --git a/substrate/primitives/crypto/ec-utils/src/lib.rs b/substrate/primitives/crypto/ec-utils/src/lib.rs index 970ad71765a5b27791a84a4fb963f8e4386b9297..3254bbe648f4f2e8fae02c33d03d50e10c0a91cb 100644 --- a/substrate/primitives/crypto/ec-utils/src/lib.rs +++ b/substrate/primitives/crypto/ec-utils/src/lib.rs @@ -32,6 +32,8 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + #[cfg(feature = "bls12-377")] pub mod bls12_377; #[cfg(feature = "bls12-381")] diff --git a/substrate/primitives/crypto/ec-utils/src/utils.rs b/substrate/primitives/crypto/ec-utils/src/utils.rs index d0dd8ed8131c4d40b68bdbc0a1f7155318a9c449..47a49fe16cc2405c3edc9a1ffe6dfb86e19fe183 100644 --- a/substrate/primitives/crypto/ec-utils/src/utils.rs +++ b/substrate/primitives/crypto/ec-utils/src/utils.rs @@ -21,6 +21,7 @@ // curve may be excluded by the build we resort to `#[allow(unused)]` to // suppress the expected warning. +use alloc::vec::Vec; use ark_ec::{ pairing::{MillerLoopOutput, Pairing}, short_weierstrass::{Affine as SWAffine, Projective as SWProjective, SWCurveConfig}, @@ -31,7 +32,6 @@ use ark_scale::{ ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Validate}, scale::{Decode, Encode}, }; -use sp_std::vec::Vec; // SCALE encoding parameters shared by all the enabled modules const SCALE_USAGE: u8 = ark_scale::make_usage(Compress::No, Validate::No); diff --git a/substrate/primitives/externalities/Cargo.toml b/substrate/primitives/externalities/Cargo.toml index 6dc4f0a0dadf7d4ab906c21edca72cc3ac69a51a..20fa3e3e397c23632a67ca86cecf1a593e48a0e2 100644 --- a/substrate/primitives/externalities/Cargo.toml +++ b/substrate/primitives/externalities/Cargo.toml @@ -19,9 +19,8 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } environmental = { version = "1.1.3", default-features = false } -sp-std = { path = "../std", default-features = false } sp-storage = { path = "../storage", default-features = false } [features] default = ["std"] -std = ["codec/std", "environmental/std", "sp-std/std", "sp-storage/std"] +std = ["codec/std", "environmental/std", "sp-storage/std"] diff --git a/substrate/primitives/externalities/src/extensions.rs b/substrate/primitives/externalities/src/extensions.rs index d99dfe6cf530ae29718a1cf0a24a5a6afa851894..a4aa847a1aa8495763e4da7862e907256cf678c4 100644 --- a/substrate/primitives/externalities/src/extensions.rs +++ b/substrate/primitives/externalities/src/extensions.rs @@ -23,10 +23,12 @@ //! It is required that each extension implements the [`Extension`] trait. use crate::Error; -use sp_std::{ - any::{Any, TypeId}, +use alloc::{ boxed::Box, collections::btree_map::{BTreeMap, Entry}, +}; +use core::{ + any::{Any, TypeId}, ops::DerefMut, }; diff --git a/substrate/primitives/externalities/src/lib.rs b/substrate/primitives/externalities/src/lib.rs index 411ec97a6b82423ee6053027f03deeb802f361d0..142200f614a69d8a5e195db8d434fba4bb5a83b7 100644 --- a/substrate/primitives/externalities/src/lib.rs +++ b/substrate/primitives/externalities/src/lib.rs @@ -25,11 +25,10 @@ //! //! This crate exposes the main [`Externalities`] trait. -use sp_std::{ - any::{Any, TypeId}, - boxed::Box, - vec::Vec, -}; +extern crate alloc; + +use alloc::{boxed::Box, vec::Vec}; +use core::any::{Any, TypeId}; use sp_storage::{ChildInfo, StateVersion, TrackedStorageKey}; diff --git a/substrate/primitives/genesis-builder/Cargo.toml b/substrate/primitives/genesis-builder/Cargo.toml index bf12433c5f4048c9ad4b6dca688ffa6ef0cd3a8e..5a8f1c2962ce6ae24621fa3b8e855e5aec985297 100644 --- a/substrate/primitives/genesis-builder/Cargo.toml +++ b/substrate/primitives/genesis-builder/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true license = "Apache-2.0" homepage = "https://substrate.io" repository.workspace = true -description = "Substrate GenesisConfig builder API" +description = "Substrate RuntimeGenesisConfig builder API" readme = "README.md" [lints] @@ -18,9 +18,8 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sp-api = { path = "../api", default-features = false } sp-runtime = { path = "../runtime", default-features = false } -sp-std = { path = "../std", default-features = false } serde_json = { features = ["alloc", "arbitrary_precision"], workspace = true } [features] default = ["std"] -std = ["serde_json/std", "sp-api/std", "sp-runtime/std", "sp-std/std"] +std = ["serde_json/std", "sp-api/std", "sp-runtime/std"] diff --git a/substrate/primitives/genesis-builder/src/lib.rs b/substrate/primitives/genesis-builder/src/lib.rs index e002cd3aa6f704d8889fd35d04fdd65b46a1b754..4970042187ce0239e29c23ec31597bc5ad21c42e 100644 --- a/substrate/primitives/genesis-builder/src/lib.rs +++ b/substrate/primitives/genesis-builder/src/lib.rs @@ -19,36 +19,39 @@ //! Substrate genesis config builder //! -//! This Runtime API allows to construct `GenesisConfig`, in particular: -//! - serialize the runtime default `GenesisConfig` struct into json format, -//! - put the GenesisConfig struct into the storage. Internally this operation calls +//! This Runtime API allows to construct `RuntimeGenesisConfig`, in particular: +//! - serialize the runtime default `RuntimeGenesisConfig` struct into json format, +//! - put the RuntimeGenesisConfig struct into the storage. Internally this operation calls //! `GenesisBuild::build` function for all runtime pallets, which is typically provided by //! pallet's author. -//! - deserialize the `GenesisConfig` from given json blob and put `GenesisConfig` into the state -//! storage. Allows to build customized configuration. +//! - deserialize the `RuntimeGenesisConfig` from given json blob and put `RuntimeGenesisConfig` +//! into the state storage. Allows to build customized configuration. //! -//! Providing externalities with empty storage and putting `GenesisConfig` into storage allows to -//! catch and build the raw storage of `GenesisConfig` which is the foundation for genesis block. +//! Providing externalities with empty storage and putting `RuntimeGenesisConfig` into storage +//! allows to catch and build the raw storage of `RuntimeGenesisConfig` which is the foundation for +//! genesis block. + +extern crate alloc; /// The result type alias, used in build methods. `Err` contains formatted error message. pub type Result = core::result::Result<(), sp_runtime::RuntimeString>; sp_api::decl_runtime_apis! { - /// API to interact with GenesisConfig for the runtime + /// API to interact with RuntimeGenesisConfig for the runtime pub trait GenesisBuilder { - /// Creates the default `GenesisConfig` and returns it as a JSON blob. + /// Creates the default `RuntimeGenesisConfig` and returns it as a JSON blob. /// - /// This function instantiates the default `GenesisConfig` struct for the runtime and serializes it into a JSON - /// blob. It returns a `Vec` containing the JSON representation of the default `GenesisConfig`. - fn create_default_config() -> sp_std::vec::Vec; + /// This function instantiates the default `RuntimeGenesisConfig` struct for the runtime and serializes it into a JSON + /// blob. It returns a `Vec` containing the JSON representation of the default `RuntimeGenesisConfig`. + fn create_default_config() -> alloc::vec::Vec; - /// Build `GenesisConfig` from a JSON blob not using any defaults and store it in the storage. + /// Build `RuntimeGenesisConfig` from a JSON blob not using any defaults and store it in the storage. /// - /// This function deserializes the full `GenesisConfig` from the given JSON blob and puts it into the storage. + /// This function deserializes the full `RuntimeGenesisConfig` from the given JSON blob and puts it into the storage. /// If the provided JSON blob is incorrect or incomplete or the deserialization fails, an error is returned. /// It is recommended to log any errors encountered during the process. /// - /// Please note that provided json blob must contain all `GenesisConfig` fields, no defaults will be used. - fn build_config(json: sp_std::vec::Vec) -> Result; + /// Please note that provided json blob must contain all `RuntimeGenesisConfig` fields, no defaults will be used. + fn build_config(json: alloc::vec::Vec) -> Result; } } diff --git a/substrate/primitives/inherents/Cargo.toml b/substrate/primitives/inherents/Cargo.toml index bfb1d7733471fa7fd9f0a13c14d337e34188065d..6463c423fe7b7e8368c642af8fdf3d88974747ef 100644 --- a/substrate/primitives/inherents/Cargo.toml +++ b/substrate/primitives/inherents/Cargo.toml @@ -23,7 +23,6 @@ scale-info = { version = "2.10.0", default-features = false, features = ["derive impl-trait-for-tuples = "0.2.2" thiserror = { optional = true, workspace = true } sp-runtime = { path = "../runtime", default-features = false, optional = true } -sp-std = { path = "../std", default-features = false } [dev-dependencies] futures = "0.3.21" @@ -35,6 +34,5 @@ std = [ "codec/std", "scale-info/std", "sp-runtime/std", - "sp-std/std", "thiserror", ] diff --git a/substrate/primitives/inherents/src/client_side.rs b/substrate/primitives/inherents/src/client_side.rs index 27479de136f2dedb333883e642d7ddc899446c45..3c299dfa4eea0203e7853a437c712c6618c09b00 100644 --- a/substrate/primitives/inherents/src/client_side.rs +++ b/substrate/primitives/inherents/src/client_side.rs @@ -23,7 +23,7 @@ use sp_runtime::traits::Block as BlockT; /// It is possible for the caller to provide custom arguments to the callee by setting the /// `ExtraArgs` generic parameter. /// -/// The crate already provides some convience implementations of this trait for +/// The crate already provides some convince implementations of this trait for /// `Box` and closures. So, it should not be required to implement /// this trait manually. #[async_trait::async_trait] diff --git a/substrate/primitives/inherents/src/lib.rs b/substrate/primitives/inherents/src/lib.rs index dd7c294f1e245126bd02ef14a470595dc13841bb..80787669856ffc46af28207d2cec787e5ed78d5c 100644 --- a/substrate/primitives/inherents/src/lib.rs +++ b/substrate/primitives/inherents/src/lib.rs @@ -162,9 +162,11 @@ #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs)] +extern crate alloc; + use codec::{Decode, Encode}; -use sp_std::{ +use alloc::{ collections::btree_map::{BTreeMap, Entry, IntoIter}, vec::Vec, }; diff --git a/substrate/primitives/io/Cargo.toml b/substrate/primitives/io/Cargo.toml index c78def9bf4429eab1e5c9822a52732ee3a3c74ca..dddea4ffa232541023c91d354aa6fb2013332a98 100644 --- a/substrate/primitives/io/Cargo.toml +++ b/substrate/primitives/io/Cargo.toml @@ -38,6 +38,9 @@ tracing-core = { version = "0.1.32", default-features = false } # Required for backwards compatibility reason, but only used for verifying when `UseDalekExt` is set. ed25519-dalek = { version = "2.1", default-features = false, optional = true } +[target.'cfg(all(any(target_arch = "riscv32", target_arch = "riscv64"), substrate_runtime))'.dependencies] +polkavm-derive = { workspace = true } + [build-dependencies] rustversion = "1.0.6" @@ -82,7 +85,7 @@ disable_allocator = [] # This gives the caller direct programmatic access to the error message. # # When disabled the error message will only be printed out in the -# logs, with the caller receving a generic "wasm `unreachable` instruction executed" +# logs, with the caller receiving a generic "wasm `unreachable` instruction executed" # error message. # # This has no effect if both `disable_panic_handler` and `disable_oom` diff --git a/substrate/primitives/io/src/global_alloc_riscv.rs b/substrate/primitives/io/src/global_alloc_riscv.rs new file mode 100644 index 0000000000000000000000000000000000000000..8d7c69c20945946a352238dc8a94ecaba2f2a266 --- /dev/null +++ b/substrate/primitives/io/src/global_alloc_riscv.rs @@ -0,0 +1,19 @@ +// 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. + +#[global_allocator] +static ALLOCATOR: polkavm_derive::LeakingAllocator = polkavm_derive::LeakingAllocator; diff --git a/substrate/primitives/io/src/global_alloc_wasm.rs b/substrate/primitives/io/src/global_alloc_wasm.rs new file mode 100644 index 0000000000000000000000000000000000000000..cf19a6e21a2d26f20d7989a9b155709b2afa73ac --- /dev/null +++ b/substrate/primitives/io/src/global_alloc_wasm.rs @@ -0,0 +1,34 @@ +// 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. + +use core::alloc::{GlobalAlloc, Layout}; + +/// Allocator used by Substrate from within the runtime. +struct RuntimeAllocator; + +#[global_allocator] +static ALLOCATOR: RuntimeAllocator = RuntimeAllocator; + +unsafe impl GlobalAlloc for RuntimeAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + crate::allocator::malloc(layout.size() as u32) + } + + unsafe fn dealloc(&self, ptr: *mut u8, _: Layout) { + crate::allocator::free(ptr) + } +} diff --git a/substrate/primitives/io/src/lib.rs b/substrate/primitives/io/src/lib.rs index 6d34199416a36deeb011003989f6e361d535a0e6..ec32b7290330fc7a1fd76dc4d7ce86210bcceba0 100644 --- a/substrate/primitives/io/src/lib.rs +++ b/substrate/primitives/io/src/lib.rs @@ -129,6 +129,16 @@ use sp_externalities::{Externalities, ExternalitiesExt}; pub use sp_externalities::MultiRemovalResults; +#[cfg(all(not(feature = "disable_allocator"), substrate_runtime, target_family = "wasm"))] +mod global_alloc_wasm; + +#[cfg(all( + not(feature = "disable_allocator"), + substrate_runtime, + any(target_arch = "riscv32", target_arch = "riscv64") +))] +mod global_alloc_riscv; + #[cfg(feature = "std")] const LOG_TARGET: &str = "runtime::io"; @@ -1071,7 +1081,7 @@ pub trait Crypto { /// 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. + /// If batch verification is not enabled, the signature will be verified immediately. /// To get the result of the batch verification, [`finish_batch_verify`] /// needs to be called. /// @@ -1686,9 +1696,9 @@ mod tracing_setup { /// The PassingTracingSubscriber implements `tracing_core::Subscriber` /// and pushes the information across the runtime interface to the host - struct PassingTracingSubsciber; + struct PassingTracingSubscriber; - impl tracing_core::Subscriber for PassingTracingSubsciber { + impl tracing_core::Subscriber for PassingTracingSubscriber { fn enabled(&self, metadata: &Metadata<'_>) -> bool { wasm_tracing::enabled(Crossing(metadata.into())) } @@ -1721,7 +1731,7 @@ mod tracing_setup { /// set the global bridging subscriber once. pub fn init_tracing() { if TRACING_SET.load(Ordering::Relaxed) == false { - set_global_default(Dispatch::new(PassingTracingSubsciber {})) + set_global_default(Dispatch::new(PassingTracingSubscriber {})) .expect("We only ever call this once"); TRACING_SET.store(true, Ordering::Relaxed); } @@ -1737,30 +1747,6 @@ mod tracing_setup { pub use tracing_setup::init_tracing; -/// Allocator used by Substrate from within the runtime. -#[cfg(substrate_runtime)] -struct RuntimeAllocator; - -#[cfg(all(not(feature = "disable_allocator"), substrate_runtime))] -#[global_allocator] -static ALLOCATOR: RuntimeAllocator = RuntimeAllocator; - -#[cfg(substrate_runtime)] -mod allocator_impl { - use super::*; - use core::alloc::{GlobalAlloc, Layout}; - - unsafe impl GlobalAlloc for RuntimeAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - allocator::malloc(layout.size() as u32) - } - - unsafe fn dealloc(&self, ptr: *mut u8, _: Layout) { - allocator::free(ptr) - } - } -} - /// Crashes the execution of the program. /// /// Equivalent to the WASM `unreachable` instruction, RISC-V `unimp` instruction, diff --git a/substrate/primitives/keyring/Cargo.toml b/substrate/primitives/keyring/Cargo.toml index 1c936b6685be86c54a70824ab902f616c0639945..940fe90916d25aabef032437f0a6e0a05d270464 100644 --- a/substrate/primitives/keyring/Cargo.toml +++ b/substrate/primitives/keyring/Cargo.toml @@ -18,10 +18,13 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] strum = { version = "0.24.1", features = ["derive"], default-features = false } -sp-core = { path = "../core" } -sp-runtime = { path = "../runtime" } +sp-core = { path = "../core", default-features = false } +sp-runtime = { path = "../runtime", default-features = false } [features] +default = ["std"] +std = ["sp-core/std", "sp-runtime/std", "strum/std"] + # This feature adds Bandersnatch crypto primitives. # It should not be used in production since the implementation and interface may still # be subject to significant changes. diff --git a/substrate/primitives/keyring/check-features-variants.sh b/substrate/primitives/keyring/check-features-variants.sh new file mode 100755 index 0000000000000000000000000000000000000000..9c28d83589465483a290b6485f9c77fa7dbdb5a8 --- /dev/null +++ b/substrate/primitives/keyring/check-features-variants.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env -S bash -eux + +export RUSTFLAGS="-Cdebug-assertions=y -Dwarnings" +T=wasm32-unknown-unknown + +cargo check --release +cargo check --release --features="bandersnatch-experimental" +cargo check --release --target=$T --no-default-features diff --git a/substrate/primitives/keyring/src/bandersnatch.rs b/substrate/primitives/keyring/src/bandersnatch.rs index eb60f85632725ca9efbac46c63222fc71609d74f..67fc5c47df643ee4b45b8ebbb99089abdb466d96 100644 --- a/substrate/primitives/keyring/src/bandersnatch.rs +++ b/substrate/primitives/keyring/src/bandersnatch.rs @@ -18,14 +18,21 @@ //! A set of well-known keys used for testing. pub use sp_core::bandersnatch; +#[cfg(feature = "std")] +use sp_core::bandersnatch::Signature; use sp_core::{ - bandersnatch::{Pair, Public, Signature}, + bandersnatch::{Pair, Public}, crypto::UncheckedFrom, hex2array, ByteArray, Pair as PairT, }; +extern crate alloc; +use alloc::{fmt, format, str::FromStr, string::String, vec::Vec}; + /// Set of test accounts. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, strum::EnumIter)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, strum::EnumIter, Ord, PartialOrd, +)] pub enum Keyring { Alice, Bob, @@ -56,6 +63,7 @@ impl Keyring { Public::from(self).to_raw_vec() } + #[cfg(feature = "std")] pub fn sign(self, msg: &[u8]) -> Signature { Pair::from(self).sign(msg) } @@ -102,16 +110,16 @@ impl From for &'static str { #[derive(Debug)] pub struct ParseKeyringError; -impl std::fmt::Display for ParseKeyringError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for ParseKeyringError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ParseKeyringError") } } -impl std::str::FromStr for Keyring { +impl FromStr for Keyring { type Err = ParseKeyringError; - fn from_str(s: &str) -> Result::Err> { + fn from_str(s: &str) -> Result::Err> { match s { "Alice" => Ok(Keyring::Alice), "Bob" => Ok(Keyring::Bob), diff --git a/substrate/primitives/keyring/src/ed25519.rs b/substrate/primitives/keyring/src/ed25519.rs index ade42b294940213664d0553a4e4ec537ce553f15..98ca368e53caac26ae690b76500475b1749a07b1 100644 --- a/substrate/primitives/keyring/src/ed25519.rs +++ b/substrate/primitives/keyring/src/ed25519.rs @@ -18,14 +18,21 @@ //! Support code for the runtime. A set of test accounts. pub use sp_core::ed25519; +#[cfg(feature = "std")] +use sp_core::ed25519::Signature; use sp_core::{ - ed25519::{Pair, Public, Signature}, + ed25519::{Pair, Public}, hex2array, ByteArray, Pair as PairT, H256, }; use sp_runtime::AccountId32; +extern crate alloc; +use alloc::{format, string::String, vec::Vec}; + /// Set of test accounts. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, strum::EnumIter)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, strum::EnumIter, Ord, PartialOrd, +)] pub enum Keyring { Alice, Bob, @@ -76,6 +83,7 @@ impl Keyring { self.to_raw_public().into() } + #[cfg(feature = "std")] pub fn sign(self, msg: &[u8]) -> Signature { Pair::from(self).sign(msg) } diff --git a/substrate/primitives/keyring/src/lib.rs b/substrate/primitives/keyring/src/lib.rs index ee7fd56ba11bf5855d8ae648f073ea004bb257aa..f753bf4b0dd684d0aa292eeca1f97ad9f5cafdda 100644 --- a/substrate/primitives/keyring/src/lib.rs +++ b/substrate/primitives/keyring/src/lib.rs @@ -17,6 +17,8 @@ //! Support code for the runtime. A set of test accounts. +#![cfg_attr(not(feature = "std"), no_std)] + /// Test account crypto for sr25519. pub mod sr25519; diff --git a/substrate/primitives/keyring/src/sr25519.rs b/substrate/primitives/keyring/src/sr25519.rs index 1c2a2526efb1eccb16cb0696f89cb3fc7583357b..a3a506152d7d6f0b76b366a930860e39f3f8fae0 100644 --- a/substrate/primitives/keyring/src/sr25519.rs +++ b/substrate/primitives/keyring/src/sr25519.rs @@ -18,15 +18,22 @@ //! Support code for the runtime. A set of test accounts. pub use sp_core::sr25519; +#[cfg(feature = "std")] +use sp_core::sr25519::Signature; use sp_core::{ hex2array, - sr25519::{Pair, Public, Signature}, + sr25519::{Pair, Public}, ByteArray, Pair as PairT, H256, }; use sp_runtime::AccountId32; +extern crate alloc; +use alloc::{fmt, format, str::FromStr, string::String, vec::Vec}; + /// Set of test accounts. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, strum::EnumIter)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, strum::EnumIter, Ord, PartialOrd, +)] pub enum Keyring { Alice, Bob, @@ -77,6 +84,7 @@ impl Keyring { self.to_raw_public().into() } + #[cfg(feature = "std")] pub fn sign(self, msg: &[u8]) -> Signature { Pair::from(self).sign(msg) } @@ -140,16 +148,16 @@ impl From for sp_runtime::MultiSigner { #[derive(Debug)] pub struct ParseKeyringError; -impl std::fmt::Display for ParseKeyringError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for ParseKeyringError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ParseKeyringError") } } -impl std::str::FromStr for Keyring { +impl FromStr for Keyring { type Err = ParseKeyringError; - fn from_str(s: &str) -> Result::Err> { + fn from_str(s: &str) -> Result::Err> { match s { "alice" => Ok(Keyring::Alice), "bob" => Ok(Keyring::Bob), diff --git a/substrate/primitives/keystore/src/testing.rs b/substrate/primitives/keystore/src/testing.rs index e10660b126a33f23e00f5774412f65d665cc6737..d8610ecfa5b60d8891b6690e00ce6efb55342983 100644 --- a/substrate/primitives/keystore/src/testing.rs +++ b/substrate/primitives/keystore/src/testing.rs @@ -528,7 +528,7 @@ mod tests { assert!(res.is_some()); // does not verify with default out-of-the-box verification - assert!(!ecdsa_bls377::Pair::verify(&res.clone().unwrap(), &msg[..], &pair.public())); + assert!(!ecdsa_bls377::Pair::verify(&res.unwrap(), &msg[..], &pair.public())); // should verify using keccak256 as hasher assert!(ecdsa_bls377::Pair::verify_with_hasher::( diff --git a/substrate/primitives/maybe-compressed-blob/Cargo.toml b/substrate/primitives/maybe-compressed-blob/Cargo.toml index fa5383d03b10d5c6560e50ff28be7b6e71ffe2b0..178c915ce837efeccad42e52e1cb57bf30d5ff72 100644 --- a/substrate/primitives/maybe-compressed-blob/Cargo.toml +++ b/substrate/primitives/maybe-compressed-blob/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true license = "Apache-2.0" homepage = "https://substrate.io" repository.workspace = true -description = "Handling of blobs, usually Wasm code, which may be compresed" +description = "Handling of blobs, usually Wasm code, which may be compressed" documentation = "https://docs.rs/sp-maybe-compressed-blob" readme = "README.md" diff --git a/substrate/primitives/merkle-mountain-range/Cargo.toml b/substrate/primitives/merkle-mountain-range/Cargo.toml index 50d8477823948a4005a9cb0ccfabf73349f91222..9c07f699b37a419c972bfaf34854f376eae5b800 100644 --- a/substrate/primitives/merkle-mountain-range/Cargo.toml +++ b/substrate/primitives/merkle-mountain-range/Cargo.toml @@ -24,7 +24,6 @@ sp-api = { path = "../api", default-features = false } sp-core = { path = "../core", default-features = false } sp-debug-derive = { path = "../debug-derive", default-features = false } sp-runtime = { path = "../runtime", default-features = false } -sp-std = { path = "../std", default-features = false } thiserror = { optional = true, workspace = true } [dev-dependencies] @@ -43,7 +42,6 @@ std = [ "sp-core/std", "sp-debug-derive/std", "sp-runtime/std", - "sp-std/std", ] # Serde support without relying on std features. diff --git a/substrate/primitives/merkle-mountain-range/src/lib.rs b/substrate/primitives/merkle-mountain-range/src/lib.rs index 6c0e75005ead8609a597735ef3ed19a85076dc89..c76d66bb08ea700bd6bcc1272d56310bda51881f 100644 --- a/substrate/primitives/merkle-mountain-range/src/lib.rs +++ b/substrate/primitives/merkle-mountain-range/src/lib.rs @@ -20,14 +20,16 @@ #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs)] +extern crate alloc; + pub use mmr_lib; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; +use core::fmt; use scale_info::TypeInfo; use sp_debug_derive::RuntimeDebug; use sp_runtime::traits; -use sp_std::fmt; -#[cfg(not(feature = "std"))] -use sp_std::prelude::Vec; pub mod utils; @@ -248,10 +250,10 @@ impl DataOrHash { pub struct Compact { /// Internal tuple representation. pub tuple: T, - _hash: sp_std::marker::PhantomData, + _hash: core::marker::PhantomData, } -impl sp_std::ops::Deref for Compact { +impl core::ops::Deref for Compact { type Target = T; fn deref(&self) -> &Self::Target { diff --git a/substrate/primitives/merkle-mountain-range/src/utils.rs b/substrate/primitives/merkle-mountain-range/src/utils.rs index b9171c96a6201eb99a63355841245a2a7853513b..72674e24a272849fcabf16f243764dbb2858c0fa 100644 --- a/substrate/primitives/merkle-mountain-range/src/utils.rs +++ b/substrate/primitives/merkle-mountain-range/src/utils.rs @@ -20,9 +20,9 @@ use codec::Encode; use mmr_lib::helper; -use sp_runtime::traits::{CheckedAdd, CheckedSub, Header, One}; #[cfg(not(feature = "std"))] -use sp_std::prelude::Vec; +use alloc::vec::Vec; +use sp_runtime::traits::{CheckedAdd, CheckedSub, Header, One}; use crate::{Error, LeafIndex, NodeIndex}; @@ -131,7 +131,7 @@ impl NodesUtils { /// Used for nodes added by now finalized blocks. /// Never read keys using `node_canon_offchain_key` unless you sure that /// there's no `node_offchain_key` key in the storage. - pub fn node_canon_offchain_key(prefix: &[u8], pos: NodeIndex) -> sp_std::prelude::Vec { + pub fn node_canon_offchain_key(prefix: &[u8], pos: NodeIndex) -> alloc::vec::Vec { (prefix, pos).encode() } } diff --git a/substrate/primitives/metadata-ir/Cargo.toml b/substrate/primitives/metadata-ir/Cargo.toml index e4203d0e378efb557ace209761e012bc571400fa..31c839b5c485e71a09aaf2f4966c0ed8443aaab5 100644 --- a/substrate/primitives/metadata-ir/Cargo.toml +++ b/substrate/primitives/metadata-ir/Cargo.toml @@ -19,8 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } frame-metadata = { version = "16.0.0", default-features = false, features = ["current"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -sp-std = { path = "../std", default-features = false } [features] default = ["std"] -std = ["codec/std", "frame-metadata/std", "scale-info/std", "sp-std/std"] +std = ["codec/std", "frame-metadata/std", "scale-info/std"] diff --git a/substrate/primitives/metadata-ir/src/lib.rs b/substrate/primitives/metadata-ir/src/lib.rs index edfa58f8618942e34b6937733cbd2a5f5c4dffbb..18b20f2ccaac31a92175a420d0f569ef7dd1677b 100644 --- a/substrate/primitives/metadata-ir/src/lib.rs +++ b/substrate/primitives/metadata-ir/src/lib.rs @@ -20,6 +20,8 @@ #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs)] +extern crate alloc; + // Re-export. #[doc(hidden)] pub use frame_metadata; @@ -52,8 +54,8 @@ pub fn into_version(metadata: MetadataIR, version: u32) -> Option sp_std::vec::Vec { - sp_std::vec![V14, V15] +pub fn supported_versions() -> alloc::vec::Vec { + alloc::vec![V14, V15] } /// Transform the IR to the latest stable metadata version. diff --git a/substrate/primitives/metadata-ir/src/types.rs b/substrate/primitives/metadata-ir/src/types.rs index b107d20a8e2bfbf55d0d558bb60346cb7a52dd70..b05f26ff55d4e408e1d8459ba5539f44423b2c18 100644 --- a/substrate/primitives/metadata-ir/src/types.rs +++ b/substrate/primitives/metadata-ir/src/types.rs @@ -160,7 +160,7 @@ pub struct ExtrinsicMetadataIR { pub ty: T::Type, /// Extrinsic version. pub version: u8, - /// The type of the address that signes the extrinsic + /// The type of the address that signs the extrinsic pub address_ty: T::Type, /// The type of the outermost Call enum. pub call_ty: T::Type, diff --git a/substrate/primitives/mixnet/Cargo.toml b/substrate/primitives/mixnet/Cargo.toml index 39cf684b9772a60f0b8b0932470ccdaf3e4d89ac..8ba7f36da43c144de727cba6f8d8427323be4320 100644 --- a/substrate/primitives/mixnet/Cargo.toml +++ b/substrate/primitives/mixnet/Cargo.toml @@ -20,7 +20,6 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } sp-api = { default-features = false, path = "../api" } sp-application-crypto = { default-features = false, path = "../application-crypto" } -sp-std = { default-features = false, path = "../std" } [features] default = ["std"] @@ -29,5 +28,4 @@ std = [ "scale-info/std", "sp-api/std", "sp-application-crypto/std", - "sp-std/std", ] diff --git a/substrate/primitives/mixnet/src/lib.rs b/substrate/primitives/mixnet/src/lib.rs index 58b8a10f0cd8d5d4198e6fcf7b75e09d4f639827..462d7fc61f217e7496f76b7113b3a62345a6288b 100644 --- a/substrate/primitives/mixnet/src/lib.rs +++ b/substrate/primitives/mixnet/src/lib.rs @@ -20,5 +20,7 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + pub mod runtime_api; pub mod types; diff --git a/substrate/primitives/mixnet/src/runtime_api.rs b/substrate/primitives/mixnet/src/runtime_api.rs index 28ab40e633787545c4710043d746a9de3295d232..f3260782b0b9a42b15d2b68d705802186fc50cc5 100644 --- a/substrate/primitives/mixnet/src/runtime_api.rs +++ b/substrate/primitives/mixnet/src/runtime_api.rs @@ -18,7 +18,7 @@ //! Runtime API for querying mixnet configuration and registering mixnodes. use super::types::{Mixnode, MixnodesErr, SessionIndex, SessionStatus}; -use sp_std::vec::Vec; +use alloc::vec::Vec; sp_api::decl_runtime_apis! { /// API to query the mixnet session status and mixnode sets, and to register mixnodes. diff --git a/substrate/primitives/mixnet/src/types.rs b/substrate/primitives/mixnet/src/types.rs index fc214f94d1cbf2e4063efdf09dcdbc1e0aceb258..3b069e1fb3ad7f92dcd32867d2ffe51890579cf6 100644 --- a/substrate/primitives/mixnet/src/types.rs +++ b/substrate/primitives/mixnet/src/types.rs @@ -17,9 +17,9 @@ //! Mixnet types used by both host and runtime. +use alloc::vec::Vec; use codec::{Decode, Encode}; use scale_info::TypeInfo; -use sp_std::vec::Vec; mod app { use sp_application_crypto::{app_crypto, key_types::MIXNET, sr25519}; @@ -90,8 +90,8 @@ pub enum MixnodesErr { }, } -impl sp_std::fmt::Display for MixnodesErr { - fn fmt(&self, fmt: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { +impl core::fmt::Display for MixnodesErr { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { match self { MixnodesErr::InsufficientRegistrations { num, min } => write!(fmt, "{num} mixnode(s) registered; {min} is the minimum"), diff --git a/substrate/primitives/npos-elections/Cargo.toml b/substrate/primitives/npos-elections/Cargo.toml index 7373aa849cb8d8f47fe3cffefd5302eee28a7dfb..b0b9890c061918aa25d5cc47129c7d1d29b1300d 100644 --- a/substrate/primitives/npos-elections/Cargo.toml +++ b/substrate/primitives/npos-elections/Cargo.toml @@ -22,7 +22,6 @@ serde = { features = ["alloc", "derive"], optional = true, workspace = true } sp-arithmetic = { path = "../arithmetic", default-features = false } sp-core = { path = "../core", default-features = false } sp-runtime = { path = "../runtime", default-features = false } -sp-std = { path = "../std", default-features = false } [dev-dependencies] rand = "0.8.5" @@ -38,7 +37,6 @@ std = [ "sp-arithmetic/std", "sp-core/std", "sp-runtime/std", - "sp-std/std", ] # Serde support without relying on std features. diff --git a/substrate/primitives/npos-elections/fuzzer/Cargo.toml b/substrate/primitives/npos-elections/fuzzer/Cargo.toml index bcd908b970508f19bd131d9eba08c18306b632c8..8e1dbaf2377e7583fb9d37f1d43170fe6ca9edca 100644 --- a/substrate/primitives/npos-elections/fuzzer/Cargo.toml +++ b/substrate/primitives/npos-elections/fuzzer/Cargo.toml @@ -17,7 +17,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } honggfuzz = "0.5" rand = { version = "0.8", features = ["small_rng", "std"] } sp-npos-elections = { path = ".." } diff --git a/substrate/primitives/npos-elections/src/assignments.rs b/substrate/primitives/npos-elections/src/assignments.rs index 2ac2b9bebd771cf480a36e128133387026681f7e..0686fdda643bd471b3cc2e23c5d6aa6b0222ca5e 100644 --- a/substrate/primitives/npos-elections/src/assignments.rs +++ b/substrate/primitives/npos-elections/src/assignments.rs @@ -18,6 +18,7 @@ //! Structs and helpers for distributing a voter's stake among various winners. use crate::{ExtendedBalance, IdentifierT, PerThing128}; +use alloc::vec::Vec; #[cfg(feature = "serde")] use codec::{Decode, Encode}; use sp_arithmetic::{ @@ -25,7 +26,6 @@ use sp_arithmetic::{ Normalizable, PerThing, }; use sp_core::RuntimeDebug; -use sp_std::vec::Vec; /// A voter's stake assignment among a set of targets, represented as ratios. #[derive(RuntimeDebug, Clone, Default)] diff --git a/substrate/primitives/npos-elections/src/balancing.rs b/substrate/primitives/npos-elections/src/balancing.rs index 90dbe7eb7147817882eb85f9ba321619ac74150e..fb14c8686581977974145b4b082c2dc6d401182e 100644 --- a/substrate/primitives/npos-elections/src/balancing.rs +++ b/substrate/primitives/npos-elections/src/balancing.rs @@ -27,8 +27,8 @@ //! See [`balance`] for more information. use crate::{BalancingConfig, Edge, ExtendedBalance, IdentifierT, Voter}; +use alloc::vec::Vec; use sp_arithmetic::traits::Zero; -use sp_std::prelude::*; /// Balance the weight distribution of a given `voters` at most `iterations` times, or up until the /// point where the biggest difference created per iteration of all stakes is `tolerance`. If this diff --git a/substrate/primitives/npos-elections/src/helpers.rs b/substrate/primitives/npos-elections/src/helpers.rs index 082491ea04281b5ebb73c33e7cb970ebc4c18071..7df6ec9d9dbaaffcf6e3966a7adf87cb521982b8 100644 --- a/substrate/primitives/npos-elections/src/helpers.rs +++ b/substrate/primitives/npos-elections/src/helpers.rs @@ -18,8 +18,8 @@ //! Helper methods for npos-elections. use crate::{Assignment, Error, IdentifierT, PerThing128, StakedAssignment, VoteWeight}; +use alloc::vec::Vec; use sp_arithmetic::PerThing; -use sp_std::prelude::*; /// Converts a vector of ratio assignments into ones with absolute budget value. /// diff --git a/substrate/primitives/npos-elections/src/lib.rs b/substrate/primitives/npos-elections/src/lib.rs index 8d741f4130d9ca724dc6a7566edf8ed6a483e7fa..82ac40fe273780ab33b84f752cea8a876f61b6a6 100644 --- a/substrate/primitives/npos-elections/src/lib.rs +++ b/substrate/primitives/npos-elections/src/lib.rs @@ -74,15 +74,16 @@ #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + +use alloc::{collections::btree_map::BTreeMap, rc::Rc, vec, vec::Vec}; use codec::{Decode, Encode, MaxEncodedLen}; +use core::{cell::RefCell, cmp::Ordering}; use scale_info::TypeInfo; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use sp_arithmetic::{traits::Zero, Normalizable, PerThing, Rational128, ThresholdOrd}; use sp_core::{bounded::BoundedVec, RuntimeDebug}; -use sp_std::{ - cell::RefCell, cmp::Ordering, collections::btree_map::BTreeMap, prelude::*, rc::Rc, vec, -}; #[cfg(test)] mod mock; @@ -198,7 +199,7 @@ impl ElectionScore { } } -impl sp_std::cmp::Ord for ElectionScore { +impl core::cmp::Ord for ElectionScore { fn cmp(&self, other: &Self) -> Ordering { // we delegate this to the lexicographic cmp of slices`, and to incorporate that we want the // third element to be minimized, we swap them. @@ -210,7 +211,7 @@ impl sp_std::cmp::Ord for ElectionScore { } } -impl sp_std::cmp::PartialOrd for ElectionScore { +impl core::cmp::PartialOrd for ElectionScore { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } @@ -278,8 +279,8 @@ impl Edge { } #[cfg(feature = "std")] -impl sp_std::fmt::Debug for Edge { - fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { +impl core::fmt::Debug for Edge { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "Edge({:?}, weight = {:?})", self.who, self.weight) } } @@ -299,7 +300,7 @@ pub struct Voter { #[cfg(feature = "std")] impl std::fmt::Debug for Voter { - fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Voter({:?}, budget = {}, edges = {:?})", self.who, self.budget, self.edges) } } diff --git a/substrate/primitives/npos-elections/src/mock.rs b/substrate/primitives/npos-elections/src/mock.rs index 2fc49fd72cd03874c33ddfab637eb1870a3d52dc..91757404145f3e86e2dbe414441b567edea63638 100644 --- a/substrate/primitives/npos-elections/src/mock.rs +++ b/substrate/primitives/npos-elections/src/mock.rs @@ -19,12 +19,12 @@ #![cfg(test)] +use alloc::collections::btree_map::BTreeMap; use sp_arithmetic::{ traits::{One, SaturatedConversion, Zero}, PerThing, }; use sp_runtime::assert_eq_error_rate; -use sp_std::collections::btree_map::BTreeMap; use crate::{seq_phragmen, Assignment, ElectionResult, ExtendedBalance, PerThing128, VoteWeight}; @@ -131,7 +131,7 @@ where if let Some(winner) = candidates .iter_mut() .filter(|c| !c.elected) - .min_by(|x, y| x.score.partial_cmp(&y.score).unwrap_or(sp_std::cmp::Ordering::Equal)) + .min_by(|x, y| x.score.partial_cmp(&y.score).unwrap_or(core::cmp::Ordering::Equal)) { winner.elected = true; for n in &mut voters { @@ -226,10 +226,10 @@ where if backing_backed_stake.len() > 0 { let max_stake = backing_backed_stake .iter() - .max_by(|x, y| x.partial_cmp(&y).unwrap_or(sp_std::cmp::Ordering::Equal)) + .max_by(|x, y| x.partial_cmp(&y).unwrap_or(core::cmp::Ordering::Equal)) .expect("vector with positive length will have a max; qed"); let min_stake = backed_stakes_iter - .min_by(|x, y| x.partial_cmp(&y).unwrap_or(sp_std::cmp::Ordering::Equal)) + .min_by(|x, y| x.partial_cmp(&y).unwrap_or(core::cmp::Ordering::Equal)) .expect("iterator with positive length will have a min; qed"); difference = max_stake - min_stake; @@ -254,7 +254,7 @@ where support_map .get(&x.0) .and_then(|x| support_map.get(&y.0).and_then(|y| x.total.partial_cmp(&y.total))) - .unwrap_or(sp_std::cmp::Ordering::Equal) + .unwrap_or(core::cmp::Ordering::Equal) }); let mut cumulative_stake = 0.0; diff --git a/substrate/primitives/npos-elections/src/node.rs b/substrate/primitives/npos-elections/src/node.rs index caca9561d83970ec935ce8dc86d4415280dedfea..6fe50ad1d0acf124d2a91e86c666fe31046d442c 100644 --- a/substrate/primitives/npos-elections/src/node.rs +++ b/substrate/primitives/npos-elections/src/node.rs @@ -17,7 +17,8 @@ //! (very) Basic implementation of a graph node used in the reduce algorithm. -use sp_std::{cell::RefCell, fmt, prelude::*, rc::Rc}; +use alloc::{rc::Rc, vec::Vec}; +use core::{cell::RefCell, fmt}; /// The role that a node can accept. #[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Debug)] @@ -49,8 +50,8 @@ impl NodeId { } #[cfg(feature = "std")] -impl sp_std::fmt::Debug for NodeId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> sp_std::fmt::Result { +impl fmt::Debug for NodeId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "Node({:?}, {:?})", diff --git a/substrate/primitives/npos-elections/src/phragmen.rs b/substrate/primitives/npos-elections/src/phragmen.rs index c3578065f364cd9eac0a82bc7c6bbf5daef3b632..f331152e722a2205064e7ab291b445897758034c 100644 --- a/substrate/primitives/npos-elections/src/phragmen.rs +++ b/substrate/primitives/npos-elections/src/phragmen.rs @@ -24,12 +24,12 @@ use crate::{ balancing, setup_inputs, BalancingConfig, CandidatePtr, ElectionResult, ExtendedBalance, IdentifierT, PerThing128, VoteWeight, Voter, }; +use alloc::vec::Vec; use sp_arithmetic::{ helpers_128bit::multiply_by_rational_with_rounding, traits::{Bounded, Zero}, Rational128, Rounding, }; -use sp_std::prelude::*; /// The denominator used for loads. Since votes are collected as u64, the smallest ratio that we /// might collect is `1/approval_stake` where approval stake is the sum of votes. Hence, some number diff --git a/substrate/primitives/npos-elections/src/phragmms.rs b/substrate/primitives/npos-elections/src/phragmms.rs index df6becf47472f6b6663f217ff6b8a08cd5bf78f9..9a17f0dfa7ce972531640887b2c4bc3e734c6bbe 100644 --- a/substrate/primitives/npos-elections/src/phragmms.rs +++ b/substrate/primitives/npos-elections/src/phragmms.rs @@ -25,8 +25,8 @@ use crate::{ balance, setup_inputs, BalancingConfig, CandidatePtr, ElectionResult, ExtendedBalance, IdentifierT, PerThing128, VoteWeight, Voter, }; +use alloc::{rc::Rc, vec, vec::Vec}; use sp_arithmetic::{traits::Bounded, PerThing, Rational128}; -use sp_std::{prelude::*, rc::Rc}; /// Execute the phragmms method. /// @@ -232,8 +232,8 @@ pub(crate) fn apply_elected( mod tests { use super::*; use crate::{Assignment, ElectionResult}; + use alloc::rc::Rc; use sp_runtime::{Perbill, Percent}; - use sp_std::rc::Rc; #[test] fn basic_election_manual_works() { diff --git a/substrate/primitives/npos-elections/src/pjr.rs b/substrate/primitives/npos-elections/src/pjr.rs index f0e59a25d440b67f65656d7eb04f16743bc1c353..6e3775199a21944a96b908536485009204c14b4d 100644 --- a/substrate/primitives/npos-elections/src/pjr.rs +++ b/substrate/primitives/npos-elections/src/pjr.rs @@ -26,8 +26,8 @@ use crate::{ Candidate, CandidatePtr, Edge, ExtendedBalance, IdentifierT, Support, SupportMap, Supports, VoteWeight, Voter, }; +use alloc::{collections::btree_map::BTreeMap, rc::Rc, vec::Vec}; use sp_arithmetic::{traits::Zero, Perbill}; -use sp_std::{collections::btree_map::BTreeMap, rc::Rc, vec::Vec}; /// The type used as the threshold. /// /// Just some reading sugar; Must always be same as [`ExtendedBalance`]; @@ -261,7 +261,7 @@ fn prepare_pjr_input( } } - // Convert Suppports into a SupportMap + // Convert Supports into a SupportMap // // As a flat list, we're limited to linear search. That gives the production of `candidates`, // below, a complexity of `O(s*c)`, where `s == supports.len()` and `c == all_candidates.len()`. diff --git a/substrate/primitives/npos-elections/src/reduce.rs b/substrate/primitives/npos-elections/src/reduce.rs index 6a5a0159e4efb9ee6d742d94adc1cc328c24aef1..3fd291f88abefada6812c971d379c9934eae7c2f 100644 --- a/substrate/primitives/npos-elections/src/reduce.rs +++ b/substrate/primitives/npos-elections/src/reduce.rs @@ -51,11 +51,12 @@ use crate::{ node::{Node, NodeId, NodeRef, NodeRole}, ExtendedBalance, IdentifierT, StakedAssignment, }; -use sp_arithmetic::traits::{Bounded, Zero}; -use sp_std::{ +use alloc::{ collections::btree_map::{BTreeMap, Entry::*}, - prelude::*, + vec, + vec::Vec, }; +use sp_arithmetic::traits::{Bounded, Zero}; /// Map type used for reduce_4. Can be easily swapped with HashMap. type Map = BTreeMap<(A, A), A>; @@ -392,7 +393,7 @@ fn reduce_all(assignments: &mut Vec>) -> u32 // voter_root_path.last().unwrap()); TODO: @kian // the common path must be non-void.. debug_assert!(common_count > 0); - // and smaller than btoh + // and smaller than both debug_assert!(common_count <= voter_root_path.len()); debug_assert!(common_count <= target_root_path.len()); diff --git a/substrate/primitives/npos-elections/src/traits.rs b/substrate/primitives/npos-elections/src/traits.rs index d49970873b7072c364e1bf26d713a4498a92578b..afa6ac70880a5ac0f32e4c3b00ec8f40beefc52d 100644 --- a/substrate/primitives/npos-elections/src/traits.rs +++ b/substrate/primitives/npos-elections/src/traits.rs @@ -18,8 +18,8 @@ //! Traits for the npos-election operations. use crate::ExtendedBalance; +use core::{fmt::Debug, ops::Mul}; use sp_arithmetic::PerThing; -use sp_std::{fmt::Debug, ops::Mul, prelude::*}; /// an aggregator trait for a generic type of a voter/target identifier. This usually maps to /// substrate's account id. diff --git a/substrate/primitives/runtime-interface/src/lib.rs b/substrate/primitives/runtime-interface/src/lib.rs index 8b0edf1ec818e57237e6170c8d27f2d1f492cc46..f6ef27789b36f08a6a5e7a58035304cab345737e 100644 --- a/substrate/primitives/runtime-interface/src/lib.rs +++ b/substrate/primitives/runtime-interface/src/lib.rs @@ -283,7 +283,7 @@ pub use sp_std; /// /// `key` holds the pointer and the length to the `data` slice. /// pub fn call(data: &[u8]) -> Vec { /// extern "C" { pub fn ext_call_version_2(key: u64); } -/// // Should call into extenal `ext_call_version_2(<[u8] as IntoFFIValue>::into_ffi_value(key))` +/// // Should call into external `ext_call_version_2(<[u8] as IntoFFIValue>::into_ffi_value(key))` /// // But this is too much to replicate in a doc test so here we just return a dummy vector. /// // Note that we jump into the latest version not marked as `register_only` (i.e. version 2). /// Vec::new() diff --git a/substrate/primitives/runtime-interface/test-wasm-deprecated/src/lib.rs b/substrate/primitives/runtime-interface/test-wasm-deprecated/src/lib.rs index 2f42e60504eb77af41c9107f9a5bb1178ffaf0ea..871a4922ce3ab13e147f1b4f5039021e309b1132 100644 --- a/substrate/primitives/runtime-interface/test-wasm-deprecated/src/lib.rs +++ b/substrate/primitives/runtime-interface/test-wasm-deprecated/src/lib.rs @@ -45,19 +45,19 @@ pub fn import_sp_io() { #[runtime_interface] pub trait TestApi { - fn test_versionning(&self, _data: u32) -> bool { + fn test_versioning(&self, _data: u32) -> bool { // should not be called unimplemented!() } } wasm_export_functions! { - fn test_versionning_works() { + fn test_versioning_works() { // old api allows only 42 and 50 - assert!(test_api::test_versionning(42)); - assert!(test_api::test_versionning(50)); + assert!(test_api::test_versioning(42)); + assert!(test_api::test_versioning(50)); - assert!(!test_api::test_versionning(142)); - assert!(!test_api::test_versionning(0)); + assert!(!test_api::test_versioning(142)); + assert!(!test_api::test_versioning(0)); } } diff --git a/substrate/primitives/runtime-interface/test-wasm/src/lib.rs b/substrate/primitives/runtime-interface/test-wasm/src/lib.rs index cf1ff3bca088fa7ba25047f48a452d775679c0fc..2b3fc728f6ff46cc1332e413d8dee965f6ec7e3d 100644 --- a/substrate/primitives/runtime-interface/test-wasm/src/lib.rs +++ b/substrate/primitives/runtime-interface/test-wasm/src/lib.rs @@ -126,21 +126,21 @@ pub trait TestApi { val } - fn test_versionning(&self, data: u32) -> bool { + fn test_versioning(&self, data: u32) -> bool { data == 42 || data == 50 } #[version(2)] - fn test_versionning(&self, data: u32) -> bool { + fn test_versioning(&self, data: u32) -> bool { data == 42 } - fn test_versionning_register_only(&self, data: u32) -> bool { + fn test_versioning_register_only(&self, data: u32) -> bool { data == 80 } #[version(2, register_only)] - fn test_versionning_register_only(&self, data: u32) -> bool { + fn test_versioning_register_only(&self, data: u32) -> bool { data == 42 } @@ -282,21 +282,21 @@ wasm_export_functions! { assert_eq!(0, len); } - fn test_versionning_works() { + fn test_versioning_works() { // we fix new api to accept only 42 as a proper input // as opposed to sp-runtime-interface-test-wasm-deprecated::test_api::verify_input // which accepted 42 and 50. - assert!(test_api::test_versionning(42)); + assert!(test_api::test_versioning(42)); - assert!(!test_api::test_versionning(50)); - assert!(!test_api::test_versionning(102)); + assert!(!test_api::test_versioning(50)); + assert!(!test_api::test_versioning(102)); } - fn test_versionning_register_only_works() { + fn test_versioning_register_only_works() { // Ensure that we will import the version of the runtime interface function that // isn't tagged with `register_only`. - assert!(!test_api::test_versionning_register_only(42)); - assert!(test_api::test_versionning_register_only(80)); + assert!(!test_api::test_versioning_register_only(42)); + assert!(test_api::test_versioning_register_only(80)); } fn test_return_input_as_tuple() { diff --git a/substrate/primitives/runtime-interface/test/src/lib.rs b/substrate/primitives/runtime-interface/test/src/lib.rs index 215704a1121543f8290623273a1ad6be8295389d..05a955fbe3f86106ab1e95a67e6c7c0074770ac4 100644 --- a/substrate/primitives/runtime-interface/test/src/lib.rs +++ b/substrate/primitives/runtime-interface/test/src/lib.rs @@ -163,18 +163,18 @@ fn test_array_return_value_memory_is_freed() { } #[test] -fn test_versionining_with_new_host_works() { +fn test_versioning_with_new_host_works() { // We call to the new wasm binary with new host function. - call_wasm_method::(wasm_binary_unwrap(), "test_versionning_works"); + call_wasm_method::(wasm_binary_unwrap(), "test_versioning_works"); // we call to the old wasm binary with a new host functions // old versions of host functions should be called and test should be ok! - call_wasm_method::(wasm_binary_deprecated_unwrap(), "test_versionning_works"); + call_wasm_method::(wasm_binary_deprecated_unwrap(), "test_versioning_works"); } #[test] -fn test_versionining_register_only() { - call_wasm_method::(wasm_binary_unwrap(), "test_versionning_register_only_works"); +fn test_versioning_register_only() { + call_wasm_method::(wasm_binary_unwrap(), "test_versioning_register_only_works"); } fn run_test_in_another_process( diff --git a/substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs b/substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs index 5b54caf597b7396dd5f32b0f695db2e011a9fca1..8f6c0c6f650daa36f18f132e3fc0fa13eaa76621 100644 --- a/substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs +++ b/substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs @@ -40,7 +40,7 @@ use sp_std::{fmt, prelude::*}; /// the decoding fails. const EXTRINSIC_FORMAT_VERSION: u8 = 4; -/// The `SingaturePayload` of `UncheckedExtrinsic`. +/// The `SignaturePayload` of `UncheckedExtrinsic`. type UncheckedSignaturePayload = (Address, Signature, Extra); /// An extrinsic right from the external world. This is unchecked and so can contain a signature. diff --git a/substrate/primitives/runtime/src/lib.rs b/substrate/primitives/runtime/src/lib.rs index ddf92554c83056f192015a26285408b7fdf33695..44bf3c969e5441245d1547e779195cdb68a83599 100644 --- a/substrate/primitives/runtime/src/lib.rs +++ b/substrate/primitives/runtime/src/lib.rs @@ -998,6 +998,16 @@ impl TransactionOutcome { } } +/// Confines the kind of extrinsics that can be included in a block. +#[derive(Debug, Default, PartialEq, Eq, Clone, Copy, Encode, Decode, TypeInfo)] +pub enum ExtrinsicInclusionMode { + /// All extrinsics are allowed to be included in this block. + #[default] + AllExtrinsics, + /// Inherents are allowed to be included. + OnlyInherents, +} + #[cfg(test)] mod tests { use crate::traits::BlakeTwo256; diff --git a/substrate/primitives/runtime/src/multiaddress.rs b/substrate/primitives/runtime/src/multiaddress.rs index 89b0a3bcf8cccb0d740aa5f7bc4e750a6e9aaa08..c435606312e43ab0772ac71dd06f62489b416248 100644 --- a/substrate/primitives/runtime/src/multiaddress.rs +++ b/substrate/primitives/runtime/src/multiaddress.rs @@ -32,7 +32,7 @@ pub enum MultiAddress { Raw(Vec), /// It's a 32 byte representation. Address32([u8; 32]), - /// Its a 20 byte representation. + /// It's a 20 byte representation. Address20([u8; 20]), } diff --git a/substrate/primitives/runtime/src/offchain/storage_lock.rs b/substrate/primitives/runtime/src/offchain/storage_lock.rs index 56d0eeae527cf072c4b7756690956ab192c5e805..cfdaa954fe5e8e79e276f3e29bd0df7a1cc63267 100644 --- a/substrate/primitives/runtime/src/offchain/storage_lock.rs +++ b/substrate/primitives/runtime/src/offchain/storage_lock.rs @@ -556,7 +556,7 @@ mod tests { let res = lock.try_lock(); assert_eq!(res.is_ok(), false); - // sleep again untill sleep_until > deadline + // sleep again until sleep_until > deadline offchain::sleep_until(offchain::timestamp().add(Duration::from_millis(200))); // the lock has expired, failed to extend it diff --git a/substrate/primitives/runtime/src/testing.rs b/substrate/primitives/runtime/src/testing.rs index 5f94c834a8f295bee945e9d772019e12ed37c828..b4aeda5a0e7a3e75a44c9d50b4ed22788d1f96e8 100644 --- a/substrate/primitives/runtime/src/testing.rs +++ b/substrate/primitives/runtime/src/testing.rs @@ -284,9 +284,9 @@ where } /// The signature payload of a `TestXt`. -type TxSingaturePayload = (u64, Extra); +type TxSignaturePayload = (u64, Extra); -impl SignaturePayload for TxSingaturePayload { +impl SignaturePayload for TxSignaturePayload { type SignatureAddress = u64; type Signature = (); type SignatureExtra = Extra; @@ -299,7 +299,7 @@ impl SignaturePayload for TxSingaturePayload { #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo)] pub struct TestXt { /// Signature of the extrinsic. - pub signature: Option>, + pub signature: Option>, /// Call of the extrinsic. pub call: Call, } @@ -348,7 +348,7 @@ impl traits::Extrinsic for TestXt { type Call = Call; - type SignaturePayload = TxSingaturePayload; + type SignaturePayload = TxSignaturePayload; fn is_signed(&self) -> Option { Some(self.signature.is_some()) diff --git a/substrate/primitives/runtime/src/traits.rs b/substrate/primitives/runtime/src/traits.rs index caede5e2b59a7e3843c1ec966a02f2bbd86f2a84..5a6c306dd2a10d8a8e133ff89fedb8793f87d66b 100644 --- a/substrate/primitives/runtime/src/traits.rs +++ b/substrate/primitives/runtime/src/traits.rs @@ -133,7 +133,7 @@ impl Verify for sp_core::ecdsa::Signature { self.as_ref(), &sp_io::hashing::blake2_256(msg.get()), ) { - Ok(pubkey) => signer.as_ref() == &pubkey[..], + Ok(pubkey) => signer.0 == pubkey, _ => false, } } @@ -330,7 +330,7 @@ impl> Morph for MorphInto { } } -/// Implementation of `TryMorph` which attmepts to convert between types using `TryInto`. +/// Implementation of `TryMorph` which attempts to convert between types using `TryInto`. pub struct TryMorphInto(sp_std::marker::PhantomData); impl> TryMorph for TryMorphInto { type Outcome = T; @@ -1449,7 +1449,7 @@ pub trait Dispatchable { /// to represent the dispatch class and weight. type Info; /// Additional information that is returned by `dispatch`. Can be used to supply the caller - /// with information about a `Dispatchable` that is ownly known post dispatch. + /// with information about a `Dispatchable` that is only known post dispatch. type PostInfo: Eq + PartialEq + Clone + Copy + Encode + Decode + Printable; /// Actually dispatch this call and return the result of it. fn dispatch(self, origin: Self::RuntimeOrigin) diff --git a/substrate/primitives/session/Cargo.toml b/substrate/primitives/session/Cargo.toml index 99764c0a17f92b4772ee64a134633403835b1348..784228c42e9bf3bd245e77b74e10f78e26bdd53d 100644 --- a/substrate/primitives/session/Cargo.toml +++ b/substrate/primitives/session/Cargo.toml @@ -22,7 +22,6 @@ sp-api = { path = "../api", default-features = false } sp-core = { path = "../core", default-features = false } sp-runtime = { path = "../runtime", optional = true } sp-staking = { path = "../staking", default-features = false } -sp-std = { path = "../std", default-features = false } sp-keystore = { path = "../keystore", optional = true } [features] @@ -35,5 +34,4 @@ std = [ "sp-keystore/std", "sp-runtime/std", "sp-staking/std", - "sp-std/std", ] diff --git a/substrate/primitives/session/src/lib.rs b/substrate/primitives/session/src/lib.rs index 9933495fd60135c826d822cc3d3e1471492797fb..fe7a38047338d92d18e5427587c7e46bdee11829 100644 --- a/substrate/primitives/session/src/lib.rs +++ b/substrate/primitives/session/src/lib.rs @@ -19,6 +19,8 @@ #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + use codec::{Decode, Encode}; #[cfg(feature = "std")] @@ -26,9 +28,9 @@ use sp_api::ProvideRuntimeApi; #[cfg(feature = "std")] use sp_runtime::traits::Block as BlockT; +use alloc::vec::Vec; use sp_core::RuntimeDebug; use sp_staking::SessionIndex; -use sp_std::vec::Vec; pub mod runtime_api; pub use runtime_api::*; diff --git a/substrate/primitives/session/src/runtime_api.rs b/substrate/primitives/session/src/runtime_api.rs index 5e508cd3dbd3872bc5a5cf99b80e0eed1f755116..3acc882aabcff943fc78d886d0c6a112d9687e00 100644 --- a/substrate/primitives/session/src/runtime_api.rs +++ b/substrate/primitives/session/src/runtime_api.rs @@ -15,8 +15,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +use alloc::vec::Vec; pub use sp_core::crypto::KeyTypeId; -use sp_std::prelude::*; sp_api::decl_runtime_apis! { /// Session keys runtime api. diff --git a/substrate/primitives/staking/Cargo.toml b/substrate/primitives/staking/Cargo.toml index 21346fbaca5383554d1285ceeaba43815b81857a..6304551b8e60e3e2c9030b2addcc83f3e95b01de 100644 --- a/substrate/primitives/staking/Cargo.toml +++ b/substrate/primitives/staking/Cargo.toml @@ -23,7 +23,6 @@ impl-trait-for-tuples = "0.2.2" sp-core = { path = "../core", default-features = false } sp-runtime = { path = "../runtime", default-features = false } -sp-std = { path = "../std", default-features = false } [features] default = ["std"] @@ -33,6 +32,5 @@ std = [ "serde/std", "sp-core/std", "sp-runtime/std", - "sp-std/std", ] runtime-benchmarks = ["sp-runtime/runtime-benchmarks"] diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index f5b4a1ed63fb3da2e9d69f51f8bc10355dbd0730..11b7ef41b9a7b06e1b67d43abb58e9aacc33d8ab 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -20,14 +20,17 @@ //! A crate which contains primitives that are useful for implementation that uses staking //! approaches in general. Definitions related to sessions, slashing, etc go here. +extern crate alloc; + use crate::currency_to_vote::CurrencyToVote; +use alloc::{collections::btree_map::BTreeMap, vec, vec::Vec}; use codec::{Decode, Encode, FullCodec, HasCompact, MaxEncodedLen}; +use core::ops::Sub; use scale_info::TypeInfo; use sp_runtime::{ traits::{AtLeast32BitUnsigned, Zero}, DispatchError, DispatchResult, RuntimeDebug, Saturating, }; -use sp_std::{collections::btree_map::BTreeMap, ops::Sub, vec, vec::Vec}; pub mod offence; @@ -172,7 +175,7 @@ pub trait StakingInterface { + Saturating; /// AccountId type used by the staking system. - type AccountId: Clone + sp_std::fmt::Debug; + type AccountId: Clone + core::fmt::Debug; /// Means of converting Currency to VoteWeight. type CurrencyToVote: CurrencyToVote; diff --git a/substrate/primitives/staking/src/offence.rs b/substrate/primitives/staking/src/offence.rs index 8013166374e064ab1b573e57991449567e3845da..30d96d0cbafce9f0540ffbb49cf15c7375ea405f 100644 --- a/substrate/primitives/staking/src/offence.rs +++ b/substrate/primitives/staking/src/offence.rs @@ -18,10 +18,10 @@ //! Common traits and types that are useful for describing offences for usage in environments //! that use staking. +use alloc::vec::Vec; use codec::{Decode, Encode}; use sp_core::Get; use sp_runtime::{transaction_validity::TransactionValidityError, DispatchError, Perbill}; -use sp_std::vec::Vec; use crate::SessionIndex; @@ -117,7 +117,7 @@ pub trait Offence { /// Errors that may happen on offence reports. #[derive(PartialEq, sp_runtime::RuntimeDebug)] pub enum OffenceError { - /// The report has already been sumbmitted. + /// The report has already been submitted. DuplicateReport, /// Other error has happened. diff --git a/substrate/primitives/state-machine/Cargo.toml b/substrate/primitives/state-machine/Cargo.toml index 09994f1ae91e1c9a7dba2faf169870f08a640b22..aaedb500b59cc1155c61fcf50732777e4e5c8ad4 100644 --- a/substrate/primitives/state-machine/Cargo.toml +++ b/substrate/primitives/state-machine/Cargo.toml @@ -28,7 +28,6 @@ tracing = { version = "0.1.29", optional = true } sp-core = { path = "../core", default-features = false } sp-externalities = { path = "../externalities", default-features = false } sp-panic-handler = { path = "../panic-handler", optional = true } -sp-std = { path = "../std", default-features = false } sp-trie = { path = "../trie", default-features = false } trie-db = { version = "0.28.0", default-features = false } @@ -51,7 +50,6 @@ std = [ "sp-externalities/std", "sp-panic-handler", "sp-runtime/std", - "sp-std/std", "sp-trie/std", "thiserror", "tracing", diff --git a/substrate/primitives/state-machine/src/backend.rs b/substrate/primitives/state-machine/src/backend.rs index ea9cd442d70ba55056d2ebdc27fa1eb26d0ba9c9..90be55d58a4ed87aa31b875ab0ffd13a7d2ea78a 100644 --- a/substrate/primitives/state-machine/src/backend.rs +++ b/substrate/primitives/state-machine/src/backend.rs @@ -23,13 +23,13 @@ use crate::{ trie_backend_essence::TrieBackendStorage, ChildStorageCollection, StorageCollection, StorageKey, StorageValue, UsageInfo, }; +use alloc::vec::Vec; use codec::Encode; use core::marker::PhantomData; use hash_db::Hasher; use sp_core::storage::{ChildInfo, StateVersion, TrackedStorageKey}; #[cfg(feature = "std")] use sp_core::traits::RuntimeCode; -use sp_std::vec::Vec; use sp_trie::{MerkleValue, PrefixedMemoryDB}; /// A struct containing arguments for iterating over the storage. @@ -179,7 +179,7 @@ pub type BackendTransaction = PrefixedMemoryDB; /// to it. /// /// The clone operation (if implemented) should be cheap. -pub trait Backend: sp_std::fmt::Debug { +pub trait Backend: core::fmt::Debug { /// An error type when fetching data is not possible. type Error: super::Error; diff --git a/substrate/primitives/state-machine/src/ext.rs b/substrate/primitives/state-machine/src/ext.rs index 11df46f2a4a3a901753c10a5acf1b6535e61f8ea..9aa32bc866cfab9e2db44959c93bc4fe0b11e120 100644 --- a/substrate/primitives/state-machine/src/ext.rs +++ b/substrate/primitives/state-machine/src/ext.rs @@ -32,12 +32,10 @@ use sp_core::storage::{ use sp_externalities::{Extension, ExtensionStore, Externalities, MultiRemovalResults}; use crate::{log_error, trace, warn}; -use sp_std::{ +use alloc::{boxed::Box, vec, vec::Vec}; +use core::{ any::{Any, TypeId}, - boxed::Box, cmp::Ordering, - vec, - vec::Vec, }; #[cfg(feature = "std")] use std::error; @@ -46,7 +44,7 @@ const EXT_NOT_ALLOWED_TO_FAIL: &str = "Externalities not allowed to fail within const BENCHMARKING_FN: &str = "\ This is a special fn only for benchmarking where a database commit happens from the runtime. For that reason client started transactions before calling into runtime are not allowed. - Without client transactions the loop condition garantuees the success of the tx close."; + Without client transactions the loop condition guarantees the success of the tx close."; #[cfg(feature = "std")] fn guard() -> sp_panic_handler::AbortGuard { @@ -724,7 +722,7 @@ impl Encode for EncodeOpaqueValue { } } -/// Auxialiary structure for appending a value to a storage item. +/// Auxiliary structure for appending a value to a storage item. pub(crate) struct StorageAppend<'a>(&'a mut Vec); impl<'a> StorageAppend<'a> { @@ -739,7 +737,7 @@ impl<'a> StorageAppend<'a> { pub fn append(&mut self, value: Vec) { let value = vec![EncodeOpaqueValue(value)]; - let item = sp_std::mem::take(self.0); + let item = core::mem::take(self.0); *self.0 = match Vec::::append_or_new(item, &value) { Ok(item) => item, diff --git a/substrate/primitives/state-machine/src/lib.rs b/substrate/primitives/state-machine/src/lib.rs index 5909a30a814c32686acbb6c369831a6d43649a5d..13087431d387b9e986519bbdcabfe9eb3c4fe5c5 100644 --- a/substrate/primitives/state-machine/src/lib.rs +++ b/substrate/primitives/state-machine/src/lib.rs @@ -20,6 +20,8 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + pub mod backend; #[cfg(feature = "std")] mod basic; @@ -118,8 +120,8 @@ pub type DefaultError = String; pub struct DefaultError; #[cfg(not(feature = "std"))] -impl sp_std::fmt::Display for DefaultError { - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { +impl core::fmt::Display for DefaultError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { write!(f, "DefaultError") } } @@ -1449,7 +1451,7 @@ mod tests { enum Item { InitializationItem, DiscardedItem, - CommitedItem, + CommittedItem, } let key = b"events".to_vec(); @@ -1486,21 +1488,21 @@ mod tests { assert_eq!(ext.storage(key.as_slice()), Some(vec![Item::InitializationItem].encode())); - ext.storage_append(key.clone(), Item::CommitedItem.encode()); + ext.storage_append(key.clone(), Item::CommittedItem.encode()); assert_eq!( ext.storage(key.as_slice()), - Some(vec![Item::InitializationItem, Item::CommitedItem].encode()), + Some(vec![Item::InitializationItem, Item::CommittedItem].encode()), ); } overlay.start_transaction(); - // Then only initlaization item and second (committed) item should persist. + // Then only initialization item and second (committed) item should persist. { let ext = Ext::new(&mut overlay, backend, None); assert_eq!( ext.storage(key.as_slice()), - Some(vec![Item::InitializationItem, Item::CommitedItem].encode()), + Some(vec![Item::InitializationItem, Item::CommittedItem].encode()), ); } } @@ -1864,7 +1866,7 @@ mod tests { // a inner hashable node (&b"k"[..], Some(&long_vec[..])), // need to ensure this is not an inline node - // otherwhise we do not know what is accessed when + // otherwise we do not know what is accessed when // storing proof. (&b"key1"[..], Some(&vec![5u8; 32][..])), (&b"key2"[..], Some(&b"val3"[..])), diff --git a/substrate/primitives/state-machine/src/overlayed_changes/changeset.rs b/substrate/primitives/state-machine/src/overlayed_changes/changeset.rs index 59589dbbb37e8c37022ab176df0b02c25d53b3b6..601bc2e29198561d501e1f20b11c2323c0064989 100644 --- a/substrate/primitives/state-machine/src/overlayed_changes/changeset.rs +++ b/substrate/primitives/state-machine/src/overlayed_changes/changeset.rs @@ -20,16 +20,14 @@ use super::{Extrinsics, StorageKey, StorageValue}; #[cfg(not(feature = "std"))] -use sp_std::collections::btree_set::BTreeSet as Set; +use alloc::collections::btree_set::BTreeSet as Set; #[cfg(feature = "std")] use std::collections::HashSet as Set; use crate::warn; +use alloc::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; +use core::hash::Hash; use smallvec::SmallVec; -use sp_std::{ - collections::{btree_map::BTreeMap, btree_set::BTreeSet}, - hash::Hash, -}; const PROOF_OVERLAY_NON_EMPTY: &str = "\ An OverlayValue is always created with at least one transaction and dropped as soon @@ -49,7 +47,7 @@ pub struct NoOpenTransaction; #[cfg_attr(test, derive(PartialEq))] pub struct AlreadyInRuntime; -/// Error when calling `exit_runtime` when not being in runtime exection mdde. +/// Error when calling `exit_runtime` when not being in runtime execution mode. #[derive(Debug)] #[cfg_attr(test, derive(PartialEq))] pub struct NotInRuntime; @@ -225,7 +223,7 @@ impl OverlayedMap { /// This changeset might be created when there are already open transactions. /// We need to catch up here so that the child is at the same transaction depth. pub fn spawn_child(&self) -> Self { - use sp_std::iter::repeat; + use core::iter::repeat; Self { changes: Default::default(), dirty_keys: repeat(Set::new()).take(self.transaction_depth()).collect(), @@ -242,7 +240,7 @@ impl OverlayedMap { /// Get an optional reference to the value stored for the specified key. pub fn get(&self, key: &Q) -> Option<&OverlayedEntry> where - K: sp_std::borrow::Borrow, + K: core::borrow::Borrow, Q: Ord + ?Sized, { self.changes.get(key) @@ -271,7 +269,7 @@ impl OverlayedMap { /// /// Panics: /// Panics if there are open transactions: `transaction_depth() > 0` - pub fn drain_commited(self) -> impl Iterator { + pub fn drain_committed(self) -> impl Iterator { assert!(self.transaction_depth() == 0, "Drain is not allowed with open transactions."); self.changes.into_iter().map(|(k, mut v)| (k, v.pop_transaction().value)) } @@ -283,7 +281,7 @@ impl OverlayedMap { self.dirty_keys.len() } - /// Call this before transfering control to the runtime. + /// Call this before transferring control to the runtime. /// /// This protects all existing transactions from being removed by the runtime. /// Calling this while already inside the runtime will return an error. @@ -448,7 +446,7 @@ impl OverlayedChangeSet { /// Get the iterator over all changes that follow the supplied `key`. pub fn changes_after(&self, key: &[u8]) -> impl Iterator { - use sp_std::ops::Bound; + use core::ops::Bound; let range = (Bound::Excluded(key), Bound::Unbounded); self.changes.range::<[u8], _>(range).map(|(k, v)| (k.as_slice(), v)) } @@ -473,7 +471,7 @@ mod test { } fn assert_drained_changes(is: OverlayedChangeSet, expected: Changes) { - let is = is.drain_commited().collect::>(); + let is = is.drain_committed().collect::>(); let expected = expected .iter() .map(|(k, v)| (k.to_vec(), v.0.map(From::from))) @@ -482,7 +480,7 @@ mod test { } fn assert_drained(is: OverlayedChangeSet, expected: Drained) { - let is = is.drain_commited().collect::>(); + let is = is.drain_committed().collect::>(); let expected = expected .iter() .map(|(k, v)| (k.to_vec(), v.map(From::from))) @@ -528,7 +526,7 @@ mod test { changeset.set(b"key0".to_vec(), Some(b"val0-rolled".to_vec()), Some(1000)); changeset.set(b"key5".to_vec(), Some(b"val5-rolled".to_vec()), None); - // changes contain all changes not only the commmited ones. + // changes contain all changes not only the committed ones. let all_changes: Changes = vec![ (b"key0", (Some(b"val0-rolled"), vec![1, 10, 1000])), (b"key1", (Some(b"val1"), vec![1])), @@ -809,7 +807,7 @@ mod test { fn drain_with_open_transaction_panics() { let mut changeset = OverlayedChangeSet::default(); changeset.start_transaction(); - let _ = changeset.drain_commited(); + let _ = changeset.drain_committed(); } #[test] diff --git a/substrate/primitives/state-machine/src/overlayed_changes/mod.rs b/substrate/primitives/state-machine/src/overlayed_changes/mod.rs index 626cf6c3cafe128c98f6c5bedfe5d6cbe1ab958d..d6fc404e84fb5256656c5849d8f52894f37bfa02 100644 --- a/substrate/primitives/state-machine/src/overlayed_changes/mod.rs +++ b/substrate/primitives/state-machine/src/overlayed_changes/mod.rs @@ -22,6 +22,7 @@ mod offchain; use self::changeset::OverlayedChangeSet; use crate::{backend::Backend, stats::StateMachineStats, BackendTransaction, DefaultError}; +use alloc::{collections::btree_set::BTreeSet, vec::Vec}; use codec::{Decode, Encode}; use hash_db::Hasher; pub use offchain::OffchainOverlayedChanges; @@ -31,12 +32,13 @@ use sp_core::{ }; #[cfg(feature = "std")] use sp_externalities::{Extension, Extensions}; -#[cfg(not(feature = "std"))] -use sp_std::collections::btree_map::BTreeMap as Map; -use sp_std::{collections::btree_set::BTreeSet, vec::Vec}; use sp_trie::{empty_child_trie_root, LayoutV1}; + +#[cfg(not(feature = "std"))] +use alloc::collections::btree_map::BTreeMap as Map; #[cfg(feature = "std")] use std::collections::{hash_map::Entry as MapEntry, HashMap as Map}; + #[cfg(feature = "std")] use std::{ any::{Any, TypeId}, @@ -136,7 +138,7 @@ impl Clone for OverlayedChanges { } } -impl sp_std::fmt::Debug for OverlayedChanges { +impl core::fmt::Debug for OverlayedChanges { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("OverlayedChanges") .field("top", &self.top) @@ -259,7 +261,7 @@ impl Clone for StorageTransactionCache { } } -impl sp_std::fmt::Debug for StorageTransactionCache { +impl core::fmt::Debug for StorageTransactionCache { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let mut debug = f.debug_struct("StorageTransactionCache"); @@ -478,7 +480,7 @@ impl OverlayedChanges { Ok(()) } - /// Call this before transfering control to the runtime. + /// Call this before transferring control to the runtime. /// /// This protects all existing transactions from being removed by the runtime. /// Calling this while already inside the runtime will return an error. @@ -572,11 +574,11 @@ impl OverlayedChanges { }, }; - use sp_std::mem::take; - let main_storage_changes = take(&mut self.top).drain_commited(); + use core::mem::take; + let main_storage_changes = take(&mut self.top).drain_committed(); let child_storage_changes = take(&mut self.children) .into_iter() - .map(|(key, (val, info))| (key, (val.drain_commited(), info))); + .map(|(key, (val, info))| (key, (val.drain_committed(), info))); let offchain_storage_changes = self.offchain_drain_committed().collect(); @@ -777,7 +779,7 @@ where K: Ord, F: FnMut(&K, &mut V) -> bool, { - let old = sp_std::mem::replace(map, Map::default()); + let old = core::mem::replace(map, Map::default()); for (k, mut v) in old.into_iter() { if f(&k, &mut v) { map.insert(k, v); @@ -807,7 +809,7 @@ pub struct OverlayedExtensions<'a> { #[cfg(feature = "std")] impl<'a> OverlayedExtensions<'a> { - /// Create a new instance of overalyed extensions from the given extensions. + /// Create a new instance of overlaid extensions from the given extensions. pub fn new(extensions: &'a mut Extensions) -> Self { Self { extensions: extensions diff --git a/substrate/primitives/state-machine/src/overlayed_changes/offchain.rs b/substrate/primitives/state-machine/src/overlayed_changes/offchain.rs index 66e7ab5864c065f4d335b7daae9e3c686d9c3ec8..1e6965e874759e30b415783a560192af8412027d 100644 --- a/substrate/primitives/state-machine/src/overlayed_changes/offchain.rs +++ b/substrate/primitives/state-machine/src/overlayed_changes/offchain.rs @@ -18,8 +18,8 @@ //! Overlayed changes for offchain indexing. use super::changeset::OverlayedMap; +use alloc::vec::Vec; use sp_core::offchain::OffchainOverlayedChange; -use sp_std::prelude::Vec; /// In-memory storage for offchain workers recoding changes for the actual offchain storage /// implementation. @@ -48,7 +48,7 @@ impl OffchainOverlayedChanges { /// Drain all elements of changeset. pub fn drain(&mut self) -> impl Iterator { - sp_std::mem::take(self).into_iter() + core::mem::take(self).into_iter() } /// Remove a key and its associated value from the offchain database. diff --git a/substrate/primitives/state-machine/src/testing.rs b/substrate/primitives/state-machine/src/testing.rs index 0eb7b6d1118f9aa3cff1caee089d526b3f711d94..e19ba95755c1b16f5cc6a2d59a8ede48997707f0 100644 --- a/substrate/primitives/state-machine/src/testing.rs +++ b/substrate/primitives/state-machine/src/testing.rs @@ -417,7 +417,7 @@ mod tests { original_ext.backend.clone().into_storage(), ); - // Ensure all have the correct ref counrt + // Ensure all have the correct ref count assert!(original_ext.backend.backend_storage().keys().values().all(|r| *r == 2)); // Drain the raw storage and root. diff --git a/substrate/primitives/state-machine/src/trie_backend.rs b/substrate/primitives/state-machine/src/trie_backend.rs index 7496463e642100dd6cd6908525e29ee5fc34bf9a..f91ce5d2e52f6df1a4a3f61231691e9105118167 100644 --- a/substrate/primitives/state-machine/src/trie_backend.rs +++ b/substrate/primitives/state-machine/src/trie_backend.rs @@ -297,7 +297,7 @@ struct CachedIter where H: Hasher, { - last_key: sp_std::vec::Vec, + last_key: alloc::vec::Vec, iter: RawIter, } @@ -390,9 +390,9 @@ where } impl, H: Hasher, C: TrieCacheProvider, R: TrieRecorderProvider> - sp_std::fmt::Debug for TrieBackend + core::fmt::Debug for TrieBackend { - fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "TrieBackend") } } diff --git a/substrate/primitives/state-machine/src/trie_backend_essence.rs b/substrate/primitives/state-machine/src/trie_backend_essence.rs index 3f789111deeffe27ea4e8d8f7c47de8ea61eb082..a1f0057987411fc14d96c0c817659d585c4477f0 100644 --- a/substrate/primitives/state-machine/src/trie_backend_essence.rs +++ b/substrate/primitives/state-machine/src/trie_backend_essence.rs @@ -23,18 +23,19 @@ use crate::{ trie_backend::TrieCacheProvider, warn, StorageKey, StorageValue, }; +#[cfg(feature = "std")] +use alloc::sync::Arc; +use alloc::{boxed::Box, vec::Vec}; use codec::Codec; +use core::marker::PhantomData; use hash_db::{self, AsHashDB, HashDB, HashDBRef, Hasher, Prefix}; #[cfg(feature = "std")] use parking_lot::RwLock; use sp_core::storage::{ChildInfo, ChildType, StateVersion}; -#[cfg(feature = "std")] -use sp_std::sync::Arc; -use sp_std::{boxed::Box, marker::PhantomData, vec::Vec}; use sp_trie::{ child_delta_trie_root, delta_trie_root, empty_child_trie_root, - read_child_trie_first_descedant_value, read_child_trie_hash, read_child_trie_value, - read_trie_first_descedant_value, read_trie_value, + read_child_trie_first_descendant_value, read_child_trie_hash, read_child_trie_value, + read_trie_first_descendant_value, read_trie_value, trie_types::{TrieDBBuilder, TrieError}, DBValue, KeySpacedDB, MerkleValue, NodeCodec, PrefixedMemoryDB, Trie, TrieCache, TrieDBRawIterator, TrieRecorder, TrieRecorderProvider, @@ -55,7 +56,7 @@ macro_rules! format { }; } -type Result = sp_std::result::Result; +type Result = core::result::Result; /// Patricia trie-based storage trait. pub trait Storage: Send + Sync { @@ -553,7 +554,7 @@ where let map_e = |e| format!("Trie lookup error: {}", e); self.with_recorder_and_cache(None, |recorder, cache| { - read_trie_first_descedant_value::, _>(self, &self.root, key, recorder, cache) + read_trie_first_descendant_value::, _>(self, &self.root, key, recorder, cache) .map_err(map_e) }) } @@ -569,7 +570,7 @@ where let map_e = |e| format!("Trie lookup error: {}", e); self.with_recorder_and_cache(Some(child_root), |recorder, cache| { - read_child_trie_first_descedant_value::, _>( + read_child_trie_first_descendant_value::, _>( child_info.keyspace(), self, &child_root, diff --git a/substrate/primitives/statement-store/Cargo.toml b/substrate/primitives/statement-store/Cargo.toml index 652ab3ef13aa695f13c9b9c0a525b3f90dff8f0c..000fcd9870403872b1399466324eb18f728f2b41 100644 --- a/substrate/primitives/statement-store/Cargo.toml +++ b/substrate/primitives/statement-store/Cargo.toml @@ -21,7 +21,6 @@ scale-info = { version = "2.10.0", default-features = false, features = ["derive sp-core = { path = "../core", default-features = false } sp-crypto-hashing = { path = "../crypto/hashing", default-features = false } sp-runtime = { path = "../runtime", default-features = false } -sp-std = { path = "../std", default-features = false } sp-api = { path = "../api", default-features = false } sp-application-crypto = { path = "../application-crypto", default-features = false } sp-runtime-interface = { path = "../runtime-interface", default-features = false } @@ -57,7 +56,6 @@ std = [ "sp-externalities/std", "sp-runtime-interface/std", "sp-runtime/std", - "sp-std/std", "thiserror", "x25519-dalek", ] diff --git a/substrate/primitives/statement-store/src/ecies.rs b/substrate/primitives/statement-store/src/ecies.rs index 80a040fd4c8e03de91bdd851bb635d506c20208f..6fa16658e0071c78d99fd3b88f4bdbca6af551a6 100644 --- a/substrate/primitives/statement-store/src/ecies.rs +++ b/substrate/primitives/statement-store/src/ecies.rs @@ -148,7 +148,7 @@ mod test { #[test] fn basic_ed25519_encryption() { let (pair, _) = sp_core::ed25519::Pair::generate(); - let pk = pair.into(); + let pk = pair.public(); let plain_message = b"An important secret message"; let encrypted = encrypt_ed25519(&pk, plain_message).unwrap(); diff --git a/substrate/primitives/statement-store/src/lib.rs b/substrate/primitives/statement-store/src/lib.rs index 04175f6d6160e50985673338c7b187539570bdfa..dbac017ff6493e1da04251b33da9510d12eed078 100644 --- a/substrate/primitives/statement-store/src/lib.rs +++ b/substrate/primitives/statement-store/src/lib.rs @@ -20,13 +20,15 @@ //! A crate which contains statement-store primitives. +extern crate alloc; + +use alloc::vec::Vec; use codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_application_crypto::RuntimeAppPublic; #[cfg(feature = "std")] use sp_core::Pair; use sp_runtime_interface::pass_by::PassByCodec; -use sp_std::vec::Vec; /// Statement topic. pub type Topic = [u8; 32]; @@ -338,8 +340,8 @@ impl Statement { Some(Proof::OnChain { .. }) | None => SignatureVerificationResult::NoSignature, Some(Proof::Sr25519 { signature, signer }) => { let to_sign = self.signature_material(); - let signature = sp_core::sr25519::Signature(*signature); - let public = sp_core::sr25519::Public(*signer); + let signature = sp_core::sr25519::Signature::from(*signature); + let public = sp_core::sr25519::Public::from(*signer); if signature.verify(to_sign.as_slice(), &public) { SignatureVerificationResult::Valid(*signer) } else { @@ -348,8 +350,8 @@ impl Statement { }, Some(Proof::Ed25519 { signature, signer }) => { let to_sign = self.signature_material(); - let signature = sp_core::ed25519::Signature(*signature); - let public = sp_core::ed25519::Public(*signer); + let signature = sp_core::ed25519::Signature::from(*signature); + let public = sp_core::ed25519::Public::from(*signer); if signature.verify(to_sign.as_slice(), &public) { SignatureVerificationResult::Valid(*signer) } else { @@ -358,8 +360,8 @@ impl Statement { }, Some(Proof::Secp256k1Ecdsa { signature, signer }) => { let to_sign = self.signature_material(); - let signature = sp_core::ecdsa::Signature(*signature); - let public = sp_core::ecdsa::Public(*signer); + let signature = sp_core::ecdsa::Signature::from(*signature); + let public = sp_core::ecdsa::Public::from(*signer); if signature.verify(to_sign.as_slice(), &public) { let sender_hash = ::hash(signer); diff --git a/substrate/primitives/statement-store/src/runtime_api.rs b/substrate/primitives/statement-store/src/runtime_api.rs index 13f88bc977e9efc8f9be5f9fdcfdd8ac098c8de2..4d25576c801fa608aed18884bbe9b9ca7b5023de 100644 --- a/substrate/primitives/statement-store/src/runtime_api.rs +++ b/substrate/primitives/statement-store/src/runtime_api.rs @@ -18,11 +18,11 @@ //! Runtime support for the statement store. use crate::{Hash, Statement, Topic}; +use alloc::vec::Vec; use codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_runtime::RuntimeDebug; use sp_runtime_interface::{pass_by::PassByEnum, runtime_interface}; -use sp_std::vec::Vec; #[cfg(feature = "std")] use sp_externalities::ExternalitiesExt; diff --git a/substrate/primitives/storage/Cargo.toml b/substrate/primitives/storage/Cargo.toml index d3ade87ea47f848e8036ce0a4d374fa4d77184b2..acedc8d0004970fc10f5771ba4b4e2cb87a55198 100644 --- a/substrate/primitives/storage/Cargo.toml +++ b/substrate/primitives/storage/Cargo.toml @@ -22,7 +22,6 @@ impl-serde = { version = "0.4.0", optional = true, default-features = false } ref-cast = "1.0.0" serde = { features = ["alloc", "derive"], optional = true, workspace = true } sp-debug-derive = { path = "../debug-derive", default-features = false } -sp-std = { path = "../std", default-features = false } [features] default = ["std"] @@ -31,7 +30,6 @@ std = [ "impl-serde/std", "serde/std", "sp-debug-derive/std", - "sp-std/std", ] # Serde support without relying on std features. diff --git a/substrate/primitives/storage/src/lib.rs b/substrate/primitives/storage/src/lib.rs index 79c090cabf8dbd686f6b92e3770c1944ed1feb05..197994f574719ec0b000b8dbd79f333c71291708 100644 --- a/substrate/primitives/storage/src/lib.rs +++ b/substrate/primitives/storage/src/lib.rs @@ -19,18 +19,19 @@ #![cfg_attr(not(feature = "std"), no_std)] -use core::fmt::Display; +extern crate alloc; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use sp_debug_derive::RuntimeDebug; +use alloc::vec::Vec; use codec::{Decode, Encode}; -use ref_cast::RefCast; -use sp_std::{ +use core::{ + fmt::Display, ops::{Deref, DerefMut}, - vec::Vec, }; +use ref_cast::RefCast; /// Storage key. #[derive(PartialEq, Eq, RuntimeDebug)] @@ -49,9 +50,7 @@ impl AsRef<[u8]> for StorageKey { } /// Storage key with read/write tracking information. -#[derive( - PartialEq, Eq, Ord, PartialOrd, sp_std::hash::Hash, RuntimeDebug, Clone, Encode, Decode, -)] +#[derive(PartialEq, Eq, Ord, PartialOrd, core::hash::Hash, RuntimeDebug, Clone, Encode, Decode)] pub struct TrackedStorageKey { pub key: Vec, pub reads: u32, @@ -441,7 +440,7 @@ impl From for u8 { impl TryFrom for StateVersion { type Error = (); - fn try_from(val: u8) -> sp_std::result::Result { + fn try_from(val: u8) -> core::result::Result { match val { 0 => Ok(StateVersion::V0), 1 => Ok(StateVersion::V1), @@ -453,7 +452,7 @@ impl TryFrom for StateVersion { impl StateVersion { /// If defined, values in state of size bigger or equal /// to this threshold will use a separate trie node. - /// Otherwhise, value will be inlined in branch or leaf + /// Otherwise, value will be inlined in branch or leaf /// node. pub fn state_value_threshold(&self) -> Option { match self { diff --git a/substrate/primitives/test-primitives/Cargo.toml b/substrate/primitives/test-primitives/Cargo.toml index f310216dd58d737be7cde512d3fbdf27f4fbda25..1b51c5f591904335d547a052a7b035ea3f6cd108 100644 --- a/substrate/primitives/test-primitives/Cargo.toml +++ b/substrate/primitives/test-primitives/Cargo.toml @@ -21,7 +21,6 @@ serde = { features = ["derive"], optional = true, workspace = true } sp-application-crypto = { path = "../application-crypto", default-features = false } sp-core = { path = "../core", default-features = false } sp-runtime = { path = "../runtime", default-features = false } -sp-std = { path = "../std", default-features = false } [features] default = ["std"] @@ -32,7 +31,6 @@ std = [ "sp-application-crypto/std", "sp-core/std", "sp-runtime/std", - "sp-std/std", ] # Serde support without relying on std features. diff --git a/substrate/primitives/test-primitives/src/lib.rs b/substrate/primitives/test-primitives/src/lib.rs index 82bdb6967b842914799493c6143a2287c85919e7..1e3b912eaf48911a3d9ca2ce6d93dec917482071 100644 --- a/substrate/primitives/test-primitives/src/lib.rs +++ b/substrate/primitives/test-primitives/src/lib.rs @@ -19,14 +19,16 @@ #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + use codec::{Decode, Encode}; pub use sp_application_crypto; use sp_application_crypto::sr25519; +use alloc::vec::Vec; pub use sp_core::{hash::H256, RuntimeDebug}; use sp_runtime::traits::{BlakeTwo256, Extrinsic as ExtrinsicT, Verify}; -use sp_std::vec::Vec; /// Extrinsic for test-runtime. #[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, scale_info::TypeInfo)] diff --git a/substrate/primitives/timestamp/Cargo.toml b/substrate/primitives/timestamp/Cargo.toml index 9e2b802bfb15e2e89169923d2e6f4274d2cdf1f2..11afb1755908880fc63a52abb1a659609acddccd 100644 --- a/substrate/primitives/timestamp/Cargo.toml +++ b/substrate/primitives/timestamp/Cargo.toml @@ -21,7 +21,6 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = thiserror = { optional = true, workspace = true } sp-inherents = { path = "../inherents", default-features = false } sp-runtime = { path = "../runtime", default-features = false } -sp-std = { path = "../std", default-features = false } [features] default = ["std"] @@ -30,6 +29,5 @@ std = [ "codec/std", "sp-inherents/std", "sp-runtime/std", - "sp-std/std", "thiserror", ] diff --git a/substrate/primitives/tracing/Cargo.toml b/substrate/primitives/tracing/Cargo.toml index 58b1e48c4622c3b2ddd97072a7e2c3524110e613..368f8c096dd4a5f193521514e5a784ccc4122657 100644 --- a/substrate/primitives/tracing/Cargo.toml +++ b/substrate/primitives/tracing/Cargo.toml @@ -21,7 +21,6 @@ features = ["with-tracing"] targets = ["wasm32-unknown-unknown", "x86_64-unknown-linux-gnu"] [dependencies] -sp-std = { path = "../std", default-features = false } codec = { version = "3.6.1", package = "parity-scale-codec", default-features = false, features = [ "derive", ] } @@ -36,7 +35,6 @@ default = ["std"] with-tracing = ["codec/derive", "codec/full"] std = [ "codec/std", - "sp-std/std", "tracing-core/std", "tracing-subscriber", "tracing/std", diff --git a/substrate/primitives/tracing/src/lib.rs b/substrate/primitives/tracing/src/lib.rs index cc65183684667b404d88a2b61ef6105f176b6336..34ed088aed0b9d648f429b6558846332cbed35e3 100644 --- a/substrate/primitives/tracing/src/lib.rs +++ b/substrate/primitives/tracing/src/lib.rs @@ -17,7 +17,7 @@ //! Substrate tracing primitives and macros. //! -//! To trace functions or invidual code in Substrate, this crate provides [`within_span`] +//! To trace functions or individual code in Substrate, this crate provides [`within_span`] //! and [`enter_span`]. See the individual docs for how to use these macros. //! //! Note that to allow traces from wasm execution environment there are @@ -37,6 +37,8 @@ #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + #[cfg(feature = "std")] use tracing; pub use tracing::{ @@ -68,7 +70,7 @@ pub use crate::types::{WASM_NAME_KEY, WASM_TARGET_KEY, WASM_TRACE_IDENTIFIER}; /// directly as they yield nothing without the feature present. Instead you should use /// `enter_span!` and `within_span!` – which would strip away even any parameter conversion /// you do within the span-definition (and thus optimise your performance). For your -/// convineience you directly specify the `Level` and name of the span or use the full +/// convenience you directly specify the `Level` and name of the span or use the full /// feature set of `span!`/`span_*!` on it: /// /// # Example @@ -96,7 +98,7 @@ pub use crate::types::{WASM_NAME_KEY, WASM_TARGET_KEY, WASM_TRACE_IDENTIFIER}; /// This project only provides the macros and facilities to manage tracing /// it doesn't implement the tracing subscriber or backend directly – that is /// up to the developer integrating it into a specific environment. In native -/// this can and must be done through the regular `tracing`-facitilies, please +/// this can and must be done through the regular `tracing`-facilities, please /// see their documentation for details. /// /// On the wasm-side we've adopted a similar approach of having a global @@ -137,7 +139,7 @@ pub fn init_for_tests() { /// Runs given code within a tracing span, measuring it's execution time. /// /// If tracing is not enabled, the code is still executed. Pass in level and name or -/// use any valid `sp_tracing::Span`followe by `;` and the code to execute, +/// use any valid `sp_tracing::Span`followed by `;` and the code to execute, /// /// # Example /// diff --git a/substrate/primitives/tracing/src/types.rs b/substrate/primitives/tracing/src/types.rs index 003787f310d8c73637189189e58297b2bc5345e4..46f38383d98063469b9f889ee1debabb34ad8f15 100644 --- a/substrate/primitives/tracing/src/types.rs +++ b/substrate/primitives/tracing/src/types.rs @@ -15,11 +15,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +use alloc::{vec, vec::Vec}; use codec::{Decode, Encode}; -/// Types for wasm based tracing. Loosly inspired by `tracing-core` but +/// Types for wasm based tracing. Loosely inspired by `tracing-core` but /// optimised for the specific use case. use core::{fmt::Debug, format_args}; -use sp_std::{vec, vec::Vec, Writer}; /// The Tracing Level – the user can filter by this #[derive(Clone, Encode, Decode, Debug)] @@ -54,7 +54,7 @@ impl core::default::Default for WasmLevel { } } -/// A paramter value provided to the span/event +/// A parameter value provided to the span/event #[derive(Encode, Decode, Clone)] pub enum WasmValue { U8(u8), @@ -132,9 +132,9 @@ impl From for WasmValue { impl From> for WasmValue { fn from(inp: core::fmt::Arguments<'_>) -> WasmValue { - let mut buf = Writer::default(); + let mut buf = alloc::string::String::default(); core::fmt::write(&mut buf, inp).expect("Writing of arguments doesn't fail"); - WasmValue::Formatted(buf.into_inner()) + WasmValue::Formatted(buf.into_bytes()) } } @@ -180,9 +180,9 @@ impl From for WasmValue { } } -/// The name of a field provided as the argument name when contstructing an +/// The name of a field provided as the argument name when constructing an /// `event!` or `span!`. -/// Generally generated automaticaly via `stringify` from an `'static &str`. +/// Generally generated automatically via `stringify` from an `'static &str`. /// Likely print-able. #[derive(Encode, Decode, Clone)] pub struct WasmFieldName(Vec); @@ -320,7 +320,7 @@ impl tracing_core::field::Visit for WasmValuesSet { self.0.push((field.name().into(), Some(WasmValue::from(value)))) } } -/// Metadata provides generic information about the specifc location of the +/// Metadata provides generic information about the specific location of the /// `span!` or `event!` call on the wasm-side. #[derive(Encode, Decode, Clone)] pub struct WasmMetadata { diff --git a/substrate/primitives/transaction-pool/src/lib.rs b/substrate/primitives/transaction-pool/src/lib.rs index 431f429e29f9f12545375e799d753dd9c5864850..2ce735491b2c9e087147c01231c3feb59bcd743c 100644 --- a/substrate/primitives/transaction-pool/src/lib.rs +++ b/substrate/primitives/transaction-pool/src/lib.rs @@ -20,4 +20,6 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + pub mod runtime_api; diff --git a/substrate/primitives/transaction-storage-proof/Cargo.toml b/substrate/primitives/transaction-storage-proof/Cargo.toml index e2fb54dafdf1c97f5ebc0bb8662a213803f0901e..fbd0a4752fca988fbe2e3a18228ad3fb754993dd 100644 --- a/substrate/primitives/transaction-storage-proof/Cargo.toml +++ b/substrate/primitives/transaction-storage-proof/Cargo.toml @@ -22,7 +22,6 @@ scale-info = { version = "2.10.0", default-features = false, features = ["derive sp-core = { path = "../core", optional = true } sp-inherents = { path = "../inherents", default-features = false } sp-runtime = { path = "../runtime", default-features = false } -sp-std = { path = "../std", default-features = false } sp-trie = { path = "../trie", optional = true } [features] @@ -34,6 +33,5 @@ std = [ "sp-core/std", "sp-inherents/std", "sp-runtime/std", - "sp-std/std", "sp-trie/std", ] diff --git a/substrate/primitives/transaction-storage-proof/src/lib.rs b/substrate/primitives/transaction-storage-proof/src/lib.rs index 9d540ae68d163f3d9477c121ec616b7db1dd0c78..893b2e33bee6ca21bd900e485d482cb0acba3cc7 100644 --- a/substrate/primitives/transaction-storage-proof/src/lib.rs +++ b/substrate/primitives/transaction-storage-proof/src/lib.rs @@ -15,13 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Storage proof primitives. Constains types and basic code to extract storage +//! Storage proof primitives. Contains types and basic code to extract storage //! proofs for indexed transactions. #![cfg_attr(not(feature = "std"), no_std)] -use sp_std::{prelude::*, result::Result}; +extern crate alloc; +use core::result::Result; + +use alloc::vec::Vec; use codec::{Decode, Encode}; use sp_inherents::{InherentData, InherentIdentifier, IsFatalError}; use sp_runtime::traits::{Block as BlockT, NumberFor}; diff --git a/substrate/primitives/trie/Cargo.toml b/substrate/primitives/trie/Cargo.toml index 16d3ca19a179ce97bbc2c5c42023ceb54bf8b32b..dd7ab080e5f4d3cda170ec6f8c8c6be55b8a46b3 100644 --- a/substrate/primitives/trie/Cargo.toml +++ b/substrate/primitives/trie/Cargo.toml @@ -35,7 +35,6 @@ tracing = { version = "0.1.29", optional = true } trie-db = { version = "0.28.0", default-features = false } trie-root = { version = "0.18.0", default-features = false } sp-core = { path = "../core", default-features = false } -sp-std = { path = "../std", default-features = false } sp-externalities = { path = "../externalities", default-features = false } schnellru = { version = "0.2.1", optional = true } @@ -62,7 +61,6 @@ std = [ "sp-core/std", "sp-externalities/std", "sp-runtime/std", - "sp-std/std", "thiserror", "tracing", "trie-db/std", diff --git a/substrate/primitives/trie/src/cache/mod.rs b/substrate/primitives/trie/src/cache/mod.rs index 01f08a78adcf2fff3612b7dfe0aebc83b2b17658..32078169b50305ad08ecaf4b07679a792411dff0 100644 --- a/substrate/primitives/trie/src/cache/mod.rs +++ b/substrate/primitives/trie/src/cache/mod.rs @@ -323,7 +323,7 @@ type ValueAccessSet = /// /// This cache should be used per state instance created by the backend. One state instance is /// referring to the state of one block. It will cache all the accesses that are done to the state -/// which could not be fullfilled by the [`SharedTrieCache`]. These locally cached items are merged +/// which could not be fulfilled by the [`SharedTrieCache`]. These locally cached items are merged /// back to the shared trie cache when this instance is dropped. /// /// When using [`Self::as_trie_db_cache`] or [`Self::as_trie_db_mut_cache`], it will lock Mutexes. diff --git a/substrate/primitives/trie/src/error.rs b/substrate/primitives/trie/src/error.rs index 17be556d3489ab03856a66498ba69de165a1a191..e3986e50b08ef5e0565f09636bc4ab90489a551f 100644 --- a/substrate/primitives/trie/src/error.rs +++ b/substrate/primitives/trie/src/error.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use sp_std::{boxed::Box, vec::Vec}; +use alloc::{boxed::Box, vec::Vec}; /// Error type used for trie related errors. #[derive(Debug, PartialEq, Eq, Clone)] diff --git a/substrate/primitives/trie/src/lib.rs b/substrate/primitives/trie/src/lib.rs index fd1320b3fbcb1a6c0aa7c7ece504ec2da7c34c24..0ae448aff6e13db074665dbf7500c8a35e407af5 100644 --- a/substrate/primitives/trie/src/lib.rs +++ b/substrate/primitives/trie/src/lib.rs @@ -19,6 +19,8 @@ #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + #[cfg(feature = "std")] pub mod cache; mod error; @@ -33,6 +35,8 @@ mod trie_stream; #[cfg(feature = "std")] pub mod proof_size_extension; +use alloc::{borrow::Borrow, boxed::Box, vec, vec::Vec}; +use core::marker::PhantomData; /// Our `NodeCodec`-specific error. pub use error::Error; /// Various re-exports from the `hash-db` crate. @@ -42,7 +46,6 @@ use hash_db::{Hasher, Prefix}; pub use memory_db::{prefixed_key, HashKey, KeyFunction, PrefixedKey}; /// The Substrate format implementation of `NodeCodec`. pub use node_codec::NodeCodec; -use sp_std::{borrow::Borrow, boxed::Box, marker::PhantomData, vec::Vec}; pub use storage_proof::{CompactProof, StorageProof}; /// Trie codec reexport, mainly child trie support /// for trie compact proof. @@ -323,7 +326,7 @@ pub fn read_trie_value( +pub fn read_trie_first_descendant_value( db: &DB, root: &TrieHash, key: &[u8], @@ -444,7 +447,7 @@ where /// Read the [`trie_db::MerkleValue`] of the node that is the closest descendant for /// the provided child key. -pub fn read_child_trie_first_descedant_value( +pub fn read_child_trie_first_descendant_value( keyspace: &[u8], db: &DB, root: &TrieHash, @@ -500,7 +503,7 @@ pub struct KeySpacedDBMut<'a, DB: ?Sized, H>(&'a mut DB, &'a [u8], PhantomData (Vec, Option) { - let mut result = sp_std::vec![0; ks.len() + prefix.0.len()]; + let mut result = vec![0; ks.len() + prefix.0.len()]; result[..ks.len()].copy_from_slice(ks); result[ks.len()..].copy_from_slice(prefix.0); (result, prefix.1) diff --git a/substrate/primitives/trie/src/node_codec.rs b/substrate/primitives/trie/src/node_codec.rs index 46acde77c0543af88e6f3d62cbfb460fe88c117a..78896988ec4c6027c201aafd05e8d45a47b84157 100644 --- a/substrate/primitives/trie/src/node_codec.rs +++ b/substrate/primitives/trie/src/node_codec.rs @@ -19,9 +19,10 @@ use super::node_header::{NodeHeader, NodeKind}; use crate::{error::Error, trie_constants}; +use alloc::{borrow::Borrow, vec::Vec}; use codec::{Compact, Decode, Encode, Input}; +use core::{marker::PhantomData, ops::Range}; use hash_db::Hasher; -use sp_std::{borrow::Borrow, marker::PhantomData, ops::Range, vec::Vec}; use trie_db::{ nibble_ops, node::{NibbleSlicePlan, NodeHandlePlan, NodePlan, Value, ValuePlan}, @@ -30,7 +31,7 @@ use trie_db::{ /// Helper struct for trie node decoder. This implements `codec::Input` on a byte slice, while /// tracking the absolute position. This is similar to `std::io::Cursor` but does not implement -/// `Read` and `io` is not in `sp-std`. +/// `Read` and `io` are not in `core` or `alloc`. struct ByteSliceInput<'a> { data: &'a [u8], offset: usize, diff --git a/substrate/primitives/trie/src/storage_proof.rs b/substrate/primitives/trie/src/storage_proof.rs index 6c871d73b043a997820b9a5645e459d7739dfc48..e46c49be19cb84af46f63003abcdb56ca421d9d0 100644 --- a/substrate/primitives/trie/src/storage_proof.rs +++ b/substrate/primitives/trie/src/storage_proof.rs @@ -15,14 +15,12 @@ // See the License for the specific language governing permissions and // limitations under the License. +use alloc::{collections::btree_set::BTreeSet, vec::Vec}; use codec::{Decode, Encode}; +use core::iter::{DoubleEndedIterator, IntoIterator}; use hash_db::{HashDB, Hasher}; use scale_info::TypeInfo; -use sp_std::{ - collections::btree_set::BTreeSet, - iter::{DoubleEndedIterator, IntoIterator}, - vec::Vec, -}; + // Note that `LayoutV1` usage here (proof compaction) is compatible // with `LayoutV0`. use crate::LayoutV1 as Layout; diff --git a/substrate/primitives/trie/src/trie_codec.rs b/substrate/primitives/trie/src/trie_codec.rs index f29e009c4761e8503b57fde00bb2af6cb3b3330d..65b4f50535990222aa269eee44813eaae9d8b9b7 100644 --- a/substrate/primitives/trie/src/trie_codec.rs +++ b/substrate/primitives/trie/src/trie_codec.rs @@ -21,7 +21,7 @@ //! it to substrate specific layout and child trie system. use crate::{CompactProof, HashDBT, TrieConfiguration, TrieHash, EMPTY_PREFIX}; -use sp_std::{boxed::Box, vec::Vec}; +use alloc::{boxed::Box, vec::Vec}; use trie_db::{CError, Trie}; /// Error for trie node decoding. diff --git a/substrate/primitives/trie/src/trie_stream.rs b/substrate/primitives/trie/src/trie_stream.rs index f57b80f978ffb1484b2301fdb612cf6b805c4321..459b5895f207e927161820a984f92daca7e4cea5 100644 --- a/substrate/primitives/trie/src/trie_stream.rs +++ b/substrate/primitives/trie/src/trie_stream.rs @@ -21,9 +21,9 @@ use crate::{ node_header::{size_and_prefix_iterator, NodeKind}, trie_constants, }; +use alloc::vec::Vec; use codec::{Compact, Encode}; use hash_db::Hasher; -use sp_std::vec::Vec; use trie_root; /// Codec-flavored TrieStream. diff --git a/substrate/primitives/version/proc-macro/src/decl_runtime_version.rs b/substrate/primitives/version/proc-macro/src/decl_runtime_version.rs index 7ca2d9b71f60a8059b4b3004ef3c089f3d110002..3671d4aff6bb0d14dd100427340f366b92b5b3a1 100644 --- a/substrate/primitives/version/proc-macro/src/decl_runtime_version.rs +++ b/substrate/primitives/version/proc-macro/src/decl_runtime_version.rs @@ -52,7 +52,7 @@ fn decl_runtime_version_impl_inner(item: ItemConst) -> Result { /// enable `std` feature even for `no_std` wasm runtime builds. /// /// One difference from the original definition is the `apis` field. Since we don't actually parse -/// `apis` from this macro it will always be emitteed as empty. An empty vector can be encoded as +/// `apis` from this macro it will always be emitted as empty. An empty vector can be encoded as /// a zero-byte, thus `u8` is sufficient here. #[derive(Encode)] struct RuntimeVersion { diff --git a/substrate/primitives/version/src/lib.rs b/substrate/primitives/version/src/lib.rs index 9b14a809ac100de9e7a0655fdd7d5e55d23fbc0e..789c507742f779983b478ccd8bc9ff87a87d7165 100644 --- a/substrate/primitives/version/src/lib.rs +++ b/substrate/primitives/version/src/lib.rs @@ -327,7 +327,7 @@ impl RuntimeVersion { /// /// For runtime with core api version less than 4, /// V0 trie version will be applied to state. - /// Otherwhise, V1 trie version will be use. + /// Otherwise, V1 trie version will be use. pub fn state_version(&self) -> StateVersion { // If version > than 1, keep using latest version. self.state_version.try_into().unwrap_or(StateVersion::V1) diff --git a/substrate/primitives/wasm-interface/Cargo.toml b/substrate/primitives/wasm-interface/Cargo.toml index f7d1038903eab6954374e6dac141b74683315a25..c05cc05ff06de9c5d1a96c3f406323e8551f388b 100644 --- a/substrate/primitives/wasm-interface/Cargo.toml +++ b/substrate/primitives/wasm-interface/Cargo.toml @@ -21,10 +21,9 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = impl-trait-for-tuples = "0.2.2" log = { optional = true, workspace = true, default-features = true } wasmtime = { version = "8.0.1", default-features = false, optional = true } -anyhow = { version = "1.0.68", optional = true } -sp-std = { path = "../std", default-features = false } +anyhow = { version = "1.0.81", optional = true } [features] default = ["std"] -std = ["codec/std", "log/std", "sp-std/std", "wasmtime"] +std = ["codec/std", "log/std", "wasmtime"] wasmtime = ["anyhow", "dep:wasmtime"] diff --git a/substrate/primitives/wasm-interface/src/lib.rs b/substrate/primitives/wasm-interface/src/lib.rs index 9d5d2bb358d57926bedd69ce5b38132df8761151..4fc78ca15535ba942cbd4eb4cd59697290ffac68 100644 --- a/substrate/primitives/wasm-interface/src/lib.rs +++ b/substrate/primitives/wasm-interface/src/lib.rs @@ -19,7 +19,10 @@ #![cfg_attr(not(feature = "std"), no_std)] -use sp_std::{borrow::Cow, iter::Iterator, marker::PhantomData, mem, result, vec, vec::Vec}; +extern crate alloc; + +use alloc::{borrow::Cow, vec, vec::Vec}; +use core::{iter::Iterator, marker::PhantomData, mem, result}; #[cfg(not(all(feature = "std", feature = "wasmtime")))] #[macro_export] @@ -76,7 +79,7 @@ impl From for u8 { impl TryFrom for ValueType { type Error = (); - fn try_from(val: u8) -> sp_std::result::Result { + fn try_from(val: u8) -> core::result::Result { match val { 0 => Ok(Self::I32), 1 => Ok(Self::I64), diff --git a/substrate/primitives/weights/Cargo.toml b/substrate/primitives/weights/Cargo.toml index a7d61de001b5525af1a43148a6c7f6f284c4e51f..d3118defb58d77227507f1718d0d2896712d50a1 100644 --- a/substrate/primitives/weights/Cargo.toml +++ b/substrate/primitives/weights/Cargo.toml @@ -23,7 +23,6 @@ serde = { optional = true, features = ["alloc", "derive"], workspace = true } smallvec = "1.11.0" sp-arithmetic = { path = "../arithmetic", default-features = false } sp-debug-derive = { path = "../debug-derive", default-features = false } -sp-std = { path = "../std", default-features = false } schemars = { version = "0.8.3", default-features = false, optional = true } [features] @@ -35,7 +34,6 @@ std = [ "serde/std", "sp-arithmetic/std", "sp-debug-derive/std", - "sp-std/std", ] # By default some types have documentation, `full-metadata-docs` allows to add documentation to # more types in the metadata. diff --git a/substrate/primitives/weights/src/lib.rs b/substrate/primitives/weights/src/lib.rs index aede9473535a361325f64c177dd9f1861664ca40..b2c956266e2c3a346ca32c7b74ceb7f4c5cc2c0c 100644 --- a/substrate/primitives/weights/src/lib.rs +++ b/substrate/primitives/weights/src/lib.rs @@ -18,9 +18,6 @@ //! # Primitives for transaction weighting. #![cfg_attr(not(feature = "std"), no_std)] -// TODO remove once `OldWeight` is gone. I dont know why this is needed, maybe by one of the macros -// of `OldWeight`. -#![allow(deprecated)] extern crate self as sp_weights; @@ -28,7 +25,7 @@ mod weight_meter; mod weight_v2; use bounded_collections::Get; -use codec::{CompactAs, Decode, Encode, MaxEncodedLen}; +use codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -52,28 +49,6 @@ pub mod constants { pub const WEIGHT_PROOF_SIZE_PER_KB: u64 = 1024; } -/// The old weight type. -/// -/// NOTE: This type exists purely for compatibility purposes! Use [`weight_v2::Weight`] in all other -/// cases. -#[derive( - Decode, - Encode, - CompactAs, - PartialEq, - Eq, - Clone, - Copy, - RuntimeDebug, - Default, - MaxEncodedLen, - TypeInfo, -)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde", serde(transparent))] -#[deprecated(note = "Will be removed soon; use `Weight` instead.")] -pub struct OldWeight(pub u64); - /// The weight of database operations that the runtime can invoke. /// /// NOTE: This is currently only measured in computational time, and will probably diff --git a/substrate/primitives/weights/src/weight_meter.rs b/substrate/primitives/weights/src/weight_meter.rs index 584d22304c3ae33ece669bc69927a2568fb9b288..cfe8396ae6d67ed36d038f6afc2a7700c56b815a 100644 --- a/substrate/primitives/weights/src/weight_meter.rs +++ b/substrate/primitives/weights/src/weight_meter.rs @@ -118,13 +118,6 @@ impl WeightMeter { debug_assert!(self.consumed.all_lte(self.limit), "Weight counter overflow"); } - /// Consume the given weight after checking that it can be consumed and return `true`. Otherwise - /// do nothing and return `false`. - #[deprecated(note = "Use `try_consume` instead. Will be removed after December 2023.")] - pub fn check_accrue(&mut self, w: Weight) -> bool { - self.try_consume(w).is_ok() - } - /// Consume the given weight after checking that it can be consumed. /// /// Returns `Ok` if the weight can be consumed or otherwise an `Err`. @@ -139,16 +132,15 @@ impl WeightMeter { }) } - /// Check if the given weight can be consumed. - #[deprecated(note = "Use `can_consume` instead. Will be removed after December 2023.")] - pub fn can_accrue(&self, w: Weight) -> bool { - self.can_consume(w) - } - /// Check if the given weight can be consumed. pub fn can_consume(&self, w: Weight) -> bool { self.consumed.checked_add(&w).map_or(false, |t| t.all_lte(self.limit)) } + + /// Reclaim the given weight. + pub fn reclaim_proof_size(&mut self, s: u64) { + self.consumed.saturating_reduce(Weight::from_parts(0, s)); + } } #[cfg(test)] @@ -160,80 +152,80 @@ mod tests { fn weight_meter_remaining_works() { let mut meter = WeightMeter::with_limit(Weight::from_parts(10, 20)); - assert!(meter.check_accrue(Weight::from_parts(5, 0))); + assert_eq!(meter.try_consume(Weight::from_parts(5, 0)), Ok(())); assert_eq!(meter.consumed, Weight::from_parts(5, 0)); assert_eq!(meter.remaining(), Weight::from_parts(5, 20)); - assert!(meter.check_accrue(Weight::from_parts(2, 10))); + assert_eq!(meter.try_consume(Weight::from_parts(2, 10)), Ok(())); assert_eq!(meter.consumed, Weight::from_parts(7, 10)); assert_eq!(meter.remaining(), Weight::from_parts(3, 10)); - assert!(meter.check_accrue(Weight::from_parts(3, 10))); + assert_eq!(meter.try_consume(Weight::from_parts(3, 10)), Ok(())); assert_eq!(meter.consumed, Weight::from_parts(10, 20)); assert_eq!(meter.remaining(), Weight::from_parts(0, 0)); } #[test] - fn weight_meter_can_accrue_works() { + fn weight_meter_can_consume_works() { let meter = WeightMeter::with_limit(Weight::from_parts(1, 1)); - assert!(meter.can_accrue(Weight::from_parts(0, 0))); - assert!(meter.can_accrue(Weight::from_parts(1, 1))); - assert!(!meter.can_accrue(Weight::from_parts(0, 2))); - assert!(!meter.can_accrue(Weight::from_parts(2, 0))); - assert!(!meter.can_accrue(Weight::from_parts(2, 2))); + assert!(meter.can_consume(Weight::from_parts(0, 0))); + assert!(meter.can_consume(Weight::from_parts(1, 1))); + assert!(!meter.can_consume(Weight::from_parts(0, 2))); + assert!(!meter.can_consume(Weight::from_parts(2, 0))); + assert!(!meter.can_consume(Weight::from_parts(2, 2))); } #[test] - fn weight_meter_check_accrue_works() { + fn weight_meter_try_consume_works() { let mut meter = WeightMeter::with_limit(Weight::from_parts(2, 2)); - assert!(meter.check_accrue(Weight::from_parts(0, 0))); - assert!(meter.check_accrue(Weight::from_parts(1, 1))); - assert!(!meter.check_accrue(Weight::from_parts(0, 2))); - assert!(!meter.check_accrue(Weight::from_parts(2, 0))); - assert!(!meter.check_accrue(Weight::from_parts(2, 2))); - assert!(meter.check_accrue(Weight::from_parts(0, 1))); - assert!(meter.check_accrue(Weight::from_parts(1, 0))); + assert_eq!(meter.try_consume(Weight::from_parts(0, 0)), Ok(())); + assert_eq!(meter.try_consume(Weight::from_parts(1, 1)), Ok(())); + assert_eq!(meter.try_consume(Weight::from_parts(0, 2)), Err(())); + assert_eq!(meter.try_consume(Weight::from_parts(2, 0)), Err(())); + assert_eq!(meter.try_consume(Weight::from_parts(2, 2)), Err(())); + assert_eq!(meter.try_consume(Weight::from_parts(0, 1)), Ok(())); + assert_eq!(meter.try_consume(Weight::from_parts(1, 0)), Ok(())); } #[test] - fn weight_meter_check_and_can_accrue_works() { + fn weight_meter_check_and_can_consume_works() { let mut meter = WeightMeter::new(); - assert!(meter.can_accrue(Weight::from_parts(u64::MAX, 0))); - assert!(meter.check_accrue(Weight::from_parts(u64::MAX, 0))); + assert!(meter.can_consume(Weight::from_parts(u64::MAX, 0))); + assert_eq!(meter.try_consume(Weight::from_parts(u64::MAX, 0)), Ok(())); - assert!(meter.can_accrue(Weight::from_parts(0, u64::MAX))); - assert!(meter.check_accrue(Weight::from_parts(0, u64::MAX))); + assert!(meter.can_consume(Weight::from_parts(0, u64::MAX))); + assert_eq!(meter.try_consume(Weight::from_parts(0, u64::MAX)), Ok(())); - assert!(!meter.can_accrue(Weight::from_parts(0, 1))); - assert!(!meter.check_accrue(Weight::from_parts(0, 1))); + assert!(!meter.can_consume(Weight::from_parts(0, 1))); + assert_eq!(meter.try_consume(Weight::from_parts(0, 1)), Err(())); - assert!(!meter.can_accrue(Weight::from_parts(1, 0))); - assert!(!meter.check_accrue(Weight::from_parts(1, 0))); + assert!(!meter.can_consume(Weight::from_parts(1, 0))); + assert_eq!(meter.try_consume(Weight::from_parts(1, 0)), Err(())); - assert!(meter.can_accrue(Weight::zero())); - assert!(meter.check_accrue(Weight::zero())); + assert!(meter.can_consume(Weight::zero())); + assert_eq!(meter.try_consume(Weight::zero()), Ok(())); } #[test] fn consumed_ratio_works() { let mut meter = WeightMeter::with_limit(Weight::from_parts(10, 20)); - assert!(meter.check_accrue(Weight::from_parts(5, 0))); + assert_eq!(meter.try_consume(Weight::from_parts(5, 0)), Ok(())); assert_eq!(meter.consumed_ratio(), Perbill::from_percent(50)); - assert!(meter.check_accrue(Weight::from_parts(0, 12))); + assert_eq!(meter.try_consume(Weight::from_parts(0, 12)), Ok(())); assert_eq!(meter.consumed_ratio(), Perbill::from_percent(60)); - assert!(meter.check_accrue(Weight::from_parts(2, 0))); + assert_eq!(meter.try_consume(Weight::from_parts(2, 0)), Ok(())); assert_eq!(meter.consumed_ratio(), Perbill::from_percent(70)); - assert!(meter.check_accrue(Weight::from_parts(0, 4))); + assert_eq!(meter.try_consume(Weight::from_parts(0, 4)), Ok(())); assert_eq!(meter.consumed_ratio(), Perbill::from_percent(80)); - assert!(meter.check_accrue(Weight::from_parts(3, 0))); + assert_eq!(meter.try_consume(Weight::from_parts(3, 0)), Ok(())); assert_eq!(meter.consumed_ratio(), Perbill::from_percent(100)); - assert!(meter.check_accrue(Weight::from_parts(0, 4))); + assert_eq!(meter.try_consume(Weight::from_parts(0, 4)), Ok(())); assert_eq!(meter.consumed_ratio(), Perbill::from_percent(100)); } @@ -277,6 +269,21 @@ mod tests { assert_eq!(meter.consumed(), Weight::from_parts(5, 10)); } + #[test] + #[cfg(debug_assertions)] + fn reclaim_works() { + let mut meter = WeightMeter::with_limit(Weight::from_parts(5, 10)); + + meter.consume(Weight::from_parts(5, 10)); + assert_eq!(meter.consumed(), Weight::from_parts(5, 10)); + + meter.reclaim_proof_size(3); + assert_eq!(meter.consumed(), Weight::from_parts(5, 7)); + + meter.reclaim_proof_size(10); + assert_eq!(meter.consumed(), Weight::from_parts(5, 0)); + } + #[test] #[cfg(debug_assertions)] #[should_panic(expected = "Weight counter overflow")] diff --git a/substrate/scripts/ci/node-template-release/Cargo.toml b/substrate/scripts/ci/node-template-release/Cargo.toml index cc60d0a4510132c543162c3bcf9964e35644b24e..4327b6857433f40482b21a91e116479ba05da121 100644 --- a/substrate/scripts/ci/node-template-release/Cargo.toml +++ b/substrate/scripts/ci/node-template-release/Cargo.toml @@ -14,7 +14,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } flate2 = "1.0" fs_extra = "1.3" glob = "0.3" diff --git a/substrate/scripts/run_all_benchmarks.sh b/substrate/scripts/run_all_benchmarks.sh index 83848100a7e5189f312411fb42eae7d4eb6a43c4..6dd7cede319f974a074d951d34ec3c970f02c32f 100755 --- a/substrate/scripts/run_all_benchmarks.sh +++ b/substrate/scripts/run_all_benchmarks.sh @@ -59,11 +59,12 @@ done if [ "$skip_build" != true ] then echo "[+] Compiling Substrate benchmarks..." - cargo build --profile=production --locked --features=runtime-benchmarks --bin substrate + cargo build --profile=production --locked --features=runtime-benchmarks --bin substrate-node fi # The executable to use. -SUBSTRATE=./target/production/substrate +# Parent directory because of the monorepo structure. +SUBSTRATE=../target/production/substrate-node # Manually exclude some pallets. EXCLUDED_PALLETS=( @@ -80,11 +81,7 @@ EXCLUDED_PALLETS=( # Load all pallet names in an array. ALL_PALLETS=($( - $SUBSTRATE benchmark pallet --list --chain=dev |\ - tail -n+2 |\ - cut -d',' -f1 |\ - sort |\ - uniq + $SUBSTRATE benchmark pallet --list=pallets --no-csv-header --chain=dev )) # Filter out the excluded pallets by concatenating the arrays and discarding duplicates. diff --git a/substrate/test-utils/client/src/lib.rs b/substrate/test-utils/client/src/lib.rs index e3f06e27563581b100f489072d939b02d99a3247..d283b24f286aed743ba613f095f2cd319caba263 100644 --- a/substrate/test-utils/client/src/lib.rs +++ b/substrate/test-utils/client/src/lib.rs @@ -72,6 +72,7 @@ pub struct TestClientBuilder, bad_blocks: BadBlocks, enable_offchain_indexing_api: bool, + enable_import_proof_recording: bool, no_genesis: bool, } @@ -120,6 +121,7 @@ impl bad_blocks: None, enable_offchain_indexing_api: false, no_genesis: false, + enable_import_proof_recording: false, } } @@ -165,6 +167,12 @@ impl self } + /// Enable proof recording on import. + pub fn enable_import_proof_recording(mut self) -> Self { + self.enable_import_proof_recording = true; + self + } + /// Disable writing genesis. pub fn set_no_genesis(mut self) -> Self { self.no_genesis = true; @@ -202,6 +210,7 @@ impl }; let client_config = ClientConfig { + enable_import_proof_recording: self.enable_import_proof_recording, offchain_indexing_api: self.enable_offchain_indexing_api, no_genesis: self.no_genesis, ..Default::default() diff --git a/substrate/test-utils/runtime/Cargo.toml b/substrate/test-utils/runtime/Cargo.toml index 3bba5cd5bf0473f9f26bfc56619fc9ee8d098414..f49503da8ca50d8c648e63332c4423b203338639 100644 --- a/substrate/test-utils/runtime/Cargo.toml +++ b/substrate/test-utils/runtime/Cargo.toml @@ -24,11 +24,10 @@ sp-block-builder = { path = "../../primitives/block-builder", default-features = codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } sp-inherents = { path = "../../primitives/inherents", default-features = false } -sp-keyring = { path = "../../primitives/keyring", optional = true } +sp-keyring = { path = "../../primitives/keyring", default-features = false } sp-offchain = { path = "../../primitives/offchain", default-features = false } -sp-core = { path = "../../primitives/core", default-features = false } +sp-core = { path = "../../primitives/core", default-features = false, features = ["serde"] } sp-crypto-hashing = { path = "../../primitives/crypto/hashing", default-features = false } -sp-std = { path = "../../primitives/std", default-features = false } sp-io = { path = "../../primitives/io", default-features = false } frame-support = { path = "../../frame/support", default-features = false } sp-version = { path = "../../primitives/version", default-features = false } @@ -99,12 +98,11 @@ std = [ "sp-genesis-builder/std", "sp-inherents/std", "sp-io/std", - "sp-keyring", + "sp-keyring/std", "sp-offchain/std", "sp-runtime/std", "sp-session/std", "sp-state-machine/std", - "sp-std/std", "sp-tracing/std", "sp-transaction-pool/std", "sp-trie/std", diff --git a/substrate/test-utils/runtime/src/extrinsic.rs b/substrate/test-utils/runtime/src/extrinsic.rs index 05ffb7db5d5b95d6d6dbadf5f48da563c04a4c59..e355e5d099ad5a01c74512f79d722fdf968af02e 100644 --- a/substrate/test-utils/runtime/src/extrinsic.rs +++ b/substrate/test-utils/runtime/src/extrinsic.rs @@ -26,7 +26,6 @@ use frame_system::{CheckNonce, CheckWeight}; use sp_core::crypto::Pair as TraitPair; use sp_keyring::AccountKeyring; use sp_runtime::{transaction_validity::TransactionPriority, Perbill}; -use sp_std::prelude::*; /// Transfer used in test substrate pallet. Extrinsic is created and signed using this data. #[derive(Clone)] diff --git a/substrate/test-utils/runtime/src/genesismap.rs b/substrate/test-utils/runtime/src/genesismap.rs index 5ed9c8a645886f741611bc9fdc5d4eae01ae2704..9e972886b3771469284a7ee66e5f823623a75b07 100644 --- a/substrate/test-utils/runtime/src/genesismap.rs +++ b/substrate/test-utils/runtime/src/genesismap.rs @@ -124,7 +124,6 @@ impl GenesisStorageBuilder { .into_iter() .map(|x| (x.into(), 1)) .collect(), - epoch_config: Some(crate::TEST_RUNTIME_BABE_EPOCH_CONFIGURATION), ..Default::default() }, substrate_test: substrate_test_pallet::GenesisConfig { diff --git a/substrate/test-utils/runtime/src/lib.rs b/substrate/test-utils/runtime/src/lib.rs index 8bc6f72a82ec2d0690c24565e841931bada92f05..7148d2b2fc0f43aa01bbea3a7df5af5b01770629 100644 --- a/substrate/test-utils/runtime/src/lib.rs +++ b/substrate/test-utils/runtime/src/lib.rs @@ -19,6 +19,8 @@ #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + #[cfg(feature = "std")] pub mod extrinsic; #[cfg(feature = "std")] @@ -42,9 +44,10 @@ use frame_system::{ CheckNonce, CheckWeight, }; use scale_info::TypeInfo; -use sp_std::prelude::*; + +use alloc::boxed::Box; #[cfg(not(feature = "std"))] -use sp_std::vec; +use alloc::{vec, vec::Vec}; use sp_application_crypto::{ecdsa, ed25519, sr25519, RuntimeAppPublic}; use sp_core::{OpaqueMetadata, RuntimeDebug}; @@ -61,7 +64,7 @@ use sp_runtime::{ create_runtime_str, impl_opaque_keys, traits::{BlakeTwo256, Block as BlockT, DispatchInfoOf, NumberFor, Verify}, transaction_validity::{TransactionSource, TransactionValidity, TransactionValidityError}, - ApplyExtrinsicResult, Perbill, + ApplyExtrinsicResult, ExtrinsicInclusionMode, Perbill, }; #[cfg(any(feature = "std", test))] use sp_version::NativeVersion; @@ -265,7 +268,7 @@ impl sp_runtime::traits::SignedExtension for CheckSubstrateCall { fn additional_signed( &self, - ) -> sp_std::result::Result { + ) -> core::result::Result { Ok(()) } @@ -342,7 +345,7 @@ parameter_types! { .build_or_panic(); } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::pallet::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = RuntimeBlockWeights; @@ -440,7 +443,7 @@ fn code_using_trie() -> u64 { .to_vec(); let mut mdb = PrefixedMemoryDB::default(); - let mut root = sp_std::default::Default::default(); + let mut root = core::default::Default::default(); { let mut t = TrieDBMutBuilderV1::::new(&mut mdb, &mut root).build(); for (key, value) in &pairs { @@ -480,9 +483,9 @@ impl_runtime_apis! { Executive::execute_block(block); } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> ExtrinsicInclusionMode { log::trace!(target: LOG_TARGET, "initialize_block: {header:#?}"); - Executive::initialize_block(header); + Executive::initialize_block(header) } } @@ -494,7 +497,7 @@ impl_runtime_apis! { fn metadata_at_version(_version: u32) -> Option { unimplemented!() } - fn metadata_versions() -> sp_std::vec::Vec { + fn metadata_versions() -> alloc::vec::Vec { unimplemented!() } } @@ -1291,7 +1294,7 @@ mod tests { let r = Vec::::decode(&mut &r[..]).unwrap(); let json = String::from_utf8(r.into()).expect("returned value is json. qed."); - let expected = r#"{"system":{},"babe":{"authorities":[],"epochConfig":null},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#; + let expected = r#"{"system":{},"babe":{"authorities":[],"epochConfig":{"c":[1,4],"allowed_slots":"PrimaryAndSecondaryVRFSlots"}},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#; assert_eq!(expected.to_string(), json); } diff --git a/substrate/test-utils/runtime/src/substrate_test_pallet.rs b/substrate/test-utils/runtime/src/substrate_test_pallet.rs index ed1ad990472ba2053998f4d5ac4944ec91b4b7d7..8375a68c8ff2f8111c68ddb7e15c91ea7fcb314a 100644 --- a/substrate/test-utils/runtime/src/substrate_test_pallet.rs +++ b/substrate/test-utils/runtime/src/substrate_test_pallet.rs @@ -21,6 +21,7 @@ //! functioning runtime. Some calls are allowed to be submitted as unsigned extrinsics, however most //! of them requires signing. Refer to `pallet::Call` for further details. +use alloc::{vec, vec::Vec}; use frame_support::{pallet_prelude::*, storage}; use sp_core::sr25519::Public; use sp_runtime::{ @@ -29,7 +30,6 @@ use sp_runtime::{ InvalidTransaction, TransactionSource, TransactionValidity, ValidTransaction, }, }; -use sp_std::prelude::*; pub use self::pallet::*; @@ -59,7 +59,7 @@ pub mod pallet { pub struct GenesisConfig { pub authorities: Vec, #[serde(skip)] - pub _config: sp_std::marker::PhantomData, + pub _config: core::marker::PhantomData, } #[pallet::genesis_build] diff --git a/substrate/test-utils/runtime/transaction-pool/src/lib.rs b/substrate/test-utils/runtime/transaction-pool/src/lib.rs index 8c8345b06bd32e4efbe2f721ddebe2cc2f2f9c4e..5202e6e65154d62085a0656cc780cc39452e0ed9 100644 --- a/substrate/test-utils/runtime/transaction-pool/src/lib.rs +++ b/substrate/test-utils/runtime/transaction-pool/src/lib.rs @@ -81,6 +81,7 @@ pub struct ChainState { pub block_by_hash: HashMap, pub nonces: HashMap, pub invalid_hashes: HashSet, + pub priorities: HashMap, } /// Test Api for transaction pool. @@ -214,6 +215,22 @@ impl TestApi { self.chain.write().invalid_hashes.insert(Self::hash_and_length_inner(xts).0); } + /// Remove a transaction that was previously declared as invalid via `[Self::add_invalid]`. + /// + /// Next time transaction pool will try to validate this + /// extrinsic, api will succeed. + pub fn remove_invalid(&self, xts: &Extrinsic) { + self.chain.write().invalid_hashes.remove(&Self::hash_and_length_inner(xts).0); + } + + /// Set a transaction priority. + pub fn set_priority(&self, xts: &Extrinsic, priority: u64) { + self.chain + .write() + .priorities + .insert(Self::hash_and_length_inner(xts).0, priority); + } + /// Query validation requests received. pub fn validation_requests(&self) -> Vec { self.validation_requests.read().clone() @@ -300,8 +317,14 @@ impl ChainApi for TestApi { return ready(Ok(Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(0))))) } - let mut validity = - ValidTransaction { priority: 1, requires, provides, longevity: 64, propagate: true }; + let priority = self.chain.read().priorities.get(&self.hash_and_length(&uxt).0).cloned(); + let mut validity = ValidTransaction { + priority: priority.unwrap_or(1), + requires, + provides, + longevity: 64, + propagate: true, + }; (self.valid_modifier.read())(&mut validity); diff --git a/substrate/utils/fork-tree/src/lib.rs b/substrate/utils/fork-tree/src/lib.rs index cd175166b9cdf4129b4718592dc2277b8c78103f..ff86467c85d5445a66b3d86cc04f83fcf5a110c7 100644 --- a/substrate/utils/fork-tree/src/lib.rs +++ b/substrate/utils/fork-tree/src/lib.rs @@ -683,7 +683,7 @@ where node.data }); - // Retain only roots that are descendents of the finalized block (this + // Retain only roots that are descendants 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 @@ -1168,7 +1168,7 @@ mod test { Ok(Some(false)), ); - // finalizing "E" is not allowed since there are not finalized anchestors. + // finalizing "E" is not allowed since there are not finalized ancestors. assert_eq!( tree.finalizes_any_with_descendent_if(&"E", 15, &is_descendent_of, |c| c.effective == 10), @@ -1309,7 +1309,7 @@ mod test { fn map_works() { let (mut tree, _) = test_fork_tree(); - // Extend the single root fork-tree to also excercise the roots order during map. + // Extend the single root fork-tree to also exercise the roots order during map. let is_descendent_of = |_: &&str, _: &&str| -> Result { Ok(false) }; let is_root = tree.import("A1", 10, 1, &is_descendent_of).unwrap(); assert!(is_root); diff --git a/substrate/utils/frame/benchmarking-cli/Cargo.toml b/substrate/utils/frame/benchmarking-cli/Cargo.toml index a2db83052c54508b14e37805cbb4b004542c6f30..b7fbb24b1a09b1f36531e1ba497722de5db780ca 100644 --- a/substrate/utils/frame/benchmarking-cli/Cargo.toml +++ b/substrate/utils/frame/benchmarking-cli/Cargo.toml @@ -18,10 +18,10 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = "6.1" chrono = "0.4" -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.6.1" } comfy-table = { version = "7.1.0", default-features = false } -handlebars = "4.2.2" +handlebars = "5.1.0" Inflector = "0.11.4" itertools = "0.10.3" lazy_static = "1.4.0" diff --git a/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs b/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs index dce49db15f7d4764aeba5f2bd0e0d1ede65cd15b..80a5d27d8c26285f621912540a22e95682d44ccd 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::{writer, PalletCmd}; +use super::{writer, ListOutput, PalletCmd}; use codec::{Decode, Encode}; use frame_benchmarking::{ Analysis, BenchmarkBatch, BenchmarkBatchSplitResults, BenchmarkList, BenchmarkParameter, @@ -39,7 +39,13 @@ use sp_externalities::Extensions; use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; use sp_runtime::traits::Hash; use sp_state_machine::StateMachine; -use std::{collections::HashMap, fmt::Debug, fs, str::FromStr, time}; +use std::{ + collections::{BTreeMap, BTreeSet, HashMap}, + fmt::Debug, + fs, + str::FromStr, + time, +}; /// Logging target const LOG_TARGET: &'static str = "frame::benchmark::pallet"; @@ -191,6 +197,7 @@ impl PalletCmd { let spec = config.chain_spec; let pallet = self.pallet.clone().unwrap_or_default(); let pallet = pallet.as_bytes(); + let extrinsic = self.extrinsic.clone().unwrap_or_default(); let extrinsic_split: Vec<&str> = extrinsic.split(',').collect(); let extrinsics: Vec<_> = extrinsic_split.iter().map(|x| x.trim().as_bytes()).collect(); @@ -291,16 +298,23 @@ impl PalletCmd { // Convert `Vec` to `String` for better readability. let benchmarks_to_run: Vec<_> = benchmarks_to_run .into_iter() - .map(|b| { + .map(|(pallet, extrinsic, components, pov_modes)| { + let pallet_name = + String::from_utf8(pallet.clone()).expect("Encoded from String; qed"); + let extrinsic_name = + String::from_utf8(extrinsic.clone()).expect("Encoded from String; qed"); ( - b.0, - b.1, - b.2, - b.3.into_iter() + pallet, + extrinsic, + components, + pov_modes + .into_iter() .map(|(p, s)| { (String::from_utf8(p).unwrap(), String::from_utf8(s).unwrap()) }) .collect(), + pallet_name, + extrinsic_name, ) }) .collect(); @@ -309,9 +323,8 @@ impl PalletCmd { return Err("No benchmarks found which match your input.".into()) } - if self.list { - // List benchmarks instead of running them - list_benchmark(benchmarks_to_run); + if let Some(list_output) = self.list { + list_benchmark(benchmarks_to_run, list_output, self.no_csv_header); return Ok(()) } @@ -323,12 +336,12 @@ impl PalletCmd { let mut component_ranges = HashMap::<(Vec, Vec), Vec>::new(); let pov_modes = Self::parse_pov_modes(&benchmarks_to_run)?; - for (pallet, extrinsic, components, _) in benchmarks_to_run.clone() { + for (pallet, extrinsic, components, _, pallet_name, extrinsic_name) in + benchmarks_to_run.clone() + { log::info!( target: LOG_TARGET, - "Starting benchmark: {}::{}", - String::from_utf8(pallet.clone()).expect("Encoded from String; qed"), - String::from_utf8(extrinsic.clone()).expect("Encoded from String; qed"), + "Starting benchmark: {pallet_name}::{extrinsic_name}" ); let all_components = if components.is_empty() { vec![Default::default()] @@ -402,19 +415,14 @@ impl PalletCmd { .map_err(|e| { format!("Error executing and verifying runtime benchmark: {}", e) })?; - // Dont use these results since verification code will add overhead. + // Don't use these results since verification code will add overhead. let _batch = , String> as Decode>::decode( &mut &result[..], ) .map_err(|e| format!("Failed to decode benchmark results: {:?}", e))? .map_err(|e| { - format!( - "Benchmark {}::{} failed: {}", - String::from_utf8_lossy(&pallet), - String::from_utf8_lossy(&extrinsic), - e - ) + format!("Benchmark {pallet_name}::{extrinsic_name} failed: {e}",) })?; } // Do one loop of DB tracking. @@ -429,7 +437,7 @@ impl PalletCmd { &pallet.clone(), &extrinsic.clone(), &selected_components.clone(), - false, // dont run verification code for final values + false, // don't run verification code for final values self.repeat, ) .encode(), @@ -461,7 +469,7 @@ impl PalletCmd { &pallet.clone(), &extrinsic.clone(), &selected_components.clone(), - false, // dont run verification code for final values + false, // don't run verification code for final values self.repeat, ) .encode(), @@ -488,11 +496,7 @@ impl PalletCmd { log::info!( target: LOG_TARGET, - "Running benchmark: {}.{}({} args) {}/{} {}/{}", - String::from_utf8(pallet.clone()) - .expect("Encoded from String; qed"), - String::from_utf8(extrinsic.clone()) - .expect("Encoded from String; qed"), + "Running benchmark: {pallet_name}::{extrinsic_name}({} args) {}/{} {}/{}", components.len(), s + 1, // s starts at 0. all_components.len(), @@ -701,12 +705,14 @@ impl PalletCmd { Vec, Vec<(BenchmarkParameter, u32, u32)>, Vec<(String, String)>, + String, + String, )>, ) -> Result { use std::collections::hash_map::Entry; let mut parsed = PovModesMap::new(); - for (pallet, call, _components, pov_modes) in benchmarks { + for (pallet, call, _components, pov_modes, _, _) in benchmarks { for (pallet_storage, mode) in pov_modes { let mode = PovEstimationMode::from_str(&mode)?; let splits = pallet_storage.split("::").collect::>(); @@ -755,19 +761,45 @@ impl CliConfiguration for PalletCmd { /// List the benchmarks available in the runtime, in a CSV friendly format. fn list_benchmark( - mut benchmarks_to_run: Vec<( + benchmarks_to_run: Vec<( Vec, Vec, Vec<(BenchmarkParameter, u32, u32)>, Vec<(String, String)>, + String, + String, )>, + list_output: ListOutput, + no_csv_header: bool, ) { - // Sort and de-dub by pallet and function name. - benchmarks_to_run.sort_by(|(pa, sa, _, _), (pb, sb, _, _)| (pa, sa).cmp(&(pb, sb))); - benchmarks_to_run.dedup_by(|(pa, sa, _, _), (pb, sb, _, _)| (pa, sa) == (pb, sb)); + let mut benchmarks = BTreeMap::new(); - println!("pallet, benchmark"); - for (pallet, extrinsic, _, _) in benchmarks_to_run { - println!("{}, {}", String::from_utf8_lossy(&pallet), String::from_utf8_lossy(&extrinsic)); + // Sort and de-dub by pallet and function name. + benchmarks_to_run.iter().for_each(|(_, _, _, _, pallet_name, extrinsic_name)| { + benchmarks + .entry(pallet_name) + .or_insert_with(BTreeSet::new) + .insert(extrinsic_name); + }); + + match list_output { + ListOutput::All => { + if !no_csv_header { + println!("pallet,extrinsic"); + } + for (pallet, extrinsics) in benchmarks { + for extrinsic in extrinsics { + println!("{pallet},{extrinsic}"); + } + } + }, + ListOutput::Pallets => { + if !no_csv_header { + println!("pallet"); + }; + for pallet in benchmarks.keys() { + println!("{pallet}"); + } + }, } } diff --git a/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs b/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs index c69ce1765fc9dc490f1e0f40966ca07948b6b523..6dc56c0724eaffde1d70d25ef1161b29e01df0c4 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs @@ -19,6 +19,7 @@ mod command; mod writer; use crate::shared::HostInfoParams; +use clap::ValueEnum; use sc_cli::{ WasmExecutionMethod, WasmtimeInstantiationStrategy, DEFAULT_WASMTIME_INSTANTIATION_STRATEGY, DEFAULT_WASM_EXECUTION_METHOD, @@ -31,17 +32,32 @@ fn parse_pallet_name(pallet: &str) -> std::result::Result { Ok(pallet.replace("-", "_")) } +/// List options for available benchmarks. +#[derive(Debug, Clone, Copy, ValueEnum)] +pub enum ListOutput { + /// List all available pallets and extrinsics. + All, + /// List all available pallets only. + Pallets, +} + /// Benchmark the extrinsic weight of FRAME Pallets. #[derive(Debug, clap::Parser)] pub struct PalletCmd { /// Select a FRAME Pallet to benchmark, or `*` for all (in which case `extrinsic` must be `*`). - #[arg(short, long, value_parser = parse_pallet_name, required_unless_present_any = ["list", "json_input"])] + #[arg(short, long, value_parser = parse_pallet_name, required_unless_present_any = ["list", "json_input", "all"], default_value_if("all", "true", Some("*".into())))] pub pallet: Option, /// Select an extrinsic inside the pallet to benchmark, or `*` for all. - #[arg(short, long, required_unless_present_any = ["list", "json_input"])] + #[arg(short, long, required_unless_present_any = ["list", "json_input", "all"], default_value_if("all", "true", Some("*".into())))] pub extrinsic: Option, + /// Run benchmarks for all pallets and extrinsics. + /// + /// This is equivalent to running `--pallet * --extrinsic *`. + #[arg(long)] + pub all: bool, + /// Select how many samples we should take across the variable components. #[arg(short, long, default_value_t = 50)] pub steps: u32, @@ -158,11 +174,15 @@ pub struct PalletCmd { #[arg(long = "db-cache", value_name = "MiB", default_value_t = 1024)] pub database_cache_size: u32, - /// List the benchmarks that match your query rather than running them. + /// List and print available benchmarks in a csv-friendly format. /// - /// When nothing is provided, we list all benchmarks. - #[arg(long)] - pub list: bool, + /// NOTE: `num_args` and `require_equals` are required to allow `--list` + #[arg(long, value_enum, ignore_case = true, num_args = 0..=1, require_equals = true, default_missing_value("All"))] + pub list: Option, + + /// Don't include csv header when listing benchmarks. + #[arg(long, requires("list"))] + pub no_csv_header: bool, /// If enabled, the storage info is not displayed in the output next to the analysis. /// diff --git a/substrate/utils/frame/benchmarking-cli/src/pallet/writer.rs b/substrate/utils/frame/benchmarking-cli/src/pallet/writer.rs index 9493a693bbed321785bf0d23326d0f68b3efd534..bd4b65d8a2e378d543b54bebd8db4a1c3d378f98 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/writer.rs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/writer.rs @@ -153,8 +153,8 @@ fn map_results( continue } - let pallet_string = String::from_utf8(batch.pallet.clone()).unwrap(); - let instance_string = String::from_utf8(batch.instance.clone()).unwrap(); + let pallet_name = String::from_utf8(batch.pallet.clone()).unwrap(); + let instance_name = String::from_utf8(batch.instance.clone()).unwrap(); let benchmark_data = get_benchmark_data( batch, storage_info, @@ -166,7 +166,7 @@ fn map_results( worst_case_map_values, additional_trie_layers, ); - let pallet_benchmarks = all_benchmarks.entry((pallet_string, instance_string)).or_default(); + let pallet_benchmarks = all_benchmarks.entry((pallet_name, instance_name)).or_default(); pallet_benchmarks.push(benchmark_data); } Ok(all_benchmarks) @@ -571,19 +571,22 @@ pub(crate) fn process_storage_results( let mut prefix_result = result.clone(); let key_info = storage_info_map.get(&prefix); + let pallet_name = match key_info { + Some(k) => String::from_utf8(k.pallet_name.clone()).expect("encoded from string"), + None => "".to_string(), + }; + let storage_name = match key_info { + Some(k) => String::from_utf8(k.storage_name.clone()).expect("encoded from string"), + None => "".to_string(), + }; let max_size = key_info.and_then(|k| k.max_size); let override_pov_mode = match key_info { - Some(StorageInfo { pallet_name, storage_name, .. }) => { - let pallet_name = - String::from_utf8(pallet_name.clone()).expect("encoded from string"); - let storage_name = - String::from_utf8(storage_name.clone()).expect("encoded from string"); - + Some(_) => { // Is there an override for the storage key? - pov_modes.get(&(pallet_name.clone(), storage_name)).or( + pov_modes.get(&(pallet_name.clone(), storage_name.clone())).or( // .. or for the storage prefix? - pov_modes.get(&(pallet_name, "ALL".to_string())).or( + pov_modes.get(&(pallet_name.clone(), "ALL".to_string())).or( // .. or for the benchmark? pov_modes.get(&("ALL".to_string(), "ALL".to_string())), ), @@ -662,15 +665,10 @@ pub(crate) fn process_storage_results( // writes. if !is_prefix_identified { match key_info { - Some(key_info) => { + Some(_) => { let comment = format!( "Storage: `{}::{}` (r:{} w:{})", - String::from_utf8(key_info.pallet_name.clone()) - .expect("encoded from string"), - String::from_utf8(key_info.storage_name.clone()) - .expect("encoded from string"), - reads, - writes, + pallet_name, storage_name, reads, writes, ); comments.push(comment) }, @@ -698,11 +696,7 @@ pub(crate) fn process_storage_results( ) { Some(new_pov) => { let comment = format!( - "Proof: `{}::{}` (`max_values`: {:?}, `max_size`: {:?}, added: {}, mode: `{:?}`)", - String::from_utf8(key_info.pallet_name.clone()) - .expect("encoded from string"), - String::from_utf8(key_info.storage_name.clone()) - .expect("encoded from string"), + "Proof: `{pallet_name}::{storage_name}` (`max_values`: {:?}, `max_size`: {:?}, added: {}, mode: `{:?}`)", key_info.max_values, key_info.max_size, new_pov, @@ -711,13 +705,9 @@ pub(crate) fn process_storage_results( comments.push(comment) }, None => { - let pallet = String::from_utf8(key_info.pallet_name.clone()) - .expect("encoded from string"); - let item = String::from_utf8(key_info.storage_name.clone()) - .expect("encoded from string"); let comment = format!( "Proof: `{}::{}` (`max_values`: {:?}, `max_size`: {:?}, mode: `{:?}`)", - pallet, item, key_info.max_values, key_info.max_size, + pallet_name, storage_name, key_info.max_values, key_info.max_size, used_pov_mode, ); comments.push(comment); diff --git a/substrate/utils/frame/frame-utilities-cli/Cargo.toml b/substrate/utils/frame/frame-utilities-cli/Cargo.toml index 919016b2d8f09f4327ed34e4dc1a7d0fe9b3fe6b..3952c9fd219f495270c811f0f849eb4178a96a92 100644 --- a/substrate/utils/frame/frame-utilities-cli/Cargo.toml +++ b/substrate/utils/frame/frame-utilities-cli/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" workspace = true [dependencies] -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } frame-support = { path = "../../../frame/support" } frame-system = { path = "../../../frame/system" } sc-cli = { path = "../../../client/cli" } diff --git a/substrate/utils/frame/generate-bags/node-runtime/Cargo.toml b/substrate/utils/frame/generate-bags/node-runtime/Cargo.toml index 68d4733614c1521c285109aa7d8e3c8e9b8ea188..37d96d7e12b963b7777ac4f21e70e5c419515d37 100644 --- a/substrate/utils/frame/generate-bags/node-runtime/Cargo.toml +++ b/substrate/utils/frame/generate-bags/node-runtime/Cargo.toml @@ -17,4 +17,4 @@ kitchensink-runtime = { path = "../../../../bin/node/runtime" } generate-bags = { path = ".." } # third-party -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } diff --git a/substrate/utils/frame/generate-bags/src/lib.rs b/substrate/utils/frame/generate-bags/src/lib.rs index 923017261a44b3ce065dab99a55046ebfdcae594..62485c442d3654441765331f73d52951db014ee9 100644 --- a/substrate/utils/frame/generate-bags/src/lib.rs +++ b/substrate/utils/frame/generate-bags/src/lib.rs @@ -183,7 +183,7 @@ pub fn generate_thresholds( total_issuance: u128, minimum_balance: u128, ) -> Result<(), std::io::Error> { - // ensure the file is accessable + // ensure the file is accessible if let Some(parent) = output.parent() { if !parent.exists() { std::fs::create_dir_all(parent)?; diff --git a/substrate/utils/frame/remote-externalities/src/lib.rs b/substrate/utils/frame/remote-externalities/src/lib.rs index c7399468da9da7e9e0d692ec659db3076a2d5a79..254d33deec80d777eaba930e06cd087d9c4d26f1 100644 --- a/substrate/utils/frame/remote-externalities/src/lib.rs +++ b/substrate/utils/frame/remote-externalities/src/lib.rs @@ -762,7 +762,7 @@ where let mut sp = Spinner::with_timer(Spinners::Dots, "Inserting keys into DB...".into()); let start = Instant::now(); pending_ext.batch_insert(key_values.clone().into_iter().filter_map(|(k, v)| { - // Don't insert the child keys here, they need to be inserted seperately with all their + // Don't insert the child keys here, they need to be inserted separately with all their // data in the load_child_remote function. match is_default_child_storage_key(&k.0) { true => None, diff --git a/substrate/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs b/substrate/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs index f45258ea593d74c417bcb301c8ca755f0d396945..c0333bb7dac0f19ed30adc108845f88c918c900f 100644 --- a/substrate/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs +++ b/substrate/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs @@ -132,7 +132,7 @@ pub trait StateMigrationApi { /// Check current migration state. /// /// This call is performed locally without submitting any transactions. Thus executing this - /// won't change any state. Nonetheless it is a VERY costy call that should be + /// won't change any state. Nonetheless it is a VERY costly call that should be /// only exposed to trusted peers. #[method(name = "state_trieMigrationStatus")] fn call(&self, at: Option) -> RpcResult; diff --git a/substrate/utils/frame/rpc/support/src/lib.rs b/substrate/utils/frame/rpc/support/src/lib.rs index 8280c46aadf2629c1f2b1efccfa98d2c7a5f5bdf..a839bbc34021382e8acb77f92bbf120fe21310c5 100644 --- a/substrate/utils/frame/rpc/support/src/lib.rs +++ b/substrate/utils/frame/rpc/support/src/lib.rs @@ -49,7 +49,7 @@ use sp_storage::{StorageData, StorageKey}; /// # /// # type Hash = sp_core::H256; /// # -/// # #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +/// # #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] /// # impl frame_system::Config for TestRuntime { /// # type BaseCallFilter = (); /// # type BlockWeights = (); diff --git a/substrate/utils/frame/try-runtime/cli/Cargo.toml b/substrate/utils/frame/try-runtime/cli/Cargo.toml index d123fc5f62dc5848dc560f1eca37523435ebb331..3ff57745a2dbea89e0b0e01ac3f24e32ac8126b7 100644 --- a/substrate/utils/frame/try-runtime/cli/Cargo.toml +++ b/substrate/utils/frame/try-runtime/cli/Cargo.toml @@ -38,7 +38,7 @@ frame-try-runtime = { path = "../../../../frame/try-runtime", optional = true } substrate-rpc-client = { path = "../../rpc/client" } async-trait = "0.1.74" -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } hex = { version = "0.4.3", default-features = false } log = { workspace = true, default-features = true } parity-scale-codec = "3.6.1" diff --git a/substrate/utils/substrate-bip39/Cargo.toml b/substrate/utils/substrate-bip39/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..a46f81ee24d96d666495ee5cea5ca58b415a9cb0 --- /dev/null +++ b/substrate/utils/substrate-bip39/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "substrate-bip39" +version = "0.4.7" +license = "Apache-2.0" +description = "Converting BIP39 entropy to valid Substrate (sr25519) SecretKeys" +documentation = "https://docs.rs/substrate-bip39" +authors.workspace = true +edition.workspace = true +repository.workspace = true + +[dependencies] +hmac = "0.12.1" +pbkdf2 = { version = "0.12.2", default-features = false } +schnorrkel = { version = "0.11.4", default-features = false } +sha2 = { version = "0.10.7", default-features = false } +zeroize = { version = "1.4.3", default-features = false } + +[dev-dependencies] +bip39 = "2.0.0" +rustc-hex = "2.1.0" + +[features] +default = ["std"] +std = [ + "hmac/std", + "pbkdf2/std", + "schnorrkel/std", + "sha2/std", + "zeroize/alloc", + "zeroize/std", +] diff --git a/substrate/utils/substrate-bip39/README.md b/substrate/utils/substrate-bip39/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e7a80ca5f2cf8c4924cfb2629ce6e3ad97c295cc --- /dev/null +++ b/substrate/utils/substrate-bip39/README.md @@ -0,0 +1,55 @@ +# Substrate BIP39 + +This is a crate for deriving secret keys for Ristretto compressed Ed25519 (should be compatible with Ed25519 at this +time) from BIP39 phrases. + +## Why? + +The natural approach here would be to use the 64-byte seed generated from the BIP39 phrase, and use that to construct +the key. This approach, while reasonable and fairly straight forward to implement, also means we would have to inherit +all the characteristics of seed generation. Since we are breaking compatibility with both BIP32 and BIP44 anyway (which +we are free to do as we are no longer using the Secp256k1 curve), there is also no reason why we should adhere to BIP39 +seed generation from the mnemonic. + +BIP39 seed generation was designed to be compatible with user supplied brain wallet phrases as well as being extensible +to wallets providing their own dictionaries and checksum mechanism. Issues with those two points: + +1. Brain wallets are a horrible idea, simply because humans are bad entropy generators. It's next to impossible to + educate users on how to use that feature in a secure manner. The 2048 rounds of PBKDF2 is a mere inconvenience that + offers no real protection against dictionary attacks for anyone equipped with modern consumer hardware. Brain wallets + have given users false sense of security. _People have lost money_ this way and wallet providers today tend to stick + to CSPRNG supplied dictionary phrases. + +2. Providing own dictionaries felt into the _you ain't gonna need it_ anti-pattern category on day 1. Wallet providers + (be it hardware or software) typically want their products to be compatible with other wallets so that users can + migrate to their product without having to migrate all their assets. + +To achieve the above phrases have to be precisely encoded in _The One True Canonical Encoding_, for which UTF-8 NFKD was +chosen. This is largely irrelevant (and even ignored) for English phrases, as they encode to basically just ASCII in +virtually every character encoding known to mankind, but immediately becomes a problem for dictionaries that do use +non-ASCII characters. Even if the right encoding is used and implemented correctly, there are still [other caveats +present for some non-english dictionaries](https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md), +such as normalizing spaces to a canonical form, or making some latin based characters equivalent to their base in +dictionary lookups (eg. Spanish `ñ` and `n` are meant to be interchangeable). Thinking about all of this gives me a +headache, and opens doors for disagreements between buggy implementations, breaking compatibility. + +BIP39 does already provide a form of the mnemonic that is free from all of these issues: the entropy byte array. Since +verifying the checksum requires that we recover the entropy from which the phrase was generated, no extra work is +actually needed here. Wallet implementors can encode the dictionaries in whatever encoding they find convenient (as +long as they are the standard BIP39 dictionaries), no harm in using UTF-16 string primitives that Java and JavaScript +provide. Since the dictionary is fixed and known, and the checksum is done on the entropy itself, the exact character +encoding used becomes irrelevant, as are the precise codepoints and amount of whitespace around the words. It is thus +much harder to create a buggy implementation. + +PBKDF2 was kept in place, along with the password. Using 24 words (with its 256 bits entropy) makes the extra hashing +redundant (if you could brute force 256 bit entropy, you can also just brute force secret keys), however some users +might be still using 12 word phrases from other applications. There is no good reason to prohibit users from recovering +their old wallets using 12 words that I can see, in which case the extra hashing does provide _some_ protection. +Passwords are also a feature that some power users find useful - particularly for creating a decoy address with a small +balance with empty password, while the funds proper are stored on an address that requires a password to be entered. + +## Why not ditch BIP39 altogether? + +Because there are hardware wallets that use a single phrase for the entire device, and operate multiple accounts on +multiple networks using that. A completely different wordlist would make their life much harder when it comes to +providing future Substrate support. diff --git a/substrate/utils/substrate-bip39/src/lib.rs b/substrate/utils/substrate-bip39/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..5b68bef0c390e364f4c87cbfe0d1b7723b1f54f0 --- /dev/null +++ b/substrate/utils/substrate-bip39/src/lib.rs @@ -0,0 +1,232 @@ +// 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. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(not(feature = "std"))] +extern crate alloc; +#[cfg(not(feature = "std"))] +use alloc::string::String; + +use hmac::Hmac; +use pbkdf2::pbkdf2; +use schnorrkel::keys::MiniSecretKey; +use sha2::Sha512; +use zeroize::Zeroize; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum Error { + InvalidEntropy, +} + +/// `entropy` should be a byte array from a correctly recovered and checksumed BIP39. +/// +/// This function accepts slices of different length for different word lengths: +/// +/// + 16 bytes for 12 words. +/// + 20 bytes for 15 words. +/// + 24 bytes for 18 words. +/// + 28 bytes for 21 words. +/// + 32 bytes for 24 words. +/// +/// Any other length will return an error. +/// +/// `password` is analog to BIP39 seed generation itself, with an empty string being default. +pub fn mini_secret_from_entropy(entropy: &[u8], password: &str) -> Result { + let seed = seed_from_entropy(entropy, password)?; + Ok(MiniSecretKey::from_bytes(&seed[..32]).expect("Length is always correct; qed")) +} + +/// Similar to `mini_secret_from_entropy`, except that it provides the 64-byte seed directly. +pub fn seed_from_entropy(entropy: &[u8], password: &str) -> Result<[u8; 64], Error> { + if entropy.len() < 16 || entropy.len() > 32 || entropy.len() % 4 != 0 { + return Err(Error::InvalidEntropy); + } + + let mut salt = String::with_capacity(8 + password.len()); + salt.push_str("mnemonic"); + salt.push_str(password); + + let mut seed = [0u8; 64]; + + pbkdf2::>(entropy, salt.as_bytes(), 2048, &mut seed) + .map_err(|_| Error::InvalidEntropy)?; + + salt.zeroize(); + + Ok(seed) +} + +#[cfg(test)] +mod test { + use super::*; + + #[cfg(not(feature = "std"))] + use alloc::vec::Vec; + + use bip39::{Language, Mnemonic}; + use rustc_hex::FromHex; + + // phrase, entropy, seed, expanded secret_key + // + // ALL SEEDS GENERATED USING "Substrate" PASSWORD! + static VECTORS: &[[&str; 3]] = &[ + [ + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", + "00000000000000000000000000000000", + "44e9d125f037ac1d51f0a7d3649689d422c2af8b1ec8e00d71db4d7bf6d127e33f50c3d5c84fa3e5399c72d6cbbbbc4a49bf76f76d952f479d74655a2ef2d453", + ], + [ + "legal winner thank year wave sausage worth useful legal winner thank yellow", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "4313249608fe8ac10fd5886c92c4579007272cb77c21551ee5b8d60b780416850f1e26c1f4b8d88ece681cb058ab66d6182bc2ce5a03181f7b74c27576b5c8bf", + ], + [ + "letter advice cage absurd amount doctor acoustic avoid letter advice cage above", + "80808080808080808080808080808080", + "27f3eb595928c60d5bc91a4d747da40ed236328183046892ed6cd5aa9ae38122acd1183adf09a89839acb1e6eaa7fb563cc958a3f9161248d5a036e0d0af533d", + ], + [ + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", + "ffffffffffffffffffffffffffffffff", + "227d6256fd4f9ccaf06c45eaa4b2345969640462bbb00c5f51f43cb43418c7a753265f9b1e0c0822c155a9cabc769413ecc14553e135fe140fc50b6722c6b9df", + ], + [ + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent", + "000000000000000000000000000000000000000000000000", + "44e9d125f037ac1d51f0a7d3649689d422c2af8b1ec8e00d71db4d7bf6d127e33f50c3d5c84fa3e5399c72d6cbbbbc4a49bf76f76d952f479d74655a2ef2d453", + ], + [ + "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "cb1d50e14101024a88905a098feb1553d4306d072d7460e167a60ccb3439a6817a0afc59060f45d999ddebc05308714733c9e1e84f30feccddd4ad6f95c8a445", + ], + [ + "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always", + "808080808080808080808080808080808080808080808080", + "9ddecf32ce6bee77f867f3c4bb842d1f0151826a145cb4489598fe71ac29e3551b724f01052d1bc3f6d9514d6df6aa6d0291cfdf997a5afdb7b6a614c88ab36a", + ], + [ + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when", + "ffffffffffffffffffffffffffffffffffffffffffffffff", + "8971cb290e7117c64b63379c97ed3b5c6da488841bd9f95cdc2a5651ac89571e2c64d391d46e2475e8b043911885457cd23e99a28b5a18535fe53294dc8e1693", + ], + [ + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art", + "0000000000000000000000000000000000000000000000000000000000000000", + "44e9d125f037ac1d51f0a7d3649689d422c2af8b1ec8e00d71db4d7bf6d127e33f50c3d5c84fa3e5399c72d6cbbbbc4a49bf76f76d952f479d74655a2ef2d453", + ], + [ + "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "3037276a5d05fcd7edf51869eb841bdde27c574dae01ac8cfb1ea476f6bea6ef57ab9afe14aea1df8a48f97ae25b37d7c8326e49289efb25af92ba5a25d09ed3", + ], + [ + "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless", + "8080808080808080808080808080808080808080808080808080808080808080", + "2c9c6144a06ae5a855453d98c3dea470e2a8ffb78179c2e9eb15208ccca7d831c97ddafe844ab933131e6eb895f675ede2f4e39837bb5769d4e2bc11df58ac42", + ], + [ + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "047e89ef7739cbfe30da0ad32eb1720d8f62441dd4f139b981b8e2d0bd412ed4eb14b89b5098c49db2301d4e7df4e89c21e53f345138e56a5e7d63fae21c5939", + ], + [ + "ozone drill grab fiber curtain grace pudding thank cruise elder eight picnic", + "9e885d952ad362caeb4efe34a8e91bd2", + "f4956be6960bc145cdab782e649a5056598fd07cd3f32ceb73421c3da27833241324dc2c8b0a4d847eee457e6d4c5429f5e625ece22abaa6a976e82f1ec5531d", + ], + [ + "gravity machine north sort system female filter attitude volume fold club stay feature office ecology stable narrow fog", + "6610b25967cdcca9d59875f5cb50b0ea75433311869e930b", + "fbcc5229ade0c0ff018cb7a329c5459f91876e4dde2a97ddf03c832eab7f26124366a543f1485479c31a9db0d421bda82d7e1fe562e57f3533cb1733b001d84d", + ], + [ + "hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy gospel tennis maple dilemma loan word shrug inflict delay length", + "68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c", + "7c60c555126c297deddddd59f8cdcdc9e3608944455824dd604897984b5cc369cad749803bb36eb8b786b570c9cdc8db275dbe841486676a6adf389f3be3f076", + ], + [ + "scheme spot photo card baby mountain device kick cradle pact join borrow", + "c0ba5a8e914111210f2bd131f3d5e08d", + "c12157bf2506526c4bd1b79a056453b071361538e9e2c19c28ba2cfa39b5f23034b974e0164a1e8acd30f5b4c4de7d424fdb52c0116bfc6a965ba8205e6cc121", + ], + [ + "horn tenant knee talent sponsor spell gate clip pulse soap slush warm silver nephew swap uncle crack brave", + "6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3", + "23766723e970e6b79dec4d5e4fdd627fd27d1ee026eb898feb9f653af01ad22080c6f306d1061656d01c4fe9a14c05f991d2c7d8af8730780de4f94cd99bd819", + ], + [ + "panda eyebrow bullet gorilla call smoke muffin taste mesh discover soft ostrich alcohol speed nation flash devote level hobby quick inner drive ghost inside", + "9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863", + "f4c83c86617cb014d35cd87d38b5ef1c5d5c3d58a73ab779114438a7b358f457e0462c92bddab5a406fe0e6b97c71905cf19f925f356bc673ceb0e49792f4340", + ], + [ + "cat swing flag economy stadium alone churn speed unique patch report train", + "23db8160a31d3e0dca3688ed941adbf3", + "719d4d4de0638a1705bf5237262458983da76933e718b2d64eb592c470f3c5d222e345cc795337bb3da393b94375ff4a56cfcd68d5ea25b577ee9384d35f4246", + ], + [ + "light rule cinnamon wrap drastic word pride squirrel upgrade then income fatal apart sustain crack supply proud access", + "8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0", + "7ae1291db32d16457c248567f2b101e62c5549d2a64cd2b7605d503ec876d58707a8d663641e99663bc4f6cc9746f4852e75e7e54de5bc1bd3c299c9a113409e", + ], + [ + "all hour make first leader extend hole alien behind guard gospel lava path output census museum junior mass reopen famous sing advance salt reform", + "066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad", + "a911a5f4db0940b17ecb79c4dcf9392bf47dd18acaebdd4ef48799909ebb49672947cc15f4ef7e8ef47103a1a91a6732b821bda2c667e5b1d491c54788c69391", + ], + [ + "vessel ladder alter error federal sibling chat ability sun glass valve picture", + "f30f8c1da665478f49b001d94c5fc452", + "4e2314ca7d9eebac6fe5a05a5a8d3546bc891785414d82207ac987926380411e559c885190d641ff7e686ace8c57db6f6e4333c1081e3d88d7141a74cf339c8f", + ], + [ + "scissors invite lock maple supreme raw rapid void congress muscle digital elegant little brisk hair mango congress clump", + "c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05", + "7a83851102849edc5d2a3ca9d8044d0d4f00e5c4a292753ed3952e40808593251b0af1dd3c9ed9932d46e8608eb0b928216a6160bd4fc775a6e6fbd493d7c6b2", + ], + [ + "void come effort suffer camp survey warrior heavy shoot primary clutch crush open amazing screen patrol group space point ten exist slush involve unfold", + "f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f", + "938ba18c3f521f19bd4a399c8425b02c716844325b1a65106b9d1593fbafe5e0b85448f523f91c48e331995ff24ae406757cff47d11f240847352b348ff436ed", + ] + ]; + + #[test] + fn vectors_are_correct() { + for vector in VECTORS { + let phrase = vector[0]; + + let expected_entropy: Vec = vector[1].from_hex().unwrap(); + let expected_seed: Vec = vector[2].from_hex().unwrap(); + + let mnemonic = Mnemonic::parse_in(Language::English, phrase).unwrap(); + let seed = seed_from_entropy(&mnemonic.to_entropy(), "Substrate").unwrap(); + let secret = mini_secret_from_entropy(&mnemonic.to_entropy(), "Substrate") + .unwrap() + .to_bytes(); + + assert_eq!( + mnemonic.to_entropy(), + &expected_entropy[..], + "Entropy is incorrect for {}", + phrase + ); + assert_eq!(&seed[..], &expected_seed[..], "Seed is incorrect for {}", phrase); + assert_eq!(&secret[..], &expected_seed[..32], "Secret is incorrect for {}", phrase); + } + } +} diff --git a/substrate/utils/wasm-builder/src/lib.rs b/substrate/utils/wasm-builder/src/lib.rs index 5cde48c0950b341cd36f3a5594620be683c799ea..178e499e8f5bb6d7531d73004e2f37e1e9b1d146 100644 --- a/substrate/utils/wasm-builder/src/lib.rs +++ b/substrate/utils/wasm-builder/src/lib.rs @@ -207,7 +207,7 @@ fn get_cargo_command(target: RuntimeTarget) -> CargoCommand { } else { // If no command before provided us with a cargo that supports our Substrate wasm env, we // try to search one with rustup. If that fails as well, we return the default cargo and let - // the prequisities check fail. + // the perquisites check fail. get_rustup_command(target).unwrap_or(default_cargo) } } diff --git a/substrate/utils/wasm-builder/src/prerequisites.rs b/substrate/utils/wasm-builder/src/prerequisites.rs index a601e3210dd0c2b89beffa1d5d6b788cb35fd6a3..22caf8950637960310c62eb98c5358de55c1662d 100644 --- a/substrate/utils/wasm-builder/src/prerequisites.rs +++ b/substrate/utils/wasm-builder/src/prerequisites.rs @@ -149,6 +149,14 @@ impl<'a> DummyCrate<'a> { sysroot_cmd.output().ok().and_then(|o| String::from_utf8(o.stdout).ok()) } + fn get_toolchain(&self) -> Option { + let sysroot = self.get_sysroot()?; + Path::new(sysroot.trim()) + .file_name() + .and_then(|s| s.to_str()) + .map(|s| s.to_string()) + } + fn try_build(&self) -> Result<(), Option> { let Ok(result) = self.prepare_command("build").output() else { return Err(None) }; if !result.status.success() { @@ -164,14 +172,15 @@ fn check_wasm_toolchain_installed( let dummy_crate = DummyCrate::new(&cargo_command, RuntimeTarget::Wasm); if let Err(error) = dummy_crate.try_build() { + let toolchain = dummy_crate.get_toolchain().unwrap_or("".to_string()); let basic_error_message = colorize_error_message( - "Rust WASM toolchain is not properly installed; please install it!", + &format!("Rust WASM target for toolchain {toolchain} is not properly installed; please install it!") ); return match error { None => Err(basic_error_message), Some(error) if error.contains("the `wasm32-unknown-unknown` target may not be installed") => { - Err(colorize_error_message("Cannot compile the WASM runtime: the `wasm32-unknown-unknown` target is not installed!\n\ - You can install it with `rustup target add wasm32-unknown-unknown` if you're using `rustup`.")) + Err(colorize_error_message(&format!("Cannot compile the WASM runtime: the `wasm32-unknown-unknown` target is not installed!\n\ + You can install it with `rustup target add wasm32-unknown-unknown --toolchain {toolchain}` if you're using `rustup`."))) }, // Apparently this can happen when we're running on a non Tier 1 platform. Some(ref error) if error.contains("linker `rust-lld` not found") => @@ -193,9 +202,10 @@ fn check_wasm_toolchain_installed( let src_path = Path::new(sysroot.trim()).join("lib").join("rustlib").join("src").join("rust"); if !src_path.exists() { + let toolchain = dummy_crate.get_toolchain().unwrap_or("".to_string()); return Err(colorize_error_message( - "Cannot compile the WASM runtime: no standard library sources found!\n\ - You can install them with `rustup component add rust-src` if you're using `rustup`.", + &format!("Cannot compile the WASM runtime: no standard library sources found at {}!\n\ + You can install them with `rustup component add rust-src --toolchain {toolchain}` if you're using `rustup`.", src_path.display()), )) } } diff --git a/substrate/utils/wasm-builder/src/wasm_project.rs b/substrate/utils/wasm-builder/src/wasm_project.rs index bc8c9b3b4b3999da9be5e1fb18a8c73444c1867b..b58e6bfa36b478b91995789a148b042d167d9f3f 100644 --- a/substrate/utils/wasm-builder/src/wasm_project.rs +++ b/substrate/utils/wasm-builder/src/wasm_project.rs @@ -695,7 +695,7 @@ impl BuildConfiguration { /// "production". It would only contain the builtin profile where the custom profile /// inherits from. This is why we inspect the build path to learn which profile is used. /// - /// When not overriden by a env variable we always default to building wasm with the `Release` + /// When not overridden by a env variable we always default to building wasm with the `Release` /// profile even when the main build uses the debug build. This is because wasm built with the /// `Debug` profile is too slow for normal development activities and almost never intended. /// @@ -704,9 +704,9 @@ impl BuildConfiguration { /// /// # Note /// - /// Can be overriden by setting [`crate::WASM_BUILD_TYPE_ENV`]. + /// Can be overridden by setting [`crate::WASM_BUILD_TYPE_ENV`]. fn detect(target: RuntimeTarget, wasm_project: &Path) -> Self { - let (name, overriden) = if let Ok(name) = env::var(crate::WASM_BUILD_TYPE_ENV) { + let (name, overridden) = if let Ok(name) = env::var(crate::WASM_BUILD_TYPE_ENV) { (name, true) } else { // First go backwards to the beginning of the target directory. @@ -731,14 +731,14 @@ impl BuildConfiguration { (name, false) }; let outer_build_profile = Profile::iter().find(|p| p.directory() == name); - let blob_build_profile = match (outer_build_profile.clone(), overriden) { - // When not overriden by a env variable we default to using the `Release` profile + let blob_build_profile = match (outer_build_profile.clone(), overridden) { + // When not overridden by a env variable we default to using the `Release` profile // for the wasm build even when the main build uses the debug build. This // is because the `Debug` profile is too slow for normal development activities. (Some(Profile::Debug), false) => Profile::Release, - // For any other profile or when overriden we take it at face value. + // For any other profile or when overridden we take it at face value. (Some(profile), _) => profile, - // For non overriden unknown profiles we fall back to `Release`. + // For non overridden unknown profiles we fall back to `Release`. // This allows us to continue building when a custom profile is used for the // main builds cargo. When explicitly passing a profile via env variable we are // not doing a fallback. @@ -855,7 +855,7 @@ fn build_bloaty_blob( println!("{} {}", colorize_info_message("Using rustc version:"), cargo_cmd.rustc_version()); // Use `process::exit(1)` to have a clean error output. - if build_cmd.status().map(|s| s.success()).is_err() { + if !build_cmd.status().map_or(false, |s| s.success()) { process::exit(1); } @@ -877,7 +877,7 @@ fn build_bloaty_blob( if polkavm_path .metadata() .map(|polkavm_metadata| { - polkavm_metadata.modified().unwrap() >= elf_metadata.modified().unwrap() + polkavm_metadata.modified().unwrap() < elf_metadata.modified().unwrap() }) .unwrap_or(true) { diff --git a/substrate/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl b/substrate/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl index ea0f15e5d8ace1315e231c6db2e01399e0ce44f3..b68bce508c008396d699e67e08b98cdf74ba5138 100644 --- a/substrate/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl +++ b/substrate/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl @@ -34,8 +34,8 @@ bob: reports block height is at least {{DB_BLOCK_HEIGHT}} within 10 seconds alice: reports substrate_beefy_best_block is at least {{DB_BLOCK_HEIGHT}} within 180 seconds bob: reports substrate_beefy_best_block is at least {{DB_BLOCK_HEIGHT}} within 180 seconds -alice: reports substrate_beefy_best_block is greater than {{DB_BLOCK_HEIGHT}} within 60 seconds -bob: reports substrate_beefy_best_block is greater than {{DB_BLOCK_HEIGHT}} within 60 seconds +alice: reports substrate_beefy_best_block is greater than {{DB_BLOCK_HEIGHT}} within 180 seconds +bob: reports substrate_beefy_best_block is greater than {{DB_BLOCK_HEIGHT}} within 180 seconds alice: count of log lines containing "error" is 0 within 10 seconds bob: count of log lines containing "verification failed" is 0 within 10 seconds diff --git a/templates/minimal/README.md b/templates/minimal/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/templates/minimal/node/Cargo.toml b/templates/minimal/node/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..41d9708ea607aaca6fd88587709e650cc91a6ab9 --- /dev/null +++ b/templates/minimal/node/Cargo.toml @@ -0,0 +1,62 @@ +[package] +name = "minimal-template-node" +description = "A minimal Substrate-based Substrate node, ready for hacking." +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true +publish = false +build = "build.rs" + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +clap = { version = "4.5.3", features = ["derive"] } +futures = { version = "0.3.21", features = ["thread-pool"] } +futures-timer = "3.0.1" +jsonrpsee = { version = "0.22", features = ["server"] } +serde_json = { workspace = true, default-features = true } + +sc-cli = { path = "../../../substrate/client/cli" } +sc-executor = { path = "../../../substrate/client/executor" } +sc-network = { path = "../../../substrate/client/network" } +sc-service = { path = "../../../substrate/client/service" } +sc-telemetry = { path = "../../../substrate/client/telemetry" } +sc-transaction-pool = { path = "../../../substrate/client/transaction-pool" } +sc-transaction-pool-api = { path = "../../../substrate/client/transaction-pool/api" } +sc-consensus = { path = "../../../substrate/client/consensus/common" } +sc-consensus-manual-seal = { path = "../../../substrate/client/consensus/manual-seal" } +sc-rpc-api = { path = "../../../substrate/client/rpc-api" } +sc-basic-authorship = { path = "../../../substrate/client/basic-authorship" } +sc-offchain = { path = "../../../substrate/client/offchain" } +sc-client-api = { path = "../../../substrate/client/api" } + +sp-timestamp = { path = "../../../substrate/primitives/timestamp" } +sp-keyring = { path = "../../../substrate/primitives/keyring" } +sp-api = { path = "../../../substrate/primitives/api" } +sp-blockchain = { path = "../../../substrate/primitives/blockchain" } +sp-block-builder = { path = "../../../substrate/primitives/block-builder" } +sp-io = { path = "../../../substrate/primitives/io" } +sp-runtime = { path = "../../../substrate/primitives/runtime" } + +substrate-frame-rpc-system = { path = "../../../substrate/utils/frame/rpc/system" } + +# Once the native runtime is gone, there should be little to no dependency on FRAME here, and +# certainly no dependency on the runtime. +frame = { path = "../../../substrate/frame", features = [ + "experimental", + "runtime", +] } +runtime = { package = "minimal-template-runtime", path = "../runtime" } + +[build-dependencies] +substrate-build-script-utils = { path = "../../../substrate/utils/build-script-utils" } + +[features] +default = [] diff --git a/substrate/bin/minimal/node/build.rs b/templates/minimal/node/build.rs similarity index 100% rename from substrate/bin/minimal/node/build.rs rename to templates/minimal/node/build.rs diff --git a/substrate/bin/minimal/node/src/chain_spec.rs b/templates/minimal/node/src/chain_spec.rs similarity index 100% rename from substrate/bin/minimal/node/src/chain_spec.rs rename to templates/minimal/node/src/chain_spec.rs diff --git a/substrate/bin/minimal/node/src/cli.rs b/templates/minimal/node/src/cli.rs similarity index 100% rename from substrate/bin/minimal/node/src/cli.rs rename to templates/minimal/node/src/cli.rs diff --git a/substrate/bin/minimal/node/src/command.rs b/templates/minimal/node/src/command.rs similarity index 100% rename from substrate/bin/minimal/node/src/command.rs rename to templates/minimal/node/src/command.rs diff --git a/substrate/bin/minimal/node/src/lib.rs b/templates/minimal/node/src/lib.rs similarity index 94% rename from substrate/bin/minimal/node/src/lib.rs rename to templates/minimal/node/src/lib.rs index c2065def736aeeb3b1102bade04063532128f3cd..cb8ed3bd209dd24d1bfa9d53b904d411acbea02e 100644 --- a/substrate/bin/minimal/node/src/lib.rs +++ b/templates/minimal/node/src/lib.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Polkadot Sdk. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/substrate/bin/minimal/node/src/main.rs b/templates/minimal/node/src/main.rs similarity index 100% rename from substrate/bin/minimal/node/src/main.rs rename to templates/minimal/node/src/main.rs diff --git a/substrate/bin/minimal/node/src/rpc.rs b/templates/minimal/node/src/rpc.rs similarity index 100% rename from substrate/bin/minimal/node/src/rpc.rs rename to templates/minimal/node/src/rpc.rs diff --git a/substrate/bin/minimal/node/src/service.rs b/templates/minimal/node/src/service.rs similarity index 100% rename from substrate/bin/minimal/node/src/service.rs rename to templates/minimal/node/src/service.rs diff --git a/templates/minimal/pallets/template/Cargo.toml b/templates/minimal/pallets/template/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..9982e5ea53bc3eff28fb0331cfb14ad41a15eec7 --- /dev/null +++ b/templates/minimal/pallets/template/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "pallet-minimal-template" +description = "A minimal pallet built with FRAME, part of Polkadot Sdk." +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true +publish = false + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", features = [ + "derive", +], default-features = false } +scale-info = { version = "2.10.0", default-features = false, features = [ + "derive", +] } +frame = { path = "../../../../substrate/frame", default-features = false, features = [ + "experimental", + "runtime", +] } + + +[features] +default = ["std"] +std = ["codec/std", "frame/std", "scale-info/std"] diff --git a/templates/minimal/pallets/template/src/lib.rs b/templates/minimal/pallets/template/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..713f014bbe61fc9fa7df5019c00afddebf02bbc6 --- /dev/null +++ b/templates/minimal/pallets/template/src/lib.rs @@ -0,0 +1,19 @@ +//! A shell pallet built with [`frame`]. + +#![cfg_attr(not(feature = "std"), no_std)] + +use frame::prelude::*; + +// Re-export all pallet parts, this is needed to properly import the pallet into the runtime. +pub use pallet::*; + +#[frame::pallet] +pub mod pallet { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); +} diff --git a/templates/minimal/runtime/Cargo.toml b/templates/minimal/runtime/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..a99a1e43f85fcaf3e4d2b7122cfca701c6b8b4e4 --- /dev/null +++ b/templates/minimal/runtime/Cargo.toml @@ -0,0 +1,59 @@ +[package] +name = "minimal-template-runtime" +description = "A solochain runtime template built with Substrate, part of Polkadot Sdk." +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +parity-scale-codec = { version = "3.0.0", default-features = false } +scale-info = { version = "2.6.0", default-features = false } + +# this is a frame-based runtime, thus importing `frame` with runtime feature enabled. +frame = { path = "../../../substrate/frame", default-features = false, features = [ + "experimental", + "runtime", +] } + +# pallets that we want to use +pallet-balances = { path = "../../../substrate/frame/balances", default-features = false } +pallet-sudo = { path = "../../../substrate/frame/sudo", default-features = false } +pallet-timestamp = { path = "../../../substrate/frame/timestamp", default-features = false } +pallet-transaction-payment = { path = "../../../substrate/frame/transaction-payment", default-features = false } +pallet-transaction-payment-rpc-runtime-api = { path = "../../../substrate/frame/transaction-payment/rpc/runtime-api", default-features = false } + +# genesis builder that allows us to interact with runtime genesis config +sp-genesis-builder = { path = "../../../substrate/primitives/genesis-builder", default-features = false } + +# local pallet templates +pallet-minimal-template = { path = "../pallets/template", default-features = false } + +[build-dependencies] +substrate-wasm-builder = { path = "../../../substrate/utils/wasm-builder", optional = true } + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "scale-info/std", + + "frame/std", + + "pallet-balances/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + + "pallet-minimal-template/std", + + "sp-genesis-builder/std", + "substrate-wasm-builder", +] diff --git a/substrate/bin/minimal/runtime/build.rs b/templates/minimal/runtime/build.rs similarity index 100% rename from substrate/bin/minimal/runtime/build.rs rename to templates/minimal/runtime/build.rs diff --git a/substrate/bin/minimal/runtime/src/lib.rs b/templates/minimal/runtime/src/lib.rs similarity index 92% rename from substrate/bin/minimal/runtime/src/lib.rs rename to templates/minimal/runtime/src/lib.rs index 610289693d91b778295b3e48569fc887f805a204..00fcaf1cec76a3aecefa64bc7da3a0a74df2f06e 100644 --- a/substrate/bin/minimal/runtime/src/lib.rs +++ b/templates/minimal/runtime/src/lib.rs @@ -22,21 +22,24 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); use frame::{ - deps::frame_support::weights::{FixedFee, NoFee}, + deps::frame_support::{ + genesis_builder_helper::{build_config, create_default_config}, + weights::{FixedFee, NoFee}, + }, prelude::*, runtime::{ apis::{ - self, impl_runtime_apis, ApplyExtrinsicResult, CheckInherentsResult, OpaqueMetadata, + self, impl_runtime_apis, ApplyExtrinsicResult, CheckInherentsResult, + ExtrinsicInclusionMode, OpaqueMetadata, }, prelude::*, }, }; -use frame_support::genesis_builder_helper::{build_config, create_default_config}; #[runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("minimal-runtime"), - impl_name: create_runtime_str!("minimal-runtime"), + spec_name: create_runtime_str!("minimal-template-runtime"), + impl_name: create_runtime_str!("minimal-template-runtime"), authoring_version: 1, spec_version: 0, impl_version: 1, @@ -70,6 +73,9 @@ construct_runtime!( Balances: pallet_balances, Sudo: pallet_sudo, TransactionPayment: pallet_transaction_payment, + + // our local pallet + Template: pallet_minimal_template, } ); @@ -77,7 +83,7 @@ parameter_types! { pub const Version: RuntimeVersion = VERSION; } -#[derive_impl(frame_system::config_preludes::SolochainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)] impl frame_system::Config for Runtime { type Block = Block; type Version = Version; @@ -85,24 +91,26 @@ impl frame_system::Config for Runtime { type AccountData = pallet_balances::AccountData<::Balance>; } -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for Runtime { type AccountStore = System; } -#[derive_impl(pallet_sudo::config_preludes::TestDefaultConfig as pallet_sudo::DefaultConfig)] +#[derive_impl(pallet_sudo::config_preludes::TestDefaultConfig)] impl pallet_sudo::Config for Runtime {} -#[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig as pallet_timestamp::DefaultConfig)] +#[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)] impl pallet_timestamp::Config for Runtime {} -#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig as pallet_transaction_payment::DefaultConfig)] +#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)] impl pallet_transaction_payment::Config for Runtime { type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; type WeightToFee = NoFee<::Balance>; type LengthToFee = FixedFee<1, ::Balance>; } +impl pallet_minimal_template::Config for Runtime {} + type Block = frame::runtime::types_common::BlockOf; type Header = HeaderFor; @@ -121,7 +129,7 @@ impl_runtime_apis! { RuntimeExecutive::execute_block(block) } - fn initialize_block(header: &Header) { + fn initialize_block(header: &Header) -> ExtrinsicInclusionMode { RuntimeExecutive::initialize_block(header) } } diff --git a/cumulus/parachain-template/LICENSE b/templates/parachain/LICENSE similarity index 100% rename from cumulus/parachain-template/LICENSE rename to templates/parachain/LICENSE diff --git a/cumulus/parachain-template/README.md b/templates/parachain/README.md similarity index 100% rename from cumulus/parachain-template/README.md rename to templates/parachain/README.md diff --git a/cumulus/parachain-template/node/Cargo.toml b/templates/parachain/node/Cargo.toml similarity index 77% rename from cumulus/parachain-template/node/Cargo.toml rename to templates/parachain/node/Cargo.toml index 0ef678c4cbaeafc7a603748aec54ce2129a95f54..63267acdbca83fb7035b51bd100a1fd13ee2d8e7 100644 --- a/cumulus/parachain-template/node/Cargo.toml +++ b/templates/parachain/node/Cargo.toml @@ -1,20 +1,23 @@ [package] name = "parachain-template-node" -version = "0.1.0" -authors = ["Anonymous"] -description = "A new Cumulus FRAME-based Substrate Node, ready for hacking together a parachain." -license = "Unlicense" -homepage = "https://substrate.io" +description = "A parachain node template built with Substrate and Cumulus, part of Polkadot Sdk." +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true repository.workspace = true edition.workspace = true -build = "build.rs" publish = false +build = "build.rs" [lints] workspace = true +# [[bin]] +# name = "parachain-template-node" + [dependencies] -clap = { version = "4.5.1", features = ["derive"] } +clap = { version = "4.5.3", features = ["derive"] } log = { workspace = true, default-features = true } codec = { package = "parity-scale-codec", version = "3.0.0" } serde = { features = ["derive"], workspace = true, default-features = true } @@ -63,15 +66,15 @@ polkadot-primitives = { path = "../../../polkadot/primitives" } xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false } # Cumulus -cumulus-client-cli = { path = "../../client/cli" } -cumulus-client-collator = { path = "../../client/collator" } -cumulus-client-consensus-aura = { path = "../../client/consensus/aura" } -cumulus-client-consensus-common = { path = "../../client/consensus/common" } -cumulus-client-consensus-proposer = { path = "../../client/consensus/proposer" } -cumulus-client-service = { path = "../../client/service" } -cumulus-primitives-core = { path = "../../primitives/core" } -cumulus-primitives-parachain-inherent = { path = "../../primitives/parachain-inherent" } -cumulus-relay-chain-interface = { path = "../../client/relay-chain-interface" } +cumulus-client-cli = { path = "../../../cumulus/client/cli" } +cumulus-client-collator = { path = "../../../cumulus/client/collator" } +cumulus-client-consensus-aura = { path = "../../../cumulus/client/consensus/aura" } +cumulus-client-consensus-common = { path = "../../../cumulus/client/consensus/common" } +cumulus-client-consensus-proposer = { path = "../../../cumulus/client/consensus/proposer" } +cumulus-client-service = { path = "../../../cumulus/client/service" } +cumulus-primitives-core = { path = "../../../cumulus/primitives/core" } +cumulus-primitives-parachain-inherent = { path = "../../../cumulus/primitives/parachain-inherent" } +cumulus-relay-chain-interface = { path = "../../../cumulus/client/relay-chain-interface" } color-print = "0.3.4" [build-dependencies] diff --git a/cumulus/parachain-template/node/build.rs b/templates/parachain/node/build.rs similarity index 100% rename from cumulus/parachain-template/node/build.rs rename to templates/parachain/node/build.rs diff --git a/cumulus/parachain-template/node/src/chain_spec.rs b/templates/parachain/node/src/chain_spec.rs similarity index 93% rename from cumulus/parachain-template/node/src/chain_spec.rs rename to templates/parachain/node/src/chain_spec.rs index a79c78699c07102aca27ed06c73f93ccf86ed3ce..16c91865cdb4aa9ae3c5882652100f5ef56a988c 100644 --- a/cumulus/parachain-template/node/src/chain_spec.rs +++ b/templates/parachain/node/src/chain_spec.rs @@ -1,5 +1,6 @@ use cumulus_primitives_core::ParaId; -use parachain_template_runtime::{AccountId, AuraId, Signature, EXISTENTIAL_DEPOSIT}; +use parachain_template_runtime as runtime; +use runtime::{AccountId, AuraId, Signature, EXISTENTIAL_DEPOSIT}; use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}; use sc_service::ChainType; use serde::{Deserialize, Serialize}; @@ -56,8 +57,8 @@ where /// Generate the session keys from individual elements. /// /// The input must be a tuple of individual keys (a single arg for now since we have just one key). -pub fn template_session_keys(keys: AuraId) -> parachain_template_runtime::SessionKeys { - parachain_template_runtime::SessionKeys { aura: keys } +pub fn template_session_keys(keys: AuraId) -> runtime::SessionKeys { + runtime::SessionKeys { aura: keys } } pub fn development_config() -> ChainSpec { @@ -68,8 +69,7 @@ pub fn development_config() -> ChainSpec { properties.insert("ss58Format".into(), 42.into()); ChainSpec::builder( - parachain_template_runtime::WASM_BINARY - .expect("WASM binary was not built, please build it!"), + runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "rococo-local".into(), // You MUST set this to the correct network! @@ -120,8 +120,7 @@ pub fn local_testnet_config() -> ChainSpec { #[allow(deprecated)] ChainSpec::builder( - parachain_template_runtime::WASM_BINARY - .expect("WASM binary was not built, please build it!"), + runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "rococo-local".into(), // You MUST set this to the correct network! diff --git a/cumulus/parachain-template/node/src/cli.rs b/templates/parachain/node/src/cli.rs similarity index 99% rename from cumulus/parachain-template/node/src/cli.rs rename to templates/parachain/node/src/cli.rs index 73ef996b7504114b3578604a8d2c37661c9261fc..a5c55b8118a89d3dede620099a7447e3ab5dda6d 100644 --- a/cumulus/parachain-template/node/src/cli.rs +++ b/templates/parachain/node/src/cli.rs @@ -40,7 +40,7 @@ pub enum Subcommand { /// Try-runtime has migrated to a standalone /// [CLI](). The subcommand exists as a stub and - /// deprecation notice. It will be removed entirely some time after Janurary 2024. + /// deprecation notice. It will be removed entirely some time after January 2024. TryRuntime, } diff --git a/cumulus/parachain-template/node/src/command.rs b/templates/parachain/node/src/command.rs similarity index 98% rename from cumulus/parachain-template/node/src/command.rs rename to templates/parachain/node/src/command.rs index 72b3ab7bb4b94735cdf4910b2cd1f46c22e7b16e..82624ae0be59e3159477b6f35b95f86177a35754 100644 --- a/cumulus/parachain-template/node/src/command.rs +++ b/templates/parachain/node/src/command.rs @@ -1,5 +1,6 @@ use std::net::SocketAddr; +use cumulus_client_service::storage_proof_size::HostFunctions as ReclaimHostFunctions; use cumulus_primitives_core::ParaId; use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}; use log::info; @@ -183,7 +184,7 @@ pub fn run() -> Result<()> { match cmd { BenchmarkCmd::Pallet(cmd) => if cfg!(feature = "runtime-benchmarks") { - runner.sync_run(|config| cmd.run::, ()>(config)) + runner.sync_run(|config| cmd.run::, ReclaimHostFunctions>(config)) } else { Err("Benchmarking wasn't enabled when building the node. \ You can enable it with `--features runtime-benchmarks`." diff --git a/cumulus/parachain-template/node/src/main.rs b/templates/parachain/node/src/main.rs similarity index 100% rename from cumulus/parachain-template/node/src/main.rs rename to templates/parachain/node/src/main.rs diff --git a/cumulus/parachain-template/node/src/rpc.rs b/templates/parachain/node/src/rpc.rs similarity index 100% rename from cumulus/parachain-template/node/src/rpc.rs rename to templates/parachain/node/src/rpc.rs diff --git a/cumulus/parachain-template/node/src/service.rs b/templates/parachain/node/src/service.rs similarity index 98% rename from cumulus/parachain-template/node/src/service.rs rename to templates/parachain/node/src/service.rs index 830b6e82f969190b69425bd59a939c49372bf9f3..4dd24803e9b124d386d4912236ae1abd42229802 100644 --- a/cumulus/parachain-template/node/src/service.rs +++ b/templates/parachain/node/src/service.rs @@ -40,7 +40,10 @@ use substrate_prometheus_endpoint::Registry; pub struct ParachainNativeExecutor; impl sc_executor::NativeExecutionDispatch for ParachainNativeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; + type ExtendHostFunctions = ( + cumulus_client_service::storage_proof_size::HostFunctions, + frame_benchmarking::benchmarking::HostFunctions, + ); fn dispatch(method: &str, data: &[u8]) -> Option> { parachain_template_runtime::api::dispatch(method, data) @@ -100,10 +103,11 @@ pub fn new_partial(config: &Configuration) -> Result let executor = ParachainExecutor::new_with_wasm_executor(wasm); let (client, backend, keystore_container, task_manager) = - sc_service::new_full_parts::( + sc_service::new_full_parts_record_import::( config, telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), executor, + true, )?; let client = Arc::new(client); diff --git a/cumulus/parachain-template/pallets/template/Cargo.toml b/templates/parachain/pallets/template/Cargo.toml similarity index 68% rename from cumulus/parachain-template/pallets/template/Cargo.toml rename to templates/parachain/pallets/template/Cargo.toml index 04858a161fa43356e0082d99769fabd850be26a8..89eb9d5171630459e2e313ad94cee53e97921cac 100644 --- a/cumulus/parachain-template/pallets/template/Cargo.toml +++ b/templates/parachain/pallets/template/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-parachain-template" -authors = ["Anonymous"] description = "FRAME pallet template for defining custom runtime logic." -version = "0.7.0" -license = "Unlicense" -homepage = "https://substrate.io" +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true repository.workspace = true edition.workspace = true +publish = false [lints] workspace = true @@ -15,21 +16,22 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"], default-features = false } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ + "derive", +] } +scale-info = { version = "2.10.0", default-features = false, features = [ + "derive", +] } -# Substrate +# frame deps frame-benchmarking = { path = "../../../../substrate/frame/benchmarking", default-features = false, optional = true } frame-support = { path = "../../../../substrate/frame/support", default-features = false } frame-system = { path = "../../../../substrate/frame/system", default-features = false } [dev-dependencies] -serde = { workspace = true, default-features = true } - -# Substrate -sp-core = { path = "../../../../substrate/primitives/core", default-features = false } -sp-io = { path = "../../../../substrate/primitives/io", default-features = false } -sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } +sp-core = { path = "../../../../substrate/primitives/core" } +sp-io = { path = "../../../../substrate/primitives/io" } +sp-runtime = { path = "../../../../substrate/primitives/runtime" } [features] default = ["std"] @@ -41,7 +43,7 @@ runtime-benchmarks = [ ] std = [ "codec/std", - "frame-benchmarking/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "scale-info/std", diff --git a/substrate/bin/node-template/pallets/template/README.md b/templates/parachain/pallets/template/README.md similarity index 100% rename from substrate/bin/node-template/pallets/template/README.md rename to templates/parachain/pallets/template/README.md diff --git a/substrate/bin/node-template/pallets/template/src/benchmarking.rs b/templates/parachain/pallets/template/src/benchmarking.rs similarity index 100% rename from substrate/bin/node-template/pallets/template/src/benchmarking.rs rename to templates/parachain/pallets/template/src/benchmarking.rs diff --git a/cumulus/parachain-template/pallets/template/src/lib.rs b/templates/parachain/pallets/template/src/lib.rs similarity index 96% rename from cumulus/parachain-template/pallets/template/src/lib.rs rename to templates/parachain/pallets/template/src/lib.rs index 5f3252bfc3a70aa731bf572493a3797bfef574bb..11587d1df426f485139eab9e333b292768c6c571 100644 --- a/cumulus/parachain-template/pallets/template/src/lib.rs +++ b/templates/parachain/pallets/template/src/lib.rs @@ -11,6 +11,8 @@ mod mock; #[cfg(test)] mod tests; +pub mod weights; + #[cfg(feature = "runtime-benchmarks")] mod benchmarking; @@ -24,6 +26,8 @@ pub mod pallet { pub trait Config: frame_system::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// A type representing the weights required by the dispatchables of this pallet. + type WeightInfo: crate::weights::WeightInfo; } #[pallet::pallet] @@ -32,7 +36,6 @@ pub mod pallet { // The pallet's runtime storage items. // https://docs.substrate.io/v3/runtime/storage #[pallet::storage] - #[pallet::getter(fn something)] // Learn more about declaring storage items: // https://docs.substrate.io/v3/runtime/storage#declaring-storage-items pub type Something = StorageValue<_, u32>; diff --git a/cumulus/parachain-template/pallets/template/src/mock.rs b/templates/parachain/pallets/template/src/mock.rs similarity index 94% rename from cumulus/parachain-template/pallets/template/src/mock.rs rename to templates/parachain/pallets/template/src/mock.rs index 411a16b116c8f94757c29a686022842e159e6924..8a88be3e3e9f6d08137ae1bb16767145e2d84aa9 100644 --- a/cumulus/parachain-template/pallets/template/src/mock.rs +++ b/templates/parachain/pallets/template/src/mock.rs @@ -22,7 +22,7 @@ parameter_types! { pub const SS58Prefix: u8 = 42; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl system::Config for Test { type BaseCallFilter = Everything; type BlockWeights = (); @@ -51,6 +51,7 @@ impl system::Config for Test { impl crate::Config for Test { type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); } // Build genesis storage according to the mock runtime. diff --git a/cumulus/parachain-template/pallets/template/src/tests.rs b/templates/parachain/pallets/template/src/tests.rs similarity index 86% rename from cumulus/parachain-template/pallets/template/src/tests.rs rename to templates/parachain/pallets/template/src/tests.rs index 527aec8ed00c058e5a9329a50d60267d7f9d1851..9ad3076be2cc9927063e1b50c293cafadf8f3361 100644 --- a/cumulus/parachain-template/pallets/template/src/tests.rs +++ b/templates/parachain/pallets/template/src/tests.rs @@ -1,4 +1,4 @@ -use crate::{mock::*, Error}; +use crate::{mock::*, Error, Something}; use frame_support::{assert_noop, assert_ok}; #[test] @@ -7,7 +7,7 @@ fn it_works_for_default_value() { // Dispatch a signed extrinsic. assert_ok!(TemplateModule::do_something(RuntimeOrigin::signed(1), 42)); // Read pallet storage and assert an expected result. - assert_eq!(TemplateModule::something(), Some(42)); + assert_eq!(Something::::get(), Some(42)); }); } diff --git a/substrate/bin/node-template/pallets/template/src/weights.rs b/templates/parachain/pallets/template/src/weights.rs similarity index 100% rename from substrate/bin/node-template/pallets/template/src/weights.rs rename to templates/parachain/pallets/template/src/weights.rs diff --git a/cumulus/parachain-template/polkadot-launch/config.json b/templates/parachain/polkadot-launch/config.json similarity index 100% rename from cumulus/parachain-template/polkadot-launch/config.json rename to templates/parachain/polkadot-launch/config.json diff --git a/cumulus/parachain-template/runtime/Cargo.toml b/templates/parachain/runtime/Cargo.toml similarity index 81% rename from cumulus/parachain-template/runtime/Cargo.toml rename to templates/parachain/runtime/Cargo.toml index 44d96ffc4e628e5b48209e46fe39da1a75826043..41a510c5ed35dd2c4d9c6a8266edc2d4a10a8f11 100644 --- a/cumulus/parachain-template/runtime/Cargo.toml +++ b/templates/parachain/runtime/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "parachain-template-runtime" -version = "0.7.0" -authors = ["Anonymous"] -description = "A new Cumulus FRAME-based Substrate Runtime, ready for hacking together a parachain." -license = "Unlicense" -homepage = "https://substrate.io" +description = "A parachain runtime template built with Substrate and Cumulus, part of Polkadot Sdk." +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true repository.workspace = true edition.workspace = true +publish = false [lints] workspace = true @@ -18,16 +19,20 @@ targets = ["x86_64-unknown-linux-gnu"] substrate-wasm-builder = { path = "../../../substrate/utils/wasm-builder", optional = true } [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ + "derive", +] } hex-literal = { version = "0.4.1", optional = true } log = { workspace = true } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.10.0", default-features = false, features = [ + "derive", +] } smallvec = "1.11.0" # Local pallet-parachain-template = { path = "../pallets/template", default-features = false } -# Substrate +# Substrate / FRAME frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } frame-executive = { path = "../../../substrate/frame/executive", default-features = false } frame-support = { path = "../../../substrate/frame/support", default-features = false } @@ -35,6 +40,8 @@ frame-system = { path = "../../../substrate/frame/system", default-features = fa frame-system-benchmarking = { path = "../../../substrate/frame/system/benchmarking", default-features = false, optional = true } frame-system-rpc-runtime-api = { path = "../../../substrate/frame/system/rpc/runtime-api", default-features = false } frame-try-runtime = { path = "../../../substrate/frame/try-runtime", default-features = false, optional = true } + +# FRAME Pallets pallet-aura = { path = "../../../substrate/frame/aura", default-features = false } pallet-authorship = { path = "../../../substrate/frame/authorship", default-features = false } pallet-balances = { path = "../../../substrate/frame/balances", default-features = false } @@ -44,6 +51,8 @@ pallet-sudo = { path = "../../../substrate/frame/sudo", default-features = false pallet-timestamp = { path = "../../../substrate/frame/timestamp", default-features = false } pallet-transaction-payment = { path = "../../../substrate/frame/transaction-payment", default-features = false } pallet-transaction-payment-rpc-runtime-api = { path = "../../../substrate/frame/transaction-payment/rpc/runtime-api", default-features = false } + +# Substrate Primitives sp-api = { path = "../../../substrate/primitives/api", default-features = false } sp-block-builder = { path = "../../../substrate/primitives/block-builder", default-features = false } sp-consensus-aura = { path = "../../../substrate/primitives/consensus/aura", default-features = false } @@ -66,16 +75,19 @@ xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot/xcm/x xcm-executor = { package = "staging-xcm-executor", path = "../../../polkadot/xcm/xcm-executor", default-features = false } # Cumulus -cumulus-pallet-aura-ext = { path = "../../pallets/aura-ext", default-features = false } -cumulus-pallet-parachain-system = { path = "../../pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook"] } -cumulus-pallet-session-benchmarking = { path = "../../pallets/session-benchmarking", default-features = false } -cumulus-pallet-xcm = { path = "../../pallets/xcm", default-features = false } -cumulus-pallet-xcmp-queue = { path = "../../pallets/xcmp-queue", default-features = false } -cumulus-primitives-core = { path = "../../primitives/core", default-features = false } -cumulus-primitives-utility = { path = "../../primitives/utility", default-features = false } -pallet-collator-selection = { path = "../../pallets/collator-selection", default-features = false } -parachains-common = { path = "../../parachains/common", default-features = false } -parachain-info = { package = "staging-parachain-info", path = "../../parachains/pallets/parachain-info", default-features = false } +cumulus-pallet-aura-ext = { path = "../../../cumulus/pallets/aura-ext", default-features = false } +cumulus-pallet-parachain-system = { path = "../../../cumulus/pallets/parachain-system", default-features = false, features = [ + "parameterized-consensus-hook", +] } +cumulus-pallet-session-benchmarking = { path = "../../../cumulus/pallets/session-benchmarking", default-features = false } +cumulus-pallet-xcm = { path = "../../../cumulus/pallets/xcm", default-features = false } +cumulus-pallet-xcmp-queue = { path = "../../../cumulus/pallets/xcmp-queue", default-features = false } +cumulus-primitives-core = { path = "../../../cumulus/primitives/core", default-features = false } +cumulus-primitives-utility = { path = "../../../cumulus/primitives/utility", default-features = false } +cumulus-primitives-storage-weight-reclaim = { path = "../../../cumulus/primitives/storage-weight-reclaim", default-features = false } +pallet-collator-selection = { path = "../../../cumulus/pallets/collator-selection", default-features = false } +parachains-common = { path = "../../../cumulus/parachains/common", default-features = false } +parachain-info = { package = "staging-parachain-info", path = "../../../cumulus/parachains/pallets/parachain-info", default-features = false } [features] default = ["std"] @@ -87,6 +99,7 @@ std = [ "cumulus-pallet-xcm/std", "cumulus-pallet-xcmp-queue/std", "cumulus-primitives-core/std", + "cumulus-primitives-storage-weight-reclaim/std", "cumulus-primitives-utility/std", "frame-benchmarking?/std", "frame-executive/std", @@ -181,5 +194,3 @@ try-runtime = [ "polkadot-runtime-common/try-runtime", "sp-runtime/try-runtime", ] - -experimental = ["pallet-aura/experimental"] diff --git a/cumulus/parachain-template/runtime/build.rs b/templates/parachain/runtime/build.rs similarity index 100% rename from cumulus/parachain-template/runtime/build.rs rename to templates/parachain/runtime/build.rs diff --git a/cumulus/parachain-template/runtime/src/lib.rs b/templates/parachain/runtime/src/lib.rs similarity index 98% rename from cumulus/parachain-template/runtime/src/lib.rs rename to templates/parachain/runtime/src/lib.rs index d9bc111fcef7f1932d4b52e3f0c9f42d0dccc6c0..ad21b79a5b1baf48759e7696c7233a9bdf9bdc38 100644 --- a/cumulus/parachain-template/runtime/src/lib.rs +++ b/templates/parachain/runtime/src/lib.rs @@ -57,6 +57,8 @@ use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; +use pallet_aura::Authorities; + // XCM Imports use xcm::latest::prelude::BodyId; @@ -107,6 +109,7 @@ pub type SignedExtra = ( frame_system::CheckNonce, frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim, ); /// Unchecked extrinsic type as expected by this runtime. @@ -179,8 +182,8 @@ impl_opaque_keys! { #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("template-parachain"), - impl_name: create_runtime_str!("template-parachain"), + spec_name: create_runtime_str!("parachain-template-runtime"), + impl_name: create_runtime_str!("parachain-template-runtime"), authoring_version: 1, spec_version: 1, impl_version: 0, @@ -276,7 +279,7 @@ parameter_types! { /// The default types are being injected by [`derive_impl`](`frame_support::derive_impl`) from /// [`ParaChainDefaultConfig`](`struct@frame_system::config_preludes::ParaChainDefaultConfig`), /// but overridden as needed. -#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig)] impl frame_system::Config for Runtime { /// The identifier used to distinguish between accounts. type AccountId = AccountId; @@ -452,7 +455,6 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; type AllowMultipleBlocksPerSlot = ConstBool; - #[cfg(feature = "experimental")] type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; } @@ -488,6 +490,7 @@ impl pallet_collator_selection::Config for Runtime { /// Configure the pallet template in pallets/template. impl pallet_parachain_template::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_parachain_template::weights::SubstrateWeight; } // Create the runtime by composing the FRAME pallets that were previously configured. @@ -546,7 +549,7 @@ impl_runtime_apis! { } fn authorities() -> Vec { - Aura::authorities().into_inner() + Authorities::::get().into_inner() } } @@ -559,7 +562,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } diff --git a/cumulus/parachain-template/runtime/src/weights/block_weights.rs b/templates/parachain/runtime/src/weights/block_weights.rs similarity index 100% rename from cumulus/parachain-template/runtime/src/weights/block_weights.rs rename to templates/parachain/runtime/src/weights/block_weights.rs diff --git a/cumulus/parachain-template/runtime/src/weights/extrinsic_weights.rs b/templates/parachain/runtime/src/weights/extrinsic_weights.rs similarity index 100% rename from cumulus/parachain-template/runtime/src/weights/extrinsic_weights.rs rename to templates/parachain/runtime/src/weights/extrinsic_weights.rs diff --git a/cumulus/parachain-template/runtime/src/weights/mod.rs b/templates/parachain/runtime/src/weights/mod.rs similarity index 100% rename from cumulus/parachain-template/runtime/src/weights/mod.rs rename to templates/parachain/runtime/src/weights/mod.rs diff --git a/cumulus/parachain-template/runtime/src/weights/paritydb_weights.rs b/templates/parachain/runtime/src/weights/paritydb_weights.rs similarity index 100% rename from cumulus/parachain-template/runtime/src/weights/paritydb_weights.rs rename to templates/parachain/runtime/src/weights/paritydb_weights.rs diff --git a/cumulus/parachain-template/runtime/src/weights/rocksdb_weights.rs b/templates/parachain/runtime/src/weights/rocksdb_weights.rs similarity index 100% rename from cumulus/parachain-template/runtime/src/weights/rocksdb_weights.rs rename to templates/parachain/runtime/src/weights/rocksdb_weights.rs diff --git a/cumulus/parachain-template/runtime/src/xcm_config.rs b/templates/parachain/runtime/src/xcm_config.rs similarity index 98% rename from cumulus/parachain-template/runtime/src/xcm_config.rs rename to templates/parachain/runtime/src/xcm_config.rs index b1230ba1e5d4151f12776040c76437b29152b468..7dce7164888634bba7a12406cd9d1868d2f26795 100644 --- a/cumulus/parachain-template/runtime/src/xcm_config.rs +++ b/templates/parachain/runtime/src/xcm_config.rs @@ -137,6 +137,9 @@ impl xcm_executor::Config for XcmConfig { type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); } /// No local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/substrate/bin/node-template/LICENSE b/templates/solochain/LICENSE similarity index 100% rename from substrate/bin/node-template/LICENSE rename to templates/solochain/LICENSE diff --git a/substrate/bin/node-template/README.md b/templates/solochain/README.md similarity index 100% rename from substrate/bin/node-template/README.md rename to templates/solochain/README.md diff --git a/substrate/bin/node-template/docs/rust-setup.md b/templates/solochain/docs/rust-setup.md similarity index 100% rename from substrate/bin/node-template/docs/rust-setup.md rename to templates/solochain/docs/rust-setup.md diff --git a/substrate/bin/node-template/env-setup/README.md b/templates/solochain/env-setup/README.md similarity index 100% rename from substrate/bin/node-template/env-setup/README.md rename to templates/solochain/env-setup/README.md diff --git a/substrate/bin/node-template/env-setup/flake.lock b/templates/solochain/env-setup/flake.lock similarity index 100% rename from substrate/bin/node-template/env-setup/flake.lock rename to templates/solochain/env-setup/flake.lock diff --git a/substrate/bin/node-template/env-setup/flake.nix b/templates/solochain/env-setup/flake.nix similarity index 100% rename from substrate/bin/node-template/env-setup/flake.nix rename to templates/solochain/env-setup/flake.nix diff --git a/substrate/bin/node-template/env-setup/rust-toolchain.toml b/templates/solochain/env-setup/rust-toolchain.toml similarity index 100% rename from substrate/bin/node-template/env-setup/rust-toolchain.toml rename to templates/solochain/env-setup/rust-toolchain.toml diff --git a/templates/solochain/node/Cargo.toml b/templates/solochain/node/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..a0bb5c27ed3b56a8656ec55be8c2ad58f87a78d8 --- /dev/null +++ b/templates/solochain/node/Cargo.toml @@ -0,0 +1,91 @@ +[package] +name = "solochain-template-node" +description = "A solochain node template built with Substrate, part of Polkadot Sdk." +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true +publish = false + +build = "build.rs" + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +clap = { version = "4.5.3", features = ["derive"] } +futures = { version = "0.3.21", features = ["thread-pool"] } +serde_json = { workspace = true, default-features = true } +jsonrpsee = { version = "0.22", features = ["server"] } + +# substrate client +sc-cli = { path = "../../../substrate/client/cli" } +sp-core = { path = "../../../substrate/primitives/core" } +sc-executor = { path = "../../../substrate/client/executor" } +sc-network = { path = "../../../substrate/client/network" } +sc-service = { path = "../../../substrate/client/service" } +sc-telemetry = { path = "../../../substrate/client/telemetry" } +sc-transaction-pool = { path = "../../../substrate/client/transaction-pool" } +sc-transaction-pool-api = { path = "../../../substrate/client/transaction-pool/api" } +sc-offchain = { path = "../../../substrate/client/offchain" } +sc-consensus-aura = { path = "../../../substrate/client/consensus/aura" } +sp-consensus-aura = { path = "../../../substrate/primitives/consensus/aura" } +sc-consensus = { path = "../../../substrate/client/consensus/common" } +sc-consensus-grandpa = { path = "../../../substrate/client/consensus/grandpa" } +sp-consensus-grandpa = { path = "../../../substrate/primitives/consensus/grandpa" } +sc-client-api = { path = "../../../substrate/client/api" } +sc-rpc-api = { path = "../../../substrate/client/rpc-api" } +sc-basic-authorship = { path = "../../../substrate/client/basic-authorship" } + +# substrate primitives +sp-runtime = { path = "../../../substrate/primitives/runtime" } +sp-io = { path = "../../../substrate/primitives/io" } +sp-timestamp = { path = "../../../substrate/primitives/timestamp" } +sp-inherents = { path = "../../../substrate/primitives/inherents" } +sp-keyring = { path = "../../../substrate/primitives/keyring" } +sp-api = { path = "../../../substrate/primitives/api" } +sp-blockchain = { path = "../../../substrate/primitives/blockchain" } +sp-block-builder = { path = "../../../substrate/primitives/block-builder" } + +# frame and pallets +frame-system = { path = "../../../substrate/frame/system" } +pallet-transaction-payment = { path = "../../../substrate/frame/transaction-payment", default-features = false } +pallet-transaction-payment-rpc = { path = "../../../substrate/frame/transaction-payment/rpc" } +substrate-frame-rpc-system = { path = "../../../substrate/utils/frame/rpc/system" } + +# These dependencies are used for runtime benchmarking +frame-benchmarking-cli = { path = "../../../substrate/utils/frame/benchmarking-cli" } + +# Local Dependencies +solochain-template-runtime = { path = "../runtime" } + +# CLI-specific dependencies +try-runtime-cli = { path = "../../../substrate/utils/frame/try-runtime/cli", optional = true } + +[build-dependencies] +substrate-build-script-utils = { path = "../../../substrate/utils/build-script-utils" } + +[features] +default = [] +# Dependencies that are only required if runtime benchmarking should be build. +runtime-benchmarks = [ + "frame-benchmarking-cli/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sc-service/runtime-benchmarks", + "solochain-template-runtime/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +# Enable features that allow the runtime to be tried and debugged. Name might be subject to change +# in the near future. +try-runtime = [ + "frame-system/try-runtime", + "pallet-transaction-payment/try-runtime", + "solochain-template-runtime/try-runtime", + "sp-runtime/try-runtime", + "try-runtime-cli/try-runtime", +] diff --git a/substrate/bin/node-template/node/build.rs b/templates/solochain/node/build.rs similarity index 100% rename from substrate/bin/node-template/node/build.rs rename to templates/solochain/node/build.rs diff --git a/substrate/bin/node-template/node/src/benchmarking.rs b/templates/solochain/node/src/benchmarking.rs similarity index 99% rename from substrate/bin/node-template/node/src/benchmarking.rs rename to templates/solochain/node/src/benchmarking.rs index 6e29ad1a123118d953c1fad654bad9343fd8ca53..d1d8c2ccabaf64c730e14eb32e076052ae4a9e2b 100644 --- a/substrate/bin/node-template/node/src/benchmarking.rs +++ b/templates/solochain/node/src/benchmarking.rs @@ -4,10 +4,10 @@ use crate::service::FullClient; -use node_template_runtime as runtime; use runtime::{AccountId, Balance, BalancesCall, SystemCall}; use sc_cli::Result; use sc_client_api::BlockBackend; +use solochain_template_runtime as runtime; use sp_core::{Encode, Pair}; use sp_inherents::{InherentData, InherentDataProvider}; use sp_keyring::Sr25519Keyring; diff --git a/substrate/bin/node-template/node/src/chain_spec.rs b/templates/solochain/node/src/chain_spec.rs similarity index 97% rename from substrate/bin/node-template/node/src/chain_spec.rs rename to templates/solochain/node/src/chain_spec.rs index 6e0d78f647a594d7e3c247041a7499691edaad1e..be49f2c1fc731ee4e5ece7841151068abf0f0790 100644 --- a/substrate/bin/node-template/node/src/chain_spec.rs +++ b/templates/solochain/node/src/chain_spec.rs @@ -1,5 +1,5 @@ -use node_template_runtime::{AccountId, RuntimeGenesisConfig, Signature, WASM_BINARY}; use sc_service::ChainType; +use solochain_template_runtime::{AccountId, RuntimeGenesisConfig, Signature, WASM_BINARY}; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_consensus_grandpa::AuthorityId as GrandpaId; use sp_core::{sr25519, Pair, Public}; diff --git a/substrate/bin/node-template/node/src/cli.rs b/templates/solochain/node/src/cli.rs similarity index 98% rename from substrate/bin/node-template/node/src/cli.rs rename to templates/solochain/node/src/cli.rs index 98037eb886a8ec04ee78bc8f0b21b2269872d0fc..7f1b67b3696e6844f48c91b742a9017bff267d82 100644 --- a/substrate/bin/node-template/node/src/cli.rs +++ b/templates/solochain/node/src/cli.rs @@ -43,7 +43,7 @@ pub enum Subcommand { /// Try-runtime has migrated to a standalone CLI /// (). The subcommand exists as a stub and - /// deprecation notice. It will be removed entirely some time after Janurary 2024. + /// deprecation notice. It will be removed entirely some time after January 2024. TryRuntime, /// Db meta columns information. diff --git a/substrate/bin/node-template/node/src/command.rs b/templates/solochain/node/src/command.rs similarity index 98% rename from substrate/bin/node-template/node/src/command.rs rename to templates/solochain/node/src/command.rs index 3778df6642294684780c347730eb580b69b54d65..42d1477f22f18579d49fb48fdc91df39b10aaa2b 100644 --- a/substrate/bin/node-template/node/src/command.rs +++ b/templates/solochain/node/src/command.rs @@ -5,9 +5,9 @@ use crate::{ service, }; use frame_benchmarking_cli::{BenchmarkCmd, ExtrinsicFactory, SUBSTRATE_REFERENCE_HARDWARE}; -use node_template_runtime::{Block, EXISTENTIAL_DEPOSIT}; use sc_cli::SubstrateCli; use sc_service::PartialComponents; +use solochain_template_runtime::{Block, EXISTENTIAL_DEPOSIT}; use sp_keyring::Sr25519Keyring; impl SubstrateCli for Cli { diff --git a/substrate/bin/node-template/node/src/main.rs b/templates/solochain/node/src/main.rs similarity index 100% rename from substrate/bin/node-template/node/src/main.rs rename to templates/solochain/node/src/main.rs diff --git a/substrate/bin/node-template/node/src/rpc.rs b/templates/solochain/node/src/rpc.rs similarity index 96% rename from substrate/bin/node-template/node/src/rpc.rs rename to templates/solochain/node/src/rpc.rs index 246391adcbbe88a03b6cf9cf9043d82b8de18b60..fe2b6ca72ede5c8d9f8d878db88efa2da5c5ac97 100644 --- a/substrate/bin/node-template/node/src/rpc.rs +++ b/templates/solochain/node/src/rpc.rs @@ -8,8 +8,8 @@ use std::sync::Arc; use jsonrpsee::RpcModule; -use node_template_runtime::{opaque::Block, AccountId, Balance, Nonce}; use sc_transaction_pool_api::TransactionPool; +use solochain_template_runtime::{opaque::Block, AccountId, Balance, Nonce}; use sp_api::ProvideRuntimeApi; use sp_block_builder::BlockBuilder; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; diff --git a/substrate/bin/node-template/node/src/service.rs b/templates/solochain/node/src/service.rs similarity index 94% rename from substrate/bin/node-template/node/src/service.rs rename to templates/solochain/node/src/service.rs index 25cd651178411c6338a2b0d1e7836804a3a5b676..dc25f7579129fe3ea86d0686732be5e6baec40ba 100644 --- a/substrate/bin/node-template/node/src/service.rs +++ b/templates/solochain/node/src/service.rs @@ -1,13 +1,13 @@ //! Service and ServiceFactory implementation. Specialized wrapper over substrate service. use futures::FutureExt; -use node_template_runtime::{self, opaque::Block, RuntimeApi}; use sc_client_api::{Backend, BlockBackend}; use sc_consensus_aura::{ImportQueueParams, SlotProportion, StartAuraParams}; use sc_consensus_grandpa::SharedVoterState; use sc_service::{error::Error as ServiceError, Configuration, TaskManager, WarpSyncParams}; use sc_telemetry::{Telemetry, TelemetryWorker}; use sc_transaction_pool_api::OffchainTransactionPoolFactory; +use solochain_template_runtime::{self, opaque::Block, RuntimeApi}; use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; use std::{sync::Arc, time::Duration}; @@ -80,23 +80,29 @@ pub fn new_partial(config: &Configuration) -> Result { telemetry.as_ref().map(|x| x.handle()), )?; - let slot_duration = sc_consensus_aura::slot_duration(&*client)?; - + let cidp_client = client.clone(); let import_queue = sc_consensus_aura::import_queue::(ImportQueueParams { block_import: grandpa_block_import.clone(), justification_import: Some(Box::new(grandpa_block_import.clone())), client: client.clone(), - create_inherent_data_providers: move |_, ()| async move { - let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + create_inherent_data_providers: move |parent_hash, _| { + let cidp_client = cidp_client.clone(); + async move { + let slot_duration = sc_consensus_aura::standalone::slot_duration_at( + &*cidp_client, + parent_hash, + )?; + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); - let slot = - sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( - *timestamp, - slot_duration, - ); + let slot = + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); - Ok((slot, timestamp)) + Ok((slot, timestamp)) + } }, spawner: &task_manager.spawn_essential_handle(), registry: config.prometheus_registry(), diff --git a/substrate/bin/node-template/pallets/template/Cargo.toml b/templates/solochain/pallets/template/Cargo.toml similarity index 55% rename from substrate/bin/node-template/pallets/template/Cargo.toml rename to templates/solochain/pallets/template/Cargo.toml index 51410a71c7bcee0267f36bbfcf20c616a5537ce3..bd2347151989df8950dc4dd96f039c74241d28a5 100644 --- a/substrate/bin/node-template/pallets/template/Cargo.toml +++ b/templates/solochain/pallets/template/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "pallet-template" -version = "4.0.0-dev" description = "FRAME pallet template for defining custom runtime logic." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io" -edition.workspace = true +version = "0.0.0" license = "MIT-0" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" [lints] workspace = true @@ -19,16 +19,19 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ "derive", ] } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -frame-benchmarking = { path = "../../../../frame/benchmarking", default-features = false, optional = true } -frame-support = { path = "../../../../frame/support", default-features = false } -frame-system = { path = "../../../../frame/system", default-features = false } -sp-std = { path = "../../../../primitives/std", default-features = false } +scale-info = { version = "2.10.0", default-features = false, features = [ + "derive", +] } + +# frame deps +frame-benchmarking = { path = "../../../../substrate/frame/benchmarking", default-features = false, optional = true } +frame-support = { path = "../../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../../substrate/frame/system", default-features = false } [dev-dependencies] -sp-core = { path = "../../../../primitives/core" } -sp-io = { path = "../../../../primitives/io" } -sp-runtime = { path = "../../../../primitives/runtime" } +sp-core = { path = "../../../../substrate/primitives/core" } +sp-io = { path = "../../../../substrate/primitives/io" } +sp-runtime = { path = "../../../../substrate/primitives/runtime" } [features] default = ["std"] @@ -41,7 +44,6 @@ std = [ "sp-core/std", "sp-io/std", "sp-runtime/std", - "sp-std/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", diff --git a/templates/solochain/pallets/template/README.md b/templates/solochain/pallets/template/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9e4dc55267d69c47fff971cb0427bcb2e0ff871c --- /dev/null +++ b/templates/solochain/pallets/template/README.md @@ -0,0 +1 @@ +License: MIT-0 diff --git a/templates/solochain/pallets/template/src/benchmarking.rs b/templates/solochain/pallets/template/src/benchmarking.rs new file mode 100644 index 0000000000000000000000000000000000000000..5a262417629c579c6ecf5ada30ae803217623766 --- /dev/null +++ b/templates/solochain/pallets/template/src/benchmarking.rs @@ -0,0 +1,35 @@ +//! Benchmarking setup for pallet-template +#![cfg(feature = "runtime-benchmarks")] +use super::*; + +#[allow(unused)] +use crate::Pallet as Template; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn do_something() { + let value = 100u32.into(); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + do_something(RawOrigin::Signed(caller), value); + + assert_eq!(Something::::get(), Some(value)); + } + + #[benchmark] + fn cause_error() { + Something::::put(100u32); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + cause_error(RawOrigin::Signed(caller)); + + assert_eq!(Something::::get(), Some(101u32)); + } + + impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/substrate/bin/node-template/pallets/template/src/lib.rs b/templates/solochain/pallets/template/src/lib.rs similarity index 98% rename from substrate/bin/node-template/pallets/template/src/lib.rs rename to templates/solochain/pallets/template/src/lib.rs index 4a2e53baa774ee5ca333602024d458aa078f9c10..90dfe370145856cc08483cdbce2bbc542b21b2b9 100644 --- a/substrate/bin/node-template/pallets/template/src/lib.rs +++ b/templates/solochain/pallets/template/src/lib.rs @@ -90,9 +90,7 @@ pub mod pallet { /// /// In this template, we are declaring a storage item called `Something` that stores a single /// `u32` value. Learn more about runtime storage here: - /// The [`getter`] macro generates a function to conveniently retrieve the value from storage. #[pallet::storage] - #[pallet::getter(fn something)] pub type Something = StorageValue<_, u32>; /// Events that functions in this pallet can emit. @@ -187,7 +185,7 @@ pub mod pallet { let _who = ensure_signed(origin)?; // Read a value from storage. - match Pallet::::something() { + match Something::::get() { // Return an error if the value has not been set. None => Err(Error::::NoneValue.into()), Some(old) => { diff --git a/substrate/bin/node-template/pallets/template/src/mock.rs b/templates/solochain/pallets/template/src/mock.rs similarity index 93% rename from substrate/bin/node-template/pallets/template/src/mock.rs rename to templates/solochain/pallets/template/src/mock.rs index 8346461e6ed9bd97ff306bc69f11876a5b3391be..3f1fd2dd6d4441c2177d3f44a0f377b3ab2edc72 100644 --- a/substrate/bin/node-template/pallets/template/src/mock.rs +++ b/templates/solochain/pallets/template/src/mock.rs @@ -20,7 +20,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); diff --git a/substrate/bin/node-template/pallets/template/src/tests.rs b/templates/solochain/pallets/template/src/tests.rs similarity index 88% rename from substrate/bin/node-template/pallets/template/src/tests.rs rename to templates/solochain/pallets/template/src/tests.rs index 7c2b853ee4dc56fdf526a17557fe37281a50e803..83e4bea7377b348d8ac1d389813a788f79b81970 100644 --- a/substrate/bin/node-template/pallets/template/src/tests.rs +++ b/templates/solochain/pallets/template/src/tests.rs @@ -1,4 +1,4 @@ -use crate::{mock::*, Error, Event}; +use crate::{mock::*, Error, Event, Something}; use frame_support::{assert_noop, assert_ok}; #[test] @@ -9,7 +9,7 @@ fn it_works_for_default_value() { // Dispatch a signed extrinsic. assert_ok!(TemplateModule::do_something(RuntimeOrigin::signed(1), 42)); // Read pallet storage and assert an expected result. - assert_eq!(TemplateModule::something(), Some(42)); + assert_eq!(Something::::get(), Some(42)); // Assert that the correct event was deposited System::assert_last_event(Event::SomethingStored { something: 42, who: 1 }.into()); }); diff --git a/templates/solochain/pallets/template/src/weights.rs b/templates/solochain/pallets/template/src/weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..7c42936e09f292de831d28460a3bc39436c3323f --- /dev/null +++ b/templates/solochain/pallets/template/src/weights.rs @@ -0,0 +1,90 @@ + +//! Autogenerated weights for pallet_template +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `Alexs-MacBook-Pro-2.local`, CPU: `` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ../../target/release/node-template +// benchmark +// pallet +// --chain +// dev +// --pallet +// pallet_template +// --extrinsic +// * +// --steps=50 +// --repeat=20 +// --wasm-execution=compiled +// --output +// pallets/template/src/weights.rs +// --template +// ../../.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for pallet_template. +pub trait WeightInfo { + fn do_something() -> Weight; + fn cause_error() -> Weight; +} + +/// Weights for pallet_template using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: TemplateModule Something (r:0 w:1) + /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn do_something() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(9_000_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: TemplateModule Something (r:1 w:1) + /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn cause_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `32` + // Estimated: `1489` + // Minimum execution time: 6_000_000 picoseconds. + Weight::from_parts(6_000_000, 1489) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: TemplateModule Something (r:0 w:1) + /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn do_something() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(9_000_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: TemplateModule Something (r:1 w:1) + /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn cause_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `32` + // Estimated: `1489` + // Minimum execution time: 6_000_000 picoseconds. + Weight::from_parts(6_000_000, 1489) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/templates/solochain/runtime/Cargo.toml b/templates/solochain/runtime/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..90dd823eb645772771199995e1168346b8330638 --- /dev/null +++ b/templates/solochain/runtime/Cargo.toml @@ -0,0 +1,149 @@ +[package] +name = "solochain-template-runtime" +description = "A solochain runtime template built with Substrate, part of Polkadot Sdk." +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true +publish = false + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ + "derive", +] } +scale-info = { version = "2.10.0", default-features = false, features = [ + "derive", + "serde", +] } + +# frame +frame-support = { path = "../../../substrate/frame/support", default-features = false, features = ["experimental"] } +frame-system = { path = "../../../substrate/frame/system", default-features = false } +frame-try-runtime = { path = "../../../substrate/frame/try-runtime", default-features = false, optional = true } +frame-executive = { path = "../../../substrate/frame/executive", default-features = false } + +# frame pallets +pallet-aura = { path = "../../../substrate/frame/aura", default-features = false } +pallet-balances = { path = "../../../substrate/frame/balances", default-features = false } +pallet-grandpa = { path = "../../../substrate/frame/grandpa", default-features = false } +pallet-sudo = { path = "../../../substrate/frame/sudo", default-features = false } +pallet-timestamp = { path = "../../../substrate/frame/timestamp", default-features = false } +pallet-transaction-payment = { path = "../../../substrate/frame/transaction-payment", default-features = false } + +# primitives +sp-api = { path = "../../../substrate/primitives/api", default-features = false } +sp-block-builder = { path = "../../../substrate/primitives/block-builder", default-features = false } +sp-consensus-aura = { path = "../../../substrate/primitives/consensus/aura", default-features = false, features = [ + "serde", +] } +sp-consensus-grandpa = { path = "../../../substrate/primitives/consensus/grandpa", default-features = false, features = [ + "serde", +] } +sp-core = { path = "../../../substrate/primitives/core", default-features = false, features = [ + "serde", +] } +sp-inherents = { path = "../../../substrate/primitives/inherents", default-features = false } +sp-offchain = { path = "../../../substrate/primitives/offchain", default-features = false } +sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false, features = [ + "serde", +] } +sp-session = { path = "../../../substrate/primitives/session", default-features = false } +sp-std = { path = "../../../substrate/primitives/std", default-features = false } +sp-storage = { path = "../../../substrate/primitives/storage", default-features = false } +sp-transaction-pool = { path = "../../../substrate/primitives/transaction-pool", default-features = false } +sp-version = { path = "../../../substrate/primitives/version", default-features = false, features = [ + "serde", +] } +sp-genesis-builder = { path = "../../../substrate/primitives/genesis-builder", default-features = false } + +# RPC related +frame-system-rpc-runtime-api = { path = "../../../substrate/frame/system/rpc/runtime-api", default-features = false } +pallet-transaction-payment-rpc-runtime-api = { path = "../../../substrate/frame/transaction-payment/rpc/runtime-api", default-features = false } + +# Used for runtime benchmarking +frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } +frame-system-benchmarking = { path = "../../../substrate/frame/system/benchmarking", default-features = false, optional = true } + +# The pallet in this template. +pallet-template = { path = "../pallets/template", default-features = false } + +[build-dependencies] +substrate-wasm-builder = { path = "../../../substrate/utils/wasm-builder", optional = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "scale-info/std", + + "frame-executive/std", + "frame-support/std", + "frame-system-benchmarking?/std", + "frame-system-rpc-runtime-api/std", + "frame-system/std", + + "frame-benchmarking?/std", + "frame-try-runtime?/std", + + "pallet-aura/std", + "pallet-balances/std", + "pallet-grandpa/std", + "pallet-sudo/std", + "pallet-template/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-aura/std", + "sp-consensus-grandpa/std", + "sp-core/std", + "sp-genesis-builder/std", + "sp-inherents/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-storage/std", + "sp-transaction-pool/std", + "sp-version/std", + + "substrate-wasm-builder", +] + +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system-benchmarking/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-grandpa/runtime-benchmarks", + "pallet-sudo/runtime-benchmarks", + "pallet-template/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] + +try-runtime = [ + "frame-executive/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "frame-try-runtime/try-runtime", + "pallet-aura/try-runtime", + "pallet-balances/try-runtime", + "pallet-grandpa/try-runtime", + "pallet-sudo/try-runtime", + "pallet-template/try-runtime", + "pallet-timestamp/try-runtime", + "pallet-transaction-payment/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/bin/node-template/runtime/build.rs b/templates/solochain/runtime/build.rs similarity index 100% rename from substrate/bin/node-template/runtime/build.rs rename to templates/solochain/runtime/build.rs diff --git a/substrate/bin/node-template/runtime/src/lib.rs b/templates/solochain/runtime/src/lib.rs similarity index 93% rename from substrate/bin/node-template/runtime/src/lib.rs rename to templates/solochain/runtime/src/lib.rs index 3b6a74be2512c6214b2fadada68bb22d746f07a9..5a97bd2f39120b1bdf119b0638464e6eceb45047 100644 --- a/substrate/bin/node-template/runtime/src/lib.rs +++ b/templates/solochain/runtime/src/lib.rs @@ -1,8 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. -#![recursion_limit = "256"] -// Make the WASM binary available. #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); @@ -22,7 +19,6 @@ use sp_version::NativeVersion; use sp_version::RuntimeVersion; use frame_support::genesis_builder_helper::{build_config, create_default_config}; -// A few exports that help ease life for downstream crates. pub use frame_support::{ construct_runtime, derive_impl, parameter_types, traits::{ @@ -95,8 +91,8 @@ pub mod opaque { // https://docs.substrate.io/main-docs/build/upgrade#runtime-versioning #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("node-template"), - impl_name: create_runtime_str!("node-template"), + spec_name: create_runtime_str!("solochain-template-runtime"), + impl_name: create_runtime_str!("solochain-template-runtime"), authoring_version: 1, // The version of the runtime specification. A full node will not attempt to use its native // runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`, @@ -152,7 +148,7 @@ parameter_types! { /// The default types are being injected by [`derive_impl`](`frame_support::derive_impl`) from /// [`SoloChainDefaultConfig`](`struct@frame_system::config_preludes::SolochainDefaultConfig`), /// but overridden as needed. -#[derive_impl(frame_system::config_preludes::SolochainDefaultConfig as frame_system::DefaultConfig)] +#[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)] impl frame_system::Config for Runtime { /// The block type for the runtime. type Block = Block; @@ -184,8 +180,6 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<32>; type AllowMultipleBlocksPerSlot = ConstBool; - - #[cfg(feature = "experimental")] type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; } @@ -256,19 +250,47 @@ impl pallet_template::Config for Runtime { } // Create the runtime by composing the FRAME pallets that were previously configured. -construct_runtime!( - pub enum Runtime { - System: frame_system, - Timestamp: pallet_timestamp, - Aura: pallet_aura, - Grandpa: pallet_grandpa, - Balances: pallet_balances, - TransactionPayment: pallet_transaction_payment, - Sudo: pallet_sudo, - // Include the custom logic from the pallet-template in the runtime. - TemplateModule: pallet_template, - } -); +#[frame_support::runtime] +mod runtime { + #[runtime::runtime] + #[runtime::derive( + RuntimeCall, + RuntimeEvent, + RuntimeError, + RuntimeOrigin, + RuntimeFreezeReason, + RuntimeHoldReason, + RuntimeSlashReason, + RuntimeLockId, + RuntimeTask + )] + pub struct Runtime; + + #[runtime::pallet_index(0)] + pub type System = frame_system; + + #[runtime::pallet_index(1)] + pub type Timestamp = pallet_timestamp; + + #[runtime::pallet_index(2)] + pub type Aura = pallet_aura; + + #[runtime::pallet_index(3)] + pub type Grandpa = pallet_grandpa; + + #[runtime::pallet_index(4)] + pub type Balances = pallet_balances; + + #[runtime::pallet_index(5)] + pub type TransactionPayment = pallet_transaction_payment; + + #[runtime::pallet_index(6)] + pub type Sudo = pallet_sudo; + + // Include the custom logic from the pallet-template in the runtime. + #[runtime::pallet_index(7)] + pub type TemplateModule = pallet_template; +} /// The address format for describing accounts. pub type Address = sp_runtime::MultiAddress; @@ -331,7 +353,7 @@ impl_runtime_apis! { Executive::execute_block(block); } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -393,7 +415,7 @@ impl_runtime_apis! { } fn authorities() -> Vec { - Aura::authorities().into_inner() + pallet_aura::Authorities::::get().into_inner() } }